Quellcode durchsuchen

Merge remote-tracking branch 'origin/master'

lrj vor 3 Monaten
Ursprung
Commit
a4badabfc9

+ 5 - 0
src/main/java/cn/cslg/pas/common/model/dify/OAMessageDTO.java

@@ -16,4 +16,9 @@ public class OAMessageDTO {
 
     private Map<String,Object> inputs;
     private List<DifyFile> files;
+
+    @SerializedName("conversation_id")
+    private String conversationId;
+
+    private String  query;
 }

+ 13 - 0
src/main/java/cn/cslg/pas/common/model/dify/OAParamDTO.java

@@ -0,0 +1,13 @@
+package cn.cslg.pas.common.model.dify;
+
+import com.google.gson.annotations.SerializedName;
+import lombok.Data;
+
+@Data
+public class OAParamDTO {
+
+    private Integer confessionSessionId;
+
+    @SerializedName("patent_fileUrls")
+    private String patentFileUrls;
+}

+ 1 - 0
src/main/java/cn/cslg/pas/common/model/dify/confessionSession/UpdateConfessionSessionDTO.java

@@ -8,4 +8,5 @@ public class UpdateConfessionSessionDTO {
     private String inventionPoint;
     private String conversationName;
     private String conversationId;
+    private String content;
 }

+ 78 - 0
src/main/java/cn/cslg/pas/common/utils/MathUtils.java

@@ -8,6 +8,8 @@ import java.util.List;
  * @Date 2023/3/28
  */
 public class MathUtils {
+    private static final String[] DIGITS = {"零", "一", "二", "三", "四", "五", "六", "七", "八", "九"};
+    private static final String[] UNITS = {"", "十", "百", "千"};
 
     public static double saveTwoDecimal(double value) {
         BigDecimal bd = new BigDecimal(value);
@@ -63,4 +65,80 @@ public class MathUtils {
         }
         return min;
     }
+
+    public static String numberToChinese(int num) {
+        if (num == 0) {
+            return "零";
+        }
+
+        StringBuilder result = new StringBuilder();
+        String numStr = Integer.toString(num);
+        int length = numStr.length();
+        boolean prevZero = false; // 标记前一个字符是否为零
+
+        for (int i = 0; i < length; i++) {
+            int digit = numStr.charAt(i) - '0';
+            int unitIndex = length - i - 1; // 计算单位位置(从高位开始)
+
+            if (digit == 0) {
+                prevZero = true;
+            } else {
+                if (prevZero) {
+                    result.append(DIGITS[0]);
+                    prevZero = false;
+                }
+                result.append(DIGITS[digit]);
+                if (unitIndex > 0) { // 个位不添加单位
+                    result.append(UNITS[unitIndex]);
+                }
+                prevZero = false;
+            }
+        }
+
+        String str = result.toString();
+
+        // 处理十位为1的特殊情况(10-19)
+        if (str.startsWith("一十") && str.length() > 1) {
+            str = str.substring(1);
+        }
+
+        // 清理多余零
+        str = str.replaceAll("零+", "零");
+        if (str.endsWith("零")) {
+            str = str.substring(0, str.length() - 1);
+        }
+
+        return str;
+    }
+
+    public static String convertToChinese(int num) {
+        if (num < 0 || num > 100) {
+            return "";
+        }
+        if (num == 0) {
+            return "零";
+        }
+        if (num == 100) {
+            return "一百";
+        }
+
+        int ten = num / 10;
+        int unit = num % 10;
+        StringBuilder result = new StringBuilder();
+
+        // 处理十位
+        if (ten > 0) {
+            if (ten > 1) { // 十位大于1时需添加数字
+                result.append(DIGITS[ten]);
+            }
+            result.append("十");
+        }
+
+        // 处理个位
+        if (unit > 0 || ten == 0) { // 个位为0且十位存在时不处理,否则需添加
+            result.append(DIGITS[unit]);
+        }
+
+        return result.toString();
+    }
 }

+ 4 - 30
src/main/java/cn/cslg/pas/controller/outApi/DifyController.java

