浏览代码

迁移发放奖励,调整为:{红包、积分、转盘,随机转盘}

yfh 3 周之前
父节点
当前提交
aadfaf3267

+ 1 - 1
fs-quartz/src/main/java/com/fs/quartz/config/ScheduleConfig.java

@@ -49,7 +49,7 @@ public class ScheduleConfig
         // 可选,QuartzScheduler
         // 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
         factory.setOverwriteExistingJobs(true);
-        // 设置自动启动,默认为true 切记调整为true
+        // 设置自动启动,默认为true 切记调整为true1
         factory.setAutoStartup(true);
 //        factory.setAutoStartup(false);
 

+ 63 - 0
fs-service/src/main/java/com/fs/course/domain/FsCourseReward.java

@@ -0,0 +1,63 @@
+package com.fs.course.domain;
+
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 奖励配置对象 fs_course_reward
+ *
+ * @author 杨衍生
+ * @date 2025-09-02
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FsCourseReward extends BaseEntity{
+
+    /** 主键ID */
+    private Long id;
+
+    /** 奖励名称 */
+    @Excel(name = "奖励名称")
+    private String name;
+
+    /** 奖励描述 */
+    @Excel(name = "奖励描述")
+    private String description;
+
+    /** 奖励类型 (1:宝箱, 2:红包, 3:积分, 4:转盘, 5:保底转盘) */
+    @Excel(name = "奖励类型 (1:宝箱, 2:红包, 3:积分, 4:转盘, 5:保底转盘)")
+    private Long rewardType;
+
+    /** 状态 (0:禁用, 1:启用) */
+    @Excel(name = "状态 (0:禁用, 1:启用)")
+    private Long status;
+
+    /** 期望值 */
+    @Excel(name = "期望值")
+    private String expectedValue;
+
+    /** 创建人ID */
+    @Excel(name = "创建人ID")
+    private Long createId;
+
+    /** 实际获得的奖励内容 */
+    @Excel(name = "实际获得的奖励内容")
+    private String actualRewards;
+
+    /**
+     * 关闭宝箱url
+     */
+    private String closeChestUrl;
+
+    /**
+     * 开启宝箱url
+     */
+    private String openChestUrl;
+
+    /**
+     * 奖励id
+     */
+    private Long [] rewardIds;
+}

+ 79 - 0
fs-service/src/main/java/com/fs/course/domain/FsCourseRewardRound.java

@@ -0,0 +1,79 @@
+package com.fs.course.domain;
+
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 奖励领取记录对象 reward_round
+ *
+ * @author 杨衍生
+ * @date 2025-09-02
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FsCourseRewardRound extends BaseEntity{
+
+    /** 主键ID */
+    private Long id;
+
+    /** 奖励ID */
+    @Excel(name = "奖励ID")
+    private Long rewardId;
+
+    /** 领取用户ID */
+    @Excel(name = "领取用户ID")
+    private Long userId;
+    private String userIdByName;
+
+    /** 奖励类型 */
+    @Excel(name = "奖励类型")
+    private Long rewardType;
+
+    /** 公司ID */
+    @Excel(name = "公司ID")
+    private Long companyId;
+    private String companyName;
+
+    /** 实际领取到的奖励 */
+    @Excel(name = "实际领取到的奖励")
+    private String actualRewards;
+
+    /** 创建人ID */
+    @Excel(name = "创建人ID")
+    private Long createId;
+
+    /** 奖励状态 (0:已作废, 1:已领取, 2:已过期) */
+    @Excel(name = "奖励状态 (0:已作废, 1:已领取, 2:已过期)")
+    private Long status;
+
+    /** 规则id */
+    @Excel(name = "规则id")
+    private String ruleId;
+
+    /** 看课记录id */
+    @Excel(name = "看课记录id")
+    private Long watchId;
+
+    /** 奖励规则关联id */
+    @Excel(name = "奖励规则关联id")
+    private Long rewardVideoRelationId;
+    /**
+     * 时长
+     */
+    private String second;
+    /**
+     * 小节id
+     */
+    private Long videoId;
+    private String qwUserId;
+    private Long qwExternalId;
+
+    private Integer linkType;
+
+    /**
+     * 奖励商品ID
+     */
+    private Long goodsId;
+}

+ 47 - 0
fs-service/src/main/java/com/fs/course/domain/FsCourseRewardVideoRelation.java

@@ -0,0 +1,47 @@
+package com.fs.course.domain;
+
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 奖励与视频小节关联关系对象 fs_course_reward_video_relation
+ *
+ * @author fs
+ * @date 2025-09-03
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FsCourseRewardVideoRelation extends BaseEntity{
+
+    /** 关联关系ID */
+    private Long id;
+
+    /** 关联reward_.id */
+    @Excel(name = "关联reward_.id")
+    private Long rewardId;
+
+    /** 关联视频小节ID */
+    @Excel(name = "关联视频小节ID")
+    private Long videoSectionId;
+
+    /** 标志百分比 */
+    @Excel(name = "标志百分比")
+    private String mark;
+
+
+    /** 创建人ID */
+    @Excel(name = "创建人ID")
+    private Long createId;
+
+    /** 创建人ID */
+    @Excel(name = "创建人ID")
+    private Long updateId;
+    /** 公司ID */
+    @Excel(name = "公司ID")
+    private Long companyId;
+
+    private Integer type;
+
+}

+ 68 - 0
fs-service/src/main/java/com/fs/course/mapper/FsCourseRewardMapper.java

