Browse Source

添加
1、获取单条聊天记录
2、单条聊天记录的点赞或差评

183207892172 1 week ago
parent
commit
f03fc58ec7

+ 60 - 2
java/storlead-knowledge/storlead-knowledge-api/src/main/java/com/storlead/knowledge/api/ChatController.java

@@ -6,8 +6,10 @@ import com.fasterxml.jackson.databind.JsonNode;
 import com.storlead.framework.common.result.Result;
 import com.storlead.knowledge.config.DifyProperties;
 import com.storlead.knowledge.entity.AiMessageEntity;
+import com.storlead.knowledge.entity.AiMessageReviewEntity;
 import com.storlead.knowledge.pojo.dto.ChatDTO;
 import com.storlead.knowledge.pojo.dto.QueryPageDTO;
+import com.storlead.knowledge.service.AiMessageReviewService;
 import com.storlead.knowledge.service.AiMessageService;
 import com.storlead.knowledge.utils.HttpService;
 import com.storlead.knowledge.utils.JacksonHolder;
@@ -40,6 +42,9 @@ public class ChatController {
     @Resource
     private AiMessageService aiMessageService;
 
+    @Resource
+    private AiMessageReviewService aiMessageReviewService;
+
     /**
      * 创建聊天 blocking模式
      * @param chatDTO
@@ -99,9 +104,62 @@ public class ChatController {
     }
 
     @GetMapping("messages")
-    @ApiOperation("获取聊天记录(支持关键字搜索和分页)")
+    @ApiOperation("获取聊天记录(支持关键字搜索和分页,含好评/差评总数)")
     public Result<Object> listMessages(QueryPageDTO page) {
-        return Result.ok(aiMessageService.pageQuery(page));
+        return Result.ok(aiMessageService.pageQueryWithReview(page));
+    }
+
+    @GetMapping("message/{id}")
+    @ApiOperation("根据ID获取单条聊天记录")
+    public Result<Object> getMessage(@PathVariable Long id) {
+        AiMessageEntity entity = aiMessageService.getById(id);
+        if (entity == null) {
+            return Result.error("聊天记录不存在");
+        }
+        return Result.ok(entity);
+    }
+
+    @PutMapping("message")
+    @ApiOperation("更新单条聊天记录")
+    public Result<Object> updateMessage(@RequestBody AiMessageEntity entity) {
+        if (entity.getId() == null) {
+            return Result.error("ID不能为空");
+        }
+        boolean success = aiMessageService.update(entity);
+        if (!success) {
+            return Result.error("更新失败,聊天记录不存在");
+        }
+        return Result.ok("更新成功");
+    }
+
+    @GetMapping("message/{id}/review")
+    @ApiOperation("根据聊天消息ID获取评价记录")
+    public Result<Object> getReview(@PathVariable Long id) {
+        AiMessageReviewEntity entity = aiMessageReviewService.getByAiMessageId(id);
+        if (entity == null) {
+            return Result.ok(new AiMessageReviewEntity().setAiMessageId(id).setAgree(0).setDisagree(0));
+        }
+        return Result.ok(entity);
+    }
+
+    @PostMapping("message/review")
+    @ApiOperation("添加或更新评价记录(点赞/差评)")
+    public Result<Object> saveOrUpdateReview(@RequestBody AiMessageReviewEntity entity) {
+        if (entity.getAiMessageId() == null) {
+            return Result.error("aiMessageId不能为空");
+        }
+        // 确保 agree 和 disagree 不为 null
+        if (entity.getAgree() == null) {
+            entity.setAgree(0);
+        }
+        if (entity.getDisagree() == null) {
+            entity.setDisagree(0);
+        }
+        boolean success = aiMessageReviewService.saveOrUpdateReview(entity);
+        if (!success) {
+            return Result.error("操作失败");
+        }
+        return Result.ok("操作成功");
     }
 
     @GetMapping("history")

+ 14 - 0
java/storlead-knowledge/storlead-knowledge-biz/src/main/java/com/storlead/knowledge/mapper/AiMessageMapper.java

@@ -1,7 +1,12 @@
 package com.storlead.knowledge.mapper;
 
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Constants;
 import com.storlead.framework.mybatis.mapper.MyBaseMapper;
 import com.storlead.knowledge.entity.AiMessageEntity;
+import com.storlead.knowledge.pojo.vo.AiMessageWithReviewVO;
+import org.apache.ibatis.annotations.Param;
 
 /**
  * <p>
@@ -13,4 +18,13 @@ import com.storlead.knowledge.entity.AiMessageEntity;
  */
 public interface AiMessageMapper extends MyBaseMapper<AiMessageEntity> {
 
+    /**
+     * 分页查询聊天记录(含好评/差评总数),LEFT JOIN ai_message_review 表
+     *
+     * @param page    分页参数
+     * @param wrapper 查询条件(由 MyBatis-Plus 自动构建)
+     * @return 分页结果(含 agree / disagree 字段)
+     */
+    IPage<AiMessageWithReviewVO> pageQueryWithReview(IPage<?> page,
+                                                     @Param(Constants.WRAPPER) Wrapper<AiMessageEntity> wrapper);
 }

+ 16 - 0
java/storlead-knowledge/storlead-knowledge-biz/src/main/java/com/storlead/knowledge/mapper/AiMessageReviewMapper.java

@@ -0,0 +1,16 @@
+package com.storlead.knowledge.mapper;
+
+import com.storlead.framework.mybatis.mapper.MyBaseMapper;
+import com.storlead.knowledge.entity.AiMessageReviewEntity;
+
+/**
+ * <p>
+ * AI对话消息评价 Mapper 接口
+ * </p>
+ *
+ * @author storlead
+ * @since 2026-06-09
+ */
+public interface AiMessageReviewMapper extends MyBaseMapper<AiMessageReviewEntity> {
+
+}

+ 47 - 0
java/storlead-knowledge/storlead-knowledge-biz/src/main/java/com/storlead/knowledge/service/impl/AiMessageReviewServiceImpl.java

@@ -0,0 +1,47 @@
+package com.storlead.knowledge.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.storlead.framework.mybatis.service.impl.MyBaseServiceImpl;
+import com.storlead.knowledge.entity.AiMessageReviewEntity;
+import com.storlead.knowledge.mapper.AiMessageReviewMapper;
+import com.storlead.knowledge.service.AiMessageReviewService;
+import org.springframework.stereotype.Service;
+
+/**
+ * <p>
+ * AI对话消息评价 服务实现类
+ * </p>
+ *
+ * @author storlead
+ * @since 2026-06-09
+ */
+@Service
+public class AiMessageReviewServiceImpl extends MyBaseServiceImpl<AiMessageReviewMapper, AiMessageReviewEntity> implements AiMessageReviewService {
+
+    @Override
+    public AiMessageReviewEntity getByAiMessageId(Long aiMessageId) {
+        LambdaQueryWrapper<AiMessageReviewEntity> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(AiMessageReviewEntity::getAiMessageId, aiMessageId);
+        return baseMapper.selectOne(wrapper);
+    }
+
+    @Override
+    public boolean saveOrUpdateReview(AiMessageReviewEntity entity) {
+        if (entity.getAiMessageId() == null) {
+            return false;
+        }
+
+        // 先查询是否已有评价记录
+        AiMessageReviewEntity existing = getByAiMessageId(entity.getAiMessageId());
+
+        if (existing != null) {
+            // 已有记录,更新 agree 和 disagree
+            existing.setAgree(entity.getAgree());
+            existing.setDisagree(entity.getDisagree());
+            return baseMapper.updateById(existing) > 0;
+        } else {
+            // 没有记录,新增
+            return baseMapper.insert(entity) > 0;
+        }
+    }
+}

+ 31 - 0
java/storlead-knowledge/storlead-knowledge-biz/src/main/java/com/storlead/knowledge/service/impl/AiMessageServiceImpl.java

@@ -7,6 +7,7 @@ import com.storlead.framework.mybatis.service.impl.MyBaseServiceImpl;
 import com.storlead.knowledge.entity.AiMessageEntity;
 import com.storlead.knowledge.mapper.AiMessageMapper;
 import com.storlead.knowledge.pojo.dto.QueryPageDTO;
+import com.storlead.knowledge.pojo.vo.AiMessageWithReviewVO;
 import com.storlead.knowledge.service.AiMessageService;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.stereotype.Service;
@@ -41,4 +42,34 @@ public class AiMessageServiceImpl extends MyBaseServiceImpl<AiMessageMapper, AiM
 
         return pageWithScope(pageParam, wrapper);
     }
