新版数智员工

This commit is contained in:
wong
2026-03-24 10:39:16 +08:00
parent 1853934d85
commit 7fb61b77a4
123 changed files with 38491 additions and 1018 deletions

View File

@@ -0,0 +1,412 @@
<?php
namespace app\store\controller;
use think\Controller;
use think\Db;
use app\store\service\SmsService;
use app\common\util\JwtUtil;
use app\common\service\UserApiKeyService;
/**
* Store模块 - 认证控制器
* Class AuthController
* @package app\store\controller
*/
class AuthController extends Controller
{
/**
* 账号密码登录
* @return \think\response\Json
*/
public function passwordLogin()
{
// 获取参数
$account = trim($this->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' => '密码错误']);
}
// 如果传了设备IDAPP端验证设备是否存在
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());
}
}
}