浏览代码

自动化任务 添加自动上下架功能,修改直播间复制逻辑,聊天室消息置顶功能

yuhongqi 1 周之前
父节点
当前提交
667fba2e36
共有 24 个文件被更改,包括 470 次插入63 次删除
  1. 17 0
      fs-admin/src/main/java/com/fs/live/controller/LiveCouponController.java
  2. 1 1
      fs-admin/src/main/java/com/fs/live/controller/LiveCouponIssueController.java
  3. 40 0
      fs-admin/src/main/java/com/fs/live/controller/LiveProfitController.java
  4. 17 0
      fs-company/src/main/java/com/fs/company/controller/live/LiveCouponController.java
  5. 23 2
      fs-company/src/main/java/com/fs/company/controller/live/LiveProfitController.java
  6. 12 0
      fs-live-socket/src/main/java/com/fs/live/websocket/service/WebSocketServer.java
  7. 8 0
      fs-service-system/src/main/java/com/fs/live/domain/LiveCoupon.java
  8. 1 0
      fs-service-system/src/main/java/com/fs/live/domain/LiveCouponIssue.java
  9. 11 0
      fs-service-system/src/main/java/com/fs/live/mapper/LiveCouponIssueUserMapper.java
  10. 11 0
      fs-service-system/src/main/java/com/fs/live/mapper/LiveCouponMapper.java
  11. 11 1
      fs-service-system/src/main/java/com/fs/live/mapper/LiveUserFirstEntryMapper.java
  12. 5 0
      fs-service-system/src/main/java/com/fs/live/service/ILiveCouponIssueUserService.java
  13. 11 1
      fs-service-system/src/main/java/com/fs/live/service/ILiveUserFirstEntryService.java
  14. 3 3
      fs-service-system/src/main/java/com/fs/live/service/impl/LiveAutoTaskServiceImpl.java
  15. 5 0
      fs-service-system/src/main/java/com/fs/live/service/impl/LiveCouponIssueUserServiceImpl.java
  16. 37 2
      fs-service-system/src/main/java/com/fs/live/service/impl/LiveCouponServiceImpl.java
  17. 40 0
      fs-service-system/src/main/java/com/fs/live/service/impl/LiveGoodsServiceImpl.java
  18. 37 0
      fs-service-system/src/main/java/com/fs/live/service/impl/LiveLotteryConfServiceImpl.java
  19. 38 0
      fs-service-system/src/main/java/com/fs/live/service/impl/LiveRedConfServiceImpl.java
  20. 8 2
      fs-service-system/src/main/java/com/fs/live/service/impl/LiveUserFirstEntryServiceImpl.java
  21. 46 0
      fs-service-system/src/main/java/com/fs/live/vo/LiveUserEntryDetail.java
  22. 16 21
      fs-service-system/src/main/java/com/fs/live/vo/LiveUserFirstProfit.java
  23. 24 13
      fs-service-system/src/main/resources/mapper/live/LiveCouponMapper.xml
  24. 48 17
      fs-service-system/src/main/resources/mapper/live/LiveUserFirstEntryMapper.xml

+ 17 - 0
fs-admin/src/main/java/com/fs/live/controller/LiveCouponController.java

@@ -16,6 +16,7 @@ import com.fs.store.domain.FsStoreCoupon;
 import com.fs.store.domain.FsStoreCouponIssue;
 import com.fs.store.param.FsStoreCouponPublishParam;
 import com.fs.store.param.LiveCouponPublishParam;
+import com.fs.store.service.IFsStoreProductService;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
@@ -42,6 +43,8 @@ public class LiveCouponController extends BaseController
     private ILiveCouponService liveCouponService;
     @Autowired
     private ILiveCouponIssueService liveCouponIssueService;
+    @Autowired
+    private IFsStoreProductService fsStoreProductService;
 
 
 
@@ -231,4 +234,18 @@ public class LiveCouponController extends BaseController
     public R updateLiveCouponBind(@RequestBody LiveCouponListVo listVo) {
         return liveCouponService.updateLiveCouponBind(listVo);
     }
+
+    /**
+     * 查询上架商品列表(用于优惠券绑定商品)
+     */
+    @PreAuthorize("@ss.hasPermi('live:liveCoupon:query')")
+    @GetMapping("/listOnSaleProducts")
+    public TableDataInfo listOnSaleProducts(com.fs.store.domain.FsStoreProduct fsStoreProduct)
+    {
+        startPage();
+        fsStoreProduct.setIsDel(0);
+        fsStoreProduct.setIsShow(1); // 只查询上架商品
+        List<com.fs.store.vo.FsStoreProductListVO> list = fsStoreProductService.selectFsStoreProductListVO(fsStoreProduct);
+        return getDataTable(list);
+    }
 }

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

@@ -93,7 +93,7 @@ public class LiveCouponIssueController extends BaseController
     /**
      * 删除优惠券领取
      */
-    @PreAuthorize("@ss.hasPermi('live:issue:remove')")
+//    @PreAuthorize("@ss.hasPermi('live:issue:remove')")
     @Log(title = "优惠券领取", businessType = BusinessType.DELETE)
 	@DeleteMapping("/{ids}")
     public AjaxResult remove(@PathVariable Long[] ids)

+ 40 - 0
fs-admin/src/main/java/com/fs/live/controller/LiveProfitController.java

