Переглянути джерело

1、后台配置页面新增是否允许销售公司金额位负数条件
2、发红包优化,增加配置直接发送红包
3、增加补发定时任务

yfh 6 днів тому
батько
коміт
10af4a20b0

+ 20 - 0
fs-admin/src/main/java/com/fs/his/task/SendRedPacketTask.java

@@ -0,0 +1,20 @@
+package com.fs.his.task;
+
+import com.fs.course.service.IFsCourseRedPacketLogService;
+import com.fs.course.service.IFsUserCourseVideoRedPackageService;
+import com.fs.course.service.IFsUserCourseVideoService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service("sendRedPacketTask")
+public class SendRedPacketTask {
+    @Autowired
+    private IFsCourseRedPacketLogService redPacketLogService;
+
+    public void sendRedPacket(){
+        redPacketLogService.sendRedPacketBf();
+    }
+
+}

+ 1 - 0
fs-service/src/main/java/com/fs/course/config/CourseConfig.java

@@ -39,6 +39,7 @@ public class CourseConfig implements Serializable {
     private String sidebarImageUrl;//侧边栏公共图
     private Integer delayStart;
     private Integer delayEnd;
+    private Integer isNegative;//是否为负数 0、不允许,1、允许
 
     /**
      * 小程序授权头像昵称方式(目前仅会员看课有效)

+ 1 - 1
fs-service/src/main/java/com/fs/course/domain/FsCourseRedPacketLog.java

@@ -49,7 +49,7 @@ public class FsCourseRedPacketLog extends BaseEntity
     @Excel(name = "分享企微userid")
     private String qwUserId;
 
-    private Integer status;//状态 0 发送中  1  已发送
+    private Integer status;//状态 0 发送中  1  已发送 ,2、待发送
 
 
     private Long watchLogId;//观看记录 id

+ 1 - 0
fs-service/src/main/java/com/fs/course/service/IFsCourseRedPacketLogService.java

@@ -82,4 +82,5 @@ public interface IFsCourseRedPacketLogService
 
     R retryCourseRedPacketLog(Long[] logIds);
 
+    void sendRedPacketBf();
 }

+ 142 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsCourseRedPacketLogServiceImpl.java

@@ -5,6 +5,7 @@ import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 
+import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
 import com.fs.common.core.domain.R;
 import com.fs.common.utils.DateUtils;
@@ -12,6 +13,7 @@ import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyMoneyLogs;
 import com.fs.company.mapper.CompanyMapper;
 import com.fs.company.mapper.CompanyMoneyLogsMapper;
+import com.fs.course.config.CourseConfig;
 import com.fs.course.domain.FsCourseWatchLog;
 import com.fs.course.mapper.FsCourseWatchLogMapper;
 import com.fs.course.param.FsCourseRedPacketLogParam;
@@ -20,7 +22,10 @@ import com.fs.his.domain.FsUser;
 import com.fs.his.mapper.FsUserMapper;
 import com.fs.his.param.WxSendRedPacketParam;
 import com.fs.his.service.IFsStorePaymentService;
+import com.fs.system.service.ISysConfigService;
 import com.github.binarywang.wxpay.bean.transfer.TransferBillsResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import com.fs.course.mapper.FsCourseRedPacketLogMapper;
@@ -37,10 +42,14 @@ import org.springframework.transaction.annotation.Transactional;
 @Service
 public class FsCourseRedPacketLogServiceImpl implements IFsCourseRedPacketLogService
 {
+    private static final Logger logger = LoggerFactory.getLogger(FsCourseRedPacketLogServiceImpl.class);
     @Autowired
     private FsCourseRedPacketLogMapper fsCourseRedPacketLogMapper;
     @Autowired
     private CompanyMapper companyMapper;
+
+    @Autowired
+    private ISysConfigService configService;
     /**
      * 查询短链课程看课记录
      *
@@ -236,4 +245,137 @@ public class FsCourseRedPacketLogServiceImpl implements IFsCourseRedPacketLogSer
         return R.ok("成功:"+suc+" 失败:"+err);
     }
 
+    @Override
+    public void sendRedPacketBf() {
+        try {
+            logger.info("【红包发放】开始执行红包发放任务");
+
+            // 初始化查询对象
+            FsCourseRedPacketLog query = new FsCourseRedPacketLog();
+            query.setStatus(2); // 状态2表示待处理红包
+
+            // 获取红包配置
+            String json = configService.selectConfigByKey("course.config");
+            CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+            logger.info("【红包发放】当前红包发放模式:{}", config.getRedPacketMode());
+
+            // 获取待处理红包列表
+            List<FsCourseRedPacketLog> pendingPackets = fsCourseRedPacketLogMapper.selectFsCourseRedPacketLogList(query);
+            if (pendingPackets == null || pendingPackets.isEmpty()) {
+                logger.info("【红包发放】没有待处理的红包记录");
+                return;
+            }
+
+            logger.info("【红包发放】共发现{}条待处理红包记录", pendingPackets.size());
+
+            // 处理每条红包记录
+            for (FsCourseRedPacketLog redPacket : pendingPackets) {
+                try {
+                    logger.info("【红包处理】开始处理红包记录ID:{},用户ID:{},金额:{}元",
+                            redPacket.getLogId(), redPacket.getUserId(), redPacket.getAmount());
+
+                    processRedPacket(redPacket, config);
+                } catch (Exception e) {
+                    logger.error("【红包处理】处理红包记录ID:{}时发生异常", redPacket.getLogId(), e);
+                    // 即使一条记录失败也继续处理下一条
+                }
+            }
+
+            logger.info("【红包发放】红包发放任务执行完成");
+        } catch (Exception e) {
+            logger.error("【红包发放】红包发放任务执行过程中发生未预期异常", e);
+        }
+    }
+
+    private void processRedPacket(FsCourseRedPacketLog redPacket, CourseConfig config) {
+        // 获取用户信息
+        FsUser user = fsUserMapper.selectFsUserByUserId(redPacket.getUserId());
+        if (user == null || user.getMpOpenId() == null) {
+            logger.error("【红包处理】错误:未找到用户ID:{}或用户缺少openId", redPacket.getUserId());
+            return;
+        }
+
+        logger.info("【红包处理】准备为用户{}发放红包,openId:{}", user.getUserId(), user.getMpOpenId());
+
+        // 准备红包参数
+        WxSendRedPacketParam packetParam = new WxSendRedPacketParam();
+        packetParam.setOpenId(user.getMpOpenId());
+        packetParam.setAmount(redPacket.getAmount());
+        packetParam.setSource(2);
+        packetParam.setAppId(redPacket.getAppId());
+        packetParam.setRedPacketMode(config.getRedPacketMode());
+        packetParam.setCompanyId(redPacket.getCompanyId());
+
+        // 处理企业资金(使用悲观锁)
+        Company company = companyMapper.selectCompanyByIdForUpdate(redPacket.getCompanyId());
+        if (company == null) {
+            logger.error("【红包处理】错误:未找到企业ID:{}", redPacket.getCompanyId());
+            return;
+        }
+
+        BigDecimal remainingBalance = company.getMoney().subtract(redPacket.getAmount());
+        if (remainingBalance.compareTo(BigDecimal.ZERO) < 0) {
+            logger.warn("【红包处理】企业{}余额不足(当前余额:{}元,需要扣除:{}元)",
+                    company.getCompanyId(), company.getMoney(), redPacket.getAmount());
+            return;
+        }
+
+        logger.info("【红包处理】企业{}当前余额:{}元,发放后余额:{}元",
+                company.getCompanyId(), company.getMoney(), remainingBalance);
+
+        // 发送红包
+        R sendRedPacketResult = paymentService.sendRedPacket(packetParam);
+        if (sendRedPacketResult == null) {
+            logger.error("【红包处理】红包接口返回空结果");
+            return;
+        }
+
+        if (!sendRedPacketResult.get("code").equals(200)) {
+            logger.error("【红包处理】红包发放失败,错误码:{},错误信息:{}",
+                    sendRedPacketResult.get("code"), sendRedPacketResult.get("msg"));
+            return;
+        }
+
+        // 处理成功结果
+        logger.info("【红包处理】红包发放成功");
+
+        // 更新红包记录
+        if (sendRedPacketResult.get("isNew").equals(1)) {
+            TransferBillsResult transferBillsResult = (TransferBillsResult)sendRedPacketResult.get("data");
+            redPacket.setResult(JSON.toJSONString(sendRedPacketResult));
+            redPacket.setOutBatchNo(transferBillsResult.getOutBillNo());
+            logger.info("【红包处理】新批次红包,批次号:{}", transferBillsResult.getOutBillNo());
+        } else {
+            redPacket.setOutBatchNo(sendRedPacketResult.get("orderCode").toString());
+            logger.info("【红包处理】已有批次红包,订单号:{}", redPacket.getOutBatchNo());
+        }
+
+        fsCourseRedPacketLogMapper.updateFsCourseRedPacketLog(redPacket);
+
+        // 更新观看记录
+        FsCourseWatchLog watchLog = courseWatchLogMapper.selectFsCourseWatchLogByLogId(redPacket.getLogId());
+        if (watchLog != null) {
+            watchLog.setRewardType(config.getRewardType());
+            courseWatchLogMapper.updateFsCourseWatchLog(watchLog);
+            logger.info("【红包处理】更新观看记录{}的奖励类型为{}", watchLog.getLogId(), config.getRewardType());
+        }
+
+        // 更新企业余额
+        company.setMoney(remainingBalance);
+        companyMapper.updateCompany(company);
+
+        // 记录资金流水
+        CompanyMoneyLogs moneyLog = new CompanyMoneyLogs();
+        moneyLog.setCompanyId(company.getCompanyId());
+        moneyLog.setRemark("扣除红包金额");
+        moneyLog.setMoney(redPacket.getAmount().multiply(new BigDecimal(-1)));
+        moneyLog.setLogsType(15);
+        moneyLog.setBalance(company.getMoney());
+        moneyLog.setCreateTime(new Date());
+        moneyLogsMapper.insertCompanyMoneyLogs(moneyLog);
+
+        logger.info("【红包处理】企业资金流水记录成功,企业ID:{},变动金额:{}元",
+                company.getCompanyId(), moneyLog.getMoney());
+    }
+
 }

+ 66 - 1
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -4,6 +4,7 @@ import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.fs.common.BeanCopyUtils;
 import com.fs.common.core.domain.R;
@@ -1171,6 +1172,9 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
 
         System.out.println("红包金额"+amount);
         System.out.println("红包商户号"+packetParam);
+        if (ObjectUtils.isNotEmpty(config.getIsNegative())&&config.getIsNegative() == 1) {
+            return processRedPacket(config, packetParam, param, amount, log);
+        }
         //2025.6.19 红包金额为0的时候
         if (amount.compareTo(BigDecimal.ZERO)>0){
 
@@ -1183,13 +1187,14 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
                 redPacketLog.setCompanyId(param.getCompanyId());
                 redPacketLog.setUserId(param.getUserId());
                 redPacketLog.setVideoId(param.getVideoId());
-                redPacketLog.setStatus(2);
+                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);
                 return R.error("销售公司余额不足");
             }
@@ -1218,6 +1223,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
                 redPacketLog.setAmount(amount);
                 redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
                 redPacketLog.setPeriodId(param.getPeriodId());
+                redPacketLog.setAppId(param.getAppId());
                 redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
 
                 // 更新观看记录的奖励类型
@@ -1254,6 +1260,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             redPacketLog.setAmount(BigDecimal.ZERO);
             redPacketLog.setWatchLogId(log.getLogId() != null ? log.getLogId() : null);
             redPacketLog.setPeriodId(param.getPeriodId());
+            redPacketLog.setAppId(param.getAppId());
             redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
 
             // 更新观看记录的奖励类型
@@ -1266,6 +1273,64 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
 
     }
 
+    /**
+     * 直接发送奖励
+     *
+     * @param config
+     * @param packetParam
+     * @param param
+     * @param amount
+     * @param log
+     * @return
+     */
+    private R processRedPacket(CourseConfig config, WxSendRedPacketParam packetParam, FsCourseSendRewardUParam param, BigDecimal amount, FsCourseWatchLog log) {
+        R sendRedPacket = paymentService.sendRedPacket(packetParam);
+
+        if (!sendRedPacket.get("code").equals(200)) {
+            return R.error("奖励发送失败,请联系客服");
+        }
+
+        createRedPacketLog(sendRedPacket, param, amount, log);
+        updateWatchLogRewardType(log, config);
+
+        return sendRedPacket;
+    }
+
+    private void createRedPacketLog(R sendRedPacket, FsCourseSendRewardUParam param, BigDecimal amount, FsCourseWatchLog log) {
+        FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
+
+        // Set common fields
+        redPacketLog.setCourseId(param.getCourseId());
+        redPacketLog.setCompanyId(param.getCompanyId());
+        redPacketLog.setUserId(param.getUserId());
+        redPacketLog.setVideoId(param.getVideoId());
+        redPacketLog.setStatus(0);
+        redPacketLog.setQwUserId(param.getQwUserId());
+        redPacketLog.setCompanyUserId(param.getCompanyUserId());
+        redPacketLog.setCreateTime(new Date());
+        redPacketLog.setAmount(amount);
+        redPacketLog.setWatchLogId(log != null ? log.getLogId() : null);
+        redPacketLog.setPeriodId(param.getPeriodId());
+        redPacketLog.setAppId(param.getAppId());
+
+        // Set batch number based on isNew flag
+        if (sendRedPacket.get("isNew").equals(1)) {
+            TransferBillsResult transferBillsResult = (TransferBillsResult) sendRedPacket.get("data");
+            redPacketLog.setResult(JSON.toJSONString(sendRedPacket));
+            redPacketLog.setOutBatchNo(transferBillsResult.getOutBillNo());
+        } else {
+            redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
+        }
+
+        redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
+    }
+
+    private void updateWatchLogRewardType(FsCourseWatchLog log, CourseConfig config) {
+        if (log != null) {
+            log.setRewardType(config.getRewardType());
+            courseWatchLogMapper.updateFsCourseWatchLog(log);
+        }
+    }
     /**
      * 获取用户openId
      *

+ 1 - 0
fs-service/src/main/resources/mapper/course/FsCourseRedPacketLogMapper.xml

@@ -38,6 +38,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="companyUserId != null "> and company_user_id = #{companyUserId}</if>
             <if test="companyId != null "> and company_id = #{companyId}</if>
             <if test="amount != null "> and amount = #{amount}</if>
+            <if test="status != null "> and `status` = #{status}</if>
             <if test="qwUserId != null  and qwUserId != ''"> and qw_user_id = #{qwUserId}</if>
         </where>
     </select>