Pārlūkot izejas kodu

红德堂-打卡活动转盘抽奖及积分商品免单券相关代码

Long 2 nedēļas atpakaļ
vecāks
revīzija
7cdc6f383d
90 mainītis faili ar 6043 papildinājumiem un 164 dzēšanām
  1. 5 5
      fs-admin/pom.xml
  2. 102 0
      fs-admin/src/main/java/com/fs/course/controller/FsCourseRewardController.java
  3. 123 0
      fs-admin/src/main/java/com/fs/course/controller/FsCourseRewardRoundController.java
  4. 6 8
      fs-admin/src/main/java/com/fs/his/controller/FsCouponController.java
  5. 26 0
      fs-admin/src/main/java/com/fs/his/task/RewardErpTask.java
  6. 80 0
      fs-admin/src/main/java/com/fs/reward/controller/FsRewardGoodsController.java
  7. 139 0
      fs-admin/src/main/java/com/fs/reward/controller/FsRewardGoodsOrderController.java
  8. 2 0
      fs-service/src/main/java/com/fs/course/constant/CourseConstant.java
  9. 3 0
      fs-service/src/main/java/com/fs/course/domain/FsCourseCheckinActivity.java
  10. 7 1
      fs-service/src/main/java/com/fs/course/domain/FsCourseCheckinPrize.java
  11. 20 1
      fs-service/src/main/java/com/fs/course/domain/FsCourseCheckinReceive.java
  12. 77 0
      fs-service/src/main/java/com/fs/course/domain/FsCourseReward.java
  13. 115 0
      fs-service/src/main/java/com/fs/course/domain/FsCourseRewardRound.java
  14. 47 0
      fs-service/src/main/java/com/fs/course/domain/FsCourseRewardVideoRelation.java
  15. 28 0
      fs-service/src/main/java/com/fs/course/enums/FsCourseRewardTypeEnum.java
  16. 8 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseCheckinReceiveMapper.java
  17. 63 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseRewardMapper.java
  18. 80 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseRewardRoundMapper.java
  19. 87 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseRewardVideoRelationMapper.java
  20. 29 0
      fs-service/src/main/java/com/fs/course/param/FsCourseActualRewardsParam.java
  21. 17 0
      fs-service/src/main/java/com/fs/course/param/FsCourseGrandGiftParam.java
  22. 2 1
      fs-service/src/main/java/com/fs/course/param/FsCourseSendRewardUParam.java
  23. 13 0
      fs-service/src/main/java/com/fs/course/service/IFsCourseCheckinReceiveService.java
  24. 102 0
      fs-service/src/main/java/com/fs/course/service/IFsCourseRewardRoundService.java
  25. 67 0
      fs-service/src/main/java/com/fs/course/service/IFsCourseRewardService.java
  26. 26 9
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseCheckinActivityServiceImpl.java
  27. 377 15
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseCheckinReceiveServiceImpl.java
  28. 718 0
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseRewardRoundServiceImpl.java
  29. 225 0
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseRewardServiceImpl.java
  30. 37 0
      fs-service/src/main/java/com/fs/course/utils/luckyDraw/LotteryUtil.java
  31. 87 0
      fs-service/src/main/java/com/fs/course/utils/luckyDraw/Prize.java
  32. 1 1
      fs-service/src/main/java/com/fs/course/vo/CourseCheckinResultVO.java
  33. 15 0
      fs-service/src/main/java/com/fs/erp/service/IErpOrderService.java
  34. 259 0
      fs-service/src/main/java/com/fs/erp/service/impl/JSTErpOrderServiceImpl.java
  35. 32 0
      fs-service/src/main/java/com/fs/his/domain/FsCoupon.java
  36. 3 0
      fs-service/src/main/java/com/fs/his/domain/FsIntegralOrder.java
  37. 3 0
      fs-service/src/main/java/com/fs/his/domain/FsPackage.java
  38. 39 0
      fs-service/src/main/java/com/fs/his/dto/ComputeIntegralOrderMoneyDTO.java
  39. 1 0
      fs-service/src/main/java/com/fs/his/enums/BusinessTypeEnum.java
  40. 1 0
      fs-service/src/main/java/com/fs/his/enums/FsUserIntegralLogTypeEnum.java
  41. 1 14
      fs-service/src/main/java/com/fs/his/mapper/FsUserCouponMapper.java
  42. 16 0
      fs-service/src/main/java/com/fs/his/param/FsIntegralOrderComputeParam.java
  43. 1 10
      fs-service/src/main/java/com/fs/his/param/FsIntegralOrderCreateParam.java
  44. 1 4
      fs-service/src/main/java/com/fs/his/param/FsUserCouponUParam.java
  45. 5 0
      fs-service/src/main/java/com/fs/his/service/IFsCouponService.java
  46. 5 0
      fs-service/src/main/java/com/fs/his/service/IFsIntegralOrderService.java
  47. 12 2
      fs-service/src/main/java/com/fs/his/service/impl/FsCouponServiceImpl.java
  48. 169 59
      fs-service/src/main/java/com/fs/his/service/impl/FsIntegralOrderServiceImpl.java
  49. 5 0
      fs-service/src/main/java/com/fs/his/vo/FsCouponListVO.java
  50. 5 0
      fs-service/src/main/java/com/fs/his/vo/FsPackageVO.java
  51. 9 0
      fs-service/src/main/java/com/fs/his/vo/FsUserCouponListUVO.java
  52. 110 0
      fs-service/src/main/java/com/fs/reward/domain/FsRewardGoods.java
  53. 160 0
      fs-service/src/main/java/com/fs/reward/domain/FsRewardGoodsOrder.java
  54. 32 0
      fs-service/src/main/java/com/fs/reward/mapper/FsRewardGoodsMapper.java
  55. 39 0
      fs-service/src/main/java/com/fs/reward/mapper/FsRewardGoodsOrderMapper.java
  56. 24 0
      fs-service/src/main/java/com/fs/reward/param/FsAppRewardGoodsOrderAddParam.java
  57. 68 0
      fs-service/src/main/java/com/fs/reward/param/FsRewardGoodsAddParam.java
  58. 20 0
      fs-service/src/main/java/com/fs/reward/param/FsRewardGoodsEditParam.java
  59. 23 0
      fs-service/src/main/java/com/fs/reward/param/FsRewardGoodsListParam.java
  60. 24 0
      fs-service/src/main/java/com/fs/reward/param/FsRewardGoodsOrderAddParam.java
  61. 56 0
      fs-service/src/main/java/com/fs/reward/param/FsRewardGoodsOrderListParam.java
  62. 29 0
      fs-service/src/main/java/com/fs/reward/param/FsRewardGoodsOrderPayParam.java
  63. 67 0
      fs-service/src/main/java/com/fs/reward/service/IFsRewardGoodsOrderService.java
  64. 38 0
      fs-service/src/main/java/com/fs/reward/service/IFsRewardGoodsService.java
  65. 751 0
      fs-service/src/main/java/com/fs/reward/service/impl/FsRewardGoodsOrderServiceImpl.java
  66. 105 0
      fs-service/src/main/java/com/fs/reward/service/impl/FsRewardGoodsServiceImpl.java
  67. 168 0
      fs-service/src/main/java/com/fs/reward/vo/FsRewardGoodsOrderVO.java
  68. 113 0
      fs-service/src/main/java/com/fs/reward/vo/FsRewardGoodsVO.java
  69. 38 0
      fs-service/src/main/java/com/fs/reward/vo/FsRewardListVO.java
  70. 19 0
      fs-service/src/main/java/com/fs/utils/Range.java
  71. 4 4
      fs-service/src/main/resources/application-config-dev.yml
  72. 1 1
      fs-service/src/main/resources/application-config-druid.yml
  73. 5 1
      fs-service/src/main/resources/mapper/course/FsCourseCheckinActivityMapper.xml
  74. 12 4
      fs-service/src/main/resources/mapper/course/FsCourseCheckinPrizeMapper.xml
  75. 27 14
      fs-service/src/main/resources/mapper/course/FsCourseCheckinReceiveMapper.xml
  76. 137 0
      fs-service/src/main/resources/mapper/course/FsCourseRewardVideoRelationMapper.xml
  77. 116 0
      fs-service/src/main/resources/mapper/course/RewardMapper.xml
  78. 213 0
      fs-service/src/main/resources/mapper/course/RewardRoundMapper.xml
  79. 21 1
      fs-service/src/main/resources/mapper/his/FsCouponMapper.xml
  80. 5 1
      fs-service/src/main/resources/mapper/his/FsPackageMapper.xml
  81. 26 0
      fs-service/src/main/resources/mapper/his/FsUserCouponMapper.xml
  82. 52 0
      fs-service/src/main/resources/mapper/reward/FsRewardGoodsMapper.xml
  83. 93 0
      fs-service/src/main/resources/mapper/reward/FsRewardGoodsOrderMapper.xml
  84. 6 0
      fs-user-app/src/main/java/com/fs/app/controller/HuifuPayController.java
  85. 8 0
      fs-user-app/src/main/java/com/fs/app/controller/IntegralController.java
  86. 89 0
      fs-user-app/src/main/java/com/fs/app/controller/RewardGoodsController.java
  87. 7 0
      fs-user-app/src/main/java/com/fs/app/controller/TzPayController.java
  88. 13 1
      fs-user-app/src/main/java/com/fs/app/controller/WxPayController.java
  89. 6 0
      fs-user-app/src/main/java/com/fs/app/controller/YbPayController.java
  90. 17 7
      fs-user-app/src/main/java/com/fs/app/controller/course/CourseFsUserController.java

+ 5 - 5
fs-admin/pom.xml

@@ -27,11 +27,11 @@
         </dependency>
 
         <!-- spring-boot-devtools -->
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-devtools</artifactId>
-            <optional>true</optional> <!-- 表示依赖不会传递 -->
-        </dependency>
+<!--        <dependency>-->
+<!--            <groupId>org.springframework.boot</groupId>-->
+<!--            <artifactId>spring-boot-devtools</artifactId>-->
+<!--            <optional>true</optional> &lt;!&ndash; 表示依赖不会传递 &ndash;&gt;-->
+<!--        </dependency>-->
          <!-- Mysql驱动包 -->
         <dependency>
             <groupId>mysql</groupId>

+ 102 - 0
fs-admin/src/main/java/com/fs/course/controller/FsCourseRewardController.java

@@ -0,0 +1,102 @@
+package com.fs.course.controller;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.course.domain.FsCourseReward;
+import com.fs.course.service.IFsCourseRewardService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 奖励配置Controller
+ *
+ * @author 杨衍生
+ * @date 2025-09-02
+ */
+@RestController
+@RequestMapping("/course/reward")
+public class FsCourseRewardController extends BaseController
+{
+    @Autowired
+    private IFsCourseRewardService rewardService;
+
+    /**
+     * 查询奖励配置列表
+     */
+    @PreAuthorize("@ss.hasPermi('course:reward:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsCourseReward reward)
+    {
+        startPage();
+        List<FsCourseReward> list = rewardService.selectFsCourseRewardList(reward);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出奖励配置列表
+     */
+    @PreAuthorize("@ss.hasPermi('course:reward:export')")
+    @Log(title = "奖励配置", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FsCourseReward reward)
+    {
+        List<FsCourseReward> list = rewardService.selectFsCourseRewardList(reward);
+        ExcelUtil<FsCourseReward> util = new ExcelUtil<FsCourseReward>(FsCourseReward.class);
+        return util.exportExcel(list, "奖励配置数据");
+    }
+
+    /**
+     * 获取奖励配置详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('course:reward:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(rewardService.selectFsCourseRewardById(id));
+    }
+
+    /**
+     * 新增奖励配置
+     */
+    @PreAuthorize("@ss.hasPermi('course:reward:add')")
+    @Log(title = "奖励配置", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FsCourseReward reward)
+    {
+        return toAjax(rewardService.insertFsCourseReward(reward));
+    }
+
+    /**
+     * 修改奖励配置
+     */
+    @PreAuthorize("@ss.hasPermi('course:reward:edit')")
+    @Log(title = "奖励配置", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FsCourseReward reward)
+    {
+        return toAjax(rewardService.updateFsCourseReward(reward));
+    }
+
+    /**
+     * 删除奖励配置
+     */
+    @PreAuthorize("@ss.hasPermi('course:reward:remove')")
+    @Log(title = "奖励配置", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(rewardService.deleteFsCourseRewardByIds(ids));
+    }
+
+    @GetMapping("/listByIds/{ids}")
+    public AjaxResult listByIds(@PathVariable List<Long> ids) {
+        return AjaxResult.success(rewardService.selectFsCourseRewardByIds(ids));
+    }
+}

+ 123 - 0
fs-admin/src/main/java/com/fs/course/controller/FsCourseRewardRoundController.java

@@ -0,0 +1,123 @@
+package com.fs.course.controller;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.company.domain.Company;
+import com.fs.company.service.ICompanyService;
+import com.fs.course.domain.FsCourseRewardRound;
+import com.fs.course.service.IFsCourseRewardRoundService;
+import com.fs.his.domain.FsUser;
+import com.fs.his.service.IFsUserService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 奖励领取记录Controller
+ *
+ * @author 杨衍生
+ * @date 2025-09-02
+ */
+@RestController
+@RequestMapping("/course/rewardRound")
+public class FsCourseRewardRoundController extends BaseController
+{
+    @Autowired
+    private IFsCourseRewardRoundService rewardRoundService;
+
+    @Autowired
+    private IFsUserService fsUserService;
+    @Autowired
+    private ICompanyService companyService;
+
+    /**
+     * 查询奖励领取记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('course:rewardRound:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsCourseRewardRound rewardRound)
+    {
+        startPage();
+        List<FsCourseRewardRound> list = rewardRoundService.selectFsCourseRewardRoundList(rewardRound);
+        for (FsCourseRewardRound fsCourseRewardRound : list) {
+            // 检查userId是否为空
+            if (fsCourseRewardRound.getUserId() != null) {
+                FsUser fsUser = fsUserService.selectFsUserByUserId(fsCourseRewardRound.getUserId());
+                if (fsUser != null) {
+                    fsCourseRewardRound.setUserIdByName(fsUser.getUserId()+"_"+fsUser.getNickName());
+                }
+            }
+
+            // 检查companyId是否为空
+            if (fsCourseRewardRound.getCompanyId() != null) {
+                Company company = companyService.selectCompanyById(fsCourseRewardRound.getCompanyId());
+                if (company != null) {
+                    fsCourseRewardRound.setCompanyName(company.getCompanyName());
+                }
+            }
+        }
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出奖励领取记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('course:rewardRound:export')")
+    @Log(title = "奖励领取记录", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FsCourseRewardRound rewardRound)
+    {
+        List<FsCourseRewardRound> list = rewardRoundService.selectFsCourseRewardRoundList(rewardRound);
+        ExcelUtil<FsCourseRewardRound> util = new ExcelUtil<FsCourseRewardRound>(FsCourseRewardRound.class);
+        return util.exportExcel(list, "奖励领取记录数据");
+    }
+
+    /**
+     * 获取奖励领取记录详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('course:rewardRound:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(rewardRoundService.selectFsCourseRewardRoundById(id));
+    }
+
+    /**
+     * 新增奖励领取记录
+     */
+    @PreAuthorize("@ss.hasPermi('course:rewardRound:add')")
+    @Log(title = "奖励领取记录", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FsCourseRewardRound rewardRound)
+    {
+        return toAjax(rewardRoundService.insertFsCourseRewardRound(rewardRound));
+    }
+
+    /**
+     * 修改奖励领取记录
+     */
+    @PreAuthorize("@ss.hasPermi('course:rewardRound:edit')")
+    @Log(title = "奖励领取记录", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FsCourseRewardRound rewardRound)
+    {
+        return toAjax(rewardRoundService.updateFsCourseRewardRound(rewardRound));
+    }
+
+    /**
+     * 删除奖励领取记录
+     */
+    @PreAuthorize("@ss.hasPermi('course:rewardRound:remove')")
+    @Log(title = "奖励领取记录", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(rewardRoundService.deleteFsCourseRewardRoundByIds(ids));
+    }
+}

+ 6 - 8
fs-admin/src/main/java/com/fs/his/controller/FsCouponController.java

@@ -8,14 +8,7 @@ import com.fs.his.vo.FsCouponAllListVO;
 import com.fs.his.vo.FsCouponListVO;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
@@ -123,4 +116,9 @@ public class FsCouponController extends BaseController
     {
         return toAjax(fsCouponService.deleteFsCouponByCouponIds(couponIds));
     }
+
+    @GetMapping(value = "/getByIds")
+    public AjaxResult getByIds(@RequestParam List<Long> ids) {
+        return AjaxResult.success(fsCouponService.selectFsCouponListByIds(ids));
+    }
 }

+ 26 - 0
fs-admin/src/main/java/com/fs/his/task/RewardErpTask.java

@@ -0,0 +1,26 @@
+package com.fs.his.task;
+
+import com.fs.reward.service.IFsRewardGoodsOrderService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component("rewardErpTask")
+public class RewardErpTask {
+
+    @Autowired
+    private IFsRewardGoodsOrderService fsRewardGoodsOrderService;
+
+    public void pushErp() throws InterruptedException {
+        fsRewardGoodsOrderService.pushErp();
+    }
+
+    public void syncErpData() throws InterruptedException {
+        fsRewardGoodsOrderService.syncErpData();
+    }
+
+    public void completeOrder() {
+        fsRewardGoodsOrderService.completeOrder();
+    }
+}

+ 80 - 0
fs-admin/src/main/java/com/fs/reward/controller/FsRewardGoodsController.java

@@ -0,0 +1,80 @@
+package com.fs.reward.controller;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.SecurityUtils;
+import com.fs.reward.param.FsRewardGoodsAddParam;
+import com.fs.reward.param.FsRewardGoodsEditParam;
+import com.fs.reward.param.FsRewardGoodsListParam;
+import com.fs.reward.service.IFsRewardGoodsService;
+import com.fs.reward.vo.FsRewardGoodsVO;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@Slf4j
+@Api("奖励商品管理")
+@RestController
+@RequestMapping(value="/reward/rewardGoods")
+@AllArgsConstructor
+public class FsRewardGoodsController extends BaseController {
+
+    private final IFsRewardGoodsService rewardGoodsService;
+
+    @ApiOperation("奖励商品列表")
+    @PreAuthorize("@ss.hasPermi('reward:rewardGoods:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsRewardGoodsListParam param) {
+        startPage();
+        List<FsRewardGoodsVO> list = rewardGoodsService.selectFsRewardGoodsVOList(param);
+        return getDataTable(list);
+    }
+
+    @ApiOperation("奖励商品详情")
+    @PreAuthorize("@ss.hasPermi('reward:rewardGoods:query')")
+    @GetMapping("/{goodsId}")
+    public AjaxResult getInfo(@PathVariable Long goodsId) {
+        return AjaxResult.success(rewardGoodsService.selectFsRewardGoodsVOById(goodsId));
+    }
+
+    @ApiOperation("添加奖励商品")
+    @PreAuthorize("@ss.hasPermi('reward:rewardGoods:add')")
+    @Log(title = "奖励商品", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@Validated @RequestBody FsRewardGoodsAddParam param) {
+        param.setCreateBy(SecurityUtils.getUsername());
+        return toAjax(rewardGoodsService.insertFsRewardGoods(param));
+    }
+
+    @ApiOperation("修改奖励商品")
+    @PreAuthorize("@ss.hasPermi('reward:rewardGoods:edit')")
+    @Log(title = "奖励商品", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@Validated @RequestBody FsRewardGoodsEditParam param) {
+        param.setUpdateBy(SecurityUtils.getUsername());
+        return toAjax(rewardGoodsService.updateFsRewardGoods(param));
+    }
+
+    @ApiOperation("删除奖励商品")
+    @PreAuthorize("@ss.hasPermi('reward:rewardGoods:remove')")
+    @Log(title = "奖励商品", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{goodsIds}")
+    public AjaxResult remove(@PathVariable Long[] goodsIds) {
+        return toAjax(rewardGoodsService.logicDeleteFsRewardGoods(goodsIds));
+    }
+
+    @ApiOperation("根据ID查询奖励商品")
+    @GetMapping(value = "/getByIds")
+    public AjaxResult getByIds(@RequestParam List<Long> ids) {
+        return AjaxResult.success(rewardGoodsService.listByIds(ids));
+    }
+}

+ 139 - 0
fs-admin/src/main/java/com/fs/reward/controller/FsRewardGoodsOrderController.java

@@ -0,0 +1,139 @@
+package com.fs.reward.controller;
+
+import cn.hutool.core.util.StrUtil;
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.StringUtils;
+import com.fs.his.dto.ExpressInfoDTO;
+import com.fs.his.enums.ShipperCodeEnum;
+import com.fs.his.service.IFsExpressService;
+import com.fs.his.utils.PhoneUtil;
+import com.fs.reward.domain.FsRewardGoodsOrder;
+import com.fs.reward.param.FsRewardGoodsOrderListParam;
+import com.fs.reward.service.IFsRewardGoodsOrderService;
+import com.fs.reward.vo.FsRewardGoodsOrderVO;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Objects;
+
+import static com.fs.his.utils.PhoneUtil.decryptPhone;
+
+@Slf4j
+@Api("奖励商品订单管理")
+@RestController
+@RequestMapping(value="/reward/rewardGoodsOrder")
+public class FsRewardGoodsOrderController extends BaseController {
+
+    @Autowired
+    private IFsRewardGoodsOrderService rewardGoodsOrderService;
+    @Autowired
+    private IFsExpressService expressService;
+
+    @ApiOperation("奖励商品订单列表")
+    @PreAuthorize("@ss.hasPermi('reward:rewardGoodsOrder:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsRewardGoodsOrderListParam param) {
+        if (param != null && StringUtils.isNotBlank(param.getReceiveUserPhone())) {
+            param.setReceiveUserPhone(PhoneUtil.encryptPhone(param.getReceiveUserPhone()));
+        }
+        if (param != null && StringUtils.isNotBlank(param.getReceiveUserName())) {
+            param.setReceiveUserName(param.getReceiveUserName().trim());
+        }
+
+        startPage();
+        List<FsRewardGoodsOrderVO> list = rewardGoodsOrderService.selectFsRewardGoodsOrderVOList(param);
+        // 隐藏手机号码和地址
+        for (FsRewardGoodsOrderVO fsRewardGoodsOrderVO : list) {
+            fsRewardGoodsOrderVO.setAddress("**** **** ****");
+            fsRewardGoodsOrderVO.setMobile("****");
+        }
+        return getDataTable(list);
+    }
+
+    @ApiOperation("奖励商品订单详情")
+    @PreAuthorize("@ss.hasPermi('reward:rewardGoodsOrder:query')")
+    @GetMapping("/{orderId}")
+    public AjaxResult getInfo(@PathVariable Long orderId) {
+        FsRewardGoodsOrderVO fsRewardGoodsOrderVO = rewardGoodsOrderService.selectFsRewardGoodsOrderVOById(orderId);
+        // 隐藏手机号码和地址
+        fsRewardGoodsOrderVO.setAddress("**** **** ****");
+        fsRewardGoodsOrderVO.setMobile("****");
+        return AjaxResult.success(fsRewardGoodsOrderVO);
+    }
+
+    @ApiOperation("支付信息")
+    @GetMapping("/payment/{orderSn}")
+    public AjaxResult getPayInfo(@PathVariable String orderSn) {
+        return AjaxResult.success(rewardGoodsOrderService.getPayInfo(orderSn));
+    }
+
+    @ApiOperation("物流信息")
+    @GetMapping(value = "/getExpress/{orderId}")
+    public R getExpress(@PathVariable Long orderId) {
+        FsRewardGoodsOrder order = rewardGoodsOrderService.getById(orderId);
+        if (Objects.isNull(order)) {
+            return R.error("订单不存在");
+        }
+
+        ExpressInfoDTO expressInfoDTO=null;
+        if(StringUtils.isNotEmpty(order.getDeliverySn())){
+            String lastFourNumber = "";
+            if (order.getDeliveryCode().equals(ShipperCodeEnum.SF.getValue())) {
+                lastFourNumber = order.getMobile().length() > 11 ? decryptPhone(order.getMobile()) : order.getMobile();
+                lastFourNumber = StrUtil.sub(lastFourNumber, lastFourNumber.length(), -4);
+            }
+            expressInfoDTO = expressService.getExpressInfo(order.getOrderSn(),order.getDeliveryCode(),order.getDeliverySn(),lastFourNumber);
+
+            if(expressInfoDTO != null && (expressInfoDTO.getStateEx()!=null&&expressInfoDTO.getStateEx().equals("0"))&&(expressInfoDTO.getState()!=null&&expressInfoDTO.getState().equals("0"))){
+                lastFourNumber = "19923690275";
+                if (order.getDeliveryCode().equals(ShipperCodeEnum.SF.getValue())) {
+                    lastFourNumber = StrUtil.sub(lastFourNumber, lastFourNumber.length(), -4);
+                }
+
+                expressInfoDTO = expressService.getExpressInfo(order.getOrderSn(),order.getDeliveryCode(),order.getDeliverySn(),lastFourNumber);
+
+            }
+        }
+        return R.ok().put("data", expressInfoDTO);
+    }
+
+    @Log(title = "取消奖励商品订单", businessType = BusinessType.UPDATE)
+    @PreAuthorize("@ss.hasPermi('reward:rewardGoodsOrder:cancelOrder')")
+    @PutMapping("/cancelOrder/{orderId}")
+    public AjaxResult cancelOrder(@PathVariable Long orderId) {
+        return toAjax(rewardGoodsOrderService.cancelOrder(orderId));
+    }
+
+    @GetMapping(value = "/queryPhone/{orderId}")
+    @Log(title = "查看电话", businessType = BusinessType.GRANT)
+    @PreAuthorize("@ss.hasPermi('reward:rewardGoodsOrder:queryPhone')")
+    public R getPhone(@PathVariable("orderId") Long orderId)
+    {
+        FsRewardGoodsOrderVO order = rewardGoodsOrderService.selectFsRewardGoodsOrderVOById(orderId);
+        String userPhone = order.getMobile();
+
+        if (userPhone!=null&&userPhone.length()>11){
+            userPhone = decryptPhone(userPhone);
+        }
+        return R.ok().put("userPhone",userPhone);
+    }
+
+    @GetMapping(value = "/queryAddress/{orderId}")
+    @Log(title = "查看地址", businessType = BusinessType.GRANT)
+    @PreAuthorize("@ss.hasPermi('reward:rewardGoodsOrder:queryAddress')")
+    public R getAddress(@PathVariable("orderId") Long orderId)
+    {
+        FsRewardGoodsOrderVO order = rewardGoodsOrderService.selectFsRewardGoodsOrderVOById(orderId);
+        return R.ok().put("userAddress", order.getAddress());
+    }
+}

+ 2 - 0
fs-service/src/main/java/com/fs/course/constant/CourseConstant.java

@@ -4,6 +4,8 @@ public class CourseConstant {
 
     public static final String FEI_SHU_WATCH_HEART = "h5feishu:watch:heartbeat:";
     public static final String FEI_SHU_WATCH_DURATION = "h5feishu:watch:duration:";
+    // 抽奖大礼品缓存key
+    public static String REDIS_GRAND_GIFT = "grand:gift:";
 
     public static String getFeiShuWatchHeartKey(Long userId, Long videoId, Long companyUserId, Long periodId) {
         return String.format(FEI_SHU_WATCH_HEART + "%s:%s:%s:%s", userId, videoId, companyUserId, periodId);

+ 3 - 0
fs-service/src/main/java/com/fs/course/domain/FsCourseCheckinActivity.java

@@ -74,6 +74,9 @@ public class FsCourseCheckinActivity implements Serializable {
     /** 备注 */
     private String remark;
 
+    /** 奖品类型 1-红包 2-积分商品免单券 3-大礼品 **/
+    private Integer prizeType;
+
     /** 请求参数 */
     private Map<String, Object> params;
 

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

@@ -30,7 +30,7 @@ public class FsCourseCheckinPrize implements Serializable {
     private Long activityId;
 
     /** 奖品类型:1-红包,2-积分商品券 */
-    @Excel(name = "奖品类型", readConverterExp = "1=红包,2=积分商品券")
+    @Excel(name = "奖品类型", readConverterExp = "1=红包,2=积分商品券,3=大礼品")
     private Integer prizeType;
 
     /** 奖品名称 */
@@ -44,6 +44,12 @@ public class FsCourseCheckinPrize implements Serializable {
     /** 积分商品ID,关联fs_integral_goods,prize_type=2时有效 */
     private Long goodsId;
 
+    /** 奖励配置ID **/
+    private Long grandGiftId;
+
+    /** 免单券ID **/
+    private Long freeCouponId;
+
     /** 排序 */
     private Integer sort;
 

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

@@ -42,7 +42,7 @@ public class FsCourseCheckinReceive implements Serializable {
     private Long projectId;
 
     /** 奖品类型:1-红包,2-积分商品券 */
-    @Excel(name = "奖品类型", readConverterExp = "1=红包,2=积分商品券")
+    @Excel(name = "奖品类型", readConverterExp = "1=红包,2=积分商品券,3=大礼品")
     private Integer prizeType;
 
     /** 奖品名称 */
@@ -56,6 +56,18 @@ public class FsCourseCheckinReceive implements Serializable {
     /** 积分商品ID */
     private Long goodsId;
 
+    /** 大礼品转盘ID **/
+    private Long grandGiftId;
+
+    /** 中奖项code **/
+    private String grandGiftCode;
+
+    /** 大礼品中奖类型,1-红包 2-积分 3-空 4-优惠券 5-商品 **/
+    private Integer grandGiftType;
+
+    /** 免单券ID **/
+    private Long freeCouponId;
+
     /** 领取状态:0-待发放,1-发放成功,2-发放失败 */
     @Excel(name = "领取状态", readConverterExp = "0=待发放,1=发放成功,2=发放失败")
     private Integer receiveStatus;
@@ -134,4 +146,11 @@ public class FsCourseCheckinReceive implements Serializable {
     public void setParams(Map<String, Object> params) {
         this.params = params;
     }
+
+    /** 大礼品订单ID **/
+    private Long grandGiftOrderId;
+
+    // 非数据库字段
+    /** 状态 0未使用 1已使用 2已过期 **/
+    private Integer useState;
 }

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

@@ -0,0 +1,77 @@
+package com.fs.course.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.time.LocalDateTime;
+
+/**
+ * 奖励配置对象 fs_course_reward
+ *
+ * @author 杨衍生
+ * @date 2025-09-02
+ */
+@Data
+@TableName("fs_course_reward")
+public class FsCourseReward {
+
+    /** 主键ID */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 奖励名称 */
+    @Excel(name = "奖励名称")
+    private String name;
+
+    /** 奖励描述 */
+    @Excel(name = "奖励描述")
+    private String description;
+
+    /** 奖励类型 (1:宝箱, 2:红包, 3:积分, 4:转盘, 5:保底转盘, 6:大礼品) */
+    @Excel(name = "奖励类型 (1:宝箱, 2:红包, 3:积分, 4:转盘, 5:保底转盘, 6:大礼品)")
+    private Long rewardType;
+
+    /** 状态 (0:禁用, 1:启用) */
+    @Excel(name = "状态 (0:禁用, 1:启用)")
+    private Long status;
+
+    /** 期望值 */
+    @Excel(name = "期望值")
+    private String expectedValue;
+
+    /** 创建人ID **/
+    @Excel(name = "创建人ID")
+    private Long createId;
+
+    /** 创建时间 **/
+    private LocalDateTime createTime;
+
+    /** 更新时间 **/
+    private LocalDateTime updateTime;
+
+    /** 实际获得的奖励内容 */
+    @Excel(name = "实际获得的奖励内容")
+    private String actualRewards;
+
+    /**
+     * 关闭宝箱url
+     */
+    private String closeChestUrl;
+
+    /**
+     * 开启宝箱url
+     */
+    private String openChestUrl;
+
+    /**
+     * 奖励id
+     */
+    @TableField(exist = false)
+    private Long [] rewardIds;
+}

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

@@ -0,0 +1,115 @@
+package com.fs.course.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 奖励领取记录对象 reward_round
+ *
+ * @author 杨衍生
+ * @date 2025-09-02
+ */
+@Data
+@TableName("fs_course_reward_round")
+public class FsCourseRewardRound{
+
+    /** 主键ID */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 奖励ID */
+    @Excel(name = "奖励ID")
+    private Long rewardId;
+
+    /** 领取用户ID */
+    @Excel(name = "领取用户ID")
+    private Long userId;
+
+    @TableField(exist = false)
+    private String userIdByName;
+
+    /** 奖励类型 */
+    @Excel(name = "奖励类型")
+    private Long rewardType;
+
+    /** 公司ID */
+    @Excel(name = "公司ID")
+    private Long companyId;
+
+    @TableField(exist = false)
+    private String companyName;
+
+    /** 实际领取到的奖励 */
+    @Excel(name = "实际领取到的奖励")
+    private String actualRewards;
+
+    /** 创建人ID */
+    @Excel(name = "创建人ID")
+    private Long createId;
+
+    /** 创建时间 **/
+    private LocalDateTime createTime;
+
+    /** 更新时间 **/
+    private LocalDateTime updateTime;
+
+    /** 奖励状态 (0:已作废, 1:已领取, 2:已过期) */
+    @Excel(name = "奖励状态 (0:已作废, 1:已领取, 2:已过期)")
+    private Long status;
+
+    /** 规则id */
+    @Excel(name = "规则id")
+    private String ruleId;
+
+    /**
+     * 规则名称
+     */
+    private String ruleName;
+
+    /** 看课记录id */
+    @Excel(name = "看课记录id")
+    private Long watchId;
+
+    /** 奖励规则关联id */
+    @Excel(name = "奖励规则关联id")
+    private Long rewardVideoRelationId;
+    /**
+     * 时长
+     */
+    @TableField(exist = false)
+    private String second;
+    /**
+     * 小节id
+     */
+    @TableField(exist = false)
+    private Long videoId;
+    @TableField(exist = false)
+    private String qwUserId;
+    @TableField(exist = false)
+    private Long qwExternalId;
+    @TableField(exist = false)
+    private Integer linkType;
+
+    /**
+     * 奖励商品ID
+     */
+    private Long goodsId;
+    /**
+     * 奖励商品价格
+     */
+    private BigDecimal goodsPrice;
+
+    @TableField(exist = false)
+    private Map<String, Object> params = new HashMap<>();
+}

+ 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;
+
+}

+ 28 - 0
fs-service/src/main/java/com/fs/course/enums/FsCourseRewardTypeEnum.java

@@ -0,0 +1,28 @@
+package com.fs.course.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+import java.util.Arrays;
+
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+public enum FsCourseRewardTypeEnum {
+    TYPE_1(1L,"宝箱"),
+    TYPE_2(2L,"红包"),
+    TYPE_3(3L,"积分"),
+    TYPE_4(4L,"转盘"),
+    TYPE_5(5L,"保底转盘"),
+    TYPE_6(6L,"大礼品"),
+    ;
+    private Long value;
+    private String desc;
+
+    public static FsCourseRewardTypeEnum getByValue(Long value) {
+        if (value == null)
+            return null;
+        return Arrays.stream(values()).filter(e -> e.value.equals(value)).findFirst().orElse(null);
+    }
+}

+ 8 - 0
fs-service/src/main/java/com/fs/course/mapper/FsCourseCheckinReceiveMapper.java

@@ -2,7 +2,9 @@ package com.fs.course.mapper;
 
 import com.fs.course.domain.FsCourseCheckinReceive;
 import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
 
+import javax.validation.constraints.NotNull;
 import java.util.List;
 
 /**
@@ -103,4 +105,10 @@ public interface FsCourseCheckinReceiveMapper {
      * @return 领取记录列表
      */
     List<FsCourseCheckinReceive> selectCheckinPrizeReceiveByUserIds(@Param("userIds") List<Long> userIds, @Param("goodsName") String goodsName);
+
+    /**
+     * 根据用户ID,记录ID,大礼品ID查询领取记录
+     */
+    @Select("select * from fs_course_checkin_receive where receive_id = #{receiveId} and user_id = #{userId} and grand_gift_id = #{grandGiftId}")
+    FsCourseCheckinReceive selectByUserIdAndReceiveIdAndGrandGiftId(@Param("userId") Long userId, @Param("receiveId") Long receiveId, @Param("grandGiftId") Long grandGiftId);
 }

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

@@ -0,0 +1,63 @@
+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;
+
+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);
+}

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

@@ -0,0 +1,80 @@
+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 com.fs.reward.vo.FsRewardListVO;
+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);
+
+    /**
+     * 查询用户中奖记录
+     */
+    List<FsRewardListVO> getRewardListByUserId(@Param("userId") Long userId);
+}

+ 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);
+}

+ 29 - 0
fs-service/src/main/java/com/fs/course/param/FsCourseActualRewardsParam.java

@@ -0,0 +1,29 @@
+package com.fs.course.param;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@NoArgsConstructor
+@Data
+public class FsCourseActualRewardsParam {
+
+    @JsonProperty("type")
+    private Integer type;
+    @JsonProperty("jltype")
+    private Integer jltype;
+    @JsonProperty("name")
+    private String name;
+    @JsonProperty("amount")
+    private String amount;
+    @JsonProperty("probability")
+    private String probability;
+    @JsonProperty("code")
+    private String code;
+    @JsonProperty("iconUrl")
+    private String iconUrl;
+    @JsonProperty("isGuarantee")
+    private Integer isGuarantee;
+    @JsonProperty("couponId")
+    private String couponId;
+}

+ 17 - 0
fs-service/src/main/java/com/fs/course/param/FsCourseGrandGiftParam.java

@@ -0,0 +1,17 @@
+package com.fs.course.param;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+public class FsCourseGrandGiftParam {
+
+    @NotNull(message = "奖励ID不能为空")
+    private Long grandGiftId;
+
+    @NotNull(message = "领取记录ID不能为空")
+    private Long receiveId;
+
+    private Long userId;
+}

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

@@ -23,10 +23,11 @@ public class FsCourseSendRewardUParam implements Serializable
     @NotNull(message = "客服参数不能为空")
     private Long companyUserId;
     @NotNull(message = "经销商参数不能为空")
-//    private Long companyId;
+    private Long companyId;
     @NotNull(message = "课程参数不能为空")
     private Long courseId;
     private String corpId;
+    private Integer rewardType; //奖励类型 1红包 2积分 3随机转盘 4保底转盘 5大礼品
     private Integer linkType;
     @NotNull(message = "课程参数不能为空")
     private Long qwExternalId;

+ 13 - 0
fs-service/src/main/java/com/fs/course/service/IFsCourseCheckinReceiveService.java

@@ -1,6 +1,9 @@
 package com.fs.course.service;
 
+import com.alibaba.fastjson.JSONArray;
+import com.fs.common.core.domain.R;
 import com.fs.course.domain.FsCourseCheckinReceive;
+import com.fs.course.param.FsCourseGrandGiftParam;
 import com.fs.course.param.FsCourseSendRewardUParam;
 
 import java.util.List;
@@ -135,4 +138,14 @@ public interface IFsCourseCheckinReceiveService {
      * @return 领取记录列表
      */
     List<FsCourseCheckinReceive> selectReceiveListByUserId(Long userId);
+
+    /**
+     * 获取大礼品奖品项
+     */
+    JSONArray getRewardRules(FsCourseGrandGiftParam param);
+
+    /**
+     * 领取大礼品奖品
+     */
+    R sendActiveReward(FsCourseGrandGiftParam param);
 }

+ 102 - 0
fs-service/src/main/java/com/fs/course/service/IFsCourseRewardRoundService.java

@@ -0,0 +1,102 @@
+package com.fs.course.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.common.core.domain.R;
+import com.fs.company.vo.RedPacketMoneyVO;
+import com.fs.course.domain.FsCourseRewardRound;
+import com.fs.reward.vo.FsRewardListVO;
+
+import java.time.LocalDate;
+import java.util.List;
+
+/**
+ * 奖励领取记录Service接口
+ *
+ * @author 杨衍生
+ * @date 2025-09-02
+ */
+public interface IFsCourseRewardRoundService extends IService<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 ids 需要删除的奖励领取记录主键集合
+     * @return 结果
+     */
+    int deleteFsCourseRewardRoundByIds(Long[] ids);
+
+    /**
+     * 删除奖励领取记录信息
+     *
+     * @param id 奖励领取记录主键
+     * @return 结果
+     */
+    int deleteFsCourseRewardRoundById(Long id);
+
+    /**
+     * 领取奖励
+     * @param rewardRound
+     * @return
+     */
+    R claim(FsCourseRewardRound rewardRound);
+
+    /**
+     * 校验是否能领取奖励
+     *
+     * @param rewardRound
+     * @return
+     */
+    R isClaim(FsCourseRewardRound rewardRound);
+
+    /**
+     * 同步扣除企业金额
+     */
+    List<RedPacketMoneyVO> syncUpdatedCompanyAmount(Integer status);
+
+    /**
+     * 同步扣除企业金额
+     */
+    List<RedPacketMoneyVO> syncUpdatedCompanyAmountForLuckyBag(Integer status);
+
+    /**
+     * 查询1w条指定状态且小于指定时间的数据
+     */
+    List<FsCourseRewardRound> get1kByStatusAndLtDate(int status, LocalDate endTime);
+
+    /**
+     * 查询用户中奖记录
+     */
+    List<FsRewardListVO> getRewardListByUserId(Long userId);
+
+}

