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