zoujiajian пре 2 година
родитељ
комит
470de247c8
31 измењених фајлова са 1106 додато и 66 уклоњено
  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. 13 0
      netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpFollowActiveCodeClickRecord.java
  4. 13 0
      netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpFollowActiveCodeMapper.java
  5. 17 0
      netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpFollowDutyRelateMapper.java
  6. 36 0
      netflix-dao/src/main/java/com/cyksj/model/dto/CorpFollowActiveCodeDto.java
  7. 50 0
      netflix-dao/src/main/java/com/cyksj/model/entity/CorpFollowActiveCode.java
  8. 16 0
      netflix-dao/src/main/java/com/cyksj/model/entity/CorpFollowActiveCodeClickRecord.java
  9. 35 0
      netflix-dao/src/main/java/com/cyksj/model/entity/CorpFollowDutyRelate.java
  10. 5 0
      netflix-dao/src/main/java/com/cyksj/model/entity/CorpUser.java
  11. 1 0
      netflix-dao/src/main/java/com/cyksj/model/entity/CouponUser.java
  12. 5 0
      netflix-dao/src/main/java/com/cyksj/model/request/corp/CorpFollowContact.java
  13. 59 0
      netflix-dao/src/main/java/com/cyksj/model/views/CorpActiveCodeGoodsOrderView.java
  14. 44 0
      netflix-dao/src/main/java/com/cyksj/model/views/CorpActiveCodeOrderView.java
  15. 86 0
      netflix-dao/src/main/java/com/cyksj/model/views/CorpFollowActiveCodeView.java
  16. 14 0
      netflix-dao/src/main/resources/mapper/CorpFollowDutyRelateMapper.xml
  17. 8 0
      netflix-dao/src/main/resources/mapper/OrderDonMapper.xml
  18. 2 1
      netflix-service/src/main/java/com/cyksj/redis/RedisService.java
  19. 11 0
      netflix-service/src/main/java/com/cyksj/service/corp/CorpFollowActiveCodeFrontService.java
  20. 6 0
      netflix-service/src/main/java/com/cyksj/service/corp/CorpFollowDynamicConfigService.java
  21. 17 0
      netflix-service/src/main/java/com/cyksj/service/corp/CorpFollowService.java
  22. 7 3
      netflix-service/src/main/java/com/cyksj/service/corp/WxCorpOps.java
  23. 103 25
      netflix-service/src/main/java/com/cyksj/service/corp/impl/CorpEventActionServiceImpl.java
  24. 101 0
      netflix-service/src/main/java/com/cyksj/service/corp/impl/CorpFollowActiveCodeFrontServiceImpl.java
  25. 71 0
      netflix-service/src/main/java/com/cyksj/service/corp/impl/CorpFollowDynamicConfigServiceImpl.java
  26. 141 0
      netflix-service/src/main/java/com/cyksj/service/corp/impl/CorpFollowServiceImpl.java
  27. 19 2
      netflix-service/src/main/java/com/cyksj/service/corp/impl/WxCorpOpsImpl.java
  28. 28 1
      netflix-service/src/main/java/com/cyksj/service/user/impl/UserServiceImpl.java
  29. 32 0
      netflix-web/src/main/java/com/cyksj/web/controller/landing/CorpFollowActiveCodeController.java
  30. 0 34
      netflix-web/src/main/java/com/cyksj/web/controller/manage/corp/CorpFollowConfigController.java
  31. 159 0
      netflix-web/src/main/java/com/cyksj/web/controller/manage/corp/CorpFollowController.java

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

@@ -195,4 +195,9 @@ public interface Constant {
 	String USER_OPEN_SHOP_DS_ORDERS_LIMIT_KEY = "user_open_shop_ds_orders_limit_key";
 
 	String DOUBLE_VERIFY_CODE_TIMES_KEY = "double_verify_code_times_key";
+
+	/**
+	 * 活码标记key
+	 */
+	String AC_CODE_KEY = "ac_code_key:";
 }

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

@@ -54,4 +54,6 @@ public interface OrderDonMapper extends BaseMapper<OrderDon> {
 	BigDecimal statisticsUsedBalance(@Param("startDate") Date startDate, @Param("endDate") Date endDate, @Param("noOrderAllStatus") List<String> noOrderAllStatus);
 
 	Map<String, Object> selectYhShopDistributeOrderDetail(@Param("startDate") Date startDate, @Param("endDate") Date endDate);
+
+	Integer selectCountActiveCodeOrderByAcId(Long acId);
 }

+ 13 - 0
netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpFollowActiveCodeClickRecord.java

@@ -0,0 +1,13 @@
+package com.cyksj.mapper.corp;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.CorpFollowActiveCodeClickRecord;
+
+/**
+ * 项目名: yhlxj
+ * 文件名: CorpFollowActiveCodeClickRecordMapper
+ * 创建者: JavaZou
+ * 创建时间:2023/10/9 16:28
+ */
+public interface CorpFollowActiveCodeClickRecordMapper extends BaseMapper<CorpFollowActiveCodeClickRecord> {
+}

+ 13 - 0
netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpFollowActiveCodeMapper.java

@@ -0,0 +1,13 @@
+package com.cyksj.mapper.corp;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.CorpFollowActiveCode;
+
+/**
+ * 项目名: yhlxj
+ * 文件名: CorpFollowActiveCodeMapper
+ * 创建者: JavaZou
+ * 创建时间:2023/10/9 16:27
+ */
+public interface CorpFollowActiveCodeMapper extends BaseMapper<CorpFollowActiveCode> {
+}

+ 17 - 0
netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpFollowDutyRelateMapper.java

@@ -0,0 +1,17 @@
+package com.cyksj.mapper.corp;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.CorpFollowDutyRelate;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: CorpFollowDutyRelateMapper
+ * 创建者: JavaZou
+ * 创建时间:2023/10/9 17:20
+ */
+public interface CorpFollowDutyRelateMapper extends BaseMapper<CorpFollowDutyRelate> {
+	List<CorpFollowDutyRelate> selectCorpFollowRelate(@Param("list") List<String> followIds);
+}

+ 36 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/CorpFollowActiveCodeDto.java

@@ -0,0 +1,36 @@
+package com.cyksj.model.dto;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj
+ * 文件名: CorpFollowActiveCodeDto
+ * 创建者: JavaZou
+ * 创建时间:2023/10/9 17:10
+ */
+
+@Getter
+@Setter
+public class CorpFollowActiveCodeDto {
+
+	/**
+	 * 企业微信的客服id 数组
+	 */
+	private String followIds;
+
+	/**
+	 * 对应系统配置的客服
+	 */
+	private String serviceNames;
+
+	/**
+	 * 配置id
+	 */
+	private String configId;
+
+	/**
+	 * 活码
+	 */
+	private String qrCode;
+}

+ 50 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/CorpFollowActiveCode.java

@@ -0,0 +1,50 @@
+package com.cyksj.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj
+ * 文件名: CorpFollowActiveCode
+ * 创建者: JavaZou
+ * 创建时间:2023/10/9 16:23
+ */
+@Getter
+@Setter
+public class CorpFollowActiveCode extends BaseEntity{
+
+	/**
+	 * 客服
+	 */
+	private String followStr;
+
+	/**
+	 * 欢迎语
+	 */
+	private String welcomeStr;
+
+	/**
+	 * 活码
+	 */
+	private String codeStr;
+
+
+	/**
+	 * 客户标签
+	 */
+	private String markTag;
+
+	/**
+	 * 是否值班
+	 */
+	private Boolean isDuty;
+
+	/**
+	 * 创建者
+	 */
+	private String operator;
+
+	private Long couponId;
+
+	private Boolean deleted;
+}

