|  | @@ -0,0 +1,226 @@
 | 
	
		
			
				|  |  | +package com.zhongshu.reward.server.core.service;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +import cn.hutool.core.bean.BeanUtil;
 | 
	
		
			
				|  |  | +import cn.hutool.core.lang.Snowflake;
 | 
	
		
			
				|  |  | +import com.github.microservice.auth.client.content.ResultContent;
 | 
	
		
			
				|  |  | +import com.github.microservice.auth.client.content.ResultState;
 | 
	
		
			
				|  |  | +import com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferResponse;
 | 
	
		
			
				|  |  | +import com.wechat.pay.java.service.transferbatch.model.TransferBatchEntity;
 | 
	
		
			
				|  |  | +import com.wechat.pay.java.service.transferbatch.model.TransferDetailInput;
 | 
	
		
			
				|  |  | +import com.zhongshu.reward.client.model.param.WalletTransferParam;
 | 
	
		
			
				|  |  | +import com.zhongshu.reward.client.model.wallet.TransferModel;
 | 
	
		
			
				|  |  | +import com.zhongshu.reward.client.model.wallet.TransferStatusModel;
 | 
	
		
			
				|  |  | +import com.zhongshu.reward.client.model.wallet.WxTransferBatchModel;
 | 
	
		
			
				|  |  | +import com.zhongshu.reward.client.ret.CommentException;
 | 
	
		
			
				|  |  | +import com.zhongshu.reward.client.type.TimeUnitType;
 | 
	
		
			
				|  |  | +import com.zhongshu.reward.client.type.TransferChannel;
 | 
	
		
			
				|  |  | +import com.zhongshu.reward.client.type.TransferStatus;
 | 
	
		
			
				|  |  | +import com.zhongshu.reward.server.core.dao.TransferRulerDao;
 | 
	
		
			
				|  |  | +import com.zhongshu.reward.server.core.dao.WalletDao;
 | 
	
		
			
				|  |  | +import com.zhongshu.reward.server.core.dao.WxTransferBatchDao;
 | 
	
		
			
				|  |  | +import com.zhongshu.reward.server.core.domain.TransferRuler;
 | 
	
		
			
				|  |  | +import com.zhongshu.reward.server.core.domain.Wallet;
 | 
	
		
			
				|  |  | +import com.zhongshu.reward.server.core.domain.WxTransferBatch;
 | 
	
		
			
				|  |  | +import com.zhongshu.reward.server.core.util.DateUtils;
 | 
	
		
			
				|  |  | +import lombok.extern.slf4j.Slf4j;
 | 
	
		
			
				|  |  | +import org.apache.commons.lang3.ObjectUtils;
 | 
	
		
			
				|  |  | +import org.springframework.beans.factory.annotation.Autowired;
 | 
	
		
			
				|  |  | +import org.springframework.data.mongodb.core.aggregation.DateOperators;
 | 
	
		
			
				|  |  | +import org.springframework.stereotype.Service;
 | 
	
		
			
				|  |  | +import org.springframework.transaction.annotation.Transactional;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +import java.math.BigDecimal;
 | 
	
		
			
				|  |  | +import java.time.LocalDateTime;
 | 
	
		
			
				|  |  | +import java.time.temporal.TemporalAdjuster;
 | 
	
		
			
				|  |  | +import java.time.temporal.TemporalAdjusters;
 | 
	
		
			
				|  |  | +import java.util.ArrayList;
 | 
	
		
			
				|  |  | +import java.util.Calendar;
 | 
	
		
			
				|  |  | +import java.util.List;
 | 
	
		
			
				|  |  | +import java.util.stream.Collectors;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * @author wjf
 | 
	
		
			
				|  |  | + * @date 2024/8/5
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +@Service
 | 
	
		
			
				|  |  | +@Slf4j
 | 
	
		
			
				|  |  | +public class WxTransferService {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Autowired
 | 
	
		
			
				|  |  | +    WalletDao walletDao;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Autowired
 | 
	
		
			
				|  |  | +    ChatTransferBatchService wechatService;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Autowired
 | 
	
		
			
				|  |  | +    Snowflake snowflake;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Autowired
 | 
	
		
			
				|  |  | +    WxTransferBatchDao wxTransferBatchDao;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Autowired
 | 
	
		
			
				|  |  | +    TransferRulerDao transferRulerDao;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 发起提现
 | 
	
		
			
				|  |  | +     * @param param
 | 
	
		
			
				|  |  | +     * @return
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    @Transactional
 | 
	
		
			
				|  |  | +    public Object transfer(WalletTransferParam param){
 | 
	
		
			
				|  |  | +        Wallet wallet = walletDao.findTop1ById(param.getWalletId());
 | 
	
		
			
				|  |  | +        //校验提现规则
 | 
	
		
			
				|  |  | +        validTransfer(param.getTotal(), wallet);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        String outBatchNo = snowflake.nextIdStr();
 | 
	
		
			
				|  |  | +        String outDetailNo = snowflake.nextIdStr();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        TransferModel transferModel = new TransferModel();
 | 
	
		
			
				|  |  | +        transferModel.setBatchName("佣金/返利发放");
 | 
	
		
			
				|  |  | +        transferModel.setBatchRemark("佣金/返利发放");
 | 
	
		
			
				|  |  | +        transferModel.setTotalAmount(param.getTotal().longValue());
 | 
	
		
			
				|  |  | +        transferModel.setOutBatchNo(outBatchNo);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        List<TransferDetailInput> detailList = new ArrayList<>();
 | 
	
		
			
				|  |  | +        TransferDetailInput detail = new TransferDetailInput();
 | 
	
		
			
				|  |  | +        detail.setTransferAmount(param.getTotal().longValue());
 | 
	
		
			
				|  |  | +        detail.setTransferRemark("佣金/返利发放");
 | 
	
		
			
				|  |  | +        detail.setOpenid(param.getOpenid());
 | 
	
		
			
				|  |  | +        detail.setOutDetailNo(outDetailNo);
 | 
	
		
			
				|  |  | +        detailList.add(detail);
 | 
	
		
			
				|  |  | +        transferModel.setTotalNum(detailList.size());
 | 
	
		
			
				|  |  | +        transferModel.setTransferDetailList(detailList);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        InitiateBatchTransferResponse response = wechatService.initiateBatchTransfer(transferModel);
 | 
	
		
			
				|  |  | +        if (response==null){
 | 
	
		
			
				|  |  | +            return ResultContent.build(ResultState.Fail);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        WxTransferBatch wxTransferBatch = new WxTransferBatch();
 | 
	
		
			
				|  |  | +        wxTransferBatch.setBatchNo(outBatchNo);
 | 
	
		
			
				|  |  | +        wxTransferBatch.setDetailNo(outDetailNo);
 | 
	
		
			
				|  |  | +        wxTransferBatch.setWallet(wallet);
 | 
	
		
			
				|  |  | +        wxTransferBatch.setTotal(param.getTotal());
 | 
	
		
			
				|  |  | +        wxTransferBatch.setChannel(TransferChannel.WeChat);
 | 
	
		
			
				|  |  | +        wxTransferBatch.setBeforeWalletInfo(wallet);
 | 
	
		
			
				|  |  | +        wxTransferBatch.setBatchStatus(response.getBatchStatus());
 | 
	
		
			
				|  |  | +        wxTransferBatch.setWxBatchId(response.getBatchId());
 | 
	
		
			
				|  |  | +        wxTransferBatch.setTransferStatus(TransferStatus.PROCESSING);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        wxTransferBatchDao.save(wxTransferBatch);
 | 
	
		
			
				|  |  | +        wallet.setAmount(wallet.getAmount().subtract(param.getTotal()));
 | 
	
		
			
				|  |  | +        wallet.setTodayTransferAmount(wallet.getTodayTransferAmount().add(param.getTotal()));
 | 
	
		
			
				|  |  | +        walletDao.save(wallet);
 | 
	
		
			
				|  |  | +        return ResultContent.build(ResultState.Success);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    public Object queryTransfer(String walletId, Integer year, Integer month){
 | 
	
		
			
				|  |  | +        Long monthStartTime = DateUtils.getMonthStartTime(year, month);
 | 
	
		
			
				|  |  | +        Long monthEndTime = DateUtils.getMonthEndTime(year, month);
 | 
	
		
			
				|  |  | +        List<WxTransferBatch> list = wxTransferBatchDao.list(walletId, monthStartTime, monthEndTime);
 | 
	
		
			
				|  |  | +        return ResultContent.buildContent(list.stream().map(this::toModel).collect(Collectors.toList()));
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    @Transactional
 | 
	
		
			
				|  |  | +    public Object updateTransferStatus(String transferId){
 | 
	
		
			
				|  |  | +        WxTransferBatch wxTransferBatch = wxTransferBatchDao.findTop1ById(transferId);
 | 
	
		
			
				|  |  | +        if (ObjectUtils.isEmpty(wxTransferBatch)){
 | 
	
		
			
				|  |  | +            return ResultContent.build(ResultState.Fail, "提现订单不存在");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        TransferStatus transferStatus = wxTransferBatch.getTransferStatus();
 | 
	
		
			
				|  |  | +        if (!transferStatus.equals(TransferStatus.PROCESSING)){
 | 
	
		
			
				|  |  | +            return ResultContent.build(ResultState.Success);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        TransferStatusModel transferStatusModel = new TransferStatusModel();
 | 
	
		
			
				|  |  | +        transferStatusModel.setBatchId(wxTransferBatch.getWxBatchId());
 | 
	
		
			
				|  |  | +        transferStatusModel.setNeedQueryDetail(true);
 | 
	
		
			
				|  |  | +        transferStatusModel.setDetailStatus("ALL");
 | 
	
		
			
				|  |  | +        TransferBatchEntity transferBatchByNo = wechatService.getTransferBatchByNo(transferStatusModel);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        String batchStatus = transferBatchByNo.getTransferBatch().getBatchStatus();
 | 
	
		
			
				|  |  | +        String detailStatus = transferBatchByNo.getTransferDetailList().get(0).getDetailStatus();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        if(batchStatus.equals("FINISHED") && detailStatus.equals("SUCCESS")){
 | 
	
		
			
				|  |  | +            transferStatus = TransferStatus.SUCCESS;
 | 
	
		
			
				|  |  | +        }else if(batchStatus.equals("CLOSED")){
 | 
	
		
			
				|  |  | +            transferStatus = TransferStatus.CLOSE;
 | 
	
		
			
				|  |  | +            Wallet wallet = walletDao.findTop1ById(wxTransferBatch.getWallet().getId());
 | 
	
		
			
				|  |  | +            wallet.setAmount(wallet.getAmount().add(wxTransferBatch.getTotal()));
 | 
	
		
			
				|  |  | +            wallet.setTodayTransferAmount(wallet.getTodayTransferAmount().subtract(wxTransferBatch.getTotal()));
 | 
	
		
			
				|  |  | +            walletDao.save(wallet);
 | 
	
		
			
				|  |  | +        }else if (detailStatus.equals("FAIL")){
 | 
	
		
			
				|  |  | +            transferStatus = TransferStatus.FAIL;
 | 
	
		
			
				|  |  | +            Wallet wallet = walletDao.findTop1ById(wxTransferBatch.getWallet().getId());
 | 
	
		
			
				|  |  | +            wallet.setAmount(wallet.getAmount().add(wxTransferBatch.getTotal()));
 | 
	
		
			
				|  |  | +            wallet.setTodayTransferAmount(wallet.getTodayTransferAmount().subtract(wxTransferBatch.getTotal()));
 | 
	
		
			
				|  |  | +            walletDao.save(wallet);
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        boolean update = wxTransferBatchDao.updateStatus(transferId, batchStatus, detailStatus, transferStatus);
 | 
	
		
			
				|  |  | +        log.info("更新微信转账批次状态:结果:{}, id:{}, batchStatus:{}, detailStatus: {}, transferStatus: {}",update, transferId, batchStatus, detailStatus, transferStatus);
 | 
	
		
			
				|  |  | +        return ResultContent.build(update);
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    WxTransferBatchModel toModel(WxTransferBatch wxTransferBatch){
 | 
	
		
			
				|  |  | +        WxTransferBatchModel model = new WxTransferBatchModel();
 | 
	
		
			
				|  |  | +        if (null != wxTransferBatch){
 | 
	
		
			
				|  |  | +            BeanUtil.copyProperties(wxTransferBatch, model, "beforeWalletInfo", "wallet");
 | 
	
		
			
				|  |  | +            model.setWalletId(wxTransferBatch.getWallet().getId());
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        return model;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    void validTransfer(BigDecimal total, Wallet wallet){
 | 
	
		
			
				|  |  | +        long currentTime = System.currentTimeMillis();
 | 
	
		
			
				|  |  | +        Integer size = 0;
 | 
	
		
			
				|  |  | +        List<TransferRuler> list = transferRulerDao.findAll();
 | 
	
		
			
				|  |  | +        if (list.isEmpty()){
 | 
	
		
			
				|  |  | +            throw new CommentException(com.zhongshu.reward.client.ret.ResultState.Exception, "管理员未设置提现规则");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        TransferRuler transferRuler = list.get(0);
 | 
	
		
			
				|  |  | +        //提现时间
 | 
	
		
			
				|  |  | +        if (transferRuler.getTimeUnit().equals(TimeUnitType.MONTH)){//每月
 | 
	
		
			
				|  |  | +            Long dayOfMonthStartTime = DateUtils.getDayOfMonthStartTime(transferRuler.getStartDay());
 | 
	
		
			
				|  |  | +            Long dayOfMonthEndTime = DateUtils.getDayOfMonthEndTime(transferRuler.getEndDay());
 | 
	
		
			
				|  |  | +            if (currentTime < dayOfMonthStartTime || currentTime > dayOfMonthEndTime){
 | 
	
		
			
				|  |  | +                throw new CommentException(com.zhongshu.reward.client.ret.ResultState.Exception, "当前不处于提现时间段");
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            size = wxTransferBatchDao.countByTime(wallet.getId(), dayOfMonthStartTime, dayOfMonthEndTime).intValue();
 | 
	
		
			
				|  |  | +        }else if (transferRuler.getTimeUnit().equals(TimeUnitType.DAY)){
 | 
	
		
			
				|  |  | +            Long hourMinuteStartTime = DateUtils.getHourMinuteStartTime(transferRuler.getStartHour(), transferRuler.getStartMinute());
 | 
	
		
			
				|  |  | +            Long hourMinuteEndTime = DateUtils.getHourMinuteEndTime(transferRuler.getEndHour(), transferRuler.getEndMinute());
 | 
	
		
			
				|  |  | +            if (currentTime < hourMinuteStartTime || currentTime > hourMinuteEndTime){
 | 
	
		
			
				|  |  | +                throw new CommentException(com.zhongshu.reward.client.ret.ResultState.Exception, "当前不处于提现时间段");
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            size = wxTransferBatchDao.countByTime(wallet.getId(), hourMinuteStartTime, hourMinuteEndTime).intValue();
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        //单笔金额
 | 
	
		
			
				|  |  | +        if (total.compareTo(transferRuler.getMinTotal()) < 0){
 | 
	
		
			
				|  |  | +            throw new CommentException(com.zhongshu.reward.client.ret.ResultState.Exception, "单笔提现金额低于最小值");
 | 
	
		
			
				|  |  | +        }else if (total.compareTo(transferRuler.getMaxTotal()) > 0){
 | 
	
		
			
				|  |  | +            throw new CommentException(com.zhongshu.reward.client.ret.ResultState.Exception, "单笔提现金额超出最大值");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        //判断提现额度是否超出每日上限
 | 
	
		
			
				|  |  | +        Integer sumDayTotal = wxTransferBatchDao.sumDayTotal(wallet.getId(), DateUtils.getCurrentDayStartTime().getTime(), DateUtils.getCurrentDayEndTime().getTime());
 | 
	
		
			
				|  |  | +        if (total.add(BigDecimal.valueOf(sumDayTotal)).compareTo(transferRuler.getDayMaxTotal()) > 0){
 | 
	
		
			
				|  |  | +            throw new CommentException(com.zhongshu.reward.client.ret.ResultState.Exception, "超出每日提现金额上限");
 | 
	
		
			
				|  |  | +//            return ResultContent.build(ResultState.Fail, "超出每日提现金额上限");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        //判断可提现金额是否足够
 | 
	
		
			
				|  |  | +        if (total.compareTo(wallet.getAmount()) < 0){
 | 
	
		
			
				|  |  | +            throw new CommentException(com.zhongshu.reward.client.ret.ResultState.Exception, "可提现余额不足");
 | 
	
		
			
				|  |  | +//            return ResultContent.build(ResultState.Fail, "可提现余额不足");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        //提现次数
 | 
	
		
			
				|  |  | +        if (transferRuler.getSize() >= size){
 | 
	
		
			
				|  |  | +            throw new CommentException(com.zhongshu.reward.client.ret.ResultState.Exception, "超过可提现次数");
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +}
 |