@@ -0,0 +1,40 @@
+package com.fs.live.controller;
+
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.live.service.ILiveUserFirstEntryService;
+import com.fs.live.vo.LiveUserFirstProfit;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * 业绩归属控制类Controller
+ *
+ * @author fs
+ * @date 2025-09-08
+ */
+@RestController
+@RequestMapping("/live/liveProfit")
+public class LiveProfitController extends BaseController {
+
+    @Autowired
+    private ILiveUserFirstEntryService liveUserFirstEntryService;
+
+    /**
+     * 查询销售端直播数据列表
+     */
+    @PreAuthorize("@ss.hasPermi('live:liveProfit:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(LiveUserFirstProfit liveUserFirstProfit) {
+        startPage();
+        List<LiveUserFirstProfit> list = liveUserFirstEntryService.selectLiveProfitList(liveUserFirstProfit);
+        return getDataTable(list);
+    }
+
+}
+

+ 17 - 0
fs-company/src/main/java/com/fs/company/controller/live/LiveCouponController.java

@@ -14,6 +14,7 @@ import com.fs.live.service.ILiveCouponService;
 import com.fs.live.vo.LiveCouponListVo;
 import com.fs.store.param.FsStoreCouponPublishParam;
 import com.fs.store.param.LiveCouponPublishParam;
+import com.fs.store.service.IFsStoreProductService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
@@ -36,6 +37,8 @@ public class LiveCouponController extends BaseController
     private ILiveCouponService liveCouponService;
     @Autowired
     private ILiveCouponIssueService liveCouponIssueService;
+    @Autowired
+    private IFsStoreProductService fsStoreProductService;
 
 
     /**
@@ -220,4 +223,18 @@ public class LiveCouponController extends BaseController
     public R updateLiveCouponBind(@RequestBody LiveCouponListVo listVo) {
         return liveCouponService.updateLiveCouponBind(listVo);
     }
+
+    /**
+     * 查询上架商品列表(用于优惠券绑定商品)
+     */
+//    @PreAuthorize("@ss.hasPermi('live:liveCoupon:query')")
+    @GetMapping("/listOnSaleProducts")
+    public TableDataInfo listOnSaleProducts(com.fs.store.domain.FsStoreProduct fsStoreProduct)
+    {
+        startPage();
+        fsStoreProduct.setIsDel(0);
+        fsStoreProduct.setIsShow(1); // 只查询上架商品
+        List<com.fs.store.vo.FsStoreProductListVO> list = fsStoreProductService.selectFsStoreProductListVO(fsStoreProduct);
+        return getDataTable(list);
+    }
 }

+ 23 - 2
fs-company/src/main/java/com/fs/company/controller/live/LiveProfitController.java

@@ -1,12 +1,17 @@
 package com.fs.company.controller.live;
 
 import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.page.TableDataInfo;
+import com.fs.company.domain.CompanyUser;
+import com.fs.core.security.SecurityUtils;
 import com.fs.live.service.ILiveUserFirstEntryService;
+import com.fs.live.vo.LiveUserEntryDetail;
 import com.fs.live.vo.LiveUserFirstProfit;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
 import java.util.List;
@@ -25,10 +30,26 @@ public class LiveProfitController extends BaseController {
     private ILiveUserFirstEntryService liveUserFirstEntryService;
 
     @GetMapping("/list")
-    public TableDataInfo list() {
+    public TableDataInfo list(LiveUserFirstProfit liveUserFirstProfit) {
+        CompanyUser user = SecurityUtils.getLoginUser().getUser();
+//        liveUserFirstProfit.setCompanyUserId(user.getUserId());
+        liveUserFirstProfit.setCompanyId(user.getCompanyId());
         startPage();
-        List<LiveUserFirstProfit> list = liveUserFirstEntryService.selectLiveProfitList();
+        List<LiveUserFirstProfit> list = liveUserFirstEntryService.selectLiveProfitList(liveUserFirstProfit);
         return getDataTable(list);
     }
 
+    /**
+     * 查询直播间用户进入详情
+     *
+     * @param liveId 直播间ID
+     * @param companyUserId 销售ID
+     * @return 用户进入详情列表
+     */
+    @GetMapping("/detail")
+    public AjaxResult detail(@RequestParam Long liveId, @RequestParam Long companyUserId) {
+        List<LiveUserEntryDetail> list = liveUserFirstEntryService.selectLiveUserEntryDetailList(liveId, companyUserId);
+        return AjaxResult.success(list);
+    }
+
 }

+ 12 - 0
fs-live-socket/src/main/java/com/fs/live/websocket/service/WebSocketServer.java

@@ -299,6 +299,9 @@ public class WebSocketServer {
                 case "goods":
                     sendGoodsMessage(msg);
                     break;
+                case "deleteMsg":
+                    deleteMsg(liveId,msg);
+                    break;
                 case "red":
                     processRed(liveId, msg);
                     break;
@@ -319,6 +322,15 @@ public class WebSocketServer {
         }
     }
 
+    private void deleteMsg(long liveId,SendMsgVo msg) {
+        SendMsgVo sendMsgVo = new SendMsgVo();
+        sendMsgVo.setLiveId(liveId);
+        sendMsgVo.setUserType(0L);
+        sendMsgVo.setCmd("deleteMsg");
+        sendMsgVo.setMsg(msg.getMsg());
+        broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
+    }
+
     private void processCoupon(long liveId, SendMsgVo msg) {
         JSONObject jsonObject = JSON.parseObject(msg.getData());
         Integer status = jsonObject.getInteger("status");

+ 8 - 0
fs-service-system/src/main/java/com/fs/live/domain/LiveCoupon.java

@@ -73,4 +73,12 @@ public class LiveCoupon extends BaseEntity
     private String productName;
     private String image;
 
+    /** 单场直播可使用优惠券数量 */
+    @Excel(name = "单场直播可使用优惠券数量")
+    private Integer liveLimitNum;
+
+    /** 限制当前优惠券绑定商品 */
+    @Excel(name = "限制当前优惠券绑定商品")
+    private Long productId;
+
 }

+ 1 - 0
fs-service-system/src/main/java/com/fs/live/domain/LiveCouponIssue.java

@@ -68,5 +68,6 @@ public class LiveCouponIssue extends BaseEntity
     private BigDecimal couponPrice;
     private BigDecimal useMinPrice;
     private Long couponTime;
+    private Long productId;
 
 }