+
+    @Override
+    public IPage<AiMessageWithReviewVO> pageQueryWithReview(QueryPageDTO page) {
+        // 构建分页参数
+        Page<AiMessageEntity> pageParam = new Page<>(page.getPageIndex(), page.getPageSize());
+
+        // 构建查询条件(与 pageQuery 一致)
+        LambdaQueryWrapper<AiMessageEntity> wrapper = new LambdaQueryWrapper<>();
+        if (StringUtils.isNotBlank(page.getKeyword())) {
+            wrapper.and(w -> w
+                    .like(AiMessageEntity::getQuery, page.getKeyword())
+                    .or()
+                    .like(AiMessageEntity::getAnswer, page.getKeyword())
+            );
+        }
+        wrapper.orderByDesc(AiMessageEntity::getCreatedAt);
+
+        // 直接调用 Mapper 的 LEFT JOIN 分页查询
+        return baseMapper.pageQueryWithReview(pageParam, wrapper);
+    }
+
+    @Override
+    public AiMessageEntity getById(Long id) {
+        return baseMapper.selectById(id);
+    }
+
+    @Override
+    public boolean update(AiMessageEntity entity) {
+        return baseMapper.updateById(entity) > 0;
+    }
 }

+ 34 - 0
java/storlead-knowledge/storlead-knowledge-biz/src/main/resources/mapper/AiMessageMapper.xml

