Przeglądaj źródła

红德堂-优化红包发送逻辑,新增红包发送状态检查定时任务

Long 2 tygodni temu
rodzic
commit
b26b548bfb

+ 113 - 0
fs-admin/src/main/java/com/fs/task/FsCompanyTask.java

@@ -3,18 +3,34 @@ package com.fs.task;
 import cn.hutool.json.JSONUtil;
 import com.fs.common.constant.FsConstants;
 import com.fs.common.core.redis.RedisCache;
+import com.fs.common.exception.CustomException;
+import com.fs.common.utils.StringUtils;
 import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyMoneyLogs;
 import com.fs.company.domain.CompanyRedPacketBalanceDeductionRecord;
 import com.fs.company.mapper.CompanyMoneyLogsMapper;
 import com.fs.company.service.ICompanyRedPacketBalanceDeductionRecordService;
 import com.fs.company.service.ICompanyService;
+import com.fs.company.util.CompanyRedPacketBalanceUtil;
 import com.fs.company.vo.RedPacketMoneyVO;
 import com.fs.course.config.CourseConfig;
+import com.fs.course.config.RedPacketConfig;
+import com.fs.course.domain.FsCourseRedPacketLog;
 import com.fs.course.mapper.FsCourseRedPacketLogMapper;
+import com.fs.course.service.IFsCourseRedPacketLogService;
 import com.fs.system.service.ISysConfigService;
+import com.github.binarywang.wxpay.bean.transfer.QueryTransferBatchesRequest;
+import com.github.binarywang.wxpay.bean.transfer.QueryTransferBatchesResult;
+import com.github.binarywang.wxpay.bean.transfer.TransferBillsGetResult;
+import com.github.binarywang.wxpay.config.WxPayConfig;
+import com.github.binarywang.wxpay.exception.WxPayException;
+import com.github.binarywang.wxpay.service.TransferService;
+import com.github.binarywang.wxpay.service.WxPayService;
+import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Component;
 