+ 67 - 0
fs-service/src/main/java/com/fs/course/service/IFsCourseRewardService.java

@@ -0,0 +1,67 @@
+package com.fs.course.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.course.domain.FsCourseReward;
+
+import java.util.List;
+
+/**
+ * 奖励配置Service接口
+ *
+ * @author 杨衍生
+ * @date 2025-09-02
+ */
+public interface IFsCourseRewardService extends IService<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 ids 需要删除的奖励配置主键集合
+     * @return 结果
+     */
+    int deleteFsCourseRewardByIds(Long[] ids);
+
+    /**
+     * 删除奖励配置信息
+     *
+     * @param id 奖励配置主键
+     * @return 结果
+     */
+    int deleteFsCourseRewardById(Long id);
+
+    /**
+     * 根据id集合查询奖励配置
+     */
+    List<FsCourseReward> selectFsCourseRewardByIds(List<Long> ids);
+}

+ 26 - 9
fs-service/src/main/java/com/fs/course/service/impl/FsCourseCheckinActivityServiceImpl.java

@@ -7,21 +7,16 @@ import com.fs.common.utils.DictUtils;
 import com.fs.common.utils.SecurityUtils;
 import com.fs.company.domain.Company;
 import com.fs.company.mapper.CompanyMapper;
-import com.fs.course.domain.FsCourseCheckinActivity;
-import com.fs.course.domain.FsCourseCheckinPrize;
-import com.fs.course.domain.FsCourseCheckinWarningQuery;
-import com.fs.course.domain.FsCourseCheckinWarningVO;
-import com.fs.course.domain.FsUserCompanyUser;
-import com.fs.course.mapper.FsCourseCheckinActivityMapper;
-import com.fs.course.mapper.FsCourseCheckinPrizeMapper;
-import com.fs.course.mapper.FsCourseCheckinReceiveMapper;
-import com.fs.course.mapper.FsCourseCheckinUserMapper;
+import com.fs.course.domain.*;
+import com.fs.course.mapper.*;
 import com.fs.course.service.IFsCourseCheckinActivityService;
 import com.fs.course.service.IFsUserCompanyUserService;
 import com.fs.crm.domain.CrmMsg;
 import com.fs.crm.service.ICrmMsgService;
+import com.fs.his.domain.FsCoupon;
 import com.fs.his.domain.FsIntegralGoods;
 import com.fs.his.domain.FsIntegralOrder;
+import com.fs.his.mapper.FsCouponMapper;
 import com.fs.his.mapper.FsIntegralGoodsMapper;
 import com.fs.his.mapper.FsIntegralOrderMapper;
 import com.fs.his.utils.PhoneUtil;
@@ -78,6 +73,10 @@ public class FsCourseCheckinActivityServiceImpl implements IFsCourseCheckinActiv
 
     @Autowired
     private RedisCacheUtil redisCacheUtil;
+    @Autowired
+    private FsCourseRewardMapper fsCourseRewardMapper;
+    @Autowired
+    private FsCouponMapper fsCouponMapper;
 
 
     /**
@@ -160,6 +159,24 @@ public class FsCourseCheckinActivityServiceImpl implements IFsCourseCheckinActiv
             } else {
                 prize.setPrizeName("积分商品");
             }
+        } else if (prize.getPrizeType() == 3) {
+            if (prize.getGrandGiftId() != null) {
+                FsCourseReward fsCourseReward = fsCourseRewardMapper.selectFsCourseRewardById(prize.getGrandGiftId());
+                if (fsCourseReward != null) {
+                    prize.setPrizeName(fsCourseReward.getName());
+                } else {
+                    prize.setPrizeName("大礼品转盘");
+                }
+            }
+        } else if (prize.getPrizeType() == 4) {
+            if (prize.getFreeCouponId() != null) {
+                FsCoupon fsCoupon = fsCouponMapper.selectFsCouponByCouponId(prize.getFreeCouponId());
+                if (fsCoupon != null) {
+                    prize.setPrizeName(fsCoupon.getTitle());
+                } else {
+                    prize.setPrizeName("积分商品免单券");
+                }
+            }
         }
     }
 

+ 377 - 15
fs-service/src/main/java/com/fs/course/service/impl/FsCourseCheckinReceiveServiceImpl.java

@@ -2,7 +2,11 @@ package com.fs.course.service.impl;
 
 import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
 import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.common.exception.CustomException;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.company.domain.Company;
@@ -10,23 +14,28 @@ import com.fs.company.mapper.CompanyMapper;
 import com.fs.config.cloud.CloudHostProper;
 import com.fs.core.utils.OrderCodeUtils;
 import com.fs.course.config.CourseConfig;
+import com.fs.course.constant.CourseConstant;
 import com.fs.course.domain.*;
 import com.fs.course.mapper.*;
+import com.fs.course.param.FsCourseGrandGiftParam;
 import com.fs.course.param.FsCourseSendRewardUParam;
 import com.fs.course.service.IFsCourseCheckinReceiveService;
-import com.fs.his.domain.FsIntegralGoods;
-import com.fs.his.domain.FsUser;
-import com.fs.his.domain.FsUserWx;
-import com.fs.his.mapper.FsIntegralGoodsMapper;
-import com.fs.his.mapper.FsUserMapper;
+import com.fs.course.utils.luckyDraw.LotteryUtil;
+import com.fs.course.utils.luckyDraw.Prize;
+import com.fs.his.domain.*;
+import com.fs.his.enums.FsUserIntegralLogTypeEnum;
+import com.fs.his.mapper.*;
 import com.fs.his.param.WxSendRedPacketParam;
 import com.fs.his.service.IFsStorePaymentService;
 import com.fs.his.service.IFsUserWxService;
 import com.fs.his.utils.RedisCacheUtil;
+import com.fs.reward.domain.FsRewardGoods;
+import com.fs.reward.domain.FsRewardGoodsOrder;
+import com.fs.reward.mapper.FsRewardGoodsMapper;
+import com.fs.reward.mapper.FsRewardGoodsOrderMapper;
 import com.fs.system.service.ISysConfigService;
 import com.github.binarywang.wxpay.bean.transfer.TransferBillsResult;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.hc.core5.http2.hpack.HPackException;
 import org.redisson.api.RLock;
 import org.redisson.api.RedissonClient;
 import org.slf4j.Logger;
@@ -36,6 +45,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
+import java.time.LocalDateTime;
 import java.time.OffsetDateTime;
 import java.util.*;
 import java.util.stream.Collectors;
@@ -97,6 +107,22 @@ public class FsCourseCheckinReceiveServiceImpl implements IFsCourseCheckinReceiv
 
     @Autowired
     private CloudHostProper cloudHostProper;
+    @Autowired
+    private FsCourseRewardMapper fsCourseRewardMapper;
+    @Autowired
+    private FsCourseRewardRoundMapper fsCourseRewardRoundMapper;
+    @Autowired
+    private FsRewardGoodsMapper rewardGoodsMapper;
+    @Autowired
+    private FsRewardGoodsOrderMapper rewardGoodsOrderMapper;
+    @Autowired
+    private RedisCache redisCache;
+    @Autowired
+    private FsUserIntegralLogsMapper fsUserIntegralLogsMapper;
+    @Autowired
+    private FsCouponMapper fsCouponMapper;
+    @Autowired
+    private FsUserCouponMapper fsUserCouponMapper;
 
 
     /**
@@ -283,6 +309,7 @@ public class FsCourseCheckinReceiveServiceImpl implements IFsCourseCheckinReceiv
         receive.setPrizeName(prize.getPrizeName());
         receive.setRedpacketAmount(prize.getRedpacketAmount());
         receive.setGoodsId(prize.getGoodsId());
+        receive.setGrandGiftId(prize.getGrandGiftId());
         receive.setReceiveStatus(0); // 待发放
         receive.setCreateTime(now);
         //创建批次号
@@ -295,7 +322,7 @@ public class FsCourseCheckinReceiveServiceImpl implements IFsCourseCheckinReceiv
         fsCourseCheckinReceiveMapper.insertFsCourseCheckinReceive(receive);
 
         // 7. 发放奖励
-        result = sendPrize(receive.getReceiveId(), fsCourseSendRewardUParam);
+        result = sendPrize(receive.getReceiveId(), fsCourseSendRewardUParam, prize.getFreeCouponId());
 
         return result;
     }
@@ -322,7 +349,7 @@ public class FsCourseCheckinReceiveServiceImpl implements IFsCourseCheckinReceiv
      * @return 结果
      */
     @Transactional
