Sfoglia il codice sorgente

企业微信动态标签;群发任务

zoujiajian 3 anni fa
parent
commit
de00f0e8d8
76 ha cambiato i file con 5009 aggiunte e 1214 eliminazioni
  1. 18 0
      netflix-common/src/main/java/com/cyksj/dto/XxlJobResult.java
  2. 171 0
      netflix-common/src/main/java/com/cyksj/xxljob/XxlJobInfo.java
  3. 140 0
      netflix-common/src/main/java/com/cyksj/xxljob/XxlJobUtil.java
  4. 18 0
      netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpMsgBulkTaskMapper.java
  5. 13 0
      netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpMsgBulkTaskRecordMapper.java
  6. 13 0
      netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpMsgBulkTaskTagRelationMapper.java
  7. 13 0
      netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpMsgMapper.java
  8. 10 0
      netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpTagGroupMapper.java
  9. 12 0
      netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpTagMapper.java
  10. 13 0
      netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpTagRuleMapper.java
  11. 13 0
      netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpTagRuleRelationMapper.java
  12. 7 1
      netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpUserFollowRelationMapper.java
  13. 3 0
      netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpUserMapper.java
  14. 12 0
      netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpUserSendRecordMapper.java
  15. 9 0
      netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpUserTagMapper.java
  16. 10 0
      netflix-dao/src/main/java/com/cyksj/mapper/manage/coupon/CouponMapper.java
  17. 20 0
      netflix-dao/src/main/java/com/cyksj/model/dto/CornMetaDto.java
  18. 23 0
      netflix-dao/src/main/java/com/cyksj/model/dto/CorpMsgBulkTaskContent.java
  19. 28 0
      netflix-dao/src/main/java/com/cyksj/model/dto/CorpUserFollowRelationDto.java
  20. 39 0
      netflix-dao/src/main/java/com/cyksj/model/entity/CorpMsg.java
  21. 153 0
      netflix-dao/src/main/java/com/cyksj/model/entity/CorpMsgBulkTask.java
  22. 73 0
      netflix-dao/src/main/java/com/cyksj/model/entity/CorpMsgBulkTaskRecord.java
  23. 18 0
      netflix-dao/src/main/java/com/cyksj/model/entity/CorpMsgBulkTaskTagRelation.java
  24. 31 0
      netflix-dao/src/main/java/com/cyksj/model/entity/CorpTag.java
  25. 1 1
      netflix-dao/src/main/java/com/cyksj/model/entity/CorpTagGroup.java
  26. 29 0
      netflix-dao/src/main/java/com/cyksj/model/entity/CorpTagRule.java
  27. 41 0
      netflix-dao/src/main/java/com/cyksj/model/entity/CorpTagRuleRelation.java
  28. 54 0
      netflix-dao/src/main/java/com/cyksj/model/entity/CorpUserSendRecord.java
  29. 6 5
      netflix-dao/src/main/java/com/cyksj/model/entity/CouponUser.java
  30. 34 0
      netflix-dao/src/main/java/com/cyksj/model/excel/CorpUserTagExcelData.java
  31. 18 0
      netflix-dao/src/main/java/com/cyksj/model/request/CorpDelTagReq.java
  32. 47 0
      netflix-dao/src/main/java/com/cyksj/model/request/CorpMsgBulkTaskReq.java
  33. 114 3
      netflix-dao/src/main/java/com/cyksj/model/request/corp/Attachments.java
  34. 45 0
      netflix-dao/src/main/java/com/cyksj/model/request/corp/CorpMsgTemplate.java
  35. 22 0
      netflix-dao/src/main/java/com/cyksj/model/response/corp/CorpBaseResult.java
  36. 3 1
      netflix-dao/src/main/java/com/cyksj/model/response/corp/CorpExternalContactInfo.java
  37. 17 0
      netflix-dao/src/main/java/com/cyksj/model/response/corp/CorpMediaUploadResult.java
  38. 42 0
      netflix-dao/src/main/java/com/cyksj/model/response/corp/CorpMsgSendResult.java
  39. 28 0
      netflix-dao/src/main/java/com/cyksj/model/response/corp/CorpMsgTemplateAddResult.java
  40. 72 0
      netflix-dao/src/main/java/com/cyksj/model/response/corp/CorpUserExternalTagGroupInfo.java
  41. 1 1
      netflix-dao/src/main/java/com/cyksj/model/response/corp/CorpUserExternalTagGroupList.java
  42. 1 1
      netflix-dao/src/main/java/com/cyksj/model/response/corp/CorpUserInfo.java
  43. 52 0
      netflix-dao/src/main/java/com/cyksj/model/views/CorpMsgBulkTaskSendRecordView.java
  44. 59 0
      netflix-dao/src/main/java/com/cyksj/model/views/CorpTagBackView.java
  45. 32 0
      netflix-dao/src/main/java/com/cyksj/model/views/CorpTagGroupView.java
  46. 24 0
      netflix-dao/src/main/java/com/cyksj/model/views/CorpTagHeadDataView.java
  47. 28 0
      netflix-dao/src/main/java/com/cyksj/model/views/CorpUserView.java
  48. 14 0
      netflix-dao/src/main/resources/mapper/CorpMsgBulkTaskMapper.xml
  49. 58 0
      netflix-dao/src/main/resources/mapper/CorpTagGroupMapper.xml
  50. 56 0
      netflix-dao/src/main/resources/mapper/CorpTagMapper.xml
  51. 21 5
      netflix-dao/src/main/resources/mapper/CorpUserFollowRelationMapper.xml
  52. 64 0
      netflix-dao/src/main/resources/mapper/CorpUserTagMapper.xml
  53. 10 0
      netflix-dao/src/main/resources/mapper/CouponMapper.xml
  54. 3 0
      netflix-service/src/main/java/com/cyksj/redis/RedisService.java
  55. 22 0
      netflix-service/src/main/java/com/cyksj/service/corp/CorpMsgBulkTaskService.java
  56. 11 0
      netflix-service/src/main/java/com/cyksj/service/corp/CorpService.java
  57. 45 0
      netflix-service/src/main/java/com/cyksj/service/corp/CorpTagService.java
  58. 13 0
      netflix-service/src/main/java/com/cyksj/service/corp/CorpUserSendRecordService.java
  59. 55 3
      netflix-service/src/main/java/com/cyksj/service/corp/WxCorpOps.java
  60. 25 2
      netflix-service/src/main/java/com/cyksj/service/corp/impl/CorpEventActionServiceImpl.java
  61. 358 0
      netflix-service/src/main/java/com/cyksj/service/corp/impl/CorpMsgBulkTaskServiceImpl.java
  62. 79 1
      netflix-service/src/main/java/com/cyksj/service/corp/impl/CorpServiceImpl.java
  63. 378 0
      netflix-service/src/main/java/com/cyksj/service/corp/impl/CorpTagServiceImpl.java
  64. 18 0
      netflix-service/src/main/java/com/cyksj/service/corp/impl/CorpUserSendRecordServiceImpl.java
  65. 227 1
      netflix-service/src/main/java/com/cyksj/service/corp/impl/WxCorpOpsImpl.java
  66. 27 0
      netflix-service/src/main/java/com/cyksj/service/mange/statistics/StatisticsDataDayService.java
  67. 371 0
      netflix-service/src/main/java/com/cyksj/service/mange/statistics/impl/StatisticsDataDayServiceImpl.java
  68. 6 5
      netflix-service/src/main/java/com/cyksj/service/mange/statistics/impl/StatisticsDataServiceImpl.java
  69. 110 110
      netflix-service/src/main/java/com/cyksj/task/OrderCustomerHandleScheduler.java
  70. 548 548
      netflix-service/src/main/java/com/cyksj/task/Scheduler.java
  71. 130 130
      netflix-service/src/main/java/com/cyksj/task/SignStatisticsDataScheduler.java
  72. 395 395
      netflix-service/src/main/java/com/cyksj/task/StatisticsDataScheduler.java
  73. 2 1
      netflix-web/src/main/java/com/cyksj/web/controller/manage/CmsUserController.java
  74. 169 0
      netflix-web/src/main/java/com/cyksj/web/controller/manage/corp/CorpMsgBulkTaskController.java
  75. 164 0
      netflix-web/src/main/java/com/cyksj/web/controller/manage/corp/CorpTagController.java
  76. 0 0
      netflix-web/src/main/resources/application-dev.yml

+ 18 - 0
netflix-common/src/main/java/com/cyksj/dto/XxlJobResult.java

@@ -0,0 +1,18 @@
+package com.cyksj.dto;
+
+import lombok.Data;
+
+/**
+ * @Author Huang yuanzhi
+ * @Date 2023/6/13 9:47
+ * @Version 1.0.0
+ **/
+@Data
+public class XxlJobResult {
+
+    private int code;
+
+    private String msg;
+
+    private String content;
+}

+ 171 - 0
netflix-common/src/main/java/com/cyksj/xxljob/XxlJobInfo.java

@@ -0,0 +1,171 @@
+package com.cyksj.xxljob;
+
+
+import cn.hutool.core.net.URLEncoder;
+import com.cyksj.common.exception.BusinessRuntimeException;
+import lombok.Data;
+import lombok.Getter;
+import org.apache.commons.lang3.StringUtils;
+
+import java.nio.charset.Charset;
+
+/**
+ * @ClassName XxlJobInfo
+ * @Description TODO
+ * @Author Huang yuanzhi
+ * @Date 2023/6/12 16:11
+ * @Version 1.0.0
+ **/
+@Data
+public class XxlJobInfo {
+
+	public XxlJobInfo() {
+	}
+
+	public XxlJobInfo(int id, String jobDesc, String jobCron, int jobGroup, String author, String alarmEmail, String executorHandler, String executorParam) {
+		this.id = id;
+		this.jobGroup = jobGroup;
+		this.jobDesc = jobDesc;
+		this.jobCron = jobCron;
+		this.author = author;
+		this.alarmEmail = alarmEmail;
+		this.executorHandler = executorHandler;
+		this.executorParam = executorParam;
+		this.triggerStatus = 1;
+	}
+
+	private int id;
+	/**
+	 * 执行器主键ID
+	 */
+	private int jobGroup = 5;
+	/**
+	 * 备注
+	 */
+	private String jobDesc;
+	/**
+	 * 表达式
+	 */
+	private String jobCron;
+
+	private String glueType = "BEAN";
+	/**
+	 * 负责人
+	 */
+	private String author;
+	/**
+	 * 报警邮件
+	 */
+	private String alarmEmail;
+
+	/**
+	 * 执行器路由策略
+	 */
+	private String executorRouteStrategy = "FIRST";
+	/**
+	 * 执行器,任务Handler名称
+	 */
+	private String executorHandler;
+	/**
+	 * 执行器,任务参数
+	 */
+	private String executorParam;
+	/**
+	 * 阻塞处理策略
+	 */
+	private String executorBlockStrategy = "SERIAL_EXECUTION";
+	/**
+	 * 任务执行超时时间,单位秒
+	 */
+	private int executorTimeout;
+
+	private String glueRemark = "GLUE代码初始化";
+	/**
+	 * 失败重试次数
+	 */
+	private int executorFailRetryCount;
+
+	/**
+	 * 子任务ID,多个逗号分隔
+	 */
+	private String childJobId;
+	/**
+	 * 调度状态:0-停止,1-运行
+	 */
+	private int triggerStatus;
+	/**
+	 * 上次调度时间
+	 */
+	private long triggerLastTime;
+	/**
+	 * 下次调度时间
+	 */
+	private long triggerNextTime;
+
+	//银河执行器 id
+	private static int jobGroups = 10;
+
+
+	/**
+	 * 创建执行器
+	 *
+	 * @param author
+	 * @param jobDesc         任务秒速
+	 * @param corn            表达式
+	 * @param executorHandler executorHandler
+	 * @param executorParam   参数
+	 * @return
+	 */
+	public static XxlJobInfo getInstance(AUTHOR author, String jobDesc, String corn, int jopGroup, String executorHandler, String executorParam) {
+		return new XxlJobInfo(0, jobDesc, URLEncoder.createQuery().encode(corn, Charset.defaultCharset()), jopGroup, author.getName(), author.email, executorHandler, executorParam);
+	}
+
+	public static XxlJobInfo getUpdateInstance(int id, AUTHOR author, String jobDesc, String corn, int jopGroup, String executorHandler, String executorParam) {
+		return new XxlJobInfo(id, jobDesc, URLEncoder.createQuery().encode(corn, Charset.defaultCharset()), jopGroup, author.getName(), author.email, executorHandler, executorParam);
+	}
+
+	public static XxlJobInfo getTaskInstance(int id, String corn, String executorParam) {
+		AUTHOR author = AUTHOR.Zou;
+		if (StringUtils.isBlank(corn)) {
+			throw BusinessRuntimeException.getInstance("任务运行周期不能为空");
+		}
+		return new XxlJobInfo(id, "周期群发任务", URLEncoder.createQuery().encode(corn, Charset.defaultCharset()), jobGroups, author.getName(), author.email, "yh_corp_msg_bulk_task_cycle_job", executorParam);
+	}
+
+	@Getter
+	public enum AUTHOR {
+
+		Zou("zou", "1091496505@qq.com");
+
+		AUTHOR(String name, String email) {
+			this.name = name;
+			this.email = email;
+		}
+
+		private String name;
+
+		private String email;
+	}
+
+	@Override
+	public String toString() {
+		return
+				"?id=" + id +
+						"&jobDesc=" + jobDesc +
+						"&jobCron=" + jobCron +
+						"&jobGroup=" + jobGroup +
+						"&glueType=" + glueType +
+						"&author=" + author +
+						"&alarmEmail=" + alarmEmail +
+						"&executorRouteStrategy=" + executorRouteStrategy +
+						"&executorHandler=" + executorHandler +
+						"&executorParam=" + executorParam +
+						"&executorBlockStrategy=" + executorBlockStrategy +
+						"&executorTimeout=" + executorTimeout +
+						"&glueRemark=" + glueRemark +
+						"&executorFailRetryCount=" + executorFailRetryCount +
+						"&triggerStatus=" + triggerStatus +
+						"&triggerLastTime=" + triggerLastTime +
+						"&triggerNextTime=" + triggerNextTime;
+	}
+}

+ 140 - 0
netflix-common/src/main/java/com/cyksj/xxljob/XxlJobUtil.java