@@ -0,0 +1,34 @@
+<?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.storlead.knowledge.mapper.AiMessageMapper">
+
+    <!-- 聊天记录 + 评价信息 分页查询结果映射 -->
+    <resultMap id="AiMessageWithReviewMap" type="com.storlead.knowledge.pojo.vo.AiMessageWithReviewVO"
+               extends="com.storlead.framework.mybatis.mapper.SysBaseFieldMapper.BaseResultMap">
+        <id column="id" property="id"/>
+        <result column="task_id" property="taskId"/>
+        <result column="message_id" property="messageId"/>
+        <result column="conversation_id" property="conversationId"/>
+        <result column="app_id" property="appId"/>
+        <result column="inputs" property="inputs"/>
+        <result column="query" property="query"/>
+        <result column="response_mode" property="responseMode"/>
+        <result column="user_id" property="userId"/>
+        <result column="answer" property="answer"/>
+        <result column="created_at" property="createdAt"/>
+        <result column="agree" property="agree"/>
+        <result column="disagree" property="disagree"/>
+    </resultMap>
+
+    <!-- 分页查询聊天记录(含评价信息) -->
+    <select id="pageQueryWithReview" resultMap="AiMessageWithReviewMap">
+        SELECT
+            m.*,
+            COALESCE(r.agree, 0) AS agree,
+            COALESCE(r.disagree, 0) AS disagree
+        FROM ai_messages m
+        LEFT JOIN ai_messages_review r ON r.ai_message_id = m.id
+        ${ew.customSqlSegment}
+    </select>
+
+</mapper>

+ 48 - 0
java/storlead-knowledge/storlead-knowledge-core/src/main/java/com/storlead/knowledge/entity/AiMessageReviewEntity.java