@@ -35,6 +51,8 @@ public class FsCompanyTask {
     private final RedisCache redisCache;
     private final CompanyMoneyLogsMapper moneyLogsMapper;
     private final RedisTemplate<String, Object> redisTemplate;
+    private final IFsCourseRedPacketLogService redPacketLogService;
+    private final CompanyRedPacketBalanceUtil redPacketBalanceUtil;
 
     public void refreshCompanyMoney() {
         // 使用红包余额
@@ -155,4 +173,99 @@ public class FsCompanyTask {
         }
 
     }
+
+
+    /**
+     * 补偿检查红包发送状态未变更记录
+     */
+    public void syncRedPacketSendStatus() {
+        if (isRedPackBalance()) {
+            LocalDateTime createTime = LocalDateTime.now().minusMinutes(30);
+            List<CompanyRedPacketBalanceDeductionRecord> records = deductionRecordService.selectAllProcessDeductionRecord(createTime);
+            records.forEach(record -> {
+                try {
+                    if (Objects.isNull(record.getRedPacketId())) {
+                        redPacketBalanceUtil.invalidRecord(record);
+                        return;
+                    }
+                    FsCourseRedPacketLog log = fsCourseRedPacketLogMapper.selectFsCourseRedPacketLogByLogId(record.getRedPacketId());
+                    String batchId = checkMchTransferStatus(log.getOutBatchNo());
+                    if (StringUtils.isNotBlank(batchId)) {
+                        redPacketLogService.syncRedPacket(log.getOutBatchNo(), batchId);
+                    } else {
+                        redPacketBalanceUtil.invalidRecord(record);
+                    }
+                } catch (Exception ignore) {}
+            });
+        } else {
+            LocalDateTime startTime = LocalDateTime.now().minusMinutes(90);
+            LocalDateTime endTime = LocalDateTime.now().minusMinutes(30);
+            List<FsCourseRedPacketLog> redPacketLogs = fsCourseRedPacketLogMapper.selectRedPacketLogListByCreateTime(startTime, endTime);
+            redPacketLogs.forEach(log -> {
+                try {
+                    String batchId = checkMchTransferStatus(log.getOutBatchNo());
+                    if (StringUtils.isNotBlank(batchId)) {
+                        redPacketLogService.syncRedPacket(log.getOutBatchNo(), batchId);
+                    }
+                } catch (Exception ignore) {}
+            });
+        }
+    }
+
+    /**
+     * 检查商家转账到零钱状态
+     */
+    private String checkMchTransferStatus(String outBatchNo) {
+        String json = configService.selectConfigByKey("redPacket.config");
+        RedPacketConfig config = JSONUtil.toBean(json, RedPacketConfig.class);
+
+        WxPayConfig payConfig = new WxPayConfig();
+        BeanUtils.copyProperties(config, payConfig);
+
+        WxPayService wxPayService = new WxPayServiceImpl();
+        wxPayService.setConfig(payConfig);
+
+        TransferService transferService = wxPayService.getTransferService();
+
+        if (Objects.isNull(config.getIsNew()) || !Arrays.asList(0,1).contains(config.getIsNew())) {
+            log.error("红包配置错误 isNew is err");
+            throw new CustomException("红包配置错误 isNew is err ");
+        }
+
+        try {
+            if (config.getIsNew() == 0) {
+                QueryTransferBatchesRequest request = new QueryTransferBatchesRequest();
+                request.setOutBatchNo(outBatchNo);
+                request.setNeedQueryDetail(true);
+                request.setOffset(0);
+                request.setLimit(20);
+                request.setDetailStatus("ALL");
+                QueryTransferBatchesResult result = transferService.transferBatchesOutBatchNo(request);
+                List<QueryTransferBatchesResult.TransferDetail> detailList = result.getTransferDetailList();
+                boolean isSuccess = detailList.stream().anyMatch(d -> "SUCCESS".equals(d.getDetailStatus()));
+                if (isSuccess) {
+                    return result.getTransferBatch().getBatchId();
+                }
+
+                boolean isFail = detailList.stream().anyMatch(d -> "FAIL".equals(d.getDetailStatus()));
+                if (isFail) {
+                    return "";
+                }
+            } else {
+                TransferBillsGetResult result = transferService.getBillsByOutBillNo(outBatchNo);
+                if ("SUCCESS".equals(result.getState())) {
+                    return result.getTransferBillNo();
+                } else if ("FAIL".equals(result.getState())) {
+                    return "";
+                } else if ("CANCELLED".equals(result.getState())) {
+                    return "";
+                }
+            }
+        } catch (WxPayException e) {
+            log.error("查询转账单失败 err: {}", e.getMessage(), e);
+            throw new CustomException("查询转账单失败:" + e.getMessage());
+        }
+
+        throw new CustomException("转账处理中");
+    }
 }

+ 8 - 1
fs-service/src/main/java/com/fs/company/mapper/CompanyRedPacketBalanceDeductionRecordMapper.java

@@ -6,6 +6,7 @@ import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 
 import java.math.BigDecimal;
+import java.time.LocalDateTime;
 import java.util.List;
 
 public interface CompanyRedPacketBalanceDeductionRecordMapper extends BaseMapper<CompanyRedPacketBalanceDeductionRecord> {
@@ -26,5 +27,11 @@ public interface CompanyRedPacketBalanceDeductionRecordMapper extends BaseMapper
      * 查询所有已完成扣款的数据
      */
     @Select("select * from company_red_packet_balance_deduction_record where status = 1 limit 5000")
-    List<CompanyRedPacketBalanceDeductionRecord> selectByStatus();
+    List<CompanyRedPacketBalanceDeductionRecord> selectByComplete();
+
+    /**
+     *  查询所有处理中的数据
+     */
+    @Select("select * from company_red_packet_balance_deduction_record where status = 0 and create_time <= #{createTime} ")
+    List<CompanyRedPacketBalanceDeductionRecord> selectByProcess(@Param("createTime") LocalDateTime createTime);
 }

+ 6 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyRedPacketBalanceDeductionRecordService.java

@@ -3,6 +3,7 @@ package com.fs.company.service;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.fs.company.domain.CompanyRedPacketBalanceDeductionRecord;
 