+ 11 - 0
fs-service-system/src/main/java/com/fs/live/mapper/LiveCouponIssueUserMapper.java

@@ -4,6 +4,8 @@ import java.util.List;
 import com.fs.live.domain.LiveCouponIssueUser;
 import com.fs.live.domain.LiveCouponUser;
 import com.fs.live.param.CouponPO;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
 
 /**
  * 优惠券用户领取记录Mapper接口
@@ -63,4 +65,13 @@ public interface LiveCouponIssueUserMapper
 
 
     List<LiveCouponUser> selectLiveCouponUserByCouponPO(CouponPO coupon);
+
+    /**
+     * 统计直播间优惠券领取数量
+     */
+    @Select("select count(1) from live_coupon_issue_user lciu " +
+            "left join live_coupon_issue lci on lci.id = lciu.issue_id " +
+            "left join live_coupon_issue_relation lcir on lcir.coupon_issue_id = lci.id " +
+            "where lcir.live_id = #{liveId} and lciu.issue_id = #{couponIssueId} and lciu.is_del = 0")
+    int countClaimedByLiveIdAndIssueId(@Param("liveId") Long liveId, @Param("couponIssueId") Long couponIssueId);
 }

+ 11 - 0
fs-service-system/src/main/java/com/fs/live/mapper/LiveCouponMapper.java

@@ -1,6 +1,8 @@
 package com.fs.live.mapper;
 
 import java.util.List;
+import java.util.Set;
+
 import com.fs.live.domain.LiveCoupon;
 import com.fs.live.domain.LiveCouponIssue;
 import com.fs.live.domain.LiveCouponIssueRelation;
@@ -90,9 +92,18 @@ public interface LiveCouponMapper
     @Insert("insert into live_coupon_issue_relation(live_id,coupon_issue_id,is_show) values(#{liveId},#{couponId},0)")
     void insertLiveCouponEntity(@Param("liveId")Long liveId,@Param("couponId") Long couponId);
 
+
+    @Insert("insert into live_coupon_issue_relation(live_id,coupon_issue_id,is_show) values(#{liveId},#{couponId},0)")
+    void insertLiveCouponEntityWithProduct(@Param("liveId")Long liveId,@Param("couponId") Long couponId,@Param("goodsId") Long goodsId);
+
     @Select("select coupon_issue_id as id from live_coupon_issue_relation where live_id = #{liveId}")
     List<Long> selectLiveCouponIssueByLiveId(Long liveId);
 
+    @Select("select lci.*,lc.product_id from live_coupon_issue_relation lcir left join live_coupon_issue lci on lci.id=lcir.coupon_issue_id " +
+            " left join live_coupon lc on lc.coupon_id = lci。coupon_id " +
+            "where lcir.live_id = #{liveId}")
+    List<LiveCouponIssue> selectCouponIssueByLiveId(@Param("liveId") Long liveId);
+
     void handleDeleteSelectedAdmin(@Param("listVo") LiveCouponListVo listVo);
 
     @Select("select count(1) from live_coupon_issue_relation where live_id = #{liveId} and is_show = 1")

+ 11 - 1
fs-service-system/src/main/java/com/fs/live/mapper/LiveUserFirstEntryMapper.java

@@ -3,6 +3,7 @@ package com.fs.live.mapper;
 
 import com.fs.live.domain.LiveUserFirstEntry;
 import com.fs.live.vo.LiveUserFirstProfit;
+import com.fs.live.vo.LiveUserEntryDetail;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 
@@ -67,8 +68,17 @@ public interface LiveUserFirstEntryMapper {
     @Select("select count(*) from live_user_first_entry where user_id=#{userId} and DATE(entry_date)=DATE(#{now})")
     int selectTodayEntry(@Param("userId") long userId,@Param("now") Date now);
 
-    List<LiveUserFirstProfit> selectLiveProfitList();
+    List<LiveUserFirstProfit> selectLiveProfitList(LiveUserFirstProfit liveUserFirstProfit);
 
     @Select("select * from live_user_first_entry where live_id=#{liveId} and user_id=#{userId}")
     LiveUserFirstEntry selectEntityByLiveIdUserId(@Param("liveId") long liveId,@Param("userId") long userId);
+
+    /**
+     * 根据直播间ID和销售ID查询用户进入详情列表
+     *
+     * @param liveId 直播间ID
+     * @param companyUserId 销售ID
+     * @return 用户进入详情列表
+     */
+    List<LiveUserEntryDetail> selectLiveUserEntryDetailList(@Param("liveId") Long liveId, @Param("companyUserId") Long companyUserId);
 }

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

@@ -63,4 +63,9 @@ public interface ILiveCouponIssueUserService
 
 
     List<LiveCouponUser> selectLiveCouponUserByCouponPO(CouponPO coupon);
+
+    /**
+     * 统计直播间优惠券领取数量
+     */
+    int countClaimedByLiveIdAndIssueId(Long liveId, Long couponIssueId);
 }

+ 11 - 1
fs-service-system/src/main/java/com/fs/live/service/ILiveUserFirstEntryService.java

@@ -3,6 +3,7 @@ package com.fs.live.service;
 
 import com.fs.live.domain.LiveUserFirstEntry;
 import com.fs.live.vo.LiveUserFirstProfit;
