浏览代码

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

yuhongqi 2 周之前
父节点
当前提交
ffd44893f3

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

@@ -106,9 +106,9 @@ public class LiveAutoTaskController extends BaseController
     @PreAuthorize("@ss.hasPermi('live:task:edit')")
     @Log(title = "直播间自动化任务配置", businessType = BusinessType.UPDATE)
     @PutMapping
-    public AjaxResult edit(@RequestBody LiveAutoTask liveAutoTask)
+    public R edit(@RequestBody LiveAutoTask liveAutoTask)
     {
-        return toAjax(liveAutoTaskService.updateLiveAutoTask(liveAutoTask));
+        return liveAutoTaskService.updateLiveAutoTask(liveAutoTask);
     }
 
     /**

+ 1 - 0
fs-common/src/main/java/com/fs/common/constant/LiveKeysConstant.java

@@ -24,6 +24,7 @@ public class LiveKeysConstant {
     public static final String LIVE_HOME_PAGE_CONFIG_RED = "live:config:%s:red:%s"; //红包记录
     public static final String LIVE_HOME_PAGE_CONFIG_COUPON = "live:config:%s:coupon:%s"; //优惠券记录
     public static final String LIVE_HOME_PAGE_CONFIG_DRAW = "live:config:%s:draw:%s"; //抽奖记录
+    public static final String TOP_MSG = "topMsg"; //抽奖记录
 
 
 

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

@@ -112,9 +112,9 @@ public class LiveAutoTaskController extends BaseController
     @PreAuthorize("@ss.hasPermi('live:task:edit')")
     @Log(title = "直播间自动化任务配置", businessType = BusinessType.UPDATE)
     @PutMapping
-    public AjaxResult edit(@RequestBody LiveAutoTask liveAutoTask)
+    public R edit(@RequestBody LiveAutoTask liveAutoTask)
     {
-        return toAjax(liveAutoTaskService.updateLiveAutoTask(liveAutoTask));
+        return liveAutoTaskService.updateLiveAutoTask(liveAutoTask);
     }
 
     /**

+ 6 - 5
fs-live-socket/src/main/java/com/fs/live/task/Task.java

@@ -161,6 +161,12 @@ public class Task {
                 }
             }
         });
+
+        if(!liveList.isEmpty()){
+            for (Live live : liveList) {
+                liveService.updateLiveEntity(live);
+            }
+        }
         String key = "live:auto_task:";
         if (!startLiveList.isEmpty()) {
             for (Live live : startLiveList) {
@@ -201,11 +207,6 @@ public class Task {
             // 重新更新所有在直播的缓存
             liveService.asyncToCache();
         }
-        if(!liveList.isEmpty()){
-            for (Live live : liveList) {
-                liveService.updateLiveEntity(live);
-            }
-        }
     }
     @Scheduled(cron = "0/1 * * * * ?")
     @DistributeLock(key = "liveLotteryTask", scene = "task")

+ 1 - 0
fs-live-socket/src/main/java/com/fs/live/websocket/bean/SendMsgVo.java

@@ -33,5 +33,6 @@ public class SendMsgVo {
     private String avatar;
     private boolean on = false;
     private Integer status;
+    private Integer duration;
 
 }

+ 49 - 5
fs-live-socket/src/main/java/com/fs/live/websocket/service/WebSocketServer.java

@@ -25,6 +25,7 @@ import org.springframework.stereotype.Component;
 
 import javax.websocket.*;
 import javax.websocket.server.ServerEndpoint;
+import java.io.EOFException;
 import java.io.IOException;
 import java.util.*;
 import java.util.concurrent.ConcurrentHashMap;
@@ -33,6 +34,7 @@ import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.TimeUnit;
 
 import static com.fs.common.constant.LiveKeysConstant.*;
+import static com.fs.common.constant.LiveKeysConstant.TOP_MSG;
 
 @ServerEndpoint(value = "/ws/app/webSocket",configurator = WebSocketConfigurator.class)
 @Component
@@ -129,8 +131,8 @@ public class WebSocketServer {
             if (isFirstViewer) {
                 redisCache.increment(UNIQUE_VIEWERS_KEY + liveId, 1);
             }
-            LiveWatchUser liveWatchUser = liveWatchUserService.getByLiveIdAndUserId(liveId, userId);
-            liveWatchUserVO.setMsgStatus(liveWatchUser.getMsgStatus());
+
+            liveWatchUserVO.setMsgStatus(liveWatchUserVO.getMsgStatus());
             SendMsgVo sendMsgVo = new SendMsgVo();
             sendMsgVo.setLiveId(liveId);
             sendMsgVo.setUserId(userId);
@@ -237,6 +239,7 @@ public class WebSocketServer {
 
         SendMsgVo msg = JSONObject.parseObject(message, SendMsgVo.class);
         if(msg.isOn()) return;
+        LiveMsg liveMsg;
         try {
             switch (msg.getCmd()) {
                 case "heartbeat":
@@ -245,7 +248,7 @@ public class WebSocketServer {
                 case "sendMsg":
                     msg.setMsg(productionWordFilter.filter(msg.getMsg()).getFilteredText());
                     if(StringUtils.isEmpty(msg.getMsg())) return;
-                    LiveMsg liveMsg = new LiveMsg();
+                    liveMsg = new LiveMsg();
                     liveMsg.setLiveId(msg.getLiveId());
                     liveMsg.setUserId(msg.getUserId());
                     liveMsg.setNickName(msg.getNickName());
@@ -271,6 +274,25 @@ public class WebSocketServer {
                     break;
                 case "sendGift":
                     break;
+                case "sendTopMsg":
+                    msg.setMsg(productionWordFilter.filter(msg.getMsg()).getFilteredText());
+                    if(StringUtils.isEmpty(msg.getMsg())) return;
+                    liveMsg = new LiveMsg();
+                    liveMsg.setLiveId(msg.getLiveId());
+                    liveMsg.setUserId(msg.getUserId());
+                    liveMsg.setNickName(msg.getNickName());
+                    liveMsg.setAvatar(msg.getAvatar());
+                    liveMsg.setMsg(msg.getMsg());
+                    liveMsg.setEndTime(DateUtils.addMinutes(new Date(),msg.getDuration()).toString());
+                    msg.setOn(true);
+                    msg.setData(JSONObject.toJSONString(liveMsg));
+                    // 广播消息
+                    broadcastMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)));
+                    // 放在当前活动里面
+                    redisCache.deleteObject(String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG, liveId, TOP_MSG));
+                    redisCache.setCacheObject(String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG, liveId, TOP_MSG), JSONObject.toJSONString(liveMsg));
+                    redisCache.expire(String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG, liveId, TOP_MSG), msg.getDuration(), TimeUnit.MINUTES);
+                    break;
                 case "blockUser":
                     sendBlockMessage(liveId, msg.getUserId());
                     break;
@@ -367,7 +389,16 @@ public class WebSocketServer {
     //错误时调用
     @OnError
     public void onError(Session session, Throwable throwable) {
-        log.error("webSocKet连接错误 msg: {}", throwable.getMessage(), throwable);
+        try {
+            this.onClose(session);
+        } catch (Exception e) {
+            log.error("webSocket 错误 onError", e);
+        }
+        if (throwable instanceof EOFException) {
+            log.info("WebSocket连接被客户端正常关闭(EOF),sessionId: {}", session.getId());
+        } else {
+            log.error("WebSocket连接错误", throwable);
+        }
     }
 
     /**
@@ -549,6 +580,19 @@ public class WebSocketServer {
                 data.put("couponTime", liveCoupon.getCouponTime());
                 msg.setData(JSON.toJSONString(data));
                 liveCouponMapper.updateChangeShow(task.getLiveId(), liveCouponIssue.getId());
+            } else if (task.getTaskType() == 6L) {
+                // 上架/下架商品
+                msg.setCmd("goods");
+                JSONObject jsonObject = JSON.parseObject(task.getContent());
+                Long goodsId = jsonObject.getLong("goodsId");
+                Integer status = jsonObject.getInteger("status");
+                if (goodsId == null || status == null) {
+                    log.error("商品ID或状态为空");
+                    return;
+                }
+                liveGoodsService.updateLiveGoodsStatus(goodsId, status);
+                return ;
+
             }
             msg.setStatus(1);
             broadcastMessage(task.getLiveId(), JSONObject.toJSONString(R.ok().put("data", msg)));
@@ -567,7 +611,7 @@ public class WebSocketServer {
     }
 
 
-    @Scheduled(fixedRate = 300)// 每分钟执行一次
+    @Scheduled(fixedRate = 300)// 每0.3分钟执行一次
     public void broadcastLikeMessage() {
         Set<Long> activeLiveIds = new HashSet<>();
         for (Map.Entry<Long, ConcurrentHashMap<Long, Session>> entry : rooms.entrySet()) {

+ 7 - 0
fs-service-system/src/main/java/com/fs/live/mapper/LiveGoodsMapper.java

@@ -149,4 +149,11 @@ public interface LiveGoodsMapper {
             "ELSE 0 END where live_id = #{liveId}" +
             "</script>"})
     void updateLiveIsShow(@Param("goodsId")Long goodsId,@Param("liveId") Long liveId);
+    /**
+     * 根据goodsId更新商品上下架状态
+     * @param goodsId 商品ID
+     * @param status 状态:1-上架 0-下架
+     */
+    @Update("update live_goods set status = #{status} where goods_id = #{goodsId}")
+    void updateLiveGoodsStatus(@Param("goodsId") Long goodsId, @Param("status") Integer status);
 }

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

