TakaiAiServiceImpl.java 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075
  1. package com.takai.ai.service.impl;
  2. import com.alibaba.fastjson2.JSON;
  3. import com.alibaba.fastjson2.JSONArray;
  4. import com.alibaba.fastjson2.JSONObject;
  5. import com.takai.ai.domain.TakaiSysOss;
  6. import com.takai.ai.domain.dto.TakaiDialogDetailReqDTO;
  7. import com.takai.ai.domain.entity.*;
  8. import com.takai.ai.mapper.*;
  9. import com.takai.ai.service.ITakaiAiService;
  10. import com.takai.ai.utils.MinioUtil;
  11. import com.takai.ai.utils.SnowflakeDigitGenerator;
  12. import com.takai.common.annotation.DataSource;
  13. import com.takai.common.config.DeepseekConfig;
  14. import com.takai.common.core.redis.RedisCache;
  15. import com.takai.common.enums.DataSourceType;
  16. import com.takai.ai.domain.dto.TakaiDialogReqDTO;
  17. import com.takai.ai.domain.dto.TakaiDialogRespDTO;
  18. import com.takai.ai.domain.dto.TakaiQuestionDTO;
  19. import com.takai.common.utils.StringUtils;
  20. import com.takai.common.utils.uuid.IdUtils;
  21. import lombok.extern.slf4j.Slf4j;
  22. import okhttp3.*;
  23. import okhttp3.sse.EventSource;
  24. import okhttp3.sse.EventSourceListener;
  25. import okhttp3.sse.EventSources;
  26. import org.slf4j.Logger;
  27. import org.slf4j.LoggerFactory;
  28. import org.springframework.beans.factory.annotation.Autowired;
  29. import org.springframework.data.redis.core.RedisTemplate;
  30. import org.springframework.stereotype.Service;
  31. import org.springframework.web.multipart.MultipartFile;
  32. import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
  33. import javax.validation.constraints.NotNull;
  34. import java.io.IOException;
  35. import java.time.LocalDate;
  36. import java.time.LocalDateTime;
  37. import java.time.temporal.ChronoUnit;
  38. import java.util.*;
  39. import java.util.concurrent.TimeUnit;
  40. import java.util.stream.Collectors;
  41. /**
  42. * 高井 业务层处理
  43. *
  44. * @author takai
  45. */
  46. @Slf4j
  47. @Service
  48. @DataSource(DataSourceType.MASTER)
  49. public class TakaiAiServiceImpl implements ITakaiAiService {
  50. private static final Logger logger = LoggerFactory.getLogger(TakaiAiServiceImpl.class);
  51. @Autowired
  52. private TakaiDialogMapper takaiDialogMapper;
  53. @Autowired
  54. private TakaiApplicationMapper takaiApplicationMapper;
  55. @Autowired
  56. private TakaiQuestionMapper takaiQuestionMapper;
  57. @Autowired
  58. private TakaiKnowledgeMapper takaiKnowledgeMapper;
  59. @Autowired
  60. private TakaiDocumentMapper takaiDocumentMapper;
  61. @Autowired
  62. private TakaiSysOssConfigMapper takaiSysOssConfigMapper;
  63. @Autowired
  64. private TakaiSysOssMapper takaiSysOssMapper;
  65. @Autowired
  66. private MinioUtil minioUtil;
  67. @Autowired
  68. private TakaiDocumentSettingsMapper takaiDocumentSettingsMapper;
  69. @Autowired
  70. private DeepseekConfig deepseekConfig;
  71. @Autowired
  72. private TakaiAppInfoMapper takaiAppInfoMapper;
  73. @Autowired
  74. private TakaiMediaReplacementMapper takaiMediaReplacementMapper;
  75. @Autowired
  76. private RedisCache redisCache;
  77. @Autowired
  78. public RedisTemplate redisTemplate;
  79. public static final String START_SIGN = "【";
  80. public static final String END_SIGN = "】";
  81. public static final String SYMBOL = "【示意图序号";
  82. public static final String USER_STR = "user";
  83. public static final String TODAY = "今天";
  84. public static final String BEFOR = "天前";
  85. @Override
  86. public void initDeepseekInfo() {
  87. //获取应用列表
  88. // List<TakaiApplication> appList = takaiApplicationMapper.selectApplicationList(null);
  89. setRedisCache(null);
  90. }
  91. private void setRedisCache(String knowledgeId) {
  92. //获取知识库列表
  93. List<TakaiKnowledge> knowledgeList = null;
  94. if (StringUtils.isEmpty(knowledgeId)) {
  95. knowledgeList = takaiKnowledgeMapper.selectKnowledgeList(null);
  96. } else {
  97. knowledgeList = takaiKnowledgeMapper.selectKnowledgeList(TakaiKnowledge.builder().knowledgeId(knowledgeId).build());
  98. }
  99. //获取知识列表
  100. if (!knowledgeList.isEmpty()) {
  101. for (TakaiKnowledge bmKnowledge : knowledgeList) {
  102. TakaiDocument conditon = TakaiDocument.builder().knowledgeId(bmKnowledge.getKnowledgeId()).build();
  103. List<TakaiDocument> documentList = takaiDocumentMapper.selectDocumentList(conditon);
  104. if (!documentList.isEmpty()) {
  105. for (TakaiDocument bmDocument : documentList) {
  106. //获取知识图片列表
  107. TakaiMediaReplacement conditonR = TakaiMediaReplacement.builder().documentId(bmDocument.getDocumentId()).build();
  108. List<TakaiMediaReplacement> imageList = takaiMediaReplacementMapper.selectMediaList(conditonR);
  109. if (!imageList.isEmpty()) {
  110. for (TakaiMediaReplacement image : imageList) {
  111. redisCache.setCacheObject(image.getOriginText(), image.getMediaUrl());
  112. }
  113. }
  114. }
  115. }
  116. }
  117. }
  118. }
  119. @Override
  120. public SseEmitter sseInvoke(TakaiSseInfoParams sseParams) {
  121. TakaiApplication appInfo = takaiApplicationMapper.selectTargetApplication(TakaiApplication.builder().appId(sseParams.getAppId()).build());
  122. if (appInfo != null) {
  123. TakaiKnowledge knowledge = takaiKnowledgeMapper.selectTargetKnowledge(TakaiKnowledge.builder().knowledgeId(appInfo.getKnowledgeIds()).build());
  124. if (knowledge != null) {
  125. SseEmitter sseEmitter = new SseEmitter(0L);
  126. String url = deepseekConfig.getBaseurl() + deepseekConfig.getChat();
  127. TakaiAppInfo info = takaiAppInfoMapper.selectAppInfoByAppId(sseParams.getAppId());
  128. JSONObject json = JSONObject.parseObject(info.getAppInfo());
  129. String query = sseParams.getPrompt().get(sseParams.getPrompt().size() - 1).getContent();
  130. log.info("deepseek聊天请求参数:" + query);
  131. json.put("query", query);
  132. json.put("embeddingId", knowledge.getEmbeddingId());
  133. RequestBody requestBody = FormBody.create(MediaType.parse("application/json; charset=utf-8"), json.toJSONString());
  134. Request request = buildPostRequest(url, requestBody);
  135. // 使用EventSourceListener处理来自服务器的SSE事件
  136. EventSourceListener listener = new EventSourceListener() {
  137. private String preData = "";
  138. //图片识别
  139. private String symbolData = "";
  140. @Override
  141. public void onOpen(@NotNull EventSource eventSource, @NotNull Response response) {
  142. log.info("deepseek Connection opened. Headers: {}", response.headers());
  143. log.info("deepseek Request URL: {}", response.request().url());
  144. }
  145. @Override
  146. public void onClosed(@NotNull EventSource eventSource) {
  147. log.info("deepseek Connection closed.");
  148. sseEmitter.complete();
  149. }
  150. @Override
  151. public void onEvent(@NotNull EventSource eventSource, String id, String type, @NotNull String data) {
  152. if (!StringUtils.isEmpty(data)) {
  153. String newData = data.substring(preData.length());
  154. if (com.takai.common.utils.StringUtils.isNotEmpty(type) && "finish".equals(type)) {
  155. logger.info("deepseek聊天结束", newData);
  156. }
  157. logger.info("deepseek聊天返回消息:" + data);
  158. preData = data;
  159. if (newData.indexOf(START_SIGN) > -1 || symbolData.length() > 0) {
  160. symbolData = symbolData + newData;
  161. //接收到了完整的图片标识
  162. if (newData.indexOf(END_SIGN) > -1) {
  163. //是图片
  164. if (symbolData.indexOf(SYMBOL) > -1) {
  165. //获取完整Key
  166. int startPos = symbolData.indexOf(START_SIGN);
  167. int endPos = symbolData.indexOf(END_SIGN) + 1;
  168. String key = symbolData.substring(startPos, endPos);
  169. String url = redisCache.getCacheObject(key);
  170. if (org.springframework.util.StringUtils.hasText(url)) {
  171. if(url.contains("https://10.1.28.14:9000")){
  172. url = url.replace("https://10.1.28.14:9000", "https://minio.jkec.info:9000");
  173. }
  174. //匹配markdown图片格式
  175. symbolData = symbolData.replace(key, "![](" + url + ")");
  176. }
  177. }
  178. JSONObject json = new JSONObject();
  179. json.put("id", id);
  180. json.put("event", "add");
  181. long count = symbolData.chars().filter(c -> c == '【').count();
  182. if(count > 1){
  183. int firstPos = symbolData.indexOf(START_SIGN);
  184. if (firstPos != -1) {
  185. // 从第一个 '【' 的下一个位置开始查找第二个 '【'
  186. int secondPos = symbolData.indexOf(START_SIGN, firstPos + 1);
  187. if (secondPos != -1) {
  188. // 从第二个 '【' 开始截取
  189. json.put("data", symbolData.substring(0, secondPos));
  190. symbolData = symbolData.substring(secondPos);
  191. logger.info("截取结果{}: " + symbolData);
  192. }
  193. }
  194. }else{
  195. json.put("data", symbolData);
  196. symbolData = "";
  197. }
  198. log.info("deepseek返回信息:" + json);
  199. send(sseEmitter, json);
  200. }
  201. } else {
  202. JSONObject json = new JSONObject();
  203. json.put("id", id);
  204. json.put("event", type);
  205. json.put("data", newData);
  206. log.info("deepseek返回信息:" + json);
  207. send(sseEmitter, json);
  208. }
  209. }
  210. }
  211. @Override
  212. public void onFailure(@NotNull EventSource eventSource, Throwable t, Response response) {
  213. if (response != null) {
  214. log.error("deepseek Connection failed. Response code: {}, message: {}, body: {}",
  215. response.code(), response.message(), response.body() != null ? response.body().toString() : "null");
  216. } else {
  217. log.error("deepseek Connection failed with no response", t);
  218. }
  219. sseEmitter.completeWithError(t);
  220. }
  221. private void send(SseEmitter sseEmitter, Object obj) {
  222. try {
  223. sseEmitter.send(obj);
  224. } catch (IOException e) {
  225. log.error("deepseek 推送数据失败", e);
  226. }
  227. }
  228. };
  229. OkHttpClient client = buildOkHttpClient();
  230. EventSource.Factory factory = EventSources.createFactory(client);
  231. final EventSource eventSources = factory.newEventSource(request, listener);
  232. // 客户端主动关闭连接
  233. sseEmitter.onCompletion(() -> {
  234. logger.info("deepseek客户端主动关闭连接 -- SSE 连接关闭");
  235. });
  236. // 超时回调
  237. sseEmitter.onTimeout(() -> {
  238. logger.info("deepseek客户端连接超时 -- SSE 连接关闭");
  239. if(eventSources != null) {
  240. logger.info("deepseek超时回调 -- 成功关闭SSE连接 ");
  241. eventSources.cancel();
  242. }
  243. });
  244. // 错误回调
  245. sseEmitter.onError(e -> {
  246. logger.info("deepseek客户端回调失败 -- SSE 连接关闭");
  247. if(eventSources != null) {
  248. logger.info("deepseek错误回调 -- 成功关闭SSE连接 ");
  249. eventSources.cancel();
  250. }
  251. });
  252. return sseEmitter;
  253. }
  254. }
  255. return null;
  256. }
  257. @Override
  258. public List<String> asyncCompletions(TakaiCompletionsParams params) {
  259. List<String> list = new ArrayList<>();
  260. TakaiApplication vo = takaiApplicationMapper.selectTargetApplication(TakaiApplication.builder().appId(params.getAppId()).build());
  261. if (vo != null) {
  262. TakaiPromptInfo info = new TakaiPromptInfo();
  263. info.setContent(deepseekConfig.getPrompt());
  264. info.setRole("user");
  265. params.getMessages().add(info);
  266. JSONObject jsonObject = new JSONObject();
  267. jsonObject.put("model", vo.getModel());
  268. jsonObject.put("messages", params.getMessages());
  269. JSONObject result = getasyncCompletions(jsonObject);
  270. if (result != null && result.getInteger("code") == 200) {
  271. JSONArray array = result.getJSONArray("data");
  272. list.addAll(array.toJavaList(String.class));
  273. }
  274. }
  275. return list;
  276. }
  277. private JSONObject getasyncCompletions(JSONObject jsonObject) {
  278. String url = deepseekConfig.getBaseurl() + deepseekConfig.getAsyncCompletions();
  279. RequestBody requestBody = FormBody.create(MediaType.parse("application/json; charset=utf-8"), jsonObject.toJSONString());
  280. Request request = buildPostRequest(url, requestBody);
  281. OkHttpClient client = buildOkHttpClient();
  282. try {
  283. Response response = client.newCall(request).execute();
  284. if (response.isSuccessful()) {
  285. String body = response.body().string();
  286. if (isJsonObject(body)) {
  287. return JSON.parseObject(body);
  288. }
  289. }
  290. } catch (IOException e) {
  291. log.info("deepseek Request URL: {}", e.getMessage());
  292. }
  293. return null;
  294. }
  295. @Override
  296. public List<TakaiDialogRespDTO> getDialogDetail(String dialogId) {
  297. List<TakaiDialogRespDTO> detail = takaiDialogMapper.selectDialogDetail(dialogId);
  298. return detail;
  299. }
  300. @Override
  301. public void saveDialog(TakaiDialogReqDTO dialogReqDTO) {
  302. String dialogId = null;
  303. TakaiDialogRespDTO dialogVo = null;
  304. if (com.takai.common.utils.StringUtils.isNotEmpty(dialogReqDTO.getId())) {
  305. dialogVo = takaiDialogMapper.selectDialogById(dialogReqDTO.getId());
  306. }
  307. if (dialogVo == null) {
  308. takaiDialogMapper.insertDialog(dialogReqDTO);
  309. dialogId = dialogReqDTO.getId();
  310. } else {
  311. dialogId = dialogReqDTO.getId();
  312. }
  313. for (TakaiDialogDetailReqDTO dto : dialogReqDTO.getMessages()) {
  314. dto.setDialogId(dialogId);
  315. TakaiDialogRespDTO detail = takaiDialogMapper.selectDialogDetailById(dto.getId());
  316. if (detail == null) {
  317. takaiDialogMapper.insertDialogDetail(dto);
  318. }
  319. }
  320. }
  321. @Override
  322. public List<Object> getDialogList(String appId) {
  323. List<TakaiDialogRespDTO> dialogRespDTOS = takaiDialogMapper.selectDialogList(appId);
  324. //当前日期
  325. LocalDate today = LocalDate.now();
  326. //根据时间分组降序排序
  327. NavigableMap<LocalDateTime, List<TakaiDialogRespDTO>> groupList = dialogRespDTOS.stream().collect(Collectors.groupingBy(TakaiDialogRespDTO::getCreate_time, TreeMap::new, Collectors.toList())).descendingMap();
  328. List<Object> obj = new ArrayList();
  329. if (!groupList.isEmpty()) {
  330. for (Map.Entry<LocalDateTime, List<TakaiDialogRespDTO>> entry : groupList.entrySet()) {
  331. // 对比两个时间相差多少天
  332. long daysBetween = ChronoUnit.DAYS.between(today, entry.getKey());
  333. Map<String, Object> m = new HashMap();
  334. m.put("key", System.nanoTime());
  335. m.put("type", "group");
  336. if (daysBetween == 0l) {
  337. m.put("label", TODAY);
  338. m.put("children", setValue(entry.getValue()));
  339. obj.add(m);
  340. } else {
  341. m.put("label", -daysBetween + BEFOR);
  342. m.put("children", setValue(entry.getValue()));
  343. obj.add(m);
  344. }
  345. }
  346. }
  347. return obj;
  348. }
  349. @Override
  350. public void DelDialogDetail(String id) {
  351. int result = takaiDialogMapper.delDialog(id);
  352. if (result > 0) {
  353. takaiDialogMapper.delDialogDetail(id);
  354. }
  355. }
  356. @Override
  357. public void updateDialog(TakaiDialogReqDTO dialogReqDTO) {
  358. takaiDialogMapper.updateDialog(dialogReqDTO);
  359. }
  360. @Override
  361. public List<TakaiDialogRespDTO> exportExcel(String dialogId) {
  362. return takaiDialogMapper.selectDialogExport(dialogId);
  363. }
  364. @Override
  365. public int createKnowledge(TakaiKnowledgeParams knowledge) {
  366. SnowflakeDigitGenerator snowflakeDigitGenerator = new SnowflakeDigitGenerator(1, 1);
  367. long knowledgeId = snowflakeDigitGenerator.nextId();
  368. String url = deepseekConfig.getBaseurl() + deepseekConfig.getCreateKnowledge();
  369. JSONObject jsonObject = new JSONObject();
  370. jsonObject.put("knowledge_id", "a" + knowledgeId);
  371. jsonObject.put("embedding_id", knowledge.getEmbeddingId());
  372. RequestBody requestBody = FormBody.create(MediaType.parse("application/json; charset=utf-8"), jsonObject.toJSONString());
  373. Request request = buildPostRequest(url, requestBody);
  374. OkHttpClient client = buildOkHttpClient();
  375. try {
  376. Response response = client.newCall(request).execute();
  377. if (response.isSuccessful()) {
  378. String body = response.body().string();
  379. JSONObject obj = JSON.parseObject(body);
  380. Integer code = obj.getInteger("code");
  381. if (code == 200) {
  382. TakaiKnowledge params = TakaiKnowledge.builder()
  383. .knowledgeId("a" + knowledgeId)
  384. .name(knowledge.getName())
  385. .embeddingId(knowledge.getEmbeddingId())
  386. .description(knowledge.getDescription())
  387. .build();
  388. return takaiKnowledgeMapper.insertKnowledge(params);
  389. }
  390. } else {
  391. logger.error("创建知识库调用python接口失败,返回状态码:{}", response.code());
  392. }
  393. } catch (IOException e) {
  394. logger.error("创建知识库调用python接口失败", e.getMessage());
  395. }
  396. return 0;
  397. }
  398. @Override
  399. public int updateKnowledge(TakaiKnowledgeParams knowledge, String knowledgeId) {
  400. TakaiKnowledge params = TakaiKnowledge.builder().knowledgeId(knowledgeId)
  401. .name(knowledge.getName())
  402. .description(knowledge.getDescription())
  403. .embeddingId(knowledge.getEmbeddingId())
  404. .build();
  405. return takaiKnowledgeMapper.updateKnowledge(params);
  406. }
  407. @Override
  408. public TakaiKnowledge detailKnowledge(String knowledgeId) {
  409. TakaiKnowledge vo = TakaiKnowledge.builder().knowledgeId(knowledgeId).build();
  410. return takaiKnowledgeMapper.selectTargetKnowledge(vo);
  411. }
  412. @Override
  413. public int delKnowledge(String knowledgeId) {
  414. String url = deepseekConfig.getBaseurl() + deepseekConfig.getDeleteKnowledge() + "/" + knowledgeId;
  415. Request request = buildDeleteRequest(url);
  416. OkHttpClient client = buildOkHttpClient();
  417. Response response = null;
  418. try {
  419. response = client.newCall(request).execute();
  420. if (response.isSuccessful()) {
  421. String body = response.body().string();
  422. JSONObject obj = JSON.parseObject(body);
  423. Integer code = obj.getInteger("code");
  424. if (code == 200) {
  425. // TakaiKnowledge info = takaiKnowledgeMapper.selectTargetKnowledge(TakaiKnowledge.builder().knowledgeId(knowledgeId).build());
  426. List<TakaiMediaReplacement> list = takaiMediaReplacementMapper.selectMediaList(TakaiMediaReplacement.builder().knowledgeId(knowledgeId).build());
  427. List<TakaiDocument> documentList = takaiDocumentMapper.selectDocumentList(TakaiDocument.builder().knowledgeId(knowledgeId).build());
  428. int knowledgeInt = takaiKnowledgeMapper.delKnowledge(knowledgeId);
  429. if (knowledgeInt > 0) {
  430. takaiDocumentMapper.delDocumentByKnowledgeId(knowledgeId);
  431. takaiDocumentSettingsMapper.deleteDocumentSettingsByKnowledgeId(knowledgeId);
  432. if(list != null && list.size() > 0){
  433. try {
  434. for(TakaiDocument document: documentList){
  435. logger.info("删除知识库->删除minio文件");
  436. minioUtil.remove("deepseek-doc", "/"+document.getName());
  437. }
  438. for (int k = 0; k < list.size(); k++ ) {
  439. logger.info("删除知识库->删除minio图片{}", k);
  440. String key = "/pdf/"+knowledgeId+"/"+list.get(k).getDocumentId()+"/【示意图序号_"+list.get(k).getDocumentId()+"_"+k+1+"】.jpg";
  441. logger.info("删除知识库->删除minio图片key{}", key);
  442. minioUtil.remove("deepseek-doc" , key);
  443. }
  444. } catch (Exception e) {
  445. logger.error("删除知识库->删除minion文件失败", e.getMessage());
  446. throw new RuntimeException(e);
  447. }
  448. }
  449. }
  450. return knowledgeInt;
  451. }
  452. }
  453. } catch (IOException e) {
  454. logger.error("删除知识库调用python接口失败", e.getMessage());
  455. }
  456. return 0;
  457. }
  458. @Override
  459. public int uploadDocument(MultipartFile[] files, String knowledgeId) {
  460. List<TakaiSysOss> result = null;
  461. try {
  462. result = minioUtil.uploadMultiple(files);
  463. } catch (Exception e) {
  464. logger.error("上传文件失败", e.getMessage());
  465. }
  466. if (result != null && result.size() > 0) {
  467. List<UploadDocumentParams> params = new ArrayList<>();
  468. for (int i = 0; i<result.size(); i++) {
  469. SnowflakeDigitGenerator documentIdGenerator = new SnowflakeDigitGenerator(i, i);
  470. long documentId = documentIdGenerator.nextId();
  471. UploadDocumentParams param = new UploadDocumentParams();
  472. param.setDocument_id("a" + documentId);
  473. param.setName(result.get(i).getOriginalName());
  474. param.setUrl(result.get(i).getUrl());
  475. params.add(param);
  476. }
  477. TakaiDocumentSettings settings = new TakaiDocumentSettings();
  478. settings.setKnowledgeId(knowledgeId);
  479. JSONObject jsonObject = analysisFile(params, settings, "upload");
  480. if (jsonObject != null && jsonObject.containsKey("code") && jsonObject.getInteger("code") == 200) {
  481. for (UploadDocumentParams vo : params) {
  482. TakaiKnowledge info = takaiKnowledgeMapper.selectTargetKnowledge(TakaiKnowledge.builder().knowledgeId(knowledgeId).build());
  483. if(info != null){
  484. // 更新知识库文件大小,总字符数, 文件总数
  485. JSONObject docInfo = jsonObject.getJSONObject("doc_info");
  486. if(docInfo != null){
  487. List<TakaiDocument> documentList = takaiDocumentMapper.selectDocumentList(TakaiDocument.builder().knowledgeId(knowledgeId).build());
  488. Integer fileLen = docInfo.getInteger("file_size");
  489. Integer wordNum = docInfo.getInteger("total_char_len");
  490. Integer sliceTotal = docInfo.getInteger("slice_num");
  491. TakaiKnowledge paramInfo = TakaiKnowledge.builder().knowledgeId(knowledgeId)
  492. .length(info.getLength() == null ? fileLen : info.getLength() + fileLen ) // 文件大小
  493. .wordNum(info.getWordNum() == null ? wordNum : info.getWordNum() + wordNum) // 总字符数
  494. .documentSize(documentList == null || documentList.size() == 0 ? 1 : documentList.size() + 1) // 文件总数
  495. .build();
  496. takaiKnowledgeMapper.updateKnowledge(paramInfo);
  497. for (TakaiSysOss oss : result) {
  498. // oss保存到数据库
  499. takaiSysOssMapper.insertSysOss(oss);
  500. }
  501. // 保存知识信息
  502. TakaiDocument document = TakaiDocument.builder()
  503. .documentId(vo.getDocument_id())
  504. .knowledgeId(knowledgeId)
  505. .customSeparator(String.format("[\"%s\"", "\\n") + "]")
  506. .sentenceSize("300")
  507. .name(vo.getName())
  508. .url(vo.getUrl())
  509. .sliceTotal(sliceTotal)
  510. .length(fileLen)
  511. .wordNum(wordNum)
  512. .build();
  513. int documentIdInsert = takaiDocumentMapper.insertDocument(document);
  514. if (documentIdInsert > 0) {
  515. // 保存知识设置信息
  516. SnowflakeDigitGenerator snowflakeDigitGenerator = new SnowflakeDigitGenerator(1, 1);
  517. long id = snowflakeDigitGenerator.nextId();
  518. TakaiDocumentSettings dSettings = new TakaiDocumentSettings();
  519. dSettings.setId(String.valueOf(id));
  520. dSettings.setKnowledgeId(knowledgeId);
  521. dSettings.setDocumentId(vo.getDocument_id());
  522. dSettings.setSetSlice("0"); // 默认 按标题段落切片
  523. dSettings.setSetAnalyze("1"); // 默认 图片转换成标识符
  524. dSettings.setSetTable("0"); // 默认 ttable转图片
  525. takaiDocumentSettingsMapper.insertDocumentSettings(dSettings);
  526. }
  527. }
  528. }
  529. }
  530. setRedisCache(knowledgeId);
  531. return 1;
  532. }
  533. }
  534. return 0;
  535. }
  536. @Override
  537. public int updateDocument(TakaiDocumentParams documentParams, String documentId) {
  538. int i = takaiDocumentMapper.updateDocument(TakaiDocument.builder()
  539. .documentId(documentId)
  540. .name(documentParams.getName())
  541. .build());
  542. return i;
  543. }
  544. @Override
  545. public List<TakaiDocument> documentList(TakaiDocumentParams documentParams) {
  546. return takaiDocumentMapper.selectDocumentList(TakaiDocument.builder().knowledgeId(documentParams.getKnowledge_id()).build());
  547. }
  548. @Override
  549. public TakaiDocument documentDetail(String documentId) {
  550. return takaiDocumentMapper.selectTargetDocument(TakaiDocument.builder().documentId(documentId).build());
  551. }
  552. @Override
  553. public int delDocument(String documentId) {
  554. TakaiDocument vo = takaiDocumentMapper.selectTargetDocument(TakaiDocument.builder().documentId(documentId).build());
  555. if(vo != null){
  556. String url = deepseekConfig.getBaseurl() + deepseekConfig.getDeleteDoc() + "/" + documentId + "/" + vo.getKnowledgeId();
  557. Request request = buildDeleteRequest(url);
  558. OkHttpClient client = buildOkHttpClient();
  559. try {
  560. Response response = client.newCall(request).execute();
  561. if (response.isSuccessful()) {
  562. String body = response.body().string();
  563. logger.info("删除知识文件调用python接口返回结果:{}", body);
  564. JSONObject obj = JSON.parseObject(body);
  565. Integer code = obj.getInteger("code");
  566. if (code == 200) {
  567. List<TakaiMediaReplacement> list = takaiMediaReplacementMapper.selectMediaList(TakaiMediaReplacement.builder().documentId(documentId).build());
  568. // 删除图片
  569. takaiMediaReplacementMapper.deleteMedia(documentId);
  570. // 删除document文件
  571. int i = takaiDocumentMapper.delDocument(documentId);
  572. if(i > 0){
  573. TakaiKnowledge info = takaiKnowledgeMapper.selectTargetKnowledge(TakaiKnowledge.builder().knowledgeId(vo.getKnowledgeId()).build());
  574. if(info != null){
  575. // 更新知识库文件大小,总字符数, 文件总数
  576. TakaiKnowledge paramInfo = TakaiKnowledge.builder().knowledgeId(vo.getKnowledgeId())
  577. .length(vo.getLength() == null ? info.getLength() == null ? 0 : info.getLength() : info.getLength() - vo.getLength()) // 文件大小
  578. .wordNum(vo.getWordNum() == null ? info.getWordNum() == null ? 0 : info.getWordNum() : info.getWordNum() - vo.getWordNum()) // 总字符数
  579. .documentSize(info.getDocumentSize() == null ? 0 : info.getDocumentSize() - 1) // 文件总数
  580. .build();
  581. takaiKnowledgeMapper.updateKnowledge(paramInfo);
  582. }
  583. // 删除minio文件
  584. try {
  585. logger.info("删除minio文件");
  586. minioUtil.remove("deepseek-doc", "/"+vo.getName());
  587. if(list != null && list.size() > 0){
  588. for (int k=1; k<=list.size(); k++ ) {
  589. logger.info("删除minio图片{}", k);
  590. minioUtil.remove("deepseek-doc" , "/pdf/"+vo.getKnowledgeId()+"/"+documentId+"/【示意图序号_"+documentId+"_"+k+"】.jpg");
  591. }
  592. }
  593. } catch (Exception e) {
  594. logger.error("删除minion文件失败", e.getMessage());
  595. throw new RuntimeException(e);
  596. }
  597. return i;
  598. }
  599. }
  600. }
  601. } catch (IOException e) {
  602. logger.error("删除知识文件调用python接口失败", e.getMessage());
  603. }
  604. }
  605. return 0;
  606. }
  607. @Override
  608. public void uploadUrl(TakaiDocumentObject object) {
  609. }
  610. @Override
  611. public int createApplication(TakaiApplicationParams params) {
  612. SnowflakeDigitGenerator snowflakeDigitGenerator = new SnowflakeDigitGenerator(1, 1);
  613. long appId = snowflakeDigitGenerator.nextId();
  614. TakaiApplication info = TakaiApplication.builder().appId(String.valueOf(appId))
  615. .name(params.getName())
  616. .desc(params.getDesc())
  617. .model(params.getModel())
  618. .knowledgeIds(params.getKnowledge_ids().toString())
  619. .topP(params.getTop_p())
  620. .temperature(params.getTemperature())
  621. .maxToken(params.getMax_token())
  622. .prompt(params.getPrompt())
  623. .knowledgeInfo(params.getKnowledge_info())
  624. .sliceCount(params.getSlice_count())
  625. .build();
  626. int i = takaiApplicationMapper.insertApplication(info);
  627. if (i > 0) {
  628. logger.info("create application success, id:{}, name:{}", appId, params.getName());
  629. TakaiAppInfo appInfo = new TakaiAppInfo();
  630. appInfo.setAppId(String.valueOf(appId));
  631. appInfo.setAppInfo(String.valueOf(JSONObject.from(info).toJSONString()));
  632. takaiAppInfoMapper.insertAppInfo(appInfo);
  633. List<String> list = params.getQuestionList();
  634. if (!list.isEmpty() && list.size() > 0) {
  635. for (String str : list) {
  636. if (org.apache.commons.lang3.StringUtils.isNotBlank(str)) {
  637. String uuid = IdUtils.simpleUUID();
  638. TakaiQuestion question = TakaiQuestion.builder().id(uuid).question(str).appId(String.valueOf(appId)).build();
  639. takaiQuestionMapper.insertQuestion(question);
  640. }
  641. }
  642. }
  643. }
  644. return i;
  645. }
  646. @Override
  647. public int updateApplication(TakaiApplicationParams params, String appId) {
  648. TakaiApplication info = TakaiApplication.builder().appId(appId)
  649. .name(params.getName())
  650. .desc(params.getDesc())
  651. .model(params.getModel())
  652. .knowledgeIds(params.getKnowledge_ids().toString())
  653. .topP(params.getTop_p())
  654. .temperature(params.getTemperature())
  655. .maxToken(params.getMax_token())
  656. .prompt(params.getPrompt())
  657. .knowledgeInfo(params.getKnowledge_info())
  658. .sliceCount(params.getSlice_count())
  659. .build();
  660. int i = takaiApplicationMapper.updateApplication(info);
  661. if (1 > 0) {
  662. TakaiAppInfo appInfo = new TakaiAppInfo();
  663. appInfo.setAppId(appId);
  664. appInfo.setAppInfo(String.valueOf(JSONObject.from(info).toJSONString()));
  665. takaiAppInfoMapper.updateAppInfoByAppId(appInfo);
  666. logger.info("update application success, id:{}, name:{}", appId, params.getName());
  667. //预设问题写入数据库
  668. takaiQuestionMapper.delQuestionByAppId(appId);
  669. List<String> list = params.getQuestionList();
  670. if (!list.isEmpty() && list.size() > 0) {
  671. for (String str : list) {
  672. if (org.apache.commons.lang3.StringUtils.isNotBlank(str)) {
  673. String uuid = IdUtils.simpleUUID();
  674. TakaiQuestion question = TakaiQuestion.builder().id(uuid).question(str).appId(String.valueOf(appId)).build();
  675. takaiQuestionMapper.insertQuestion(question);
  676. }
  677. }
  678. }
  679. }
  680. return i;
  681. }
  682. @Override
  683. public List<TakaiApplication> getApplicationList() {
  684. return takaiApplicationMapper.selectApplicationList(null);
  685. }
  686. @Override
  687. public JSONObject selectApplication(String appId) {
  688. JSONObject object = new JSONObject();
  689. TakaiApplication application = TakaiApplication.builder().appId(appId).build();
  690. TakaiApplication takaiApplication = takaiApplicationMapper.selectTargetApplication(application);
  691. List<TakaiQuestionDTO> list = takaiQuestionMapper.getQuestionList(appId);
  692. object.put("detail", takaiApplication);
  693. object.put("questionlist", list);
  694. return object;
  695. }
  696. @Override
  697. public int delApplication(String appId) {
  698. int i = takaiApplicationMapper.delApplication(appId);
  699. if (i > 0) {
  700. takaiAppInfoMapper.delAppInfoByAppId(appId);
  701. takaiQuestionMapper.delQuestionByAppId(appId);
  702. }
  703. return i;
  704. }
  705. @Override
  706. public List<TakaiQuestionDTO> selectQuestionByAppId(String appId) {
  707. List<TakaiQuestionDTO> List = takaiQuestionMapper.getQuestionList(appId);
  708. return List;
  709. }
  710. @Override
  711. public List<TakaiKnowledge> queryKnowledgeList() {
  712. return takaiKnowledgeMapper.selectKnowledgeList(null);
  713. }
  714. @Override
  715. public List<Object> selectDialogListByAppId(String appId, String userId) {
  716. List<TakaiDialogRespDTO> list = takaiDialogMapper.selectDialog(appId, userId);
  717. List<Object> result = setDialogList(list);
  718. return result;
  719. }
  720. @Override
  721. public TakaiDocumentSettings documentSetting(String documentId) {
  722. return takaiDocumentSettingsMapper.selectById(documentId);
  723. }
  724. @Override
  725. public int updateDocumentSetting(TakaiDocumentSettings documentSettings, String documentId) {
  726. TakaiDocumentSettings settings = new TakaiDocumentSettings();
  727. TakaiDocumentSettings settingsVo = takaiDocumentSettingsMapper.selectById(documentId);
  728. settings.setKnowledgeId(settingsVo.getKnowledgeId());
  729. settings.setSetSlice(documentSettings.getSetSlice());
  730. settings.setSliceValue(documentSettings.getSliceValue());
  731. settings.setSetAnalyze(documentSettings.getSetAnalyze());
  732. settings.setSetTable(documentSettings.getSetTable());
  733. TakaiDocument document = takaiDocumentMapper.selectTargetDocument(TakaiDocument.builder().documentId(documentId).build());
  734. List<UploadDocumentParams> listParams = new ArrayList<>();
  735. if (document != null) {
  736. UploadDocumentParams params = new UploadDocumentParams();
  737. params.setDocument_id(documentId);
  738. params.setName(document.getName());
  739. params.setUrl(document.getUrl());
  740. listParams.add(params);
  741. }
  742. JSONObject jsonObject = analysisFile(listParams, settings, "update");
  743. if (jsonObject != null && jsonObject.containsKey("code") && jsonObject.getInteger("code") == 200) {
  744. logger.info("update document setting success, id:{}", documentId);
  745. documentSettings.setDocumentId(documentId);
  746. return takaiDocumentSettingsMapper.updateDocumentSettings(documentSettings);
  747. }
  748. return 0;
  749. }
  750. @Override
  751. public JSONObject getSliceList(TakaiSliceParams params) {
  752. String url = deepseekConfig.getBaseurl() + deepseekConfig.getSlicePage();
  753. JSONObject jsonObject = new JSONObject();
  754. jsonObject.put("document_id", params.getDocument_id());
  755. jsonObject.put("knowledge_id", params.getKnowledge_id());
  756. jsonObject.put("text", params.getText());
  757. jsonObject.put("pageNum", params.getPageNum());
  758. jsonObject.put("pageSize", params.getPageSize());
  759. RequestBody requestBody = FormBody.create(MediaType.parse("application/json; charset=utf-8"), jsonObject.toJSONString());
  760. Request request = buildPostRequest(url, requestBody);
  761. OkHttpClient client = buildOkHttpClient();
  762. Response response = null;
  763. try {
  764. response = client.newCall(request).execute();
  765. if (response.isSuccessful()) {
  766. String body = response.body().string();
  767. JSONObject obj = JSON.parseObject(body);
  768. Integer code = obj.getInteger("code");
  769. if (code == 200) {
  770. return obj;
  771. }
  772. } else {
  773. logger.info("获取切片列表调用python接口失败,返回状态码:{}", response.code());
  774. }
  775. } catch (IOException e) {
  776. logger.error("获取切片列表调用python接口失败", e.getMessage());
  777. }
  778. return null;
  779. }
  780. @Override
  781. public int deleteSlice(String sliceId, String knowledgeId, String documentId) {
  782. String url = deepseekConfig.getBaseurl() + deepseekConfig.getDeleteSlice() + "/" + sliceId + "/" + knowledgeId + "/" + documentId;
  783. Request request = buildDeleteRequest(url);
  784. OkHttpClient client = buildOkHttpClient();
  785. Response response = null;
  786. try {
  787. response = client.newCall(request).execute();
  788. if (response.isSuccessful()) {
  789. String body = response.body().string();
  790. JSONObject obj = JSON.parseObject(body);
  791. Integer code = obj.getInteger("code");
  792. if (code == 200) {
  793. return 1;
  794. }
  795. }
  796. } catch (IOException e) {
  797. logger.error("删除切片调用python接口失败", e.getMessage());
  798. }
  799. return 0;
  800. }
  801. @Override
  802. public JSONObject getSliceDetail(String sliceId, String knowledgeId) {
  803. String url = deepseekConfig.getBaseurl() + deepseekConfig.getSliceDetail() + "/" + knowledgeId + "/" + sliceId;
  804. Request request = buildGetRequest(url);
  805. OkHttpClient client = buildOkHttpClient();
  806. try {
  807. Response response = client.newCall(request).execute();
  808. if (response.isSuccessful()) {
  809. String body = response.body().string();
  810. log.info("查询切片详情返回:" + body);
  811. return JSON.parseObject(body);
  812. } else {
  813. logger.info("获取切片详情调用python接口失败,返回状态码:{}", response.code());
  814. }
  815. } catch (IOException e) {
  816. log.error("查询切片详情调用python接口失败", e.getMessage());
  817. }
  818. return null;
  819. }
  820. @Override
  821. public int updateSliceInfo(TakaiSliceUpdateParams params) {
  822. String url = deepseekConfig.getBaseurl() + deepseekConfig.getUpdateSlice();
  823. JSONObject object = new JSONObject();
  824. object.put("knowledge_id", params.getKnowledgeId());
  825. object.put("slice_id", params.getSliceId());
  826. object.put("slice_text", params.getSliceText());
  827. object.put("document_id", params.getDocumentId());
  828. RequestBody requestBody = FormBody.create(MediaType.parse("application/json; charset=utf-8"), object.toJSONString());
  829. Request request = buildPutRequest(url, requestBody);
  830. OkHttpClient client = buildOkHttpClient();
  831. try {
  832. Response response = client.newCall(request).execute();
  833. if (response.isSuccessful()) {
  834. String body = response.body().string();
  835. JSONObject obj = JSON.parseObject(body);
  836. Integer code = obj.getInteger("code");
  837. if (code == 200) {
  838. return 1;
  839. }
  840. }
  841. } catch (Exception e) {
  842. logger.error("更新切片信息调用python接口失败", e.getMessage());
  843. }
  844. return 0;
  845. }
  846. @Override
  847. public TakaiMediaReplacement getTakaiMediaReplacement() {
  848. TakaiMediaReplacement mrParams = TakaiMediaReplacement.builder()
  849. .documentId("a2912832435041734656")
  850. .originText("【示意图序号_a2912832435041734656_1】")
  851. .mediaType("image").build();
  852. TakaiMediaReplacement mrVo = takaiMediaReplacementMapper.selectTargetMedia(mrParams);
  853. return mrVo;
  854. }
  855. private JSONObject analysisFile(List<UploadDocumentParams> result, TakaiDocumentSettings settings, String flag) {
  856. String url = deepseekConfig.getBaseurl() + deepseekConfig.getUploadKnowledge();
  857. JSONObject json = new JSONObject();
  858. json.put("knowledge_id", settings.getKnowledgeId());
  859. json.put("docs", result);
  860. if ("upload".equals(flag)) {
  861. json.put("set_slice", "0");
  862. json.put("slice_value", null);
  863. json.put("set_analyze", "1");
  864. json.put("set_table", "0");
  865. json.put("flag", "upload");
  866. } else {
  867. json.put("set_slice", settings.getSetSlice());
  868. json.put("slice_value", settings.getSliceValue());
  869. json.put("set_analyze", settings.getSetAnalyze());
  870. json.put("set_table", settings.getSetTable());
  871. json.put("flag", "update");
  872. }
  873. RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), json.toJSONString());
  874. Request request = buildPostRequest(url, requestBody);
  875. OkHttpClient client = buildOkHttpClient();
  876. Response response = null;
  877. try {
  878. response = client.newCall(request).execute();
  879. if (response.isSuccessful()) {
  880. String body = response.body().string();
  881. JSONObject obj = JSON.parseObject(body);
  882. Integer code = obj.getInteger("code");
  883. if (code == 200) {
  884. logger.info("上传文档调用python接口成功,返回内容:{}", body);
  885. return obj;
  886. }
  887. return obj;
  888. } else {
  889. logger.error("上传文档调用python接口失败返回code:{}", response.code());
  890. return null;
  891. }
  892. } catch (IOException e) {
  893. logger.error("上传文档调用python接口失败", e.getMessage());
  894. return null;
  895. }
  896. }
  897. private List<Object> setDialogList(List<TakaiDialogRespDTO> list) {
  898. //根据时间分组降序排序
  899. NavigableMap<LocalDateTime, List<TakaiDialogRespDTO>> groupList = list.stream().collect(Collectors.groupingBy(TakaiDialogRespDTO::getCreate_time, TreeMap::new, Collectors.toList())).descendingMap();
  900. List<Object> obj = new ArrayList();
  901. if (!groupList.isEmpty()) {
  902. for (Map.Entry<LocalDateTime, List<TakaiDialogRespDTO>> entry : groupList.entrySet()) {
  903. List<TakaiDialogRespDTO> aList = entry.getValue();
  904. List<TakaiDialogRespDTO> detailList = takaiDialogMapper.selectDialogExport(aList.get(0).getId());
  905. Map<String, Object> map = new HashMap();
  906. map.put("dialog_name", aList.get(0).getDialog_name());
  907. map.put("length", detailList.size());
  908. map.put("create_time", aList.get(0).getCreate_time());
  909. map.put("did", aList.get(0).getDid());
  910. map.put("id", aList.get(0).getId());
  911. obj.add(map);
  912. }
  913. }
  914. return obj;
  915. }
  916. private List<Map<String, Object>> setValue(List<TakaiDialogRespDTO> list) {
  917. List<Map<String, Object>> l = new ArrayList();
  918. for (TakaiDialogRespDTO vo : list) {
  919. Map<String, Object> map = new HashMap();
  920. map.put("key", vo.getId());
  921. map.put("label", vo.getDialog_name());
  922. l.add(map);
  923. }
  924. return l;
  925. }
  926. private OkHttpClient buildOkHttpClient() {
  927. return new OkHttpClient.Builder()
  928. .connectTimeout(10, TimeUnit.SECONDS)
  929. .writeTimeout(50, TimeUnit.SECONDS)
  930. .readTimeout(10, TimeUnit.MINUTES)
  931. .build();
  932. }
  933. private Request buildGetRequest(String url) {
  934. return new Request.Builder()
  935. .addHeader("User-Agent", "insomnia/10.3.1")
  936. .url(url).get().build();
  937. }
  938. private Request buildPostRequest(String url, RequestBody requestBody) {
  939. return new Request.Builder()
  940. .addHeader("User-Agent", "insomnia/10.3.1")
  941. .addHeader("Content-Type", "application/json")
  942. .url(url).post(requestBody).build();
  943. }
  944. private Request buildPutRequest(String url, RequestBody requestBody) {
  945. return new Request.Builder()
  946. .addHeader("User-Agent", "insomnia/10.3.1")
  947. .url(url).put(requestBody).build();
  948. }
  949. private Request buildDeleteRequest(String url) {
  950. return new Request.Builder()
  951. .addHeader("Content-Type", "multipart/form-data")
  952. .addHeader("User-Agent", "insomnia/10.3.1")
  953. .url(url).delete().build();
  954. }
  955. private Request buildUploadPostRequest(String url, RequestBody requestBody) {
  956. return new Request.Builder()
  957. .addHeader("User-Agent", "insomnia/10.3.1")
  958. .addHeader("Content-Type", "application/json")
  959. .url(url).post(requestBody).build();
  960. }
  961. private boolean isJsonObject(String data) {
  962. try {
  963. JSON.parseObject(data);
  964. return true;
  965. } catch (Exception e) {
  966. return false;
  967. }
  968. }
  969. }