+import com.fs.live.vo.LiveUserEntryDetail;
 
 import java.util.List;
 
@@ -63,7 +64,16 @@ public interface ILiveUserFirstEntryService {
 
     int selectTodayEntry(long userId);
 
-    List<LiveUserFirstProfit> selectLiveProfitList();
+    List<LiveUserFirstProfit> selectLiveProfitList(LiveUserFirstProfit liveUserFirstProfit);
 
     LiveUserFirstEntry selectEntityByLiveIdUserId(long liveId, long userId);
+
+    /**
+     * 根据直播间ID和销售ID查询用户进入详情列表
+     *
+     * @param liveId 直播间ID
+     * @param companyUserId 销售ID
+     * @return 用户进入详情列表
+     */
+    List<LiveUserEntryDetail> selectLiveUserEntryDetailList(Long liveId, Long companyUserId);
 }

+ 3 - 3
fs-service-system/src/main/java/com/fs/live/service/impl/LiveAutoTaskServiceImpl.java

@@ -146,11 +146,11 @@ public class LiveAutoTaskServiceImpl implements ILiveAutoTaskService {
         } else if(liveAutoTask.getTaskType() == 3L){
             baseMapper.insertLiveAutoTask(liveAutoTask);
         } else if (liveAutoTask.getTaskType() == 5L) {
+            LiveCouponIssue liveCouponIssue = liveCouponIssueMapper.selectLiveCouponIssueById(Long.valueOf(liveAutoTask.getContent()));
+            if(liveCouponIssue == null)return R.error("优惠券未发布");
             // 自动优惠券
-            LiveCoupon liveCoupon = liveCouponMapper.selectLiveCouponById(Long.valueOf(liveAutoTask.getContent()));
+            LiveCoupon liveCoupon = liveCouponMapper.selectLiveCouponById(liveCouponIssue.getCouponId());
             if(liveCoupon == null) return R.error("优惠券不存在");
-            LiveCouponIssue liveCouponIssue = liveCouponIssueMapper.selectLiveCouponIssueByCouponId(liveCoupon.getCouponId());
-            if(liveCouponIssue == null)return R.error("优惠券未发布");
             LiveCouponIssueRelation liveCouponIssueRelation = liveCouponMapper.selectCouponRelation(liveAutoTask.getLiveId(),liveCouponIssue.getId());
             if(liveCouponIssueRelation == null) return R.error("优惠券尚未添加在直播间");
             if(ObjectUtil.isEmpty(liveCouponIssueRelation.getGoodsId())) return R.error("未绑定商品,无法制定自动化任务!");

+ 5 - 0
fs-service-system/src/main/java/com/fs/live/service/impl/LiveCouponIssueUserServiceImpl.java

@@ -101,4 +101,9 @@ public class LiveCouponIssueUserServiceImpl implements ILiveCouponIssueUserServi
     public List<LiveCouponUser> selectLiveCouponUserByCouponPO(CouponPO coupon) {
         return liveCouponIssueUserMapper.selectLiveCouponUserByCouponPO(coupon);
     }
+
+    @Override
+    public int countClaimedByLiveIdAndIssueId(Long liveId, Long couponIssueId) {
+        return liveCouponIssueUserMapper.countClaimedByLiveIdAndIssueId(liveId, couponIssueId);
+    }
 }

+ 37 - 2
fs-service-system/src/main/java/com/fs/live/service/impl/LiveCouponServiceImpl.java

@@ -11,6 +11,7 @@ import com.fs.common.core.redis.RedisUtil;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
 import com.fs.live.domain.*;
+import com.fs.live.mapper.LiveGoodsMapper;
 import com.fs.live.mapper.LiveMapper;
 import com.fs.live.param.CouponPO;
 import com.fs.live.service.ILiveCouponIssueService;
@@ -48,6 +49,8 @@ public class LiveCouponServiceImpl implements ILiveCouponService
     private ILiveCouponUserService liveCouponUserService;
     @Autowired
     private ILiveCouponIssueService liveCouponIssueService;
