feat(app): 收藏与观看历史支持分页
This commit is contained in:
1 parent
ec15950851
commit
71b0c7315a
11 files changed
+672
-37
No files matched your search
+32
-16
@@ -2,8 +2,12 @@
|
||||
|
||||
package com.fruitshorts.playback.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.fruitshorts.common.pojo.PageResult;
|
||||
import com.fruitshorts.content.dal.entity.Drama;
|
||||
import com.fruitshorts.content.enums.ContentStatus;
|
||||
import com.fruitshorts.content.service.DramaService;
|
||||
import com.fruitshorts.playback.dal.entity.UserFavorite;
|
||||
import com.fruitshorts.playback.dal.mapper.UserFavoriteMapper;
|
||||
@@ -13,13 +17,17 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/** 收藏:切换、列表、批量删除;收藏数在同一事务内同步维护。 */
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class FavoriteService {
|
||||
|
||||
private static final int MAX_PAGE_SIZE = 100;
|
||||
|
||||
private final UserFavoriteMapper favoriteMapper;
|
||||
private final DramaService dramaService;
|
||||
|
||||
@@ -55,30 +63,38 @@ public class FavoriteService {
|
||||
return count != null && count > 0;
|
||||
}
|
||||
|
||||
public List<Long> dramaIds(Long userId) {
|
||||
return favoriteMapper.selectList(Wrappers.<UserFavorite>lambdaQuery()
|
||||
/** 分页查询收藏短剧,保持收藏时间与 dramaId 稳定排序。 */
|
||||
public PageResult<Drama> page(Long userId, int pageIndex, int pageSize) {
|
||||
int safeIndex = Math.max(pageIndex, 1);
|
||||
int safeSize = Math.min(Math.max(pageSize, 1), MAX_PAGE_SIZE);
|
||||
Page<UserFavorite> request = new Page<>(safeIndex, safeSize);
|
||||
String publishedDramaSql = "SELECT id FROM drama WHERE deleted = 0 AND status = '"
|
||||
+ ContentStatus.PUBLISHED.getCode() + "'";
|
||||
IPage<UserFavorite> result = favoriteMapper.selectPage(request,
|
||||
Wrappers.<UserFavorite>lambdaQuery()
|
||||
.eq(UserFavorite::getUserId, userId)
|
||||
.orderByDesc(UserFavorite::getCreatedAt))
|
||||
.stream().map(UserFavorite::getDramaId).toList();
|
||||
}
|
||||
|
||||
public List<Drama> listDramas(Long userId) {
|
||||
List<Long> ids = dramaIds(userId);
|
||||
.inSql(UserFavorite::getDramaId, publishedDramaSql)
|
||||
.orderByDesc(UserFavorite::getCreatedAt)
|
||||
.orderByDesc(UserFavorite::getDramaId));
|
||||
List<Long> ids = result.getRecords().stream()
|
||||
.map(UserFavorite::getDramaId)
|
||||
.toList();
|
||||
if (ids.isEmpty()) {
|
||||
return List.of();
|
||||
return new PageResult<>(result.getTotal(), List.of());
|
||||
}
|
||||
List<Drama> dramas = dramaService.listPublishedByIds(ids);
|
||||
// 按收藏时间排序,已被下架的剧自然被过滤掉
|
||||
Map<Long, Drama> dramaById = new HashMap<>();
|
||||
for (Drama drama : dramas) {
|
||||
dramaById.put(drama.getId(), drama);
|
||||
}
|
||||
List<Drama> ordered = new ArrayList<>(dramas.size());
|
||||
for (Long id : ids) {
|
||||
for (Drama drama : dramas) {
|
||||
if (drama.getId().equals(id)) {
|
||||
ordered.add(drama);
|
||||
break;
|
||||
}
|
||||
Drama drama = dramaById.get(id);
|
||||
if (drama != null) {
|
||||
ordered.add(drama);
|
||||
}
|
||||
}
|
||||
return ordered;
|
||||
return new PageResult<>(result.getTotal(), ordered);
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
|
||||
+66
-10
@@ -2,13 +2,16 @@
|
||||
|
||||
package com.fruitshorts.playback.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.fruitshorts.common.pojo.PageResult;
|
||||
import com.fruitshorts.content.dal.entity.Drama;
|
||||
import com.fruitshorts.content.dal.entity.DramaEpisode;
|
||||
import com.fruitshorts.content.service.DramaService;
|
||||
import com.fruitshorts.content.service.EpisodeService;
|
||||
import com.fruitshorts.playback.dal.entity.WatchProgress;
|
||||
import com.fruitshorts.playback.dal.entity.WatchHistory;
|
||||
import com.fruitshorts.playback.dal.entity.WatchProgress;
|
||||
import com.fruitshorts.playback.dal.mapper.WatchHistoryMapper;
|
||||
import com.fruitshorts.playback.dal.mapper.WatchProgressMapper;
|
||||
import com.fruitshorts.playback.vo.ProgressRespVO;
|
||||
@@ -19,7 +22,10 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 观看进度。
|
||||
@@ -36,6 +42,8 @@ import java.util.List;
|
||||
public class WatchProgressService {
|
||||
|
||||
private static final long FINISH_THRESHOLD_MS = 1000L;
|
||||
private static final int DEFAULT_PAGE_SIZE = 20;
|
||||
private static final int MAX_PAGE_SIZE = 100;
|
||||
|
||||
private final WatchProgressMapper progressMapper;
|
||||
private final WatchHistoryMapper historyMapper;
|
||||
@@ -110,15 +118,21 @@ public class WatchProgressService {
|
||||
|
||||
/** 最近观看列表("我的-观看记录")。 */
|
||||
public List<ProgressRespVO> recent(Long userId, int limit) {
|
||||
List<WatchProgress> list = progressMapper.selectList(Wrappers.<WatchProgress>lambdaQuery()
|
||||
.eq(WatchProgress::getUserId, userId)
|
||||
.orderByDesc(WatchProgress::getUpdatedAt)
|
||||
.last("limit " + (limit <= 0 || limit > 100 ? 20 : limit)));
|
||||
List<ProgressRespVO> result = new ArrayList<>(list.size());
|
||||
for (WatchProgress item : list) {
|
||||
result.add(toResp(item));
|
||||
}
|
||||
return result;
|
||||
int safeLimit = limit <= 0 || limit > MAX_PAGE_SIZE ? DEFAULT_PAGE_SIZE : limit;
|
||||
return recentPage(userId, 1, safeLimit).getList();
|
||||
}
|
||||
|
||||
/** 分页查询最近观看,按更新时间与 dramaId 稳定倒序。 */
|
||||
public PageResult<ProgressRespVO> recentPage(Long userId, int pageIndex, int pageSize) {
|
||||
int safeIndex = Math.max(pageIndex, 1);
|
||||
int safeSize = Math.min(Math.max(pageSize, 1), MAX_PAGE_SIZE);
|
||||
Page<WatchProgress> request = new Page<>(safeIndex, safeSize);
|
||||
IPage<WatchProgress> result = progressMapper.selectPage(request,
|
||||
Wrappers.<WatchProgress>lambdaQuery()
|
||||
.eq(WatchProgress::getUserId, userId)
|
||||
.orderByDesc(WatchProgress::getUpdatedAt)
|
||||
.orderByDesc(WatchProgress::getDramaId));
|
||||
return new PageResult<>(result.getTotal(), enrichDisplay(result.getRecords()));
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@@ -146,6 +160,48 @@ public class WatchProgressService {
|
||||
return vo;
|
||||
}
|
||||
|
||||
private List<ProgressRespVO> enrichDisplay(List<WatchProgress> records) {
|
||||
if (records.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
List<Long> dramaIds = records.stream()
|
||||
.map(WatchProgress::getDramaId)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.sorted()
|
||||
.toList();
|
||||
List<Long> episodeIds = records.stream()
|
||||
.map(WatchProgress::getEpisodeId)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.sorted()
|
||||
.toList();
|
||||
Map<Long, Drama> dramaById = new HashMap<>();
|
||||
for (Drama drama : dramaService.listByIds(dramaIds)) {
|
||||
dramaById.put(drama.getId(), drama);
|
||||
}
|
||||
Map<Long, DramaEpisode> episodeById = new HashMap<>();
|
||||
for (DramaEpisode episode : episodeService.listByIds(episodeIds)) {
|
||||
episodeById.put(episode.getId(), episode);
|
||||
}
|
||||
List<ProgressRespVO> result = new ArrayList<>(records.size());
|
||||
for (WatchProgress progress : records) {
|
||||
ProgressRespVO vo = toResp(progress);
|
||||
Drama drama = dramaById.get(progress.getDramaId());
|
||||
if (drama != null) {
|
||||
vo.setDramaTitle(drama.getTitle());
|
||||
vo.setDramaCover(drama.getCoverUrl());
|
||||
vo.setTotalEpisodes(drama.getTotalEpisodes());
|
||||
}
|
||||
DramaEpisode episode = episodeById.get(progress.getEpisodeId());
|
||||
if (episode != null) {
|
||||
vo.setEpisodeNumber(episode.getEpNo());
|
||||
}
|
||||
result.add(vo);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** 观看流水明细(客服视图与后续行为分析用),只读。 */
|
||||
public List<WatchHistory> history(Long userId, int limit) {
|
||||
return historyMapper.selectList(Wrappers.<WatchHistory>lambdaQuery()
|
||||
|
||||
+8
@@ -12,6 +12,14 @@ public class ProgressRespVO {
|
||||
|
||||
private Long dramaId;
|
||||
private Long episodeId;
|
||||
/** 短剧标题,仅列表类接口批量装配。 */
|
||||
private String dramaTitle;
|
||||
/** 短剧封面,仅列表类接口批量装配。 */
|
||||
private String dramaCover;
|
||||
/** 当前续看集号。 */
|
||||
private Integer episodeNumber;
|
||||
/** 短剧总集数;短剧不存在时为空。 */
|
||||
private Integer totalEpisodes;
|
||||
private Long positionMs;
|
||||
private Long durationMs;
|
||||
private Boolean finished;
|
||||
|
||||
Reference in new issue
Block a user