@@ -0,0 +1,140 @@
+package com.cyksj.xxljob;
+
+/**
+ * @ClassName XxlJobUtil
+ * @Description TODO
+ * @Author Huang yuanzhi
+ * @Date 2023/6/12 16:11
+ * @Version 1.0.0
+ **/
+
+import cn.hutool.http.HttpException;
+import cn.hutool.http.HttpResponse;
+import cn.hutool.http.HttpUtil;
+import cn.hutool.json.JSONObject;
+import com.cyksj.common.util.Jsons;
+import com.cyksj.dto.XxlJobResult;
+
+import java.io.IOException;
+import java.net.HttpCookie;
+import java.util.List;
+
+/**
+ * @Author: JCccc
+ * @Date: 2022-6-22 9:51
+ * @Description:
+ */
+public class XxlJobUtil {
+
+	private final static String BASE_URL = "http://app7.liuliangbang.vip:9099/xxl-job-admin";
+
+	private static String cookie = "";
+
+
+//    /**
+//     * 查询现有的任务(可以关注这个整个调用链,可以自己模仿着写其他的拓展接口)
+//     * @return
+//     * @throws HttpException
+//     * @throws IOException
+//     */
+//    public static JSONObject pageList(Map<String,Object> param) throws Exception {
+//        String path = "/jobinfo/pageList" + "?jobGroup=5&triggerStatus=-1";
+//        String targetUrl = BASE_URL + path;
+//        System.out.println(cookie);
+//        HttpResponse execute = HttpUtil.createPost(targetUrl)
+//                .header("Content-Type","application/json")
+//                .header("Cookie",getCookie())
+//                .execute();
+//        return Jsons.parseObject(execute.body(),JSONObject.class);
+//    }
+
+
+	/**
+	 * 新增任务
+	 *
+	 * @param jobParam
+	 * @return
+	 * @throws HttpException
+	 * @throws IOException
+	 */
+	public static XxlJobResult addJob(String jobParam) throws Exception {
+		String path = "/jobinfo/add";
+		String targetUrl = BASE_URL + path + jobParam;
+
+		HttpResponse execute = HttpUtil.createPost(targetUrl)
+				.header("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
+				.header("Cookie", getCookie())
+				.execute();
+		return Jsons.parseObject(execute.body(), XxlJobResult.class);
+	}
+
+	public static void main(String[] args) throws Exception {
+		// updateJob(XxlJobInfo.getUpdateInstance(58,XxlJobInfo.AUTHOR.HUANG_YUAN_ZHI,"修改job","0/4 * * * * ?",5,"LikeJob","").toString());
+		JSONObject jsonObject = deleteJob("67");
+		System.out.println(jsonObject);
+	}
+
+	/**
+	 * 编辑任务
+	 *
+	 * @param query
+	 * @return
+	 * @throws Exception
+	 */
+	public static JSONObject updateJob(String query) throws Exception {
+		String path = "/jobinfo/update";
+		String targetUrl = BASE_URL + path + query;
+
+		HttpResponse execute = HttpUtil.createPost(targetUrl)
+				.header("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
+				.header("Cookie", getCookie())
+				.execute();
+		return Jsons.parseObject(execute.body(), JSONObject.class);
+	}
+
+	/**
+	 * 删除任务
+	 *
+	 * @param id
+	 * @return
+	 * @throws HttpException
+	 * @throws IOException
+	 */
+	public static JSONObject deleteJob(String id) throws Exception {
+		String path = "/jobinfo/remove?id=" + id;
+		String url = BASE_URL + path;
+		HttpResponse execute = HttpUtil.createGet(url).header("Cookie", getCookie()).execute();
+		return Jsons.parseObject(execute.body(), JSONObject.class);
+	}
+
+
+	/**
+	 * 获取登录cookie
+	 *
+	 * @return
+	 */
+	public static String getCookie() {
+		String path = "/login";
+		String targetUrl = BASE_URL + path;
+		HttpResponse execute = HttpUtil.createPost(targetUrl)
+				.form("userName", "admin")
+				.form("password", "123456")
+				.execute();
+		if (execute.getStatus() == 200) {
+			List<HttpCookie> cookies = execute.getCookies();
+			StringBuffer tmpcookies = new StringBuffer();
+			for (HttpCookie c : cookies) {
+				tmpcookies.append(c.getName()).append("=").append(c.getValue() + ";");
+			}
+			cookie = tmpcookies.toString();
+		} else {
+			try {
+				cookie = "";
+			} catch (Exception e) {
+				cookie = "";
+			}
+		}
+		return cookie;
+	}
+
+}

+ 18 - 0
netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpMsgBulkTaskMapper.java

@@ -0,0 +1,18 @@
+package com.cyksj.mapper.corp;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.CorpMsgBulkTask;
+import com.cyksj.model.entity.CorpUserSendRecord;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/*
+ *项目名: netflix
+ *文件名: CorpMsgBulkTaskMapper
+ *创建者: JavaZou
+ *创建时间:2023/6/21 10:31
+ */
+public interface CorpMsgBulkTaskMapper extends BaseMapper<CorpMsgBulkTask> {
+	List<CorpUserSendRecord> getsSuccessCorpUser(@Param("corpUserIdList") List<String> corpUserIdList);
+}

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

@@ -0,0 +1,13 @@
+package com.cyksj.mapper.corp;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.CorpMsgBulkTaskRecord;
+
+/*
+ *项目名: netflix
+ *文件名: CorpMsgBulkTaskRecordMapper
+ *创建者: JavaZou
+ *创建时间:2023/6/21 11:19
+ */
+public interface CorpMsgBulkTaskRecordMapper extends BaseMapper<CorpMsgBulkTaskRecord> {
+}

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

@@ -0,0 +1,13 @@
+package com.cyksj.mapper.corp;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.CorpMsgBulkTaskTagRelation;
+
+/*
+ *项目名: netflix
+ *文件名: CorpMsgBulkTaskTagRelationMapper
+ *创建者: JavaZou
+ *创建时间:2023/7/4 16:02
+ */
+public interface CorpMsgBulkTaskTagRelationMapper extends BaseMapper<CorpMsgBulkTaskTagRelation> {
+}

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

@@ -0,0 +1,13 @@
+package com.cyksj.mapper.corp;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.CorpMsg;
+
+/*
+ *项目名: netflix
+ *文件名: CorpMsgMapper
+ *创建者: JavaZou
+ *创建时间:2023/6/21 11:20
+ */
+public interface CorpMsgMapper extends BaseMapper<CorpMsg> {
+}

+ 10 - 0
netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpTagGroupMapper.java

@@ -2,6 +2,11 @@ package com.cyksj.mapper.corp;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.cyksj.model.entity.CorpTagGroup;
+import com.cyksj.model.views.CorpTagBackView;
+import com.cyksj.model.views.CorpTagGroupView;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
 
 /*
  *项目名: netflix
@@ -10,4 +15,9 @@ import com.cyksj.model.entity.CorpTagGroup;
  *创建时间:2022/10/13 18:01
  */
 public interface CorpTagGroupMapper extends BaseMapper<CorpTagGroup> {
+	CorpTagGroupView getOneCorpGroupTag(@Param("groupId") String groupId, @Param("groupName") String groupName);
+
+	List<CorpTagGroupView> getCorpTagList();
+
+	List<CorpTagBackView> getCorpTagBackView(@Param("groupIds") String[] groupIds, @Param("sort") String sort, @Param("order") String order);
 }

+ 12 - 0
netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpTagMapper.java

@@ -2,6 +2,11 @@ package com.cyksj.mapper.corp;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.cyksj.model.entity.CorpTag;
+import com.cyksj.model.views.CorpTagBackView;
+import com.cyksj.model.views.CorpTagHeadDataView;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
 
 /*
  *项目名: netflix
@@ -10,4 +15,11 @@ import com.cyksj.model.entity.CorpTag;
  *创建时间:2022/10/13 18:07
  */
 public interface CorpTagMapper extends BaseMapper<CorpTag> {
+	List<CorpTagBackView> getCorpTagBackView(@Param("tagIds") String[] tagIds, @Param("sort") String sort, @Param("order") String order);
+
+	List<String> selectTagsIdByGroupId(String groupId);
+
+	CorpTag getCorpTagById(Long corpTagId);
+
+	CorpTagHeadDataView getCorpTagHeadDataView();
 }

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

@@ -0,0 +1,13 @@
+package com.cyksj.mapper.corp;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.CorpTagRule;
+
+/*
+ *项目名: netflix
+ *文件名: CorpTagRuleMapper
+ *创建者: JavaZou
+ *创建时间:2023/6/20 18:02
+ */
+public interface CorpTagRuleMapper extends BaseMapper<CorpTagRule> {
+}

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

@@ -0,0 +1,13 @@
+package com.cyksj.mapper.corp;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.CorpTagRuleRelation;
+
+/*
+ *项目名: netflix
+ *文件名: CorpTagRuleRelationMapper
+ *创建者: JavaZou
+ *创建时间:2023/6/20 17:22
+ */
+public interface CorpTagRuleRelationMapper extends BaseMapper<CorpTagRuleRelation> {
+}

+ 7 - 1
netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpUserFollowRelationMapper.java

@@ -1,7 +1,11 @@
 package com.cyksj.mapper.corp;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.dto.CorpUserFollowRelationDto;
 import com.cyksj.model.entity.CorpUserFollowRelation;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
 
 /**
  * @author chan
@@ -9,5 +13,7 @@ import com.cyksj.model.entity.CorpUserFollowRelation;
  */
 public interface CorpUserFollowRelationMapper extends BaseMapper<CorpUserFollowRelation> {
 
-	String selectCorpCustomers(String unionid);
+	String selectCorpCustomers(@Param("unionid") String unionid, @Param("wxCorpId") Long wxCorpId);
+
+	List<CorpUserFollowRelationDto> selectCorpUserFollowRelationByTagIds(@Param("list") List<String> tagIds, @Param("wxCorpId") Long wxCorpId);
 }

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

@@ -2,6 +2,7 @@ package com.cyksj.mapper.corp;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.cyksj.model.entity.CorpUser;
+import org.apache.ibatis.annotations.Select;
 
 
 /**
@@ -9,4 +10,6 @@ import com.cyksj.model.entity.CorpUser;
  * @date 2022/3/15 上午9:49
  */
 public interface CorpUserMapper extends BaseMapper<CorpUser> {
+	@Select("select cu.id,cu.user_id,cu.unionid from (select corp_user_id from corp_user_auth where external_userid = #{corpUserId} limit 1) cua inner join corp_user cu on cu.id = cua.corp_user_id and cu.unionid != '' limit 1")
+	CorpUser setCorpUserUserId(String corpUserId);
 }

+ 12 - 0
netflix-dao/src/main/java/com/cyksj/mapper/corp/CorpUserSendRecordMapper.java

@@ -0,0 +1,12 @@
+package com.cyksj.mapper.corp;
+
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.CorpUserSendRecord;
+
+/**
+ * @author Administrator
+ */
+public interface CorpUserSendRecordMapper extends BaseMapper<CorpUserSendRecord> {
+
+}

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

@@ -1,7 +1,13 @@
 package com.cyksj.mapper.corp;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.cyksj.model.entity.CorpUserTag;
+import com.cyksj.model.excel.CorpUserTagExcelData;
+import com.cyksj.model.views.CorpUserView;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
 
 
 /**
@@ -9,4 +15,7 @@ import com.cyksj.model.entity.CorpUserTag;
  * @date 2022/3/15 上午11:20
  */
 public interface CorpUserTagMapper extends BaseMapper<CorpUserTag> {
+	Page<CorpUserView> selectCorpUserDetailByCondition(@Param("page") Page<CorpUserView> page, @Param("groupId") String groupId, @Param("tagId") String tagId, @Param("wxCorpId") Long wxCorpId);
+
+	List<CorpUserTagExcelData> exportUserTagDetail(@Param("tagIds") String[] tagIds, @Param("groupIds") String[] groupIds);
 }

+ 10 - 0
netflix-dao/src/main/java/com/cyksj/mapper/manage/coupon/CouponMapper.java

@@ -3,8 +3,13 @@ package com.cyksj.mapper.manage.coupon;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.cyksj.model.dto.CouponPopularizeDto;
 import com.cyksj.model.entity.Coupon;
+import com.cyksj.model.entity.CouponUser;
+import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 
+import java.util.Date;
+import java.util.List;
+
 /*
  *项目名: netflix
  *文件名: CouponMapper
@@ -19,4 +24,9 @@ public interface CouponMapper extends BaseMapper<Coupon> {
 
 	@Select("select c.name from coupon c inner join coupon_user cu on cu.coupon_id = c.id where cu.id = #{couponUserId} limit 1")
 	String getCouponNameByCouponUserId(Long couponUserId);
+
+	void batchSendCouponToUsers(@Param("userIds") List<Long> userIds, @Param("couponId") Long couponId, @Param("channel") CouponUser.Channel channel, @Param("status") CouponUser.Status status, @Param("validStartTime") Date validStartTime, @Param("validEndTime") Date validEndTime);
+
+	@Select("select name from coupon where id = #{couponId}")
+	String getCouponNameById(Long couponId);
 }

+ 20 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/CornMetaDto.java

@@ -0,0 +1,20 @@
+package com.cyksj.model.dto;
+
+import lombok.Data;
+
+/**
+ * @Author Huang yuanzhi
+ * @Date 2023/6/13 11:25
+ * @Version 1.0.0
+ **/
+@Data
+public class CornMetaDto {
+
+    private String hour;
+
+    private String day;
+
+    private String month;
+
+    private String week;
+}

+ 23 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/CorpMsgBulkTaskContent.java

@@ -0,0 +1,23 @@
+package com.cyksj.model.dto;
+
+import com.cyksj.model.request.corp.Attachments;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+/*
+ *项目名: netflix
+ *文件名: CorpMsgBulkTaskContent
+ *创建者: JavaZou
+ *创建时间:2023/6/21 10:23
+ */
+@Getter
+@Setter
+public class CorpMsgBulkTaskContent {
+	private String text;
+
+	@JsonProperty("attachments")
+	private List<Attachments> attachments;
+}

+ 28 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/CorpUserFollowRelationDto.java

@@ -0,0 +1,28 @@
+package com.cyksj.model.dto;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * @ClassName CorpUserRalationDto
+ * @Description TODO
+ * @Author Huang yuanzhi
+ * @Date 2023/4/20 17:13
+ * @Version 1.0.0
+ **/
+@EqualsAndHashCode
+@Data
+public class CorpUserFollowRelationDto {
+    private String corpUserId;
+
+    /**
+     * corp_follow 成员id
+     */
+    private String followId;
+
+    /**
+     * 用户 银河id
+     */
+    private Long userId;
+
+}

+ 39 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/CorpMsg.java

@@ -0,0 +1,39 @@
+package com.cyksj.model.entity;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * @author zwhui
+ * @date 2022/4/20 17:17
+ */
+@Data
+public class CorpMsg {
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    private Long corpId;
+
+    private String msgid;
+
+    private String followId;
+
+    private String content;
+
+    /**
+     * 创建时间
+     */
+    @TableField(fill = FieldFill.INSERT)
+    private Date createdTime;
+
+    /**
+     * 修改时间
+     */
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Date updateTime;
+}

+ 153 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/CorpMsgBulkTask.java

@@ -0,0 +1,153 @@
+package com.cyksj.model.entity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.cyksj.model.dto.CornMetaDto;
+import com.cyksj.model.request.corp.Attachments;
+import com.ejlchina.searcher.bean.DbIgnore;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * <p>
+ * 企业微信群发消息任务
+ * </p>
+ *
+ * @author zoujiajian
+ * @since 2022-05-12
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@SearchBean(tables = "corp_msg_bulk_task",
+		where = ":tag_id:")
+public class CorpMsgBulkTask implements Serializable {
+
+	@TableId(value = "id", type = IdType.AUTO)
+	private Long id;
+
+	/**
+	 * 企业微信id
+	 */
+	private Long wxCorpId;
+
+	/**
+	 * 群发任务选择标签
+	 */
+	@NotEmpty(message = "标签不能为空")
+	private String chooseTagStr;
+
+	/**
+	 * 标签数量
+	 */
+	@TableField(exist = false)
+	@DbIgnore
+	private Integer tagSize;
+
+	/**
+	 * 群发内容 文本、附件
+	 */
+	@TableField(updateStrategy = FieldStrategy.IGNORED)
+	private String text;
+
+	@TableField(exist = false)
+	@DbIgnore
+	private List<Attachments> attachments;
+
+	/**
+	 * 附件字符串
+	 */
+	@TableField(updateStrategy = FieldStrategy.IGNORED)
+	private String attachmentsStr;
+
+	/**
+	 * 1立即发送,2每日循环,3每周循环,4每月循环
+	 */
+	@NotNull(message = "定时发送或者立即发送不能为空")
+	private Integer sendTimeType;
+
+	private String corn;
+
+	/**
+	 * 优惠券id
+	 */
+	@TableField(updateStrategy = FieldStrategy.IGNORED)
+	private Long couponId;
+
+	@DbIgnore
+	@TableField(exist = false)
+	private String couponName;
+
+	/**
+	 * xxljobId
+	 */
+	private String jobId;
+
+	/**
+	 * 定时发送的发送时间
+	 */
+	@TableField(updateStrategy = FieldStrategy.IGNORED)
+	private String sendTime;
+	/**
+	 * 群发消息任务名称
+	 */
+	private String name;
+
+	@TableField(exist = false)
+	@DbIgnore
+	private CornMetaDto cornDto;
+
+	/**
+	 * 选择时间
+	 */
+	private String chooseTime;
+
+	/**
+	 * 是否启动(true:启用,false:停用)
+	 */
+	private Boolean status;
+
+	private Boolean deleted;
+
+	/**
+	 * 创建时间
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	private Date createdTime;
+
+	/**
+	 * 更新时间
+	 */
+	@TableField(fill = FieldFill.INSERT_UPDATE)
+	private Date updateTime;
+
+	@Getter
+	public enum Week {
+		MON("1"),
+		TUE("2"),
+		WED("3"),
+		THU("4"),
+		FRI("5"),
+		SAT("6"),
+		SUN("7");
+
+		 Week(String week) {
+			this.week = week;
+		}
+
+		public static String getWeek(String week) {
+
+		 return Arrays.stream(values()).filter(week1 -> week1.getWeek().equals(week)).findFirst().get().name();
+		}
+		private String week;
+	}
+
+
+}

+ 73 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/CorpMsgBulkTaskRecord.java

@@ -0,0 +1,73 @@
+package com.cyksj.model.entity;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * <p>
+ * 企业微信群发消息任务记录表
+ * </p>
+ *
+ * @author zoujiajian
+ * @since 2022-05-12
+ */
+@Getter
+@Setter
+public class CorpMsgBulkTaskRecord implements Serializable {
+
+	@TableId(value = "id", type = IdType.AUTO)
+	private Long id;
+
+	/**
+	 * 企业微信群发消息任务id
+	 */
+	private Long taskId;
+
+	/**
+	 * 企业微信id
+	 */
+	private Long corpId;
+
+	/**
+	 * 企业成员follow_id
+	 */
+	private String followId;
+
+	/**
+	 * 企业微信消息id
+	 */
+	@JsonProperty(value = "msgid")
+	private String msgid;
+
+	/**
+	 * 发送的内容
+	 */
+	private String context;
+
+	private Integer total;
+
+	/**
+	 * 成员是否发送,true已发送
+	 */
+	private Boolean status;
+
+	/**
+	 * 创建时间
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	private Date createdTime;
+
+	/**
+	 * 创建时间
+	 */
+	@TableField(fill = FieldFill.INSERT_UPDATE)
+	private Date updateTime;
+}

+ 18 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/CorpMsgBulkTaskTagRelation.java

@@ -0,0 +1,18 @@
+package com.cyksj.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/*
+ *项目名: netflix
+ *文件名: CorpMsgBulkTaskTagRelation
+ *创建者: JavaZou
+ *创建时间:2023/7/4 16:02
+ */
+@Getter
+@Setter
+public class CorpMsgBulkTaskTagRelation extends BaseEntity{
+	private String tagId;
+
+	private Long taskId;
+}

+ 31 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/CorpTag.java

@@ -1,8 +1,13 @@
 package com.cyksj.model.entity;
 
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.ejlchina.searcher.bean.DbIgnore;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
 import lombok.Data;
 
 import java.io.Serializable;
+import java.util.List;
 
 /**
  * @author chan
@@ -30,4 +35,30 @@ public class CorpTag extends BaseEntity implements Serializable {
      * 标签id
      */
     private String tagId;
+
+    /**
+     * 标签所属类型 1普通,2动态
+     */
+    private Integer type;
+
+    private Long ordered;
+
+    /**
+     * 该标签对应的规则
+     */
+    @TableField(exist = false)
+    @DbIgnore
+    @JsonProperty("ruleRelationList")
+    private List<CorpTagRuleRelation> ruleRelationList;
+
+    /**
+     * 动态标签 规则数组名
+     */
+    @TableField(exist = false)
+    @DbIgnore
+    @JsonProperty("ruleNameList")
+    private List<String> ruleNameList;
+
+    @JsonIgnore
+    private String ruleNameListStr;
 }

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

@@ -24,7 +24,7 @@ public class CorpTagGroup extends BaseEntity implements Serializable {
     /**
      * 排序
      */
-    private Integer order;
+    private Long ordered;
 
     /**
      * 是否删除

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

@@ -0,0 +1,29 @@
+package com.cyksj.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/*
+ *项目名: netflix
+ *文件名: CorpTagRule
+ *创建者: JavaZou
+ *创建时间:2023/6/20 16:45
+ */
+@Getter
+@Setter
+public class CorpTagRule extends BaseEntity{
+	/**
+	 * 规则名
+	 */
+	private String name;
+
+	/**
+	 * 规则条件值
+	 */
+	private Integer value;
+
+	/**
+	 * 规则类型 1、购买状态,2、账号状态,3、下单状态
+	 */
+	private Integer type;
+}

+ 41 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/CorpTagRuleRelation.java

@@ -0,0 +1,41 @@
+package com.cyksj.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/*
+ *项目名: netflix
+ *文件名: CorpTagRuleRelation
+ *创建者: JavaZou
+ *创建时间:2023/6/20 16:47
+ */
+@Getter
+@Setter
+public class CorpTagRuleRelation extends BaseEntity{
+	/**
+	 * 标签id
+	 */
+	private Long tagId;
+
+	/**
+	 * 平台id
+	 */
+	private Long goodsId;
+
+	/**
+	 * 购买状态
+	 * 1、意向购买 2、已购买
+	 */
+	private Integer payStatus;
+
+	/**
+	 * 账号状态
+	 * -1已过期,0已续费
+	 */
+	private Integer accountStatus;
+
+	/**
+	 * 下单状态
+	 */
+	private Integer orderStatus;
+}

+ 54 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/CorpUserSendRecord.java

@@ -0,0 +1,54 @@
+package com.cyksj.model.entity;
+
+import com.alibaba.excel.annotation.ExcelIgnore;
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * corp_user_send_record
+ * @author huang
+ */
+@Data
+public class CorpUserSendRecord implements Serializable {
+
+    @TableId(value = "id",type = IdType.AUTO)
+    @ExcelIgnore
+    private Long id;
+
+    /**
+     * 企业群发记录消息记录表id
+     */
+    @ExcelIgnore
+    private Long corpMsgRecordId;
+    @ExcelProperty("姓名")
+    private String name;
+
+    @TableField(exist = false)
+    @ExcelIgnore
+    private Integer sendType;
+
+    /**
+     * 头像
+     */
+    @ExcelProperty("头像")
+    private String avatar;
+
+    @ExcelProperty("企业用户id")
+    private String externalUserid;
+
+    @TableField(fill = FieldFill.INSERT)
+    @ExcelProperty("发送时间")
+    private Date sendTime;
+
+    @ExcelProperty("状态(true已发送)")
+    private Boolean status;
+
+    private static final long serialVersionUID = 1L;
+}

+ 6 - 5
netflix-dao/src/main/java/com/cyksj/model/entity/CouponUser.java

@@ -56,11 +56,12 @@ public class CouponUser extends BaseEntity {
 		center("unused", "领卷中心"),
 		exCode("exCode", "兑换码"),
 		wxCorp("wxCorp", "企业微信"),
-		manual("manual","手动发放"),
-		lottery("lottery","抽奖"),
-		task("task","完成任务"),
-		equipment("equipment","实物设备分销"),
-		ques_response("ques_response","问卷回答"),
+		manual("manual", "手动发放"),
+		lottery("lottery", "抽奖"),
+		task("task", "完成任务"),
+		equipment("equipment", "实物设备分销"),
+		ques_response("ques_response", "问卷回答"),
+		corp_bulk_task("corp_bulk_task", "企业微信群发任务")
 		;
 		private String channel;
 		private String desc;

+ 34 - 0
netflix-dao/src/main/java/com/cyksj/model/excel/CorpUserTagExcelData.java

@@ -0,0 +1,34 @@
+package com.cyksj.model.excel;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.ejlchina.searcher.bean.DbField;
+import lombok.Getter;
+import lombok.Setter;
+
+/*
+ *项目名: netflix
+ *文件名: CorpUserTagExcelData
+ *创建者: JavaZou
+ *创建时间:2023/6/26 15:04
+ */
+@Getter
+@Setter
+public class CorpUserTagExcelData {
+	@ExcelProperty("用户ID")
+	private Long userId;
+
+	@ExcelProperty("用户名")
+	private String nickname;
+
+	@DbField("用户头像")
+	private String headimgurl;
+
+	@ExcelProperty("所属客服")
+	private String customers;
+
+	@ExcelProperty("标签")
+	private String tagNames;
+
+	@ExcelProperty("打上标签时间")
+	private String tagTime;
+}

+ 18 - 0
netflix-dao/src/main/java/com/cyksj/model/request/CorpDelTagReq.java

@@ -0,0 +1,18 @@
+package com.cyksj.model.request;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/*
+ *项目名: netflix
+ *文件名: CorpDelTagReq
+ *创建者: JavaZou
+ *创建时间:2023/7/3 16:19
+ */
+@Getter
+@Setter
+public class CorpDelTagReq {
+	private String tagId;
+
+	private String groupId;
+}

+ 47 - 0
netflix-dao/src/main/java/com/cyksj/model/request/CorpMsgBulkTaskReq.java

@@ -0,0 +1,47 @@
+package com.cyksj.model.request;
+
+import com.baomidou.mybatisplus.annotation.FieldStrategy;
+import com.baomidou.mybatisplus.annotation.TableField;
+import lombok.Getter;
+import lombok.Setter;
+
+import javax.validation.constraints.NotNull;
+
+/*
+ *项目名: netflix
+ *文件名: CorpMsgBulkTaskReq
+ *创建者: JavaZou
+ *创建时间:2023/6/21 10:02
+ */
+@Getter
+@Setter
+public class CorpMsgBulkTaskReq {
+	/**
+	 * 群发消息任务名称
+	 */
+	private String name;
+
+	/**
+	 * 群发任务选择标签
+	 */
+	private String chooseTagStr;
+
+	/**
+	 * 1立即发送,2每日循环,3每周循环,4每月循环
+	 */
+	@NotNull(message = "定时发送或者立即发送不能为空")
+	private Integer sendTimeType;
+
+	/**
+	 * 定时发送的发送时间
+	 */
+	@TableField(updateStrategy = FieldStrategy.IGNORED)
+	private String sendTime;
+
+	/**
+	 * 选择时间
+	 */
+	private String chooseTime;
+
+	private Long couponId;
+}

+ 114 - 3
netflix-dao/src/main/java/com/cyksj/model/request/corp/Attachments.java

@@ -1,5 +1,7 @@
 package com.cyksj.model.request.corp;
 
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
@@ -7,7 +9,7 @@ import lombok.NoArgsConstructor;
 import java.io.Serializable;
 
 /*
- *项目名: netflix
+ *项目名: shanxing-admin
  *文件名: LinkTitle
  *创建者: JavaZou
  *创建时间:2022/6/20 16:01
@@ -15,16 +17,71 @@ import java.io.Serializable;
 @Data
 @AllArgsConstructor
 @NoArgsConstructor
+@JsonInclude(JsonInclude.Include.NON_NULL)
 public class Attachments implements Serializable {
+
+	private static final long serialVersionUID = 3529000560142115378L;
+
+	/**
+	 * 图片消息.
+	 */
+	public static final String IMAGE = "image";
+	/**
+	 * 图文消息.
+	 */
+	public static final String LINK = "link";
+	/**
+	 * 视频消息.
+	 */
+	public static final String VIDEO = "video";
+	/**
+	 * 小程序消息.
+	 */
+	public static final String MINIPROGRAM = "miniprogram";
+
+
 	private String msgtype;
 
+	private Image image;
+
 	private Link link;
 
-	private static final long serialVersionUID = 3529000560142115378L;
+	@JsonProperty("miniprogram")
+	private MiniProgram miniProgram;
+
+	private Video video;
+
+	public Attachments(String msgtype, Link link) {
+		this.msgtype = msgtype;
+		this.link = link;
+	}
+
+	public void setImage(Image image) {
+		this.image = image;
+		this.msgtype = IMAGE;
+	}
+
+	public void setLink(Link link) {
+		this.link = link;
+		this.msgtype = LINK;
+	}
+
+	public void setMiniProgram(MiniProgram miniProgram) {
+		this.miniProgram = miniProgram;
+		this.msgtype = MINIPROGRAM;
+	}
+
+
+	public void setVideo(Video video) {
+		this.video = video;
+		this.msgtype = VIDEO;
+	}
+
 
 	@Data
 	@AllArgsConstructor
 	@NoArgsConstructor
+	@JsonInclude(JsonInclude.Include.NON_NULL)
 	public static class Link implements Serializable{
 		private static final long serialVersionUID = -1036758941402144780L;
 
@@ -46,5 +103,59 @@ public class Attachments implements Serializable {
 		 * 项目地址
 		 */
 		private String url;
+
+		@JsonProperty("media_id")
+		private String mediaId;
+
+		public Link(String title, String picurl, String desc, String url) {
+			this.title = title;
+			this.picurl = picurl;
+			this.desc = desc;
+			this.url = url;
+		}
 	}
-}
+
+	@Data
+	@AllArgsConstructor
+	@NoArgsConstructor
+	@JsonInclude(JsonInclude.Include.NON_NULL)
+	public static class Image implements Serializable {
+		private static final long serialVersionUID = -606286372867787121L;
+
+		@JsonProperty("media_id")
+		private String mediaId;
+
+		@JsonProperty("pic_url")
+		private String picUrl;
+	}
+
+
+	@Data
+	@AllArgsConstructor
+	@NoArgsConstructor
+	@JsonInclude(JsonInclude.Include.NON_NULL)
+	public static class MiniProgram implements Serializable {
+		private static final long serialVersionUID = 4242074162638170679L;
+
+		private String title;
+		@JsonProperty("pic_media_id")
+		private String picMediaId;
+		private String appid;
+		private String page;
+		private String url;
+	}
+
+	@Data
+	@AllArgsConstructor
+	@NoArgsConstructor
+	@JsonInclude(JsonInclude.Include.NON_NULL)
+	public static class Video implements Serializable {
+		private static final long serialVersionUID = -6048642921382867138L;
+		@JsonProperty("media_id")
+		private String mediaId;
+		@JsonProperty("thumb_media_id")
+		private String thumbMediaId;
+		private String url;
+	}
+
+}

+ 45 - 0
netflix-dao/src/main/java/com/cyksj/model/request/corp/CorpMsgTemplate.java

@@ -0,0 +1,45 @@
+package com.cyksj.model.request.corp;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author chan
+ * @date 2022/3/16 下午2:33
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class CorpMsgTemplate implements Serializable {
+
+    /**
+     * 群发任务的类型,默认为single,表示发送给客户,group表示发送给客户群
+     */
+    @JsonProperty("chat_type")
+    private String chatType;
+
+    /**
+     * 客户的外部联系人id列表,仅在chat_type为single时有效,不可与sender同时为空,最多可传入1万个客户
+     */
+    @JsonProperty("external_userid")
+    private List<String> externalUserid;
+
+    /**
+     * 发送企业群发消息的成员userid,当类型为发送给客户群时必填
+     */
+    private String sender;
+
+    /**
+     * 消息文本内容,最多4000个字节
+     */
+    private Text text;
+
+
+
+    List<Attachments> attachments;
+
+
+}

+ 22 - 0
netflix-dao/src/main/java/com/cyksj/model/response/corp/CorpBaseResult.java

@@ -0,0 +1,22 @@
+package com.cyksj.model.response.corp;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * @author chan
+ * @date 2022/3/24 下午5:14
+ */
+@Data
+public class CorpBaseResult implements Serializable {
+
+    @JsonProperty("errcode")
+    protected Long errcode;
+
+    @JsonProperty("errmsg")
+    protected String errmsg;
+
+
+}

+ 3 - 1
netflix-dao/src/main/java/com/cyksj/model/response/CorpExternalContactInfo.java → netflix-dao/src/main/java/com/cyksj/model/response/corp/CorpExternalContactInfo.java

@@ -1,5 +1,7 @@
-package com.cyksj.model.response;
+package com.cyksj.model.response.corp;
 
+import com.cyksj.model.response.ExternalContact;
+import com.cyksj.model.response.FollowedUser;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import lombok.Data;
 

+ 17 - 0
netflix-dao/src/main/java/com/cyksj/model/response/corp/CorpMediaUploadResult.java

@@ -0,0 +1,17 @@
+package com.cyksj.model.response.corp;
+
+import lombok.Data;
+
+/**
+ * @author chan
+ * @date 2023/3/15 3:14 PM
+ */
+@Data
+public class CorpMediaUploadResult {
+    private String url;
+    private String type;
+    private String mediaId;
+    private String thumbMediaId;
+    private long createdAt;
+
+}

+ 42 - 0
netflix-dao/src/main/java/com/cyksj/model/response/corp/CorpMsgSendResult.java

@@ -0,0 +1,42 @@
+package com.cyksj.model.response.corp;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author chan
+ * @date 2022/3/16 下午2:42
+ */
+@Data
+public class CorpMsgSendResult implements Serializable {
+
+    private Integer errCode;
+
+    private String errMsg;
+
+    @JsonProperty("next_cursor")
+    private String nextCursor;
+
+    @JsonProperty("send_list")
+    private List<SendResult> sendList;
+
+    @Data
+    public static class SendResult{
+        @JsonProperty("external_userid")
+        private String externalUserid;
+
+        private String userid;
+
+        /**
+         * 发送状态:0-未发送 1-已发送 2-因客户不是好友导致发送失败 3-因客户已经收到其他群发消息导致发送失败
+         */
+        private Integer status;
+
+        @JsonProperty("send_time")
+        private Long sendTime = 1L;
+    }
+
+}

+ 28 - 0
netflix-dao/src/main/java/com/cyksj/model/response/corp/CorpMsgTemplateAddResult.java

@@ -0,0 +1,28 @@
+package com.cyksj.model.response.corp;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author chan
+ * @date 2022/3/16 下午2:42
+ */
+@Data
+public class CorpMsgTemplateAddResult implements Serializable {
+
+    @JsonProperty("errcode")
+    private Integer errCode;
+
+    @JsonProperty("errmsg")
+    private String errMsg;
+
+    @JsonProperty("fail_list")
+    private List<String> failList;
+
+    @JsonProperty("msgid")
+    private String msgId;
+
+}

+ 72 - 0
netflix-dao/src/main/java/com/cyksj/model/response/corp/CorpUserExternalTagGroupInfo.java

@@ -0,0 +1,72 @@
+package com.cyksj.model.response.corp;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author chan
+ * @date 2022/3/24 下午5:10
+ */
+@Getter
+@Setter
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class CorpUserExternalTagGroupInfo extends CorpBaseResult {
+
+    @JsonProperty("tag_group")
+    private TagGroup tagGroup;
+
+    @Getter
+    @Setter
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    public static class TagGroup implements Serializable {
+        private static final long serialVersionUID = -4301684507150486556L;
+
+        @JsonProperty("group_id")
+        private String groupId;
+
+        @JsonProperty("group_name")
+        private String groupName;
+
+        @JsonProperty("create_time")
+        private Long createTime;
+
+        @JsonProperty("order")
+        private Long order;
+
+        @JsonProperty("deleted")
+        private Boolean deleted;
+
+        @JsonProperty("tag")
+        private List<Tag> tag;
+
+    }
+
+    @Getter
+    @Setter
+    @JsonInclude(JsonInclude.Include.NON_NULL)
+    public static class Tag implements Serializable {
+        private static final long serialVersionUID = -4301684507150486556L;
+
+        @JsonProperty("id")
+        private String id;
+
+
+        @JsonProperty("name")
+        private String name;
+
+        @JsonProperty("create_time")
+        private Long createTime;
+
+        @JsonProperty("order")
+        private Long order;
+
+        @JsonProperty("deleted")
+        private Boolean deleted;
+
+    }
+}

+ 1 - 1
netflix-dao/src/main/java/com/cyksj/model/response/CorpUserExternalTagGroupList.java → netflix-dao/src/main/java/com/cyksj/model/response/corp/CorpUserExternalTagGroupList.java

@@ -1,4 +1,4 @@
-package com.cyksj.model.response;
+package com.cyksj.model.response.corp;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 import lombok.Data;

+ 1 - 1
netflix-dao/src/main/java/com/cyksj/model/response/CorpUserInfo.java → netflix-dao/src/main/java/com/cyksj/model/response/corp/CorpUserInfo.java

@@ -1,4 +1,4 @@
-package com.cyksj.model.response;
+package com.cyksj.model.response.corp;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 import lombok.Data;

+ 52 - 0
netflix-dao/src/main/java/com/cyksj/model/views/CorpMsgBulkTaskSendRecordView.java

@@ -0,0 +1,52 @@
+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.util.Date;
+
+/*
+ *项目名: netflix
+ *文件名: CorpMsgBulkTaskSendRecordView
+ *创建者: JavaZou
+ *创建时间:2023/6/21 11:42
+ */
+@Getter
+@Setter
+@SearchBean(tables = "(select * from corp_msg_bulk_task_record where :task_id:) cmr left join corp_user_send_record cr on cr.corp_msg_record_id = cmr.id",
+		groupBy = "cmr.msgid")
+public class CorpMsgBulkTaskSendRecordView {
+
+	@DbField("cmr.msgid")
+	private String msgid;
+
+	/**
+	 * 总数
+	 */
+	@DbField("count(cr.id)")
+	private Integer total;
+
+	/**
+	 * 送达
+	 */
+	@DbField("count(if(cr.status is true,cr.id,null))")
+	private Integer send;
+
+	/**
+	 * 未送达
+	 */
+	@DbField("count(if(cr.status is false,cr.id,null))")
+	private Integer notSend;
+
+	/**
+	 * true已发送 一个未发
+	 */
+	@DbField("if(count(if(cmr.status is false,1,NULL))>0,false,true)")
+	private Boolean status;
+
+	@DbField("any_value(cmr.created_time)")
+	private Date createTime;
+
+}

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

@@ -0,0 +1,59 @@
+package com.cyksj.model.views;
+
+import com.ejlchina.searcher.bean.SearchBean;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+import java.util.List;
+
+/*
+ *项目名: netflix
+ *文件名: CorpTagBackView
+ *创建者: JavaZou
+ *创建时间:2023/6/21 17:26
+ */
+@Getter
+@Setter
+@SearchBean
+public class CorpTagBackView {
+
+	private Long groupId;
+
+	private String groupIdStr;
+	/**
+	 * 标签组名称
+	 */
+	private String groupName;
+
+
+	private Long tagId;
+
+	private String tagIdStr;
+
+	/**
+	 * 标签名
+	 */
+	private String tagName;
+
+	/**
+	 * 标签组下标签数量
+	 */
+	private Integer tagSize;
+
+	/**
+	 * 关联的客户数量
+	 */
+	private Integer numOfCustomer;
+
+	/**
+	 * 时间
+	 */
+	private Date updateTime;
+
+	@JsonIgnore
+	private String ruleNameListStr;
+
+	private List<String> ruleNameList;
+}

+ 32 - 0
netflix-dao/src/main/java/com/cyksj/model/views/CorpTagGroupView.java

@@ -0,0 +1,32 @@
+package com.cyksj.model.views;
+
+import com.cyksj.model.entity.CorpTag;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author chan
+ * @date 2022/3/24 下午1:58
+ */
+@Data
+public class CorpTagGroupView implements Serializable {
+
+    /**
+     * 标签组id
+     */
+    private String groupId;
+
+    /**
+     * 标签组名称
+     */
+    private String groupName;
+
+    /**
+     * 标签集合
+     */
+    @JsonProperty("tagList")
+    private List<CorpTag> tagList;
+}

+ 24 - 0
netflix-dao/src/main/java/com/cyksj/model/views/CorpTagHeadDataView.java

@@ -0,0 +1,24 @@
+package com.cyksj.model.views;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/*
+ *项目名: netflix
+ *文件名: CorpTagHeadDataView
+ *创建者: JavaZou
+ *创建时间:2023/6/30 15:01
+ */
+@Getter
+@Setter
+public class CorpTagHeadDataView {
+	/**
+	 * 标签数
+	 */
+	private Integer numOfTag;
+
+	/**
+	 * 标签组数
+	 */
+	private Integer numOfGroup;
+}

+ 28 - 0
netflix-dao/src/main/java/com/cyksj/model/views/CorpUserView.java

@@ -0,0 +1,28 @@
+package com.cyksj.model.views;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/*
+ *项目名: netflix
+ *文件名: CorpUserView
+ *创建者: JavaZou
+ *创建时间:2023/6/21 17:53
+ */
+@Getter
+@Setter
+public class CorpUserView {
+	private Long userId;
+
+	private String unionId;
+
+	private String nickname;
+
+	private String headimgurl;
+
+	private String tagNames;
+
+	private String customers;
+
+	private String createdTime;
+}

+ 14 - 0
netflix-dao/src/main/resources/mapper/CorpMsgBulkTaskMapper.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.CorpMsgBulkTaskMapper">
+
+    <select id="getsSuccessCorpUser" resultType="com.cyksj.model.entity.CorpUserSendRecord">
+        select cu.name,cu.avatar,cua.external_userid from corp_user cu inner join corp_user_auth cua on cu.id = cua.corp_user_id
+        where cua.external_userid in (
+        <foreach collection="corpUserIdList" item="courUserId" separator=",">
+            #{courUserId}
+        </foreach>
+        )
+    </select>
+</mapper>

+ 58 - 0
netflix-dao/src/main/resources/mapper/CorpTagGroupMapper.xml

@@ -0,0 +1,58 @@
+<?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.CorpTagGroupMapper">
+
+    <resultMap type="com.cyksj.model.views.CorpTagGroupView" id="groupTag">
+        <result column="group_id" property="groupId"/>
+        <result column="group_name" property="groupName"/>
+
+        <collection property="tagList" column="groupId=group_id" ofType="com.cyksj.model.entity.CorpTag" select="getCorpTagListByGroupId"/>
+
+    </resultMap>
+
+    <select id="getOneCorpGroupTag" resultMap="groupTag">
+        select * from corp_tag_group
+        <where>
+            <if test="groupId != null and groupId != ''">
+                group_id = #{groupId,jdbcType=VARCHAR}
+            </if>
+            <if test="groupName != null and groupName != ''">
+                and group_name = #{groupName,jdbcType=VARCHAR}
+            </if>
+        </where>
+        limit 1
+    </select>
+
+    <select id="getCorpTagListByGroupId" resultType="com.cyksj.model.entity.CorpTag">
+        select * from corp_tag where group_id = #{groupId}
+    </select>
+
+    <select id="getCorpTagList" resultMap="groupTag">
+        select * from corp_tag_group
+    </select>
+
+    <select id="getCorpTagBackView" resultType="com.cyksj.model.views.CorpTagBackView">
+        select * from (select cg.id as group_id,
+        cg.group_id as groupIdStr,
+        cg.group_name,
+        count(distinct ct.id) as tagSize,
+        count(cut.id) as numOfCustomer,
+        any_value(cg.update_time) as updateTime
+        from corp_tag_group cg
+        left join corp_tag ct on ct.group_id = cg.group_id
+        left join corp_user_tag cut on cut.tag_id = ct.tag_id
+        <where>
+            <if test="groupIds != null and groupIds.length >0">
+                cg.group_id in
+                <foreach collection="groupIds" item="item" open="(" separator="," close=")">
+                    #{item}
+                </foreach>
+            </if>
+        </where>
+        group by cg.id,cg.group_id,cg.group_name)s
+        <if test="sort != null">
+            order by ${sort} ${order}
+        </if>
+    </select>
+</mapper>

+ 56 - 0
netflix-dao/src/main/resources/mapper/CorpTagMapper.xml

@@ -0,0 +1,56 @@
+<?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.CorpTagMapper">
+
+    <resultMap type="com.cyksj.model.entity.CorpTag" id="corpTagRulesMap">
+        <result column="id" property="id"/>
+
+        <collection property="ruleRelationList" column="tagId=id" ofType="com.cyksj.model.entity.CorpTagRuleRelation" select="getCorpRulesListByTagId"/>
+
+    </resultMap>
+
+    <select id="getCorpTagBackView" resultType="com.cyksj.model.views.CorpTagBackView">
+        select * from (select ct.id as tagId,
+        ct.tag_id as tagIdStr,
+        ct.group_id groupIdStr,
+        ct.group_name,
+        ct.name as tagName,
+        count(cut.id) as numOfCustomer,
+        any_value(ct.update_time) as updateTime,
+        ct.rule_name_list_str
+        from `corp_tag` ct
+        left join corp_user_tag cut on cut.tag_id = ct.tag_id
+        <where>
+            <if test="tagIds != null and tagIds.length > 0">
+                ct.id in
+                <foreach collection="tagIds" item="item" open="(" separator="," close=")">
+                    #{item}
+                </foreach>
+            </if>
+        </where>
+        group by ct.id, ct.group_name, ct.name, ct.tag_id,ct.group_id,ct.rule_name_list_str) s
+        <if test="sort != null">
+            order by ${sort} ${order}
+        </if>
+    </select>
+
+    <select id="selectTagsIdByGroupId" resultType="java.lang.String">
+        select tag_id from corp_tag where group_id = #{groupId}
+    </select>
+
+    <select id="getCorpTagById" resultMap="corpTagRulesMap">
+        select * from `corp_tag` where id = #{corpTagId} limit 1
+    </select>
+
+    <select id="getCorpRulesListByTagId" resultType="com.cyksj.model.entity.CorpTagRuleRelation">
+        select * from `corp_tag_rule_relation` where tag_id = #{tagId}
+    </select>
+
+    <select id="getCorpTagHeadDataView" resultType="com.cyksj.model.views.CorpTagHeadDataView">
+        select count(distinct ct.tag_id) as numOfTag,
+               count(distinct cg.group_id) as numOfGroup
+        from `corp_tag_group` cg
+                 left join corp_tag ct on ct.group_id = ct.group_id
+    </select>
+</mapper>

+ 21 - 5
netflix-dao/src/main/resources/mapper/CorpUserFollowRelationMapper.xml

@@ -4,10 +4,26 @@
 <mapper namespace="com.cyksj.mapper.corp.CorpUserFollowRelationMapper">
 
     <select id="selectCorpCustomers" resultType="java.lang.String">
-        select group_concat(cf.name) from (select id from corp_user
-        where unionid = #{unionid}) cu
-        inner join corp_user_auth ca on ca.corp_user_id = cu.id and wx_corp_id = 2
-        inner join corp_user_follow_relation cfr on cfr.corp_user_id = ca.external_userid
-        inner join corp_follow cf on cf.userid = cfr.follow_id and cf.wx_corp_id = 2
+        select group_concat(cf.name)
+        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 and wx_corp_id = #{wxCorpId}
+                 inner join corp_user_follow_relation cfr on cfr.corp_user_id = ca.external_userid
+                 inner join corp_follow cf on cf.userid = cfr.follow_id and cf.wx_corp_id = #{wxCorpId}
+    </select>
+
+    <select id="selectCorpUserFollowRelationByTagIds" resultType="com.cyksj.model.dto.CorpUserFollowRelationDto">
+        select cufr.corp_user_id,any_value(cufr.follow_id) as followId,any_value(u.id) as user_id from (select
+        relation_id from `corp_user_tag`
+        where tag_id in
+        <foreach collection="list" item="item" open="(" separator="," close=")">
+            #{item}
+        </foreach>) cut left join `corp_user_follow_relation` cufr on cufr.id = cut.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 and cu.unionid != ''
+        left join `user` u on u.unionid = cu.unionid
+        where cufr.wx_corp_id = #{wxCorpId}
+        group by cufr.corp_user_id
     </select>
 </mapper>

+ 64 - 0
netflix-dao/src/main/resources/mapper/CorpUserTagMapper.xml

@@ -0,0 +1,64 @@
+<?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.CorpUserTagMapper">
+
+    <select id="selectCorpUserDetailByCondition" resultType="com.cyksj.model.views.CorpUserView">
+        select cu.id,
+        cu.unionid,
+        any_value(remark) as nickname,
+        group_concat(ct.tag_name) as tagNames,
+        group_concat(cf.name) as customers,
+        any_value(ct.created_time) as createdTime
+        from (select * from corp_user_tag where
+        <choose>
+            <when test="groupId!=null and groupId != ''">
+                tag_id in (select tag_id from corp_tag where group_id = #{groupId})
+            </when>
+            <otherwise>
+                tag_id = #{tagId}
+            </otherwise>
+        </choose>
+        ) ct
+        inner join corp_user_follow_relation cfr on cfr.id = ct.relation_id and wx_corp_id = #{wxCorpId}
+        left join corp_follow cf on cf.userid = cfr.follow_id
+        left join corp_user_auth cua on cua.external_userid = cfr.corp_user_id
+        left join corp_user cu on cu.id = cua.corp_user_id and cu.unionid != ''
+        group by cu.id, cu.unionid
+    </select>
+
+    <select id="exportUserTagDetail" resultType="com.cyksj.model.excel.CorpUserTagExcelData">
+        select cu.id,
+        cu.unionid,
+        any_value(remark) as nickname,
+        group_concat(ct.tag_name) as tagNames,
+        group_concat(cf.name) as customers,
+        any_value(ct.created_time) as createdTime,
+        u.id as userId,
+        u.nickname,
+        u.headimgurl
+        from (select * from corp_user_tag where
+        <choose>
+            <when test="groupIds != null and groupIds.length > 0">
+                tag_id in (select tag_id from corp_tag where group_id in
+                <foreach collection="groupIds" item="item" open="(" separator="," close=")">
+                    #{item}
+                </foreach>
+                )
+            </when>
+            <when test="tagIds != null and tagIds.length > 0">
+                tag_id in
+                <foreach collection="tagIds" item="item" open="(" separator="," close=")">
+                    #{item}
+                </foreach>
+            </when>
+        </choose>
+        ) ct
+        inner join corp_user_follow_relation cfr on cfr.id = ct.relation_id and wx_corp_id = #{wxCorpId}
+        left join corp_follow cf on cf.userid = cfr.follow_id
+        left join corp_user_auth cua on cua.external_userid = cfr.corp_user_id
+        left join corp_user cu on cu.id = cua.corp_user_id and cu.unionid != ''
+        left join user u on u.unionid = cu.unionid
+        group by cu.id, cu.unionid
+    </select>
+</mapper>

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

@@ -3,6 +3,16 @@
 
 <mapper namespace="com.cyksj.mapper.manage.coupon.CouponMapper">
 
+    <insert id="batchSendCouponToUsers">
+        insert into
+        coupon_user(`user_id`,`coupon_id`,`channel`,`valid_start_time`,`valid_end_time`,`status`,`created_time`,`update_time`)
+        values
+        <foreach collection="userIds" item="userId" separator=",">
+            (#{userId},#{couponId},#{channel},#{validStartTime},#{validEndTime},#{status},now(),now())
+        </foreach>
+        ON DUPLICATE KEY UPDATE coupon_id = VALUES(coupon_id)
+    </insert>
+
     <select id="checkExCode" resultType="com.cyksj.model.dto.CouponPopularizeDto">
         select cp.id         as couponId,
                cdp.id        as popularizeId,

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

@@ -609,6 +609,9 @@ public class RedisService {
         QUESTION_RESPONSE_KEY("question_response_key:%s", "问卷回答key", 60 * 60 * 24L),
         IMPORT_ACCOUNT_CACHE_KEY("import_account_cache_key:%s", "导入缓存key", 60 * 10L),
         MAKERTING_MSG_KEY("makerting_msg_key", "营销短信", 60 * 60 * 24 * 30),
+        //企业微信欢迎语 临时素材 mediaId
+        CORP_WECLOME_MEDIA("corp:%s:weclome:media:","临时素材 mediaId",60*60*24*3 - 60*60*1L),
+        CORP_UPLOAD_ATTACHMENTS("corp:%s:upload:attachments:","临时附件",60*60*24*3 - 60*60*1L),
         ;
 
         private String name;

+ 22 - 0
netflix-service/src/main/java/com/cyksj/service/corp/CorpMsgBulkTaskService.java

@@ -0,0 +1,22 @@
+package com.cyksj.service.corp;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.cyksj.model.entity.CorpMsgBulkTask;
+import com.cyksj.model.entity.CorpTag;
+
+import java.util.List;
+import java.util.Map;
+
+/*
+ *项目名: netflix
+ *文件名: CorpMsgBulkTaskService
+ *创建者: JavaZou
+ *创建时间:2023/6/21 10:24
+ */
+public interface CorpMsgBulkTaskService extends IService<CorpMsgBulkTask> {
+	void saveOrUpdateMsgBulkTask(CorpMsgBulkTask corpMsgBulkTask) throws Exception;
+
+	void remindBulkTask(Long id);
+
+	Map<String, List<CorpTag>> getTagDetail(Long id) throws Exception;
+}

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

@@ -2,6 +2,7 @@ package com.cyksj.service.corp;
 
 import com.cyksj.dto.CorpOauth2UserInfo;
 import com.cyksj.model.entity.CorpFollow;
+import com.cyksj.model.request.corp.Attachments;
 import com.cyksj.model.views.YHLXUserDetailView;
 
 /*
@@ -28,4 +29,14 @@ public interface CorpService {
 	String getAgentJsTicket(String corpId, String agentId) throws Exception;
 
 	YHLXUserDetailView getUserDetailView(String externalUserid, String corpId) throws Exception;
+
+	/**
+	 **
+	 * 上传附件
+	 * @param attachments 附件类容
+	 * @param corpId 企业微信id
+	 * @param type 1群发素材,2朋友圈附件
+	 * @return
+	 */
+	Attachments updateMaterial(Attachments attachments, String corpId,Integer type);
 }

+ 45 - 0
netflix-service/src/main/java/com/cyksj/service/corp/CorpTagService.java

@@ -0,0 +1,45 @@
+package com.cyksj.service.corp;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.cyksj.model.entity.CorpTag;
+import com.cyksj.model.entity.CorpTagRule;
+import com.cyksj.model.entity.CorpTagRuleRelation;
+import com.cyksj.model.excel.CorpUserTagExcelData;
+import com.cyksj.model.request.CorpDelTagReq;
+import com.cyksj.model.views.CorpTagBackView;
+import com.cyksj.model.views.CorpTagGroupView;
+import com.cyksj.model.views.CorpTagHeadDataView;
+import com.cyksj.model.views.CorpUserView;
+
+import java.util.List;
+
+/*
+ *项目名: netflix
+ *文件名: CorpTagService
+ *创建者: JavaZou
+ *创建时间:2023/6/20 16:06
+ */
+public interface CorpTagService {
+
+	CorpTagGroupView addCorpTagGroup(CorpTagGroupView tagGroup) throws Exception;
+
+	void editCorpTag(CorpTagGroupView tagGroup) throws Exception;
+
+	List<CorpTagGroupView> getCorpTagList();
+
+	void saveOrUpdateRule(CorpTagRule rule);
+
+	List<CorpTagBackView> getCorpTagBackView(Integer type, String[] tagIds, String[] groupIds, String sort, String order);
+
+	List<CorpTag> getGroupTagDetail(String groupId);
+
+	Page<CorpUserView> getCorpUserDetail(String tagId, String groupId, Integer start, Integer limit);
+
+	List<CorpUserTagExcelData> exportUserTagDetail(String[] tagIds, String[] groupIds);
+
+	List<CorpTagRuleRelation> getCorpTagRuleDetail(Long tagId);
+
+	CorpTagHeadDataView getCorpTagHeadDataView();
+
+	void delTags(CorpDelTagReq req) throws Exception;
+}

+ 13 - 0
netflix-service/src/main/java/com/cyksj/service/corp/CorpUserSendRecordService.java

@@ -0,0 +1,13 @@
+package com.cyksj.service.corp;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.cyksj.model.entity.CorpUserSendRecord;
+
+/**
+
+ * @Author Huang yuanzhi
+ * @Date 2023/4/20 17:28
+ * @Version 1.0.0
+ **/
+public interface CorpUserSendRecordService extends IService<CorpUserSendRecord> {
+}

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

@@ -1,9 +1,9 @@
 package com.cyksj.service.corp;
 
+import cn.hutool.json.JSONObject;
 import com.cyksj.dto.CorpOauth2UserInfo;
-import com.cyksj.model.response.CorpExternalContactInfo;
-import com.cyksj.model.response.CorpUserExternalTagGroupList;
-import com.cyksj.model.response.CorpUserInfo;
+import com.cyksj.model.request.corp.CorpMsgTemplate;
+import com.cyksj.model.response.corp.*;
 
 import java.util.List;
 
@@ -74,4 +74,56 @@ public interface WxCorpOps {
 	 * 企业微信获取应用js_ticket
 	 */
 	String getAgentJsTicket(String corpId, String agentId) throws Exception;
+
+	/**
+	 * 添加企业客户标签
+	 *
+	 * @author chan
+	 * @date 2022-03-24 下午5:12
+	 */
+	CorpUserExternalTagGroupInfo addCorpTag(CorpUserExternalTagGroupInfo tagGroup, String corpId) throws Exception;
+
+	/**
+	 * 编辑企业客户标签
+	 * @author chan
+	 * @date 2022-03-24 下午5:54
+	 */
+	CorpBaseResult editCorpTag(JSONObject params) throws Exception;
+
+	/**
+	 * 删除企业客户标签
+	 * @author chan
+	 * @date 2022-03-24 下午6:05
+	 */
+	CorpBaseResult delCorpTag(String[] tagIds, String[] groupIds) throws Exception;
+
+	/**
+	 * 编辑客户标签
+	 * @author chan
+	 * @date 2022-03-25 上午9:43
+	 */
+	CorpBaseResult markTag(String userId, String followId, String[] tags, String[] removeTags) throws Exception;
+
+	/**
+	 * 企业群发消息
+	 * @author chan
+	 * @date 2022-03-16 下午3:08
+	 */
+	CorpMsgTemplateAddResult addMsgTemplate(String corpId, CorpMsgTemplate msgTemplate)throws Exception;
+
+	/**
+	 * 上传附件
+	 */
+	String getMediaIdByFileName(String msgType,String url,String corpId);
+
+	/**
+	 * 提醒群发
+	 */
+	CorpBaseResult remindBulkTask(String msgid, String corpId) throws Exception;
+
+	/**
+	 * 获取标签组下所有标签
+	 */
+	List<CorpUserExternalTagGroupInfo.TagGroup> getAllCorpTagByGroupsId(String groupId,String corpId) throws Exception;
 }
+

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

@@ -19,8 +19,8 @@ import com.cyksj.model.entity.*;
 import com.cyksj.model.request.corp.Attachments;
 import com.cyksj.model.request.corp.CorpXmlMessage;
 import com.cyksj.model.request.corp.CorpXmlOutMessage;
-import com.cyksj.model.response.CorpExternalContactInfo;
-import com.cyksj.model.response.CorpUserExternalTagGroupList;
+import com.cyksj.model.response.corp.CorpExternalContactInfo;
+import com.cyksj.model.response.corp.CorpUserExternalTagGroupList;
 import com.cyksj.model.response.ExternalContact;
 import com.cyksj.model.response.FollowedUser;
 import com.cyksj.service.corp.WxCorpOps;
@@ -312,6 +312,29 @@ public class CorpEventActionServiceImpl implements CorpEventActionService {
 			corpUserTag.setType(tag.getType());
 			corpUserTagMapper.insert(corpUserTag);
 		});
+		CorpUserAuth corpUserAuth = corpUserAuthMapper.selectOne(Wrappers.lambdaQuery(CorpUserAuth.class)
+				.eq(CorpUserAuth::getExternalUserid, relation.getCorpUserId()).last("limit 1"));
+		if (corpUserAuth != null) {
+			CorpUser corpUser = corpUserMapper.selectById(corpUserAuth.getCorpUserId());
+			User user = userMapper.selectOne(Wrappers.lambdaQuery(User.class).eq(User::getUnionid, corpUser.getUnionid()));
+			if (user != null) {
+				corpUser.setUserId(user.getId());
+				corpUserMapper.updateById(corpUser);
+			}
+		}
+		//同步企业微信 银河用户id
+		Optional.ofNullable(corpUserMapper.setCorpUserUserId(relation.getCorpUserId()))
+				.ifPresent(corpUser -> {
+					if (corpUser.getUserId() == 0) {
+						Optional.ofNullable(userMapper.selectOne(Wrappers.lambdaQuery(User.class)
+								.eq(User::getUnionid, corpUser.getUnionid()).last("limit 1")))
+								.ifPresent(user -> {
+									corpUser.setUserId(user.getId());
+									corpUserMapper.updateById(corpUser);
+								});
+					}
+				});
+
 	}
 
 	/**

+ 358 - 0
netflix-service/src/main/java/com/cyksj/service/corp/impl/CorpMsgBulkTaskServiceImpl.java

@@ -0,0 +1,358 @@
+package com.cyksj.service.corp.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.date.DateTime;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.toolkit.Assert;
+import com.baomidou.mybatisplus.core.toolkit.StringPool;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
+import com.cyksj.common.util.Jsons;
+import com.cyksj.common.util.StringUtil;
+import com.cyksj.dto.XxlJobResult;
+import com.cyksj.mapper.corp.*;
+import com.cyksj.mapper.manage.coupon.CouponMapper;
+import com.cyksj.model.dto.CornMetaDto;
+import com.cyksj.model.dto.CorpUserFollowRelationDto;
+import com.cyksj.model.entity.*;
+import com.cyksj.model.request.corp.Attachments;
+import com.cyksj.model.request.corp.CorpMsgTemplate;
+import com.cyksj.model.request.corp.Text;
+import com.cyksj.model.response.corp.CorpMsgTemplateAddResult;
+import com.cyksj.service.corp.CorpMsgBulkTaskService;
+import com.cyksj.service.corp.CorpService;
+import com.cyksj.service.corp.CorpUserSendRecordService;
+import com.cyksj.service.corp.WxCorpOps;
+import com.cyksj.xxljob.XxlJobInfo;
+import com.cyksj.xxljob.XxlJobUtil;
+import com.google.common.collect.Lists;
+import lombok.RequiredArgsConstructor;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/*
+ *项目名: netflix
+ *文件名: CorpMsgBulkTaskServiceImpl
+ *创建者: JavaZou
+ *创建时间:2023/6/21 10:24
+ */
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class CorpMsgBulkTaskServiceImpl extends ServiceImpl<CorpMsgBulkTaskMapper, CorpMsgBulkTask> implements CorpMsgBulkTaskService {
+
+	private final WxCorpOps corpOps;
+
+	private final CorpMsgBulkTaskMapper corpMsgBulkTaskMapper;
+
+	private final CorpUserFollowRelationMapper corpUserFollowRelationMapper;
+
+	private final WxCorpAppMapper wxCorpAppMapper;
+
+	private final CorpService corpService;
+
+	private final CorpMsgBulkTaskRecordMapper corpMsgBulkTaskRecordMapper;
+
+	private final CorpMsgMapper corpMsgMapper;
+
+	private final CorpUserSendRecordService corpUserSendRecordService;
+
+	private final CorpTagMapper corpTagMapper;
+
+	private final CouponMapper couponMapper;
+
+	private final CorpMsgBulkTaskTagRelationMapper corpMsgBulkTaskTagRelationMapper;
+
+	private final int LIMIT = 10000;
+
+	private static final GlobalThreadPoolTaskExecutor TASK_EXECUTOR = GlobalThreadPoolTaskExecutor.getInstance();
+
+	@Override
+	public void saveOrUpdateMsgBulkTask(CorpMsgBulkTask corpMsgBulkTask) throws Exception {
+		if (corpMsgBulkTask.getWxCorpId() == null) corpMsgBulkTask.setWxCorpId(2l);
+		if (CollUtil.isNotEmpty(corpMsgBulkTask.getAttachments())) {
+			corpMsgBulkTask.setAttachmentsStr(Jsons.toJson(corpMsgBulkTask.getAttachments()));
+		}
+		setCorn(corpMsgBulkTask);
+		this.saveOrUpdate(corpMsgBulkTask);
+		//处理群发任务和标签的关系
+		handleTaskTagRelation(corpMsgBulkTask);
+		//立即群发
+		if (corpMsgBulkTask.getSendTimeType() == 1) {
+			// 去群发
+			TASK_EXECUTOR.execute(() -> {
+				addMsgTemplate(corpMsgBulkTask);
+			});
+			return;
+		}
+		if (StrUtil.isEmpty(corpMsgBulkTask.getJobId())) {
+			// 定时发送
+			XxlJobResult xxlJobResult = XxlJobUtil.addJob(XxlJobInfo.getTaskInstance(0, corpMsgBulkTask.getCorn(), "").toString());
+			corpMsgBulkTask.setJobId(xxlJobResult.getContent());
+			this.updateById(corpMsgBulkTask);
+		}
+		XxlJobUtil.updateJob(XxlJobInfo.getTaskInstance(Integer.valueOf(corpMsgBulkTask.getJobId()), corpMsgBulkTask.getCorn(), corpMsgBulkTask.getId().toString()).toString());
+	}
+
+	@Override
+	public void remindBulkTask(Long id) {
+		List<CorpMsgBulkTaskRecord> corpMsgBulkTaskRecords = corpMsgBulkTaskRecordMapper.selectList(Wrappers.lambdaQuery(CorpMsgBulkTaskRecord.class)
+				.eq(CorpMsgBulkTaskRecord::getTaskId, id)
+				.eq(CorpMsgBulkTaskRecord::getStatus, false));
+		if (CollUtil.isEmpty(corpMsgBulkTaskRecords)) {
+			throw BusinessRuntimeException.getInstance("该群发任务已发送");
+		}
+		corpMsgBulkTaskRecords.forEach(corpMsgBulkTaskRecord -> {
+			String msgid = corpMsgBulkTaskRecord.getMsgid();
+			WxCorpApp wxCorpApp = wxCorpAppMapper.selectById(corpMsgBulkTaskRecord.getCorpId());
+			try {
+				corpOps.remindBulkTask(msgid, wxCorpApp.getCorpId());
+			} catch (Exception e) {
+				log.error("提醒群发任务:{}失败:{}", id, StringUtil.getErrorText(e));
+			}
+		});
+	}
+
+	@Override
+	public Map<String, List<CorpTag>> getTagDetail(Long id) throws Exception {
+		CorpMsgBulkTask task = this.getById(id);
+		Assert.notNull(task, "群发任务不存在");
+		List<CorpTag> corpTags = corpTagMapper.selectList(Wrappers.lambdaQuery(CorpTag.class)
+				.in(CorpTag::getTagId, Jsons.parseList(task.getChooseTagStr(), String.class))
+				.orderByAsc(CorpTag::getGroupId));
+		Map<String, List<CorpTag>> collect = corpTags.stream().collect(Collectors.groupingBy(CorpTag::getGroupName));
+		return collect;
+	}
+
+	/**
+	 * 创建企业群发
+	 */
+	@SneakyThrows
+	public void addMsgTemplate(CorpMsgBulkTask corpMsgBulkTask) {
+		//获取对应标签的客户
+		WxCorpApp wxCorpApp = wxCorpAppMapper.selectById(corpMsgBulkTask.getWxCorpId());
+		Assert.notNull(wxCorpApp, "企业微信未配置");
+		List<String> tagIds = Jsons.parseList(corpMsgBulkTask.getChooseTagStr(), String.class);
+		List<CorpUserFollowRelationDto> corpUserFollowRelations = corpUserFollowRelationMapper.selectCorpUserFollowRelationByTagIds(tagIds, wxCorpApp.getId());
+		//将成员与客户相同关联聚合
+		Map<String, List<CorpUserFollowRelationDto>> userRelationMap = corpUserFollowRelations.stream().collect(Collectors.groupingBy(CorpUserFollowRelationDto::getFollowId));
+		//附件
+		LinkedList<Attachments> attachments = null;
+		if (CollectionUtil.isNotEmpty(corpMsgBulkTask.getAttachments())) {
+			attachments = new LinkedList<>();
+			for (Attachments attachment : corpMsgBulkTask.getAttachments()) {
+				attachments.add(corpService.updateMaterial(attachment, wxCorpApp.getCorpId(), 1));
+
+			}
+		}
+		sendToMsgTemplate(userRelationMap, attachments, corpMsgBulkTask, wxCorpApp);
+	}
+
+	/**
+	 * 设置corn表达式
+	 * @param corpMsgBulkTask
+	 */
+	private void setCorn(CorpMsgBulkTask corpMsgBulkTask) {
+		if(corpMsgBulkTask.getSendTimeType() == 1) {
+			return;
+		}
+
+		CornMetaDto cornDto = corpMsgBulkTask.getCornDto();
+		String corn = "0 0 %s %s %s %s";
+		String day = "*";
+		if(StringUtils.isNotBlank(cornDto.getDay()) ){
+			day = cornDto.getDay();
+			// 按月最后一天
+			if(corpMsgBulkTask.getSendTimeType() == 4) {
+				if("30".equals(day) || "31".equals(day)) {
+					day = "L";
+				}
+			}
+		}
+		if(StringUtils.isNotBlank(cornDto.getWeek())) {
+			day = StringPool.QUESTION_MARK;
+		}
+		corn = String.format(corn,cornDto.getHour(),
+				day
+				,StringPool.ASTERISK
+				,StringUtils.isNotBlank(cornDto.getWeek())?CorpMsgBulkTask.Week.getWeek(cornDto.getWeek()):"?");
+		corpMsgBulkTask.setCorn(corn);
+	}
+
+
+	/**
+	 * 群发
+	 */
+	private void sendToMsgTemplate(Map<String, List<CorpUserFollowRelationDto>> collect
+			, LinkedList<Attachments> attachments,
+			                       CorpMsgBulkTask corpMsgBulkTask,
+			                       WxCorpApp wxCorpApp) {
+		CorpMsgTemplate corpMsgTemplate = new CorpMsgTemplate();
+		corpMsgTemplate.setAttachments(attachments);
+		corpMsgTemplate.setChatType("single");
+		if (StringUtils.isNotBlank(corpMsgBulkTask.getText())) {
+			Text text = new Text();
+			text.setContent(corpMsgBulkTask.getText());
+			corpMsgTemplate.setText(text);
+		}
+		// 每一万条发一次
+		collect.forEach((followId, val) -> {
+			try {
+				List<String> extendIds = new ArrayList<>(val.size());
+				List<Long> userIds = null;
+				if (corpMsgBulkTask.getCouponId() != null) {
+					userIds = new ArrayList<>(val.size());
+				}
+				for (CorpUserFollowRelationDto dto : val) {
+					extendIds.add(dto.getCorpUserId());
+					if (CollUtil.isNotEmpty(userIds)) {
+						userIds.add(dto.getUserId());
+					}
+				}
+				List<List<String>> split = CollectionUtil.split(extendIds, LIMIT);
+				for (List<String> corpUserIdList : split) {
+					corpMsgTemplate.setSender(followId);
+					corpMsgTemplate.setExternalUserid(corpUserIdList);
+					CorpMsgTemplateAddResult result = corpOps.addMsgTemplate(wxCorpApp.getCorpId(), corpMsgTemplate);
+					//记录企业微信群发任务记录
+					CorpMsgBulkTaskRecord corpMsgBulkTaskRecord = new CorpMsgBulkTaskRecord();
+					//该企业微信群发任务id ->> corp_msg_bulk_task.id
+					corpMsgBulkTaskRecord.setTaskId(corpMsgBulkTask.getId());
+					corpMsgBulkTaskRecord.setCorpId(wxCorpApp.getId());
+					//每次群发客户总数
+					corpMsgBulkTaskRecord.setTotal(corpUserIdList.size());
+					//该企业微信下的企业成员id
+					corpMsgBulkTaskRecord.setFollowId(followId);
+					//该企业微信群发任务返回的msgid
+					corpMsgTemplate.setExternalUserid(null);
+					String content = Jsons.toJson(corpMsgTemplate);
+					corpMsgBulkTaskRecord.setMsgid(result.getMsgId());
+					corpMsgBulkTaskRecord.setContext(content);
+					corpMsgBulkTaskRecordMapper.insert(corpMsgBulkTaskRecord);
+
+					CorpMsg corpMsg = new CorpMsg();
+					corpMsg.setCorpId(wxCorpApp.getId());
+					corpMsg.setFollowId(followId);
+					corpMsg.setMsgid(result.getMsgId());
+					corpMsg.setContent(content);
+					corpMsgMapper.insert(corpMsg);
+					//客户保存
+					sendResultRecord(corpMsgBulkTaskRecord, corpUserIdList, result.getFailList());
+
+					//若需要发优惠券
+					if (CollUtil.isNotEmpty(userIds)) {
+						Long couponId = corpMsgBulkTask.getCouponId();
+						Coupon coupon = couponMapper.getCouponById(couponId);
+						Date validStartTime = null;
+						Date validEndTime = null;
+						if (coupon != null) {
+							if (coupon.getIsValidTime()) {
+								validStartTime = coupon.getValidStartTime();
+								validEndTime = coupon.getValidEndTime();
+							} else {
+								validStartTime = DateTime.now();
+								validEndTime = DateUtil.offsetDay(validStartTime, coupon.getValidDays());
+							}
+						}
+						if (userIds.size() <= 500) {
+							couponMapper.batchSendCouponToUsers(userIds, corpMsgBulkTask.getCouponId(), CouponUser.Channel.corp_bulk_task, CouponUser.Status.unused, validStartTime, validEndTime);
+						} else {
+							for (List<Long> userIdsList : Lists.partition(userIds, 500)) {
+								couponMapper.batchSendCouponToUsers(userIdsList, corpMsgBulkTask.getCouponId(), CouponUser.Channel.corp_bulk_task, CouponUser.Status.unused, validStartTime, validEndTime);
+							}
+						}
+					}
+				}
+			} catch (Exception e) {
+				log.error("群发失败任务id{},,{}", corpMsgBulkTask.getId(), e.getMessage());
+			}
+			corpMsgBulkTaskMapper.updateById(corpMsgBulkTask);
+		});
+	}
+
+	/**
+	 * 客户接受结果记录
+	 *
+	 * @param corpUserIdList 已发送集合
+	 * @param failList       失败集合
+	 */
+	private void sendResultRecord(CorpMsgBulkTaskRecord corpMsgBulkTaskRecord, List<String> corpUserIdList, List<String> failList) {
+		if (CollectionUtil.isNotEmpty(failList)) {
+			corpUserIdList.retainAll(failList);
+		}
+
+		if (CollectionUtil.isNotEmpty(corpUserIdList)) {
+			List<List<String>> split = CollectionUtil.split(corpUserIdList, LIMIT / 4);
+			for (List<String> strings : split) {
+				List<CorpUserSendRecord> corpUserSendRecords = corpMsgBulkTaskMapper.getsSuccessCorpUser(strings);
+				corpUserSendRecords.forEach(corpUserSendRecord -> corpUserSendRecord.setCorpMsgRecordId(corpMsgBulkTaskRecord.getId()));
+				corpUserSendRecordService.saveBatch(corpUserSendRecords);
+			}
+		}
+
+		if (CollectionUtil.isNotEmpty(failList)) {
+			List<CorpUserSendRecord> corpUserSendRecords = corpMsgBulkTaskMapper.getsSuccessCorpUser(failList);
+			if (CollectionUtil.isNotEmpty(corpUserSendRecords)) {
+				corpUserSendRecords.forEach(corpUserSendRecord -> {
+					corpUserSendRecord.setCorpMsgRecordId(corpMsgBulkTaskRecord.getId());
+					corpMsgBulkTaskRecord.setTaskId(corpMsgBulkTaskRecord.getTaskId());
+					corpUserSendRecordService.saveBatch(corpUserSendRecords);
+				});
+			}
+		}
+	}
+
+	/**
+	 * 处理群发任务和标签的关系
+	 */
+	private void handleTaskTagRelation(CorpMsgBulkTask corpMsgBulkTask) throws Exception {
+		Long taskId = corpMsgBulkTask.getId();
+		List<CorpMsgBulkTaskTagRelation> relations = corpMsgBulkTaskTagRelationMapper.selectList(Wrappers.lambdaQuery(CorpMsgBulkTaskTagRelation.class)
+				.eq(CorpMsgBulkTaskTagRelation::getTaskId, taskId));
+		String chooseTagStr = corpMsgBulkTask.getChooseTagStr();
+		List<String> corpTagIds = Jsons.parseList(chooseTagStr, String.class);
+		if (CollUtil.isNotEmpty(relations)) {
+			//清除删除的关系
+			List<Long> del_relation_ids = new ArrayList<>();
+			relations.forEach(relation -> {
+				Boolean is_del = true;
+				for (String tagId : corpTagIds) {
+					if (tagId.equals(relation.getTagId())) {
+						is_del = false;
+						break;
+					}
+				}
+				if (is_del) {
+					del_relation_ids.add(relation.getId());
+				}
+			});
+			if (del_relation_ids.size() > 0) {
+				corpMsgBulkTaskTagRelationMapper.deleteBatchIds(del_relation_ids);
+			}
+		}
+		corpTagIds.forEach(tagId -> {
+			CorpMsgBulkTaskTagRelation tagRelation = corpMsgBulkTaskTagRelationMapper.selectOne(Wrappers.lambdaQuery(CorpMsgBulkTaskTagRelation.class)
+					.eq(CorpMsgBulkTaskTagRelation::getTaskId, taskId)
+					.eq(CorpMsgBulkTaskTagRelation::getTagId, tagId).last("limit 1"));
+			if (tagRelation == null) {
+				try {
+					tagRelation = new CorpMsgBulkTaskTagRelation();
+					tagRelation.setTagId(tagId);
+					tagRelation.setTaskId(taskId);
+					corpMsgBulkTaskTagRelationMapper.insert(tagRelation);
+				} catch (DuplicateKeyException e) {
+				}
+			}
+		});
+	}
+}

+ 79 - 1
netflix-service/src/main/java/com/cyksj/service/corp/impl/CorpServiceImpl.java

@@ -2,11 +2,16 @@ package com.cyksj.service.corp.impl;
 
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.date.DateTime;
+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.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.common.exception.BusinessRuntimeException;
 import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
 import com.cyksj.common.util.ListsUtil;
+import com.cyksj.common.util.StringUtil;
 import com.cyksj.common.util.Xmls;
 import com.cyksj.common.util.corp.WxCryptUtil;
 import com.cyksj.config.corp.WeChatCorpFactory;
@@ -20,9 +25,11 @@ import com.cyksj.mapper.corp.CorpUserMapper;
 import com.cyksj.model.dto.WxCorpEncryptDto;
 import com.cyksj.model.entity.*;
 import com.cyksj.model.manage.views.CorpWxOrderDonView;
+import com.cyksj.model.request.corp.Attachments;
 import com.cyksj.model.request.corp.CorpXmlMessage;
 import com.cyksj.model.request.corp.CorpXmlOutMessage;
-import com.cyksj.model.response.CorpUserInfo;
+import com.cyksj.model.response.corp.CorpMediaUploadResult;
+import com.cyksj.model.response.corp.CorpUserInfo;
 import com.cyksj.model.views.TicketView;
 import com.cyksj.model.views.YHLXUserDetailView;
 import com.cyksj.redis.RedisService;
@@ -37,11 +44,14 @@ import com.ejlchina.searcher.BeanSearcher;
 import com.ejlchina.searcher.param.Operator;
 import com.ejlchina.searcher.util.MapUtils;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.PostConstruct;
+import java.io.File;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -353,4 +363,72 @@ public class CorpServiceImpl implements CorpService {
 		}
 		return view;
 	}
+
+	@Override
+	public Attachments updateMaterial(Attachments attachments, String corpId, Integer type) {
+		if("image".equals(attachments.getMsgtype())) {
+			Attachments.Image image = attachments.getImage();
+			if(type == 1) {
+				image.setMediaId(wxCorpOps.getMediaIdByFileName(attachments.getMsgtype(),image.getPicUrl(),corpId));
+			} else {
+				image.setMediaId(this.uploadAttachmentAndGetMediaIdByFileName(attachments.getMsgtype(),image.getPicUrl(),corpId,1));
+			}
+
+		} else if("link".equals(attachments.getMsgtype())) {
+			// 链接不做处理
+//            Attachments.Link link = attachments.getLink();
+//            link.setMediaId(corpService.getMediaIdByFileName("image",link.getPicurl(),corpId));
+
+		} else if("video".equals(attachments.getMsgtype())) {
+			Attachments.Video video = attachments.getVideo();
+			if(type == 1) {
+				video.setMediaId(wxCorpOps.getMediaIdByFileName(attachments.getMsgtype(),video.getUrl(),corpId));
+			} else {
+				video.setMediaId(this.uploadAttachmentAndGetMediaIdByFileName(attachments.getMsgtype(),video.getUrl(),corpId,1));
+			}
+		} else if("miniprogram".equals(attachments.getMsgtype())) {
+			Attachments.MiniProgram miniProgram = attachments.getMiniProgram();
+			miniProgram.setPicMediaId(wxCorpOps.getMediaIdByFileName("image",miniProgram.getUrl(),corpId));
+		} else {
+			throw BusinessRuntimeException.getInstance("附件类型错误");
+		}
+		return attachments;
+	}
+
+	public String uploadAttachmentAndGetMediaIdByFileName(String msgType, String url, String corpId, Integer attachmentType) {
+		//临时解决
+		if ("miniprogram".equals(msgType)) {
+			msgType = "image";
+		}
+		String[] split = url.split("/");
+		String filename = split[split.length - 1];
+		String mediaId = redisService.getStr(String.format(RedisService.key.CORP_UPLOAD_ATTACHMENTS.getName(), corpId) + filename.split("\\.")[0]);
+
+		if (StringUtils.isBlank(mediaId)) {
+			File file = new File("./" + filename);
+			HttpUtil.downloadFile(url, file);
+
+			Map<String, Object> paramMap = new HashMap<>();
+			//文件上传只需将参数中的键指定(默认file),值设为文件对象即可,对于使用者来说,文件上传与普通表单提交并无区别
+			paramMap.put("media", FileUtil.file(file.getAbsolutePath()));
+
+			try {
+				String result = HttpUtil.post("https://qyapi.weixin.qq.com/cgi-bin/media/upload_attachment?media_type=" + msgType + "&access_token=" + wxCorpOps.getAccessToken(corpId) + "&attachment_type=" + attachmentType, paramMap);
+
+				CorpMediaUploadResult corpMediaUploadResult = JSONUtil.toBean(result, CorpMediaUploadResult.class);
+				if (StringUtils.isBlank(corpMediaUploadResult.getMediaId())) {
+					throw BusinessRuntimeException.getInstance("企微上传附件错误。回现素材为空.");
+				}
+				FileUtil.del(file);
+				long time = System.currentTimeMillis() / 1000 - (corpMediaUploadResult.getCreatedAt() + 60 * 60 * 24 * 3 - 60 * 60);
+				redisService.set(String.format(RedisService.key.CORP_UPLOAD_ATTACHMENTS.getName(), corpId) + filename, corpMediaUploadResult.getMediaId(), time);
+				return corpMediaUploadResult.getMediaId();
+			} catch (Exception e) {
+				FileUtil.del(file);
+				throw BusinessRuntimeException.getInstance("上传临时附件出错,errorMsg:" + StringUtil.getErrorText(e));
+			}
+		}
+
+		return mediaId;
+	}
 }

+ 378 - 0
netflix-service/src/main/java/com/cyksj/service/corp/impl/CorpTagServiceImpl.java

@@ -0,0 +1,378 @@
+package com.cyksj.service.corp.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONObject;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.util.Jsons;
+import com.cyksj.mapper.UserMapper;
+import com.cyksj.mapper.corp.*;
+import com.cyksj.model.entity.*;
+import com.cyksj.model.excel.CorpUserTagExcelData;
+import com.cyksj.model.request.CorpDelTagReq;
+import com.cyksj.model.response.corp.CorpUserExternalTagGroupInfo;
+import com.cyksj.model.views.CorpTagBackView;
+import com.cyksj.model.views.CorpTagGroupView;
+import com.cyksj.model.views.CorpTagHeadDataView;
+import com.cyksj.model.views.CorpUserView;
+import com.cyksj.service.corp.CorpTagService;
+import com.cyksj.service.corp.WxCorpOps;
+import com.google.common.collect.Maps;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+/*
+ *项目名: netflix
+ *文件名: CorpTagServiceImpl
+ *创建者: JavaZou
+ *创建时间:2023/6/20 16:06
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Service
+public class CorpTagServiceImpl implements CorpTagService {
+	private final WxCorpOps wxCorpOps;
+
+	private final CorpTagMapper corpTagMapper;
+
+	private final CorpTagGroupMapper corpTagGroupMapper;
+
+	private final CorpTagRuleRelationMapper corpTagRuleRelationMapper;
+
+	private final CorpTagRuleMapper corpTagRuleMapper;
+
+	private final UserMapper userMapper;
+
+	private final CorpUserTagMapper corpUserTagMapper;
+
+	@Override
+	@Transactional(rollbackFor = Throwable.class)
+	public CorpTagGroupView addCorpTagGroup(CorpTagGroupView tagGroup) throws Exception {
+		CorpUserExternalTagGroupInfo tagGroupInfoReq = new CorpUserExternalTagGroupInfo();
+
+		CorpUserExternalTagGroupInfo.TagGroup group = new CorpUserExternalTagGroupInfo.TagGroup();
+		group.setGroupId(tagGroup.getGroupId());
+		group.setGroupName(tagGroup.getGroupName());
+		Map<String, CorpTag> corpTagMap = new ConcurrentHashMap<>();
+		if (CollUtil.isEmpty(tagGroup.getTagList())) {
+			throw BusinessRuntimeException.getInstance("标签不能为空");
+		}
+		List<CorpUserExternalTagGroupInfo.Tag> tagList = tagGroup.getTagList().stream().map((corpTag) -> {
+			if (StrUtil.isEmpty(corpTag.getGroupName())) {
+				corpTag.setGroupName(tagGroup.getGroupName());
+			}
+			CorpUserExternalTagGroupInfo.Tag tag = new CorpUserExternalTagGroupInfo.Tag();
+			tag.setName(corpTag.getName());
+			CorpTag exists = corpTagMapper.selectOne(Wrappers.lambdaQuery(CorpTag.class)
+					.eq(CorpTag::getGroupName, corpTag.getGroupName())
+					.eq(CorpTag::getName, corpTag.getName()).last("limit 1"));
+			if (exists != null) throw BusinessRuntimeException.getInstance("标签组下重复标签");
+			corpTagMap.putIfAbsent(corpTag.getName(), corpTag);
+			return tag;
+		}).collect(Collectors.toList());
+		group.setTag(tagList);
+		tagGroupInfoReq.setTagGroup(group);
+		CorpUserExternalTagGroupInfo corpUserExternalTagGroupInfo = wxCorpOps.addCorpTag(tagGroupInfoReq, null);
+
+		CorpUserExternalTagGroupInfo.TagGroup corp_tagGroup = corpUserExternalTagGroupInfo.getTagGroup();
+
+		corpUserExternalTagGroupInfo.getTagGroup().getTag().forEach(tag -> {
+			try {
+				CorpTag corpTag = corpTagMap.get(tag.getName());
+				if (corpTag == null) return;
+				corpTag.setGroupId(corp_tagGroup.getGroupId());
+				corpTag.setGroupName(corp_tagGroup.getGroupName());
+				corpTag.setOrdered(tag.getOrder());
+				corpTag.setTagId(tag.getId());
+				corpTag.setName(tag.getName());
+				if (CollUtil.isNotEmpty(corpTag.getRuleNameList())) {
+					try {
+						corpTag.setRuleNameListStr(Jsons.toJson(corpTag.getRuleNameList()));
+					} catch (Exception e) {
+					}
+				}
+				//对应标签入库
+				corpTagMapper.insert(corpTag);
+				if (corpTag.getType() == 2) {
+					corpTag.getRuleRelationList().forEach(data -> {
+						data.setTagId(corpTag.getId());
+						corpTagRuleRelationMapper.insert(data);
+					});
+				}
+				CorpTagGroup corpTagGroup = Optional.ofNullable(corpTagGroupMapper.selectOne(Wrappers.lambdaQuery(CorpTagGroup.class)
+						.eq(CorpTagGroup::getGroupId, corp_tagGroup.getGroupId()).last("limit 1")))
+						.orElseGet(() -> {
+							CorpTagGroup corpTagGroup2 = new CorpTagGroup();
+							corpTagGroup2.setGroupId(corp_tagGroup.getGroupId());
+							corpTagGroup2.setGroupName(corp_tagGroup.getGroupName());
+							corpTagGroup2.setOrdered(corp_tagGroup.getOrder());
+							return corpTagGroup2;
+						});
+				if (corpTagGroup.getId() == null) corpTagGroupMapper.insert(corpTagGroup);
+				corpTagGroupMapper.updateById(corpTagGroup);
+			} catch (DuplicateKeyException e) {
+
+			}
+		});
+		CorpTagGroupView corpTagGroupView = corpTagGroupMapper.getOneCorpGroupTag(tagGroup.getGroupId(), tagGroup.getGroupName());
+		return corpTagGroupView;
+	}
+
+	@Override
+	@Transactional(rollbackFor = Throwable.class)
+	public void editCorpTag(CorpTagGroupView tagGroup) throws Exception {
+		String groupId = tagGroup.getGroupId();
+		CorpTagGroup corpTagGroup = corpTagGroupMapper.selectOne(Wrappers.lambdaQuery(CorpTagGroup.class)
+				.eq(CorpTagGroup::getGroupId, groupId).last("limit 1"));
+		if (corpTagGroup != null && !StrUtil.equals(tagGroup.getGroupName(), corpTagGroup.getGroupName())) {
+			//编辑标签组名称
+			JSONObject params = new JSONObject();
+			params.putOpt("id", tagGroup.getGroupId());
+			params.putOpt("name", tagGroup.getGroupName());
+			wxCorpOps.editCorpTag(params);
+
+			corpTagGroup.setGroupName(tagGroup.getGroupName());
+			corpTagGroupMapper.updateById(corpTagGroup);
+		}
+
+		List<CorpUserExternalTagGroupInfo.Tag> newTagsList = new ArrayList<>();
+		Map<String, CorpTag> corpTagMap = new ConcurrentHashMap<>();
+		for (CorpTag tagDto : tagGroup.getTagList()) {
+			Long corpTagId = tagDto.getId();
+			if (CollUtil.isNotEmpty(tagDto.getRuleNameList())) {
+				tagDto.setRuleNameListStr(Jsons.toJson(tagDto.getRuleNameList()));
+			}
+			if (corpTagId != null) {
+				CorpTag dbCorpTag = corpTagMapper.getCorpTagById(corpTagId);
+				if (dbCorpTag != null) {
+					if (!StrUtil.equals(dbCorpTag.getName(), tagDto.getName())) {
+						JSONObject params = new JSONObject();
+						params.putOpt("id", tagDto.getTagId());
+						params.putOpt("name", tagDto.getName());
+						wxCorpOps.editCorpTag(params);
+
+						dbCorpTag.setName(tagDto.getName());
+						corpTagMapper.updateById(dbCorpTag);
+					}
+
+					List<CorpTagRuleRelation> ruleRelationList = dbCorpTag.getRuleRelationList();
+
+					List<CorpTagRuleRelation> updateRuleRelationList = tagDto.getRuleRelationList();
+					Map<Long, List<CorpTagRuleRelation>> collect = null;
+					if (CollUtil.isNotEmpty(updateRuleRelationList)) {
+						collect = updateRuleRelationList.stream().filter(dto -> {
+							if (dto.getId() == null) {
+								dto.setTagId(tagDto.getId());
+								corpTagRuleRelationMapper.insert(dto);
+								return false;
+							}
+							return true;
+						}).collect(Collectors.groupingBy(CorpTagRuleRelation::getId));
+					}
+
+					final Map<Long, List<CorpTagRuleRelation>> updateMap = Optional.ofNullable(collect).orElse(Maps.newHashMap());
+
+					if (CollUtil.isNotEmpty(ruleRelationList)) {
+						ruleRelationList.forEach(dbRule -> {
+							List<CorpTagRuleRelation> existsRules = updateMap.get(dbRule.getId());
+							if (CollUtil.isEmpty(existsRules)) {
+								corpTagRuleRelationMapper.deleteById(dbRule.getId());
+								return;
+							}
+							corpTagRuleRelationMapper.updateById(existsRules.get(0));
+						});
+					}
+				}
+				corpTagMapper.updateById(tagDto);
+				continue;
+			}
+
+			if (StrUtil.isEmpty(tagDto.getGroupName())) {
+				tagDto.setGroupName(tagGroup.getGroupName());
+			}
+			CorpTag exists = corpTagMapper.selectOne(Wrappers.lambdaQuery(CorpTag.class)
+					.eq(CorpTag::getGroupName, tagDto.getGroupName())
+					.eq(CorpTag::getName, tagDto.getName()).last("limit 1"));
+			if (exists != null) throw BusinessRuntimeException.getInstance("标签组下重复标签");
+			corpTagMap.putIfAbsent(tagDto.getName(), tagDto);
+			CorpUserExternalTagGroupInfo.Tag newTag = new CorpUserExternalTagGroupInfo.Tag();
+			newTag.setName(tagDto.getName());
+			newTagsList.add(newTag);
+		}
+
+		//添加新标签
+		if (newTagsList.size() > 0) {
+			CorpUserExternalTagGroupInfo tagGroupInfoReq = new CorpUserExternalTagGroupInfo();
+			CorpUserExternalTagGroupInfo.TagGroup group = new CorpUserExternalTagGroupInfo.TagGroup();
+			group.setGroupId(tagGroup.getGroupId());
+			group.setGroupName(tagGroup.getGroupName());
+			group.setTag(newTagsList);
+			tagGroupInfoReq.setTagGroup(group);
+			CorpUserExternalTagGroupInfo corpUserExternalTagGroupInfo = wxCorpOps.addCorpTag(tagGroupInfoReq, null);
+
+			CorpUserExternalTagGroupInfo.TagGroup corp_tagGroup = corpUserExternalTagGroupInfo.getTagGroup();
+			corpUserExternalTagGroupInfo.getTagGroup().getTag().forEach(tag -> {
+				try {
+					CorpTag corpTag = corpTagMap.get(tag.getName());
+					if (corpTag == null) return;
+					corpTag.setGroupId(corp_tagGroup.getGroupId());
+					corpTag.setGroupName(corp_tagGroup.getGroupName());
+					corpTag.setOrdered(tag.getOrder());
+					corpTag.setTagId(tag.getId());
+					corpTag.setName(tag.getName());
+					if (CollUtil.isNotEmpty(corpTag.getRuleNameList())) {
+						try {
+							corpTag.setRuleNameListStr(Jsons.toJson(corpTag.getRuleNameList()));
+						} catch (Exception e) {
+						}
+					}
+					//对应标签入库
+					corpTagMapper.insert(corpTag);
+					if (corpTag.getType() == 2) {
+						corpTag.getRuleRelationList().forEach(data -> {
+							data.setTagId(corpTag.getId());
+							corpTagRuleRelationMapper.insert(data);
+						});
+					}
+				} catch (DuplicateKeyException e) {
+
+				}
+			});
+		}
+	}
+
+	@Override
+	public List<CorpTagGroupView> getCorpTagList() {
+		List<CorpTagGroupView> tagGroupList = corpTagGroupMapper.getCorpTagList();
+		tagGroupList.forEach(tagGrous -> {
+			tagGrous.getTagList().forEach(tag -> {
+				if (tag.getType() == 2) {
+					Optional.ofNullable(corpTagRuleRelationMapper.selectList(Wrappers.lambdaQuery(CorpTagRuleRelation.class)
+							.eq(CorpTagRuleRelation::getTagId, tag.getId())))
+							.ifPresent(rules -> {
+								tag.setRuleRelationList(rules);
+								if (StrUtil.isNotEmpty(tag.getRuleNameListStr())) {
+									try {
+										tag.setRuleNameList(Jsons.parseList(tag.getRuleNameListStr(), String.class));
+									} catch (Exception e) {
+									}
+								}
+							});
+				}
+			});
+		});
+		return tagGroupList;
+	}
+
+	@Override
+	public void saveOrUpdateRule(CorpTagRule rule) {
+		Long id = rule.getId();
+		if (id == null) corpTagRuleMapper.insert(rule);
+		corpTagRuleMapper.updateById(rule);
+	}
+
+	@Override
+	public List<CorpTagBackView> getCorpTagBackView(Integer type, String[] tagIds, String[] groupIds, String sort, String order) {
+		if (type == 1) {
+			List<CorpTagBackView> corpTagBackView = corpTagMapper.getCorpTagBackView(tagIds, sort, order);
+			corpTagBackView.forEach(view -> {
+				if (StrUtil.isNotEmpty(view.getRuleNameListStr())) {
+					try {
+						view.setRuleNameList(Jsons.parseList(view.getRuleNameListStr(), String.class));
+					} catch (Exception e) {
+					}
+				}
+			});
+			return corpTagBackView;
+		}
+		return corpTagGroupMapper.getCorpTagBackView(groupIds, sort, order);
+	}
+
+	@Override
+	public List<CorpTag> getGroupTagDetail(String groupId) {
+		List<CorpTag> corpTags = corpTagMapper.selectList(Wrappers.lambdaQuery(CorpTag.class).eq(CorpTag::getGroupId, groupId));
+		return corpTags;
+	}
+
+	@Override
+	public Page<CorpUserView> getCorpUserDetail(String tagId, String groupId, Integer start, Integer limit) {
+		Long wxCorpId = 2l;
+		Page<CorpUserView> page = corpUserTagMapper.selectCorpUserDetailByCondition(new Page<>(start, limit), groupId, tagId, wxCorpId);
+		page.getRecords().forEach(data -> {
+			Optional.ofNullable(userMapper.selectOne(Wrappers.lambdaQuery(User.class)
+					.eq(User::getUnionid, data.getUnionId())
+					.last("limit 1")))
+					.ifPresent(user -> {
+						data.setUserId(user.getId());
+						data.setNickname(user.getNickname());
+						data.setHeadimgurl(user.getHeadimgurl());
+					});
+		});
+		return page;
+	}
+
+	@Override
+	public List<CorpUserTagExcelData> exportUserTagDetail(String[] tagIds, String[] groupIds) {
+		return corpUserTagMapper.exportUserTagDetail(tagIds, groupIds);
+	}
+
+	@Override
+	public List<CorpTagRuleRelation> getCorpTagRuleDetail(Long tagId) {
+		List<CorpTagRuleRelation> ruleRelationList = corpTagRuleRelationMapper.selectList(Wrappers.lambdaQuery(CorpTagRuleRelation.class)
+				.eq(CorpTagRuleRelation::getTagId, tagId));
+		return ruleRelationList;
+	}
+
+	@Override
+	public CorpTagHeadDataView getCorpTagHeadDataView() {
+		CorpTagHeadDataView dataView = corpTagMapper.getCorpTagHeadDataView();
+		return dataView;
+	}
+
+	@Override
+	@Transactional(rollbackFor = Throwable.class)
+	public void delTags(CorpDelTagReq req) throws Exception {
+		String tagId = req.getTagId();
+		String groupId = req.getGroupId();
+		if (StrUtil.isEmpty(tagId) && StrUtil.isEmpty(groupId)) {
+			throw BusinessRuntimeException.getInstance("请选择指定要删除的标签或组");
+		}
+		String[] tagIds = null;
+		String[] groupIds = null;
+		if (StrUtil.isNotEmpty(tagId)) {
+			List<CorpTag> corpTags = corpTagMapper.selectList(Wrappers.lambdaQuery(CorpTag.class)
+					.eq(CorpTag::getTagId, tagId));
+			tagIds = corpTags.stream().filter(tag -> StrUtil.isNotBlank(tag.getTagId())).map(CorpTag::getTagId).toArray(String[]::new);
+		}
+
+		if (StrUtil.isNotEmpty(groupId)) {
+			List<CorpTagGroup> corpTagGroups = corpTagGroupMapper.selectList(Wrappers.lambdaQuery(CorpTagGroup.class)
+					.eq(CorpTagGroup::getGroupId, groupId));
+			groupIds = corpTagGroups.stream().filter(group -> StrUtil.isNotBlank(group.getGroupId())).map(CorpTagGroup::getGroupId).toArray(String[]::new);
+		}
+
+		if (tagIds != null || groupIds != null) {
+			wxCorpOps.delCorpTag(tagIds, groupIds);
+		}
+		if (StrUtil.isNotBlank(tagId)) {
+			corpTagMapper.delete(Wrappers.lambdaQuery(CorpTag.class).eq(CorpTag::getTagId, tagId));
+		}
+
+		if (StrUtil.isNotBlank(groupId)) {
+			corpTagGroupMapper.delete(Wrappers.lambdaQuery(CorpTagGroup.class).eq(CorpTagGroup::getGroupId, groupId));
+			corpTagMapper.delete(Wrappers.lambdaQuery(CorpTag.class).eq(CorpTag::getGroupId, groupId));
+		}
+	}
+}

+ 18 - 0
netflix-service/src/main/java/com/cyksj/service/corp/impl/CorpUserSendRecordServiceImpl.java

@@ -0,0 +1,18 @@
+package com.cyksj.service.corp.impl;
+
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.cyksj.mapper.corp.CorpUserSendRecordMapper;
+import com.cyksj.model.entity.CorpUserSendRecord;
+import com.cyksj.service.corp.CorpUserSendRecordService;
+import org.springframework.stereotype.Service;
+
+/**
+ * @ClassName CorpUserSendRecordServiceImpl
+ * @Author Huang yuanzhi
+ * @Date 2023/4/20 17:29
+ * @Version 1.0.0
+ **/
+@Service
+public class CorpUserSendRecordServiceImpl extends ServiceImpl<CorpUserSendRecordMapper, CorpUserSendRecord> implements CorpUserSendRecordService {
+}

+ 227 - 1
netflix-service/src/main/java/com/cyksj/service/corp/impl/WxCorpOpsImpl.java

@@ -1,17 +1,26 @@
 package com.cyksj.service.corp.impl;
 
+import cn.hutool.core.io.FileUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpUtil;
 import cn.hutool.json.JSONObject;
+import cn.hutool.json.JSONUtil;
 import com.cyksj.common.exception.BusinessRuntimeException;
 import com.cyksj.common.util.IoKit;
 import com.cyksj.common.util.J11HttpC;
 import com.cyksj.common.util.Jsons;
+import com.cyksj.common.util.StringUtil;
 import com.cyksj.config.corp.WeChatCorpFactory;
 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.response.*;
+import com.cyksj.model.request.corp.CorpMsgTemplate;
+import com.cyksj.model.response.OffiaccountJsTicket;
+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.redis.RedisService;
 import com.cyksj.service.corp.WxCorpOps;
 import lombok.extern.slf4j.Slf4j;
@@ -19,10 +28,14 @@ import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.io.File;
 import java.net.URLEncoder;
 import java.net.http.HttpRequest;
 import java.net.http.HttpResponse;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Optional;
 
 /*
  *项目名: netflix
@@ -57,6 +70,7 @@ public class WxCorpOpsImpl implements WxCorpOps {
 
 	@Override
 	public String getAccessToken(String corpId) throws Exception {
+		corpId = Optional.ofNullable(corpId).orElse(yhlXWxCorpConfig.getCorpId());
 		String accessToken = redisService.getStr(RedisKey.CORP_ACCESS_TOKEN_KEY + corpId);
 		if (StringUtils.isNotBlank(accessToken)) {
 			return accessToken;
@@ -185,6 +199,218 @@ public class WxCorpOpsImpl implements WxCorpOps {
 		return ticket;
 	}
 
+	@Override
+	public CorpUserExternalTagGroupInfo addCorpTag(CorpUserExternalTagGroupInfo tagGroup, String corpId) throws Exception {
+		corpId = Optional.ofNullable(corpId).orElse(yhlXWxCorpConfig.getCorpId());
+		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/add_corp_tag?access_token=" + getAccessToken(corpId);
+
+		HttpResponse<String> res =
+				J11HttpC.custom()
+						.ofPost()
+						.url(url)
+						.headers(J11HttpC.ReqType.raw_json)
+						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(tagGroup.getTagGroup()), IoKit.Charsets.UTF_8.getCharset()))
+						.send(HttpResponse.BodyHandlers.ofString());
+
+		if (200 != res.statusCode()) {
+			throw BusinessRuntimeException.getInstance("企业微信添加企业客户标签错误: " + res.body());
+		}
+
+		CorpUserExternalTagGroupInfo corpUserExternalTagGroupInfo = Jsons.parseObject(res.body(), CorpUserExternalTagGroupInfo.class);
+		if (corpUserExternalTagGroupInfo.getErrcode() != 0) {
+			throw BusinessRuntimeException.getInstance("新增标签错误:{0}", corpUserExternalTagGroupInfo.getErrmsg());
+		}
+
+		return corpUserExternalTagGroupInfo;
+	}
+
+	@Override
+	public List<CorpUserExternalTagGroupInfo.TagGroup> getAllCorpTagByGroupsId(String groupId, String corpId) throws Exception {
+		corpId = Optional.ofNullable(corpId).orElse(yhlXWxCorpConfig.getCorpId());
+		//获取该标签组下的所有标签
+		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/get_corp_tag_list?access_token=" + getAccessToken(corpId);
+		JSONObject jsonObject = new JSONObject();
+		jsonObject.putOpt("group_id", List.of(groupId));
+		HttpResponse<String> res = J11HttpC.custom()
+				.ofPost()
+				.url(url)
+				.headers(J11HttpC.ReqType.raw_json)
+				.body(HttpRequest.BodyPublishers.ofString(jsonObject.toString(), IoKit.Charsets.UTF_8.getCharset()))
+				.send(HttpResponse.BodyHandlers.ofString());
+		JSONObject re = Jsons.parseObject(res.body(), JSONObject.class);
+		List<CorpUserExternalTagGroupInfo.TagGroup> tagGroups = Jsons.parseList(re.get("tag_group"), CorpUserExternalTagGroupInfo.TagGroup.class);
+		if (re.getInt("errcode") != 0) {
+			throw BusinessRuntimeException.getInstance("获取标签组下所有标签错误");
+		}
+		return tagGroups;
+	}
+
+	@Override
+	public CorpBaseResult editCorpTag(JSONObject params) throws Exception {
+		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/edit_corp_tag?access_token=" + getAccessToken(null);
+
+		HttpResponse<String> res =
+				J11HttpC.custom()
+						.ofPost()
+						.url(url)
+						.headers(J11HttpC.ReqType.raw_json)
+						.body(HttpRequest.BodyPublishers.ofString(params.toString(), IoKit.Charsets.UTF_8.getCharset()))
+						.send(HttpResponse.BodyHandlers.ofString());
+
+		if (200 != res.statusCode()) {
+			throw BusinessRuntimeException.getInstance("企业微信编辑企业客户标签错误: " + res.body());
+		}
+
+		CorpBaseResult corpBaseResult = Jsons.parseObject(res.body(), CorpBaseResult.class);
+		if (corpBaseResult.getErrcode() != 0) {
+			throw BusinessRuntimeException.getInstance("修改标签错误:{0}", corpBaseResult.getErrmsg());
+		}
+		return corpBaseResult;
+	}
+
+	@Override
+	public CorpBaseResult delCorpTag(String[] tagIds, String[] groupIds) throws Exception {
+		JSONObject params = new JSONObject();
+		if (tagIds != null && tagIds.length > 0) {
+			params.putOpt("tag_id", tagIds);
+		}
+		if (groupIds != null && groupIds.length > 0) {
+			params.putOpt("group_id", groupIds);
+		}
+		//https://qyapi.weixin.qq.com/cgi-bin/externalcontact/del_corp_tag?access_token=ACCESS_TOKEN
+
+		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/del_corp_tag?access_token=" + getAccessToken(null);
+
+		HttpResponse<String> res =
+				J11HttpC.custom()
+						.ofPost()
+						.url(url)
+						.headers(J11HttpC.ReqType.raw_json)
+						.body(HttpRequest.BodyPublishers.ofString(params.toString(), IoKit.Charsets.UTF_8.getCharset()))
+						.send(HttpResponse.BodyHandlers.ofString());
+
+		if (200 != res.statusCode()) {
+			throw BusinessRuntimeException.getInstance("企业微信删除企业客户标签错误: " + res.body());
+		}
+
+		CorpUserExternalTagGroupInfo corpUserExternalTagGroupInfo = Jsons.parseObject(res.body(), CorpUserExternalTagGroupInfo.class);
+		if (corpUserExternalTagGroupInfo.getErrcode() != 0) {
+			throw BusinessRuntimeException.getInstance("企业微信删除企业客户标签错误:{0}", corpUserExternalTagGroupInfo.getErrmsg());
+		}
+
+		return Jsons.parseObject(res.body(), CorpBaseResult.class);
+	}
+
+	@Override
+	public CorpBaseResult markTag(String externalUserid, String userid, String[] tags, String[] removeTags) throws Exception {
+		JSONObject params = new JSONObject();
+		params.putOpt("external_userid", externalUserid);
+		params.putOpt("userid", userid);
+		if (tags != null && tags.length > 0) {
+			params.putOpt("add_tag", tags);
+		}
+		if (removeTags != null && removeTags.length > 0) {
+			params.putOpt("remove_tag", removeTags);
+		}
+		//https://qyapi.weixin.qq.com/cgi-bin/externalcontact/del_corp_tag?access_token=ACCESS_TOKEN
+
+		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/mark_tag?access_token=" + getAccessToken(null);
+
+		HttpResponse<String> res =
+				J11HttpC.custom()
+						.ofPost()
+						.url(url)
+						.headers(J11HttpC.ReqType.raw_json)
+						.body(HttpRequest.BodyPublishers.ofString(params.toString(), IoKit.Charsets.UTF_8.getCharset()))
+						.send(HttpResponse.BodyHandlers.ofString());
+
+		if (200 != res.statusCode()) {
+			throw BusinessRuntimeException.getInstance("企业微信编辑企业客户标签错误: " + res.body());
+		}
+
+		return Jsons.parseObject(res.body(), CorpBaseResult.class);
+	}
+
+	@Override
+	public CorpMsgTemplateAddResult addMsgTemplate(String corpId, CorpMsgTemplate msgTemplate) throws Exception {
+		corpId = Optional.ofNullable(corpId).orElse(yhlXWxCorpConfig.getCorpId());
+		String accessToken = getAccessToken(corpId);
+		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/add_msg_template?access_token=" + accessToken;
+		log.info("发送的内容为: {}",Jsons.toJson(msgTemplate));
+		log.info("企业微信群发消息 corpId:{}, sender:{},userCount:{}",corpId,msgTemplate.getSender(),msgTemplate.getExternalUserid().size());
+		HttpResponse<String> res =
+				J11HttpC.custom()
+						.ofPost()
+						.url(url)
+						.headers(J11HttpC.ReqType.raw_json)
+						.body(HttpRequest.BodyPublishers.ofString(Jsons.toJson(msgTemplate), IoKit.Charsets.UTF_8.getCharset()))
+						.send(HttpResponse.BodyHandlers.ofString());
+		if (200 != res.statusCode()) {
+			throw BusinessRuntimeException.getInstance("企业微信创建企业群发错误: " + res.body());
+		}
+		log.info("企业微信创建群发 result:{}",res.body());
+
+		return Jsons.parseObject(res.body(),CorpMsgTemplateAddResult.class);
+	}
+
+	@Override
+	public String getMediaIdByFileName(String msgType, String url, String corpId) {
+		//临时解决
+		if ("miniprogram".equals(msgType)) {
+			msgType = "image";
+		}
+		String[] split = url.split("/");
+		String filename = split[split.length - 1];
+		String mediaId = redisService.getStr(String.format(RedisService.key.CORP_WECLOME_MEDIA.getName(), corpId) + filename.split("\\.")[0]);
+
+		if (StringUtils.isBlank(mediaId)) {
+			File file = new File("./" + filename);
+			HttpUtil.downloadFile(url, file);
+
+			Map<String, Object> paramMap = new HashMap<>();
+			//文件上传只需将参数中的键指定(默认file),值设为文件对象即可,对于使用者来说,文件上传与普通表单提交并无区别
+			paramMap.put("media", FileUtil.file(file.getAbsolutePath()));
+
+			try {
+				String result = HttpUtil.post("https://qyapi.weixin.qq.com/cgi-bin/media/upload?type=" + msgType + "&access_token=" + getAccessToken(corpId), paramMap);
+
+				CorpMediaUploadResult corpMediaUploadResult = JSONUtil.toBean(result, CorpMediaUploadResult.class);
+				if (StringUtils.isBlank(corpMediaUploadResult.getMediaId())) {
+					throw BusinessRuntimeException.getInstance("企微上传素材错误。回现素材为空.");
+				}
+				FileUtil.del(file);
+				long time = (corpMediaUploadResult.getCreatedAt() + 60 * 60 * 24 * 3 - 60 * 60) - System.currentTimeMillis() / 1000;
+				redisService.set(String.format(RedisService.key.CORP_WECLOME_MEDIA.getName(), corpId) + filename, corpMediaUploadResult.getMediaId(), time);
+				return corpMediaUploadResult.getMediaId();
+			} catch (Exception e) {
+				FileUtil.del(file);
+				throw BusinessRuntimeException.getInstance("上传临时素材出错,errorMsg:" + StringUtil.getErrorText(e));
+			}
+		}
+
+		return mediaId;
+	}
+
+	@Override
+	public CorpBaseResult remindBulkTask(String msgid, String corpId) throws Exception {
+		String url = "https://qyapi.weixin.qq.com/cgi-bin/externalcontact/remind_groupmsg_send?access_token=" + getAccessToken(corpId);
+		log.info("提醒客服群发消息msgId:{}", msgid);
+		JSONObject json = new JSONObject();
+		json.putOpt("msgid", msgid);
+		HttpResponse<String> res =
+				J11HttpC.custom()
+						.ofPost()
+						.url(url)
+						.headers(J11HttpC.ReqType.raw_json)
+						.body(HttpRequest.BodyPublishers.ofString(json.toString(), IoKit.Charsets.UTF_8.getCharset()))
+						.send(HttpResponse.BodyHandlers.ofString());
+		if (200 != res.statusCode()) {
+			throw BusinessRuntimeException.getInstance("提醒客服群发消息错误: " + res.body());
+		}
+		log.info("提醒客服群发消息 result:{}", res.body());
+		return Jsons.parseObject(res.body(), CorpBaseResult.class);
+	}
+
 	public String access_token(String corpId) throws Exception {
 		WxCorpApp wxCorpApp = WeChatCorpFactory.getCorpConfigStrategy(corpId);
 		if (corpId == null) {

+ 27 - 0
netflix-service/src/main/java/com/cyksj/service/mange/statistics/StatisticsDataDayService.java

@@ -0,0 +1,27 @@
+package com.cyksj.service.mange.statistics;
+
+import com.cyksj.model.entity.*;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/*
+ *项目名: netflix
+ *文件名: StatisticsDataDayService
+ *创建者: JavaZou
+ *创建时间:2023/7/6 13:38
+ */
+public interface StatisticsDataDayService {
+	List<StatisticsFashionGoodsData> statisticsFashionGoodsData(Date yesZeroDate, Date thisZeroDate, Boolean today);
+
+	List<RegisterAccountData> statisticsRegisterAccountData(Date yesZeroDate, Date thisZeroDate, Boolean today);
+
+	List<AppleOneData> statisticsAppleOneData(Date yesZeroDate, Date thisZeroDate, Boolean today);
+
+	List<StreamingAccountData> statisticsStreamingAccountData(Date yesZeroDate, Date thisZeroDate, Boolean today);
+
+	List<RealGoodsData> statisticsRealGoodsData(Date yesZeroDate, Date thisZeroDate, Boolean today);
+
+	Map<String, List<GoodsDonSku>> getAppleOneMap();
+}

+ 371 - 0
netflix-service/src/main/java/com/cyksj/service/mange/statistics/impl/StatisticsDataDayServiceImpl.java

@@ -0,0 +1,371 @@
+package com.cyksj.service.mange.statistics.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.collection.CollUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.common.constant.Constant;
+import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
+import com.cyksj.mapper.GoodsDonMapper;
+import com.cyksj.mapper.RegisterAccountDataMapper;
+import com.cyksj.mapper.RegisterOrderDonMapper;
+import com.cyksj.mapper.corp.CorpUserAuthMapper;
+import com.cyksj.mapper.manage.statistics.*;
+import com.cyksj.model.entity.*;
+import com.cyksj.model.views.CorpUserTagView;
+import com.cyksj.model.views.StatisticsGoodsDataView;
+import com.cyksj.service.mange.CmsAccountService;
+import com.cyksj.service.mange.CmsGoodsDonSkuService;
+import com.cyksj.service.mange.statistics.StatisticsDataDayService;
+import com.cyksj.service.order.OrderDonService;
+import com.ejlchina.searcher.BeanSearcher;
+import com.ejlchina.searcher.util.MapBuilder;
+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.util.*;
+import java.util.stream.Collectors;
+
+/*
+ *项目名: netflix
+ *文件名: StatisticsDataDayServiceImpl
+ *创建者: JavaZou
+ *创建时间:2023/7/6 13:40
+ */
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class StatisticsDataDayServiceImpl implements StatisticsDataDayService {
+	private final OrderDonService orderDonService;
+
+	private final CmsGoodsDonSkuService goodsDonSkuService;
+
+	private final CmsAccountService accountService;
+
+	private static final GlobalThreadPoolTaskExecutor TASK_POOL = GlobalThreadPoolTaskExecutor.getInstance();
+
+	private final AppleOneDataMapper appleOneDataMapper;
+
+	private final StreamingAccountDataMapper streamingAccountDataMapper;
+
+	private final GoodsDonMapper goodsDonMapper;
+
+	private final RealGoodsDataMapper realGoodsDataMapper;
+
+	private final RegisterOrderDonMapper registerOrderDonMapper;
+
+	private final RegisterAccountDataMapper registerAccountDataMapper;
+
+	private final StatisticsUserDataMapper statisticsUserDataMapper;
+
+	private final StatisticsFashionGoodsDataMapper statisticsFashionGoodsDataMapper;
+
+	private final BeanSearcher beanSearcher;
+
+	private final CorpUserAuthMapper corpUserAuthMapper;
+
+	public List<StatisticsFashionGoodsData> statisticsFashionGoodsData(Date yesZeroDate, Date thisZeroDate, Boolean today) {
+		//潮玩好物
+		List<GoodsDon> goodsDons = goodsDonMapper.selectList(Wrappers.lambdaQuery(GoodsDon.class).eq(GoodsDon::getType, Constant.realGoodsType.get(1)));
+		List<StatisticsFashionGoodsData> fashionGoodsData = new ArrayList<>();
+		goodsDons.forEach(goods -> {
+			List<StatisticsGoodsDataView> goodsDataViews = beanSearcher.searchAll(StatisticsGoodsDataView.class, MapUtils.builder()
+					.field(StatisticsGoodsDataView::getGoodsId, goods.getId())
+					.put("time", String.format("od.created_time between '%s' and '%s'", yesZeroDate, thisZeroDate))
+					.build());
+			goodsDataViews.forEach(goodsDataView -> {
+				StatisticsFashionGoodsData statisticsFashionGoodsData = new StatisticsFashionGoodsData();
+				BeanUtil.copyProperties(goodsDataView, statisticsFashionGoodsData);
+				fashionGoodsData.add(statisticsFashionGoodsData);
+			});
+		});
+		if (today) {
+			return fashionGoodsData;
+		}
+		if (!fashionGoodsData.isEmpty()) {
+			fashionGoodsData.forEach(data -> {
+				data.setStatisticsDate(yesZeroDate);
+				statisticsFashionGoodsDataMapper.insert(data);
+			});
+		}
+		return null;
+	}
+
+	/**
+	 * 用户数据统计
+	 */
+	private void statisticsUserData(Date yesZeroDate, Date thisZeroDate) {
+		StatisticsUserData yesData = statisticsUserDataMapper.selectOne(Wrappers.lambdaQuery(StatisticsUserData.class).orderByDesc(StatisticsUserData::getId)
+				.lt(StatisticsUserData::getStatisticsDate, yesZeroDate)
+				.select(StatisticsUserData::getNumOfUser, StatisticsUserData::getPcNumOfUser).last("limit 1"));
+		Integer yesNumOfUser = yesData == null ? 0 : yesData.getNumOfUser();
+		Integer yesPcNumOfUser = yesData == null ? 0 : yesData.getPcNumOfUser();
+		StatisticsUserData statisticsUserData = Optional.ofNullable(statisticsUserDataMapper.selectOne(Wrappers.lambdaQuery(StatisticsUserData.class).eq(StatisticsUserData::getStatisticsDate, yesZeroDate).last("limit 1"))).orElse(new StatisticsUserData());
+		statisticsUserData.setNumOfNewUser(statisticsUserDataMapper.getNumOfNewUser(yesZeroDate, thisZeroDate));
+		statisticsUserData.setNumOfActiveUser(statisticsUserDataMapper.getNumOfActiveUser(yesZeroDate, thisZeroDate));
+		statisticsUserData.setNumOfUser(yesNumOfUser + statisticsUserData.getNumOfNewUser());
+
+		statisticsUserData.setNumOfDistributeUser(statisticsUserDataMapper.getNumOfDistributeUserToday(yesZeroDate, thisZeroDate));
+
+		List<CorpUserAuth> corpUserAuths = corpUserAuthMapper.selectList(Wrappers.lambdaQuery(CorpUserAuth.class)
+				.between(CorpUserAuth::getCreatedTime, yesZeroDate, thisZeroDate).select(CorpUserAuth::getExternalUserid).groupBy(CorpUserAuth::getExternalUserid));
+		Integer numOfCorpUser = corpUserAuths.isEmpty() ? 0 : corpUserAuths.size();
+		statisticsUserData.setNumOfCorpUser(numOfCorpUser);
+		MapBuilder builder = MapUtils.builder()
+				.put("time", String.format("cfr.created_time between '%s' and '%s'", yesZeroDate, thisZeroDate))
+				.group("cfr.corp_user_id");
+		Number equipments = beanSearcher.searchCount(CorpUserTagView.class, builder.put("tagName", "ct.tag_name = '设备'").build());
+		statisticsUserData.setNumOfCorpEquipmentUser(equipments.intValue());
+		Number accounts = beanSearcher.searchCount(CorpUserTagView.class, builder.put("tagName", "ct.tag_name = '账号'").build());
+		statisticsUserData.setNumOfCorpAccountUser(accounts.intValue());
+
+		statisticsUserData.setPcNumOfNewUser(statisticsUserDataMapper.getPcNumOfNewUser(yesZeroDate, thisZeroDate));
+		statisticsUserData.setPcNumOfActiveUser(statisticsUserDataMapper.getPcNumOfActiveUser(yesZeroDate, thisZeroDate));
+		statisticsUserData.setPcNumOfUser(yesPcNumOfUser + statisticsUserData.getPcNumOfNewUser());
+
+		statisticsUserData.setStatisticsDate(yesZeroDate);
+		if (statisticsUserData.getId() != null) {
+			statisticsUserDataMapper.updateById(statisticsUserData);
+			return;
+		}
+		statisticsUserDataMapper.insert(statisticsUserData);
+	}
+
+	/**
+	 * 注册账号数据
+	 */
+	public List<RegisterAccountData> statisticsRegisterAccountData(Date yesZeroDate, Date thisZeroDate, Boolean today) {
+		List<RegisterAccountData> list = new ArrayList<>();
+		Constant.registerAccount.forEach(platform -> {
+			RegisterAccountData entity = Optional.ofNullable(registerOrderDonMapper.registerAccountDataEveryDay(yesZeroDate, thisZeroDate, platform)).orElse(new RegisterAccountData());
+			entity.setPlatform(platform);
+			RegisterAccountData dto = Optional.ofNullable(registerOrderDonMapper.selectRefundOrderDetail(yesZeroDate, thisZeroDate, platform)).orElse(new RegisterAccountData());
+			entity.setNumOfRefundOrder(dto.getNumOfRefundOrder());
+			entity.setRefundMoney(dto.getRefundMoney());
+			entity.setStatisticsDate(yesZeroDate);
+			if (today) {
+				list.add(entity);
+				return;
+			}
+			registerAccountDataMapper.insert(entity);
+		});
+		if (today) {
+			return list;
+		}
+		return null;
+	}
+
+	/**
+	 * 统计AppleOne数据
+	 *
+	 * @return
+	 */
+	public List<AppleOneData> statisticsAppleOneData(Date yesZeroDate, Date thisZeroDate, Boolean today) {
+		Map<String, List<GoodsDonSku>> areaDataMap = getAppleOneMap();
+
+		List<AbstractBackData> appleOneSave = new ArrayList<>();
+		List<AppleOneData> appleOneData = new ArrayList<>();
+		areaDataMap.forEach((areaName, skus) -> {
+			AppleOneData appleOne = new AppleOneData();
+			appleOne.setAreaName(areaName);
+			//通过skuIds找到对应的appleOne统计数据
+			List<Long> skuIds = skus.stream().map(GoodsDonSku::getId).collect(Collectors.toList());
+			List<OrderDon> orderDons = Optional.ofNullable(orderDonService.getAppleOneOrderDons(skuIds, Constant.noOrderStatus, yesZeroDate, thisZeroDate)).orElse(new ArrayList<>());
+			appleOneDataStatistics(orderDons, appleOne, appleOneSave);
+		});
+
+		for (AbstractBackData appleOne : appleOneSave) {
+			appleOne.setStatisticsDate(yesZeroDate);
+			if (today) {
+				appleOneData.add((AppleOneData) appleOne);
+				continue;
+			}
+			appleOneDataMapper.insert((AppleOneData) appleOne);
+		}
+		if (today) {
+			return appleOneData;
+		}
+		return null;
+	}
+
+	private void appleOneDataStatistics(List<OrderDon> orderDons, AppleOneData appleOne, List<AbstractBackData> appleOneSave) {
+		if (CollUtil.isNotEmpty(orderDons)) {
+			//用户人数
+			Set<Long> numOfUser = Optional.ofNullable(orderDons.stream().map(OrderDon::getUserId).collect(Collectors.toSet())).orElse(new HashSet<>());
+			appleOne.setNumOfUser(numOfUser.size());
+			//订单数
+			appleOne.setNumOfOrder(orderDons.size());
+			//兑换码订单数
+			List<OrderDon> exCodeOrders = Optional.ofNullable(orderDons.stream().filter(order -> order.getType() == OrderDon.Type.EX_CODE).collect(Collectors.toList())).orElse(new ArrayList<>());
+			appleOne.setNumOfCodeOrder(exCodeOrders.size());
+			//金额
+			Optional<BigDecimal> money = orderDons.stream().map(OrderDon::getMoney).reduce(BigDecimal::add);
+			if (money.isPresent()) {
+				appleOne.setMoney(money.get());
+			} else appleOne.setMoney(BigDecimal.ZERO);
+			//分销订单
+			List<OrderDon> distributeOrders = Optional.ofNullable(orderDons.stream().filter(ord -> ord.getIsDistribute()).collect(Collectors.toList())).orElse(new ArrayList<>());
+			appleOne.setNumOfDistributeOrder(distributeOrders.size());
+			Optional<BigDecimal> distributeMoney = distributeOrders.stream().map(OrderDon::getMoney).reduce(BigDecimal::add);
+			appleOne.setDistributeOrderMoney(distributeMoney.isPresent() ? distributeMoney.get() : BigDecimal.ZERO);
+
+			getNumOfOrderByPayType(orderDons, appleOne);
+		}
+		appleOneSave.add(appleOne);
+	}
+
+	/**
+	 * 统计流媒体账号数据
+	 */
+	public List<StreamingAccountData> statisticsStreamingAccountData(Date yesZeroDate, Date thisZeroDate, Boolean today) {
+		List<Account> accounts = accountService.getNoAppleOneAccount(2l);
+		Map<Long, List<Account>> accountMaps = accounts.stream().collect(Collectors.groupingBy(Account::getGoodsId));
+		List<StreamingAccountData> streamingAccountDataSave = new ArrayList<>(accounts.size());
+		accountMaps.forEach((goodsId, accountList) -> {
+			StreamingAccountData streamingAccount = new StreamingAccountData();
+			streamingAccount.setAccountName(accountList.get(0).getTitle());
+			streamingAccount.setGoodsId(goodsId);
+			streamingAccountDataStatistics(goodsId, streamingAccount, streamingAccountDataSave, yesZeroDate, thisZeroDate);
+		});
+		if (today) {
+			return streamingAccountDataSave;
+		}
+		for (StreamingAccountData streamingAccountData : streamingAccountDataSave) {
+			streamingAccountData.setStatisticsDate(yesZeroDate);
+			streamingAccountDataMapper.insert(streamingAccountData);
+		}
+		return null;
+	}
+
+	/**
+	 * 统计实物数据
+	 */
+	public List<RealGoodsData> statisticsRealGoodsData(Date yesZeroDate, Date thisZeroDate, Boolean today) {
+		List<GoodsDon> goodsDons = goodsDonMapper.selectList(Wrappers.lambdaQuery(GoodsDon.class).eq(GoodsDon::getType, Constant.realGoodsType.get(0)));
+		List<RealGoodsData> realGoodsDataList = new ArrayList<>();
+		if (!goodsDons.isEmpty()) {
+			goodsDons.forEach(goods -> {
+				List<GoodsDonSku> skuList = goodsDonSkuService.list(Wrappers.lambdaQuery(GoodsDonSku.class).eq(GoodsDonSku::getGoodsId, goods.getId()));
+				if (!skuList.isEmpty()) {
+					skuList.forEach(sku -> {
+						RealGoodsData realGoodsData = new RealGoodsData();
+						realGoodsData.setGoodsId(goods.getId());
+						realGoodsData.setGoodsTitle(goods.getTitle());
+						realGoodsData.setSkuId(sku.getId());
+						realGoodsData.setSpecVal(sku.getSpecVal());
+						//订单
+						List<OrderDon> orderDon = Optional.ofNullable(orderDonService.list(Wrappers.lambdaQuery(OrderDon.class).eq(OrderDon::getSkuId, sku.getId()).between(OrderDon::getCreatedTime, yesZeroDate, thisZeroDate).notIn(OrderDon::getStatus, Constant.noOrderStatus).select(OrderDon::getUserId, OrderDon::getMoney, OrderDon::getId))).orElse(new ArrayList<>());
+						Set<Long> userList = Optional.ofNullable(orderDon.stream().map(OrderDon::getUserId).collect(Collectors.toSet())).orElse(new HashSet<>());
+						//用户数
+						realGoodsData.setNumOfUser(userList.size());
+						//订单数
+						realGoodsData.setNumOfOrder(orderDon.size());
+						//金额
+						Optional<BigDecimal> money = orderDon.stream().map(OrderDon::getMoney).reduce(BigDecimal::add);
+						realGoodsData.setMoney(money.isPresent() ? money.get() : BigDecimal.ZERO);
+						realGoodsDataList.add(realGoodsData);
+					});
+				}
+			});
+		}
+		if (today) {
+			return realGoodsDataList;
+		}
+		if (!realGoodsDataList.isEmpty()) {
+			realGoodsDataList.forEach(data -> {
+				data.setStatisticsDate(yesZeroDate);
+				realGoodsDataMapper.insert(data);
+			});
+		}
+		return null;
+	}
+
+	/**
+	 * @param goodsId              平台id
+	 * @param streamingAccountData
+	 * @param dataSave             入库实体集合
+	 * @param yesZeroDate          昨日零时
+	 * @param thisZeroDate         今日零时
+	 */
+	private void streamingAccountDataStatistics(Long goodsId, StreamingAccountData streamingAccountData, List<StreamingAccountData> dataSave, Date yesZeroDate, Date thisZeroDate) {
+		//规格
+		List<GoodsDonSku> list = Optional.ofNullable(goodsDonSkuService.list(Wrappers.lambdaQuery(GoodsDonSku.class).eq(GoodsDonSku::getGoodsId, goodsId).select(GoodsDonSku::getId))).orElse(new ArrayList<>());
+		List<Long> skuIds = list.stream().map(GoodsDonSku::getId).collect(Collectors.toList());
+		if (CollUtil.isNotEmpty(skuIds)) {
+			List<OrderDon> orderDons = Optional.ofNullable(accountService.getNoAppleOneOrderDon(skuIds, yesZeroDate, thisZeroDate, Constant.noOrderStatus)).orElse(new ArrayList<>());
+			//用户数
+			streamingAccountData.setNumOfUser(orderDons.stream().map(OrderDon::getUserId).collect(Collectors.toSet()).size());
+			getNumOfOrderByPayType(orderDons, streamingAccountData);
+			//兑换码订单数
+			List<OrderDon> exCodeOrders = Optional.ofNullable(orderDons.stream().filter(order -> order.getType() == OrderDon.Type.EX_CODE).collect(Collectors.toList())).orElse(new ArrayList<>());
+			streamingAccountData.setNumOfCodeOrder(exCodeOrders.size());
+			streamingAccountData.setNumOfOrder(orderDons.size());
+			Optional<BigDecimal> reduce = orderDons.stream().map(OrderDon::getMoney).reduce(BigDecimal::add);
+			BigDecimal totalMoney = BigDecimal.valueOf(0);
+			if (reduce.isPresent()) {
+				totalMoney = reduce.get();
+			}
+			//金额
+			streamingAccountData.setMoney(totalMoney);
+			//分销订单
+			List<OrderDon> distributeOrders = Optional.ofNullable(orderDons.stream().filter(ord -> ord.getIsDistribute()).collect(Collectors.toList())).orElse(new ArrayList<>());
+			streamingAccountData.setNumOfDistributeOrder(distributeOrders.size());
+			Optional<BigDecimal> distributeMoney = distributeOrders.stream().map(OrderDon::getMoney).reduce(BigDecimal::add);
+			streamingAccountData.setDistributeOrderMoney(distributeMoney.isPresent() ? distributeMoney.get() : BigDecimal.ZERO);
+		}
+		dataSave.add(streamingAccountData);
+	}
+
+	/**
+	 * 根据不同支付条件统计对应月、季、年订单
+	 */
+	private void getNumOfOrderByPayType(List<OrderDon> orderDons, AbstractBackData backData) {
+		Integer numOfMonthOrder = 0;
+		Integer numOfSeasonOrder = 0;
+		Integer numOfYearOrder = 0;
+		Map<Long, List<OrderDon>> skuIdMaps = Optional.ofNullable(orderDons.stream().collect(Collectors.groupingBy(OrderDon::getSkuId))).orElse(new HashMap<>());
+		if (!skuIdMaps.isEmpty()) {
+			List<GoodsDonSku> goodsDonSkus = Optional.ofNullable(goodsDonSkuService.list(Wrappers.lambdaQuery(GoodsDonSku.class).in(GoodsDonSku::getId, skuIdMaps.keySet()))).orElse(new ArrayList<>());
+			for (GoodsDonSku donSkus : goodsDonSkus) {
+				int size = skuIdMaps.get(donSkus.getId()).size();
+				if (donSkus.getSpecVal().contains(Constant.payType.get(0)) && !donSkus.getSpecVal().contains(Constant.payType.get(2))) {
+					numOfMonthOrder += size;
+				} else if (donSkus.getSpecVal().contains(Constant.payType.get(1))) {
+					numOfSeasonOrder += size;
+				} else if (donSkus.getSpecVal().contains(Constant.payType.get(2))) {
+					numOfYearOrder += size;
+				}
+			}
+		}
+		//月付
+		backData.setNumOfMonthOrder(numOfMonthOrder);
+		//季付
+		backData.setNumOfSeasonOrder(numOfSeasonOrder);
+		//年付
+		backData.setNumOfYearOrder(numOfYearOrder);
+	}
+
+	/**
+	 * 获取Apple区域map
+	 */
+	public Map<String, List<GoodsDonSku>> getAppleOneMap() {
+		GoodsDon goodsDon = goodsDonMapper.selectOne(Wrappers.lambdaQuery(GoodsDon.class).eq(GoodsDon::getId, 2).last("limit 1"));
+		List<GoodsDonSku> list = goodsDonSkuService.list(Wrappers.lambdaQuery(GoodsDonSku.class).eq(GoodsDonSku::getGoodsId, goodsDon.getId()).select(GoodsDonSku::getId, GoodsDonSku::getSpecVal));
+		Set<String> area = new HashSet<>();
+		list.forEach(sku -> {
+			String areaName = sku.getSpecVal().substring(0, sku.getSpecVal().indexOf("区") + 1);
+			if (!area.contains(areaName)) {
+				area.add(areaName);
+			}
+		});
+		Map<String, List<GoodsDonSku>> areaDataMap = new HashMap<>();
+		for (String name : area) {
+			List<GoodsDonSku> sku = Optional.ofNullable(goodsDonSkuService.list(Wrappers.lambdaQuery(GoodsDonSku.class).like(GoodsDonSku::getSpecVal, name).select(GoodsDonSku::getId, GoodsDonSku::getGoodsId, GoodsDonSku::getSpecVal))).orElse(new ArrayList<>());
+			areaDataMap.put(name, sku);
+		}
+		return areaDataMap;
+	}
+
+}

+ 6 - 5
netflix-service/src/main/java/com/cyksj/service/mange/statistics/impl/StatisticsDataServiceImpl.java

@@ -14,15 +14,17 @@ import com.cyksj.mapper.manage.statistics.*;
 import com.cyksj.model.entity.*;
 import com.cyksj.model.excel.ExcelAccountData;
 import com.cyksj.model.excel.ExcelSheetAndData;
-import com.cyksj.model.excel.statistics.*;
+import com.cyksj.model.excel.statistics.ExcelAppleOneData;
+import com.cyksj.model.excel.statistics.ExcelRealGoodsData;
+import com.cyksj.model.excel.statistics.ExcelRegisterAccountData;
+import com.cyksj.model.excel.statistics.ExcelStreamingAccountData;
 import com.cyksj.model.manage.views.GroupsRelationView;
 import com.cyksj.model.manage.views.OrderDonView;
 import com.cyksj.model.views.*;
 import com.cyksj.service.mange.CmsAccountService;
+import com.cyksj.service.mange.statistics.StatisticsDataDayService;
 import com.cyksj.service.mange.statistics.StatisticsDataService;
 import com.cyksj.service.order.OrderDonService;
-import com.cyksj.service.user.UserService;
-import com.cyksj.task.StatisticsDataScheduler;
 import com.ejlchina.searcher.BeanSearcher;
 import com.ejlchina.searcher.param.Operator;
 import com.ejlchina.searcher.util.MapUtils;
@@ -42,7 +44,6 @@ import java.util.stream.Collectors;
 @Service
 @RequiredArgsConstructor
 public class StatisticsDataServiceImpl implements StatisticsDataService {
-	private final UserService userService;
 
 	private final OrderDonService orderDonService;
 
@@ -62,7 +63,7 @@ public class StatisticsDataServiceImpl implements StatisticsDataService {
 
 	private final BeanSearcher beanSearcher;
 
-	private final StatisticsDataScheduler statistics;
+	private final StatisticsDataDayService statistics;
 
 	private final GroupsMapper groupsMapper;
 

+ 110 - 110
netflix-service/src/main/java/com/cyksj/task/OrderCustomerHandleScheduler.java

@@ -1,110 +1,110 @@
-package com.cyksj.task;
-
-import cn.hutool.core.date.DateTime;
-import cn.hutool.core.date.DateUtil;
-import cn.hutool.core.util.StrUtil;
-import com.cyksj.common.constant.Constant;
-import com.cyksj.mapper.manage.CustomerServiceEveryMonthDetailMapper;
-import com.cyksj.mapper.manage.StatisticsCustomerServiceEveryMonthMapper;
-import com.cyksj.model.entity.CustomerServiceEveryMonthDetail;
-import com.cyksj.model.entity.CustomerServiceRenewOrderRecord;
-import com.cyksj.model.entity.RealGoodsOrderHandle;
-import com.cyksj.model.entity.StatisticsCustomerServiceEveryMonth;
-import com.ejlchina.searcher.BeanSearcher;
-import com.ejlchina.searcher.param.Operator;
-import com.ejlchina.searcher.util.MapUtils;
-import com.google.common.util.concurrent.AtomicDouble;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-
-import java.math.BigDecimal;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.stream.Collectors;
-
-/*
- *项目名: netflix
- *文件名: OrderCustomerHandleScheduler
- *创建者: JavaZou
- *创建时间:2022/11/29 16:44
- */
-@Component
-@Slf4j
-@RequiredArgsConstructor
-public class OrderCustomerHandleScheduler {
-	private final CustomerServiceEveryMonthDetailMapper detailMapper;
-
-	private final StatisticsCustomerServiceEveryMonthMapper statisticsEveryMonthMapper;
-
-	private final BeanSearcher beanSearcher;
-
-	/**
-	 * 客服业绩数据统计
-	 */
-	@Scheduled(cron = "59 59 23 L * ?")
-	public void orderCustomerHandle() {
-		DateTime now = DateTime.now();
-		DateTime begin = DateUtil.beginOfMonth(now);
-		String statisticsDate = DateUtil.format(now, "yyyy-MM");
-		List<RealGoodsOrderHandle> realGoodsOrderHandles = beanSearcher.searchAll(RealGoodsOrderHandle.class, MapUtils.builder()
-				.field(RealGoodsOrderHandle::getStatus, Constant.noOrderAllStatus).op(Operator.NotIn)
-				.field(RealGoodsOrderHandle::getCreatedTime, begin, now).op(Operator.Between)
-				.build());
-		Map<String, List<RealGoodsOrderHandle>> before = realGoodsOrderHandles.stream().collect(Collectors.groupingBy(RealGoodsOrderHandle::getBeforeServiceName));
-		Map<String, List<RealGoodsOrderHandle>> after = realGoodsOrderHandles.stream().collect(Collectors.groupingBy(RealGoodsOrderHandle::getAfterServiceName));
-		StatisticsCustomerServiceEveryMonth statistics = new StatisticsCustomerServiceEveryMonth();
-		statistics.setStatisticsData(statisticsDate);
-		statisticsEveryMonthMapper.insert(statistics);
-		before.forEach((k, v) -> {
-			customerServiceRecord(k, v, 1, statistics.getId());
-		});
-		after.forEach((k, v) -> {
-			customerServiceRecord(k, v, 2, statistics.getId());
-		});
-		//续费业绩
-		List<CustomerServiceRenewOrderRecord> customerServiceRecords = beanSearcher.searchAll(CustomerServiceRenewOrderRecord.class, MapUtils.builder()
-				.field(RealGoodsOrderHandle::getCreatedTime, begin, now).op(Operator.Between)
-				.build());
-		Map<String, List<CustomerServiceRenewOrderRecord>> renews = customerServiceRecords.stream().collect(Collectors.groupingBy(CustomerServiceRenewOrderRecord::getCustomerService));
-		renews.forEach((k,v)->{
-			renewServiceOrderRecord(k, v, 3, statistics.getId());
-		});
-	}
-
-
-	public void customerServiceRecord(String serviceName, List<RealGoodsOrderHandle> handlers, Integer type, Long statisticsId) {
-		if (StrUtil.isNotBlank(serviceName)) {
-			CustomerServiceEveryMonthDetail record = new CustomerServiceEveryMonthDetail();
-			record.setType(type);
-			record.setCustomerServiceName(serviceName);
-			record.setStatisticsId(statisticsId);
-			AtomicDouble numOfOrder = new AtomicDouble(0);
-			handlers.forEach(handler -> {
-				if ("red_book".equals(handler.getLabelSource())) {
-					numOfOrder.addAndGet(1.5);
-					return;
-				}
-				numOfOrder.addAndGet(1);
-			});
-			record.setNumOfOrder(BigDecimal.valueOf(numOfOrder.doubleValue()));
-			Optional<BigDecimal> money = handlers.stream().map(RealGoodsOrderHandle::getMoney).reduce(BigDecimal::add);
-			record.setMoney(money.isPresent() ? money.get() : BigDecimal.ZERO);
-			detailMapper.insert(record);
-		}
-	}
-
-	public void renewServiceOrderRecord(String serviceName, List<CustomerServiceRenewOrderRecord> renewOrderRecords, Integer type, Long statisticsId) {
-		if (StrUtil.isNotBlank(serviceName)) {
-			CustomerServiceEveryMonthDetail record = new CustomerServiceEveryMonthDetail();
-			record.setType(type);
-			record.setCustomerServiceName(serviceName);
-			record.setStatisticsId(statisticsId);
-			record.setNumOfOrder(BigDecimal.valueOf(renewOrderRecords.size()));
-			record.setMoney(BigDecimal.ZERO);
-			detailMapper.insert(record);
-		}
-	}
-}
+//package com.cyksj.task;
+//
+//import cn.hutool.core.date.DateTime;
+//import cn.hutool.core.date.DateUtil;
+//import cn.hutool.core.util.StrUtil;
+//import com.cyksj.common.constant.Constant;
+//import com.cyksj.mapper.manage.CustomerServiceEveryMonthDetailMapper;
+//import com.cyksj.mapper.manage.StatisticsCustomerServiceEveryMonthMapper;
+//import com.cyksj.model.entity.CustomerServiceEveryMonthDetail;
+//import com.cyksj.model.entity.CustomerServiceRenewOrderRecord;
+//import com.cyksj.model.entity.RealGoodsOrderHandle;
+//import com.cyksj.model.entity.StatisticsCustomerServiceEveryMonth;
+//import com.ejlchina.searcher.BeanSearcher;
+//import com.ejlchina.searcher.param.Operator;
+//import com.ejlchina.searcher.util.MapUtils;
+//import com.google.common.util.concurrent.AtomicDouble;
+//import lombok.RequiredArgsConstructor;
+//import lombok.extern.slf4j.Slf4j;
+//import org.springframework.scheduling.annotation.Scheduled;
+//import org.springframework.stereotype.Component;
+//
+//import java.math.BigDecimal;
+//import java.util.List;
+//import java.util.Map;
+//import java.util.Optional;
+//import java.util.stream.Collectors;
+//
+///*
+// *项目名: netflix
+// *文件名: OrderCustomerHandleScheduler
+// *创建者: JavaZou
+// *创建时间:2022/11/29 16:44
+// */
+//@Component
+//@Slf4j
+//@RequiredArgsConstructor
+//public class OrderCustomerHandleScheduler {
+//	private final CustomerServiceEveryMonthDetailMapper detailMapper;
+//
+//	private final StatisticsCustomerServiceEveryMonthMapper statisticsEveryMonthMapper;
+//
+//	private final BeanSearcher beanSearcher;
+//
+//	/**
+//	 * 客服业绩数据统计
+//	 */
+//	@Scheduled(cron = "59 59 23 L * ?")
+//	public void orderCustomerHandle() {
+//		DateTime now = DateTime.now();
+//		DateTime begin = DateUtil.beginOfMonth(now);
+//		String statisticsDate = DateUtil.format(now, "yyyy-MM");
+//		List<RealGoodsOrderHandle> realGoodsOrderHandles = beanSearcher.searchAll(RealGoodsOrderHandle.class, MapUtils.builder()
+//				.field(RealGoodsOrderHandle::getStatus, Constant.noOrderAllStatus).op(Operator.NotIn)
+//				.field(RealGoodsOrderHandle::getCreatedTime, begin, now).op(Operator.Between)
+//				.build());
+//		Map<String, List<RealGoodsOrderHandle>> before = realGoodsOrderHandles.stream().collect(Collectors.groupingBy(RealGoodsOrderHandle::getBeforeServiceName));
+//		Map<String, List<RealGoodsOrderHandle>> after = realGoodsOrderHandles.stream().collect(Collectors.groupingBy(RealGoodsOrderHandle::getAfterServiceName));
+//		StatisticsCustomerServiceEveryMonth statistics = new StatisticsCustomerServiceEveryMonth();
+//		statistics.setStatisticsData(statisticsDate);
+//		statisticsEveryMonthMapper.insert(statistics);
+//		before.forEach((k, v) -> {
+//			customerServiceRecord(k, v, 1, statistics.getId());
+//		});
+//		after.forEach((k, v) -> {
+//			customerServiceRecord(k, v, 2, statistics.getId());
+//		});
+//		//续费业绩
+//		List<CustomerServiceRenewOrderRecord> customerServiceRecords = beanSearcher.searchAll(CustomerServiceRenewOrderRecord.class, MapUtils.builder()
+//				.field(RealGoodsOrderHandle::getCreatedTime, begin, now).op(Operator.Between)
+//				.build());
+//		Map<String, List<CustomerServiceRenewOrderRecord>> renews = customerServiceRecords.stream().collect(Collectors.groupingBy(CustomerServiceRenewOrderRecord::getCustomerService));
+//		renews.forEach((k,v)->{
+//			renewServiceOrderRecord(k, v, 3, statistics.getId());
+//		});
+//	}
+//
+//
+//	public void customerServiceRecord(String serviceName, List<RealGoodsOrderHandle> handlers, Integer type, Long statisticsId) {
+//		if (StrUtil.isNotBlank(serviceName)) {
+//			CustomerServiceEveryMonthDetail record = new CustomerServiceEveryMonthDetail();
+//			record.setType(type);
+//			record.setCustomerServiceName(serviceName);
+//			record.setStatisticsId(statisticsId);
+//			AtomicDouble numOfOrder = new AtomicDouble(0);
+//			handlers.forEach(handler -> {
+//				if ("red_book".equals(handler.getLabelSource())) {
+//					numOfOrder.addAndGet(1.5);
+//					return;
+//				}
+//				numOfOrder.addAndGet(1);
+//			});
+//			record.setNumOfOrder(BigDecimal.valueOf(numOfOrder.doubleValue()));
+//			Optional<BigDecimal> money = handlers.stream().map(RealGoodsOrderHandle::getMoney).reduce(BigDecimal::add);
+//			record.setMoney(money.isPresent() ? money.get() : BigDecimal.ZERO);
+//			detailMapper.insert(record);
+//		}
+//	}
+//
+//	public void renewServiceOrderRecord(String serviceName, List<CustomerServiceRenewOrderRecord> renewOrderRecords, Integer type, Long statisticsId) {
+//		if (StrUtil.isNotBlank(serviceName)) {
+//			CustomerServiceEveryMonthDetail record = new CustomerServiceEveryMonthDetail();
+//			record.setType(type);
+//			record.setCustomerServiceName(serviceName);
+//			record.setStatisticsId(statisticsId);
+//			record.setNumOfOrder(BigDecimal.valueOf(renewOrderRecords.size()));
+//			record.setMoney(BigDecimal.ZERO);
+//			detailMapper.insert(record);
+//		}
+//	}
+//}

+ 548 - 548
netflix-service/src/main/java/com/cyksj/task/Scheduler.java

@@ -1,552 +1,552 @@
-package com.cyksj.task;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.date.BetweenFormater;
-import cn.hutool.core.date.DateField;
-import cn.hutool.core.date.DateTime;
-import cn.hutool.core.date.DateUtil;
-import cn.hutool.core.util.StrUtil;
-import com.baomidou.mybatisplus.core.toolkit.Wrappers;
-import com.cyksj.common.EnvCommonService;
-import com.cyksj.common.constant.Constant;
-import com.cyksj.common.util.SmsUtil;
-import com.cyksj.common.util.StringUtil;
-import com.cyksj.dto.RedisKey;
-import com.cyksj.mapper.*;
-import com.cyksj.mapper.manage.cms.CmsUserMapper;
-import com.cyksj.mapper.manage.distribute.DistributeWaitingSendPointsMapper;
-import com.cyksj.mapper.manage.sys.WorkOrderMapper;
-import com.cyksj.mapper.market.task.UserBindDetailMapper;
-import com.cyksj.model.entity.*;
-import com.cyksj.model.manage.views.AccountView;
-import com.cyksj.model.manage.views.GroupsRelationView;
-import com.cyksj.model.views.CloseGroupsTripsView;
-import com.cyksj.model.views.CmsUserVO;
-import com.cyksj.model.views.UserDistributeViews;
-import com.cyksj.redis.RedisService;
-import com.cyksj.service.mange.CmsAccountService;
-import com.cyksj.service.order.OrderDonService;
-import com.cyksj.service.register.RegisterOrderDonService;
-import com.cyksj.service.register.SpotifyService;
-import com.cyksj.service.relation.GroupRelationClearService;
-import com.cyksj.service.scheduler.SchedulerService;
-import com.cyksj.service.sms.SmsService;
-import com.cyksj.service.user.UserBenefitsService;
-import com.ejlchina.searcher.BeanSearcher;
-import com.ejlchina.searcher.param.Operator;
-import com.ejlchina.searcher.util.MapUtils;
-import com.google.common.collect.Lists;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.dao.DuplicateKeyException;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.stream.Collectors;
-
-@Component
-@Slf4j
-@RequiredArgsConstructor
-public class Scheduler {
-
-        private final GoodsDonMapper donMapper;
-
-	    private final GoodsDonSkuMapper skuMapper;
-
-        private final GroupsMapper groupsMapper;
-
-        private final OrderDonService orderDonService;
-
-        private final GroupsRelationMapper groupsRelationMapper;
-
-        private final UserMapper userMapper;
-
-        private final BeanSearcher beanSearcher;
-
-        private final SpotifyService spotifyService;
-
-        private final RegisterOrderDonMapper registerOrderDonMapper;
-
-        private final RedisService redisService;
-
-        private final SysConfigMapper sysConfigMapper;
-
-        private final DistributeWaitingSendPointsMapper distributeWaitingSendPointsMapper;
-
-        private final RegisterOrderDonService registerOrderDonService;
-
-        private final AppleAutoRegisterDataMapper appleAutoRegisterDataMapper;
-
-        private final RealGoodsInterestUserMapper realGoodsInterestUserMapper;
-
-        private final GroupRelationClearService groupRelationClearService;
-
-        private final CmsAccountService cmsAccountService;
-
-        private final EnvCommonService envCommonService;
-
-        private final WorkOrderMapper workOrderMapper;
-
-        private final SmsService smsService;
-
-        private final CmsUserMapper cmsUserMapper;
-
-	    private final AccountDoubleVerifyRecordMapper doubleVerifyRecordMapper;
-
-	    private final UserBenefitsService userBenefitsService;
-
-	    private final SchedulerService schedulerService;
-
-	    private final UserBindDetailMapper userBindDetailMapper;
-
-	    private final static List<Long> filter_skuIds = List.of(177l);
-
-    /**
-     * 每5分钟清理过期账号
-     */
-    @Scheduled(cron = "0 0/5 * * * ?")
-    public void clearExpiry() {
-        DateTime now = new DateTime();
-        //List<GroupsRelation> expiryList = groupsRelationMapper.selectList(Wrappers.lambdaQuery(GroupsRelation.class).le(GroupsRelation::getExpiryTime, now));
-	    List<GroupsRelation> expiryList = groupsRelationMapper.selectExpiredRelationByCondition(now, filter_skuIds);
-        log.info("定时清理过期账号start ===> 数量:{}", expiryList.size());
-        //过期账号清楚有效期
-        expiryList.forEach(relation -> groupRelationClearService.clearTicket(relation, UserTicketClearedRecord.Source.self));
-    }
-
-	/**
-	 * 每天凌晨清除当天过期的ChatGPT PLUS车票
-	 */
-	@Scheduled(cron = "0 1 0 * * ?")
-	public void clearChatGPTPlusTicket() {
-		DateTime now = new DateTime();
-		DateTime nextBeginDay = DateUtil.offsetDay(DateUtil.beginOfDay(now), 1);
-		List<Long> gptSkuIds = skuMapper.selectList(Wrappers.lambdaQuery(GoodsDonSku.class)
-				.eq(GoodsDonSku::getGoodsId, 18)).stream().filter((sku) -> !filter_skuIds.contains(sku.getId())).map(GoodsDonSku::getId).collect(Collectors.toList());
-		List<GroupsRelation> expiryGPTListToday = groupsRelationMapper.selectExpiredChatGPTRelation(nextBeginDay, gptSkuIds);
-		log.info("每日凌晨清除当日过期GPT账号start ===> 数量:{}", expiryGPTListToday.size());
-		//过期账号清楚有效期
-		expiryGPTListToday.forEach(relation -> groupRelationClearService.clearTicket(relation, UserTicketClearedRecord.Source.self));
-	}
-
-	/**
-	 * 每日过期主账号 迁移该账号对应车队的未过期用户车票
-	 */
-	@Scheduled(cron = "0 30 0 * * ?")
-	public void expiryAccountTrans() {
-		schedulerService.transExpiryAccountValidRelation();
-	}
-
-	@Scheduled(cron = "0 0 9 * * ?")
-	public void expiryWxMsg() throws Exception {
-		if (envCommonService.isPrdEnv()) {
-			DateTime now = new DateTime();
-			DateTime zero = DateUtil.offset(now, DateField.HOUR, -9);
-			DateTime time = DateUtil.offset(zero, DateField.DAY_OF_MONTH, 1);
-			List<GroupsRelationView> groupsRelations = beanSearcher.searchAll(GroupsRelationView.class, MapUtils.builder().field(GroupsRelationView::getExpiryTime, time).op(Operator.LessEqual).build());
-			//String url = String.format("%sauth/weChat?url=%syinhe/web/ticket", envCommonService.getHost(), envCommonService.getDomain());
-			for (GroupsRelationView relation : groupsRelations) {
-//            User user = userMapper.selectById(relation.getUserId());
-//            if (user != null && StrUtil.isNotBlank(user.getOpenId())) {
-//                String openId = user.getOpenId();
-//                WxAppTemplateView wxAppTemplateView = templateCommonService.getWxAppByUserId(openId, TemplateEnum.EXPIRY_TEMPLATE.getDesc());
-//                if (wxAppTemplateView != null) {
-//                    WxMpTemplateMessage templateMessage = new WxMpTemplateMessage()
-//                            .setToUser(openId)
-//                            .setTemplateId(wxAppTemplateView.getTemplateId())
-//                            .setUrl(url);
-//
-//                    /**
-//                     * 您的账号即将到期
-//                     * 账号名称:Netflix账号
-//                     * 平台:Netflix
-//                     * 失效日期:3天后
-//                     * 异常原因:账号即将到期
-//                     * 请尽快续费,以免影响使用
-//                     */
-//                    GroupsTrips groupsTrips = groupsMapper.selectById(relation.getGroupsId());
-//                    GoodsDonSku donSku = skuMapper.selectById(groupsTrips.getSkuId());
-//                    GoodsDon goodsDon = donMapper.selectById(donSku.getGoodsId());
-//                    WxMpTemplateMessage.TemplateData data = templateMessage.getData();
-//                    data.setFirst(new WxMpTemplateData("您的账号即将到期"));
-//                    data.setKeyword1(new WxMpTemplateData(goodsDon.getTitle()));
-//                    data.setKeyword2(new WxMpTemplateData(DateUtil.format(relation.getExpiryTime(), "yyyy-MM-dd HH:mm:ss")));
-//                    data.setRemark(new WxMpTemplateData("请尽快续费,以免影响使用"));
-//                    log.info("发送账号即将到期消息至用户:{}", user.getId());
-//                    weChatService.sendTemplateMessage(weChatService.getAccessToken(wxAppTemplateView.getAppId()), Jsons.toJson(templateMessage));
+//package com.cyksj.task;
+//
+//import cn.hutool.core.collection.CollUtil;
+//import cn.hutool.core.date.BetweenFormater;
+//import cn.hutool.core.date.DateField;
+//import cn.hutool.core.date.DateTime;
+//import cn.hutool.core.date.DateUtil;
+//import cn.hutool.core.util.StrUtil;
+//import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+//import com.cyksj.common.EnvCommonService;
+//import com.cyksj.common.constant.Constant;
+//import com.cyksj.common.util.SmsUtil;
+//import com.cyksj.common.util.StringUtil;
+//import com.cyksj.dto.RedisKey;
+//import com.cyksj.mapper.*;
+//import com.cyksj.mapper.manage.cms.CmsUserMapper;
+//import com.cyksj.mapper.manage.distribute.DistributeWaitingSendPointsMapper;
+//import com.cyksj.mapper.manage.sys.WorkOrderMapper;
+//import com.cyksj.mapper.market.task.UserBindDetailMapper;
+//import com.cyksj.model.entity.*;
+//import com.cyksj.model.manage.views.AccountView;
+//import com.cyksj.model.manage.views.GroupsRelationView;
+//import com.cyksj.model.views.CloseGroupsTripsView;
+//import com.cyksj.model.views.CmsUserVO;
+//import com.cyksj.model.views.UserDistributeViews;
+//import com.cyksj.redis.RedisService;
+//import com.cyksj.service.mange.CmsAccountService;
+//import com.cyksj.service.order.OrderDonService;
+//import com.cyksj.service.register.RegisterOrderDonService;
+//import com.cyksj.service.register.SpotifyService;
+//import com.cyksj.service.relation.GroupRelationClearService;
+//import com.cyksj.service.scheduler.SchedulerService;
+//import com.cyksj.service.sms.SmsService;
+//import com.cyksj.service.user.UserBenefitsService;
+//import com.ejlchina.searcher.BeanSearcher;
+//import com.ejlchina.searcher.param.Operator;
+//import com.ejlchina.searcher.util.MapUtils;
+//import com.google.common.collect.Lists;
+//import lombok.RequiredArgsConstructor;
+//import lombok.extern.slf4j.Slf4j;
+//import org.springframework.dao.DuplicateKeyException;
+//import org.springframework.scheduling.annotation.Scheduled;
+//import org.springframework.stereotype.Component;
+//import org.springframework.transaction.annotation.Transactional;
+//
+//import java.math.BigDecimal;
+//import java.math.RoundingMode;
+//import java.util.*;
+//import java.util.concurrent.ConcurrentHashMap;
+//import java.util.stream.Collectors;
+//
+//@Component
+//@Slf4j
+//@RequiredArgsConstructor
+//public class Scheduler {
+//
+//        private final GoodsDonMapper donMapper;
+//
+//	    private final GoodsDonSkuMapper skuMapper;
+//
+//        private final GroupsMapper groupsMapper;
+//
+//        private final OrderDonService orderDonService;
+//
+//        private final GroupsRelationMapper groupsRelationMapper;
+//
+//        private final UserMapper userMapper;
+//
+//        private final BeanSearcher beanSearcher;
+//
+//        private final SpotifyService spotifyService;
+//
+//        private final RegisterOrderDonMapper registerOrderDonMapper;
+//
+//        private final RedisService redisService;
+//
+//        private final SysConfigMapper sysConfigMapper;
+//
+//        private final DistributeWaitingSendPointsMapper distributeWaitingSendPointsMapper;
+//
+//        private final RegisterOrderDonService registerOrderDonService;
+//
+//        private final AppleAutoRegisterDataMapper appleAutoRegisterDataMapper;
+//
+//        private final RealGoodsInterestUserMapper realGoodsInterestUserMapper;
+//
+//        private final GroupRelationClearService groupRelationClearService;
+//
+//        private final CmsAccountService cmsAccountService;
+//
+//        private final EnvCommonService envCommonService;
+//
+//        private final WorkOrderMapper workOrderMapper;
+//
+//        private final SmsService smsService;
+//
+//        private final CmsUserMapper cmsUserMapper;
+//
+//	    private final AccountDoubleVerifyRecordMapper doubleVerifyRecordMapper;
+//
+//	    private final UserBenefitsService userBenefitsService;
+//
+//	    private final SchedulerService schedulerService;
+//
+//	    private final UserBindDetailMapper userBindDetailMapper;
+//
+//	    private final static List<Long> filter_skuIds = List.of(177l);
+//
+//    /**
+//     * 每5分钟清理过期账号
+//     */
+//    @Scheduled(cron = "0 0/5 * * * ?")
+//    public void clearExpiry() {
+//        DateTime now = new DateTime();
+//        //List<GroupsRelation> expiryList = groupsRelationMapper.selectList(Wrappers.lambdaQuery(GroupsRelation.class).le(GroupsRelation::getExpiryTime, now));
+//	    List<GroupsRelation> expiryList = groupsRelationMapper.selectExpiredRelationByCondition(now, filter_skuIds);
+//        log.info("定时清理过期账号start ===> 数量:{}", expiryList.size());
+//        //过期账号清楚有效期
+//        expiryList.forEach(relation -> groupRelationClearService.clearTicket(relation, UserTicketClearedRecord.Source.self));
+//    }
+//
+//	/**
+//	 * 每天凌晨清除当天过期的ChatGPT PLUS车票
+//	 */
+//	@Scheduled(cron = "0 1 0 * * ?")
+//	public void clearChatGPTPlusTicket() {
+//		DateTime now = new DateTime();
+//		DateTime nextBeginDay = DateUtil.offsetDay(DateUtil.beginOfDay(now), 1);
+//		List<Long> gptSkuIds = skuMapper.selectList(Wrappers.lambdaQuery(GoodsDonSku.class)
+//				.eq(GoodsDonSku::getGoodsId, 18)).stream().filter((sku) -> !filter_skuIds.contains(sku.getId())).map(GoodsDonSku::getId).collect(Collectors.toList());
+//		List<GroupsRelation> expiryGPTListToday = groupsRelationMapper.selectExpiredChatGPTRelation(nextBeginDay, gptSkuIds);
+//		log.info("每日凌晨清除当日过期GPT账号start ===> 数量:{}", expiryGPTListToday.size());
+//		//过期账号清楚有效期
+//		expiryGPTListToday.forEach(relation -> groupRelationClearService.clearTicket(relation, UserTicketClearedRecord.Source.self));
+//	}
+//
+//	/**
+//	 * 每日过期主账号 迁移该账号对应车队的未过期用户车票
+//	 */
+//	@Scheduled(cron = "0 30 0 * * ?")
+//	public void expiryAccountTrans() {
+//		schedulerService.transExpiryAccountValidRelation();
+//	}
+//
+//	@Scheduled(cron = "0 0 9 * * ?")
+//	public void expiryWxMsg() throws Exception {
+//		if (envCommonService.isPrdEnv()) {
+//			DateTime now = new DateTime();
+//			DateTime zero = DateUtil.offset(now, DateField.HOUR, -9);
+//			DateTime time = DateUtil.offset(zero, DateField.DAY_OF_MONTH, 1);
+//			List<GroupsRelationView> groupsRelations = beanSearcher.searchAll(GroupsRelationView.class, MapUtils.builder().field(GroupsRelationView::getExpiryTime, time).op(Operator.LessEqual).build());
+//			//String url = String.format("%sauth/weChat?url=%syinhe/web/ticket", envCommonService.getHost(), envCommonService.getDomain());
+//			for (GroupsRelationView relation : groupsRelations) {
+////            User user = userMapper.selectById(relation.getUserId());
+////            if (user != null && StrUtil.isNotBlank(user.getOpenId())) {
+////                String openId = user.getOpenId();
+////                WxAppTemplateView wxAppTemplateView = templateCommonService.getWxAppByUserId(openId, TemplateEnum.EXPIRY_TEMPLATE.getDesc());
+////                if (wxAppTemplateView != null) {
+////                    WxMpTemplateMessage templateMessage = new WxMpTemplateMessage()
+////                            .setToUser(openId)
+////                            .setTemplateId(wxAppTemplateView.getTemplateId())
+////                            .setUrl(url);
+////
+////                    /**
+////                     * 您的账号即将到期
+////                     * 账号名称:Netflix账号
+////                     * 平台:Netflix
+////                     * 失效日期:3天后
+////                     * 异常原因:账号即将到期
+////                     * 请尽快续费,以免影响使用
+////                     */
+////                    GroupsTrips groupsTrips = groupsMapper.selectById(relation.getGroupsId());
+////                    GoodsDonSku donSku = skuMapper.selectById(groupsTrips.getSkuId());
+////                    GoodsDon goodsDon = donMapper.selectById(donSku.getGoodsId());
+////                    WxMpTemplateMessage.TemplateData data = templateMessage.getData();
+////                    data.setFirst(new WxMpTemplateData("您的账号即将到期"));
+////                    data.setKeyword1(new WxMpTemplateData(goodsDon.getTitle()));
+////                    data.setKeyword2(new WxMpTemplateData(DateUtil.format(relation.getExpiryTime(), "yyyy-MM-dd HH:mm:ss")));
+////                    data.setRemark(new WxMpTemplateData("请尽快续费,以免影响使用"));
+////                    log.info("发送账号即将到期消息至用户:{}", user.getId());
+////                    weChatService.sendTemplateMessage(weChatService.getAccessToken(wxAppTemplateView.getAppId()), Jsons.toJson(templateMessage));
+////            }
+//
+////                List<Account> accounts = accountMapper.selectList(Wrappers.lambdaQuery(Account.class).le(Account::getExpiryTime, time));
+////                List<CustomerService> customerServices = customerServiceMapper.selectList(null);
+////                if(!accounts.isEmpty()){
+////                    for (CustomerService customerService : customerServices) {
+////                        openId = customerService.getOpenId();
+////                        WxMpTemplateMessage manageMessage = new WxMpTemplateMessage()
+////                                .setToUser(openId)
+////                                .setTemplateId(wxAppTemplateView.getTemplateId())
+////                                .setUrl(" ");
+////                        WxMpTemplateMessage.TemplateData data2 = manageMessage.getData();
+////                        data2.setFirst(new WxMpTemplateData("您有" + accounts.size() + "个主账号即将到期"));
+////                        data2.setKeyword1(new WxMpTemplateData("车队主账号"));
+////                        data2.setKeyword2(new WxMpTemplateData("车队主账号"));
+////                        data2.setKeyword3(new WxMpTemplateData("5天后"));
+////                        data2.setKeyword4(new WxMpTemplateData("账号即将到期"));
+////                        data2.setRemark(new WxMpTemplateData("请尽快登录后台-在账号管理中查看"));
+////                        log.info("发送账号即将到期消息至客服:{}", customerService.getNickName());
+////                        weChatService.sendTemplateMessage(weChatService.getAccessToken(),Jsons.toJson(manageMessage));
+////                    }
+////                }
+////            }
+//				//短信通知过期账号 用户
+//				try {
+//					String day = DateUtil.formatBetween(zero, relation.getExpiryTime(), BetweenFormater.Level.DAY);
+//					if ("0".equals(day)) day = "今";
+//					smsService.sendSmsToRelationUser(relation.getUserId(), String.format(Constant.ACCOUNT_EXPIRY_MSG_TEMPLATE, relation.getTitle(), day));
+//				} catch (Exception e) {
+//					log.error("过期账号用户:{}发送短信错误:{}", relation.getUserId(), StringUtil.getErrorText(e));
+//				}
+//			}
+//		}
+//	}
+//
+//    /**
+//     * 定时关闭订单
+//     */
+//    @Scheduled(cron = "0 0/5 * * * ?")
+//    public void closeOrder() {
+//        //获取为支付订单
+//        DateTime now = new DateTime();
+//        Date newDate = DateUtil.offset(now, DateField.MINUTE, -5);
+//        List<OrderDon> list = orderDonService.list(Wrappers.lambdaQuery(OrderDon.class).eq(OrderDon::getStatus, OrderDon.Status.noPayment).le(OrderDon::getCreatedTime,newDate));
+//        log.info("定时任务 关闭 订单数:{}",list.size());
+//        list.forEach((orderDon)->{
+//            orderDonService.closeOrder(orderDon.getId());
+//        });
+//    }
+//
+//    /**
+//     * spotify 注册
+//     */
+//    @Scheduled(cron = "0 0/15 * * * ?")
+//    public void spotify() {
+//        List<RegisterOrderDon> spotify = registerOrderDonMapper.selectList(Wrappers.lambdaQuery(RegisterOrderDon.class)
+//                .select(RegisterOrderDon::getId)
+//                .eq(RegisterOrderDon::getPlatform, "spotify")
+//                .eq(RegisterOrderDon::getStatus, RegisterOrderDon.Status.hasPayment));
+//        for (RegisterOrderDon orderDon : spotify) {
+//            spotifyService.register(orderDon.getId());
+//        }
+//    }
+//
+//    /**
+//     * 每日免单重置
+//     */
+//    @Scheduled(cron = "0 0 0 * * ?")
+//    public void freeReset() {
+//        log.info("每日重置 spotify 免单账户数 [start]");
+//        SysConfig register_free_num = sysConfigMapper.selectOne(Wrappers.lambdaQuery(SysConfig.class).eq(SysConfig::getSysKey, "register_free_num"));
+//        redisService.set(RedisKey.SPOTIFY_FREE_NUM, Long.parseLong(register_free_num.getSysValue()));
+//        log.info("每日重置 spotify 免单账户数 [end] 数量:{}",register_free_num.getSysValue());
+//    }
+//
+//    /**
+//     * 分销订单积分奖励分发
+//     */
+//    @Scheduled(cron = "0 0 0/2 * * ?")
+//    @Transactional(rollbackFor = Throwable.class)
+//    public void distributeOrdersSendPoints() {
+//	    List<DistributeWaitingSendPoints> waitingSendPoints = Optional.ofNullable(distributeWaitingSendPointsMapper.selectList(Wrappers.lambdaQuery(DistributeWaitingSendPoints.class)
+//			    .eq(DistributeWaitingSendPoints::getSendStatus, DistributeWaitingSendPoints.Status.waiting)
+//			    .eq(DistributeWaitingSendPoints::getIsDelivery, true)
+//			    .le(DistributeWaitingSendPoints::getSendTime, DateTime.now())
+//			    .select(DistributeWaitingSendPoints::getId,
+//					    DistributeWaitingSendPoints::getOrderId,
+//					    DistributeWaitingSendPoints::getPoints,
+//					    DistributeWaitingSendPoints::getUserId,
+//					    DistributeWaitingSendPoints::getSharedId,
+//					    DistributeWaitingSendPoints::getSendTime))).orElse(new ArrayList<>());
+//        log.info("查询分销订单积分分发记录开始,条数:{}=======》", waitingSendPoints.size());
+//        waitingSendPoints.forEach(wait -> {
+//            //积分待发人
+//            Long sharedId = wait.getSharedId();
+//            User user = userMapper.selectById(sharedId);
+//            if (user == null) {
+//                wait.setSendStatus(DistributeWaitingSendPoints.Status.not_exist);
+//                log.info("分销订单,发送积分用户不存在");
+//                distributeWaitingSendPointsMapper.updateById(wait);
+//            } else {
+//                wait.setSendStatus(DistributeWaitingSendPoints.Status.success);
+//                log.info("更新用户积分开始=====>");
+//	            userBenefitsService.addDistributePoints(sharedId, wait.getPoints());
+//	            distributeWaitingSendPointsMapper.updateById(wait);
+//	            log.info("分销积分表订单id:{}发放积分success", wait.getOrderId());
 //            }
-
-//                List<Account> accounts = accountMapper.selectList(Wrappers.lambdaQuery(Account.class).le(Account::getExpiryTime, time));
-//                List<CustomerService> customerServices = customerServiceMapper.selectList(null);
-//                if(!accounts.isEmpty()){
-//                    for (CustomerService customerService : customerServices) {
-//                        openId = customerService.getOpenId();
-//                        WxMpTemplateMessage manageMessage = new WxMpTemplateMessage()
-//                                .setToUser(openId)
-//                                .setTemplateId(wxAppTemplateView.getTemplateId())
-//                                .setUrl(" ");
-//                        WxMpTemplateMessage.TemplateData data2 = manageMessage.getData();
-//                        data2.setFirst(new WxMpTemplateData("您有" + accounts.size() + "个主账号即将到期"));
-//                        data2.setKeyword1(new WxMpTemplateData("车队主账号"));
-//                        data2.setKeyword2(new WxMpTemplateData("车队主账号"));
-//                        data2.setKeyword3(new WxMpTemplateData("5天后"));
-//                        data2.setKeyword4(new WxMpTemplateData("账号即将到期"));
-//                        data2.setRemark(new WxMpTemplateData("请尽快登录后台-在账号管理中查看"));
-//                        log.info("发送账号即将到期消息至客服:{}", customerService.getNickName());
-//                        weChatService.sendTemplateMessage(weChatService.getAccessToken(),Jsons.toJson(manageMessage));
+//        });
+//    }
+//
+//    /**
+//     * 定时更新未支付AppleID自动注册订单
+//     */
+//    @Scheduled(cron = "0 0/5 * * * ?")
+//    public void updateAppleAutoStatus() {
+//        //获取为支付订单
+//        DateTime now = new DateTime();
+//        Date newDate = DateUtil.offset(now, DateField.MINUTE, -5);
+//        List<Long> orderDons = appleAutoRegisterDataMapper.getAllNoPayOrders(newDate);
+//        log.info("定时更新未支付AppleID自动注册订单 订单数:{}",orderDons.size());
+//        orderDons.forEach((orderId)->{
+//            registerOrderDonService.updateAppleAutoStatus(orderId);
+//        });
+//    }
+//
+//    /**
+//     * 实物订单意愿数据
+//     */
+//    @Scheduled(cron = "0 0/5 * * * ?")
+//    public void realGoodsInterestUser() {
+//        Optional.ofNullable(orderDonService.selectNoPaymentRealGoods()).ifPresent(list -> {
+//                    if (list.size() > 0) {
+//                        Map<String, List<RealGoodsInterestUser>> collect = list.stream().collect(Collectors.groupingBy(RealGoodsInterestUser::getGroupBy));
+//                        log.info("实物订单意愿数据同步开始");
+//                        collect.forEach((k, data) -> {
+//                            RealGoodsInterestUser realGoodsInterestUser = null;
+//                            if (data.size() == 0) {
+//                                realGoodsInterestUser = data.get(0);
+//                            } else {
+//                                List<RealGoodsInterestUser> high = data.stream().sorted(Comparator.comparing(RealGoodsInterestUser::getMoney).thenComparing(RealGoodsInterestUser::getOrderCreatedTime).reversed()).collect(Collectors.toList());
+//                                realGoodsInterestUser = high.get(0);
+//                            }
+//                            try {
+//                                realGoodsInterestUserMapper.insert(realGoodsInterestUser);
+//                            } catch (DuplicateKeyException e) {
+//                                log.info("重复插入用户相同实物商品记录");
+//                            }
+//                        });
+//                        log.info("实物订单意愿数据同步结束");
 //                    }
 //                }
+//        );
+//        Integer update = orderDonService.updateHasPaymentInterestRealGoods();
+//        orderDonService.updateHasPaymentInterestRealGoodsPayTime();
+//        if (update > 0) {
+//            log.info("更新用户记录:{}已购买实物意愿商品状态成功", update);
+//        }
+//    }
+//
+//    /**
+//     * 统计昨日过期账号 分配给客服
+//     */
+//    @Scheduled(cron = "0 30 8 * * ?")
+//    public void assignNeedUpdateExpiredAccount() {
+//        DateTime beginOfDay = DateUtil.beginOfDay(DateTime.now());
+//	    String yesTimeSql = String.format("and ur.created_time < '%s'", beginOfDay);
+//	    String extra_sql = String.format("(if((select count(ur.id) from user_ticket_cleared_record ur where ur.account_id = a.id and deleted is true and source = 'self' %s)>=4,true,false)) = 1", yesTimeSql);
+//	    List<AccountView> accountViews = beanSearcher.searchAll(AccountView.class, MapUtils.builder()
+//			    .put("extra_sql", extra_sql)
+//			    .field(AccountView::getCustomerServiceId, 0)
+//			    .field(AccountView::getGoodsSecondType, 2)
+//			    .build());
+//        if (accountViews.size() > 0) {
+//            int accountSize = accountViews.size();
+//            log.info("开始给客服分配过期账号,过期账号数量:{}", accountSize);
+//            List<CmsUserVO> customers = beanSearcher.searchAll(CmsUserVO.class, MapUtils.builder().field(CmsUserVO::getRoleNames, "客服").op(Operator.Contain).build());
+//            if (customers.isEmpty()) {
+//                log.info("暂无客服");
+//                return;
 //            }
-				//短信通知过期账号 用户
-				try {
-					String day = DateUtil.formatBetween(zero, relation.getExpiryTime(), BetweenFormater.Level.DAY);
-					if ("0".equals(day)) day = "今";
-					smsService.sendSmsToRelationUser(relation.getUserId(), String.format(Constant.ACCOUNT_EXPIRY_MSG_TEMPLATE, relation.getTitle(), day));
-				} catch (Exception e) {
-					log.error("过期账号用户:{}发送短信错误:{}", relation.getUserId(), StringUtil.getErrorText(e));
-				}
-			}
-		}
-	}
-
-    /**
-     * 定时关闭订单
-     */
-    @Scheduled(cron = "0 0/5 * * * ?")
-    public void closeOrder() {
-        //获取为支付订单
-        DateTime now = new DateTime();
-        Date newDate = DateUtil.offset(now, DateField.MINUTE, -5);
-        List<OrderDon> list = orderDonService.list(Wrappers.lambdaQuery(OrderDon.class).eq(OrderDon::getStatus, OrderDon.Status.noPayment).le(OrderDon::getCreatedTime,newDate));
-        log.info("定时任务 关闭 订单数:{}",list.size());
-        list.forEach((orderDon)->{
-            orderDonService.closeOrder(orderDon.getId());
-        });
-    }
-
-    /**
-     * spotify 注册
-     */
-    @Scheduled(cron = "0 0/15 * * * ?")
-    public void spotify() {
-        List<RegisterOrderDon> spotify = registerOrderDonMapper.selectList(Wrappers.lambdaQuery(RegisterOrderDon.class)
-                .select(RegisterOrderDon::getId)
-                .eq(RegisterOrderDon::getPlatform, "spotify")
-                .eq(RegisterOrderDon::getStatus, RegisterOrderDon.Status.hasPayment));
-        for (RegisterOrderDon orderDon : spotify) {
-            spotifyService.register(orderDon.getId());
-        }
-    }
-
-    /**
-     * 每日免单重置
-     */
-    @Scheduled(cron = "0 0 0 * * ?")
-    public void freeReset() {
-        log.info("每日重置 spotify 免单账户数 [start]");
-        SysConfig register_free_num = sysConfigMapper.selectOne(Wrappers.lambdaQuery(SysConfig.class).eq(SysConfig::getSysKey, "register_free_num"));
-        redisService.set(RedisKey.SPOTIFY_FREE_NUM, Long.parseLong(register_free_num.getSysValue()));
-        log.info("每日重置 spotify 免单账户数 [end] 数量:{}",register_free_num.getSysValue());
-    }
-
-    /**
-     * 分销订单积分奖励分发
-     */
-    @Scheduled(cron = "0 0 0/2 * * ?")
-    @Transactional(rollbackFor = Throwable.class)
-    public void distributeOrdersSendPoints() {
-	    List<DistributeWaitingSendPoints> waitingSendPoints = Optional.ofNullable(distributeWaitingSendPointsMapper.selectList(Wrappers.lambdaQuery(DistributeWaitingSendPoints.class)
-			    .eq(DistributeWaitingSendPoints::getSendStatus, DistributeWaitingSendPoints.Status.waiting)
-			    .eq(DistributeWaitingSendPoints::getIsDelivery, true)
-			    .le(DistributeWaitingSendPoints::getSendTime, DateTime.now())
-			    .select(DistributeWaitingSendPoints::getId,
-					    DistributeWaitingSendPoints::getOrderId,
-					    DistributeWaitingSendPoints::getPoints,
-					    DistributeWaitingSendPoints::getUserId,
-					    DistributeWaitingSendPoints::getSharedId,
-					    DistributeWaitingSendPoints::getSendTime))).orElse(new ArrayList<>());
-        log.info("查询分销订单积分分发记录开始,条数:{}=======》", waitingSendPoints.size());
-        waitingSendPoints.forEach(wait -> {
-            //积分待发人
-            Long sharedId = wait.getSharedId();
-            User user = userMapper.selectById(sharedId);
-            if (user == null) {
-                wait.setSendStatus(DistributeWaitingSendPoints.Status.not_exist);
-                log.info("分销订单,发送积分用户不存在");
-                distributeWaitingSendPointsMapper.updateById(wait);
-            } else {
-                wait.setSendStatus(DistributeWaitingSendPoints.Status.success);
-                log.info("更新用户积分开始=====>");
-	            userBenefitsService.addDistributePoints(sharedId, wait.getPoints());
-	            distributeWaitingSendPointsMapper.updateById(wait);
-	            log.info("分销积分表订单id:{}发放积分success", wait.getOrderId());
-            }
-        });
-    }
-
-    /**
-     * 定时更新未支付AppleID自动注册订单
-     */
-    @Scheduled(cron = "0 0/5 * * * ?")
-    public void updateAppleAutoStatus() {
-        //获取为支付订单
-        DateTime now = new DateTime();
-        Date newDate = DateUtil.offset(now, DateField.MINUTE, -5);
-        List<Long> orderDons = appleAutoRegisterDataMapper.getAllNoPayOrders(newDate);
-        log.info("定时更新未支付AppleID自动注册订单 订单数:{}",orderDons.size());
-        orderDons.forEach((orderId)->{
-            registerOrderDonService.updateAppleAutoStatus(orderId);
-        });
-    }
-
-    /**
-     * 实物订单意愿数据
-     */
-    @Scheduled(cron = "0 0/5 * * * ?")
-    public void realGoodsInterestUser() {
-        Optional.ofNullable(orderDonService.selectNoPaymentRealGoods()).ifPresent(list -> {
-                    if (list.size() > 0) {
-                        Map<String, List<RealGoodsInterestUser>> collect = list.stream().collect(Collectors.groupingBy(RealGoodsInterestUser::getGroupBy));
-                        log.info("实物订单意愿数据同步开始");
-                        collect.forEach((k, data) -> {
-                            RealGoodsInterestUser realGoodsInterestUser = null;
-                            if (data.size() == 0) {
-                                realGoodsInterestUser = data.get(0);
-                            } else {
-                                List<RealGoodsInterestUser> high = data.stream().sorted(Comparator.comparing(RealGoodsInterestUser::getMoney).thenComparing(RealGoodsInterestUser::getOrderCreatedTime).reversed()).collect(Collectors.toList());
-                                realGoodsInterestUser = high.get(0);
-                            }
-                            try {
-                                realGoodsInterestUserMapper.insert(realGoodsInterestUser);
-                            } catch (DuplicateKeyException e) {
-                                log.info("重复插入用户相同实物商品记录");
-                            }
-                        });
-                        log.info("实物订单意愿数据同步结束");
-                    }
-                }
-        );
-        Integer update = orderDonService.updateHasPaymentInterestRealGoods();
-        orderDonService.updateHasPaymentInterestRealGoodsPayTime();
-        if (update > 0) {
-            log.info("更新用户记录:{}已购买实物意愿商品状态成功", update);
-        }
-    }
-
-    /**
-     * 统计昨日过期账号 分配给客服
-     */
-    @Scheduled(cron = "0 30 8 * * ?")
-    public void assignNeedUpdateExpiredAccount() {
-        DateTime beginOfDay = DateUtil.beginOfDay(DateTime.now());
-	    String yesTimeSql = String.format("and ur.created_time < '%s'", beginOfDay);
-	    String extra_sql = String.format("(if((select count(ur.id) from user_ticket_cleared_record ur where ur.account_id = a.id and deleted is true and source = 'self' %s)>=4,true,false)) = 1", yesTimeSql);
-	    List<AccountView> accountViews = beanSearcher.searchAll(AccountView.class, MapUtils.builder()
-			    .put("extra_sql", extra_sql)
-			    .field(AccountView::getCustomerServiceId, 0)
-			    .field(AccountView::getGoodsSecondType, 2)
-			    .build());
-        if (accountViews.size() > 0) {
-            int accountSize = accountViews.size();
-            log.info("开始给客服分配过期账号,过期账号数量:{}", accountSize);
-            List<CmsUserVO> customers = beanSearcher.searchAll(CmsUserVO.class, MapUtils.builder().field(CmsUserVO::getRoleNames, "客服").op(Operator.Contain).build());
-            if (customers.isEmpty()) {
-                log.info("暂无客服");
-                return;
-            }
-            //分配过期账号
-            cmsAccountService.assignCustomerAccount(customers, accountViews);
-        }
-
-    }
-
-    /**
-     * 2天前的工单未关闭 直接关闭
-     */
-    @Scheduled(cron = "0 10 0 * * ?")
-    public void closeWorkOrderBeforeTowDays() {
-        DateTime twoBefore = DateUtil.offsetDay(DateTime.now(), -2);
-        List<WorkOrder> workOrders = beanSearcher.searchAll(WorkOrder.class, MapUtils.builder()
-                .field(WorkOrder::getStatus, WorkOrder.Status.waiting.name())
-                .field(WorkOrder::getCreatedTime, twoBefore).op(Operator.LessEqual)
-                .onlySelect(WorkOrder::getId)
-                .build());
-        if (CollUtil.isNotEmpty(workOrders)) {
-            List<Long> collect = workOrders.stream().map(WorkOrder::getId).collect(Collectors.toList());
-            List<List<Long>> lists = Lists.partition(collect, 1000);
-            log.info("关闭2天前未关闭的工单");
-            lists.forEach(ids -> {
-                workOrderMapper.update(null, Wrappers.lambdaUpdate(WorkOrder.class)
-                        .set(WorkOrder::getStatus, WorkOrder.Status.close)
-                        .in(WorkOrder::getId, ids));
-            });
-        }
-    }
-
-    /**
-     * 推广者渠道昨天订单金额是否小于过去7天平均订单金额的一半
-     * 若小于 则发送短信到对应商务
-     */
-    @Scheduled(cron = "0 30 8 * * ?")
-    public void distributeOrderDetailAndSendMsgToBusiness() {
-	    Integer daysRule = 7;
-	    DateTime thisZero = DateUtil.beginOfDay(DateTime.now());
-
-	    DateTime yesZero = DateUtil.offsetDay(thisZero, -1);
-	    DateTime thePast7dZero = DateUtil.offsetDay(yesZero, -daysRule);
-
-	    DateTime twoYesZero = DateUtil.offsetDay(thisZero, -2);
-	    DateTime theTwoPast7dZero = DateUtil.offsetDay(twoYesZero, -daysRule);
-	    //总销量小于1000的 不算、建立天数低于7天的不算、 连续2天销量低于7天平均值的50%的 提醒
-	    List<UserDistributeViews> userDistributeViews = beanSearcher.searchAll(UserDistributeViews.class, MapUtils.builder()
-			    .field(UserDistributeViews::getCreatedTime, thePast7dZero).op(Operator.LessEqual)
-			    .field(UserDistribute::getBusinessId, 0).op(Operator.GreaterThan)
-			    .put("time", String.format("and od.created_time < '%s'", thisZero))
-			    .field(UserDistributeViews::getDistributeOrdersMoney, BigDecimal.valueOf(100000)).op(Operator.GreaterEqual)
-			    .build());
-	    String thePast7dTimeSql = String.format("and od.created_time >= '%s' and od.created_time < '%s'", thePast7dZero, yesZero);
-	    String yesTimeSql = String.format("and od.created_time >= '%s' and od.created_time < '%s'", yesZero, thisZero);
-
-	    String theTwoPast7dTimeSql = String.format("and od.created_time >= '%s' and od.created_time < '%s'", theTwoPast7dZero, twoYesZero);
-	    String twoYesTimeSql = String.format("and od.created_time >= '%s' and od.created_time < '%s'", twoYesZero, yesZero);
-
-	    Map<Long, String> phoneMap = new ConcurrentHashMap<>();
-	    userDistributeViews.forEach(data -> {
-		    //昨天过去7天总订单金额
-		    Number yesPastFtMoneySum = beanSearcher.searchSum(UserDistributeViews.class, MapUtils.builder()
-				    .field(UserDistributeViews::getUserId, data.getUserId())
-				    .put("time", thePast7dTimeSql).build(), "distributeOrdersMoney");
-		    //前天过去7天总订单金额
-		    Number towYesPastMoneySum = beanSearcher.searchSum(UserDistributeViews.class, MapUtils.builder()
-				    .field(UserDistributeViews::getUserId, data.getUserId())
-				    .put("time", theTwoPast7dTimeSql).build(), "distributeOrdersMoney");
-
-		    int theYes7dPastMoney = yesPastFtMoneySum.intValue();
-
-		    int theTwoYes7dPastMoney = towYesPastMoneySum.intValue();
-		    UserDistributeViews yesDistribute = beanSearcher.searchFirst(UserDistributeViews.class, MapUtils.builder()
-				    .field(UserDistributeViews::getUserId, data.getUserId())
-				    .put("time", yesTimeSql).build());
-
-		    UserDistributeViews twoYesDistribute = beanSearcher.searchFirst(UserDistributeViews.class, MapUtils.builder()
-				    .field(UserDistributeViews::getUserId, data.getUserId())
-				    .put("time", twoYesTimeSql).build());
-		    BigDecimal yesDistributeOrdersMoney = yesDistribute.getDistributeOrdersMoney();
-		    BigDecimal twoYesDistributeOrdersMoney = twoYesDistribute.getDistributeOrdersMoney();
-
-		    BigDecimal thePast7dAverage = BigDecimal.valueOf(theYes7dPastMoney).divide(BigDecimal.valueOf(daysRule).multiply(BigDecimal.valueOf(2)), 2, RoundingMode.HALF_DOWN);
-		    BigDecimal theTwoPast7dAverage = BigDecimal.valueOf(theTwoYes7dPastMoney).divide(BigDecimal.valueOf(daysRule).multiply(BigDecimal.valueOf(2)), 2, RoundingMode.HALF_DOWN);
-		    if (yesDistributeOrdersMoney.compareTo(thePast7dAverage) < 0 && twoYesDistributeOrdersMoney.compareTo(theTwoPast7dAverage) < 0) {
-			    User user = userMapper.selectById(data.getUserId());
-			    String msg = String.format(Constant.BUSINESS_CHANNEL_MSG, (user == null ? "用户" : user.getNickname()), data.getUserId());
-			    String phone = phoneMap.get(data.getUserId());
-			    if (StrUtil.isBlank(phone)) {
-				    CmsUser cmsUser = cmsUserMapper.selectById(data.getBusinessId());
-				    if (cmsUser != null) {
-					    phone = cmsUser.getPhone();
-					    phoneMap.putIfAbsent(data.getBusinessId(), phone);
-				    }
-			    }
-			    if (StrUtil.isNotBlank(phone)) {
-				    log.info("商务businessId:{}所下渠道用户id:{}分销数据已低于7天平均值的50%%", data.getBusinessId(), data.getUserId());
-				    //发短信
-				    SmsUtil.sendLuoKey2Msg(phone, msg);
-			    }
-			    return;
-		    }
-	    });
-    }
-
-	@Scheduled(cron = "0 0 0/3 * * ?")
-	public void accountDoubleVerify() {
-    	//chatGPT PLUS
-		Long goodsId = 18l;
-		List<AccountDoubleVerifyRecord> doubleVerifyRecords = groupsMapper.selectGPTDoubleVerifyTrips(goodsId);
-		doubleVerifyRecords.forEach(data -> {
-			AccountDoubleVerifyRecord exist = doubleVerifyRecordMapper.selectOne(Wrappers.lambdaQuery(AccountDoubleVerifyRecord.class)
-					.eq(AccountDoubleVerifyRecord::getAccountId, data.getAccountId())
-					.eq(AccountDoubleVerifyRecord::getDeleted, true).last("limit 1"));
-			if (exist == null) {
-				if (redisService.setNx(String.format("%s-%s", data.getAccountId(), data.getId()), data.getId(), 60l)) {
-					log.info("插入待开启双重验证号账号:{}", data.getAccount());
-					data.setRemark("车队满员后三天后自动移入");
-					doubleVerifyRecordMapper.insert(data);
-				}
-			}
-		});
-	}
-
-	/**
-	 * 每日凌晨下架今日过期车队账号
-	 */
-	@Scheduled(cron = "0 0 0 * * ?")
-	public void closeGroupsTrips() {
-		DateTime now = DateTime.now();
-		Constant.AI_goodsIds.forEach(goodsId -> {
-			//ChatGPT 10天提前下架
-			//midJourney 提前3天
-			Integer day = 3;
-			if (goodsId == 18) {
-				day = 10;
-			}
-			DateTime nextDay = DateUtil.offsetDay(DateUtil.beginOfDay(now), day);
-			List<CloseGroupsTripsView> closeGroupsTripsViews = Optional.ofNullable(beanSearcher.searchAll(CloseGroupsTripsView.class, MapUtils.builder()
-					.field(CloseGroupsTripsView::getExpiryTime, nextDay).op(Operator.LessEqual)
-					.field(CloseGroupsTripsView::getGoodsId, goodsId)
-					.field(CloseGroupsTripsView::getGroupsId).op(Operator.NotNull)
-					.field(CloseGroupsTripsView::getGroupsStatus, GroupsTrips.Status.down.name()).op(Operator.NotEqual)
-					.build())).orElse(new ArrayList<>());
-
-			log.info("下架goodsId:{}过期账号数量:{}", goodsId, closeGroupsTripsViews.size());
-			closeGroupsTripsViews.forEach(account -> {
-				int update = groupsMapper.update(null, Wrappers.lambdaUpdate(GroupsTrips.class)
-						.set(GroupsTrips::getStatus, GroupsTrips.Status.down)
-						.ne(GroupsTrips::getStatus, GroupsTrips.Status.down)
-						.eq(GroupsTrips::getId, account.getGroupsId()));
-				if (update == 1) {
-					log.info("将过期账号:{}车队:{}下架", account.getId(), account.getGroupsId());
-				}
-			});
-		});
-	}
-
-	/**
-	 * 1w条短信 每小时
-	 */
-	//@Scheduled(cron = "0 0 12,15,17,18 * * ?")
-	public void marketingMsg() {
-		//获取最大条件
-		String key = RedisService.key.MAKERTING_MSG_KEY.getName();
-		Object value = redisService.get(key);
-		Integer max_filter = 0;
-		if (value != null) {
-			max_filter = Integer.parseInt(value.toString());
-			log.info("过滤条数:{}", max_filter);
-		}
-		//获取手机号 每次获取3000次
-		List<String> phones = userBindDetailMapper.selectPhones(max_filter);
-		if (phones.isEmpty()) {
-			log.info("营销短信已发完");
-			return;
-		}
-		log.info("营销短信手机号数量大小:{}", phones.size());
-		try {
-			Boolean isSuccess = SmsUtil.batchSendLuoKey2Msg(phones, "618大促流媒体、Ai会员全场9折续费可用!电视看奈飞方案最多减100!登录https://nf.video下单【银河录像局】");
-			if (isSuccess) {
-				if (value != null) {
-					redisService.incr(key, 10000l);
-					return;
-				}
-				redisService.set(key, 10000, RedisService.key.MAKERTING_MSG_KEY.getTimeout());
-			}
-		} catch (Exception e) {
-
-		}
-	}
-}
-
+//            //分配过期账号
+//            cmsAccountService.assignCustomerAccount(customers, accountViews);
+//        }
+//
+//    }
+//
+//    /**
+//     * 2天前的工单未关闭 直接关闭
+//     */
+//    @Scheduled(cron = "0 10 0 * * ?")
+//    public void closeWorkOrderBeforeTowDays() {
+//        DateTime twoBefore = DateUtil.offsetDay(DateTime.now(), -2);
+//        List<WorkOrder> workOrders = beanSearcher.searchAll(WorkOrder.class, MapUtils.builder()
+//                .field(WorkOrder::getStatus, WorkOrder.Status.waiting.name())
+//                .field(WorkOrder::getCreatedTime, twoBefore).op(Operator.LessEqual)
+//                .onlySelect(WorkOrder::getId)
+//                .build());
+//        if (CollUtil.isNotEmpty(workOrders)) {
+//            List<Long> collect = workOrders.stream().map(WorkOrder::getId).collect(Collectors.toList());
+//            List<List<Long>> lists = Lists.partition(collect, 1000);
+//            log.info("关闭2天前未关闭的工单");
+//            lists.forEach(ids -> {
+//                workOrderMapper.update(null, Wrappers.lambdaUpdate(WorkOrder.class)
+//                        .set(WorkOrder::getStatus, WorkOrder.Status.close)
+//                        .in(WorkOrder::getId, ids));
+//            });
+//        }
+//    }
+//
+//    /**
+//     * 推广者渠道昨天订单金额是否小于过去7天平均订单金额的一半
+//     * 若小于 则发送短信到对应商务
+//     */
+//    @Scheduled(cron = "0 30 8 * * ?")
+//    public void distributeOrderDetailAndSendMsgToBusiness() {
+//	    Integer daysRule = 7;
+//	    DateTime thisZero = DateUtil.beginOfDay(DateTime.now());
+//
+//	    DateTime yesZero = DateUtil.offsetDay(thisZero, -1);
+//	    DateTime thePast7dZero = DateUtil.offsetDay(yesZero, -daysRule);
+//
+//	    DateTime twoYesZero = DateUtil.offsetDay(thisZero, -2);
+//	    DateTime theTwoPast7dZero = DateUtil.offsetDay(twoYesZero, -daysRule);
+//	    //总销量小于1000的 不算、建立天数低于7天的不算、 连续2天销量低于7天平均值的50%的 提醒
+//	    List<UserDistributeViews> userDistributeViews = beanSearcher.searchAll(UserDistributeViews.class, MapUtils.builder()
+//			    .field(UserDistributeViews::getCreatedTime, thePast7dZero).op(Operator.LessEqual)
+//			    .field(UserDistribute::getBusinessId, 0).op(Operator.GreaterThan)
+//			    .put("time", String.format("and od.created_time < '%s'", thisZero))
+//			    .field(UserDistributeViews::getDistributeOrdersMoney, BigDecimal.valueOf(100000)).op(Operator.GreaterEqual)
+//			    .build());
+//	    String thePast7dTimeSql = String.format("and od.created_time >= '%s' and od.created_time < '%s'", thePast7dZero, yesZero);
+//	    String yesTimeSql = String.format("and od.created_time >= '%s' and od.created_time < '%s'", yesZero, thisZero);
+//
+//	    String theTwoPast7dTimeSql = String.format("and od.created_time >= '%s' and od.created_time < '%s'", theTwoPast7dZero, twoYesZero);
+//	    String twoYesTimeSql = String.format("and od.created_time >= '%s' and od.created_time < '%s'", twoYesZero, yesZero);
+//
+//	    Map<Long, String> phoneMap = new ConcurrentHashMap<>();
+//	    userDistributeViews.forEach(data -> {
+//		    //昨天过去7天总订单金额
+//		    Number yesPastFtMoneySum = beanSearcher.searchSum(UserDistributeViews.class, MapUtils.builder()
+//				    .field(UserDistributeViews::getUserId, data.getUserId())
+//				    .put("time", thePast7dTimeSql).build(), "distributeOrdersMoney");
+//		    //前天过去7天总订单金额
+//		    Number towYesPastMoneySum = beanSearcher.searchSum(UserDistributeViews.class, MapUtils.builder()
+//				    .field(UserDistributeViews::getUserId, data.getUserId())
+//				    .put("time", theTwoPast7dTimeSql).build(), "distributeOrdersMoney");
+//
+//		    int theYes7dPastMoney = yesPastFtMoneySum.intValue();
+//
+//		    int theTwoYes7dPastMoney = towYesPastMoneySum.intValue();
+//		    UserDistributeViews yesDistribute = beanSearcher.searchFirst(UserDistributeViews.class, MapUtils.builder()
+//				    .field(UserDistributeViews::getUserId, data.getUserId())
+//				    .put("time", yesTimeSql).build());
+//
+//		    UserDistributeViews twoYesDistribute = beanSearcher.searchFirst(UserDistributeViews.class, MapUtils.builder()
+//				    .field(UserDistributeViews::getUserId, data.getUserId())
+//				    .put("time", twoYesTimeSql).build());
+//		    BigDecimal yesDistributeOrdersMoney = yesDistribute.getDistributeOrdersMoney();
+//		    BigDecimal twoYesDistributeOrdersMoney = twoYesDistribute.getDistributeOrdersMoney();
+//
+//		    BigDecimal thePast7dAverage = BigDecimal.valueOf(theYes7dPastMoney).divide(BigDecimal.valueOf(daysRule).multiply(BigDecimal.valueOf(2)), 2, RoundingMode.HALF_DOWN);
+//		    BigDecimal theTwoPast7dAverage = BigDecimal.valueOf(theTwoYes7dPastMoney).divide(BigDecimal.valueOf(daysRule).multiply(BigDecimal.valueOf(2)), 2, RoundingMode.HALF_DOWN);
+//		    if (yesDistributeOrdersMoney.compareTo(thePast7dAverage) < 0 && twoYesDistributeOrdersMoney.compareTo(theTwoPast7dAverage) < 0) {
+//			    User user = userMapper.selectById(data.getUserId());
+//			    String msg = String.format(Constant.BUSINESS_CHANNEL_MSG, (user == null ? "用户" : user.getNickname()), data.getUserId());
+//			    String phone = phoneMap.get(data.getUserId());
+//			    if (StrUtil.isBlank(phone)) {
+//				    CmsUser cmsUser = cmsUserMapper.selectById(data.getBusinessId());
+//				    if (cmsUser != null) {
+//					    phone = cmsUser.getPhone();
+//					    phoneMap.putIfAbsent(data.getBusinessId(), phone);
+//				    }
+//			    }
+//			    if (StrUtil.isNotBlank(phone)) {
+//				    log.info("商务businessId:{}所下渠道用户id:{}分销数据已低于7天平均值的50%%", data.getBusinessId(), data.getUserId());
+//				    //发短信
+//				    SmsUtil.sendLuoKey2Msg(phone, msg);
+//			    }
+//			    return;
+//		    }
+//	    });
+//    }
+//
+//	@Scheduled(cron = "0 0 0/3 * * ?")
+//	public void accountDoubleVerify() {
+//    	//chatGPT PLUS
+//		Long goodsId = 18l;
+//		List<AccountDoubleVerifyRecord> doubleVerifyRecords = groupsMapper.selectGPTDoubleVerifyTrips(goodsId);
+//		doubleVerifyRecords.forEach(data -> {
+//			AccountDoubleVerifyRecord exist = doubleVerifyRecordMapper.selectOne(Wrappers.lambdaQuery(AccountDoubleVerifyRecord.class)
+//					.eq(AccountDoubleVerifyRecord::getAccountId, data.getAccountId())
+//					.eq(AccountDoubleVerifyRecord::getDeleted, true).last("limit 1"));
+//			if (exist == null) {
+//				if (redisService.setNx(String.format("%s-%s", data.getAccountId(), data.getId()), data.getId(), 60l)) {
+//					log.info("插入待开启双重验证号账号:{}", data.getAccount());
+//					data.setRemark("车队满员后三天后自动移入");
+//					doubleVerifyRecordMapper.insert(data);
+//				}
+//			}
+//		});
+//	}
+//
+//	/**
+//	 * 每日凌晨下架今日过期车队账号
+//	 */
+//	@Scheduled(cron = "0 0 0 * * ?")
+//	public void closeGroupsTrips() {
+//		DateTime now = DateTime.now();
+//		Constant.AI_goodsIds.forEach(goodsId -> {
+//			//ChatGPT 10天提前下架
+//			//midJourney 提前3天
+//			Integer day = 3;
+//			if (goodsId == 18) {
+//				day = 10;
+//			}
+//			DateTime nextDay = DateUtil.offsetDay(DateUtil.beginOfDay(now), day);
+//			List<CloseGroupsTripsView> closeGroupsTripsViews = Optional.ofNullable(beanSearcher.searchAll(CloseGroupsTripsView.class, MapUtils.builder()
+//					.field(CloseGroupsTripsView::getExpiryTime, nextDay).op(Operator.LessEqual)
+//					.field(CloseGroupsTripsView::getGoodsId, goodsId)
+//					.field(CloseGroupsTripsView::getGroupsId).op(Operator.NotNull)
+//					.field(CloseGroupsTripsView::getGroupsStatus, GroupsTrips.Status.down.name()).op(Operator.NotEqual)
+//					.build())).orElse(new ArrayList<>());
+//
+//			log.info("下架goodsId:{}过期账号数量:{}", goodsId, closeGroupsTripsViews.size());
+//			closeGroupsTripsViews.forEach(account -> {
+//				int update = groupsMapper.update(null, Wrappers.lambdaUpdate(GroupsTrips.class)
+//						.set(GroupsTrips::getStatus, GroupsTrips.Status.down)
+//						.ne(GroupsTrips::getStatus, GroupsTrips.Status.down)
+//						.eq(GroupsTrips::getId, account.getGroupsId()));
+//				if (update == 1) {
+//					log.info("将过期账号:{}车队:{}下架", account.getId(), account.getGroupsId());
+//				}
+//			});
+//		});
+//	}
+//
+//	/**
+//	 * 1w条短信 每小时
+//	 */
+//	//@Scheduled(cron = "0 0 12,15,17,18 * * ?")
+//	public void marketingMsg() {
+//		//获取最大条件
+//		String key = RedisService.key.MAKERTING_MSG_KEY.getName();
+//		Object value = redisService.get(key);
+//		Integer max_filter = 0;
+//		if (value != null) {
+//			max_filter = Integer.parseInt(value.toString());
+//			log.info("过滤条数:{}", max_filter);
+//		}
+//		//获取手机号 每次获取3000次
+//		List<String> phones = userBindDetailMapper.selectPhones(max_filter);
+//		if (phones.isEmpty()) {
+//			log.info("营销短信已发完");
+//			return;
+//		}
+//		log.info("营销短信手机号数量大小:{}", phones.size());
+//		try {
+//			Boolean isSuccess = SmsUtil.batchSendLuoKey2Msg(phones, "618大促流媒体、Ai会员全场9折续费可用!电视看奈飞方案最多减100!登录https://nf.video下单【银河录像局】");
+//			if (isSuccess) {
+//				if (value != null) {
+//					redisService.incr(key, 10000l);
+//					return;
+//				}
+//				redisService.set(key, 10000, RedisService.key.MAKERTING_MSG_KEY.getTimeout());
+//			}
+//		} catch (Exception e) {
+//
+//		}
+//	}
+//}
+//

+ 130 - 130
netflix-service/src/main/java/com/cyksj/task/SignStatisticsDataScheduler.java

@@ -1,130 +1,130 @@
-package com.cyksj.task;
-
-import cn.hutool.core.date.DateTime;
-import cn.hutool.core.date.DateUtil;
-import com.baomidou.mybatisplus.core.toolkit.Wrappers;
-import com.cyksj.common.constant.Constant;
-import com.cyksj.common.constant.PointsRecordConst;
-import com.cyksj.mapper.manage.statistics.SignStatisticsDataMapper;
-import com.cyksj.mapper.market.sign.SignUserMapper;
-import com.cyksj.mapper.market.sign.UserPointsRecordMapper;
-import com.cyksj.mapper.market.task.TaskFollowPlatVerifyMapper;
-import com.cyksj.mapper.market.task.TaskUserCompleteRecordMapper;
-import com.cyksj.model.entity.SignStatisticsData;
-import com.cyksj.model.entity.SignUser;
-import com.cyksj.model.entity.TaskFollowPlatVerify;
-import com.cyksj.model.entity.TaskUserCompleteRecord;
-import com.cyksj.service.market.MarketFrontService;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-
-import java.util.Date;
-import java.util.List;
-
-/*
- *项目名: netflix
- *文件名: SignStatisticsDataScheduler
- *创建者: JavaZou
- *创建时间:2022/12/27 18:27
- */
-@Component
-@Slf4j
-@RequiredArgsConstructor
-public class SignStatisticsDataScheduler {
-	private final SignStatisticsDataMapper signStatisticsDataMapper;
-
-	private final UserPointsRecordMapper userPointsRecordMapper;
-
-	private final SignUserMapper signUserMapper;
-
-	private final TaskFollowPlatVerifyMapper taskFollowPlatVerifyMapper;
-
-	private final TaskUserCompleteRecordMapper taskUserCompleteRecordMapper;
-
-	private final MarketFrontService marketFrontService;
-
-	@Scheduled(cron = "0 0 0 * * ?")
-	public void signStatisticsData() {
-		Date thisZeroDate = DateUtil.beginOfDay(DateTime.now());
-		Date yesZeroDate = DateTime.of(thisZeroDate.getTime() - Constant.DAY_MILLS);
-		SignStatisticsData signStatisticsData = new SignStatisticsData();
-		//今日签到人数
-		Integer todayNumOfSign = signUserMapper.selectCount(Wrappers.lambdaQuery(SignUser.class)
-				.between(SignUser::getUpdateTime, yesZeroDate, thisZeroDate));
-		signStatisticsData.setTodayNumOfSign(todayNumOfSign);
-
-		//今日发放积分
-		Integer todayNumOfPoints = userPointsRecordMapper.getTodayNumOfPoints(yesZeroDate, thisZeroDate);
-
-		signStatisticsData.setTodayNumOfPoints(todayNumOfPoints);
-
-		//累计发放积分
-		Integer sumOfPoints = userPointsRecordMapper.getSumOfPoints();
-
-		signStatisticsData.setSumOfPoints(sumOfPoints);
-
-		//今日使用积分
-		Integer todayUseOfPoints = userPointsRecordMapper.getTodayUseOfPoints(yesZeroDate, thisZeroDate);
-		signStatisticsData.setTodayUseOfPoints(-todayUseOfPoints);
-
-		//累计使用积分
-		Integer sumUseOfPoints = userPointsRecordMapper.getSumUseOfPoints();
-		signStatisticsData.setSumUseOfPoints(-sumUseOfPoints);
-
-		//今日抽奖次数
-		Integer todayNumOfLottery = userPointsRecordMapper.getTodayNumOfLottery(PointsRecordConst.POINTS_LOTTERY, yesZeroDate, thisZeroDate);
-		signStatisticsData.setTodayNumOfLottery(todayNumOfLottery);
-
-		//连签2天
-		Integer sign2Day = userPointsRecordMapper.getSignDayByCondition(2, yesZeroDate, thisZeroDate);
-		signStatisticsData.setT2(sign2Day);
-
-		//连签3天
-		Integer sign3Day = userPointsRecordMapper.getSignDayByCondition(3, yesZeroDate, thisZeroDate);
-		signStatisticsData.setT3(sign3Day);
-
-		//连签4天
-		Integer sign4Day = userPointsRecordMapper.getSignDayByCondition(4, yesZeroDate, thisZeroDate);
-		signStatisticsData.setT4(sign4Day);
-
-		//连签5天
-		Integer sign5Day = userPointsRecordMapper.getSignDayByCondition(5, yesZeroDate, thisZeroDate);
-		signStatisticsData.setT5(sign5Day);
-
-		//连签6天
-		Integer sign6Day = userPointsRecordMapper.getSignDayByCondition(6, yesZeroDate, thisZeroDate);
-		signStatisticsData.setT6(sign6Day);
-
-		//连签7天
-		Integer sign7Day = userPointsRecordMapper.getSignDayByCondition(7, yesZeroDate, thisZeroDate);
-		signStatisticsData.setT7(sign7Day);
-
-		signStatisticsData.setStatisticsDate(yesZeroDate);
-		signStatisticsDataMapper.insert(signStatisticsData);
-	}
-
-	/**
-	 * 关注任务3天未审核 自动审核成功
-	 */
-	@Scheduled(cron = "0 30 0 * * ?")
-	public void autoVerifySuccess() {
-		DateTime now = DateTime.now();
-		DateTime threeBefore = DateUtil.offsetDay(now, -3);
-		List<TaskFollowPlatVerify> taskFollowPlatVerifies = taskFollowPlatVerifyMapper.selectList(Wrappers.lambdaQuery(TaskFollowPlatVerify.class)
-				.le(TaskFollowPlatVerify::getCreatedTime, threeBefore)
-				.eq(TaskFollowPlatVerify::getStatus, TaskFollowPlatVerify.Status.verifying));
-		taskFollowPlatVerifies.forEach(data->{
-			data.setStatus(TaskFollowPlatVerify.Status.success);
-			TaskUserCompleteRecord record = taskUserCompleteRecordMapper.selectOne(Wrappers.lambdaQuery(TaskUserCompleteRecord.class)
-					.eq(TaskUserCompleteRecord::getUserId, data.getUserId())
-					.eq(TaskUserCompleteRecord::getTaskId, data.getTaskId())
-					.last("limit 1"));
-			record.setStatus(TaskUserCompleteRecord.Status.received);
-			taskUserCompleteRecordMapper.updateById(record);
-			taskFollowPlatVerifyMapper.updateById(data);
-			marketFrontService.pointsRecord(record.getUserId(), record.getPoints(), PointsRecordConst.TASK, false);
-		});
-	}
-}
+//package com.cyksj.task;
+//
+//import cn.hutool.core.date.DateTime;
+//import cn.hutool.core.date.DateUtil;
+//import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+//import com.cyksj.common.constant.Constant;
+//import com.cyksj.common.constant.PointsRecordConst;
+//import com.cyksj.mapper.manage.statistics.SignStatisticsDataMapper;
+//import com.cyksj.mapper.market.sign.SignUserMapper;
+//import com.cyksj.mapper.market.sign.UserPointsRecordMapper;
+//import com.cyksj.mapper.market.task.TaskFollowPlatVerifyMapper;
+//import com.cyksj.mapper.market.task.TaskUserCompleteRecordMapper;
+//import com.cyksj.model.entity.SignStatisticsData;
+//import com.cyksj.model.entity.SignUser;
+//import com.cyksj.model.entity.TaskFollowPlatVerify;
+//import com.cyksj.model.entity.TaskUserCompleteRecord;
+//import com.cyksj.service.market.MarketFrontService;
+//import lombok.RequiredArgsConstructor;
+//import lombok.extern.slf4j.Slf4j;
+//import org.springframework.scheduling.annotation.Scheduled;
+//import org.springframework.stereotype.Component;
+//
+//import java.util.Date;
+//import java.util.List;
+//
+///*
+// *项目名: netflix
+// *文件名: SignStatisticsDataScheduler
+// *创建者: JavaZou
+// *创建时间:2022/12/27 18:27
+// */
+//@Component
+//@Slf4j
+//@RequiredArgsConstructor
+//public class SignStatisticsDataScheduler {
+//	private final SignStatisticsDataMapper signStatisticsDataMapper;
+//
+//	private final UserPointsRecordMapper userPointsRecordMapper;
+//
+//	private final SignUserMapper signUserMapper;
+//
+//	private final TaskFollowPlatVerifyMapper taskFollowPlatVerifyMapper;
+//
+//	private final TaskUserCompleteRecordMapper taskUserCompleteRecordMapper;
+//
+//	private final MarketFrontService marketFrontService;
+//
+//	@Scheduled(cron = "0 0 0 * * ?")
+//	public void signStatisticsData() {
+//		Date thisZeroDate = DateUtil.beginOfDay(DateTime.now());
+//		Date yesZeroDate = DateTime.of(thisZeroDate.getTime() - Constant.DAY_MILLS);
+//		SignStatisticsData signStatisticsData = new SignStatisticsData();
+//		//今日签到人数
+//		Integer todayNumOfSign = signUserMapper.selectCount(Wrappers.lambdaQuery(SignUser.class)
+//				.between(SignUser::getUpdateTime, yesZeroDate, thisZeroDate));
+//		signStatisticsData.setTodayNumOfSign(todayNumOfSign);
+//
+//		//今日发放积分
+//		Integer todayNumOfPoints = userPointsRecordMapper.getTodayNumOfPoints(yesZeroDate, thisZeroDate);
+//
+//		signStatisticsData.setTodayNumOfPoints(todayNumOfPoints);
+//
+//		//累计发放积分
+//		Integer sumOfPoints = userPointsRecordMapper.getSumOfPoints();
+//
+//		signStatisticsData.setSumOfPoints(sumOfPoints);
+//
+//		//今日使用积分
+//		Integer todayUseOfPoints = userPointsRecordMapper.getTodayUseOfPoints(yesZeroDate, thisZeroDate);
+//		signStatisticsData.setTodayUseOfPoints(-todayUseOfPoints);
+//
+//		//累计使用积分
+//		Integer sumUseOfPoints = userPointsRecordMapper.getSumUseOfPoints();
+//		signStatisticsData.setSumUseOfPoints(-sumUseOfPoints);
+//
+//		//今日抽奖次数
+//		Integer todayNumOfLottery = userPointsRecordMapper.getTodayNumOfLottery(PointsRecordConst.POINTS_LOTTERY, yesZeroDate, thisZeroDate);
+//		signStatisticsData.setTodayNumOfLottery(todayNumOfLottery);
+//
+//		//连签2天
+//		Integer sign2Day = userPointsRecordMapper.getSignDayByCondition(2, yesZeroDate, thisZeroDate);
+//		signStatisticsData.setT2(sign2Day);
+//
+//		//连签3天
+//		Integer sign3Day = userPointsRecordMapper.getSignDayByCondition(3, yesZeroDate, thisZeroDate);
+//		signStatisticsData.setT3(sign3Day);
+//
+//		//连签4天
+//		Integer sign4Day = userPointsRecordMapper.getSignDayByCondition(4, yesZeroDate, thisZeroDate);
+//		signStatisticsData.setT4(sign4Day);
+//
+//		//连签5天
+//		Integer sign5Day = userPointsRecordMapper.getSignDayByCondition(5, yesZeroDate, thisZeroDate);
+//		signStatisticsData.setT5(sign5Day);
+//
+//		//连签6天
+//		Integer sign6Day = userPointsRecordMapper.getSignDayByCondition(6, yesZeroDate, thisZeroDate);
+//		signStatisticsData.setT6(sign6Day);
+//
+//		//连签7天
+//		Integer sign7Day = userPointsRecordMapper.getSignDayByCondition(7, yesZeroDate, thisZeroDate);
+//		signStatisticsData.setT7(sign7Day);
+//
+//		signStatisticsData.setStatisticsDate(yesZeroDate);
+//		signStatisticsDataMapper.insert(signStatisticsData);
+//	}
+//
+//	/**
+//	 * 关注任务3天未审核 自动审核成功
+//	 */
+//	@Scheduled(cron = "0 30 0 * * ?")
+//	public void autoVerifySuccess() {
+//		DateTime now = DateTime.now();
+//		DateTime threeBefore = DateUtil.offsetDay(now, -3);
+//		List<TaskFollowPlatVerify> taskFollowPlatVerifies = taskFollowPlatVerifyMapper.selectList(Wrappers.lambdaQuery(TaskFollowPlatVerify.class)
+//				.le(TaskFollowPlatVerify::getCreatedTime, threeBefore)
+//				.eq(TaskFollowPlatVerify::getStatus, TaskFollowPlatVerify.Status.verifying));
+//		taskFollowPlatVerifies.forEach(data->{
+//			data.setStatus(TaskFollowPlatVerify.Status.success);
+//			TaskUserCompleteRecord record = taskUserCompleteRecordMapper.selectOne(Wrappers.lambdaQuery(TaskUserCompleteRecord.class)
+//					.eq(TaskUserCompleteRecord::getUserId, data.getUserId())
+//					.eq(TaskUserCompleteRecord::getTaskId, data.getTaskId())
+//					.last("limit 1"));
+//			record.setStatus(TaskUserCompleteRecord.Status.received);
+//			taskUserCompleteRecordMapper.updateById(record);
+//			taskFollowPlatVerifyMapper.updateById(data);
+//			marketFrontService.pointsRecord(record.getUserId(), record.getPoints(), PointsRecordConst.TASK, false);
+//		});
+//	}
+//}

+ 395 - 395
netflix-service/src/main/java/com/cyksj/task/StatisticsDataScheduler.java

@@ -1,395 +1,395 @@
-package com.cyksj.task;
-
-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 com.baomidou.mybatisplus.core.toolkit.Wrappers;
-import com.cyksj.common.constant.Constant;
-import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
-import com.cyksj.mapper.GoodsDonMapper;
-import com.cyksj.mapper.RegisterAccountDataMapper;
-import com.cyksj.mapper.RegisterOrderDonMapper;
-import com.cyksj.mapper.corp.CorpUserAuthMapper;
-import com.cyksj.mapper.manage.statistics.*;
-import com.cyksj.model.entity.*;
-import com.cyksj.model.views.CorpUserTagView;
-import com.cyksj.model.views.StatisticsGoodsDataView;
-import com.cyksj.service.mange.CmsAccountService;
-import com.cyksj.service.mange.CmsGoodsDonSkuService;
-import com.cyksj.service.order.OrderDonService;
-import com.ejlchina.searcher.BeanSearcher;
-import com.ejlchina.searcher.util.MapBuilder;
-import com.ejlchina.searcher.util.MapUtils;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.scheduling.annotation.Scheduled;
-import org.springframework.stereotype.Component;
-
-import java.math.BigDecimal;
-import java.util.*;
-import java.util.stream.Collectors;
-
-/*
- *项目名: netflix
- *文件名: StatisticsDataScheduler
- *创建者: JavaZou
- *创建时间:2022/10/18 12:07
- */
-@Component
-@Slf4j
-@RequiredArgsConstructor
-public class StatisticsDataScheduler {
-
-	private final OrderDonService orderDonService;
-
-	private final CmsGoodsDonSkuService goodsDonSkuService;
-
-	private final CmsAccountService accountService;
-
-	private static final GlobalThreadPoolTaskExecutor TASK_POOL = GlobalThreadPoolTaskExecutor.getInstance();
-
-	private final AppleOneDataMapper appleOneDataMapper;
-
-	private final StreamingAccountDataMapper streamingAccountDataMapper;
-
-	private final GoodsDonMapper goodsDonMapper;
-
-	private final RealGoodsDataMapper realGoodsDataMapper;
-
-	private final RegisterOrderDonMapper registerOrderDonMapper;
-
-	private final RegisterAccountDataMapper registerAccountDataMapper;
-
-	private final StatisticsUserDataMapper statisticsUserDataMapper;
-
-	private final StatisticsFashionGoodsDataMapper statisticsFashionGoodsDataMapper;
-
-	private final BeanSearcher beanSearcher;
-
-	private final CorpUserAuthMapper corpUserAuthMapper;
-
-	/**
-	 * 后台数据统计 定时器
-	 */
-	@Scheduled(cron = "0 0 1 * * ?")
-	public void statisticsData() {
-		Date thisZeroDate = DateUtil.beginOfDay(DateTime.now());
-		Date yesZeroDate = DateTime.of(thisZeroDate.getTime() - Constant.DAY_MILLS);
-		//统计AppleOne数据
-		TASK_POOL.execute(() -> statisticsAppleOneData(yesZeroDate, thisZeroDate, false));
-		//统计流媒体账号数据
-		TASK_POOL.execute(() -> statisticsStreamingAccountData(yesZeroDate, thisZeroDate, false));
-		//统计实物数据
-		TASK_POOL.execute(() -> statisticsRealGoodsData(yesZeroDate, thisZeroDate, false));
-		//注册账号数据
-		TASK_POOL.execute(() -> statisticsRegisterAccountData(yesZeroDate, thisZeroDate, false));
-		//用户数据统计
-		statisticsUserData(yesZeroDate, thisZeroDate);
-		//潮玩好物数据统计
-		statisticsFashionGoodsData(yesZeroDate, thisZeroDate, false);
-
-	}
-
-	public List<StatisticsFashionGoodsData> statisticsFashionGoodsData(Date yesZeroDate, Date thisZeroDate, Boolean today) {
-		//潮玩好物
-		List<GoodsDon> goodsDons = goodsDonMapper.selectList(Wrappers.lambdaQuery(GoodsDon.class).eq(GoodsDon::getType, Constant.realGoodsType.get(1)));
-		List<StatisticsFashionGoodsData> fashionGoodsData = new ArrayList<>();
-		goodsDons.forEach(goods->{
-			List<StatisticsGoodsDataView> goodsDataViews = beanSearcher.searchAll(StatisticsGoodsDataView.class, MapUtils.builder()
-					.field(StatisticsGoodsDataView::getGoodsId, goods.getId())
-					.put("time", String.format("od.created_time between '%s' and '%s'", yesZeroDate, thisZeroDate))
-					.build());
-			goodsDataViews.forEach(goodsDataView->{
-				StatisticsFashionGoodsData statisticsFashionGoodsData = new StatisticsFashionGoodsData();
-				BeanUtil.copyProperties(goodsDataView, statisticsFashionGoodsData);
-				fashionGoodsData.add(statisticsFashionGoodsData);
-			});
-		});
-		if (today) {
-			return fashionGoodsData;
-		}
-		if (!fashionGoodsData.isEmpty()) {
-			fashionGoodsData.forEach(data -> {
-				data.setStatisticsDate(yesZeroDate);
-				statisticsFashionGoodsDataMapper.insert(data);
-			});
-		}
-		return null;
-	}
-
-	/**
-	 * 用户数据统计
-	 */
-	private void statisticsUserData(Date yesZeroDate, Date thisZeroDate) {
-		StatisticsUserData yesData = statisticsUserDataMapper.selectOne(Wrappers.lambdaQuery(StatisticsUserData.class).orderByDesc(StatisticsUserData::getId)
-				.lt(StatisticsUserData::getStatisticsDate, yesZeroDate)
-				.select(StatisticsUserData::getNumOfUser, StatisticsUserData::getPcNumOfUser).last("limit 1"));
-		Integer yesNumOfUser = yesData == null ? 0 : yesData.getNumOfUser();
-		Integer yesPcNumOfUser = yesData == null ? 0 : yesData.getPcNumOfUser();
-		StatisticsUserData statisticsUserData = Optional.ofNullable(statisticsUserDataMapper.selectOne(Wrappers.lambdaQuery(StatisticsUserData.class).eq(StatisticsUserData::getStatisticsDate, yesZeroDate).last("limit 1"))).orElse(new StatisticsUserData());
-		statisticsUserData.setNumOfNewUser(statisticsUserDataMapper.getNumOfNewUser(yesZeroDate, thisZeroDate));
-		statisticsUserData.setNumOfActiveUser(statisticsUserDataMapper.getNumOfActiveUser(yesZeroDate, thisZeroDate));
-		statisticsUserData.setNumOfUser(yesNumOfUser + statisticsUserData.getNumOfNewUser());
-
-		statisticsUserData.setNumOfDistributeUser(statisticsUserDataMapper.getNumOfDistributeUserToday(yesZeroDate, thisZeroDate));
-
-		List<CorpUserAuth> corpUserAuths = corpUserAuthMapper.selectList(Wrappers.lambdaQuery(CorpUserAuth.class)
-				.between(CorpUserAuth::getCreatedTime, yesZeroDate, thisZeroDate).select(CorpUserAuth::getExternalUserid).groupBy(CorpUserAuth::getExternalUserid));
-		Integer numOfCorpUser = corpUserAuths.isEmpty() ? 0 : corpUserAuths.size();
-		statisticsUserData.setNumOfCorpUser(numOfCorpUser);
-		MapBuilder builder = MapUtils.builder()
-				.put("time", String.format("cfr.created_time between '%s' and '%s'", yesZeroDate, thisZeroDate))
-				.group("cfr.corp_user_id");
-		Number equipments = beanSearcher.searchCount(CorpUserTagView.class, builder.put("tagName", "ct.tag_name = '设备'").build());
-		statisticsUserData.setNumOfCorpEquipmentUser(equipments.intValue());
-		Number accounts = beanSearcher.searchCount(CorpUserTagView.class, builder.put("tagName", "ct.tag_name = '账号'").build());
-		statisticsUserData.setNumOfCorpAccountUser(accounts.intValue());
-
-		statisticsUserData.setPcNumOfNewUser(statisticsUserDataMapper.getPcNumOfNewUser(yesZeroDate, thisZeroDate));
-		statisticsUserData.setPcNumOfActiveUser(statisticsUserDataMapper.getPcNumOfActiveUser(yesZeroDate, thisZeroDate));
-		statisticsUserData.setPcNumOfUser(yesPcNumOfUser + statisticsUserData.getPcNumOfNewUser());
-
-		statisticsUserData.setStatisticsDate(yesZeroDate);
-		if (statisticsUserData.getId() != null) {
-			statisticsUserDataMapper.updateById(statisticsUserData);
-			return;
-		}
-		statisticsUserDataMapper.insert(statisticsUserData);
-	}
-
-	/**
-	 * 注册账号数据
-	 */
-	public List<RegisterAccountData> statisticsRegisterAccountData(Date yesZeroDate, Date thisZeroDate, Boolean today) {
-		List<RegisterAccountData> list = new ArrayList<>();
-		Constant.registerAccount.forEach(platform -> {
-			RegisterAccountData entity = Optional.ofNullable(registerOrderDonMapper.registerAccountDataEveryDay(yesZeroDate, thisZeroDate, platform)).orElse(new RegisterAccountData());
-			entity.setPlatform(platform);
-			RegisterAccountData dto = Optional.ofNullable(registerOrderDonMapper.selectRefundOrderDetail(yesZeroDate, thisZeroDate, platform)).orElse(new RegisterAccountData());
-			entity.setNumOfRefundOrder(dto.getNumOfRefundOrder());
-			entity.setRefundMoney(dto.getRefundMoney());
-			entity.setStatisticsDate(yesZeroDate);
-			if (today) {
-				list.add(entity);
-				return;
-			}
-			registerAccountDataMapper.insert(entity);
-		});
-		if (today) {
-			return list;
-		}
-		return null;
-	}
-
-	/**
-	 * 统计AppleOne数据
-	 *
-	 * @return
-	 */
-	public List<AppleOneData> statisticsAppleOneData(Date yesZeroDate, Date thisZeroDate, Boolean today) {
-		Map<String, List<GoodsDonSku>> areaDataMap = getAppleOneMap();
-
-		List<AbstractBackData> appleOneSave = new ArrayList<>();
-		List<AppleOneData> appleOneData = new ArrayList<>();
-		areaDataMap.forEach((areaName, skus) -> {
-			AppleOneData appleOne = new AppleOneData();
-			appleOne.setAreaName(areaName);
-			//通过skuIds找到对应的appleOne统计数据
-			List<Long> skuIds = skus.stream().map(GoodsDonSku::getId).collect(Collectors.toList());
-			List<OrderDon> orderDons = Optional.ofNullable(orderDonService.getAppleOneOrderDons(skuIds, Constant.noOrderStatus, yesZeroDate, thisZeroDate)).orElse(new ArrayList<>());
-			appleOneDataStatistics(orderDons, appleOne, appleOneSave);
-		});
-
-		for (AbstractBackData appleOne : appleOneSave) {
-			appleOne.setStatisticsDate(yesZeroDate);
-			if (today) {
-				appleOneData.add((AppleOneData) appleOne);
-				continue;
-			}
-			appleOneDataMapper.insert((AppleOneData) appleOne);
-		}
-		if (today) {
-			return appleOneData;
-		}
-		return null;
-	}
-
-	private void appleOneDataStatistics(List<OrderDon> orderDons, AppleOneData appleOne, List<AbstractBackData> appleOneSave) {
-		if (CollUtil.isNotEmpty(orderDons)) {
-			//用户人数
-			Set<Long> numOfUser = Optional.ofNullable(orderDons.stream().map(OrderDon::getUserId).collect(Collectors.toSet())).orElse(new HashSet<>());
-			appleOne.setNumOfUser(numOfUser.size());
-			//订单数
-			appleOne.setNumOfOrder(orderDons.size());
-			//兑换码订单数
-			List<OrderDon> exCodeOrders = Optional.ofNullable(orderDons.stream().filter(order -> order.getType() == OrderDon.Type.EX_CODE).collect(Collectors.toList())).orElse(new ArrayList<>());
-			appleOne.setNumOfCodeOrder(exCodeOrders.size());
-			//金额
-			Optional<BigDecimal> money = orderDons.stream().map(OrderDon::getMoney).reduce(BigDecimal::add);
-			if (money.isPresent()) {
-				appleOne.setMoney(money.get());
-			} else appleOne.setMoney(BigDecimal.ZERO);
-			//分销订单
-			List<OrderDon> distributeOrders = Optional.ofNullable(orderDons.stream().filter(ord -> ord.getIsDistribute()).collect(Collectors.toList())).orElse(new ArrayList<>());
-			appleOne.setNumOfDistributeOrder(distributeOrders.size());
-			Optional<BigDecimal> distributeMoney = distributeOrders.stream().map(OrderDon::getMoney).reduce(BigDecimal::add);
-			appleOne.setDistributeOrderMoney(distributeMoney.isPresent() ? distributeMoney.get() : BigDecimal.ZERO);
-
-			getNumOfOrderByPayType(orderDons, appleOne);
-		}
-		appleOneSave.add(appleOne);
-	}
-
-	/**
-	 * 统计流媒体账号数据
-	 */
-	public List<StreamingAccountData> statisticsStreamingAccountData(Date yesZeroDate, Date thisZeroDate, Boolean today) {
-		List<Account> accounts = accountService.getNoAppleOneAccount(2l);
-		Map<Long, List<Account>> accountMaps = accounts.stream().collect(Collectors.groupingBy(Account::getGoodsId));
-		List<StreamingAccountData> streamingAccountDataSave = new ArrayList<>(accounts.size());
-		accountMaps.forEach((goodsId, accountList) -> {
-			StreamingAccountData streamingAccount = new StreamingAccountData();
-			streamingAccount.setAccountName(accountList.get(0).getTitle());
-			streamingAccount.setGoodsId(goodsId);
-			streamingAccountDataStatistics(goodsId, streamingAccount, streamingAccountDataSave, yesZeroDate, thisZeroDate);
-		});
-		if (today) {
-			return streamingAccountDataSave;
-		}
-		for (StreamingAccountData streamingAccountData : streamingAccountDataSave) {
-			streamingAccountData.setStatisticsDate(yesZeroDate);
-			streamingAccountDataMapper.insert(streamingAccountData);
-		}
-		return null;
-	}
-
-	/**
-	 * 统计实物数据
-	 */
-	public List<RealGoodsData> statisticsRealGoodsData(Date yesZeroDate, Date thisZeroDate, Boolean today) {
-		List<GoodsDon> goodsDons = goodsDonMapper.selectList(Wrappers.lambdaQuery(GoodsDon.class).eq(GoodsDon::getType, Constant.realGoodsType.get(0)));
-		List<RealGoodsData> realGoodsDataList = new ArrayList<>();
-		if (!goodsDons.isEmpty()) {
-			goodsDons.forEach(goods -> {
-				List<GoodsDonSku> skuList = goodsDonSkuService.list(Wrappers.lambdaQuery(GoodsDonSku.class).eq(GoodsDonSku::getGoodsId, goods.getId()));
-				if (!skuList.isEmpty()) {
-					skuList.forEach(sku -> {
-						RealGoodsData realGoodsData = new RealGoodsData();
-						realGoodsData.setGoodsId(goods.getId());
-						realGoodsData.setGoodsTitle(goods.getTitle());
-						realGoodsData.setSkuId(sku.getId());
-						realGoodsData.setSpecVal(sku.getSpecVal());
-						//订单
-						List<OrderDon> orderDon = Optional.ofNullable(orderDonService.list(Wrappers.lambdaQuery(OrderDon.class).eq(OrderDon::getSkuId, sku.getId()).between(OrderDon::getCreatedTime, yesZeroDate, thisZeroDate).notIn(OrderDon::getStatus, Constant.noOrderStatus).select(OrderDon::getUserId, OrderDon::getMoney, OrderDon::getId))).orElse(new ArrayList<>());
-						Set<Long> userList = Optional.ofNullable(orderDon.stream().map(OrderDon::getUserId).collect(Collectors.toSet())).orElse(new HashSet<>());
-						//用户数
-						realGoodsData.setNumOfUser(userList.size());
-						//订单数
-						realGoodsData.setNumOfOrder(orderDon.size());
-						//金额
-						Optional<BigDecimal> money = orderDon.stream().map(OrderDon::getMoney).reduce(BigDecimal::add);
-						realGoodsData.setMoney(money.isPresent() ? money.get() : BigDecimal.ZERO);
-						realGoodsDataList.add(realGoodsData);
-					});
-				}
-			});
-		}
-		if (today) {
-			return realGoodsDataList;
-		}
-		if (!realGoodsDataList.isEmpty()) {
-			realGoodsDataList.forEach(data -> {
-				data.setStatisticsDate(yesZeroDate);
-				realGoodsDataMapper.insert(data);
-			});
-		}
-		return null;
-	}
-
-	/**
-	 * @param goodsId              平台id
-	 * @param streamingAccountData
-	 * @param dataSave             入库实体集合
-	 * @param yesZeroDate          昨日零时
-	 * @param thisZeroDate         今日零时
-	 */
-	private void streamingAccountDataStatistics(Long goodsId, StreamingAccountData streamingAccountData, List<StreamingAccountData> dataSave, Date yesZeroDate, Date thisZeroDate) {
-		//规格
-		List<GoodsDonSku> list = Optional.ofNullable(goodsDonSkuService.list(Wrappers.lambdaQuery(GoodsDonSku.class).eq(GoodsDonSku::getGoodsId, goodsId).select(GoodsDonSku::getId))).orElse(new ArrayList<>());
-		List<Long> skuIds = list.stream().map(GoodsDonSku::getId).collect(Collectors.toList());
-		if (CollUtil.isNotEmpty(skuIds)) {
-			List<OrderDon> orderDons = Optional.ofNullable(accountService.getNoAppleOneOrderDon(skuIds, yesZeroDate, thisZeroDate, Constant.noOrderStatus)).orElse(new ArrayList<>());
-			//用户数
-			streamingAccountData.setNumOfUser(orderDons.stream().map(OrderDon::getUserId).collect(Collectors.toSet()).size());
-			getNumOfOrderByPayType(orderDons, streamingAccountData);
-			//兑换码订单数
-			List<OrderDon> exCodeOrders = Optional.ofNullable(orderDons.stream().filter(order -> order.getType() == OrderDon.Type.EX_CODE).collect(Collectors.toList())).orElse(new ArrayList<>());
-			streamingAccountData.setNumOfCodeOrder(exCodeOrders.size());
-			streamingAccountData.setNumOfOrder(orderDons.size());
-			Optional<BigDecimal> reduce = orderDons.stream().map(OrderDon::getMoney).reduce(BigDecimal::add);
-			BigDecimal totalMoney = BigDecimal.valueOf(0);
-			if (reduce.isPresent()) {
-				totalMoney = reduce.get();
-			}
-			//金额
-			streamingAccountData.setMoney(totalMoney);
-			//分销订单
-			List<OrderDon> distributeOrders = Optional.ofNullable(orderDons.stream().filter(ord -> ord.getIsDistribute()).collect(Collectors.toList())).orElse(new ArrayList<>());
-			streamingAccountData.setNumOfDistributeOrder(distributeOrders.size());
-			Optional<BigDecimal> distributeMoney = distributeOrders.stream().map(OrderDon::getMoney).reduce(BigDecimal::add);
-			streamingAccountData.setDistributeOrderMoney(distributeMoney.isPresent() ? distributeMoney.get() : BigDecimal.ZERO);
-		}
-		dataSave.add(streamingAccountData);
-	}
-
-	/**
-	 * 根据不同支付条件统计对应月、季、年订单
-	 */
-	private void getNumOfOrderByPayType(List<OrderDon> orderDons, AbstractBackData backData) {
-		Integer numOfMonthOrder = 0;
-		Integer numOfSeasonOrder = 0;
-		Integer numOfYearOrder = 0;
-		Map<Long, List<OrderDon>> skuIdMaps = Optional.ofNullable(orderDons.stream().collect(Collectors.groupingBy(OrderDon::getSkuId))).orElse(new HashMap<>());
-		if (!skuIdMaps.isEmpty()) {
-			List<GoodsDonSku> goodsDonSkus = Optional.ofNullable(goodsDonSkuService.list(Wrappers.lambdaQuery(GoodsDonSku.class).in(GoodsDonSku::getId, skuIdMaps.keySet()))).orElse(new ArrayList<>());
-			for (GoodsDonSku donSkus : goodsDonSkus) {
-				int size = skuIdMaps.get(donSkus.getId()).size();
-				if (donSkus.getSpecVal().contains(Constant.payType.get(0)) && !donSkus.getSpecVal().contains(Constant.payType.get(2))) {
-					numOfMonthOrder += size;
-				} else if (donSkus.getSpecVal().contains(Constant.payType.get(1))) {
-					numOfSeasonOrder += size;
-				} else if (donSkus.getSpecVal().contains(Constant.payType.get(2))) {
-					numOfYearOrder += size;
-				}
-			}
-		}
-		//月付
-		backData.setNumOfMonthOrder(numOfMonthOrder);
-		//季付
-		backData.setNumOfSeasonOrder(numOfSeasonOrder);
-		//年付
-		backData.setNumOfYearOrder(numOfYearOrder);
-	}
-
-	/**
-	 * 获取Apple区域map
-	 */
-	public Map<String, List<GoodsDonSku>> getAppleOneMap() {
-		GoodsDon goodsDon = goodsDonMapper.selectOne(Wrappers.lambdaQuery(GoodsDon.class).eq(GoodsDon::getId, 2).last("limit 1"));
-		List<GoodsDonSku> list = goodsDonSkuService.list(Wrappers.lambdaQuery(GoodsDonSku.class).eq(GoodsDonSku::getGoodsId, goodsDon.getId()).select(GoodsDonSku::getId, GoodsDonSku::getSpecVal));
-		Set<String> area = new HashSet<>();
-		list.forEach(sku -> {
-			String areaName = sku.getSpecVal().substring(0, sku.getSpecVal().indexOf("区") + 1);
-			if (!area.contains(areaName)) {
-				area.add(areaName);
-			}
-		});
-		Map<String, List<GoodsDonSku>> areaDataMap = new HashMap<>();
-		for (String name : area) {
-			List<GoodsDonSku> sku = Optional.ofNullable(goodsDonSkuService.list(Wrappers.lambdaQuery(GoodsDonSku.class).like(GoodsDonSku::getSpecVal, name).select(GoodsDonSku::getId, GoodsDonSku::getGoodsId, GoodsDonSku::getSpecVal))).orElse(new ArrayList<>());
-			areaDataMap.put(name, sku);
-		}
-		return areaDataMap;
-	}
-}
+//package com.cyksj.task;
+//
+//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 com.baomidou.mybatisplus.core.toolkit.Wrappers;
+//import com.cyksj.common.constant.Constant;
+//import com.cyksj.common.task.GlobalThreadPoolTaskExecutor;
+//import com.cyksj.mapper.GoodsDonMapper;
+//import com.cyksj.mapper.RegisterAccountDataMapper;
+//import com.cyksj.mapper.RegisterOrderDonMapper;
+//import com.cyksj.mapper.corp.CorpUserAuthMapper;
+//import com.cyksj.mapper.manage.statistics.*;
+//import com.cyksj.model.entity.*;
+//import com.cyksj.model.views.CorpUserTagView;
+//import com.cyksj.model.views.StatisticsGoodsDataView;
+//import com.cyksj.service.mange.CmsAccountService;
+//import com.cyksj.service.mange.CmsGoodsDonSkuService;
+//import com.cyksj.service.order.OrderDonService;
+//import com.ejlchina.searcher.BeanSearcher;
+//import com.ejlchina.searcher.util.MapBuilder;
+//import com.ejlchina.searcher.util.MapUtils;
+//import lombok.RequiredArgsConstructor;
+//import lombok.extern.slf4j.Slf4j;
+//import org.springframework.scheduling.annotation.Scheduled;
+//import org.springframework.stereotype.Component;
+//
+//import java.math.BigDecimal;
+//import java.util.*;
+//import java.util.stream.Collectors;
+//
+///*
+// *项目名: netflix
+// *文件名: StatisticsDataScheduler
+// *创建者: JavaZou
+// *创建时间:2022/10/18 12:07
+// */
+//@Component
+//@Slf4j
+//@RequiredArgsConstructor
+//public class StatisticsDataScheduler {
+//
+//	private final OrderDonService orderDonService;
+//
+//	private final CmsGoodsDonSkuService goodsDonSkuService;
+//
+//	private final CmsAccountService accountService;
+//
+//	private static final GlobalThreadPoolTaskExecutor TASK_POOL = GlobalThreadPoolTaskExecutor.getInstance();
+//
+//	private final AppleOneDataMapper appleOneDataMapper;
+//
+//	private final StreamingAccountDataMapper streamingAccountDataMapper;
+//
+//	private final GoodsDonMapper goodsDonMapper;
+//
+//	private final RealGoodsDataMapper realGoodsDataMapper;
+//
+//	private final RegisterOrderDonMapper registerOrderDonMapper;
+//
+//	private final RegisterAccountDataMapper registerAccountDataMapper;
+//
+//	private final StatisticsUserDataMapper statisticsUserDataMapper;
+//
+//	private final StatisticsFashionGoodsDataMapper statisticsFashionGoodsDataMapper;
+//
+//	private final BeanSearcher beanSearcher;
+//
+//	private final CorpUserAuthMapper corpUserAuthMapper;
+//
+//	/**
+//	 * 后台数据统计 定时器
+//	 */
+//	@Scheduled(cron = "0 0 1 * * ?")
+//	public void statisticsData() {
+//		Date thisZeroDate = DateUtil.beginOfDay(DateTime.now());
+//		Date yesZeroDate = DateTime.of(thisZeroDate.getTime() - Constant.DAY_MILLS);
+//		//统计AppleOne数据
+//		TASK_POOL.execute(() -> statisticsAppleOneData(yesZeroDate, thisZeroDate, false));
+//		//统计流媒体账号数据
+//		TASK_POOL.execute(() -> statisticsStreamingAccountData(yesZeroDate, thisZeroDate, false));
+//		//统计实物数据
+//		TASK_POOL.execute(() -> statisticsRealGoodsData(yesZeroDate, thisZeroDate, false));
+//		//注册账号数据
+//		TASK_POOL.execute(() -> statisticsRegisterAccountData(yesZeroDate, thisZeroDate, false));
+//		//用户数据统计
+//		statisticsUserData(yesZeroDate, thisZeroDate);
+//		//潮玩好物数据统计
+//		statisticsFashionGoodsData(yesZeroDate, thisZeroDate, false);
+//
+//	}
+//
+//	public List<StatisticsFashionGoodsData> statisticsFashionGoodsData(Date yesZeroDate, Date thisZeroDate, Boolean today) {
+//		//潮玩好物
+//		List<GoodsDon> goodsDons = goodsDonMapper.selectList(Wrappers.lambdaQuery(GoodsDon.class).eq(GoodsDon::getType, Constant.realGoodsType.get(1)));
+//		List<StatisticsFashionGoodsData> fashionGoodsData = new ArrayList<>();
+//		goodsDons.forEach(goods->{
+//			List<StatisticsGoodsDataView> goodsDataViews = beanSearcher.searchAll(StatisticsGoodsDataView.class, MapUtils.builder()
+//					.field(StatisticsGoodsDataView::getGoodsId, goods.getId())
+//					.put("time", String.format("od.created_time between '%s' and '%s'", yesZeroDate, thisZeroDate))
+//					.build());
+//			goodsDataViews.forEach(goodsDataView->{
+//				StatisticsFashionGoodsData statisticsFashionGoodsData = new StatisticsFashionGoodsData();
+//				BeanUtil.copyProperties(goodsDataView, statisticsFashionGoodsData);
+//				fashionGoodsData.add(statisticsFashionGoodsData);
+//			});
+//		});
+//		if (today) {
+//			return fashionGoodsData;
+//		}
+//		if (!fashionGoodsData.isEmpty()) {
+//			fashionGoodsData.forEach(data -> {
+//				data.setStatisticsDate(yesZeroDate);
+//				statisticsFashionGoodsDataMapper.insert(data);
+//			});
+//		}
+//		return null;
+//	}
+//
+//	/**
+//	 * 用户数据统计
+//	 */
+//	private void statisticsUserData(Date yesZeroDate, Date thisZeroDate) {
+//		StatisticsUserData yesData = statisticsUserDataMapper.selectOne(Wrappers.lambdaQuery(StatisticsUserData.class).orderByDesc(StatisticsUserData::getId)
+//				.lt(StatisticsUserData::getStatisticsDate, yesZeroDate)
+//				.select(StatisticsUserData::getNumOfUser, StatisticsUserData::getPcNumOfUser).last("limit 1"));
+//		Integer yesNumOfUser = yesData == null ? 0 : yesData.getNumOfUser();
+//		Integer yesPcNumOfUser = yesData == null ? 0 : yesData.getPcNumOfUser();
+//		StatisticsUserData statisticsUserData = Optional.ofNullable(statisticsUserDataMapper.selectOne(Wrappers.lambdaQuery(StatisticsUserData.class).eq(StatisticsUserData::getStatisticsDate, yesZeroDate).last("limit 1"))).orElse(new StatisticsUserData());
+//		statisticsUserData.setNumOfNewUser(statisticsUserDataMapper.getNumOfNewUser(yesZeroDate, thisZeroDate));
+//		statisticsUserData.setNumOfActiveUser(statisticsUserDataMapper.getNumOfActiveUser(yesZeroDate, thisZeroDate));
+//		statisticsUserData.setNumOfUser(yesNumOfUser + statisticsUserData.getNumOfNewUser());
+//
+//		statisticsUserData.setNumOfDistributeUser(statisticsUserDataMapper.getNumOfDistributeUserToday(yesZeroDate, thisZeroDate));
+//
+//		List<CorpUserAuth> corpUserAuths = corpUserAuthMapper.selectList(Wrappers.lambdaQuery(CorpUserAuth.class)
+//				.between(CorpUserAuth::getCreatedTime, yesZeroDate, thisZeroDate).select(CorpUserAuth::getExternalUserid).groupBy(CorpUserAuth::getExternalUserid));
+//		Integer numOfCorpUser = corpUserAuths.isEmpty() ? 0 : corpUserAuths.size();
+//		statisticsUserData.setNumOfCorpUser(numOfCorpUser);
+//		MapBuilder builder = MapUtils.builder()
+//				.put("time", String.format("cfr.created_time between '%s' and '%s'", yesZeroDate, thisZeroDate))
+//				.group("cfr.corp_user_id");
+//		Number equipments = beanSearcher.searchCount(CorpUserTagView.class, builder.put("tagName", "ct.tag_name = '设备'").build());
+//		statisticsUserData.setNumOfCorpEquipmentUser(equipments.intValue());
+//		Number accounts = beanSearcher.searchCount(CorpUserTagView.class, builder.put("tagName", "ct.tag_name = '账号'").build());
+//		statisticsUserData.setNumOfCorpAccountUser(accounts.intValue());
+//
+//		statisticsUserData.setPcNumOfNewUser(statisticsUserDataMapper.getPcNumOfNewUser(yesZeroDate, thisZeroDate));
+//		statisticsUserData.setPcNumOfActiveUser(statisticsUserDataMapper.getPcNumOfActiveUser(yesZeroDate, thisZeroDate));
+//		statisticsUserData.setPcNumOfUser(yesPcNumOfUser + statisticsUserData.getPcNumOfNewUser());
+//
+//		statisticsUserData.setStatisticsDate(yesZeroDate);
+//		if (statisticsUserData.getId() != null) {
+//			statisticsUserDataMapper.updateById(statisticsUserData);
+//			return;
+//		}
+//		statisticsUserDataMapper.insert(statisticsUserData);
+//	}
+//
+//	/**
+//	 * 注册账号数据
+//	 */
+//	public List<RegisterAccountData> statisticsRegisterAccountData(Date yesZeroDate, Date thisZeroDate, Boolean today) {
+//		List<RegisterAccountData> list = new ArrayList<>();
+//		Constant.registerAccount.forEach(platform -> {
+//			RegisterAccountData entity = Optional.ofNullable(registerOrderDonMapper.registerAccountDataEveryDay(yesZeroDate, thisZeroDate, platform)).orElse(new RegisterAccountData());
+//			entity.setPlatform(platform);
+//			RegisterAccountData dto = Optional.ofNullable(registerOrderDonMapper.selectRefundOrderDetail(yesZeroDate, thisZeroDate, platform)).orElse(new RegisterAccountData());
+//			entity.setNumOfRefundOrder(dto.getNumOfRefundOrder());
+//			entity.setRefundMoney(dto.getRefundMoney());
+//			entity.setStatisticsDate(yesZeroDate);
+//			if (today) {
+//				list.add(entity);
+//				return;
+//			}
+//			registerAccountDataMapper.insert(entity);
+//		});
+//		if (today) {
+//			return list;
+//		}
+//		return null;
+//	}
+//
+//	/**
+//	 * 统计AppleOne数据
+//	 *
+//	 * @return
+//	 */
+//	public List<AppleOneData> statisticsAppleOneData(Date yesZeroDate, Date thisZeroDate, Boolean today) {
+//		Map<String, List<GoodsDonSku>> areaDataMap = getAppleOneMap();
+//
+//		List<AbstractBackData> appleOneSave = new ArrayList<>();
+//		List<AppleOneData> appleOneData = new ArrayList<>();
+//		areaDataMap.forEach((areaName, skus) -> {
+//			AppleOneData appleOne = new AppleOneData();
+//			appleOne.setAreaName(areaName);
+//			//通过skuIds找到对应的appleOne统计数据
+//			List<Long> skuIds = skus.stream().map(GoodsDonSku::getId).collect(Collectors.toList());
+//			List<OrderDon> orderDons = Optional.ofNullable(orderDonService.getAppleOneOrderDons(skuIds, Constant.noOrderStatus, yesZeroDate, thisZeroDate)).orElse(new ArrayList<>());
+//			appleOneDataStatistics(orderDons, appleOne, appleOneSave);
+//		});
+//
+//		for (AbstractBackData appleOne : appleOneSave) {
+//			appleOne.setStatisticsDate(yesZeroDate);
+//			if (today) {
+//				appleOneData.add((AppleOneData) appleOne);
+//				continue;
+//			}
+//			appleOneDataMapper.insert((AppleOneData) appleOne);
+//		}
+//		if (today) {
+//			return appleOneData;
+//		}
+//		return null;
+//	}
+//
+//	private void appleOneDataStatistics(List<OrderDon> orderDons, AppleOneData appleOne, List<AbstractBackData> appleOneSave) {
+//		if (CollUtil.isNotEmpty(orderDons)) {
+//			//用户人数
+//			Set<Long> numOfUser = Optional.ofNullable(orderDons.stream().map(OrderDon::getUserId).collect(Collectors.toSet())).orElse(new HashSet<>());
+//			appleOne.setNumOfUser(numOfUser.size());
+//			//订单数
+//			appleOne.setNumOfOrder(orderDons.size());
+//			//兑换码订单数
+//			List<OrderDon> exCodeOrders = Optional.ofNullable(orderDons.stream().filter(order -> order.getType() == OrderDon.Type.EX_CODE).collect(Collectors.toList())).orElse(new ArrayList<>());
+//			appleOne.setNumOfCodeOrder(exCodeOrders.size());
+//			//金额
+//			Optional<BigDecimal> money = orderDons.stream().map(OrderDon::getMoney).reduce(BigDecimal::add);
+//			if (money.isPresent()) {
+//				appleOne.setMoney(money.get());
+//			} else appleOne.setMoney(BigDecimal.ZERO);
+//			//分销订单
+//			List<OrderDon> distributeOrders = Optional.ofNullable(orderDons.stream().filter(ord -> ord.getIsDistribute()).collect(Collectors.toList())).orElse(new ArrayList<>());
+//			appleOne.setNumOfDistributeOrder(distributeOrders.size());
+//			Optional<BigDecimal> distributeMoney = distributeOrders.stream().map(OrderDon::getMoney).reduce(BigDecimal::add);
+//			appleOne.setDistributeOrderMoney(distributeMoney.isPresent() ? distributeMoney.get() : BigDecimal.ZERO);
+//
+//			getNumOfOrderByPayType(orderDons, appleOne);
+//		}
+//		appleOneSave.add(appleOne);
+//	}
+//
+//	/**
+//	 * 统计流媒体账号数据
+//	 */
+//	public List<StreamingAccountData> statisticsStreamingAccountData(Date yesZeroDate, Date thisZeroDate, Boolean today) {
+//		List<Account> accounts = accountService.getNoAppleOneAccount(2l);
+//		Map<Long, List<Account>> accountMaps = accounts.stream().collect(Collectors.groupingBy(Account::getGoodsId));
+//		List<StreamingAccountData> streamingAccountDataSave = new ArrayList<>(accounts.size());
+//		accountMaps.forEach((goodsId, accountList) -> {
+//			StreamingAccountData streamingAccount = new StreamingAccountData();
+//			streamingAccount.setAccountName(accountList.get(0).getTitle());
+//			streamingAccount.setGoodsId(goodsId);
+//			streamingAccountDataStatistics(goodsId, streamingAccount, streamingAccountDataSave, yesZeroDate, thisZeroDate);
+//		});
+//		if (today) {
+//			return streamingAccountDataSave;
+//		}
+//		for (StreamingAccountData streamingAccountData : streamingAccountDataSave) {
+//			streamingAccountData.setStatisticsDate(yesZeroDate);
+//			streamingAccountDataMapper.insert(streamingAccountData);
+//		}
+//		return null;
+//	}
+//
+//	/**
+//	 * 统计实物数据
+//	 */
+//	public List<RealGoodsData> statisticsRealGoodsData(Date yesZeroDate, Date thisZeroDate, Boolean today) {
+//		List<GoodsDon> goodsDons = goodsDonMapper.selectList(Wrappers.lambdaQuery(GoodsDon.class).eq(GoodsDon::getType, Constant.realGoodsType.get(0)));
+//		List<RealGoodsData> realGoodsDataList = new ArrayList<>();
+//		if (!goodsDons.isEmpty()) {
+//			goodsDons.forEach(goods -> {
+//				List<GoodsDonSku> skuList = goodsDonSkuService.list(Wrappers.lambdaQuery(GoodsDonSku.class).eq(GoodsDonSku::getGoodsId, goods.getId()));
+//				if (!skuList.isEmpty()) {
+//					skuList.forEach(sku -> {
+//						RealGoodsData realGoodsData = new RealGoodsData();
+//						realGoodsData.setGoodsId(goods.getId());
+//						realGoodsData.setGoodsTitle(goods.getTitle());
+//						realGoodsData.setSkuId(sku.getId());
+//						realGoodsData.setSpecVal(sku.getSpecVal());
+//						//订单
+//						List<OrderDon> orderDon = Optional.ofNullable(orderDonService.list(Wrappers.lambdaQuery(OrderDon.class).eq(OrderDon::getSkuId, sku.getId()).between(OrderDon::getCreatedTime, yesZeroDate, thisZeroDate).notIn(OrderDon::getStatus, Constant.noOrderStatus).select(OrderDon::getUserId, OrderDon::getMoney, OrderDon::getId))).orElse(new ArrayList<>());
+//						Set<Long> userList = Optional.ofNullable(orderDon.stream().map(OrderDon::getUserId).collect(Collectors.toSet())).orElse(new HashSet<>());
+//						//用户数
+//						realGoodsData.setNumOfUser(userList.size());
+//						//订单数
+//						realGoodsData.setNumOfOrder(orderDon.size());
+//						//金额
+//						Optional<BigDecimal> money = orderDon.stream().map(OrderDon::getMoney).reduce(BigDecimal::add);
+//						realGoodsData.setMoney(money.isPresent() ? money.get() : BigDecimal.ZERO);
+//						realGoodsDataList.add(realGoodsData);
+//					});
+//				}
+//			});
+//		}
+//		if (today) {
+//			return realGoodsDataList;
+//		}
+//		if (!realGoodsDataList.isEmpty()) {
+//			realGoodsDataList.forEach(data -> {
+//				data.setStatisticsDate(yesZeroDate);
+//				realGoodsDataMapper.insert(data);
+//			});
+//		}
+//		return null;
+//	}
+//
+//	/**
+//	 * @param goodsId              平台id
+//	 * @param streamingAccountData
+//	 * @param dataSave             入库实体集合
+//	 * @param yesZeroDate          昨日零时
+//	 * @param thisZeroDate         今日零时
+//	 */
+//	private void streamingAccountDataStatistics(Long goodsId, StreamingAccountData streamingAccountData, List<StreamingAccountData> dataSave, Date yesZeroDate, Date thisZeroDate) {
+//		//规格
+//		List<GoodsDonSku> list = Optional.ofNullable(goodsDonSkuService.list(Wrappers.lambdaQuery(GoodsDonSku.class).eq(GoodsDonSku::getGoodsId, goodsId).select(GoodsDonSku::getId))).orElse(new ArrayList<>());
+//		List<Long> skuIds = list.stream().map(GoodsDonSku::getId).collect(Collectors.toList());
+//		if (CollUtil.isNotEmpty(skuIds)) {
+//			List<OrderDon> orderDons = Optional.ofNullable(accountService.getNoAppleOneOrderDon(skuIds, yesZeroDate, thisZeroDate, Constant.noOrderStatus)).orElse(new ArrayList<>());
+//			//用户数
+//			streamingAccountData.setNumOfUser(orderDons.stream().map(OrderDon::getUserId).collect(Collectors.toSet()).size());
+//			getNumOfOrderByPayType(orderDons, streamingAccountData);
+//			//兑换码订单数
+//			List<OrderDon> exCodeOrders = Optional.ofNullable(orderDons.stream().filter(order -> order.getType() == OrderDon.Type.EX_CODE).collect(Collectors.toList())).orElse(new ArrayList<>());
+//			streamingAccountData.setNumOfCodeOrder(exCodeOrders.size());
+//			streamingAccountData.setNumOfOrder(orderDons.size());
+//			Optional<BigDecimal> reduce = orderDons.stream().map(OrderDon::getMoney).reduce(BigDecimal::add);
+//			BigDecimal totalMoney = BigDecimal.valueOf(0);
+//			if (reduce.isPresent()) {
+//				totalMoney = reduce.get();
+//			}
+//			//金额
+//			streamingAccountData.setMoney(totalMoney);
+//			//分销订单
+//			List<OrderDon> distributeOrders = Optional.ofNullable(orderDons.stream().filter(ord -> ord.getIsDistribute()).collect(Collectors.toList())).orElse(new ArrayList<>());
+//			streamingAccountData.setNumOfDistributeOrder(distributeOrders.size());
+//			Optional<BigDecimal> distributeMoney = distributeOrders.stream().map(OrderDon::getMoney).reduce(BigDecimal::add);
+//			streamingAccountData.setDistributeOrderMoney(distributeMoney.isPresent() ? distributeMoney.get() : BigDecimal.ZERO);
+//		}
+//		dataSave.add(streamingAccountData);
+//	}
+//
+//	/**
+//	 * 根据不同支付条件统计对应月、季、年订单
+//	 */
+//	private void getNumOfOrderByPayType(List<OrderDon> orderDons, AbstractBackData backData) {
+//		Integer numOfMonthOrder = 0;
+//		Integer numOfSeasonOrder = 0;
+//		Integer numOfYearOrder = 0;
+//		Map<Long, List<OrderDon>> skuIdMaps = Optional.ofNullable(orderDons.stream().collect(Collectors.groupingBy(OrderDon::getSkuId))).orElse(new HashMap<>());
+//		if (!skuIdMaps.isEmpty()) {
+//			List<GoodsDonSku> goodsDonSkus = Optional.ofNullable(goodsDonSkuService.list(Wrappers.lambdaQuery(GoodsDonSku.class).in(GoodsDonSku::getId, skuIdMaps.keySet()))).orElse(new ArrayList<>());
+//			for (GoodsDonSku donSkus : goodsDonSkus) {
+//				int size = skuIdMaps.get(donSkus.getId()).size();
+//				if (donSkus.getSpecVal().contains(Constant.payType.get(0)) && !donSkus.getSpecVal().contains(Constant.payType.get(2))) {
+//					numOfMonthOrder += size;
+//				} else if (donSkus.getSpecVal().contains(Constant.payType.get(1))) {
+//					numOfSeasonOrder += size;
+//				} else if (donSkus.getSpecVal().contains(Constant.payType.get(2))) {
+//					numOfYearOrder += size;
+//				}
+//			}
+//		}
+//		//月付
+//		backData.setNumOfMonthOrder(numOfMonthOrder);
+//		//季付
+//		backData.setNumOfSeasonOrder(numOfSeasonOrder);
+//		//年付
+//		backData.setNumOfYearOrder(numOfYearOrder);
+//	}
+//
+//	/**
+//	 * 获取Apple区域map
+//	 */
+//	public Map<String, List<GoodsDonSku>> getAppleOneMap() {
+//		GoodsDon goodsDon = goodsDonMapper.selectOne(Wrappers.lambdaQuery(GoodsDon.class).eq(GoodsDon::getId, 2).last("limit 1"));
+//		List<GoodsDonSku> list = goodsDonSkuService.list(Wrappers.lambdaQuery(GoodsDonSku.class).eq(GoodsDonSku::getGoodsId, goodsDon.getId()).select(GoodsDonSku::getId, GoodsDonSku::getSpecVal));
+//		Set<String> area = new HashSet<>();
+//		list.forEach(sku -> {
+//			String areaName = sku.getSpecVal().substring(0, sku.getSpecVal().indexOf("区") + 1);
+//			if (!area.contains(areaName)) {
+//				area.add(areaName);
+//			}
+//		});
+//		Map<String, List<GoodsDonSku>> areaDataMap = new HashMap<>();
+//		for (String name : area) {
+//			List<GoodsDonSku> sku = Optional.ofNullable(goodsDonSkuService.list(Wrappers.lambdaQuery(GoodsDonSku.class).like(GoodsDonSku::getSpecVal, name).select(GoodsDonSku::getId, GoodsDonSku::getGoodsId, GoodsDonSku::getSpecVal))).orElse(new ArrayList<>());
+//			areaDataMap.put(name, sku);
+//		}
+//		return areaDataMap;
+//	}
+//}

+ 2 - 1
netflix-web/src/main/java/com/cyksj/web/controller/manage/CmsUserController.java

@@ -168,7 +168,8 @@ public class CmsUserController{
         if (user == null || StrUtil.isEmpty(user.getUnionid())) {
             return GatewayResponse.SUCCESS.newBuilder().toResult();
         }
-        String corpCustomers = cmsUserFollowRelationMapper.selectCorpCustomers(user.getUnionid());
+        Long wxCorpId = 2l;
+        String corpCustomers = cmsUserFollowRelationMapper.selectCorpCustomers(user.getUnionid(), wxCorpId);
         return GatewayResponse.SUCCESS.newBuilder().toResult(corpCustomers);
     }
 

+ 169 - 0
netflix-web/src/main/java/com/cyksj/web/controller/manage/corp/CorpMsgBulkTaskController.java

@@ -0,0 +1,169 @@
+package com.cyksj.web.controller.manage.corp;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.toolkit.StringPool;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.util.Jsons;
+import com.cyksj.dto.Result;
+import com.cyksj.enums.GatewayResponse;
+import com.cyksj.mapper.corp.CorpTagMapper;
+import com.cyksj.mapper.manage.coupon.CouponMapper;
+import com.cyksj.model.dto.CornMetaDto;
+import com.cyksj.model.entity.CorpMsgBulkTask;
+import com.cyksj.model.entity.CorpTag;
+import com.cyksj.model.request.corp.Attachments;
+import com.cyksj.model.views.CorpMsgBulkTaskSendRecordView;
+import com.cyksj.service.corp.CorpMsgBulkTaskService;
+import com.cyksj.xxljob.XxlJobUtil;
+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.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/*
+ *项目名: netflix
+ *文件名: CorpMsgBulkTaskController
+ *创建者: JavaZou
+ *创建时间:2023/6/21 9:31
+ */
+@RestController
+@RequestMapping("/manage/corpMsgBulkTask")
+@RequiredArgsConstructor
+public class CorpMsgBulkTaskController {
+
+	private final CorpMsgBulkTaskService corpMsgBulkTaskService;
+
+	private final BeanSearcher beanSearcher;
+
+	private final CouponMapper couponMapper;
+
+	private final HttpServletRequest request;
+
+	private final CorpTagMapper corpTagMapper;
+
+
+	/**
+	 * 新建群发
+	 */
+	@PostMapping("/post")
+	public Result<String> saveCorpMsgBulkTask(@RequestBody @Validated CorpMsgBulkTask corpMsgBulkTask) throws Exception {
+		corpMsgBulkTaskService.saveOrUpdateMsgBulkTask(corpMsgBulkTask);
+		return GatewayResponse.SUCCESS.newBuilder().toResult("新建群发成功");
+	}
+
+
+	/**
+	 * 编辑群发
+	 */
+	@PutMapping("/put")
+	public Result<String> updateCorpMsgBulkTask(@RequestBody @Validated CorpMsgBulkTask corpMsgBulkTask) throws Exception {
+		corpMsgBulkTaskService.saveOrUpdateMsgBulkTask(corpMsgBulkTask);
+		return GatewayResponse.SUCCESS.newBuilder().toResult("修改群发成功");
+	}
+
+	/**
+	 * 群发列表
+	 */
+	@GetMapping("/get")
+	public Result<SearchResult<CorpMsgBulkTask>> getCorpMsgBulkTaskList(String[] tagIds) {
+		MapBuilder builder = MapUtils.flatBuilder(request.getParameterMap());
+		if (tagIds != null && tagIds.length > 0) {
+			List<String> collect = corpTagMapper.selectList(Wrappers.lambdaQuery(CorpTag.class)
+					.in(CorpTag::getId, tagIds)).stream().map(CorpTag::getTagId).collect(Collectors.toList());
+			if (CollUtil.isNotEmpty(collect)) {
+				StringBuilder sb = new StringBuilder();
+				for (String tagId : collect) {
+					sb.append("\"" + tagId + "\"");
+					sb.append(",");
+					String substring = sb.substring(0, sb.lastIndexOf(","));
+					builder.put("tag_id", String.format("id in (select distinct task_id from corp_msg_bulk_task_tag_relation where tag_id in(%s))", substring));
+				}
+			}
+		}
+		SearchResult<CorpMsgBulkTask> search = beanSearcher.search(CorpMsgBulkTask.class, builder
+				.field(CorpMsgBulkTask::getDeleted, true)
+				.build());
+		search.getDataList().forEach(data -> {
+			try {
+				List<String> tagList = Jsons.parseList(data.getChooseTagStr(), String.class);
+				data.setTagSize(tagList.size());
+				if (StringUtils.isNotBlank(data.getCorn())) {
+					String[] s = data.getCorn().split(" ");
+					CornMetaDto cornMetaDto = new CornMetaDto();
+					cornMetaDto.setHour(s[2]);
+					cornMetaDto.setDay(StringPool.ASTERISK.equals(s[3]) ? "" : s[3]);
+					cornMetaDto.setMonth(StringPool.ASTERISK.equals(s[4]) ? "" : s[4]);
+					cornMetaDto.setWeek(StringPool.QUESTION_MARK.equals(s[5]) ? "" : s[5]);
+					data.setCornDto(cornMetaDto);
+				}
+				if (StrUtil.isNotBlank(data.getAttachmentsStr())) {
+					data.setAttachments(Jsons.parseList(data.getAttachmentsStr(), Attachments.class));
+				}
+				if (data.getCouponId() != null) {
+					data.setCouponName(couponMapper.getCouponNameById(data.getCouponId()));
+				}
+			} catch (Exception e) {
+
+			}
+		});
+		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
+	}
+
+	/**
+	 * 标签详情
+	 */
+	@GetMapping("/get/tag/detail/{id}")
+	public Result<Map<String, List<CorpTag>>> getTagDetail(@PathVariable Long id) throws Exception {
+		Map<String, List<CorpTag>> collect = corpMsgBulkTaskService.getTagDetail(id);
+		return GatewayResponse.SUCCESS.newBuilder().toResult(collect);
+	}
+
+	/**
+	 * 删除群发
+	 */
+	@DeleteMapping("/delete/{id}")
+	public Result<String> deleteBulkTaskList(@PathVariable Long id) throws Exception {
+		CorpMsgBulkTask task = corpMsgBulkTaskService.getById(id);
+		if (task == null || !task.getDeleted()) {
+			throw BusinessRuntimeException.getInstance("群发任务不存在");
+		}
+		task.setDeleted(!task.getDeleted());
+		corpMsgBulkTaskService.updateById(task);
+
+		if (task.getJobId() != null) {
+			XxlJobUtil.deleteJob(task.getJobId());
+		}
+		return GatewayResponse.SUCCESS.newBuilder().toResult();
+	}
+
+	/**
+	 * 群发记录
+	 */
+	@GetMapping("/get/sendRecord/{id}")
+	public Result<SearchResult<CorpMsgBulkTaskSendRecordView>> getSendRecord(@PathVariable Long id) {
+		MapBuilder builder = MapUtils.flatBuilder(request.getParameterMap());
+		builder.put("task_id", String.format("task_id = %s", id));
+		SearchResult<CorpMsgBulkTaskSendRecordView> search = beanSearcher.search(CorpMsgBulkTaskSendRecordView.class, builder.build());
+		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
+	}
+
+	/**
+	 * 提醒群发
+	 */
+	@PutMapping("/remind/{id}")
+	public Result<String> remindBulkTask(@PathVariable Long id) {
+		corpMsgBulkTaskService.remindBulkTask(id);
+		return GatewayResponse.SUCCESS.newBuilder().toResult("提醒群发成功");
+	}
+}

+ 164 - 0
netflix-web/src/main/java/com/cyksj/web/controller/manage/corp/CorpTagController.java

@@ -0,0 +1,164 @@
+package com.cyksj.web.controller.manage.corp;
+
+import com.alibaba.excel.support.ExcelTypeEnum;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.cyksj.dto.Result;
+import com.cyksj.enums.GatewayResponse;
+import com.cyksj.model.entity.CorpTag;
+import com.cyksj.model.entity.CorpTagRule;
+import com.cyksj.model.entity.CorpTagRuleRelation;
+import com.cyksj.model.excel.CorpUserTagExcelData;
+import com.cyksj.model.request.CorpDelTagReq;
+import com.cyksj.model.views.CorpTagBackView;
+import com.cyksj.model.views.CorpTagGroupView;
+import com.cyksj.model.views.CorpTagHeadDataView;
+import com.cyksj.model.views.CorpUserView;
+import com.cyksj.service.corp.CorpTagService;
+import com.cyksj.web.util.EasyExcelUtils;
+import com.ejlchina.searcher.BeanSearcher;
+import com.ejlchina.searcher.util.MapUtils;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/*
+ *项目名: netflix
+ *文件名: CorpTagController
+ *创建者: JavaZou
+ *创建时间:2023/6/20 16:01
+ */
+@RestController
+@RequestMapping("/manage/corp")
+@RequiredArgsConstructor
+public class CorpTagController {
+
+	private final CorpTagService corpTagService;
+
+	private final BeanSearcher beanSearcher;
+
+	private final HttpServletRequest request;
+
+	/**
+	 * 添加企业标签组及标签
+	 */
+	@PostMapping("/addCorpTagGroup")
+	public Result<CorpTagGroupView> addCorpTagGroup(@RequestBody CorpTagGroupView tagGroup) throws Exception {
+		CorpTagGroupView corpTagGroupView = corpTagService.addCorpTagGroup(tagGroup);
+		return GatewayResponse.SUCCESS.newBuilder().toResult(corpTagGroupView);
+	}
+
+	/**
+	 * 编辑企业标签
+	 */
+	@PutMapping("/editCorpTag")
+	public Result<String> editCorpTag(@RequestBody CorpTagGroupView tagGroup) throws Exception {
+		corpTagService.editCorpTag(tagGroup);
+
+		return GatewayResponse.SUCCESS.newBuilder().toResult();
+	}
+
+	/**
+	 * 删除企业标签组、标签
+	 */
+	@PutMapping("/del")
+	public Result<String> delTags(@RequestBody CorpDelTagReq req) throws Exception {
+		corpTagService.delTags(req);
+		return GatewayResponse.SUCCESS.newBuilder().toResult();
+	}
+
+	/**
+	 * 获取企业微信标签列表
+	 *
+	 * @author chan
+	 * @date 2022-03-24 下午1:58
+	 */
+	@RequestMapping("/get/corpTagList")
+	public Result<List<CorpTagGroupView>> getCorpTagList(){
+		List<CorpTagGroupView> tagGroupList = corpTagService.getCorpTagList();
+		return GatewayResponse.SUCCESS.newBuilder().toResult(tagGroupList);
+	}
+
+	/**
+	 * 规则列表展示
+	 */
+	@GetMapping("/get/ruleList")
+	public Result<List<CorpTagRule>> rules() {
+		List<CorpTagRule> corpTagRules = beanSearcher.searchAll(CorpTagRule.class, MapUtils.flatBuilder(request.getParameterMap()).build());
+		return GatewayResponse.SUCCESS.newBuilder().toResult(corpTagRules);
+	}
+
+	/**
+	 * 新增规则
+	 */
+	@PostMapping("/post/rule")
+	public Result<String> postRule(CorpTagRule rule) {
+		corpTagService.saveOrUpdateRule(rule);
+		return GatewayResponse.SUCCESS.newBuilder().toResult();
+	}
+
+	/**
+	 * 编辑规则
+	 */
+	@PutMapping("/put/rule")
+	public Result<String> updateRule(CorpTagRule rule) {
+		corpTagService.saveOrUpdateRule(rule);
+		return GatewayResponse.SUCCESS.newBuilder().toResult();
+	}
+
+	/**
+	 * 标签管理详情
+	 */
+	@GetMapping("/get/corpTag")
+	public Result<List<CorpTagBackView>> getCorpTagBackView(@RequestParam(required = false, defaultValue = "1") Integer type, String[] tagIds, String[] groupIds, String sort, String order) {
+		List<CorpTagBackView> corpTagBackViews = corpTagService.getCorpTagBackView(type, tagIds, groupIds, sort, order);
+		return GatewayResponse.SUCCESS.newBuilder().toResult(corpTagBackViews);
+	}
+
+	/**
+	 * 标签对应规则详情
+	 */
+	@GetMapping("/get/corpTag/rule/detail/{id}")
+	public Result<List<CorpTagRuleRelation>> getCorpTagRuleDetail(@PathVariable Long id) {
+		return GatewayResponse.SUCCESS.newBuilder().toResult(corpTagService.getCorpTagRuleDetail(id));
+	}
+
+	/**
+	 * 客户详情
+	 */
+	@GetMapping("/get/corpUser/detail")
+	public Result<Page<CorpUserView>> getCorpUserDetail(String tagId, String groupId, @RequestParam(defaultValue = "1") Integer start, @RequestParam(defaultValue = "10") Integer limit) {
+		Page<CorpUserView> result = corpTagService.getCorpUserDetail(tagId, groupId, start, limit);
+		return GatewayResponse.SUCCESS.newBuilder().toResult(result);
+	}
+
+	/**
+	 * 标签组内标签详情
+	 */
+	@GetMapping("/get/groupTag/detail/{groupId}")
+	public Result<List<CorpTag>> getGroupTagDetail(@PathVariable String groupId) {
+		List<CorpTag> corpTags = corpTagService.getGroupTagDetail(groupId);
+		return GatewayResponse.SUCCESS.newBuilder().toResult(corpTags);
+	}
+
+	/**
+	 * 导出标签下所有客户
+	 */
+	@GetMapping("/export")
+	public void export(String[] tagIds, String[] groupIds, HttpServletResponse response) {
+		List<CorpUserTagExcelData> list = corpTagService.exportUserTagDetail(tagIds, groupIds);
+		EasyExcelUtils.createExcelStreamMutilByEasyExcel(response, CorpUserTagExcelData.class, list, "标签下客户详情", "标签下客户详情", ExcelTypeEnum.XLSX, null);
+	}
+
+	/**
+	 * 企业微信标签 头部数据
+	 */
+	@GetMapping("/get/headData")
+	public Result<CorpTagHeadDataView> getCorpTagHeadDataView() {
+		CorpTagHeadDataView corpTagHeadDataView = corpTagService.getCorpTagHeadDataView();
+		return GatewayResponse.SUCCESS.newBuilder().toResult(corpTagHeadDataView);
+	}
+
+}

File diff suppressed because it is too large
+ 0 - 0
netflix-web/src/main/resources/application-dev.yml


Some files were not shown because too many files changed in this diff