+import java.time.LocalDateTime;
 import java.util.List;
 
 public interface ICompanyRedPacketBalanceDeductionRecordService extends IService<CompanyRedPacketBalanceDeductionRecord> {
@@ -11,4 +12,9 @@ public interface ICompanyRedPacketBalanceDeductionRecordService extends IService
      * 查询所有已完成扣款的数据
      */
     List<CompanyRedPacketBalanceDeductionRecord> selectAllCompleteDeductionRecord();
+
+    /**
+     * 查询所有处理中的数据
+     */
+    List<CompanyRedPacketBalanceDeductionRecord> selectAllProcessDeductionRecord(LocalDateTime createTime);
 }

+ 10 - 1
fs-service/src/main/java/com/fs/company/service/impl/CompanyRedPacketBalanceDeductionRecordServiceImpl.java

@@ -6,6 +6,7 @@ import com.fs.company.mapper.CompanyRedPacketBalanceDeductionRecordMapper;
 import com.fs.company.service.ICompanyRedPacketBalanceDeductionRecordService;
 import org.springframework.stereotype.Service;
 
+import java.time.LocalDateTime;
 import java.util.Collections;
 import java.util.List;
 
@@ -18,6 +19,14 @@ public class CompanyRedPacketBalanceDeductionRecordServiceImpl extends ServiceIm
      */
     @Override
     public List<CompanyRedPacketBalanceDeductionRecord> selectAllCompleteDeductionRecord() {
-        return baseMapper.selectByStatus();
+        return baseMapper.selectByComplete();
+    }
+
+    /**
+     *  查询所有处理中的数据
+     */
+    @Override
+    public List<CompanyRedPacketBalanceDeductionRecord> selectAllProcessDeductionRecord(LocalDateTime createTime) {
+        return baseMapper.selectByProcess(createTime);
     }
 }

+ 6 - 0
fs-service/src/main/java/com/fs/course/mapper/FsCourseRedPacketLogMapper.java

@@ -178,4 +178,10 @@ public interface FsCourseRedPacketLogMapper
     List<CourseRedPacketStatisticsDTO> statistics(CourseRedPacketStatisticsParam param);
 
     List<FsCourseRedPacketLog> selectFsCourseRedPacketLogListBySending(@Param("maps") Map<String, Object> map);
+
+    /**
+     * 根据创建时间查询红包记录
+     */
+    @Select("select * from fs_course_red_packet_log where status = 0 and batch_id is not null and create_time >= #{startTime} and create_time <= #{endTime}")
+    List<FsCourseRedPacketLog> selectRedPacketLogListByCreateTime(@Param("startTime") LocalDateTime startTime, @Param("endTime") LocalDateTime endTime);
 }

+ 69 - 33
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -31,6 +31,7 @@ import com.fs.company.mapper.*;
 import com.fs.company.service.ICompanyService;
 import com.fs.company.util.CompanyRedPacketBalanceUtil;
 import com.fs.config.cloud.CloudHostProper;
+import com.fs.core.utils.OrderCodeUtils;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.domain.*;
 import com.fs.course.dto.CoursePackageDTO;
@@ -1201,10 +1202,6 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
                         return R.error("操作频繁,请稍后再试!");
                     }
                 }
-                if (packetLog != null && packetLog.getStatus() == 2) {
-                    return R.error("请联系客服补发");
-                }
-                return R.error("奖励已发放");
             }
             // 获取视频信息
             FsUserCourseVideo video = fsUserCourseVideoMapper.selectFsUserCourseVideoByVideoId(param.getVideoId());
@@ -1482,6 +1479,34 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
             }
         }
 
