From 1644e478c76135e1ff7ac8a951c20fe2575c7c0a Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Tue, 28 Oct 2025 09:25:39 +0800 Subject: [PATCH 01/14] =?UTF-8?q?AI=E5=AF=B9=E8=AF=9D=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/ai/controller/CozeAI.php | 74 +++- Server/application/chukebao/config/route.php | 2 + .../chukebao/controller/AiChatController.php | 372 ++++++++++++++++-- .../chukebao/controller/MessageController.php | 4 +- .../controller/WechatChatroomController.php | 44 +++ .../controller/WechatFriendController.php | 40 ++ 6 files changed, 488 insertions(+), 48 deletions(-) diff --git a/Server/application/ai/controller/CozeAI.php b/Server/application/ai/controller/CozeAI.php index 9d9bf150..5208326f 100644 --- a/Server/application/ai/controller/CozeAI.php +++ b/Server/application/ai/controller/CozeAI.php @@ -76,7 +76,7 @@ class CozeAI extends Controller $result = requestCurl($this->apiUrl . '/v1/bot/create', $params, 'POST', $this->headers, 'json'); $result = json_decode($result, true); if ($result['code'] != 0) { - return errorJson($result['msg'], $result['code']); + return json_encode(['code' => $result['code'], 'msg' => $result['msg'], 'data' => []]); } return json_encode(['code' => 200, 'msg' => '创建成功', 'data' => $result['data']]); @@ -136,9 +136,9 @@ class CozeAI extends Controller $result = requestCurl($this->apiUrl . '/v1/bot/update', $params, 'POST', $this->headers, 'json'); $result = json_decode($result, true); if ($result['code'] != 0) { - return errorJson($result['msg'], $result['code']); + return json_encode(['code' => $result['code'], 'msg' => $result['msg'], 'data' => []]); } - return json_encode(['code' => 200, 'msg' => '获取成功']); + return json_encode(['code' => 200, 'msg' => '更新成功', 'data' => []]); } @@ -162,9 +162,9 @@ class CozeAI extends Controller $result = requestCurl($this->apiUrl . '/v1/bot/publish', $params, 'POST', $this->headers, 'json'); $result = json_decode($result, true); if ($result['code'] != 0) { - return errorJson($result['msg'], $result['code']); + return json_encode(['code' => $result['code'], 'msg' => $result['msg'], 'data' => []]); } - return json_encode(['code' => 200, 'msg' => '发布成功']); + return json_encode(['code' => 200, 'msg' => '发布成功', 'data' => []]); } @@ -191,7 +191,7 @@ class CozeAI extends Controller $result = json_decode($result, true); if ($result['code'] != 0) { - return errorJson($result['msg'], $result['code']); + return json_encode(['code' => $result['code'], 'msg' => $result['msg'], 'data' => []]); } return json_encode(['code' => 200, 'msg' => '创建成功','data' => $result['data']]); } @@ -231,7 +231,7 @@ class CozeAI extends Controller $result = requestCurl($this->apiUrl . '/open_api/knowledge/document/create', $params, 'POST', $headers, 'json'); $result = json_decode($result, true); if ($result['code'] != 0) { - return errorJson($result['msg'], $result['code']); + return json_encode(['code' => $result['code'], 'msg' => $result['msg'], 'data' => []]); } return json_encode(['code' => 200, 'msg' => '创建成功','data' => $result['document_infos']]); } @@ -254,9 +254,9 @@ class CozeAI extends Controller $result = requestCurl($this->apiUrl . '/open_api/knowledge/document/delete', $params, 'POST', $headers, 'json'); $result = json_decode($result, true); if ($result['code'] != 0) { - return errorJson($result['msg'], $result['code']); + return json_encode(['code' => $result['code'], 'msg' => $result['msg'], 'data' => []]); } - return json_encode(['code' => 200, 'msg' => '删除成功']); + return json_encode(['code' => 200, 'msg' => '删除成功', 'data' => []]); } @@ -285,7 +285,7 @@ class CozeAI extends Controller $result = requestCurl($this->apiUrl . '/v1/conversation/create', $params, 'POST', $this->headers, 'json'); $result = json_decode($result, true); if ($result['code'] != 0) { - return errorJson($result['msg'], $result['code']); + return json_encode(['code' => $result['code'], 'msg' => $result['msg'], 'data' => []]); } return json_encode(['code' => 200, 'msg' => '创建成功','data' => $result['data']]); } @@ -306,15 +306,15 @@ class CozeAI extends Controller if(empty($bot_id)){ - return errorJson('智能体ID不能为空'); + return json_encode(['code' => 500, 'msg' => '智能体ID不能为空', 'data' => []]); } if(empty($conversation_id)){ - return errorJson('会话ID不能为空'); + return json_encode(['code' => 500, 'msg' => '会话ID不能为空', 'data' => []]); } if(empty($question)){ - return errorJson('问题不能为空'); + return json_encode(['code' => 500, 'msg' => '问题不能为空', 'data' => []]); } // 构建请求数据 @@ -330,16 +330,21 @@ class CozeAI extends Controller $result = requestCurl($url, $params, 'POST', $this->headers, 'json'); $result = json_decode($result, true); if ($result['code'] != 0) { - return errorJson($result['msg'], $result['code']); + return json_encode(['code' => $result['code'], 'msg' => $result['msg'], 'data' => []]); } return json_encode(['code' => 200, 'msg' => '发送成功','data' => $result['data']]); } catch (\Exception $e) { - return errorJson('创建对话失败:' . $e->getMessage()); + return json_encode(['code' => 500, 'msg' => '创建对话失败:' . $e->getMessage(), 'data' => []]); } } + /** + * 查看对话详情 + * @param $data + * @return false|string|\think\response\Json + */ public function getConversationChat($data = []) { $conversation_id = !empty($data['conversation_id']) ? $data['conversation_id'] : ''; @@ -348,12 +353,17 @@ class CozeAI extends Controller $result = requestCurl($url, [], 'GET', $this->headers, 'json'); $result = json_decode($result, true); if ($result['code'] != 0) { - return errorJson($result['msg'], $result['code']); + return json_encode(['code' => $result['code'], 'msg' => $result['msg'], 'data' => []]); } - return json_encode(['code' => 200, 'msg' => '发送成功','data' => $result['data']]); + return json_encode(['code' => 200, 'msg' => '获取成功','data' => $result['data']]); } + /** + * 查看对话消息详情 + * @param $data + * @return false|string|\think\response\Json + */ public function listConversationMessage($data = []) { $conversation_id = !empty($data['conversation_id']) ? $data['conversation_id'] : ''; @@ -362,9 +372,35 @@ class CozeAI extends Controller $result = requestCurl($url, [], 'GET', $this->headers, 'json'); $result = json_decode($result, true); if ($result['code'] != 0) { - return errorJson($result['msg'], $result['code']); + return json_encode(['code' => $result['code'], 'msg' => $result['msg'], 'data' => []]); } - return json_encode(['code' => 200, 'msg' => '发送成功','data' => $result['data']]); + return json_encode(['code' => 200, 'msg' => '获取成功','data' => $result['data']]); + } + + + /** + * 取消进行中的对话 + * @param $data + * @return false|string|\think\response\Json + */ + public function cancelConversationChat($data = []) + { + $conversation_id = !empty($data['conversation_id']) ? $data['conversation_id'] : ''; + $chat_id = !empty($data['chat_id']) ? $data['chat_id'] : ''; + + // 构建请求数据 + $params = [ + 'conversation_id' => (string) $conversation_id, + 'chat_id' => (string) $chat_id + ]; + + $url = $this->apiUrl . '/v3/chat/cancel'; + $result = requestCurl($url, $params, 'POST', $this->headers, 'json'); + $result = json_decode($result, true); + if ($result['code'] != 0) { + return json_encode(['code' => $result['code'], 'msg' => $result['msg'], 'data' => []]); + } + return json_encode(['code' => 200, 'msg' => '取消成功', 'data' => []]); } } \ No newline at end of file diff --git a/Server/application/chukebao/config/route.php b/Server/application/chukebao/config/route.php index 9dd32c2c..491f16e4 100644 --- a/Server/application/chukebao/config/route.php +++ b/Server/application/chukebao/config/route.php @@ -12,10 +12,12 @@ Route::group('v1/', function () { //好友相关 Route::group('wechatFriend/', function () { Route::get('list', 'app\chukebao\controller\WechatFriendController@getList'); // 获取好友列表 + Route::get('detail', 'app\chukebao\controller\WechatFriendController@getDetail'); // 获取好友详情 }); //群相关 Route::group('wechatChatroom/', function () { Route::get('list', 'app\chukebao\controller\WechatChatroomController@getList'); // 获取好友列表 + Route::get('detail', 'app\chukebao\controller\WechatChatroomController@getDetail'); // 获取群详情 Route::post('aiAnnouncement', 'app\chukebao\controller\WechatChatroomController@aiAnnouncement'); // AI群公告 }); diff --git a/Server/application/chukebao/controller/AiChatController.php b/Server/application/chukebao/controller/AiChatController.php index 919204f0..29699262 100644 --- a/Server/application/chukebao/controller/AiChatController.php +++ b/Server/application/chukebao/controller/AiChatController.php @@ -11,6 +11,9 @@ use app\chukebao\model\FriendSettings; use app\chukebao\model\TokensCompany; use library\ResponseHelper; use think\Db; +use think\facade\Cache; +use think\facade\Log; + /** * AI聊天控制器 @@ -27,8 +30,16 @@ class AiChatController extends BaseController const STATUS_CANCELED = 'canceled'; // 对话已取消 // 轮询配置 - const MAX_RETRY_TIMES = 30; // 最大重试次数 - const RETRY_INTERVAL = 2; // 重试间隔(秒) + const MAX_RETRY_TIMES = 1000; // 最大重试次数 + const RETRY_INTERVAL = 500000; // 重试间隔(微秒,即500毫秒) + + // 并发控制 + const CACHE_EXPIRE = 30; // 缓存过期时间(秒) + + // 请求唯一标识符 + private $requestKey = ''; + private $requestId = ''; + private $currentStep = 0; /** * AI聊天主入口 @@ -40,59 +51,219 @@ class AiChatController extends BaseController try { // 1. 参数验证和初始化 $params = $this->validateAndInitParams(); + if ($params === false) { return ResponseHelper::error('参数验证失败'); } + + // 并发控制:检查并处理同一用户的重复请求 + $this->requestKey = "aichat_{$params['friendId']}_{$params['wechatAccountId']}"; + $this->requestId = uniqid('req_', true); + + $concurrentCheck = $this->handleConcurrentRequest($params); + if ($concurrentCheck !== true) { + return $concurrentCheck; // 返回错误响应 + } + + $this->currentStep = 1; // 2. 验证Tokens余额 - if (!$this->checkTokensBalance($params['companyId'])) { + $this->updateRequestStep(2); + if ($this->isRequestCanceled()) { + return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消'); + } + $hasBalance = $this->checkTokensBalance($params['companyId']); + + if (!$hasBalance) { + $this->clearRequestCache(); return ResponseHelper::error('Tokens余额不足,请充值后再试'); } // 3. 获取AI配置 + $this->updateRequestStep(3); + if ($this->isRequestCanceled()) { + return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消'); + } $setting = $this->getAiSettings($params['companyId']); + if (!$setting) { + $this->clearRequestCache(); return ResponseHelper::error('未找到AI配置信息,请先配置AI策略'); } // 4. 获取好友AI设置 + $this->updateRequestStep(4); + if ($this->isRequestCanceled()) { + return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消'); + } $friendSettings = $this->getFriendSettings($params['companyId'], $params['friendId']); + if (!$friendSettings) { + $this->clearRequestCache(); return ResponseHelper::error('该好友未配置或未开启AI功能'); } // 5. 确保会话存在 + $this->updateRequestStep(5); + if ($this->isRequestCanceled()) { + return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消'); + } $conversationId = $this->ensureConversation($friendSettings, $setting, $params); + if (!$conversationId) { + $this->clearRequestCache(); return ResponseHelper::error('创建会话失败'); } // 6. 获取历史消息 + $this->updateRequestStep(6); + if ($this->isRequestCanceled()) { + return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消'); + } $msgData = $this->getHistoryMessages($params['friendId'], $friendSettings); - // 7. 创建AI对话 + // 7. 创建AI对话(从这步开始需要保存对话ID以便取消) + $this->updateRequestStep(7); + if ($this->isRequestCanceled($conversationId, null)) { + return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消'); + } $chatId = $this->createAiChat($setting, $friendSettings, $msgData); + if (!$chatId) { + $this->clearRequestCache(); return ResponseHelper::error('创建对话失败'); } + + // 保存对话ID到缓存,以便新请求可以取消 + $this->updateRequestStep(7, $conversationId, $chatId); // 8. 等待AI处理完成(轮询) - $chatResult = $this->waitForChatCompletion($conversationId, $chatId); - if (!$chatResult) { - return ResponseHelper::error('AI处理超时或失败'); + $this->updateRequestStep(8, $conversationId, $chatId); + if ($this->isRequestCanceled($conversationId, $chatId)) { + return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消'); } + $chatResult = $this->waitForChatCompletion($conversationId, $chatId); + + if (!$chatResult['success']) { + $this->clearRequestCache(); + return ResponseHelper::error($chatResult['error']); + } + + $chatResult = $chatResult['data']; // 9. 扣除Tokens + $this->updateRequestStep(9, $conversationId, $chatId); + if ($this->isRequestCanceled($conversationId, $chatId)) { + return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消'); + } $this->consumeTokens($chatResult, $params, $friendSettings); // 10. 获取对话消息 + $this->updateRequestStep(10, $conversationId, $chatId); + if ($this->isRequestCanceled($conversationId, $chatId)) { + return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消'); + } $messages = $this->getChatMessages($conversationId, $chatId); + if (!$messages) { return ResponseHelper::error('获取对话消息失败'); } - return ResponseHelper::success($messages[1]['content'], '对话成功'); + + // 筛选type为answer的消息(AI回复的内容) + $answerContent = ''; + foreach ($messages as $msg) { + if (isset($msg['type']) && $msg['type'] === 'answer') { + $answerContent = $msg['content'] ?? ''; + break; + } + } + + if (empty($answerContent)) { + Log::warning('未找到AI回复内容,messages: ' . json_encode($messages)); + return ResponseHelper::error('未获取到AI回复内容'); + } + + // 清理请求缓存 + $this->clearRequestCache(); + + // 返回结果 + return ResponseHelper::success(['content' => $answerContent], '对话成功'); + } catch (\Exception $e) { - \think\facade\Log::error('AI聊天异常:' . $e->getMessage()); + Log::error('AI聊天异常:' . $e->getMessage()); + + // 清理请求缓存 + $this->clearRequestCache(); + + return ResponseHelper::error('系统异常:' . $e->getMessage()); + } + } + + /** + * 取消AI对话 + * 取消当前正在进行的AI对话请求 + * + * @return \think\response\Json + */ + public function cancel() + { + try { + // 获取参数 + $friendId = $this->request->param('friendId', ''); + $wechatAccountId = $this->request->param('wechatAccountId', ''); + + if (empty($wechatAccountId) || empty($friendId)) { + return ResponseHelper::error('参数缺失'); + } + + // 生成缓存键 + $requestKey = "aichat_{$friendId}_{$wechatAccountId}"; + + // 获取缓存数据 + $cacheData = Cache::get($requestKey); + + if (!$cacheData) { + return ResponseHelper::error('当前没有正在进行的AI对话'); + } + + $requestId = $cacheData['request_id'] ?? ''; + $step = $cacheData['step'] ?? 0; + $conversationId = $cacheData['conversation_id'] ?? ''; + $chatId = $cacheData['chat_id'] ?? ''; + + Log::info("手动取消AI对话 - 请求ID: {$requestId}, 步骤: {$step}"); + + // 如果已经到达步骤7或之后,需要调用取消API + if ($step >= 7 && !empty($conversationId) && !empty($chatId)) { + try { + $cozeAI = new CozeAI(); + $cancelResult = $cozeAI->cancelConversationChat([ + 'conversation_id' => $conversationId, + 'chat_id' => $chatId, + ]); + + $result = json_decode($cancelResult, true); + if ($result['code'] != 200) { + Log::error("调用取消API失败 - conversation_id: {$conversationId}, chat_id: {$chatId}, 错误: " . ($result['msg'] ?? '未知错误')); + } else { + Log::info("成功调用取消API - conversation_id: {$conversationId}, chat_id: {$chatId}"); + } + } catch (\Exception $e) { + Log::error("调用取消API异常:" . $e->getMessage()); + } + } + + // 清理缓存 + Cache::rm($requestKey); + Log::info("已清理AI对话缓存 - 请求ID: {$requestId}"); + + return ResponseHelper::success([ + 'canceled_request_id' => $requestId, + 'step' => $step + ], 'AI对话已取消'); + + } catch (\Exception $e) { + Log::error('取消AI对话异常:' . $e->getMessage()); return ResponseHelper::error('系统异常:' . $e->getMessage()); } } @@ -194,7 +365,7 @@ class AiChatController extends BaseController $res = json_decode($res, true); if ($res['code'] != 200) { - \think\facade\Log::error('创建会话失败:' . ($res['msg'] ?? '未知错误')); + Log::error('创建会话失败:' . ($res['msg'] ?? '未知错误')); return null; } @@ -292,7 +463,7 @@ class AiChatController extends BaseController $res = json_decode($res, true); if ($res['code'] != 200) { - \think\facade\Log::error('创建对话失败:' . ($res['msg'] ?? '未知错误')); + Log::error('创建对话失败:' . ($res['msg'] ?? '未知错误')); return null; } @@ -304,7 +475,7 @@ class AiChatController extends BaseController * * @param string $conversationId 会话ID * @param string $chatId 对话ID - * @return array|null + * @return array ['success' => bool, 'data' => array|null, 'error' => string] */ private function waitForChatCompletion($conversationId, $chatId) { @@ -320,8 +491,9 @@ class AiChatController extends BaseController $res = json_decode($res, true); if ($res['code'] != 200) { - \think\facade\Log::error('获取对话状态失败:' . ($res['msg'] ?? '未知错误')); - return null; + $errorMsg = 'AI接口调用失败:' . ($res['msg'] ?? '未知错误'); + Log::error($errorMsg); + return ['success' => false, 'data' => null, 'error' => $errorMsg]; } $status = $res['data']['status'] ?? ''; @@ -330,36 +502,41 @@ class AiChatController extends BaseController switch ($status) { case self::STATUS_COMPLETED: // 对话完成,返回结果 - return $res['data']; + return ['success' => true, 'data' => $res['data'], 'error' => '']; case self::STATUS_IN_PROGRESS: case self::STATUS_CREATED: // 继续等待 $retryCount++; - sleep(self::RETRY_INTERVAL); + usleep(self::RETRY_INTERVAL); break; case self::STATUS_FAILED: - \think\facade\Log::error('对话失败,chat_id: ' . $chatId); - return null; + $errorMsg = 'AI对话处理失败'; + Log::error($errorMsg . ',chat_id: ' . $chatId); + return ['success' => false, 'data' => null, 'error' => $errorMsg]; case self::STATUS_CANCELED: - \think\facade\Log::error('对话已取消,chat_id: ' . $chatId); - return null; + $errorMsg = 'AI对话已被取消'; + Log::error($errorMsg . ',chat_id: ' . $chatId); + return ['success' => false, 'data' => null, 'error' => $errorMsg]; case self::STATUS_REQUIRES_ACTION: - \think\facade\Log::warning('对话需要进一步处理,chat_id: ' . $chatId); - return null; + $errorMsg = 'AI对话需要进一步处理'; + Log::warning($errorMsg . ',chat_id: ' . $chatId); + return ['success' => false, 'data' => null, 'error' => $errorMsg]; default: - \think\facade\Log::error('未知状态:' . $status); - return null; + $errorMsg = 'AI返回未知状态:' . $status; + Log::error($errorMsg); + return ['success' => false, 'data' => null, 'error' => $errorMsg]; } } // 超时 - \think\facade\Log::error('对话处理超时,chat_id: ' . $chatId); - return null; + $errorMsg = 'AI对话处理超时,已等待' . (self::MAX_RETRY_TIMES * self::RETRY_INTERVAL / 1000000) . '秒'; + Log::error($errorMsg . ',chat_id: ' . $chatId); + return ['success' => false, 'data' => null, 'error' => $errorMsg]; } /** @@ -412,13 +589,154 @@ class AiChatController extends BaseController $res = json_decode($res, true); if ($res['code'] != 200) { - \think\facade\Log::error('获取对话消息失败:' . ($res['msg'] ?? '未知错误')); + Log::error('获取对话消息失败:' . ($res['msg'] ?? '未知错误')); return null; } return $res['data'] ?? []; } + /** + * 处理并发请求 + * 检查是否有同一用户的旧请求正在处理,如果有则取消旧请求 + * + * @param array $params 请求参数 + * @return true|\think\response\Json true表示可以继续,否则返回错误响应 + */ + private function handleConcurrentRequest($params) + { + $cacheData = Cache::get($this->requestKey); + + if ($cacheData) { + // 有旧请求正在处理 + $oldRequestId = $cacheData['request_id'] ?? ''; + $oldStep = $cacheData['step'] ?? 0; + $oldConversationId = $cacheData['conversation_id'] ?? ''; + $oldChatId = $cacheData['chat_id'] ?? ''; + + Log::info("检测到并发请求 - 旧请求: {$oldRequestId} (步骤{$oldStep}), 新请求: {$this->requestId}"); + + // 如果旧请求已经到达步骤7或之后,需要调用取消API + if ($oldStep >= 7 && !empty($oldConversationId) && !empty($oldChatId)) { + try { + $cozeAI = new CozeAI(); + $cancelResult = $cozeAI->cancelConversationChat([ + 'conversation_id' => $oldConversationId, + 'chat_id' => $oldChatId, + ]); + Log::info("已调用取消API取消旧请求的对话 - conversation_id: {$oldConversationId}, chat_id: {$oldChatId}"); + } catch (\Exception $e) { + Log::error("取消旧请求对话失败:" . $e->getMessage()); + } + } + + // 标记旧请求为已取消(通过更新缓存的 canceled 标志) + $cacheData['canceled'] = true; + $cacheData['canceled_by'] = $this->requestId; + Cache::set($this->requestKey, $cacheData, self::CACHE_EXPIRE); + } + + // 设置当前请求为活动请求 + $newCacheData = [ + 'request_id' => $this->requestId, + 'step' => 1, + 'start_time' => time(), + 'canceled' => false, + 'conversation_id' => '', + 'chat_id' => '', + ]; + Cache::set($this->requestKey, $newCacheData, self::CACHE_EXPIRE); + + return true; + } + + /** + * 检查当前请求是否被新请求取消 + * + * @param string $conversationId 会话ID(可选,用于取消对话) + * @param string $chatId 对话ID(可选,用于取消对话) + * @return bool + */ + private function isRequestCanceled($conversationId = '', $chatId = '') + { + $cacheData = Cache::get($this->requestKey); + + if (!$cacheData) { + // 缓存不存在,说明被清理或过期,视为被取消 + return true; + } + + $currentRequestId = $cacheData['request_id'] ?? ''; + $isCanceled = $cacheData['canceled'] ?? false; + + // 如果缓存中的请求ID与当前请求ID不一致,或者被标记为取消 + if ($currentRequestId !== $this->requestId || $isCanceled) { + Log::info("当前请求已被取消 - 请求ID: {$this->requestId}, 缓存请求ID: {$currentRequestId}, 取消标志: " . ($isCanceled ? 'true' : 'false')); + + // 如果提供了对话ID,尝试取消对话 + if (!empty($conversationId) && !empty($chatId) && $this->currentStep >= 7) { + try { + $cozeAI = new CozeAI(); + $cancelResult = $cozeAI->cancelConversationChat([ + 'conversation_id' => $conversationId, + 'chat_id' => $chatId, + ]); + Log::info("已取消当前请求的对话 - conversation_id: {$conversationId}, chat_id: {$chatId}"); + } catch (\Exception $e) { + Log::error("取消当前请求对话失败:" . $e->getMessage()); + } + } + + return true; + } + + return false; + } + + /** + * 更新请求步骤 + * + * @param int $step 当前步骤 + * @param string $conversationId 会话ID(可选) + * @param string $chatId 对话ID(可选) + */ + private function updateRequestStep($step, $conversationId = '', $chatId = '') + { + $this->currentStep = $step; + + $cacheData = Cache::get($this->requestKey); + + if ($cacheData && $cacheData['request_id'] === $this->requestId) { + $cacheData['step'] = $step; + $cacheData['update_time'] = time(); + + if (!empty($conversationId)) { + $cacheData['conversation_id'] = $conversationId; + } + if (!empty($chatId)) { + $cacheData['chat_id'] = $chatId; + } + + Cache::set($this->requestKey, $cacheData, self::CACHE_EXPIRE); + } + } + + /** + * 清理请求缓存 + */ + private function clearRequestCache() + { + if (!empty($this->requestKey)) { + $cacheData = Cache::get($this->requestKey); + + // 只有当前请求才能清理自己的缓存 + if ($cacheData && isset($cacheData['request_id']) && $cacheData['request_id'] === $this->requestId) { + Cache::rm($this->requestKey); + Log::info("已清理请求缓存 - 请求ID: {$this->requestId}"); + } + } + } + public function index2222() { diff --git a/Server/application/chukebao/controller/MessageController.php b/Server/application/chukebao/controller/MessageController.php index 5593eafb..1d44d14d 100644 --- a/Server/application/chukebao/controller/MessageController.php +++ b/Server/application/chukebao/controller/MessageController.php @@ -30,7 +30,7 @@ class MessageController extends BaseController // 优化后的查询:使用MySQL兼容的查询方式 $unionQuery = " - (SELECT m.id, m.content, m.wechatFriendId, m.wechatChatroomId, m.createTime, m.wechatTime, 2 as msgType, wc.nickname, wc.chatroomAvatar as avatar, wc.chatroomId + (SELECT m.id, m.content, m.wechatFriendId, m.wechatChatroomId, m.createTime, m.wechatTime,m.wechatAccountId, 2 as msgType, wc.nickname, wc.chatroomAvatar as avatar, wc.chatroomId FROM s2_wechat_chatroom wc INNER JOIN s2_wechat_message m ON wc.id = m.wechatChatroomId AND m.type = 2 INNER JOIN ( @@ -42,7 +42,7 @@ class MessageController extends BaseController WHERE wc.accountId = {$accountId} AND wc.isDeleted = 0 ) UNION ALL - (SELECT m.id, m.content, m.wechatFriendId, m.wechatChatroomId, m.createTime, m.wechatTime, 1 as msgType, 1 as nickname, 1 as avatar, 1 as chatroomId + (SELECT m.id, m.content, m.wechatFriendId, m.wechatChatroomId, m.createTime, m.wechatTime, 1 as msgType, 1 as nickname, 1 as avatar, 1 as chatroomId, 1 as wechatAccountId FROM s2_wechat_message m INNER JOIN ( SELECT wechatFriendId, MAX(wechatTime) as maxTime, MAX(id) as maxId diff --git a/Server/application/chukebao/controller/WechatChatroomController.php b/Server/application/chukebao/controller/WechatChatroomController.php index 76edb7e0..bd07bb31 100644 --- a/Server/application/chukebao/controller/WechatChatroomController.php +++ b/Server/application/chukebao/controller/WechatChatroomController.php @@ -83,7 +83,51 @@ class WechatChatroomController extends BaseController return ResponseHelper::success(['list'=>$list,'total'=>$total]); } + public function getDetail(){ + $id = input('id', 0); + + if (!$id) { + return ResponseHelper::error('聊天室ID不能为空'); + } + $accountId = $this->getUserInfo('s2_accountId'); + if (empty($accountId)){ + return ResponseHelper::error('请先登录'); + } + + $detail = Db::table('s2_wechat_chatroom') + ->where(['accountId' => $accountId, 'id' => $id, 'isDeleted' => 0]) + ->find(); + + if (!$detail) { + return ResponseHelper::error('聊天室不存在或无权限访问'); + } + + // 处理时间格式 + $detail['createTime'] = !empty($detail['createTime']) ? date('Y-m-d H:i:s', $detail['createTime']) : ''; + $detail['updateTime'] = !empty($detail['updateTime']) ? date('Y-m-d H:i:s', $detail['updateTime']) : ''; + + // 查询未读消息数量 + $unreadCount = Db::table('s2_wechat_message') + ->where('wechatChatroomId', $id) + ->where('isRead', 0) + ->count(); + + // 查询最新消息 + $latestMessage = Db::table('s2_wechat_message') + ->where('wechatChatroomId', $id) + ->order('id desc') + ->find(); + + $config = [ + 'unreadCount' => $unreadCount, + 'chat' => !empty($latestMessage), + 'msgTime' => isset($latestMessage['wechatTime']) ? $latestMessage['wechatTime'] : 0 + ]; + $detail['config'] = $config; + + return ResponseHelper::success($detail); + } public function aiAnnouncement() { diff --git a/Server/application/chukebao/controller/WechatFriendController.php b/Server/application/chukebao/controller/WechatFriendController.php index d94a0ebb..573e6811 100644 --- a/Server/application/chukebao/controller/WechatFriendController.php +++ b/Server/application/chukebao/controller/WechatFriendController.php @@ -101,4 +101,44 @@ class WechatFriendController extends BaseController return ResponseHelper::success(['list' => $list, 'total' => $total]); } + + /** + * 获取单个好友详情 + * @return \think\response\Json + */ + public function getDetail() + { + $friendId = $this->request->param('id'); + $accountId = $this->getUserInfo('s2_accountId'); + + if (empty($accountId)) { + return ResponseHelper::error('请先登录'); + } + + if (empty($friendId)) { + return ResponseHelper::error('好友ID不能为空'); + } + + // 查询好友详情 + $friend = Db::table('s2_wechat_friend') + ->where(['id' => $friendId, 'isDeleted' => 0]) + ->find(); + + if (empty($friend)) { + return ResponseHelper::error('好友不存在'); + } + + // 处理好友数据 + $friend['labels'] = json_decode($friend['labels'], true); + $friend['siteLabels'] = json_decode($friend['siteLabels'], true); + $friend['createTime'] = !empty($friend['createTime']) ? date('Y-m-d H:i:s', $friend['createTime']) : ''; + $friend['updateTime'] = !empty($friend['updateTime']) ? date('Y-m-d H:i:s', $friend['updateTime']) : ''; + $friend['passTime'] = !empty($friend['passTime']) ? date('Y-m-d H:i:s', $friend['passTime']) : ''; + + // 获取AI类型设置 + $aiTypeSetting = FriendSettings::where('friendId', $friendId)->find(); + $friend['aiType'] = $aiTypeSetting ? $aiTypeSetting['type'] : 0; + + return ResponseHelper::success(['detail' => $friend]); + } } \ No newline at end of file From 18a7055a4ebb781e90fd96ec2db2614e577c364a Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Tue, 28 Oct 2025 16:46:38 +0800 Subject: [PATCH 02/14] =?UTF-8?q?=E6=B6=88=E6=81=AF=E6=96=B0=E5=A2=9Eai?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chukebao/controller/MessageController.php | 7 ++ .../controller/WechatFriendController.php | 78 +++++++++---------- 2 files changed, 46 insertions(+), 39 deletions(-) diff --git a/Server/application/chukebao/controller/MessageController.php b/Server/application/chukebao/controller/MessageController.php index 1d44d14d..5a6402c2 100644 --- a/Server/application/chukebao/controller/MessageController.php +++ b/Server/application/chukebao/controller/MessageController.php @@ -2,6 +2,7 @@ namespace app\chukebao\controller; +use app\chukebao\model\FriendSettings; use library\ResponseHelper; use think\Db; @@ -98,6 +99,10 @@ class MessageController extends BaseController ->column('COUNT(*) AS cnt', 'wechatChatroomId'); } + $aiTypeData = []; + if (!empty($friendIds)) { + $aiTypeData = FriendSettings::where('friendId', 'in', $friendIds)->column('friendId,type'); + } foreach ($list as $k => &$v) { @@ -106,6 +111,7 @@ class MessageController extends BaseController $unreadCount = 0; + $v['aiType'] = 0; if (!empty($v['wechatFriendId'])) { $v['nickname'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['nickname'] : ''; $v['avatar'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['avatar'] : ''; @@ -115,6 +121,7 @@ class MessageController extends BaseController $v['wechatId'] = !empty($friends[$v['wechatFriendId']]) ? $friends[$v['wechatFriendId']]['wechatId'] : ''; $v['labels'] = !empty($friends[$v['wechatFriendId']]) ? json_decode($friends[$v['wechatFriendId']]['labels'], true) : []; $unreadCount = isset($friendUnreadMap[$v['wechatFriendId']]) ? (int)$friendUnreadMap[$v['wechatFriendId']] : 0; + $v['aiType'] = isset($aiTypeData[$v['wechatFriendId']]) ? $aiTypeData[$v['wechatFriendId']] : 0; unset($v['chatroomId']); } diff --git a/Server/application/chukebao/controller/WechatFriendController.php b/Server/application/chukebao/controller/WechatFriendController.php index 573e6811..5851e0f8 100644 --- a/Server/application/chukebao/controller/WechatFriendController.php +++ b/Server/application/chukebao/controller/WechatFriendController.php @@ -24,50 +24,50 @@ class WechatFriendController extends BaseController $list = $query->page($page, $limit)->select(); - /* // 提取所有好友ID + // 提取所有好友ID $friendIds = array_column($list, 'id'); - // 一次性查询所有好友的未读消息数量 - $unreadCounts = []; - if (!empty($friendIds)) { - $unreadResults = Db::table('s2_wechat_message') - ->field('wechatFriendId, COUNT(*) as count') - ->where('wechatFriendId', 'in', $friendIds) - ->where('isRead', 0) - ->group('wechatFriendId') - ->select(); - if (!empty($unreadResults)) { - foreach ($unreadResults as $result) { - $unreadCounts[$result['wechatFriendId']] = $result['count']; - } - } - } + /* // 一次性查询所有好友的未读消息数量 + $unreadCounts = []; + if (!empty($friendIds)) { + $unreadResults = Db::table('s2_wechat_message') + ->field('wechatFriendId, COUNT(*) as count') + ->where('wechatFriendId', 'in', $friendIds) + ->where('isRead', 0) + ->group('wechatFriendId') + ->select(); + if (!empty($unreadResults)) { + foreach ($unreadResults as $result) { + $unreadCounts[$result['wechatFriendId']] = $result['count']; + } + } + } - // 一次性查询所有好友的最新消息 - $latestMessages = []; - if (!empty($friendIds)) { - // 使用子查询获取每个好友的最新消息ID - $subQuery = Db::table('s2_wechat_message') - ->field('MAX(id) as max_id, wechatFriendId') - ->where('wechatFriendId', 'in', $friendIds) - ->group('wechatFriendId') - ->buildSql(); + // 一次性查询所有好友的最新消息 + $latestMessages = []; + if (!empty($friendIds)) { + // 使用子查询获取每个好友的最新消息ID + $subQuery = Db::table('s2_wechat_message') + ->field('MAX(id) as max_id, wechatFriendId') + ->where('wechatFriendId', 'in', $friendIds) + ->group('wechatFriendId') + ->buildSql(); - if (!empty($subQuery)) { - // 查询最新消息的详细信息 - $messageResults = Db::table('s2_wechat_message') - ->alias('m') - ->join([$subQuery => 'sub'], 'm.id = sub.max_id') - ->field('m.*, sub.wechatFriendId') - ->select(); + if (!empty($subQuery)) { + // 查询最新消息的详细信息 + $messageResults = Db::table('s2_wechat_message') + ->alias('m') + ->join([$subQuery => 'sub'], 'm.id = sub.max_id') + ->field('m.*, sub.wechatFriendId') + ->select(); - if (!empty($messageResults)) { - foreach ($messageResults as $message) { - $latestMessages[$message['wechatFriendId']] = $message; - } - } - } - }*/ + if (!empty($messageResults)) { + foreach ($messageResults as $message) { + $latestMessages[$message['wechatFriendId']] = $message; + } + } + } + }*/ $aiTypeData = []; From 175c78c3d0f530381152ce7add1e91fb7e2e8a38 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Wed, 29 Oct 2025 10:30:50 +0800 Subject: [PATCH 03/14] =?UTF-8?q?=E6=95=B0=E6=99=BA=E5=91=98=E5=B7=A5?= =?UTF-8?q?=E5=85=8D=E5=AF=86=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/PasswordLoginController.php | 42 +- Server/application/cunkebao/config/route.php | 9 + .../controller/StoreAccountController.php | 404 ++++++++++++++++++ .../store/controller/LoginController.php | 25 ++ Store_vue/api/modules/auth.js | 8 +- Store_vue/pages/login/index.vue | 397 +++++------------ 6 files changed, 572 insertions(+), 313 deletions(-) create mode 100644 Server/application/cunkebao/controller/StoreAccountController.php create mode 100644 Server/application/store/controller/LoginController.php diff --git a/Server/application/common/controller/PasswordLoginController.php b/Server/application/common/controller/PasswordLoginController.php index a3748a06..fd6da7ec 100644 --- a/Server/application/common/controller/PasswordLoginController.php +++ b/Server/application/common/controller/PasswordLoginController.php @@ -102,15 +102,27 @@ class PasswordLoginController extends BaseController * @param string $account 账号(手机号) * @param string $password 密码(可能是加密后的) * @param string $typeId 登录IP + * @param string $deviceId 本地设备imei * @return array * @throws \Exception */ - protected function doLogin(string $account, string $password, int $typeId): array + protected function doLogin(string $account, string $password, int $typeId, string $deviceId): array { // 获取用户信息 $member = $this->getUser($account, $password, $typeId); $deviceTotal = Db::name('device')->where(['companyId' => $member['companyId'],'deleteTime' => 0])->count(); + //更新设备imei + if ($typeId == 2 && !empty($deviceId)){ + $deviceUser = Db::name('device_user')->where(['companyId' => $member['companyId'],'userId' => $member['id'],'deleteTime' => 0])->find(); + if (!empty($deviceUser)){ + $s2_device = Db::table('s2_device')->where(['companyId' => $member['companyId'],'deleteTime' => 0,'id' => $deviceUser])->find(); + if (!empty($s2_device) && empty($s2_device['deviceImei'])){ + Db::table('s2_device')->where(['id' => $s2_device['id']])->update(['deviceImei' => $deviceId,'updateTime' => time()]); + Db::name('device')->where(['id' => $s2_device['id']])->update(['deviceImei' => $deviceId,'updateTime' => time()]); + } + } + } // 生成JWT令牌 @@ -126,34 +138,16 @@ class PasswordLoginController extends BaseController */ public function index() { - $params = $this->request->only(['account', 'password', 'typeId']); + $params = $this->request->only(['account', 'password', 'typeId','deviceId']); try { $userData = $this->dataValidate($params)->doLogin( $params['account'], $params['password'], - $params['typeId'] + $params['typeId'], + $params['deviceId'] ); - //同时登录客服系统 - /* if (!empty($userData['member']['passwordLocal'])){ - $params = [ - 'grant_type' => 'password', - 'username' => $userData['member']['account'], - 'password' => localDecrypt($userData['member']['passwordLocal']) - ]; - // 调用登录接口获取token - $headerData = ['client:kefu-client']; - $header = setHeader($headerData, '', 'plain'); - $result = requestCurl('https://s2.siyuguanli.com:9991/token', $params, 'POST', $header); - $token = handleApiResponse($result); - $userData['kefuData']['token'] = $token; - if (isset($token['access_token']) && !empty($token['access_token'])) { - $headerData = ['client:kefu-client']; - $header = setHeader($headerData, $token['access_token']); - $result = requestCurl( 'https://s2.siyuguanli.com:9991/api/account/self', [], 'GET', $header,'json'); - $self = handleApiResponse($result); - $userData['kefuData']['self'] = $self; - } - }*/ + + return ResponseHelper::success($userData, '登录成功'); } catch (Exception $e) { return ResponseHelper::error($e->getMessage(), $e->getCode()); diff --git a/Server/application/cunkebao/config/route.php b/Server/application/cunkebao/config/route.php index d7f4012e..b2c4217b 100644 --- a/Server/application/cunkebao/config/route.php +++ b/Server/application/cunkebao/config/route.php @@ -178,6 +178,15 @@ Route::group('v1/', function () { Route::get('detailType', 'app\cunkebao\controller\AiKnowledgeBaseController@detailType'); }); + // 门店端账号管理 + Route::group('store-accounts', function () { + Route::get('', 'app\cunkebao\controller\StoreAccountController@index'); // 获取账号列表 + Route::post('', 'app\cunkebao\controller\StoreAccountController@create'); // 创建账号 + Route::put('', 'app\cunkebao\controller\StoreAccountController@update'); // 编辑账号 + Route::delete('', 'app\cunkebao\controller\StoreAccountController@delete'); // 删除账号 + Route::post('disable', 'app\cunkebao\controller\StoreAccountController@disable'); // 禁用/启用账号 + }); + })->middleware(['jwt']); diff --git a/Server/application/cunkebao/controller/StoreAccountController.php b/Server/application/cunkebao/controller/StoreAccountController.php new file mode 100644 index 00000000..e58d6865 --- /dev/null +++ b/Server/application/cunkebao/controller/StoreAccountController.php @@ -0,0 +1,404 @@ +request->param('account', ''); + $username = $this->request->param('username', ''); + $phone = $this->request->param('phone', ''); + $password = $this->request->param('password', ''); + $deviceId = $this->request->param('deviceId', 0); + + $companyId = $this->getUserInfo('companyId'); + + // 参数验证 + if (empty($account)) { + return ResponseHelper::error('账号不能为空'); + } + if (empty($username)) { + return ResponseHelper::error('昵称不能为空'); + } + if (empty($phone)) { + return ResponseHelper::error('手机号不能为空'); + } + if (!preg_match('/^1[3-9]\d{9}$/', $phone)) { + return ResponseHelper::error('手机号格式不正确'); + } + if (empty($password)) { + return ResponseHelper::error('密码不能为空'); + } + if (strlen($password) < 6 || strlen($password) > 20) { + return ResponseHelper::error('密码长度必须在6-20个字符之间'); + } + if (empty($deviceId)) { + return ResponseHelper::error('请选择设备'); + } + + // 检查账号是否已存在(同一 typeId 和 companyId 下不能重复) + $existUser = Db::name('users')->where(['account' => $account, 'companyId' => $companyId, 'typeId' => 2, 'deleteTime' => 0]) + ->find(); + if ($existUser) { + return ResponseHelper::error('账号已存在'); + } + + // 检查手机号是否已存在(同一 typeId 和 companyId 下不能重复) + $existPhone = Db::name('users')->where(['phone' => $phone, 'companyId' => $companyId, 'typeId' => 2, 'deleteTime' => 0]) + ->find(); + if ($existPhone) { + return ResponseHelper::error('手机号已被使用'); + } + + // 检查设备是否存在且属于当前公司 + $device = Device::where('id', $deviceId) + ->where('companyId', $companyId) + ->find(); + if (!$device) { + return ResponseHelper::error('设备不存在或没有权限'); + } + + // 开始事务 + Db::startTrans(); + try { + // 创建用户 + $userData = [ + 'account' => $account, + 'username' => $username, + 'phone' => $phone, + 'passwordMd5' => md5($password), + 'passwordLocal' => localEncrypt($password), + 'avatar' => 'https://img.icons8.com/color/512/circled-user-male-skin-type-7.png', + 'isAdmin' => 0, + 'companyId' => $companyId, + 'typeId' => 2, // 门店端固定为2 + 'status' => 1, // 默认可用 + 'balance' => 0, + 'tokens' => 0, + 'createTime' => time(), + ]; + + $userId = Db::name('users')->insertGetId($userData); + + // 绑定设备 + Db::name('device_user')->insert([ + 'companyId' => $companyId, + 'userId' => $userId, + 'deviceId' => $deviceId, + 'deleteTime' => 0, + ]); + + // 提交事务 + Db::commit(); + + return ResponseHelper::success('创建账号成功'); + } catch (\Exception $e) { + Db::rollback(); + throw $e; + } + } catch (\Exception $e) { + return ResponseHelper::error($e->getMessage(), $e->getCode() ?: 500); + } + } + + /** + * 编辑账号 + * @return \think\response\Json + */ + public function update() + { + try { + $userId = $this->request->param('userId', 0); + $account = $this->request->param('account', ''); + $username = $this->request->param('username', ''); + $phone = $this->request->param('phone', ''); + $password = $this->request->param('password', ''); + $deviceId = $this->request->param('deviceId', 0); + + $companyId = $this->getUserInfo('companyId'); + + // 参数验证 + if (empty($userId)) { + return ResponseHelper::error('用户ID不能为空'); + } + + // 检查用户是否存在且属于当前公司 + $user = Db::name('users')->where(['id' => $userId, 'companyId' => $companyId, 'typeId' => 2])->find(); + if (!$user) { + return ResponseHelper::error('用户不存在或没有权限'); + } + + $updateData = []; + + // 更新账号 + if (!empty($account)) { + // 检查账号是否已被其他用户使用(同一 typeId 下) + $existUser = Db::name('users')->where(['account' => $account, 'companyId' => $companyId, 'typeId' => 2, 'deleteTime' => 0]) + ->where('id', '<>', $userId) + ->find(); + if ($existUser) { + return ResponseHelper::error('账号已被使用'); + } + $updateData['account'] = $account; + } + + // 更新昵称 + if (!empty($username)) { + $updateData['username'] = $username; + } + + // 更新手机号 + if (!empty($phone)) { + if (!preg_match('/^1[3-9]\d{9}$/', $phone)) { + return ResponseHelper::error('手机号格式不正确'); + } + // 检查手机号是否已被其他用户使用(同一 typeId 下) + $existPhone = Db::name('users')->where(['phone' => $phone, 'companyId' => $companyId, 'typeId' => 2, 'deleteTime' => 0]) + ->where('id', '<>', $userId) + ->find(); + if ($existPhone) { + return ResponseHelper::error('手机号已被使用'); + } + $updateData['phone'] = $phone; + } + + // 更新密码 + if (!empty($password)) { + if (strlen($password) < 6 || strlen($password) > 20) { + return ResponseHelper::error('密码长度必须在6-20个字符之间'); + } + $updateData['passwordMd5'] = md5($password); + $updateData['passwordLocal'] = localEncrypt($password); + } + + // 更新设备绑定 + if (!empty($deviceId)) { + // 检查设备是否存在且属于当前公司 + $device = Device::where('id', $deviceId) + ->where('companyId', $companyId) + ->find(); + if (!$device) { + return ResponseHelper::error('设备不存在或没有权限'); + } + } + + // 开始事务 + Db::startTrans(); + try { + // 更新用户信息 + if (!empty($updateData)) { + $updateData['updateTime'] = time(); + Db::name('users')->where(['id' => $userId])->update($updateData); + } + + // 更新设备绑定 + if (!empty($deviceId)) { + // 删除旧的设备绑定 + Db::name('device_user')->where(['userId' => $userId, 'companyId' => $companyId])->delete(); + + // 添加新的设备绑定 + Db::name('device_user')->insert([ + 'companyId' => $companyId, + 'userId' => $userId, + 'deviceId' => $deviceId, + 'deleteTime' => 0, + ]); + } + + // 提交事务 + Db::commit(); + + return ResponseHelper::success('更新账号成功'); + } catch (\Exception $e) { + Db::rollback(); + throw $e; + } + } catch (\Exception $e) { + return ResponseHelper::error($e->getMessage(), $e->getCode() ?: 500); + } + } + + /** + * 删除账号 + * @return \think\response\Json + */ + public function delete() + { + try { + $userId = $this->request->param('userId', 0); + $companyId = $this->getUserInfo('companyId'); + + if (empty($userId)) { + return ResponseHelper::error('用户ID不能为空'); + } + + // 检查用户是否存在且属于当前公司 + $user = Db::name('users')->where(['id' => $userId, 'companyId' => $companyId, 'typeId' => 2])->find(); + if (!$user) { + return ResponseHelper::error('用户不存在或没有权限'); + } + + // 检查是否是管理账号 + if ($user['isAdmin'] == 1) { + return ResponseHelper::error('管理账号无法删除'); + } + + // 软删除用户 + Db::name('users')->where(['id' => $userId])->update([ + 'deleteTime' => time(), + 'updateTime' => time() + ]); + + // 软删除设备绑定关系 + Db::name('device_user')->where(['userId' => $userId, 'companyId' => $companyId])->update([ + 'deleteTime' => time() + ]); + + return ResponseHelper::success('删除账号成功'); + } catch (\Exception $e) { + return ResponseHelper::error($e->getMessage(), $e->getCode() ?: 500); + } + } + + /** + * 禁用/启用账号 + * @return \think\response\Json + */ + public function disable() + { + try { + $userId = $this->request->param('userId', 0); + $status = $this->request->param('status', -1); // 0-禁用 1-启用 + $companyId = $this->getUserInfo('companyId'); + + if (empty($userId)) { + return ResponseHelper::error('用户ID不能为空'); + } + + if ($status != 0 && $status != 1) { + return ResponseHelper::error('状态参数错误'); + } + + // 检查用户是否存在且属于当前公司 + $user = Db::name('users')->where(['id' => $userId, 'companyId' => $companyId, 'typeId' => 2])->find(); + if (!$user) { + return ResponseHelper::error('用户不存在或没有权限'); + } + + // 检查是否是管理账号 + if ($user['isAdmin'] == 1 && $status == 0) { + return ResponseHelper::error('管理账号无法禁用'); + } + + // 更新状态 + Db::name('users')->where(['id' => $userId])->update([ + 'status' => $status, + 'updateTime' => time() + ]); + + $message = $status == 0 ? '禁用账号成功' : '启用账号成功'; + return ResponseHelper::success($message); + } catch (\Exception $e) { + return ResponseHelper::error($e->getMessage(), $e->getCode() ?: 500); + } + } + + /** + * 获取账号列表 + * @return \think\response\Json + */ + public function index() + { + try { + $keyword = $this->request->param('keyword', ''); + $status = $this->request->param('status', ''); + $page = $this->request->param('page/d', 1); + $limit = $this->request->param('limit/d', 10); + + $companyId = $this->getUserInfo('companyId'); + + // 构建查询条件 + $where = [ + ['companyId', '=', $companyId], + ['typeId', '=', 2], // 只查询门店端账号 + ['deleteTime', '=', 0] + ]; + + // 关键词搜索(账号、昵称、手机号) + if (!empty($keyword)) { + $where[] = ['account|username|phone', "LIKE", '%'.$keyword.'%']; + } + + // 状态筛选 + if ($status !== '') { + $where[] = ['status', '=', $status]; + } + + // 分页查询 + $query = Db::name('users')->where($where); + $total = $query->count(); + + $list = $query->field('id,account,username,phone,avatar,isAdmin,status,balance,tokens,createTime') + ->order('id desc') + ->page($page, $limit) + ->select(); + + + // 获取每个账号绑定的设备(单个设备) + if (!empty($list)) { + $userIds = array_column($list, 'id'); + $deviceBindings = Db::name('device_user') + ->alias('du') + ->join('device d', 'd.id = du.deviceId', 'left') + ->where([ + ['du.userId', 'in', $userIds], + ['du.companyId', '=', $companyId], + ['du.deleteTime', '=', 0] + ]) + ->field('du.userId,du.deviceId,d.imei,d.memo') + ->order('du.id desc') + ->select(); + + // 组织设备数据(单个设备对象) + $deviceMap = []; + foreach ($deviceBindings as $binding) { + $deviceMap[$binding['userId']] = [ + 'deviceId' => $binding['deviceId'], + 'imei' => $binding['imei'], + 'memo' => $binding['memo'] + ]; + } + + // 将设备信息添加到用户数据中 + foreach ($list as &$item) { + $item['device'] = $deviceMap[$item['id']] ?? null; + } + } + + return ResponseHelper::success([ + 'list' => $list, + 'total' => $total, + 'page' => $page, + 'limit' => $limit + ]); + } catch (\Exception $e) { + return ResponseHelper::error($e->getMessage(), $e->getCode() ?: 500); + } + } +} diff --git a/Server/application/store/controller/LoginController.php b/Server/application/store/controller/LoginController.php new file mode 100644 index 00000000..f507295a --- /dev/null +++ b/Server/application/store/controller/LoginController.php @@ -0,0 +1,25 @@ +request->param('deviceId', ''); + if (empty($deviceId)) { + return errorJson('缺少必要参数'); + } + + $user = Db::name('user')->alias('u') + ->join('device_user du','u.id = du.userId and u.companyId = du.companyId') + ->join('device d','du.deviceId = d.id and u.companyId = du.companyId') + ->where(['d.deviceImei' => $deviceId,'u.deleteTime' => 0,'du.deleteTime' => 0,'d.deleteTime'=> 0 ]) + ->find(); + + exit_data($user); + + } +} \ No newline at end of file diff --git a/Store_vue/api/modules/auth.js b/Store_vue/api/modules/auth.js index b6855228..cd7c7f56 100644 --- a/Store_vue/api/modules/auth.js +++ b/Store_vue/api/modules/auth.js @@ -3,14 +3,18 @@ import { request, requestWithRetry } from '../config' // 认证相关API export const authApi = { // 用户登录 - login: (account, password) => { + // @param {string} account - 账号 + // @param {string} password - 密码 + // @param {string} deviceId - 设备ID(仅APP端传递,H5端为空字符串) + login: (account, password, deviceId) => { return request({ url: '/v1/auth/login', method: 'POST', data: { account: account, password: password, - typeId: 2 // 固定为2 + typeId: 2, // 固定为2 + deviceId: deviceId || '' // 设备ID(仅APP端有值) } }) } diff --git a/Store_vue/pages/login/index.vue b/Store_vue/pages/login/index.vue index 87bcaec9..4c5eb2e9 100644 --- a/Store_vue/pages/login/index.vue +++ b/Store_vue/pages/login/index.vue @@ -8,96 +8,39 @@ - - - - 验证码登录 - - - 密码登录 + + + + + + + - - - 您所在地区仅支持 手机号 + + + + + + + + - - - - - +86 - - - - - - - - {{ codeText }} - - - - - - - - - - +86 - - - - - - - - - - - - - @@ -118,40 +61,6 @@ 登录 - - - - - - - - - - 联系我们 @@ -168,44 +77,63 @@ export default { data() { return { - loginType: 'password', // 默认密码登录 - phone: '', // 手机号 - code: '', // 验证码 + account: '', // 账号 password: '', // 密码 passwordVisible: false, // 密码是否可见 agreement: true, // 是否同意协议 - codeSending: false, // 是否正在发送验证码 - countdown: 60, // 倒计时 - codeText: '发送验证码' // 验证码按钮文本 + deviceId: '' // 设备ID } }, // 页面加载时检查token onLoad() { this.checkTokenStatus(); + this.getDeviceId(); }, // 页面显示时检查token onShow() { this.checkTokenStatus(); }, computed: { - // 验证手机号是否有效 - isPhoneValid() { - return this.phone && this.phone.length === 11; - }, // 验证是否可以登录 canLogin() { - if (!this.phone || !this.agreement) { - return false; - } - - if (this.loginType === 'code') { - return this.isPhoneValid && this.code && this.code.length === 6; - } else { - return this.password && this.password.length >= 6; - } + return this.account && + this.password && + this.password.length >= 6 && + this.agreement; } }, methods: { + // 获取设备ID(仅APP端) + getDeviceId() { + // #ifdef APP-PLUS + try { + // 获取设备信息 + uni.getSystemInfo({ + success: (res) => { + console.log('设备信息:', res); + // 优先使用deviceId,如果没有则使用uuid或其他唯一标识 + this.deviceId = res.deviceId || res.uuid || res.system + '_' + res.model; + console.log('APP设备ID:', this.deviceId); + }, + fail: (err) => { + console.error('获取设备信息失败:', err); + // 如果获取失败,使用一个临时ID + this.deviceId = 'unknown_device'; + } + }); + } catch (err) { + console.error('获取设备ID异常:', err); + this.deviceId = 'unknown_device'; + } + // #endif + + // #ifdef H5 + // H5端不传设备ID + this.deviceId = ''; + console.log('H5端不传设备ID'); + // #endif + }, + // 检查token状态 checkTokenStatus() { // 如果token有效,则跳转到聊天页面 @@ -219,37 +147,7 @@ uni.navigateBack(); }, - // 切换登录类型 - switchLoginType(type) { - this.loginType = type; - }, - - // 发送验证码 - sendCode() { - if (this.codeSending || !this.isPhoneValid) return; - - this.codeSending = true; - this.countdown = 60; - this.codeText = `${this.countdown}秒后重发`; - - // 模拟发送验证码 - uni.showToast({ - title: '验证码已发送', - icon: 'success' - }); - - const timer = setInterval(() => { - this.countdown--; - this.codeText = `${this.countdown}秒后重发`; - - if (this.countdown <= 0) { - clearInterval(timer); - this.codeSending = false; - this.codeText = '发送验证码'; - } - }, 1000); - }, - + // 用户协议复选框变化 checkboxChange(){ this.agreement = !this.agreement }, @@ -258,7 +156,6 @@ // 处理登录 async handleLogin() { // 检查是否同意协议 - console.log(this.agreement) if (!this.agreement) { uni.showToast({ title: '请阅读并同意用户协议和隐私政策', @@ -268,24 +165,28 @@ return; } - if (!this.canLogin) { - // 显示错误原因 - if (!this.isPhoneValid) { - uni.showToast({ - title: '请输入正确的手机号', - icon: 'none' - }); - } else if (this.loginType === 'code' && (!this.code || this.code.length !== 6)) { - uni.showToast({ - title: '请输入6位验证码', - icon: 'none' - }); - } else if (this.loginType === 'password' && (!this.password || this.password.length < 6)) { - uni.showToast({ - title: '密码不能少于6位', - icon: 'none' - }); - } + // 验证表单 + if (!this.account) { + uni.showToast({ + title: '请输入账号', + icon: 'none' + }); + return; + } + + if (!this.password) { + uni.showToast({ + title: '请输入密码', + icon: 'none' + }); + return; + } + + if (this.password.length < 6) { + uni.showToast({ + title: '密码不能少于6位', + icon: 'none' + }); return; } @@ -295,11 +196,16 @@ }); try { - // 调用登录API - const loginPassword = this.loginType === 'password' ? this.password : this.code; - const response = await authApi.login(this.phone, loginPassword); + // 调用登录API,传递账号、密码和设备ID(仅APP端传递) + const response = await authApi.login(this.account, this.password, this.deviceId); - console.log(response); + console.log('登录响应:', response); + // #ifdef APP-PLUS + console.log('APP端登录 - 设备ID:', this.deviceId); + // #endif + // #ifdef H5 + console.log('H5端登录 - 不传设备ID'); + // #endif if (response.code === 200) { // 成功code是200 // 登录成功,缓存token信息 @@ -337,20 +243,12 @@ } }, - // 第三方登录 - handleThirdLogin(platform) { - // uni.showToast({ - // title: `${platform === 'wechat' ? '微信' : 'Apple'}登录功能暂未实现`, - // icon: 'none' - // }); - }, - // 打开协议 openAgreement(type) { - // uni.showToast({ - // title: `打开${type === 'user' ? '用户协议' : '隐私政策'}`, - // icon: 'none' - // }); + uni.showToast({ + title: `打开${type === 'user' ? '用户协议' : '隐私政策'}`, + icon: 'none' + }); }, // 联系我们 @@ -404,42 +302,12 @@ padding: 0 30px; } - .tab-container { - display: flex; - justify-content: space-between; - margin-bottom: 20px; - position: relative; - } - - .tab-item { - position: relative; - text-align: center; - padding: 10px 0; - font-size: 16px; - color: #666; - flex: 1; - } - - .tab-item.active { - color: #4080ff; + .login-title { + font-size: 24px; font-weight: bold; - } - - .tab-item.active::after { - content: ''; - position: absolute; - bottom: -2px; - left: 25%; - width: 50%; - height: 3px; - background-color: #4080ff; - border-radius: 2px; - } - - .tip-text { - font-size: 14px; - color: #666; - margin-bottom: 20px; + color: #333; + margin: 30px 0 40px; + text-align: center; } .input-item { @@ -447,15 +315,8 @@ align-items: center; border-bottom: 1px solid #eee; padding: 12px 0; - height: 24px; - } - - .input-prefix { - color: #333; - margin-right: 10px; - padding-right: 10px; - border-right: 1px solid #eee; - font-size: 14px; + min-height: 50px; + position: relative; } .input-field { @@ -464,25 +325,9 @@ font-size: 15px; } - .code-input-box { - position: relative; - } - - .send-code-btn { + .password-icon { position: absolute; right: 0; - background-color: #4080ff; - color: #fff; - padding: 5px 10px; - border-radius: 4px; - font-size: 12px; - } - - .send-code-btn.disabled { - background-color: #ccc; - } - - .password-icon { padding: 0 5px; height: 100%; display: flex; @@ -526,28 +371,6 @@ background-color: #4080ff; } - .divider { - display: flex; - align-items: center; - margin: 20px 0; - } - - .divider-line { - flex: 1; - height: 1px; - background-color: #eee; - } - - .divider-text { - color: #999; - padding: 0 15px; - font-size: 14px; - } - - .third-party-login { - margin: 20px 0; - } - .contact-us { text-align: center; color: #999; From 942f08bbf21e191b4157b94cf6dc0755a2371b74 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Wed, 29 Oct 2025 10:38:11 +0800 Subject: [PATCH 04/14] 111 --- .../application/common/controller/PasswordLoginController.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Server/application/common/controller/PasswordLoginController.php b/Server/application/common/controller/PasswordLoginController.php index fd6da7ec..534a978a 100644 --- a/Server/application/common/controller/PasswordLoginController.php +++ b/Server/application/common/controller/PasswordLoginController.php @@ -106,7 +106,7 @@ class PasswordLoginController extends BaseController * @return array * @throws \Exception */ - protected function doLogin(string $account, string $password, int $typeId, string $deviceId): array + protected function doLogin(string $account, string $password, int $typeId): array { // 获取用户信息 $member = $this->getUser($account, $password, $typeId); @@ -144,7 +144,6 @@ class PasswordLoginController extends BaseController $params['account'], $params['password'], $params['typeId'], - $params['deviceId'] ); From 1862b11c0dd58415bd01adfb2467fc496f17c1d2 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Wed, 29 Oct 2025 10:52:44 +0800 Subject: [PATCH 05/14] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/controller/PasswordLoginController.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Server/application/common/controller/PasswordLoginController.php b/Server/application/common/controller/PasswordLoginController.php index 534a978a..232e4e68 100644 --- a/Server/application/common/controller/PasswordLoginController.php +++ b/Server/application/common/controller/PasswordLoginController.php @@ -106,7 +106,7 @@ class PasswordLoginController extends BaseController * @return array * @throws \Exception */ - protected function doLogin(string $account, string $password, int $typeId): array + protected function doLogin(string $account, string $password, int $typeId, string $deviceId): array { // 获取用户信息 $member = $this->getUser($account, $password, $typeId); @@ -116,10 +116,10 @@ class PasswordLoginController extends BaseController if ($typeId == 2 && !empty($deviceId)){ $deviceUser = Db::name('device_user')->where(['companyId' => $member['companyId'],'userId' => $member['id'],'deleteTime' => 0])->find(); if (!empty($deviceUser)){ - $s2_device = Db::table('s2_device')->where(['companyId' => $member['companyId'],'deleteTime' => 0,'id' => $deviceUser])->find(); - if (!empty($s2_device) && empty($s2_device['deviceImei'])){ - Db::table('s2_device')->where(['id' => $s2_device['id']])->update(['deviceImei' => $deviceId,'updateTime' => time()]); - Db::name('device')->where(['id' => $s2_device['id']])->update(['deviceImei' => $deviceId,'updateTime' => time()]); + $device = Db::name('device')->where(['companyId' => $member['companyId'],'deleteTime' => 0,'id' => $deviceUser['deviceId']])->find(); + if (!empty($device) && empty($device['deviceImei'])){ + Db::table('s2_device')->where(['id' => $device['id']])->update(['deviceImei' => $deviceId,'updateTime' => time()]); + Db::name('device')->where(['id' => $device['id']])->update(['deviceImei' => $deviceId,'updateTime' => time()]); } } } @@ -140,10 +140,12 @@ class PasswordLoginController extends BaseController { $params = $this->request->only(['account', 'password', 'typeId','deviceId']); try { + $deviceId = isset($params['deviceId']) ? $params['deviceId'] : ''; $userData = $this->dataValidate($params)->doLogin( $params['account'], $params['password'], $params['typeId'], + $deviceId ); From da8bcb04c946a9e59188b7b4572c6796011e43be Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Wed, 29 Oct 2025 11:48:02 +0800 Subject: [PATCH 06/14] =?UTF-8?q?=E5=85=8D=E5=AF=86=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/store/config/route.php | 4 +- .../store/controller/LoginController.php | 27 ++++-- Store_vue/api/config/index.js | 4 +- Store_vue/api/modules/auth.js | 12 +++ Store_vue/pages/login/index.vue | 85 +++++++++++++++++++ 5 files changed, 123 insertions(+), 9 deletions(-) diff --git a/Server/application/store/config/route.php b/Server/application/store/config/route.php index dbae6f08..c5fcfe0f 100644 --- a/Server/application/store/config/route.php +++ b/Server/application/store/config/route.php @@ -45,4 +45,6 @@ Route::group('v1/store', function () { Route::get('detail', 'app\store\controller\VendorController@detail'); // 获取供应商详情 Route::post('order', 'app\store\controller\VendorController@createOrder'); // 创建订单 }); -})->middleware(['jwt']); \ No newline at end of file +})->middleware(['jwt']); + +Route::get('v1/store/login', 'app\store\controller\LoginController@index'); \ No newline at end of file diff --git a/Server/application/store/controller/LoginController.php b/Server/application/store/controller/LoginController.php index f507295a..ea364f4e 100644 --- a/Server/application/store/controller/LoginController.php +++ b/Server/application/store/controller/LoginController.php @@ -2,9 +2,11 @@ namespace app\store\controller; +use app\common\util\JwtUtil; use think\Db; +use think\Controller; -class LoginController extends BaseController +class LoginController extends Controller { public function index() { @@ -13,13 +15,26 @@ class LoginController extends BaseController return errorJson('缺少必要参数'); } - $user = Db::name('user')->alias('u') - ->join('device_user du','u.id = du.userId and u.companyId = du.companyId') - ->join('device d','du.deviceId = d.id and u.companyId = du.companyId') - ->where(['d.deviceImei' => $deviceId,'u.deleteTime' => 0,'du.deleteTime' => 0,'d.deleteTime'=> 0 ]) + $user = Db::name('users')->alias('u') + ->field('u.*') + ->join('device_user du', 'u.id = du.userId and u.companyId = du.companyId') + ->join('device d', 'du.deviceId = d.id and u.companyId = du.companyId') + ->where(['d.deviceImei' => $deviceId, 'u.deleteTime' => 0, 'du.deleteTime' => 0, 'd.deleteTime' => 0]) ->find(); + $member = array_merge($user, [ + 'lastLoginIp' => $this->request->ip(), + 'lastLoginTime' => time() + ]); - exit_data($user); + // 生成JWT令牌 + $token = JwtUtil::createToken($user, 86400 * 30); + $token_expired = time() + 86400 * 30; + $data = [ + 'member' => $member, + 'token' => $token, + 'token_expired' => $token_expired + ]; + return successJson($data, '登录成功'); } } \ No newline at end of file diff --git a/Store_vue/api/config/index.js b/Store_vue/api/config/index.js index 961630bf..29c6eba6 100644 --- a/Store_vue/api/config/index.js +++ b/Store_vue/api/config/index.js @@ -1,8 +1,8 @@ // API配置文件 // 基础配置 -export const BASE_URL = 'http://yishi.com' -//export const BASE_URL = 'https://ckbapi.quwanzhi.com' +//export const BASE_URL = 'http://yishi.com' +export const BASE_URL = 'https://ckbapi.quwanzhi.com' // 获取请求头 const getHeaders = (options = {}) => { diff --git a/Store_vue/api/modules/auth.js b/Store_vue/api/modules/auth.js index cd7c7f56..cee024a6 100644 --- a/Store_vue/api/modules/auth.js +++ b/Store_vue/api/modules/auth.js @@ -17,5 +17,17 @@ export const authApi = { deviceId: deviceId || '' // 设备ID(仅APP端有值) } }) + }, + + // 免密登录 + // @param {string} deviceId - 设备ID + noPasswordLogin: (deviceId) => { + return request({ + url: '/v1/store/login', + method: 'GET', + data: { + deviceId: deviceId || '' + } + }) } } \ No newline at end of file diff --git a/Store_vue/pages/login/index.vue b/Store_vue/pages/login/index.vue index 4c5eb2e9..29d345d8 100644 --- a/Store_vue/pages/login/index.vue +++ b/Store_vue/pages/login/index.vue @@ -61,6 +61,14 @@ 登录 + + + 联系我们 @@ -243,6 +251,66 @@ } }, + // 处理免密登录 + async handleNoPasswordLogin() { + // 检查设备ID是否存在 + if (!this.deviceId) { + uni.showToast({ + title: '获取设备信息失败,请重试', + icon: 'none' + }); + return; + } + + uni.showLoading({ + title: '免密登录中...', + mask: true + }); + + try { + // 调用免密登录API + const response = await authApi.noPasswordLogin(this.deviceId); + + console.log('免密登录响应:', response); + console.log('设备ID:', this.deviceId); + + if (response.code === 200) { + // 登录成功,缓存token信息 + const { token, member, token_expired } = response.data; + + // 存储token信息 + uni.setStorageSync('token', token); + uni.setStorageSync('member', JSON.stringify(member)); + uni.setStorageSync('token_expired', token_expired); + + uni.showToast({ + title: '免密登录成功', + icon: 'success' + }); + + // 登录成功后跳转到对话页面 + setTimeout(() => { + redirectToChat(); + }, 1500); + } else { + // 登录失败,显示错误信息 + uni.showToast({ + title: response.msg || '免密登录失败,请使用账号密码登录', + icon: 'none', + duration: 2000 + }); + } + } catch (err) { + console.error('免密登录失败:', err); + uni.showToast({ + title: '网络异常,请稍后重试', + icon: 'none' + }); + } finally { + uni.hideLoading(); + } + }, + // 打开协议 openAgreement(type) { uni.showToast({ @@ -371,6 +439,23 @@ background-color: #4080ff; } + .no-password-login-btn { + height: 44px; + line-height: 44px; + text-align: center; + background-color: #fff; + color: #4080ff; + border: 1px solid #4080ff; + border-radius: 22px; + margin: 10px 0 20px; + font-size: 16px; + transition: all 0.3s; + } + + .no-password-login-btn:active { + background-color: #f0f5ff; + } + .contact-us { text-align: center; color: #999; From f684c4cb3f584f1fb0524677567c729f348c2dae Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Wed, 29 Oct 2025 15:31:43 +0800 Subject: [PATCH 07/14] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/chukebao/controller/AiChatController.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Server/application/chukebao/controller/AiChatController.php b/Server/application/chukebao/controller/AiChatController.php index 29699262..5583d583 100644 --- a/Server/application/chukebao/controller/AiChatController.php +++ b/Server/application/chukebao/controller/AiChatController.php @@ -109,8 +109,8 @@ class AiChatController extends BaseController return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消'); } $conversationId = $this->ensureConversation($friendSettings, $setting, $params); - - if (!$conversationId) { + + if (empty($conversationId)) { $this->clearRequestCache(); return ResponseHelper::error('创建会话失败'); } @@ -374,7 +374,6 @@ class AiChatController extends BaseController $friendSettings->conversationId = $conversationId; $friendSettings->conversationTime = time(); $friendSettings->save(); - return $conversationId; } From 8ae8e66a3aee7179dda20036f6a293fa130f380c Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Wed, 29 Oct 2025 15:48:48 +0800 Subject: [PATCH 08/14] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/chukebao/controller/AiChatController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Server/application/chukebao/controller/AiChatController.php b/Server/application/chukebao/controller/AiChatController.php index 5583d583..4090d578 100644 --- a/Server/application/chukebao/controller/AiChatController.php +++ b/Server/application/chukebao/controller/AiChatController.php @@ -128,8 +128,8 @@ class AiChatController extends BaseController return ResponseHelper::error('该好友有新的AI对话请求正在处理中,当前请求已被取消'); } $chatId = $this->createAiChat($setting, $friendSettings, $msgData); - - if (!$chatId) { + + if (empty($chatId)) { $this->clearRequestCache(); return ResponseHelper::error('创建对话失败'); } From 903a29ed0908b80080d72314ed2fa61ff1077da6 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Thu, 30 Oct 2025 10:58:45 +0800 Subject: [PATCH 09/14] =?UTF-8?q?app=E6=9B=B4=E6=96=B0=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/common/config/route.php | 2 +- Server/application/common/controller/Api.php | 2 +- Server/application/cunkebao/config/route.php | 2 + .../controller/AiKnowledgeBaseController.php | 120 +++++++++++++++++- .../cunkebao/controller/TokensController.php | 114 +++++++++++++++++ 5 files changed, 233 insertions(+), 7 deletions(-) diff --git a/Server/application/common/config/route.php b/Server/application/common/config/route.php index fc94c79b..54001bde 100644 --- a/Server/application/common/config/route.php +++ b/Server/application/common/config/route.php @@ -30,4 +30,4 @@ Route::group('v1/pay', function () { -Route::get('app/update', 'app\common\controller\PaymentService@createOrder'); \ No newline at end of file +Route::get('v1/app/update', 'app\common\controller\Api@uploadApp'); //检测app是否需要更新 \ No newline at end of file diff --git a/Server/application/common/controller/Api.php b/Server/application/common/controller/Api.php index e95ca699..4887c310 100644 --- a/Server/application/common/controller/Api.php +++ b/Server/application/common/controller/Api.php @@ -136,7 +136,7 @@ class Api extends Controller return ResponseHelper::error('参数缺失'); } - if (!in_array($type,['ckb','ai_store'])){ + if (!in_array($type,['ckb','aiStore'])){ return ResponseHelper::error('参数错误'); } diff --git a/Server/application/cunkebao/config/route.php b/Server/application/cunkebao/config/route.php index b2c4217b..2b9d9665 100644 --- a/Server/application/cunkebao/config/route.php +++ b/Server/application/cunkebao/config/route.php @@ -156,6 +156,7 @@ Route::group('v1/', function () { Route::get('list', 'app\cunkebao\controller\TokensController@getList'); Route::post('pay', 'app\cunkebao\controller\TokensController@pay'); Route::get('queryOrder', 'app\cunkebao\controller\TokensController@queryOrder'); + Route::get('orderList', 'app\cunkebao\controller\TokensController@getOrderList'); // 获取订单列表 }); @@ -174,6 +175,7 @@ Route::group('v1/', function () { Route::post('delete', 'app\cunkebao\controller\AiKnowledgeBaseController@delete'); Route::post('addType', 'app\cunkebao\controller\AiKnowledgeBaseController@addType'); Route::post('editType', 'app\cunkebao\controller\AiKnowledgeBaseController@editType'); + Route::put('updateTypeStatus', 'app\cunkebao\controller\AiKnowledgeBaseController@updateTypeStatus'); // 修改类型状态 Route::delete('deleteType', 'app\cunkebao\controller\AiKnowledgeBaseController@deleteType'); Route::get('detailType', 'app\cunkebao\controller\AiKnowledgeBaseController@detailType'); }); diff --git a/Server/application/cunkebao/controller/AiKnowledgeBaseController.php b/Server/application/cunkebao/controller/AiKnowledgeBaseController.php index 7ab048a9..8ba90722 100644 --- a/Server/application/cunkebao/controller/AiKnowledgeBaseController.php +++ b/Server/application/cunkebao/controller/AiKnowledgeBaseController.php @@ -39,14 +39,13 @@ class AiKnowledgeBaseController extends BaseController if ($includeSystem == 1) { // 包含系统类型和本公司创建的类型 - $where[] = ['type', '=', AiKnowledgeBaseType::TYPE_SYSTEM]; - $where[] = ['companyId|type', 'in', [$companyId, 0]]; + $where[] = ['companyId', 'in', [$companyId, 0]]; } else { // 只显示本公司创建的类型 $where[] = ['companyId', '=', $companyId]; $where[] = ['type', '=', AiKnowledgeBaseType::TYPE_USER]; } - + // 查询数据 $list = AiKnowledgeBaseType::where($where) ->order('type', 'asc') // 系统类型排在前面 @@ -80,6 +79,7 @@ class AiKnowledgeBaseController extends BaseController $description = $this->request->param('description', ''); $label = $this->request->param('label', []); $prompt = $this->request->param('prompt', ''); + $status = $this->request->param('status', 1); // 默认启用 // 参数验证 if (empty($name)) { @@ -103,8 +103,9 @@ class AiKnowledgeBaseController extends BaseController 'type' => AiKnowledgeBaseType::TYPE_USER, 'name' => $name, 'description' => $description, - 'label' => json_decode($label,256), + 'label' => json_encode($label,256), 'prompt' => $prompt, + 'status' => $status, 'companyId' => $companyId, 'userId' => $userId, 'createTime' => time(), @@ -142,6 +143,7 @@ class AiKnowledgeBaseController extends BaseController $description = $this->request->param('description', ''); $label = $this->request->param('label', []); $prompt = $this->request->param('prompt', ''); + $status = $this->request->param('status', ''); // 参数验证 if (empty($id)) { @@ -187,8 +189,11 @@ class AiKnowledgeBaseController extends BaseController // 更新数据 $typeModel->name = $name; $typeModel->description = $description; - $typeModel->label = $label; + $typeModel->label = json_encode($label,256); $typeModel->prompt = $prompt; + if ($status !== '') { + $typeModel->status = $status; + } $typeModel->updateTime = time(); if ($typeModel->save()) { @@ -202,6 +207,111 @@ class AiKnowledgeBaseController extends BaseController } } + /** + * 修改知识库类型状态 + * + * @return \think\response\Json + */ + public function updateTypeStatus() + { + try { + $companyId = $this->getUserInfo('companyId'); + if (empty($companyId)) { + return ResponseHelper::error('公司信息获取失败'); + } + + // 获取参数 + $id = $this->request->param('id', 0); + $status = $this->request->param('status', -1); + + // 参数验证 + if (empty($id)) { + return ResponseHelper::error('类型ID不能为空'); + } + + if ($status != 0 && $status != 1) { + return ResponseHelper::error('状态参数错误'); + } + + // 查找类型 + $typeModel = AiKnowledgeBaseType::where([ + ['id', '=', $id], + ['isDel', '=', 0] + ])->find(); + + if (!$typeModel) { + return ResponseHelper::error('类型不存在'); + } + + // 检查是否为系统类型 + if ($typeModel->isSystemType()) { + return ResponseHelper::error('系统类型不允许修改状态'); + } + + // 检查权限(只能修改本公司的类型) + if ($typeModel->companyId != $companyId) { + return ResponseHelper::error('无权限修改该类型'); + } + + // 更新状态 + $typeModel->status = $status; + $typeModel->updateTime = time(); + + if ($typeModel->save()) { + $message = $status == 0 ? '禁用成功' : '启用成功'; + return ResponseHelper::success([], $message); + } else { + return ResponseHelper::error('操作失败'); + } + + } catch (\Exception $e) { + return ResponseHelper::error('系统异常:' . $e->getMessage()); + } + } + + /** + * 获取知识库类型详情 + * + * @return \think\response\Json + */ + public function detailType() + { + try { + $companyId = $this->getUserInfo('companyId'); + if (empty($companyId)) { + return ResponseHelper::error('公司信息获取失败'); + } + + // 获取参数 + $id = $this->request->param('id', 0); + + // 参数验证 + if (empty($id)) { + return ResponseHelper::error('类型ID不能为空'); + } + + // 查找类型 + $typeModel = AiKnowledgeBaseType::where([ + ['id', '=', $id], + ['isDel', '=', 0] + ])->find(); + + if (!$typeModel) { + return ResponseHelper::error('类型不存在'); + } + + // 检查权限(系统类型或本公司的类型都可以查看) + if ($typeModel->companyId != 0 && $typeModel->companyId != $companyId) { + return ResponseHelper::error('无权限查看该类型'); + } + + return ResponseHelper::success($typeModel, '获取成功'); + + } catch (\Exception $e) { + return ResponseHelper::error('系统异常:' . $e->getMessage()); + } + } + /** * 删除知识库类型 * diff --git a/Server/application/cunkebao/controller/TokensController.php b/Server/application/cunkebao/controller/TokensController.php index df70d924..b6ec7728 100644 --- a/Server/application/cunkebao/controller/TokensController.php +++ b/Server/application/cunkebao/controller/TokensController.php @@ -115,4 +115,118 @@ class TokensController extends BaseController return ResponseHelper::success('','订单已支付'); } } + + /** + * 获取订单列表 + * @return \think\response\Json + */ + public function getOrderList() + { + try { + $page = $this->request->param('page', 1); + $limit = $this->request->param('limit', 10); + $status = $this->request->param('status', ''); // 订单状态筛选 + $keyword = $this->request->param('keyword', ''); // 关键词搜索(订单号) + $orderType = $this->request->param('orderType', ''); // 订单类型筛选 + $startTime = $this->request->param('startTime', ''); // 开始时间 + $endTime = $this->request->param('endTime', ''); // 结束时间 + + $userId = $this->getUserInfo('id'); + $companyId = $this->getUserInfo('companyId'); + + // 构建查询条件 + $where = [ + ['companyId', '=', $companyId] + ]; + + // 关键词搜索(订单号、商品名称) + if (!empty($keyword)) { + $where[] = ['orderNo|goodsName', 'like', '%' . $keyword . '%']; + } + + // 状态筛选 (0-待支付 1-已付款 2-已退款 3-付款失败) + if ($status !== '') { + $where[] = ['status', '=', $status]; + } + + // 订单类型筛选 + if ($orderType !== '') { + $where[] = ['orderType', '=', $orderType]; + } + + // 时间范围筛选 + if (!empty($startTime)) { + $where[] = ['createTime', '>=', strtotime($startTime)]; + } + if (!empty($endTime)) { + $where[] = ['createTime', '<=', strtotime($endTime . ' 23:59:59')]; + } + + // 分页查询 + $query = Order::where($where) + ->where(function($query) { + $query->whereNull('deleteTime')->whereOr('deleteTime', 0); + }); + $total = $query->count(); + + $list = $query->field('id,orderNo,goodsId,goodsName,goodsSpecs,orderType,money,status,payType,payTime,createTime') + ->order('id desc') + ->page($page, $limit) + ->select(); + + // 格式化数据 + foreach ($list as &$item) { + // 金额转换(分转元) + $item['money'] = round($item['money'] / 100, 2); + + // 解析商品规格 + if (!empty($item['goodsSpecs'])) { + $specs = is_string($item['goodsSpecs']) ? json_decode($item['goodsSpecs'], true) : $item['goodsSpecs']; + $item['goodsSpecs'] = $specs; + + // 添加算力数量 + if (isset($specs['tokens'])) { + $item['tokens'] = number_format($specs['tokens']); + } + } + + // 状态文本 + $statusText = [ + 0 => '待支付', + 1 => '已付款', + 2 => '已退款', + 3 => '付款失败' + ]; + $item['statusText'] = $statusText[$item['status']] ?? '未知'; + + // 订单类型文本 + $orderTypeText = [ + 1 => '购买算力' + ]; + $item['orderTypeText'] = $orderTypeText[$item['orderType']] ?? '其他'; + + // 支付类型文本 + $payTypeText = [ + 1 => '微信支付', + 2 => '支付宝' + ]; + $item['payTypeText'] = !empty($item['payType']) ? ($payTypeText[$item['payType']] ?? '未知') : ''; + + // 格式化时间 + $item['createTime'] = $item['createTime'] ? date('Y-m-d H:i:s', $item['createTime']) : ''; + $item['payTime'] = $item['payTime'] ? date('Y-m-d H:i:s', $item['payTime']) : ''; + } + unset($item); + + return ResponseHelper::success([ + 'list' => $list, + 'total' => $total, + 'page' => $page, + 'limit' => $limit + ]); + + } catch (\Exception $e) { + return ResponseHelper::error('获取订单列表失败:' . $e->getMessage()); + } + } } \ No newline at end of file From e3afa24795fd008896e53d282e160eadd6e72efe Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Thu, 30 Oct 2025 11:41:22 +0800 Subject: [PATCH 10/14] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/common/controller/Api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/application/common/controller/Api.php b/Server/application/common/controller/Api.php index 4887c310..459e8ee3 100644 --- a/Server/application/common/controller/Api.php +++ b/Server/application/common/controller/Api.php @@ -141,7 +141,7 @@ class Api extends Controller } $data = Db::name('app_version') - ->field('version,downloadUrl,updateContent') + ->field('version,downloadUrl,updateContent,forceUpdate') ->where(['type'=>$type]) ->order('id DESC') ->find(); From 367a76a54f620923336aaf879448dddc16be73bf Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Mon, 3 Nov 2025 14:06:58 +0800 Subject: [PATCH 11/14] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=8F=90=E4=BA=A4?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/application/ai/controller/DouBaoAI.php | 9 +- .../common/controller/Attachment.php | 2 +- .../common/controller/GetOpenid.php | 52 +++++ .../common/controller/PaymentService.php | 189 ++++++++++++++---- Server/application/cunkebao/config/route.php | 6 +- .../controller/AiKnowledgeBaseController.php | 29 ++- .../controller/AiSettingsController.php | 93 ++++++++- .../cunkebao/controller/TokensController.php | 101 +++++++++- .../plan/PosterWeChatMiniProgram.php | 27 ++- 9 files changed, 444 insertions(+), 64 deletions(-) create mode 100644 Server/application/common/controller/GetOpenid.php diff --git a/Server/application/ai/controller/DouBaoAI.php b/Server/application/ai/controller/DouBaoAI.php index e7a50646..5cb71a3b 100644 --- a/Server/application/ai/controller/DouBaoAI.php +++ b/Server/application/ai/controller/DouBaoAI.php @@ -4,15 +4,18 @@ namespace app\ai\controller; use app\common\util\JwtUtil; use think\facade\Env; +use think\Controller; -class DouBaoAI +class DouBaoAI extends Controller { protected $apiUrl; protected $apiKey; protected $headers; - public function __init() + public function __construct() { + parent::__construct(); + $this->apiUrl = Env::get('doubaoAi.api_url'); $this->apiKey = Env::get('doubaoAi.api_key'); @@ -31,7 +34,7 @@ class DouBaoAI public function text($params = []) { - $this->__init(); + if (empty($params)){ return json_encode(['code' => 500, 'msg' => '提示词缺失']); } diff --git a/Server/application/common/controller/Attachment.php b/Server/application/common/controller/Attachment.php index 68659283..7a2973bf 100644 --- a/Server/application/common/controller/Attachment.php +++ b/Server/application/common/controller/Attachment.php @@ -28,7 +28,7 @@ class Attachment extends Controller $validate = \think\facade\Validate::rule([ 'file' => [ 'fileSize' => 10485760, // 10MB - 'fileExt' => 'jpg,jpeg,png,gif,doc,docx,pdf,zip,rar,mp4,mp3,csv,xlsx,xls,ppt,pptx', + 'fileExt' => 'jpg,jpeg,png,gif,doc,docx,pdf,zip,rar,mp4,mp3,csv,xlsx,xls,ppt,pptx,txt', ] ]); diff --git a/Server/application/common/controller/GetOpenid.php b/Server/application/common/controller/GetOpenid.php new file mode 100644 index 00000000..b33c81de --- /dev/null +++ b/Server/application/common/controller/GetOpenid.php @@ -0,0 +1,52 @@ + Env::get('weChat.appid'), + 'secret' => Env::get('weChat.secret'), + 'response_type' => 'array' + ]; + $this->app = Factory::officialAccount($config); + } + + + + public function index() + { + $app = $this->app; + $oauth = $app->oauth; + + // 未登录 + if (empty($_SESSION['wechat_user'])) { + + $_SESSION['target_url'] = 'user/profile'; + + $redirectUrl = $oauth->redirect(); + + exit_data($redirectUrl); + header("Location: {$redirectUrl}"); + exit; + } + + // 已经登录过 + $user = $_SESSION['wechat_user']; + + exit_data($user); + + return 'Hello, World!'; + } + +} \ No newline at end of file diff --git a/Server/application/common/controller/PaymentService.php b/Server/application/common/controller/PaymentService.php index 3d4f31eb..60e5385b 100644 --- a/Server/application/common/controller/PaymentService.php +++ b/Server/application/common/controller/PaymentService.php @@ -16,38 +16,74 @@ use app\common\model\Order; class PaymentService { /** - * 下单 + * 统一支付下单接口 + * 支持扫码付款、微信支付、支付宝支付 * * @param array $order - * - out_trade_no: string 商户订单号(必填) - * - total_fee: int 金额(分,必填) - * - body: string 商品描述(必填) - * - notify_url: string 异步通知地址(可覆盖配置) - * - attach: string 附加数据(可选) - * - time_expire: string 订单失效时间(可选) - * - client_ip: string 终端IP(可选) - * - sign_type: string MD5/RSA_1_256/RSA_1_1(可选,默认MD5) - * - pay_type: string 支付场景,如 JSAPI/APP/H5(可选) - * @return array - * @throws \Exception + * - orderNo: string 商户订单号(必填) + * - money: int 金额(分,必填) + * - goodsName: string 商品描述(必填) + * - service: string 支付服务类型(可选) + * - 'wechat' 或 'pay.weixin.jspay': 微信JSAPI支付 + * - 'alipay' 或 'pay.alipay.jspay': 支付宝JSAPI支付 + * - 不传或空: 默认扫码付款 + * - openid: string 微信用户openid(微信JSAPI支付必填) + * - buyer_id: string 支付宝用户ID(支付宝JSAPI支付可选) + * - notify_url: string 异步通知地址(可选) + * @return string JSON格式响应 */ public function createOrder(array $order) { + // 确定service类型:支持简写形式 wechat/alipay,或完整的 service 值 + $serviceType = $order['service'] ?? ''; + + // 映射简写形式到完整的 service 值 + if ($serviceType === 'wechat' || $serviceType === 'pay.weixin.jspay') { + $service = 'pay.weixin.jspay'; + } elseif ($serviceType === 'alipay' || $serviceType === 'pay.alipay.jspay') { + $service = 'pay.alipay.jspay'; + } elseif ($serviceType === 'qrCode' || $serviceType === 'unified.trade.native') { + $service = 'unified.trade.native'; + } else { + // 默认扫码支付 + $service = 'unified.trade.native'; + } + + // 构建基础参数 $params = [ - 'service' => 'unified.trade.native', + 'service' => $service, 'sign_type' => PaymentUtil::SIGN_TYPE_MD5, 'mch_id' => Env::get('payment.mchId'), 'out_trade_no' => $order['orderNo'], 'body' => $order['goodsName'] ?? '', 'total_fee' => $order['money'] ?? 0, 'mch_create_ip' => Request::ip(), - 'notify_url' => Env::get('payment.notify_url', '127.0.0.1'), + 'notify_url' => $order['notify_url'] ?? Env::get('payment.notify_url', '127.0.0.1'), 'nonce_str' => PaymentUtil::generateNonceStr(), ]; + $ddd = new GetOpenid(); + $ddd->index(); + exit_data(1111); + + // 微信JSAPI支付需要openid + if ($service == 'pay.weixin.jspay') { + // $params['sub_openid'] = 'oB44Yw1T6bfVAZwjj729P-6CUSPE'; + $params['is_raw'] = 0; + $params['mch_app_name'] = '存客宝'; + $params['mch_app_id'] = 'https://kr-op.quwanzhi.com'; + } + + // 支付宝JSAPI支付需要buyer_id(可选) + if ($service == 'pay.alipay.jspay') { + $params['is_raw'] = 0; + $params['quit_url'] = $params['notify_url']; + $params['buyer_id'] = ''; + } + Db::startTrans(); try { - // 过滤空值签名 + // 签名 $secret = Env::get('payment.key'); $params['sign_type'] = 'MD5'; $params['sign'] = PaymentUtil::generateSign($params, $secret, 'MD5'); @@ -57,7 +93,7 @@ class PaymentService throw new \Exception('支付网关地址未配置'); } - //创建订单 + // 创建订单 Order::create([ 'mchId' => $params['mch_id'], 'companyId' => isset($order['companyId']) ? $order['companyId'] : 0, @@ -73,17 +109,43 @@ class PaymentService 'nonceStr' => isset($order['nonceStr']) ? $order['nonceStr'] : '', 'createTime' => time(), ]); + // XML POST 请求 $xmlBody = $this->arrayToXml($params); $response = $this->postXml($url, $xmlBody); $parsed = $this->parseXmlOrRaw($response); + + exit_data($parsed); + if ($parsed['status'] == 0 && $parsed['result_code'] == 0) { Db::commit(); - return json_encode(['code' => 200, 'msg' => '订单创建成功', 'data' => $parsed['code_img_url']]); + + // 根据service类型返回不同的数据格式(仅返回接口文档中的字段) + $responseData = null; + if ($service == 'unified.trade.native') { + // 扫码支付返回二维码URL + $responseData = $parsed['code_img_url'] ?? ''; + } elseif ($service == 'pay.weixin.jspay') { + // 微信JSAPI支付返回支付参数(仅返回接口文档中存在的字段) + $responseData = []; + if (isset($parsed['appid'])) $responseData['appid'] = $parsed['appid']; + if (isset($parsed['time_stamp'])) $responseData['time_stamp'] = $parsed['time_stamp']; + if (isset($parsed['nonce_str'])) $responseData['nonce_str'] = $parsed['nonce_str']; + if (isset($parsed['package'])) $responseData['package'] = $parsed['package']; + if (isset($parsed['sign_type'])) $responseData['sign_type'] = $parsed['sign_type']; + if (isset($parsed['pay_sign'])) $responseData['pay_sign'] = $parsed['pay_sign']; + } elseif ($service == 'pay.alipay.jspay') { + // 支付宝JSAPI支付返回订单信息(仅返回接口文档中存在的字段) + $responseData = []; + if (isset($parsed['order_info'])) $responseData['order_info'] = $parsed['order_info']; + if (isset($parsed['order_string'])) $responseData['order_string'] = $parsed['order_string']; + } + + return json_encode(['code' => 200, 'msg' => '订单创建成功', 'data' => $responseData]); } else { Db::rollback(); - return json_encode(['code' => 500, 'msg' => '订单创建失败:' . $parsed['err_msg']]); + return json_encode(['code' => 500, 'msg' => '订单创建失败:' . ($parsed['err_msg'] ?? '未知错误')]); } } catch (\Exception $e) { @@ -194,54 +256,104 @@ class PaymentService /** * 支付结果异步通知 * - 威富通回调为 XML;需校验签名与业务字段并更新订单 - * - 回应:成功回"success",失败回"fail" - * @return void + * - 支持扫码付款、微信支付、支付宝支付的通知 + * - 回应:成功返回XML格式SUCCESS,失败返回XML格式FAIL + * @return string XML响应 */ public function notify() { $rawBody = file_get_contents('php://input'); $payload = $this->parseXmlOrRaw($rawBody); if (!is_array($payload) || empty($payload)) { - return json_encode(['code' => 500, 'msg' => 'XML解析错误']); + \think\facade\Log::error('支付通知:XML解析错误', ['rawBody' => $rawBody]); + return ''; } + // 验证签名 + $secret = Env::get('payment.key'); + if (!empty($secret) && isset($payload['sign'])) { + $signType = $payload['sign_type'] ?? 'MD5'; + if (!PaymentUtil::verifySign($payload, $secret, $signType)) { + \think\facade\Log::error('支付通知:签名验证失败', ['payload' => $payload]); + return ''; + } + } - if ($payload['status'] != 0 || $payload['result_code'] != 0) { - $errMsg = (isset($payload['err_msg']) ? $payload['err_msg'] : isset($payload['err_msg'])) ? $payload['err_msg'] : '未知错误'; - return json_encode(['code' => 500, 'msg' => $errMsg]); + // 检查通信状态 + if (isset($payload['status']) && $payload['status'] != 0) { + $errMsg = $payload['err_msg'] ?? '通信失败'; + \think\facade\Log::error('支付通知:通信失败', ['payload' => $payload]); + return ''; + } + + // 检查业务结果 + if (isset($payload['result_code']) && $payload['result_code'] != 0) { + $errMsg = $payload['err_msg'] ?? '业务处理失败'; + \think\facade\Log::error('支付通知:业务处理失败', ['payload' => $payload]); + return ''; } // 业务处理:更新订单 Db::startTrans(); try { - $outTradeNo = $payload['out_trade_no']; - $pay_result = $payload['pay_result']; - $time_end = $payload['time_end']; + $outTradeNo = $payload['out_trade_no'] ?? ''; + $pay_result = $payload['pay_result'] ?? 0; + $time_end = $payload['time_end'] ?? ''; + + if (empty($outTradeNo)) { + Db::rollback(); + \think\facade\Log::error('支付通知:订单号为空', ['payload' => $payload]); + return ''; + } + $order = Order::where('orderNo', $outTradeNo)->find(); if (!$order) { Db::rollback(); - return json_encode(['code' => 500, 'msg' => '该订单不存在']); + \think\facade\Log::error('支付通知:订单不存在', ['out_trade_no' => $outTradeNo]); + return ''; + } + + // 如果订单已支付,直接返回成功(防止重复处理) + if ($order->status == 1) { + Db::rollback(); + return ''; } if ($pay_result != 0) { - $order->payInfo = $payload['pay_info']; + $order->payInfo = $payload['pay_info'] ?? '支付失败'; $order->status = 3; $order->save(); Db::commit(); - return json_encode(['code' => 500, 'msg' => $payload['pay_info']]); + \think\facade\Log::error('支付通知:支付失败', ['orderNo' => $outTradeNo, 'pay_info' => $payload['pay_info'] ?? '']); + return ''; } - $order->payType = $payload['trade_type'] == 'pay.wechat.jspay' ? 1 : 2; + + // 根据trade_type判断支付方式 + $tradeType = $payload['trade_type'] ?? ''; + if (strpos($tradeType, 'wechat') !== false || strpos($tradeType, 'weixin') !== false) { + $order->payType = 1; // 微信支付 + } elseif (strpos($tradeType, 'alipay') !== false) { + $order->payType = 2; // 支付宝支付 + } else { + // 默认根据原有逻辑判断 + $order->payType = $tradeType == 'pay.wechat.jspay' ? 1 : 2; + } + $order->status = 1; $order->payTime = $this->parsePayTime($time_end); + $order->transactionId = $payload['transaction_id'] ?? ''; $order->save(); //订单处理 $this->processOrder($order); Db::commit(); - return json_encode(['code' => 200, 'msg' => '付款成功']); + + // 返回成功响应(XML格式) + return ''; } catch (\Exception $e) { Db::rollback(); - return json_encode(['code' => 500, 'msg' => '付款失败' . $e->getMessage()]); + \think\facade\Log::error('支付通知:处理异常', ['error' => $e->getMessage(), 'trace' => $e->getTraceAsString()]); + return ''; } } @@ -359,12 +471,12 @@ class PaymentService //订单处理 $this->processOrder($order); Db::commit(); - return json_encode(['code' => 200, 'msg' => '支付成功'] ); + return json_encode(['code' => 200, 'msg' => '支付成功']); } catch (\Exception $e) { Db::rollback(); return json_encode(['code' => 500, 'msg' => '付款失败' . $e->getMessage()]); } - }else{ + } else { $order = Order::where('orderNo', $resp['out_trade_no'])->lock(true)->find(); if ($order) { $order->status = 3; @@ -373,8 +485,8 @@ class PaymentService } return json_encode(['code' => 500, 'msg' => '支付失败', 'data' => $resp]); } - - }else{ + + } else { return json_encode(['code' => 500, 'msg' => '通信失败']); } } @@ -413,7 +525,7 @@ class PaymentService $record->form = 5; $record->wechatAccountId = 0; $record->friendIdOrGroupId = 0; - $record->remarks = '购买算力【'.$goodsSpecs['name'].'】'; + $record->remarks = '购买算力【' . $goodsSpecs['name'] . '】'; $record->tokens = $goodsSpecs['tokens']; $record->balanceTokens = $newTokens; $record->createTime = time(); @@ -423,4 +535,5 @@ class PaymentService return true; } + } \ No newline at end of file diff --git a/Server/application/cunkebao/config/route.php b/Server/application/cunkebao/config/route.php index 2b9d9665..cf64cef7 100644 --- a/Server/application/cunkebao/config/route.php +++ b/Server/application/cunkebao/config/route.php @@ -154,9 +154,10 @@ Route::group('v1/', function () { // 算力相关 Route::group('tokens', function () { Route::get('list', 'app\cunkebao\controller\TokensController@getList'); - Route::post('pay', 'app\cunkebao\controller\TokensController@pay'); - Route::get('queryOrder', 'app\cunkebao\controller\TokensController@queryOrder'); + Route::post('pay', 'app\cunkebao\controller\TokensController@pay'); // 扫码付款 + Route::get('queryOrder', 'app\cunkebao\controller\TokensController@queryOrder'); // 查询订单(扫码付款) Route::get('orderList', 'app\cunkebao\controller\TokensController@getOrderList'); // 获取订单列表 + Route::get('statistics', 'app\cunkebao\controller\TokensController@getTokensStatistics'); // 获取算力统计 }); @@ -165,6 +166,7 @@ Route::group('v1/', function () { Route::group('knowledge', function () { Route::get('init', 'app\cunkebao\controller\AiSettingsController@init'); Route::get('release', 'app\cunkebao\controller\AiSettingsController@release'); + Route::post('savePrompt', 'app\cunkebao\controller\AiSettingsController@savePrompt'); // 保存统一提示词 Route::get('typeList', 'app\cunkebao\controller\AiKnowledgeBaseController@typeList'); Route::get('getList', 'app\cunkebao\controller\AiKnowledgeBaseController@getList'); Route::post('add', 'app\cunkebao\controller\AiKnowledgeBaseController@add'); diff --git a/Server/application/cunkebao/controller/AiKnowledgeBaseController.php b/Server/application/cunkebao/controller/AiKnowledgeBaseController.php index 8ba90722..06a95c88 100644 --- a/Server/application/cunkebao/controller/AiKnowledgeBaseController.php +++ b/Server/application/cunkebao/controller/AiKnowledgeBaseController.php @@ -46,13 +46,35 @@ class AiKnowledgeBaseController extends BaseController $where[] = ['type', '=', AiKnowledgeBaseType::TYPE_USER]; } + // 统计开启的类型总数 + $enabledCountWhere = $where; + $enabledCountWhere[] = ['status', '=', 1]; + $enabledCount = AiKnowledgeBaseType::where($enabledCountWhere)->count(); + // 查询数据 $list = AiKnowledgeBaseType::where($where) ->order('type', 'asc') // 系统类型排在前面 ->order('createTime', 'desc') ->paginate($pageSize, false, ['page' => $page]); - return ResponseHelper::success($list, '获取成功'); + // 为每个类型添加素材数量统计 + $listData = $list->toArray(); + foreach ($listData['data'] as &$item) { + // 统计该类型下的知识库数量(素材数量) + $item['materialCount'] = AiKnowledgeBase::where([ + ['typeId', '=', $item['id']], + ['isDel', '=', 0] + ])->count(); + } + + // 重新构造返回数据 + $result = [ + 'total' => $listData['total'], + 'data' => $listData['data'], + 'enabledCount' => $enabledCount, // 开启的类型总数 + ]; + + return ResponseHelper::success($result, '获取成功'); } catch (\Exception $e) { return ResponseHelper::error('系统异常:' . $e->getMessage()); @@ -419,6 +441,11 @@ class AiKnowledgeBaseController extends BaseController ->order('createTime', 'desc') ->paginate($pageSize, false, ['page' => $page]); + foreach ($list as &$v){ + $v['size'] = 0; + } + unset($v); + return ResponseHelper::success($list, '获取成功'); } catch (\Exception $e) { diff --git a/Server/application/cunkebao/controller/AiSettingsController.php b/Server/application/cunkebao/controller/AiSettingsController.php index 8eb41423..429410be 100644 --- a/Server/application/cunkebao/controller/AiSettingsController.php +++ b/Server/application/cunkebao/controller/AiSettingsController.php @@ -40,6 +40,7 @@ class AiSettingsController extends BaseController // 确保智能体已创建 if (empty($settings->botId)) { + $settings->releaseTime = 0; $botCreated = $this->createBot($settings); if (!$botCreated) { return ResponseHelper::error('智能体创建失败'); @@ -48,12 +49,13 @@ class AiSettingsController extends BaseController // 确保知识库已创建 if (empty($settings->datasetId)) { + $settings->releaseTime = 0; $knowledgeCreated = $this->createKnowledge($settings); if (!$knowledgeCreated) { return ResponseHelper::error('知识库创建失败'); } } - if (!empty($settings->botId) && !empty($settings->datasetId)) { + if (!empty($settings->botId) && !empty($settings->datasetId) && $settings->releaseTime <= 0) { $cozeAI = new CozeAI(); $config = json_decode($settings->config,true); $config['bot_id'] = $settings->botId; @@ -133,7 +135,8 @@ class AiSettingsController extends BaseController ## 限制 - 仅依据知识库内容回答问题,对于知识库中没有的信息,如实告知用户无法回答。 - 回答必须严格遵循中国法律法规,不得出现任何违法违规内容。 -- 回答需简洁明了,避免冗长复杂的表述。'; +- 回答需简洁明了,避免冗长复杂的表述(尽量在100字内)。 +- 适当加些表情点缀。'; } /** @@ -341,4 +344,90 @@ class AiSettingsController extends BaseController $settings->save(); return ResponseHelper::success('', '发布成功'); } + + /** + * 保存统一提示词 + * 先更新数据库,再调用CozeAI接口更新智能体 + * + * @return \think\response\Json + */ + public function savePrompt() + { + try { + $companyId = $this->getUserInfo('companyId'); + if (empty($companyId)) { + return ResponseHelper::error('公司信息获取失败'); + } + + // 获取提示词参数 + $promptInfo = $this->request->param('promptInfo', ''); + if (empty($promptInfo)) { + return ResponseHelper::error('提示词内容不能为空'); + } + + // 查找AI设置 + $settings = AiSettingsModel::where(['companyId' => $companyId])->find(); + if (empty($settings)) { + return ResponseHelper::error('AI设置不存在,请先初始化'); + } + + // 检查智能体是否已创建 + if (empty($settings->botId)) { + return ResponseHelper::error('智能体未创建,请先初始化AI设置'); + } + + // 解析现有配置 + $config = json_decode($settings->config, true); + if (!is_array($config)) { + $config = []; + } + + // 更新提示词 + $config['prompt_info'] = $promptInfo; + + // 第一步:更新数据库 + $settings->config = json_encode($config, JSON_UNESCAPED_UNICODE); + $settings->isRelease = 0; // 标记为未发布状态 + $settings->updateTime = time(); + + if (!$settings->save()) { + return ResponseHelper::error('数据库更新失败'); + } + + // 第二步:调用CozeAI接口更新智能体 + try { + $cozeAI = new CozeAI(); + + // 参考 init 方法的参数格式,传递完整的 config + $updateData = $config; + $updateData['bot_id'] = $settings->botId; + + // 如果有知识库,也一并传入 + if (!empty($settings->datasetId)) { + $updateData['dataset_ids'] = [$settings->datasetId]; + } + + $result = $cozeAI->updateBot($updateData); + $result = json_decode($result, true); + + if ($result['code'] != 200) { + \think\facade\Log::error('更新智能体提示词失败:' . json_encode($result)); + return ResponseHelper::error('更新智能体失败:' . ($result['msg'] ?? '未知错误')); + } + + return ResponseHelper::success([ + 'prompt_info' => $promptInfo, + 'isRelease' => 0 + ], '提示词保存成功,请重新发布智能体'); + + } catch (\Exception $e) { + \think\facade\Log::error('调用CozeAI更新接口异常:' . $e->getMessage()); + return ResponseHelper::error('更新智能体接口调用失败:' . $e->getMessage()); + } + + } catch (\Exception $e) { + \think\facade\Log::error('保存提示词异常:' . $e->getMessage()); + return ResponseHelper::error('系统异常:' . $e->getMessage()); + } + } } \ No newline at end of file diff --git a/Server/application/cunkebao/controller/TokensController.php b/Server/application/cunkebao/controller/TokensController.php index b6ec7728..e59fab74 100644 --- a/Server/application/cunkebao/controller/TokensController.php +++ b/Server/application/cunkebao/controller/TokensController.php @@ -5,6 +5,8 @@ namespace app\cunkebao\controller; use app\common\controller\PaymentService; use app\common\model\Order; use app\cunkebao\model\TokensPackage; +use app\chukebao\model\TokensCompany; +use app\chukebao\model\TokensRecord; use library\ResponseHelper; use think\facade\Env; @@ -23,9 +25,9 @@ class TokensController extends BaseController $list = $query->where($where)->page($page, $limit)->order('sort ASC,id desc')->select(); foreach ($list as &$item) { $item['description'] = json_decode($item['description'], true); - $item['discount'] = round(((($item['originalPrice'] - $item['price']) / $item['originalPrice']) * 100),2); - $item['price'] = round( $item['price'], 2); - $item['unitPrice'] = round( $item['price'] / $item['tokens'],6); + $item['discount'] = round(((($item['originalPrice'] - $item['price']) / $item['originalPrice']) * 100), 2); + $item['price'] = round($item['price'], 2); + $item['unitPrice'] = round($item['price'] / $item['tokens'], 6); $item['originalPrice'] = round($item['originalPrice'] / 100, 2); $item['tokens'] = number_format($item['tokens']); } @@ -40,6 +42,12 @@ class TokensController extends BaseController $price = $this->request->param('price', ''); $userId = $this->getUserInfo('id'); $companyId = $this->getUserInfo('companyId'); + $payType = $this->request->param('payType', ''); + + if (!in_array($payType, ['wechat', 'alipay', 'qrCode'])) { + return ResponseHelper::error('付款类型不正确'); + } + if (empty($id) && empty($price)) { return ResponseHelper::error('套餐和自定义购买金额必须选一个'); @@ -73,6 +81,7 @@ class TokensController extends BaseController ]; } + $orderNo = date('YmdHis') . rand(100000, 999999); $order = [ 'companyId' => $companyId, @@ -82,7 +91,8 @@ class TokensController extends BaseController 'goodsName' => $specs['name'], 'goodsSpecs' => $specs, 'orderType' => 1, - 'money' => $specs['price'] + 'money' => $specs['price'], + 'service' => $payType ]; $paymentService = new PaymentService(); $res = $paymentService->createOrder($order); @@ -106,16 +116,18 @@ class TokensController extends BaseController $res = $paymentService->queryOrder($orderNo); $res = json_decode($res, true); if ($res['code'] == 200) { - return ResponseHelper::success('','订单已支付'); + return ResponseHelper::success('', '订单已支付'); } else { $errorMsg = !empty($order['payInfo']) ? $order['payInfo'] : '订单未支付'; return ResponseHelper::error($errorMsg); } } else { - return ResponseHelper::success('','订单已支付'); + return ResponseHelper::success('', '订单已支付'); } } + + /** * 获取订单列表 * @return \think\response\Json @@ -164,7 +176,7 @@ class TokensController extends BaseController // 分页查询 $query = Order::where($where) - ->where(function($query) { + ->where(function ($query) { $query->whereNull('deleteTime')->whereOr('deleteTime', 0); }); $total = $query->count(); @@ -178,12 +190,12 @@ class TokensController extends BaseController foreach ($list as &$item) { // 金额转换(分转元) $item['money'] = round($item['money'] / 100, 2); - + // 解析商品规格 if (!empty($item['goodsSpecs'])) { $specs = is_string($item['goodsSpecs']) ? json_decode($item['goodsSpecs'], true) : $item['goodsSpecs']; $item['goodsSpecs'] = $specs; - + // 添加算力数量 if (isset($specs['tokens'])) { $item['tokens'] = number_format($specs['tokens']); @@ -229,4 +241,75 @@ class TokensController extends BaseController return ResponseHelper::error('获取订单列表失败:' . $e->getMessage()); } } + + /** + * 获取公司算力统计信息 + * 包括:总算力、今日使用、本月使用、剩余算力 + * + * @return \think\response\Json + */ + public function getTokensStatistics() + { + try { + $companyId = $this->getUserInfo('companyId'); + if (empty($companyId)) { + return ResponseHelper::error('公司信息获取失败'); + } + + // 获取公司算力余额 + $tokensCompany = TokensCompany::where('companyId', $companyId)->find(); + $remainingTokens = $tokensCompany ? intval($tokensCompany->tokens) : 0; + + // 获取今日开始和结束时间戳 + $todayStart = strtotime(date('Y-m-d 00:00:00')); + $todayEnd = strtotime(date('Y-m-d 23:59:59')); + + // 获取本月开始和结束时间戳 + $monthStart = strtotime(date('Y-m-01 00:00:00')); + $monthEnd = strtotime(date('Y-m-t 23:59:59')); + + // 统计今日消费(type=0表示消费) + $todayUsed = TokensRecord::where([ + ['companyId', '=', $companyId], + ['type', '=', 0], // 0为减少(消费) + ['createTime', '>=', $todayStart], + ['createTime', '<=', $todayEnd] + ])->sum('tokens'); + $todayUsed = intval($todayUsed); + + // 统计本月消费 + $monthUsed = TokensRecord::where([ + ['companyId', '=', $companyId], + ['type', '=', 0], // 0为减少(消费) + ['createTime', '>=', $monthStart], + ['createTime', '<=', $monthEnd] + ])->sum('tokens'); + $monthUsed = intval($monthUsed); + + // 计算总算力(当前剩余 + 历史总消费) + $totalConsumed = TokensRecord::where([ + ['companyId', '=', $companyId], + ['type', '=', 0] + ])->sum('tokens'); + $totalConsumed = intval($totalConsumed); + + // 总充值算力 + $totalRecharged = TokensRecord::where([ + ['companyId', '=', $companyId], + ['type', '=', 1] // 1为增加(充值) + ])->sum('tokens'); + $totalRecharged = intval($totalRecharged); + + return ResponseHelper::success([ + 'totalTokens' => $totalRecharged, // 总算力(累计充值) + 'todayUsed' => $todayUsed, // 今日使用 + 'monthUsed' => $monthUsed, // 本月使用 + 'remainingTokens' => $remainingTokens, // 剩余算力 + 'totalConsumed' => $totalConsumed, // 累计消费 + ], '获取成功'); + + } catch (\Exception $e) { + return ResponseHelper::error('获取算力统计失败:' . $e->getMessage()); + } + } } \ No newline at end of file diff --git a/Server/application/cunkebao/controller/plan/PosterWeChatMiniProgram.php b/Server/application/cunkebao/controller/plan/PosterWeChatMiniProgram.php index f51e5d76..aae46d9c 100644 --- a/Server/application/cunkebao/controller/plan/PosterWeChatMiniProgram.php +++ b/Server/application/cunkebao/controller/plan/PosterWeChatMiniProgram.php @@ -12,17 +12,28 @@ use think\Db; class PosterWeChatMiniProgram extends Controller { + + protected $config; + + + public function __construct() + { + parent::__construct(); + + // 从环境变量获取配置 + $this->config = [ + 'app_id' => Env::get('weChat.appidMiniApp','wx789850448e26c91d'), + 'secret' => Env::get('weChat.secretMiniApp','d18f75b3a3623cb40da05648b08365a1'), + 'response_type' => 'array' + ]; + } + + public function index() { return 'Hello, World!'; } - const MINI_PROGRAM_CONFIG = [ - 'app_id' => 'wx789850448e26c91d', - 'secret' => 'd18f75b3a3623cb40da05648b08365a1', - 'response_type' => 'array' - ]; - // 生成小程序码,存客宝-操盘手调用 public function generateMiniProgramCodeWithScene($taskId = '') @@ -34,7 +45,7 @@ class PosterWeChatMiniProgram extends Controller try { - $app = Factory::miniProgram(self::MINI_PROGRAM_CONFIG); + $app = Factory::miniProgram($this->config); // scene参数长度限制为32位 //$scene = 'taskId=' . $taskId; $scene = sprintf("%s", $taskId); @@ -83,7 +94,7 @@ class PosterWeChatMiniProgram extends Controller ]); } - $app = Factory::miniProgram(self::MINI_PROGRAM_CONFIG); + $app = Factory::miniProgram($this->config); $result = $app->phone_number->getUserPhoneNumber($code); From 469ab4a085a8b7fc5d4c8fb325d31bc7aa456503 Mon Sep 17 00:00:00 2001 From: wong <106998207@qq.com> Date: Mon, 3 Nov 2025 14:07:15 +0800 Subject: [PATCH 12/14] =?UTF-8?q?=E9=97=A8=E5=BA=97=E7=AB=AF=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Store_vue/App.vue | 97 ++++- Store_vue/api/config/index.js | 6 + Store_vue/api/modules/app.js | 19 + Store_vue/components/SideMenu.vue | 124 +++++- Store_vue/components/UpdateDialog.vue | 556 +++++++++++++++++++++++++ Store_vue/components/UpdateModal.vue | 131 ++++++ Store_vue/manifest.json | 18 +- Store_vue/pages.json | 6 +- Store_vue/pages/login/index.vue | 558 +++++++++++++++++--------- Store_vue/static/logo.png | Bin 4023 -> 538209 bytes 10 files changed, 1326 insertions(+), 189 deletions(-) create mode 100644 Store_vue/api/modules/app.js create mode 100644 Store_vue/components/UpdateDialog.vue create mode 100644 Store_vue/components/UpdateModal.vue diff --git a/Store_vue/App.vue b/Store_vue/App.vue index b97e7bc2..0a0f5448 100644 --- a/Store_vue/App.vue +++ b/Store_vue/App.vue @@ -1,14 +1,37 @@ + + + diff --git a/Store_vue/components/UpdateModal.vue b/Store_vue/components/UpdateModal.vue new file mode 100644 index 00000000..8de78f38 --- /dev/null +++ b/Store_vue/components/UpdateModal.vue @@ -0,0 +1,131 @@ + + + + + + diff --git a/Store_vue/manifest.json b/Store_vue/manifest.json index 4c22f42d..7beae997 100644 --- a/Store_vue/manifest.json +++ b/Store_vue/manifest.json @@ -2,8 +2,8 @@ "name" : "AI数智员工", "appid" : "__UNI__9421F6C", "description" : "", - "versionName" : "1.0.1", - "versionCode" : "100", + "versionName" : "1.1.0", + "versionCode" : 100, "transformPx" : false, /* 5+App特有相关 */ "app-plus" : { @@ -107,5 +107,17 @@ }, "vueVersion" : "2", "locale" : "zh-Hans", - "fallbackLocale" : "zh-Hans" + "fallbackLocale" : "zh-Hans", + /* H5特有相关 */ + "h5" : { + "router" : { + "mode" : "hash", + "base" : "./" + }, + "title" : "AI数智员工", + "devServer" : { + "port" : 8080, + "disableHostCheck" : true + } + } } diff --git a/Store_vue/pages.json b/Store_vue/pages.json index 427192f0..6a6c25ed 100644 --- a/Store_vue/pages.json +++ b/Store_vue/pages.json @@ -4,15 +4,15 @@ }, "pages": [ { - "path": "pages/chat/index", + "path": "pages/login/index", "style": { - "navigationBarTitleText": "AI数智员工", "navigationStyle": "custom" } }, { - "path": "pages/login/index", + "path": "pages/chat/index", "style": { + "navigationBarTitleText": "AI数智员工", "navigationStyle": "custom" } } diff --git a/Store_vue/pages/login/index.vue b/Store_vue/pages/login/index.vue index 29d345d8..9b8a7da5 100644 --- a/Store_vue/pages/login/index.vue +++ b/Store_vue/pages/login/index.vue @@ -1,80 +1,214 @@