diff --git a/src/main/java/com/yolo/keyborad/common/ErrorCode.java b/src/main/java/com/yolo/keyborad/common/ErrorCode.java index fb57031..153d547 100644 --- a/src/main/java/com/yolo/keyborad/common/ErrorCode.java +++ b/src/main/java/com/yolo/keyborad/common/ErrorCode.java @@ -30,6 +30,8 @@ public enum ErrorCode { CHAT_SAVE_DATA_EMPTY(40010, "保存数据不能为空"), COMPANION_MESSAGE_EMPTY(40011, "消息内容不能为空"), COMPANION_ID_EMPTY(40012, "AI陪聊角色ID不能为空"), + COMMENT_CONTENT_EMPTY(40013, "评论内容不能为空"), + COMMENT_NOT_FOUND(40014, "评论不存在"), TOKEN_NOT_FOUND(40102, "未能读取到有效用户令牌"), TOKEN_INVALID(40103, "令牌无效"), TOKEN_TIMEOUT(40104, "令牌已过期"), diff --git a/src/main/java/com/yolo/keyborad/controller/AiCompanionCommentController.java b/src/main/java/com/yolo/keyborad/controller/AiCompanionCommentController.java new file mode 100644 index 0000000..2e75ed0 --- /dev/null +++ b/src/main/java/com/yolo/keyborad/controller/AiCompanionCommentController.java @@ -0,0 +1,60 @@ +package com.yolo.keyborad.controller; + +import cn.dev33.satoken.stp.StpUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.yolo.keyborad.common.BaseResponse; +import com.yolo.keyborad.common.ErrorCode; +import com.yolo.keyborad.common.ResultUtils; +import com.yolo.keyborad.exception.BusinessException; +import com.yolo.keyborad.model.dto.comment.CommentAddReq; +import com.yolo.keyborad.model.dto.comment.CommentPageReq; +import com.yolo.keyborad.model.vo.CommentVO; +import com.yolo.keyborad.service.KeyboardAiCompanionCommentService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +/* + * @author: ziin + * @date: 2026/1/26 + */ +@RestController +@Slf4j +@RequestMapping("/ai-companion/comment") +@Tag(name = "AI陪聊角色评论", description = "AI陪聊角色评论管理接口") +public class AiCompanionCommentController { + + @Resource + private KeyboardAiCompanionCommentService commentService; + + @PostMapping("/add") + @Operation(summary = "发表评论", description = "用户对AI陪聊角色发表评论") + public BaseResponse addComment(@RequestBody CommentAddReq req) { + if (req.getCompanionId() == null) { + throw new BusinessException(ErrorCode.COMPANION_ID_EMPTY); + } + if (StrUtil.isBlank(req.getContent())) { + throw new BusinessException(ErrorCode.COMMENT_CONTENT_EMPTY); + } + + Long userId = StpUtil.getLoginIdAsLong(); + Long commentId = commentService.addComment(userId, req.getCompanionId(), req.getContent(), + req.getParentId(), req.getRootId()); + return ResultUtils.success(commentId); + } + + @PostMapping("/page") + @Operation(summary = "分页查询评论", description = "分页查询AI陪聊角色的评论列表") + public BaseResponse> pageComments(@RequestBody CommentPageReq req) { + if (req.getCompanionId() == null) { + throw new BusinessException(ErrorCode.COMPANION_ID_EMPTY); + } + + IPage result = commentService.pageComments(req.getCompanionId(), + req.getPageNum(), req.getPageSize()); + return ResultUtils.success(result); + } +} diff --git a/src/main/java/com/yolo/keyborad/mapper/KeyboardAiCompanionCommentMapper.java b/src/main/java/com/yolo/keyborad/mapper/KeyboardAiCompanionCommentMapper.java new file mode 100644 index 0000000..88357fc --- /dev/null +++ b/src/main/java/com/yolo/keyborad/mapper/KeyboardAiCompanionCommentMapper.java @@ -0,0 +1,12 @@ +package com.yolo.keyborad.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.yolo.keyborad.model.entity.KeyboardAiCompanionComment; + +/* +* @author: ziin +* @date: 2026/1/26 20:31 +*/ + +public interface KeyboardAiCompanionCommentMapper extends BaseMapper { +} \ No newline at end of file diff --git a/src/main/java/com/yolo/keyborad/model/dto/comment/CommentAddReq.java b/src/main/java/com/yolo/keyborad/model/dto/comment/CommentAddReq.java new file mode 100644 index 0000000..ef68422 --- /dev/null +++ b/src/main/java/com/yolo/keyborad/model/dto/comment/CommentAddReq.java @@ -0,0 +1,25 @@ +package com.yolo.keyborad.model.dto.comment; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/* + * @author: ziin + * @date: 2026/1/26 + */ +@Data +@Schema(description = "发表评论请求") +public class CommentAddReq { + + @Schema(description = "被评论的AI陪伴角色ID", requiredMode = Schema.RequiredMode.REQUIRED) + private Long companionId; + + @Schema(description = "评论内容", requiredMode = Schema.RequiredMode.REQUIRED) + private String content; + + @Schema(description = "父评论ID,NULL表示一级评论") + private Long parentId; + + @Schema(description = "根评论ID,用于标识同一评论线程") + private Long rootId; +} diff --git a/src/main/java/com/yolo/keyborad/model/dto/comment/CommentPageReq.java b/src/main/java/com/yolo/keyborad/model/dto/comment/CommentPageReq.java new file mode 100644 index 0000000..53cb2f3 --- /dev/null +++ b/src/main/java/com/yolo/keyborad/model/dto/comment/CommentPageReq.java @@ -0,0 +1,22 @@ +package com.yolo.keyborad.model.dto.comment; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/* + * @author: ziin + * @date: 2026/1/26 + */ +@Data +@Schema(description = "评论分页查询请求") +public class CommentPageReq { + + @Schema(description = "AI陪伴角色ID", requiredMode = Schema.RequiredMode.REQUIRED) + private Long companionId; + + @Schema(description = "页码", example = "1") + private Integer pageNum = 1; + + @Schema(description = "每页数量", example = "20") + private Integer pageSize = 20; +} diff --git a/src/main/java/com/yolo/keyborad/model/entity/KeyboardAiCompanionComment.java b/src/main/java/com/yolo/keyborad/model/entity/KeyboardAiCompanionComment.java new file mode 100644 index 0000000..db2092e --- /dev/null +++ b/src/main/java/com/yolo/keyborad/model/entity/KeyboardAiCompanionComment.java @@ -0,0 +1,99 @@ +package com.yolo.keyborad.model.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.Date; +import lombok.Data; + +/* +* @author: ziin +* @date: 2026/1/26 20:31 +*/ + +/** + * 用户对AI陪伴角色的评论表,支持多级评论结构(一级评论与回复) + */ +@Schema(description="用户对AI陪伴角色的评论表,支持多级评论结构(一级评论与回复)") +@Data +@TableName(value = "keyboard_ai_companion_comment") +public class KeyboardAiCompanionComment { + /** + * 评论唯一ID + */ + @TableId(value = "id", type = IdType.AUTO) + @Schema(description="评论唯一ID") + private Long id; + + /** + * 被评论的AI陪伴角色ID + */ + @TableField(value = "companion_id") + @Schema(description="被评论的AI陪伴角色ID") + private Long companionId; + + /** + * 发表评论的用户ID + */ + @TableField(value = "user_id") + @Schema(description="发表评论的用户ID") + private Long userId; + + /** + * 父评论ID,NULL表示一级评论 + */ + @TableField(value = "parent_id") + @Schema(description="父评论ID,NULL表示一级评论") + private Long parentId; + + /** + * 根评论ID,用于标识同一评论线程 + */ + @TableField(value = "root_id") + @Schema(description="根评论ID,用于标识同一评论线程") + private Long rootId; + + /** + * 评论内容 + */ + @TableField(value = "content") + @Schema(description="评论内容") + private String content; + + /** + * 点赞数量 + */ + @TableField(value = "\"like\"") + @Schema(description="点赞数量") + private Long like; + + /** + * 评论状态:1=正常,0=隐藏,-1=删除 + */ + @TableField(value = "\"status\"") + @Schema(description="评论状态:1=正常,0=隐藏,-1=删除") + private Short status; + + /** + * 评论点赞数 + */ + @TableField(value = "like_count") + @Schema(description="评论点赞数") + private Integer likeCount; + + /** + * 评论创建时间 + */ + @TableField(value = "created_at") + @Schema(description="评论创建时间") + private Date createdAt; + + /** + * 评论更新时间 + */ + @TableField(value = "updated_at") + @Schema(description="评论更新时间") + private Date updatedAt; +} \ No newline at end of file diff --git a/src/main/java/com/yolo/keyborad/model/vo/CommentVO.java b/src/main/java/com/yolo/keyborad/model/vo/CommentVO.java new file mode 100644 index 0000000..ddcc961 --- /dev/null +++ b/src/main/java/com/yolo/keyborad/model/vo/CommentVO.java @@ -0,0 +1,45 @@ +package com.yolo.keyborad.model.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.Date; + +/* + * @author: ziin + * @date: 2026/1/26 + */ +@Data +@Schema(description = "评论VO") +public class CommentVO { + + @Schema(description = "评论ID") + private Long id; + + @Schema(description = "被评论的AI陪伴角色ID") + private Long companionId; + + @Schema(description = "发表评论的用户ID") + private Long userId; + + @Schema(description = "用户昵称") + private String userName; + + @Schema(description = "用户头像") + private String userAvatar; + + @Schema(description = "父评论ID") + private Long parentId; + + @Schema(description = "根评论ID") + private Long rootId; + + @Schema(description = "评论内容") + private String content; + + @Schema(description = "点赞数") + private Integer likeCount; + + @Schema(description = "评论创建时间") + private Date createdAt; +} diff --git a/src/main/java/com/yolo/keyborad/service/KeyboardAiCompanionCommentService.java b/src/main/java/com/yolo/keyborad/service/KeyboardAiCompanionCommentService.java new file mode 100644 index 0000000..2b3fab1 --- /dev/null +++ b/src/main/java/com/yolo/keyborad/service/KeyboardAiCompanionCommentService.java @@ -0,0 +1,35 @@ +package com.yolo.keyborad.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.yolo.keyborad.model.entity.KeyboardAiCompanionComment; +import com.baomidou.mybatisplus.extension.service.IService; +import com.yolo.keyborad.model.vo.CommentVO; + +/* + * @author: ziin + * @date: 2026/1/26 20:31 + */ +public interface KeyboardAiCompanionCommentService extends IService { + + /** + * 发表评论 + * + * @param userId 用户ID + * @param companionId AI陪伴角色ID + * @param content 评论内容 + * @param parentId 父评论ID(可为null) + * @param rootId 根评论ID(可为null) + * @return 评论ID + */ + Long addComment(Long userId, Long companionId, String content, Long parentId, Long rootId); + + /** + * 分页查询评论 + * + * @param companionId AI陪伴角色ID + * @param pageNum 页码 + * @param pageSize 每页数量 + * @return 分页结果 + */ + IPage pageComments(Long companionId, Integer pageNum, Integer pageSize); +} diff --git a/src/main/java/com/yolo/keyborad/service/impl/KeyboardAiCompanionCommentServiceImpl.java b/src/main/java/com/yolo/keyborad/service/impl/KeyboardAiCompanionCommentServiceImpl.java new file mode 100644 index 0000000..073ce22 --- /dev/null +++ b/src/main/java/com/yolo/keyborad/service/impl/KeyboardAiCompanionCommentServiceImpl.java @@ -0,0 +1,91 @@ +package com.yolo.keyborad.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.yolo.keyborad.mapper.KeyboardAiCompanionCommentMapper; +import com.yolo.keyborad.model.entity.KeyboardAiCompanionComment; +import com.yolo.keyborad.model.entity.KeyboardUser; +import com.yolo.keyborad.model.vo.CommentVO; +import com.yolo.keyborad.service.KeyboardAiCompanionCommentService; +import com.yolo.keyborad.service.UserService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/* + * @author: ziin + * @date: 2026/1/26 20:31 + */ +@Service +public class KeyboardAiCompanionCommentServiceImpl extends ServiceImpl implements KeyboardAiCompanionCommentService { + + @Resource + private UserService userService; + + @Override + public Long addComment(Long userId, Long companionId, String content, Long parentId, Long rootId) { + KeyboardAiCompanionComment comment = new KeyboardAiCompanionComment(); + comment.setUserId(userId); + comment.setCompanionId(companionId); + comment.setContent(content); + comment.setParentId(parentId); + comment.setRootId(rootId); + comment.setStatus((short) 1); + comment.setLikeCount(0); + comment.setCreatedAt(new Date()); + this.save(comment); + return comment.getId(); + } + + @Override + public IPage pageComments(Long companionId, Integer pageNum, Integer pageSize) { + Page page = new Page<>(pageNum, pageSize); + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(KeyboardAiCompanionComment::getCompanionId, companionId) + .eq(KeyboardAiCompanionComment::getStatus, 1) + .isNull(KeyboardAiCompanionComment::getParentId) + .orderByDesc(KeyboardAiCompanionComment::getCreatedAt); + IPage entityPage = this.page(page, queryWrapper); + + // 获取所有用户ID + List userIds = entityPage.getRecords().stream() + .map(KeyboardAiCompanionComment::getUserId) + .distinct() + .collect(Collectors.toList()); + + // 批量查询用户信息 + Map userMap = Map.of(); + if (!userIds.isEmpty()) { + List users = userService.listByIds(userIds); + userMap = users.stream().collect(Collectors.toMap(KeyboardUser::getId, u -> u)); + } + + // 转换为VO + Map finalUserMap = userMap; + return entityPage.convert(entity -> { + CommentVO vo = new CommentVO(); + vo.setId(entity.getId()); + vo.setCompanionId(entity.getCompanionId()); + vo.setUserId(entity.getUserId()); + vo.setParentId(entity.getParentId()); + vo.setRootId(entity.getRootId()); + vo.setContent(entity.getContent()); + vo.setLikeCount(entity.getLikeCount()); + vo.setCreatedAt(entity.getCreatedAt()); + + // 填充用户信息 + KeyboardUser user = finalUserMap.get(entity.getUserId()); + if (user != null) { + vo.setUserName(user.getNickName()); + vo.setUserAvatar(user.getAvatarUrl()); + } + return vo; + }); + } +} diff --git a/src/main/resources/mapper/KeyboardAiCompanionCommentMapper.xml b/src/main/resources/mapper/KeyboardAiCompanionCommentMapper.xml new file mode 100644 index 0000000..f523613 --- /dev/null +++ b/src/main/resources/mapper/KeyboardAiCompanionCommentMapper.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + id, companion_id, user_id, parent_id, root_id, content, "like", "status", like_count, + created_at, updated_at + + \ No newline at end of file