Browse Source

增加用户作品点赞模块

glx_phase2
glx 1 day ago
parent
commit
252db3db99
  1. 4
      src/main/java/com/youlai/boot/mini/controller/AdoptionDiaryCommentController.java
  2. 8
      src/main/java/com/youlai/boot/mini/controller/StrayAnimalNoteCommentController.java
  3. 94
      src/main/java/com/youlai/boot/mini/controller/UserPostCommentController.java
  4. 3
      src/main/java/com/youlai/boot/mini/controller/UserPostController.java
  5. 7
      src/main/java/com/youlai/boot/mini/mapper/MiniUserPostCommentLikeMapper.java
  6. 36
      src/main/java/com/youlai/boot/mini/mapper/MiniUserPostCommentMapper.java
  7. 2
      src/main/java/com/youlai/boot/mini/mapper/MiniUserPostViewMapper.java
  8. 18
      src/main/java/com/youlai/boot/mini/model/form/DeleteUserPostCommentForm.java
  9. 14
      src/main/java/com/youlai/boot/mini/model/form/PostCommentLikeForm.java
  10. 27
      src/main/java/com/youlai/boot/mini/model/form/UserPostCommentForm.java
  11. 15
      src/main/java/com/youlai/boot/mini/model/query/PostFirstLevelCommentQueryParam.java
  12. 19
      src/main/java/com/youlai/boot/mini/model/query/PostSecondLevelCommentQueryParam.java
  13. 44
      src/main/java/com/youlai/boot/mini/model/vo/PostFirstLevelCommentVO.java
  14. 51
      src/main/java/com/youlai/boot/mini/model/vo/PostSecondLevelCommentVO.java
  15. 55
      src/main/java/com/youlai/boot/mini/model/vo/UserPostCommentVO.java
  16. 28
      src/main/java/com/youlai/boot/mini/service/UserPostCommentService.java
  17. 2
      src/main/java/com/youlai/boot/mini/service/UserPostService.java
  18. 318
      src/main/java/com/youlai/boot/mini/service/impl/UserPostCommentServiceImpl.java
  19. 37
      src/main/java/com/youlai/boot/mini/service/impl/UserPostServiceImpl.java
  20. 30
      src/main/resources/mapper/mini/MiniUserPostCommentLikeMapper.xml
  21. 118
      src/main/resources/mapper/mini/MiniUserPostCommentMapper.xml
  22. 11
      src/main/resources/mapper/mini/MiniUserPostViewMapper.xml

4
src/main/java/com/youlai/boot/mini/controller/AdoptionDiaryCommentController.java

