yuhongqi 5 dni temu
rodzic
commit
710caebb5f
66 zmienionych plików z 2510 dodań i 131 usunięć
  1. 17 0
      fs-admin/src/main/java/com/fs/his/task/Task.java
  2. 105 0
      fs-admin/src/main/java/com/fs/live/controller/LiveAutoTaskController.java
  3. 10 2
      fs-admin/src/main/java/com/fs/live/controller/LiveGoodsController.java
  4. 104 0
      fs-admin/src/main/java/com/fs/live/controller/LiveRewardRecordController.java
  5. 11 1
      fs-admin/src/main/java/com/fs/live/controller/LiveWatchUserController.java
  6. 10 0
      fs-company/src/main/java/com/fs/company/controller/common/CommonController.java
  7. 105 0
      fs-company/src/main/java/com/fs/company/controller/live/LiveAutoTaskController.java
  8. 12 0
      fs-company/src/main/java/com/fs/company/controller/live/LiveController.java
  9. 11 2
      fs-company/src/main/java/com/fs/company/controller/live/LiveGoodsController.java
  10. 104 0
      fs-company/src/main/java/com/fs/company/controller/live/LiveRewardRecordController.java
  11. 10 0
      fs-company/src/main/java/com/fs/company/controller/live/LiveWatchUserController.java
  12. 1 1
      fs-live-app/src/main/java/com/fs/app/config/ProductionWordFilter.java
  13. 27 6
      fs-live-app/src/main/java/com/fs/app/controller/LiveController.java
  14. 11 2
      fs-live-app/src/main/java/com/fs/app/controller/LiveGoodsController.java
  15. 22 1
      fs-live-app/src/main/java/com/fs/app/controller/LiveLotteryController.java
  16. 0 1
      fs-live-app/src/main/java/com/fs/app/controller/LiveWatchUserController.java
  17. 141 12
      fs-live-app/src/main/java/com/fs/app/task/Task.java
  18. 93 7
      fs-live-app/src/main/java/com/fs/app/websocket/service/WebSocketServer.java
  19. 8 0
      fs-service/src/main/java/com/fs/his/mapper/FsUserMapper.java
  20. 3 0
      fs-service/src/main/java/com/fs/his/service/IFsUserService.java
  21. 6 0
      fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java
  22. 60 0
      fs-service/src/main/java/com/fs/live/domain/LiveAutoTask.java
  23. 4 0
      fs-service/src/main/java/com/fs/live/domain/LiveGoods.java
  24. 4 0
      fs-service/src/main/java/com/fs/live/domain/LiveLotteryRegistration.java
  25. 60 0
      fs-service/src/main/java/com/fs/live/domain/LiveRewardRecord.java
  26. 77 0
      fs-service/src/main/java/com/fs/live/mapper/LiveAutoTaskMapper.java
  27. 24 2
      fs-service/src/main/java/com/fs/live/mapper/LiveGoodsMapper.java
  28. 5 0
      fs-service/src/main/java/com/fs/live/mapper/LiveLotteryConfMapper.java
  29. 5 1
      fs-service/src/main/java/com/fs/live/mapper/LiveLotteryProductConfMapper.java
  30. 12 7
      fs-service/src/main/java/com/fs/live/mapper/LiveLotteryRegistrationMapper.java
  31. 0 1
      fs-service/src/main/java/com/fs/live/mapper/LiveMapper.java
  32. 5 0
      fs-service/src/main/java/com/fs/live/mapper/LiveOrderMapper.java
  33. 4 0
      fs-service/src/main/java/com/fs/live/mapper/LiveRedConfMapper.java
  34. 61 0
      fs-service/src/main/java/com/fs/live/mapper/LiveRewardRecordMapper.java
  35. 3 0
      fs-service/src/main/java/com/fs/live/mapper/LiveWatchUserMapper.java
  36. 11 0
      fs-service/src/main/java/com/fs/live/param/LiveOrderFinishParam.java
  37. 29 0
      fs-service/src/main/java/com/fs/live/param/LotteryPO.java
  38. 78 0
      fs-service/src/main/java/com/fs/live/service/ILiveAutoTaskService.java
  39. 10 1
      fs-service/src/main/java/com/fs/live/service/ILiveGoodsService.java
  40. 9 0
      fs-service/src/main/java/com/fs/live/service/ILiveLotteryConfService.java
  41. 5 0
      fs-service/src/main/java/com/fs/live/service/ILiveOrderService.java
  42. 4 0
      fs-service/src/main/java/com/fs/live/service/ILiveRedConfService.java
  43. 61 0
      fs-service/src/main/java/com/fs/live/service/ILiveRewardRecordService.java
  44. 4 0
      fs-service/src/main/java/com/fs/live/service/ILiveService.java
  45. 5 0
      fs-service/src/main/java/com/fs/live/service/ILiveWatchUserService.java
  46. 233 0
      fs-service/src/main/java/com/fs/live/service/impl/LiveAutoTaskServiceImpl.java
  47. 18 16
      fs-service/src/main/java/com/fs/live/service/impl/LiveDataServiceImpl.java
  48. 54 13
      fs-service/src/main/java/com/fs/live/service/impl/LiveGoodsServiceImpl.java
  49. 44 0
      fs-service/src/main/java/com/fs/live/service/impl/LiveLotteryConfServiceImpl.java
  50. 30 3
      fs-service/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java
  51. 52 16
      fs-service/src/main/java/com/fs/live/service/impl/LiveRedConfServiceImpl.java
  52. 93 0
      fs-service/src/main/java/com/fs/live/service/impl/LiveRewardRecordServiceImpl.java
  53. 169 11
      fs-service/src/main/java/com/fs/live/service/impl/LiveServiceImpl.java
  54. 21 6
      fs-service/src/main/java/com/fs/live/service/impl/LiveWatchUserServiceImpl.java
  55. 1 0
      fs-service/src/main/java/com/fs/live/vo/LiveGoodsListVo.java
  56. 1 0
      fs-service/src/main/java/com/fs/live/vo/LiveGoodsVo.java
  57. 52 0
      fs-service/src/main/java/com/fs/live/vo/LiveLotteryConfVo.java
  58. 47 0
      fs-service/src/main/java/com/fs/live/vo/LiveLotteryProductListVo.java
  59. 114 0
      fs-service/src/main/resources/mapper/live/LiveAutoTaskMapper.xml
  60. 70 14
      fs-service/src/main/resources/mapper/live/LiveGoodsMapper.xml
  61. 12 0
      fs-service/src/main/resources/mapper/live/LiveLotteryProductConfMapper.xml
  62. 5 1
      fs-service/src/main/resources/mapper/live/LiveLotteryRegistrationMapper.xml
  63. 18 2
      fs-service/src/main/resources/mapper/live/LiveMapper.xml
  64. 1 1
      fs-service/src/main/resources/mapper/live/LiveOrderMapper.xml
  65. 104 0
      fs-service/src/main/resources/mapper/live/LiveRewardRecordMapper.xml
  66. 20 1
      fs-service/src/main/resources/mapper/live/LiveWatchUserMapper.xml

+ 17 - 0
fs-admin/src/main/java/com/fs/his/task/Task.java

@@ -39,6 +39,7 @@ import com.fs.his.vo.FsSubOrderResultVO;
 import com.fs.im.dto.*;
 import com.fs.im.service.IImService;
 import com.fs.live.domain.LiveOrder;
+import com.fs.live.param.LiveOrderFinishParam;
 import com.fs.live.service.ILiveOrderService;
 import com.fs.qw.service.IQwAppContactWayService;
 import com.fs.qw.service.IQwExternalContactTransferLogService;
@@ -463,6 +464,22 @@ public class Task {
         }
     }
 
+    public void finishLiveOrder(){
+        List<LiveOrder> orders = liveOrderService.selectLiveOrderByFinish();
+        for (LiveOrder order : orders) {
+            // 订单已超过48小时,执行关闭操作
+            LiveOrderFinishParam param = new LiveOrderFinishParam();
+            param.setOrderId(order.getOrderId());
+            try {
+                // todo yhq 未完成
+                liveOrderService.autoFinishOrder(param);
+            }catch (Exception e){
+                logger.info("订单已超过48小时关闭异常"+param);
+            }
+
+        }
+    }
+
 
     public void finishStoreOrderByXN(){
         List<FsStoreOrder> orders = fsStoreOrderMapper.selectFinishStoreOrderByXN();

+ 105 - 0
fs-admin/src/main/java/com/fs/live/controller/LiveAutoTaskController.java

@@ -0,0 +1,105 @@
+package com.fs.live.controller;
+
+import java.util.List;
+
+import com.fs.common.core.domain.R;
+import com.fs.live.domain.LiveAutoTask;
+import com.fs.live.service.ILiveAutoTaskService;
+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 com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+
+/**
+ * 直播间自动化任务配置Controller
+ *
+ * @author fs
+ * @date 2025-08-29
+ */
+@RestController
+@RequestMapping("/live/task")
+public class LiveAutoTaskController extends BaseController
+{
+    @Autowired
+    private ILiveAutoTaskService liveAutoTaskService;
+
+    /**
+     * 查询直播间自动化任务配置列表
+     */
+    @PreAuthorize("@ss.hasPermi('shop:task:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(LiveAutoTask liveAutoTask)
+    {
+        startPage();
+        List<LiveAutoTask> list = liveAutoTaskService.selectLiveAutoTaskList(liveAutoTask);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出直播间自动化任务配置列表
+     */
+    @PreAuthorize("@ss.hasPermi('shop:task:export')")
+    @Log(title = "直播间自动化任务配置", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(LiveAutoTask liveAutoTask)
+    {
+        List<LiveAutoTask> list = liveAutoTaskService.selectLiveAutoTaskList(liveAutoTask);
+        ExcelUtil<LiveAutoTask> util = new ExcelUtil<LiveAutoTask>(LiveAutoTask.class);
+        return util.exportExcel(list, "直播间自动化任务配置数据");
+    }
+
+    /**
+     * 获取直播间自动化任务配置详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('shop:task:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(liveAutoTaskService.selectLiveAutoTaskById(id));
+    }
+
+    /**
+     * 新增直播间自动化任务配置
+     */
+    @PreAuthorize("@ss.hasPermi('shop:task:add')")
+    @Log(title = "直播间自动化任务配置", businessType = BusinessType.INSERT)
+    @PostMapping
+    public R add(@RequestBody LiveAutoTask liveAutoTask)
+    {
+        return liveAutoTaskService.insertLiveAutoTask(liveAutoTask);
+    }
+
+    /**
+     * 修改直播间自动化任务配置
+     */
+    @PreAuthorize("@ss.hasPermi('shop:task:edit')")
+    @Log(title = "直播间自动化任务配置", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody LiveAutoTask liveAutoTask)
+    {
+        return toAjax(liveAutoTaskService.updateLiveAutoTask(liveAutoTask));
+    }
+
+    /**
+     * 删除直播间自动化任务配置
+     */
+    @PreAuthorize("@ss.hasPermi('shop:task:remove')")
+    @Log(title = "直播间自动化任务配置", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(liveAutoTaskService.deleteLiveAutoTaskByIds(ids));
+    }
+}

+ 10 - 2
fs-admin/src/main/java/com/fs/live/controller/LiveGoodsController.java

@@ -102,9 +102,9 @@ public class LiveGoodsController extends BaseController
     @PreAuthorize("@ss.hasPermi('live:liveGoods:edit')")
     @Log(title = "直播商品", businessType = BusinessType.UPDATE)
     @PutMapping
-    public AjaxResult edit(@RequestBody LiveGoods liveGoods)
+    public R edit(@RequestBody LiveGoods liveGoods)
     {
-        return toAjax(liveGoodsService.updateLiveGoods(liveGoods));
+        return liveGoodsService.updateLiveGoods(liveGoods);
     }
 
     /**
@@ -156,4 +156,12 @@ public class LiveGoodsController extends BaseController
     public R handleDeleteSelected(@RequestBody LiveGoodsListVo listVo) {
         return liveGoodsService.handleDeleteSelectedAdmin(listVo);
     }
+
+    /**
+     * 更新展示状态
+     */
+    @PostMapping("/handleIsShowChange")
+    public R handleIsShowChange(@RequestBody LiveGoodsListVo listVo) {
+        return liveGoodsService.handleIsShowChange(listVo);
+    }
 }

+ 104 - 0
fs-admin/src/main/java/com/fs/live/controller/LiveRewardRecordController.java

@@ -0,0 +1,104 @@
+package com.fs.live.controller;
+
+import java.util.List;
+
+import com.fs.live.domain.LiveRewardRecord;
+import com.fs.live.service.ILiveRewardRecordService;
+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 com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+
+/**
+ * 用户直播间奖励记录Controller
+ *
+ * @author fs
+ * @date 2025-08-27
+ */
+@RestController
+@RequestMapping("/live/record")
+public class LiveRewardRecordController extends BaseController
+{
+    @Autowired
+    private ILiveRewardRecordService liveRewardRecordService;
+
+    /**
+     * 查询用户直播间奖励记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('live:record:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(LiveRewardRecord liveRewardRecord)
+    {
+        startPage();
+        List<LiveRewardRecord> list = liveRewardRecordService.selectLiveRewardRecordList(liveRewardRecord);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出用户直播间奖励记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('live:record:export')")
+    @Log(title = "用户直播间奖励记录", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(LiveRewardRecord liveRewardRecord)
+    {
+        List<LiveRewardRecord> list = liveRewardRecordService.selectLiveRewardRecordList(liveRewardRecord);
+        ExcelUtil<LiveRewardRecord> util = new ExcelUtil<LiveRewardRecord>(LiveRewardRecord.class);
+        return util.exportExcel(list, "用户直播间奖励记录数据");
+    }
+
+    /**
+     * 获取用户直播间奖励记录详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('live:record:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(liveRewardRecordService.selectLiveRewardRecordById(id));
+    }
+
+    /**
+     * 新增用户直播间奖励记录
+     */
+    @PreAuthorize("@ss.hasPermi('live:record:add')")
+    @Log(title = "用户直播间奖励记录", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody LiveRewardRecord liveRewardRecord)
+    {
+        return toAjax(liveRewardRecordService.insertLiveRewardRecord(liveRewardRecord));
+    }
+
+    /**
+     * 修改用户直播间奖励记录
+     */
+    @PreAuthorize("@ss.hasPermi('live:record:edit')")
+    @Log(title = "用户直播间奖励记录", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody LiveRewardRecord liveRewardRecord)
+    {
+        return toAjax(liveRewardRecordService.updateLiveRewardRecord(liveRewardRecord));
+    }
+
+    /**
+     * 删除用户直播间奖励记录
+     */
+    @PreAuthorize("@ss.hasPermi('live:record:remove')")
+    @Log(title = "用户直播间奖励记录", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(liveRewardRecordService.deleteLiveRewardRecordByIds(ids));
+    }
+}

+ 11 - 1
fs-admin/src/main/java/com/fs/live/controller/LiveWatchUserController.java

@@ -1,4 +1,4 @@
-package com.fs.live.controller.controller;
+package com.fs.live.controller;
 
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
@@ -118,4 +118,14 @@ public class LiveWatchUserController extends BaseController
         return toAjax(liveWatchUserService.changeUserState(liveId, userId));
     }
 
+    /**
+     * 封禁用户账号
+     */
+    @PreAuthorize("@ss.hasPermi('live:liveWatchUser:edit')")
+    @Log(title = "直播间观看用户", businessType = BusinessType.UPDATE)
+    @GetMapping("/blockUser/{userId}")
+    public AjaxResult blockUser(@PathVariable Long userId) {
+        return toAjax(liveWatchUserService.blockUser(userId));
+    }
+
 }

+ 10 - 0
fs-company/src/main/java/com/fs/company/controller/common/CommonController.java

@@ -10,6 +10,7 @@ import com.fs.common.utils.file.FileUploadUtils;
 import com.fs.common.utils.file.FileUtils;
 import com.fs.company.utils.AudioUtils;
 import com.fs.company.vo.WangUploadVO;
+import com.fs.course.service.ITencentCloudCosService;
 import com.fs.framework.config.ServerConfig;
 import com.fs.his.domain.FsExportTask;
 import com.fs.his.service.IFsExportTaskService;
@@ -49,6 +50,9 @@ public class CommonController
     @Autowired
     private ServerConfig serverConfig;
 
+    @Autowired
+    private ITencentCloudCosService tencentCloudCosService;
+
     @Autowired
     private QwApiService qwApiService;
 
@@ -276,6 +280,12 @@ public class CommonController
         }
     }
 
+    @GetMapping("/common/getTmpSecretKey")
+    public R getTmpSecretKey()
+    {
+        return tencentCloudCosService.getKeyAndCredentials();
+    }
+
 
 
 }

+ 105 - 0
fs-company/src/main/java/com/fs/company/controller/live/LiveAutoTaskController.java