+    @Autowired
+    private LiveGoodsMapper liveGoodsMapper;
 
     /**
      * 查询优惠券
@@ -201,13 +204,39 @@ public class LiveCouponServiceImpl implements ILiveCouponService
             return R.error("您已经领取过优惠券了!");
         }
         LiveCouponIssue issue = liveCouponMapper.selectLiveCouponIssueByLiveIdAndCouponId(coupon.getLiveId(), coupon.getCouponIssueId());
-        if (coupon == null || issue.getStatus() != 1) {
+        if (coupon == null || issue == null || issue.getStatus() != 1) {
             return R.error("优惠券不存在或者已下架!");
         }
-        Long decrement = redisUtil.decrement(String.format(LiveKeysConstant.LIVE_COUPON_NUM , coupon.getCouponIssueId()));
 
         LiveCoupon liveCoupon = liveCouponMapper.selectLiveCouponById(issue.getCouponId());
+        if (liveCoupon == null) {
+            return R.error("优惠券不存在!");
+        }
+
+        // 检查单场直播可使用优惠券数量限制
+        String liveLimitKey = String.format("live:coupon:limit:%s:%s", coupon.getLiveId(), coupon.getCouponIssueId());
+        Integer liveLimitNum = liveCoupon.getLiveLimitNum();
+        int currentClaimedCount = 0;
+
+        if (liveLimitNum != null && liveLimitNum > 0) {
+            // 先查Redis缓存
+            Object cachedCount = redisUtil.get(liveLimitKey);
+
+            if (cachedCount != null) {
+                currentClaimedCount = Integer.parseInt(cachedCount.toString());
+            } else {
+                // 缓存不存在,查询数据库
+                currentClaimedCount = liveCouponIssueUserService.countClaimedByLiveIdAndIssueId(coupon.getLiveId(), coupon.getCouponIssueId());
+                // 存入Redis,24小时过期
+                redisUtil.set(liveLimitKey, currentClaimedCount, 24 * 60 * 60, java.util.concurrent.TimeUnit.SECONDS);
+            }
 
+            if (currentClaimedCount >= liveLimitNum) {
+                return R.error("此优惠券单场直播领取数量已达上限!");
+            }
+        }
+
+        Long decrement = redisUtil.decrement(String.format(LiveKeysConstant.LIVE_COUPON_NUM , coupon.getCouponIssueId()));
 
         if (decrement < 0L) {
             issue.setStatus(-1);
@@ -244,6 +273,12 @@ public class LiveCouponServiceImpl implements ILiveCouponService
         liveCouponUserService.insertLiveCouponUser(userRecord);
         liveCouponIssueUserService.insertLiveCouponIssueUser(record);
         redisUtil.hashPut(String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG_COUPON, coupon.getLiveId(), coupon.getCouponIssueId()), String.valueOf(coupon.getUserId()), JSONUtil.toJsonStr(record));
+
+        // 更新单场直播领取数量缓存
+        if (liveLimitNum != null && liveLimitNum > 0) {
+            redisUtil.set(liveLimitKey, currentClaimedCount + 1, 24 * 60 * 60, java.util.concurrent.TimeUnit.SECONDS);
+        }
+
         return R.ok("恭喜您抢到优惠券");
     }
 

+ 40 - 0
fs-service-system/src/main/java/com/fs/live/service/impl/LiveGoodsServiceImpl.java

@@ -14,6 +14,10 @@ import com.fs.store.domain.FsStoreProduct;
 import com.fs.store.mapper.FsStoreProductMapper;
 import com.fs.store.service.IFsStoreProductService;
 import com.fs.store.service.IFsStoreShopService;
+import com.fs.live.domain.LiveAutoTask;
+import com.fs.live.service.ILiveAutoTaskService;
+import cn.hutool.json.JSONUtil;
+import cn.hutool.json.JSONObject;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -38,6 +42,9 @@ public class LiveGoodsServiceImpl  implements ILiveGoodsService {
     @Autowired
     private LiveGoodsMapper baseMapper;
 
+    @Autowired
+    private ILiveAutoTaskService liveAutoTaskService;
+
     /**
      * 查询直播商品
      *
@@ -107,9 +114,42 @@ public class LiveGoodsServiceImpl  implements ILiveGoodsService {
     @Override
     public int deleteLiveGoodsByGoodsIds(Long[] goodsIds)
     {
+        // 删除前检查live_auto_task,删除task_type=1和6的定时任务
+        for (Long goodsId : goodsIds) {
+            deleteRelatedAutoTasksForGoods(goodsId);
+        }
         return baseMapper.deleteLiveGoodsByGoodsIds(goodsIds);
     }
 
+    /**
+     * 删除商品相关的定时任务(task_type=1和6)
+     */
+    private void deleteRelatedAutoTasksForGoods(Long goodsId) {
+        LiveAutoTask queryTask = new LiveAutoTask();
+        List<LiveAutoTask> tasks = liveAutoTaskService.selectLiveAutoTaskList(queryTask);
+        List<Long> taskIdsToDelete = new ArrayList<>();
+        
+        for (LiveAutoTask task : tasks) {
+            if (task.getTaskType() != null && (task.getTaskType() == 1L || task.getTaskType() == 6L)) {
+                if (task.getContent() != null && !task.getContent().isEmpty()) {
+                    try {
+                        JSONObject contentJson = JSONUtil.parseObj(task.getContent());
+                        Object contentGoodsId = contentJson.get("goodsId");
+                        if (contentGoodsId != null && goodsId.equals(Long.parseLong(contentGoodsId.toString()))) {
+                            taskIdsToDelete.add(task.getId());
+                        }
+                    } catch (Exception e) {
+                        // 解析失败,跳过
+                    }
+                }
+            }
+        }
+        
+        if (!taskIdsToDelete.isEmpty()) {
+            liveAutoTaskService.deleteLiveAutoTaskByIds(taskIdsToDelete.toArray(new Long[0]));
+        }
+    }
+
     /**
      * 删除直播商品信息
      *

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

@@ -4,6 +4,7 @@ package com.fs.live.service.impl;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.DateUtils;
+import com.fs.live.domain.LiveAutoTask;
 import com.fs.live.domain.LiveLotteryConf;
 import com.fs.live.domain.LiveLotteryRegistration;
 import com.fs.live.domain.LiveUserLotteryRecord;
@@ -14,6 +15,7 @@ import com.fs.live.mapper.LiveUserLotteryRecordMapper;
 import com.fs.live.param.LiveLotteryProduct;
 import com.fs.live.param.LiveLotteryProductSaveParam;
 import com.fs.live.param.LotteryPO;
+import com.fs.live.service.ILiveAutoTaskService;
 import com.fs.live.service.ILiveLotteryConfService;
 import com.fs.live.vo.LiveLotteryConfVo;
 import com.fs.live.vo.LiveLotteryProductListVo;
@@ -47,6 +49,8 @@ public class LiveLotteryConfServiceImpl implements ILiveLotteryConfService {
     private LiveLotteryRegistrationMapper lotteryRegistrationMapper;
     @Autowired
     private LiveUserLotteryRecordMapper liveUserLotteryRecordMapper;
+    @Autowired
+    private ILiveAutoTaskService liveAutoTaskService;
 
     @Autowired
     private LiveLotteryConfMapper baseMapper;
@@ -123,9 +127,42 @@ public class LiveLotteryConfServiceImpl implements ILiveLotteryConfService {
     @Override
     public int deleteLiveLotteryConfByLotteryIds(Long[] lotteryIds)
     {
+        // 删除前检查live_auto_task,删除task_type=4的定时任务
+        for (Long lotteryId : lotteryIds) {
+            deleteRelatedAutoTasksForLottery(lotteryId);
+        }
         return baseMapper.deleteLiveLotteryConfByLotteryIds(lotteryIds);
     }
 
+    /**
+     * 删除抽奖相关的定时任务(task_type=4)
+     */
+    private void deleteRelatedAutoTasksForLottery(Long lotteryId) {
+        LiveAutoTask queryTask = new LiveAutoTask();
+        List<LiveAutoTask> tasks = liveAutoTaskService.selectLiveAutoTaskList(queryTask);
+        List<Long> taskIdsToDelete = new ArrayList<>();
+
+        for (LiveAutoTask task : tasks) {
+            if (task.getTaskType() != null && task.getTaskType() == 4L) {
+                if (task.getContent() != null && !task.getContent().isEmpty()) {
+                    try {
+                        cn.hutool.json.JSONObject contentJson = cn.hutool.json.JSONUtil.parseObj(task.getContent());
+                        Object contentLotteryId = contentJson.get("lotteryId");
+                        if (contentLotteryId != null && lotteryId.equals(Long.parseLong(contentLotteryId.toString()))) {
+                            taskIdsToDelete.add(task.getId());
+                        }
+                    } catch (Exception e) {
+                        // 解析失败,跳过
+                    }
+                }
+            }
+        }
+
+        if (!taskIdsToDelete.isEmpty()) {
+            liveAutoTaskService.deleteLiveAutoTaskByIds(taskIdsToDelete.toArray(new Long[0]));
+        }
+    }
+
     /**
      * 删除直播抽奖配置信息
      *

+ 38 - 0
fs-service-system/src/main/java/com/fs/live/service/impl/LiveRedConfServiceImpl.java

@@ -17,6 +17,9 @@ import com.fs.live.mapper.LiveRewardRecordMapper;
 import com.fs.live.mapper.LiveUserRedRecordMapper;
 import com.fs.live.param.RedPO;
 import com.fs.live.service.ILiveRedConfService;
+import com.fs.live.domain.LiveAutoTask;
+import com.fs.live.service.ILiveAutoTaskService;
+import cn.hutool.json.JSONObject;
 import com.fs.store.domain.FsUser;
 import com.fs.store.service.IFsUserService;
 import com.fs.store.service.impl.FsUserServiceImpl;
@@ -67,6 +70,8 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
     private LiveRedConfMapper baseMapper;
     @Autowired
     private FsUserServiceImpl fsUserServiceImpl;
+    @Autowired
+    private ILiveAutoTaskService liveAutoTaskService;
 
     /**
      * 查询直播红包记录配置
@@ -141,9 +146,42 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
     @Override
     public int deleteLiveRedConfByRedIds(Long[] redIds)
     {
+        // 删除前检查live_auto_task,删除task_type=2的定时任务
+        for (Long redId : redIds) {
+            deleteRelatedAutoTasksForRed(redId);
+        }
         return baseMapper.deleteLiveRedConfByRedIds(redIds);
     }
 
+    /**
+     * 删除红包相关的定时任务(task_type=2)
+     */
+    private void deleteRelatedAutoTasksForRed(Long redId) {
+        LiveAutoTask queryTask = new LiveAutoTask();
+        List<LiveAutoTask> tasks = liveAutoTaskService.selectLiveAutoTaskList(queryTask);
+        List<Long> taskIdsToDelete = new ArrayList<>();
+        
+        for (LiveAutoTask task : tasks) {
+            if (task.getTaskType() != null && task.getTaskType() == 2L) {
+                if (task.getContent() != null && !task.getContent().isEmpty()) {
+                    try {
+                        JSONObject contentJson = JSONUtil.parseObj(task.getContent());
+                        Object contentRedId = contentJson.get("redId");
+                        if (contentRedId != null && redId.equals(Long.parseLong(contentRedId.toString()))) {
+                            taskIdsToDelete.add(task.getId());
+                        }
+                    } catch (Exception e) {
+                        // 解析失败,跳过
+                    }
+                }
+            }
+        }
+        
+        if (!taskIdsToDelete.isEmpty()) {
+            liveAutoTaskService.deleteLiveAutoTaskByIds(taskIdsToDelete.toArray(new Long[0]));
+        }
+    }
+
     /**
      * 删除直播红包记录配置信息
      *

+ 8 - 2
fs-service-system/src/main/java/com/fs/live/service/impl/LiveUserFirstEntryServiceImpl.java

@@ -6,6 +6,7 @@ import java.util.List;
 import com.fs.common.utils.DateUtils;
 
 import com.fs.live.vo.LiveUserFirstProfit;
+import com.fs.live.vo.LiveUserEntryDetail;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import com.fs.live.mapper.LiveUserFirstEntryMapper;
@@ -104,12 +105,17 @@ public class LiveUserFirstEntryServiceImpl implements ILiveUserFirstEntryService
     }
 
     @Override
-    public List<LiveUserFirstProfit> selectLiveProfitList() {
-        return baseMapper.selectLiveProfitList();
+    public List<LiveUserFirstProfit> selectLiveProfitList(LiveUserFirstProfit liveUserFirstProfit) {
+        return baseMapper.selectLiveProfitList(liveUserFirstProfit);
     }
 
     @Override
     public LiveUserFirstEntry selectEntityByLiveIdUserId(long liveId,long userId) {
         return baseMapper.selectEntityByLiveIdUserId(liveId, userId);
     }
+
+    @Override
+    public List<LiveUserEntryDetail> selectLiveUserEntryDetailList(Long liveId, Long companyUserId) {
+        return baseMapper.selectLiveUserEntryDetailList(liveId, companyUserId);
+    }
 }

+ 46 - 0
fs-service-system/src/main/java/com/fs/live/vo/LiveUserEntryDetail.java

@@ -0,0 +1,46 @@
+package com.fs.live.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+/**
+ * 直播间用户进入详情对象
+ *
+ * @author fs
+ * @date 2025-01-XX
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class LiveUserEntryDetail extends BaseEntity {
+
+    /** 直播间ID */
+    private Long liveId;
+
+    /** 直播间名称 */
+    private String liveName;
+
+    /** 销售ID */
+    private Long companyUserId;
+
+    /** 销售名称 */
+    private String companyUserName;
+
+    /** 用户ID */
+    private Long userId;
+
+    /** 用户名称 */
+    private String userName;
+
+    /** 进入时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date firstEntryTime;
+
+    /** 进入日期 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private Date entryDate;
+}
+

+ 16 - 21
fs-service-system/src/main/java/com/fs/live/vo/LiveUserFirstProfit.java

@@ -1,11 +1,13 @@
 package com.fs.live.vo;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.BaseEntity;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 
 import java.math.BigDecimal;
+import java.util.Date;
 
 /**
  * 用户每日首次进入直播间记录对象 live_user_first_entry
@@ -17,32 +19,25 @@ import java.math.BigDecimal;
 @EqualsAndHashCode(callSuper = true)
 public class LiveUserFirstProfit extends BaseEntity{
 
-    /** 主键ID */
-    private Long orderId;
+   private Long liveId;
+   private String liveName;
 
