Parcourir la source

feat:新增从dify平台同步对话详情,页面展示对话记录 TODO定时获取

lihao16 il y a 4 mois
Parent
commit
f1fb4c8d90

+ 11 - 0
smsb-modules/smsb-device/src/main/java/com/inspur/device/controller/SmsbDeviceChatRecordController.java

@@ -1,7 +1,9 @@
 package com.inspur.device.controller;
 
 import cn.dev33.satoken.annotation.SaCheckPermission;
+import com.inspur.device.domain.SmsbDeviceChatDetail;
 import com.inspur.device.domain.bo.SmsbDeviceChatRecordBo;
+import com.inspur.device.domain.vo.SmsbDeviceChatDetailVo;
 import com.inspur.device.domain.vo.SmsbDeviceChatRecordVo;
 import com.inspur.device.service.ISmsbDeviceChatRecordService;
 import jakarta.servlet.http.HttpServletResponse;
@@ -46,6 +48,15 @@ public class SmsbDeviceChatRecordController extends BaseController {
         return smsbDeviceChatRecordService.queryPageList(bo, pageQuery);
     }
 
+    /**
+     * 查询问答记录列表
+     */
+    @SaCheckPermission("device:chatRecord:query")
+    @GetMapping("/list/detail/{conversationId}")
+    public R<List<SmsbDeviceChatDetailVo>> listDetail(@PathVariable String conversationId) {
+        return R.ok(smsbDeviceChatRecordService.listDetail(conversationId));
+    }
+
     /**
      * 导出问答记录列表
      */

+ 63 - 0
smsb-modules/smsb-device/src/main/java/com/inspur/device/domain/SmsbDeviceChatDetail.java

@@ -0,0 +1,63 @@
+package com.inspur.device.domain;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serial;
+import java.util.Date;
+
+/**
+ * 设备AI对话详情对象 smsb_device_chat_detail
+ *
+ * @author Hao Li
+ * @date 2025-06-19
+ */
+@Data
+@TableName("smsb_device_chat_detail")
+public class SmsbDeviceChatDetail {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @TableId(value = "id")
+    private Long id;
+
+    /**
+     * dify对话记录ID
+     */
+    private String conversationId;
+
+    /**
+     * dify平台ID
+     */
+    private String difyId;
+
+    /**
+     * 问题
+     */
+    private String query;
+
+    /**
+     * 回答
+     */
+    private String answer;
+
+    /**
+     * dify created_at
+     */
+    private Long createdAt;
+
+    /**
+     * 租户编号
+     */
+    private String tenantId;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+}

+ 53 - 0
smsb-modules/smsb-device/src/main/java/com/inspur/device/domain/bo/SmsbDeviceChatDetailBo.java

@@ -0,0 +1,53 @@
+package com.inspur.device.domain.bo;
+
+import com.inspur.device.domain.SmsbDeviceChatDetail;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 设备AI对话详情业务对象 smsb_device_chat_detail
+ *
+ * @author Hao Li
+ * @date 2025-06-19
+ */
+@Data
+@AutoMapper(target = SmsbDeviceChatDetail.class, reverseConvertGenerate = false)
+public class SmsbDeviceChatDetailBo {
+
+    /**
+     * 主键ID
+     */
+    private Long id;
+
+    /**
+     * dify对话记录ID
+     */
+    private String conversationId;
+
+    /**
+     * dify平台ID
+     */
+    private String difyId;
+
+    /**
+     * 问题
+     */
+    private String query;
+
+    /**
+     * 回答
+     */
+    private String answer;
+
+    /**
+     * dify created_at
+     */
+    private Long createdAt;
+
+    /**
+     * 创建时间
+     */
+    private Date createTime;
+}

+ 22 - 0
smsb-modules/smsb-device/src/main/java/com/inspur/device/domain/vo/DifyChatDetailRspData.java

@@ -0,0 +1,22 @@
+package com.inspur.device.domain.vo;
+
+import lombok.Data;
+
+/**
+ * 同步dify对话记录 详情
+ * @author lihao16
+ */
+@Data
+public class DifyChatDetailRspData {
+
+    private String id;
+
+    private String conversation_id;
+
+    private String query;
+
+    private String answer;
+
+    private Long created_at;
+
+}

+ 71 - 0
smsb-modules/smsb-device/src/main/java/com/inspur/device/domain/vo/SmsbDeviceChatDetailVo.java

