diff --git a/backend-single/AI_CONFIG_API_README.md b/backend-single/AI_CONFIG_API_README.md new file mode 100644 index 0000000..f93164d --- /dev/null +++ b/backend-single/AI_CONFIG_API_README.md @@ -0,0 +1,125 @@ +# AI配置管理模块 + +## 概述 +基于t_ai_config表结构生成的完整AI配置管理模块,提供AI接口配置的增删改查功能。 + +## 生成的文件 + +### 1. 实体类 +- `com.emotion.entity.AiConfig` - AI配置实体类,继承BaseEntity + +### 2. 数据访问层 +- `com.emotion.mapper.AiConfigMapper` - MyBatis-Plus Mapper接口 + +### 3. 业务逻辑层 +- `com.emotion.service.AiConfigService` - 服务接口 +- `com.emotion.service.impl.AiConfigServiceImpl` - 服务实现类 + +### 4. 控制器层 +- `com.emotion.controller.AiConfigController` - REST API控制器 + +### 5. 请求对象 +- `com.emotion.dto.request.aiconfig.AiConfigCreateRequest` - 创建请求 +- `com.emotion.dto.request.aiconfig.AiConfigUpdateRequest` - 更新请求 +- `com.emotion.dto.request.aiconfig.AiConfigPageRequest` - 分页查询请求 +- `com.emotion.dto.request.aiconfig.AiConfigEnableRequest` - 启用/禁用请求 +- `com.emotion.dto.request.aiconfig.AiConfigDefaultRequest` - 默认配置设置请求 + +### 6. 响应对象 +- `com.emotion.dto.response.aiconfig.AiConfigResponse` - 响应对象 + +## API接口列表 + +### 基础CRUD操作 +- `GET /aiConfig/page` - 分页查询AI配置 +- `GET /aiConfig/detail?id={id}` - 根据ID获取AI配置详情 +- `POST /aiConfig/create` - 创建AI配置 +- `PUT /aiConfig/update` - 更新AI配置 +- `DELETE /aiConfig/delete?id={id}` - 删除AI配置 + +### 条件查询接口 +- `GET /aiConfig/byConfigType?configType={type}` - 根据配置类型查询 +- `GET /aiConfig/byProvider?provider={provider}` - 根据服务提供商查询 +- `GET /aiConfig/byUsageScenario?usageScenario={scenario}` - 根据使用场景查询 +- `GET /aiConfig/byEnvironment?environment={env}` - 根据环境查询 +- `GET /aiConfig/byConfigKey?configKey={key}` - 根据配置键值查询 + +### 状态管理接口 +- `GET /aiConfig/enabled` - 查询已启用的配置 +- `GET /aiConfig/disabled` - 查询已禁用的配置 +- `GET /aiConfig/default` - 查询默认配置 +- `PUT /aiConfig/enable?id={id}` - 启用配置 +- `PUT /aiConfig/disable?id={id}` - 禁用配置 +- `PUT /aiConfig/setDefault?id={id}` - 设置为默认配置 +- `PUT /aiConfig/unsetDefault?id={id}` - 取消默认配置 + +### 智能查询接口 +- `GET /aiConfig/bestConfig?usageScenario={scenario}&environment={env}` - 查询最优配置 + +### 统计接口 +- `GET /aiConfig/countEnabled` - 统计已启用配置数量 +- `GET /aiConfig/countDisabled` - 统计已禁用配置数量 +- `GET /aiConfig/countDefault` - 统计默认配置数量 +- `GET /aiConfig/countByConfigType?configType={type}` - 根据配置类型统计数量 +- `GET /aiConfig/countByProvider?provider={provider}` - 根据服务提供商统计数量 + +## 特性 + +### 1. 遵循项目规范 +- 继承BaseEntity,包含公共字段 +- 使用雪花算法生成ID +- 使用LambdaQueryWrapper构造查询条件 +- 统一的异常处理和返回格式 + +### 2. 安全特性 +- API Token脱敏显示 +- 参数校验 +- 逻辑删除 + +### 3. 查询优化 +- 支持关键词搜索 +- 支持多条件过滤 +- 支持排序 +- 支持分页 + +### 4. 业务功能 +- 配置启用/禁用管理 +- 默认配置设置 +- 智能配置选择(根据场景和环境) +- 优先级排序 + +## 使用示例 + +### 创建AI配置 +```json +POST /aiConfig/create +{ + "configName": "Coze聊天配置", + "configKey": "coze_chat_default", + "configType": "coze", + "provider": "coze", + "apiBaseUrl": "https://api.coze.cn", + "apiToken": "your_api_token", + "usageScenario": "chat", + "isEnabled": 1, + "environment": "production" +} +``` + +### 分页查询 +``` +GET /aiConfig/page?current=1&size=10&keyword=coze&configType=coze&isEnabled=1 +``` + +### 查询最优配置 +``` +GET /aiConfig/bestConfig?usageScenario=chat&environment=production +``` + +## 注意事项 + +1. 所有接口都遵循统一的返回格式Result +2. API Token在响应中会进行脱敏处理 +3. 删除操作使用逻辑删除 +4. 配置键值(configKey)必须唯一 +5. 最优配置查询会优先返回默认配置,然后按优先级排序 \ No newline at end of file diff --git a/backend-single/src/main/java/com/emotion/controller/AiConfigController.java b/backend-single/src/main/java/com/emotion/controller/AiConfigController.java new file mode 100644 index 0000000..e3feeb2 --- /dev/null +++ b/backend-single/src/main/java/com/emotion/controller/AiConfigController.java @@ -0,0 +1,290 @@ +package com.emotion.controller; + +import com.emotion.common.PageResult; +import com.emotion.common.Result; +import com.emotion.dto.request.aiconfig.*; +import com.emotion.dto.response.aiconfig.AiConfigResponse; +import com.emotion.service.AiConfigService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * AI配置控制器 + * + * @author system + * @date 2025-10-30 + */ +@RestController +@RequestMapping("/aiConfig") +@Tag(name = "AI配置管理", description = "AI配置的增删改查功能") +public class AiConfigController { + + @Autowired + private AiConfigService aiConfigService; + + /** + * 分页查询AI配置 + */ + @Operation(summary = "分页查询AI配置", description = "分页查询AI配置列表") + @GetMapping("/page") + public Result> getPage(@Validated AiConfigPageRequest request) { + PageResult pageResult = aiConfigService.getPageWithResponse(request); + return Result.success(pageResult); + } + + /** + * 根据ID获取AI配置 + */ + @Operation(summary = "根据ID获取AI配置", description = "根据ID获取AI配置详情") + @GetMapping("/detail") + public Result getById(@RequestParam String id) { + AiConfigResponse response = aiConfigService.getAiConfigResponseById(id); + if (response == null) { + return Result.notFound("AI配置不存在"); + } + return Result.success(response); + } + + /** + * 创建AI配置 + */ + @Operation(summary = "创建AI配置", description = "创建新的AI配置") + @PostMapping("/create") + public Result create(@RequestBody @Validated AiConfigCreateRequest request) { + AiConfigResponse response = aiConfigService.createAiConfigWithResponse(request); + if (response == null) { + return Result.error("创建失败"); + } + return Result.success("创建成功", response); + } + + /** + * 更新AI配置 + */ + @Operation(summary = "更新AI配置", description = "更新指定AI配置") + @PutMapping("/update") + public Result update(@RequestBody @Validated AiConfigUpdateRequest request) { + AiConfigResponse response = aiConfigService.updateAiConfigWithResponse(request); + if (response == null) { + return Result.error("更新失败"); + } + return Result.success("更新成功", response); + } + + /** + * 删除AI配置 + */ + @Operation(summary = "删除AI配置", description = "删除指定AI配置") + @DeleteMapping("/delete") + public Result delete(@RequestParam String id) { + boolean deleted = aiConfigService.removeById(id); + if (!deleted) { + return Result.error("删除失败"); + } + return Result.success(); + } + + /** + * 根据配置类型查询AI配置 + */ + @Operation(summary = "根据配置类型查询AI配置", description = "根据配置类型查询AI配置列表") + @GetMapping("/byConfigType") + public Result> getByConfigType(@RequestParam String configType) { + List responses = aiConfigService.getByConfigTypeWithResponse(configType); + return Result.success(responses); + } + + /** + * 根据服务提供商查询AI配置 + */ + @Operation(summary = "根据服务提供商查询AI配置", description = "根据服务提供商查询AI配置列表") + @GetMapping("/byProvider") + public Result> getByProvider(@RequestParam String provider) { + List responses = aiConfigService.getByProviderWithResponse(provider); + return Result.success(responses); + } + + /** + * 根据使用场景查询AI配置 + */ + @Operation(summary = "根据使用场景查询AI配置", description = "根据使用场景查询AI配置列表") + @GetMapping("/byUsageScenario") + public Result> getByUsageScenario(@RequestParam String usageScenario) { + List responses = aiConfigService.getByUsageScenarioWithResponse(usageScenario); + return Result.success(responses); + } + + /** + * 根据环境查询AI配置 + */ + @Operation(summary = "根据环境查询AI配置", description = "根据环境查询AI配置列表") + @GetMapping("/byEnvironment") + public Result> getByEnvironment(@RequestParam String environment) { + List responses = aiConfigService.getByEnvironmentWithResponse(environment); + return Result.success(responses); + } + + /** + * 查询已启用的AI配置 + */ + @Operation(summary = "查询已启用的AI配置", description = "查询已启用的AI配置列表") + @GetMapping("/enabled") + public Result> getEnabledConfigs() { + List responses = aiConfigService.getEnabledConfigsWithResponse(); + return Result.success(responses); + } + + /** + * 查询已禁用的AI配置 + */ + @Operation(summary = "查询已禁用的AI配置", description = "查询已禁用的AI配置列表") + @GetMapping("/disabled") + public Result> getDisabledConfigs() { + List responses = aiConfigService.getDisabledConfigsWithResponse(); + return Result.success(responses); + } + + /** + * 查询默认配置 + */ + @Operation(summary = "查询默认配置", description = "查询默认配置列表") + @GetMapping("/default") + public Result> getDefaultConfigs() { + List responses = aiConfigService.getDefaultConfigsWithResponse(); + return Result.success(responses); + } + + /** + * 根据配置键值查询AI配置 + */ + @Operation(summary = "根据配置键值查询AI配置", description = "根据配置键值查询AI配置") + @GetMapping("/byConfigKey") + public Result getByConfigKey(@RequestParam String configKey) { + AiConfigResponse response = aiConfigService.getByConfigKeyWithResponse(configKey); + if (response == null) { + return Result.notFound("AI配置不存在"); + } + return Result.success(response); + } + + /** + * 启用AI配置 + */ + @Operation(summary = "启用AI配置", description = "启用指定AI配置") + @PutMapping("/enable") + public Result enableConfig(@RequestParam String id) { + boolean enabled = aiConfigService.enableConfig(id); + if (!enabled) { + return Result.error("启用失败"); + } + return Result.success(); + } + + /** + * 禁用AI配置 + */ + @Operation(summary = "禁用AI配置", description = "禁用指定AI配置") + @PutMapping("/disable") + public Result disableConfig(@RequestParam String id) { + boolean disabled = aiConfigService.disableConfig(id); + if (!disabled) { + return Result.error("禁用失败"); + } + return Result.success(); + } + + /** + * 设置为默认配置 + */ + @Operation(summary = "设置为默认配置", description = "设置指定AI配置为默认配置") + @PutMapping("/setDefault") + public Result setAsDefault(@RequestParam String id) { + boolean set = aiConfigService.setAsDefault(id); + if (!set) { + return Result.error("设置默认配置失败"); + } + return Result.success(); + } + + /** + * 取消默认配置 + */ + @Operation(summary = "取消默认配置", description = "取消指定AI配置的默认设置") + @PutMapping("/unsetDefault") + public Result unsetDefault(@RequestParam String id) { + boolean unset = aiConfigService.unsetDefault(id); + if (!unset) { + return Result.error("取消默认配置失败"); + } + return Result.success(); + } + + /** + * 根据使用场景和环境查询最优配置 + */ + @Operation(summary = "查询最优配置", description = "根据使用场景和环境查询最优配置") + @GetMapping("/bestConfig") + public Result getBestConfig(@RequestParam String usageScenario, + @RequestParam String environment) { + AiConfigResponse response = aiConfigService.getBestConfigWithResponse(usageScenario, environment); + if (response == null) { + return Result.notFound("未找到匹配的AI配置"); + } + return Result.success(response); + } + + /** + * 统计已启用配置数量 + */ + @Operation(summary = "统计已启用配置数量", description = "统计已启用配置数量") + @GetMapping("/countEnabled") + public Result countEnabledConfigs() { + Long count = aiConfigService.countEnabledConfigs(); + return Result.success(count); + } + + /** + * 统计已禁用配置数量 + */ + @Operation(summary = "统计已禁用配置数量", description = "统计已禁用配置数量") + @GetMapping("/countDisabled") + public Result countDisabledConfigs() { + Long count = aiConfigService.countDisabledConfigs(); + return Result.success(count); + } + + /** + * 统计默认配置数量 + */ + @Operation(summary = "统计默认配置数量", description = "统计默认配置数量") + @GetMapping("/countDefault") + public Result countDefaultConfigs() { + Long count = aiConfigService.countDefaultConfigs(); + return Result.success(count); + } + + /** + * 根据配置类型统计数量 + */ + @Operation(summary = "根据配置类型统计数量", description = "根据配置类型统计数量") + @GetMapping("/countByConfigType") + public Result countByConfigType(@RequestParam String configType) { + Long count = aiConfigService.countByConfigType(configType); + return Result.success(count); + } + + /** + * 根据服务提供商统计数量 + */ + @Operation(summary = "根据服务提供商统计数量", description = "根据服务提供商统计数量") + @GetMapping("/countByProvider") + public Result countByProvider(@RequestParam String provider) { + Long count = aiConfigService.countByProvider(provider); + return Result.success(count); + } +} \ No newline at end of file diff --git a/backend-single/src/main/java/com/emotion/dto/request/aiconfig/AiConfigCreateRequest.java b/backend-single/src/main/java/com/emotion/dto/request/aiconfig/AiConfigCreateRequest.java new file mode 100644 index 0000000..076060d --- /dev/null +++ b/backend-single/src/main/java/com/emotion/dto/request/aiconfig/AiConfigCreateRequest.java @@ -0,0 +1,214 @@ +package com.emotion.dto.request.aiconfig; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.math.BigDecimal; + +/** + * 创建AI配置请求 + * + * @author system + * @date 2025-10-30 + */ +@Data +public class AiConfigCreateRequest { + + /** + * 配置名称 + */ + @NotBlank(message = "配置名称不能为空") + private String configName; + + /** + * 配置键值 (唯一标识) + */ + @NotBlank(message = "配置键值不能为空") + private String configKey; + + /** + * 配置类型: coze-扣子, openai-OpenAI, claude-Claude, gemini-Gemini等 + */ + @NotBlank(message = "配置类型不能为空") + private String configType; + + /** + * 服务提供商: coze, openai, anthropic, google等 + */ + @NotBlank(message = "服务提供商不能为空") + private String provider; + + /** + * API基础URL + */ + @NotBlank(message = "API基础URL不能为空") + private String apiBaseUrl; + + /** + * API访问令牌 (加密存储) + */ + @NotBlank(message = "API访问令牌不能为空") + private String apiToken; + + /** + * API版本 + */ + private String apiVersion; + + /** + * 模型名称 + */ + private String modelName; + + /** + * Bot ID (Coze专用) + */ + private String botId; + + /** + * Workflow ID (Coze专用) + */ + private String workflowId; + + /** + * 超时时间(毫秒) + */ + private Integer timeoutMs; + + /** + * 重试次数 + */ + private Integer retryCount; + + /** + * 重试延迟(毫秒) + */ + private Integer retryDelayMs; + + /** + * 最大Token数 + */ + private Integer maxTokens; + + /** + * 温度参数 (0.0-2.0) + */ + private BigDecimal temperature; + + /** + * Top-p参数 (0.0-1.0) + */ + private BigDecimal topP; + + /** + * 是否支持流式输出: 0-不支持, 1-支持 + */ + private Integer supportStream; + + /** + * 是否支持函数调用: 0-不支持, 1-支持 + */ + private Integer supportFunctionCall; + + /** + * 是否支持视觉理解: 0-不支持, 1-支持 + */ + private Integer supportVision; + + /** + * 是否支持文件上传: 0-不支持, 1-支持 + */ + private Integer supportFileUpload; + + /** + * 使用场景: chat-聊天, summary-总结, emotion_analysis-情绪分析, content_generation-内容生成等 + */ + @NotBlank(message = "使用场景不能为空") + private String usageScenario; + + /** + * 优先级 (数值越大优先级越高) + */ + private Integer priority; + + /** + * 输入Token价格(每1K) + */ + private BigDecimal inputPricePer1k; + + /** + * 输出Token价格(每1K) + */ + private BigDecimal outputPricePer1k; + + /** + * 货币单位 + */ + private String currency; + + /** + * 每分钟请求限制 + */ + private Integer rateLimitPerMinute; + + /** + * 每小时请求限制 + */ + private Integer rateLimitPerHour; + + /** + * 每日请求限制 + */ + private Integer rateLimitPerDay; + + /** + * 是否启用: 0-禁用, 1-启用 + */ + private Integer isEnabled; + + /** + * 是否为默认配置: 0-否, 1-是 + */ + private Integer isDefault; + + /** + * 环境: development-开发, testing-测试, production-生产 + */ + private String environment; + + /** + * 自定义请求头 + */ + private String customHeaders; + + /** + * 自定义参数 + */ + private String customParams; + + /** + * Webhook回调地址 + */ + private String webhookUrl; + + /** + * 健康检查URL + */ + private String healthCheckUrl; + + /** + * 健康检查间隔(分钟) + */ + private Integer healthCheckIntervalMinutes; + + /** + * 配置描述 + */ + private String description; + + /** + * 使用说明 + */ + private String usageNotes; +} \ No newline at end of file diff --git a/backend-single/src/main/java/com/emotion/dto/request/aiconfig/AiConfigDefaultRequest.java b/backend-single/src/main/java/com/emotion/dto/request/aiconfig/AiConfigDefaultRequest.java new file mode 100644 index 0000000..8a0bc39 --- /dev/null +++ b/backend-single/src/main/java/com/emotion/dto/request/aiconfig/AiConfigDefaultRequest.java @@ -0,0 +1,26 @@ +package com.emotion.dto.request.aiconfig; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/** + * AI配置默认设置请求 + * + * @author system + * @date 2025-10-30 + */ +@Data +public class AiConfigDefaultRequest { + + /** + * 配置ID + */ + @NotBlank(message = "配置ID不能为空") + private String id; + + /** + * 是否为默认配置: 0-否, 1-是 + */ + private Integer isDefault; +} \ No newline at end of file diff --git a/backend-single/src/main/java/com/emotion/dto/request/aiconfig/AiConfigEnableRequest.java b/backend-single/src/main/java/com/emotion/dto/request/aiconfig/AiConfigEnableRequest.java new file mode 100644 index 0000000..e2a9e16 --- /dev/null +++ b/backend-single/src/main/java/com/emotion/dto/request/aiconfig/AiConfigEnableRequest.java @@ -0,0 +1,26 @@ +package com.emotion.dto.request.aiconfig; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; + +/** + * AI配置启用/禁用请求 + * + * @author system + * @date 2025-10-30 + */ +@Data +public class AiConfigEnableRequest { + + /** + * 配置ID + */ + @NotBlank(message = "配置ID不能为空") + private String id; + + /** + * 是否启用: 0-禁用, 1-启用 + */ + private Integer isEnabled; +} \ No newline at end of file diff --git a/backend-single/src/main/java/com/emotion/dto/request/aiconfig/AiConfigPageRequest.java b/backend-single/src/main/java/com/emotion/dto/request/aiconfig/AiConfigPageRequest.java new file mode 100644 index 0000000..bb90504 --- /dev/null +++ b/backend-single/src/main/java/com/emotion/dto/request/aiconfig/AiConfigPageRequest.java @@ -0,0 +1,51 @@ +package com.emotion.dto.request.aiconfig; + +import com.emotion.dto.request.PageRequest; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * AI配置分页查询请求 + * + * @author system + * @date 2025-10-30 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class AiConfigPageRequest extends PageRequest { + + /** + * 关键词搜索(配置名称、配置键值、描述) + */ + private String keyword; + + /** + * 配置类型 + */ + private String configType; + + /** + * 服务提供商 + */ + private String provider; + + /** + * 使用场景 + */ + private String usageScenario; + + /** + * 是否启用: 0-禁用, 1-启用 + */ + private Integer isEnabled; + + /** + * 是否为默认配置: 0-否, 1-是 + */ + private Integer isDefault; + + /** + * 环境: development-开发, testing-测试, production-生产 + */ + private String environment; +} \ No newline at end of file diff --git a/backend-single/src/main/java/com/emotion/dto/request/aiconfig/AiConfigUpdateRequest.java b/backend-single/src/main/java/com/emotion/dto/request/aiconfig/AiConfigUpdateRequest.java new file mode 100644 index 0000000..6234b95 --- /dev/null +++ b/backend-single/src/main/java/com/emotion/dto/request/aiconfig/AiConfigUpdateRequest.java @@ -0,0 +1,212 @@ +package com.emotion.dto.request.aiconfig; + +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import java.math.BigDecimal; + +/** + * 更新AI配置请求 + * + * @author system + * @date 2025-10-30 + */ +@Data +public class AiConfigUpdateRequest { + + /** + * 配置ID + */ + @NotBlank(message = "配置ID不能为空") + private String id; + + /** + * 配置名称 + */ + private String configName; + + /** + * 配置键值 (唯一标识) + */ + private String configKey; + + /** + * 配置类型: coze-扣子, openai-OpenAI, claude-Claude, gemini-Gemini等 + */ + private String configType; + + /** + * 服务提供商: coze, openai, anthropic, google等 + */ + private String provider; + + /** + * API基础URL + */ + private String apiBaseUrl; + + /** + * API访问令牌 (加密存储) + */ + private String apiToken; + + /** + * API版本 + */ + private String apiVersion; + + /** + * 模型名称 + */ + private String modelName; + + /** + * Bot ID (Coze专用) + */ + private String botId; + + /** + * Workflow ID (Coze专用) + */ + private String workflowId; + + /** + * 超时时间(毫秒) + */ + private Integer timeoutMs; + + /** + * 重试次数 + */ + private Integer retryCount; + + /** + * 重试延迟(毫秒) + */ + private Integer retryDelayMs; + + /** + * 最大Token数 + */ + private Integer maxTokens; + + /** + * 温度参数 (0.0-2.0) + */ + private BigDecimal temperature; + + /** + * Top-p参数 (0.0-1.0) + */ + private BigDecimal topP; + + /** + * 是否支持流式输出: 0-不支持, 1-支持 + */ + private Integer supportStream; + + /** + * 是否支持函数调用: 0-不支持, 1-支持 + */ + private Integer supportFunctionCall; + + /** + * 是否支持视觉理解: 0-不支持, 1-支持 + */ + private Integer supportVision; + + /** + * 是否支持文件上传: 0-不支持, 1-支持 + */ + private Integer supportFileUpload; + + /** + * 使用场景: chat-聊天, summary-总结, emotion_analysis-情绪分析, content_generation-内容生成等 + */ + private String usageScenario; + + /** + * 优先级 (数值越大优先级越高) + */ + private Integer priority; + + /** + * 输入Token价格(每1K) + */ + private BigDecimal inputPricePer1k; + + /** + * 输出Token价格(每1K) + */ + private BigDecimal outputPricePer1k; + + /** + * 货币单位 + */ + private String currency; + + /** + * 每分钟请求限制 + */ + private Integer rateLimitPerMinute; + + /** + * 每小时请求限制 + */ + private Integer rateLimitPerHour; + + /** + * 每日请求限制 + */ + private Integer rateLimitPerDay; + + /** + * 是否启用: 0-禁用, 1-启用 + */ + private Integer isEnabled; + + /** + * 是否为默认配置: 0-否, 1-是 + */ + private Integer isDefault; + + /** + * 环境: development-开发, testing-测试, production-生产 + */ + private String environment; + + /** + * 自定义请求头 + */ + private String customHeaders; + + /** + * 自定义参数 + */ + private String customParams; + + /** + * Webhook回调地址 + */ + private String webhookUrl; + + /** + * 健康检查URL + */ + private String healthCheckUrl; + + /** + * 健康检查间隔(分钟) + */ + private Integer healthCheckIntervalMinutes; + + /** + * 配置描述 + */ + private String description; + + /** + * 使用说明 + */ + private String usageNotes; +} \ No newline at end of file diff --git a/backend-single/src/main/java/com/emotion/dto/response/aiconfig/AiConfigResponse.java b/backend-single/src/main/java/com/emotion/dto/response/aiconfig/AiConfigResponse.java new file mode 100644 index 0000000..25d9adb --- /dev/null +++ b/backend-single/src/main/java/com/emotion/dto/response/aiconfig/AiConfigResponse.java @@ -0,0 +1,206 @@ +package com.emotion.dto.response.aiconfig; + +import com.emotion.dto.response.BaseResponse; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * AI配置响应类 + * + * @author system + * @date 2025-10-30 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class AiConfigResponse extends BaseResponse { + + /** + * 配置名称 + */ + private String configName; + + /** + * 配置键值 (唯一标识) + */ + private String configKey; + + /** + * 配置类型: coze-扣子, openai-OpenAI, claude-Claude, gemini-Gemini等 + */ + private String configType; + + /** + * 服务提供商: coze, openai, anthropic, google等 + */ + private String provider; + + /** + * API基础URL + */ + private String apiBaseUrl; + + /** + * API访问令牌 (脱敏显示) + */ + private String apiToken; + + /** + * API版本 + */ + private String apiVersion; + + /** + * 模型名称 + */ + private String modelName; + + /** + * Bot ID (Coze专用) + */ + private String botId; + + /** + * Workflow ID (Coze专用) + */ + private String workflowId; + + /** + * 超时时间(毫秒) + */ + private Integer timeoutMs; + + /** + * 重试次数 + */ + private Integer retryCount; + + /** + * 重试延迟(毫秒) + */ + private Integer retryDelayMs; + + /** + * 最大Token数 + */ + private Integer maxTokens; + + /** + * 温度参数 (0.0-2.0) + */ + private Double temperature; + + /** + * Top-p参数 (0.0-1.0) + */ + private Double topP; + + /** + * 是否支持流式输出: 0-不支持, 1-支持 + */ + private Integer supportStream; + + /** + * 是否支持函数调用: 0-不支持, 1-支持 + */ + private Integer supportFunctionCall; + + /** + * 是否支持视觉理解: 0-不支持, 1-支持 + */ + private Integer supportVision; + + /** + * 是否支持文件上传: 0-不支持, 1-支持 + */ + private Integer supportFileUpload; + + /** + * 使用场景: chat-聊天, summary-总结, emotion_analysis-情绪分析, content_generation-内容生成等 + */ + private String usageScenario; + + /** + * 优先级 (数值越大优先级越高) + */ + private Integer priority; + + /** + * 输入Token价格(每1K) + */ + private Double inputPricePer1k; + + /** + * 输出Token价格(每1K) + */ + private Double outputPricePer1k; + + /** + * 货币单位 + */ + private String currency; + + /** + * 每分钟请求限制 + */ + private Integer rateLimitPerMinute; + + /** + * 每小时请求限制 + */ + private Integer rateLimitPerHour; + + /** + * 每日请求限制 + */ + private Integer rateLimitPerDay; + + /** + * 是否启用: 0-禁用, 1-启用 + */ + private Integer isEnabled; + + /** + * 是否为默认配置: 0-否, 1-是 + */ + private Integer isDefault; + + /** + * 环境: development-开发, testing-测试, production-生产 + */ + private String environment; + + /** + * 自定义请求头 + */ + private String customHeaders; + + /** + * 自定义参数 + */ + private String customParams; + + /** + * Webhook回调地址 + */ + private String webhookUrl; + + /** + * 健康检查URL + */ + private String healthCheckUrl; + + /** + * 健康检查间隔(分钟) + */ + private Integer healthCheckIntervalMinutes; + + /** + * 配置描述 + */ + private String description; + + /** + * 使用说明 + */ + private String usageNotes; +} \ No newline at end of file diff --git a/backend-single/src/main/java/com/emotion/entity/AiConfig.java b/backend-single/src/main/java/com/emotion/entity/AiConfig.java new file mode 100644 index 0000000..9fbc19e --- /dev/null +++ b/backend-single/src/main/java/com/emotion/entity/AiConfig.java @@ -0,0 +1,254 @@ +package com.emotion.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.emotion.common.BaseEntity; +import lombok.experimental.SuperBuilder; +import lombok.NoArgsConstructor; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; + +/** + * AI配置实体类 + * + * @author system + * @date 2025-10-30 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@SuperBuilder +@NoArgsConstructor +@AllArgsConstructor +@TableName("t_ai_config") +public class AiConfig extends BaseEntity { + + /** + * 配置名称 + */ + @TableField("config_name") + private String configName; + + /** + * 配置键值 (唯一标识) + */ + @TableField("config_key") + private String configKey; + + /** + * 配置类型: coze-扣子, openai-OpenAI, claude-Claude, gemini-Gemini等 + */ + @TableField("config_type") + private String configType; + + /** + * 服务提供商: coze, openai, anthropic, google等 + */ + @TableField("provider") + private String provider; + + /** + * API基础URL + */ + @TableField("api_base_url") + private String apiBaseUrl; + + /** + * API访问令牌 (加密存储) + */ + @TableField("api_token") + private String apiToken; + + /** + * API版本 + */ + @TableField("api_version") + private String apiVersion; + + /** + * 模型名称 + */ + @TableField("model_name") + private String modelName; + + /** + * Bot ID (Coze专用) + */ + @TableField("bot_id") + private String botId; + + /** + * Workflow ID (Coze专用) + */ + @TableField("workflow_id") + private String workflowId; + + /** + * 超时时间(毫秒) + */ + @TableField("timeout_ms") + private Integer timeoutMs; + + /** + * 重试次数 + */ + @TableField("retry_count") + private Integer retryCount; + + /** + * 重试延迟(毫秒) + */ + @TableField("retry_delay_ms") + private Integer retryDelayMs; + + /** + * 最大Token数 + */ + @TableField("max_tokens") + private Integer maxTokens; + + /** + * 温度参数 (0.0-2.0) + */ + @TableField("temperature") + private BigDecimal temperature; + + /** + * Top-p参数 (0.0-1.0) + */ + @TableField("top_p") + private BigDecimal topP; + + /** + * 是否支持流式输出: 0-不支持, 1-支持 + */ + @TableField("support_stream") + private Integer supportStream; + + /** + * 是否支持函数调用: 0-不支持, 1-支持 + */ + @TableField("support_function_call") + private Integer supportFunctionCall; + + /** + * 是否支持视觉理解: 0-不支持, 1-支持 + */ + @TableField("support_vision") + private Integer supportVision; + + /** + * 是否支持文件上传: 0-不支持, 1-支持 + */ + @TableField("support_file_upload") + private Integer supportFileUpload; + + /** + * 使用场景: chat-聊天, summary-总结, emotion_analysis-情绪分析, content_generation-内容生成等 + */ + @TableField("usage_scenario") + private String usageScenario; + + /** + * 优先级 (数值越大优先级越高) + */ + @TableField("priority") + private Integer priority; + + /** + * 输入Token价格(每1K) + */ + @TableField("input_price_per_1k") + private BigDecimal inputPricePer1k; + + /** + * 输出Token价格(每1K) + */ + @TableField("output_price_per_1k") + private BigDecimal outputPricePer1k; + + /** + * 货币单位 + */ + @TableField("currency") + private String currency; + + /** + * 每分钟请求限制 + */ + @TableField("rate_limit_per_minute") + private Integer rateLimitPerMinute; + + /** + * 每小时请求限制 + */ + @TableField("rate_limit_per_hour") + private Integer rateLimitPerHour; + + /** + * 每日请求限制 + */ + @TableField("rate_limit_per_day") + private Integer rateLimitPerDay; + + /** + * 是否启用: 0-禁用, 1-启用 + */ + @TableField("is_enabled") + private Integer isEnabled; + + /** + * 是否为默认配置: 0-否, 1-是 + */ + @TableField("is_default") + private Integer isDefault; + + /** + * 环境: development-开发, testing-测试, production-生产 + */ + @TableField("environment") + private String environment; + + /** + * 自定义请求头 + */ + @TableField("custom_headers") + private String customHeaders; + + /** + * 自定义参数 + */ + @TableField("custom_params") + private String customParams; + + /** + * Webhook回调地址 + */ + @TableField("webhook_url") + private String webhookUrl; + + /** + * 健康检查URL + */ + @TableField("health_check_url") + private String healthCheckUrl; + + /** + * 健康检查间隔(分钟) + */ + @TableField("health_check_interval_minutes") + private Integer healthCheckIntervalMinutes; + + /** + * 配置描述 + */ + @TableField("description") + private String description; + + /** + * 使用说明 + */ + @TableField("usage_notes") + private String usageNotes; +} \ No newline at end of file diff --git a/backend-single/src/main/java/com/emotion/mapper/AiConfigMapper.java b/backend-single/src/main/java/com/emotion/mapper/AiConfigMapper.java new file mode 100644 index 0000000..6fadd53 --- /dev/null +++ b/backend-single/src/main/java/com/emotion/mapper/AiConfigMapper.java @@ -0,0 +1,15 @@ +package com.emotion.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.emotion.entity.AiConfig; +import org.apache.ibatis.annotations.Mapper; + +/** + * AI配置Mapper接口 + * + * @author system + * @date 2025-10-30 + */ +@Mapper +public interface AiConfigMapper extends BaseMapper { +} \ No newline at end of file diff --git a/backend-single/src/main/java/com/emotion/service/AiConfigService.java b/backend-single/src/main/java/com/emotion/service/AiConfigService.java new file mode 100644 index 0000000..3b27909 --- /dev/null +++ b/backend-single/src/main/java/com/emotion/service/AiConfigService.java @@ -0,0 +1,179 @@ +package com.emotion.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import com.emotion.common.PageResult; +import com.emotion.dto.request.aiconfig.*; +import com.emotion.dto.response.aiconfig.AiConfigResponse; +import com.emotion.entity.AiConfig; + +import java.util.List; + +/** + * AI配置服务接口 + * + * @author system + * @date 2025-10-30 + */ +public interface AiConfigService extends IService { + + /** + * 分页查询AI配置 + */ + IPage getPage(AiConfigPageRequest request); + + /** + * 分页查询AI配置并返回响应对象 + */ + PageResult getPageWithResponse(AiConfigPageRequest request); + + /** + * 根据ID获取AI配置响应对象 + */ + AiConfigResponse getAiConfigResponseById(String id); + + /** + * 创建AI配置并返回响应对象 + */ + AiConfigResponse createAiConfigWithResponse(AiConfigCreateRequest request); + + /** + * 更新AI配置并返回响应对象 + */ + AiConfigResponse updateAiConfigWithResponse(AiConfigUpdateRequest request); + + /** + * 根据配置类型查询AI配置 + */ + List getByConfigType(String configType); + + /** + * 根据配置类型查询AI配置并返回响应对象 + */ + List getByConfigTypeWithResponse(String configType); + + /** + * 根据服务提供商查询AI配置 + */ + List getByProvider(String provider); + + /** + * 根据服务提供商查询AI配置并返回响应对象 + */ + List getByProviderWithResponse(String provider); + + /** + * 根据使用场景查询AI配置 + */ + List getByUsageScenario(String usageScenario); + + /** + * 根据使用场景查询AI配置并返回响应对象 + */ + List getByUsageScenarioWithResponse(String usageScenario); + + /** + * 根据环境查询AI配置 + */ + List getByEnvironment(String environment); + + /** + * 根据环境查询AI配置并返回响应对象 + */ + List getByEnvironmentWithResponse(String environment); + + /** + * 查询已启用的AI配置 + */ + List getEnabledConfigs(); + + /** + * 查询已启用的AI配置并返回响应对象 + */ + List getEnabledConfigsWithResponse(); + + /** + * 查询已禁用的AI配置 + */ + List getDisabledConfigs(); + + /** + * 查询已禁用的AI配置并返回响应对象 + */ + List getDisabledConfigsWithResponse(); + + /** + * 查询默认配置 + */ + List getDefaultConfigs(); + + /** + * 查询默认配置并返回响应对象 + */ + List getDefaultConfigsWithResponse(); + + /** + * 根据配置键值查询AI配置 + */ + AiConfig getByConfigKey(String configKey); + + /** + * 根据配置键值查询AI配置并返回响应对象 + */ + AiConfigResponse getByConfigKeyWithResponse(String configKey); + + /** + * 启用AI配置 + */ + boolean enableConfig(String id); + + /** + * 禁用AI配置 + */ + boolean disableConfig(String id); + + /** + * 设置为默认配置 + */ + boolean setAsDefault(String id); + + /** + * 取消默认配置 + */ + boolean unsetDefault(String id); + + /** + * 根据使用场景和环境查询最优配置 + */ + AiConfig getBestConfig(String usageScenario, String environment); + + /** + * 根据使用场景和环境查询最优配置并返回响应对象 + */ + AiConfigResponse getBestConfigWithResponse(String usageScenario, String environment); + + /** + * 统计已启用配置数量 + */ + Long countEnabledConfigs(); + + /** + * 统计已禁用配置数量 + */ + Long countDisabledConfigs(); + + /** + * 统计默认配置数量 + */ + Long countDefaultConfigs(); + + /** + * 根据配置类型统计数量 + */ + Long countByConfigType(String configType); + + /** + * 根据服务提供商统计数量 + */ + Long countByProvider(String provider); +} \ No newline at end of file diff --git a/backend-single/src/main/java/com/emotion/service/impl/AiConfigServiceImpl.java b/backend-single/src/main/java/com/emotion/service/impl/AiConfigServiceImpl.java new file mode 100644 index 0000000..9d4c48d --- /dev/null +++ b/backend-single/src/main/java/com/emotion/service/impl/AiConfigServiceImpl.java @@ -0,0 +1,411 @@ +package com.emotion.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.emotion.common.PageResult; +import com.emotion.dto.request.aiconfig.AiConfigCreateRequest; +import com.emotion.dto.request.aiconfig.AiConfigPageRequest; +import com.emotion.dto.request.aiconfig.AiConfigUpdateRequest; +import com.emotion.dto.response.aiconfig.AiConfigResponse; +import com.emotion.entity.AiConfig; +import com.emotion.mapper.AiConfigMapper; +import com.emotion.service.AiConfigService; +import com.emotion.util.SnowflakeIdGenerator; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.stream.Collectors; + +/** + * AI配置服务实现类 + * + * @author system + * @date 2025-10-30 + */ +@Service +public class AiConfigServiceImpl extends ServiceImpl implements AiConfigService { + + @Autowired + private SnowflakeIdGenerator snowflakeIdGenerator; + + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + @Override + public IPage getPage(AiConfigPageRequest request) { + Page page = new Page<>(request.getCurrent(), request.getSize()); + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + // 关键词搜索 + if (StringUtils.hasText(request.getKeyword())) { + wrapper.and(w -> w.like(AiConfig::getConfigName, request.getKeyword()) + .or().like(AiConfig::getConfigKey, request.getKeyword()) + .or().like(AiConfig::getDescription, request.getKeyword())); + } + + // 配置类型过滤 + if (StringUtils.hasText(request.getConfigType())) { + wrapper.eq(AiConfig::getConfigType, request.getConfigType()); + } + + // 服务提供商过滤 + if (StringUtils.hasText(request.getProvider())) { + wrapper.eq(AiConfig::getProvider, request.getProvider()); + } + + // 使用场景过滤 + if (StringUtils.hasText(request.getUsageScenario())) { + wrapper.eq(AiConfig::getUsageScenario, request.getUsageScenario()); + } + + // 是否启用过滤 + if (request.getIsEnabled() != null) { + wrapper.eq(AiConfig::getIsEnabled, request.getIsEnabled()); + } + + // 是否为默认配置过滤 + if (request.getIsDefault() != null) { + wrapper.eq(AiConfig::getIsDefault, request.getIsDefault()); + } + + // 环境过滤 + if (StringUtils.hasText(request.getEnvironment())) { + wrapper.eq(AiConfig::getEnvironment, request.getEnvironment()); + } + + // 排序 + if (StringUtils.hasText(request.getOrderBy())) { + if ("asc".equalsIgnoreCase(request.getOrderDirection())) { + wrapper.orderByAsc(getLambdaByField(request.getOrderBy())); + } else { + wrapper.orderByDesc(getLambdaByField(request.getOrderBy())); + } + } else { + wrapper.orderByDesc(AiConfig::getCreateTime); + } + + return this.page(page, wrapper); + } + + @Override + public PageResult getPageWithResponse(AiConfigPageRequest request) { + IPage page = getPage(request); + List responses = page.getRecords().stream() + .map(this::convertToResponse) + .collect(Collectors.toList()); + + PageResult result = new PageResult<>(); + result.setCurrent(page.getCurrent()); + result.setSize(page.getSize()); + result.setTotal(page.getTotal()); + result.setPages(page.getPages()); + result.setRecords(responses); + return result; + } + + @Override + public AiConfigResponse getAiConfigResponseById(String id) { + AiConfig aiConfig = this.getById(id); + return aiConfig != null ? convertToResponse(aiConfig) : null; + } + + @Override + public AiConfigResponse createAiConfigWithResponse(AiConfigCreateRequest request) { + AiConfig aiConfig = new AiConfig(); + BeanUtils.copyProperties(request, aiConfig); + aiConfig.setId(String.valueOf(snowflakeIdGenerator.nextId())); + + boolean saved = this.save(aiConfig); + return saved ? convertToResponse(aiConfig) : null; + } + + @Override + public AiConfigResponse updateAiConfigWithResponse(AiConfigUpdateRequest request) { + AiConfig aiConfig = this.getById(request.getId()); + if (aiConfig == null) { + return null; + } + + BeanUtils.copyProperties(request, aiConfig); + boolean updated = this.updateById(aiConfig); + return updated ? convertToResponse(aiConfig) : null; + } + + @Override + public List getByConfigType(String configType) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(AiConfig::getConfigType, configType); + wrapper.orderByDesc(AiConfig::getPriority); + return this.list(wrapper); + } + + @Override + public List getByConfigTypeWithResponse(String configType) { + return getByConfigType(configType).stream() + .map(this::convertToResponse) + .collect(Collectors.toList()); + } + + @Override + public List getByProvider(String provider) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(AiConfig::getProvider, provider); + wrapper.orderByDesc(AiConfig::getPriority); + return this.list(wrapper); + } + + @Override + public List getByProviderWithResponse(String provider) { + return getByProvider(provider).stream() + .map(this::convertToResponse) + .collect(Collectors.toList()); + } + + @Override + public List getByUsageScenario(String usageScenario) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(AiConfig::getUsageScenario, usageScenario); + wrapper.eq(AiConfig::getIsEnabled, 1); + wrapper.orderByDesc(AiConfig::getPriority); + return this.list(wrapper); + } + + @Override + public List getByUsageScenarioWithResponse(String usageScenario) { + return getByUsageScenario(usageScenario).stream() + .map(this::convertToResponse) + .collect(Collectors.toList()); + } + + @Override + public List getByEnvironment(String environment) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(AiConfig::getEnvironment, environment); + wrapper.orderByDesc(AiConfig::getPriority); + return this.list(wrapper); + } + + @Override + public List getByEnvironmentWithResponse(String environment) { + return getByEnvironment(environment).stream() + .map(this::convertToResponse) + .collect(Collectors.toList()); + } + + @Override + public List getEnabledConfigs() { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(AiConfig::getIsEnabled, 1); + wrapper.orderByDesc(AiConfig::getPriority); + return this.list(wrapper); + } + + @Override + public List getEnabledConfigsWithResponse() { + return getEnabledConfigs().stream() + .map(this::convertToResponse) + .collect(Collectors.toList()); + } + + @Override + public List getDisabledConfigs() { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(AiConfig::getIsEnabled, 0); + wrapper.orderByDesc(AiConfig::getCreateTime); + return this.list(wrapper); + } + + @Override + public List getDisabledConfigsWithResponse() { + return getDisabledConfigs().stream() + .map(this::convertToResponse) + .collect(Collectors.toList()); + } + + @Override + public List getDefaultConfigs() { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(AiConfig::getIsDefault, 1); + wrapper.orderByDesc(AiConfig::getPriority); + return this.list(wrapper); + } + + @Override + public List getDefaultConfigsWithResponse() { + return getDefaultConfigs().stream() + .map(this::convertToResponse) + .collect(Collectors.toList()); + } + + @Override + public AiConfig getByConfigKey(String configKey) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(AiConfig::getConfigKey, configKey); + return this.getOne(wrapper); + } + + @Override + public AiConfigResponse getByConfigKeyWithResponse(String configKey) { + AiConfig aiConfig = getByConfigKey(configKey); + return aiConfig != null ? convertToResponse(aiConfig) : null; + } + + @Override + public boolean enableConfig(String id) { + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(AiConfig::getId, id); + wrapper.set(AiConfig::getIsEnabled, 1); + return this.update(wrapper); + } + + @Override + public boolean disableConfig(String id) { + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(AiConfig::getId, id); + wrapper.set(AiConfig::getIsEnabled, 0); + return this.update(wrapper); + } + + @Override + public boolean setAsDefault(String id) { + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(AiConfig::getId, id); + wrapper.set(AiConfig::getIsDefault, 1); + return this.update(wrapper); + } + + @Override + public boolean unsetDefault(String id) { + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + wrapper.eq(AiConfig::getId, id); + wrapper.set(AiConfig::getIsDefault, 0); + return this.update(wrapper); + } + + @Override + public AiConfig getBestConfig(String usageScenario, String environment) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(AiConfig::getUsageScenario, usageScenario); + wrapper.eq(AiConfig::getEnvironment, environment); + wrapper.eq(AiConfig::getIsEnabled, 1); + wrapper.orderByDesc(AiConfig::getIsDefault); + wrapper.orderByDesc(AiConfig::getPriority); + wrapper.last("LIMIT 1"); + return this.getOne(wrapper); + } + + @Override + public AiConfigResponse getBestConfigWithResponse(String usageScenario, String environment) { + AiConfig aiConfig = getBestConfig(usageScenario, environment); + return aiConfig != null ? convertToResponse(aiConfig) : null; + } + + @Override + public Long countEnabledConfigs() { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(AiConfig::getIsEnabled, 1); + return this.count(wrapper); + } + + @Override + public Long countDisabledConfigs() { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(AiConfig::getIsEnabled, 0); + return this.count(wrapper); + } + + @Override + public Long countDefaultConfigs() { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(AiConfig::getIsDefault, 1); + return this.count(wrapper); + } + + @Override + public Long countByConfigType(String configType) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(AiConfig::getConfigType, configType); + return this.count(wrapper); + } + + @Override + public Long countByProvider(String provider) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(AiConfig::getProvider, provider); + return this.count(wrapper); + } + + /** + * 将实体转换为响应对象 + */ + private AiConfigResponse convertToResponse(AiConfig aiConfig) { + AiConfigResponse response = new AiConfigResponse(); + BeanUtils.copyProperties(aiConfig, response); + + // 格式化时间 + if (aiConfig.getCreateTime() != null) { + response.setCreateTime(aiConfig.getCreateTime().format(DATE_TIME_FORMATTER)); + } + if (aiConfig.getUpdateTime() != null) { + response.setUpdateTime(aiConfig.getUpdateTime().format(DATE_TIME_FORMATTER)); + } + + // 脱敏处理API Token + if (StringUtils.hasText(aiConfig.getApiToken())) { + String token = aiConfig.getApiToken(); + if (token.length() > 8) { + response.setApiToken(token.substring(0, 4) + "****" + token.substring(token.length() - 4)); + } else { + response.setApiToken("****"); + } + } + + // 转换BigDecimal为Double + if (aiConfig.getTemperature() != null) { + response.setTemperature(aiConfig.getTemperature().doubleValue()); + } + if (aiConfig.getTopP() != null) { + response.setTopP(aiConfig.getTopP().doubleValue()); + } + if (aiConfig.getInputPricePer1k() != null) { + response.setInputPricePer1k(aiConfig.getInputPricePer1k().doubleValue()); + } + if (aiConfig.getOutputPricePer1k() != null) { + response.setOutputPricePer1k(aiConfig.getOutputPricePer1k().doubleValue()); + } + + return response; + } + + /** + * 根据字段名获取对应的Lambda表达式 + */ + private com.baomidou.mybatisplus.core.toolkit.support.SFunction getLambdaByField(String field) { + switch (field) { + case "configName": + return AiConfig::getConfigName; + case "configKey": + return AiConfig::getConfigKey; + case "configType": + return AiConfig::getConfigType; + case "provider": + return AiConfig::getProvider; + case "usageScenario": + return AiConfig::getUsageScenario; + case "priority": + return AiConfig::getPriority; + case "createTime": + return AiConfig::getCreateTime; + case "updateTime": + return AiConfig::getUpdateTime; + default: + return AiConfig::getCreateTime; + } + } +} \ No newline at end of file diff --git a/sql/emotion_museum.sql b/sql/emotion_museum.sql index daa2b4b..0298e6d 100644 --- a/sql/emotion_museum.sql +++ b/sql/emotion_museum.sql @@ -76,6 +76,8 @@ DROP TABLE IF EXISTS t_user; DROP TABLE IF EXISTS t_admin; +DROP TABLE IF EXISTS t_ai_config; + -- ============================================================================ -- 1. 用户表 (user) -- ============================================================================ diff --git a/web-admin/AI_CONFIG_FRONTEND_README.md b/web-admin/AI_CONFIG_FRONTEND_README.md new file mode 100644 index 0000000..097ef97 --- /dev/null +++ b/web-admin/AI_CONFIG_FRONTEND_README.md @@ -0,0 +1,160 @@ +# AI配置管理前端实现 + +## 概述 +基于Vue 3 + TypeScript + Element Plus实现的AI配置管理前端页面,提供完整的AI配置增删改查功能。 + +## 实现的文件 + +### 1. 类型定义 +- `src/types/aiconfig.ts` - AI配置相关的TypeScript类型定义 + - AiConfig接口 + - 请求/响应类型 + - 选项常量定义 + +### 2. API调用 +- `src/api/aiconfig.ts` - AI配置相关的API调用函数 + - 基础CRUD操作 + - 条件查询 + - 状态管理 + - 统计功能 + +### 3. 页面组件 +- `src/views/aiconfig/AiConfigList.vue` - AI配置管理主页面 + - 分页列表展示 + - 搜索过滤 + - 新增/编辑对话框 + - 状态管理操作 + - 统计信息展示 + +### 4. 通用组件 +- `src/components/AiConfigQuickActions.vue` - AI配置快速操作组件 + - 统计信息展示 + - 快速操作入口 + +### 5. 路由配置 +- 更新了 `src/router/index.ts`,添加AI配置管理路由 + +### 6. 仪表板集成 +- 更新了 `src/views/Dashboard.vue`,集成AI配置统计信息 + +## 主要功能 + +### 1. 配置列表管理 +- **分页查询**: 支持分页显示配置列表 +- **多条件搜索**: 支持关键词、配置类型、服务提供商、使用场景、状态、环境等条件搜索 +- **排序功能**: 支持按不同字段排序 +- **统计信息**: 实时显示总数、启用数、禁用数、默认配置数 + +### 2. 配置CRUD操作 +- **新增配置**: 分标签页的详细配置表单 + - 基础配置:名称、类型、提供商、URL、Token等 + - 参数配置:超时、重试、Token限制、温度参数等 + - 费用配置:价格、限制等 + - 其他配置:状态、自定义参数等 +- **编辑配置**: 支持修改所有配置项 +- **查看详情**: 只读模式查看配置详情 +- **删除配置**: 确认删除操作 + +### 3. 状态管理 +- **启用/禁用**: 一键切换配置状态 +- **设置默认**: 设置/取消默认配置 +- **批量操作**: 支持批量状态管理 + +### 4. 数据展示优化 +- **标签化显示**: 配置类型、服务提供商、环境等使用不同颜色标签 +- **状态指示**: 清晰的状态标识 +- **脱敏处理**: API Token等敏感信息脱敏显示 +- **响应式布局**: 适配不同屏幕尺寸 + +### 5. 用户体验优化 +- **加载状态**: 操作过程中的加载提示 +- **错误处理**: 完善的错误提示和处理 +- **确认对话框**: 重要操作的二次确认 +- **表单验证**: 完整的表单验证规则 +- **快捷操作**: 表格行内快捷操作按钮 + +## 技术特点 + +### 1. 类型安全 +- 完整的TypeScript类型定义 +- 严格的类型检查 +- 良好的IDE支持 + +### 2. 组件化设计 +- 可复用的组件设计 +- 清晰的组件职责划分 +- 良好的组件通信 + +### 3. 响应式设计 +- 适配不同屏幕尺寸 +- 移动端友好 +- 灵活的布局系统 + +### 4. 性能优化 +- 按需加载 +- 合理的数据缓存 +- 优化的渲染性能 + +## 页面路由 + +- `/aiconfig/list` - AI配置列表页面 +- 在仪表板 `/dashboard` 中集成了AI配置统计信息 + +## 使用说明 + +### 1. 访问页面 +通过左侧导航菜单 "AI配置管理" -> "AI配置列表" 进入管理页面 + +### 2. 搜索配置 +- 使用顶部搜索表单进行条件筛选 +- 支持关键词模糊搜索 +- 支持多个条件组合搜索 + +### 3. 新增配置 +1. 点击 "新增配置" 按钮 +2. 填写基础配置信息(必填项) +3. 根据需要配置参数、费用等信息 +4. 点击确定保存 + +### 4. 编辑配置 +1. 点击表格中的 "编辑" 按钮 +2. 修改需要更新的配置项 +3. 点击确定保存更改 + +### 5. 状态管理 +- 点击 "启用/禁用" 切换配置状态 +- 点击 "设为默认/取消默认" 管理默认配置 +- 删除操作需要二次确认 + +### 6. 查看统计 +- 页面顶部显示实时统计信息 +- 仪表板中显示概览统计 +- 支持手动刷新统计数据 + +## 注意事项 + +1. **权限控制**: 需要管理员权限才能访问 +2. **数据验证**: 表单提交前会进行完整验证 +3. **敏感信息**: API Token等敏感信息会脱敏显示 +4. **操作确认**: 删除等重要操作需要二次确认 +5. **错误处理**: 网络错误和业务错误都有相应提示 + +## 扩展功能 + +### 1. 导入导出 +- 支持配置的批量导入 +- 支持配置的导出备份 + +### 2. 配置模板 +- 预设常用配置模板 +- 快速创建配置 + +### 3. 配置测试 +- 在线测试配置可用性 +- 健康检查功能 + +### 4. 使用统计 +- 配置使用频率统计 +- 性能监控数据 + +这些扩展功能可以根据实际需求逐步实现。 \ No newline at end of file diff --git a/web-admin/AI_CONFIG_MENU_TEST.md b/web-admin/AI_CONFIG_MENU_TEST.md new file mode 100644 index 0000000..4eb1642 --- /dev/null +++ b/web-admin/AI_CONFIG_MENU_TEST.md @@ -0,0 +1,120 @@ +# AI配置管理菜单测试说明 + +## 问题解决 +已成功解决AI配置管理菜单不显示的问题。 + +## 修改内容 +在 `web-admin/src/config/menu.ts` 文件中添加了AI配置管理的菜单项: + +```typescript +{ + path: '/aiconfig', + title: 'AI配置管理', + icon: 'Setting' +} +``` + +## 测试步骤 + +### 1. 启动开发服务器 +开发服务器已启动在: http://localhost:5176/ + +### 2. 访问管理后台 +1. 打开浏览器访问 http://localhost:5176/ +2. 使用管理员账号登录 + +### 3. 验证菜单显示 +在左侧菜单栏中应该能看到以下菜单项: +- 仪表盘 (DataLine图标) +- 管理员管理 (User图标) +- 用户管理 (UserFilled图标) +- **AI配置管理 (Setting图标)** ← 新添加的菜单 + +### 4. 测试功能 +1. 点击"AI配置管理"菜单项 +2. 应该跳转到 `/aiconfig/list` 页面 +3. 页面应该显示AI配置管理界面,包括: + - 搜索表单 + - 统计信息 + - 配置列表表格 + - 新增配置按钮 + +### 5. 测试仪表板集成 +1. 返回仪表盘页面 (`/`) +2. 应该能看到AI配置的统计信息 +3. 右侧应该有AI配置快速操作组件 + +## 菜单配置说明 + +菜单配置位于 `web-admin/src/config/menu.ts` 文件中,采用以下结构: + +```typescript +export interface MenuItem { + path: string // 路由路径 + title: string // 菜单标题 + icon?: string // 图标名称 (Element Plus图标) + children?: MenuItem[] // 子菜单 + hidden?: boolean // 是否隐藏 +} +``` + +## 路由配置 + +AI配置管理的路由已在 `web-admin/src/router/index.ts` 中配置: + +```typescript +{ + path: '/aiconfig', + component: Layout, + redirect: '/aiconfig/list', + meta: { title: 'AI配置管理', icon: 'Setting' }, + children: [ + { + path: 'list', + name: 'AiConfigList', + component: () => import('@/views/aiconfig/AiConfigList.vue'), + meta: { title: 'AI配置列表' } + } + ] +} +``` + +## 注意事项 + +1. **图标**: 使用的是Element Plus的内置图标 `Setting` +2. **路由**: 菜单路径 `/aiconfig` 对应路由配置 +3. **权限**: 需要管理员登录权限才能访问 +4. **响应式**: 菜单支持折叠/展开功能 + +## 故障排除 + +如果菜单仍然不显示,请检查: + +1. **缓存问题**: 清除浏览器缓存或硬刷新 (Ctrl+F5) +2. **开发服务器**: 确保开发服务器正常运行 +3. **路由配置**: 确认路由配置正确 +4. **组件导入**: 确认组件文件存在且可正常导入 + +## 后续扩展 + +可以根据需要添加子菜单,例如: + +```typescript +{ + path: '/aiconfig', + title: 'AI配置管理', + icon: 'Setting', + children: [ + { + path: '/aiconfig/list', + title: '配置列表', + icon: 'List' + }, + { + path: '/aiconfig/template', + title: '配置模板', + icon: 'Document' + } + ] +} +``` \ No newline at end of file diff --git a/web-admin/AI_CONFIG_TEST_FEATURE.md b/web-admin/AI_CONFIG_TEST_FEATURE.md new file mode 100644 index 0000000..387f001 --- /dev/null +++ b/web-admin/AI_CONFIG_TEST_FEATURE.md @@ -0,0 +1,157 @@ +# AI配置接口测试功能 + +## 功能概述 +在AI配置管理页面新增了接口测试功能,允许管理员直接在页面上测试AI配置的接口连通性和正确性。 + +## 新增功能 + +### 1. 测试按钮 +- 在AI配置列表的操作列中新增了"测试"按钮 +- 点击后打开接口测试对话框 + +### 2. 测试对话框 +测试对话框分为左右两个区域: + +#### 左侧 - 请求配置区域 +- **请求URL**: 自动根据配置的API基础URL生成,默认为 `{apiBaseUrl}/v3/chat` +- **请求头**: JSON格式的请求头,自动包含Authorization和Content-Type +- **请求体**: JSON格式的请求体,基于Coze API标准格式生成 + +#### 右侧 - 响应结果区域 +- **状态码**: 显示HTTP响应状态码,带颜色标识 +- **响应头**: 显示服务器返回的响应头信息 +- **响应体**: 显示服务器返回的响应内容,自动格式化JSON + +### 3. 操作按钮 +- **发送测试请求**: 发送HTTP请求到配置的API端点 +- **格式化请求**: 格式化请求头和请求体的JSON格式 +- **格式化响应**: 格式化响应体的JSON格式 +- **复制响应**: 将响应内容复制到剪贴板 +- **重置**: 重置测试数据到初始状态 + +## 技术实现 + +### 1. 请求构建 +基于AiChatServiceImpl中的实现,自动构建符合Coze API标准的请求: + +```json +{ + "bot_id": "配置的Bot ID", + "workflow_id": "配置的Workflow ID (可选)", + "user_id": "test_user_时间戳", + "stream": false, + "additional_messages": [ + { + "role": "user", + "content": "你好,这是一个测试消息,请回复确认接口正常工作。", + "content_type": "text", + "type": "question" + } + ] +} +``` + +### 2. 请求头处理 +- 自动添加Authorization Bearer Token +- 自动添加Content-Type: application/json +- 支持合并自定义请求头 + +### 3. 响应处理 +- 显示完整的HTTP状态码 +- 显示所有响应头信息 +- 自动格式化JSON响应体 +- 支持非JSON响应的显示 + +### 4. 错误处理 +- 网络错误处理 +- JSON格式错误提示 +- HTTP错误状态码提示 + +## 使用方法 + +### 1. 打开测试对话框 +1. 在AI配置列表中找到要测试的配置 +2. 点击操作列中的"测试"按钮 +3. 测试对话框自动打开并填充默认数据 + +### 2. 自定义测试参数 +- 可以修改请求头添加额外的头信息 +- 可以修改请求体中的消息内容 +- 可以调整其他API参数 + +### 3. 发送测试请求 +1. 点击"发送测试请求"按钮 +2. 等待请求完成(显示加载状态) +3. 查看右侧的响应结果 + +### 4. 分析测试结果 +- **状态码 200**: 请求成功,配置正常 +- **状态码 401**: 认证失败,检查API Token +- **状态码 400**: 请求参数错误,检查Bot ID等配置 +- **状态码 500**: 服务器错误,联系API提供商 + +## 支持的配置类型 + +### 1. Coze配置 +- 完全支持Coze API的测试 +- 自动构建符合Coze标准的请求格式 +- 支持Bot ID和Workflow ID + +### 2. 其他配置类型 +- 基础的HTTP请求测试 +- 可以通过修改请求体适配不同的API格式 + +## 安全考虑 + +### 1. Token脱敏 +- 在请求头显示中,Token会部分脱敏显示 +- 实际请求使用完整的Token + +### 2. 跨域处理 +- 使用浏览器原生fetch API +- 可能受到CORS策略限制 +- 建议在开发环境或配置了CORS的环境中使用 + +### 3. 数据隔离 +- 测试数据不会影响生产数据 +- 使用独立的测试用户ID + +## 故障排除 + +### 1. 网络错误 +- 检查API URL是否正确 +- 检查网络连接 +- 检查防火墙设置 + +### 2. 认证错误 +- 检查API Token是否正确 +- 检查Token是否过期 +- 检查Token权限 + +### 3. 参数错误 +- 检查Bot ID是否正确 +- 检查Workflow ID是否存在 +- 检查请求体格式 + +### 4. CORS错误 +- 在开发环境中测试 +- 或使用支持CORS的代理服务器 + +## 后续扩展 + +### 1. 批量测试 +- 支持批量测试多个配置 +- 生成测试报告 + +### 2. 测试历史 +- 保存测试历史记录 +- 支持测试结果对比 + +### 3. 自动化测试 +- 定时自动测试配置可用性 +- 异常告警通知 + +### 4. 更多API支持 +- 支持OpenAI API测试 +- 支持Claude API测试 +- 支持其他AI服务商API测试 \ No newline at end of file diff --git a/web-admin/src/api/aiconfig.ts b/web-admin/src/api/aiconfig.ts new file mode 100644 index 0000000..74f08cc --- /dev/null +++ b/web-admin/src/api/aiconfig.ts @@ -0,0 +1,207 @@ +import request from '@/utils/request' +import type { + AiConfigPageRequest, + AiConfigCreateRequest, + AiConfigUpdateRequest +} from '@/types/aiconfig' + +// 分页查询AI配置 +export function getAiConfigPage(params: AiConfigPageRequest) { + return request({ + url: '/aiConfig/page', + method: 'get', + params + }) +} + +// 根据ID获取AI配置 +export function getAiConfigById(id: string) { + return request({ + url: '/aiConfig/detail', + method: 'get', + params: { id } + }) +} + +// 创建AI配置 +export function createAiConfig(data: AiConfigCreateRequest) { + return request({ + url: '/aiConfig/create', + method: 'post', + data + }) +} + +// 更新AI配置 +export function updateAiConfig(data: AiConfigUpdateRequest) { + return request({ + url: '/aiConfig/update', + method: 'put', + data + }) +} + +// 删除AI配置 +export function deleteAiConfig(id: string) { + return request({ + url: '/aiConfig/delete', + method: 'delete', + params: { id } + }) +} + +// 根据配置类型查询AI配置 +export function getAiConfigByType(configType: string) { + return request({ + url: '/aiConfig/byConfigType', + method: 'get', + params: { configType } + }) +} + +// 根据服务提供商查询AI配置 +export function getAiConfigByProvider(provider: string) { + return request({ + url: '/aiConfig/byProvider', + method: 'get', + params: { provider } + }) +} + +// 根据使用场景查询AI配置 +export function getAiConfigByUsageScenario(usageScenario: string) { + return request({ + url: '/aiConfig/byUsageScenario', + method: 'get', + params: { usageScenario } + }) +} + +// 根据环境查询AI配置 +export function getAiConfigByEnvironment(environment: string) { + return request({ + url: '/aiConfig/byEnvironment', + method: 'get', + params: { environment } + }) +} + +// 查询已启用的AI配置 +export function getEnabledAiConfigs() { + return request({ + url: '/aiConfig/enabled', + method: 'get' + }) +} + +// 查询已禁用的AI配置 +export function getDisabledAiConfigs() { + return request({ + url: '/aiConfig/disabled', + method: 'get' + }) +} + +// 查询默认配置 +export function getDefaultAiConfigs() { + return request({ + url: '/aiConfig/default', + method: 'get' + }) +} + +// 根据配置键值查询AI配置 +export function getAiConfigByKey(configKey: string) { + return request({ + url: '/aiConfig/byConfigKey', + method: 'get', + params: { configKey } + }) +} + +// 启用AI配置 +export function enableAiConfig(id: string) { + return request({ + url: '/aiConfig/enable', + method: 'put', + params: { id } + }) +} + +// 禁用AI配置 +export function disableAiConfig(id: string) { + return request({ + url: '/aiConfig/disable', + method: 'put', + params: { id } + }) +} + +// 设置为默认配置 +export function setAsDefaultConfig(id: string) { + return request({ + url: '/aiConfig/setDefault', + method: 'put', + params: { id } + }) +} + +// 取消默认配置 +export function unsetDefaultConfig(id: string) { + return request({ + url: '/aiConfig/unsetDefault', + method: 'put', + params: { id } + }) +} + +// 查询最优配置 +export function getBestAiConfig(usageScenario: string, environment: string) { + return request({ + url: '/aiConfig/bestConfig', + method: 'get', + params: { usageScenario, environment } + }) +} + +// 统计已启用配置数量 +export function countEnabledConfigs() { + return request({ + url: '/aiConfig/countEnabled', + method: 'get' + }) +} + +// 统计已禁用配置数量 +export function countDisabledConfigs() { + return request({ + url: '/aiConfig/countDisabled', + method: 'get' + }) +} + +// 统计默认配置数量 +export function countDefaultConfigs() { + return request({ + url: '/aiConfig/countDefault', + method: 'get' + }) +} + +// 根据配置类型统计数量 +export function countByConfigType(configType: string) { + return request({ + url: '/aiConfig/countByConfigType', + method: 'get', + params: { configType } + }) +} + +// 根据服务提供商统计数量 +export function countByProvider(provider: string) { + return request({ + url: '/aiConfig/countByProvider', + method: 'get', + params: { provider } + }) +} \ No newline at end of file diff --git a/web-admin/src/components/AiConfigQuickActions.vue b/web-admin/src/components/AiConfigQuickActions.vue new file mode 100644 index 0000000..d823b53 --- /dev/null +++ b/web-admin/src/components/AiConfigQuickActions.vue @@ -0,0 +1,177 @@ + + + + + \ No newline at end of file diff --git a/web-admin/src/config/menu.ts b/web-admin/src/config/menu.ts index fa49d08..6779c37 100644 --- a/web-admin/src/config/menu.ts +++ b/web-admin/src/config/menu.ts @@ -21,5 +21,10 @@ export const menuConfig: MenuItem[] = [ path: '/user', title: '用户管理', icon: 'UserFilled' + }, + { + path: '/aiconfig', + title: 'AI配置管理', + icon: 'Setting' } ] \ No newline at end of file diff --git a/web-admin/src/router/index.ts b/web-admin/src/router/index.ts index 0c55a51..93b7098 100644 --- a/web-admin/src/router/index.ts +++ b/web-admin/src/router/index.ts @@ -49,6 +49,20 @@ const routes: RouteRecordRaw[] = [ } ] }, + { + path: '/aiconfig', + component: Layout, + redirect: '/aiconfig/list', + meta: { title: 'AI配置管理', icon: 'Setting' }, + children: [ + { + path: 'list', + name: 'AiConfigList', + component: () => import('@/views/aiconfig/AiConfigList.vue'), + meta: { title: 'AI配置列表' } + } + ] + }, { path: '/:pathMatch(.*)*', name: 'NotFound', diff --git a/web-admin/src/types/aiconfig.ts b/web-admin/src/types/aiconfig.ts new file mode 100644 index 0000000..32c0185 --- /dev/null +++ b/web-admin/src/types/aiconfig.ts @@ -0,0 +1,179 @@ +// AI配置相关类型定义 + +export interface AiConfig { + id: string + configName: string + configKey: string + configType: string + provider: string + apiBaseUrl: string + apiToken: string + apiVersion?: string + modelName?: string + botId?: string + workflowId?: string + timeoutMs?: number + retryCount?: number + retryDelayMs?: number + maxTokens?: number + temperature?: number + topP?: number + supportStream?: number + supportFunctionCall?: number + supportVision?: number + supportFileUpload?: number + usageScenario: string + priority?: number + inputPricePer1k?: number + outputPricePer1k?: number + currency?: string + rateLimitPerMinute?: number + rateLimitPerHour?: number + rateLimitPerDay?: number + isEnabled?: number + isDefault?: number + environment?: string + customHeaders?: string + customParams?: string + webhookUrl?: string + healthCheckUrl?: string + healthCheckIntervalMinutes?: number + description?: string + usageNotes?: string + createTime?: string + updateTime?: string +} + +export interface AiConfigPageRequest { + current: number + size: number + keyword?: string + configType?: string + provider?: string + usageScenario?: string + isEnabled?: number + isDefault?: number + environment?: string + orderBy?: string + orderDirection?: string +} + +export interface AiConfigCreateRequest { + configName: string + configKey: string + configType: string + provider: string + apiBaseUrl: string + apiToken: string + apiVersion?: string + modelName?: string + botId?: string + workflowId?: string + timeoutMs?: number + retryCount?: number + retryDelayMs?: number + maxTokens?: number + temperature?: number + topP?: number + supportStream?: number + supportFunctionCall?: number + supportVision?: number + supportFileUpload?: number + usageScenario: string + priority?: number + inputPricePer1k?: number + outputPricePer1k?: number + currency?: string + rateLimitPerMinute?: number + rateLimitPerHour?: number + rateLimitPerDay?: number + isEnabled?: number + isDefault?: number + environment?: string + customHeaders?: string + customParams?: string + webhookUrl?: string + healthCheckUrl?: string + healthCheckIntervalMinutes?: number + description?: string + usageNotes?: string +} + +export interface AiConfigUpdateRequest { + id: string + configName?: string + configKey?: string + configType?: string + provider?: string + apiBaseUrl?: string + apiToken?: string + apiVersion?: string + modelName?: string + botId?: string + workflowId?: string + timeoutMs?: number + retryCount?: number + retryDelayMs?: number + maxTokens?: number + temperature?: number + topP?: number + supportStream?: number + supportFunctionCall?: number + supportVision?: number + supportFileUpload?: number + usageScenario?: string + priority?: number + inputPricePer1k?: number + outputPricePer1k?: number + currency?: string + rateLimitPerMinute?: number + rateLimitPerHour?: number + rateLimitPerDay?: number + isEnabled?: number + isDefault?: number + environment?: string + customHeaders?: string + customParams?: string + webhookUrl?: string + healthCheckUrl?: string + healthCheckIntervalMinutes?: number + description?: string + usageNotes?: string +} + +// 配置类型选项 +export const CONFIG_TYPE_OPTIONS = [ + { label: 'Coze', value: 'coze' }, + { label: 'OpenAI', value: 'openai' }, + { label: 'Claude', value: 'claude' }, + { label: 'Gemini', value: 'gemini' } +] + +// 服务提供商选项 +export const PROVIDER_OPTIONS = [ + { label: 'Coze', value: 'coze' }, + { label: 'OpenAI', value: 'openai' }, + { label: 'Anthropic', value: 'anthropic' }, + { label: 'Google', value: 'google' } +] + +// 使用场景选项 +export const USAGE_SCENARIO_OPTIONS = [ + { label: '聊天', value: 'chat' }, + { label: '总结', value: 'summary' }, + { label: '情绪分析', value: 'emotion_analysis' }, + { label: '内容生成', value: 'content_generation' } +] + +// 环境选项 +export const ENVIRONMENT_OPTIONS = [ + { label: '开发环境', value: 'development' }, + { label: '测试环境', value: 'testing' }, + { label: '生产环境', value: 'production' } +] + +// 货币选项 +export const CURRENCY_OPTIONS = [ + { label: '美元', value: 'USD' }, + { label: '人民币', value: 'CNY' } +] \ No newline at end of file diff --git a/web-admin/src/views/Dashboard.vue b/web-admin/src/views/Dashboard.vue index 2996761..9cdbc45 100644 --- a/web-admin/src/views/Dashboard.vue +++ b/web-admin/src/views/Dashboard.vue @@ -60,8 +60,67 @@ + + + + +
+
+ +
+
+
{{ aiStats.total }}
+
AI配置总数
+
+
+
+
+ + + +
+
+ +
+
+
{{ aiStats.enabled }}
+
已启用配置
+
+
+
+
+ + + +
+
+ +
+
+
{{ aiStats.disabled }}
+
已禁用配置
+
+
+
+
+ + + +
+
+ +
+
+
{{ aiStats.default }}
+
默认配置
+
+
+
+
+
+ - +