Переглянути джерело

Merge branch 'midjourney' into dev

# Conflicts:
#	netflix-service/src/main/java/com/cyksj/redis/RedisService.java
#	netflix-service/src/main/java/com/cyksj/service/midjourney/impl/MidjourneyServiceImpl.java
#	netflix-service/src/main/java/com/cyksj/service/relation/impl/GroupRelationFrontServiceImpl.java
#	netflix-web/src/main/java/com/cyksj/web/controller/goods/GoodsDonController.java
#	netflix-web/src/main/java/com/cyksj/web/controller/payment/OrderController.java
#	netflix-web/src/main/resources/application-dev.yml
#	netflix-web/src/main/resources/application-prd.yml
chenbiao 2 роки тому
батько
коміт
32a851a65d
48 змінених файлів з 1399 додано та 532 видалено
  1. 5 0
      netflix-common/src/main/java/com/cyksj/common/constant/Constant.java
  2. 2 0
      netflix-dao/src/main/java/com/cyksj/mapper/OrderDonMapper.java
  3. 2 0
      netflix-dao/src/main/java/com/cyksj/mapper/UserMapper.java
  4. 2 0
      netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpUserFollowRelationMapper.java
  5. 3 0
      netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpUserMapper.java
  6. 2 0
      netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpUserTagMapper.java
  7. 13 0
      netflix-dao/src/main/java/com/cyksj/mapper/manage/distribute/equipment/EquipmentUserOrderVerifyRecordMapper.java
  8. 8 0
      netflix-dao/src/main/java/com/cyksj/model/dto/SubmitActionDTO.java
  9. 8 0
      netflix-dao/src/main/java/com/cyksj/model/dto/SubmitBlendDTO.java
  10. 8 0
      netflix-dao/src/main/java/com/cyksj/model/dto/SubmitDescribeDTO.java
  11. 9 0
      netflix-dao/src/main/java/com/cyksj/model/dto/SubmitImagineDTO.java
  12. 8 0
      netflix-dao/src/main/java/com/cyksj/model/dto/SubmitShortenDTO.java
  13. 24 0
      netflix-dao/src/main/java/com/cyksj/model/entity/CorpUserPayIntention.java
  14. 10 0
      netflix-dao/src/main/java/com/cyksj/model/entity/CorpUserTag.java
  15. 29 0
      netflix-dao/src/main/java/com/cyksj/model/entity/EquipmentUserOrderVerifyRecord.java
  16. 1 0
      netflix-dao/src/main/java/com/cyksj/model/entity/ShopConfig.java
  17. 163 0
      netflix-dao/src/main/java/com/cyksj/model/manage/views/MaliceUserView.java
  18. 5 0
      netflix-dao/src/main/java/com/cyksj/model/views/ChatgptCarInfoView.java
  19. 1 1
      netflix-dao/src/main/java/com/cyksj/model/views/ChatgptCarSessionView.java
  20. 4 0
      netflix-dao/src/main/java/com/cyksj/model/views/EquipmentUserDistributeDetailView.java
  21. 10 0
      netflix-dao/src/main/resources/mapper/CorpUserFollowRelationMapper.xml
  22. 16 8
      netflix-dao/src/main/resources/mapper/CorpUserTagMapper.xml
  23. 6 0
      netflix-dao/src/main/resources/mapper/OrderDonMapper.xml
  24. 12 0
      netflix-dao/src/main/resources/mapper/UserMapper.xml
  25. 1 1
      netflix-service/src/main/java/com/cyksj/service/chatgpt/ChatGptAccountService.java
  26. 40 14
      netflix-service/src/main/java/com/cyksj/service/chatgpt/impl/ChatGptAccountServiceImpl.java
  27. 89 4
      netflix-service/src/main/java/com/cyksj/service/corp/impl/CorpServiceImpl.java
  28. 1 1
      netflix-service/src/main/java/com/cyksj/service/distribute/impl/DistributeServiceImpl.java
  29. 6 2
      netflix-service/src/main/java/com/cyksj/service/mail/impl/CuiQiuMailServiceImpl.java
  30. 5 5
      netflix-service/src/main/java/com/cyksj/service/mange/impl/CmsOrderDonServiceImpl.java
  31. 56 0
      netflix-service/src/main/java/com/cyksj/service/market/task/impl/BindTaskServiceImpl.java
  32. 9 5
      netflix-service/src/main/java/com/cyksj/service/midjourney/MidjourneyService.java
  33. 244 84
      netflix-service/src/main/java/com/cyksj/service/midjourney/impl/MidjourneyServiceImpl.java
  34. 64 18
      netflix-service/src/main/java/com/cyksj/service/order/impl/OrderDonServiceImpl.java
  35. 2 2
      netflix-service/src/main/java/com/cyksj/service/pay/impl/TransferFuncServiceImpl.java
  36. 2 0
      netflix-service/src/main/java/com/cyksj/service/relation/GroupRelationFrontService.java
  37. 13 13
      netflix-service/src/main/java/com/cyksj/service/scheduler/SchedulerService.java
  38. 272 272
      netflix-service/src/main/java/com/cyksj/service/scheduler/impl/SchedulerServiceImpl.java
  39. 83 83
      netflix-service/src/main/java/com/cyksj/task/CorpChatScheduler.java
  40. 11 0
      netflix-web/src/main/java/com/cyksj/web/controller/distribute/EquipmentDistributeController.java
  41. 10 0
      netflix-web/src/main/java/com/cyksj/web/controller/group/GroupRelationController.java
  42. 24 0
      netflix-web/src/main/java/com/cyksj/web/controller/manage/CmsUserController.java
  43. 19 1
      netflix-web/src/main/java/com/cyksj/web/controller/manage/corp/WxCorpController.java
  44. 11 0
      netflix-web/src/main/java/com/cyksj/web/controller/manage/coupon/CouponController.java
  45. 49 4
      netflix-web/src/main/java/com/cyksj/web/controller/manage/distribute/EquipmentController.java
  46. 3 3
      netflix-web/src/main/java/com/cyksj/web/controller/manage/group/CmsGroupRelationController.java
  47. 31 8
      netflix-web/src/main/java/com/cyksj/web/controller/mirror/MidjourneyController.java
  48. 3 3
      netflix-web/src/main/java/com/cyksj/web/controller/mirror/MirrorController.java

+ 5 - 0
netflix-common/src/main/java/com/cyksj/common/constant/Constant.java

@@ -312,4 +312,9 @@ public interface Constant {
 	 * 有效获客
 	 */
 	String CUSTOMER_EFFECTIVE = "customer_effective";
+
+	/**
+	 * 设备意向标签组
+	 */
+	String EP_INTENTION_GROUP_NAME= "设备群发标签";
 }

+ 2 - 0
netflix-dao/src/main/java/com/cyksj/mapper/OrderDonMapper.java

@@ -89,4 +89,6 @@ public interface OrderDonMapper extends BaseMapper<OrderDon> {
 	List<Long> getPayGoodsIds(@Param("userIds") List<Long> relationUserIdList);
 
 	BigDecimal getTotalRefundAmountTime(@Param("startDate") Date startDate, @Param("endDate") Date endDate);
+
+	BigDecimal getCouponUsedMoney(Long couponId);
 }

+ 2 - 0
netflix-dao/src/main/java/com/cyksj/mapper/UserMapper.java

@@ -73,4 +73,6 @@ public interface UserMapper extends BaseMapper<User> {
 
     @Update("update user set sub_apply_money = sub_apply_money + #{addApplyMoney} where id = #{id} and sub_apply_money = #{subApplyMoney}")
     Integer addDistributeSubMoney(@Param("id") Long id, @Param("subApplyMoney") BigDecimal subApplyMoney, @Param("addApplyMoney") BigDecimal addApplyMoney);
+
+    Integer getNewMaliceUser(DateTime beginOfDay);
 }

+ 2 - 0
netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpUserFollowRelationMapper.java

@@ -21,4 +21,6 @@ public interface CorpUserFollowRelationMapper extends BaseMapper<CorpUserFollowR
 	List<CorpUserFollowRelationDto> selectCorpUserFollowRelation(@Param("wxCorpId") Long wxCorpId, @Param("followIds") List<String> service_follow_ids);
 
 	List<CorpUserFollowRelationDto> selectCorpUserNotUnderTagIds(@Param("list") List<String> tagIds, @Param("wxCorpId") Long wxCorpId, @Param("followIds") List<String> followIds, @Param("userScope") Integer userScope, @Param("consumerScope") Integer consumerScope, @Param("joinStartTime") Date joinStartTime, @Param("joinEndTime") Date joinEndTime);
+
+	CorpUserFollowRelation getCorpUserFollowRelation(@Param("unionId") String unionId, @Param("followId") String followId);
 }

+ 3 - 0
netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpUserMapper.java

