소스 검색

直播录播进度上报

xw 1 일 전
부모
커밋
a79ef506f5

+ 39 - 0
fs-live-app/src/main/java/com/fs/app/controller/live/LivePersonalizedPushController.java

@@ -0,0 +1,39 @@
+package com.fs.app.controller.live;
+
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.R;
+import com.fs.live.param.LiveWatchProgressParam;
+import com.fs.live.service.ILivePersonalizedPushService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.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;
+
+/**
+ * 直播个性化推送Controller
+ *
+ * @author fs
+ * @date 2025-01-19
+ */
+@Api(tags = "直播录播进度上报")
+@RestController
+@RequestMapping("/app/live/replay")
+@Slf4j
+public class LivePersonalizedPushController extends BaseController {
+
+    @Autowired
+    private ILivePersonalizedPushService personalizedPushService;
+
+    /**
+     * 上报观看进度并获取需要展示的任务
+     */
+    @ApiOperation("上报观看进度")
+    @PostMapping("/reportProgress")
+    public R reportProgress(@RequestBody LiveWatchProgressParam param) {
+        return personalizedPushService.reportProgressAndGetTasks(param);
+    }
+}

+ 28 - 0
fs-service/src/main/java/com/fs/live/param/LiveWatchProgressParam.java

@@ -0,0 +1,28 @@
+package com.fs.live.param;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * 直播观看进度上报参数
+ *
+ * @author 夏伟
+ * @date 2025-01-19
+ */
+@Data
+@ApiModel("直播观看进度上报参数")
+public class LiveWatchProgressParam {
+
+    @ApiModelProperty("直播ID")
+    private Long liveId;
+
+    @ApiModelProperty("用户ID")
+    private Long userId;
+
+    @ApiModelProperty("当前观看进度(秒)")
+    private Long currentProgress;
+
+    @ApiModelProperty("录播标识 0-直播 1-录播")
+    private Integer replayFlag;
+}

+ 21 - 0
fs-service/src/main/java/com/fs/live/service/ILivePersonalizedPushService.java

@@ -0,0 +1,21 @@
+package com.fs.live.service;
+
+import com.fs.common.core.domain.R;
+import com.fs.live.param.LiveWatchProgressParam;
+
+/**
+ * 直播个性化推送Service接口
+ *
+ * @author 夏伟
+ * @date 2025-01-19
+ */
+public interface ILivePersonalizedPushService {
+
+    /**
+     * 上报观看进度并获取需要展示的任务
+     *
+     * @param param 观看进度参数
+     * @return 返回需要展示的任务列表
+     */
+    R reportProgressAndGetTasks(LiveWatchProgressParam param);
+}

+ 167 - 0
fs-service/src/main/java/com/fs/live/service/impl/LivePersonalizedPushServiceImpl.java

