Преглед на файлове

红德堂-V1.2 测一测结果对接豆包

Long преди 4 дни
родител
ревизия
0e1faa35a5

+ 23 - 9
fs-admin/src/main/java/com/fs/his/controller/FsTestReportController.java

@@ -2,20 +2,13 @@ package com.fs.his.controller;
 
 import java.util.List;
 
+import com.fs.his.domain.FsTestReportAiLog;
 import com.fs.his.param.FsTestReportParam;
 import com.fs.his.vo.FsTestReportListVO;
 import com.fs.his.vo.FsTestReportVO;
-import com.fs.his.vo.FsUserCouponListVO;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
@@ -122,4 +115,25 @@ public class FsTestReportController extends BaseController
     {
         return toAjax(fsTestReportService.deleteFsTestReportByReportIds(reportIds));
     }
+
+    /**
+     * 查询测试报告AI日志列表
+     */
+    @GetMapping("/reportAiLogByReportId")
+    public TableDataInfo reportAiLogByReportId(@RequestParam Long reportId)
+    {
+        startPage();
+        List<FsTestReportAiLog> list = fsTestReportService.selectAiLogByReportId(reportId);
+        return getDataTable(list);
+    }
+
+    /**
+     * 查询测试报告Token总计
+     */
+    @GetMapping("/tokenTotal")
+    public AjaxResult tokenTotal(FsTestReportParam param)
+    {
+        Long total = fsTestReportService.selectTokenTotal(param);
+        return AjaxResult.success(total);
+    }
 }

+ 5 - 5
fs-common-api/pom.xml

@@ -18,11 +18,11 @@
     <dependencies>
 
         <!-- spring-boot-devtools -->
-        <dependency>
-            <groupId>org.springframework.boot</groupId>
-            <artifactId>spring-boot-devtools</artifactId>
-            <optional>true</optional> <!-- 表示依赖不会传递 -->
-        </dependency>
+<!--        <dependency>-->
+<!--            <groupId>org.springframework.boot</groupId>-->
+<!--            <artifactId>spring-boot-devtools</artifactId>-->
+<!--            <optional>true</optional> &lt;!&ndash; 表示依赖不会传递 &ndash;&gt;-->
+<!--        </dependency>-->
         <!-- swagger2-->
         <dependency>
             <groupId>io.springfox</groupId>

+ 12 - 0
fs-service/src/main/java/com/fs/his/config/TestConfig.java

@@ -0,0 +1,12 @@
+package com.fs.his.config;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class TestConfig implements Serializable {
+    private Boolean enable;
+    private String url;
+    private String appKey;
+}

+ 56 - 0
fs-service/src/main/java/com/fs/his/domain/FsTestReportAiLog.java

@@ -0,0 +1,56 @@
+package com.fs.his.domain;
+
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * AI报告分析日志对象 fs_test_report_ai_log
+ *
+ * @author fs
+ * @date 2026-05-14
+ */
+@Data
+public class FsTestReportAiLog
+{
+
+    /** ID */
+    private Long id;
+
+    /** 关联的报告ID */
+    private Long reportId;
+
+    /** 用户ID */
+    private Long userId;
+
+    /** FastGPT chatId */
+    private String chatId;
+
+    /** 模型名称 */
+    private String modelName;
+
+    /** 用户输入 */
+    private String queryContent;
+
+    /** AI思考 */
+    private String reasoningText;
+
+    /** AI返回的优化内容 */
+    private String aiContent;
+
+    /** 输入token数 */
+    private Integer inputTokens;
+
+    /** 输出token数 */
+    private Integer outputTokens;
+
+    /** 总token数 */
+    private Integer totalTokens;
+
+    /** 消费时间 **/
+    private Double costTime;
+
+    /** 创建时间**/
+    private LocalDateTime createTime;
+
+}

+ 14 - 0
fs-service/src/main/java/com/fs/his/mapper/FsTestReportAiLogMapper.java

@@ -0,0 +1,14 @@
+package com.fs.his.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.his.domain.FsTestReportAiLog;
+
+/**
+ * AI报告分析日志Mapper接口
+ *
+ * @author fs
+ * @date 2026-05-14
+ */
+public interface FsTestReportAiLogMapper extends BaseMapper<FsTestReportAiLog> {
+
+}

