评论功能

This commit is contained in:
曹佳豪 2025-06-13 10:57:07 +08:00
parent 847f8b0372
commit ff26d17d11
16 changed files with 668 additions and 269 deletions

View File

@ -1,116 +0,0 @@
//package com.wzj.soopin.content.controller;
//
//
//import com.wzj.soopin.content.domain.base.BaseInfoProperties;
//import com.wzj.soopin.content.domain.bo.CommentBO;
//import com.wzj.soopin.content.domain.po.Comment;
//import com.wzj.soopin.content.domain.po.Vlog;
//import com.wzj.soopin.content.domain.vo.CommentVO;
//import com.wzj.soopin.content.enums.MessageEnum;
//import com.wzj.soopin.content.service.CommentService;
//import com.wzj.soopin.content.service.MsgService;
//import com.wzj.soopin.content.service.VlogService;
//import jakarta.validation.Valid;
//import lombok.extern.slf4j.Slf4j;
//import org.apache.commons.lang3.StringUtils;
//import org.dromara.common.core.domain.R;
//import org.dromara.common.web.core.BaseController;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.web.bind.annotation.*;
//
//import java.util.HashMap;
//import java.util.Map;
//
//
//
//@Slf4j
//@RequestMapping("comment")
//@RestController
//public class CommentController extends BaseInfoProperties {
//
// @Autowired
// private CommentService commentService;
// @Autowired
// private MsgService msgService;
// @Autowired
// private VlogService vlogService;
////
// @PostMapping("create")
// public R create(@RequestBody @Valid CommentBO commentBO)
// throws Exception {
//
// CommentVO commentVO = commentService.createComment(commentBO);
// return R.ok(commentVO);
// }
////
// @GetMapping("counts")
// public R counts(@RequestParam String vlogId) {
//
// String countsStr = redis.get(REDIS_VLOG_COMMENT_COUNTS + ":" + vlogId);
// if (StringUtils.isBlank(countsStr)) {
// countsStr = "0";
// }
//
// return R.ok(Integer.valueOf(countsStr));
// }
//
// @GetMapping("list")
// public R list(@RequestParam String vlogId,
// @RequestParam(defaultValue = "") String userId,
// @RequestParam Integer page,
// @RequestParam Integer pageSize) {
//
// return R.ok(
// commentService.queryVlogComments(
// vlogId,
// userId,
// page,
// pageSize));
// }
//
// @DeleteMapping("delete")
// public R delete(@RequestParam String commentUserId,
// @RequestParam String commentId,
// @RequestParam String vlogId) {
// commentService.deleteComment(commentUserId,
// commentId,
// vlogId);
// return R.ok();
// }
//
// @PostMapping("like")
// public R like(@RequestParam String commentId,
// @RequestParam String userId) {
//
// // 故意犯错bigkey
// redis.incrementHash(REDIS_VLOG_COMMENT_LIKED_COUNTS, commentId, 1);
// redis.setHashValue(REDIS_USER_LIKE_COMMENT, userId + ":" + commentId, "1");
//// redis.hset(REDIS_USER_LIKE_COMMENT, userId, "1");
//
//
// // 系统消息点赞评论
// Comment comment = commentService.getComment(commentId);
// Vlog vlog = vlogService.getVlog(comment.getVlogId());
// Map msgContent = new HashMap();
// msgContent.put("vlogId", vlog.getId());
// msgContent.put("vlogCover", vlog.getCover());
// msgContent.put("commentId", commentId);
// msgService.createMsg(userId,
// comment.getCommentUserId(),
// MessageEnum.LIKE_COMMENT.type,
// msgContent);
//
//
// return R.ok();
// }
//
// @PostMapping("unlike")
// public R unlike(@RequestParam String commentId,
// @RequestParam String userId) {
//
// redis.decrementHash(REDIS_VLOG_COMMENT_LIKED_COUNTS, commentId, 1);
// redis.hdel(REDIS_USER_LIKE_COMMENT, userId + ":" + commentId);
//
// return R.ok();
// }
//}

View File

