Scheduler.java 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. package com.cyksj.task;
  2. import cn.hutool.core.collection.CollUtil;
  3. import cn.hutool.core.date.BetweenFormater;
  4. import cn.hutool.core.date.DateField;
  5. import cn.hutool.core.date.DateTime;
  6. import cn.hutool.core.date.DateUtil;
  7. import cn.hutool.core.util.StrUtil;
  8. import com.baomidou.mybatisplus.core.toolkit.Wrappers;
  9. import com.cyksj.common.EnvCommonService;
  10. import com.cyksj.common.constant.Constant;
  11. import com.cyksj.common.util.SmsUtil;
  12. import com.cyksj.common.util.StringUtil;
  13. import com.cyksj.dto.RedisKey;
  14. import com.cyksj.mapper.*;
  15. import com.cyksj.mapper.manage.cms.CmsUserMapper;
  16. import com.cyksj.mapper.manage.distribute.DistributeWaitingSendPointsMapper;
  17. import com.cyksj.mapper.manage.sys.WorkOrderMapper;
  18. import com.cyksj.model.entity.*;
  19. import com.cyksj.model.manage.views.AccountView;
  20. import com.cyksj.model.manage.views.GroupsRelationView;
  21. import com.cyksj.model.views.CmsUserVO;
  22. import com.cyksj.model.views.UserDistributeViews;
  23. import com.cyksj.redis.RedisService;
  24. import com.cyksj.service.mange.CmsAccountService;
  25. import com.cyksj.service.order.OrderDonService;
  26. import com.cyksj.service.register.RegisterOrderDonService;
  27. import com.cyksj.service.register.SpotifyService;
  28. import com.cyksj.service.relation.GroupRelationClearService;
  29. import com.cyksj.service.sms.SmsService;
  30. import com.cyksj.service.template.TemplateCommonService;
  31. import com.cyksj.service.wechat.WeChatService;
  32. import com.ejlchina.searcher.BeanSearcher;
  33. import com.ejlchina.searcher.param.Operator;
  34. import com.ejlchina.searcher.util.MapUtils;
  35. import com.google.common.collect.Lists;
  36. import lombok.RequiredArgsConstructor;
  37. import lombok.extern.slf4j.Slf4j;
  38. import org.springframework.dao.DuplicateKeyException;
  39. import org.springframework.scheduling.annotation.Scheduled;
  40. import org.springframework.stereotype.Component;
  41. import org.springframework.transaction.annotation.Transactional;
  42. import java.math.BigDecimal;
  43. import java.math.RoundingMode;
  44. import java.util.*;
  45. import java.util.concurrent.ConcurrentHashMap;
  46. import java.util.stream.Collectors;
  47. @Component
  48. @Slf4j
  49. @RequiredArgsConstructor
  50. public class Scheduler {
  51. private final GoodsDonMapper donMapper;
  52. private final GroupsMapper groupsMapper;
  53. private final OrderDonService orderDonService;
  54. private final GroupsRelationMapper groupsRelationMapper;
  55. private final UserMapper userMapper;
  56. private final GoodsDonSkuMapper skuMapper;
  57. private final WeChatService weChatService;
  58. private final AccountMapper accountMapper;
  59. private final BeanSearcher beanSearcher;
  60. private final SpotifyService spotifyService;
  61. private final RegisterOrderDonMapper registerOrderDonMapper;
  62. private final RedisService redisService;
  63. private final SysConfigMapper sysConfigMapper;
  64. private final DistributeWaitingSendPointsMapper distributeWaitingSendPointsMapper;
  65. private final RegisterOrderDonService registerOrderDonService;
  66. private final AppleAutoRegisterDataMapper appleAutoRegisterDataMapper;
  67. private final RealGoodsInterestUserMapper realGoodsInterestUserMapper;
  68. private final TemplateCommonService templateCommonService;
  69. private final GroupRelationClearService groupRelationClearService;
  70. private final CmsAccountService cmsAccountService;
  71. private final EnvCommonService envCommonService;
  72. private final WorkOrderMapper workOrderMapper;
  73. private final SmsService smsService;
  74. private final CmsUserMapper cmsUserMapper;
  75. /**
  76. * 每5分钟清理过期账号
  77. */
  78. @Scheduled(cron = "0 0/5 * * * ?")
  79. @Transactional(rollbackFor = Throwable.class)
  80. public void clearExpiry() {
  81. DateTime now = new DateTime();
  82. List<GroupsRelation> expiryList = groupsRelationMapper.selectList(Wrappers.lambdaQuery(GroupsRelation.class).le(GroupsRelation::getExpiryTime, now));
  83. log.info("定时清理过期账号start ===> 数量:{}", expiryList.size());
  84. //过期账号清楚有效期
  85. expiryList.forEach(relation -> groupRelationClearService.clearTicket(relation, UserTicketClearedRecord.Source.self));
  86. }
  87. @Scheduled(cron = "0 0 9 * * ?")
  88. public void expiryWxMsg() throws Exception {
  89. if (envCommonService.isPrdEnv()) {
  90. DateTime now = new DateTime();
  91. DateTime zero = DateUtil.offset(now, DateField.HOUR, -9);
  92. DateTime time = DateUtil.offset(zero, DateField.DAY_OF_MONTH, 3);
  93. List<GroupsRelationView> groupsRelations = beanSearcher.searchAll(GroupsRelationView.class, MapUtils.builder().field(GroupsRelationView::getExpiryTime, time).op(Operator.LessEqual).build());
  94. //String url = String.format("%sauth/weChat?url=%syinhe/web/ticket", envCommonService.getHost(), envCommonService.getDomain());
  95. for (GroupsRelationView relation : groupsRelations) {
  96. // User user = userMapper.selectById(relation.getUserId());
  97. // if (user != null && StrUtil.isNotBlank(user.getOpenId())) {
  98. // String openId = user.getOpenId();
  99. // WxAppTemplateView wxAppTemplateView = templateCommonService.getWxAppByUserId(openId, TemplateEnum.EXPIRY_TEMPLATE.getDesc());
  100. // if (wxAppTemplateView != null) {
  101. // WxMpTemplateMessage templateMessage = new WxMpTemplateMessage()
  102. // .setToUser(openId)
  103. // .setTemplateId(wxAppTemplateView.getTemplateId())
  104. // .setUrl(url);
  105. //
  106. // /**
  107. // * 您的账号即将到期
  108. // * 账号名称:Netflix账号
  109. // * 平台:Netflix
  110. // * 失效日期:3天后
  111. // * 异常原因:账号即将到期
  112. // * 请尽快续费,以免影响使用
  113. // */
  114. // GroupsTrips groupsTrips = groupsMapper.selectById(relation.getGroupsId());
  115. // GoodsDonSku donSku = skuMapper.selectById(groupsTrips.getSkuId());
  116. // GoodsDon goodsDon = donMapper.selectById(donSku.getGoodsId());
  117. // WxMpTemplateMessage.TemplateData data = templateMessage.getData();
  118. // data.setFirst(new WxMpTemplateData("您的账号即将到期"));
  119. // data.setKeyword1(new WxMpTemplateData(goodsDon.getTitle()));
  120. // data.setKeyword2(new WxMpTemplateData(DateUtil.format(relation.getExpiryTime(), "yyyy-MM-dd HH:mm:ss")));
  121. // data.setRemark(new WxMpTemplateData("请尽快续费,以免影响使用"));
  122. // log.info("发送账号即将到期消息至用户:{}", user.getId());
  123. // weChatService.sendTemplateMessage(weChatService.getAccessToken(wxAppTemplateView.getAppId()), Jsons.toJson(templateMessage));
  124. // }
  125. // List<Account> accounts = accountMapper.selectList(Wrappers.lambdaQuery(Account.class).le(Account::getExpiryTime, time));
  126. // List<CustomerService> customerServices = customerServiceMapper.selectList(null);
  127. // if(!accounts.isEmpty()){
  128. // for (CustomerService customerService : customerServices) {
  129. // openId = customerService.getOpenId();
  130. // WxMpTemplateMessage manageMessage = new WxMpTemplateMessage()
  131. // .setToUser(openId)
  132. // .setTemplateId(wxAppTemplateView.getTemplateId())
  133. // .setUrl(" ");
  134. // WxMpTemplateMessage.TemplateData data2 = manageMessage.getData();
  135. // data2.setFirst(new WxMpTemplateData("您有" + accounts.size() + "个主账号即将到期"));
  136. // data2.setKeyword1(new WxMpTemplateData("车队主账号"));
  137. // data2.setKeyword2(new WxMpTemplateData("车队主账号"));
  138. // data2.setKeyword3(new WxMpTemplateData("5天后"));
  139. // data2.setKeyword4(new WxMpTemplateData("账号即将到期"));
  140. // data2.setRemark(new WxMpTemplateData("请尽快登录后台-在账号管理中查看"));
  141. // log.info("发送账号即将到期消息至客服:{}", customerService.getNickName());
  142. // weChatService.sendTemplateMessage(weChatService.getAccessToken(),Jsons.toJson(manageMessage));
  143. // }
  144. // }
  145. // }
  146. //短信通知过期账号 用户
  147. try {
  148. String day = DateUtil.formatBetween(zero, relation.getExpiryTime(), BetweenFormater.Level.DAY);
  149. smsService.sendSmsToRelationUser(relation.getUserId(), String.format(Constant.ACCOUNT_EXPIRY_MSG_TEMPLATE, relation.getTitle(), day));
  150. } catch (Exception e) {
  151. log.error("过期账号用户:{}发送短信错误:{}", relation.getUserId(), StringUtil.getErrorText(e));
  152. }
  153. }
  154. }
  155. }
  156. /**
  157. * 定时关闭订单
  158. */
  159. @Scheduled(cron = "0 0/5 * * * ?")
  160. public void closeOrder() {
  161. //获取为支付订单
  162. DateTime now = new DateTime();
  163. Date newDate = DateUtil.offset(now, DateField.MINUTE, -5);
  164. List<OrderDon> list = orderDonService.list(Wrappers.lambdaQuery(OrderDon.class).eq(OrderDon::getStatus, OrderDon.Status.noPayment).le(OrderDon::getCreatedTime,newDate));
  165. log.info("定时任务 关闭 订单数:{}",list.size());
  166. list.forEach((orderDon)->{
  167. orderDonService.closeOrder(orderDon.getId());
  168. });
  169. }
  170. /**
  171. * spotify 注册
  172. */
  173. @Scheduled(cron = "0 0/15 * * * ?")
  174. public void spotify() {
  175. List<RegisterOrderDon> spotify = registerOrderDonMapper.selectList(Wrappers.lambdaQuery(RegisterOrderDon.class)
  176. .select(RegisterOrderDon::getId)
  177. .eq(RegisterOrderDon::getPlatform, "spotify")
  178. .eq(RegisterOrderDon::getStatus, RegisterOrderDon.Status.hasPayment));
  179. for (RegisterOrderDon orderDon : spotify) {
  180. spotifyService.register(orderDon.getId());
  181. }
  182. }
  183. /**
  184. * 每日免单重置
  185. */
  186. @Scheduled(cron = "0 0 0 * * ?")
  187. public void freeReset() {
  188. log.info("每日重置 spotify 免单账户数 [start]");
  189. SysConfig register_free_num = sysConfigMapper.selectOne(Wrappers.lambdaQuery(SysConfig.class).eq(SysConfig::getSysKey, "register_free_num"));
  190. redisService.set(RedisKey.SPOTIFY_FREE_NUM, Long.parseLong(register_free_num.getSysValue()));
  191. log.info("每日重置 spotify 免单账户数 [end] 数量:{}",register_free_num.getSysValue());
  192. }
  193. /**
  194. * 分销订单积分奖励分发
  195. */
  196. @Scheduled(cron = "0 0 0/2 * * ?")
  197. @Transactional(rollbackFor = Throwable.class)
  198. public void distributeOrdersSendPoints() {
  199. List<DistributeWaitingSendPoints> waitingSendPoints = Optional.ofNullable(distributeWaitingSendPointsMapper.selectList(Wrappers.lambdaQuery(DistributeWaitingSendPoints.class)
  200. .eq(DistributeWaitingSendPoints::getSendStatus, DistributeWaitingSendPoints.Status.waiting)
  201. .lt(DistributeWaitingSendPoints::getSendTime, DateTime.now())
  202. .select(DistributeWaitingSendPoints::getId,
  203. DistributeWaitingSendPoints::getOrderId,
  204. DistributeWaitingSendPoints::getPoints,
  205. DistributeWaitingSendPoints::getUserId,
  206. DistributeWaitingSendPoints::getSharedId,
  207. DistributeWaitingSendPoints::getSendTime))).orElse(new ArrayList<>());
  208. log.info("查询分销订单积分分发记录开始,条数:{}=======》", waitingSendPoints.size());
  209. waitingSendPoints.forEach(wait -> {
  210. //积分待发人
  211. Long sharedId = wait.getSharedId();
  212. User user = userMapper.selectById(sharedId);
  213. if (user == null) {
  214. wait.setSendStatus(DistributeWaitingSendPoints.Status.not_exist);
  215. log.info("分销订单,发送积分用户不存在");
  216. distributeWaitingSendPointsMapper.updateById(wait);
  217. } else {
  218. wait.setSendStatus(DistributeWaitingSendPoints.Status.success);
  219. log.info("更新用户积分开始=====>");
  220. while (true) {
  221. user = userMapper.selectById(sharedId);
  222. if (user == null) {
  223. break;
  224. }
  225. Integer update = userMapper.updatePoints(user.getId(), user.getPoints(), wait.getPoints());
  226. if (update == 1) {
  227. distributeWaitingSendPointsMapper.updateById(wait);
  228. log.info("分销积分表订单id:{}发放积分success", wait.getOrderId());
  229. break;
  230. }
  231. }
  232. }
  233. });
  234. }
  235. /**
  236. * 定时更新未支付AppleID自动注册订单
  237. */
  238. @Scheduled(cron = "0 0/5 * * * ?")
  239. public void updateAppleAutoStatus() {
  240. //获取为支付订单
  241. DateTime now = new DateTime();
  242. Date newDate = DateUtil.offset(now, DateField.MINUTE, -5);
  243. List<Long> orderDons = appleAutoRegisterDataMapper.getAllNoPayOrders(newDate);
  244. log.info("定时更新未支付AppleID自动注册订单 订单数:{}",orderDons.size());
  245. orderDons.forEach((orderId)->{
  246. registerOrderDonService.updateAppleAutoStatus(orderId);
  247. });
  248. }
  249. /**
  250. * 实物订单意愿数据
  251. */
  252. @Scheduled(cron = "0 0/5 * * * ?")
  253. public void realGoodsInterestUser() {
  254. Optional.ofNullable(orderDonService.selectNoPaymentRealGoods()).ifPresent(list -> {
  255. if (list.size() > 0) {
  256. Map<String, List<RealGoodsInterestUser>> collect = list.stream().collect(Collectors.groupingBy(RealGoodsInterestUser::getGroupBy));
  257. log.info("实物订单意愿数据同步开始");
  258. collect.forEach((k, data) -> {
  259. RealGoodsInterestUser realGoodsInterestUser = null;
  260. if (data.size() == 0) {
  261. realGoodsInterestUser = data.get(0);
  262. } else {
  263. List<RealGoodsInterestUser> high = data.stream().sorted(Comparator.comparing(RealGoodsInterestUser::getMoney).thenComparing(RealGoodsInterestUser::getOrderCreatedTime).reversed()).collect(Collectors.toList());
  264. realGoodsInterestUser = high.get(0);
  265. }
  266. try {
  267. realGoodsInterestUserMapper.insert(realGoodsInterestUser);
  268. } catch (DuplicateKeyException e) {
  269. log.info("重复插入用户相同实物商品记录");
  270. }
  271. });
  272. log.info("实物订单意愿数据同步结束");
  273. }
  274. }
  275. );
  276. Integer update = orderDonService.updateHasPaymentInterestRealGoods();
  277. orderDonService.updateHasPaymentInterestRealGoodsPayTime();
  278. if (update > 0) {
  279. log.info("更新用户记录:{}已购买实物意愿商品状态成功", update);
  280. }
  281. }
  282. /**
  283. * 统计昨日过期账号 分配给客服
  284. */
  285. @Scheduled(cron = "0 30 8 * * ?")
  286. public void assignNeedUpdateExpiredAccount() {
  287. DateTime beginOfDay = DateUtil.beginOfDay(DateTime.now());
  288. List<AccountView> accountViews = beanSearcher.searchAll(AccountView.class, MapUtils.builder()
  289. .field(AccountView::getExpiredAll, 1)
  290. .field(AccountView::getCustomerServiceId, 0)
  291. .put("yesTime", String.format("and ur.created_time < '%s'", beginOfDay))
  292. .build());
  293. if (accountViews.size() > 0) {
  294. int accountSize = accountViews.size();
  295. log.info("开始给客服分配过期账号,过期账号数量:{}", accountSize);
  296. List<CmsUserVO> customers = beanSearcher.searchAll(CmsUserVO.class, MapUtils.builder().field(CmsUserVO::getRoleNames, "客服").op(Operator.Contain).build());
  297. if (customers.isEmpty()) {
  298. log.info("暂无客服");
  299. return;
  300. }
  301. //分配过期账号
  302. cmsAccountService.assignCustomerAccount(customers, accountViews);
  303. }
  304. }
  305. /**
  306. * 2天前的工单未关闭 直接关闭
  307. */
  308. @Scheduled(cron = "0 10 0 * * ?")
  309. public void closeWorkOrderBeforeTowDays() {
  310. DateTime twoBefore = DateUtil.offsetDay(DateTime.now(), -2);
  311. List<WorkOrder> workOrders = beanSearcher.searchAll(WorkOrder.class, MapUtils.builder()
  312. .field(WorkOrder::getStatus, WorkOrder.Status.waiting.name())
  313. .field(WorkOrder::getCreatedTime, twoBefore).op(Operator.LessEqual)
  314. .onlySelect(WorkOrder::getId)
  315. .build());
  316. if (CollUtil.isNotEmpty(workOrders)) {
  317. List<Long> collect = workOrders.stream().map(WorkOrder::getId).collect(Collectors.toList());
  318. List<List<Long>> lists = Lists.partition(collect, 1000);
  319. log.info("关闭2天前未关闭的工单");
  320. lists.forEach(ids -> {
  321. workOrderMapper.update(null, Wrappers.lambdaUpdate(WorkOrder.class)
  322. .set(WorkOrder::getStatus, WorkOrder.Status.close)
  323. .in(WorkOrder::getId, ids));
  324. });
  325. }
  326. }
  327. /**
  328. * 推广者渠道昨天订单金额是否小于过去7天平均订单金额的一半
  329. * 若小于 则发送短信到对应商务
  330. */
  331. @Scheduled(cron = "0 30 8 * * ?")
  332. public void distributeOrderDetailAndSendMsgToBusiness() {
  333. Integer daysRule = 7;
  334. DateTime thisZero = DateUtil.beginOfDay(DateTime.now());
  335. DateTime yesZero = DateUtil.offsetDay(thisZero, -1);
  336. DateTime thePast7dZero = DateUtil.offsetDay(yesZero, -daysRule);
  337. DateTime twoYesZero = DateUtil.offsetDay(thisZero, -2);
  338. DateTime theTwoPast7dZero = DateUtil.offsetDay(twoYesZero, -daysRule);
  339. //总销量小于1000的 不算、建立天数低于7天的不算、 连续2天销量低于7天平均值的50%的 提醒
  340. List<UserDistributeViews> userDistributeViews = beanSearcher.searchAll(UserDistributeViews.class, MapUtils.builder()
  341. .field(UserDistributeViews::getCreatedTime, thePast7dZero).op(Operator.LessEqual)
  342. .field(UserDistribute::getBusinessId, 0).op(Operator.GreaterThan)
  343. .put("time", String.format("and od.created_time < '%s'", thisZero))
  344. .field(UserDistributeViews::getDistributeOrdersMoney, BigDecimal.valueOf(100000)).op(Operator.GreaterEqual)
  345. .build());
  346. String thePast7dTimeSql = String.format("and od.created_time >= '%s' and od.created_time < '%s'", thePast7dZero, yesZero);
  347. String yesTimeSql = String.format("and od.created_time >= '%s' and od.created_time < '%s'", yesZero, thisZero);
  348. String theTwoPast7dTimeSql = String.format("and od.created_time >= '%s' and od.created_time < '%s'", theTwoPast7dZero, twoYesZero);
  349. String twoYesTimeSql = String.format("and od.created_time >= '%s' and od.created_time < '%s'", twoYesZero, yesZero);
  350. Map<Long, String> phoneMap = new ConcurrentHashMap<>();
  351. userDistributeViews.forEach(data -> {
  352. //昨天过去7天总订单金额
  353. Number yesPastFtMoneySum = beanSearcher.searchSum(UserDistributeViews.class, MapUtils.builder()
  354. .field(UserDistributeViews::getUserId, data.getUserId())
  355. .put("time", thePast7dTimeSql).build(), "distributeOrdersMoney");
  356. //前天过去7天总订单金额
  357. Number towYesPastMoneySum = beanSearcher.searchSum(UserDistributeViews.class, MapUtils.builder()
  358. .field(UserDistributeViews::getUserId, data.getUserId())
  359. .put("time", theTwoPast7dTimeSql).build(), "distributeOrdersMoney");
  360. int theYes7dPastMoney = yesPastFtMoneySum.intValue();
  361. int theTwoYes7dPastMoney = towYesPastMoneySum.intValue();
  362. UserDistributeViews yesDistribute = beanSearcher.searchFirst(UserDistributeViews.class, MapUtils.builder()
  363. .field(UserDistributeViews::getUserId, data.getUserId())
  364. .put("time", yesTimeSql).build());
  365. UserDistributeViews twoYesDistribute = beanSearcher.searchFirst(UserDistributeViews.class, MapUtils.builder()
  366. .field(UserDistributeViews::getUserId, data.getUserId())
  367. .put("time", twoYesTimeSql).build());
  368. BigDecimal yesDistributeOrdersMoney = yesDistribute.getDistributeOrdersMoney();
  369. BigDecimal twoYesDistributeOrdersMoney = twoYesDistribute.getDistributeOrdersMoney();
  370. BigDecimal thePast7dAverage = BigDecimal.valueOf(theYes7dPastMoney).divide(BigDecimal.valueOf(daysRule).multiply(BigDecimal.valueOf(2)), 2, RoundingMode.HALF_DOWN);
  371. BigDecimal theTwoPast7dAverage = BigDecimal.valueOf(theTwoYes7dPastMoney).divide(BigDecimal.valueOf(daysRule).multiply(BigDecimal.valueOf(2)), 2, RoundingMode.HALF_DOWN);
  372. if (yesDistributeOrdersMoney.compareTo(thePast7dAverage) < 0 && twoYesDistributeOrdersMoney.compareTo(theTwoPast7dAverage) < 0) {
  373. User user = userMapper.selectById(data.getUserId());
  374. String msg = String.format(Constant.BUSINESS_CHANNEL_MSG, (user == null ? "用户" : user.getNickname()), data.getUserId());
  375. String phone = phoneMap.get(data.getUserId());
  376. if (StrUtil.isBlank(phone)) {
  377. CmsUser cmsUser = cmsUserMapper.selectById(data.getBusinessId());
  378. if (cmsUser != null) {
  379. phone = cmsUser.getPhone();
  380. phoneMap.putIfAbsent(data.getBusinessId(), phone);
  381. }
  382. }
  383. if (StrUtil.isNotBlank(phone)) {
  384. log.info("商务businessId:{}所下渠道用户id:{}分销数据已低于7天平均值的50%%", data.getBusinessId(), data.getUserId());
  385. //发短信
  386. SmsUtil.sendLuoKey2Msg(phone, msg);
  387. }
  388. return;
  389. }
  390. });
  391. }
  392. }