-    public Map<String, Object> sendPrize(Long receiveId, FsCourseSendRewardUParam param) {
+    public Map<String, Object> sendPrize(Long receiveId, FsCourseSendRewardUParam param, Long freeCouponId) {
         FsCourseCheckinReceive receive = fsCourseCheckinReceiveMapper.selectFsCourseCheckinReceiveByReceiveId(receiveId);
         if (receive == null || receive.getReceiveStatus() != 0) {
             return new HashMap<>();
@@ -349,19 +376,53 @@ public class FsCourseCheckinReceiveServiceImpl implements IFsCourseCheckinReceiv
                 // 红包类型 - 调用红包发放接口
                 success = sendRedpacket(receive, param, user);
             } else if (receive.getPrizeType() == 2) {
-                // 积分商品类型 - 直接返回标识,不实际发放
-                success = true; // 积分商品券标记为成功,由前端处理
-                result.put("prizeType", 2);
-                result.put("goodsId", receive.getGoodsId());
+                // 发放积分商品优惠券
+                FsCoupon coupon = fsCouponMapper.selectFsCouponByCouponId(freeCouponId);
+                if (coupon != null && coupon.getStatus() != 0) {
+                    int receiveCount = fsUserCouponMapper.checkReceive(param.getUserId(), coupon.getCouponId());
+                    if (receiveCount < coupon.getLimitCount()) {
+                        FsUserCoupon fsUserCoupon = new FsUserCoupon();
+                        fsUserCoupon.setCouponId(coupon.getCouponId());
+                        fsUserCoupon.setCouponCode("C"+System.currentTimeMillis());
+                        fsUserCoupon.setUserId(param.getUserId());
+                        fsUserCoupon.setBusinessType(4);
+                        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);
+
+                        //更新剩余数量
+                        fsCouponMapper.updateRemainCount(coupon.getCouponId());
+
+                        success = true;
+                        result.put("prizeType", 2);
+                        result.put("freeCouponId", fsUserCoupon.getId());
+                        result.put("goodsId", coupon.getFreeGoodsId());
+
+                        // 更新领取记录中的优惠券ID
+                        receive.setFreeCouponId(fsUserCoupon.getId());
+                        receive.setGoodsId(coupon.getFreeGoodsId());
+                    }
+                }
+            } else if (receive.getPrizeType() == 3) {
+                success = true;
+                result.put("prizeType", 3);
+                result.put("grandGiftId", receive.getGrandGiftId());
+                result.put("receiveId", receiveId);
             }
 
             if (success) {
-                if (receive.getPrizeType() == 1) {
-                    //红包类型
+                //红包类型, 积分商品免单券
+                if (receive.getPrizeType() == 1 || receive.getPrizeType() == 2) {
                     receive.setReceiveStatus(1); // 发放成功
                     receive.setReceiveTime(now);
                 }else {
-                    // 积分商品类型
                     receive.setReceiveStatus(0); // 待发放
                 }
                 result.put("success", true);
@@ -660,4 +721,305 @@ public class FsCourseCheckinReceiveServiceImpl implements IFsCourseCheckinReceiv
 
         return list;
     }
+
+    /**
+     * 获取大礼品奖品项
+     */
+    @Override
+    public JSONArray getRewardRules(FsCourseGrandGiftParam param) {
+        FsCourseCheckinReceive receive = fsCourseCheckinReceiveMapper.selectByUserIdAndReceiveIdAndGrandGiftId(param.getUserId(), param.getReceiveId(), param.getGrandGiftId());
+        if (receive == null) {
+            throw new CustomException("领取记录不存在!");
+        }
+
+        if (receive.getReceiveStatus() == 1) {
+            throw new CustomException("不可重复领取!");
+        }
+
+        FsCourseReward rewardConfig = fsCourseRewardMapper.selectFsCourseRewardById(receive.getGrandGiftId());
+        if (Objects.isNull(rewardConfig) || StringUtils.isBlank(rewardConfig.getActualRewards())) {
+            throw new CustomException("配置错误,请联系管理员");
+        }
+
+        JSONArray jsonArray = JSON.parseArray(rewardConfig.getActualRewards());
+        JSONArray array = new JSONArray(25);
+        Map<String, JSONObject> prizeMap = jsonArray.stream()
+                .map(o -> (JSONObject) o)
+                .collect(Collectors.toMap(
+                        o -> o.getString("code"),
+                        o -> o,
+                        (v1, v2) -> v1 // 如果 code 重复,保留第一个
+                ));
+        List<Prize> realPrizes = buildPrizes(jsonArray, true);
+        Prize luckyPrize = LotteryUtil.draw(realPrizes);
+        if (Objects.isNull(luckyPrize)) {
+            throw new CustomException("大礼品配置错误,请联系管理员#1");
+        }
+
+        // 缓存中奖记录
+        redisCache.setCacheObject(CourseConstant.REDIS_GRAND_GIFT + param.getUserId(), JSON.toJSONString(luckyPrize), 30, TimeUnit.MINUTES);
+        JSONObject luckyJson = prizeMap.get(luckyPrize.getCode());
+        if (luckyJson != null) {
+            array.add(luckyJson);
+        }
+
+        List<Prize> giftPrizes = buildPrizes(jsonArray, false);
+        for (int i = 0; i < 24; i++) {
+            Prize giftPrize = LotteryUtil.draw(giftPrizes);
+            if (Objects.isNull(giftPrize)) {
+                throw new CustomException("大礼品配置错误,请联系管理员#2");
+            }
+            JSONObject giftJson = prizeMap.get(giftPrize.getCode());
+            if (giftJson != null) {
+                array.add(giftJson);
+            }
+        }
+
+        Collections.shuffle(array);
+
+        return array;
+    }
+
+    /**
+     * 构建奖品列表
+     */
+    private List<Prize> buildPrizes(JSONArray jsonArray, boolean isReal) {
+        List<Prize> prizes = new ArrayList<>();
+
+        // 收集所有需要查询的商品ID,批量查询避免N+1问题
+        Set<Long> goodsIds = new HashSet<>();
+        for (Object o : jsonArray) {
+            try {
+                JSONObject json = (JSONObject) o;
+                String goodsId = json.getString("goodsId");
+                if (StringUtils.isNotBlank(goodsId)) {
+                    goodsIds.add(Long.parseLong(goodsId));
+                }
+            } catch (Exception e) {
+                // 忽略解析错误,在后续循环中处理
+            }
+        }
+
+        // 批量查询商品信息
+        Map<Long, FsRewardGoods> goodsMap = new HashMap<>();
+        if (!goodsIds.isEmpty()) {
+            List<FsRewardGoods> goodsList = rewardGoodsMapper.selectBatchIds(goodsIds);
+            goodsMap = goodsList.stream().collect(Collectors.toMap(FsRewardGoods::getGoodsId, g -> g));
+        }
+
+        for (Object o : jsonArray) {
+            try {
+                JSONObject json = (JSONObject) o;
+
+                String name = json.getString("name");
+                Integer type = json.getInteger("type");
+                String amount = json.getString("amount");
+                String code = json.getString("code");
+                String couponId = json.getString("couponId");
+                String goodsId = json.getString("goodsId");
+
+                if (StringUtils.isNotBlank(goodsId)) {
+                    FsRewardGoods fsRewardGoods = goodsMap.get(Long.parseLong(goodsId));
+                    if (Objects.isNull(fsRewardGoods) || fsRewardGoods.getStatus() == 0 || fsRewardGoods.getIsDel() == 1) {
+                        continue;
+                    }
+
+                    if (fsRewardGoods.getStock() <= 0 && isReal) {
+                        continue;
+                    }
+                }
+
+                // 安全解析概率
+                double probability = parseProbability(json.getString("probability"));
+                if (probability <= 0) {
+                    log.warn("奖品概率配置错误,跳过 {}", json);
+                    continue;
+                }
+
+                prizes.add(new Prize(type, amount, code, isReal ? probability : 1, couponId, goodsId, name));
+
+            } 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;
+        }
+    }
+
+    /**
+     * 领取大礼品奖励
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public R sendActiveReward(FsCourseGrandGiftParam param) {
+        FsCourseCheckinReceive receive = fsCourseCheckinReceiveMapper.selectByUserIdAndReceiveIdAndGrandGiftId(param.getUserId(), param.getReceiveId(), param.getGrandGiftId());
+        if (receive == null) {
+            throw new CustomException("领取记录不存在!");
+        }
+
+        if (receive.getReceiveStatus() == 1) {
+            throw new CustomException("不可重复领取!");
+        }
+
+        String prizeStr = redisCache.getCacheObject(CourseConstant.REDIS_GRAND_GIFT + param.getUserId());
+        if (StringUtils.isBlank(prizeStr)) {
+            throw new CustomException("请求超时,请刷新后重试!");
+        }
+        Prize prize = JSONObject.parseObject(prizeStr, Prize.class);
+
+        FsCourseReward rewardConfig = fsCourseRewardMapper.selectFsCourseRewardById(receive.getGrandGiftId());
+        FsUser fsUser = fsUserMapper.selectFsUserById(param.getUserId());
+
+        FsCourseRewardRound rewardRound = new FsCourseRewardRound();
+        rewardRound.setRewardId(rewardConfig.getId());
+        rewardRound.setCompanyId(receive.getCompanyId());
+        rewardRound.setUserId(fsUser.getUserId());
+        rewardRound.setRewardType(rewardConfig.getRewardType());
+        rewardRound.setActualRewards(prize.getAmount());
+        rewardRound.setCreateTime(LocalDateTime.now());
+        rewardRound.setWatchId(0L);
+        rewardRound.setStatus(1L);
+        rewardRound.setRuleId(prize.getCode());
+        rewardRound.setRuleName(prize.getCodeName());
+        fsCourseRewardRoundMapper.insertFsCourseRewardRound(rewardRound);
+
+        // 抽中奖励商品
+        FsRewardGoodsOrder fsRewardGoodsOrder = null;
+        if (prize.getType() == 5) {
+            FsRewardGoods fsRewardGoods = rewardGoodsMapper.selectById(Long.parseLong(prize.getGoodsId()));
+
+            // 减库存
+            int cnt = rewardGoodsMapper.reduceStock(fsRewardGoods.getGoodsId());
+            if (cnt <= 0) {
+                throw new CustomException("系统繁忙,请刷新后重试!");
+            }
+
+            // 创建订单
+            String orderSn = OrderCodeUtils.getOrderSn();
+            if (StringUtils.isEmpty(orderSn)) {
+                throw new CustomException("订单号生成失败,请重试!");
+            }
+
+            fsRewardGoodsOrder = new FsRewardGoodsOrder();
+            fsRewardGoodsOrder.setOrderSn(orderSn);
+            fsRewardGoodsOrder.setStoreId(fsRewardGoods.getStoreId());
+            fsRewardGoodsOrder.setUserId(fsUser.getUserId());
+            fsRewardGoodsOrder.setOrderMoney(fsRewardGoods.getPrice());
+            fsRewardGoodsOrder.setPayMoney(fsRewardGoods.getPrice());
+            fsRewardGoodsOrder.setRoundId(rewardRound.getId());
+            fsRewardGoodsOrder.setReceiveId(receive.getReceiveId());
+            fsRewardGoodsOrder.setGoodsId(fsRewardGoods.getGoodsId());
+            fsRewardGoodsOrder.setGoodsJson(JSON.toJSONString(fsRewardGoods));
+            fsRewardGoodsOrder.setStatus(1);
+            fsRewardGoodsOrder.setExpiredTime(LocalDateTime.now().plusDays(30));
+            fsRewardGoodsOrder.setCreateTime(LocalDateTime.now());
+            fsRewardGoodsOrder.setCreateBy(fsUser.getNickName());
+            fsRewardGoodsOrder.setRemark("实物抽奖");
+            rewardGoodsOrderMapper.insert(fsRewardGoodsOrder);
+        }
+
+        receive.setGoodsName(prize.getCodeName());
+        receive.setReceiveStatus(1);
+        receive.setGrandGiftCode(prize.getCode());
+        receive.setGrandGiftType(prize.getType());
+        receive.setReceiveTime(new Date());
+        fsCourseCheckinReceiveMapper.updateFsCourseCheckinReceive(receive);
+
+        switch (prize.getType()) {
+            case 1:
+                return R.error("不支持的奖励类型!");
+            case 2:
+                return sendIntegralReward(param, fsUser, new BigDecimal(prize.getAmount())).put("data", prize.getCode()).put("type", prize.getType());
+            case 3:
+                return R.ok().put("data", prize.getCode()).put("type", prize.getType());
+            case 4:
+                return sendCoupon(fsUser.getUserId(), prize.getCouponId(), Integer.parseInt(prize.getAmount()), 3).put("data", prize.getCode()).put("type", prize.getType());
+            case 5:
+                Map<String, Object> result = new HashMap<>();
+                result.put("data", prize.getCode());
+                result.put("order", fsRewardGoodsOrder);
+                result.put("type", prize.getType());
+                return R.ok(result);
+            default:
+                return R.error("配置错误,请联系管理员#5");
+        }
+    }
+
+    /**
+     * 发放积分奖励
+     *
+     * @param user   用户信息
+     * @return 处理结果
+     */
+    private R sendIntegralReward(FsCourseGrandGiftParam param, FsUser user, BigDecimal integral) {
+        // 更新用户积分
+        FsUser userMap = new FsUser();
+        userMap.setUserId(user.getUserId());
+        userMap.setIntegral(user.getIntegral() + integral.intValue());
+        fsUserMapper.updateFsUser(userMap);
+
+        // 记录积分日志
+        FsUserIntegralLogs integralLogs = new FsUserIntegralLogs();
+        integralLogs.setIntegral(integral.longValue());
+        integralLogs.setUserId(user.getUserId());
+        integralLogs.setBalance(userMap.getIntegral());
+        integralLogs.setLogType(FsUserIntegralLogTypeEnum.TYPE_33.getValue());
+        integralLogs.setBusinessId(param.getReceiveId().toString());
+        integralLogs.setCreateTime(new Date());
+        fsUserIntegralLogsMapper.insertFsUserIntegralLogs(integralLogs);
+
+        return R.ok("积分奖励发放成功");
+    }
+
+    /**
+     * 发送优惠券
+     */
+    private R sendCoupon(Long userId, String couponId, Integer num, Integer businessType) {
+        log.debug("发送优惠券 userId: {}, couponId: {}, num: {}", userId, 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(userId);
+        fsUserCoupon.setBusinessType(businessType);
+        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("优惠券奖励发放成功");
+    }
 }

+ 718 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsCourseRewardRoundServiceImpl.java

@@ -0,0 +1,718 @@
+package com.fs.course.service.impl;
+
+import cn.hutool.core.util.ObjectUtil;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.common.utils.StringUtils;
+import com.fs.company.vo.RedPacketMoneyVO;
+import com.fs.course.domain.*;
+import com.fs.course.mapper.*;
+import com.fs.course.param.FsCourseSendRewardUParam;
+import com.fs.course.service.IFsCourseRewardRoundService;
+import com.fs.course.service.IFsUserCourseVideoService;
+import com.fs.his.domain.FsUser;
+import com.fs.his.domain.FsUserIntegralLogs;
+import com.fs.his.domain.FsUserWx;
+import com.fs.his.mapper.FsUserMapper;
+import com.fs.his.param.WxSendRedPacketParam;
+import com.fs.his.service.IFsStorePaymentService;
+import com.fs.his.service.IFsUserIntegralLogsService;
+import com.fs.his.service.IFsUserWxService;
+import com.fs.reward.vo.FsRewardListVO;
+import com.fs.system.service.ISysConfigService;
+import com.fs.utils.Range;
+import org.apache.commons.collections4.CollectionUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+
+/**
+ * 奖励领取记录Service业务层处理
+ *
+ * @author 杨衍生
+ * @date 2025-09-02
+ */
+@Service
+public class FsCourseRewardRoundServiceImpl extends ServiceImpl<FsCourseRewardRoundMapper, FsCourseRewardRound> implements IFsCourseRewardRoundService {
+    @Autowired
+    private IFsUserCourseVideoService courseVideoService;
+    private static final Logger logger = LoggerFactory.getLogger(FsUserCourseVideoServiceImpl.class);
+    @Autowired
+    private RedisCache redisCache;
+    @Autowired
+    private IFsUserWxService fsUserWxService;
+    @Autowired
+    private FsUserCourseVideoMapper fsUserCourseVideoMapper;
+    @Autowired
+    private FsCourseWatchLogMapper courseWatchLogMapper;
+    @Autowired
+    private ISysConfigService configService;
+    @Autowired
+    private FsUserCourseVideoRedPackageMapper fsUserCourseVideoRedPackageMapper;
+    @Autowired
+    private IFsUserIntegralLogsService iFsUserIntegralLogsService;
+    @Autowired
+    private FsUserMapper fsUserMapper;
+    @Autowired
+    private IFsStorePaymentService paymentService;
+    @Autowired
+    private FsCourseRedPacketLogMapper redPacketLogMapper;
+    @Autowired
+    private FsCourseRewardMapper fsCourseRewardMapper;
+    @Autowired
+    private FsCourseRewardVideoRelationMapper fsCourseRewardVideoRelationMapper;
+
+    /**
+     * 查询奖励领取记录
+     *
+     * @param id 奖励领取记录主键
+     * @return 奖励领取记录
+     */
+    @Override
+    public FsCourseRewardRound selectFsCourseRewardRoundById(Long id)
+    {
+        return baseMapper.selectFsCourseRewardRoundById(id);
+    }
+
+    /**
+     * 查询奖励领取记录列表
+     *
+     * @param rewardRound 奖励领取记录
+     * @return 奖励领取记录
+     */
+    @Override
+    public List<FsCourseRewardRound> selectFsCourseRewardRoundList(FsCourseRewardRound rewardRound)
+    {
+        return baseMapper.selectFsCourseRewardRoundList(rewardRound);
+    }
+
+    /**
+     * 新增奖励领取记录
+     *
+     * @param rewardRound 奖励领取记录
+     * @return 结果
+     */
+    @Override
+    public int insertFsCourseRewardRound(FsCourseRewardRound rewardRound)
+    {
+        FsCourseWatchLog fsCourseWatchLog = courseWatchLogMapper.getWatchCourseVideo(
+                rewardRound.getUserId(), rewardRound.getVideoId(),rewardRound.getQwUserId(),rewardRound.getQwExternalId());
+        if (ObjectUtil.isNotEmpty(fsCourseWatchLog)){
+            rewardRound.setWatchId(fsCourseWatchLog.getLogId());
+            rewardRound.setCompanyId(fsCourseWatchLog.getCompanyId());
+        }
+        rewardRound.setCreateTime(LocalDateTime.now());
+        return baseMapper.insertFsCourseRewardRound(rewardRound);
+    }
+
+    /**
+     * 修改奖励领取记录
+     *
+     * @param rewardRound 奖励领取记录
+     * @return 结果
+     */
+    @Override
+    public int updateFsCourseRewardRound(FsCourseRewardRound rewardRound)
+    {
+        rewardRound.setUpdateTime(LocalDateTime.now());
+        return baseMapper.updateFsCourseRewardRound(rewardRound);
+    }
+
+    /**
+     * 批量删除奖励领取记录
+     *
+     * @param ids 需要删除的奖励领取记录主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsCourseRewardRoundByIds(Long[] ids)
+    {
+        return baseMapper.deleteFsCourseRewardRoundByIds(ids);
+    }
+
+    /**
+     * 删除奖励领取记录信息
+     *
+     * @param id 奖励领取记录主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsCourseRewardRoundById(Long id)
+    {
+        return baseMapper.deleteFsCourseRewardRoundById(id);
+    }
+
+    @Override
+    public R claim(FsCourseRewardRound rewardRound) {
+        Double inputSecond = Double.parseDouble(rewardRound.getSecond());
+        if (inputSecond == null) {
+            return R.error(400, "请输入有效时长");
+        }
+        FsCourseWatchLog fsCourseWatchLog = courseWatchLogMapper.getWatchCourseVideo(
+                rewardRound.getUserId(), rewardRound.getVideoId(),rewardRound.getQwUserId(),rewardRound.getQwExternalId());
+        if (fsCourseWatchLog == null) {
+            return R.error(502, "未找到观看记录");
+        }
+        String redisKey = "h5user:watch:duration:" + fsCourseWatchLog.getQwUserId()+ ":" + fsCourseWatchLog.getQwExternalContactId() + ":" + fsCourseWatchLog.getVideoId();
+        String videoRedisKey = "h5user:video:duration:" + fsCourseWatchLog.getVideoId();
+        String redisSecondStr = redisCache.getCacheObject(redisKey);
+        Long videoRedisKeyStr = redisCache.getCacheObject(videoRedisKey);
+//        String redisSecondStr = "304";
+//        String videoRedisKeyStr = "1000";
+        if (ObjectUtil.isEmpty(redisSecondStr) || ObjectUtil.isEmpty(videoRedisKeyStr)) {
+
+            return R.error(500, "系统配置错误,请稍后再试");
+        }
+        double range = 60.0;
+        double lowerBound = Long.parseLong(redisSecondStr) - range;
+        double upperBound = Long.parseLong(redisSecondStr) + range;
+        if (inputSecond < lowerBound || inputSecond > upperBound) {
+            return R.error(501, "您的观看时长不符合领取条件");
+        }
+        FsCourseRewardRound fsCourseRewardRound = new FsCourseRewardRound();
+        fsCourseRewardRound.setWatchId(fsCourseWatchLog.getLogId());
+        fsCourseRewardRound.setUserId(rewardRound.getUserId());
+        List<FsCourseRewardRound> fsCourseRewardRounds = baseMapper.selectFsCourseRewardRoundList(fsCourseRewardRound);
+
+        FsCourseRewardVideoRelation fsCourseRewardVideoRelation = new FsCourseRewardVideoRelation();
+        fsCourseRewardVideoRelation.setVideoSectionId(rewardRound.getVideoId());
+        fsCourseRewardVideoRelation.setCompanyId(rewardRound.getCompanyId());
+//        fsCourseRewardVideoRelation.setRewardId(rewardRound.getRewardId());
+        List<FsCourseRewardVideoRelation> fsCourseRewardVideoRelationList = fsCourseRewardVideoRelationMapper.selectFsCourseRewardVideoRelationList(fsCourseRewardVideoRelation);
+
+        if (CollectionUtils.isEmpty(fsCourseRewardVideoRelationList)){
+            return R.error(504, "没有配置领取规则,请联系客服!");
+        }
+        if (fsCourseRewardVideoRelationList.size()>1){
+            if (CollectionUtils.isNotEmpty(fsCourseRewardRounds) && fsCourseRewardRounds.size() >= 2) {
+                return R.error(504, "已经领取过两次了,不能领取了");
+            }
+        }else {
+            if (CollectionUtils.isNotEmpty(fsCourseRewardRounds) && fsCourseRewardRounds.size() >= 1) {
+                return R.error(504, "已经领取过了,不能领取了");
+            }
+        }
+
+
+        double actualPercentage = (inputSecond / videoRedisKeyStr) * 100;
+
+        String targetPercentage = findClosestPercentage(actualPercentage);
+        if (targetPercentage == null) {
+            return R.error(503, "您的观看时长不在任何奖励区间内");
+        }
+        FsCourseRewardVideoRelation fsCourseRewardVideoRelation1 = new FsCourseRewardVideoRelation();
+        fsCourseRewardVideoRelation1.setVideoSectionId(rewardRound.getVideoId());
+        fsCourseRewardVideoRelation1.setCompanyId(rewardRound.getCompanyId());
+        fsCourseRewardVideoRelation1.setRewardId(rewardRound.getRewardId());
+        fsCourseRewardVideoRelation1.setMark(targetPercentage+"%");
+        System.out.println("参数:"+fsCourseRewardVideoRelation1);
+        List<FsCourseRewardVideoRelation> fsCourseRewardVideoRelationList1 = fsCourseRewardVideoRelationMapper
+                .selectFsCourseRewardVideoRelationList(fsCourseRewardVideoRelation1);
+        FsCourseRewardVideoRelation fsCourseRewardVideoRelation2 = new FsCourseRewardVideoRelation();
+        if (CollectionUtils.isNotEmpty(fsCourseRewardVideoRelationList1)){
+            fsCourseRewardVideoRelation2 = fsCourseRewardVideoRelationList1.get(0);
+        }else {
+            return R.error(503, "当前时间段不能领取!");
+        }
+        FsCourseReward fsCourseReward = fsCourseRewardMapper.selectFsCourseRewardById(rewardRound.getRewardId());
+
+        rewardRound.setRewardType(fsCourseReward.getRewardType());
+        rewardRound.setRewardVideoRelationId(fsCourseRewardVideoRelation2.getId());
+        List<FsCourseRewardRound> rewardRounds = selectFsCourseRewardRoundList(rewardRound);
+        if (CollectionUtils.isNotEmpty(rewardRounds)){
+            return R.error(503, "已经领取过奖励了,不能在领取了");
+        }
+        if (ObjectUtil.isNotEmpty(rewardRound.getStatus())&&(!rewardRound.getStatus().equals(1L))){
+            rewardRound.setActualRewards("0");
+            insertFsCourseRewardRound(rewardRound);
+            return R.ok();
+        }
+        FsCourseSendRewardUParam param = new FsCourseSendRewardUParam();
+//        param.setCourseId(rewardRound.getCourseId());
+        param.setUserId(rewardRound.getUserId());
+        param.setVideoId(rewardRound.getVideoId());
+        param.setLinkType(rewardRound.getLinkType());
+        param.setRewardType(2);
+        param.setQwUserId(rewardRound.getQwUserId());
+        param.setQwExternalId(rewardRound.getQwExternalId());
+        Integer auctual = findIntegral(fsCourseReward.getActualRewards());
+        R result = sendReward(param,auctual);
+        rewardRound.setActualRewards(String.valueOf(auctual));
+        if (result.get("code").equals(200)){
+            insertFsCourseRewardRound(rewardRound);
+            return R.ok("芳华币+"+auctual);
+        }else {
+            return result;
+        }
+    }
+
+
+    /**
+     * 随机宝箱奖励数
+     *
+     * @return
+     */
+    private Integer findIntegral(String listString) {
+        List<Map> items = JSONObject.parseArray(listString, Map.class);
+
+        // 根据probability概率随机选择一个项
+        Map<String, Object> selectedItem = new HashMap<>();
+
+        // 1. 提取并转换概率值
+        List<Double> probabilities = new ArrayList<>();
+        double totalProbability = 0.0;
+
+        for (Map item : items) {
+            String probStr = (String) item.get("probability");
+            // 移除百分号并转换为小数
+            double prob = Double.parseDouble(probStr.replace("%", "")) / 100.0;
+            probabilities.add(prob);
+            totalProbability += prob;
+        }
+
+        // 2. 验证概率总和(应该是1.0,即100%)
+        if (Math.abs(totalProbability - 1.0) > 0.0001) {
+            for (int i = 0; i < probabilities.size(); i++) {
+                probabilities.set(i, probabilities.get(i) / totalProbability);
+            }
+        }
+
+        // 3. 生成随机数并选择
+        double random = Math.random();
+        double cumulativeProbability = 0.0;
+
+        for (int i = 0; i < probabilities.size(); i++) {
+            cumulativeProbability += probabilities.get(i);
+            if (random <= cumulativeProbability) {
+                selectedItem = items.get(i);
+                break;
+            }
+        }
+
+        Integer amount = (Integer) selectedItem.get("amount");
+        return amount;
+    }
+
+    /**
+     * 发放奖励
+     * @param param
+     * @return
+     */
+    private R sendReward(FsCourseSendRewardUParam param,Integer integral) {
+        FsUser user = fsUserMapper.selectFsUserByUserId(param.getUserId());
+        if (user.getStatus()==0){
+            return R.error("会员被停用,无权限,请联系客服!");
+        }
+        FsCourseWatchLog log = new FsCourseWatchLog();
+
+        //判断链接类型
+        if (param.getLinkType()!=null&&param.getLinkType()==1){
+            FsCourseRedPacketLog packetLog = redPacketLogMapper.selectFsCourseRedPacketLogByTemporary(param.getVideoId(),param.getUserId());
+            if (packetLog!=null){
+                return R.error("奖励已发放");
+            }
+        }else {
+            log = courseWatchLogMapper.getWatchCourseVideo(param.getUserId(),param.getVideoId(),param.getQwUserId(),param.getQwExternalId());
+            if (log==null){
+                return R.error("无记录");
+            }
+            if (log.getRewardType()!=null){
+                return R.error("奖励已发放");
+            }
+        }
+
+
+
+        FsUserCourseVideo video = fsUserCourseVideoMapper.selectFsUserCourseVideoByVideoId(param.getVideoId());
+        //发放奖励
+        switch (param.getRewardType()){
+            //红包奖励
+            case 1:
+                BigDecimal amount = BigDecimal.ZERO;
+                FsUserCourseVideoRedPackage redPackage = fsUserCourseVideoRedPackageMapper.selectRedPacketByCompanyId(param.getVideoId(), param.getCompanyId(), param.getPeriodId());
+                WxSendRedPacketParam packetParam = new WxSendRedPacketParam();
+
+                if (redPackage!=null){
+                    amount = redPackage.getRedPacketMoney();
+                }else if (video!=null){
+                    amount = video.getRedPacketMoney();
+                }
+                packetParam.setOpenId(user.getMpOpenId());
+                //来源是小程序切换openId
+                if (param.getSource()==2){
+                    FsUserWx fsUserWx = fsUserWxService.selectByAppIdAndUserId(param.getAppId(),user.getUserId(),1);
+                    if (fsUserWx ==null || fsUserWx.getOpenId()==null){
+                        packetParam.setOpenId(user.getCourseMaOpenId());
+                        try {
+                            handleFsUserWx(user,param.getAppId());
+                        }catch (Exception e){
+                            logger.error("zyp \n 【更新或插入用户与小程序的绑定关系失败】:{}", user.getUserId());
+                        }
+
+                    }else {
+                        packetParam.setOpenId(fsUserWx.getOpenId());
+                    }
+//                    packetParam.setOpenId(user.getCourseMaOpenId());
+                }
+                if (param.getAppId()!=null){
+                    packetParam.setAppId(param.getAppId());
+                }
+                packetParam.setAmount(amount);
+                packetParam.setSource(param.getSource());
+                R sendRedPacket = paymentService.sendRedPacket(packetParam);
+                //更改发送状态
+                if (param.getLinkType()==null || param.getLinkType()==0 || param.getLinkType()==3 || param.getLinkType()==5){
+                    log.setRewardType(param.getRewardType());
+                    courseWatchLogMapper.updateFsCourseWatchLog(log);
+                }
+                if (sendRedPacket.get("code").equals(200)){
+                    //添加红包记录
+                    FsCourseRedPacketLog redPacketLog = new FsCourseRedPacketLog();
+                    redPacketLog.setCourseId(param.getCourseId());
+                    redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
+                    redPacketLog.setBatchId(sendRedPacket.get("batchId").toString());
+                    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.setAppId(param.getAppId());
+                    redPacketLogMapper.insertFsCourseRedPacketLog(redPacketLog);
+                    return R.ok("奖励发放成功");
+                }
+                else {
+                    return R.error("奖励发放失败!");
+                }
+                //积分奖励
+            case 2:
+                //增加积分
+                FsUser userMap=new FsUser();
+                userMap.setUserId(user.getUserId());
+                userMap.setIntegral(user.getIntegral()+integral);
+                // 增加可提现积分
+                userMap.setWithdrawIntegral(user.getWithdrawIntegral() + integral);
+                fsUserMapper.updateFsUser(userMap);
+                FsUserIntegralLogs integralLogs = new FsUserIntegralLogs();
+                integralLogs.setIntegral(integral.longValue());
+                integralLogs.setUserId(user.getUserId());
+                integralLogs.setBalance(userMap.getIntegral());
+                integralLogs.setLogType(22);
+                integralLogs.setBusinessId(StringUtils.isNotEmpty(log.getLogId().toString()) ? log.getLogId().toString() : null);
+                integralLogs.setCreateTime(new Date());
+                integralLogs.setNickName(user.getNickName());
+                integralLogs.setPhone(user.getPhone());
+                //integralLogs.setId(integralLogsService.getFsUserIntegralLogsInsertId());
+//                fsUserIntegralLogsMapper.insertFsUserIntegralLogs(integralLogs);
+                iFsUserIntegralLogsService.insertFsUserIntegralLogs(integralLogs);
+
+                return R.ok("奖励发放成功");
+            default:
+                return R.error("参数错误!");
+        }
+    }
+
+    /**
+     * 处理用户与小程序的绑定
+     */
+    private void handleFsUserWx(FsUser user,String appId) {
+        FsUserWx fsUserWx = new FsUserWx();
+        fsUserWx.setType(1);
+        fsUserWx.setFsUserId(user.getUserId());
+        fsUserWx.setAppId(appId);
+        fsUserWx.setOpenId(user.getCourseMaOpenId());
+        fsUserWx.setUnionId(user.getUnionId());
+        fsUserWx.setCreateTime(new Date());
+        fsUserWx.setUpdateTime(new Date());
+        fsUserWxService.saveOrUpdateByUniqueKey(fsUserWx);
+
+        logger.info("zyp \n 【更新或插入用户与小程序{}的绑定关系】:{}", appId, user.getUserId());
+
+    }
+
+
+    @Override
+    public R isClaim(FsCourseRewardRound rewardRound) {
+        Double inputSecond = Double.parseDouble(rewardRound.getSecond());
+        if (inputSecond == null) {
+            return R.error(400, "请输入有效时长");
+        }
+        FsCourseWatchLog fsCourseWatchLog = courseWatchLogMapper.getWatchCourseVideo(
+                rewardRound.getUserId(), rewardRound.getVideoId(),rewardRound.getQwUserId(),rewardRound.getQwExternalId());
+        if (fsCourseWatchLog == null) {
+            return R.error(502, "未找到观看记录");
+        }
+        String redisKey = "h5user:watch:duration:" + fsCourseWatchLog.getQwUserId()+ ":" + fsCourseWatchLog.getQwExternalContactId() + ":" + fsCourseWatchLog.getVideoId();
+        String videoRedisKey = "h5user:video:duration:" + fsCourseWatchLog.getVideoId();
+        String redisSecondStr = redisCache.getCacheObject(redisKey);
+        Long videoRedisKeyStr = redisCache.getCacheObject(videoRedisKey);
+//        String redisSecondStr = "304";
+//        String videoRedisKeyStr = "1000";
+        if (ObjectUtil.isEmpty(redisSecondStr) || ObjectUtil.isEmpty(videoRedisKeyStr)) {
+
+            return R.error(500, "系统配置错误,请稍后再试");
+        }
+        double range = 60.0;
+        double lowerBound = Long.parseLong(redisSecondStr) - range;
+        double upperBound = Long.parseLong(redisSecondStr) + range;
+        if (inputSecond < lowerBound || inputSecond > upperBound) {
+            return R.error(501, "您的观看时长不符合领取条件");
+        }
+        FsCourseRewardRound fsCourseRewardRound = new FsCourseRewardRound();
+        fsCourseRewardRound.setWatchId(fsCourseWatchLog.getLogId());
+        fsCourseRewardRound.setUserId(rewardRound.getUserId());
+        List<FsCourseRewardRound> fsCourseRewardRounds = baseMapper.selectFsCourseRewardRoundList(fsCourseRewardRound);
+
+        FsCourseRewardVideoRelation fsCourseRewardVideoRelation = new FsCourseRewardVideoRelation();
+        fsCourseRewardVideoRelation.setVideoSectionId(rewardRound.getVideoId());
+        fsCourseRewardVideoRelation.setCompanyId(rewardRound.getCompanyId());
+//        fsCourseRewardVideoRelation.setRewardId(rewardRound.getRewardId());
+        List<FsCourseRewardVideoRelation> fsCourseRewardVideoRelationList = fsCourseRewardVideoRelationMapper.selectFsCourseRewardVideoRelationList(fsCourseRewardVideoRelation);
+
+        if (CollectionUtils.isEmpty(fsCourseRewardVideoRelationList)){
+            return R.error(504, "没有配置领取规则,请联系客服!");
+        }
+        if (fsCourseRewardVideoRelationList.size()>1){
+            if (CollectionUtils.isNotEmpty(fsCourseRewardRounds) && fsCourseRewardRounds.size() >= 2) {
+                return R.error(504, "已经领取过两次了,不能领取了");
+            }
+        }else {
+            if (CollectionUtils.isNotEmpty(fsCourseRewardRounds) && fsCourseRewardRounds.size() >= 1) {
+                return R.error(504, "已经领取过了,不能领取了");
+            }
+        }
+
+
+        double actualPercentage = (inputSecond / videoRedisKeyStr) * 100;
+
+        String targetPercentage = findClosestPercentage(actualPercentage);
+        if (targetPercentage == null) {
+            return R.error(503, "您的观看时长不在任何奖励区间内");
+        }
+        FsCourseRewardVideoRelation fsCourseRewardVideoRelation1 = new FsCourseRewardVideoRelation();
+        fsCourseRewardVideoRelation1.setVideoSectionId(rewardRound.getVideoId());
+        fsCourseRewardVideoRelation1.setCompanyId(rewardRound.getCompanyId());
+        fsCourseRewardVideoRelation1.setRewardId(rewardRound.getRewardId());
+        fsCourseRewardVideoRelation1.setMark(targetPercentage+"%");
+        System.out.println("参数:"+fsCourseRewardVideoRelation1);
+        List<FsCourseRewardVideoRelation> fsCourseRewardVideoRelationList1 = fsCourseRewardVideoRelationMapper
+                .selectFsCourseRewardVideoRelationList(fsCourseRewardVideoRelation1);
+        FsCourseRewardVideoRelation fsCourseRewardVideoRelation2 = new FsCourseRewardVideoRelation();
+        if (CollectionUtils.isNotEmpty(fsCourseRewardVideoRelationList1)){
+            fsCourseRewardVideoRelation2 = fsCourseRewardVideoRelationList1.get(0);
+        }else {
+            return R.error(503, "当前时间段不能领取!");
+        }
+        FsCourseReward fsCourseReward = fsCourseRewardMapper.selectFsCourseRewardById(rewardRound.getRewardId());
+
+        rewardRound.setRewardType(fsCourseReward.getRewardType());
+        rewardRound.setRewardVideoRelationId(fsCourseRewardVideoRelation2.getId());
+        List<FsCourseRewardRound> rewardRounds = selectFsCourseRewardRoundList(rewardRound);
+        if (CollectionUtils.isNotEmpty(rewardRounds)){
+            return R.error(503, "已经领取过奖励了,不能在领取了");
+        }
+        return R.ok();
+    }
+
+
+    // 辅助方法:查找最接近的配置百分比
+    private String findClosestPercentage(double actualPercentage) {
+        Map<String, Range<Double>> percentageRanges = new LinkedHashMap<>();
+        percentageRanges.put("10", Range.between(5.0, 15.0));
+        percentageRanges.put("20", Range.between(15.0, 25.0));
+        percentageRanges.put("30", Range.between(25.0, 35.0));
+        percentageRanges.put("40", Range.between(35.0, 45.0));
+        percentageRanges.put("50", Range.between(45.0, 55.0));
+        percentageRanges.put("70", Range.between(65.0, 75.0));
+        percentageRanges.put("80", Range.between(75.0, 85.0));
+
+        for (Map.Entry<String, Range<Double>> entry : percentageRanges.entrySet()) {
+            if (entry.getValue().contains(actualPercentage)) {
+                return entry.getKey();
+            }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * 同步公司奖励金额(按状态区分初始化与每日更新)
+     *
+     * @param status 0=初始化统计(统计截止昨日23:59:59之前所有数据)
+     *               1=每日统计(统计昨日00:00:00 - 昨日23:59:59的数据)
+     * @return 按公司统计的红包金额列表
+     */
+    @Override
+    public List<RedPacketMoneyVO> syncUpdatedCompanyAmount(Integer status) {
+        // 定义时间变量
+        LocalDateTime startTime = null;
+        LocalDateTime endTime;
+        String start = null;
+        String end = null;
+
+        // 获取Redis缓存标识
+        Long kcdate = redisCache.getCacheObject("syncUpdatedCompanyAmount");
+
+        // 定义日期格式化器
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+        // 校验入参
+        if (status == null) {
+            logger.warn("【syncUpdatedCompanyAmount】status参数为空,默认执行每日统计逻辑。");
+            status = 1;
+        }
+
+        try {
+            if (status.equals(0)
+                    && ObjectUtil.isEmpty(kcdate)
+            ) {
+                // 初始化逻辑:统计截止到昨天23:59:59前的所有数据
+                LocalDate yesterdayEnd = LocalDate.now().minusDays(1);
+                endTime = yesterdayEnd.atTime(23, 59, 59);
+                end = endTime.format(formatter);
+
+                logger.info("【初始化公司奖励金额统计】截止时间:{}", end);
+
+                List<RedPacketMoneyVO> resultList = baseMapper.selectFsCourseRewardRoundByAmount(null, end);
+                logger.info("【初始化公司奖励金额统计】共统计公司数:{}",
+                        resultList != null ? resultList.size() : 0);
+                return Optional.ofNullable(resultList).orElse(Collections.emptyList());
+
+            } else {
+                // 每日统计逻辑:统计昨天整天的数据
+                LocalDate yesterday = LocalDate.now().minusDays(1);
+                startTime = yesterday.atStartOfDay();
+                endTime = yesterday.atTime(23, 59, 59);
+
+                start = startTime.format(formatter);
+                end = endTime.format(formatter);
+
+                logger.info("【每日公司奖励金额统计】统计时间范围:{} - {}", start, end);
+
+                List<RedPacketMoneyVO> resultList = baseMapper.selectFsCourseRewardRoundByAmount(start, end);
+                logger.info("【每日公司奖励金额统计】共统计公司数:{}",
+                        resultList != null ? resultList.size() : 0);
+                return Optional.ofNullable(resultList).orElse(Collections.emptyList());
+            }
+        } catch (Exception e) {
+            logger.error("【公司奖励金额统计异常】status={}, start={}, end={}, error={}",
+                    status, start, end, e.getMessage(), e);
+            return Collections.emptyList();
+        }
+    }
+
+    /**
+     * 同步公司奖励金额(按状态区分初始化与每日更新) 福袋
+     *
+     * @param status 0=初始化统计(统计截止昨日23:59:59之前所有数据)
+     *               1=每日统计(统计昨日00:00:00 - 昨日23:59:59的数据)
+     * @return 按公司统计的红包金额列表
+     */
+    @Override
+    public List<RedPacketMoneyVO> syncUpdatedCompanyAmountForLuckyBag(Integer status) {
+        // 定义时间变量
+        LocalDateTime startTime = null;
+        LocalDateTime endTime;
+        String start = null;
+        String end = null;
+
+        // 获取Redis缓存标识
+        Long kcdate = redisCache.getCacheObject("syncUpdatedCompanyAmountForLuckyBag");
+
+        // 定义日期格式化器
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+        // 校验入参
+        if (status == null) {
+            logger.warn("【syncUpdatedCompanyAmountForLuckyBag】status参数为空,默认执行每日统计逻辑。");
+            status = 1;
+        }
+
+        try {
+            if (status.equals(0)
+                    && ObjectUtil.isEmpty(kcdate)
+            ) {
+                // 初始化逻辑:统计截止到昨天23:59:59前的所有数据
+                LocalDate yesterdayEnd = LocalDate.now().minusDays(1);
+                endTime = yesterdayEnd.atTime(23, 59, 59);
+                end = endTime.format(formatter);
+
+                logger.info("【初始化公司福袋奖励金额统计】截止时间:{}", end);
+
+                List<RedPacketMoneyVO> resultList = baseMapper.selectFsCourseRewardRoundByAmountForLuckyBag(null, end);
+                logger.info("【初始化公司福袋奖励金额统计】共统计公司数:{}",
+                        resultList != null ? resultList.size() : 0);
+                return Optional.ofNullable(resultList).orElse(Collections.emptyList());
+
+            } else {
+                // 每日统计逻辑:统计昨天整天的数据
+                LocalDate yesterday = LocalDate.now().minusDays(1);
+                startTime = yesterday.atStartOfDay();
+                endTime = yesterday.atTime(23, 59, 59);
+
+                start = startTime.format(formatter);
+                end = endTime.format(formatter);
+
+                logger.info("【每日公司福袋奖励金额统计】统计时间范围:{} - {}", start, end);
+
+                List<RedPacketMoneyVO> resultList = baseMapper.selectFsCourseRewardRoundByAmountForLuckyBag(start, end);
+                logger.info("【每日公司福袋奖励金额统计】共统计公司数:{}",
+                        resultList != null ? resultList.size() : 0);
+                return Optional.ofNullable(resultList).orElse(Collections.emptyList());
+            }
+        } catch (Exception e) {
+            logger.error("【公司奖励福袋金额统计异常】status={}, start={}, end={}, error={}",
+                    status, start, end, e.getMessage(), e);
+            return Collections.emptyList();
+        }
+    }
+
+    /**
+     * 查询1w条指定状态且小于指定时间的数据
+     */
+    @Override
+    public List<FsCourseRewardRound> get1kByStatusAndLtDate(int status, LocalDate endTime) {
+        return baseMapper.get1kByStatusAndLtDate(status, endTime);
+    }
+
+    /**
+     * 查询用户中奖记录
+     */
+    @Override
+    public List<FsRewardListVO> getRewardListByUserId(Long userId) {
+        List<FsRewardListVO> rewardList = baseMapper.getRewardListByUserId(userId);
+        rewardList.forEach(reward -> {
+            if (StringUtils.isNotBlank(reward.getCodeName())) {
+                reward.setContent(reward.getCodeName());
+                return;
+            }
+            String content = "-";
+            try {
+                JSONArray array = JSONObject.parseArray(reward.getContent());
+                for (Object item : array) {
+                    JSONObject json = (JSONObject) item;
+                    if (json.getString("code").equals(reward.getCode())) {
+                        content = json.getString("name");
+                    }
+                }
+            } catch (Exception ignore) {}
+            reward.setContent(content);
+        });
+        return rewardList;
+    }
+
+}

+ 225 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsCourseRewardServiceImpl.java

@@ -0,0 +1,225 @@
+package com.fs.course.service.impl;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.exception.ServiceException;
+import com.fs.common.utils.StringUtils;
+import com.fs.course.domain.FsCourseReward;
+import com.fs.course.enums.FsCourseRewardTypeEnum;
+import com.fs.course.mapper.FsCourseRewardMapper;
+import com.fs.course.param.FsCourseActualRewardsParam;
+import com.fs.course.service.IFsCourseRewardService;
+import com.fs.his.domain.FsCoupon;
+import com.fs.his.mapper.FsCouponMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 奖励配置Service业务层处理
+ *
+ * @author 杨衍生
+ * @date 2025-09-02
+ */
+@Service
+public class FsCourseRewardServiceImpl extends ServiceImpl<FsCourseRewardMapper, FsCourseReward> implements IFsCourseRewardService {
+
+    @Autowired
+    private FsCouponMapper fsCouponMapper;
+
+    /**
+     * 查询奖励配置
+     *
+     * @param id 奖励配置主键
+     * @return 奖励配置
+     */
+    @Override
+    public FsCourseReward selectFsCourseRewardById(Long id)
+    {
+        return baseMapper.selectFsCourseRewardById(id);
+    }
+
+    /**
+     * 查询奖励配置列表
+     *
+     * @param fsCourseReward 奖励配置
+     * @return 奖励配置
+     */
+    @Override
+    public List<FsCourseReward> selectFsCourseRewardList(FsCourseReward fsCourseReward)
+    {
+        return baseMapper.selectFsCourseRewardList(fsCourseReward);
+    }
+
+    /**
+     * 新增奖励配置
+     *
+     * @param fsCourseReward 奖励配置
+     * @return 结果
+     */
+    @Override
+    public int insertFsCourseReward(FsCourseReward fsCourseReward)
+    {
+        checkParam(fsCourseReward);
+        fsCourseReward.setCreateTime(LocalDateTime.now());
+
+        return baseMapper.insertFsCourseReward(fsCourseReward);
+    }
+
+    /**
+     * 计算成本
+     */
+    private void checkParam(FsCourseReward fsCourseReward) {
+        FsCourseRewardTypeEnum rewardTypeEnum = FsCourseRewardTypeEnum.getByValue(fsCourseReward.getRewardType());
+        JSONObject jsonObject;
+        switch (rewardTypeEnum) {
+            case TYPE_1:
+            case TYPE_4:
+            case TYPE_5:
+            case TYPE_6:
+                List<FsCourseActualRewardsParam> actualRewardsParams = JSONArray.parseArray(fsCourseReward.getActualRewards(), FsCourseActualRewardsParam.class);
+                if (actualRewardsParams.isEmpty()) {
+                    throw new ServiceException("配置不能为空");
+                }
+
+                // 检查宝箱图片
+                if (rewardTypeEnum == FsCourseRewardTypeEnum.TYPE_1 &&
+                        (StringUtils.isBlank(fsCourseReward.getOpenChestUrl()) || StringUtils.isBlank(fsCourseReward.getCloseChestUrl()))) {
+                    throw new ServiceException("配置错误,宝箱开启或关闭图片不能为空");
+                }
+
+                int guaranteeSum = 0;
+                for (FsCourseActualRewardsParam actualRewardsParam : actualRewardsParams) {
+                    // 检查百分比配置
+                    if (!isValidPercentage(actualRewardsParam.getProbability())) {
+                        throw new ServiceException("配置百分比概率错误或格式不正确");
+                    }
+
+                    // 检查icon和name
+                    if (rewardTypeEnum != FsCourseRewardTypeEnum.TYPE_1 &&
+                            (StringUtils.isBlank(actualRewardsParam.getIconUrl()) || StringUtils.isBlank(actualRewardsParam.getName()))) {
+                        throw new ServiceException("配置错误,icon或name不能为空");
+                    }
+
+                    // 奖励数量检查
+                    if (rewardTypeEnum != FsCourseRewardTypeEnum.TYPE_1 &&
+                            actualRewardsParam.getType() != 3 && StringUtils.isBlank(actualRewardsParam.getAmount())) {
+                        throw new ServiceException("配置错误,奖励数量不能为空");
+                    }
+
+                    // 优惠券检查
+                    if (Objects.nonNull(actualRewardsParam.getType()) && actualRewardsParam.getType() == 4 ||
+                            Objects.nonNull(actualRewardsParam.getJltype()) && actualRewardsParam.getJltype() == 3) {
+
+                        if (StringUtils.isBlank(actualRewardsParam.getCouponId())) {
+                            throw new ServiceException("配置错误,优惠券不能为空");
+                        }
+
+                        FsCoupon coupon = fsCouponMapper.selectFsCouponByCouponId(Long.parseLong(actualRewardsParam.getCouponId()));
+                        //不存在
+                        if (coupon == null) {
+                            throw new ServiceException("配置错误,优惠券不存在");
+                        }
+
+                        //停用
+                        if (coupon.getStatus()==0) {
+                            throw new ServiceException("配置错误,优惠券不存在");
+                        }
+                    }
+
+                    guaranteeSum += Objects.isNull(actualRewardsParam.getIsGuarantee()) ? 0 : actualRewardsParam.getIsGuarantee();
+                }
+
+                // 检查保底
+                if (rewardTypeEnum == FsCourseRewardTypeEnum.TYPE_5 && guaranteeSum > 1) {
+                    throw new ServiceException("配置错误,保底配置不正确");
+                }
+                break;
+            case TYPE_2:
+                jsonObject = JSONObject.parseObject(fsCourseReward.getActualRewards());
+                BigDecimal amount = jsonObject.getBigDecimal("amount");
+                if (Objects.isNull(amount)) {
+                    throw new ServiceException("红包金额不能为空");
+                }
+                break;
+            case TYPE_3:
+                jsonObject = JSONObject.parseObject(fsCourseReward.getActualRewards());
+                BigDecimal points = jsonObject.getBigDecimal("points");
+                if (points == null) {
+                    throw new ServiceException("积分数量不能为空");
+                }
+                break;
+            default:
+                throw new ServiceException("类型错误");
+        }
+    }
+
+    /**
+     * 百分比校验
+     */
+    public boolean isValidPercentage(String percentage) {
+        if (percentage == null) {
+            return false;
+        }
+
+        try {
+            String value = percentage.trim().replace("%", "");
+            BigDecimal number = new BigDecimal(value);
+            return number.compareTo(BigDecimal.ZERO) >= 0;
+        } catch (NumberFormatException e) {
+            return false;
+        }
+    }
+
+    /**
+     * 修改奖励配置
+     *
+     * @param fsCourseReward 奖励配置
+     * @return 结果
+     */
+    @Override
+    public int updateFsCourseReward(FsCourseReward fsCourseReward)
+    {
+        checkParam(fsCourseReward);
+        fsCourseReward.setUpdateTime(LocalDateTime.now());
+        return baseMapper.updateFsCourseReward(fsCourseReward);
+    }
+
+    /**
+     * 批量删除奖励配置
+     *
+     * @param ids 需要删除的奖励配置主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsCourseRewardByIds(Long[] ids)
+    {
+        return baseMapper.deleteFsCourseRewardByIds(ids);
+    }
+
+    /**
+     * 删除奖励配置信息
+     *
+     * @param id 奖励配置主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsCourseRewardById(Long id)
+    {
+        return baseMapper.deleteFsCourseRewardById(id);
+    }
+
+    /**
+     * 根据id集合查询奖励配置
+     */
+    @Override
+    public List<FsCourseReward> selectFsCourseRewardByIds(List<Long> ids) {
+        return baseMapper.selectFsCourseRewardByIds(ids);
+    }
+
+}

+ 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);
+    }
+}

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

@@ -0,0 +1,87 @@
+package com.fs.course.utils.luckyDraw;
+
+
+public class Prize {
+
+    private Integer type;
+    private String amount;
+    private String code;
+    private String codeName;
+    private double probability;
+    private String couponId;
+    private String goodsId;
+
+    public Prize(Integer type, String amount, String code, double probability, String couponId, String goodsId, String codeName) {
+        this.type = type;
+        this.amount = amount;
+        this.code = code;
+        this.probability = probability;
+        this.couponId = couponId;
+        this.goodsId = goodsId;
+        this.codeName = codeName;
+    }
+
+    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;
+    }
+
+    public String getCodeName() {
+        return codeName;
+    }
+
+    public void setCodeName(String codeName) {
+        this.codeName = codeName;
+    }
+}

