package com.emotion.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.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.emotion.common.PageResult; import com.emotion.dto.request.DiaryCommentCreateRequest; import com.emotion.dto.request.DiaryCommentPageRequest; import com.emotion.dto.response.DiaryCommentResponse; import com.emotion.entity.DiaryComment; import com.emotion.mapper.DiaryCommentMapper; import com.emotion.service.DiaryCommentService; import com.emotion.util.SnowflakeIdGenerator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.List; import java.util.stream.Collectors; /** * 日记评论服务实现类 * * @author emotion-museum * @date 2025-07-23 */ @Service public class DiaryCommentServiceImpl extends ServiceImpl implements DiaryCommentService { @Autowired private SnowflakeIdGenerator snowflakeIdGenerator; @Autowired private ObjectMapper objectMapper; private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); @Override public PageResult getPageWithResponse(DiaryCommentPageRequest request) { Page page = new Page<>(request.getCurrent(), request.getSize()); LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); // 基础查询条件 wrapper.eq(DiaryComment::getIsDeleted, 0); // 根据请求参数添加查询条件 if (StringUtils.hasText(request.getDiaryId())) { wrapper.eq(DiaryComment::getDiaryId, request.getDiaryId()); } if (StringUtils.hasText(request.getUserId())) { wrapper.eq(DiaryComment::getUserId, request.getUserId()); } if (StringUtils.hasText(request.getCommentType())) { wrapper.eq(DiaryComment::getCommentType, request.getCommentType()); } if (request.getTopLevelOnly() != null && request.getTopLevelOnly()) { wrapper.isNull(DiaryComment::getParentCommentId); } // 排序 wrapper.orderByDesc(DiaryComment::getIsTop) .orderByDesc(DiaryComment::getPublishTime); IPage resultPage = this.page(page, wrapper); return convertPageToResponse(resultPage); } @Override public DiaryCommentResponse getCommentResponseById(String id) { DiaryComment comment = this.getById(id); if (comment == null) { return null; } return convertToResponse(comment); } @Override public List getCommentTreeWithResponse(String diaryId) { List comments = this.getCommentTree(diaryId); return comments.stream() .map(this::convertToResponse) .collect(Collectors.toList()); } @Override public DiaryCommentResponse createCommentWithResponse(DiaryCommentCreateRequest request) { DiaryComment comment = DiaryComment.builder() .id(snowflakeIdGenerator.nextIdAsString()) // 使用雪花算法生成ID .diaryId(request.getDiaryId()) .userId(request.getUserId()) .content(request.getContent()) .images(convertListToJson(request.getImages())) .parentCommentId(request.getParentCommentId()) .commentType("user") .likeCount(0) .replyCount(0) .isAnonymous(request.getIsAnonymous()) .isTop(0) .status("published") .publishTime(LocalDateTime.now()) .build(); this.save(comment); // 如果有父评论,更新父评论的回复数 if (StringUtils.hasText(request.getParentCommentId())) { this.incrementReplyCount(request.getParentCommentId()); this.updateLastReplyTime(request.getParentCommentId()); } return convertToResponse(comment); } @Override public DiaryCommentResponse updateCommentWithResponse(DiaryCommentCreateRequest request) { LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); wrapper.eq(DiaryComment::getId, request.getId()) .set(StringUtils.hasText(request.getContent()), DiaryComment::getContent, request.getContent()) .set(request.getImages() != null, DiaryComment::getImages, convertListToJson(request.getImages())) .set(DiaryComment::getUpdateTime, LocalDateTime.now()); boolean updated = this.update(wrapper); if (!updated) { return null; } DiaryComment updatedComment = this.getById(request.getId()); return updatedComment != null ? convertToResponse(updatedComment) : null; } @Override public boolean deleteComment(String commentId) { return this.removeById(commentId); } @Override public boolean softDeleteComment(String commentId) { LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); wrapper.eq(DiaryComment::getId, commentId) .set(DiaryComment::getIsDeleted, 1) .set(DiaryComment::getUpdateTime, LocalDateTime.now()); return this.update(wrapper); } @Override public boolean restoreComment(String commentId) { LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); wrapper.eq(DiaryComment::getId, commentId) .set(DiaryComment::getIsDeleted, 0) .set(DiaryComment::getUpdateTime, LocalDateTime.now()); return this.update(wrapper); } @Override public boolean incrementLikeCount(String commentId) { LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); wrapper.eq(DiaryComment::getId, commentId) .setSql("like_count = like_count + 1") .set(DiaryComment::getUpdateTime, LocalDateTime.now()); return this.update(wrapper); } @Override public boolean decrementLikeCount(String commentId) { LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); wrapper.eq(DiaryComment::getId, commentId) .setSql("like_count = GREATEST(like_count - 1, 0)") .set(DiaryComment::getUpdateTime, LocalDateTime.now()); return this.update(wrapper); } @Override public boolean setTop(String commentId, Integer isTop) { LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); wrapper.eq(DiaryComment::getId, commentId) .set(DiaryComment::getIsTop, isTop) .set(DiaryComment::getUpdateTime, LocalDateTime.now()); return this.update(wrapper); } /** * 获取评论树结构 */ private List getCommentTree(String diaryId) { // 获取所有顶级评论(没有父评论的评论) LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(DiaryComment::getDiaryId, diaryId) .isNull(DiaryComment::getParentCommentId) .eq(DiaryComment::getIsDeleted, 0) .orderByDesc(DiaryComment::getIsTop) .orderByDesc(DiaryComment::getPublishTime); List topComments = this.list(wrapper); // 为每个顶级评论加载回复 return topComments.stream() .peek(comment -> { List replies = getRepliesByParentId(comment.getId()); // 这里可以递归加载更深层的回复,但为了性能考虑,通常只加载一层 }) .collect(Collectors.toList()); } /** * 根据父评论ID查询回复列表 */ private List getRepliesByParentId(String parentCommentId) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(DiaryComment::getParentCommentId, parentCommentId) .eq(DiaryComment::getIsDeleted, 0) .orderByAsc(DiaryComment::getPublishTime); return this.list(wrapper); } /** * 增加回复数 */ private boolean incrementReplyCount(String commentId) { LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); wrapper.eq(DiaryComment::getId, commentId) .setSql("reply_count = reply_count + 1") .set(DiaryComment::getUpdateTime, LocalDateTime.now()); return this.update(wrapper); } /** * 减少回复数 */ private boolean decrementReplyCount(String commentId) { LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); wrapper.eq(DiaryComment::getId, commentId) .setSql("reply_count = GREATEST(reply_count - 1, 0)") .set(DiaryComment::getUpdateTime, LocalDateTime.now()); return this.update(wrapper); } /** * 更新最后回复时间 */ private boolean updateLastReplyTime(String commentId) { LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); wrapper.eq(DiaryComment::getId, commentId) .set(DiaryComment::getLastReplyTime, LocalDateTime.now()) .set(DiaryComment::getUpdateTime, LocalDateTime.now()); return this.update(wrapper); } /** * 转换为响应对象 */ private DiaryCommentResponse convertToResponse(DiaryComment comment) { DiaryCommentResponse response = new DiaryCommentResponse(); BeanUtils.copyProperties(comment, response); // 转换时间格式 if (comment.getPublishTime() != null) { response.setPublishTime(comment.getPublishTime().format(DATE_TIME_FORMATTER)); } if (comment.getLastReplyTime() != null) { response.setLastReplyTime(comment.getLastReplyTime().format(DATE_TIME_FORMATTER)); } if (comment.getCreateTime() != null) { response.setCreateTime(comment.getCreateTime().format(DATE_TIME_FORMATTER)); } if (comment.getUpdateTime() != null) { response.setUpdateTime(comment.getUpdateTime().format(DATE_TIME_FORMATTER)); } // 转换JSON字段 try { if (comment.getImages() != null) { response.setImages(objectMapper.readValue(comment.getImages(), new TypeReference>() { })); } if (comment.getMetadata() != null) { response.setMetadata(objectMapper.readValue(comment.getMetadata(), Object.class)); } } catch (JsonProcessingException e) { // 忽略JSON解析错误 } return response; } /** * 转换分页对象为响应对象 */ private PageResult convertPageToResponse(IPage page) { PageResult responsePage = new PageResult<>(); responsePage.setCurrent(page.getCurrent()); responsePage.setSize(page.getSize()); responsePage.setTotal(page.getTotal()); responsePage.setPages(page.getPages()); responsePage.setRecords( page.getRecords().stream() .map(this::convertToResponse) .collect(Collectors.toList())); return responsePage; } /** * 将List转换为JSON字符串 */ private String convertListToJson(List list) { if (list == null || list.isEmpty()) { return null; } try { return objectMapper.writeValueAsString(list); } catch (JsonProcessingException e) { return null; } } }