request->param('account', '')); $password = trim($this->request->param('password', '')); $typeId = (int)$this->request->param('typeId', 2); // 类型ID,默认为2 $deviceId = trim($this->request->param('deviceId', '')); // 设备ID(可选,仅APP端传递) // 验证必填参数 if (empty($account) || empty($password)) { return json(['code' => 400, 'msg' => '账号和密码不能为空']); } try { // 查找账号(门店端使用 ck_users 表,typeId=2) $accountInfo = Db::name('users') ->where(function($query) use ($account) { $query->where('account', $account) ->whereOr('phone', $account); }) ->where('typeId', 2) // 门店端固定为2 ->where('deleteTime', 0) ->find(); if (empty($accountInfo)) { return json(['code' => 404, 'msg' => '账号不存在']); } // 验证密码(支持MD5和本地加密密码) $passwordMd5 = md5($password); $passwordMatch = false; if (!empty($accountInfo['passwordMd5']) && $accountInfo['passwordMd5'] === $passwordMd5) { $passwordMatch = true; } elseif (!empty($accountInfo['passwordLocal'])) { // 验证本地加密密码(需要localDecrypt函数) if (function_exists('localDecrypt')) { $decryptedPassword = localDecrypt($accountInfo['passwordLocal']); if ($decryptedPassword === $password) { $passwordMatch = true; } } } if (!$passwordMatch) { return json(['code' => 401, 'msg' => '密码错误']); } // 如果传了设备ID(APP端),验证设备是否存在 if (!empty($deviceId)) { $device = Db::name('device') ->where('deviceImei', $deviceId) ->where('companyId', $accountInfo['companyId']) ->where('deleteTime', 0) ->find(); if (empty($device)) { return json(['code' => 404, 'msg' => '设备不存在或与账号不匹配']); } } // 生成JWT令牌(与旧版一致) $token = JwtUtil::createToken($accountInfo, 86400 * 30); // 30天过期 $tokenExpired = time() + 86400 * 30; // 更新账号最后登录信息(ck_users表没有lastLoginTime和lastLoginIp字段,只更新密码和updateTime) Db::name('users') ->where('id', $accountInfo['id']) ->update([ 'passwordMd5' => $passwordMd5, 'updateTime' => time() ]); // 准备返回的会员信息 $memberInfo = [ 'id' => $accountInfo['id'], 'account' => $accountInfo['account'] ?? '', 'username' => $accountInfo['username'] ?? '', 'phone' => $accountInfo['phone'] ?? '', 'avatar' => $accountInfo['avatar'] ?? '', 'companyId' => $accountInfo['companyId'] ?? 0, 'typeId' => $accountInfo['typeId'] ?? 2, ]; // 记录登录日志 $this->recordLoginLog($accountInfo['id'], $deviceId, '账号密码登录成功'); return json([ 'code' => 200, 'msg' => '登录成功', 'data' => [ 'token' => $token, 'token_expired' => $tokenExpired, 'member' => $memberInfo ] ]); } catch (\Exception $e) { // 记录错误日志 $this->recordLoginLog(0, $deviceId, '账号密码登录失败:' . $e->getMessage()); return json([ 'code' => 500, 'msg' => '登录失败:' . $e->getMessage() ]); } } /** * 免密登录(基于设备ID) * @return \think\response\Json */ public function noPasswordLogin() { // 获取设备ID $deviceId = trim($this->request->param('deviceId', '')); if (empty($deviceId)) { return json(['code' => 400, 'msg' => '设备ID不能为空']); } try { // 根据设备IMEI查找设备信息 $device = Db::name('device') ->where('deviceImei', $deviceId) ->where('deleteTime', 0) ->find(); if (empty($device)) { return json(['code' => 404, 'msg' => '设备不存在或已被删除']); } // 检查设备是否在线 if ($device['alive'] != 1) { return json(['code' => 403, 'msg' => '设备未在线,请确保设备已连接']); } // 获取设备关联的公司ID $companyId = $device['companyId']; // 查找公司账号信息(通过device_user关联查找用户) // 门店端使用 ck_users 表,通过 device_user 关联 $account = Db::name('users')->alias('u') ->join('device_user du', 'u.id = du.userId AND u.companyId = du.companyId') ->where([ 'du.deviceId' => $device['id'], 'u.companyId' => $companyId, 'u.typeId' => 2, // 门店端固定为2 'u.deleteTime' => 0, 'du.deleteTime' => 0 ]) ->field('u.*') ->find(); if (empty($account)) { return json(['code' => 404, 'msg' => '未找到关联的账号信息,请先绑定设备']); } // 生成JWT令牌(与旧版一致) $token = JwtUtil::createToken($account, 86400 * 30); // 30天过期 $tokenExpired = time() + 86400 * 30; // 更新账号最后登录信息(ck_users表没有lastLoginTime和lastLoginIp字段) Db::name('users') ->where('id', $account['id']) ->update([ 'updateTime' => time() ]); // 准备返回的会员信息 $memberInfo = [ 'id' => $account['id'], 'account' => $account['account'] ?? '', 'username' => $account['username'] ?? '', 'phone' => $account['phone'] ?? '', 'avatar' => $account['avatar'] ?? '', 'companyId' => $companyId, 'typeId' => $account['typeId'] ?? 2, ]; // 记录登录日志 $this->recordLoginLog($account['id'], $deviceId, '免密登录成功'); return json([ 'code' => 200, 'msg' => '登录成功', 'data' => [ 'token' => $token, 'token_expired' => $tokenExpired, 'member' => $memberInfo ] ]); } catch (\Exception $e) { // 记录错误日志 $this->recordLoginLog(0, $deviceId, '免密登录失败:' . $e->getMessage()); return json([ 'code' => 500, 'msg' => '登录失败:' . $e->getMessage() ]); } } /** * 发送短信验证码 * @return \think\response\Json */ public function sendVerificationCode() { // 获取参数 $mobile = trim($this->request->param('mobile', '')); $type = trim($this->request->param('type', 'login')); // login/register/reset // 验证必填参数 if (empty($mobile)) { return json(['code' => 400, 'msg' => '手机号不能为空']); } try { $smsService = new SmsService(); $result = $smsService->sendVerificationCode($mobile, $type); if ($result['success']) { return json([ 'code' => 200, 'msg' => $result['message'], 'data' => $result['data'] ?? [] ]); } else { return json([ 'code' => 400, 'msg' => $result['message'] ]); } } catch (\Exception $e) { return json([ 'code' => 500, 'msg' => '发送失败:' . $e->getMessage() ]); } } /** * 手机号验证码登录 * @return \think\response\Json */ public function mobileLogin() { // 获取参数 $mobile = trim($this->request->param('mobile', '')); $code = trim($this->request->param('code', '')); $isEncrypted = $this->request->param('is_encrypted', false); // 验证必填参数 if (empty($mobile) || empty($code)) { return json(['code' => 400, 'msg' => '手机号和验证码不能为空']); } try { // 1. 验证短信验证码 $smsService = new SmsService(); $verifyResult = $smsService->verifyCode($mobile, $code, 'login'); if (!$verifyResult['success']) { return json(['code' => 400, 'msg' => $verifyResult['message']]); } // 2. 查找或创建账号(根据手机号) // 门店端使用 ck_users 表,typeId=2 $account = Db::name('users') ->where('phone', $mobile) ->where('typeId', 2) // 门店端固定为2 ->where('deleteTime', 0) ->find(); // 如果账号不存在,自动创建(新用户注册) if (empty($account)) { // 注意:新用户注册需要companyId,这里暂时设为0,实际应该从设备或其他地方获取 $accountId = Db::name('users')->insertGetId([ 'account' => $mobile, // 使用手机号作为账号 'username' => '用户' . substr($mobile, -4), // 默认昵称 'phone' => $mobile, 'passwordMd5' => '', // 手机验证码登录不需要密码 'avatar' => 'https://img.icons8.com/color/512/circled-user-male-skin-type-7.png', 'isAdmin' => 0, 'companyId' => 0, // 新用户默认companyId为0,后续需要绑定设备或公司 'typeId' => 2, // 门店端固定为2 'status' => 1, // 默认可用 'balance' => 0, 'tokens' => 0, 'createTime' => time(), 'updateTime' => time(), 'deleteTime' => 0 ]); // 重新查询账号信息 $account = Db::name('users')->where('id', $accountId)->find(); // 新用户自动生成对外 API Key try { UserApiKeyService::bindOrGet((int)$accountId); } catch (\Exception $e) { \think\facade\Log::error('新用户自动生成 apiKey 失败:' . $e->getMessage()); } // 记录注册日志 $this->recordLoginLog($accountId, '', '手机验证码注册成功'); } // 3. 生成JWT令牌(与旧版一致) $token = JwtUtil::createToken($account, 86400 * 30); // 30天过期 $tokenExpired = time() + 86400 * 30; // 4. 更新账号最后登录信息(ck_users表没有lastLoginTime和lastLoginIp字段) Db::name('users') ->where('id', $account['id']) ->update([ 'updateTime' => time() ]); // 5. 准备返回的用户信息 $userInfo = [ 'id' => $account['id'], 'account' => $account['account'] ?? '', 'username' => $account['username'] ?? '', 'phone' => $mobile, 'avatar' => $account['avatar'] ?? '', 'companyId' => $account['companyId'] ?? 0, 'typeId' => $account['typeId'] ?? 2, ]; // 记录登录日志 $this->recordLoginLog($account['id'], '', '手机验证码登录成功'); return json([ 'code' => 200, 'msg' => '登录成功', 'data' => [ 'token' => $token, 'token_expired' => $tokenExpired, 'userInfo' => $userInfo ] ]); } catch (\Exception $e) { // 记录错误日志 $this->recordLoginLog(0, '', '手机验证码登录失败:' . $e->getMessage()); return json([ 'code' => 500, 'msg' => '登录失败:' . $e->getMessage() ]); } } /** * 记录登录日志 * @param int $accountId 账号ID * @param string $deviceId 设备ID * @param string $message 日志信息 */ private function recordLoginLog($accountId, $deviceId, $message) { try { // 使用ThinkPHP的日志记录功能,避免表不存在的问题 \think\facade\Log::info('Store登录日志', [ 'accountId' => $accountId, 'deviceId' => $deviceId, 'action' => 'STORE_LOGIN', 'message' => $message, 'ip' => $this->request->ip(), 'time' => date('Y-m-d H:i:s') ]); // 如果存在operation_log表,也可以记录到数据库 // Db::name('operation_log')->insert([ // 'accountId' => $accountId, // 'deviceId' => $deviceId, // 'action' => 'STORE_LOGIN', // 'message' => $message, // 'ip' => $this->request->ip(), // 'createTime' => time() // ]); } catch (\Exception $e) { // 日志记录失败不影响主流程 \think\facade\Log::error('登录日志记录失败:' . $e->getMessage()); } } }