@@ -0,0 +1,68 @@
+package com.fs.course.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.course.domain.FsCourseReward;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 奖励配置Mapper接口
+ *
+ * @author 杨衍生
+ * @date 2025-09-02
+ */
+public interface FsCourseRewardMapper extends BaseMapper<FsCourseReward>{
+    /**
+     * 查询奖励配置
+     *
+     * @param id 奖励配置主键
+     * @return 奖励配置
+     */
+    FsCourseReward selectFsCourseRewardById(Long id);
+
+    /**
+     * 查询奖励配置列表
+     *
+     * @param reward 奖励配置
+     * @return 奖励配置集合
+     */
+    List<FsCourseReward> selectFsCourseRewardList(FsCourseReward reward);
+
+    /**
+     * 新增奖励配置
+     *
+     * @param reward 奖励配置
+     * @return 结果
+     */
+    int insertFsCourseReward(FsCourseReward reward);
+
+    /**
+     * 修改奖励配置
+     *
+     * @param reward 奖励配置
+     * @return 结果
+     */
+    int updateFsCourseReward(FsCourseReward reward);
+
+    /**
+     * 删除奖励配置
+     *
+     * @param id 奖励配置主键
+     * @return 结果
+     */
+    int deleteFsCourseRewardById(Long id);
+
+    /**
+     * 批量删除奖励配置
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteFsCourseRewardByIds(Long[] ids);
+
+    /**
+     * 根据id集合查询奖励配置
+     */
+    List<FsCourseReward> selectFsCourseRewardByIds(@Param("ids") List<Long> ids);
+}

+ 75 - 0
fs-service/src/main/java/com/fs/course/mapper/FsCourseRewardRoundMapper.java

@@ -0,0 +1,75 @@
+package com.fs.course.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.company.vo.RedPacketMoneyVO;
+import com.fs.course.domain.FsCourseRewardRound;
+import org.apache.ibatis.annotations.Param;
+
+import java.time.LocalDate;
+import java.util.List;
+
+/**
+ * 奖励领取记录Mapper接口
+ *
+ * @author 杨衍生
+ * @date 2025-09-02
+ */
+public interface FsCourseRewardRoundMapper extends BaseMapper<FsCourseRewardRound>{
+    /**
+     * 查询奖励领取记录
+     *
+     * @param id 奖励领取记录主键
+     * @return 奖励领取记录
+     */
+    FsCourseRewardRound selectFsCourseRewardRoundById(Long id);
+
+    /**
+     * 查询奖励领取记录列表
+     *
+     * @param rewardRound 奖励领取记录
+     * @return 奖励领取记录集合
+     */
+    List<FsCourseRewardRound> selectFsCourseRewardRoundList(FsCourseRewardRound rewardRound);
+
+    /**
+     * 新增奖励领取记录
+     *
+     * @param rewardRound 奖励领取记录
+     * @return 结果
+     */
+    int insertFsCourseRewardRound(FsCourseRewardRound rewardRound);
+
+    /**
+     * 修改奖励领取记录
+     *
+     * @param rewardRound 奖励领取记录
+     * @return 结果
+     */
+    int updateFsCourseRewardRound(FsCourseRewardRound rewardRound);
+
+    /**
+     * 删除奖励领取记录
+     *
+     * @param id 奖励领取记录主键
+     * @return 结果
+     */
+    int deleteFsCourseRewardRoundById(Long id);
+
+    /**
+     * 批量删除奖励领取记录
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteFsCourseRewardRoundByIds(Long[] ids);
+
+    List<RedPacketMoneyVO> selectFsCourseRewardRoundByAmount(@Param("start") String start,@Param("end") String end);
+
+    List<RedPacketMoneyVO> selectFsCourseRewardRoundByAmountForLuckyBag(@Param("start") String start,@Param("end") String end);
+
+    /**
+     * 查询1000条指定状态且小于指定时间的数据
+     */
+    List<FsCourseRewardRound> get1kByStatusAndLtDate(@Param("status") int status, @Param("endTime") LocalDate endTime);
+
+}

+ 87 - 0
fs-service/src/main/java/com/fs/course/mapper/FsCourseRewardVideoRelationMapper.java

@@ -0,0 +1,87 @@
+package com.fs.course.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.course.domain.FsCourseReward;
+import com.fs.course.domain.FsCourseRewardVideoRelation;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+
+/**
+ * 奖励与视频小节关联关系Mapper接口
+ *
+ * @author fs
+ * @date 2025-09-03
+ */
+public interface FsCourseRewardVideoRelationMapper extends BaseMapper<FsCourseRewardVideoRelation>{
+    /**
+     * 查询奖励与视频小节关联关系
+     *
+     * @param id 奖励与视频小节关联关系主键
+     * @return 奖励与视频小节关联关系
+     */
+    FsCourseRewardVideoRelation selectFsCourseRewardVideoRelationById(Long id);
+
+    /**
+     * 查询奖励与视频小节关联关系列表
+     *
+     * @param fsCourseRewardVideoRelation 奖励与视频小节关联关系
+     * @return 奖励与视频小节关联关系集合
+     */
+    List<FsCourseRewardVideoRelation> selectFsCourseRewardVideoRelationList(FsCourseRewardVideoRelation fsCourseRewardVideoRelation);
+
+    /**
+     * 新增奖励与视频小节关联关系
+     *
+     * @param fsCourseRewardVideoRelation 奖励与视频小节关联关系
+     * @return 结果
+     */
+    int insertFsCourseRewardVideoRelation(FsCourseRewardVideoRelation fsCourseRewardVideoRelation);
+
+    /**
+     * 修改奖励与视频小节关联关系
+     *
+     * @param fsCourseRewardVideoRelation 奖励与视频小节关联关系
+     * @return 结果
+     */
+    int updateFsCourseRewardVideoRelation(FsCourseRewardVideoRelation fsCourseRewardVideoRelation);
+
+    /**
+     * 删除奖励与视频小节关联关系
+     *
+     * @param id 奖励与视频小节关联关系主键
+     * @return 结果
+     */
+    int deleteFsCourseRewardVideoRelationById(Long id);
+
+    /**
+     * 批量删除奖励与视频小节关联关系
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteFsCourseRewardVideoRelationByIds(Long[] ids);
+
+    /**
+     * 根据公司ID、小节ID和类型删除关系
+     */
+    void deleteByTypes(@Param("videoId") Long videoId, @Param("companyId") Long companyId, @Param("types") List<Integer> types);
+
+    /**
+     * 根据公司ID、小节ID和类型查询奖励ID
+     */
+    Long getRewardIdByCompanyIdAndVideoIdAndType(@Param("companyId") Long companyId, @Param("videoId") Long videoId, @Param("type") int type);
+
+    /**
+     * 根据公司ID、小节ID和类型查询奖励配置
+     */
+    FsCourseReward getRewardByCompanyIdAndVideoIdAndType(@Param("companyId") Long companyId, @Param("videoId") Long videoId, @Param("type") Integer type);
+
+    List<FsCourseRewardVideoRelation> selectFsCourseRewardVideoRelationListByType(FsCourseRewardVideoRelation fsCourseRewardVideoRelation);
+
+    /**
+     * 根据公司ID、小节ID和奖励ID查询配置关系
+     */
+    FsCourseRewardVideoRelation selectByCompanyIdAndVideoIdAndRewardId(@Param("companyId") Long companyId, @Param("videoId") Long videoId, @Param("rewardId") Long rewardId);
+}

