fix(playback): 恢复已删除的观看进度

This commit is contained in:
Zhou Weidong committed 2026-10-23 12:34:13 +08:00
1 parent 5a80e6693f
commit 9c25b1b397
4 files changed
+111 -20

No files matched your search

@@ -4,6 +4,32 @@ package com.fruitshorts.playback.dal.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.fruitshorts.playback.dal.entity.WatchProgress;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
public interface WatchProgressMapper extends BaseMapper<WatchProgress> {
/** 含逻辑删除行。唯一索引不区分 deleted,再看同一部剧必须能找回旧行。 */
@Select("""
SELECT * FROM watch_progress
WHERE user_id = #{userId} AND drama_id = #{dramaId}
LIMIT 1
""")
WatchProgress selectAny(@Param("userId") Long userId, @Param("dramaId") Long dramaId);
/** 恢复并更新。手写 SQL 不带 deleted=0 条件,逻辑删除行也能写回。 */
@Update("""
UPDATE watch_progress SET
deleted = 0,
episode_id = #{episodeId},
position_ms = #{positionMs},
duration_ms = #{durationMs},
finished = #{finished},
play_count = #{playCount},
device_id = #{deviceId},
source = #{source}
WHERE id = #{id}
""")
int updateAny(WatchProgress progress);
}
@@ -17,6 +17,7 @@ import com.fruitshorts.playback.dal.mapper.WatchProgressMapper;
import com.fruitshorts.playback.vo.ProgressRespVO;
import com.fruitshorts.playback.vo.ProgressSaveReqVO;
import lombok.RequiredArgsConstructor;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -58,30 +59,23 @@ public class WatchProgressService {
boolean finished = Boolean.TRUE.equals(req.getFinished())
|| (duration > 0 && position + FINISH_THRESHOLD_MS >= duration);
WatchProgress existing = progressMapper.selectOne(Wrappers.<WatchProgress>lambdaQuery()
.eq(WatchProgress::getUserId, userId)
.eq(WatchProgress::getDramaId, drama.getId())
.last("limit 1"));
if (existing == null) {
WatchProgress progress = new WatchProgress();
progress.setUserId(userId);
progress.setDramaId(drama.getId());
progress.setEpisodeId(episode.getId());
progress.setPositionMs(position);
progress.setDurationMs(duration);
progress.setFinished(finished ? 1 : 0);
progress.setPlayCount(1);
progress.setDeviceId(req.getDeviceId() == null ? "" : req.getDeviceId());
progress.setSource(req.getSource() == null ? "" : req.getSource());
progressMapper.insert(progress);
LoadedProgress loaded = loadForSave(userId, drama.getId(), episode, position, duration,
finished, req);
WatchProgress existing = loaded.progress();
if (loaded.created()) {
appendHistory(userId, drama.getId(), episode.getId(), position, duration,
progress.getSource(), progress.getDeviceId());
// updated_at 由触发器维护,回读一次,保证响应与后续 recent 列表口径一致
return toResp(progressMapper.selectById(progress.getId()));
existing.getSource(), existing.getDeviceId());
return toResp(progressMapper.selectById(existing.getId()));
}
boolean hidden = existing.getDeleted() != null && existing.getDeleted() != 0;
boolean sameEpisode = existing.getEpisodeId() != null
&& existing.getEpisodeId().equals(episode.getId());
if (hidden && sameEpisode) {
existing.setPlayCount((existing.getPlayCount() == null ? 0 : existing.getPlayCount()) + 1);
appendHistory(userId, drama.getId(), episode.getId(), position, duration,
req.getSource(), existing.getDeviceId());
}
if (!sameEpisode) {
// 换集:直接采纳新集与新的播放位置
existing.setEpisodeId(episode.getId());
@@ -103,10 +97,52 @@ public class WatchProgressService {
}
existing.setDeviceId(req.getDeviceId() == null ? "" : req.getDeviceId());
existing.setSource(req.getSource() == null ? "" : req.getSource());
progressMapper.updateById(existing);
if (hidden) {
existing.setDeleted(0);
progressMapper.updateAny(existing);
} else {
progressMapper.updateById(existing);
}
return toResp(progressMapper.selectById(existing.getId()));
}
private LoadedProgress loadForSave(Long userId, Long dramaId, DramaEpisode episode, long position,
long duration, boolean finished, ProgressSaveReqVO req) {
WatchProgress existing = progressMapper.selectAny(userId, dramaId);
if (existing != null) {
return new LoadedProgress(existing, false);
}
WatchProgress created = newProgress(userId, dramaId, episode, position, duration, finished, req);
try {
progressMapper.insert(created);
return new LoadedProgress(created, true);
} catch (DuplicateKeyException ex) {
WatchProgress raced = progressMapper.selectAny(userId, dramaId);
if (raced == null) {
throw ex;
}
return new LoadedProgress(raced, false);
}
}
private WatchProgress newProgress(Long userId, Long dramaId, DramaEpisode episode, long position,
long duration, boolean finished, ProgressSaveReqVO req) {
WatchProgress progress = new WatchProgress();
progress.setUserId(userId);
progress.setDramaId(dramaId);
progress.setEpisodeId(episode.getId());
progress.setPositionMs(position);
progress.setDurationMs(duration);
progress.setFinished(finished ? 1 : 0);
progress.setPlayCount(1);
progress.setDeviceId(req.getDeviceId() == null ? "" : req.getDeviceId());
progress.setSource(req.getSource() == null ? "" : req.getSource());
return progress;
}
private record LoadedProgress(WatchProgress progress, boolean created) {
}
public ProgressRespVO ofDrama(Long userId, Long dramaId) {
WatchProgress progress = progressMapper.selectOne(Wrappers.<WatchProgress>lambdaQuery()
.eq(WatchProgress::getUserId, userId)
@@ -185,6 +221,7 @@ public class WatchProgressService {
vo.setDramaTitle(drama.getTitle());
vo.setDramaCover(drama.getCoverUrl());
vo.setTotalEpisodes(drama.getTotalEpisodes());
vo.setViewCount(drama.getViewCount());
}
DramaEpisode episode = episodeById.get(progress.getEpisodeId());
if (episode != null) {
@@ -20,6 +20,8 @@ public class ProgressRespVO {
private Integer episodeNumber;
/** 短剧总集数;短剧不存在时为空。 */
private Integer totalEpisodes;
/** 短剧热度,取 drama.view_count。不是用户自己的播放次数。 */
private Long viewCount;
private Long positionMs;
private Long durationMs;
private Boolean finished;