feat(interaction): 点赞与评论按集统计

This commit is contained in:
Zhou Weidong committed 2026-10-21 15:56:26 +08:00
1 parent c7ff729f84
commit c6f729fb27
28 files changed
+683 -41

No files matched your search

@@ -3,6 +3,8 @@
package com.fruitshorts.app.controller;
import com.fruitshorts.common.pojo.CursorResult;
import com.fruitshorts.content.dal.entity.DramaEpisode;
import com.fruitshorts.content.service.EpisodeService;
import com.fruitshorts.common.pojo.Result;
import com.fruitshorts.framework.security.SecurityUtils;
import com.fruitshorts.framework.web.WebUtils;
@@ -24,6 +26,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 评论。
*
@@ -38,6 +42,7 @@ import org.springframework.web.bind.annotation.RestController;
public class AppCommentController {
private final CommentService commentService;
private final EpisodeService episodeService;
@Operation(summary = "发布评论")
@PostMapping
@@ -63,9 +68,18 @@ public class AppCommentController {
return Result.ok(commentService.page(SecurityUtils.getUserIdOrNull(), query));
}
@Operation(summary = "短剧评论总数(可匿名访问)")
@Operation(summary = "评论总数:传 episodeId 按集统计,仅 dramaId 时全剧统计(可匿名访问)")
@GetMapping("/count")
public Result<Long> count(@RequestParam Long dramaId) {
public Result<Long> count(@RequestParam Long dramaId,
@RequestParam(required = false) Long episodeId) {
if (episodeId != null) {
DramaEpisode episode = episodeService.getPublished(episodeId);
if (!dramaId.equals(episode.getDramaId())) {
return Result.ok(0L);
}
return Result.ok(commentService.countByEpisodes(List.of(episodeId))
.getOrDefault(episodeId, 0L));
}
return Result.ok(commentService.countByDrama(dramaId));
}
@@ -5,14 +5,20 @@ package com.fruitshorts.app.controller;
import com.fruitshorts.app.service.AppCatalogService;
import com.fruitshorts.app.vo.AppDramaDetailVO;
import com.fruitshorts.common.pojo.Result;
import com.fruitshorts.content.dal.entity.DramaEpisode;
import com.fruitshorts.content.service.EpisodeService;
import com.fruitshorts.framework.security.SecurityUtils;
import com.fruitshorts.framework.web.WebUtils;
import com.fruitshorts.interaction.service.CommentService;
import com.fruitshorts.playback.service.PlaybackService;
import com.fruitshorts.playback.vo.PlayRespVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -26,6 +32,8 @@ public class AppDramaController {
private final AppCatalogService appCatalogService;
private final PlaybackService playbackService;
private final EpisodeService episodeService;
private final CommentService commentService;
/** 详情:包含剧集列表,客户端进详情页一次请求即可渲染 */
@Operation(summary = "短剧详情(含剧集列表,进详情页一次请求即可渲染)")
@@ -41,10 +49,14 @@ public class AppDramaController {
@Operation(summary = "播放票据:判定权益后签发短时效播放地址")
@GetMapping("/episodes/{id}/play")
public Result<PlayRespVO> play(@PathVariable Long id, HttpServletRequest request) {
return Result.ok(playbackService.play(
PlayRespVO vo = playbackService.play(
SecurityUtils.getUserIdOrNull(),
id,
request.getHeader("X-Device-Id"),
WebUtils.getClientIp(request)));
WebUtils.getClientIp(request));
DramaEpisode episode = episodeService.getExisting(id);
vo.setLikeCount(episode.getLikeCount() == null ? 0L : episode.getLikeCount());
vo.setCommentCount(commentService.countByEpisodes(List.of(id)).getOrDefault(id, 0L));
return Result.ok(vo);
}
}
@@ -106,6 +106,8 @@ public class AppCatalogService {
public AppDramaDetailVO dramaDetail(Long dramaId) {
Drama drama = dramaService.getPublished(dramaId);
List<DramaEpisode> episodes = episodeService.listPublishedByDrama(dramaId);
Map<Long, Long> episodeCommentCounts = commentService.countByEpisodes(
episodes.stream().map(DramaEpisode::getId).toList());
String categoryName = categoryNameMap().getOrDefault(drama.getCategoryId(), "");
AppDramaDetailVO detail = new AppDramaDetailVO();
@@ -123,7 +125,9 @@ public class AppCatalogService {
detail.setLikeCount(drama.getLikeCount());
detail.setFavoriteCount(drama.getFavoriteCount());
detail.setCommentCount(commentService.countByDrama(dramaId));
detail.setEpisodes(episodes.stream().map(this::toEpisode).toList());
detail.setEpisodes(episodes.stream()
.map(episode -> toEpisode(episode, episodeCommentCounts))
.toList());
return detail;
}
@@ -168,7 +172,8 @@ public class AppCatalogService {
private List<AppDramaCardVO> cards(List<Drama> dramas, Map<Long, String> categoryNames) {
List<Long> ids = dramas.stream().map(Drama::getId).toList();
Map<Long, DramaEpisode> previews = episodeService.firstPlayableByDramas(ids);
Map<Long, Long> commentCounts = commentService.countByDramas(ids);
Map<Long, Long> commentCounts = commentService.countByEpisodes(
previews.values().stream().map(DramaEpisode::getId).toList());
return dramas.stream()
.map(drama -> toCard(
drama, categoryNames, previews.get(drama.getId()), commentCounts))
@@ -187,16 +192,18 @@ public class AppCatalogService {
card.setTotalEpisodes(drama.getTotalEpisodes());
card.setIsHot(drama.getIsHot() != null && drama.getIsHot() == 1);
card.setViewCount(drama.getViewCount());
card.setLikeCount(drama.getLikeCount());
card.setLikeCount(preview == null || preview.getLikeCount() == null
? 0L : preview.getLikeCount());
card.setFavoriteCount(drama.getFavoriteCount());
card.setCommentCount(commentCounts.getOrDefault(drama.getId(), 0L));
card.setCommentCount(preview == null
? 0L : commentCounts.getOrDefault(preview.getId(), 0L));
card.setLanguage(drama.getLanguage());
card.setDramaType(drama.getDramaType());
card.setPreview(preview == null ? null : toEpisode(preview));
card.setPreview(preview == null ? null : toEpisode(preview, commentCounts));
return card;
}
private AppEpisodeVO toEpisode(DramaEpisode episode) {
private AppEpisodeVO toEpisode(DramaEpisode episode, Map<Long, Long> commentCounts) {
AppEpisodeVO vo = new AppEpisodeVO();
vo.setId(episode.getId());
vo.setNumber(episode.getEpNo());
@@ -208,6 +215,8 @@ public class AppCatalogService {
vo.setIsFree(free);
vo.setLocked(!free);
vo.setUnlockCoin(episode.getUnlockCoin());
vo.setLikeCount(episode.getLikeCount() == null ? 0L : episode.getLikeCount());
vo.setCommentCount(commentCounts.getOrDefault(episode.getId(), 0L));
if (free) {
// 播放地址统一在这里签名:客户端拿不到可长期复用的裸地址
vo.setPlayUrl(playbackSigner.sign(playableSource(episode)));
@@ -17,6 +17,8 @@ public class AppEpisodeVO {
/** true 表示需要解锁后才能播放 */
private Boolean locked;
private Integer unlockCoin;
private Long likeCount;
private Long commentCount;
/**
* 可播放时的地址;锁定或不含片源时为空。
* 详情页拿到就能直接播,不必按集再请求一次播放票据。