@@ -17,4 +17,7 @@ public interface CorpUserMapper extends BaseMapper<CorpUser> {
 	@Select("select u.id from (select corp_user_id from corp_user_auth where wx_corp_id = #{wxCorpId} and external_userid = #{corpUserId} limit 1) ca inner join corp_user cu on cu.id = ca.corp_user_id and cu.unionid != ''" +
 			" inner join user u on u.uionid = cu.unionid")
 	Long selectUserIdByCropUserId(@Param("wxCorpId") Long wxCorpId, @Param("corpUserId") String corpUserId);
+
+	@Select("select external_userid from (select id from corp_user where unionid = #{unionid} limit 1 ) cu inner join corp_user_auth ca on ca.corp_user_id = cu.id limit 1")
+	String selectUserExternalUserId(String unionid);
 }

+ 2 - 0
netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpUserTagMapper.java

@@ -28,4 +28,6 @@ public interface CorpUserTagMapper extends BaseMapper<CorpUserTag> {
 	List<UserIntentionPieView> getUserIntentionPie(@Param("tagIdStr") String tagId, @Param("thisDate") String date);
 
 	Page<CorpUserIntentionView> getCorpUserIntentionView(@Param("page") Page<CorpUserIntentionView> page, @Param("userId") Long userId, @Param("nickname") String nickname, @Param("tagIdStr") String tagId, @Param("thisDate") String date, @Param("intention") Integer intention);
+
+	List<CorpUserTag> selectIntentionUserTag(@Param("externalUserId") String externalUserId, @Param("groupName") String groupName);
 }

+ 13 - 0
netflix-dao/src/main/java/com/cyksj/mapper/manage/distribute/equipment/EquipmentUserOrderVerifyRecordMapper.java

@@ -0,0 +1,13 @@
+package com.cyksj.mapper.manage.distribute.equipment;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.EquipmentUserOrderVerifyRecord;
+
+/**
+ * 项目名: yhlxj
+ * 文件名: EquipmentUserOrderVerifyRecordMapper
+ * 创建者: JavaZou
+ * 创建时间:2024/5/14 11:41
+ */
+public interface EquipmentUserOrderVerifyRecordMapper extends BaseMapper<EquipmentUserOrderVerifyRecord> {
+}

+ 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";
 }

+ 24 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/CorpUserPayIntention.java

@@ -1,5 +1,7 @@
 package com.cyksj.model.entity;
 
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.ejlchina.searcher.bean.DbIgnore;
 import lombok.Getter;
 import lombok.Setter;
 
@@ -18,4 +20,26 @@ public class CorpUserPayIntention extends BaseEntity {
 	 * 意向 0无回复 1无意向 2.问相关问题 3有意向 4很有意向
 	 */
 	private Integer intention;
+
+	@DbIgnore
+	@TableField(exist = false)
+	private String followId;
+
+
+	@Getter
+	public enum Intention {
+		whf("无回复", "etvj2DPQAA13a2LWPMwCikcmnp6T40FA"),
+		wyx("无意向", "etvj2DPQAASnQouiyPZD9el0QLLSNs8Q"),
+		wxgwt("问相关问题", "etvj2DPQAAUOcl8WGtkF27Ul_V7ixF4A"),
+		yyx("有意向", "etvj2DPQAA41WEz2wXpuLlf1XzKTnK9g"),
+		hyyx("很有意向", "etvj2DPQAAPQOeqPoY-ByfdmCrC3PDkw");
+		String tagName;
+
+		String tagId;
+
+		Intention(String tagName, String tagId) {
+			this.tagName = tagName;
+			this.tagId = tagId;
+		}
+	}
 }

+ 10 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/CorpUserTag.java

@@ -1,5 +1,7 @@
 package com.cyksj.model.entity;
 
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.ejlchina.searcher.bean.DbIgnore;
 import lombok.Data;
 
 import java.io.Serializable;