+        FsCourseRedPacketLog redPacketLog = redPacketLogMapper.selectFsCourseRedPacketLogByTemporary(param.getVideoId(), param.getUserId());
+        if (redPacketLog != null && redPacketLog.getStatus() != 2) {
+            return R.error("操作频繁,请稍后再试!");
+        } else {
+            // 添加红包记录
+            redPacketLog = new FsCourseRedPacketLog();
+            redPacketLog.setCourseId(param.getCourseId());
+            redPacketLog.setCompanyId(param.getCompanyId());
+            redPacketLog.setUserId(param.getUserId());
+            redPacketLog.setVideoId(param.getVideoId());
+            redPacketLog.setStatus(2);
+            redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
+            redPacketLog.setCompanyUserId(param.getCompanyUserId());
+            redPacketLog.setCreateTime(new Date());
+            redPacketLog.setAmount(amount);
+            redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
+            redPacketLog.setPeriodId(param.getPeriodId());
+
+            String code =  OrderCodeUtils.getOrderSn();
+            if (StringUtils.isEmpty(code)) {
+                return R.error("红包单号生成失败,请重试");
+            }
+            redPacketLog.setOutBatchNo("fsCourse"+cloudHostProper.getProjectCode() + code);
+
+            redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
+        }
+
+        packetParam.setOutBatchNo(redPacketLog.getOutBatchNo());
         // 发送红包
         R sendRedPacket;
         try {
@@ -1492,29 +1517,20 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
         }
 
         if (sendRedPacket.get("code").equals(200)) {
-            FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
             TransferBillsResult transferBillsResult;
+            OffsetDateTime offsetDateTime;
             if (sendRedPacket.get("isNew").equals(1)) {
                 transferBillsResult = (TransferBillsResult) sendRedPacket.get("data");
                 redPacketLog.setResult(JSON.toJSONString(sendRedPacket));
                 redPacketLog.setOutBatchNo(transferBillsResult.getOutBillNo());
+                offsetDateTime = OffsetDateTime.parse(transferBillsResult.getCreateTime());
             } else {
                 redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
+                offsetDateTime = OffsetDateTime.parse(sendRedPacket.get("createTime").toString());
             }
-            // 添加红包记录
-            redPacketLog.setCourseId(param.getCourseId());
-            redPacketLog.setCompanyId(param.getCompanyId());
-            redPacketLog.setUserId(param.getUserId());
-            redPacketLog.setVideoId(param.getVideoId());
             redPacketLog.setStatus(0);
-            redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
-            redPacketLog.setCompanyUserId(param.getCompanyUserId());
-            redPacketLog.setCreateTime(new Date());
-            redPacketLog.setAmount(amount);
-            redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
-            redPacketLog.setPeriodId(param.getPeriodId());
-
-            redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
+            redPacketLog.setCreateTime(Date.from(offsetDateTime.toInstant()));
+            redPacketLogMapper.updateFsCourseRedPacketLog(redPacketLog);
             // 更新观看记录的奖励类型
             log.setRewardType(config.getRewardType());
             courseWatchLogMapper.updateFsCourseWatchLog(log);
@@ -1624,6 +1640,36 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
                 }
             }
 
