From f9dfd61b89923e6d1481590a97efd832bcb373a0 Mon Sep 17 00:00:00 2001 From: litianxiang Date: Thu, 9 Oct 2025 15:05:09 +0800 Subject: [PATCH] =?UTF-8?q?fix=EF=BC=9A=E8=B0=B7=E6=AD=8C=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E6=96=B0=E5=8A=A0=E9=87=8D=E8=AF=95=E6=9C=BA=E5=88=B6?= =?UTF-8?q?=EF=BC=8C=E4=BF=AE=E6=94=B9nanobanana=E6=AD=A3=E5=BC=8F?= =?UTF-8?q?=E7=89=88ID?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ai/da/common/constant/ModelConstants.java | 2 +- .../ai/da/model/vo/CollectionElementVO.java | 3 + .../da/service/impl/GenerateServiceImpl.java | 360 ++++++++++++------ 3 files changed, 245 insertions(+), 120 deletions(-) diff --git a/src/main/java/com/ai/da/common/constant/ModelConstants.java b/src/main/java/com/ai/da/common/constant/ModelConstants.java index 136abcbe..bc0b1745 100644 --- a/src/main/java/com/ai/da/common/constant/ModelConstants.java +++ b/src/main/java/com/ai/da/common/constant/ModelConstants.java @@ -23,7 +23,7 @@ public class ModelConstants { public static final String PRINTBOARD_HIGH_I2I = "doubao-seededit-3-0-i2i-250628"; public static final String PRINTBOARD_ADVANCED_I2I = "doubao-seedream-4-0-250828"; public static final String IMAGEN_MODEL = "imagen-4.0-generate-001"; - public static final String NANO_BANANA = "gemini-2.5-flash-image-preview"; + public static final String NANO_BANANA = "gemini-2.5-flash-image"; public static final String LOCAL_MODEL = "local"; // 风格常量 diff --git a/src/main/java/com/ai/da/model/vo/CollectionElementVO.java b/src/main/java/com/ai/da/model/vo/CollectionElementVO.java index 505ed4ef..1bf91702 100644 --- a/src/main/java/com/ai/da/model/vo/CollectionElementVO.java +++ b/src/main/java/com/ai/da/model/vo/CollectionElementVO.java @@ -25,6 +25,9 @@ public class CollectionElementVO { @ApiModelProperty("二级类型 Outwear Dress Blouse Skirt Trousers(只争对Sketchboard一级类型)") private String level2Type; + @ApiModelProperty("三级类型 性别") + private String level3Type; + @ApiModelProperty("design类型 用户design生成时候区别library和collection") private String designType; 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 2691c2a2..eae5f1ba 100644 --- a/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java +++ b/src/main/java/com/ai/da/service/impl/GenerateServiceImpl.java @@ -675,7 +675,15 @@ public class GenerateServiceImpl extends ServiceImpl i 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: {}", + useModel, imagePath, prompt); + + System.setProperty("https.proxyHost", "127.0.0.1"); + System.setProperty("https.proxyPort", "10809"); + if (StringUtil.isNullOrEmpty(imagePath)||StringUtil.isNullOrEmpty(useModel)||StringUtil.isNullOrEmpty(prompt)){ + log.error("参数验证失败 - imagePath: {}, useModel: {}, prompt: {}", imagePath, useModel, prompt); throw new BusinessException("Parameter Exception"); } AuthPrincipalVo userHolder = UserContext.getUserHolder(); @@ -740,7 +748,7 @@ public class GenerateServiceImpl extends ServiceImpl i generationConfig.set("imageConfig", imageConfig); requestBody.set("generationConfig", generationConfig); String jsonBody = requestBody.toString(); - log.info("Google 请求入参:{}", jsonBody); +// log.info("Google 请求入参:{}", jsonBody); GenerateResultVO resultVO = new GenerateResultVO(taskId, null, null, "Pending"); redisUtil.addToString(key, new Gson().toJson(resultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); @@ -762,60 +770,111 @@ public class GenerateServiceImpl extends ServiceImpl i throw new RuntimeException("google token error"); } - // 异步发送API请求 + // 异步发送API请求 - 优化网络配置以解决连接中断问题 + ConnectionPool connectionPool = new ConnectionPool(10, 5, TimeUnit.MINUTES); OkHttpClient client = new OkHttpClient.Builder() - .connectTimeout(30, TimeUnit.SECONDS) // 连接超时时间 - .readTimeout(60, TimeUnit.SECONDS) // 读取超时时间 - .writeTimeout(60, TimeUnit.SECONDS) // 写入超时时间 - .retryOnConnectionFailure(true) + .connectTimeout(45, TimeUnit.SECONDS) // 增加连接超时时间 + .readTimeout(120, TimeUnit.SECONDS) // 增加读取超时时间 + .writeTimeout(120, TimeUnit.SECONDS) // 增加写入超时时间 + .callTimeout(180, TimeUnit.SECONDS) // 添加总调用超时时间 + .connectionPool(connectionPool) // 使用连接池 + .retryOnConnectionFailure(true) // 启用连接失败重试 .build(); Request request = new Request.Builder() .url(endpoint) + // 移除Connection: close头,使用Keep-Alive连接 .addHeader("Authorization", "Bearer " + tokenValue) .addHeader("Content-Type", "application/json") + .addHeader("User-Agent", "AIDA-Client/1.0") + .addHeader("Accept", "application/json") .post(RequestBody.create(MediaType.parse("application/json"), jsonBody)) .build(); - try (Response response = client.newCall(request).execute()) { - String result = response.body().string(); - - log.info("Google 响应结果:{}", result); - com.alibaba.fastjson.JSONObject jsonResponse = JSON.parseObject(result); - - String base64Data = null; - - //根据模型类别按照api取出结果 - if (ModelConstants.NANO_BANANA.equals(useModel)) { - JSONArray candidates = jsonResponse.getJSONArray("candidates"); - - if (candidates != null && !candidates.isEmpty()) { - com.alibaba.fastjson.JSONObject candidate = candidates.getJSONObject(0); - com.alibaba.fastjson.JSONObject contentResult = candidate.getJSONObject("content"); - JSONArray parts = contentResult.getJSONArray("parts"); - - // 遍历parts数组找到包含inlineData的对象 - 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"); - base64Data = inlineDataResult.getString("data"); - break; + // 实现重试逻辑来处理网络连接问题 + 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: {}", + response.code(), (requestEndTime - requestStartTime), taskId); + if (!response.isSuccessful()) { + log.warn("Google API响应失败,状态码: {} for taskId: {}", response.code(), taskId); + if (attempt < maxRetries) { + Thread.sleep(retryDelay * attempt); // 递增延迟 + continue; + } else { + 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); - if (base64Data != null && !base64Data.isEmpty()) { - String resultPath = userId + "/product_image" + "/" + uuid; - String minioPath = minioUtil.base64UploadToPath("data:image/png;base64," + base64Data, userBucket, resultPath); - // 生成成功,更新Redis状态和URL - GenerateResultVO successResultVO = new GenerateResultVO(taskId, null, minioPath, "Success"); - redisUtil.addToString(key, new Gson().toJson(successResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); - } else { - // 没有找到图像数据或数据为空,标记为失败 - log.warn("Google generation response does not contain valid image data for taskId: {}", taskId); - GenerateResultVO failResultVO = new GenerateResultVO(taskId, null, null, "Fail"); - redisUtil.addToString(key, new Gson().toJson(failResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + String base64Data = null; + + //根据模型类别按照api取出结果 + if (ModelConstants.NANO_BANANA.equals(useModel)) { + JSONArray candidates = jsonResponse.getJSONArray("candidates"); + + if (candidates != null && !candidates.isEmpty()) { + com.alibaba.fastjson.JSONObject candidate = candidates.getJSONObject(0); + com.alibaba.fastjson.JSONObject contentResult = candidate.getJSONObject("content"); + JSONArray parts = contentResult.getJSONArray("parts"); + + // 遍历parts数组找到包含inlineData的对象 + 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"); + base64Data = inlineDataResult.getString("data"); + break; + } + } + } + } + + if (base64Data != null && !base64Data.isEmpty()) { + String resultPath = userId + "/product_image" + "/" + uuid; + String minioPath = minioUtil.base64UploadToPath("data:image/png;base64," + base64Data, userBucket, resultPath); + // 生成成功,更新Redis状态和URL + GenerateResultVO successResultVO = new GenerateResultVO(taskId, null, minioPath, "Success"); + redisUtil.addToString(key, new Gson().toJson(successResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + success = true; + log.info("Google API调用成功 for taskId: {}", taskId); + } else { + // 没有找到图像数据或数据为空,标记为失败 + log.warn("Google generation response does not contain valid image data for taskId: {}", taskId); + GenerateResultVO failResultVO = new GenerateResultVO(taskId, null, null, "Fail"); + redisUtil.addToString(key, new Gson().toJson(failResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + success = true; // 这是业务逻辑失败,不需要重试 + } + } + } catch (IOException e) { + log.warn("网络连接问题 - 异常类型: {}, 尝试: {}/{}, taskId: {}, 错误详情: {}", + e.getClass().getSimpleName(), attempt, maxRetries, taskId, e.getMessage()); + if (attempt < maxRetries) { + int delayMs = retryDelay * attempt; + log.info("准备重试 - 延迟: {}ms, 下次尝试: {}/{}, taskId: {}", + delayMs, (attempt + 1), maxRetries, taskId); + try { + Thread.sleep(delayMs); // 递增延迟重试 + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + log.error("重试被中断 - taskId: {}", taskId, ie); + throw new RuntimeException("重试被中断", ie); + } + } else { + log.error("Google API调用最终失败 - 已达到最大重试次数: {}, taskId: {}", maxRetries, taskId, e); + throw e; // 最后一次尝试失败,抛出异常 + } } } } catch (Exception e) { @@ -825,6 +884,9 @@ 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; } @@ -909,6 +971,9 @@ public class GenerateServiceImpl extends ServiceImpl i // generationConfig.set("temperature", 1); generationConfig.set("maxOutputTokens", 8192); generationConfig.set("responseModalities", Arrays.asList("TEXT", "IMAGE")); + JSONObject imageConfig = new JSONObject(); + imageConfig.set("aspectRatio", "9:16"); + generationConfig.set("imageConfig", imageConfig); // generationConfig.set("topP", 0.95); requestBody.set("generationConfig", generationConfig); @@ -933,7 +998,7 @@ public class GenerateServiceImpl extends ServiceImpl i String jsonBody = requestBody.toString(); - log.info("Google 请求入参:{}", jsonBody); +// log.info("Google 请求入参:{}", jsonBody); // 生成唯一的任务ID String taskId = uuid + "-" + userId; @@ -945,91 +1010,145 @@ public class GenerateServiceImpl extends ServiceImpl i // 异步处理token获取和API调用 CompletableFuture.runAsync(() -> { - try { - // 异步获取token - String tokenValue = null; - try (InputStream inputStream = GenerateServiceImpl.class.getClassLoader() - .getResourceAsStream("aida-461108-b4afaabebb84.json")) { + 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() + .getResourceAsStream("aida-461108-b4afaabebb84.json")) { - GoogleCredentials credentials = GoogleCredentials - .fromStream(inputStream) - .createScoped(Collections.singletonList("https://www.googleapis.com/auth/cloud-platform")); + GoogleCredentials credentials = GoogleCredentials + .fromStream(inputStream) + .createScoped(Collections.singletonList("https://www.googleapis.com/auth/cloud-platform")); - credentials.refreshIfExpired(); - tokenValue = credentials.getAccessToken().getTokenValue(); - } - if (tokenValue == null) { - throw new RuntimeException("google token error"); - } - - // 异步发送API请求 - OkHttpClient client = new OkHttpClient.Builder() - .connectTimeout(30, TimeUnit.SECONDS) // 连接超时时间 - .readTimeout(60, TimeUnit.SECONDS) // 读取超时时间 - .writeTimeout(60, TimeUnit.SECONDS) // 写入超时时间 - .retryOnConnectionFailure(true) - .build(); - Request request = new Request.Builder() - .url(endpoint) - .addHeader("Authorization", "Bearer " + tokenValue) - .addHeader("Content-Type", "application/json") - .post(RequestBody.create(MediaType.parse("application/json"), jsonBody)) - .build(); - - try (Response response = client.newCall(request).execute()) { - String result = response.body().string(); - - log.info("Google 响应结果:{}", result); - com.alibaba.fastjson.JSONObject jsonResponse = JSON.parseObject(result); - - String base64Data = null; - - //根据模型类别按照api取出结果 - if (ModelConstants.NANO_BANANA.equals(useModel)) { - JSONArray candidates = jsonResponse.getJSONArray("candidates"); - - if (candidates != null && !candidates.isEmpty()) { - com.alibaba.fastjson.JSONObject candidate = candidates.getJSONObject(0); - com.alibaba.fastjson.JSONObject content = candidate.getJSONObject("content"); - JSONArray parts = content.getJSONArray("parts"); - - // 遍历parts数组找到包含inlineData的对象 - for (int i = 0; i < parts.size(); i++) { - com.alibaba.fastjson.JSONObject part = parts.getJSONObject(i); - if (part.containsKey("inlineData")) { - com.alibaba.fastjson.JSONObject inlineData = part.getJSONObject("inlineData"); - base64Data = inlineData.getString("data"); - break; - } - } - } - } else if (ModelConstants.IMAGEN_MODEL.equals(useModel)) { - JSONArray predictions = jsonResponse.getJSONArray("predictions"); - - if (predictions != null && !predictions.isEmpty()) { - com.alibaba.fastjson.JSONObject prediction = predictions.getJSONObject(0); - base64Data = prediction.getString("bytesBase64Encoded"); - } + credentials.refreshIfExpired(); + tokenValue = credentials.getAccessToken().getTokenValue(); + } + if (tokenValue == null) { + throw new RuntimeException("google token error"); } - if (base64Data != null && !base64Data.isEmpty()) { - String minioPath = minioUtil.base64UploadToPath("data:image/png;base64," + base64Data, userBucket, resultPath); - // 生成成功,更新Redis状态和URL - GenerateResultVO successResultVO = new GenerateResultVO(taskId, null, minioPath, "Success"); - redisUtil.addToString(key, new Gson().toJson(successResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + // 异步发送API请求 + OkHttpClient client = new OkHttpClient.Builder() + .connectionPool(new ConnectionPool(10, 5, TimeUnit.MINUTES)) + .connectTimeout(45, TimeUnit.SECONDS) + .readTimeout(120, TimeUnit.SECONDS) + .writeTimeout(120, TimeUnit.SECONDS) + .callTimeout(180, TimeUnit.SECONDS) + .retryOnConnectionFailure(true) + .build(); + + Request request = new Request.Builder() + .url(endpoint) + .addHeader("Authorization", "Bearer " + tokenValue) + .addHeader("Content-Type", "application/json") + .addHeader("User-Agent", "AIDA-Backend/1.0") + .addHeader("Accept", "application/json") + .post(RequestBody.create(MediaType.parse("application/json"), jsonBody)) + .build(); + + long requestStartTime = System.currentTimeMillis(); + try (Response response = client.newCall(request).execute()) { + long requestDuration = System.currentTimeMillis() - requestStartTime; + log.info("Google API 请求完成 - taskId: {}, 尝试: {}, URL: {}, 状态码: {}, 耗时: {}ms", + taskId, attempt, endpoint, response.code(), requestDuration); + + String result = response.body().string(); + +// log.info("Google 响应结果:{}", result); + com.alibaba.fastjson.JSONObject jsonResponse = JSON.parseObject(result); + + String base64Data = null; + + //根据模型类别按照api取出结果 + if (ModelConstants.NANO_BANANA.equals(useModel)) { + JSONArray candidates = jsonResponse.getJSONArray("candidates"); + + if (candidates != null && !candidates.isEmpty()) { + com.alibaba.fastjson.JSONObject candidate = candidates.getJSONObject(0); + com.alibaba.fastjson.JSONObject content = candidate.getJSONObject("content"); + JSONArray parts = content.getJSONArray("parts"); + + // 遍历parts数组找到包含inlineData的对象 + for (int i = 0; i < parts.size(); i++) { + com.alibaba.fastjson.JSONObject part = parts.getJSONObject(i); + if (part.containsKey("inlineData")) { + com.alibaba.fastjson.JSONObject inlineData = part.getJSONObject("inlineData"); + base64Data = inlineData.getString("data"); + break; + } + } + } + } else if (ModelConstants.IMAGEN_MODEL.equals(useModel)) { + JSONArray predictions = jsonResponse.getJSONArray("predictions"); + + if (predictions != null && !predictions.isEmpty()) { + com.alibaba.fastjson.JSONObject prediction = predictions.getJSONObject(0); + base64Data = prediction.getString("bytesBase64Encoded"); + } + } + + if (base64Data != null && !base64Data.isEmpty()) { + String minioPath = minioUtil.base64UploadToPath("data:image/png;base64," + base64Data, userBucket, resultPath); + // 生成成功,更新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; // 成功后立即返回 + } else { + // 没有找到图像数据或数据为空,标记为失败(业务逻辑失败,不重试) + log.warn("Google generation response does not contain valid image data for taskId: {}", taskId); + GenerateResultVO failResultVO = new GenerateResultVO(taskId, null, null, "Fail"); + redisUtil.addToString(key, new Gson().toJson(failResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); + return; // 业务逻辑失败,直接返回不重试 + } + } + } catch (IOException e) { + // 只对 IOException 进行重试 + log.warn("Google API 调用发生 IOException - taskId: {}, 尝试: {}/{}, 异常: {}", + taskId, attempt, maxRetries, e.getMessage()); + + if (attempt < maxRetries) { + try { + long delay = retryDelayMs * attempt; // 递增延迟 + log.info("等待 {}ms 后进行第 {} 次重试 - taskId: {}", delay, attempt + 1, taskId); + Thread.sleep(delay); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + log.error("重试等待被中断 - taskId: {}", taskId); + break; + } } else { - // 没有找到图像数据或数据为空,标记为失败 - log.warn("Google generation response does not contain valid image data for taskId: {}", taskId); + // 达到最大重试次数,记录错误并更新Redis状态 + log.error("Google API 调用最终失败,已达到最大重试次数 {} - taskId: {}", maxRetries, taskId, e); GenerateResultVO failResultVO = new GenerateResultVO(taskId, null, null, "Fail"); redisUtil.addToString(key, new Gson().toJson(failResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); } + } catch (Exception e) { + // 对于其他异常(非IOException),记录错误并直接返回,不重试 + 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; // 非网络异常,直接返回不重试 } - } catch (Exception e) { - log.error("Google generation failed for taskId: {}", taskId, e); - // 生成失败,更新Redis状态 - GenerateResultVO failResultVO = new GenerateResultVO(taskId, null, null, "Fail"); - redisUtil.addToString(key, new Gson().toJson(failResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); } + + // 如果所有重试都失败了,记录最终失败日志 + long totalDuration = System.currentTimeMillis() - startTime; + log.error("Google API 调用最终失败,所有重试都已用尽 - taskId: {}, 总耗时: {}ms", taskId, totalDuration); + GenerateResultVO failResultVO = new GenerateResultVO(taskId, null, null, "Fail"); + redisUtil.addToString(key, new Gson().toJson(failResultVO), CommonConstant.GENERATE_RESULT_EXPIRE_TIME); }, asyncTaskExecutor); // 保存生成记录到数据库 @@ -1044,6 +1163,9 @@ public class GenerateServiceImpl extends ServiceImpl i new Date() ); save(generate); + + + return taskId; }