+   private String companyName;
+   private Long companyId;
+   private Long companyUserId;
+   private String companyUserName;
 
-    private String orderCode;
+   /** 进入日期 */
 
+   private Date entryDate;
 
-    private String userName;
+   /** 当天首次进入时间 */
 
+   private Date firstEntryTime;
 
-    private String userPhone;
-
-
-    private BigDecimal totalPrice;
-    private BigDecimal payPrice;
-
-    /** 支付状态 待支付 1已支付 */
-    @Excel(name = "支付状态 待支付 1已支付")
-    private String isPay;
-
-    /** 订单状态(-1 : 申请退款 -2 : 退货成功 0:已取消 1:待支付 2:待发货;3:待收货;4:待评价;5:已完成) */
-    @Excel(name = "订单状态", readConverterExp = "-=1,:=,申=请退款,-=2,:=,退=货成功,1=:待支付,2=:待发货;3:待收货;4:待评价;5:已完成")
-    private Integer status;
-
-    private Long companyUserId;
-    private String companyUserName;
-    private String companyName;
+   /**
+   * 直播间人数
+   */
+   private Integer liveNum;
 
 }

+ 24 - 13
fs-service-system/src/main/resources/mapper/live/LiveCouponMapper.xml

@@ -19,26 +19,31 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="createTime"    column="create_time"    />
         <result property="updateTime"    column="update_time"    />
         <result property="isDel"    column="is_del"    />