+ 1 - 1
fs-service/src/main/java/com/fs/course/param/FsCourseSendRewardUParam.java

@@ -36,7 +36,7 @@ public class FsCourseSendRewardUParam implements Serializable
     private Long periodId;
     @NotBlank(message = "小程序参数不能为空")
     private String appId; //前端传来的小程序的appid
-
+    private Integer rewardType; //奖励类型 1红包 2积分 3随机转盘 4保底转盘
     private String code;
 
 }

+ 238 - 17
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -49,16 +49,14 @@ import com.fs.course.service.IFsCourseLinkService;
 import com.fs.course.service.IFsUserCompanyBindService;
 import com.fs.course.service.IFsUserCompanyUserService;
 import com.fs.course.service.IFsUserCourseVideoService;
+import com.fs.course.utils.luckyDraw.LotteryUtil;
+import com.fs.course.utils.luckyDraw.Prize;
 import com.fs.course.vo.*;
 import com.fs.course.vo.newfs.*;
 import com.fs.enums.ExceptionCodeEnum;
 import com.fs.his.config.AppConfig;
-import com.fs.his.domain.FsUser;
-import com.fs.his.domain.FsUserIntegralLogs;
-import com.fs.his.domain.FsUserWx;
-import com.fs.his.mapper.FsPackageMapper;
-import com.fs.his.mapper.FsUserIntegralLogsMapper;
-import com.fs.his.mapper.FsUserMapper;
+import com.fs.his.domain.*;
+import com.fs.his.mapper.*;
 import com.fs.his.param.WxSendRedPacketParam;
 import com.fs.his.service.IFsStorePaymentService;
 import com.fs.his.service.IFsUserIntegralLogsService;
@@ -76,11 +74,9 @@ import com.fs.qw.mapper.*;
 import com.fs.qw.param.FsUserCourseRedPageParam;
 import com.fs.qw.service.IQwCompanyService;
 import com.fs.qw.service.IQwExternalContactService;
-import com.fs.qw.vo.QwSopCourseFinishTempSetting;
 import com.fs.qwApi.Result.QwAddContactWayResult;
 import com.fs.qwApi.param.QwAddContactWayParam;
 import com.fs.qwApi.service.QwApiService;
-import com.fs.sop.domain.QwSopLogs;
 import com.fs.sop.domain.SopUserLogsInfo;
 import com.fs.sop.mapper.SopUserLogsInfoMapper;
 import com.fs.sop.service.ISopUserLogsInfoService;
@@ -106,7 +102,6 @@ import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.bind.annotation.RequestBody;
-
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.time.*;
@@ -136,6 +131,14 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
     @Autowired
     private OpenIMService openIMService;
     @Autowired
+    private FsCouponMapper fsCouponMapper;
+    @Autowired
+    private FsUserCouponMapper fsUserCouponMapper;
+    @Autowired
+    private FsCourseRewardVideoRelationMapper videoRelationMapper;
+    @Autowired
+    private FsCourseRewardRoundMapper roundMapper;
+    @Autowired
     private LuckyBagMapper luckyBagMapper;
     @Autowired
     private LuckyBagCollectRecordMapper luckyBagCollectRecordMapper;
@@ -4952,9 +4955,11 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
         if (sourceApp == param.getSource() /*&& !CloudHostUtils.hasCloudHostName("中康")*/) {
             return sendIntegralReward(param, user, log, config);
         }
-
+        if (ObjectUtils.isEmpty(param.getRewardType())){
+            param.setRewardType(config.getRewardType());
+        }
         // 根据奖励类型发放不同奖励
-        switch (config.getRewardType()) {
+        switch (param.getRewardType()) {
             // 红包奖励
             case 1:
                 //来源是小程序切换openId
@@ -4971,18 +4976,234 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
             // 积分奖励
             case 2:
                 return sendIntegralReward(param, user, log, config);
-            // 红包+积分
+            // 转盘
             case 3:
-                R sendRed = sendRedPacketRewardFsUser(param, user, log, video, config);
-                if (!Objects.equals(sendRed.get("code"), 200)) {
-                    return sendRed;
-                }
-                return sendIntegralReward(param, user, log, config);
+                return drawTurntable(param, user, log);
+            // 保底转盘
+            case 4:
+                return drawTurntableGuarantee(param, user, log);
             default:
                 return R.error("参数错误!");
         }
     }
