From b6d124619e0278e998f5dc53d974742f86e27d1c Mon Sep 17 00:00:00 2001 From: ziin Date: Tue, 27 Jan 2026 18:33:26 +0800 Subject: [PATCH] =?UTF-8?q?feat(ai-companion):=20=E6=96=B0=E5=A2=9EAI?= =?UTF-8?q?=E8=A7=92=E8=89=B2=E7=82=B9=E8=B5=9E=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增点赞/取消点赞接口,包含实体、Mapper、Service及DTO,支持用户点赞状态切换与异常处理。 --- .omc/ultrawork-state.json | 4 +- .../com/yolo/keyborad/common/ErrorCode.java | 1 + .../controller/AiCompanionController.java | 20 ++++ .../mapper/KeyboardAiCompanionLikeMapper.java | 12 ++ .../model/dto/companion/CompanionLikeReq.java | 16 +++ .../model/entity/KeyboardAiCompanionLike.java | 64 +++++++++++ .../KeyboardAiCompanionLikeService.java | 41 +++++++ .../KeyboardAiCompanionLikeServiceImpl.java | 103 ++++++++++++++++++ .../mapper/KeyboardAiCompanionLikeMapper.xml | 18 +++ 9 files changed, 277 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/yolo/keyborad/mapper/KeyboardAiCompanionLikeMapper.java create mode 100644 src/main/java/com/yolo/keyborad/model/dto/companion/CompanionLikeReq.java create mode 100644 src/main/java/com/yolo/keyborad/model/entity/KeyboardAiCompanionLike.java create mode 100644 src/main/java/com/yolo/keyborad/service/KeyboardAiCompanionLikeService.java create mode 100644 src/main/java/com/yolo/keyborad/service/impl/KeyboardAiCompanionLikeServiceImpl.java create mode 100644 src/main/resources/mapper/KeyboardAiCompanionLikeMapper.xml diff --git a/.omc/ultrawork-state.json b/.omc/ultrawork-state.json index d876b5c..3b5f77d 100644 --- a/.omc/ultrawork-state.json +++ b/.omc/ultrawork-state.json @@ -2,6 +2,6 @@ "active": true, "started_at": "2026-01-26T13:01:18.447Z", "original_prompt": "刚刚回滚了代码,现在AI陪聊角色评论需要使用KeyboardAiCompanionCommentLikeService添加一个评论点赞接口,用来记录点赞和取消点赞。 ulw", - "reinforcement_count": 5, - "last_checked_at": "2026-01-27T05:14:53.054Z" + "reinforcement_count": 7, + "last_checked_at": "2026-01-27T10:31:33.079Z" } \ No newline at end of file diff --git a/src/main/java/com/yolo/keyborad/common/ErrorCode.java b/src/main/java/com/yolo/keyborad/common/ErrorCode.java index 9c76eea..d4f5ac7 100644 --- a/src/main/java/com/yolo/keyborad/common/ErrorCode.java +++ b/src/main/java/com/yolo/keyborad/common/ErrorCode.java @@ -30,6 +30,7 @@ public enum ErrorCode { CHAT_SAVE_DATA_EMPTY(40010, "保存数据不能为空"), COMPANION_MESSAGE_EMPTY(40011, "消息内容不能为空"), COMPANION_ID_EMPTY(40012, "AI陪聊角色ID不能为空"), + COMPANION_NOT_FOUND(40019, "AI陪聊角色不存在"), COMMENT_CONTENT_EMPTY(40013, "评论内容不能为空"), COMMENT_NOT_FOUND(40014, "评论不存在"), COMMENT_ID_EMPTY(40015, "评论ID不能为空"), diff --git a/src/main/java/com/yolo/keyborad/controller/AiCompanionController.java b/src/main/java/com/yolo/keyborad/controller/AiCompanionController.java index 01a810e..c0e5a19 100644 --- a/src/main/java/com/yolo/keyborad/controller/AiCompanionController.java +++ b/src/main/java/com/yolo/keyborad/controller/AiCompanionController.java @@ -1,10 +1,15 @@ package com.yolo.keyborad.controller; +import cn.dev33.satoken.stp.StpUtil; 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.PageDTO; +import com.yolo.keyborad.model.dto.companion.CompanionLikeReq; import com.yolo.keyborad.model.vo.AiCompanionVO; +import com.yolo.keyborad.service.KeyboardAiCompanionLikeService; import com.yolo.keyborad.service.KeyboardAiCompanionService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -25,10 +30,25 @@ public class AiCompanionController { @Resource private KeyboardAiCompanionService aiCompanionService; + @Resource + private KeyboardAiCompanionLikeService aiCompanionLikeService; + @PostMapping("/page") @Operation(summary = "分页查询AI陪聊角色", description = "分页查询已上线的AI陪聊角色列表") public BaseResponse> pageList(@RequestBody PageDTO pageDTO) { IPage result = aiCompanionService.pageList(pageDTO.getPageNum(), pageDTO.getPageSize()); return ResultUtils.success(result); } + + @PostMapping("/like") + @Operation(summary = "点赞/取消点赞AI角色", description = "对AI角色进行点赞或取消点赞操作,返回true表示点赞成功,false表示取消点赞成功") + public BaseResponse 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); + } } diff --git a/src/main/java/com/yolo/keyborad/mapper/KeyboardAiCompanionLikeMapper.java b/src/main/java/com/yolo/keyborad/mapper/KeyboardAiCompanionLikeMapper.java new file mode 100644 index 0000000..1187278 --- /dev/null +++ b/src/main/java/com/yolo/keyborad/mapper/KeyboardAiCompanionLikeMapper.java @@ -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 { +} \ No newline at end of file diff --git a/src/main/java/com/yolo/keyborad/model/dto/companion/CompanionLikeReq.java b/src/main/java/com/yolo/keyborad/model/dto/companion/CompanionLikeReq.java new file mode 100644 index 0000000..eea994d --- /dev/null +++ b/src/main/java/com/yolo/keyborad/model/dto/companion/CompanionLikeReq.java @@ -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; +} diff --git a/src/main/java/com/yolo/keyborad/model/entity/KeyboardAiCompanionLike.java b/src/main/java/com/yolo/keyborad/model/entity/KeyboardAiCompanionLike.java new file mode 100644 index 0000000..c88876c --- /dev/null +++ b/src/main/java/com/yolo/keyborad/model/entity/KeyboardAiCompanionLike.java @@ -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; +} \ No newline at end of file diff --git a/src/main/java/com/yolo/keyborad/service/KeyboardAiCompanionLikeService.java b/src/main/java/com/yolo/keyborad/service/KeyboardAiCompanionLikeService.java new file mode 100644 index 0000000..9ea1aba --- /dev/null +++ b/src/main/java/com/yolo/keyborad/service/KeyboardAiCompanionLikeService.java @@ -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 { + + /** + * 点赞/取消点赞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 getLikedCompanionIds(Long userId, List companionIds); +} diff --git a/src/main/java/com/yolo/keyborad/service/impl/KeyboardAiCompanionLikeServiceImpl.java b/src/main/java/com/yolo/keyborad/service/impl/KeyboardAiCompanionLikeServiceImpl.java new file mode 100644 index 0000000..edf0d47 --- /dev/null +++ b/src/main/java/com/yolo/keyborad/service/impl/KeyboardAiCompanionLikeServiceImpl.java @@ -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 implements KeyboardAiCompanionLikeService { + + @Resource + private KeyboardAiCompanionMapper companionMapper; + + @Override + public boolean hasLiked(Long userId, Long companionId) { + LambdaQueryWrapper 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 getLikedCompanionIds(Long userId, List companionIds) { + if (companionIds == null || companionIds.isEmpty()) { + return new HashSet<>(); + } + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(KeyboardAiCompanionLike::getUserId, userId) + .in(KeyboardAiCompanionLike::getCompanionId, companionIds) + .eq(KeyboardAiCompanionLike::getStatus, (short) 1); + List 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 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; + } +} diff --git a/src/main/resources/mapper/KeyboardAiCompanionLikeMapper.xml b/src/main/resources/mapper/KeyboardAiCompanionLikeMapper.xml new file mode 100644 index 0000000..543bc08 --- /dev/null +++ b/src/main/resources/mapper/KeyboardAiCompanionLikeMapper.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + id, companion_id, user_id, "status", created_at, updated_at + + \ No newline at end of file