MidjourneyServiceImpl.java 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. package com.cyksj.service.midjourney.impl;
  2. import cn.hutool.core.collection.CollectionUtil;
  3. import cn.hutool.core.io.FileUtil;
  4. import cn.hutool.core.map.MapUtil;
  5. import cn.hutool.http.HttpRequest;
  6. import cn.hutool.http.HttpUtil;
  7. import cn.hutool.json.JSONArray;
  8. import cn.hutool.json.JSONObject;
  9. import cn.hutool.json.JSONUtil;
  10. import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
  11. import com.baomidou.mybatisplus.core.toolkit.Wrappers;
  12. import com.cyksj.common.EnvCommonService;
  13. import com.cyksj.common.exception.BusinessRuntimeException;
  14. import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
  15. import com.cyksj.common.util.Codec;
  16. import com.cyksj.common.util.J11HttpC;
  17. import com.cyksj.common.util.Jsons;
  18. import com.cyksj.common.util.StringUtil;
  19. import com.cyksj.mapper.MidjourneyUserConversationMapper;
  20. import com.cyksj.mapper.MidjourneyUserMapper;
  21. import com.cyksj.model.dto.BlendDimensions;
  22. import com.cyksj.model.dto.MessageButton;
  23. import com.cyksj.model.entity.MidjourneyAccount;
  24. import com.cyksj.model.entity.MidjourneyUser;
  25. import com.cyksj.model.entity.MidjourneyUserConversation;
  26. import com.cyksj.model.response.SubmitResult;
  27. import com.cyksj.redis.RedisService;
  28. import com.cyksj.service.midjourney.MidjourneyAccountService;
  29. import com.cyksj.service.midjourney.MidjourneyService;
  30. import lombok.RequiredArgsConstructor;
  31. import lombok.extern.slf4j.Slf4j;
  32. import org.apache.commons.lang3.StringUtils;
  33. import org.springframework.beans.factory.annotation.Value;
  34. import org.springframework.stereotype.Service;
  35. import javax.imageio.ImageIO;
  36. import javax.imageio.stream.FileImageOutputStream;
  37. import java.awt.image.BufferedImage;
  38. import java.io.*;
  39. import java.net.URI;
  40. import java.net.URLEncoder;
  41. import java.net.http.HttpResponse;
  42. import java.nio.charset.StandardCharsets;
  43. import java.nio.file.Files;
  44. import java.nio.file.Path;
  45. import java.security.MessageDigest;
  46. import java.time.ZoneOffset;
  47. import java.util.*;
  48. import java.util.concurrent.atomic.AtomicBoolean;
  49. import java.util.stream.Collectors;
  50. /**
  51. * @author zwhui
  52. * @date 2024/4/23 15:44
  53. */
  54. @Service
  55. @Slf4j
  56. @RequiredArgsConstructor
  57. public class MidjourneyServiceImpl implements MidjourneyService {
  58. private static final String RELAX_HOST = "http://43.154.230.104:8080";
  59. private static final String FAST_HOST = "https://aigc.api4midjourney.com/api";
  60. private static final String FAST_TOKEN = "14a6b469-7cb2-4b99-9238-676c7c5cffef";
  61. private final MidjourneyUserConversationMapper conversationMapper;
  62. private static final List<String> progress = List.of("100%");
  63. private static final List<String> status = List.of("MODAL","CANCEL","FAILURE");
  64. private final RedisService redisService;
  65. private final MidjourneyUserMapper midjourneyUserMapper;
  66. @Value("${midjourney.url}")
  67. private String midjourneyHost;
  68. private final EnvCommonService envCommonService;
  69. private static final GlobalThreadPoolTaskExecutor TASK_EXECUTOR = GlobalThreadPoolTaskExecutor.getInstance();
  70. private final MidjourneyAccountService midjourneyAccountService;
  71. @Override
  72. public MidjourneyUserConversation submitImagine(MidjourneyUser user, String prompt,String botType, List<String> base64Array) throws Exception {
  73. Map<String, Object> imagineParam = MapUtil.builder(new HashMap<String,Object>())
  74. .put("prompt", prompt)
  75. .put("state", user.getId())
  76. .build();
  77. if (CollectionUtil.isNotEmpty(base64Array)) {
  78. imagineParam.put("base64Array", base64Array);
  79. }
  80. SubmitResult result = submit(user.getMode(),"imagine", null,imagineParam);
  81. return saveConversation(user.getId(), user.getMode(),result,"IMAGINE", StringUtils.EMPTY, botType);
  82. }
  83. public MidjourneyUserConversation saveConversation(Long userId,Integer mode,SubmitResult result, String action,String prompt, String botType) throws Exception {
  84. Long taskId = Long.parseLong(result.getResult());
  85. Map<String, Object> properties = result.getProperties();
  86. MidjourneyUserConversation conversation = new MidjourneyUserConversation()
  87. .setUserId(userId).setTaskId(taskId).setAction(action).setPrompt(prompt)
  88. .setMode(mode).setStartTime(System.currentTimeMillis()).setProgress("0%")
  89. .setStatus(result.getCode() == 21 ? "NOT_START" : "IN_PROGRESS").setTaskId(taskId);
  90. if (MapUtil.isNotEmpty(properties)) {
  91. conversation.setProperties(Jsons.toJson(properties));
  92. conversation.setChannelId(properties.get("discordChannelId") == null ? null : Long.parseLong(properties.get("discordChannelId").toString()))
  93. .setInstanceId(properties.get("discordInstanceId") == null ? null : Long.parseLong(properties.get("discordInstanceId").toString()));
  94. }
  95. if (conversation.getInstanceId() == null) {
  96. conversation.setInstanceId(result.getInstanceId());
  97. }
  98. conversationMapper.insert(conversation);
  99. return conversation;
  100. }
  101. @Override
  102. public MidjourneyUserConversation submitDescribe(MidjourneyUser user, String botType, String base64) throws Exception {
  103. Map<String, Object> param = MapUtil.builder(new HashMap<String,Object>())
  104. .put("state", user.getId())
  105. .put("base64", base64)
  106. .build();
  107. SubmitResult result = submit(user.getMode(),"describe", null, param);
  108. return saveConversation(user.getId(), user.getMode(),result,"DESCRIBE", StringUtils.EMPTY, botType);
  109. }
  110. @Override
  111. public MidjourneyUserConversation submitBlend(MidjourneyUser user, BlendDimensions dimensions, String botType, List<String> base64Array) throws Exception {
  112. Map<String, Object> param = MapUtil.builder(new HashMap<String,Object>())
  113. .put("base64Array", base64Array)
  114. .put("state", user.getId())
  115. .build();
  116. if (dimensions != null) {
  117. param.put("dimensions", dimensions);
  118. }
  119. SubmitResult result = submit(user.getMode(),"blend", null, param);
  120. return saveConversation(user.getId(), user.getMode(),result,"BLEND", StringUtils.EMPTY, botType);
  121. }
  122. @Override
  123. public MidjourneyUserConversation submitModal(MidjourneyUser user, Long taskId, String prompt, String maskBase64) throws Exception {
  124. Map<String, Object> param = MapUtil.builder(new HashMap<String,Object>())
  125. .put("taskId", taskId)
  126. .put("state", user.getId())
  127. .build();
  128. if (StringUtils.isNotBlank(prompt)) {
  129. param.put("prompt", prompt);
  130. }
  131. if (StringUtils.isNotBlank(maskBase64)) {
  132. param.put("maskBase64", maskBase64);
  133. }
  134. SubmitResult result = submit(user.getMode(),"modal", null, param);
  135. return saveConversation(user.getId(), user.getMode(),result,"MODAL", StringUtils.EMPTY, StringUtils.EMPTY);
  136. }
  137. @Override
  138. public MidjourneyUserConversation submitShorten(MidjourneyUser user, String botType, String prompt) throws Exception {
  139. Map<String, Object> param = MapUtil.builder(new HashMap<String,Object>())
  140. .put("prompt", prompt)
  141. .put("state", user.getId())
  142. .build();
  143. SubmitResult result = submit(user.getMode(),"shorten", null, param);
  144. return saveConversation(user.getId(), user.getMode(), result,"SHORTEN", StringUtils.EMPTY, botType);
  145. }
  146. /**
  147. * 恢复次数
  148. */
  149. public Long recoverUserLimit(Long id,Integer mode,Long num){
  150. if (mode == 1){
  151. num = redisService.incr(RedisService.key.MIDJOURNEY_FAST_LIMIT.getName() + id, 1L);
  152. }
  153. if (mode == 2){
  154. if (num != null) {
  155. num = redisService.incr(RedisService.key.MIDJOURNEY_RELAX_LIMIT.getName() + id, 1L);
  156. }
  157. }
  158. return num;
  159. }
  160. /**
  161. * 同步数据库
  162. */
  163. public void syncUser(Long id,Integer mode,Long num){
  164. log.info("同步次数 id:{},mode:{},num:{}",id,mode,num);
  165. if (num == null){
  166. return;
  167. }
  168. LambdaUpdateWrapper<MidjourneyUser> wrapper = Wrappers.lambdaUpdate(MidjourneyUser.class)
  169. .eq(MidjourneyUser::getId, id);
  170. if (mode == 1){
  171. wrapper.set(MidjourneyUser::getMjFastNum, num);
  172. }
  173. if (mode == 2){
  174. wrapper.set(MidjourneyUser::getMjRelaxNum, num);
  175. }
  176. midjourneyUserMapper.update(null, wrapper);
  177. }
  178. @Override
  179. public SubmitResult submitAction(MidjourneyUser user, Long taskId, String customId, Long num, String botType) throws Exception {
  180. MidjourneyUserConversation conversation = conversationMapper.selectOne(Wrappers.lambdaQuery(MidjourneyUserConversation.class).eq(MidjourneyUserConversation::getTaskId, taskId).last("limit 1"));
  181. if (conversation == null) {
  182. throw BusinessRuntimeException.getInstance("关联任务不存在或已失效");
  183. }
  184. if (!user.getMode().equals(conversation.getMode())) {
  185. throw BusinessRuntimeException.getInstance("当前出图模式与关联任务出图模式不符");
  186. }
  187. if (StringUtils.isNotBlank(conversation.getButtons())){
  188. List<MessageButton> messageButtons = Jsons.parseList(conversation.getButtons(), MessageButton.class);
  189. messageButtons.forEach(button -> {
  190. if (button.getCustomId().equals(customId)) {
  191. log.info("action customId:{}", customId);
  192. button.setStyle(3);
  193. }
  194. });
  195. conversation.setButtons(Jsons.toJson(messageButtons));
  196. }
  197. if (customId.contains("BOOKMARK")) {
  198. conversation.setBookmark(true);
  199. conversationMapper.updateById(conversation);
  200. return null;
  201. }
  202. Map<String, Object> param = MapUtil.builder(new HashMap<String,Object>())
  203. .put("taskId", taskId)
  204. .put("state", user.getId())
  205. .put("customId", customId)
  206. .build();
  207. SubmitResult result = submit(user.getMode(),"action", conversation.getInstanceId(),param);
  208. if (result.getCode() == 21) {
  209. // 以上操作有弹窗确认,恢复次数
  210. recoverUserLimit(user.getId(), user.getMode(),num);
  211. if (user.getMode() == 2){
  212. redisService.hdecr(RedisService.key.MIDJOURNEY_ACCOUNT.getName() , String.valueOf(conversation.getInstanceId()), 1.0);
  213. }
  214. }else {
  215. syncUser(user.getId(), user.getMode(),num);
  216. }
  217. saveConversation(user.getId(), user.getMode(), result,"ACTION", StringUtils.EMPTY, botType);
  218. return result;
  219. }
  220. public SubmitResult submit(Integer mode,String action,Long instanceId, Map<String, Object> param) throws Exception {
  221. String url = "";
  222. String accountWithMinUsage = "";
  223. if (mode == 1) {
  224. param.put("mode", "FAST");
  225. url = FAST_HOST;
  226. } else if (mode == 2) {
  227. url = RELAX_HOST;
  228. //慢速查询在使用次数最少的账号
  229. accountWithMinUsage = getAccountWithMinUsage(instanceId);
  230. param.put("accountFilter",MapUtil.builder(new HashMap<String,Object>())
  231. .put("instanceId",accountWithMinUsage).build());
  232. }
  233. url = url + getActionUrl(action);
  234. param.put("notifyHook",midjourneyHost +(EnvCommonService.active.equals(envCommonService.getEnv()) ? "/8081":"/8082") + "/api/applets/midjourney/notifyHook");
  235. String body = HttpRequest.post(url).body(Jsons.toJson(param)).header("Authorization", FAST_TOKEN).execute().body();
  236. log.info("action body:{}", body);
  237. SubmitResult submitResult = Jsons.parseObject(body, SubmitResult.class);
  238. int code = submitResult.getCode();
  239. if (code != 1 && code != 21 && code != 22) {
  240. if (code == 3) {
  241. if(mode == 2 && StringUtils.isNotBlank(accountWithMinUsage)){
  242. redisService.hdel(RedisService.key.MIDJOURNEY_ACCOUNT.getName(),accountWithMinUsage);
  243. String finalAccountWithMinUsage = accountWithMinUsage;
  244. TASK_EXECUTOR.execute(() -> {
  245. MidjourneyAccount account = midjourneyAccountService.getOne(Wrappers.lambdaQuery(MidjourneyAccount.class).eq(MidjourneyAccount::getInstanceId, finalAccountWithMinUsage).last("limit 1"));
  246. midjourneyAccountService.updateStatus(account.getId());
  247. });
  248. }
  249. throw BusinessRuntimeException.getInstance("账号不存在");
  250. }
  251. if (code == 4) {
  252. throw BusinessRuntimeException.getInstance(submitResult.getDescription());
  253. }
  254. if (code == 24) {
  255. throw BusinessRuntimeException.getInstance("prompt包含敏感词");
  256. }
  257. log.error("action:" + action + " error message:" + submitResult.getDescription());
  258. throw BusinessRuntimeException.getInstance("队列已满,请稍后尝试");
  259. }
  260. submitResult.setInstanceId(Long.valueOf(accountWithMinUsage));
  261. // 增加使用次数
  262. if (StringUtils.isNotBlank(accountWithMinUsage) && mode == 2) {
  263. redisService.hincr(RedisService.key.MIDJOURNEY_ACCOUNT.getName(), accountWithMinUsage, 1.0);
  264. }
  265. return submitResult;
  266. }
  267. public String getAccountWithMinUsage(Long instanceId) {
  268. String key = RedisService.key.MIDJOURNEY_ACCOUNT.getName();
  269. try {
  270. // 获取所有账号ID和使用次数
  271. Map<Object, Object> accountsUsage = redisService.hmget(key);
  272. if (accountsUsage == null || accountsUsage.isEmpty()) {
  273. Map<Object, Object> map = midjourneyAccountService.list(Wrappers.lambdaQuery(MidjourneyAccount.class).eq(MidjourneyAccount::getStatus,Boolean.TRUE)).stream().collect(Collectors.toMap(k -> k.getInstanceId().toString(), v -> 0));
  274. // 保存所有账号ID和使用次数
  275. redisService.hmset(key, map);
  276. }
  277. // 找到使用次数最少的账号ID
  278. String minAccountId = null;
  279. if (instanceId != null) {
  280. minAccountId = instanceId.toString();
  281. }else {
  282. int minUsage = Integer.MAX_VALUE;
  283. for (Map.Entry<Object, Object> entry : accountsUsage.entrySet()) {
  284. int usage = Integer.parseInt(entry.getValue().toString());
  285. if (usage < minUsage) {
  286. minUsage = usage;
  287. minAccountId = entry.getKey().toString();
  288. }
  289. }
  290. }
  291. return minAccountId;
  292. } catch (Exception e) {
  293. throw BusinessRuntimeException.getInstance("获取账号失败");
  294. }
  295. }
  296. private String getActionUrl(String action) {
  297. switch (action) {
  298. case "imagine":
  299. return "/mj/submit/imagine";
  300. case "action":
  301. return "/mj/submit/action";
  302. case "describe":
  303. return "/mj/submit/describe";
  304. case "blend":
  305. return"/mj/submit/blend";
  306. case "modal":
  307. return "/mj/submit/modal";
  308. case "shorten":
  309. return "/mj/submit/shorten";
  310. default: throw new IllegalArgumentException("Unknown action: " + action);
  311. }
  312. }
  313. @Override
  314. public List<MidjourneyUserConversation> listConversationByIds(Integer mode, List<Long> ids) {
  315. List<MidjourneyUserConversation> list = new ArrayList<>();
  316. ids.forEach(id ->{
  317. String queryKey = RedisService.key.MIDJOURNEY_QUERY.getName();
  318. Long count = redisService.incr(queryKey + id, 1L);
  319. if (count%5 == 0) {
  320. try {
  321. listByIds(mode,List.of(id)).forEach(json ->{
  322. JSONObject jsons = JSONUtil.parseObj(json);
  323. MidjourneyUserConversation conversation = JSONUtil.toBean(jsons, MidjourneyUserConversation.class);
  324. if (StringUtil.isNotBlank(conversation.getImageUrl())) {
  325. conversation.setImageUrl(conversation.getImageUrl().replace("cdn.discordapp.com", "mj.galaxydvd.com"));
  326. }
  327. conversation.setUserId(jsons.getLong("state"));
  328. conversation.setTaskId(jsons.getLong("id"));
  329. list.add(conversation);
  330. TASK_EXECUTOR.execute(() -> {
  331. try {
  332. sync(conversation);
  333. } catch (IOException e) {
  334. throw new RuntimeException(e);
  335. }
  336. });
  337. });
  338. } catch (Exception e) {
  339. throw new RuntimeException(e);
  340. }
  341. }else {
  342. list.add((MidjourneyUserConversation) redisService.get(RedisService.key.MIDJOURNEY_CONVERSATION.getName() + id));
  343. }
  344. });
  345. return list;
  346. }
  347. public void sync(MidjourneyUserConversation conversation) throws IOException {
  348. if ((StringUtils.isNotBlank(conversation.getProgress()) && progress.contains(conversation.getProgress())) || status.contains(conversation.getStatus())) {
  349. log.info("同步任务:{},进度:{}",conversation.getTaskId(),conversation.getProgress());
  350. Long userId = conversation.getUserId();
  351. MidjourneyUserConversation dbConversation = conversationMapper.selectOne(Wrappers.lambdaQuery(MidjourneyUserConversation.class).eq(MidjourneyUserConversation::getTaskId, conversation.getTaskId())
  352. .eq(MidjourneyUserConversation::getUserId, userId).orderByDesc(MidjourneyUserConversation::getId).last("limit 1"));
  353. if (dbConversation != null) {
  354. if ("SUCCESS".equals(dbConversation.getStatus())) {
  355. return;
  356. }
  357. conversation.setId(dbConversation.getId());
  358. conversationMapper.updateById(conversation);
  359. if (dbConversation.getMode() == 2){
  360. redisService.hdecr(RedisService.key.MIDJOURNEY_ACCOUNT.getName() , dbConversation.getInstanceId().toString(), 1.0);
  361. }
  362. TASK_EXECUTOR.execute(() -> {
  363. try {
  364. String imageUrl = conversation.getImageUrl();
  365. if (StringUtil.isNotBlank(imageUrl)) {
  366. conversation.setImageUrl(uploadPic(imageUrl, "conversation"+dbConversation.getId()));
  367. conversationMapper.updateById(conversation);
  368. }
  369. } catch (IOException e) {
  370. log.error("上传图片失败",e);
  371. }
  372. });
  373. //失败返还次数
  374. if ("FAILURE".equals(conversation.getStatus())){
  375. Object num = redisService.get(RedisService.key.MIDJOURNEY_RELAX_LIMIT.getName() + userId);
  376. LambdaUpdateWrapper<MidjourneyUser> wrapper = Wrappers.lambdaUpdate(MidjourneyUser.class)
  377. .eq(MidjourneyUser::getId, userId);
  378. Integer mode = dbConversation.getMode();
  379. if (mode == 1){
  380. num = redisService.incr(RedisService.key.MIDJOURNEY_FAST_LIMIT.getName() + userId, 1L);
  381. wrapper.set(MidjourneyUser::getMjFastNum, num);
  382. }
  383. if (mode == 2){
  384. if (num != null) {
  385. num = redisService.incr(RedisService.key.MIDJOURNEY_RELAX_LIMIT.getName() + userId, 1L);
  386. wrapper.set(MidjourneyUser::getMjRelaxNum, num);
  387. }
  388. }
  389. midjourneyUserMapper.update(null, wrapper);
  390. }
  391. }
  392. }
  393. }
  394. private static String uploadPic(String url, String prefix) throws IOException {
  395. byte[] body = HttpUtil.downloadBytes(url);
  396. Path tempFile = Files.createTempFile(prefix, ".png");
  397. File file = tempFile.toFile();
  398. try (FileImageOutputStream imageOutput = new FileImageOutputStream(file)) {
  399. imageOutput.write(body, 0, body.length);
  400. }
  401. Map<String, Object> paramMap = new HashMap<>();
  402. paramMap.put("file", file);
  403. String json = HttpUtil.post("https://files.liuliangbang.vip/pic/ups", paramMap);
  404. log.info("上传图片结果:url:{},json:{}",url, json);
  405. FileUtil.del(file);
  406. return new JSONObject(json).getJSONObject("value").getJSONArray("saved").getJSONObject(0).getJSONObject("info").getStr("cdnUrl");
  407. }
  408. private static List<String> uploadBase64Pic(List<String> base64Array) throws IOException {
  409. List<String> list = new ArrayList<>();
  410. for (String base64 : base64Array) {
  411. String fileExt = "jpeg";
  412. if (base64.contains(";")){
  413. fileExt = base64.split(";")[0].split("/")[1];
  414. base64 = base64.split(",")[1];
  415. }
  416. byte[] body = Base64.getDecoder().decode(base64);
  417. Path tempFile = Files.createTempFile(UUID.randomUUID().toString(), "." + fileExt);
  418. try (FileImageOutputStream imageOutput = new FileImageOutputStream(tempFile.toFile())) {
  419. imageOutput.write(body, 0, body.length);
  420. }
  421. Map<String, Object> paramMap = new HashMap<>();
  422. paramMap.put("file", tempFile.toFile());
  423. String json = HttpUtil.post("https://files.liuliangbang.vip/pic/ups", paramMap);
  424. list.add(new JSONObject(json).getJSONObject("value").getJSONArray("saved").getJSONObject(0).getJSONObject("info").getStr("cdnUrl"));
  425. }
  426. return list;
  427. }
  428. public JSONArray listByIds(Integer mode,List<Long> ids) throws Exception {
  429. Map<String, Object> param = MapUtil.builder(new HashMap<String, Object>()).put("ids", ids).build();
  430. String body = HttpRequest.post((mode == 1 ? FAST_HOST : RELAX_HOST) + "/mj/task/list-by-condition").header("Authorization", FAST_TOKEN).body(Jsons.toJson(param)).execute().body();
  431. log.info("listByIds body:{}", body);
  432. return JSONUtil.parseArray(body);
  433. }
  434. @Override
  435. public MidjourneyUserConversation cancelConversation(MidjourneyUser user, Long id) {
  436. MidjourneyUserConversation conversation = conversationMapper.selectOne(Wrappers.lambdaQuery(MidjourneyUserConversation.class).eq(MidjourneyUserConversation::getId, id));
  437. if (conversation == null) {
  438. throw BusinessRuntimeException.getInstance("会话不存在");
  439. }
  440. if (!Objects.equals(conversation.getUserId(), user.getId())) {
  441. throw BusinessRuntimeException.getInstance("不是您的会话");
  442. }
  443. conversation.setStatus("CANCEL");
  444. conversationMapper.updateById(conversation);
  445. TASK_EXECUTOR.execute(() -> {
  446. cancel(user.getMode(),conversation.getTaskId());
  447. });
  448. return conversation;
  449. }
  450. public void cancel(Integer mode,Long taskId) {
  451. String body = HttpUtil.post(mode == 1 ? FAST_HOST : RELAX_HOST + "/mj/task/"+taskId+"/cancel",MapUtil.empty());
  452. log.info("cancel body:{}", body);
  453. JSONObject jsonObject = JSONUtil.parseObj(body);
  454. if (jsonObject.getInt("code") != 1) {
  455. throw BusinessRuntimeException.getInstance("cancel error message:" + jsonObject.getStr("description"));
  456. }
  457. }
  458. @Override
  459. public void notifyHook(String json) {
  460. log.info("notifyHook:{}", json);
  461. JSONObject jsons = JSONUtil.parseObj(json);
  462. MidjourneyUserConversation conversation = JSONUtil.toBean(jsons, MidjourneyUserConversation.class);
  463. conversation.setUserId(jsons.getLong("state"));
  464. conversation.setTaskId(jsons.getLong("id"));
  465. if (StringUtil.isNotBlank(conversation.getImageUrl())) {
  466. conversation.setImageUrl(conversation.getImageUrl().replace("cdn.discordapp.com", "mj.galaxydvd.com"));
  467. }
  468. redisService.set(RedisService.key.MIDJOURNEY_CONVERSATION.getName() + conversation.getTaskId(), conversation,RedisService.key.MIDJOURNEY_CONVERSATION.getTimeout());
  469. TASK_EXECUTOR.execute(() -> {
  470. try {
  471. sync(conversation);
  472. } catch (IOException e) {
  473. throw new RuntimeException(e);
  474. }
  475. });
  476. }
  477. }