From e40c4a975bb5c0a7f8f8da2120650819d9dc7bb3 Mon Sep 17 00:00:00 2001 From: zhangsan <646228430@qq.com> Date: Mon, 7 Apr 2025 17:23:26 +0800 Subject: [PATCH] =?UTF-8?q?4.7=20=E9=98=BF=E9=87=8C=E4=BA=91=E7=99=BE?= =?UTF-8?q?=E7=82=BCAI=E6=89=A9=E5=9B=BE=E6=8E=A5=E5=8F=A3=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/aliyunai/AliYunAiApi.java | 87 ++++++++++++++ .../model/CreateOutPaintingTaskRequest.java | 112 ++++++++++++++++++ .../model/CreateOutPaintingTaskResponse.java | 60 ++++++++++ .../model/GetOutPaintingTaskResponse.java | 111 +++++++++++++++++ .../controller/PictureController.java | 34 +++++- .../manager/upload/PictureUploadTemplate.java | 1 + .../manager/upload/UrlPictureUpload.java | 30 +++-- .../CreatePictureOutPaintingTaskRequest.java | 24 ++++ .../service/PictureService.java | 8 ++ .../service/impl/PictureServiceImpl.java | 26 ++++ src/main/resources/application.yml | 6 +- 11 files changed, 486 insertions(+), 13 deletions(-) create mode 100644 src/main/java/edu/whut/smilepicturebackend/api/aliyunai/AliYunAiApi.java create mode 100644 src/main/java/edu/whut/smilepicturebackend/api/aliyunai/model/CreateOutPaintingTaskRequest.java create mode 100644 src/main/java/edu/whut/smilepicturebackend/api/aliyunai/model/CreateOutPaintingTaskResponse.java create mode 100644 src/main/java/edu/whut/smilepicturebackend/api/aliyunai/model/GetOutPaintingTaskResponse.java create mode 100644 src/main/java/edu/whut/smilepicturebackend/model/dto/picture/CreatePictureOutPaintingTaskRequest.java diff --git a/src/main/java/edu/whut/smilepicturebackend/api/aliyunai/AliYunAiApi.java b/src/main/java/edu/whut/smilepicturebackend/api/aliyunai/AliYunAiApi.java new file mode 100644 index 0000000..04b3084 --- /dev/null +++ b/src/main/java/edu/whut/smilepicturebackend/api/aliyunai/AliYunAiApi.java @@ -0,0 +1,87 @@ +package edu.whut.smilepicturebackend.api.aliyunai; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import cn.hutool.json.JSONUtil; +import edu.whut.smilepicturebackend.api.aliyunai.model.CreateOutPaintingTaskRequest; +import edu.whut.smilepicturebackend.api.aliyunai.model.CreateOutPaintingTaskResponse; +import edu.whut.smilepicturebackend.api.aliyunai.model.GetOutPaintingTaskResponse; +import edu.whut.smilepicturebackend.exception.BusinessException; +import edu.whut.smilepicturebackend.exception.ErrorCode; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +public class AliYunAiApi { + + // 读取配置文件 + @Value("${smile-picture.aliyun.apiKey}") + private String apiKey; + + // 创建任务地址 + public static final String CREATE_OUT_PAINTING_TASK_URL = "https://dashscope.aliyuncs.com/api/v1/services/aigc/image2image/out-painting"; + + // 查询任务状态 + public static final String GET_OUT_PAINTING_TASK_URL = "https://dashscope.aliyuncs.com/api/v1/tasks/%s"; + + + /** + * 创建任务 + * + * @param createOutPaintingTaskRequest + * @return + */ + public CreateOutPaintingTaskResponse createOutPaintingTask(CreateOutPaintingTaskRequest createOutPaintingTaskRequest) { + if (createOutPaintingTaskRequest == null) { + throw new BusinessException(ErrorCode.OPERATION_ERROR, "扩图参数为空"); + } + // 发送请求 + HttpRequest httpRequest = HttpRequest.post(CREATE_OUT_PAINTING_TASK_URL) + .header("Authorization", "Bearer " + apiKey) + // 必须开启异步处理 enable + .header("X-DashScope-Async", "enable") + .header("Content-Type", "application/json") + .body(JSONUtil.toJsonStr(createOutPaintingTaskRequest)); + // 处理响应 + //这段代码会在 try 块结束时(正常返回或抛出异常)自动调用 httpResponse.close(),从而释放所有底层资源 + try (HttpResponse httpResponse = httpRequest.execute()) { + if (!httpResponse.isOk()) { + log.error("请求异常:{}", httpResponse.body()); + throw new BusinessException(ErrorCode.OPERATION_ERROR, "AI 扩图失败"); + } + CreateOutPaintingTaskResponse createOutPaintingTaskResponse = JSONUtil.toBean(httpResponse.body(), CreateOutPaintingTaskResponse.class); + if (createOutPaintingTaskResponse.getCode() != null) { + String errorMessage = createOutPaintingTaskResponse.getMessage(); + log.error("请求异常:{}", errorMessage); + throw new BusinessException(ErrorCode.OPERATION_ERROR, "AI 扩图失败," + errorMessage); + } + return createOutPaintingTaskResponse; + } + } + + /** + * 查询创建的任务结果 + * + * @param taskId + * @return + */ + public GetOutPaintingTaskResponse getOutPaintingTask(String taskId) { + if (StrUtil.isBlank(taskId)) { + throw new BusinessException(ErrorCode.OPERATION_ERROR, "任务 ID 不能为空"); + } + // 处理响应 + String url = String.format(GET_OUT_PAINTING_TASK_URL, taskId); + try (HttpResponse httpResponse = HttpRequest.get(url) + .header("Authorization", "Bearer " + apiKey) + .execute()) { + if (!httpResponse.isOk()) { + log.error("请求异常:{}", httpResponse.body()); + throw new BusinessException(ErrorCode.OPERATION_ERROR, "获取任务结果失败"); + } + return JSONUtil.toBean(httpResponse.body(), GetOutPaintingTaskResponse.class); + } + } +} diff --git a/src/main/java/edu/whut/smilepicturebackend/api/aliyunai/model/CreateOutPaintingTaskRequest.java b/src/main/java/edu/whut/smilepicturebackend/api/aliyunai/model/CreateOutPaintingTaskRequest.java new file mode 100644 index 0000000..b39fa33 --- /dev/null +++ b/src/main/java/edu/whut/smilepicturebackend/api/aliyunai/model/CreateOutPaintingTaskRequest.java @@ -0,0 +1,112 @@ +package edu.whut.smilepicturebackend.api.aliyunai.model; + +import cn.hutool.core.annotation.Alias; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * 创建扩图任务请求 + */ +@Data +public class CreateOutPaintingTaskRequest implements Serializable { + + /** + * 模型,例如 "image-out-painting" + */ + private String model = "image-out-painting"; + + /** + * 输入图像信息 + */ + private Input input; + + /** + * 图像处理参数 + */ + private Parameters parameters; + + @Data + public static class Input { + /** + * 必选,图像 URL + */ + @Alias("image_url") //java类中一般写驼峰式的,但是大模型接收的是image_url 所以用@Alias + private String imageUrl; + } + + @Data + public static class Parameters implements Serializable { + /** + * 可选,逆时针旋转角度,默认值 0,取值范围 [0, 359] + */ + private Integer angle; + + /** + * 可选,输出图像的宽高比,默认空字符串,不设置宽高比 + * 可选值:["", "1:1", "3:4", "4:3", "9:16", "16:9"] + */ + @Alias("output_ratio") + private String outputRatio; + + /** + * 可选,图像居中,在水平方向上按比例扩展,默认值 1.0,范围 [1.0, 3.0] + */ + @Alias("x_scale") + @JsonProperty("xScale") + private Float xScale; + + /** + * 可选,图像居中,在垂直方向上按比例扩展,默认值 1.0,范围 [1.0, 3.0] + */ + @Alias("y_scale") + @JsonProperty("yScale") + private Float yScale; + + /** + * 可选,在图像上方添加像素,默认值 0 + */ + @Alias("top_offset") + private Integer topOffset; + + /** + * 可选,在图像下方添加像素,默认值 0 + */ + @Alias("bottom_offset") + private Integer bottomOffset; + + /** + * 可选,在图像左侧添加像素,默认值 0 + */ + @Alias("left_offset") + private Integer leftOffset; + + /** + * 可选,在图像右侧添加像素,默认值 0 + */ + @Alias("right_offset") + private Integer rightOffset; + + /** + * 可选,开启图像最佳质量模式,默认值 false + * 若为 true,耗时会成倍增加 + */ + @Alias("best_quality") + private Boolean bestQuality; + + /** + * 可选,限制模型生成的图像文件大小,默认值 true + * - 单边长度 <= 10000:输出图像文件大小限制为 5MB 以下 + * - 单边长度 > 10000:输出图像文件大小限制为 10MB 以下 + */ + @Alias("limit_image_size") + private Boolean limitImageSize; + + /** + * 可选,添加 "Generated by AI" 水印,默认值 true + */ + @Alias("add_watermark") + private Boolean addWatermark = false; + } +} \ No newline at end of file diff --git a/src/main/java/edu/whut/smilepicturebackend/api/aliyunai/model/CreateOutPaintingTaskResponse.java b/src/main/java/edu/whut/smilepicturebackend/api/aliyunai/model/CreateOutPaintingTaskResponse.java new file mode 100644 index 0000000..40a9607 --- /dev/null +++ b/src/main/java/edu/whut/smilepicturebackend/api/aliyunai/model/CreateOutPaintingTaskResponse.java @@ -0,0 +1,60 @@ +package edu.whut.smilepicturebackend.api.aliyunai.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 创建扩图任务响应类 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class CreateOutPaintingTaskResponse { + + private Output output; + + /** + * 表示任务的输出信息 + */ + @Data + public static class Output { + + /** + * 任务 ID + */ + private String taskId; + + /** + * 任务状态 + *
接口成功请求不会返回该参数。
+ */ + private String code; + + /** + * 接口错误信息。 + *接口成功请求不会返回该参数。
+ */ + private String message; + + /** + * 请求唯一标识。 + *可用于请求明细溯源和问题排查。
+ */ + private String requestId; + +} \ No newline at end of file diff --git a/src/main/java/edu/whut/smilepicturebackend/api/aliyunai/model/GetOutPaintingTaskResponse.java b/src/main/java/edu/whut/smilepicturebackend/api/aliyunai/model/GetOutPaintingTaskResponse.java new file mode 100644 index 0000000..58ee6aa --- /dev/null +++ b/src/main/java/edu/whut/smilepicturebackend/api/aliyunai/model/GetOutPaintingTaskResponse.java @@ -0,0 +1,111 @@ +package edu.whut.smilepicturebackend.api.aliyunai.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 查询扩图任务响应类 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class GetOutPaintingTaskResponse { + + /** + * 请求唯一标识 + */ + private String requestId; + + /** + * 输出信息 + */ + private Output output; + + /** + * 表示任务的输出信息 + */ + @Data + public static class Output { + + /** + * 任务 ID + */ + private String taskId; + + /** + * 任务状态 + *接口成功请求不会返回该参数
+ */ + private String code; + + /** + * 接口错误信息 + *接口成功请求不会返回该参数
+ */ + private String message; + + /** + * 任务指标信息 + */ + private TaskMetrics taskMetrics; + } + + /** + * 表示任务的统计信息 + */ + @Data + public static class TaskMetrics { + + /** + * 总任务数 + */ + private Integer total; + + /** + * 成功任务数 + */ + private Integer succeeded; + + /** + * 失败任务数 + */ + private Integer failed; + } +} \ No newline at end of file diff --git a/src/main/java/edu/whut/smilepicturebackend/controller/PictureController.java b/src/main/java/edu/whut/smilepicturebackend/controller/PictureController.java index 39c60e1..c6e0ec9 100644 --- a/src/main/java/edu/whut/smilepicturebackend/controller/PictureController.java +++ b/src/main/java/edu/whut/smilepicturebackend/controller/PictureController.java @@ -1,7 +1,11 @@ package edu.whut.smilepicturebackend.controller; +import cn.hutool.core.util.StrUtil; import cn.hutool.json.JSONUtil; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import edu.whut.smilepicturebackend.annotation.AuthCheck; +import edu.whut.smilepicturebackend.api.aliyunai.AliYunAiApi; +import edu.whut.smilepicturebackend.api.aliyunai.model.CreateOutPaintingTaskResponse; +import edu.whut.smilepicturebackend.api.aliyunai.model.GetOutPaintingTaskResponse; import edu.whut.smilepicturebackend.api.imagesearch.ImageSearchApiFacade; import edu.whut.smilepicturebackend.api.imagesearch.model.ImageSearchResult; import edu.whut.smilepicturebackend.common.BaseResponse; @@ -24,7 +28,6 @@ import edu.whut.smilepicturebackend.service.UserService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; -import org.springframework.util.DigestUtils; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; @@ -42,6 +45,7 @@ public class PictureController { private final UserService userService; private final PictureService pictureService; private final SpaceService spaceService; + private final AliYunAiApi aliYunAiApi; @@ -305,4 +309,32 @@ public class PictureController { pictureService.editPictureByBatch(pictureEditByBatchRequest, loginUser); return ResultUtils.success(true); } + + /** + *创建 AI 扩图任务 + * @param createPictureOutPaintingTaskRequest + * @param request + * @return + */ + @PostMapping("/out_painting/create_task") +// @SaSpaceCheckPermission(value = SpaceUserPermissionConstant.PICTURE_EDIT) + public BaseResponse