@@ -0,0 +1,71 @@
+package com.inspur.device.domain.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.inspur.device.domain.SmsbDeviceChatDetail;
+import io.github.linpeilie.annotations.AutoMapper;
+import lombok.Data;
+
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.Date;
+
+
+/**
+ * 设备AI对话详情视图对象 smsb_device_chat_detail
+ *
+ * @author Hao Li
+ * @date 2025-06-19
+ */
+@Data
+@ExcelIgnoreUnannotated
+@AutoMapper(target = SmsbDeviceChatDetail.class)
+public class SmsbDeviceChatDetailVo implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 主键ID
+     */
+    @ExcelProperty(value = "主键ID")
+    private Long id;
+
+    /**
+     * dify对话记录ID
+     */
+    @ExcelProperty(value = "dify对话记录ID")
+    private String conversationId;
+
+    /**
+     * dify平台ID
+     */
+    @ExcelProperty(value = "dify平台ID")
+    private String difyId;
+
+    /**
+     * 问题
+     */
+    @ExcelProperty(value = "问题")
+    private String query;
+
+    /**
+     * 回答
+     */
+    @ExcelProperty(value = "回答")
+    private String answer;
+
+    /**
+     * dify created_at
+     */
+    @ExcelProperty(value = "dify created_at")
+    private Long createdAt;
+
+    /**
+     * 创建时间
+     */
+    @ExcelProperty(value = "创建时间")
+    private Date createTime;
+
+
+}

+ 15 - 0
smsb-modules/smsb-device/src/main/java/com/inspur/device/mapper/SmsbDeviceChatDetailMapper.java

@@ -0,0 +1,15 @@
+package com.inspur.device.mapper;
+
+import com.inspur.device.domain.SmsbDeviceChatDetail;
+import com.inspur.device.domain.vo.SmsbDeviceChatDetailVo;
+import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
+
+/**
+ * 设备AI对话详情Mapper接口
+ *
+ * @author Hao Li
+ * @date 2025-06-19
+ */
+public interface SmsbDeviceChatDetailMapper extends BaseMapperPlus<SmsbDeviceChatDetail, SmsbDeviceChatDetailVo> {
+
+}

+ 8 - 0
smsb-modules/smsb-device/src/main/java/com/inspur/device/service/ISmsbDeviceChatRecordService.java

@@ -1,6 +1,7 @@
 package com.inspur.device.service;
 
 import com.inspur.device.domain.bo.SmsbDeviceChatRecordBo;
+import com.inspur.device.domain.vo.SmsbDeviceChatDetailVo;
 import com.inspur.device.domain.vo.SmsbDeviceChatRecordVo;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
@@ -71,4 +72,11 @@ public interface ISmsbDeviceChatRecordService {
      * @return
      */
     Boolean syncRecordFromDify();
+
+    /**
+     * 对话详情
+     * @param conversationId
+     * @return
+     */
+    List<SmsbDeviceChatDetailVo> listDetail(String conversationId);
 }

+ 39 - 2
smsb-modules/smsb-device/src/main/java/com/inspur/device/service/impl/SmsbDeviceChatRecordServiceImpl.java

@@ -8,9 +8,11 @@ import com.alibaba.fastjson2.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.inspur.device.domain.SmsbDeviceChatDetail;
 import com.inspur.device.domain.SmsbDeviceChatRecord;
 import com.inspur.device.domain.bo.SmsbDeviceChatRecordBo;
 import com.inspur.device.domain.vo.*;
+import com.inspur.device.mapper.SmsbDeviceChatDetailMapper;
 import com.inspur.device.mapper.SmsbDeviceChatKeyMapper;
 import com.inspur.device.mapper.SmsbDeviceChatRecordMapper;
 import com.inspur.device.service.ISmsbDeviceChatRecordService;
@@ -39,10 +41,13 @@ public class SmsbDeviceChatRecordServiceImpl implements ISmsbDeviceChatRecordSer
 
     private final SmsbDeviceChatRecordMapper baseMapper;
     private final SmsbDeviceChatKeyMapper smsbDeviceChatKeyMapper;
+    private final SmsbDeviceChatDetailMapper smsbDeviceChatDetailMapper;
     @Value("${dify.url}")
     private String difyBaseUrl;
     private static final String SYNC_CHAT_RECORD_URL = "/v1/conversations";
 
