413 lines
15 KiB
PHP
413 lines
15 KiB
PHP
<?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' => '密码错误']);
|
||
}
|
||
|
||
// 如果传了设备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());
|
||
}
|
||
}
|
||
}
|
||
|