From df72ab8095e0eaa8beb2088322128f2ad2829caa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=B3=E6=B8=85=E7=88=BD?= Date: Wed, 30 Apr 2025 14:52:51 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E5=85=AC=E5=85=B1=E7=99=BB=E5=BD=95=20-=20?= =?UTF-8?q?=E6=89=8B=E6=9C=BA=E5=90=8E=E3=80=81=E8=B4=A6=E5=8F=B7=E5=90=8C?= =?UTF-8?q?=E6=97=B6=E5=8F=AF=E4=BD=9C=E4=B8=BA=E7=99=BB=E5=BD=95=E6=9D=A1?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/common/config/route.php | 14 +- .../common/controller/BaseController.php | 12 ++ .../controller/PasswordLoginController.php | 130 ++++++++++++++++ .../common/controller/SendCodeController.php | 39 +++++ Server/application/common/model/User.php | 68 +------- .../common/service/AuthService.php | 145 +++++++----------- .../application/common/service/SmsService.php | 51 +++--- .../cunkebao/controller/BaseController.php | 2 +- 8 files changed, 275 insertions(+), 186 deletions(-) create mode 100644 Server/application/common/controller/BaseController.php create mode 100644 Server/application/common/controller/PasswordLoginController.php create mode 100644 Server/application/common/controller/SendCodeController.php diff --git a/Server/application/common/config/route.php b/Server/application/common/config/route.php index c7f823d8..8b133a1a 100644 --- a/Server/application/common/config/route.php +++ b/Server/application/common/config/route.php @@ -6,17 +6,17 @@ use think\facade\Route; // 定义RESTful风格的API路由 - 认证相关 Route::group('v1/auth', function () { // 无需认证的接口 - Route::post('login', 'app\\common\\controller\\Auth@login'); // 账号密码登录 - Route::post('mobile-login', 'app\\common\\controller\\Auth@mobileLogin'); // 手机号验证码登录 - Route::post('code', 'app\\common\\controller\\Auth@sendCode'); // 发送验证码 + Route::post('login', 'app\common\controller\PasswordLoginController@index'); // 账号密码登录 + Route::post('mobile-login', 'app\common\controller\Auth@mobileLogin'); // 手机号验证码登录 + Route::post('code', 'app\common\controller\Auth@SendCodeController'); // 发送验证码 // 需要JWT认证的接口 - Route::get('info', 'app\\common\\controller\\Auth@info')->middleware(['jwt']); // 获取用户信息 - Route::post('refresh', 'app\\common\\controller\\Auth@refresh')->middleware(['jwt']); // 刷新令牌 + Route::get('info', 'app\common\controller\Auth@info')->middleware(['jwt']); // 获取用户信息 + Route::post('refresh', 'app\common\controller\Auth@refresh')->middleware(['jwt']); // 刷新令牌 }); // 附件上传相关路由 Route::group('v1/', function () { - Route::post('attachment/upload', 'app\\common\\controller\\Attachment@upload'); // 上传附件 - Route::get('attachment/:id', 'app\\common\\controller\\Attachment@info'); // 获取附件信息 + Route::post('attachment/upload', 'app\common\controller\Attachment@upload'); // 上传附件 + Route::get('attachment/:id', 'app\common\controller\Attachment@info'); // 获取附件信息 })->middleware(['jwt']); \ No newline at end of file diff --git a/Server/application/common/controller/BaseController.php b/Server/application/common/controller/BaseController.php new file mode 100644 index 00000000..81dcf83c --- /dev/null +++ b/Server/application/common/controller/BaseController.php @@ -0,0 +1,12 @@ +where('phone', $account)->whereOr('account', $account); + }) + ->where(function ($query) use ($typeId) { + $query->where('status', 1)->where('typeId', $typeId); + })->find(); + + return $user; + } + + /** + * 获取用户信息 + * + * @param string $account 账号(手机号) + * @param string $password 密码(可能是加密后的) + * @param int $typeId 身份信息 + * @return array|null + */ + protected function getUser(string $account, string $password, int $typeId): array + { + $user = $this->getUserProfileWithAccountAndType($account, $typeId); + + if (!$user) { + throw new \Exception('用户不存在或已禁用', 403); + } + + if ($user->passwordMd5 !== md5($password)) { + throw new \Exception('账号或密码错误', 403); + } + + return $user->toArray(); + } + + /** + * 数据验证 + * + * @param array $params + * @return $this + * @throws \Exception + */ + protected function dataValidate(array $params): self + { + $validate = Validate::make([ + 'account' => 'require', + 'password' => 'require|length:6,64', + 'typeId' => 'require|in:1,2', + ], [ + 'account.require' => '账号不能为空', + 'password.require' => '密码不能为空', + 'password.length' => '密码长度必须在6-64个字符之间', + 'typeId.require' => '用户类型不能为空', + 'typeId.in' => '用户类型错误', + ]); + + if (!$validate->check($params)) { + throw new \Exception($validate->getError(), 400); + } + + return $this; + } + + /** + * 用户登录 + * + * @param string $account 账号(手机号) + * @param string $password 密码(可能是加密后的) + * @param string $typeId 登录IP + * @return array + * @throws \Exception + */ + protected function doLogin(string $account, string $password, int $typeId): array + { + // 获取用户信息 + $member = $this->getUser($account, $password, $typeId); + + // 生成JWT令牌 + $token = JwtUtil::createToken($member, 7200); + $token_expired = time() + 7200; + + return compact('member', 'token', 'token_expired'); + } + + /** + * 用户登录 + * + * @return Json + */ + public function index() + { + $params = $this->request->only(['account', 'password', 'typeId']); + + try { + $result = $this->dataValidate($params)->doLogin( + $params['account'], + $params['password'], + $params['typeId'] + ); + + return ResponseHelper::success($result, '登录成功'); + } catch (Exception $e) { + return ResponseHelper::error($e->getMessage(), $e->getCode()); + } + } +} \ No newline at end of file diff --git a/Server/application/common/controller/SendCodeController.php b/Server/application/common/controller/SendCodeController.php new file mode 100644 index 00000000..7dc58d23 --- /dev/null +++ b/Server/application/common/controller/SendCodeController.php @@ -0,0 +1,39 @@ +request->only(['account', 'type']); + + // 参数验证 + $validate = validate('common/Auth'); + if (!$validate->scene('send_code')->check($params)) { + return ResponseHelper::error($validate->getError()); + } + + try { + // 调用发送验证码服务 + $result = $this->authService->sendLoginCode( + $params['account'], + $params['type'] + ); + return ResponseHelper::success($result, '验证码发送成功'); + } catch (\Exception $e) { + return ResponseHelper::error($e->getMessage()); + } + } +} \ No newline at end of file diff --git a/Server/application/common/model/User.php b/Server/application/common/model/User.php index bd37c17f..c3535d5c 100644 --- a/Server/application/common/model/User.php +++ b/Server/application/common/model/User.php @@ -1,4 +1,5 @@ 'integer', - 'isAdmin' => 'integer', - 'companyId' => 'integer', - 'typeId' => 'integer', - 'lastLoginTime' => 'integer', - 'status' => 'integer', - 'createTime' => 'integer', - 'updateTime' => 'integer', - 'deleteTime' => 'integer' - ]; - - /** - * 通过手机号获取用户信息 - * @param string $account 手机号 - * @return array|null - */ - public static function getUserByMobile($account) - { - // 查询用户 - $user = self::where('account', $account) - ->where('status', 1) - ->find(); - - if (!$user) { - return null; - } - - // 用手机号当做默认用户名(如果没有设置用户名) - $username = $user->username ?: $user->account; - // 默认头像地址 - $avatar = $user->avatar ?: ''; - - return [ - 'id' => $user->id, - 'username' => $username, - 'account' => $user->account, - 'avatar' => $avatar, - 'isAdmin' => $user->isAdmin, - 'companyId' => $user->companyId, - 'typeId' => $user->typeId, - 'lastLoginIp' => $user->lastLoginIp, - 'lastLoginTime' => $user->lastLoginTime - ]; - } } \ No newline at end of file diff --git a/Server/application/common/service/AuthService.php b/Server/application/common/service/AuthService.php index b94c57cc..a2eac90e 100644 --- a/Server/application/common/service/AuthService.php +++ b/Server/application/common/service/AuthService.php @@ -1,85 +1,59 @@ where('phone', $account)->whereOr('account', $account); + }) + ->where(function ($query) use ($typeId) { + $query->where('status', 1)->where('typeId', $typeId); + })->find(); + + return $user; + } + /** * 获取用户信息 + * * @param string $account 账号(手机号) * @param string $password 密码(可能是加密后的) * @param int $typeId 身份信息 * @return array|null */ - protected function getUser($account, $password, $typeId) + protected function getUser(string $account, string $password, int $typeId): array { - // 查询用户 - $user = User::where('account', $account) - ->where('typeId', $typeId) - ->where('status', 1) - ->find(); + $user = $this->getUserProfileWithAccountAndType($account, $typeId); if (!$user) { - // 记录日志 - \think\facade\Log::info('用户不存在或已禁用', ['account' => $account]); - return null; + throw new \Exception('用户不存在或已禁用', 403); } - // 记录密码验证信息 - \think\facade\Log::info('密码验证', [ - 'account' => $account, - 'input_password' => $password, - 'stored_hash' => $user->passwordMd5, - ]); - - // 验证密码 - $isValid = ($user->passwordMd5 == md5($password)); - - \think\facade\Log::info('密码验证结果', [ - 'account' => $account, - 'is_valid' => $isValid, - ]); - - if (!$isValid) { - return null; + if ($user->passwordMd5 !== md5($password)) { + throw new \Exception('账号或密码错误', 403); } - // 更新登录信息 - $user->lastLoginIp = request()->ip(); - $user->lastLoginTime = time(); - $user->save(); - - // 用手机号当做默认用户名(如果没有设置用户名) - $username = $user->username ?: $user->account; - - return [ - 'id' => $user->id, - 'username' => $username, - 'account' => $user->account, - 'avatar' => $user->avatar, - 'isAdmin' => $user->isAdmin, - 'companyId' => $user->companyId, - 'typeId' => $user->typeId, - 'lastLoginIp' => $user->lastLoginIp, - 'lastLoginTime' => $user->lastLoginTime - ]; + return $user->toArray(); } /** @@ -92,39 +66,28 @@ class AuthService /** * 用户登录 + * * @param string $account 账号(手机号) * @param string $password 密码(可能是加密后的) * @param string $ip 登录IP * @return array * @throws \Exception */ - public function login($account, $password, $typeId, $ip) + public function login(string $account, string $password, int $typeId, string $ip) { // 获取用户信息 - $user = $this->getUser($account, $password, $typeId); + $member = $this->getUser($account, $password, $typeId); - if (empty($user)) { - // 记录登录失败 - Log::info('登录失败', ['account' => $account, 'ip' => $ip, 'is_encrypted' => true]); - throw new \Exception('账号或密码错误'); - } - // 生成JWT令牌 $token = JwtUtil::createToken($user, self::TOKEN_EXPIRE); - $expireTime = time() + self::TOKEN_EXPIRE; - - // 记录登录成功 - Log::info('登录成功', ['account' => $account, 'ip' => $ip]); - - return [ - 'token' => $token, - 'token_expired' => $expireTime, - 'member' => $user - ]; + $token_expired = time() + self::TOKEN_EXPIRE; + + return compact('member', 'token', 'token_expired'); } /** * 手机号验证码登录 + * * @param string $account 手机号 * @param string $code 验证码(可能是加密后的) * @param string $ip 登录IP @@ -137,14 +100,14 @@ class AuthService // 验证验证码 if (!$this->smsService->verifyCode($account, $code, 'login', $isEncrypted)) { Log::info('验证码验证失败', ['account' => $account, 'ip' => $ip, 'is_encrypted' => $isEncrypted]); - throw new \Exception('验证码错误或已过期'); + throw new \Exception('验证码错误或已过期', 404); } // 获取用户信息 $user = User::getUserByMobile($account); if (empty($user)) { Log::info('用户不存在', ['account' => $account, 'ip' => $ip]); - throw new \Exception('用户不存在'); + throw new \Exception('用户不存在', 404); } // 生成JWT令牌 @@ -163,6 +126,7 @@ class AuthService /** * 发送登录验证码 + * * @param string $account 手机号 * @param string $type 验证码类型 * @return array @@ -175,6 +139,7 @@ class AuthService /** * 获取用户信息 + * * @param array $userInfo JWT中的用户信息 * @return array * @throws \Exception @@ -184,16 +149,17 @@ class AuthService if (empty($userInfo)) { throw new \Exception('获取用户信息失败'); } - + // 移除不需要返回的字段 unset($userInfo['exp']); unset($userInfo['iat']); - + return $userInfo; } /** * 刷新令牌 + * * @param array $userInfo JWT中的用户信息 * @return array * @throws \Exception @@ -203,15 +169,15 @@ class AuthService if (empty($userInfo)) { throw new \Exception('刷新令牌失败'); } - + // 移除过期时间信息 unset($userInfo['exp']); unset($userInfo['iat']); - + // 生成新令牌 $token = JwtUtil::createToken($userInfo, self::TOKEN_EXPIRE); $expireTime = time() + self::TOKEN_EXPIRE; - + return [ 'token' => $token, 'token_expired' => $expireTime @@ -220,52 +186,53 @@ class AuthService /** * 获取系统授权信息,使用缓存存储10分钟 + * * @return string */ public static function getSystemAuthorization() { // 定义缓存键名 $cacheKey = 'system_authorization_token'; - + // 尝试从缓存获取授权信息 $authorization = Cache::get($cacheKey); //$authorization = 'mYpVVhPY7PxctvYw1pn1VCTS2ck0yZG8q11gAiJrRN_D3q7KXXBPAfXoAmqs7kKHeaAx-h4GB7DiqVIQJ09HiXVhaQT6PtgLX3w8YV16erThC-lG1fyJB4DJxu-QxA3Q8ogSs1WFOa8aAXD1QQUZ7Kbjkw_VMLL4lrfe0Yjaqy3DnO7aL1xGnNjjX8P5uqCAZgHKlN8NjuDEGyYvXygW1YyoK9pNpwvq-6DYKjLWdmbHvFaAybHf-hU1XyrFavZqcZYxIoVXjfJ5ASp4XxeCWqMCzwtSoz9RAvwLAlNxGweowtuyX9389ZaXI-zbqb2T0S8llg'; - // 如果缓存中没有或已过期,则重新获取 + // 如果缓存中没有或已过期,则重新获取 if (empty($authorization)) { try { // 从环境变量中获取API用户名和密码 $username = Env::get('api.username', ''); $password = Env::get('api.password', ''); - + if (empty($username) || empty($password)) { Log::error('缺少API用户名或密码配置'); return ''; } - + // 构建登录参数 $params = [ 'grant_type' => 'password', 'username' => $username, 'password' => $password ]; - + // 获取API基础URL $baseUrl = Env::get('api.wechat_url', ''); if (empty($baseUrl)) { Log::error('缺少API基础URL配置'); return ''; } - + // 调用登录接口获取token // 设置请求头 $headerData = ['client:system']; $header = setHeader($headerData, '', 'plain'); - $result = requestCurl($baseUrl . 'token', $params, 'POST',$header); + $result = requestCurl($baseUrl . 'token', $params, 'POST', $header); $result_array = handleApiResponse($result); if (isset($result_array['access_token']) && !empty($result_array['access_token'])) { $authorization = $result_array['access_token']; - + // 存入缓存,有效期10分钟(600秒) Cache::set($cacheKey, $authorization, 600); Cache::set('system_refresh_token', $result_array['refresh_token'], 600); @@ -281,7 +248,7 @@ class AuthService return ''; } } - + return $authorization; } } \ No newline at end of file diff --git a/Server/application/common/service/SmsService.php b/Server/application/common/service/SmsService.php index 91d08773..e0552f11 100644 --- a/Server/application/common/service/SmsService.php +++ b/Server/application/common/service/SmsService.php @@ -1,4 +1,5 @@ checkSendLimit($mobile, $type); - + // 生成验证码 $code = $this->generateCode(); - + // 缓存验证码 $this->saveCode($mobile, $code, $type); - + // 发送验证码(实际项目中对接短信平台) $this->doSend($mobile, $code, $type); - + // 记录日志 Log::info('发送验证码', [ 'mobile' => $mobile, 'type' => $type, 'code' => $code ]); - + return [ 'mobile' => $mobile, 'expire' => self::CODE_EXPIRE, @@ -54,7 +55,7 @@ class SmsService 'code' => $code ]; } - + /** * 验证验证码 * @param string $mobile 手机号 @@ -67,7 +68,7 @@ class SmsService { $cacheKey = $this->getCodeCacheKey($mobile, $type); $cacheCode = Cache::get($cacheKey); - + if (!$cacheCode) { Log::info('验证码不存在或已过期', [ 'mobile' => $mobile, @@ -75,15 +76,15 @@ class SmsService ]); return false; } - + // 验证码是否匹配 $isValid = false; - + if ($isEncrypted) { // 前端已加密,需要对缓存中的验证码进行相同的加密处理 $encryptedCacheCode = $this->encryptCode($cacheCode); $isValid = hash_equals($encryptedCacheCode, $code); - + // 记录日志 Log::info('加密验证码验证', [ 'mobile' => $mobile, @@ -95,7 +96,7 @@ class SmsService } else { // 未加密,直接比较 $isValid = ($cacheCode === $code); - + // 记录日志 Log::info('明文验证码验证', [ 'mobile' => $mobile, @@ -104,15 +105,15 @@ class SmsService 'is_valid' => $isValid ]); } - + // 验证成功后删除缓存 if ($isValid) { Cache::rm($cacheKey); } - + return $isValid; } - + /** * 检查发送频率限制 * @param string $mobile 手机号 @@ -122,24 +123,24 @@ class SmsService protected function checkSendLimit($mobile, $type) { $cacheKey = $this->getCodeCacheKey($mobile, $type); - + // 检查是否存在未过期的验证码 if (Cache::has($cacheKey)) { throw new \Exception('验证码已发送,请稍后再试'); } - + // 检查当日发送次数限制 $limitKey = "sms_limit:{$mobile}:" . date('Ymd'); $sendCount = Cache::get($limitKey, 0); - + if ($sendCount >= 10) { throw new \Exception('今日发送次数已达上限'); } - + // 更新发送次数 Cache::set($limitKey, $sendCount + 1, 86400); } - + /** * 生成随机验证码 * @return string @@ -149,7 +150,7 @@ class SmsService // 生成4位数字验证码 return sprintf("%0" . self::CODE_LENGTH . "d", mt_rand(0, pow(10, self::CODE_LENGTH) - 1)); } - + /** * 保存验证码到缓存 * @param string $mobile 手机号 @@ -161,7 +162,7 @@ class SmsService $cacheKey = $this->getCodeCacheKey($mobile, $type); Cache::set($cacheKey, $code, self::CODE_EXPIRE); } - + /** * 执行发送验证码 * @param string $mobile 手机号 @@ -175,7 +176,7 @@ class SmsService // 这里仅做模拟,返回成功 return true; } - + /** * 获取验证码缓存键名 * @param string $mobile 手机号 @@ -186,7 +187,7 @@ class SmsService { return "sms_code:{$mobile}:{$type}"; } - + /** * 加密验证码 * 使用与前端相同的加密算法 diff --git a/Server/application/cunkebao/controller/BaseController.php b/Server/application/cunkebao/controller/BaseController.php index 1189cafa..852858f9 100644 --- a/Server/application/cunkebao/controller/BaseController.php +++ b/Server/application/cunkebao/controller/BaseController.php @@ -32,7 +32,7 @@ class BaseController extends Controller * @return mixed * @throws \Exception */ - protected function getUserInfo(string $column = '') + protected function getUserInfo(?string $column = null) { $user = $this->request->userInfo; From af1b3b6988bb0f94b93a480dd85ccb6de44d4325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=B3=E6=B8=85=E7=88=BD?= Date: Wed, 30 Apr 2025 15:19:31 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E5=AD=98=E5=AE=A2=E5=AE=9D=20-=20=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=E5=AD=98=E5=82=A8=E7=99=BB=E5=BD=95=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E9=A2=9D=E5=A4=96=E8=BF=94=E5=9B=9E=E7=9A=84=20s2=5FaccountId?= =?UTF-8?q?=20=E5=AD=97=E6=AE=B5=E5=8F=82=E6=95=B0=EF=BC=8C=E7=94=A8?= =?UTF-8?q?=E4=BA=8E=E6=93=8D=E7=9B=98=E6=89=8B=E6=B7=BB=E5=8A=A0=E8=AE=BE?= =?UTF-8?q?=E5=A4=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cunkebao/app/devices/page.tsx | 52 +++++++++++++++++++++++++++-------- Cunkebao/app/login/page.tsx | 17 +++++------- 2 files changed, 47 insertions(+), 22 deletions(-) diff --git a/Cunkebao/app/devices/page.tsx b/Cunkebao/app/devices/page.tsx index dacaae01..81acd216 100644 --- a/Cunkebao/app/devices/page.tsx +++ b/Cunkebao/app/devices/page.tsx @@ -171,8 +171,6 @@ export default function DevicesPage() { setIsLoadingQRCode(true) setQrCodeImage("") // 清空当前二维码 - console.log("正在请求二维码..."); - // 发起请求获取二维码 - 直接使用fetch避免api工具添加基础URL const response = await fetch('http://yi.54word.com/v1/api/device/add', { method: 'POST', @@ -183,8 +181,6 @@ export default function DevicesPage() { body: JSON.stringify({}) }) - console.log("二维码请求响应状态:", response.status); - // 保存原始响应文本以便调试 const responseText = await response.text(); console.log("原始响应内容:", responseText); @@ -499,6 +495,43 @@ export default function DevicesPage() { router.push(`/devices/${deviceId}`); } + // 处理添加设备 + const handleAddDevice = async () => { + try { + const s2_accountId = localStorage.getItem('s2_accountId'); + if (!s2_accountId) { + toast.error('未获取到用户信息,请重新登录'); + return; + } + + const response = await fetch('/api/devices', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${localStorage.getItem('token')}`, + }, + body: JSON.stringify({ + imei: deviceImei, + memo: deviceName, + s2_accountId: s2_accountId, + }), + }); + + const data = await response.json(); + if (data.code === 200) { + toast.success('添加设备成功'); + setIsAddDeviceOpen(false); + // 刷新设备列表 + loadDevices(1, true); + } else { + toast.error(data.msg || '添加设备失败'); + } + } catch (error) { + console.error('添加设备失败:', error); + toast.error('添加设备失败,请稍后重试'); + } + }; + return (
@@ -748,15 +781,10 @@ export default function DevicesPage() { 取消
diff --git a/Cunkebao/app/login/page.tsx b/Cunkebao/app/login/page.tsx index ba7f1e16..41410b7e 100644 --- a/Cunkebao/app/login/page.tsx +++ b/Cunkebao/app/login/page.tsx @@ -98,17 +98,14 @@ export default function LoginPage() { const response = await loginApi.login(form.phone, form.password) if (response.code === 200 && response.data) { - // 获取用户信息和token - const { token, token_expired, member } = response.data + // 保存登录信息 + localStorage.setItem('token', response.data.token) + localStorage.setItem('token_expired', response.data.token_expired) + localStorage.setItem('s2_accountId', response.data.member.s2_accountId) + + // 保存用户信息 + localStorage.setItem('userInfo', JSON.stringify(response.data.member)) - // 保存token和用户信息 - login(token, { - id: member.id, - username: member.username || member.account || '', - account: member.account, - avatar: member.avatar - }) - // 显示成功提示 toast({ title: "登录成功", From 4935d8c8c098e90088af446b2dfbf6d57d423df7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=B3=E6=B8=85=E7=88=BD?= Date: Wed, 30 Apr 2025 17:15:11 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E5=AD=98=E5=AE=A2=E5=AE=9D=20-=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E6=B7=BB=E5=8A=A0=E8=AE=BE=E5=A4=87=E4=BA=8C=E7=BB=B4?= =?UTF-8?q?=E7=A0=81=E6=9C=89=E6=97=B6=E4=BC=9A=E9=94=99=E8=AF=AF=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cunkebao/app/contact-import/page.tsx | 1 + Cunkebao/app/devices/[id]/page.tsx | 6 +-- Cunkebao/app/devices/page.tsx | 52 +++++++------------ Cunkebao/app/profile/page.tsx | 25 +++++---- .../app/workspace/group-sync/new/page.tsx | 47 +++++++++++++---- .../traffic-distribution/[id]/edit/page.tsx | 41 ++++++++++++--- .../controller/PasswordLoginController.php | 28 ++++++---- 7 files changed, 125 insertions(+), 75 deletions(-) diff --git a/Cunkebao/app/contact-import/page.tsx b/Cunkebao/app/contact-import/page.tsx index 335dfed2..667b9c22 100644 --- a/Cunkebao/app/contact-import/page.tsx +++ b/Cunkebao/app/contact-import/page.tsx @@ -7,6 +7,7 @@ import { Card } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { AlertCircle } from "lucide-react"; +import { toast } from "@/components/ui/use-toast"; interface ContactData { mobile: number; diff --git a/Cunkebao/app/devices/[id]/page.tsx b/Cunkebao/app/devices/[id]/page.tsx index f62bb40b..867a0947 100644 --- a/Cunkebao/app/devices/[id]/page.tsx +++ b/Cunkebao/app/devices/[id]/page.tsx @@ -134,13 +134,9 @@ export default function DeviceDetailPage() { } else if (serverData.taskConfig) { try { // 解析taskConfig字段 - let taskConfig = serverData.taskConfig - if (typeof taskConfig === 'string') { - taskConfig = JSON.parse(taskConfig) - } + const taskConfig = JSON.parse(serverData.taskConfig || '{}'); if (taskConfig) { - console.log('解析的taskConfig:', taskConfig); formattedDevice.features = { autoAddFriend: Boolean(taskConfig.autoAddFriend), autoReply: Boolean(taskConfig.autoReply), diff --git a/Cunkebao/app/devices/page.tsx b/Cunkebao/app/devices/page.tsx index 81acd216..2aa2e5ab 100644 --- a/Cunkebao/app/devices/page.tsx +++ b/Cunkebao/app/devices/page.tsx @@ -171,26 +171,37 @@ export default function DevicesPage() { setIsLoadingQRCode(true) setQrCodeImage("") // 清空当前二维码 + // 获取保存的accountId + const accountId = localStorage.getItem('s2_accountId') + if (!accountId) { + toast({ + title: "获取二维码失败", + description: "未获取到用户信息,请重新登录", + variant: "destructive", + }) + return + } + // 发起请求获取二维码 - 直接使用fetch避免api工具添加基础URL - const response = await fetch('http://yi.54word.com/v1/api/device/add', { + const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/v1/api/device/add`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, - body: JSON.stringify({}) + body: JSON.stringify({ + accountId: accountId + }) }) // 保存原始响应文本以便调试 const responseText = await response.text(); - console.log("原始响应内容:", responseText); // 尝试将响应解析为JSON let result; try { result = JSON.parse(responseText); } catch (e) { - console.error("响应不是有效的JSON:", e); toast({ title: "获取二维码失败", description: "服务器返回的数据格式无效", @@ -199,25 +210,19 @@ export default function DevicesPage() { return; } - console.log("二维码响应数据:", result); - if (result && result.code === 200) { // 尝试多种可能的返回数据结构 let qrcodeData = null; if (result.data?.qrCode) { qrcodeData = result.data.qrCode; - console.log("找到二维码数据在 result.data.qrCode"); } else if (result.data?.qrcode) { qrcodeData = result.data.qrcode; - console.log("找到二维码数据在 result.data.qrcode"); } else if (result.data?.image) { qrcodeData = result.data.image; - console.log("找到二维码数据在 result.data.image"); } else if (result.data?.url) { // 如果返回的是URL而不是base64 qrcodeData = result.data.url; - console.log("找到二维码URL在 result.data.url"); setQrCodeImage(qrcodeData); toast({ @@ -229,9 +234,7 @@ export default function DevicesPage() { } else if (typeof result.data === 'string') { // 如果data直接是字符串 qrcodeData = result.data; - console.log("二维码数据直接在 result.data 字符串中"); } else { - console.error("无法找到二维码数据:", result); toast({ title: "获取二维码失败", description: "返回数据格式不正确", @@ -242,7 +245,6 @@ export default function DevicesPage() { // 检查数据是否为空 if (!qrcodeData) { - console.error("二维码数据为空"); toast({ title: "获取二维码失败", description: "服务器返回的二维码数据为空", @@ -251,16 +253,12 @@ export default function DevicesPage() { return; } - console.log("处理前的二维码数据:", qrcodeData); - // 检查是否已经是完整的data URL if (qrcodeData.startsWith('data:image')) { - console.log("数据已包含data:image前缀"); setQrCodeImage(qrcodeData); } // 检查是否是URL else if (qrcodeData.startsWith('http')) { - console.log("数据是HTTP URL"); setQrCodeImage(qrcodeData); } // 尝试作为base64处理 @@ -268,7 +266,6 @@ export default function DevicesPage() { try { // 确保base64字符串没有空格等干扰字符 const cleanedBase64 = qrcodeData.trim(); - console.log("处理后的base64数据:", cleanedBase64.substring(0, 30) + "..."); // 直接以图片src格式设置 setQrCodeImage(`data:image/png;base64,${cleanedBase64}`); @@ -276,10 +273,9 @@ export default function DevicesPage() { // 预加载图片,确认是否有效 const img = new Image(); img.onload = () => { - console.log("二维码图片加载成功"); + // 图片加载成功 }; img.onerror = (e) => { - console.error("二维码图片加载失败:", e); toast({ title: "二维码加载失败", description: "服务器返回的数据无法显示为图片", @@ -288,7 +284,6 @@ export default function DevicesPage() { }; img.src = `data:image/png;base64,${cleanedBase64}`; } catch (e) { - console.error("处理base64数据出错:", e); toast({ title: "获取二维码失败", description: "图片数据处理失败", @@ -303,7 +298,6 @@ export default function DevicesPage() { description: "请使用手机扫描新的二维码添加设备", }); } else { - console.error("获取二维码失败:", result); toast({ title: "获取二维码失败", description: result?.msg || "请稍后重试", @@ -311,7 +305,6 @@ export default function DevicesPage() { }); } } catch (error) { - console.error("获取二维码失败", error); toast({ title: "获取二维码失败", description: "请检查网络连接后重试", @@ -343,7 +336,6 @@ export default function DevicesPage() { try { setIsSubmittingImei(true); - console.log("正在添加设备,IMEI:", deviceImei, "设备名称:", deviceName); // 使用api.post发送请求到/v1/devices const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/v1/devices`, { @@ -359,18 +351,14 @@ export default function DevicesPage() { }) }); - console.log("添加设备响应状态:", response.status); - // 保存原始响应文本以便调试 const responseText = await response.text(); - console.log("原始响应内容:", responseText); // 尝试将响应解析为JSON let result; try { result = JSON.parse(responseText); } catch (e) { - console.error("响应不是有效的JSON:", e); toast({ title: "添加设备失败", description: "服务器返回的数据格式无效", @@ -379,8 +367,6 @@ export default function DevicesPage() { return; } - console.log("添加设备响应:", result); - if (result && result.code === 200) { toast({ title: "设备添加成功", @@ -395,7 +381,6 @@ export default function DevicesPage() { // 刷新设备列表 loadDevices(1, true); } else { - console.error("添加设备失败:", result); toast({ title: "添加设备失败", description: result?.msg || "请检查设备信息是否正确", @@ -403,7 +388,6 @@ export default function DevicesPage() { }); } } catch (error) { - console.error("添加设备请求失败:", error); toast({ title: "请求失败", description: "网络错误,请稍后重试", @@ -675,7 +659,7 @@ export default function DevicesPage() { - + {/* 扫码添加 @@ -684,7 +668,7 @@ export default function DevicesPage() { 手动添加 - + */}
diff --git a/Cunkebao/app/profile/page.tsx b/Cunkebao/app/profile/page.tsx index 2a5ee1e2..ca4fb1d2 100644 --- a/Cunkebao/app/profile/page.tsx +++ b/Cunkebao/app/profile/page.tsx @@ -20,18 +20,23 @@ const menuItems = [ export default function ProfilePage() { const router = useRouter() - const { isAuthenticated, user, logout } = useAuth() const [showLogoutDialog, setShowLogoutDialog] = useState(false) + const [userInfo, setUserInfo] = useState(null) - // 处理身份验证状态,将路由重定向逻辑移至useEffect + // 从localStorage获取用户信息 useEffect(() => { - if (!isAuthenticated) { - router.push("/login") + const userInfoStr = localStorage.getItem('userInfo') + if (userInfoStr) { + setUserInfo(JSON.parse(userInfoStr)) } - }, [isAuthenticated, router]) + }, []) const handleLogout = () => { - logout() // 使用AuthProvider中的logout方法删除本地保存的用户信息 + // 清除本地存储的用户信息 + localStorage.removeItem('token') + localStorage.removeItem('token_expired') + localStorage.removeItem('s2_accountId') + localStorage.removeItem('userInfo') setShowLogoutDialog(false) router.push("/login") } @@ -57,14 +62,14 @@ export default function ProfilePage() {
- - {user?.username ? user.username.slice(0, 2) : "用户"} + + {userInfo?.username ? userInfo.username.slice(0, 2) : "用户"}
-

{user?.username || "用户"}

+

{userInfo?.username || "用户"}

账号: - {user?.account || Math.floor(10000000 + Math.random() * 90000000).toString()} + {userInfo?.account || Math.floor(10000000 + Math.random() * 90000000).toString()}

diff --git a/Cunkebao/app/workspace/group-sync/new/page.tsx b/Cunkebao/app/workspace/group-sync/new/page.tsx index 862e0858..fd644590 100644 --- a/Cunkebao/app/workspace/group-sync/new/page.tsx +++ b/Cunkebao/app/workspace/group-sync/new/page.tsx @@ -9,6 +9,7 @@ import { BasicSettings } from "../components/basic-settings" import { GroupSelector } from "../components/group-selector" import { ContentSelector } from "../components/content-selector" import type { WechatGroup, ContentLibrary } from "@/types/group-sync" +import { toast } from "@/components/ui/use-toast" const steps = [ { id: 1, title: "步骤 1", subtitle: "基础设置" }, @@ -46,11 +47,39 @@ export default function NewGroupSyncPage() { setFormData((prev) => ({ ...prev, contentLibraries })) } - const handleSave = () => { - // 这里可以添加保存逻辑,例如API调用 - console.log("保存表单数据:", formData) - router.push("/workspace/group-sync") - } + const handleSubmit = async (formData: any) => { + try { + const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/v1/api/group-sync`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${localStorage.getItem('token')}` + }, + body: JSON.stringify(formData) + }); + + const data = await response.json(); + if (data.code === 200) { + toast({ + title: "创建成功", + description: "群同步计划已创建", + }); + router.push('/workspace/group-sync'); + } else { + toast({ + title: "创建失败", + description: data.msg || "请稍后重试", + variant: "destructive", + }); + } + } catch (error) { + toast({ + title: "创建失败", + description: "网络错误,请稍后重试", + variant: "destructive", + }); + } + }; const handleCancel = () => { router.push("/workspace/group-sync") @@ -81,7 +110,7 @@ export default function NewGroupSyncPage() { isEnabled: formData.isEnabled, }} onNext={handleBasicSettingsNext} - onSave={handleSave} + onSave={handleSubmit} onCancel={handleCancel} /> )} @@ -92,7 +121,7 @@ export default function NewGroupSyncPage() { onGroupsChange={handleGroupsChange} onPrevious={() => setCurrentStep(1)} onNext={() => setCurrentStep(3)} - onSave={handleSave} + onSave={handleSubmit} onCancel={handleCancel} /> )} @@ -103,7 +132,7 @@ export default function NewGroupSyncPage() { onLibrariesChange={handleLibrariesChange} onPrevious={() => setCurrentStep(2)} onNext={() => setCurrentStep(4)} - onSave={handleSave} + onSave={handleSubmit} onCancel={handleCancel} /> )} @@ -118,7 +147,7 @@ export default function NewGroupSyncPage() { - -
diff --git a/Server/application/common/controller/PasswordLoginController.php b/Server/application/common/controller/PasswordLoginController.php index a4cbdd0d..c5cce3d4 100644 --- a/Server/application/common/controller/PasswordLoginController.php +++ b/Server/application/common/controller/PasswordLoginController.php @@ -6,7 +6,6 @@ use app\common\model\User as UserModel; use app\common\util\JwtUtil; use Exception; use library\ResponseHelper; -use think\response\Json; use think\Validate; /** @@ -24,12 +23,16 @@ class PasswordLoginController extends BaseController */ protected function getUserProfileWithAccountAndType(string $account, int $typeId): UserModel { - $user = UserModel::where(function ($query) use ($account) { - $query->where('phone', $account)->whereOr('account', $account); - }) - ->where(function ($query) use ($typeId) { - $query->where('status', 1)->where('typeId', $typeId); - })->find(); + $user = UserModel::where( + function ($query) use ($account) { + $query->where('phone', $account)->whereOr('account', $account); + } + ) + ->where( + function ($query) use ($typeId) { + $query->where('status', 1)->where('typeId', $typeId); + } + )->find(); return $user; } @@ -54,7 +57,10 @@ class PasswordLoginController extends BaseController throw new \Exception('账号或密码错误', 403); } - return $user->toArray(); + return array_merge($user->toArray(), [ + 'lastLoginIp' => $this->request->ip(), + 'lastLoginTime' => time() + ]); } /** @@ -100,8 +106,8 @@ class PasswordLoginController extends BaseController $member = $this->getUser($account, $password, $typeId); // 生成JWT令牌 - $token = JwtUtil::createToken($member, 7200); - $token_expired = time() + 7200; + $token = JwtUtil::createToken($member, 86400); + $token_expired = time() + 86400; return compact('member', 'token', 'token_expired'); } @@ -109,7 +115,7 @@ class PasswordLoginController extends BaseController /** * 用户登录 * - * @return Json + * @return \think\response\Json */ public function index() { From 6708adde877df2df8afb7c0ca2c40d6cfbbab2c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9F=B3=E6=B8=85=E7=88=BD?= Date: Wed, 30 Apr 2025 17:28:25 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E5=AD=98=E5=AE=A2=E5=AE=9D=20-=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E8=AE=BE=E5=A4=87=E5=90=8E=E5=9B=9E=E8=B0=83=E8=BD=AE?= =?UTF-8?q?=E8=AF=A2=E8=8E=B7=E5=8F=96=E6=9C=80=E6=96=B0=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/cunkebao/config/route.php | 69 +++++----- .../GetAddResultedDevicesController.php | 120 ++++++++++++++++++ 2 files changed, 155 insertions(+), 34 deletions(-) create mode 100644 Server/application/cunkebao/controller/device/GetAddResultedDevicesController.php diff --git a/Server/application/cunkebao/config/route.php b/Server/application/cunkebao/config/route.php index 6a14bb07..309a6dfd 100644 --- a/Server/application/cunkebao/config/route.php +++ b/Server/application/cunkebao/config/route.php @@ -10,72 +10,73 @@ Route::group('v1/', function () { // 设备管理相关 Route::group('devices', function () { - Route::get(':id/related-accounts', 'app\\cunkebao\\controller\\device\\GetRelatedAccountsV1Controller@index'); // 设备关联微信账号路由 - Route::get(':id/handle-logs', 'app\\cunkebao\\controller\\device\\GetDeviceHandleLogsV1Controller@index'); // 获取设备操作记录 - Route::get('', 'app\\cunkebao\\controller\\device\\GetDeviceListV1Controller@index'); // 获取设备列表 - Route::get(':id', 'app\\cunkebao\\controller\\device\\GetDeviceDetailV1Controller@index'); // 获取设备详情 - Route::post('', 'app\\cunkebao\\controller\\device\\PostAddDeviceV1Controller@index'); // 添加设备 - Route::put('refresh', 'app\\cunkebao\\controller\\device\\RefreshDeviceDetailV1Controller@index'); // 刷新设备状态 - Route::delete(':id', 'app\\cunkebao\\controller\\Device@delete'); // 删除设备 - Route::post('task-config', 'app\\cunkebao\\controller\\device\\UpdateDeviceTaskConfigV1Controller@index'); // 更新设备任务配置 + Route::get(':id/related-accounts', 'app\cunkebao\controller\device\GetRelatedAccountsV1Controller@index'); // 设备关联微信账号路由 + Route::get(':id/handle-logs', 'app\cunkebao\controller\device\GetDeviceHandleLogsV1Controller@index'); // 获取设备操作记录 + Route::get('', 'app\cunkebao\controller\device\GetDeviceListV1Controller@index'); // 获取设备列表 + Route::get(':id', 'app\cunkebao\controller\device\GetDeviceDetailV1Controller@index'); // 获取设备详情 + Route::post('', 'app\cunkebao\controller\device\PostAddDeviceV1Controller@index'); // 添加设备 + Route::put('refresh', 'app\cunkebao\controller\device\RefreshDeviceDetailV1Controller@index'); // 刷新设备状态 + Route::delete(':id', 'app\cunkebao\controller\Device@delete'); // 删除设备 + Route::post('task-config', 'app\cunkebao\controller\device\UpdateDeviceTaskConfigV1Controller@index'); + Route::get('add-results', 'app\cunkebao\controller\device\GetAddResultedDevicesController@index'); // 更新设备任务配置 }); // 设备微信相关 Route::group('device/wechats', function () { - Route::get('friends', 'app\\cunkebao\\controller\\DeviceWechat@getFriends'); // 获取微信好友列表 - Route::get('count', 'app\\cunkebao\\controller\\DeviceWechat@count'); // 获取在线微信账号数量 - Route::get('device-count', 'app\\cunkebao\\controller\\DeviceWechat@deviceCount'); // 获取有登录微信的设备数量 - Route::get('', 'app\\cunkebao\\controller\\DeviceWechat@index'); // 获取在线微信账号列表 - Route::get(':id', 'app\\cunkebao\\controller\\DeviceWechat@detail'); // 获取微信号详情 - Route::put('refresh', 'app\\cunkebao\\controller\\DeviceWechat@refresh'); // 刷新设备微信状态 - Route::post('transfer-friends', 'app\\cunkebao\\controller\\DeviceWechat@transferFriends'); // 微信好友转移 + Route::get('friends', 'app\cunkebao\controller\DeviceWechat@getFriends'); // 获取微信好友列表 + Route::get('count', 'app\cunkebao\controller\DeviceWechat@count'); // 获取在线微信账号数量 + Route::get('device-count', 'app\cunkebao\controller\DeviceWechat@deviceCount'); // 获取有登录微信的设备数量 + Route::get('', 'app\cunkebao\controller\DeviceWechat@index'); // 获取在线微信账号列表 + Route::get(':id', 'app\cunkebao\controller\DeviceWechat@detail'); // 获取微信号详情 + Route::put('refresh', 'app\cunkebao\controller\DeviceWechat@refresh'); // 刷新设备微信状态 + Route::post('transfer-friends', 'app\cunkebao\controller\DeviceWechat@transferFriends'); // 微信好友转移 }); // 获客场景相关 Route::group('plan/scenes', function () { - Route::get('', 'app\\cunkebao\\controller\\Scene@index'); // 获取场景列表 + Route::get('', 'app\cunkebao\controller\Scene@index'); // 获取场景列表 }); // 流量标签相关 Route::group('traffic/tags', function () { - Route::get('', 'app\\cunkebao\\controller\\TrafficTag@index'); // 获取标签列表 + Route::get('', 'app\cunkebao\controller\TrafficTag@index'); // 获取标签列表 }); // 流量池相关 Route::group('traffic/pool', function () { - Route::post('import', 'app\\cunkebao\\controller\\TrafficPool@importOrders'); // 导入订单标签 + Route::post('import', 'app\cunkebao\controller\TrafficPool@importOrders'); // 导入订单标签 }); // 工作台相关 Route::group('workbench', function () { - Route::post('create', 'app\\cunkebao\\controller\\WorkbenchController@create'); // 创建工作台 - Route::get('list', 'app\\cunkebao\\controller\\WorkbenchController@getList'); // 获取工作台列表 - Route::post('update-status', 'app\\cunkebao\\controller\\WorkbenchController@updateStatus'); // 更新工作台状态 - Route::delete('delete', 'app\\cunkebao\\controller\\WorkbenchController@delete'); // 删除工作台 - Route::post('copy', 'app\\cunkebao\\controller\\WorkbenchController@copy'); // 拷贝工作台 - Route::get('detail', 'app\\cunkebao\\controller\\WorkbenchController@detail'); // 获取工作台详情 - Route::post('update', 'app\\cunkebao\\controller\\WorkbenchController@update'); // 更新工作台 + Route::post('create', 'app\cunkebao\controller\WorkbenchController@create'); // 创建工作台 + Route::get('list', 'app\cunkebao\controller\WorkbenchController@getList'); // 获取工作台列表 + Route::post('update-status', 'app\cunkebao\controller\WorkbenchController@updateStatus'); // 更新工作台状态 + Route::delete('delete', 'app\cunkebao\controller\WorkbenchController@delete'); // 删除工作台 + Route::post('copy', 'app\cunkebao\controller\WorkbenchController@copy'); // 拷贝工作台 + Route::get('detail', 'app\cunkebao\controller\WorkbenchController@detail'); // 获取工作台详情 + Route::post('update', 'app\cunkebao\controller\WorkbenchController@update'); // 更新工作台 }); // 内容库相关 Route::group('content/library', function () { - Route::post('create', 'app\\cunkebao\\controller\\ContentLibraryController@create'); // 创建内容库 - Route::get('list', 'app\\cunkebao\\controller\\ContentLibraryController@getList'); // 获取内容库列表 - Route::post('update', 'app\\cunkebao\\controller\\ContentLibraryController@update'); // 更新内容库 - Route::delete('delete', 'app\\cunkebao\\controller\\ContentLibraryController@delete'); // 删除内容库 - Route::get('detail', 'app\\cunkebao\\controller\\ContentLibraryController@detail'); // 获取内容库详情 - Route::get('collectMoments', 'app\\cunkebao\\controller\\ContentLibraryController@collectMoments'); // 采集朋友圈 + Route::post('create', 'app\cunkebao\controller\ContentLibraryController@create'); // 创建内容库 + Route::get('list', 'app\cunkebao\controller\ContentLibraryController@getList'); // 获取内容库列表 + Route::post('update', 'app\cunkebao\controller\ContentLibraryController@update'); // 更新内容库 + Route::delete('delete', 'app\cunkebao\controller\ContentLibraryController@delete'); // 删除内容库 + Route::get('detail', 'app\cunkebao\controller\ContentLibraryController@detail'); // 获取内容库详情 + Route::get('collectMoments', 'app\cunkebao\controller\ContentLibraryController@collectMoments'); // 采集朋友圈 }); // 好友相关 Route::group('friend', function () { - Route::get('', 'app\\cunkebao\\controller\\friend\\GetFriendListV1Controller@index'); // 获取好友列表 + Route::get('', 'app\cunkebao\controller\friend\GetFriendListV1Controller@index'); // 获取好友列表 }); //群相关 Route::group('chatroom', function () { - Route::get('', 'app\\cunkebao\\controller\\chatroom\\GetChatroomListV1Controller@index'); // 获取群列表 - Route::get('getMemberList', 'app\\cunkebao\\controller\\chatroom\\GetChatroomListV1Controller@getMemberList'); // 获取群详情 + Route::get('', 'app\cunkebao\controller\chatroom\GetChatroomListV1Controller@index'); // 获取群列表 + Route::get('getMemberList', 'app\cunkebao\controller\chatroom\GetChatroomListV1Controller@getMemberList'); // 获取群详情 }); diff --git a/Server/application/cunkebao/controller/device/GetAddResultedDevicesController.php b/Server/application/cunkebao/controller/device/GetAddResultedDevicesController.php new file mode 100644 index 00000000..6a5e23a9 --- /dev/null +++ b/Server/application/cunkebao/controller/device/GetAddResultedDevicesController.php @@ -0,0 +1,120 @@ +value('companyId'); + } + + /** + * 获取项目下的所有设备。 + * + * @param int $companyId + * @return array + */ + protected function getAllDevicesIdWithInCompany(int $companyId): array + { + return DeviceModel::where('companyId', $companyId)->column('id') ?: [0]; + } + + /** + * 执行数据迁移。 + * + * @param int $accountId + * @return void + */ + protected function migrateData(int $accountId): void + { + $companyId = $this->getCompanyIdByAccountId($accountId); + $deviceIds = $this->getAllDevicesIdWithInCompany($companyId) ?: [0]; + + // 从 s2_device 导入数据。 + $this->getNewDeviceFromS2_device($deviceIds, $companyId); + } + + /** + * 从 s2_device 导入数据。 + * + * @param array $ids + * @param int $companyId + * @return void + */ + protected function getNewDeviceFromS2_device(array $ids, int $companyId): void + { + $ids = implode(',', $ids); + + $sql = "insert into ck_device(`id`, `imei`, `model`, phone, operatingSystem,memo,alive,brand,rooted,xPosed,softwareVersion,extra,createTime,updateTime,deleteTime,companyId) + select + d.id,d.imei,d.model,d.phone,d.operatingSystem,d.memo,d.alive,d.brand,d.rooted,d.xPosed,d.softwareVersion,d.extra,d.createTime,d.lastUpdateTime,d.deleteTime,a.departmentId companyId + from s2_device d + join s2_company_account a on d.currentAccountId = a.id + where isDeleted = 0 and deletedAndStop = 0 and d.id not in ({$ids}) and a.departmentId = {$companyId} + "; + + Db::query($sql); + } + + /** + * 获取添加的关联设备结果。 + * + * @param int $accountId + * @return bool + */ + protected function getAddResulted(int $accountId): bool + { + $result = (new ApiDeviceController())->getlist( + [ + 'accountId' => $accountId, + 'pageIndex' => 0, + 'pageSize' => 1 + ], + true + ); + + $result = json_decode($result, true); + $result = $result['data']['results'][0] ?? false; + + return $result ? ( + // 125是前端延迟5秒 + 轮询120次 1次/s + time() - strtotime($result['lastUpdateTime']) <= 65 + ) : false; + } + + /** + * 获取基础统计信息 + * + * @return \think\response\Json + */ + public function index() + { + $accountId = $this->request->param('accountId/d'); + + $isAdded = $this->getAddResulted($accountId); + $isAdded && $this->migrateData($accountId); + + return ResponseHelper::success( + [ + 'added' => $isAdded + ] + ); + } +} \ No newline at end of file