重构部分表与接口,新增帖子,评论的相关接口

main
forely 2 weeks ago
parent 3b23ad399d
commit f07adec658

@ -106,6 +106,12 @@
<artifactId>commons-codec</artifactId> <artifactId>commons-codec</artifactId>
<version>1.15</version> <version>1.15</version>
</dependency> </dependency>
<!-- websocket -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
</dependencies> </dependencies>
<build> <build>

@ -0,0 +1,17 @@
package com.luojia_channel.modules.message.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new MyWebSocketHandler(), "/ws")
.setAllowedOrigins("*"); // 允许跨域
}
}

@ -0,0 +1,44 @@
package com.luojia_channel.modules.message.mq;
import com.alibaba.fastjson.JSON;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.messaging.MessageHeaders;
@Slf4j
@RequiredArgsConstructor
// 模板方法类,提供消息发送的模板方法
public abstract class AbstractSendProduceTemplate<T> {
private final RabbitTemplate rabbitTemplate;
private static final String KEYS = "keys";
protected abstract BaseSendExtendDTO buildBaseSendParam(T messageSendEvent);
public void sendMessage(T messageSendEvent) {
BaseSendExtendDTO baseSendDTO = buildBaseSendParam(messageSendEvent);
try {
// 发送消息
rabbitTemplate.convertAndSend(
baseSendDTO.getExchange(),
baseSendDTO.getRoutingKey(),
new MessageWrapper(baseSendDTO.getKeys(), messageSendEvent),
m -> {
// 设置消息头
m.getMessageProperties().setHeader(KEYS, baseSendDTO.getKeys());
// 设置消息属性(如延迟时间) TODO 若需要延迟消息,需安装延时插件
if (baseSendDTO.getDelay() != null) {
// m.getMessageProperties().setDelay(baseSendDTO.getDelay());
}
return m;
}
);
log.info("[{}] 消息发送成功Exchange{}Routing Key{}",
baseSendDTO.getEventName(), baseSendDTO.getExchange(), baseSendDTO.getRoutingKey());
} catch (Throwable ex) {
log.error("[{}] 消息发送失败,消息体:{}", baseSendDTO.getEventName(), JSON.toJSONString(messageSendEvent), ex);
throw ex;
}
}
}

@ -0,0 +1,18 @@
package com.luojia_channel.modules.message.mq;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public final class BaseSendExtendDTO {
private String eventName; //业务名称
private String exchange; //交换机
private String routingKey; //路由键
private String keys; // 消息唯一标识
private Long delay; // 延迟时间毫秒需RabbitMQ安装延迟插件
}

@ -0,0 +1,41 @@
package com.luojia_channel.modules.message.mq;
import lombok.*;
import java.io.Serializable;
import java.util.UUID;
/**
*
*/
@Data
@Builder
@NoArgsConstructor(force = true)
@AllArgsConstructor
@RequiredArgsConstructor
public final class MessageWrapper<T> implements Serializable {
private static final long serialVersionUID = 1L;
/**
* Keys
*/
@NonNull
private String keys;
/**
*
*/
@NonNull
private T message;
/**
*
*/
private String uuid = UUID.randomUUID().toString();
/**
*
*/
private Long timestamp = System.currentTimeMillis();
}

