Procházet zdrojové kódy

Merge branch 'midjourney' into pre

# Conflicts:
#	netflix-service/src/main/java/com/cyksj/redis/RedisService.java
#	netflix-web/src/main/java/com/cyksj/web/controller/mirror/MidjourneyController.java
#	netflix-web/src/main/resources/application-prd.yml
zwhui před 2 roky
rodič
revize
fa31c974fd
17 změnil soubory, kde provedl 805 přidání a 524 odebrání
  1. 14 0
      netflix-dao/src/main/java/com/cyksj/mapper/MidjourneyUserMapper.java
  2. 8 0
      netflix-dao/src/main/java/com/cyksj/model/dto/SubmitActionDTO.java
  3. 8 0
      netflix-dao/src/main/java/com/cyksj/model/dto/SubmitBlendDTO.java
  4. 8 0
      netflix-dao/src/main/java/com/cyksj/model/dto/SubmitDescribeDTO.java
  5. 9 0
      netflix-dao/src/main/java/com/cyksj/model/dto/SubmitImagineDTO.java
  6. 8 0
      netflix-dao/src/main/java/com/cyksj/model/dto/SubmitShortenDTO.java
  7. 6 0
      netflix-dao/src/main/java/com/cyksj/model/entity/MidjourneyUserConversation.java
  8. 1 0
      netflix-dao/src/main/java/com/cyksj/model/response/SubmitResult.java
  9. 7 3
      netflix-service/src/main/java/com/cyksj/redis/RedisService.java
  10. 9 5
      netflix-service/src/main/java/com/cyksj/service/midjourney/MidjourneyService.java
  11. 8 0
      netflix-service/src/main/java/com/cyksj/service/midjourney/impl/MidJourneyAccountServiceImpl.java
  12. 256 78
      netflix-service/src/main/java/com/cyksj/service/midjourney/impl/MidjourneyServiceImpl.java
  13. 13 13
      netflix-service/src/main/java/com/cyksj/service/scheduler/SchedulerService.java
  14. 272 272
      netflix-service/src/main/java/com/cyksj/service/scheduler/impl/SchedulerServiceImpl.java
  15. 83 83
      netflix-service/src/main/java/com/cyksj/task/CorpChatScheduler.java
  16. 3 3
      netflix-web/src/main/java/com/cyksj/web/controller/manage/group/CmsGroupRelationController.java
  17. 92 67
      netflix-web/src/main/java/com/cyksj/web/controller/mirror/MidjourneyController.java

+ 14 - 0
netflix-dao/src/main/java/com/cyksj/mapper/MidjourneyUserMapper.java

@@ -2,10 +2,24 @@ package com.cyksj.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.cyksj.model.entity.MidjourneyUser;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Update;
 
 /**
  * @author zwhui
  * @date 2024/4/16 17:46
  */
 public interface MidjourneyUserMapper extends BaseMapper<MidjourneyUser> {
+
+    @Update("update midjourney_user set mj_fast_num = mj_fast_num + #{num} where id = #{id}")
+    int incrFastNum(@Param("id") Long id, @Param("num") Integer num);
+
+    @Update("update midjourney_user set mj_fast_num = mj_fast_num - #{num} where id = #{id} and mj_fast_num = #{mjFastNum} and mj_fast_num > 1")
+    int decFastNum(@Param("id") Long id, @Param("num") Integer num, @Param("mjFastNum") Integer mjFastNum);
+
+    @Update("update midjourney_user set mj_relax_num = mj_relax_num + #{num} where id = #{id}")
+    int incrRelaxNum(@Param("id") Long id, @Param("num") Integer num);
+
+    @Update("update midjourney_user set mj_relax_num = mj_relax_num - #{num} where id = #{id} and mj_relax_num = #{relaxNum} and mj_relax_num > 1")
+    int decRelaxNum(@Param("id") Long id, @Param("num") Integer num, @Param("relaxNum") Integer relaxNum);
 }

+ 8 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/SubmitActionDTO.java

@@ -22,4 +22,12 @@ public class SubmitActionDTO {
      */
     @NotNull(message = "任务ID不能为空")
     private Long taskId;
+
+    /**
+     * 机器人类型
+     * bot类型,mj(默认)或niji,可用值:MID_JOURNEY,NIJI_JOURNEY,示例值(MID_JOURNEY)
+     * MID_JOURNEY
+     * NIJI_JOURNEY
+     */
+    private String botType = "MID_JOURNEY";
 }

+ 8 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/SubmitBlendDTO.java

@@ -22,4 +22,12 @@ public class SubmitBlendDTO {
 	 * 比例: PORTRAIT(2:3); SQUARE(1:1); LANDSCAPE(3:2)
 	 */
 	private BlendDimensions dimensions = BlendDimensions.SQUARE;
+
+	/**
+	 * 机器人类型
+	 * bot类型,mj(默认)或niji,可用值:MID_JOURNEY,NIJI_JOURNEY,示例值(MID_JOURNEY)
+	 * MID_JOURNEY
+	 * NIJI_JOURNEY
+	 */
+	private String botType = "MID_JOURNEY";
 }

+ 8 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/SubmitDescribeDTO.java

@@ -16,4 +16,12 @@ public class SubmitDescribeDTO {
 	 */
 	@NotBlank(message = "请上传图片")
 	private String base64;
+
+	/**
+	 * 机器人类型
+	 * bot类型,mj(默认)或niji,可用值:MID_JOURNEY,NIJI_JOURNEY,示例值(MID_JOURNEY)
+	 * MID_JOURNEY
+	 * NIJI_JOURNEY
+	 */
+	private String botType = "MID_JOURNEY";
 }

+ 9 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/SubmitImagineDTO.java

@@ -1,5 +1,6 @@
 package com.cyksj.model.dto;
 
+import lombok.AllArgsConstructor;
 import lombok.Data;
 
 import javax.validation.constraints.NotBlank;
@@ -23,4 +24,12 @@ public class SubmitImagineDTO {
      */
     private List<String> base64Array;
 
+    /**
+     * 机器人类型
+     * bot类型,mj(默认)或niji,可用值:MID_JOURNEY,NIJI_JOURNEY,示例值(MID_JOURNEY)
+     * MID_JOURNEY
+     * NIJI_JOURNEY
+     */
+    private String botType = "MID_JOURNEY";
+
 }

+ 8 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/SubmitShortenDTO.java

@@ -16,4 +16,12 @@ public class SubmitShortenDTO {
      */
     @NotBlank(message = "提示词不能为空")
     private String prompt;
+
+    /**
+     * 机器人类型
+     * bot类型,mj(默认)或niji,可用值:MID_JOURNEY,NIJI_JOURNEY,示例值(MID_JOURNEY)
+     * MID_JOURNEY
+     * NIJI_JOURNEY
+     */
+    private String botType = "MID_JOURNEY";
 }

+ 6 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/MidjourneyUserConversation.java

@@ -44,6 +44,12 @@ public class MidjourneyUserConversation extends BaseEntity implements Serializab
      */
     private Integer mode;
 
+    /**
+     *  MID_JOURNEY
+     *  NIJI_JOURNEY
+     */
+    private String BotType;
+
     /**
      * 开始时间
      */

+ 1 - 0
netflix-dao/src/main/java/com/cyksj/model/response/SubmitResult.java

