Commit 1ac1c007ad5f0128712e841c5da59568ec202ecf
1 parent
e781129d
【解决todo】修改命名、方法返回、链式调用、显示保存、代码逻辑换货
Showing
4 changed files
with
66 additions
and
68 deletions
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.java
| @@ -74,16 +74,18 @@ public class AiImageController { | @@ -74,16 +74,18 @@ public class AiImageController { | ||
| 74 | @Operation(summary = "Midjourney 回调通知", description = "由 Midjourney Proxy 回调") | 74 | @Operation(summary = "Midjourney 回调通知", description = "由 Midjourney Proxy 回调") |
| 75 | @PostMapping("/midjourney-notify") | 75 | @PostMapping("/midjourney-notify") |
| 76 | @PermitAll | 76 | @PermitAll |
| 77 | - public CommonResult<Boolean> midjourneyNotify(@RequestBody MidjourneyNotifyReqVO notifyReqVO) { | ||
| 78 | - return success(imageService.midjourneyNotify(notifyReqVO)); | 77 | + public void midjourneyNotify(@RequestBody MidjourneyNotifyReqVO notifyReqVO) { |
| 78 | + imageService.midjourneyNotify(notifyReqVO); | ||
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | @Operation(summary = "Midjourney Action", description = "例如说:放大、缩小、U1、U2 等") | 81 | @Operation(summary = "Midjourney Action", description = "例如说:放大、缩小、U1、U2 等") |
| 82 | @GetMapping("/midjourney/action") | 82 | @GetMapping("/midjourney/action") |
| 83 | - // TODO @fan:id、customerId 的 swagger 注解 | 83 | + @Parameter(name = "id", description = "图片id", example = "1") |
| 84 | + @Parameter(name = "customId", description = "操作id", example = "MJ::JOB::upsample::1::85a4b4c1-8835-46c5-a15c-aea34fad1862") | ||
| 84 | public CommonResult<Boolean> midjourneyAction(@RequestParam("id") Long imageId, | 85 | public CommonResult<Boolean> midjourneyAction(@RequestParam("id") Long imageId, |
| 85 | @RequestParam("customId") String customId) { | 86 | @RequestParam("customId") String customId) { |
| 86 | - return success(imageService.midjourneyAction(getLoginUserId(), imageId, customId)); | 87 | + imageService.midjourneyAction(getLoginUserId(), imageId, customId); |
| 88 | + return success(null); | ||
| 87 | } | 89 | } |
| 88 | 90 | ||
| 89 | } | 91 | } |
| 90 | \ No newline at end of file | 92 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java
| @@ -103,7 +103,7 @@ public class AiImageDO extends BaseDO { | @@ -103,7 +103,7 @@ public class AiImageDO extends BaseDO { | ||
| 103 | private MidjourneyNotifyReqVO response; | 103 | private MidjourneyNotifyReqVO response; |
| 104 | 104 | ||
| 105 | /** | 105 | /** |
| 106 | - * mj 进度 | 106 | + * mj 进度(10%、50%、100%) |
| 107 | */ | 107 | */ |
| 108 | private String progress; | 108 | private String progress; |
| 109 | 109 |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageService.java
| @@ -63,7 +63,7 @@ public interface AiImageService { | @@ -63,7 +63,7 @@ public interface AiImageService { | ||
| 63 | * @param notifyReqVO | 63 | * @param notifyReqVO |
| 64 | * @return | 64 | * @return |
| 65 | */ | 65 | */ |
| 66 | - Boolean midjourneyNotify(MidjourneyNotifyReqVO notifyReqVO); | 66 | + void midjourneyNotify(MidjourneyNotifyReqVO notifyReqVO); |
| 67 | 67 | ||
| 68 | /** | 68 | /** |
| 69 | * 构建 midjourney - 更新对象 | 69 | * 构建 midjourney - 更新对象 |
| @@ -82,5 +82,5 @@ public interface AiImageService { | @@ -82,5 +82,5 @@ public interface AiImageService { | ||
| 82 | * @param customId | 82 | * @param customId |
| 83 | * @return | 83 | * @return |
| 84 | */ | 84 | */ |
| 85 | - Boolean midjourneyAction(Long loginUserId, Long imageId, String customId); | 85 | + void midjourneyAction(Long loginUserId, Long imageId, String customId); |
| 86 | } | 86 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java
| @@ -134,49 +134,39 @@ public class AiImageServiceImpl implements AiImageService { | @@ -134,49 +134,39 @@ public class AiImageServiceImpl implements AiImageService { | ||
| 134 | @Override | 134 | @Override |
| 135 | @Transactional(rollbackFor = Exception.class) | 135 | @Transactional(rollbackFor = Exception.class) |
| 136 | public Long midjourneyImagine(Long userId, AiImageMidjourneyImagineReqVO req) { | 136 | public Long midjourneyImagine(Long userId, AiImageMidjourneyImagineReqVO req) { |
| 137 | - // TODO @fan:1 和 2 应该放在一个 1 里面。不然 = = 一个逻辑就显得有很多 1、/2、/3、/4;这么分的原因,是方便阅读的时候,容易理解。 | ||
| 138 | - // 1、构建 AiImageDO | ||
| 139 | - // TODO @fan:1)aiImageDO 可以缩写成 image 更简洁;2)可以链式调用,把相同的放在一行里,这样更容易分组 | ||
| 140 | - AiImageDO aiImageDO = new AiImageDO(); | ||
| 141 | - aiImageDO.setUserId(userId); | ||
| 142 | - aiImageDO.setPrompt(req.getPrompt()); | ||
| 143 | - aiImageDO.setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform()); | ||
| 144 | - aiImageDO.setModel(req.getModel()); | ||
| 145 | - aiImageDO.setWidth(req.getWidth()); | ||
| 146 | - aiImageDO.setHeight(req.getHeight()); | ||
| 147 | - aiImageDO.setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()); | ||
| 148 | - // 2、保存 image | ||
| 149 | - imageMapper.insert(aiImageDO); | ||
| 150 | - // TODO @fan:3 和 2 之间,应该空一行;因为这里是开始发起请求第三方,是个单独的小块逻辑 | 137 | + // 1、构建 AiImageDO 并 保存 |
| 138 | + AiImageDO image = new AiImageDO() | ||
| 139 | + .setUserId(userId) | ||
| 140 | + .setPrompt(req.getPrompt()) | ||
| 141 | + .setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform()) | ||
| 142 | + .setModel(req.getModel()) | ||
| 143 | + .setWidth(req.getWidth()) | ||
| 144 | + .setHeight(req.getHeight()) | ||
| 145 | + .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()); | ||
| 146 | + imageMapper.insert(image); | ||
| 147 | + | ||
| 151 | // 3、调用 MidjourneyProxy 提交任务 | 148 | // 3、调用 MidjourneyProxy 提交任务 |
| 149 | + | ||
| 150 | + // 3.1、设置 midjourney 扩展参数 | ||
| 152 | MidjourneyImagineReqVO imagineReqVO = BeanUtils.toBean(req, MidjourneyImagineReqVO.class); | 151 | MidjourneyImagineReqVO imagineReqVO = BeanUtils.toBean(req, MidjourneyImagineReqVO.class); |
| 153 | imagineReqVO.setNotifyHook(midjourneyNotifyUrl); | 152 | imagineReqVO.setNotifyHook(midjourneyNotifyUrl); |
| 154 | - // 4、设置 midjourney 扩展参数 | ||
| 155 | imagineReqVO.setState(buildParams(req.getWidth(), | 153 | imagineReqVO.setState(buildParams(req.getWidth(), |
| 156 | req.getHeight(), req.getVersion(), MidjourneyModelEnum.valueOfModel(req.getModel()))); | 154 | req.getHeight(), req.getVersion(), MidjourneyModelEnum.valueOfModel(req.getModel()))); |
| 157 | - // 5、提交绘画请求 | 155 | + // 3.2、提交绘画请求 |
| 158 | // TODO @fan:5 这里,失败的情况,到底抛出异常,还是 RespVO,可以参考 OpenAI 的 API 封装 | 156 | // TODO @fan:5 这里,失败的情况,到底抛出异常,还是 RespVO,可以参考 OpenAI 的 API 封装 |
| 159 | MidjourneySubmitRespVO submitRespVO = midjourneyProxyClient.imagine(imagineReqVO); | 157 | MidjourneySubmitRespVO submitRespVO = midjourneyProxyClient.imagine(imagineReqVO); |
| 160 | - // 6、保存任务 id (状态码: 1(提交成功), 21(已存在), 22(排队中), other(错误)) | 158 | + |
| 159 | + // 4、保存任务 id (状态码: 1(提交成功), 21(已存在), 22(排队中), other(错误)) | ||
| 161 | if (!MidjourneySubmitCodeEnum.SUCCESS_CODES.contains(submitRespVO.getCode())) { | 160 | if (!MidjourneySubmitCodeEnum.SUCCESS_CODES.contains(submitRespVO.getCode())) { |
| 162 | throw exception(AI_IMAGE_MIDJOURNEY_SUBMIT_FAIL, submitRespVO.getDescription()); | 161 | throw exception(AI_IMAGE_MIDJOURNEY_SUBMIT_FAIL, submitRespVO.getDescription()); |
| 163 | } | 162 | } |
| 164 | - // TODO @fan:7 和 6 之间,应该空一行;这样,最终这个逻辑,就会有 2 个空行,3 小块逻辑:1)插入;2)调用;3)更新 | ||
| 165 | - // 7、构建 imageOptions 参数 | ||
| 166 | - // TODO @fan:1)链式调用;2)其实可以直接使用 AiImageMidjourneyImagineReqVO。不用单独一个 options 类哈。 | ||
| 167 | - MidjourneyImageOptions imageOptions = new MidjourneyImageOptions() | ||
| 168 | - .setWidth(req.getWidth()) | ||
| 169 | - .setHeight(req.getHeight()) | ||
| 170 | - .setModel(req.getModel()) | ||
| 171 | - .setVersion(req.getVersion()) | ||
| 172 | - .setState(imagineReqVO.getState()); | ||
| 173 | - // 8、更新 taskId 和参数 | 163 | + // 4.1、更新 taskId 和参数 |
| 174 | imageMapper.updateById(new AiImageDO() | 164 | imageMapper.updateById(new AiImageDO() |
| 175 | - .setId(aiImageDO.getId()) | 165 | + .setId(image.getId()) |
| 176 | .setTaskId(submitRespVO.getResult()) | 166 | .setTaskId(submitRespVO.getResult()) |
| 177 | - .setOptions(BeanUtil.beanToMap(imageOptions)) | 167 | + .setOptions(BeanUtil.beanToMap(req)) |
| 178 | ); | 168 | ); |
| 179 | - return aiImageDO.getId(); | 169 | + return image.getId(); |
| 180 | } | 170 | } |
| 181 | 171 | ||
| 182 | 172 | ||
| @@ -191,23 +181,21 @@ public class AiImageServiceImpl implements AiImageService { | @@ -191,23 +181,21 @@ public class AiImageServiceImpl implements AiImageService { | ||
| 191 | imageMapper.deleteById(id); | 181 | imageMapper.deleteById(id); |
| 192 | } | 182 | } |
| 193 | 183 | ||
| 194 | - // TODO @fan:建议返回 void;然后如果不存在,就抛出异常哈; | ||
| 195 | @Override | 184 | @Override |
| 196 | - public Boolean midjourneyNotify(MidjourneyNotifyReqVO notifyReqVO) { | 185 | + public void midjourneyNotify(MidjourneyNotifyReqVO notifyReqVO) { |
| 197 | // 1、根据 job id 查询关联的 image | 186 | // 1、根据 job id 查询关联的 image |
| 198 | AiImageDO image = imageMapper.selectByJobId(notifyReqVO.getId()); | 187 | AiImageDO image = imageMapper.selectByJobId(notifyReqVO.getId()); |
| 199 | if (image == null) { | 188 | if (image == null) { |
| 200 | log.warn("midjourneyNotify 回调的 jobId 不存在! jobId: {}", notifyReqVO.getId()); | 189 | log.warn("midjourneyNotify 回调的 jobId 不存在! jobId: {}", notifyReqVO.getId()); |
| 201 | - return false; | ||
| 202 | } | 190 | } |
| 203 | // 2、转换状态 | 191 | // 2、转换状态 |
| 204 | AiImageDO updateImage = buildUpdateImage(image.getId(), notifyReqVO); | 192 | AiImageDO updateImage = buildUpdateImage(image.getId(), notifyReqVO); |
| 205 | // 3、更新 image 状态 | 193 | // 3、更新 image 状态 |
| 206 | - return imageMapper.updateById(updateImage) > 0; | 194 | + imageMapper.updateById(updateImage); |
| 207 | } | 195 | } |
| 208 | 196 | ||
| 209 | public AiImageDO buildUpdateImage(Long imageId, MidjourneyNotifyReqVO notifyReqVO) { | 197 | public AiImageDO buildUpdateImage(Long imageId, MidjourneyNotifyReqVO notifyReqVO) { |
| 210 | - // 2、转换状态 | 198 | + // 1、转换状态 |
| 211 | String imageStatus = null; | 199 | String imageStatus = null; |
| 212 | MidjourneyTaskStatusEnum taskStatusEnum = MidjourneyTaskStatusEnum.valueOf(notifyReqVO.getStatus()); | 200 | MidjourneyTaskStatusEnum taskStatusEnum = MidjourneyTaskStatusEnum.valueOf(notifyReqVO.getStatus()); |
| 213 | if (MidjourneyTaskStatusEnum.SUCCESS == taskStatusEnum) { | 201 | if (MidjourneyTaskStatusEnum.SUCCESS == taskStatusEnum) { |
| @@ -215,7 +203,8 @@ public class AiImageServiceImpl implements AiImageService { | @@ -215,7 +203,8 @@ public class AiImageServiceImpl implements AiImageService { | ||
| 215 | } else if (MidjourneyTaskStatusEnum.FAILURE == taskStatusEnum) { | 203 | } else if (MidjourneyTaskStatusEnum.FAILURE == taskStatusEnum) { |
| 216 | imageStatus = AiImageStatusEnum.FAIL.getStatus(); | 204 | imageStatus = AiImageStatusEnum.FAIL.getStatus(); |
| 217 | } | 205 | } |
| 218 | - // 3、上传图片 | 206 | + |
| 207 | + // 2、上传图片 | ||
| 219 | String filePath = null; | 208 | String filePath = null; |
| 220 | if (!StrUtil.isBlank(notifyReqVO.getImageUrl())) { | 209 | if (!StrUtil.isBlank(notifyReqVO.getImageUrl())) { |
| 221 | try { | 210 | try { |
| @@ -224,7 +213,8 @@ public class AiImageServiceImpl implements AiImageService { | @@ -224,7 +213,8 @@ public class AiImageServiceImpl implements AiImageService { | ||
| 224 | log.warn("midjourneyNotify 图片上传失败! {} 异常:{}", notifyReqVO.getImageUrl(), ExceptionUtil.getMessage(e)); | 213 | log.warn("midjourneyNotify 图片上传失败! {} 异常:{}", notifyReqVO.getImageUrl(), ExceptionUtil.getMessage(e)); |
| 225 | } | 214 | } |
| 226 | } | 215 | } |
| 227 | - // 4、更新 image 状态 | 216 | + |
| 217 | + // 3、更新 image 状态 | ||
| 228 | return new AiImageDO() | 218 | return new AiImageDO() |
| 229 | .setId(imageId) | 219 | .setId(imageId) |
| 230 | .setStatus(imageStatus) | 220 | .setStatus(imageStatus) |
| @@ -235,55 +225,60 @@ public class AiImageServiceImpl implements AiImageService { | @@ -235,55 +225,60 @@ public class AiImageServiceImpl implements AiImageService { | ||
| 235 | .setErrorMessage(notifyReqVO.getFailReason()); | 225 | .setErrorMessage(notifyReqVO.getFailReason()); |
| 236 | } | 226 | } |
| 237 | 227 | ||
| 238 | - // TODO @fan:1)不用返回 Boolean | ||
| 239 | @Override | 228 | @Override |
| 240 | - @Transactional(rollbackFor = Exception.class) // TODO @fan:只操作一个 db,不用事务哈; | ||
| 241 | - public Boolean midjourneyAction(Long loginUserId, Long imageId, String customId) { | ||
| 242 | - // TODO @fan:1)1 和 2,可以写成 1.1、1.2,都是在做校验;2)validateCustomId 可以直接抛出 AI_IMAGE_CUSTOM_ID_NOT_EXISTS 异常;一般情况下,validateXXX 都是失败抛出异常,isXXXValid 返回 true、false | 229 | + public void midjourneyAction(Long loginUserId, Long imageId, String customId) { |
| 243 | // 1、检查 image | 230 | // 1、检查 image |
| 244 | - // TODO @fan:1)aiImageDO 缩写成 image; | ||
| 245 | - AiImageDO aiImageDO = validateImageExists(imageId); | 231 | + AiImageDO image = validateImageExists(imageId); |
| 246 | // 2、检查 customId | 232 | // 2、检查 customId |
| 247 | - if (!validateCustomId(customId, aiImageDO.getButtons())) { | ||
| 248 | - throw exception(AI_IMAGE_CUSTOM_ID_NOT_EXISTS); | ||
| 249 | - } | ||
| 250 | - // TODO @fan:2 和 3 之间,空一行 | 233 | + validateCustomId(customId, image.getButtons()); |
| 234 | + | ||
| 251 | // 3、调用 midjourney proxy | 235 | // 3、调用 midjourney proxy |
| 252 | MidjourneySubmitRespVO submitRespVO = midjourneyProxyClient.action( | 236 | MidjourneySubmitRespVO submitRespVO = midjourneyProxyClient.action( |
| 253 | new MidjourneyActionReqVO() | 237 | new MidjourneyActionReqVO() |
| 254 | .setCustomId(customId) | 238 | .setCustomId(customId) |
| 255 | - .setTaskId(aiImageDO.getTaskId()) | 239 | + .setTaskId(image.getTaskId()) |
| 256 | .setNotifyHook(midjourneyNotifyUrl) | 240 | .setNotifyHook(midjourneyNotifyUrl) |
| 257 | ); | 241 | ); |
| 258 | - // 6、保存任务 id (状态码: 1(提交成功), 21(已存在), 22(排队中), other(错误)) | 242 | + // 4、检查错误 code (状态码: 1(提交成功), 21(已存在), 22(排队中), other(错误)) |
| 259 | if (!MidjourneySubmitCodeEnum.SUCCESS_CODES.contains(submitRespVO.getCode())) { | 243 | if (!MidjourneySubmitCodeEnum.SUCCESS_CODES.contains(submitRespVO.getCode())) { |
| 260 | throw exception(AI_IMAGE_MIDJOURNEY_SUBMIT_FAIL, submitRespVO.getDescription()); | 244 | throw exception(AI_IMAGE_MIDJOURNEY_SUBMIT_FAIL, submitRespVO.getDescription()); |
| 261 | } | 245 | } |
| 262 | - // TODO 6 和 4 之间空一行; | ||
| 263 | - // 4、新增 image 记录 | ||
| 264 | - AiImageDO newImage = BeanUtils.toBean(aiImageDO, AiImageDO.class); | ||
| 265 | - // TODO @fan:最好不要 copy 属性。因为未来如果加属性,可能会导致额外 copy 了;最好是 new 赋值下,显示声明。 | ||
| 266 | - // 4.1、重置参数 | 246 | + |
| 247 | + // 5、新增 image 记录(根据 image 新增一个) | ||
| 248 | + AiImageDO newImage = new AiImageDO(); | ||
| 267 | newImage.setId(null); | 249 | newImage.setId(null); |
| 250 | + newImage.setUserId(image.getUserId()); | ||
| 251 | + newImage.setPrompt(image.getPrompt()); | ||
| 252 | + | ||
| 253 | + newImage.setPlatform(image.getPlatform()); | ||
| 254 | + newImage.setModel(image.getModel()); | ||
| 255 | + newImage.setWidth(image.getWidth()); | ||
| 256 | + newImage.setHeight(image.getHeight()); | ||
| 257 | + | ||
| 268 | newImage.setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()); | 258 | newImage.setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()); |
| 259 | + newImage.setPublicStatus(image.getPublicStatus()); | ||
| 260 | + | ||
| 269 | newImage.setPicUrl(null); | 261 | newImage.setPicUrl(null); |
| 270 | - newImage.setResponse(null); | ||
| 271 | newImage.setProgress(null); | 262 | newImage.setProgress(null); |
| 263 | + newImage.setButtons(null); | ||
| 264 | + newImage.setOptions(image.getOptions()); | ||
| 265 | + newImage.setResponse(image.getResponse()); | ||
| 272 | newImage.setTaskId(submitRespVO.getResult()); | 266 | newImage.setTaskId(submitRespVO.getResult()); |
| 273 | newImage.setErrorMessage(null); | 267 | newImage.setErrorMessage(null); |
| 274 | - newImage.setButtons(null); | ||
| 275 | - // 4.2、保存数据库 | ||
| 276 | imageMapper.insert(newImage); | 268 | imageMapper.insert(newImage); |
| 277 | - return Boolean.TRUE; | ||
| 278 | } | 269 | } |
| 279 | 270 | ||
| 280 | - private static boolean validateCustomId(String customId, List<MidjourneyNotifyReqVO.Button> buttons) { | 271 | + private static void validateCustomId(String customId, List<MidjourneyNotifyReqVO.Button> buttons) { |
| 272 | + boolean isTrue = false; | ||
| 281 | for (MidjourneyNotifyReqVO.Button button : buttons) { | 273 | for (MidjourneyNotifyReqVO.Button button : buttons) { |
| 282 | if (button.getCustomId().equals(customId)) { | 274 | if (button.getCustomId().equals(customId)) { |
| 283 | - return true; | 275 | + isTrue = true; |
| 276 | + break; | ||
| 284 | } | 277 | } |
| 285 | } | 278 | } |
| 286 | - return false; | 279 | + if (isTrue) { |
| 280 | + throw exception(AI_IMAGE_CUSTOM_ID_NOT_EXISTS); | ||
| 281 | + } | ||
| 287 | } | 282 | } |
| 288 | 283 | ||
| 289 | private AiImageDO validateImageExists(Long id) { | 284 | private AiImageDO validateImageExists(Long id) { |
| @@ -304,6 +299,7 @@ public class AiImageServiceImpl implements AiImageService { | @@ -304,6 +299,7 @@ public class AiImageServiceImpl implements AiImageService { | ||
| 304 | } | 299 | } |
| 305 | 300 | ||
| 306 | // TODO @fan:这个是不是应该放在 MJ API 的封装里面搞哈? | 301 | // TODO @fan:这个是不是应该放在 MJ API 的封装里面搞哈? |
| 302 | + | ||
| 307 | /** | 303 | /** |
| 308 | * 构建 Midjourney 自定义参数 | 304 | * 构建 Midjourney 自定义参数 |
| 309 | * | 305 | * |