+ 16 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/CorpFollowActiveCodeClickRecord.java

@@ -0,0 +1,16 @@
+package com.cyksj.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj
+ * 文件名: CorpFollowActiveCodeClickRecord
+ * 创建者: JavaZou
+ * 创建时间:2023/10/9 16:28
+ */
+@Getter
+@Setter
+public class CorpFollowActiveCodeClickRecord extends BaseEntity{
+	private Long acId;
+}

+ 35 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/CorpFollowDutyRelate.java

@@ -0,0 +1,35 @@
+package com.cyksj.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: CorpFollowDutyRelate
+ * 创建者: JavaZou
+ * 创建时间:2023/10/9 17:19
+ */
+@Getter
+@Setter
+public class CorpFollowDutyRelate extends BaseEntity{
+
+	/**
+	 * 企业微信客服id
+	 */
+	private String followId;
+
+
+	/**
+	 * 企业微信客服名称
+	 */
+	private String followName;
+
+	/**
+	 * 值班客服名称
+	 */
+	private String serviceName;
+
+	private Integer st;
+
+	private Integer et;
+}

+ 5 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/CorpUser.java

@@ -47,4 +47,9 @@ public class CorpUser extends BaseEntity implements Serializable {
 	 * 添加渠道来源
 	 */
 	private String state;
+
+	/**
+	 * 活动活码id
+	 */
+	private Long acId;
 }

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