+ 1 - 1
fs-service/src/main/java/com/fs/course/vo/CourseCheckinResultVO.java

@@ -29,7 +29,7 @@ public class CourseCheckinResultVO {
     @ApiModelProperty("奖品名称")
     private String prizeName;
 
-    @ApiModelProperty("奖品类型:1-红包,2-积分商品券")
+    @ApiModelProperty("奖品类型:1-红包,2-积分商品券, 3-大礼品")
     private Integer prizeType;
 
     @ApiModelProperty("活动ID")

+ 15 - 0
fs-service/src/main/java/com/fs/erp/service/IErpOrderService.java

@@ -40,5 +40,20 @@ public interface IErpOrderService
     void getOrderScrmDeliveryStatus(FsStoreOrderScrm order);
     void getOrderLiveDeliveryStatus(LiveOrder order);
     ErpOrderResponse batchAddIntegralCartOrder(List<ErpOrder> erpOrders) throws InterruptedException;
+
+    /**
+     * 推送奖品订单
+     */
+    default ErpOrderResponse batchAddRewardOrder(List<ErpOrder> erpOrders) throws InterruptedException { return null; }
+
+    /**
+     * 取消奖品订单
+     */
+    default BaseResponse cancelRewardOrder(ErpRefundUpdateRequest param) { return null; }
+
+    /**
+     * 获取奖品订单
+     */
+    default ErpOrderQueryResponse getRewardOrder(ErpOrderQueryRequert request) { return null; }
 }
 

+ 259 - 0
fs-service/src/main/java/com/fs/erp/service/impl/JSTErpOrderServiceImpl.java

@@ -6,6 +6,8 @@ import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.alibaba.fastjson.TypeReference;
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
@@ -39,6 +41,9 @@ import com.fs.live.domain.LiveOrder;
 import com.fs.live.domain.LiveOrderItem;
 import com.fs.live.mapper.LiveOrderItemMapper;
 import com.fs.live.mapper.LiveOrderMapper;
+import com.fs.reward.domain.FsRewardGoods;
+import com.fs.reward.domain.FsRewardGoodsOrder;
+import com.fs.reward.mapper.FsRewardGoodsOrderMapper;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.ObjectUtils;
 import org.apache.http.util.Asserts;
@@ -48,6 +53,7 @@ import org.springframework.util.CollectionUtils;
 
 import java.math.BigDecimal;
 import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -103,6 +109,8 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
 
     @Autowired
     private FsIntegralGoodsMapper fsIntegralGoodsMapper;
+    @Autowired
+    private FsRewardGoodsOrderMapper fsRewardGoodsOrderMapper;
 
 
 
@@ -1378,5 +1386,256 @@ public class JSTErpOrderServiceImpl implements IErpOrderService {
         erpOrderResponse.setCode(String.valueOf(orderData.getOId()));
         return erpOrderResponse;
     }
+
+    /**
+     * 推送奖品订单
+     */
+    @Override
+    public ErpOrderResponse batchAddRewardOrder(List<ErpOrder> erpOrders) throws InterruptedException {
+        List<ShopOrderDTO> normalOrders = new ArrayList<>();
+        for (ErpOrder order : erpOrders) {
+            Wrapper<FsRewardGoodsOrder> queryWrapper = Wrappers.<FsRewardGoodsOrder>lambdaQuery()
+                    .eq(FsRewardGoodsOrder::getOrderSn, order.getPlatform_code());
+            FsRewardGoodsOrder rewardGoodsOrder = fsRewardGoodsOrderMapper.selectOne(queryWrapper);
+
+            ShopOrderDTO shopOrderDTO = new ShopOrderDTO();
+            shopOrderDTO.setShopId(Long.valueOf(order.getShop_code()));
+            shopOrderDTO.setSoId(order.getPlatform_code());
+            shopOrderDTO.setOrderDate(order.getDeal_datetime());
+            shopOrderDTO.setShopStatus(OrderStatusEnum.WAIT_SELLER_SEND_GOODS.name());
+            shopOrderDTO.setShopBuyerId(order.getBuyer_account());
+            shopOrderDTO.setReceiverState(order.getReceiver_province());
+            shopOrderDTO.setReceiverCity(order.getReceiver_city());
+            shopOrderDTO.setReceiverDistrict(order.getReceiver_district());
+            shopOrderDTO.setReceiverAddress(order.getReceiver_address());
+            shopOrderDTO.setReceiverName(order.getReceiver_name());
+            shopOrderDTO.setReceiverPhone(order.getReceiver_mobile());
+            shopOrderDTO.setPayAmount(rewardGoodsOrder.getPayMoney().doubleValue());
+            shopOrderDTO.setRemark(order.getRemark());
+            shopOrderDTO.setFreight(0.0);
+            shopOrderDTO.setBuyerMessage(order.getBuyer_memo());
+
+            // 订单商品项列表
+            FsRewardGoods fsRewardGoods = JSON.parseObject(rewardGoodsOrder.getGoodsJson(), FsRewardGoods.class);
+            List<OrderItemDTO> itemDTOList = new ArrayList<>();
+            OrderItemDTO orderItemDTO = new OrderItemDTO();
+            orderItemDTO.setSkuId(fsRewardGoods.getStoreGoodsSn());
+            orderItemDTO.setShopSkuId(fsRewardGoods.getStoreGoodsSn());
+            orderItemDTO.setName(fsRewardGoods.getGoodsName());
+            orderItemDTO.setShopIId(orderItemDTO.getSkuId());
+            orderItemDTO.setPropertiesValue("默认");
+            orderItemDTO.setAmount(fsRewardGoods.getPrice());
+            orderItemDTO.setPic(fsRewardGoods.getGoodsImg());
+            orderItemDTO.setPrice(fsRewardGoods.getPrice());
+            orderItemDTO.setQty(1);
+            orderItemDTO.setOuterOiId(String.format("%s%s", rewardGoodsOrder.getOrderSn(), rewardGoodsOrder.getRoundId()));
+            itemDTOList.add(orderItemDTO);
+            shopOrderDTO.setItems(itemDTOList);
+
+            // 实际支付金额
+            PaymentDTO paymentDTO = new PaymentDTO();
+            paymentDTO.setAmount(rewardGoodsOrder.getPayMoney().doubleValue());
+            paymentDTO.setOuterPayId(order.getPlatform_code());
+            paymentDTO.setPayDate(order.getDeal_datetime());
+            paymentDTO.setPayment("微信支付");
+            paymentDTO.setBuyerAccount(order.getBuyer_account());
+            paymentDTO.setSellerAccount("平台销售");
+            shopOrderDTO.setPay(paymentDTO);
+
+            normalOrders.add(shopOrderDTO);
+        }
+
+        // 批量推送普通订单(每50条一批)
+        int batchSize = 50;  // 修正为50
+        ErpOrderResponseDTO lastResponse = null;
+        // 推送普通订单
+        for (int i = 0; i < normalOrders.size(); i += batchSize) {
+            int endIndex = Math.min(i + batchSize, normalOrders.size());
+            List<ShopOrderDTO> batch = new ArrayList<>(normalOrders.subList(i, endIndex));
+            lastResponse = jstErpHttpService.batchUpload(batch);
+            List<ErpOrderResponseDTO.OrderData> datas = lastResponse.getDatas();
+            for (ErpOrderResponseDTO.OrderData data : datas) {
+                if (data!=null&&data.getIssuccess()) {
+                    Wrapper<FsRewardGoodsOrder> queryWrapper = Wrappers.<FsRewardGoodsOrder>lambdaQuery()
+                            .eq(FsRewardGoodsOrder::getOrderSn, data.getSoId());
+                    FsRewardGoodsOrder rewardGoodsOrder = fsRewardGoodsOrderMapper.selectOne(queryWrapper);
+                    //普通订单
+                    if (rewardGoodsOrder!=null&&StringUtils.isEmpty(rewardGoodsOrder.getExtendOrderSn())){
+                        //支付成功后 将订单号写入待发货的REDIS中
+                        redisCache.setCacheObject("delivery" + ":" + data.getOId(), rewardGoodsOrder.getOrderSn());
+                        //写入外部订单号
+                        rewardGoodsOrder.setExtendOrderSn(data.getOId().toString());
+                        rewardGoodsOrder.setUpdateTime(LocalDateTime.now());
+                        rewardGoodsOrder.setUpdateBy("ERP Push");
+                        fsRewardGoodsOrderMapper.updateById(rewardGoodsOrder);
+                    }
+                }
+            }
+            log.info("批量推送普通订单,数量: {}", batch.size());
+            Thread.sleep(600);
+        }
+
+        // 返回最后一次推送的结果
+        if (lastResponse != null && CollectionUtils.isEmpty(lastResponse.getDatas())) {
+            log.info("推送ERP返回结果: {}", lastResponse);
+            throw new IllegalArgumentException("推送ERP返回数不应该为0");
+        }
+
+        // 如果没有任何订单推送,创建一个默认响应
+        if (lastResponse == null) {
+            lastResponse = new ErpOrderResponseDTO();
+            ErpOrderResponseDTO.OrderData data = new ErpOrderResponseDTO.OrderData();
+            data.setOId(0);
+            lastResponse.setDatas(Collections.singletonList(data));
+        }
+
+        ErpOrderResponseDTO.OrderData orderData = lastResponse.getDatas().get(0);
+
+        ErpOrderResponse erpOrderResponse = new ErpOrderResponse();
+        erpOrderResponse.setSuccess(true);
+        erpOrderResponse.setCode(String.valueOf(orderData.getOId()));
+        return erpOrderResponse;
+    }
+
+    /**
+     * 取消奖品订单
+     */
+    @Override
+    public BaseResponse cancelRewardOrder(ErpRefundUpdateRequest param) {
+        //判断当前订单在聚水潭是否是已取消状态如果已取消则不必再往下执行
+        OrderQueryRequestDTO queryRequestDTO = new OrderQueryRequestDTO();
+        queryRequestDTO.setSoIds(Collections.singletonList(String.valueOf(param.getTid())));
+        // 调用ERP服务查询订单
+        OrderQueryResponseDTO query = jstErpHttpService.query(queryRequestDTO);
+        if(query!=null&&query.getOrders().get(0).getStatus().equals("Cancelled")){
+            BaseResponse baseResponse = new BaseResponse();
+            baseResponse.setSuccess(true);
+            return baseResponse;
+        }
+
+        OrderCancelRequestDTO requestDTO = new OrderCancelRequestDTO();
+        requestDTO.setOIds(Collections.singletonList(Integer.valueOf(param.getOid())));
+        requestDTO.setCancelType("后台取消");
+        requestDTO.setRemark("后台取消");
+        CommonResponse cancel = jstErpHttpService.cancel(requestDTO);
+
+        BaseResponse baseResponse = new BaseResponse();
+        baseResponse.setSuccess(cancel.getCode() == 0);
+
+        return baseResponse;
+    }
+
+    /**
+     * 获取奖品订单
+     */
+    @Override
+    public ErpOrderQueryResponse getRewardOrder(ErpOrderQueryRequert param) {
+        // 1. 构建查询请求DTO
+        OrderQueryRequestDTO requestDTO = new OrderQueryRequestDTO();
+        if(StringUtils.isNotEmpty(param.getCode())) {
+            requestDTO.setOIds(Collections.singletonList(Long.valueOf(param.getCode())));
+        }else {
+            requestDTO.setOIds(param.getO_ids());
+            requestDTO.setShopId(Integer.parseInt(param.getShop_id()));
+            requestDTO.setPageIndex(param.getPage_index());
+            requestDTO.setPageSize(param.getPage_size());
+        }
+        // 2. 调用ERP服务查询订单
+        OrderQueryResponseDTO query = jstErpHttpService.query(requestDTO);
+
+        // 3. 构建响应对象
+        ErpOrderQueryResponse response = new ErpOrderQueryResponse();
+
+        // 4. 设置基本响应信息
+
+        // 5. 转换订单数据
+        if (query.getOrders() != null && !query.getOrders().isEmpty()) {
+            List<ErpOrderQuery> erpOrders = query.getOrders().stream()
+                        .map(this::convertToErpRewardOrderQuery)
+                        .collect(Collectors.toList());
+            response.setOrders(erpOrders);
+            response.setHasNext(query.getHasNext());
+        } else {
+            response.setOrders(Collections.emptyList());
+        }
+
+        return response;
+    }
+
+    private ErpOrderQuery convertToErpRewardOrderQuery(OrderQueryResponseDTO.Order order) {
+        ErpOrderQuery erpOrder = new ErpOrderQuery();
+
+        Wrapper<FsRewardGoodsOrder> queryWrapper = Wrappers.<FsRewardGoodsOrder>lambdaQuery()
+                .eq(FsRewardGoodsOrder::getOrderSn, order.getSoId());
+        FsRewardGoodsOrder rewardGoodsOrder = fsRewardGoodsOrderMapper.selectOne(queryWrapper);
+        Asserts.notNull(rewardGoodsOrder,"该订单号没有找到!");
+
+        // 设置基本订单信息
+        erpOrder.setCode(order.getSoId());
+
+        // 计算订单总数量
+        if (order.getItems() != null && !order.getItems().isEmpty()) {
+            int totalQty = order.getItems().stream()
+                    .mapToInt(OrderQueryResponseDTO.OrderItem::getQty)
+                    .sum();
+            erpOrder.setQty(totalQty);
+        }
+        // 设置金额相关信息
+        erpOrder.setAmount(order.getAmount() != null ? order.getAmount().doubleValue() : null);
+        erpOrder.setPayment(order.getPaidAmount() != null ? order.getPaidAmount().doubleValue() : null);
+
+        // 设置其他订单属性
+        erpOrder.setCod(order.getIsCod());
+        erpOrder.setPlatform_code(order.getOrderFrom());
+
+        // 尝试解析创建时间
+        try {
+            if (order.getCreated() != null) {
+                SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                erpOrder.setCreatetime(formatter.parse(order.getCreated()));
+            }
+        } catch (Exception e) {
+            // 日期解析异常处理
+            // 可以记录日志或保持为null
+        }
+
+        // 设置店铺信息
+        erpOrder.setShop_name(order.getShopName());
+        erpOrder.setShop_code(String.valueOf(order.getShopId()));
+
+        // 设置物流信息
+        erpOrder.setExpress_name(order.getLogisticsCompany());
+        erpOrder.setExpress_code(order.getLId());
+
+        // 设置收件人信息
+        erpOrder.setReceiver_name(rewardGoodsOrder.getUserName());
+        erpOrder.setReceiver_phone(rewardGoodsOrder.getMobile());
+        erpOrder.setReceiver_mobile(rewardGoodsOrder.getMobile());
+
+        // 构建完整地址
+        erpOrder.setReceiver_address(rewardGoodsOrder.getAddress());
+
+        // 如果是已发货
+        if(ObjectUtil.equal(order.getStatus(), ErpQueryOrderStatusEnum.SENT.getCode())){
+            List<ErpDeliverys> deliverysList = new ArrayList<>();
+
+            ErpDeliverys delivery = new ErpDeliverys();
+            delivery.setMail_no(order.getLId());
+            delivery.setExpress_code(order.getLcId());
+            delivery.setExpress_name(order.getLogisticsCompany());
+            delivery.setDelivery(true);
+            delivery.setCode(order.getSoId());
+
+            deliverysList.add(delivery);
+            erpOrder.setDeliverys(deliverysList);
+
+            // 设置发货状态,假设有物流单号就是已发货状态
+            erpOrder.setDelivery_state(1);
+        } else {
+            erpOrder.setDelivery_state(0);
+        }
+
+        return erpOrder;
+    }
 }
 

+ 32 - 0
fs-service/src/main/java/com/fs/his/domain/FsCoupon.java

@@ -66,6 +66,38 @@ public class FsCoupon extends BaseEntity
 
     private String cateIds;
 
+    /** 免单商品ID **/
+    private Long freeGoodsId;
+
+    /** 归属项目 **/
+    private Integer projectId;
+
+    /** 优惠券logo **/
+    private String logo;
+
+    public Long getFreeGoodsId() {
+        return freeGoodsId;
+    }
+
+    public void setFreeGoodsId(Long freeGoodsId) {
+        this.freeGoodsId = freeGoodsId;
+    }
+
+    public Integer getProjectId() {
+        return projectId;
+    }
+
+    public void setProjectId(Integer projectId) {
+        this.projectId = projectId;
+    }
+
+    public String getLogo() {
+        return logo;
+    }
+
+    public void setLogo(String logo) {
+        this.logo = logo;
+    }
 
     public String getCateIds() {
         return cateIds;

+ 3 - 0
fs-service/src/main/java/com/fs/his/domain/FsIntegralOrder.java

@@ -87,6 +87,7 @@ public class FsIntegralOrder
     private Integer isPay;
 
     /** 支付时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     @Excel(name = "支付时间")
     private LocalDateTime payTime;
 
@@ -139,7 +140,9 @@ public class FsIntegralOrder
 
     private String remark;
 
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     private Date createTime;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     @TableField(exist = false)
     private Date updateTime;
 

+ 3 - 0
fs-service/src/main/java/com/fs/his/domain/FsPackage.java

@@ -107,4 +107,7 @@ public class FsPackage extends BaseEntity
 
     /** 课程id */
     private  Long  courseId;
+
+    /** 项目ID **/
+    private Integer projectId;
 }

