|
|
@@ -0,0 +1,265 @@
|
|
|
+package com.cyksj.service.midjourney.impl;
|
|
|
+
|
|
|
+import cn.hutool.core.bean.BeanUtil;
|
|
|
+import cn.hutool.core.collection.CollectionUtil;
|
|
|
+import cn.hutool.core.map.MapUtil;
|
|
|
+import cn.hutool.http.HttpUtil;
|
|
|
+import cn.hutool.json.JSONArray;
|
|
|
+import cn.hutool.json.JSONObject;
|
|
|
+import cn.hutool.json.JSONUtil;
|
|
|
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
+import com.cyksj.common.exception.BusinessRuntimeException;
|
|
|
+import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
|
|
|
+import com.cyksj.mapper.MidjourneyUserConversationMapper;
|
|
|
+import com.cyksj.model.dto.BlendDimensions;
|
|
|
+import com.cyksj.model.entity.MidjourneyAccount;
|
|
|
+import com.cyksj.model.entity.MidjourneyUser;
|
|
|
+import com.cyksj.model.entity.MidjourneyUserConversation;
|
|
|
+import com.cyksj.service.midjourney.MidjourneyService;
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import javax.imageio.stream.FileImageOutputStream;
|
|
|
+import java.io.IOException;
|
|
|
+import java.nio.file.Files;
|
|
|
+import java.nio.file.Path;
|
|
|
+import java.util.*;
|
|
|
+import java.util.regex.Matcher;
|
|
|
+import java.util.regex.Pattern;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @author zwhui
|
|
|
+ * @date 2024/4/23 15:44
|
|
|
+ */
|
|
|
+@Service
|
|
|
+@Slf4j
|
|
|
+@RequiredArgsConstructor
|
|
|
+public class MidjourneyServiceImpl implements MidjourneyService {
|
|
|
+ private static final String HOST = "http://43.154.230.104:8080";
|
|
|
+
|
|
|
+ private final MidjourneyUserConversationMapper conversationMapper;
|
|
|
+
|
|
|
+ private static final List<String> progress = Arrays.asList("25%", "45%", "75%", "100%");
|
|
|
+
|
|
|
+ private static final GlobalThreadPoolTaskExecutor TASK_EXECUTOR = GlobalThreadPoolTaskExecutor.getInstance();
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public MidjourneyUserConversation submitImagine(MidjourneyUser user, String prompt, List<String> base64Array) {
|
|
|
+ JSONObject imagine = imagine(user.getId(), prompt, base64Array);
|
|
|
+ JSONObject properties = imagine.getJSONObject("properties");
|
|
|
+ return saveConversation(user.getId(), imagine.getLong("result"), properties,"IMAGINE", prompt);
|
|
|
+ }
|
|
|
+
|
|
|
+ public MidjourneyUserConversation saveConversation(Long userId,Long taskId,JSONObject properties, String action,String prompt) {
|
|
|
+ MidjourneyUserConversation conversation = new MidjourneyUserConversation()
|
|
|
+ .setUserId(userId).setTaskId(taskId).setAction(action).setPrompt(prompt)
|
|
|
+ .setMode(1).setStartTime(System.currentTimeMillis()).setProgress("0%")
|
|
|
+ .setStatus("IN_PROGRESS").setChannelId(properties.getLong("discordChannelId"))
|
|
|
+ .setInstanceId(properties.getLong("discordInstanceId")).setTaskId(taskId);
|
|
|
+ conversationMapper.insert(conversation);
|
|
|
+ return conversation;
|
|
|
+ }
|
|
|
+
|
|
|
+ private JSONObject imagine(Long userId,String prompt, List<String> base64Array) {
|
|
|
+ Map<String, Object> imagineParam = MapUtil.builder(new HashMap<String,Object>())
|
|
|
+ .put("prompt", prompt).put("state", userId).build();
|
|
|
+ if (CollectionUtil.isNotEmpty(base64Array)) {
|
|
|
+ imagineParam.put("base64Array", base64Array);
|
|
|
+ }
|
|
|
+ String body = HttpUtil.post(HOST + "/mj/submit/imagine", imagineParam);
|
|
|
+ log.info("imagine body:{}", body);
|
|
|
+ JSONObject jsonObject = JSONUtil.parseObj(body);
|
|
|
+ if (jsonObject.getInt("code") != 1) {
|
|
|
+ throw BusinessRuntimeException.getInstance("imagine error message:" + jsonObject.getStr("description"));
|
|
|
+ }
|
|
|
+ return jsonObject;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public MidjourneyUserConversation submitDescribe(MidjourneyUser user, String base64) {
|
|
|
+ JSONObject describe = describe(user.getId(), base64);
|
|
|
+ JSONObject properties = describe.getJSONObject("properties");
|
|
|
+ return saveConversation(user.getId(), describe.getLong("result"), properties,"DESCRIBE", StringUtils.EMPTY);
|
|
|
+ }
|
|
|
+
|
|
|
+ private JSONObject describe(Long userId,String base64) {
|
|
|
+ Map<String, Object> param = MapUtil.builder(new HashMap<String,Object>()).put("state", userId).put("base64", base64).build();
|
|
|
+ String body = HttpUtil.post(HOST + "/mj/submit/describe", param);
|
|
|
+ log.info("describe body:{}", body);
|
|
|
+ JSONObject jsonObject = JSONUtil.parseObj(body);
|
|
|
+ if (jsonObject.getInt("code") != 1) {
|
|
|
+ throw BusinessRuntimeException.getInstance("describe error message:" + jsonObject.getStr("description"));
|
|
|
+ }
|
|
|
+ return jsonObject;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public MidjourneyUserConversation submitBlend(MidjourneyUser user, BlendDimensions dimensions, List<String> base64Array) {
|
|
|
+ JSONObject blend = blend(user.getId(), dimensions, base64Array);
|
|
|
+ JSONObject properties = blend.getJSONObject("properties");
|
|
|
+ return saveConversation(user.getId(), blend.getLong("result"), properties,"BLEND", StringUtils.EMPTY);
|
|
|
+ }
|
|
|
+
|
|
|
+ private JSONObject blend(Long userId,BlendDimensions dimensions, List<String> base64Array) {
|
|
|
+ Map<String, Object> param = MapUtil.builder(new HashMap<String,Object>())
|
|
|
+ .put("base64Array", base64Array).put("state", userId).build();
|
|
|
+ if (dimensions != null) {
|
|
|
+ param.put("dimensions", dimensions);
|
|
|
+ }
|
|
|
+ String body = HttpUtil.post(HOST + "/mj/submit/blend", param);
|
|
|
+ log.info("blend body:{}", body);
|
|
|
+ JSONObject jsonObject = JSONUtil.parseObj(body);
|
|
|
+ if (jsonObject.getInt("code") != 1) {
|
|
|
+ throw BusinessRuntimeException.getInstance("blend error message:" + jsonObject.getStr("description"));
|
|
|
+ }
|
|
|
+ return jsonObject;
|
|
|
+ }
|
|
|
+ @Override
|
|
|
+ public MidjourneyUserConversation submitModal(MidjourneyUser user, Long taskId, String prompt, String maskBase64) {
|
|
|
+ JSONObject modal = modal(user.getId(),taskId, prompt, maskBase64);
|
|
|
+ JSONObject properties = modal.getJSONObject("properties");
|
|
|
+ return saveConversation(user.getId(), modal.getLong("result"), properties,"MODAL", prompt);
|
|
|
+ }
|
|
|
+
|
|
|
+ private JSONObject modal(Long userId,Long taskId,String prompt, String maskBase64) {
|
|
|
+ Map<String, Object> param = MapUtil.builder(new HashMap<String,Object>())
|
|
|
+ .put("taskId", taskId).put("state", userId).build();
|
|
|
+ if (StringUtils.isNotBlank(prompt)) {
|
|
|
+ param.put("prompt", prompt);
|
|
|
+ }
|
|
|
+ if (StringUtils.isNotBlank(maskBase64)) {
|
|
|
+ param.put("maskBase64", maskBase64);
|
|
|
+ }
|
|
|
+ String body = HttpUtil.post(HOST + "/mj/submit/modal", param);
|
|
|
+ log.info("shorten body:{}", body);
|
|
|
+ JSONObject jsonObject = JSONUtil.parseObj(body);
|
|
|
+ if (jsonObject.getInt("code") != 1) {
|
|
|
+ throw BusinessRuntimeException.getInstance("shorten error message:" + jsonObject.getStr("description"));
|
|
|
+ }
|
|
|
+ return jsonObject;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public MidjourneyUserConversation submitShorten(MidjourneyUser user, String prompt) {
|
|
|
+ JSONObject shorten = shorten(user.getId(), prompt);
|
|
|
+ JSONObject properties = shorten.getJSONObject("properties");
|
|
|
+ return saveConversation(user.getId(), shorten.getLong("result"), properties,"SHORTEN", prompt);
|
|
|
+ }
|
|
|
+
|
|
|
+ private JSONObject shorten(Long userId,String prompt) {
|
|
|
+ Map<String, Object> param = MapUtil.builder(new HashMap<String,Object>())
|
|
|
+ .put("prompt", prompt).put("state", userId).build();
|
|
|
+ String body = HttpUtil.post(HOST + "/mj/submit/shorten", param);
|
|
|
+ log.info("shorten body:{}", body);
|
|
|
+ JSONObject jsonObject = JSONUtil.parseObj(body);
|
|
|
+ if (jsonObject.getInt("code") != 1) {
|
|
|
+ throw BusinessRuntimeException.getInstance("shorten error message:" + jsonObject.getStr("description"));
|
|
|
+ }
|
|
|
+ return jsonObject;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public MidjourneyUserConversation submitAction(MidjourneyUser user, Long taskId, String customId) {
|
|
|
+ JSONObject action = action(user.getId(), taskId, customId);
|
|
|
+ JSONObject properties = action.getJSONObject("properties");
|
|
|
+ return saveConversation(user.getId(), action.getLong("result"), properties,"ACTION", StringUtils.EMPTY);
|
|
|
+ }
|
|
|
+
|
|
|
+ public JSONObject action(Long userId, Long taskId, String customId) {
|
|
|
+ Map<String, Object> param = MapUtil.builder(new HashMap<String,Object>())
|
|
|
+ .put("taskId", taskId).put("state", userId).put("customId", customId).build();
|
|
|
+ String body = HttpUtil.post(HOST + "/mj/submit/action", param);
|
|
|
+ log.info("action body:{}", body);
|
|
|
+ JSONObject jsonObject = JSONUtil.parseObj(body);
|
|
|
+ if (jsonObject.getInt("code") != 1) {
|
|
|
+ throw BusinessRuntimeException.getInstance("action error message:" + jsonObject.getStr("description"));
|
|
|
+ }
|
|
|
+ return jsonObject;
|
|
|
+ }
|
|
|
+ @Override
|
|
|
+ public List<MidjourneyUserConversation> listConversationByIds(List<Long> ids) {
|
|
|
+ List<MidjourneyUserConversation> list = new ArrayList<>();
|
|
|
+ listByIds(ids).forEach(json -> {
|
|
|
+ JSONObject jsons = JSONUtil.parseObj(json);
|
|
|
+ MidjourneyUserConversation conversation = JSONUtil.toBean(jsons, MidjourneyUserConversation.class);
|
|
|
+ conversation.setUserId(jsons.getLong("state"));
|
|
|
+ conversation.setTaskId(jsons.getLong("id"));
|
|
|
+ list.add(conversation);
|
|
|
+ TASK_EXECUTOR.execute(() -> {
|
|
|
+ try {
|
|
|
+ sync(conversation);
|
|
|
+ } catch (IOException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ return list;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ public void sync(MidjourneyUserConversation conversation) throws IOException {
|
|
|
+ if (progress.contains(conversation.getProgress())){
|
|
|
+ log.info("同步任务:{},进度:{}",conversation.getTaskId(),conversation.getProgress());
|
|
|
+ MidjourneyUserConversation dbConversation = conversationMapper.selectOne(Wrappers.lambdaQuery(MidjourneyUserConversation.class).eq(MidjourneyUserConversation::getTaskId, conversation.getTaskId())
|
|
|
+ .eq(MidjourneyUserConversation::getUserId, conversation.getUserId()).last("limit 1"));
|
|
|
+ if (dbConversation != null) {
|
|
|
+ if (StringUtils.isNotBlank(conversation.getImageUrl())) {
|
|
|
+ dbConversation.setImageUrl(uploadPic(conversation.getImageUrl(), "conversation"+dbConversation.getId()));
|
|
|
+ }
|
|
|
+ conversation.setId(dbConversation.getId());
|
|
|
+ conversationMapper.updateById(conversation);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private static String uploadPic(String url, String prefix) throws IOException {
|
|
|
+ byte[] body = HttpUtil.downloadBytes(url);
|
|
|
+ Path tempFile = Files.createTempFile(prefix, ".png");
|
|
|
+ try (FileImageOutputStream imageOutput = new FileImageOutputStream(tempFile.toFile())) {
|
|
|
+ imageOutput.write(body, 0, body.length);
|
|
|
+ }
|
|
|
+ Map<String, Object> paramMap = new HashMap<>();
|
|
|
+ paramMap.put("file", tempFile.toFile());
|
|
|
+ JSONObject result = JSONUtil.parseObj(HttpUtil.post("https://files.liuliangbang.vip/pic/ups", paramMap));
|
|
|
+ return result.getJSONObject("value").getJSONArray("saved").getJSONObject(0).getJSONObject("info").getStr("cdnUrl");
|
|
|
+ }
|
|
|
+ public JSONArray listByIds(List<Long> ids) {
|
|
|
+ Map<String, Object> param = MapUtil.builder(new HashMap<String, Object>()).put("ids", ids).build();
|
|
|
+ String body = HttpUtil.post(HOST + "/mj/task/list-by-ids", param);
|
|
|
+ log.info("listByIds body:{}", body);
|
|
|
+ return JSONUtil.parseArray(body);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public MidjourneyUserConversation cancelConversation(MidjourneyUser user, Long id) {
|
|
|
+ MidjourneyUserConversation conversation = conversationMapper.selectOne(Wrappers.lambdaQuery(MidjourneyUserConversation.class).eq(MidjourneyUserConversation::getId, id));
|
|
|
+ if (conversation == null) {
|
|
|
+ throw BusinessRuntimeException.getInstance("会话不存在");
|
|
|
+ }
|
|
|
+ if (!Objects.equals(conversation.getUserId(), user.getId())) {
|
|
|
+ throw BusinessRuntimeException.getInstance("不是您的会话");
|
|
|
+ }
|
|
|
+ conversation.setStatus("CANCEL");
|
|
|
+ conversationMapper.updateById(conversation);
|
|
|
+ cancel(conversation.getTaskId());
|
|
|
+ return conversation;
|
|
|
+ }
|
|
|
+
|
|
|
+ public void cancel(Long taskId) {
|
|
|
+ String body = HttpUtil.post(HOST + "/mj/task/"+taskId+"/cancel",MapUtil.empty());
|
|
|
+ log.info("cancel body:{}", body);
|
|
|
+ JSONObject jsonObject = JSONUtil.parseObj(body);
|
|
|
+ if (jsonObject.getInt("code") != 1) {
|
|
|
+ throw BusinessRuntimeException.getInstance("cancel error message:" + jsonObject.getStr("description"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+}
|