@ -1,7 +1,10 @@
package com.wzj.soopin.content.controller.admin;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wzj.soopin.content.domain.bo.CommentBO;
import com.wzj.soopin.content.domain.po.Comment;
import com.wzj.soopin.content.domain.vo.CommentVO;
import com.wzj.soopin.content.domain.vo.ChildCommentVO;
import com.wzj.soopin.content.result.GraceJSONResult;
import com.wzj.soopin.content.service.CommentService;
// import com.wzj.soopin.content.utils.PagedGridResult;
@ -9,8 +12,13 @@ import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.domain.R;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.web.core.BaseController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@ -22,22 +30,21 @@ import java.util.HashMap;
@Api(tags = "管理端-评论管理接口")
@RequestMapping("admin/comment")
@RestController
public class CommentController {
public class CommentController extends BaseController {
@Autowired
private CommentService commentService;
@ApiOperation("获取评论列表")
@GetMapping("/list")
public TableDataInfo<CommentVO> getCommentList(
@ApiParam(value = "视频ID") @RequestParam(required = false) String vlogId,
PageQuery pageQuery) {
try {
return commentService.getCommentList(vlogId, pageQuery);
} catch (Exception e) {
log.error("获取评论列表失败", e);
return null;
}
@PostMapping("/list")
public R<Page<CommentVO>> list(
@RequestBody CommentBO bo,
@RequestParam(defaultValue = "1") long current,
@RequestParam(defaultValue = "10") long size
) {
Page<Comment> page = new Page<>(current, size);
Page<CommentVO> commentPage = commentService.pageComment(page, bo);
return R.ok(commentPage);
}
@ApiOperation("查询视频评论列表")
@ -59,7 +66,8 @@ public class CommentController {
public GraceJSONResult deleteComment(
@ApiParam(value = "评论ID") @RequestParam String commentId) {
try {
commentService.deleteComment(commentId);
String username = LoginHelper.getUsername();
commentService.deleteComment(commentId, username);
return GraceJSONResult.ok();
} catch (Exception e) {
log.error("删除评论失败", e);
@ -89,4 +97,28 @@ public class CommentController {
return GraceJSONResult.errorMsg("获取评论详情失败: " + e.getMessage());
}
}
@ApiOperation("获取子评论列表")
@GetMapping("/childList")
public R<Page<ChildCommentVO>> getChildComments(
@RequestParam String fatherCommentId,
@RequestParam(defaultValue = "1") long current,
@RequestParam(defaultValue = "10") long size
) {
Page<ChildCommentVO> page = commentService.getChildCommentVOsPaged(fatherCommentId, current, size);
return R.ok(page);
}
@ApiOperation("下架评论")
@PostMapping("/offline")
public GraceJSONResult offlineComment(@ApiParam(value = "评论ID") @RequestParam String commentId) {
try {
String username = LoginHelper.getUsername();
commentService.offlineComment(commentId, username);
return GraceJSONResult.ok();
} catch (Exception e) {
log.error("下架评论失败", e);
return GraceJSONResult.errorMsg("下架评论失败: " + e.getMessage());
}
}
}

View File

@ -1,9 +1,11 @@
package com.wzj.soopin.content.controller.admin;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tencentcloudapi.vod.v20180717.models.*;
import com.wzj.soopin.content.domain.base.BaseInfoProperties;
import com.wzj.soopin.content.domain.bo.VlogBO;
import com.wzj.soopin.content.result.GraceJSONResult;
import com.wzj.soopin.content.service.VlogService;
import com.wzj.soopin.content.service.VlogUploadService;
@ -20,6 +22,10 @@ import org.springframework.web.bind.annotation.*;
import org.apache.commons.lang3.StringUtils;
import java.util.*;
import java.time.Instant;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.ZonedDateTime;
@Slf4j
@Api(tags = "管理端-视频上传接口")
@ -43,35 +49,12 @@ public class VlogUploadController extends BaseInfoProperties {
private IFansService fansService;
@ApiOperation("获取腾讯云点播视频列表")
@GetMapping("/list")
@PostMapping("/list")
public GraceJSONResult getVodList(
@ApiParam(value = "当前页码", defaultValue = "1") @RequestParam(value = "current", required = false, defaultValue = "1") Integer current,
@ApiParam(value = "每页记录数", defaultValue = "10") @RequestParam(value = "size", required = false, defaultValue = "10") Integer size,
@ApiParam(value = "文件状态") @RequestParam(required = false) String[] status,
@ApiParam(value = "文件类型") @RequestParam(required = false) String[] categories,
@ApiParam(value = "媒体文件来源") @RequestParam(required = false) String[] sourceTypes,
@ApiParam(value = "媒体文件封装格式") @RequestParam(required = false) String[] mediaTypes,
@ApiParam(value = "文件名") @RequestParam(required = false) String[] names,
@ApiParam(value = "文件名前缀") @RequestParam(required = false) String[] namePrefixes,
@ApiParam(value = "文件描述") @RequestParam(required = false) String[] descriptions,
@ApiParam(value = "标签") @RequestParam(required = false) String[] tags,
@ApiParam(value = "分类ID") @RequestParam(required = false) Long[] classIds,
@ApiParam(value = "存储地区") @RequestParam(required = false) String[] storageRegions,
@ApiParam(value = "创建时间范围-开始时间") @RequestParam(required = false) String createTimeAfter,
@ApiParam(value = "创建时间范围-结束时间") @RequestParam(required = false) String createTimeBefore,
@ApiParam(value = "排序字段", defaultValue = "CreateTime", allowableValues = "CreateTime,LikeCounts,CommentsCounts,Name,Status")
@RequestParam(required = false, defaultValue = "CreateTime") String sortField,
@ApiParam(value = "排序方式", defaultValue = "Asc", allowableValues = "Asc,Desc")
@RequestParam(required = false, defaultValue = "Asc") String sortOrder,
@ApiParam(value = "需要返回的信息类型") @RequestParam(required = false) String[] filters) {
@RequestBody VlogBO vlogBO) {
try {
// 验证密钥配置
String secretId = tencentCloudUtil.getSecretId();
String secretKey = tencentCloudUtil.getSecretKey();
if (secretId == null || secretId.isEmpty() || secretKey == null || secretKey.isEmpty()) {
log.error("腾讯云密钥未配置");
return GraceJSONResult.errorMsg("腾讯云密钥未配置");
}
Integer current = (int)vlogBO.getCurrent();
Integer size = (int)vlogBO.getSize();
// 验证分页参数
if (current < 1) {
@ -81,6 +64,95 @@ public class VlogUploadController extends BaseInfoProperties {
size = 10;
}
// 根据VlogBO的orders确定排序类型
Integer sortType = 0; // 默认为按审核状态排序
String tencentCloudSortField = "CreateTime"; // 腾讯云默认排序字段
String tencentCloudSortOrder = "Asc"; // 腾讯云默认排序方式
if (vlogBO.getOrders() != null && !vlogBO.getOrders().isEmpty()) {
OrderItem orderItem = vlogBO.getOrders().get(0); // 取第一个排序项
String column = orderItem.getColumn();
boolean asc = orderItem.isAsc();
if ("status".equalsIgnoreCase(column)) {
sortType = 0;
} else if ("commentsCounts".equalsIgnoreCase(column)) {
sortType = 1;
} else if ("likeCounts".equalsIgnoreCase(column)) {
sortType = 2;
} else if ("CreateTime".equalsIgnoreCase(column) || "Name".equalsIgnoreCase(column)) {
// 如果是腾讯云支持的排序字段则直接传递给腾讯云API
tencentCloudSortField = column;
tencentCloudSortOrder = asc ? "Asc" : "Desc";
}
}
// 如果有模糊查询条件或有时间区间条件则只查询数据库
if (StringUtils.isNotBlank(vlogBO.getMobile())
|| StringUtils.isNotBlank(vlogBO.getNickname())
|| StringUtils.isNotBlank(vlogBO.getTitle())
|| StringUtils.isNotBlank(vlogBO.getStartTime())
|| StringUtils.isNotBlank(vlogBO.getEndTime())
|| (vlogBO.getStatusFilter() != null && vlogBO.getStatusFilter().length > 0)) {
// 创建分页对象
Page<Map<String, Object>> page = new Page<>(current, size);
// 构建查询参数
Map<String, Object> params = new HashMap<>();
if (StringUtils.isNotBlank(vlogBO.getMobile())) {
params.put("mobile", vlogBO.getMobile());
}
if (StringUtils.isNotBlank(vlogBO.getNickname())) {
params.put("nickname", vlogBO.getNickname());
}
if (StringUtils.isNotBlank(vlogBO.getTitle())) {
params.put("title", vlogBO.getTitle());
}
if (vlogBO.getStatusFilter() != null && vlogBO.getStatusFilter().length > 0) {
params.put("status", vlogBO.getStatusFilter()[0]); // 取第一个状态值
}
params.put("sortType", sortType); // 添加排序类型参数
// 时间格式转换
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
if (StringUtils.isNotBlank(vlogBO.getStartTime())) {
try {
Instant instant = Instant.parse(vlogBO.getStartTime().replace("\"", ""));
ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault());
String formatted = zdt.format(formatter);
params.put("startTime", formatted);
} catch (Exception e) {
log.error("开始时间格式错误: {}", vlogBO.getStartTime());
return GraceJSONResult.errorMsg("开始时间格式错误请使用yyyy-MM-dd HH:mm:ss格式");
}
}
if (StringUtils.isNotBlank(vlogBO.getEndTime())) {
try {
Instant instant = Instant.parse(vlogBO.getEndTime().replace("\"", ""));
ZonedDateTime zdt = instant.atZone(ZoneId.systemDefault());
String formatted = zdt.format(formatter);
params.put("endTime", formatted);
} catch (Exception e) {
log.error("结束时间格式错误: {}", vlogBO.getEndTime());
return GraceJSONResult.errorMsg("结束时间格式错误请使用yyyy-MM-dd HH:mm:ss格式");
}
}
// 调用服务层方法进行查询
IPage<Map<String, Object>> result = vlogService.getVlogListByMobile(page, params);
return GraceJSONResult.ok(result);
}
// 否则查询腾讯云点播
// 验证密钥配置
String secretId = tencentCloudUtil.getSecretId();
String secretKey = tencentCloudUtil.getSecretKey();
if (secretId == null || secretId.isEmpty() || secretKey == null || secretKey.isEmpty()) {
log.error("腾讯云密钥未配置");
return GraceJSONResult.errorMsg("腾讯云密钥未配置");
}
// 计算实际的offset
long offset = (long) ((current - 1) * size);
if (offset < 0) {
@ -89,18 +161,7 @@ public class VlogUploadController extends BaseInfoProperties {
// 打印分页参数
log.info("分页参数 - 当前页码: {}, 每页记录数: {}, 偏移量: {}", current, size, offset);
log.info("排序参数 - 排序字段: {}, 排序方式: {}", sortField, sortOrder);
// 验证排序参数
List<String> validSortFields = Arrays.asList("CreateTime", "LikeCounts", "CommentsCounts", "Name", "Status");
List<String> validSortOrders = Arrays.asList("Asc", "Desc");
if (!validSortFields.contains(sortField)) {
return GraceJSONResult.errorMsg("无效的排序字段,支持的字段有:" + String.join(",", validSortFields));
}
if (!validSortOrders.contains(sortOrder)) {
return GraceJSONResult.errorMsg("无效的排序方式,只能是 Asc(升序) 或 Desc(降序)");
}
log.info("排序参数 - 排序类型: {}, 腾讯云排序字段: {}, 腾讯云排序方式: {}", sortType, tencentCloudSortField, tencentCloudSortOrder);
// 创建请求对象
SearchMediaRequest req = new SearchMediaRequest();
@ -109,39 +170,39 @@ public class VlogUploadController extends BaseInfoProperties {
req.setOffset(offset);
req.setLimit((long) size);
// 设置排序
// 设置腾讯云API的排序 (只对腾讯云支持的字段有效)
SortBy sort = new SortBy();
sort.setField(sortField);
sort.setOrder(sortOrder);
sort.setField(tencentCloudSortField);
sort.setOrder(tencentCloudSortOrder);
req.setSort(sort);
// 设置过滤条件
if (categories != null && categories.length > 0) {
req.setCategories(categories);
if (vlogBO.getCategories() != null && vlogBO.getCategories().length > 0) {
req.setCategories(vlogBO.getCategories());
}
if (sourceTypes != null && sourceTypes.length > 0) {
req.setSourceTypes(sourceTypes);
if (vlogBO.getSourceTypes() != null && vlogBO.getSourceTypes().length > 0) {
req.setSourceTypes(vlogBO.getSourceTypes());
}
if (names != null && names.length > 0) {
req.setNames(names);
if (vlogBO.getNames() != null && vlogBO.getNames().length > 0) {
req.setNames(vlogBO.getNames());
}
if (namePrefixes != null && namePrefixes.length > 0) {
req.setNamePrefixes(namePrefixes);
if (vlogBO.getNamePrefixes() != null && vlogBO.getNamePrefixes().length > 0) {
req.setNamePrefixes(vlogBO.getNamePrefixes());
}
if (descriptions != null && descriptions.length > 0) {
req.setDescriptions(descriptions);
if (vlogBO.getDescriptions() != null && vlogBO.getDescriptions().length > 0) {
req.setDescriptions(vlogBO.getDescriptions());
}
if (tags != null && tags.length > 0) {
req.setTags(tags);
if (vlogBO.getTags() != null && vlogBO.getTags().length > 0) {
req.setTags(vlogBO.getTags());
}
if (classIds != null && classIds.length > 0) {
req.setClassIds(classIds);
if (vlogBO.getClassIds() != null && vlogBO.getClassIds().length > 0) {
req.setClassIds(vlogBO.getClassIds());
}
if (storageRegions != null && storageRegions.length > 0) {
req.setStorageRegions(storageRegions);
if (vlogBO.getStorageRegions() != null && vlogBO.getStorageRegions().length > 0) {
req.setStorageRegions(vlogBO.getStorageRegions());
}
if (filters != null && filters.length > 0) {
req.setFilters(filters);
if (vlogBO.getFilters() != null && vlogBO.getFilters().length > 0) {
req.setFilters(vlogBO.getFilters());
}
// 调用腾讯云API获取视频列表
@ -194,6 +255,43 @@ public class VlogUploadController extends BaseInfoProperties {
mediaList.add(mediaMap);
}
}
// 根据sortType进行内存排序
if (sortType != null) {
switch (sortType) {
case 0: // 按审核状态排序
mediaList.sort((a, b) -> {
String statusA = (String) a.get("status");
String statusB = (String) b.get("status");
// 定义状态优先级未审核 > 复审核 > 通过 > 不通过
Map<String, Integer> statusPriority = new HashMap<>();
statusPriority.put("PROCESSING", 0); // 未审核
statusPriority.put("REVIEWING", 1); // 复审核
statusPriority.put("NORMAL", 2); // 通过
statusPriority.put("REJECTED", 3); // 不通过
int priorityA = statusPriority.getOrDefault(statusA, 999);
int priorityB = statusPriority.getOrDefault(statusB, 999);
return priorityA - priorityB;
});
break;
case 1: // 按评论数排序
mediaList.sort((a, b) -> {
Integer commentsA = (Integer) a.getOrDefault("commentsCounts", 0);
Integer commentsB = (Integer) b.getOrDefault("commentsCounts", 0);
return commentsB.compareTo(commentsA); // 从高到低排序
});
break;
case 2: // 按点赞数排序
mediaList.sort((a, b) -> {
Integer likesA = (Integer) a.getOrDefault("likeCounts", 0);
Integer likesB = (Integer) b.getOrDefault("likeCounts", 0);
return likesB.compareTo(likesA); // 从高到低排序
});
break;
}
}
result.put("records", mediaList); // 改为 records 以匹配前端预期
return GraceJSONResult.ok(result);

View File

@ -29,4 +29,10 @@ public class CommentBO {
@NotBlank(message = "评论内容不能为空")
@Length(max = 50, message = "评论内容长度不能超过50")
private String content;
private String mobile;
public String getMobile() { return mobile; }
public void setMobile(String mobile) { this.mobile = mobile; }
}

View File

@ -43,9 +43,17 @@ public class Comment {
* 评论的点赞总数
*/
private Integer likeCounts;
/**
* 评论状态
*/
private Integer status;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 操作人
*/
private String updateBy;
}

View File

@ -0,0 +1,20 @@
package com.wzj.soopin.content.domain.vo;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class ChildCommentVO {
private String id;
private String commentId;
private String content;
private Integer status;
private String replyedUserNickname; // 被回复的昵称父评论用户昵称
private String replyUserNickname; // 回复人昵称当前子评论用户昵称
private String replyUserFace; // 回复人头像
private String fatherCommentId;
private String likeCounts;
private String vlogId;
private String commentUserId;
private LocalDateTime createTime;
}

View File

@ -5,6 +5,7 @@ import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.time.LocalDateTime;
import java.util.Date;
@Data
@ -23,6 +24,10 @@ public class CommentVO {
private String content;
private Integer likeCounts;
private String replyedUserNickname;
private Date createTime;
private LocalDateTime createTime;
private Integer isLike = 0;
private Integer childCount; // 子评论数
private Integer status;
private String vlogerMobile; // 视频作者手机号
private String vlogUrl; // 视频播放地址
}

View File

@ -1,5 +1,7 @@
package com.wzj.soopin.content.mapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wzj.soopin.content.domain.po.Comment;
import com.wzj.soopin.content.domain.vo.CommentVO;
import org.apache.ibatis.annotations.Param;
@ -12,6 +14,16 @@ import java.util.Map;
@Repository
public interface CommentMapperCustom extends BaseMapperPlus<Comment, CommentVO> {
public List<CommentVO> getCommentList(@Param("paramMap") Map<String, Object> map);
/**
* 获取评论列表
* @param map 查询参数
* @return 评论列表
*/
List<CommentVO> getCommentList(@Param("paramMap") Map<String, Object> map);
/**
* 分页查询评论支持手机号和vlogId联合查询
*/
IPage<CommentVO> selectByMobileAndVlogId(Page<?> page, @Param("mobile") String mobile, @Param("vlogId") String vlogId);
}

View File

@ -0,0 +1,8 @@
package com.wzj.soopin.content.mapper;
import com.wzj.soopin.content.domain.po.UserMember;
import com.wzj.soopin.content.domain.po.Users;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
public interface UsersMapper extends BaseMapperPlus<Users, Users> {
}

View File

@ -2,7 +2,10 @@ package com.wzj.soopin.content.mapper;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wzj.soopin.content.domain.po.Vlog;
import com.wzj.soopin.content.domain.vo.CommentVO;
import com.wzj.soopin.content.domain.vo.IndexVlogVO;
import org.apache.ibatis.annotations.Param;
@ -72,4 +75,14 @@ public interface VlogMapper extends BaseMapper<Vlog> {
* @return Vlog对象或null
*/
Vlog selectByFileId(@Param("fileId") String fileId);
/**
* 分页查询视频列表
* @param page 分页对象
* @param params 查询参数
* @return 分页结果
*/
IPage<Map<String, Object>> selectVlogList(Page<Map<String, Object>> page, @Param("params") Map<String, Object> params);
IPage<CommentVO> selectByMobileAndVlogId(Page<?> page, @Param("mobile") String mobile, @Param("vlogId") String vlogId);
}

View File

@ -1,12 +1,15 @@
package com.wzj.soopin.content.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wzj.soopin.content.domain.bo.CommentBO;
import com.wzj.soopin.content.domain.po.Comment;
import com.wzj.soopin.content.domain.vo.CommentVO;
import com.wzj.soopin.content.domain.vo.ChildCommentVO;
// import com.wzj.soopin.content.utils.PagedGridResult;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import java.util.List;
public interface CommentService {
@ -14,12 +17,12 @@ public interface CommentService {
/**
* 获取评论列表
*/
TableDataInfo<CommentVO> getCommentList(String vlogId, PageQuery pageQuery);
TableDataInfo<CommentVO> getCommentList(String vlogId, String mobile, PageQuery pageQuery);
/**
* 删除评论
*/
void deleteComment(String commentId);
void deleteComment(String commentId, String username);
/**
* 获取评论详情
@ -45,4 +48,24 @@ public interface CommentService {
* 查询视频评论列表
*/
TableDataInfo<CommentVO> queryVlogComments(String vlogId, String userId, PageQuery pageQuery);
/**
* 分页查询评论只查一级评论
*/
Page<CommentVO> pageComment(Page<Comment> page, CommentBO bo);
/**
* 获取子评论VO列表
*/
List<ChildCommentVO> getChildCommentVOs(String fatherCommentId);
/**
* 获取子评论VO列表分页
*/
Page<ChildCommentVO> getChildCommentVOsPaged(String fatherCommentId, long current, long size);
/**
* 下架评论status=2
*/
void offlineComment(String commentId, String username);
}

View File

@ -1,6 +1,7 @@
package com.wzj.soopin.content.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wzj.soopin.content.domain.bo.VlogBO;
import com.wzj.soopin.content.domain.po.Vlog;
import com.wzj.soopin.content.domain.vo.IndexVlogVO;
@ -141,4 +142,12 @@ public interface VlogService {
* @return 包含上传者姓名和手机号的Map
*/
Map<String, String> getVlogUploaderInfo(String fileId);
/**
* 分页查询视频列表
* @param page 分页对象
* @param params 查询参数
* @return 分页结果
*/
IPage<Map<String, Object>> getVlogListByMobile(Page<Map<String, Object>> page, Map<String, Object> params);
}

View File

@ -1,17 +1,21 @@
package com.wzj.soopin.content.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wzj.soopin.content.domain.base.BaseInfoProperties;
import com.wzj.soopin.content.domain.bo.CommentBO;
import com.wzj.soopin.content.domain.po.Comment;
import com.wzj.soopin.content.domain.po.Vlog;
import com.wzj.soopin.content.domain.po.Users;
import com.wzj.soopin.content.domain.vo.ChildCommentVO;
import com.wzj.soopin.content.domain.vo.CommentVO;
import com.wzj.soopin.content.enums.MessageEnum;
import com.wzj.soopin.content.enums.YesOrNo;
import com.wzj.soopin.content.mapper.CommentMapper;
import com.wzj.soopin.content.mapper.CommentMapperCustom;
import com.wzj.soopin.content.mapper.UsersMapper;
import com.wzj.soopin.content.mapper.VlogMapper;
import com.wzj.soopin.content.service.CommentService;
import com.wzj.soopin.content.service.MsgService;
import com.wzj.soopin.content.service.VlogService;
@ -20,7 +24,7 @@ import com.wzj.soopin.content.utils.Sid;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.core.utils.MapstructUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -28,6 +32,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -46,40 +51,38 @@ public class CommentServiceImpl extends BaseInfoProperties implements CommentSer
private MsgService msgService;
@Autowired
private Sid sid;
@Autowired
private UsersMapper usersMapper;
@Autowired
private VlogMapper vlogMapper;
@Override
public TableDataInfo<CommentVO> getCommentList(String vlogId, PageQuery pageQuery) {
LambdaQueryWrapper<Comment> queryWrapper = new LambdaQueryWrapper<>();
// 构建查询条件
if (StringUtils.hasText(vlogId)) {
queryWrapper.eq(Comment::getVlogId, vlogId);
public TableDataInfo<CommentVO> getCommentList(String vlogId, String mobile, PageQuery pageQuery) {
// 如果既没有vlogId也没有mobile返回空结果
if (!StringUtils.hasText(vlogId) && !StringUtils.hasText(mobile)) {
Page<CommentVO> emptyPage = new Page<>(pageQuery.getPageNum(), pageQuery.getPageSize());
emptyPage.setRecords(new ArrayList<>());
emptyPage.setTotal(0);
return TableDataInfo.build(emptyPage);
}
// 按创建时间倒序排序
queryWrapper.orderByDesc(Comment::getCreateTime);
// 使用自定义SQL查询
Map<String, Object> params = new HashMap<>();
if (StringUtils.hasText(vlogId)) {
params.put("vlogId", vlogId);
}
if (StringUtils.hasText(mobile)) {
params.put("mobile", mobile);
}
// 分页查询
Page<Comment> page = pageQuery.build();
Page<Comment> result = commentMapper.selectPage(page, queryWrapper);
List<CommentVO> commentList = commentMapperCustom.getCommentList(params);
// 转换为CommentVO列表
List<CommentVO> commentVOList = result.getRecords().stream()
.map(comment -> {
CommentVO vo = new CommentVO();
BeanUtils.copyProperties(comment, vo);
// 设置额外的VO属性
vo.setCommentId(comment.getId());
return vo;
})
.collect(Collectors.toList());
// 创建分页对象
Page<CommentVO> page = new Page<>(pageQuery.getPageNum(), pageQuery.getPageSize());
page.setRecords(commentList);
page.setTotal(commentList.size());
// 创建新的Page对象包含转换后的VO列表
Page<CommentVO> voPage = new Page<>(result.getCurrent(), result.getSize(), result.getTotal());
voPage.setRecords(commentVOList);
// 构建并返回TableDataInfo
return TableDataInfo.build(voPage);
return TableDataInfo.build(page);
}
@Override
@ -116,17 +119,22 @@ public class CommentServiceImpl extends BaseInfoProperties implements CommentSer
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteComment(String commentId) {
public void deleteComment(String commentId, String username) {
// 1. 逻辑删除父评论
Comment comment = commentMapper.selectByCommentId(commentId);
if (comment == null) {
throw new RuntimeException("评论不存在");
}
comment.setStatus(1);
comment.setUpdateBy(username);
commentMapper.updateById(comment);
// 删除评论及其子评论
commentMapper.deleteById(commentId);
// 2. 逻辑删除所有子评论
List<Comment> childComments = commentMapper.selectByFatherCommentId(commentId);
for (Comment childComment : childComments) {
commentMapper.deleteById(childComment.getId());
childComment.setStatus(1);
childComment.setUpdateBy(username);
commentMapper.updateById(childComment);
}
}
@ -161,4 +169,172 @@ public class CommentServiceImpl extends BaseInfoProperties implements CommentSer
public void updateComment(Comment comment) {
commentMapper.updateById(comment);
}
@Override
public Page<CommentVO> pageComment(Page<Comment> page, CommentBO bo) {
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Comment::getFatherCommentId, "0");
// 手机号查视频
if (bo != null && org.springframework.util.StringUtils.hasText(bo.getMobile())) {
Users user = usersMapper.selectOne(new LambdaQueryWrapper<Users>().eq(Users::getMobile, bo.getMobile()));
if (user == null) {
Page<CommentVO> emptyPage = new Page<>(page.getCurrent(), page.getSize(), 0);
emptyPage.setRecords(new ArrayList<>());
return emptyPage;
}
List<Vlog> vlogList = vlogMapper.selectList(new LambdaQueryWrapper<Vlog>().eq(Vlog::getVlogerId, user.getId()));
if (vlogList.isEmpty()) {
Page<CommentVO> emptyPage = new Page<>(page.getCurrent(), page.getSize(), 0);
emptyPage.setRecords(new ArrayList<>());
return emptyPage;
}
List<String> vlogIds = vlogList.stream().map(Vlog::getId).collect(Collectors.toList());
wrapper.in(Comment::getVlogId, vlogIds);
} else if (bo != null && org.springframework.util.StringUtils.hasText(bo.getVlogId())) {
wrapper.eq(Comment::getVlogId, bo.getVlogId());
}
Page<Comment> commentPage = commentMapper.selectPage(page, wrapper);
// 转VO并加子评论数
Page<CommentVO> voPage = new Page<>(commentPage.getCurrent(), commentPage.getSize(), commentPage.getTotal());
List<CommentVO> voList = commentPage.getRecords().stream().map(comment -> {
CommentVO vo = new CommentVO();
BeanUtils.copyProperties(comment, vo);
vo.setCommentId(comment.getId());
// 查评论用户信息
Users user = usersMapper.selectById(comment.getCommentUserId());
if (user != null) {
vo.setCommentUserFace(user.getFace());
vo.setCommentUserNickname(user.getNickname());
}
// 查视频作者手机号和视频播放地址
Vlog vlog = vlogMapper.selectById(comment.getVlogId());
if (vlog != null) {
Users vloger = usersMapper.selectById(vlog.getVlogerId());
if (vloger != null) {
vo.setVlogerMobile(vloger.getMobile());
}
vo.setVlogUrl(vlog.getUrl()); // 假设Vlog实体有getUrl()方法
}
// 子评论数
Long childCount = commentMapper.selectCount(
new LambdaQueryWrapper<Comment>()
.eq(Comment::getFatherCommentId, comment.getId())
);
vo.setChildCount(Math.toIntExact(childCount));
return vo;
}).collect(Collectors.toList());
voPage.setRecords(voList);
return voPage;
}
@Override
public List<ChildCommentVO> getChildCommentVOs(String fatherCommentId) {
// 查所有子评论
List<Comment> childComments = commentMapper.selectByFatherCommentId(fatherCommentId);
// 查父评论
Comment fatherComment = commentMapper.selectById(fatherCommentId);
String replyedUserNickname = null;
if (fatherComment != null) {
Users replyedUser = usersMapper.selectById(fatherComment.getCommentUserId());
if (replyedUser != null) {
replyedUserNickname = replyedUser.getNickname();
}
}
// 组装VO
List<ChildCommentVO> voList = new java.util.ArrayList<>();
for (Comment child : childComments) {
ChildCommentVO vo = new ChildCommentVO();
vo.setId(child.getId());
vo.setCommentId(child.getId());
vo.setContent(child.getContent());
vo.setFatherCommentId(child.getFatherCommentId());
vo.setVlogId(child.getVlogId());
vo.setCommentUserId(child.getCommentUserId());
vo.setCreateTime(child.getCreateTime());
vo.setReplyedUserNickname(replyedUserNickname);
// 回复人昵称和头像
Users replyUser = usersMapper.selectById(child.getCommentUserId());
if (replyUser != null) {
vo.setReplyUserNickname(replyUser.getNickname());
vo.setReplyUserFace(replyUser.getFace());
}
voList.add(vo);
}
return voList;
}
@Override
public Page<ChildCommentVO> getChildCommentVOsPaged(String fatherCommentId, long current, long size) {
Page<Comment> page = new Page<>(current, size);
LambdaQueryWrapper<Comment> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Comment::getFatherCommentId, fatherCommentId);
wrapper.orderByAsc(Comment::getCreateTime);
Page<Comment> commentPage = commentMapper.selectPage(page, wrapper);
// 查父评论
Comment fatherComment = commentMapper.selectById(fatherCommentId);
String replyedUserNickname;
if (fatherComment != null) {
Users replyedUser = usersMapper.selectById(fatherComment.getCommentUserId());
if (replyedUser != null) {
replyedUserNickname = replyedUser.getNickname();
} else {
replyedUserNickname = null;
}
} else {
replyedUserNickname = null;
}
// 组装VO
List<ChildCommentVO> voList = commentPage.getRecords().stream().map(child -> {
ChildCommentVO vo = new ChildCommentVO();
vo.setId(child.getId());
vo.setCommentId(child.getId());
vo.setContent(child.getContent());
vo.setFatherCommentId(child.getFatherCommentId());
vo.setVlogId(child.getVlogId());
vo.setStatus(child.getStatus());
vo.setLikeCounts(String.valueOf(child.getLikeCounts()));
vo.setCommentUserId(child.getCommentUserId());
vo.setCreateTime(child.getCreateTime());
vo.setReplyedUserNickname(replyedUserNickname);
// 回复人昵称和头像
Users replyUser = usersMapper.selectById(child.getCommentUserId());
if (replyUser != null) {
vo.setReplyUserNickname(replyUser.getNickname());
vo.setReplyUserFace(replyUser.getFace());
}
return vo;
}).collect(java.util.stream.Collectors.toList());
Page<ChildCommentVO> voPage = new Page<>(commentPage.getCurrent(), commentPage.getSize(), commentPage.getTotal());
voPage.setRecords(voList);
return voPage;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void offlineComment(String commentId, String username) {
// 1. 查父评论
Comment comment = commentMapper.selectByCommentId(commentId);
if (comment == null) {
throw new RuntimeException("评论不存在");
}
int newStatus = (comment.getStatus() != null && comment.getStatus() == 2) ? 0 : 2;
comment.setStatus(newStatus);
comment.setUpdateBy(username);
commentMapper.updateById(comment);
// 2. 查并切换所有子评论
List<Comment> childComments = commentMapper.selectByFatherCommentId(commentId);
for (Comment childComment : childComments) {
childComment.setStatus(newStatus);
childComment.setUpdateBy(username);
commentMapper.updateById(childComment);
}
}
}

View File

@ -2,6 +2,7 @@ package com.wzj.soopin.content.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
@ -383,6 +384,7 @@ public class VlogServiceImpl extends BaseInfoProperties implements VlogService {
result.put("title", vlog.getTitle());
result.put("width",vlog.getWidth());
result.put("height",vlog.getHeight());
result.put("reason",vlog.getReason());
// 点赞数通过点赞表统计
int likeCounts = myLikedVlogMapper.countLikesByVlogId(vlog.getId());
@ -408,6 +410,7 @@ public class VlogServiceImpl extends BaseInfoProperties implements VlogService {
result.put("vlogId", null);
result.put("likeCounts", 0);
result.put("commentCounts", 0);
result.put("reason",vlog.getReason());
result.put("fansCounts", 0);
}
@ -503,4 +506,9 @@ public class VlogServiceImpl extends BaseInfoProperties implements VlogService {
return result;
}
@Override
public IPage<Map<String, Object>> getVlogListByMobile(Page<Map<String, Object>> page, Map<String, Object> params) {
return vlogMapper.selectVlogList(page, params);
}
}

View File

@ -8,39 +8,62 @@
我们应该如何优化呢使得sql脚本查询表位三表或者两表查询
来实现呢又或者说我们能不能不使用数据库使用别的手段中间件mycat
-->
<select id="getCommentList" parameterType="map" resultType="com.wzj.soopin.content.domain.vo.CommentVO">
<select id="getCommentList" resultType="com.wzj.soopin.content.domain.vo.CommentVO">
SELECT
c.id,
c.id as commentId,
c.vloger_id as vlogerId,
c.status,
c.father_comment_id as fatherCommentId,
c.vlog_id as vlogId,
u.id as vlogerId,
c.comment_user_id as commentUserId,
u.nickname as commentUserNickname,
u.face as commentUserFace,
c.father_comment_id as fatherCommentId,
c.comment_user_id as commentUserId,
c.content as content,
c.content,
c.like_counts as likeCounts,
fu.nickname as replyedUserNickname,
c.create_time as createTime
FROM
`t_comment` as c
LEFT JOIN
t_users as u
ON
c.comment_user_id = u.id
LEFT JOIN
`t_comment` as fc
ON
c.father_comment_id = fc.id
LEFT JOIN
t_users as fu
ON
fc.comment_user_id = fu.id
WHERE
c.vlog_id = #{paramMap.vlogId}
ORDER BY
c.like_counts DESC,
c.create_time DESC
FROM t_comment c
LEFT JOIN t_users u ON c.comment_user_id = u.id
<where>
<if test="paramMap.vlogId != null and paramMap.vlogId != ''">
AND c.vlog_id = #{paramMap.vlogId}
</if>
<if test="paramMap.mobile != null and paramMap.mobile != ''">
AND u.mobile LIKE CONCAT('%', #{paramMap.mobile}, '%')
</if>
<if test="true">
AND (c.father_comment_id = '0' OR c.father_comment_id IS NULL)
</if>
</where>
ORDER BY c.create_time DESC
</select>
<select id="selectByMobileAndVlogId" resultType="com.wzj.soopin.content.domain.vo.CommentVO">
SELECT
c.id,
c.id as commentId,
c.vloger_id as vlogerId,
c.father_comment_id as fatherCommentId,
c.vlog_id as vlogId,
c.comment_user_id as commentUserId,
c.status,
u.nickname as commentUserNickname,
u.face as commentUserFace,
c.content,
c.like_counts as likeCounts,
c.create_time as createTime
FROM t_comment c
LEFT JOIN t_users u ON c.comment_user_id = u.id
<where>
c.father_comment_id = '0'
<if test="vlogId != null and vlogId != ''">
AND c.vlog_id = #{vlogId}
</if>
<if test="mobile != null and mobile != ''">
AND u.mobile LIKE CONCAT('%', #{mobile}, '%')
</if>
</where>
ORDER BY c.create_time DESC
</select>
</mapper>

View File

@ -172,5 +172,79 @@
SELECT * FROM t_vlog WHERE file_id = #{fileId}
</select>
<select id="selectVlogList" resultType="java.util.Map">
SELECT
v.id,
v.vloger_id,
v.url as mediaUrl,
v.cover,
v.title,
v.width,
v.height,
v.like_counts as likeCounts,
v.comments_counts as commentCounts,
v.is_private,
v.status,
v.file_id,
v.reason,
v.city_code,
v.first_frame_img as coverUrl,
v.create_time,
v.update_time,
m.mobile,
m.nickname
FROM
t_vlog v
LEFT JOIN t_users m ON v.vloger_id = m.id
<where>
<if test="params.mobile != null and params.mobile != ''">
AND m.mobile LIKE CONCAT('%', #{params.mobile}, '%')
</if>
<if test="params.nickname != null and params.nickname != ''">
AND m.nickname LIKE CONCAT('%', #{params.nickname}, '%')
</if>
<if test="params.title != null and params.title != ''">
AND v.title LIKE CONCAT('%', #{params.title}, '%')
</if>
<if test="params.status != null">
AND v.status = #{params.status}
</if>
<if test="params.startTime != null">
AND v.create_time >= #{params.startTime}
</if>
<if test="params.endTime != null">
AND v.create_time &lt;= #{params.endTime}
</if>
</where>
ORDER BY v.create_time DESC
</select>
<select id="selectByMobileAndVlogId" resultType="com.wzj.soopin.content.domain.vo.CommentVO">
SELECT
c.id,
c.id as commentId,
c.vloger_id as vlogerId,
c.father_comment_id as fatherCommentId,
c.vlog_id as vlogId,
c.comment_user_id as commentUserId,
u.nickname as commentUserNickname,
u.face as commentUserFace,
c.content,
c.like_counts as likeCounts,
c.create_time as createTime
FROM t_comment c
LEFT JOIN t_users u ON c.comment_user_id = u.id
<where>
c.father_comment_id = '0'
<if test="vlogId != null and vlogId != ''">
AND c.vlog_id = #{vlogId}
</if>
<if test="mobile != null and mobile != ''">
AND u.mobile LIKE CONCAT('%', #{mobile}, '%')
</if>
</where>
ORDER BY c.create_time DESC
</select>
</mapper>