+ 12 - 0
fs-service/src/main/java/com/fs/his/mapper/FsTestReportMapper.java

@@ -77,5 +77,17 @@ public interface FsTestReportMapper
     @Select("select tr.*,t.`name`,u.nick_name,u.phone FROM fs_test_report tr LEFT JOIN fs_test_temp t ON t.temp_id=tr.temp_id LEFT JOIN fs_user u ON u.user_id=tr.user_id where tr.report_id =#{reportId}")
     FsTestReportVO selectFsTestReportByReportIdVO(Long reportId);
 
+    @Select({"<script> " +
+            "select COALESCE(SUM(ail.total_tokens), 0) FROM fs_test_report_ai_log ail " +
+            "LEFT JOIN fs_test_report tr ON tr.report_id = ail.report_id " +
+            "LEFT JOIN fs_test_temp t ON t.temp_id = tr.temp_id " +
+            "<where> " +
+            "<if test=\"reportSn != null and reportSn != ''\"> and tr.report_sn = #{reportSn}</if>" +
+            "<if test=\"name != null and name != ''\"> and t.name like concat('%', #{name}, '%')</if>" +
+            "<if test=\"sTime != null\"> and DATE(tr.create_time) &gt;= DATE(#{sTime})</if>" +
+            "<if test=\"eTime != null\"> and DATE(tr.create_time) &lt;= DATE(#{eTime})</if>" +
+            "</where>" +
+            "</script>"})
+    Long selectTokenTotal(FsTestReportParam param);
 
 }

+ 3 - 0
fs-service/src/main/java/com/fs/his/service/IFsTestReportService.java

@@ -4,6 +4,7 @@ import java.util.List;
 
 import com.fs.common.core.domain.R;
 import com.fs.his.domain.FsTestReport;
+import com.fs.his.domain.FsTestReportAiLog;
 import com.fs.his.param.FsTestReportParam;
 import com.fs.his.vo.FsTestReportListVO;
 import com.fs.his.vo.FsTestReportVO;
@@ -70,6 +71,8 @@ public interface IFsTestReportService
 
     FsTestReportVO selectFsTestReportByReportIdVO(Long reportId);
 
+    List<FsTestReportAiLog> selectAiLogByReportId(Long reportId);
 
+    Long selectTokenTotal(FsTestReportParam param);
 
 }

+ 153 - 2
fs-service/src/main/java/com/fs/his/service/impl/FsTestReportServiceImpl.java

@@ -1,11 +1,18 @@
 package com.fs.his.service.impl;
 
 
+import java.time.LocalDateTime;
 import java.util.*;
 import java.util.List;
 import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import cn.hutool.json.JSONUtil;
 import com.fs.common.core.domain.R;
 import com.fs.common.utils.DateUtils;
+import com.fs.fastgptApi.config.FastGptApiConfig;
+import com.fs.fastgptApi.util.HttpUtil;
+import com.fs.his.config.TestConfig;
 import com.fs.his.domain.FsTestTemp;
 import com.fs.his.domain.FsTestTempItem;
 import com.fs.his.mapper.FsTestTempItemMapper;
@@ -14,12 +21,17 @@ import com.fs.his.param.FsTestReportParam;
 import com.fs.his.utils.TestReportUtil;
 import com.fs.his.vo.FsTestReportListVO;
 import com.fs.his.vo.FsTestReportVO;
+import com.fs.system.service.ISysConfigService;
 import com.qiniu.util.Json;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import com.fs.his.mapper.FsTestReportMapper;
 import com.fs.his.domain.FsTestReport;
 import com.fs.his.service.IFsTestReportService;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.fs.his.domain.FsTestReportAiLog;
+import com.fs.his.mapper.FsTestReportAiLogMapper;
 /**
  * 测试报告Service业务层处理
  *
@@ -28,6 +40,7 @@ import com.fs.his.service.IFsTestReportService;
  */
 @SuppressWarnings("all")
 @Service