+ 39 - 0
fs-service/src/main/java/com/fs/his/dto/ComputeIntegralOrderMoneyDTO.java

@@ -0,0 +1,39 @@
+package com.fs.his.dto;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class ComputeIntegralOrderMoneyDTO {
+
+    /**
+     * 总积分
+     */
+    private BigDecimal totalIntegral;
+
+    /**
+     * 支付积分
+     */
+    private BigDecimal integral;
+
+    /**
+     * 优惠积分
+     */
+    private BigDecimal discountIntegral = BigDecimal.ZERO;
+
+    /**
+     * 总金额
+     */
+    private BigDecimal totalCash;
+
+    /**
+     * 支付金额
+     */
+    private BigDecimal cash;
+
+    /**
+     * 优惠金额
+     */
+    private BigDecimal discountCash = BigDecimal.ZERO;
+}

+ 1 - 0
fs-service/src/main/java/com/fs/his/enums/BusinessTypeEnum.java

@@ -7,6 +7,7 @@ import lombok.Getter;
 @AllArgsConstructor
 public enum BusinessTypeEnum {
     INTEGRAL_ORDER("integral", 6, "积分商城订单支付"),
+    REWARD_ORDER("reward", 10, "奖励商品订单支付"),
     ;
 
     private final String prefix;

+ 1 - 0
fs-service/src/main/java/com/fs/his/enums/FsUserIntegralLogTypeEnum.java

@@ -34,6 +34,7 @@ public enum FsUserIntegralLogTypeEnum {
     TYPE_24(24, "付费课程订阅"),
     TYPE_31(31,"广告积分"),
     TYPE_32(32,"积分兑换佣金"),
+    TYPE_33(33, "打卡活动积分"),
     ;
 
 

+ 1 - 14
fs-service/src/main/java/com/fs/his/mapper/FsUserCouponMapper.java

@@ -92,20 +92,7 @@ public interface FsUserCouponMapper
 
     @Select("SELECT COUNT(c.coupon_id) FROM fs_user_coupon u LEFT JOIN fs_coupon c ON u.coupon_id=c.coupon_id WHERE u.user_id=#{userId} and c.coupon_id=#{couponId}")
     Integer selectFsUserCouponByUserIdTypeCount(@Param("userId") Long userId,@Param("couponId")Long couponId);
-    @Select({"<script> " +
-            "select uc.coupon_code,uc.status,uc.use_time,c.limit_time,c.title,c.price,c.min_price  from fs_user_coupon uc left join fs_coupon c on c.coupon_id=uc.coupon_id    " +
-            "where 1=1  " +
-            "<if test = ' maps.userId != null '> " +
-            "and uc.user_id = #{maps.userId} " +
-            "</if>" +
-            "<if test = ' maps.couponType != null '> " +
-            "and uc.coupon_type = #{maps.couponType} " +
-            "</if>" +
-            "<if test = 'maps.status != null      '> " +
-            "and uc.status = #{maps.status} " +
-            "</if>" +
-            " order by uc.create_time desc "+
-            "</script>"})
+
     List<FsUserCouponListUVO> selectFsUserCouponListUVO(@Param("maps")FsUserCouponUParam param);
 
     @Select("SELECT COUNT(c.coupon_type) FROM fs_user_coupon u LEFT JOIN fs_coupon c ON u.coupon_id=c.coupon_id WHERE u.user_id=#{userId} and c.coupon_type=#{couponType}")

+ 16 - 0
fs-service/src/main/java/com/fs/his/param/FsIntegralOrderComputeParam.java

@@ -0,0 +1,16 @@
+package com.fs.his.param;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+public class FsIntegralOrderComputeParam {
+
+    @NotNull(message = "订单ID不能为空")
+    private Long orderId;
+
+    private Long userId;
+
+    private Long userCouponId;
+}

+ 1 - 10
fs-service/src/main/java/com/fs/his/param/FsIntegralOrderCreateParam.java

@@ -15,15 +15,6 @@ public class FsIntegralOrderCreateParam {
     private Long goodsId;
     private Long companyUserId;
 
-    /**
-     * 打卡积分商品活动id
-     */
-    private Long activityId;
-
-    /**
-     * 是否是app端入口
-     */
-    private Long isAPP;
-
+    private Long userCouponId;
 
 }

+ 1 - 4
fs-service/src/main/java/com/fs/his/param/FsUserCouponUParam.java

@@ -1,12 +1,8 @@
 package com.fs.his.param;
 
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.fs.common.annotation.Excel;
 import lombok.Data;
 
 import java.io.Serializable;
-import java.math.BigDecimal;
-import java.util.Date;
 
 @Data
 public class FsUserCouponUParam  extends BaseParam implements Serializable {
@@ -15,4 +11,5 @@ public class FsUserCouponUParam  extends BaseParam implements Serializable {
     String couponType;//可多个 用,
     Integer status;
     Long cateId=0l;
+    private Long goodsId;
 }

+ 5 - 0
fs-service/src/main/java/com/fs/his/service/IFsCouponService.java

@@ -76,6 +76,11 @@ public interface IFsCouponService
 
     R receive(FsCouponReceiveParam param);
 
+    /**
+     * 根据id集合查询优惠券列表
+     */
+    List<FsCoupon> selectFsCouponListByIds(List<Long> ids);
+
     /**
      * 查询用户指定类型的优惠券列表
      * @param userId 用户ID

+ 5 - 0
fs-service/src/main/java/com/fs/his/service/IFsIntegralOrderService.java

@@ -126,4 +126,9 @@ public interface IFsIntegralOrderService
      * @return 结果
      */
     R createOrderByManually(FsIntegralOrderManuallyParam param);
+
+    /**
+     * 积分订单支付金额计算
+     */
+    R computeOrder(FsIntegralOrderComputeParam param);
 }

+ 12 - 2
fs-service/src/main/java/com/fs/his/service/impl/FsCouponServiceImpl.java

@@ -2,8 +2,7 @@ package com.fs.his.service.impl;
 
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
 import java.util.stream.Collectors;
 
 import com.fs.common.core.domain.R;
@@ -189,4 +188,15 @@ public class FsCouponServiceImpl implements IFsCouponService
         String verifyCode = year + day + intRandom;
         return verifyCode;
     }
+
+    /**
+     * 根据id集合查询优惠券列表
+     */
+    @Override
+    public List<FsCoupon> selectFsCouponListByIds(List<Long> ids) {
+        if (Objects.isNull(ids) || ids.isEmpty()) {
+            return new ArrayList<>();
+        }
+        return fsCouponMapper.selectFsCouponListByIds(ids);
+    }
 }

+ 169 - 59
fs-service/src/main/java/com/fs/his/service/impl/FsIntegralOrderServiceImpl.java

@@ -27,6 +27,7 @@ import com.fs.course.domain.FsCourseCheckinPrize;
 import com.fs.course.domain.FsCourseCheckinReceive;
 import com.fs.course.mapper.FsCourseCheckinPrizeMapper;
 import com.fs.course.mapper.FsCourseCheckinReceiveMapper;
+import com.fs.his.dto.ComputeIntegralOrderMoneyDTO;
 import com.fs.his.utils.RedisCacheUtil;
 import com.fs.erp.domain.ErpOrder;
 import com.fs.erp.domain.ErpOrderPayment;
