diff --git a/web-api/ruoyi-admin/src/main/java/com/ruoyi/api/ByteApiController.java b/web-api/ruoyi-admin/src/main/java/com/ruoyi/api/ByteApiController.java index bfd3026..a40499b 100644 --- a/web-api/ruoyi-admin/src/main/java/com/ruoyi/api/ByteApiController.java +++ b/web-api/ruoyi-admin/src/main/java/com/ruoyi/api/ByteApiController.java @@ -223,8 +223,24 @@ public class ByteApiController extends BaseController { aiOrderService.updateOrderTaskInfo(aiOrder); return AjaxResult.success(taskId); } catch (IllegalArgumentException e) { + logger.error("[NanoImgToImg] 参数校验异常: functionType={}, nanoApiType={}, resolution={}, aspectRatio={}, numImages={}, imageUrlsSize={}, message={}", + functionType, + nanoApiType, + request.getResolution(), + request.getAspectRatio(), + request.getNumImages(), + imageUrls.size(), + e.getMessage(), e); return AjaxResult.error(e.getMessage()); } catch (Exception e) { + logger.error("[NanoImgToImg] 调用上游异常并退款: functionType={}, nanoApiType={}, resolution={}, aspectRatio={}, numImages={}, imageUrlsSize={}, message={}", + functionType, + nanoApiType, + request.getResolution(), + request.getAspectRatio(), + request.getNumImages(), + imageUrls.size(), + e.getMessage(), e); if (aiOrder != null) { aiOrderService.orderFailure(aiOrder); } @@ -285,6 +301,15 @@ public class ByteApiController extends BaseController { NanoBananaResponse nanoResponse = byteService.generateNanoBanana(nanoType, nanoRequest); if (nanoResponse == null || nanoResponse.getCode() != 200 || nanoResponse.getData() == null) { + logger.error("[PromptToImg] 生成失败并退款: functionType={}, nanoApiType={}, resolution={}, aspectRatio={}, textLen={}, upstreamCode={}, upstreamMsg={}, upstreamDataNull={}", + functionType, + nanoType, + resolution, + request.getAspectRatio(), + text == null ? 0 : text.length(), + nanoResponse == null ? null : nanoResponse.getCode(), + nanoResponse == null ? "NULL_RESPONSE" : nanoResponse.getMessage(), + nanoResponse == null || nanoResponse.getData() == null); aiOrderService.orderFailure(aiOrder); return AjaxResult.error(-2, "generation failed, balance has been refunded"); } @@ -551,54 +576,109 @@ public class ByteApiController extends BaseController { @ApiOperation("NanoBanana生成回调(兼容 V2 和 Pro 格式)") @Anonymous public AjaxResult nanoCallback(@RequestBody NanoBananaCallback callback) throws Exception { + logger.info("[NanoCallback] 接收到回调请求: {}", callback); if (callback == null || callback.getData() == null) { - return AjaxResult.error("回调数据为空"); + logger.warn("[NanoCallback] 回调数据为空,callback={}", callback); + return buildNanoCallbackResult("EMPTY_DATA", AjaxResult.error("回调数据为空")); } NanoBananaCallback.NanoBananaCallbackData data = callback.getData(); String taskId = data.getTaskId(); Integer successFlag = data.getSuccessFlag(); + logger.info("[NanoCallback] 关键字段解析: taskId={}, successFlag={}", taskId, successFlag); + if (StringUtils.isBlank(taskId)) { + logger.warn("[NanoCallback] taskId为空,无法匹配订单,data={}", data); + return buildNanoCallbackResult("EMPTY_TASK_ID", AjaxResult.error("taskId为空")); + } // 从 V2 或 Pro 格式中获取 resultImageUrl String resultImageUrl = null; - if (data.getResponse() != null) { - // V2 格式 - resultImageUrl = data.getResponse().getResultImageUrl(); - } else if (data.getInfo() != null) { - // Pro 格式 - resultImageUrl = data.getInfo().getResultImageUrl(); + try { + NanoBananaCallback.NanoBananaCallbackResponse response = data.getResponse(); + NanoBananaCallback.NanoBananaCallbackInfo info = data.getInfo(); + if (response != null) { + // V2 格式 + resultImageUrl = response.getResultImageUrl(); + logger.info("[NanoCallback] 命中 V2 响应结构, resultImageUrl={}", resultImageUrl); + } else if (info != null) { + // Pro 格式 + resultImageUrl = info.getResultImageUrl(); + logger.info("[NanoCallback] 命中 Pro 响应结构, resultImageUrl={}", resultImageUrl); + } else { + logger.warn("[NanoCallback] 未命中 V2/Pro 响应结构, data={}", data); + } + } catch (Exception e) { + logger.error("[NanoCallback] 提取resultImageUrl异常: taskId={}, data={}", taskId, data, e); + return buildNanoCallbackResult("PARSE_RESULT_URL_ERROR", AjaxResult.error("回调结果解析异常")); } - // 根据 successFlag 处理不同状态 - if (successFlag == 1 && resultImageUrl != null) { - // 成功 - 上传到腾讯云COS并更新订单状态为 1(成功) - String s3Url = tencentCosUtil.uploadFileByUrl(resultImageUrl); - - if (s3Url != null) { - AiOrder aiOrderByResult = aiOrderService.getAiOrderByResult(taskId); - if (aiOrderByResult != null) { - aiOrderByResult.setResult(s3Url); - aiOrderByResult.setTaskId(taskId); - // orderSuccess 会设置 status=1 并更新统计 - aiOrderService.orderSuccess(aiOrderByResult); - return AjaxResult.success("回调处理成功,图像已上传"); - } + // 根据 successFlag 和 resultImageUrl 处理不同状态 + // 某些 NanoBanana 回调场景 successFlag 可能为 null,但会返回有效 resultImageUrl,此时按成功处理。 + boolean hasResultImage = StringUtils.isNotBlank(resultImageUrl); + boolean isSuccessByFlag = Integer.valueOf(1).equals(successFlag); + boolean isSuccessByCode = Integer.valueOf(200).equals(callback.getCode()); + if (hasResultImage && (isSuccessByFlag || successFlag == null || isSuccessByCode)) { + // 成功 - 优先上传到腾讯云COS,若失败则回退使用回调原图URL写库 + logger.info("[NanoCallback] 判定成功并开始上传结果图到COS: taskId={}, successFlag={}, originUrl={}", + taskId, successFlag, resultImageUrl); + String s3Url = null; + try { + s3Url = tencentCosUtil.uploadFileByUrl(resultImageUrl); + logger.info("[NanoCallback] COS上传结果: taskId={}, s3Url={}", taskId, s3Url); + } catch (Exception e) { + // 上游临时图可能有防盗链、过期或地域限制导致403,这里降级为直接使用回调原图URL写库 + logger.error("[NanoCallback] COS上传异常,降级使用原图URL写库: taskId={}, originUrl={}", + taskId, resultImageUrl, e); } - return AjaxResult.error("图像上传失败"); - } else if (successFlag == 0) { - // 生成中 - 订单初始状态已经是 0,不需要额外更新 - return AjaxResult.success("任务生成中"); - } else if (successFlag == 2 || successFlag == 3) { - // 失败 - 退款并更新订单状态为 2(失败) + String finalResultUrl = StringUtils.isNotBlank(s3Url) ? s3Url : resultImageUrl; + if (StringUtils.isBlank(s3Url)) { + logger.warn("[NanoCallback] COS上传失败,回退使用回调原图URL写库: taskId={}, resultImageUrl={}", + taskId, resultImageUrl); + } + AiOrder aiOrderByResult = aiOrderService.getAiOrderByResult(taskId); + logger.info("[NanoCallback] 根据taskId查询订单结果: taskId={}, order={}", taskId, aiOrderByResult); + if (aiOrderByResult != null) { + aiOrderByResult.setResult(finalResultUrl); + aiOrderByResult.setTaskId(taskId); + // orderSuccess 会设置 status=1 并更新统计 + aiOrderService.orderSuccess(aiOrderByResult); + logger.info("[NanoCallback] 回调处理成功并已更新订单: taskId={}, orderId={}, result={}", + taskId, aiOrderByResult.getId(), finalResultUrl); + return buildNanoCallbackResult("SUCCESS", AjaxResult.success("回调处理成功,图像已写入")); + } + logger.warn("[NanoCallback] 未找到对应订单,无法更新: taskId={}", taskId); + return buildNanoCallbackResult("ORDER_NOT_FOUND", AjaxResult.error("未找到对应订单")); + } else if (Integer.valueOf(0).equals(successFlag)) { + // 生成中 - 订单初始状态已经是 0,不需要额外更新 + logger.info("[NanoCallback] 任务生成中: taskId={}", taskId); + return buildNanoCallbackResult("IN_PROGRESS", AjaxResult.success("任务生成中")); + } else if (Integer.valueOf(2).equals(successFlag) || Integer.valueOf(3).equals(successFlag)) { + // 失败 - 退款并更新订单状态为 2(失败) + logger.warn("[NanoCallback] 任务失败回调,准备退款: taskId={}, successFlag={}", taskId, successFlag); + AiOrder aiOrderByResult = aiOrderService.getAiOrderByResult(taskId); + logger.info("[NanoCallback] 失败回调订单查询结果: taskId={}, order={}", taskId, aiOrderByResult); if (aiOrderByResult != null) { // orderFailure 会设置 status=2 并处理退款 aiOrderService.orderFailure(aiOrderByResult); - return AjaxResult.success("回调处理失败,已退款"); + logger.info("[NanoCallback] 失败回调处理完成,已退款: taskId={}, orderId={}", taskId, aiOrderByResult.getId()); + return buildNanoCallbackResult("FAILED_REFUNDED", AjaxResult.success("回调处理失败,已退款")); } + logger.warn("[NanoCallback] 失败回调未找到订单,无法退款: taskId={}", taskId); } - return AjaxResult.success("回调已接收"); + logger.warn("[NanoCallback] 未命中明确处理分支,按已接收返回: taskId={}, successFlag={}, resultImageUrl={}", + taskId, successFlag, resultImageUrl); + return buildNanoCallbackResult("FALLBACK", AjaxResult.success("回调已接收")); + } + + /** + * 统一打印 NanoBanana 回调返回结果,便于对比“接收内容-处理路径-返回值”。 + */ + private AjaxResult buildNanoCallbackResult(String stage, AjaxResult result) { + logger.info("[NanoCallback] 返回结果: stage={}, code={}, msg={}", + stage, result.get("code"), result.get("msg")); + return result; } /**