diff --git a/src/main/java/com/youlai/boot/codegen/freemarker/MyBatisPlusGenerator.java b/src/main/java/com/youlai/boot/codegen/freemarker/MyBatisPlusGenerator.java index e776058..23b270d 100644 --- a/src/main/java/com/youlai/boot/codegen/freemarker/MyBatisPlusGenerator.java +++ b/src/main/java/com/youlai/boot/codegen/freemarker/MyBatisPlusGenerator.java @@ -109,6 +109,8 @@ public class MyBatisPlusGenerator { ,new TableConfig("mini_adoption_diary_view", IdType.AUTO, "mini") ,new TableConfig("mini_adoption_diary_like", IdType.AUTO, "mini") ,new TableConfig("mini_adoption_diary_collect", IdType.AUTO, "mini") + ,new TableConfig("mini_adoption_diary_comment", IdType.AUTO, "mini") + ,new TableConfig("mini_adoption_diary_comment_like", IdType.AUTO, "mini") // ,new TableConfig("mini_stray_animal", IdType.AUTO, "mini") // ,new TableConfig("mini_stray_animal", IdType.INPUT, "minitest") diff --git a/src/main/java/com/youlai/boot/common/enums/LogModuleEnum.java b/src/main/java/com/youlai/boot/common/enums/LogModuleEnum.java index 6c12cd2..eec696f 100644 --- a/src/main/java/com/youlai/boot/common/enums/LogModuleEnum.java +++ b/src/main/java/com/youlai/boot/common/enums/LogModuleEnum.java @@ -34,6 +34,7 @@ public enum LogModuleEnum implements IBaseEnum { POINT_RULE(102, "积分规则"), SIGN_RECORD(103, "签到记录"), STRAY_ANIMAL_NOTE_COMMENT(110, "流浪动物笔记评论"), + ADOPTION_DIARY_COMMENT(111, "领养日记评论"), ADOPTION_DIARY_INFO(112, "领养日记信息"); @EnumValue diff --git a/src/main/java/com/youlai/boot/mini/controller/AdoptionDiaryCommentController.java b/src/main/java/com/youlai/boot/mini/controller/AdoptionDiaryCommentController.java new file mode 100644 index 0000000..e8b1a89 --- /dev/null +++ b/src/main/java/com/youlai/boot/mini/controller/AdoptionDiaryCommentController.java @@ -0,0 +1,96 @@ +package com.youlai.boot.mini.controller; + +import com.baomidou.mybatisplus.core.metadata.IPage; +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.AdoptionDiaryCommentForm; +import com.youlai.boot.mini.model.form.DeleteAdoptionDiaryCommentForm; +import com.youlai.boot.mini.model.form.DiaryCommentLikeForm; +import com.youlai.boot.mini.model.query.DiaryFirstLevelCommentQueryParam; +import com.youlai.boot.mini.model.query.DiarySecondLevelCommentQueryParam; +import com.youlai.boot.mini.model.vo.AdoptionDiaryCommentVO; +import com.youlai.boot.mini.model.vo.DiaryFirstLevelCommentVO; +import com.youlai.boot.mini.model.vo.DiarySecondLevelCommentVO; +import com.youlai.boot.mini.service.AdoptionDiaryCommentService; +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.*; + +import java.util.Map; + +@Tag(name = "领养日记评论的相关接口") +@RestController +@RequestMapping("/api/v1/mini/diaryComment") +@RequiredArgsConstructor +public class AdoptionDiaryCommentController { + + private final AdoptionDiaryCommentService adoptionDiaryCommentService; + + @Operation(summary = "领养日记评论接口") + @PostMapping(value = "add") + @RepeatSubmit + @Log(module = LogModuleEnum.ADOPTION_DIARY_COMMENT, value = ActionTypeEnum.INSERT) + public Result addDiaryComment( + @Valid @RequestBody AdoptionDiaryCommentForm formData + ) { + AdoptionDiaryCommentVO vo = adoptionDiaryCommentService.addDiaryComment(formData); + return Result.success(vo); + } + + @Operation(summary = "删除领养日记评论接口") + @PostMapping(value = "delete") + @PreAuthorize("isAuthenticated()") + @RepeatSubmit + @Log(module = LogModuleEnum.ADOPTION_DIARY_COMMENT, value = ActionTypeEnum.DELETE) + public Result deleteDiaryComment( + @Valid @RequestBody DeleteAdoptionDiaryCommentForm formData + ) { + Long userId = SecurityUtils.getUserId(); + adoptionDiaryCommentService.deleteDiaryComment(formData, userId); + return Result.success(); + } + + @Operation(summary = "分页查询领养日记一级评论列表接口") + @PostMapping(value = "firstLevel/list") + @Log(module = LogModuleEnum.ADOPTION_DIARY_COMMENT, value = ActionTypeEnum.LIST) + public Result> getFirstLevelCommentList( + @Valid @RequestBody DiaryFirstLevelCommentQueryParam queryParam + ) { + Long userId = SecurityUtils.getUserId(); + IPage result = adoptionDiaryCommentService.getFirstLevelCommentList(queryParam, userId); + return Result.success(result); + } + + @Operation(summary = "分页查询领养日记二级评论列表接口") + @PostMapping(value = "secondLevel/list") + @Log(module = LogModuleEnum.ADOPTION_DIARY_COMMENT, value = ActionTypeEnum.LIST) + public Result> getSecondLevelCommentList( + @Valid @RequestBody DiarySecondLevelCommentQueryParam queryParam + ) { + Long userId = SecurityUtils.getUserId(); + IPage result = adoptionDiaryCommentService.getSecondLevelCommentList(queryParam, userId); + return Result.success(result); + } + + @Operation(summary = "评论点赞/取消点赞接口") + @PostMapping(value = "like/toggle") + @PreAuthorize("isAuthenticated()") + @RepeatSubmit(expire = 1) + @Log(module = LogModuleEnum.ADOPTION_DIARY_COMMENT, value = ActionTypeEnum.UPDATE) + public Result> toggleLike( + @Valid @RequestBody DiaryCommentLikeForm form + ) { + Long userId = SecurityUtils.getUserId(); + Map result = adoptionDiaryCommentService.toggleLike(form, userId); + return Result.success(result); + } + +} diff --git a/src/main/java/com/youlai/boot/mini/mapper/MiniAdoptionDiaryCommentLikeMapper.java b/src/main/java/com/youlai/boot/mini/mapper/MiniAdoptionDiaryCommentLikeMapper.java new file mode 100644 index 0000000..73a043a --- /dev/null +++ b/src/main/java/com/youlai/boot/mini/mapper/MiniAdoptionDiaryCommentLikeMapper.java @@ -0,0 +1,29 @@ +package com.youlai.boot.mini.mapper; + +import com.youlai.boot.mini.model.entity.MiniAdoptionDiaryCommentLike; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** +* 领养日记评论点赞表 Mapper 接口 +* +* @author jwy +* @since +*/ +public interface MiniAdoptionDiaryCommentLikeMapper extends BaseMapper { + + /** + * 查询用户是否点赞该评论 + */ + Integer selectUserLikeCount(@Param("commentId") Long commentId, @Param("userId") Long userId); + + /** + * 新增或更新点赞记录(原子操作) + */ + int insertOrUpdateLike(MiniAdoptionDiaryCommentLike like); + + /** + * 逻辑删除点赞记录(取消点赞) + */ + int deleteLike(@Param("commentId") Long commentId, @Param("userId") Long userId, @Param("currentTime") Long currentTime); + +} diff --git a/src/main/java/com/youlai/boot/mini/mapper/MiniAdoptionDiaryCommentMapper.java b/src/main/java/com/youlai/boot/mini/mapper/MiniAdoptionDiaryCommentMapper.java new file mode 100644 index 0000000..b8ec2bb --- /dev/null +++ b/src/main/java/com/youlai/boot/mini/mapper/MiniAdoptionDiaryCommentMapper.java @@ -0,0 +1,83 @@ +package com.youlai.boot.mini.mapper; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.youlai.boot.mini.model.entity.MiniAdoptionDiaryComment; +import com.youlai.boot.mini.model.entity.MiniAdoptionDiaryCommentLike; +import com.youlai.boot.mini.model.query.DiaryFirstLevelCommentQueryParam; +import com.youlai.boot.mini.model.query.DiarySecondLevelCommentQueryParam; +import com.youlai.boot.mini.model.vo.DiaryFirstLevelCommentVO; +import com.youlai.boot.mini.model.vo.DiarySecondLevelCommentVO; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** +* 领养日记评论表 Mapper 接口 +* +* @author jwy +* @since +*/ +public interface MiniAdoptionDiaryCommentMapper extends BaseMapper { + + /** + * 根据UUID查询评论ID + */ + Long selectIdByUuid(String uuid); + + /** + * 根据UUID查询父评论必要字段 + */ + MiniAdoptionDiaryComment selectParentCommentInfoByUuid(String uuid); + + /** + * 根据ID查询评论UUID + */ + String selectUuidById(Long id); + + /** + * 原子增加日记评论数 + */ + int incrementCommentCount(Long diaryId); + + /** + * 原子减少日记评论数 + */ + int decrementCommentCount(@Param("diaryId") Long diaryId); + + /** + * 带权限校验的原子删除评论 + */ + int deleteCommentWithPermissionCheck(@Param("diaryUuid") String diaryUuid, @Param("commentUuid") String commentUuid, @Param("userId") Long userId); + + /** + * 分页查询领养日记一级评论列表 + */ + IPage getFirstLevelComment(IPage page, @Param("query") DiaryFirstLevelCommentQueryParam query); + + /** + * 批量查询用户对指定评论的点赞状态 + */ + List batchGetUserCommentLikes(@Param("commentIds") List commentIds, @Param("userId") Long userId); + + /** + * 分页查询领养日记二级评论列表 + */ + IPage getSecondLevelComment(IPage page, @Param("query") DiarySecondLevelCommentQueryParam query); + + /** + * 原子增加评论点赞数 + */ + int incrementLikeCount(@Param("commentId") Long commentId); + + /** + * 原子减少评论点赞数 + */ + int decrementLikeCount(@Param("commentId") Long commentId); + + /** + * 查询评论点赞数 + */ + Long selectLikeCount(@Param("commentId") Long commentId); + +} diff --git a/src/main/java/com/youlai/boot/mini/model/entity/MiniAdoptionDiaryComment.java b/src/main/java/com/youlai/boot/mini/model/entity/MiniAdoptionDiaryComment.java new file mode 100644 index 0000000..7331756 --- /dev/null +++ b/src/main/java/com/youlai/boot/mini/model/entity/MiniAdoptionDiaryComment.java @@ -0,0 +1,94 @@ +package com.youlai.boot.mini.model.entity; + +import com.baomidou.mybatisplus.annotation.*; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; + +@Getter +@Setter +@ToString +@Accessors(chain = true) +@TableName("mini_adoption_diary_comment") +@Schema(description = "领养日记评论表") +public class MiniAdoptionDiaryComment implements Serializable { + + @TableId(value = "id", type = IdType.AUTO) + @Schema(description = "领养日记评论主键id") + private Long id; + + + @TableField("uuid") + @Schema(description = "uuid唯一标识,前后端用这个进行数据交互") + private String uuid; + + @TableField("diary_id") + @Schema(description = "领养日记id") + private Long diaryId; + + @TableField("mini_user_id") + @Schema(description = "评论者id") + private Long miniUserId; + + @TableField("content") + @Schema(description = "评论内容") + private String content; + + @TableField("parent_id") + @Schema(description = "父评论ID,0为一级评论") + private Long parentId; + + @TableField("root_id") + @Schema(description = "根评论ID,一级评论为自身ID,二级及以上为所属一级评论ID") + private Long rootId; + + @TableField("reply_to_user_id") + @Schema(description = "被回复的用户ID") + private Long replyToUserId; + + @TableField("like_count") + @Schema(description = "点赞数") + private Integer likeCount; + + @TableField("province") + @Schema(description = "所在省") + private String province; + + @TableField("create_time") + @Schema(description = "创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date createTime; + + @TableField("create_timestamp") + @Schema(description = "创建时间毫秒级时间戳") + private Long createTimestamp; + + @TableField("create_by") + @Schema(description = "创建人ID") + private Long createBy; + + @TableField("update_time") + @Schema(description = "更新时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date updateTime; + + @TableField("update_timestamp") + @Schema(description = "更新时间毫秒级时间戳") + private Long updateTimestamp; + + @TableField("update_by") + @Schema(description = "修改人ID") + private Long updateBy; + + @TableField("is_deleted") + @Schema(description = "逻辑删除标识(0-未删除 1-已删除)") + private Boolean deleted; + + +} diff --git a/src/main/java/com/youlai/boot/mini/model/entity/MiniAdoptionDiaryCommentLike.java b/src/main/java/com/youlai/boot/mini/model/entity/MiniAdoptionDiaryCommentLike.java new file mode 100644 index 0000000..9689e7e --- /dev/null +++ b/src/main/java/com/youlai/boot/mini/model/entity/MiniAdoptionDiaryCommentLike.java @@ -0,0 +1,70 @@ +package com.youlai.boot.mini.model.entity; + +import com.baomidou.mybatisplus.annotation.*; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; +import com.fasterxml.jackson.annotation.JsonFormat; + +@Getter +@Setter +@ToString +@Accessors(chain = true) +@TableName("mini_adoption_diary_comment_like") +@Schema(description = "领养日记评论点赞表") +public class MiniAdoptionDiaryCommentLike implements Serializable { + + @TableId(value = "id", type = IdType.AUTO) + @Schema(description = "领养日记评论点赞表主键id") + private Long id; + + + @TableField("uuid") + @Schema(description = "uuid唯一标识,前后端用这个进行数据交互") + private String uuid; + + @TableField("diary_comment_id") + @Schema(description = "领养日记评论id") + private Long diaryCommentId; + + @TableField("mini_user_id") + @Schema(description = "点赞用户id") + private Long miniUserId; + + @TableField("create_time") + @Schema(description = "创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date createTime; + + @TableField("create_timestamp") + @Schema(description = "创建时间毫秒级时间戳") + private Long createTimestamp; + + @TableField("create_by") + @Schema(description = "创建人ID") + private Long createBy; + + @TableField("update_time") + @Schema(description = "更新时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date updateTime; + + @TableField("update_timestamp") + @Schema(description = "更新时间毫秒级时间戳") + private Long updateTimestamp; + + @TableField("update_by") + @Schema(description = "修改人ID") + private Long updateBy; + + @TableField("is_deleted") + @Schema(description = "逻辑删除标识(0-未删除 1-已删除)") + private Boolean deleted; + + +} diff --git a/src/main/java/com/youlai/boot/mini/model/form/AdoptionDiaryCommentForm.java b/src/main/java/com/youlai/boot/mini/model/form/AdoptionDiaryCommentForm.java new file mode 100644 index 0000000..590ff46 --- /dev/null +++ b/src/main/java/com/youlai/boot/mini/model/form/AdoptionDiaryCommentForm.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 AdoptionDiaryCommentForm { + + @NotBlank(message = "领养日记uuid不能为空") + @Schema(type = "string", description = "领养日记uuid", example = "0677d62d63ec693bf1bd6dab8a877dc1", required = true) + private String diaryUuid; + + @NotBlank(message = "评论内容不能为空") + @Length(max = 255, message = "评论不能超过255个字符") + @Schema(description = "评论内容", example = "写的真好") + private String content; + + @NotBlank(message = "父评论uuid不能为空") + @Schema(type = "string", description = "父评论ID,0为一级评论", example = "0", required = true) + private String parentUuId; + +} diff --git a/src/main/java/com/youlai/boot/mini/model/form/DeleteAdoptionDiaryCommentForm.java b/src/main/java/com/youlai/boot/mini/model/form/DeleteAdoptionDiaryCommentForm.java new file mode 100644 index 0000000..99cf0c2 --- /dev/null +++ b/src/main/java/com/youlai/boot/mini/model/form/DeleteAdoptionDiaryCommentForm.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 DeleteAdoptionDiaryCommentForm { + + @NotBlank(message = "领养日记UUID不能为空") + @Schema(description = "领养日记UUID", requiredMode = Schema.RequiredMode.REQUIRED, example = "0677d62d63ec693bf1bd6dab8a877dc1") + private String diaryUuid; + + @NotBlank(message = "评论UUID不能为空") + @Schema(description = "评论UUID", requiredMode = Schema.RequiredMode.REQUIRED, example = "0677d62d63ec693bf1bd6dab8a877dc2") + private String commentUuid; +} diff --git a/src/main/java/com/youlai/boot/mini/model/form/DiaryCommentLikeForm.java b/src/main/java/com/youlai/boot/mini/model/form/DiaryCommentLikeForm.java new file mode 100644 index 0000000..b75daf7 --- /dev/null +++ b/src/main/java/com/youlai/boot/mini/model/form/DiaryCommentLikeForm.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 DiaryCommentLikeForm { + + @NotBlank(message = "评论UUID不能为空") + @Schema(description = "评论UUID", requiredMode = Schema.RequiredMode.REQUIRED, example = "0677d62d63ec693bf1bd6dab8a877dc1") + private String commentUuid; +} diff --git a/src/main/java/com/youlai/boot/mini/model/query/DiaryFirstLevelCommentQueryParam.java b/src/main/java/com/youlai/boot/mini/model/query/DiaryFirstLevelCommentQueryParam.java new file mode 100644 index 0000000..7c8da7f --- /dev/null +++ b/src/main/java/com/youlai/boot/mini/model/query/DiaryFirstLevelCommentQueryParam.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 DiaryFirstLevelCommentQueryParam extends BaseQuery { + + @NotBlank(message = "领养日记UUID不能为空") + @Schema(description = "领养日记UUID", example = "0677d62d63ec693bf1bd6dab8a877dc1", requiredMode = Schema.RequiredMode.REQUIRED) + private String diaryUuid; +} diff --git a/src/main/java/com/youlai/boot/mini/model/query/DiarySecondLevelCommentQueryParam.java b/src/main/java/com/youlai/boot/mini/model/query/DiarySecondLevelCommentQueryParam.java new file mode 100644 index 0000000..6b6f341 --- /dev/null +++ b/src/main/java/com/youlai/boot/mini/model/query/DiarySecondLevelCommentQueryParam.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 DiarySecondLevelCommentQueryParam extends BaseQuery { + + @NotBlank(message = "领养日记UUID不能为空") + @Schema(description = "领养日记UUID", example = "0677d62d63ec693bf1bd6dab8a877dc1", requiredMode = Schema.RequiredMode.REQUIRED) + private String diaryUuid; + + @NotBlank(message = "根评论UUID不能为空") + @Schema(description = "根评论(一级评论)UUID", example = "0677d62d63ec693bf1bd6dab8a877dc2", requiredMode = Schema.RequiredMode.REQUIRED) + private String rootUuid; +} diff --git a/src/main/java/com/youlai/boot/mini/model/vo/AdoptionDiaryCommentVO.java b/src/main/java/com/youlai/boot/mini/model/vo/AdoptionDiaryCommentVO.java new file mode 100644 index 0000000..c7ae824 --- /dev/null +++ b/src/main/java/com/youlai/boot/mini/model/vo/AdoptionDiaryCommentVO.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 AdoptionDiaryCommentVO { + + @Schema(description = "领养日记uuid") + private String diaryUuId; + + @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; + +} \ No newline at end of file diff --git a/src/main/java/com/youlai/boot/mini/model/vo/AnimalNoteFirstLevelCommentVO.java b/src/main/java/com/youlai/boot/mini/model/vo/AnimalNoteFirstLevelCommentVO.java index 8926e43..4833456 100644 --- a/src/main/java/com/youlai/boot/mini/model/vo/AnimalNoteFirstLevelCommentVO.java +++ b/src/main/java/com/youlai/boot/mini/model/vo/AnimalNoteFirstLevelCommentVO.java @@ -14,7 +14,7 @@ public class AnimalNoteFirstLevelCommentVO { private String uuid; @JsonIgnore - @Schema(hidden = true) + @Schema(description = "评论自增id" ,hidden = true) private Long commentId; @Schema(description = "评论用户UUID") diff --git a/src/main/java/com/youlai/boot/mini/model/vo/DiaryFirstLevelCommentVO.java b/src/main/java/com/youlai/boot/mini/model/vo/DiaryFirstLevelCommentVO.java new file mode 100644 index 0000000..221150e --- /dev/null +++ b/src/main/java/com/youlai/boot/mini/model/vo/DiaryFirstLevelCommentVO.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 DiaryFirstLevelCommentVO { + + @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; +} diff --git a/src/main/java/com/youlai/boot/mini/model/vo/DiarySecondLevelCommentVO.java b/src/main/java/com/youlai/boot/mini/model/vo/DiarySecondLevelCommentVO.java new file mode 100644 index 0000000..8768abe --- /dev/null +++ b/src/main/java/com/youlai/boot/mini/model/vo/DiarySecondLevelCommentVO.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 DiarySecondLevelCommentVO { + + @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; +} diff --git a/src/main/java/com/youlai/boot/mini/service/AdoptionDiaryCommentService.java b/src/main/java/com/youlai/boot/mini/service/AdoptionDiaryCommentService.java new file mode 100644 index 0000000..9ae6008 --- /dev/null +++ b/src/main/java/com/youlai/boot/mini/service/AdoptionDiaryCommentService.java @@ -0,0 +1,37 @@ +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.MiniAdoptionDiaryComment; +import com.youlai.boot.mini.model.form.AdoptionDiaryCommentForm; +import com.youlai.boot.mini.model.form.DeleteAdoptionDiaryCommentForm; +import com.youlai.boot.mini.model.form.DiaryCommentLikeForm; +import com.youlai.boot.mini.model.query.DiaryFirstLevelCommentQueryParam; +import com.youlai.boot.mini.model.query.DiarySecondLevelCommentQueryParam; +import com.youlai.boot.mini.model.vo.AdoptionDiaryCommentVO; +import com.youlai.boot.mini.model.vo.DiaryFirstLevelCommentVO; +import com.youlai.boot.mini.model.vo.DiarySecondLevelCommentVO; + +import java.util.Map; + +public interface AdoptionDiaryCommentService extends IService{ + + AdoptionDiaryCommentVO addDiaryComment(AdoptionDiaryCommentForm formData); + + void deleteDiaryComment(DeleteAdoptionDiaryCommentForm formData, Long userId); + + /** + * 分页查询领养日记一级评论列表 + */ + IPage getFirstLevelCommentList(DiaryFirstLevelCommentQueryParam queryParam, Long userId); + + /** + * 分页查询领养日记二级评论列表 + */ + IPage getSecondLevelCommentList(DiarySecondLevelCommentQueryParam queryParam, Long userId); + + /** + * 评论点赞/取消点赞 + */ + Map toggleLike(DiaryCommentLikeForm form, Long userId); +} diff --git a/src/main/java/com/youlai/boot/mini/service/impl/AdoptionDiaryCommentServiceImpl.java b/src/main/java/com/youlai/boot/mini/service/impl/AdoptionDiaryCommentServiceImpl.java new file mode 100644 index 0000000..f02213e --- /dev/null +++ b/src/main/java/com/youlai/boot/mini/service/impl/AdoptionDiaryCommentServiceImpl.java @@ -0,0 +1,330 @@ +package com.youlai.boot.mini.service.impl; + +import cn.hutool.core.util.IdUtil; +import com.baomidou.mybatisplus.core.toolkit.IdWorker; +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.MiniAdoptionDiaryCommentMapper; +import com.youlai.boot.mini.mapper.MiniAdoptionDiaryCommentLikeMapper; +import com.youlai.boot.mini.mapper.MiniAdoptionDiaryMapper; +import com.youlai.boot.mini.model.entity.MiniAdoptionDiaryCommentLike; +import java.util.HashMap; +import com.youlai.boot.mini.model.entity.MiniAdoptionDiaryComment; +import com.youlai.boot.mini.model.form.AdoptionDiaryCommentForm; +import com.youlai.boot.mini.model.form.DeleteAdoptionDiaryCommentForm; +import com.youlai.boot.mini.model.form.DiaryCommentLikeForm; +import com.youlai.boot.mini.model.vo.AdoptionDiaryCommentVO; +import com.youlai.boot.mini.service.AdoptionDiaryCommentService; +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.transaction.annotation.Transactional; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.youlai.boot.mini.model.entity.MiniAdoptionDiaryCommentLike; +import com.youlai.boot.mini.model.query.DiaryFirstLevelCommentQueryParam; +import com.youlai.boot.mini.model.query.DiarySecondLevelCommentQueryParam; +import com.youlai.boot.mini.model.vo.DiaryFirstLevelCommentVO; +import com.youlai.boot.mini.model.vo.DiarySecondLevelCommentVO; + +@Slf4j +@Service +@RequiredArgsConstructor +public class AdoptionDiaryCommentServiceImpl extends ServiceImpl implements AdoptionDiaryCommentService { + + private final MiniAdoptionDiaryMapper miniAdoptionDiaryMapper; + private final UserMapper userMapper; + private final MiniAdoptionDiaryCommentLikeMapper miniAdoptionDiaryCommentLikeMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public AdoptionDiaryCommentVO addDiaryComment(AdoptionDiaryCommentForm formData) { + // 1. 登录校验 + Long userId = SecurityUtils.getUserId(); + if (userId == null) { + throw new MsgException("请先登录"); + } + // 2. 保存用户评论信息 + return saveCommentInfo(formData, userId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteDiaryComment(DeleteAdoptionDiaryCommentForm formData, Long userId) { + // 1. 执行原子删除(带权限校验、存在性校验) + int affectRows = baseMapper.deleteCommentWithPermissionCheck( + formData.getDiaryUuid(), + formData.getCommentUuid(), + userId + ); + + // 2. 删除成功,扣减评论数 + if (affectRows == 1) { + Long diaryId = miniAdoptionDiaryMapper.selectIdByUuid(formData.getDiaryUuid()); + baseMapper.decrementCommentCount(diaryId); + return; + } + + // 3. 删除失败,查询具体错误原因(仅失败场景执行) + Long diaryId = miniAdoptionDiaryMapper.selectIdByUuid(formData.getDiaryUuid()); + if (diaryId == null) { + throw new MsgException("领养日记不存在或已删除"); + } + + // 查询评论是否存在且属于该日记 + MiniAdoptionDiaryComment comment = baseMapper.selectParentCommentInfoByUuid(formData.getCommentUuid()); + if (comment == null || !comment.getDiaryId().equals(diaryId)) { + throw new MsgException("评论不存在或已删除"); + } + + // 剩余情况:权限不足 + throw new MsgException("无权限删除该评论"); + } + + @Override + @Transactional(readOnly = true, rollbackFor = Exception.class) + public IPage getFirstLevelCommentList(DiaryFirstLevelCommentQueryParam queryParam, Long userId) { + // 1. 构造分页参数,默认值兼容 + Page page = new Page<>(queryParam.getPageNum(), queryParam.getPageSize()); + + // 2. 查询一级评论分页 + IPage result = baseMapper.getFirstLevelComment(page, queryParam); + List commentList = result.getRecords(); + if (commentList.isEmpty() || userId == null) { + return result; + } + + // 3. 提取一级评论内部主键ID(用于查询点赞状态) + List firstLevelCommentIds = commentList.stream() + .map(DiaryFirstLevelCommentVO::getCommentId) + .collect(Collectors.toList()); + + // 4. 批量查询登录用户的点赞状态 + Map likeStatusMap = Collections.emptyMap(); + try { + List likeList = baseMapper.batchGetUserCommentLikes(firstLevelCommentIds, userId); + likeStatusMap = likeList.stream() + .collect(Collectors.toMap( + MiniAdoptionDiaryCommentLike::getDiaryCommentId, + like -> Boolean.TRUE, + (v1, v2) -> v1 + )); + } catch (Exception e) { + log.error("批量查询评论点赞状态失败", e); + } + + // 5. 设置点赞状态 + for (DiaryFirstLevelCommentVO comment : commentList) { + comment.setIsLiked(likeStatusMap.getOrDefault(comment.getCommentId(), Boolean.FALSE)); + } + + return result; + } + + @Override + @Transactional(readOnly = true, rollbackFor = Exception.class) + public IPage getSecondLevelCommentList(DiarySecondLevelCommentQueryParam queryParam, Long userId) { + // 1. 构造分页参数,默认值兼容 + Page page = new Page<>(queryParam.getPageNum(), queryParam.getPageSize()); + + // 2. 查询二级评论分页 + IPage result = baseMapper.getSecondLevelComment(page, queryParam); + List commentList = result.getRecords(); + if (commentList.isEmpty() || userId == null) { + return result; + } + + // 3. 提取二级评论内部主键ID(用于查询点赞状态) + List secondLevelCommentIds = commentList.stream() + .map(DiarySecondLevelCommentVO::getCommentId) + .collect(Collectors.toList()); + + // 4. 批量查询登录用户的点赞状态 + Map likeStatusMap = Collections.emptyMap(); + try { + List likeList = baseMapper.batchGetUserCommentLikes(secondLevelCommentIds, userId); + likeStatusMap = likeList.stream() + .collect(Collectors.toMap( + MiniAdoptionDiaryCommentLike::getDiaryCommentId, + like -> Boolean.TRUE, + (v1, v2) -> v1 + )); + } catch (Exception e) { + log.error("批量查询二级评论点赞状态失败", e); + } + + // 5. 设置点赞状态和已注销用户昵称 + for (DiarySecondLevelCommentVO 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 toggleLike(DiaryCommentLikeForm form, Long userId) { + // 1. 校验评论是否存在 + Long commentId = baseMapper.selectIdByUuid(form.getCommentUuid()); + if (commentId == null) { + throw new MsgException("评论不存在或已删除"); + } + + // 2. 查询用户是否已经点赞 + Integer likeCount = miniAdoptionDiaryCommentLikeMapper.selectUserLikeCount(commentId, userId); + boolean isLiked = likeCount != null && likeCount > 0; + boolean targetLike = !isLiked; + long currentTime = System.currentTimeMillis(); + + if (targetLike) { + // 3. 点赞:新增或更新点赞记录 + MiniAdoptionDiaryCommentLike like = new MiniAdoptionDiaryCommentLike(); + like.setUuid(IdWorker.get32UUID()); + like.setDiaryCommentId(commentId); + like.setMiniUserId(userId); + like.setCreateTimestamp(currentTime); + like.setCreateTime(new Date(currentTime)); + like.setCreateBy(userId); + miniAdoptionDiaryCommentLikeMapper.insertOrUpdateLike(like); + + // 4. 原子增加点赞数 + baseMapper.incrementLikeCount(commentId); + } else { + // 5. 取消点赞:逻辑删除点赞记录 + miniAdoptionDiaryCommentLikeMapper.deleteLike(commentId, userId, currentTime); + + // 6. 原子减少点赞数 + baseMapper.decrementLikeCount(commentId); + } + + // 7. 查询最新点赞数 + Long latestLikeCount = baseMapper.selectLikeCount(commentId); + Map result = new HashMap<>(); + result.put("isLiked", targetLike); + result.put("likeCount", latestLikeCount != null ? latestLikeCount : 0); + return result; + } + + private AdoptionDiaryCommentVO saveCommentInfo(AdoptionDiaryCommentForm addCommentForm, Long appUserId) { + // 1. 根据日记UUID查询ID + Long diaryId = miniAdoptionDiaryMapper.selectIdByUuid(addCommentForm.getDiaryUuid()); + if (diaryId == null) { + throw new MsgException("领养日记不存在或已删除"); + } + + // 2. 获取父评论信息 + Long parentCommentId = 0L; + MiniAdoptionDiaryComment parentComment = null; + SysUser replyToUser = null; + if (!"0".equals(addCommentForm.getParentUuId())) { + // 查询父评论 + parentComment = baseMapper.selectParentCommentInfoByUuid(addCommentForm.getParentUuId()); + if (parentComment == null) { + throw new MsgException("父评论已消失"); + } + // 父评论归属校验 + if (!parentComment.getDiaryId().equals(diaryId)) { + 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(); + MiniAdoptionDiaryComment diaryComment = new MiniAdoptionDiaryComment(); + diaryComment.setUuid(IdUtil.fastSimpleUUID()); + diaryComment.setDiaryId(diaryId); + diaryComment.setMiniUserId(appUserId); + diaryComment.setContent(addCommentForm.getContent()); + diaryComment.setParentId(parentCommentId); + // rootId默认0,后续更新 + diaryComment.setRootId(0L); + if (replyToUser != null) { + diaryComment.setReplyToUserId(replyToUser.getId()); + } + diaryComment.setLikeCount(0); + diaryComment.setProvince(provinceShortName); + diaryComment.setCreateTimestamp(currentTimestamp); + diaryComment.setCreateTime(new Date(currentTimestamp)); + diaryComment.setCreateBy(appUserId); + baseMapper.insert(diaryComment); + + // 5. 一级评论的rootId是自己,二级评论是父评论的rootId + Long rootId = parentCommentId == 0 ? diaryComment.getId() : parentComment.getRootId(); + diaryComment.setRootId(rootId); + baseMapper.updateById(diaryComment); + + // 6. 构建返回VO + AdoptionDiaryCommentVO diaryCommentVO = new AdoptionDiaryCommentVO(); + SysUser appUser = userMapper.selectById(appUserId); + diaryCommentVO.setNickname(appUser.getNickname()); + diaryCommentVO.setAvatarUrl(appUser.getAvatar()); + + diaryCommentVO.setDiaryUuId(addCommentForm.getDiaryUuid()); + diaryCommentVO.setUuid(diaryComment.getUuid()); + diaryCommentVO.setAppUserId(appUser.getUuid()); + diaryCommentVO.setContent(diaryComment.getContent()); + diaryCommentVO.setParentUuId(addCommentForm.getParentUuId()); + + // 根据rootId查询UUID + String rootUuid = baseMapper.selectUuidById(diaryComment.getRootId()); + if (rootUuid == null) { + throw new MsgException("评论所属楼层已删除"); + } + diaryCommentVO.setRootId(rootUuid); + + // 设置回复用户信息 + if (diaryComment.getReplyToUserId() != null && replyToUser != null) { + if (replyToUser.getIsDeleted() == 1) { + diaryCommentVO.setReplyToNickname("用户已注销"); + } else { + diaryCommentVO.setReplyToUserId(replyToUser.getUuid()); + diaryCommentVO.setReplyToNickname(replyToUser.getNickname()); + diaryCommentVO.setReplyToAvatarUrl(replyToUser.getAvatar()); + } + } + + diaryCommentVO.setCreatedAt(diaryComment.getCreateTimestamp()); + diaryCommentVO.setIsLiked(false); + diaryCommentVO.setLikeCount(0); + diaryCommentVO.setProvince(diaryComment.getProvince()); + + // 7. 更新日记评论数 + baseMapper.incrementCommentCount(diaryId); + + return diaryCommentVO; + } + +} diff --git a/src/main/resources/mapper/mini/MiniAdoptionDiaryCommentLikeMapper.xml b/src/main/resources/mapper/mini/MiniAdoptionDiaryCommentLikeMapper.xml new file mode 100644 index 0000000..e6d25eb --- /dev/null +++ b/src/main/resources/mapper/mini/MiniAdoptionDiaryCommentLikeMapper.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + INSERT INTO mini_adoption_diary_comment_like + (uuid, note_comment_id, mini_user_id, create_timestamp, create_time, create_by, is_deleted) + VALUES + (#{uuid}, #{noteCommentId}, #{miniUserId}, #{createTimestamp}, #{createTime}, #{createBy}, 0) + ON DUPLICATE KEY UPDATE + is_deleted = 0, + update_timestamp = #{createTimestamp}, + update_time = #{createTime}, + update_by = #{createBy} + + + + + UPDATE mini_adoption_diary_comment_like + SET is_deleted = 1, + update_time = NOW(), + update_timestamp = #{currentTime}, + update_by = #{userId} + WHERE note_comment_id = #{commentId} + AND mini_user_id = #{userId} + AND is_deleted = 0 + + + diff --git a/src/main/resources/mapper/mini/MiniAdoptionDiaryCommentMapper.xml b/src/main/resources/mapper/mini/MiniAdoptionDiaryCommentMapper.xml new file mode 100644 index 0000000..4777aed --- /dev/null +++ b/src/main/resources/mapper/mini/MiniAdoptionDiaryCommentMapper.xml @@ -0,0 +1,153 @@ + + + + + + + UPDATE mini_adoption_diary + SET comment_count = comment_count + 1 + WHERE id = #{diaryId} + + + + + + + + + + + + + + UPDATE mini_adoption_diary + SET comment_count = comment_count - 1 + WHERE id = #{diaryId} + AND comment_count > 0 + + + + + UPDATE mini_adoption_diary_comment c + INNER JOIN mini_adoption_diary d ON c.diary_id = d.id + SET c.is_deleted = 1, + c.update_by = #{userId}, + c.update_time = NOW(), + c.update_timestamp = UNIX_TIMESTAMP(NOW(3)) * 1000 + WHERE d.uuid = #{diaryUuid} + AND c.uuid = #{commentUuid} + AND c.is_deleted = 0 + AND d.is_deleted = 0 + AND (c.mini_user_id = #{userId} OR d.mini_user_id = #{userId}) + + + + + + + + + + + + + + UPDATE mini_adoption_diary_comment + SET like_count = like_count + 1 + WHERE id = #{commentId} + AND is_deleted = 0 + + + + + UPDATE mini_adoption_diary_comment + SET like_count = like_count - 1 + WHERE id = #{commentId} + AND is_deleted = 0 + AND like_count > 0 + + + + + +