@ -4,61 +4,58 @@ import com.luojia_channel.common.domain.Result;
import com.luojia_channel.common.domain.page.PageResponse; import com.luojia_channel.common.domain.page.PageResponse;
import com.luojia_channel.common.utils.UserContext; import com.luojia_channel.common.utils.UserContext;
import com.luojia_channel.modules.post.dto.req.CommentPageQueryDTO; import com.luojia_channel.modules.post.dto.req.CommentPageQueryDTO;
import com.luojia_channel.modules.post.dto.req.CommentSaveDTO;
import com.luojia_channel.modules.post.dto.resp.CommentInfoDTO; import com.luojia_channel.modules.post.dto.resp.CommentInfoDTO;
import com.luojia_channel.modules.post.entity.Comment; import com.luojia_channel.modules.post.entity.Comment;
import com.luojia_channel.modules.post.service.CommentService; import com.luojia_channel.modules.post.service.CommentService;
import com.luojia_channel.modules.post.utils.ValidatePostUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.List;
@RequiredArgsConstructor
@RestController @RestController
@RequestMapping("/comments") @RequestMapping("/comments")
public class CommentController { public class CommentController {
private final CommentService commentService; private final CommentService commentService;
@Autowired
public CommentController(CommentService commentService) {
this.commentService = commentService;
}
// 创建评论 // 创建评论
@PostMapping @PostMapping
public Result<Void> saveComment(@RequestBody Comment comment) { public Result<Void> saveComment(@RequestBody CommentSaveDTO commentSaveDTO) {
commentService.saveComment(comment); commentService.saveComment(commentSaveDTO);
return Result.success(); return Result.success();
} }
// 更新评论 // 更新评论
@PutMapping("/{id}") @PutMapping
public Result<Void> updateComment(@PathVariable Long id, @RequestBody Comment comment) { public Result<Void> updateComment(@RequestBody CommentSaveDTO commentSaveDTO) {
Long currentUserId = UserContext.getUserId(); commentService.updateComment(commentSaveDTO);
commentService.updateComment(comment, currentUserId);
return Result.success(); return Result.success();
} }
// 删除评论 // 删除评论
@DeleteMapping("/{id}") @DeleteMapping
public Result<Void> deleteComment(@PathVariable Long id) { public Result<Void> deleteComment(@RequestParam("id") Long id) {
Long currentUserId = UserContext.getUserId(); commentService.deleteComment(id);
commentService.deleteComment(id, currentUserId);
return Result.success(); return Result.success();
} }
// 根据帖子ID分页获取评论 // 根据帖子ID分页获取评论
@GetMapping("/list") @GetMapping("/list")
public Result<PageResponse<CommentInfoDTO>> getCommentsByPostId(@RequestBody CommentPageQueryDTO commentPageQueryDTO) { public Result<PageResponse<CommentInfoDTO>> getCommentsByPostId(@RequestBody CommentPageQueryDTO commentPageQueryDTO) {
PageResponse<CommentInfoDTO> commentList = commentService.getCommentsByPostId(commentPageQueryDTO); PageResponse<CommentInfoDTO> commentList = commentService.getCommentsByPostId(commentPageQueryDTO);
return Result.success(commentList); return Result.success(commentList);
} }
// 根据帖子ID获取嵌套评论
@GetMapping("/nested/post/{postId}") // 根据评论ID获取回复
public Result<List<CommentInfoDTO>> getNestedCommentsByPostId(@PathVariable Long postId) { @GetMapping("/list/reply")
List<CommentInfoDTO> nestedComments = commentService.getNestedCommentsByPostId(postId); public Result<PageResponse<CommentInfoDTO>> getReplyById(@RequestBody CommentPageQueryDTO commentPageQueryDTO) {
return Result.success(nestedComments); PageResponse<CommentInfoDTO> commentInfoDTOList = commentService.getReplyById(commentPageQueryDTO);
return Result.success(commentInfoDTOList);
} }
} }

