Commit 776d6e4e1e1758641e973125424eb06b4a99e45e
1 parent
79a094be
【增加】AI Image mj 增加 action 操作
Showing
5 changed files
with
82 additions
and
22 deletions
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java
| ... | ... | @@ -44,5 +44,6 @@ public interface ErrorCodeConstants { |
| 44 | 44 | |
| 45 | 45 | ErrorCode AI_IMAGE_NOT_EXISTS = new ErrorCode(1_022_005_000, "image 不存在!"); |
| 46 | 46 | ErrorCode AI_IMAGE_MIDJOURNEY_SUBMIT_FAIL = new ErrorCode(1_022_005_001, "Midjourney 提交失败! {}"); |
| 47 | + ErrorCode AI_IMAGE_CUSTOM_ID_NOT_EXISTS = new ErrorCode(1_022_005_002, "Midjourney 按钮 customId 不存在! {}"); | |
| 47 | 48 | |
| 48 | 49 | } | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.java
| ... | ... | @@ -74,8 +74,14 @@ public class AiImageController { |
| 74 | 74 | @Operation(summary = "midjourney proxy - 回调通知") |
| 75 | 75 | @PostMapping("/midjourney-notify") |
| 76 | 76 | @PermitAll |
| 77 | - public CommonResult<Boolean> midjourneyNotify( @RequestBody MidjourneyNotifyReqVO notifyReqVO) { | |
| 78 | - return success(imageService.midjourneyNotify(getLoginUserId(), notifyReqVO)); | |
| 77 | + public CommonResult<Boolean> midjourneyNotify(@RequestBody MidjourneyNotifyReqVO notifyReqVO) { | |
| 78 | + return success(imageService.midjourneyNotify(notifyReqVO)); | |
| 79 | 79 | } |
| 80 | 80 | |
| 81 | + @Operation(summary = "midjourney - action(放大、缩小、U1、U2...)") | |
| 82 | + @PostMapping("/midjourney/action") | |
| 83 | + public CommonResult<Boolean> midjourneyAction(@RequestParam("id") Long imageId, | |
| 84 | + @RequestParam("customId") String customId) { | |
| 85 | + return success(imageService.midjourneyAction(getLoginUserId(), imageId, customId)); | |
| 86 | + } | |
| 81 | 87 | } |
| 82 | 88 | \ No newline at end of file | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageService.java
| ... | ... | @@ -59,10 +59,18 @@ public interface AiImageService { |
| 59 | 59 | /** |
| 60 | 60 | * midjourney proxy - 回调通知 |
| 61 | 61 | * |
| 62 | - * @param loginUserId | |
| 63 | 62 | * @param notifyReqVO |
| 64 | 63 | * @return |
| 65 | 64 | */ |
| 66 | - Boolean midjourneyNotify(Long loginUserId, MidjourneyNotifyReqVO notifyReqVO); | |
| 65 | + Boolean midjourneyNotify(MidjourneyNotifyReqVO notifyReqVO); | |
| 67 | 66 | |
| 67 | + /** | |
| 68 | + * midjourney - action(放大、缩小、U1、U2...) | |
| 69 | + * | |
| 70 | + * @param loginUserId | |
| 71 | + * @param imageId | |
| 72 | + * @param customId | |
| 73 | + * @return | |
| 74 | + */ | |
| 75 | + Boolean midjourneyAction(Long loginUserId, Long imageId, String customId); | |
| 68 | 76 | } | ... | ... |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java
| ... | ... | @@ -15,6 +15,7 @@ import cn.iocoder.yudao.module.ai.client.MidjourneyProxyClient; |
| 15 | 15 | import cn.iocoder.yudao.module.ai.client.enums.MidjourneyModelEnum; |
| 16 | 16 | import cn.iocoder.yudao.module.ai.client.enums.MidjourneySubmitCodeEnum; |
| 17 | 17 | import cn.iocoder.yudao.module.ai.client.enums.MidjourneyTaskStatusEnum; |
| 18 | +import cn.iocoder.yudao.module.ai.client.vo.MidjourneyActionReqVO; | |
| 18 | 19 | import cn.iocoder.yudao.module.ai.client.vo.MidjourneyImagineReqVO; |
| 19 | 20 | import cn.iocoder.yudao.module.ai.client.vo.MidjourneyNotifyReqVO; |
| 20 | 21 | import cn.iocoder.yudao.module.ai.client.vo.MidjourneySubmitRespVO; |
| ... | ... | @@ -39,9 +40,10 @@ import org.springframework.scheduling.annotation.Async; |
| 39 | 40 | import org.springframework.stereotype.Service; |
| 40 | 41 | import org.springframework.transaction.annotation.Transactional; |
| 41 | 42 | |
| 43 | +import java.util.List; | |
| 44 | + | |
| 42 | 45 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 43 | -import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.AI_IMAGE_MIDJOURNEY_SUBMIT_FAIL; | |
| 44 | -import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.AI_IMAGE_NOT_EXISTS; | |
| 46 | +import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; | |
| 45 | 47 | |
| 46 | 48 | /** |
| 47 | 49 | * AI 绘画 Service 实现类 |
| ... | ... | @@ -136,30 +138,21 @@ public class AiImageServiceImpl implements AiImageService { |
| 136 | 138 | aiImageDO.setUserId(loginUserId); |
| 137 | 139 | aiImageDO.setPrompt(req.getPrompt()); |
| 138 | 140 | aiImageDO.setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform()); |
| 139 | - // todo @范 平台需要转换(mj 模型一般分版本) | |
| 140 | 141 | aiImageDO.setModel(null); |
| 141 | 142 | aiImageDO.setWidth(null); |
| 142 | 143 | aiImageDO.setHeight(null); |
| 143 | 144 | aiImageDO.setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()); |
| 144 | - | |
| 145 | 145 | // 2、保存 image |
| 146 | 146 | imageMapper.insert(aiImageDO); |
| 147 | - | |
| 148 | 147 | // 3、调用 MidjourneyProxy 提交任务 |
| 149 | 148 | MidjourneyImagineReqVO imagineReqVO = BeanUtils.toBean(req, MidjourneyImagineReqVO.class); |
| 150 | 149 | imagineReqVO.setNotifyHook(midjourneyNotifyUrl); |
| 151 | - // 设置 midjourney 扩展参数 | |
| 152 | - // --ar 来设置尺寸 | |
| 153 | - String midjourneySizeParam = String.format(" --ar %s:%s ", req.getWidth(), req.getHeight()); | |
| 154 | - // --v 版本 | |
| 155 | - String midjourneyVersionParam = String.format(" --v %s ", req.getVersion()); | |
| 156 | - // --niji 模型 | |
| 157 | - MidjourneyModelEnum midjourneyModelEnum = MidjourneyModelEnum.valueOfModel(req.getModel()); | |
| 158 | - String midjourneyNijiParam = MidjourneyModelEnum.NIJI == midjourneyModelEnum ? " --niji " : ""; | |
| 159 | - // 设置参数 | |
| 160 | - imagineReqVO.setState(midjourneySizeParam.concat(midjourneyVersionParam).concat(midjourneyNijiParam)); | |
| 150 | + // 4、设置 midjourney 扩展参数 | |
| 151 | + imagineReqVO.setState(buildParams(req.getWidth(), | |
| 152 | + req.getHeight(), req.getVersion(), MidjourneyModelEnum.valueOfModel(req.getModel()))); | |
| 153 | + // 5、提交绘画请求 | |
| 161 | 154 | MidjourneySubmitRespVO submitRespVO = midjourneyProxyClient.imagine(imagineReqVO); |
| 162 | - // 4、保存任务 id (状态码: 1(提交成功), 21(已存在), 22(排队中), other(错误)) | |
| 155 | + // 6、保存任务 id (状态码: 1(提交成功), 21(已存在), 22(排队中), other(错误)) | |
| 163 | 156 | if (!MidjourneySubmitCodeEnum.SUCCESS_CODES.contains(submitRespVO.getCode())) { |
| 164 | 157 | throw exception(AI_IMAGE_MIDJOURNEY_SUBMIT_FAIL, submitRespVO.getDescription()); |
| 165 | 158 | } |
| ... | ... | @@ -170,6 +163,8 @@ public class AiImageServiceImpl implements AiImageService { |
| 170 | 163 | return aiImageDO.getId(); |
| 171 | 164 | } |
| 172 | 165 | |
| 166 | + | |
| 167 | + | |
| 173 | 168 | @Override |
| 174 | 169 | public void deleteImageMy(Long id, Long userId) { |
| 175 | 170 | // 1. 校验是否存在 |
| ... | ... | @@ -182,7 +177,7 @@ public class AiImageServiceImpl implements AiImageService { |
| 182 | 177 | } |
| 183 | 178 | |
| 184 | 179 | @Override |
| 185 | - public Boolean midjourneyNotify(Long loginUserId, MidjourneyNotifyReqVO notifyReqVO) { | |
| 180 | + public Boolean midjourneyNotify(MidjourneyNotifyReqVO notifyReqVO) { | |
| 186 | 181 | // 1、根据 job id 查询关联的 image |
| 187 | 182 | AiImageDO image = imageMapper.selectByJobId(notifyReqVO.getId()); |
| 188 | 183 | if (image == null) { |
| ... | ... | @@ -220,6 +215,34 @@ public class AiImageServiceImpl implements AiImageService { |
| 220 | 215 | return true; |
| 221 | 216 | } |
| 222 | 217 | |
| 218 | + @Override | |
| 219 | + @Transactional(rollbackFor = Exception.class) | |
| 220 | + public Boolean midjourneyAction(Long loginUserId, Long imageId, String customId) { | |
| 221 | + // 1、检查 image | |
| 222 | + AiImageDO aiImageDO = validateImageExists(imageId); | |
| 223 | + // 2、检查 customId | |
| 224 | + if (!validateCustomId(customId, aiImageDO.getButtons())) { | |
| 225 | + throw exception(AI_IMAGE_CUSTOM_ID_NOT_EXISTS); | |
| 226 | + } | |
| 227 | + // 3、调用 midjourney proxy | |
| 228 | + midjourneyProxyClient.action( | |
| 229 | + new MidjourneyActionReqVO() | |
| 230 | + .setCustomId(customId) | |
| 231 | + .setTaskId(aiImageDO.getJobId()) | |
| 232 | + .setNotifyHook(midjourneyNotifyUrl) | |
| 233 | + ); | |
| 234 | + return Boolean.TRUE; | |
| 235 | + } | |
| 236 | + | |
| 237 | + private static boolean validateCustomId(String customId, List<MidjourneyNotifyReqVO.Button> buttons) { | |
| 238 | + for (MidjourneyNotifyReqVO.Button button : buttons) { | |
| 239 | + if (button.getCustomId().equals(customId)) { | |
| 240 | + return true; | |
| 241 | + } | |
| 242 | + } | |
| 243 | + return false; | |
| 244 | + } | |
| 245 | + | |
| 223 | 246 | private AiImageDO validateImageExists(Long id) { |
| 224 | 247 | AiImageDO image = imageMapper.selectById(id); |
| 225 | 248 | if (image == null) { |
| ... | ... | @@ -237,4 +260,25 @@ public class AiImageServiceImpl implements AiImageService { |
| 237 | 260 | return SpringUtil.getBean(getClass()); |
| 238 | 261 | } |
| 239 | 262 | |
| 263 | + /** | |
| 264 | + * 构建 Midjourney 自定义参数 | |
| 265 | + * | |
| 266 | + * @param width | |
| 267 | + * @param height | |
| 268 | + * @param version | |
| 269 | + * @param model | |
| 270 | + * @return | |
| 271 | + */ | |
| 272 | + private String buildParams(String width, String height, String version, MidjourneyModelEnum model) { | |
| 273 | + StringBuilder params = new StringBuilder(); | |
| 274 | + // --ar 来设置尺寸 | |
| 275 | + params.append(String.format(" --ar %s:%s ", width, height)); | |
| 276 | + // --v 版本 | |
| 277 | + params.append(String.format(" --v %s ", version)); | |
| 278 | + // --niji 模型 | |
| 279 | + if (MidjourneyModelEnum.NIJI == model) { | |
| 280 | + params.append(" --niji "); | |
| 281 | + } | |
| 282 | + return params.toString(); | |
| 283 | + } | |
| 240 | 284 | } | ... | ... |
yudao-server/src/main/resources/application-local.yaml
| ... | ... | @@ -80,7 +80,8 @@ server: |
| 80 | 80 | ai: |
| 81 | 81 | midjourney-proxy: |
| 82 | 82 | url: https://api.holdai.top/mj |
| 83 | - notifyUrl: http://7b1aada4.r26.cpolar.top/admin-api/ai/image/midjourney-notify | |
| 83 | + notifyUrl: http://61d61685.r21.cpolar.top/admin-api/ai/image/midjourney-notify | |
| 84 | + key: sk-c3qxUCVKsPfdQiYU8440E3Fc8dE5424d9cB124A4Ee2489E3 | |
| 84 | 85 | |
| 85 | 86 | |
| 86 | 87 | --- #################### 定时任务相关配置 #################### | ... | ... |