@@ -37,6 +39,14 @@ public class CorpUserTag extends BaseEntity implements Serializable {
 	 */
 	private int type;
 
+	@DbIgnore
+	@TableField(exist = false)
+	private String followId;
+
+	@DbIgnore
+	@TableField(exist = false)
+	private String externalUserId;
+
 	@Override
 	public boolean equals(Object o) {
 		if (this == o) return true;

+ 29 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/EquipmentUserOrderVerifyRecord.java

@@ -0,0 +1,29 @@
+package com.cyksj.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+
+/**
+ * 项目名: yhlxj
+ * 文件名: EquipmentUserOrderVerifyRecord
+ * 创建者: JavaZou
+ * 创建时间:2024/5/14 11:39
+ */
+@Getter
+@Setter
+public class EquipmentUserOrderVerifyRecord extends BaseEntity{
+	private Long sharedId;
+
+	private Long userId;
+
+	private Long orderId;
+
+	private BigDecimal money;
+
+	/**
+	 * 打款截图
+	 */
+	private String img;
+}

+ 1 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/ShopConfig.java

@@ -20,6 +20,7 @@ public class ShopConfig extends BaseEntity{
 
     /**
      * 分销提现支付宝默认商户
+     *
      */
     public static final String DISTRIBUTE_ZFB_DEFAULT_APP_ID = "2021003171697073";
 

+ 163 - 0
netflix-dao/src/main/java/com/cyksj/model/manage/views/MaliceUserView.java

@@ -0,0 +1,163 @@
+package com.cyksj.model.manage.views;
+
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.DbIgnore;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: MaliceUserView
+ * 创建者: JavaZou
+ * 创建时间:2024/5/16 16:13
+ */
+@Getter
+@Setter
+@SearchBean(tables = "(select distinct user_id from order_don where status not in ('close','noPayment') and buyer_id is not null group by user_id having count(distinct buyer_id) > 4) o inner join user u on u.id = o.user_id")
+public class MaliceUserView {
+	/**
+	 * id
+	 */
+	@DbField("u.id")
+	private Long id;
+
+	/**
+	 * 名称
+	 */
+	@DbField("u.nickname")
+	private String nickname;
+
+	/**
+	 * 手机号
+	 */
+	@DbField("u.phone")
+	private String phone;
+
+	/**
+	 * 绑定手机号
+	 */
+	@DbIgnore
+	private String bindPhone;
+
+	/**
+	 * 绑定邮箱
+	 */
+	@DbIgnore
+	private String bindEmail;
+
+	/**
+	 * 头像
+	 */
+	@DbField("u.headimgurl")
+	private String headimgurl;
+
+	/**
+	 * 积分
+	 */
+	@DbField("u.points")
+	private BigDecimal points;
+
+	@DbField("u.balance")
+	private BigDecimal balance;
+
+	@DbField("case when u.login_phone != '' then 'phone' when u.email != '' then 'email' when u.open_id != '' then 'wechat' else 'wechat' end")
+	private String register;
+
+	@DbField("u.show_id")
+	private String showId;
+
+	/**
+	 * 登录注册手机号
+	 */
+	@DbField("u.login_phone")
+	private String loginPhone;
+
+	/**
+	 * 登录注册邮箱
+	 */
+	@DbField("u.email")
+	private String email;
+
+	/**
+	 * 活跃抽奖积分
+	 */
+	@DbField("u.active_points")
+	private BigDecimal activePoints;
+
+	@DbField("u.register_env")
+	private String registerEnv;
+
+	@DbField("u.last_time")
+	private Date lastTime;
+
+	@DbField("u.is_black")
+	private Boolean isBlack;
+
+	/**
+	 * 企业微信客服
+	 */
+	@DbIgnore
+	private String corpCustomers;
+
+	/**
+	 * 首单信息
+	 */
+	@DbIgnore
+	private String firstOrderDetail;
+
+	/**
+	 * 有无路由器订单
+	 */
+	@DbIgnore
+	private Boolean isHadRouteOrder;
+
+
+	/**
+	 * 订单金额
+	 */
+	@DbIgnore
+	private BigDecimal orderMoney;
+
+	/**
+	 * 订单数
+	 */
+	@DbIgnore
+	private Integer numOfOrder;
+
+	/**
+	 * 退款金额
+	 */
+	@DbIgnore
+	private BigDecimal refundMoney;
+
+	/**
+	 * 退款率
+	 */
+	@DbIgnore
+	private BigDecimal refundRate;
+
+	/**
+	 * 创建时间
+	 */
+	@DbField("u.created_time")
+	private Date createdTime;
+
+	/**
+	 * 最后更新时间
+	 */
+	@DbField("u.update_time")
+	private Date updateTime;
+
+	@DbField("u.is_google")
+	private Boolean isGoogle;
+
+	/**
+	 * 是否实名
+	 */
+	@DbIgnore
+	private Boolean isCert;
+}

+ 5 - 0
netflix-dao/src/main/java/com/cyksj/model/views/ChatgptCarInfoView.java

@@ -39,6 +39,11 @@ public class ChatgptCarInfoView {
      */
     private Date expTime;
 
+    /**
+     * 车队可用时间
+     */
+    private Date teamExpTime;
+
     /**
      * 车次类型 team plus 3.5
      */

+ 1 - 1
netflix-dao/src/main/java/com/cyksj/model/views/ChatgptCarSessionView.java

@@ -31,7 +31,7 @@ public class ChatgptCarSessionView {
 	private String password;
 
 	@DbField("cs.carID")
-	private String carID;
+	private String carId;
 
 
 	/**

+ 4 - 0
netflix-dao/src/main/java/com/cyksj/model/views/EquipmentUserDistributeDetailView.java

@@ -1,6 +1,7 @@
 package com.cyksj.model.views;
 
 import com.cyksj.model.entity.EquipmentUserChanceRecord;
+import com.cyksj.model.entity.EquipmentUserOrderVerifyRecord;
 import com.ejlchina.searcher.bean.DbField;
 import com.ejlchina.searcher.bean.DbIgnore;
 import com.ejlchina.searcher.bean.SearchBean;
@@ -64,4 +65,7 @@ public class EquipmentUserDistributeDetailView {
 
 	@DbField("od.pay_time")
 	private Date payTime;
+
+	@DbIgnore
+	private EquipmentUserOrderVerifyRecord verifyRecord;
 }

+ 10 - 0
netflix-dao/src/main/resources/mapper/CorpUserFollowRelationMapper.xml

@@ -127,4 +127,14 @@
         </if>
         group by cufr.corp_user_id
     </select>
+
+    <select id="getCorpUserFollowRelation" resultType="com.cyksj.model.entity.CorpUserFollowRelation">
+        select cufr.*
+        from (select id
+              from corp_user
+              where unionid = #{unionId} limit 1) cu
+                 inner join corp_user_auth ca on ca.corp_user_id = cu.id
+                 inner join corp_user_follow_relation cufr on cufr.corp_user_id = ca.external_userid
+        where cufr.follow_id = #{followId} limit 1
+    </select>
 </mapper>

+ 16 - 8
netflix-dao/src/main/resources/mapper/CorpUserTagMapper.xml

@@ -89,20 +89,19 @@
         as intention
         from (select relation_id
         from `corp_user_tag` ct
-        where tag_id = #{tagIdStr}
-        group by relation_id
-        <if test="thisDate != null">
-            having date_format(min(ct.created_time),'%Y-%m-%d') = #{thisDate}
-        </if>) s
+        where tag_id = #{tagIdStr}) s
         left join corp_user_follow_relation cufr on cufr.id = s.relation_id
         left join corp_user_auth ca on ca.external_userid = cufr.corp_user_id
         left join corp_user cu on cu.id = ca.corp_user_id
-        inner join corp_customer_acquistion_callback ccc on ccc.union_id = cu.unionid
+        inner join corp_user_pay_intention ccc on ccc.union_id = cu.unionid
+        <if test="thisDate != null">
+            and date_format(cu.created_time,'%Y-%m-%d') = #{thisDate}
+        </if>
         group by ccc.intention
     </select>
 
     <select id="getCorpUserIntentionView" resultType="com.cyksj.model.views.CorpUserIntentionView">
-        select cu.user_id,cu.name as nickname,cu.created_time,ccc.id as intentionId,ccc.intention
+        select cu.user_id,cu.name as nickname,cu.created_time,ccc2.id as intentionId,ccc.intention
         from (select distinct cu.*
         from (select DISTINCT relation_id from corp_user_tag where tag_id = #{tagIdStr}) ct
         inner join corp_user_follow_relation cufr on cufr.id = ct.relation_id
@@ -112,7 +111,8 @@
             where date_format(cu.created_time,'%Y-%m-%d') = #{thisDate}
         </if>
         ) cu
-        left join corp_customer_acquistion_callback ccc on ccc.union_id = cu.unionid
+        left join corp_user_pay_intention ccc on ccc.union_id = cu.unionid
+        left join corp_customer_acquistion_callback ccc2 on ccc2.union_id = cu.unionid
         <where>
             <if test="userId != null">
                 cu.user_id = #{userId}
@@ -125,4 +125,12 @@
             </if>
         </where>
     </select>
+
+    <select id="selectIntentionUserTag" resultType="com.cyksj.model.entity.CorpUserTag">
+        select ct.*, cufr.follow_id, cufr.corp_user_id as externalUserId
+        from corp_user_tag ct
+                 inner join corp_user_follow_relation cufr on cufr.id = ct.relation_id
+        where ct.group_name = #{groupName}
+        and cufr.corp_user_id = #{externalUserId}
+    </select>
 </mapper>

+ 6 - 0
netflix-dao/src/main/resources/mapper/OrderDonMapper.xml

@@ -507,4 +507,10 @@
             and created_time &lt; #{endDate}
         </if>
     </select>
+
+    <select id="getCouponUsedMoney" resultType="java.math.BigDecimal">
+        select sum(money) - sum(ifnull(refund_money, 0))
+        from (select id from coupon_user where coupon_id = #{couponId}) cu
+                 inner join order_don o on o.coupon_user_id = cu.id and o.status not in ('close', 'noPayment')
+    </select>
 </mapper>

+ 12 - 0
netflix-dao/src/main/resources/mapper/UserMapper.xml

@@ -27,4 +27,16 @@
         where not exists (select user_id,sum(money) from order_don o where o.user_id = s.user_id and o.relation_id != 0 and o.status not in ('close','noPayment','refund') and o.created_time &lt; #{beginOfDay} group by user_id, date_format(o.created_time,'%Y-%m-%d')
         having sum(money) >  #{largeLimit})
     </select>
+
+    <select id="getNewMaliceUser" resultType="java.lang.Integer">
+        select count(distinct o.user_id)
+        from (select user_id
+              from order_don
+              where created_time >= #{beginOfDay}
+                and status not in ('close', 'noPayment')
+                and buyer_id is not null
+              group by user_id
+              having count(distinct buyer_id) > 4) o
+                 inner join user u on u.id = o.user_id
+    </select>
 </mapper>

+ 1 - 1
netflix-service/src/main/java/com/cyksj/service/chatgpt/ChatGptAccountService.java

@@ -68,7 +68,7 @@ public interface ChatGptAccountService {
     /**
      * oai 触发车队限制
      */
-    void carLimited(String carId, String userToken, Long expTime);
+    void carLimited(String carId, String userToken, Long expTime, Boolean isTeam);
 
     /**
      * 获取GPT session

+ 40 - 14
netflix-service/src/main/java/com/cyksj/service/chatgpt/impl/ChatGptAccountServiceImpl.java

@@ -37,6 +37,7 @@ import org.springframework.stereotype.Service;
 
 import javax.annotation.PostConstruct;
 import javax.imageio.stream.FileImageOutputStream;
+import java.math.BigDecimal;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.time.LocalDateTime;
@@ -568,7 +569,10 @@ public class ChatGptAccountServiceImpl implements ChatGptAccountService {
     }
 
 
-    private Long carOaiLimit(String carId) {
+    private Long carOaiLimit(String carId, Boolean isTeam) {
+        if(isTeam && redisService.hasKey("chatgpt:team:clears_in:" + carId)){
+            return redisService.getExpire("chatgpt:team:clears_in:" + carId);
+        }
         if (redisService.hasKey("chatgpt:clears_in:" + carId)) {
             return redisService.getExpire("chatgpt:clears_in:" + carId);
         }
@@ -666,9 +670,6 @@ public class ChatGptAccountServiceImpl implements ChatGptAccountService {
         // 更新体验数据
         redisService.zAdd(key, timestamp, String.valueOf(timestamp));
 
-        // 更新车队评分
-        redisService.zIncrementScore(RedisService.key.CHATGPT_CAR_SCORES.getName(), carId, scoreToAdd);
-
         // 清理旧数据(可选)和重新计算评分(根据需要实现)
         cleanupOldExperiencesAndRecalculateScore(carId, timestamp);
     }
@@ -694,6 +695,9 @@ public class ChatGptAccountServiceImpl implements ChatGptAccountService {
         Double highExperienceScore = (double) redisService.zCount(RedisService.key.CHATGPT_CAR_HIGH_CHAT.getName() + carId, currentTimestamp - (3 * 60 * 60 * 1000), currentTimestamp) * 2;
         Double lowExperienceScore = (double) redisService.zCount(RedisService.key.CHATGPT_CAR_LOW_CHAT.getName() + carId, currentTimestamp - (3 * 60 * 60 * 1000), currentTimestamp);
         double newScore = highExperienceScore + lowExperienceScore;
+        if(carId.contains("T")){
+            newScore = BigDecimal.valueOf(newScore).divide(BigDecimal.valueOf(5), 2, BigDecimal.ROUND_HALF_UP).doubleValue();
+        }
         redisService.zAdd(RedisService.key.CHATGPT_CAR_SCORES.getName(), newScore, carId);
     }
 
@@ -745,15 +749,32 @@ public class ChatGptAccountServiceImpl implements ChatGptAccountService {
         view.setScore(Math.min(score, 200));
         view.setStatus(score >= 50 ? "繁忙" : "空闲"); // 或“繁忙”
 
-        view.setType(isPlus == 1 ? "plus" : "3.5");
-        view.setGptLimit(40); // 假设值
-        int use = getCarConversationCount(carId, 3L).intValue();
-        view.setUse(Math.min(use, 40));
-        Long aLong = carOaiLimit(carId);
-        if (aLong != 0L) {
-            view.setStatus("停运");
-            view.setExpTime(new DateTime(System.currentTimeMillis() + aLong * 1000));
+        if(carName.contains("T")){
+            view.setType("Team");
+            Long team = carOaiLimit(carId, true);
+            if (team != 0L) {
+                view.setTeamExpTime(new DateTime(System.currentTimeMillis() + team * 1000));
+            }
+            Long aLong = carOaiLimit(carId, false);
+            if (aLong != 0L) {
+                view.setExpTime(new DateTime(System.currentTimeMillis() + aLong * 1000));
+            }
+            if (view.getExpTime() != null && view.getTeamExpTime() != null) {
+                view.setStatus("全部停运");
+            }else if(view.getExpTime() != null || view.getTeamExpTime() != null) {
+                view.setStatus("部分停运");
+            }
+        }else {
+            view.setType(isPlus == 1 ? "Plus" : "3.5");
+            Long aLong = carOaiLimit(carId, false);
+            if (aLong != 0L) {
+                view.setStatus("停运");
+                view.setExpTime(new DateTime(System.currentTimeMillis() + aLong * 1000));
+            }
         }
+        int use = getCarConversationCount(carId, 3L).intValue();
+        view.setUse(use);
+
         view.setCarName(carName);
         return view;
     }
@@ -810,7 +831,7 @@ public class ChatGptAccountServiceImpl implements ChatGptAccountService {
     }
 
     @Override
-    public void carLimited(String carId, String userToken, Long expTime) {
+    public void carLimited(String carId, String userToken, Long expTime, Boolean isTeam) {
         if (carId == null) {
             if (StringUtils.isNotBlank(userToken)) {
                 ChatgptUser chatgptUser = chatgptUserMapper.selectOne(Wrappers.lambdaQuery(ChatgptUser.class).eq(ChatgptUser::getUserToken, userToken));
@@ -822,7 +843,12 @@ public class ChatGptAccountServiceImpl implements ChatGptAccountService {
                 }
             }
         }
-        redisService.set("chatgpt:clears_in:" + carId, expTime, expTime);
+        if(isTeam){
+            redisService.set("chatgpt:team:clears_in:" + carId, expTime, expTime);
+        }else {
+            redisService.set("chatgpt:clears_in:" + carId, expTime, expTime);
+        }
+
 
         try {
             //记录用户触发限制

+ 89 - 4
netflix-service/src/main/java/com/cyksj/service/corp/impl/CorpServiceImpl.java

@@ -1,10 +1,13 @@
 package com.cyksj.service.corp.impl;
 
 import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.hutool.http.HttpUtil;
 import cn.hutool.json.JSONUtil;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.common.constant.Constant;
 import com.cyksj.common.exception.BusinessRuntimeException;
 import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
 import com.cyksj.common.util.StringUtil;
@@ -13,9 +16,8 @@ import com.cyksj.common.util.corp.WxCryptUtil;
 import com.cyksj.config.corp.WeChatCorpFactory;
 import com.cyksj.config.corp.YHLXWxCorpConfig;
 import com.cyksj.dto.CorpOauth2UserInfo;
-import com.cyksj.mapper.corp.CorpCustomerAcquistionCallbackMapper;
-import com.cyksj.mapper.corp.CorpFollowMapper;
-import com.cyksj.mapper.corp.CorpUserPayIntentionMapper;
+import com.cyksj.mapper.OrderDonMapper;
+import com.cyksj.mapper.corp.*;
 import com.cyksj.model.dto.WxCorpEncryptDto;
 import com.cyksj.model.entity.*;
 import com.cyksj.model.request.corp.Attachments;
@@ -43,6 +45,7 @@ import java.io.File;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /*
  *项目名: netflix
@@ -82,6 +85,18 @@ public class CorpServiceImpl implements CorpService {
 	@Autowired
 	private CorpUserPayIntentionMapper corpUserPayIntentionMapper;
 
+	@Autowired
+	private CorpUserMapper corpUserMapper;
+
+	@Autowired
+	private CorpUserTagMapper corpUserTagMapper;
+
+	@Autowired
+	private OrderDonMapper orderDonMapper;
+
+	@Autowired
+	private CorpUserFollowRelationMapper corpUserFollowRelationMapper;
+
 	@PostConstruct
 	private void init() {
 		corpMessageRouter = new CorpMessageRouter(this)
@@ -382,8 +397,78 @@ public class CorpServiceImpl implements CorpService {
 				corpUserPayIntentionMapper.insert(corpUserPayIntention);
 			} catch (DuplicateKeyException e) {
 			}
+		} else {
+			corpUserPayIntentionMapper.updateById(corpUserPayIntention);
+		}
+		//将所属意向标签组不属于该标签的全删除
+		//并打上对应标签
+		CorpUserPayIntention.Intention intention = CorpUserPayIntention.Intention.values()[corpUserPayIntention.getIntention()];
+		String tagName = intention.getTagName();
+		String tagId = intention.getTagId();
+		String externalUserId = corpUserMapper.selectUserExternalUserId(corpUserPayIntention.getUnionId());
+		if (StrUtil.isEmpty(externalUserId)) {
+			return;
+		}
+		List<CorpUserTag> corpUserTags = corpUserTagMapper.selectIntentionUserTag(externalUserId, Constant.EP_INTENTION_GROUP_NAME);
+		//是否存在对应标签
+		AtomicBoolean isExistIntentionTag = new AtomicBoolean(false);
+		if (CollUtil.isNotEmpty(corpUserTags)) {
+			//删除之前的意向标签
+			corpUserTags.forEach(userTag -> {
+				if (!StrUtil.equals(userTag.getTagId(), tagId)) {
+					try {
+						wxCorpOps.markTag(userTag.getExternalUserId(), userTag.getFollowId(), null, new String[]{userTag.getTagId()});
+						corpUserTagMapper.deleteById(userTag.getId());
+					} catch (Exception e) {
+					}
+				} else {
+					if (!isExistIntentionTag.get()) {
+						isExistIntentionTag.set(true);
+					}
+				}
+			});
+		}
+		if (!isExistIntentionTag.get()) {
+			CorpUser corpUser = corpUserMapper.selectOne(Wrappers.lambdaQuery(CorpUser.class)
+					.eq(CorpUser::getUnionid, corpUserPayIntention.getUnionId())
+					.last("limit 1"));
+			Boolean isPayEp = false;
+			if (corpUser != null && corpUser.getUserId() != 0) {
+				//是否买过设备
+				Integer selectCount = orderDonMapper.selectCount(Wrappers.lambdaQuery(OrderDon.class)
+						.eq(OrderDon::getGoodsId, 15)
+						.eq(OrderDon::getUserId, corpUser.getUserId())
+						.notIn(OrderDon::getStatus, Constant.noOrderAllStatus));
+				if (selectCount > 0) {
+					isPayEp = true;
+				}
+			}
+			if (!isPayEp) {
+				insertUserIntentionTag(corpUserPayIntention, tagId, tagName);
+			}
+		}
+	}
+
+	public void insertUserIntentionTag(CorpUserPayIntention corpUserPayIntention, String tagId, String tagName) {
+		CorpUserFollowRelation corpUserFollowRelation = corpUserFollowRelationMapper.getCorpUserFollowRelation(corpUserPayIntention.getUnionId(), corpUserPayIntention.getFollowId());
+		if (corpUserFollowRelation == null) {
 			return;
 		}
-		corpUserPayIntentionMapper.updateById(corpUserPayIntention);
+		String externalUserId = corpUserFollowRelation.getCorpUserId();
+		String followId = corpUserFollowRelation.getFollowId();
+		try {
+			wxCorpOps.markTag(externalUserId, followId, new String[]{tagId}, null);
+			//新增用户标签
+			CorpUserTag corpUserTag = new CorpUserTag();
+			corpUserTag.setRelationId(corpUserFollowRelation.getId());
+			corpUserTag.setGroupName(Constant.EP_INTENTION_GROUP_NAME);
+			corpUserTag.setTagId(tagId);
+			corpUserTag.setTagName(tagName);
+			corpUserTag.setType(1);
+			corpUserTagMapper.insert(corpUserTag);
+		} catch (Exception e) {
+			log.error("新增用户意向标签错误:{}", StringUtil.getErrorText(e));
+		}
+
 	}
 }

+ 1 - 1
netflix-service/src/main/java/com/cyksj/service/distribute/impl/DistributeServiceImpl.java

@@ -346,7 +346,7 @@ public class DistributeServiceImpl implements DistributeService {
 			throw BusinessRuntimeException.getInstance("该专属推广优惠码已被设置");
 		} else {
 			Coupon existsCoupon = couponMapper.selectOne(Wrappers.lambdaQuery(Coupon.class)
-					.eq(Coupon::getExCode, couponDistributePopularize.getExCode())
+					.eq(Coupon::getExCode, code)
 					.eq(Coupon::getDeleted, true)
 					.select(Coupon::getId)
 					.last("limit 1"));

+ 6 - 2
netflix-service/src/main/java/com/cyksj/service/mail/impl/CuiQiuMailServiceImpl.java

@@ -55,6 +55,7 @@ public class CuiQiuMailServiceImpl implements CuiQiuMailService {
 		params.put("mail_id", ADMIN_MAIL_ID);
 		params.put("start_time", DateUtil.offsetDay(now, -1).toDateStr());
 		params.put("end_time", DateUtil.offsetDay(now, 1).toDateStr());
+		params.put("to", email);
 		params.put("limit", 20);
 
 		HttpRequest post = HttpUtil.createPost(url);
@@ -101,8 +102,11 @@ public class CuiQiuMailServiceImpl implements CuiQiuMailService {
 		CuiQiuEmailDetailDto.Content content = cuiQiuEmailDetailDto.getData().getContent();
 
 		String contentBody = content.getBody();
-
-		String code = StrUtil.subWithLength(contentBody, contentBody.indexOf("ADOBE"), 215);
+		String str = "color:#505050; font-family:adobe-clean, Helvetica Neue, Helvetica, Verdana, Arial, sans-serif;";
+		String code = StrUtil.subWithLength(contentBody, contentBody.indexOf(str), 512);
+		if (StrUtil.isNotBlank(code)) {
+			code = code.replace(str, "");
+		}
 		return StringUtil.getVerifyCodePattern(code);
 	}
 }

+ 5 - 5
netflix-service/src/main/java/com/cyksj/service/mange/impl/CmsOrderDonServiceImpl.java

@@ -861,11 +861,11 @@ public class CmsOrderDonServiceImpl extends ServiceImpl<OrderDonMapper,OrderDon>
 
             try {
                 marketFrontService.subSpecificChanceNum(orderDon.getUserId(), orderDon.getId(), orderDon.getGoodsId(), orderDon.getUpdateTime());
-                if (goodsDon.getType() == 2) {
-                    equipmentDistributeService.subDistributeBenefitsChance(orderDon.getUserId(), orderDon.getId());
-                    //扣除用户自身实物权益
-                    userRealOrderBenefitsService.deductUserRealGoodsBenefits(orderDon.getId());
-                }
+//                if (goodsDon.getType() == 2) {
+//                    equipmentDistributeService.subDistributeBenefitsChance(orderDon.getUserId(), orderDon.getId());
+//                    //扣除用户自身实物权益
+//                    userRealOrderBenefitsService.deductUserRealGoodsBenefits(orderDon.getId());
+//                }
             } catch (Exception e) {
                 log.error("wx减少抽奖机会错误,orderId:{},error:{}", orderDon.getId(), e);
             }

+ 56 - 0
netflix-service/src/main/java/com/cyksj/service/market/task/impl/BindTaskServiceImpl.java

@@ -451,5 +451,61 @@ public class BindTaskServiceImpl implements BindTaskService {
 			userMapper.updateById(phoneUser);
 			return;
 		}
+
+		if (type == 2) {
+			//是否已注册
+			User changeUser = userMapper.selectOne(Wrappers.lambdaQuery(User.class)
+					.eq(User::getEmail, account).last("limit 1"));
+			if (changeUser != null) {
+				throw BusinessRuntimeException.getInstance("您更换的手机号已注册,无法更换");
+			}
+			//是否已绑定
+			UserBindDetail changeUserBindDetail = userBindDetailMapper.selectOne(Wrappers.lambdaQuery(UserBindDetail.class)
+					.eq(UserBindDetail::getEmail, account).last("limit 1"));
+			if (changeUserBindDetail != null) {
+				throw BusinessRuntimeException.getInstance("您更换的邮箱已被绑定,无法更换");
+			}
+			//校验邮箱
+			checkEmailCode(account, code);
+			String email = user.getEmail();
+			User emailUser = user;
+			Long emailUserId = emailUser.getId();
+			//邮箱用户
+			if (StrUtil.isNotBlank(email)) {
+				UserBindDetail userBindDetail = userBindDetailMapper.selectOne(Wrappers.lambdaQuery(UserBindDetail.class)
+						.eq(UserBindDetail::getEmail, email).last("limit 1"));
+				if (userBindDetail != null) {
+					userBindDetail.setEmail(account);
+					userBindDetail.setEmailUserId(emailUserId);
+					userBindDetailMapper.updateById(userBindDetail);
+				}
+				user.setEmail(account);
+				userMapper.updateById(user);
+				return;
+			}
+			//非邮箱用户
+			UserBindDetail userBindDetail = userBindDetailMapper.selectOne(Wrappers.lambdaQuery(UserBindDetail.class)
+					.and(qr -> qr.eq(UserBindDetail::getUserId, userId)
+							.or().eq(UserBindDetail::getPhoneUserId, userId))
+					.last("limit 1"));
+			if (userBindDetail == null) {
+				throw BusinessRuntimeException.getInstance("你未有绑定其他账号");
+			}
+			if (StrUtil.isEmpty(userBindDetail.getEmail())) {
+				throw BusinessRuntimeException.getInstance("您未绑定邮箱,无法更换");
+			}
+			email = userBindDetail.getEmail();
+			emailUser = userMapper.selectOne(Wrappers.lambdaQuery(User.class)
+					.eq(User::getEmail, email).last("limit 1"));
+			if (emailUser == null) throw BusinessRuntimeException.getInstance("你绑定的邮箱未注册");
+			emailUserId = emailUser.getId();
+			userBindDetail.setEmail(account);
+			userBindDetail.setEmailUserId(emailUserId);
+			userBindDetailMapper.updateById(userBindDetail);
+
+			emailUser.setEmail(account);
+			userMapper.updateById(emailUser);
+			return;
+		}
 	}
 }

+ 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;
 }

+ 244 - 84
netflix-service/src/main/java/com/cyksj/service/midjourney/impl/MidjourneyServiceImpl.java

@@ -1,6 +1,5 @@
 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.HttpRequest;
@@ -8,37 +7,47 @@ 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.MidjourneyUser;
-import com.cyksj.model.entity.MidjourneyUserConversation;
-import com.cyksj.model.response.SubmitResult;
-import com.cyksj.redis.RedisService;
 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 org.springframework.util.CollectionUtils;
 
+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;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 
 /**
  * @author zwhui
@@ -60,25 +69,39 @@ 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())
+                .put("botType", botType)
+                .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);
     }
 
-    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) 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()))
@@ -90,67 +113,92 @@ public class MidjourneyServiceImpl implements MidjourneyService {
 
 
     @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)
+                .put("botType", botType)
+                .build();
+        SubmitResult result = submit(user.getMode(),"describe", null, param);
+        return saveConversation(user.getId(), user.getMode(),result,"DESCRIBE", StringUtils.EMPTY);
     }
 
     @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())
+                .put("botType", botType)
+                .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);
     }
 
     @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);
     }
 
     @Override
-    public MidjourneyUserConversation submitShorten(MidjourneyUser user, String prompt) throws Exception {
+    public MidjourneyUserConversation submitShorten(MidjourneyUser user, String prompt, String botType) 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())
+                .put("botType", botType)
+                .build();
+        SubmitResult result = submit(user.getMode(),"shorten", null, param);
+        return saveConversation(user.getId(), user.getMode(), result,"SHORTEN", StringUtils.EMPTY);
     }
 
     /**
      * 恢复次数
      */
-    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("关联任务不存在或已失效");
@@ -158,54 +206,67 @@ 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)
+                .put("botType", botType)
+                .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);
+        }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);
+        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);
+                }
                 throw BusinessRuntimeException.getInstance("账号不存在");
             }
             if (code == 4) {
-                throw BusinessRuntimeException.getInstance("图片重复");
+                throw BusinessRuntimeException.getInstance(submitResult.getDescription());
             }
             if (code == 24) {
                 throw BusinessRuntimeException.getInstance("prompt包含敏感词");
@@ -215,6 +276,42 @@ public class MidjourneyServiceImpl implements MidjourneyService {
         }
         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();
+                    }
+                }
+            }
+            // 增加使用次数
+            if (minAccountId != null) {
+                redisService.hincr(key, minAccountId, 1.0);
+            }
+            return minAccountId;
+        } catch (Exception e) {
+            throw BusinessRuntimeException.getInstance("获取账号失败");
+        }
+    }
     private String getActionUrl(String action) {
          switch (action) {
             case "imagine":
@@ -233,21 +330,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;
     }
@@ -256,18 +368,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);
+                    }
+                });
+                //失败返还次数
+                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);
+                        }
                     }