+    private R drawTurntable(FsCourseSendRewardUParam param, FsUser user, FsCourseWatchLog watchLog) {
+        log.debug("转盘抽奖 param: {}, user: {}, watchLog: {}",
+                JSON.toJSONString(param),JSON.toJSONString(user),JSON.toJSONString(watchLog));
+        return draw(param, user, watchLog, 4);
+    }
+
+    /**
+     * 保底转盘
+     */
+    private R drawTurntableGuarantee(FsCourseSendRewardUParam param, FsUser user, FsCourseWatchLog watchLog) {
+        log.debug("保底转盘 param: {}, user: {}, watchLog: {}",
+                JSON.toJSONString(param),JSON.toJSONString(user),JSON.toJSONString(watchLog));
+        return draw(param, user, watchLog, 5);
+    }
+
+
+    /**
+     * 抽奖
+     */
+    private R draw(FsCourseSendRewardUParam param, FsUser user, FsCourseWatchLog watchLog, Integer drawType) {
+        FsCourseReward rewardConfig = videoRelationMapper.getRewardByCompanyIdAndVideoIdAndType(watchLog.getCompanyId(), watchLog.getVideoId(), drawType);
+        String typeName = drawType == 4 ? "转盘" : "保底转盘";
+        if (Objects.isNull(rewardConfig)) {
+            throw new CustomException("销售公司未配置"+ typeName +",请选择其他奖励类型");
+        }
 
+        if (StringUtils.isBlank(rewardConfig.getActualRewards())) {
+            throw new CustomException(typeName + "配置错误1,请联系管理员");
+        }
+
+        // 解析JSON配置
+        JSONArray jsonArray;
+        try {
+            jsonArray = JSON.parseArray(rewardConfig.getActualRewards());
+        } catch (Exception e) {
+            log.error("解析{}配置JSON失败", typeName, e);
+            throw new CustomException(typeName + "配置错误:JSON格式不正确");
+        }
+
+        if (jsonArray == null || jsonArray.isEmpty()) {
+            throw new CustomException(typeName + "配置错误:奖品列表为空");
+        }
+
+        // 配置关系
+        FsCourseRewardVideoRelation relation = videoRelationMapper.selectByCompanyIdAndVideoIdAndRewardId(watchLog.getCompanyId(), watchLog.getVideoId(), rewardConfig.getId());
+
+        List<Prize> prizes = buildPrizes(jsonArray, param, rewardConfig.getId(), drawType);
+        Prize prize = LotteryUtil.draw(prizes);
+        if (Objects.isNull(prize)) {
+            throw new CustomException(typeName + "无可抽取奖励");
+        }
+
+        FsCourseRewardRound rewardRound = new FsCourseRewardRound();
+        rewardRound.setRewardId(rewardConfig.getId());
+        rewardRound.setUserId(user.getUserId());
+        rewardRound.setRewardType(rewardConfig.getRewardType());
+        rewardRound.setCompanyId(watchLog.getCompanyId());
+        rewardRound.setActualRewards(prize.getAmount());
+        rewardRound.setCreateTime(new Date());
+        rewardRound.setStatus(1L);
+        rewardRound.setWatchId(watchLog.getLogId());
+        rewardRound.setRuleId(prize.getCode());
+        rewardRound.setRewardVideoRelationId(relation.getId());
+
+        // 抽中奖励商品
+        if (prize.getType() == 5) {
+            rewardRound.setGoodsId(Long.parseLong(prize.getGoodsId()));
+        }
+
+        roundMapper.insertFsCourseRewardRound(rewardRound);
+
+        // 获取配置信息
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        FsUserCourseVideo video = fsUserCourseVideoMapper.selectFsUserCourseVideoByVideoId(param.getVideoId());
+        switch (prize.getType()) {
+            case 1:
+                //来源是小程序切换openId
+                WxSendRedPacketParam packetParam = new WxSendRedPacketParam();
+                String openId = getOpenId(param, user);
+                if (StringUtils.isBlank(openId)) {
+                    return R.error("请重新使用微信登录");
+                }
+                packetParam.setOpenId(openId);
+                packetParam.setAmount(new BigDecimal(prize.getAmount()));
+                packetParam.setSource(param.getSource());
+                packetParam.setAppId(param.getAppId());
+                return sendAppRedPacket(packetParam, watchLog,video, config);
+            case 2:
+                return sendIntegralReward(param, user, watchLog, config);
+            case 3:
+                return R.ok().put("data", prize.getCode());
+            case 4:
+                return sendCoupon(param, prize.getCouponId(), Integer.parseInt(prize.getAmount())).put("data", prize.getCode());
+            case 5:
+                Map<String, Object> result = new HashMap<>();
+                result.put("roundId", rewardRound.getId());
+                result.put("goodsId", prize.getGoodsId());
+                result.put("data", prize.getCode());
+                return R.ok(result);
+            default:
+                return R.error(typeName + "配置错误4,请联系管理员");
+        }
+    }
+    /**
+     * 发送优惠券
+     */
+    private R sendCoupon(FsCourseSendRewardUParam param, String couponId, Integer num) {
+        log.debug("发送优惠券 param: {}, couponId: {}, num: {}", JSON.toJSONString(param), couponId, num);
+
+        FsCoupon coupon = fsCouponMapper.selectFsCouponByCouponId(Long.parseLong(couponId));
+        //不存在
+        if (coupon == null) {
+            return R.error("优惠券不存在");
+        }
+        //停用
+        if (coupon.getStatus()==0) {
+            return R.error("优惠券不存在");
+        }
+
+        FsUserCoupon fsUserCoupon = new FsUserCoupon();
+        fsUserCoupon.setCouponId(coupon.getCouponId());
+        fsUserCoupon.setCouponCode("C"+System.currentTimeMillis());
+        fsUserCoupon.setUserId(param.getUserId());
+        fsUserCoupon.setCreateTime(DateUtils.getNowDate());
+        if (coupon.getLimitType() == 2){
+            long limitDay = coupon.getLimitDay().longValue() * 24 * 60 * 60 * 1000;
+            long time = new Date().getTime();
+            fsUserCoupon.setLimitTime(new Date(limitDay+time));
+        }else {
+            fsUserCoupon.setLimitTime(coupon.getLimitTime());
+        }
+        fsUserCoupon.setStatus(0);
+        fsUserCouponMapper.insertFsUserCoupon(fsUserCoupon);
+        return R.ok("奖励发放成功");
+    }
+
+    /**
+     * 构建奖品列表
+     */
+    private List<Prize> buildPrizes(JSONArray jsonArray, FsCourseSendRewardUParam param, Long rewardId, Integer drawType) {
+        List<Prize> prizes = new ArrayList<>();
+
+        // 如果是保底转盘,需要查询已领取记录
+        List<FsCourseRewardRound> rounds = new ArrayList<>();
+        if (drawType == 5) { // 保底转盘
+            FsCourseRewardRound query = new FsCourseRewardRound();
+            query.setUserId(param.getUserId());
+            query.setCompanyId(param.getCompanyId());
+            query.setRewardId(rewardId);
+            rounds = roundMapper.selectFsCourseRewardRoundList(query);
+        }
+
+        Set<String> claimedCodes = rounds.stream()
+                .map(FsCourseRewardRound::getRuleId)
+                .collect(Collectors.toSet());
+
+        for (Object o : jsonArray) {
+            try {
+                JSONObject json = (JSONObject) o;
+
+                Integer type = json.getInteger("type");
+                String amount = json.getString("amount");
+                String code = json.getString("code");
+                String couponId = json.getString("couponId");
+
+                // 安全解析概率
+                double probability = parseProbability(json.getString("probability"));
+                if (probability <= 0) {
+                    log.warn("奖品概率配置错误,跳过: {}", json);
+                    continue;
+                }
+
+                // 保底转盘特殊逻辑
+                if (drawType == 5) {
+                    // 跳过已领取的
+                    if (claimedCodes.contains(code)) {
+                        continue;
+                    }
+
+                    // 保底奖品逻辑
+                    Boolean isGuarantee = json.getBoolean("isGuarantee");
+                    if (Boolean.TRUE.equals(isGuarantee) && jsonArray.size() - rounds.size() > 5) {
+                        continue;
+                    }
+                }
+
+                // APP跳过现金红包
+                if (param.getSource() == 3 && type == 1) {
+                    continue;
+                }
+
+                prizes.add(new Prize(type, amount, code, probability, couponId, null));
+
+            } catch (Exception e) {
+                log.warn("解析奖品配置失败,跳过: {}", JSON.toJSONString(o), e);
+            }
+        }
+
+        return prizes;
+    }
+    /**
+     * 安全解析概率值
+     */
+    private double parseProbability(String probabilityStr) {
+        try {
+            if (StringUtils.isBlank(probabilityStr)) {
+                return -1;
+            }
+
+            // 移除百分号并转换
+            String cleanStr = probabilityStr.replace("%", "").trim();
+            return Double.parseDouble(cleanStr);
+        } catch (NumberFormatException e) {
+            log.error("概率格式错误: {}", probabilityStr, e);
+            return -1;
+        }
+    }
 
     private R sendAppRedPacket(WxSendRedPacketParam packetParam,FsCourseWatchLog log,FsUserCourseVideo video,CourseConfig config) {
         FsUserCoursePeriodDays periodDays = new FsUserCoursePeriodDays();

+ 37 - 0
fs-service/src/main/java/com/fs/course/utils/luckyDraw/LotteryUtil.java

@@ -0,0 +1,37 @@
+package com.fs.course.utils.luckyDraw;
+
+import java.util.List;
+import java.util.concurrent.ThreadLocalRandom;
+
+public class LotteryUtil {
+
+    /**
+     * 根据奖品配置抽奖
+     *
+     * @param prizes 奖品列表
+     * @return 中奖的奖品
+     */
+    public static Prize draw(List<Prize> prizes) {
+        if (prizes == null || prizes.isEmpty()) {
+            return null;
+        }
+
+        // 计算总概率和
+        double total = prizes.stream().mapToDouble(Prize::getProbability).sum();
+
+        // [0, total) 之间取随机数
+        double random = ThreadLocalRandom.current().nextDouble() * total;
+
+        // 逐步累加找到落点
+        double sum = 0;
+        for (Prize prize : prizes) {
+            sum += prize.getProbability();
+            if (random < sum) {
+                return prize;
+            }
+        }
+
+        // 理论上不会到这里
+        return prizes.get(prizes.size() - 1);
+    }
+}

+ 77 - 0
fs-service/src/main/java/com/fs/course/utils/luckyDraw/Prize.java

@@ -0,0 +1,77 @@
+package com.fs.course.utils.luckyDraw;
+
+
+public class Prize {
+
+    private Integer type;
+    private String amount;
+    private String code;
+    private double probability;
+    private String couponId;
+    private String goodsId;
+
+    public Prize(Integer type, String amount, String code, double probability, String couponId, String goodsId) {
+        this.type = type;
+        this.amount = amount;
+        this.code = code;
+        this.probability = probability;
+        this.couponId = couponId;
+        this.goodsId = goodsId;
+    }
+
+    public Prize(Integer type, String amount, String code, double probability, String couponId) {
+        this.type = type;
+        this.amount = amount;
+        this.code = code;
+        this.probability = probability;
+        this.couponId = couponId;
+    }
+
+    public Integer getType() {
+        return type;
+    }
+
+    public void setType(Integer type) {
+        this.type = type;
+    }
+
+    public String getAmount() {
+        return amount;
+    }
+
+    public void setAmount(String amount) {
+        this.amount = amount;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public double getProbability() {
+        return probability;
+    }
+
+    public void setProbability(double probability) {
+        this.probability = probability;
+    }
+
+    public String getCouponId() {
+        return couponId;
+    }
+
+    public void setCouponId(String couponId) {
+        this.couponId = couponId;
+    }
+
+    public String getGoodsId() {
+        return goodsId;
+    }
+
+    public void setGoodsId(String goodsId) {
+        this.goodsId = goodsId;
+    }
+}

+ 0 - 3
fs-service/src/main/java/com/fs/tzBankPay/TzBankService/TzBankServiceImpl/TzBankServiceImpl.java

@@ -74,9 +74,6 @@ public class TzBankServiceImpl implements TzBankService {
         return messageInfo;
     }
 
-
-
-
     //退款
     public TzBankResult<RefundResult> refund(RefundParam tzBankResult) {
         tzBankResult.setTrxSendTime(new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()));

+ 1 - 0
fs-service/src/main/java/com/fs/tzBankPay/doman/PayCreateOrder.java

@@ -33,5 +33,6 @@ public class PayCreateOrder {
     private String identityName; // 用户真实名称 银联签约支付时需要上送
     private String openId;
     private String orderId;
+    private String appId;
     private Integer orderType;//1问诊 2药品 3套餐包 4课程订单 5开通会员 6积分商城
 }

+ 1 - 0
fs-service/src/main/java/com/fs/tzBankPay/doman/PayType.java

@@ -9,6 +9,7 @@ public enum PayType {
     E_PURCHASE_LOAN_PAYMENT("08"),
     DIGITAL_RMB_PAYMENT("09"),
     WECHAT_MINI_PROGRAM_PAYMENT("14"),
+    APP_WECHAT_ACCOUNT("11"),
     WECHAT_PUBLIC_ACCOUNT("15"),
     WECHAT_QR_CODE_PAYMENT("16"),
     ALIPAY_BARCODE_PAYMENT("27"),

+ 138 - 0
fs-service/src/main/resources/mapper/course/FsCourseRewardVideoRelationMapper.xml

@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.course.mapper.FsCourseRewardVideoRelationMapper">
+
+    <resultMap type="FsCourseRewardVideoRelation" id="FsCourseRewardVideoRelationResult">
+        <result property="id"    column="id"    />
+        <result property="rewardId"    column="reward_id"    />
+        <result property="videoSectionId"    column="video_section_id"    />
+        <result property="mark"    column="mark"    />
+        <result property="createId"    column="create_id"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateId"    column="update_id"    />
+        <result property="updateTime"    column="update_time"    />
+        <result property="companyId"    column="company_id"    />
+    </resultMap>
+
+    <sql id="selectFsCourseRewardVideoRelationVo">
+        select id, reward_id, company_id,video_section_id, mark, create_id, create_time, update_id, update_time from fs_course_reward_video_relation
+    </sql>
+
+    <select id="selectFsCourseRewardVideoRelationList" parameterType="FsCourseRewardVideoRelation" resultMap="FsCourseRewardVideoRelationResult">
+        <include refid="selectFsCourseRewardVideoRelationVo"/>
+        <where>
+            <if test="rewardId != null  and rewardId != ''"> and reward_id = #{rewardId}</if>
+            <if test="videoSectionId != null  and videoSectionId != ''"> and video_section_id = #{videoSectionId}</if>
+            <if test="mark != null "> and mark = #{mark}</if>
+            <if test="createId != null "> and create_id = #{createId}</if>
+            <if test="updateId != null "> and update_id = #{updateId}</if>
+        </where>
+    </select>
+
+    <select id="selectFsCourseRewardVideoRelationById" parameterType="String" resultMap="FsCourseRewardVideoRelationResult">
+        <include refid="selectFsCourseRewardVideoRelationVo"/>
+        where id = #{id}
+    </select>
+
+    <select id="getRewardIdByCompanyIdAndVideoIdAndType" resultType="java.lang.Long">
+        select cr.id
+        from fs_course_reward_video_relation crvr
+        inner join fs_course_reward cr on cr.id = crvr.reward_id and cr.reward_type = #{type}
+        where crvr.company_id = #{companyId} and crvr.video_section_id = #{videoId} and cr.status = 1
+        limit 1
+    </select>
+
+    <select id="getRewardByCompanyIdAndVideoIdAndType" resultType="com.fs.course.domain.FsCourseReward">
+        select cr.*
+        from fs_course_reward cr
+        inner join fs_course_reward_video_relation crvr on cr.id = crvr.reward_id
+        where crvr.company_id = #{companyId} and crvr.video_section_id = #{videoId} and cr.reward_type = #{type} and cr.status = 1
+        limit 1
+    </select>
+
+    <insert id="insertFsCourseRewardVideoRelation" parameterType="FsCourseRewardVideoRelation" useGeneratedKeys="true" keyProperty="id">
+        insert into fs_course_reward_video_relation
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="rewardId != null and rewardId != ''">reward_id,</if>
+            <if test="videoSectionId != null and videoSectionId != ''">video_section_id,</if>
+            <if test="mark != null">mark,</if>
+            <if test="createId != null">create_id,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateId != null">update_id,</if>
+            <if test="updateTime != null">update_time,</if>
+            <if test="companyId != null">company_id,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="rewardId != null and rewardId != ''">#{rewardId},</if>
+            <if test="videoSectionId != null and videoSectionId != ''">#{videoSectionId},</if>
+            <if test="mark != null">#{mark},</if>
+            <if test="createId != null">#{createId},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateId != null">#{updateId},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+            <if test="companyId != null">#{companyId},</if>
+         </trim>
+    </insert>
+
+    <update id="updateFsCourseRewardVideoRelation" parameterType="FsCourseRewardVideoRelation">
+        update fs_course_reward_video_relation
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="rewardId != null and rewardId != ''">reward_id = #{rewardId},</if>
+            <if test="videoSectionId != null and videoSectionId != ''">video_section_id = #{videoSectionId},</if>
+            <if test="mark != null">mark = #{mark},</if>
+            <if test="createId != null">create_id = #{createId},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="updateId != null">update_id = #{updateId},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+            <if test="companyId != null">company_id = #{companyId},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteFsCourseRewardVideoRelationById" parameterType="String">
+        delete from fs_course_reward_video_relation where id = #{id}
+    </delete>
+
+    <delete id="deleteFsCourseRewardVideoRelationByIds" parameterType="String">
+        delete from fs_course_reward_video_relation where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+    <delete id="deleteByTypes">
+        delete crvr from fs_course_reward_video_relation crvr
+        inner join fs_course_reward cr on cr.id = crvr.reward_id
+        where crvr.video_section_id = #{videoId} and crvr.company_id = #{companyId}
+        and cr.reward_type in
+        <foreach item="type" collection="types" open="(" separator="," close=")">
+            #{type}
+        </foreach>
+    </delete>
+
+
+    <select id="selectFsCourseRewardVideoRelationListByType" resultMap="FsCourseRewardVideoRelationResult">
+        select crvr.id, crvr.reward_id, crvr.company_id,crvr.video_section_id, crvr.mark, crvr.create_id, crvr.create_time, crvr.update_id, crvr.update_time
+        from fs_course_reward_video_relation crvr
+                 inner join fs_course_reward cr on cr.id = crvr.reward_id
+            <where>
+                <if test="rewardId != null  and rewardId != ''"> and crvr.reward_id = #{rewardId}</if>
+                <if test="type != null  and type != ''">   and cr.reward_type = #{type}</if>
+                <if test="videoSectionId != null  and videoSectionId != ''"> and video_section_id = #{videoSectionId}</if>
+                <if test="mark != null "> and crvr.mark = #{mark}</if>
+                <if test="companyId != null ">and crvr.company_id = #{companyId}</if>
+                <if test="createId != null "> and crvr.create_id = #{createId}</if>
+                <if test="updateId != null "> and crvr.update_id = #{updateId}</if>
+            </where>
+    </select>
+
+    <select id="selectByCompanyIdAndVideoIdAndRewardId" resultType="com.fs.course.domain.FsCourseRewardVideoRelation">
+        select
+            *
+        from fs_course_reward_video_relation
+        where company_id = #{companyId} and video_section_id = #{videoId} and reward_id = #{rewardId}
+    </select>
+
+</mapper>

+ 116 - 0
fs-service/src/main/resources/mapper/course/RewardMapper.xml

@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.course.mapper.FsCourseRewardMapper">
+
+    <resultMap type="FsCourseReward" id="FsCourseRewardResult">
+        <result property="id"    column="id"    />
+        <result property="name"    column="name"    />
+        <result property="description"    column="description"    />
+        <result property="rewardType"    column="reward_type"    />
+        <result property="status"    column="status"    />
+        <result property="expectedValue"    column="expected_value"    />
+        <result property="createId"    column="create_id"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateTime"    column="update_time"    />
+        <result property="actualRewards"    column="actual_rewards"    />
+        <result property="closeChestUrl"    column="close_chest_url"    />
+        <result property="openChestUrl"    column="open_chest_url"    />
+    </resultMap>
+
+    <sql id="selectFsCourseRewardVo">
+        select id, name, description,open_chest_url,close_chest_url, reward_type, status, expected_value, create_id, create_time, update_time, actual_rewards from fs_course_reward
+    </sql>
+
+    <select id="selectFsCourseRewardList" parameterType="FsCourseReward" resultMap="FsCourseRewardResult">
+        <include refid="selectFsCourseRewardVo"/>
+        <where>
+            <if test="name != null  and name != ''"> and name like concat('%', #{name}, '%')</if>
+            <if test="description != null  and description != ''"> and description = #{description}</if>
+            <if test="rewardType != null "> and reward_type = #{rewardType}</if>
+            <if test="status != null "> and status = #{status}</if>
+            <if test="expectedValue != null  and expectedValue != ''"> and expected_value = #{expectedValue}</if>
+            <if test="createId != null "> and create_id = #{createId}</if>
+            <if test="rewardIds != null">
+               and id in
+                <foreach item="item" collection="rewardIds" open="(" separator="," close=")">
+                    #{item}
+                </foreach>
+            </if>
+        </where>
+        order by id desc
+    </select>
+
+    <select id="selectFsCourseRewardById" parameterType="Long" resultMap="FsCourseRewardResult">
+        <include refid="selectFsCourseRewardVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertFsCourseReward" parameterType="FsCourseReward" useGeneratedKeys="true" keyProperty="id">
+        insert into fs_course_reward
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="name != null and name != ''">name,</if>
+            <if test="description != null">description,</if>
+            <if test="rewardType != null">reward_type,</if>
+            <if test="status != null">status,</if>
+            <if test="expectedValue != null and expectedValue != ''">expected_value,</if>
+            <if test="createId != null">create_id,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateTime != null">update_time,</if>
+            <if test="actualRewards != null and actualRewards != ''">actual_rewards,</if>
+            <if test="closeChestUrl != null and closeChestUrl != ''">close_chest_url,</if>
+            <if test="openChestUrl != null and openChestUrl != ''">open_chest_url,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="name != null and name != ''">#{name},</if>
+            <if test="description != null">#{description},</if>
+            <if test="rewardType != null">#{rewardType},</if>
+            <if test="status != null">#{status},</if>
+            <if test="expectedValue != null and expectedValue != ''">#{expectedValue},</if>
+            <if test="createId != null">#{createId},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+            <if test="actualRewards != null and actualRewards != ''">#{actualRewards},</if>
+            <if test="closeChestUrl != null and closeChestUrl != ''">#{closeChestUrl},</if>
+            <if test="openChestUrl != null and openChestUrl != ''">#{openChestUrl},</if>
+         </trim>
+    </insert>
+
+    <update id="updateFsCourseReward" parameterType="FsCourseReward">
+        update fs_course_reward
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="name != null and name != ''">name = #{name},</if>
+            <if test="description != null">description = #{description},</if>
+            <if test="rewardType != null">reward_type = #{rewardType},</if>
+            <if test="status != null">status = #{status},</if>
+            <if test="expectedValue != null and expectedValue != ''">expected_value = #{expectedValue},</if>
+            <if test="createId != null">create_id = #{createId},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+            <if test="actualRewards != null and actualRewards != ''">actual_rewards = #{actualRewards},</if>
+            <if test="closeChestUrl != null and closeChestUrl != ''">close_chest_url = #{closeChestUrl},</if>
+            <if test="openChestUrl != null and openChestUrl != ''">open_chest_url = #{openChestUrl},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteFsCourseRewardById" parameterType="Long">
+        delete from fs_course_reward where id = #{id}
+    </delete>
+
+    <delete id="deleteFsCourseRewardByIds" parameterType="String">
+        delete from fs_course_reward where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+    <select id="selectFsCourseRewardByIds" resultType="com.fs.course.domain.FsCourseReward">
+        <include refid="selectFsCourseRewardVo"/>
+        where id in
+        <foreach item="id" collection="ids" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </select>
+</mapper>

+ 153 - 0
fs-service/src/main/resources/mapper/course/RewardRoundMapper.xml

@@ -0,0 +1,153 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.course.mapper.FsCourseRewardRoundMapper">
+
+    <resultMap type="FsCourseRewardRound" id="FsCourseRewardRoundResult">
+        <result property="id"    column="id"    />
+        <result property="rewardId"    column="reward_id"    />
+        <result property="userId"    column="user_id"    />
+        <result property="rewardType"    column="reward_type"    />
+        <result property="companyId"    column="company_id"    />
+        <result property="actualRewards"    column="actual_rewards"    />
+        <result property="createId"    column="create_id"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateTime"    column="update_time"    />
+        <result property="status"    column="status"    />
+        <result property="rewardVideoRelationId"    column="reward_video_relation_id"    />
+        <result property="watchId"    column="watch_id"    />
+        <result property="ruleId"    column="rule_id"    />
+        <result property="goodsId"    column="goods_id"    />
+    </resultMap>
+
+    <sql id="selectFsCourseRewardRoundVo">
+        select id, reward_id, user_id, reward_type, company_id, actual_rewards, create_id, create_time, update_time,
+               status,  watch_id, rule_id, reward_video_relation_id, goods_id
+        from fs_course_reward_round
+    </sql>
+
+    <select id="selectFsCourseRewardRoundList" parameterType="FsCourseRewardRound" resultMap="FsCourseRewardRoundResult">
+        <include refid="selectFsCourseRewardRoundVo"/>
+        <where>
+            status &lt;&gt; 0
+            <if test="rewardId != null "> and reward_id = #{rewardId}</if>
+            <if test="userId != null "> and user_id = #{userId}</if>
+            <if test="rewardType != null "> and reward_type = #{rewardType}</if>
+            <if test="companyId != null "> and company_id = #{companyId}</if>
+            <if test="actualRewards != null  and actualRewards != ''"> and actual_rewards = #{actualRewards}</if>
+            <if test="createId != null "> and create_id = #{createId}</if>
+            <if test="status != null "> and status = #{status}</if>
+            <if test="rewardVideoRelationId != null "> and reward_video_relation_id = #{rewardVideoRelationId}</if>
+            <if test="watchId != null "> and watch_id = #{watchId}</if>
+            <if test="ruleId != null "> and rule_id = #{ruleId}</if>
+            <if test="params.beginTime != null and params.beginTime != ''"><!-- 开始时间检索 -->
+                and date_format(create_time,'%y%m%d') &gt;= date_format(#{params.beginTime},'%y%m%d')
+            </if>
+            <if test="params.endTime != null and params.endTime != ''"><!-- 结束时间检索 -->
+                and date_format(create_time,'%y%m%d') &lt;= date_format(#{params.endTime},'%y%m%d')
+            </if>
+        </where>
+        order by id desc
+    </select>
+
+    <select id="selectFsCourseRewardRoundById" parameterType="Long" resultMap="FsCourseRewardRoundResult">
+        <include refid="selectFsCourseRewardRoundVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertFsCourseRewardRound" parameterType="FsCourseRewardRound" useGeneratedKeys="true" keyProperty="id">
+        insert into fs_course_reward_round
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="rewardId != null">reward_id,</if>
+            <if test="userId != null">user_id,</if>
+            <if test="rewardType != null">reward_type,</if>
+            <if test="companyId != null">company_id,</if>
+            <if test="actualRewards != null and actualRewards != ''">actual_rewards,</if>
+            <if test="createId != null">create_id,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateTime != null">update_time,</if>
+            <if test="status != null">status,</if>
+            <if test="rewardVideoRelationId != null">reward_video_relation_id,</if>
+            <if test="watchId != null">watch_id,</if>
+            <if test="ruleId != null">rule_id,</if>
+            <if test="goodsId != null">goods_id,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="rewardId != null">#{rewardId},</if>
+            <if test="userId != null">#{userId},</if>
+            <if test="rewardType != null">#{rewardType},</if>
+            <if test="companyId != null">#{companyId},</if>
+            <if test="actualRewards != null and actualRewards != ''">#{actualRewards},</if>
+            <if test="createId != null">#{createId},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+            <if test="status != null">#{status},</if>
+            <if test="rewardVideoRelationId != null">#{rewardVideoRelationId},</if>
+            <if test="watchId != null">#{watchId},</if>
+            <if test="ruleId != null">#{ruleId},</if>
+            <if test="goodsId != null">#{goodsId},</if>
+         </trim>
+    </insert>
+
+    <update id="updateFsCourseRewardRound" parameterType="FsCourseRewardRound">
+        update fs_course_reward_round
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="rewardId != null">reward_id = #{rewardId},</if>
+            <if test="userId != null">user_id = #{userId},</if>
+            <if test="rewardType != null">reward_type = #{rewardType},</if>
+            <if test="companyId != null">company_id = #{companyId},</if>
+            <if test="actualRewards != null and actualRewards != ''">actual_rewards = #{actualRewards},</if>
+            <if test="createId != null">create_id = #{createId},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+            <if test="status != null">status = #{status},</if>
+            <if test="rewardVideoRelationId != null">reward_video_relation_id = #{rewardVideoRelationId},</if>
+            <if test="watchId != null">watch_id = #{watchId},</if>
+            <if test="ruleId != null">rule_id = #{ruleId},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteFsCourseRewardRoundById" parameterType="Long">
+        delete from fs_course_reward_round where id = #{id}
+    </delete>
+
+    <delete id="deleteFsCourseRewardRoundByIds" parameterType="String">
+        delete from fs_course_reward_round where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+
+    <select id="selectFsCourseRewardRoundByAmount" resultType="com.fs.company.vo.RedPacketMoneyVO">
+        select company_id ,sum(actual_rewards)/1000 as money from fs_course_reward_round
+        <where>
+            status =1 and reward_type=1
+            <if test="start != null "> and create_time &gt;= #{start} </if>
+            <if test="end != null "> and create_time &lt;= #{end}</if>
+        </where>
+        group by company_id
+    </select>
+
+
+    <select id="selectFsCourseRewardRoundByAmountForLuckyBag" resultType="com.fs.company.vo.RedPacketMoneyVO">
+        select company_id ,sum(nullif(coin_amount,0) )/1000 as money from lucky_bag_collect_record
+        <where>
+            collect_type =1
+            <if test="start != null "> and collect_time &gt;= #{start} </if>
+            <if test="end != null "> and collect_time &lt;= #{end}</if>
+        </where>
+        group by company_id
+    </select>
+
+    <select id="get1kByStatusAndLtDate" resultMap="FsCourseRewardRoundResult">
+        <include refid="selectFsCourseRewardRoundVo"/>
+        where status = #{status} and create_time &lt; #{endTime}
+        order by id asc
+        limit 1000
+    </select>
+
+
+</mapper>