+    private static final String SYNC_CHAT_DETAIL_URL = "/v1/messages";
+
     /**
      * 查询问答记录
      *
@@ -149,6 +154,7 @@ public class SmsbDeviceChatRecordServiceImpl implements ISmsbDeviceChatRecordSer
             return true;
         }
         List<SmsbDeviceChatRecord> addRecordList = new ArrayList<>();
+        List<SmsbDeviceChatDetail> addDetailList = new ArrayList<>();
         for (SmsbDeviceChatKeyVo chatKey : chatKeyVOList) {
             String requestUrl = difyBaseUrl + SYNC_CHAT_RECORD_URL + "?user=role&limit=20";
             String apiKey = chatKey.getApiKey();
@@ -176,14 +182,45 @@ public class SmsbDeviceChatRecordServiceImpl implements ISmsbDeviceChatRecordSer
                 SmsbDeviceChatRecord addRecord = new SmsbDeviceChatRecord();
                 createNewAddRecord(addRecord,rspData,chatKey);
                 addRecordList.add(addRecord);
-                // 5、需要插入 再获取对话内容 TODO
-
+                // 6、再获取对话内容
+                String requestDetailUrl = difyBaseUrl + SYNC_CHAT_DETAIL_URL + "?user=role&limit=100&conversation_id=" + difyId;
+                JSONObject difyResponseDetail = getRecordFromDify(requestDetailUrl,apiKey);
+                if (null == difyResponseDetail) {
+                    continue;
+                }
+                List<DifyChatDetailRspData> rspDetailDataList = difyResponseDetail.getJSONArray("data").toJavaList(DifyChatDetailRspData.class);
+                if (CollectionUtils.isEmpty(rspDetailDataList)) {
+                    continue;
+                }
+                // 7 组装详情数据
+                for (DifyChatDetailRspData detailRspData : rspDetailDataList) {
+                    SmsbDeviceChatDetail addDetail = new SmsbDeviceChatDetail();
+                    createNewAddDetail(addDetail,detailRspData);
+                    addDetailList.add(addDetail);
+                }
             }
         }
         baseMapper.insertBatch(addRecordList);
+        smsbDeviceChatDetailMapper.insertBatch(addDetailList);
         return true;
     }
 
+    @Override
+    public List<SmsbDeviceChatDetailVo> listDetail(String conversationId) {
+        return smsbDeviceChatDetailMapper.selectVoList(new LambdaQueryWrapper<SmsbDeviceChatDetail>()
+            .eq(SmsbDeviceChatDetail::getConversationId,conversationId)
+            .orderByDesc(SmsbDeviceChatDetail::getId));
+    }
+
+    private void createNewAddDetail(SmsbDeviceChatDetail addDetail, DifyChatDetailRspData detailRspData) {
+        addDetail.setConversationId(detailRspData.getConversation_id());
+        addDetail.setDifyId(detailRspData.getId());
+        addDetail.setQuery(detailRspData.getQuery());
+        addDetail.setAnswer(detailRspData.getAnswer());
+        addDetail.setCreatedAt(detailRspData.getCreated_at());
+        addDetail.setCreateTime(DateUtil.date(detailRspData.getCreated_at() * 1000));
+    }
+
     private void createNewAddRecord(SmsbDeviceChatRecord addRecord, DifyChatRecordRspData rspData, SmsbDeviceChatKeyVo chatKey) {
         addRecord.setDeviceId(chatKey.getDeviceId());
         addRecord.setDeviceName(chatKey.getDeviceName());

+ 7 - 0
smsb-modules/smsb-device/src/main/resources/mapper/device/SmsbDeviceChatDetailMapper.xml

@@ -0,0 +1,7 @@
+<?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.inspur.device.mapper.SmsbDeviceChatDetailMapper">
+
+</mapper>

+ 7 - 1
smsb-plus-ui/src/api/smsb/device/device_chat_record.ts

@@ -1,6 +1,6 @@
 import request from '@/utils/request';
 import { AxiosPromise } from 'axios';
-import { ChatRecordVO, ChatRecordForm, ChatRecordQuery } from '@/api/smsb/device/device_chat_record_type';
+import {ChatRecordVO, ChatRecordForm, ChatRecordQuery, ChatDetailVO} from '@/api/smsb/device/device_chat_record_type';
 
 /**
  * 查询问答记录列表
@@ -16,6 +16,12 @@ export const listChatRecord = (query?: ChatRecordQuery): AxiosPromise<ChatRecord
   });
 };
 
+export const listChatDetail = (conversationId: string | number): AxiosPromise<ChatDetailVO[]> => {
+  return request({
+    url: '/device/chatRecord/list/detail/' + conversationId,
+    method: 'get',
+  });
+};
 export const syncChatRecord = (): AxiosPromise<ChatRecordVO[]> => {
   return request({
     url: '/device/chatRecord/sync',

+ 36 - 0
smsb-plus-ui/src/api/smsb/device/device_chat_record_type.ts

@@ -107,5 +107,41 @@ export interface ChatRecordQuery extends PageQuery {
   params?: any;
 }
 
+export interface ChatDetailVO {
+  /**
+   * 主键ID
+   */
+  id: string | number;
+
+  /**
+   * dify对话记录ID
+   */
+  conversationId: string | number;
+
+  /**
+   * dify平台ID
+   */
+  difyId: string | number;
+
+  /**
+   * 问题
+   */
+  query: string;
 