+        <result property="liveLimitNum"    column="live_limit_num"    />
+        <result property="productId"    column="product_id"    />
     </resultMap>
 
     <sql id="selectLiveCouponVo">
-        select coupon_id, title, integral, coupon_price, use_min_price, coupon_time, sort, status, product_ids, package_cate_ids, type, create_time, update_time, is_del from live_coupon
+        select coupon_id, title, integral, coupon_price, use_min_price, coupon_time, sort, status, product_ids, package_cate_ids, type, create_time, update_time, is_del, live_limit_num, product_id from live_coupon
     </sql>
 
     <select id="selectLiveCouponList" parameterType="LiveCoupon" resultMap="LiveCouponResult">
-        <include refid="selectLiveCouponVo"/>
+        select lc.coupon_id, lc.title, lc.integral, lc.coupon_price, lc.use_min_price, lc.coupon_time, lc.sort, lc.status, lc.product_ids, lc.package_cate_ids, lc.type, lc.create_time, lc.update_time, lc.is_del, lc.live_limit_num, lc.product_id, fsp.product_name from live_coupon lc
+        left join fs_store_product fsp on lc.product_id = fsp.product_id
         <where>
-            <if test="title != null  and title != ''"> and title = #{title}</if>
-            <if test="integral != null  and integral != ''"> and integral = #{integral}</if>
-            <if test="couponPrice != null "> and coupon_price = #{couponPrice}</if>
-            <if test="useMinPrice != null "> and use_min_price = #{useMinPrice}</if>
-            <if test="couponTime != null "> and coupon_time = #{couponTime}</if>
-            <if test="sort != null "> and sort = #{sort}</if>
-            <if test="status != null "> and status = #{status}</if>
-            <if test="productIds != null  and productIds != ''"> and product_ids = #{productIds}</if>
-            <if test="packageCateIds != null  and packageCateIds != ''"> and package_cate_ids = #{packageCateIds}</if>
-            <if test="type != null "> and type = #{type}</if>
-            <if test="isDel != null "> and is_del = #{isDel}</if>
+            <if test="title != null  and title != ''"> and lc.title = #{title}</if>
+            <if test="integral != null  and integral != ''"> and lc.integral = #{integral}</if>
+            <if test="couponPrice != null "> and lc.coupon_price = #{couponPrice}</if>
+            <if test="useMinPrice != null "> and lc.use_min_price = #{useMinPrice}</if>
+            <if test="couponTime != null "> and lc.coupon_time = #{couponTime}</if>
+            <if test="sort != null "> and lc.sort = #{sort}</if>
+            <if test="status != null "> and lc.status = #{status}</if>
+            <if test="productIds != null  and productIds != ''"> and lc.product_ids = #{productIds}</if>
+            <if test="packageCateIds != null  and packageCateIds != ''"> and lc.package_cate_ids = #{packageCateIds}</if>
+            <if test="type != null "> and lc.type = #{type}</if>
+            <if test="isDel != null "> and lc.is_del = #{isDel}</if>
+            <if test="liveLimitNum != null "> and lc.live_limit_num = #{liveLimitNum}</if>
+            <if test="productId != null "> and lc.product_id = #{productId}</if>
         </where>
     </select>
 