@@ -53,7 +53,7 @@ public interface ILiveAutoTaskService {
      * @param liveAutoTask 直播间自动化任务配置
      * @return 结果
      */
-    int updateLiveAutoTask(LiveAutoTask liveAutoTask);
+    R updateLiveAutoTask(LiveAutoTask liveAutoTask);
 
     /**
      * 批量删除直播间自动化任务配置

+ 2 - 0
fs-service-system/src/main/java/com/fs/live/service/ILiveGoodsService.java

@@ -124,4 +124,6 @@ public interface ILiveGoodsService {
     void updateLiveIsShow(Long goodsId, Long liveId);
 
     R getStoreByLiveId(Long liveId, String key, String userId);
+
+    void updateLiveGoodsStatus(Long goodsId, Integer status);
 }

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

@@ -23,6 +23,8 @@ import com.fs.live.vo.LiveGoodsVo;
 import com.fs.live.vo.LiveLotteryConfVo;
 import com.fs.live.vo.LiveLotteryProductListVo;
 import org.checkerframework.checker.units.qual.A;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -35,6 +37,8 @@ import org.springframework.stereotype.Service;
 @Service
 public class LiveAutoTaskServiceImpl implements ILiveAutoTaskService {
 
+    private static final Logger log = LoggerFactory.getLogger(LiveAutoTaskServiceImpl.class);
+
     @Autowired
     private LiveMapper liveMapper;
     @Autowired
@@ -153,6 +157,30 @@ public class LiveAutoTaskServiceImpl implements ILiveAutoTaskService {
             liveCoupon.setGoodsId(liveCouponIssueRelation.getGoodsId());
             liveAutoTask.setContent(JSON.toJSONString(liveCoupon));
             baseMapper.insertLiveAutoTask(liveAutoTask);
+        } else if (liveAutoTask.getTaskType() == 6L) {
+            // 上架/下架商品
+            try {
+                com.alibaba.fastjson.JSONObject jsonObject = JSON.parseObject(liveAutoTask.getContent());
+                Long goodsId = jsonObject.getLong("goodsId");
+                Integer status = jsonObject.getInteger("status");
+                if (goodsId == null) return R.error("商品ID不能为空");
+                if (status == null) return R.error("状态不能为空");
+
+                // 查询商品信息
+                LiveGoodsVo liveGoodsVo = goodsService.selectLiveGoodsVoByGoodsId(goodsId);
+                if(liveGoodsVo == null) return R.error("商品不存在");
+
+                // 保存商品信息和上下架状态
+                com.alibaba.fastjson.JSONObject content = new com.alibaba.fastjson.JSONObject();
+                content.put("goodsId", liveGoodsVo.getGoodsId());
+                content.put("productId", liveGoodsVo.getProductId());
+                content.put("productName", liveGoodsVo.getProductName());
+                content.put("status", status);
+                liveAutoTask.setContent(content.toJSONString());
+                baseMapper.insertLiveAutoTask(liveAutoTask);
+            } catch (Exception e) {
+                return R.error("内容格式错误,应为{\"goodsId\":123,\"status\":1}");
+            }
         } else {
             return R.error("任务类型错误");
         }
@@ -224,51 +252,76 @@ public class LiveAutoTaskServiceImpl implements ILiveAutoTaskService {
      * @return 结果
      */
     @Override
-    public int updateLiveAutoTask(LiveAutoTask liveAutoTask)
+    public R updateLiveAutoTask(LiveAutoTask liveAutoTask)
     {
         LiveAutoTask existTask = baseMapper.selectLiveAutoTaskById(liveAutoTask.getId());
         redisCache.redisTemplate.opsForZSet().removeRangeByScore("live:auto_task:" + existTask.getLiveId(), existTask.getAbsValue().getTime(), existTask.getAbsValue().getTime());
         if (liveAutoTask.getTaskType() == 1L) {
             // 商品
             LiveGoodsVo liveGoodsVo = goodsService.selectLiveGoodsVoByGoodsId(Long.valueOf(liveAutoTask.getContent()));
-            if(liveGoodsVo == null) return -1;
+            if(liveGoodsVo == null) return R.error("商品不存在");
             liveAutoTask.setContent(JSON.toJSONString(liveGoodsVo));
-            return baseMapper.updateLiveAutoTask(liveAutoTask);
+            baseMapper.updateLiveAutoTask(liveAutoTask);
         }else if (liveAutoTask.getTaskType() == 2L) {
             // 红包
             LiveRedConf liveRedConf = liveRedConfMapper.selectLiveRedConfByRedId(Long.valueOf(liveAutoTask.getContent()));
-            if(liveRedConf == null) return -1;
-            if(liveRedConf.getRedStatus() != 0L) return -1;
+            if(liveRedConf == null) return R.error("红包不存在!");
+            if(liveRedConf.getRedStatus() != 0L) return R.error("红包已结束!");
             liveAutoTask.setContent(JSON.toJSONString(liveRedConf));
-            return baseMapper.updateLiveAutoTask(liveAutoTask);
+            baseMapper.updateLiveAutoTask(liveAutoTask);
         }else if (liveAutoTask.getTaskType() == 4L) {
             // 开启抽奖
             LiveLotteryConf liveLotteryConf = liveLotteryConfMapper.selectLiveLotteryConfByLotteryId(Long.valueOf(liveAutoTask.getContent()));
-            if(liveLotteryConf == null) return -1;
-            if(!"0".equals(liveLotteryConf.getLotteryStatus())) return -1;
+            if(liveLotteryConf == null) return R.error("抽奖不存在!");
+            if(!"0".equals(liveLotteryConf.getLotteryStatus())) return R.error("抽奖未开始!");
             List<LiveLotteryProduct> prizes = lotteryProductConfMapper.selectLiveLotteryProductConfByLotteryId(liveLotteryConf.getLotteryId());
             if (prizes == null || prizes.isEmpty()) {
-                return -1;
+                return R.error("请先添加奖品");
             }
             liveAutoTask.setContent(JSON.toJSONString(liveLotteryConf));
-            return baseMapper.updateLiveAutoTask(liveAutoTask);
+            baseMapper.updateLiveAutoTask(liveAutoTask);
         } else if(liveAutoTask.getTaskType() == 3L){
-            return baseMapper.updateLiveAutoTask(liveAutoTask);
-        }else if( liveAutoTask.getTaskType() == 5L){
+            baseMapper.updateLiveAutoTask(liveAutoTask);
+        } else if( liveAutoTask.getTaskType() == 5L){
             // 自动优惠券
             LiveCoupon liveCoupon = liveCouponMapper.selectLiveCouponById(Long.valueOf(liveAutoTask.getContent()));
-            if(liveCoupon == null) return -1;
+            if(liveCoupon == null) return R.error("优惠券不存在!");
             LiveCouponIssue liveCouponIssue = liveCouponIssueMapper.selectLiveCouponIssueByCouponId(liveCoupon.getCouponId());
-            if(liveCouponIssue == null)return -1;
+            if(liveCouponIssue == null)return R.error("未发布优惠券!");
             LiveCouponIssueRelation liveCouponIssueRelation = liveCouponMapper.selectCouponRelation(liveAutoTask.getLiveId(),liveCouponIssue.getId());
-            if(liveCouponIssueRelation == null) return -1;
-            if(ObjectUtil.isEmpty(liveCouponIssueRelation.getGoodsId())) return -1;
+            if(liveCouponIssueRelation == null) return R.error("未绑定商品,无法发布!");
+            if(ObjectUtil.isEmpty(liveCouponIssueRelation.getGoodsId())) return R.error("未绑定商品,无法发布!");
             liveCoupon.setGoodsId(liveCouponIssueRelation.getGoodsId());
             liveAutoTask.setContent(JSON.toJSONString(liveCoupon));
-            return baseMapper.updateLiveAutoTask(liveAutoTask);
+            baseMapper.updateLiveAutoTask(liveAutoTask);
+        } else if (liveAutoTask.getTaskType() == 6L) {
+            // 上架/下架商品
+            try {
+                com.alibaba.fastjson.JSONObject jsonObject = JSON.parseObject(liveAutoTask.getContent());
+                Long goodsId = jsonObject.getLong("goodsId");
+                Integer status = jsonObject.getInteger("status");
+                if (goodsId == null || status == null) return R.error("商品ID或状态为空");
+
+                // 查询商品信息
+                LiveGoodsVo liveGoodsVo = goodsService.selectLiveGoodsVoByGoodsId(goodsId);
+                if(liveGoodsVo == null) return R.error("商品不存在");
+
+                // 保存商品信息和上下架状态
+                com.alibaba.fastjson.JSONObject content = new com.alibaba.fastjson.JSONObject();
+                content.put("goodsId", liveGoodsVo.getGoodsId());
+                content.put("productId", liveGoodsVo.getProductId());
+                content.put("productName", liveGoodsVo.getProductName());
+                content.put("status", status);
+                liveAutoTask.setContent(content.toJSONString());
+                baseMapper.updateLiveAutoTask(liveAutoTask);
+            } catch (Exception e) {
+                log.error("上架/下架商品自动化任务,更新异常!" + e.getMessage());
+                return R.error("上架/下架商品自动化任务,更新异常!");
+            }
         } else {
-            return -1;
+            return R.error("任务类型错误");
         }
+        return R.ok("更新成功");
     }
 
     /**

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

@@ -174,6 +174,11 @@ public class LiveGoodsServiceImpl  implements ILiveGoodsService {
         return R.ok().put("data", liveGoodsVos);
     }
 
+    @Override
+    public void updateLiveGoodsStatus(Long goodsId, Integer status) {
+        baseMapper.updateLiveGoodsStatus(goodsId, status);
+    }
+
     /**
      * 批量新增直播商品
      *

+ 291 - 141
fs-service-system/src/main/java/com/fs/live/service/impl/LiveServiceImpl.java

@@ -698,24 +698,70 @@ public class LiveServiceImpl implements ILiveService
         if (exist == null) {
             return R.error("直播间不存在");
         }
-        // 复制直播间, 创建 livedata
-        // 身份认证
+
+        // 1. 复制直播间基础信息
+        Long newLiveId = copyLiveBasicInfo(exist, live, now);
+
+        // 2. 创建直播数据
+        createLiveData(newLiveId);
+
+        // 3. 复制直播视频
+        copyLiveVideos(existLiveId, newLiveId, now);
+
+        // 4. 获取所有自动化任务并按类型分组
+        List<LiveAutoTask> allTasks = liveAutoTaskService.selectLiveAutoTaskByLiveId(existLiveId);
+        Map<Long, List<LiveAutoTask>> tasksByType = allTasks.stream()
+                .collect(Collectors.groupingBy(LiveAutoTask::getTaskType));
+
+        // 5. 复制弹幕任务(taskType=3)
+        copyBarrageTasks(tasksByType.get(3L), newLiveId, now);
+
+        // 6. 复制红包配置及任务(taskType=2)
+        Map<Long, Long> redIdMapping = copyRedConfs(existLiveId, newLiveId, tasksByType.get(2L), now);
+
+        // 7. 复制抽奖配置及任务(taskType=4)
+        Map<Long, Long> lotteryIdMapping = copyLotteryConfs(existLiveId, newLiveId, tasksByType.get(4L), now);
+
+        // 8. 复制商品、商品任务、上下架任务及优惠券关系
+        Map<Long, Long> goodsIdMapping = copyGoodsAndTasks(existLiveId, newLiveId, live,
+                tasksByType.get(1L), tasksByType.get(6L), now);
+
+        // 9. 复制优惠券关系
+        copyCouponRelations(existLiveId, newLiveId, goodsIdMapping);
+
+        // 10. 复制优惠券自动化任务(taskType=5)
+        copyCouponTasks(tasksByType.get(5L), newLiveId, goodsIdMapping, now);
+
+        return R.ok("复制成功");
+    }
+    /**
+     * 复制直播间基础信息
+     */
+    private Long copyLiveBasicInfo(Live exist, Live live, Date now) {
         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);
+        liveEntity.setRtmpUrl(null);
+        liveEntity.setFlvHlsUrl(null);
+        liveEntity.setFinishTime(null);
+        liveEntity.setIsAudit(0);
+        liveEntity.setStatus(1);
+        liveEntity.setCreateTime(now);
         baseMapper.insertLive(liveEntity);
-        if(liveEntity.getLiveId() == null) throw new RuntimeException("插入直播间异常");
-        Long newLiveId = liveEntity.getLiveId();
+        if (liveEntity.getLiveId() == null) {
+            throw new RuntimeException("插入直播间异常");
+        }
+        return liveEntity.getLiveId();
+    }
+
+    /**
+     * 创建直播数据
+     */
+    private void createLiveData(Long liveId) {
         LiveData liveData = new LiveData();
-        liveData.setLiveId(newLiveId);
+        liveData.setLiveId(liveId);
         liveData.setPageViews(0L);
         liveData.setUniqueVisitors(0L);
         liveData.setTotalViews(0L);
@@ -725,167 +771,271 @@ public class LiveServiceImpl implements ILiveService
         liveData.setFavouriteNum(0L);
         liveData.setFollowNum(0L);
         liveDataService.insertLiveData(liveData);
-        // 优惠券
-        List<LiveCouponIssueRelation> liveCouponIssueRelations = liveCouponIssueMapper.selectRelationByLiveId(existLiveId);
-        // 直播视频
+    }
+
+    /**
+     * 复制直播视频
+     */
+    private void copyLiveVideos(Long existLiveId, Long newLiveId, Date now) {
         List<LiveVideo> liveVideos = liveVideoService.selectLiveVideosByLiveId(existLiveId);
-        if (!liveVideos.isEmpty()) {
-            for (LiveVideo liveVideo : liveVideos) {
-                LiveVideo videoEntity = new LiveVideo();
-                BeanUtils.copyBeanProp(videoEntity, liveVideo);
-                videoEntity.setVideoId(null);
-                videoEntity.setLiveId(newLiveId);
-                videoEntity.setCreateTime(now);
-                liveVideoService.insertLiveVideo(videoEntity);
-            }
+        for (LiveVideo liveVideo : liveVideos) {
+            LiveVideo videoEntity = new LiveVideo();
+            BeanUtils.copyBeanProp(videoEntity, liveVideo);
+            videoEntity.setVideoId(null);
+            videoEntity.setLiveId(newLiveId);
+            videoEntity.setCreateTime(now);
+            liveVideoService.insertLiveVideo(videoEntity);
         }
+    }
 
-        // 运营自动化
-        List<LiveAutoTask> liveAutoTasksList = liveAutoTaskService.selectLiveAutoTaskByLiveId(existLiveId);
-        List<LiveAutoTask> barrageTask = liveAutoTasksList.stream().filter(liveAutoTask -> liveAutoTask.getTaskType() == 3L).collect(Collectors.toList());
-        List<LiveAutoTask> goodsTaskList = liveAutoTasksList.stream().filter(liveAutoTask -> liveAutoTask.getTaskType() == 1L).collect(Collectors.toList());
-        List<LiveAutoTask> redTaskList = liveAutoTasksList.stream().filter(liveAutoTask -> liveAutoTask.getTaskType() == 2L).collect(Collectors.toList());
-        List<LiveAutoTask> lotteryTaskList = liveAutoTasksList.stream().filter(liveAutoTask -> liveAutoTask.getTaskType() == 4L).collect(Collectors.toList());
-        List<LiveAutoTask> couponTaskList = liveAutoTasksList.stream().filter(liveAutoTask -> liveAutoTask.getTaskType() == 5L).collect(Collectors.toList());
+    /**
+     * 复制弹幕任务(taskType=3)
+     */
+    private void copyBarrageTasks(List<LiveAutoTask> tasks, Long newLiveId, Date now) {
+        if (tasks == null || tasks.isEmpty()) {
+            return;
+        }
         List<LiveAutoTask> addList = new ArrayList<>();
-        if (!barrageTask.isEmpty()) {
-            for (LiveAutoTask liveAutoTask : barrageTask) {
-                LiveAutoTask liveAutoTaskEntity = new LiveAutoTask();
-                BeanUtils.copyBeanProp(liveAutoTaskEntity, liveAutoTask);
-                liveAutoTaskEntity.setId(null);
-                liveAutoTaskEntity.setLiveId(newLiveId);
-                liveAutoTaskEntity.setCreateTime(now);
-                liveAutoTaskEntity.setUpdateTime(now);
-                liveAutoTaskEntity.setFinishStatus(0L);
-                addList.add(liveAutoTaskEntity);
-                if (addList.size() > 100) {
-                    liveAutoTaskService.batchInsertLiveAutoTask(addList);
-                    addList.clear();
-                }
+        for (LiveAutoTask task : tasks) {
+            LiveAutoTask newTask = createAutoTaskEntity(task, newLiveId, now, null);
+            addList.add(newTask);
+            if (addList.size() >= 100) {
+                liveAutoTaskService.batchInsertLiveAutoTask(addList);
+                addList.clear();
             }
         }
         if (!addList.isEmpty()) {
             liveAutoTaskService.batchInsertLiveAutoTask(addList);
-            addList.clear();
         }
-        //直播间红包配置
+    }
+
+    /**
+     * 复制红包配置及任务
+     */
+    private Map<Long, Long> copyRedConfs(Long existLiveId, Long newLiveId,
+                                         List<LiveAutoTask> redTasks, Date now) {
+        Map<Long, Long> redIdMapping = new HashMap<>();
+        if (redTasks == null) {
+            redTasks = Collections.emptyList();
+        }
+        Map<Long, LiveAutoTask> redTaskMap = redTasks.stream()
+                .collect(Collectors.toMap(task -> parseIdFromContent(task.getContent(), "redId"),
+                        Function.identity(), (existing, replacement) -> existing));
+
         List<LiveRedConf> liveRedConfs = liveRedConfService.selectByLiveId(existLiveId);
-        if (!liveRedConfs.isEmpty()) {
-            for (LiveRedConf liveRedConf : liveRedConfs) {
-                LiveRedConf liveRedConfEntity = new LiveRedConf();
-                BeanUtils.copyBeanProp(liveRedConfEntity, liveRedConf);
-                liveRedConfEntity.setRedId(null);
-                liveRedConfEntity.setLiveId(newLiveId);
-                liveRedConfEntity.setRedStatus(0L);
-                liveRedConfEntity.setCreateTime(now);
-                liveRedConfEntity.setTotalSend(0L);
-                liveRedConfService.insertLiveRedConf(liveRedConfEntity);
-                LiveAutoTask liveAutoTask = redTaskList.stream().filter(item -> parseIdFromContent(item.getContent(), "redId").equals(liveRedConf.getRedId())).findFirst().orElse(null);
-                if(liveAutoTask == null) continue;
-                LiveAutoTask liveAutoTaskEntity = new LiveAutoTask();
-                BeanUtils.copyBeanProp(liveAutoTaskEntity, liveAutoTask);
-                liveAutoTaskEntity.setId(null);
-                liveAutoTaskEntity.setLiveId(newLiveId);
-                liveAutoTaskEntity.setCreateTime(now);
-                liveAutoTaskEntity.setUpdateTime(now);
-                liveAutoTaskEntity.setFinishStatus(0L);
-                liveAutoTaskEntity.setContent(JSON.toJSONString(liveRedConfEntity));
-                liveAutoTaskService.directInsertLiveAutoTask(liveAutoTaskEntity);
+        for (LiveRedConf liveRedConf : liveRedConfs) {
+            LiveRedConf newRedConf = new LiveRedConf();
+            BeanUtils.copyBeanProp(newRedConf, liveRedConf);
+            newRedConf.setRedId(null);
+            newRedConf.setLiveId(newLiveId);
+            newRedConf.setRedStatus(0L);
+            newRedConf.setCreateTime(now);
+            newRedConf.setTotalSend(0L);
+            liveRedConfService.insertLiveRedConf(newRedConf);
+
+            redIdMapping.put(liveRedConf.getRedId(), newRedConf.getRedId());
+
+            LiveAutoTask task = redTaskMap.get(liveRedConf.getRedId());
+            if (task != null) {
+                LiveAutoTask newTask = createAutoTaskEntity(task, newLiveId, now,
+                        JSON.toJSONString(newRedConf));
+                liveAutoTaskService.directInsertLiveAutoTask(newTask);
             }
         }
-        // 直播间礼物配置
+        return redIdMapping;
+    }
+
+    /**
+     * 复制抽奖配置及任务
+     */
+    private Map<Long, Long> copyLotteryConfs(Long existLiveId, Long newLiveId,
+                                             List<LiveAutoTask> lotteryTasks, Date now) {
+        Map<Long, Long> lotteryIdMapping = new HashMap<>();
+        if (lotteryTasks == null) {
+            lotteryTasks = Collections.emptyList();
+        }
+        Map<Long, LiveAutoTask> lotteryTaskMap = lotteryTasks.stream()
+                .collect(Collectors.toMap(task -> parseIdFromContent(task.getContent(), "lotteryId"),
+                        Function.identity(), (existing, replacement) -> existing));
+
         List<LiveLotteryConf> liveLotteryConfs = liveLotteryConfService.selectByLiveId(existLiveId);
         if (!liveLotteryConfs.isEmpty()) {
-
-            List<Long> lotteryIds = liveLotteryConfs.stream().map(LiveLotteryConf::getLotteryId).collect(Collectors.toList());
+            List<Long> lotteryIds = liveLotteryConfs.stream()
+                    .map(LiveLotteryConf::getLotteryId).collect(Collectors.toList());
             List<LiveLotteryProductConf> products = liveLotteryProductConfMapper.selectEntityByIds(lotteryIds);
+            Map<Long, List<LiveLotteryProductConf>> productsByLotteryId = products.stream()
+                    .collect(Collectors.groupingBy(LiveLotteryProductConf::getLotteryId));
+
             for (LiveLotteryConf liveLotteryConf : liveLotteryConfs) {
-                LiveLotteryConf liveLotteryConfEntity = new LiveLotteryConf();
-                BeanUtils.copyBeanProp(liveLotteryConfEntity, liveLotteryConf);
-                liveLotteryConfEntity.setLotteryId(null);
-                liveLotteryConfEntity.setLiveId(newLiveId);
-                liveLotteryConfEntity.setLotteryStatus(String.valueOf(0));
-                liveLotteryConfEntity.setCreateTime(now);
-                liveLotteryConfEntity.setUpdateTime(now);
-                liveLotteryConfService.insertLiveLotteryConf(liveLotteryConfEntity);
-                products.stream().filter(product -> product.getLotteryId().equals(liveLotteryConf.getLotteryId()))
-                        .forEach(product -> {
-                            product.setId(null);
-                            product.setLotteryId(liveLotteryConfEntity.getLotteryId());
-                            liveLotteryProductConfMapper.insertLiveLotteryProductConf(product);
-                        });
-                LiveAutoTask liveAutoTask = lotteryTaskList.stream().filter(item -> parseIdFromContent(item.getContent(), "lotteryId").equals(liveLotteryConf.getLotteryId())).findFirst().orElse(null);
-                if(liveAutoTask == null) continue;
-                LiveAutoTask liveAutoTaskEntity = new LiveAutoTask();
-                BeanUtils.copyBeanProp(liveAutoTaskEntity, liveAutoTask);
-                liveAutoTaskEntity.setId(null);
-                liveAutoTaskEntity.setLiveId(newLiveId);
-                liveAutoTaskEntity.setCreateTime(now);
-                liveAutoTaskEntity.setUpdateTime(now);
-                liveAutoTaskEntity.setFinishStatus(0L);
-                liveAutoTaskEntity.setContent(JSON.toJSONString(liveLotteryConfEntity));
-                liveAutoTaskService.directInsertLiveAutoTask(liveAutoTaskEntity);
+                LiveLotteryConf newLotteryConf = new LiveLotteryConf();
+                BeanUtils.copyBeanProp(newLotteryConf, liveLotteryConf);
+                newLotteryConf.setLotteryId(null);
+                newLotteryConf.setLiveId(newLiveId);
+                newLotteryConf.setLotteryStatus(String.valueOf(0));
+                newLotteryConf.setCreateTime(now);
+                newLotteryConf.setUpdateTime(now);
+                liveLotteryConfService.insertLiveLotteryConf(newLotteryConf);
+
+                lotteryIdMapping.put(liveLotteryConf.getLotteryId(), newLotteryConf.getLotteryId());
+
+                // 复制奖品
+                List<LiveLotteryProductConf> lotteryProducts = productsByLotteryId.get(liveLotteryConf.getLotteryId());
+                if (lotteryProducts != null) {
+                    for (LiveLotteryProductConf product : lotteryProducts) {
+                        LiveLotteryProductConf newProduct = new LiveLotteryProductConf();
+                        BeanUtils.copyBeanProp(newProduct, product);
+                        newProduct.setId(null);
+                        newProduct.setLotteryId(newLotteryConf.getLotteryId());
+                        liveLotteryProductConfMapper.insertLiveLotteryProductConf(newProduct);
+                    }
+                }
+
+                LiveAutoTask task = lotteryTaskMap.get(liveLotteryConf.getLotteryId());
+                if (task != null) {
+                    LiveAutoTask newTask = createAutoTaskEntity(task, newLiveId, now,
+                            JSON.toJSONString(newLotteryConf));
+                    liveAutoTaskService.directInsertLiveAutoTask(newTask);
+                }
             }
         }
+        return lotteryIdMapping;
+    }
+
+    /**
+     * 复制商品、商品任务、上下架任务
+     */
+    private Map<Long, Long> copyGoodsAndTasks(Long existLiveId, Long newLiveId, Live live,
+                                              List<LiveAutoTask> goodsTasks, List<LiveAutoTask> shelfTasks, Date now) {
+        Map<Long, Long> goodsIdMapping = new HashMap<>();
+        if (goodsTasks == null) {
+            goodsTasks = Collections.emptyList();
+        }
+        if (shelfTasks == null) {
+            shelfTasks = Collections.emptyList();
+        }
+
+        Map<Long, LiveAutoTask> goodsTaskMap = goodsTasks.stream()
+                .collect(Collectors.toMap(task -> parseIdFromContent(task.getContent(), "goodsId"),
+                        Function.identity(), (existing, replacement) -> existing));
+        Map<Long, LiveAutoTask> shelfTaskMap = shelfTasks.stream()
+                .collect(Collectors.toMap(task -> parseIdFromContent(task.getContent(), "goodsId"),
+                        Function.identity(), (existing, replacement) -> existing));
+
         LiveGoods queryParam = new LiveGoods();
         queryParam.setLiveId(existLiveId);
-        // 直播间商品
         List<LiveGoodsVo> goodsList = liveGoodsService.selectProductListByLiveId(queryParam);
+
         if (!goodsList.isEmpty()) {
-            List<String> goodsProductIds = goodsList.stream().map(item -> String.valueOf(item.getProductId())).collect(Collectors.toList());
-            Map<Long, FsStoreProduct> collect = fsStoreProductMapper.selectFsStoreProductByProductIds(goodsProductIds).stream().collect(Collectors.toMap(FsStoreProduct::getProductId, item -> item));
+            List<String> goodsProductIds = goodsList.stream()
+                    .map(LiveGoodsVo::getProductId).map(String::valueOf).collect(Collectors.toList());
+            Map<Long, FsStoreProduct> goodsMap = fsStoreProductMapper
+                    .selectFsStoreProductByProductIds(goodsProductIds).stream()
+                    .collect(Collectors.toMap(FsStoreProduct::getProductId, Function.identity()));
+
             for (LiveGoodsVo liveGoods : goodsList) {
-                LiveGoods liveGoodsEntity = new LiveGoods();
-                BeanUtils.copyBeanProp(liveGoodsEntity, liveGoods);
-                liveGoodsEntity.setGoodsId(null);
-                liveGoodsEntity.setLiveId(newLiveId);
-                liveGoodsEntity.setCreateTime(now);
-                liveGoodsEntity.setIsShow(false);
-                liveGoodsEntity.setStock(collect.containsKey(liveGoods.getProductId()) ? collect.get(liveGoods.getProductId()).getStock() : 0);
-                liveGoodsEntity.setCompanyId(live.getCompanyId());
-                liveGoodsEntity.setCompanyUserId(live.getCompanyUserId());
-                liveGoodsService.insertLiveGoods(liveGoodsEntity);
-                // 优惠券
-                liveCouponIssueRelations.stream().filter(relation -> liveGoods.getGoodsId().equals(relation.getGoodsId())).findFirst().ifPresent(liveCouponIssueRelation -> liveCouponIssueRelation.setGoodsId(liveGoodsEntity.getGoodsId()));
-                LiveAutoTask liveAutoTask = goodsTaskList.stream().filter(item -> parseIdFromContent(item.getContent(), "goodsId").equals(liveGoods.getGoodsId())).findFirst().orElse(null);
-                // 优惠券定时任务
-                LiveAutoTask couponTask = couponTaskList.stream().filter(item -> parseIdFromContent(item.getContent(), "goodsId").equals(liveGoods.getGoodsId())).findFirst().orElse(null);
-                if (couponTask != null) {
-                    LiveAutoTask tempTask = new LiveAutoTask();
-                    BeanUtils.copyBeanProp(tempTask, couponTask);
-                    tempTask.setId(null);
-                    tempTask.setLiveId(newLiveId);
-                    tempTask.setCreateTime(now);
-                    tempTask.setUpdateTime(now);
-                    tempTask.setFinishStatus(0L);
-                    LiveCoupon liveCoupon = JSON.parseObject(tempTask.getContent(), LiveCoupon.class);
-                    liveCoupon.setGoodsId(liveGoodsEntity.getGoodsId());
-                    tempTask.setContent(JSON.toJSONString(liveCoupon));
-                    liveAutoTaskService.directInsertLiveAutoTask(tempTask);
+                LiveGoods newGoods = new LiveGoods();
+                BeanUtils.copyBeanProp(newGoods, liveGoods);
+                newGoods.setGoodsId(null);
+                newGoods.setLiveId(newLiveId);
+                newGoods.setCreateTime(now);
+                newGoods.setIsShow(false);
+                newGoods.setCompanyId(live.getCompanyId());
+                newGoods.setCompanyUserId(live.getCompanyUserId());
+                newGoods.setStock(goodsMap.containsKey(liveGoods.getProductId())
+                        ? goodsMap.get(liveGoods.getProductId()).getStock() : 0);
+                liveGoodsService.insertLiveGoods(newGoods);
+
+                goodsIdMapping.put(liveGoods.getGoodsId(), newGoods.getGoodsId());
+
+                // 复制商品推送任务(taskType=1)
+                LiveAutoTask goodsTask = goodsTaskMap.get(liveGoods.getGoodsId());
+                if (goodsTask != null) {
+                    // 使用新的goodsId创建LiveGoodsVo对象
+                    LiveGoodsVo newGoodsVo = new LiveGoodsVo();
+                    BeanUtils.copyBeanProp(newGoodsVo, liveGoods);
+                    newGoodsVo.setGoodsId(newGoods.getGoodsId());
+                    LiveAutoTask newTask = createAutoTaskEntity(goodsTask, newLiveId, now,
+                            JSON.toJSONString(newGoodsVo));
+                    liveAutoTaskService.directInsertLiveAutoTask(newTask);
                 }
-                if(liveAutoTask == null) continue;
-                liveGoods.setGoodsId(liveGoodsEntity.getGoodsId());
-                LiveAutoTask liveAutoTaskEntity = new LiveAutoTask();
-                BeanUtils.copyBeanProp(liveAutoTaskEntity, liveAutoTask);
-                liveAutoTaskEntity.setId(null);
-                liveAutoTaskEntity.setLiveId(newLiveId);
-                liveAutoTaskEntity.setCreateTime(now);
-                liveAutoTaskEntity.setUpdateTime(now);
-                liveAutoTaskEntity.setFinishStatus(0L);
-                liveAutoTaskEntity.setContent(JSON.toJSONString(liveGoods));
-                liveAutoTaskService.directInsertLiveAutoTask(liveAutoTaskEntity);
 
+                // 复制上下架任务(taskType=6)
+                LiveAutoTask shelfTask = shelfTaskMap.get(liveGoods.getGoodsId());
+                if (shelfTask != null) {
+                    JSONObject contentJson = JSON.parseObject(shelfTask.getContent());
+                    contentJson.put("goodsId", newGoods.getGoodsId());
+                    LiveAutoTask newTask = createAutoTaskEntity(shelfTask, newLiveId, now,
+                            contentJson.toJSONString());
+                    liveAutoTaskService.directInsertLiveAutoTask(newTask);
+                }
             }
         }
-        for (LiveCouponIssueRelation liveCouponIssueRelation : liveCouponIssueRelations) {
-            liveCouponIssueRelation.setLiveId(newLiveId);
-            liveCouponIssueRelation.setIsShow(0);
-            liveCouponIssueMapper.insertLiveCouponIssueRelation(liveCouponIssueRelation);
+        return goodsIdMapping;
+    }
+
+    /**
+     * 复制优惠券关系
+     */
+    private void copyCouponRelations(Long existLiveId, Long newLiveId, Map<Long, Long> goodsIdMapping) {
+        List<LiveCouponIssueRelation> relations = liveCouponIssueMapper.selectRelationByLiveId(existLiveId);
+        for (LiveCouponIssueRelation relation : relations) {
+            LiveCouponIssueRelation newRelation = new LiveCouponIssueRelation();
+            BeanUtils.copyBeanProp(newRelation, relation);
+            newRelation.setLiveId(newLiveId);
+            newRelation.setIsShow(0);
+            // 更新goodsId映射
+            if (relation.getGoodsId() != null && goodsIdMapping.containsKey(relation.getGoodsId())) {
+                newRelation.setGoodsId(goodsIdMapping.get(relation.getGoodsId()));
+            }
+            liveCouponIssueMapper.insertLiveCouponIssueRelation(newRelation);
         }
+    }
 
-        return R.ok("复制成功");
+    /**
+     * 复制优惠券自动化任务(taskType=5)
+     */
+    private void copyCouponTasks(List<LiveAutoTask> couponTasks, Long newLiveId,
+                                 Map<Long, Long> goodsIdMapping, Date now) {
+        if (couponTasks == null || couponTasks.isEmpty()) {
+            return;
+        }
+        for (LiveAutoTask task : couponTasks) {
+            try {
+                LiveCoupon liveCoupon = JSON.parseObject(task.getContent(), LiveCoupon.class);
+                if (liveCoupon != null && liveCoupon.getGoodsId() != null) {
+                    // 更新content中的goodsId为新的goodsId
+                    Long newGoodsId = goodsIdMapping.get(liveCoupon.getGoodsId());
+                    if (newGoodsId != null) {
+                        liveCoupon.setGoodsId(newGoodsId);
+                        LiveAutoTask newTask = createAutoTaskEntity(task, newLiveId, now,
+                                JSON.toJSONString(liveCoupon));
+                        liveAutoTaskService.directInsertLiveAutoTask(newTask);
+                    }
+                }
+            } catch (Exception e) {
+                log.error("复制优惠券自动化任务失败,taskId: {}", task.getId(), e);
+            }
+        }
     }
 
+    /**
+     * 创建自动化任务实体
+     */
+    private LiveAutoTask createAutoTaskEntity(LiveAutoTask source, Long newLiveId, Date now, String content) {
+        LiveAutoTask newTask = new LiveAutoTask();
+        BeanUtils.copyBeanProp(newTask, source);
+        newTask.setId(null);
+        newTask.setLiveId(newLiveId);
+        newTask.setCreateTime(now);
+        newTask.setUpdateTime(now);
+        newTask.setFinishStatus(0L);
+        if (content != null) {
+            newTask.setContent(content);
+        }
+        return newTask;
+    }
+
+
     private Long parseIdFromContent(String content, String key) {
         // 假设content是JSON字符串,使用FastJSON解析
         JSONObject jsonObject = JSON.parseObject(content);

+ 53 - 0
fs-service-system/src/main/java/com/fs/live/vo/LiveMsgVo.java

@@ -0,0 +1,53 @@
+package com.fs.live.vo;
+
+
+
+
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+/**
+ * 直播讨论对象 live_msg
+ *
+ * @author fs
+ * @date 2025-01-17
+ */
+@Data
+public class LiveMsgVo {
+
+    /** id */
+
+    private Long msgId;
+
+    /** 直播ID */
+    @Excel(name = "直播ID")
+    private Long liveId;
+
+    /** 用户ID */
+    @Excel(name = "用户ID")
+    private Long userId;
+
+    /** 用户ID */
+    @Excel(name = "用户ID")
+    private String nickName;
+
+    /** 用户ID */
+    @Excel(name = "用户ID")
+    private String avatar;
+
+    /** 消息 */
+    @Excel(name = "消息")
+    private String msg;
+
+    private Integer singleVisible;
+
+    /** 直播消息标记:0-否 1-是 */
+    @Excel(name = "直播消息标记")
+    private Integer liveFlag = 0;
+
+    /** 回放消息标记:0-否 1-是 */
+    @Excel(name = "回放消息标记")
+    private Integer replayFlag = 0;
+
+    private String endTime;
+}

+ 8 - 0
fs-user-app/src/main/java/com/fs/app/facade/impl/LiveFacadeServiceImpl.java

@@ -23,6 +23,7 @@ import com.fs.live.param.RedPO;
 import com.fs.live.service.*;
 import com.fs.live.vo.LiveConfigVo;
 import com.fs.live.vo.LiveLotteryConfVo;
+import com.fs.live.vo.LiveMsgVo;
 import com.fs.live.vo.LiveWatchUserVO;
 import com.github.pagehelper.PageInfo;
 import lombok.extern.slf4j.Slf4j;
@@ -153,6 +154,13 @@ public class LiveFacadeServiceImpl extends BaseController implements LiveFacadeS
                 .stream()
                 .filter(item -> ObjectUtil.isEmpty(redisUtil.hashGet(String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG_RED, liveId, item.getLotteryId()), userId)))
                 .collect(Collectors.toList());
+        Object cacheObject = redisUtil.get(String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG, liveId, LiveKeysConstant.TOP_MSG));
+        LiveMsgVo liveMsg;
+        if (ObjectUtil.isNotEmpty(cacheObject)) {
+            liveMsg = JSON.parseObject(cacheObject.toString(), LiveMsgVo.class);
+        } else {
+            liveMsg = null;
+        }
         return R.ok()
                 .put("red", redConfs)
                 .put("lottery", lotteryConfs)