diff --git a/app/admin/controller/LoginController.php b/app/admin/controller/LoginController.php index 6a04180..90c778a 100644 --- a/app/admin/controller/LoginController.php +++ b/app/admin/controller/LoginController.php @@ -13,6 +13,7 @@ use app\service\JwtService; use app\model\AdminUser; use app\model\System\SystemSiteSettings; +use app\model\Tenant\Tenant; class LoginController extends BaseController { @@ -35,14 +36,10 @@ class LoginController extends BaseController try { $data = $this->request->param(); - if (isset($data['email'])) { - $data['account'] = $data['email']; - } elseif (isset($data['phone'])) { - $data['account'] = $data['phone']; - } - + // 1. 先验证租户名称是否传入(新增:必须传租户名称) try { $this->validate($data, [ + 'tenant_name|租户名称' => 'require|length:1,128', // 匹配租户表tenant字段长度 'account|账号' => 'require|length:3,32', 'password|密码' => 'require|length:6,32' ]); @@ -54,44 +51,80 @@ class LoginController extends BaseController ]); } - $user = AdminUser::where('account', $data['account']) - ->where('status', 1) - ->where('delete_time', null) - ->find(); + // 2. 处理账号兼容(邮箱/手机号转account) + if (isset($data['email'])) { + $data['account'] = $data['email']; + } elseif (isset($data['phone'])) { + $data['account'] = $data['phone']; + } - if (!$user) { + // 3. 查询租户ID(新增:只查正常状态的租户 status=1) + $tenant = Tenant::where('tenant_name', $data['tenant_name']) + ->where('status', 1) // 过滤停用/删除的租户 + ->field(['id', 'tenant_name']) // 只查需要的字段,提升性能 + ->find(); // ThinkPHP 使用 find() + + + + // 4. 验证租户是否存在 + + if (!$tenant) { + $this->logFail('登录管理', '登录', '租户不存在或已禁用,租户名称:' . $data['tenant_name']); return json([ 'code' => 401, - 'msg' => '账号不存在或已禁用' + 'msg' => '租户不存在或已禁用' + ]); + } + $tenant_id = $tenant->id; + $tenant_name = $tenant->tenant_name; + + // 5. 查询用户(新增:关联租户ID,确保用户属于该租户) + $user = AdminUser::where('account', $data['account']) + ->where('tenant_id', $tenant_id) // 核心:验证用户所属租户 + ->where('status', 1) + ->find(); + + // 6. 验证用户是否存在 + if (!$user) { + $this->logFail('登录管理', '登录', '账号不存在/已禁用,或不属于当前租户,账号:' . $data['account'] . ',租户:' . $tenant_name); + return json([ + 'code' => 401, + 'msg' => '账号不存在或已禁用,或不属于当前租户' ]); } + // 7. 验证密码 if (md5($data['password']) !== $user['password']) { + $this->logFail('登录管理', '登录', '密码错误,账号:' . $data['account'] . ',租户:' . $tenant_name); return json([ 'code' => 401, 'msg' => '密码错误' ]); } - // 更新登录次数和IP,确保 login_count 不为 null + // 8. 更新登录次数和IP try { $loginCount = isset($user['login_count']) && $user['login_count'] !== null ? (int)$user['login_count'] : 0; AdminUser::where('id', $user['id'])->update([ 'login_count' => $loginCount + 1, - 'last_login_ip' => $this->request->ip() + 'last_login_ip' => $this->request->ip(), + 'last_login_time' => date('Y-m-d H:i:s') // 新增:记录最后登录时间,更实用 ]); } catch (\Exception $e) { - // 更新登录信息失败不影响登录流程 error_log('更新登录信息失败: ' . $e->getMessage()); } + // 9. 组装用户信息(新增:加入租户ID和租户名称) $userInfo = [ 'id' => $user['id'], 'account' => $user['account'], 'name' => $user['name'], - 'group_id' => $user['group_id'] + 'group_id' => $user['group_id'], + 'tenant_id' => $tenant_id, // 新增:租户ID + 'tenant' => $tenant // 新增:租户名称 ]; + // 10. 生成Token(Token中已包含租户信息,后续可通过Token解析获取) try { $token = $this->generateToken($userInfo); } catch (\Exception $e) { @@ -102,33 +135,48 @@ class LoginController extends BaseController ]); } - // 记录日志,但不影响登录流程 + // 11. 写入用户数据缓存(核心:缓存包含租户信息,示例用Redis,可根据你的缓存工具调整) try { - $this->logSuccess('登录管理', '登录', ['id' => $user['id']], $userInfo); + $cacheKey = 'admin_user_' . $user['id'] . '_' . $tenant_id; // 缓存键加入租户ID,避免多租户冲突 + $cacheExpire = 86400 * 7; // 缓存7天,可根据需求调整 + // 写入缓存(这里假设你使用thinkphp的Cache类,若用其他工具可替换) + \think\facade\Cache::set($cacheKey, $userInfo, $cacheExpire); + } catch (\Exception $e) { + error_log('用户缓存写入失败: ' . $e->getMessage()); + // 缓存写入失败不影响登录,但记录日志 + } + + // 12. 记录登录成功日志 + try { + $this->logSuccess('登录管理', '登录', [ + 'id' => $user['id'], + 'tenant_id' => $tenant_id, + 'tenant' => $tenant + ], $userInfo); } catch (\Exception $e) { - // 日志记录失败不影响登录 error_log('登录日志记录失败: ' . $e->getMessage()); } + // 13. 返回登录结果(包含租户信息) return json([ 'code' => 200, 'msg' => '登录成功', 'data' => [ 'token' => $token, - 'user' => $userInfo + 'user' => $userInfo // 前端可直接获取租户ID和名称 ] ]); } catch (\Exception $e) { - // 捕获所有未预期的错误 - error_log('登录失败: ' . $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine()); + $errorMsg = $e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine(); + error_log('登录失败: ' . $errorMsg); try { - $this->logFail('登录管理', '登录', $e->getMessage()); + $this->logFail('登录管理', '登录', $errorMsg); } catch (\Exception $logError) { error_log('记录登录失败日志也失败: ' . $logError->getMessage()); } return json([ 'code' => 500, - 'msg' => '登录失败,请稍后重试' + 'msg' => '登录失败:' . $e->getMessage() ]); } } @@ -227,11 +275,11 @@ class LoginController extends BaseController return JwtService::getUserFromHeader($this->request->header('Authorization', '')); } - + public function loginInfo() { - + $loginInfo = SystemSiteSettings::select(); return json([ 'code' => 200, diff --git a/app/model/Tenant/Tenant.php b/app/model/Tenant/Tenant.php new file mode 100644 index 0000000..5bfdb53 --- /dev/null +++ b/app/model/Tenant/Tenant.php @@ -0,0 +1,38 @@ + +// +---------------------------------------------------------------------- + +namespace app\model\Tenant; + +use think\Model; + +/** + * 租户模型 + */ +class Tenant extends Model +{ + // 数据库表名 + protected $name = 'mete_tenant'; + + // 字段类型转换 + protected $type = [ + 'id' => 'integer', + 'tenant_code' => 'string', + 'tenant_name' => 'string', + 'contact_person' => 'string', + 'contact_phone' => 'string', + 'contact_email' => 'string', + 'address' => 'string', + 'status' => 'integer', + 'create_time' => 'datetime', + 'update_time' => 'datetime', + 'remark' => 'string', + ]; +} diff --git a/runtime/cache/01/296e54db593b14cdb27079fd1ee7fc.php b/runtime/cache/01/296e54db593b14cdb27079fd1ee7fc.php new file mode 100644 index 0000000..adc18db --- /dev/null +++ b/runtime/cache/01/296e54db593b14cdb27079fd1ee7fc.php @@ -0,0 +1,4 @@ + +a:6:{s:2:"id";i:2;s:7:"account";s:10:"hero920103";s:4:"name";s:9:"李志强";s:8:"group_id";i:1;s:9:"tenant_id";i:1;s:6:"tenant";O:23:"app\model\Tenant\Tenant":26:{s:3:"get";a:2:{s:2:"id";i:1;s:11:"tenant_name";s:39:"连云港云泽广告传媒有限公司";}s:4:"data";a:2:{s:2:"id";i:1;s:11:"tenant_name";s:39:"连云港云泽广告传媒有限公司";}s:6:"origin";a:2:{s:2:"id";i:1;s:11:"tenant_name";s:39:"连云港云泽广告传媒有限公司";}s:8:"relation";a:0:{}s:8:"together";a:0:{}s:5:"allow";a:0:{}s:8:"withAttr";a:0:{}s:6:"schema";a:11:{s:2:"id";s:7:"integer";s:11:"tenant_code";s:6:"string";s:11:"tenant_name";s:6:"string";s:14:"contact_person";s:6:"string";s:13:"contact_phone";s:6:"string";s:13:"contact_email";s:6:"string";s:7:"address";s:6:"string";s:6:"status";s:7:"integer";s:11:"create_time";s:8:"datetime";s:11:"update_time";s:8:"datetime";s:6:"remark";s:6:"string";}s:10:"updateTime";s:11:"update_time";s:10:"createTime";s:11:"create_time";s:6:"suffix";s:0:"";s:8:"validate";s:0:"";s:4:"type";a:0:{}s:8:"readonly";a:0:{}s:6:"disuse";a:0:{}s:6:"hidden";a:0:{}s:7:"visible";a:0:{}s:6:"append";a:0:{}s:7:"mapping";a:0:{}s:6:"strict";b:1;s:8:"bindAttr";a:0:{}s:12:"autoRelation";a:0:{}s:18:"autoWriteTimestamp";b:1;s:10:"dateFormat";s:11:"Y-m-d H:i:s";s:2:"pk";s:2:"id";s:6:"exists";b:1;}} \ No newline at end of file