feat(comment): 新增AI伴侣评论功能并补充相关错误码

This commit is contained in:
2026-01-26 20:39:34 +08:00
parent ac9a352004
commit 5a58c4ff38
10 changed files with 415 additions and 0 deletions

View File

@@ -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, "令牌已过期"),

View File

@@ -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<Long> 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<IPage<CommentVO>> pageComments(@RequestBody CommentPageReq req) {
if (req.getCompanionId() == null) {
throw new BusinessException(ErrorCode.COMPANION_ID_EMPTY);
}
IPage<CommentVO> result = commentService.pageComments(req.getCompanionId(),
req.getPageNum(), req.getPageSize());
return ResultUtils.success(result);
}
}

View File

@@ -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<KeyboardAiCompanionComment> {
}

View File

@@ -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 = "父评论IDNULL表示一级评论")
private Long parentId;
@Schema(description = "根评论ID用于标识同一评论线程")
private Long rootId;
}

View File

@@ -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;
}

View File

@@ -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;
/**
* 父评论IDNULL表示一级评论
*/
@TableField(value = "parent_id")
@Schema(description="父评论IDNULL表示一级评论")
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;
}

View File

@@ -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;
}

View File

@@ -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<KeyboardAiCompanionComment> {
/**
* 发表评论
*
* @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<CommentVO> pageComments(Long companionId, Integer pageNum, Integer pageSize);
}

View File

@@ -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<KeyboardAiCompanionCommentMapper, KeyboardAiCompanionComment> 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<CommentVO> pageComments(Long companionId, Integer pageNum, Integer pageSize) {
Page<KeyboardAiCompanionComment> page = new Page<>(pageNum, pageSize);
LambdaQueryWrapper<KeyboardAiCompanionComment> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(KeyboardAiCompanionComment::getCompanionId, companionId)
.eq(KeyboardAiCompanionComment::getStatus, 1)
.isNull(KeyboardAiCompanionComment::getParentId)
.orderByDesc(KeyboardAiCompanionComment::getCreatedAt);
IPage<KeyboardAiCompanionComment> entityPage = this.page(page, queryWrapper);
// 获取所有用户ID
List<Long> userIds = entityPage.getRecords().stream()
.map(KeyboardAiCompanionComment::getUserId)
.distinct()
.collect(Collectors.toList());
// 批量查询用户信息
Map<Long, KeyboardUser> userMap = Map.of();
if (!userIds.isEmpty()) {
List<KeyboardUser> users = userService.listByIds(userIds);
userMap = users.stream().collect(Collectors.toMap(KeyboardUser::getId, u -> u));
}
// 转换为VO
Map<Long, KeyboardUser> 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;
});
}
}

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yolo.keyborad.mapper.KeyboardAiCompanionCommentMapper">
<resultMap id="BaseResultMap" type="com.yolo.keyborad.model.entity.KeyboardAiCompanionComment">
<!--@mbg.generated-->
<!--@Table keyboard_ai_companion_comment-->
<id column="id" jdbcType="BIGINT" property="id" />
<result column="companion_id" jdbcType="BIGINT" property="companionId" />
<result column="user_id" jdbcType="BIGINT" property="userId" />
<result column="parent_id" jdbcType="BIGINT" property="parentId" />
<result column="root_id" jdbcType="BIGINT" property="rootId" />
<result column="content" jdbcType="VARCHAR" property="content" />
<result column="like" jdbcType="BIGINT" property="like" />
<result column="status" jdbcType="SMALLINT" property="status" />
<result column="like_count" jdbcType="INTEGER" property="likeCount" />
<result column="created_at" jdbcType="TIMESTAMP" property="createdAt" />
<result column="updated_at" jdbcType="TIMESTAMP" property="updatedAt" />
</resultMap>
<sql id="Base_Column_List">
<!--@mbg.generated-->
id, companion_id, user_id, parent_id, root_id, content, "like", "status", like_count,
created_at, updated_at
</sql>
</mapper>