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.model.entity.*; import com.cyksj.model.manage.views.AccountView; import com.cyksj.model.manage.views.GroupsRelationView; 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.sms.SmsService; import com.cyksj.service.template.TemplateCommonService; import com.cyksj.service.wechat.WeChatService; 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 GroupsMapper groupsMapper; private final OrderDonService orderDonService; private final GroupsRelationMapper groupsRelationMapper; private final UserMapper userMapper; private final GoodsDonSkuMapper skuMapper; private final WeChatService weChatService; private final AccountMapper accountMapper; 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 TemplateCommonService templateCommonService; private final GroupRelationClearService groupRelationClearService; private final CmsAccountService cmsAccountService; private final EnvCommonService envCommonService; private final WorkOrderMapper workOrderMapper; private final SmsService smsService; private final CmsUserMapper cmsUserMapper; /** * 每5分钟清理过期账号 */ @Scheduled(cron = "0 0/5 * * * ?") @Transactional(rollbackFor = Throwable.class) public void clearExpiry() { DateTime now = new DateTime(); List expiryList = groupsRelationMapper.selectList(Wrappers.lambdaQuery(GroupsRelation.class).le(GroupsRelation::getExpiryTime, now)); log.info("定时清理过期账号start ===> 数量:{}", expiryList.size()); //过期账号清楚有效期 expiryList.forEach(relation -> groupRelationClearService.clearTicket(relation, UserTicketClearedRecord.Source.self)); } @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, 3); List 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 accounts = accountMapper.selectList(Wrappers.lambdaQuery(Account.class).le(Account::getExpiryTime, time)); // List 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); 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 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 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 waitingSendPoints = Optional.ofNullable(distributeWaitingSendPointsMapper.selectList(Wrappers.lambdaQuery(DistributeWaitingSendPoints.class) .eq(DistributeWaitingSendPoints::getSendStatus, DistributeWaitingSendPoints.Status.waiting) .lt(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("更新用户积分开始=====>"); while (true) { user = userMapper.selectById(sharedId); if (user == null) { break; } Integer update = userMapper.updatePoints(user.getId(), user.getPoints(), wait.getPoints()); if (update == 1) { distributeWaitingSendPointsMapper.updateById(wait); log.info("分销积分表订单id:{}发放积分success", wait.getOrderId()); break; } } } }); } /** * 定时更新未支付AppleID自动注册订单 */ @Scheduled(cron = "0 0/5 * * * ?") public void updateAppleAutoStatus() { //获取为支付订单 DateTime now = new DateTime(); Date newDate = DateUtil.offset(now, DateField.MINUTE, -5); List 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> 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 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()); List accountViews = beanSearcher.searchAll(AccountView.class, MapUtils.builder() .field(AccountView::getExpiredAll, 1) .field(AccountView::getCustomerServiceId, 0) .put("yesTime", String.format("and ur.created_time < '%s'", beginOfDay)) .build()); if (accountViews.size() > 0) { int accountSize = accountViews.size(); log.info("开始给客服分配过期账号,过期账号数量:{}", accountSize); List 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 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 collect = workOrders.stream().map(WorkOrder::getId).collect(Collectors.toList()); List> 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 = 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 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; } }); } }