@@ -22,6 +22,7 @@ public class SubmitResult {
      */
     private String result;
 
+    private Long instanceId;
     /**
      * 扩展字段
      */

+ 7 - 3
netflix-service/src/main/java/com/cyksj/redis/RedisService.java

@@ -1,5 +1,6 @@
 package com.cyksj.redis;
 
+import com.cyksj.common.constant.RandomConstant;
 import com.cyksj.common.util.StringUtil;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
@@ -213,7 +214,7 @@ public class RedisService {
      * @param map 对应多个键值
      * @return true 成功 false 失败
      */
-    public Boolean hmset(String key, Map<String, Object> map) {
+    public Boolean hmset(String key, Map<Object, Object> map) {
         try {
             redisTemplate.opsForHash().putAll(key, map);
             return true;
@@ -835,10 +836,13 @@ public class RedisService {
         CHATGPT_CAR_LOW_CHAT("chatgpt:car:low:chat:","chatGpt 3.5 对话次数", 48 * 60 * 60L),
         //车票到期前每日通知key
         TICKET_EXPIRY_NOTIFY_DAY("ticket_expiry_notify_day:%s:%s", "车票到期前每日通知key", 60 * 60 * 24L),
-        MIDJOURNEY_FAST_LIMIT("midjourney:fast:limit:", "midjourney fast次数", 60 * 60 * 48L),
-        MIDJOURNEY_RELAX_LIMIT("midjourney:relax:limit:", "midjourney relax次数", 60 * 60 * 48L),
+        MIDJOURNEY_FAST_LIMIT("midjourney:fast:limit:", "midjourney fast次数", 60 * 60 * 24L * 7),
+        MIDJOURNEY_RELAX_LIMIT("midjourney:relax:limit:", "midjourney relax次数", 60 * 60 * 24L * 7),
         MIDJOURNEY_EXPIRE_TIME("midjourney:expire:time:", "midjourney expire time", 60 * 60 * 48L),
         MIDJOURNEY_USER("midjourney:user:", "midjourney user", 60 * 60 * 2L),
+        MIDJOURNEY_CONVERSATION("midjourney:conversation:", "midjourney conversation", 60 * 60 * 2L),
+        MIDJOURNEY_ACCOUNT("midjourney:account:", "midjourney account", 60 * 60 * 48L),
+        MIDJOURNEY_QUERY("midjourney:query:", "midjourney query", 60 * 60 * 2L),
         APPLY_MONEY_DAY_LIMIT_KEY("apply_money_day_limit_key:%s:%s:%s", "每日提现次数限制", 60 * 60 * 24L),
 
         ;

+ 9 - 5
netflix-service/src/main/java/com/cyksj/service/midjourney/MidjourneyService.java

@@ -3,27 +3,31 @@ package com.cyksj.service.midjourney;
 import com.cyksj.model.dto.BlendDimensions;
 import com.cyksj.model.entity.MidjourneyUser;
 import com.cyksj.model.entity.MidjourneyUserConversation;
+import com.cyksj.model.response.SubmitResult;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * @author zwhui
  * @date 2024/4/23 15:44
  */
 public interface MidjourneyService {
-    MidjourneyUserConversation submitImagine(MidjourneyUser user, String prompt, List<String> base64Array) throws Exception;
+    MidjourneyUserConversation submitImagine(MidjourneyUser user, String prompt, String botType,  List<String> base64Array) throws Exception;
 
-    MidjourneyUserConversation submitDescribe(MidjourneyUser user, String base64) throws Exception;
+    MidjourneyUserConversation submitDescribe(MidjourneyUser user, String botType, String base64) throws Exception;
 
-    MidjourneyUserConversation submitBlend(MidjourneyUser user, BlendDimensions dimensions, List<String> base64Array) throws Exception;
+    MidjourneyUserConversation submitBlend(MidjourneyUser user, BlendDimensions dimensions, String botType, List<String> base64Array) throws Exception;
 
     MidjourneyUserConversation submitModal(MidjourneyUser user, Long taskId, String prompt, String maskBase64) throws Exception;
 
-    MidjourneyUserConversation submitShorten(MidjourneyUser user, String prompt) throws Exception;
+    MidjourneyUserConversation submitShorten(MidjourneyUser user, String botType, String prompt) throws Exception;
 
     List<MidjourneyUserConversation> listConversationByIds(Integer mode, List<Long> ids) throws Exception;
 
-    MidjourneyUserConversation submitAction(MidjourneyUser user, Long taskId, String customId) throws Exception;
+    SubmitResult submitAction(MidjourneyUser user, Long taskId, String customId, Long num, String botType) throws Exception;
 
     MidjourneyUserConversation cancelConversation(MidjourneyUser user, Long id);
+
+    void notifyHook(String conversation) throws Exception;
 }

+ 8 - 0
netflix-service/src/main/java/com/cyksj/service/midjourney/impl/MidJourneyAccountServiceImpl.java

@@ -63,6 +63,7 @@ public class MidJourneyAccountServiceImpl extends ServiceImpl<MidjourneyAccountM
         Long instanceId = getAccount(midjourneyAccount.getId());
         midjourneyAccount.setInstanceId(instanceId);
         baseMapper.updateById(midjourneyAccount);
+        redisService.hset(RedisService.key.MIDJOURNEY_ACCOUNT.getName(),midjourneyAccount.getInstanceId().toString(),0);
     }
 
     @Override
@@ -75,6 +76,7 @@ public class MidJourneyAccountServiceImpl extends ServiceImpl<MidjourneyAccountM
         baseMapper.deleteById(id);
         //删除plus服务账号信息
         delAcount(midjourneyAccount.getInstanceId());
+        redisService.hdel(RedisService.key.MIDJOURNEY_ACCOUNT.getName(),midjourneyAccount.getInstanceId());
     }
 
     @Override
@@ -98,6 +100,12 @@ public class MidJourneyAccountServiceImpl extends ServiceImpl<MidjourneyAccountM
         midjourneyAccount.setStatus(!midjourneyAccount.getStatus());
         baseMapper.updateById(midjourneyAccount);
         updAccount(midjourneyAccount);
+
+        if (!midjourneyAccount.getStatus()) {
+            redisService.hdel(RedisService.key.MIDJOURNEY_ACCOUNT.getName(),midjourneyAccount.getInstanceId());
+        }else {
+            redisService.hset(RedisService.key.MIDJOURNEY_ACCOUNT.getName(),midjourneyAccount.getInstanceId().toString(),0);
+        }
     }
 
     @Override

+ 256 - 78
netflix-service/src/main/java/com/cyksj/service/midjourney/impl/MidjourneyServiceImpl.java

@@ -1,33 +1,51 @@
 package com.cyksj.service.midjourney.impl;
 
 import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.io.FileUtil;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.http.HttpRequest;
 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.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.common.EnvCommonService;
 import com.cyksj.common.exception.BusinessRuntimeException;
 import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
+import com.cyksj.common.util.Codec;
+import com.cyksj.common.util.J11HttpC;
 import com.cyksj.common.util.Jsons;
+import com.cyksj.common.util.StringUtil;
 import com.cyksj.mapper.MidjourneyUserConversationMapper;
+import com.cyksj.mapper.MidjourneyUserMapper;
 import com.cyksj.model.dto.BlendDimensions;
 import com.cyksj.model.dto.MessageButton;
+import com.cyksj.model.entity.MidjourneyAccount;
 import com.cyksj.model.entity.MidjourneyUser;
 import com.cyksj.model.entity.MidjourneyUserConversation;
 import com.cyksj.model.response.SubmitResult;
 import com.cyksj.redis.RedisService;
+import com.cyksj.service.midjourney.MidjourneyAccountService;
 import com.cyksj.service.midjourney.MidjourneyService;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
+import javax.imageio.ImageIO;
 import javax.imageio.stream.FileImageOutputStream;
-import java.io.IOException;
+import java.awt.image.BufferedImage;
+import java.io.*;
+import java.net.URI;
+import java.net.URLEncoder;
+import java.net.http.HttpResponse;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.security.MessageDigest;
+import java.time.ZoneOffset;
 import java.util.*;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.stream.Collectors;
@@ -52,97 +70,135 @@ public class MidjourneyServiceImpl implements MidjourneyService {
 
     private final RedisService redisService;
 
+    private final MidjourneyUserMapper midjourneyUserMapper;
+
+    @Value("${midjourney.url}")
+    private String midjourneyHost;
+
+    private final EnvCommonService envCommonService;
+
     private static final GlobalThreadPoolTaskExecutor TASK_EXECUTOR = GlobalThreadPoolTaskExecutor.getInstance();
 
+    private final MidjourneyAccountService midjourneyAccountService;
+
 
     @Override
-    public MidjourneyUserConversation submitImagine(MidjourneyUser user, String prompt, List<String> base64Array) throws Exception {
+    public MidjourneyUserConversation submitImagine(MidjourneyUser user, String prompt,String botType, List<String> base64Array) throws Exception {
         Map<String, Object> imagineParam = MapUtil.builder(new HashMap<String,Object>())
-                .put("prompt", prompt).put("state", user.getId()).build();
+                .put("prompt", prompt)
+                .put("state", user.getId())
+                .build();
         if (CollectionUtil.isNotEmpty(base64Array)) {
             imagineParam.put("base64Array", base64Array);
         }
-        SubmitResult result = submit(user.getMode(),"imagine", imagineParam);
-        return saveConversation(user.getId(), user.getMode(),Long.parseLong(result.getResult()),result.getProperties(),"IMAGINE", StringUtils.EMPTY);
+        SubmitResult result = submit(user.getMode(),"imagine", null,imagineParam);
+        return saveConversation(user.getId(), user.getMode(),result,"IMAGINE", StringUtils.EMPTY, botType);
     }
 
-    public MidjourneyUserConversation saveConversation(Long userId,Integer mode,Long taskId,Map<String,Object> properties, String action,String prompt) throws Exception {
+    public MidjourneyUserConversation saveConversation(Long userId,Integer mode,SubmitResult result, String action,String prompt, String botType) throws Exception {
+        Long taskId = Long.parseLong(result.getResult());
+        Map<String, Object> properties = result.getProperties();
         MidjourneyUserConversation conversation = new MidjourneyUserConversation()
                 .setUserId(userId).setTaskId(taskId).setAction(action).setPrompt(prompt)
                 .setMode(mode).setStartTime(System.currentTimeMillis()).setProgress("0%")
-                .setStatus("IN_PROGRESS").setTaskId(taskId);
+                .setStatus(result.getCode() == 21 ? "NOT_START" : "IN_PROGRESS").setTaskId(taskId);
         if (MapUtil.isNotEmpty(properties)) {
             conversation.setProperties(Jsons.toJson(properties));
             conversation.setChannelId(properties.get("discordChannelId") == null ? null : Long.parseLong(properties.get("discordChannelId").toString()))
                     .setInstanceId(properties.get("discordInstanceId") == null ? null : Long.parseLong(properties.get("discordInstanceId").toString()));
         }
+        if (conversation.getInstanceId() == null) {
+            conversation.setInstanceId(result.getInstanceId());
+        }
         conversationMapper.insert(conversation);
         return conversation;
     }
 
 
     @Override
-    public MidjourneyUserConversation submitDescribe(MidjourneyUser user, String base64) throws Exception {
-        Map<String, Object> param = MapUtil.builder(new HashMap<String,Object>()).put("state", user.getId()).put("base64", base64).build();
-        SubmitResult result = submit(user.getMode(),"describe", param);
-        return saveConversation(user.getId(), user.getMode(),Long.parseLong(result.getResult()),result.getProperties(),"DESCRIBE", StringUtils.EMPTY);
+    public MidjourneyUserConversation submitDescribe(MidjourneyUser user, String botType, String base64) throws Exception {
+        Map<String, Object> param = MapUtil.builder(new HashMap<String,Object>())
+                .put("state", user.getId())
+                .put("base64", base64)
+                .build();
+        SubmitResult result = submit(user.getMode(),"describe", null, param);
+        return saveConversation(user.getId(), user.getMode(),result,"DESCRIBE", StringUtils.EMPTY, botType);
     }
 
     @Override
-    public MidjourneyUserConversation submitBlend(MidjourneyUser user, BlendDimensions dimensions, List<String> base64Array) throws Exception {
+    public MidjourneyUserConversation submitBlend(MidjourneyUser user, BlendDimensions dimensions, String botType, List<String> base64Array) throws Exception {
         Map<String, Object> param = MapUtil.builder(new HashMap<String,Object>())
-                .put("base64Array", base64Array).put("state", user.getId()).build();
+                .put("base64Array", base64Array)
+                .put("state", user.getId())
+                .build();
         if (dimensions != null) {
             param.put("dimensions", dimensions);
         }
-        SubmitResult result = submit(user.getMode(),"blend", param);
-        Map<String, Object> properties = result.getProperties();
-        if (user.getMode() == 1) {
-            String finalPrompt = "%s --ar %s --style raw --s 250";
-            List<String> picList = uploadBase64Pic(base64Array);
-            String pics = picList.stream().map(pic -> "<" + pic + ">").collect(Collectors.joining(" "));
-            properties.put("finalPrompt", String.format(finalPrompt, pics,dimensions.getValue()));
-        }
-        return saveConversation(user.getId(), user.getMode(), Long.parseLong(result.getResult()),properties,"BLEND", StringUtils.EMPTY);
+        SubmitResult result = submit(user.getMode(),"blend", null, param);
+        return saveConversation(user.getId(), user.getMode(),result,"BLEND", StringUtils.EMPTY, botType);
     }
 
     @Override
     public MidjourneyUserConversation submitModal(MidjourneyUser user, Long taskId, String prompt, String maskBase64) throws Exception {
         Map<String, Object> param = MapUtil.builder(new HashMap<String,Object>())
-                .put("taskId", taskId).put("state", user.getId()).build();
+                .put("taskId", taskId)
+                .put("state", user.getId())
+                .build();
         if (StringUtils.isNotBlank(prompt)) {
             param.put("prompt", prompt);
         }
         if (StringUtils.isNotBlank(maskBase64)) {
             param.put("maskBase64", maskBase64);
         }
-        SubmitResult result = submit(user.getMode(),"modal", param);
-        return saveConversation(user.getId(),  user.getMode(),Long.parseLong(result.getResult()),result.getProperties(),"MODAL", StringUtils.EMPTY);
+        SubmitResult result = submit(user.getMode(),"modal", null, param);
+        return saveConversation(user.getId(),  user.getMode(),result,"MODAL", StringUtils.EMPTY, StringUtils.EMPTY);
     }
 
     @Override
-    public MidjourneyUserConversation submitShorten(MidjourneyUser user, String prompt) throws Exception {
+    public MidjourneyUserConversation submitShorten(MidjourneyUser user, String botType, String prompt) throws Exception {
         Map<String, Object> param = MapUtil.builder(new HashMap<String,Object>())
-                .put("prompt", prompt).put("state", user.getId()).build();
-        SubmitResult result = submit(user.getMode(),"shorten", param);
-        return saveConversation(user.getId(), user.getMode(), Long.parseLong(result.getResult()),result.getProperties(),"SHORTEN", StringUtils.EMPTY);
+                .put("prompt", prompt)
+                .put("state", user.getId())
+                .build();
+        SubmitResult result = submit(user.getMode(),"shorten", null, param);
+        return saveConversation(user.getId(), user.getMode(), result,"SHORTEN", StringUtils.EMPTY, botType);
     }
 
     /**
      * 恢复次数
      */
-    public void recoverUserLimit(Long id,Integer mode,Integer num){
+    public Long recoverUserLimit(Long id,Integer mode,Long num){
         if (mode == 1){
-            redisService.incr(RedisService.key.MIDJOURNEY_FAST_LIMIT.getName() + id, 1L);
+            num =  redisService.incr(RedisService.key.MIDJOURNEY_FAST_LIMIT.getName() + id, 1L);
         }
         if (mode == 2){
             if (num != null) {
-                redisService.incr(RedisService.key.MIDJOURNEY_RELAX_LIMIT.getName() + id, 1L);
+                num = redisService.incr(RedisService.key.MIDJOURNEY_RELAX_LIMIT.getName() + id, 1L);
             }
         }
+        return num;
     }
+    /**
+     * 同步数据库
+     */
+    public void syncUser(Long id,Integer mode,Long num){
+        log.info("同步次数 id:{},mode:{},num:{}",id,mode,num);
+        if (num == null){
+            return;
+        }
+        LambdaUpdateWrapper<MidjourneyUser> wrapper = Wrappers.lambdaUpdate(MidjourneyUser.class)
+                .eq(MidjourneyUser::getId, id);
+        if (mode == 1){
+            wrapper.set(MidjourneyUser::getMjFastNum, num);
+        }
+        if (mode == 2){
+            wrapper.set(MidjourneyUser::getMjRelaxNum, num);
+        }
+        midjourneyUserMapper.update(null, wrapper);
+    }
+
     @Override
-    public MidjourneyUserConversation submitAction(MidjourneyUser user, Long taskId, String customId) throws Exception {
+    public SubmitResult submitAction(MidjourneyUser user, Long taskId, String customId, Long num, String botType) throws Exception {
         MidjourneyUserConversation conversation = conversationMapper.selectOne(Wrappers.lambdaQuery(MidjourneyUserConversation.class).eq(MidjourneyUserConversation::getTaskId, taskId).last("limit 1"));
         if (conversation == null) {
             throw BusinessRuntimeException.getInstance("关联任务不存在或已失效");
@@ -150,54 +206,74 @@ public class MidjourneyServiceImpl implements MidjourneyService {
         if (!user.getMode().equals(conversation.getMode())) {
             throw BusinessRuntimeException.getInstance("当前出图模式与关联任务出图模式不符");
         }
-        AtomicBoolean flag = new AtomicBoolean(false);
         if (StringUtils.isNotBlank(conversation.getButtons())){
             List<MessageButton> messageButtons = Jsons.parseList(conversation.getButtons(), MessageButton.class);
             messageButtons.forEach(button -> {
                 if (button.getCustomId().equals(customId)) {
                     log.info("action customId:{}", customId);
                     button.setStyle(3);
-                    if (button.getLabel().contains("Vary") || button.getCustomId().contains("::pan_")
-                            || button.getEmoji().equals("🔄") || button.getCustomId().contains("PromptAnalyzer:")
-                            || button.getCustomId().contains("PicReader::") || button.getCustomId().contains("::variation::")
-                            || button.getCustomId().contains("::CustomZoom::")) {
-                        flag.set(true);
-                    }
                 }
             });
             conversation.setButtons(Jsons.toJson(messageButtons));
         }
-        conversation.setBookmark(customId.contains("BOOKMARK"));
-        conversationMapper.updateById(conversation);
-        if (conversation.getBookmark()) {
-            return conversation;
+        if (customId.contains("BOOKMARK")) {
+            conversation.setBookmark(true);
+            conversationMapper.updateById(conversation);
+            return null;
         }
         Map<String, Object> param = MapUtil.builder(new HashMap<String,Object>())
-                .put("taskId", taskId).put("state", user.getId()).put("customId", customId).build();
-        SubmitResult result = submit(user.getMode(),"action", param);
-        if (flag.get()) {
+                .put("taskId", taskId)
+                .put("state", user.getId())
+                .put("customId", customId)
+                .build();
+        SubmitResult result = submit(user.getMode(),"action", conversation.getInstanceId(),param);
+        if (result.getCode() == 21) {
             // 以上操作有弹窗确认,恢复次数
-            recoverUserLimit(user.getId(), user.getMode(),user.getMjRelaxNum());
+            recoverUserLimit(user.getId(), user.getMode(),num);
+            if (user.getMode() == 2){
+                redisService.hdecr(RedisService.key.MIDJOURNEY_ACCOUNT.getName() , String.valueOf(conversation.getInstanceId()), 1.0);
+            }
+        }else {
+            syncUser(user.getId(), user.getMode(),num);
         }
-        return saveConversation(user.getId(), user.getMode(), Long.parseLong(result.getResult()),result.getProperties(),"ACTION", StringUtils.EMPTY);
+        saveConversation(user.getId(), user.getMode(), result,"ACTION", StringUtils.EMPTY, botType);
+        return result;
     }
 
 
-    public SubmitResult submit(Integer mode,String action, Map<String, Object> param) throws Exception {
+    public SubmitResult submit(Integer mode,String action,Long instanceId, Map<String, Object> param) throws Exception {
+        String url = "";
+        String accountWithMinUsage = "";
         if (mode == 1) {
             param.put("mode", "FAST");
+            url = FAST_HOST;
+        } else if (mode == 2) {
+            url = RELAX_HOST;
+            //慢速查询在使用次数最少的账号
+            accountWithMinUsage = getAccountWithMinUsage(instanceId);
+            param.put("accountFilter",MapUtil.builder(new HashMap<String,Object>())
+                    .put("instanceId",accountWithMinUsage).build());
         }
-        String url = (mode == 1 ? FAST_HOST : RELAX_HOST) + getActionUrl(action);
+        url = url + getActionUrl(action);
+        param.put("notifyHook",midjourneyHost +(EnvCommonService.active.equals(envCommonService.getEnv()) ? "/8081":"/8082") + "/api/applets/midjourney/notifyHook");
         String body = HttpRequest.post(url).body(Jsons.toJson(param)).header("Authorization", FAST_TOKEN).execute().body();
         log.info("action body:{}", body);
         SubmitResult submitResult = Jsons.parseObject(body, SubmitResult.class);
         int code = submitResult.getCode();
         if (code != 1 && code != 21 && code != 22) {
             if (code == 3) {
+                if(mode == 2 && StringUtils.isNotBlank(accountWithMinUsage)){
+                    redisService.hdel(RedisService.key.MIDJOURNEY_ACCOUNT.getName(),accountWithMinUsage);
+                    String finalAccountWithMinUsage = accountWithMinUsage;
+                    TASK_EXECUTOR.execute(() -> {
+                        MidjourneyAccount account = midjourneyAccountService.getOne(Wrappers.lambdaQuery(MidjourneyAccount.class).eq(MidjourneyAccount::getInstanceId, finalAccountWithMinUsage).last("limit 1"));
+                        midjourneyAccountService.updateStatus(account.getId());
+                    });
+                }
                 throw BusinessRuntimeException.getInstance("账号不存在");
             }
             if (code == 4) {
-                throw BusinessRuntimeException.getInstance("图片重复");
+                throw BusinessRuntimeException.getInstance(submitResult.getDescription());
             }
             if (code == 24) {
                 throw BusinessRuntimeException.getInstance("prompt包含敏感词");
@@ -205,8 +281,45 @@ public class MidjourneyServiceImpl implements MidjourneyService {
             log.error("action:" + action + " error message:" + submitResult.getDescription());
             throw BusinessRuntimeException.getInstance("队列已满,请稍后尝试");
         }
+        submitResult.setInstanceId(Long.valueOf(accountWithMinUsage));
+        // 增加使用次数
+        if (StringUtils.isNotBlank(accountWithMinUsage) && mode == 2) {
+            redisService.hincr(RedisService.key.MIDJOURNEY_ACCOUNT.getName(), accountWithMinUsage, 1.0);
+        }
         return submitResult;
     }
+
+    public String getAccountWithMinUsage(Long instanceId) {
+        String key = RedisService.key.MIDJOURNEY_ACCOUNT.getName();
+        try {
+            // 获取所有账号ID和使用次数
+            Map<Object, Object> accountsUsage = redisService.hmget(key);
+
+            if (accountsUsage == null || accountsUsage.isEmpty()) {
+                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));
+                // 保存所有账号ID和使用次数
+                redisService.hmset(key, map);
+            }
+
+            // 找到使用次数最少的账号ID
+            String minAccountId = null;
+            if (instanceId != null) {
+                minAccountId = instanceId.toString();
+            }else {
+                int minUsage = Integer.MAX_VALUE;
+                for (Map.Entry<Object, Object> entry : accountsUsage.entrySet()) {
+                    int usage = Integer.parseInt(entry.getValue().toString());
+                    if (usage < minUsage) {
+                        minUsage = usage;
+                        minAccountId = entry.getKey().toString();
+                    }
+                }
+            }
+            return minAccountId;
+        } catch (Exception e) {
+            throw BusinessRuntimeException.getInstance("获取账号失败");
+        }
+    }
     private String getActionUrl(String action) {
          switch (action) {
             case "imagine":
@@ -225,21 +338,36 @@ public class MidjourneyServiceImpl implements MidjourneyService {
         }
     }
     @Override
-    public List<MidjourneyUserConversation> listConversationByIds(Integer mode, List<Long> ids) throws Exception {
+    public List<MidjourneyUserConversation> listConversationByIds(Integer mode, List<Long> ids) {
         List<MidjourneyUserConversation> list = new ArrayList<>();
-        listByIds(mode,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(() -> {
+        ids.forEach(id ->{
+            String queryKey = RedisService.key.MIDJOURNEY_QUERY.getName();
+            Long count = redisService.incr(queryKey + id, 1L);
+            if (count%5 == 0) {
                 try {
-                    sync(conversation);
-                } catch (IOException e) {
+                    listByIds(mode,List.of(id)).forEach(json ->{
+                        JSONObject jsons = JSONUtil.parseObj(json);
+                        MidjourneyUserConversation conversation = JSONUtil.toBean(jsons, MidjourneyUserConversation.class);
+                        if (StringUtil.isNotBlank(conversation.getImageUrl())) {
+                            conversation.setImageUrl(conversation.getImageUrl().replace("cdn.discordapp.com", "mj.galaxydvd.com"));
+                        }
+                        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);
+                            }
+                        });
+                    });
+                } catch (Exception e) {
                     throw new RuntimeException(e);
                 }
-            });
+            }else {
+                list.add((MidjourneyUserConversation) redisService.get(RedisService.key.MIDJOURNEY_CONVERSATION.getName() + id));
+            }
         });
         return list;
     }
@@ -248,18 +376,46 @@ public class MidjourneyServiceImpl implements MidjourneyService {
     public void sync(MidjourneyUserConversation conversation) throws IOException {
         if ((StringUtils.isNotBlank(conversation.getProgress()) && progress.contains(conversation.getProgress())) || status.contains(conversation.getStatus())) {
             log.info("同步任务:{},进度:{}",conversation.getTaskId(),conversation.getProgress());
+            Long userId = conversation.getUserId();
             MidjourneyUserConversation dbConversation = conversationMapper.selectOne(Wrappers.lambdaQuery(MidjourneyUserConversation.class).eq(MidjourneyUserConversation::getTaskId, conversation.getTaskId())
-                    .eq(MidjourneyUserConversation::getUserId, conversation.getUserId()).last("limit 1"));
+                    .eq(MidjourneyUserConversation::getUserId, userId).orderByDesc(MidjourneyUserConversation::getId).last("limit 1"));
             if (dbConversation != null) {
-                try {
-                    if (StringUtils.isNotBlank(conversation.getImageUrl())) {
-                        conversation.setImageUrl(uploadPic(conversation.getImageUrl(), "conversation"+dbConversation.getId()));
+                if ("SUCCESS".equals(dbConversation.getStatus())) {
+                    return;
+                }
+                conversation.setId(dbConversation.getId());
+                conversationMapper.updateById(conversation);
+                if (dbConversation.getMode() == 2){
+                    redisService.hdecr(RedisService.key.MIDJOURNEY_ACCOUNT.getName() , dbConversation.getInstanceId().toString(), 1.0);
+                }
+                TASK_EXECUTOR.execute(() -> {
+                    try {
+                        String imageUrl = conversation.getImageUrl();
+                        if (StringUtil.isNotBlank(imageUrl)) {
+                            conversation.setImageUrl(uploadPic(imageUrl, "conversation"+dbConversation.getId()));
+                            conversationMapper.updateById(conversation);
+                        }
+                    } catch (IOException e) {
+                        log.error("上传图片失败",e);
                     }
-                } catch (IOException e) {
-                    log.error("上传图片失败",e);
-                }finally {
-                    conversation.setId(dbConversation.getId());
-                    conversationMapper.updateById(conversation);
+                });
+                //失败返还次数
+                if ("FAILURE".equals(conversation.getStatus())){
+                    Object num = redisService.get(RedisService.key.MIDJOURNEY_RELAX_LIMIT.getName() + userId);
+                    LambdaUpdateWrapper<MidjourneyUser> wrapper = Wrappers.lambdaUpdate(MidjourneyUser.class)
+                            .eq(MidjourneyUser::getId, userId);
+                    Integer mode = dbConversation.getMode();
+                    if (mode == 1){
+                        num =  redisService.incr(RedisService.key.MIDJOURNEY_FAST_LIMIT.getName() + userId, 1L);
+                        wrapper.set(MidjourneyUser::getMjFastNum, num);
+                    }
+                    if (mode == 2){
+                        if (num != null) {
+                            num = redisService.incr(RedisService.key.MIDJOURNEY_RELAX_LIMIT.getName() + userId, 1L);
+                            wrapper.set(MidjourneyUser::getMjRelaxNum, num);
+                        }
+                    }
+                    midjourneyUserMapper.update(null, wrapper);
                 }
             }
         }
@@ -269,13 +425,16 @@ public class MidjourneyServiceImpl implements MidjourneyService {
     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())) {
+        File file = tempFile.toFile();
+        try (FileImageOutputStream imageOutput = new FileImageOutputStream(file)) {
             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");
+        paramMap.put("file", file);
+        String json = HttpUtil.post("https://files.liuliangbang.vip/pic/ups", paramMap);
+        log.info("上传图片结果:url:{},json:{}",url, json);
+        FileUtil.del(file);
+        return new JSONObject(json).getJSONObject("value").getJSONArray("saved").getJSONObject(0).getJSONObject("info").getStr("cdnUrl");
     }
 
     private static List<String> uploadBase64Pic(List<String> base64Array) throws IOException {
@@ -293,8 +452,8 @@ public class MidjourneyServiceImpl implements MidjourneyService {
             }
             Map<String, Object> paramMap = new HashMap<>();
             paramMap.put("file", tempFile.toFile());
-            JSONObject result = JSONUtil.parseObj(HttpUtil.post("https://files.liuliangbang.vip/pic/ups", paramMap));
-            list.add(result.getJSONObject("value").getJSONArray("saved").getJSONObject(0).getJSONObject("info").getStr("cdnUrl"));
+            String json = HttpUtil.post("https://files.liuliangbang.vip/pic/ups", paramMap);
+            list.add(new JSONObject(json).getJSONObject("value").getJSONArray("saved").getJSONObject(0).getJSONObject("info").getStr("cdnUrl"));
         }
         return list;
     }
@@ -331,4 +490,23 @@ public class MidjourneyServiceImpl implements MidjourneyService {
         }
     }
 
+    @Override
+    public void notifyHook(String json) {
+        log.info("notifyHook:{}", json);
+        JSONObject jsons = JSONUtil.parseObj(json);
+        MidjourneyUserConversation conversation = JSONUtil.toBean(jsons, MidjourneyUserConversation.class);
+        conversation.setUserId(jsons.getLong("state"));
+        conversation.setTaskId(jsons.getLong("id"));
+        if (StringUtil.isNotBlank(conversation.getImageUrl())) {
+            conversation.setImageUrl(conversation.getImageUrl().replace("cdn.discordapp.com", "mj.galaxydvd.com"));
+        }
+        redisService.set(RedisService.key.MIDJOURNEY_CONVERSATION.getName() + conversation.getTaskId(), conversation,RedisService.key.MIDJOURNEY_CONVERSATION.getTimeout());
+        TASK_EXECUTOR.execute(() -> {
+            try {
+                sync(conversation);
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        });
+    }
 }

+ 13 - 13
netflix-service/src/main/java/com/cyksj/service/scheduler/SchedulerService.java

@@ -1,13 +1,13 @@
-package com.cyksj.service.scheduler;
-
-/*
- *项目名: netflix
- *文件名: SchedulerService
- *创建者: JavaZou
- *创建时间:2023/6/8 16:47
- */
-public interface SchedulerService {
-	void transExpiryAccountValidRelation();
-
-	void assignUserGroupsRelation(Long groupsId);
-}
+//package com.cyksj.service.scheduler;
+//
+///*
+// *项目名: netflix
+// *文件名: SchedulerService
+// *创建者: JavaZou
+// *创建时间:2023/6/8 16:47
+// */
+//public interface SchedulerService {
+//	void transExpiryAccountValidRelation();
+//
+//	void assignUserGroupsRelation(Long groupsId);
+//}

+ 272 - 272
netflix-service/src/main/java/com/cyksj/service/scheduler/impl/SchedulerServiceImpl.java

@@ -1,272 +1,272 @@
-package com.cyksj.service.scheduler.impl;
-
-import cn.hutool.core.date.DateTime;
-import cn.hutool.core.date.DateUnit;
-import cn.hutool.core.date.DateUtil;
-import cn.hutool.core.util.StrUtil;
-import com.baomidou.mybatisplus.core.toolkit.Wrappers;
-import com.cyksj.common.constant.Constant;
-import com.cyksj.common.util.StringUtil;
-import com.cyksj.dto.RedisKey;
-import com.cyksj.mapper.*;
-import com.cyksj.model.entity.*;
-import com.cyksj.model.manage.views.GroupsRelationView;
-import com.cyksj.model.request.ChangeRelationReq;
-import com.cyksj.redis.RedisService;
-import com.cyksj.service.error.UserRelationChangeErrorRecordService;
-import com.cyksj.service.groups.GroupsFuncService;
-import com.cyksj.service.mange.CmsOrderDonService;
-import com.cyksj.service.order.OrderRefundService;
-import com.cyksj.service.relation.GroupRelationClearService;
-import com.cyksj.service.scheduler.SchedulerService;
-import com.cyksj.service.user.UserBenefitsService;
-import com.ejlchina.searcher.BeanSearcher;
-import com.ejlchina.searcher.param.Operator;
-import com.ejlchina.searcher.util.MapUtils;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/*
- *项目名: netflix
- *文件名: SchedulerServiceImpl
- *创建者: JavaZou
- *创建时间:2023/6/8 16:48
- */
-@Service
-@RequiredArgsConstructor
-@Slf4j
-public class SchedulerServiceImpl implements SchedulerService {
-
-	private final GoodsDonSkuMapper skuMapper;
-
-	private final GroupsMapper groupsMapper;
-
-	private final BeanSearcher beanSearcher;
-
-	private final OrderDonMapper orderDonMapper;
-
-	private final GroupsRelationMapper groupsRelationMapper;
-
-	private final GroupRelationClearService groupRelationClearService;
-
-	private final UserBenefitsService userBenefitsService;
-
-	private final GroupsFuncService groupsFuncService;
-
-	private final OrderRefundService orderRefundService;
-
-	private final CmsOrderDonService cmsOrderDonService;
-
-	private final UserRelationChangeErrorRecordService changeErrorRecordService;
-
-	private final UserRelationChangeErrorRecordMapper userRelationChangeErrorRecordMapper;
-
-	private final GoodsDonMapper goodsDonMapper;
-
-	private final RedisService redisService;
-
-	@Override
-	public void transExpiryAccountValidRelation() {
-		DateTime now = new DateTime();
-		DateTime nextBeginDay = DateUtil.offsetDay(DateUtil.beginOfDay(now), 1);
-		//针对AI类 CHAT PLUS、MidJourney 月付
-		List<Long> aiSkuIds = skuMapper.selectAIMonthSkuIds(Constant.AI_goodsIds);
-		List<GroupsTrips> expiryAccountGroups = groupsMapper.getExpiryAccountGroups(nextBeginDay, aiSkuIds);
-		expiryAccountGroups.forEach(expiry_groups -> {
-			if (expiry_groups.getStatus() != GroupsTrips.Status.down) {
-				expiry_groups.setStatus(GroupsTrips.Status.down);
-				groupsMapper.updateById(expiry_groups);
-			}
-			Long g_groupsId = expiry_groups.getId();
-			Date a_expiryTime = expiry_groups.getExpiryTime();
-			//未过期用户 只分配有效的用户车票 outside状态过滤
-			List<GroupsRelationView> groupsRelations = beanSearcher.searchAll(GroupsRelationView.class, MapUtils.builder()
-					.field(GroupsRelationView::getGroupsId, g_groupsId)
-					.field(GroupsRelationView::getStatus, GroupsRelation.Status.validity.name())
-					.field(GroupsRelationView::getExpiryTime, nextBeginDay).op(Operator.GreaterEqual)
-					.build());
-			reAssign(g_groupsId, now, groupsRelations, UserTicketClearedRecord.Source.timing_change_ticket);
-		});
-	}
-
-	@Override
-	public void assignUserGroupsRelation(Long groupsId) {
-		GroupsTrips groupsTrips = groupsMapper.selectById(groupsId);
-		if (groupsTrips == null || groupsTrips.getAccountId() == null) return;
-		String key = RedisKey.DISABLE_ACCOUNT_ASSIGN + groupsId;
-		if (!redisService.setNx(key, groupsId, 60 * 3l)) {
-			return;
-		}
-		if (groupsTrips.getStatus() != GroupsTrips.Status.down) {
-			groupsTrips.setStatus(GroupsTrips.Status.down);
-			groupsMapper.updateById(groupsTrips);
-		}
-		DateTime now = DateTime.now();
-		//迁移用户
-		List<GroupsRelationView> groupsRelations = beanSearcher.searchAll(GroupsRelationView.class, MapUtils.builder()
-				.field(GroupsRelationView::getGroupsId, groupsId)
-				.field(GroupsRelationView::getStatus, List.of(GroupsRelation.Status.validity.name(), GroupsRelation.Status.outside.name())).op(Operator.InList)
-				.field(GroupsRelationView::getExpiryTime, now).op(Operator.GreaterEqual)
-				.build());
-		reAssign(groupsId, now, groupsRelations, UserTicketClearedRecord.Source.disable);
-		redisService.del(key);
-	}
-
-	/**
-	 * 重新分配车位
-	 */
-	public void reAssign(Long g_groupsId, DateTime now, List<GroupsRelationView> groupsRelations, UserTicketClearedRecord.Source source) {
-		log.info("迁移groupsId:{}未过期用户数量:{}", g_groupsId, groupsRelations.size());
-		Map<Long, GoodsDon> goodsMap = new ConcurrentHashMap<>();
-		Map<Long, GoodsDonSku> skuMap = new ConcurrentHashMap<>();
-		groupsRelations.forEach(relationView -> {
-			Long change_relationId = relationView.getId();
-			Long userId = relationView.getUserId();
-			UserRelationChangeErrorRecord exists = userRelationChangeErrorRecordMapper.selectOne(Wrappers.lambdaQuery(UserRelationChangeErrorRecord.class)
-					.eq(UserRelationChangeErrorRecord::getRelationId, change_relationId)
-					.eq(UserRelationChangeErrorRecord::getUserId, userId)
-					.eq(UserRelationChangeErrorRecord::getDeleted, true)
-					.last("limit 1"));
-			if (exists != null) {
-				log.info("存在userId:{}无法转移的车票relationId:{}", userId, change_relationId);
-				return;
-			}
-			Date expiryTime = DateUtil.beginOfDay(relationView.getExpiryTime());
-			Long skuId = relationView.getSkuId();
-			//仅针对chatGPT
-			if (Constant.AI_goodsIds.contains(relationView.getGoodsId())) {
-				Long bet_day = DateUtil.between(now, expiryTime, DateUnit.DAY);
-				//10人月付 10天内硬塞20个 10天外硬塞10个
-				//4人月付 硬塞5个
-				Integer extra_num = 5;
-				//10天以内
-				if (bet_day <= 10) {
-					if (skuId.equals(161l)) {
-						extra_num = 20;
-					}
-					//分配到过期账号5天内误差的相同规格车队里
-					DateTime five_day = DateUtil.offsetDay(expiryTime, 5);
-					Long item_groupsId = groupsMapper.selectSameSpecItemGroupsByTime(g_groupsId, skuId, expiryTime, five_day, extra_num);
-					if (item_groupsId == null) {
-						//无对应车队 退款
-						//退款至余额
-						OrderDon orderDon = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class)
-								.eq(OrderDon::getRelationId, change_relationId)
-								.eq(OrderDon::getUserId, userId)
-								.notIn(OrderDon::getStatus, Constant.noOrderAllStatus)
-								.orderByDesc(OrderDon::getId)
-								.last("limit 1"));
-						if (orderDon == null) {
-							//更换过车票
-							orderDon = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class)
-									.eq(OrderDon::getSkuId, skuId)
-									.eq(OrderDon::getUserId, userId)
-									.notIn(OrderDon::getStatus, Constant.noOrderAllStatus)
-									.orderByDesc(OrderDon::getId)
-									.last("limit 1"));
-						}
-						if (orderDon == null) {
-							log.error("用户userId:{}的车票relationId:{}账号过期后转移错误", userId, change_relationId);
-							changeErrorRecordService.changeTicketErrorRecord(relationView, UserRelationChangeErrorRecord.Source.change);
-							return;
-						}
-						//清除车票
-						GroupsRelation relation = groupsRelationMapper.selectById(change_relationId);
-						groupRelationClearService.clearTicket(relation, UserTicketClearedRecord.Source.refund_balance);
-						//退款至余额
-						//实付金额 + 余额
-						BigDecimal money = orderDon.getMoney().add(orderDon.getBalance());
-						//当月天数
-						Date payTime = orderDon.getPayTime();
-						if (payTime == null) {
-							payTime = orderDon.getCreatedTime();
-						}
-						if (money.compareTo(BigDecimal.ZERO) == 0) {
-							GoodsDonSku sku = skuMapper.selectById(skuId);
-							if (sku != null) {
-								money = sku.getPrice();
-							}
-						}
-						int dayNum = DateUtil.dayOfMonth(DateUtil.endOfMonth(payTime));
-						BigDecimal re_balance = money.divide(BigDecimal.valueOf(dayNum), 0, RoundingMode.DOWN).multiply(BigDecimal.valueOf(bet_day));
-						if (re_balance.compareTo(BigDecimal.ZERO) <= 0) {
-							log.info("user_id:{},relationId:{}返回余额为0", userId, change_relationId);
-							return;
-						}
-						GoodsDon goodsDon = goodsMap.get(relationView.getGoodsId());
-						if (goodsDon == null) {
-							goodsDon = goodsDonMapper.selectById(relationView.getGoodsId());
-							if (goodsDon == null) return;
-							goodsMap.putIfAbsent(relationView.getGoodsId(), goodsDon);
-						}
-						userBenefitsService.addUserBalance(userId, re_balance, UserBalanceSourceRecord.Source.clear_valid, relationView.getYhsId(), orderDon.getId(), goodsDon.getTitle() + UserBalanceSourceRecord.Source.clear_valid.getDesc(), true);
-						log.info("车票未到期清除,退款至用户user_id:{}余额:{}成功", userId, re_balance);
-						//修改订单状态 为退款
-						orderDon.setStatus(OrderDon.Status.refund);
-						orderDon.setRefundBalance(re_balance);
-						orderDon.setRefundMoney(BigDecimal.ZERO);
-						orderDonMapper.updateById(orderDon);
-						GoodsDonSku sku = skuMap.get(orderDon.getSkuId());
-						if (sku == null) {
-							sku = skuMapper.selectById(orderDon.getSkuId());
-							if (sku == null) return;
-							skuMap.putIfAbsent(orderDon.getSkuId(), sku);
-						}
-						orderRefundService.refundRecord(orderDon, orderDon.getRefundMoney(), null, goodsDon, sku, StrUtil.EMPTY, "balance", null);
-						return;
-					}
-					changeTickerRelation(item_groupsId, relationView, source, relationView.getYhsId());
-					return;
-				}
-				//大于10天
-				//分配到过期账号10天内误差的相同规格车队里
-				if (skuId.equals(161l)) {
-					extra_num = 10;
-				}
-				DateTime ten_day = DateUtil.offsetDay(expiryTime, 10);
-				Long item_groupsId = groupsMapper.selectSameSpecItemGroupsByTime(g_groupsId, skuId, expiryTime, ten_day, extra_num);
-				if (item_groupsId == null) {
-					//无对应车队 生成空车队
-					//寻找空车队
-					item_groupsId = groupsMapper.selectSameSpecEmptyGroups(skuId);
-					if (item_groupsId == null) {
-						//新增空车队
-						GoodsDonSku sku = skuMapper.selectById(skuId);
-						GroupsRelation new_relation = groupsFuncService.createNewGroupsTrips(sku);
-						item_groupsId = new_relation.getGroupsId();
-					}
-					//新车队
-					changeTickerRelation(item_groupsId, relationView, UserTicketClearedRecord.Source.new_groups_ticket, relationView.getYhsId());
-					return;
-				}
-				changeTickerRelation(item_groupsId, relationView, source, relationView.getYhsId());
-				return;
-			}
-		});
-	}
-
-	public void changeTickerRelation(Long item_groupsId, GroupsRelationView relationView, UserTicketClearedRecord.Source source, Long yhsId) {
-		Long change_relationId = relationView.getId();
-		try {
-			ChangeRelationReq req = new ChangeRelationReq();
-			req.setGroupsId(item_groupsId);
-			req.setRelationId(change_relationId);
-			req.setSource(source);
-			req.setIsOutside(true);
-			req.setYhsId(yhsId);
-			cmsOrderDonService.changeRelation(req);
-		} catch (Exception e) {
-			log.error("更换用户relationId:{}错误:{}", change_relationId, StringUtil.getErrorText(e));
-			relationView.setErrorMsg(StringUtil.getErrorMsg(e));
-			changeErrorRecordService.changeTicketErrorRecord(relationView, UserRelationChangeErrorRecord.Source.change);
-		}
-	}
-}
+//package com.cyksj.service.scheduler.impl;
+//
+//import cn.hutool.core.date.DateTime;
+//import cn.hutool.core.date.DateUnit;
+//import cn.hutool.core.date.DateUtil;
+//import cn.hutool.core.util.StrUtil;
+//import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+//import com.cyksj.common.constant.Constant;
+//import com.cyksj.common.util.StringUtil;
+//import com.cyksj.dto.RedisKey;
+//import com.cyksj.mapper.*;
+//import com.cyksj.model.entity.*;
+//import com.cyksj.model.manage.views.GroupsRelationView;
+//import com.cyksj.model.request.ChangeRelationReq;
+//import com.cyksj.redis.RedisService;
+//import com.cyksj.service.error.UserRelationChangeErrorRecordService;
+//import com.cyksj.service.groups.GroupsFuncService;
+//import com.cyksj.service.mange.CmsOrderDonService;
+//import com.cyksj.service.order.OrderRefundService;
+//import com.cyksj.service.relation.GroupRelationClearService;
+//import com.cyksj.service.scheduler.SchedulerService;
+//import com.cyksj.service.user.UserBenefitsService;
+//import com.ejlchina.searcher.BeanSearcher;
+//import com.ejlchina.searcher.param.Operator;
+//import com.ejlchina.searcher.util.MapUtils;
+//import lombok.RequiredArgsConstructor;
+//import lombok.extern.slf4j.Slf4j;
+//import org.springframework.stereotype.Service;
+//
+//import java.math.BigDecimal;
+//import java.math.RoundingMode;
+//import java.util.Date;
+//import java.util.List;
+//import java.util.Map;
+//import java.util.concurrent.ConcurrentHashMap;
+//
+///*
+// *项目名: netflix
+// *文件名: SchedulerServiceImpl
+// *创建者: JavaZou
+// *创建时间:2023/6/8 16:48
+// */
+//@Service
+//@RequiredArgsConstructor
+//@Slf4j
+//public class SchedulerServiceImpl implements SchedulerService {
+//
+//	private final GoodsDonSkuMapper skuMapper;
+//
+//	private final GroupsMapper groupsMapper;
+//
+//	private final BeanSearcher beanSearcher;
+//
+//	private final OrderDonMapper orderDonMapper;
+//
+//	private final GroupsRelationMapper groupsRelationMapper;
+//
+//	private final GroupRelationClearService groupRelationClearService;
+//
+//	private final UserBenefitsService userBenefitsService;
+//
+//	private final GroupsFuncService groupsFuncService;
+//
+//	private final OrderRefundService orderRefundService;
+//
+//	private final CmsOrderDonService cmsOrderDonService;
+//
+//	private final UserRelationChangeErrorRecordService changeErrorRecordService;
+//
+//	private final UserRelationChangeErrorRecordMapper userRelationChangeErrorRecordMapper;
+//
+//	private final GoodsDonMapper goodsDonMapper;
+//
+//	private final RedisService redisService;
+//
+//	@Override
+//	public void transExpiryAccountValidRelation() {
+//		DateTime now = new DateTime();
+//		DateTime nextBeginDay = DateUtil.offsetDay(DateUtil.beginOfDay(now), 1);
+//		//针对AI类 CHAT PLUS、MidJourney 月付
+//		List<Long> aiSkuIds = skuMapper.selectAIMonthSkuIds(Constant.AI_goodsIds);
+//		List<GroupsTrips> expiryAccountGroups = groupsMapper.getExpiryAccountGroups(nextBeginDay, aiSkuIds);
+//		expiryAccountGroups.forEach(expiry_groups -> {
+//			if (expiry_groups.getStatus() != GroupsTrips.Status.down) {
+//				expiry_groups.setStatus(GroupsTrips.Status.down);
+//				groupsMapper.updateById(expiry_groups);
+//			}
+//			Long g_groupsId = expiry_groups.getId();
+//			Date a_expiryTime = expiry_groups.getExpiryTime();
+//			//未过期用户 只分配有效的用户车票 outside状态过滤
+//			List<GroupsRelationView> groupsRelations = beanSearcher.searchAll(GroupsRelationView.class, MapUtils.builder()
+//					.field(GroupsRelationView::getGroupsId, g_groupsId)
+//					.field(GroupsRelationView::getStatus, GroupsRelation.Status.validity.name())
+//					.field(GroupsRelationView::getExpiryTime, nextBeginDay).op(Operator.GreaterEqual)
+//					.build());
+//			reAssign(g_groupsId, now, groupsRelations, UserTicketClearedRecord.Source.timing_change_ticket);
+//		});
+//	}
+//
+//	@Override
+//	public void assignUserGroupsRelation(Long groupsId) {
+//		GroupsTrips groupsTrips = groupsMapper.selectById(groupsId);
+//		if (groupsTrips == null || groupsTrips.getAccountId() == null) return;
+//		String key = RedisKey.DISABLE_ACCOUNT_ASSIGN + groupsId;
+//		if (!redisService.setNx(key, groupsId, 60 * 3l)) {
+//			return;
+//		}
+//		if (groupsTrips.getStatus() != GroupsTrips.Status.down) {
+//			groupsTrips.setStatus(GroupsTrips.Status.down);
+//			groupsMapper.updateById(groupsTrips);
+//		}
+//		DateTime now = DateTime.now();
+//		//迁移用户
+//		List<GroupsRelationView> groupsRelations = beanSearcher.searchAll(GroupsRelationView.class, MapUtils.builder()
+//				.field(GroupsRelationView::getGroupsId, groupsId)
+//				.field(GroupsRelationView::getStatus, List.of(GroupsRelation.Status.validity.name(), GroupsRelation.Status.outside.name())).op(Operator.InList)
+//				.field(GroupsRelationView::getExpiryTime, now).op(Operator.GreaterEqual)
+//				.build());
+//		reAssign(groupsId, now, groupsRelations, UserTicketClearedRecord.Source.disable);
+//		redisService.del(key);
+//	}
+//
+//	/**
+//	 * 重新分配车位
+//	 */
+//	public void reAssign(Long g_groupsId, DateTime now, List<GroupsRelationView> groupsRelations, UserTicketClearedRecord.Source source) {
+//		log.info("迁移groupsId:{}未过期用户数量:{}", g_groupsId, groupsRelations.size());
+//		Map<Long, GoodsDon> goodsMap = new ConcurrentHashMap<>();
+//		Map<Long, GoodsDonSku> skuMap = new ConcurrentHashMap<>();
+//		groupsRelations.forEach(relationView -> {
+//			Long change_relationId = relationView.getId();
+//			Long userId = relationView.getUserId();
+//			UserRelationChangeErrorRecord exists = userRelationChangeErrorRecordMapper.selectOne(Wrappers.lambdaQuery(UserRelationChangeErrorRecord.class)
+//					.eq(UserRelationChangeErrorRecord::getRelationId, change_relationId)
+//					.eq(UserRelationChangeErrorRecord::getUserId, userId)
+//					.eq(UserRelationChangeErrorRecord::getDeleted, true)
+//					.last("limit 1"));
+//			if (exists != null) {
+//				log.info("存在userId:{}无法转移的车票relationId:{}", userId, change_relationId);
+//				return;
+//			}
+//			Date expiryTime = DateUtil.beginOfDay(relationView.getExpiryTime());
+//			Long skuId = relationView.getSkuId();
+//			//仅针对chatGPT
+//			if (Constant.AI_goodsIds.contains(relationView.getGoodsId())) {
+//				Long bet_day = DateUtil.between(now, expiryTime, DateUnit.DAY);
+//				//10人月付 10天内硬塞20个 10天外硬塞10个
+//				//4人月付 硬塞5个
+//				Integer extra_num = 5;
+//				//10天以内
+//				if (bet_day <= 10) {
+//					if (skuId.equals(161l)) {
+//						extra_num = 20;
+//					}
+//					//分配到过期账号5天内误差的相同规格车队里
+//					DateTime five_day = DateUtil.offsetDay(expiryTime, 5);
+//					Long item_groupsId = groupsMapper.selectSameSpecItemGroupsByTime(g_groupsId, skuId, expiryTime, five_day, extra_num);
+//					if (item_groupsId == null) {
+//						//无对应车队 退款
+//						//退款至余额
+//						OrderDon orderDon = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class)
+//								.eq(OrderDon::getRelationId, change_relationId)
+//								.eq(OrderDon::getUserId, userId)
+//								.notIn(OrderDon::getStatus, Constant.noOrderAllStatus)
+//								.orderByDesc(OrderDon::getId)
+//								.last("limit 1"));
+//						if (orderDon == null) {
+//							//更换过车票
+//							orderDon = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class)
+//									.eq(OrderDon::getSkuId, skuId)
+//									.eq(OrderDon::getUserId, userId)
+//									.notIn(OrderDon::getStatus, Constant.noOrderAllStatus)
+//									.orderByDesc(OrderDon::getId)
+//									.last("limit 1"));
+//						}
+//						if (orderDon == null) {
+//							log.error("用户userId:{}的车票relationId:{}账号过期后转移错误", userId, change_relationId);
+//							changeErrorRecordService.changeTicketErrorRecord(relationView, UserRelationChangeErrorRecord.Source.change);
+//							return;
+//						}
+//						//清除车票
+//						GroupsRelation relation = groupsRelationMapper.selectById(change_relationId);
+//						groupRelationClearService.clearTicket(relation, UserTicketClearedRecord.Source.refund_balance);
+//						//退款至余额
+//						//实付金额 + 余额
+//						BigDecimal money = orderDon.getMoney().add(orderDon.getBalance());
+//						//当月天数
+//						Date payTime = orderDon.getPayTime();
+//						if (payTime == null) {
+//							payTime = orderDon.getCreatedTime();
+//						}
+//						if (money.compareTo(BigDecimal.ZERO) == 0) {
+//							GoodsDonSku sku = skuMapper.selectById(skuId);
+//							if (sku != null) {
+//								money = sku.getPrice();
+//							}
+//						}
+//						int dayNum = DateUtil.dayOfMonth(DateUtil.endOfMonth(payTime));
+//						BigDecimal re_balance = money.divide(BigDecimal.valueOf(dayNum), 0, RoundingMode.DOWN).multiply(BigDecimal.valueOf(bet_day));
+//						if (re_balance.compareTo(BigDecimal.ZERO) <= 0) {
+//							log.info("user_id:{},relationId:{}返回余额为0", userId, change_relationId);
+//							return;
+//						}
+//						GoodsDon goodsDon = goodsMap.get(relationView.getGoodsId());
+//						if (goodsDon == null) {
+//							goodsDon = goodsDonMapper.selectById(relationView.getGoodsId());
+//							if (goodsDon == null) return;
+//							goodsMap.putIfAbsent(relationView.getGoodsId(), goodsDon);
+//						}
+//						userBenefitsService.addUserBalance(userId, re_balance, UserBalanceSourceRecord.Source.clear_valid, relationView.getYhsId(), orderDon.getId(), goodsDon.getTitle() + UserBalanceSourceRecord.Source.clear_valid.getDesc(), true);
+//						log.info("车票未到期清除,退款至用户user_id:{}余额:{}成功", userId, re_balance);
+//						//修改订单状态 为退款
+//						orderDon.setStatus(OrderDon.Status.refund);
+//						orderDon.setRefundBalance(re_balance);
+//						orderDon.setRefundMoney(BigDecimal.ZERO);
+//						orderDonMapper.updateById(orderDon);
+//						GoodsDonSku sku = skuMap.get(orderDon.getSkuId());
+//						if (sku == null) {
+//							sku = skuMapper.selectById(orderDon.getSkuId());
+//							if (sku == null) return;
+//							skuMap.putIfAbsent(orderDon.getSkuId(), sku);
+//						}
+//						orderRefundService.refundRecord(orderDon, orderDon.getRefundMoney(), null, goodsDon, sku, StrUtil.EMPTY, "balance", null);
+//						return;
+//					}
+//					changeTickerRelation(item_groupsId, relationView, source, relationView.getYhsId());
+//					return;
+//				}
+//				//大于10天
+//				//分配到过期账号10天内误差的相同规格车队里
+//				if (skuId.equals(161l)) {
+//					extra_num = 10;
+//				}
+//				DateTime ten_day = DateUtil.offsetDay(expiryTime, 10);
+//				Long item_groupsId = groupsMapper.selectSameSpecItemGroupsByTime(g_groupsId, skuId, expiryTime, ten_day, extra_num);
+//				if (item_groupsId == null) {
+//					//无对应车队 生成空车队
+//					//寻找空车队
+//					item_groupsId = groupsMapper.selectSameSpecEmptyGroups(skuId);
+//					if (item_groupsId == null) {
+//						//新增空车队
+//						GoodsDonSku sku = skuMapper.selectById(skuId);
+//						GroupsRelation new_relation = groupsFuncService.createNewGroupsTrips(sku);
+//						item_groupsId = new_relation.getGroupsId();
+//					}
+//					//新车队
+//					changeTickerRelation(item_groupsId, relationView, UserTicketClearedRecord.Source.new_groups_ticket, relationView.getYhsId());
+//					return;
+//				}
+//				changeTickerRelation(item_groupsId, relationView, source, relationView.getYhsId());
+//				return;
+//			}
+//		});
+//	}
+//
+//	public void changeTickerRelation(Long item_groupsId, GroupsRelationView relationView, UserTicketClearedRecord.Source source, Long yhsId) {
+//		Long change_relationId = relationView.getId();
+//		try {
+//			ChangeRelationReq req = new ChangeRelationReq();
+//			req.setGroupsId(item_groupsId);
+//			req.setRelationId(change_relationId);
+//			req.setSource(source);
+//			req.setIsOutside(true);
+//			req.setYhsId(yhsId);
+//			cmsOrderDonService.changeRelation(req);
+//		} catch (Exception e) {
+//			log.error("更换用户relationId:{}错误:{}", change_relationId, StringUtil.getErrorText(e));
+//			relationView.setErrorMsg(StringUtil.getErrorMsg(e));
+//			changeErrorRecordService.changeTicketErrorRecord(relationView, UserRelationChangeErrorRecord.Source.change);
+//		}
+//	}
+//}

+ 83 - 83
netflix-service/src/main/java/com/cyksj/task/CorpChatScheduler.java

@@ -1,83 +1,83 @@
-package com.cyksj.task;
-
-import cn.hutool.core.util.StrUtil;
-import cn.hutool.json.JSONObject;
-import cn.hutool.json.JSONUtil;
-import com.baomidou.mybatisplus.core.toolkit.Wrappers;
-import com.cyksj.common.util.StringUtil;
-import com.cyksj.mapper.corp.CorpUserAuthMapper;
-import com.cyksj.mapper.corp.CorpUserMapper;
-import com.cyksj.model.entity.CorpUser;
-import com.cyksj.model.entity.CorpUserAuth;
-import com.cyksj.model.entity.OrderDonPost;
-import com.cyksj.redis.RedisService;
-import com.cyksj.service.corp.WxCorpOps;
-import com.cyksj.service.corp.msg.CorpEventActionService;
-import com.cyksj.service.order.OrderDonPostService;
-import com.ejlchina.searcher.BeanSearcher;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-
-import java.util.Optional;
-import java.util.Set;
-
-/**
- * @author chan
- * @date 2024-04-29 12:14
- */
-@Component
-@Slf4j
-@RequiredArgsConstructor
-public class CorpChatScheduler {
-
-	private final CorpUserMapper corpUserMapper;
-
-	private final CorpUserAuthMapper corpUserAuthMapper;
-
-	private final RedisService redisService;
-
-	private final OrderDonPostService orderDonPostService;
-
-	/**
-	 * 客服业绩数据统计
-	 */
-    @Scheduled(cron = "5 0/2 * * * ?")
-	public void chatPost() {
-		Set<Object> chatRecord = redisService.sGet(RedisService.key.CORP_CUSTOMER_ACQUISTION_START_CHAT_SET.getEnvName());
-		log.info("发起对话回传. size: {}", chatRecord.size());
-		chatRecord.forEach((key) -> {
-			log.info("发起对话回传. key: {}", key);
-			CorpUserAuth corpUserAuth = corpUserAuthMapper.selectOne(Wrappers.lambdaQuery(CorpUserAuth.class)
-					.eq(CorpUserAuth::getExternalUserid, key).last("limit 1"));
-			String unionId = null;
-			if (corpUserAuth != null) {
-				CorpUser corpUser = corpUserMapper.selectById(corpUserAuth.getCorpUserId());
-				if (corpUser != null) {
-					unionId = corpUser.getUnionid();
-				}
-			}
-			log.info("发起对话回传. unionId: {}", unionId);
-			if (StrUtil.isNotBlank(unionId)) {
-				String json = StringUtil.getString(redisService.hget(RedisService.key.CORP_CUSTOMER_ACQUISTION_START_CHAT.getEnvName(), unionId));
-				if (StringUtil.isNotBlank(json)) {
-					JSONObject jsonObject = JSONUtil.parseObj(json);
-					String callback = jsonObject.getStr("callback");
-					try {
-						orderDonPostService.post(OrderDonPost.Type.OE.getType(), callback, Optional.ofNullable(jsonObject.getStr("event")).orElse("customer_effective"));
-						redisService.setRemove(RedisService.key.CORP_CUSTOMER_ACQUISTION_START_CHAT_SET.getEnvName(), key);
-						log.info("发起对话回传成功. {}", key);
-					} catch (Exception e) {
-						log.error("发起对话回传失败. {}", e.getMessage());
-					}
-				}
-			}else {
-				Long count = redisService.incr(RedisService.key.CORP_CUSTOMER_ACQUISTION_START_CHAT_COUNT.getNameFormat(key), 1L);
-				if(count > 3){
-					redisService.setRemove(RedisService.key.CORP_CUSTOMER_ACQUISTION_START_CHAT_SET.getEnvName(), key);
-				}
-			}
-		});
-	}
-}
+//package com.cyksj.task;
+//
+//import cn.hutool.core.util.StrUtil;
+//import cn.hutool.json.JSONObject;
+//import cn.hutool.json.JSONUtil;
+//import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+//import com.cyksj.common.util.StringUtil;
+//import com.cyksj.mapper.corp.CorpUserAuthMapper;
+//import com.cyksj.mapper.corp.CorpUserMapper;
+//import com.cyksj.model.entity.CorpUser;
+//import com.cyksj.model.entity.CorpUserAuth;
+//import com.cyksj.model.entity.OrderDonPost;
+//import com.cyksj.redis.RedisService;
+//import com.cyksj.service.corp.WxCorpOps;
+//import com.cyksj.service.corp.msg.CorpEventActionService;
+//import com.cyksj.service.order.OrderDonPostService;
+//import com.ejlchina.searcher.BeanSearcher;
+//import lombok.RequiredArgsConstructor;
+//import lombok.extern.slf4j.Slf4j;
+//import org.springframework.scheduling.annotation.Scheduled;
+//import org.springframework.stereotype.Component;
+//
+//import java.util.Optional;
+//import java.util.Set;
+//
+///**
+// * @author chan
+// * @date 2024-04-29 12:14
+// */
+//@Component
+//@Slf4j
+//@RequiredArgsConstructor
+//public class CorpChatScheduler {
+//
+//	private final CorpUserMapper corpUserMapper;
+//
+//	private final CorpUserAuthMapper corpUserAuthMapper;
+//
+//	private final RedisService redisService;
+//
+//	private final OrderDonPostService orderDonPostService;
+//
+//	/**
+//	 * 客服业绩数据统计
+//	 */
+//    @Scheduled(cron = "5 0/2 * * * ?")
+//	public void chatPost() {
+//		Set<Object> chatRecord = redisService.sGet(RedisService.key.CORP_CUSTOMER_ACQUISTION_START_CHAT_SET.getEnvName());
+//		log.info("发起对话回传. size: {}", chatRecord.size());
+//		chatRecord.forEach((key) -> {
+//			log.info("发起对话回传. key: {}", key);
+//			CorpUserAuth corpUserAuth = corpUserAuthMapper.selectOne(Wrappers.lambdaQuery(CorpUserAuth.class)
+//					.eq(CorpUserAuth::getExternalUserid, key).last("limit 1"));
+//			String unionId = null;
+//			if (corpUserAuth != null) {
+//				CorpUser corpUser = corpUserMapper.selectById(corpUserAuth.getCorpUserId());
+//				if (corpUser != null) {
+//					unionId = corpUser.getUnionid();
+//				}
+//			}
+//			log.info("发起对话回传. unionId: {}", unionId);
+//			if (StrUtil.isNotBlank(unionId)) {
+//				String json = StringUtil.getString(redisService.hget(RedisService.key.CORP_CUSTOMER_ACQUISTION_START_CHAT.getEnvName(), unionId));
+//				if (StringUtil.isNotBlank(json)) {
+//					JSONObject jsonObject = JSONUtil.parseObj(json);
+//					String callback = jsonObject.getStr("callback");
+//					try {
+//						orderDonPostService.post(OrderDonPost.Type.OE.getType(), callback, Optional.ofNullable(jsonObject.getStr("event")).orElse("customer_effective"));
+//						redisService.setRemove(RedisService.key.CORP_CUSTOMER_ACQUISTION_START_CHAT_SET.getEnvName(), key);
+//						log.info("发起对话回传成功. {}", key);
+//					} catch (Exception e) {
+//						log.error("发起对话回传失败. {}", e.getMessage());
+//					}
+//				}
+//			}else {
+//				Long count = redisService.incr(RedisService.key.CORP_CUSTOMER_ACQUISTION_START_CHAT_COUNT.getNameFormat(key), 1L);
+//				if(count > 3){
+//					redisService.setRemove(RedisService.key.CORP_CUSTOMER_ACQUISTION_START_CHAT_SET.getEnvName(), key);
+//				}
+//			}
+//		});
+//	}
+//}

+ 3 - 3
netflix-web/src/main/java/com/cyksj/web/controller/manage/group/CmsGroupRelationController.java

@@ -27,7 +27,7 @@ import com.cyksj.model.views.GroupsRelationIndependentView;
 import com.cyksj.model.views.GroupsRelationRechargeView;
 import com.cyksj.model.views.OrderDonRenewView;
 import com.cyksj.service.relation.GroupRelationClearService;
-import com.cyksj.service.scheduler.SchedulerService;
+
 import com.ejlchina.searcher.BeanSearcher;
 import com.ejlchina.searcher.SearchResult;
 import com.ejlchina.searcher.param.Operator;
@@ -56,7 +56,7 @@ public class CmsGroupRelationController {
 
     private final HttpServletRequest request;
 
-    private final SchedulerService schedulerService;
+    //private final SchedulerService schedulerService;
 
     private final GroupsRelationMapper groupsRelationMapper;
 
@@ -85,7 +85,7 @@ public class CmsGroupRelationController {
     @PutMapping("/disable/{groupsId}")
     @NoSubmit
     public Result<String> disable(@PathVariable Long groupsId) {
-        schedulerService.assignUserGroupsRelation(groupsId);
+        //schedulerService.assignUserGroupsRelation(groupsId);
         return GatewayResponse.SUCCESS.newBuilder().toResult("车队账号禁用成功");
     }
 

+ 92 - 67
netflix-web/src/main/java/com/cyksj/web/controller/mirror/MidjourneyController.java

@@ -3,14 +3,19 @@ package com.cyksj.web.controller.mirror;
 import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.common.annotation.NoSubmit;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.util.IoKit;
 import com.cyksj.dto.Result;
 import com.cyksj.enums.GatewayResponse;
 import com.cyksj.mapper.GroupsRelationMapper;
 import com.cyksj.mapper.MidjourneyPaintingCollectRecordMapper;
 import com.cyksj.mapper.MidjourneyPaintingPlazaMapper;
 import com.cyksj.mapper.MidjourneyUserMapper;
+import com.cyksj.model.entity.MidjourneyUser;
+import com.cyksj.model.entity.MidjourneyUserConversation;
+import com.cyksj.model.response.SubmitResult;
 import com.cyksj.model.dto.*;
 import com.cyksj.model.entity.*;
 import com.cyksj.model.views.MidjourneyPaintingUserView;
@@ -29,8 +34,11 @@ import org.springframework.dao.DuplicateKeyException;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
 import java.util.Date;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 
 /**
@@ -63,44 +71,25 @@ public class MidjourneyController {
 
     public MidjourneyUser getUser(){
         String userToken = request.getHeader("user-token");
-        MidjourneyUser midjourneyUser = (MidjourneyUser) redisService.get(RedisService.key.MIDJOURNEY_USER.getName() + userToken);
+
+        MidjourneyUser midjourneyUser = midjourneyUserMapper.selectOne(Wrappers.lambdaQuery(MidjourneyUser.class)
+                .eq(MidjourneyUser::getUserToken, userToken).last("limit 1"));
         if (midjourneyUser == null){
-            midjourneyUser = midjourneyUserMapper.selectOne(Wrappers.lambdaQuery(MidjourneyUser.class)
-                    .eq(MidjourneyUser::getUserToken, userToken).last("limit 1"));
-            if (midjourneyUser == null){
-                throw new BusinessRuntimeException("账号不存在,请重新登录");
-            }
-            Long relationId = midjourneyUser.getRelationId();
-            GroupsRelation relation = groupsRelationMapper.selectById(relationId);
-            midjourneyUser.setAqType(relation.getAqType());
-            redisService.set(RedisService.key.MIDJOURNEY_USER.getName() + userToken, midjourneyUser, RedisService.key.MIDJOURNEY_USER.getTimeout());
-            Object fastNum = redisService.get(RedisService.key.MIDJOURNEY_FAST_LIMIT.getName() + midjourneyUser.getId());
-            if (fastNum == null){
-                redisService.set(RedisService.key.MIDJOURNEY_FAST_LIMIT.getName() + midjourneyUser.getId(), midjourneyUser.getMjFastNum(), RedisService.key.MIDJOURNEY_FAST_LIMIT.getTimeout());
-            }
-            Object relaxNum = redisService.get(RedisService.key.MIDJOURNEY_RELAX_LIMIT.getName() + midjourneyUser.getId());
-            if (relaxNum == null){
-                if (midjourneyUser.getMjRelaxNum() != null){
-                    redisService.set(RedisService.key.MIDJOURNEY_RELAX_LIMIT.getName() + midjourneyUser.getId(), midjourneyUser.getMjRelaxNum(), RedisService.key.MIDJOURNEY_RELAX_LIMIT.getTimeout());
-                }
-            }
-        }else {
-            Object fastNum = redisService.get(RedisService.key.MIDJOURNEY_FAST_LIMIT.getName() + midjourneyUser.getId());
-            if (fastNum == null){
-                redisService.set(RedisService.key.MIDJOURNEY_FAST_LIMIT.getName() + midjourneyUser.getId(), midjourneyUser.getMjFastNum(), RedisService.key.MIDJOURNEY_FAST_LIMIT.getTimeout());
-            }else {
-                midjourneyUser.setMjFastNum(Integer.parseInt(fastNum.toString()));
-            }
-            Object relaxNum = redisService.get(RedisService.key.MIDJOURNEY_RELAX_LIMIT.getName() + midjourneyUser.getId());
-            if (relaxNum == null){
-                if (midjourneyUser.getMjRelaxNum() != null){
-                    redisService.set(RedisService.key.MIDJOURNEY_RELAX_LIMIT.getName() + midjourneyUser.getId(), midjourneyUser.getMjRelaxNum(), RedisService.key.MIDJOURNEY_RELAX_LIMIT.getTimeout());
-                }
-            }else {
-                midjourneyUser.setMjRelaxNum(Integer.parseInt(relaxNum.toString()));
-            }
+            throw new BusinessRuntimeException("账号不存在,请重新登录");
         }
+        Long relationId = midjourneyUser.getRelationId();
+        GroupsRelation relation = groupsRelationMapper.selectById(relationId);
+        midjourneyUser.setAqType(relation.getAqType());
+        if(!redisService.hasKey(RedisService.key.MIDJOURNEY_FAST_LIMIT.getName() + midjourneyUser.getId())){
+            redisService.set(RedisService.key.MIDJOURNEY_FAST_LIMIT.getName() + midjourneyUser.getId(),midjourneyUser.getMjFastNum());
+        }
+
+        if(!redisService.hasKey(RedisService.key.MIDJOURNEY_RELAX_LIMIT.getName() + midjourneyUser.getId())){
+            redisService.set(RedisService.key.MIDJOURNEY_RELAX_LIMIT.getName() + midjourneyUser.getId(),midjourneyUser.getMjRelaxNum());
+        }
+
         return midjourneyUser;
+
     }
 
 
@@ -113,18 +102,35 @@ public class MidjourneyController {
             throw BusinessRuntimeException.getInstance("账号已过期");
         }
        if (user.getMode() == 1){
+
            num = redisService.decr(RedisService.key.MIDJOURNEY_FAST_LIMIT.getName() + user.getId(), 1L);
+
            if (num < 0) {
                user.setMode(2);
+               midjourneyUserMapper.update(null, Wrappers.lambdaUpdate(MidjourneyUser.class).eq(MidjourneyUser::getId, user.getId()).set(MidjourneyUser::getMode, user.getMode()));
                redisService.incr(RedisService.key.MIDJOURNEY_FAST_LIMIT.getName() + user.getId(), 1L);
+
+           }else {
+               int i = midjourneyUserMapper.decFastNum(user.getId(), 1, user.getMjFastNum());
+               if (i == 0) {
+                   redisService.incr(RedisService.key.MIDJOURNEY_FAST_LIMIT.getName() + user.getId(), 1L);
+                   throw BusinessRuntimeException.getInstance("网络异常,请稍后再试");
+               }
            }
        }
        if (user.getMode() == 2){
            Object relax = redisService.get(RedisService.key.MIDJOURNEY_RELAX_LIMIT.getName() + user.getId());
+
            if (relax != null){
                num = redisService.decr(RedisService.key.MIDJOURNEY_RELAX_LIMIT.getName() + user.getId(), 1L);
                if (num < 0){
                    redisService.incr(RedisService.key.MIDJOURNEY_RELAX_LIMIT.getName() + user.getId(), 1L);
+               }else {
+                   int i = midjourneyUserMapper.decRelaxNum(user.getId(), 1, user.getMjRelaxNum());
+                   if (i == 0) {
+                       redisService.incr(RedisService.key.MIDJOURNEY_RELAX_LIMIT.getName() + user.getId(), 1L);
+                       throw BusinessRuntimeException.getInstance("网络异常,请稍后再试");
+                   }
                }
            }else {
                num = null;
@@ -139,32 +145,34 @@ public class MidjourneyController {
     public void recoverUserLimit(Long id,Integer mode,Long num){
         if (mode == 1){
             redisService.incr(RedisService.key.MIDJOURNEY_FAST_LIMIT.getName() + id, 1L);
+            midjourneyUserMapper.incrFastNum(id, 1);
         }
         if (mode == 2){
             if (num != null){
                 redisService.incr(RedisService.key.MIDJOURNEY_RELAX_LIMIT.getName() + id, 1L);
+                midjourneyUserMapper.incrRelaxNum(id, 1);
             }
         }
     }
 
-    /**
-     * 同步数据库
-     */
-    public void syncUser(Long id,Integer mode,Long num){
-        log.info("同步次数 id:{},mode:{},num:{}",id,mode,num);
-        if (num == null){
-            return;
-        }
-        LambdaUpdateWrapper<MidjourneyUser> wrapper = Wrappers.lambdaUpdate(MidjourneyUser.class)
-                .eq(MidjourneyUser::getId, id);
-        if (mode == 1){
-            wrapper.set(MidjourneyUser::getMjFastNum, num);
-        }
-        if (mode == 2){
-            wrapper.set(MidjourneyUser::getMjRelaxNum, num);
-        }
-        midjourneyUserMapper.update(null, wrapper);
-    }
+    ///**
+    // * 同步数据库
+    // */
+    //public void syncUser(Long id,Integer mode,Long num){
+    //    log.info("同步次数 id:{},mode:{},num:{}",id,mode,num);
+    //    if (num == null){
+    //        return;
+    //    }
+    //    LambdaUpdateWrapper<MidjourneyUser> wrapper = Wrappers.lambdaUpdate(MidjourneyUser.class)
+    //            .eq(MidjourneyUser::getId, id);
+    //    if (mode == 1){
+    //        wrapper.set(MidjourneyUser::getMjFastNum, num);
+    //    }
+    //    if (mode == 2){
+    //        wrapper.set(MidjourneyUser::getMjRelaxNum, num);
+    //    }
+    //    midjourneyUserMapper.update(null, wrapper);
+    //}
 
 
     /**
@@ -181,6 +189,7 @@ public class MidjourneyController {
      * 提交Imagine任务
      */
     @PostMapping("/submit/imagine")
+    @NoSubmit
     public Result<MidjourneyUserConversation> submitImagine(@RequestBody SubmitImagineDTO submitImagineDTO) {
         log.info("提交Imagine任务,提示:{},base64数组长度:{}",submitImagineDTO.getPrompt(),submitImagineDTO.getBase64Array());
         MidjourneyUser user = getUser();
@@ -190,12 +199,12 @@ public class MidjourneyController {
         }
         MidjourneyUserConversation conversation;
         try {
-            conversation = midjourneyService.submitImagine(user, submitImagineDTO.getPrompt(), submitImagineDTO.getBase64Array());
+            conversation = midjourneyService.submitImagine(user, submitImagineDTO.getPrompt(), submitImagineDTO.getBotType(), submitImagineDTO.getBase64Array());
         } catch (Exception e) {
             recoverUserLimit(user.getId(), user.getMode(),num);
             throw BusinessRuntimeException.getInstance(e.getMessage());
         }
-        syncUser(user.getId(), user.getMode(), num);
+        //syncUser(user.getId(), user.getMode(), num);
         return GatewayResponse.SUCCESS.newBuilder().toResult(conversation);
     }
 
@@ -203,6 +212,7 @@ public class MidjourneyController {
      * 提交Describe任务
      */
     @PostMapping("/submit/describe")
+    @NoSubmit
     public Result<MidjourneyUserConversation> submitDescribe(@RequestBody SubmitDescribeDTO submitDescribeDTO) {
         log.info("提交Describe任务");
         MidjourneyUser user = getUser();
@@ -212,12 +222,12 @@ public class MidjourneyController {
         }
         MidjourneyUserConversation conversation;
         try {
-            conversation = midjourneyService.submitDescribe(user, submitDescribeDTO.getBase64());
+            conversation = midjourneyService.submitDescribe(user, submitDescribeDTO.getBotType(),submitDescribeDTO.getBase64());
         } catch (Exception e) {
             recoverUserLimit(user.getId(), user.getMode(),num);
             throw BusinessRuntimeException.getInstance(e.getMessage());
         }
-        syncUser(user.getId(), user.getMode(), num);
+        //syncUser(user.getId(), user.getMode(), num);
         return GatewayResponse.SUCCESS.newBuilder().toResult(conversation);
     }
 
@@ -225,6 +235,7 @@ public class MidjourneyController {
      * 提交Blend任务
      */
     @PostMapping("/submit/blend")
+    @NoSubmit
     public Result<MidjourneyUserConversation> submitBlend(@RequestBody SubmitBlendDTO submitBlendDTO) {
         log.info("提交Blend任务 dimensions:{},base64数组长度:{}", submitBlendDTO.getDimensions(), submitBlendDTO.getBase64Array().size());
         MidjourneyUser user = getUser();
@@ -234,12 +245,12 @@ public class MidjourneyController {
         }
         MidjourneyUserConversation conversation;
         try {
-            conversation = midjourneyService.submitBlend(user, submitBlendDTO.getDimensions(), submitBlendDTO.getBase64Array());
+            conversation = midjourneyService.submitBlend(user, submitBlendDTO.getDimensions(), submitBlendDTO.getBotType(), submitBlendDTO.getBase64Array());
         } catch (Exception e) {
             recoverUserLimit(user.getId(), user.getMode(),num);
             throw BusinessRuntimeException.getInstance(e.getMessage());
         }
-        syncUser(user.getId(), user.getMode(), num);
+        //syncUser(user.getId(), user.getMode(), num);
         return GatewayResponse.SUCCESS.newBuilder().toResult(conversation);
     }
 
@@ -247,6 +258,7 @@ public class MidjourneyController {
      * 提交Modal任务
      */
     @PostMapping("/submit/modal")
+    @NoSubmit
     public Result<MidjourneyUserConversation> submitModal(@RequestBody SubmitModalDTO submitModalDTO) {
         log.info("提交Modal任务,taskId:{},提示:{},base64数组长度:{}",submitModalDTO.getTaskId(),submitModalDTO.getPrompt(),submitModalDTO.getMaskBase64());
         MidjourneyUser user = getUser();
@@ -261,7 +273,7 @@ public class MidjourneyController {
             recoverUserLimit(user.getId(), user.getMode(),num);
             throw BusinessRuntimeException.getInstance(e.getMessage());
         }
-        syncUser(user.getId(), user.getMode(), num);
+        //syncUser(user.getId(), user.getMode(), num);
         return GatewayResponse.SUCCESS.newBuilder().toResult(conversation);
     }
 
@@ -269,6 +281,7 @@ public class MidjourneyController {
      * 提交Shorten任务
      */
     @PostMapping("/submit/shorten")
+    @NoSubmit
     public Result<MidjourneyUserConversation> submitShorten(@RequestBody SubmitShortenDTO submitShortenDTO) {
         log.info("提交Shorten任务 提示词:{}",submitShortenDTO.getPrompt());
         MidjourneyUser user = getUser();
@@ -278,12 +291,12 @@ public class MidjourneyController {
         }
         MidjourneyUserConversation conversation;
         try {
-            conversation = midjourneyService.submitShorten(user, submitShortenDTO.getPrompt());
+            conversation = midjourneyService.submitShorten(user, submitShortenDTO.getBotType(), submitShortenDTO.getPrompt());
         } catch (Exception e) {
             recoverUserLimit(user.getId(), user.getMode(),num);
             throw BusinessRuntimeException.getInstance(e.getMessage());
         }
-        syncUser(user.getId(), user.getMode(), num);
+        //syncUser(user.getId(), user.getMode(), num);
         return GatewayResponse.SUCCESS.newBuilder().toResult(conversation);
     }
 
@@ -291,21 +304,21 @@ public class MidjourneyController {
      * 执行动作
      */
     @PostMapping("/submit/action")
-    public Result<MidjourneyUserConversation> action(@RequestBody SubmitActionDTO actionDTO) {
+    @NoSubmit
+    public Result<SubmitResult> action(@RequestBody SubmitActionDTO actionDTO) {
         log.info("任务id:{},执行动作:{}",actionDTO.getTaskId(),actionDTO.getCustomId());
         MidjourneyUser user = getUser();
         Long num = checkUserLimit(user);
         if (num != null && num < 0){
             throw BusinessRuntimeException.getInstance("次数已用完");
         }
-        MidjourneyUserConversation conversation;
+        SubmitResult conversation;
         try {
-            conversation = midjourneyService.submitAction(user, actionDTO.getTaskId(), actionDTO.getCustomId());
+            conversation = midjourneyService.submitAction(user, actionDTO.getTaskId(), actionDTO.getCustomId(),num, actionDTO.getBotType());
         } catch (Exception e) {
             recoverUserLimit(user.getId(), user.getMode(),num);
             throw BusinessRuntimeException.getInstance(e.getMessage());
         }
-        syncUser(user.getId(), user.getMode(), num);
         return GatewayResponse.SUCCESS.newBuilder().toResult(conversation);
     }
 
@@ -337,6 +350,7 @@ public class MidjourneyController {
      * 取消任务
      */
     @PostMapping("/conversation/{id}/cancel")
+    @NoSubmit
     public Result<MidjourneyUserConversation> conversationCancel(@PathVariable("id") Long id){
         MidjourneyUser user = getUser();
         log.info("取消任务 id:{}",id);
@@ -356,10 +370,21 @@ public class MidjourneyController {
         }
         midjourneyUser.setMode(mode);
         midjourneyUserMapper.updateById(midjourneyUser);
-        redisService.del(RedisService.key.MIDJOURNEY_USER.getName() + midjourneyUser.getUserToken());
+        //redisService.del(RedisService.key.MIDJOURNEY_USER.getName() + midjourneyUser.getUserToken());
         return GatewayResponse.SUCCESS.newBuilder().toResult();
     }
 
+    /**
+     * midjourney 回调
+     */
+    @PostMapping("/notifyHook")
+    public void notifyHook(HttpServletRequest request) throws Exception {
+        InputStream inputStream = request.getInputStream();
+        byte[] bytes = IoKit.toBytes(inputStream);
+        String json = new String(bytes, StandardCharsets.UTF_8);
+        midjourneyService.notifyHook(json);
+    }
+
 
     /**
      * mj绘画广场 我的收藏