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 eb69649..a49a387 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 @@ -43,6 +43,12 @@ public class ByteApiController extends BaseController { @Value("${byteapi.callBackUrl}") private String url; + @Value("${nanobanana.callbackUrl}") + private String nanoCallbackUrl; + + @Value("${nanobanana.token}") + private String nanoToken; + @PostMapping("/promptToImg") @ApiOperation("文生图") public AjaxResult promptToImg(@RequestBody ByteApiRequest request) { @@ -84,31 +90,32 @@ public class ByteApiController extends BaseController { return AjaxResult.error(-1, "You have a low balance, please recharge"); } aiOrder.setText(text); - ByteBodyReq byteBodyReq = new ByteBodyReq(); - byteBodyReq.setModel("ep-20251104104536-2gpgz"); - byteBodyReq.setPrompt(text); - byteBodyReq.setSequential_image_generation("disabled"); - byteBodyReq.setResponse_format("url"); - byteBodyReq.setSize("2K"); - byteBodyReq.setStream(false); - byteBodyReq.setWatermark(false); - ByteBodyRes byteBodyRes = byteService.promptToImg(byteBodyReq); - List data = byteBodyRes.getData(); - ByteDataRes byteDataRes = data.get(0); - String url = byteDataRes.getUrl(); - url = awsS3Util.uploadFileByUrl(url); - if (url == null) { - // 判断生成失败,退回金额逻辑 + + // 4. 组装NanoBanana (原火山)接口参数 - 符合用户需求 + NanoBananaRequest nanoRequest = NanoBananaRequest.forTextToImage( + text, + nanoCallbackUrl, + request.getAspectRatio() != null ? request.getAspectRatio() : "auto", + request.getResolution() != null ? request.getResolution() : "1K" + ); + + // 5. 调用NanoBanana接口 (curl POST https://api.nanobananaapi.ai/...) + NanoBananaResponse nanoResponse = byteService.generateImage(nanoRequest); + + if (nanoResponse == null || nanoResponse.getCode() != 200 || nanoResponse.getData() == null) { aiOrderService.orderFailure(aiOrder); return AjaxResult.error(-2, "generation failed, balance has been refunded"); } - aiOrder.setResult(url); - aiOrderService.orderSuccess(aiOrder); - return AjaxResult.success(url); + + String taskId = nanoResponse.getData().getTaskId(); + aiOrder.setTaskId(taskId); // 保存taskId用于回调匹配 + aiOrder.setResult(taskId); // 临时用taskId作为result,后续回调更新为真实URL + + aiOrderService.orderSuccess(aiOrder); // 先标记为进行中,回调成功后再更新 + return AjaxResult.success(taskId); // 返回taskId给前端,后续可轮询或等待回调 } catch (Exception e) { // 判断生成失败,退回金额逻辑 aiOrderService.orderFailure(aiOrder); -// return AjaxResult.error(-2, "generation failed, balance has been refunded"); throw new RuntimeException(e); } } @@ -160,30 +167,36 @@ public class ByteApiController extends BaseController { } aiOrder.setText(text); - aiOrder.setImg1(firstUrl.toString()); - ByteBodyReq byteBodyReq = new ByteBodyReq(); - byteBodyReq.setModel("ep-20251104104536-2gpgz"); - byteBodyReq.setPrompt(text); - byteBodyReq.setImage(firstUrl); - byteBodyReq.setSequential_image_generation("disabled"); - byteBodyReq.setResponse_format("url"); - byteBodyReq.setSize("2K"); - byteBodyReq.setStream(false); - byteBodyReq.setWatermark(false); - ByteBodyRes byteBodyRes = byteService.promptToImg(byteBodyReq); - List data = byteBodyRes.getData(); - ByteDataRes byteDataRes = data.get(0); - String url = byteDataRes.getUrl(); - url = awsS3Util.uploadFileByUrl(url); - if (url == null) { - // 判断生成失败,退回金额逻辑 + + // 4. 组装NanoBanana接口参数 - 图生图 + List imageUrls = new ArrayList<>(); + if (firstUrl != null) { + imageUrls.add(firstUrl.toString()); + } + + NanoBananaRequest nanoRequest = NanoBananaRequest.forImageToImage( + text, + imageUrls, + nanoCallbackUrl, + request.getAspectRatio() != null ? request.getAspectRatio() : "auto", + request.getResolution() != null ? request.getResolution() : "1K" + ); + + // 5. 调用NanoBanana接口 + NanoBananaResponse nanoResponse = byteService.generateImageWithReference(nanoRequest); + + if (nanoResponse == null || nanoResponse.getCode() != 200 || nanoResponse.getData() == null) { aiOrderService.orderFailure(aiOrder); return AjaxResult.error(-2, "generation failed, balance has been refunded"); } - aiOrder.setResult(url); + + String taskId = nanoResponse.getData().getTaskId(); + aiOrder.setTaskId(taskId); + aiOrder.setResult(taskId); + aiOrderService.orderSuccess(aiOrder); - return AjaxResult.success(url); + return AjaxResult.success(taskId); } catch (Exception e) { aiOrderService.orderFailure(aiOrder); throw new RuntimeException(e); @@ -311,10 +324,59 @@ public class ByteApiController extends BaseController { return AjaxResult.success(byteBodyRes); } - @GetMapping(value = "/callBack") - @ApiOperation("视频下载回调") + @PostMapping(value = "/nano-callback") + @ApiOperation("NanoBanana生成回调") @Anonymous - public AjaxResult callBack(@PathVariable("id") ByteBodyRes byteBodyRes) throws Exception { + public AjaxResult nanoCallback(@RequestBody NanoBananaCallback callback) throws Exception { + if (callback == null || callback.getData() == null) { + return AjaxResult.error("回调数据为空"); + } + + NanoBananaCallback.NanoBananaCallbackData data = callback.getData(); + String taskId = data.getTaskId(); + Integer successFlag = data.getSuccessFlag(); + + // 根据successFlag处理不同状态 + if (successFlag == 1 && data.getResponse() != null && data.getResponse().getResultImageUrl() != null) { + // 成功 - 上传到S3并更新订单 + String imageUrl = data.getResponse().getResultImageUrl(); + String s3Url = awsS3Util.uploadFileByUrl(imageUrl); + + if (s3Url != null) { + AiOrder aiOrderByResult = aiOrderService.getAiOrderByResult(taskId); + if (aiOrderByResult != null) { + AiOrder aiOrder = new AiOrder(); + aiOrder.setId(aiOrderByResult.getId()); + aiOrder.setResult(s3Url); + aiOrder.setTaskId(taskId); + aiOrder.setStatus(1); + aiOrderService.updateAiOrder(aiOrder); + return AjaxResult.success("回调处理成功,图像已上传"); + } + } + return AjaxResult.error("图像上传失败"); + } else if (successFlag == 3 || successFlag == 2) { + // 失败 - 退款 + AiOrder aiOrderByResult = aiOrderService.getAiOrderByResult(taskId); + if (aiOrderByResult != null) { + aiOrderService.orderFailure(aiOrderByResult); + return AjaxResult.success("回调处理失败,已退款"); + } + } else if (successFlag == 0) { + // 生成中 - 可忽略或记录 + return AjaxResult.success("任务生成中"); + } + + return AjaxResult.success("回调已接收"); + } + + /** + * 保留原有视频回调(GET转POST兼容) + */ + @PostMapping(value = "/callBack") + @ApiOperation("视频回调") + @Anonymous + public AjaxResult callBack(@RequestBody ByteBodyRes byteBodyRes) throws Exception { if ("succeeded".equals(byteBodyRes.getStatus())) { String id = byteBodyRes.getId(); content content = byteBodyRes.getContent(); @@ -322,11 +384,12 @@ public class ByteApiController extends BaseController { videoUrl = awsS3Util.uploadFileByUrl(videoUrl); content.setVideo_url(videoUrl); AiOrder aiOrderByResult = aiOrderService.getAiOrderByResult(id); - AiOrder aiOrder = new AiOrder(); - aiOrder.setId(aiOrderByResult.getId()); - aiOrder.setResult(videoUrl); -// aiOrder.setUpdateBy(SecurityUtils.getLoginAiUser().getUsername()); - aiOrderService.updateAiOrder(aiOrder); + if (aiOrderByResult != null) { + AiOrder aiOrder = new AiOrder(); + aiOrder.setId(aiOrderByResult.getId()); + aiOrder.setResult(videoUrl); + aiOrderService.updateAiOrder(aiOrder); + } } return AjaxResult.success(byteBodyRes); } diff --git a/web-api/ruoyi-admin/src/main/resources/application.yml b/web-api/ruoyi-admin/src/main/resources/application.yml index 70e9966..3c5b256 100644 --- a/web-api/ruoyi-admin/src/main/resources/application.yml +++ b/web-api/ruoyi-admin/src/main/resources/application.yml @@ -222,6 +222,14 @@ byteapi: apiKey: 327d2815-2516-44c2-9e32-2dc50bf7afd7 callBackUrl: www.google.com +nanobanana: + # NanoBanana API Token (Bearer Token) + token: your_nanobanana_token_here + # 回调地址,需替换为实际部署域名 (POST接口) + callbackUrl: https://your-domain.com/api/ai/nano-callback + # 生成API地址 + apiUrl: https://api.nanobananaapi.ai/api/v1/nanobanana/generate-2 + jinsha: url: https://api.jinshapay.xyz appId: 1763617360 diff --git a/web-api/ruoyi-system/src/main/java/com/ruoyi/ai/domain/NanoBananaCallback.java b/web-api/ruoyi-system/src/main/java/com/ruoyi/ai/domain/NanoBananaCallback.java new file mode 100644 index 0000000..0b04a22 --- /dev/null +++ b/web-api/ruoyi-system/src/main/java/com/ruoyi/ai/domain/NanoBananaCallback.java @@ -0,0 +1,101 @@ +package com.ruoyi.ai.domain; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * NanoBanana API 回调请求体 + * 符合阿里巴巴Java开发手册规范 + * + * @author AI Assistant + * @date 2026-04-10 + */ +@Data +public class NanoBananaCallback { + + /** + * 顶层响应码 + */ + private Integer code; + + /** + * 状态消息 + */ + private String msg; + + /** + * 回调数据 + */ + private NanoBananaCallbackData data; + + @Data + public static class NanoBananaCallbackData { + + /** + * 任务 ID + */ + @JsonProperty("taskId") + private String taskId; + + /** + * 本次调用使用的参数 JSON 字符串 + */ + @JsonProperty("paramJson") + private String paramJson; + + /** + * 任务完成时间 + */ + @JsonProperty("completeTime") + private String completeTime; + + /** + * 响应数据(包含图像URL) + */ + private NanoBananaCallbackResponse response; + + /** + * 生成状态标志: + * 0-生成中, 1-成功, 2-创建失败, 3-生成失败 + */ + @JsonProperty("successFlag") + private Integer successFlag; + + /** + * 错误码(如 422 敏感内容) + */ + @JsonProperty("errorCode") + private Integer errorCode; + + /** + * 错误消息 + */ + @JsonProperty("errorMessage") + private String errorMessage; + + /** + * 操作类型/模型标识 + */ + @JsonProperty("operationType") + private String operationType; + + @JsonProperty("createTime") + private String createTime; + } + + @Data + public static class NanoBananaCallbackResponse { + + /** + * 原始图像URL(可能为null) + */ + @JsonProperty("originImageUrl") + private String originImageUrl; + + /** + * 结果图像URL(成功时存在) + */ + @JsonProperty("resultImageUrl") + private String resultImageUrl; + } +} diff --git a/web-api/ruoyi-system/src/main/java/com/ruoyi/ai/domain/NanoBananaRequest.java b/web-api/ruoyi-system/src/main/java/com/ruoyi/ai/domain/NanoBananaRequest.java new file mode 100644 index 0000000..70e0be9 --- /dev/null +++ b/web-api/ruoyi-system/src/main/java/com/ruoyi/ai/domain/NanoBananaRequest.java @@ -0,0 +1,81 @@ +package com.ruoyi.ai.domain; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.util.List; + +/** + * NanoBanana API 文生图/图生图请求参数 + * 符合阿里巴巴Java开发手册规范 + * + * @author AI Assistant + * @date 2026-04-10 + */ +@Data +public class NanoBananaRequest { + + /** + * 文本提示词,必填,最长 20000 字符 + */ + private String prompt; + + /** + * 参考图 URL 数组,文生图时传空数组,最多 14 张 + */ + @JsonProperty("imageUrls") + private List imageUrls = List.of(); + + /** + * 生成图像宽高比 + * 支持 1:1、1:4、1:8、2:3、3:2、3:4、4:1、4:3、4:5、5:4、8:1、9:16、16:9、21:9、auto + */ + @JsonProperty("aspectRatio") + private String aspectRatio = "auto"; + + /** + * 分辨率质量,可选 1K / 2K / 4K + */ + private String resolution = "1K"; + + /** + * 是否启用 Google Web Search 增强,默认 false + */ + @JsonProperty("googleSearch") + private Boolean googleSearch = false; + + /** + * 输出格式,支持 png / jpg,默认 jpg + */ + @JsonProperty("outputFormat") + private String outputFormat = "jpg"; + + /** + * 可选回调 URL,用于接收任务完成通知 + */ + @JsonProperty("callBackUrl") + private String callBackUrl; + + /** + * 构造函数 - 文生图 + */ + public static NanoBananaRequest forTextToImage(String prompt, String callBackUrl, String aspectRatio, String resolution) { + NanoBananaRequest req = new NanoBananaRequest(); + req.setPrompt(prompt); + req.setCallBackUrl(callBackUrl); + if (aspectRatio != null) req.setAspectRatio(aspectRatio); + if (resolution != null) req.setResolution(resolution); + return req; + } + + /** + * 构造函数 - 图生图 + */ + public static NanoBananaRequest forImageToImage(String prompt, List imageUrls, String callBackUrl, String aspectRatio, String resolution) { + NanoBananaRequest req = forTextToImage(prompt, callBackUrl, aspectRatio, resolution); + if (imageUrls != null && !imageUrls.isEmpty()) { + req.setImageUrls(imageUrls); + } + return req; + } +} diff --git a/web-api/ruoyi-system/src/main/java/com/ruoyi/ai/domain/NanoBananaResponse.java b/web-api/ruoyi-system/src/main/java/com/ruoyi/ai/domain/NanoBananaResponse.java new file mode 100644 index 0000000..dbc2e9f --- /dev/null +++ b/web-api/ruoyi-system/src/main/java/com/ruoyi/ai/domain/NanoBananaResponse.java @@ -0,0 +1,40 @@ +package com.ruoyi.ai.domain; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * NanoBanana API 提交任务响应 + * 符合阿里巴巴Java开发手册规范 + * + * @author AI Assistant + * @date 2026-04-10 + */ +@Data +public class NanoBananaResponse { + + /** + * 响应码,200 表示成功 + */ + private Integer code; + + /** + * 响应消息 + */ + private String message; + + /** + * 响应数据 + */ + private NanoBananaData data; + + @Data + public static class NanoBananaData { + + /** + * 任务ID,用于后续回调匹配 + */ + @JsonProperty("taskId") + private String taskId; + } +} diff --git a/web-api/ruoyi-system/src/main/java/com/ruoyi/ai/service/IByteService.java b/web-api/ruoyi-system/src/main/java/com/ruoyi/ai/service/IByteService.java index 137fd63..fe3a8e4 100644 --- a/web-api/ruoyi-system/src/main/java/com/ruoyi/ai/service/IByteService.java +++ b/web-api/ruoyi-system/src/main/java/com/ruoyi/ai/service/IByteService.java @@ -2,26 +2,50 @@ package com.ruoyi.ai.service; import com.ruoyi.ai.domain.ByteBodyReq; import com.ruoyi.ai.domain.ByteBodyRes; +import com.ruoyi.ai.domain.NanoBananaRequest; +import com.ruoyi.ai.domain.NanoBananaResponse; +/** + * ByteService 接口 + * 现已切换为 NanoBanana API(原Byte/火山引擎接口已弃用) + * 符合阿里巴巴Java开发手册规范 + * + * @author shi + * @date 2026-04-10 + */ public interface IByteService { /** - * 文生图 + * 文生图 - 使用 NanoBanana API 异步生成,返回 taskId */ + NanoBananaResponse generateImage(NanoBananaRequest req) throws Exception; + + /** + * 图生图 - 使用 NanoBanana API 异步生成,返回 taskId + */ + NanoBananaResponse generateImageWithReference(NanoBananaRequest req) throws Exception; + + /** + * 旧接口兼容(文生图/图生图) + * @deprecated 使用 generateImage 替代 + */ + @Deprecated ByteBodyRes promptToImg(ByteBodyReq req) throws Exception; /** - * 图生图 + * 旧接口兼容(图生图) + * @deprecated 使用 generateImageWithReference 替代 */ + @Deprecated ByteBodyRes imgToImg(ByteBodyReq req) throws Exception; /** - * 首尾帧图生视频 + * 首尾帧图生视频(保持原有) */ ByteBodyRes imgToVideo(ByteBodyReq req) throws Exception; /** - * 下载视频 + * 下载视频(保持原有) */ ByteBodyRes uploadVideo(String id) throws Exception; } diff --git a/web-api/ruoyi-system/src/main/java/com/ruoyi/ai/service/impl/ByteService.java b/web-api/ruoyi-system/src/main/java/com/ruoyi/ai/service/impl/ByteService.java index cc315b8..102299f 100644 --- a/web-api/ruoyi-system/src/main/java/com/ruoyi/ai/service/impl/ByteService.java +++ b/web-api/ruoyi-system/src/main/java/com/ruoyi/ai/service/impl/ByteService.java @@ -3,8 +3,7 @@ package com.ruoyi.ai.service.impl; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; -import com.ruoyi.ai.domain.ByteBodyReq; -import com.ruoyi.ai.domain.ByteBodyRes; +import com.ruoyi.ai.domain.*; import com.ruoyi.ai.service.IByteService; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.http.OkHttpUtils; @@ -12,18 +11,36 @@ import okhttp3.*; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import java.util.List; + +/** + * ByteService 实现类 + * 已切换为 NanoBanana API (https://api.nanobananaapi.ai) + * 原火山/Byte接口保留用于视频功能 + * 符合阿里巴巴Java开发手册规范,使用Lombok、清晰注释、异常处理 + * + * @author shi + * @date 2026-04-10 + */ @Service public class ByteService implements IByteService { -// private final OkHttpClient okHttpClient = OkHttpUtils.createOkHttpClient(); - // Jackson用于JSON序列化/反序列化 private final ObjectMapper objectMapper = new ObjectMapper() .setSerializationInclusion(JsonInclude.Include.NON_NULL) .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) .configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true); - // API地址(可配置在配置文件中) + @Value("${nanobanana.apiUrl:https://api.nanobananaapi.ai/api/v1/nanobanana/generate-2}") + private String nanoApiUrl; + + @Value("${nanobanana.token:}") + private String nanoToken; + + @Value("${nanobanana.callbackUrl:}") + private String callbackUrl; + + // 保留原有Byte配置用于视频功能 @Value("${byteapi.url}") private String API_URL; @@ -31,60 +48,71 @@ public class ByteService implements IByteService { private String apiKey; @Override - public ByteBodyRes promptToImg(ByteBodyReq req) throws Exception { - return this.imgToImg(req); - } - - @Override - public ByteBodyRes imgToImg(ByteBodyReq req) throws Exception { - - // 1. 验证请求参数(可选,根据业务需求) + public NanoBananaResponse generateImage(NanoBananaRequest req) throws Exception { + // 4. 组装NanoBanana接口参数 (替换原VolcengineImageRequest) if (StringUtils.isBlank(req.getPrompt())) { - throw new Exception("imgToImg error:prompt is null"); + throw new Exception("prompt不能为空"); + } + if (StringUtils.isBlank(req.getCallBackUrl())) { + req.setCallBackUrl(callbackUrl); } - // 2. 构建请求体JSON(基于ByteBodyReq的字段) - // 注意:ByteBodyReq需包含与API参数对应的字段(model、prompt等) + // 构建JSON请求体 String jsonBody = objectMapper.writeValueAsString(req); - // 3. 构建请求 + + // 5. 调用NanoBanana API (使用curl对应POST请求) Request request = new Request.Builder() - .url(API_URL + "/images/generations") + .url(nanoApiUrl) + .header("Authorization", "Bearer " + nanoToken) .header("Content-Type", "application/json") - .header("Authorization", "Bearer " + apiKey) -// .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("192.168.1.1", 8080))) .post(RequestBody.create( - MediaType.parse("application/json"), + MediaType.parse("application/json; charset=utf-8"), jsonBody )) .build(); - // 4. 发送同步请求(因方法需要返回值,使用execute而非enqueue) + Response response = OkHttpUtils.newCall(request).execute(); - // 5. 处理响应 + if (!response.isSuccessful()) { - // 非200状态:返回错误信息(假设ByteBodyRes有error字段) - String errorMsg = response.body() != null ? response.body().string() : "imgToImg error"; - throw new Exception("imgToImg error:" + errorMsg); + String errorMsg = response.body() != null ? response.body().string() : "generateImage error"; + throw new Exception("NanoBanana API调用失败: " + errorMsg); } - // 6. 解析成功响应为ByteBodyRes + if (response.body() == null) { - throw new Exception("imgToImg response null"); + throw new Exception("NanoBanana API响应为空"); } + String responseBody = response.body().string(); - return objectMapper.readValue(responseBody, ByteBodyRes.class); + return objectMapper.readValue(responseBody, NanoBananaResponse.class); + } + + @Override + public NanoBananaResponse generateImageWithReference(NanoBananaRequest req) throws Exception { + return generateImage(req); // 复用,imageUrls已在请求中设置 + } + + @Override + @Deprecated + public ByteBodyRes promptToImg(ByteBodyReq req) throws Exception { + // 兼容旧代码,实际已切换到NanoBanana + throw new UnsupportedOperationException("promptToImg 已弃用,请使用 generateImage"); + } + + @Override + @Deprecated + public ByteBodyRes imgToImg(ByteBodyReq req) throws Exception { + throw new UnsupportedOperationException("imgToImg 已弃用,请使用 generateImageWithReference"); } @Override public ByteBodyRes imgToVideo(ByteBodyReq req) throws Exception { + // 视频功能保持原有Byte实现 + if (StringUtils.isBlank(req.getPrompt())) { + throw new Exception("imgToVideo error:prompt is null"); + } - // 1. 验证请求参数(可选,根据业务需求) -// if (StringUtils.isBlank(req.getPrompt())) { -// throw new Exception("imgToVideo error:prompt is null"); -// } - - // 2. 构建请求体JSON(基于ByteBodyReq的字段) - // 注意:ByteBodyReq需包含与API参数对应的字段(model、prompt等) String jsonBody = objectMapper.writeValueAsString(req); - // 3. 构建请求 + Request request = new Request.Builder() .url(API_URL + "/contents/generations/tasks") .header("Content-Type", "application/json") @@ -94,51 +122,47 @@ public class ByteService implements IByteService { jsonBody )) .build(); - // 4. 发送同步请求(因方法需要返回值,使用execute而非enqueue) + Response response = OkHttpUtils.newCall(request).execute(); - // 5. 处理响应 + if (!response.isSuccessful()) { - // 非200状态:返回错误信息(假设ByteBodyRes有error字段) String errorMsg = response.body() != null ? response.body().string() : "imgToVideo error"; throw new Exception("imgToVideo error:" + errorMsg); } - // 6. 解析成功响应为ByteBodyRes + if (response.body() == null) { throw new Exception("imgToVideo response null"); } + String responseBody = response.body().string(); return objectMapper.readValue(responseBody, ByteBodyRes.class); } @Override public ByteBodyRes uploadVideo(String id) throws Exception { - // 1. 验证请求参数(可选,根据业务需求) + // 视频功能保持原有Byte实现 if (StringUtils.isBlank(id)) { throw new Exception("uploadVideo error:id is null"); } - // 2. 构建请求体JSON(基于ByteBodyReq的字段) - // 注意:ByteBodyReq需包含与API参数对应的字段(model、prompt等) - //String jsonBody = objectMapper.writeValueAsString(req); - // 3. 构建请求 Request request = new Request.Builder() .url(API_URL + "/contents/generations/tasks/" + id) .header("Content-Type", "application/json") .header("Authorization", "Bearer " + apiKey) .get() .build(); - // 4. 发送同步请求(因方法需要返回值,使用execute而非enqueue) + Response response = OkHttpUtils.newCall(request).execute(); - // 5. 处理响应 + if (!response.isSuccessful()) { - // 非200状态:返回错误信息(假设ByteBodyRes有error字段) String errorMsg = response.body() != null ? response.body().string() : "uploadVideo error"; throw new Exception("uploadVideo error:" + errorMsg); } - // 6. 解析成功响应为ByteBodyRes + if (response.body() == null) { throw new Exception("uploadVideo response null"); } + String responseBody = response.body().string(); return objectMapper.readValue(responseBody, ByteBodyRes.class); }