@@ -70,6 +70,7 @@ public class CouponUser extends BaseEntity {
 		corp_bulk_task("corp_bulk_task", "企业微信群发任务"),
 		channel_popularize("channel_popularize", "渠道推广"),
 		new_welfare("new_welfare", "新人福利"),
+		corp_active_code("corp_active_code", "企业微信活码")
 		;
 		private String channel;
 		private String desc;

+ 5 - 0
netflix-dao/src/main/java/com/cyksj/model/request/corp/CorpFollowContact.java

@@ -12,6 +12,11 @@ import java.util.List;
 @Setter
 @JsonInclude(JsonInclude.Include.NON_NULL)
 public class CorpFollowContact {
+	/**
+	 * 配置id
+	 */
+	private String configId;
+
 	/**
 	 * 联系方式类型,1-单人, 2-多人
 	 */

+ 59 - 0
netflix-dao/src/main/java/com/cyksj/model/views/CorpActiveCodeGoodsOrderView.java

@@ -0,0 +1,59 @@
+package com.cyksj.model.views;
+
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+
+/**
+ * 项目名: yhlxj
+ * 文件名: CorpActiveCodeGoodsOrderView
+ * 创建者: JavaZou
+ * 创建时间:2023/10/10 13:45
+ */
+@Getter
+@Setter
+@SearchBean(tables = "crop_user cu left join order_don o and cu.user_id = o.user_id and cu.user_id != 0 and o.status not in ('close','noPayment') :acId: :time:" +
+		" left join goods_don g on g.id = o.goods_id" +
+		" left join goods_don_sku sku on sku.id = o.sku_id" +
+		" left join user u on u.id = o.user_id",
+		groupBy = "g.id,g.title")
+public class CorpActiveCodeGoodsOrderView {
+	/**
+	 * 平台名称
+	 */
+	@DbField("g.title")
+	private String goodsName;
+
+	@DbField("g.id")
+	private Long goodsId;
+
+	@DbField("g.type")
+	private Integer goodsDonType;
+
+	/**
+	 * 订单数
+	 */
+	@DbField("count(o.id)")
+	private Integer orderCount;
+
+	/**
+	 * 订单金额
+	 */
+	@DbField("IFNULL(sum(o1.money),0)")
+	private BigDecimal orderMoney;
+
+	/**
+	 * 退款数
+	 */
+	@DbField("count(if(o.status = 'refund',1,NULL))")
+	private Integer refundCount;
+
+	/**
+	 * 退款金额
+	 */
+	@DbField("IFNULL(sum(if(o.status = 'refund',o.refund_money,0)),0)")
+	private BigDecimal refundMoney;
+}

+ 44 - 0
netflix-dao/src/main/java/com/cyksj/model/views/CorpActiveCodeOrderView.java

@@ -0,0 +1,44 @@
+package com.cyksj.model.views;
+
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 项目名: yhlxj
+ * 文件名: CorpActiveCodeOrderView
+ * 创建者: JavaZou
+ * 创建时间:2023/10/10 11:35
+ */
+@Getter
+@Setter
+@SearchBean(tables = "crop_user cu left join order_don o and cu.user_id = o.user_id and cu.user_id != 0 and o.status not in ('close','noPayment','refund') :acId:" +
+		" left join goods_don g on g.id = o.goods_id" +
+		" left join goods_don_sku sku on sku.id = o.sku_id" +
+		" left join user u on u.id = o.user_id")
+public class CorpActiveCodeOrderView {
+	@DbField("o.id")
+	private Long orderId;
+
+	@DbField("o.user_id")
+	private Long userId;
+
+	@DbField("u.nickname")
+	private String nickname;
+
+	@DbField("g.title")
+	private String title;
+
+	@DbField("sku.spec_val")
+	private String specVal;
+
+	@DbField("o.money")
+	private BigDecimal money;
+
+	@DbField("o.created_time")
+	private Date createdTime;
+}

+ 86 - 0
netflix-dao/src/main/java/com/cyksj/model/views/CorpFollowActiveCodeView.java

@@ -0,0 +1,86 @@
+package com.cyksj.model.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.util.Date;
+
+/**
+ * 项目名: yhlxj
+ * 文件名: CorpFollowActiveCodeView
+ * 创建者: JavaZou
+ * 创建时间:2023/10/10 10:22
+ */
+@Getter
+@Setter
+@SearchBean(tables = "corp_follow_active_code cfa",
+		where = "cfa.deleted is true")
+public class CorpFollowActiveCodeView {
+
+	@DbField("cfa.id")
+	private Long id;
+
+	/**
+	 * 客服
+	 */
+	@DbField("cfa.follow_str")
+	private String followStr;
+
+	/**
+	 * 欢迎语
+	 */
+	@DbField("cfa.welcome")
+	private String welcome;
+
+	/**
+	 * 活码
+	 */
+	@DbField("cfa.code_str")
+	private String codeStr;
+
+
+	/**
+	 * 客户标签
+	 */
+	@DbField("cfa.mark_tag")
+	private String markTag;
+
+	/**
+	 * 是否值班
+	 */
+	@DbField("cfa.is_duty")
+	private Boolean isDuty;
+
+	/**
+	 * 创建者
+	 */
+	@DbField("cfa.operator")
+	private String operator;
+
+	@DbField("cfa.coupon_id")
+	private Long couponId;
+
+	/**
+	 * 优惠券名
+	 */
+	@DbIgnore
+	private String couponName;
+
+	/**
+	 * 用户数
+	 */
+	@DbIgnore
+	private Integer numOfUser;
+
+	/**
+	 * 订单数
+	 */
+	@DbIgnore
+	private Integer orderNum;
+
+	@DbField("cfa.created_time")
+	private Date createdTime;
+}

+ 14 - 0
netflix-dao/src/main/resources/mapper/CorpFollowDutyRelateMapper.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+
+<mapper namespace="com.cyksj.mapper.corp.CorpFollowDutyRelateMapper">
+
+
+    <select id="selectCorpFollowRelate" resultType="com.cyksj.model.entity.CorpFollowDutyRelate">
+        select * from corp_follow_duty_relate where follow_id in
+        <foreach collection="list" item="item" open="(" separator="," close=")">
+            #{item}
+        </foreach>
+        and st = (select st from corp_follow_duty_relate order by st limit 1)
+    </select>
+</mapper>

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

@@ -232,4 +232,12 @@
             and created_time &lt; #{endDate}
         </if>
     </select>
+
+    <select id="selectCountActiveCodeOrderByAcId" resultType="java.lang.Integer">
+        select count(distinct o.id)
+        from (select user_id from corp_user where ac_id = #{acId} and user_id != 0) s
+                 left join order_don o
+                           on o.user_id = s.user_Id
+        where o.status not in ('close', 'noPayment', 'refund')
+    </select>
 </mapper>

+ 2 - 1
netflix-service/src/main/java/com/cyksj/redis/RedisService.java

@@ -652,6 +652,7 @@ public class RedisService {
         PLUS_GOODS_ADDRESS_KEY("plus_goods_address_key:", "优惠加购实物地址key", 10 * 60l),
         GROUPS_TRIPS_OUTSIDE_RELATION("groups_trips_outside_relation:key", "额外车位", 30l),
         DISTRIBUTE_RULE_TIME_KEY("DISTRIBUTE_RULE_TIME_KEY", "渠道规格记录时间key", 5 * 60L),
+        CORP_FOLLOW_ACTIVE_CODE_KEY("corp_follow_active_code_key:%s:%s", "活动客服活码key", 60 * 60 * 12L),
         ;
 
         private String name;
@@ -664,7 +665,7 @@ public class RedisService {
             return this.name + env;
         }
         public String getNameFormat(Object ... str) {
-            return String.format(this.getName(), str);
+            return String.format(this.getEnvName(), str);
         }
 
     }

+ 11 - 0
netflix-service/src/main/java/com/cyksj/service/corp/CorpFollowActiveCodeFrontService.java

@@ -0,0 +1,11 @@
+package com.cyksj.service.corp;
+
+/**
+ * 项目名: yhlxj
+ * 文件名: CorpFollowActiveCodeFrontService
+ * 创建者: JavaZou
+ * 创建时间:2023/10/10 14:36
+ */
+public interface CorpFollowActiveCodeFrontService {
+	String getCorpActiveQrCode(Long acId) throws Exception;
+}

+ 6 - 0
netflix-service/src/main/java/com/cyksj/service/corp/CorpFollowDynamicConfigService.java

@@ -2,6 +2,8 @@ package com.cyksj.service.corp;
 
 import com.cyksj.model.request.corp.CorpFollowContact;
 
+import java.util.List;
+
 /*
  *项目名: netflix
  *文件名: CorpFollowDynamicConfigService
@@ -10,4 +12,8 @@ import com.cyksj.model.request.corp.CorpFollowContact;
  */
 public interface CorpFollowDynamicConfigService {
 	String corpFollowContactConfig(CorpFollowContact corpFollowContact) throws Exception;
+
+	String createCorpFollowActiveCode(List<String> followIds, String state, Boolean isOne) throws Exception;
+
+	void updateCorpFollowActiveCode(String codeStr, String markTag) throws Exception;
 }

+ 17 - 0
netflix-service/src/main/java/com/cyksj/service/corp/CorpFollowService.java

@@ -0,0 +1,17 @@
+package com.cyksj.service.corp;
+
+import com.cyksj.model.entity.CorpFollowActiveCode;
+
+/**
+ * 项目名: yhlxj
+ * 文件名: CorpFollowService
+ * 创建者: JavaZou
+ * 创建时间:2023/10/9 16:27
+ */
+public interface CorpFollowService{
+	void postActiveCode(Long adminId, CorpFollowActiveCode activeCode) throws Exception;
+
+	void upActiveCode(CorpFollowActiveCode activeCode) throws Exception;
+
+	void deleteActiveCodeById(Long id);
+}

+ 7 - 3
netflix-service/src/main/java/com/cyksj/service/corp/WxCorpOps.java

@@ -2,11 +2,10 @@ package com.cyksj.service.corp;
 
 import cn.hutool.json.JSONObject;
 import com.cyksj.dto.CorpOauth2UserInfo;
+import com.cyksj.model.request.corp.CorpFollowContact;
 import com.cyksj.model.request.corp.CorpGroupConfig;
 import com.cyksj.model.request.corp.CorpMsgTemplate;
 import com.cyksj.model.response.corp.*;
-import com.cyksj.model.request.corp.CorpFollowContact;
-
 
 import java.util.List;
 
@@ -140,7 +139,12 @@ public interface WxCorpOps {
 	void updateGroupsConfig(CorpGroupConfig config) throws Exception;
 
 	/**
-	 * 联系客配置
+	 * 联系客配置
 	 */
 	String corpFollowContactConfig(CorpFollowContact corpFollowContact) throws Exception;
+
+	/**
+	 * 更新客服配置
+	 */
+	void updateCorpFollowContactConfig(CorpFollowContact corpFollowContact) throws Exception;
 }

+ 103 - 25
netflix-service/src/main/java/com/cyksj/service/corp/impl/CorpEventActionServiceImpl.java

@@ -4,6 +4,7 @@ import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.date.DateTime;
 import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.lang.Validator;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONObject;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -83,6 +84,8 @@ public class CorpEventActionServiceImpl implements CorpEventActionService {
 
 	private final CouponCommonService couponCommonService;
 
+	private final CorpFollowActiveCodeMapper corpFollowActiveCodeMapper;
+
 	private static final String DEFAULT_MSG = "您好,欢迎加入银河录像局";
 
 	/**
@@ -105,7 +108,7 @@ public class CorpEventActionServiceImpl implements CorpEventActionService {
 		}
 
 
-		THREAD_POOL.execute(()->{
+		THREAD_POOL.execute(() -> {
 			//2.客户添加成员信息
 			List<FollowedUser> followedUsers = externalContact.getFollowedUsers();
 			//保存企微用户
@@ -313,7 +316,7 @@ public class CorpEventActionServiceImpl implements CorpEventActionService {
 				.ifPresent(corpUser -> {
 					if (corpUser.getUserId() == 0) {
 						Optional.ofNullable(userMapper.selectOne(Wrappers.lambdaQuery(User.class)
-								.eq(User::getUnionid, corpUser.getUnionid()).last("limit 1")))
+										.eq(User::getUnionid, corpUser.getUnionid()).last("limit 1")))
 								.ifPresent(user -> {
 									corpUser.setUserId(user.getId());
 									corpUserMapper.updateById(corpUser);
@@ -414,8 +417,8 @@ public class CorpEventActionServiceImpl implements CorpEventActionService {
 	public CorpUser saveOrUpdateCorpUser(CorpXmlMessage message, ExternalContact corpUserInfo, List<FollowedUser> followedUsers) {
 		//查询客户是否存在
 		CorpUser corpUser = Optional.ofNullable(
-				corpUserMapper.selectOne(Wrappers.lambdaQuery(CorpUser.class)
-						.eq(CorpUser::getUnionid, corpUserInfo.getUnionId())))
+						corpUserMapper.selectOne(Wrappers.lambdaQuery(CorpUser.class)
+								.eq(CorpUser::getUnionid, corpUserInfo.getUnionId())))
 				.orElseGet(() -> new CorpUser()
 						.setUnionid(corpUserInfo.getUnionId()));
 
@@ -423,32 +426,42 @@ public class CorpEventActionServiceImpl implements CorpEventActionService {
 		corpUser.setGender(corpUserInfo.getGender());
 		corpUser.setAvatar(corpUserInfo.getAvatar());
 		corpUser.setType(corpUserInfo.getType());
-		//扫码来源
-		corpUser.setState(message.getState());
-		//打标签
-		if (StrUtil.equals(message.getState(), Constant.DY_CS_CODE_KEY) && CollUtil.isNotEmpty(followedUsers)) {
-			//查询对应标签是否存在
-			CorpTag corpTag = corpTagMapper.selectOne(Wrappers.lambdaQuery(CorpTag.class)
-					.eq(CorpTag::getName, Constant.DY_CS_CODE_KEY).last("limit 1"));
-			if (corpTag == null) {
+		String state = message.getState();
+		CorpFollowActiveCode corpFollowActiveCode = null;
+		if (StrUtil.isNotBlank(state) && corpUser.getId() == null && state.contains(Constant.AC_CODE_KEY)) {
+			//查找对应的活动活码 记录
+			String acIdStr = StrUtil.subAfter(state, StrUtil.C_COLON, true);
+			if (Validator.isNumber(acIdStr)) {
+				Long acId = Long.parseLong(acIdStr);
+				corpFollowActiveCode = corpFollowActiveCodeMapper.selectById(acId);
+				if (corpFollowActiveCode != null) {
+					corpUser.setAcId(acId);
+					String welcomeStr = corpFollowActiveCode.getWelcomeStr();
+					//发送对应欢迎语
+					if (StrUtil.isNotBlank(corpFollowActiveCode.getWelcomeStr())) {
+						THREAD_POOL.execute(() -> {
+							sendCorpFollowActiveCode(message, welcomeStr);
+						});
+					}
+				}
 				try {
-					corpTag = addCorpTag(Constant.DY_CS_CODE_KEY, Constant.DY_CS_CODE_KEY, message.getToUserName());
+					List<CorpFollow> corpFollows = Jsons.parseList(corpFollowActiveCode.getFollowStr(), CorpFollow.class);
+					List<String> followIds = corpFollows.stream().map(CorpFollow::getUserid).collect(Collectors.toList());
+					//对客户进行标记
+					markAddUserTag(corpFollowActiveCode.getMarkTag(), message.getToUserName(), corpUserInfo.getExternalUserId(), followIds, followedUsers);
 				} catch (Exception e) {
 				}
-			}
-			if (corpTag != null) {
-				List<String> dy_tag = List.of(corpTag.getTagId());
-				followedUsers.forEach(follow -> {
-					if (!Constant.DY_CUS.contains(follow.getUserId())) {
-						return;
-					}
-					try {
-						wxCorpOps.markTag(corpUserInfo.getExternalUserId(), follow.getUserId(), dy_tag.toArray(new String[dy_tag.size()]), null);
-					} catch (Exception e) {
-					}
-				});
+
 			}
 		}
+		//扫码来源
+		if (corpUser.getId() == null) {
+			corpUser.setState(state);
+		}
+		//打标签 抖音渠道活码 已有
+		if (StrUtil.equals(message.getState(), Constant.DY_CS_CODE_KEY) && CollUtil.isNotEmpty(followedUsers)) {
+			markAddUserTag(Constant.DY_CS_CODE_KEY, message.getToUserName(), corpUserInfo.getExternalUserId(), Constant.DY_CUS, followedUsers);
+		}
 
 		//新增修改客户信息
 		if (corpUser.getId() == null) {
@@ -465,6 +478,11 @@ public class CorpEventActionServiceImpl implements CorpEventActionService {
 				}
 				//添加固定客服发放优惠券
 				isJoinFixedCustomer(user.getId(), message.getUserId());
+
+				//活码 优惠券发送
+				if (corpFollowActiveCode != null && corpFollowActiveCode.getCouponId() > 0) {
+					couponCommonService.sendCouponToUser(user.getId(), corpFollowActiveCode.getCouponId(), 0l, 0l, 0l, CouponUser.Channel.corp_active_code);
+				}
 			}
 			corpUserMapper.insert(corpUser);
 		} else {
@@ -500,4 +518,64 @@ public class CorpEventActionServiceImpl implements CorpEventActionService {
 		}
 		return null;
 	}
+
+	/**
+	 * 发送添加活动活码客服 发送欢迎语
+	 */
+	public void sendCorpFollowActiveCode(CorpXmlMessage message, String welcomeStr) {
+		try {
+			String welcomeCode = message.getWelcomeCode();
+			String followId = message.getUserId();
+			//新用户欢迎语
+			JSONObject params = Jsons.parseObject(welcomeStr, JSONObject.class);
+			params.putOpt("welcome_code", welcomeCode);
+			HttpResponse<byte[]> res = null;
+			res = J11HttpC.custom()
+					.ofPost()
+					.url(String.format("https://qyapi.weixin.qq.com/cgi-bin/externalcontact/send_welcome_msg?access_token=%s", wxCorpOps.getAccessToken(message.getToUserName())))
+					.body(HttpRequest.BodyPublishers.ofString(params.toString(), IoKit.Charsets.UTF_8.getCharset()))
+					.headers(J11HttpC.ReqType.raw_json)
+					.send(HttpResponse.BodyHandlers.ofByteArray());
+			JSONObject jsonObject = Jsons.parseObject(res.body(), JSONObject.class);
+			if (jsonObject.getInt("errcode") == 0) {
+				log.info("{}成员,发送欢迎语至客户{}成功", followId, message.getExternalUserId());
+				return;
+			}
+			log.info("{}成员,发送欢迎语至客户{}失败,msg:{}", followId, message.getExternalUserId(), jsonObject.get("errmsg"));
+		} catch (Exception e) {
+			log.info("发送欢迎语过程异常,{}", e);
+		}
+	}
+
+	/**
+	 * 创建标签 并标记客户
+	 */
+	public void markAddUserTag(String tagName, String corpId, String externalUserId, List<String> relateFollowIds, List<FollowedUser> followedUsers) {
+		//对客户进行标记
+		//查询对应标签是否存在
+		CorpTag corpTag = corpTagMapper.selectOne(Wrappers.lambdaQuery(CorpTag.class)
+				.eq(CorpTag::getName, tagName).last("limit 1"));
+		if (corpTag == null) {
+			try {
+				corpTag = addCorpTag(tagName, tagName, corpId);
+			} catch (Exception e) {
+			}
+		}
+		if (corpTag != null) {
+			List<String> tag = List.of(corpTag.getTagId());
+			try {
+				followedUsers.forEach(follow -> {
+					if (!relateFollowIds.contains(follow.getUserId())) {
+						return;
+					}
+					try {
+						wxCorpOps.markTag(externalUserId, follow.getUserId(), tag.toArray(new String[tag.size()]), null);
+					} catch (Exception e) {
+					}
+				});
+			} catch (Exception e) {
+			}
+
+		}
+	}
 }

+ 101 - 0
netflix-service/src/main/java/com/cyksj/service/corp/impl/CorpFollowActiveCodeFrontServiceImpl.java

@@ -0,0 +1,101 @@
+package com.cyksj.service.corp.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.common.util.Jsons;
+import com.cyksj.mapper.corp.CorpFollowActiveCodeClickRecordMapper;
+import com.cyksj.mapper.corp.CorpFollowActiveCodeMapper;
+import com.cyksj.mapper.corp.CorpFollowDutyRelateMapper;
+import com.cyksj.model.dto.CorpFollowActiveCodeDto;
+import com.cyksj.model.entity.CorpFollow;
+import com.cyksj.model.entity.CorpFollowActiveCode;
+import com.cyksj.model.entity.CorpFollowActiveCodeClickRecord;
+import com.cyksj.model.entity.CorpFollowDutyRelate;
+import com.cyksj.redis.RedisService;
+import com.cyksj.service.corp.CorpFollowActiveCodeFrontService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 项目名: yhlxj
+ * 文件名: CorpFollowActiveCodeFrontServiceImpl
+ * 创建者: JavaZou
+ * 创建时间:2023/10/10 14:37
+ */
+@Service
+@RequiredArgsConstructor
+public class CorpFollowActiveCodeFrontServiceImpl implements CorpFollowActiveCodeFrontService {
+
+	private final CorpFollowActiveCodeMapper corpFollowActiveCodeMapper;
+
+	private final CorpFollowDutyRelateMapper corpFollowDutyRelateMapper;
+
+	private final CorpFollowActiveCodeClickRecordMapper corpFollowActiveCodeClickRecordMapper;
+
+	private final RedisService redisService;
+
+	@Override
+	public String getCorpActiveQrCode(Long acId) throws Exception {
+		CorpFollowActiveCode corpFollowActiveCode = corpFollowActiveCodeMapper.selectById(acId);
+		if (corpFollowActiveCode == null) {
+			return null;
+		}
+		CorpFollowActiveCodeClickRecord clickRecord = new CorpFollowActiveCodeClickRecord();
+		clickRecord.setAcId(acId);
+		corpFollowActiveCodeClickRecordMapper.insert(clickRecord);
+
+		int hour = DateUtil.thisHour(true);
+		String activeCodeKey = RedisService.key.CORP_FOLLOW_ACTIVE_CODE_KEY.getNameFormat(acId, hour);
+		String qrCode = redisService.getStr(activeCodeKey);
+		if (StrUtil.isNotBlank(qrCode)) {
+			return qrCode;
+		}
+		String followStr = corpFollowActiveCode.getFollowStr();
+		List<CorpFollow> corpFollows = Jsons.parseList(followStr, CorpFollow.class);
+		List<String> followIds = corpFollows.stream().map(CorpFollow::getUserid).collect(Collectors.toList());
+		String codeStr = corpFollowActiveCode.getCodeStr();
+		List<CorpFollowActiveCodeDto> activeCodeDtos = Jsons.parseList(codeStr, CorpFollowActiveCodeDto.class);
+		//值班
+		if (corpFollowActiveCode.getIsDuty()) {
+			List<CorpFollowDutyRelate> corpFollowDutyRelates = corpFollowDutyRelateMapper.selectList(Wrappers.lambdaQuery(CorpFollowDutyRelate.class)
+					.ge(CorpFollowDutyRelate::getSt, hour)
+					.le(CorpFollowDutyRelate::getEt, hour)
+					.in(CorpFollowDutyRelate::getFollowId, followIds));
+			if (corpFollowDutyRelates.size() > 0) {
+				qrCode = getActiveQrCode(corpFollowDutyRelates, activeCodeDtos);
+				redisService.set(activeCodeKey, qrCode, RedisService.key.CORP_FOLLOW_ACTIVE_CODE_KEY.getTimeout());
+				return qrCode;
+			}
+			corpFollowDutyRelates = corpFollowDutyRelateMapper.selectCorpFollowRelate(followIds);
+			qrCode = getActiveQrCode(corpFollowDutyRelates, activeCodeDtos);
+			redisService.set(activeCodeKey, qrCode, RedisService.key.CORP_FOLLOW_ACTIVE_CODE_KEY.getTimeout());
+			return qrCode;
+		}
+		if (CollUtil.isEmpty(activeCodeDtos)) {
+			return null;
+		}
+		qrCode = activeCodeDtos.get(0).getQrCode();
+		redisService.set(activeCodeKey, qrCode, RedisService.key.CORP_FOLLOW_ACTIVE_CODE_KEY.getTimeout());
+		return qrCode;
+	}
+
+
+	public String getActiveQrCode(List<CorpFollowDutyRelate> corpFollowDutyRelates, List<CorpFollowActiveCodeDto> activeCodeDtos) {
+		Integer randNum = 0;
+		if (corpFollowDutyRelates.size() > 1) {
+			randNum = RandomUtil.randomInt(corpFollowDutyRelates.size());
+		}
+		for (CorpFollowActiveCodeDto activeCodeDto : activeCodeDtos) {
+			if (activeCodeDto.getFollowIds().contains(corpFollowDutyRelates.get(randNum).getFollowId())) {
+				return activeCodeDto.getQrCode();
+			}
+		}
+		return null;
+	}
+}

+ 71 - 0
netflix-service/src/main/java/com/cyksj/service/corp/impl/CorpFollowDynamicConfigServiceImpl.java

@@ -1,9 +1,15 @@
 package com.cyksj.service.corp.impl;
 
+import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONObject;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.common.exception.BusinessRuntimeException;
 import com.cyksj.common.util.Jsons;
 import com.cyksj.mapper.corp.CorpFollowConfigMapper;
+import com.cyksj.mapper.corp.CorpFollowDutyRelateMapper;
+import com.cyksj.model.dto.CorpFollowActiveCodeDto;
 import com.cyksj.model.entity.CorpFollowConfig;
+import com.cyksj.model.entity.CorpFollowDutyRelate;
 import com.cyksj.model.request.corp.CorpFollowContact;
 import com.cyksj.service.corp.CorpFollowDynamicConfigService;
 import com.cyksj.service.corp.WxCorpOps;
@@ -11,7 +17,10 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
+import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 /*
  *项目名: netflix
@@ -27,6 +36,8 @@ public class CorpFollowDynamicConfigServiceImpl implements CorpFollowDynamicConf
 
 	private final CorpFollowConfigMapper corpFollowConfigMapper;
 
+	private final CorpFollowDutyRelateMapper corpFollowDutyRelateMapper;
+
 	@Override
 	public String corpFollowContactConfig(CorpFollowContact corpFollowContact) throws Exception {
 		String body = wxCorpOps.corpFollowContactConfig(corpFollowContact);
@@ -43,4 +54,64 @@ public class CorpFollowDynamicConfigServiceImpl implements CorpFollowDynamicConf
 		});
 		return body;
 	}
+
+	@Override
+	public String createCorpFollowActiveCode(List<String> followIds, String state, Boolean isOne) throws Exception {
+		List<CorpFollowActiveCodeDto> codeList = new ArrayList<>();
+		if (isOne) {
+			Integer type = 2;
+			if (followIds.size() == 1) {
+				type = 1;
+			}
+			String serviceNames = corpFollowDutyRelateMapper.selectList(Wrappers.lambdaQuery(CorpFollowDutyRelate.class)
+					.in(CorpFollowDutyRelate::getFollowId, followIds)).stream().map(CorpFollowDutyRelate::getServiceName).collect(Collectors.joining(","));
+			if (StrUtil.isEmpty(serviceNames)) {
+				throw BusinessRuntimeException.getInstance("存在被删除的客服,请刷新页面重试");
+			}
+			CorpFollowActiveCodeDto corpFollowActiveCodeDto = createCorpFollowActiveCode(followIds, state, type, serviceNames);
+			codeList.add(corpFollowActiveCodeDto);
+		} else {
+			Map<String, List<CorpFollowDutyRelate>> collect = corpFollowDutyRelateMapper.selectList(Wrappers.lambdaQuery(CorpFollowDutyRelate.class)
+					.in(CorpFollowDutyRelate::getFollowId, followIds)).stream().collect(Collectors.groupingBy(CorpFollowDutyRelate::getFollowId));
+			if (collect.keySet().size() != followIds.size()) {
+				throw BusinessRuntimeException.getInstance("存在被删除的客服,请刷新页面重试");
+			}
+			for (String followId : followIds) {
+				CorpFollowActiveCodeDto corpFollowActiveCodeDto = createCorpFollowActiveCode(List.of(followId), state, 1, collect.get(followId).get(0).getServiceName());
+				codeList.add(corpFollowActiveCodeDto);
+			}
+		}
+		return Jsons.toJson(codeList);
+	}
+
+	@Override
+	public void updateCorpFollowActiveCode(String codeStr, String markTag) throws Exception {
+		List<CorpFollowActiveCodeDto> activeCodeDtos = Jsons.parseList(codeStr, CorpFollowActiveCodeDto.class);
+		for (CorpFollowActiveCodeDto activeCode : activeCodeDtos) {
+			String configId = activeCode.getConfigId();
+			CorpFollowContact c = new CorpFollowContact();
+			c.setConfigId(configId);
+			c.setState(markTag);
+			wxCorpOps.updateCorpFollowContactConfig(c);
+		}
+	}
+
+	public CorpFollowActiveCodeDto createCorpFollowActiveCode(List<String> followIds, String state, Integer type, String serviceName) throws Exception {
+		CorpFollowContact c = new CorpFollowContact();
+		c.setType(type);
+		c.setScene(2);
+		c.setUser(followIds);
+		c.setState(state);
+		c.setRemark("客服活动活码");
+		String body = wxCorpOps.corpFollowContactConfig(c);
+		JSONObject jsonObject = Jsons.parseObject(body, JSONObject.class);
+		String qrCode = jsonObject.getStr("qr_code");
+		String configId = jsonObject.getStr("config_id");
+		CorpFollowActiveCodeDto corpFollowActiveCodeDto = new CorpFollowActiveCodeDto();
+		corpFollowActiveCodeDto.setFollowIds(followIds.toString());
+		corpFollowActiveCodeDto.setServiceNames(serviceName);
+		corpFollowActiveCodeDto.setQrCode(qrCode);
+		corpFollowActiveCodeDto.setConfigId(configId);
+		return corpFollowActiveCodeDto;
+	}
 }

+ 141 - 0
netflix-service/src/main/java/com/cyksj/service/corp/impl/CorpFollowServiceImpl.java

@@ -0,0 +1,141 @@
+package com.cyksj.service.corp.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.common.constant.Constant;
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.util.Jsons;
+import com.cyksj.mapper.corp.CorpFollowActiveCodeMapper;
+import com.cyksj.mapper.corp.CorpFollowDutyRelateMapper;
+import com.cyksj.model.entity.CmsUser;
+import com.cyksj.model.entity.CorpFollow;
+import com.cyksj.model.entity.CorpFollowActiveCode;
+import com.cyksj.service.corp.CorpFollowDynamicConfigService;
+import com.cyksj.service.corp.CorpFollowService;
+import com.cyksj.service.mange.cms.CmsUserManager;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * 项目名: yhlxj
+ * 文件名: CorpFollowServiceImpl
+ * 创建者: JavaZou
+ * 创建时间:2023/10/9 16:27
+ */
+@Service
+@RequiredArgsConstructor
+public class CorpFollowServiceImpl implements CorpFollowService {
+	private final CorpFollowActiveCodeMapper corpFollowActiveCodeMapper;
+
+	private final CmsUserManager cmsUserManager;
+
+	private final CorpFollowDynamicConfigService corpFollowDynamicConfigService;
+
+	private final CorpFollowDutyRelateMapper corpFollowDutyRelateMapper;
+
+	@Override
+	@Transactional(rollbackFor = Throwable.class)
+	public void postActiveCode(Long adminId, CorpFollowActiveCode activeCode) throws Exception {
+		CmsUser cmsUser = cmsUserManager.getUser(adminId);
+		if (cmsUser != null) {
+			activeCode.setOperator(cmsUser.getNickname());
+		}
+		Boolean isDuty = Optional.ofNullable(activeCode.getIsDuty()).orElse(false);
+		String followStr = activeCode.getFollowStr();
+		if (StrUtil.isEmpty(followStr)) {
+			throw BusinessRuntimeException.getInstance("请选择对应客服");
+		}
+		List<CorpFollow> corpFollows = Jsons.parseList(followStr, CorpFollow.class);
+		List<String> followIds = corpFollows.stream().map(CorpFollow::getUserid).collect(Collectors.toList());
+		activeCode.setMarkTag(activeCode.getMarkTag().trim());
+		//是否值班
+		String codeStr;
+		corpFollowActiveCodeMapper.insert(activeCode);
+		if (!isDuty) {
+			//生成一个活码 一个客服或多个客服
+			codeStr = corpFollowDynamicConfigService.createCorpFollowActiveCode(followIds, Constant.AC_CODE_KEY + activeCode.getId(), true);
+		} else {
+			codeStr = corpFollowDynamicConfigService.createCorpFollowActiveCode(followIds, Constant.AC_CODE_KEY + activeCode.getId(), false);
+		}
+		activeCode.setCodeStr(codeStr);
+		corpFollowActiveCodeMapper.updateById(activeCode);
+	}
+
+	@Override
+	public void upActiveCode(CorpFollowActiveCode activeCode) throws Exception {
+		Long activeCodeId = activeCode.getId();
+		if (activeCodeId == null) {
+			throw BusinessRuntimeException.getInstance("请编辑对应的数据");
+		}
+		CorpFollowActiveCode dbActiveCode = corpFollowActiveCodeMapper.selectById(activeCodeId);
+		if (dbActiveCode == null || !dbActiveCode.getDeleted()) {
+			throw BusinessRuntimeException.getInstance("对应的数据已不存在");
+		}
+		String db_followStr = dbActiveCode.getFollowStr();
+		String up_followStr = activeCode.getFollowStr();
+		if (StrUtil.isEmpty(up_followStr)) {
+			throw BusinessRuntimeException.getInstance("请选择对应客服");
+		}
+		List<CorpFollow> up_corpFollows = Jsons.parseList(up_followStr, CorpFollow.class);
+		if (CollUtil.isEmpty(up_corpFollows)) {
+			throw BusinessRuntimeException.getInstance("请选择对应客服");
+		}
+		if (StrUtil.isEmpty(activeCode.getMarkTag())) {
+			throw BusinessRuntimeException.getInstance("请输入标签");
+		}
+		List<CorpFollow> db_corpFollows = Jsons.parseList(db_followStr, CorpFollow.class);
+		Boolean isReCreateAcCode = false;
+		Boolean isUpdateAcCode = false;
+		List<String> followIds = null;
+		if (db_corpFollows.size() != up_corpFollows.size()) {
+			isReCreateAcCode = true;
+			followIds = db_corpFollows.stream().map(CorpFollow::getUserid).collect(Collectors.toList());
+		}
+		if (!isReCreateAcCode) {
+			Map<String, List<CorpFollow>> db_follow_collect = db_corpFollows.stream().collect(Collectors.groupingBy(CorpFollow::getUserid));
+			for (CorpFollow upCorpFollow : up_corpFollows) {
+				List<CorpFollow> corpFollows = db_follow_collect.get(upCorpFollow.getUserid());
+				if (CollUtil.isEmpty(corpFollows)) {
+					isReCreateAcCode = true;
+					break;
+				}
+			}
+		}
+		Boolean db_isDuty = dbActiveCode.getIsDuty();
+		Boolean up_isDuty = activeCode.getIsDuty();
+		if (db_isDuty != up_isDuty) {
+			isReCreateAcCode = true;
+		}
+		activeCode.setMarkTag(activeCode.getMarkTag().trim());
+		if (!dbActiveCode.getMarkTag().equals(activeCode.getMarkTag())) {
+			isUpdateAcCode = true;
+		}
+
+		//重新构建活码配置
+		if (isReCreateAcCode) {
+			String code_str = corpFollowDynamicConfigService.createCorpFollowActiveCode(followIds, Constant.AC_CODE_KEY + activeCode.getId(), up_isDuty);
+			activeCode.setCodeStr(code_str);
+		} else if (isUpdateAcCode) {
+			corpFollowDynamicConfigService.updateCorpFollowActiveCode(activeCode.getCodeStr(), Constant.AC_CODE_KEY + activeCode.getId());
+		}
+		if (activeCode.getCouponId() == null) {
+			activeCode.setCouponId(0l);
+		}
+		corpFollowActiveCodeMapper.updateById(activeCode);
+	}
+
+	@Override
+	public void deleteActiveCodeById(Long id) {
+		corpFollowActiveCodeMapper.update(null, Wrappers.lambdaUpdate(CorpFollowActiveCode.class)
+				.eq(CorpFollowActiveCode::getId, id)
+				.set(CorpFollowActiveCode::getDeleted, false)
+				.eq(CorpFollowActiveCode::getDeleted, true));
+	}
+}

+ 19 - 2
netflix-service/src/main/java/com/cyksj/service/corp/impl/WxCorpOpsImpl.java

@@ -15,6 +15,7 @@ import com.cyksj.config.corp.YHLXWxCorpConfig;
 import com.cyksj.dto.CorpOauth2UserInfo;
 import com.cyksj.dto.RedisKey;
 import com.cyksj.model.entity.WxCorpApp;
+import com.cyksj.model.request.corp.CorpFollowContact;
 import com.cyksj.model.request.corp.CorpGroupConfig;
 import com.cyksj.model.request.corp.CorpMsgTemplate;
 import com.cyksj.model.response.OffiaccountJsTicket;
@@ -22,8 +23,6 @@ import com.cyksj.model.response.UserExternalContactList;
 import com.cyksj.model.response.UserWithExternalPermission;
 import com.cyksj.model.response.WechatAccessToken;
 import com.cyksj.model.response.corp.*;
-import com.cyksj.model.request.corp.CorpFollowContact;
-import com.cyksj.model.response.*;
 import com.cyksj.redis.RedisService;
 import com.cyksj.service.corp.WxCorpOps;
 import lombok.extern.slf4j.Slf4j;
@@ -315,6 +314,24 @@ public class WxCorpOpsImpl implements WxCorpOps {
 		return body;
 	}
 
+	@Override
+	public void updateCorpFollowContactConfig(CorpFollowContact corpFollowContact) throws Exception {
+		//配置
+		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/update_contact_way?access_token=" + getAccessToken(null);
+		HttpResponse<byte[]> res =
+				J11HttpC.custom()
+						.ofPost()
+						.url(url)
+						.cacheURI()
+						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(corpFollowContact), IoKit.Charsets.UTF_8.getCharset()))
+						.send(HttpResponse.BodyHandlers.ofByteArray());
+		String body = IoKit.toString(res.body());
+		JSONObject jsonObject = Jsons.parseObject(body, JSONObject.class);
+		if (jsonObject.getInt("errcode") != 0) {
+			throw BusinessRuntimeException.getInstance("更新客服活码失败");
+		}
+	}
+
 	@Override
 	public CorpUserExternalTagGroupInfo addCorpTag(CorpUserExternalTagGroupInfo tagGroup, String corpId) throws Exception {
 		corpId = Optional.ofNullable(corpId).orElse(yhlXWxCorpConfig.getCorpId());

+ 28 - 1
netflix-service/src/main/java/com/cyksj/service/user/impl/UserServiceImpl.java

@@ -18,6 +18,8 @@ import com.cyksj.common.util.StringUtil;
 import com.cyksj.config.wxlogin.WxOpenPlatYHLXLoginConfig;
 import com.cyksj.mapper.*;
 import com.cyksj.mapper.channel.ChannelPopularizeMapper;
+import com.cyksj.mapper.corp.CorpFollowActiveCodeMapper;
+import com.cyksj.mapper.corp.CorpUserMapper;
 import com.cyksj.mapper.manage.cps.UserDistributePopularizeMapper;
 import com.cyksj.mapper.market.task.UserBindDetailMapper;
 import com.cyksj.model.dto.*;
@@ -99,6 +101,10 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
 
 	private final YhShopDistributeService yhShopDistributeService;
 
+	private final CorpUserMapper corpUserMapper;
+
+	private final CorpFollowActiveCodeMapper corpFollowActiveCodeMapper;
+
 	private static final GlobalThreadPoolTaskExecutor TASK_POOL = GlobalThreadPoolTaskExecutor.getInstance();
 
 	@Override
@@ -149,6 +155,8 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
 					sendNewUserWelfare(fUid);
 				} catch (Exception e) {
 				}
+				//去关联对应企业微信用户
+				relateCorpUserAndSengWelfare(fUid, unionid);
 			});
 		} else {
 			if (StrUtil.isEmpty(user.getRegisterEnv())) {
@@ -196,7 +204,7 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
 		return user;
 	}
 
-    @Override
+	@Override
     public UserWhoami whoami(long userId) {
 	    User user = this.getById(userId);
 	    Assert.notNull(user, "业务处理失败,请联系客服");
@@ -659,4 +667,23 @@ public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements Us
 			couponIds.forEach(couponId -> couponCommonService.sendCouponToUser(userId, couponId, null, null, null, CouponUser.Channel.new_welfare));
 		}
 	}
