后台管理功能开发,AI配置管理
This commit is contained in:
@@ -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<T>
|
||||
2. API Token在响应中会进行脱敏处理
|
||||
3. 删除操作使用逻辑删除
|
||||
4. 配置键值(configKey)必须唯一
|
||||
5. 最优配置查询会优先返回默认配置,然后按优先级排序
|
||||
@@ -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<PageResult<AiConfigResponse>> getPage(@Validated AiConfigPageRequest request) {
|
||||
PageResult<AiConfigResponse> pageResult = aiConfigService.getPageWithResponse(request);
|
||||
return Result.success(pageResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID获取AI配置
|
||||
*/
|
||||
@Operation(summary = "根据ID获取AI配置", description = "根据ID获取AI配置详情")
|
||||
@GetMapping("/detail")
|
||||
public Result<AiConfigResponse> 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<AiConfigResponse> 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<AiConfigResponse> 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<Void> 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<List<AiConfigResponse>> getByConfigType(@RequestParam String configType) {
|
||||
List<AiConfigResponse> responses = aiConfigService.getByConfigTypeWithResponse(configType);
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据服务提供商查询AI配置
|
||||
*/
|
||||
@Operation(summary = "根据服务提供商查询AI配置", description = "根据服务提供商查询AI配置列表")
|
||||
@GetMapping("/byProvider")
|
||||
public Result<List<AiConfigResponse>> getByProvider(@RequestParam String provider) {
|
||||
List<AiConfigResponse> responses = aiConfigService.getByProviderWithResponse(provider);
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据使用场景查询AI配置
|
||||
*/
|
||||
@Operation(summary = "根据使用场景查询AI配置", description = "根据使用场景查询AI配置列表")
|
||||
@GetMapping("/byUsageScenario")
|
||||
public Result<List<AiConfigResponse>> getByUsageScenario(@RequestParam String usageScenario) {
|
||||
List<AiConfigResponse> responses = aiConfigService.getByUsageScenarioWithResponse(usageScenario);
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据环境查询AI配置
|
||||
*/
|
||||
@Operation(summary = "根据环境查询AI配置", description = "根据环境查询AI配置列表")
|
||||
@GetMapping("/byEnvironment")
|
||||
public Result<List<AiConfigResponse>> getByEnvironment(@RequestParam String environment) {
|
||||
List<AiConfigResponse> responses = aiConfigService.getByEnvironmentWithResponse(environment);
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询已启用的AI配置
|
||||
*/
|
||||
@Operation(summary = "查询已启用的AI配置", description = "查询已启用的AI配置列表")
|
||||
@GetMapping("/enabled")
|
||||
public Result<List<AiConfigResponse>> getEnabledConfigs() {
|
||||
List<AiConfigResponse> responses = aiConfigService.getEnabledConfigsWithResponse();
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询已禁用的AI配置
|
||||
*/
|
||||
@Operation(summary = "查询已禁用的AI配置", description = "查询已禁用的AI配置列表")
|
||||
@GetMapping("/disabled")
|
||||
public Result<List<AiConfigResponse>> getDisabledConfigs() {
|
||||
List<AiConfigResponse> responses = aiConfigService.getDisabledConfigsWithResponse();
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询默认配置
|
||||
*/
|
||||
@Operation(summary = "查询默认配置", description = "查询默认配置列表")
|
||||
@GetMapping("/default")
|
||||
public Result<List<AiConfigResponse>> getDefaultConfigs() {
|
||||
List<AiConfigResponse> responses = aiConfigService.getDefaultConfigsWithResponse();
|
||||
return Result.success(responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据配置键值查询AI配置
|
||||
*/
|
||||
@Operation(summary = "根据配置键值查询AI配置", description = "根据配置键值查询AI配置")
|
||||
@GetMapping("/byConfigKey")
|
||||
public Result<AiConfigResponse> 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<Void> 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<Void> 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<Void> 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<Void> unsetDefault(@RequestParam String id) {
|
||||
boolean unset = aiConfigService.unsetDefault(id);
|
||||
if (!unset) {
|
||||
return Result.error("取消默认配置失败");
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据使用场景和环境查询最优配置
|
||||
*/
|
||||
@Operation(summary = "查询最优配置", description = "根据使用场景和环境查询最优配置")
|
||||
@GetMapping("/bestConfig")
|
||||
public Result<AiConfigResponse> 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<Long> countEnabledConfigs() {
|
||||
Long count = aiConfigService.countEnabledConfigs();
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计已禁用配置数量
|
||||
*/
|
||||
@Operation(summary = "统计已禁用配置数量", description = "统计已禁用配置数量")
|
||||
@GetMapping("/countDisabled")
|
||||
public Result<Long> countDisabledConfigs() {
|
||||
Long count = aiConfigService.countDisabledConfigs();
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计默认配置数量
|
||||
*/
|
||||
@Operation(summary = "统计默认配置数量", description = "统计默认配置数量")
|
||||
@GetMapping("/countDefault")
|
||||
public Result<Long> countDefaultConfigs() {
|
||||
Long count = aiConfigService.countDefaultConfigs();
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据配置类型统计数量
|
||||
*/
|
||||
@Operation(summary = "根据配置类型统计数量", description = "根据配置类型统计数量")
|
||||
@GetMapping("/countByConfigType")
|
||||
public Result<Long> countByConfigType(@RequestParam String configType) {
|
||||
Long count = aiConfigService.countByConfigType(configType);
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据服务提供商统计数量
|
||||
*/
|
||||
@Operation(summary = "根据服务提供商统计数量", description = "根据服务提供商统计数量")
|
||||
@GetMapping("/countByProvider")
|
||||
public Result<Long> countByProvider(@RequestParam String provider) {
|
||||
Long count = aiConfigService.countByProvider(provider);
|
||||
return Result.success(count);
|
||||
}
|
||||
}
|
||||
+214
@@ -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;
|
||||
}
|
||||
+26
@@ -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;
|
||||
}
|
||||
+26
@@ -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;
|
||||
}
|
||||
+51
@@ -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;
|
||||
}
|
||||
+212
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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<AiConfig> {
|
||||
}
|
||||
@@ -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<AiConfig> {
|
||||
|
||||
/**
|
||||
* 分页查询AI配置
|
||||
*/
|
||||
IPage<AiConfig> getPage(AiConfigPageRequest request);
|
||||
|
||||
/**
|
||||
* 分页查询AI配置并返回响应对象
|
||||
*/
|
||||
PageResult<AiConfigResponse> getPageWithResponse(AiConfigPageRequest request);
|
||||
|
||||
/**
|
||||
* 根据ID获取AI配置响应对象
|
||||
*/
|
||||
AiConfigResponse getAiConfigResponseById(String id);
|
||||
|
||||
/**
|
||||
* 创建AI配置并返回响应对象
|
||||
*/
|
||||
AiConfigResponse createAiConfigWithResponse(AiConfigCreateRequest request);
|
||||
|
||||
/**
|
||||
* 更新AI配置并返回响应对象
|
||||
*/
|
||||
AiConfigResponse updateAiConfigWithResponse(AiConfigUpdateRequest request);
|
||||
|
||||
/**
|
||||
* 根据配置类型查询AI配置
|
||||
*/
|
||||
List<AiConfig> getByConfigType(String configType);
|
||||
|
||||
/**
|
||||
* 根据配置类型查询AI配置并返回响应对象
|
||||
*/
|
||||
List<AiConfigResponse> getByConfigTypeWithResponse(String configType);
|
||||
|
||||
/**
|
||||
* 根据服务提供商查询AI配置
|
||||
*/
|
||||
List<AiConfig> getByProvider(String provider);
|
||||
|
||||
/**
|
||||
* 根据服务提供商查询AI配置并返回响应对象
|
||||
*/
|
||||
List<AiConfigResponse> getByProviderWithResponse(String provider);
|
||||
|
||||
/**
|
||||
* 根据使用场景查询AI配置
|
||||
*/
|
||||
List<AiConfig> getByUsageScenario(String usageScenario);
|
||||
|
||||
/**
|
||||
* 根据使用场景查询AI配置并返回响应对象
|
||||
*/
|
||||
List<AiConfigResponse> getByUsageScenarioWithResponse(String usageScenario);
|
||||
|
||||
/**
|
||||
* 根据环境查询AI配置
|
||||
*/
|
||||
List<AiConfig> getByEnvironment(String environment);
|
||||
|
||||
/**
|
||||
* 根据环境查询AI配置并返回响应对象
|
||||
*/
|
||||
List<AiConfigResponse> getByEnvironmentWithResponse(String environment);
|
||||
|
||||
/**
|
||||
* 查询已启用的AI配置
|
||||
*/
|
||||
List<AiConfig> getEnabledConfigs();
|
||||
|
||||
/**
|
||||
* 查询已启用的AI配置并返回响应对象
|
||||
*/
|
||||
List<AiConfigResponse> getEnabledConfigsWithResponse();
|
||||
|
||||
/**
|
||||
* 查询已禁用的AI配置
|
||||
*/
|
||||
List<AiConfig> getDisabledConfigs();
|
||||
|
||||
/**
|
||||
* 查询已禁用的AI配置并返回响应对象
|
||||
*/
|
||||
List<AiConfigResponse> getDisabledConfigsWithResponse();
|
||||
|
||||
/**
|
||||
* 查询默认配置
|
||||
*/
|
||||
List<AiConfig> getDefaultConfigs();
|
||||
|
||||
/**
|
||||
* 查询默认配置并返回响应对象
|
||||
*/
|
||||
List<AiConfigResponse> 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);
|
||||
}
|
||||
@@ -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<AiConfigMapper, AiConfig> implements AiConfigService {
|
||||
|
||||
@Autowired
|
||||
private SnowflakeIdGenerator snowflakeIdGenerator;
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
@Override
|
||||
public IPage<AiConfig> getPage(AiConfigPageRequest request) {
|
||||
Page<AiConfig> page = new Page<>(request.getCurrent(), request.getSize());
|
||||
LambdaQueryWrapper<AiConfig> 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<AiConfigResponse> getPageWithResponse(AiConfigPageRequest request) {
|
||||
IPage<AiConfig> page = getPage(request);
|
||||
List<AiConfigResponse> responses = page.getRecords().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
PageResult<AiConfigResponse> 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<AiConfig> getByConfigType(String configType) {
|
||||
LambdaQueryWrapper<AiConfig> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(AiConfig::getConfigType, configType);
|
||||
wrapper.orderByDesc(AiConfig::getPriority);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AiConfigResponse> getByConfigTypeWithResponse(String configType) {
|
||||
return getByConfigType(configType).stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AiConfig> getByProvider(String provider) {
|
||||
LambdaQueryWrapper<AiConfig> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(AiConfig::getProvider, provider);
|
||||
wrapper.orderByDesc(AiConfig::getPriority);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AiConfigResponse> getByProviderWithResponse(String provider) {
|
||||
return getByProvider(provider).stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AiConfig> getByUsageScenario(String usageScenario) {
|
||||
LambdaQueryWrapper<AiConfig> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(AiConfig::getUsageScenario, usageScenario);
|
||||
wrapper.eq(AiConfig::getIsEnabled, 1);
|
||||
wrapper.orderByDesc(AiConfig::getPriority);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AiConfigResponse> getByUsageScenarioWithResponse(String usageScenario) {
|
||||
return getByUsageScenario(usageScenario).stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AiConfig> getByEnvironment(String environment) {
|
||||
LambdaQueryWrapper<AiConfig> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(AiConfig::getEnvironment, environment);
|
||||
wrapper.orderByDesc(AiConfig::getPriority);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AiConfigResponse> getByEnvironmentWithResponse(String environment) {
|
||||
return getByEnvironment(environment).stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AiConfig> getEnabledConfigs() {
|
||||
LambdaQueryWrapper<AiConfig> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(AiConfig::getIsEnabled, 1);
|
||||
wrapper.orderByDesc(AiConfig::getPriority);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AiConfigResponse> getEnabledConfigsWithResponse() {
|
||||
return getEnabledConfigs().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AiConfig> getDisabledConfigs() {
|
||||
LambdaQueryWrapper<AiConfig> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(AiConfig::getIsEnabled, 0);
|
||||
wrapper.orderByDesc(AiConfig::getCreateTime);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AiConfigResponse> getDisabledConfigsWithResponse() {
|
||||
return getDisabledConfigs().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AiConfig> getDefaultConfigs() {
|
||||
LambdaQueryWrapper<AiConfig> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(AiConfig::getIsDefault, 1);
|
||||
wrapper.orderByDesc(AiConfig::getPriority);
|
||||
return this.list(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AiConfigResponse> getDefaultConfigsWithResponse() {
|
||||
return getDefaultConfigs().stream()
|
||||
.map(this::convertToResponse)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AiConfig getByConfigKey(String configKey) {
|
||||
LambdaQueryWrapper<AiConfig> 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<AiConfig> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(AiConfig::getId, id);
|
||||
wrapper.set(AiConfig::getIsEnabled, 1);
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean disableConfig(String id) {
|
||||
LambdaUpdateWrapper<AiConfig> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(AiConfig::getId, id);
|
||||
wrapper.set(AiConfig::getIsEnabled, 0);
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setAsDefault(String id) {
|
||||
LambdaUpdateWrapper<AiConfig> wrapper = new LambdaUpdateWrapper<>();
|
||||
wrapper.eq(AiConfig::getId, id);
|
||||
wrapper.set(AiConfig::getIsDefault, 1);
|
||||
return this.update(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unsetDefault(String id) {
|
||||
LambdaUpdateWrapper<AiConfig> 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<AiConfig> 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<AiConfig> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(AiConfig::getIsEnabled, 1);
|
||||
return this.count(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countDisabledConfigs() {
|
||||
LambdaQueryWrapper<AiConfig> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(AiConfig::getIsEnabled, 0);
|
||||
return this.count(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countDefaultConfigs() {
|
||||
LambdaQueryWrapper<AiConfig> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(AiConfig::getIsDefault, 1);
|
||||
return this.count(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countByConfigType(String configType) {
|
||||
LambdaQueryWrapper<AiConfig> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(AiConfig::getConfigType, configType);
|
||||
return this.count(wrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long countByProvider(String provider) {
|
||||
LambdaQueryWrapper<AiConfig> 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<AiConfig, ?> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
-- ============================================================================
|
||||
|
||||
@@ -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. 使用统计
|
||||
- 配置使用频率统计
|
||||
- 性能监控数据
|
||||
|
||||
这些扩展功能可以根据实际需求逐步实现。
|
||||
@@ -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'
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
@@ -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测试
|
||||
@@ -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 }
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>AI配置快速操作</span>
|
||||
<el-button type="text" @click="$router.push('/aiconfig/list')">
|
||||
查看全部
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="quick-actions">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<div class="action-item">
|
||||
<el-icon class="action-icon"><Setting /></el-icon>
|
||||
<div class="action-content">
|
||||
<div class="action-title">配置管理</div>
|
||||
<div class="action-desc">管理AI接口配置</div>
|
||||
</div>
|
||||
<el-button type="primary" size="small" @click="$router.push('/aiconfig/list')">
|
||||
管理
|
||||
</el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<div class="action-item">
|
||||
<el-icon class="action-icon"><Star /></el-icon>
|
||||
<div class="action-content">
|
||||
<div class="action-title">默认配置</div>
|
||||
<div class="action-desc">{{ defaultConfig ? defaultConfig.configName : '未设置' }}</div>
|
||||
</div>
|
||||
<el-button type="warning" size="small" @click="handleViewDefault" :disabled="!defaultConfig">
|
||||
查看
|
||||
</el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20" style="margin-top: 15px;">
|
||||
<el-col :span="12">
|
||||
<div class="action-item">
|
||||
<el-icon class="action-icon"><CircleCheck /></el-icon>
|
||||
<div class="action-content">
|
||||
<div class="action-title">启用配置</div>
|
||||
<div class="action-desc">{{ stats.enabled }} 个配置已启用</div>
|
||||
</div>
|
||||
<el-button type="success" size="small" @click="handleViewEnabled">
|
||||
查看
|
||||
</el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<div class="action-item">
|
||||
<el-icon class="action-icon"><Plus /></el-icon>
|
||||
<div class="action-content">
|
||||
<div class="action-title">新增配置</div>
|
||||
<div class="action-desc">添加新的AI配置</div>
|
||||
</div>
|
||||
<el-button type="primary" size="small" @click="$router.push('/aiconfig/list')">
|
||||
新增
|
||||
</el-button>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { Setting, Star, CircleCheck, Plus } from '@element-plus/icons-vue'
|
||||
import { getDefaultAiConfigs, countEnabledConfigs, countDisabledConfigs, countDefaultConfigs } from '@/api/aiconfig'
|
||||
import type { AiConfig } from '@/types/aiconfig'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const stats = reactive({
|
||||
enabled: 0,
|
||||
disabled: 0,
|
||||
default: 0
|
||||
})
|
||||
|
||||
const defaultConfig = ref<AiConfig | null>(null)
|
||||
|
||||
// 获取统计信息
|
||||
const fetchStats = async () => {
|
||||
try {
|
||||
const [enabledRes, disabledRes, defaultRes] = await Promise.all([
|
||||
countEnabledConfigs(),
|
||||
countDisabledConfigs(),
|
||||
countDefaultConfigs()
|
||||
])
|
||||
stats.enabled = enabledRes.data
|
||||
stats.disabled = disabledRes.data
|
||||
stats.default = defaultRes.data
|
||||
} catch (error) {
|
||||
console.error('获取统计信息失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取默认配置
|
||||
const fetchDefaultConfig = async () => {
|
||||
try {
|
||||
const res = await getDefaultAiConfigs()
|
||||
if (res.data && res.data.length > 0) {
|
||||
defaultConfig.value = res.data[0]
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取默认配置失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 查看默认配置
|
||||
const handleViewDefault = () => {
|
||||
router.push('/aiconfig/list')
|
||||
}
|
||||
|
||||
// 查看启用的配置
|
||||
const handleViewEnabled = () => {
|
||||
router.push('/aiconfig/list')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
fetchStats()
|
||||
fetchDefaultConfig()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.quick-actions {
|
||||
.action-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:hover {
|
||||
border-color: #409eff;
|
||||
background-color: #f0f9ff;
|
||||
}
|
||||
|
||||
.action-icon {
|
||||
font-size: 24px;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.action-content {
|
||||
flex: 1;
|
||||
|
||||
.action-title {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.action-desc {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -21,5 +21,10 @@ export const menuConfig: MenuItem[] = [
|
||||
path: '/user',
|
||||
title: '用户管理',
|
||||
icon: 'UserFilled'
|
||||
},
|
||||
{
|
||||
path: '/aiconfig',
|
||||
title: 'AI配置管理',
|
||||
icon: 'Setting'
|
||||
}
|
||||
]
|
||||
@@ -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',
|
||||
|
||||
@@ -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' }
|
||||
]
|
||||
@@ -60,8 +60,67 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- AI配置统计 -->
|
||||
<el-row :gutter="20" class="stats-row">
|
||||
<el-col :span="6">
|
||||
<el-card class="stat-card">
|
||||
<div class="stat-content">
|
||||
<div class="stat-icon" style="background-color: #909399;">
|
||||
<el-icon><Setting /></el-icon>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ aiStats.total }}</div>
|
||||
<div class="stat-label">AI配置总数</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="6">
|
||||
<el-card class="stat-card">
|
||||
<div class="stat-content">
|
||||
<div class="stat-icon" style="background-color: #67c23a;">
|
||||
<el-icon><CircleCheck /></el-icon>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ aiStats.enabled }}</div>
|
||||
<div class="stat-label">已启用配置</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="6">
|
||||
<el-card class="stat-card">
|
||||
<div class="stat-content">
|
||||
<div class="stat-icon" style="background-color: #f56c6c;">
|
||||
<el-icon><CircleClose /></el-icon>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ aiStats.disabled }}</div>
|
||||
<div class="stat-label">已禁用配置</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="6">
|
||||
<el-card class="stat-card">
|
||||
<div class="stat-content">
|
||||
<div class="stat-icon" style="background-color: #e6a23c;">
|
||||
<el-icon><Star /></el-icon>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ aiStats.default }}</div>
|
||||
<div class="stat-label">默认配置</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20" class="chart-row">
|
||||
<el-col :span="16">
|
||||
<el-col :span="12">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<span>用户增长趋势</span>
|
||||
@@ -70,7 +129,7 @@
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8">
|
||||
<el-col :span="6">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<span>最近登录</span>
|
||||
@@ -81,17 +140,30 @@
|
||||
</el-table>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="6">
|
||||
<AiConfigQuickActions />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { User, UserFilled, TrendCharts, ChatDotRound } from '@element-plus/icons-vue'
|
||||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { User, UserFilled, TrendCharts, ChatDotRound, Setting, CircleCheck, CircleClose, Star } from '@element-plus/icons-vue'
|
||||
import * as echarts from 'echarts'
|
||||
import { countEnabledConfigs, countDisabledConfigs, countDefaultConfigs } from '@/api/aiconfig'
|
||||
import AiConfigQuickActions from '@/components/AiConfigQuickActions.vue'
|
||||
|
||||
const userChartRef = ref<HTMLElement>()
|
||||
|
||||
const aiStats = reactive({
|
||||
total: 0,
|
||||
enabled: 0,
|
||||
disabled: 0,
|
||||
default: 0
|
||||
})
|
||||
|
||||
const recentLogins = ref([
|
||||
{ username: '张三', time: '2分钟前' },
|
||||
{ username: '李四', time: '5分钟前' },
|
||||
@@ -101,8 +173,26 @@ const recentLogins = ref([
|
||||
|
||||
onMounted(() => {
|
||||
initUserChart()
|
||||
fetchAiStats()
|
||||
})
|
||||
|
||||
// 获取AI配置统计
|
||||
const fetchAiStats = async () => {
|
||||
try {
|
||||
const [enabledRes, disabledRes, defaultRes] = await Promise.all([
|
||||
countEnabledConfigs(),
|
||||
countDisabledConfigs(),
|
||||
countDefaultConfigs()
|
||||
])
|
||||
aiStats.enabled = enabledRes.data
|
||||
aiStats.disabled = disabledRes.data
|
||||
aiStats.default = defaultRes.data
|
||||
aiStats.total = aiStats.enabled + aiStats.disabled
|
||||
} catch (error) {
|
||||
console.error('获取AI配置统计失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const initUserChart = () => {
|
||||
if (!userChartRef.value) return
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user