@ -59,7 +59,7 @@ public class AdoptionDiaryCommentController {
}
@Operation(summary = "分页查询领养日记一级评论列表接口")
@PostMapping(value = "firstLevel/list")
@GetMapping(value = "firstLevel/list")
@Log(module = LogModuleEnum.ADOPTION_DIARY_COMMENT, value = ActionTypeEnum.LIST)
public Result<IPage<DiaryFirstLevelCommentVO>> getFirstLevelCommentList(
@Valid @RequestBody DiaryFirstLevelCommentQueryParam queryParam
@ -70,7 +70,7 @@ public class AdoptionDiaryCommentController {
}
@Operation(summary = "分页查询领养日记二级评论列表接口")
@PostMapping(value = "secondLevel/list")
@GetMapping(value = "secondLevel/list")
@Log(module = LogModuleEnum.ADOPTION_DIARY_COMMENT, value = ActionTypeEnum.LIST)
public Result<IPage<DiarySecondLevelCommentVO>> getSecondLevelCommentList(
@Valid @RequestBody DiarySecondLevelCommentQueryParam queryParam

8
src/main/java/com/youlai/boot/mini/controller/StrayAnimalNoteCommentController.java

@ -57,10 +57,10 @@ public class StrayAnimalNoteCommentController {
}
@Operation(summary = "分页查询笔记一级评论列表接口")
@PostMapping(value = "firstLevel/list")
@GetMapping(value = "firstLevel/list")
@Log(module = LogModuleEnum.STRAY_ANIMAL_NOTE_COMMENT, value = ActionTypeEnum.LIST)
public Result<IPage<AnimalNoteFirstLevelCommentVO>> getFirstLevelCommentList(
@Valid @RequestBody AnimalNoteFirstLevelCommentQueryParam queryParam
@Valid AnimalNoteFirstLevelCommentQueryParam queryParam
) {
Long userId = SecurityUtils.getUserId();
IPage<AnimalNoteFirstLevelCommentVO> result = strayAnimalNoteCommentService.getFirstLevelCommentList(queryParam, userId);
@ -68,10 +68,10 @@ public class StrayAnimalNoteCommentController {
}
@Operation(summary = "分页查询笔记二级评论列表接口")
@PostMapping(value = "secondLevel/list")
@GetMapping(value = "secondLevel/list")
@Log(module = LogModuleEnum.STRAY_ANIMAL_NOTE_COMMENT, value = ActionTypeEnum.LIST)
public Result<IPage<AnimalNoteSecondLevelCommentVO>> getSecondLevelCommentList(
@Valid @RequestBody AnimalNoteSecondLevelCommentQueryParam queryParam
@Valid AnimalNoteSecondLevelCommentQueryParam queryParam
) {
Long userId = SecurityUtils.getUserId();
IPage<AnimalNoteSecondLevelCommentVO> result = strayAnimalNoteCommentService.getSecondLevelCommentList(queryParam, userId);

94
src/main/java/com/youlai/boot/mini/controller/UserPostCommentController.java

@ -0,0 +1,94 @@
package com.youlai.boot.mini.controller;
import com.youlai.boot.common.annotation.Log;
import com.youlai.boot.common.annotation.RepeatSubmit;
import com.youlai.boot.common.enums.ActionTypeEnum;
import com.youlai.boot.common.enums.LogModuleEnum;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.youlai.boot.common.result.Result;
import com.youlai.boot.framework.security.util.SecurityUtils;
import com.youlai.boot.mini.model.form.DeleteUserPostCommentForm;
import com.youlai.boot.mini.model.form.PostCommentLikeForm;
import com.youlai.boot.mini.model.form.UserPostCommentForm;
import com.youlai.boot.mini.model.query.PostFirstLevelCommentQueryParam;
import com.youlai.boot.mini.model.query.PostSecondLevelCommentQueryParam;
import com.youlai.boot.mini.model.vo.PostFirstLevelCommentVO;
import com.youlai.boot.mini.model.vo.PostSecondLevelCommentVO;
import com.youlai.boot.mini.model.vo.UserPostCommentVO;
import com.youlai.boot.mini.service.UserPostCommentService;
import java.util.Map;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
@Tag(name = "用户作品评论的相关接口")
@RestController
@RequestMapping("/api/v1/mini/postComment")
@RequiredArgsConstructor
public class UserPostCommentController {
private final UserPostCommentService userPostCommentService;
@Operation(summary = "用户作品评论接口")
@PostMapping(value = "add")
@RepeatSubmit
@Log(module = LogModuleEnum.USER_POST_COMMENT, value = ActionTypeEnum.INSERT)
public Result<?> addUserPostComment(
@Valid @RequestBody UserPostCommentForm formData
) {
UserPostCommentVO vo = userPostCommentService.addUserPostComment(formData);
return Result.success(vo);
}
@Operation(summary = "删除用户作品评论接口")
@PostMapping(value = "delete")
@PreAuthorize("isAuthenticated()")
@RepeatSubmit
@Log(module = LogModuleEnum.USER_POST_COMMENT, value = ActionTypeEnum.DELETE)
public Result<?> deleteUserPostComment(
@Valid @RequestBody DeleteUserPostCommentForm formData
) {
Long userId = SecurityUtils.getUserId();
userPostCommentService.deleteUserPostComment(formData, userId);
return Result.success();
}
@Operation(summary = "分页查询用户作品一级评论列表接口")
@GetMapping(value = "firstLevel/list")
@Log(module = LogModuleEnum.USER_POST_COMMENT, value = ActionTypeEnum.LIST)
public Result<IPage<PostFirstLevelCommentVO>> getFirstLevelCommentList(
@Valid PostFirstLevelCommentQueryParam queryParam
) {
Long userId = SecurityUtils.getUserId();
IPage<PostFirstLevelCommentVO> result = userPostCommentService.getFirstLevelCommentList(queryParam, userId);
return Result.success(result);
}
@Operation(summary = "分页查询用户作品二级评论列表接口")
@GetMapping(value = "secondLevel/list")
@Log(module = LogModuleEnum.USER_POST_COMMENT, value = ActionTypeEnum.LIST)
public Result<IPage<PostSecondLevelCommentVO>> getSecondLevelCommentList(
@Valid PostSecondLevelCommentQueryParam queryParam
) {
Long userId = SecurityUtils.getUserId();
IPage<PostSecondLevelCommentVO> result = userPostCommentService.getSecondLevelCommentList(queryParam, userId);
return Result.success(result);
}
@Operation(summary = "评论点赞/取消点赞接口")
@PostMapping(value = "like/toggle")
@PreAuthorize("isAuthenticated()")
@RepeatSubmit(expire = 1)
@Log(module = LogModuleEnum.USER_POST_COMMENT, value = ActionTypeEnum.UPDATE)
public Result<Map<String, Object>> toggleLike(
@Valid @RequestBody PostCommentLikeForm form
) {
Long userId = SecurityUtils.getUserId();
Map<String, Object> result = userPostCommentService.toggleLike(form, userId);
return Result.success(result);
}
}

3
src/main/java/com/youlai/boot/mini/controller/UserPostController.java

@ -121,7 +121,8 @@ public class UserPostController {
@Operation(summary = "获取用户作品详情")
@RequestMapping(value = "/getDetails/{postUuid}", method = RequestMethod.GET)
public Result<UserPostDetailsVO> getDetails(@PathVariable String postUuid) {
return Result.success(userPostService.getDetails(postUuid));
Long userId = SecurityUtils.getUserId();
return Result.success(userPostService.getDetails(postUuid, userId));
}
@Operation(summary = "获取某个用户创建的作品列表")

7
src/main/java/com/youlai/boot/mini/mapper/MiniUserPostCommentLikeMapper.java

@ -2,6 +2,7 @@ package com.youlai.boot.mini.mapper;
import com.youlai.boot.mini.model.entity.MiniUserPostCommentLike;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
/**
* 用户作品评论点赞表 Mapper 接口
@ -11,4 +12,10 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
*/
public interface MiniUserPostCommentLikeMapper extends BaseMapper<MiniUserPostCommentLike> {
Integer selectUserLikeCount(@Param("commentId") Long commentId, @Param("userId") Long userId);
int insertOrUpdateLike(MiniUserPostCommentLike like);
int deleteLike(@Param("commentId") Long commentId, @Param("userId") Long userId, @Param("currentTime") Long currentTime);
}

36
src/main/java/com/youlai/boot/mini/mapper/MiniUserPostCommentMapper.java

@ -1,7 +1,16 @@
package com.youlai.boot.mini.mapper;
import com.youlai.boot.mini.model.entity.MiniUserPostComment;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.youlai.boot.mini.model.entity.MiniUserPostComment;
import com.youlai.boot.mini.model.entity.MiniUserPostCommentLike;
import com.youlai.boot.mini.model.query.PostFirstLevelCommentQueryParam;
import com.youlai.boot.mini.model.query.PostSecondLevelCommentQueryParam;
import com.youlai.boot.mini.model.vo.PostFirstLevelCommentVO;
import com.youlai.boot.mini.model.vo.PostSecondLevelCommentVO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 用户作品评论表 Mapper 接口
@ -11,4 +20,29 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
*/
public interface MiniUserPostCommentMapper extends BaseMapper<MiniUserPostComment> {
Long selectIdByUuid(String uuid);
MiniUserPostComment selectParentCommentInfoByUuid(String uuid);
String selectUuidById(Long id);
int incrementCommentCount(Long postId);
int decrementCommentCount(Long postId);
int deleteCommentWithPermissionCheck(@Param("postUuid") String postUuid, @Param("commentUuid") String commentUuid, @Param("userId") Long userId);
IPage<PostFirstLevelCommentVO> getFirstLevelComment(IPage<PostFirstLevelCommentVO> page, @Param("query") PostFirstLevelCommentQueryParam query);
List<MiniUserPostCommentLike> batchGetUserCommentLikes(@Param("commentIds") List<Long> commentIds, @Param("userId") Long userId);
IPage<PostSecondLevelCommentVO> getSecondLevelComment(IPage<PostSecondLevelCommentVO> page, @Param("query") PostSecondLevelCommentQueryParam query);
int incrementLikeCount(@Param("commentId") Long commentId);
int decrementLikeCount(@Param("commentId") Long commentId);
Long selectLikeCount(@Param("commentId") Long commentId);
}

2
src/main/java/com/youlai/boot/mini/mapper/MiniUserPostViewMapper.java

@ -16,4 +16,6 @@ public interface MiniUserPostViewMapper extends BaseMapper<MiniUserPostView> {
List<Long> selectViewedPostIdsByUserAndTime(@Param("userId") Long userId, @Param("startTime") Long startTime);
int insertOrUpdateView(MiniUserPostView view);
}

18
src/main/java/com/youlai/boot/mini/model/form/DeleteUserPostCommentForm.java

@ -0,0 +1,18 @@
package com.youlai.boot.mini.model.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
@Schema(description = "删除用户作品评论请求参数")
public class DeleteUserPostCommentForm {
@NotBlank(message = "用户作品UUID不能为空")
@Schema(description = "用户作品UUID", requiredMode = Schema.RequiredMode.REQUIRED)
private String postUuid;
@NotBlank(message = "评论UUID不能为空")
@Schema(description = "评论UUID", requiredMode = Schema.RequiredMode.REQUIRED)
private String commentUuid;
}

14
src/main/java/com/youlai/boot/mini/model/form/PostCommentLikeForm.java

@ -0,0 +1,14 @@
package com.youlai.boot.mini.model.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
@Schema(description = "用户作品评论点赞请求参数")
public class PostCommentLikeForm {
@NotBlank(message = "评论UUID不能为空")
@Schema(description = "评论UUID", requiredMode = Schema.RequiredMode.REQUIRED)
private String commentUuid;
}

27
src/main/java/com/youlai/boot/mini/model/form/UserPostCommentForm.java

@ -0,0 +1,27 @@
package com.youlai.boot.mini.model.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.validator.constraints.Length;
@Schema(description = "用户作品评论表单对象")
@Getter
@Setter
public class UserPostCommentForm {
@NotBlank(message = "用户作品uuid不能为空")
@Schema(type = "string", description = "用户作品uuid", required = true)
private String postUuid;
@NotBlank(message = "评论内容不能为空")
@Length(max = 255, message = "评论不能超过255个字符")
@Schema(description = "评论内容")
private String content;
@NotBlank(message = "父评论uuid不能为空")
@Schema(type = "string", description = "父评论ID,0为一级评论", required = true)
private String parentUuId;
}

15
src/main/java/com/youlai/boot/mini/model/query/PostFirstLevelCommentQueryParam.java

@ -0,0 +1,15 @@
package com.youlai.boot.mini.model.query;
import com.youlai.boot.common.base.BaseQuery;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
@Schema(description = "用户作品一级评论分页查询参数")
public class PostFirstLevelCommentQueryParam extends BaseQuery {
@NotBlank(message = "用户作品UUID不能为空")
@Schema(description = "用户作品UUID", requiredMode = Schema.RequiredMode.REQUIRED)
private String postUuid;
}

19
src/main/java/com/youlai/boot/mini/model/query/PostSecondLevelCommentQueryParam.java

@ -0,0 +1,19 @@
package com.youlai.boot.mini.model.query;
import com.youlai.boot.common.base.BaseQuery;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
@Schema(description = "用户作品二级评论分页查询参数")
public class PostSecondLevelCommentQueryParam extends BaseQuery {
@NotBlank(message = "用户作品UUID不能为空")
@Schema(description = "用户作品UUID", requiredMode = Schema.RequiredMode.REQUIRED)
private String postUuid;
@NotBlank(message = "根评论UUID不能为空")
@Schema(description = "根评论(一级评论)UUID", requiredMode = Schema.RequiredMode.REQUIRED)
private String rootUuid;
}

44
src/main/java/com/youlai/boot/mini/model/vo/PostFirstLevelCommentVO.java

@ -0,0 +1,44 @@
package com.youlai.boot.mini.model.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "用户作品一级评论VO")
public class PostFirstLevelCommentVO {
@JsonIgnore
@Schema(description = "评论自增id", hidden = true)
private Long commentId;
@Schema(description = "评论UUID")
private String uuid;
@Schema(description = "评论用户UUID")
private String appUserId;
@Schema(description = "评论用户头像")
private String avatarUrl;
@Schema(description = "评论用户昵称")
private String nickname;
@Schema(description = "评论内容")
private String content;
@Schema(description = "点赞数")
private Integer likeCount;
@Schema(description = "二级评论数")
private Integer replyCount;
@Schema(description = "创建时间戳")
private Long createTimestamp;
@Schema(description = "评论IP归属地")
private String province;
@Schema(description = "当前用户是否点赞")
private Boolean isLiked;
}

51
src/main/java/com/youlai/boot/mini/model/vo/PostSecondLevelCommentVO.java

@ -0,0 +1,51 @@
package com.youlai.boot.mini.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "用户作品二级评论VO")
public class PostSecondLevelCommentVO {
@Schema(description = "评论内部ID")
private Long commentId;
@Schema(description = "评论UUID")
private String uuid;
@Schema(description = "评论用户UUID")
private String appUserId;
@Schema(description = "评论用户头像")
private String avatarUrl;
@Schema(description = "评论用户昵称")
private String nickname;
@Schema(description = "评论内容")
private String content;
@Schema(description = "父评论UUID")
private String parentUuid;
@Schema(description = "被回复的用户ID")
private String replyToUserId;
@Schema(description = "被回复用户头像")
private String replyToAvatarUrl;
@Schema(description = "被回复用户昵称")
private String replyToNickname;
@Schema(description = "点赞数")
private Integer likeCount;
@Schema(description = "创建时间戳")
private Long createTimestamp;
@Schema(description = "评论IP归属地")
private String province;
@Schema(description = "当前用户是否点赞")
private Boolean isLiked;
}

55
src/main/java/com/youlai/boot/mini/model/vo/UserPostCommentVO.java

@ -0,0 +1,55 @@
package com.youlai.boot.mini.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "用户作品评论VO")
public class UserPostCommentVO {
@Schema(description = "用户作品uuid")
private String postUuId;
@Schema(description = "用户作品评论uuid")
private String uuid;
@Schema(description = "评论用户uuid")
private String appUserId;
@Schema(description = "评论用户头像")
private String avatarUrl;
@Schema(description = "评论用户昵称")
private String nickname = "用户已注销";
@Schema(description = "评论内容")
private String content;
@Schema(description = "父评论uuid,0为一级评论")
private String parentUuId;
@Schema(description = "根评论ID,一级评论为自身ID,二级及以上为所属一级评论ID")
private String rootId;
@Schema(description = "被回复的用户ID")
private String replyToUserId;
@Schema(description = "被回复用户头像")
private String replyToAvatarUrl;
@Schema(description = "被回复用户昵称")
private String replyToNickname;
@Schema(description = "点赞数")
private Integer likeCount;
@Schema(description = "创建时间")
private Long createdAt;
@Schema(description = "当前用户是否点赞评论,true点赞,false未点赞")
private Boolean isLiked;
@Schema(description = "所在省")
private String province;
}

28
src/main/java/com/youlai/boot/mini/service/UserPostCommentService.java

@ -0,0 +1,28 @@
package com.youlai.boot.mini.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.youlai.boot.mini.model.entity.MiniUserPostComment;
import com.youlai.boot.mini.model.form.PostCommentLikeForm;
import com.youlai.boot.mini.model.form.UserPostCommentForm;
import com.youlai.boot.mini.model.query.PostFirstLevelCommentQueryParam;
import com.youlai.boot.mini.model.query.PostSecondLevelCommentQueryParam;
import com.youlai.boot.mini.model.vo.PostFirstLevelCommentVO;
import com.youlai.boot.mini.model.vo.PostSecondLevelCommentVO;
import com.youlai.boot.mini.model.vo.UserPostCommentVO;
import java.util.Map;
public interface UserPostCommentService extends IService<MiniUserPostComment> {
UserPostCommentVO addUserPostComment(UserPostCommentForm formData);
void deleteUserPostComment(com.youlai.boot.mini.model.form.DeleteUserPostCommentForm formData, Long userId);
IPage<PostFirstLevelCommentVO> getFirstLevelCommentList(PostFirstLevelCommentQueryParam queryParam, Long userId);
IPage<PostSecondLevelCommentVO> getSecondLevelCommentList(PostSecondLevelCommentQueryParam queryParam, Long userId);
Map<String, Object> toggleLike(PostCommentLikeForm form, Long userId);
}

2
src/main/java/com/youlai/boot/mini/service/UserPostService.java

@ -37,7 +37,7 @@ public interface UserPostService extends IService<MiniUserPost> {
IPage<UserPostVO> getSelfCreatedPage(OwnUserPostQuery queryParams);
UserPostDetailsVO getDetails(String postUuid);
UserPostDetailsVO getDetails(String postUuid, Long userId);
IPage<UserPostVO> getOthersCreatedPage(String authorUuid, OwnUserPostQuery queryParams);

318
src/main/java/com/youlai/boot/mini/service/impl/UserPostCommentServiceImpl.java

@ -0,0 +1,318 @@
package com.youlai.boot.mini.service.impl;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
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.youlai.boot.common.exception.MsgException;
import com.youlai.boot.common.util.IPUtils;
import com.youlai.boot.common.util.HttpContext;
import com.youlai.boot.framework.security.util.SecurityUtils;
import com.youlai.boot.mini.mapper.MiniUserPostCommentLikeMapper;
import com.youlai.boot.mini.mapper.MiniUserPostCommentMapper;
import com.youlai.boot.mini.mapper.MiniUserPostMapper;
import com.youlai.boot.mini.model.entity.MiniUserPostComment;
import com.youlai.boot.mini.model.entity.MiniUserPostCommentLike;
import com.youlai.boot.mini.model.form.DeleteUserPostCommentForm;
import com.youlai.boot.mini.model.form.PostCommentLikeForm;
import com.youlai.boot.mini.model.form.UserPostCommentForm;
import com.youlai.boot.mini.model.query.PostFirstLevelCommentQueryParam;
import com.youlai.boot.mini.model.query.PostSecondLevelCommentQueryParam;
import com.youlai.boot.mini.model.vo.PostFirstLevelCommentVO;
import com.youlai.boot.mini.model.vo.PostSecondLevelCommentVO;
import com.youlai.boot.mini.model.vo.UserPostCommentVO;
import com.youlai.boot.mini.service.UserPostCommentService;
import com.youlai.boot.system.mapper.UserMapper;
import com.youlai.boot.system.model.entity.SysUser;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Slf4j
@Service
@RequiredArgsConstructor
public class UserPostCommentServiceImpl extends ServiceImpl<MiniUserPostCommentMapper, MiniUserPostComment> implements UserPostCommentService {
private final MiniUserPostMapper miniUserPostMapper;
private final UserMapper userMapper;
private final MiniUserPostCommentLikeMapper miniUserPostCommentLikeMapper;
@Override
@Transactional(rollbackFor = Exception.class)
public UserPostCommentVO addUserPostComment(UserPostCommentForm formData) {
Long userId = SecurityUtils.getUserId();
if (userId == null) {
throw new MsgException("请先登录");
}
return saveCommentInfo(formData, userId);
}
private UserPostCommentVO saveCommentInfo(UserPostCommentForm formData, Long appUserId) {
// 1. 根据作品UUID查询ID
Long postId = miniUserPostMapper.selectIdByUuid(formData.getPostUuid());
if (postId == null) {
throw new MsgException("作品不存在或已删除");
}
// 2. 获取父评论信息
Long parentCommentId = 0L;
MiniUserPostComment parentComment = null;
SysUser replyToUser = null;
if (!"0".equals(formData.getParentUuId())) {
parentComment = baseMapper.selectParentCommentInfoByUuid(formData.getParentUuId());
if (parentComment == null) {
throw new MsgException("父评论已消失");
}
if (!parentComment.getPostId().equals(postId)) {
throw new MsgException("父评论不属于当前作品");
}
parentCommentId = parentComment.getId();
replyToUser = userMapper.selectById(parentComment.getMiniUserId());
}
// 3. 获取用户IP位置
String userIp = IPUtils.getIpAddr(HttpContext.getRequest());
String ipArr = IPUtils.getRegion(userIp);
String provinceShortName = "未知";
try {
if (ipArr != null) {
String[] ipInfo = ipArr.split("\\|");
if (ipInfo.length >= 3 && !"0".equals(ipInfo[2])) {
provinceShortName = ipInfo[2];
}
}
} catch (Exception e) {
log.error("获取用户ip归属地失败", e);
}
// 4. 保存评论
long currentTimestamp = System.currentTimeMillis();
MiniUserPostComment comment = new MiniUserPostComment();
comment.setUuid(IdUtil.fastSimpleUUID());
comment.setPostId(postId);
comment.setMiniUserId(appUserId);
comment.setContent(formData.getContent());
comment.setParentId(parentCommentId);
comment.setRootId(0L);
if (replyToUser != null) {
comment.setReplyToUserId(replyToUser.getId());
}
comment.setLikeCount(0);
comment.setProvince(provinceShortName);
comment.setCreateTimestamp(currentTimestamp);
comment.setCreateTime(new Date(currentTimestamp));
comment.setCreateBy(appUserId);
baseMapper.insert(comment);
// 5. 一级评论的rootId是自己,二级评论是父评论的rootId
Long rootId = parentCommentId == 0 ? comment.getId() : parentComment.getRootId();
comment.setRootId(rootId);
baseMapper.updateById(comment);
// 6. 构建返回VO
UserPostCommentVO vo = new UserPostCommentVO();
SysUser appUser = userMapper.selectById(appUserId);
vo.setNickname(appUser.getNickname());
vo.setAvatarUrl(appUser.getAvatar());
vo.setPostUuId(formData.getPostUuid());
vo.setUuid(comment.getUuid());
vo.setAppUserId(appUser.getUuid());
vo.setContent(comment.getContent());
vo.setParentUuId(formData.getParentUuId());
String rootUuid = baseMapper.selectUuidById(comment.getRootId());
if (rootUuid == null) {
throw new MsgException("评论所属楼层已删除");
}
vo.setRootId(rootUuid);
if (comment.getReplyToUserId() != null && replyToUser != null) {
if (replyToUser.getIsDeleted() == 1) {
vo.setReplyToNickname("用户已注销");
} else {
vo.setReplyToUserId(replyToUser.getUuid());
vo.setReplyToNickname(replyToUser.getNickname());
vo.setReplyToAvatarUrl(replyToUser.getAvatar());
}
}
vo.setCreatedAt(comment.getCreateTimestamp());
vo.setIsLiked(false);
vo.setLikeCount(0);
vo.setProvince(comment.getProvince());
// 7. 更新作品评论数
baseMapper.incrementCommentCount(postId);
return vo;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void deleteUserPostComment(DeleteUserPostCommentForm formData, Long userId) {
// 1. 执行原子删除(带权限校验、存在性校验)
int affectRows = baseMapper.deleteCommentWithPermissionCheck(
formData.getPostUuid(),
formData.getCommentUuid(),
userId
);
// 2. 删除成功,扣减评论数
if (affectRows == 1) {
Long postId = miniUserPostMapper.selectIdByUuid(formData.getPostUuid());
baseMapper.decrementCommentCount(postId);
return;
}
// 3. 删除失败,查询具体错误原因
Long postId = miniUserPostMapper.selectIdByUuid(formData.getPostUuid());
if (postId == null) {
throw new MsgException("作品不存在或已删除");
}
MiniUserPostComment comment = baseMapper.selectParentCommentInfoByUuid(formData.getCommentUuid());
if (comment == null || !comment.getPostId().equals(postId)) {
throw new MsgException("评论不存在或已删除");
}
throw new MsgException("无权限删除该评论");
}
@Override
@Transactional(readOnly = true, rollbackFor = Exception.class)
public IPage<PostFirstLevelCommentVO> getFirstLevelCommentList(PostFirstLevelCommentQueryParam queryParam, Long userId) {
// 1. 构造分页参数
Page<PostFirstLevelCommentVO> page = new Page<>(queryParam.getPageNum(), queryParam.getPageSize());
// 2. 查询一级评论分页
IPage<PostFirstLevelCommentVO> result = baseMapper.getFirstLevelComment(page, queryParam);
List<PostFirstLevelCommentVO> commentList = result.getRecords();
if (commentList.isEmpty() || userId == null) {
return result;
}
// 3. 提取一级评论内部主键ID
List<Long> firstLevelCommentIds = commentList.stream()
.map(PostFirstLevelCommentVO::getCommentId)
.collect(Collectors.toList());
// 4. 批量查询登录用户的点赞状态
Map<Long, Boolean> likeStatusMap = Collections.emptyMap();
try {
List<MiniUserPostCommentLike> likeList = baseMapper.batchGetUserCommentLikes(firstLevelCommentIds, userId);
likeStatusMap = likeList.stream()
.collect(Collectors.toMap(
MiniUserPostCommentLike::getPostCommentId,
like -> Boolean.TRUE,
(v1, v2) -> v1
));
} catch (Exception e) {
log.error("批量查询评论点赞状态失败", e);
}
// 5. 设置点赞状态
for (PostFirstLevelCommentVO comment : commentList) {
comment.setIsLiked(likeStatusMap.getOrDefault(comment.getCommentId(), Boolean.FALSE));
}
return result;
}
@Override
@Transactional(readOnly = true, rollbackFor = Exception.class)
public IPage<PostSecondLevelCommentVO> getSecondLevelCommentList(PostSecondLevelCommentQueryParam queryParam, Long userId) {
// 1. 构造分页参数
Page<PostSecondLevelCommentVO> page = new Page<>(queryParam.getPageNum(), queryParam.getPageSize());
// 2. 查询二级评论分页
IPage<PostSecondLevelCommentVO> result = baseMapper.getSecondLevelComment(page, queryParam);
List<PostSecondLevelCommentVO> commentList = result.getRecords();
if (commentList.isEmpty() || userId == null) {
return result;
}
// 3. 提取二级评论内部主键ID
List<Long> secondLevelCommentIds = commentList.stream()
.map(PostSecondLevelCommentVO::getCommentId)
.collect(Collectors.toList());
// 4. 批量查询登录用户的点赞状态
Map<Long, Boolean> likeStatusMap = Collections.emptyMap();
try {
List<MiniUserPostCommentLike> likeList = baseMapper.batchGetUserCommentLikes(secondLevelCommentIds, userId);
likeStatusMap = likeList.stream()
.collect(Collectors.toMap(
MiniUserPostCommentLike::getPostCommentId,
like -> Boolean.TRUE,
(v1, v2) -> v1
));
} catch (Exception e) {
log.error("批量查询二级评论点赞状态失败", e);
}
// 5. 设置点赞状态和已注销用户昵称
for (PostSecondLevelCommentVO comment : commentList) {
comment.setIsLiked(likeStatusMap.getOrDefault(comment.getCommentId(), Boolean.FALSE));
if (comment.getReplyToUserId() != null && comment.getReplyToNickname() == null) {
comment.setReplyToNickname("用户已注销");
}
}
return result;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Map<String, Object> toggleLike(PostCommentLikeForm form, Long userId) {
// 1. 校验评论是否存在
Long commentId = baseMapper.selectIdByUuid(form.getCommentUuid());
if (commentId == null) {
throw new MsgException("评论不存在或已删除");
}
// 2. 查询用户是否已经点赞
Integer likeCount = miniUserPostCommentLikeMapper.selectUserLikeCount(commentId, userId);
boolean isLiked = likeCount != null && likeCount > 0;
boolean targetLike = !isLiked;
long currentTime = System.currentTimeMillis();
if (targetLike) {
// 3. 点赞:新增或更新点赞记录
MiniUserPostCommentLike like = new MiniUserPostCommentLike();
like.setUuid(IdWorker.get32UUID());
like.setPostCommentId(commentId);
like.setMiniUserId(userId);
like.setCreateTimestamp(currentTime);
like.setCreateTime(new Date(currentTime));
like.setCreateBy(userId);
miniUserPostCommentLikeMapper.insertOrUpdateLike(like);
// 4. 原子增加点赞数
baseMapper.incrementLikeCount(commentId);
} else {
// 5. 取消点赞:逻辑删除点赞记录
miniUserPostCommentLikeMapper.deleteLike(commentId, userId, currentTime);
// 6. 原子减少点赞数
baseMapper.decrementLikeCount(commentId);
}
// 7. 查询最新点赞数
Long latestLikeCount = baseMapper.selectLikeCount(commentId);
Map<String, Object> result = new HashMap<>();
result.put("isLiked", targetLike);
result.put("likeCount", latestLikeCount != null ? latestLikeCount : 0);
return result;
}
}

37
src/main/java/com/youlai/boot/mini/service/impl/UserPostServiceImpl.java

@ -27,6 +27,7 @@ import com.youlai.boot.mini.model.entity.MiniUserPost;
import com.youlai.boot.mini.model.entity.MiniUserPostCollect;
import com.youlai.boot.mini.model.entity.MiniUserPostLike;
import com.youlai.boot.mini.model.entity.MiniUserPostMedia;
import com.youlai.boot.mini.model.entity.MiniUserPostView;
import com.youlai.boot.mini.model.enums.AnimalNoteMediaTypeEnum;
import com.youlai.boot.mini.model.form.UserPostCollectForm;
import com.youlai.boot.mini.model.form.UserPostForm;
@ -513,7 +514,7 @@ public class UserPostServiceImpl extends ServiceImpl<MiniUserPostMapper, MiniUse
}
@Override
public UserPostDetailsVO getDetails(String postUuid) {
public UserPostDetailsVO getDetails(String postUuid, Long userId) {
MiniUserPost post = lambdaQuery()
.eq(MiniUserPost::getUuid, postUuid)
.eq(MiniUserPost::getDeleted, false)
@ -542,6 +543,40 @@ public class UserPostServiceImpl extends ServiceImpl<MiniUserPostMapper, MiniUse
detailsVO.setImages(images);
detailsVO.setVideos(videos);
// 记录用户浏览历史
if (userId != null) {
Long postId = post.getId();
long currentTime = System.currentTimeMillis();
// 1. 写数据库
try {
MiniUserPostView view = new MiniUserPostView();
view.setUuid(IdWorker.get32UUID());
view.setMiniUserId(userId);
view.setPostId(postId);
view.setCreateTimestamp(currentTime);
view.setCreateTime(new Date(currentTime));
view.setCreateBy(userId);
miniUserPostViewMapper.insertOrUpdateView(view);
} catch (Exception e) {
log.error("记录浏览历史到数据库失败,userId:{}, postId:{}", userId, postId, e);
}
// 2. 写布隆过滤器
try {
String bloomKey = BLOOM_VIEW_KEY_PREFIX + userId;
RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter(bloomKey);
if (!bloomFilter.isExists()) {
bloomFilter.tryInit(bloomExpectedInsertions, bloomFpp);
if (bloomExpireDays != null && bloomExpireDays > 0) {
bloomFilter.expire(bloomExpireDays, TimeUnit.DAYS);
}
}
bloomFilter.add(postId);
} catch (Exception e) {
log.error("记录浏览历史到布隆失败,userId:{}, postId:{}", userId, postId, e);
}
}
return detailsVO;
}

30
src/main/resources/mapper/mini/MiniUserPostCommentLikeMapper.xml

@ -5,5 +5,35 @@
<mapper namespace="com.youlai.boot.mini.mapper.MiniUserPostCommentLikeMapper">
<select id="selectUserLikeCount" resultType="java.lang.Integer">
SELECT COUNT(1)
FROM mini_user_post_comment_like
WHERE post_comment_id = #{commentId}
AND mini_user_id = #{userId}
AND is_deleted = 0
</select>
<insert id="insertOrUpdateLike">
INSERT INTO mini_user_post_comment_like
(uuid, post_comment_id, mini_user_id, create_timestamp, create_time, create_by, is_deleted)
VALUES
(#{uuid}, #{postCommentId}, #{miniUserId}, #{createTimestamp}, #{createTime}, #{createBy}, 0)
ON DUPLICATE KEY UPDATE
is_deleted = 0,
update_timestamp = #{createTimestamp},
update_time = #{createTime},
update_by = #{createBy}
</insert>
<update id="deleteLike">
UPDATE mini_user_post_comment_like
SET is_deleted = 1,
update_time = NOW(),
update_timestamp = #{currentTime},
update_by = #{userId}
WHERE post_comment_id = #{commentId}
AND mini_user_id = #{userId}
AND is_deleted = 0
</update>
</mapper>

118
src/main/resources/mapper/mini/MiniUserPostCommentMapper.xml

@ -5,5 +5,123 @@
<mapper namespace="com.youlai.boot.mini.mapper.MiniUserPostCommentMapper">
<select id="selectIdByUuid" resultType="java.lang.Long">
SELECT id FROM mini_user_post_comment WHERE uuid = #{uuid} AND is_deleted = 0
</select>
<select id="selectParentCommentInfoByUuid" resultType="com.youlai.boot.mini.model.entity.MiniUserPostComment">
SELECT id, post_id, mini_user_id, parent_id, root_id FROM mini_user_post_comment WHERE uuid = #{uuid} AND is_deleted = 0
</select>
<select id="selectUuidById" resultType="java.lang.String">
SELECT uuid FROM mini_user_post_comment WHERE id = #{id} AND is_deleted = 0
</select>
<update id="incrementCommentCount">
UPDATE mini_user_post SET comment_count = comment_count + 1 WHERE id = #{postId}
</update>
<update id="decrementCommentCount">
UPDATE mini_user_post
SET comment_count = comment_count - 1
WHERE id = #{postId}
AND comment_count > 0
</update>
<update id="deleteCommentWithPermissionCheck">
UPDATE mini_user_post_comment c
INNER JOIN mini_user_post p ON c.post_id = p.id
SET c.is_deleted = 1,
c.update_by = #{userId},
c.update_time = NOW(),
c.update_timestamp = UNIX_TIMESTAMP(NOW(3)) * 1000
WHERE p.uuid = #{postUuid}
AND c.uuid = #{commentUuid}
AND c.create_by = #{userId}
AND c.is_deleted = 0
</update>
<select id="getFirstLevelComment" resultType="com.youlai.boot.mini.model.vo.PostFirstLevelCommentVO">
SELECT
c.id AS commentId,
c.uuid AS uuid,
u.uuid AS appUserId,
u.avatar AS avatarUrl,
u.nickname AS nickname,
c.content AS content,
c.like_count AS likeCount,
(SELECT COUNT(1) FROM mini_user_post_comment c2 WHERE c2.parent_id = c.id AND c2.is_deleted = 0) AS replyCount,
c.create_timestamp AS createTimestamp,
c.province AS province
FROM mini_user_post_comment c
INNER JOIN mini_user_post p ON c.post_id = p.id AND p.uuid = #{query.postUuid} AND p.is_deleted = 0
LEFT JOIN sys_user u ON c.mini_user_id = u.id AND u.is_deleted = 0
WHERE c.parent_id = 0
AND c.is_deleted = 0
ORDER BY c.create_timestamp DESC
</select>
<select id="batchGetUserCommentLikes" resultType="com.youlai.boot.mini.model.entity.MiniUserPostCommentLike">
SELECT
post_comment_id AS postCommentId,
mini_user_id AS miniUserId
FROM
mini_user_post_comment_like
WHERE
mini_user_id = #{userId}
AND post_comment_id IN
<foreach item="commentId" collection="commentIds" open="(" separator="," close=")">
#{commentId}
</foreach>
AND is_deleted = 0
</select>
<select id="getSecondLevelComment" resultType="com.youlai.boot.mini.model.vo.PostSecondLevelCommentVO">
SELECT
c.id AS commentId,
c.uuid AS uuid,
u.uuid AS appUserId,
u.avatar AS avatarUrl,
u.nickname AS nickname,
c.content AS content,
parent_c.uuid AS parentUuid,
reply_u.uuid AS replyToUserId,
reply_u.avatar AS replyToAvatarUrl,
reply_u.nickname AS replyToNickname,
c.like_count AS likeCount,
c.create_timestamp AS createTimestamp,
c.province AS province
FROM mini_user_post_comment c
INNER JOIN mini_user_post p ON c.post_id = p.id AND p.uuid = #{query.postUuid} AND p.is_deleted = 0
INNER JOIN mini_user_post_comment root_c ON c.root_id = root_c.id AND root_c.uuid = #{query.rootUuid} AND root_c.is_deleted = 0
LEFT JOIN mini_user_post_comment parent_c ON c.parent_id = parent_c.id AND parent_c.is_deleted = 0
LEFT JOIN sys_user u ON c.mini_user_id = u.id AND u.is_deleted = 0
LEFT JOIN sys_user reply_u ON c.reply_to_user_id = reply_u.id AND reply_u.is_deleted = 0
WHERE c.parent_id != 0
AND c.is_deleted = 0
ORDER BY c.create_timestamp ASC
</select>
<update id="incrementLikeCount">
UPDATE mini_user_post_comment
SET like_count = like_count + 1
WHERE id = #{commentId}
AND is_deleted = 0
</update>
<update id="decrementLikeCount">
UPDATE mini_user_post_comment
SET like_count = like_count - 1
WHERE id = #{commentId}
AND is_deleted = 0
AND like_count > 0
</update>
<select id="selectLikeCount" resultType="java.lang.Long">
SELECT like_count
FROM mini_user_post_comment
WHERE id = #{commentId}
AND is_deleted = 0
</select>
</mapper>

11
src/main/resources/mapper/mini/MiniUserPostViewMapper.xml

@ -5,6 +5,17 @@
<mapper namespace="com.youlai.boot.mini.mapper.MiniUserPostViewMapper">
<insert id="insertOrUpdateView">
INSERT INTO mini_user_post_view
(uuid, mini_user_id, post_id, create_timestamp, create_time, create_by, is_deleted)
VALUES
(#{uuid}, #{miniUserId}, #{postId}, #{createTimestamp}, #{createTime}, #{createBy}, 0)
ON DUPLICATE KEY UPDATE
create_timestamp = VALUES(create_timestamp),
create_time = VALUES(create_time),
is_deleted = 0
</insert>
<select id="selectViewedPostIdsByUserAndTime" resultType="java.lang.Long">
SELECT post_id
FROM mini_user_post_view

Loading…
Cancel
Save