+
+	/**
+	 * 关联企业微信用户 并发放福利
+	 */
+	private void relateCorpUserAndSengWelfare(Long fUid, String unionid) {
+		CorpUser corpUser = corpUserMapper.selectOne(Wrappers.lambdaQuery(CorpUser.class)
+				.eq(CorpUser::getUnionid, unionid).last("limit 1"));
+		if (corpUser != null) {
+			corpUser.setUserId(fUid);
+			corpUserMapper.updateById(corpUser);
+			//活码福利
+			if (corpUser.getAcId() != null && corpUser.getAcId() > 0) {
+				CorpFollowActiveCode corpFollowActiveCode = corpFollowActiveCodeMapper.selectById(corpUser.getAcId());
+				if (corpFollowActiveCode != null && corpFollowActiveCode.getCouponId() > 0) {
+					couponCommonService.sendCouponToUser(fUid, corpFollowActiveCode.getCouponId(), 0l, 0l, 0l, CouponUser.Channel.corp_active_code);
+				}
+			}
+		}
+	}
 }

+ 32 - 0
netflix-web/src/main/java/com/cyksj/web/controller/landing/CorpFollowActiveCodeController.java

@@ -0,0 +1,32 @@
+package com.cyksj.web.controller.landing;
+
+import com.cyksj.dto.Result;
+import com.cyksj.enums.GatewayResponse;
+import com.cyksj.service.corp.CorpFollowActiveCodeFrontService;
+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;
+
+/**
+ * 项目名: yhlxj
+ * 文件名: CorpFollowActiveCodeController
+ * 创建者: JavaZou
+ * 创建时间:2023/10/10 14:31
+ */
+@RestController
+@RequestMapping("/applets/landing")
+@RequiredArgsConstructor
+public class CorpFollowActiveCodeController {
+	private final CorpFollowActiveCodeFrontService corpFollowActiveCodeFrontService;
+
+	/**
+	 * 活动活码落地页 获取对应客服二维码
+	 */
+	@GetMapping("/get/corp/active/qrCode/{id}")
+	public Result<String> getCorpActiveQrCode(@PathVariable Long id) throws Exception {
+		String qrCode = corpFollowActiveCodeFrontService.getCorpActiveQrCode(id);
+		return GatewayResponse.SUCCESS.newBuilder().toResult(qrCode);
+	}
+}

