Files
YwxAppThink/ywxapp/model/MemberUser.php
T
ywxapp 1d49e6f5ee feat: 公共表更名 common/member 前缀 + 插件命令自动加载 + 版本 1.1.1
- 18 张公共表更名(addon/attachment/configure/links/spider_log/spider_stat/sms/notice/ad/task/prop/medal/help/card -> common_*,member_wallets->member_wallet,score_rule/score_log -> member_*,addon_config->common_addonconf),模型全部对齐新表名,Db 直引用清零
- Attachment 模型补  表名绑定,修复富文本上传查 wxapp_attachment 1146 隐患
- install.sql + 迁移 SQL:backend_admin/backend_role delete_at 默认 0,修复软删除(NULL != 0)误过滤导致后台菜单为空
- AppService::boot() 支持插件 info.php 声明 commands 自动注册插件命令(psr-4 自动加载,坏类名自动跳过)
- 各插件(haonav/mqttbroker/wxchat/articles/blog/forum 等)字段与配置同步调整
- 框架版本 1.1.0 -> 1.1.1
2026-08-20 20:19:15 +08:00

191 lines
5.8 KiB
PHP

<?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,
'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');
}
// 密码加密:bcrypt 自带盐,无需独立 salt 列
// 兼容旧账户(若库里是 password_hash(value.salt) 的老哈希,
// 这里统一改为 password_hash(value),老账户需要先重置密码)。
public function setPasswordAttr($value)
{
return password_hash($value, PASSWORD_DEFAULT);
}
// 验证密码:bcrypt 哈希内已含盐,直接验证即可
public function checkPassword($password)
{
if (empty($this->password)) {
return false;
}
return password_verify($password, $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_user', 'member_bill', 'member_group', 'member_group_access', 'member_group_rule', 'member_log', 'member_profile', 'member_rule', 'member_wallet'] as $t) {
BaseModel::ensureTableFromInstall($prefix, $t);
}
}
}