-                } catch (IOException e) {
-                    log.error("上传图片失败",e);
-                }finally {
-                    conversation.setId(dbConversation.getId());
-                    conversationMapper.updateById(conversation);
+                    midjourneyUserMapper.update(null, wrapper);
                 }
             }
         }
@@ -282,8 +422,9 @@ 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));
-        return result.getJSONObject("value").getJSONArray("saved").getJSONObject(0).getJSONObject("info").getStr("cdnUrl");
+        String json = HttpUtil.post("https://files.liuliangbang.vip/pic/ups", paramMap);
+        log.info("上传图片结果:url:{},json:{}",url, json);
+        return new JSONObject(json).getJSONObject("value").getJSONArray("saved").getJSONObject(0).getJSONObject("info").getStr("cdnUrl");
     }
 
     private static List<String> uploadBase64Pic(List<String> base64Array) throws IOException {
@@ -301,8 +442,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;
     }
@@ -339,4 +480,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);
+            }
+        });
+    }
 }

+ 64 - 18
netflix-service/src/main/java/com/cyksj/service/order/impl/OrderDonServiceImpl.java

@@ -43,6 +43,8 @@ import com.cyksj.mapper.*;
 import com.cyksj.mapper.channel.ChannelPopularizeMapper;
 import com.cyksj.mapper.channel.ShopConfigMapper;
 import com.cyksj.mapper.corp.CorpInteractiveUserOrderRelationMapper;