@@ -0,0 +1,167 @@
+package com.fs.live.service.impl;
+
+import com.alibaba.fastjson.JSONObject;
+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.param.LiveWatchProgressParam;
+import com.fs.live.service.ILivePersonalizedPushService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.time.LocalTime;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+/**
+ * 直播个性化推送Service实现
+ *
+ * @author 夏伟
+ * @date 2025-01-19
+ */
+@Slf4j
+@Service
+public class LivePersonalizedPushServiceImpl implements ILivePersonalizedPushService {
+
+    @Autowired
+    private RedisCache redisCache;
+
+    @Autowired
+    private LiveAutoTaskMapper liveAutoTaskMapper;
+
+    @Autowired
+    private LiveMapper liveMapper;
+
+    private static final String PROGRESS_KEY_PREFIX = "live:user:progress:";
+    private static final String PUSHED_TASKS_KEY_PREFIX = "live:user:pushed:";
+
+    /**
+     * 上报观看进度并获取需要展示的任务
+     */
+    @Override
+    public R reportProgressAndGetTasks(LiveWatchProgressParam param) {
+        try {
+            if (param.getLiveId() == null || param.getUserId() == null ||
+                param.getCurrentProgress() == null || param.getReplayFlag() == null) {
+                return R.error("参数不完整");
+            }
+
+            if (param.getReplayFlag() == 0) {
+                return R.ok();
+            }
+
+            Live live = liveMapper.selectLiveByLiveId(param.getLiveId());
+            if (live == null) {
+                return R.error("直播间不存在");
+            }
+
+            if (!(live.getLiveType() == 2 || (live.getLiveType() == 3 && live.getStatus() == 4))) {
+                return R.ok();
+            }
+
+            // 上次的观看时长
+            String progressKey = buildProgressKey(param.getLiveId(), param.getUserId(), param.getReplayFlag());
+            Long lastProgress = redisCache.getCacheObject(progressKey);
+
+            if (lastProgress != null && param.getCurrentProgress() < lastProgress) {
+                return R.ok();
+            }
+
+            redisCache.setCacheObject(progressKey, param.getCurrentProgress(), 2, TimeUnit.HOURS);
+
+            LiveAutoTask queryTask = new LiveAutoTask();
+            queryTask.setLiveId(param.getLiveId());
+            queryTask.setStatus(1L);
+            List<LiveAutoTask> allTasks = liveAutoTaskMapper.selectLiveAutoTaskList(queryTask);
+
+            if (allTasks == null || allTasks.isEmpty()) {
+                return R.ok();
+            }
+
+            List<JSONObject> tasksToShow = new ArrayList<>();
+            Long currentProgress = param.getCurrentProgress();
+            String pushedKey = buildPushedKey(param.getLiveId(), param.getUserId(), param.getReplayFlag());
+
+            for (LiveAutoTask task : allTasks) {
+                Long taskTriggerSeconds = calculateTaskTriggerSeconds(task);
+                if (taskTriggerSeconds == null) {
+                    continue;
+                }
+
+                if ((lastProgress == null || lastProgress < taskTriggerSeconds) &&
+                    currentProgress >= taskTriggerSeconds) {
+
+                    String taskKey = task.getId().toString();
+                    if (redisCache.hashGet(pushedKey, taskKey) == null) {
+
+                        redisCache.hashPut(pushedKey, taskKey, "1");
+
+                        JSONObject taskData = new JSONObject();
+                        taskData.put("taskId", task.getId());
+                        taskData.put("taskType", task.getTaskType());
+                        taskData.put("taskName", task.getTaskName());
+                        taskData.put("content", task.getContent());
+                        taskData.put("triggerProgress", taskTriggerSeconds);
+
+                        tasksToShow.add(taskData);
+                    }
+                }
+            }
+
+            if (!tasksToShow.isEmpty()) {
+                redisCache.expire(pushedKey, 2, TimeUnit.HOURS);
+            }
+
+            return R.ok().put("tasks", tasksToShow);
+
+        } catch (Exception e) {
+            log.error("上报观看进度失败,liveId: {}, userId: {}",
+                param.getLiveId(), param.getUserId(), e);
+            return R.error("系统异常");
+        }
+    }
+
+    /**
+     * 计算任务的触发进度(秒)
+     */
+    private Long calculateTaskTriggerSeconds(LiveAutoTask task) {
+        try {
+            if (task.getTriggerValue() == null) {
+                return null;
+            }
+
+            Date triggerValue = task.getTriggerValue();
+
+            LocalTime triggerTime = triggerValue.toInstant()
+                .atZone(ZoneId.systemDefault())
+                .toLocalTime();
+
+            return (long) triggerTime.toSecondOfDay();
+        } catch (Exception e) {
+            log.error("计算任务触发时间失败,taskId: {}", task.getId(), e);
+            return null;
+        }
+    }
+
+    /**
+     * 构建进度缓存Key
+     */
+    private String buildProgressKey(Long liveId, Long userId, Integer replayFlag) {
+        return String.format("%s%d:%d:%d", PROGRESS_KEY_PREFIX, liveId, userId, replayFlag);
+    }
+
+    /**
+     * 构建已推送任务缓存Key
+     */
+    private String buildPushedKey(Long liveId, Long userId, Integer replayFlag) {
+        return String.format("%s%d:%d:%d", PUSHED_TASKS_KEY_PREFIX, liveId, userId, replayFlag);
+    }
+}