From 02f7031a6761ce68d20210672f0a5f19a006aed2 Mon Sep 17 00:00:00 2001 From: litianxiang Date: Mon, 13 Oct 2025 13:42:38 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=85=8D=E7=BD=AE=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E6=A8=A1=E7=89=B9=E5=8A=9F=E8=83=BD=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=20fix=EF=BC=9A=E6=A8=A1=E7=89=B9=E6=9C=89=E6=97=B6=E4=BC=9A?= =?UTF-8?q?=E6=B2=A1=E6=9C=89=E8=AE=BE=E7=BD=AE=E6=88=90=E5=8A=9F=E6=80=A7?= =?UTF-8?q?=E5=88=AB=20=E8=B1=86=E5=8C=85qwen=E6=A8=A1=E5=9E=8B=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E8=BF=9D=E8=A7=84=E6=8F=90=E7=A4=BA=E8=AF=8D=E6=8F=90?= =?UTF-8?q?=E9=86=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../primary/entity/CollectionElement.java | 2 +- .../impl/CollectionElementServiceImpl.java | 40 ++- .../da/service/impl/GenerateServiceImpl.java | 334 +++++++++++++++--- .../ai/da/service/impl/LLMServiceImpl.java | 2 + .../da/service/impl/WorkspaceServiceImpl.java | 6 +- 5 files changed, 308 insertions(+), 76 deletions(-) diff --git a/src/main/java/com/ai/da/mapper/primary/entity/CollectionElement.java b/src/main/java/com/ai/da/mapper/primary/entity/CollectionElement.java index 8b233808..40ef64b8 100644 --- a/src/main/java/com/ai/da/mapper/primary/entity/CollectionElement.java +++ b/src/main/java/com/ai/da/mapper/primary/entity/CollectionElement.java @@ -53,7 +53,7 @@ public class CollectionElement implements Serializable { private String level2Type; /** - * 三级类型 目前只为线稿标注性别 + * 三级类型 目前为线稿和模特标注性别 */ @TableField("level3_type") private String level3Type; diff --git a/src/main/java/com/ai/da/service/impl/CollectionElementServiceImpl.java b/src/main/java/com/ai/da/service/impl/CollectionElementServiceImpl.java index 6acff360..741b5e2c 100644 --- a/src/main/java/com/ai/da/service/impl/CollectionElementServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/CollectionElementServiceImpl.java @@ -579,24 +579,28 @@ public class CollectionElementServiceImpl extends ServiceImpl sketchIterator = designDTO.getSketchBoards().iterator(); while (sketchIterator.hasNext()) { - CollectionSketchDTO sketchBoard = sketchIterator.next(); - String level2Type = sketchBoard.getLevel2Type(); - String level3Type = null; - if ("Collection".equals(sketchBoard.getDesignType())) { - level3Type = collectionElementMapper.selectOne(new LambdaQueryWrapper().eq(CollectionElement::getId, sketchBoard.getSketchBoardId()).select(CollectionElement::getLevel3Type)).getLevel3Type(); - } else if ("Library".equals(sketchBoard.getDesignType())){ - level3Type = libraryService.getById(sketchBoard.getSketchBoardId()).getLevel3Type(); - } else if ("Generate".equals(sketchBoard.getDesignType())){ - GenerateDetail generateDetail = generateDetailMapper.selectById(sketchBoard.getSketchBoardId()); - assert generateDetail != null; - Generate generate = generateMapper.selectById(generateDetail.getGenerateId()); - assert generate != null; - level3Type = GenerateServiceImpl.extractGender(generate.getGenerateType()); - } - //判断性别和当前project性别是否一致,不一致则移除 - assert level3Type != null; - if (!level3Type.equalsIgnoreCase(designDTO.getModelSex())) { - sketchIterator.remove(); + try { + CollectionSketchDTO sketchBoard = sketchIterator.next(); + String level2Type = sketchBoard.getLevel2Type(); + String level3Type = null; + if ("Collection".equals(sketchBoard.getDesignType())) { + level3Type = collectionElementMapper.selectOne(new LambdaQueryWrapper().eq(CollectionElement::getId, sketchBoard.getSketchBoardId()).select(CollectionElement::getLevel3Type)).getLevel3Type(); + } else if ("Library".equals(sketchBoard.getDesignType())){ + level3Type = libraryService.getById(sketchBoard.getSketchBoardId()).getLevel3Type(); + } else if ("Generate".equals(sketchBoard.getDesignType())){ + GenerateDetail generateDetail = generateDetailMapper.selectById(sketchBoard.getSketchBoardId()); + assert generateDetail != null; + Generate generate = generateMapper.selectById(generateDetail.getGenerateId()); + assert generate != null; + level3Type = GenerateServiceImpl.extractGender(generate.getGenerateType()); + } + //判断性别和当前project性别是否一致,不一致则移除 + assert level3Type != null; + if (!level3Type.equalsIgnoreCase(designDTO.getModelSex())) { + sketchIterator.remove(); + } + } catch (Exception e) { + continue; } } diff --git a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java index 08387744..51282147 100644 --- a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java @@ -31,6 +31,7 @@ import com.alibaba.dashscope.utils.JsonUtils; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.serializer.SerializerFeature; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException; @@ -463,7 +464,7 @@ public class GenerateServiceImpl extends ServiceImpl i case "Sketchboard": // text = "clear lines, simple outlines monochrome white vector image of " + translated + ", no background, sketch flat, front view display, best quality, ultra-high resolution 8k"; text = "a single item of sketch of " + translated + ", 4k, white background"; - if (Objects.nonNull(generate) && generate.getModelName().equals("high")){ + if (Objects.nonNull(generate) && generate.getModelName().equals("high")) { text = text + ", only technical pattern of a single garment"; } if (!StringUtil.isNullOrEmpty(ageGroup) && ageGroup.equals("Child")) { @@ -619,7 +620,7 @@ public class GenerateServiceImpl extends ServiceImpl i } String modelName = generateDTO.getModelName(); - if (StringUtil.isNullOrEmpty(modelName)){ + if (StringUtil.isNullOrEmpty(modelName)) { return handleStandardGeneration(generateDTO); } @@ -674,12 +675,14 @@ public class GenerateServiceImpl extends ServiceImpl i // processCreditDeduction(generateDTO.getUserId(), taskId, CreditsEventsEnum.WX_TEXT2IMG); return new PrepareForGenerateVO(Collections.singletonList(taskId), 200); } + public String toProductAsyncTask(String imagePath, String useModel, String prompt) { + long startTime = System.currentTimeMillis(); - log.info("开始执行toProductAsyncTask - model: {}, imagePath: {}, prompt: {}", + log.info("开始执行toProductAsyncTask - model: {}, imagePath: {}, prompt: {}", useModel, imagePath, prompt); - if (StringUtil.isNullOrEmpty(imagePath)||StringUtil.isNullOrEmpty(useModel)||StringUtil.isNullOrEmpty(prompt)){ + if (StringUtil.isNullOrEmpty(imagePath) || StringUtil.isNullOrEmpty(useModel) || StringUtil.isNullOrEmpty(prompt)) { log.error("参数验证失败 - imagePath: {}, useModel: {}, prompt: {}", imagePath, useModel, prompt); throw new BusinessException("Parameter Exception"); } @@ -694,10 +697,10 @@ public class GenerateServiceImpl extends ServiceImpl i finalImagePath = addWhiteBackground(imagePath); //去掉"data:image/png;base64," finalImagePath = finalImagePath.replace("data:image/png;base64,", ""); - // 如果白色背景处理失败或不需要,直接获取原图的base64编码 - if (StringUtil.isNullOrEmpty(finalImagePath)) { - finalImagePath = minioUtil.getImageAsBase64(imagePath); - } + // 如果白色背景处理失败或不需要,直接获取原图的base64编码 + if (StringUtil.isNullOrEmpty(finalImagePath)) { + finalImagePath = minioUtil.getImageAsBase64(imagePath); + } } catch (IOException e) { log.error("Error getting image as base64 taskId: {} ", taskId, e); throw new BusinessException("Parameter Exception"); @@ -791,15 +794,15 @@ public class GenerateServiceImpl extends ServiceImpl i int maxRetries = 3; int retryDelay = 2000; // 2秒 boolean success = false; - + for (int attempt = 1; attempt <= maxRetries && !success; attempt++) { try { log.info("发起HTTP请求 - 尝试次数: {}/{}, URL: {}, taskId: {}", attempt, maxRetries, request.url(), taskId); long requestStartTime = System.currentTimeMillis(); - + try (Response response = client.newCall(request).execute()) { long requestEndTime = System.currentTimeMillis(); - log.info("HTTP请求完成 - 响应状态: {}, 耗时: {}ms, taskId: {}", + log.info("HTTP请求完成 - 响应状态: {}, 耗时: {}ms, taskId: {}", response.code(), (requestEndTime - requestStartTime), taskId); if (!response.isSuccessful()) { log.warn("Google API响应失败,状态码: {} for taskId: {}", response.code(), taskId); @@ -810,7 +813,7 @@ public class GenerateServiceImpl extends ServiceImpl i throw new IOException("HTTP error code: " + response.code()); } } - + String result = response.body().string(); // log.info("Google 响应结果:{}", result); com.alibaba.fastjson.JSONObject jsonResponse = JSON.parseObject(result); @@ -830,7 +833,7 @@ public class GenerateServiceImpl extends ServiceImpl i for (int i = 0; i < parts.size(); i++) { com.alibaba.fastjson.JSONObject part = parts.getJSONObject(i); if (part.containsKey("inlineData")) { - com.alibaba.fastjson.JSONObject inlineDataResult= part.getJSONObject("inlineData"); + com.alibaba.fastjson.JSONObject inlineDataResult = part.getJSONObject("inlineData"); base64Data = inlineDataResult.getString("data"); break; } @@ -855,11 +858,11 @@ public class GenerateServiceImpl extends ServiceImpl i } } } catch (IOException e) { - log.warn("网络连接问题 - 异常类型: {}, 尝试: {}/{}, taskId: {}, 错误详情: {}", + log.warn("网络连接问题 - 异常类型: {}, 尝试: {}/{}, taskId: {}, 错误详情: {}", e.getClass().getSimpleName(), attempt, maxRetries, taskId, e.getMessage()); if (attempt < maxRetries) { int delayMs = retryDelay * attempt; - log.info("准备重试 - 延迟: {}ms, 下次尝试: {}/{}, taskId: {}", + log.info("准备重试 - 延迟: {}ms, 下次尝试: {}/{}, taskId: {}", delayMs, (attempt + 1), maxRetries, taskId); try { Thread.sleep(delayMs); // 递增延迟重试 @@ -881,15 +884,14 @@ public class GenerateServiceImpl extends ServiceImpl i redisUtil.addToString(key, new Gson().toJson(failResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); } }, asyncTaskExecutor); - + long endTime = System.currentTimeMillis(); log.info("toProductAsyncTask执行完成 - taskId: {}, 总耗时: {}ms", taskId, (endTime - startTime)); return taskId; } - - public String createGoogleAsyncTask(GenerateThroughImageTextDTO generateDTO, String useModel, String prompt) { + public String createGoogleAsyncTask(GenerateThroughImageTextDTO generateDTO, String useModel, String prompt) { // 从 resources 加载 JSON 文件 String level1Type = generateDTO.getLevel1Type(); String level2Type = generateDTO.getLevel2Type(); @@ -987,6 +989,7 @@ public class GenerateServiceImpl extends ServiceImpl i // imagen 模型的参数 parametersObj.set("addWatermark", false); parametersObj.set("sampleCount", 1); + parametersObj.set("aspectRatio", "9:16"); // 构建完整的请求体 requestBody.set("instances", Arrays.asList(instanceObj)); @@ -1009,14 +1012,14 @@ public class GenerateServiceImpl extends ServiceImpl i CompletableFuture.runAsync(() -> { long startTime = System.currentTimeMillis(); log.info("开始执行 createGoogleAsyncTask 方法,taskId: {}, model: {}, prompt: {}", taskId, useModel, prompt); - + final int maxRetries = 3; final int retryDelayMs = 2000; - + for (int attempt = 1; attempt <= maxRetries; attempt++) { try { log.info("Google API 调用尝试 {}/{} - taskId: {}", attempt, maxRetries, taskId); - + // 异步获取token String tokenValue = null; try (InputStream inputStream = GenerateServiceImpl.class.getClassLoader() @@ -1042,7 +1045,7 @@ public class GenerateServiceImpl extends ServiceImpl i .callTimeout(180, TimeUnit.SECONDS) .retryOnConnectionFailure(true) .build(); - + Request request = new Request.Builder() .url(endpoint) .addHeader("Authorization", "Bearer " + tokenValue) @@ -1055,9 +1058,9 @@ public class GenerateServiceImpl extends ServiceImpl i long requestStartTime = System.currentTimeMillis(); try (Response response = client.newCall(request).execute()) { long requestDuration = System.currentTimeMillis() - requestStartTime; - log.info("Google API 请求完成 - taskId: {}, 尝试: {}, URL: {}, 状态码: {}, 耗时: {}ms", + log.info("Google API 请求完成 - taskId: {}, 尝试: {}, URL: {}, 状态码: {}, 耗时: {}ms", taskId, attempt, endpoint, response.code(), requestDuration); - + String result = response.body().string(); // log.info("Google 响应结果:{}", result); @@ -1098,7 +1101,7 @@ public class GenerateServiceImpl extends ServiceImpl i // 生成成功,更新Redis状态和URL GenerateResultVO successResultVO = new GenerateResultVO(taskId, null, minioPath, "Success"); redisUtil.addToString(key, new Gson().toJson(successResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - + long totalDuration = System.currentTimeMillis() - startTime; log.info("Google 图片生成成功 - taskId: {}, 总耗时: {}ms", taskId, totalDuration); return; // 成功后立即返回 @@ -1112,9 +1115,9 @@ public class GenerateServiceImpl extends ServiceImpl i } } catch (IOException e) { // 只对 IOException 进行重试 - log.warn("Google API 调用发生 IOException - taskId: {}, 尝试: {}/{}, 异常: {}", + log.warn("Google API 调用发生 IOException - taskId: {}, 尝试: {}/{}, 异常: {}", taskId, attempt, maxRetries, e.getMessage()); - + if (attempt < maxRetries) { try { long delay = retryDelayMs * attempt; // 递增延迟 @@ -1133,14 +1136,14 @@ public class GenerateServiceImpl extends ServiceImpl i } } catch (Exception e) { // 对于其他异常(非IOException),记录错误并直接返回,不重试 - log.error("Google API 调用发生意外异常 - taskId: {}, 异常类型: {}, 异常信息: {}", + log.error("Google API 调用发生意外异常 - taskId: {}, 异常类型: {}, 异常信息: {}", taskId, e.getClass().getSimpleName(), e.getMessage(), e); GenerateResultVO failResultVO = new GenerateResultVO(taskId, null, null, "Fail"); redisUtil.addToString(key, new Gson().toJson(failResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); return; // 非网络异常,直接返回不重试 } } - + // 如果所有重试都失败了,记录最终失败日志 long totalDuration = System.currentTimeMillis() - startTime; log.error("Google API 调用最终失败,所有重试都已用尽 - taskId: {}, 总耗时: {}ms", taskId, totalDuration); @@ -1154,15 +1157,14 @@ public class GenerateServiceImpl extends ServiceImpl i taskId, level1Type, level2Type, - prompt, + generateDTO.getText(), "text(" + gender + ")", useModel, // 记录使用的具体模型名称 new Date() ); save(generate); - - + return taskId; } @@ -1256,7 +1258,7 @@ public class GenerateServiceImpl extends ServiceImpl i && StringUtil.isNullOrEmpty(generateDTO.getLevel2Type())) { throw new BusinessException("level2Type.cannot.be.empty"); } - if (generateDTO.getLevel1Type().equals(PRINT_BOARD.getRealName())&&generateDTO.getLevel2Type().equals(CreditsEventsEnum.PATTERN.getName())) { + if (generateDTO.getLevel1Type().equals(PRINT_BOARD.getRealName()) && generateDTO.getLevel2Type().equals(CreditsEventsEnum.PATTERN.getName())) { int firstCommaIndex = generateDTO.getText().indexOf(","); String style = generateDTO.getText().substring(0, firstCommaIndex).trim(); //如果style不等于painting style,illustration style,real style中的一种 @@ -1335,16 +1337,37 @@ public class GenerateServiceImpl extends ServiceImpl i try { result = imageSynthesis.asyncCall(param); } catch (Exception e) { + + String exceptionMessage = e.getMessage(); + if (exceptionMessage != null && exceptionMessage.contains("\"message\"")) { + try { + // 解析JSON格式的异常信息 + JSONObject jsonObj = JSONUtil.parseObj(exceptionMessage); + exceptionMessage = jsonObj.getStr("message"); + if ("Input data may contain inappropriate content.".equals(exceptionMessage)){ + LambdaQueryWrapper select = new LambdaQueryWrapper().eq(Account::getId, userId).select(Account::getLanguage); + Account account = accountService.getOne(select); + if ("CHINESE_SIMPLIFIED".equals(account.getLanguage())){ + exceptionMessage = "输入数据可能包含不当内容。"; + } + } + throw new RuntimeException(exceptionMessage); + } catch (Exception parseException) { + // 如果解析失败,返回原始消息 + throw new RuntimeException(exceptionMessage); + } + } throw new RuntimeException(e.getMessage()); } String taskId = result.getOutput().getTaskId(); log.info("qwen text2image 请求生成:{}, taskId:{}", JsonUtils.toJson(result), taskId); - Generate generate = new Generate(userId, taskId, level1Type, level2Type, prompt, "text(" + gender + ")", "qwen-image", new Date()); + Generate generate = new Generate(userId, taskId, level1Type, level2Type, generateDTO.getText(), "text(" + gender + ")", "qwen-image", new Date()); save(generate); return taskId; } + public String createDouBaoAsyncTask(GenerateThroughImageTextDTO generateDTO, String useModel, String prompt) { // 从DTO中获取基础参数 String level1Type = generateDTO.getLevel1Type(); @@ -1393,6 +1416,19 @@ public class GenerateServiceImpl extends ServiceImpl i requestBuilder.image(finalImagePath1); } + // 保存生成记录到数据库 + Generate generate = new Generate( + userId, + taskId, + level1Type, + level2Type, + generateDTO.getText(), + "text(" + gender + ")", + useModel, // 记录使用的具体模型名称 + new Date() + ); + save(generate); + GenerateImagesRequest generateRequest = requestBuilder.build(); ImagesResponse imagesResponse = service.generateImages(generateRequest); @@ -1404,27 +1440,88 @@ public class GenerateServiceImpl extends ServiceImpl i GenerateResultVO successResultVO = new GenerateResultVO(taskId, null, imageUrl, "Success"); redisUtil.addToString(key, new Gson().toJson(successResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - // 保存生成记录到数据库 - Generate generate = new Generate( - userId, - taskId, - level1Type, - level2Type, - prompt, - "text(" + gender + ")", - useModel, // 记录使用的具体模型名称 - new Date() - ); - save(generate); // TODO: 处理积分扣除逻辑 } catch (Exception e) { log.error("Doubao image generation failed for taskId: {}", taskId, e); - // 生成失败,更新Redis状态 - GenerateResultVO failResultVO = new GenerateResultVO(taskId, null, null, "Fail"); + LambdaQueryWrapper select = new LambdaQueryWrapper().eq(Account::getId, userId).select(Account::getLanguage); + Account account = accountService.getOne(select); + + // 根据用户语言设置默认错误信息 + String errorMessage = "图像生成失败,请稍后重试"; // 默认中文 + String userLanguage = "CHINESE_SIMPLIFIED"; // 默认语言 + + if (account != null && account.getLanguage() != null) { + userLanguage = account.getLanguage(); + if (account.getLanguage().equals("ENGLISH")) { + errorMessage = "Image generation failed, please try again later"; + } else if (account.getLanguage().equals("CHINESE_SIMPLIFIED")) { + errorMessage = "图像生成失败,请稍后重试"; + } + } + String errorCode = null; + + // 检查是否为ArkHttpException,提取code和detailMessage + try { + // 通过反射获取code字段 + java.lang.reflect.Field codeField = e.getClass().getDeclaredField("code"); + codeField.setAccessible(true); + Object codeValue = codeField.get(e); + if (codeValue != null) { + errorCode = codeValue.toString(); + } + + // 通过反射获取detailMessage字段 + e.printStackTrace(); + String detailMessageValue = e.getMessage(); + if (detailMessageValue != null) { + String detailMessage = detailMessageValue.toString(); + + // 根据错误码返回相应的用户友好错误信息 + errorMessage = getDoubaoErrorMessage(errorCode, detailMessage, userLanguage); + } + } catch (Exception reflectionException) { + reflectionException.printStackTrace(); + log.warn("Failed to extract error details from ArkHttpException: {}", reflectionException.getMessage()); + // 如果反射失败,尝试从异常消息中提取信息 + String exceptionMessage = e.getMessage(); + if (exceptionMessage != null) { + boolean isEnglish = "ENGLISH".equals(userLanguage); + if (exceptionMessage.contains("OutputImageSensitiveContentDetected")) { + errorMessage = isEnglish ? "Generated image may contain sensitive content, please try with different input" : "生成的图像可能包含敏感信息,请更换输入内容后重试"; + } else if (exceptionMessage.contains("InputTextSensitiveContentDetected")) { + errorMessage = isEnglish ? "Input text may contain sensitive content, please try again" : "输入文本可能包含敏感信息,请更换后重试"; + } else if (exceptionMessage.contains("InputImageSensitiveContentDetected")) { + errorMessage = isEnglish ? "Input image may contain sensitive content, please try again" : "输入图像可能包含敏感信息,请更换后重试"; + } else if (exceptionMessage.contains("SensitiveContentDetected")) { + errorMessage = isEnglish ? "Input content may contain sensitive information, please try again" : "输入内容可能包含敏感信息,请更换后重试"; + } else if (exceptionMessage.contains("MissingParameter")) { + errorMessage = isEnglish ? "Request parameters are incomplete, please check input" : "请求参数不完整,请检查输入内容"; + } else if (exceptionMessage.contains("InvalidParameter")) { + errorMessage = isEnglish ? "Request parameters are invalid, please check input format" : "请求参数无效,请检查输入格式"; + } else if (exceptionMessage.contains("InvalidImageURL")) { + errorMessage = isEnglish ? "Image format is incorrect or data is corrupted, please re-upload" : "图片格式不正确或数据损坏,请重新上传"; + } else if (exceptionMessage.contains("OutofContextError")) { + errorMessage = isEnglish ? "Input content is too long, please shorten text or image size" : "输入内容过长,请缩短文本或图片尺寸"; + } else if (exceptionMessage.contains("AuthenticationError")) { + errorMessage = isEnglish ? "Service authentication failed, please contact administrator" : "服务认证失败,请联系管理员"; + } else if (exceptionMessage.contains("TooManyRequests") || exceptionMessage.contains("429")) { + errorMessage = isEnglish ? "Too many requests, please try again later" : "请求过于频繁,请稍后重试"; + } else if (exceptionMessage.contains("InternalServerError") || exceptionMessage.contains("500")) { + errorMessage = isEnglish ? "Image generation service is temporarily unavailable, please try again later" : "图像生成服务暂时不可用,请稍后重试"; + } + } + } + + // 更新Redis状态为失败 + GenerateResultVO failResultVO = new GenerateResultVO(taskId, null, errorMessage, "Fail"); redisUtil.addToString(key, new Gson().toJson(failResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + // 记录详细错误信息用于调试 + log.error("Doubao API error - taskId: {}, errorCode: {}, errorMessage: {}", + taskId, errorCode, errorMessage); + } }); @@ -3238,6 +3335,12 @@ public class GenerateServiceImpl extends ServiceImpl i } else { throw new BusinessException("Unknown generate task"); } + } else if ("Fail".equals(cachedResult.getStatus())) { + // 获取失败原因 + String errorMessage = cachedResult.getUrl(); + if (errorMessage != null) { + throw new BusinessException(errorMessage, ResultEnum.PROMPT.getCode()); + } } // 4. 其他状态(如 Unknown),返回默认失败 @@ -3764,16 +3867,16 @@ public class GenerateServiceImpl extends ServiceImpl i /** * 接入flux模型,用于imageToSketch(sketch extract) || relighting || to product image * - * @param func 功能枚举名,指定使用flux模型的具体功能类型 - * @param prompt 用户输入的提示词,如果为空则使用默认提示词 - * @param imagePath 图片minio路径,作为输入图像的base64编码源 + * @param func 功能枚举名,指定使用flux模型的具体功能类型 + * @param prompt 用户输入的提示词,如果为空则使用默认提示词 + * @param imagePath 图片minio路径,作为输入图像的base64编码源 * @param childStyle 是否为儿童风格,影响提示词的构建 * @return 返回taskId,用于异步获取结果 */ public String flux(CreditsEventsEnum func, String prompt, String imagePath, boolean childStyle) { // Flux API的请求地址 String fluxRequestUrl = "https://api.bfl.ai/v1/flux-kontext-pro"; - + // 如果用户没有提供提示词,根据功能类型设置默认提示词 if (StringUtil.isNullOrEmpty(prompt)) { switch (func) { @@ -3807,7 +3910,7 @@ public class GenerateServiceImpl extends ServiceImpl i break; } } - + // 构建Flux API请求体 JSONObject requestBody = new JSONObject(); @@ -3829,7 +3932,7 @@ public class GenerateServiceImpl extends ServiceImpl i log.info("flux 请求入参:{}", requestBody); // 提示词不能为空的校验 if (prompt.isEmpty()) throw new BusinessException("test"); - + // 如果提供了输入图片路径,需要将图片转换为base64格式 if (!StringUtil.isNullOrEmpty(imagePath)) { try { @@ -3854,7 +3957,7 @@ public class GenerateServiceImpl extends ServiceImpl i String resp = sendRequestUtil.sendFluxPost(fluxRequestUrl, requestBody.toString()); JSONObject respObj = JSONUtil.parseObj(resp); log.info("flux 发起生成请求返回结果: {}", respObj); - + // 从响应中提取任务ID String taskId = respObj.getStr("id"); if (StringUtil.isNullOrEmpty(taskId)) { @@ -3869,7 +3972,7 @@ public class GenerateServiceImpl extends ServiceImpl i String key = RedisUtil.FLUX_POLLING_URL + taskId; // 将轮询URL存储到Redis中,设置过期时间 redisUtil.addToString(key, pollingUrl, CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - + // 添加到api_generate表中,以便之后对结果查询做补偿机制 apiGenerateService.addAPIGenerateRecordAsync(UserContext.getUserHolder().getId(), taskId, func.getName(), "flux", "Pending"); @@ -4019,4 +4122,123 @@ public class GenerateServiceImpl extends ServiceImpl i return null; } } + + /** + * 根据豆包错误码和详细信息返回用户友好的错误信息 + * + * @param errorCode 错误码 + * @param detailMessage 详细错误信息 + * @return 用户友好的错误信息 + */ + private String getDoubaoErrorMessage(String errorCode, String detailMessage, String userLanguage) { + boolean isEnglish = "ENGLISH".equals(userLanguage); + + if (errorCode == null) { + return isEnglish ? "Image generation failed, please try again later" : "图像生成失败,请稍后重试"; + } + + switch (errorCode) { + // === 用户输入问题(用户可以自行解决) === + + // 敏感内容检测 - 用户需要修改提示词 + case "SensitiveContentDetected": + case "SensitiveContentDetected.SevereViolation": + case "SensitiveContentDetected.Violence": + case "InputTextSensitiveContentDetected": + return isEnglish ? "Your prompt contains sensitive content. Please modify your description and try again." + : "您的提示词包含敏感内容,请修改描述后重试"; + case "InputImageSensitiveContentDetected": + return isEnglish ? "Your uploaded image contains sensitive content. Please use a different image and try again." + : "您上传的图片包含敏感内容,请更换图片后重试"; + case "InputVideoSensitiveContentDetected": + return isEnglish ? "Your uploaded video contains sensitive content. Please use a different video and try again." + : "您上传的视频包含敏感内容,请更换视频后重试"; + case "OutputTextSensitiveContentDetected": + case "OutputImageSensitiveContentDetected": + case "OutputVideoSensitiveContentDetected": + return isEnglish ? "The generated content may contain sensitive information. Please modify your prompt and try again." + : "生成的内容可能包含敏感信息,请修改提示词后重试"; + + // 图片格式问题 - 用户需要检查图片 + case "InvalidImageURL.EmptyURL": + return isEnglish ? "No image was uploaded. Please upload an image and try again." + : "未上传图片,请上传图片后重试"; + case "InvalidImageURL.InvalidFormat": + return isEnglish ? "The image format is not supported or the image is corrupted. Please upload a valid image (JPG, PNG, etc.) and try again." + : "图片格式不支持或图片已损坏,请上传有效的图片格式(JPG、PNG等)后重试"; + + // 内容长度问题 - 用户需要缩短内容 + case "OutofContextError": + return isEnglish ? "Your prompt and image content is too long. Please shorten your description or use a smaller image and try again." + : "您的提示词和图片内容过长,请缩短描述或使用更小的图片后重试"; + + // 请求频率问题 - 用户需要等待 + case "TooManyRequests": + return isEnglish ? "You are sending requests too frequently. Please wait a moment and try again." + : "您的请求过于频繁,请稍等片刻后重试"; + + // === 系统问题(需要联系管理员) === + + // 参数格式错误 - 系统问题 + case "MissingParameter": + case "InvalidParameter": + case "Duplicate.Tags.Key": + case "InvalidArgumentError": + case "InvalidArgumentError.UnknownRole": + case "InvalidArgumentError.InvalidImageDetail": + case "InvalidArgumentError.InvalidPixelLimit": + return isEnglish ? "System error: Invalid request parameters. Please contact the administrator." + : "系统错误:请求参数格式有误,请联系管理员"; + + // 认证问题 - 系统问题 + case "AuthenticationError": + return isEnglish ? "System error: Authentication failed. Please contact the administrator." + : "系统错误:身份验证失败,请联系管理员"; + + // 服务配置问题 - 系统问题 + case "InvalidEndpoint.ClosedEndpoint": + case "OperationDenied.InvalidState": + case "OperationDenied.ConflictedValidationSet": + case "NotFound.Model": + case "NotFound.Endpoint": + case "NotFound.Context": + case "InvalidOperation.Conflict": + case "ModelNotOpen": + return isEnglish ? "System error: Service configuration issue. Please contact the administrator." + : "系统错误:服务配置问题,请联系管理员"; + + // 配额/费用问题 - 系统问题 + case "QuotaExceeded.DailyQuota": + case "QuotaExceeded.MonthlyQuota": + case "QuotaExceeded.TotalQuota": + case "QuotaExceeded.RPM": + case "QuotaExceeded.TPM": + case "QuotaExceeded.RPD": + case "QuotaExceeded.TPD": + return isEnglish ? "System error: Service quota exceeded. Please contact the administrator." + : "系统错误:服务配额已用完,请联系管理员"; + + // 服务器错误 - 系统问题 + case "InternalError": + case "ServiceUnavailable": + return isEnglish ? "System error: Service is temporarily unavailable. Please contact the administrator or try again later." + : "系统错误:服务暂时不可用,请联系管理员或稍后重试"; + + default: + // 根据详细信息判断是用户问题还是系统问题 + if (detailMessage != null) { + if (detailMessage.contains("sensitive") || detailMessage.contains("敏感")) { + return isEnglish ? "Your content contains sensitive information. Please modify and try again." + : "您的内容包含敏感信息,请修改后重试"; + } else if (detailMessage.contains("quota") || detailMessage.contains("配额") || + detailMessage.contains("parameter") || detailMessage.contains("参数") || + detailMessage.contains("auth") || detailMessage.contains("认证")) { + return isEnglish ? "System error occurred. Please contact the administrator." + : "系统错误,请联系管理员"; + } + } + return isEnglish ? "Image generation failed. Please try again later or contact the administrator if the problem persists." + : "图像生成失败,请稍后重试,如问题持续请联系管理员"; + } + } } diff --git a/src/main/java/com/ai/da/service/impl/LLMServiceImpl.java b/src/main/java/com/ai/da/service/impl/LLMServiceImpl.java index e87c2291..b0cceeeb 100644 --- a/src/main/java/com/ai/da/service/impl/LLMServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/LLMServiceImpl.java @@ -604,6 +604,8 @@ public class LLMServiceImpl implements LLMService { collectionElement.setAccountId(userHolder.getId()); collectionElement.setProjectId(project.getId()); collectionElement.setLevel1Type(CollectionLevel1TypeEnum.MODEL.getRealName()); + collectionElement.setLevel3Type(workspaceService.getProjectSexById(project.getId())); + // collectionElement.setLevel2Type(board.getLevel2Type()); collectionElement.setName(sysFile.getName()); collectionElement.setUrl(sysFile.getUrl()); diff --git a/src/main/java/com/ai/da/service/impl/WorkspaceServiceImpl.java b/src/main/java/com/ai/da/service/impl/WorkspaceServiceImpl.java index 0d5f72ff..2531c5e7 100644 --- a/src/main/java/com/ai/da/service/impl/WorkspaceServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/WorkspaceServiceImpl.java @@ -728,7 +728,10 @@ public class WorkspaceServiceImpl extends ServiceImpl queryWrapper = new LambdaQueryWrapper().eq(CollectionElement::getProjectId, projectId) + .eq(CollectionElement::getLevel1Type, CollectionLevel1TypeEnum.MODEL.getRealName()) + .eq(CollectionElement::getLevel3Type, workspaceNew.getSex()); + if (collectionElementMapper.selectCount(queryWrapper) == 0){ CollectionElement collectionElement = new CollectionElement(); collectionElement.setAccountId(userInfo.getId()); collectionElement.setProjectId(projectId); @@ -812,6 +815,7 @@ public class WorkspaceServiceImpl extends ServiceImpl