+            FsCourseRedPacketLog redPacketLog = redPacketLogMapper.selectUserFsCourseRedPacketLog(param.getVideoId(), param.getUserId(), param.getPeriodId());
+            if (redPacketLog != null && redPacketLog.getStatus() != 2) {
+                return R.error("操作频繁,请稍后再试!");
+            } else {
+                // 添加红包记录
+                redPacketLog = new FsCourseRedPacketLog();
+                redPacketLog.setCourseId(param.getCourseId());
+                redPacketLog.setCompanyId(param.getCompanyId());
+                redPacketLog.setUserId(param.getUserId());
+                redPacketLog.setVideoId(param.getVideoId());
+                redPacketLog.setStatus(2);
+                redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
+                redPacketLog.setCompanyUserId(param.getCompanyUserId());
+                redPacketLog.setCreateTime(new Date());
+                redPacketLog.setAmount(amount);
+                redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
+                redPacketLog.setPeriodId(param.getPeriodId());
+                redPacketLog.setAppId(param.getAppId());
+
+                String code =  OrderCodeUtils.getOrderSn();
+                if (StringUtils.isEmpty(code)) {
+                    return R.error("红包单号生成失败,请重试");
+                }
+                redPacketLog.setOutBatchNo("fsCourse"+cloudHostProper.getProjectCode() + code);
+
+                redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
+            }
+
+            packetParam.setOutBatchNo(redPacketLog.getOutBatchNo());
+
             // 发送红包
             R sendRedPacket;
             try {
@@ -1635,32 +1681,22 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService {
 
             // 红包发送成功处理
             if (sendRedPacket.get("code").equals(200)) {
-                FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
                 TransferBillsResult transferBillsResult;
+                OffsetDateTime offsetDateTime;
                 if (sendRedPacket.get("isNew").equals(1)){
                     transferBillsResult = (TransferBillsResult)sendRedPacket.get("data");
                     redPacketLog.setResult(JSON.toJSONString(sendRedPacket));
                     redPacketLog.setOutBatchNo(transferBillsResult.getOutBillNo());
                     redPacketLog.setBatchId(transferBillsResult.getTransferBillNo());
+                    offsetDateTime = OffsetDateTime.parse(transferBillsResult.getCreateTime());
                 }else {
                     redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
                     redPacketLog.setBatchId(sendRedPacket.get("batchId").toString());
+                    offsetDateTime = OffsetDateTime.parse(sendRedPacket.get("createTime").toString());
                 }
-                // 添加红包记录
-                redPacketLog.setCourseId(param.getCourseId());
-                redPacketLog.setCompanyId(param.getCompanyId());
-                redPacketLog.setUserId(param.getUserId());
-                redPacketLog.setVideoId(param.getVideoId());
                 redPacketLog.setStatus(0);
-                redPacketLog.setQwUserId(param.getQwUserId() != null ? param.getQwUserId() : null);
-                redPacketLog.setCompanyUserId(param.getCompanyUserId());
-                redPacketLog.setCreateTime(new Date());
-                redPacketLog.setAmount(amount);
-                redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
-                redPacketLog.setPeriodId(param.getPeriodId());
-                redPacketLog.setAppId(param.getAppId());
-
-                redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
+                redPacketLog.setCreateTime(Date.from(offsetDateTime.toInstant()));
+                redPacketLogMapper.updateFsCourseRedPacketLog(redPacketLog);
 
                 // 更新观看记录的奖励类型
                 log.setRewardType(config.getRewardType());

+ 2 - 0
fs-service/src/main/java/com/fs/his/param/WxSendRedPacketParam.java

@@ -28,5 +28,7 @@ public class WxSendRedPacketParam implements Serializable {
 
     private String mpAppId;
 
+    private String outBatchNo; // 批次号
+
 
 }

+ 6 - 16
fs-service/src/main/java/com/fs/his/service/impl/FsStorePaymentServiceImpl.java

@@ -816,13 +816,7 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
         TransferBillsRequest request = new TransferBillsRequest();
         request.setAppid(config.getAppId());
         request.setOpenid(param.getOpenId());
-
-        String code =  OrderCodeUtils.getOrderSn();
-        if(StringUtils.isEmpty(code)){
-            return R.error("订单生成失败,请重试");
-        }
-//        String code = String.valueOf(IdUtil.getSnowflake(0, 0).nextId());
-        request.setOutBillNo("fsCourse" + code);
+        request.setOutBillNo(param.getOutBatchNo());
 
         Integer amount = WxPayUnifiedOrderRequest.yuanToFen(param.getAmount() != null ? param.getAmount().toString() : "0.1");
         request.setTransferAmount(amount);
@@ -872,14 +866,7 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
         TransferBatchesRequest request = new TransferBatchesRequest();
         request.setAppid(config.getAppId());
 
-
-        // todo 如果未配置负载均衡请还原原本的单号方式
-//        String code = IdUtil.getSnowflake(0, 0).nextIdStr();
-        String code =  OrderCodeUtils.getOrderSn();
-        if(StringUtils.isEmpty(code)){
-            return R.error("红包单号生成失败,请重试");
-        }
-        request.setOutBatchNo("fsCourse"+cloudHostProper.getProjectCode() + code);
+        request.setOutBatchNo(param.getOutBatchNo());
         request.setBatchRemark("课堂答题奖励");
         request.setBatchName("课堂答题奖励");
         Integer amount = WxPayUnifiedOrderRequest.yuanToFen(param.getAmount().toString());
@@ -899,7 +886,10 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
 
         try {
             TransferBatchesResult transferBatchesResult = transferService.transferBatches(request);
-            return R.ok("发送红包成功").put("orderCode", transferBatchesResult.getOutBatchNo()).put("batchId", transferBatchesResult.getBatchId());
+            return R.ok("发送红包成功")
+                    .put("orderCode", transferBatchesResult.getOutBatchNo())
+                    .put("batchId", transferBatchesResult.getBatchId())
+                    .put("createTime", transferBatchesResult.getCreateTime());
         } catch (Exception e) {
             logger.error("商家转账支付失败:参数: {} :原因: {}",JSON.toJSONString(param), e.getMessage(),e);
             throw new RuntimeException(e);