Files
CKB-Interface/application/store/controller/AuthController.php
2026-03-24 10:39:16 +08:00

413 lines
15 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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());
}
}
}