@@ -0,0 +1,48 @@
+package com.storlead.knowledge.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 com.storlead.framework.mybatis.entity.SysBaseField;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ * <p>
+ * AI对话消息评价表
+ * </p>
+ *
+ * @author storlead
+ * @since 2026-06-09
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@ApiModel(value = "AiMessageReviewEntity对象", description = "AI对话消息评价")
+@TableName("ai_messages_review")
+public class AiMessageReviewEntity extends SysBaseField implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value = "主键ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty(value = "关联的聊天消息ID")
+    @TableField("ai_message_id")
+    private Long aiMessageId;
+
+    @ApiModelProperty(value = "点赞数")
+    @TableField("agree")
+    private Integer agree;
+
+    @ApiModelProperty(value = "差评数")
+    @TableField("disagree")
+    private Integer disagree;
+}

+ 24 - 0
java/storlead-knowledge/storlead-knowledge-core/src/main/java/com/storlead/knowledge/pojo/vo/AiMessageWithReviewVO.java

@@ -0,0 +1,24 @@
+package com.storlead.knowledge.pojo.vo;
+
+import com.storlead.knowledge.entity.AiMessageEntity;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * <p>
+ * 聊天记录 + 评价信息 VO
+ * </p>
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ApiModel(value = "AiMessageWithReviewVO对象", description = "聊天记录及评价信息")
+public class AiMessageWithReviewVO extends AiMessageEntity {
+
+    @ApiModelProperty(value = "点赞数")
+    private Integer agree;
+
+    @ApiModelProperty(value = "差评数")
+    private Integer disagree;
+}

+ 32 - 0
java/storlead-knowledge/storlead-knowledge-spi/src/main/java/com/storlead/knowledge/service/AiMessageReviewService.java

@@ -0,0 +1,32 @@
+package com.storlead.knowledge.service;
+
+import com.storlead.framework.mybatis.service.MyBaseService;
+import com.storlead.knowledge.entity.AiMessageReviewEntity;
+
+/**
+ * <p>
+ * AI对话消息评价 服务类
+ * </p>
+ *
+ * @author storlead
+ * @since 2026-06-09
+ */
+public interface AiMessageReviewService extends MyBaseService<AiMessageReviewEntity> {
+
+    /**
+     * 根据聊天消息ID获取评价记录
+     *
+     * @param aiMessageId 聊天消息ID
+     * @return 评价记录
+     */
+    AiMessageReviewEntity getByAiMessageId(Long aiMessageId);
+
+    /**
+     * 添加或更新评价记录(点赞/差评)
+     * 如果该消息已有评价记录则更新,否则新增
+     *
+     * @param entity 评价实体(需包含 aiMessageId、agree、disagree)
+     * @return 是否操作成功
+     */
+    boolean saveOrUpdateReview(AiMessageReviewEntity entity);
+}

+ 25 - 0
java/storlead-knowledge/storlead-knowledge-spi/src/main/java/com/storlead/knowledge/service/AiMessageService.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.storlead.framework.mybatis.service.MyBaseService;
 import com.storlead.knowledge.entity.AiMessageEntity;
 import com.storlead.knowledge.pojo.dto.QueryPageDTO;
+import com.storlead.knowledge.pojo.vo.AiMessageWithReviewVO;
 
 /**
  * <p>
@@ -22,4 +23,28 @@ public interface AiMessageService extends MyBaseService<AiMessageEntity> {
      * @return 分页结果
      */
     IPage<AiMessageEntity> pageQuery(QueryPageDTO page);
+
+    /**
+     * 分页查询聊天记录(含好评/差评总数),keyword 可搜索 query 和 answer 字段
+     *
+     * @param page 分页参数
+     * @return 分页结果(含 agree / disagree 字段)
+     */
+    IPage<AiMessageWithReviewVO> pageQueryWithReview(QueryPageDTO page);
+
+    /**
+     * 根据主键获取单条聊天记录
+     *
+     * @param id 主键ID
+     * @return 聊天记录实体
+     */
+    AiMessageEntity getById(Long id);
+
+    /**
+     * 更新单条聊天记录
+     *
+     * @param entity 聊天记录实体(必须包含id)
+     * @return 是否更新成功
+     */
+    boolean update(AiMessageEntity entity);
 }