@@ -1,53 +1,27 @@
 package cn.cslg.pas.controller.outApi;
 
 import cn.cslg.pas.common.core.base.Constants;
-import cn.cslg.pas.common.model.dify.ChatMessageDTO;
-import cn.cslg.pas.common.model.dify.DifyChatMessageDTO;
-import cn.cslg.pas.common.model.dify.DifyHistoryMessageDTO;
-import cn.cslg.pas.common.model.dify.OAMessageDTO;
-import cn.cslg.pas.common.model.dify.GenerateClaimDTO;
+import cn.cslg.pas.common.model.dify.*;
 import cn.cslg.pas.common.utils.CacheUtils;
 import cn.cslg.pas.common.utils.LoginUtils;
 import cn.cslg.pas.common.utils.Response;
-import cn.cslg.pas.domain.business.ProjectTask;
-import cn.cslg.pas.domain.business.ReportProject;
-import cn.cslg.pas.domain.business.TaskCode;
-import cn.cslg.pas.service.business.ProjectTaskService;
-import cn.cslg.pas.service.business.ReportProjectService;
-import cn.cslg.pas.service.business.TaskCodeService;
-import cn.cslg.pas.service.business.es.EsPatentVectorService;
 import cn.cslg.pas.service.common.DifyService;
 import cn.cslg.pas.service.dify.DifySessionService;
 import cn.cslg.pas.service.dify.GenerateInstructionService;
 import com.alibaba.fastjson2.JSONObject;
-import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.google.gson.JsonObject;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
-import jakarta.servlet.http.HttpServletRequest;
-import jakarta.servlet.http.HttpServletResponse;
 import lombok.RequiredArgsConstructor;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
-import org.springframework.core.io.InputStreamResource;
 import org.springframework.http.MediaType;
-import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.context.request.RequestAttributes;
-import org.springframework.web.context.request.RequestContextHolder;
-import org.springframework.web.context.request.ServletRequestAttributes;
-import org.springframework.web.multipart.MultipartFile;
-import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
 import reactor.core.publisher.Flux;
 
 import java.io.IOException;
-import java.time.Duration;
-import java.time.LocalTime;
-import java.util.List;
 
 
 @SuppressWarnings({"all"})
@@ -105,10 +79,10 @@ public class DifyController {
         return Response.success(difyService.generateClaimExplain(generateClaimDTO));
     }
 
-    @RequestMapping(value = "/sendOADefense", method = RequestMethod.GET, produces = MediaType.TEXT_EVENT_STREAM_VALUE)
+    @RequestMapping(value = "/sendOADefense", method = RequestMethod.POST, produces = MediaType.TEXT_EVENT_STREAM_VALUE)
     @Operation(summary = "OA答辩")
