AI配置增加字段适配处理
This commit is contained in:
@@ -287,4 +287,17 @@ public class AiConfigController {
|
||||
Long count = aiConfigService.countByProvider(provider);
|
||||
return Result.success(count);
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试后更新AI配置
|
||||
*/
|
||||
@Operation(summary = "测试后更新AI配置", description = "从测试请求中解析参数并更新配置")
|
||||
@PutMapping("/updateFromTest")
|
||||
public Result<AiConfigResponse> updateFromTest(@RequestBody @Validated AiConfigTestUpdateRequest request) {
|
||||
AiConfigResponse response = aiConfigService.updateFromTestRequest(request);
|
||||
if (response == null) {
|
||||
return Result.error("更新失败,配置不存在");
|
||||
}
|
||||
return Result.success("更新成功", response);
|
||||
}
|
||||
}
|
||||
+15
@@ -57,6 +57,21 @@ public class AiConfigCreateRequest {
|
||||
*/
|
||||
private String apiVersion;
|
||||
|
||||
/**
|
||||
* OAuth客户端ID
|
||||
*/
|
||||
private String clientId;
|
||||
|
||||
/**
|
||||
* OAuth客户端密钥 (加密存储)
|
||||
*/
|
||||
private String clientSecret;
|
||||
|
||||
/**
|
||||
* 授权类型: client_credentials, authorization_code, password, refresh_token
|
||||
*/
|
||||
private String grantType;
|
||||
|
||||
/**
|
||||
* 模型名称
|
||||
*/
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
package com.emotion.dto.request.aiconfig;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* AI配置测试后更新请求
|
||||
* 用于在测试接口成功后,保存测试时使用的参数
|
||||
*
|
||||
* @author system
|
||||
* @date 2025-12-22
|
||||
*/
|
||||
@Data
|
||||
public class AiConfigTestUpdateRequest {
|
||||
|
||||
/**
|
||||
* 配置ID
|
||||
*/
|
||||
@NotBlank(message = "配置ID不能为空")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* API完整URL(从测试请求中获取)
|
||||
*/
|
||||
private String apiBaseUrl;
|
||||
|
||||
/**
|
||||
* API访问令牌(从测试请求头中解析)
|
||||
*/
|
||||
private String apiToken;
|
||||
|
||||
/**
|
||||
* OAuth客户端ID
|
||||
*/
|
||||
private String clientId;
|
||||
|
||||
/**
|
||||
* OAuth客户端密钥
|
||||
*/
|
||||
private String clientSecret;
|
||||
|
||||
/**
|
||||
* 授权类型: client_credentials, authorization_code, password, refresh_token
|
||||
*/
|
||||
private String grantType;
|
||||
|
||||
/**
|
||||
* Bot ID(从测试请求体中解析,Coze专用)
|
||||
*/
|
||||
private String botId;
|
||||
|
||||
/**
|
||||
* Workflow ID(从测试请求体中解析,Coze专用)
|
||||
*/
|
||||
private String workflowId;
|
||||
|
||||
/**
|
||||
* 自定义请求头(JSON格式,从测试请求头中提取)
|
||||
*/
|
||||
private String customHeaders;
|
||||
|
||||
/**
|
||||
* 自定义参数(JSON格式,从测试请求体中提取)
|
||||
*/
|
||||
private String customParams;
|
||||
|
||||
/**
|
||||
* 是否支持流式输出(从测试请求体中解析)
|
||||
*/
|
||||
private Integer supportStream;
|
||||
}
|
||||
+15
@@ -56,6 +56,21 @@ public class AiConfigUpdateRequest {
|
||||
*/
|
||||
private String apiVersion;
|
||||
|
||||
/**
|
||||
* OAuth客户端ID
|
||||
*/
|
||||
private String clientId;
|
||||
|
||||
/**
|
||||
* OAuth客户端密钥 (加密存储)
|
||||
*/
|
||||
private String clientSecret;
|
||||
|
||||
/**
|
||||
* 授权类型: client_credentials, authorization_code, password, refresh_token
|
||||
*/
|
||||
private String grantType;
|
||||
|
||||
/**
|
||||
* 模型名称
|
||||
*/
|
||||
|
||||
@@ -50,6 +50,21 @@ public class AiConfigResponse extends BaseResponse {
|
||||
*/
|
||||
private String apiVersion;
|
||||
|
||||
/**
|
||||
* OAuth客户端ID
|
||||
*/
|
||||
private String clientId;
|
||||
|
||||
/**
|
||||
* OAuth客户端密钥 (脱敏显示)
|
||||
*/
|
||||
private String clientSecret;
|
||||
|
||||
/**
|
||||
* 授权类型: client_credentials, authorization_code, password, refresh_token
|
||||
*/
|
||||
private String grantType;
|
||||
|
||||
/**
|
||||
* 模型名称
|
||||
*/
|
||||
|
||||
@@ -67,6 +67,24 @@ public class AiConfig extends BaseEntity {
|
||||
@TableField("api_version")
|
||||
private String apiVersion;
|
||||
|
||||
/**
|
||||
* 客户端ID (OAuth认证)
|
||||
*/
|
||||
@TableField("client_id")
|
||||
private String clientId;
|
||||
|
||||
/**
|
||||
* 客户端密钥 (OAuth认证,加密存储)
|
||||
*/
|
||||
@TableField("client_secret")
|
||||
private String clientSecret;
|
||||
|
||||
/**
|
||||
* 授权类型: client_credentials, authorization_code, password等
|
||||
*/
|
||||
@TableField("grant_type")
|
||||
private String grantType;
|
||||
|
||||
/**
|
||||
* 模型名称
|
||||
*/
|
||||
|
||||
@@ -155,4 +155,30 @@ public interface AiChatService {
|
||||
* @return 情绪总结状态响应
|
||||
*/
|
||||
EmotionSummaryStatusResponse getEmotionSummaryStatusWithResponse(String userId);
|
||||
|
||||
/**
|
||||
* 通过配置键调用Coze工作流API
|
||||
* 根据config_key从数据库获取AI配置,构建请求并调用Coze工作流接口
|
||||
* 默认使用流式调用方式
|
||||
*
|
||||
* @param configKey AI配置键(如:coze.course.life.generate)
|
||||
* @param input 输入参数,将作为parameters.input传递给工作流
|
||||
* @param userId 用户ID
|
||||
* @return AI生成的内容
|
||||
* @throws RuntimeException 如果配置不存在或已禁用
|
||||
*/
|
||||
String callWorkflowByConfigKey(String configKey, String input, String userId);
|
||||
|
||||
/**
|
||||
* 通过配置键调用Coze工作流API(带自定义参数)
|
||||
* 根据config_key从数据库获取AI配置,将自定义参数与配置中的custom_params合并后调用工作流
|
||||
* 默认使用流式调用方式
|
||||
*
|
||||
* @param configKey AI配置键
|
||||
* @param parameters 自定义参数Map,将合并到请求的parameters中
|
||||
* @param userId 用户ID
|
||||
* @return AI生成的内容
|
||||
* @throws RuntimeException 如果配置不存在或已禁用
|
||||
*/
|
||||
String callWorkflowByConfigKey(String configKey, Map<String, Object> parameters, String userId);
|
||||
}
|
||||
@@ -176,4 +176,10 @@ public interface AiConfigService extends IService<AiConfig> {
|
||||
* 根据服务提供商统计数量
|
||||
*/
|
||||
Long countByProvider(String provider);
|
||||
|
||||
/**
|
||||
* 测试后更新AI配置
|
||||
* 从测试请求中解析参数并更新配置
|
||||
*/
|
||||
AiConfigResponse updateFromTestRequest(AiConfigTestUpdateRequest request);
|
||||
}
|
||||
@@ -2088,4 +2088,687 @@ public class AiChatServiceImpl implements AiChatService {
|
||||
|
||||
log.debug("Coze请求参数验证通过");
|
||||
}
|
||||
|
||||
// ==================== 通用工作流调用方法 ====================
|
||||
|
||||
@Override
|
||||
public String callWorkflowByConfigKey(String configKey, String input, String userId) {
|
||||
log.info("通过配置键调用Coze工作流: configKey={}, userId={}", configKey, userId);
|
||||
|
||||
// 构建参数Map
|
||||
Map<String, Object> parameters = new HashMap<>();
|
||||
parameters.put("input", input);
|
||||
parameters.put("user_id", userId);
|
||||
|
||||
return callWorkflowByConfigKey(configKey, parameters, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String callWorkflowByConfigKey(String configKey, Map<String, Object> parameters, String userId) {
|
||||
log.info("通过配置键调用Coze工作流(带参数): configKey={}, userId={}", configKey, userId);
|
||||
|
||||
// 1. 获取AI配置
|
||||
AiConfig config = aiConfigService.getByConfigKey(configKey);
|
||||
if (config == null) {
|
||||
log.error("未找到AI配置或配置已禁用: configKey={}", configKey);
|
||||
throw new RuntimeException("未找到AI配置: " + configKey);
|
||||
}
|
||||
|
||||
// 2. 构建工作流请求
|
||||
Map<String, Object> requestBody = buildWorkflowRequest(config, parameters, userId);
|
||||
|
||||
// 3. 创建API调用记录
|
||||
String userInput = parameters != null ? String.valueOf(parameters.get("input")) : "";
|
||||
CozeApiCall apiCall = createWorkflowApiCallRecord(config, configKey, userInput, userId);
|
||||
|
||||
// 4. 执行工作流调用
|
||||
return executeWorkflowCallWithRecord(config, requestBody, configKey, userId, apiCall);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建Coze工作流请求体
|
||||
*
|
||||
* @param config AI配置
|
||||
* @param parameters 运行时参数
|
||||
* @param userId 用户ID
|
||||
* @return 请求体Map
|
||||
*/
|
||||
private Map<String, Object> buildWorkflowRequest(AiConfig config, Map<String, Object> parameters, String userId) {
|
||||
Map<String, Object> requestBody = new HashMap<>();
|
||||
|
||||
// 设置workflow_id
|
||||
if (config.getWorkflowId() != null && !config.getWorkflowId().trim().isEmpty()) {
|
||||
requestBody.put("workflow_id", config.getWorkflowId());
|
||||
}
|
||||
|
||||
// 设置user_id
|
||||
requestBody.put("user_id", userId != null ? userId : DEFAULT_USER_ID);
|
||||
|
||||
// 设置stream为true(默认流式调用)
|
||||
requestBody.put("stream", true);
|
||||
|
||||
// 合并custom_params和运行时参数
|
||||
Map<String, Object> mergedParameters = mergeParameters(config, parameters);
|
||||
requestBody.put("parameters", mergedParameters);
|
||||
|
||||
log.info("构建工作流请求完成: workflowId={}, userId={}, parametersKeys={}",
|
||||
config.getWorkflowId(), userId, mergedParameters.keySet());
|
||||
|
||||
return requestBody;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并配置中的custom_params和运行时参数
|
||||
* 运行时参数优先级高于custom_params
|
||||
*
|
||||
* @param config AI配置
|
||||
* @param runtimeParameters 运行时参数
|
||||
* @return 合并后的参数Map
|
||||
*/
|
||||
private Map<String, Object> mergeParameters(AiConfig config, Map<String, Object> runtimeParameters) {
|
||||
Map<String, Object> mergedParams = new HashMap<>();
|
||||
|
||||
// 1. 先加载custom_params中的parameters
|
||||
if (config.getCustomParams() != null && !config.getCustomParams().trim().isEmpty()) {
|
||||
try {
|
||||
JSONObject customParamsJson = JSON.parseObject(config.getCustomParams());
|
||||
if (customParamsJson.containsKey("parameters")) {
|
||||
JSONObject configParams = customParamsJson.getJSONObject("parameters");
|
||||
if (configParams != null) {
|
||||
mergedParams.putAll(configParams);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("解析custom_params失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 运行时参数覆盖配置参数
|
||||
if (runtimeParameters != null) {
|
||||
mergedParams.putAll(runtimeParameters);
|
||||
}
|
||||
|
||||
return mergedParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行Coze工作流调用(带重试机制)
|
||||
*
|
||||
* @param config AI配置
|
||||
* @param requestBody 请求体
|
||||
* @param configKey 配置键(用于日志)
|
||||
* @param userId 用户ID
|
||||
* @return AI生成的内容
|
||||
*/
|
||||
private String executeWorkflowCall(AiConfig config, Map<String, Object> requestBody, String configKey, String userId) {
|
||||
// 获取重试配置
|
||||
int maxRetries = config.getRetryCount() != null ? config.getRetryCount() : 0;
|
||||
int retryDelayMs = config.getRetryDelayMs() != null ? config.getRetryDelayMs() : 1000;
|
||||
|
||||
Exception lastException = null;
|
||||
|
||||
for (int attempt = 0; attempt <= maxRetries; attempt++) {
|
||||
try {
|
||||
if (attempt > 0) {
|
||||
log.info("Coze工作流调用重试: configKey={}, 第{}次重试", configKey, attempt);
|
||||
Thread.sleep(retryDelayMs);
|
||||
}
|
||||
|
||||
return doExecuteWorkflowCall(config, requestBody, configKey);
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.error("Coze工作流调用被中断: configKey={}", configKey);
|
||||
throw new RuntimeException("AI服务调用被中断");
|
||||
} catch (Exception e) {
|
||||
lastException = e;
|
||||
log.warn("Coze工作流调用失败: configKey={}, 尝试次数={}/{}, error={}",
|
||||
configKey, attempt + 1, maxRetries + 1, e.getMessage());
|
||||
|
||||
if (attempt >= maxRetries) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.error("Coze工作流调用最终失败: configKey={}, 已重试{}次, error={}",
|
||||
configKey, maxRetries, lastException != null ? lastException.getMessage() : "未知错误");
|
||||
throw new RuntimeException("AI服务调用失败: " + (lastException != null ? lastException.getMessage() : "未知错误"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行Coze工作流调用(带重试机制和API调用记录)
|
||||
*
|
||||
* @param config AI配置
|
||||
* @param requestBody 请求体
|
||||
* @param configKey 配置键(用于日志)
|
||||
* @param userId 用户ID
|
||||
* @param apiCall API调用记录
|
||||
* @return AI生成的内容
|
||||
*/
|
||||
private String executeWorkflowCallWithRecord(AiConfig config, Map<String, Object> requestBody,
|
||||
String configKey, String userId, CozeApiCall apiCall) {
|
||||
// 获取重试配置
|
||||
int maxRetries = config.getRetryCount() != null ? config.getRetryCount() : 0;
|
||||
int retryDelayMs = config.getRetryDelayMs() != null ? config.getRetryDelayMs() : 1000;
|
||||
|
||||
Exception lastException = null;
|
||||
|
||||
for (int attempt = 0; attempt <= maxRetries; attempt++) {
|
||||
try {
|
||||
if (attempt > 0) {
|
||||
log.info("Coze工作流调用重试: configKey={}, 第{}次重试", configKey, attempt);
|
||||
Thread.sleep(retryDelayMs);
|
||||
}
|
||||
|
||||
return doExecuteWorkflowCallWithRecord(config, requestBody, configKey, apiCall);
|
||||
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
log.error("Coze工作流调用被中断: configKey={}", configKey);
|
||||
updateWorkflowApiCallError(apiCall, "INTERRUPTED", "AI服务调用被中断");
|
||||
throw new RuntimeException("AI服务调用被中断");
|
||||
} catch (Exception e) {
|
||||
lastException = e;
|
||||
log.warn("Coze工作流调用失败: configKey={}, 尝试次数={}/{}, error={}",
|
||||
configKey, attempt + 1, maxRetries + 1, e.getMessage());
|
||||
|
||||
if (attempt >= maxRetries) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.error("Coze工作流调用最终失败: configKey={}, 已重试{}次, error={}",
|
||||
configKey, maxRetries, lastException != null ? lastException.getMessage() : "未知错误");
|
||||
updateWorkflowApiCallError(apiCall, "MAX_RETRY_EXCEEDED",
|
||||
lastException != null ? lastException.getMessage() : "未知错误");
|
||||
throw new RuntimeException("AI服务调用失败: " + (lastException != null ? lastException.getMessage() : "未知错误"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行单次Coze工作流调用
|
||||
*
|
||||
* @param config AI配置
|
||||
* @param requestBody 请求体
|
||||
* @param configKey 配置键(用于日志)
|
||||
* @return AI生成的内容
|
||||
*/
|
||||
private String doExecuteWorkflowCall(AiConfig config, Map<String, Object> requestBody, String configKey) {
|
||||
// 构建请求头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("Authorization", "Bearer " + config.getApiToken());
|
||||
headers.set("Content-Type", "application/json");
|
||||
headers.set("Accept", "text/event-stream");
|
||||
|
||||
String apiUrl = config.getApiBaseUrl();
|
||||
|
||||
log.info("发送Coze工作流请求: configKey={}, url={}", configKey, apiUrl);
|
||||
log.debug("请求体: {}", JSON.toJSONString(requestBody));
|
||||
|
||||
// 执行流式调用
|
||||
String result = handleWorkflowStreamResponse(apiUrl, headers, requestBody, config);
|
||||
|
||||
log.info("Coze工作流调用成功: configKey={}, resultLength={}", configKey, result != null ? result.length() : 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行单次Coze工作流调用(带API调用记录)
|
||||
*
|
||||
* @param config AI配置
|
||||
* @param requestBody 请求体
|
||||
* @param configKey 配置键(用于日志)
|
||||
* @param apiCall API调用记录
|
||||
* @return AI生成的内容
|
||||
*/
|
||||
private String doExecuteWorkflowCallWithRecord(AiConfig config, Map<String, Object> requestBody,
|
||||
String configKey, CozeApiCall apiCall) {
|
||||
// 构建请求头
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.set("Authorization", "Bearer " + config.getApiToken());
|
||||
headers.set("Content-Type", "application/json");
|
||||
headers.set("Accept", "text/event-stream");
|
||||
|
||||
String apiUrl = config.getApiBaseUrl();
|
||||
|
||||
// 更新API调用记录的请求信息
|
||||
updateWorkflowApiCallRequest(apiCall, apiUrl, requestBody, headers);
|
||||
|
||||
log.info("发送Coze工作流请求: configKey={}, url={}, apiCallId={}", configKey, apiUrl, apiCall.getId());
|
||||
log.debug("请求体: {}", JSON.toJSONString(requestBody));
|
||||
|
||||
// 执行流式调用
|
||||
String result = handleWorkflowStreamResponseWithRecord(apiUrl, headers, requestBody, config, apiCall);
|
||||
|
||||
// 更新API调用记录的成功结果
|
||||
updateWorkflowApiCallSuccess(apiCall, result);
|
||||
|
||||
log.info("Coze工作流调用成功: configKey={}, resultLength={}, apiCallId={}",
|
||||
configKey, result != null ? result.length() : 0, apiCall.getId());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理Coze工作流流式响应
|
||||
*
|
||||
* @param url API URL
|
||||
* @param headers 请求头
|
||||
* @param requestBody 请求体
|
||||
* @param config AI配置
|
||||
* @return 提取的output内容
|
||||
*/
|
||||
private String handleWorkflowStreamResponse(String url, HttpHeaders headers, Map<String, Object> requestBody, AiConfig config) {
|
||||
try {
|
||||
log.info("开始处理工作流流式响应,URL: {}", url);
|
||||
|
||||
// 获取超时配置
|
||||
int timeoutMs = config.getTimeoutMs() != null ? config.getTimeoutMs() : 30000;
|
||||
|
||||
// 创建HTTP客户端
|
||||
java.net.http.HttpClient client = java.net.http.HttpClient.newBuilder()
|
||||
.connectTimeout(java.time.Duration.ofMillis(timeoutMs))
|
||||
.build();
|
||||
|
||||
// 构建请求
|
||||
java.net.http.HttpRequest.Builder requestBuilder = java.net.http.HttpRequest.newBuilder()
|
||||
.uri(java.net.URI.create(url))
|
||||
.timeout(java.time.Duration.ofMillis(timeoutMs * 2))
|
||||
.POST(java.net.http.HttpRequest.BodyPublishers.ofString(JSON.toJSONString(requestBody)));
|
||||
|
||||
// 添加请求头
|
||||
headers.forEach((key, values) -> {
|
||||
if (values != null && !values.isEmpty()) {
|
||||
requestBuilder.header(key, values.get(0));
|
||||
}
|
||||
});
|
||||
|
||||
java.net.http.HttpRequest request = requestBuilder.build();
|
||||
|
||||
// 发送请求并处理流式响应
|
||||
java.net.http.HttpResponse<java.util.stream.Stream<String>> response = client.send(request,
|
||||
java.net.http.HttpResponse.BodyHandlers.ofLines());
|
||||
|
||||
log.info("工作流流式响应状态码: {}", response.statusCode());
|
||||
|
||||
if (response.statusCode() != 200) {
|
||||
String errorBody = response.body().collect(java.util.stream.Collectors.joining("\n"));
|
||||
log.error("工作流请求失败,状态码: {}, 响应: {}", response.statusCode(), errorBody);
|
||||
throw new RuntimeException("工作流请求失败,状态码: " + response.statusCode());
|
||||
}
|
||||
|
||||
// 解析SSE流式响应
|
||||
return parseWorkflowSseResponse(response.body());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理工作流流式响应失败: {}", e.getMessage(), e);
|
||||
throw new RuntimeException("处理工作流响应失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析Coze工作流SSE响应
|
||||
* 格式:
|
||||
* id: 0
|
||||
* event: Message
|
||||
* data: {"node_title":"End",...,"content":"{\"output\":\"...\"}","node_type":"End",...}
|
||||
*
|
||||
* id: 1
|
||||
* event: Done
|
||||
* data: {...}
|
||||
*
|
||||
* @param lines 响应行流
|
||||
* @return 提取的output内容
|
||||
*/
|
||||
private String parseWorkflowSseResponse(java.util.stream.Stream<String> lines) {
|
||||
StringBuilder resultBuilder = new StringBuilder();
|
||||
StringBuilder fullStreamData = new StringBuilder();
|
||||
String currentEvent = null;
|
||||
|
||||
java.util.Iterator<String> lineIterator = lines.iterator();
|
||||
while (lineIterator.hasNext()) {
|
||||
String line = lineIterator.next();
|
||||
fullStreamData.append(line).append("\n");
|
||||
|
||||
if (line.trim().isEmpty()) {
|
||||
currentEvent = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 解析event行
|
||||
if (line.startsWith("event:")) {
|
||||
currentEvent = line.substring(6).trim();
|
||||
log.debug("工作流事件类型: {}", currentEvent);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 解析data行
|
||||
if (line.startsWith("data:")) {
|
||||
String data = line.substring(5).trim();
|
||||
|
||||
// 检查是否为结束标记
|
||||
if ("\"[DONE]\"".equals(data) || "[DONE]".equals(data)) {
|
||||
log.info("收到工作流响应结束标记");
|
||||
break;
|
||||
}
|
||||
|
||||
if (data.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 处理Message事件
|
||||
if ("Message".equals(currentEvent)) {
|
||||
try {
|
||||
JSONObject jsonData = JSON.parseObject(data);
|
||||
String nodeType = jsonData.getString("node_type");
|
||||
|
||||
// 只处理End节点的内容
|
||||
if ("End".equals(nodeType)) {
|
||||
String content = jsonData.getString("content");
|
||||
if (content != null && !content.trim().isEmpty()) {
|
||||
// 从content中提取output
|
||||
String output = extractOutputFromContent(content);
|
||||
if (output != null) {
|
||||
resultBuilder.append(output);
|
||||
log.info("成功提取工作流output内容,长度: {}", output.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("解析工作流Message数据失败: {}, 数据: {}", e.getMessage(), data);
|
||||
}
|
||||
}
|
||||
// 处理Done事件
|
||||
else if ("Done".equals(currentEvent)) {
|
||||
log.info("工作流执行完成");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String result = resultBuilder.toString();
|
||||
if (result.isEmpty()) {
|
||||
log.warn("工作流响应解析完成但内容为空,原始数据: {}", fullStreamData);
|
||||
throw new RuntimeException("AI响应解析失败:未能提取到有效内容");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// ==================== 工作流API调用记录相关方法 ====================
|
||||
|
||||
/**
|
||||
* 创建工作流API调用记录
|
||||
*
|
||||
* @param config AI配置
|
||||
* @param configKey 配置键
|
||||
* @param userInput 用户输入
|
||||
* @param userId 用户ID
|
||||
* @return API调用记录
|
||||
*/
|
||||
private CozeApiCall createWorkflowApiCallRecord(AiConfig config, String configKey, String userInput, String userId) {
|
||||
CozeApiCall apiCall = CozeApiCall.builder()
|
||||
.id(snowflakeIdGenerator.nextIdAsString())
|
||||
.workflowId(config.getWorkflowId())
|
||||
.botId(config.getBotId())
|
||||
.userId(userId)
|
||||
.requestType("workflow")
|
||||
.userMessage(userInput)
|
||||
.userMessageType("text")
|
||||
.status("pending")
|
||||
.startTime(LocalDateTime.now())
|
||||
.traceId(java.util.UUID.randomUUID().toString().replace("-", ""))
|
||||
.metadata(JSON.toJSONString(java.util.Map.of("configKey", configKey)))
|
||||
.createBy(userId)
|
||||
.build();
|
||||
|
||||
// 保存API调用记录
|
||||
cozeApiCallService.save(apiCall);
|
||||
log.info("创建工作流API调用记录: id={}, workflowId={}, configKey={}, traceId={}",
|
||||
apiCall.getId(), config.getWorkflowId(), configKey, apiCall.getTraceId());
|
||||
|
||||
return apiCall;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新工作流API调用记录的请求信息
|
||||
*
|
||||
* @param apiCall API调用记录
|
||||
* @param requestUrl 请求URL
|
||||
* @param requestBody 请求体
|
||||
* @param headers 请求头
|
||||
*/
|
||||
private void updateWorkflowApiCallRequest(CozeApiCall apiCall, String requestUrl,
|
||||
Map<String, Object> requestBody, HttpHeaders headers) {
|
||||
try {
|
||||
apiCall.setRequestUrl(requestUrl);
|
||||
apiCall.setRequestBody(JSON.toJSONString(requestBody));
|
||||
// 脱敏处理请求头,移除Authorization中的token
|
||||
Map<String, String> safeHeaders = new HashMap<>();
|
||||
headers.toSingleValueMap().forEach((key, value) -> {
|
||||
if ("Authorization".equalsIgnoreCase(key)) {
|
||||
safeHeaders.put(key, "Bearer ***");
|
||||
} else {
|
||||
safeHeaders.put(key, value);
|
||||
}
|
||||
});
|
||||
apiCall.setRequestHeaders(JSON.toJSONString(safeHeaders));
|
||||
cozeApiCallService.updateById(apiCall);
|
||||
} catch (Exception e) {
|
||||
log.error("更新工作流API调用记录请求信息失败: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新工作流API调用记录的成功结果
|
||||
*
|
||||
* @param apiCall API调用记录
|
||||
* @param aiReply AI回复内容
|
||||
*/
|
||||
private void updateWorkflowApiCallSuccess(CozeApiCall apiCall, String aiReply) {
|
||||
try {
|
||||
LocalDateTime endTime = LocalDateTime.now();
|
||||
long durationMs = java.time.Duration.between(apiCall.getStartTime(), endTime).toMillis();
|
||||
|
||||
apiCall.setEndTime(endTime);
|
||||
apiCall.setDurationMs((int) durationMs);
|
||||
apiCall.setAiReply(aiReply);
|
||||
apiCall.setAiReplyType("text");
|
||||
apiCall.setStatus("success");
|
||||
apiCall.setFinalStatus("completed");
|
||||
apiCall.setResponseStatus(200);
|
||||
apiCall.setUpdateBy(apiCall.getUserId());
|
||||
|
||||
cozeApiCallService.updateById(apiCall);
|
||||
log.info("工作流API调用成功: id={}, duration={}ms, replyLength={}",
|
||||
apiCall.getId(), durationMs, aiReply != null ? aiReply.length() : 0);
|
||||
} catch (Exception e) {
|
||||
log.error("更新工作流API调用记录成功结果失败: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新工作流API调用记录的错误信息
|
||||
*
|
||||
* @param apiCall API调用记录
|
||||
* @param errorCode 错误代码
|
||||
* @param errorMessage 错误信息
|
||||
*/
|
||||
private void updateWorkflowApiCallError(CozeApiCall apiCall, String errorCode, String errorMessage) {
|
||||
try {
|
||||
LocalDateTime endTime = LocalDateTime.now();
|
||||
long durationMs = java.time.Duration.between(apiCall.getStartTime(), endTime).toMillis();
|
||||
|
||||
apiCall.setEndTime(endTime);
|
||||
apiCall.setDurationMs((int) durationMs);
|
||||
apiCall.setStatus("failed");
|
||||
apiCall.setFinalStatus("failed");
|
||||
apiCall.setErrorCode(errorCode);
|
||||
apiCall.setErrorMessage(errorMessage);
|
||||
apiCall.setUpdateBy(apiCall.getUserId());
|
||||
|
||||
cozeApiCallService.updateById(apiCall);
|
||||
log.error("工作流API调用失败: id={}, errorCode={}, errorMessage={}",
|
||||
apiCall.getId(), errorCode, errorMessage);
|
||||
} catch (Exception e) {
|
||||
log.error("更新工作流API调用记录错误信息失败: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理Coze工作流流式响应(带API调用记录)
|
||||
*
|
||||
* @param url API URL
|
||||
* @param headers 请求头
|
||||
* @param requestBody 请求体
|
||||
* @param config AI配置
|
||||
* @param apiCall API调用记录
|
||||
* @return 提取的output内容
|
||||
*/
|
||||
private String handleWorkflowStreamResponseWithRecord(String url, HttpHeaders headers,
|
||||
Map<String, Object> requestBody, AiConfig config, CozeApiCall apiCall) {
|
||||
try {
|
||||
log.info("开始处理工作流流式响应,URL: {}, apiCallId: {}", url, apiCall.getId());
|
||||
|
||||
// 获取超时配置
|
||||
int timeoutMs = config.getTimeoutMs() != null ? config.getTimeoutMs() : 30000;
|
||||
|
||||
// 创建HTTP客户端
|
||||
java.net.http.HttpClient client = java.net.http.HttpClient.newBuilder()
|
||||
.connectTimeout(java.time.Duration.ofMillis(timeoutMs))
|
||||
.build();
|
||||
|
||||
// 构建请求
|
||||
java.net.http.HttpRequest.Builder requestBuilder = java.net.http.HttpRequest.newBuilder()
|
||||
.uri(java.net.URI.create(url))
|
||||
.timeout(java.time.Duration.ofMillis(timeoutMs * 2))
|
||||
.POST(java.net.http.HttpRequest.BodyPublishers.ofString(JSON.toJSONString(requestBody)));
|
||||
|
||||
// 添加请求头
|
||||
headers.forEach((key, values) -> {
|
||||
if (values != null && !values.isEmpty()) {
|
||||
requestBuilder.header(key, values.get(0));
|
||||
}
|
||||
});
|
||||
|
||||
java.net.http.HttpRequest request = requestBuilder.build();
|
||||
|
||||
// 发送请求并处理流式响应
|
||||
java.net.http.HttpResponse<java.util.stream.Stream<String>> response = client.send(request,
|
||||
java.net.http.HttpResponse.BodyHandlers.ofLines());
|
||||
|
||||
log.info("工作流流式响应状态码: {}", response.statusCode());
|
||||
|
||||
// 更新响应状态码
|
||||
apiCall.setResponseStatus(response.statusCode());
|
||||
|
||||
if (response.statusCode() != 200) {
|
||||
String errorBody = response.body().collect(java.util.stream.Collectors.joining("\n"));
|
||||
log.error("工作流请求失败,状态码: {}, 响应: {}", response.statusCode(), errorBody);
|
||||
apiCall.setResponseBody(errorBody);
|
||||
cozeApiCallService.updateById(apiCall);
|
||||
throw new RuntimeException("工作流请求失败,状态码: " + response.statusCode());
|
||||
}
|
||||
|
||||
// 解析SSE流式响应并记录原始响应
|
||||
return parseWorkflowSseResponseWithRecord(response.body(), apiCall);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理工作流流式响应失败: {}", e.getMessage(), e);
|
||||
updateWorkflowApiCallError(apiCall, "STREAM_ERROR", e.getMessage());
|
||||
throw new RuntimeException("处理工作流响应失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析Coze工作流SSE响应(带API调用记录)
|
||||
*
|
||||
* @param lines 响应行流
|
||||
* @param apiCall API调用记录
|
||||
* @return 提取的output内容
|
||||
*/
|
||||
private String parseWorkflowSseResponseWithRecord(java.util.stream.Stream<String> lines, CozeApiCall apiCall) {
|
||||
StringBuilder resultBuilder = new StringBuilder();
|
||||
StringBuilder fullStreamData = new StringBuilder();
|
||||
String currentEvent = null;
|
||||
|
||||
java.util.Iterator<String> lineIterator = lines.iterator();
|
||||
while (lineIterator.hasNext()) {
|
||||
String line = lineIterator.next();
|
||||
fullStreamData.append(line).append("\n");
|
||||
|
||||
if (line.trim().isEmpty()) {
|
||||
currentEvent = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 解析event行
|
||||
if (line.startsWith("event:")) {
|
||||
currentEvent = line.substring(6).trim();
|
||||
log.debug("工作流事件类型: {}", currentEvent);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 解析data行
|
||||
if (line.startsWith("data:")) {
|
||||
String data = line.substring(5).trim();
|
||||
|
||||
// 检查是否为结束标记
|
||||
if ("\"[DONE]\"".equals(data) || "[DONE]".equals(data)) {
|
||||
log.info("收到工作流响应结束标记");
|
||||
break;
|
||||
}
|
||||
|
||||
if (data.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 处理Message事件
|
||||
if ("Message".equals(currentEvent)) {
|
||||
try {
|
||||
JSONObject jsonData = JSON.parseObject(data);
|
||||
String nodeType = jsonData.getString("node_type");
|
||||
|
||||
// 只处理End节点的内容
|
||||
if ("End".equals(nodeType)) {
|
||||
String content = jsonData.getString("content");
|
||||
if (content != null && !content.trim().isEmpty()) {
|
||||
// 从content中提取output
|
||||
String output = extractOutputFromContent(content);
|
||||
if (output != null) {
|
||||
resultBuilder.append(output);
|
||||
log.info("成功提取工作流output内容,长度: {}", output.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("解析工作流Message数据失败: {}, 数据: {}", e.getMessage(), data);
|
||||
}
|
||||
}
|
||||
// 处理Done事件
|
||||
else if ("Done".equals(currentEvent)) {
|
||||
log.info("工作流执行完成");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 保存原始响应数据到API调用记录
|
||||
try {
|
||||
apiCall.setResponseBody(fullStreamData.toString());
|
||||
cozeApiCallService.updateById(apiCall);
|
||||
} catch (Exception e) {
|
||||
log.error("保存工作流响应数据失败: {}", e.getMessage(), e);
|
||||
}
|
||||
|
||||
String result = resultBuilder.toString();
|
||||
if (result.isEmpty()) {
|
||||
log.warn("工作流响应解析完成但内容为空,原始数据: {}", fullStreamData);
|
||||
throw new RuntimeException("AI响应解析失败:未能提取到有效内容");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ 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.AiConfigTestUpdateRequest;
|
||||
import com.emotion.dto.request.aiconfig.AiConfigUpdateRequest;
|
||||
import com.emotion.dto.response.aiconfig.AiConfigResponse;
|
||||
import com.emotion.entity.AiConfig;
|
||||
@@ -264,6 +265,7 @@ public class AiConfigServiceImpl extends ServiceImpl<AiConfigMapper, AiConfig> i
|
||||
public AiConfig getByConfigKey(String configKey) {
|
||||
LambdaQueryWrapper<AiConfig> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(AiConfig::getConfigKey, configKey);
|
||||
wrapper.eq(AiConfig::getIsEnabled, 1);
|
||||
return this.getOne(wrapper);
|
||||
}
|
||||
|
||||
@@ -418,4 +420,60 @@ public class AiConfigServiceImpl extends ServiceImpl<AiConfigMapper, AiConfig> i
|
||||
return AiConfig::getCreateTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AiConfigResponse updateFromTestRequest(AiConfigTestUpdateRequest request) {
|
||||
// 查询现有配置
|
||||
AiConfig aiConfig = this.getById(request.getId());
|
||||
if (aiConfig == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 更新字段
|
||||
if (StringUtils.hasText(request.getApiBaseUrl())) {
|
||||
aiConfig.setApiBaseUrl(request.getApiBaseUrl());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(request.getApiToken())) {
|
||||
aiConfig.setApiToken(request.getApiToken());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(request.getClientId())) {
|
||||
aiConfig.setClientId(request.getClientId());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(request.getClientSecret())) {
|
||||
aiConfig.setClientSecret(request.getClientSecret());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(request.getGrantType())) {
|
||||
aiConfig.setGrantType(request.getGrantType());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(request.getBotId())) {
|
||||
aiConfig.setBotId(request.getBotId());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(request.getWorkflowId())) {
|
||||
aiConfig.setWorkflowId(request.getWorkflowId());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(request.getCustomHeaders())) {
|
||||
aiConfig.setCustomHeaders(request.getCustomHeaders());
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(request.getCustomParams())) {
|
||||
aiConfig.setCustomParams(request.getCustomParams());
|
||||
}
|
||||
|
||||
if (request.getSupportStream() != null) {
|
||||
aiConfig.setSupportStream(request.getSupportStream());
|
||||
}
|
||||
|
||||
// 保存更新
|
||||
this.updateById(aiConfig);
|
||||
|
||||
// 返回响应对象
|
||||
return convertToResponse(aiConfig);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,11 @@ import com.emotion.dto.request.EpicScriptUpdateRequest;
|
||||
import com.emotion.dto.response.EpicScriptResponse;
|
||||
import com.emotion.entity.EpicScript;
|
||||
import com.emotion.mapper.EpicScriptMapper;
|
||||
import com.emotion.service.AiChatService;
|
||||
import com.emotion.service.EpicScriptService;
|
||||
import com.emotion.service.LifePathService;
|
||||
import com.emotion.util.UserContextHolder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
@@ -21,6 +23,7 @@ import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -29,16 +32,25 @@ import java.util.stream.Collectors;
|
||||
* @author huazhongmin
|
||||
* @date 2025-12-22
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class EpicScriptServiceImpl extends ServiceImpl<EpicScriptMapper, EpicScript>
|
||||
public class EpicScriptServiceImpl extends ServiceImpl<EpicScriptMapper, EpicScript>
|
||||
implements EpicScriptService {
|
||||
|
||||
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
/**
|
||||
* Coze工作流配置键 - 爽文剧本生成
|
||||
*/
|
||||
private static final String COZE_EPIC_SCRIPT_CONFIG_KEY = "coze.course.life.generate";
|
||||
|
||||
@Autowired
|
||||
@Lazy
|
||||
private LifePathService lifePathService;
|
||||
|
||||
@Autowired
|
||||
private AiChatService aiChatService;
|
||||
|
||||
@Override
|
||||
public PageResult<EpicScriptResponse> getPageByCurrentUser(EpicScriptPageRequest request) {
|
||||
String currentUserId = UserContextHolder.getCurrentUserId();
|
||||
@@ -138,10 +150,140 @@ public class EpicScriptServiceImpl extends ServiceImpl<EpicScriptMapper, EpicScr
|
||||
script.setPlotJson(request.getPlotJson());
|
||||
script.setIsSelected(request.getIsSelected() != null && request.getIsSelected() ? 1 : 0);
|
||||
|
||||
// 调用Coze AI生成剧本内容
|
||||
String aiGeneratedContent = generateScriptByAi(request, currentUserId);
|
||||
if (aiGeneratedContent != null) {
|
||||
// 将AI生成的内容存储到plotJson中
|
||||
Map<String, Object> plotJson = script.getPlotJson();
|
||||
if (plotJson == null) {
|
||||
plotJson = new java.util.HashMap<>();
|
||||
}
|
||||
plotJson.put("aiGeneratedContent", aiGeneratedContent);
|
||||
script.setPlotJson(plotJson);
|
||||
log.info("AI生成剧本内容成功,用户ID: {}, 内容长度: {}", currentUserId, aiGeneratedContent.length());
|
||||
}
|
||||
|
||||
this.save(script);
|
||||
return convertToResponse(script);
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用Coze AI生成爽文剧本内容
|
||||
*
|
||||
* @param request 剧本创建请求
|
||||
* @param userId 用户ID
|
||||
* @return AI生成的剧本内容,失败时返回null
|
||||
*/
|
||||
private String generateScriptByAi(EpicScriptCreateRequest request, String userId) {
|
||||
try {
|
||||
// 组装AI输入
|
||||
String input = assembleScriptInput(request);
|
||||
log.info("开始调用AI生成剧本,用户ID: {}, 输入长度: {}", userId, input.length());
|
||||
|
||||
// 调用Coze工作流
|
||||
String result = aiChatService.callWorkflowByConfigKey(COZE_EPIC_SCRIPT_CONFIG_KEY, input, userId);
|
||||
|
||||
log.info("AI生成剧本完成,用户ID: {}, 结果长度: {}", userId, result != null ? result.length() : 0);
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("AI生成剧本失败,用户ID: {}, 错误: {}", userId, e.getMessage(), e);
|
||||
// AI调用失败不影响剧本创建,返回null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装AI输入内容
|
||||
* 将EpicScriptCreateRequest的字段组装为格式化字符串
|
||||
*
|
||||
* @param request 剧本创建请求
|
||||
* @return 格式化的输入字符串
|
||||
*/
|
||||
private String assembleScriptInput(EpicScriptCreateRequest request) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
// 标题
|
||||
if (StringUtils.hasText(request.getTitle())) {
|
||||
sb.append("【剧本标题】").append(request.getTitle()).append("\n");
|
||||
}
|
||||
|
||||
// 主题/渴望
|
||||
if (StringUtils.hasText(request.getTheme())) {
|
||||
sb.append("【主题渴望】").append(request.getTheme()).append("\n");
|
||||
}
|
||||
|
||||
// 风格
|
||||
if (StringUtils.hasText(request.getStyle())) {
|
||||
String styleDesc = getStyleDescription(request.getStyle());
|
||||
sb.append("【剧本风格】").append(styleDesc).append("\n");
|
||||
}
|
||||
|
||||
// 篇幅
|
||||
if (StringUtils.hasText(request.getLength())) {
|
||||
String lengthDesc = getLengthDescription(request.getLength());
|
||||
sb.append("【篇幅长度】").append(lengthDesc).append("\n");
|
||||
}
|
||||
|
||||
// 序幕:低谷回响
|
||||
if (StringUtils.hasText(request.getPlotIntro())) {
|
||||
sb.append("【序幕-低谷回响】").append(request.getPlotIntro()).append("\n");
|
||||
}
|
||||
|
||||
// 转折:契机出现
|
||||
if (StringUtils.hasText(request.getPlotTurning())) {
|
||||
sb.append("【转折-契机出现】").append(request.getPlotTurning()).append("\n");
|
||||
}
|
||||
|
||||
// 高潮:命运抉择
|
||||
if (StringUtils.hasText(request.getPlotClimax())) {
|
||||
sb.append("【高潮-命运抉择】").append(request.getPlotClimax()).append("\n");
|
||||
}
|
||||
|
||||
// 结局:新的开始
|
||||
if (StringUtils.hasText(request.getPlotEnding())) {
|
||||
sb.append("【结局-新的开始】").append(request.getPlotEnding()).append("\n");
|
||||
}
|
||||
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取风格描述
|
||||
*
|
||||
* @param style 风格代码
|
||||
* @return 风格描述
|
||||
*/
|
||||
private String getStyleDescription(String style) {
|
||||
switch (style) {
|
||||
case "career":
|
||||
return "职场逆袭";
|
||||
case "love":
|
||||
return "情感圆满";
|
||||
case "fantasy":
|
||||
return "玄幻觉醒";
|
||||
default:
|
||||
return style;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取篇幅描述
|
||||
*
|
||||
* @param length 篇幅代码
|
||||
* @return 篇幅描述
|
||||
*/
|
||||
private String getLengthDescription(String length) {
|
||||
switch (length) {
|
||||
case "medium":
|
||||
return "标准篇";
|
||||
case "long":
|
||||
return "长篇";
|
||||
default:
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EpicScriptResponse updateScript(EpicScriptUpdateRequest request) {
|
||||
EpicScript script = this.getById(request.getId());
|
||||
|
||||
Reference in New Issue
Block a user