+import com.cyksj.mapper.corp.CorpUserMapper;
+import com.cyksj.mapper.corp.CorpUserTagMapper;
 import com.cyksj.mapper.manage.coupon.CouponDistributePopularizeMapper;
 import com.cyksj.mapper.manage.coupon.CouponMapper;
 import com.cyksj.mapper.manage.coupon.CouponSkuMapper;
@@ -66,6 +68,7 @@ import com.cyksj.model.manage.views.UpgradeOrderDetailView;
 import com.cyksj.model.request.*;
 import com.cyksj.model.views.*;
 import com.cyksj.redis.RedisService;
+import com.cyksj.service.corp.WxCorpOps;
 import com.cyksj.service.coupon.CouponFontService;
 import com.cyksj.service.distribute.UserBusinessFrontService;
 import com.cyksj.service.distribute.equipment.EquipmentDistributeService;
@@ -287,6 +290,12 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 
 	private final OrderDonSubsidyRelateMapper orderDonSubsidyRelateMapper;
 
+	private final CorpUserTagMapper corpUserTagMapper;
+
+	private final CorpUserMapper corpUserMapper;
+
+	private final WxCorpOps wxCorpOps;
+
 	private final UpgradePackageMapper upgradePackageMapper;
 
 	private final UpgradeOrderDonMapper upgradeOrderDonMapper;