-    public Flux<String> sendOADefense(Integer confessionSessionId) throws IOException {
-        return difyService.successGetOAHttp(confessionSessionId);
+    public Flux<String> sendOADefense(@RequestBody OAParamDTO paramDTO) throws IOException {
+        return difyService.successGetOAHttp1(paramDTO);
     }
 
     @RequestMapping(value = "/generateInstruction", method = RequestMethod.POST, produces = MediaType.TEXT_EVENT_STREAM_VALUE)

+ 228 - 4
src/main/java/cn/cslg/pas/service/common/DifyService.java

@@ -6,24 +6,30 @@ import cn.cslg.pas.common.model.dify.*;
 import cn.cslg.pas.common.model.dify.GenerateClaimDTO;
 import cn.cslg.pas.common.model.dify.confessionSession.AddConfessionSessionDTO;
 import cn.cslg.pas.common.model.dify.confessionSession.UpdateConfessionSessionDTO;
-import cn.cslg.pas.common.utils.CacheUtils;
+import cn.cslg.pas.common.utils.*;
 import cn.cslg.pas.common.utils.ClaimUtils.ClaimSplitUtils;
-import cn.cslg.pas.common.utils.DataUtils;
-import cn.cslg.pas.common.utils.DateUtils;
-import cn.cslg.pas.common.utils.LoginUtils;
 import cn.cslg.pas.common.vo.PatentRightParams;
+import cn.cslg.pas.domain.business.ReportTemple;
 import cn.cslg.pas.domain.business.TechnicalCase;
+import cn.cslg.pas.domain.dify.AssoConfessionSessionFile;
 import cn.cslg.pas.domain.dify.ConfessionSession;
 import cn.cslg.pas.domain.report.AssoProjectConfession;
+import cn.cslg.pas.exception.ExceptionEnum;
+import cn.cslg.pas.exception.XiaoShiException;
 import cn.cslg.pas.mapper.dify.ConfessionSessionMapper;
+import cn.cslg.pas.service.business.ReportTempleService;
 import cn.cslg.pas.service.business.TechnicalCaseService;
 import cn.cslg.pas.service.dify.ConfessionSessionService;
 import cn.cslg.pas.service.dify.DifySessionService;
+import cn.cslg.pas.service.dify.GenerateInstructionService;
 import cn.cslg.pas.service.report.AssoProjectConfessionService;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.deepoove.poi.XWPFTemplate;
+import com.deepoove.poi.config.Configure;
+import com.deepoove.poi.config.ConfigureBuilder;
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.gson.Gson;
@@ -31,13 +37,17 @@ import com.google.gson.GsonBuilder;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import okhttp3.*;
+import okhttp3.Response;
 import org.apache.commons.lang3.ObjectUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.ddr.poi.html.HtmlRenderPolicy;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Lazy;
 import org.springframework.http.HttpHeaders;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
 import org.w3c.dom.DocumentType;
 import reactor.core.publisher.Flux;
 
@@ -81,6 +91,15 @@ public class DifyService {
     private final ConfessionSessionService confessionSessionService;
     @Autowired
     private ConfessionSessionMapper confessionSessionMapper;
+    @Autowired
+    private ReportTempleService templeService;
+    @Autowired
+    private FileUtils fileUtils;
+    @Autowired
+    private FileManagerService fileManagerService;
+    @Autowired
+    @Lazy
+    private GenerateInstructionService generateInstructionService;
 
     /**
      * 调用文件系统删除文件接口
@@ -482,6 +501,211 @@ public class DifyService {
         });
     }
 
+    public Flux<String> successGetOAHttp1(OAParamDTO vo) {
+        Integer confessionSessionId = vo.getConfessionSessionId();
+        String patentFileUrls = vo.getPatentFileUrls();
+        ConfessionSession confessionSession = confessionSessionService.getById(confessionSessionId);
+        if (ObjectUtils.isEmpty(confessionSession)) {
+            throw new XiaoShiException(ExceptionEnum.BUSINESS_ERROR, "未查询到OA答辩记录");
+        }
+        String conversationId = confessionSession.getConversationId();
+        String sessionContent = confessionSession.getContent();
+        String conversationName = confessionSession.getConversationName();
+        String fileUrl = fileDownloadUrl + confessionSession.getGuid();
+        String userId = loginUtils.getId().toString();
+
+        OkHttpClient client = new OkHttpClient.Builder()
+                .connectTimeout(600, TimeUnit.SECONDS)
+                .writeTimeout(600, TimeUnit.SECONDS)
+                .readTimeout(600, TimeUnit.SECONDS)
+                .build();
+
+        Map<String, Object> map = new HashMap<>();
+        map.put("fileUrl", fileUrl);
+        map.put("patent_fileUrls",patentFileUrls);
+
+        OAMessageDTO oaMessageDTO = new OAMessageDTO();
+        oaMessageDTO.setInputs(map);
+        oaMessageDTO.setResponseMode("streaming");
+        oaMessageDTO.setUser(userId);
+        oaMessageDTO.setQuery("OA答辩");
+        oaMessageDTO.setConversationId(conversationId);
+        oaMessageDTO.setFiles(new ArrayList<>());
+
+        String param = new Gson().toJson(oaMessageDTO);
+        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), param);
+        Request request = new Request.Builder()
+                .url(url + "chat-messages")
+                .addHeader("Authorization", "Bearer " + OAApiKey)
+                .addHeader(HttpHeaders.CONTENT_TYPE, "application/json")
+                .post(requestBody)
+                .build();
+
+
+        return Flux.create(emitter -> {
+            client.newCall(request).enqueue(new Callback() {
+
+                @Override
+                public void onFailure(Call call, IOException e) {
+                    emitter.error(e);
+                }
+
+                @Override
+                public void onResponse(Call call, Response response) throws IOException {
+
+                    if (!response.isSuccessful()) {
+                        emitter.error(new IOException("Unexpected code: " + response));
+                        return;
+                    }
+
+                    try (Reader reader = response.body().charStream()) {
+                        BufferedReader bufferedReader = new BufferedReader(reader);
+                        String line;
+                        String prefixToRemove = "data: ";
+                        String runId = conversationId;
+                        while ((line = bufferedReader.readLine()) != null) {
+                            if (line.isEmpty()) {
+                                continue;
+                            }
+                            if (line.startsWith(prefixToRemove)) {
+                                line = line.substring(prefixToRemove.length());
+                            }
+                            try {
+                                JSONObject jsonObject = JSON.parseObject(line);
+                                String sessionConversationId  = jsonObject.get("conversation_id").toString();
+                                String event = jsonObject.get("event").toString();
+                                if (StringUtils.isEmpty(runId)) {
+                                    if (StringUtils.isNotEmpty(sessionConversationId)) {
+                                        runId = sessionConversationId;
+                                        confessionSessionMapper.updateSingleField(confessionSessionId, "conversation_id", sessionConversationId);
+                                    }
+                                }
+                                if (event.equals("message")) {
+                                    String data = jsonObject.get("answer").toString();
+                                    JSONObject dataObject = null;
+                                    if (StringUtils.isNotEmpty(data)) {
+                                        try {
+                                            dataObject = JSON.parseObject(data);
+                                            String code = dataObject.get("code").toString();
+                                            if (StringUtils.equals(code, "200")) {
+                                                JSONObject object = dataObject.getJSONObject("data");
+                                                generateDoc(conversationName, confessionSessionId, object);
+                                            }
+                                        } catch (Exception e) {
+
+                                        }
+                                    }
+                                    if (ObjectUtils.isNotEmpty(dataObject)) {
+                                        JSONObject obj;
+                                        if (StringUtils.isNotEmpty(sessionContent)) {
+                                            obj = JSON.parseObject(sessionContent);
+                                            obj.put("data", dataObject);
+                                        } else {
+                                            obj = new JSONObject();
+                                            obj.put("data", dataObject);
+                                        }
+                                        confessionSessionMapper.updateSingleField(confessionSessionId, "content", obj.toString());
+                                    }
+                                }
+                            } catch (Exception e) {
+                            }
+                            emitter.next(line); // 将每行数据发送到 Flux
+                        }
+                    } catch (IOException e) {
+                        emitter.error(e);
+                    } finally {
+                        emitter.complete();
+                    }
+                }
+            });
+        });
+    }
+
+    public void generateDoc(String conversationName,Integer confessionSessionId,JSONObject object) {
+        Map<String, Object> map = new HashMap<>();
+        String reason = object.getString("reason");
+        String num = object.getString("num");
+        String claimChange = object.getString("claim_change");
+        List<String> claimList = new ArrayList<>();
+        if (StringUtils.isNotEmpty(claimChange)) {
+            claimList = Arrays.asList(claimChange.split("\n"));
+        }
+
+        JSONArray jsonArray = object.getJSONArray("defense_opinion");
+        // 创建List集合
+        List<String> list = new ArrayList<>();
+        // 遍历JSONArray并添加元素到List
+        if (!CollectionUtils.isEmpty(jsonArray)) {
+            for (Object o : jsonArray) {
+                String str = o.toString();
+                if (StringUtils.isNotEmpty(str)) {
+                    String[] split1 = str.split("\n");
+                    list.addAll(Arrays.asList(split1));
+                }
+            }
+        }
+        map.put("num", num);
+        map.put("reason", reason);
+        map.put("claim_change", claimList);
+        map.put("defense_opinion", list);
+
+        ReportTemple reportTemplate = templeService.getById(20);
+        String templateFilePath = fileUtils.getPath(reportTemplate.getTemplatePath());
+        //更新会话
+        String name = DateUtils.dateTimeToStr(new Date(), "yyyyMMdd");
+        String finalName = name + "-" + conversationName + "-陈述意见书";
+        //生成文档
+        String fileGuid = null;
+        try {
+            fileGuid = this.generateOADefenseFile(map, templateFilePath, finalName);
+            AssoConfessionSessionFile assoConfessionSessionFile = new AssoConfessionSessionFile();
+            assoConfessionSessionFile.setGuid(fileGuid);
+            assoConfessionSessionFile.setConfessionSessionId(confessionSessionId);
+            assoConfessionSessionFile.insert();
+        } catch (Exception e) {
+            throw new XiaoShiException(ExceptionEnum.BUSINESS_ERROR, "加载陈述意见书失败");
+        }
+    }
+
+    /**
+     * 生成OA答辩文件
+     *
+     * @param map
+     * @param templateFilePath
+     * @param name
+     * @return
+     * @throws Exception
+     */
+    public String generateOADefenseFile(Map<String, Object> map, String templateFilePath, String name) throws Exception {
+        XWPFTemplate xwpfTemplate = this.getHtmlTemplate(map, templateFilePath);
+        String fileName = name + ".docx";
+        String directoryName = fileUtils.createDirectory();
+        String outPath = fileUtils.getSavePath(directoryName) + fileName;
+        File file = new File(outPath);
+        // 生成word保存在指定目录
+        xwpfTemplate.writeToFile(outPath);
+        xwpfTemplate.close();
+        List<String> ids = fileManagerService.uploadFileGetGuid2(Collections.singletonList(file));
+        if (CollectionUtils.isEmpty(ids)) {
+            throw new XiaoShiException("保存记录失败");
+        }
+        return ids.get(0);
+    }
+
+    private XWPFTemplate getHtmlTemplate(Map<String, Object> map, String filePath) {
+        XWPFTemplate template = null;
+        try {
+            HtmlRenderPolicy htmlRenderPolicy = new HtmlRenderPolicy();
+            ConfigureBuilder configureBuilder = Configure.builder();
+            configureBuilder.bind("#this", htmlRenderPolicy);
+            Configure configure = configureBuilder.build();
+            template = XWPFTemplate.compile(filePath, configure).render(map);
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new XiaoShiException(ExceptionEnum.BUSINESS_ERROR, "未匹配到模版文件");
+        }
+        return template;
+    }
 
     /**
      * 调用质检

+ 1 - 0
src/main/java/cn/cslg/pas/service/dify/ConfessionSessionService.java

@@ -67,6 +67,7 @@ public class ConfessionSessionService extends ServiceImpl<ConfessionSessionMappe
         String id = loginUtils.getId().toString();
         confessionSession.setCreateId(id);
         confessionSession.setType(addConfessionSessionDTO.getType());
+        confessionSession.setContent(addConfessionSessionDTO.getContent());
         confessionSession.insert();
         return confessionSession.getId();
     }

+ 1 - 1
src/main/resources/application-dev.yml

@@ -88,7 +88,7 @@ management:
 DIFY:
   apiKey: app-DDGJt4QUmzlc2aFQ5voOAXIj
 #  cliamKey: app-fxpiWOYqtJM1BOaJnG54NlCZ
-  OAApiKey: app-mmfvVywt7wdS8HofrYpFy4RL
+  OAApiKey: app-NvKwdHvEK2UmJdmjTGDR0xu6
   checkApiKey: aa
   cliamKey: app-jF3akhYKgljPLdpeIpTNbs6f
   gInstructionKey: app-YfoUDlED4oJNO9hVk6hfdKSw

+ 1 - 1
src/main/resources/application-testNetIn.yml

@@ -80,5 +80,5 @@ WDSYS:
   password: Lqftiu807005
 DIFY:
   apiKey: app-DDGJt4QUmzlc2aFQ5voOAXIj
-  OAApiKey: app-mmfvVywt7wdS8HofrYpFy4RL
+  OAApiKey: app-NvKwdHvEK2UmJdmjTGDR0xu6
   url: https://ai.xsip.cn/v1/

BIN
src/main/resources/file/reportTemple/defense.docx