+@Slf4j
 public class FsTestReportServiceImpl implements IFsTestReportService
 {
     @Autowired
@@ -37,6 +50,10 @@ public class FsTestReportServiceImpl implements IFsTestReportService
     private FsTestTempItemMapper fsTestTempItemMapper;
     @Autowired
     private FsTestTempMapper fsTestTempMapper;
+    @Autowired
+    private FsTestReportAiLogMapper fsTestReportAiLogMapper;
+    @Autowired
+    private ISysConfigService configService;
     /**
      * 查询测试报告
      *
@@ -172,10 +189,18 @@ public class FsTestReportServiceImpl implements IFsTestReportService
                 }
             }
             testReport.setCreateTime(DateUtils.getNowDate());
+
+            // AI润色测一测答案
+            Map<String, Object> aiResult = aiOptimize(fsTestTemp.getName(), itemType, maps, itemTypeJSonValue);
+            itemTypeJSonValue = (String) aiResult.get("content");
             testReport.setConditioningPlanJson(itemTypeJSonValue);
             testReport.setTestResult(itemType);
             testReport.setScore(Long.parseLong(allScore+""));
             if(fsTestReportMapper.insertFsTestReport(testReport)>0){
+                if ((Boolean) aiResult.get("isAi")) {
+                    saveAiLog(aiResult, testReport.getReportId(), testReport.getUserId());
+                }
+
                 testReportUtil.getTestReportImageUrlByReportId(testReport.getReportId());
                 return R.ok().put("reportId",testReport.getReportId());
             }
@@ -256,10 +281,18 @@ public class FsTestReportServiceImpl implements IFsTestReportService
                 }
             }
             testReport.setCreateTime(DateUtils.getNowDate());
+
+            // AI润色测一测答案
+            Map<String, Object> aiResult = aiOptimize(fsTestTemp.getName(), tempType, maps, itemTypeJSonValue);
+            itemTypeJSonValue = (String) aiResult.get("content");
             testReport.setConditioningPlanJson(itemTypeJSonValue);
             testReport.setTestResult(tempType);
             testReport.setScore( Math.round(score));
             if(fsTestReportMapper.insertFsTestReport(testReport)>0){
+                if ((Boolean) aiResult.get("isAi")) {
+                    saveAiLog(aiResult, testReport.getReportId(), testReport.getUserId());
+                }
+
                 testReportUtil.getTestReportImageUrlByReportId(testReport.getReportId());
                 return R.ok().put("reportId",testReport.getReportId());
             }
@@ -271,6 +304,114 @@ public class FsTestReportServiceImpl implements IFsTestReportService
 
     }
 
+    /**
+     * AI润色测一测答案
+     */
+    private Map<String, Object> aiOptimize(String testName, String result, List<Map> params, String conditioningPlanJsonStr) {
+        Map<String, Object> resultMap = new HashMap<>();
+
+        String chatId = "TS" + UUID.randomUUID().toString();
+        resultMap.put("chatId", chatId);
+        resultMap.put("content", conditioningPlanJsonStr);
+        resultMap.put("isAi", false);
+        resultMap.put("inputTokens", 0);
+        resultMap.put("outputTokens", 0);
+        resultMap.put("query", "");
+        resultMap.put("reasoningText", "");
+        resultMap.put("modelName", "");
+        resultMap.put("costTime", 0D);
+
+        String json = configService.selectConfigByKey("test.config");
+        TestConfig testConfig = JSONUtil.toBean(json, TestConfig.class);
+        if (testConfig.getEnable() == null || !testConfig.getEnable()) {
+            return resultMap;
+        }
+        String appKey = testConfig.getAppKey();
+        String url = testConfig.getUrl();
+
+        try {
+            JSONObject requestJson = new JSONObject();
+            requestJson.put("stream", false);
+            requestJson.put("detail", true);
+            requestJson.put("chatId", chatId);
+
+            JSONObject content = new JSONObject();
+            content.put("testName", testName);
+            content.put("result", result);
+
+            List<Map<String, Object>> filteredParams = new ArrayList<>();
+            for (Map paramMap : params) {
+                Map<String, Object> filteredMap = new HashMap<>();
+                filteredMap.put("option", paramMap.get("option"));
+                filteredMap.put("title", paramMap.get("title"));
+                filteredParams.add(filteredMap);
+            }
+            content.put("params", filteredParams);
+            content.put("conditioningPlanJson", conditioningPlanJsonStr);
+
+            JSONObject message = new JSONObject();
+            message.put("role", "user");
+            message.put("content", JSON.toJSONString(content));
+
+            JSONArray messages = new JSONArray();
+            messages.add(message);
+            requestJson.put("messages", messages);
+
+            String response = HttpUtil.sendPost(requestJson, url + FastGptApiConfig.completionsUrl, appKey);
+
+            log.info("FastGPT Request: {}", requestJson);
+            log.info("FastGPT Response: {}", response);
+
+            JSONObject jsonObject = JSON.parseObject(response);
+
+            if (jsonObject.containsKey("responseData") && ((JSONArray)jsonObject.get("responseData")).size() > 0) {
+                JSONArray responseData = jsonObject.getJSONArray("responseData");
+                responseData.stream().map(o -> (JSONObject) o).filter(o -> "chatNode".equals(o.getString("moduleType"))).findAny().ifPresent(o -> {
+                    resultMap.put("costTime", o.getDoubleValue("runningTime"));
+                    resultMap.put("inputTokens", o.getIntValue("inputTokens"));
+                    resultMap.put("outputTokens", o.getIntValue("outputTokens"));
+                    resultMap.put("query", o.getString("query"));
+                    resultMap.put("reasoningText", o.getString("reasoningText"));
+                    resultMap.put("modelName", o.getString("model"));
+                });
+            }
+
+            if (jsonObject.containsKey("choices") && jsonObject.getJSONArray("choices").size() > 0) {
+                JSONObject choice = (JSONObject) jsonObject.getJSONArray("choices").get(0);
+                if (choice.containsKey("message")) {
+                    JSONObject messageObj = (JSONObject) choice.get("message");
+                    resultMap.put("content", messageObj.getString("content"));
+                    resultMap.put("isAi", true);
+                }
+            }
+
+        } catch (Exception e) {
+            log.error("AI优化失败: {}", e.getMessage(), e);
+        }
+
+        return resultMap;
+    }
+
+    /**
+     * 保存ai回复
+     */
+    private void saveAiLog(Map<String, Object> aiResult, Long reportId, Long userId) {
+        FsTestReportAiLog aiLog = new FsTestReportAiLog();
+        aiLog.setReportId(reportId);
+        aiLog.setUserId(userId);
+        aiLog.setChatId((String) aiResult.get("chatId"));
+        aiLog.setModelName((String) aiResult.get("modelName"));
+        aiLog.setQueryContent((String) aiResult.get("query"));
+        aiLog.setReasoningText((String) aiResult.get("reasoningText"));
+        aiLog.setAiContent((String) aiResult.get("content"));
+        aiLog.setInputTokens((Integer) aiResult.get("inputTokens"));
+        aiLog.setOutputTokens((Integer) aiResult.get("outputTokens"));
+        aiLog.setTotalTokens((Integer) aiResult.get("inputTokens") + (Integer) aiResult.get("outputTokens"));
+        aiLog.setCostTime((Double) aiResult.get("costTime"));
+        aiLog.setCreateTime(LocalDateTime.now());
+        fsTestReportAiLogMapper.insert(aiLog);
+    }
+
     /**
      * 修改测试报告
      *
@@ -320,7 +461,17 @@ public class FsTestReportServiceImpl implements IFsTestReportService
     }
 
 
+    @Override
+    public List<FsTestReportAiLog> selectAiLogByReportId(Long reportId) {
+        LambdaQueryWrapper<FsTestReportAiLog> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(FsTestReportAiLog::getReportId, reportId)
+               .orderByDesc(FsTestReportAiLog::getCreateTime);
+        return fsTestReportAiLogMapper.selectList(wrapper);
+    }
 
+    @Override
+    public Long selectTokenTotal(FsTestReportParam param) {
+        return fsTestReportMapper.selectTokenTotal(param);
+    }
 
-
-}
+}

+ 7 - 0
fs-service/src/main/resources/mapper/his/FsTestReportAiLogMapper.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.fs.his.mapper.FsTestReportAiLogMapper">
+
+</mapper>