@@ -994,6 +1003,10 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 			updateOrder.setStatus(goodsDon.getType() == 1 && goodsDon.getSecondType() == 2 ? OrderDon.Status.complete : OrderDon.Status.hasPayment);
 			updateGroupsTicket(updateOrder, goodsDon, sku);
 			orderDonMapper.updateById(updateOrder);
+			//余额使用记录
+			if (orderDon.getBalance().compareTo(BigDecimal.ZERO) > 0) {
+				userBenefitsService.recordBalanceBySource(orderDon.getUserId(), orderDon.getBalance().negate(), UserBalanceSourceRecord.Source.payment, orderDon.getId(), goodsDon.getTitle() + UserBalanceSourceRecord.Source.payment.getDesc());
+			}
 			return updateOrder;
 		}
 		//记录订单ip关联
@@ -1706,21 +1719,24 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 				couponUser.setCouponId(exists.getPopularizeCouponId());
 				couponUser.setPopularizeId(exists.getPopularizeId());
 			}
-			//非渠道推广 推广优惠码只可使用一次
-			if ((exists.getPopularizeId() != null && exists.getIsGeneral()) || exists.getPopularizeId() == null) {
-				CouponUser received = couponUserMapper.selectOne(Wrappers.lambdaQuery(CouponUser.class).eq(CouponUser::getUserId, userId).eq(CouponUser::getCouponId, couponUser.getCouponId()).last("limit 1"));
-				if (received != null && received.getStatus() != CouponUser.Status.unused) {
-					throw BusinessRuntimeException.getInstance("您已使用该优惠码所对应的优惠券");
-				}
-				couponUser.setUserId(userId);
-				couponUser.setStatus(CouponUser.Status.used);
-				couponUser.setChannel(CouponUser.Channel.exCode);
-				couponFontService.updateCouponUserValidTime(couponUser.getCouponId(), couponUser);
-				try {
-					couponUserMapper.insert(couponUser);
-				} catch (DuplicateKeyException e) {
-					log.info("用户已领取该{}优惠券", couponUser.getCouponId());
-					throw BusinessRuntimeException.getInstance("您已领取该优惠码对应的优惠券,请使用优惠券");
+			//续费优惠码可重复使用
+			if (exists.getUseScope() != Coupon.UseScope.renew) {
+				//非渠道推广 推广优惠码只可使用一次
+				if ((exists.getPopularizeId() != null && exists.getIsGeneral()) || exists.getPopularizeId() == null) {
+					CouponUser received = couponUserMapper.selectOne(Wrappers.lambdaQuery(CouponUser.class).eq(CouponUser::getUserId, userId).eq(CouponUser::getCouponId, couponUser.getCouponId()).last("limit 1"));
+					if (received != null && received.getStatus() != CouponUser.Status.unused) {
+						throw BusinessRuntimeException.getInstance("您已使用该优惠码所对应的优惠券");
+					}
+					couponUser.setUserId(userId);
+					couponUser.setStatus(CouponUser.Status.used);
+					couponUser.setChannel(CouponUser.Channel.exCode);
+					couponFontService.updateCouponUserValidTime(couponUser.getCouponId(), couponUser);
+					try {
+						couponUserMapper.insert(couponUser);
+					} catch (DuplicateKeyException e) {
+						log.info("用户已领取该{}优惠券", couponUser.getCouponId());
+						throw BusinessRuntimeException.getInstance("您已领取该优惠码对应的优惠券,请使用优惠券");
+					}
 				}
 			}
 		} else {
@@ -2359,9 +2375,9 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 				}
 				//配置了特定奖池,增加抽奖机会
 				marketFrontService.addSpecificChanceNum(orderDon.getUserId(), orderDon.getId(), orderDon.getGoodsId(), orderDon.getSkuId());
-				if (goodsDon.getType() == 2) {
-					equipmentDistributeService.addDistributeBenefitsChance(orderDon.getUserId(), orderDon.getId());
-				}
+//				if (goodsDon.getType() == 2) {
+//					equipmentDistributeService.addDistributeBenefitsChance(orderDon.getUserId(), orderDon.getId());
+//				}
 				//若该订单使用了规定推广者的兑换码 增加上下级关系
 				if (StrUtil.isNotBlank(orderDon.getCouponExCode())) {
 					addDistributeCouponCode(orderDon.getUserId(), orderDon.getCouponExCode());
@@ -2387,6 +2403,8 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 				}
 				//下级渠道目标额达标 提成金额发放
 				sendSubDistributeRewards(orderDon);
+				//删除用户意向标签
+				clearEpIntentionTag(goodsDon, orderDon.getUserId());
 			} catch (Exception e) {
 				log.error("回调修改订单信息错误,orderId:{},error:{}", orderDon.getId(), StringUtil.getErrorText(e));
 			}
@@ -3114,6 +3132,34 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 		orderDon.setMoney(price);
 	}
 
+	/**
+	 * 清除用户设备意向标签
+	 */
+	public void clearEpIntentionTag(GoodsDon goodsDon, Long userId) {
+		//实物订单,用户有设备意向,删除对应标签
+		if (goodsDon != null && goodsDon.getId() == 15) {
+			User user = userMapper.selectById(userId);
+			if (user != null && StrUtil.isNotBlank(user.getUnionid())) {
+				String externalUserId = corpUserMapper.selectUserExternalUserId(user.getUnionid());
+				if (StrUtil.isEmpty(externalUserId)) {
+					return;
+				}
+				List<CorpUserTag> corpUserTags = corpUserTagMapper.selectIntentionUserTag(externalUserId, Constant.EP_INTENTION_GROUP_NAME);
+				if (CollUtil.isNotEmpty(corpUserTags)) {
+					//删除之前的意向标签
+					corpUserTags.forEach(userTag -> {
+						try {
+							wxCorpOps.markTag(userTag.getExternalUserId(), userTag.getFollowId(), null, new String[]{userTag.getTagId()});
+							corpUserTagMapper.deleteById(userTag.getId());
+						} catch (Exception e) {
+							log.error("删除用户设备意向标签错误:{}", StringUtil.getErrorText(e));
+						}
+					});
+				}
+			}
+		}
+	}
+
 
 	@Override
 	public UpgradeOrderDon submitUpgrade(OrderPayRequest payRequest) {

+ 2 - 2
netflix-service/src/main/java/com/cyksj/service/pay/impl/TransferFuncServiceImpl.java

@@ -154,7 +154,7 @@ public class TransferFuncServiceImpl implements TransferFuncService {
 					if (StrUtil.equals(alipayResponse.getSubCode(), "SECURITY_CHECK_FAILED")) {
 						return GatewayResponse.FAIL.newBuilder().setMsg("当前支付宝提现人数较多,请稍后再试").toResult();
 					}
-					return GatewayResponse.FAIL.newBuilder().setMsg(alipayResponse.getSubMsg()).toResult();
+					return GatewayResponse.FAIL.newBuilder().setMsg("目前财务清账中,48小时后可提现").toResult();
 				}
 			}
 			try {
@@ -381,7 +381,7 @@ public class TransferFuncServiceImpl implements TransferFuncService {
 					if (StrUtil.equals(alipayResponse.getSubCode(), "SECURITY_CHECK_FAILED")) {
 						return GatewayResponse.FAIL.newBuilder().setMsg("当前支付宝提现人数较多,请稍后再试").toResult();
 					}
-					return GatewayResponse.FAIL.newBuilder().setMsg(alipayResponse.getSubMsg()).toResult();
+					return GatewayResponse.FAIL.newBuilder().setMsg("目前财务清账中,48小时后可提现").toResult();
 				}
 			}
 			try {

+ 2 - 0
netflix-service/src/main/java/com/cyksj/service/relation/GroupRelationFrontService.java

@@ -53,4 +53,6 @@ public interface GroupRelationFrontService {
 	Integer getUserCodeNumBySkuId(Long userId, Long skuId, Boolean isHasVerifyCode);
 
 	List<RenewalView>  getHasPayGoods(long userId);
+
+	List<RenewalView> getExpiryRenewalNotify(Long userId);
 }

+ 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);
+//				}
+//			}
+//		});
+//	}
+//}