+  /**
+   * 回答
+   */
+  answer: string;
+
+  /**
+   * dify created_at
+   */
+  createdAt: number;
+
+  /**
+   * 创建时间
+   */
+  createTime: string;
+
+}
 

+ 33 - 3
smsb-plus-ui/src/views/smsb/deviceChatRecord/index.vue

@@ -29,6 +29,10 @@
           <el-table-column label="对话时间" align="left" prop="createTime" width="180"/>
           <el-table-column label="操作" align="center" width="100" class-name="small-padding fixed-width">
             <template #default="scope">
+              <el-tooltip content="详情" placement="top">
+                <el-button link type="primary" icon="View" @click="handleInfo(scope.row)"
+                           v-hasPermi="['device:chatRecord:query']"></el-button>
+              </el-tooltip>
               <el-tooltip content="删除" placement="top">
                 <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)"
                            v-hasPermi="['device:chatRecord:remove']"></el-button>
@@ -42,7 +46,20 @@
                   v-model:limit="queryParams.pageSize" @pagination="getList"/>
     </el-card>
     <!-- 添加或修改问答记录对话框 -->
-    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="1500px" append-to-body>
+      <div class="table-content">
+        <el-table v-loading="infoLoading" :data="chatDetailList" >
+          <el-table-column label="" align="left" prop="" width="10"/>
+          <el-table-column label="问题" align="left" prop="query" width="400"/>
+          <el-table-column label="回答" align="left" prop="answer" />
+          <el-table-column label="对话时间" align="left" prop="createTime" width="160"/>
+        </el-table>
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="cancel">关 闭</el-button>
+        </div>
+      </template>
     </el-dialog>
   </div>
 </template>
@@ -51,17 +68,19 @@
 import {
   addChatRecord,
   delChatRecord,
-  getChatRecord,
+  getChatRecord, listChatDetail,
   listChatRecord, syncChatRecord,
   updateChatRecord
 } from '@/api/smsb/device/device_chat_record';
-import {ChatRecordForm, ChatRecordQuery, ChatRecordVO} from '@/api/smsb/device/device_chat_record_type';
+import {ChatDetailVO, ChatRecordForm, ChatRecordQuery, ChatRecordVO} from '@/api/smsb/device/device_chat_record_type';
 
 const {proxy} = getCurrentInstance() as ComponentInternalInstance;
 
 const chatRecordList = ref<ChatRecordVO[]>([]);
+const chatDetailList = ref<ChatDetailVO[]>([]);
 const buttonLoading = ref(false);
 const loading = ref(true);
+const infoLoading = ref(true);
 const showSearch = ref(true);
 const ids = ref<Array<string | number>>([]);
 const single = ref(true);
@@ -109,6 +128,17 @@ const getList = async () => {
   total.value = res.total;
   loading.value = false;
 }
+const handleInfo = async (row? : ChatRecordVO) => {
+  infoLoading.value = true;
+  dialog.visible = true;
+  dialog.title = "对话详情";
+  const conversation_id = row.difyId;
+  const res = await listChatDetail(conversation_id);
+  chatDetailList.value = res.data;
+  console.log(res.data)
+  console.log("chatDetailList : " + chatDetailList.value)
+  infoLoading.value = false;
+}
 const handleSync = async () => {
   loading.value = true;
   const res = await syncChatRecord();