Commit 776d6e4e1e1758641e973125424eb06b4a99e45e

Authored by cherishsince
1 parent 79a094be

【增加】AI Image mj 增加 action 操作

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 --- #################### 定时任务相关配置 ####################
... ...