@@ -181,15 +182,13 @@ public class FsIntegralOrderServiceImpl implements IFsIntegralOrderService {
     @Autowired
     private FsCourseCheckinReceiveMapper fsCourseCheckinReceiveMapper;
 
+    @Autowired
+    private RedisCacheUtil redisCacheUtil;
     @Autowired
     private FsUserCouponMapper fsUserCouponMapper;
-
     @Autowired
     private FsCouponMapper fsCouponMapper;
 
-    @Autowired
-    private RedisCacheUtil redisCacheUtil;
-
     //ERP 类型到服务的映射
     private Map<Integer, IErpOrderService> erpServiceMap;
 
@@ -325,31 +324,49 @@ public class FsIntegralOrderServiceImpl implements IFsIntegralOrderService {
             return R.error("库存不足");
         }
 
-        Long totalIntegral = integralGoods.getIntegral();
-        BigDecimal totalCash = integralGoods.getCash();
-        Long activityId = param.getActivityId();
-
-        if (param.getIsAPP() != null && param.getIsAPP() == 1) {
-            FsCourseCheckinPrize prizeQuery = new FsCourseCheckinPrize();
-            prizeQuery.setGoodsId(param.getGoodsId());
-            prizeQuery.setPrizeType(2);
-            List<FsCourseCheckinPrize> prizeList = fsCourseCheckinPrizeMapper.selectFsCourseCheckinPrizeList(prizeQuery);
-            if (ObjectUtil.isNotEmpty(prizeList)) {
-                FsCourseCheckinReceive receiveQuery = new FsCourseCheckinReceive();
-                receiveQuery.setUserId(param.getUserId());
-                receiveQuery.setActivityId(param.getActivityId());
-                receiveQuery.setGoodsId(param.getGoodsId());
-                receiveQuery.setReceiveStatus(0);
-                List<FsCourseCheckinReceive> receiveList = fsCourseCheckinReceiveMapper.selectFsCourseCheckinReceiveList(receiveQuery);
-                if (ObjectUtil.isNotEmpty(receiveList)) {
-                    totalIntegral = 0L;
-                    totalCash = BigDecimal.ZERO;
-                    activityId = param.getActivityId();
-                }
+        ComputeIntegralOrderMoneyDTO moneyDTO = new ComputeIntegralOrderMoneyDTO();
+        moneyDTO.setTotalIntegral(new BigDecimal(integralGoods.getIntegral()));
+        moneyDTO.setIntegral(new BigDecimal(integralGoods.getIntegral()));
+        moneyDTO.setTotalCash(integralGoods.getCash());
+        moneyDTO.setCash(integralGoods.getCash());
+
+        // 计算优惠券
+        if (param.getUserCouponId() != null) {
+
+            // 查询用户优惠券,判断是否有效,是否未使用
+            FsUserCoupon fsUserCoupon = fsUserCouponMapper.selectFsUserCouponById(param.getUserCouponId());
+            if (fsUserCoupon == null || !Objects.equals(fsUserCoupon.getUserId(), param.getUserId())) {
+                return R.error("无效的优惠券");
+            }
+            if (fsUserCoupon.getBusinessType() != 4) {
+                return  R.error("优惠券类型不匹配,无法使用");
+            }
+
+            if (fsUserCoupon.getStatus() == 1) {
+                return R.error("优惠券已使用");
+            }
+
+            if (fsUserCoupon.getStatus() == 2 || fsUserCoupon.getLimitTime().before(new Date())) {
+                return  R.error("优惠券已过期");
+            }
+
+            // 查看优惠券具体类型
+            FsCoupon fsCoupon = fsCouponMapper.selectFsCouponByCouponId(fsUserCoupon.getCouponId());
+            if (fsCoupon == null) {
+                return R.error("无效的优惠券");
+            }
+
+            if (fsCoupon.getCouponType() != 7) {
+                return R.error("无效的优惠券");
             }
+
+            moneyDTO.setDiscountIntegral(moneyDTO.getIntegral());
+            moneyDTO.setIntegral(BigDecimal.ZERO);
+            moneyDTO.setDiscountCash(moneyDTO.getCash());
+            moneyDTO.setCash(BigDecimal.ZERO);
         }
 
-        if (totalIntegral > 0 && user.getIntegral() < totalIntegral) {
+        if (moneyDTO.getIntegral().intValue() > 0 && user.getIntegral() < moneyDTO.getIntegral().intValue()) {
             return R.error("积分不足");
         }
 
@@ -359,19 +376,17 @@ public class FsIntegralOrderServiceImpl implements IFsIntegralOrderService {
         }
 
         integralGoods.setNum(1);
-        integralGoods.setIntegral(totalIntegral);
-        integralGoods.setCash(totalCash);
         List<FsIntegralGoods> goodsItem = new ArrayList<>();
         goodsItem.add(integralGoods);
 
         // 创建订单
-        return createOrder(user, address, totalIntegral, totalCash, goodsItem, param.getCompanyUserId(), param.getGoodsId(), activityId);
+        return createOrder(user, address, moneyDTO, goodsItem, param.getCompanyUserId(), param.getUserCouponId());
     }
 
     /**
      * 创建订单
      */
-    private R createOrder(FsUser user, FsUserAddress address, Long totalIntegral, BigDecimal totalCash, List<FsIntegralGoods> goodsItem, Long companyUserId, Long goodsId, Long activityId) {
+    private R createOrder(FsUser user, FsUserAddress address, ComputeIntegralOrderMoneyDTO moneyDTO, List<FsIntegralGoods> goodsItem, Long companyUserId, Long userCouponId) {
         FsIntegralOrder order = new FsIntegralOrder();
         String orderSn = null;
         try {
@@ -384,12 +399,12 @@ public class FsIntegralOrderServiceImpl implements IFsIntegralOrderService {
         }
 
         // 现金
-        if (totalCash.compareTo(BigDecimal.ZERO) > 0) {
+        if (moneyDTO.getCash().compareTo(BigDecimal.ZERO) > 0) {
             // 现金+积分
-            order.setPayType(totalIntegral > 0 ? 3 : 2);
+            order.setPayType(moneyDTO.getIntegral().intValue() > 0 ? 3 : 2);
             order.setStatus(4);
             order.setIsPay(0);
-            order.setPayMoney(totalCash);
+            order.setPayMoney(moneyDTO.getCash());
         }
         // 积分
         else {
@@ -402,7 +417,13 @@ public class FsIntegralOrderServiceImpl implements IFsIntegralOrderService {
         order.setOrderCode(orderSn);
         order.setUserId(user.getUserId());
         order.setBarCode(goodsItem.get(0).getBarCode());
-        order.setIntegral(totalIntegral.toString());
+        order.setIntegral(moneyDTO.getIntegral().toString());
+        order.setDiscountIntegral(moneyDTO.getDiscountIntegral().toString());
+        order.setTotalIntegral(moneyDTO.getTotalIntegral().toString());
+        order.setTotalMoney(moneyDTO.getTotalCash());
+        order.setPayMoney(moneyDTO.getCash());
+        order.setDiscountMoney(moneyDTO.getDiscountCash());
+        order.setUserCouponId(userCouponId);
         order.setItemJson(JSONUtil.toJsonStr(goodsItem));
         order.setUserName(address.getRealName());
         order.setUserAddress(address.getProvince() + address.getCity() + address.getDistrict() + address.getDetail());
@@ -427,6 +448,16 @@ public class FsIntegralOrderServiceImpl implements IFsIntegralOrderService {
         }
 
         if (fsIntegralOrderMapper.insertFsIntegralOrder(order) > 0) {
+            if (userCouponId != null) {
+                // 使用优惠券
+                FsUserCoupon updateUserCoupon = new FsUserCoupon();
+                updateUserCoupon.setId(userCouponId);
+                updateUserCoupon.setStatus(1);
+                updateUserCoupon.setUseTime(new Date());
+                updateUserCoupon.setBusinessId(order.getOrderId());
+                fsUserCouponMapper.updateFsUserCoupon(updateUserCoupon);
+            }
+
             if (order.getPayType() != 2) {
                 //写入日志
                 FsUser userMap = new FsUser();
@@ -434,19 +465,19 @@ public class FsIntegralOrderServiceImpl implements IFsIntegralOrderService {
 
                 // 可消费积分
                 long consumer = user.getIntegral() - user.getWithdrawIntegral();
-                if (consumer < totalIntegral) {
+                if (consumer < moneyDTO.getIntegral().intValue()) {
                     // 扣除完可消费积分后,剩余的积分
-                    long extra = totalIntegral - consumer;
+                    long extra = moneyDTO.getIntegral().intValue() - consumer;
                     // 可提现积分扣除 剩余积分
                     Long withdrawIntegral = user.getWithdrawIntegral() - extra;
                     userMap.setIntegral(withdrawIntegral);
                     userMap.setWithdrawIntegral(withdrawIntegral);
                 } else {
-                    userMap.setIntegral(user.getIntegral() - totalIntegral);
+                    userMap.setIntegral(user.getIntegral() - moneyDTO.getIntegral().intValue());
                 }
                 fsUserMapper.updateFsUser(userMap);
                 FsUserIntegralLogs logs = new FsUserIntegralLogs();
-                logs.setIntegral(-totalIntegral);
+                logs.setIntegral(-moneyDTO.getIntegral().longValue());
                 logs.setUserId(order.getUserId());
                 logs.setBalance(userMap.getIntegral());
                 logs.setLogType(5);
@@ -454,25 +485,9 @@ public class FsIntegralOrderServiceImpl implements IFsIntegralOrderService {
                 logs.setCreateTime(new Date());
                 fsUserIntegralLogsMapper.insertFsUserIntegralLogs(logs);
             }
-            //如何是打卡活动的积分商品则修改领取状态
-            if (activityId != null && goodsId != null) {
-                FsCourseCheckinReceive receiveQuery = new FsCourseCheckinReceive();
-                receiveQuery.setUserId(order.getUserId());
-                receiveQuery.setActivityId(activityId);
-                receiveQuery.setGoodsId(goodsId);
-                receiveQuery.setReceiveStatus(0);
-                List<FsCourseCheckinReceive> receiveList = fsCourseCheckinReceiveMapper.selectFsCourseCheckinReceiveList(receiveQuery);
-                if (ObjectUtil.isNotEmpty(receiveList)) {
-                    FsCourseCheckinReceive receive = receiveList.get(0);
-                    receive.setReceiveStatus(1);
-                    receive.setReceiveTime(new Date());
-                    receive.setUpdateTime(new Date());
-                    fsCourseCheckinReceiveMapper.updateFsCourseCheckinReceive(receive);
-                    if (receive.getGoodsId() != null) {
-                        redisCacheUtil.delRedisKey("getIntegralGoodsById::" + receive.getGoodsId());
-                    }
-                }
-            }
+
+            //
+
             // 积分支付
             if (order.getPayType() == 1) {
                 // 首次完成积分商城下单
@@ -546,11 +561,17 @@ public class FsIntegralOrderServiceImpl implements IFsIntegralOrderService {
             goodsItem.add(integralGoods);
         }
 
-        if (user.getIntegral() < totalIntegral) {
+        ComputeIntegralOrderMoneyDTO moneyDTO = new ComputeIntegralOrderMoneyDTO();
+        moneyDTO.setTotalIntegral(new BigDecimal(totalIntegral));
+        moneyDTO.setIntegral(new BigDecimal(totalIntegral));
+        moneyDTO.setTotalCash(totalCash);
+        moneyDTO.setCash(totalCash);
+
+        if (user.getIntegral() < moneyDTO.getIntegral().intValue()) {
             return R.error("积分不足");
         }
 
-        return createOrder(user, address, totalIntegral, totalCash, goodsItem, null, null, null);
+        return createOrder(user, address, moneyDTO, goodsItem, null, null);
     }
 
     @Override
@@ -643,6 +664,14 @@ public class FsIntegralOrderServiceImpl implements IFsIntegralOrderService {
             fsIntegralGoodsMapper.addStock(integralGoods.getGoodsId(), Objects.isNull(integralGoods.getNum()) ? 1 : integralGoods.getNum());
         }
 
+        // 还原优惠券
+        if (order.getUserCouponId() != null) {
+            FsUserCoupon userCoupon=new FsUserCoupon();
+            userCoupon.setStatus(0);
+            userCoupon.setId(order.getUserCouponId());
+            fsUserCouponMapper.updateFsUserCoupon(userCoupon);
+        }
+
         // 还原积分
         FsUser user = fsUserMapper.selectFsUserByUserId(order.getUserId());
         FsUser userMap = new FsUser();
@@ -1354,4 +1383,85 @@ public class FsIntegralOrderServiceImpl implements IFsIntegralOrderService {
             throw new CustomException("订单创建失败");
         }
     }
+
+    /**
+     * 积分订单支付金额计算
+     */
+    @Override
+    public R computeOrder(FsIntegralOrderComputeParam param) {
+
+        // 查询积分订单,判断订单是否存在,状态是否未支付
+        FsIntegralOrder order = fsIntegralOrderMapper.selectFsIntegralOrderByOrderId(param.getOrderId());
+        if (order == null) {
+            return R.error("订单不存在!");
+        }
+
+        if (order.getIsPay() != 0 || order.getStatus() != 4) {
+            return R.error("订单状态不正确");
+        }
+
+        // 查询用户,判断用户是否存在
+        FsUser fsUser = fsUserMapper.selectFsUserById(param.getUserId());
+        if (fsUser == null || fsUser.getIsDel() == 1) {
+            return R.error("用户不存在");
+        }
+
+        // 返回扣减后的支付金额
+        return R.ok().put("data", computeOrderMoney(param.getUserId(), new BigDecimal(order.getTotalIntegral()), order.getTotalMoney(), param.getUserCouponId(), order.getItemJson()));
+    }
+
+    /**
+     * 计算优惠后的价格
+     */
+    private ComputeIntegralOrderMoneyDTO computeOrderMoney(Long userId, BigDecimal orderIntegral, BigDecimal orderMoney, Long userCouponId, String goodsJson) {
+        ComputeIntegralOrderMoneyDTO moneyDTO = new ComputeIntegralOrderMoneyDTO();
+        moneyDTO.setIntegral(orderIntegral);
+        moneyDTO.setCash(orderMoney);
+
+        // 不使用优惠券
+        if (null == userCouponId) {
+            return moneyDTO;
+        }
+
+        // 查询用户优惠券,判断是否有效,是否未使用
+        FsUserCoupon fsUserCoupon = fsUserCouponMapper.selectFsUserCouponById(userCouponId);
+        if (fsUserCoupon == null || !Objects.equals(fsUserCoupon.getUserId(), userId)) {
+            return moneyDTO;
+        }
+
+        if (fsUserCoupon.getBusinessType() != 4) {
+            return moneyDTO;
+        }
+
+        if (fsUserCoupon.getStatus() == 1) {
+            return moneyDTO;
+        }
+
+        if (fsUserCoupon.getStatus() == 2 || fsUserCoupon.getLimitTime().before(new Date())) {
+            return moneyDTO;
+        }
+
+        // 查看优惠券具体类型
+        FsCoupon fsCoupon = fsCouponMapper.selectFsCouponByCouponId(fsUserCoupon.getCouponId());
+        if (fsCoupon == null) {
+            return moneyDTO;
+        }
+
+        // 积分商品免单券
+        if (fsCoupon.getCouponType() == 7) {
+            // 查找指定减免积分商品并减免价格
+            List<FsIntegralGoods> goodsItem = JSONUtil.toBean(goodsJson, new TypeReference<List<FsIntegralGoods>>(){}, true);
+            goodsItem.stream().filter(g -> g.getGoodsId().equals(fsCoupon.getFreeGoodsId())).findFirst().ifPresent(g -> {
+                BigDecimal integral = orderIntegral.subtract(new BigDecimal(g.getIntegral()));
+                BigDecimal cash = orderMoney.subtract(g.getCash());
+                moneyDTO.setIntegral(integral);
+                moneyDTO.setDiscountIntegral(new BigDecimal(g.getIntegral()));
+                moneyDTO.setCash(cash);
+                moneyDTO.setDiscountCash(g.getCash());
+            });
+        }
+
+        return moneyDTO;
+    }
+
 }

+ 5 - 0
fs-service/src/main/java/com/fs/his/vo/FsCouponListVO.java

@@ -58,4 +58,9 @@ public class FsCouponListVO implements Serializable {
     @Excel(name = "可领取数量")
     private Integer limitCount;
 
+    /** logo **/
+    private String logo;
+    /** 归属项目 **/
+    private Integer projectId;
+
 }

+ 5 - 0
fs-service/src/main/java/com/fs/his/vo/FsPackageVO.java

@@ -124,4 +124,9 @@ public class FsPackageVO {
 
     private Long solarTerm;
     private String appIds;
+
+    /** 归属项目 **/
+    private Integer projectId;
+    /** 指定课程 **/
+    private Long courseId;
 }

+ 9 - 0
fs-service/src/main/java/com/fs/his/vo/FsUserCouponListUVO.java

@@ -36,4 +36,13 @@ public class FsUserCouponListUVO implements Serializable {
     /** 最低消费金额可用 */
     private BigDecimal minPrice;
 
+    /** 优惠券指定商品 **/
+    private Long freeGoodsId;
+
+    /** 优惠券logo **/
+    private String logo;
+
+    /** 简称 **/
+    private String shortName;
+
 }

+ 110 - 0
fs-service/src/main/java/com/fs/reward/domain/FsRewardGoods.java

@@ -0,0 +1,110 @@
+package com.fs.reward.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@TableName("fs_reward_goods")
+@Data
+public class FsRewardGoods {
+
+    /**
+     * 主键ID
+     */
+    @TableId(type = IdType.AUTO)
+    private Long goodsId;
+
+    /**
+     * 商品名称
+     */
+    private String goodsName;
+
+    /**
+     * 封面
+     */
+    private String goodsImg;
+
+    /**
+     * 轮播图
+     */
+    private String goodsImages;
+
+    /**
+     * 商品介绍
+     */
+    private String goodsIntroduce;
+
+    /**
+     * 商品描述
+     */
+    private String goodsDesc;
+
+    /**
+     * 店铺ID
+     */
+    private Long storeId;
+
+    /**
+     * 店铺商品编码
+     */
+    private String storeGoodsSn;
+
+    /**
+     * 状态 1上架 0下架
+     */
+    private Integer status;
+
+    /**
+     * 单价
+     */
+    private BigDecimal price;
+
+    /**
+     * 原价
+     */
+    private BigDecimal opPrice;
+
+    /**
+     * 库存
+     */
+    private Integer stock;
+
+    /**
+     * 序号
+     */
+    private Integer sort;
+
+    /**
+     * 0未删除 1已删除
+     */
+    private Integer isDel;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+
+    /**
+     * 创建人
+     */
+    private String createBy;
+
+    /**
+     * 修改时间
+     */
+    private LocalDateTime updateTime;
+
+    /**
+     * 修改人
+     */
+    private String updateBy;
+}

+ 160 - 0
fs-service/src/main/java/com/fs/reward/domain/FsRewardGoodsOrder.java

@@ -0,0 +1,160 @@
+package com.fs.reward.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@TableName("fs_reward_goods_order")
+@Data
+public class FsRewardGoodsOrder {
+
+    /**
+     * 主键ID
+     */
+    @TableId(type = IdType.AUTO)
+    private Long orderId;
+
+    /**
+     * 订单号
+     */
+    private String orderSn;
+
+    /**
+     * 外部订单号
+     */
+    private String extendOrderSn;
+
+    /**
+     * 店铺ID
+     */
+    private Long storeId;
+
+    /**
+     * 用户ID
+     */
+    private Long userId;
+
+    /**
+     * 收货人
+     */
+    private String userName;
+
+    /**
+     * 收货手机号
+     */
+    private String mobile;
+
+    /**
+     * 收货地址
+     */
+    private String address;
+
+    /**
+     * 订单金额
+     */
+    private BigDecimal orderMoney;
+
+    /**
+     * 支付金额
+     */
+    private BigDecimal payMoney;
+
+    /**
+     * 支付类型
+     */
+    private Integer payType;
+
+    /**
+     * 支付时间
+     */
+    private LocalDateTime payTime;
+
+    /**
+     * 快递公司编号
+     */
+    private String deliveryCode;
+
+    /**
+     * 快递名称
+     */
+    private String deliveryName;
+
+    /**
+     * 快递单号
+     */
+    private String deliverySn;
+
+    /**
+     * 中奖记录ID
+     */
+    private Long roundId;
+
+    /**
+     * 领取记录ID
+     */
+    private Long receiveId;
+
+    /**
+     * 商品ID
+     */
+    private Long goodsId;
+
+    /**
+     * 商品信息
+     */
+    private String goodsJson;
+
+    /**
+     * 状态 1待支付 2已支付 3已发货 4已完成 -1已取消
+     */
+    private Integer status;
+
+    /**
+     * 发货时间
+     */
+    private LocalDateTime deliveryTime;
+
+    /**
+     * 完成时间
+     */
+    private LocalDateTime finishTime;
+
+    /**
+     * 取消时间
+     */
+    private LocalDateTime cancelTime;
+
+    /**
+     * 过期时间
+     */
+    private LocalDateTime expiredTime;
+
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+
+    /**
+     * 创建人
+     */
+    private String createBy;
+
+    /**
+     * 修改时间
+     */
+    private LocalDateTime updateTime;
+
+    /**
+     * 修改人
+     */
+    private String updateBy;
+
+    /**
+     * 备注
+     */
+    private String remark;
+}

+ 32 - 0
fs-service/src/main/java/com/fs/reward/mapper/FsRewardGoodsMapper.java

@@ -0,0 +1,32 @@
+package com.fs.reward.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.reward.domain.FsRewardGoods;
+import com.fs.reward.param.FsRewardGoodsListParam;
+import com.fs.reward.vo.FsRewardGoodsVO;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+public interface FsRewardGoodsMapper extends BaseMapper<FsRewardGoods> {
+
+    /**
+     * 查询奖励商品列表
+     */
+    List<FsRewardGoodsVO> selectFsRewardGoodsVOList(FsRewardGoodsListParam param);
+
+    /**
+     * 查询奖励商品详情
+     */
+    FsRewardGoodsVO selectFsRewardGoodsVOById(@Param("goodsId") Long goodsId);
+
+    /**
+     * 减少库存
+     */
+    int reduceStock(@Param("goodsId") Long goodsId);
+
+    /**
+     * 增加库存(订单取消时回滚)
+     */
+    int addStock(@Param("goodsId") Long goodsId);
+}

+ 39 - 0
fs-service/src/main/java/com/fs/reward/mapper/FsRewardGoodsOrderMapper.java

@@ -0,0 +1,39 @@
+package com.fs.reward.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.reward.domain.FsRewardGoodsOrder;
+import com.fs.reward.param.FsRewardGoodsOrderListParam;
+import com.fs.reward.vo.FsRewardGoodsOrderVO;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
+
+public interface FsRewardGoodsOrderMapper extends BaseMapper<FsRewardGoodsOrder> {
+
+    /**
+     * 查询奖励商品订单列表
+     */
+    List<FsRewardGoodsOrderVO> selectFsRewardGoodsOrderVOList(FsRewardGoodsOrderListParam param);
+
+    /**
+     * 根据id查询奖励商品订单详情
+     */
+    FsRewardGoodsOrderVO selectFsRewardGoodsOrderVOById(@Param("orderId") Long orderId);
+
+    /**
+     * 根据用户id、商品id和轮次id查询奖励商品订单
+     */
+    @Select("select * from fs_reward_goods_order where user_id = #{userId} and goods_id = #{goodsId} and round_id = #{roundId}")
+    FsRewardGoodsOrder selectByUserIdAndGoodsIdAndRoundId(@Param("userId") Long userId, @Param("goodsId") Long goodsId, @Param("roundId") Long roundId);
+
+    /**
+     * 查询未推送订单列表
+     */
+    List<FsRewardGoodsOrder> selectNoPushOrders();
+
+    /**
+     * 查询已推送订单列表
+     */
+    List<FsRewardGoodsOrder> selectPushOrders();
+}

+ 24 - 0
fs-service/src/main/java/com/fs/reward/param/FsAppRewardGoodsOrderAddParam.java

@@ -0,0 +1,24 @@
+package com.fs.reward.param;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@ApiModel("中奖商品下单参数")
+@Data
+public class FsAppRewardGoodsOrderAddParam {
+
+    @NotNull(message = "商品不能为空")
+    @ApiModelProperty("商品ID")
+    private Long goodsId;
+
+    @NotNull(message = "中奖记录不能为空")
+    @ApiModelProperty("中奖记录ID")
+    private String roundId;
+
+    @NotNull(message = "收货地址不能为空")
+    @ApiModelProperty("收货地址ID")
+    private Long addressId;
+}

+ 68 - 0
fs-service/src/main/java/com/fs/reward/param/FsRewardGoodsAddParam.java

@@ -0,0 +1,68 @@
+package com.fs.reward.param;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.hibernate.validator.constraints.Range;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.math.BigDecimal;
+
+@Data
+public class FsRewardGoodsAddParam {
+
+    @NotBlank(message = "商品名称不能为空")
+    @ApiModelProperty("商品名称")
+    private String goodsName;
+
+    @NotBlank(message = "商品封面不能为空")
+    @ApiModelProperty("商品封面")
+    private String goodsImg;
+
+    @NotBlank(message = "商品轮播图不能为空")
+    @ApiModelProperty("商品轮播图")
+    private String goodsImages;
+
+    @NotBlank(message = "商品介绍不能为空")
+    @ApiModelProperty("商品介绍")
+    private String goodsIntroduce;
+
+    @ApiModelProperty("商品描述")
+    private String goodsDesc;
+
+    @NotNull(message = "店铺不能为空")
+    @ApiModelProperty("店铺ID")
+    private Long storeId;
+
+    @NotBlank(message = "商品编码不能为空")
+    @ApiModelProperty("店铺商品编码")
+    private String storeGoodsSn;
+
+    @NotNull(message = "状态不能为空")
+    @Range(min = 0, max = 1, message = "状态值不正确")
+    @ApiModelProperty("状态 1上架 0下架")
+    private Integer status;
+
+    @NotNull(message = "商品单价不能为空")
+    @ApiModelProperty("单价")
+    private BigDecimal price;
+
+    @NotNull(message = "商品原价不能为空")
+    @ApiModelProperty("原价")
+    private BigDecimal opPrice;
+
+    @NotNull(message = "库存不能为空")
+    @Min(value = 0, message = "库不能小于0")
+    @ApiModelProperty("库存")
+    private Integer stock;
+
+    @ApiModelProperty("序号")
+    private Integer sort = 1;
+
+    @ApiModelProperty("备注")
+    private String remark;
+
+    @ApiModelProperty("创建人")
+    private String createBy;
+}

+ 20 - 0
fs-service/src/main/java/com/fs/reward/param/FsRewardGoodsEditParam.java

@@ -0,0 +1,20 @@
+package com.fs.reward.param;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.constraints.NotNull;
+
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class FsRewardGoodsEditParam extends FsRewardGoodsAddParam {
+
+    @NotNull(message = "商品ID不能为空")
+    @ApiModelProperty("商品ID")
+    private Long goodsId;
+
+    @ApiModelProperty("修改人")
+    private String updateBy;
+}

+ 23 - 0
fs-service/src/main/java/com/fs/reward/param/FsRewardGoodsListParam.java

@@ -0,0 +1,23 @@
+package com.fs.reward.param;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+public class FsRewardGoodsListParam {
+
+    @ApiModelProperty("商品ID")
+    private Long goodsId;
+
+    @ApiModelProperty("商品名称")
+    private String goodsName;
+
+    @ApiModelProperty("店铺ID")
+    private Long storeId;
+
+    @ApiModelProperty("商品编码")
+    private String storeGoodsSn;
+
+    @ApiModelProperty("商品状态")
+    private Integer status;
+}

+ 24 - 0
fs-service/src/main/java/com/fs/reward/param/FsRewardGoodsOrderAddParam.java

@@ -0,0 +1,24 @@
+package com.fs.reward.param;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@ApiModel("中奖商品下单参数")
+@Data
+public class FsRewardGoodsOrderAddParam {
+
+    @NotNull(message = "商品不能为空")
+    @ApiModelProperty("商品ID")
+    private Long goodsId;
+
+    @NotNull(message = "中奖记录不能为空")
+    @ApiModelProperty("中奖记录ID")
+    private Long roundId;
+
+    @NotNull(message = "收货地址不能为空")
+    @ApiModelProperty("收货地址ID")
+    private Long addressId;
+}

+ 56 - 0
fs-service/src/main/java/com/fs/reward/param/FsRewardGoodsOrderListParam.java

@@ -0,0 +1,56 @@
+package com.fs.reward.param;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+@Data
+public class FsRewardGoodsOrderListParam {
+
+    @ApiModelProperty("店铺ID")
+    private Long storeId;
+
+    @ApiModelProperty("用户ID")
+    private Long userId;
+
+    @ApiModelProperty("收货人")
+    private String receiveUserName;
+
+    @ApiModelProperty("收货人电话号码")
+    private String receiveUserPhone;
+
+    @ApiModelProperty("订单编码")
+    private String orderSn;
+
+    @ApiModelProperty("状态 1待支付 2已支付 3已发货 4已完成 -1已取消")
+    private Integer status;
+
+    @ApiModelProperty("快递单号")
+    private String deliverySn;
+
+    @ApiModelProperty("创建时间-start")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime sCreateTime;
+
+    @ApiModelProperty("创建时间-end")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime eCreateTime;
+
+    @ApiModelProperty("支付时间-start")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime sPayTime;
+
+    @ApiModelProperty("支付时间-end")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime ePayTime;
+
+    @ApiModelProperty("发货时间-start")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime sDeliveryTime;
+
+    @ApiModelProperty("发货时间-end")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime eDeliveryTime;
+}

+ 29 - 0
fs-service/src/main/java/com/fs/reward/param/FsRewardGoodsOrderPayParam.java

@@ -0,0 +1,29 @@
+package com.fs.reward.param;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+@ApiModel("支付参数")
+@Data
+public class FsRewardGoodsOrderPayParam implements Serializable {
+
+    @ApiModelProperty("订单号")
+    @NotNull(message = "订单号不能为空")
+    private Long orderId;
+
+    @ApiModelProperty("支付类型 1-微信 2-微信H5 3-微信APP 4-支付宝 5-支付宝H5")
+    @NotNull(message = "支付类型不能为空")
+    private Integer payType;
+
+    @ApiModelProperty("微信APPID")
+    private String appId;
+
+    @NotNull(message = "收货地址不能为空")
+    @ApiModelProperty("收货地址ID")
+    private Long addressId;
+
+}

+ 67 - 0
fs-service/src/main/java/com/fs/reward/service/IFsRewardGoodsOrderService.java

@@ -0,0 +1,67 @@
+package com.fs.reward.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.common.core.domain.R;
+import com.fs.his.domain.FsStorePayment;
+import com.fs.reward.domain.FsRewardGoodsOrder;
+import com.fs.reward.param.FsAppRewardGoodsOrderAddParam;
+import com.fs.reward.param.FsRewardGoodsOrderAddParam;
+import com.fs.reward.param.FsRewardGoodsOrderListParam;
+import com.fs.reward.param.FsRewardGoodsOrderPayParam;
+import com.fs.reward.vo.FsRewardGoodsOrderVO;
+
+import java.util.List;
+import java.util.Map;
+
+public interface IFsRewardGoodsOrderService extends IService<FsRewardGoodsOrder> {
+
+    /**
+     * 查询奖励商品订单列表
+     */
+    List<FsRewardGoodsOrderVO> selectFsRewardGoodsOrderVOList(FsRewardGoodsOrderListParam param);
+
+    /**
+     * 根据id查询奖励商品订单详情
+     */
+    FsRewardGoodsOrderVO selectFsRewardGoodsOrderVOById(Long orderId);
+
+    /**
+     * 支付订单
+     */
+    Map<String, Object> payment(Long userId, FsRewardGoodsOrderPayParam param);
+
+    /**
+     * 支付回调
+     */
+    R payConfirm(String orderSn, String payCode, String tradeNo, String payType, int type, String bankTransactionId, String bankSerialNo);
+
+    /**
+     * 推送erp
+     */
+    void pushErp() throws InterruptedException;
+
+    /**
+     * 同步erp信息
+     */
+    void syncErpData() throws InterruptedException;
+
+    /**
+     * 获取订单支付信息
+     */
+    List<FsStorePayment> getPayInfo(String orderSn);
+
+    /**
+     * 取消过期未支付订单
+     */
+    void cancelExpireRewardOrder();
+
+    /**
+     * 完成订单
+     */
+    void completeOrder();
+
+    /**
+     * 取消订单
+     */
+    int cancelOrder(Long orderId);
+}

+ 38 - 0
fs-service/src/main/java/com/fs/reward/service/IFsRewardGoodsService.java

@@ -0,0 +1,38 @@
+package com.fs.reward.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.reward.domain.FsRewardGoods;
+import com.fs.reward.param.FsRewardGoodsAddParam;
+import com.fs.reward.param.FsRewardGoodsEditParam;
+import com.fs.reward.param.FsRewardGoodsListParam;
+import com.fs.reward.vo.FsRewardGoodsVO;
+
+import java.util.List;
+
+public interface IFsRewardGoodsService extends IService<FsRewardGoods> {
+
+    /**
+     * 查询奖励商品列表
+     */
+    List<FsRewardGoodsVO> selectFsRewardGoodsVOList(FsRewardGoodsListParam param);
+
+    /**
+     * 查询奖励商品详情
+     */
+    FsRewardGoodsVO selectFsRewardGoodsVOById(Long goodsId);
+
+    /**
+     * 添加奖励商品
+     */
+    int insertFsRewardGoods(FsRewardGoodsAddParam param);
+
+    /**
+     * 修改奖励商品
+     */
+    int updateFsRewardGoods(FsRewardGoodsEditParam param);
+
+    /**
+     * 逻辑删除商品
+     */
+    int logicDeleteFsRewardGoods(Long[] goodsIds);
+}

+ 751 - 0
fs-service/src/main/java/com/fs/reward/service/impl/FsRewardGoodsOrderServiceImpl.java

@@ -0,0 +1,751 @@
+package com.fs.reward.service.impl;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.common.exception.CustomException;
+import com.fs.common.utils.DateUtils;
+import com.fs.core.utils.OrderCodeUtils;
+import com.fs.course.domain.FsCourseRewardRound;
+import com.fs.course.mapper.FsCourseRewardRoundMapper;
+import com.fs.erp.domain.*;
+import com.fs.erp.dto.BaseResponse;
+import com.fs.erp.dto.ErpOrderQueryRequert;
+import com.fs.erp.dto.ErpOrderQueryResponse;
+import com.fs.erp.dto.ErpRefundUpdateRequest;
+import com.fs.erp.service.IErpOrderService;
+import com.fs.his.domain.*;
+import com.fs.his.dto.ErpRemarkDTO;
+import com.fs.his.enums.BusinessTypeEnum;
+import com.fs.his.enums.PaymentMethodEnum;
+import com.fs.his.enums.ShipperCodeEnum;
+import com.fs.his.mapper.FsStoreMapper;
+import com.fs.his.mapper.FsUserAddressMapper;
+import com.fs.his.mapper.FsUserMapper;
+import com.fs.his.param.PayOrderParam;
+import com.fs.his.service.IFsExpressService;
+import com.fs.his.service.IFsStorePaymentService;
+import com.fs.his.service.IFsUserAddressService;
+import com.fs.huifuPay.domain.HuiFuRefundResult;
+import com.fs.huifuPay.sdk.opps.core.request.V2TradePaymentScanpayRefundRequest;
+import com.fs.huifuPay.service.HuiFuService;
+import com.fs.pay.mapper.PaymentMiniProgramConfigMapper;
+import com.fs.reward.domain.FsRewardGoods;
+import com.fs.reward.domain.FsRewardGoodsOrder;
+import com.fs.reward.mapper.FsRewardGoodsMapper;
+import com.fs.reward.mapper.FsRewardGoodsOrderMapper;
+import com.fs.reward.param.FsRewardGoodsOrderAddParam;
+import com.fs.reward.param.FsRewardGoodsOrderListParam;
+import com.fs.reward.param.FsRewardGoodsOrderPayParam;
+import com.fs.reward.service.IFsRewardGoodsOrderService;
+import com.fs.reward.vo.FsRewardGoodsOrderVO;
+import com.fs.tzBankPay.TzBankService.TzBankService;
+import com.fs.tzBankPay.doman.*;
+import com.fs.wx.order.domain.FsWxExpressTask;
+import com.fs.wx.order.mapper.FsWxExpressTaskMapper;
+import com.fs.ybPay.domain.OrderResult;
+import com.fs.ybPay.dto.OrderQueryDTO;
+import com.fs.ybPay.service.IPayService;
+import com.github.binarywang.wxpay.service.WxPayService;
+import com.google.common.collect.Lists;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.sql.Timestamp;
+import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static com.fs.his.utils.PhoneUtil.decryptPhone;
+
+@Slf4j
+@Service
+public class FsRewardGoodsOrderServiceImpl extends ServiceImpl<FsRewardGoodsOrderMapper, FsRewardGoodsOrder> implements IFsRewardGoodsOrderService {
+
+    @Autowired
+    private FsRewardGoodsMapper fsRewardGoodsMapper;
+    @Autowired
+    private FsUserAddressMapper fsUserAddressMapper;
+    @Autowired
+    private FsCourseRewardRoundMapper fsCourseRewardRoundMapper;
+    @Autowired
+    private FsUserMapper fsUserMapper;
+    @Autowired
+    private FsStoreMapper fsStoreMapper;
+    @Autowired
+    private IFsStorePaymentService storePaymentService;
+    @Autowired
+    private IPayService payService;
+    @Autowired
+    private HuiFuService huiFuService;
+    @Autowired
+    private IFsExpressService expressService;
+    @Autowired
+    private IFsUserAddressService fsUserAddressService;
+    @Autowired
+    @Qualifier("JSTErpOrderServiceImpl")
+    private IErpOrderService jstErpOrderService;
+    @Autowired
+    private RedisCache redisCache;
+    @Autowired
+    private FsWxExpressTaskMapper fsWxExpressTaskMapper;
+    @Autowired
+    private FsStoreMapper storeMapper;
+
+    /**
+     * 查询奖励商品订单列表
+     */
+    @Override
+    public List<FsRewardGoodsOrderVO> selectFsRewardGoodsOrderVOList(FsRewardGoodsOrderListParam param) {
+        return baseMapper.selectFsRewardGoodsOrderVOList(param);
+    }
+
+    /**
+     * 根据id查询奖励商品订单详情
+     */
+    @Override
+    public FsRewardGoodsOrderVO selectFsRewardGoodsOrderVOById(Long orderId) {
+        return baseMapper.selectFsRewardGoodsOrderVOById(orderId);
+    }
+
+    /**
+     * 支付订单
+     */
+    @Override
+    public Map<String, Object> payment(Long userId, FsRewardGoodsOrderPayParam param) {
+        // 查询订单
+        FsRewardGoodsOrder order = baseMapper.selectById(param.getOrderId());
+        if (Objects.isNull(order)) {
+            throw new CustomException("订单不存在!");
+        }
+
+        if (!order.getUserId().equals(userId)) {
+            throw new CustomException("非法操作!");
+        }
+
+        if (!order.getStatus().equals(1)) {
+            throw new CustomException("订单状态不正确!");
+        }
+
+        if (order.getExpiredTime().isBefore(LocalDateTime.now())) {
+            throw new CustomException("订单已过期!");
+        }
+
+        // 查询用户
+        FsUser user = fsUserMapper.selectFsUserByUserId(userId);
+        if (Objects.isNull(user)) {
+            throw new CustomException("用户不存在!");
+        }
+
+        FsUserAddress fsUserAddress = fsUserAddressMapper.selectFsUserAddressByAddressId(param.getAddressId());
+        if (Objects.isNull(fsUserAddress)) {
+            throw new CustomException("收货地址不存在!");
+        }
+
+        order.setUserName(fsUserAddress.getRealName().trim());
+        order.setMobile(fsUserAddress.getPhone().trim());
+        order.setAddress(fsUserAddress.getProvince() + " " + fsUserAddress.getCity() + " " + fsUserAddress.getDistrict() + " " + fsUserAddress.getDetail());
+        order.setPayType(param.getPayType());
+        baseMapper.updateById(order);
+
+        // 如果金额为0,直接完成支付
+        if (order.getPayMoney().compareTo(BigDecimal.ZERO) == 0) {
+            this.payConfirm(order.getOrderSn(), "", "", "", 2, "", "");
+
+            Map<String, Object> result = new HashMap<>();
+            result.put("isPay", 1);
+            result.put("message", "支付成功");
+            return result;
+        }
+
+        // 构建支付参数并使用统一支付处理
+        PayOrderParam payOrderParam = buildRewardPayOrderParam(param, order);
+        return storePaymentService.processPayment(payOrderParam);
+    }
+
+    /**
+     * 构建奖励商品支付参数
+     */
+    private PayOrderParam buildRewardPayOrderParam(FsRewardGoodsOrderPayParam param, FsRewardGoodsOrder order) {
+        PayOrderParam payOrderParam = new PayOrderParam();
+        payOrderParam.setOrderId(order.getOrderId());
+        payOrderParam.setOrderCode(order.getOrderSn());
+        payOrderParam.setAmount(order.getPayMoney());
+        payOrderParam.setUserId(order.getUserId());
+        payOrderParam.setCompanyId(null); // 奖励商品没有company概念
+        payOrderParam.setCompanyUserId(null);
+        payOrderParam.setPaymentMethod(getPaymentMethodEnum(param.getPayType()));
+        payOrderParam.setBusinessType(BusinessTypeEnum.REWARD_ORDER);
+        payOrderParam.setAppId(param.getAppId());
+        return payOrderParam;
+    }
+
+    /**
+     * 根据支付类型获取PaymentMethodEnum
+     */
+    private PaymentMethodEnum getPaymentMethodEnum(Integer payType) {
+        switch (payType) {
+            case 1:
+                return PaymentMethodEnum.MINIAPP_WECHAT;
+            case 2:
+            case 3:
+                return PaymentMethodEnum.H5_WECHAT;
+            case 4:
+                return PaymentMethodEnum.ALIPAY;
+            case 5:
+                return PaymentMethodEnum.H5_ALIPAY;
+            default:
+                throw new CustomException("不支持的支付类型!");
+        }
+    }
+
+
+    /**
+     * 支付回调
+     */
+    @Override
+    public R payConfirm(String orderSn, String payCode, String tradeNo, String payType, int type, String bankTransactionId, String bankSerialNo) {
+        log.info("进入奖励商品订单支付回调 orderSn: {}, payCode: {}, tradeNo: {}, payType: {}, type: {}",
+                orderSn, payCode, tradeNo, payType, type);
+        FsRewardGoodsOrder order = null;
+        if (type == 1) {
+            FsStorePayment storePayment = storePaymentService.selectFsStorePaymentByPaymentCode(payCode);
+            if (Objects.isNull(storePayment)) {
+                log.info("支付单号不存在 payCode: {}", payCode);
+                return R.error("支付单号不存在");
+            }
+
+            if (storePayment.getStatus() == 0) {
+                FsStorePayment paymentMap = new FsStorePayment();
+                paymentMap.setPaymentId(storePayment.getPaymentId());
+                paymentMap.setStatus(1);
+                paymentMap.setPayTime(new Date());
+                paymentMap.setTradeNo(tradeNo);
+                paymentMap.setBankSerialNo(bankSerialNo);
+                paymentMap.setBankTransactionId(bankTransactionId);
+                if(payType.equals(PayType.WECHAT_MINI_PROGRAM_PAYMENT.getCode())){
+                    paymentMap.setPayTypeCode(PayType.WECHAT_MINI_PROGRAM_PAYMENT.name());
+                }
+                else if(payType.equals(PayType.ALIPAY_BARCODE_PAYMENT.getCode())){
+                    paymentMap.setPayTypeCode(PayType.ALIPAY_BARCODE_PAYMENT.name());
+                }
+                if (storePayment.getPayMode().equals("yb")) {
+                    OrderQueryDTO orderQueryDTO = new OrderQueryDTO();
+                    orderQueryDTO.setUpOrderId(tradeNo);
+                    OrderResult orderResult = payService.getOrder(orderQueryDTO);
+                    paymentMap.setBankSerialNo(orderResult.getBankOrderId());
+                    paymentMap.setBankTransactionId(orderResult.getBankTrxId());
+                }
+                log.info("更新支付记录");
+                storePaymentService.updateFsStorePayment(paymentMap);
+                order = getById(Long.parseLong(storePayment.getBusinessId()));
+            }
+        } else if (type == 2) {
+            order = lambdaQuery().eq(FsRewardGoodsOrder::getOrderSn, orderSn).one();
+        } else {
+            return R.error("type类型错误");
+        }
+
+        if (Objects.isNull(order) || !order.getStatus().equals(1)) {
+            return R.error("非法操作");
+        }
+
+        order.setStatus(2); // 已支付
+        order.setPayTime(LocalDateTime.now());
+        order.setUpdateTime(LocalDateTime.now());
+        order.setUpdateBy("回调");
+        baseMapper.updateById(order);
+        
+        return R.ok();
+    }
+
+    /**
+     * 推送erp
+     */
+    @Override
+    public void pushErp() {
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+        List<FsRewardGoodsOrder> noPushOrder = baseMapper.selectNoPushOrders();
+
+        Map<Long, List<FsRewardGoodsOrder>> storeGroup = noPushOrder.stream().collect(Collectors.groupingBy(FsRewardGoodsOrder::getStoreId));
+        storeGroup.forEach((k, v) -> {
+            try {
+                FsStore fsStore = storeMapper.selectFsStoreByStoreId(k);
+                List<ErpOrder> erpOrders = v.stream().map(order -> {
+                    ErpOrder erpOrder = new ErpOrder();
+                    erpOrder.setVip_code(order.getUserId().toString());
+                    erpOrder.setPlatform_code(order.getOrderSn());
+                    erpOrder.setShop_code(fsStore.getShopCode());
+                    erpOrder.setSeller_memo(order.getRemark());
+                    erpOrder.setBuyer_account(order.getUserName());
+                    erpOrder.setRemark(order.getRemark());
+
+                    List<ErpOrderPayment> payments = new ArrayList<>();
+                    ErpOrderPayment payment = new ErpOrderPayment();
+                    payment.setPay_type_code("weixin");
+                    payment.setPayment(order.getPayMoney().doubleValue());
+                    payment.setPaytime(Timestamp.valueOf(order.getPayTime()));
+                    payments.add(payment);
+                    erpOrder.setPayments(payments);
+
+                    //大于100发发顺丰云配
+                    if (order.getPayMoney().compareTo(new BigDecimal(100)) > 0) {
+                        //发互联网医院SF.0235488558_241101
+                        FsExpress express = expressService.selectFsExpressByOmsCode("SF.0235488558_241101");
+                        erpOrder.setExpress_code(express.getOmsCode());
+                        order.setDeliveryName(express.getName());
+                        order.setDeliveryCode(express.getCode());
+                    } else {
+                        //发ztpdd
+                        FsExpress express = expressService.selectFsExpressByOmsCode("CDYJFYD.400011111705_241230");
+                        erpOrder.setExpress_code(express.getOmsCode());
+                        order.setDeliveryName(express.getName());
+                        order.setDeliveryCode(express.getCode());
+                    }
+
+                    ErpRemarkDTO remarkDTO = new ErpRemarkDTO();
+                    remarkDTO.setTotalPrice(order.getOrderMoney());
+                    remarkDTO.setPayPrice(order.getPayMoney());
+                    remarkDTO.setDeliveryMoney(BigDecimal.ZERO);
+                    remarkDTO.setPayMoney(order.getPayMoney());
+                    remarkDTO.setCouponMoney(BigDecimal.ZERO);
+                    remarkDTO.setOrderId(order.getOrderSn());
+                    remarkDTO.setYdMoney(BigDecimal.ZERO);
+                    remarkDTO.setPayTime(order.getPayTime().format(formatter));
+
+                    erpOrder.setSeller_memo(erpOrder.getSeller_memo() + JSONUtil.toJsonStr(remarkDTO));
+                    erpOrder.setRemark(erpOrder.getRemark() + JSONUtil.toJsonStr(remarkDTO));
+                    erpOrder.setOrder_type_code("order");
+                    erpOrder.setDeal_datetime(LocalDateTime.now().format(formatter));
+
+                    List<ErpOrderItem> details = new ArrayList<>();
+                    FsRewardGoods fsRewardGoods = JSON.parseObject(order.getGoodsJson(), FsRewardGoods.class);
+                    ErpOrderItem item = new ErpOrderItem();
+                    item.setItem_code(fsRewardGoods.getStoreGoodsSn().trim());
+                    item.setPrice(fsRewardGoods.getPrice().toString());
+                    item.setQty(1);
+                    item.setRefund(0);
+                    details.add(item);
+
+                    erpOrder.setDetails(details);
+                    erpOrder.setReceiver_name(order.getUserName().replaceAll("[^\\u4e00-\\u9fa5a-zA-Z0-9]", ""));
+
+                    String userPhone = order.getMobile().length() > 11 ? decryptPhone(order.getMobile()) : order.getMobile();
+                    erpOrder.setReceiver_mobile(userPhone);
+
+                    String[] address = order.getAddress().split(" ");
+                    if (address.length < 3) {
+                        String kdnAddress = fsUserAddressService.getKdnAddress(order.getAddress());
+                        Map<String, Object> addDAta = (Map<String, Object>) JSON.parse(kdnAddress);
+                        Map<String, String> add = (Map<String, String>) addDAta.get("Data");
+                        erpOrder.setReceiver_province(add.get("ProvinceName"));
+                        erpOrder.setReceiver_city(add.get("CityName"));
+                        erpOrder.setReceiver_district(add.get("ExpAreaName"));
+                        erpOrder.setReceiver_address(add.get("StreetName") + add.get("Address"));
+                    } else {
+                        erpOrder.setReceiver_province(address[0]);
+                        erpOrder.setReceiver_city(address[1]);
+                        erpOrder.setReceiver_district(address[2]);
+                        //处理地址多空隔问题
+                        if (address.length > 3) {
+                            StringBuilder addrs = new StringBuilder();
+                            for (int i = 3; i < address.length; i++) {
+                                addrs.append(address[i]);
+                            }
+                            erpOrder.setReceiver_address(addrs.toString());
+                        } else {
+                            erpOrder.setReceiver_address(address[2]);
+                        }
+                    }
+                    erpOrder.setReceiver_address(erpOrder.getReceiver_address().replace("+", "加"));
+                    erpOrder.setReceiver_address(erpOrder.getReceiver_address().replace("\n", ""));
+
+                    baseMapper.updateById(order);
+
+                    return erpOrder;
+                }).collect(Collectors.toList());
+
+                if (!erpOrders.isEmpty()) {
+                    jstErpOrderService.batchAddRewardOrder(erpOrders);
+                }
+            } catch (Exception e) {
+                log.error("推送ERP订单失败,店铺ID: {}, 订单数量: {}", k, v.size(), e);
+            }
+        });
+    }
+
+    /**
+     * 同步erp信息
+     */
+    @Override
+    public void syncErpData() {
+        List<FsRewardGoodsOrder> extendOrderSnList = baseMapper.selectPushOrders();
+
+        Map<Long, List<FsRewardGoodsOrder>> storeGroup = extendOrderSnList.stream().collect(Collectors.groupingBy(FsRewardGoodsOrder::getStoreId));
+        storeGroup.forEach((k, v) -> {
+            try {
+                FsStore fsStore = storeMapper.selectFsStoreByStoreId(k);
+                if (!extendOrderSnList.isEmpty()) {
+                    List<List<FsRewardGoodsOrder>> batches = Lists.partition(v, 100);
+                    for (List<FsRewardGoodsOrder> batch : batches) {
+                        int pageIndex = 1;
+                        boolean has_next;
+
+                        List<Long> orderIds = batch.stream().map(o -> Long.parseLong(o.getExtendOrderSn())).collect(Collectors.toList());
+                        do {
+                            try {
+                                Thread.sleep(300);
+                            } catch (InterruptedException ignored) {}
+
+                            ErpOrderQueryRequert request = new ErpOrderQueryRequert();
+                            request.setPage_size(100);
+                            request.setPage_index(pageIndex);
+                            request.setShop_id(fsStore.getShopCode());
+                            request.setO_ids(orderIds); // 设置当前批次
+
+                            ErpOrderQueryResponse response = jstErpOrderService.getRewardOrder(request);
+                            has_next = response.hasNextSafe();
+
+                            if (response.getOrders() != null) {
+                                for(ErpOrderQuery orderQuery : response.getOrders()){
+                                    if(orderQuery.getDeliverys()!=null&& !orderQuery.getDeliverys().isEmpty()){
+                                        for(ErpDeliverys delivery:orderQuery.getDeliverys()){
+                                            if(delivery.getDelivery()&& StringUtils.isNotEmpty(delivery.getMail_no())){
+                                                deliveryOrder(orderQuery.getCode(),delivery.getMail_no(),delivery.getExpress_code(),delivery.getExpress_name());
+                                                redisCache.deleteObject("delivery"+":"+orderQuery.getO_id());
+                                            }
+                                        }
+
+                                    }
+                                }
+                            }
+                            pageIndex++;
+                        } while (has_next);
+                    }
+                }
+            } catch (Exception e) {
+                log.error("同步ERP数据失败,店铺ID: {}, 订单数量: {}", k, v.size(), e);
+            }
+        });
+    }
+
+    /**
+     * 发货
+     */
+    private void deliveryOrder(String orderCode, String deliveryId, String deliverCode, String deliverName) {
+        FsRewardGoodsOrder order = lambdaQuery().eq(FsRewardGoodsOrder::getOrderSn, orderCode).one();
+        if (order == null || order.getStatus() != 2) {
+            return;
+        }
+
+        FsExpress express = expressService.selectFsExpressByOmsCode(deliverCode);
+        if (express != null) {
+            order.setDeliveryName(deliverName);
+            order.setDeliveryCode(express.getCode());
+        }
+        order.setStatus(3);
+        order.setDeliverySn(deliveryId);
+        order.setDeliveryTime(LocalDateTime.now());
+        baseMapper.updateById(order);
+
+        FsStore store = fsStoreMapper.selectFsStoreByStoreId(order.getStoreId());
+        //订阅物流回调
+        String lastFourNumber = "";
+        if (order.getDeliveryCode().equals(ShipperCodeEnum.SF.getValue())) {
+            if (store != null && store.getSendPhone() != null) {
+                lastFourNumber = store.getSendPhone();
+            } else {
+                lastFourNumber = order.getMobile().length() > 11 ?
+                        decryptPhone(order.getMobile()) :
+                        order.getMobile();
+            }
+            if (lastFourNumber.length() == 11) {
+                lastFourNumber = StrUtil.sub(lastFourNumber, lastFourNumber.length(), -4);
+            }
+        }
+
+        expressService.subscribeEspress(order.getOrderSn(), order.getDeliveryCode(), order.getDeliverySn(), lastFourNumber);
+
+        FsStorePayment params = new FsStorePayment();
+        params.setBusinessCode(order.getOrderSn());
+        params.setBusinessType(10);
+        List<FsStorePayment> fsStorePayments = storePaymentService.selectFsStorePaymentList(params);
+        if(CollectionUtils.isNotEmpty(fsStorePayments)){
+            FsStorePayment fsStorePayment = fsStorePayments.get(0);
+            FsWxExpressTask fsWxExpressTask = new FsWxExpressTask();
+            fsWxExpressTask.setUserId(order.getUserId());
+            fsWxExpressTask.setStatus(0);
+            fsWxExpressTask.setRetryCount(0);
+            fsWxExpressTask.setCreateTime(LocalDateTime.now());
+            fsWxExpressTask.setUpdateTime(LocalDateTime.now());
+            fsWxExpressTask.setOrderCode(order.getOrderSn());
+            fsWxExpressTask.setExpressCompany(Objects.nonNull(express) ? express.getCode() : "");
+            fsWxExpressTask.setExpressNo(deliveryId);
+            fsWxExpressTask.setAppid(fsStorePayment.getAppId());
+            fsWxExpressTaskMapper.insert(fsWxExpressTask);
+        }
+
+    }
+
+    /**
+     * 获取订单支付信息
+     */
+    @Override
+    public List<FsStorePayment> getPayInfo(String orderSn) {
+        FsStorePayment params = new FsStorePayment();
+        params.setBusinessCode(orderSn);
+        params.setBusinessType(10);
+        return storePaymentService.selectFsStorePaymentList(params);
+    }
+
+    /**
+     * 取消过期未支付订单
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void cancelExpireRewardOrder() {
+        // 查询需要取消的订单
+        List<FsRewardGoodsOrder> expiredOrders = lambdaQuery()
+                .eq(FsRewardGoodsOrder::getStatus, 1)
+                .le(FsRewardGoodsOrder::getExpiredTime, LocalDateTime.now())
+                .list();
+
+        if (CollectionUtils.isEmpty(expiredOrders)) {
+            return;
+        }
+
+        // 批量更新订单状态
+        List<Long> orderIds = expiredOrders.stream()
+                .map(FsRewardGoodsOrder::getOrderId)
+                .collect(Collectors.toList());
+
+        lambdaUpdate()
+                .set(FsRewardGoodsOrder::getStatus, -1)
+                .in(FsRewardGoodsOrder::getOrderId, orderIds)
+                .update();
+
+        // 回滚库存
+        for (FsRewardGoodsOrder order : expiredOrders) {
+            fsRewardGoodsMapper.addStock(order.getGoodsId());
+        }
+
+        log.info("取消过期奖励商品订单数量: {}, 回滚库存完成", orderIds.size());
+    }
+
+    /**
+     * 完成订单
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void completeOrder() {
+        // 自动完成发货时间超过7天且处于已发货状态的订单
+        LocalDateTime sevenDaysAgo = LocalDateTime.now().minusDays(7);
+
+        // 查询需要完成的订单:状态为3(已发货)且发货时间超过7天
+        List<FsRewardGoodsOrder> ordersToComplete = lambdaQuery()
+                .eq(FsRewardGoodsOrder::getStatus, 3)
+                .le(FsRewardGoodsOrder::getDeliveryTime, sevenDaysAgo)
+                .list();
+
+        if (CollectionUtils.isEmpty(ordersToComplete)) {
+            log.info("没有需要自动完成的订单");
+            return;
+        }
+
+        // 批量更新订单状态为已完成
+        List<Long> orderIds = ordersToComplete.stream()
+                .map(FsRewardGoodsOrder::getOrderId)
+                .collect(Collectors.toList());
+
+        lambdaUpdate()
+                .set(FsRewardGoodsOrder::getStatus, 4)
+                .set(FsRewardGoodsOrder::getFinishTime, LocalDateTime.now())
+                .set(FsRewardGoodsOrder::getUpdateTime, LocalDateTime.now())
+                .set(FsRewardGoodsOrder::getUpdateBy, "系统自动完成")
+                .in(FsRewardGoodsOrder::getOrderId, orderIds)
+                .update();
+
+        log.info("自动完成奖励商品订单数量: {}", orderIds.size());
+    }
+
+    /**
+     * 取消订单
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int cancelOrder(Long orderId) {
+        // 查询订单信息
+        FsRewardGoodsOrder order = baseMapper.selectById(orderId);
+        if (order == null) {
+            throw new CustomException("订单不存在");
+        }
+
+        // 订单状态:1待支付 2已支付 3已发货 4已完成 -1已取消
+        Integer status = order.getStatus();
+
+        // 已取消状态,直接返回成功
+        if (status == -1) {
+            return 1;
+        }
+
+        // 已完成状态,提示不可取消
+        if (status == 4) {
+            throw new CustomException("订单已完成,不可取消");
+        }
+
+        // 还原库存
+        fsRewardGoodsMapper.addStock(order.getGoodsId());
+
+        // 待支付状态,直接修改订单状态
+        if (status == 1) {
+            return lambdaUpdate()
+                    .set(FsRewardGoodsOrder::getStatus, -1)
+                    .set(FsRewardGoodsOrder::getCancelTime, LocalDateTime.now())
+                    .set(FsRewardGoodsOrder::getUpdateTime, LocalDateTime.now())
+                    .set(FsRewardGoodsOrder::getUpdateBy, "系统取消")
+                    .eq(FsRewardGoodsOrder::getOrderId, orderId)
+                    .update() ? 1 : 0;
+        }
+
+        // 已支付、已发货状态,先进行聚水潭订单取消,取消成功后再修改订单状态并退款
+        if (status == 2 || status == 3) {
+            return processShippedOrderCancellation(order);
+        }
+
+        throw new CustomException("订单状态异常,无法取消");
+    }
+
+    /**
+     * 处理已发货订单的取消
+     */
+    private int processShippedOrderCancellation(FsRewardGoodsOrder order) {
+        try {
+            // 先取消聚水潭订单
+            if (StringUtils.isNotEmpty(order.getExtendOrderSn())) {
+                cancelJstOrder(order);
+            }
+
+            // 执行退款
+            processRefund(order);
+
+            // 修改订单状态
+            return lambdaUpdate()
+                    .set(FsRewardGoodsOrder::getStatus, -1)
+                    .set(FsRewardGoodsOrder::getCancelTime, LocalDateTime.now())
+                    .set(FsRewardGoodsOrder::getUpdateTime, LocalDateTime.now())
+                    .set(FsRewardGoodsOrder::getUpdateBy, "系统取消")
+                    .eq(FsRewardGoodsOrder::getOrderId, order.getOrderId())
+                    .update() ? 1 : 0;
+
+        } catch (Exception e) {
+            log.error("已发货订单取消失败,订单号:{}", order.getOrderSn(), e);
+            throw new CustomException("订单取消失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 取消聚水潭订单
+     */
+    private void cancelJstOrder(FsRewardGoodsOrder order) {
+        try {
+            ErpRefundUpdateRequest requestDTO = new ErpRefundUpdateRequest();
+            requestDTO.setTid(order.getOrderSn());
+            requestDTO.setOid(order.getExtendOrderSn());
+            BaseResponse response = jstErpOrderService.cancelRewardOrder(requestDTO);
+
+            if (response == null || !response.getSuccess()) {
+                throw new CustomException("聚水潭订单取消失败");
+            }
+
+            log.info("聚水潭订单取消成功,订单号:{},外部订单号:{}", order.getOrderSn(), order.getExtendOrderSn());
+
+        } catch (CustomException e) {
+            throw e;
+        } catch (Exception e) {
+            log.error("聚水潭订单取消失败,订单号:{}", order.getOrderSn(), e);
+            throw new CustomException("聚水潭订单取消失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 处理退款
+     */
+    private void processRefund(FsRewardGoodsOrder order) {
+        if (order.getPayMoney() == null || order.getPayMoney().compareTo(BigDecimal.ZERO) <= 0) {
+            return; // 无需退款
+        }
+
+        String orderType = "reward";
+
+        try {
+            // 查询支付记录
+            FsStorePayment params = new FsStorePayment();
+            params.setBusinessType(10);
+            params.setBusinessCode(order.getOrderSn());
+            params.setStatus(1);
+            List<FsStorePayment> payments = storePaymentService.selectFsStorePaymentList(params);
+
+            if (payments != null && !payments.isEmpty()) {
+                FsStorePayment payment = payments.get(0);
+                processHfRefund(payment, orderType);
+            } else {
+                throw new CustomException("未找到支付明细");
+            }
+
+            log.info("退款处理成功,订单号:{},退款金额:{}", order.getOrderSn(), order.getPayMoney());
+
+        } catch (Exception e) {
+            log.error("退款处理失败,订单号:{}", order.getOrderSn(), e);
+            throw new CustomException("退款处理失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 汇付支付退款
+     */
+    private void processHfRefund(FsStorePayment payment, String orderType) {
+        V2TradePaymentScanpayRefundRequest request = new V2TradePaymentScanpayRefundRequest();
+        request.setOrdAmt(payment.getPayMoney().toString());
+        request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
+        request.setReqSeqId("refund-" + payment.getPayCode());
+        Map<String, Object> extendInfoMap = new HashMap<>();
+        extendInfoMap.put("org_req_seq_id", orderType + "-" + payment.getPayCode());
+        request.setExtendInfo(extendInfoMap);
+
+        HuiFuRefundResult refund = huiFuService.refund(request);
+        log.info("订单退款返回结果: {}" +  refund);
+        if ((refund.getResp_code().equals("00000000") || refund.getResp_code().equals("00000100")) &&
+            (refund.getTrans_stat().equals("S") || refund.getTrans_stat().equals("P"))) {
+            // 更新支付记录状态
+            updatePaymentRefundStatus(payment);
+        } else {
+            throw new CustomException("退款请求失败" + refund.getResp_desc());
+        }
+    }
+
+    /**
+     * 更新支付记录退款状态
+     */
+    private void updatePaymentRefundStatus(FsStorePayment payment) {
+        FsStorePayment paymentMap = new FsStorePayment();
+        paymentMap.setPaymentId(payment.getPaymentId());
+        paymentMap.setStatus(-1);
+        paymentMap.setRefundTime(DateUtils.getNowDate());
+        paymentMap.setRefundMoney(payment.getPayMoney());
+        storePaymentService.updateFsStorePayment(paymentMap);
+    }
+}

+ 105 - 0
fs-service/src/main/java/com/fs/reward/service/impl/FsRewardGoodsServiceImpl.java

@@ -0,0 +1,105 @@
+package com.fs.reward.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.exception.CustomException;
+import com.fs.common.utils.bean.BeanUtils;
+import com.fs.reward.domain.FsRewardGoods;
+import com.fs.reward.mapper.FsRewardGoodsMapper;
+import com.fs.reward.param.FsRewardGoodsAddParam;
+import com.fs.reward.param.FsRewardGoodsEditParam;
+import com.fs.reward.param.FsRewardGoodsListParam;
+import com.fs.reward.service.IFsRewardGoodsService;
+import com.fs.reward.vo.FsRewardGoodsVO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalDateTime;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+@Slf4j
+@Service
+public class FsRewardGoodsServiceImpl extends ServiceImpl<FsRewardGoodsMapper, FsRewardGoods> implements IFsRewardGoodsService {
+
+    /**
+     * 查询奖励商品列表
+     */
+    @Override
+    public List<FsRewardGoodsVO> selectFsRewardGoodsVOList(FsRewardGoodsListParam param) {
+        return baseMapper.selectFsRewardGoodsVOList(param);
+    }
+
+    /**
+     * 查询奖励商品详情
+     */
+    @Override
+    public FsRewardGoodsVO selectFsRewardGoodsVOById(Long goodsId) {
+        return baseMapper.selectFsRewardGoodsVOById(goodsId);
+    }
+
+    /**
+     * 添加奖励商品
+     */
+    @Override
+    public int insertFsRewardGoods(FsRewardGoodsAddParam param) {
+        FsRewardGoods goods = new FsRewardGoods();
+        BeanUtils.copyProperties(param, goods, "goodsId");
+
+        if (isNameUsedByOthers(null, param.getGoodsName())) {
+            throw new CustomException("商品名称已存在");
+        }
+
+        goods.setIsDel(0);
+        goods.setCreateTime(LocalDateTime.now());
+        return baseMapper.insert(goods);
+    }
+
+    /**
+     * 商品名称是否已存在
+     */
+    private boolean isNameUsedByOthers(Long goodsId, String goodsName) {
+        FsRewardGoods rewardGoods = lambdaQuery()
+                .eq(FsRewardGoods::getGoodsName, goodsName)
+                .eq(FsRewardGoods::getIsDel, 0)
+                .last("limit 1")
+                .one();
+
+        if (rewardGoods == null) {
+            return false;
+        }
+
+        return !Objects.equals(rewardGoods.getGoodsId(), goodsId);
+    }
+
+    /**
+     * 修改奖励商品
+     */
+    @Override
+    public int updateFsRewardGoods(FsRewardGoodsEditParam param) {
+        FsRewardGoods goods = getById(param.getGoodsId());
+        if (goods == null) {
+            throw new CustomException("商品不存在");
+        }
+
+        if (isNameUsedByOthers(param.getGoodsId(), param.getGoodsName())) {
+            throw new CustomException("商品名称已存在");
+        }
+
+        BeanUtils.copyProperties(param, goods, "goodsId", "createBy");
+        return baseMapper.updateById(goods);
+    }
+
+    /**
+     * 逻辑删除商品
+     */
+    @Override
+    public int logicDeleteFsRewardGoods(Long[] goodsIds) {
+        lambdaUpdate()
+            .set(FsRewardGoods::getIsDel, 1)
+            .set(FsRewardGoods::getUpdateTime, LocalDateTime.now())
+            .in(FsRewardGoods::getGoodsId, Arrays.asList(goodsIds))
+            .update();
+        return 1;
+    }
+}

+ 168 - 0
fs-service/src/main/java/com/fs/reward/vo/FsRewardGoodsOrderVO.java

@@ -0,0 +1,168 @@
+package com.fs.reward.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+public class FsRewardGoodsOrderVO {
+
+    /**
+     * 主键ID
+     */
+    private Long orderId;
+
+    /**
+     * 订单号
+     */
+    private String orderSn;
+
+    /**
+     * 外部订单号
+     */
+    private String extendOrderSn;
+
+    /**
+     * 店铺ID
+     */
+    private Long storeId;
+
+    /**
+     * 店铺名称
+     */
+    private String storeName;
+
+    /**
+     * 用户ID
+     */
+    private Long userId;
+
+    /**
+     * 用户名称
+     */
+    private String nickName;
+
+    /**
+     * 收货人
+     */
+    private String userName;
+
+    /**
+     * 收货手机号
+     */
+    private String mobile;
+
+    /**
+     * 收货地址
+     */
+    private String address;
+
+    /**
+     * 订单金额
+     */
+    private BigDecimal orderMoney;
+
+    /**
+     * 支付金额
+     */
+    private BigDecimal payMoney;
+
+    /**
+     * 支付类型
+     */
+    private Integer payType;
+
+    /**
+     * 支付时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime payTime;
+
+    /**
+     * 快递公司编号
+     */
+    private String deliveryCode;
+
+    /**
+     * 快递名称
+     */
+    private String deliveryName;
+
+    /**
+     * 快递单号
+     */
+    private String deliverySn;
+
+    /**
+     * 中奖记录ID
+     */
+    private Long roundId;
+
+    /**
+     * 商品ID
+     */
+    private Long goodsId;
+
+    /**
+     * 商品信息
+     */
+    private String goodsJson;
+
+    /**
+     * 状态 1待支付 2已支付 3已发货 4已完成 -1已取消
+     */
+    private Integer status;
+
+    /**
+     * 发货时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime deliveryTime;
+
+    /**
+     * 完成时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime finishTime;
+
+    /**
+     * 创建时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime createTime;
+
+    /**
+     * 取消时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime cancelTime;
+
+    /**
+     * 过期时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime expiredTime;
+
+    /**
+     * 创建人
+     */
+    private String createBy;
+
+    /**
+     * 修改时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime updateTime;
+
+    /**
+     * 修改人
+     */
+    private String updateBy;
+
+    /**
+     * 备注
+     */
+    private String remark;
+}

+ 113 - 0
fs-service/src/main/java/com/fs/reward/vo/FsRewardGoodsVO.java

@@ -0,0 +1,113 @@
+package com.fs.reward.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+public class FsRewardGoodsVO {
+
+    /**
+     * 主键ID
+     */
+    private Long goodsId;
+
+    /**
+     * 商品名称
+     */
+    private String goodsName;
+
+    /**
+     * 封面
+     */
+    private String goodsImg;
+
+    /**
+     * 轮播图
+     */
+    private String goodsImages;
+
+    /**
+     * 商品介绍
+     */
+    private String goodsIntroduce;
+
+    /**
+     * 商品描述
+     */
+    private String goodsDesc;
+
+    /**
+     * 店铺ID
+     */
+    private Long storeId;
+
+    /**
+     * 店铺名称
+     */
+    private String storeName;
+
+    /**
+     * 店铺商品编码
+     */
+    private String storeGoodsSn;
+
+    /**
+     * 状态 1上架 0下架
+     */
+    private Integer status;
+
+    /**
+     * 单价
+     */
+    private BigDecimal price;
+
+    /**
+     * 原价
+     */
+    private BigDecimal opPrice;
+
+    /**
+     * 库存
+     */
+    private Integer stock;
+
+    /**
+     * 序号
+     */
+    private Integer sort;
+
+    /**
+     * 0未删除 1已删除
+     */
+    private Integer isDel;
+
+    /**
+     * 备注
+     */
+    private String remark;
+
+    /**
+     * 创建时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime createTime;
+
+    /**
+     * 创建人
+     */
+    private String createBy;
+
+    /**
+     * 修改时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime updateTime;
+
+    /**
+     * 修改人
+     */
+    private String updateBy;
+}

+ 38 - 0
fs-service/src/main/java/com/fs/reward/vo/FsRewardListVO.java

@@ -0,0 +1,38 @@
+package com.fs.reward.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@ApiModel("中奖列表VO")
+@Data
+public class FsRewardListVO {
+
+    @ApiModelProperty("中奖记录ID")
+    private String roundId;
+
+    @ApiModelProperty("中奖码")
+    private String code;
+
+    @ApiModelProperty("中奖名称")
+    private String codeName;
+
+    @ApiModelProperty("中奖时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("中奖内容")
+    private String content;
+
+    @ApiModelProperty("商品ID")
+    private Long goodsId;
+
+    @ApiModelProperty("订单ID")
+    private Long orderId;
+
+    @ApiModelProperty("订单类型1企微2app")
+    private Long orderType;
+}

+ 19 - 0
fs-service/src/main/java/com/fs/utils/Range.java

@@ -0,0 +1,19 @@
+package com.fs.utils;
+
+public class Range<T extends Comparable<T>> {
+    private final T min;
+    private final T max;
+
+    public Range(T min, T max) {
+        this.min = min;
+        this.max = max;
+    }
+
+    public boolean contains(T value) {
+        return value.compareTo(min) >= 0 && value.compareTo(max) <= 0;
+    }
+
+    public static <T extends Comparable<T>> Range<T> between(T min, T max) {
+        return new Range<>(min, max);
+    }
+}

+ 4 - 4
fs-service/src/main/resources/application-config-dev.yml

@@ -91,12 +91,12 @@ nuonuo:
 # 存储捅配置
 # 存储捅配置
 tencent_cloud_config:
-  secret_id: AKIDiMq9lDf2EOM9lIfqqfKo7FNgM5meD0sT
-  secret_key: u5SuS80342xzx8FRBukza9lVNHKNMSaB
-  bucket: jkj-1323137866
+  secret_id: AKIDLl1tguyrZ6QddTCi2BLJ4e3OXVIuJVVK
+  secret_key: g9R6kLrMp8EDzXszylLispiQxHRN6cw5
+  bucket: hylj-1323137866
   app_id: 1323137866
   region: ap-chongqing
-  proxy: jkj
+  proxy: hylj
 tmp_secret_config:
   secret_id: AKIDCj7NSNAovtqeJpBau8GZ4CGB71thXIxX
   secret_key: lTB5zwqqz7CNhzDOWivFWedgfTBgxgBT

+ 1 - 1
fs-service/src/main/resources/application-config-druid.yml

@@ -60,7 +60,7 @@ watch:
   password3: v9xsKuqn_$d2y
 
 fs :
-  commonApi: http://172.16.0.16:8010
+  commonApi: http://127.0.0.1:7771
   h5CommonApi: http://119.29.195.254:8010
 nuonuo:
   key: 10924508

+ 5 - 1
fs-service/src/main/resources/mapper/course/FsCourseCheckinActivityMapper.xml

@@ -19,11 +19,12 @@
         <result property="updateBy" column="update_by"/>
         <result property="updateTime" column="update_time"/>
         <result property="remark" column="remark"/>
+        <result property="prizeType" column="prize_type"/>
     </resultMap>
 
     <sql id="selectFsCourseCheckinActivityVo">
         select activity_id, activity_name, start_time, end_time, checkin_days, company_ids, project_ids,
-               notify_template, status, create_by, create_time, update_by, update_time, remark
+               notify_template, status, create_by, create_time, update_by, update_time, remark, prize_type
         from fs_course_checkin_activity
     </sql>
 
@@ -70,6 +71,7 @@
             <if test="updateBy != null and updateBy != ''">update_by,</if>
             <if test="updateTime != null">update_time,</if>
             <if test="remark != null and remark != ''">remark,</if>
+            <if test="prizeType != null">prize_type,</if>
         </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="activityName != null and activityName != ''">#{activityName},</if>
@@ -85,6 +87,7 @@
             <if test="updateBy != null and updateBy != ''">#{updateBy},</if>
             <if test="updateTime != null">#{updateTime},</if>
             <if test="remark != null and remark != ''">#{remark},</if>
+            <if test="prizeType != null">#{prizeType},</if>
         </trim>
     </insert>
 
@@ -102,6 +105,7 @@
             <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
             <if test="updateTime != null">update_time = #{updateTime},</if>
             <if test="remark != null">remark = #{remark},</if>
+            <if test="prizeType != null">prize_type = #{prizeType},</if>
         </trim>
         where activity_id = #{activityId}
     </update>

+ 12 - 4
fs-service/src/main/resources/mapper/course/FsCourseCheckinPrizeMapper.xml

@@ -11,17 +11,19 @@
         <result property="prizeName" column="prize_name"/>
         <result property="redpacketAmount" column="redpacket_amount"/>
         <result property="goodsId" column="goods_id"/>
+        <result property="grandGiftId" column="grand_gift_id"/>
         <result property="sort" column="sort"/>
         <result property="createBy" column="create_by"/>
         <result property="createTime" column="create_time"/>
         <result property="updateBy" column="update_by"/>
         <result property="updateTime" column="update_time"/>
         <result property="remark" column="remark"/>
+        <result property="freeCouponId" column="free_coupon_id"/>
     </resultMap>
 
     <sql id="selectFsCourseCheckinPrizeVo">
         select prize_id, activity_id, prize_type, prize_name, redpacket_amount, goods_id,
-               sort, create_by, create_time, update_by, update_time, remark
+               sort, create_by, create_time, update_by, update_time, remark, grand_gift_id, free_coupon_id
         from fs_course_checkin_prize
     </sql>
 
@@ -62,12 +64,14 @@
             <if test="prizeName != null and prizeName != ''">prize_name,</if>
             <if test="redpacketAmount != null">redpacket_amount,</if>
             <if test="goodsId != null">goods_id,</if>
+            <if test="grandGiftId != null">grand_gift_id,</if>
             <if test="sort != null">sort,</if>
             <if test="createBy != null and createBy != ''">create_by,</if>
             <if test="createTime != null">create_time,</if>
             <if test="updateBy != null and updateBy != ''">update_by,</if>
             <if test="updateTime != null">update_time,</if>
             <if test="remark != null and remark != ''">remark,</if>
+            <if test="freeCouponId != null">free_coupon_id,</if>
         </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="activityId != null">#{activityId},</if>
@@ -75,12 +79,14 @@
             <if test="prizeName != null and prizeName != ''">#{prizeName},</if>
             <if test="redpacketAmount != null">#{redpacketAmount},</if>
             <if test="goodsId != null">#{goodsId},</if>
+            <if test="grandGiftId != null">#{grand_gift_id},</if>
             <if test="sort != null">#{sort},</if>
             <if test="createBy != null and createBy != ''">#{createBy},</if>
             <if test="createTime != null">#{createTime},</if>
             <if test="updateBy != null and updateBy != ''">#{updateBy},</if>
             <if test="updateTime != null">#{updateTime},</if>
             <if test="remark != null and remark != ''">#{remark},</if>
+            <if test="freeCouponId != null">#{freeCouponId},</if>
         </trim>
     </insert>
 
@@ -92,10 +98,12 @@
             <if test="prizeName != null and prizeName != ''">prize_name = #{prizeName},</if>
             <if test="redpacketAmount != null">redpacket_amount = #{redpacketAmount},</if>
             <if test="goodsId != null">goods_id = #{goodsId},</if>
+            <if test="grandGiftId != null">grand_gift_id = #{grandGiftId},</if>
             <if test="sort != null">sort = #{sort},</if>
             <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
             <if test="updateTime != null">update_time = #{updateTime},</if>
             <if test="remark != null">remark = #{remark},</if>
+            <if test="freeCouponId != null">free_coupon_id = #{freeCouponId},</if>
         </trim>
         where prize_id = #{prizeId}
     </update>
@@ -117,11 +125,11 @@
 
     <insert id="batchInsertFsCourseCheckinPrize">
         insert into fs_course_checkin_prize
-        (activity_id, prize_type, prize_name, redpacket_amount, goods_id, sort, create_by, create_time, remark)
+        (activity_id, prize_type, prize_name, redpacket_amount, goods_id, grand_gift_id, sort, create_by, create_time, remark, free_coupon_id)
         values
         <foreach collection="list" item="item" separator=",">
-            (#{item.activityId}, #{item.prizeType}, #{item.prizeName}, #{item.redpacketAmount}, #{item.goodsId},
-             #{item.sort}, #{item.createBy}, #{item.createTime}, #{item.remark})
+            (#{item.activityId}, #{item.prizeType}, #{item.prizeName}, #{item.redpacketAmount}, #{item.goodsId}, #{item.grandGiftId},
+             #{item.sort}, #{item.createBy}, #{item.createTime}, #{item.remark}, #{item.freeCouponId})
         </foreach>
     </insert>
 

+ 27 - 14
fs-service/src/main/resources/mapper/course/FsCourseCheckinReceiveMapper.xml

@@ -23,16 +23,15 @@
         <result property="updateTime" column="update_time"/>
         <result property="outBatchNo" column="out_batch_no"/>
         <result property="batchId" column="batch_id"/>
+        <result property="grandGiftId" column="grand_gift_id"/>
+        <result property="grandGiftCode" column="grand_gift_code"/>
+        <result property="grandGiftType" column="grand_gift_type"/>
+        <result property="freeCouponId" column="free_coupon_id"/>
 
     </resultMap>
 
     <sql id="selectFsCourseCheckinReceiveVo">
-        select receive_id, r.activity_id, r.prize_id, r.user_id, r.company_id, r.project_id,
-               r.prize_type, r.prize_name, r.redpacket_amount, r.goods_id,
-               r.receive_status, r.receive_time,
-               r.notify_status, r.notify_time,
-               r.create_time, r.update_time,r.out_batch_no,r.batch_id
-        from fs_course_checkin_receive r
+        select * from fs_course_checkin_receive r
     </sql>
 
     <select id="selectFsCourseCheckinReceiveByReceiveId" parameterType="Long"
@@ -42,10 +41,13 @@
     </select>
 
     <select id="selectFsCourseCheckinReceiveList" parameterType="com.fs.course.domain.FsCourseCheckinReceive"
-            resultMap="FsCourseCheckinReceiveResult">
-        <include refid="selectFsCourseCheckinReceiveVo"/>
-        left join fs_user u on r.user_id = u.user_id
-        left join fs_course_checkin_activity a on r.activity_id = a.activity_id
+            resultType="com.fs.course.domain.FsCourseCheckinReceive">
+        select r.*,
+               o.order_id as grandGiftOrderId,
+               uc.status as useState
+        from fs_course_checkin_receive r
+        left join fs_reward_goods_order o on o.receive_id = r.receive_id
+        left join fs_user_coupon uc on r.user_id = uc.user_id and r.free_coupon_id = uc.id
         <where>
             <if test="activityId != null">
                 and r.activity_id = #{activityId}
@@ -96,6 +98,10 @@
             <if test="updateTime != null">update_time,</if>
             <if test="outBatchNo != null">out_batch_no,</if>
             <if test="batchId != null">batch_id,</if>
+            <if test="grandGiftId != null">grand_gift_id,</if>
+            <if test="grandGiftCode != null and grandGiftCode != ''">grand_gift_code,</if>
+            <if test="grandGiftType != null">grand_gift_type,</if>
+            <if test="freeCouponId != null">free_coupon_id,</if>
         </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="activityId != null">#{activityId},</if>
@@ -107,6 +113,7 @@
             <if test="prizeName != null and prizeName != ''">#{prizeName},</if>
             <if test="redpacketAmount != null">#{redpacketAmount},</if>
             <if test="goodsId != null">#{goodsId},</if>
+            <if test="goodsName != null">#{goods_name},</if>
             <if test="receiveStatus != null">#{receiveStatus},</if>
             <if test="receiveTime != null">#{receiveTime},</if>
             <if test="notifyStatus != null">#{notifyStatus},</if>
@@ -115,6 +122,10 @@
             <if test="updateTime != null">#{updateTime},</if>
             <if test="outBatchNo != null">#{outBatchNo},</if>
             <if test="batchId != null">#{batchId},</if>
+            <if test="grandGiftId != null">#{grandGiftId},</if>
+            <if test="grandGiftCode != null and grandGiftCode != ''">#{grandGiftCode},</if>
+            <if test="grandGiftType != null">#{grandGiftType},</if>
+            <if test="freeCouponId != null">#{freeCouponId},</if>
         </trim>
     </insert>
 
@@ -130,6 +141,7 @@
             <if test="prizeName != null">prize_name = #{prizeName},</if>
             <if test="redpacketAmount != null">redpacket_amount = #{redpacketAmount},</if>
             <if test="goodsId != null">goods_id = #{goodsId},</if>
+            <if test="goodsName != null">goods_name = #{goodsName},</if>
             <if test="receiveStatus != null">receive_status = #{receiveStatus},</if>
             <if test="receiveTime != null">receive_time = #{receiveTime},</if>
             <if test="notifyStatus != null">notify_status = #{notifyStatus},</if>
@@ -137,6 +149,10 @@
             <if test="updateTime != null">update_time = #{updateTime},</if>
             <if test="outBatchNo != null">out_batch_no = #{outBatchNo},</if>
             <if test="batchId != null">batch_id = #{batchId},</if>
+            <if test="grandGiftId != null">grand_gift_id = #{grandGiftId},</if>
+            <if test="grandGiftCode != null">grand_gift_code = #{grandGiftCode},</if>
+            <if test="grandGiftType != null">grand_gift_type = #{grandGiftType},</if>
+            <if test="freeCouponId != null">free_coupon_id = #{freeCouponId},</if>
         </trim>
         where receive_id = #{receiveId}
     </update>
@@ -170,10 +186,7 @@
 
     <select id="selectReceiveListByActivityAndType" resultMap="FsCourseCheckinReceiveResult">
         select
-            r.receive_id, r.activity_id, r.prize_id, r.user_id, r.company_id, r.project_id,
-            r.prize_type, r.prize_name, r.redpacket_amount, r.goods_id,
-            r.receive_status, r.receive_time, r.notify_status, r.notify_time,
-            r.create_time, r.update_time, r.out_batch_no, r.batch_id
+            r.*
         from fs_course_checkin_receive r
         where r.activity_id = #{activityId}
         <if test="prizeType != null">

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

@@ -0,0 +1,137 @@
+<?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>

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

@@ -0,0 +1,213 @@
+<?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="ruleName"    column="rule_name"    />
+        <result property="goodsId"    column="goods_id"    />
+        <result property="goodsPrice"    column="goods_price"    />
+    </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, goods_price, rule_name
+        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>
+            <if test="goodsPrice != null">goods_price,</if>
+            <if test="ruleName != null">rule_name,</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>
+            <if test="goodsPrice != null">#{goodsPrice},</if>
+            <if test="ruleName != null">#{ruleName},</if>
+         </trim>
+    </insert>
+    <insert id="insertFsCourseRewardRoundApp">
+        insert into fs_app_course_reward_round
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="id != null">id,</if>
+            <if test="rewardId != null">reward_id,</if>
+            <if test="userId != null">user_id,</if>
+            <if test="rewardType != null">reward_type,</if>
+            <if test="appCustomerRoleId != null">app_customer_role_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>
+            <if test="goodsPrice != null">goods_price,</if>
+            <if test="ruleName != null">rule_name,</if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="id != null">#{id},</if>
+            <if test="rewardId != null">#{rewardId},</if>
+            <if test="userId != null">#{userId},</if>
+            <if test="rewardType != null">#{rewardType},</if>
+            <if test="appCustomerRoleId != null">#{appCustomerRoleId},</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>
+            <if test="goodsPrice != null">#{goodsPrice},</if>
+            <if test="ruleName != null">#{ruleName},</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>
+
+    <select id="getRewardListByUserId" resultType="com.fs.reward.vo.FsRewardListVO">
+        select
+            round.id                as  roundId,
+            round.rule_id           as  code,
+            round.create_time       as  createTime,
+            reward.actual_rewards   as  content,
+            round.goods_id          as  goodsId,
+            o.order_id              as  orderId,
+            round.rule_name         as  codeName,
+            1 as orderType
+        from fs_course_reward_round round
+        left join fs_course_reward reward on round.reward_id = reward.id
+        left join fs_reward_goods_order o on round.id = o.round_id
+        where round.user_id = #{userId} and round.reward_type in (4,5,6)
+    </select>
+
+</mapper>

+ 21 - 1
fs-service/src/main/resources/mapper/his/FsCouponMapper.xml

@@ -21,10 +21,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="limitType"    column="limit_type"    />
         <result property="limitCount"    column="limit_count"    />
         <result property="cateIds"    column="cate_ids"    />
+        <result property="freeGoodsId" column="free_goods_id" />
+        <result property="projectId" column="project_id" />
+        <result property="logo" column="logo" />
     </resultMap>
 
     <sql id="selectFsCouponVo">
-        select coupon_id,limit_count,cate_ids, title,limit_day,limit_type, create_time,rate, limit_time, price, number, coupon_type, min_price, remain_number, status, update_time from fs_coupon
+        select coupon_id,limit_count,cate_ids, title,limit_day,limit_type, create_time,rate, limit_time, price, number, coupon_type, min_price, remain_number, status, update_time,free_goods_id,project_id,logo from fs_coupon
     </sql>
 
     <select id="selectFsCouponList" parameterType="FsCoupon" resultMap="FsCouponResult">
@@ -46,6 +49,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         where coupon_id = #{couponId}
     </select>
 
+    <select id="selectFsCouponListByIds" resultType="com.fs.his.domain.FsCoupon">
+        select * from fs_coupon
+        where coupon_id in
+        <foreach item="couponId" collection="ids" open="(" separator="," close=")">
+            #{couponId}
+        </foreach>
+    </select>
+
     <insert id="insertFsCoupon" parameterType="FsCoupon" useGeneratedKeys="true" keyProperty="couponId">
         insert into fs_coupon
         <trim prefix="(" suffix=")" suffixOverrides=",">
@@ -64,6 +75,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="limitType != null">limit_type,</if>
             <if test="limitCount != null">limit_count,</if>
             <if test="cateIds != null">cate_ids,</if>
+            <if test="freeGoodsId != null">free_goods_id,</if>
+            <if test="projectId != null">project_id,</if>
+            <if test="logo != null and logo != ''">logo,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="title != null">#{title},</if>
@@ -81,6 +95,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="limitType != null">#{limitType},</if>
             <if test="limitCount != null">#{limitCount},</if>
             <if test="cateIds != null">#{cateIds},</if>
+            <if test="freeGoodsId != null">#{freeGoodsId},</if>
+            <if test="projectId != null">#{projectId},</if>
+            <if test="logo != null and logo != ''">#{logo},</if>
          </trim>
     </insert>
 
@@ -102,6 +119,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="limitType != null">limit_type = #{limitType},</if>
             <if test="limitCount != null">limit_count = #{limitCount},</if>
             <if test="cateIds != null">cate_ids = #{cateIds},</if>
+            <if test="freeGoodsId != null">free_goods_id = #{freeGoodsId},</if>
+            <if test="projectId != null">project_id = #{projectId},</if>
+            <if test="logo != null">logo = #{logo},</if>
         </trim>
         where coupon_id = #{couponId}
     </update>

+ 5 - 1
fs-service/src/main/resources/mapper/his/FsPackageMapper.xml

@@ -49,10 +49,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="description"    column="description"    />
         <result property="solarTerm"    column="solar_term"    />
         <result property="appIds"    column="app_ids"    />
+        <result property="projectId"    column="project_id"    />
     </resultMap>
 
     <sql id="selectFsPackageVo">
-        select package_id,description,usage_per_use_count,icd_code,images,doctor_remark,second_name,follow_temp_id,product_cost_price,inquiry_cost_price,total_cost_price,follow_num,`explain`,indication,store_id,private_type,recipe_type,counts,usage_frequency_unit, package_name,img_url,is_show,total_price,cycle,duration,`desc`,product_type,price,tags,sales,disease_type,num,package_sub_type,describe_json,pay_type,package_type, sort, product_json, status, create_time, update_time, is_del,solar_term,app_ids from fs_package
+        select package_id,description,usage_per_use_count,icd_code,images,doctor_remark,second_name,follow_temp_id,product_cost_price,inquiry_cost_price,total_cost_price,follow_num,`explain`,indication,store_id,private_type,recipe_type,counts,usage_frequency_unit, package_name,img_url,is_show,total_price,cycle,duration,`desc`,product_type,price,tags,sales,disease_type,num,package_sub_type,describe_json,pay_type,package_type, sort, product_json, status, create_time, update_time, is_del,solar_term,app_ids,project_id from fs_package
     </sql>
 
     <select id="selectFsPackageList" parameterType="FsPackage" resultMap="FsPackageResult">
@@ -180,6 +181,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="solarTerm != null">solar_term,</if>
             <if test="appIds != null">app_ids,</if>
             <if test="courseId != null">course_id,</if>
+            <if test="projectId != null">project_id,</if>
         </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="packageName != null">#{packageName},</if>
@@ -226,6 +228,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="solarTerm != null">#{solarTerm},</if>
             <if test="appIds != null">#{appIds},</if>
             <if test="courseId != null">#{courseId},</if>
+            <if test="projectId != null">#{projectId},</if>
         </trim>
     </insert>
 
@@ -276,6 +279,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="solarTerm != null">solar_term = #{solarTerm},</if>
             <if test="appIds != null ">app_ids = #{appIds},</if>
             <if test="courseId != null">course_id = #{courseId},</if>
+            <if test="projectId != null">project_id = #{projectId},</if>
         </trim>
         where package_id = #{packageId}
     </update>

+ 26 - 0
fs-service/src/main/resources/mapper/his/FsUserCouponMapper.xml

@@ -42,6 +42,32 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <include refid="selectFsUserCouponVo"/>
         where id = #{id}
     </select>
+    <select id="selectFsUserCouponListUVO" resultType="com.fs.his.vo.FsUserCouponListUVO">
+        select
+        uc.id,uc.coupon_code,uc.status,uc.use_time,c.limit_time,c.title,c.price,c.min_price,c.coupon_type,c.free_goods_id,c.logo,
+            CASE
+                WHEN c.coupon_type IN (3, 7) THEN '免单券'
+                WHEN c.coupon_type IN (5, 6) THEN
+                    IF(c.project_id IS NOT NULL, '优惠券', '现金券')
+                ELSE ''
+            END AS shortName
+        from fs_user_coupon uc
+        left join fs_coupon c on c.coupon_id=uc.coupon_id
+        where 1=1
+        <if test = ' maps.userId != null '>
+           and uc.user_id = #{maps.userId}
+        </if>
+        <if test = ' maps.couponType != null '>
+           and c.coupon_type = #{maps.couponType}
+        </if>
+        <if test = 'maps.status != null      '>
+           and uc.status = #{maps.status}
+        </if>
+        <if test = 'maps.goodsId != null      '>
+           and c.free_goods_id = #{maps.goodsId}
+        </if>
+         order by uc.create_time desc
+    </select>
 
     <insert id="insertFsUserCoupon" parameterType="FsUserCoupon" useGeneratedKeys="true" keyProperty="id">
         insert into fs_user_coupon

+ 52 - 0
fs-service/src/main/resources/mapper/reward/FsRewardGoodsMapper.xml

@@ -0,0 +1,52 @@
+<?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.reward.mapper.FsRewardGoodsMapper">
+
+    <select id="selectFsRewardGoodsVOList" resultType="com.fs.reward.vo.FsRewardGoodsVO">
+        select
+            rg.*,
+            s.store_name
+        from fs_reward_goods rg
+        left join fs_store s on rg.store_id = s.store_id
+        where rg.is_del = 0
+        <if test="goodsId != null">
+            and rg.goods_id = #{goodsId}
+        </if>
+        <if test="goodsName != null and goodsName != ''">
+            and rg.goods_name like concat('%', #{goodsName}, '%')
+        </if>
+        <if test="storeId != null">
+            and rg.store_id = #{storeId}
+        </if>
+        <if test="storeGoodsSn != null and storeGoodsSn != ''">
+            and rg.store_goods_sn = #{storeGoodsSn}
+        </if>
+        <if test="status != null">
+            and rg.status = #{status}
+        </if>
+        order by rg.sort, rg.goods_id
+    </select>
+
+    <select id="selectFsRewardGoodsVOById" resultType="com.fs.reward.vo.FsRewardGoodsVO">
+        select
+            rg.*,
+            s.store_name
+        from fs_reward_goods rg
+        left join fs_store s on rg.store_id = s.store_id
+        where goods_id = #{goodsId}
+    </select>
+
+    <update id="reduceStock">
+        update fs_reward_goods 
+        set stock = stock - 1 
+        where goods_id = #{goodsId} and stock > 0
+    </update>
+
+    <update id="addStock">
+        update fs_reward_goods 
+        set stock = stock + 1 
+        where goods_id = #{goodsId}
+    </update>
+</mapper>

+ 93 - 0
fs-service/src/main/resources/mapper/reward/FsRewardGoodsOrderMapper.xml

@@ -0,0 +1,93 @@
+<?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.reward.mapper.FsRewardGoodsOrderMapper">
+
+    <select id="selectFsRewardGoodsOrderVOList" resultType="com.fs.reward.vo.FsRewardGoodsOrderVO">
+        select
+            rgo.*,
+            u.nick_name,
+            s.store_name
+        from fs_reward_goods_order rgo
+        left join fs_user u on rgo.user_id = u.user_id
+        left join fs_store s on s.store_id = rgo.store_id
+        <where>
+            <if test="storeId != null">
+                and rgo.store_id = #{storeId}
+            </if>
+            <if test="userId != null">
+                and rgo.user_id = #{userId}
+            </if>
+            <if test="receiveUserName != null">
+                and rgo.user_name = #{receiveUserName}
+            </if>
+            <if test="receiveUserPhone != null">
+                and rgo.mobile = #{receiveUserPhone}
+            </if>
+            <if test="orderSn != null and orderSn != ''">
+                and rgo.order_sn = #{orderSn}
+            </if>
+            <if test="deliverySn != null and deliverySn != ''">
+                and rgo.delivery_id = #{deliverySn}
+            </if>
+            <if test="status != null">
+                and rgo.status = #{status}
+            </if>
+            <if test="sCreateTime != null">
+                and rgo.create_time >= #{sCreateTime}
+            </if>
+            <if test="eCreateTime != null">
+                <![CDATA[
+                and rgo.create_time < date_add(#{eCreateTime}, interval 1 day)
+                ]]>
+            </if>
+            <if test="sPayTime != null">
+                and rgo.pay_time >= #{sPayTime}
+            </if>
+            <if test="ePayTime != null">
+                <![CDATA[
+                and rgo.pay_time < date_add(#{ePayTime}, interval 1 day)
+                ]]>
+            </if>
+            <if test="sDeliveryTime != null">
+                and rgo.delivery_time >= #{sDeliveryTime}
+            </if>
+            <if test="eDeliveryTime != null">
+                <![CDATA[
+                and rgo.delivery_time < date_add(#{eDeliveryTime}, interval 1 day)
+                ]]>
+            </if>
+        </where>
+        order by rgo.create_time desc, rgo.order_id desc
+    </select>
+
+    <select id="selectFsRewardGoodsOrderVOById" resultType="com.fs.reward.vo.FsRewardGoodsOrderVO">
+        select
+            rgo.*,
+            u.nick_name,
+            s.store_name
+        from fs_reward_goods_order rgo
+        left join fs_user u on rgo.user_id = u.user_id
+        left join fs_store s on s.store_id = rgo.store_id
+        where rgo.order_id = #{orderId}
+    </select>
+
+    <select id="selectNoPushOrders" resultType="com.fs.reward.domain.FsRewardGoodsOrder">
+        select
+            o.*
+        from fs_reward_goods_order o
+        left join fs_store s on o.store_id = s.store_id
+        where o.status = 2 and s.delivery_type = 4 and o.extend_order_sn is null
+        limit 1000
+    </select>
+
+    <select id="selectPushOrders" resultType="com.fs.reward.domain.FsRewardGoodsOrder">
+        select
+            o.*
+        from fs_reward_goods_order o
+        left join fs_store s on o.store_id = s.store_id
+        where o.status = 2 and s.delivery_type = 4 and o.extend_order_sn is not null
+    </select>
+
+</mapper>

+ 6 - 0
fs-user-app/src/main/java/com/fs/app/controller/HuifuPayController.java

@@ -11,6 +11,7 @@ import com.fs.huifuPay.domain.HuiFuOnlinePay;
 import com.fs.huifuPay.domain.HuiFuOnlineRefund;
 import com.fs.huifuPay.domain.HuiFuResult;
 import com.fs.huifuPay.sdk.opps.core.utils.HuiFuUtils;
+import com.fs.reward.service.IFsRewardGoodsOrderService;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -36,6 +37,8 @@ public class HuifuPayController {
     private IFsCourseProductOrderService courseProductOrderService;
     @Autowired
     private IFsStorePaymentService storePaymentService;
+    @Autowired
+    private IFsRewardGoodsOrderService rewardGoodsOrderService;
 
     org.slf4j.Logger logger= LoggerFactory.getLogger(getClass());
     @Autowired
@@ -112,6 +115,9 @@ public class HuifuPayController {
 //                        logger.error("-------分账明细回调错误{}", e.getMessage());
 //                    }
                     break;
+                case "reward":
+                    rewardGoodsOrderService.payConfirm("",orderId[1],huiFuResult.getHf_seq_id(),"",1,huiFuResult.getOut_trans_id(),huiFuResult.getParty_order_id());
+                    break;
             }
         }
 

+ 8 - 0
fs-user-app/src/main/java/com/fs/app/controller/IntegralController.java

@@ -145,6 +145,14 @@ public class IntegralController extends  AppBaseController {
         return integralOrderService.createOrder(param);
     }
 
+    @Login
+    @ApiOperation("计算订单")
+    @PostMapping("/compute")
+    public R compute(@Validated @RequestBody FsIntegralOrderComputeParam param){
+        param.setUserId(Long.parseLong(getUserId()));
+        return integralOrderService.computeOrder(param);
+    }
+
     @Login
     @ApiOperation("取消订单")
     @PostMapping("/cannelOrder")

+ 89 - 0
fs-user-app/src/main/java/com/fs/app/controller/RewardGoodsController.java

@@ -0,0 +1,89 @@
+package com.fs.app.controller;
+
+import cn.hutool.core.util.StrUtil;
+import com.fs.app.annotation.Login;
+import com.fs.common.annotation.RepeatSubmit;
+import com.fs.common.core.domain.R;
+import com.fs.common.utils.StringUtils;
+import com.fs.his.dto.ExpressInfoDTO;
+import com.fs.his.enums.ShipperCodeEnum;
+import com.fs.his.service.IFsExpressService;
+import com.fs.reward.domain.FsRewardGoodsOrder;
+import com.fs.reward.param.FsRewardGoodsOrderPayParam;
+import com.fs.reward.service.IFsRewardGoodsOrderService;
+import com.fs.reward.service.IFsRewardGoodsService;
+import com.fs.reward.vo.FsRewardGoodsOrderVO;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Objects;
+
+import static com.fs.his.utils.PhoneUtil.decryptPhone;
+
+
+@Slf4j
+@Api("中奖处理接口")
+@RestController
+@RequestMapping(value="/app/reward")
+public class RewardGoodsController extends AppBaseController {
+
+    @Autowired
+    private IFsRewardGoodsService rewardGoodsService;
+    @Autowired
+    private IFsRewardGoodsOrderService rewardGoodsOrderService;
+    @Autowired
+    private IFsExpressService expressService;
+
+    @Login
+    @ApiOperation("商品详情接口")
+    @GetMapping("/goodsDetail")
+    public R goodsDetail(@RequestParam Long goodsId) {
+        return R.ok().put("data", rewardGoodsService.selectFsRewardGoodsVOById(goodsId));
+    }
+
+    @RepeatSubmit
+    @Login
+    @ApiOperation("通用支付接口")
+    @PostMapping("/payment")
+    public R payment(@Validated @RequestBody FsRewardGoodsOrderPayParam param) {
+        return R.ok(rewardGoodsOrderService.payment(Long.parseLong(getUserId()), param));
+    }
+
+    @Login
+    @ApiOperation("订单详情接口")
+    @GetMapping("/orderDetail")
+    public R getOrderDetail(@RequestParam Long orderId) {
+        Long userId = Long.parseLong(getUserId());
+        FsRewardGoodsOrderVO fsRewardGoodsOrderVO = rewardGoodsOrderService.selectFsRewardGoodsOrderVOById(orderId);
+        if (Objects.isNull(fsRewardGoodsOrderVO) || !fsRewardGoodsOrderVO.getUserId().equals(userId)) {
+            return R.error("订单不存在");
+        }
+        return R.ok().put("data", fsRewardGoodsOrderVO);
+    }
+
+    @Login
+    @ApiOperation("物流信息")
+    @GetMapping(value = "/getExpress/{orderId}")
+    public R getExpress(@PathVariable Long orderId) {
+        FsRewardGoodsOrder order = rewardGoodsOrderService.getById(orderId);
+        if (Objects.isNull(order)) {
+            return R.error("订单不存在");
+        }
+
+        ExpressInfoDTO expressInfoDTO=null;
+        if(StringUtils.isNotEmpty(order.getDeliverySn())){
+            String lastFourNumber = "";
+            if (order.getDeliveryCode().equals(ShipperCodeEnum.SF.getValue())) {
+                lastFourNumber = order.getMobile().length() > 11 ? decryptPhone(order.getMobile()) : order.getMobile();
+                lastFourNumber = StrUtil.sub(lastFourNumber, lastFourNumber.length(), -4);
+            }
+            expressInfoDTO = expressService.getExpressInfo(order.getOrderSn(),order.getDeliveryCode(),order.getDeliverySn(),lastFourNumber);
+        }
+        return R.ok().put("data", expressInfoDTO).put("deliveryName", order.getDeliveryName());
+    }
+
+}

+ 7 - 0
fs-user-app/src/main/java/com/fs/app/controller/TzPayController.java

@@ -12,6 +12,7 @@ import com.fs.his.service.IFsInquiryOrderService;
 import com.fs.his.service.IFsIntegralOrderService;
 import com.fs.his.service.IFsPackageOrderService;
 import com.fs.his.service.IFsStoreOrderService;
+import com.fs.reward.service.IFsRewardGoodsOrderService;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysConfigMapper;
 import com.fs.tzBankPay.config.TzBankConfig;
@@ -44,6 +45,8 @@ public class TzPayController extends BaseController
     private IFsUserVipOrderService userVipOrderService;
     @Autowired
     private IFsIntegralOrderService integralOrderService;
+    @Autowired
+    private IFsRewardGoodsOrderService rewardGoodsOrderService;
 
     @RequestMapping(value = "/payNotify",method = RequestMethod.POST)
     public String payNotify(@RequestBody String  decryptForm) throws Exception {
@@ -74,6 +77,10 @@ public class TzPayController extends BaseController
             else if (tzBankOrderDecrypt.getOrderNo().indexOf("integral")>-1) {
                 integralOrderService.payConfirm("",tzBankOrderDecrypt.getOrderNo().substring(8),tzBankOrderDecrypt.getOrderFlowNo(),tzBankOrderDecrypt.getPayType(),1,null,null);
             }
+            else if (tzBankOrderDecrypt.getOrderNo().indexOf("reward")>-1) {
+                rewardGoodsOrderService.payConfirm("",tzBankOrderDecrypt.getOrderNo().substring(6),tzBankOrderDecrypt.getOrderFlowNo(),tzBankOrderDecrypt.getPayType(),1,null,null);
+            }
+
         }
         return decrypt;
     }

+ 13 - 1
fs-user-app/src/main/java/com/fs/app/controller/WxPayController.java

@@ -16,6 +16,7 @@ import com.fs.course.service.IFsUserCourseOrderService;
 import com.fs.course.service.IFsUserVipOrderService;
 import com.fs.his.domain.FsPayConfig;
 import com.fs.his.service.*;
+import com.fs.reward.service.IFsRewardGoodsOrderService;
 import com.fs.sop.service.ISopUserLogsInfoService;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.service.ISysConfigService;
@@ -77,7 +78,8 @@ public class WxPayController {
     private ISopUserLogsInfoService iSopUserLogsInfoService;
     @Autowired
     private IFsIntegralOrderService integralOrderService;
-
+    @Autowired
+    private IFsRewardGoodsOrderService rewardGoodsOrderService;
     @Autowired
     private IFsCourseProductOrderService courseProductOrderService;
 
@@ -111,21 +113,31 @@ public class WxPayController {
                 switch (orderId[0]){
                     case "inquiry":
                         inquiryOrderService.payConfirm("",orderId[1],tradeNo,"",1,tradeNo,null);
+                        break;
                     case "store":
                         storeOrderService.payConfirm("",orderId[1],tradeNo,"",1,tradeNo,null);
+                        break;
                     case "package":
                         packageOrderService.payConfirm("",orderId[1],tradeNo,"",1,tradeNo,null);
+                        break;
                     case "course":
                         courseOrderService.payConfirm("",orderId[1],tradeNo,"",1,tradeNo,null);
+                        break;
                     case "appvip":
                         userVipOrderService.payConfirm("",orderId[1],tradeNo,"",1,tradeNo,null);
+                        break;
                     case "integral":
                         integralOrderService.payConfirm("",orderId[1],tradeNo,"",1,tradeNo,null);
                         break;
                     case "product":
                         courseProductOrderService.payConfirm("",orderId[1],tradeNo,"",1,tradeNo,null);
+                        break;
                     case "payment":
                         paymentService.payConfirm(orderId[1], outtradeno,tradeNo,null);
+                        break;
+                    case "reward":
+                        rewardGoodsOrderService.payConfirm("",orderId[1],tradeNo,"",1,tradeNo,null);
+                        break;
                 }
                 return WxPayNotifyResponse.success("处理成功!");
             }else{

+ 6 - 0
fs-user-app/src/main/java/com/fs/app/controller/YbPayController.java

@@ -11,6 +11,7 @@ import com.fs.his.service.IFsIntegralOrderService;
 import com.fs.his.service.IFsPackageOrderService;
 import com.fs.his.service.IFsStoreOrderService;
 
+import com.fs.reward.service.IFsRewardGoodsOrderService;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysConfigMapper;
 import com.fs.ybPay.domain.OrderCallback;
@@ -46,6 +47,8 @@ public class YbPayController extends BaseController
     private IFsUserVipOrderService userVipOrderService;
     @Autowired
     private IFsIntegralOrderService integralOrderService;
+    @Autowired
+    private IFsRewardGoodsOrderService rewardGoodsOrderService;
 
     @PostMapping("/payNotify")
     public String payNotify(@RequestBody Map<String, String> params, @RequestBody OrderCallback o) {
@@ -79,6 +82,9 @@ public class YbPayController extends BaseController
             case "integral":
                 integralOrderService.payConfirm("",orderId[1],o.getUpOrderId(),"",1,null,null);
                 break;
+            case "reward":
+                rewardGoodsOrderService.payConfirm("",orderId[1],o.getUpOrderId(),"",1,null,null);
+                break;
         }
         System.out.println(o);
         return "SUCCESS";

+ 17 - 7
fs-user-app/src/main/java/com/fs/app/controller/course/CourseFsUserController.java

@@ -2,7 +2,6 @@ package com.fs.app.controller.course;
 
 
 import cn.hutool.core.util.ObjectUtil;
-import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fs.app.annotation.UserOperationLog;
 import com.fs.app.controller.AppBaseController;
 import com.fs.common.annotation.RepeatSubmit;
@@ -10,11 +9,8 @@ import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
 import com.fs.app.annotation.Login;
-import com.fs.common.core.domain.model.LoginUser;
-import com.fs.common.utils.SecurityUtils;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.course.domain.FsCourseCheckinReceive;
-import com.fs.course.dto.BatchSendCourseDTO;
 import com.fs.course.param.*;
 import com.fs.course.param.newfs.FsUserCourseAddCompanyUserParam;
 import com.fs.course.param.newfs.FsUserCourseVideoLinkParam;
@@ -27,13 +23,10 @@ import com.fs.his.domain.FsUser;
 import com.fs.his.dto.FsUserBindSalesParamDTO;
 import com.fs.his.enums.FsUserOperationEnum;
 import com.fs.his.service.IFsUserService;
-import com.fs.im.dto.OpenImResponseDTO;
-import com.fs.im.service.OpenIMService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
@@ -248,4 +241,21 @@ public class CourseFsUserController extends AppBaseController {
         return R.ok().put("data", receiveList);
     }
 
+    @Login
+    @ApiOperation("获取大礼品奖励类型对应规则")
+    @PostMapping("/getRewardRules")
+    public R getRewardRules(@Valid @RequestBody FsCourseGrandGiftParam param) {
+        param.setUserId(Long.parseLong(getUserId()));
+        return R.ok().put("data", fsCourseCheckinReceiveService.getRewardRules(param));
+    }
+
+    @Login
+    @ApiOperation("发放活动奖励")
+    @PostMapping("/sendActiveReward")
+    @RepeatSubmit
+    public R sendActiveReward(@Valid @RequestBody FsCourseGrandGiftParam param) {
+        param.setUserId(Long.parseLong(getUserId()));
+        return fsCourseCheckinReceiveService.sendActiveReward(param);
+    }
+
 }