+ 11 - 0
netflix-web/src/main/java/com/cyksj/web/controller/distribute/EquipmentDistributeController.java

@@ -10,6 +10,7 @@ import com.cyksj.common.constant.Constant;
 import com.cyksj.common.exception.BusinessRuntimeException;
 import com.cyksj.common.util.Jsons;
 import com.cyksj.dto.Result;
+import com.cyksj.dto.UserSharedDto;
 import com.cyksj.enums.GatewayResponse;
 import com.cyksj.mapper.OrderDonMapper;
 import com.cyksj.mapper.UserMapper;
@@ -27,6 +28,7 @@ import com.cyksj.redis.RedisService;
 import com.cyksj.service.distribute.equipment.EquipmentDistributeFrontService;
 import com.cyksj.service.distribute.equipment.EquipmentDistributeService;
 import com.cyksj.service.sys.SysConfigService;
+import com.cyksj.service.user.UserService;
 import com.cyksj.web.util.StpUserUtil;
 import com.ejlchina.searcher.BeanSearcher;
 import com.ejlchina.searcher.SearchResult;
@@ -70,6 +72,8 @@ public class EquipmentDistributeController {
 
 	private final OrderDonMapper orderDonMapper;
 
+	private final UserService userService;
+
 	private final EquipmentDistributeService equipDistributeService;
 
 	private final EquipmentUserDistributeInviteRecordMapper inviteRecordMapper;
@@ -216,7 +220,14 @@ public class EquipmentDistributeController {
 				.between(User::getCreatedTime, DateUtil.beginOfDay(now), DateUtil.endOfDay(now)).last("limit 1"));
 		Boolean isNewUser = user != null ? true : false;
 		Long sharedId = req.getSharedId();
+		String dsCode = req.getDsCode();
 		Integer dsType = req.getDsType();
+		if (StrUtil.isNotBlank(dsCode)) {
+			UserSharedDto userSharedDto = userService.getUserShredDtoByDsCode(dsCode);
+			if (userSharedDto != null) {
+				sharedId = userSharedDto.getSharedId();
+			}
+		}
 		//新旧用户都可实物分销
 		if (sharedId != null && dsType != null && dsType == 2) {
 			equipDistributeService.saveDistributionInvitation(sharedId, isNewUser, userId);

+ 10 - 0
netflix-web/src/main/java/com/cyksj/web/controller/group/GroupRelationController.java

@@ -753,4 +753,14 @@ public class GroupRelationController {
         cmsOrderDonService.unifiedRefund(refundReq);
         return GatewayResponse.SUCCESS.newBuilder().toResult();
     }
+
+    /**
+     * 每日车票到期弹窗
+     */
+    @GetMapping("/get/expiry/notify")
+    public Result<List<RenewalView>> getExpiryRenewalNotify() {
+        Long userId = StpUserUtil.getLoginIdAsLong();
+        List<RenewalView> expiryRenewals = groupRelationFrontService.getExpiryRenewalNotify(userId);
+        return GatewayResponse.SUCCESS.newBuilder().toResult(expiryRenewals);
+    }
 }

+ 24 - 0
netflix-web/src/main/java/com/cyksj/web/controller/manage/CmsUserController.java

@@ -20,6 +20,7 @@ import com.cyksj.mapper.corp.CorpUserFollowRelationMapper;
 import com.cyksj.mapper.manage.cms.CmsUserMapper;
 import com.cyksj.mapper.market.task.UserBindDetailMapper;
 import com.cyksj.model.entity.*;
+import com.cyksj.model.manage.views.MaliceUserView;
 import com.cyksj.model.manage.views.UserView;
 import com.cyksj.model.request.UserBalanceReq;
 import com.cyksj.model.request.UserFuncPropertyReq;
@@ -445,4 +446,27 @@ public class CmsUserController{
         Integer newConsumerCount = userMapper.getLargeNewConsumer(beginOfDay, largeLimit);
         return GatewayResponse.SUCCESS.newBuilder().toResult(newConsumerCount > 0 ? true : false);
     }
+
+    /**
+     * 超过恶意用户列表
+     * 使用五个以上得支付宝
+     */
+    @GetMapping("/get/malice/user")
+    public Result<SearchResult<MaliceUserView>> getMaliceUser() {
+        MapBuilder builder = MapUtils.flatBuilder(request.getParameterMap());
+        SearchResult<MaliceUserView> search = beanSearcher.search(MaliceUserView.class, builder
+                .orderBy(MaliceUserView::getId).asc()
+                .build());
+        return GatewayResponse.SUCCESS.newBuilder().toResult(search);
+    }
+
+    /**
+     * 是否有最新恶意用户
+     */
+    @GetMapping("get/malice/user/newUser")
+    public Result<Boolean> getNewMaliceUser() {
+        DateTime beginOfDay = DateUtil.beginOfDay(DateTime.now());
+        Integer newConsumerCount = userMapper.getNewMaliceUser(beginOfDay);
+        return GatewayResponse.SUCCESS.newBuilder().toResult(newConsumerCount > 0 ? true : false);
+    }
 }

+ 19 - 1
netflix-web/src/main/java/com/cyksj/web/controller/manage/corp/WxCorpController.java

@@ -223,7 +223,19 @@ public class WxCorpController {
 	}
 
 	public String getCorpUuid() {
-		return Optional.ofNullable(request.getHeader(CORP_UUID)).orElse(StringUtil.EMPTY);
+		Cookie[] cookies = this.request.getCookies();
+		if (cookies != null) {
+			Cookie[] var3 = cookies;
+			int var4 = cookies.length;
+
+			for(int var5 = 0; var5 < var4; ++var5) {
+				Cookie cookie = var3[var5];
+				if (cookie != null && CORP_UUID.equals(cookie.getName())) {
+					return cookie.getValue();
+				}
+			}
+		}
+		return StrUtil.EMPTY;
 	}
 
 
@@ -359,11 +371,16 @@ public class WxCorpController {
 		return GatewayResponse.SUCCESS.newBuilder().toResult(corpUserPayIntention);
 	}
 
+	/**
+	 * 修改意向
+	 */
 	@PutMapping("/update/user/intention")
 	public Result<CorpUserPayIntention> updateUserIntention(@RequestBody CorpUserPayIntention corpUserPayIntention) {
 		if (corpUserPayIntention.getUnionId() == null || corpUserPayIntention.getIntention() == null) {
 			throw BusinessRuntimeException.getInstance("缺少必填参数.");
 		}
+		String followId = getCorpUuid();
+		corpUserPayIntention.setFollowId(followId);
 		corpService.updateUserIntention(corpUserPayIntention);
 		return GatewayResponse.SUCCESS.newBuilder().toResult(corpUserPayIntention);
 	}
