chore: 重写初始提交(清空历史,整理后全量提交)

This commit is contained in:
ywxapp
2026-08-16 16:54:14 +08:00
commit 6c1a106bc1
1808 changed files with 238144 additions and 0 deletions
+201
View File
@@ -0,0 +1,201 @@
<?php
// +----------------------------------------------------------------------
// | YwxApp [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2026-2036 http://ywxapp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: ywxapp<admin@ywxapp.cn>
// +----------------------------------------------------------------------
namespace ywxapp\model;
use think\model\concern\SoftDelete;
use ywxapp\model\BaseModel;
/**
* @author ywx <1790337789@qq.com>
* @date 2021-07-06
* 用户模型
* @return \think\Model
*/
class MemberUser extends BaseModel
{
use SoftDelete;
protected function getOptions(): array
{
return [
'strict' => true,
'name' => 'member_user',
'autoWriteTimestamp' => 'int',
'readonly' => ['uid'],
'createTime' => 'create_at',
'updateTime' => 'update_at',
'deleteTime' => 'delete_at',
'hidden' => ['password', 'delete_at'],
'append' => ['status_text', 'last_login'],
];
}
// 关联用户资料
public function profile()
{
return $this->hasOne(MemberProfile::class, 'uid', 'uid');
}
// 关联用户组(会员组)
public function groups()
{
return $this->belongsToMany(MemberGroup::class, MemberGroupAccess::class, 'gid', 'uid');
}
/**
* 获取用户有权访问的菜单树
*/
public function getAccessibleMenus()
{
$permissions = $this->getAllPermissions();
if (empty($permissions)) {
return [];
}
$flatMenus = MemberRule::field('id,title,name,pid,sort,route,icon,type')
->whereIn('id', $permissions)
->order('sort', 'asc')
->select()
->toArray();
return MemberRule::buildTree($flatMenus);
}
/**
* 获取用户所有权限标识(字符串数组)
*/
public function getAllPermissions(): array
{
$roleIds = $this->groups->column('id');
if (empty($roleIds)) {
return [];
}
return MemberGroupRule::where('gid', 'in', $roleIds)
->distinct(true)
->column('rid');
}
// 密码加密
public function setPasswordAttr($value)
{
$this->salt = $this->generateSalt();
return password_hash($value . $this->salt, PASSWORD_DEFAULT);
}
// 生成随机盐
protected function generateSalt($length = 6)
{
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_[]{}<>`+=,.;:/?|';
$salt = '';
for ($i = 0; $i < $length; $i++) {
$salt .= $chars[mt_rand(0, strlen($chars) - 1)];
}
return $salt;
}
// 验证密码
public function checkPassword($password)
{
return password_verify($password . $this->salt, $this->password);
}
// 获取状态文本
public function getStatusTextAttr()
{
$statusMap = [0 => '禁用', 1 => '正常', 2 => '锁定'];
return $statusMap[$this->status] ?? '未知';
}
// 获取最后登录描述
public function getLastLoginAttr()
{
if (empty($this->login_time)) {
return '从未登录';
}
$diff = time() - strtotime($this->login_time);
if ($diff < 60) {
return '刚刚';
} elseif ($diff < 3600) {
return floor($diff / 60) . '分钟前';
} elseif ($diff < 86400) {
return floor($diff / 3600) . '小时前';
} else {
return date('Y-m-d', strtotime($this->login_time));
}
}
// 检查账户是否被锁定(统一使用 user 表已有的 loginfailure / loginfailuretime 字段)
// 复用该字段:失败次数 >=5 且最后一次失败距今不足 30 分钟则锁定。
public function isLocked()
{
if ($this->status == 0) {
return true; // 已禁用
}
if ($this->loginfailure >= 5 && $this->loginfailuretime > 0) {
$lockUntil = $this->loginfailuretime + 1800; // 锁定 30 分钟
return time() < $lockUntil;
}
return false;
}
// 记录登录成功
public function recordLogin($ip)
{
$this->login_ip = $ip;
$this->login_time = date('Y-m-d H:i:s');
$this->login_count += 1;
$this->loginfailure = 0; // 重置失败计数
$this->loginfailuretime = 0; // 清除锁定时间
$this->save();
}
// 记录登录失败(统一使用 loginfailure / loginfailuretime 字段)
public function recordLoginFail($ip)
{
$this->loginfailure += 1;
$this->loginfailuretime = time(); // 记录最后一次失败时间,满 5 次即锁定 30 分钟
$this->save();
}
// 重置密码
public function resetPassword($newPassword)
{
$this->password = $newPassword; // 会触发setPasswordAttr自动加密
$this->need_reset = 0; // 标记无需重置
return $this->save();
}
// public function getAvatarAttr($value, $data)
// {
// return '/static/common/images/avatar.jpg';
// }
/**
* 表结构自愈:建表 + 关键列兜底(install.sql 为事实源)。
* 查询/写入前调用,避免老库缺表导致 1146。
*/
public static function ensureSchema(): void
{
$prefix = BaseModel::currentPrefix();
foreach (['member', 'member_bill', 'member_group', 'member_group_access', 'member_group_rule', 'member_log', 'member_profile', 'member_rule', 'member_wallets'] as $t) {
BaseModel::ensureTableFromInstall($prefix, $t);
}
}
}