request->user ?? null; if (!$user || $user['role'] !== 'superadmin') { return error('无权限访问', 403); } // 列表只返回“显示”的配置(visible=1 或未设);隐藏的由数据库 visible=0 控制,不在此列表展示 $providers = AiProviderModel::order('id', 'asc') ->whereRaw('(visible IS NULL OR visible = 1)') ->select() ->toArray(); // 处理返回数据 $result = []; foreach ($providers as $provider) { $result[] = [ 'id' => $provider['providerId'], 'name' => $provider['name'], 'enabled' => $provider['enabled'] == 1, 'visible' => isset($provider['visible']) ? ($provider['visible'] == 1) : true, 'apiKey' => $provider['apiKey'] ?? '', // 脱敏后的密钥 'apiEndpoint' => $provider['apiEndpoint'] ?? '', 'model' => $provider['model'] ?? '', 'organizationId' => $provider['organizationId'] ?? '', 'maxTokens' => $provider['maxTokens'] ?? 4096, 'balanceAlertEnabled' => $provider['balanceAlertEnabled'] == 1, 'balanceAlertThreshold' => floatval($provider['balanceAlertThreshold'] ?? 10), 'notes' => $provider['notes'] ?? '', 'docUrl' => $provider['docUrl'] ?? '', 'isFree' => $provider['isFree'] == 1, 'supportsBalance' => $provider['supportsBalance'] == 1, '_hasKey' => !empty($provider['apiKey']), 'lastBalance' => $provider['lastBalance'] ? floatval($provider['lastBalance']) : null, 'lastBalanceCurrency' => $provider['lastBalanceCurrency'] ?? null, 'lastBalanceCheckedAt' => $provider['lastBalanceCheckedAt'] ? date('Y-m-d H:i:s', $provider['lastBalanceCheckedAt']) : null, 'extraConfig' => is_array($provider['extraConfig'] ?? null) ? $provider['extraConfig'] : (isset($provider['extraConfig']) && is_string($provider['extraConfig']) ? (json_decode($provider['extraConfig'], true) ?: []) : []) ]; } return success($result); } /** * 更新AI服务商配置 * @return \think\response\Json */ public function update() { // 验证是否为超级管理员 $user = $this->request->user ?? null; if (!$user || $user['role'] !== 'superadmin') { return error('无权限访问', 403); } $providerId = Request::param('providerId', ''); $data = Request::only([ 'name', 'enabled', 'visible', 'apiKey', 'apiEndpoint', 'model', 'organizationId', 'maxTokens', 'balanceAlertEnabled', 'balanceAlertThreshold', 'notes', 'extraConfig' ]); if (empty($providerId)) { return error('服务商ID不能为空', 400); } // 查找服务商配置 $provider = AiProviderModel::where('providerId', $providerId)->find(); if (!$provider) { return error('服务商配置不存在', 404); } // 处理 enabled 字段(前端传的是布尔值) if (isset($data['enabled'])) { $data['enabled'] = $data['enabled'] ? 1 : 0; } // 处理 balanceAlertEnabled 字段 if (isset($data['balanceAlertEnabled'])) { $data['balanceAlertEnabled'] = $data['balanceAlertEnabled'] ? 1 : 0; } // 处理 visible 字段(显示/隐藏,数据库直接控制) if (isset($data['visible'])) { $data['visible'] = $data['visible'] ? 1 : 0; } // extraConfig 可为数组或 JSON 字符串,模型 type=json 会处理 if (isset($data['extraConfig']) && is_string($data['extraConfig'])) { $decoded = json_decode($data['extraConfig'], true); $data['extraConfig'] = is_array($decoded) ? $decoded : []; } // 如果API Key为空或包含脱敏标记(****),不更新(保持原值) if (isset($data['apiKey'])) { if (empty($data['apiKey']) || strpos($data['apiKey'], '****') !== false) { unset($data['apiKey']); } } // 更新配置 $provider->save($data); // 返回更新后的数据(脱敏) $result = [ 'id' => $provider->providerId, 'name' => $provider->name, 'enabled' => $provider->enabled == 1, 'visible' => isset($provider->visible) ? ($provider->visible == 1) : true, 'apiKey' => $provider->apiKey ?? '', 'apiEndpoint' => $provider->apiEndpoint ?? '', 'model' => $provider->model ?? '', 'organizationId' => $provider->organizationId ?? '', 'maxTokens' => $provider->maxTokens ?? 4096, 'balanceAlertEnabled' => $provider->balanceAlertEnabled == 1, 'balanceAlertThreshold' => floatval($provider->balanceAlertThreshold ?? 10), 'notes' => $provider->notes ?? '', 'isFree' => $provider->isFree == 1, 'supportsBalance' => $provider->supportsBalance == 1, '_hasKey' => !empty($provider->apiKey), 'extraConfig' => $provider->extraConfig ?? [] ]; return success($result, '保存成功'); } /** * 批量更新AI服务商配置 * @return \think\response\Json */ public function batchUpdate() { // 验证是否为超级管理员 $user = $this->request->user ?? null; if (!$user || $user['role'] !== 'superadmin') { return error('无权限访问', 403); } $providers = Request::param('providers', []); if (empty($providers) || !is_array($providers)) { return error('配置数据不能为空', 400); } $successCount = 0; $errors = []; Db::startTrans(); try { foreach ($providers as $providerData) { $providerId = $providerData['id'] ?? $providerData['providerId'] ?? ''; if (empty($providerId)) { $errors[] = '服务商ID不能为空'; continue; } $provider = AiProviderModel::where('providerId', $providerId)->find(); if (!$provider) { $errors[] = "服务商 {$providerId} 不存在"; continue; } // 准备更新数据 $updateData = []; if (isset($providerData['enabled'])) { $updateData['enabled'] = $providerData['enabled'] ? 1 : 0; } if (isset($providerData['apiKey']) && !empty($providerData['apiKey'])) { $updateData['apiKey'] = $providerData['apiKey']; } if (isset($providerData['apiEndpoint'])) { $updateData['apiEndpoint'] = $providerData['apiEndpoint']; } if (isset($providerData['model'])) { $updateData['model'] = $providerData['model']; } if (isset($providerData['organizationId'])) { $updateData['organizationId'] = $providerData['organizationId']; } if (isset($providerData['maxTokens'])) { $updateData['maxTokens'] = intval($providerData['maxTokens']); } if (isset($providerData['balanceAlertEnabled'])) { $updateData['balanceAlertEnabled'] = $providerData['balanceAlertEnabled'] ? 1 : 0; } if (isset($providerData['balanceAlertThreshold'])) { $updateData['balanceAlertThreshold'] = floatval($providerData['balanceAlertThreshold']); } if (isset($providerData['notes'])) { $updateData['notes'] = $providerData['notes']; } if (isset($providerData['visible'])) { $updateData['visible'] = $providerData['visible'] ? 1 : 0; } if (isset($providerData['extraConfig'])) { $updateData['extraConfig'] = is_array($providerData['extraConfig']) ? $providerData['extraConfig'] : (is_string($providerData['extraConfig']) ? json_decode($providerData['extraConfig'], true) : []); if (!is_array($updateData['extraConfig'])) { $updateData['extraConfig'] = []; } } $provider->save($updateData); $successCount++; } Db::commit(); } catch (\Exception $e) { Db::rollback(); return error('批量保存失败:' . $e->getMessage(), 500); } if (!empty($errors)) { return error('部分配置保存失败:' . implode(';', $errors), 400); } return success(null, "成功保存 {$successCount} 个配置"); } /** * 查询余额(单个服务商) * @return \think\response\Json */ public function queryBalance() { // 验证是否为超级管理员 $user = $this->request->user ?? null; if (!$user || $user['role'] !== 'superadmin') { return error('无权限访问', 403); } $providerId = Request::param('providerId', ''); if (empty($providerId)) { return error('服务商ID不能为空', 400); } $provider = AiProviderModel::where('providerId', $providerId)->find(); if (!$provider) { return error('服务商配置不存在', 404); } if (empty($provider->apiKey)) { return error('请先配置 API Key', 400); } if (!$provider->supportsBalance) { return error('该服务商暂不支持余额查询', 400); } // 调用余额查询服务 $balanceResult = $this->queryProviderBalance($provider); // 更新最后查询的余额 if ($balanceResult['status'] === 'success' && isset($balanceResult['balance'])) { $provider->lastBalance = $balanceResult['balance']; $provider->lastBalanceCurrency = $balanceResult['currency'] ?? 'CNY'; $provider->lastBalanceCheckedAt = time(); $provider->save(); } return success($balanceResult); } /** * 批量查询余额 * @return \think\response\Json */ public function queryAllBalances() { // 验证是否为超级管理员 $user = $this->request->user ?? null; if (!$user || $user['role'] !== 'superadmin') { return error('无权限访问', 403); } $providerIds = Request::param('providerIds', []); // 如果没有指定,查询所有已启用且已配置密钥的服务商 if (empty($providerIds)) { $providers = AiProviderModel::where('enabled', 1) ->where('apiKey', '<>', '') ->where('apiKey', '<>', null) ->select(); } else { $providers = AiProviderModel::where('providerId', 'in', $providerIds) ->where('apiKey', '<>', '') ->where('apiKey', '<>', null) ->select(); } $results = []; foreach ($providers as $provider) { if (!$provider->supportsBalance) { continue; } $balanceResult = $this->queryProviderBalance($provider); // 更新最后查询的余额 if ($balanceResult['status'] === 'success' && isset($balanceResult['balance'])) { $provider->lastBalance = $balanceResult['balance']; $provider->lastBalanceCurrency = $balanceResult['currency'] ?? 'CNY'; $provider->lastBalanceCheckedAt = time(); $provider->save(); } $results[] = $balanceResult; } return success($results); } /** * 查询服务商余额(内部方法) * @param AiProviderModel $provider * @return array */ private function queryProviderBalance($provider) { // 这里需要实现各服务商的余额查询逻辑 // 由于各服务商的API不同,这里提供一个基础框架 $providerId = $provider->providerId; $apiKey = $provider->getRawApiKey(); // 获取原始密钥用于API调用 // TODO: 实现各服务商的余额查询API调用 // 目前返回模拟数据,实际需要调用各服务商的API try { switch ($providerId) { case 'openai': // OpenAI余额查询逻辑 return $this->queryOpenAIBalance($apiKey); case 'deepseek': // DeepSeek余额查询逻辑 return $this->queryDeepSeekBalance($apiKey); case 'moonshot': // Moonshot余额查询逻辑 return $this->queryMoonshotBalance($apiKey); default: return [ 'providerId' => $providerId, 'providerName' => $provider->name, 'status' => 'unsupported', 'message' => '该服务商暂不支持余额查询', 'balance' => null, 'currency' => null, 'checkedAt' => date('Y-m-d H:i:s') ]; } } catch (\Exception $e) { return [ 'providerId' => $providerId, 'providerName' => $provider->name, 'status' => 'error', 'message' => '查询失败:' . $e->getMessage(), 'balance' => null, 'currency' => null, 'checkedAt' => date('Y-m-d H:i:s') ]; } } /** * 查询OpenAI余额 * @param string $apiKey * @return array */ private function queryOpenAIBalance($apiKey) { // TODO: 实现OpenAI余额查询 // OpenAI没有直接的余额查询API,需要通过使用情况估算 return [ 'providerId' => 'openai', 'providerName' => 'OpenAI (GPT)', 'status' => 'success', 'message' => '余额查询成功:$100.00', 'balance' => 100.00, 'currency' => 'USD', 'checkedAt' => date('Y-m-d H:i:s') ]; } /** * 查询DeepSeek余额 * @param string $apiKey * @return array */ private function queryDeepSeekBalance($apiKey) { // TODO: 实现DeepSeek余额查询 try { // 示例:调用DeepSeek API查询余额 // $response = file_get_contents('https://api.deepseek.com/v1/balance', [ // 'http' => [ // 'method' => 'GET', // 'header' => "Authorization: Bearer {$apiKey}\r\n" // ] // ]); return [ 'providerId' => 'deepseek', 'providerName' => 'DeepSeek', 'status' => 'success', 'message' => '余额查询成功:¥500.00', 'balance' => 500.00, 'currency' => 'CNY', 'checkedAt' => date('Y-m-d H:i:s') ]; } catch (\Exception $e) { return [ 'providerId' => 'deepseek', 'providerName' => 'DeepSeek', 'status' => 'error', 'message' => '查询失败:' . $e->getMessage(), 'balance' => null, 'currency' => null, 'checkedAt' => date('Y-m-d H:i:s') ]; } } /** * 查询Moonshot余额 * @param string $apiKey * @return array */ private function queryMoonshotBalance($apiKey) { // TODO: 实现Moonshot余额查询 try { // 示例:调用Moonshot API查询余额 return [ 'providerId' => 'moonshot', 'providerName' => 'Moonshot (Kimi)', 'status' => 'success', 'message' => '余额查询成功:¥200.00', 'balance' => 200.00, 'currency' => 'CNY', 'checkedAt' => date('Y-m-d H:i:s') ]; } catch (\Exception $e) { return [ 'providerId' => 'moonshot', 'providerName' => 'Moonshot (Kimi)', 'status' => 'error', 'message' => '查询失败:' . $e->getMessage(), 'balance' => null, 'currency' => null, 'checkedAt' => date('Y-m-d H:i:s') ]; } } }