@@ -383,6 +400,7 @@ public class WxCorpController {
 	 * 修改 意向
 	 */
 	@PutMapping("/upCallbackIntention")
+	@Deprecated
 	public Result<String> upCallbackIntention(@RequestBody CorpCustomerAcquistionCallback corpCustomerAcquistionCallback) {
 		log.info("修改意向:{}", corpCustomerAcquistionCallback);
 		if (corpCustomerAcquistionCallback.getId() == null || corpCustomerAcquistionCallback.getId() == 0 || corpCustomerAcquistionCallback.getIntention() == null) {

+ 11 - 0
netflix-web/src/main/java/com/cyksj/web/controller/manage/coupon/CouponController.java

@@ -38,6 +38,8 @@ import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
 import javax.validation.constraints.Min;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -536,4 +538,13 @@ public class CouponController {
 				.build());
 		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
 	}
+
+	/**
+	 * 使用优惠券订单金额
+	 */
+	@GetMapping("/get/used/money/{couponId}")
+	public Result<BigDecimal> getUsedMoney(@PathVariable Long couponId) {
+		BigDecimal money = Optional.ofNullable(orderDonMapper.getCouponUsedMoney(couponId)).orElse(BigDecimal.ZERO);
+		return GatewayResponse.SUCCESS.newBuilder().toResult(money.divide(BigDecimal.valueOf(100), 2, RoundingMode.DOWN));
+	}
 }

+ 49 - 4
netflix-web/src/main/java/com/cyksj/web/controller/manage/distribute/EquipmentController.java

@@ -1,12 +1,16 @@
 package com.cyksj.web.controller.manage.distribute;
 
+import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.common.exception.BusinessRuntimeException;
 import com.cyksj.dto.Result;
 import com.cyksj.enums.GatewayResponse;
 import com.cyksj.mapper.GoodsDonSkuMapper;
 import com.cyksj.mapper.UserMapper;
 import com.cyksj.mapper.manage.coupon.CouponMapper;
+import com.cyksj.mapper.manage.distribute.equipment.EquipmentUserOrderVerifyRecordMapper;
 import com.cyksj.mapper.manage.distribute.equipment.EquipmentUserPayRecordMapper;
+import com.cyksj.model.entity.EquipmentUserOrderVerifyRecord;
 import com.cyksj.model.entity.EquipmentUserPayRecord;
 import com.cyksj.model.entity.GoodsDonSku;
 import com.cyksj.model.entity.User;
@@ -17,12 +21,11 @@ import com.ejlchina.searcher.BeanSearcher;
 import com.ejlchina.searcher.SearchResult;
 import com.ejlchina.searcher.util.MapUtils;
 import lombok.RequiredArgsConstructor;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
+import java.math.BigDecimal;
 import java.util.Optional;
 
 /*
@@ -47,6 +50,8 @@ public class EquipmentController {
 
 	private final GoodsDonSkuMapper goodsDonSkuMapper;
 
+	private final EquipmentUserOrderVerifyRecordMapper equipmentUserOrderVerifyRecordMapper;
+
 	/**
 	 * 实物分销列表
 	 */
@@ -77,7 +82,47 @@ public class EquipmentController {
 			data.setCouponName(couponName);
 			GoodsDonSku goodsDonSku = goodsDonSkuMapper.selectById(data.getSkuId());
 			Optional.ofNullable(goodsDonSku).ifPresent(sku -> data.setSpecVal(sku.getSpecVal()));
+			EquipmentUserOrderVerifyRecord equipmentUserOrderVerifyRecord = equipmentUserOrderVerifyRecordMapper.selectOne(Wrappers.lambdaQuery(EquipmentUserOrderVerifyRecord.class)
+					.eq(EquipmentUserOrderVerifyRecord::getOrderId, data.getOrderId())
+					.last("limit 1"));
+			data.setVerifyRecord(equipmentUserOrderVerifyRecord);
 		});
 		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
 	}
+
+	/**
+	 * 发送奖励记录
+	 */
+	@PutMapping("/verify/reward/record")
+	public Result<SearchResult> verifyRewardRecord(@RequestBody EquipmentUserOrderVerifyRecord verifyRecord) {
+		Long orderId = verifyRecord.getOrderId();
+		EquipmentUserPayRecord equipmentUserPayRecord = payRecordMapper.selectOne(Wrappers.lambdaQuery(EquipmentUserPayRecord.class).eq(EquipmentUserPayRecord::getOrderId, orderId).last("limit 1"));
+		if (equipmentUserPayRecord == null) {
+			throw BusinessRuntimeException.getInstance("实物分销订单不存在");
+		}
+		if (equipmentUserPayRecord.getStatus() == EquipmentUserPayRecord.Status.refund) {
+			throw BusinessRuntimeException.getInstance("实物分销订单已退款");
+		}
+		BigDecimal money = verifyRecord.getMoney();
+		String img = verifyRecord.getImg();
+		if (StrUtil.isNotBlank(img)) {
+			throw BusinessRuntimeException.getInstance("转账截图不为空");
+		}
+		if (money == null) {
+			throw BusinessRuntimeException.getInstance("转账金额不为空");
+		}
+		if (verifyRecord.getId() == null) {
+			verifyRecord.setSharedId(equipmentUserPayRecord.getShareId());
+			verifyRecord.setUserId(equipmentUserPayRecord.getUserId());
+			try {
+				equipmentUserOrderVerifyRecordMapper.insert(verifyRecord);
+			} catch (DuplicateKeyException e) {
+				throw BusinessRuntimeException.getInstance("其他客服已转账..");
+			}
+		} else {
+			equipmentUserOrderVerifyRecordMapper.updateById(verifyRecord);
+		}
+		return GatewayResponse.SUCCESS.newBuilder().toResult();
+	}
+
 }

+ 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("车队账号禁用成功");
     }
 

+ 31 - 8
netflix-web/src/main/java/com/cyksj/web/controller/mirror/MidjourneyController.java

@@ -2,13 +2,16 @@ package com.cyksj.web.controller.mirror;
 
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.common.annotation.NoSubmit;
 import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.util.IoKit;
 import com.cyksj.dto.Result;
 import com.cyksj.model.dto.*;
 import com.cyksj.enums.GatewayResponse;
 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.redis.RedisService;
 import com.cyksj.service.midjourney.MidjourneyAccountService;
 import com.cyksj.service.midjourney.MidjourneyService;
@@ -21,8 +24,11 @@ import lombok.extern.slf4j.Slf4j;
 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;
 
 /**
  * @author zwhui
@@ -161,6 +167,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();
@@ -170,7 +177,7 @@ 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());
@@ -183,6 +190,7 @@ public class MidjourneyController {
      * 提交Describe任务
      */
     @PostMapping("/submit/describe")
+    @NoSubmit
     public Result<MidjourneyUserConversation> submitDescribe(@RequestBody SubmitDescribeDTO submitDescribeDTO) {
         log.info("提交Describe任务");
         MidjourneyUser user = getUser();
@@ -192,7 +200,7 @@ public class MidjourneyController {
         }
         MidjourneyUserConversation conversation;
         try {
-            conversation = midjourneyService.submitDescribe(user, submitDescribeDTO.getBase64());
+            conversation = midjourneyService.submitDescribe(user, submitDescribeDTO.getBase64(), submitDescribeDTO.getBotType());
         } catch (Exception e) {
             recoverUserLimit(user.getId(), user.getMode(),num);
             throw BusinessRuntimeException.getInstance(e.getMessage());
@@ -205,6 +213,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();
@@ -214,7 +223,7 @@ 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());
@@ -227,6 +236,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();
@@ -249,6 +259,7 @@ public class MidjourneyController {
      * 提交Shorten任务
      */
     @PostMapping("/submit/shorten")
+    @NoSubmit
     public Result<MidjourneyUserConversation> submitShorten(@RequestBody SubmitShortenDTO submitShortenDTO) {
         log.info("提交Shorten任务 提示词:{}",submitShortenDTO.getPrompt());
         MidjourneyUser user = getUser();
@@ -258,7 +269,7 @@ 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());
@@ -271,21 +282,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);
     }
 
@@ -317,6 +328,7 @@ public class MidjourneyController {
      * 取消任务
      */
     @PostMapping("/conversation/{id}/cancel")
+    @NoSubmit
     public Result<MidjourneyUserConversation> conversationCancel(@PathVariable("id") Long id){
         MidjourneyUser user = getUser();
         log.info("取消任务 id:{}",id);
@@ -339,4 +351,15 @@ public class MidjourneyController {
         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);
+    }
 }

+ 3 - 3
netflix-web/src/main/java/com/cyksj/web/controller/mirror/MirrorController.java

@@ -239,7 +239,7 @@ public class MirrorController {
      * oai触发车队限制
      */
     @GetMapping("/gpt/car/limited")
-    public Result<String> carLimited(String carId, Long expTime) {
+    public Result<String> carLimited(String carId, Long expTime, Boolean isTeam) {
         String authorization = request.getHeader("Authorization");
         String userToken = "";
         if(StringUtils.isNotBlank(authorization)){
@@ -253,7 +253,7 @@ public class MirrorController {
 
         String finalUserToken = userToken;
         TASK_EXECUTOR.execute(()->{
-            chatGptAccountService.carLimited(carId, finalUserToken, expTime);
+            chatGptAccountService.carLimited(carId, finalUserToken, expTime,isTeam);
         });
         return GatewayResponse.SUCCESS.newBuilder().toResult();
     }
@@ -319,7 +319,7 @@ public class MirrorController {
     public Result<String> midjourneyMirror(@PathVariable Long relationId) {
         Long userId = StpUserUtil.getLoginIdAsLong();
         MidjourneyUser midjourneyUser = midjourneyAccountService.getMidjourneyUserToken(userId, relationId);
-        return GatewayResponse.SUCCESS.newBuilder().toResult(midjourneyHost + (envCommonService.isPreEnv() ? "/8082":"/8081") +"/api/applets/mirror/midjourneyMirrorWithToken/" + midjourneyUser.getUserToken());
+        return GatewayResponse.SUCCESS.newBuilder().toResult(midjourneyHost + (EnvCommonService.active.equals(envCommonService.getEnv()) ? "/8081":"/8082") +"/api/applets/mirror/midjourneyMirrorWithToken/" + midjourneyUser.getUserToken());
     }
 
     @GetMapping("/midjourneyMirrorWithToken/{userToken}")