Commit 4932ce60d24e0b9615df4314927a2f14ecd5861a

Authored by YunaiV
1 parent cfeb4bdc

【代码评审】AI:AI 绘图的逻辑

yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiImageStatusEnum.java
@@ -14,6 +14,7 @@ import lombok.Getter; @@ -14,6 +14,7 @@ import lombok.Getter;
14 @Getter 14 @Getter
15 public enum AiImageStatusEnum { 15 public enum AiImageStatusEnum {
16 16
  17 + // TODO @fan:改成 10 生成中;20 成功;30 失败;其它可以去掉噢
17 SUBMIT("submit", "提交任务"), 18 SUBMIT("submit", "提交任务"),
18 WAITING("waiting", "等待"), 19 WAITING("waiting", "等待"),
19 IN_PROGRESS("in_progress", "进行中"), 20 IN_PROGRESS("in_progress", "进行中"),
@@ -22,6 +23,7 @@ public enum AiImageStatusEnum { @@ -22,6 +23,7 @@ public enum AiImageStatusEnum {
22 23
23 ; 24 ;
24 25
  26 + // TODO @fan:final 一下
25 private String status; 27 private String status;
26 28
27 private String name; 29 private String name;
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.java
@@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.*; @@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.*;
14 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 14 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
15 15
16 // TODO @芋艿:整理接口定义 16 // TODO @芋艿:整理接口定义
  17 +// TODO @fan:参考 AiChatMessageController 改下 swagger 注解、注释
17 /** 18 /**
18 * ai作图 19 * ai作图
19 * 20 *
@@ -28,20 +29,28 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; @@ -28,20 +29,28 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
28 @AllArgsConstructor 29 @AllArgsConstructor
29 public class AiImageController { 30 public class AiImageController {
30 31
  32 + // TODO @fan:使用 @Resource 注入哈;然后 aiImageService => imageService;
31 private final AiImageService aiImageService; 33 private final AiImageService aiImageService;
32 34
  35 + // TODO @fan:分页列表,建议是 getImagePage,包括接口 path 也建议改下哈;
  36 + // TODO @fan:@ModelAttribute 不需要哈;
  37 + // TODO @fan:这个要不搞成 my-page?因为是我的哈
33 @Operation(summary = "获取image列表", description = "dall3、midjourney") 38 @Operation(summary = "获取image列表", description = "dall3、midjourney")
34 @GetMapping("/list") 39 @GetMapping("/list")
35 public CommonResult<PageResult<AiImageListRespVO>> list(@Validated @ModelAttribute AiImageListReqVO req) { 40 public CommonResult<PageResult<AiImageListRespVO>> list(@Validated @ModelAttribute AiImageListReqVO req) {
  41 + // TODO @fan:import static,这样只要 success() 就行啦
36 return CommonResult.success(aiImageService.list(req)); 42 return CommonResult.success(aiImageService.list(req));
37 } 43 }
38 44
  45 + // TODO @fan:搞成 get-my?
  46 + // TODO @fan:方法名改下哈。
39 @Operation(summary = "获取image信息", description = "获取image信息") 47 @Operation(summary = "获取image信息", description = "获取image信息")
40 @GetMapping("/get") 48 @GetMapping("/get")
41 public CommonResult<AiImageListRespVO> get(@RequestParam("id") Long id) { 49 public CommonResult<AiImageListRespVO> get(@RequestParam("id") Long id) {
42 return CommonResult.success(aiImageService.get(id)); 50 return CommonResult.success(aiImageService.get(id));
43 } 51 }
44 52
  53 + // TODO @fan:建议把 dallDrawing、midjourney 融合成一个 draw 接口,异步绘制;然后返回一个 id 给前端;前端通过 get 接口轮询,直到获取到生成成功
45 @Operation(summary = "dall2/dall3绘画", description = "openAi dall3是付费的!") 54 @Operation(summary = "dall2/dall3绘画", description = "openAi dall3是付费的!")
46 @PostMapping("/dall") 55 @PostMapping("/dall")
47 public AiImageDallRespVO dallDrawing(@Validated @RequestBody AiImageDallReqVO req) { 56 public AiImageDallRespVO dallDrawing(@Validated @RequestBody AiImageDallReqVO req) {
@@ -62,6 +71,7 @@ public class AiImageController { @@ -62,6 +71,7 @@ public class AiImageController {
62 return success(null); 71 return success(null);
63 } 72 }
64 73
  74 + // TODO @fan:要不先不要 midjourneyOperate、cancelMidjourney 接口哈
65 @Operation(summary = "取消 midjourney 绘画", description = "取消 midjourney 绘画") 75 @Operation(summary = "取消 midjourney 绘画", description = "取消 midjourney 绘画")
66 @PostMapping("/cancel-midjourney") 76 @PostMapping("/cancel-midjourney")
67 public CommonResult<Void> cancelMidjourney(@RequestParam("id") Long id) { 77 public CommonResult<Void> cancelMidjourney(@RequestParam("id") Long id) {
@@ -69,10 +79,12 @@ public class AiImageController { @@ -69,10 +79,12 @@ public class AiImageController {
69 return success(null); 79 return success(null);
70 } 80 }
71 81
  82 + // TODO @fan:delete-my?需要校验是不是当前人哈
72 @Operation(summary = "删除绘画记录", description = "") 83 @Operation(summary = "删除绘画记录", description = "")
73 @DeleteMapping("/delete") 84 @DeleteMapping("/delete")
74 public CommonResult<Void> delete(@RequestParam("id") Long id) { 85 public CommonResult<Void> delete(@RequestParam("id") Long id) {
75 aiImageService.delete(id); 86 aiImageService.delete(id);
76 return success(null); 87 return success(null);
77 } 88 }
  89 +
78 } 90 }
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/convert/AiImageConvert.java
@@ -11,6 +11,7 @@ import org.mapstruct.factory.Mappers; @@ -11,6 +11,7 @@ import org.mapstruct.factory.Mappers;
11 11
12 import java.util.List; 12 import java.util.List;
13 13
  14 +// TODO @fan:convert 可以考虑去掉,使用 BeanUtils.copy 替代
14 /** 15 /**
15 * ai image convert 16 * ai image convert
16 * 17 *
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java
@@ -29,6 +29,8 @@ public class AiImageDO extends BaseDO { @@ -29,6 +29,8 @@ public class AiImageDO extends BaseDO {
29 @Schema(description = "提示词") 29 @Schema(description = "提示词")
30 private String prompt; 30 private String prompt;
31 31
  32 + // TODO @fan:要加一个平台字段;platform;因为一个平台,会有多种 model 模型;
  33 +
32 @Schema(description = "模型 dall2/dall3、MJ、NIJI") 34 @Schema(description = "模型 dall2/dall3、MJ、NIJI")
33 private String model; 35 private String model;
34 36
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java
@@ -39,6 +39,7 @@ import java.util.concurrent.LinkedBlockingQueue; @@ -39,6 +39,7 @@ import java.util.concurrent.LinkedBlockingQueue;
39 import java.util.concurrent.ThreadPoolExecutor; 39 import java.util.concurrent.ThreadPoolExecutor;
40 import java.util.concurrent.TimeUnit; 40 import java.util.concurrent.TimeUnit;
41 41
  42 +// TODO @fan:注释优化下哈
42 /** 43 /**
43 * ai 作图 44 * ai 作图
44 * 45 *
@@ -51,14 +52,23 @@ import java.util.concurrent.TimeUnit; @@ -51,14 +52,23 @@ import java.util.concurrent.TimeUnit;
51 @Slf4j 52 @Slf4j
52 public class AiImageServiceImpl implements AiImageService { 53 public class AiImageServiceImpl implements AiImageService {
53 54
  55 + // TODO @fan:使用 @Resource 注入
  56 +
  57 + // TODO @fan:imageMapper
54 private final AiImageMapper aiImageMapper; 58 private final AiImageMapper aiImageMapper;
  59 +
55 private final FileApi fileApi; 60 private final FileApi fileApi;
  61 +
56 private final OpenAiImageClient openAiImageClient; 62 private final OpenAiImageClient openAiImageClient;
  63 +
57 private final MidjourneyWebSocketStarter midjourneyWebSocketStarter; 64 private final MidjourneyWebSocketStarter midjourneyWebSocketStarter;
  65 +
58 private final MidjourneyInteractionsApi midjourneyInteractionsApi; 66 private final MidjourneyInteractionsApi midjourneyInteractionsApi;
  67 +
59 private static ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor( 68 private static ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(
60 3, 5, 1, TimeUnit.HOURS, new LinkedBlockingQueue<>(32)); 69 3, 5, 1, TimeUnit.HOURS, new LinkedBlockingQueue<>(32));
61 70
  71 + // TODO @fan:接 mj proxy
62 @PostConstruct 72 @PostConstruct
63 public void startMidjourney() { 73 public void startMidjourney() {
64 log.info("midjourney web socket starter..."); 74 log.info("midjourney web socket starter...");
@@ -75,6 +85,7 @@ public class AiImageServiceImpl implements AiImageService { @@ -75,6 +85,7 @@ public class AiImageServiceImpl implements AiImageService {
75 }); 85 });
76 } 86 }
77 87
  88 + // TODO @fan:1)分页,然后 loginUser 通过参数传入,这样 Service 无状态;2)另外,返回 DO;VO 的翻译,交给 Controller;3:还有,使用 BeanUtils 替代哈
78 @Override 89 @Override
79 public PageResult<AiImageListRespVO> list(AiImageListReqVO req) { 90 public PageResult<AiImageListRespVO> list(AiImageListReqVO req) {
80 // 获取登录用户 91 // 获取登录用户
@@ -92,29 +103,33 @@ public class AiImageServiceImpl implements AiImageService { @@ -92,29 +103,33 @@ public class AiImageServiceImpl implements AiImageService {
92 return result; 103 return result;
93 } 104 }
94 105
  106 + // TODO @fan:1)返回 DO;VO 的翻译,交给 Controller;2)还有,使用 BeanUtils 替代哈
95 @Override 107 @Override
96 public AiImageListRespVO get(Long id) { 108 public AiImageListRespVO get(Long id) {
97 AiImageDO aiImageDO = aiImageMapper.selectById(id); 109 AiImageDO aiImageDO = aiImageMapper.selectById(id);
98 return AiImageConvert.INSTANCE.convertAiImageListRespVO(aiImageDO); 110 return AiImageConvert.INSTANCE.convertAiImageListRespVO(aiImageDO);
99 } 111 }
100 112
  113 + // TODO @fan:1)loginUserId 通过 controller 传入;
101 @Override 114 @Override
102 public AiImageDallRespVO dallDrawing(AiImageDallReqVO req) { 115 public AiImageDallRespVO dallDrawing(AiImageDallReqVO req) {
103 Long loginUserId = SecurityFrameworkUtils.getLoginUserId(); 116 Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
104 // 保存数据库 117 // 保存数据库
  118 + // TODO @fan:1)使用 BeanUtils;2)使用链式调用哈;
105 AiImageDO aiImageDO = AiImageConvert.INSTANCE.convertAiImageDO(req); 119 AiImageDO aiImageDO = AiImageConvert.INSTANCE.convertAiImageDO(req);
106 aiImageDO.setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()); 120 aiImageDO.setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus());
107 aiImageDO.setUserId(loginUserId); 121 aiImageDO.setUserId(loginUserId);
108 aiImageMapper.insert(aiImageDO); 122 aiImageMapper.insert(aiImageDO);
109 // 异步执行 123 // 异步执行
  124 + // TODO @fan:使用 @Async 去调用哈;
110 EXECUTOR.execute(() -> { 125 EXECUTOR.execute(() -> {
111 try { 126 try {
112 -  
113 // 获取 model 127 // 获取 model
114 OpenAiImageModelEnum openAiImageModelEnum = OpenAiImageModelEnum.valueOfModel(req.getModel()); 128 OpenAiImageModelEnum openAiImageModelEnum = OpenAiImageModelEnum.valueOfModel(req.getModel());
115 OpenAiImageStyleEnum openAiImageStyleEnum = OpenAiImageStyleEnum.valueOfStyle(req.getStyle()); 129 OpenAiImageStyleEnum openAiImageStyleEnum = OpenAiImageStyleEnum.valueOfStyle(req.getStyle());
116 130
117 // 转换openai 参数 131 // 转换openai 参数
  132 + // TODO @fan:需要考虑,不同平台,参数不同;
118 OpenAiImageOptions openAiImageOptions = new OpenAiImageOptions(); 133 OpenAiImageOptions openAiImageOptions = new OpenAiImageOptions();
119 openAiImageOptions.setModel(openAiImageModelEnum.getModel()); 134 openAiImageOptions.setModel(openAiImageModelEnum.getModel());
120 openAiImageOptions.setStyle(openAiImageStyleEnum.getStyle()); 135 openAiImageOptions.setStyle(openAiImageStyleEnum.getStyle());
@@ -125,23 +140,16 @@ public class AiImageServiceImpl implements AiImageService { @@ -125,23 +140,16 @@ public class AiImageServiceImpl implements AiImageService {
125 // 图片保存到服务器 140 // 图片保存到服务器
126 String filePath = fileApi.createFile(HttpUtil.downloadBytes(imageGeneration.getOutput().getUrl())); 141 String filePath = fileApi.createFile(HttpUtil.downloadBytes(imageGeneration.getOutput().getUrl()));
127 // 更新数据库 142 // 更新数据库
128 - aiImageMapper.updateById(  
129 - new AiImageDO()  
130 - .setId(aiImageDO.getId())  
131 - .setStatus(AiImageStatusEnum.COMPLETE.getStatus())  
132 - .setPicUrl(filePath)  
133 - .setOriginalPicUrl(imageGeneration.getOutput().getUrl()) 143 + aiImageMapper.updateById(new AiImageDO().setId(aiImageDO.getId()).setStatus(AiImageStatusEnum.COMPLETE.getStatus())
  144 + .setPicUrl(filePath).setOriginalPicUrl(imageGeneration.getOutput().getUrl())
134 ); 145 );
135 } catch (AiException aiException) { 146 } catch (AiException aiException) {
136 - // 更新错误信息  
137 - aiImageMapper.updateById(  
138 - new AiImageDO()  
139 - .setId(aiImageDO.getId())  
140 - .setStatus(AiImageStatusEnum.FAIL.getStatus())  
141 - .setErrorMessage(aiException.getMessage())  
142 - ); 147 + // TODO @fan:错误日志,也打印下哈;因为 aiException.getMessage() 比较精简;
  148 + aiImageMapper.updateById(new AiImageDO().setId(aiImageDO.getId()).setStatus(AiImageStatusEnum.FAIL.getStatus())
  149 + .setErrorMessage(aiException.getMessage()));
143 } 150 }
144 }); 151 });
  152 + // TODO @fan:返回 id 就可以啦
145 // 转换 AiImageDallDrawingRespVO 153 // 转换 AiImageDallDrawingRespVO
146 return AiImageConvert.INSTANCE.convertAiImageDallDrawingRespVO(aiImageDO); 154 return AiImageConvert.INSTANCE.convertAiImageDallDrawingRespVO(aiImageDO);
147 } 155 }
@@ -188,6 +196,7 @@ public class AiImageServiceImpl implements AiImageService { @@ -188,6 +196,7 @@ public class AiImageServiceImpl implements AiImageService {
188 ); 196 );
189 } 197 }
190 198
  199 + // TODO @fan:1)需要校验存在;2)需要校验属于我;
191 @Override 200 @Override
192 public void delete(Long id) { 201 public void delete(Long id) {
193 // 校验记录是否存在 202 // 校验记录是否存在