Commit 4932ce60d24e0b9615df4314927a2f14ecd5861a
1 parent
cfeb4bdc
【代码评审】AI:AI 绘图的逻辑
Showing
5 changed files
with
40 additions
and
14 deletions
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
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 | // 校验记录是否存在 |