+ 0 - 34
netflix-web/src/main/java/com/cyksj/web/controller/manage/corp/CorpFollowConfigController.java

@@ -1,34 +0,0 @@
-package com.cyksj.web.controller.manage.corp;
-
-import com.cyksj.dto.Result;
-import com.cyksj.enums.GatewayResponse;
-import com.cyksj.model.request.corp.CorpFollowContact;
-import com.cyksj.service.corp.CorpFollowDynamicConfigService;
-import lombok.RequiredArgsConstructor;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-/*
- *项目名: netflix
- *文件名: CorpFollowConfigController
- *创建者: JavaZou
- *创建时间:2023/7/6 18:36
- */
-@RestController
-@RequestMapping("/manage/corp")
-@RequiredArgsConstructor
-public class CorpFollowConfigController {
-
-	private final CorpFollowDynamicConfigService corpFollowDynamicConfigService;
-
-	/**
-	 * 生成动态配置
-	 */
-	@PostMapping("/post/config")
-	public Result<String> getCorpFollowConcat(@RequestBody CorpFollowContact corpFollowContact) throws Exception {
-		String body = corpFollowDynamicConfigService.corpFollowContactConfig(corpFollowContact);
-		return GatewayResponse.SUCCESS.newBuilder().toResult(body);
-	}
-}