@@ -0,0 +1,105 @@
+package com.fs.company.controller.live;
+
+import java.util.List;
+
+import com.fs.common.core.domain.R;
+import com.fs.live.domain.LiveAutoTask;
+import com.fs.live.service.ILiveAutoTaskService;
+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 com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+
+/**
+ * 直播间自动化任务配置Controller
+ *
+ * @author fs
+ * @date 2025-08-29
+ */
+@RestController
+@RequestMapping("/live/task")
+public class LiveAutoTaskController extends BaseController
+{
+    @Autowired
+    private ILiveAutoTaskService liveAutoTaskService;
+
+    /**
+     * 查询直播间自动化任务配置列表
+     */
+    @PreAuthorize("@ss.hasPermi('shop:task:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(LiveAutoTask liveAutoTask)
+    {
+        startPage();
+        List<LiveAutoTask> list = liveAutoTaskService.selectLiveAutoTaskList(liveAutoTask);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出直播间自动化任务配置列表
+     */
+    @PreAuthorize("@ss.hasPermi('shop:task:export')")
+    @Log(title = "直播间自动化任务配置", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(LiveAutoTask liveAutoTask)
+    {
+        List<LiveAutoTask> list = liveAutoTaskService.selectLiveAutoTaskList(liveAutoTask);
+        ExcelUtil<LiveAutoTask> util = new ExcelUtil<LiveAutoTask>(LiveAutoTask.class);
+        return util.exportExcel(list, "直播间自动化任务配置数据");
+    }
+
+    /**
+     * 获取直播间自动化任务配置详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('shop:task:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(liveAutoTaskService.selectLiveAutoTaskById(id));
+    }
+
+    /**
+     * 新增直播间自动化任务配置
+     */
+    @PreAuthorize("@ss.hasPermi('shop:task:add')")
+    @Log(title = "直播间自动化任务配置", businessType = BusinessType.INSERT)
+    @PostMapping
+    public R add(@RequestBody LiveAutoTask liveAutoTask)
+    {
+        return liveAutoTaskService.insertLiveAutoTask(liveAutoTask);
+    }
+
+    /**
+     * 修改直播间自动化任务配置
+     */
+    @PreAuthorize("@ss.hasPermi('shop:task:edit')")
+    @Log(title = "直播间自动化任务配置", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody LiveAutoTask liveAutoTask)
+    {
+        return toAjax(liveAutoTaskService.updateLiveAutoTask(liveAutoTask));
+    }
+
+    /**
+     * 删除直播间自动化任务配置
+     */
+    @PreAuthorize("@ss.hasPermi('shop:task:remove')")
+    @Log(title = "直播间自动化任务配置", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(liveAutoTaskService.deleteLiveAutoTaskByIds(ids));
+    }
+}

+ 12 - 0
fs-company/src/main/java/com/fs/company/controller/live/LiveController.java

@@ -123,6 +123,18 @@ public class LiveController extends BaseController
         return liveService.finishLive(live);
     }
 
+    /**
+     * 复制直播
+     */
+    @PreAuthorize("@ss.hasPermi('live:live:edit')")
+    @GetMapping("/copyLive")
+    public R copyLive(Live live) {
+        CompanyUser user = SecurityUtils.getLoginUser().getUser();
+        live.setCompanyUserId(user.getUserId());
+        live.setCompanyId(user.getCompanyId());
+        return liveService.copyLive(live);
+    }
+
     /**
      * 开启直播
      */

+ 11 - 2
fs-company/src/main/java/com/fs/company/controller/live/LiveGoodsController.java

@@ -97,9 +97,9 @@ public class LiveGoodsController extends BaseController
     @PreAuthorize("@ss.hasPermi('live:liveGoods:edit')")
     @Log(title = "直播商品", businessType = BusinessType.UPDATE)
     @PutMapping
-    public AjaxResult edit(@RequestBody LiveGoods liveGoods)
+    public R edit(@RequestBody LiveGoods liveGoods)
     {
-        return toAjax(liveGoodsService.updateLiveGoods(liveGoods));
+        return liveGoodsService.updateLiveGoods(liveGoods);
     }
 
     /**
@@ -143,6 +143,15 @@ public class LiveGoodsController extends BaseController
         return liveGoodsService.handleDeleteSelected(listVo);
     }
 
+    /**
+     * 更新展示状态
+     */
+    @PostMapping("/handleIsShowChange")
+    public R handleIsShowChange(@RequestBody LiveGoodsListVo listVo) {
+        setListCompanyId(listVo);
+        return liveGoodsService.handleIsShowChange(listVo);
+    }
+
     /**
      * 设置企业ID和企业用户ID
      */

+ 104 - 0
fs-company/src/main/java/com/fs/company/controller/live/LiveRewardRecordController.java

@@ -0,0 +1,104 @@
+package com.fs.company.controller.live;
+
+import java.util.List;
+
+import com.fs.live.domain.LiveRewardRecord;
+import com.fs.live.service.ILiveRewardRecordService;
+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 com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+
+/**
+ * 用户直播间奖励记录Controller
+ *
+ * @author fs
+ * @date 2025-08-27
+ */
+@RestController
+@RequestMapping("/live/record")
+public class LiveRewardRecordController extends BaseController
+{
+    @Autowired
+    private ILiveRewardRecordService liveRewardRecordService;
+
+    /**
+     * 查询用户直播间奖励记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('live:record:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(LiveRewardRecord liveRewardRecord)
+    {
+        startPage();
+        List<LiveRewardRecord> list = liveRewardRecordService.selectLiveRewardRecordList(liveRewardRecord);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出用户直播间奖励记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('live:record:export')")
+    @Log(title = "用户直播间奖励记录", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(LiveRewardRecord liveRewardRecord)
+    {
+        List<LiveRewardRecord> list = liveRewardRecordService.selectLiveRewardRecordList(liveRewardRecord);
+        ExcelUtil<LiveRewardRecord> util = new ExcelUtil<LiveRewardRecord>(LiveRewardRecord.class);
+        return util.exportExcel(list, "用户直播间奖励记录数据");
+    }
+
+    /**
+     * 获取用户直播间奖励记录详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('live:record:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(liveRewardRecordService.selectLiveRewardRecordById(id));
+    }
+
+    /**
+     * 新增用户直播间奖励记录
+     */
+    @PreAuthorize("@ss.hasPermi('live:record:add')")
+    @Log(title = "用户直播间奖励记录", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody LiveRewardRecord liveRewardRecord)
+    {
+        return toAjax(liveRewardRecordService.insertLiveRewardRecord(liveRewardRecord));
+    }
+
+    /**
+     * 修改用户直播间奖励记录
+     */
+    @PreAuthorize("@ss.hasPermi('live:record:edit')")
+    @Log(title = "用户直播间奖励记录", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody LiveRewardRecord liveRewardRecord)
+    {
+        return toAjax(liveRewardRecordService.updateLiveRewardRecord(liveRewardRecord));
+    }
+
+    /**
+     * 删除用户直播间奖励记录
+     */
+    @PreAuthorize("@ss.hasPermi('live:record:remove')")
+    @Log(title = "用户直播间奖励记录", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(liveRewardRecordService.deleteLiveRewardRecordByIds(ids));
+    }
+}

+ 10 - 0
fs-company/src/main/java/com/fs/company/controller/live/LiveWatchUserController.java

@@ -119,4 +119,14 @@ public class LiveWatchUserController extends BaseController
         return toAjax(liveWatchUserService.changeUserState(liveId, userId));
     }
 
+    /**
+     * 封禁用户账号
+     */
+    @PreAuthorize("@ss.hasPermi('live:liveWatchUser:edit')")
+    @Log(title = "直播间观看用户", businessType = BusinessType.UPDATE)
+    @GetMapping("/blockUser/{userId}")
+    public AjaxResult blockUser(@PathVariable Long userId) {
+        return toAjax(liveWatchUserService.blockUser(userId));
+    }
+
 }

+ 1 - 1
fs-live-app/src/main/java/com/fs/app/config/ProductionWordFilter.java