@ -6,6 +6,7 @@ import com.luojia_channel.common.domain.page.PageResponse;
import com.luojia_channel.modules.post.dto.req.PostSaveDTO; import com.luojia_channel.modules.post.dto.req.PostSaveDTO;
import com.luojia_channel.modules.post.dto.req.PostPageQueryDTO; import com.luojia_channel.modules.post.dto.req.PostPageQueryDTO;
import com.luojia_channel.modules.post.dto.resp.PostBasicInfoDTO; import com.luojia_channel.modules.post.dto.resp.PostBasicInfoDTO;
import com.luojia_channel.modules.post.dto.resp.PostInfoDTO;
import com.luojia_channel.modules.post.entity.Post; import com.luojia_channel.modules.post.entity.Post;
import com.luojia_channel.modules.post.service.PostService; import com.luojia_channel.modules.post.service.PostService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@ -48,7 +49,7 @@ public class PostController {
// 根据ID获取帖子详情 // 根据ID获取帖子详情
@GetMapping("/detail") @GetMapping("/detail")
public Post getPostDetail(@RequestParam("id") Long id) { public PostInfoDTO getPostDetail(@RequestParam("id") Long id) {
return postService.getPostDetail(id); return postService.getPostDetail(id);
} }

@ -5,5 +5,6 @@ import lombok.Data;
@Data @Data
public class CommentPageQueryDTO extends PageRequest { public class CommentPageQueryDTO extends PageRequest {
private Integer postId; private Long postId;
private Long commentId;
} }

@ -4,6 +4,7 @@ import lombok.Data;
@Data @Data
public class PostSaveDTO { public class PostSaveDTO {
private Long id;
private String title; private String title;
private String image; private String image;
private String content; private String content;

@ -15,12 +15,16 @@ import java.util.List;
public class CommentInfoDTO { public class CommentInfoDTO {
private Long id; private Long id;
private String content; private String content;
private Long likeCount;
private Long replyCount;
private Long userId; private Long userId;
private String postType;
private Long postId; private Long postId;
private Long parentCommentId; private Long parentCommentId;
private Long topId; private Long topId;
private LocalDateTime createTime; private LocalDateTime createTime;
private String userName;
private String userAvatar;
private List<CommentInfoDTO> commentInfoDTOList; private List<CommentInfoDTO> commentInfoDTOList;
} }

@ -1,22 +0,0 @@
package com.luojia_channel.modules.post.dto.resp;
import com.luojia_channel.modules.post.entity.Comment;
import java.util.List;
public class PostInfoDTO {
private Long id;
private String image;
private String title;
private Integer likeCount;
private Integer commentCount;
private Integer favoriteCount;
private String content;
private Boolean isLike;
private Long userId;
private String userName;
private String userAvatar;
private List<Comment> commentList;
}

@ -16,8 +16,9 @@ import java.time.LocalDateTime;
public class Comment { public class Comment {
private Long id; private Long id;
private String content; private String content;
private Long likeCount;
private Long replyCount;
private Long userId; private Long userId;
private String postType;
private Long postId; private Long postId;
private Long parentCommentId; private Long parentCommentId;
private Long topId; private Long topId;

@ -2,6 +2,7 @@ package com.luojia_channel.modules.post.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.luojia_channel.common.domain.page.PageResponse; import com.luojia_channel.common.domain.page.PageResponse;
import com.luojia_channel.modules.post.dto.req.CommentPageQueryDTO; import com.luojia_channel.modules.post.dto.req.CommentPageQueryDTO;
import com.luojia_channel.modules.post.dto.req.CommentSaveDTO;
import com.luojia_channel.modules.post.dto.resp.CommentInfoDTO; import com.luojia_channel.modules.post.dto.resp.CommentInfoDTO;
import com.luojia_channel.modules.post.entity.Comment; import com.luojia_channel.modules.post.entity.Comment;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -11,13 +12,13 @@ import java.util.List;
@Service @Service
public interface CommentService { public interface CommentService {
void saveComment(Comment comment); void saveComment(CommentSaveDTO commentSaveDTO);
void updateComment(Comment comment, Long userId); void updateComment(CommentSaveDTO commentSaveDTO);
void deleteComment(Long id, Long userId); void deleteComment(Long id);
PageResponse<CommentInfoDTO> getCommentsByPostId(CommentPageQueryDTO commentPageQueryDTO); PageResponse<CommentInfoDTO> getCommentsByPostId(CommentPageQueryDTO commentPageQueryDTO);
List<CommentInfoDTO> getNestedCommentsByPostId(Long postId); PageResponse<CommentInfoDTO> getReplyById(CommentPageQueryDTO commentPageQueryDTO);
} }

@ -5,6 +5,7 @@ import com.luojia_channel.common.domain.page.PageResponse;
import com.luojia_channel.modules.post.dto.req.PostSaveDTO; import com.luojia_channel.modules.post.dto.req.PostSaveDTO;
import com.luojia_channel.modules.post.dto.req.PostPageQueryDTO; import com.luojia_channel.modules.post.dto.req.PostPageQueryDTO;
import com.luojia_channel.modules.post.dto.resp.PostBasicInfoDTO; import com.luojia_channel.modules.post.dto.resp.PostBasicInfoDTO;
import com.luojia_channel.modules.post.dto.resp.PostInfoDTO;
import com.luojia_channel.modules.post.entity.Post; import com.luojia_channel.modules.post.entity.Post;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@ -17,7 +18,7 @@ public interface PostService extends IService<Post> {
void deletePost(Long id); void deletePost(Long id);
Post getPostDetail(Long id); PostInfoDTO getPostDetail(Long id);
PageResponse<PostBasicInfoDTO> pagePost(PostPageQueryDTO postPageQueryDTO); PageResponse<PostBasicInfoDTO> pagePost(PostPageQueryDTO postPageQueryDTO);

@ -1,91 +1,150 @@
package com.luojia_channel.modules.post.service.impl; package com.luojia_channel.modules.post.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 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.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.luojia_channel.common.domain.page.PageResponse; import com.luojia_channel.common.domain.page.PageResponse;
import com.luojia_channel.common.exception.PostException; import com.luojia_channel.common.exception.PostException;
import com.luojia_channel.common.utils.PageUtil; import com.luojia_channel.common.utils.PageUtil;
import com.luojia_channel.common.utils.UserContext;
import com.luojia_channel.modules.post.dto.req.CommentPageQueryDTO; import com.luojia_channel.modules.post.dto.req.CommentPageQueryDTO;
import com.luojia_channel.modules.post.dto.req.CommentSaveDTO;
import com.luojia_channel.modules.post.dto.resp.CommentInfoDTO; import com.luojia_channel.modules.post.dto.resp.CommentInfoDTO;
import com.luojia_channel.modules.post.dto.resp.PostBasicInfoDTO; import com.luojia_channel.modules.post.dto.resp.PostBasicInfoDTO;
import com.luojia_channel.modules.post.entity.Comment; import com.luojia_channel.modules.post.entity.Comment;
import com.luojia_channel.modules.post.entity.Post; import com.luojia_channel.modules.post.entity.Post;
import com.luojia_channel.modules.post.mapper.CommentMapper; import com.luojia_channel.modules.post.mapper.CommentMapper;
import com.luojia_channel.modules.post.service.CommentService; import com.luojia_channel.modules.post.service.CommentService;
import com.luojia_channel.modules.post.utils.ValidatePostUtil;
import com.luojia_channel.modules.user.entity.User;
import com.luojia_channel.modules.user.mapper.UserMapper;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment> implements CommentService { public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment> implements CommentService {
private CommentMapper commentMapper; private final ValidatePostUtil validatePostUtil;
private final CommentMapper commentMapper;
private final UserMapper userMapper;
@Override @Override
public void saveComment(Comment comment) { @Transactional(rollbackFor = Exception.class)
comment.setCreateTime(LocalDateTime.now()); public void saveComment(CommentSaveDTO commentSaveDTO) {
comment.setUpdateTime(LocalDateTime.now()); validatePostUtil.validateComment(commentSaveDTO);
commentMapper.insert(comment); Comment comment = BeanUtil.copyProperties(commentSaveDTO, Comment.class);
if(!save(comment)){
throw new PostException("创建评论失败");
}
Long parentCommentId = commentSaveDTO.getParentCommentId();
if(parentCommentId != null){
LambdaUpdateWrapper<Comment> updateWrapper = Wrappers.lambdaUpdate(Comment.class)
.eq(Comment::getId, parentCommentId)
.setSql("reply_count = reply_count + 1");
int update = commentMapper.update(null, updateWrapper);
if(update <= 0) {
throw new PostException("回复评论失败");
}
Long topId = commentSaveDTO.getTopId();
// 更新顶级评论回复数
if(!parentCommentId.equals(topId)){
updateWrapper = Wrappers.lambdaUpdate(Comment.class)
.eq(Comment::getId, topId)
.setSql("reply_count = reply_count + 1");
update = commentMapper.update(null, updateWrapper);
if(update <= 0) {
throw new PostException("回复顶级评论失败");
}
}
// TODO 消息通知
}
} }
@Override @Override
public void updateComment(Comment comment, Long userId) { public void updateComment(CommentSaveDTO commentSaveDTO) {
validatePostOwnership(comment.getId(), userId); validatePostUtil.validateComment(commentSaveDTO);
validatePostUtil.validateCommentOwnership(commentSaveDTO.getId());
Comment comment = BeanUtil.copyProperties(commentSaveDTO, Comment.class);
comment.setUpdateTime(LocalDateTime.now()); comment.setUpdateTime(LocalDateTime.now());
if(!updateById(comment)){ if(!updateById(comment)){
throw new PostException("更新帖子失败"); throw new PostException("更新评论失败");
} }
} }
@Override @Override
public void deleteComment(Long id, Long userId) { public void deleteComment(Long id) {
validatePostOwnership(id, userId); validatePostUtil.validateCommentOwnership(id);
commentMapper.deleteById(id); LambdaQueryWrapper<Comment> queryWrapper = Wrappers.lambdaQuery(Comment.class)
.eq(Comment::getTopId, id);
int delete = commentMapper.delete(queryWrapper);
if(delete <= 0) {
throw new PostException("删除评论失败");
}
// TODO 如果根评论删除,那么其他评论怎么办,目前做法是删除其下所有的子评论
} }
// 分页查询一系列根评论
@Override @Override
public PageResponse<CommentInfoDTO> getCommentsByPostId(CommentPageQueryDTO commentPageQueryDTO) { public PageResponse<CommentInfoDTO> getCommentsByPostId(CommentPageQueryDTO commentPageQueryDTO) {
if(commentPageQueryDTO.getPostId() == null || commentPageQueryDTO.getPostId() < 0){
throw new PostException("帖子id不合法");
}
LambdaQueryWrapper<Comment> queryWrapper = Wrappers.lambdaQuery(Comment.class) LambdaQueryWrapper<Comment> queryWrapper = Wrappers.lambdaQuery(Comment.class)
.eq(Comment::getPostId, commentPageQueryDTO.getPostId()) .eq(Comment::getPostId, commentPageQueryDTO.getPostId())
.eq(Comment::getParentCommentId, 0L)
.orderByDesc(Comment::getCreateTime); .orderByDesc(Comment::getCreateTime);
IPage<Comment> commentPage = commentMapper.selectPage(PageUtil.convert(commentPageQueryDTO), queryWrapper); return getCommentInfoDTOPageResponse(commentPageQueryDTO, queryWrapper);
return PageUtil.convert(commentPage, CommentInfoDTO.class);
} }
@Override @Override
public List<CommentInfoDTO> getNestedCommentsByPostId(Long postId) { public PageResponse<CommentInfoDTO> getReplyById(CommentPageQueryDTO commentPageQueryDTO) {
LambdaQueryWrapper<Comment> queryWrapper = new LambdaQueryWrapper<>(); if(commentPageQueryDTO.getCommentId() == null || commentPageQueryDTO.getCommentId() < 0){
queryWrapper.eq(Comment::getPostId, postId) throw new PostException("评论id不合法");
.orderByAsc(Comment::getCreateTime); }
LambdaQueryWrapper<Comment> queryWrapper = Wrappers.lambdaQuery(Comment.class)
List<Comment> comments = commentMapper.selectList(queryWrapper); .eq(Comment::getTopId, commentPageQueryDTO.getCommentId())
return buildNestedComments(comments); .orderByDesc(Comment::getCreateTime);
return getCommentInfoDTOPageResponse(commentPageQueryDTO, queryWrapper);
} }
private void validatePostOwnership(Long commentId, Long userId) { private PageResponse<CommentInfoDTO> getCommentInfoDTOPageResponse(CommentPageQueryDTO commentPageQueryDTO, LambdaQueryWrapper<Comment> queryWrapper) {
Comment comment = commentMapper.selectById(commentId); IPage<Comment> commentPage = commentMapper.selectPage(PageUtil.convert(commentPageQueryDTO), queryWrapper);
if (comment == null) { List<Long> userIds = new ArrayList<>();
throw new PostException("评论不存在"); commentPage.getRecords().forEach(comment -> {
} userIds.add(comment.getUserId());
if (!userId.equals(comment.getUserId())) { });
throw new PostException("你无权操作他人的评论"); List<User> users = userMapper.selectBatchIds(userIds);
} Map<Long, User> userMap = users.stream()
.collect(Collectors.toMap(User::getId, user -> user));
return PageUtil.convert(commentPage, (comment) -> {
CommentInfoDTO commentInfoDTO = BeanUtil.copyProperties(comment, CommentInfoDTO.class);
User user = userMap.getOrDefault(comment.getUserId(), new User());
commentInfoDTO.setUserAvatar(user.getAvatar());
commentInfoDTO.setUserName(user.getUsername());
return commentInfoDTO;
});
} }
private List<CommentInfoDTO> convertToDTO(List<Comment> comments) { private List<CommentInfoDTO> convertToDTO(List<Comment> commentList) {
List<CommentInfoDTO> dtos = new ArrayList<>(); List<CommentInfoDTO> dtos = new ArrayList<>();
for (Comment comment : comments) { for (Comment comment : commentList) {
CommentInfoDTO dto = new CommentInfoDTO(); CommentInfoDTO dto = new CommentInfoDTO();
BeanUtils.copyProperties(comment, dto); BeanUtils.copyProperties(comment, dto);
dtos.add(dto); dtos.add(dto);
@ -93,16 +152,14 @@ public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment> impl
return dtos; return dtos;
} }
private List<CommentInfoDTO> buildNestedComments(List<Comment> comments) { // 根据评论创建嵌套评论,目前没用到
private List<CommentInfoDTO> buildNestedCommentList(List<Comment> comments) {
Map<Long, CommentInfoDTO> map = new HashMap<>(); Map<Long, CommentInfoDTO> map = new HashMap<>();
List<CommentInfoDTO> rootComments = new ArrayList<>(); List<CommentInfoDTO> rootComments = new ArrayList<>();
for (Comment comment : comments) { for (Comment comment : comments) {
CommentInfoDTO dto = new CommentInfoDTO(); CommentInfoDTO dto = new CommentInfoDTO();
BeanUtils.copyProperties(comment, dto); BeanUtils.copyProperties(comment, dto);
map.put(comment.getId(), dto); map.put(comment.getId(), dto);
if (comment.getParentCommentId() == null) { if (comment.getParentCommentId() == null) {
rootComments.add(dto); rootComments.add(dto);
} else { } else {
@ -115,7 +172,6 @@ public class CommentServiceImpl extends ServiceImpl<CommentMapper, Comment> impl
} }
} }
} }
return rootComments; return rootComments;
} }
} }

@ -11,18 +11,28 @@ import com.luojia_channel.common.utils.PageUtil;
import com.luojia_channel.common.utils.RedisUtil; import com.luojia_channel.common.utils.RedisUtil;
import com.luojia_channel.common.utils.UserContext; import com.luojia_channel.common.utils.UserContext;
import com.luojia_channel.modules.file.service.impl.FileServiceImpl; import com.luojia_channel.modules.file.service.impl.FileServiceImpl;
import com.luojia_channel.modules.post.dto.req.CommentPageQueryDTO;
import com.luojia_channel.modules.post.dto.req.PostSaveDTO; import com.luojia_channel.modules.post.dto.req.PostSaveDTO;
import com.luojia_channel.modules.post.dto.req.PostPageQueryDTO; import com.luojia_channel.modules.post.dto.req.PostPageQueryDTO;
import com.luojia_channel.modules.post.dto.resp.CommentInfoDTO;
import com.luojia_channel.modules.post.dto.resp.PostBasicInfoDTO; import com.luojia_channel.modules.post.dto.resp.PostBasicInfoDTO;
import com.luojia_channel.modules.post.dto.resp.PostInfoDTO;
import com.luojia_channel.modules.post.entity.Comment;
import com.luojia_channel.modules.post.entity.Post; import com.luojia_channel.modules.post.entity.Post;
import com.luojia_channel.modules.post.mapper.PostMapper; import com.luojia_channel.modules.post.mapper.PostMapper;
import com.luojia_channel.modules.post.service.PostService; import com.luojia_channel.modules.post.service.PostService;
import com.luojia_channel.modules.post.utils.ValidatePostUtil; import com.luojia_channel.modules.post.utils.ValidatePostUtil;
import com.luojia_channel.modules.user.entity.User;
import com.luojia_channel.modules.user.mapper.UserMapper;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
@ -32,13 +42,12 @@ public class PostServiceImpl extends ServiceImpl<PostMapper, Post> implements Po
private final FileServiceImpl fileService; private final FileServiceImpl fileService;
private final ValidatePostUtil validatePostUtil; private final ValidatePostUtil validatePostUtil;
private final RedisUtil redisUtil; private final RedisUtil redisUtil;
private final UserMapper userMapper;
@Override @Override
public void createPost(PostSaveDTO postSaveDTO) { public void createPost(PostSaveDTO postSaveDTO) {
validatePostUtil.validatePost(postSaveDTO); validatePostUtil.validatePost(postSaveDTO);
Long userId = UserContext.getUserId();
Post post = BeanUtil.copyProperties(postSaveDTO, Post.class); Post post = BeanUtil.copyProperties(postSaveDTO, Post.class);
post.setUserId(userId);
if(!save(post)){ if(!save(post)){
throw new PostException("创建帖子失败"); throw new PostException("创建帖子失败");
} }
@ -52,6 +61,7 @@ public class PostServiceImpl extends ServiceImpl<PostMapper, Post> implements Po
@Override @Override
public void updatePost(PostSaveDTO postSaveDTO) { public void updatePost(PostSaveDTO postSaveDTO) {
validatePostUtil.validatePost(postSaveDTO); validatePostUtil.validatePost(postSaveDTO);
validatePostUtil.validatePostOwnership(postSaveDTO.getId());
Post post = BeanUtil.copyProperties(postSaveDTO, Post.class); Post post = BeanUtil.copyProperties(postSaveDTO, Post.class);
post.setUpdateTime(LocalDateTime.now()); post.setUpdateTime(LocalDateTime.now());
if(!updateById(post)){ if(!updateById(post)){
@ -69,15 +79,21 @@ public class PostServiceImpl extends ServiceImpl<PostMapper, Post> implements Po
} }
@Override @Override
public Post getPostDetail(Long id) { public PostInfoDTO getPostDetail(Long id) {
return redisUtil.safeGet("post:detail" + id.toString(), Post.class, return redisUtil.safeGet("post:detail" + id.toString(), PostInfoDTO.class,
() -> { () -> {
Post post = getById(id); Post post = getById(id);
if(post == null){ if(post == null){
throw new PostException("帖子不存在或被删除"); throw new PostException("帖子不存在或被删除");
} }
return post; PostInfoDTO postInfoDTO = BeanUtil.copyProperties(post, PostInfoDTO.class);
}, 10, TimeUnit.MINUTES); User user = userMapper.selectOne(Wrappers.lambdaQuery(User.class)
.eq(User::getId, post.getUserId()));
postInfoDTO.setUserAvatar(user.getAvatar());
postInfoDTO.setUserName(user.getUsername());
return postInfoDTO;
},
60, TimeUnit.MINUTES);
} }
@Override @Override
@ -86,10 +102,23 @@ public class PostServiceImpl extends ServiceImpl<PostMapper, Post> implements Po
LambdaQueryWrapper<Post> queryWrapper = Wrappers.lambdaQuery(Post.class) LambdaQueryWrapper<Post> queryWrapper = Wrappers.lambdaQuery(Post.class)
.orderByDesc(Post::getCreateTime); .orderByDesc(Post::getCreateTime);
IPage<Post> postPage = postMapper.selectPage(PageUtil.convert(postPageQueryDTO), queryWrapper); IPage<Post> postPage = postMapper.selectPage(PageUtil.convert(postPageQueryDTO), queryWrapper);
return PageUtil.convert(postPage, PostBasicInfoDTO.class); List<Long> userIds = new ArrayList<>();
postPage.getRecords().forEach(comment -> {
userIds.add(comment.getUserId());
});
List<User> users = userMapper.selectBatchIds(userIds);
Map<Long, User> userMap = users.stream()
.collect(Collectors.toMap(User::getId, user -> user));
// 组装用户头像与名称,批量查询只要查一次数据库
return PageUtil.convert(postPage, post -> {
PostBasicInfoDTO postBasicInfoDTO = BeanUtil.copyProperties(post, PostBasicInfoDTO.class);
User user = userMap.getOrDefault(post.getUserId(), new User());
postBasicInfoDTO.setUserAvatar(user.getAvatar());
postBasicInfoDTO.setUserName(user.getUsername());
return postBasicInfoDTO;
});
} }
@Override @Override
public PageResponse<PostBasicInfoDTO> pagePostOfMe(PostPageQueryDTO postPageQueryDTO) { public PageResponse<PostBasicInfoDTO> pagePostOfMe(PostPageQueryDTO postPageQueryDTO) {
Long userId = UserContext.getUserId(); Long userId = UserContext.getUserId();
@ -99,4 +128,5 @@ public class PostServiceImpl extends ServiceImpl<PostMapper, Post> implements Po
IPage<Post> postPage = postMapper.selectPage(PageUtil.convert(postPageQueryDTO), queryWrapper); IPage<Post> postPage = postMapper.selectPage(PageUtil.convert(postPageQueryDTO), queryWrapper);
return PageUtil.convert(postPage, PostBasicInfoDTO.class); return PageUtil.convert(postPage, PostBasicInfoDTO.class);
} }
} }

@ -3,8 +3,11 @@ package com.luojia_channel.modules.post.utils;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.luojia_channel.common.exception.PostException; import com.luojia_channel.common.exception.PostException;
import com.luojia_channel.common.utils.UserContext; import com.luojia_channel.common.utils.UserContext;
import com.luojia_channel.modules.post.dto.req.CommentSaveDTO;
import com.luojia_channel.modules.post.dto.req.PostSaveDTO; import com.luojia_channel.modules.post.dto.req.PostSaveDTO;
import com.luojia_channel.modules.post.entity.Comment;
import com.luojia_channel.modules.post.entity.Post; import com.luojia_channel.modules.post.entity.Post;
import com.luojia_channel.modules.post.mapper.CommentMapper;
import com.luojia_channel.modules.post.mapper.PostMapper; import com.luojia_channel.modules.post.mapper.PostMapper;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -16,6 +19,8 @@ public class ValidatePostUtil {
private final PostMapper postMapper; private final PostMapper postMapper;
private final CommentMapper commentMapper;
public void validatePost(PostSaveDTO postSaveDTO) { public void validatePost(PostSaveDTO postSaveDTO) {
// 非空字段检验 // 非空字段检验
if (StrUtil.isBlank(postSaveDTO.getTitle())) { if (StrUtil.isBlank(postSaveDTO.getTitle())) {
@ -34,6 +39,9 @@ public class ValidatePostUtil {
public void validatePostOwnership(Long id){ public void validatePostOwnership(Long id){
Long userId = UserContext.getUserId(); Long userId = UserContext.getUserId();
if(id == null){
throw new PostException("传入id不能为空");
}
Post post = postMapper.selectById(id); Post post = postMapper.selectById(id);
if(post == null){ if(post == null){
throw new PostException("帖子不存在"); throw new PostException("帖子不存在");
@ -43,7 +51,32 @@ public class ValidatePostUtil {
} }
} }
public void AIValidate(PostSaveDTO postSaveDTO){ public void validateComment(CommentSaveDTO commentSaveDTO) {
if (StrUtil.isBlank(commentSaveDTO.getContent())) {
throw new PostException("内容不能为空");
}
if (commentSaveDTO.getPostId() == null || commentSaveDTO.getPostId() < 0){
throw new PostException("帖子id不合法");
}
if(commentSaveDTO.getParentCommentId() != null && commentSaveDTO.getParentCommentId() < 0){
throw new PostException("父评论id不合法");
}
if(commentSaveDTO.getTopId() != null && commentSaveDTO.getTopId() <= 0){
throw new PostException("顶级评论id不合法");
}
}
public void validateCommentOwnership(Long id) {
Long userId = UserContext.getUserId();
if(id == null){
throw new PostException("传入id不能为空");
}
Comment comment = commentMapper.selectById(id);
if(comment == null){
throw new PostException("评论不存在");
}
if(!userId.equals(comment.getUserId())){
throw new PostException("你无权操作他人的评论");
}
} }
} }

@ -101,10 +101,11 @@ DROP TABLE IF EXISTS `comment`;
CREATE TABLE `comment` ( CREATE TABLE `comment` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID', `id` BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
`content` TEXT NOT NULL COMMENT '评论内容', `content` TEXT NOT NULL COMMENT '评论内容',
`like_count` INT DEFAULT 0 COMMENT '点赞数',
`reply_count` INT DEFAULT 0 COMMENT '回复数',
`user_id` BIGINT NOT NULL COMMENT '评论用户ID', `user_id` BIGINT NOT NULL COMMENT '评论用户ID',
`post_type` VARCHAR(20) NOT NULL COMMENT '帖子类型post/video',
`post_id` BIGINT NOT NULL COMMENT '关联的帖子ID', `post_id` BIGINT NOT NULL COMMENT '关联的帖子ID',
`parent_comment_id` BIGINT COMMENT '父评论ID', `parent_comment_id` BIGINT DEFAULT 0 COMMENT '父评论ID',
`top_id` BIGINT COMMENT '顶层评论ID', `top_id` BIGINT COMMENT '顶层评论ID',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',

Loading…
Cancel
Save