+ 159 - 0
netflix-web/src/main/java/com/cyksj/web/controller/manage/corp/CorpFollowController.java

@@ -0,0 +1,159 @@
+package com.cyksj.web.controller.manage.corp;
+
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.dto.Result;
+import com.cyksj.enums.GatewayResponse;
+import com.cyksj.mapper.OrderDonMapper;
+import com.cyksj.mapper.corp.CorpUserMapper;
+import com.cyksj.mapper.manage.coupon.CouponMapper;
+import com.cyksj.model.entity.CorpFollowActiveCode;
+import com.cyksj.model.entity.CorpUser;
+import com.cyksj.model.request.corp.CorpFollowContact;
+import com.cyksj.model.views.CorpActiveCodeGoodsOrderView;
+import com.cyksj.model.views.CorpActiveCodeOrderView;
+import com.cyksj.model.views.CorpFollowActiveCodeView;
+import com.cyksj.service.corp.CorpFollowDynamicConfigService;
+import com.cyksj.service.corp.CorpFollowService;
+import com.ejlchina.searcher.BeanSearcher;
+import com.ejlchina.searcher.SearchResult;
+import com.ejlchina.searcher.util.MapBuilder;
+import com.ejlchina.searcher.util.MapUtils;
+import lombok.RequiredArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Optional;
+
+/*
+ *项目名: netflix
+ *文件名: CorpFollowController
+ *创建者: JavaZou
+ *创建时间:2023/7/6 18:36
+ */
+@RestController
+@RequestMapping("/manage/corp")
+@RequiredArgsConstructor
+public class CorpFollowController {
+
+	private final CorpFollowDynamicConfigService corpFollowDynamicConfigService;
+
+	private final CorpFollowService corpFollowService;
+
+	private final BeanSearcher beanSearcher;
+
+	private final CouponMapper couponMapper;
+
+	private final OrderDonMapper orderDonMapper;
+
+	private final CorpUserMapper corpUserMapper;
+
+	private final HttpServletRequest request;
+
+	/**
+	 * 生成动态配置
+	 */
+	@PostMapping("/post/config")
+	public Result<String> getCorpFollowConcat(@RequestBody CorpFollowContact corpFollowContact) throws Exception {
+		String body = corpFollowDynamicConfigService.corpFollowContactConfig(corpFollowContact);
+		return GatewayResponse.SUCCESS.newBuilder().toResult(body);
+	}
+
+	/**
+	 * 创建活动活码
+	 */
+	@PostMapping("/post/active/code")
+	public Result<String> postActiveCode(@RequestBody CorpFollowActiveCode activeCode) throws Exception {
+		long adminId = StpUtil.getLoginIdAsLong();
+		corpFollowService.postActiveCode(adminId, activeCode);
+		return GatewayResponse.SUCCESS.newBuilder().toResult();
+	}
+
+	/**
+	 * 编辑活动活码
+	 */
+	@PutMapping("/update/active/code")
+	public Result<String> upActiveCode(@RequestBody CorpFollowActiveCode activeCode) throws Exception {
+		corpFollowService.upActiveCode(activeCode);
+		return GatewayResponse.SUCCESS.newBuilder().toResult();
+	}
+
+	/**
+	 * 活动活码列表
+	 */
+	@DeleteMapping("/del/active/code/{id}")
+	public Result<String> getActiveCode(@PathVariable Long id) {
+		corpFollowService.deleteActiveCodeById(id);
+		return GatewayResponse.SUCCESS.newBuilder().toResult();
+	}
+
+	/**
+	 * 活动活码列表
+	 */
+	@GetMapping("/get/active/code")
+	public Result<SearchResult<CorpFollowActiveCodeView>> getActiveCode() {
+		SearchResult<CorpFollowActiveCodeView> search = beanSearcher.search(CorpFollowActiveCodeView.class, MapUtils.flatBuilder(request.getParameterMap()).build());
+		search.getDataList().forEach(data->{
+			if (data.getCouponId() > 0) {
+				Optional.ofNullable(couponMapper.selectById(data.getCouponId())).ifPresent(c -> data.setCouponName(c.getName()));
+			}
+			Integer numOfUser = corpUserMapper.selectCount(Wrappers.lambdaQuery(CorpUser.class)
+					.eq(CorpUser::getAcId, data.getId()));
+			data.setNumOfUser(numOfUser);
+			Integer orderNum = orderDonMapper.selectCountActiveCodeOrderByAcId(data.getId());
+			data.setOrderNum(orderNum);
+		});
+		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
+	}
+
+	/**
+	 * 查看活码人数列表
+	 */
+	@GetMapping("/get/active/code/userList")
+	public Result<SearchResult<CorpUser>> getCorpActiveCodeUser(Long id) {
+		MapBuilder mapBuilder = MapUtils.flatBuilder(request.getParameterMap());
+		if (id != null) {
+			mapBuilder.field(CorpUser::getAcId, id);
+		}
+		SearchResult<CorpUser> search = beanSearcher.search(CorpUser.class, mapBuilder.build());
+		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
+	}
+
+	/**
+	 * 查看活码订单 详情
+	 */
+	@GetMapping("/get/active/code/userList")
+	public Result<SearchResult<CorpActiveCodeOrderView>> getCorpActiveCodeOrderView(Long id) {
+		MapBuilder mapBuilder = MapUtils.flatBuilder(request.getParameterMap());
+		if (id != null) {
+			mapBuilder.put("acId", String.format(" and cu.ac_id = %s", id));
+		}
+		SearchResult<CorpActiveCodeOrderView> search = beanSearcher.search(CorpActiveCodeOrderView.class, mapBuilder.build());
+		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
+	}
+
+	/**
+	 * 活码 平台订单数据
+	 */
+	@GetMapping("/get/active/code/goods/order")
+	public Result<SearchResult<CorpActiveCodeGoodsOrderView>> getCorpActiveCodeGoodsOrder(Long id, String startTime, String endTime) {
+		String timeSql = StringUtils.EMPTY;
+		if (StringUtils.isNotBlank(startTime)) {
+			timeSql += " and o.created_time >= '" + startTime + "'";
+		}
+		if (StringUtils.isNotBlank(endTime)) {
+			timeSql += " and o.created_time <= '" + endTime + "'";
+		}
+		MapBuilder mapBuilder = MapUtils.flatBuilder(request.getParameterMap());
+		if (id != null) {
+			mapBuilder.put("acId", String.format(" and cu.ac_id = %s", id));
+		}
+		if (StrUtil.isNotBlank(timeSql)) {
+			mapBuilder.put("time", timeSql);
+		}
+		SearchResult<CorpActiveCodeGoodsOrderView> search = beanSearcher.search(CorpActiveCodeGoodsOrderView.class, mapBuilder.build());
+		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
+	}
+}