@@ -96,7 +96,7 @@ public class ProductionWordFilter implements InitializingBean {
             MatchResult match = findNextMatch(text, i);
             if (match != null) {
                 foundWords.add(match.getWord());
-                result.append(StringUtils.repeat('*', match.getLength()));
+                result.append(StringUtils.repeat("", match.getLength()));
                 replacedCount++;
                 i = match.getEndIndex();
             } else {

+ 27 - 6
fs-live-app/src/main/java/com/fs/app/controller/LiveController.java

@@ -14,12 +14,12 @@ import com.fs.common.annotation.RepeatSubmit;
 import com.fs.common.core.domain.BaseEntity;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.bean.BeanUtils;
-import com.fs.live.domain.Live;
-import com.fs.live.domain.LiveMsg;
-import com.fs.live.service.ILiveGoodsService;
-import com.fs.live.service.ILiveMsgService;
-import com.fs.live.service.ILiveService;
+import com.fs.his.service.IFsUserService;
+import com.fs.live.domain.*;
+import com.fs.live.service.*;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiResponse;
@@ -28,6 +28,7 @@ import org.apache.http.HttpRequest;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.Base64Utils;
 import org.springframework.web.bind.annotation.*;
 
@@ -39,6 +40,7 @@ import java.nio.charset.StandardCharsets;
 import java.time.LocalDateTime;
 import java.time.temporal.ChronoUnit;
 import java.util.*;
+import java.util.stream.Collectors;
 
 
 @Api("直播信息接口")
@@ -57,6 +59,8 @@ public class LiveController extends AppBaseController {
 	@Autowired
 	private ILiveGoodsService liveGoodsService;
 
+
+
 	@ApiOperation("直播封面信息")
 	@GetMapping("/liveInfo")
 	@ApiResponse(code = 200, message = "", response = LiveInfoVo.class)
@@ -99,6 +103,7 @@ public class LiveController extends AppBaseController {
 	@GetMapping("/liveList")
 	@ApiResponse(code = 200, message = "", response = LiveInfoVo.class)
 	public TableDataInfo liveList() {
+		startPage();
 		return getDataTable(liveService.liveList());
 	}
 
@@ -160,7 +165,7 @@ public class LiveController extends AppBaseController {
 		webSocketServer.broadcastMessage(Long.valueOf(params.get("stream_id")), JSONObject.toJSONString(R.ok().put("data",sendMsgVo)));
 		Live live = new Live();
 		live.setLiveId(Long.valueOf(params.get("stream_id")));
-		live.setStatus(1);
+		live.setStatus(3);
 		live.setFinishTime(LocalDateTime.now());
 		live.setLiveType(2);
 		liveService.updateLive(live);
@@ -173,4 +178,20 @@ public class LiveController extends AppBaseController {
 //				user_ip=113.248.98.28, width=1920}
 
 	}
+
+	@GetMapping("/currentActivities")
+	@Transactional
+	@Login
+	public R currentActivities(Long liveId) {
+		String userId = getUserId();
+		return liveService.currentActivities(liveId,userId);
+	}
+
+	@GetMapping("/test")
+	@Transactional
+	public void test() {
+
+	}
+
+
 }

+ 11 - 2
fs-live-app/src/main/java/com/fs/app/controller/LiveGoodsController.java

@@ -84,9 +84,9 @@ public class LiveGoodsController extends BaseController
     @PreAuthorize("@ss.hasPermi('live:liveGoods:edit')")
     @Log(title = "直播商品", businessType = BusinessType.UPDATE)
     @PutMapping
-    public AjaxResult edit(@RequestBody LiveGoods liveGoods)
+    public R edit(@RequestBody LiveGoods liveGoods)
     {
-        return toAjax(liveGoodsService.updateLiveGoods(liveGoods));
+        return liveGoodsService.updateLiveGoods(liveGoods);
     }
 
     /**
@@ -118,5 +118,14 @@ public class LiveGoodsController extends BaseController
         return R.ok().put("data",fsStoreProductService.selectFsStoreProductById(productId));
     }
 
+    /**
+     *当前正在展示的商品
+     */
+    @GetMapping("/showGoods/{liveId}")
+    public R showGoods(@PathVariable Long liveId)
+    {
+        return liveGoodsService.showGoods(liveId);
+    }
+
 
 }

+ 22 - 1
fs-live-app/src/main/java/com/fs/app/controller/LiveLotteryController.java

@@ -1,11 +1,22 @@
 package com.fs.app.controller;
 
+import com.fs.app.annotation.Login;
+import com.fs.common.core.domain.R;
+import com.fs.live.param.LotteryPO;
+import com.fs.live.param.RedPO;
+import com.fs.live.service.ILiveLotteryConfService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
 @RestController
 @RequestMapping("/app/live/liveLottery")
-public class LiveLotteryController {
+public class LiveLotteryController extends AppBaseController{
+
+    @Autowired
+    private ILiveLotteryConfService liveLotteryConfService;
 
     /**
      * 参与抽奖
@@ -15,4 +26,14 @@ public class LiveLotteryController {
         return null;
     }
 
+    /**
+     * 领取红包
+     * */
+    @Login
+    @PostMapping("/claim")
+    public R claim(@RequestBody LotteryPO lottery) {
+        lottery.setUserId(Long.parseLong(getUserId()));
+        return liveLotteryConfService.claimLotteryPacket(lottery);
+    }
+
 }

+ 0 - 1
fs-live-app/src/main/java/com/fs/app/controller/LiveWatchUserController.java

@@ -37,7 +37,6 @@ public class LiveWatchUserController extends BaseController
     @GetMapping("/watchUserList")
     public TableDataInfo watchUserList(LiveWatchUser param) {
         param.setOnline(0);
-        startPage();
         List<LiveWatchUserVO> onLineUserList = liveWatchUserService.selectOnlineUserList(param);
         return getDataTable(onLineUserList);
     }

+ 141 - 12
fs-live-app/src/main/java/com/fs/app/task/Task.java

@@ -1,18 +1,28 @@
 package com.fs.app.task;
 
+import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.fs.app.websocket.service.WebSocketServer;
 import com.fs.common.core.redis.RedisCache;
+import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.spring.SpringUtils;
-import com.fs.live.domain.Live;
-import com.fs.live.domain.LiveData;
-import com.fs.live.service.ILiveDataService;
-import com.fs.live.service.ILiveService;
+import com.fs.his.service.IFsUserService;
+import com.fs.live.domain.*;
+import com.fs.live.service.*;
 import lombok.AllArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
 
+import java.math.BigDecimal;
+import java.time.Instant;
 import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 @Component
 @AllArgsConstructor
@@ -24,25 +34,144 @@ public class Task {
 
     private final RedisCache redisCache;
 
-//    @Scheduled(cron = "0 0/1 * * * ?")
+    @Autowired
+    private ILiveWatchUserService liveWatchUserService;
+    @Autowired
+    private IFsUserService fsUserService;
+    @Autowired
+    private ILiveRewardRecordService liveRewardRecordService;
+    @Autowired
+    private WebSocketServer webSocketServer;
+    @Autowired
+    private ILiveAutoTaskService liveAutoTaskService;
+
+    @Scheduled(cron = "0 0/1 * * * ?")
     //public void selectSopUserLogsListByTime() {
     public void updateLiveStatusByTime() {
         LocalDateTime now = LocalDateTime.now();
-        List<Live> list = liveService.list(new QueryWrapper<Live>().ne("status", 3));
+        List<Live> list = liveService.list(new QueryWrapper<Live>().ne("status", 3).eq("live_type", 2).eq("is_audit", 1));
+        List<Long> liveIdLists = list.stream().map(Live::getLiveId).collect(Collectors.toList());
+        List<LiveAutoTask> liveAutoTasks = liveAutoTaskService.selectLiveAutoTaskByLiveIds(liveIdLists);
         if (list.isEmpty())
             return;
+        List<Live> activeLiveList = new ArrayList<>();
         list.forEach(live -> {
-            if (now.isAfter(live.getStartTime()) && now.isBefore(live.getFinishTime())) {
-                live.setStatus(2);
-            } else if (now.isBefore(live.getStartTime())) {
-                live.setStatus(1);
-            } else if (now.isAfter(live.getFinishTime())) {
-                live.setStatus(3);
+            if (live.getFinishTime() == null) {
+                if (now.isAfter(live.getStartTime())){
+                    live.setStatus(2);
+                    activeLiveList.add( live);
+                } else if (now.isBefore(live.getStartTime())) {
+                    live.setStatus(1);
+                }
+            } else {
+                if (now.isAfter(live.getStartTime()) && now.isBefore(live.getFinishTime())) {
+                    live.setStatus(2);
+                    activeLiveList.add( live);
+                } else if (now.isBefore(live.getStartTime())) {
+                    live.setStatus(1);
+                } else if (now.isAfter(live.getFinishTime())) {
+                    live.setStatus(3);
+                }
             }
         });
+        String key = "live:auto_task:";
+        if(!activeLiveList.isEmpty()){
+            activeLiveList.forEach(live -> {
+                List<LiveAutoTask> collect = liveAutoTasks.stream().filter(liveAutoTask -> liveAutoTask.getLiveId().equals(live.getLiveId())).collect(Collectors.toList());
+                if (!collect.isEmpty()) {
+                    collect.forEach(liveAutoTask -> {
+                        liveAutoTask.setCreateTime(null);
+                        liveAutoTask.setUpdateTime(null);
+                        redisCache.redisTemplate.opsForZSet().add(key + live.getLiveId(), JSON.toJSONString(liveAutoTask),liveAutoTask.getAbsValue().getTime());
+                    });
+                }
+            });
+        }
+
         if(!list.isEmpty()){
             liveService.updateBatchById(list);
         }
     }
 
+    @Scheduled(cron = "0/1 * * * * ?")
+    public void liveAutoTask() {
+        long currentTime = Instant.now().toEpochMilli(); // 当前时间戳(毫秒)
+
+        Set<String> allLiveKeys = redisCache.redisTemplate.keys("live:auto_task:*");
+        if (allLiveKeys == null || allLiveKeys.isEmpty()) {
+            return; // 没有数据,直接返回
+        }
+        // 2. 遍历每个直播间的ZSet键
+        for (String liveKey : allLiveKeys) {
+            // 3. 获取当前直播间ZSet中所有元素(按score排序)
+            // range方法:0表示第一个元素,-1表示最后一个元素,即获取全部
+            Set<String> range = redisCache.redisTemplate.opsForZSet().rangeByScore(liveKey, 0, currentTime);
+            if (range == null || range.isEmpty()) {
+                continue; // 没有数据,直接返回
+            }
+            redisCache.redisTemplate.opsForZSet()
+                    .removeRangeByScore(liveKey, 0, currentTime);
+            processAutoTask(range);
+        }
+    }
+
+    private void processAutoTask(Set<String> range) {
+        for (String liveAutoTask : range) {
+            LiveAutoTask task = JSON.parseObject(liveAutoTask, LiveAutoTask.class);
+            webSocketServer.handleAutoTask(task);
+            task.setFinishStatus(1L);
+            liveAutoTaskService.updateLiveAutoTask(task);
+        }
+    }
+
+    @Scheduled(cron = "0 0/1 * * * ?")
+    @Transactional
+    public void autoUpdateWatchReward() {
+
+        // 1.查询所有直播中的直播间
+        List<Live> lives = liveService.liveList();
+
+
+        // 2.检查是否开启观看奖励
+        List<Live> openRewardLives = lives.stream().filter(live -> StringUtils.isNotEmpty(live.getConfigJson())).collect(Collectors.toList());
+        Date now = new Date();
+
+        for (Live openRewardLive : openRewardLives) {
+            String configJson = openRewardLive.getConfigJson();
+            LiveWatchConfig config = JSON.parseObject(configJson, LiveWatchConfig.class);
+            if (config.getEnabled()) {
+                // 3.检查当前直播间的在线用户(可以传入一个时间,然后查出来当天没领取奖励的用户)
+                List<LiveWatchUser> onlineUser = liveWatchUserService.checkOnlineNoRewardUser(openRewardLive.getLiveId(), now)
+                        .stream().filter(user -> now.getTime() - user.getUpdateTime().getTime() > config.getWatchDuration() * 60 * 1000)
+                        .collect(Collectors.toList());
+                if(onlineUser.isEmpty()) continue;
+
+                List<Long> userIds = onlineUser.stream().map(LiveWatchUser::getUserId).collect(Collectors.toList());
+                // 4.保存用户领取记录
+                saveUserRewardRecord(openRewardLive.getLiveId(), userIds,config.getScoreAmount());
+                // 5.更新用户积分(芳华币
+                fsUserService.increaseIntegral(userIds,config.getScoreAmount());
+                // 6.发送websocket事件消息 通知用户自动领取成功
+                userIds.forEach(userId -> webSocketServer.sendIntegralMessage(openRewardLive.getLiveId(),userId,config.getScoreAmount()));
+
+            }
+        }
+    }
+    private void saveUserRewardRecord(Long liveId, List<Long> userIds,Long scoreAmount) {
+        for (Long userId : userIds) {
+            LiveRewardRecord record = new LiveRewardRecord();
+            record.setLiveId(liveId);
+            record.setUserId(userId);
+            record.setIncomeType(1L);
+            record.setSourceType(1L);
+            record.setSourceId(0L);
+            record.setRewardType(2L);
+            record.setNum(BigDecimal.valueOf(scoreAmount));
+            record.setRewardType(2L);
+            record.setCreateTime(new Date());
+            record.setCreateBy(String.valueOf(userId));
+            liveRewardRecordService.insertLiveRewardRecord(record);
+        }
+    }
+
 }

+ 93 - 7
fs-live-app/src/main/java/com/fs/app/websocket/service/WebSocketServer.java

@@ -9,16 +9,16 @@ import com.fs.app.websocket.bean.SendMsgVo;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.exception.base.BaseException;
+import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.spring.SpringUtils;
 import com.fs.his.domain.FsUser;
 import com.fs.his.service.IFsUserService;
-import com.fs.live.domain.LiveData;
-import com.fs.live.domain.LiveMsg;
-import com.fs.live.domain.LiveRedConf;
-import com.fs.live.domain.LiveWatchUser;
+import com.fs.live.domain.*;
 import com.fs.live.service.*;
+import com.fs.live.vo.LiveGoodsVo;
 import com.fs.live.vo.LiveWatchUserVO;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.time.DateUtils;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.stereotype.Component;
 
@@ -49,8 +49,10 @@ public class WebSocketServer {
     private final ILiveDataService liveDataService = SpringUtils.getBean(ILiveDataService.class);
     private final ProductionWordFilter productionWordFilter = SpringUtils.getBean(ProductionWordFilter.class);
     private final ILiveRedConfService liveRedConfService =  SpringUtils.getBean(ILiveRedConfService.class);
+    private final ILiveLotteryConfService liveLotteryConfService =  SpringUtils.getBean(ILiveLotteryConfService.class);
+    private final ILiveGoodsService liveGoodsService =  SpringUtils.getBean(ILiveGoodsService.class);
     // 直播间在线用户缓存
-    //private static final ConcurrentHashMap<Long, Integer> liveOnlineUsers = new ConcurrentHashMap<>();
+//    private static final ConcurrentHashMap<Long, Integer> liveOnlineUsers = new ConcurrentHashMap<>();
 
 
     //建立连接成功调用
@@ -188,6 +190,7 @@ public class WebSocketServer {
                     break;
                 case "sendMsg":
                     msg.setMsg(productionWordFilter.filter(msg.getMsg()).getFilteredText());
+                    if(StringUtils.isEmpty(msg.getMsg())) return;
                     LiveMsg liveMsg = new LiveMsg();
                     liveMsg.setLiveId(msg.getLiveId());
                     liveMsg.setUserId(msg.getUserId());
@@ -214,11 +217,22 @@ public class WebSocketServer {
                     break;
                 case "sendGift":
                     break;
+                case "blockUser":
+                    sendBlockMessage(liveId, msg.getUserId());
+                    break;
+                case "goods":
+                    sendGoodsMessage(msg);
+                    break;
                 case "red":
                     processRed(liveId, msg);
                     break;
                 case "lottery":
-                    System.out.println("抽奖");
+                    processLottery(liveId, msg);
+                    break;
+                case "delAutoTask":
+                    if (userType == 1) {
+                        delAutoTask(liveId, DateUtils.parseDate(msg.getData(),"yyyy-MM-dd'T'HH:mm:ss.SSSZ").getTime());
+                    }
                     break;
             }
         } catch (Exception e) {
@@ -226,6 +240,19 @@ public class WebSocketServer {
         }
     }
 
+
+
+    private void sendGoodsMessage(SendMsgVo msg) {
+        JSONObject jsonObject = JSON.parseObject(msg.getData());
+        Long goodsId = jsonObject.getLong("goodsId");
+        Long liveId = jsonObject.getLong("liveId");
+        LiveGoodsVo liveGoods = liveGoodsService.selectLiveGoodsVoByGoodsId(goodsId);
+        if(liveGoods == null) return;
+        msg.setLiveId(liveId);
+        msg.setData(JSONObject.toJSONString(liveGoods));
+        broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)));
+    }
+
     /**
      * 处理红包变动消息
      */
@@ -239,6 +266,19 @@ public class WebSocketServer {
         }
     }
 
+    /**
+     * 处理抽奖变动消息
+     */
+    private void processLottery(Long liveId, SendMsgVo msg) {
+        log.debug("lotteryData: {}", msg);
+        JSONObject jsonObject = JSON.parseObject(msg.getData());
+        LiveLotteryConf liveLotteryConf = liveLotteryConfService.selectLiveLotteryConfByLotteryId(jsonObject.getLong("lotteryId"));
+        if (Objects.nonNull(liveLotteryConf)) {
+            msg.setData(JSONObject.toJSONString(liveLotteryConf));
+            broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)));
+        }
+    }
+
     //错误时调用
     @OnError
     public void onError(Session session, Throwable throwable) {
@@ -268,6 +308,34 @@ public class WebSocketServer {
         session.getAsyncRemote().sendText(message);
     }
 
+    public void sendIntegralMessage(Long liveId, Long userId,Long scoreAmount) {
+        SendMsgVo sendMsgVo = new SendMsgVo();
+        sendMsgVo.setLiveId(liveId);
+        sendMsgVo.setUserId(userId);
+        sendMsgVo.setUserType(0L);
+        sendMsgVo.setCmd("Integral");
+        sendMsgVo.setMsg("恭喜你成功获得观看奖励:" + scoreAmount + "芳华币");
+        sendMsgVo.setData(String.valueOf(scoreAmount));
+        ConcurrentHashMap<Long, Session> room = getRoom(liveId);
+        Session session = room.get(userId);
+        if(Objects.isNull( session)) return;
+        session.getAsyncRemote().sendText(JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
+    }
+
+    private void sendBlockMessage(Long liveId, Long userId) {
+        SendMsgVo sendMsgVo = new SendMsgVo();
+        sendMsgVo.setLiveId(liveId);
+        sendMsgVo.setUserId(userId);
+        sendMsgVo.setUserType(0L);
+        sendMsgVo.setCmd("blockUser");
+        sendMsgVo.setMsg("账号已被停用");
+        sendMsgVo.setData(null);
+        ConcurrentHashMap<Long, Session> room = getRoom(liveId);
+        Session session = room.get(userId);
+        if(Objects.isNull( session)) return;
+        session.getAsyncRemote().sendText(JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
+    }
+
     /**
      * 广播消息
      * @param liveId   直播间ID
@@ -334,5 +402,23 @@ public class WebSocketServer {
     }
 
 
-
+    public void handleAutoTask(LiveAutoTask task) {
+        if (task.getTaskType() == 1L) {
+            SendMsgVo msg = new SendMsgVo();
+            msg.setLiveId(task.getLiveId());
+            msg.setData(task.getContent());
+            msg.setCmd("goods");
+            try {
+                LiveGoodsVo liveGoodsVo = JSON.parseObject(task.getContent(), LiveGoodsVo.class);
+                liveGoodsService.updateLiveIsShow(liveGoodsVo.getGoodsId(), task.getLiveId());
+            } catch (Exception e) {
+                log.error("定时任务执行异常:{}", e.getMessage());
+            }
+            broadcastMessage(task.getLiveId(), JSONObject.toJSONString(R.ok().put("data", msg)));
+        }
+    }
+    private void delAutoTask(long liveId, Long data) {
+        String key = "live:auto_task:";
+        redisCache.redisTemplate.opsForZSet().removeRangeByScore(key + liveId, data, data);
+    }
 }

+ 8 - 0
fs-service/src/main/java/com/fs/his/mapper/FsUserMapper.java

@@ -340,4 +340,12 @@ public interface FsUserMapper
 
     @Select("select * from fs_user where course_ma_open_id=#{openId}")
     FsUser selectFsUserByCourseMaOpenId(String openId);
+
+    @Update({"<script> " +
+            "update fs_user set integral=integral+#{scoreAmount} where user_id in " +
+            "<foreach item='item' index='index' collection='userIds' open='(' separator=',' close=')'>" +
+            "#{item}" +
+            "</foreach>" +
+            "</script>"})
+    void increaseIntegral(@Param("userIds") List<Long> userIds,@Param("scoreAmount") Long scoreAmount);
 }

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

@@ -17,6 +17,7 @@ import com.fs.his.vo.FsUserExportListVO;
 import com.fs.his.vo.FsUserFollowDoctorVO;
 import com.fs.his.vo.FsUserVO;
 import com.fs.his.vo.UserVo;
+import com.fs.live.domain.LiveWatchUser;
 import com.fs.qw.dto.FsUserTransferParamDTO;
 import com.fs.qw.param.QwFsUserParam;
 import com.fs.qw.vo.QwFsUserVO;
@@ -173,4 +174,6 @@ public interface IFsUserService
     ResponseResult<Boolean> becomeMember(@Valid FsUserCourseBeMemberParam param);
 
     FsUserPageListVO selectFsUserPageListVOByUserId(Long userId);
+
+    void increaseIntegral(List<Long> userIds, Long scoreAmount);
 }

+ 6 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java

@@ -43,6 +43,7 @@ import com.fs.his.vo.FsUserExportListVO;
 import com.fs.his.vo.FsUserFollowDoctorVO;
 import com.fs.his.vo.FsUserVO;
 import com.fs.his.vo.UserVo;
+import com.fs.live.domain.LiveWatchUser;
 import com.fs.qw.cache.IQwExternalContactCacheService;
 import com.fs.qw.dto.FsUserTransferParamDTO;
 import com.fs.qw.param.QwFsUserParam;
@@ -1023,6 +1024,11 @@ public class FsUserServiceImpl implements IFsUserService
         return item;
     }
 
+    @Override
+    public void increaseIntegral(List<Long> userIds, Long scoreAmount) {
+        fsUserMapper.increaseIntegral(userIds, scoreAmount);
+    }
+
 
     private FsUserStatisticsVO getUserStatistics(UserStatisticsCommonParam param) {
         FsUserStatisticsVO fsUserStatisticsVO = new FsUserStatisticsVO();

+ 60 - 0
fs-service/src/main/java/com/fs/live/domain/LiveAutoTask.java

@@ -0,0 +1,60 @@
+package com.fs.live.domain;
+
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 直播间自动化任务配置对象 live_auto_task
+ *
+ * @author fs
+ * @date 2025-08-29
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class LiveAutoTask extends BaseEntity{
+
+    /** 任务ID */
+    private Long id;
+
+    /** 直播间ID */
+    @Excel(name = "直播间ID")
+    private Long liveId;
+
+    /** 任务名称 */
+    @Excel(name = "任务名称")
+    private String taskName;
+
+    /** 任务类型:1-定时推送卡片商品 2-定时发送红包 3-定时开启互动 */
+    @Excel(name = "任务类型:1-定时推送卡片商品 2-定时发送红包 3-定时开启互动")
+    private Long taskType;
+
+    /** 触发类型:1-绝对时间 2-相对直播开始时间 */
+    @Excel(name = "触发类型:1-绝对时间 2-相对直播开始时间")
+    private Long triggerType;
+
+    /** 触发值:绝对时间用yyyy-MM-dd HH:mm:ss,相对时间用分钟数 */
+    @Excel(name = "触发值:绝对时间用yyyy-MM-dd HH:mm:ss,相对时间用分钟数")
+    private Date triggerValue;
+
+    /** 触发值:绝对时间用yyyy-MM-dd HH:mm:ss,相对时间用分钟数 */
+    @Excel(name = "触发值:绝对时间用yyyy-MM-dd HH:mm:ss,相对时间用分钟数")
+    private Date absValue;
+
+    /** 任务内容:如消息文本、红包配置等JSON格式 */
+    @Excel(name = "任务内容:如消息文本、红包配置等JSON格式")
+    private String content;
+
+    /** 状态:0-禁用 1-启用 */
+    @Excel(name = "状态:0-禁用 1-启用")
+    private Long status;
+
+    private Long finishStatus;
+
+
+
+}

+ 4 - 0
fs-service/src/main/java/com/fs/live/domain/LiveGoods.java

@@ -44,6 +44,10 @@ public class LiveGoods extends BaseEntity{
     @Excel(name = "状态 1上架 0下架")
     private Integer status;
 
+    /** 卡片推荐状态 1上架 0下架 */
+    @Excel(name = "状态 1不展示 0展示")
+    private Boolean isShow;
+
     /** 库存表 */
     @Excel(name = "库存表")
     private Long stock;

+ 4 - 0
fs-service/src/main/java/com/fs/live/domain/LiveLotteryRegistration.java

@@ -26,6 +26,10 @@ public class LiveLotteryRegistration extends BaseEntity{
     @Excel(name = "直播间ID")
     private Long liveId;
 
+    /** 奖品id */
+    @Excel(name = "奖品id")
+    private Long lotteryId;
+
     /** 用户ID */
     @Excel(name = "用户ID")
     private Long userId;

+ 60 - 0
fs-service/src/main/java/com/fs/live/domain/LiveRewardRecord.java

@@ -0,0 +1,60 @@
+package com.fs.live.domain;
+
+import java.math.BigDecimal;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 用户直播间奖励记录对象 live_reward_record
+ *
+ * @author fs
+ * @date 2025-08-27
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class LiveRewardRecord extends BaseEntity{
+
+    /** 主键ID */
+    private Long id;
+
+    /** 直播ID */
+    @Excel(name = "直播ID")
+    private Long liveId;
+
+    /** 用户ID */
+    @Excel(name = "用户ID")
+    private Long userId;
+
+    /** 收支类型 1收入 2支出 */
+    @Excel(name = "收支类型 1收入 2支出")
+    private Long incomeType;
+
+    /** 来源类型 1观看奖励 2答题红包 3观看积分 */
+    @Excel(name = "来源类型 1观看奖励 2答题红包 3观看积分")
+    private Long sourceType;
+
+    /** 来源ID */
+    @Excel(name = "来源ID")
+    private Long sourceId;
+
+    /** 奖励类型 1现金 2积分 */
+    @Excel(name = "奖励类型 1现金 2积分")
+    private Long rewardType;
+
+    /** 奖励数量 */
+    @Excel(name = "奖励数量")
+    private BigDecimal num;
+
+    /** 奖励前数量 */
+    @Excel(name = "奖励前数量")
+    private BigDecimal beforeNum;
+
+    /** 奖励后数量 */
+    @Excel(name = "奖励后数量")
+    private BigDecimal afterNum;
+
+
+}

+ 77 - 0
fs-service/src/main/java/com/fs/live/mapper/LiveAutoTaskMapper.java

@@ -0,0 +1,77 @@
+package com.fs.live.mapper;
+
+import java.util.Date;
+import java.util.List;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.live.domain.LiveAutoTask;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.SelectProvider;
+import java.util.Map;
+
+/**
+ * 直播间自动化任务配置Mapper接口
+ *
+ * @author fs
+ * @date 2025-08-29
+ */
+public interface LiveAutoTaskMapper extends BaseMapper<LiveAutoTask>{
+    /**
+     * 查询直播间自动化任务配置
+     *
+     * @param id 直播间自动化任务配置主键
+     * @return 直播间自动化任务配置
+     */
+    LiveAutoTask selectLiveAutoTaskById(Long id);
+
+    /**
+     * 查询直播间自动化任务配置列表
+     *
+     * @param liveAutoTask 直播间自动化任务配置
+     * @return 直播间自动化任务配置集合
+     */
+    List<LiveAutoTask> selectLiveAutoTaskList(LiveAutoTask liveAutoTask);
+
+    /**
+     * 新增直播间自动化任务配置
+     *
+     * @param liveAutoTask 直播间自动化任务配置
+     * @return 结果
+     */
+    int insertLiveAutoTask(LiveAutoTask liveAutoTask);
+
+    /**
+     * 修改直播间自动化任务配置
+     *
+     * @param liveAutoTask 直播间自动化任务配置
+     * @return 结果
+     */
+    int updateLiveAutoTask(LiveAutoTask liveAutoTask);
+
+    /**
+     * 删除直播间自动化任务配置
+     *
+     * @param id 直播间自动化任务配置主键
+     * @return 结果
+     */
+    int deleteLiveAutoTaskById(Long id);
+
+    /**
+     * 批量删除直播间自动化任务配置
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteLiveAutoTaskByIds(Long[] ids);
+
+    @Select("select * from live_auto_task where live_id=#{liveId} and trigger_value=#{triggerValue}")
+    LiveAutoTask selectLiveAutoTaskByTriggerValue(@Param("liveId") Long liveId, @Param("triggerValue")  Date triggerValue);
+
+    @Select("select * from live_auto_task where live_id= #{liveId}")
+    List<LiveAutoTask> selectLiveAutoTaskByLiveId(@Param("liveId") Long liveId);
+
+    @Select("select * from live_auto_task where live_id= #{liveId} and status=1 and finish_status = 0 and abs_value>#{now} order by abs_value")
+    List<LiveAutoTask> selectNoActivedByLiveId(@Param("liveId") Long existLiveId,@Param("now") Date now);
+
+    List<LiveAutoTask> selectLiveAutoTaskByLiveIds(@Param("liveIdLists") List<Long> liveIdLists, @Param("now") Date now);
+}

+ 24 - 2
fs-service/src/main/java/com/fs/live/mapper/LiveGoodsMapper.java

@@ -12,6 +12,7 @@ import com.fs.live.vo.LiveGoodsListVo;
 import com.fs.live.vo.LiveGoodsVo;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.Update;
 
 /**
  * 直播商品Mapper接口
@@ -136,7 +137,28 @@ public interface LiveGoodsMapper extends BaseMapper<LiveGoods>{
     @Select("select distinct store_id from live_goods where live_id = #{liveId}")
     List<Long> selectStoreIdsByLiveId(@Param("liveId") Long liveId);
 
-    R handleShelfOrUnAdmin(@Param("listVo") LiveGoodsListVo listVo);
+    int handleShelfOrUnAdmin(@Param("listVo") LiveGoodsListVo listVo);
 
-    R handleDeleteSelectedAdmin(@Param("listVo") LiveGoodsListVo listVo);
+    int handleDeleteSelectedAdmin(@Param("listVo") LiveGoodsListVo listVo);
+
+    List<LiveGoodsVo> selectProductListByLiveIdAll(LiveGoods liveGoods);
+
+    @Select("select count(1) from live_goods where is_show = true and live_id = #{liveId}")
+    int selectIsShowDTO(@Param("liveId") Integer liveId);
+
+    void handleIsShowChange(@Param("listVo") LiveGoodsListVo listVo);
+
+    LiveGoodsVo selectLiveGoodsVoByGoodsId(@Param("goodsId") Long goodsId);
+
+    LiveGoodsVo showGoods(@Param("liveId") Long liveId);
+
+    @Select("select * from live_goods where live_id = #{liveId}")
+    List<LiveGoods> selectLiveGoodsByLiveId(@Param("liveId") Long liveId);
+
+    @Update({"<script>" +
+            "update live_goods set is_show " +
+            "CASE WHEN goods_id=#{goodsId} THEN 1" +
+            "ELSE 0 where live_id = #{liveId}" +
+            "</script>"})
+    void updateLiveIsShow(Long goodsId, Long liveId);
 }

+ 5 - 0
fs-service/src/main/java/com/fs/live/mapper/LiveLotteryConfMapper.java

@@ -3,6 +3,7 @@ package com.fs.live.mapper;
 import java.util.List;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.fs.live.domain.LiveLotteryConf;
+import com.fs.live.vo.LiveLotteryConfVo;
 import org.apache.ibatis.annotations.*;
 
 /**
@@ -85,4 +86,8 @@ public interface LiveLotteryConfMapper extends BaseMapper<LiveLotteryConf>{
 
     @Delete("DELETE FROM live_lottery_conf WHERE lottery_id = #{lotteryId}")
     void deleteById(Long lotteryId);
+
+    @Select("SELECT * FROM live_lottery_conf WHERE live_id = #{liveId} AND lottery_status = 1 " +
+            "AND lottery_id NOT IN (SELECT live_id FROM live_lottery_registration WHERE user_id = #{userId} AND is_win = 1)")
+    List<LiveLotteryConfVo> selectActivedLottery(@Param("liveId") Long liveId, @Param("userId") String userId);
 }

+ 5 - 1
fs-service/src/main/java/com/fs/live/mapper/LiveLotteryProductConfMapper.java

@@ -5,6 +5,8 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.fs.live.domain.LiveLotteryProductConf;
 import com.fs.live.param.LiveLotteryProduct;
 import com.fs.live.param.LiveLotteryProductSaveParam;
+import com.fs.live.vo.LiveLotteryProductListVo;
+import org.apache.ibatis.annotations.Param;
 
 /**
  * 直播抽奖记录Mapper接口
@@ -24,7 +26,7 @@ public interface LiveLotteryProductConfMapper extends BaseMapper<LiveLotteryProd
     /**
      * 查询直播抽奖记录列表
      *
-     * @param liveLotteryProductConf 直播抽奖记录
+     * @param
      * @return 直播抽奖记录集合
      */
     List<LiveLotteryProduct> selectLiveLotteryProductConfByLotteryId(Long lotteryId);
@@ -72,4 +74,6 @@ public interface LiveLotteryProductConfMapper extends BaseMapper<LiveLotteryProd
     void saveProducts(LiveLotteryProductSaveParam saveParam);
 
     void deleteLiveLotteryProductConfByLotteryId(Long lotteryId);
+
+    List<LiveLotteryProductListVo> selectLiveLotteryProductConfByLotteryIds(@Param("lotteryIds") List<Long> lotteryIds);
 }

+ 12 - 7
fs-service/src/main/java/com/fs/live/mapper/LiveLotteryRegistrationMapper.java

@@ -3,17 +3,19 @@ package com.fs.live.mapper;
 import java.util.List;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.fs.live.domain.LiveLotteryRegistration;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
 
 /**
  * 直播抽奖登记Mapper接口
- * 
+ *
  * @author fs
  * @date 2025-07-17
  */
 public interface LiveLotteryRegistrationMapper extends BaseMapper<LiveLotteryRegistration>{
     /**
      * 查询直播抽奖登记
-     * 
+     *
      * @param registrationId 直播抽奖登记主键
      * @return 直播抽奖登记
      */
@@ -21,7 +23,7 @@ public interface LiveLotteryRegistrationMapper extends BaseMapper<LiveLotteryReg
 
     /**
      * 查询直播抽奖登记列表
-     * 
+     *
      * @param liveLotteryRegistration 直播抽奖登记
      * @return 直播抽奖登记集合
      */
@@ -29,7 +31,7 @@ public interface LiveLotteryRegistrationMapper extends BaseMapper<LiveLotteryReg
 
     /**
      * 新增直播抽奖登记
-     * 
+     *
      * @param liveLotteryRegistration 直播抽奖登记
      * @return 结果
      */
@@ -37,7 +39,7 @@ public interface LiveLotteryRegistrationMapper extends BaseMapper<LiveLotteryReg
 
     /**
      * 修改直播抽奖登记
-     * 
+     *
      * @param liveLotteryRegistration 直播抽奖登记
      * @return 结果
      */
@@ -45,7 +47,7 @@ public interface LiveLotteryRegistrationMapper extends BaseMapper<LiveLotteryReg
 
     /**
      * 删除直播抽奖登记
-     * 
+     *
      * @param registrationId 直播抽奖登记主键
      * @return 结果
      */
@@ -53,9 +55,12 @@ public interface LiveLotteryRegistrationMapper extends BaseMapper<LiveLotteryReg
 
     /**
      * 批量删除直播抽奖登记
-     * 
+     *
      * @param registrationIds 需要删除的数据主键集合
      * @return 结果
      */
     int deleteLiveLotteryRegistrationByRegistrationIds(Long[] registrationIds);
+
+    @Select("select count(1) from live_lottery_registration where user_id=#{userId} and lottery_id=#{lotteryId}")
+    int selectRecord(@Param("userId") Long userId,@Param("lotteryId") Long lotteryId);
 }

+ 0 - 1
fs-service/src/main/java/com/fs/live/mapper/LiveMapper.java

@@ -76,7 +76,6 @@ public interface LiveMapper extends BaseMapper<Live>
      */
     public int deleteLiveByLiveIds(Long[] liveIds);
 
-    @Select("select * from live where status = 2 and is_show=1 and is_del != 1")
     List<Live> liveList();
 
     @Select("select * from live where talent_id=#{talentId}")

+ 5 - 0
fs-service/src/main/java/com/fs/live/mapper/LiveOrderMapper.java

@@ -2,6 +2,7 @@ package com.fs.live.mapper;
 
 import java.util.List;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.his.domain.FsInquiryOrder;
 import com.fs.live.domain.LiveOrder;
 import com.fs.live.vo.LiveGoodsVo;
 import com.fs.live.vo.LiveOrderListVo;
@@ -91,4 +92,8 @@ public interface LiveOrderMapper extends BaseMapper<LiveOrder>{
             "order by create_time desc" +
             "</script>"})
     List<LiveOrderListVo> selectLiveOrderListVo(@Param("userId") String userId,@Param("status") Integer status);
+
+    @Select("select * from live_order where status = 3 AND TIMESTAMPDIFF(HOUR, start_time, NOW()) >= 48  ")
+    List<LiveOrder> selectLiveOrderByFinish();
+
 }

+ 4 - 0
fs-service/src/main/java/com/fs/live/mapper/LiveRedConfMapper.java

@@ -86,4 +86,8 @@ public interface LiveRedConfMapper extends BaseMapper<LiveRedConf>{
 
     @Delete("DELETE FROM live_red_conf WHERE red_id = #{redId}")
     void deleteById(Long redId);
+
+    @Select("SELECT * FROM live_red_conf WHERE red_status = 1 AND live_id = #{liveId} " +
+            "AND red_id NOT IN (SELECT red_id FROM live_user_red_record WHERE user_id = #{userId})")
+    List<LiveRedConf> selectActivedRed(@Param("liveId") Long liveId, @Param("userId") String userId);
 }

+ 61 - 0
fs-service/src/main/java/com/fs/live/mapper/LiveRewardRecordMapper.java

@@ -0,0 +1,61 @@
+package com.fs.live.mapper;
+
+import java.util.List;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.live.domain.LiveRewardRecord;
+
+/**
+ * 用户直播间奖励记录Mapper接口
+ *
+ * @author fs
+ * @date 2025-08-27
+ */
+public interface LiveRewardRecordMapper extends BaseMapper<LiveRewardRecord>{
+    /**
+     * 查询用户直播间奖励记录
+     *
+     * @param id 用户直播间奖励记录主键
+     * @return 用户直播间奖励记录
+     */
+    LiveRewardRecord selectLiveRewardRecordById(Long id);
+
+    /**
+     * 查询用户直播间奖励记录列表
+     *
+     * @param liveRewardRecord 用户直播间奖励记录
+     * @return 用户直播间奖励记录集合
+     */
+    List<LiveRewardRecord> selectLiveRewardRecordList(LiveRewardRecord liveRewardRecord);
+
+    /**
+     * 新增用户直播间奖励记录
+     *
+     * @param liveRewardRecord 用户直播间奖励记录
+     * @return 结果
+     */
+    int insertLiveRewardRecord(LiveRewardRecord liveRewardRecord);
+
+    /**
+     * 修改用户直播间奖励记录
+     *
+     * @param liveRewardRecord 用户直播间奖励记录
+     * @return 结果
+     */
+    int updateLiveRewardRecord(LiveRewardRecord liveRewardRecord);
+
+    /**
+     * 删除用户直播间奖励记录
+     *
+     * @param id 用户直播间奖励记录主键
+     * @return 结果
+     */
+    int deleteLiveRewardRecordById(Long id);
+
+    /**
+     * 批量删除用户直播间奖励记录
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteLiveRewardRecordByIds(Long[] ids);
+}

+ 3 - 0
fs-service/src/main/java/com/fs/live/mapper/LiveWatchUserMapper.java

@@ -6,6 +6,7 @@ import com.fs.live.vo.LiveWatchUserVO;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
@@ -91,4 +92,6 @@ public interface LiveWatchUserMapper extends BaseMapper<LiveWatchUser>{
 
 
     List<LiveWatchUserVO> selectOnlineUserList(LiveWatchUser param);
+
+    List<LiveWatchUser> checkOnlineNoRewardUser(@Param("liveId") Long liveId,@Param("now") Date now);
 }

+ 11 - 0
fs-service/src/main/java/com/fs/live/param/LiveOrderFinishParam.java

@@ -0,0 +1,11 @@
+package com.fs.live.param;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class LiveOrderFinishParam implements Serializable {
+    Long orderId;
+
+}

+ 29 - 0
fs-service/src/main/java/com/fs/live/param/LotteryPO.java

@@ -0,0 +1,29 @@
+package com.fs.live.param;
+
+import lombok.Data;
+
+@Data
+public class LotteryPO {
+
+    /**
+     * 直播间id
+     * */
+    private Long liveId;
+
+    /**
+     * 抽奖id
+     * */
+    private Long lotteryId;
+
+    /**
+     * 红包金额
+     * */
+    private Long integral;
+
+    /**
+     * 用户Id
+     * */
+    private Long userId;
+
+
+}

+ 78 - 0
fs-service/src/main/java/com/fs/live/service/ILiveAutoTaskService.java

@@ -0,0 +1,78 @@
+package com.fs.live.service;
+
+import java.util.Date;
+import java.util.List;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.common.core.domain.R;
+import com.fs.live.domain.LiveAutoTask;
+
+/**
+ * 直播间自动化任务配置Service接口
+ *
+ * @author fs
+ * @date 2025-08-29
+ */
+public interface ILiveAutoTaskService extends IService<LiveAutoTask>{
+    /**
+     * 查询直播间自动化任务配置
+     *
+     * @param id 直播间自动化任务配置主键
+     * @return 直播间自动化任务配置
+     */
+    LiveAutoTask selectLiveAutoTaskById(Long id);
+
+    /**
+     * 查询直播间自动化任务配置列表
+     *
+     * @param liveAutoTask 直播间自动化任务配置
+     * @return 直播间自动化任务配置集合
+     */
+    List<LiveAutoTask> selectLiveAutoTaskList(LiveAutoTask liveAutoTask);
+
+    /**
+     * 新增直播间自动化任务配置
+     *
+     * @param liveAutoTask 直播间自动化任务配置
+     * @return 结果
+     */
+    R insertLiveAutoTask(LiveAutoTask liveAutoTask);
+
+    /**
+     * 复制新增直播间自动化任务配置
+     *
+     * @param liveAutoTask 直播间自动化任务配置
+     * @return 结果
+     */
+    R copyInsertLiveAutoTask(LiveAutoTask liveAutoTask);
+
+    /**
+     * 修改直播间自动化任务配置
+     *
+     * @param liveAutoTask 直播间自动化任务配置
+     * @return 结果
+     */
+    int updateLiveAutoTask(LiveAutoTask liveAutoTask);
+
+    /**
+     * 批量删除直播间自动化任务配置
+     *
+     * @param ids 需要删除的直播间自动化任务配置主键集合
+     * @return 结果
+     */
+    int deleteLiveAutoTaskByIds(Long[] ids);
+
+    /**
+     * 删除直播间自动化任务配置信息
+     *
+     * @param id 直播间自动化任务配置主键
+     * @return 结果
+     */
+    int deleteLiveAutoTaskById(Long id);
+
+    List<LiveAutoTask> selectLiveAutoTaskByLiveId(Long existLiveId);
+    List<LiveAutoTask> selectNoActivedByLiveId(Long existLiveId, Date now);
+
+    List<LiveAutoTask> selectLiveAutoTaskByLiveIds(List<Long> liveIdLists);
+
+    void recalcLiveAutoTask(Long liveId);
+}

+ 10 - 1
fs-service/src/main/java/com/fs/live/service/ILiveGoodsService.java

@@ -49,7 +49,7 @@ public interface ILiveGoodsService extends IService<LiveGoods>{
      * @param liveGoods 直播商品
      * @return 结果
      */
-    int updateLiveGoods(LiveGoods liveGoods);
+    R updateLiveGoods(LiveGoods liveGoods);
 
     /**
      * 批量删除直播商品
@@ -116,4 +116,13 @@ public interface ILiveGoodsService extends IService<LiveGoods>{
     R handleShelfOrUnAdmin(LiveGoodsListVo listVo);
 
     R handleDeleteSelectedAdmin(LiveGoodsListVo listVo);
+
+    R handleIsShowChange(LiveGoodsListVo listVo);
+    LiveGoodsVo selectLiveGoodsVoByGoodsId(Long goodsId);
+
+    R showGoods(Long liveId);
+
+    List<LiveGoods> selectByLiveId(Long existLiveId);
+
+    void updateLiveIsShow(Long goodsId, Long liveId);
 }

+ 9 - 0
fs-service/src/main/java/com/fs/live/service/ILiveLotteryConfService.java

@@ -2,8 +2,11 @@ package com.fs.live.service;
 
 import java.util.List;
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.common.core.domain.R;
 import com.fs.live.domain.LiveLotteryConf;
 import com.fs.live.param.LiveLotteryProductSaveParam;
+import com.fs.live.param.LotteryPO;
+import com.fs.live.vo.LiveLotteryConfVo;
 import com.fs.live.vo.LiveLotteryProductVO;
 
 /**
@@ -76,4 +79,10 @@ public interface ILiveLotteryConfService extends IService<LiveLotteryConf>{
     void saveProducts(LiveLotteryProductSaveParam liveLotteryProducts);
 
     LiveLotteryProductVO getGoods(Long lotteryId);
+
+    List<LiveLotteryConf> selectByLiveId(Long existLiveId);
+
+    R claimLotteryPacket(LotteryPO lottery);
+
+    List<LiveLotteryConfVo> selectActivedLottery(Long liveId, String userId);
 }

+ 5 - 0
fs-service/src/main/java/com/fs/live/service/ILiveOrderService.java

@@ -8,6 +8,7 @@ import com.fs.course.param.FsUserLiveOrderPayUParam;
 import com.fs.live.domain.LiveGoods;
 import com.fs.live.domain.LiveOrder;
 import com.fs.live.param.LiveOrderConfirmParam;
+import com.fs.live.param.LiveOrderFinishParam;
 import com.fs.live.vo.LiveGoodsVo;
 import com.fs.live.vo.LiveOrderListVo;
 import com.fs.live.vo.LiveOrderVo;
@@ -149,4 +150,8 @@ public interface ILiveOrderService extends IService<LiveOrder>{
     R buy(LiveOrder liveOrder);
 
     R finishOrder(Long orderId);
+
+    List<LiveOrder> selectLiveOrderByFinish();
+
+    void autoFinishOrder(LiveOrderFinishParam param);
 }

+ 4 - 0
fs-service/src/main/java/com/fs/live/service/ILiveRedConfService.java

@@ -74,4 +74,8 @@ public interface ILiveRedConfService extends IService<LiveRedConf>{
     R claimRedPacket(RedPO red);
 
     String start(String redId, Long userId);
+
+    List<LiveRedConf> selectByLiveId(Long existLiveId);
+
+    List<LiveRedConf> selectActivedRed(Long liveId, String userId);
 }

+ 61 - 0
fs-service/src/main/java/com/fs/live/service/ILiveRewardRecordService.java

@@ -0,0 +1,61 @@
+package com.fs.live.service;
+
+import java.util.List;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.live.domain.LiveRewardRecord;
+
+/**
+ * 用户直播间奖励记录Service接口
+ *
+ * @author fs
+ * @date 2025-08-27
+ */
+public interface ILiveRewardRecordService extends IService<LiveRewardRecord>{
+    /**
+     * 查询用户直播间奖励记录
+     *
+     * @param id 用户直播间奖励记录主键
+     * @return 用户直播间奖励记录
+     */
+    LiveRewardRecord selectLiveRewardRecordById(Long id);
+
+    /**
+     * 查询用户直播间奖励记录列表
+     *
+     * @param liveRewardRecord 用户直播间奖励记录
+     * @return 用户直播间奖励记录集合
+     */
+    List<LiveRewardRecord> selectLiveRewardRecordList(LiveRewardRecord liveRewardRecord);
+
+    /**
+     * 新增用户直播间奖励记录
+     *
+     * @param liveRewardRecord 用户直播间奖励记录
+     * @return 结果
+     */
+    int insertLiveRewardRecord(LiveRewardRecord liveRewardRecord);
+
+    /**
+     * 修改用户直播间奖励记录
+     *
+     * @param liveRewardRecord 用户直播间奖励记录
+     * @return 结果
+     */
+    int updateLiveRewardRecord(LiveRewardRecord liveRewardRecord);
+
+    /**
+     * 批量删除用户直播间奖励记录
+     *
+     * @param ids 需要删除的用户直播间奖励记录主键集合
+     * @return 结果
+     */
+    int deleteLiveRewardRecordByIds(Long[] ids);
+
+    /**
+     * 删除用户直播间奖励记录信息
+     *
+     * @param id 用户直播间奖励记录主键
+     * @return 结果
+     */
+    int deleteLiveRewardRecordById(Long id);
+}

+ 4 - 0
fs-service/src/main/java/com/fs/live/service/ILiveService.java

@@ -152,4 +152,8 @@ public interface ILiveService extends IService<Live>
     R handleShelfOrUnAdmin(LiveListVo listVo);
 
     R handleDeleteSelectedAdmin(LiveListVo listVo);
+
+    R copyLive(Live live);
+
+    R currentActivities(Long liveId, String userId);
 }

+ 5 - 0
fs-service/src/main/java/com/fs/live/service/ILiveWatchUserService.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
 import com.fs.live.domain.LiveWatchUser;
 import com.fs.live.vo.LiveWatchUserVO;
 
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
@@ -100,4 +101,8 @@ public interface ILiveWatchUserService extends IService<LiveWatchUser>{
     LiveWatchUserVO selectWatchUserByLiveIdAndUserId(Long liveId, Long userId);
 
     List<LiveWatchUserVO> selectOnlineUserList(LiveWatchUser param);
+
+    List<LiveWatchUser> checkOnlineNoRewardUser(Long liveId, Date now);
+
+    int blockUser(Long userId);
 }

+ 233 - 0
fs-service/src/main/java/com/fs/live/service/impl/LiveAutoTaskServiceImpl.java

@@ -0,0 +1,233 @@
+package com.fs.live.service.impl;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+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.live.domain.Live;
+import com.fs.live.domain.LiveAutoTask;
+import com.fs.live.mapper.LiveAutoTaskMapper;
+import com.fs.live.mapper.LiveMapper;
+import com.fs.live.service.ILiveAutoTaskService;
+import com.fs.live.service.ILiveGoodsService;
+import com.fs.live.service.ILiveService;
+import com.fs.live.vo.LiveGoodsVo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * 直播间自动化任务配置Service业务层处理
+ *
+ * @author fs
+ * @date 2025-08-29
+ */
+@Service
+public class LiveAutoTaskServiceImpl extends ServiceImpl<LiveAutoTaskMapper, LiveAutoTask> implements ILiveAutoTaskService {
+
+    @Autowired
+    private LiveMapper liveMapper;
+    @Autowired
+    private ILiveGoodsService goodsService;
+    @Autowired
+    private RedisCache redisCache;
+
+    /**
+     * 查询直播间自动化任务配置
+     *
+     * @param id 直播间自动化任务配置主键
+     * @return 直播间自动化任务配置
+     */
+    @Override
+    public LiveAutoTask selectLiveAutoTaskById(Long id)
+    {
+        return baseMapper.selectLiveAutoTaskById(id);
+    }
+
+    /**
+     * 查询直播间自动化任务配置列表
+     *
+     * @param liveAutoTask 直播间自动化任务配置
+     * @return 直播间自动化任务配置
+     */
+    @Override
+    public List<LiveAutoTask> selectLiveAutoTaskList(LiveAutoTask liveAutoTask)
+    {
+        return baseMapper.selectLiveAutoTaskList(liveAutoTask);
+    }
+
+    /**
+     * 新增直播间自动化任务配置
+     *
+     * @param liveAutoTask 直播间自动化任务配置
+     * @return 结果
+     */
+    @Override
+    public R insertLiveAutoTask(LiveAutoTask liveAutoTask)
+    {
+
+        Live live = liveMapper.selectLiveByLiveId(liveAutoTask.getLiveId());
+        if(live == null) return R.error("直播间不存在");
+        //截取triggerValue 时分秒部分 ,然后根据查询出来的直播间开始时间计算出相对时间
+        Date triggerValue = liveAutoTask.getTriggerValue();
+        if (triggerValue == null) {
+            return R.error("请设置触发时间");
+        }
+        Date now = new Date();
+        // 获取直播间开始时间进行计算触发时间
+        liveAutoTask.setTriggerValue(triggerValue);
+        liveAutoTask.setAbsValue(getTriggerValue(triggerValue, live.getStartTime()));
+        //如果触发时间小于当前时间 报错
+        if (liveAutoTask.getAbsValue().before(now)) {return R.error("请勿设置触发时间小于当前时间");}
+
+        // 根据触发时间 查看同一时间有没有相同的数据 1.直播间id 2.时间
+        LiveAutoTask existTask = baseMapper.selectLiveAutoTaskByTriggerValue(liveAutoTask.getLiveId(), liveAutoTask.getTriggerValue());
+        if(existTask != null) return R.error("该时间段有任务");
+
+
+        liveAutoTask.setCreateTime(now);
+        liveAutoTask.setUpdateTime(now);
+        liveAutoTask.setTriggerType(2L);
+        liveAutoTask.setFinishStatus(0L);
+        if(liveAutoTask.getStatus() == null) liveAutoTask.setStatus(1L);
+        LiveGoodsVo liveGoodsVo = goodsService.selectLiveGoodsVoByGoodsId(Long.valueOf(liveAutoTask.getContent()));
+        if(liveGoodsVo == null) return R.error("商品不存在");
+        liveAutoTask.setContent(JSON.toJSONString(liveGoodsVo));
+        baseMapper.insertLiveAutoTask(liveAutoTask);
+
+        if (live.getStatus() == 2 && liveAutoTask.getStatus() == 1L) {
+            liveAutoTask.setUpdateTime(null);
+            liveAutoTask.setCreateTime(null);
+            redisCache.redisTemplate.opsForZSet().add("live:auto_task:" + live.getLiveId(), JSON.toJSONString(liveAutoTask),liveAutoTask.getAbsValue().getTime());
+        }
+
+
+        return R.ok();
+    }
+
+    /**
+     * 复制新增直播间自动化任务配置
+     *
+     * @param liveAutoTask 直播间自动化任务配置
+     * @return 结果
+     */
+    @Override
+    public R copyInsertLiveAutoTask(LiveAutoTask liveAutoTask)
+    {
+
+        Live live = liveMapper.selectLiveByLiveId(liveAutoTask.getLiveId());
+        if(live == null) return R.error("直播间不存在");
+        //截取triggerValue 时分秒部分 ,然后根据查询出来的直播间开始时间计算出相对时间
+        Date triggerValue = liveAutoTask.getTriggerValue();
+        if (triggerValue == null) {
+            return R.error("请设置触发时间");
+        }
+        Date now = new Date();
+        // 获取直播间开始时间进行计算触发时间
+        liveAutoTask.setTriggerValue(triggerValue);
+        liveAutoTask.setAbsValue(getTriggerValue(triggerValue, live.getStartTime()));
+
+        liveAutoTask.setCreateTime(now);
+        liveAutoTask.setUpdateTime(now);
+        liveAutoTask.setTriggerType(2L);
+        liveAutoTask.setFinishStatus(0L);
+        if(liveAutoTask.getStatus() == null) liveAutoTask.setStatus(1L);
+        baseMapper.insertLiveAutoTask(liveAutoTask);
+
+        if (live.getStatus() == 2 && liveAutoTask.getStatus() == 1L) {
+            liveAutoTask.setUpdateTime(null);
+            liveAutoTask.setCreateTime(null);
+            redisCache.redisTemplate.opsForZSet().add("live:auto_task:" + live.getLiveId(), JSON.toJSONString(liveAutoTask),liveAutoTask.getAbsValue().getTime());
+        }
+        return R.ok();
+    }
+
+    private Date getTriggerValue( Date triggerValue, LocalDateTime liveStartTime) {
+        LocalDateTime triggerDateTime = LocalDateTime.ofInstant(
+                triggerValue.toInstant(),
+                ZoneId.systemDefault()
+        );
+        LocalTime triggerTime = triggerDateTime.toLocalTime();
+        LocalDateTime combinedTime = liveStartTime
+                .plusHours(triggerTime.getHour())
+                .plusMinutes(triggerTime.getMinute())
+                .plusSeconds(triggerTime.getSecond());
+        return Date.from(combinedTime.atZone(ZoneId.systemDefault()).toInstant());
+    }
+
+    /**
+     * 修改直播间自动化任务配置
+     *
+     * @param liveAutoTask 直播间自动化任务配置
+     * @return 结果
+     */
+    @Override
+    public int updateLiveAutoTask(LiveAutoTask liveAutoTask)
+    {
+        LiveAutoTask existTask = baseMapper.selectLiveAutoTaskById(liveAutoTask.getId());
+        redisCache.redisTemplate.opsForZSet().removeRangeByScore("live:auto_task:" + existTask.getLiveId(), existTask.getAbsValue().getTime(), existTask.getAbsValue().getTime());
+
+        baseMapper.deleteLiveAutoTaskById(liveAutoTask.getId());
+        return baseMapper.insertLiveAutoTask(liveAutoTask);
+    }
+
+    /**
+     * 批量删除直播间自动化任务配置
+     *
+     * @param ids 需要删除的直播间自动化任务配置主键
+     * @return 结果
+     */
+    @Override
+    public int deleteLiveAutoTaskByIds(Long[] ids)
+    {
+        return baseMapper.deleteLiveAutoTaskByIds(ids);
+    }
+
+    /**
+     * 删除直播间自动化任务配置信息
+     *
+     * @param id 直播间自动化任务配置主键
+     * @return 结果
+     */
+    @Override
+    public int deleteLiveAutoTaskById(Long id)
+    {
+        return baseMapper.deleteLiveAutoTaskById(id);
+    }
+
+    @Override
+    public List<LiveAutoTask> selectLiveAutoTaskByLiveId(Long existLiveId) {
+        return  baseMapper.selectLiveAutoTaskByLiveId(existLiveId);
+    }
+
+    @Override
+    public List<LiveAutoTask> selectNoActivedByLiveId(Long existLiveId, Date now) {
+        return baseMapper.selectNoActivedByLiveId(existLiveId, now);
+    }
+
+    @Override
+    public List<LiveAutoTask> selectLiveAutoTaskByLiveIds(List<Long> liveIdLists) {
+        return baseMapper.selectLiveAutoTaskByLiveIds(liveIdLists, new Date());
+    }
+
+    @Override
+    public void recalcLiveAutoTask(Long liveId) {
+        Live live = liveMapper.selectLiveByLiveId(liveId);
+        if(live == null) return;
+        List<LiveAutoTask> liveAutoTasks = baseMapper.selectLiveAutoTaskByLiveId(liveId);
+        Date now = new Date();
+        for (LiveAutoTask liveAutoTask : liveAutoTasks) {
+            liveAutoTask.setAbsValue(getTriggerValue(liveAutoTask.getTriggerValue(), live.getStartTime()));
+            liveAutoTask.setUpdateTime(now);
+            liveAutoTask.setFinishStatus(0L);
+            baseMapper.updateLiveAutoTask(liveAutoTask);
+        }
+    }
+}

+ 18 - 16
fs-service/src/main/java/com/fs/live/service/impl/LiveDataServiceImpl.java

@@ -364,9 +364,21 @@ public class LiveDataServiceImpl extends ServiceImpl<LiveDataMapper, LiveData> i
     public R updateLikeByLiveId(Long liveId, Long userId) {
         //一个用户一天只能点赞十次
         String key = "live:like:" + liveId + ":user:" + userId+":date:"+ DateUtils.getDate();
+        Integer count =redisCache.getCacheObject(key);
+        if((count==null?0: count)>=10) {
+            //当前时间
+            LocalDateTime now = LocalDateTime.now();
+            //明天0点
+            LocalDateTime tomorrow = now.plusDays(1).withHour(0).withMinute(0).withSecond(0);
+            //间隔秒
+            long seconds = ChronoUnit.SECONDS.between(now, tomorrow);
+            redisCache.expire(key, (int)seconds, TimeUnit.SECONDS);
+            return R.ok("每天最多为直播间点赞10次哟");
+        }
         LiveUserLike liveUserLike;
+        boolean firstLike = redisCache.setIfAbsent(key, 1, 1, TimeUnit.DAYS);
         //用户直播间第一次点赞
-        if(redisCache.setIfAbsent( key,1, 1, TimeUnit.DAYS)) {
+        if(firstLike) {
             //直播间总点赞数
             redisCache.increment("live:like:" + liveId,1);
             liveUserLike = liveUserLikeService.selectLiveUserLikeByIds(liveId, userId);
@@ -378,22 +390,12 @@ public class LiveDataServiceImpl extends ServiceImpl<LiveDataMapper, LiveData> i
                 liveUserLikeService.insertLiveUserLike(liveUserLike);
                 return R.ok().put("like",redisCache.getCacheObject(key));
             }
+        }else{
+            //用户直播间点赞数
+            redisCache.increment(key,1);
+            //直播间总点赞数
+            redisCache.increment("live:like:" + liveId,1);
         }
-        Integer count =redisCache.getCacheObject(key);
-        if((count==null?0: count)>=10) {
-            //当前时间
-            LocalDateTime now = LocalDateTime.now();
-            //明天0点
-            LocalDateTime tomorrow = now.plusDays(1).withHour(0).withMinute(0).withSecond(0);
-            //间隔秒
-            long seconds = ChronoUnit.SECONDS.between(now, tomorrow);
-            redisCache.expire(key, (int)seconds, TimeUnit.SECONDS);
-            return R.ok("每天最多为直播间点赞10次哟");
-        }
-        //用户直播间点赞数
-        redisCache.increment(key,1);
-        //直播间总点赞数
-        redisCache.increment("live:like:" + liveId,1);
         return R.ok().put("like",redisCache.getCacheObject(key));
     }
 

+ 54 - 13
fs-service/src/main/java/com/fs/live/service/impl/LiveGoodsServiceImpl.java

@@ -87,10 +87,20 @@ public class LiveGoodsServiceImpl extends ServiceImpl<LiveGoodsMapper, LiveGoods
      * @return 结果
      */
     @Override
-    public int updateLiveGoods(LiveGoods liveGoods)
+    public R updateLiveGoods(LiveGoods liveGoods)
     {
+        if(liveGoods.getGoodsId() == null ) return R.error("请选择商品");
         liveGoods.setUpdateTime(DateUtils.getNowDate());
-        return baseMapper.updateLiveGoods(liveGoods);
+        LiveGoods existGoods = baseMapper.selectLiveGoodsByGoodsId(liveGoods.getGoodsId());
+        if (liveGoods.getStock() != null) {
+            if(liveGoods.getProductId() == null) return R.error("店铺已停止售卖商品!");
+            FsStoreProduct fsStoreProduct = fsStoreProductService.selectFsStoreProductByProductId(liveGoods.getProductId());
+            if(fsStoreProduct == null) return R.error("商品不存在");
+            if(fsStoreProduct.getIsShow() == 0 || existGoods.getStatus() == 0) return R.error("商品已下架");
+            if(fsStoreProduct.getStock() < liveGoods.getStock()) return R.error("商品库存不足");
+        }
+        baseMapper.updateLiveGoods(liveGoods);
+        return R.ok();
     }
 
     /**
@@ -133,7 +143,28 @@ public class LiveGoodsServiceImpl extends ServiceImpl<LiveGoodsMapper, LiveGoods
 
     @Override
     public List<LiveGoodsVo> selectProductListByLiveId(LiveGoods liveGoods) {
-        return baseMapper.selectProductListByLiveId(liveGoods);
+        return baseMapper.selectProductListByLiveIdAll(liveGoods);
+    }
+
+    @Override
+    public LiveGoodsVo selectLiveGoodsVoByGoodsId(Long goodsId) {
+        return baseMapper.selectLiveGoodsVoByGoodsId(goodsId);
+    }
+
+    @Override
+    public R showGoods(Long liveId) {
+        LiveGoodsVo dto = baseMapper.showGoods(liveId);
+        return R.ok().put("data", dto);
+    }
+
+    @Override
+    public List<LiveGoods> selectByLiveId(Long existLiveId) {
+        return baseMapper.selectLiveGoodsByLiveId(existLiveId);
+    }
+
+    @Override
+    public void updateLiveIsShow(Long goodsId, Long liveId) {
+        baseMapper.updateLiveIsShow(goodsId, liveId);
     }
 
     /**
@@ -152,14 +183,6 @@ public class LiveGoodsServiceImpl extends ServiceImpl<LiveGoodsMapper, LiveGoods
         }
         //  查询商品信息列表(假设返回 List<StoreProduct>)
         List<FsStoreProduct> productInfoList = fsStoreProductMapper.selectFsStoreProductByProductIds(productIdList);
-        Set<Long> storeSet = productInfoList.stream().map(FsStoreProduct::getStoreId).collect(Collectors.toSet());
-        List<Long> storeIds = baseMapper.selectStoreIdsByLiveId(liveId);
-        if (!storeIds.isEmpty()) {
-            storeSet.addAll(storeIds);
-        }
-        if (storeSet.size() > 1) {
-            return R.error("商品来自不同的店铺,请重新选择商品");
-        }
 
         //  转换为 LiveGoods 并批量插入
         List<LiveGoods> liveGoodsList = productInfoList.stream()
@@ -207,6 +230,7 @@ public class LiveGoodsServiceImpl extends ServiceImpl<LiveGoodsMapper, LiveGoods
                     liveGoods.setSort(product.getSort());
                     liveGoods.setCreateTime(DateUtils.getNowDate());
                     liveGoods.setCreateBy(String.valueOf(user.getUserId()));
+                    liveGoods.setIsShow(false);
                     return liveGoods;
                 })
                 .collect(Collectors.toList());
@@ -253,11 +277,28 @@ public class LiveGoodsServiceImpl extends ServiceImpl<LiveGoodsMapper, LiveGoods
 
     @Override
     public R handleShelfOrUnAdmin(LiveGoodsListVo listVo) {
-        return baseMapper.handleShelfOrUnAdmin(listVo);
+        baseMapper.handleShelfOrUnAdmin(listVo);
+        return R.ok();
     }
 
     @Override
     public R handleDeleteSelectedAdmin(LiveGoodsListVo listVo) {
-        return baseMapper.handleDeleteSelectedAdmin(listVo);
+        baseMapper.handleDeleteSelectedAdmin(listVo);
+        return R.ok();
+    }
+
+    @Override
+    public R handleIsShowChange(LiveGoodsListVo listVo) {
+        if (listVo.getGoodsIds().isEmpty()) {
+            return R.error("请选择商品");
+        }
+        if (listVo.getIsShow()) {
+            int count = baseMapper.selectIsShowDTO(listVo.getLiveId());
+            if (count > 0 || listVo.getGoodsIds().size() > 1) {
+                return R.ok("目前仅支持单一物品展示").put("isShow", !listVo.getIsShow());
+            }
+        }
+        baseMapper.handleIsShowChange(listVo);
+        return R.ok().put("isShow", listVo.getIsShow());
     }
 }

+ 44 - 0
fs-service/src/main/java/com/fs/live/service/impl/LiveLotteryConfServiceImpl.java

@@ -1,12 +1,21 @@
 package com.fs.live.service.impl;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Date;
 import java.util.List;
+
+import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.DateUtils;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.live.domain.LiveLotteryRegistration;
 import com.fs.live.mapper.LiveLotteryProductConfMapper;
+import com.fs.live.mapper.LiveLotteryRegistrationMapper;
 import com.fs.live.param.LiveLotteryProduct;
 import com.fs.live.param.LiveLotteryProductSaveParam;
+import com.fs.live.param.LotteryPO;
+import com.fs.live.vo.LiveLotteryConfVo;
 import com.fs.live.vo.LiveLotteryProductVO;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -29,6 +38,10 @@ public class LiveLotteryConfServiceImpl extends ServiceImpl<LiveLotteryConfMappe
 
     @Autowired
     private LiveLotteryProductConfMapper productMapper;
+    @Autowired
+    private RedisCache redisCache;
+    @Autowired
+    private LiveLotteryRegistrationMapper lotteryRegistrationMapper;
     /**
      * 查询直播抽奖配置
      *
@@ -76,6 +89,7 @@ public class LiveLotteryConfServiceImpl extends ServiceImpl<LiveLotteryConfMappe
     @Override
     public int updateLiveLotteryConf(LiveLotteryConf liveLotteryConf)
     {
+        String cacheKey = "live:lottery:" + liveLotteryConf.getLotteryId();
         if("1".equals(liveLotteryConf.getLotteryStatus())){
             List<LiveLotteryProduct> prizes = productMapper.selectLiveLotteryProductConfByLotteryId(liveLotteryConf.getLotteryId());
             if (prizes == null || prizes.isEmpty()){
@@ -175,4 +189,34 @@ public class LiveLotteryConfServiceImpl extends ServiceImpl<LiveLotteryConfMappe
         );
         return liveLotteryProductVO;
     }
+
+    @Override
+    public List<LiveLotteryConf> selectByLiveId(Long existLiveId) {
+        return baseMapper.selectByLiveId(existLiveId);
+    }
+
+    @Override
+    @Transactional
+    public R claimLotteryPacket(LotteryPO lottery) {
+        int i = lotteryRegistrationMapper.selectRecord(lottery.getUserId(), lottery.getLotteryId());
+        if (i > 0) {
+            return R.ok("您已参加抽奖活动,请等待开奖!");
+        }
+        LiveLotteryRegistration registration = new LiveLotteryRegistration();
+        Date now = new Date();
+        registration.setLiveId(lottery.getLiveId());
+        registration.setUserId(lottery.getUserId());
+        registration.setLotteryId(lottery.getLotteryId());
+        registration.setIsWin(0L);
+        registration.setRizeLevel(-1L);
+        registration.setCreateTime(now);
+        registration.setUpdateTime(now);
+        lotteryRegistrationMapper.insertLiveLotteryRegistration(registration);
+        return R.ok("参与抽奖成功!请在直播间等待开奖");
+    }
+
+    @Override
+    public List<LiveLotteryConfVo> selectActivedLottery(Long liveId, String userId) {
+        return baseMapper.selectActivedLottery(liveId, userId);
+    }
 }

+ 30 - 3
fs-service/src/main/java/com/fs/live/service/impl/LiveOrderServiceImpl.java

@@ -64,6 +64,7 @@ import com.fs.live.domain.*;
 import com.fs.live.dto.LiveOrderItemDTO;
 import com.fs.live.mapper.*;
 import com.fs.live.param.LiveOrderConfirmParam;
+import com.fs.live.param.LiveOrderFinishParam;
 import com.fs.live.service.ILiveOrderLogsService;
 import com.fs.live.vo.LiveGoodsVo;
 import com.fs.live.vo.LiveOrderItemVo;
@@ -803,6 +804,16 @@ public class LiveOrderServiceImpl extends ServiceImpl<LiveOrderMapper, LiveOrder
         return finishStoreOrder(orderId);
     }
 
+    @Override
+    public List<LiveOrder> selectLiveOrderByFinish() {
+        return baseMapper.selectLiveOrderByFinish();
+    }
+
+    @Override
+    public void autoFinishOrder(LiveOrderFinishParam param) {
+
+    }
+
     @Transactional
     public R finishStoreOrder(Long orderId){
         LiveOrder order= baseMapper.selectLiveOrderByOrderId(String.valueOf(orderId));
@@ -881,7 +892,7 @@ public class LiveOrderServiceImpl extends ServiceImpl<LiveOrderMapper, LiveOrder
     /**
      * 删除订单信息
      *
-     * @param orderId 订单主键
+     * @param
      * @return 结果
      */
     /*@Override
@@ -1081,15 +1092,20 @@ public class LiveOrderServiceImpl extends ServiceImpl<LiveOrderMapper, LiveOrder
         LiveGoods goods = liveGoodsMapper.selectLiveGoodsByProductId(liveOrder.getLiveId(), liveOrder.getProductId());
         if(fsStoreProduct == null) return R.error("商品不存在");
         if(fsStoreProduct.getIsShow() == 0 || goods.getStatus() == 0) return R.error("商品已下架");
-        if(fsStoreProduct.getStock() < Integer.parseInt(liveOrder.getTotalNum())) return R.error("库存不足");
+        if(fsStoreProduct.getStock() < Integer.parseInt(liveOrder.getTotalNum()) || goods.getStock() < Integer.parseInt(liveOrder.getTotalNum())) return R.error("库存不足");
 
-        // 更改库存
+        // 更改店铺库存
         fsStoreProduct.setStock(fsStoreProduct.getStock()-Integer.parseInt(liveOrder.getTotalNum()));
         fsStoreProductService.updateFsStoreProduct(fsStoreProduct);
+        // 更新直播间库存
+        goods.setStock(goods.getStock()-Integer.parseInt(liveOrder.getTotalNum()));
+        liveGoodsMapper.updateLiveGoods(goods);
 
 //        liveOrderItemMapper
 
 
+
+
         //todo yhq
         String orderSn = OrderCodeUtils.getOrderSn();
         log.info("订单生成:"+orderSn);
@@ -1143,11 +1159,22 @@ public class LiveOrderServiceImpl extends ServiceImpl<LiveOrderMapper, LiveOrder
 
 
     @Override
+    @Transactional
     public R cancelOrder(LiveOrder order) {
         if(order.getStatus() == FsStoreOrderStatusEnum.STATUS_1.getValue()){
+            LiveOrder liveOrder = baseMapper.selectLiveOrderByOrderId(String.valueOf(order.getOrderId()));
+            if(liveOrder == null) return R.error("订单不存在");
             baseMapper.cancelOrder(order.getOrderId());
             liveOrderLogsService.create(order.getOrderId(), FsStoreOrderLogEnum.CANCEL_ORDER.getValue(),
                     FsStoreOrderLogEnum.CANCEL_ORDER.getDesc());
+            // 恢复库存
+            FsStoreProduct fsStoreProduct = fsStoreProductService.selectFsStoreProductByProductId(liveOrder.getProductId());
+            LiveGoods goods = liveGoodsMapper.selectLiveGoodsByProductId(liveOrder.getLiveId(), liveOrder.getProductId());
+            fsStoreProduct.setStock(fsStoreProduct.getStock()+Integer.parseInt(liveOrder.getTotalNum()));
+            fsStoreProductService.updateFsStoreProduct(fsStoreProduct);
+            goods.setStock(goods.getStock()+Long.parseLong(liveOrder.getTotalNum()));
+            liveGoodsMapper.updateLiveGoods(goods);
+
             return R.ok("操作成功");
         }else {
             return R.error("非法操作");

+ 52 - 16
fs-service/src/main/java/com/fs/live/service/impl/LiveRedConfServiceImpl.java

@@ -8,9 +8,12 @@ import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.DateUtils;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.his.service.IFsUserService;
 import com.fs.live.domain.LiveUserRedRecord;
 import com.fs.live.mapper.LiveUserRedRecordMapper;
 import com.fs.live.param.RedPO;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.script.DefaultRedisScript;
 import org.springframework.stereotype.Service;
@@ -28,14 +31,19 @@ import org.springframework.transaction.annotation.Transactional;
 @Service
 public class LiveRedConfServiceImpl extends ServiceImpl<LiveRedConfMapper, LiveRedConf> implements ILiveRedConfService {
 
+    private static final Logger log = LoggerFactory.getLogger(LiveRedConfServiceImpl.class);
     @Autowired
     private LiveUserRedRecordMapper userRedRecordMapper;
 
     @Autowired
     private RedisCache redisCache;
 
+    @Autowired
+    private IFsUserService userService;
+
     private static final String REDPACKET_REMAININGLOTS_KEY = "live:red:remainingLots:";
     private static final String REDPACKET_REMAININGNUM_KEY = "live:red:remainingNum:";
+    private static final String REDPACKET_CLAIM_KEY = "live:red:claim:";
     /**
      * 查询直播红包记录配置
      *
@@ -82,6 +90,16 @@ public class LiveRedConfServiceImpl extends ServiceImpl<LiveRedConfMapper, LiveR
     @Override
     public int updateLiveRedConf(LiveRedConf liveRedConf)
     {
+        // 开始
+        if (liveRedConf.getRedStatus() == 1) {
+            setRemaining(liveRedConf.getRedId(), liveRedConf.getTotalLots());
+        } else if (liveRedConf.getRedStatus() == 2) {
+            // 结算
+            redisCache.deleteObject(REDPACKET_REMAININGLOTS_KEY + liveRedConf.getRedId());
+        } else if (liveRedConf.getRedStatus() == 3) {
+            // 暂停
+            redisCache.deleteObject(REDPACKET_REMAININGLOTS_KEY + liveRedConf.getRedId());
+        }
         return baseMapper.updateLiveRedConf(liveRedConf);
     }
 
@@ -145,17 +163,19 @@ public class LiveRedConfServiceImpl extends ServiceImpl<LiveRedConfMapper, LiveR
     @Override
     @Transactional
     public R claimRedPacket(RedPO red) {
-        String lockKey = REDPACKET_REMAININGLOTS_KEY + red.getRedId();
-        List<String> keyList =  new ArrayList<>();
-        keyList.add(lockKey);
+        String claimKey = REDPACKET_CLAIM_KEY + red.getRedId();
 
         try {
             Integer remaining = getRemaining(red.getRedId());
             if (remaining <= 0) {
+                LiveRedConf liveRedConf = new LiveRedConf();
+                liveRedConf.setRedId(red.getRedId());
+                liveRedConf.setRedStatus(2L);
+                baseMapper.updateLiveRedConf(liveRedConf);
                 return R.error("手慢了,红包已被抢完~");
             }
             //获取红包锁
-            if (!tryLock(lockKey, red.getUserId().toString(), 5)) {
+            if (!tryLock(claimKey, red.getUserId().toString(), 5)) {
                 return R.error("您已经领取过红包了!");
             }
 
@@ -172,20 +192,19 @@ public class LiveRedConfServiceImpl extends ServiceImpl<LiveRedConfMapper, LiveR
             }
             Date now = new Date();
             //剩余金额
-            Integer remainingNum = getRemainingNum(red.getRedId());
+//            Integer remainingNum = getRemainingNum(red.getRedId());
 
 
             conf.setTotalSend(conf.getTotalSend() + 1);
             conf.setRemaining(Math.toIntExact(conf.getTotalLots() - conf.getTotalSend()));
             conf.setUpdateTime(now);
 
-            // todo yhq 更新用户芳华币数量
-
+            userService.increaseIntegral(Collections.singletonList(red.getUserId()), integral);
 
             // 更新数据库和缓存
             baseMapper.updateLiveRedConf(conf);
             decreaseRemainingLotsIfPossible(red.getRedId());
-            decreaseRemainingNumIfPossible(red.getRedId(),integral);
+//            decreaseRemainingNumIfPossible(red.getRedId(), integral);
 
             // 记录用户红包
             LiveUserRedRecord record = new LiveUserRedRecord();
@@ -200,10 +219,12 @@ public class LiveRedConfServiceImpl extends ServiceImpl<LiveRedConfMapper, LiveR
             //String msg = String.format("用户 %d 抢到了红包 %d,获得 %d 芳华币", userId, redId, integral);
             //WebSocketServer.notifyUsers(msg);
 
-            return R.ok();
-        } finally {
-            releaseLock(keyList, red.getUserId().toString());
+            return R.ok("恭喜您成功抢到"+ integral+"芳华币");
+        } catch (Exception e) {
+            e.printStackTrace();
+            log.error("抢红包异常:" + e.getMessage());
         }
+        return R.error("抢红包异常");
     }
 
     @Override
@@ -219,6 +240,16 @@ public class LiveRedConfServiceImpl extends ServiceImpl<LiveRedConfMapper, LiveR
         return "红包发放失败";
     }
 
+    @Override
+    public List<LiveRedConf> selectByLiveId(Long existLiveId) {
+        return baseMapper.selectByLiveId(existLiveId);
+    }
+
+    @Override
+    public List<LiveRedConf> selectActivedRed(Long liveId, String userId) {
+        return baseMapper.selectActivedRed(liveId, userId);
+    }
+
     // 初始化剩余数量
     public void initRemainingLots(Long redId, Integer totalLots) {
         String key = REDPACKET_REMAININGLOTS_KEY + redId;
@@ -230,13 +261,18 @@ public class LiveRedConfServiceImpl extends ServiceImpl<LiveRedConfMapper, LiveR
         redisCache.setCacheObject(key, redNum.toString());
     }
 
-    // 获取剩余数量
+    // 获取剩余红包数量
     public Integer getRemaining(Long redId) {
         String key = REDPACKET_REMAININGLOTS_KEY + redId;
-        String value = redisCache.getCacheObject(key);
-        return value == null ? 0 : Integer.parseInt(value);
+        Integer value = redisCache.getCacheObject(key);
+        return value == null ? 0 : value;
+    }
+    // 获取剩余红包数量
+    public void setRemaining(Long redId,Long totalLots) {
+        String key = REDPACKET_REMAININGLOTS_KEY + redId;
+        redisCache.setCacheObject(key,totalLots.intValue(),5,TimeUnit.MINUTES);
     }
-    // 获取剩余数量
+    // 获取剩余红包金额数量
     public Integer getRemainingNum(Long redId) {
         String key = REDPACKET_REMAININGNUM_KEY + redId;
         String value = redisCache.getCacheObject(key);
@@ -255,7 +291,7 @@ public class LiveRedConfServiceImpl extends ServiceImpl<LiveRedConfMapper, LiveR
     }
 
     public boolean tryLock(String lockKey, String clientId, long expireTime) {
-        Boolean success = redisCache.setIfAbsent(lockKey, clientId, expireTime, TimeUnit.SECONDS);
+        Boolean success = redisCache.setIfAbsent(lockKey + ":" + clientId, clientId, expireTime, TimeUnit.SECONDS);
         return Boolean.TRUE.equals(success);
     }
 

+ 93 - 0
fs-service/src/main/java/com/fs/live/service/impl/LiveRewardRecordServiceImpl.java

@@ -0,0 +1,93 @@
+package com.fs.live.service.impl;
+
+import java.util.List;
+import com.fs.common.utils.DateUtils;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.live.domain.LiveRewardRecord;
+import com.fs.live.mapper.LiveRewardRecordMapper;
+import com.fs.live.service.ILiveRewardRecordService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * 用户直播间奖励记录Service业务层处理
+ *
+ * @author fs
+ * @date 2025-08-27
+ */
+@Service
+public class LiveRewardRecordServiceImpl extends ServiceImpl<LiveRewardRecordMapper, LiveRewardRecord> implements ILiveRewardRecordService {
+
+    /**
+     * 查询用户直播间奖励记录
+     *
+     * @param id 用户直播间奖励记录主键
+     * @return 用户直播间奖励记录
+     */
+    @Override
+    public LiveRewardRecord selectLiveRewardRecordById(Long id)
+    {
+        return baseMapper.selectLiveRewardRecordById(id);
+    }
+
+    /**
+     * 查询用户直播间奖励记录列表
+     *
+     * @param liveRewardRecord 用户直播间奖励记录
+     * @return 用户直播间奖励记录
+     */
+    @Override
+    public List<LiveRewardRecord> selectLiveRewardRecordList(LiveRewardRecord liveRewardRecord)
+    {
+        return baseMapper.selectLiveRewardRecordList(liveRewardRecord);
+    }
+
+    /**
+     * 新增用户直播间奖励记录
+     *
+     * @param liveRewardRecord 用户直播间奖励记录
+     * @return 结果
+     */
+    @Override
+    public int insertLiveRewardRecord(LiveRewardRecord liveRewardRecord)
+    {
+        liveRewardRecord.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insertLiveRewardRecord(liveRewardRecord);
+    }
+
+    /**
+     * 修改用户直播间奖励记录
+     *
+     * @param liveRewardRecord 用户直播间奖励记录
+     * @return 结果
+     */
+    @Override
+    public int updateLiveRewardRecord(LiveRewardRecord liveRewardRecord)
+    {
+        return baseMapper.updateLiveRewardRecord(liveRewardRecord);
+    }
+
+    /**
+     * 批量删除用户直播间奖励记录
+     *
+     * @param ids 需要删除的用户直播间奖励记录主键
+     * @return 结果
+     */
+    @Override
+    public int deleteLiveRewardRecordByIds(Long[] ids)
+    {
+        return baseMapper.deleteLiveRewardRecordByIds(ids);
+    }
+
+    /**
+     * 删除用户直播间奖励记录信息
+     *
+     * @param id 用户直播间奖励记录主键
+     * @return 结果
+     */
+    @Override
+    public int deleteLiveRewardRecordById(Long id)
+    {
+        return baseMapper.deleteLiveRewardRecordById(id);
+    }
+}

+ 169 - 11
fs-service/src/main/java/com/fs/live/service/impl/LiveServiceImpl.java

@@ -5,23 +5,24 @@ import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.fs.ad.yk.utils.Md5Util;
 import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
 import com.fs.common.exception.base.BaseException;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.bean.BeanUtils;
 import com.fs.course.domain.FsUserTalent;
 import com.fs.course.service.IFsUserTalentService;
-import com.fs.live.domain.Live;
-import com.fs.live.domain.LiveData;
-import com.fs.live.domain.LiveVideo;
-import com.fs.live.mapper.LiveMapper;
-import com.fs.live.service.ILiveDataService;
-import com.fs.live.service.ILiveService;
-import com.fs.live.service.ILiveVideoService;
+import com.fs.live.domain.*;
+import com.fs.live.mapper.*;
+import com.fs.live.service.*;
 import com.fs.live.utils.ProcessManager;
 import com.fs.live.vo.LiveListVo;
+import com.fs.live.vo.LiveLotteryConfVo;
+import com.fs.live.vo.LiveLotteryProductListVo;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.service.ISysConfigService;
 import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import okhttp3.FormBody;
 import okhttp3.OkHttpClient;
 import okhttp3.Request;
@@ -29,11 +30,13 @@ import okhttp3.Response;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import com.fs.aicall.utils.Md5Utils;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.Base64Utils;
 
 import java.io.IOException;
 import java.time.LocalDateTime;
 import java.util.*;
+import java.util.stream.Collectors;
 
 /**
  * 直播Service业务层处理
@@ -43,6 +46,7 @@ import java.util.*;
  */
 @Service
 @AllArgsConstructor
+@Slf4j
 public class LiveServiceImpl extends ServiceImpl<LiveMapper, Live> implements ILiveService
 {
     private final LiveMapper liveMapper;
@@ -58,6 +62,22 @@ public class LiveServiceImpl extends ServiceImpl<LiveMapper, Live> implements IL
     @Autowired
     private ProcessManager processManager;
 
+    @Autowired
+    private ILiveGoodsService liveGoodsService;
+
+    @Autowired
+    private ILiveRedConfService liveRedConfService;
+
+    @Autowired
+    private ILiveLotteryConfService liveLotteryConfService;
+
+    @Autowired
+    private ILiveAutoTaskService liveAutoTaskService;
+    @Autowired
+    private LiveLotteryProductConfMapper liveLotteryProductConfMapper;
+    @Autowired
+    private RedisCache redisCache;
+
     /**
      * 查询直播
      *
@@ -76,6 +96,23 @@ public class LiveServiceImpl extends ServiceImpl<LiveMapper, Live> implements IL
         return byId;
     }
 
+    @Override
+    public R currentActivities(Long liveId,String userId) {
+        R r = liveGoodsService.showGoods(liveId);
+        Object data = r.get("data");
+        List<LiveRedConf> liveRedConfs = liveRedConfService.selectActivedRed(liveId, userId);
+        List<LiveLotteryConfVo> liveLotteryConfs = liveLotteryConfService.selectActivedLottery(liveId, userId);
+        List<Long> lotteryIds = liveLotteryConfs.stream().map(LiveLotteryConfVo::getLotteryId).collect(Collectors.toList());
+        if (!lotteryIds.isEmpty()) {
+            List<LiveLotteryProductListVo> products = liveLotteryProductConfMapper.selectLiveLotteryProductConfByLotteryIds(lotteryIds);
+            for (LiveLotteryConfVo liveLotteryConf : liveLotteryConfs) {
+                liveLotteryConf.setProducts(products.stream().filter(product -> product.getLotteryId().equals(liveLotteryConf.getLotteryId())).collect(Collectors.toList()));
+            }
+        }
+
+        return R.ok().put("red", liveRedConfs).put("lottery", liveLotteryConfs).put("goods", data);
+    }
+
     /**
      * 查询企业直播
      * @param liveId            直播ID
@@ -147,6 +184,14 @@ public class LiveServiceImpl extends ServiceImpl<LiveMapper, Live> implements IL
         //新增直播数据表
         LiveData liveData = new LiveData();
         liveData.setLiveId(live.getLiveId());
+        liveData.setPageViews(0L);
+        liveData.setUniqueVisitors(0L);
+        liveData.setTotalViews(0L);
+        liveData.setUniqueViewers(0L);
+        liveData.setPeakConcurrentViewers(0L);
+        liveData.setLikes(0L);
+        liveData.setFavourite_num(0L);
+        liveData.setFollow_num(0L);
         liveDataService.insertLiveData(liveData);
         return save ? 1 : 0;
     }
@@ -168,7 +213,9 @@ public class LiveServiceImpl extends ServiceImpl<LiveMapper, Live> implements IL
             liveVideo.setDuration(live.getDuration());
             liveVideoService.updateById(liveVideo);
         }
-        return liveMapper.updateLive(live);
+        int result = liveMapper.updateLive(live);
+        liveAutoTaskService.recalcLiveAutoTask(live.getLiveId());
+        return result;
     }
 
     /**
@@ -416,16 +463,18 @@ public class LiveServiceImpl extends ServiceImpl<LiveMapper, Live> implements IL
             return R.error("直播已下架");
         }
         String rtmpPushUrl = generateRtmpPushUrl("rtmp://200149.push.tlivecloud.com", "live", exist.getLiveId().toString());
-        String hlvPlayUrl = generateHlvPlayUrl("http://live.test.ifeiyu100.com", "live", exist.getLiveId().toString());
+        String hlvPlayUrl = generateHlvPlayUrl("https://live.test.ifeiyu100.com", "live", exist.getLiveId().toString());
+        Date now = new Date();
         exist.setRtmpUrl(rtmpPushUrl);
         exist.setFlvHlsUrl(hlvPlayUrl);
 
         exist.setStatus(2);
-        exist.setUpdateTime(new Date());
+        exist.setUpdateTime(now);
         exist.setFinishTime( null);
         exist.setStartTime(LocalDateTime.now());
         liveMapper.updateLive(exist);
 
+
         return R.ok();
     }
 
@@ -441,6 +490,115 @@ public class LiveServiceImpl extends ServiceImpl<LiveMapper, Live> implements IL
         return R.ok();
     }
 
+    @Override
+    @Transactional
+    public R copyLive(Live live) {
+        Long existLiveId = live.getLiveId();
+        Date now = new Date();
+        Live exist = liveMapper.selectLiveByLiveId(existLiveId);
+        if (exist == null) {
+            return R.error("直播间不存在");
+        }
+        // 复制直播间, 创建 livedata
+        // 身份认证
+        Live liveEntity = new Live();
+        BeanUtils.copyBeanProp(liveEntity, exist);
+        liveEntity.setLiveId(null);
+        liveEntity.setCompanyId(live.getCompanyId());
+        liveEntity.setCompanyUserId(live.getCompanyUserId());
+        liveEntity.setRtmpUrl( null);
+        liveEntity.setFlvHlsUrl( null);
+        liveEntity.setFinishTime( null);
+        liveEntity.setIsAudit( 0);
+        liveEntity.setStatus( 1);
+        liveEntity.setCreateTime( now);
+        liveMapper.insertLive(liveEntity);
+        if(liveEntity.getLiveId() == null) throw new RuntimeException("插入直播间异常");
+        Long newLiveId = liveEntity.getLiveId();
+        LiveData liveData = new LiveData();
+        liveData.setLiveId(newLiveId);
+        liveData.setPageViews(0L);
+        liveData.setUniqueVisitors(0L);
+        liveData.setTotalViews(0L);
+        liveData.setUniqueViewers(0L);
+        liveData.setPeakConcurrentViewers(0L);
+        liveData.setLikes(0L);
+        liveData.setFavourite_num(0L);
+        liveData.setFollow_num(0L);
+        liveDataService.insertLiveData(liveData);
+        // 直播视频
+        LiveVideo liveVideo = liveVideoService.selectLiveVideoByLiveId(existLiveId);
+        if (!Objects.isNull(liveVideo)) {
+            LiveVideo videoEntity = new LiveVideo();
+            BeanUtils.copyBeanProp(videoEntity, liveVideo);
+            videoEntity.setVideoId(null);
+            videoEntity.setLiveId(newLiveId);
+            videoEntity.setCreateTime(now);
+            liveVideoService.insertLiveVideo(videoEntity);
+        }
+        //直播间红包配置
+        List<LiveRedConf> liveRedConfs = liveRedConfService.selectByLiveId(existLiveId);
+        if (!liveRedConfs.isEmpty()) {
+            LiveRedConf liveRedConfEntity = new LiveRedConf();
+            for (LiveRedConf liveRedConf : liveRedConfs) {
+                BeanUtils.copyBeanProp(liveRedConfEntity, liveRedConf);
+                liveRedConfEntity.setRedId(null);
+                liveRedConfEntity.setLiveId(newLiveId);
+                liveRedConfEntity.setRedStatus(0L);
+                liveRedConfEntity.setCreateTime(now);
+                liveRedConfService.insertLiveRedConf(liveRedConfEntity);
+            }
+        }
+        // 直播间礼物配置
+        List<LiveLotteryConf> liveLotteryConfs = liveLotteryConfService.selectByLiveId(existLiveId);
+        if (!liveLotteryConfs.isEmpty()) {
+            LiveLotteryConf liveLotteryConfEntity = new LiveLotteryConf();
+            for (LiveLotteryConf liveLotteryConf : liveLotteryConfs) {
+                BeanUtils.copyBeanProp(liveLotteryConfEntity, liveLotteryConf);
+                liveLotteryConfEntity.setLotteryId(null);
+                liveLotteryConfEntity.setLiveId(newLiveId);
+                liveLotteryConfEntity.setLotteryStatus(String.valueOf(0));
+                liveLotteryConfEntity.setCreateTime(now);
+                liveLotteryConfService.insertLiveLotteryConf(liveLotteryConfEntity);
+            }
+        }
+        // 直播间商品
+        List<LiveGoods> goodsList = liveGoodsService.selectByLiveId(existLiveId);
+        if (!goodsList.isEmpty()) {
+            LiveGoods liveGoodsEntity = new LiveGoods();
+            for (LiveGoods liveGoods : goodsList) {
+                BeanUtils.copyBeanProp(liveGoodsEntity, liveGoods);
+                liveGoodsEntity.setGoodsId(null);
+                liveGoodsEntity.setLiveId(newLiveId);
+                liveGoodsEntity.setCreateTime(now);
+                liveGoodsEntity.setCompanyId(live.getCompanyId());
+                liveGoodsEntity.setCompanyUserId(live.getCompanyUserId());
+                liveGoodsService.insertLiveGoods(liveGoodsEntity);
+            }
+        }
+        // 运营自动化
+        List<LiveAutoTask> liveAutoTasksList = liveAutoTaskService.selectLiveAutoTaskByLiveId(existLiveId);
+        if (!liveAutoTasksList.isEmpty()) {
+            LiveAutoTask liveAutoTaskEntity = new LiveAutoTask();
+            for (LiveAutoTask liveAutoTask : liveAutoTasksList) {
+                BeanUtils.copyBeanProp(liveAutoTaskEntity, liveAutoTask);
+                liveAutoTaskEntity.setId(null);
+                liveAutoTaskEntity.setLiveId(newLiveId);
+                liveAutoTaskEntity.setCreateTime(now);
+                liveAutoTaskEntity.setUpdateTime(now);
+                liveAutoTaskEntity.setFinishStatus(0L);
+                liveAutoTaskService.copyInsertLiveAutoTask(liveAutoTaskEntity);
+            }
+        }
+
+
+
+
+        return R.ok("复制成功");
+    }
+
+
+
     /**
      * 构建FFmpeg推流命令
      */
@@ -539,7 +697,7 @@ public class LiveServiceImpl extends ServiceImpl<LiveMapper, Live> implements IL
                 app +
                 "/" +
                 liveId +
-                ".m3u8?" +
+                ".flv?" +
                 "txSecret=" +
                 Md5Util.MD5("NiMfMYG5HQxZQ5bk88ri" + liveId + to16Hex(now)) +
                 "&" +

+ 21 - 6
fs-service/src/main/java/com/fs/live/service/impl/LiveWatchUserServiceImpl.java

@@ -4,6 +4,8 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.DateUtils;
+import com.fs.his.domain.FsUser;
+import com.fs.his.service.IFsUserService;
 import com.fs.live.domain.LiveWatchUser;
 import com.fs.live.mapper.LiveWatchUserMapper;
 import com.fs.live.service.ILiveWatchUserService;
@@ -11,10 +13,7 @@ import com.fs.live.vo.LiveWatchUserVO;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
+import java.util.*;
 
 /**
  * 直播间观看用户Service业务层处理
@@ -27,6 +26,9 @@ public class LiveWatchUserServiceImpl extends ServiceImpl<LiveWatchUserMapper, L
 
     @Autowired
     private RedisCache redisCache;
+    @Autowired
+    private IFsUserService fsUserService;
+
 
     /**
      * 查询直播间观看用户
@@ -125,8 +127,7 @@ public class LiveWatchUserServiceImpl extends ServiceImpl<LiveWatchUserMapper, L
         LiveWatchUser liveWatchUser = getByLiveIdAndUserId(liveId, userId);
         if(liveWatchUser != null) {
             liveWatchUser.setUpdateTime(DateUtils.getNowDate());
-            liveWatchUser.setOnline(1);
-            liveWatchUser.setUpdateTime(DateUtils.getNowDate());
+            liveWatchUser.setOnline(0);
         }else{
             liveWatchUser = new LiveWatchUser();
             liveWatchUser.setLiveId(liveId);
@@ -134,6 +135,7 @@ public class LiveWatchUserServiceImpl extends ServiceImpl<LiveWatchUserMapper, L
             liveWatchUser.setMsgStatus(0);
             liveWatchUser.setOnline(0);
             liveWatchUser.setCreateTime(DateUtils.getNowDate());
+            liveWatchUser.setUpdateTime(DateUtils.getNowDate());
         }
         super.saveOrUpdate(liveWatchUser);
         return liveWatchUser;
@@ -190,4 +192,17 @@ public class LiveWatchUserServiceImpl extends ServiceImpl<LiveWatchUserMapper, L
         return baseMapper.selectOnlineUserList(param);
     }
 
+    @Override
+    public List<LiveWatchUser> checkOnlineNoRewardUser(Long liveId, Date now) {
+        return baseMapper.checkOnlineNoRewardUser(liveId,now);
+    }
+
+    @Override
+    public int blockUser(Long userId) {
+        FsUser fsUser = new FsUser();
+        fsUser.setUserId(userId);
+        fsUser.setStatus(0);
+        return fsUserService.updateFsUser(fsUser);
+    }
+
 }

+ 1 - 0
fs-service/src/main/java/com/fs/live/vo/LiveGoodsListVo.java

@@ -14,6 +14,7 @@ public class LiveGoodsListVo {
     List<Long> goodsIds;
     Integer status;
     Integer liveId;
+    Boolean isShow;
 
 
     /**

+ 1 - 0
fs-service/src/main/java/com/fs/live/vo/LiveGoodsVo.java

@@ -22,4 +22,5 @@ public class LiveGoodsVo {
 
     // 是否收藏
     private Boolean isFavorite;
+    private Boolean isShow;
 }

+ 52 - 0
fs-service/src/main/java/com/fs/live/vo/LiveLotteryConfVo.java

@@ -0,0 +1,52 @@
+package com.fs.live.vo;
+
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import com.fs.his.domain.FsStoreProduct;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+/**
+ * 直播抽奖配置对象 live_lottery_conf
+ *
+ * @author fs
+ * @date 2025-07-17
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class LiveLotteryConfVo extends BaseEntity{
+
+    /** 抽奖ID */
+    private Long lotteryId;
+
+    /** 直播间ID */
+    @Excel(name = "直播间ID")
+    private Long liveId;
+
+    /** 抽奖状态*/
+    @Excel(name = "抽奖状态 0:未开始 1:进行中 2:已结束")
+    private String lotteryStatus;
+
+    /** 参与抽奖方式 0:在线观众参与 1:关注参与 2:送礼参与 3:下单参与 */
+    @Excel(name = "参与抽奖方式 0:在线观众参与 1:关注参与 2:送礼参与 3:下单参与")
+    private Long require;
+
+    /** 参与抽奖方式 2/3对应的配置,2:送礼数量,以最低档位礼物为单位 3:指定商品Id及数量,|竖线分割 */
+    @Excel(name = "参与抽奖方式 2/3对应的配置,礼物/商品Id竖线分割数量")
+    private String requireConf;
+
+    /** 持续时间 单位:分 */
+    @Excel(name = "持续时间 单位:分")
+    private Long duration;
+
+    /** 描述 */
+    @Excel(name = "描述")
+    private String desc;
+
+    private List<LiveLotteryProductListVo> products;
+
+
+
+}

+ 47 - 0
fs-service/src/main/java/com/fs/live/vo/LiveLotteryProductListVo.java

@@ -0,0 +1,47 @@
+package com.fs.live.vo;
+
+import com.fs.common.core.domain.BaseEntity;
+import com.fs.live.param.LiveLotteryProduct;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 直播抽奖配置对象 live_lottery_conf
+ *
+ * @author fs
+ * @date 2025-07-17
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class LiveLotteryProductListVo extends BaseEntity{
+
+    /** ID */
+    private Long lotteryId;
+
+    /** 直播间ID */
+    private Long liveId;
+
+    /** 商品ID */
+    private Long productId;
+
+    /** 奖品等级 */
+    private Long prizeLevel;
+
+    /** 单次中奖商品数量 */
+    private Long perLotteryNum;
+
+    /** 奖励总份数 */
+    private Long totalLots;
+
+    private String imgUrl;
+    private String productName;
+    private BigDecimal price;
+    private BigDecimal otPrice;
+    private Integer sales;
+    private Integer stock;
+
+
+}

+ 114 - 0
fs-service/src/main/resources/mapper/live/LiveAutoTaskMapper.xml

@@ -0,0 +1,114 @@
+<?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.live.mapper.LiveAutoTaskMapper">
+
+    <resultMap type="LiveAutoTask" id="LiveAutoTaskResult">
+        <result property="id"    column="id"    />
+        <result property="liveId"    column="live_id"    />
+        <result property="taskName"    column="task_name"    />
+        <result property="taskType"    column="task_type"    />
+        <result property="triggerType"    column="trigger_type"    />
+        <result property="triggerValue"    column="trigger_value"    />
+        <result property="absValue"    column="abs_value"    />
+        <result property="content"    column="content"    />
+        <result property="status"    column="status"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateTime"    column="update_time"    />
+        <result property="finishStatus"    column="finish_status"    />
+    </resultMap>
+
+    <sql id="selectLiveAutoTaskVo">
+        select id, live_id, task_name, task_type, trigger_type, trigger_value, content, status, create_time, update_time,abs_value,finish_status from live_auto_task
+    </sql>
+
+    <select id="selectLiveAutoTaskList" parameterType="LiveAutoTask" resultMap="LiveAutoTaskResult">
+        <include refid="selectLiveAutoTaskVo"/>
+        <where>
+            <if test="liveId != null "> and live_id = #{liveId}</if>
+            <if test="taskName != null  and taskName != ''"> and task_name like concat('%', #{taskName}, '%')</if>
+            <if test="taskType != null "> and task_type = #{taskType}</if>
+            <if test="triggerType != null "> and trigger_type = #{triggerType}</if>
+            <if test="triggerValue != null"> and trigger_value = #{triggerValue}</if>
+            <if test="absValue != null"> and abs_value = #{absValue}</if>
+            <if test="content != null  and content != ''"> and content = #{content}</if>
+            <if test="status != null "> and status = #{status}</if>
+            <if test="createTime != null "> and create_time = #{createTime}</if>
+            <if test="updateTime != null "> and update_time = #{updateTime}</if>
+            <if test="finishStatus != null "> and finish_status = #{finishStatus}</if>
+        </where>
+    </select>
+
+    <select id="selectLiveAutoTaskById" parameterType="Long" resultMap="LiveAutoTaskResult">
+        <include refid="selectLiveAutoTaskVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertLiveAutoTask" parameterType="LiveAutoTask" useGeneratedKeys="true" keyProperty="id">
+        insert into live_auto_task
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="liveId != null">live_id,</if>
+            <if test="taskName != null and taskName != ''">task_name,</if>
+            <if test="taskType != null">task_type,</if>
+            <if test="triggerType != null">trigger_type,</if>
+            <if test="triggerValue != null">trigger_value,</if>
+            <if test="absValue != null">abs_value,</if>
+            <if test="content != null">content,</if>
+            <if test="status != null">status,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateTime != null">update_time,</if>
+            <if test="finishStatus != null">finish_status,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="liveId != null">#{liveId},</if>
+            <if test="taskName != null and taskName != ''">#{taskName},</if>
+            <if test="taskType != null">#{taskType},</if>
+            <if test="triggerType != null">#{triggerType},</if>
+            <if test="triggerValue != null">#{triggerValue},</if>
+            <if test="absValue != null">#{absValue},</if>
+            <if test="content != null">#{content},</if>
+            <if test="status != null">#{status},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+            <if test="finishStatus != null">#{finishStatus},</if>
+         </trim>
+    </insert>
+
+    <update id="updateLiveAutoTask" parameterType="LiveAutoTask">
+        update live_auto_task
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="liveId != null">live_id = #{liveId},</if>
+            <if test="taskName != null and taskName != ''">task_name = #{taskName},</if>
+            <if test="taskType != null">task_type = #{taskType},</if>
+            <if test="triggerType != null">trigger_type = #{triggerType},</if>
+            <if test="triggerValue != null">trigger_value = #{triggerValue},</if>
+            <if test="absValue != null">abs_value = #{absValue},</if>
+            <if test="content != null">content = #{content},</if>
+            <if test="status != null">status = #{status},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+            <if test="finishStatus != null">finish_status = #{finishStatus},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteLiveAutoTaskById" parameterType="Long">
+        delete from live_auto_task where id = #{id}
+    </delete>
+
+    <delete id="deleteLiveAutoTaskByIds" parameterType="String">
+        delete from live_auto_task where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+    <select id="selectLiveAutoTaskByLiveIds" resultMap="LiveAutoTaskResult">
+        select * from live_auto_task where live_id in
+        <foreach item="id" collection="liveIdLists" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+        and status=1 and finish_status = 0 and abs_value>#{now} order by abs_value
+    </select>
+</mapper>

+ 70 - 14
fs-service/src/main/resources/mapper/live/LiveGoodsMapper.xml

@@ -19,10 +19,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="status"    column="status"    />
         <result property="stock"    column="stock"    />
         <result property="sort"    column="sort"    />
+        <result property="isShow"    column="is_show"    />
     </resultMap>
 
     <sql id="selectLiveGoodsVo">
-        select goods_id, live_id, company_id, company_user_id, store_id, product_id, create_time, create_by, update_by, update_time, remark, status, stock, sort from live_goods
+        select goods_id, live_id, company_id, company_user_id, store_id, product_id, create_time, create_by, update_by, update_time, remark, status, stock, sort,case when is_show = 1 then true else false end as is_show from live_goods
     </sql>
 
     <select id="selectLiveGoodsList" parameterType="LiveGoods" resultMap="LiveGoodsResult">
@@ -36,6 +37,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="status != null "> and status = #{status}</if>
             <if test="stock != null "> and stock = #{stock}</if>
             <if test="sort != null "> and sort = #{sort}</if>
+            <if test="isShow != null "> and is_show = #{isShow}</if>
         </where>
     </select>
 
@@ -60,6 +62,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="status != null">status,</if>
             <if test="stock != null">stock,</if>
             <if test="sort != null">sort,</if>
+            <if test="isShow != null">is_show,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="liveId != null">#{liveId},</if>
@@ -75,6 +78,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="status != null">#{status},</if>
             <if test="stock != null">#{stock},</if>
             <if test="sort != null">#{sort},</if>
+            <if test="isShow != null">#{isShow},</if>
          </trim>
     </insert>
 
@@ -94,6 +98,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="status != null">status = #{status},</if>
             <if test="stock != null">stock = #{stock},</if>
             <if test="sort != null">sort = #{sort},</if>
+            <if test="isShow != null">is_show = #{isShow},</if>
         </trim>
         where goods_id = #{goodsId}
     </update>
@@ -116,7 +121,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         INSERT INTO live_goods (
         live_id, company_id, company_user_id, store_id,
         product_id, create_time, create_by, update_by,
-        update_time, remark, status, stock, sort
+        update_time, remark, status, stock, sort, is_show
         ) VALUES
         <foreach collection="liveGoodsList" item="item" separator=",">
             (
@@ -146,7 +151,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{item.remark},
             #{item.status},
             #{item.stock},
-            #{item.sort}
+            #{item.sort},
+            #{item.isShow}
             )
         </foreach>
     </insert>
@@ -159,7 +165,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 
     <select id="selectProductListByLiveId" parameterType="LiveGoods" resultType="com.fs.live.vo.LiveGoodsVo">
 
-        select lg.goods_id,sp.img_url,sp.product_name,sp.price,sp.stock,sp.sales,lg.status,sp.product_id,sp.ot_price,sp.store_id
+        select lg.goods_id,sp.img_url,sp.product_name,sp.price,sp.stock,sp.sales,lg.status,sp.product_id,sp.ot_price,sp.store_id,case when lg.is_show = 1 then true else false end as is_show
         from live_goods lg
         left join fs_store_product sp
         ON lg.store_id = sp.store_id AND lg.product_id = sp.product_id
@@ -173,14 +179,60 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="status != null "> and status = #{status}</if>
             <if test="stock != null "> and stock = #{stock}</if>
             <if test="sort != null "> and sort = #{sort}</if>
+            <if test="isShow != null "> and is_show = #{isShow}</if>
             <if test="keywords != null and keywords != ''">
                 and sp.product_name like concat('%',#{keywords},'%')
             </if>
         </where>
     </select>
 
+    <select id="selectProductListByLiveIdAll" parameterType="LiveGoods" resultType="com.fs.live.vo.LiveGoodsVo">
+
+        select lg.goods_id,sp.img_url,sp.product_name,sp.price,lg.stock,sp.sales,lg.status,sp.product_id,sp.ot_price,sp.store_id, case when lg.is_show = 1 then true else false end as is_show
+        from live_goods lg
+        left join fs_store_product sp
+        ON lg.store_id = sp.store_id AND lg.product_id = sp.product_id
+
+        <where>
+            live_id = #{liveId}
+            <if test="storeId != null "> and store_id = #{storeId}</if>
+            <if test="productId != null "> and product_id = #{productId}</if>
+            <if test="status != null "> and status = #{status}</if>
+            <if test="stock != null "> and stock = #{stock}</if>
+            <if test="sort != null "> and sort = #{sort}</if>
+            <if test="isShow != null "> and is_show = #{isShow}</if>
+            <if test="keywords != null and keywords != ''">
+                and sp.product_name like concat('%',#{keywords},'%')
+            </if>
+        </where>
+    </select>
+
+    <select id="selectLiveGoodsVoByGoodsId"  resultType="com.fs.live.vo.LiveGoodsVo">
+
+        select lg.goods_id,sp.img_url,sp.product_name,sp.price,lg.stock,sp.sales,lg.status,sp.product_id,sp.ot_price,sp.store_id, case when lg.is_show = 1 then true else false end as is_show
+        from live_goods lg
+        left join fs_store_product sp
+        ON lg.store_id = sp.store_id AND lg.product_id = sp.product_id
+
+        <where>
+            goods_id = #{goodsId}
+        </where>
+    </select>
+
+    <select id="showGoods"  resultType="com.fs.live.vo.LiveGoodsVo">
+
+        select lg.goods_id,sp.img_url,sp.product_name,sp.price,lg.stock,sp.sales,lg.status,sp.product_id,sp.ot_price,sp.store_id, case when lg.is_show = 1 then true else false end as is_show
+        from live_goods lg
+        left join fs_store_product sp
+        ON lg.store_id = sp.store_id AND lg.product_id = sp.product_id
+        <where>
+            lg.live_id = #{liveId} and lg.is_show = 1
+        </where>
+        limit 1
+    </select>
+
     <update id="updateBatchList" parameterType="com.fs.live.vo.LiveGoodsListVo">
-        update live_goods set status = #{listVo.status} where company_id = #{listVo.companyId} and company_user_id = #{listVo.companyUserId} and goods_id in
+        update live_goods set status = #{listVo.status} where goods_id in
         <foreach item="goodsId" collection="listVo.goodsIds" open="(" separator="," close=")">
             #{goodsId}
         </foreach>
@@ -188,7 +240,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 
     <delete id="deleteBatchList" parameterType="com.fs.live.vo.LiveGoodsListVo">
         delete from live_goods
-        WHERE company_id = #{listVo.companyId} and company_user_id = #{listVo.companyUserId} and goods_id IN
+        WHERE goods_id IN
         <foreach item="goodsId" collection="listVo.goodsIds" open="(" separator="," close=")">
             #{goodsId}
         </foreach>
@@ -196,11 +248,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 
     <select id="selectProductListByOrder" parameterType="com.fs.live.domain.LiveOrder" resultType="com.fs.live.vo.LiveGoodsVo">
 
-        select lg.goods_id,sp.img_url,sp.product_name,sp.price,sp.stock,sp.sales,lg.status,sp.product_id,sp.ot_price, sp.store_id
+        select sp.img_url,sp.product_name,sp.price,lg.stock,sp.sales,sp.product_id,sp.ot_price, sp.store_id
         from fs_store_product sp
-        INNER JOIN live_goods lg
-        ON lg.store_id = sp.store_id AND lg.product_id = sp.product_id
-
         <where>
             sp.product_id = #{productId}
         </where>
@@ -219,16 +268,23 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <if test="params.liveId != null "> and g.live_id = #{params.liveId}</if>
     </select>
 
-    <update id="updateBatchList" parameterType="com.fs.live.vo.LiveGoodsListVo">
-        update live_goods set status = #{listVo.status} where live_id = #{listVo.liveId} and goods_id in
+    <update id="handleShelfOrUnAdmin" parameterType="com.fs.live.vo.LiveGoodsListVo">
+        update live_goods set status = #{listVo.status}  where goods_id in
         <foreach item="goodsId" collection="listVo.goodsIds" open="(" separator="," close=")">
             #{goodsId}
         </foreach>
     </update>
 
-    <delete id="deleteBatchList" parameterType="com.fs.live.vo.LiveGoodsListVo">
+    <update id="handleIsShowChange" parameterType="com.fs.live.vo.LiveGoodsListVo">
+        update live_goods set is_show = #{listVo.isShow} and status = 1 where goods_id in
+        <foreach item="goodsId" collection="listVo.goodsIds" open="(" separator="," close=")">
+            #{goodsId}
+        </foreach>
+    </update>
+
+    <delete id="handleDeleteSelectedAdmin" parameterType="com.fs.live.vo.LiveGoodsListVo">
         delete from live_goods
-        WHERE live_id = #{listVo.liveId} and goods_id IN
+        WHERE goods_id IN
         <foreach item="goodsId" collection="listVo.goodsIds" open="(" separator="," close=")">
             #{goodsId}
         </foreach>

+ 12 - 0
fs-service/src/main/resources/mapper/live/LiveLotteryProductConfMapper.xml

@@ -127,4 +127,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <delete id="deleteLiveLotteryProductConfByLotteryId" parameterType="Long" >
         delete from live_lottery_product_conf where lottery_id = #{lotteryId}
     </delete>
+
+    <select id="selectLiveLotteryProductConfByLotteryIds"  resultType="com.fs.live.vo.LiveLotteryProductListVo">
+        select llpc.lottery_id,llpc.live_id,llpc.product_id,llpc.prize_level,llpc.per_lottery_num,llpc.total_lots,
+               fs.img_url,fs.product_name,fs.price,fs.ot_price,fs.sales,fs.stock
+               from live_lottery_product_conf llpc
+            left join live_goods lg on lg.goods_id = llpc.product_id
+            left join fs_store_product fs on fs.product_id = lg.product_id
+            where llpc.lottery_id in
+            <foreach item="id" collection="lotteryIds" separator="," open="(" close=")">
+                #{id}
+            </foreach>
+    </select>
 </mapper>

+ 5 - 1
fs-service/src/main/resources/mapper/live/LiveLotteryRegistrationMapper.xml

@@ -8,6 +8,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="registrationId"    column="registration_id"    />
         <result property="liveId"    column="live_id"    />
         <result property="userId"    column="user_id"    />
+        <result property="lotteryId"    column="lottery_id"    />
         <result property="isWin"    column="is_win"    />
         <result property="rizeLevel"    column="rize_level"    />
         <result property="createTime"    column="create_time"    />
@@ -17,7 +18,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </resultMap>
 
     <sql id="selectLiveLotteryRegistrationVo">
-        select registration_id, live_id, user_id, is_win, rize_level, create_time, update_time, create_by, update_by from live_lottery_registration
+        select registration_id, live_id, user_id, lottery_id, is_win, rize_level, create_time, update_time, create_by, update_by from live_lottery_registration
     </sql>
 
     <select id="selectLiveLotteryRegistrationList" parameterType="LiveLotteryRegistration" resultMap="LiveLotteryRegistrationResult">
@@ -36,6 +37,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <trim prefix="(" suffix=")" suffixOverrides=",">
             <if test="liveId != null">live_id,</if>
             <if test="userId != null">user_id,</if>
+            <if test="lotteryId != null">lottery_id,</if>
             <if test="isWin != null">is_win,</if>
             <if test="rizeLevel != null">rize_level,</if>
             <if test="createTime != null">create_time,</if>
@@ -46,6 +48,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="liveId != null">#{liveId},</if>
             <if test="userId != null">#{userId},</if>
+            <if test="lotteryId != null">#{lotteryId},</if>
             <if test="isWin != null">#{isWin},</if>
             <if test="rizeLevel != null">#{rizeLevel},</if>
             <if test="createTime != null">#{createTime},</if>
@@ -60,6 +63,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <trim prefix="SET" suffixOverrides=",">
             <if test="liveId != null">live_id = #{liveId},</if>
             <if test="userId != null">user_id = #{userId},</if>
+            <if test="lotteryId != null">lottery_id = #{lotteryId},</if>
             <if test="isWin != null">is_win = #{isWin},</if>
             <if test="rizeLevel != null">rize_level = #{rizeLevel},</if>
             <if test="createTime != null">create_time = #{createTime},</if>

+ 18 - 2
fs-service/src/main/resources/mapper/live/LiveMapper.xml

@@ -30,12 +30,26 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="remark"    column="remark"    />
         <result property="configJson"    column="config_json"    />
         <result property="isAudit"    column="is_audit"    />
+        <result property="idCardUrl"    column="id_card_url"    />
     </resultMap>
 
     <sql id="selectLiveVo">
-        select live_id, company_id, company_user_id,talent_id, live_name, is_audit, live_desc, show_type, status, anchor_id, live_type, start_time, finish_time, live_img_url, live_config, id_card_url, is_show, is_del, qw_qr_code, rtmp_url, flv_hls_url, create_time, create_by, update_by, update_time, remark,config_json from live
+        select live_id, company_id, company_user_id,talent_id, live_name, is_audit, live_desc, show_type, status, anchor_id, live_type, start_time, finish_time,
+               live_img_url, live_config, id_card_url, is_show, is_del, qw_qr_code, rtmp_url, flv_hls_url,
+               create_time, create_by, update_by, update_time, remark,config_json from live
     </sql>
 
+    <select id="liveList" parameterType="Live" resultMap="LiveResult">
+        select a.live_id, a.company_id, a.company_user_id,talent_id, a.live_name, a.is_audit, a.live_desc, a.show_type, a.status, a.anchor_id,
+        a.live_type, a.start_time, a.finish_time, a.live_img_url, a.live_config, a.id_card_url, a.is_show, a.is_del, a.qw_qr_code, a.rtmp_url,
+        a.flv_hls_url, a.create_time, a.create_by, a.update_by, a.update_time, a.remark,config_json, b.video_url from live
+        a
+        left join live_video b on a.live_id = b.live_id
+        where 1=1 and is_del = 0 and is_show = 1 and status = 2
+
+        order by create_time desc
+    </select>
+
     <select id="selectLiveList" parameterType="Live" resultMap="LiveResult">
         select a.live_id, a.company_id, a.company_user_id,talent_id, a.live_name, a.is_audit, a.live_desc, a.show_type, a.status, a.anchor_id,
                a.live_type, a.start_time, a.finish_time, a.live_img_url, a.live_config, a.id_card_url, a.is_show, a.is_del, a.qw_qr_code, a.rtmp_url,
@@ -115,6 +129,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="configJson != null">config_json,</if>
             <if test="flvHlsUrl != null">flv_hls_url,</if>
             <if test="isAudit != null">is_audit,</if>
+            <if test="idCardUrl != null">id_card_url,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="companyId != null">#{companyId},</if>
@@ -142,6 +157,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="configJson != null">#{configJson},</if>
             <if test="flvHlsUrl != null">#{flvHlsUrl},</if>
             <if test="isAudit != null">#{isAudit},</if>
+            <if test="idCardUrl != null">#{idCardUrl},</if>
          </trim>
     </insert>
 
@@ -189,7 +205,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </delete>
 
     <update id="updateBatchLiveList" parameterType="com.fs.live.vo.LiveListVo">
-        update live set is_show = #{liveVo.isShow},status = 3,finish_time = NOW() where company_id = #{liveVo.companyId} and company_user_id = #{liveVo.companyUserId} and live_id in
+        update live set is_show = #{liveVo.isShow},status = 3,finish_time = NOW() where live_id in
         <foreach item="liveId" collection="liveVo.liveIds" open="(" separator="," close=")">
             #{liveId}
         </foreach>

+ 1 - 1
fs-service/src/main/resources/mapper/live/LiveOrderMapper.xml

@@ -126,7 +126,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="verifyCode != null  and verifyCode != ''"> and verify_code = #{verifyCode}</if>
             <if test="shippingType != null "> and shipping_type = #{shippingType}</if>
             <if test="isChannel != null  and isChannel != ''"> and is_channel = #{isChannel}</if>
-            <if test="finishTime != null "> and finish_time = #{finishTime}</if>
+            <if test="finishTime != null ">and finish_time >= #{finishTime} and finish_time &lt; date_add(#{finishTime}, interval 1 day)</if>
             <if test="deliveryTime != null  and deliveryTime != ''"> and delivery_time = #{deliveryTime}</if>
             <if test="tuiMoney != null "> and tui_money = #{tuiMoney}</if>
             <if test="tuiMoneyStatus != null "> and tui_money_status = #{tuiMoneyStatus}</if>

+ 104 - 0
fs-service/src/main/resources/mapper/live/LiveRewardRecordMapper.xml

@@ -0,0 +1,104 @@
+<?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.live.mapper.LiveRewardRecordMapper">
+
+    <resultMap type="LiveRewardRecord" id="LiveRewardRecordResult">
+        <result property="id"    column="id"    />
+        <result property="liveId"    column="live_id"    />
+        <result property="userId"    column="user_id"    />
+        <result property="incomeType"    column="income_type"    />
+        <result property="sourceType"    column="source_type"    />
+        <result property="sourceId"    column="source_id"    />
+        <result property="rewardType"    column="reward_type"    />
+        <result property="num"    column="num"    />
+        <result property="beforeNum"    column="before_num"    />
+        <result property="afterNum"    column="after_num"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="createBy"    column="create_by"    />
+    </resultMap>
+
+    <sql id="selectLiveRewardRecordVo">
+        select id, live_id, user_id, income_type, source_type, source_id, reward_type, num, before_num, after_num, create_time, create_by from live_reward_record
+    </sql>
+
+    <select id="selectLiveRewardRecordList" parameterType="LiveRewardRecord" resultMap="LiveRewardRecordResult">
+        <include refid="selectLiveRewardRecordVo"/>
+        <where>
+            <if test="liveId != null "> and live_id = #{liveId}</if>
+            <if test="userId != null "> and user_id = #{userId}</if>
+            <if test="incomeType != null "> and income_type = #{incomeType}</if>
+            <if test="sourceType != null "> and source_type = #{sourceType}</if>
+            <if test="sourceId != null "> and source_id = #{sourceId}</if>
+            <if test="rewardType != null "> and reward_type = #{rewardType}</if>
+            <if test="num != null "> and num = #{num}</if>
+            <if test="beforeNum != null "> and before_num = #{beforeNum}</if>
+            <if test="afterNum != null "> and after_num = #{afterNum}</if>
+        </where>
+    </select>
+
+    <select id="selectLiveRewardRecordById" parameterType="Long" resultMap="LiveRewardRecordResult">
+        <include refid="selectLiveRewardRecordVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertLiveRewardRecord" parameterType="LiveRewardRecord" useGeneratedKeys="true" keyProperty="id">
+        insert into live_reward_record
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="liveId != null">live_id,</if>
+            <if test="userId != null">user_id,</if>
+            <if test="incomeType != null">income_type,</if>
+            <if test="sourceType != null">source_type,</if>
+            <if test="sourceId != null">source_id,</if>
+            <if test="rewardType != null">reward_type,</if>
+            <if test="num != null">num,</if>
+            <if test="beforeNum != null">before_num,</if>
+            <if test="afterNum != null">after_num,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="createBy != null and createBy != ''">create_by,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="liveId != null">#{liveId},</if>
+            <if test="userId != null">#{userId},</if>
+            <if test="incomeType != null">#{incomeType},</if>
+            <if test="sourceType != null">#{sourceType},</if>
+            <if test="sourceId != null">#{sourceId},</if>
+            <if test="rewardType != null">#{rewardType},</if>
+            <if test="num != null">#{num},</if>
+            <if test="beforeNum != null">#{beforeNum},</if>
+            <if test="afterNum != null">#{afterNum},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="createBy != null and createBy != ''">#{createBy},</if>
+         </trim>
+    </insert>
+
+    <update id="updateLiveRewardRecord" parameterType="LiveRewardRecord">
+        update live_reward_record
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="liveId != null">live_id = #{liveId},</if>
+            <if test="userId != null">user_id = #{userId},</if>
+            <if test="incomeType != null">income_type = #{incomeType},</if>
+            <if test="sourceType != null">source_type = #{sourceType},</if>
+            <if test="sourceId != null">source_id = #{sourceId},</if>
+            <if test="rewardType != null">reward_type = #{rewardType},</if>
+            <if test="num != null">num = #{num},</if>
+            <if test="beforeNum != null">before_num = #{beforeNum},</if>
+            <if test="afterNum != null">after_num = #{afterNum},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="createBy != null and createBy != ''">create_by = #{createBy},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteLiveRewardRecordById" parameterType="Long">
+        delete from live_reward_record where id = #{id}
+    </delete>
+
+    <delete id="deleteLiveRewardRecordByIds" parameterType="String">
+        delete from live_reward_record where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>

+ 20 - 1
fs-service/src/main/resources/mapper/live/LiveWatchUserMapper.xml

@@ -69,7 +69,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </select>
 
     <select id="selectOnlineUserList" parameterType="LiveWatchUser" resultType="com.fs.live.vo.LiveWatchUserVO">
-        select * from live_watch_user
+        select lwu.*,
+        fu.nick_name    nickName,
+        fu.avatar       avatar
+
+        from live_watch_user lwu
+        left join fs_user fu on lwu.user_id = fu.user_id
         <where>
         <if test="liveId != null "> and live_id = #{liveId}</if>
         <if test="online != null "> and online = #{online}</if>
@@ -128,4 +133,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{id}
         </foreach>
     </delete>
+
+    <select id="checkOnlineNoRewardUser" resultType="com.fs.live.domain.LiveWatchUser">
+        SELECT lwu.*
+        FROM live_watch_user lwu
+        WHERE lwu.live_id = #{liveId}
+          AND lwu.online = 0
+          AND NOT EXISTS (
+            SELECT 1
+            FROM live_reward_record lrr
+            WHERE lrr.user_id = lwu.user_id
+              AND lrr.live_id = lwu.live_id
+              AND DATE(lrr.create_time) = DATE(#{now})
+        )
+    </select>
 </mapper>