Commit 458f5acdf0c0929fc942bda031338237913ecad1

Authored by YunaiV
1 parent c1f2e490

【代码优化】AI:Image 图片的列表、详情等接口

yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.java
1 1 package cn.iocoder.yudao.module.ai.controller.admin.image;
2 2  
  3 +import cn.hutool.core.util.ObjUtil;
3 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  5 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
4 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 7 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 8 import cn.iocoder.yudao.module.ai.client.vo.MidjourneyNotifyReqVO;
7 9 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallReqVO;
8   -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageListReqVO;
9 10 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO;
10   -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImagePageMyRespVO;
  11 +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageRespVO;
11 12 import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
12 13 import cn.iocoder.yudao.module.ai.service.image.AiImageService;
13 14 import io.swagger.v3.oas.annotations.Operation;
... ... @@ -21,35 +22,30 @@ import org.springframework.web.bind.annotation.*;
21 22 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
22 23 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
23 24  
24   -@Tag(name = "管理后台 - Ai 绘画")
  25 +@Tag(name = "管理后台 - AI 绘画")
25 26 @RestController
26 27 @RequestMapping("/ai/image")
27 28 @Slf4j
28 29 public class AiImageController {
29 30  
30 31 @Resource
31   - private AiImageService aiImageService;
  32 + private AiImageService imageService;
32 33  
33 34 @Operation(summary = "获取【我的】绘图分页")
34 35 @GetMapping("/my-page")
35   - public CommonResult<PageResult<AiImagePageMyRespVO>> getImagePageMy(@Validated AiImageListReqVO req) {
36   - // 转换 resp
37   - PageResult<AiImageDO> pageResult = aiImageService.getImagePageMy(getLoginUserId(), req);
38   - // 转换 PageResult<AiImageListRespVO> 返回
39   - PageResult<AiImagePageMyRespVO> result = new PageResult<>();
40   - result.setTotal(pageResult.getTotal());
41   - result.setList(BeanUtils.toBean(pageResult.getList(), AiImagePageMyRespVO.class));
42   - return success(result);
  36 + public CommonResult<PageResult<AiImageRespVO>> getImagePageMy(@Validated PageParam pageReqVO) {
  37 + PageResult<AiImageDO> pageResult = imageService.getImagePageMy(getLoginUserId(), pageReqVO);
  38 + return success(BeanUtils.toBean(pageResult, AiImageRespVO.class));
43 39 }
44 40  
45   - // TODO @fan:类似 /my-page 的建议
46   - @Operation(summary = "获取【我的】绘图记录", description = "...")
  41 + @Operation(summary = "获取【我的】绘图记录")
47 42 @GetMapping("/get-my")
48   - public CommonResult<AiImagePageMyRespVO> getMy(@RequestParam("id") Long id) {
49   - // 获取 image 信息
50   - AiImageDO imageDO = aiImageService.getMy(id);
51   - // 转 resp 并返回
52   - return CommonResult.success(BeanUtils.toBean(imageDO, AiImagePageMyRespVO.class));
  43 + public CommonResult<AiImageRespVO> getImageMy(@RequestParam("id") Long id) {
  44 + AiImageDO image = imageService.getImage(id);
  45 + if (image == null || ObjUtil.notEqual(getLoginUserId(), image.getUserId())) {
  46 + return success(null);
  47 + }
  48 + return success(BeanUtils.toBean(image, AiImageRespVO.class));
53 49 }
54 50  
55 51 // TODO @fan:建议把 dallDrawing、midjourney 融合成一个 draw 接口,异步绘制;然后返回一个 id 给前端;前端通过 get 接口轮询,直到获取到生成成功
... ... @@ -58,14 +54,15 @@ public class AiImageController {
58 54 @Operation(summary = "dall2/dall3绘画", description = "openAi dall3是付费的!")
59 55 @PostMapping("/dall")
60 56 public CommonResult<Long> dall(@Validated @RequestBody AiImageDallReqVO req) {
61   - return success(aiImageService.dall(getLoginUserId(), req));
  57 + return success(imageService.dall(getLoginUserId(), req));
62 58 }
63 59  
64 60 @Operation(summary = "删除【我的】绘画记录")
65   - @DeleteMapping("/delete-id-my")
  61 + @DeleteMapping("/delete-my")
66 62 @Parameter(name = "id", required = true, description = "绘画编号", example = "1024")
67   - public CommonResult<Boolean> deleteIdMy(@RequestParam("id") Long id) {
68   - return success(aiImageService.deleteIdMy(id, getLoginUserId()));
  63 + public CommonResult<Boolean> deleteImageMy(@RequestParam("id") Long id) {
  64 + imageService.deleteImageMy(id, getLoginUserId());
  65 + return success(true);
69 66 }
70 67  
71 68 // ================ midjourney 接口
... ... @@ -73,12 +70,14 @@ public class AiImageController {
73 70 @Operation(summary = "midjourney-imagine 绘画", description = "...")
74 71 @PostMapping("/midjourney/imagine")
75 72 public CommonResult<Long> midjourneyImagine(@Validated @RequestBody AiImageMidjourneyImagineReqVO req) {
76   - return success(aiImageService.midjourneyImagine(getLoginUserId(), req));
  73 + return success(imageService.midjourneyImagine(getLoginUserId(), req));
77 74 }
78 75  
  76 + // TODO @fan:可以考虑,复用 AiImageDallRespVO,统一成 AIImageRespVO
79 77 @Operation(summary = "midjourney proxy - 回调通知")
80 78 @RequestMapping("/midjourney-notify")
81 79 public CommonResult<Boolean> midjourneyNotify(MidjourneyNotifyReqVO notifyReqVO) {
82   - return success(aiImageService.midjourneyNotify(getLoginUserId(), notifyReqVO));
  80 + return success(imageService.midjourneyNotify(getLoginUserId(), notifyReqVO));
83 81 }
  82 +
84 83 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageListReqVO.java deleted
1   -package cn.iocoder.yudao.module.ai.controller.admin.image.vo;
2   -
3   -import cn.iocoder.yudao.framework.common.pojo.PageParam;
4   -import lombok.Data;
5   -import lombok.experimental.Accessors;
6   -
7   -/**
8   - * AI Image 我的图片列表 req
9   - *
10   - * @author fansili
11   - * @time 2024/4/28 17:42
12   - * @since 1.0
13   - */
14   -@Data
15   -@Accessors(chain = true)
16   -public class AiImageListReqVO extends PageParam {
17   -
18   -}
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageMidjourneyRes.java deleted
1   -package cn.iocoder.yudao.module.ai.controller.admin.image.vo;
2   -
3   -import lombok.Data;
4   -import lombok.experimental.Accessors;
5   -
6   -/**
7   - * midjourney
8   - *
9   - * @author fansili
10   - * @time 2024/4/28 17:42
11   - * @since 1.0
12   - */
13   -@Data
14   -@Accessors(chain = true)
15   -public class AiImageMidjourneyRes {
16   -}
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImagePageMyRespVO.java renamed to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageRespVO.java
1 1 package cn.iocoder.yudao.module.ai.controller.admin.image.vo;
2 2  
3   -import cn.iocoder.yudao.framework.common.pojo.PageParam;
4 3 import io.swagger.v3.oas.annotations.media.Schema;
5 4 import lombok.Data;
6   -import lombok.experimental.Accessors;
7 5  
8 6 import java.util.Map;
9 7  
10   -// TODO @fan:可以考虑,复用 AiImageDallRespVO,统一成 AIImageRespVO
11   -/**
12   - * midjourney req
13   - *
14   - * @author fansili
15   - * @time 2024/4/28 17:42
16   - * @since 1.0
17   - */
  8 +// TODO @芋艿:完善 swagger 注解
18 9 @Data
19   -@Accessors(chain = true)
20   -public class AiImagePageMyRespVO extends PageParam {
  10 +public class AiImageRespVO {
21 11  
22 12 @Schema(description = "id编号", example = "1")
23 13 private Long id;
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/image/AiImageMapper.java
1 1 package cn.iocoder.yudao.module.ai.dal.mysql.image;
2 2  
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
3 5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
4 6 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
5 7 import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
... ... @@ -35,4 +37,10 @@ public interface AiImageMapper extends BaseMapperX&lt;AiImageDO&gt; {
35 37 return this.selectOne(new LambdaQueryWrapperX<AiImageDO>().eq(AiImageDO::getJobId, id));
36 38 }
37 39  
  40 + default PageResult<AiImageDO> selectPage(Long userId, PageParam pageReqVO) {
  41 + return selectPage(pageReqVO, new LambdaQueryWrapperX<AiImageDO>()
  42 + .eq(AiImageDO::getUserId, userId)
  43 + .orderByDesc(AiImageDO::getId));
  44 + }
  45 +
38 46 }
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageService.java
1 1 package cn.iocoder.yudao.module.ai.service.image;
2 2  
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
3 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 5 import cn.iocoder.yudao.module.ai.client.vo.MidjourneyNotifyReqVO;
5 6 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallReqVO;
6   -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageListReqVO;
7 7 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO;
8   -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyOperateReqVO;
9 8 import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
10 9  
11 10 /**
12   - * ai 作图
  11 + * AI 绘图 Service 接口
13 12 *
14 13 * @author fansili
15   - * @time 2024/4/25 15:50
16   - * @since 1.0
17 14 */
18 15 public interface AiImageService {
19 16  
20 17 /**
21   - * ai绘画 - 列表
  18 + * 获取【我的】绘图分页
22 19 *
23   - * @param loginUserId
24   - * @param req
25   - * @return
  20 + * @param userId 用户编号
  21 + * @param pageReqVO 分页条件
  22 + * @return 绘图分页
26 23 */
27   - PageResult<AiImageDO> getImagePageMy(Long loginUserId, AiImageListReqVO req);
  24 + PageResult<AiImageDO> getImagePageMy(Long userId, PageParam pageReqVO);
28 25  
29 26 /**
30   - * 获取 - image 信息
  27 + * 获得绘图记录
31 28 *
32   - * @param id
33   - * @return
  29 + * @param id 绘图编号
  30 + * @return 绘图记录
34 31 */
35   - AiImageDO getMy(Long id);
  32 + AiImageDO getImage(Long id);
36 33  
37 34 /**
38 35 * ai绘画 - dall2/dall3 绘画
... ... @@ -52,19 +49,12 @@ public interface AiImageService {
52 49 Long midjourneyImagine(Long loginUserId, AiImageMidjourneyImagineReqVO req);
53 50  
54 51 /**
55   - * midjourney 操作(u1、u2、放大、换一批...)
56   - *
57   - * @param req
58   - */
59   - void midjourneyOperate(AiImageMidjourneyOperateReqVO req);
60   -
61   - /**
62   - * 删除 - image 记录
  52 + * 删除【我的】绘画记录
63 53 *
64   - * @param id
65   - * @param loginUserId
  54 + * @param id 绘画编号
  55 + * @param userId 用户编号
66 56 */
67   - Boolean deleteIdMy(Long id, Long loginUserId);
  57 + void deleteImageMy(Long id, Long userId);
68 58  
69 59 /**
70 60 * midjourney proxy - 回调通知
... ...
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java
... ... @@ -2,17 +2,17 @@ package cn.iocoder.yudao.module.ai.service.image;
2 2  
3 3 import cn.hutool.core.bean.BeanUtil;
4 4 import cn.hutool.core.util.StrUtil;
  5 +import cn.hutool.core.util.ObjUtil;
  6 +import cn.hutool.extra.spring.SpringUtil;
5 7 import cn.hutool.http.HttpUtil;
6 8 import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum;
7 9 import cn.iocoder.yudao.framework.ai.core.enums.OpenAiImageModelEnum;
8 10 import cn.iocoder.yudao.framework.ai.core.enums.OpenAiImageStyleEnum;
9 11 import cn.iocoder.yudao.framework.ai.core.exception.AiException;
10   -import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
  12 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
11 13 import cn.iocoder.yudao.framework.common.pojo.PageResult;
12 14 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
13   -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
14 15 import cn.iocoder.yudao.module.ai.AiCommonConstants;
15   -import cn.iocoder.yudao.module.ai.ErrorCodeConstants;
16 16 import cn.iocoder.yudao.module.ai.client.MidjourneyProxyClient;
17 17 import cn.iocoder.yudao.module.ai.client.enums.MidjourneyModelEnum;
18 18 import cn.iocoder.yudao.module.ai.client.enums.MidjourneySubmitCodeEnum;
... ... @@ -21,9 +21,7 @@ import cn.iocoder.yudao.module.ai.client.vo.MidjourneyImagineReqVO;
21 21 import cn.iocoder.yudao.module.ai.client.vo.MidjourneyNotifyReqVO;
22 22 import cn.iocoder.yudao.module.ai.client.vo.MidjourneySubmitRespVO;
23 23 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDallReqVO;
24   -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageListReqVO;
25 24 import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO;
26   -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyOperateReqVO;
27 25 import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
28 26 import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper;
29 27 import cn.iocoder.yudao.module.ai.enums.AiImagePublicStatusEnum;
... ... @@ -46,12 +44,12 @@ import org.springframework.transaction.annotation.Transactional;
46 44 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
47 45  
48 46  
  47 +import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.AI_IMAGE_NOT_EXISTS;
  48 +
49 49 /**
50   - * AI 绘画(接入 dall2/dall3、midjourney)
  50 + * AI 绘画 Service 实现类
51 51 *
52 52 * @author fansili
53   - * @time 2024/4/25 15:51
54   - * @since 1.0
55 53 */
56 54 @Service
57 55 @Slf4j
... ... @@ -59,10 +57,13 @@ public class AiImageServiceImpl implements AiImageService {
59 57  
60 58 @Resource
61 59 private AiImageMapper imageMapper;
  60 +
62 61 @Resource
63 62 private FileApi fileApi;
  63 +
64 64 @Resource
65 65 private OpenAiImageClient openAiImageClient;
  66 +
66 67 @Autowired
67 68 private MidjourneyProxyClient midjourneyProxyClient;
68 69  
... ... @@ -70,16 +71,12 @@ public class AiImageServiceImpl implements AiImageService {
70 71 private String midjourneyNotifyUrl;
71 72  
72 73 @Override
73   - public PageResult<AiImageDO> getImagePageMy(Long loginUserId, AiImageListReqVO req) {
74   - // 查询当前用户下所有的绘画记录
75   - return imageMapper.selectPage(req,
76   - new LambdaQueryWrapperX<AiImageDO>()
77   - .eq(AiImageDO::getUserId, loginUserId)
78   - .orderByDesc(AiImageDO::getId));
  74 + public PageResult<AiImageDO> getImagePageMy(Long userId, PageParam pageReqVO) {
  75 + return imageMapper.selectPage(userId, pageReqVO);
79 76 }
80 77  
81 78 @Override
82   - public AiImageDO getMy(Long id) {
  79 + public AiImageDO getImage(Long id) {
83 80 return imageMapper.selectById(id);
84 81 }
85 82  
... ... @@ -95,7 +92,7 @@ public class AiImageServiceImpl implements AiImageService {
95 92 .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus());
96 93 imageMapper.insert(aiImageDO);
97 94 // 异步执行
98   - doDall(aiImageDO, req);
  95 + getSelf().doDall(aiImageDO, req);
99 96 // 转换 AiImageDallDrawingRespVO
100 97 return aiImageDO.getId();
101 98 }
... ... @@ -185,42 +182,15 @@ public class AiImageServiceImpl implements AiImageService {
185 182 return aiImageDO.getId();
186 183 }
187 184  
188   - @Transactional(rollbackFor = Exception.class)
189   - @Override
190   - public void midjourneyOperate(AiImageMidjourneyOperateReqVO req) {
191   -// // 校验是否存在
192   -// AiImageDO aiImageDO = validateExists(req.getId());
193   -// // 获取 midjourneyOperations
194   -// List<AiImageMidjourneyOperationsVO> midjourneyOperations = getMidjourneyOperations(aiImageDO);
195   -// // 校验 OperateId 是否存在
196   -// AiImageMidjourneyOperationsVO midjourneyOperationsVO = validateMidjourneyOperationsExists(midjourneyOperations, req.getOperateId());
197   -// // 校验 messageId
198   -// validateMessageId(aiImageDO.getMjNonceId(), req.getMessageId());
199   -// // 获取 mjOperationName
200   -// String mjOperationName = midjourneyOperationsVO.getLabel();
201   -// // 保存一个 image 任务记录
202   -// // todo
203   -//// doSave(aiImageDO.getPrompt(), aiImageDO.getSize(), aiImageDO.getModel(),
204   -//// null, null, AiImageStatusEnum.SUBMIT, null,
205   -//// req.getMessageId(), req.getOperateId(), mjOperationName);
206   -// // 提交操作
207   -// midjourneyInteractionsApi.reRoll(
208   -// new ReRollReq()
209   -// .setCustomId(req.getOperateId())
210   -// .setMessageId(req.getMessageId())
211   -// );
212   - }
213   -
214 185 @Override
215   - public Boolean deleteIdMy(Long id, Long userId) {
216   - // 校验是否存在,并获取 image
217   - AiImageDO image = validateExists(id);
218   - // 是否属于当前用户
219   - if (!image.getUserId().equals(userId)) {
220   - throw exception(ErrorCodeConstants.AI_IMAGE_NOT_EXISTS);
  186 + public void deleteImageMy(Long id, Long userId) {
  187 + // 1. 校验是否存在
  188 + AiImageDO image = validateImageExists(id);
  189 + if (ObjUtil.notEqual(image.getUserId(), userId)) {
  190 + throw exception(AI_IMAGE_NOT_EXISTS);
221 191 }
222 192 // 删除记录
223   - return imageMapper.deleteById(id) > 0;
  193 + imageMapper.deleteById(id);
224 194 }
225 195  
226 196 @Override
... ... @@ -255,11 +225,21 @@ public class AiImageServiceImpl implements AiImageService {
255 225 return true;
256 226 }
257 227  
258   - private AiImageDO validateExists(Long id) {
259   - AiImageDO aiImageDO = imageMapper.selectById(id);
260   - if (aiImageDO == null) {
261   - throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MIDJOURNEY_IMAGINE_FAIL);
  228 + private AiImageDO validateImageExists(Long id) {
  229 + AiImageDO image = imageMapper.selectById(id);
  230 + if (image == null) {
  231 + throw exception(AI_IMAGE_NOT_EXISTS);
262 232 }
263   - return aiImageDO;
  233 + return image;
264 234 }
  235 +
  236 + /**
  237 + * 获得自身的代理对象,解决 AOP 生效问题
  238 + *
  239 + * @return 自己
  240 + */
  241 + private AiImageServiceImpl getSelf() {
  242 + return SpringUtil.getBean(getClass());
  243 + }
  244 +
265 245 }
... ...