package com.cyksj.service.scheduler.impl; import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateUnit; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.cyksj.common.constant.Constant; import com.cyksj.common.util.StringUtil; import com.cyksj.dto.RedisKey; import com.cyksj.mapper.*; import com.cyksj.model.entity.*; import com.cyksj.model.manage.views.GroupsRelationView; import com.cyksj.model.request.ChangeRelationReq; import com.cyksj.redis.RedisService; import com.cyksj.service.error.UserRelationChangeErrorRecordService; import com.cyksj.service.groups.GroupsFuncService; import com.cyksj.service.mange.CmsOrderDonService; import com.cyksj.service.relation.GroupRelationClearService; import com.cyksj.service.scheduler.SchedulerService; import com.cyksj.service.user.UserBenefitsService; import com.ejlchina.searcher.BeanSearcher; import com.ejlchina.searcher.param.Operator; import com.ejlchina.searcher.util.MapUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.Date; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /* *项目名: netflix *文件名: SchedulerServiceImpl *创建者: JavaZou *创建时间:2023/6/8 16:48 */ @Service @RequiredArgsConstructor @Slf4j public class SchedulerServiceImpl implements SchedulerService { private final GoodsDonSkuMapper skuMapper; private final GroupsMapper groupsMapper; private final BeanSearcher beanSearcher; private final OrderDonMapper orderDonMapper; private final GroupsRelationMapper groupsRelationMapper; private final GroupRelationClearService groupRelationClearService; private final UserBenefitsService userBenefitsService; private final GroupsFuncService groupsFuncService; private final CmsOrderDonService cmsOrderDonService; private final UserRelationChangeErrorRecordService changeErrorRecordService; private final UserRelationChangeErrorRecordMapper userRelationChangeErrorRecordMapper; private final GoodsDonMapper goodsDonMapper; private final RedisService redisService; @Override public void transExpiryAccountValidRelation() { DateTime now = new DateTime(); DateTime nextBeginDay = DateUtil.offsetDay(DateUtil.beginOfDay(now), 1); //针对AI类 CHAT PLUS、MidJourney 月付 List aiSkuIds = skuMapper.selectAIMonthSkuIds(Constant.AI_goodsIds); List expiryAccountGroups = groupsMapper.getExpiryAccountGroups(nextBeginDay, aiSkuIds); expiryAccountGroups.forEach(expiry_groups -> { if (expiry_groups.getStatus() != GroupsTrips.Status.down) { expiry_groups.setStatus(GroupsTrips.Status.down); groupsMapper.updateById(expiry_groups); } Long g_groupsId = expiry_groups.getId(); Date a_expiryTime = expiry_groups.getExpiryTime(); //未过期用户 只分配有效的用户车票 outside状态过滤 List groupsRelations = beanSearcher.searchAll(GroupsRelationView.class, MapUtils.builder() .field(GroupsRelationView::getGroupsId, g_groupsId) .field(GroupsRelationView::getStatus, GroupsRelation.Status.validity.name()) .field(GroupsRelationView::getExpiryTime, nextBeginDay).op(Operator.GreaterEqual) .build()); reAssign(g_groupsId, now, groupsRelations); }); } @Override public void assignUserGroupsRelation(Long groupsId) { GroupsTrips groupsTrips = groupsMapper.selectById(groupsId); if (groupsTrips == null || groupsTrips.getAccountId() == null) return; String key = RedisKey.DISABLE_ACCOUNT_ASSIGN + groupsId; if (!redisService.setNx(key, groupsId, 60 * 3l)) { return; } if (groupsTrips.getStatus() != GroupsTrips.Status.down) { groupsTrips.setStatus(GroupsTrips.Status.down); groupsMapper.updateById(groupsTrips); } DateTime now = DateTime.now(); //迁移用户 List groupsRelations = beanSearcher.searchAll(GroupsRelationView.class, MapUtils.builder() .field(GroupsRelationView::getGroupsId, groupsId) .field(GroupsRelationView::getStatus, List.of(GroupsRelation.Status.validity.name(), GroupsRelation.Status.outside.name())).op(Operator.InList) .field(GroupsRelationView::getExpiryTime, now).op(Operator.GreaterEqual) .build()); reAssign(groupsId, now, groupsRelations); redisService.del(key); } /** * 重新分配车位 */ public void reAssign(Long g_groupsId, DateTime now, List groupsRelations) { log.info("迁移groupsId:{}未过期用户数量:{}", g_groupsId, groupsRelations.size()); Map goodsMap = new ConcurrentHashMap<>(); Map skuMap = new ConcurrentHashMap<>(); groupsRelations.forEach(relationView -> { Long change_relationId = relationView.getId(); Long userId = relationView.getUserId(); UserRelationChangeErrorRecord exists = userRelationChangeErrorRecordMapper.selectOne(Wrappers.lambdaQuery(UserRelationChangeErrorRecord.class) .eq(UserRelationChangeErrorRecord::getRelationId, change_relationId) .eq(UserRelationChangeErrorRecord::getUserId, userId) .eq(UserRelationChangeErrorRecord::getDeleted, true) .last("limit 1")); if (exists != null) { log.info("存在userId:{}无法转移的车票relationId:{}", userId, change_relationId); return; } Date expiryTime = relationView.getExpiryTime(); Long skuId = relationView.getSkuId(); //仅针对chatGPT if (relationView.getGoodsId() == 18) { Long bet_day = DateUtil.between(now, expiryTime, DateUnit.DAY); //10天以内 if (bet_day <= 10) { //分配到过期账号5天内误差的相同规格车队里 DateTime five_day = DateUtil.offsetDay(expiryTime, 5); Long item_groupsId = groupsMapper.selectSameSpecItemGroupsByTime(g_groupsId, skuId, expiryTime, five_day, false); if (item_groupsId == null) { //无对应车队 退款 //退款至余额 OrderDon orderDon = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class) .eq(OrderDon::getRelationId, change_relationId) .eq(OrderDon::getUserId, userId) .notIn(OrderDon::getStatus, Constant.noOrderAllStatus) .orderByDesc(OrderDon::getId) .last("limit 1")); if (orderDon == null) { //更换过车票 orderDon = orderDonMapper.selectOne(Wrappers.lambdaQuery(OrderDon.class) .eq(OrderDon::getSkuId, skuId) .eq(OrderDon::getUserId, userId) .notIn(OrderDon::getStatus, Constant.noOrderAllStatus) .orderByDesc(OrderDon::getId) .last("limit 1")); } if (orderDon == null) { log.error("用户userId:{}的车票relationId:{}账号过期后转移错误", userId, change_relationId); changeErrorRecordService.changeTicketErrorRecord(relationView, UserRelationChangeErrorRecord.Source.change); return; } //清除车票 GroupsRelation relation = groupsRelationMapper.selectById(change_relationId); groupRelationClearService.clearTicket(relation, UserTicketClearedRecord.Source.timing_change_ticket); //退款至余额 //实付金额 + 余额 BigDecimal money = orderDon.getMoney().add(orderDon.getBalance()); //当月天数 Date payTime = orderDon.getPayTime(); if (payTime == null) { payTime = orderDon.getCreatedTime(); } if (money.compareTo(BigDecimal.ZERO) == 0) { GoodsDonSku sku = skuMapper.selectById(skuId); if (sku != null) { money = sku.getPrice(); } } int dayNum = DateUtil.dayOfMonth(DateUtil.endOfMonth(payTime)); BigDecimal re_balance = money.divide(BigDecimal.valueOf(dayNum), 0, RoundingMode.DOWN).multiply(BigDecimal.valueOf(bet_day)); if (re_balance.compareTo(BigDecimal.ZERO) <= 0) { log.info("user_id:{},relationId:{}返回余额为0", userId, change_relationId); return; } userBenefitsService.addUserBalance(userId, re_balance); log.info("车票未到期清除,退款至用户user_id:{}余额:{}成功", userId, re_balance); userBenefitsService.recordBalanceBySource(userId, re_balance, UserBalanceSourceRecord.Source.clear_valid); //修改订单状态 为退款 orderDon.setStatus(OrderDon.Status.refund); orderDon.setRefundBalance(re_balance); orderDon.setRefundMoney(BigDecimal.ZERO); orderDonMapper.updateById(orderDon); GoodsDon goodsDon = goodsMap.get(relationView.getGoodsId()); if (goodsDon == null) { goodsDon = goodsDonMapper.selectById(relationView.getGoodsId()); if (goodsDon == null) return; goodsMap.putIfAbsent(relationView.getGoodsId(), goodsDon); } GoodsDonSku sku = skuMap.get(orderDon.getSkuId()); if (sku == null) { sku = skuMapper.selectById(orderDon.getSkuId()); if (sku == null) return; skuMap.putIfAbsent(orderDon.getSkuId(), sku); } cmsOrderDonService.refundRecord(orderDon, goodsDon, sku, StrUtil.EMPTY, "balance"); return; } changeTickerRelation(item_groupsId, relationView, UserTicketClearedRecord.Source.timing_change_ticket); return; } //大于10天 //分配到过期账号10天内误差的相同规格车队里 DateTime ten_day = DateUtil.offsetDay(expiryTime, 10); Long item_groupsId = groupsMapper.selectSameSpecItemGroupsByTime(g_groupsId, skuId, expiryTime, ten_day, true); if (item_groupsId == null) { //无对应车队 生成空车队 //寻找空车队 item_groupsId = groupsMapper.selectSameSpecEmptyGroups(skuId); if (item_groupsId == null) { //新增空车队 GoodsDonSku sku = skuMapper.selectById(skuId); GroupsRelation new_relation = groupsFuncService.createNewGroupsTrips(sku); item_groupsId = new_relation.getGroupsId(); } //新车队 changeTickerRelation(item_groupsId, relationView, UserTicketClearedRecord.Source.new_groups_ticket); return; } changeTickerRelation(item_groupsId, relationView, UserTicketClearedRecord.Source.timing_change_ticket); return; } if (relationView.getGoodsId() == 26) { //移入合适车队 Long item_groupsId = groupsMapper.selectMidJourneySameSpecItemGroups(g_groupsId, skuId, expiryTime); if (item_groupsId == null) { //midJourney 无合适车队 log.error("midJourney账号过期groupsId:{},用户车票relation_id:{}", g_groupsId, change_relationId); changeErrorRecordService.changeTicketErrorRecord(relationView, UserRelationChangeErrorRecord.Source.change); return; } changeTickerRelation(item_groupsId, relationView, UserTicketClearedRecord.Source.timing_change_ticket); } }); } public void changeTickerRelation(Long item_groupsId, GroupsRelationView relationView, UserTicketClearedRecord.Source source) { Long change_relationId = relationView.getId(); try { ChangeRelationReq req = new ChangeRelationReq(); req.setGroupsId(item_groupsId); req.setRelationId(change_relationId); req.setSource(source); cmsOrderDonService.changeRelation(req); } catch (Exception e) { log.error("更换用户relationId:{}错误:{}", change_relationId, StringUtil.getErrorText(e)); relationView.setErrorMsg(StringUtil.getErrorMsg(e)); changeErrorRecordService.changeTicketErrorRecord(relationView, UserRelationChangeErrorRecord.Source.change); } } }