@@ -63,6 +68,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="createTime != null">create_time,</if>
             <if test="updateTime != null">update_time,</if>
             <if test="isDel != null">is_del,</if>
+            <if test="liveLimitNum != null">live_limit_num,</if>
+            <if test="productId != null">product_id,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="title != null and title != ''">#{title},</if>
@@ -78,6 +85,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="createTime != null">#{createTime},</if>
             <if test="updateTime != null">#{updateTime},</if>
             <if test="isDel != null">#{isDel},</if>
+            <if test="liveLimitNum != null">#{liveLimitNum},</if>
+            <if test="productId != null">#{productId},</if>
          </trim>
     </insert>
 
@@ -97,6 +106,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateTime != null">update_time = #{updateTime},</if>
             <if test="isDel != null">is_del = #{isDel},</if>
+            <if test="liveLimitNum != null">live_limit_num = #{liveLimitNum},</if>
+            <if test="productId != null">product_id = #{productId},</if>
         </trim>
         where coupon_id = #{couponId}
     </update>

+ 48 - 17
fs-service-system/src/main/resources/mapper/live/LiveUserFirstEntryMapper.xml

@@ -87,23 +87,54 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </foreach>
     </delete>
 
-    <select id="selectLiveProfitList"  resultType="com.fs.live.vo.LiveUserFirstProfit">
-        SELECT
-            lo.order_id,lo.order_code,lo.user_name,lo.user_phone,lo.total_price,lo.pay_price,lo.is_pay,lo.status,
-            cu.user_id AS company_user_id,
-            cu.nick_name AS company_user_name,
-            c.company_name
-        FROM
-            live_order lo
-                LEFT JOIN
-            live_user_first_entry lufe
-            ON lo.user_id = lufe.user_id
-                AND DATE(lo.create_time) = lufe.entry_date
-            LEFT JOIN
-            company_user cu
-        ON lufe.company_user_id = cu.user_id
-            left join company c on cu.company_id = c.company_id
+    <select id="selectLiveProfitList" parameterType="com.fs.live.vo.LiveUserFirstProfit" resultType="com.fs.live.vo.LiveUserFirstProfit">
+
+        select lv.live_id,lv.live_name,count(1) as liveNum,c.company_name,cu.user_id as companyUserId,cu.nick_name as companyUserName from live_user_first_entry lufe left join live lv on lv.live_id = lufe.live_id
+        left join company_user cu on cu.user_id = lufe.company_user_id
+        left join company c on c.company_id = cu.company_id
+        where lufe.company_id > 0 and lufe.company_user_id > 0 and  lufe.company_id  is not null
+        <if test="companyId != null"> and lufe.company_id = #{companyId}</if>
+        <if test="companyUserId != null"> and lufe.company_user_id = #{companyUserId}</if>
+        <if test="liveName != null and liveName != '' "> and lv.liveName like concat('%',#{liveName},'%')</if>
+        group by lufe.live_id,lufe.company_id,lufe.company_user_id
+
         ORDER BY
-            lo.create_time DESC
+        lv.create_time DESC
+
+    </select>
+
+    <select id="selectLiveUserEntryDetailList" resultType="com.fs.live.vo.LiveUserEntryDetail">
+        select 
+            lv.live_id,
+            lv.live_name,
+            cu.user_id as companyUserId,
+            cu.nick_name as companyUserName,
+            lufe.user_id as userId,
+            fu.nickname as userName,
+            lufe.first_entry_time,
+            lufe.entry_date
+        from live_user_first_entry lufe
+        left join live lv on lv.live_id = lufe.live_id
+        left join company_user cu on cu.user_id = lufe.company_user_id
+        left join fs_user fu on fu.user_id = lufe.user_id
+        where 1=1
+        <if test="liveId != null"> and lufe.live_id = #{liveId}</if>
+        <if test="companyUserId != null"> and lufe.company_user_id = #{companyUserId}</if>
+        order by lufe.first_entry_time desc
     </select>
+
+<!--    select lv.live_id,lv.live_name,c.company_name,c.company_id,lufe.user_id,lufe.live_id,lufe.entry_date,lufe.first_entry_time,lufe.company_user_id,lufe.* from live_user_first_entry lufe left join live lv on lv.live_id = lufe.live_id-->
+<!--    left join company_user cu on cu.company_user_id = lufe.company_user_id-->
+<!--    left join company c on c.company_id = cu.company_id-->
+
+<!--    where 1=1-->
+<!--    <if test="companyId != null"> and lufe.company_id = #{companyId}</if>-->
+<!--    <if test="companyUserId != null"> and lufe.company_user_id = #{companyUserId}</if>-->
+
+<!--    <if test="liveName != null and liveName != '' "> and lv.liveName like concat('%',#{liveName},'%')</if>-->
+
+<!--    group by lv.live_id-->
+
+<!--    ORDER BY-->
+<!--    lv.create_time DESC-->
 </mapper>