feat(ai-companion): 新增AI角色点赞功能

新增点赞/取消点赞接口,包含实体、Mapper、Service及DTO,支持用户点赞状态切换与异常处理。
This commit is contained in:
2026-01-27 18:33:26 +08:00
parent 6cf0275980
commit b6d124619e
9 changed files with 277 additions and 2 deletions

View File

@@ -2,6 +2,6 @@
"active": true, "active": true,
"started_at": "2026-01-26T13:01:18.447Z", "started_at": "2026-01-26T13:01:18.447Z",
"original_prompt": "刚刚回滚了代码现在AI陪聊角色评论需要使用KeyboardAiCompanionCommentLikeService添加一个评论点赞接口用来记录点赞和取消点赞。 ulw", "original_prompt": "刚刚回滚了代码现在AI陪聊角色评论需要使用KeyboardAiCompanionCommentLikeService添加一个评论点赞接口用来记录点赞和取消点赞。 ulw",
"reinforcement_count": 5, "reinforcement_count": 7,
"last_checked_at": "2026-01-27T05:14:53.054Z" "last_checked_at": "2026-01-27T10:31:33.079Z"
} }

View File

@@ -30,6 +30,7 @@ public enum ErrorCode {
CHAT_SAVE_DATA_EMPTY(40010, "保存数据不能为空"), CHAT_SAVE_DATA_EMPTY(40010, "保存数据不能为空"),
COMPANION_MESSAGE_EMPTY(40011, "消息内容不能为空"), COMPANION_MESSAGE_EMPTY(40011, "消息内容不能为空"),
COMPANION_ID_EMPTY(40012, "AI陪聊角色ID不能为空"), COMPANION_ID_EMPTY(40012, "AI陪聊角色ID不能为空"),
COMPANION_NOT_FOUND(40019, "AI陪聊角色不存在"),
COMMENT_CONTENT_EMPTY(40013, "评论内容不能为空"), COMMENT_CONTENT_EMPTY(40013, "评论内容不能为空"),
COMMENT_NOT_FOUND(40014, "评论不存在"), COMMENT_NOT_FOUND(40014, "评论不存在"),
COMMENT_ID_EMPTY(40015, "评论ID不能为空"), COMMENT_ID_EMPTY(40015, "评论ID不能为空"),

View File

@@ -1,10 +1,15 @@
package com.yolo.keyborad.controller; package com.yolo.keyborad.controller;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yolo.keyborad.common.BaseResponse; import com.yolo.keyborad.common.BaseResponse;
import com.yolo.keyborad.common.ErrorCode;
import com.yolo.keyborad.common.ResultUtils; import com.yolo.keyborad.common.ResultUtils;
import com.yolo.keyborad.exception.BusinessException;
import com.yolo.keyborad.model.dto.PageDTO; import com.yolo.keyborad.model.dto.PageDTO;
import com.yolo.keyborad.model.dto.companion.CompanionLikeReq;
import com.yolo.keyborad.model.vo.AiCompanionVO; import com.yolo.keyborad.model.vo.AiCompanionVO;
import com.yolo.keyborad.service.KeyboardAiCompanionLikeService;
import com.yolo.keyborad.service.KeyboardAiCompanionService; import com.yolo.keyborad.service.KeyboardAiCompanionService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
@@ -25,10 +30,25 @@ public class AiCompanionController {
@Resource @Resource
private KeyboardAiCompanionService aiCompanionService; private KeyboardAiCompanionService aiCompanionService;
@Resource
private KeyboardAiCompanionLikeService aiCompanionLikeService;
@PostMapping("/page") @PostMapping("/page")
@Operation(summary = "分页查询AI陪聊角色", description = "分页查询已上线的AI陪聊角色列表") @Operation(summary = "分页查询AI陪聊角色", description = "分页查询已上线的AI陪聊角色列表")
public BaseResponse<IPage<AiCompanionVO>> pageList(@RequestBody PageDTO pageDTO) { public BaseResponse<IPage<AiCompanionVO>> pageList(@RequestBody PageDTO pageDTO) {
IPage<AiCompanionVO> result = aiCompanionService.pageList(pageDTO.getPageNum(), pageDTO.getPageSize()); IPage<AiCompanionVO> result = aiCompanionService.pageList(pageDTO.getPageNum(), pageDTO.getPageSize());
return ResultUtils.success(result); return ResultUtils.success(result);
} }
@PostMapping("/like")
@Operation(summary = "点赞/取消点赞AI角色", description = "对AI角色进行点赞或取消点赞操作返回true表示点赞成功false表示取消点赞成功")
public BaseResponse<Boolean> toggleLike(@RequestBody CompanionLikeReq req) {
if (req.getCompanionId() == null) {
throw new BusinessException(ErrorCode.COMPANION_ID_EMPTY);
}
Long userId = StpUtil.getLoginIdAsLong();
boolean result = aiCompanionLikeService.toggleLike(userId, req.getCompanionId());
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.KeyboardAiCompanionLike;
/*
* @author: ziin
* @date: 2026/1/27 18:18
*/
public interface KeyboardAiCompanionLikeMapper extends BaseMapper<KeyboardAiCompanionLike> {
}

View File

@@ -0,0 +1,16 @@
package com.yolo.keyborad.model.dto.companion;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/*
* @author: ziin
* @date: 2026/1/27
*/
@Data
@Schema(description = "AI角色点赞请求")
public class CompanionLikeReq {
@Schema(description = "AI角色ID", requiredMode = Schema.RequiredMode.REQUIRED)
private Long companionId;
}

View File

@@ -0,0 +1,64 @@
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/27 18:18
*/
/**
* 用户对AI陪伴角色的点赞行为记录表用于记录点赞与取消点赞
*/
@Schema(description="用户对AI陪伴角色的点赞行为记录表用于记录点赞与取消点赞")
@Data
@TableName(value = "keyboard_ai_companion_like")
public class KeyboardAiCompanionLike {
/**
* AI角色点赞记录唯一ID
*/
@TableId(value = "id", type = IdType.AUTO)
@Schema(description="AI角色点赞记录唯一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;
/**
* 点赞状态1=已点赞0=已取消
*/
@TableField(value = "\"status\"")
@Schema(description="点赞状态1=已点赞0=已取消")
private Short status;
/**
* 首次点赞时间
*/
@TableField(value = "created_at")
@Schema(description="首次点赞时间")
private Date createdAt;
/**
* 点赞状态最近更新时间
*/
@TableField(value = "updated_at")
@Schema(description="点赞状态最近更新时间")
private Date updatedAt;
}

View File

@@ -0,0 +1,41 @@
package com.yolo.keyborad.service;
import com.yolo.keyborad.model.entity.KeyboardAiCompanionLike;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
import java.util.Set;
/*
* @author: ziin
* @date: 2026/1/27 18:18
*/
public interface KeyboardAiCompanionLikeService extends IService<KeyboardAiCompanionLike> {
/**
* 点赞/取消点赞AI角色
*
* @param userId 用户ID
* @param companionId AI角色ID
* @return true=点赞成功false=取消点赞成功
*/
boolean toggleLike(Long userId, Long companionId);
/**
* 检查用户是否已点赞某AI角色
*
* @param userId 用户ID
* @param companionId AI角色ID
* @return true=已点赞false=未点赞
*/
boolean hasLiked(Long userId, Long companionId);
/**
* 批量获取用户已点赞的AI角色ID列表
*
* @param userId 用户ID
* @param companionIds AI角色ID列表
* @return 已点赞的AI角色ID集合
*/
Set<Long> getLikedCompanionIds(Long userId, List<Long> companionIds);
}

View File

@@ -0,0 +1,103 @@
package com.yolo.keyborad.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yolo.keyborad.common.ErrorCode;
import com.yolo.keyborad.exception.BusinessException;
import com.yolo.keyborad.mapper.KeyboardAiCompanionLikeMapper;
import com.yolo.keyborad.mapper.KeyboardAiCompanionMapper;
import com.yolo.keyborad.model.entity.KeyboardAiCompanion;
import com.yolo.keyborad.model.entity.KeyboardAiCompanionLike;
import com.yolo.keyborad.service.KeyboardAiCompanionLikeService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/*
* @author: ziin
* @date: 2026/1/27 18:18
*/
@Service
public class KeyboardAiCompanionLikeServiceImpl extends ServiceImpl<KeyboardAiCompanionLikeMapper, KeyboardAiCompanionLike> implements KeyboardAiCompanionLikeService {
@Resource
private KeyboardAiCompanionMapper companionMapper;
@Override
public boolean hasLiked(Long userId, Long companionId) {
LambdaQueryWrapper<KeyboardAiCompanionLike> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(KeyboardAiCompanionLike::getUserId, userId)
.eq(KeyboardAiCompanionLike::getCompanionId, companionId)
.eq(KeyboardAiCompanionLike::getStatus, (short) 1);
return this.count(queryWrapper) > 0;
}
@Override
public Set<Long> getLikedCompanionIds(Long userId, List<Long> companionIds) {
if (companionIds == null || companionIds.isEmpty()) {
return new HashSet<>();
}
LambdaQueryWrapper<KeyboardAiCompanionLike> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(KeyboardAiCompanionLike::getUserId, userId)
.in(KeyboardAiCompanionLike::getCompanionId, companionIds)
.eq(KeyboardAiCompanionLike::getStatus, (short) 1);
List<KeyboardAiCompanionLike> likes = this.list(queryWrapper);
return likes.stream()
.map(KeyboardAiCompanionLike::getCompanionId)
.collect(Collectors.toSet());
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean toggleLike(Long userId, Long companionId) {
// 检查AI角色是否存在
KeyboardAiCompanion companion = companionMapper.selectById(companionId);
if (companion == null || companion.getStatus() != 1) {
throw new BusinessException(ErrorCode.COMPANION_NOT_FOUND);
}
// 查找现有点赞记录
LambdaQueryWrapper<KeyboardAiCompanionLike> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(KeyboardAiCompanionLike::getUserId, userId)
.eq(KeyboardAiCompanionLike::getCompanionId, companionId);
KeyboardAiCompanionLike existingLike = this.getOne(queryWrapper);
Date now = new Date();
boolean isLiked;
if (existingLike != null) {
// 切换点赞状态
if (existingLike.getStatus() == 1) {
// 取消点赞
existingLike.setStatus((short) 0);
existingLike.setUpdatedAt(now);
this.updateById(existingLike);
isLiked = false;
} else {
// 重新点赞
existingLike.setStatus((short) 1);
existingLike.setUpdatedAt(now);
this.updateById(existingLike);
isLiked = true;
}
} else {
// 新增点赞记录
KeyboardAiCompanionLike like = new KeyboardAiCompanionLike();
like.setUserId(userId);
like.setCompanionId(companionId);
like.setStatus((short) 1);
like.setCreatedAt(now);
like.setUpdatedAt(now);
this.save(like);
isLiked = true;
}
return isLiked;
}
}

View File

@@ -0,0 +1,18 @@
<?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.KeyboardAiCompanionLikeMapper">
<resultMap id="BaseResultMap" type="com.yolo.keyborad.model.entity.KeyboardAiCompanionLike">
<!--@mbg.generated-->
<!--@Table keyboard_ai_companion_like-->
<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="status" jdbcType="SMALLINT" property="status" />
<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, "status", created_at, updated_at
</sql>
</mapper>