From 83e1b4d0e661f11a407fd6ea86e906b9b87b7180 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期五, 31 七月 2026 17:57:09 +0800
Subject: [PATCH] feat(aftersales): 售后工单新增问题类型和严重程度字段
---
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/oa/OaNoticeTypeEnum.java | 19
yudao-server/pom.xml | 10
yudao-server/src/main/resources/application.yaml | 82
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgePageReqVO.java | 13
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/ai/CrmSaleQuotationAiService.java | 10
sql/mysql/oa_notice_init.sql | 57
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSplitReqVO.java | 16
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java | 6
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java | 87
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java | 24
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/OaNoticePageReqVO.java | 25
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiMaterialPredictReqVO.java | 15
yudao-server/src/main/resources/application-dev.yaml | 11
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/ai/CrmSaleQuotationAiController.java | 33
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/api/chat/AiChatApiImpl.java | 68
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateStatusReqVO.java | 9
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyRespVO.java | 21
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateStatusReqVO.java | 13
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyPageReqVO.java | 19
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/oa/OaNoticeStatusEnum.java | 20
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/ai/MesProAiServiceImpl.java | 782 ++++++
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ai/vo/MesQcAiSuggestReqVO.java | 19
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/splitter/MarkdownQaSplitter.java | 264 -
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java | 11
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelSaveReqVO.java | 37
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchReqVO.java | 13
yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/api/chat/AiChatApi.java | 28
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiPlatformEnum.java | 54
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiRiskPredictReqVO.java | 15
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiApiKeyDO.java | 33
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java | 1
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/oa/OaNoticeMapper.java | 29
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java | 47
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeySaveReqVO.java | 24
docs/test-quotation-files/测试客户报价单.txt | 35
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiDurationPredictReqVO.java | 15
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java | 19
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java | 149
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiModelDO.java | 58
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java | 66
pom.xml | 3
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiApiKeyMapper.java | 33
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiModelTypeEnum.java | 13
yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/controller/admin/ticket/vo/ticket/AfterSaleTicketSaveReqVO.java | 9
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java | 92
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiApiKeyController.java | 25
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java | 11
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java | 41
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSplitRespVO.java | 19
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java | 8
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiMaterialPredictRespVO.java | 48
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentCreateReqVO.java | 49
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/oa/OaNoticeSendScopeEnum.java | 19
yudao-server/src/main/resources/application-local.yaml | 29
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/oa/OaNoticeUserDO.java | 32
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/oa/OaNoticeDO.java | 73
docs/test-quotation-files/南通客户报价单.csv | 14
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java | 42
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchRespVO.java | 28
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java | 6
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/BpmOANoticeController.java | 79
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java | 462 ---
yudao-module-mes/pom.xml | 7
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ai/MesQcAiServiceImpl.java | 284 ++
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiRiskPredictRespVO.java | 41
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/OaNoticeSaveReqVO.java | 45
docs/mes_pro_ai_integration.md | 208 +
yudao-module-crm/pom.xml | 14
docs/ai_knowledge_api(1).md | 67
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notify/NotifyMessageMapper.java | 6
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiDeliveryPredictReqVO.java | 15
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ai/MesQcAiController.java | 31
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java | 81
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/oa/OaNoticeUserMapper.java | 23
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchRespBO.java | 33
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/oa/OaNoticeReadDO.java | 39
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentProcessRespVO.java | 10
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/OaNoticeRespVO.java | 52
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/ai/vo/CrmSaleQuotationOcrReqVO.java | 15
docs/crm_sale_quotation_ocr_integration.md | 126
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelRespVO.java | 27
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java | 137
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/web/config/AiWebConfiguration.java | 7
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java | 31
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java | 146 -
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/MesProAiController.java | 51
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java | 78
yudao-module-ai-api/pom.xml | 33
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java | 33
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelPageReqVO.java | 21
yudao-module-ai/pom.xml | 12
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/oa/OaNoticeReadMapper.java | 39
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/ai/vo/CrmSaleQuotationOcrRespVO.java | 58
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/splitter/SemanticTextSplitter.java | 227 -
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/ai/CrmSaleQuotationAiServiceImpl.java | 281 ++
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiModelMapper.java | 38
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/OaNoticeServiceImpl.java | 256 +
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java | 10
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/indicatorresult/MesQcIndicatorResultMapper.java | 8
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchReqBO.java | 25
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiModelController.java | 28
docs/mes_qc_ai_integration.md | 164 +
yudao-server/src/main/resources/application-test.yaml | 11
docs/ai_knowledge_rag_integration.md | 373 ++
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiDurationPredictRespVO.java | 51
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiDeliveryPredictRespVO.java | 31
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/OaNoticeService.java | 62
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/YudaoAiProperties.java | 245 -
docs/test-quotation-files/聊天记录生成提示词.txt | 15
docs/oa_notice_frontend.md | 209 +
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ai/vo/MesQcAiSuggestRespVO.java | 24
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java | 109
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java | 53
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/ai/MesProAiService.java | 15
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSaveReqVO.java | 17
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ai/MesQcAiService.java | 19
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java | 117
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeSaveReqVO.java | 24
/dev/null | 30
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/AiDocumentSplitStrategyEnum.java | 31
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/AiModelFactory.java | 91
121 files changed, 4,933 insertions(+), 2,793 deletions(-)
diff --git "a/docs/ai_knowledge_api\0501\051.md" "b/docs/ai_knowledge_api\0501\051.md"
new file mode 100644
index 0000000..43a0d5f
--- /dev/null
+++ "b/docs/ai_knowledge_api\0501\051.md"
@@ -0,0 +1,67 @@
+# AI 鐭ヨ瘑搴撴ā鍧� - 鎺ュ彛鏂囨。
+
+## 娑夊強椤甸潰
+
+```
+src/views/ai/knowledge/knowledge/index.vue # 鐭ヨ瘑搴撳垪琛�
+src/views/ai/knowledge/knowledge/modules/form.vue # 鐭ヨ瘑搴撹〃鍗曞脊绐�
+src/views/ai/knowledge/knowledge/data.ts # 鐭ヨ瘑搴撻厤缃�
+src/views/ai/knowledge/knowledge/retrieval/index.vue # 鍙洖娴嬭瘯
+src/views/ai/knowledge/document/index.vue # 鏂囨。鍒楄〃
+src/views/ai/knowledge/document/data.ts # 鏂囨。閰嶇疆
+src/views/ai/knowledge/document/form/index.vue # 鏂囨。鍒涘缓/缂栬緫锛堟楠ら〉锛�
+src/views/ai/knowledge/document/form/modules/upload-step.vue # 涓婁紶姝ラ
+src/views/ai/knowledge/document/form/modules/split-step.vue # 鍒嗘姝ラ
+src/views/ai/knowledge/document/form/modules/process-step.vue # 澶勭悊姝ラ
+src/views/ai/knowledge/segment/index.vue # 鍒嗘鍒楄〃
+src/views/ai/knowledge/segment/data.ts # 鍒嗘閰嶇疆
+src/views/ai/knowledge/segment/modules/form.vue # 鍒嗘琛ㄥ崟寮圭獥
+```
+
+---
+
+## 鐭ヨ瘑搴� API锛坄src/api/ai/knowledge/knowledge/index.ts`锛�
+
+| 鍑芥暟 | 鏂规硶 | 璺緞 | 浣跨敤浣嶇疆 |
+|------|------|------|----------|
+| `getKnowledgePage` | GET | `/ai/knowledge/page` | knowledge/index.vue |
+| `getKnowledge` | GET | `/ai/knowledge/get?id={id}` | knowledge/form.vue銆乲nowledge/retrieval/index.vue |
+| `createKnowledge` | POST | `/ai/knowledge/create` | knowledge/form.vue |
+| `updateKnowledge` | PUT | `/ai/knowledge/update` | knowledge/form.vue |
+| `deleteKnowledge` | DELETE | `/ai/knowledge/delete?id={id}` | knowledge/index.vue |
+
+## 鏂囨。 API锛坄src/api/ai/knowledge/document/index.ts`锛�
+
+| 鍑芥暟 | 鏂规硶 | 璺緞 | 浣跨敤浣嶇疆 |
+|------|------|------|----------|
+| `getKnowledgeDocumentPage` | GET | `/ai/knowledge/document/page` | document/index.vue |
+| `getKnowledgeDocument` | GET | `/ai/knowledge/document/get?id={id}` | document/form/index.vue |
+| `createKnowledgeDocumentList` | POST | `/ai/knowledge/document/create-list` | document/form/modules/split-step.vue |
+| `updateKnowledgeDocument` | PUT | `/ai/knowledge/document/update` | document/form/modules/split-step.vue |
+| `updateKnowledgeDocumentStatus` | PUT | `/ai/knowledge/document/update-status` | document/index.vue |
+| `deleteKnowledgeDocument` | DELETE | `/ai/knowledge/document/delete?id={id}` | document/index.vue |
+| `processDocument` | POST | `/ai/knowledge/document/process/{id}` | document/index.vue |
+
+## 鍒嗘 API锛坄src/api/ai/knowledge/segment/index.ts`锛�
+
+| 鍑芥暟 | 鏂规硶 | 璺緞 | 浣跨敤浣嶇疆 |
+|------|------|------|----------|
+| `getKnowledgeSegmentPage` | GET | `/ai/knowledge/segment/page` | segment/index.vue |
+| `getKnowledgeSegment` | GET | `/ai/knowledge/segment/get?id={id}` | segment/modules/form.vue |
+| `createKnowledgeSegment` | POST | `/ai/knowledge/segment/create` | segment/modules/form.vue |
+| `updateKnowledgeSegment` | PUT | `/ai/knowledge/segment/update` | segment/modules/form.vue |
+| `updateKnowledgeSegmentStatus` | PUT | `/ai/knowledge/segment/update-status` | segment/index.vue |
+| `deleteKnowledgeSegment` | DELETE | `/ai/knowledge/segment/delete?id={id}` | segment/index.vue |
+| `splitContent` | GET | `/ai/knowledge/segment/split` | document/form/modules/split-step.vue |
+| `getKnowledgeSegmentProcessList` | GET | `/ai/knowledge/segment/get-process-list` | document/form/modules/process-step.vue |
+| `searchKnowledgeSegment` | POST | `/ai/knowledge/segment/search` | knowledge/retrieval/index.vue |
+
+## 妯″瀷 API锛坄src/api/ai/model/model/index.ts`锛�
+
+| 鍑芥暟 | 鏂规硶 | 璺緞 | 浣跨敤浣嶇疆 |
+|------|------|------|----------|
+| `getModelSimpleList` | GET | `/ai/model/simple-list?type={type}` | knowledge/data.ts |
+
+---
+
+> 鍏� 4 涓� API 鏂囦欢锛�22 涓帴鍙e嚱鏁�
diff --git a/docs/ai_knowledge_rag_integration.md b/docs/ai_knowledge_rag_integration.md
new file mode 100644
index 0000000..dd7a0f4
--- /dev/null
+++ b/docs/ai_knowledge_rag_integration.md
@@ -0,0 +1,373 @@
+# AI 鐭ヨ瘑搴� RAG 鍔熻兘 - 鍓嶇鑱旇皟鏂规
+
+## 娑夊強椤甸潰
+
+- **API 瀵嗛挜绠$悊**锛歚ai/views/apikey/` 鈥� 绠$悊 AI 骞冲彴 API 瀵嗛挜
+- **妯″瀷绠$悊**锛歚ai/views/model/` 鈥� 绠$悊 AI 妯″瀷閰嶇疆锛堣亰澶┿�佸悜閲忓寲绛夛級
+- **鐭ヨ瘑搴撶鐞�**锛歚ai/views/knowledge/` 鈥� 鐭ヨ瘑搴� CRUD銆佹枃妗d笂浼犮�佸垎娈电鐞嗐�丷AG 妫�绱�
+
+## 涓氬姟娴佺▼涓庢暟鎹甫鍏�
+
+```
+姝�1: 閰嶇疆 API 瀵嗛挜锛堜竴娆℃�э級
+ 鈹斺攢 鍦ㄣ�孉PI 瀵嗛挜绠$悊銆嶉〉閰嶇疆閫氫箟鍗冮棶 API Key 鈫� 鑾峰緱 apiKeyId
+
+姝�2: 閰嶇疆鍚戦噺鍖栨ā鍨嬶紙涓�娆℃�э級
+ 鈹斺攢 鍦ㄣ�屾ā鍨嬬鐞嗐�嶉〉鏂板缓 Embedding 妯″瀷 鈫� keyId 浠庢1甯﹀叆
+ 鈹斺攢 妯″瀷鏍囪瘑濉� "text-embedding-v3"锛屽钩鍙伴�� "TongYi"锛岀被鍨嬮�� "EMBEDDING(5)"
+ 鈹斺攢 鑾峰緱 embeddingModelId
+
+姝�3: 鍒涘缓鐭ヨ瘑搴�
+ 鈹斺攢 鍦ㄣ�岀煡璇嗗簱绠$悊銆嶉〉鏂板缓鐭ヨ瘑搴� 鈫� embeddingModelId 浠庢2甯﹀叆
+ 鈹斺攢 鍙缃� topK 鍜岀浉浼煎害闃堝��
+
+姝�4: 鍒涘缓鏂囨。
+ 鈹斺攢 鍦ㄧ煡璇嗗簱璇︽儏椤典笂浼犳枃妗o紙鎻愪緵鍚嶇О鍜� URL锛�
+ 鈹斺攢 鍚庣鑷姩涓嬭浇鏂囨。銆佹彁鍙栨枃鏈唴瀹癸紙Tika 瑙f瀽锛�
+
+姝�5: 澶勭悊鏂囨。锛堝垎娈�+鍚戦噺鍖栵級
+ 鈹斺攢 鍦ㄦ枃妗e垪琛ㄧ偣鍑汇�屽鐞嗐�嶆寜閽�
+ 鈹斺攢 鍚庣灏嗘枃妗e唴瀹瑰垏鐗囥�佽皟鐢ㄥ悜閲忔ā鍨嬬敓鎴� Embedding銆佸啓鍏� Milvus
+
+姝�6: RAG 妫�绱�
+ 鈹斺攢 鍦ㄧ煡璇嗗簱璇︽儏椤佃緭鍏ユ绱㈠唴瀹� 鈫� 鍚庣鍚戦噺妫�绱� + 鐩镐技搴﹁繃婊�
+ 鈹斺攢 杩斿洖鏈�鍖归厤鐨勫垎娈靛強鐩镐技搴﹀垎鏁�
+```
+
+---
+
+## API 鎺ュ彛
+
+### 1. API 瀵嗛挜绠$悊
+
+**鍩虹璺緞**锛歚/ai/api-key`
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| POST | `/ai/api-key/create` | 鍒涘缓 API 瀵嗛挜 |
+| PUT | `/ai/api-key/update` | 鏇存柊 API 瀵嗛挜 |
+| DELETE | `/ai/api-key/delete` | 鍒犻櫎 API 瀵嗛挜 |
+| GET | `/ai/api-key/get` | 鑾峰彇鍗曚釜 API 瀵嗛挜 |
+| GET | `/ai/api-key/page` | 鍒嗛〉鏌ヨ API 瀵嗛挜 |
+
+**POST /create 璇锋眰鍙傛暟锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| name | String | 鏄� | 瀵嗛挜鍚嶇О锛屽"閫氫箟鍗冮棶" |
+| apiKey | String | 鏄� | API 瀵嗛挜瀛楃涓� |
+| platform | String | 鏄� | 骞冲彴锛歚TongYi`锛堥�氫箟鍗冮棶锛� |
+| url | String | 鍚� | API 鍦板潃锛堜竴鑸暀绌猴級 |
+| status | Integer | 鍚� | 鐘舵�侊細0=鍚敤锛�1=绂佺敤 |
+
+**鍝嶅簲锛�** `{ "code": 0, "data": 1 }` (杩斿洖瀵嗛挜 ID)
+
+---
+
+### 2. 妯″瀷绠$悊
+
+**鍩虹璺緞**锛歚/ai/model`
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| POST | `/ai/model/create` | 鍒涘缓 AI 妯″瀷 |
+| PUT | `/ai/model/update` | 鏇存柊 AI 妯″瀷 |
+| DELETE | `/ai/model/delete` | 鍒犻櫎 AI 妯″瀷 |
+| GET | `/ai/model/get` | 鑾峰彇鍗曚釜妯″瀷 |
+| GET | `/ai/model/page` | 鍒嗛〉鏌ヨ妯″瀷 |
+
+**POST /create 璇锋眰鍙傛暟锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| keyId | Long | 鏄� | API 瀵嗛挜 ID锛堜粠涓婁竴姝ュ甫鍏ワ級 |
+| name | String | 鏄� | 妯″瀷鍚嶇О锛屽"閫氫箟鍗冮棶鍚戦噺鍖�" |
+| model | String | 鏄� | 妯″瀷鏍囪瘑锛屽悜閲忓寲妯″瀷濉� `text-embedding-v3` |
+| platform | String | 鏄� | 骞冲彴锛屽浐瀹� `TongYi` |
+| type | Integer | 鏄� | 妯″瀷绫诲瀷锛�1=CHAT锛�5=EMBEDDING |
+| sort | Integer | 鍚� | 鎺掑簭 |
+| status | Integer | 鍚� | 鐘舵�侊細0=鍚敤 |
+| temperature | Double | 鍚� | 娓╁害锛圗mbedding 绫诲瀷涓嶉渶瑕侊級 |
+| maxTokens | Integer | 鍚� | 鏈�澶� Token |
+| maxContexts | Integer | 鍚� | 鏈�澶т笂涓嬫枃 |
+
+**妯″瀷绫诲瀷鏋氫妇锛�**
+
+| type 鍊� | 璇存槑 |
+|---------|------|
+| 1 | CHAT锛堣亰澶╋級 |
+| 5 | EMBEDDING锛堝悜閲忓寲锛� |
+
+**骞冲彴鏋氫妇锛�**
+
+| platform | 璇存槑 |
+|----------|------|
+| TongYi | 閫氫箟鍗冮棶 |
+
+---
+
+### 3. 鐭ヨ瘑搴撶鐞�
+
+**鍩虹璺緞**锛歚/ai/knowledge`
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| POST | `/ai/knowledge/create` | 鍒涘缓鐭ヨ瘑搴� |
+| PUT | `/ai/knowledge/update` | 鏇存柊鐭ヨ瘑搴� |
+| DELETE | `/ai/knowledge/delete` | 鍒犻櫎鐭ヨ瘑搴� |
+| GET | `/ai/knowledge/get` | 鑾峰彇鍗曚釜鐭ヨ瘑搴� |
+| GET | `/ai/knowledge/page` | 鍒嗛〉鏌ヨ鐭ヨ瘑搴� |
+| POST | `/ai/knowledge/document/create` | 鍒涘缓鏂囨。锛堟壒閲忥級 |
+
+**POST /create 璇锋眰鍙傛暟锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| name | String | 鏄� | 鐭ヨ瘑搴撳悕绉� |
+| description | String | 鍚� | 鐭ヨ瘑搴撴弿杩� |
+| embeddingModelId | Long | 鏄� | 鍚戦噺妯″瀷 ID锛堜粠妯″瀷绠$悊椤靛甫鍏ワ級 |
+| topK | Integer | 鍚� | 妫�绱㈣繑鍥炴潯鏁帮紝榛樿 3 |
+| similarityThreshold | Double | 鍚� | 鐩镐技搴﹂槇鍊硷紝榛樿 0.7 |
+| status | Integer | 鍚� | 鐘舵�侊細0=鍚敤 |
+
+**鎻愮ず**锛氬垱寤虹煡璇嗗簱鍓嶏紝闇�瑕佸厛鏈変竴涓� `type=5 (EMBEDDING)` 鐨勬ā鍨嬨�傚墠绔簲鍦ㄩ�夋嫨鍚戦噺妯″瀷鏃惰繃婊ゅ彧鏄剧ず `type=5` 鐨勬ā鍨嬨��
+
+**POST /document/create 璇锋眰鍙傛暟锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| knowledgeId | Long | 鏄� | 鐭ヨ瘑搴� ID |
+| names | List\<String\> | 鏄� | 鏂囨。鍚嶇О鍒楄〃锛屽 `["浜у搧鎵嬪唽.pdf", "API鏂囨。.docx"]` |
+| urls | List\<String\> | 鏄� | 鏂囨。 URL 鍒楄〃锛堜笌 names 涓�涓�瀵瑰簲锛� |
+
+---
+
+### 4. 鏂囨。绠$悊
+
+**鍩虹璺緞**锛歚/ai/knowledge/document`
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| PUT | `/ai/knowledge/document/update` | 鏇存柊鏂囨。 |
+| DELETE | `/ai/knowledge/document/delete` | 鍒犻櫎鏂囨。锛堝惈鍒嗘+鍚戦噺锛� |
+| GET | `/ai/knowledge/document/get` | 鑾峰彇鍗曚釜鏂囨。 |
+| GET | `/ai/knowledge/document/page` | 鍒嗛〉鏌ヨ鏂囨。 |
+| PUT | `/ai/knowledge/document/update-status` | 鏇存柊鏂囨。鐘舵�� |
+| POST | `/ai/knowledge/document/process/{id}` | 澶勭悊鏂囨。锛堝垎娈�+鍚戦噺鍖栵級 |
+| GET | `/ai/knowledge/document/progress` | 鏌ヨ鏂囨。澶勭悊杩涘害 |
+
+**PUT /update 璇锋眰鍙傛暟锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| id | Long | 鏄� | 鏂囨。 ID |
+| name | String | 鍚� | 鏂囨。鍚嶇О |
+| url | String | 鍚� | 鏂囨。 URL |
+
+**POST /process/{id}**锛氬皢鏂囨。鍐呭鍒囩墖骞跺啓鍏� Milvus 鍚戦噺搴撱�傚鐞嗗畬鎵嶈兘琚绱㈠埌銆�
+
+**GET /progress 璇锋眰鍙傛暟锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| documentIds | List\<Long\> | 鏄� | 鏂囨。 ID 鍒楄〃 |
+
+**GET /progress 鍝嶅簲鏂板瀛楁锛�**
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| documentId | Long | 鏂囨。 ID |
+| count | Long | 鍒嗘鎬绘暟 |
+| embeddingCount | Long | 宸插悜閲忓寲鏁伴噺 |
+
+---
+
+### 5. 鍒嗘绠$悊 & RAG 妫�绱�
+
+**鍩虹璺緞**锛歚/ai/knowledge/segment`
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| GET | `/ai/knowledge/segment/get` | 鑾峰彇鍗曚釜鍒嗘 |
+| GET | `/ai/knowledge/segment/page` | 鍒嗛〉鏌ヨ鍒嗘 |
+| **GET** | **`/ai/knowledge/segment/split`** | **鏂囨。鍒囧垎棰勮锛堝鐞嗗墠棰勮鍒囧垎鏁堟灉锛�** |
+| PUT | `/ai/knowledge/segment/update-status` | 鏇存柊鍒嗘鐘舵�� |
+| **POST** | **`/ai/knowledge/segment/search`** | **RAG 鍚戦噺妫�绱紙鏍稿績鎺ュ彛锛�** |
+
+### GET /ai/knowledge/segment/split
+
+鐢ㄤ簬鍦ㄦ枃妗e鐞嗗墠棰勮鍒囧垎鏁堟灉锛屼笉淇濆瓨浠讳綍鏁版嵁銆�
+
+**璇锋眰鍙傛暟锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| url | String | 鏄� | 鏂囨。 URL |
+| name | String | 鏄� | 鏂囨。鍚嶇О锛堢敤浜庡垽鏂枃浠剁被鍨嬶級 |
+| segmentMaxTokens | Integer | 鍚� | 鍒嗘鏈�澶� Token 鏁帮紝榛樿 800 |
+
+**鍝嶅簲绀轰緥锛�**
+
+```json
+{
+ "code": 0,
+ "data": [
+ {
+ "content": "绗竴娈靛唴瀹�...",
+ "contentLength": 256,
+ "tokens": 180
+ },
+ {
+ "content": "绗簩娈靛唴瀹�...",
+ "contentLength": 300,
+ "tokens": 210
+ }
+ ]
+}
+```
+
+---
+
+## RAG 妫�绱紙鏍稿績鍔熻兘锛�
+
+### POST /ai/knowledge/segment/search
+
+**璇锋眰鍙傛暟锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| knowledgeId | Long | 鏄� | 鐭ヨ瘑搴� ID |
+| content | String | 鏄� | 妫�绱㈠唴瀹癸紙鑷劧璇█锛� |
+| topK | Integer | 鍚� | 杩斿洖鏉℃暟锛岄粯璁ゅ彇鐭ヨ瘑搴撻厤缃殑 topK |
+| similarityThreshold | Double | 鍚� | 鐩镐技搴﹂槇鍊硷紝榛樿鍙栫煡璇嗗簱閰嶇疆鐨勯槇鍊� |
+
+**鍝嶅簲锛�**
+
+```json
+{
+ "code": 0,
+ "data": [
+ {
+ "id": 100,
+ "documentId": 10,
+ "knowledgeId": 1,
+ "content": "鍒嗘鍐呭鏂囨湰...",
+ "contentLength": 256,
+ "tokens": 180,
+ "score": 0.85
+ }
+ ]
+}
+```
+
+**鍝嶅簲瀛楁璇存槑锛�**
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| id | Long | 鍒嗘 ID |
+| documentId | Long | 鎵�灞炴枃妗� ID |
+| knowledgeId | Long | 鎵�灞炵煡璇嗗簱 ID |
+| content | String | 鍒嗘鍐呭鏂囨湰 |
+| contentLength | Integer | 鍐呭瀛楃闀垮害 |
+| tokens | Integer | 浼扮畻 Token 鏁� |
+| score | Double | 鐩镐技搴﹀垎鏁帮紙0~1锛夛紝瓒婇珮瓒婄浉鍏� |
+
+---
+
+## 瀛楁灞曠ず瑙勫垯
+
+### 鐭ヨ瘑搴撳垪琛�/璇︽儏
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| name | 鍒楄〃銆佽鎯� | 鐭ヨ瘑搴撳悕绉� |
+| description | 璇︽儏 | 鐭ヨ瘑搴撴弿杩� |
+| embeddingModel | 鍒楄〃銆佽鎯� | 鏄剧ず妯″瀷鏍囪瘑濡� `text-embedding-v3` |
+| topK | 璇︽儏 | 妫�绱㈣繑鍥炴渶澶ф潯鏁� |
+| similarityThreshold | 璇︽儏 | 鐩镐技搴﹂槇鍊硷紙0~1锛� |
+| status | 鍒楄〃銆佽鎯� | 鍚敤/绂佺敤 |
+| createTime | 鍒楄〃 | 鍒涘缓鏃堕棿 |
+
+### 鏂囨。鍒楄〃/璇︽儏
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| name | 鍒楄〃銆佽鎯� | 鏂囨。鍚嶇О |
+| url | 璇︽儏 | 鏂囨。 URL |
+| contentLength | 璇︽儏 | 鍐呭闀垮害 |
+| tokens | 璇︽儏 | Token 鏁� |
+| retrievalCount | 璇︽儏 | 琚绱㈠懡涓鏁� |
+| status | 鍒楄〃锛坱ag锛� | 鐘舵�佹爣绛� |
+
+### 鍒嗘鍒楄〃
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| documentId | 鍒楄〃 | 鎵�灞炴枃妗� ID |
+| content | 鍒楄〃锛堟埅鏂級 | 鍒嗘鍐呭锛堝彲鎴柇鏄剧ず鍓� 100 瀛楋級 |
+| contentLength | 鍒楄〃 | 鍐呭闀垮害 |
+| tokens | 鍒楄〃 | Token 鏁� |
+| vectorId | 鍒楄〃/璇︽儏 | 鍚戦噺 ID锛堥潪绌鸿〃绀哄凡鍚戦噺鍖栵級 |
+| retrievalCount | 鍒楄〃 | 妫�绱㈠懡涓鏁� |
+
+### RAG 妫�绱㈢粨鏋�
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| content | 缁撴灉鍗$墖 | 鍒嗘鍏ㄦ枃鍐呭 |
+| score | 缁撴灉鍗$墖 | 鐩镐技搴﹀垎鏁帮紝寤鸿鐢ㄨ繘搴︽潯鎴栭鑹叉爣璇嗭紙>0.8 缁胯壊锛�0.6~0.8 榛勮壊锛�<0.6 鐏拌壊锛� |
+| documentId | 杈呭姪淇℃伅 | 鍙偣鍑昏烦杞埌鏉ユ簮鏂囨。 |
+| contentLength | 杈呭姪淇℃伅 | 鍐呭闀垮害 |
+
+---
+
+## 涓氬姟瑙勫垯璇存槑
+
+| 鍦烘櫙 | 瑙勫垯 |
+|------|------|
+| 鍒涘缓鏂囨。 | 鍚庣鑷姩涓嬭浇 URL 鏂囦欢骞舵彁鍙栨枃鏈�傛敮鎸� `txt/md/json/xml/csv/yaml/yml`锛堢洿鎺ヨ鍙栵級鍜屽叾浠栨牸寮忥紙Tika 瑙f瀽锛� |
+| 鏂囨。澶勭悊 | 蹇呴』鍏� `POST /process/{id}` 澶勭悊鍚庯紝鏂囨。鍐呭鎵嶄細琚绱㈠埌 |
+| 鏂囨。鍒嗘 | 鍚庣浣跨敤 `TokenTextSplitter` 鑷姩鍒囩墖锛岄粯璁ゆ瘡娈� 800 tokens |
+| 鍚戦噺鍐欏叆 | 澶勭悊鏂囨。鏃惰嚜鍔ㄨ皟鐢� Embedding 妯″瀷鐢熸垚鍚戦噺骞跺啓鍏� Milvus |
+| 鍒犻櫎鏂囨。 | 鍚屾椂鍒犻櫎鏂囨。璁板綍銆佹墍鏈夊垎娈佃褰曞拰 Milvus 涓搴斿悜閲� |
+| 妫�绱㈢浉浼煎害 | 杩斿洖缁撴灉鎸夌浉浼煎害闄嶅簭鎺掑垪锛屼綆浜� `similarityThreshold` 鐨勭粨鏋滀笉浼氳杩斿洖 |
+| 妫�绱㈣鏁板櫒 | 姣忔妫�绱㈠懡涓悗锛岃嚜鍔ㄦ洿鏂板搴斿垎娈靛拰鏂囨。鐨� `retrievalCount` |
+| API 瀵嗛挜 | 鍒涘缓鐭ヨ瘑搴撳墠锛屽繀椤诲厛鍒涘缓 API 瀵嗛挜鍜� Embedding 妯″瀷 |
+
+---
+
+## 鎺ュ彛璋冪敤鏃跺簭
+
+### 鍒涘缓鐭ヨ瘑搴撳畬鏁存祦绋�
+
+```
+1. POST /ai/api-key/create 鈫� apiKeyId
+2. POST /ai/model/create 鈫� modelId (type=5, keyId=apiKeyId)
+3. POST /ai/knowledge/create 鈫� knowledgeId (embeddingModelId=modelId)
+4. POST /ai/knowledge/document/create 鈫� documentIds
+5. POST /ai/knowledge/document/process/{documentId} 鈫� 鍚戦噺鍖�
+6. GET /ai/knowledge/document/progress?documentIds=... 鈫� 妫�鏌ヨ繘搴�
+7. POST /ai/knowledge/segment/search 鈫� RAG 妫�绱�
+```
+
+### 妫�绱㈡祦绋嬶紙宸查厤缃ソ鐨勭煡璇嗗簱锛�
+
+```
+1. POST /ai/knowledge/segment/search
+ { knowledgeId, content, topK?, similarityThreshold? }
+ 鈫�
+ 鍚庣: 鏌ヨ鍚戦噺 鈫� Milvus鐩镐技搴︽绱� 鈫� 杩囨护浣庝簬闃堝�肩殑缁撴灉 鈫� 鏇存柊妫�绱㈣鏁�
+ 鈫�
+ 杩斿洖鎼滅储缁撴灉鍒楄〃
+```
+
+---
+
+## 娉ㄦ剰浜嬮」
+
+- 鐭ヨ瘑搴撳垱寤烘椂蹇呴』閫夋嫨 `type=5 (EMBEDDING)` 鐨勬ā鍨嬶紝鍓嶇鑾峰彇妯″瀷鍒楄〃鍚庨渶瑕佽繃婊�
+- 鏂囨。涓婁紶鍚�**涓嶄細鑷姩澶勭悊**锛岄渶瑕佸湪鏂囨。鍒楄〃鐐瑰嚮銆屽鐞嗐�嶆寜閽皟鐢� `POST /process/{id}`
+- 澶勭悊杩涘害鍙�氳繃 `GET /progress` 杞鏌ヨ锛坄embeddingCount >= count` 琛ㄧず澶勭悊瀹屾垚锛�
+- 妫�绱㈢殑 `similarityThreshold` 寤鸿榛樿 0.7锛屽彲鏍规嵁瀹為檯鏁堟灉璋冩暣
+- `vectorId` 涓虹┖锛坄""`锛夌殑鍒嗘琛ㄧず灏氭湭鍐欏叆鍚戦噺搴擄紝涓嶄細琚绱㈠埌
+- 鍒犻櫎鐭ヨ瘑搴撴椂锛屽悗绔綋鍓嶅疄鐜板彧鍒犻櫎鐭ヨ瘑搴撹褰曟湰韬紝闇�纭鏄惁闇�瑕佺骇鑱斿垹闄ゆ枃妗e拰鍒嗘
diff --git a/docs/crm_sale_quotation_ocr_integration.md b/docs/crm_sale_quotation_ocr_integration.md
new file mode 100644
index 0000000..aaf655d
--- /dev/null
+++ b/docs/crm_sale_quotation_ocr_integration.md
@@ -0,0 +1,126 @@
+# CRM 閿�鍞姤浠峰崟 AI OCR 璇嗗埆 - 鍓嶇鑱旇皟鏂规
+
+## 娑夊強椤甸潰
+
+- `saleQuotation/index.vue` 鈥� 鎶ヤ环鍗曞垪琛ㄩ〉锛屾柊澧�"AI 璇嗗埆褰曞叆"鎸夐挳
+- `saleQuotation/form.vue` 鈥� 鎶ヤ环鍗曟柊澧�/缂栬緫寮圭獥锛屾帴鏀� OCR 璇嗗埆缁撴灉棰勫~琛ㄥ崟
+
+## 涓氬姟娴佺▼涓庢暟鎹甫鍏�
+
+1. 鎶ヤ环鍗曞垪琛ㄩ〉 鈫� 鐐瑰嚮"AI 璇嗗埆褰曞叆"鎸夐挳 鈫� 寮瑰嚭涓婁紶寮圭獥
+2. 涓婁紶寮圭獥涓細閫夋嫨鏂囦欢涓婁紶 鈫� 璋冪敤 `/admin-api/system/storage-blob/upload` 鈫� 鑾峰緱 `blobId`
+3. 璋冪敤 `/admin-api/crm/sale-quotation/ai/ocr` 浼犲叆 `blobId` 鈫� 鑾峰緱璇嗗埆缁撴灉
+4. 棰勮璇嗗埆缁撴灉 鈫� 鐢ㄦ埛鐐瑰嚮"纭骞跺~鍏ヨ〃鍗�" 鈫� 鍏抽棴涓婁紶寮圭獥 鈫� 鎵撳紑鏂板鎶ヤ环鍗曞脊绐�
+5. 鏂板寮圭獥涓細璇嗗埆缁撴灉棰勫~鍒板搴斿瓧娈碉紝鐢ㄦ埛琛ュ厖瀹屽杽鍚庝繚瀛�
+
+## API
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| POST | `/admin-api/crm/sale-quotation/ai/ocr` | AI OCR 璇嗗埆鎶ヤ环鏂囦欢 |
+
+**璇锋眰鍙傛暟锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| blobId | Long | 鏄� | 宸蹭笂浼犳枃浠剁殑 blob ID锛堥�氳繃 `/admin-api/system/storage-blob/upload` 鑾峰彇锛� |
+
+**鍝嶅簲瀛楁锛�**
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| name | String | 鎶ヤ环鍗曞悕绉� |
+| customerName | String | 瀹㈡埛鍚嶇О锛堟枃鏈紝闇�鐢ㄦ埛鎵嬪姩鍖归厤 CRM 瀹㈡埛锛� |
+| quotationTime | String (LocalDate) | 鎶ヤ环鏃ユ湡锛屾牸寮� yyyy-MM-dd |
+| validUntil | String (LocalDate) | 鏈夋晥鏈熻嚦锛屾牸寮� yyyy-MM-dd |
+| taxRate | BigDecimal | 绋庣巼锛堢櫨鍒嗘瘮锛夛紝濡� 13.0 |
+| discountPercent | BigDecimal | 鎶樻墸鐜囷紙鐧惧垎姣旓級锛屽 5.0 |
+| remark | String | 澶囨敞 |
+| items | Array\<Item\> | 鐗╂枡鏄庣粏鍒楄〃 |
+| items[].itemName | String | 鐗╂枡鍚嶇О锛堟枃鏈紝闇�鐢ㄦ埛鍦� MdmItemSelect 涓尮閰嶏級 |
+| items[].itemSpec | String | 鐗╂枡瑙勬牸鍨嬪彿 |
+| items[].count | BigDecimal | 鏁伴噺 |
+| items[].quotationPrice | BigDecimal | 鍗曚环 |
+| rawText | String | 鍘熷璇嗗埆鏂囨湰锛屽墠绔彲閫夊睍绀虹敤浜庤皟璇�/鍙傝�� |
+
+鎵�鏈夊瓧娈靛潎涓哄彲閫夛紝AI 鏈瘑鍒埌鐨勫瓧娈典负 null銆�
+
+**鍝嶅簲绀轰緥锛�**
+
+```json
+{
+ "code": 0,
+ "data": {
+ "name": "XX鍏徃鎶ヤ环鍗�",
+ "customerName": "XX绉戞妧鏈夐檺鍏徃",
+ "quotationTime": "2026-07-31",
+ "validUntil": "2026-08-30",
+ "taxRate": 13.0,
+ "discountPercent": 5.0,
+ "remark": "鍚◣鍚繍璐�",
+ "items": [
+ {
+ "itemName": "浼烘湇鐢垫満",
+ "itemSpec": "SV-1000W",
+ "count": 10,
+ "quotationPrice": 1500.00
+ }
+ ],
+ "rawText": "鍘熷鏂囦欢鍐呭..."
+ }
+}
+```
+
+**閿欒鎯呭喌锛圚TTP 200锛屾墍鏈夊瓧娈典负 null锛宺awText 涓寘鍚敊璇彁绀猴級锛�**
+
+| rawText 鍊� | 鍦烘櫙 |
+|------------|------|
+| 鏂囦欢涓嶅瓨鍦� | blobId 鏃犳晥 |
+| 涓嶆敮鎸佺殑鏂囦欢绫诲瀷锛岃涓婁紶 PDF/Word/Excel/鍥剧墖鏂囦欢 | 鏂囦欢鍚庣紑涓嶅湪鐧藉悕鍗曞唴 |
+| 鏂囦欢璇诲彇澶辫触 | 鏂囦欢涓嬭浇澶辫触 |
+| 鏂囦欢鍐呭涓虹┖锛屾棤娉曡瘑鍒� | 鏂囦欢瑙f瀽鍑虹┖鍐呭 |
+| AI 璇嗗埆鏆傛椂涓嶅彲鐢紝璇锋墜鍔ㄥ綍鍏� | AI 鏈嶅姟寮傚父 |
+
+## 鏀寔鐨勬枃浠剁被鍨�
+
+| 绫诲瀷 | 鍚庣紑 |
+|------|------|
+| PDF | .pdf |
+| Word | .doc, .docx |
+| Excel | .xls, .xlsx |
+| 鍥剧墖 | .png, .jpg, .jpeg, .gif, .bmp |
+| 绾枃鏈� | .txt, .csv |
+
+## 鏁版嵁濉叆瑙勫垯
+
+| OCR 瀛楁 | 濉叆鏂板琛ㄥ崟瀛楁 | 璇存槑 |
+|----------|-----------------|------|
+| name | 鎶ヤ环鍗曞悕绉� | 鐩存帴濉叆 |
+| customerName | 瀹㈡埛鍚嶇О锛堟枃鏈睍绀猴級 | 涓嶈嚜鍔ㄥ尮閰� CRM 瀹㈡埛锛岀敤鎴烽渶鎵嬪姩閫氳繃 CrmCustomerSelect 閫夋嫨 |
+| quotationTime | 鎶ヤ环鏃ユ湡 | 鐩存帴濉叆鏃ユ湡閫夋嫨鍣� |
+| validUntil | 鏈夋晥鏈熻嚦 | 鐩存帴濉叆鏃ユ湡閫夋嫨鍣� |
+| taxRate | 绋庣巼 | 鐩存帴濉叆锛屽崟浣嶄负鐧惧垎姣� |
+| discountPercent | 鎶樻墸鐜� | 鐩存帴濉叆锛屽崟浣嶄负鐧惧垎姣� |
+| remark | 澶囨敞 | 鐩存帴濉叆 |
+| items[].itemName + itemSpec | 鐗╂枡鏄庣粏琛� | 濉叆鐗╂枡鍚嶇О鍜岃鏍兼枃鏈紝鐢ㄦ埛闇�鍦� MdmItemSelect 涓墜鍔ㄥ尮閰嶅疄闄呯墿鏂� |
+| items[].count | 鐗╂枡鏄庣粏琛� - 鏁伴噺 | 鐩存帴濉叆 |
+| items[].quotationPrice | 鐗╂枡鏄庣粏琛� - 鍗曚环 | 鐩存帴濉叆 |
+
+## 涓氬姟瑙勫垯璇存槑
+
+| 鍦烘櫙 | 瑙勫垯 |
+|------|------|
+| AI 璇嗗埆缁撴灉鍏ㄤ负 null | 寮圭獥鎻愮ず"鏈瘑鍒埌鎶ヤ环鏁版嵁"锛岀敤鎴峰彲鎵嬪姩褰曞叆鎴栭噸鏂颁笂浼� |
+| 閮ㄥ垎瀛楁璇嗗埆鎴愬姛 | 棰勫~宸叉湁瀛楁锛岀己澶卞瓧娈电暀绌虹敱鐢ㄦ埛鎵嬪姩濉啓 |
+| 璇嗗埆鍒板涓墿鏂欎絾鐢ㄦ埛鍙渶閮ㄥ垎 | 鐢ㄦ埛鍦ㄦ柊澧炲脊绐椾腑澧炲垹璋冩暣鐗╂枡琛� |
+| 瀹㈡埛鍚嶇О鏃犳硶鍖归厤 CRM 瀹㈡埛 | 鐢ㄦ埛閫氳繃 CrmCustomerSelect 鎼滅储骞堕�夋嫨姝g‘鐨勫鎴� |
+
+## 娉ㄦ剰浜嬮」
+
+- 鏂囦欢涓婁紶浣跨敤鐜版湁鐨� `/admin-api/system/storage-blob/upload` 鎺ュ彛锛屼笉瑕佷娇鐢� `/infra/file/upload`
+- 鏂囦欢澶у皬闄愬埗鐢� system 妯″潡鐨� storage-blob 涓婁紶鎺ュ彛鎺у埗
+- AI OCR 璇嗗埆鍙兘闇�瑕佸嚑绉掓椂闂达紝鍓嶇闇�鏄剧ず loading 鐘舵��
+- `customerName` 鏄函鏂囨湰锛屽墠绔笉瑕佸皾璇曡嚜鍔ㄥ尮閰嶏紝闇�瑕佺敤鎴烽�氳繃 CrmCustomerSelect 鎵嬪姩閫夋嫨
+- `itemName` 鍚岀悊锛岀敤鎴烽渶鍦� MdmItemSelect 涓悳绱㈢墿鏂欑紪鐮�/鍚嶇О鏉ュ尮閰嶅疄闄呯墿鏂�
+- 璇嗗埆缁撴灉涓殑鏃ユ湡鏍煎紡涓� `yyyy-MM-dd`锛岀洿鎺ュ彲鐢ㄤ簬鏃ユ湡閫夋嫨鍣�
+- 褰� rawText 涓寘鍚敊璇彁绀猴紙濡�"鏂囦欢涓嶅瓨鍦�"锛夋椂锛屽墠绔簲灞曠ず閿欒淇℃伅锛屼笉瑕佺户缁~鍏ヨ〃鍗曟祦绋�
diff --git a/docs/mes_pro_ai_integration.md b/docs/mes_pro_ai_integration.md
new file mode 100644
index 0000000..c02ae1a
--- /dev/null
+++ b/docs/mes_pro_ai_integration.md
@@ -0,0 +1,208 @@
+# 鐢熶骇妯″潡 AI 杈呭姪澧炲己 - 鍓嶇鑱旇皟鏂规
+
+## 娑夊強椤甸潰
+
+- 鐢熶骇宸ュ崟璇︽儏椤碉紙MesProWorkOrder锛�
+
+---
+
+## 鍔熻兘涓�锛欰I 鐗╂枡鐭己棰勬祴
+
+### 涓氬姟娴佺▼
+
+```
+鍦ㄥ伐鍗曡鎯呴〉
+ 鈫� 鐐瑰嚮"AI 鐗╂枡棰勬祴"鎸夐挳
+ 鈫� 鍚庣鏀堕泦 BOM + 搴撳瓨鏁版嵁锛岃皟鐢� AI
+ 鈫� 寮圭獥灞曠ず鐗╂枡椋庨櫓鍒嗘瀽锛堟暣浣撻闄╃瓑绾� + 鍚勭墿鏂欐槑缁嗭級
+```
+
+### API
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| POST | `/admin-api/mes/pro/ai/predict-material` | AI 鐗╂枡鐭己棰勬祴 |
+
+**璇锋眰鍙傛暟锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| workOrderId | Long | 鏄� | 宸ュ崟 ID |
+
+**鍝嶅簲瀛楁锛�**
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| riskLevel | Integer | 鏁翠綋椋庨櫓绛夌骇锛�1=浣庨闄� 2=涓闄� 3=楂橀闄� |
+| reasoning | String | AI 缁煎悎鍒嗘瀽 |
+| riskItems | List\<RiskItem\> | 鐗╂枡椋庨櫓鏄庣粏 |
+
+**RiskItem 瀛愬瓧娈碉細**
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| itemCode | String | 鐗╂枡缂栫爜 |
+| itemName | String | 鐗╂枡鍚嶇О |
+| requiredQuantity | BigDecimal | 闇�姹傞噺 |
+| availableQuantity | BigDecimal | 搴撳瓨鍙敤閲� |
+| shortageQuantity | BigDecimal | 鐭己鏁伴噺 |
+| riskLevel | String | 椋庨櫓绛夌骇锛氬厖瓒�/绱у紶/鐭己 |
+| suggestion | String | 寤鸿鎺柦 |
+
+---
+
+## 鍔熻兘浜岋細AI 鐢熶骇鏃堕暱棰勬祴
+
+### API
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| POST | `/admin-api/mes/pro/ai/predict-duration` | AI 鐢熶骇鏃堕暱棰勬祴 |
+
+**鍝嶅簲瀛楁锛�**
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| predictedTotalHours | BigDecimal | 棰勬祴鎬绘椂闀匡紙灏忔椂锛� |
+| standardTotalHours | BigDecimal | 鏍囧噯鎬绘椂闀匡紙灏忔椂锛� |
+| reasoning | String | AI 鎺ㄧ悊鍒嗘瀽 |
+| keyPoints | List\<String\> | 鍏抽敭鍏虫敞鐐� |
+| processBreakdown | List\<ProcessTimeBreakdown\> | 鍚勫伐搴忔椂闀挎槑缁� |
+
+**ProcessTimeBreakdown 瀛愬瓧娈碉細**
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| processName | String | 宸ュ簭鍚嶇О |
+| sort | Integer | 宸ュ簭椤哄簭 |
+| standardPrepareTime | Integer | 鏍囧噯鍑嗗鏃堕棿锛堝垎閽燂級 |
+| standardWaitTime | Integer | 鏍囧噯绛夊緟鏃堕棿锛堝垎閽燂級 |
+| standardProductionTime | BigDecimal | 鏍囧噯鐢熶骇鏃堕棿锛堝皬鏃讹級 |
+| predictedProductionTime | BigDecimal | 棰勬祴鐢熶骇鏃堕棿锛堝皬鏃讹級 |
+
+---
+
+## 鍔熻兘涓夛細AI 鐢熶骇椋庨櫓棰勬祴
+
+### API
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| POST | `/admin-api/mes/pro/ai/predict-risk` | AI 鐢熶骇椋庨櫓棰勬祴 |
+
+**鍝嶅簲瀛楁锛�**
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| overallRiskLevel | Integer | 鏁翠綋椋庨櫓绛夌骇锛�1=浣庨闄� 2=涓闄� 3=楂橀闄� |
+| reasoning | String | AI 缁煎悎鍒嗘瀽 |
+| keyPoints | List\<String\> | 鍏抽敭鍏虫敞鐐� |
+| risks | List\<RiskItem\> | 椋庨櫓鏄庣粏鍒楄〃 |
+
+**RiskItem 瀛愬瓧娈碉細**
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| category | String | 椋庨櫓绫诲埆锛氳川閲�/璁惧/鐗╂枡/宸ヨ壓鐡堕 |
+| description | String | 椋庨櫓鎻忚堪 |
+| severity | String | 涓ラ噸绋嬪害锛氶珮/涓�/浣� |
+| suggestion | String | 寤鸿鎺柦 |
+
+---
+
+## 鍔熻兘鍥涳細AI 鏄惁鑳芥寜鏃朵氦浠�
+
+### API
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| POST | `/admin-api/mes/pro/ai/predict-delivery` | AI 鎸夋椂浜や粯棰勬祴 |
+
+**鍝嶅簲瀛楁锛�**
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| onTime | Boolean | 鏄惁鑳芥寜鏃朵氦浠� |
+| confidence | Double | 缃俊搴︼紙0-100锛� |
+| reasoning | String | AI 鎺ㄧ悊鍒嗘瀽 |
+| delayFactors | List\<String\> | 寤舵湡鍥犵礌 |
+| progressPercent | BigDecimal | 鐢熶骇杩涘害鐧惧垎姣� |
+| remainingDays | Integer | 璺濈闇�姹傛棩鏈熷墿浣欏ぉ鏁� |
+
+---
+
+## 瀛楁灞曠ず瑙勫垯
+
+### 鐗╂枡棰勬祴寮圭獥
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| riskLevel | 寮圭獥椤堕儴閱掔洰鏄剧ず锛堥鑹诧細缁胯壊=浣庨闄┿�佹鑹�=涓闄┿�佺孩鑹�=楂橀闄╋級 | 鏁翠綋椋庨櫓绛夌骇 |
+| reasoning | 寮圭獥姝f枃鍖哄煙 | 娈佃惤鏂囨湰 |
+| riskItems | 寮圭獥涓儴琛ㄦ牸 | 鐗╂枡鏄庣粏鍒楄〃 |
+
+### 鏃堕暱棰勬祴寮圭獥
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| predictedTotalHours | 寮圭獥椤堕儴閱掔洰鏄剧ず锛屽姣� standardTotalHours | 棰勬祴 vs 鏍囧噯 |
+| reasoning | 寮圭獥姝f枃鍖哄煙 | 娈佃惤鏂囨湰 |
+| keyPoints | 寮圭獥搴曢儴 | icon + 鏂囨湰鍒楄〃 |
+| processBreakdown | 寮圭獥涓儴琛ㄦ牸 | 姣忛亾宸ュ簭鐨勬椂闀垮垎瑙� |
+
+### 椋庨櫓棰勬祴寮圭獥
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| overallRiskLevel | 寮圭獥椤堕儴閱掔洰鏄剧ず | 棰滆壊鍖哄垎 |
+| reasoning | 寮圭獥姝f枃鍖哄煙 | 娈佃惤鏂囨湰 |
+| keyPoints | 寮圭獥涓儴 | 鍏虫敞鐐瑰垪琛� |
+| risks | 寮圭獥搴曢儴琛ㄦ牸 | 鎸夌被鍒垎缁勫睍绀� |
+
+### 浜や粯棰勬祴寮圭獥
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| onTime | 寮圭獥椤堕儴閱掔洰鏄剧ず锛堢豢鑹�=鍙氦浠樸�佺孩鑹�=涓嶅彲浜や粯锛� | 鏄惁鎸夋椂 |
+| confidence | onTime 鏃佽竟 | 缃俊搴︾櫨鍒嗘瘮 |
+| progressPercent | 杩涘害鏉� | 鍙鍖栬繘搴� |
+| remainingDays | 杩涘害鏉″彸渚� | 鍓╀綑澶╂暟 |
+| reasoning | 寮圭獥姝f枃鍖哄煙 | 娈佃惤鏂囨湰 |
+| delayFactors | 寮圭獥搴曢儴 | 寤舵湡鍥犵礌鍒楄〃锛堜粎涓嶅彲浜や粯鏃舵樉绀猴級 |
+
+---
+
+## 涓氬姟瑙勫垯
+
+| 鍦烘櫙 | 瑙勫垯 |
+|------|------|
+| 鐐瑰嚮鎸夐挳 | 鎸夐挳鏄剧ず loading 鐘舵�侊紝涓嶅彲閲嶅鐐瑰嚮 |
+| AI 杩斿洖鎴愬姛 | 寮圭獥灞曠ず缁撴灉锛屼粎浣滃弬鑰� |
+| AI 杩斿洖澶辫触 | 寮圭獥鏄剧ず"AI 鍒嗘瀽鏆傛椂涓嶅彲鐢紝璇风◢鍚庨噸璇�"锛屼笉褰卞搷姝e父鎿嶄綔 |
+| 宸ュ崟鐘舵�佷负鑽夌/宸插彇娑� | 浠嶅彲璋冪敤 AI 棰勬祴锛堢敤浜庣敓浜у墠璇勪及锛� |
+| 鏃� BOM 鏁版嵁 | 鐗╂枡棰勬祴杩斿洖绌洪闄╅」 |
+| 鏈帓浜э紙鏃犱换鍔★級 | 鏃堕暱棰勬祴浠呰繑鍥炴爣鍑嗘椂闂翠及绠� |
+
+### 鎸夐挳鏀剧疆浣嶇疆
+
+鍦ㄥ伐鍗曡鎯呴〉鐨勬搷浣滃尯鍩熸柊澧� 4 涓� AI 杈呭姪鎸夐挳銆傚缓璁斁鍦�"纭/瀹屾垚/鍙栨秷"鎸夐挳鍖哄煙鐨勪笂鏂癸紝鐢ㄥ垎闅旂嚎闅斿紑锛�
+
+```
+[AI 鐗╂枡棰勬祴] [AI 鏃堕暱棰勬祴] [AI 椋庨櫓棰勬祴] [AI 浜や粯璇勪及]
+鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+[纭宸ュ崟] [瀹屾垚宸ュ崟] [鍙栨秷宸ュ崟]
+```
+
+4 涓� AI 鎸夐挳鍦ㄥ伐鍗曠姸鎬佷负"鑽夌"鍜�"宸茬‘璁�"鏃跺潎鍙偣鍑汇��
+
+---
+
+## 娉ㄦ剰浜嬮」
+
+- AI 鍒嗘瀽寤鸿浠呬綔鍙傝�冿紝涓嶈嚜鍔ㄤ慨鏀瑰伐鍗曟暟鎹�
+- AI 鍝嶅簲鏃堕棿鍙兘杈冮暱锛�5-15 绉掞級锛屽姟蹇呮樉绀� loading 鐘舵��
+- 4 涓� AI 绔偣鐙珛璋冪敤锛屽墠绔彲鎸夐渶璋冪敤鍏朵腑涓�涓垨澶氫釜
+- `workOrderId` 鍙栧綋鍓嶅伐鍗曡鎯呴〉鐨� `id` 瀛楁
+- 鎵�鏈夌鐐规潈闄愪负 `mes:pro-work-order:query`锛屽綋鍓嶇敤鎴烽渶鏈夌敓浜у伐鍗曟煡璇㈡潈闄�
+- 鍝嶅簲涓殑瀛楁鍧囦负鍙�夛紙AI 瑙f瀽澶辫触鏃跺彲鑳戒负绌猴級锛屽墠绔渶鍋氶槻寰℃�у垽鏂�
+- 鐗╂枡/鏃堕暱/椋庨櫓/浜や粯 4 涓垎鏋愬彲鐙珛浣跨敤锛屾棤鍏堝悗渚濊禆
diff --git a/docs/mes_qc_ai_integration.md b/docs/mes_qc_ai_integration.md
new file mode 100644
index 0000000..3cefecc
--- /dev/null
+++ b/docs/mes_qc_ai_integration.md
@@ -0,0 +1,164 @@
+# MES 璐ㄦ AI 鍔熻兘 - 鍓嶇鑱旇皟鏂规
+
+## 娑夊強椤甸潰
+
+- 鏉ユ枡妫�楠屽崟璇︽儏椤碉紙IQC锛�
+- 杩囩▼妫�楠屽崟璇︽儏椤碉紙IPQC锛�
+- 鍑鸿揣妫�楠屽崟璇︽儏椤碉紙OQC锛�
+- 閫�璐ф楠屽崟璇︽儏椤碉紙RQC锛�
+- 璐ㄦ鏍囧噯鐭ヨ瘑搴撶鐞嗛〉锛堝彲閫夛級
+
+---
+
+## 鍔熻兘涓�锛欰I 杈呭姪鍒ゅ畾妫�楠岀粨鏋�
+
+### 涓氬姟娴佺▼
+
+```
+妫�楠屽憳褰曞叆鎸囨爣妫�娴嬪�� + 缂洪櫡璁板綍
+ 鈫� 鐐瑰嚮"AI 杈呭姪鍒ゅ畾"鎸夐挳
+ 鈫� 璋冪敤 AI 鎺ュ彛鑾峰彇鍒ゅ畾寤鸿
+ 鈫� 寮圭獥灞曠ず AI 寤鸿锛堝垽瀹氱粨鏋� + 鎺ㄧ悊渚濇嵁 + 鍏虫敞鐐癸級
+ 鈫� 妫�楠屽憳鍙傝�冨缓璁紝鎵嬪姩閫夋嫨鏈�缁堝垽瀹氱粨鏋�
+ 鈫� 鐐瑰嚮"瀹屾垚妫�楠�"
+```
+
+### API
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| POST | `/admin-api/mes/qc/ai/suggest-result` | AI 杈呭姪鍒ゅ畾 |
+
+**璇锋眰鍙傛暟锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| qcId | Long | 鏄� | 璐ㄦ鍗� ID |
+| qcType | Integer | 鏄� | 璐ㄦ绫诲瀷锛�1=鏉ユ枡 2=杩囩▼ 3=鍑鸿揣 4=閫�璐� |
+
+**鍝嶅簲瀛楁锛�**
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| suggestedResult | Integer | 寤鸿鍒ゅ畾缂栫爜锛�1鍚堟牸 2鐗归噰 3涓嶅悎鏍奸��璐� 4涓嶅悎鏍兼姤搴� |
+| suggestedResultName | String | 寤鸿鍒ゅ畾鍚嶇О |
+| reasoning | String | AI 鎺ㄧ悊渚濇嵁锛堟枃鏈級 |
+| keyPoints | List\<String\> | 鍏抽敭鍏虫敞鐐规竻鍗� |
+
+**鍝嶅簲绀轰緥锛�**
+```json
+{
+ "code": 0,
+ "data": {
+ "suggestedResult": 1,
+ "suggestedResultName": "鍚堟牸",
+ "reasoning": "鎵�鏈夋娴嬫寚鏍囧潎鍦ㄥ厑璁歌寖鍥村唴锛岀己闄锋暟閲忔湭瓒呰繃涓婇檺锛屽缓璁垽瀹氫负鍚堟牸銆�",
+ "keyPoints": ["澶栬妫�鏌ユ寚鏍囨帴杩戜笂闄愶紝寤鸿鍔犲己鏉ユ枡澶栬绠℃帶"]
+ }
+}
+```
+
+### 瀛楁灞曠ず瑙勫垯
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| suggestedResultName | 寮圭獥椤堕儴閱掔洰鏄剧ず锛堢敤棰滆壊鍖哄垎锛氱豢鑹�=鍚堟牸銆佹鑹�=鐗归噰銆佺孩鑹�=涓嶅悎鏍硷級 | 鍒ゅ畾寤鸿 |
+| reasoning | 寮圭獥姝f枃鍖哄煙 | 鎺ㄧ悊渚濇嵁锛屽睍绀轰负娈佃惤鏂囨湰 |
+| keyPoints | 寮圭獥搴曢儴锛屽垪琛ㄥ睍绀� | 鍏虫敞鐐逛互 icon + 鏂囨湰灞曠ず |
+
+### 涓氬姟瑙勫垯
+
+| 鍦烘櫙 | 瑙勫垯 |
+|------|------|
+| 鐐瑰嚮鎸夐挳 | 鎸夐挳鏄剧ず loading 鐘舵�侊紝**涓嶅彲閲嶅鐐瑰嚮** |
+| AI 杩斿洖鎴愬姛 | 寮圭獥灞曠ず缁撴灉锛屾楠屽憳鑷鍐冲畾鏄惁閲囩撼 |
+| AI 杩斿洖澶辫触 | 鎻愮ず"AI 鍒ゅ畾鏆傛椂涓嶅彲鐢紝璇蜂汉宸ュ垽瀹�"锛屼笉褰卞搷姝e父妫�楠屾祦绋� |
+| AI 鍒ゅ畾瓒呮椂 | 璁剧疆 30 绉掕秴鏃讹紝瓒呮椂鍚庢彁绀虹敤鎴烽噸璇� |
+| 鏈綍鍏ユ娴嬪�� | 鍏佽璋冪敤 AI锛孉I 浼氬熀浜庡凡鏈夌己闄疯褰曠粰鍑哄缓璁� |
+| 宸插畬鎴愭楠� | 涓嶉渶瑕佸睍绀� AI 鍒ゅ畾鎸夐挳锛堝凡瀹屾垚鐘舵�侀殣钘忥級 |
+
+### 鎸夐挳鏀剧疆浣嶇疆
+
+鍦ㄦ楠屽崟璇︽儏椤电殑"瀹屾垚妫�楠�"鎸夐挳鏃佽竟锛屾柊澧� `AI 杈呭姪鍒ゅ畾` 鎸夐挳銆傛寜閽粎鍦ㄨ川妫�鍗曠姸鎬佷负"鑽夌"鏃跺睍绀恒��
+
+---
+
+## 鍔熻兘浜岋細璐ㄦ鏍囧噯鐭ヨ瘑搴� RAG 妫�绱�
+
+### 涓氬姟娴佺▼
+
+```
+绠$悊鍛橈細涓婁紶璐ㄦ鏍囧噯鏂囨。 鈫� 绯荤粺鑷姩鍒嗘+鍚戦噺鍖�
+妫�楠屽憳锛氬湪璐ㄦ椤甸潰鍙充晶"璐ㄦ鍔╂墜"闈㈡澘杈撳叆闂 鈫� 妫�绱㈢浉鍏虫爣鍑嗘钀� 鈫� 灞曠ず鍙傝�冨唴瀹�
+```
+
+### API
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| POST | `/admin-api/ai/knowledge/segment/search` | 鍚戦噺妫�绱㈢煡璇嗗簱鍒嗘 |
+
+**璇锋眰鍙傛暟锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| knowledgeId | Long | 鏄� | 璐ㄦ鏍囧噯鐭ヨ瘑搴� ID |
+| content | String | 鏄� | 妫�绱㈡煡璇㈡枃鏈紙鑷劧璇█闂锛� |
+| topK | Integer | 鍚� | 杩斿洖鏁伴噺锛岄粯璁ゅ彇鐭ヨ瘑搴撻厤缃�硷紙寤鸿 3~5锛� |
+| similarityThreshold | Double | 鍚� | 鐩镐技搴﹂槇鍊� 0~1锛屽缓璁� 0.5~0.7 |
+
+**鍝嶅簲瀛楁锛�**
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| id | Long | 鍒嗘 ID |
+| content | String | 鍖归厤鐨勫垎娈垫枃鏈唴瀹� |
+| score | Double | 鐩镐技搴﹀緱鍒� |
+| documentName | String | 鏉ユ簮鏂囨。鍚嶇О |
+| documentId | Long | 鏉ユ簮鏂囨。 ID |
+
+**鍝嶅簲绀轰緥锛�**
+```json
+{
+ "code": 0,
+ "data": [
+ {
+ "id": 10,
+ "content": "澶栬妫�楠屾爣鍑嗭細琛ㄩ潰搴斿厜婊戝钩鏁达紝鏃犺绾广�佹皵娉°�佹瘺鍒恒�侀攬铓�绛夌己闄�...",
+ "score": 0.85,
+ "documentName": "鏉ユ枡妫�楠屼綔涓氭寚瀵间功_v2.pdf",
+ "documentId": 5
+ }
+ ]
+}
+```
+
+### 鍓嶇疆鍑嗗
+
+1. 鍦� AI 鐭ヨ瘑搴撶鐞嗕腑鍒涘缓鍚嶇О涓�"璐ㄦ鏍囧噯鐭ヨ瘑搴�"鐨勭煡璇嗗簱
+2. 涓婁紶璐ㄦ鐩稿叧鏂囨。锛堝浗鏍囥�佷紒鏍囥�佷綔涓氭寚瀵间功绛夛級
+3. 璁板綍鐭ヨ瘑搴� ID锛屽墠绔厤缃负甯搁噺鎴栭�氳繃鎺ュ彛鑾峰彇
+
+### 瀛楁灞曠ず瑙勫垯
+
+| 瀛楁 | 灞曠ず浣嶇疆 | 璇存槑 |
+|------|----------|------|
+| content | 缁撴灉鍗$墖姝f枃 | 楂樹寒鍛戒腑鍏抽敭璇� |
+| score | 缁撴灉鍗$墖搴曢儴灏忓瓧 | 鏍煎紡鍖栦负鐧惧垎姣旓紝濡�"鐩镐技搴� 85%" |
+| documentName | 缁撴灉鍗$墖椤堕儴 | 鐐瑰嚮鍙烦杞埌鏂囨。棰勮椤� |
+
+---
+
+## 娉ㄦ剰浜嬮」
+
+### AI 鍒ゅ畾
+- AI 寤鸿浠呬綔**鍙傝��**锛屾渶缁堝垽瀹氱敱妫�楠屽憳鍐冲畾锛屼笉鑷姩鍐欏叆
+- AI 鍝嶅簲鏃堕棿鍙兘杈冮暱锛�5~15 绉掞級锛屽墠绔渶鏄剧ず loading 鐘舵��
+- `qcType` 鍙栧�硷細IQC 璇︽儏椤典紶 1銆両PQC 璇︽儏椤典紶 2銆丱QC 璇︽儏椤典紶 3銆丷QC 璇︽儏椤典紶 4
+- `qcId` 鍙栧綋鍓嶈川妫�鍗曠殑 `id` 瀛楁
+
+### 鐭ヨ瘑搴撴绱�
+- 鐭ヨ瘑搴� ID 闇�鍏堝垱寤虹煡璇嗗簱骞朵笂浼犳枃妗e悗鎵嶈兘浣跨敤
+- 濡傛灉妫�绱㈡棤缁撴灉锛堢┖鏁扮粍锛夛紝灞曠ず"鏈壘鍒扮浉鍏虫爣鍑嗭紝璇峰皾璇曞叾浠栧叧閿瘝"
+- 寤鸿鍦ㄨ川妫�褰曞叆椤甸潰鍙充晶鐢ㄥ彲鎶樺彔闈㈡澘灞曠ず锛屼笉褰卞搷涓绘搷浣滃尯鍩�
+- 妫�绱㈣緭鍏ユ寤鸿 placeholder锛�"杈撳叆闂锛屽锛氬瑙傛楠屾爣鍑嗘槸浠�涔堬紵"
diff --git a/docs/oa_notice_frontend.md b/docs/oa_notice_frontend.md
new file mode 100644
index 0000000..2befdc1
--- /dev/null
+++ b/docs/oa_notice_frontend.md
@@ -0,0 +1,209 @@
+# OA 閫氱煡鍏憡 - 鍓嶇鑱旇皟鏂规
+
+## 娑夊強椤甸潰
+
+| 椤甸潰 | 璺緞 | 璇存槑 |
+|------|------|------|
+| 閫氱煡鍏憡鍒楄〃 | `/bpm/oa/notice` | 鏂板缓锛歚src/views/bpm/oa/notice/index.vue` |
+| 閫氱煡鍏憡璇︽儏 | `/bpm/oa/notice/detail` | 鏂板缓锛歚src/views/bpm/oa/notice/detail.vue` |
+
+## API
+
+| 鏂规硶 | 璺緞 | 鏉冮檺 | 璇存槑 |
+|------|------|------|------|
+| POST | `/bpm/oa/notice/create` | `bpm:oa-notice:create` | 鍒涘缓 |
+| PUT | `/bpm/oa/notice/update` | `bpm:oa-notice:update` | 淇敼 |
+| DELETE | `/bpm/oa/notice/delete` | `bpm:oa-notice:delete` | 鍒犻櫎 |
+| GET | `/bpm/oa/notice/page` | `bpm:oa-notice:query` | 鍒嗛〉鍒楄〃 |
+| GET | `/bpm/oa/notice/get` | `bpm:oa-notice:query` | 璇︽儏锛堣皟鐢ㄥ嵆鏍囪宸茶锛� |
+| GET | `/bpm/oa/notice/my-unread-count` | - | 鎴戠殑鏈鏁伴噺 |
+
+### 璇锋眰鍙傛暟
+
+**POST `/bpm/oa/notice/create` & PUT `/bpm/oa/notice/update`锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| id | Long | 鍚︼紙鏇存柊鏃跺繀濉級 | 閫氱煡鍏憡ID |
+| title | String | 鏄� | 鏍囬锛屾渶闀�50瀛楃 |
+| content | String | 鏄� | 鍐呭 |
+| type | Integer | 鏄� | 绫诲瀷锛�1=閫氱煡 2=鍏憡 |
+| status | Integer | 鏄� | 鐘舵�侊細0=鑽夌 1=宸插彂甯� 2=宸叉挙閿� |
+| sendScope | Integer | 鏄� | 鍙戦�佽寖鍥达細1=閫氱煡鎵�鏈変汉 2=鎸囧畾浜� |
+| isTop | Integer | 鍚� | 鏄惁缃《锛�0=鍚� 1=鏄� |
+| userIds | List\<Long\> | 鍚︼紙sendScope=2鏃跺繀濉級 | 鎸囧畾鐢ㄦ埛ID鍒楄〃 |
+
+**GET `/bpm/oa/notice/page`锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| title | String | 鍚� | 鏍囬锛屾ā绯婂尮閰� |
+| type | Integer | 鍚� | 绫诲瀷 |
+| status | Integer | 鍚� | 鐘舵�� |
+| sendScope | Integer | 鍚� | 鍙戦�佽寖鍥� |
+| pageNo | Integer | 鍚� | 椤电爜锛岄粯璁�1 |
+| pageSize | Integer | 鍚� | 姣忛〉鏉℃暟锛岄粯璁�10 |
+
+**GET `/bpm/oa/notice/get`锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| id | Long | 鏄� | 閫氱煡鍏憡ID |
+
+**DELETE `/bpm/oa/notice/delete`锛�**
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| id | Long | 鏄� | 閫氱煡鍏憡ID |
+
+### 鍝嶅簲鏍煎紡
+
+**鍒嗛〉鍒楄〃 `GET /bpm/oa/notice/page` 鍝嶅簲锛�**
+
+```json
+{
+ "code": 0,
+ "data": {
+ "list": [
+ {
+ "id": 1,
+ "title": "鍏充簬鏀惧亣鐨勯�氱煡",
+ "content": "鏍规嵁鍥藉瑙勫畾...",
+ "type": 1,
+ "status": 1,
+ "sendScope": 1,
+ "isTop": 0,
+ "publishUserId": 1,
+ "publishTime": "2025-01-01 10:00:00",
+ "createTime": "2025-01-01 10:00:00"
+ }
+ ],
+ "total": 100
+ }
+}
+```
+
+**璇︽儏 `GET /bpm/oa/notice/get` 鍝嶅簲锛�**
+
+涓庡垪琛ㄩ」鐩稿悓缁撴瀯锛岄澶栬繑鍥烇細
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| userIds | List\<Long\> | 鎸囧畾鐢ㄦ埛ID鍒楄〃锛堜粎 sendScope=2 鏃舵湁鍊硷級 |
+
+**鎴戠殑鏈鏁伴噺 `GET /bpm/oa/notice/my-unread-count` 鍝嶅簲锛�**
+
+```json
+{
+ "code": 0,
+ "data": 5
+}
+```
+
+## 瀛楁灞曠ず瑙勫垯
+
+| 瀛楁 | 鍒楄〃 | 璇︽儏 | 璇存槑 |
+|------|------|------|------|
+| title | 鉁� | 鉁� | 閫氱煡鍏憡鏍囬锛岀疆椤堕」鍓嶅姞 `[缃《]` 鏍囪瘑 |
+| type | 鉁� | 鉁� | 1=閫氱煡 2=鍏憡锛岀敤 Tag 鍖哄垎棰滆壊 |
+| status | 鉁� | 鉁� | 0=鑽夌锛堢伆鑹诧級1=宸插彂甯冿紙缁胯壊锛�2=宸叉挙閿�锛堢孩鑹诧級|
+| sendScope | 鉁� | 鉁� | 1=閫氱煡鎵�鏈変汉 2=鎸囧畾浜� |
+| isTop | 鉁� | 鉁� | 缃《椤规帓鍦ㄥ墠闈� |
+| publishUserId | - | 鉁� | 鍙戝竷浜哄鍚嶏紙闇�鍏宠仈鐢ㄦ埛琛ㄥ睍绀猴級 |
+| publishTime | 鉁� | 鉁� | 鍙戝竷鏃堕棿 |
+| createTime | 鉁� | 鉁� | 鍒涘缓鏃堕棿 |
+| content | - | 鉁� | 瀵屾枃鏈唴瀹� |
+| userIds | - | 鉁� | 浠� sendScope=2 鏃跺睍绀烘寚瀹氫汉鍛樺垪琛� |
+
+## 涓氬姟瑙勫垯璇存槑
+
+| 鍦烘櫙 | 瑙勫垯 |
+|------|------|
+| 鍒涘缓鏃堕�夋嫨銆岄�氱煡鎵�鏈変汉銆� | 鎵�鏈夌櫥褰曠敤鎴峰湪鍒楄〃椤靛彲瑙佹鍏憡 |
+| 鍒涘缓鏃堕�夋嫨銆屾寚瀹氫汉銆嶄笖鐘舵�佷负銆屽凡鍙戝竷銆� | 浠呭垱寤鸿��+琚寚瀹氱敤鎴峰彲瑙侊紱绯荤粺鑷姩缁欐寚瀹氫汉鍙戦�佺珯鍐呬俊 |
+| 鍒涘缓鏃剁姸鎬佷负銆岃崏绋裤�� | 浠呭垱寤鸿�呮湰浜哄彲瑙侊紝涓嶅彂閫侀�氱煡 |
+| 鐐瑰嚮璇︽儏 | 鑷姩鏍囪褰撳墠鐢ㄦ埛銆屽凡璇汇�嶏紝鍐嶆鎵撳紑涓嶉噸澶嶈褰� |
+| 鍒楄〃鎺掑簭 | 鎸� isTop 闄嶅簭锛屽啀鎸� id 闄嶅簭 |
+| 淇敼鍏憡 | 濡傛灉鍘熸潵鏄崏绋挎敼涓哄凡鍙戝竷锛屼細瑙﹀彂鍙戝竷鏃堕棿鏇存柊鍜岄�氱煡鍙戦�� |
+| 鎾ら攢鍏憡 | 灏嗙姸鎬佹敼涓恒�屽凡鎾ら攢銆嶅悗锛屽垪琛ㄤ粛鍙浣嗘樉绀轰负鎾ら攢鐘舵�� |
+| 閾冮摏閫氱煡 | 鎸囧畾浜烘ā寮忓彂甯冩椂鍙戠珯鍐呬俊锛堟ā鏉� code: `oa-notice-publish`锛夛紝澶撮儴閾冮摏鍙樉绀烘湭璇绘暟锛涢�氱煡鎵�鏈変汉妯″紡涓嶉�愭潯鎺ㄩ�� |
+
+## API 灏佽鍙傝��
+
+鏂板缓 `src/api/bpm/oa/notice/index.ts`锛屽弬鐓� `src/api/bpm/oa/leave/index.ts` 妯″紡锛�
+
+```typescript
+import { requestClient } from '#/api/request';
+
+export namespace OaNoticeApi {
+ export interface NoticeVO {
+ id: number;
+ title: string;
+ content: string;
+ type: number;
+ status: number;
+ sendScope: number;
+ isTop: number;
+ publishUserId: number;
+ publishTime: string;
+ createTime: string;
+ userIds?: number[];
+ }
+
+ export interface NoticeSaveReqVO {
+ id?: number;
+ title: string;
+ content: string;
+ type: number;
+ status: number;
+ sendScope: number;
+ isTop?: number;
+ userIds?: number[];
+ }
+}
+
+const BASE = '/bpm/oa/notice';
+
+export function getNoticePage(params: any) {
+ return requestClient.get(`${BASE}/page`, { params });
+}
+
+export function getNotice(id: number) {
+ return requestClient.get(`${BASE}/get`, { params: { id } });
+}
+
+export function createNotice(data: OaNoticeApi.NoticeSaveReqVO) {
+ return requestClient.post(`${BASE}/create`, data);
+}
+
+export function updateNotice(data: OaNoticeApi.NoticeSaveReqVO) {
+ return requestClient.put(`${BASE}/update`, data);
+}
+
+export function deleteNotice(id: number) {
+ return requestClient.delete(`${BASE}/delete`, { params: { id } });
+}
+
+export function getMyUnreadNoticeCount() {
+ return requestClient.get(`${BASE}/my-unread-count`);
+}
+```
+
+## 椤甸潰鏂囦欢寤鸿
+
+| 鏂囦欢 | 璺緞 |
+|------|------|
+| 鍒楄〃椤� | `src/views/bpm/oa/notice/index.vue` |
+| 璇︽儏椤� | `src/views/bpm/oa/notice/detail.vue` |
+| 琛ㄥ崟寮圭獥 | `src/views/bpm/oa/notice/modules/form.vue` |
+| 鍒楄〃閰嶇疆 | `src/views/bpm/oa/notice/data.ts` |
+
+## 娉ㄦ剰浜嬮」
+
+- 鍒楄〃椤电殑鍙鏁版嵁鐢卞悗绔寜 sendScope + 褰撳墠鐢ㄦ埛鑷姩杩囨护锛屽墠绔棤闇�棰濆澶勭悊
+- 鍒涘缓/缂栬緫琛ㄥ崟涓紝銆屾寚瀹氫汉銆嶉�夋嫨鍣ㄤ粎鍦� sendScope=2 鏃舵樉绀�
+- 琛ㄥ崟鎻愪氦鏃� status 鍙洿鎺ラ�夋嫨銆屽凡鍙戝竷銆嶏紝姝ゆ椂浼氱珛鍗宠Е鍙戦�氱煡鍙戦��
+- 宸茶鐘舵�佺敱鍚庣鍦ㄨ皟鐢� get 鎺ュ彛鏃惰嚜鍔ㄦ爣璁帮紝鍓嶇鏃犻渶璋冪敤鏍囪宸茶鎺ュ彛
+- 澶撮儴閾冮摏鐨勬湭璇绘暟濡傛灉鑳借幏鍙� `getUnreadNotifyMessageCount`锛屽彲浠ュ彔鍔� `getMyUnreadNoticeCount` 鐨勭粨鏋滃睍绀�
+- 鍚庣鑿滃崟闇�瑕侀厤缃� `component` 瀛楁涓� `bpm/oa/notice/index`锛屾潈闄愭爣璇嗗垎鍒负 `bpm:oa-notice:create/update/delete/query`
+- 鍙傝�冪幇鏈夐〉闈細鍒楄〃椤靛弬鐓� `src/views/system/notice/index.vue`锛岃〃鍗曞脊绐楀弬鐓� `src/views/bpm/oa/leave/create.vue`
diff --git "a/docs/test-quotation-files/\345\215\227\351\200\232\345\256\242\346\210\267\346\212\245\344\273\267\345\215\225.csv" "b/docs/test-quotation-files/\345\215\227\351\200\232\345\256\242\346\210\267\346\212\245\344\273\267\345\215\225.csv"
new file mode 100644
index 0000000..bfdfb27
--- /dev/null
+++ "b/docs/test-quotation-files/\345\215\227\351\200\232\345\256\242\346\210\267\346\212\245\344\273\267\345\215\225.csv"
@@ -0,0 +1,14 @@
+鎶ヤ环鍗曞悕绉�: 鍗楅�氬鎴烽挗鑺摑缁炵嚎閲囪喘鎶ヤ环
+瀹㈡埛鍚嶇О: 鍗楅�氬鎴�
+鎶ヤ环鏃ユ湡: 2026-07-31
+鏈夋晥鏈熻嚦: 2026-08-30
+绋庣巼: 13
+鎶樻墸鐜�: 5
+澶囨敞: 鍚◣鍚繍璐癸紝30澶╄处鏈�
+
+搴忓彿,鐗╂枡鍚嶇О,瑙勬牸鍨嬪彿,鏁伴噺,鍗曚环(鍏�)
+1,閾濇潌,1A60,500,25.5
+2,閾濆崟绾�,L-1.72,300,18.2
+3,閽㈣姱閾濈粸绾�,JL/L1-40/10-6/1,2000,8.8
+4,闀�閿岄挗绾�,JG3A-1*1.5,800,6.5
+5,閾濆悎瀵间綋,LH8030JY-10,1500,15.3
diff --git "a/docs/test-quotation-files/\346\265\213\350\257\225\345\256\242\346\210\267\346\212\245\344\273\267\345\215\225.txt" "b/docs/test-quotation-files/\346\265\213\350\257\225\345\256\242\346\210\267\346\212\245\344\273\267\345\215\225.txt"
new file mode 100644
index 0000000..7a01589
--- /dev/null
+++ "b/docs/test-quotation-files/\346\265\213\350\257\225\345\256\242\346\210\267\346\212\245\344\273\267\345\215\225.txt"
@@ -0,0 +1,35 @@
+===========================================
+ 鐢电嚎鐢电紗鎶ヤ环鍗�
+===========================================
+
+鎶ヤ环鏃ユ湡锛�2026骞�7鏈�31鏃�
+鏈夋晥鏈熻嚦锛�2026骞�9鏈�15鏃�
+
+瀹㈡埛鍚嶇О锛氭祴璇�
+鑱旂郴浜虹數璇濓細13598585685
+
+-------------------------------------------
+涓�銆佹姤浠疯鏄�
+-------------------------------------------
+1. 鏈姤浠峰惈13%澧炲�肩◣
+2. 鎶ヤ环鏈夋晥鏈�45澶�
+3. 浠樻鏂瑰紡锛氭湀缁�30澶�
+4. 浜よ揣鏈燂細10涓伐浣滄棩
+
+-------------------------------------------
+浜屻�佹姤浠锋槑缁�
+-------------------------------------------
+鐗╂枡鍚嶇О 瑙勬牸鍨嬪彿 鏁伴噺 鍗曚环(鍏�)
+------------------------------------------------------
+閾濇潌 1A60 1000鍗冨厠 25.50
+閾濆崟绾� L-1.72 500鍗冨厠 18.20
+閽㈣姱閾濈粸绾� JL/L1-40/10-6/1 3000鍗冨厠 8.80
+闀�閿岄挗绾� JG3A-1*1.5 1200鍗冨厠 6.50
+閾濆悎瀵间綋 LH8030JY-10 800鍗冪背 15.30
+
+-------------------------------------------
+涓夈�佸娉�
+-------------------------------------------
+1. 鏁翠綋鎶樻墸鐜囷細5%
+2. 浠ヤ笂浠锋牸鍚◣鍚繍璐�
+3. 璐ㄤ繚鏈�12涓湀
diff --git "a/docs/test-quotation-files/\350\201\212\345\244\251\350\256\260\345\275\225\347\224\237\346\210\220\346\217\220\347\244\272\350\257\215.txt" "b/docs/test-quotation-files/\350\201\212\345\244\251\350\256\260\345\275\225\347\224\237\346\210\220\346\217\220\347\244\272\350\257\215.txt"
new file mode 100644
index 0000000..067a392
--- /dev/null
+++ "b/docs/test-quotation-files/\350\201\212\345\244\251\350\256\260\345\275\225\347\224\237\346\210\220\346\217\220\347\244\272\350\257\215.txt"
@@ -0,0 +1,15 @@
+鐢熸垚涓�寮犲井淇¤亰澶╂埅鍥撅紝鍐呭濡備笅锛�
+
+瀵规柟锛堝鎴锋矡閫氾級锛�
+"鏉庣粡鐞嗭紝杩欐槸杩欐瑕侀噰璐殑娓呭崟锛岄夯鐑︾粰涓姤浠峰惂"
+"涓�鍏�5绉�"
+"閾濇潌锛岃鏍�1A60锛岃1000鍗冨厠"
+"閾濆崟绾匡紝瑙勬牸L-1.72锛岃500鍗冨厠"
+"閽㈣姱閾濈粸绾匡紝瑙勬牸JL/L1-40/10-6/1锛岃3000鍗冨厠"
+"闀�閿岄挗绾匡紝瑙勬牸JG3A-1*1.5锛岃1200鍗冨厠"
+"閾濆悎瀵间綋锛岃鏍糒H8030JY-10锛岃800鍗冪背"
+
+鑷繁锛堟垜鏂归攢鍞級锛�
+"鏀跺埌寮犳�伙紝鎴戞暣鐞嗕竴涓嬫槑澶╃粰鎮ㄥ嚭姝e紡鎶ヤ环鍗�"
+"绋庣巼13%锛屾暣浣撶粰鎮ㄦ墦95鎶橈紝鍚◣鍚繍璐�"
+"鏈夋晥鏈熷埌9鏈�15鍙�"
diff --git a/pom.xml b/pom.xml
index 72800d2..ee58c37 100644
--- a/pom.xml
+++ b/pom.xml
@@ -42,7 +42,8 @@
<module>yudao-module-aftersales-api</module>
<module>yudao-module-aftersales</module>
<!-- 璇峰弬鑰� https://doc.iocoder.cn/ai/build/ 鏂囨。锛屽畬鎴� AI 妯″潡鐨勫惎鍔紒锛侊紒 -->
-<!-- <module>yudao-module-ai</module>-->
+ <module>yudao-module-ai-api</module>
+ <module>yudao-module-ai</module>
</modules>
<name>${project.artifactId}</name>
diff --git a/sql/mysql/oa_notice_init.sql b/sql/mysql/oa_notice_init.sql
new file mode 100644
index 0000000..fcd736c
--- /dev/null
+++ b/sql/mysql/oa_notice_init.sql
@@ -0,0 +1,57 @@
+-- =============================================
+-- OA 閫氱煡鍏憡妯″潡鍒濆鍖�
+-- =============================================
+
+CREATE TABLE IF NOT EXISTS oa_notice (
+ id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '涓婚敭ID',
+ title VARCHAR(50) NOT NULL COMMENT '鍏憡鏍囬',
+ content TEXT NOT NULL COMMENT '鍏憡鍐呭',
+ type TINYINT NOT NULL COMMENT '鍏憡绫诲瀷锛�1閫氱煡 2鍏憡锛�',
+ status TINYINT NOT NULL DEFAULT 0 COMMENT '鐘舵�侊紙0鑽夌 1宸插彂甯� 2宸叉挙閿�锛�',
+ send_scope TINYINT NOT NULL COMMENT '鍙戦�佽寖鍥达紙1閫氱煡鎵�鏈変汉 2鎸囧畾浜猴級',
+ is_top TINYINT NOT NULL DEFAULT 0 COMMENT '鏄惁缃《锛�0鍚� 1鏄級',
+ publish_user_id BIGINT COMMENT '鍙戝竷浜篒D',
+ publish_time DATETIME COMMENT '鍙戝竷鏃堕棿',
+ creator VARCHAR(64) DEFAULT '' COMMENT '鍒涘缓鑰�',
+ create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+ updater VARCHAR(64) DEFAULT '' COMMENT '鏇存柊鑰�',
+ update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
+ deleted BIT DEFAULT 0 COMMENT '鏄惁鍒犻櫎',
+ INDEX idx_type (type),
+ INDEX idx_status (status),
+ INDEX idx_publish_time (publish_time),
+ INDEX idx_is_top (is_top)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='OA閫氱煡鍏憡琛�';
+
+CREATE TABLE IF NOT EXISTS oa_notice_user (
+ id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '涓婚敭ID',
+ notice_id BIGINT NOT NULL COMMENT '閫氱煡鍏憡ID',
+ user_id BIGINT NOT NULL COMMENT '鎸囧畾鐢ㄦ埛ID',
+ creator VARCHAR(64) DEFAULT '' COMMENT '鍒涘缓鑰�',
+ create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+ updater VARCHAR(64) DEFAULT '' COMMENT '鏇存柊鑰�',
+ update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
+ deleted BIT DEFAULT 0 COMMENT '鏄惁鍒犻櫎',
+ INDEX idx_notice_id (notice_id),
+ INDEX idx_user_id (user_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='OA閫氱煡鍏憡鎸囧畾浜哄叧鑱旇〃';
+
+CREATE TABLE IF NOT EXISTS oa_notice_read (
+ id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '涓婚敭ID',
+ notice_id BIGINT NOT NULL COMMENT '閫氱煡鍏憡ID',
+ user_id BIGINT NOT NULL COMMENT '鐢ㄦ埛ID',
+ read_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '闃呰鏃堕棿',
+ creator VARCHAR(64) DEFAULT '' COMMENT '鍒涘缓鑰�',
+ create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+ updater VARCHAR(64) DEFAULT '' COMMENT '鏇存柊鑰�',
+ update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
+ deleted BIT DEFAULT 0 COMMENT '鏄惁鍒犻櫎',
+ UNIQUE KEY uk_notice_user (notice_id, user_id),
+ INDEX idx_notice_id (notice_id),
+ INDEX idx_user_id (user_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='OA閫氱煡鍏憡宸茶璁板綍琛�';
+
+-- 绯荤粺閫氱煡妯℃澘锛堢敤浜嶰A閫氱煡鍏憡鍙戝竷鏃跺彂閫佺珯鍐呬俊锛�
+INSERT INTO system_notify_template (name, code, nickname, content, type, params, status, creator, create_time, updater, update_time, deleted)
+VALUES ('OA閫氱煡鍏憡鎻愰啋', 'oa-notice-publish', '绯荤粺閫氱煡', '鎮ㄦ湁涓�鏉℃柊鐨勯�氱煡鍏憡锛歿title}', 1, '["title"]', 0, '', NOW(), '', NOW(), 0)
+ON DUPLICATE KEY UPDATE id = id;
diff --git a/yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/controller/admin/ticket/vo/ticket/AfterSaleTicketSaveReqVO.java b/yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/controller/admin/ticket/vo/ticket/AfterSaleTicketSaveReqVO.java
index c19012e..deb2ab0 100644
--- a/yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/controller/admin/ticket/vo/ticket/AfterSaleTicketSaveReqVO.java
+++ b/yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/controller/admin/ticket/vo/ticket/AfterSaleTicketSaveReqVO.java
@@ -38,9 +38,18 @@
@Schema(description = "鍚堝悓缂栧彿", example = "1")
private Long contractId;
+ @Schema(description = "闂绫诲瀷: 1-涓�鑸棶棰� 2-缁翠慨闂 3-閫�璐ч棶棰�", example = "1")
+ private Integer issueType;
+
+ @Schema(description = "闂鍒嗙被", example = "澶栬缂洪櫡")
+ private String issueCategory;
+
@Schema(description = "闂鎻忚堪", example = "鏀跺埌璐у悗鍙戠幇浜у搧琛ㄩ潰鏈夊垝鐥�")
private String issueDescription;
+ @Schema(description = "涓ラ噸绋嬪害: 0-杞诲井 1-涓�鑸� 2-涓ラ噸 3-鑷村懡", example = "1")
+ private Integer severityLevel;
+
@Schema(description = "澶囨敞", example = "瀹㈡埛瑕佹眰灏藉揩澶勭悊")
private String remark;
diff --git a/yudao-module-ai-api/pom.xml b/yudao-module-ai-api/pom.xml
new file mode 100644
index 0000000..374985a
--- /dev/null
+++ b/yudao-module-ai-api/pom.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>cn.iocoder.boot</groupId>
+ <artifactId>yudao</artifactId>
+ <version>${revision}</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>yudao-module-ai-api</artifactId>
+ <packaging>jar</packaging>
+
+ <name>${project.artifactId}</name>
+ <description>
+ ai 妯″潡涓嬶紝AI 鑳藉姏鐨� API 鎺ュ彛瀹氫箟锛屼緵鍏朵粬妯″潡寮曠敤
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>cn.iocoder.boot</groupId>
+ <artifactId>yudao-common</artifactId>
+ </dependency>
+
+ <!-- Swagger 娉ㄨВ -->
+ <dependency>
+ <groupId>org.springdoc</groupId>
+ <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/api/chat/AiChatApi.java b/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/api/chat/AiChatApi.java
new file mode 100644
index 0000000..bebfa78
--- /dev/null
+++ b/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/api/chat/AiChatApi.java
@@ -0,0 +1,28 @@
+package cn.iocoder.yudao.module.ai.api.chat;
+
+/**
+ * AI 瀵硅瘽 API锛屼緵鍏朵粬妯″潡璋冪敤
+ */
+public interface AiChatApi {
+
+ /**
+ * 鍙戦�佸璇濊姹傦紙绾枃鏈級
+ *
+ * @param systemPrompt 绯荤粺鎻愮ず璇嶏紙瑙掕壊璁惧畾锛�
+ * @param userMessage 鐢ㄦ埛娑堟伅
+ * @return AI 鍥炲鍐呭
+ */
+ String chat(String systemPrompt, String userMessage);
+
+ /**
+ * 鍙戦�佸甫鍥剧墖鐨勫璇濊姹傦紙澶氭ā鎬佽瑙夎瘑鍒級
+ *
+ * @param systemPrompt 绯荤粺鎻愮ず璇嶏紙瑙掕壊璁惧畾锛�
+ * @param userMessage 鐢ㄦ埛娑堟伅
+ * @param imageBase64 鍥剧墖 base64 缂栫爜
+ * @param mimeType 鍥剧墖 MIME 绫诲瀷锛堝 image/png锛�
+ * @return AI 鍥炲鍐呭
+ */
+ String chatWithImage(String systemPrompt, String userMessage, String imageBase64, String mimeType);
+
+}
diff --git a/yudao-module-ai/pom.xml b/yudao-module-ai/pom.xml
index 2eac23a..409a9c3 100644
--- a/yudao-module-ai/pom.xml
+++ b/yudao-module-ai/pom.xml
@@ -28,6 +28,11 @@
<dependencies>
<dependency>
<groupId>cn.iocoder.boot</groupId>
+ <artifactId>yudao-module-ai-api</artifactId>
+ <version>${revision}</version>
+ </dependency>
+ <dependency>
+ <groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-module-system</artifactId>
<version>${revision}</version>
</dependency>
@@ -111,13 +116,6 @@
<artifactId>spring-ai-starter-model-stability-ai</artifactId>
<version>${spring-ai.version}</version>
</dependency>
- <dependency>
- <!-- 閫氫箟鍗冮棶 -->
- <groupId>com.alibaba.cloud.ai</groupId>
- <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
- <version>${alibaba-ai.version}</version>
- </dependency>
-
<!-- 鍚戦噺瀛樺偍锛歨ttps://db-engines.com/en/ranking/vector+dbms -->
<dependency>
<!-- Qdrant锛歨ttps://qdrant.tech/ -->
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/api/chat/AiChatApiImpl.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/api/chat/AiChatApiImpl.java
new file mode 100644
index 0000000..22f4291
--- /dev/null
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/api/chat/AiChatApiImpl.java
@@ -0,0 +1,68 @@
+package cn.iocoder.yudao.module.ai.api.chat;
+
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
+import cn.iocoder.yudao.module.ai.enums.model.AiModelTypeEnum;
+import cn.iocoder.yudao.module.ai.service.model.AiModelService;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.ai.chat.messages.SystemMessage;
+import org.springframework.ai.chat.messages.UserMessage;
+import org.springframework.ai.chat.model.ChatModel;
+import org.springframework.ai.chat.prompt.Prompt;
+import org.springframework.ai.content.Media;
+import org.springframework.core.io.ByteArrayResource;
+import org.springframework.stereotype.Service;
+import org.springframework.util.MimeTypeUtils;
+
+import java.util.Base64;
+import java.util.List;
+
+@Service
+@Slf4j
+public class AiChatApiImpl implements AiChatApi {
+
+ @Resource
+ private AiModelService modelService;
+
+ @Override
+ public String chat(String systemPrompt, String userMessage) {
+ AiModelDO defaultModel = modelService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType());
+ ChatModel chatModel = modelService.getChatModel(defaultModel.getId());
+ Prompt prompt = new Prompt(List.of(
+ new SystemMessage(systemPrompt),
+ new UserMessage(userMessage)));
+ String result = chatModel.call(prompt).getResult().getOutput().getText();
+ log.info("AI Chat 璋冪敤瀹屾垚锛屾ā鍨�: {}, 鍥炲闀垮害: {}", defaultModel.getModel(),
+ result != null ? result.length() : 0);
+ return result;
+ }
+
+ @Override
+ public String chatWithImage(String systemPrompt, String userMessage, String imageBase64, String mimeType) {
+ // 浼樺厛閫夋嫨瑙嗚妯″瀷锛堟ā鍨嬪悕鍚� vl锛夛紝鍚﹀垯鍥為��鍒伴粯璁ゅ璇濇ā鍨�
+ List<AiModelDO> chatModels = modelService.getModelListByStatusAndType(
+ CommonStatusEnum.ENABLE.getStatus(), AiModelTypeEnum.CHAT.getType(), null);
+ AiModelDO visionModel = chatModels.stream()
+ .filter(m -> m.getModel() != null && m.getModel().contains("vl"))
+ .findFirst()
+ .orElseGet(() -> modelService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType()));
+
+ ChatModel chatModel = modelService.getChatModel(visionModel.getId());
+ byte[] imageBytes = Base64.getDecoder().decode(imageBase64);
+ Media media = new Media(MimeTypeUtils.parseMimeType(mimeType),
+ new ByteArrayResource(imageBytes));
+ UserMessage imageMessage = UserMessage.builder()
+ .text(userMessage)
+ .media(List.of(media))
+ .build();
+ Prompt prompt = new Prompt(List.of(
+ new SystemMessage(systemPrompt),
+ imageMessage));
+ String result = chatModel.call(prompt).getResult().getOutput().getText();
+ log.info("AI Chat(澶氭ā鎬�) 璋冪敤瀹屾垚锛屾ā鍨�: {}, 鍥炲闀垮害: {}", visionModel.getModel(),
+ result != null ? result.length() : 0);
+ return result;
+ }
+
+}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatConversationController.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatConversationController.java
deleted file mode 100644
index 1450fe9..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatConversationController.java
+++ /dev/null
@@ -1,118 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.chat;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.util.ObjUtil;
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
-import cn.iocoder.yudao.module.ai.service.chat.AiChatConversationService;
-import cn.iocoder.yudao.module.ai.service.chat.AiChatMessageService;
-import org.dromara.core.trans.anno.TransMethodResult;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.Parameter;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import jakarta.annotation.Resource;
-import jakarta.validation.Valid;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.List;
-import java.util.Map;
-
-import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
-import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
-
-@Tag(name = "绠$悊鍚庡彴 - AI 鑱婂ぉ瀵硅瘽")
-@RestController
-@RequestMapping("/ai/chat/conversation")
-@Validated
-public class AiChatConversationController {
-
- @Resource
- private AiChatConversationService chatConversationService;
- @Resource
- private AiChatMessageService chatMessageService;
-
- @PostMapping("/create-my")
- @Operation(summary = "鍒涘缓銆愭垜鐨勩�戣亰澶╁璇�")
- public CommonResult<Long> createChatConversationMy(@RequestBody @Valid AiChatConversationCreateMyReqVO createReqVO) {
- return success(chatConversationService.createChatConversationMy(createReqVO, getLoginUserId()));
- }
-
- @PutMapping("/update-my")
- @Operation(summary = "鏇存柊銆愭垜鐨勩�戣亰澶╁璇�")
- public CommonResult<Boolean> updateChatConversationMy(@RequestBody @Valid AiChatConversationUpdateMyReqVO updateReqVO) {
- chatConversationService.updateChatConversationMy(updateReqVO, getLoginUserId());
- return success(true);
- }
-
- @GetMapping("/my-list")
- @Operation(summary = "鑾峰緱銆愭垜鐨勩�戣亰澶╁璇濆垪琛�")
- @TransMethodResult
- public CommonResult<List<AiChatConversationRespVO>> getChatConversationMyList() {
- List<AiChatConversationDO> list = chatConversationService.getChatConversationListByUserId(getLoginUserId());
- return success(BeanUtils.toBean(list, AiChatConversationRespVO.class));
- }
-
- @GetMapping("/get-my")
- @Operation(summary = "鑾峰緱銆愭垜鐨勩�戣亰澶╁璇�")
- @Parameter(name = "id", required = true, description = "瀵硅瘽缂栧彿", example = "1024")
- @TransMethodResult
- public CommonResult<AiChatConversationRespVO> getChatConversationMy(@RequestParam("id") Long id) {
- AiChatConversationDO conversation = chatConversationService.getChatConversation(id);
- if (conversation != null && ObjUtil.notEqual(conversation.getUserId(), getLoginUserId())) {
- conversation = null;
- }
- return success(BeanUtils.toBean(conversation, AiChatConversationRespVO.class));
- }
-
- @DeleteMapping("/delete-my")
- @Operation(summary = "鍒犻櫎鑱婂ぉ瀵硅瘽")
- @Parameter(name = "id", required = true, description = "瀵硅瘽缂栧彿", example = "1024")
- public CommonResult<Boolean> deleteChatConversationMy(@RequestParam("id") Long id) {
- chatConversationService.deleteChatConversationMy(id, getLoginUserId());
- return success(true);
- }
-
- @DeleteMapping("/delete-by-unpinned")
- @Operation(summary = "鍒犻櫎鏈疆椤剁殑鑱婂ぉ瀵硅瘽")
- public CommonResult<Boolean> deleteChatConversationMyByUnpinned() {
- chatConversationService.deleteChatConversationMyByUnpinned(getLoginUserId());
- return success(true);
- }
-
- // ========== 瀵硅瘽绠$悊 ==========
-
- @GetMapping("/page")
- @Operation(summary = "鑾峰緱瀵硅瘽鍒嗛〉", description = "鐢ㄤ簬銆愬璇濈鐞嗐�戣彍鍗�")
- @PreAuthorize("@ss.hasPermission('ai:chat-conversation:query')")
- @TransMethodResult
- public CommonResult<PageResult<AiChatConversationRespVO>> getChatConversationPage(AiChatConversationPageReqVO pageReqVO) {
- PageResult<AiChatConversationDO> pageResult = chatConversationService.getChatConversationPage(pageReqVO);
- if (CollUtil.isEmpty(pageResult.getList())) {
- return success(PageResult.empty());
- }
- // 鎷兼帴鍏宠仈鏁版嵁
- Map<Long, Integer> messageCountMap = chatMessageService.getChatMessageCountMap(
- convertList(pageResult.getList(), AiChatConversationDO::getId));
- return success(BeanUtils.toBean(pageResult, AiChatConversationRespVO.class,
- conversation -> conversation.setMessageCount(messageCountMap.getOrDefault(conversation.getId(), 0))));
- }
-
- @Operation(summary = "绠$悊鍛樺垹闄ゅ璇�")
- @DeleteMapping("/delete-by-admin")
- @Parameter(name = "id", required = true, description = "瀵硅瘽缂栧彿", example = "1024")
- @PreAuthorize("@ss.hasPermission('ai:chat-conversation:delete')")
- public CommonResult<Boolean> deleteChatConversationByAdmin(@RequestParam("id") Long id) {
- chatConversationService.deleteChatConversationByAdmin(id);
- return success(true);
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.http b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.http
deleted file mode 100644
index 017714e..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.http
+++ /dev/null
@@ -1,66 +0,0 @@
-### 鍙戦�佹秷鎭紙娈靛紡锛�
-POST {{baseUrl}}/ai/chat/message/send
-Content-Type: application/json
-Authorization: {{token}}
-tenant-id: {{adminTenantId}}
-
-{
- "conversationId": "1781604279872581724",
- "content": "浣犳槸 OpenAI 涔堬紵"
-}
-
-### 鍙戦�佹秷鎭紙娴佸紡锛�
-POST {{baseUrl}}/ai/chat/message/send-stream
-Content-Type: application/json
-Authorization: {{token}}
-tenant-id: {{adminTenantId}}
-
-{
- "conversationId": "1781604279872581724",
- "content": "1+1=?"
-}
-
-### 鍙戦�佹秷鎭紙娴佸紡锛夈�愬甫鏂囦欢銆�
-POST {{baseUrl}}/ai/chat/message/send-stream
-Content-Type: application/json
-Authorization: {{token}}
-tenant-id: {{adminTenantId}}
-
-{
- "conversationId": "1781604279872581797",
- "content": "鍥剧墖閲屾湁浠�涔堬紵",
- "attachmentUrls": ["http://test.yudao.iocoder.cn/1755531278.jpeg"]
-}
-
-### 鍙戦�佹秷鎭紙娴佸紡锛夈�愯拷闂甫鏂囦欢銆�
-POST {{baseUrl}}/ai/chat/message/send-stream
-Content-Type: application/json
-Authorization: {{token}}
-tenant-id: {{adminTenantId}}
-
-{
- "conversationId": "1781604279872581799",
- "content": "璇翠笅鍥剧墖閲岋紝鏈夊摢浜涘瓧锛�",
- "useContext": true
-}
-
-### 鍙戦�佹秷鎭紙娴佸紡锛夈�愯仈缃戞悳绱€��
-POST {{baseUrl}}/ai/chat/message/send-stream
-Content-Type: application/json
-Authorization: {{token}}
-tenant-id: {{adminTenantId}}
-
-{
- "conversationId": "1781604279872581799",
- "content": "浠婂ぉ鏄懆鍑狅紵",
- "useSearch": true
-}
-
-### 鑾峰緱鎸囧畾瀵硅瘽鐨勬秷鎭垪琛�
-GET {{baseUrl}}/ai/chat/message/list-by-conversation-id?conversationId=1781604279872581799
-Authorization: {{token}}
-tenant-id: {{adminTenantId}}
-
-### 鍒犻櫎娑堟伅
-DELETE {{baseUrl}}/ai/chat/message/delete?id=50
-Authorization: {{token}}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java
deleted file mode 100644
index bfd1e41..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatMessageController.java
+++ /dev/null
@@ -1,157 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.chat;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.util.ObjUtil;
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
-import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
-import cn.iocoder.yudao.module.ai.service.chat.AiChatConversationService;
-import cn.iocoder.yudao.module.ai.service.chat.AiChatMessageService;
-import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;
-import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService;
-import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.Parameter;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import jakarta.annotation.Resource;
-import jakarta.validation.Valid;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.http.MediaType;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
-import reactor.core.publisher.Flux;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
-import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
-
-@Tag(name = "绠$悊鍚庡彴 - 鑱婂ぉ娑堟伅")
-@RestController
-@RequestMapping("/ai/chat/message")
-@Slf4j
-public class AiChatMessageController {
-
- @Resource
- private AiChatMessageService chatMessageService;
- @Resource
- private AiChatConversationService chatConversationService;
- @Resource
- private AiChatRoleService chatRoleService;
- @Resource
- private AiKnowledgeSegmentService knowledgeSegmentService;
- @Resource
- private AiKnowledgeDocumentService knowledgeDocumentService;
-
- @Operation(summary = "鍙戦�佹秷鎭紙娈靛紡锛�", description = "涓�娆℃�ц繑鍥烇紝鍝嶅簲杈冩參")
- @PostMapping("/send")
- public CommonResult<AiChatMessageSendRespVO> sendMessage(@Valid @RequestBody AiChatMessageSendReqVO sendReqVO) {
- return success(chatMessageService.sendMessage(sendReqVO, getLoginUserId()));
- }
-
- @Operation(summary = "鍙戦�佹秷鎭紙娴佸紡锛�", description = "娴佸紡杩斿洖锛屽搷搴旇緝蹇�")
- @PostMapping(value = "/send-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
- public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(@Valid @RequestBody AiChatMessageSendReqVO sendReqVO) {
- return chatMessageService.sendChatMessageStream(sendReqVO, getLoginUserId());
- }
-
- @Operation(summary = "鑾峰緱鎸囧畾瀵硅瘽鐨勬秷鎭垪琛�")
- @GetMapping("/list-by-conversation-id")
- @Parameter(name = "conversationId", required = true, description = "瀵硅瘽缂栧彿", example = "1024")
- public CommonResult<List<AiChatMessageRespVO>> getChatMessageListByConversationId(
- @RequestParam("conversationId") Long conversationId) {
- AiChatConversationDO conversation = chatConversationService.getChatConversation(conversationId);
- if (conversation == null || ObjUtil.notEqual(conversation.getUserId(), getLoginUserId())) {
- return success(Collections.emptyList());
- }
- // 1. 鑾峰彇娑堟伅鍒楄〃
- List<AiChatMessageDO> messageList = chatMessageService.getChatMessageListByConversationId(conversationId);
- if (CollUtil.isEmpty(messageList)) {
- return success(Collections.emptyList());
- }
-
- // 2. 鎷兼帴鏁版嵁锛屼富瑕佹槸鐭ヨ瘑搴撴钀戒俊鎭�
- Map<Long, AiKnowledgeSegmentDO> segmentMap = knowledgeSegmentService.getKnowledgeSegmentMap(convertListByFlatMap(messageList,
- message -> CollUtil.isEmpty(message.getSegmentIds()) ? null : message.getSegmentIds().stream()));
- Map<Long, AiKnowledgeDocumentDO> documentMap = knowledgeDocumentService.getKnowledgeDocumentMap(
- convertList(segmentMap.values(), AiKnowledgeSegmentDO::getDocumentId));
- List<AiChatMessageRespVO> messageVOList = BeanUtils.toBean(messageList, AiChatMessageRespVO.class);
- for (int i = 0; i < messageList.size(); i++) {
- AiChatMessageDO message = messageList.get(i);
- if (CollUtil.isEmpty(message.getSegmentIds())) {
- continue;
- }
- // 璁剧疆鐭ヨ瘑搴撴钀戒俊鎭�
- messageVOList.get(i).setSegments(convertList(message.getSegmentIds(), segmentId -> {
- AiKnowledgeSegmentDO segment = segmentMap.get(segmentId);
- if (segment == null) {
- return null;
- }
- AiKnowledgeDocumentDO document = documentMap.get(segment.getDocumentId());
- if (document == null) {
- return null;
- }
- return new AiChatMessageRespVO.KnowledgeSegment().setId(segment.getId()).setContent(segment.getContent())
- .setDocumentId(segment.getDocumentId()).setDocumentName(document.getName());
- }));
- }
- return success(messageVOList);
- }
-
- @Operation(summary = "鍒犻櫎娑堟伅")
- @DeleteMapping("/delete")
- @Parameter(name = "id", required = true, description = "娑堟伅缂栧彿", example = "1024")
- public CommonResult<Boolean> deleteChatMessage(@RequestParam("id") Long id) {
- chatMessageService.deleteChatMessage(id, getLoginUserId());
- return success(true);
- }
-
- @Operation(summary = "鍒犻櫎鎸囧畾瀵硅瘽鐨勬秷鎭�")
- @DeleteMapping("/delete-by-conversation-id")
- @Parameter(name = "conversationId", required = true, description = "瀵硅瘽缂栧彿", example = "1024")
- public CommonResult<Boolean> deleteChatMessageByConversationId(@RequestParam("conversationId") Long conversationId) {
- chatMessageService.deleteChatMessageByConversationId(conversationId, getLoginUserId());
- return success(true);
- }
-
- // ========== 瀵硅瘽绠$悊 ==========
-
- @GetMapping("/page")
- @Operation(summary = "鑾峰緱娑堟伅鍒嗛〉", description = "鐢ㄤ簬銆愬璇濈鐞嗐�戣彍鍗�")
- @PreAuthorize("@ss.hasPermission('ai:chat-conversation:query')")
- public CommonResult<PageResult<AiChatMessageRespVO>> getChatMessagePage(AiChatMessagePageReqVO pageReqVO) {
- PageResult<AiChatMessageDO> pageResult = chatMessageService.getChatMessagePage(pageReqVO);
- if (CollUtil.isEmpty(pageResult.getList())) {
- return success(PageResult.empty());
- }
- // 鎷兼帴鏁版嵁
- Map<Long, AiChatRoleDO> roleMap = chatRoleService.getChatRoleMap(
- convertSet(pageResult.getList(), AiChatMessageDO::getRoleId));
- return success(BeanUtils.toBean(pageResult, AiChatMessageRespVO.class,
- respVO -> MapUtils.findAndThen(roleMap, respVO.getRoleId(),
- role -> respVO.setRoleName(role.getName()))));
- }
-
- @Operation(summary = "绠$悊鍛樺垹闄ゆ秷鎭�")
- @DeleteMapping("/delete-by-admin")
- @Parameter(name = "id", required = true, description = "娑堟伅缂栧彿", example = "1024")
- @PreAuthorize("@ss.hasPermission('ai:chat-message:delete')")
- public CommonResult<Boolean> deleteChatMessageByAdmin(@RequestParam("id") Long id) {
- chatMessageService.deleteChatMessageByAdmin(id);
- return success(true);
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationCreateMyReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationCreateMyReqVO.java
deleted file mode 100644
index 84595be..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationCreateMyReqVO.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 鑱婂ぉ瀵硅瘽鍒涘缓銆愭垜鐨勩�� Request VO")
-@Data
-public class AiChatConversationCreateMyReqVO {
-
- @Schema(description = "鑱婂ぉ瑙掕壊缂栧彿", example = "666")
- private Long roleId;
-
- @Schema(description = "鐭ヨ瘑搴撶紪鍙�", example = "1204")
- private Long knowledgeId;
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationPageReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationPageReqVO.java
deleted file mode 100644
index 967e866..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationPageReqVO.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation;
-
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-import org.springframework.format.annotation.DateTimeFormat;
-
-import java.time.LocalDateTime;
-
-import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 鑱婂ぉ瀵硅瘽鐨勫垎椤� Request VO")
-@Data
-public class AiChatConversationPageReqVO extends PageParam {
-
- @Schema(description = "鐢ㄦ埛缂栧彿", example = "1024")
- private Long userId;
-
- @Schema(description = "瀵硅瘽鏍囬", example = "浣犲ソ")
- private String title;
-
- @Schema(description = "鍒涘缓鏃堕棿")
- @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
- private LocalDateTime[] createTime;
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationRespVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationRespVO.java
deleted file mode 100644
index b2ab07f..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationRespVO.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation;
-
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
-import org.dromara.core.trans.anno.Trans;
-import org.dromara.core.trans.constant.TransType;
-import org.dromara.core.trans.vo.VO;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-
-import java.time.LocalDateTime;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 鑱婂ぉ瀵硅瘽 Response VO")
-@Data
-public class AiChatConversationRespVO implements VO {
-
- @Schema(description = "瀵硅瘽缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
- private Long id;
-
- @Schema(description = "鐢ㄦ埛缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
- private Long userId;
-
- @Schema(description = "瀵硅瘽鏍囬", requiredMode = Schema.RequiredMode.REQUIRED, example = "鎴戞槸涓�涓爣棰�")
- private String title;
-
- @Schema(description = "鏄惁缃《", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
- private Boolean pinned;
-
- @Schema(description = "瑙掕壊缂栧彿", example = "1")
- @Trans(type = TransType.SIMPLE, target = AiChatRoleDO.class, fields = {"name", "avatar"}, refs = {"roleName", "roleAvatar"})
- private Long roleId;
-
- @Schema(description = "妯″瀷缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- @Trans(type = TransType.SIMPLE, target = AiModelDO.class, fields = "name", ref = "modelName")
- private Long modelId;
-
- @Schema(description = "妯″瀷鏍囧織", requiredMode = Schema.RequiredMode.REQUIRED, example = "ERNIE-Bot-turbo-0922")
- private String model;
-
- @Schema(description = "妯″瀷鍚嶅瓧", requiredMode = Schema.RequiredMode.REQUIRED, example = "寮犱笁")
- private String modelName;
-
- @Schema(description = "瑙掕壊璁惧畾", example = "涓�涓揩涔愮殑绋嬪簭鍛�")
- private String systemMessage;
-
- @Schema(description = "娓╁害鍙傛暟", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.8")
- private Double temperature;
-
- @Schema(description = "鍗曟潯鍥炲鐨勬渶澶� Token 鏁伴噺", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096")
- private Integer maxTokens;
-
- @Schema(description = "涓婁笅鏂囩殑鏈�澶� Message 鏁伴噺", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
- private Integer maxContexts;
-
- @Schema(description = "鍒涘缓鏃堕棿", requiredMode = Schema.RequiredMode.REQUIRED)
- private LocalDateTime createTime;
-
- // ========== 鍏宠仈 role 淇℃伅 ==========
-
- @Schema(description = "瑙掕壊澶村儚", example = "https://www.iocoder.cn/1.png")
- private String roleAvatar;
-
- @Schema(description = "瑙掕壊鍚嶅瓧", example = "灏忛粍")
- private String roleName;
-
- // ========== 浠呭湪銆愬璇濈鐞嗐�戞椂鍔犺浇 ==========
-
- @Schema(description = "娑堟伅鏁伴噺", example = "20")
- private Integer messageCount;
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationUpdateMyReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationUpdateMyReqVO.java
deleted file mode 100644
index 2b57572..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/conversation/AiChatConversationUpdateMyReqVO.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotNull;
-import lombok.Data;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 鑱婂ぉ瀵硅瘽鏇存柊銆愭垜鐨勩�� Request VO")
-@Data
-public class AiChatConversationUpdateMyReqVO {
-
- @Schema(description = "瀵硅瘽缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
- @NotNull(message = "瀵硅瘽缂栧彿涓嶈兘涓虹┖")
- private Long id;
-
- @Schema(description = "瀵硅瘽鏍囬", example = "鎴戞槸涓�涓爣棰�")
- private String title;
-
- @Schema(description = "鏄惁缃《", example = "true")
- private Boolean pinned;
-
- @Schema(description = "妯″瀷缂栧彿", example = "1")
- private Long modelId;
-
- @Schema(description = "鐭ヨ瘑搴撶紪鍙�", example = "1")
- private Long knowledgeId;
-
- @Schema(description = "瑙掕壊璁惧畾", example = "涓�涓揩涔愮殑绋嬪簭鍛�")
- private String systemMessage;
-
- @Schema(description = "娓╁害鍙傛暟", example = "0.8")
- private Double temperature;
-
- @Schema(description = "鍗曟潯鍥炲鐨勬渶澶� Token 鏁伴噺", example = "4096")
- private Integer maxTokens;
-
- @Schema(description = "涓婁笅鏂囩殑鏈�澶� Message 鏁伴噺", example = "10")
- private Integer maxContexts;
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessagePageReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessagePageReqVO.java
deleted file mode 100644
index 7ccb6aa..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessagePageReqVO.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message;
-
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-import org.springframework.format.annotation.DateTimeFormat;
-
-import java.time.LocalDateTime;
-
-import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 鑱婂ぉ娑堟伅鐨勫垎椤� Request VO")
-@Data
-public class AiChatMessagePageReqVO extends PageParam {
-
- @Schema(description = "瀵硅瘽缂栧彿", example = "2048")
- private Long conversationId;
-
- @Schema(description = "鐢ㄦ埛缂栧彿", example = "1024")
- private Long userId;
-
- @Schema(description = "娑堟伅鍐呭", example = "浣犲ソ")
- private String content;
-
- @Schema(description = "鍒涘缓鏃堕棿")
- @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
- private LocalDateTime[] createTime;
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageRespVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageRespVO.java
deleted file mode 100644
index b0f13e3..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageRespVO.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message;
-
-import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchResponse;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-
-import java.time.LocalDateTime;
-import java.util.List;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 鑱婂ぉ娑堟伅 Response VO")
-@Data
-public class AiChatMessageRespVO {
-
- @Schema(description = "缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
- private Long id;
-
- @Schema(description = "瀵硅瘽缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
- private Long conversationId;
-
- @Schema(description = "鍥炲娑堟伅缂栧彿", example = "1024")
- private Long replyId;
-
- @Schema(description = "娑堟伅绫诲瀷", requiredMode = Schema.RequiredMode.REQUIRED, example = "role")
- private String type; // 鍙傝 MessageType 鏋氫妇绫�
-
- @Schema(description = "鐢ㄦ埛缂栧彿", example = "4096")
- private Long userId;
-
- @Schema(description = "瑙掕壊缂栧彿", example = "888")
- private Long roleId;
-
- @Schema(description = "妯″瀷鏍囧織", requiredMode = Schema.RequiredMode.REQUIRED, example = "gpt-3.5-turbo")
- private String model;
-
- @Schema(description = "妯″瀷缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "123")
- private Long modelId;
-
- @Schema(description = "鑱婂ぉ鍐呭", requiredMode = Schema.RequiredMode.REQUIRED, example = "浣犲ソ锛屼綘濂藉晩")
- private String content;
-
- @Schema(description = "鎺ㄧ悊鍐呭", example = "瑕佽揪鍒拌繖涓洰鏍囷紝浣犻渶瑕�...")
- private String reasoningContent;
-
- @Schema(description = "鏄惁鎼哄甫涓婁笅鏂�", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
- private Boolean useContext;
-
- @Schema(description = "鐭ヨ瘑搴撴钀界紪鍙锋暟缁�", example = "[1,2,3]")
- private List<Long> segmentIds;
-
- @Schema(description = "鐭ヨ瘑搴撴钀芥暟缁�")
- private List<KnowledgeSegment> segments;
-
- @Schema(description = "鑱旂綉鎼滅储鐨勭綉椤靛唴瀹规暟缁�")
- private List<AiWebSearchResponse.WebPage> webSearchPages;
-
- @Schema(description = "闄勪欢 URL 鏁扮粍", example = "https://www.iocoder.cn/1.png")
- private List<String> attachmentUrls;
-
- @Schema(description = "鍒涘缓鏃堕棿", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-05-12 12:51")
- private LocalDateTime createTime;
-
- // ========== 浠呭湪銆愬璇濈鐞嗐�戞椂鍔犺浇 ==========
-
- @Schema(description = "瑙掕壊鍚嶅瓧", example = "灏忛粍")
- private String roleName;
-
- @Schema(description = "鐭ヨ瘑搴撴钀�", example = "Java 寮�鍙戞墜鍐�")
- @Data
- public static class KnowledgeSegment {
-
- @Schema(description = "娈佃惤缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
- private Long id;
-
- @Schema(description = "鍒囩墖鍐呭", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 寮�鍙戞墜鍐�")
- private String content;
-
- @Schema(description = "鏂囨。缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
- private Long documentId;
-
- @Schema(description = "鏂囨。鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED, example = "浜у搧浣跨敤鎵嬪唽")
- private String documentName;
-
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageSendReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageSendReqVO.java
deleted file mode 100644
index 06ce0d1..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageSendReqVO.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotEmpty;
-import jakarta.validation.constraints.NotNull;
-import lombok.Data;
-
-import java.util.List;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 鑱婂ぉ娑堟伅鍙戦�� Request VO")
-@Data
-public class AiChatMessageSendReqVO {
-
- @Schema(description = "鑱婂ぉ瀵硅瘽缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
- @NotNull(message = "鑱婂ぉ瀵硅瘽缂栧彿涓嶈兘涓虹┖")
- private Long conversationId;
-
- @Schema(description = "鑱婂ぉ鍐呭", requiredMode = Schema.RequiredMode.REQUIRED, example = "甯垜鍐欎釜 Java 绠楁硶")
- @NotEmpty(message = "鑱婂ぉ鍐呭涓嶈兘涓虹┖")
- private String content;
-
- @Schema(description = "鏄惁鎼哄甫涓婁笅鏂�", example = "true")
- private Boolean useContext;
-
- @Schema(description = "鏄惁鑱旂綉鎼滅储", example = "true")
- private Boolean useSearch;
-
- @Schema(description = "闄勪欢 URL 鏁扮粍", example = "https://www.iocoder.cn/1.png")
- private List<String> attachmentUrls;
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageSendRespVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageSendRespVO.java
deleted file mode 100644
index 520712b..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/vo/message/AiChatMessageSendRespVO.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message;
-
-import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchResponse;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-
-import java.time.LocalDateTime;
-import java.util.List;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 鑱婂ぉ娑堟伅鍙戦�� Response VO")
-@Data
-public class AiChatMessageSendRespVO {
-
- @Schema(description = "鍙戦�佹秷鎭�", requiredMode = Schema.RequiredMode.REQUIRED)
- private Message send;
-
- @Schema(description = "鎺ユ敹娑堟伅", requiredMode = Schema.RequiredMode.REQUIRED)
- private Message receive;
-
- @Schema(description = "娑堟伅")
- @Data
- public static class Message {
-
- @Schema(description = "缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
- private Long id;
-
- @Schema(description = "娑堟伅绫诲瀷", requiredMode = Schema.RequiredMode.REQUIRED, example = "role")
- private String type; // 鍙傝 MessageType 鏋氫妇绫�
-
- @Schema(description = "鑱婂ぉ鍐呭", requiredMode = Schema.RequiredMode.REQUIRED, example = "浣犲ソ锛屼綘濂藉晩")
- private String content;
-
- @Schema(description = "鎺ㄧ悊鍐呭", example = "瑕佽揪鍒拌繖涓洰鏍囷紝浣犻渶瑕�...")
- private String reasoningContent;
-
- @Schema(description = "鐭ヨ瘑搴撴钀界紪鍙锋暟缁�", example = "[1,2,3]")
- private List<Long> segmentIds;
-
- @Schema(description = "鐭ヨ瘑搴撴钀芥暟缁�")
- private List<AiChatMessageRespVO.KnowledgeSegment> segments;
-
- @Schema(description = "鑱旂綉鎼滅储鐨勭綉椤靛唴瀹规暟缁�")
- private List<AiWebSearchResponse.WebPage> webSearchPages;
-
- @Schema(description = "鍒涘缓鏃堕棿", requiredMode = Schema.RequiredMode.REQUIRED)
- private LocalDateTime createTime;
-
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.http b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.http
deleted file mode 100644
index 9047610..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.http
+++ /dev/null
@@ -1,42 +0,0 @@
-### 鐢熸垚鍥剧墖锛歄penAI锛圖ALL锛�
-POST {{baseUrl}}/ai/image/draw
-Content-Type: application/json
-Authorization: {{token}}
-
-{
- "platform": "OpenAI",
- "prompt": "鍙埍鐨勫皬鍠垫槦浜�",
- "model": "dall-e-3",
- "height": "1024",
- "width": "1024",
- "options": {
- "style": "vivid"
- }
-}
-
-### 鐢熸垚鍥剧墖锛歋tableDiffusion
-POST {{baseUrl}}/ai/image/draw
-Content-Type: application/json
-Authorization: {{token}}
-
-{
- "platform": "StableDiffusion",
- "prompt": "涓浗闀垮煄",
- "model": "stable-diffusion-v1-6",
- "height": "1024",
- "width": "1024",
- "style": "vivid"
-}
-
-### 鐢熸垚鍥剧墖锛氱敓鎴愬浘鐗囷紙Midjourney锛�
-POST {{baseUrl}}/ai/image/midjourney/imagine
-Content-Type: application/json
-Authorization: {{token}}
-
-{
- "prompt": "涓浗鏃楄",
- "model": "midjourney",
- "width": "1",
- "height": "1",
- "version": "6.0"
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.java
deleted file mode 100644
index 1d9503f..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.java
+++ /dev/null
@@ -1,139 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.image;
-
-import cn.hutool.core.util.ObjUtil;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
-import cn.iocoder.yudao.module.ai.controller.admin.image.vo.*;
-import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyActionReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyImagineReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
-import cn.iocoder.yudao.module.ai.service.image.AiImageService;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.Parameter;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import jakarta.annotation.Resource;
-import jakarta.annotation.security.PermitAll;
-import jakarta.validation.Valid;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.List;
-
-import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
-
-@Tag(name = "绠$悊鍚庡彴 - AI 缁樼敾")
-@RestController
-@RequestMapping("/ai/image")
-@Slf4j
-public class AiImageController {
-
- @Resource
- private AiImageService imageService;
-
- @GetMapping("/my-page")
- @Operation(summary = "鑾峰彇銆愭垜鐨勩�戠粯鍥惧垎椤�")
- public CommonResult<PageResult<AiImageRespVO>> getImagePageMy(@Validated AiImagePageReqVO pageReqVO) {
- PageResult<AiImageDO> pageResult = imageService.getImagePageMy(getLoginUserId(), pageReqVO);
- return success(BeanUtils.toBean(pageResult, AiImageRespVO.class));
- }
-
- @GetMapping("/public-page")
- @Operation(summary = "鑾峰彇鍏紑鐨勭粯鍥惧垎椤�")
- public CommonResult<PageResult<AiImageRespVO>> getImagePagePublic(AiImagePublicPageReqVO pageReqVO) {
- PageResult<AiImageDO> pageResult = imageService.getImagePagePublic(pageReqVO);
- return success(BeanUtils.toBean(pageResult, AiImageRespVO.class));
- }
-
- @GetMapping("/get-my")
- @Operation(summary = "鑾峰彇銆愭垜鐨勩�戠粯鍥捐褰�")
- @Parameter(name = "id", required = true, description = "缁樼敾缂栧彿", example = "1024")
- public CommonResult<AiImageRespVO> getImageMy(@RequestParam("id") Long id) {
- AiImageDO image = imageService.getImage(id);
- if (image == null || ObjUtil.notEqual(getLoginUserId(), image.getUserId())) {
- return success(null);
- }
- return success(BeanUtils.toBean(image, AiImageRespVO.class));
- }
-
- @GetMapping("/my-list-by-ids")
- @Operation(summary = "鑾峰彇銆愭垜鐨勩�戠粯鍥捐褰曞垪琛�")
- @Parameter(name = "ids", required = true, description = "缁樼敾缂栧彿鏁扮粍", example = "1024,2048")
- public CommonResult<List<AiImageRespVO>> getImageListMyByIds(@RequestParam("ids") List<Long> ids) {
- List<AiImageDO> imageList = imageService.getImageList(ids);
- imageList.removeIf(item -> !ObjUtil.equal(getLoginUserId(), item.getUserId()));
- return success(BeanUtils.toBean(imageList, AiImageRespVO.class));
- }
-
- @Operation(summary = "鐢熸垚鍥剧墖")
- @PostMapping("/draw")
- public CommonResult<Long> drawImage(@Valid @RequestBody AiImageDrawReqVO drawReqVO) {
- return success(imageService.drawImage(getLoginUserId(), drawReqVO));
- }
-
- @Operation(summary = "鍒犻櫎銆愭垜鐨勩�戠粯鐢昏褰�")
- @DeleteMapping("/delete-my")
- @Parameter(name = "id", required = true, description = "缁樼敾缂栧彿", example = "1024")
- public CommonResult<Boolean> deleteImageMy(@RequestParam("id") Long id) {
- imageService.deleteImageMy(id, getLoginUserId());
- return success(true);
- }
-
- // ================ midjourney 涓撳睘 ================
-
- @Operation(summary = "銆怣idjourney銆戠敓鎴愬浘鐗�")
- @PostMapping("/midjourney/imagine")
- public CommonResult<Long> midjourneyImagine(@Valid @RequestBody AiMidjourneyImagineReqVO reqVO) {
- Long imageId = imageService.midjourneyImagine(getLoginUserId(), reqVO);
- return success(imageId);
- }
-
- @Operation(summary = "銆怣idjourney銆戦�氱煡鍥剧墖杩涘睍", description = "鐢� Midjourney Proxy 鍥炶皟")
- @PostMapping("/midjourney/notify") // 蹇呴』鏄� POST 鏂规硶锛屽惁鍒欎細鎶ラ敊
- @PermitAll
- @TenantIgnore
- public CommonResult<Boolean> midjourneyNotify(@Valid @RequestBody MidjourneyApi.Notify notify) {
- imageService.midjourneyNotify(notify);
- return success(true);
- }
-
- @Operation(summary = "銆怣idjourney銆慉ction 鎿嶄綔锛堜簩娆$敓鎴愬浘鐗囷級", description = "渚嬪璇达細鏀惧ぇ銆佺缉灏忋�乁1銆乁2 绛�")
- @PostMapping("/midjourney/action")
- public CommonResult<Long> midjourneyAction(@Valid @RequestBody AiMidjourneyActionReqVO reqVO) {
- Long imageId = imageService.midjourneyAction(getLoginUserId(), reqVO);
- return success(imageId);
- }
-
- // ================ 缁樺浘绠$悊 ================
-
- @GetMapping("/page")
- @Operation(summary = "鑾峰緱缁樼敾鍒嗛〉")
- @PreAuthorize("@ss.hasPermission('ai:image:query')")
- public CommonResult<PageResult<AiImageRespVO>> getImagePage(@Valid AiImagePageReqVO pageReqVO) {
- PageResult<AiImageDO> pageResult = imageService.getImagePage(pageReqVO);
- return success(BeanUtils.toBean(pageResult, AiImageRespVO.class));
- }
-
- @PutMapping("/update")
- @Operation(summary = "鏇存柊缁樼敾")
- @PreAuthorize("@ss.hasPermission('ai:image:update')")
- public CommonResult<Boolean> updateImage(@Valid @RequestBody AiImageUpdateReqVO updateReqVO) {
- imageService.updateImage(updateReqVO);
- return success(true);
- }
-
- @DeleteMapping("/delete")
- @Operation(summary = "鍒犻櫎缁樼敾")
- @Parameter(name = "id", description = "缂栧彿", required = true)
- @PreAuthorize("@ss.hasPermission('ai:image:delete')")
- public CommonResult<Boolean> deleteImage(@RequestParam("id") Long id) {
- imageService.deleteImage(id);
- return success(true);
- }
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageDrawReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageDrawReqVO.java
deleted file mode 100644
index 02225ea..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageDrawReqVO.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.image.vo;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotEmpty;
-import jakarta.validation.constraints.NotNull;
-import jakarta.validation.constraints.Size;
-import lombok.Data;
-import org.springframework.ai.openai.OpenAiImageOptions;
-import org.springframework.ai.stabilityai.api.StabilityAiImageOptions;
-
-import java.util.Map;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 缁樼敾 Request VO")
-@Data
-public class AiImageDrawReqVO {
-
- @Schema(description = "妯″瀷缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
- @NotNull(message = "妯″瀷缂栧彿涓嶈兘涓虹┖")
- private Long modelId;
-
- @Schema(description = "鎻愮ず璇�", requiredMode = Schema.RequiredMode.REQUIRED, example = "鐢讳竴涓暱鍩�")
- @NotEmpty(message = "鎻愮ず璇嶄笉鑳戒负绌�")
- @Size(max = 1200, message = "鎻愮ず璇嶆渶澶� 1200")
- private String prompt;
-
- /**
- * 1. dall-e-2 妯″瀷锛�256x256銆�512x512銆�1024x1024
- * 2. dall-e-3 妯″瀷锛�1024x1024, 1792x1024, 鎴� 1024x1792
- */
- @Schema(description = "鍥剧墖楂樺害")
- @NotNull(message = "鍥剧墖楂樺害涓嶈兘涓虹┖")
- private Integer height;
-
- @Schema(description = "鍥剧墖瀹藉害")
- @NotNull(message = "鍥剧墖瀹藉害涓嶈兘涓虹┖")
- private Integer width;
-
- // ========== 鍚勫钩鍙扮粯鐢荤殑鎷撳睍鍙傛暟 ==========
-
- /**
- * 缁樺埗鍙傛暟锛屼笉鍚� platform 鐨勪笉鍚屽弬鏁�
- *
- * 1. {@link OpenAiImageOptions}
- * 2. {@link StabilityAiImageOptions}
- */
- @Schema(description = "缁樺埗鍙傛暟")
- private Map<String, String> options;
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImagePageReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImagePageReqVO.java
deleted file mode 100644
index 3b48686..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImagePageReqVO.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.image.vo;
-
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-import org.springframework.format.annotation.DateTimeFormat;
-
-import java.time.LocalDateTime;
-
-import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 缁樼敾鍒嗛〉 Request VO")
-@Data
-public class AiImagePageReqVO extends PageParam {
-
- @Schema(description = "鐢ㄦ埛缂栧彿", example = "28987")
- private Long userId;
-
- @Schema(description = "骞冲彴", example = "OpenAI")
- private String platform;
-
- @Schema(description = "鎻愮ず璇�", example = "1")
- private String prompt;
-
- @Schema(description = "缁樼敾鐘舵��", example = "1")
- private Integer status;
-
- @Schema(description = "鏄惁鍙戝竷", example = "1")
- private Boolean publicStatus;
-
- @Schema(description = "鍒涘缓鏃堕棿")
- @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
- private LocalDateTime[] createTime;
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImagePublicPageReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImagePublicPageReqVO.java
deleted file mode 100644
index e7ff80a..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImagePublicPageReqVO.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.image.vo;
-
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 缁樼敾鍏紑鐨勫垎椤� Request VO")
-@Data
-public class AiImagePublicPageReqVO extends PageParam {
-
- @Schema(description = "鎻愮ず璇�")
- private String prompt;
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageRespVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageRespVO.java
deleted file mode 100644
index fa3a957..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageRespVO.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.image.vo;
-
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-
-import java.time.LocalDateTime;
-import java.util.List;
-import java.util.Map;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 缁樼敾 Response VO")
-@Data
-public class AiImageRespVO {
-
- @Schema(description = "缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- private Long id;
-
- @Schema(description = "鐢ㄦ埛缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- private Long userId;
-
- @Schema(description = "骞冲彴", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
- private String platform; // 鍙傝 AiPlatformEnum 鏋氫妇
-
- @Schema(description = "妯″瀷", requiredMode = Schema.RequiredMode.REQUIRED, example = "stable-diffusion-v1-6")
- private String model;
-
- @Schema(description = "鎻愮ず璇�", requiredMode = Schema.RequiredMode.REQUIRED, example = "鍗楁瀬鐨勫皬浼侀箙")
- private String prompt;
-
- @Schema(description = "鍥剧墖瀹藉害", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
- private Integer width;
-
- @Schema(description = "鍥剧墖楂樺害", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
- private Integer height;
-
- @Schema(description = "缁樼敾鐘舵��", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
- private Integer status;
-
- @Schema(description = "鏄惁鍙戝竷", requiredMode = Schema.RequiredMode.REQUIRED, example = "public")
- private Boolean publicStatus;
-
- @Schema(description = "鍥剧墖鍦板潃", example = "https://www.iocoder.cn/1.png")
- private String picUrl;
-
- @Schema(description = "缁樼敾閿欒淇℃伅", example = "鍥剧墖閿欒淇℃伅")
- private String errorMessage;
-
- @Schema(description = "缁樺埗鍙傛暟")
- private Map<String, String> options;
-
- @Schema(description = "mj buttons 鎸夐挳")
- private List<MidjourneyApi.Button> buttons;
-
- @Schema(description = "瀹屾垚鏃堕棿")
- private LocalDateTime finishTime;
-
- @Schema(description = "鍒涘缓鏃堕棿", requiredMode = Schema.RequiredMode.REQUIRED)
- private LocalDateTime createTime;
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageUpdateReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageUpdateReqVO.java
deleted file mode 100644
index 45df010..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageUpdateReqVO.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.image.vo;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotNull;
-import lombok.Data;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 缁樼敾淇敼 Request VO")
-@Data
-public class AiImageUpdateReqVO {
-
- @Schema(description = "缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583")
- @NotNull(message = "缂栧彿涓嶈兘涓虹┖")
- private Long id;
-
- @Schema(description = "鏄惁鍙戝竷", example = "true")
- private Boolean publicStatus;
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/midjourney/AiMidjourneyActionReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/midjourney/AiMidjourneyActionReqVO.java
deleted file mode 100644
index 28803a0..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/midjourney/AiMidjourneyActionReqVO.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotEmpty;
-import jakarta.validation.constraints.NotNull;
-import lombok.Data;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 缁樺浘鎿嶄綔锛圡idjourney锛� Request VO")
-@Data
-public class AiMidjourneyActionReqVO {
-
- @Schema(description = "鍥剧墖缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- @NotNull(message = "鍥剧墖缂栧彿涓嶈兘涓虹┖")
- private Long id;
-
- @Schema(description = "鎿嶄綔鎸夐挳缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "MJ::JOB::variation::4::06aa3e66-0e97-49cc-8201-e0295d883de4")
- @NotEmpty(message = "鎿嶄綔鎸夐挳缂栧彿涓嶈兘涓虹┖")
- private String customId;
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/midjourney/AiMidjourneyImagineReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/midjourney/AiMidjourneyImagineReqVO.java
deleted file mode 100644
index efb5906..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/midjourney/AiMidjourneyImagineReqVO.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotEmpty;
-import jakarta.validation.constraints.NotNull;
-import lombok.Data;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 缁樼敾鐢熸垚锛圡idjourney锛� Request VO")
-@Data
-public class AiMidjourneyImagineReqVO {
-
- @Schema(description = "鎻愮ず璇�", requiredMode = Schema.RequiredMode.REQUIRED, example = "涓浗绁為緳")
- @NotEmpty(message = "鎻愮ず璇嶄笉鑳戒负绌�!")
- private String prompt;
-
- @Schema(description = "妯″瀷缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- @NotNull(message = "妯″瀷缂栧彿涓嶈兘涓虹┖")
- private Long modelId;
-
- @Schema(description = "鍥剧墖瀹藉害", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- @NotNull(message = "鍥剧墖瀹藉害涓嶈兘涓虹┖")
- private Integer width;
-
- @Schema(description = "鍥剧墖楂樺害", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- @NotNull(message = "鍥剧墖楂樺害涓嶈兘涓虹┖")
- private Integer height;
-
- @Schema(description = "鐗堟湰鍙�", requiredMode = Schema.RequiredMode.REQUIRED, example = "6.0")
- @NotEmpty(message = "鐗堟湰鍙蜂笉鑳戒负绌�")
- private String version;
-
- @Schema(description = "鍙傝�冨浘", example = "https://www.iocoder.cn/x.png")
- private String referImageUrl;
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.http b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.http
deleted file mode 100644
index a0f1278..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.http
+++ /dev/null
@@ -1,35 +0,0 @@
-### 鍒涘缓鐭ヨ瘑搴�
-POST {{baseUrl}}/ai/knowledge/create
-Content-Type: application/json
-Authorization: {{token}}
-tenant-id: {{adminTenantId}}
-
-{
- "name": "娴嬭瘯鏍囬",
- "description": "娴嬭瘯鎻忚堪",
- "embeddingModelId": 30,
- "topK": 3,
- "similarityThreshold": 0.5,
- "status": 0
-}
-
-### 鏇存柊鐭ヨ瘑搴�
-PUT {{baseUrl}}/ai/knowledge/update
-Content-Type: application/json
-Authorization: {{token}}
-tenant-id: {{adminTenantId}}
-
-{
- "id": 1,
- "name": "娴嬭瘯鏍囬锛堟洿鏂帮級",
- "description": "娴嬭瘯鎻忚堪",
- "embeddingModelId": 30,
- "topK": 5,
- "similarityThreshold": 0.6,
- "status": 0
-}
-
-### 鑾峰彇鐭ヨ瘑搴撳垎椤�
-GET {{baseUrl}}/ai/knowledge/page?pageNo=1&pageSize=10
-Authorization: {{token}}
-tenant-id: {{adminTenantId}}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java
index b9daa95..22cd05e 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeController.java
@@ -1,36 +1,65 @@
package cn.iocoder.yudao.module.ai.controller.admin.knowledge;
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeRespVO;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
+import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService;
import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@Tag(name = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴�")
@RestController
@RequestMapping("/ai/knowledge")
-@Validated
public class AiKnowledgeController {
@Resource
private AiKnowledgeService knowledgeService;
+ @Resource
+ private AiKnowledgeDocumentService documentService;
+
+ @PostMapping("/create")
+ @Operation(summary = "鍒涘缓鐭ヨ瘑搴�")
+ @PreAuthorize("@ss.hasPermission('ai:knowledge:create')")
+ public CommonResult<Long> createKnowledge(@Valid @RequestBody AiKnowledgeSaveReqVO createReqVO) {
+ return success(knowledgeService.createKnowledge(createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "鏇存柊鐭ヨ瘑搴�")
+ @PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
+ public CommonResult<Boolean> updateKnowledge(@Valid @RequestBody AiKnowledgeSaveReqVO updateReqVO) {
+ knowledgeService.updateKnowledge(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "鍒犻櫎鐭ヨ瘑搴�")
+ @PreAuthorize("@ss.hasPermission('ai:knowledge:delete')")
+ public CommonResult<Boolean> deleteKnowledge(@RequestParam("id") Long id) {
+ knowledgeService.deleteKnowledge(id);
+ return success(true);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "鑾峰彇鐭ヨ瘑搴�")
+ @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
+ public CommonResult<AiKnowledgeRespVO> getKnowledge(@RequestParam("id") Long id) {
+ return success(BeanUtils.toBean(knowledgeService.getKnowledge(id), AiKnowledgeRespVO.class));
+ }
@GetMapping("/page")
@Operation(summary = "鑾峰彇鐭ヨ瘑搴撳垎椤�")
@@ -40,45 +69,11 @@
return success(BeanUtils.toBean(pageResult, AiKnowledgeRespVO.class));
}
- @GetMapping("/get")
- @Operation(summary = "鑾峰緱鐭ヨ瘑搴�")
- @Parameter(name = "id", description = "缂栧彿", required = true, example = "1024")
- @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
- public CommonResult<AiKnowledgeRespVO> getKnowledge(@RequestParam("id") Long id) {
- AiKnowledgeDO knowledge = knowledgeService.getKnowledge(id);
- return success(BeanUtils.toBean(knowledge, AiKnowledgeRespVO.class));
- }
-
- @PostMapping("/create")
- @Operation(summary = "鍒涘缓鐭ヨ瘑搴�")
+ @PostMapping("/document/create")
+ @Operation(summary = "鍒涘缓鏂囨。")
@PreAuthorize("@ss.hasPermission('ai:knowledge:create')")
- public CommonResult<Long> createKnowledge(@RequestBody @Valid AiKnowledgeSaveReqVO createReqVO) {
- return success(knowledgeService.createKnowledge(createReqVO));
- }
-
- @PutMapping("/update")
- @Operation(summary = "鏇存柊鐭ヨ瘑搴�")
- @PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
- public CommonResult<Boolean> updateKnowledge(@RequestBody @Valid AiKnowledgeSaveReqVO updateReqVO) {
- knowledgeService.updateKnowledge(updateReqVO);
- return success(true);
- }
-
- @DeleteMapping("/delete")
- @Operation(summary = "鍒犻櫎鐭ヨ瘑搴�")
- @Parameter(name = "id", description = "缂栧彿", required = true, example = "1024")
- @PreAuthorize("@ss.hasPermission('ai:knowledge:delete')")
- public CommonResult<Boolean> deleteKnowledge(@RequestParam("id") Long id) {
- knowledgeService.deleteKnowledge(id);
- return success(true);
- }
-
- @GetMapping("/simple-list")
- @Operation(summary = "鑾峰緱鐭ヨ瘑搴撶殑绮剧畝鍒楄〃")
- public CommonResult<List<AiKnowledgeRespVO>> getKnowledgeSimpleList() {
- List<AiKnowledgeDO> list = knowledgeService.getKnowledgeSimpleListByStatus(CommonStatusEnum.ENABLE.getStatus());
- return success(convertList(list, knowledge -> new AiKnowledgeRespVO()
- .setId(knowledge.getId()).setName(knowledge.getName())));
+ public CommonResult<List<Long>> createDocument(@Valid @RequestBody AiKnowledgeDocumentCreateReqVO createReqVO) {
+ return success(documentService.createDocuments(createReqVO));
}
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.http b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.http
deleted file mode 100644
index 1c858ed..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.http
+++ /dev/null
@@ -1,35 +0,0 @@
-### 鍒涘缓鐭ヨ瘑鏂囨。
-POST {{baseUrl}}/ai/knowledge/document/create
-Content-Type: application/json
-Authorization: Bearer {{token}}
-tenant-id: {{adminTenantId}}
-
-{
- "knowledgeId": 2,
- "name": "娴嬭瘯鏂囨。",
- "url": "https://static.iocoder.cn/README.md",
- "segmentMaxTokens": 800
-}
-
-### 鎵归噺鍒涘缓鐭ヨ瘑鏂囨。
-POST {{baseUrl}}/ai/knowledge/document/create-list
-Content-Type: application/json
-Authorization: Bearer {{token}}
-tenant-id: {{adminTenantId}}
-
-{
- "knowledgeId": 1,
- "list": [
- {
- "name": "娴嬭瘯鏂囨。1",
- "url": "https://static.iocoder.cn/README.md",
- "segmentMaxTokens": 800
- },
- {
- "name": "娴嬭瘯鏂囨。2",
- "url": "https://static.iocoder.cn/README_yudao.md",
- "segmentMaxTokens": 400
- }
- ]
-}
-
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java
index 68fe49a..e138821 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeDocumentController.java
@@ -4,7 +4,7 @@
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.*;
-import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;
import io.swagger.v3.oas.annotations.Operation;
@@ -12,7 +12,6 @@
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@@ -22,69 +21,70 @@
@Tag(name = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撴枃妗�")
@RestController
@RequestMapping("/ai/knowledge/document")
-@Validated
public class AiKnowledgeDocumentController {
@Resource
private AiKnowledgeDocumentService documentService;
- @GetMapping("/page")
- @Operation(summary = "鑾峰彇鏂囨。鍒嗛〉")
- @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
- public CommonResult<PageResult<AiKnowledgeDocumentRespVO>> getKnowledgeDocumentPage(
- @Valid AiKnowledgeDocumentPageReqVO pageReqVO) {
- PageResult<AiKnowledgeDocumentDO> pageResult = documentService.getKnowledgeDocumentPage(pageReqVO);
- return success(BeanUtils.toBean(pageResult, AiKnowledgeDocumentRespVO.class));
- }
-
- @GetMapping("/get")
- @Operation(summary = "鑾峰彇鏂囨。璇︽儏")
- @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
- public CommonResult<AiKnowledgeDocumentRespVO> getKnowledgeDocument(@RequestParam("id") Long id) {
- AiKnowledgeDocumentDO document = documentService.getKnowledgeDocument(id);
- return success(BeanUtils.toBean(document, AiKnowledgeDocumentRespVO.class));
- }
-
- @PostMapping("/create")
- @Operation(summary = "鏂板缓鏂囨。锛堝崟涓級")
- @PreAuthorize("@ss.hasPermission('ai:knowledge:create')")
- public CommonResult<Long> createKnowledgeDocument(@RequestBody @Valid AiKnowledgeDocumentCreateReqVO reqVO) {
- Long id = documentService.createKnowledgeDocument(reqVO);
- return success(id);
- }
-
@PostMapping("/create-list")
- @Operation(summary = "鏂板缓鏂囨。锛堝涓級")
+ @Operation(summary = "鍒涘缓鏂囨。锛堟壒閲忥級")
@PreAuthorize("@ss.hasPermission('ai:knowledge:create')")
- public CommonResult<List<Long>> createKnowledgeDocumentList(
- @RequestBody @Valid AiKnowledgeDocumentCreateListReqVO reqVO) {
- List<Long> ids = documentService.createKnowledgeDocumentList(reqVO);
- return success(ids);
+ public CommonResult<List<Long>> createDocumentList(@Valid @RequestBody AiKnowledgeDocumentCreateReqVO createReqVO) {
+ return success(documentService.createDocuments(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "鏇存柊鏂囨。")
@PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
- public CommonResult<Boolean> updateKnowledgeDocument(@Valid @RequestBody AiKnowledgeDocumentUpdateReqVO reqVO) {
- documentService.updateKnowledgeDocument(reqVO);
- return success(true);
- }
-
- @PutMapping("/update-status")
- @Operation(summary = "鏇存柊鏂囨。鐘舵��")
- @PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
- public CommonResult<Boolean> updateKnowledgeDocumentStatus(
- @Valid @RequestBody AiKnowledgeDocumentUpdateStatusReqVO reqVO) {
- documentService.updateKnowledgeDocumentStatus(reqVO);
+ public CommonResult<Boolean> updateDocument(@Valid @RequestBody AiKnowledgeDocumentUpdateReqVO updateReqVO) {
+ documentService.updateDocument(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "鍒犻櫎鏂囨。")
@PreAuthorize("@ss.hasPermission('ai:knowledge:delete')")
- public CommonResult<Boolean> deleteKnowledgeDocument(@RequestParam("id") Long id) {
- documentService.deleteKnowledgeDocument(id);
+ public CommonResult<Boolean> deleteDocument(@RequestParam("id") Long id) {
+ documentService.deleteDocument(id);
return success(true);
}
+ @GetMapping("/get")
+ @Operation(summary = "鑾峰彇鏂囨。")
+ @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
+ public CommonResult<AiKnowledgeDocumentRespVO> getDocument(@RequestParam("id") Long id) {
+ return success(BeanUtils.toBean(documentService.getDocument(id), AiKnowledgeDocumentRespVO.class));
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "鑾峰彇鏂囨。鍒嗛〉")
+ @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
+ public CommonResult<PageResult<AiKnowledgeDocumentRespVO>> getDocumentPage(@Valid AiKnowledgeDocumentPageReqVO pageReqVO) {
+ PageResult<AiKnowledgeDocumentDO> pageResult = documentService.getDocumentPage(pageReqVO);
+ return success(BeanUtils.toBean(pageResult, AiKnowledgeDocumentRespVO.class));
+ }
+
+ @PutMapping("/update-status")
+ @Operation(summary = "鏇存柊鏂囨。鐘舵��")
+ @PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
+ public CommonResult<Boolean> updateDocumentStatus(@Valid @RequestBody AiKnowledgeDocumentUpdateStatusReqVO updateStatusReqVO) {
+ documentService.updateDocumentStatus(updateStatusReqVO);
+ return success(true);
+ }
+
+ @PostMapping("/process/{id}")
+ @Operation(summary = "澶勭悊鏂囨。锛堝垎娈�+鍚戦噺鍖栵級")
+ @PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
+ public CommonResult<Boolean> processDocument(@PathVariable("id") Long id) {
+ documentService.processDocumentSegments(id);
+ return success(true);
+ }
+
+ @GetMapping("/progress")
+ @Operation(summary = "鑾峰彇鏂囨。澶勭悊杩涘害")
+ @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
+ public CommonResult<List<AiKnowledgeSegmentProcessRespVO>> getProcessingProgress(@RequestParam("documentIds") List<Long> documentIds) {
+ return success(documentService.getDocumentProcessingProgress(documentIds));
+ }
+
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.http b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.http
deleted file mode 100644
index 09018da..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.http
+++ /dev/null
@@ -1,17 +0,0 @@
-### 鍒囩墖鍐呭
-GET {{baseUrl}}/ai/knowledge/segment/split?url=https://static.iocoder.cn/README_yudao.md&segmentMaxTokens=800
-Content-Type: application/json
-Authorization: Bearer {{token}}
-tenant-id: {{adminTenantId}}
-
-### 鎼滅储娈佃惤鍐呭
-GET {{baseUrl}}/ai/knowledge/segment/search?knowledgeId=2&content=濡備綍浣跨敤杩欎釜浜у搧&topK=5&similarityThreshold=0.1
-Content-Type: application/json
-Authorization: Bearer {{token}}
-tenant-id: {{adminTenantId}}
-
-### 鑾峰彇鏂囨。澶勭悊鍒楄〃
-GET {{baseUrl}}/ai/knowledge/segment/get-process-list?documentIds=1,2,3
-Content-Type: application/json
-Authorization: Bearer {{token}}
-tenant-id: {{adminTenantId}}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java
index 6c87be8..2d30199 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/AiKnowledgeSegmentController.java
@@ -1,39 +1,30 @@
package cn.iocoder.yudao.module.ai.controller.admin.knowledge;
-import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.*;
-import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService;
import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO;
import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO;
import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.Parameter;
-import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
-import org.hibernate.validator.constraints.URL;
import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
-import java.util.Collections;
+import java.util.ArrayList;
import java.util.List;
-import java.util.Map;
+import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
-@Tag(name = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撴钀�")
+@Tag(name = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撳垎娈�")
@RestController
@RequestMapping("/ai/knowledge/segment")
-@Validated
public class AiKnowledgeSegmentController {
@Resource
@@ -41,99 +32,105 @@
@Resource
private AiKnowledgeDocumentService documentService;
- @GetMapping("/get")
- @Operation(summary = "鑾峰彇娈佃惤璇︽儏")
- @Parameter(name = "id", description = "娈佃惤缂栧彿", required = true, example = "1024")
+ @GetMapping("/split")
+ @Operation(summary = "棰勮鏂囨。鍒囧垎缁撴灉")
@PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
- public CommonResult<AiKnowledgeSegmentRespVO> getKnowledgeSegment(@RequestParam("id") Long id) {
- AiKnowledgeSegmentDO segment = segmentService.getKnowledgeSegment(id);
- return success(BeanUtils.toBean(segment, AiKnowledgeSegmentRespVO.class));
+ public CommonResult<List<AiKnowledgeSegmentSplitRespVO>> previewSplit(@Valid AiKnowledgeSegmentSplitReqVO reqVO) {
+ String fileUrl = reqVO.getUrl();
+ if (fileUrl == null || fileUrl.isEmpty()) {
+ throw new IllegalArgumentException("鏂囨。 URL 涓嶈兘涓虹┖");
+ }
+ // 浠� URL 璺緞涓彁鍙栨枃浠跺悕
+ String name = fileUrl;
+ int queryIdx = name.indexOf('?');
+ if (queryIdx > 0) name = name.substring(0, queryIdx);
+ int slashIdx = name.lastIndexOf('/');
+ name = slashIdx >= 0 ? name.substring(slashIdx + 1) : name;
+ List<String> segments = documentService.previewSplit(fileUrl, name, reqVO.getSegmentMaxTokens());
+ List<AiKnowledgeSegmentSplitRespVO> result = new ArrayList<>();
+ for (String content : segments) {
+ result.add(new AiKnowledgeSegmentSplitRespVO()
+ .setContent(content)
+ .setContentLength(content.length())
+ .setTokens(estimateTokens(content)));
+ }
+ return success(result);
+ }
+
+ private int estimateTokens(String text) {
+ if (text == null || text.isEmpty()) return 0;
+ int chineseChars = 0, englishWords = 0;
+ for (char c : text.toCharArray()) {
+ if (c >= 0x4E00 && c <= 0x9FA5) chineseChars++;
+ }
+ for (String word : text.split("\\s+")) {
+ if (word.matches(".*[a-zA-Z].*")) englishWords++;
+ }
+ return chineseChars + (int) (englishWords * 1.3);
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "鑾峰彇鍒嗘")
+ @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
+ public CommonResult<AiKnowledgeSegmentRespVO> getSegment(@RequestParam("id") Long id) {
+ return success(BeanUtils.toBean(segmentService.getSegment(id), AiKnowledgeSegmentRespVO.class));
}
@GetMapping("/page")
- @Operation(summary = "鑾峰彇娈佃惤鍒嗛〉")
+ @Operation(summary = "鑾峰彇鍒嗘鍒嗛〉")
@PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
- public CommonResult<PageResult<AiKnowledgeSegmentRespVO>> getKnowledgeSegmentPage(
- @Valid AiKnowledgeSegmentPageReqVO pageReqVO) {
- PageResult<AiKnowledgeSegmentDO> pageResult = segmentService.getKnowledgeSegmentPage(pageReqVO);
+ public CommonResult<PageResult<AiKnowledgeSegmentRespVO>> getSegmentPage(@Valid AiKnowledgeSegmentPageReqVO pageReqVO) {
+ PageResult<AiKnowledgeSegmentDO> pageResult = segmentService.getSegmentPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, AiKnowledgeSegmentRespVO.class));
}
@PostMapping("/create")
- @Operation(summary = "鍒涘缓娈佃惤")
+ @Operation(summary = "鍒涘缓鍒嗘")
@PreAuthorize("@ss.hasPermission('ai:knowledge:create')")
- public CommonResult<Long> createKnowledgeSegment(@Valid @RequestBody AiKnowledgeSegmentSaveReqVO createReqVO) {
- return success(segmentService.createKnowledgeSegment(createReqVO));
+ public CommonResult<Long> createSegment(@Valid @RequestBody AiKnowledgeSegmentSaveReqVO saveReqVO) {
+ return success(segmentService.createSegment(saveReqVO));
}
@PutMapping("/update")
- @Operation(summary = "鏇存柊娈佃惤鍐呭")
+ @Operation(summary = "鏇存柊鍒嗘")
@PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
- public CommonResult<Boolean> updateKnowledgeSegment(@Valid @RequestBody AiKnowledgeSegmentSaveReqVO reqVO) {
- segmentService.updateKnowledgeSegment(reqVO);
- return success(true);
- }
-
- @PutMapping("/update-status")
- @Operation(summary = "鍚鐢ㄦ钀藉唴瀹�")
- @PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
- public CommonResult<Boolean> updateKnowledgeSegmentStatus(
- @Valid @RequestBody AiKnowledgeSegmentUpdateStatusReqVO reqVO) {
- segmentService.updateKnowledgeSegmentStatus(reqVO);
+ public CommonResult<Boolean> updateSegment(@Valid @RequestBody AiKnowledgeSegmentSaveReqVO saveReqVO) {
+ segmentService.updateSegment(saveReqVO);
return success(true);
}
@DeleteMapping("/delete")
- @Operation(summary = "鍒犻櫎娈佃惤")
- @Parameter(name = "id", description = "娈佃惤缂栧彿", required = true, example = "1024")
+ @Operation(summary = "鍒犻櫎鍒嗘")
@PreAuthorize("@ss.hasPermission('ai:knowledge:delete')")
- public CommonResult<Boolean> deleteKnowledgeSegment(@RequestParam("id") Long id) {
- segmentService.deleteKnowledgeSegment(id);
+ public CommonResult<Boolean> deleteSegment(@RequestParam("id") Long id) {
+ segmentService.deleteSegment(id);
return success(true);
}
- @GetMapping("/split")
- @Operation(summary = "鍒囩墖鍐呭")
- @Parameters({
- @Parameter(name = "url", description = "鏂囨。 URL", required = true),
- @Parameter(name = "segmentMaxTokens", description = "鍒嗘鐨勬渶澶� Token 鏁�", required = true)
- })
- @PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
- public CommonResult<List<AiKnowledgeSegmentRespVO>> splitContent(
- @RequestParam("url") @URL String url,
- @RequestParam(value = "segmentMaxTokens") Integer segmentMaxTokens) {
- List<AiKnowledgeSegmentDO> segments = segmentService.splitContent(url, segmentMaxTokens);
- return success(BeanUtils.toBean(segments, AiKnowledgeSegmentRespVO.class));
+ @PutMapping("/update-status")
+ @Operation(summary = "鏇存柊鍒嗘鐘舵��")
+ @PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
+ public CommonResult<Boolean> updateSegmentStatus(@Valid @RequestBody AiKnowledgeSegmentUpdateStatusReqVO updateStatusReqVO) {
+ segmentService.updateSegmentStatus(updateStatusReqVO);
+ return success(true);
}
@GetMapping("/get-process-list")
- @Operation(summary = "鑾峰彇鏂囨。澶勭悊鍒楄〃")
- @Parameter(name = "documentIds", description = "鏂囨。缂栧彿鍒楄〃", required = true, example = "1,2,3")
+ @Operation(summary = "鑾峰彇鏂囨。澶勭悊杩涘害")
@PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
- public CommonResult<List<AiKnowledgeSegmentProcessRespVO>> getKnowledgeSegmentProcessList(
- @RequestParam("documentIds") List<Long> documentIds) {
- List<AiKnowledgeSegmentProcessRespVO> list = segmentService.getKnowledgeSegmentProcessList(documentIds);
- return success(list);
+ public CommonResult<List<AiKnowledgeSegmentProcessRespVO>> getProcessList(@RequestParam("documentIds") List<Long> documentIds) {
+ return success(documentService.getDocumentProcessingProgress(documentIds));
}
- @GetMapping("/search")
- @Operation(summary = "鎼滅储娈佃惤鍐呭")
+ @PostMapping("/search")
+ @Operation(summary = "妫�绱㈠垎娈碉紙RAG 鍚戦噺妫�绱級")
@PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
- public CommonResult<List<AiKnowledgeSegmentSearchRespVO>> searchKnowledgeSegment(
- @Valid AiKnowledgeSegmentSearchReqVO reqVO) {
- // 1. 鎼滅储娈佃惤
- List<AiKnowledgeSegmentSearchRespBO> segments = segmentService
- .searchKnowledgeSegment(BeanUtils.toBean(reqVO, AiKnowledgeSegmentSearchReqBO.class));
- if (CollUtil.isEmpty(segments)) {
- return success(Collections.emptyList());
- }
-
- // 2. 鎷兼帴 VO
- Map<Long, AiKnowledgeDocumentDO> documentMap = documentService.getKnowledgeDocumentMap(convertSet(
- segments, AiKnowledgeSegmentSearchRespBO::getDocumentId));
- return success(BeanUtils.toBean(segments, AiKnowledgeSegmentSearchRespVO.class,
- segment -> MapUtils.findAndThen(documentMap, segment.getDocumentId(),
- document -> segment.setDocumentName(document.getName()))));
+ public CommonResult<List<AiKnowledgeSegmentSearchRespVO>> searchSegments(@Valid @RequestBody AiKnowledgeSegmentSearchReqVO searchReqVO) {
+ AiKnowledgeSegmentSearchReqBO searchReqBO = BeanUtils.toBean(searchReqVO, AiKnowledgeSegmentSearchReqBO.class);
+ List<AiKnowledgeSegmentSearchRespBO> results = segmentService.searchSegments(searchReqBO);
+ return success(results.stream()
+ .map(bo -> BeanUtils.toBean(bo, AiKnowledgeSegmentSearchRespVO.class))
+ .collect(Collectors.toList()));
}
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentCreateListReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentCreateListReqVO.java
deleted file mode 100644
index 6545c0b..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentCreateListReqVO.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotBlank;
-import jakarta.validation.constraints.NotEmpty;
-import jakarta.validation.constraints.NotNull;
-import lombok.Data;
-import org.hibernate.validator.constraints.URL;
-
-import java.util.List;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撴枃妗f壒閲忓垱寤� Request VO")
-@Data
-public class AiKnowledgeDocumentCreateListReqVO {
-
- @Schema(description = "鐭ヨ瘑搴撶紪鍙�", requiredMode = Schema.RequiredMode.REQUIRED, example = "1204")
- @NotNull(message = "鐭ヨ瘑搴撶紪鍙蜂笉鑳戒负绌�")
- private Long knowledgeId;
-
- @Schema(description = "鍒嗘鐨勬渶澶� Token 鏁�", requiredMode = Schema.RequiredMode.REQUIRED, example = "800")
- @NotNull(message = "鍒嗘鐨勬渶澶� Token 鏁颁笉鑳戒负绌�")
- private Integer segmentMaxTokens;
-
- @Schema(description = "鏂囨。鍒楄〃", requiredMode = Schema.RequiredMode.REQUIRED)
- @NotEmpty(message = "鏂囨。鍒楄〃涓嶈兘涓虹┖")
- private List<Document> list;
-
- @Schema(description = "鏂囨。")
- @Data
- public static class Document {
-
- @Schema(description = "鏂囨。鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED, example = "涓夋柟鐧婚檰")
- @NotBlank(message = "鏂囨。鍚嶇О涓嶈兘涓虹┖")
- private String name;
-
- @Schema(description = "鏂囨。 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn")
- @URL(message = "鏂囨。 URL 鏍煎紡涓嶆纭�")
- private String url;
-
- }
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentCreateReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentCreateReqVO.java
new file mode 100644
index 0000000..f33662a
--- /dev/null
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentCreateReqVO.java
@@ -0,0 +1,49 @@
+package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+import java.util.List;
+
+@Schema(description = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撴枃妗e垱寤� Request VO")
+@Data
+public class AiKnowledgeDocumentCreateReqVO {
+
+ @Schema(description = "鐭ヨ瘑搴撶紪鍙�", example = "1")
+ @NotNull(message = "鐭ヨ瘑搴撲笉鑳戒负绌�")
+ private Long knowledgeId;
+
+ @Schema(description = "鍒嗘鏈�澶� Token 鏁�", example = "500")
+ private Integer segmentMaxTokens;
+
+ @Schema(description = "鏂囨。鍒楄〃")
+ @NotEmpty(message = "鏂囨。鍒楄〃涓嶈兘涓虹┖")
+ private List<@Valid DocumentItem> list;
+
+ @Data
+ public static class DocumentItem {
+
+ @Schema(description = "鏂囨。鍚嶇О", example = "浜у搧鎵嬪唽.pdf")
+ @NotEmpty(message = "鏂囨。鍚嶇О涓嶈兘涓虹┖")
+ private String name;
+
+ @Schema(description = "鏂囨。 URL 淇℃伅")
+ private UrlInfo url;
+ }
+
+ @Data
+ public static class UrlInfo {
+
+ @Schema(description = "鏂囨。 URL")
+ private String url;
+
+ @Schema(description = "鏂囨。鍚嶇О")
+ private String name;
+
+ @Schema(description = "鏂囦欢 ID")
+ private Long id;
+ }
+}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java
index 15bb603..1b686fa 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentPageReqVO.java
@@ -3,15 +3,17 @@
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
+import lombok.EqualsAndHashCode;
-@Schema(description = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撴枃妗g殑鍒嗛〉 Request VO")
+@Schema(description = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撴枃妗e垎椤� Request VO")
@Data
+@EqualsAndHashCode(callSuper = true)
public class AiKnowledgeDocumentPageReqVO extends PageParam {
@Schema(description = "鐭ヨ瘑搴撶紪鍙�", example = "1")
private Long knowledgeId;
- @Schema(description = "鏂囨。鍚嶇О", example = "Java 寮�鍙戞墜鍐�")
+ @Schema(description = "鏂囨。鍚嶇О")
private String name;
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java
index 7aef94a..d1b7a38 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentRespVO.java
@@ -2,44 +2,40 @@
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
-
import java.time.LocalDateTime;
@Schema(description = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撴枃妗� Response VO")
@Data
public class AiKnowledgeDocumentRespVO {
- @Schema(description = "鏂囨。缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
+ @Schema(description = "缂栧彿", example = "1")
private Long id;
- @Schema(description = "鐭ヨ瘑搴撶紪鍙�", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
+ @Schema(description = "鐭ヨ瘑搴撶紪鍙�", example = "1")
private Long knowledgeId;
- @Schema(description = "鏂囨。鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 寮�鍙戞墜鍐�")
+ @Schema(description = "鏂囨。鍚嶇О", example = "浜у搧鎵嬪唽")
private String name;
- @Schema(description = "鏂囨。 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn")
+ @Schema(description = "鏂囨。 URL")
private String url;
- @Schema(description = "鏂囨。鍐呭", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 鏄竴闂ㄩ潰鍚戝璞$殑璇█.....")
- private String content;
-
- @Schema(description = "鏂囨。鍐呭闀垮害", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
+ @Schema(description = "鍐呭闀垮害")
private Integer contentLength;
- @Schema(description = "鏂囨。 Token 鏁伴噺", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ @Schema(description = "Token 鏁�")
private Integer tokens;
- @Schema(description = "鍒嗙墖鏈�澶� Token 鏁�", requiredMode = Schema.RequiredMode.REQUIRED, example = "512")
+ @Schema(description = "鍒嗘鏈�澶� Token 鏁�")
private Integer segmentMaxTokens;
- @Schema(description = "鍙洖娆℃暟", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+ @Schema(description = "妫�绱㈡鏁�")
private Integer retrievalCount;
- @Schema(description = "鏂囨。鐘舵��", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
+ @Schema(description = "鐘舵��", example = "0")
private Integer status;
- @Schema(description = "鍒涘缓鏃堕棿", requiredMode = Schema.RequiredMode.REQUIRED)
+ @Schema(description = "鍒涘缓鏃堕棿")
private LocalDateTime createTime;
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java
index e6dbe5c..c3913c5 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateReqVO.java
@@ -8,14 +8,14 @@
@Data
public class AiKnowledgeDocumentUpdateReqVO {
- @Schema(description = "缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583")
+ @Schema(description = "缂栧彿", example = "1")
@NotNull(message = "缂栧彿涓嶈兘涓虹┖")
private Long id;
- @Schema(description = "鍚嶇О", example = "Java 寮�鍙戞墜鍐�")
+ @Schema(description = "鏂囨。鍚嶇О", example = "浜у搧鎵嬪唽")
private String name;
- @Schema(description = "鍒嗙墖鏈�澶� Token 鏁�", example = "1000")
- private Integer segmentMaxTokens;
+ @Schema(description = "鏂囨。 URL")
+ private String url;
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateStatusReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateStatusReqVO.java
index 93d393a..26dc7b6 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateStatusReqVO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/document/AiKnowledgeDocumentUpdateStatusReqVO.java
@@ -1,7 +1,5 @@
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@@ -10,13 +8,12 @@
@Data
public class AiKnowledgeDocumentUpdateStatusReqVO {
- @Schema(description = "缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583")
+ @Schema(description = "缂栧彿", example = "1")
@NotNull(message = "缂栧彿涓嶈兘涓虹┖")
private Long id;
- @Schema(description = "鐘舵��", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
+ @Schema(description = "鐘舵��", example = "0")
@NotNull(message = "鐘舵�佷笉鑳戒负绌�")
- @InEnum(CommonStatusEnum.class)
private Integer status;
-}
\ No newline at end of file
+}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java
deleted file mode 100644
index 1d2e493..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeDocumentCreateReqVO.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotBlank;
-import jakarta.validation.constraints.NotNull;
-import lombok.Data;
-import org.hibernate.validator.constraints.URL;
-
-
-@Schema(description = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撴枃妗g殑鍒涘缓 Request VO")
-@Data
-public class AiKnowledgeDocumentCreateReqVO {
-
- @Schema(description = "鐭ヨ瘑搴撶紪鍙�", requiredMode = Schema.RequiredMode.REQUIRED, example = "1204")
- @NotNull(message = "鐭ヨ瘑搴撶紪鍙蜂笉鑳戒负绌�")
- private Long knowledgeId;
-
- @Schema(description = "鏂囨。鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED, example = "涓夋柟鐧婚檰")
- @NotBlank(message = "鏂囨。鍚嶇О涓嶈兘涓虹┖")
- private String name;
-
- @Schema(description = "鏂囨。 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn")
- @URL(message = "鏂囨。 URL 鏍煎紡涓嶆纭�")
- private String url;
-
- @Schema(description = "鍒嗘鐨勬渶澶� Token 鏁�", requiredMode = Schema.RequiredMode.REQUIRED, example = "800")
- @NotNull(message = "鍒嗘鐨勬渶澶� Token 鏁颁笉鑳戒负绌�")
- private Integer segmentMaxTokens;
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgePageReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgePageReqVO.java
index dc7943c..e7fc276 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgePageReqVO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgePageReqVO.java
@@ -1,25 +1,22 @@
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
-import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
+import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;
-
import java.time.LocalDateTime;
-
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
-@Schema(description = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撶殑鍒嗛〉 Request VO")
+@Schema(description = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撳垎椤� Request VO")
@Data
+@EqualsAndHashCode(callSuper = true)
public class AiKnowledgePageReqVO extends PageParam {
- @Schema(description = "鐭ヨ瘑搴撳悕绉�", example = "鑺嬭壙")
+ @Schema(description = "鐭ヨ瘑搴撳悕绉�")
private String name;
- @Schema(description = "鏄惁鍚敤", example = "1")
- @InEnum(CommonStatusEnum.class)
+ @Schema(description = "鐘舵��")
private Integer status;
@Schema(description = "鍒涘缓鏃堕棿")
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java
index 5e83b85..c7890a7 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeRespVO.java
@@ -2,38 +2,37 @@
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
-
import java.time.LocalDateTime;
@Schema(description = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴� Response VO")
@Data
public class AiKnowledgeRespVO {
- @Schema(description = "缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
+ @Schema(description = "缂栧彿", example = "1")
private Long id;
- @Schema(description = "鐭ヨ瘑搴撳悕绉�", requiredMode = Schema.RequiredMode.REQUIRED, example = "ruoyi-vue-pro 鐢ㄦ埛鎸囧崡")
+ @Schema(description = "鐭ヨ瘑搴撳悕绉�", example = "浜у搧鏂囨。")
private String name;
- @Schema(description = "鐭ヨ瘑搴撴弿杩�", example = "甯姪浣犲揩閫熸瀯寤虹郴缁�")
+ @Schema(description = "鐭ヨ瘑搴撴弿杩�", example = "浜у搧鐩稿叧鏂囨。")
private String description;
- @Schema(description = "鍚戦噺妯″瀷缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "14")
+ @Schema(description = "鍚戦噺妯″瀷缂栧彿", example = "1")
private Long embeddingModelId;
- @Schema(description = "鍚戦噺妯″瀷鏍囪瘑", requiredMode = Schema.RequiredMode.REQUIRED, example = "qwen-72b-chat")
+ @Schema(description = "鍚戦噺妯″瀷鏍囪瘑", example = "text-embedding-v3")
private String embeddingModel;
- @Schema(description = "topK", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
+ @Schema(description = "TopK", example = "3")
private Integer topK;
- @Schema(description = "鐩镐技搴﹂槇鍊�", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.7")
+ @Schema(description = "鐩镐技搴﹂槇鍊�", example = "0.7")
private Double similarityThreshold;
- @Schema(description = "鐘舵��", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @Schema(description = "鐘舵��", example = "0")
private Integer status;
- @Schema(description = "鍒涘缓鏃堕棿", requiredMode = Schema.RequiredMode.REQUIRED)
+ @Schema(description = "鍒涘缓鏃堕棿")
private LocalDateTime createTime;
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeSaveReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeSaveReqVO.java
index 774b723..b941d95 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeSaveReqVO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/knowledge/AiKnowledgeSaveReqVO.java
@@ -1,9 +1,7 @@
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@@ -11,31 +9,27 @@
@Data
public class AiKnowledgeSaveReqVO {
- @Schema(description = "瀵硅瘽缂栧彿", example = "1204")
+ @Schema(description = "缂栧彿", example = "1")
private Long id;
- @Schema(description = "鐭ヨ瘑搴撳悕绉�", requiredMode = Schema.RequiredMode.REQUIRED, example = "ruoyi-vue-pro 鐢ㄦ埛鎸囧崡")
- @NotBlank(message = "鐭ヨ瘑搴撳悕绉颁笉鑳戒负绌�")
+ @Schema(description = "鐭ヨ瘑搴撳悕绉�", example = "浜у搧鏂囨。")
+ @NotEmpty(message = "鐭ヨ瘑搴撳悕绉颁笉鑳戒负绌�")
private String name;
- @Schema(description = "鐭ヨ瘑搴撴弿杩�", requiredMode = Schema.RequiredMode.REQUIRED, example = "瀛樺偍 ruoyi-vue-pro 鎿嶄綔鏂囨。")
+ @Schema(description = "鐭ヨ瘑搴撴弿杩�", example = "浜у搧鐩稿叧鏂囨。")
private String description;
- @Schema(description = "鍚戦噺妯″瀷缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @Schema(description = "鍚戦噺妯″瀷缂栧彿", example = "1")
@NotNull(message = "鍚戦噺妯″瀷涓嶈兘涓虹┖")
private Long embeddingModelId;
- @Schema(description = "topK", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
- @NotNull(message = "topK 涓嶈兘涓虹┖")
+ @Schema(description = "TopK", example = "3")
private Integer topK;
- @Schema(description = "鐩镐技鎬ч槇鍊�", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.5")
- @NotNull(message = "鐩镐技鎬ч槇鍊间笉鑳戒负绌�")
+ @Schema(description = "鐩镐技搴﹂槇鍊�", example = "0.7")
private Double similarityThreshold;
- @Schema(description = "鏄惁鍚敤", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- @NotNull(message = "鏄惁鍚敤涓嶈兘涓虹┖")
- @InEnum(CommonStatusEnum.class)
+ @Schema(description = "鐘舵��", example = "0")
private Integer status;
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java
index dd3b903..e7e9a53 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentPageReqVO.java
@@ -1,23 +1,22 @@
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
-import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
+import lombok.EqualsAndHashCode;
-@Schema(description = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撳垎娈电殑鍒嗛〉 Request VO")
+@Schema(description = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撳垎娈靛垎椤� Request VO")
@Data
+@EqualsAndHashCode(callSuper = true)
public class AiKnowledgeSegmentPageReqVO extends PageParam {
@Schema(description = "鏂囨。缂栧彿", example = "1")
private Long documentId;
- @Schema(description = "鍒嗘鍐呭鍏抽敭瀛�", example = "Java 寮�鍙�")
+ @Schema(description = "鍒嗘鍐呭")
private String content;
- @Schema(description = "鍒嗘鐘舵��", example = "1")
- @InEnum(CommonStatusEnum.class)
+ @Schema(description = "鐘舵��")
private Integer status;
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentProcessRespVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentProcessRespVO.java
index a6b9526..500e6a6 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentProcessRespVO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentProcessRespVO.java
@@ -3,17 +3,17 @@
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
-@Schema(description = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撴钀藉悜閲忚繘搴� Response VO")
+@Schema(description = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撳垎娈靛鐞嗚繘搴� Response VO")
@Data
public class AiKnowledgeSegmentProcessRespVO {
- @Schema(description = "鏂囨。缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @Schema(description = "鏂囨。缂栧彿", example = "1")
private Long documentId;
- @Schema(description = "鎬绘钀芥暟閲�", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+ @Schema(description = "鍒嗘鎬绘暟")
private Long count;
- @Schema(description = "宸插悜閲忓寲娈佃惤鏁伴噺", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
+ @Schema(description = "宸插悜閲忓寲鏁伴噺")
private Long embeddingCount;
-}
\ No newline at end of file
+}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java
index 24c4526..76a01a3 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentRespVO.java
@@ -2,39 +2,40 @@
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
+import java.time.LocalDateTime;
-@Schema(description = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撴枃妗e垎鐗� Response VO")
+@Schema(description = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撳垎娈� Response VO")
@Data
public class AiKnowledgeSegmentRespVO {
- @Schema(description = "缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
+ @Schema(description = "缂栧彿", example = "1")
private Long id;
- @Schema(description = "鏂囨。缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
- private Long documentId;
-
- @Schema(description = "鐭ヨ瘑搴撶紪鍙�", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
+ @Schema(description = "鐭ヨ瘑搴撶紪鍙�", example = "1")
private Long knowledgeId;
- @Schema(description = "鍚戦噺搴撶紪鍙�", requiredMode = Schema.RequiredMode.REQUIRED, example = "1858496a-1dde-4edf-a43e-0aed08f37f8c")
- private String vectorId;
+ @Schema(description = "鏂囨。缂栧彿", example = "1")
+ private Long documentId;
- @Schema(description = "鍒囩墖鍐呭", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 寮�鍙戞墜鍐�")
+ @Schema(description = "鍒嗘鍐呭")
private String content;
- @Schema(description = "鍒囩墖鍐呭闀垮害", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ @Schema(description = "鍐呭闀垮害")
private Integer contentLength;
- @Schema(description = "token 鏁伴噺", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ @Schema(description = "鍚戦噺 ID")
+ private String vectorId;
+
+ @Schema(description = "Token 鏁�")
private Integer tokens;
- @Schema(description = "鍙洖娆℃暟", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+ @Schema(description = "妫�绱㈡鏁�")
private Integer retrievalCount;
- @Schema(description = "鏂囨。鐘舵��", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @Schema(description = "鐘舵��", example = "0")
private Integer status;
- @Schema(description = "鍒涘缓鏃堕棿", requiredMode = Schema.RequiredMode.REQUIRED)
- private Long createTime;
+ @Schema(description = "鍒涘缓鏃堕棿")
+ private LocalDateTime createTime;
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSaveReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSaveReqVO.java
index 0c5dad1..8ca72a1 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSaveReqVO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSaveReqVO.java
@@ -2,20 +2,27 @@
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
import lombok.Data;
-@Schema(description = "绠$悊鍚庡彴 - AI 鏂板/淇敼鐭ヨ瘑搴撴钀� request VO")
+@Schema(description = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撳垎娈垫柊澧�/淇敼 Request VO")
@Data
public class AiKnowledgeSegmentSaveReqVO {
- @Schema(description = "缂栧彿", example = "24790")
+ @Schema(description = "缂栧彿", example = "1")
private Long id;
- @Schema(description = "鐭ヨ瘑搴撴枃妗g紪鍙�", example = "1024")
+ @Schema(description = "鐭ヨ瘑搴撶紪鍙凤紙鍒涘缓鏃跺繀濉紝鏇存柊鏃跺彲浠庡凡鏈夊垎娈佃幏鍙栵級", example = "1")
+ private Long knowledgeId;
+
+ @Schema(description = "鏂囨。缂栧彿锛堝垱寤烘椂蹇呭~锛屾洿鏂版椂鍙粠宸叉湁鍒嗘鑾峰彇锛�", example = "1")
private Long documentId;
- @Schema(description = "鍒囩墖鍐呭", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 寮�鍙戞墜鍐�")
- @NotEmpty(message = "鍒囩墖鍐呭涓嶈兘涓虹┖")
+ @Schema(description = "鍒嗘鍐呭")
+ @NotEmpty(message = "鍒嗘鍐呭涓嶈兘涓虹┖")
private String content;
+ @Schema(description = "鐘舵��", example = "0")
+ private Integer status;
+
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchReqVO.java
index 3b3cd98..6eca979 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchReqVO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchReqVO.java
@@ -1,24 +1,23 @@
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
+import lombok.Data;
-@Schema(description = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撴钀芥悳绱� Request VO")
+@Schema(description = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撳垎娈垫绱� Request VO")
@Data
public class AiKnowledgeSegmentSearchReqVO {
- @Schema(description = "鐭ヨ瘑搴撶紪鍙�", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ @Schema(description = "鐭ヨ瘑搴撶紪鍙�", example = "1")
@NotNull(message = "鐭ヨ瘑搴撶紪鍙蜂笉鑳戒负绌�")
private Long knowledgeId;
- @Schema(description = "鍐呭", requiredMode = Schema.RequiredMode.REQUIRED, example = "濡備綍浣跨敤杩欎釜浜у搧")
- @NotEmpty(message = "鍐呭涓嶈兘涓虹┖")
+ @Schema(description = "妫�绱㈠唴瀹�")
+ @NotEmpty(message = "妫�绱㈠唴瀹逛笉鑳戒负绌�")
private String content;
- @Schema(description = "鏈�澶ц繑鍥炴暟閲�", example = "5")
+ @Schema(description = "杩斿洖鏁伴噺", example = "3")
private Integer topK;
@Schema(description = "鐩镐技搴﹂槇鍊�", example = "0.7")
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchRespVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchRespVO.java
index 50bbc5c..8efbf47 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchRespVO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSearchRespVO.java
@@ -3,14 +3,32 @@
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
-@Schema(description = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撴钀芥悳绱� Response VO")
+@Schema(description = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撳垎娈垫绱� Response VO")
@Data
-public class AiKnowledgeSegmentSearchRespVO extends AiKnowledgeSegmentRespVO {
+public class AiKnowledgeSegmentSearchRespVO {
- @Schema(description = "鏂囨。鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED, example = "浜у搧浣跨敤鎵嬪唽")
+ @Schema(description = "鍒嗘缂栧彿", example = "1")
+ private Long id;
+
+ @Schema(description = "鏂囨。缂栧彿", example = "1")
+ private Long documentId;
+
+ @Schema(description = "鏂囨。鍚嶇О", example = "xxx.pdf")
private String documentName;
- @Schema(description = "鐩镐技搴﹀垎鏁�", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.95")
+ @Schema(description = "鐭ヨ瘑搴撶紪鍙�", example = "1")
+ private Long knowledgeId;
+
+ @Schema(description = "鍒嗘鍐呭")
+ private String content;
+
+ @Schema(description = "鍐呭闀垮害")
+ private Integer contentLength;
+
+ @Schema(description = "Token 鏁�")
+ private Integer tokens;
+
+ @Schema(description = "鐩镐技搴﹀垎鏁�", example = "0.85")
private Double score;
-}
\ No newline at end of file
+}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSplitReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSplitReqVO.java
new file mode 100644
index 0000000..e90aa62
--- /dev/null
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSplitReqVO.java
@@ -0,0 +1,16 @@
+package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撴枃妗e垏鍒嗛瑙� Request VO")
+@Data
+public class AiKnowledgeSegmentSplitReqVO {
+
+ @Schema(description = "鍒嗘鏈�澶� Token 鏁�", example = "500")
+ private Integer segmentMaxTokens;
+
+ @Schema(description = "鏂囨。涓嬭浇 URL锛堝畬鏁寸殑鏂囦欢鍦板潃锛�")
+ private String url;
+
+}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSplitRespVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSplitRespVO.java
new file mode 100644
index 0000000..3f5aa8c
--- /dev/null
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentSplitRespVO.java
@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撴枃妗e垏鍒嗛瑙� Response VO")
+@Data
+public class AiKnowledgeSegmentSplitRespVO {
+
+ @Schema(description = "鍒嗘鏂囨湰")
+ private String content;
+
+ @Schema(description = "鍐呭闀垮害")
+ private Integer contentLength;
+
+ @Schema(description = "浼扮畻 Token 鏁�")
+ private Integer tokens;
+
+}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateStatusReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateStatusReqVO.java
index 2516c7d..b8ee07c 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateStatusReqVO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/knowledge/vo/segment/AiKnowledgeSegmentUpdateStatusReqVO.java
@@ -1,22 +1,19 @@
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撴钀界殑鏇存柊鐘舵�� Request VO")
+@Schema(description = "绠$悊鍚庡彴 - AI 鐭ヨ瘑搴撳垎娈垫洿鏂扮姸鎬� Request VO")
@Data
public class AiKnowledgeSegmentUpdateStatusReqVO {
- @Schema(description = "缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
+ @Schema(description = "缂栧彿", example = "1")
+ @NotNull(message = "缂栧彿涓嶈兘涓虹┖")
private Long id;
- @Schema(description = "鏄惁鍚敤", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- @NotNull(message = "鏄惁鍚敤涓嶈兘涓虹┖")
- @InEnum(CommonStatusEnum.class)
+ @Schema(description = "鐘舵��", example = "0")
+ @NotNull(message = "鐘舵�佷笉鑳戒负绌�")
private Integer status;
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/AiMindMapController.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/AiMindMapController.java
deleted file mode 100644
index db015e5..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/AiMindMapController.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.mindmap;
-
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapGenerateReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapPageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapRespVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.mindmap.AiMindMapDO;
-import cn.iocoder.yudao.module.ai.service.mindmap.AiMindMapService;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.Parameter;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import jakarta.annotation.Resource;
-import jakarta.validation.Valid;
-import org.springframework.http.MediaType;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
-import reactor.core.publisher.Flux;
-
-import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
-
-@Tag(name = "绠$悊鍚庡彴 - AI 鎬濈淮瀵煎浘")
-@RestController
-@RequestMapping("/ai/mind-map")
-public class AiMindMapController {
-
- @Resource
- private AiMindMapService mindMapService;
-
- @PostMapping(value = "/generate-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
- @Operation(summary = "瀵煎浘鐢熸垚锛堟祦寮忥級", description = "娴佸紡杩斿洖锛屽搷搴旇緝蹇�")
- public Flux<CommonResult<String>> generateMindMap(@RequestBody @Valid AiMindMapGenerateReqVO generateReqVO) {
- return mindMapService.generateMindMap(generateReqVO, getLoginUserId());
- }
-
- // ================ 瀵煎浘绠$悊 ================
-
- @DeleteMapping("/delete")
- @Operation(summary = "鍒犻櫎鎬濈淮瀵煎浘")
- @Parameter(name = "id", description = "缂栧彿", required = true)
- @PreAuthorize("@ss.hasPermission('ai:mind-map:delete')")
- public CommonResult<Boolean> deleteMindMap(@RequestParam("id") Long id) {
- mindMapService.deleteMindMap(id);
- return success(true);
- }
-
- @GetMapping("/page")
- @Operation(summary = "鑾峰緱鎬濈淮瀵煎浘鍒嗛〉")
- @PreAuthorize("@ss.hasPermission('ai:mind-map:query')")
- public CommonResult<PageResult<AiMindMapRespVO>> getMindMapPage(@Valid AiMindMapPageReqVO pageReqVO) {
- PageResult<AiMindMapDO> pageResult = mindMapService.getMindMapPage(pageReqVO);
- return success(BeanUtils.toBean(pageResult, AiMindMapRespVO.class));
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/vo/AiMindMapGenerateReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/vo/AiMindMapGenerateReqVO.java
deleted file mode 100644
index 08404bb..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/vo/AiMindMapGenerateReqVO.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotBlank;
-import lombok.Data;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 鎬濈淮瀵煎浘鐢熸垚 Request VO")
-@Data
-public class AiMindMapGenerateReqVO {
-
- @Schema(description = "鎬濈淮瀵煎浘鍐呭鎻愮ず", example = "Java 瀛︿範璺嚎")
- @NotBlank(message = "鎬濈淮瀵煎浘鍐呭鎻愮ず涓嶈兘涓虹┖")
- private String prompt;
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/vo/AiMindMapPageReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/vo/AiMindMapPageReqVO.java
deleted file mode 100644
index f7769b4..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/vo/AiMindMapPageReqVO.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo;
-
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.ToString;
-import org.springframework.format.annotation.DateTimeFormat;
-
-import java.time.LocalDateTime;
-
-import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 鎬濈淮瀵煎浘鍒嗛〉 Request VO")
-@Data
-public class AiMindMapPageReqVO extends PageParam {
-
- @Schema(description = "鐢ㄦ埛缂栧彿", example = "4325")
- private Long userId;
-
- @Schema(description = "鐢熸垚鍐呭鎻愮ず", example = "Java 瀛︿範璺嚎")
- private String prompt;
-
- @Schema(description = "鍒涘缓鏃堕棿")
- @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
- private LocalDateTime[] createTime;
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/vo/AiMindMapRespVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/vo/AiMindMapRespVO.java
deleted file mode 100644
index f65e809..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/mindmap/vo/AiMindMapRespVO.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-
-import java.time.LocalDateTime;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 鎬濈淮瀵煎浘 Response VO")
-@Data
-public class AiMindMapRespVO {
-
- @Schema(description = "缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "3373")
- private Long id;
-
- @Schema(description = "鐢ㄦ埛缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "4325")
- private Long userId;
-
- @Schema(description = "鐢熸垚鍐呭鎻愮ず", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 瀛︿範璺嚎")
- private String prompt;
-
- @Schema(description = "鐢熸垚鐨勬�濈淮瀵煎浘鍐呭")
- private String generatedContent;
-
- @Schema(description = "骞冲彴", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
- private String platform;
-
- @Schema(description = "妯″瀷", requiredMode = Schema.RequiredMode.REQUIRED, example = "gpt-3.5-turbo-0125")
- private String model;
-
- @Schema(description = "閿欒淇℃伅")
- private String errorMessage;
-
- @Schema(description = "鍒涘缓鏃堕棿", requiredMode = Schema.RequiredMode.REQUIRED)
- private LocalDateTime createTime;
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiApiKeyController.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiApiKeyController.java
index c109b03..6718af0 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiApiKeyController.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiApiKeyController.java
@@ -6,27 +6,20 @@
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyRespVO;
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelRespVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService;
import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
-import java.util.List;
-
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@Tag(name = "绠$悊鍚庡彴 - AI API 瀵嗛挜")
@RestController
@RequestMapping("/ai/api-key")
-@Validated
public class AiApiKeyController {
@Resource
@@ -49,7 +42,6 @@
@DeleteMapping("/delete")
@Operation(summary = "鍒犻櫎 API 瀵嗛挜")
- @Parameter(name = "id", description = "缂栧彿", required = true)
@PreAuthorize("@ss.hasPermission('ai:api-key:delete')")
public CommonResult<Boolean> deleteApiKey(@RequestParam("id") Long id) {
apiKeyService.deleteApiKey(id);
@@ -57,27 +49,18 @@
}
@GetMapping("/get")
- @Operation(summary = "鑾峰緱 API 瀵嗛挜")
- @Parameter(name = "id", description = "缂栧彿", required = true, example = "1024")
+ @Operation(summary = "鑾峰彇 API 瀵嗛挜")
@PreAuthorize("@ss.hasPermission('ai:api-key:query')")
public CommonResult<AiApiKeyRespVO> getApiKey(@RequestParam("id") Long id) {
- AiApiKeyDO apiKey = apiKeyService.getApiKey(id);
- return success(BeanUtils.toBean(apiKey, AiApiKeyRespVO.class));
+ return success(BeanUtils.toBean(apiKeyService.getApiKey(id), AiApiKeyRespVO.class));
}
@GetMapping("/page")
- @Operation(summary = "鑾峰緱 API 瀵嗛挜鍒嗛〉")
+ @Operation(summary = "鑾峰彇 API 瀵嗛挜鍒嗛〉")
@PreAuthorize("@ss.hasPermission('ai:api-key:query')")
public CommonResult<PageResult<AiApiKeyRespVO>> getApiKeyPage(@Valid AiApiKeyPageReqVO pageReqVO) {
PageResult<AiApiKeyDO> pageResult = apiKeyService.getApiKeyPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, AiApiKeyRespVO.class));
}
- @GetMapping("/simple-list")
- @Operation(summary = "鑾峰緱 API 瀵嗛挜鍒嗛〉鍒楄〃")
- public CommonResult<List<AiModelRespVO>> getApiKeySimpleList() {
- List<AiApiKeyDO> list = apiKeyService.getApiKeyList();
- return success(convertList(list, key -> new AiModelRespVO().setId(key.getId()).setName(key.getName())));
- }
-
-}
\ No newline at end of file
+}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatRoleController.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatRoleController.java
deleted file mode 100644
index 81f0734..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatRoleController.java
+++ /dev/null
@@ -1,128 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.model;
-
-import cn.hutool.core.util.ObjUtil;
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRolePageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleRespVO;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveMyReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
-import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
-import org.dromara.core.trans.anno.TransMethodResult;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.Parameter;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import jakarta.annotation.Resource;
-import jakarta.validation.Valid;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.List;
-
-import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
-
-@Tag(name = "绠$悊鍚庡彴 - AI 鑱婂ぉ瑙掕壊")
-@RestController
-@RequestMapping("/ai/chat-role")
-@Validated
-public class AiChatRoleController {
-
- @Resource
- private AiChatRoleService chatRoleService;
-
- @GetMapping("/my-page")
- @Operation(summary = "鑾峰緱銆愭垜鐨勩�戣亰澶╄鑹插垎椤�")
- @TransMethodResult
- public CommonResult<PageResult<AiChatRoleRespVO>> getChatRoleMyPage(@Valid AiChatRolePageReqVO pageReqVO) {
- PageResult<AiChatRoleDO> pageResult = chatRoleService.getChatRoleMyPage(pageReqVO, getLoginUserId());
- return success(BeanUtils.toBean(pageResult, AiChatRoleRespVO.class));
- }
-
- @GetMapping("/get-my")
- @Operation(summary = "鑾峰緱銆愭垜鐨勩�戣亰澶╄鑹�")
- @Parameter(name = "id", description = "缂栧彿", required = true, example = "1024")
- @TransMethodResult
- public CommonResult<AiChatRoleRespVO> getChatRoleMy(@RequestParam("id") Long id) {
- AiChatRoleDO chatRole = chatRoleService.getChatRole(id);
- if (ObjUtil.notEqual(chatRole.getUserId(), getLoginUserId())) {
- return success(null);
- }
- return success(BeanUtils.toBean(chatRole, AiChatRoleRespVO.class));
- }
-
- @PostMapping("/create-my")
- @Operation(summary = "鍒涘缓銆愭垜鐨勩�戣亰澶╄鑹�")
- public CommonResult<Long> createChatRoleMy(@Valid @RequestBody AiChatRoleSaveMyReqVO createReqVO) {
- return success(chatRoleService.createChatRoleMy(createReqVO, getLoginUserId()));
- }
-
- @PutMapping("/update-my")
- @Operation(summary = "鏇存柊銆愭垜鐨勩�戣亰澶╄鑹�")
- public CommonResult<Boolean> updateChatRoleMy(@Valid @RequestBody AiChatRoleSaveMyReqVO updateReqVO) {
- chatRoleService.updateChatRoleMy(updateReqVO, getLoginUserId());
- return success(true);
- }
-
- @DeleteMapping("/delete-my")
- @Operation(summary = "鍒犻櫎銆愭垜鐨勩�戣亰澶╄鑹�")
- @Parameter(name = "id", description = "缂栧彿", required = true)
- public CommonResult<Boolean> deleteChatRoleMy(@RequestParam("id") Long id) {
- chatRoleService.deleteChatRoleMy(id, getLoginUserId());
- return success(true);
- }
-
- @GetMapping("/category-list")
- @Operation(summary = "鑾峰緱鑱婂ぉ瑙掕壊鐨勫垎绫诲垪琛�")
- public CommonResult<List<String>> getChatRoleCategoryList() {
- return success(chatRoleService.getChatRoleCategoryList());
- }
-
- // ========== 瑙掕壊绠$悊 ==========
-
- @PostMapping("/create")
- @Operation(summary = "鍒涘缓鑱婂ぉ瑙掕壊")
- @PreAuthorize("@ss.hasPermission('ai:chat-role:create')")
- public CommonResult<Long> createChatRole(@Valid @RequestBody AiChatRoleSaveReqVO createReqVO) {
- return success(chatRoleService.createChatRole(createReqVO));
- }
-
- @PutMapping("/update")
- @Operation(summary = "鏇存柊鑱婂ぉ瑙掕壊")
- @PreAuthorize("@ss.hasPermission('ai:chat-role:update')")
- public CommonResult<Boolean> updateChatRole(@Valid @RequestBody AiChatRoleSaveReqVO updateReqVO) {
- chatRoleService.updateChatRole(updateReqVO);
- return success(true);
- }
-
- @DeleteMapping("/delete")
- @Operation(summary = "鍒犻櫎鑱婂ぉ瑙掕壊")
- @Parameter(name = "id", description = "缂栧彿", required = true)
- @PreAuthorize("@ss.hasPermission('ai:chat-role:delete')")
- public CommonResult<Boolean> deleteChatRole(@RequestParam("id") Long id) {
- chatRoleService.deleteChatRole(id);
- return success(true);
- }
-
- @GetMapping("/get")
- @Operation(summary = "鑾峰緱鑱婂ぉ瑙掕壊")
- @Parameter(name = "id", description = "缂栧彿", required = true, example = "1024")
- @PreAuthorize("@ss.hasPermission('ai:chat-role:query')")
- @TransMethodResult
- public CommonResult<AiChatRoleRespVO> getChatRole(@RequestParam("id") Long id) {
- AiChatRoleDO chatRole = chatRoleService.getChatRole(id);
- return success(BeanUtils.toBean(chatRole, AiChatRoleRespVO.class));
- }
-
- @GetMapping("/page")
- @Operation(summary = "鑾峰緱鑱婂ぉ瑙掕壊鍒嗛〉")
- @PreAuthorize("@ss.hasPermission('ai:chat-role:query')")
- public CommonResult<PageResult<AiChatRoleRespVO>> getChatRolePage(@Valid AiChatRolePageReqVO pageReqVO) {
- PageResult<AiChatRoleDO> pageResult = chatRoleService.getChatRolePage(pageReqVO);
- return success(BeanUtils.toBean(pageResult, AiChatRoleRespVO.class));
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiModelController.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiModelController.java
index 86dd4d0..e93480f 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiModelController.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiModelController.java
@@ -33,14 +33,14 @@
private AiModelService modelService;
@PostMapping("/create")
- @Operation(summary = "鍒涘缓妯″瀷")
+ @Operation(summary = "鍒涘缓 AI 妯″瀷")
@PreAuthorize("@ss.hasPermission('ai:model:create')")
public CommonResult<Long> createModel(@Valid @RequestBody AiModelSaveReqVO createReqVO) {
return success(modelService.createModel(createReqVO));
}
@PutMapping("/update")
- @Operation(summary = "鏇存柊妯″瀷")
+ @Operation(summary = "鏇存柊 AI 妯″瀷")
@PreAuthorize("@ss.hasPermission('ai:model:update')")
public CommonResult<Boolean> updateModel(@Valid @RequestBody AiModelSaveReqVO updateReqVO) {
modelService.updateModel(updateReqVO);
@@ -48,7 +48,7 @@
}
@DeleteMapping("/delete")
- @Operation(summary = "鍒犻櫎妯″瀷")
+ @Operation(summary = "鍒犻櫎 AI 妯″瀷")
@Parameter(name = "id", description = "缂栧彿", required = true)
@PreAuthorize("@ss.hasPermission('ai:model:delete')")
public CommonResult<Boolean> deleteModel(@RequestParam("id") Long id) {
@@ -57,16 +57,15 @@
}
@GetMapping("/get")
- @Operation(summary = "鑾峰緱妯″瀷")
+ @Operation(summary = "鑾峰彇 AI 妯″瀷")
@Parameter(name = "id", description = "缂栧彿", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('ai:model:query')")
public CommonResult<AiModelRespVO> getModel(@RequestParam("id") Long id) {
- AiModelDO model = modelService.getModel(id);
- return success(BeanUtils.toBean(model, AiModelRespVO.class));
+ return success(BeanUtils.toBean(modelService.getModel(id), AiModelRespVO.class));
}
@GetMapping("/page")
- @Operation(summary = "鑾峰緱妯″瀷鍒嗛〉")
+ @Operation(summary = "鑾峰彇 AI 妯″瀷鍒嗛〉")
@PreAuthorize("@ss.hasPermission('ai:model:query')")
public CommonResult<PageResult<AiModelRespVO>> getModelPage(@Valid AiModelPageReqVO pageReqVO) {
PageResult<AiModelDO> pageResult = modelService.getModelPage(pageReqVO);
@@ -74,16 +73,19 @@
}
@GetMapping("/simple-list")
- @Operation(summary = "鑾峰緱妯″瀷鍒楄〃")
- @Parameter(name = "type", description = "绫诲瀷", required = true, example = "1")
- @Parameter(name = "platform", description = "骞冲彴", example = "midjourney")
+ @Operation(summary = "鑾峰緱妯″瀷绠�鍗曞垪琛�", description = "鐢ㄤ簬涓嬫媺閫夋嫨鍣ㄧ瓑鍦烘櫙")
+ @Parameter(name = "type", description = "妯″瀷绫诲瀷", required = true, example = "1")
+ @Parameter(name = "platform", description = "妯″瀷骞冲彴", example = "TongYi")
public CommonResult<List<AiModelRespVO>> getModelSimpleList(
@RequestParam("type") Integer type,
@RequestParam(value = "platform", required = false) String platform) {
List<AiModelDO> list = modelService.getModelListByStatusAndType(
CommonStatusEnum.ENABLE.getStatus(), type, platform);
- return success(convertList(list, model -> new AiModelRespVO().setId(model.getId())
- .setName(model.getName()).setModel(model.getModel()).setPlatform(model.getPlatform())));
+ return success(convertList(list, model -> new AiModelRespVO()
+ .setId(model.getId())
+ .setName(model.getName())
+ .setModel(model.getModel())
+ .setPlatform(model.getPlatform())));
}
-}
\ No newline at end of file
+}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiToolController.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiToolController.java
deleted file mode 100644
index e98f87e..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiToolController.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.model;
-
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolRespVO;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolSaveReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO;
-import cn.iocoder.yudao.module.ai.service.model.AiToolService;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.Parameter;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import jakarta.annotation.Resource;
-import jakarta.validation.Valid;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.validation.annotation.Validated;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.List;
-
-import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
-
-@Tag(name = "绠$悊鍚庡彴 - AI 宸ュ叿")
-@RestController
-@RequestMapping("/ai/tool")
-@Validated
-public class AiToolController {
-
- @Resource
- private AiToolService toolService;
-
- @PostMapping("/create")
- @Operation(summary = "鍒涘缓宸ュ叿")
- @PreAuthorize("@ss.hasPermission('ai:tool:create')")
- public CommonResult<Long> createTool(@Valid @RequestBody AiToolSaveReqVO createReqVO) {
- return success(toolService.createTool(createReqVO));
- }
-
- @PutMapping("/update")
- @Operation(summary = "鏇存柊宸ュ叿")
- @PreAuthorize("@ss.hasPermission('ai:tool:update')")
- public CommonResult<Boolean> updateTool(@Valid @RequestBody AiToolSaveReqVO updateReqVO) {
- toolService.updateTool(updateReqVO);
- return success(true);
- }
-
- @DeleteMapping("/delete")
- @Operation(summary = "鍒犻櫎宸ュ叿")
- @Parameter(name = "id", description = "缂栧彿", required = true)
- @PreAuthorize("@ss.hasPermission('ai:tool:delete')")
- public CommonResult<Boolean> deleteTool(@RequestParam("id") Long id) {
- toolService.deleteTool(id);
- return success(true);
- }
-
- @GetMapping("/get")
- @Operation(summary = "鑾峰緱宸ュ叿")
- @Parameter(name = "id", description = "缂栧彿", required = true, example = "1024")
- @PreAuthorize("@ss.hasPermission('ai:tool:query')")
- public CommonResult<AiToolRespVO> getTool(@RequestParam("id") Long id) {
- AiToolDO tool = toolService.getTool(id);
- return success(BeanUtils.toBean(tool, AiToolRespVO.class));
- }
-
- @GetMapping("/page")
- @Operation(summary = "鑾峰緱宸ュ叿鍒嗛〉")
- @PreAuthorize("@ss.hasPermission('ai:tool:query')")
- public CommonResult<PageResult<AiToolRespVO>> getToolPage(@Valid AiToolPageReqVO pageReqVO) {
- PageResult<AiToolDO> pageResult = toolService.getToolPage(pageReqVO);
- return success(BeanUtils.toBean(pageResult, AiToolRespVO.class));
- }
-
- @GetMapping("/simple-list")
- @Operation(summary = "鑾峰緱宸ュ叿鍒楄〃")
- public CommonResult<List<AiToolRespVO>> getToolSimpleList() {
- List<AiToolDO> list = toolService.getToolListByStatus(CommonStatusEnum.ENABLE.getStatus());
- return success(convertList(list, tool -> new AiToolRespVO()
- .setId(tool.getId()).setName(tool.getName())));
- }
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyPageReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyPageReqVO.java
index 0636962..3e207e2 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyPageReqVO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyPageReqVO.java
@@ -1,25 +1,22 @@
package cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey;
-import lombok.*;
-import java.util.*;
-import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
-import org.springframework.format.annotation.DateTimeFormat;
-import java.time.LocalDateTime;
-
-import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
@Schema(description = "绠$悊鍚庡彴 - AI API 瀵嗛挜鍒嗛〉 Request VO")
@Data
+@EqualsAndHashCode(callSuper = true)
public class AiApiKeyPageReqVO extends PageParam {
- @Schema(description = "鍚嶇О", example = "鏂囧績涓�瑷�")
+ @Schema(description = "瀵嗛挜鍚嶇О")
private String name;
- @Schema(description = "骞冲彴", example = "OpenAI")
+ @Schema(description = "骞冲彴")
private String platform;
- @Schema(description = "鐘舵��", example = "1")
+ @Schema(description = "鐘舵��")
private Integer status;
-}
\ No newline at end of file
+}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyRespVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyRespVO.java
index 55d6d80..9c85c84 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyRespVO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyRespVO.java
@@ -1,28 +1,29 @@
package cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey;
import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
+import lombok.Data;
+import java.time.LocalDateTime;
@Schema(description = "绠$悊鍚庡彴 - AI API 瀵嗛挜 Response VO")
@Data
public class AiApiKeyRespVO {
- @Schema(description = "缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "23538")
+ @Schema(description = "缂栧彿", example = "1")
private Long id;
- @Schema(description = "鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED, example = "鏂囧績涓�瑷�")
+ @Schema(description = "瀵嗛挜鍚嶇О", example = "閫氫箟鍗冮棶")
private String name;
- @Schema(description = "瀵嗛挜", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC")
- private String apiKey;
-
- @Schema(description = "骞冲彴", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
+ @Schema(description = "骞冲彴", example = "TongYi")
private String platform;
- @Schema(description = "鑷畾涔� API 鍦板潃", example = "https://aip.baidubce.com")
+ @Schema(description = "API 鍦板潃")
private String url;
- @Schema(description = "鐘舵��", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @Schema(description = "鐘舵��", example = "0")
private Integer status;
-}
\ No newline at end of file
+ @Schema(description = "鍒涘缓鏃堕棿")
+ private LocalDateTime createTime;
+
+}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeySaveReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeySaveReqVO.java
index 8fbc8fd..f68b837 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeySaveReqVO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeySaveReqVO.java
@@ -1,34 +1,32 @@
package cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey;
import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-import java.util.*;
-import jakarta.validation.constraints.*;
+import jakarta.validation.constraints.NotEmpty;
+import lombok.Data;
@Schema(description = "绠$悊鍚庡彴 - AI API 瀵嗛挜鏂板/淇敼 Request VO")
@Data
public class AiApiKeySaveReqVO {
- @Schema(description = "缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "23538")
+ @Schema(description = "缂栧彿", example = "1")
private Long id;
- @Schema(description = "鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED, example = "鏂囧績涓�瑷�")
- @NotEmpty(message = "鍚嶇О涓嶈兘涓虹┖")
+ @Schema(description = "瀵嗛挜鍚嶇О", example = "閫氫箟鍗冮棶")
+ @NotEmpty(message = "瀵嗛挜鍚嶇О涓嶈兘涓虹┖")
private String name;
- @Schema(description = "瀵嗛挜", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC")
- @NotEmpty(message = "瀵嗛挜涓嶈兘涓虹┖")
+ @Schema(description = "API 瀵嗛挜", example = "sk-xxx")
+ @NotEmpty(message = "API 瀵嗛挜涓嶈兘涓虹┖")
private String apiKey;
- @Schema(description = "骞冲彴", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
+ @Schema(description = "骞冲彴", example = "TongYi")
@NotEmpty(message = "骞冲彴涓嶈兘涓虹┖")
private String platform;
- @Schema(description = "鑷畾涔� API 鍦板潃", example = "https://aip.baidubce.com")
+ @Schema(description = "API 鍦板潃")
private String url;
- @Schema(description = "鐘舵��", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- @NotNull(message = "鐘舵�佷笉鑳戒负绌�")
+ @Schema(description = "鐘舵��", example = "0")
private Integer status;
-}
\ No newline at end of file
+}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRolePageReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRolePageReqVO.java
deleted file mode 100644
index 0a9d08d..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRolePageReqVO.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole;
-
-import lombok.*;
-import io.swagger.v3.oas.annotations.media.Schema;
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 鑱婂ぉ瑙掕壊鍒嗛〉 Request VO")
-@Data
-public class AiChatRolePageReqVO extends PageParam {
-
- @Schema(description = "瑙掕壊鍚嶇О", example = "鏉庡洓")
- private String name;
-
- @Schema(description = "瑙掕壊绫诲埆", example = "鍒涗綔")
- private String category;
-
- @Schema(description = "鏄惁鍏紑", example = "1")
- private Boolean publicStatus;
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java
deleted file mode 100644
index ed9dcf9..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleRespVO.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole;
-
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
-import org.dromara.core.trans.anno.Trans;
-import org.dromara.core.trans.constant.TransType;
-import org.dromara.core.trans.vo.VO;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-
-import java.time.LocalDateTime;
-import java.util.List;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 鑱婂ぉ瑙掕壊 Response VO")
-@Data
-public class AiChatRoleRespVO implements VO {
-
- @Schema(description = "瑙掕壊缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "32746")
- private Long id;
-
- @Schema(description = "鐢ㄦ埛缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "9442")
- private Long userId;
-
- @Schema(description = "妯″瀷缂栧彿", example = "17640")
- @Trans(type = TransType.SIMPLE, target = AiModelDO.class, fields = { "name", "model" }, refs = { "modelName", "model" })
- private Long modelId;
- @Schema(description = "妯″瀷鍚嶅瓧", example = "寮犱笁")
- private String modelName;
- @Schema(description = "妯″瀷鏍囪瘑", example = "gpt-3.5-turbo-0125")
- private String model;
-
- @Schema(description = "瑙掕壊鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED, example = "鏉庡洓")
- private String name;
-
- @Schema(description = "瑙掕壊澶村儚", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
- private String avatar;
-
- @Schema(description = "瑙掕壊绫诲埆", requiredMode = Schema.RequiredMode.REQUIRED, example = "鍒涗綔")
- private String category;
-
- @Schema(description = "瑙掕壊鎺掑簭", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- private Integer sort;
-
- @Schema(description = "瑙掕壊鎻忚堪", requiredMode = Schema.RequiredMode.REQUIRED, example = "浣犺鐨勫")
- private String description;
-
- @Schema(description = "瑙掕壊璁惧畾", requiredMode = Schema.RequiredMode.REQUIRED)
- private String systemMessage;
-
- @Schema(description = "寮曠敤鐨勭煡璇嗗簱缂栧彿鍒楄〃", example = "1,2,3")
- private List<Long> knowledgeIds;
-
- @Schema(description = "寮曠敤鐨勫伐鍏风紪鍙峰垪琛�", example = "1,2,3")
- private List<Long> toolIds;
-
- @Schema(description = "寮曠敤鐨� MCP Client 鍚嶅瓧鍒楄〃", example = "filesystem")
- private List<String> mcpClientNames;
-
- @Schema(description = "鏄惁鍏紑", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- private Boolean publicStatus;
-
- @Schema(description = "鐘舵��", example = "1")
- private Integer status;
-
- @Schema(description = "鍒涘缓鏃堕棿", requiredMode = Schema.RequiredMode.REQUIRED)
- private LocalDateTime createTime;
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveMyReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveMyReqVO.java
deleted file mode 100644
index bd4a057..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveMyReqVO.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotEmpty;
-import lombok.Data;
-import org.hibernate.validator.constraints.URL;
-
-import java.util.List;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 鑱婂ぉ瑙掕壊鏂板/淇敼銆愭垜鐨勩�� Request VO")
-@Data
-public class AiChatRoleSaveMyReqVO {
-
- @Schema(description = "瑙掕壊缂栧彿", example = "32746")
- private Long id;
-
- @Schema(description = "瑙掕壊鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED, example = "鏉庡洓")
- @NotEmpty(message = "瑙掕壊鍚嶇О涓嶈兘涓虹┖")
- private String name;
-
- @Schema(description = "瑙掕壊澶村儚", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
- @NotEmpty(message = "瑙掕壊澶村儚涓嶈兘涓虹┖")
- @URL(message = "瑙掕壊澶村儚蹇呴』鏄� URL 鏍煎紡")
- private String avatar;
-
- @Schema(description = "瑙掕壊鎻忚堪", requiredMode = Schema.RequiredMode.REQUIRED, example = "浣犺鐨勫")
- @NotEmpty(message = "瑙掕壊鎻忚堪涓嶈兘涓虹┖")
- private String description;
-
- @Schema(description = "瑙掕壊璁惧畾", requiredMode = Schema.RequiredMode.REQUIRED, example = "鐜板湪寮�濮嬩綘鎵紨涓�浣嶇▼搴忓憳锛屼綘鏄竴鍚嶄紭绉�鐨勭▼搴忓憳锛屽叿鏈夊緢寮虹殑閫昏緫鎬濈淮鑳藉姏锛屾�昏兘楂樻晥鐨勮В鍐抽棶棰�")
- @NotEmpty(message = "瑙掕壊璁惧畾涓嶈兘涓虹┖")
- private String systemMessage;
-
- @Schema(description = "寮曠敤鐨勭煡璇嗗簱缂栧彿鍒楄〃", example = "1,2,3")
- private List<Long> knowledgeIds;
-
- @Schema(description = "寮曠敤鐨勫伐鍏风紪鍙峰垪琛�", example = "1,2,3")
- private List<Long> toolIds;
-
- @Schema(description = "寮曠敤鐨� MCP Client 鍚嶅瓧鍒楄〃", example = "filesystem")
- private List<String> mcpClientNames;
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveReqVO.java
deleted file mode 100644
index 8f2913d..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatRole/AiChatRoleSaveReqVO.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole;
-
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.common.validation.InEnum;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-import jakarta.validation.constraints.*;
-import org.hibernate.validator.constraints.URL;
-
-import java.util.List;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 鑱婂ぉ瑙掕壊鏂板/淇敼 Request VO")
-@Data
-public class AiChatRoleSaveReqVO {
-
- @Schema(description = "瑙掕壊缂栧彿", example = "32746")
- private Long id;
-
- @Schema(description = "妯″瀷缂栧彿", example = "17640")
- private Long modelId;
-
- @Schema(description = "瑙掕壊鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED, example = "鏉庡洓")
- @NotEmpty(message = "瑙掕壊鍚嶇О涓嶈兘涓虹┖")
- private String name;
-
- @Schema(description = "瑙掕壊澶村儚", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/1.png")
- @NotEmpty(message = "瑙掕壊澶村儚涓嶈兘涓虹┖")
- @URL(message = "瑙掕壊澶村儚蹇呴』鏄� URL 鏍煎紡")
- private String avatar;
-
- @Schema(description = "瑙掕壊绫诲埆", requiredMode = Schema.RequiredMode.REQUIRED, example = "鍒涗綔")
- @NotEmpty(message = "瑙掕壊绫诲埆涓嶈兘涓虹┖")
- private String category;
-
- @Schema(description = "瑙掕壊鎺掑簭", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- @NotNull(message = "瑙掕壊鎺掑簭涓嶈兘涓虹┖")
- private Integer sort;
-
- @Schema(description = "瑙掕壊鎻忚堪", requiredMode = Schema.RequiredMode.REQUIRED, example = "浣犺鐨勫")
- @NotEmpty(message = "瑙掕壊鎻忚堪涓嶈兘涓虹┖")
- private String description;
-
- @Schema(description = "瑙掕壊璁惧畾", requiredMode = Schema.RequiredMode.REQUIRED, example = "鐜板湪寮�濮嬩綘鎵紨涓�浣嶇▼搴忓憳锛屼綘鏄竴鍚嶄紭绉�鐨勭▼搴忓憳锛屽叿鏈夊緢寮虹殑閫昏緫鎬濈淮鑳藉姏锛屾�昏兘楂樻晥鐨勮В鍐抽棶棰�")
- @NotEmpty(message = "瑙掕壊璁惧畾涓嶈兘涓虹┖")
- private String systemMessage;
-
- @Schema(description = "寮曠敤鐨勭煡璇嗗簱缂栧彿鍒楄〃", example = "1,2,3")
- private List<Long> knowledgeIds;
-
- @Schema(description = "寮曠敤鐨勫伐鍏风紪鍙峰垪琛�", example = "1,2,3")
- private List<Long> toolIds;
-
- @Schema(description = "寮曠敤鐨� MCP Client 鍚嶅瓧鍒楄〃", example = "filesystem")
- private List<String> mcpClientNames;
-
- @Schema(description = "鏄惁鍏紑", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- @NotNull(message = "鏄惁鍏紑涓嶈兘涓虹┖")
- private Boolean publicStatus;
-
- @Schema(description = "鐘舵��", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- @NotNull(message = "鐘舵�佷笉鑳戒负绌�")
- @InEnum(CommonStatusEnum.class)
- private Integer status;
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelPageReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelPageReqVO.java
index af8d112..3a37a80 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelPageReqVO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelPageReqVO.java
@@ -1,20 +1,25 @@
package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model;
-import lombok.*;
-import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
-@Schema(description = "绠$悊鍚庡彴 - API 妯″瀷鍒嗛〉 Request VO")
+@Schema(description = "绠$悊鍚庡彴 - AI 妯″瀷鍒嗛〉 Request VO")
@Data
+@EqualsAndHashCode(callSuper = true)
public class AiModelPageReqVO extends PageParam {
- @Schema(description = "妯″瀷鍚嶅瓧", example = "寮犱笁")
+ @Schema(description = "妯″瀷鍚嶇О")
private String name;
- @Schema(description = "妯″瀷鏍囪瘑", example = "gpt-3.5-turbo-0125")
- private String model;
+ @Schema(description = "妯″瀷绫诲瀷")
+ private Integer type;
- @Schema(description = "妯″瀷骞冲彴", example = "OpenAI")
+ @Schema(description = "骞冲彴")
private String platform;
-}
\ No newline at end of file
+ @Schema(description = "鐘舵��")
+ private Integer status;
+
+}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelRespVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelRespVO.java
index b50b70a..a610e09 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelRespVO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelRespVO.java
@@ -2,47 +2,48 @@
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
-
+import lombok.experimental.Accessors;
import java.time.LocalDateTime;
@Schema(description = "绠$悊鍚庡彴 - AI 妯″瀷 Response VO")
@Data
+@Accessors(chain = true)
public class AiModelRespVO {
- @Schema(description = "缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "2630")
+ @Schema(description = "缂栧彿", example = "1")
private Long id;
- @Schema(description = "API 绉橀挜缂栧彿", example = "22042")
+ @Schema(description = "API 瀵嗛挜缂栧彿", example = "1")
private Long keyId;
- @Schema(description = "妯″瀷鍚嶅瓧", example = "寮犱笁")
+ @Schema(description = "妯″瀷鍚嶅瓧", example = "閫氫箟鍗冮棶")
private String name;
- @Schema(description = "妯″瀷鏍囪瘑", example = "gpt-3.5-turbo-0125")
+ @Schema(description = "妯″瀷鏍囪瘑", example = "qwen-plus")
private String model;
- @Schema(description = "妯″瀷骞冲彴", example = "OpenAI")
+ @Schema(description = "妯″瀷骞冲彴", example = "TongYi")
private String platform;
@Schema(description = "妯″瀷绫诲瀷", example = "1")
private Integer type;
- @Schema(description = "鎺掑簭", example = "1")
+ @Schema(description = "鎺掑簭", example = "0")
private Integer sort;
- @Schema(description = "鐘舵��", example = "2")
+ @Schema(description = "鐘舵��", example = "0")
private Integer status;
- @Schema(description = "娓╁害鍙傛暟", example = "1")
+ @Schema(description = "娓╁害")
private Double temperature;
- @Schema(description = "鍗曟潯鍥炲鐨勬渶澶� Token 鏁伴噺", example = "4096")
+ @Schema(description = "鏈�澶� Token 鏁�")
private Integer maxTokens;
- @Schema(description = "涓婁笅鏂囩殑鏈�澶� Message 鏁伴噺", example = "8192")
+ @Schema(description = "鏈�澶т笂涓嬫枃鏁�")
private Integer maxContexts;
- @Schema(description = "鍒涘缓鏃堕棿", requiredMode = Schema.RequiredMode.REQUIRED)
+ @Schema(description = "鍒涘缓鏃堕棿")
private LocalDateTime createTime;
-}
\ No newline at end of file
+}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelSaveReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelSaveReqVO.java
index 95ed6bf..6f6fce3 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelSaveReqVO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiModelSaveReqVO.java
@@ -1,59 +1,50 @@
package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model;
-import cn.iocoder.yudao.module.ai.enums.model.AiModelTypeEnum;
-import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
-@Schema(description = "绠$悊鍚庡彴 - API 妯″瀷鏂板/淇敼 Request VO")
+@Schema(description = "绠$悊鍚庡彴 - AI 妯″瀷鏂板/淇敼 Request VO")
@Data
public class AiModelSaveReqVO {
- @Schema(description = "缂栧彿", example = "2630")
+ @Schema(description = "缂栧彿", example = "1")
private Long id;
- @Schema(description = "API 绉橀挜缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "22042")
- @NotNull(message = "API 绉橀挜缂栧彿涓嶈兘涓虹┖")
+ @Schema(description = "API 瀵嗛挜缂栧彿", example = "1")
+ @NotNull(message = "API 瀵嗛挜涓嶈兘涓虹┖")
private Long keyId;
- @Schema(description = "妯″瀷鍚嶅瓧", requiredMode = Schema.RequiredMode.REQUIRED, example = "寮犱笁")
+ @Schema(description = "妯″瀷鍚嶅瓧", example = "閫氫箟鍗冮棶")
@NotEmpty(message = "妯″瀷鍚嶅瓧涓嶈兘涓虹┖")
private String name;
- @Schema(description = "妯″瀷鏍囪瘑", requiredMode = Schema.RequiredMode.REQUIRED, example = "gpt-3.5-turbo-0125")
+ @Schema(description = "妯″瀷鏍囪瘑", example = "qwen-plus")
@NotEmpty(message = "妯″瀷鏍囪瘑涓嶈兘涓虹┖")
private String model;
- @Schema(description = "妯″瀷骞冲彴", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
+ @Schema(description = "妯″瀷骞冲彴", example = "TongYi")
@NotEmpty(message = "妯″瀷骞冲彴涓嶈兘涓虹┖")
- @InEnum(AiPlatformEnum.class)
private String platform;
- @Schema(description = "妯″瀷绫诲瀷", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @Schema(description = "妯″瀷绫诲瀷", example = "1")
@NotNull(message = "妯″瀷绫诲瀷涓嶈兘涓虹┖")
- @InEnum(AiModelTypeEnum.class)
private Integer type;
- @Schema(description = "鎺掑簭", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- @NotNull(message = "鎺掑簭涓嶈兘涓虹┖")
+ @Schema(description = "鎺掑簭", example = "0")
private Integer sort;
- @Schema(description = "鐘舵��", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- @InEnum(CommonStatusEnum.class)
- @NotNull(message = "鐘舵�佷笉鑳戒负绌�")
+ @Schema(description = "鐘舵��", example = "0")
private Integer status;
- @Schema(description = "娓╁害鍙傛暟", example = "1")
+ @Schema(description = "娓╁害")
private Double temperature;
- @Schema(description = "鍗曟潯鍥炲鐨勬渶澶� Token 鏁伴噺", example = "4096")
+ @Schema(description = "鏈�澶� Token 鏁�")
private Integer maxTokens;
- @Schema(description = "涓婁笅鏂囩殑鏈�澶� Message 鏁伴噺", example = "8192")
+ @Schema(description = "鏈�澶т笂涓嬫枃鏁�")
private Integer maxContexts;
-}
\ No newline at end of file
+}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolPageReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolPageReqVO.java
deleted file mode 100644
index dc8b04c..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolPageReqVO.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool;
-
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
-import cn.iocoder.yudao.framework.common.validation.InEnum;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.ToString;
-import org.springframework.format.annotation.DateTimeFormat;
-
-import java.time.LocalDateTime;
-
-import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 宸ュ叿鍒嗛〉 Request VO")
-@Data
-public class AiToolPageReqVO extends PageParam {
-
- @Schema(description = "宸ュ叿鍚嶇О", example = "鐜嬩簲")
- private String name;
-
- @Schema(description = "宸ュ叿鎻忚堪", example = "浣犵寽")
- private String description;
-
- @Schema(description = "鐘舵��", example = "1")
- @InEnum(CommonStatusEnum.class)
- private Integer status;
-
- @Schema(description = "鍒涘缓鏃堕棿")
- @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
- private LocalDateTime[] createTime;
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolRespVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolRespVO.java
deleted file mode 100644
index 6d5a02e..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolRespVO.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.*;
-
-import java.time.LocalDateTime;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 宸ュ叿 Response VO")
-@Data
-public class AiToolRespVO {
-
- @Schema(description = "宸ュ叿缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "19661")
- private Long id;
-
- @Schema(description = "宸ュ叿鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED, example = "鐜嬩簲")
- private String name;
-
- @Schema(description = "宸ュ叿鎻忚堪", example = "浣犵寽")
- private String description;
-
- @Schema(description = "鐘舵��", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- private Integer status;
-
- @Schema(description = "鍒涘缓鏃堕棿", requiredMode = Schema.RequiredMode.REQUIRED)
- private LocalDateTime createTime;
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolSaveReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolSaveReqVO.java
deleted file mode 100644
index c85cfc3..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/tool/AiToolSaveReqVO.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool;
-
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.common.validation.InEnum;
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotEmpty;
-import lombok.Data;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 宸ュ叿鏂板/淇敼 Request VO")
-@Data
-public class AiToolSaveReqVO {
-
- @Schema(description = "宸ュ叿缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "19661")
- private Long id;
-
- @Schema(description = "宸ュ叿鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED, example = "鐜嬩簲")
- @NotEmpty(message = "宸ュ叿鍚嶇О涓嶈兘涓虹┖")
- private String name;
-
- @Schema(description = "宸ュ叿鎻忚堪", example = "浣犵寽")
- private String description;
-
- @Schema(description = "鐘舵��", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- @InEnum(CommonStatusEnum.class)
- private Integer status;
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/AiMusicController.http b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/AiMusicController.http
deleted file mode 100644
index ae68c82..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/AiMusicController.http
+++ /dev/null
@@ -1,26 +0,0 @@
-### 鐢熸垚闊充箰锛歋uno + 姝岃瘝妯″紡
-POST {{baseUrl}}/ai/music/generate
-Content-Type: application/json
-Authorization: {{token}}
-
-{
- "platform": "Suno",
- "generateMode": 2,
- "prompt": "鍒涗綔涓�棣栧甫鏈夎交鏉惧悏浠栨棆寰嬬殑娴佽姝屾洸锛孾verse] 鎻忚堪澶忔棩娴锋哗鐨勫畞闈欙紝[chorus] 鑺傚鍔犲揩锛岃〃杈惧鑷敱鐨勫悜寰�銆�",
- "model": "chirp-v3.5",
- "tags": ["Happy"],
- "title": "Happy Song"
-}
-
-### 鐢熸垚闊充箰锛歋uno + 鎻忚堪妯″紡
-POST {{baseUrl}}/ai/music/generate
-Content-Type: application/json
-Authorization: {{token}}
-
-{
- "platform": "Suno",
- "generateMode": 1,
- "model": "chirp-v3.5",
- "prompt": "happy music",
- "makeInstrumental": false
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/AiMusicController.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/AiMusicController.java
deleted file mode 100644
index 6c09e4b..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/AiMusicController.java
+++ /dev/null
@@ -1,98 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.music;
-
-import cn.hutool.core.util.ObjUtil;
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.ai.controller.admin.music.vo.*;
-import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;
-import cn.iocoder.yudao.module.ai.service.music.AiMusicService;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.Parameter;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import jakarta.annotation.Resource;
-import jakarta.validation.Valid;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
-
-import java.util.List;
-
-import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
-
-@Tag(name = "绠$悊鍚庡彴 - AI 闊充箰")
-@RestController
-@RequestMapping("/ai/music")
-public class AiMusicController {
-
- @Resource
- private AiMusicService musicService;
-
- @GetMapping("/my-page")
- @Operation(summary = "鑾峰緱銆愭垜鐨勩�戦煶涔愬垎椤�")
- public CommonResult<PageResult<AiMusicRespVO>> getMusicMyPage(@Valid AiMusicPageReqVO pageReqVO) {
- PageResult<AiMusicDO> pageResult = musicService.getMusicMyPage(pageReqVO, getLoginUserId());
- return success(BeanUtils.toBean(pageResult, AiMusicRespVO.class));
- }
-
- @PostMapping("/generate")
- @Operation(summary = "闊充箰鐢熸垚")
- public CommonResult<List<Long>> generateMusic(@RequestBody @Valid AiSunoGenerateReqVO reqVO) {
- return success(musicService.generateMusic(getLoginUserId(), reqVO));
- }
-
- @Operation(summary = "鍒犻櫎銆愭垜鐨勩�戦煶涔愯褰�")
- @DeleteMapping("/delete-my")
- @Parameter(name = "id", required = true, description = "闊充箰缂栧彿", example = "1024")
- public CommonResult<Boolean> deleteMusicMy(@RequestParam("id") Long id) {
- musicService.deleteMusicMy(id, getLoginUserId());
- return success(true);
- }
-
- @GetMapping("/get-my")
- @Operation(summary = "鑾峰彇銆愭垜鐨勩�戦煶涔�")
- @Parameter(name = "id", required = true, description = "闊充箰缂栧彿", example = "1024")
- public CommonResult<AiMusicRespVO> getMusicMy(@RequestParam("id") Long id) {
- AiMusicDO music = musicService.getMusic(id);
- if (music == null || ObjUtil.notEqual(getLoginUserId(), music.getUserId())) {
- return success(null);
- }
- return success(BeanUtils.toBean(music, AiMusicRespVO.class));
- }
-
- @PostMapping("/update-my")
- @Operation(summary = "淇敼銆愭垜鐨勩�戦煶涔� 鐩墠鍙敮鎸佷慨鏀规爣棰�")
- @Parameter(name = "title", required = true, description = "闊充箰鍚嶇О", example = "澶滅┖涓渶浜殑鏄�")
- public CommonResult<Boolean> updateMy(AiMusicUpdateMyReqVO updateReqVO) {
- musicService.updateMyMusic(updateReqVO, getLoginUserId());
- return success(true);
- }
-
- // ================ 闊充箰绠$悊 ================
-
- @GetMapping("/page")
- @Operation(summary = "鑾峰緱闊充箰鍒嗛〉")
- @PreAuthorize("@ss.hasPermission('ai:music:query')")
- public CommonResult<PageResult<AiMusicRespVO>> getMusicPage(@Valid AiMusicPageReqVO pageReqVO) {
- PageResult<AiMusicDO> pageResult = musicService.getMusicPage(pageReqVO);
- return success(BeanUtils.toBean(pageResult, AiMusicRespVO.class));
- }
-
- @DeleteMapping("/delete")
- @Operation(summary = "鍒犻櫎闊充箰")
- @Parameter(name = "id", description = "缂栧彿", required = true)
- @PreAuthorize("@ss.hasPermission('ai:music:delete')")
- public CommonResult<Boolean> deleteMusic(@RequestParam("id") Long id) {
- musicService.deleteMusic(id);
- return success(true);
- }
-
- @PutMapping("/update")
- @Operation(summary = "鏇存柊闊充箰")
- @PreAuthorize("@ss.hasPermission('ai:music:update')")
- public CommonResult<Boolean> updateMusic(@Valid @RequestBody AiMusicUpdateReqVO updateReqVO) {
- musicService.updateMusic(updateReqVO);
- return success(true);
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicPageReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicPageReqVO.java
deleted file mode 100644
index e60366f..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicPageReqVO.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.music.vo;
-
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
-import cn.iocoder.yudao.framework.common.validation.InEnum;
-import cn.iocoder.yudao.module.ai.enums.music.AiMusicGenerateModeEnum;
-import cn.iocoder.yudao.module.ai.enums.music.AiMusicStatusEnum;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-import org.springframework.format.annotation.DateTimeFormat;
-
-import java.time.LocalDateTime;
-
-import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 闊充箰鍒嗛〉 Request VO")
-@Data
-public class AiMusicPageReqVO extends PageParam {
-
- @Schema(description = "鐢ㄦ埛缂栧彿", example = "12212")
- private Long userId;
-
- @Schema(description = "闊充箰鍚嶇О", example = "澶滅┖涓渶浜殑鏄�")
- private String title;
-
- @Schema(description = "闊充箰鐘舵��", example = "20")
- @InEnum(AiMusicStatusEnum.class)
- private Integer status;
-
- @Schema(description = "鐢熸垚妯″紡", example = "1")
- @InEnum(AiMusicGenerateModeEnum.class)
- private Integer generateMode;
-
- @Schema(description = "鏄惁鍙戝竷", example = "true")
- private Boolean publicStatus;
-
- @Schema(description = "鍒涘缓鏃堕棿")
- @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
- private LocalDateTime[] createTime;
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicRespVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicRespVO.java
deleted file mode 100644
index 05044a4..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicRespVO.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.music.vo;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-
-import java.time.LocalDateTime;
-import java.util.List;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 闊充箰 Response VO")
-@Data
-public class AiMusicRespVO {
-
- @Schema(description = "缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
- private Long id;
-
- @Schema(description = "鐢ㄦ埛缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "12212")
- private Long userId;
-
- @Schema(description = "闊充箰鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED, example = "澶滅┖涓渶浜殑鏄�")
- private String title;
-
- @Schema(description = "姝岃瘝", example = "oh~鍗栫硶鐨�")
- private String lyric;
-
- @Schema(description = "鍥剧墖鍦板潃", example = "https://www.iocoder.cn")
- private String imageUrl;
-
- @Schema(description = "闊抽鍦板潃", example = "https://www.iocoder.cn")
- private String audioUrl;
-
- @Schema(description = "瑙嗛鍦板潃", example = "https://www.iocoder.cn")
- private String videoUrl;
-
- @Schema(description = "闊充箰鐘舵��", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
- private Integer status;
-
- @Schema(description = "鎻忚堪璇�", example = "涓�棣栬交蹇殑姝屾洸")
- private String gptDescriptionPrompt;
-
- @Schema(description = "鎻愮ず璇�", example = "鍒涗綔涓�棣栧甫鏈夎交鏉惧悏浠栨棆寰嬬殑娴佽姝屾洸锛孾verse] 鎻忚堪澶忔棩娴锋哗鐨勫畞闈欙紝[chorus] 鑺傚鍔犲揩锛岃〃杈惧鑷敱鐨勫悜寰�銆�")
- private String prompt;
-
- @Schema(description = "妯″瀷骞冲彴", requiredMode = Schema.RequiredMode.REQUIRED, example = "Suno")
- private String platform;
-
- @Schema(description = "妯″瀷", requiredMode = Schema.RequiredMode.REQUIRED, example = "chirp-v3.5")
- private String model;
-
- @Schema(description = "鐢熸垚妯″紡", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- private Integer generateMode;
-
- @Schema(description = "闊充箰椋庢牸鏍囩")
- private List<String> tags;
-
- @Schema(description = "闊充箰鏃堕暱", example = "[\"pop\",\"jazz\",\"punk\"]")
- private Double duration;
-
- @Schema(description = "鏄惁鍙戝竷", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
- private Boolean publicStatus;
-
- @Schema(description = "浠诲姟缂栧彿", example = "11369")
- private String taskId;
-
- @Schema(description = "閿欒淇℃伅")
- private String errorMessage;
-
- @Schema(description = "鍒涘缓鏃堕棿", requiredMode = Schema.RequiredMode.REQUIRED)
- private LocalDateTime createTime;
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicUpdateMyReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicUpdateMyReqVO.java
deleted file mode 100644
index 4576701..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicUpdateMyReqVO.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.music.vo;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotNull;
-import lombok.Data;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 淇敼鎴戠殑闊充箰 Request VO")
-@Data
-public class AiMusicUpdateMyReqVO {
-
- @Schema(description = "缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583")
- @NotNull(message = "缂栧彿涓嶈兘涓虹┖")
- private Long id;
-
- @Schema(description = "闊充箰鍚嶇О", example = "澶滅┖涓渶浜殑鏄�")
- private String title;
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicUpdateReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicUpdateReqVO.java
deleted file mode 100644
index 447bc97..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiMusicUpdateReqVO.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.music.vo;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotNull;
-import lombok.Data;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 闊充箰淇敼 Request VO")
-@Data
-public class AiMusicUpdateReqVO {
-
- @Schema(description = "缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583")
- @NotNull(message = "缂栧彿涓嶈兘涓虹┖")
- private Long id;
-
- @Schema(description = "鏄惁鍙戝竷", example = "true")
- private Boolean publicStatus;
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiSunoGenerateReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiSunoGenerateReqVO.java
deleted file mode 100644
index f72d2b5..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/AiSunoGenerateReqVO.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.music.vo;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotBlank;
-import jakarta.validation.constraints.NotEmpty;
-import jakarta.validation.constraints.NotNull;
-import lombok.Data;
-
-import java.util.List;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 闊充箰鐢熸垚 Request VO")
-@Data
-public class AiSunoGenerateReqVO {
-
- @Schema(description = "骞冲彴", requiredMode = Schema.RequiredMode.REQUIRED, example = "Suno")
- @NotBlank(message = "骞冲彴涓嶈兘涓虹┖")
- private String platform; // 鍙傝 AiPlatformEnum 鏋氫妇
-
- /**
- * 1. 鎻忚堪妯″紡锛氭弿杩拌瘝 + 鏄惁绾煶涔� + 妯″瀷
- * 2. 姝岃瘝妯″紡锛氭瓕璇� + 闊充箰椋庢牸 + 鏍囬 + 妯″瀷
- */
- @Schema(description = "鐢熸垚妯″紡", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
- @NotNull(message = "鐢熸垚妯″紡涓嶈兘涓虹┖")
- private Integer generateMode; // 鍙傝 AiMusicGenerateModeEnum 鏋氫妇
-
- @Schema(description = "鐢ㄤ簬鐢熸垚闊充箰闊抽鐨勬瓕璇嶆彁绀�",
- example = """
- 1.鎻忚堪妯″紡锛氬垱浣滀竴棣栧甫鏈夎交鏉惧悏浠栨棆寰嬬殑娴佽姝屾洸锛孾verse] 鎻忚堪澶忔棩娴锋哗鐨勫畞闈欙紝[chorus] 鑺傚鍔犲揩锛岃〃杈惧鑷敱鐨勫悜寰�銆�
- 2.姝岃瘝妯″紡锛�
- [Verse]
- 闃冲厜涓嬪璺� 澶氫箞娆㈠揩
- 鍋囨湡灏辫鏉� 蹇冮兘椋炶捣鏉�
- 鏈嬪弸鍦ㄤ竴鏃� 绗戝0鍙堢伩鐑�
- 鏃犲咖鏃犺檻鐨� 姣忎竴澶╃敎铚�
- [Chorus]
- 椹笂鏀惧亣浜� 蹇潵搴嗙
- 涓�璧峰幓鏃呰 蹇幓鍐掗櫓
- 鏃ュ瓙澶煭鏆� 鍒啀绛夊緟
- 椹笂鏀惧亣浜� 姊︽兂璧烽
- """)
- private String prompt;
-
- @Schema(description = "鏄惁绾煶涔�", example = "true")
- private Boolean makeInstrumental;
-
- @Schema(description = "妯″瀷", requiredMode = Schema.RequiredMode.REQUIRED, example = "chirp-v3.5")
- @NotEmpty(message = "妯″瀷涓嶈兘涓虹┖")
- private String model;
-
- @Schema(description = "闊充箰椋庢牸", example = "[\"pop\",\"jazz\",\"punk\"]")
- private List<String> tags;
-
- @Schema(description = "闊充箰/姝屾洸鍚嶇О", example = "澶滅┖涓渶浜殑鏄�")
- private String title;
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.http b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.http
deleted file mode 100644
index 8dc1b0c..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.http
+++ /dev/null
@@ -1,12 +0,0 @@
-### 娴嬭瘯 AI 宸ヤ綔娴�
-POST {{baseUrl}}/ai/workflow/test
-Content-Type: application/json
-Authorization: {{token}}
-tenant-id: {{adminTenantId}}
-
-{
- "id": 4,
- "params": {
- "message": "1 + 1 = ?"
- }
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.java
deleted file mode 100644
index d558d90..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/AiWorkflowController.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.workflow;
-
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.*;
-import cn.iocoder.yudao.module.ai.dal.dataobject.workflow.AiWorkflowDO;
-import cn.iocoder.yudao.module.ai.service.workflow.AiWorkflowService;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.Parameter;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import jakarta.annotation.Resource;
-import jakarta.validation.Valid;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
-
-import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-
-@Tag(name = "绠$悊鍚庡彴 - AI 宸ヤ綔娴�")
-@RestController
-@RequestMapping("/ai/workflow")
-@Slf4j
-public class AiWorkflowController {
-
- @Resource
- private AiWorkflowService workflowService;
-
- @PostMapping("/create")
- @Operation(summary = "鍒涘缓 AI 宸ヤ綔娴�")
- @PreAuthorize("@ss.hasPermission('ai:workflow:create')")
- public CommonResult<Long> createWorkflow(@Valid @RequestBody AiWorkflowSaveReqVO createReqVO) {
- return success(workflowService.createWorkflow(createReqVO));
- }
-
- @PutMapping("/update")
- @Operation(summary = "鏇存柊 AI 宸ヤ綔娴�")
- @PreAuthorize("@ss.hasPermission('ai:workflow:update')")
- public CommonResult<Boolean> updateWorkflow(@Valid @RequestBody AiWorkflowSaveReqVO updateReqVO) {
- workflowService.updateWorkflow(updateReqVO);
- return success(true);
- }
-
- @DeleteMapping("/delete")
- @Operation(summary = "鍒犻櫎 AI 宸ヤ綔娴�")
- @Parameter(name = "id", description = "缂栧彿", required = true)
- @PreAuthorize("@ss.hasPermission('ai:workflow:delete')")
- public CommonResult<Boolean> deleteWorkflow(@RequestParam("id") Long id) {
- workflowService.deleteWorkflow(id);
- return success(true);
- }
-
- @GetMapping("/get")
- @Operation(summary = "鑾峰緱 AI 宸ヤ綔娴�")
- @Parameter(name = "id", description = "缂栧彿", required = true, example = "1024")
- @PreAuthorize("@ss.hasPermission('ai:workflow:query')")
- public CommonResult<AiWorkflowRespVO> getWorkflow(@RequestParam("id") Long id) {
- AiWorkflowDO workflow = workflowService.getWorkflow(id);
- return success(BeanUtils.toBean(workflow, AiWorkflowRespVO.class));
- }
-
- @GetMapping("/page")
- @Operation(summary = "鑾峰緱 AI 宸ヤ綔娴佸垎椤�")
- @PreAuthorize("@ss.hasPermission('ai:workflow:query')")
- public CommonResult<PageResult<AiWorkflowRespVO>> getWorkflowPage(@Valid AiWorkflowPageReqVO pageReqVO) {
- PageResult<AiWorkflowDO> pageResult = workflowService.getWorkflowPage(pageReqVO);
- return success(BeanUtils.toBean(pageResult, AiWorkflowRespVO.class));
- }
-
- @PostMapping("/test")
- @Operation(summary = "娴嬭瘯 AI 宸ヤ綔娴�")
- @PreAuthorize("@ss.hasPermission('ai:workflow:test')")
- public CommonResult<Object> testWorkflow(@Valid @RequestBody AiWorkflowTestReqVO testReqVO) {
- return success(workflowService.testWorkflow(testReqVO));
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowPageReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowPageReqVO.java
deleted file mode 100644
index e55b85e..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowPageReqVO.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.workflow.vo;
-
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
-import cn.iocoder.yudao.framework.common.validation.InEnum;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-import org.springframework.format.annotation.DateTimeFormat;
-
-import java.time.LocalDateTime;
-
-import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 宸ヤ綔娴佸垎椤� Request VO")
-@Data
-public class AiWorkflowPageReqVO extends PageParam {
-
- @Schema(description = "鍚嶇О", example = "宸ヤ綔娴�")
- private String name;
-
- @Schema(description = "鏍囪瘑", example = "FLOW")
- private String code;
-
- @Schema(description = "鐘舵��", example = "1")
- @InEnum(CommonStatusEnum.class)
- private Integer status;
-
- @Schema(description = "鍒涘缓鏃堕棿")
- @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
- private LocalDateTime[] createTime;
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowRespVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowRespVO.java
deleted file mode 100644
index e3a28ad..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowRespVO.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.workflow.vo;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-
-import java.time.LocalDateTime;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 宸ヤ綔娴� Response VO")
-@Data
-public class AiWorkflowRespVO {
-
- @Schema(description = "缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- private Long id;
-
- @Schema(description = "宸ヤ綔娴佹爣璇�", requiredMode = Schema.RequiredMode.REQUIRED, example = "FLOW")
- private String code;
-
- @Schema(description = "宸ヤ綔娴佸悕绉�", requiredMode = Schema.RequiredMode.REQUIRED, example = "宸ヤ綔娴�")
- private String name;
-
- @Schema(description = "澶囨敞", requiredMode = Schema.RequiredMode.REQUIRED, example = "宸ヤ綔娴�")
- private String remark;
-
- @Schema(description = "鐘舵��", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- private Integer status;
-
- @Schema(description = "宸ヤ綔娴佹ā鍨� JSON", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
- private String graph;
-
- @Schema(description = "鍒涘缓鏃堕棿", requiredMode = Schema.RequiredMode.REQUIRED, example = "鏃堕棿鎴虫牸寮�")
- private LocalDateTime createTime;
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowSaveReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowSaveReqVO.java
deleted file mode 100644
index 0a63c37..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowSaveReqVO.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.workflow.vo;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotEmpty;
-import jakarta.validation.constraints.NotNull;
-import lombok.Data;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 宸ヤ綔娴佹柊澧�/淇敼 Request VO")
-@Data
-public class AiWorkflowSaveReqVO {
-
- @Schema(description = "缂栧彿", example = "1")
- private Long id;
-
- @Schema(description = "宸ヤ綔娴佹爣璇�", requiredMode = Schema.RequiredMode.REQUIRED, example = "FLOW")
- @NotEmpty(message = "宸ヤ綔娴佹爣璇嗕笉鑳戒负绌�")
- private String code;
-
- @Schema(description = "宸ヤ綔娴佸悕绉�", requiredMode = Schema.RequiredMode.REQUIRED, example = "宸ヤ綔娴�")
- @NotEmpty(message = "宸ヤ綔娴佸悕绉颁笉鑳戒负绌�")
- private String name;
-
- @Schema(description = "澶囨敞", example = "FLOW")
- private String remark;
-
- @Schema(description = "宸ヤ綔娴佹ā鍨�", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
- @NotEmpty(message = "宸ヤ綔娴佹ā鍨嬩笉鑳戒负绌�")
- private String graph;
-
- @Schema(description = "鐘舵��", requiredMode = Schema.RequiredMode.REQUIRED, example = "FLOW")
- @NotNull(message = "鐘舵�佷笉鑳戒负绌�")
- private Integer status;
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowTestReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowTestReqVO.java
deleted file mode 100644
index 37b455c..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/workflow/vo/AiWorkflowTestReqVO.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.workflow.vo;
-
-import cn.hutool.core.util.StrUtil;
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.AssertTrue;
-import lombok.Data;
-
-import java.util.Map;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 宸ヤ綔娴佹祴璇� Request VO")
-@Data
-public class AiWorkflowTestReqVO {
-
- @Schema(description = "宸ヤ綔娴佺紪鍙�", example = "1024")
- private Long id;
-
- @Schema(description = "宸ヤ綔娴佹ā鍨�", example = "{}")
- private String graph;
-
- @Schema(description = "鍙傛暟", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}")
- private Map<String, Object> params;
-
- @AssertTrue(message = "宸ヤ綔娴佹垨妯″瀷锛屽繀椤讳紶閫掍竴涓�")
- public boolean isGraphValid() {
- return id != null || StrUtil.isNotEmpty(graph);
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/AiWriteController.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/AiWriteController.java
deleted file mode 100644
index 7ef208b..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/AiWriteController.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.write;
-
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWriteGenerateReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWritePageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWriteRespVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.write.AiWriteDO;
-import cn.iocoder.yudao.module.ai.service.write.AiWriteService;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.Parameter;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import jakarta.annotation.Resource;
-import jakarta.validation.Valid;
-import org.springframework.http.MediaType;
-import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.web.bind.annotation.*;
-import reactor.core.publisher.Flux;
-
-import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
-
-@Tag(name = "绠$悊鍚庡彴 - AI 鍐欎綔")
-@RestController
-@RequestMapping("/ai/write")
-public class AiWriteController {
-
- @Resource
- private AiWriteService writeService;
-
- @PostMapping(value = "/generate-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
- @Operation(summary = "鍐欎綔鐢熸垚锛堟祦寮忥級", description = "娴佸紡杩斿洖锛屽搷搴旇緝蹇�")
- public Flux<CommonResult<String>> generateWriteContent(@RequestBody @Valid AiWriteGenerateReqVO generateReqVO) {
- return writeService.generateWriteContent(generateReqVO, getLoginUserId());
- }
-
- // ================ 鍐欎綔绠$悊 ================
-
- @DeleteMapping("/delete")
- @Operation(summary = "鍒犻櫎鍐欎綔")
- @Parameter(name = "id", description = "缂栧彿", required = true)
- @PreAuthorize("@ss.hasPermission('ai:write:delete')")
- public CommonResult<Boolean> deleteWrite(@RequestParam("id") Long id) {
- writeService.deleteWrite(id);
- return success(true);
- }
-
- @GetMapping("/page")
- @Operation(summary = "鑾峰緱鍐欎綔鍒嗛〉")
- @PreAuthorize("@ss.hasPermission('ai:write:query')")
- public CommonResult<PageResult<AiWriteRespVO>> getWritePage(@Valid AiWritePageReqVO pageReqVO) {
- PageResult<AiWriteDO> pageResult = writeService.getWritePage(pageReqVO);
- return success(BeanUtils.toBean(pageResult, AiWriteRespVO.class));
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteGenerateReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteGenerateReqVO.java
deleted file mode 100644
index 21c6042..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteGenerateReqVO.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.write.vo;
-
-import cn.iocoder.yudao.framework.common.validation.InEnum;
-import cn.iocoder.yudao.module.ai.enums.write.AiWriteTypeEnum;
-import io.swagger.v3.oas.annotations.media.Schema;
-import jakarta.validation.constraints.NotNull;
-import lombok.Data;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 鍐欎綔鐢熸垚 Request VO")
-@Data
-public class AiWriteGenerateReqVO {
-
- @Schema(description = "鍐欎綔绫诲瀷", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- @InEnum(value = AiWriteTypeEnum.class, message = "鍐欎綔绫诲瀷蹇呴』鏄� {value}")
- private Integer type;
-
- @Schema(description = "鍐欎綔鍐呭鎻愮ず", example = "1.鎾板啓锛氱敯蹇岃禌椹紱2.鍥炲锛氫笉鎵�")
- private String prompt;
-
- @Schema(description = "鍘熸枃", example = "棰嗗鎴戣杈炶亴")
- private String originalContent;
-
- @Schema(description = "闀垮害", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- @NotNull(message = "闀垮害涓嶈兘涓虹┖")
- private Integer length;
-
- @Schema(description = "鏍煎紡", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- @NotNull(message = "鏍煎紡涓嶈兘涓虹┖")
- private Integer format;
-
- @Schema(description = "璇皵", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- @NotNull(message = "璇皵涓嶈兘涓虹┖")
- private Integer tone;
-
- @Schema(description = "璇█", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
- @NotNull(message = "璇█涓嶈兘涓虹┖")
- private Integer language;
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWritePageReqVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWritePageReqVO.java
deleted file mode 100644
index 04f99ae..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWritePageReqVO.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.write.vo;
-
-import cn.iocoder.yudao.framework.common.pojo.PageParam;
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.ToString;
-import org.springframework.format.annotation.DateTimeFormat;
-
-import java.time.LocalDateTime;
-
-import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 鍐欎綔鍒嗛〉 Request VO")
-@Data
-public class AiWritePageReqVO extends PageParam {
-
- @Schema(description = "鐢ㄦ埛缂栧彿", example = "28404")
- private Long userId;
-
- @Schema(description = "鍐欎綔绫诲瀷", example = "1")
- private Integer type;
-
- @Schema(description = "骞冲彴", example = "TongYi")
- private String platform;
-
- @Schema(description = "鍒涘缓鏃堕棿")
- @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
- private LocalDateTime[] createTime;
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteRespVO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteRespVO.java
deleted file mode 100644
index 4160de9..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/write/vo/AiWriteRespVO.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package cn.iocoder.yudao.module.ai.controller.admin.write.vo;
-
-import io.swagger.v3.oas.annotations.media.Schema;
-import lombok.Data;
-
-import java.time.LocalDateTime;
-
-@Schema(description = "绠$悊鍚庡彴 - AI 鍐欎綔 Response VO")
-@Data
-public class AiWriteRespVO {
-
- @Schema(description = "缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "5311")
- private Long id;
-
- @Schema(description = "鐢ㄦ埛缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "28404")
- private Long userId;
-
- @Schema(description = "鍐欎綔绫诲瀷", example = "1")
- private Integer type;
-
- @Schema(description = "骞冲彴", requiredMode = Schema.RequiredMode.REQUIRED, example = "TongYi")
- private String platform;
-
- @Schema(description = "妯″瀷", requiredMode = Schema.RequiredMode.REQUIRED, example = "qwen")
- private String model;
-
- @Schema(description = "鐢熸垚鍐呭鎻愮ず", requiredMode = Schema.RequiredMode.REQUIRED, example = "鎾板啓锛氱敯蹇岃禌椹�")
- private String prompt;
-
- @Schema(description = "鐢熸垚鐨勫唴瀹�", example = "浣犻潪甯镐笉閿�")
- private String generatedContent;
-
- @Schema(description = "鍘熸枃", example = "鐪熺殑涔堬紵")
- private String originalContent;
-
- @Schema(description = "闀垮害鎻愮ず璇�", example = "1")
- private Integer length;
-
- @Schema(description = "鏍煎紡鎻愮ず璇�", example = "2")
- private Integer format;
-
- @Schema(description = "璇皵鎻愮ず璇�", example = "3")
- private Integer tone;
-
- @Schema(description = "璇█鎻愮ず璇�", example = "4")
- private Integer language;
-
- @Schema(description = "閿欒淇℃伅")
- private String errorMessage;
-
- @Schema(description = "鍒涘缓鏃堕棿", requiredMode = Schema.RequiredMode.REQUIRED)
- private LocalDateTime createTime;
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/app/package-info.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/app/package-info.java
deleted file mode 100644
index 05b1ce2..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/app/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * TODO 鑺嬭壙锛氱珯浣嶏紝鏃犵壒娈婁綔鐢�
- */
-package cn.iocoder.yudao.module.ai.controller.app;
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/package-info.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/package-info.java
deleted file mode 100644
index 68dfddf..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/package-info.java
+++ /dev/null
@@ -1,6 +0,0 @@
-/**
- * 鎻愪緵 RESTful API 缁欏墠绔細
- * 1. admin 鍖咃細鎻愪緵缁欑鐞嗗悗鍙� yudao-ui-admin 鍓嶇椤圭洰
- * 2. app 鍖咃細鎻愪緵缁欑敤鎴� APP yudao-ui-app 鍓嶇椤圭洰锛屽畠鐨� Controller 鍜� VO 閮借娣诲姞 App 鍓嶇紑锛岀敤浜庡拰绠$悊鍚庡彴杩涜鍖哄垎
- */
-package cn.iocoder.yudao.module.ai.controller;
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java
deleted file mode 100644
index 23aec27..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatConversationDO.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package cn.iocoder.yudao.module.ai.dal.dataobject.chat;
-
-import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
-import com.baomidou.mybatisplus.annotation.KeySequence;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-import lombok.*;
-
-import java.time.LocalDateTime;
-
-/**
- * AI Chat 瀵硅瘽 DO
- *
- * 鐢ㄦ埛姣忔鍙戣捣 Chat 鑱婂ぉ鏃讹紝浼氬垱寤轰竴涓� {@link AiChatConversationDO} 瀵硅薄锛屽皢瀹冪殑娑堟伅鍏宠仈鍦ㄤ竴璧�
- *
- * @author fansili
- * @since 2024/4/14 17:35
- */
-@TableName("ai_chat_conversation")
-@KeySequence("ai_chat_conversation_seq") // 鐢ㄤ簬 Oracle銆丳ostgreSQL銆並ingbase銆丏B2銆丠2 鏁版嵁搴撶殑涓婚敭鑷銆傚鏋滄槸 MySQL 绛夋暟鎹簱锛屽彲涓嶅啓銆�
-@Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-public class AiChatConversationDO extends BaseDO {
-
- public static final String TITLE_DEFAULT = "鏂板璇�";
-
- /**
- * ID 缂栧彿锛岃嚜澧�
- */
- @TableId
- private Long id;
-
- /**
- * 鐢ㄦ埛缂栧彿
- *
- * 鍏宠仈 AdminUserDO 鐨� userId 瀛楁
- */
- private Long userId;
-
- /**
- * 瀵硅瘽鏍囬
- *
- * 榛樿鐢辩郴缁熻嚜鍔ㄧ敓鎴愶紝鍙敤鎴锋墜鍔ㄤ慨鏀�
- */
- private String title;
- /**
- * 鏄惁缃《
- */
- private Boolean pinned;
- /**
- * 缃《鏃堕棿
- */
- private LocalDateTime pinnedTime;
-
- /**
- * 瑙掕壊缂栧彿
- *
- * 鍏宠仈 {@link AiChatRoleDO#getId()}
- */
- private Long roleId;
-
- /**
- * 妯″瀷缂栧彿
- *
- * 鍏宠仈 {@link AiModelDO#getId()} 瀛楁
- */
- private Long modelId;
- /**
- * 妯″瀷鏍囧織
- *
- * 鍐椾綑 {@link AiModelDO#getModel()} 瀛楁
- */
- private String model;
-
- // ========== 瀵硅瘽閰嶇疆 ==========
-
- /**
- * 瑙掕壊璁惧畾
- */
- private String systemMessage;
- /**
- * 娓╁害鍙傛暟
- *
- * 鐢ㄤ簬璋冩暣鐢熸垚鍥炲鐨勯殢鏈烘�у拰澶氭牱鎬х▼搴︼細杈冧綆鐨勬俯搴﹀�间細浣胯緭鍑烘洿鏀舵暃浜庨珮棰戣瘝姹囷紝杈冮珮鐨勫垯澧炲姞澶氭牱鎬�
- */
- private Double temperature;
- /**
- * 鍗曟潯鍥炲鐨勬渶澶� Token 鏁伴噺
- */
- private Integer maxTokens;
- /**
- * 涓婁笅鏂囩殑鏈�澶� Message 鏁伴噺
- */
- private Integer maxContexts;
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java
deleted file mode 100644
index 77012c2..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/chat/AiChatMessageDO.java
+++ /dev/null
@@ -1,126 +0,0 @@
-package cn.iocoder.yudao.module.ai.dal.dataobject.chat;
-
-import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
-import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
-import cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler;
-import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
-import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchResponse;
-import com.baomidou.mybatisplus.annotation.KeySequence;
-import com.baomidou.mybatisplus.annotation.TableField;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-import com.baomidou.mybatisplus.extension.handlers.Jackson3TypeHandler;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import org.springframework.ai.chat.messages.MessageType;
-
-import java.util.List;
-
-/**
- * AI Chat 娑堟伅 DO
- *
- * @since 2024/4/14 17:35
- * @since 2024/4/14 17:35
- */
-@TableName(value = "ai_chat_message", autoResultMap = true)
-@KeySequence("ai_chat_message_seq") // 鐢ㄤ簬 Oracle銆丳ostgreSQL銆並ingbase銆丏B2銆丠2 鏁版嵁搴撶殑涓婚敭鑷銆傚鏋滄槸 MySQL 绛夋暟鎹簱锛屽彲涓嶅啓銆�
-@Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-public class AiChatMessageDO extends BaseDO {
-
- /**
- * 缂栧彿锛屼綔涓烘瘡鏉¤亰澶╄褰曠殑鍞竴鏍囪瘑绗�
- */
- @TableId
- private Long id;
-
- /**
- * 瀵硅瘽缂栧彿
- *
- * 鍏宠仈 {@link AiChatConversationDO#getId()} 瀛楁
- */
- private Long conversationId;
- /**
- * 鍥炲娑堟伅缂栧彿
- *
- * 鍏宠仈 {@link #id} 瀛楁
- *
- * 澶фā鍨嬪洖澶嶇殑娑堟伅缂栧彿锛岀敤浜庘�滈棶绛斺�濈殑鍏宠仈
- */
- private Long replyId;
-
- /**
- * 娑堟伅绫诲瀷
- *
- * 涔熺瓑浠蜂簬 OpenAPI 鐨� role 瀛楁
- *
- * 鏋氫妇 {@link MessageType}
- */
- private String type;
- /**
- * 鐢ㄦ埛缂栧彿
- *
- * 鍏宠仈 AdminUserDO 鐨� userId 瀛楁
- */
- private Long userId;
- /**
- * 瑙掕壊缂栧彿
- *
- * 鍏宠仈 {@link AiChatRoleDO#getId()} 瀛楁
- */
- private Long roleId;
-
- /**
- * 妯″瀷鏍囧織
- *
- * 鍐椾綑 {@link AiModelDO#getModel()}
- */
- private String model;
- /**
- * 妯″瀷缂栧彿
- *
- * 鍏宠仈 {@link AiModelDO#getId()} 瀛楁
- */
- private Long modelId;
-
- /**
- * 鑱婂ぉ鍐呭
- */
- private String content;
- /**
- * 鎺ㄧ悊鍐呭
- */
- private String reasoningContent;
-
- /**
- * 鏄惁鎼哄甫涓婁笅鏂�
- */
- private Boolean useContext;
-
- /**
- * 鐭ヨ瘑搴撴钀界紪鍙锋暟缁�
- *
- * 鍏宠仈 {@link AiKnowledgeSegmentDO#getId()} 瀛楁
- */
- @TableField(typeHandler = LongListTypeHandler.class)
- private List<Long> segmentIds;
-
- /**
- * 鑱旂綉鎼滅储鐨勭綉椤靛唴瀹规暟缁�
- */
- @TableField(typeHandler = Jackson3TypeHandler.class)
- private List<AiWebSearchResponse.WebPage> webSearchPages;
-
- /**
- * 闄勪欢 URL 鏁扮粍
- */
- @TableField(typeHandler = StringListTypeHandler.class)
- private List<String> attachmentUrls;
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java
deleted file mode 100644
index 3ca31b3..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java
+++ /dev/null
@@ -1,127 +0,0 @@
-package cn.iocoder.yudao.module.ai.dal.dataobject.image;
-
-import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;
-import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
-import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum;
-import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
-import com.baomidou.mybatisplus.annotation.KeySequence;
-import com.baomidou.mybatisplus.annotation.TableField;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-import com.baomidou.mybatisplus.extension.handlers.Jackson3TypeHandler;
-import lombok.Data;
-import org.springframework.ai.openai.OpenAiImageOptions;
-import org.springframework.ai.stabilityai.api.StabilityAiImageOptions;
-
-import java.time.LocalDateTime;
-import java.util.List;
-import java.util.Map;
-
-/**
- * AI 缁樼敾 DO
- *
- * @author fansili
- */
-@TableName(value = "ai_image", autoResultMap = true)
-@KeySequence("ai_image_seq") // 鐢ㄤ簬 Oracle銆丳ostgreSQL銆並ingbase銆丏B2銆丠2 鏁版嵁搴撶殑涓婚敭鑷銆傚鏋滄槸 MySQL 绛夋暟鎹簱锛屽彲涓嶅啓銆�
-@Data
-public class AiImageDO extends BaseDO {
-
- /**
- * 缂栧彿
- */
- @TableId
- private Long id;
-
- /**
- * 鐢ㄦ埛缂栧彿
- *
- * 鍏宠仈 {@link AdminUserRespDTO#getId()}
- */
- private Long userId;
-
- /**
- * 鎻愮ず璇�
- */
- private String prompt;
-
- /**
- * 骞冲彴
- *
- * 鏋氫妇 {@link AiPlatformEnum}
- */
- private String platform;
- /**
- * 妯″瀷缂栧彿
- *
- * 鍏宠仈 {@link AiModelDO#getId()}
- */
- private Long modelId;
- /**
- * 妯″瀷鏍囪瘑
- *
- * 鍐椾綑 {@link AiModelDO#getModel()}
- */
- private String model;
-
- /**
- * 鍥剧墖瀹藉害
- */
- private Integer width;
- /**
- * 鍥剧墖楂樺害
- */
- private Integer height;
-
- /**
- * 鐢熸垚鐘舵��
- *
- * 鏋氫妇 {@link AiImageStatusEnum}
- */
- private Integer status;
-
- /**
- * 瀹屾垚鏃堕棿
- */
- private LocalDateTime finishTime;
-
- /**
- * 缁樼敾閿欒淇℃伅
- */
- private String errorMessage;
-
- /**
- * 鍥剧墖鍦板潃
- */
- private String picUrl;
- /**
- * 鏄惁鍏紑
- */
- private Boolean publicStatus;
-
- /**
- * 缁樺埗鍙傛暟锛屼笉鍚� platform 鐨勪笉鍚屽弬鏁�
- *
- * 1. {@link OpenAiImageOptions}
- * 2. {@link StabilityAiImageOptions}
- */
- @TableField(typeHandler = Jackson3TypeHandler.class)
- private Map<String, Object> options;
-
- /**
- * mj buttons 鎸夐挳
- */
- @TableField(typeHandler = Jackson3TypeHandler.class)
- private List<MidjourneyApi.Button> buttons;
-
- /**
- * 浠诲姟缂栧彿
- *
- * 1. midjourney proxy锛氬叧鑱旂殑 task id
- */
- private String taskId;
-
-}
-
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
index e1327a5..d6aaef2 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDO.java
@@ -14,50 +14,33 @@
* @author xiaoxin
*/
@TableName(value = "ai_knowledge", autoResultMap = true)
-@KeySequence("ai_knowledge_seq") // 鐢ㄤ簬 Oracle銆丳ostgreSQL銆並ingbase銆丏B2銆丠2 鏁版嵁搴撶殑涓婚敭鑷銆傚鏋滄槸 MySQL 绛夋暟鎹簱锛屽彲涓嶅啓銆�
+@KeySequence("ai_knowledge_seq")
@Data
public class AiKnowledgeDO extends BaseDO {
- /**
- * 缂栧彿
- */
@TableId
private Long id;
- /**
- * 鐭ヨ瘑搴撳悕绉�
- */
+
private String name;
- /**
- * 鐭ヨ瘑搴撴弿杩�
- */
+
private String description;
/**
- * 鍚戦噺妯″瀷缂栧彿
- *
- * 鍏宠仈 {@link AiModelDO#getId()}
+ * 鍚戦噺妯″瀷缂栧彿锛屽叧鑱� {@link AiModelDO#getId()}
*/
private Long embeddingModelId;
+
/**
- * 妯″瀷鏍囪瘑
- *
- * 鍐椾綑 {@link AiModelDO#getModel()}
+ * 妯″瀷鏍囪瘑锛屽啑浣� {@link AiModelDO#getModel()}
*/
private String embeddingModel;
- /**
- * topK
- */
private Integer topK;
- /**
- * 鐩镐技搴﹂槇鍊�
- */
+
private Double similarityThreshold;
/**
- * 鐘舵��
- * <p>
- * 鏋氫妇 {@link CommonStatusEnum}
+ * 鐘舵�侊紝鏋氫妇 {@link CommonStatusEnum}
*/
private Integer status;
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java
index ac014e9..04491ec 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeDocumentDO.java
@@ -13,56 +13,31 @@
* @author xiaoxin
*/
@TableName(value = "ai_knowledge_document")
-@KeySequence("ai_knowledge_document_seq") // 鐢ㄤ簬 Oracle銆丳ostgreSQL銆並ingbase銆丏B2銆丠2 鏁版嵁搴撶殑涓婚敭鑷銆傚鏋滄槸 MySQL 绛夋暟鎹簱锛屽彲涓嶅啓銆�
+@KeySequence("ai_knowledge_document_seq")
@Data
public class AiKnowledgeDocumentDO extends BaseDO {
- /**
- * 缂栧彿
- */
@TableId
private Long id;
- /**
- * 鐭ヨ瘑搴撶紪鍙�
- * <p>
- * 鍏宠仈 {@link AiKnowledgeDO#getId()}
- */
+
private Long knowledgeId;
- /**
- * 鏂囨。鍚嶇О
- */
+
private String name;
- /**
- * 鏂囦欢 URL
- */
+
private String url;
- /**
- * 鍐呭
- */
+
private String content;
- /**
- * 鏂囨。闀垮害
- */
+
private Integer contentLength;
- /**
- * 鏂囨。 token 鏁伴噺
- */
private Integer tokens;
- /**
- * 鍒嗙墖鏈�澶� Token 鏁�
- */
+
private Integer segmentMaxTokens;
- /**
- * 鍙洖娆℃暟
- */
private Integer retrievalCount;
/**
- * 鐘舵��
- * <p>
- * 鏋氫妇 {@link CommonStatusEnum}
+ * 鐘舵�侊紝鏋氫妇 {@link CommonStatusEnum}
*/
private Integer status;
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java
index cccbd68..05f1b77 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/knowledge/AiKnowledgeSegmentDO.java
@@ -13,59 +13,31 @@
* @author xiaoxin
*/
@TableName(value = "ai_knowledge_segment")
-@KeySequence("ai_knowledge_segment_seq") // 鐢ㄤ簬 Oracle銆丳ostgreSQL銆並ingbase銆丏B2銆丠2 鏁版嵁搴撶殑涓婚敭鑷銆傚鏋滄槸 MySQL 绛夋暟鎹簱锛屽彲涓嶅啓銆�
+@KeySequence("ai_knowledge_segment_seq")
@Data
public class AiKnowledgeSegmentDO extends BaseDO {
- /**
- * 鍚戦噺搴撶殑缂栧彿 - 绌哄��
- */
public static final String VECTOR_ID_EMPTY = "";
- /**
- * 缂栧彿
- */
@TableId
private Long id;
- /**
- * 鐭ヨ瘑搴撶紪鍙�
- * <p>
- * 鍏宠仈 {@link AiKnowledgeDO#getId()}
- */
+
private Long knowledgeId;
- /**
- * 鏂囨。缂栧彿
- * <p>
- * 鍏宠仈 {@link AiKnowledgeDocumentDO#getId()}
- */
+
private Long documentId;
- /**
- * 鍒囩墖鍐呭
- */
+
private String content;
- /**
- * 鍒囩墖鍐呭闀垮害
- */
+
private Integer contentLength;
- /**
- * 鍚戦噺搴撶殑缂栧彿
- */
private String vectorId;
- /**
- * token 鏁伴噺
- */
+
private Integer tokens;
- /**
- * 鍙洖娆℃暟
- */
private Integer retrievalCount;
/**
- * 鐘舵��
- * <p>
- * 鏋氫妇 {@link CommonStatusEnum}
+ * 鐘舵�侊紝鏋氫妇 {@link CommonStatusEnum}
*/
private Integer status;
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/mindmap/AiMindMapDO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/mindmap/AiMindMapDO.java
deleted file mode 100644
index db788b7..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/mindmap/AiMindMapDO.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package cn.iocoder.yudao.module.ai.dal.dataobject.mindmap;
-
-import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
-import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
-import com.baomidou.mybatisplus.annotation.KeySequence;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-import lombok.Data;
-
-/**
- * AI 鎬濈淮瀵煎浘 DO
- *
- * @author xiaoxin
- */
-@TableName(value = "ai_mind_map")
-@KeySequence("ai_mind_map_seq") // 鐢ㄤ簬 Oracle銆丳ostgreSQL銆並ingbase銆丏B2銆丠2 鏁版嵁搴撶殑涓婚敭鑷銆傚鏋滄槸 MySQL 绛夋暟鎹簱锛屽彲涓嶅啓銆�
-@Data
-public class AiMindMapDO extends BaseDO {
-
- /**
- * 缂栧彿
- */
- @TableId
- private Long id;
-
- /**
- * 鐢ㄦ埛缂栧彿
- * <p>
- * 鍏宠仈 AdminUserDO 鐨� userId 瀛楁
- */
- private Long userId;
-
- /**
- * 骞冲彴
- * <p>
- * 鏋氫妇 {@link AiPlatformEnum}
- */
- private String platform;
- /**
- * 妯″瀷缂栧彿
- *
- * 鍏宠仈 {@link AiModelDO#getId()}
- */
- private Long modelId;
- /**
- * 妯″瀷
- */
- private String model;
-
- /**
- * 鐢熸垚鍐呭鎻愮ず
- */
- private String prompt;
-
- /**
- * 鐢熸垚鐨勫唴瀹�
- */
- private String generatedContent;
-
- /**
- * 閿欒淇℃伅
- */
- private String errorMessage;
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiApiKeyDO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiApiKeyDO.java
index a29f8f1..fc749a2 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiApiKeyDO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiApiKeyDO.java
@@ -1,53 +1,42 @@
package cn.iocoder.yudao.module.ai.dal.dataobject.model;
-import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/**
- * AI API 绉橀挜 DO
+ * AI API 瀵嗛挜 DO
*
- * @author 鑺嬮亾婧愮爜
+ * @author fansili
*/
@TableName("ai_api_key")
-@KeySequence("ai_api_key_seq") // 鐢ㄤ簬 Oracle銆丳ostgreSQL銆並ingbase銆丏B2銆丠2 鏁版嵁搴撶殑涓婚敭鑷銆傚鏋滄槸 MySQL 绛夋暟鎹簱锛屽彲涓嶅啓銆�
+@KeySequence("ai_api_key_seq")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AiApiKeyDO extends BaseDO {
- /**
- * 缂栧彿
- */
@TableId
private Long id;
- /**
- * 鍚嶇О
- */
+
private String name;
- /**
- * 瀵嗛挜
- */
+
private String apiKey;
+
/**
- * 骞冲彴
- *
- * 鏋氫妇 {@link AiPlatformEnum}
+ * 骞冲彴锛屾灇涓� {@link AiPlatformEnum}
*/
private String platform;
- /**
- * API 鍦板潃
- */
+
private String url;
+
/**
- * 鐘舵��
- *
- * 鏋氫妇 {@link CommonStatusEnum}
+ * 鐘舵�侊紝鏋氫妇 {@link CommonStatusEnum}
*/
private Integer status;
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java
deleted file mode 100644
index d20b25e..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiChatRoleDO.java
+++ /dev/null
@@ -1,111 +0,0 @@
-package cn.iocoder.yudao.module.ai.dal.dataobject.model;
-
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
-import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
-import cn.iocoder.yudao.framework.mybatis.core.type.StringListTypeHandler;
-import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
-import com.baomidou.mybatisplus.annotation.KeySequence;
-import com.baomidou.mybatisplus.annotation.TableField;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-import lombok.*;
-
-import java.util.List;
-
-/**
- * AI 鑱婂ぉ瑙掕壊 DO
- *
- * @author fansili
- * @since 2024/4/24 19:39
- */
-@TableName(value = "ai_chat_role", autoResultMap = true)
-@KeySequence("ai_chat_role_seq") // 鐢ㄤ簬 Oracle銆丳ostgreSQL銆並ingbase銆丏B2銆丠2 鏁版嵁搴撶殑涓婚敭鑷銆傚鏋滄槸 MySQL 绛夋暟鎹簱锛屽彲涓嶅啓銆�
-@Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-public class AiChatRoleDO extends BaseDO {
-
- /**
- * 缂栧彿
- */
- @TableId
- private Long id;
- /**
- * 瑙掕壊鍚嶇О
- */
- private String name;
- /**
- * 瑙掕壊澶村儚
- */
- private String avatar;
- /**
- * 瑙掕壊鍒嗙被
- */
- private String category;
- /**
- * 瑙掕壊鎻忚堪
- */
- private String description;
- /**
- * 瑙掕壊璁惧畾
- */
- private String systemMessage;
-
- /**
- * 鐢ㄦ埛缂栧彿
- *
- * 鍏宠仈 AdminUserDO 鐨� userId 瀛楁
- */
- private Long userId;
-
- /**
- * 妯″瀷缂栧彿
- *
- * 鍏宠仈 {@link AiModelDO#getId()} 瀛楁
- */
- private Long modelId;
-
- /**
- * 寮曠敤鐨勭煡璇嗗簱缂栧彿鍒楄〃
- *
- * 鍏宠仈 {@link AiKnowledgeDO#getId()} 瀛楁
- */
- @TableField(typeHandler = LongListTypeHandler.class)
- private List<Long> knowledgeIds;
- /**
- * 寮曠敤鐨勫伐鍏风紪鍙峰垪琛�
- *
- * 鍏宠仈 {@link AiToolDO#getId()} 瀛楁
- */
- @TableField(typeHandler = LongListTypeHandler.class)
- private List<Long> toolIds;
- /**
- * 寮曠敤鐨� MCP Client 鍚嶅瓧鍒楄〃
- *
- * 鍏宠仈 spring.ai.mcp.client 涓嬬殑鍚嶅瓧
- */
- @TableField(typeHandler = StringListTypeHandler.class)
- private List<String> mcpClientNames;
-
- /**
- * 鏄惁鍏紑
- *
- * 1. true - 鍏紑锛涚敱绠$悊鍛樺湪銆愯鑹茬鐞嗐�戞墍鍒涘缓
- * 2. false - 绉佹湁锛涚敱涓汉鍦ㄣ�愭垜鐨勮鑹层�戞墍鍒涘缓
- */
- private Boolean publicStatus;
-
- /**
- * 鎺掑簭鍊�
- */
- private Integer sort;
- /**
- * 鐘舵��
- *
- * 鏋氫妇 {@link CommonStatusEnum}
- */
- private Integer status;
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiModelDO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiModelDO.java
index 9e54f94..571d85a 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiModelDO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiModelDO.java
@@ -1,9 +1,9 @@
package cn.iocoder.yudao.module.ai.dal.dataobject.model;
-import cn.iocoder.yudao.module.ai.enums.model.AiModelTypeEnum;
-import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.module.ai.enums.model.AiModelTypeEnum;
+import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@@ -12,77 +12,49 @@
/**
* AI 妯″瀷 DO
*
- * 榛樿妯″瀷锛歿@link #status} 涓哄紑鍚紝骞朵笖 {@link #sort} 鎺掑簭绗竴
- *
* @author fansili
- * @since 2024/4/24 19:39
*/
@TableName("ai_model")
-@KeySequence("ai_model_seq") // 鐢ㄤ簬 Oracle銆丳ostgreSQL銆並ingbase銆丏B2銆丠2 鏁版嵁搴撶殑涓婚敭鑷銆傚鏋滄槸 MySQL 绛夋暟鎹簱锛屽彲涓嶅啓銆�
+@KeySequence("ai_model_seq")
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AiModelDO extends BaseDO {
- /**
- * 缂栧彿
- */
@TableId
private Long id;
+
/**
- * API 绉橀挜缂栧彿
- *
- * 鍏宠仈 {@link AiApiKeyDO#getId()}
+ * API 绉橀挜缂栧彿锛屽叧鑱� {@link AiApiKeyDO#getId()}
*/
private Long keyId;
- /**
- * 妯″瀷鍚嶇О
- */
+
private String name;
- /**
- * 妯″瀷鏍囧織
- */
+
private String model;
+
/**
- * 骞冲彴
- *
- * 鏋氫妇 {@link AiPlatformEnum}
+ * 骞冲彴锛屾灇涓� {@link AiPlatformEnum}
*/
private String platform;
+
/**
- * 绫诲瀷
- *
- * 鏋氫妇 {@link AiModelTypeEnum}
+ * 绫诲瀷锛屾灇涓� {@link AiModelTypeEnum}
*/
private Integer type;
- /**
- * 鎺掑簭鍊�
- */
private Integer sort;
+
/**
- * 鐘舵��
- *
- * 鏋氫妇 {@link CommonStatusEnum}
+ * 鐘舵�侊紝鏋氫妇 {@link CommonStatusEnum}
*/
private Integer status;
- // ========== 瀵硅瘽閰嶇疆 ==========
-
- /**
- * 娓╁害鍙傛暟
- *
- * 鐢ㄤ簬璋冩暣鐢熸垚鍥炲鐨勯殢鏈烘�у拰澶氭牱鎬х▼搴︼細杈冧綆鐨勬俯搴﹀�间細浣胯緭鍑烘洿鏀舵暃浜庨珮棰戣瘝姹囷紝杈冮珮鐨勫垯澧炲姞澶氭牱鎬�
- */
private Double temperature;
- /**
- * 鍗曟潯鍥炲鐨勬渶澶� Token 鏁伴噺
- */
+
private Integer maxTokens;
- /**
- * 涓婁笅鏂囩殑鏈�澶� Message 鏁伴噺
- */
+
private Integer maxContexts;
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiToolDO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiToolDO.java
deleted file mode 100644
index 7132213..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiToolDO.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package cn.iocoder.yudao.module.ai.dal.dataobject.model;
-
-import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
-import cn.iocoder.yudao.module.ai.tool.function.DirectoryListToolFunction;
-import cn.iocoder.yudao.module.ai.tool.function.WeatherQueryToolFunction;
-import com.baomidou.mybatisplus.annotation.KeySequence;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-import lombok.*;
-
-/**
- * AI 宸ュ叿 DO
- *
- * @author 鑺嬮亾婧愮爜
- */
-@TableName("ai_tool")
-@KeySequence("ai_tool_seq") // 鐢ㄤ簬 Oracle銆丳ostgreSQL銆並ingbase銆丏B2銆丠2 鏁版嵁搴撶殑涓婚敭鑷銆傚鏋滄槸 MySQL 绛夋暟鎹簱锛屽彲涓嶅啓銆�
-@Data
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-public class AiToolDO extends BaseDO {
-
- /**
- * 宸ュ叿缂栧彿
- */
- @TableId
- private Long id;
- /**
- * 宸ュ叿鍚嶇О
- *
- * 瀵瑰簲 Bean 鐨勫悕瀛楋紝渚嬪璇达細
- * 1. {@link DirectoryListToolFunction} 鐨� Bean 鍚嶅瓧鏄� directory_list
- * 2. {@link WeatherQueryToolFunction} 鐨� Bean 鍚嶅瓧鏄� weather_query
- */
- private String name;
- /**
- * 宸ュ叿鎻忚堪
- */
- private String description;
- /**
- * 鐘舵��
- *
- * 鏋氫妇 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}
- */
- private Integer status;
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/music/AiMusicDO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/music/AiMusicDO.java
deleted file mode 100644
index 45ea488..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/music/AiMusicDO.java
+++ /dev/null
@@ -1,119 +0,0 @@
-package cn.iocoder.yudao.module.ai.dal.dataobject.music;
-
-import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
-import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
-import cn.iocoder.yudao.module.ai.enums.music.AiMusicGenerateModeEnum;
-import cn.iocoder.yudao.module.ai.enums.music.AiMusicStatusEnum;
-import com.baomidou.mybatisplus.annotation.KeySequence;
-import com.baomidou.mybatisplus.annotation.TableField;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-import com.baomidou.mybatisplus.extension.handlers.Jackson3TypeHandler;
-import lombok.Data;
-
-import java.util.List;
-
-/**
- * AI 闊充箰 DO
- *
- * @author xiaoxin
- */
-@TableName(value = "ai_music", autoResultMap = true)
-@KeySequence("ai_music_seq") // 鐢ㄤ簬 Oracle銆丳ostgreSQL銆並ingbase銆丏B2銆丠2 鏁版嵁搴撶殑涓婚敭鑷銆傚鏋滄槸 MySQL 绛夋暟鎹簱锛屽彲涓嶅啓銆�
-@Data
-public class AiMusicDO extends BaseDO {
-
- /**
- * 缂栧彿
- */
- @TableId
- private Long id;
-
- /**
- * 鐢ㄦ埛缂栧彿
- * <p>
- * 鍏宠仈 AdminUserDO 鐨� userId 瀛楁
- */
- private Long userId;
-
- /**
- * 闊充箰鍚嶇О
- */
- private String title;
-
- /**
- * 姝岃瘝
- */
- private String lyric;
-
- /**
- * 鍥剧墖鍦板潃
- */
- private String imageUrl;
- /**
- * 闊抽鍦板潃
- */
- private String audioUrl;
- /**
- * 瑙嗛鍦板潃
- */
- private String videoUrl;
-
- /**
- * 闊充箰鐘舵��
- * <p>
- * 鏋氫妇 {@link AiMusicStatusEnum}
- */
- private Integer status;
-
- /**
- * 鐢熸垚妯″紡
- * <p>
- * 鏋氫妇 {@link AiMusicGenerateModeEnum}
- */
- private Integer generateMode;
-
- /**
- * 鎻忚堪璇�
- */
- private String description;
-
- /**
- * 骞冲彴
- * <p>
- * 鏋氫妇 {@link AiPlatformEnum}
- */
- private String platform;
- // TODO @鑺嬭壙锛歮odelId锛�
- /**
- * 妯″瀷
- */
- private String model;
-
- /**
- * 闊充箰椋庢牸鏍囩
- */
- @TableField(typeHandler = Jackson3TypeHandler.class)
- private List<String> tags;
-
- /**
- * 闊充箰鏃堕暱
- */
- private Double duration;
-
- /**
- * 鏄惁鍏紑
- */
- private Boolean publicStatus;
-
- /**
- * 浠诲姟缂栧彿
- */
- private String taskId;
-
- /**
- * 閿欒淇℃伅
- */
- private String errorMessage;
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/workflow/AiWorkflowDO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/workflow/AiWorkflowDO.java
deleted file mode 100644
index d844f7d..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/workflow/AiWorkflowDO.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package cn.iocoder.yudao.module.ai.dal.dataobject.workflow;
-
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
-import com.baomidou.mybatisplus.annotation.KeySequence;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-import lombok.Data;
-
-/**
- * AI 宸ヤ綔娴� DO
- *
- * @author lesan
- */
-@TableName(value = "ai_workflow", autoResultMap = true)
-@KeySequence("ai_workflow") // 鐢ㄤ簬 Oracle銆丳ostgreSQL銆並ingbase銆丏B2銆丠2 鏁版嵁搴撶殑涓婚敭鑷銆傚鏋滄槸 MySQL 绛夋暟鎹簱锛屽彲涓嶅啓銆�
-@Data
-public class AiWorkflowDO extends BaseDO {
-
- /**
- * 缂栧彿
- */
- @TableId
- private Long id;
- /**
- * 宸ヤ綔娴佸悕绉�
- */
- private String name;
- /**
- * 宸ヤ綔娴佹爣璇�
- */
- private String code;
-
- /**
- * 宸ヤ綔娴佹ā鍨� JSON 鏁版嵁
- */
- private String graph;
-
- /**
- * 澶囨敞
- */
- private String remark;
-
- /**
- * 鐘舵��
- *
- * 鏋氫妇 {@link CommonStatusEnum}
- */
- private Integer status;
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java
deleted file mode 100644
index 75e1e29..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/write/AiWriteDO.java
+++ /dev/null
@@ -1,104 +0,0 @@
-package cn.iocoder.yudao.module.ai.dal.dataobject.write;
-
-import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
-import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
-import cn.iocoder.yudao.module.ai.enums.DictTypeConstants;
-import cn.iocoder.yudao.module.ai.enums.write.AiWriteTypeEnum;
-import com.baomidou.mybatisplus.annotation.KeySequence;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-import lombok.Data;
-
-/**
- * AI 鍐欎綔 DO
- *
- * @author xiaoxin
- */
-@TableName("ai_write")
-@KeySequence("ai_write_seq") // 鐢ㄤ簬 Oracle銆丳ostgreSQL銆並ingbase銆丏B2銆丠2 鏁版嵁搴撶殑涓婚敭鑷銆傚鏋滄槸 MySQL 绛夋暟鎹簱锛屽彲涓嶅啓銆�
-@Data
-public class AiWriteDO extends BaseDO {
-
- /**
- * 缂栧彿
- */
- @TableId
- private Long id;
-
- /**
- * 鐢ㄦ埛缂栧彿
- *
- * 鍏宠仈 AdminUserDO 鐨� userId 瀛楁
- */
- private Long userId;
-
- /**
- * 鍐欎綔绫诲瀷
- * <p>
- * 鏋氫妇 {@link AiWriteTypeEnum}
- */
- private Integer type;
-
- /**
- * 骞冲彴
- *
- * 鏋氫妇 {@link AiPlatformEnum}
- */
- private String platform;
- /**
- * 妯″瀷缂栧彿
- *
- * 鍏宠仈 {@link AiModelDO#getId()}
- */
- private Long modelId;
- /**
- * 妯″瀷
- */
- private String model;
-
- /**
- * 鐢熸垚鍐呭鎻愮ず
- */
- private String prompt;
-
- /**
- * 鐢熸垚鐨勫唴瀹�
- */
- private String generatedContent;
- /**
- * 鍘熸枃
- */
- private String originalContent;
-
- /**
- * 闀垮害鎻愮ず璇�
- *
- * 瀛楀吀锛歿@link DictTypeConstants#AI_WRITE_LENGTH}
- */
- private Integer length;
- /**
- * 鏍煎紡鎻愮ず璇�
- *
- * 瀛楀吀锛歿@link DictTypeConstants#AI_WRITE_FORMAT}
- */
- private Integer format;
- /**
- * 璇皵鎻愮ず璇�
- *
- * 瀛楀吀锛歿@link DictTypeConstants#AI_WRITE_TONE}
- */
- private Integer tone;
- /**
- * 璇█鎻愮ず璇�
- *
- * 瀛楀吀锛歿@link DictTypeConstants#AI_WRITE_LANGUAGE}
- */
- private Integer language;
-
- /**
- * 閿欒淇℃伅
- */
- private String errorMessage;
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/chat/AiChatConversationMapper.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/chat/AiChatConversationMapper.java
deleted file mode 100644
index ce9da2f..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/chat/AiChatConversationMapper.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package cn.iocoder.yudao.module.ai.dal.mysql.chat;
-
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
-import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
-import org.apache.ibatis.annotations.Mapper;
-
-import java.util.List;
-
-/**
- * AI 鑱婂ぉ瀵硅瘽 Mapper
- *
- * @author 鑺嬮亾婧愮爜
- */
-@Mapper
-public interface AiChatConversationMapper extends BaseMapperX<AiChatConversationDO> {
-
- default List<AiChatConversationDO> selectListByUserId(Long userId) {
- return selectList(AiChatConversationDO::getUserId, userId);
- }
-
- default List<AiChatConversationDO> selectListByUserIdAndPinned(Long userId, boolean pinned) {
- return selectList(new LambdaQueryWrapperX<AiChatConversationDO>()
- .eq(AiChatConversationDO::getUserId, userId)
- .eq(AiChatConversationDO::getPinned, pinned));
- }
-
- default PageResult<AiChatConversationDO> selectChatConversationPage(AiChatConversationPageReqVO pageReqVO) {
- return selectPage(pageReqVO, new LambdaQueryWrapperX<AiChatConversationDO>()
- .eqIfPresent(AiChatConversationDO::getUserId, pageReqVO.getUserId())
- .likeIfPresent(AiChatConversationDO::getTitle, pageReqVO.getTitle())
- .betweenIfPresent(AiChatConversationDO::getCreateTime, pageReqVO.getCreateTime())
- .orderByDesc(AiChatConversationDO::getId));
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/chat/AiChatMessageMapper.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/chat/AiChatMessageMapper.java
deleted file mode 100644
index 5020f39..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/chat/AiChatMessageMapper.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package cn.iocoder.yudao.module.ai.dal.mysql.chat;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.map.MapUtil;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
-import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
-import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import org.apache.ibatis.annotations.Mapper;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-/**
- * AI 鑱婂ぉ瀵硅瘽 Mapper
- *
- * @author fansili
- */
-@Mapper
-public interface AiChatMessageMapper extends BaseMapperX<AiChatMessageDO> {
-
- default List<AiChatMessageDO> selectListByConversationId(Long conversationId) {
- return selectList(new LambdaQueryWrapperX<AiChatMessageDO>()
- .eq(AiChatMessageDO::getConversationId, conversationId)
- .orderByAsc(AiChatMessageDO::getId));
- }
-
- default Map<Long, Integer> selectCountMapByConversationId(Collection<Long> conversationIds) {
- // SQL count 鏌ヨ
- List<Map<String, Object>> result = selectMaps(new QueryWrapper<AiChatMessageDO>()
- .select("COUNT(id) AS count, conversation_id AS conversationId")
- .in("conversation_id", conversationIds)
- .groupBy("conversation_id"));
- if (CollUtil.isEmpty(result)) {
- return Collections.emptyMap();
- }
- // 杞崲鏁版嵁
- return CollectionUtils.convertMap(result,
- record -> MapUtil.getLong(record, "conversationId"),
- record -> MapUtil.getInt(record, "count" ));
- }
-
- default PageResult<AiChatMessageDO> selectPage(AiChatMessagePageReqVO pageReqVO) {
- return selectPage(pageReqVO, new LambdaQueryWrapperX<AiChatMessageDO>()
- .eqIfPresent(AiChatMessageDO::getConversationId, pageReqVO.getConversationId())
- .eqIfPresent(AiChatMessageDO::getUserId, pageReqVO.getUserId())
- .likeIfPresent(AiChatMessageDO::getContent, pageReqVO.getContent())
- .betweenIfPresent(AiChatMessageDO::getCreateTime, pageReqVO.getCreateTime())
- .orderByDesc(AiChatMessageDO::getId));
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/image/AiImageMapper.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/image/AiImageMapper.java
deleted file mode 100644
index f87c947..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/image/AiImageMapper.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package cn.iocoder.yudao.module.ai.dal.mysql.image;
-
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
-import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
-import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImagePageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImagePublicPageReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
-import org.apache.ibatis.annotations.Mapper;
-
-import java.util.List;
-
-/**
- * AI 缁樺浘 Mapper
- *
- * @author fansili
- */
-@Mapper
-public interface AiImageMapper extends BaseMapperX<AiImageDO> {
-
- default AiImageDO selectByTaskId(String taskId) {
- return selectOne(AiImageDO::getTaskId, taskId);
- }
-
- default PageResult<AiImageDO> selectPage(AiImagePageReqVO reqVO) {
- return selectPage(reqVO, new LambdaQueryWrapperX<AiImageDO>()
- .eqIfPresent(AiImageDO::getUserId, reqVO.getUserId())
- .eqIfPresent(AiImageDO::getPlatform, reqVO.getPlatform())
- .eqIfPresent(AiImageDO::getStatus, reqVO.getStatus())
- .eqIfPresent(AiImageDO::getPublicStatus, reqVO.getPublicStatus())
- .betweenIfPresent(AiImageDO::getCreateTime, reqVO.getCreateTime())
- .orderByDesc(AiImageDO::getId));
- }
-
- default PageResult<AiImageDO> selectPageMy(Long userId, AiImagePageReqVO reqVO) {
- return selectPage(reqVO, new LambdaQueryWrapperX<AiImageDO>()
- .likeIfPresent(AiImageDO::getPrompt, reqVO.getPrompt())
- // 鎯呭喌涓�锛氬叕寮�
- .eq(Boolean.TRUE.equals(reqVO.getPublicStatus()), AiImageDO::getPublicStatus, reqVO.getPublicStatus())
- // 鎯呭喌浜岋細绉佹湁
- .eq(Boolean.FALSE.equals(reqVO.getPublicStatus()), AiImageDO::getUserId, userId)
- .orderByDesc(AiImageDO::getId));
- }
-
- default PageResult<AiImageDO> selectPage(AiImagePublicPageReqVO pageReqVO) {
- return selectPage(pageReqVO, new LambdaQueryWrapperX<AiImageDO>()
- .eqIfPresent(AiImageDO::getPublicStatus, Boolean.TRUE)
- .likeIfPresent(AiImageDO::getPrompt, pageReqVO.getPrompt())
- .orderByDesc(AiImageDO::getId));
- }
-
- default List<AiImageDO> selectListByStatusAndPlatform(Integer status, String platform) {
- return selectList(AiImageDO::getStatus, status,
- AiImageDO::getPlatform, platform);
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java
index 55f04bb..492f191 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeDocumentMapper.java
@@ -7,15 +7,9 @@
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
-
import java.util.Collection;
import java.util.List;
-/**
- * AI 鐭ヨ瘑搴撴枃妗� Mapper
- *
- * @author xiaoxin
- */
@Mapper
public interface AiKnowledgeDocumentMapper extends BaseMapperX<AiKnowledgeDocumentDO> {
@@ -30,10 +24,6 @@
update(new LambdaUpdateWrapper<AiKnowledgeDocumentDO>()
.setSql(" retrieval_count = retrieval_count + 1")
.in(AiKnowledgeDocumentDO::getId, ids));
- }
-
- default List<AiKnowledgeDocumentDO> selectListByStatus(Integer status) {
- return selectList(AiKnowledgeDocumentDO::getStatus, status);
}
default List<AiKnowledgeDocumentDO> selectListByKnowledgeId(Long knowledgeId) {
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java
index 3433c0b..66976a3 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeMapper.java
@@ -6,14 +6,8 @@
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
import org.apache.ibatis.annotations.Mapper;
-
import java.util.List;
-/**
- * AI 鐭ヨ瘑搴� Mapper
- *
- * @author xiaoxin
- */
@Mapper
public interface AiKnowledgeMapper extends BaseMapperX<AiKnowledgeDO> {
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java
index 2b7daac..8ff5988 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/knowledge/AiKnowledgeSegmentMapper.java
@@ -10,21 +10,15 @@
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import org.apache.ibatis.annotations.Mapper;
-
import java.util.Collection;
import java.util.List;
-/**
- * AI 鐭ヨ瘑搴撳垎鐗� Mapper
- *
- * @author xiaoxin
- */
@Mapper
public interface AiKnowledgeSegmentMapper extends BaseMapperX<AiKnowledgeSegmentDO> {
default PageResult<AiKnowledgeSegmentDO> selectPage(AiKnowledgeSegmentPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<AiKnowledgeSegmentDO>()
- .eq(AiKnowledgeSegmentDO::getDocumentId, reqVO.getDocumentId())
+ .eqIfPresent(AiKnowledgeSegmentDO::getDocumentId, reqVO.getDocumentId())
.likeIfPresent(AiKnowledgeSegmentDO::getContent, reqVO.getContent())
.eqIfPresent(AiKnowledgeSegmentDO::getStatus, reqVO.getStatus())
.orderByDesc(AiKnowledgeSegmentDO::getId));
@@ -38,8 +32,7 @@
default List<AiKnowledgeSegmentDO> selectListByDocumentId(Long documentId) {
return selectList(new LambdaQueryWrapperX<AiKnowledgeSegmentDO>()
- .eq(AiKnowledgeSegmentDO::getDocumentId, documentId)
- .orderByDesc(AiKnowledgeSegmentDO::getId));
+ .eq(AiKnowledgeSegmentDO::getDocumentId, documentId));
}
default List<AiKnowledgeSegmentDO> selectListByKnowledgeIdAndStatus(Long knowledgeId, Integer status) {
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/mindmap/AiMindMapMapper.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/mindmap/AiMindMapMapper.java
deleted file mode 100644
index 0292ef4..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/mindmap/AiMindMapMapper.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package cn.iocoder.yudao.module.ai.dal.mysql.mindmap;
-
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
-import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
-import cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapPageReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.mindmap.AiMindMapDO;
-import org.apache.ibatis.annotations.Mapper;
-
-/**
- * AI 鎬濈淮瀵煎浘 Mapper
- *
- * @author xiaoxin
- */
-@Mapper
-public interface AiMindMapMapper extends BaseMapperX<AiMindMapDO> {
-
- default PageResult<AiMindMapDO> selectPage(AiMindMapPageReqVO reqVO) {
- return selectPage(reqVO, new LambdaQueryWrapperX<AiMindMapDO>()
- .eqIfPresent(AiMindMapDO::getUserId, reqVO.getUserId())
- .eqIfPresent(AiMindMapDO::getPrompt, reqVO.getPrompt())
- .betweenIfPresent(AiMindMapDO::getCreateTime, reqVO.getCreateTime())
- .orderByDesc(AiMindMapDO::getId));
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiApiKeyMapper.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiApiKeyMapper.java
index 0a2efe3..a56c7d8 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiApiKeyMapper.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiApiKeyMapper.java
@@ -3,33 +3,26 @@
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
-import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
import org.apache.ibatis.annotations.Mapper;
-/**
- * AI API 瀵嗛挜 Mapper
- *
- * @author 鑺嬮亾婧愮爜
- */
@Mapper
public interface AiApiKeyMapper extends BaseMapperX<AiApiKeyDO> {
- default PageResult<AiApiKeyDO> selectPage(AiApiKeyPageReqVO reqVO) {
- return selectPage(reqVO, new LambdaQueryWrapperX<AiApiKeyDO>()
- .likeIfPresent(AiApiKeyDO::getName, reqVO.getName())
- .eqIfPresent(AiApiKeyDO::getPlatform, reqVO.getPlatform())
- .eqIfPresent(AiApiKeyDO::getStatus, reqVO.getStatus())
+ default AiApiKeyDO selectFirstByPlatformAndStatus(String platform, Integer status) {
+ return selectOne(new LambdaQueryWrapperX<AiApiKeyDO>()
+ .eq(AiApiKeyDO::getPlatform, platform)
+ .eq(AiApiKeyDO::getStatus, status)
+ .last("LIMIT 1"));
+ }
+
+ default PageResult<AiApiKeyDO> selectPage(AiApiKeyPageReqVO pageReqVO) {
+ return selectPage(pageReqVO, new LambdaQueryWrapperX<AiApiKeyDO>()
+ .likeIfPresent(AiApiKeyDO::getName, pageReqVO.getName())
+ .eqIfPresent(AiApiKeyDO::getPlatform, pageReqVO.getPlatform())
+ .eqIfPresent(AiApiKeyDO::getStatus, pageReqVO.getStatus())
.orderByDesc(AiApiKeyDO::getId));
}
- default AiApiKeyDO selectFirstByPlatformAndStatus(String platform, Integer status) {
- return selectOne(new QueryWrapperX<AiApiKeyDO>()
- .eq("platform", platform)
- .eq("status", status)
- .limitN(1)
- .orderByAsc("id"));
- }
-
-}
\ No newline at end of file
+}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatMapper.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatMapper.java
deleted file mode 100644
index bfe2caf..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatMapper.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package cn.iocoder.yudao.module.ai.dal.mysql.model;
-
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
-import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
-import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
-import org.apache.ibatis.annotations.Mapper;
-
-import javax.annotation.Nullable;
-import java.util.List;
-
-/**
- * API 妯″瀷 Mapper
- *
- * @author fansili
- */
-@Mapper
-public interface AiChatMapper extends BaseMapperX<AiModelDO> {
-
- default AiModelDO selectFirstByStatus(Integer type, Integer status) {
- return selectOne(new QueryWrapperX<AiModelDO>()
- .eq("type", type)
- .eq("status", status)
- .limitN(1)
- .orderByAsc("sort"));
- }
-
- default PageResult<AiModelDO> selectPage(AiModelPageReqVO reqVO) {
- return selectPage(reqVO, new LambdaQueryWrapperX<AiModelDO>()
- .likeIfPresent(AiModelDO::getName, reqVO.getName())
- .eqIfPresent(AiModelDO::getModel, reqVO.getModel())
- .eqIfPresent(AiModelDO::getPlatform, reqVO.getPlatform())
- .orderByAsc(AiModelDO::getSort));
- }
-
- default List<AiModelDO> selectListByStatusAndType(Integer status, Integer type,
- @Nullable String platform) {
- return selectList(new LambdaQueryWrapperX<AiModelDO>()
- .eq(AiModelDO::getStatus, status)
- .eq(AiModelDO::getType, type)
- .eqIfPresent(AiModelDO::getPlatform, platform)
- .orderByAsc(AiModelDO::getSort));
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatRoleMapper.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatRoleMapper.java
deleted file mode 100644
index ed91edf..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiChatRoleMapper.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package cn.iocoder.yudao.module.ai.dal.mysql.model;
-
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
-import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRolePageReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
-import org.apache.ibatis.annotations.Mapper;
-
-import java.util.List;
-
-/**
- * AI 鑱婂ぉ瑙掕壊 Mapper
- *
- * @author fansili
- */
-@Mapper
-public interface AiChatRoleMapper extends BaseMapperX<AiChatRoleDO> {
-
- default PageResult<AiChatRoleDO> selectPage(AiChatRolePageReqVO reqVO) {
- return selectPage(reqVO, new LambdaQueryWrapperX<AiChatRoleDO>()
- .likeIfPresent(AiChatRoleDO::getName, reqVO.getName())
- .eqIfPresent(AiChatRoleDO::getCategory, reqVO.getCategory())
- .eqIfPresent(AiChatRoleDO::getPublicStatus, reqVO.getPublicStatus())
- .orderByAsc(AiChatRoleDO::getSort));
- }
-
- default PageResult<AiChatRoleDO> selectPageByMy(AiChatRolePageReqVO reqVO, Long userId) {
- return selectPage(reqVO, new LambdaQueryWrapperX<AiChatRoleDO>()
- .likeIfPresent(AiChatRoleDO::getName, reqVO.getName())
- .eqIfPresent(AiChatRoleDO::getCategory, reqVO.getCategory())
- // 鎯呭喌涓�锛氬叕寮�
- .eq(Boolean.TRUE.equals(reqVO.getPublicStatus()), AiChatRoleDO::getPublicStatus, reqVO.getPublicStatus())
- // 鎯呭喌浜岋細绉佹湁
- .eq(Boolean.FALSE.equals(reqVO.getPublicStatus()), AiChatRoleDO::getUserId, userId)
- .eq(Boolean.FALSE.equals(reqVO.getPublicStatus()), AiChatRoleDO::getStatus, CommonStatusEnum.ENABLE.getStatus())
- .orderByAsc(AiChatRoleDO::getSort));
- }
-
- default List<AiChatRoleDO> selectListGroupByCategory(Integer status) {
- return selectList(new LambdaQueryWrapperX<AiChatRoleDO>()
- .select(AiChatRoleDO::getCategory)
- .eq(AiChatRoleDO::getStatus, status)
- .groupBy(AiChatRoleDO::getCategory));
- }
-
- default List<AiChatRoleDO> selectListByName(String name) {
- return selectList(new LambdaQueryWrapperX<AiChatRoleDO>()
- .likeIfPresent(AiChatRoleDO::getName, name)
- .orderByAsc(AiChatRoleDO::getSort));
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiModelMapper.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiModelMapper.java
new file mode 100644
index 0000000..d6d6b4c
--- /dev/null
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiModelMapper.java
@@ -0,0 +1,38 @@
+package cn.iocoder.yudao.module.ai.dal.mysql.model;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO;
+import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
+import org.apache.ibatis.annotations.Mapper;
+import java.util.List;
+
+@Mapper
+public interface AiModelMapper extends BaseMapperX<AiModelDO> {
+
+ default AiModelDO selectFirstByStatus(Integer type, Integer status) {
+ return selectOne(new LambdaQueryWrapperX<AiModelDO>()
+ .eq(AiModelDO::getType, type)
+ .eq(AiModelDO::getStatus, status)
+ .orderByAsc(AiModelDO::getSort)
+ .last("LIMIT 1"));
+ }
+
+ default PageResult<AiModelDO> selectPage(AiModelPageReqVO pageReqVO) {
+ return selectPage(pageReqVO, new LambdaQueryWrapperX<AiModelDO>()
+ .eqIfPresent(AiModelDO::getType, pageReqVO.getType())
+ .eqIfPresent(AiModelDO::getStatus, pageReqVO.getStatus())
+ .eqIfPresent(AiModelDO::getPlatform, pageReqVO.getPlatform())
+ .likeIfPresent(AiModelDO::getName, pageReqVO.getName())
+ .orderByAsc(AiModelDO::getSort));
+ }
+
+ default List<AiModelDO> selectListByStatusAndType(Integer status, Integer type, String platform) {
+ return selectList(new LambdaQueryWrapperX<AiModelDO>()
+ .eqIfPresent(AiModelDO::getStatus, status)
+ .eqIfPresent(AiModelDO::getType, type)
+ .eqIfPresent(AiModelDO::getPlatform, platform));
+ }
+
+}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiToolMapper.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiToolMapper.java
deleted file mode 100644
index d5d2966..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiToolMapper.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package cn.iocoder.yudao.module.ai.dal.mysql.model;
-
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
-import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO;
-import org.apache.ibatis.annotations.Mapper;
-
-import java.util.List;
-
-/**
- * AI 宸ュ叿 Mapper
- *
- * @author 鑺嬮亾婧愮爜
- */
-@Mapper
-public interface AiToolMapper extends BaseMapperX<AiToolDO> {
-
- default PageResult<AiToolDO> selectPage(AiToolPageReqVO reqVO) {
- return selectPage(reqVO, new LambdaQueryWrapperX<AiToolDO>()
- .likeIfPresent(AiToolDO::getName, reqVO.getName())
- .eqIfPresent(AiToolDO::getDescription, reqVO.getDescription())
- .eqIfPresent(AiToolDO::getStatus, reqVO.getStatus())
- .betweenIfPresent(AiToolDO::getCreateTime, reqVO.getCreateTime())
- .orderByDesc(AiToolDO::getId));
- }
-
- default List<AiToolDO> selectListByStatus(Integer status) {
- return selectList(new LambdaQueryWrapperX<AiToolDO>()
- .eq(AiToolDO::getStatus, status)
- .orderByDesc(AiToolDO::getId));
- }
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/music/AiMusicMapper.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/music/AiMusicMapper.java
deleted file mode 100644
index 025f5e0..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/music/AiMusicMapper.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package cn.iocoder.yudao.module.ai.dal.mysql.music;
-
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
-import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
-import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicPageReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;
-import org.apache.ibatis.annotations.Mapper;
-
-import java.util.List;
-
-/**
- * AI 闊充箰 Mapper
- *
- * @author xiaoxin
- */
-@Mapper
-public interface AiMusicMapper extends BaseMapperX<AiMusicDO> {
-
- default List<AiMusicDO> selectListByStatus(Integer status) {
- return selectList(AiMusicDO::getStatus, status);
- }
-
- default PageResult<AiMusicDO> selectPage(AiMusicPageReqVO reqVO) {
- return selectPage(reqVO, new LambdaQueryWrapperX<AiMusicDO>()
- .eqIfPresent(AiMusicDO::getUserId, reqVO.getUserId())
- .eqIfPresent(AiMusicDO::getTitle, reqVO.getTitle())
- .eqIfPresent(AiMusicDO::getStatus, reqVO.getStatus())
- .eqIfPresent(AiMusicDO::getGenerateMode, reqVO.getGenerateMode())
- .betweenIfPresent(AiMusicDO::getCreateTime, reqVO.getCreateTime())
- .eqIfPresent(AiMusicDO::getPublicStatus, reqVO.getPublicStatus())
- .orderByDesc(AiMusicDO::getId));
- }
-
- default PageResult<AiMusicDO> selectPageByMy(AiMusicPageReqVO reqVO, Long userId) {
- return selectPage(reqVO, new LambdaQueryWrapperX<AiMusicDO>()
- // 鎯呭喌涓�锛氬叕寮�
- .eq(Boolean.TRUE.equals(reqVO.getPublicStatus()), AiMusicDO::getPublicStatus, reqVO.getPublicStatus())
- // 鎯呭喌浜岋細绉佹湁
- .eq(Boolean.FALSE.equals(reqVO.getPublicStatus()), AiMusicDO::getUserId, userId)
- .orderByAsc(AiMusicDO::getId));
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/workflow/AiWorkflowMapper.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/workflow/AiWorkflowMapper.java
deleted file mode 100644
index 3770dbf..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/workflow/AiWorkflowMapper.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package cn.iocoder.yudao.module.ai.dal.mysql.workflow;
-
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
-import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
-import cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowPageReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.workflow.AiWorkflowDO;
-import org.apache.ibatis.annotations.Mapper;
-
-/**
- * AI 宸ヤ綔娴� Mapper
- *
- * @author lesan
- */
-@Mapper
-public interface AiWorkflowMapper extends BaseMapperX<AiWorkflowDO> {
-
- default AiWorkflowDO selectByCode(String code) {
- return selectOne(AiWorkflowDO::getCode, code);
- }
-
- default PageResult<AiWorkflowDO> selectPage(AiWorkflowPageReqVO pageReqVO) {
- return selectPage(pageReqVO, new LambdaQueryWrapperX<AiWorkflowDO>()
- .eqIfPresent(AiWorkflowDO::getStatus, pageReqVO.getStatus())
- .likeIfPresent(AiWorkflowDO::getName, pageReqVO.getName())
- .likeIfPresent(AiWorkflowDO::getCode, pageReqVO.getCode())
- .betweenIfPresent(AiWorkflowDO::getCreateTime, pageReqVO.getCreateTime()));
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/write/AiWriteMapper.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/write/AiWriteMapper.java
deleted file mode 100644
index c4983eb..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/write/AiWriteMapper.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package cn.iocoder.yudao.module.ai.dal.mysql.write;
-
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
-import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
-import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWritePageReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.write.AiWriteDO;
-import org.apache.ibatis.annotations.Mapper;
-
-/**
- * AI 鍐欎綔 Mapper
- *
- * @author xiaoxin
- */
-@Mapper
-public interface AiWriteMapper extends BaseMapperX<AiWriteDO> {
-
- default PageResult<AiWriteDO> selectPage(AiWritePageReqVO reqVO) {
- return selectPage(reqVO, new LambdaQueryWrapperX<AiWriteDO>()
- .eqIfPresent(AiWriteDO::getUserId, reqVO.getUserId())
- .eqIfPresent(AiWriteDO::getType, reqVO.getType())
- .eqIfPresent(AiWriteDO::getPlatform, reqVO.getPlatform())
- .betweenIfPresent(AiWriteDO::getCreateTime, reqVO.getCreateTime())
- .orderByDesc(AiWriteDO::getId));
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleEnum.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleEnum.java
deleted file mode 100644
index 1479274..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/AiChatRoleEnum.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package cn.iocoder.yudao.module.ai.enums;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-/**
- * AI 鍐呯疆鑱婂ぉ瑙掕壊鐨勬灇涓�
- *
- * @author xiaoxin
- */
-@AllArgsConstructor
-@Getter
-public enum AiChatRoleEnum {
-
- AI_WRITE_ROLE("鍐欎綔鍔╂墜", """
- 浣犳槸涓�浣嶅嚭鑹茬殑鍐欎綔鍔╂墜锛岃兘澶熷府鍔╃敤鎴风敓鎴愬垱鎰忓拰鐏垫劅锛屽苟鍦ㄧ敤鎴锋彁渚涘満鏅拰鎻愮ず璇嶆椂鐢熸垚瀵瑰簲鐨勫洖澶嶃�備綘鐨勪换鍔″寘鎷細
- 1. 鎾板啓寤鸿锛氭牴鎹敤鎴锋彁渚涚殑涓婚鎴栭棶棰橈紝鎻愪緵璇︾粏鐨勫啓浣滃缓璁�佹儏鑺傚彂灞曟柟鍚戙�佽鑹茶瀹氫互鍙婅儗鏅弿鍐欙紝纭繚鍐呭缁撴瀯娓呮櫚銆佹湁閫昏緫銆�
- 2. 鍥炲鐢熸垚锛氭牴鎹敤鎴锋彁渚涚殑鍦烘櫙鍜屾彁绀鸿瘝锛岀敓鎴愬悎閫傜殑瀵硅瘽鎴栨枃瀛楀洖澶嶏紝纭繚璇皵鍜岄鏍肩鍚堝満鏅渶姹傘��
- 闄ゆ涔嬪涓嶉渶瑕侀櫎浜嗘鏂囧唴瀹瑰鐨勫叾浠栧洖澶嶏紝濡傛爣棰樸�佸紑澶淬�佷换浣曡В閲婃�ц鍙ユ垨閬撴瓑銆�
- """),
-
- AI_MIND_MAP_ROLE("瀵煎浘鍔╂墜", """
- 浣犳槸涓�浣嶉潪甯镐紭绉�鐨勬�濈淮瀵煎浘鍔╂墜锛屼綘浼氭妸鐢ㄦ埛鐨勬墍鏈夋彁闂兘鎬荤粨鎴愭�濈淮瀵煎浘锛岀劧鍚庝互 Markdown 鏍煎紡杈撳嚭銆俶arkdown 鍙渶瑕佽緭鍑轰竴绾ф爣棰橈紝浜岀骇鏍囬锛屼笁绾ф爣棰橈紝鍥涚骇鏍囬锛屾渶澶氳緭鍑哄洓绾э紝闄ゆ涔嬪涓嶈杈撳嚭浠讳綍鍏朵粬 markdown 鏍囪銆備笅闈㈡槸涓�涓悎鏍肩殑渚嬪瓙锛�
- # Geek-AI 鍔╂墜
- ## 瀹屾暣鐨勫紑婧愮郴缁�
- ### 鍓嶇寮�婧�
- ### 鍚庣寮�婧�
- ## 鏀寔鍚勭澶фā鍨�
- ### OpenAI
- ### Azure
- ### 鏂囧績涓�瑷�
- ### 閫氫箟鍗冮棶
- ## 闆嗘垚澶氱鏀惰垂鏂瑰紡
- ### 鏀粯瀹�
- ### 寰俊
- 闄ゆ涔嬪涓嶈浠讳綍瑙i噴鎬ц鍙ャ��
- """),
- ;
-
- /**
- * 瑙掕壊鍚�
- */
- private final String name;
-
- /**
- * 瑙掕壊璁惧畾
- */
- private final String systemMessage;
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/AiDocumentSplitStrategyEnum.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/AiDocumentSplitStrategyEnum.java
index f0a9cd2..b05fabc 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/AiDocumentSplitStrategyEnum.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/AiDocumentSplitStrategyEnum.java
@@ -5,49 +5,18 @@
/**
* AI 鐭ヨ瘑搴撴枃妗e垏鐗囩瓥鐣ユ灇涓�
- *
- * @author runzhen
*/
@AllArgsConstructor
@Getter
public enum AiDocumentSplitStrategyEnum {
- /**
- * 鑷姩璇嗗埆鏂囨。绫诲瀷骞堕�夋嫨鏈�浣冲垏鐗囩瓥鐣�
- */
AUTO("auto", "鑷姩璇嗗埆"),
-
- /**
- * 鍩轰簬 Token 鏁伴噺鏈烘鍒囧垎锛堥粯璁ょ瓥鐣ワ級
- */
TOKEN("token", "Token 鍒囧垎"),
-
- /**
- * 鎸夋钀藉垏鍒嗭紙浠ュ弻鎹㈣绗︿负鍒嗛殧锛�
- */
PARAGRAPH("paragraph", "娈佃惤鍒囧垎"),
-
- /**
- * Markdown QA 鏍煎紡涓撶敤鍒囩墖鍣�
- * 璇嗗埆浜岀骇鏍囬浣滀负闂锛屼繚鎸侀棶绛斿瀹屾暣鎬�
- * 闀跨瓟妗堟櫤鑳藉垏鍒嗕絾淇濈暀闂浣滀负涓婁笅鏂�
- */
MARKDOWN_QA("markdown_qa", "Markdown QA 鍒囧垎"),
-
- /**
- * 璇箟鍖栧垏鍒嗭紝淇濈暀鍙ュ瓙瀹屾暣鎬�
- * 鍦ㄦ钀藉拰鍙ュ瓙杈圭晫澶勫垏鍒嗭紝閬垮厤鎴柇
- */
SEMANTIC("semantic", "璇箟鍒囧垎");
- /**
- * 绛栫暐浠g爜
- */
private final String code;
-
- /**
- * 绛栫暐鍚嶇О
- */
private final String name;
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/DictTypeConstants.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/DictTypeConstants.java
deleted file mode 100644
index 73782a2..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/DictTypeConstants.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package cn.iocoder.yudao.module.ai.enums;
-
-/**
- * AI 瀛楀吀绫诲瀷鐨勬灇涓剧被
- *
- * @author xiaoxin
- */
-public interface DictTypeConstants {
-
- // ========== AI Write ==========
- String AI_WRITE_FORMAT = "ai_write_format"; // 鍐欎綔鏍煎紡
- String AI_WRITE_LENGTH = "ai_write_length"; // 鍐欎綔闀垮害
- String AI_WRITE_LANGUAGE = "ai_write_language"; // 鍐欎綔璇█
- String AI_WRITE_TONE = "ai_write_tone"; // 鍐欎綔璇皵
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java
index 4658a08..a6b09c7 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java
@@ -4,66 +4,25 @@
/**
* AI 閿欒鐮佹灇涓剧被
- * <p>
- * ai 绯荤粺锛屼娇鐢� 1-040-000-000 娈�
*/
public interface ErrorCodeConstants {
- // ========== API 瀵嗛挜 1-040-000-000 ==========
+ // ========== API 瀵嗛挜 ==========
ErrorCode API_KEY_NOT_EXISTS = new ErrorCode(1_040_000_000, "API 瀵嗛挜涓嶅瓨鍦�");
ErrorCode API_KEY_DISABLE = new ErrorCode(1_040_000_001, "API 瀵嗛挜宸茬鐢紒");
- ErrorCode API_CONFIG_PLACEHOLDER_NOT_RESOLVED = new ErrorCode(1_040_000_002, "AI 閰嶇疆({})鏃犳硶瑙f瀽锛岃妫�鏌ョ幆澧冨彉閲忔垨閰嶇疆椤�");
- // ========== API 妯″瀷 1-040-001-000 ==========
+ // ========== API 妯″瀷 ==========
ErrorCode MODEL_NOT_EXISTS = new ErrorCode(1_040_001_000, "妯″瀷涓嶅瓨鍦�!");
ErrorCode MODEL_DISABLE = new ErrorCode(1_040_001_001, "妯″瀷({})宸茬鐢�!");
- ErrorCode MODEL_DEFAULT_NOT_EXISTS = new ErrorCode(1_040_001_002, "鎿嶄綔澶辫触锛屾壘涓嶅埌榛樿妯″瀷");
ErrorCode MODEL_USE_TYPE_ERROR = new ErrorCode(1_040_001_003, "鎿嶄綔澶辫触锛岃妯″瀷鐨勬ā鍨嬬被鍨嬩笉姝g‘");
- // ========== API 鑱婂ぉ瑙掕壊 1-040-002-000 ==========
- ErrorCode CHAT_ROLE_NOT_EXISTS = new ErrorCode(1_040_002_000, "鑱婂ぉ瑙掕壊涓嶅瓨鍦�");
- ErrorCode CHAT_ROLE_DISABLE = new ErrorCode(1_040_001_001, "鑱婂ぉ瑙掕壊({})宸茬鐢�!");
-
- // ========== API 鑱婂ぉ浼氳瘽 1-040-003-000 ==========
- ErrorCode CHAT_CONVERSATION_NOT_EXISTS = new ErrorCode(1_040_003_000, "瀵硅瘽涓嶅瓨鍦�!");
- ErrorCode CHAT_CONVERSATION_MODEL_ERROR = new ErrorCode(1_040_003_001, "鎿嶄綔澶辫触锛岃鑱婂ぉ妯″瀷鐨勯厤缃笉瀹屾暣");
-
- // ========== API 鑱婂ぉ娑堟伅 1-040-004-000 ==========
- ErrorCode CHAT_MESSAGE_NOT_EXIST = new ErrorCode(1_040_004_000, "娑堟伅涓嶅瓨鍦�!");
- ErrorCode CHAT_STREAM_ERROR = new ErrorCode(1_040_004_001, "瀵硅瘽鐢熸垚寮傚父!");
-
- // ========== API 缁樼敾 1-040-005-000 ==========
- ErrorCode IMAGE_NOT_EXISTS = new ErrorCode(1_040_005_000, "鍥剧墖涓嶅瓨鍦�!");
- ErrorCode IMAGE_MIDJOURNEY_SUBMIT_FAIL = new ErrorCode(1_040_005_001, "Midjourney 鎻愪氦澶辫触!鍘熷洜锛歿}");
- ErrorCode IMAGE_CUSTOM_ID_NOT_EXISTS = new ErrorCode(1_040_005_002, "Midjourney 鎸夐挳 customId 涓嶅瓨鍦�! {}");
-
- // ========== API 闊充箰 1-040-006-000 ==========
- ErrorCode MUSIC_NOT_EXISTS = new ErrorCode(1_040_006_000, "闊充箰涓嶅瓨鍦�!");
-
- // ========== API 鍐欎綔 1-040-007-000 ==========
- ErrorCode WRITE_NOT_EXISTS = new ErrorCode(1_040_007_000, "浣滄枃涓嶅瓨鍦�!");
- ErrorCode WRITE_STREAM_ERROR = new ErrorCode(1_040_07_001, "鍐欎綔鐢熸垚寮傚父!");
-
- // ========== API 鎬濈淮瀵煎浘 1-040-008-000 ==========
- ErrorCode MIND_MAP_NOT_EXISTS = new ErrorCode(1_040_008_000, "鎬濈淮瀵煎浘涓嶅瓨鍦�!");
-
- // ========== API 鐭ヨ瘑搴� 1-040-009-000 ==========
+ // ========== API 鐭ヨ瘑搴� ==========
ErrorCode KNOWLEDGE_NOT_EXISTS = new ErrorCode(1_040_009_000, "鐭ヨ瘑搴撲笉瀛樺湪!");
-
ErrorCode KNOWLEDGE_DOCUMENT_NOT_EXISTS = new ErrorCode(1_040_009_101, "鏂囨。涓嶅瓨鍦�!");
ErrorCode KNOWLEDGE_DOCUMENT_FILE_EMPTY = new ErrorCode(1_040_009_102, "鏂囨。鍐呭涓虹┖!");
ErrorCode KNOWLEDGE_DOCUMENT_FILE_DOWNLOAD_FAIL = new ErrorCode(1_040_009_102, "鏂囦欢涓嬭浇澶辫触!");
ErrorCode KNOWLEDGE_DOCUMENT_FILE_READ_FAIL = new ErrorCode(1_040_009_102, "鏂囨。鍔犺浇澶辫触!");
-
ErrorCode KNOWLEDGE_SEGMENT_NOT_EXISTS = new ErrorCode(1_040_009_202, "娈佃惤涓嶅瓨鍦�!");
ErrorCode KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG = new ErrorCode(1_040_009_203, "鍐呭 Token 鏁颁负 {}锛岃秴杩囨渶澶ч檺鍒� {}");
-
- // ========== AI 宸ュ叿 1-040-010-000 ==========
- ErrorCode TOOL_NOT_EXISTS = new ErrorCode(1_040_010_000, "宸ュ叿涓嶅瓨鍦�");
- ErrorCode TOOL_NAME_NOT_EXISTS = new ErrorCode(1_040_010_001, "宸ュ叿({})鎵句笉鍒� Bean");
-
- // ========== AI 宸ヤ綔娴� 1-040-011-000 ==========
- ErrorCode WORKFLOW_NOT_EXISTS = new ErrorCode(1_040_011_000, "宸ヤ綔娴佷笉瀛樺湪");
- ErrorCode WORKFLOW_CODE_EXISTS = new ErrorCode(1_040_011_001, "宸ヤ綔娴佹爣璇嗗凡瀛樺湪");
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/image/AiImageStatusEnum.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/image/AiImageStatusEnum.java
deleted file mode 100644
index cf80761..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/image/AiImageStatusEnum.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package cn.iocoder.yudao.module.ai.enums.image;
-
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-/**
- * AI 缁樼敾鐘舵�佺殑鏋氫妇
- *
- * @author fansili
- */
-@AllArgsConstructor
-@Getter
-public enum AiImageStatusEnum {
-
- IN_PROGRESS(10, "杩涜涓�"),
- SUCCESS(20, "宸插畬鎴�"),
- FAIL(30, "宸插け璐�");
-
- /**
- * 鐘舵��
- */
- private final Integer status;
- /**
- * 鐘舵�佸悕
- */
- private final String name;
-
- public static AiImageStatusEnum valueOfStatus(Integer status) {
- for (AiImageStatusEnum statusEnum : AiImageStatusEnum.values()) {
- if (statusEnum.getStatus().equals(status)) {
- return statusEnum;
- }
- }
- throw new IllegalArgumentException("鏈煡浼氳瘽鐘舵�侊細 " + status);
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiModelTypeEnum.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiModelTypeEnum.java
index bdba3e8..025e940 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiModelTypeEnum.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiModelTypeEnum.java
@@ -3,13 +3,10 @@
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
-
import java.util.Arrays;
/**
* AI 妯″瀷绫诲瀷鐨勬灇涓�
- *
- * @author 鑺嬮亾婧愮爜
*/
@Getter
@RequiredArgsConstructor
@@ -22,20 +19,12 @@
EMBEDDING(5, "鍚戦噺"),
RERANK(6, "閲嶆帓搴�");
- /**
- * 绫诲瀷
- */
private final Integer type;
- /**
- * 绫诲瀷鍚�
- */
private final String name;
public static final Integer[] ARRAYS = Arrays.stream(values()).map(AiModelTypeEnum::getType).toArray(Integer[]::new);
@Override
- public Integer[] array() {
- return ARRAYS;
- }
+ public Integer[] array() { return ARRAYS; }
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiPlatformEnum.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiPlatformEnum.java
index 3c09144..5c09c8d 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiPlatformEnum.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiPlatformEnum.java
@@ -3,71 +3,33 @@
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
-
import java.util.Arrays;
/**
* AI 妯″瀷骞冲彴
- *
- * @author fansili
*/
@Getter
@AllArgsConstructor
public enum AiPlatformEnum implements ArrayValuable<String> {
- // ========== 鍥藉唴骞冲彴 ==========
+ TONG_YI("TongYi", "閫氫箟鍗冮棶"),
+ DEEP_SEEK("DeepSeek", "DeepSeek"),
+ OPENAI("OpenAI", "OpenAI"),
+ OLLAMA("Ollama", "Ollama");
- TONG_YI("TongYi", "閫氫箟鍗冮棶"), // 闃块噷
- YI_YAN("YiYan", "鏂囧績涓�瑷�"), // 鐧惧害
- DEEP_SEEK("DeepSeek", "DeepSeek"), // DeepSeek
- ZHI_PU("ZhiPu", "鏅鸿氨"), // 鏅鸿氨 AI
- XING_HUO("XingHuo", "鏄熺伀"), // 璁
- DOU_BAO("DouBao", "璞嗗寘"), // 瀛楄妭
- HUN_YUAN("HunYuan", "娣峰厓"), // 鑵捐
- SILICON_FLOW("SiliconFlow", "纭呭熀娴佸姩"), // 纭呭熀娴佸姩
- MINI_MAX("MiniMax", "MiniMax"), // 绋�瀹囩鎶�
- MOONSHOT("Moonshot", "鏈堜箣鏆楅潰"), // KIMI
- BAI_CHUAN("BaiChuan", "鐧惧窛鏅鸿兘"), // 鐧惧窛鏅鸿兘
- STEP_FUN("StepFun", "闃惰穬鏄熻景"), // 闃惰穬鏄熻景
-
- // ========== 鍥藉骞冲彴 ==========
-
- OPENAI("OpenAI", "OpenAI"), // OpenAI 瀹樻柟
- AZURE_OPENAI("AzureOpenAI", "AzureOpenAI"), // OpenAI 寰蒋
- ANTHROPIC("Anthropic", "Anthropic"), // Anthropic Claude
- GEMINI("Gemini", "Gemini"), // 璋锋瓕 Gemini
- OLLAMA("Ollama", "Ollama"),
-
- STABLE_DIFFUSION("StableDiffusion", "StableDiffusion"), // Stability AI
- MIDJOURNEY("Midjourney", "Midjourney"), // Midjourney
- SUNO("Suno", "Suno"), // Suno AI
- GROK("Grok", "Grok"), // Grok
-
- ;
-
- /**
- * 骞冲彴
- */
private final String platform;
- /**
- * 骞冲彴鍚�
- */
private final String name;
public static final String[] ARRAYS = Arrays.stream(values()).map(AiPlatformEnum::getPlatform).toArray(String[]::new);
public static AiPlatformEnum validatePlatform(String platform) {
- for (AiPlatformEnum platformEnum : AiPlatformEnum.values()) {
- if (platformEnum.getPlatform().equals(platform)) {
- return platformEnum;
- }
+ for (AiPlatformEnum e : values()) {
+ if (e.getPlatform().equals(platform)) return e;
}
- throw new IllegalArgumentException("闈炴硶骞冲彴锛� " + platform);
+ throw new IllegalArgumentException("闈炴硶骞冲彴锛�" + platform);
}
@Override
- public String[] array() {
- return ARRAYS;
- }
+ public String[] array() { return ARRAYS; }
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/music/AiMusicGenerateModeEnum.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/music/AiMusicGenerateModeEnum.java
deleted file mode 100644
index 8205294..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/music/AiMusicGenerateModeEnum.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package cn.iocoder.yudao.module.ai.enums.music;
-
-import cn.iocoder.yudao.framework.common.core.ArrayValuable;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-import java.util.Arrays;
-
-/**
- * AI 闊充箰鐢熸垚妯″紡鐨勬灇涓�
- *
- * @author xiaoxin
- */
-@AllArgsConstructor
-@Getter
-public enum AiMusicGenerateModeEnum implements ArrayValuable<Integer> {
-
- DESCRIPTION(1, "鎻忚堪妯″紡"),
- LYRIC(2, "姝岃瘝妯″紡");
-
- /**
- * 妯″紡
- */
- private final Integer mode;
- /**
- * 妯″紡鍚�
- */
- private final String name;
-
- public static final Integer[] ARRAYS = Arrays.stream(values()).map(AiMusicGenerateModeEnum::getMode).toArray(Integer[]::new);
-
- @Override
- public Integer[] array() {
- return ARRAYS;
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/music/AiMusicStatusEnum.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/music/AiMusicStatusEnum.java
deleted file mode 100644
index 9340a59..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/music/AiMusicStatusEnum.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package cn.iocoder.yudao.module.ai.enums.music;
-
-import cn.iocoder.yudao.framework.common.core.ArrayValuable;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-import java.util.Arrays;
-
-/**
- * AI 闊充箰鐘舵�佺殑鏋氫妇
- *
- * @author xiaoxin
- */
-@AllArgsConstructor
-@Getter
-public enum AiMusicStatusEnum implements ArrayValuable<Integer> {
-
- IN_PROGRESS(10, "杩涜涓�"),
- SUCCESS(20, "宸插畬鎴�"),
- FAIL(30, "宸插け璐�");
-
- /**
- * 鐘舵��
- */
- private final Integer status;
-
- /**
- * 鐘舵�佸悕
- */
- private final String name;
-
- public static final Integer[] ARRAYS = Arrays.stream(values()).map(AiMusicStatusEnum::getStatus).toArray(Integer[]::new);
-
- @Override
- public Integer[] array() {
- return ARRAYS;
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteTypeEnum.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteTypeEnum.java
deleted file mode 100644
index 6d91433..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/write/AiWriteTypeEnum.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package cn.iocoder.yudao.module.ai.enums.write;
-
-import cn.iocoder.yudao.framework.common.core.ArrayValuable;
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
-import java.util.Arrays;
-
-/**
- * AI 鍐欎綔绫诲瀷鐨勬灇涓�
- *
- * @author xiaoxin
- */
-@AllArgsConstructor
-@Getter
-public enum AiWriteTypeEnum implements ArrayValuable<Integer> {
-
- WRITING(1, "鎾板啓", "璇锋挵鍐欎竴绡囧叧浜� [{}] 鐨勬枃绔犮�傛枃绔犵殑鍐呭鏍煎紡锛歿}锛岃姘旓細{}锛岃瑷�锛歿}锛岄暱搴︼細{}銆傝纭繚娑电洊涓昏鍐呭锛屼笉闇�瑕侀櫎浜嗘鏂囧唴瀹瑰鐨勫叾浠栧洖澶嶏紝濡傛爣棰樸�侀澶栫殑瑙i噴鎴栭亾姝夈��"),
- REPLY(2, "鍥炲", "璇烽拡瀵瑰涓嬪唴瀹癸細[{}] 鍋氫釜鍥炲銆傚洖澶嶅唴瀹瑰弬鑰冿細[{}], 鍥炲鏍煎紡锛歿}锛岃姘旓細{}锛岃瑷�锛歿}锛岄暱搴︼細{}銆備笉闇�瑕侀櫎浜嗘鏂囧唴瀹瑰鐨勫叾浠栧洖澶嶏紝濡傛爣棰樸�佸紑澶淬�侀澶栫殑瑙i噴鎴栭亾姝夈��");
-
- /**
- * 绫诲瀷
- */
- private final Integer type;
- /**
- * 绫诲瀷鍚�
- */
- private final String name;
-
- /**
- * 妯$増
- */
- private final String prompt;
-
- public static final Integer[] ARRAYS = Arrays.stream(values()).map(AiWriteTypeEnum::getType).toArray(Integer[]::new);
-
- @Override
- public Integer[] array() {
- return ARRAYS;
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java
index 17c3c42..27ceff0 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java
@@ -1,77 +1,47 @@
package cn.iocoder.yudao.module.ai.framework.ai.config;
import cn.hutool.core.util.StrUtil;
-import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.AiModelFactory;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.AiModelFactoryImpl;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.grok.GrokChatModel;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.minimax.MiniMaxChatModel;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.moonshot.MoonshotChatModel;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowApiConstants;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowChatModel;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.stepfun.StepFunChatModel;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.XingHuoChatModel;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.yiyan.YiYanChatModel;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.zhipu.ZhiPuChatModel;
-import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchClient;
-import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.bocha.AiBoChaWebSearchClient;
-import cn.iocoder.yudao.module.ai.tool.method.PersonService;
-import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
-import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi;
-import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
-import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
-import com.alibaba.cloud.ai.dashscope.embedding.text.DashScopeEmbeddingModel;
-import com.alibaba.cloud.ai.dashscope.embedding.text.DashScopeEmbeddingOptions;
-import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel;
-import com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions;
import io.micrometer.observation.ObservationRegistry;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.ai.chat.model.ChatModel;
-import org.springframework.ai.deepseek.DeepSeekChatModel;
-import org.springframework.ai.deepseek.DeepSeekChatOptions;
-import org.springframework.ai.deepseek.api.DeepSeekApi;
import org.springframework.ai.document.MetadataMode;
import org.springframework.ai.embedding.BatchingStrategy;
import org.springframework.ai.embedding.TokenCountBatchingStrategy;
import org.springframework.ai.model.tool.ToolCallingManager;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
+import org.springframework.ai.openai.OpenAiEmbeddingModel;
+import org.springframework.ai.openai.OpenAiEmbeddingOptions;
import org.springframework.ai.retry.RetryUtils;
-import org.springframework.ai.support.ToolCallbacks;
import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator;
import org.springframework.ai.tokenizer.TokenCountEstimator;
-import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusServiceClientProperties;
import org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreProperties;
import org.springframework.ai.vectorstore.qdrant.autoconfigure.QdrantVectorStoreProperties;
import org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.boot.context.properties.EnableConfigurationProperties;
-import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-
-import java.util.List;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
/**
* 鑺嬮亾 AI 鑷姩閰嶇疆
*
- * @author fansili
+ * 浣跨敤 OpenAI 鍏煎鎺ュ彛瀵规帴閫氫箟鍗冮棶 DashScope
*/
@Configuration
-@EnableConfigurationProperties({ YudaoAiProperties.class,
- QdrantVectorStoreProperties.class, // 瑙f瀽 Qdrant 閰嶇疆
- RedisVectorStoreProperties.class, // 瑙f瀽 Redis 閰嶇疆
- MilvusVectorStoreProperties.class, MilvusServiceClientProperties.class // 瑙f瀽 Milvus 閰嶇疆
+@EnableConfigurationProperties({
+ YudaoAiProperties.class,
+ QdrantVectorStoreProperties.class,
+ RedisVectorStoreProperties.class,
+ MilvusVectorStoreProperties.class,
+ MilvusServiceClientProperties.class
})
@Slf4j
public class AiAutoConfiguration {
+
+ private static final String DASHSCOPE_BASE_URL = "https://dashscope.aliyuncs.com/compatible-mode/v1";
@Bean
public AiModelFactory aiModelFactory() {
@@ -81,400 +51,46 @@
@Bean
@ConditionalOnMissingBean
public ObservationRegistry observationRegistry() {
- // 鐗规畩锛氬厹搴曟湁 ObservationRegistry Bean锛岄伩鍏嶇浉鍏崇殑 ChatModel 鍒涘缓鎶ラ敊銆傜浉鍏� issue锛歨ttps://t.zsxq.com/CuPu4
return ObservationRegistry.NOOP;
}
- // ========== 鍚勭 AI Client 鍒涘缓 ==========
-
@Bean
@ConditionalOnMissingBean
- @ConditionalOnProperty(value = "spring.ai.dashscope.api-key")
- public DashScopeChatModel dashScopeChatModel(@Value("${spring.ai.dashscope.api-key}") String apiKey,
- ToolCallingManager toolCallingManager,
- ObservationRegistry observationRegistry) {
- return buildTongYiChatModel(apiKey, toolCallingManager, observationRegistry);
- }
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(value = "spring.ai.dashscope.api-key")
- public DashScopeImageModel dashScopeImageModel(@Value("${spring.ai.dashscope.api-key}") String apiKey,
- ObservationRegistry observationRegistry) {
- return buildTongYiImagesModel(apiKey, observationRegistry);
- }
-
- @Bean
- @ConditionalOnMissingBean
- @ConditionalOnProperty(value = "spring.ai.dashscope.api-key")
- public DashScopeEmbeddingModel dashScopeEmbeddingModel(@Value("${spring.ai.dashscope.api-key}") String apiKey,
- ObservationRegistry observationRegistry) {
- return buildTongYiEmbeddingModel(apiKey, null, observationRegistry);
- }
-
- public static DashScopeChatModel buildTongYiChatModel(String apiKey) {
- return buildTongYiChatModel(apiKey, getToolCallingManager(), getObservationRegistry());
- }
-
- private static DashScopeChatModel buildTongYiChatModel(String apiKey, ToolCallingManager toolCallingManager,
- ObservationRegistry observationRegistry) {
- DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(apiKey).build();
- DashScopeChatOptions options = DashScopeChatOptions.builder()
- .model(DashScopeApi.DEFAULT_CHAT_MODEL)
- .temperature(0.7)
- .build();
- return new DashScopeChatModel(dashScopeApi, options, toolCallingManager, RetryUtils.DEFAULT_RETRY_TEMPLATE,
- observationRegistry);
- }
-
- public static DashScopeImageModel buildTongYiImagesModel(String apiKey) {
- return buildTongYiImagesModel(apiKey, getObservationRegistry());
- }
-
- private static DashScopeImageModel buildTongYiImagesModel(String apiKey, ObservationRegistry observationRegistry) {
- DashScopeImageApi dashScopeImageApi = DashScopeImageApi.builder().apiKey(apiKey).build();
- DashScopeImageOptions options = DashScopeImageOptions.builder()
- .model(DashScopeImageApi.DEFAULT_IMAGE_MODEL)
- .build();
- return new DashScopeImageModel(dashScopeImageApi, options, RetryUtils.DEFAULT_RETRY_TEMPLATE,
- observationRegistry);
- }
-
- public static DashScopeEmbeddingModel buildTongYiEmbeddingModel(String apiKey, String model) {
- return buildTongYiEmbeddingModel(apiKey, model, getObservationRegistry());
- }
-
- private static DashScopeEmbeddingModel buildTongYiEmbeddingModel(String apiKey, String model,
- ObservationRegistry observationRegistry) {
- DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(apiKey).build();
- DashScopeEmbeddingOptions options = DashScopeEmbeddingOptions.builder()
- .model(StrUtil.blankToDefault(model, DashScopeApi.DEFAULT_EMBEDDING_MODEL))
- .build();
- return new DashScopeEmbeddingModel(dashScopeApi, MetadataMode.EMBED, options, RetryUtils.DEFAULT_RETRY_TEMPLATE,
- observationRegistry);
- }
-
- @Bean
- @ConditionalOnProperty(value = "yudao.ai.doubao.enable", havingValue = "true")
- public DouBaoChatModel douBaoChatClient(YudaoAiProperties yudaoAiProperties) {
- YudaoAiProperties.DouBao properties = yudaoAiProperties.getDoubao();
- return buildDouBaoChatClient(properties);
- }
-
- public DouBaoChatModel buildDouBaoChatClient(YudaoAiProperties.DouBao properties) {
- if (StrUtil.isEmpty(properties.getModel())) {
- properties.setModel(DouBaoChatModel.MODEL_DEFAULT);
- }
- DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
- .deepSeekApi(DeepSeekApi.builder()
- .baseUrl(DouBaoChatModel.BASE_URL)
- .completionsPath(DouBaoChatModel.COMPLETE_PATH)
- .apiKey(properties.getApiKey())
- .build())
- .options(DeepSeekChatOptions.builder()
- .model(properties.getModel())
- .temperature(properties.getTemperature())
- .maxTokens(properties.getMaxTokens())
- .topP(properties.getTopP())
- .build())
- .build();
- return new DouBaoChatModel(openAiChatModel);
- }
-
- @Bean
- @ConditionalOnProperty(value = "yudao.ai.siliconflow.enable", havingValue = "true")
- public SiliconFlowChatModel siliconFlowChatClient(YudaoAiProperties yudaoAiProperties) {
- YudaoAiProperties.SiliconFlow properties = yudaoAiProperties.getSiliconflow();
- return buildSiliconFlowChatClient(properties);
- }
-
- public SiliconFlowChatModel buildSiliconFlowChatClient(YudaoAiProperties.SiliconFlow properties) {
- if (StrUtil.isEmpty(properties.getModel())) {
- properties.setModel(SiliconFlowApiConstants.MODEL_DEFAULT);
- }
- DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
- .deepSeekApi(DeepSeekApi.builder()
- .baseUrl(SiliconFlowApiConstants.DEFAULT_BASE_URL)
- .apiKey(properties.getApiKey())
- .build())
- .options(DeepSeekChatOptions.builder()
- .model(properties.getModel())
- .temperature(properties.getTemperature())
- .maxTokens(properties.getMaxTokens())
- .topP(properties.getTopP())
- .build())
- .build();
- return new SiliconFlowChatModel(openAiChatModel);
- }
-
- @Bean
- @ConditionalOnProperty(value = "yudao.ai.hunyuan.enable", havingValue = "true")
- public HunYuanChatModel hunYuanChatClient(YudaoAiProperties yudaoAiProperties) {
- YudaoAiProperties.HunYuan properties = yudaoAiProperties.getHunyuan();
- return buildHunYuanChatClient(properties);
- }
-
- public HunYuanChatModel buildHunYuanChatClient(YudaoAiProperties.HunYuan properties) {
- if (StrUtil.isEmpty(properties.getModel())) {
- properties.setModel(HunYuanChatModel.MODEL_DEFAULT);
- }
- if (StrUtil.isEmpty(properties.getBaseUrl())) {
- properties.setBaseUrl(HunYuanChatModel.BASE_URL);
- }
- // 鍒涘缓 DeepSeekChatModel銆丠unYuanChatModel 瀵硅薄
- DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
- .deepSeekApi(DeepSeekApi.builder()
- .baseUrl(properties.getBaseUrl())
- .completionsPath(HunYuanChatModel.COMPLETE_PATH)
- .apiKey(properties.getApiKey())
- .build())
- .options(DeepSeekChatOptions.builder()
- .model(properties.getModel())
- .temperature(properties.getTemperature())
- .maxTokens(properties.getMaxTokens())
- .topP(properties.getTopP())
- .build())
- .build();
- return new HunYuanChatModel(openAiChatModel);
- }
-
- @Bean
- @ConditionalOnProperty(value = "yudao.ai.xinghuo.enable", havingValue = "true")
- public XingHuoChatModel xingHuoChatClient(YudaoAiProperties yudaoAiProperties) {
- YudaoAiProperties.XingHuo properties = yudaoAiProperties.getXinghuo();
- return buildXingHuoChatClient(properties);
- }
-
- public XingHuoChatModel buildXingHuoChatClient(YudaoAiProperties.XingHuo properties) {
- if (StrUtil.isEmpty(properties.getModel())) {
- properties.setModel(XingHuoChatModel.MODEL_DEFAULT);
- }
- return XingHuoChatModel.builder()
- .apiKey(properties.getApiKey())
- .options(DeepSeekChatOptions.builder()
- .model(properties.getModel())
- .temperature(properties.getTemperature())
- .maxTokens(properties.getMaxTokens())
- .topP(properties.getTopP())
- .build())
- .build();
- }
-
- @Bean
- @ConditionalOnProperty(value = "yudao.ai.baichuan.enable", havingValue = "true")
- public BaiChuanChatModel baiChuanChatClient(YudaoAiProperties yudaoAiProperties) {
- YudaoAiProperties.BaiChuan properties = yudaoAiProperties.getBaichuan();
- return buildBaiChuanChatClient(properties);
- }
-
- public BaiChuanChatModel buildBaiChuanChatClient(YudaoAiProperties.BaiChuan properties) {
- if (StrUtil.isEmpty(properties.getModel())) {
- properties.setModel(BaiChuanChatModel.MODEL_DEFAULT);
- }
- DeepSeekChatModel deepSeekChatModel = DeepSeekChatModel.builder()
- .deepSeekApi(DeepSeekApi.builder()
- .baseUrl(BaiChuanChatModel.BASE_URL)
- .apiKey(properties.getApiKey())
- .completionsPath(BaiChuanChatModel.COMPLETE_PATH)
- .build())
- .options(DeepSeekChatOptions.builder()
- .model(properties.getModel())
- .temperature(properties.getTemperature())
- .maxTokens(properties.getMaxTokens())
- .topP(properties.getTopP())
- .build())
- .build();
- return new BaiChuanChatModel(deepSeekChatModel);
- }
-
- @Bean
- @ConditionalOnProperty(value = "yudao.ai.yiyan.enable", havingValue = "true")
- public YiYanChatModel yiYanChatClient(YudaoAiProperties yudaoAiProperties) {
- YudaoAiProperties.YiYan properties = yudaoAiProperties.getYiyan();
- return buildYiYanChatClient(properties);
- }
-
- public YiYanChatModel buildYiYanChatClient(YudaoAiProperties.YiYan properties) {
- if (StrUtil.isEmpty(properties.getModel())) {
- properties.setModel(YiYanChatModel.MODEL_DEFAULT);
- }
- return new YiYanChatModel(buildDeepSeekCompatibleChatModel(
- StrUtil.blankToDefault(properties.getBaseUrl(), YiYanChatModel.BASE_URL),
- null, properties.getApiKey(), properties.getModel(), properties.getTemperature(),
- properties.getMaxTokens(), properties.getTopP()));
- }
-
- @Bean
- @ConditionalOnProperty(value = "yudao.ai.zhipu.enable", havingValue = "true")
- public ZhiPuChatModel zhiPuChatClient(YudaoAiProperties yudaoAiProperties) {
- YudaoAiProperties.ZhiPu properties = yudaoAiProperties.getZhipu();
- return buildZhiPuChatClient(properties);
- }
-
- public ZhiPuChatModel buildZhiPuChatClient(YudaoAiProperties.ZhiPu properties) {
- if (StrUtil.isEmpty(properties.getModel())) {
- properties.setModel(ZhiPuChatModel.MODEL_DEFAULT);
- }
- DeepSeekChatModel deepSeekChatModel = DeepSeekChatModel.builder()
- .deepSeekApi(DeepSeekApi.builder()
- .baseUrl(StrUtil.blankToDefault(properties.getBaseUrl(), ZhiPuChatModel.BASE_URL))
- .apiKey(properties.getApiKey())
- .build())
- .options(DeepSeekChatOptions.builder()
- .model(properties.getModel())
- .temperature(properties.getTemperature())
- .maxTokens(properties.getMaxTokens())
- .topP(properties.getTopP())
- .build())
- .build();
- return new ZhiPuChatModel(deepSeekChatModel);
- }
-
- @Bean
- @ConditionalOnProperty(value = "yudao.ai.minimax.enable", havingValue = "true")
- public MiniMaxChatModel miniMaxChatClient(YudaoAiProperties yudaoAiProperties) {
- YudaoAiProperties.MiniMax properties = yudaoAiProperties.getMinimax();
- return buildMiniMaxChatClient(properties);
- }
-
- public MiniMaxChatModel buildMiniMaxChatClient(YudaoAiProperties.MiniMax properties) {
- if (StrUtil.isEmpty(properties.getModel())) {
- properties.setModel(MiniMaxChatModel.MODEL_DEFAULT);
- }
- return new MiniMaxChatModel(buildDeepSeekCompatibleChatModel(
- StrUtil.blankToDefault(properties.getBaseUrl(), MiniMaxChatModel.BASE_URL),
- null, properties.getApiKey(), properties.getModel(), properties.getTemperature(),
- properties.getMaxTokens(), properties.getTopP()));
- }
-
- @Bean
- @ConditionalOnProperty(value = "yudao.ai.moonshot.enable", havingValue = "true")
- public MoonshotChatModel moonshotChatClient(YudaoAiProperties yudaoAiProperties) {
- YudaoAiProperties.Moonshot properties = yudaoAiProperties.getMoonshot();
- return buildMoonshotChatClient(properties);
- }
-
- public MoonshotChatModel buildMoonshotChatClient(YudaoAiProperties.Moonshot properties) {
- if (StrUtil.isEmpty(properties.getModel())) {
- properties.setModel(MoonshotChatModel.MODEL_DEFAULT);
- }
- return new MoonshotChatModel(buildDeepSeekCompatibleChatModel(
- StrUtil.blankToDefault(properties.getBaseUrl(), MoonshotChatModel.BASE_URL),
- MoonshotChatModel.COMPLETE_PATH, properties.getApiKey(), properties.getModel(),
- properties.getTemperature(), properties.getMaxTokens(), properties.getTopP()));
- }
-
- @Bean
- @ConditionalOnProperty(value = "yudao.ai.stepfun.enable", havingValue = "true")
- public StepFunChatModel stepFunChatClient(YudaoAiProperties yudaoAiProperties) {
- YudaoAiProperties.StepFun properties = yudaoAiProperties.getStepfun();
- return buildStepFunChatClient(properties);
- }
-
- public StepFunChatModel buildStepFunChatClient(YudaoAiProperties.StepFun properties) {
- if (StrUtil.isEmpty(properties.getModel())) {
- properties.setModel(StepFunChatModel.MODEL_DEFAULT);
- }
- return new StepFunChatModel(buildDeepSeekCompatibleChatModel(
- StrUtil.blankToDefault(properties.getBaseUrl(), StepFunChatModel.BASE_URL),
- StepFunChatModel.COMPLETE_PATH, properties.getApiKey(), properties.getModel(),
- properties.getTemperature(), properties.getMaxTokens(), properties.getTopP()));
- }
-
- private static DeepSeekChatModel buildDeepSeekCompatibleChatModel(String baseUrl, String completionsPath,
- String apiKey, String model,
- Double temperature, Integer maxTokens,
- Double topP) {
- DeepSeekApi.Builder apiBuilder = DeepSeekApi.builder()
- .baseUrl(baseUrl)
- .apiKey(apiKey);
- if (StrUtil.isNotEmpty(completionsPath)) {
- apiBuilder.completionsPath(completionsPath);
- }
- return DeepSeekChatModel.builder()
- .deepSeekApi(apiBuilder.build())
- .options(DeepSeekChatOptions.builder()
- .model(model)
- .temperature(temperature)
- .maxTokens(maxTokens)
- .topP(topP)
- .build())
- .build();
- }
-
- @Bean
- @ConditionalOnProperty(value = "yudao.ai.midjourney.enable", havingValue = "true")
- public MidjourneyApi midjourneyApi(YudaoAiProperties yudaoAiProperties) {
- YudaoAiProperties.Midjourney config = yudaoAiProperties.getMidjourney();
- return new MidjourneyApi(config.getBaseUrl(), config.getApiKey(), config.getNotifyUrl());
- }
-
- @Bean
- @ConditionalOnProperty(value = "yudao.ai.suno.enable", havingValue = "true")
- public SunoApi sunoApi(YudaoAiProperties yudaoAiProperties) {
- return new SunoApi(yudaoAiProperties.getSuno().getBaseUrl());
- }
-
- @Bean
- @ConditionalOnProperty(value = "yudao.ai.grok.enable", havingValue = "true")
- public GrokChatModel grokChatClient(YudaoAiProperties yudaoAiProperties) {
- YudaoAiProperties.Grok properties = yudaoAiProperties.getGrok();
- return buildGrokChatClient(properties);
- }
-
- public GrokChatModel buildGrokChatClient(YudaoAiProperties.Grok properties) {
- if (StrUtil.isEmpty(properties.getModel())) {
- properties.setModel(GrokChatModel.MODEL_DEFAULT);
- }
- OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
- .options(OpenAiChatOptions.builder()
- .baseUrl(StrUtil.blankToDefault(properties.getBaseUrl(), GrokChatModel.BASE_URL))
- .apiKey(properties.getApiKey())
- .model(properties.getModel())
- .temperature(properties.getTemperature())
- .maxTokens(properties.getMaxTokens())
- .topP(properties.getTopP())
- .build())
- .build();
- return new GrokChatModel(openAiChatModel);
- }
-
- // ========== RAG 鐩稿叧 ==========
-
- @Bean
- public TokenCountEstimator tokenCountEstimator() {
- return new JTokkitTokenCountEstimator();
- }
-
- @Bean
public BatchingStrategy batchingStrategy() {
return new TokenCountBatchingStrategy();
}
- private static ToolCallingManager getToolCallingManager() {
- return SpringUtil.getBean(ToolCallingManager.class);
- }
-
- private static ObservationRegistry getObservationRegistry() {
- return SpringUtil.getBean(ObservationRegistry.class);
- }
-
- // ========== Web Search 鐩稿叧 ==========
-
@Bean
- @ConditionalOnProperty(value = "yudao.ai.web-search.enable", havingValue = "true")
- public AiWebSearchClient webSearchClient(YudaoAiProperties yudaoAiProperties) {
- return new AiBoChaWebSearchClient(yudaoAiProperties.getWebSearch().getApiKey());
+ @ConditionalOnMissingBean
+ public TokenCountEstimator tokenCountEstimator() {
+ return new JTokkitTokenCountEstimator();
}
- // ========== MCP 鐩稿叧 ==========
+ // ========== 閫氫箟鍗冮棶 Chat锛堥�氳繃 OpenAI 鍏煎鎺ュ彛锛�==========
- /**
- * 鍙傝�冭嚜 <a href="https://docs.spring.io/spring-ai/reference/api/mcp/mcp-client-boot-starter-docs.html">MCP Server Boot Starter</>
- */
- @Bean
- public List<ToolCallback> toolCallbacks(PersonService personService) {
- return List.of(ToolCallbacks.from(personService));
+ public static OpenAiChatModel buildTongYiChatModel(String apiKey, String model) {
+ return OpenAiChatModel.builder()
+ .options(OpenAiChatOptions.builder()
+ .baseUrl(DASHSCOPE_BASE_URL)
+ .apiKey(apiKey)
+ .model(StrUtil.blankToDefault(model, "qwen-plus"))
+ .temperature(0.7)
+ .build())
+ .toolCallingManager(ToolCallingManager.builder().build())
+ .build();
+ }
+
+ // ========== 閫氫箟鍗冮棶 Embedding锛堥�氳繃 OpenAI 鍏煎鎺ュ彛锛�==========
+
+ public static OpenAiEmbeddingModel buildTongYiEmbeddingModel(String apiKey, String model) {
+ return OpenAiEmbeddingModel.builder()
+ .options(OpenAiEmbeddingOptions.builder()
+ .baseUrl(DASHSCOPE_BASE_URL)
+ .apiKey(apiKey)
+ .model(StrUtil.blankToDefault(model, "text-embedding-v3"))
+ .build())
+ .metadataMode(MetadataMode.EMBED)
+ .build();
}
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/YudaoAiProperties.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/YudaoAiProperties.java
index 234fa2c..c225b1e 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/YudaoAiProperties.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/YudaoAiProperties.java
@@ -5,262 +5,17 @@
/**
* 鑺嬮亾 AI 閰嶇疆绫�
- *
- * @author fansili
- * @since 1.0
*/
@ConfigurationProperties(prefix = "yudao.ai")
@Data
public class YudaoAiProperties {
- /**
- * 瀛楄妭璞嗗寘
- */
- private DouBao doubao;
-
- /**
- * 鑵捐娣峰厓
- */
- private HunYuan hunyuan;
-
- /**
- * 纭呭熀娴佸姩
- */
- private SiliconFlow siliconflow;
-
- /**
- * 璁鏄熺伀
- */
- private XingHuo xinghuo;
-
- /**
- * 鐧惧窛
- */
- private BaiChuan baichuan;
-
- /**
- * 鏂囧績涓�瑷�
- */
- private YiYan yiyan;
-
- /**
- * 鏅鸿氨
- */
- private ZhiPu zhipu;
-
- /**
- * MiniMax
- */
- private MiniMax minimax;
-
- /**
- * 鏈堜箣鏆楅潰
- */
- private Moonshot moonshot;
-
- /**
- * 闃惰穬鏄熻景
- */
- private StepFun stepfun;
-
- /**
- * Grok
- */
- private Grok grok;
-
- /**
- * Midjourney 缁樺浘
- */
- private Midjourney midjourney;
-
- /**
- * Suno 闊充箰
- */
- @SuppressWarnings("SpellCheckingInspection")
- private Suno suno;
-
- /**
- * 缃戠粶鎼滅储
- */
private WebSearch webSearch;
@Data
- public static class DouBao {
-
- private String enable;
- private String apiKey;
-
- private String model;
- private Double temperature;
- private Integer maxTokens;
- private Double topP;
-
- }
-
- @Data
- public static class HunYuan {
-
- private String enable;
- private String baseUrl;
- private String apiKey;
-
- private String model;
- private Double temperature;
- private Integer maxTokens;
- private Double topP;
-
- }
-
- @Data
- public static class SiliconFlow {
-
- private String enable;
- private String apiKey;
-
- private String model;
- private Double temperature;
- private Integer maxTokens;
- private Double topP;
-
- }
-
- @Data
- public static class XingHuo {
-
- private String enable;
- private String apiKey;
-
- private String model;
- private Double temperature;
- private Integer maxTokens;
- private Double topP;
-
- }
-
- @Data
- public static class BaiChuan {
-
- private String enable;
- private String apiKey;
-
- private String model;
- private Double temperature;
- private Integer maxTokens;
- private Double topP;
-
- }
-
- @Data
- public static class YiYan {
-
- private String enable;
- private String baseUrl;
- private String apiKey;
-
- private String model;
- private Double temperature;
- private Integer maxTokens;
- private Double topP;
-
- }
-
- @Data
- public static class ZhiPu {
-
- private String enable;
- private String baseUrl;
- private String apiKey;
-
- private String model;
- private Double temperature;
- private Integer maxTokens;
- private Double topP;
-
- }
-
- @Data
- public static class MiniMax {
-
- private String enable;
- private String baseUrl;
- private String apiKey;
-
- private String model;
- private Double temperature;
- private Integer maxTokens;
- private Double topP;
-
- }
-
- @Data
- public static class Moonshot {
-
- private String enable;
- private String baseUrl;
- private String apiKey;
-
- private String model;
- private Double temperature;
- private Integer maxTokens;
- private Double topP;
-
- }
-
- @Data
- public static class Midjourney {
-
- private String enable;
- private String baseUrl;
-
- private String apiKey;
- private String notifyUrl;
-
- }
-
- @Data
- public static class Suno {
-
- private boolean enable;
-
- private String baseUrl;
-
- }
-
- @Data
- public static class Grok {
-
- private String enable;
- private String apiKey;
- private String baseUrl;
-
- private String model;
- private Double temperature;
- private Integer maxTokens;
- private Double topP;
-
- }
-
- @Data
- public static class StepFun {
-
- private String enable;
- private String apiKey;
- private String baseUrl;
-
- private String model;
- private Double temperature;
- private Integer maxTokens;
- private Double topP;
-
- }
-
- @Data
public static class WebSearch {
-
private boolean enable;
-
private String apiKey;
-
}
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/AiModelFactory.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/AiModelFactory.java
index 1c0b808..8ae05c4 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/AiModelFactory.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/AiModelFactory.java
@@ -1,111 +1,22 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model;
import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.embedding.EmbeddingModel;
-import org.springframework.ai.image.ImageModel;
import org.springframework.ai.vectorstore.VectorStore;
-
import java.util.Map;
/**
* AI Model 妯″瀷宸ュ巶鐨勬帴鍙g被
- *
- * @author fansili
*/
public interface AiModelFactory {
- /**
- * 鍩轰簬鎸囧畾閰嶇疆锛岃幏寰� ChatModel 瀵硅薄
- *
- * 濡傛灉涓嶅瓨鍦紝鍒欒繘琛屽垱寤�
- *
- * @param platform 骞冲彴
- * @param apiKey API KEY
- * @param url API URL
- * @return ChatModel 瀵硅薄
- */
- ChatModel getOrCreateChatModel(AiPlatformEnum platform, String apiKey, String url);
+ ChatModel getOrCreateChatModel(AiPlatformEnum platform, String apiKey, String url, String model);
- /**
- * 鍩轰簬榛樿閰嶇疆锛岃幏寰� ChatModel 瀵硅薄
- *
- * 榛樿閰嶇疆锛屾寚鐨勬槸鍦� application.yaml 閰嶇疆鏂囦欢涓殑 spring.ai 鐩稿叧鐨勯厤缃�
- *
- * @param platform 骞冲彴
- * @return ChatModel 瀵硅薄
- */
ChatModel getDefaultChatModel(AiPlatformEnum platform);
- /**
- * 鍩轰簬榛樿閰嶇疆锛岃幏寰� ImageModel 瀵硅薄
- *
- * 榛樿閰嶇疆锛屾寚鐨勬槸鍦� application.yaml 閰嶇疆鏂囦欢涓殑 spring.ai 鐩稿叧鐨勯厤缃�
- *
- * @param platform 骞冲彴
- * @return ImageModel 瀵硅薄
- */
- ImageModel getDefaultImageModel(AiPlatformEnum platform);
-
- /**
- * 鍩轰簬鎸囧畾閰嶇疆锛岃幏寰� ImageModel 瀵硅薄
- *
- * 濡傛灉涓嶅瓨鍦紝鍒欒繘琛屽垱寤�
- *
- * @param platform 骞冲彴
- * @param apiKey API KEY
- * @param url API URL
- * @return ImageModel 瀵硅薄
- */
- ImageModel getOrCreateImageModel(AiPlatformEnum platform, String apiKey, String url);
-
- /**
- * 鍩轰簬鎸囧畾閰嶇疆锛岃幏寰� MidjourneyApi 瀵硅薄
- *
- * 濡傛灉涓嶅瓨鍦紝鍒欒繘琛屽垱寤�
- *
- * @param apiKey API KEY
- * @param url API URL
- * @return MidjourneyApi 瀵硅薄
- */
- MidjourneyApi getOrCreateMidjourneyApi(String apiKey, String url);
-
- /**
- * 鍩轰簬鎸囧畾閰嶇疆锛岃幏寰� SunoApi 瀵硅薄
- *
- * 濡傛灉涓嶅瓨鍦紝鍒欒繘琛屽垱寤�
- *
- * @param apiKey API KEY
- * @param url API URL
- * @return SunoApi 瀵硅薄
- */
- SunoApi getOrCreateSunoApi(String apiKey, String url);
-
- /**
- * 鍩轰簬鎸囧畾閰嶇疆锛岃幏寰� EmbeddingModel 瀵硅薄
- *
- * 濡傛灉涓嶅瓨鍦紝鍒欒繘琛屽垱寤�
- *
- * @param platform 骞冲彴
- * @param apiKey API KEY
- * @param url API URL
- * @param model 妯″瀷
- * @return ChatModel 瀵硅薄
- */
EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url, String model);
- /**
- * 鍩轰簬鎸囧畾閰嶇疆锛岃幏寰� VectorStore 瀵硅薄
- *
- * 濡傛灉涓嶅瓨鍦紝鍒欒繘琛屽垱寤�
- *
- * @param type 鍚戦噺瀛樺偍绫诲瀷
- * @param embeddingModel 鍚戦噺妯″瀷
- * @param metadataFields 鍏冩暟鎹瓧娈�
- * @return VectorStore 瀵硅薄
- */
VectorStore getOrCreateVectorStore(Class<? extends VectorStore> type,
EmbeddingModel embeddingModel,
Map<String, Class<?>> metadataFields);
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/baichuan/BaiChuanChatModel.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/baichuan/BaiChuanChatModel.java
deleted file mode 100644
index 3ea5ecc..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/baichuan/BaiChuanChatModel.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan;
-
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.ai.chat.model.ChatModel;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.ChatOptions;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.deepseek.DeepSeekChatModel;
-import reactor.core.publisher.Flux;
-
-/**
- * 鐧惧窛 {@link ChatModel} 瀹炵幇绫�
- *
- * @author 鑺嬮亾婧愮爜
- */
-@Slf4j
-@RequiredArgsConstructor
-public class BaiChuanChatModel implements ChatModel {
-
- public static final String BASE_URL = "https://api.baichuan-ai.com";
-
- public static final String COMPLETE_PATH = "/v1/chat/completions";
-
- public static final String MODEL_DEFAULT = "Baichuan-M3";
-
- /**
- * 鍏煎 OpenAI 鎺ュ彛锛屽鐢� DeepSeek 瀹㈡埛绔�
- */
- private final DeepSeekChatModel deepSeekChatModel;
-
- @Override
- public ChatResponse call(Prompt prompt) {
- return deepSeekChatModel.call(prompt);
- }
-
- @Override
- public Flux<ChatResponse> stream(Prompt prompt) {
- return deepSeekChatModel.stream(prompt);
- }
-
- @Override
- public ChatOptions getOptions() {
- return deepSeekChatModel.getOptions();
- }
-
- @Override
- @Deprecated(forRemoval = true)
- @SuppressWarnings("removal")
- public ChatOptions getDefaultOptions() {
- return getOptions();
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/doubao/DouBaoChatModel.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/doubao/DouBaoChatModel.java
deleted file mode 100644
index 3caedc9..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/doubao/DouBaoChatModel.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao;
-
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.ai.chat.model.ChatModel;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.ChatOptions;
-import org.springframework.ai.chat.prompt.Prompt;
-import reactor.core.publisher.Flux;
-
-/**
- * 瀛楄妭璞嗗寘 {@link ChatModel} 瀹炵幇绫�
- *
- * @author fansili
- */
-@Slf4j
-@RequiredArgsConstructor
-public class DouBaoChatModel implements ChatModel {
-
- public static final String BASE_URL = "https://ark.cn-beijing.volces.com/api";
- public static final String COMPLETE_PATH = "/v3/chat/completions";
-
- public static final String MODEL_DEFAULT = "doubao-seed-2-1-turbo-260628";
-
- /**
- * 鍏煎 OpenAI 鎺ュ彛锛岃繘琛屽鐢�
- */
- private final ChatModel openAiChatModel;
-
- @Override
- public ChatResponse call(Prompt prompt) {
- return openAiChatModel.call(prompt);
- }
-
- @Override
- public Flux<ChatResponse> stream(Prompt prompt) {
- return openAiChatModel.stream(prompt);
- }
-
- @Override
- public ChatOptions getOptions() {
- return openAiChatModel.getOptions();
- }
-
- @Override
- @Deprecated(forRemoval = true)
- @SuppressWarnings("removal")
- public ChatOptions getDefaultOptions() {
- return getOptions();
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/grok/GrokChatModel.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/grok/GrokChatModel.java
deleted file mode 100644
index 7c9215a..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/grok/GrokChatModel.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.grok;
-
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.ai.chat.model.ChatModel;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.ChatOptions;
-import org.springframework.ai.chat.prompt.Prompt;
-import reactor.core.publisher.Flux;
-
-/**
- * Grok {@link ChatModel} 瀹炵幇绫�
- *
- *
- */
-@Slf4j
-@RequiredArgsConstructor
-public class GrokChatModel implements ChatModel {
-
- public static final String BASE_URL = "https://api.x.ai/v1";
- public static final String COMPLETE_PATH = "/v1/chat/completions";
- public static final String MODEL_DEFAULT = "grok-4.3";
-
- /**
- * 鍏煎 OpenAI 鎺ュ彛锛岃繘琛屽鐢�
- */
- private final ChatModel openAiChatModel;
-
- @Override
- public ChatResponse call(Prompt prompt) {
- return openAiChatModel.call(prompt);
- }
-
- @Override
- public Flux<ChatResponse> stream(Prompt prompt) {
- return openAiChatModel.stream(prompt);
- }
-
- @Override
- public ChatOptions getOptions() {
- return openAiChatModel.getOptions();
- }
-
- @Override
- @Deprecated(forRemoval = true)
- @SuppressWarnings("removal")
- public ChatOptions getDefaultOptions() {
- return getOptions();
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/hunyuan/HunYuanChatModel.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/hunyuan/HunYuanChatModel.java
deleted file mode 100644
index 0f3c3a9..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/hunyuan/HunYuanChatModel.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan;
-
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.ai.chat.model.ChatModel;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.ChatOptions;
-import org.springframework.ai.chat.prompt.Prompt;
-import reactor.core.publisher.Flux;
-
-/**
- * 鑵捐娣峰厓 {@link ChatModel} 瀹炵幇绫�
- *
- * 鍩轰簬 <a href="https://cloud.tencent.com/document/product/1823/132252">TokenHub OpenAI 鍏煎鎺ュ彛</a> 瀹炵幇
- *
- * @author fansili
- */
-@Slf4j
-@RequiredArgsConstructor
-public class HunYuanChatModel implements ChatModel {
-
- public static final String BASE_URL = "https://tokenhub.tencentmaas.com";
- public static final String COMPLETE_PATH = "/v1/chat/completions";
-
- public static final String MODEL_DEFAULT = "hy3-preview";
-
- /**
- * 鍏煎 OpenAI 鎺ュ彛锛岃繘琛屽鐢�
- */
- private final ChatModel openAiChatModel;
-
- @Override
- public ChatResponse call(Prompt prompt) {
- return openAiChatModel.call(prompt);
- }
-
- @Override
- public Flux<ChatResponse> stream(Prompt prompt) {
- return openAiChatModel.stream(prompt);
- }
-
- @Override
- public ChatOptions getOptions() {
- return openAiChatModel.getOptions();
- }
-
- @Override
- @Deprecated(forRemoval = true)
- @SuppressWarnings("removal")
- public ChatOptions getDefaultOptions() {
- return getOptions();
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/midjourney/api/MidjourneyApi.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/midjourney/api/MidjourneyApi.java
deleted file mode 100644
index 051ef31..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/midjourney/api/MidjourneyApi.java
+++ /dev/null
@@ -1,351 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api;
-
-import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Lists;
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import lombok.Getter;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.http.HttpRequest;
-import org.springframework.http.HttpStatusCode;
-import org.springframework.http.MediaType;
-import org.springframework.web.reactive.function.client.ClientResponse;
-import org.springframework.web.reactive.function.client.WebClient;
-import reactor.core.publisher.Mono;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Function;
-import java.util.function.Predicate;
-
-/**
- * Midjourney API
- *
- * @author fansili
- * @since 1.0
- */
-@Slf4j
-public class MidjourneyApi {
-
- private final Predicate<HttpStatusCode> STATUS_PREDICATE = status -> !status.is2xxSuccessful();
-
- private final Function<Object, Function<ClientResponse, Mono<? extends Throwable>>> EXCEPTION_FUNCTION =
- reqParam -> response -> response.bodyToMono(String.class).handle((responseBody, sink) -> {
- HttpRequest request = response.request();
- log.error("[midjourney-api] 璋冪敤澶辫触锛佽姹傛柟寮�:[{}]锛岃姹傚湴鍧�:[{}]锛岃姹傚弬鏁�:[{}]锛屽搷搴旀暟鎹�: [{}]",
- request.getMethod(), request.getURI(), reqParam, responseBody);
- sink.error(new IllegalStateException("[midjourney-api] 璋冪敤澶辫触锛�"));
- });
-
- private final WebClient webClient;
-
- /**
- * 鍥炶皟鍦板潃
- */
- private final String notifyUrl;
-
- public MidjourneyApi(String baseUrl, String apiKey, String notifyUrl) {
- this.webClient = WebClient.builder()
- .baseUrl(baseUrl)
- .defaultHeaders(httpHeaders -> {
- httpHeaders.setContentType(MediaType.APPLICATION_JSON);
- httpHeaders.setBearerAuth(apiKey);
- })
- .build();
- this.notifyUrl = notifyUrl;
- }
-
- /**
- * imagine - 鏍规嵁鎻愮ず璇嶆彁浜ょ粯鐢讳换鍔�
- *
- * @param request 璇锋眰
- * @return 鎻愪氦缁撴灉
- */
- public SubmitResponse imagine(ImagineRequest request) {
- if (StrUtil.isEmpty(request.getNotifyHook())) {
- request.setNotifyHook(notifyUrl);
- }
- String response = post("/submit/imagine", request);
- return JsonUtils.parseObject(response, SubmitResponse.class);
- }
-
- /**
- * action - 鏀惧ぇ銆佺缉灏忋�乁1銆乁2...
- *
- * @param request 璇锋眰
- * @return 鎻愪氦缁撴灉
- */
- public SubmitResponse action(ActionRequest request) {
- if (StrUtil.isEmpty(request.getNotifyHook())) {
- request.setNotifyHook(notifyUrl);
- }
- String response = post("/submit/action", request);
- return JsonUtils.parseObject(response, SubmitResponse.class);
- }
-
- /**
- * 鎵归噺鏌ヨ task 浠诲姟
- *
- * @param ids 浠诲姟缂栧彿鏁扮粍
- * @return task 浠诲姟
- */
- public List<Notify> getTaskList(Collection<String> ids) {
- String res = post("/task/list-by-condition", ImmutableMap.of("ids", ids));
- return JsonUtils.parseArray(res, Notify.class);
- }
-
- private String post(String uri, Object body) {
- return webClient.post()
- .uri(uri)
- .body(Mono.just(JsonUtils.toJsonString(body)), String.class)
- .retrieve()
- .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(body))
- .bodyToMono(String.class)
- .block();
- }
-
- // ========== record 缁撴瀯 ==========
-
- /**
- * Imagine 璇锋眰锛堢敓鎴愬浘鐗囷級
- */
- @Data
- public static final class ImagineRequest {
-
- /**
- * 鍨浘(鍙傝�冨浘) base64 鏁扮粍
- */
- private List<String> base64Array;
- /**
- * 鎻愮ず璇�
- */
- private String prompt;
- /**
- * 閫氱煡鍦板潃
- */
- private String notifyHook;
- /**
- * 鑷畾涔夊弬鏁�
- */
- private String state;
-
- public ImagineRequest(List<String> base64Array, String prompt, String notifyHook, String state) {
- this.base64Array = base64Array;
- this.prompt = prompt;
- this.notifyHook = notifyHook;
- this.state = state;
- }
-
- public static String buildState(Integer width, Integer height, String version, String model) {
- StringBuilder params = new StringBuilder();
- // --ar 鏉ヨ缃昂瀵�
- params.append(String.format(" --ar %s:%s ", width, height));
- // --niji 妯″瀷
- if (ModelEnum.NIJI.getModel().equals(model)) {
- params.append(String.format(" --niji %s ", version));
- } else {
- params.append(String.format(" --v %s ", version));
- }
- return params.toString();
- }
-
- }
-
- /**
- * Action 璇锋眰
- */
- @Data
- public static final class ActionRequest {
-
- private String customId;
- private String taskId;
- private String notifyHook;
-
- public ActionRequest(String taskId, String customId, String notifyHook) {
- this.customId = customId;
- this.taskId = taskId;
- this.notifyHook = notifyHook;
- }
-
- }
-
- /**
- * Submit 缁熶竴杩斿洖
- *
- * @param code 鐘舵�佺爜: 1(鎻愪氦鎴愬姛), 21(宸插瓨鍦�), 22(鎺掗槦涓�), other(閿欒)
- * @param description 鎻忚堪
- * @param properties 鎵╁睍瀛楁
- * @param result 浠诲姟ID
- */
- public record SubmitResponse(String code,
- String description,
- Map<String, Object> properties,
- String result) {
- }
-
- /**
- * 閫氱煡 request
- *
- * @param id job id
- * @param action 浠诲姟绫诲瀷 {@link TaskActionEnum}
- * @param status 浠诲姟鐘舵�� {@link TaskStatusEnum}
- * @param prompt 鎻愮ず璇�
- * @param promptEn 鎻愮ず璇�-鑻辨枃
- * @param description 浠诲姟鎻忚堪
- * @param state 鑷畾涔夊弬鏁�
- * @param submitTime 鎻愪氦鏃堕棿
- * @param startTime 寮�濮嬫墽琛屾椂闂�
- * @param finishTime 缁撴潫鏃堕棿
- * @param imageUrl 鍥剧墖url
- * @param progress 浠诲姟杩涘害
- * @param failReason 澶辫触鍘熷洜
- * @param buttons 浠诲姟瀹屾垚鍚庣殑鍙墽琛屾寜閽�
- */
- public record Notify(String id,
- String action,
- String status,
-
- String prompt,
- String promptEn,
-
- String description,
- String state,
-
- Long submitTime,
- Long startTime,
- Long finishTime,
-
- String imageUrl,
- String progress,
- String failReason,
- List<Button> buttons) {
-
- }
-
- /**
- * button
- *
- * @param customId MJ::JOB::upsample::1::85a4b4c1-8835-46c5-a15c-aea34fad1862 鍔ㄤ綔鏍囪瘑
- * @param emoji 鍥炬爣 emoji
- * @param label Make Variations 鏂囨湰
- * @param type 绫诲瀷锛岀郴缁熷唴閮ㄤ娇鐢�
- * @param style 鏍峰紡: 2锛圥rimary锛夈��3锛圙reen锛�
- */
- public record Button(String customId,
- String emoji,
- String label,
- String type,
- String style) {
- }
-
- // ============ enums ============
-
- /**
- * 妯″瀷鏋氫妇
- */
- @AllArgsConstructor
- @Getter
- public enum ModelEnum {
-
- MIDJOURNEY("midjourney", "midjourney"),
- NIJI("niji", "niji"),
- ;
-
- private final String model;
- private final String name;
-
- }
-
- /**
- * 鎻愪氦杩斿洖鐨勭姸鎬佺爜鐨勬灇涓�
- */
- @Getter
- @AllArgsConstructor
- public enum SubmitCodeEnum {
-
- SUBMIT_SUCCESS("1", "鎻愪氦鎴愬姛"),
- ALREADY_EXISTS("21", "宸插瓨鍦�"),
- QUEUING("22", "鎺掗槦涓�"),
- ;
-
- public static final List<String> SUCCESS_CODES = Lists.newArrayList(
- SUBMIT_SUCCESS.code,
- ALREADY_EXISTS.code,
- QUEUING.code
- );
-
- private final String code;
- private final String name;
-
- }
-
- /**
- * Action 鏋氫妇
- */
- @Getter
- @AllArgsConstructor
- public enum TaskActionEnum {
-
- /**
- * 鐢熸垚鍥剧墖
- */
- IMAGINE,
- /**
- * 閫変腑鏀惧ぇ
- */
- UPSCALE,
- /**
- * 閫変腑鍏朵腑鐨勪竴寮犲浘锛岀敓鎴愬洓寮犵浉浼肩殑
- */
- VARIATION,
- /**
- * 閲嶆柊鎵ц
- */
- REROLL,
- /**
- * 鍥捐浆 prompt
- */
- DESCRIBE,
- /**
- * 澶氬浘娣峰悎
- */
- BLEND
-
- }
-
- /**
- * 浠诲姟鐘舵�佹灇涓�
- */
- @Getter
- @AllArgsConstructor
- public enum TaskStatusEnum {
-
- /**
- * 鏈惎鍔�
- */
- NOT_START(0),
- /**
- * 宸叉彁浜�
- */
- SUBMITTED(1),
- /**
- * 鎵ц涓�
- */
- IN_PROGRESS(3),
- /**
- * 澶辫触
- */
- FAILURE(4),
- /**
- * 鎴愬姛
- */
- SUCCESS(4);
-
- private final int order;
-
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/minimax/MiniMaxChatModel.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/minimax/MiniMaxChatModel.java
deleted file mode 100644
index d464ab8..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/minimax/MiniMaxChatModel.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.minimax;
-
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.ai.chat.model.ChatModel;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.ChatOptions;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.deepseek.DeepSeekChatModel;
-import reactor.core.publisher.Flux;
-
-/**
- * MiniMax {@link ChatModel} 瀹炵幇绫�
- *
- * @author 鑺嬮亾婧愮爜
- */
-@Slf4j
-@RequiredArgsConstructor
-public class MiniMaxChatModel implements ChatModel {
-
- public static final String BASE_URL = "https://api.minimaxi.com/v1";
-
- public static final String MODEL_DEFAULT = "MiniMax-M3";
-
- /**
- * 鍏煎 OpenAI 鎺ュ彛锛屽鐢� DeepSeek 瀹㈡埛绔�
- */
- private final DeepSeekChatModel deepSeekChatModel;
-
- @Override
- public ChatResponse call(Prompt prompt) {
- return deepSeekChatModel.call(prompt);
- }
-
- @Override
- public Flux<ChatResponse> stream(Prompt prompt) {
- return deepSeekChatModel.stream(prompt);
- }
-
- @Override
- public ChatOptions getOptions() {
- return deepSeekChatModel.getOptions();
- }
-
- @Override
- @Deprecated(forRemoval = true)
- @SuppressWarnings("removal")
- public ChatOptions getDefaultOptions() {
- return getOptions();
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/moonshot/MoonshotChatModel.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/moonshot/MoonshotChatModel.java
deleted file mode 100644
index 391cec5..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/moonshot/MoonshotChatModel.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.moonshot;
-
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.ai.chat.model.ChatModel;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.ChatOptions;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.deepseek.DeepSeekChatModel;
-import reactor.core.publisher.Flux;
-
-/**
- * 鏈堜箣鏆楅潰 {@link ChatModel} 瀹炵幇绫�
- *
- * @author 鑺嬮亾婧愮爜
- */
-@Slf4j
-@RequiredArgsConstructor
-public class MoonshotChatModel implements ChatModel {
-
- public static final String BASE_URL = "https://api.moonshot.cn";
-
- public static final String COMPLETE_PATH = "/v1/chat/completions";
-
- public static final String MODEL_DEFAULT = "kimi-k2.6";
-
- /**
- * 鍏煎 OpenAI 鎺ュ彛锛屽鐢� DeepSeek 瀹㈡埛绔�
- */
- private final DeepSeekChatModel deepSeekChatModel;
-
- @Override
- public ChatResponse call(Prompt prompt) {
- return deepSeekChatModel.call(prompt);
- }
-
- @Override
- public Flux<ChatResponse> stream(Prompt prompt) {
- return deepSeekChatModel.stream(prompt);
- }
-
- @Override
- public ChatOptions getOptions() {
- return deepSeekChatModel.getOptions();
- }
-
- @Override
- @Deprecated(forRemoval = true)
- @SuppressWarnings("removal")
- public ChatOptions getDefaultOptions() {
- return getOptions();
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowApiConstants.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowApiConstants.java
deleted file mode 100644
index 6016ba3..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowApiConstants.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2023-2024 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow;
-
-/**
- * SiliconFlow API 鏋氫妇绫�
- *
- * @author zzt
- */
-public final class SiliconFlowApiConstants {
-
- public static final String DEFAULT_BASE_URL = "https://api.siliconflow.cn";
-
- public static final String MODEL_DEFAULT = "deepseek-ai/DeepSeek-V4-Pro";
-
- public static final String DEFAULT_IMAGE_MODEL = "Kwai-Kolors/Kolors";
-
- public static final String PROVIDER_NAME = "Siiconflow";
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowChatModel.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowChatModel.java
deleted file mode 100644
index 9d66f25..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowChatModel.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow;
-
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.ai.chat.model.ChatModel;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.ChatOptions;
-import org.springframework.ai.chat.prompt.Prompt;
-import reactor.core.publisher.Flux;
-
-/**
- * 纭呭熀娴佸姩 {@link ChatModel} 瀹炵幇绫�
- *
- * 1. API 鏂囨。锛�<a href="https://docs.siliconflow.cn/cn/api-reference/chat-completions/chat-completions">API 鏂囨。</a>
- *
- * @author fansili
- */
-@Slf4j
-@RequiredArgsConstructor
-public class SiliconFlowChatModel implements ChatModel {
-
- /**
- * 鍏煎 OpenAI 鎺ュ彛锛岃繘琛屽鐢�
- */
- private final ChatModel openAiChatModel;
-
- @Override
- public ChatResponse call(Prompt prompt) {
- return openAiChatModel.call(prompt);
- }
-
- @Override
- public Flux<ChatResponse> stream(Prompt prompt) {
- return openAiChatModel.stream(prompt);
- }
-
- @Override
- public ChatOptions getOptions() {
- return openAiChatModel.getOptions();
- }
-
- @Override
- @Deprecated(forRemoval = true)
- @SuppressWarnings("removal")
- public ChatOptions getDefaultOptions() {
- return getOptions();
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageApi.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageApi.java
deleted file mode 100644
index 006658e..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageApi.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright 2023-2024 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow;
-
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import org.springframework.ai.model.ApiKey;
-import org.springframework.ai.model.NoopApiKey;
-import org.springframework.ai.model.SimpleApiKey;
-import org.springframework.ai.retry.RetryUtils;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.MediaType;
-import org.springframework.http.ResponseEntity;
-import org.springframework.util.Assert;
-import org.springframework.web.client.ResponseErrorHandler;
-import org.springframework.web.client.RestClient;
-
-/**
- * 纭呭熀娴佸姩 Image API
- *
- * @see <a href= "https://docs.siliconflow.cn/cn/api-reference/images/images-generations">Images</a>
- *
- * @author zzt
- */
-public class SiliconFlowImageApi {
-
- private final RestClient restClient;
-
- public SiliconFlowImageApi(String aiToken) {
- this(SiliconFlowApiConstants.DEFAULT_BASE_URL, aiToken, RestClient.builder());
- }
-
- public SiliconFlowImageApi(String baseUrl, String openAiToken) {
- this(baseUrl, openAiToken, RestClient.builder());
- }
-
- public SiliconFlowImageApi(String baseUrl, String openAiToken, RestClient.Builder restClientBuilder) {
- this(baseUrl, openAiToken, restClientBuilder, RetryUtils.DEFAULT_RESPONSE_ERROR_HANDLER);
- }
-
- public SiliconFlowImageApi(String baseUrl, String apiKey, RestClient.Builder restClientBuilder,
- ResponseErrorHandler responseErrorHandler) {
- this(baseUrl, apiKey, new HttpHeaders(), restClientBuilder, responseErrorHandler);
- }
-
- public SiliconFlowImageApi(String baseUrl, String apiKey, HttpHeaders headers,
- RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) {
- this(baseUrl, new SimpleApiKey(apiKey), headers, restClientBuilder, responseErrorHandler);
- }
-
- public SiliconFlowImageApi(String baseUrl, ApiKey apiKey, HttpHeaders headers,
- RestClient.Builder restClientBuilder, ResponseErrorHandler responseErrorHandler) {
-
- // @formatter:off
- this.restClient = restClientBuilder.baseUrl(baseUrl)
- .defaultHeaders(h -> {
- if(!(apiKey instanceof NoopApiKey)) {
- h.setBearerAuth(apiKey.getValue());
- }
- h.setContentType(MediaType.APPLICATION_JSON);
- h.addAll(headers);
- })
- .defaultStatusHandler(responseErrorHandler)
- .build();
- // @formatter:on
- }
-
- public ResponseEntity<SiliconFlowImageResponse> createImage(SiliconflowImageRequest siliconflowImageRequest) {
- Assert.notNull(siliconflowImageRequest, "Image request cannot be null.");
- Assert.hasLength(siliconflowImageRequest.prompt(), "Prompt cannot be empty.");
-
- return this.restClient.post()
- .uri("v1/images/generations")
- .body(siliconflowImageRequest)
- .retrieve()
- .toEntity(SiliconFlowImageResponse.class);
- }
-
-
- // @formatter:off
- @JsonInclude(JsonInclude.Include.NON_NULL)
- public record SiliconflowImageRequest (
- @JsonProperty("prompt") String prompt,
- @JsonProperty("model") String model,
- @JsonProperty("batch_size") Integer batchSize,
- @JsonProperty("negative_prompt") String negativePrompt,
- @JsonProperty("seed") Integer seed,
- @JsonProperty("num_inference_steps") Integer numInferenceSteps,
- @JsonProperty("guidance_scale") Float guidanceScale,
- @JsonProperty("image") String image) {
-
- public SiliconflowImageRequest(String prompt, String model) {
- this(prompt, model, null, null, null, null, null, null);
- }
- }
-
- public record SiliconFlowImageResponse(
- @JsonProperty("created") Long created,
- @JsonProperty("data") java.util.List<Entry> data) {
-
- public record Entry(
- @JsonProperty("url") String url,
- @JsonProperty("b64_json") String b64Json,
- @JsonProperty("revised_prompt") String revisedPrompt) {
- }
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java
deleted file mode 100644
index d6d5b7b..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright 2023-2024 the original author or authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * https://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow;
-
-import io.micrometer.observation.ObservationRegistry;
-import lombok.Setter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.ai.image.*;
-import org.springframework.ai.image.observation.DefaultImageModelObservationConvention;
-import org.springframework.ai.image.observation.ImageModelObservationContext;
-import org.springframework.ai.image.observation.ImageModelObservationConvention;
-import org.springframework.ai.image.observation.ImageModelObservationDocumentation;
-import org.springframework.ai.model.ModelOptionsUtils;
-import org.springframework.ai.openai.OpenAiImageModel;
-import org.springframework.ai.openai.metadata.OpenAiImageGenerationMetadata;
-import org.springframework.ai.retry.RetryUtils;
-import org.springframework.core.retry.RetryTemplate;
-import org.springframework.http.ResponseEntity;
-import org.jspecify.annotations.Nullable;
-import org.springframework.util.Assert;
-
-import java.util.List;
-
-/**
- * 纭呭熀娴佸姩 {@link ImageModel} 瀹炵幇绫�
- *
- * 鍙傝�� {@link OpenAiImageModel} 瀹炵幇
- *
- * @author zzt
- */
-public class SiliconFlowImageModel implements ImageModel {
-
- private static final Logger logger = LoggerFactory.getLogger(SiliconFlowImageModel.class);
-
- private static final ImageModelObservationConvention DEFAULT_OBSERVATION_CONVENTION = new DefaultImageModelObservationConvention();
-
- private final SiliconFlowImageOptions defaultOptions;
-
- private final RetryTemplate retryTemplate;
-
- private final SiliconFlowImageApi siliconFlowImageApi;
-
- private final ObservationRegistry observationRegistry;
-
- @Setter
- private ImageModelObservationConvention observationConvention = DEFAULT_OBSERVATION_CONVENTION;
-
- public SiliconFlowImageModel(SiliconFlowImageApi siliconFlowImageApi) {
- this(siliconFlowImageApi, SiliconFlowImageOptions.builder().build(), RetryUtils.DEFAULT_RETRY_TEMPLATE);
- }
-
- public SiliconFlowImageModel(SiliconFlowImageApi siliconFlowImageApi, SiliconFlowImageOptions options, RetryTemplate retryTemplate) {
- this(siliconFlowImageApi, options, retryTemplate, ObservationRegistry.NOOP);
- }
-
- public SiliconFlowImageModel(SiliconFlowImageApi siliconFlowImageApi, SiliconFlowImageOptions options, RetryTemplate retryTemplate,
- ObservationRegistry observationRegistry) {
- Assert.notNull(siliconFlowImageApi, "SiliconFlowImageApi must not be null");
- Assert.notNull(options, "options must not be null");
- Assert.notNull(retryTemplate, "retryTemplate must not be null");
- Assert.notNull(observationRegistry, "observationRegistry must not be null");
- this.siliconFlowImageApi = siliconFlowImageApi;
- this.defaultOptions = options;
- this.retryTemplate = retryTemplate;
- this.observationRegistry = observationRegistry;
- }
-
- @Override
- public ImageResponse call(ImagePrompt imagePrompt) {
- SiliconFlowImageOptions requestImageOptions = mergeOptions(imagePrompt.getOptions(), this.defaultOptions);
- SiliconFlowImageApi.SiliconflowImageRequest imageRequest = createRequest(imagePrompt, requestImageOptions);
-
- var observationContext = ImageModelObservationContext.builder()
- .imagePrompt(imagePrompt)
- .provider(SiliconFlowApiConstants.PROVIDER_NAME)
- .imagePrompt(imagePrompt)
- .build();
-
- return ImageModelObservationDocumentation.IMAGE_MODEL_OPERATION
- .observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext,
- this.observationRegistry)
- .observe(() -> {
- ResponseEntity<SiliconFlowImageApi.SiliconFlowImageResponse> imageResponseEntity = RetryUtils.execute(
- this.retryTemplate, () -> this.siliconFlowImageApi.createImage(imageRequest));
-
- ImageResponse imageResponse = convertResponse(imageResponseEntity, imageRequest);
-
- observationContext.setResponse(imageResponse);
-
- return imageResponse;
- });
- }
-
- private SiliconFlowImageApi.SiliconflowImageRequest createRequest(ImagePrompt imagePrompt,
- SiliconFlowImageOptions requestImageOptions) {
- String instructions = imagePrompt.getInstructions().getFirst().getText();
-
- return new SiliconFlowImageApi.SiliconflowImageRequest(
- instructions,
- ModelOptionsUtils.mergeOption(requestImageOptions.getModel(), SiliconFlowApiConstants.DEFAULT_IMAGE_MODEL),
- requestImageOptions.getN(),
- requestImageOptions.getNegativePrompt(),
- requestImageOptions.getSeed(),
- requestImageOptions.getNumInferenceSteps(),
- requestImageOptions.getGuidanceScale(),
- requestImageOptions.getImage());
- }
-
- private ImageResponse convertResponse(ResponseEntity<SiliconFlowImageApi.SiliconFlowImageResponse> imageResponseEntity,
- SiliconFlowImageApi.SiliconflowImageRequest siliconflowImageRequest) {
- SiliconFlowImageApi.SiliconFlowImageResponse imageApiResponse = imageResponseEntity.getBody();
- if (imageApiResponse == null) {
- logger.warn("No image response returned for request: {}", siliconflowImageRequest);
- return new ImageResponse(List.of());
- }
-
- List<ImageGeneration> imageGenerationList = imageApiResponse.data()
- .stream()
- .map(entry -> new ImageGeneration(new Image(entry.url(), entry.b64Json()),
- new OpenAiImageGenerationMetadata(entry.revisedPrompt())))
- .toList();
-
- ImageResponseMetadata openAiImageResponseMetadata = new ImageResponseMetadata(imageApiResponse.created());
- return new ImageResponse(imageGenerationList, openAiImageResponseMetadata);
- }
-
- private SiliconFlowImageOptions mergeOptions(@Nullable ImageOptions runtimeOptions, SiliconFlowImageOptions defaultOptions) {
- if (runtimeOptions == null) {
- return defaultOptions;
- }
- SiliconFlowImageOptions runtimeOptionsForProvider = runtimeOptions instanceof SiliconFlowImageOptions siliconFlowImageOptions
- ? siliconFlowImageOptions
- : SiliconFlowImageOptions.builder()
- .model(runtimeOptions.getModel())
- .batchSize(runtimeOptions.getN())
- .width(runtimeOptions.getWidth())
- .height(runtimeOptions.getHeight())
- .build();
-
- return SiliconFlowImageOptions.builder()
- // Handle portable image options
- .model(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getModel(), defaultOptions.getModel()))
- .batchSize(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getN(), defaultOptions.getN()))
- .width(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getWidth(), defaultOptions.getWidth()))
- .height(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getHeight(), defaultOptions.getHeight()))
- // Handle SiliconFlow specific image options
- .negativePrompt(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getNegativePrompt(), defaultOptions.getNegativePrompt()))
- .numInferenceSteps(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getNumInferenceSteps(), defaultOptions.getNumInferenceSteps()))
- .guidanceScale(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getGuidanceScale(), defaultOptions.getGuidanceScale()))
- .seed(ModelOptionsUtils.mergeOption(runtimeOptionsForProvider.getSeed(), defaultOptions.getSeed()))
- .build();
- }
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageOptions.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageOptions.java
deleted file mode 100644
index 6b8dd9f..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageOptions.java
+++ /dev/null
@@ -1,105 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import lombok.AllArgsConstructor;
-import lombok.Builder;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import org.springframework.ai.image.ImageOptions;
-
-/**
- * 纭呭熀娴佸姩 {@link ImageOptions}
- *
- * @author zzt
- */
-@Data
-@Builder
-@AllArgsConstructor
-@NoArgsConstructor
-public class SiliconFlowImageOptions implements ImageOptions {
-
- @JsonProperty("model")
- private String model;
-
- @JsonProperty("negative_prompt")
- private String negativePrompt;
-
- /**
- * The number of images to generate. Must be between 1 and 4.
- */
- @JsonProperty("image_size")
- private String imageSize;
-
- /**
- * The number of images to generate. Must be between 1 and 4.
- */
- @JsonProperty("batch_size")
- private Integer batchSize = 1;
-
- /**
- * number of inference steps
- */
- @JsonProperty("num_inference_steps")
- private Integer numInferenceSteps = 25;
-
- /**
- * This value is used to control the degree of match between the generated image and the given prompt. The higher the value, the more the generated image will tend to strictly match the text prompt. The lower the value, the more creative and diverse the generated image will be, potentially containing more unexpected elements.
- *
- * Required range: 0 <= x <= 20
- */
- @JsonProperty("guidance_scale")
- private Float guidanceScale = 0.75F;
-
- /**
- * 濡傛灉鎯宠姣忔閮界敓鎴愬浐瀹氱殑鍥剧墖锛屽彲浠ユ妸 seed 璁剧疆涓哄浐瀹氬��
- *
- */
- @JsonProperty("seed")
- private Integer seed = (int)(Math.random() * 1_000_000_000);
-
- /**
- * The image that needs to be uploaded should be converted into base64 format.
- */
- @JsonProperty("image")
- private String image;
-
- /**
- * 瀹�
- */
- private Integer width;
-
- /**
- * 楂�
- */
- private Integer height;
-
- public void setHeight(Integer height) {
- this.height = height;
- if (this.width != null && this.height != null) {
- this.imageSize = this.width + "x" + this.height;
- }
- }
-
- public void setWidth(Integer width) {
- this.width = width;
- if (this.width != null && this.height != null) {
- this.imageSize = this.width + "x" + this.height;
- }
- }
-
- @Override
- public Integer getN() {
- return batchSize;
- }
-
- @Override
- public String getResponseFormat() {
- return "url";
- }
-
- @Override
- public String getStyle() {
- return null;
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/stepfun/StepFunChatModel.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/stepfun/StepFunChatModel.java
deleted file mode 100644
index 6b115d8..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/stepfun/StepFunChatModel.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.stepfun;
-
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.ai.chat.model.ChatModel;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.ChatOptions;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.deepseek.DeepSeekChatModel;
-import reactor.core.publisher.Flux;
-
-/**
- * 闃惰穬鏄熻景 {@link ChatModel} 瀹炵幇绫�
- *
- * @author 鑺嬮亾婧愮爜
- */
-@Slf4j
-@RequiredArgsConstructor
-public class StepFunChatModel implements ChatModel {
-
- public static final String BASE_URL = "https://api.stepfun.com";
-
- public static final String COMPLETE_PATH = "/v1/chat/completions";
-
- public static final String MODEL_DEFAULT = "step-3.7-flash";
-
- /**
- * 鍏煎 OpenAI 鎺ュ彛锛屽鐢� DeepSeek 瀹㈡埛绔�
- */
- private final DeepSeekChatModel deepSeekChatModel;
-
- @Override
- public ChatResponse call(Prompt prompt) {
- return deepSeekChatModel.call(prompt);
- }
-
- @Override
- public Flux<ChatResponse> stream(Prompt prompt) {
- return deepSeekChatModel.stream(prompt);
- }
-
- @Override
- public ChatOptions getOptions() {
- return deepSeekChatModel.getOptions();
- }
-
- @Override
- @Deprecated(forRemoval = true)
- @SuppressWarnings("removal")
- public ChatOptions getDefaultOptions() {
- return getOptions();
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/suno/api/SunoApi.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/suno/api/SunoApi.java
deleted file mode 100644
index 87e685e..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/suno/api/SunoApi.java
+++ /dev/null
@@ -1,200 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.text.StrPool;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.core.ParameterizedTypeReference;
-import org.springframework.http.HttpRequest;
-import org.springframework.http.HttpStatusCode;
-import org.springframework.http.MediaType;
-import org.springframework.web.reactive.function.client.ClientResponse;
-import org.springframework.web.reactive.function.client.WebClient;
-import reactor.core.publisher.Mono;
-
-import java.util.List;
-import java.util.function.Function;
-import java.util.function.Predicate;
-
-/**
- * Suno API
- * <p>
- * 瀵规帴 Suno Proxy锛�<a href="https://github.com/gcui-art/suno-api">suno-api</a>
- *
- * @author xiaoxin
- */
-@Slf4j
-public class SunoApi {
-
- private final WebClient webClient;
-
- private final Predicate<HttpStatusCode> STATUS_PREDICATE = status -> !status.is2xxSuccessful();
-
- private final Function<Object, Function<ClientResponse, Mono<? extends Throwable>>> EXCEPTION_FUNCTION =
- reqParam -> response -> response.bodyToMono(String.class).handle((responseBody, sink) -> {
- HttpRequest request = response.request();
- log.error("[suno-api] 璋冪敤澶辫触锛佽姹傛柟寮�:[{}]锛岃姹傚湴鍧�:[{}]锛岃姹傚弬鏁�:[{}]锛屽搷搴旀暟鎹�: [{}]",
- request.getMethod(), request.getURI(), reqParam, responseBody);
- sink.error(new IllegalStateException("[suno-api] 璋冪敤澶辫触锛�"));
- });
-
- public SunoApi(String baseUrl) {
- this.webClient = WebClient.builder()
- .baseUrl(baseUrl)
- .defaultHeaders((headers) -> headers.setContentType(MediaType.APPLICATION_JSON))
- .build();
- }
-
- public List<MusicData> generate(MusicGenerateRequest request) {
- return this.webClient.post()
- .uri("/api/generate")
- .body(Mono.just(request), MusicGenerateRequest.class)
- .retrieve()
- .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
- .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() {
- })
- .block();
- }
-
- public List<MusicData> customGenerate(MusicGenerateRequest request) {
- return this.webClient.post()
- .uri("/api/custom_generate")
- .body(Mono.just(request), MusicGenerateRequest.class)
- .retrieve()
- .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
- .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() {
- })
- .block();
- }
-
- public LyricsData generateLyrics(String prompt) {
- return this.webClient.post()
- .uri("/api/generate_lyrics")
- .body(Mono.just(new MusicGenerateRequest(prompt)), MusicGenerateRequest.class)
- .retrieve()
- .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(prompt))
- .bodyToMono(LyricsData.class)
- .block();
- }
-
- public List<MusicData> getMusicList(List<String> ids) {
- return this.webClient.get()
- .uri(uriBuilder -> uriBuilder
- .path("/api/get")
- .queryParam("ids", CollUtil.join(ids, StrPool.COMMA))
- .build())
- .retrieve()
- .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(ids))
- .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() {
- })
- .block();
- }
-
- public LimitUsageData getLimitUsage() {
- return this.webClient.get()
- .uri("/api/get_limit")
- .retrieve()
- .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(null))
- .bodyToMono(LimitUsageData.class)
- .block();
- }
-
- /**
- * 鏍规嵁鎻愮ず鐢熸垚闊抽
- *
- * @param prompt 鐢ㄤ簬鐢熸垚闊充箰闊抽鐨勬彁绀�
- * @param tags 闊充箰椋庢牸
- * @param title 闊充箰鍚嶇О
- * @param model 妯″瀷
- * @param waitAudio false 琛ㄧず鍚庡彴妯″紡锛屼粎杩斿洖闊抽浠诲姟淇℃伅锛岄渶瑕佽皟鐢� get API 鑾峰彇璇︾粏鐨勯煶棰戜俊鎭��
- * true 琛ㄧず鍚屾妯″紡锛孉PI 鏈�澶氱瓑寰� 100s锛岄煶棰戠敓鎴愬畬姣曞悗鐩存帴杩斿洖闊抽閾炬帴绛変俊鎭紝寤鸿鍦� GPT 绛� agent 涓娇鐢ㄣ��
- * @param makeInstrumental 鎸囩ず闊充箰闊抽鏄惁涓哄畾鍒讹紝濡傛灉涓� true锛屽垯浠庢瓕璇嶇敓鎴愶紝鍚﹀垯浠庢彁绀虹敓鎴�
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record MusicGenerateRequest(
- String prompt,
- String tags,
- String title,
- String model,
- @JsonProperty("wait_audio") boolean waitAudio,
- @JsonProperty("make_instrumental") boolean makeInstrumental
- ) {
-
- public MusicGenerateRequest(String prompt) {
- this(prompt, null, null, null, false, false);
- }
-
- public MusicGenerateRequest(String prompt, String model, boolean makeInstrumental) {
- this(prompt, null, null, model, false, makeInstrumental);
- }
-
- public MusicGenerateRequest(String prompt, String model, String tags, String title) {
- this(prompt, tags, title, model, false, false);
- }
-
- }
-
- /**
- * Suno API 鍝嶅簲鐨勯煶棰戞暟鎹�
- *
- * @param id 闊充箰鏁版嵁鐨� ID
- * @param title 闊充箰闊抽鐨勬爣棰�
- * @param imageUrl 闊充箰闊抽鐨勫浘鐗� URL
- * @param lyric 闊充箰闊抽鐨勬瓕璇�
- * @param audioUrl 闊充箰闊抽鐨� URL
- * @param videoUrl 闊充箰瑙嗛鐨� URL
- * @param createdAt 闊充箰闊抽鐨勫垱寤烘椂闂�
- * @param modelName 妯″瀷鍚嶇О
- * @param status submitted銆乹ueued銆乻treaming銆乧omplete
- * @param gptDescriptionPrompt 鎻忚堪璇�
- * @param prompt 鐢熸垚闊充箰闊抽鐨勬彁绀�
- * @param type 鎿嶄綔绫诲瀷
- * @param tags 闊充箰绫诲瀷鏍囩
- * @param duration 闊充箰鏃堕暱
- */
- public record MusicData(
- String id,
- String title,
- @JsonProperty("image_url") String imageUrl,
- String lyric,
- @JsonProperty("audio_url") String audioUrl,
- @JsonProperty("video_url") String videoUrl,
- @JsonProperty("created_at") String createdAt,
- @JsonProperty("model_name") String modelName,
- String status,
- @JsonProperty("gpt_description_prompt") String gptDescriptionPrompt,
- @JsonProperty("error_message") String errorMessage,
- String prompt,
- String type,
- String tags,
- Double duration
- ) {
- }
-
- /**
- * Suno API 鍝嶅簲鐨勬瓕璇嶆暟鎹��
- *
- * @param text 姝岃瘝
- * @param title 鏍囬
- * @param status 鐘舵��
- */
- public record LyricsData(
- String text,
- String title,
- String status
- ) {
- }
-
- /**
- * Suno API 鍝嶅簲鐨勯檺棰濇暟鎹紝鐩墠姣忔棩鍏嶈垂 50
- */
- public record LimitUsageData(
- @JsonProperty("credits_left") Long creditsLeft,
- String period,
- @JsonProperty("monthly_limit") Long monthlyLimit,
- @JsonProperty("monthly_usage") Long monthlyUsage
- ) {
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/wenduoduo/api/WenDuoDuoPptApi.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/wenduoduo/api/WenDuoDuoPptApi.java
deleted file mode 100644
index 69b8ec8..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/wenduoduo/api/WenDuoDuoPptApi.java
+++ /dev/null
@@ -1,381 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.wenduoduo.api;
-
-import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.core.ParameterizedTypeReference;
-import org.springframework.http.HttpRequest;
-import org.springframework.http.HttpStatusCode;
-import org.springframework.http.MediaType;
-import org.springframework.util.Assert;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
-import org.springframework.web.multipart.MultipartFile;
-import org.springframework.web.reactive.function.BodyInserters;
-import org.springframework.web.reactive.function.client.ClientResponse;
-import org.springframework.web.reactive.function.client.WebClient;
-import reactor.core.publisher.Flux;
-import reactor.core.publisher.Mono;
-
-import java.time.LocalDateTime;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.function.Function;
-import java.util.function.Predicate;
-
-/**
- * 鏂囧澶� API
- *
- * @author xiaoxin
- * @see <a href="https://docmee.cn/open-platform/api">PPT 鐢熸垚 API</a>
- */
-@Slf4j
-public class WenDuoDuoPptApi {
-
- public static final String BASE_URL = "https://docmee.cn";
- public static final String TOKEN_NAME = "token";
-
- private final WebClient webClient;
-
- private final Predicate<HttpStatusCode> STATUS_PREDICATE = status -> !status.is2xxSuccessful();
-
- private final Function<Object, Function<ClientResponse, Mono<? extends Throwable>>> EXCEPTION_FUNCTION =
- reqParam -> response -> response.bodyToMono(String.class).handle((responseBody, sink) -> {
- HttpRequest request = response.request();
- log.error("[WenDuoDuoPptApi] 璋冪敤澶辫触锛佽姹傛柟寮�:[{}]锛岃姹傚湴鍧�:[{}]锛岃姹傚弬鏁�:[{}]锛屽搷搴旀暟鎹�: [{}]",
- request.getMethod(), request.getURI(), reqParam, responseBody);
- sink.error(new IllegalStateException("[WenDuoDuoPptApi] 璋冪敤澶辫触锛�"));
- });
-
- public WenDuoDuoPptApi(String token) {
- Assert.hasText(token, "token 涓嶈兘涓虹┖");
- this.webClient = WebClient.builder()
- .baseUrl(BASE_URL)
- .defaultHeaders((headers) -> {
- headers.setContentType(MediaType.APPLICATION_JSON);
- headers.add(TOKEN_NAME, token);
- })
- .build();
- }
-
- /**
- * 鍒涘缓 token
- *
- * @param request 璇锋眰淇℃伅
- * @return token
- */
- public String createApiToken(CreateTokenRequest request) {
- return this.webClient.post()
- .uri("/api/user/createApiToken")
- .header("Api-Key", request.apiKey)
- .body(Mono.just(request), CreateTokenRequest.class)
- .retrieve()
- .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
- .bodyToMono(ApiResponse.class)
- .<String>handle((response, sink) -> {
- if (response.code != 0) {
- sink.error(new IllegalStateException("鍒涘缓 token 寮傚父锛�" + response.message));
- return;
- }
- sink.next(response.data.get("token").toString());
- })
- .block();
- }
-
- /**
- * 鍒涘缓浠诲姟
- *
- * @param type 绫诲瀷
- * @param content 鍐呭
- * @param files 鏂囦欢鍒楄〃
- * @return 浠诲姟 ID
- * @see <a href="https://docmee.cn/open-platform/api#%E5%88%9B%E5%BB%BA%E4%BB%BB%E5%8A%A1">鍒涘缓浠诲姟</a>
- */
- public ApiResponse createTask(Integer type, String content, List<MultipartFile> files) {
- MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
- formData.add("type", type);
- if (content != null) {
- formData.add("content", content);
- }
- if (files != null) {
- for (MultipartFile file : files) {
- formData.add("file", file.getResource());
- }
- }
- return this.webClient.post()
- .uri("/api/ppt/v2/createTask")
- .contentType(MediaType.MULTIPART_FORM_DATA)
- .body(BodyInserters.fromMultipartData(formData))
- .retrieve()
- .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(formData))
- .bodyToMono(ApiResponse.class)
- .block();
- }
-
- /**
- * 鑾峰彇鐢熸垚閫夐」
- *
- * @param lang 璇
- * @return 鐢熸垚閫夐」
- */
- public Map<String, Object> getOptions(String lang) {
- String uri = "/api/ppt/v2/options";
- if (lang != null) {
- uri += "?lang=" + lang;
- }
- return this.webClient.get()
- .uri(uri)
- .retrieve()
- .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(lang))
- .bodyToMono(new ParameterizedTypeReference<ApiResponse>() {
- })
- .<Map<String, Object>>handle((response, sink) -> {
- if (response.code != 0) {
- sink.error(new IllegalStateException("鑾峰彇鐢熸垚閫夐」寮傚父锛�" + response.message));
- return;
- }
- sink.next(response.data);
- })
- .block();
- }
-
- /**
- * 鍒嗛〉鏌ヨ PPT 妯℃澘
- *
- * @param token 浠ょ墝
- * @param request 璇锋眰浣�
- * @return 妯℃澘鍒楄〃
- */
- public PagePptTemplateInfo getTemplatePage(TemplateQueryRequest request) {
- return this.webClient.post()
- .uri("/api/ppt/templates")
- .bodyValue(request)
- .retrieve()
- .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
- .bodyToMono(new ParameterizedTypeReference<PagePptTemplateInfo>() {
- })
- .block();
- }
-
- /**
- * 鐢熸垚澶х翰鍐呭
- *
- * @return 澶х翰鍐呭娴�
- */
- public Flux<Map<String, Object>> createOutline(CreateOutlineRequest request) {
- return this.webClient.post()
- .uri("/api/ppt/v2/generateContent")
- .body(Mono.just(request), CreateOutlineRequest.class)
- .retrieve()
- .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
- .bodyToFlux(new ParameterizedTypeReference<>() {
- });
- }
-
- /**
- * 淇敼澶х翰鍐呭
- *
- * @param request 璇锋眰浣�
- * @return 澶х翰鍐呭娴�
- */
- public Flux<Map<String, Object>> updateOutline(UpdateOutlineRequest request) {
- return this.webClient.post()
- .uri("/api/ppt/v2/updateContent")
- .body(Mono.just(request), UpdateOutlineRequest.class)
- .retrieve()
- .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
- .bodyToFlux(new ParameterizedTypeReference<>() {
- });
- }
-
- /**
- * 鐢熸垚 PPT
- *
- * @return PPT淇℃伅
- */
- public PptInfo create(PptCreateRequest request) {
- return this.webClient.post()
- .uri("/api/ppt/v2/generatePptx")
- .body(Mono.just(request), PptCreateRequest.class)
- .retrieve()
- .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
- .bodyToMono(ApiResponse.class)
- .<PptInfo>handle((response, sink) -> {
- if (response.code != 0) {
- sink.error(new IllegalStateException("鐢熸垚 PPT 寮傚父锛�" + response.message));
- return;
- }
- sink.next(Objects.requireNonNull(JsonUtils.parseObject(JsonUtils.toJsonString(response.data.get("pptInfo")), PptInfo.class)));
- })
- .block();
- }
-
- /**
- * 鍒涘缓 Token 璇锋眰鍙傛暟
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record CreateTokenRequest(
- String apiKey,
- String uid,
- Integer limit
- ) {
-
- public CreateTokenRequest(String apiKey) {
- this(apiKey, null, null);
- }
-
- }
-
- /**
- * API 閫氱敤鍝嶅簲
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record ApiResponse(
- Integer code,
- String message,
- Map<String, Object> data
- ) {
- }
-
- /**
- * 鍒涘缓浠诲姟
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record CreateTaskRequest(
- Integer type,
- String content,
- List<MultipartFile> files
- ) {
- }
-
- /**
- * 鐢熸垚澶х翰鍐呭璇锋眰
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record CreateOutlineRequest(
- String id,
- String length,
- String scene,
- String audience,
- String lang,
- String prompt
- ) {
- }
-
- /**
- * 淇敼澶х翰鍐呭璇锋眰
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record UpdateOutlineRequest(
- String id,
- String markdown,
- String question
- ) {
- }
-
- /**
- * 鐢熸垚 PPT 璇锋眰鍙傛暟
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record PptCreateRequest(
- String id,
- String templateId,
- String markdown
- ) {
- }
-
- /**
- * PPT 淇℃伅
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record PptInfo(
- String id,
- String name,
- String subject,
- String coverUrl,
- String fileUrl,
- String templateId,
- String pptxProperty,
- String userId,
- String userName,
- int companyId,
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- LocalDateTime updateTime,
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- LocalDateTime createTime,
- String createUser,
- String updateUser
- ) {
- }
-
- /**
- * 妯℃澘鏌ヨ璇锋眰鍙傛暟
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record TemplateQueryRequest(
- int page,
- int size,
- Filter filters
- ) {
-
- /**
- * 妯℃澘鏌ヨ杩囨护鏉′欢
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record Filter(
- int type,
- String category,
- String style,
- String themeColor
- ) {
- }
-
- }
-
- /**
- * PPT妯℃澘鍒嗛〉淇℃伅
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record PagePptTemplateInfo(
- List<PptTemplateInfo> data,
- String total
- ) {
- }
-
- /**
- * PPT妯℃澘淇℃伅
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record PptTemplateInfo(
- String id,
- int type,
- Integer subType,
- String layout,
- String category,
- String style,
- String themeColor,
- String lang,
- boolean animation,
- String subject,
- String coverUrl,
- String fileUrl,
- List<String> pageCoverUrls,
- String pptxProperty,
- int sort,
- int num,
- Integer imgNum,
- int isDeleted,
- String userId,
- int companyId,
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- LocalDateTime updateTime,
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
- LocalDateTime createTime,
- String createUser,
- String updateUser
- ) {
- }
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/xinghuo/XingHuoChatModel.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/xinghuo/XingHuoChatModel.java
deleted file mode 100644
index 65a1c99..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/xinghuo/XingHuoChatModel.java
+++ /dev/null
@@ -1,160 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo;
-
-import cn.hutool.core.util.StrUtil;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.ai.chat.model.ChatModel;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.ChatOptions;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.deepseek.DeepSeekChatModel;
-import org.springframework.ai.deepseek.DeepSeekChatOptions;
-import org.springframework.ai.deepseek.api.DeepSeekApi;
-import reactor.core.publisher.Flux;
-
-/**
- * 璁鏄熺伀 {@link ChatModel} 瀹炵幇绫�
- *
- * @author fansili
- */
-@Slf4j
-public class XingHuoChatModel implements ChatModel {
-
- /**
- * 鏄熺伀 X2
- *
- * @see <a href="https://spark-api-open.xf-yun.com/x2/chat/completions">鎺ュ彛鍦板潃</a>
- */
- public static final String MODEL_X2 = "x2";
-
- /**
- * 鏄熺伀 X2 Flash
- *
- * @see <a href="https://spark-api-open.xf-yun.com/agent/v1/chat/completions">鎺ュ彛鍦板潃</a>
- */
- public static final String MODEL_X2_FLASH = "x2-flash";
-
- private static final String BASE_URL_X2 = "https://spark-api-open.xf-yun.com/x2";
-
- private static final String BASE_URL_X2_FLASH = "https://spark-api-open.xf-yun.com/agent/v1";
-
- public static final String MODEL_DEFAULT = MODEL_X2_FLASH;
-
- private static String getBaseUrl(String model) {
- if (MODEL_X2_FLASH.equals(model)) {
- return BASE_URL_X2_FLASH;
- }
- return BASE_URL_X2;
- }
-
- private final String apiKey;
-
- private final DeepSeekChatOptions options;
-
- /**
- * 鍏煎 OpenAI 鎺ュ彛锛岃繘琛屽鐢�
- */
- private final ChatModel chatModelX2;
-
- private final ChatModel chatModelX2Flash;
-
- private XingHuoChatModel(String apiKey, DeepSeekChatOptions options) {
- this.apiKey = apiKey;
- this.options = options;
- this.chatModelX2 = buildChatModel(MODEL_X2);
- this.chatModelX2Flash = buildChatModel(MODEL_X2_FLASH);
- }
-
- public static Builder builder() {
- return new Builder();
- }
-
- @Override
- public ChatResponse call(Prompt prompt) {
- return getChatModel(prompt).call(buildApiPrompt(prompt));
- }
-
- @Override
- public Flux<ChatResponse> stream(Prompt prompt) {
- return getChatModel(prompt).stream(buildApiPrompt(prompt));
- }
-
- @Override
- public ChatOptions getOptions() {
- return options;
- }
-
- @Override
- @Deprecated(forRemoval = true)
- @SuppressWarnings("removal")
- public ChatOptions getDefaultOptions() {
- return getOptions();
- }
-
- private ChatModel getChatModel(Prompt prompt) {
- String model = options.getModel();
- ChatOptions options = prompt.getOptions();
- if (options != null && isBusinessModel(options.getModel())) {
- model = options.getModel();
- }
- return getChatModel(model);
- }
-
- private ChatModel getChatModel(String model) {
- if (MODEL_X2_FLASH.equals(model)) {
- return chatModelX2Flash;
- }
- return chatModelX2;
- }
-
- private ChatModel buildChatModel(String model) {
- return DeepSeekChatModel.builder()
- .deepSeekApi(DeepSeekApi.builder()
- .baseUrl(getBaseUrl(model))
- .apiKey(apiKey)
- .build())
- .options(options.mutate().model("spark-x").build())
- .build();
- }
-
- private static Prompt buildApiPrompt(Prompt prompt) {
- ChatOptions options = prompt.getOptions();
- if (options == null) {
- return prompt;
- }
- return Prompt.builder()
- .messages(prompt.getInstructions())
- .chatOptions(options.mutate().model("spark-x").build())
- .build();
- }
-
- private static boolean isBusinessModel(String model) {
- return MODEL_X2.equals(model) || MODEL_X2_FLASH.equals(model);
- }
-
- public static final class Builder {
-
- private String apiKey;
-
- private DeepSeekChatOptions options;
-
- public Builder apiKey(String apiKey) {
- this.apiKey = apiKey;
- return this;
- }
-
- public Builder options(DeepSeekChatOptions options) {
- this.options = options;
- return this;
- }
-
- public XingHuoChatModel build() {
- DeepSeekChatOptions options = this.options != null ? this.options : DeepSeekChatOptions.builder().build();
- if (StrUtil.isEmpty(options.getModel())) {
- options = options.mutate().model(MODEL_DEFAULT).build();
- }
- return new XingHuoChatModel(apiKey, options);
- }
-
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/xinghuo/api/XunFeiPptApi.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/xinghuo/api/XunFeiPptApi.java
deleted file mode 100644
index 9705217..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/xinghuo/api/XunFeiPptApi.java
+++ /dev/null
@@ -1,522 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.api;
-
-import cn.hutool.core.util.ObjUtil;
-import cn.hutool.crypto.SecureUtil;
-import cn.hutool.crypto.digest.HmacAlgorithm;
-import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import lombok.Builder;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.core.io.ByteArrayResource;
-import org.springframework.http.HttpStatusCode;
-import org.springframework.http.MediaType;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
-import org.springframework.util.StringUtils;
-import org.springframework.web.multipart.MultipartFile;
-import org.springframework.web.reactive.function.BodyInserters;
-import org.springframework.web.reactive.function.client.ClientResponse;
-import org.springframework.web.reactive.function.client.WebClient;
-import reactor.core.publisher.Mono;
-
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Function;
-import java.util.function.Predicate;
-
-/**
- * 璁鏅鸿兘 PPT 鐢熸垚 API
- *
- * @author xiaoxin
- * @see <a href="https://www.xfyun.cn/doc/spark/PPTv2.html">鏅鸿兘 PPT 鐢熸垚 API</a>
- */
-@Slf4j
-public class XunFeiPptApi {
-
- public static final String BASE_URL = "https://zwapi.xfyun.cn/api/ppt/v2";
- private static final String HEADER_APP_ID = "appId";
- private static final String HEADER_TIMESTAMP = "timestamp";
- private static final String HEADER_SIGNATURE = "signature";
-
- private final WebClient webClient;
- private final String appId;
- private final String apiSecret;
-
- private final Predicate<HttpStatusCode> STATUS_PREDICATE = status -> !status.is2xxSuccessful();
-
- private final Function<Object, Function<ClientResponse, Mono<? extends Throwable>>> EXCEPTION_FUNCTION =
- reqParam -> response -> response.bodyToMono(String.class).handle((responseBody, sink) -> {
- log.error("[XunFeiPptApi] 璋冪敤澶辫触锛佽姹傚弬鏁�:[{}]锛屽搷搴旀暟鎹�: [{}]", reqParam, responseBody);
- sink.error(new IllegalStateException("[XunFeiPptApi] 璋冪敤澶辫触锛�"));
- });
-
- public XunFeiPptApi(String appId, String apiSecret) {
- this.appId = appId;
- this.apiSecret = apiSecret;
- this.webClient = WebClient.builder()
- .baseUrl(BASE_URL)
- .defaultHeaders((headers) -> {
- headers.setContentType(MediaType.APPLICATION_JSON);
- headers.add(HEADER_APP_ID, appId);
- })
- .build();
-
- }
-
- /**
- * 鑾峰彇绛惧悕
- *
- * @return 绛惧悕淇℃伅
- */
- private SignatureInfo getSignature() {
- long timestamp = System.currentTimeMillis() / 1000;
- String ts = String.valueOf(timestamp);
- String signature = generateSignature(appId, apiSecret, timestamp);
- return new SignatureInfo(ts, signature);
- }
-
- /**
- * 鐢熸垚绛惧悕
- *
- * @param appId 搴旂敤ID
- * @param apiSecret 搴旂敤瀵嗛挜
- * @param timestamp 鏃堕棿鎴筹紙绉掞級
- * @return 绛惧悕
- */
- private String generateSignature(String appId, String apiSecret, long timestamp) {
- String auth = SecureUtil.md5(appId + timestamp);
- return SecureUtil.hmac(HmacAlgorithm.HmacSHA1, apiSecret).digestBase64(auth, false);
- }
-
- /**
- * 鑾峰彇 PPT 妯℃澘鍒楄〃
- *
- * @param style 椋庢牸锛屽"鍟嗗姟"
- * @param pageSize 姣忛〉鏁伴噺
- * @return 妯℃澘鍒楄〃
- */
- public TemplatePageResponse getTemplatePage(String style, Integer pageSize) {
- SignatureInfo signInfo = getSignature();
- Map<String, Object> requestBody = new HashMap<>();
- requestBody.put("style", style);
- requestBody.put("pageSize", ObjUtil.defaultIfNull(pageSize, 20));
- return this.webClient.post()
- .uri("/template/list")
- .header(HEADER_TIMESTAMP, signInfo.timestamp)
- .header(HEADER_SIGNATURE, signInfo.signature)
- .contentType(MediaType.APPLICATION_JSON)
- .bodyValue(requestBody)
- .retrieve()
- .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(requestBody))
- .bodyToMono(TemplatePageResponse.class)
- .block();
- }
-
- /**
- * 鍒涘缓澶х翰锛堥�氳繃鏂囨湰锛�
- *
- * @param query 鏌ヨ鏂囨湰
- * @return 澶х翰鍒涘缓鍝嶅簲
- */
- public CreateResponse createOutline(String query) {
- SignatureInfo signInfo = getSignature();
- MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
- formData.add("query", query);
- return this.webClient.post()
- .uri("/createOutline")
- .header(HEADER_TIMESTAMP, signInfo.timestamp)
- .header(HEADER_SIGNATURE, signInfo.signature)
- .contentType(MediaType.MULTIPART_FORM_DATA)
- .body(BodyInserters.fromMultipartData(formData))
- .retrieve()
- .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(formData))
- .bodyToMono(CreateResponse.class)
- .block();
- }
-
-
- /**
- * 鐩存帴鍒涘缓 PPT锛堢畝鍖栫増 - 閫氳繃鏂囨湰锛�
- *
- * @param query 鏌ヨ鏂囨湰
- * @return 鍒涘缓鍝嶅簲
- */
- public CreateResponse create(String query) {
- CreatePptRequest request = CreatePptRequest.builder()
- .query(query)
- .build();
- return create(request);
- }
-
- /**
- * 鐩存帴鍒涘缓 PPT锛堢畝鍖栫増 - 閫氳繃鏂囦欢锛�
- *
- * @param file 鏂囦欢
- * @param fileName 鏂囦欢鍚�
- * @return 鍒涘缓鍝嶅簲
- */
- public CreateResponse create(MultipartFile file, String fileName) {
- CreatePptRequest request = CreatePptRequest.builder()
- .file(file).fileName(fileName).build();
- return create(request);
- }
-
- /**
- * 鐩存帴鍒涘缓 PPT锛堝畬鏁寸増锛�
- *
- * @param request 璇锋眰鍙傛暟
- * @return 鍒涘缓鍝嶅簲
- */
- public CreateResponse create(CreatePptRequest request) {
- SignatureInfo signInfo = getSignature();
- MultiValueMap<String, Object> formData = buildCreatePptFormData(request);
- return this.webClient.post()
- .uri("/create")
- .header(HEADER_TIMESTAMP, signInfo.timestamp)
- .header(HEADER_SIGNATURE, signInfo.signature)
- .contentType(MediaType.MULTIPART_FORM_DATA)
- .body(BodyInserters.fromMultipartData(formData))
- .retrieve()
- .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(formData))
- .bodyToMono(CreateResponse.class)
- .block();
- }
-
-
- /**
- * 閫氳繃澶х翰鍒涘缓 PPT锛堢畝鍖栫増锛�
- *
- * @param outline 澶х翰鍐呭
- * @param query 鏌ヨ鏂囨湰
- * @return 鍒涘缓鍝嶅簲
- */
- public CreateResponse createPptByOutline(OutlineData outline, String query) {
- CreatePptByOutlineRequest request = CreatePptByOutlineRequest.builder()
- .outline(outline)
- .query(query)
- .build();
- return createPptByOutline(request);
- }
-
- /**
- * 閫氳繃澶х翰鍒涘缓 PPT锛堝畬鏁寸増锛�
- *
- * @param request 璇锋眰鍙傛暟
- * @return 鍒涘缓鍝嶅簲
- */
- public CreateResponse createPptByOutline(CreatePptByOutlineRequest request) {
- SignatureInfo signInfo = getSignature();
- return this.webClient.post()
- .uri("/createPptByOutline")
- .header(HEADER_TIMESTAMP, signInfo.timestamp)
- .header(HEADER_SIGNATURE, signInfo.signature)
- .contentType(MediaType.APPLICATION_JSON)
- .bodyValue(request)
- .retrieve()
- .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(request))
- .bodyToMono(CreateResponse.class)
- .block();
- }
-
- /**
- * 妫�鏌� PPT 鐢熸垚杩涘害
- *
- * @param sid 浠诲姟 ID
- * @return 杩涘害鍝嶅簲
- */
- public ProgressResponse checkProgress(String sid) {
- SignatureInfo signInfo = getSignature();
- return this.webClient.get()
- .uri(uriBuilder -> uriBuilder
- .path("/progress")
- .queryParam("sid", sid)
- .build())
- .header(HEADER_TIMESTAMP, signInfo.timestamp)
- .header(HEADER_SIGNATURE, signInfo.signature)
- .retrieve()
- .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(sid))
- .bodyToMono(ProgressResponse.class)
- .block();
- }
-
- /**
- * 绛惧悕淇℃伅
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- private record SignatureInfo(
- String timestamp,
- String signature
- ) {
- }
-
- /**
- * 妯℃澘鍒楄〃鍝嶅簲
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record TemplatePageResponse(
- boolean flag,
- int code,
- String desc,
- Integer count,
- TemplatePageData data
- ) {
- }
-
- /**
- * 妯℃澘鍒楄〃鏁版嵁
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record TemplatePageData(
- String total,
- List<TemplateInfo> records,
- Integer pageNum
- ) {
- }
-
- /**
- * 妯℃澘淇℃伅
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record TemplateInfo(
- String templateIndexId,
- Integer pageCount,
- String type,
- String color,
- String industry,
- String style,
- String detailImage
- ) {
- }
-
- /**
- * 鍒涘缓鍝嶅簲
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record CreateResponse(
- boolean flag,
- int code,
- String desc,
- Integer count,
- CreateResponseData data
- ) {
- }
-
- /**
- * 鍒涘缓鍝嶅簲鏁版嵁
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record CreateResponseData(
- String sid,
- String coverImgSrc,
- String title,
- String subTitle,
- OutlineData outline
- ) {
- }
-
- /**
- * 澶х翰鏁版嵁缁撴瀯
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record OutlineData(
- String title,
- String subTitle,
- List<Chapter> chapters
- ) {
-
- /**
- * 绔犺妭缁撴瀯
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record Chapter(
- String chapterTitle,
- List<ChapterContent> chapterContents
- ) {
-
- /**
- * 绔犺妭鍐呭
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record ChapterContent(
- String chapterTitle
- ) {
- }
-
- }
-
- /**
- * 灏嗗ぇ绾插璞¤浆鎹负JSON瀛楃涓�
- *
- * @return 澶х翰JSON瀛楃涓�
- */
- public String toJsonString() {
- return JsonUtils.toJsonString(this);
- }
- }
-
- /**
- * 杩涘害鍝嶅簲
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record ProgressResponse(
- int code,
- String desc,
- ProgressResponseData data
- ) {
- }
-
- /**
- * 杩涘害鍝嶅簲鏁版嵁
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record ProgressResponseData(
- int process,
- String pptId,
- String pptUrl,
- String pptStatus,
- String aiImageStatus,
- String cardNoteStatus,
- String errMsg,
- Integer totalPages,
- Integer donePages
- ) {
-
- /**
- * 鏄惁鍏ㄩ儴瀹屾垚
- *
- * @return 鏄惁鍏ㄩ儴瀹屾垚
- */
- public boolean isAllDone() {
- return "done".equals(pptStatus)
- && ("done".equals(aiImageStatus) || aiImageStatus == null)
- && ("done".equals(cardNoteStatus) || cardNoteStatus == null);
- }
-
- /**
- * 鏄惁澶辫触
- *
- * @return 鏄惁澶辫触
- */
- public boolean isFailed() {
- return "build_failed".equals(pptStatus);
- }
-
- /**
- * 鑾峰彇杩涘害鐧惧垎姣�
- *
- * @return 杩涘害鐧惧垎姣�
- */
- public int getProgressPercent() {
- if (totalPages == null || totalPages == 0 || donePages == null) {
- return process; // 鍏煎鏃х増杩斿洖
- }
- return (int) (donePages * 100.0 / totalPages);
- }
-
- }
-
- /**
- * 閫氳繃澶х翰鍒涘缓 PPT 璇锋眰鍙傛暟
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- @Builder
- public record CreatePptByOutlineRequest(
- String query, // 鐢ㄦ埛鐢熸垚PPT瑕佹眰锛堟渶澶�8000瀛楋級
- String outlineSid, // 宸茬敓鎴愬ぇ绾插悗锛屽搷搴旇繑鍥炵殑璇锋眰澶х翰鍞竴id
- OutlineData outline, // 澶х翰鍐呭
- String templateId, // 妯℃澘ID
- String businessId, // 涓氬姟ID锛堥潪蹇呬紶锛�
- String author, // PPT浣滆�呭悕
- Boolean isCardNote, // 鏄惁鐢熸垚PPT婕旇澶囨敞
- Boolean search, // 鏄惁鑱旂綉鎼滅储
- String language, // 璇
- String fileUrl, // 鏂囦欢鍦板潃
- String fileName, // 鏂囦欢鍚�(甯︽枃浠跺悕鍚庣紑)
- Boolean isFigure, // 鏄惁鑷姩閰嶅浘
- String aiImage // ai閰嶅浘绫诲瀷锛歯ormal銆乤dvanced
- ) {
- }
-
-
- /**
- * 鏋勫缓鍒涘缓 PPT 鐨勮〃鍗曟暟鎹�
- *
- * @param request 璇锋眰鍙傛暟
- * @return 琛ㄥ崟鏁版嵁
- */
- private MultiValueMap<String, Object> buildCreatePptFormData(CreatePptRequest request) {
- MultiValueMap<String, Object> formData = new LinkedMultiValueMap<>();
- if (request.file() != null) {
- try {
- formData.add("file", new ByteArrayResource(request.file().getBytes()) {
- @Override
- public String getFilename() {
- return request.file().getOriginalFilename();
- }
- });
- } catch (IOException e) {
- log.error("[XunFeiPptApi] 鏂囦欢澶勭悊澶辫触", e);
- throw new IllegalStateException("[XunFeiPptApi] 鏂囦欢澶勭悊澶辫触", e);
- }
- }
- Map<String, Object> param = new HashMap<>();
- addIfPresent(param, "query", request.query());
- addIfPresent(param, "fileUrl", request.fileUrl());
- addIfPresent(param, "fileName", request.fileName());
- addIfPresent(param, "templateId", request.templateId());
- addIfPresent(param, "businessId", request.businessId());
- addIfPresent(param, "author", request.author());
- addIfPresent(param, "isCardNote", request.isCardNote());
- addIfPresent(param, "search", request.search());
- addIfPresent(param, "language", request.language());
- addIfPresent(param, "isFigure", request.isFigure());
- addIfPresent(param, "aiImage", request.aiImage());
- param.forEach(formData::add);
- return formData;
- }
-
- public static <K, V> void addIfPresent(Map<K, V> map, K key, V value) {
- if (ObjUtil.isNull(key) || ObjUtil.isNull(map)) {
- return;
- }
-
- boolean isPresent = false;
- if (ObjUtil.isNotNull(value)) {
- if (value instanceof String) {
- // 瀛楃涓诧細闇�瑕佹湁瀹為檯鍐呭
- isPresent = StringUtils.hasText((String) value);
- } else {
- // 鍏朵粬绫诲瀷锛氶潪 null 鍗宠涓哄瓨鍦�
- isPresent = true;
- }
- }
- if (isPresent) {
- map.put(key, value);
- }
- }
-
- /**
- * 鐩存帴鐢熸垚PPT璇锋眰鍙傛暟
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- @Builder
- public record CreatePptRequest(
- String query, // 鐢ㄦ埛鐢熸垚PPT瑕佹眰锛堟渶澶�8000瀛楋級
- MultipartFile file, // 涓婁紶鏂囦欢
- String fileUrl, // 鏂囦欢鍦板潃
- String fileName, // 鏂囦欢鍚�(甯︽枃浠跺悕鍚庣紑)
- String templateId, // 妯℃澘ID
- String businessId, // 涓氬姟ID锛堥潪蹇呬紶锛�
- String author, // PPT浣滆�呭悕
- Boolean isCardNote, // 鏄惁鐢熸垚PPT婕旇澶囨敞
- Boolean search, // 鏄惁鑱旂綉鎼滅储
- String language, // 璇
- Boolean isFigure, // 鏄惁鑷姩閰嶅浘
- String aiImage // ai閰嶅浘绫诲瀷锛歯ormal銆乤dvanced
- ) {
-
- }
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/yiyan/YiYanChatModel.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/yiyan/YiYanChatModel.java
deleted file mode 100644
index 75631e6..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/yiyan/YiYanChatModel.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.yiyan;
-
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.ai.chat.model.ChatModel;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.ChatOptions;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.deepseek.DeepSeekChatModel;
-import reactor.core.publisher.Flux;
-
-/**
- * 鏂囧績涓�瑷� {@link ChatModel} 瀹炵幇绫�
- *
- * @author 鑺嬮亾婧愮爜
- */
-@Slf4j
-@RequiredArgsConstructor
-public class YiYanChatModel implements ChatModel {
-
- public static final String BASE_URL = "https://qianfan.baidubce.com/v2";
-
- public static final String MODEL_DEFAULT = "ernie-5.1";
-
- /**
- * 鍏煎 OpenAI 鎺ュ彛锛屽鐢� DeepSeek 瀹㈡埛绔�
- */
- private final DeepSeekChatModel deepSeekChatModel;
-
- @Override
- public ChatResponse call(Prompt prompt) {
- return deepSeekChatModel.call(prompt);
- }
-
- @Override
- public Flux<ChatResponse> stream(Prompt prompt) {
- return deepSeekChatModel.stream(prompt);
- }
-
- @Override
- public ChatOptions getOptions() {
- return deepSeekChatModel.getOptions();
- }
-
- @Override
- @Deprecated(forRemoval = true)
- @SuppressWarnings("removal")
- public ChatOptions getDefaultOptions() {
- return getOptions();
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/zhipu/ZhiPuChatModel.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/zhipu/ZhiPuChatModel.java
deleted file mode 100644
index f22c77c..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/zhipu/ZhiPuChatModel.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.zhipu;
-
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.ai.chat.model.ChatModel;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.ChatOptions;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.deepseek.DeepSeekChatModel;
-import reactor.core.publisher.Flux;
-
-/**
- * 鏅鸿氨 {@link ChatModel} 瀹炵幇绫�
- *
- * @author 鑺嬮亾婧愮爜
- */
-@Slf4j
-@RequiredArgsConstructor
-public class ZhiPuChatModel implements ChatModel {
-
- public static final String BASE_URL = "https://open.bigmodel.cn/api/paas/v4";
-
- public static final String MODEL_DEFAULT = "glm-5.2";
-
- /**
- * 鍏煎 OpenAI 鎺ュ彛锛屽鐢� DeepSeek 瀹㈡埛绔�
- */
- private final DeepSeekChatModel deepSeekChatModel;
-
- @Override
- public ChatResponse call(Prompt prompt) {
- return deepSeekChatModel.call(prompt);
- }
-
- @Override
- public Flux<ChatResponse> stream(Prompt prompt) {
- return deepSeekChatModel.stream(prompt);
- }
-
- @Override
- public ChatOptions getOptions() {
- return deepSeekChatModel.getOptions();
- }
-
- @Override
- @Deprecated(forRemoval = true)
- @SuppressWarnings("removal")
- public ChatOptions getDefaultOptions() {
- return getOptions();
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/webserch/AiWebSearchClient.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/webserch/AiWebSearchClient.java
deleted file mode 100644
index 9fbff55..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/webserch/AiWebSearchClient.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.webserch;
-
-/**
- * 缃戠粶鎼滅储瀹㈡埛绔帴鍙�
- *
- * @author 鑺嬮亾婧愮爜
- */
-public interface AiWebSearchClient {
-
- /**
- * 缃戦〉鎼滅储
- *
- * @param request 鎼滅储璇锋眰
- * @return 鎼滅储缁撴灉
- */
- AiWebSearchResponse search(AiWebSearchRequest request);
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/webserch/AiWebSearchRequest.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/webserch/AiWebSearchRequest.java
deleted file mode 100644
index 9bd2cfe..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/webserch/AiWebSearchRequest.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.webserch;
-
-import jakarta.validation.constraints.Max;
-import jakarta.validation.constraints.Min;
-import jakarta.validation.constraints.NotEmpty;
-import jakarta.validation.constraints.NotNull;
-import lombok.Data;
-
-@Data
-public class AiWebSearchRequest {
-
- /**
- * 鐢ㄦ埛鐨勬悳绱㈣瘝
- */
- @NotEmpty(message = "鎼滅储璇嶄笉鑳戒负绌�")
- private String query;
-
- /**
- * 鏄惁鏄剧ず鏂囨湰鎽樿
- *
- * true - 鏄剧ず
- * false - 涓嶆樉绀猴紙榛樿锛�
- */
- private Boolean summary;
-
- /**
- * 杩斿洖缁撴灉鐨勬潯鏁�
- */
- @NotNull(message = "杩斿洖缁撴灉鏉℃暟涓嶈兘涓虹┖")
- @Min(message = "杩斿洖缁撴灉鏉℃暟鏈�灏忎负 1", value = 1)
- @Max(message = "杩斿洖缁撴灉鏉℃暟鏈�澶т负 50", value = 50)
- private Integer count;
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/webserch/AiWebSearchResponse.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/webserch/AiWebSearchResponse.java
deleted file mode 100644
index 8755b32..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/webserch/AiWebSearchResponse.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.webserch;
-
-import lombok.Data;
-
-import java.util.List;
-
-@Data
-public class AiWebSearchResponse {
-
- /**
- * 鎬绘暟锛堟�诲叡鍖归厤鐨勭綉椤垫暟锛�
- */
- private Long total;
-
- /**
- * 鏁版嵁鍒楄〃
- */
- private List<WebPage> lists;
-
- /**
- * 缃戦〉瀵硅薄
- */
- @Data
- public static class WebPage {
-
- /**
- * 鍚嶇О
- *
- * 渚嬪璇达細鎼滅嫄缃�
- */
- private String name;
- /**
- * 鍥炬爣
- */
- private String icon;
-
- /**
- * 鏍囬
- *
- * 渚嬪璇达細186椤祙闃块噷宸村反锛�2024骞寸幆澧冦�佺ぞ浼氬拰娌荤悊锛圗SG锛夋姤鍛�
- */
- private String title;
- /**
- * URL
- *
- * 渚嬪璇达細https://m.sohu.com/a/815036254_121819701/?pvid=000115_3w_a
- */
- @SuppressWarnings("JavadocLinkAsPlainText")
- private String url;
-
- /**
- * 鍐呭鐨勭畝鐭弿杩�
- */
- private String snippet;
- /**
- * 鍐呭鐨勬枃鏈憳瑕�
- */
- private String summary;
-
- }
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/webserch/bocha/AiBoChaWebSearchClient.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/webserch/bocha/AiBoChaWebSearchClient.java
deleted file mode 100644
index 7395fe6..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/webserch/bocha/AiBoChaWebSearchClient.java
+++ /dev/null
@@ -1,153 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.webserch.bocha;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.lang.Assert;
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchClient;
-import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchRequest;
-import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchResponse;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.core.ParameterizedTypeReference;
-import org.springframework.http.HttpStatusCode;
-import org.springframework.http.MediaType;
-import org.springframework.web.reactive.function.client.ClientResponse;
-import org.springframework.web.reactive.function.client.WebClient;
-import reactor.core.publisher.Mono;
-
-import java.util.List;
-import java.util.function.Function;
-import java.util.function.Predicate;
-
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
-
-/**
- * 鍗氭煡 {@link AiWebSearchClient} 瀹炵幇绫�
- *
- * @see <a href="https://open.bochaai.com/overview">鍗氭煡 AI 寮�鏀惧钩鍙�</a>
- *
- * @author 鑺嬮亾婧愮爜
- */
-@Slf4j
-public class AiBoChaWebSearchClient implements AiWebSearchClient {
-
- public static final String BASE_URL = "https://api.bochaai.com";
- private static final String AUTHORIZATION_HEADER = "Authorization";
- private static final String BEARER_PREFIX = "Bearer ";
-
- private final WebClient webClient;
-
- private final Predicate<HttpStatusCode> STATUS_PREDICATE = status -> !status.is2xxSuccessful();
-
- private final Function<Object, Function<ClientResponse, Mono<? extends Throwable>>> EXCEPTION_FUNCTION =
- reqParam -> response -> response.bodyToMono(String.class).handle((responseBody, sink) -> {
- log.error("[AiBoChaWebSearchClient] 璋冪敤澶辫触锛佽姹傚弬鏁�:[{}]锛屽搷搴旀暟鎹�: [{}]", reqParam, responseBody);
- sink.error(new IllegalStateException("[AiBoChaWebSearchClient] 璋冪敤澶辫触锛�"));
- });
-
- public AiBoChaWebSearchClient(String apiKey) {
- this.webClient = WebClient.builder()
- .baseUrl(BASE_URL)
- .defaultHeaders((headers) -> {
- headers.setContentType(MediaType.APPLICATION_JSON);
- headers.add(AUTHORIZATION_HEADER, BEARER_PREFIX + apiKey);
- })
- .build();
- }
-
- @Override
- public AiWebSearchResponse search(AiWebSearchRequest request) {
- // 杞崲璇锋眰鍙傛暟
- WebSearchRequest webSearchRequest = new WebSearchRequest(
- request.getQuery(),
- request.getSummary(),
- request.getCount()
- );
- // 璋冪敤鍗氭煡 API
- CommonResult<WebSearchResponse> response = this.webClient.post()
- .uri("/v1/web-search")
- .bodyValue(webSearchRequest)
- .retrieve()
- .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION.apply(webSearchRequest))
- .bodyToMono(new ParameterizedTypeReference<CommonResult<WebSearchResponse>>() {})
- .block();
- if (response == null) {
- throw new IllegalStateException("[search][鎼滅储缁撴灉涓虹┖]");
- }
- if (response.getData() == null) {
- throw new IllegalStateException(String.format("[search][鎼滅储澶辫触锛宑ode = %s, msg = %s]",
- response.getCode(), response.getMsg()));
- }
- WebSearchResponse data = response.getData();
-
- // 杞崲缁撴灉
- AiWebSearchResponse result = new AiWebSearchResponse();
- if (data.webPages() == null || CollUtil.isEmpty(data.webPages().value())) {
- return result.setTotal(0L).setLists(List.of());
- }
- return result.setTotal(data.webPages().totalEstimatedMatches())
- .setLists(convertList(data.webPages().value(), page -> new AiWebSearchResponse.WebPage()
- .setName(page.siteName()).setIcon(page.siteIcon())
- .setTitle(page.name()).setUrl(page.url())
- .setSnippet(page.snippet()).setSummary(page.summary())));
- }
-
- /**
- * 缃戦〉鎼滅储璇锋眰鍙傛暟
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record WebSearchRequest(
- String query,
- Boolean summary,
- Integer count
- ) {
- public WebSearchRequest {
- Assert.notBlank(query, "query 涓嶈兘涓虹┖");
- }
- }
-
- /**
- * 缃戦〉鎼滅储鍝嶅簲
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record WebSearchResponse(
- WebSearchWebPages webPages
- ) {
- }
-
- /**
- * 缃戦〉鎼滅储缁撴灉
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record WebSearchWebPages(
- String webSearchUrl,
- Long totalEstimatedMatches,
- List<WebPageValue> value,
- Boolean someResultsRemoved
- ) {
-
- /**
- * 缃戦〉缁撴灉鍊�
- */
- @JsonInclude(value = JsonInclude.Include.NON_NULL)
- public record WebPageValue(
- String id,
- String name,
- String url,
- String displayUrl,
- String snippet,
- String summary,
- String siteName,
- String siteIcon,
- String datePublished,
- String dateLastCrawled,
- String cachedPageUrl,
- String language,
- Boolean isFamilyFriendly,
- Boolean isNavigational
- ) {
- }
-
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/package-info.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/package-info.java
deleted file mode 100644
index dd3540a..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/package-info.java
+++ /dev/null
@@ -1,13 +0,0 @@
-/**
- * AI 澶фā鍨嬬粍浠讹紝鍩轰簬 Spring AI 鎷撳睍
- *
- * models 鍖呰矾寰勶細
- * 1. xinghuo 鍖咃細銆愯椋炪�戞槦鐏紝鑷繁瀹炵幇
- * 2. deepseek 鍖咃細銆愭繁搴︽眰绱€�慏eepSeek锛岃嚜宸卞疄鐜�
- * 3. doubao 鍖咃細銆愬瓧鑺傝眴鍖呫�慏ouBao锛岃嚜宸卞疄鐜�
- * 4. hunyuan 鍖咃細銆愯吘璁贩鍏冦�慔unYuan锛岃嚜宸卞疄鐜�
- * 5. siliconflow 鍖咃細銆愮鍩虹娴併�慡iliconFlow锛岃嚜宸卞疄鐜�
- * 6. midjourney 鍖咃細Midjourney API锛屽鎺� https://github.com/novicezk/midjourney-proxy 瀹炵幇
- * 7. suno 鍖咃細Suno API锛屽鎺� https://github.com/gcui-art/suno-api 瀹炵幇
- */
-package cn.iocoder.yudao.module.ai.framework.ai;
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/package-info.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/package-info.java
deleted file mode 100644
index a4452f2..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/package-info.java
+++ /dev/null
@@ -1,6 +0,0 @@
-/**
- * 灞炰簬 ai 妯″潡鐨� framework 灏佽
- *
- * @author 鑺嬮亾婧愮爜
- */
-package cn.iocoder.yudao.module.ai.framework;
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/security/config/SecurityConfiguration.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/security/config/SecurityConfiguration.java
deleted file mode 100644
index c763260..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/security/config/SecurityConfiguration.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.security.config;
-
-import cn.iocoder.yudao.framework.security.config.AuthorizeRequestsCustomizer;
-import cn.hutool.core.util.StrUtil;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.security.config.annotation.web.builders.HttpSecurity;
-import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
-
-/**
- * AI 妯″潡鐨� Security 閰嶇疆
- */
-@Configuration(proxyBeanMethods = false, value = "aiSecurityConfiguration")
-public class SecurityConfiguration {
-
- @Value("${spring.ai.mcp.server.sse-endpoint:/sse}")
- private String mcpSseEndpoint;
- @Value("${spring.ai.mcp.server.sse-message-endpoint:/mcp/message}")
- private String mcpSseMessageEndpoint;
- @Value("${spring.ai.mcp.server.streamable-http-endpoint:/mcp}")
- private String mcpStreamableHttpEndpoint;
-
- @Bean("aiAuthorizeRequestsCustomizer")
- public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {
- return new AuthorizeRequestsCustomizer() {
-
- @Override
- public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) {
- if (StrUtil.isNotBlank(mcpSseEndpoint)) {
- registry.requestMatchers(mcpSseEndpoint).permitAll();
- }
- if (StrUtil.isNotBlank(mcpSseMessageEndpoint)) {
- registry.requestMatchers(mcpSseMessageEndpoint).permitAll();
- }
- if (StrUtil.isNotBlank(mcpStreamableHttpEndpoint)) {
- registry.requestMatchers(mcpStreamableHttpEndpoint).permitAll();
- }
- }
-
- };
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/security/core/package-info.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/security/core/package-info.java
deleted file mode 100644
index 8796944..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/security/core/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * 鍗犱綅
- */
-package cn.iocoder.yudao.module.ai.framework.security.core;
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/web/config/AiWebConfiguration.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/web/config/AiWebConfiguration.java
index 2eb265d..03a98f4 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/web/config/AiWebConfiguration.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/web/config/AiWebConfiguration.java
@@ -6,16 +6,11 @@
import org.springframework.context.annotation.Configuration;
/**
- * ai 妯″潡鐨� web 缁勪欢鐨� Configuration
- *
- * @author 鑺嬮亾婧愮爜
+ * AI 妯″潡鐨� web 缁勪欢 Configuration
*/
@Configuration(proxyBeanMethods = false)
public class AiWebConfiguration {
- /**
- * ai 妯″潡鐨� API 鍒嗙粍
- */
@Bean
public GroupedOpenApi aiGroupedOpenApi() {
return YudaoSwaggerAutoConfiguration.buildGroupedOpenApi("ai");
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/web/package-info.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/web/package-info.java
deleted file mode 100644
index e979056..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/web/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * ai 妯″潡鐨� web 鎷撳睍灏佽
- */
-package cn.iocoder.yudao.module.ai.framework.web;
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/job/image/AiMidjourneySyncJob.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/job/image/AiMidjourneySyncJob.java
deleted file mode 100644
index 822e1e4..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/job/image/AiMidjourneySyncJob.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package cn.iocoder.yudao.module.ai.job.image;
-
-import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
-import cn.iocoder.yudao.module.ai.service.image.AiImageService;
-import jakarta.annotation.Resource;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Component;
-
-/**
- * Midjourney 鍚屾 Job锛氬畾鏃舵媺鍘� midjourney 缁樺埗鐘舵��
- *
- * @author fansili
- */
-@Component
-@Slf4j
-public class AiMidjourneySyncJob implements JobHandler {
-
- @Resource
- private AiImageService imageService;
-
- @Override
- public String execute(String param) {
- Integer count = imageService.midjourneySync();
- log.info("[execute][鍚屾 Midjourney ({}) 涓猐", count);
- return String.format("鍚屾 Midjourney %s 涓�", count);
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/job/music/AiSunoSyncJob.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/job/music/AiSunoSyncJob.java
deleted file mode 100644
index 6d85ec2..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/job/music/AiSunoSyncJob.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package cn.iocoder.yudao.module.ai.job.music;
-
-import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler;
-import cn.iocoder.yudao.module.ai.service.music.AiMusicService;
-import jakarta.annotation.Resource;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Component;
-
-
-/**
- * 鍚屾 Suno 浠诲姟鐘舵�佷互鍙婂洖鍐欏搴旂殑闊充箰淇℃伅 Job
- *
- * @author xiaoxin
- */
-@Component
-@Slf4j
-public class AiSunoSyncJob implements JobHandler {
-
- @Resource
- private AiMusicService musicService;
-
- @Override
- public String execute(String param) {
- Integer count = musicService.syncMusic();
- log.info("[execute][鍚屾 Suno ({}) 涓猐", count);
- return String.format("鍚屾 Suno %s 涓�", count);
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationService.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationService.java
deleted file mode 100644
index bce6d43..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationService.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package cn.iocoder.yudao.module.ai.service.chat;
-
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
-
-import java.util.List;
-
-/**
- * AI 鑱婂ぉ瀵硅瘽 Service 鎺ュ彛
- *
- * @author fansili
- */
-public interface AiChatConversationService {
-
- /**
- * 鍒涘缓銆愭垜鐨勩�戣亰澶╁璇�
- *
- * @param createReqVO 鍒涘缓淇℃伅
- * @param userId 鐢ㄦ埛缂栧彿
- * @return 缂栧彿
- */
- Long createChatConversationMy(AiChatConversationCreateMyReqVO createReqVO, Long userId);
-
- /**
- * 鏇存柊銆愭垜鐨勩�戣亰澶╁璇�
- *
- * @param updateReqVO 鏇存柊淇℃伅
- * @param userId 鐢ㄦ埛缂栧彿
- */
- void updateChatConversationMy(AiChatConversationUpdateMyReqVO updateReqVO, Long userId);
-
- /**
- * 鑾峰緱銆愭垜鐨勩�戣亰澶╁璇濆垪琛�
- *
- * @param userId 鐢ㄦ埛缂栧彿
- * @return 鑱婂ぉ瀵硅瘽鍒楄〃
- */
- List<AiChatConversationDO> getChatConversationListByUserId(Long userId);
-
- /**
- * 鑾峰緱鑱婂ぉ瀵硅瘽
- *
- * @param id 缂栧彿
- * @return 鑱婂ぉ瀵硅瘽
- */
- AiChatConversationDO getChatConversation(Long id);
-
- /**
- * 鍒犻櫎銆愭垜鐨勩�戣亰澶╁璇�
- *
- * @param id 缂栧彿
- * @param userId 鐢ㄦ埛缂栧彿
- */
- void deleteChatConversationMy(Long id, Long userId);
-
- /**
- * 銆愮鐞嗗憳銆戝垹闄よ亰澶╁璇�
- *
- * @param id 缂栧彿
- */
- void deleteChatConversationByAdmin(Long id);
-
- /**
- * 鏍¢獙鑱婂ぉ瀵硅瘽鏄惁瀛樺湪
- *
- * @param id 缂栧彿
- * @return 鑱婂ぉ瀵硅瘽
- */
- AiChatConversationDO validateChatConversationExists(Long id);
-
- /**
- * 鍒犻櫎銆愭垜鐨勩�� + 闈炵疆椤剁殑鑱婂ぉ瀵硅瘽
- *
- * @param userId 鐢ㄦ埛缂栧彿
- */
- void deleteChatConversationMyByUnpinned(Long userId);
-
- /**
- * 鑾峰緱鑱婂ぉ瀵硅瘽鐨勫垎椤靛垪琛�
- *
- * @param pageReqVO 鍒嗛〉鏌ヨ
- * @return 鑱婂ぉ瀵硅瘽鐨勫垎椤靛垪琛�
- */
- PageResult<AiChatConversationDO> getChatConversationPage(AiChatConversationPageReqVO pageReqVO);
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java
deleted file mode 100644
index 792ff74..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatConversationServiceImpl.java
+++ /dev/null
@@ -1,173 +0,0 @@
-package cn.iocoder.yudao.module.ai.service.chat;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.lang.Assert;
-import cn.hutool.core.util.ObjUtil;
-import cn.hutool.core.util.ObjectUtil;
-import cn.iocoder.yudao.module.ai.enums.model.AiModelTypeEnum;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
-import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatConversationMapper;
-import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService;
-import cn.iocoder.yudao.module.ai.service.model.AiModelService;
-import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
-import jakarta.annotation.Resource;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-import org.springframework.validation.annotation.Validated;
-
-import java.time.LocalDateTime;
-import java.util.List;
-import java.util.Objects;
-
-import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
-import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_MODEL_ERROR;
-import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS;
-
-/**
- * AI 鑱婂ぉ瀵硅瘽 Service 瀹炵幇绫�
- *
- * @author fansili
- */
-@Service
-@Validated
-@Slf4j
-public class AiChatConversationServiceImpl implements AiChatConversationService {
-
- @Resource
- private AiChatConversationMapper chatConversationMapper;
-
- @Resource
- private AiModelService modalService;
- @Resource
- private AiChatRoleService chatRoleService;
- @Resource
- private AiKnowledgeService knowledgeService;
-
- @Override
- public Long createChatConversationMy(AiChatConversationCreateMyReqVO createReqVO, Long userId) {
- // 1.1 鑾峰緱 AiChatRoleDO 鑱婂ぉ瑙掕壊
- AiChatRoleDO role = createReqVO.getRoleId() != null ? chatRoleService.validateChatRole(createReqVO.getRoleId()) : null;
- // 1.2 鑾峰緱 AiModelDO 鑱婂ぉ妯″瀷
- AiModelDO model = role != null && role.getModelId() != null ? modalService.validateModel(role.getModelId())
- : modalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType());
- Assert.notNull(model, "蹇呴』鎵惧埌榛樿妯″瀷");
- validateChatModel(model);
-
- // 1.3 鏍¢獙鐭ヨ瘑搴�
- if (Objects.nonNull(createReqVO.getKnowledgeId())) {
- knowledgeService.validateKnowledgeExists(createReqVO.getKnowledgeId());
- }
-
- // 2. 鍒涘缓 AiChatConversationDO 鑱婂ぉ瀵硅瘽
- AiChatConversationDO conversation = new AiChatConversationDO().setUserId(userId).setPinned(false)
- .setModelId(model.getId()).setModel(model.getModel())
- .setTemperature(model.getTemperature()).setMaxTokens(model.getMaxTokens()).setMaxContexts(model.getMaxContexts());
- if (role != null) {
- conversation.setTitle(role.getName()).setRoleId(role.getId()).setSystemMessage(role.getSystemMessage());
- } else {
- conversation.setTitle(AiChatConversationDO.TITLE_DEFAULT);
- }
- chatConversationMapper.insert(conversation);
- return conversation.getId();
- }
-
- @Override
- public void updateChatConversationMy(AiChatConversationUpdateMyReqVO updateReqVO, Long userId) {
- // 1.1 鏍¢獙瀵硅瘽鏄惁瀛樺湪
- AiChatConversationDO conversation = validateChatConversationExists(updateReqVO.getId());
- if (ObjUtil.notEqual(conversation.getUserId(), userId)) {
- throw exception(CHAT_CONVERSATION_NOT_EXISTS);
- }
- // 1.2 鏍¢獙妯″瀷鏄惁瀛樺湪锛堜慨鏀规ā鍨嬬殑鎯呭喌锛�
- AiModelDO model = null;
- if (updateReqVO.getModelId() != null) {
- model = modalService.validateModel(updateReqVO.getModelId());
- }
-
- // 1.3 鏍¢獙鐭ヨ瘑搴撴槸鍚﹀瓨鍦�
- if (updateReqVO.getKnowledgeId() != null) {
- knowledgeService.validateKnowledgeExists(updateReqVO.getKnowledgeId());
- }
-
- // 2. 鏇存柊瀵硅瘽淇℃伅
- AiChatConversationDO updateObj = BeanUtils.toBean(updateReqVO, AiChatConversationDO.class);
- if (Boolean.TRUE.equals(updateReqVO.getPinned())) {
- updateObj.setPinnedTime(LocalDateTime.now());
- }
- if (model != null) {
- updateObj.setModel(model.getModel());
- }
- chatConversationMapper.updateById(updateObj);
- }
-
- @Override
- public List<AiChatConversationDO> getChatConversationListByUserId(Long userId) {
- return chatConversationMapper.selectListByUserId(userId);
- }
-
- @Override
- public AiChatConversationDO getChatConversation(Long id) {
- return chatConversationMapper.selectById(id);
- }
-
- @Override
- public void deleteChatConversationMy(Long id, Long userId) {
- // 1. 鏍¢獙瀵硅瘽鏄惁瀛樺湪
- AiChatConversationDO conversation = validateChatConversationExists(id);
- if (conversation == null || ObjUtil.notEqual(conversation.getUserId(), userId)) {
- throw exception(CHAT_CONVERSATION_NOT_EXISTS);
- }
- // 2. 鎵ц鍒犻櫎
- chatConversationMapper.deleteById(id);
- }
-
- @Override
- public void deleteChatConversationByAdmin(Long id) {
- // 1. 鏍¢獙瀵硅瘽鏄惁瀛樺湪
- AiChatConversationDO conversation = validateChatConversationExists(id);
- if (conversation == null) {
- throw exception(CHAT_CONVERSATION_NOT_EXISTS);
- }
- // 2. 鎵ц鍒犻櫎
- chatConversationMapper.deleteById(id);
- }
-
- private void validateChatModel(AiModelDO model) {
- if (ObjectUtil.isAllNotEmpty(model.getTemperature(), model.getMaxTokens(), model.getMaxContexts())) {
- return;
- }
- Assert.equals(model.getType(), AiModelTypeEnum.CHAT.getType(), "妯″瀷绫诲瀷涓嶆纭細" + model);
- throw exception(CHAT_CONVERSATION_MODEL_ERROR);
- }
-
- public AiChatConversationDO validateChatConversationExists(Long id) {
- AiChatConversationDO conversation = chatConversationMapper.selectById(id);
- if (conversation == null) {
- throw exception(CHAT_CONVERSATION_NOT_EXISTS);
- }
- return conversation;
- }
-
- @Override
- public void deleteChatConversationMyByUnpinned(Long userId) {
- List<AiChatConversationDO> list = chatConversationMapper.selectListByUserIdAndPinned(userId, false);
- if (CollUtil.isEmpty(list)) {
- return;
- }
- chatConversationMapper.deleteByIds(convertList(list, AiChatConversationDO::getId));
- }
-
- @Override
- public PageResult<AiChatConversationDO> getChatConversationPage(AiChatConversationPageReqVO pageReqVO) {
- return chatConversationMapper.selectChatConversationPage(pageReqVO);
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageService.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageService.java
deleted file mode 100644
index f572bdd..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageService.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package cn.iocoder.yudao.module.ai.service.chat;
-
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
-import reactor.core.publisher.Flux;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-/**
- * AI 鑱婂ぉ娑堟伅 Service 鎺ュ彛
- *
- * @author fansili
- */
-public interface AiChatMessageService {
-
- /**
- * 鍙戦�佹秷鎭�
- *
- * @param sendReqVO 鍙戦�佷俊鎭�
- * @param userId 鐢ㄦ埛缂栧彿
- * @return 鍙戦�佺粨鏋�
- */
- AiChatMessageSendRespVO sendMessage(AiChatMessageSendReqVO sendReqVO, Long userId);
-
- /**
- * 鍙戦�佹秷鎭�
- *
- * @param sendReqVO 鍙戦�佷俊鎭�
- * @param userId 鐢ㄦ埛缂栧彿
- * @return 鍙戦�佺粨鏋�
- */
- Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO, Long userId);
-
- /**
- * 鑾峰緱鎸囧畾瀵硅瘽鐨勬秷鎭垪琛�
- *
- * @param conversationId 瀵硅瘽缂栧彿
- * @return 娑堟伅鍒楄〃
- */
- List<AiChatMessageDO> getChatMessageListByConversationId(Long conversationId);
-
- /**
- * 鍒犻櫎娑堟伅
- *
- * @param id 娑堟伅缂栧彿
- * @param userId 鐢ㄦ埛缂栧彿
- */
- void deleteChatMessage(Long id, Long userId);
-
- /**
- * 鍒犻櫎鎸囧畾瀵硅瘽鐨勬秷鎭�
- *
- * @param conversationId 瀵硅瘽缂栧彿
- * @param userId 鐢ㄦ埛缂栧彿
- */
- void deleteChatMessageByConversationId(Long conversationId, Long userId);
-
- /**
- * 銆愮鐞嗗憳銆戝垹闄ゆ秷鎭�
- *
- * @param id 娑堟伅缂栧彿
- */
- void deleteChatMessageByAdmin(Long id);
-
- /**
- * 鑾峰緱鑱婂ぉ瀵硅瘽鐨勬秷鎭暟閲� Map
- *
- * @param conversationIds 瀵硅瘽缂栧彿鏁扮粍
- * @return 娑堟伅鏁伴噺 Map
- */
- Map<Long, Integer> getChatMessageCountMap(Collection<Long> conversationIds);
-
- /**
- * 鑾峰緱鑱婂ぉ娑堟伅鐨勫垎椤�
- *
- * @param pageReqVO 鍒嗛〉鏌ヨ
- * @return 鑱婂ぉ娑堟伅鐨勫垎椤�
- */
- PageResult<AiChatMessageDO> getChatMessagePage(AiChatMessagePageReqVO pageReqVO);
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
deleted file mode 100644
index e211af0..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/chat/AiChatMessageServiceImpl.java
+++ /dev/null
@@ -1,572 +0,0 @@
-package cn.iocoder.yudao.module.ai.service.chat;
-
-import cn.hutool.core.codec.Base64;
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.io.file.FileNameUtil;
-import cn.hutool.core.util.ObjUtil;
-import cn.hutool.core.util.StrUtil;
-import cn.hutool.http.HttpUtil;
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO;
-import cn.iocoder.yudao.module.ai.dal.mysql.chat.AiChatMessageMapper;
-import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;
-import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
-import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchClient;
-import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchRequest;
-import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchResponse;
-import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;
-import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService;
-import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO;
-import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO;
-import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
-import cn.iocoder.yudao.module.ai.service.model.AiModelService;
-import cn.iocoder.yudao.module.ai.service.model.AiToolService;
-import cn.iocoder.yudao.module.ai.util.AiUtils;
-import cn.iocoder.yudao.module.ai.util.FileTypeUtils;
-import com.google.common.collect.Maps;
-import io.modelcontextprotocol.client.McpSyncClient;
-import jakarta.annotation.Resource;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.MessageType;
-import org.springframework.ai.chat.messages.SystemMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatModel;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.model.StreamingChatModel;
-import org.springframework.ai.chat.prompt.ChatOptions;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.mcp.SyncMcpToolCallbackProvider;
-import org.springframework.ai.tool.ToolCallback;
-import org.springframework.ai.tool.resolution.ToolCallbackResolver;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-import reactor.core.publisher.Flux;
-
-import java.time.LocalDateTime;
-import java.util.*;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.stream.Collectors;
-
-import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
-import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
-import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS;
-import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_MESSAGE_NOT_EXIST;
-
-/**
- * AI 鑱婂ぉ娑堟伅 Service 瀹炵幇绫�
- *
- * @author fansili
- */
-@Service
-@Slf4j
-public class AiChatMessageServiceImpl implements AiChatMessageService {
-
- /**
- * 鑱旂綉鎼滅储鐨勭粨鏉熸暟
- */
- private static final Integer WEB_SEARCH_COUNT = 10;
-
- // TODO @鑺嬭壙锛氬悗缁紭鍖栦笅瀵硅瘽鐨� Prompt 鏁翠綋缁撴瀯
-
- /**
- * 鐭ヨ瘑搴撹浆 {@link UserMessage} 鐨勫唴瀹规ā鐗�
- */
- private static final String KNOWLEDGE_USER_MESSAGE_TEMPLATE = "浣跨敤 <Reference></Reference> 鏍囪涓殑鍐呭浣滀负鏈瀵硅瘽鐨勫弬鑰�:\n\n" +
- "%s\n\n" + // 澶氫釜 <Reference></Reference> 鐨勬嫾鎺�
- "鍥炵瓟瑕佹眰锛歕n- 閬垮厤鎻愬強浣犳槸浠� <Reference></Reference> 鑾峰彇鐨勭煡璇嗐��";
-
- private static final String WEB_SEARCH_USER_MESSAGE_TEMPLATE = "浣跨敤 <WebSearch></WebSearch> 鏍囪涓殑鍐呭浣滀负鏈瀵硅瘽鐨勫弬鑰�:\n\n" +
- "%s\n\n" + // 澶氫釜 <WebSearch></WebSearch> 鐨勬嫾鎺�
- "鍥炵瓟瑕佹眰锛歕n- 閬垮厤鎻愬強浣犳槸浠� <WebSearch></WebSearch> 鑾峰彇鐨勭煡璇嗐��";
-
- /**
- * 闄勪欢杞� ${@link UserMessage} 鐨勫唴瀹规ā鐗�
- */
- @SuppressWarnings("TextBlockMigration")
- private static final String Attachment_USER_MESSAGE_TEMPLATE = "浣跨敤 <Attachment></Attachment> 鏍囪鐢ㄦ埛瀵硅瘽涓婁紶鐨勯檮浠跺唴瀹�:\n\n" +
- "%s\n\n" + // 澶氫釜 <Attachment></Attachment> 鐨勬嫾鎺�
- "鍥炵瓟瑕佹眰锛歕n- 閬垮厤鎻愬強 <Attachment></Attachment> 闄勪欢鐨勭紪鐮佹牸寮忋��";
-
- @Resource
- private AiChatMessageMapper chatMessageMapper;
-
- @Resource
- private AiChatConversationService chatConversationService;
- @Resource
- private AiChatRoleService chatRoleService;
- @Resource
- private AiModelService modalService;
- @Resource
- private AiKnowledgeSegmentService knowledgeSegmentService;
- @Resource
- private AiKnowledgeDocumentService knowledgeDocumentService;
- @Resource
- private AiToolService toolService;
-
- @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
- @Autowired(required = false) // 鐢变簬 yudao.ai.web-search.enable 閰嶇疆椤癸紝鍙互鍏抽棴 AiWebSearchClient 鐨勫姛鑳斤紝鎵�浠ヨ繖閲屽彧鑳戒笉寮哄埗娉ㄥ叆
- private AiWebSearchClient webSearchClient;
-
- @SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
- @Autowired(required = false) // 鐢变簬 yudao.ai.mcp.client.enable 閰嶇疆椤癸紝鍙互鍏抽棴 McpSyncClient 鐨勫姛鑳斤紝鎵�浠ヨ繖閲屽彧鑳戒笉寮哄埗娉ㄥ叆
- private List<McpSyncClient> mcpClients;
-
- @Value("${spring.ai.mcp.client.name:mcp}")
- private String mcpClientName;
-
- @Resource
- private ToolCallbackResolver toolCallbackResolver;
-
- @Transactional(rollbackFor = Exception.class)
- public AiChatMessageSendRespVO sendMessage(AiChatMessageSendReqVO sendReqVO, Long userId) {
- // 1.1 鏍¢獙瀵硅瘽瀛樺湪
- AiChatConversationDO conversation = chatConversationService
- .validateChatConversationExists(sendReqVO.getConversationId());
- if (ObjUtil.notEqual(conversation.getUserId(), userId)) {
- throw exception(CHAT_CONVERSATION_NOT_EXISTS);
- }
- List<AiChatMessageDO> historyMessages = chatMessageMapper.selectListByConversationId(conversation.getId());
- // 1.2 鏍¢獙妯″瀷
- AiModelDO model = modalService.validateModel(conversation.getModelId());
- ChatModel chatModel = modalService.getChatModel(model.getId());
-
- // 2.1 鐭ヨ瘑搴撳彫鍥�
- List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = recallKnowledgeSegment(
- sendReqVO.getContent(), conversation);
-
- // 2.2 鑱旂綉鎼滅储
- AiWebSearchResponse webSearchResponse = Boolean.TRUE.equals(sendReqVO.getUseSearch()) && webSearchClient != null ?
- webSearchClient.search(new AiWebSearchRequest().setQuery(sendReqVO.getContent())
- .setSummary(true).setCount(WEB_SEARCH_COUNT)) : null;
-
- // 3. 鎻掑叆 user 鍙戦�佹秷鎭�
- AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model,
- userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext(),
- null, sendReqVO.getAttachmentUrls(), null);
-
- // 4.1 鎻掑叆 assistant 鎺ユ敹娑堟伅
- AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model,
- userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext(),
- knowledgeSegments, null, webSearchResponse);
-
- // 4.2 鍒涘缓 chat 闇�瑕佺殑 Prompt
- Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, webSearchResponse, model, sendReqVO);
- ChatResponse chatResponse = chatModel.call(prompt);
-
- // 4.3 鏇存柊鍝嶅簲鍐呭
- String newContent = AiUtils.getChatResponseContent(chatResponse);
- String newReasoningContent = AiUtils.getChatResponseReasoningContent(chatResponse);
- chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId())
- .setContent(newContent).setReasoningContent(newReasoningContent));
- // 4.4 鍝嶅簲缁撴灉
- Map<Long, AiKnowledgeDocumentDO> documentMap = knowledgeDocumentService.getKnowledgeDocumentMap(
- convertSet(knowledgeSegments, AiKnowledgeSegmentSearchRespBO::getDocumentId));
- List<AiChatMessageRespVO.KnowledgeSegment> segments = BeanUtils.toBean(knowledgeSegments,
- AiChatMessageRespVO.KnowledgeSegment.class, segment -> {
- AiKnowledgeDocumentDO document = documentMap.get(segment.getDocumentId());
- segment.setDocumentName(document != null ? document.getName() : null);
- });
- return new AiChatMessageSendRespVO()
- .setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
- .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class)
- .setContent(newContent).setSegments(segments)
- .setWebSearchPages(webSearchResponse != null ? webSearchResponse.getLists() : null));
- }
-
- @Override
- public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO,
- Long userId) {
- // 1.1 鏍¢獙瀵硅瘽瀛樺湪
- AiChatConversationDO conversation = chatConversationService
- .validateChatConversationExists(sendReqVO.getConversationId());
- if (ObjUtil.notEqual(conversation.getUserId(), userId)) {
- throw exception(CHAT_CONVERSATION_NOT_EXISTS);
- }
- List<AiChatMessageDO> historyMessages = chatMessageMapper.selectListByConversationId(conversation.getId());
- // 1.2 鏍¢獙妯″瀷
- AiModelDO model = modalService.validateModel(conversation.getModelId());
- StreamingChatModel chatModel = modalService.getChatModel(model.getId());
-
- // 2.1 鐭ヨ瘑搴撴壘鍥�
- List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = recallKnowledgeSegment(
- sendReqVO.getContent(), conversation);
-
- // 2.2 鑱旂綉鎼滅储
- AiWebSearchResponse webSearchResponse = Boolean.TRUE.equals(sendReqVO.getUseSearch()) && webSearchClient != null ?
- webSearchClient.search(new AiWebSearchRequest().setQuery(sendReqVO.getContent())
- .setSummary(true).setCount(WEB_SEARCH_COUNT)) : null;
-
- // 3. 鎻掑叆 user 鍙戦�佹秷鎭�
- AiChatMessageDO userMessage = createChatMessage(conversation.getId(), null, model,
- userId, conversation.getRoleId(), MessageType.USER, sendReqVO.getContent(), sendReqVO.getUseContext(),
- null, sendReqVO.getAttachmentUrls(), null);
-
- // 4.1 鎻掑叆 assistant 鎺ユ敹娑堟伅
- AiChatMessageDO assistantMessage = createChatMessage(conversation.getId(), userMessage.getId(), model,
- userId, conversation.getRoleId(), MessageType.ASSISTANT, "", sendReqVO.getUseContext(),
- knowledgeSegments, null, webSearchResponse);
-
- // 4.2 鏋勫缓 Prompt锛屽苟杩涜璋冪敤
- Prompt prompt = buildPrompt(conversation, historyMessages, knowledgeSegments, webSearchResponse, model, sendReqVO);
- Flux<ChatResponse> streamResponse = chatModel.stream(prompt);
-
- // 4.3 娴佸紡杩斿洖
- StringBuffer contentBuffer = new StringBuffer();
- StringBuffer reasoningContentBuffer = new StringBuffer();
-
- // 闃叉鎵ц澶氭鐭ヨ瘑搴撳拰鑱旂綉鎼滅储
- AtomicBoolean firstExecuteFlag = new AtomicBoolean(true);
- AtomicReference<List<AiChatMessageRespVO.KnowledgeSegment>> cacheSegments = new AtomicReference<>();
- AtomicReference<List<AiWebSearchResponse.WebPage>> cacheWebSearchPages = new AtomicReference<>();
- return streamResponse.map(chunk -> {
- // 浠呴娆★細杩斿洖鐭ヨ瘑搴撱�佽仈缃戞悳绱�
- if (StrUtil.isEmpty(contentBuffer)) {
- if (firstExecuteFlag.compareAndSet(true, false)) { // CAS 鎿嶄綔锛岀‘淇濅粎鎵ц涓�娆�
- Map<Long, AiKnowledgeDocumentDO> documentMap = TenantUtils.executeIgnore(() -> knowledgeDocumentService.getKnowledgeDocumentMap(
- convertSet(knowledgeSegments, AiKnowledgeSegmentSearchRespBO::getDocumentId)));
- cacheSegments.set(BeanUtils.toBean(knowledgeSegments, AiChatMessageRespVO.KnowledgeSegment.class, segment -> {
- AiKnowledgeDocumentDO document = documentMap.get(segment.getDocumentId());
- segment.setDocumentName(document != null ? document.getName() : null);
- }));
- if (webSearchResponse != null) {
- cacheWebSearchPages.set(webSearchResponse.getLists());
- }
- }
- }
- // 鍝嶅簲缁撴灉
- String newContent = AiUtils.getChatResponseContent(chunk);
- String newReasoningContent = AiUtils.getChatResponseReasoningContent(chunk);
- if (StrUtil.isNotEmpty(newContent)) {
- contentBuffer.append(newContent);
- }
- if (StrUtil.isNotEmpty(newReasoningContent)) {
- reasoningContentBuffer.append(newReasoningContent);
- }
- return success(new AiChatMessageSendRespVO()
- .setSend(BeanUtils.toBean(userMessage, AiChatMessageSendRespVO.Message.class))
- .setReceive(BeanUtils.toBean(assistantMessage, AiChatMessageSendRespVO.Message.class)
- .setContent(StrUtil.nullToDefault(newContent, "")) // 閬垮厤 null 鐨� 鎯呭喌
- .setReasoningContent(StrUtil.nullToDefault(newReasoningContent, "")) // 閬垮厤 null 鐨� 鎯呭喌
- .setSegments(cacheSegments.get()).setWebSearchPages(cacheWebSearchPages.get()))); // 鐭ヨ瘑搴� + 鑱旂綉鎼滅储
- }).doOnComplete(() -> {
- // 蹇界暐绉熸埛锛屽洜涓� Flux 寮傛鏃犳硶閫忎紶绉熸埛
- TenantUtils.executeIgnore(() -> chatMessageMapper.updateById(
- new AiChatMessageDO().setId(assistantMessage.getId()).setContent(contentBuffer.toString())
- .setReasoningContent(reasoningContentBuffer.toString())));
- }).doOnError(throwable -> {
- log.error("[sendChatMessageStream][userId({}) sendReqVO({}) 鍙戠敓寮傚父]", userId, sendReqVO, throwable);
- // 蹇界暐绉熸埛锛屽洜涓� Flux 寮傛鏃犳硶閫忎紶绉熸埛
- TenantUtils.executeIgnore(() -> {
- // 濡傛灉鏈夊唴瀹癸紝鍒欐洿鏂板唴瀹�
- if (StrUtil.isNotEmpty(contentBuffer)) {
- chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId())
- .setContent(contentBuffer.toString()).setReasoningContent(reasoningContentBuffer.toString()));
- } else {
- // 鍚﹀垯锛屽垯杩涜鍒犻櫎
- chatMessageMapper.deleteById(assistantMessage.getId());
- }
- });
- }).doOnCancel(() -> {
- log.info("[sendChatMessageStream][userId({}) sendReqVO({}) 鍙栨秷璇锋眰]", userId, sendReqVO);
- // 蹇界暐绉熸埛锛屽洜涓� Flux 寮傛鏃犳硶閫忎紶绉熸埛
- TenantUtils.executeIgnore(() -> {
- // 濡傛灉鏈夊唴瀹癸紝鍒欐洿鏂板唴瀹�
- if (StrUtil.isNotEmpty(contentBuffer)) {
- chatMessageMapper.updateById(new AiChatMessageDO().setId(assistantMessage.getId())
- .setContent(contentBuffer.toString()).setReasoningContent(reasoningContentBuffer.toString()));
- } else {
- // 鍚﹀垯锛屽垯杩涜鍒犻櫎
- chatMessageMapper.deleteById(assistantMessage.getId());
- }
- });
- }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.CHAT_STREAM_ERROR)));
- }
-
- private List<AiKnowledgeSegmentSearchRespBO> recallKnowledgeSegment(String content,
- AiChatConversationDO conversation) {
- // 1. 鏌ヨ鑱婂ぉ瑙掕壊
- if (conversation == null || conversation.getRoleId() == null) {
- return Collections.emptyList();
- }
- AiChatRoleDO role = chatRoleService.getChatRole(conversation.getRoleId());
- if (role == null || CollUtil.isEmpty(role.getKnowledgeIds())) {
- return Collections.emptyList();
- }
-
- // 2. 閬嶅巻鍙洖
- List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments = new ArrayList<>();
- for (Long knowledgeId : role.getKnowledgeIds()) {
- knowledgeSegments.addAll(knowledgeSegmentService.searchKnowledgeSegment(new AiKnowledgeSegmentSearchReqBO()
- .setKnowledgeId(knowledgeId).setContent(content)));
- }
- return knowledgeSegments;
- }
-
- private Prompt buildPrompt(AiChatConversationDO conversation, List<AiChatMessageDO> messages,
- List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments,
- AiWebSearchResponse webSearchResponse,
- AiModelDO model, AiChatMessageSendReqVO sendReqVO) {
- List<Message> chatMessages = new ArrayList<>();
- // 1.1 System Context 瑙掕壊璁惧畾
- if (StrUtil.isNotBlank(conversation.getSystemMessage())) {
- chatMessages.add(new SystemMessage(conversation.getSystemMessage()));
- }
-
- // 1.2 鍘嗗彶 history message 鍘嗗彶娑堟伅
- List<AiChatMessageDO> contextMessages = filterContextMessages(messages, conversation, sendReqVO);
- contextMessages.forEach(message -> {
- chatMessages.add(AiUtils.buildMessage(message.getType(), message.getContent()));
- UserMessage attachmentUserMessage = buildAttachmentUserMessage(message.getAttachmentUrls());
- if (attachmentUserMessage != null) {
- chatMessages.add(attachmentUserMessage);
- }
- // TODO @鑺嬭壙锛氬巻鍙茬殑鐭ヨ瘑搴擄紱鍘嗗彶鐨勬悳绱紝瑕佷笉瑕佹嫾鎺ワ紵
- });
-
- // 1.3 褰撳墠 user message 鏂板彂閫佹秷鎭�
- chatMessages.add(new UserMessage(sendReqVO.getContent()));
-
- // 1.4 鐭ヨ瘑搴擄紝閫氳繃 UserMessage 瀹炵幇
- if (CollUtil.isNotEmpty(knowledgeSegments)) {
- String reference = knowledgeSegments.stream()
- .map(segment -> "<Reference>" + segment.getContent() + "</Reference>")
- .collect(Collectors.joining("\n\n"));
- chatMessages.add(new UserMessage(String.format(KNOWLEDGE_USER_MESSAGE_TEMPLATE, reference)));
- }
-
- // 1.5 鑱旂綉鎼滅储锛岄�氳繃 UserMessage 瀹炵幇
- if (webSearchResponse != null && CollUtil.isNotEmpty(webSearchResponse.getLists())) {
- String webSearch = webSearchResponse.getLists().stream()
- .map(page -> {
- String summary = StrUtil.isNotEmpty(page.getSummary()) ?
- "\nSummary: " + page.getSummary() : "";
- return "<WebSearch title=\"" + page.getTitle() + "\" url=\"" + page.getUrl() + "\">"
- + StrUtil.blankToDefault(page.getSummary(), page.getSnippet()) + "</WebSearch>";
- })
- .collect(Collectors.joining("\n\n"));
- chatMessages.add(new UserMessage(String.format(WEB_SEARCH_USER_MESSAGE_TEMPLATE, webSearch)));
- }
-
- // 1.6 闄勪欢锛岄�氳繃 UserMessage 瀹炵幇
- if (CollUtil.isNotEmpty(sendReqVO.getAttachmentUrls())) {
- UserMessage attachmentUserMessage = buildAttachmentUserMessage(sendReqVO.getAttachmentUrls());
- if (attachmentUserMessage != null) {
- chatMessages.add(attachmentUserMessage);
- }
- }
-
- // 2.1 鏌ヨ tool 宸ュ叿
- List<ToolCallback> toolCallbacks = getToolCallbackListByRoleId(conversation.getRoleId());
- Map<String,Object> toolContext = CollUtil.isNotEmpty(toolCallbacks) ? AiUtils.buildCommonToolContext()
- : Map.of();
- // 2.2 鏋勫缓 ChatOptions 瀵硅薄
- AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
- ChatOptions chatOptions = AiUtils.buildChatOptions(platform, model.getModel(),
- conversation.getTemperature(), conversation.getMaxTokens(),
- toolCallbacks, toolContext);
- return new Prompt(chatMessages, chatOptions);
- }
-
- private List<ToolCallback> getToolCallbackListByRoleId(Long roleId) {
- if (roleId == null) {
- return null;
- }
- AiChatRoleDO chatRole = chatRoleService.getChatRole(roleId);
- if (chatRole == null) {
- return null;
- }
- List<ToolCallback> toolCallbacks = new ArrayList<>();
- // 1. 閫氳繃 toolIds
- if (CollUtil.isNotEmpty(chatRole.getToolIds())) {
- Set<String> toolNames = convertSet(toolService.getToolList(chatRole.getToolIds()), AiToolDO::getName);
- toolNames.forEach(toolName -> {
- ToolCallback toolCallback = toolCallbackResolver.resolve(toolName);
- if (toolCallback != null) {
- toolCallbacks.add(toolCallback);
- }
- });
- }
- // 2. 閫氳繃 mcpClients
- if (CollUtil.isNotEmpty(mcpClients) && CollUtil.isNotEmpty(chatRole.getMcpClientNames())) {
- chatRole.getMcpClientNames().forEach(mcpClientName -> {
- // 2.1 鏍囧噯鍖栧悕瀛楋紝鍙傝�� McpClientAutoConfiguration 鐨� connectedClientName 鏂规硶
- String finalMcpClientName = this.mcpClientName + " - " + mcpClientName;
- // 2.2 鍖归厤瀵瑰簲鐨� McpSyncClient
- mcpClients.forEach(mcpClient -> {
- if (ObjUtil.notEqual(mcpClient.getClientInfo().name(), finalMcpClientName)) {
- return;
- }
- ToolCallback[] mcpToolCallBacks = SyncMcpToolCallbackProvider.builder()
- .mcpClients(mcpClient)
- .build()
- .getToolCallbacks();
- CollUtil.addAll(toolCallbacks, mcpToolCallBacks);
- });
- });
- }
- return toolCallbacks;
- }
-
- /**
- * 浠庡巻鍙叉秷鎭腑锛岃幏寰楀�掑簭鐨� n 缁勬秷鎭綔涓烘秷鎭笂涓嬫枃
- * <p>
- * n 缁勶細鎸囩殑鏄� user + assistant 褰㈡垚涓�缁�
- *
- * @param messages 娑堟伅鍒楄〃
- * @param conversation 瀵硅瘽
- * @param sendReqVO 鍙戦�佽姹�
- * @return 娑堟伅涓婁笅鏂�
- */
- private List<AiChatMessageDO> filterContextMessages(List<AiChatMessageDO> messages,
- AiChatConversationDO conversation,
- AiChatMessageSendReqVO sendReqVO) {
- if (conversation.getMaxContexts() == null || ObjUtil.notEqual(sendReqVO.getUseContext(), Boolean.TRUE)) {
- return Collections.emptyList();
- }
- List<AiChatMessageDO> contextMessages = new ArrayList<>(conversation.getMaxContexts() * 2);
- for (int i = messages.size() - 1; i >= 0; i--) {
- AiChatMessageDO assistantMessage = CollUtil.get(messages, i);
- if (assistantMessage == null || assistantMessage.getReplyId() == null) {
- continue;
- }
- AiChatMessageDO userMessage = CollUtil.get(messages, i - 1);
- if (userMessage == null
- || ObjUtil.notEqual(assistantMessage.getReplyId(), userMessage.getId())
- || StrUtil.isEmpty(assistantMessage.getContent())) {
- continue;
- }
- // 鐢变簬鍚庣画瑕� reverse 鍙嶈浆锛屾墍浠ュ厛娣诲姞 assistantMessage
- contextMessages.add(assistantMessage);
- contextMessages.add(userMessage);
- // 瓒呰繃鏈�澶т笂涓嬫枃锛岀粨鏉�
- if (contextMessages.size() >= conversation.getMaxContexts() * 2) {
- break;
- }
- }
- Collections.reverse(contextMessages);
- return contextMessages;
- }
-
- private UserMessage buildAttachmentUserMessage(List<String> attachmentUrls) {
- if (CollUtil.isEmpty(attachmentUrls)) {
- return null;
- }
- // 璇诲彇鏂囦欢鍐呭
- Map<String, String> attachmentContents = Maps.newLinkedHashMapWithExpectedSize(attachmentUrls.size());
- for (String attachmentUrl : attachmentUrls) {
- try {
- String name = FileNameUtil.getName(attachmentUrl);
- String mineType = FileTypeUtils.getMineType(name);
- String content;
- if (FileTypeUtils.isImage(mineType)) {
- // 鐗规畩锛氬浘鐗囧垯杞负 Base64
- byte[] bytes = HttpUtil.downloadBytes(attachmentUrl);
- content = Base64.encode(bytes);
- } else {
- content = knowledgeDocumentService.readUrl(attachmentUrl);
- }
- if (StrUtil.isNotEmpty(content)) {
- attachmentContents.put(name, content);
- }
- } catch (Exception e) {
- log.error("[buildAttachmentUserMessage][璇诲彇闄勪欢({}) 鍙戠敓寮傚父]", attachmentUrl, e);
- }
- }
- if (CollUtil.isEmpty(attachmentContents)) {
- return null;
- }
-
- // 鎷兼帴 UserMessage 娑堟伅
- String attachment = attachmentContents.entrySet().stream()
- .map(entry -> "<Attachment name=\"" + entry.getKey() + "\">" + entry.getValue() + "</Attachment>")
- .collect(Collectors.joining("\n\n"));
- return new UserMessage(String.format(Attachment_USER_MESSAGE_TEMPLATE, attachment));
- }
-
- private AiChatMessageDO createChatMessage(Long conversationId, Long replyId,
- AiModelDO model, Long userId, Long roleId,
- MessageType messageType, String content, Boolean useContext,
- List<AiKnowledgeSegmentSearchRespBO> knowledgeSegments,
- List<String> attachmentUrls,
- AiWebSearchResponse webSearchResponse) {
- AiChatMessageDO message = new AiChatMessageDO().setConversationId(conversationId).setReplyId(replyId)
- .setModel(model.getModel()).setModelId(model.getId()).setUserId(userId).setRoleId(roleId)
- .setType(messageType.getValue()).setContent(content).setUseContext(useContext)
- .setSegmentIds(convertList(knowledgeSegments, AiKnowledgeSegmentSearchRespBO::getId))
- .setAttachmentUrls(attachmentUrls);
- if (webSearchResponse != null) {
- message.setWebSearchPages(webSearchResponse.getLists());
- }
- message.setCreateTime(LocalDateTime.now());
- chatMessageMapper.insert(message);
- return message;
- }
-
- @Override
- public List<AiChatMessageDO> getChatMessageListByConversationId(Long conversationId) {
- return chatMessageMapper.selectListByConversationId(conversationId);
- }
-
- @Override
- public void deleteChatMessage(Long id, Long userId) {
- // 1. 鏍¢獙娑堟伅瀛樺湪
- AiChatMessageDO message = chatMessageMapper.selectById(id);
- if (message == null || ObjUtil.notEqual(message.getUserId(), userId)) {
- throw exception(CHAT_MESSAGE_NOT_EXIST);
- }
- // 2. 鎵ц鍒犻櫎
- chatMessageMapper.deleteById(id);
- }
-
- @Override
- public void deleteChatMessageByConversationId(Long conversationId, Long userId) {
- // 1. 鏍¢獙娑堟伅瀛樺湪
- List<AiChatMessageDO> messages = chatMessageMapper.selectListByConversationId(conversationId);
- if (CollUtil.isEmpty(messages) || ObjUtil.notEqual(messages.getFirst().getUserId(), userId)) {
- throw exception(CHAT_MESSAGE_NOT_EXIST);
- }
- // 2. 鎵ц鍒犻櫎
- chatMessageMapper.deleteByIds(convertList(messages, AiChatMessageDO::getId));
- }
-
- @Override
- public void deleteChatMessageByAdmin(Long id) {
- // 1. 鏍¢獙娑堟伅瀛樺湪
- AiChatMessageDO message = chatMessageMapper.selectById(id);
- if (message == null) {
- throw exception(CHAT_MESSAGE_NOT_EXIST);
- }
- // 2. 鎵ц鍒犻櫎
- chatMessageMapper.deleteById(id);
- }
-
- @Override
- public Map<Long, Integer> getChatMessageCountMap(Collection<Long> conversationIds) {
- return chatMessageMapper.selectCountMapByConversationId(conversationIds);
- }
-
- @Override
- public PageResult<AiChatMessageDO> getChatMessagePage(AiChatMessagePageReqVO pageReqVO) {
- return chatMessageMapper.selectPage(pageReqVO);
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageService.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageService.java
deleted file mode 100644
index 694b8e1..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageService.java
+++ /dev/null
@@ -1,126 +0,0 @@
-package cn.iocoder.yudao.module.ai.service.image;
-
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.ai.controller.admin.image.vo.*;
-import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyActionReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyImagineReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
-import jakarta.validation.Valid;
-
-import java.util.List;
-
-/**
- * AI 缁樺浘 Service 鎺ュ彛
- *
- * @author fansili
- */
-public interface AiImageService {
-
- /**
- * 鑾峰彇銆愭垜鐨勩�戠粯鍥惧垎椤�
- *
- * @param userId 鐢ㄦ埛缂栧彿
- * @param pageReqVO 鍒嗛〉鏉′欢
- * @return 缁樺浘鍒嗛〉
- */
- PageResult<AiImageDO> getImagePageMy(Long userId, AiImagePageReqVO pageReqVO);
-
- /**
- * 鑾峰彇鍏紑鐨勭粯鍥惧垎椤�
- *
- * @param pageReqVO 鍒嗛〉鏉′欢
- * @return 缁樺浘鍒嗛〉
- */
- PageResult<AiImageDO> getImagePagePublic(AiImagePublicPageReqVO pageReqVO);
-
- /**
- * 鑾峰緱缁樺浘璁板綍
- *
- * @param id 缁樺浘缂栧彿
- * @return 缁樺浘璁板綍
- */
- AiImageDO getImage(Long id);
-
- /**
- * 鑾峰緱缁樺浘鍒楄〃
- *
- * @param ids 缁樺浘缂栧彿鏁扮粍
- * @return 缁樺浘璁板綍鍒楄〃
- */
- List<AiImageDO> getImageList(List<Long> ids);
-
- /**
- * 缁樺埗鍥剧墖
- *
- * @param userId 鐢ㄦ埛缂栧彿
- * @param drawReqVO 缁樺埗璇锋眰
- * @return 缁樼敾缂栧彿
- */
- Long drawImage(Long userId, AiImageDrawReqVO drawReqVO);
-
- /**
- * 鍒犻櫎銆愭垜鐨勩�戠粯鐢昏褰�
- *
- * @param id 缁樼敾缂栧彿
- * @param userId 鐢ㄦ埛缂栧彿
- */
- void deleteImageMy(Long id, Long userId);
-
- /**
- * 鑾峰緱缁樼敾鍒嗛〉
- *
- * @param pageReqVO 鍒嗛〉鏌ヨ
- * @return 缁樼敾鍒嗛〉
- */
- PageResult<AiImageDO> getImagePage(AiImagePageReqVO pageReqVO);
-
- /**
- * 鏇存柊缁樼敾
- *
- * @param updateReqVO 鏇存柊淇℃伅
- */
- void updateImage(@Valid AiImageUpdateReqVO updateReqVO);
-
- /**
- * 鍒犻櫎缁樼敾
- *
- * @param id 缂栧彿
- */
- void deleteImage(Long id);
-
- // ================ midjourney 涓撳睘 ================
-
- /**
- * 銆怣idjourney銆戠敓鎴愬浘鐗�
- *
- * @param userId 鐢ㄦ埛缂栧彿
- * @param reqVO 缁樺埗璇锋眰
- * @return 缁樼敾缂栧彿
- */
- Long midjourneyImagine(Long userId, AiMidjourneyImagineReqVO reqVO);
-
- /**
- * 銆怣idjourney銆戝悓姝ュ浘鐗囪繘灞�
- *
- * @return 鍚屾鎴愬姛鏁伴噺
- */
- Integer midjourneySync();
-
- /**
- * 銆怣idjourney銆戦�氱煡鍥剧墖杩涘睍
- *
- * @param notify 閫氱煡
- */
- void midjourneyNotify(MidjourneyApi.Notify notify);
-
- /**
- * 銆怣idjourney銆慉ction 鎿嶄綔(鏀惧ぇ銆佺缉灏忋�乁1銆乁2...)
- *
- * @param userId 鐢ㄦ埛缂栧彿
- * @param reqVO 缁樺埗璇锋眰
- * @return 缁樼敾缂栧彿
- */
- Long midjourneyAction(Long userId, AiMidjourneyActionReqVO reqVO);
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java
deleted file mode 100644
index 0163edb..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java
+++ /dev/null
@@ -1,365 +0,0 @@
-package cn.iocoder.yudao.module.ai.service.image;
-
-import cn.hutool.core.bean.BeanUtil;
-import cn.hutool.core.codec.Base64;
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.lang.Assert;
-import cn.hutool.core.map.MapUtil;
-import cn.hutool.core.util.ObjUtil;
-import cn.hutool.core.util.StrUtil;
-import cn.hutool.extra.spring.SpringUtil;
-import cn.hutool.http.HttpUtil;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImagePageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImagePublicPageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageUpdateReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyActionReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiMidjourneyImagineReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
-import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper;
-import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum;
-import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowImageOptions;
-import cn.iocoder.yudao.module.ai.service.model.AiModelService;
-import cn.iocoder.yudao.module.infra.api.file.FileApi;
-import com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions;
-import jakarta.annotation.Resource;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.ai.image.ImageModel;
-import org.springframework.ai.image.ImageOptions;
-import org.springframework.ai.image.ImagePrompt;
-import org.springframework.ai.image.ImageResponse;
-import org.springframework.ai.openai.OpenAiImageOptions;
-import org.springframework.ai.stabilityai.api.StabilityAiImageOptions;
-import org.springframework.scheduling.annotation.Async;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.time.LocalDateTime;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
-import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
-
-/**
- * AI 缁樼敾 Service 瀹炵幇绫�
- *
- * @author fansili
- */
-@Service
-@Slf4j
-public class AiImageServiceImpl implements AiImageService {
-
- @Resource
- private AiModelService modelService;
-
- @Resource
- private AiImageMapper imageMapper;
-
- @Resource
- private FileApi fileApi;
-
- @Override
- public PageResult<AiImageDO> getImagePageMy(Long userId, AiImagePageReqVO pageReqVO) {
- return imageMapper.selectPageMy(userId, pageReqVO);
- }
-
- @Override
- public PageResult<AiImageDO> getImagePagePublic(AiImagePublicPageReqVO pageReqVO) {
- return imageMapper.selectPage(pageReqVO);
- }
-
- @Override
- public AiImageDO getImage(Long id) {
- return imageMapper.selectById(id);
- }
-
- @Override
- public List<AiImageDO> getImageList(List<Long> ids) {
- if (CollUtil.isEmpty(ids)) {
- return Collections.emptyList();
- }
- return imageMapper.selectByIds(ids);
- }
-
- @Override
- public Long drawImage(Long userId, AiImageDrawReqVO drawReqVO) {
- // 1. 鏍¢獙妯″瀷
- AiModelDO model = modelService.validateModel(drawReqVO.getModelId());
-
- // 2. 淇濆瓨鏁版嵁搴�
- AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId)
- .setPlatform(model.getPlatform()).setModelId(model.getId()).setModel(model.getModel())
- .setPublicStatus(false).setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus());
- imageMapper.insert(image);
-
- // 3. 寮傛缁樺埗锛屽悗缁墠绔�氳繃杩斿洖鐨� id 杩涜杞缁撴灉
- getSelf().executeDrawImage(image, drawReqVO, model);
- return image.getId();
- }
-
- @Async
- @SuppressWarnings("ConstantValue")
- public void executeDrawImage(AiImageDO image, AiImageDrawReqVO reqVO, AiModelDO model) {
- try {
- // 1.1 鏋勫缓璇锋眰
- ImageOptions request = buildImageOptions(reqVO, model);
- // 1.2 鎵ц璇锋眰
- ImageModel imageModel = modelService.getImageModel(model.getId());
- ImageResponse response = imageModel.call(new ImagePrompt(reqVO.getPrompt(), request));
- if (response.getResult() == null) {
- throw new IllegalArgumentException("鐢熸垚缁撴灉涓虹┖");
- }
-
- // 2. 涓婁紶鍒版枃浠舵湇鍔�
- String b64Json = response.getResult().getOutput().getB64Json();
- byte[] fileContent = StrUtil.isNotEmpty(b64Json) ? Base64.decode(b64Json)
- : HttpUtil.downloadBytes(response.getResult().getOutput().getUrl());
- String filePath = fileApi.createFile(fileContent);
-
- // 3. 鏇存柊鏁版嵁搴�
- imageMapper.updateById(new AiImageDO().setId(image.getId()).setStatus(AiImageStatusEnum.SUCCESS.getStatus())
- .setPicUrl(filePath).setFinishTime(LocalDateTime.now()));
- } catch (Exception ex) {
- log.error("[executeDrawImage][image({}) 鐢熸垚寮傚父]", image, ex);
- imageMapper.updateById(new AiImageDO().setId(image.getId())
- .setStatus(AiImageStatusEnum.FAIL.getStatus())
- .setErrorMessage(ex.getMessage()).setFinishTime(LocalDateTime.now()));
- }
- }
-
- private static ImageOptions buildImageOptions(AiImageDrawReqVO draw, AiModelDO model) {
- if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.OPENAI.getPlatform())) {
- // https://platform.openai.com/docs/api-reference/images/create
- return OpenAiImageOptions.builder().model(model.getModel())
- .height(draw.getHeight()).width(draw.getWidth())
- .style(MapUtil.getStr(draw.getOptions(), "style")) // 椋庢牸
- .responseFormat("b64_json")
- .build();
- } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.SILICON_FLOW.getPlatform())) {
- // https://docs.siliconflow.cn/cn/api-reference/images/images-generations
- return SiliconFlowImageOptions.builder().model(model.getModel())
- .height(draw.getHeight()).width(draw.getWidth())
- .build();
- } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.STABLE_DIFFUSION.getPlatform())) {
- // https://platform.stability.ai/docs/api-reference#tag/SDXL-and-SD1.6/operation/textToImage
- // https://platform.stability.ai/docs/api-reference#tag/Text-to-Image/operation/textToImage
- return StabilityAiImageOptions.builder().model(model.getModel())
- .height(draw.getHeight()).width(draw.getWidth())
- .seed(Long.valueOf(draw.getOptions().get("seed")))
- .cfgScale(Float.valueOf(draw.getOptions().get("scale")))
- .steps(Integer.valueOf(draw.getOptions().get("steps")))
- .sampler(String.valueOf(draw.getOptions().get("sampler")))
- .stylePreset(String.valueOf(draw.getOptions().get("stylePreset")))
- .clipGuidancePreset(String.valueOf(draw.getOptions().get("clipGuidancePreset")))
- .build();
- } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.TONG_YI.getPlatform())) {
- return DashScopeImageOptions.builder()
- .model(model.getModel()).n(1)
- .height(draw.getHeight()).width(draw.getWidth())
- .build();
- }
- throw new IllegalArgumentException("涓嶆敮鎸佺殑 AI 骞冲彴锛�" + model.getPlatform());
- }
-
- @Override
- public void deleteImageMy(Long id, Long userId) {
- // 1. 鏍¢獙鏄惁瀛樺湪
- AiImageDO image = validateImageExists(id);
- if (ObjUtil.notEqual(image.getUserId(), userId)) {
- throw exception(IMAGE_NOT_EXISTS);
- }
- // 2. 鍒犻櫎璁板綍
- imageMapper.deleteById(id);
- }
-
- @Override
- public PageResult<AiImageDO> getImagePage(AiImagePageReqVO pageReqVO) {
- return imageMapper.selectPage(pageReqVO);
- }
-
- @Override
- public void updateImage(AiImageUpdateReqVO updateReqVO) {
- // 1. 鏍¢獙瀛樺湪
- validateImageExists(updateReqVO.getId());
- // 2. 鏇存柊鍙戝竷鐘舵��
- imageMapper.updateById(BeanUtils.toBean(updateReqVO, AiImageDO.class));
- }
-
- @Override
- public void deleteImage(Long id) {
- // 1. 鏍¢獙瀛樺湪
- validateImageExists(id);
- // 2. 鍒犻櫎
- imageMapper.deleteById(id);
- }
-
- private AiImageDO validateImageExists(Long id) {
- AiImageDO image = imageMapper.selectById(id);
- if (image == null) {
- throw exception(IMAGE_NOT_EXISTS);
- }
- return image;
- }
-
- // ================ midjourney 涓撳睘 ================
-
- @Override
- @Transactional(rollbackFor = Exception.class)
- public Long midjourneyImagine(Long userId, AiMidjourneyImagineReqVO drawReqVO) {
- // 1. 鏍¢獙妯″瀷
- AiModelDO model = modelService.validateModel(drawReqVO.getModelId());
- Assert.equals(model.getPlatform(), AiPlatformEnum.MIDJOURNEY.getPlatform(), "骞冲彴涓嶅尮閰�");
- MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(model.getId());
-
- // 2. 淇濆瓨鏁版嵁搴�
- AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false)
- .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus())
- .setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform()).setModelId(model.getId()).setModel(model.getName());
- imageMapper.insert(image);
-
- // 3. 璋冪敤 Midjourney Proxy 鎻愪氦浠诲姟
- List<String> base64Array = StrUtil.isBlank(drawReqVO.getReferImageUrl()) ? null :
- Collections.singletonList("data:image/jpeg;base64,".concat(Base64.encode(HttpUtil.downloadBytes(drawReqVO.getReferImageUrl()))));
- MidjourneyApi.ImagineRequest imagineRequest = new MidjourneyApi.ImagineRequest(
- base64Array, drawReqVO.getPrompt(),null,
- MidjourneyApi.ImagineRequest.buildState(drawReqVO.getWidth(),
- drawReqVO.getHeight(), drawReqVO.getVersion(), model.getModel()));
- MidjourneyApi.SubmitResponse imagineResponse = midjourneyApi.imagine(imagineRequest);
-
- // 4.1 鎯呭喌涓�銆愬け璐ャ�戯細鎶涘嚭涓氬姟寮傚父
- if (!MidjourneyApi.SubmitCodeEnum.SUCCESS_CODES.contains(imagineResponse.code())) {
- String description = imagineResponse.description().contains("quota_not_enough") ?
- "璐︽埛浣欓涓嶈冻" : imagineResponse.description();
- throw exception(IMAGE_MIDJOURNEY_SUBMIT_FAIL, description);
- }
-
- // 4.2 鎯呭喌浜屻�愭垚鍔熴�戯細鏇存柊 taskId 鍜屽弬鏁�
- imageMapper.updateById(new AiImageDO().setId(image.getId())
- .setTaskId(imagineResponse.result()).setOptions(BeanUtil.beanToMap(drawReqVO)));
- return image.getId();
- }
-
- @Override
- public Integer midjourneySync() {
- // 1.1 鑾峰彇 Midjourney 骞冲彴锛岀姸鎬佸湪 鈥滆繘琛屼腑鈥� 鐨� image
- List<AiImageDO> images = imageMapper.selectListByStatusAndPlatform(
- AiImageStatusEnum.IN_PROGRESS.getStatus(), AiPlatformEnum.MIDJOURNEY.getPlatform());
- if (CollUtil.isEmpty(images)) {
- return 0;
- }
- // 1.2 璋冪敤 Midjourney Proxy 鑾峰彇浠诲姟杩涘睍
- MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(images.get(0).getModelId());
- List<MidjourneyApi.Notify> taskList = midjourneyApi.getTaskList(convertSet(images, AiImageDO::getTaskId));
- Map<String, MidjourneyApi.Notify> taskMap = convertMap(taskList, MidjourneyApi.Notify::id);
-
- // 2. 閫愪釜澶勭悊锛屾洿鏂拌繘灞�
- int count = 0;
- for (AiImageDO image : images) {
- MidjourneyApi.Notify notify = taskMap.get(image.getTaskId());
- if (notify == null) {
- log.error("[midjourneySync][image({}) 鏌ヨ涓嶅埌杩涘睍]", image);
- continue;
- }
- count++;
- updateMidjourneyStatus(image, notify);
- }
- return count;
- }
-
- @Override
- public void midjourneyNotify(MidjourneyApi.Notify notify) {
- // 1. 鏍¢獙 image 瀛樺湪
- AiImageDO image = imageMapper.selectByTaskId(notify.id());
- if (image == null) {
- log.warn("[midjourneyNotify][鍥炶皟浠诲姟({}) 涓嶅瓨鍦╙", notify.id());
- return;
- }
- // 2. 鏇存柊鐘舵��
- updateMidjourneyStatus(image, notify);
- }
-
- private void updateMidjourneyStatus(AiImageDO image, MidjourneyApi.Notify notify) {
- // 1. 杞崲鐘舵��
- Integer status = null;
- LocalDateTime finishTime = null;
- if (StrUtil.isNotBlank(notify.status())) {
- MidjourneyApi.TaskStatusEnum taskStatusEnum = MidjourneyApi.TaskStatusEnum.valueOf(notify.status());
- if (MidjourneyApi.TaskStatusEnum.SUCCESS == taskStatusEnum) {
- status = AiImageStatusEnum.SUCCESS.getStatus();
- finishTime = LocalDateTime.now();
- } else if (MidjourneyApi.TaskStatusEnum.FAILURE == taskStatusEnum) {
- status = AiImageStatusEnum.FAIL.getStatus();
- finishTime = LocalDateTime.now();
- }
- }
-
- // 2. 涓婁紶鍥剧墖
- String picUrl = null;
- if (StrUtil.isNotBlank(notify.imageUrl())) {
- try {
- picUrl = fileApi.createFile(HttpUtil.downloadBytes(notify.imageUrl()));
- } catch (Exception e) {
- picUrl = notify.imageUrl();
- log.warn("[updateMidjourneyStatus][鍥剧墖({}) 鍦板潃({}) 涓婁紶澶辫触]", image.getId(), notify.imageUrl(), e);
- }
- }
-
- // 3. 鏇存柊 image 鐘舵��
- imageMapper.updateById(new AiImageDO().setId(image.getId()).setStatus(status)
- .setPicUrl(picUrl).setButtons(notify.buttons()).setErrorMessage(notify.failReason())
- .setFinishTime(finishTime));
- }
-
- @Override
- public Long midjourneyAction(Long userId, AiMidjourneyActionReqVO reqVO) {
- // 1.1 妫�鏌� image
- AiImageDO image = validateImageExists(reqVO.getId());
- if (ObjUtil.notEqual(userId, image.getUserId())) {
- throw exception(IMAGE_NOT_EXISTS);
- }
- MidjourneyApi midjourneyApi = modelService.getMidjourneyApi(image.getModelId());
- // 1.2 妫�鏌� customId
- MidjourneyApi.Button button = CollUtil.findOne(image.getButtons(),
- buttonX -> buttonX.customId().equals(reqVO.getCustomId()));
- if (button == null) {
- throw exception(IMAGE_CUSTOM_ID_NOT_EXISTS);
- }
-
- // 2. 璋冪敤 Midjourney Proxy 鎻愪氦浠诲姟
- MidjourneyApi.SubmitResponse actionResponse = midjourneyApi.action(
- new MidjourneyApi.ActionRequest(button.customId(), image.getTaskId(), null));
- if (!MidjourneyApi.SubmitCodeEnum.SUCCESS_CODES.contains(actionResponse.code())) {
- String description = actionResponse.description().contains("quota_not_enough") ?
- "璐︽埛浣欓涓嶈冻" : actionResponse.description();
- throw exception(IMAGE_MIDJOURNEY_SUBMIT_FAIL, description);
- }
-
- // 3. 鏂板 image 璁板綍
- AiImageDO newImage = new AiImageDO().setUserId(image.getUserId()).setPublicStatus(false).setPrompt(image.getPrompt())
- .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus())
- .setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform())
- .setModel(image.getModel()).setWidth(image.getWidth()).setHeight(image.getHeight())
- .setOptions(image.getOptions()).setTaskId(actionResponse.result());
- imageMapper.insert(newImage);
- return newImage.getId();
- }
-
- /**
- * 鑾峰緱鑷韩鐨勪唬鐞嗗璞★紝瑙e喅 AOP 鐢熸晥闂
- *
- * @return 鑷繁
- */
- private AiImageServiceImpl getSelf() {
- return SpringUtil.getBean(getClass());
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java
index 66155d7..51c5121 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeDocumentService.java
@@ -1,126 +1,41 @@
package cn.iocoder.yudao.module.ai.service.knowledge;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentCreateListReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateStatusReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.*;
+import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
+import jakarta.validation.Valid;
-import java.util.Collection;
import java.util.List;
-import java.util.Map;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
-
-/**
- * AI 鐭ヨ瘑搴撴枃妗� Service 鎺ュ彛
- *
- * @author xiaoxin
- */
public interface AiKnowledgeDocumentService {
- /**
- * 鍒涘缓鏂囨。锛堝崟涓級
- *
- * @param createReqVO 鏂囨。鍒涘缓 Request VO
- * @return 鏂囨。缂栧彿
- */
- Long createKnowledgeDocument(AiKnowledgeDocumentCreateReqVO createReqVO);
+ List<Long> createDocuments(@Valid AiKnowledgeDocumentCreateReqVO createReqVO);
+
+ void updateDocument(@Valid AiKnowledgeDocumentUpdateReqVO updateReqVO);
+
+ void deleteDocument(Long id);
+
+ AiKnowledgeDocumentDO getDocument(Long id);
+
+ AiKnowledgeDocumentDO validateDocumentExists(Long id);
+
+ PageResult<AiKnowledgeDocumentDO> getDocumentPage(AiKnowledgeDocumentPageReqVO pageReqVO);
+
+ void updateDocumentStatus(@Valid AiKnowledgeDocumentUpdateStatusReqVO updateStatusReqVO);
+
+ List<AiKnowledgeSegmentProcessRespVO> getDocumentProcessingProgress(List<Long> documentIds);
+
+ void processDocumentSegments(Long documentId);
/**
- * 鍒涘缓鏂囨。锛堝涓級
+ * 棰勮鏂囨。鍒囧垎缁撴灉锛堜笉淇濆瓨锛�
*
- * @param createListReqVO 鎵归噺鍒涘缓 Request VO
- * @return 鏂囨。缂栧彿鍒楄〃
+ * @param url 鏂囨。 URL
+ * @param name 鏂囨。鍚嶇О
+ * @param segmentMaxTokens 鍒嗘鏈�澶� Token 鏁�
+ * @return 鍒囧垎鍚庣殑鍒嗘鏂囨湰鍒楄〃
*/
- List<Long> createKnowledgeDocumentList(AiKnowledgeDocumentCreateListReqVO createListReqVO);
-
- /**
- * 鑾峰彇鏂囨。鍒嗛〉
- *
- * @param pageReqVO 鍒嗛〉鍙傛暟
- * @return 鏂囨。鍒嗛〉
- */
- PageResult<AiKnowledgeDocumentDO> getKnowledgeDocumentPage(AiKnowledgeDocumentPageReqVO pageReqVO);
-
- /**
- * 鑾峰彇鏂囨。璇︽儏
- *
- * @param id 鏂囨。缂栧彿
- * @return 鏂囨。璇︽儏
- */
- AiKnowledgeDocumentDO getKnowledgeDocument(Long id);
-
- /**
- * 鏇存柊鏂囨。
- *
- * @param reqVO 鏇存柊淇℃伅
- */
- void updateKnowledgeDocument(AiKnowledgeDocumentUpdateReqVO reqVO);
-
- /**
- * 鏇存柊鏂囨。鐘舵��
- *
- * @param reqVO 鏇存柊鐘舵�佷俊鎭�
- */
- void updateKnowledgeDocumentStatus(AiKnowledgeDocumentUpdateStatusReqVO reqVO);
-
- /**
- * 鍒犻櫎鏂囨。
- *
- * @param id 鏂囨。缂栧彿
- */
- void deleteKnowledgeDocument(Long id);
-
- /**
- * 鏍规嵁鐭ヨ瘑搴撶紪鍙凤紝鎵归噺鍒犻櫎鏂囨。
- *
- * @param knowledgeId 鐭ヨ瘑搴撶紪鍙�
- */
- void deleteKnowledgeDocumentByKnowledgeId(Long knowledgeId);
-
- /**
- * 鏍¢獙鏂囨。鏄惁瀛樺湪
- *
- * @param id 鏂囨。缂栧彿
- * @return 鏂囨。淇℃伅
- */
- AiKnowledgeDocumentDO validateKnowledgeDocumentExists(Long id);
-
- /**
- * 璇诲彇 URL 鍐呭
- *
- * @param url URL
- * @return 鍐呭
- */
- String readUrl(String url);
-
- /**
- * 鑾峰彇鏂囨。鍒楄〃
- *
- * @param ids 鏂囨。缂栧彿鍒楄〃
- * @return 鏂囨。鍒楄〃
- */
- List<AiKnowledgeDocumentDO> getKnowledgeDocumentList(Collection<Long> ids);
-
- /**
- * 鏍规嵁鐭ヨ瘑搴撶紪鍙疯幏鍙栨枃妗e垪琛�
- *
- * @param knowledgeId 鐭ヨ瘑搴撶紪鍙�
- * @return 鏂囨。鍒楄〃
- */
- List<AiKnowledgeDocumentDO> getKnowledgeDocumentListByKnowledgeId(Long knowledgeId);
-
- /**
- * 鑾峰彇鏂囨。 Map
- *
- * @param ids 鏂囨。缂栧彿鍒楄〃
- * @return 鏂囨。 Map
- */
- default Map<Long, AiKnowledgeDocumentDO> getKnowledgeDocumentMap(Collection<Long> ids) {
- return convertMap(getKnowledgeDocumentList(ids), AiKnowledgeDocumentDO::getId);
- }
+ List<String> previewSplit(String url, String name, Integer segmentMaxTokens);
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java
index 9894e73..4e70bf5 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeSegmentService.java
@@ -1,157 +1,33 @@
package cn.iocoder.yudao.module.ai.service.knowledge;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentProcessRespVO;
-import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentSaveReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO;
+import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.*;
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO;
import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO;
-import org.springframework.scheduling.annotation.Async;
-import java.util.Collection;
import java.util.List;
-import java.util.Map;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
-
-/**
- * AI 鐭ヨ瘑搴撴钀� Service 鎺ュ彛
- *
- * @author xiaoxin
- */
public interface AiKnowledgeSegmentService {
- /**
- * 鑾峰彇鐭ヨ瘑搴撴钀借鎯�
- *
- * @param id 娈佃惤缂栧彿
- * @return 娈佃惤璇︽儏
- */
- AiKnowledgeSegmentDO getKnowledgeSegment(Long id);
+ AiKnowledgeSegmentDO getSegment(Long id);
- /**
- * 鑾峰彇鐭ヨ瘑搴撴钀藉垪琛�
- *
- * @param ids 娈佃惤缂栧彿鍒楄〃
- * @return 娈佃惤鍒楄〃
- */
- List<AiKnowledgeSegmentDO> getKnowledgeSegmentList(Collection<Long> ids);
+ AiKnowledgeSegmentDO validateSegmentExists(Long id);
- /**
- * 鑾峰彇鐭ヨ瘑搴撴钀� Map
- *
- * @param ids 娈佃惤缂栧彿鍒楄〃
- * @return 娈佃惤 Map
- */
- default Map<Long, AiKnowledgeSegmentDO> getKnowledgeSegmentMap(Collection<Long> ids) {
- return convertMap(getKnowledgeSegmentList(ids), AiKnowledgeSegmentDO::getId);
- }
+ PageResult<AiKnowledgeSegmentDO> getSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO);
- /**
- * 鑾峰彇娈佃惤鍒嗛〉
- *
- * @param pageReqVO 鍒嗛〉鏌ヨ
- * @return 鏂囨。鍒嗛〉
- */
- PageResult<AiKnowledgeSegmentDO> getKnowledgeSegmentPage(AiKnowledgeSegmentPageReqVO pageReqVO);
+ Long createSegment(AiKnowledgeSegmentSaveReqVO saveReqVO);
- /**
- * 鍩轰簬 content 鍐呭锛屽垏鐗囧垱寤哄涓钀�
- *
- * @param documentId 鐭ヨ瘑搴撴枃妗g紪鍙�
- * @param content 鏂囨。鍐呭
- */
- void createKnowledgeSegmentBySplitContent(Long documentId, String content);
+ void updateSegment(AiKnowledgeSegmentSaveReqVO saveReqVO);
- /**
- * 銆愬紓姝ャ�戝熀浜� content 鍐呭锛屽垏鐗囧垱寤哄涓钀�
- *
- * @param documentId 鐭ヨ瘑搴撴枃妗g紪鍙�
- * @param content 鏂囨。鍐呭
- */
- @Async
- default void createKnowledgeSegmentBySplitContentAsync(Long documentId, String content) {
- createKnowledgeSegmentBySplitContent(documentId, content);
- }
+ void deleteSegment(Long id);
- /**
- * 鍒涘缓鐭ヨ瘑搴撴钀�
- *
- * @param createReqVO 鍒涘缓淇℃伅
- * @return 娈佃惤缂栧彿
- */
- Long createKnowledgeSegment(AiKnowledgeSegmentSaveReqVO createReqVO);
+ void updateSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO updateStatusReqVO);
- /**
- * 鏇存柊娈佃惤鐨勫唴瀹�
- *
- * @param reqVO 鏇存柊鍐呭
- */
- void updateKnowledgeSegment(AiKnowledgeSegmentSaveReqVO reqVO);
+ void deleteSegmentsByDocumentId(Long documentId);
- /**
- * 鏇存柊娈佃惤鐨勭姸鎬�
- *
- * @param reqVO 鏇存柊鍐呭
- */
- void updateKnowledgeSegmentStatus(AiKnowledgeSegmentUpdateStatusReqVO reqVO);
+ void saveSegments(List<AiKnowledgeSegmentDO> segments, Long knowledgeId, Long embeddingModelId);
- /**
- * 鍒犻櫎鐭ヨ瘑搴撴钀�
- *
- * @param id 娈佃惤缂栧彿
- */
- void deleteKnowledgeSegment(Long id);
-
- /**
- * 閲嶆柊绱㈠紩鐭ヨ瘑搴撲笅鐨勬墍鏈夋枃妗f钀�
- *
- * @param knowledgeId 鐭ヨ瘑搴撶紪鍙�
- */
- void reindexKnowledgeSegmentByKnowledgeId(Long knowledgeId);
-
- /**
- * 銆愬紓姝ャ�戦噸鏂扮储寮曠煡璇嗗簱涓嬬殑鎵�鏈夋枃妗f钀�
- *
- * @param knowledgeId 鐭ヨ瘑搴撶紪鍙�
- */
- @Async
- default void reindexByKnowledgeIdAsync(Long knowledgeId) {
- reindexKnowledgeSegmentByKnowledgeId(knowledgeId);
- }
-
- /**
- * 鏍规嵁鏂囨。缂栧彿鍒犻櫎娈佃惤
- *
- * @param documentId 鏂囨。缂栧彿
- */
- void deleteKnowledgeSegmentByDocumentId(Long documentId);
-
- /**
- * 鎼滅储鐭ヨ瘑搴撴钀斤紝骞惰繑鍥炵粨鏋�
- *
- * @param reqBO 鎼滅储璇锋眰淇℃伅
- * @return 鎼滅储缁撴灉娈佃惤鍒楄〃
- */
- List<AiKnowledgeSegmentSearchRespBO> searchKnowledgeSegment(AiKnowledgeSegmentSearchReqBO reqBO);
-
- /**
- * 鏍规嵁 URL 鍐呭锛屽垏鐗囧垱寤哄涓钀�
- *
- * @param url URL 鍦板潃
- * @param segmentMaxTokens 娈佃惤鏈�澶� Token 鏁�
- * @return 鍒囩墖鍚庣殑娈佃惤鍒楄〃
- */
- List<AiKnowledgeSegmentDO> splitContent(String url, Integer segmentMaxTokens);
-
- /**
- * 鑾峰彇鏂囨。澶勭悊杩涘害锛堝涓級
- *
- * @param documentIds 鏂囨。缂栧彿鍒楄〃
- * @return 鏂囨。澶勭悊鍒楄〃
- */
- List<AiKnowledgeSegmentProcessRespVO> getKnowledgeSegmentProcessList(List<Long> documentIds);
+ List<AiKnowledgeSegmentSearchRespBO> searchSegments(AiKnowledgeSegmentSearchReqBO searchReqBO);
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java
index 6c552a4..3534430 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeService.java
@@ -4,67 +4,26 @@
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
+import jakarta.validation.Valid;
import java.util.List;
-/**
- * AI 鐭ヨ瘑搴�-鍩虹淇℃伅 Service 鎺ュ彛
- *
- * @author xiaoxin
- */
public interface AiKnowledgeService {
- /**
- * 鍒涘缓鐭ヨ瘑搴�
- *
- * @param createReqVO 鍒涘缓淇℃伅
- * @return 缂栧彿
- */
- Long createKnowledge(AiKnowledgeSaveReqVO createReqVO);
+ Long createKnowledge(@Valid AiKnowledgeSaveReqVO createReqVO);
- /**
- * 鏇存柊鐭ヨ瘑搴�
- *
- * @param updateReqVO 鏇存柊淇℃伅
- */
- void updateKnowledge(AiKnowledgeSaveReqVO updateReqVO);
+ void updateKnowledge(@Valid AiKnowledgeSaveReqVO updateReqVO);
- /**
- * 鍒犻櫎鐭ヨ瘑搴�
- *
- * @param id 鐭ヨ瘑搴撶紪鍙�
- */
void deleteKnowledge(Long id);
- /**
- * 鑾峰緱鐭ヨ瘑搴�
- *
- * @param id 缂栧彿
- * @return 鐭ヨ瘑搴�
- */
AiKnowledgeDO getKnowledge(Long id);
- /**
- * 鏍¢獙鐭ヨ瘑搴撴槸鍚﹀瓨鍦�
- *
- * @param id 璁板綍缂栧彿
- */
AiKnowledgeDO validateKnowledgeExists(Long id);
- /**
- * 鑾峰緱鐭ヨ瘑搴撳垎椤�
- *
- * @param pageReqVO 鍒嗛〉鏌ヨ
- * @return 鐭ヨ瘑搴撳垎椤�
- */
+ AiKnowledgeDO validateKnowledge(Long id);
+
PageResult<AiKnowledgeDO> getKnowledgePage(AiKnowledgePageReqVO pageReqVO);
- /**
- * 鑾峰緱鎸囧畾鐘舵�佺殑鐭ヨ瘑搴撳垪琛�
- *
- * @param status 鐘舵��
- * @return 鐭ヨ瘑搴撳垪琛�
- */
- List<AiKnowledgeDO> getKnowledgeSimpleListByStatus(Integer status);
+ List<AiKnowledgeDO> getEnabledKnowledgeList();
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
index 75b9943..163c2ad 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/AiKnowledgeServiceImpl.java
@@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.ai.service.knowledge;
-import cn.hutool.core.util.ObjUtil;
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
@@ -8,102 +8,83 @@
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
import cn.iocoder.yudao.module.ai.dal.mysql.knowledge.AiKnowledgeMapper;
+import cn.iocoder.yudao.module.ai.enums.model.AiModelTypeEnum;
import cn.iocoder.yudao.module.ai.service.model.AiModelService;
import jakarta.annotation.Resource;
-import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.KNOWLEDGE_NOT_EXISTS;
+import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
-/**
- * AI 鐭ヨ瘑搴�-鍩虹淇℃伅 Service 瀹炵幇绫�
- *
- * @author xiaoxin
- */
@Service
-@Slf4j
public class AiKnowledgeServiceImpl implements AiKnowledgeService {
@Resource
- private AiKnowledgeMapper knowledgeMapper;
-
+ private AiKnowledgeMapper mapper;
@Resource
private AiModelService modelService;
- @Resource
- private AiKnowledgeSegmentService knowledgeSegmentService;
- @Resource
- private AiKnowledgeDocumentService knowledgeDocumentService;
@Override
public Long createKnowledge(AiKnowledgeSaveReqVO createReqVO) {
- // 1. 鏍¢獙妯″瀷閰嶇疆
- AiModelDO model = modelService.validateModel(createReqVO.getEmbeddingModelId());
-
- // 2. 鎻掑叆鐭ヨ瘑搴�
- AiKnowledgeDO knowledge = BeanUtils.toBean(createReqVO, AiKnowledgeDO.class)
- .setEmbeddingModel(model.getModel());
- knowledgeMapper.insert(knowledge);
+ AiModelDO embeddingModel = modelService.validateModel(createReqVO.getEmbeddingModelId());
+ if (!AiModelTypeEnum.EMBEDDING.getType().equals(embeddingModel.getType())) {
+ throw exception(MODEL_USE_TYPE_ERROR);
+ }
+ AiKnowledgeDO knowledge = BeanUtils.toBean(createReqVO, AiKnowledgeDO.class);
+ knowledge.setEmbeddingModel(embeddingModel.getModel());
+ if (knowledge.getTopK() == null) knowledge.setTopK(3);
+ if (knowledge.getSimilarityThreshold() == null) knowledge.setSimilarityThreshold(0.7);
+ mapper.insert(knowledge);
return knowledge.getId();
}
@Override
public void updateKnowledge(AiKnowledgeSaveReqVO updateReqVO) {
- // 1.1 鏍¢獙鐭ヨ瘑搴撳瓨鍦�
- AiKnowledgeDO oldKnowledge = validateKnowledgeExists(updateReqVO.getId());
- // 1.2 鏍¢獙妯″瀷閰嶇疆
- AiModelDO model = modelService.validateModel(updateReqVO.getEmbeddingModelId());
-
- // 2. 鏇存柊鐭ヨ瘑搴�
- AiKnowledgeDO updateObj = BeanUtils.toBean(updateReqVO, AiKnowledgeDO.class)
- .setEmbeddingModel(model.getModel());
- knowledgeMapper.updateById(updateObj);
-
- // 3. 濡傛灉妯″瀷鍙樺寲锛岄渶瑕� reindex 鎵�鏈夌殑鏂囨。
- if (ObjUtil.notEqual(oldKnowledge.getEmbeddingModelId(), updateReqVO.getEmbeddingModelId())) {
- knowledgeSegmentService.reindexByKnowledgeIdAsync(updateReqVO.getId());
+ validateKnowledgeExists(updateReqVO.getId());
+ AiModelDO embeddingModel = modelService.validateModel(updateReqVO.getEmbeddingModelId());
+ if (!AiModelTypeEnum.EMBEDDING.getType().equals(embeddingModel.getType())) {
+ throw exception(MODEL_USE_TYPE_ERROR);
}
+ AiKnowledgeDO knowledge = BeanUtils.toBean(updateReqVO, AiKnowledgeDO.class);
+ knowledge.setEmbeddingModel(embeddingModel.getModel());
+ mapper.updateById(knowledge);
}
@Override
- @Transactional(rollbackFor = Exception.class)
public void deleteKnowledge(Long id) {
- // 1. 鏍¢獙瀛樺湪
validateKnowledgeExists(id);
-
- // 2. 鍒犻櫎鐭ヨ瘑搴撲笅鐨勬墍鏈夋枃妗e強娈佃惤
- knowledgeDocumentService.deleteKnowledgeDocumentByKnowledgeId(id);
-
- // 3. 鍒犻櫎鐭ヨ瘑搴�
- // 鐗规畩锛氱煡璇嗗簱闇�瑕佹渶鍚庡垹闄わ紝涓嶇劧鐩稿叧鐨勯厤缃細鎵句笉鍒�
- knowledgeMapper.deleteById(id);
+ mapper.deleteById(id);
}
@Override
public AiKnowledgeDO getKnowledge(Long id) {
- return knowledgeMapper.selectById(id);
+ return mapper.selectById(id);
}
@Override
public AiKnowledgeDO validateKnowledgeExists(Long id) {
- AiKnowledgeDO knowledge = knowledgeMapper.selectById(id);
- if (knowledge == null) {
- throw exception(KNOWLEDGE_NOT_EXISTS);
- }
+ AiKnowledgeDO knowledge = mapper.selectById(id);
+ if (knowledge == null) throw exception(KNOWLEDGE_NOT_EXISTS);
+ return knowledge;
+ }
+
+ @Override
+ public AiKnowledgeDO validateKnowledge(Long id) {
+ AiKnowledgeDO knowledge = validateKnowledgeExists(id);
+ if (CommonStatusEnum.isDisable(knowledge.getStatus())) throw exception(KNOWLEDGE_NOT_EXISTS);
return knowledge;
}
@Override
public PageResult<AiKnowledgeDO> getKnowledgePage(AiKnowledgePageReqVO pageReqVO) {
- return knowledgeMapper.selectPage(pageReqVO);
+ return mapper.selectPage(pageReqVO);
}
@Override
- public List<AiKnowledgeDO> getKnowledgeSimpleListByStatus(Integer status) {
- return knowledgeMapper.selectListByStatus(status);
+ public List<AiKnowledgeDO> getEnabledKnowledgeList() {
+ return mapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus());
}
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchReqBO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchReqBO.java
index 9ff63b6..6a198f9 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchReqBO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchReqBO.java
@@ -1,39 +1,20 @@
package cn.iocoder.yudao.module.ai.service.knowledge.bo;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
import lombok.Data;
-import javax.validation.constraints.NotNull;
-
-import jakarta.validation.constraints.NotEmpty;
-
-/**
- * AI 鐭ヨ瘑搴撴钀芥悳绱� Request BO
- *
- * @author 鑺嬮亾婧愮爜
- */
@Data
public class AiKnowledgeSegmentSearchReqBO {
- /**
- * 鐭ヨ瘑搴撶紪鍙�
- */
@NotNull(message = "鐭ヨ瘑搴撶紪鍙蜂笉鑳戒负绌�")
private Long knowledgeId;
- /**
- * 鍐呭
- */
@NotEmpty(message = "鍐呭涓嶈兘涓虹┖")
private String content;
- /**
- * 鏈�澶ц繑鍥炴暟閲�
- */
private Integer topK;
- /**
- * 鐩镐技搴﹂槇鍊�
- */
private Double similarityThreshold;
-}
\ No newline at end of file
+}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchRespBO.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchRespBO.java
index 72eb846..209bf81 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchRespBO.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/bo/AiKnowledgeSegmentSearchRespBO.java
@@ -2,44 +2,23 @@
import lombok.Data;
-/**
- * AI 鐭ヨ瘑搴撴钀芥悳绱� Response BO
- *
- * @author 鑺嬮亾婧愮爜
- */
@Data
public class AiKnowledgeSegmentSearchRespBO {
- /**
- * 娈佃惤缂栧彿
- */
private Long id;
- /**
- * 鏂囨。缂栧彿
- */
+
private Long documentId;
- /**
- * 鐭ヨ瘑搴撶紪鍙�
- */
+
+ private String documentName;
+
private Long knowledgeId;
- /**
- * 鍐呭
- */
private String content;
- /**
- * 鍐呭闀垮害
- */
+
private Integer contentLength;
- /**
- * Token 鏁伴噺
- */
private Integer tokens;
- /**
- * 鐩镐技搴﹀垎鏁�
- */
private Double score;
-}
\ No newline at end of file
+}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/splitter/MarkdownQaSplitter.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/splitter/MarkdownQaSplitter.java
index 1fbf4f2..6239602 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/splitter/MarkdownQaSplitter.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/splitter/MarkdownQaSplitter.java
@@ -5,7 +5,6 @@
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.transformer.splitter.TextSplitter;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -14,44 +13,16 @@
/**
* Markdown QA 鏍煎紡涓撶敤鍒囩墖鍣�
- *
- * <p>鍔熻兘鐗圭偣锛�
- * <ul>
- * <li>璇嗗埆浜岀骇鏍囬锛�## 锛変綔涓洪棶棰樻爣璁�</li>
- * <li>鐭� QA 瀵逛繚鎸佸畬鏁达紙涓嶈秴杩� Token 闄愬埗锛�</li>
- * <li>闀跨瓟妗堟櫤鑳藉垏鍒嗭紝姣忎釜鐗囨淇濈暀瀹屾暣闂浣滀负涓婁笅鏂�</li>
- * <li>鏀寔鑷畾涔� Token 浼扮畻鍣�</li>
- * </ul>
- *
- * @author runzhen
+ * 璇嗗埆浜岀骇鏍囬锛�## 锛変綔涓洪棶棰樻爣璁帮紝淇濇寔闂瓟瀵瑰畬鏁存��
*/
@Slf4j
-@SuppressWarnings("SizeReplaceableByIsEmpty")
public class MarkdownQaSplitter extends TextSplitter {
- /**
- * 浜岀骇鏍囬姝e垯锛氬尮閰� "## " 寮�澶寸殑琛�
- */
private static final Pattern H2_PATTERN = Pattern.compile("^##\\s+(.+)$", Pattern.MULTILINE);
-
- /**
- * 娈佃惤鍒嗛殧绗︼細鍙屾崲琛�
- */
private static final String PARAGRAPH_SEPARATOR = "\n\n";
-
- /**
- * 鍙ュ瓙鍒嗛殧绗�
- */
private static final Pattern SENTENCE_PATTERN = Pattern.compile("[銆傦紒锛�.!?]\\s*");
- /**
- * 鍒嗘鐨勬渶澶� Token 鏁�
- */
private final int chunkSize;
-
- /**
- * Token 浼扮畻鍣紙绠�鍗曞疄鐜帮細涓枃鎸夊瓧绗︽暟锛岃嫳鏂囨寜鍗曡瘝鏁扮殑 1.3 鍊嶏級
- */
private final TokenEstimator tokenEstimator;
public MarkdownQaSplitter(int chunkSize) {
@@ -61,282 +32,115 @@
@Override
protected List<String> splitText(String text) {
- if (StrUtil.isEmpty(text)) {
- return Collections.emptyList();
- }
-
- // 瑙f瀽 QA 瀵�
+ if (StrUtil.isEmpty(text)) return Collections.emptyList();
List<QaPair> qaPairs = parseQaPairs(text);
- if (CollUtil.isEmpty(qaPairs)) {
- // 濡傛灉娌℃湁璇嗗埆鍒� QA 鏍煎紡锛屾寜娈佃惤鍒囧垎
- return fallbackSplit(text);
- }
-
- // 澶勭悊姣忎釜 QA 瀵�
+ if (CollUtil.isEmpty(qaPairs)) return fallbackSplit(text);
List<String> result = new ArrayList<>();
- for (QaPair qaPair : qaPairs) {
- result.addAll(splitQaPair(qaPair));
- }
+ for (QaPair qaPair : qaPairs) result.addAll(splitQaPair(qaPair));
return result;
}
- /**
- * 瑙f瀽 Markdown QA 瀵�
- *
- * @param content 鏂囨湰鍐呭
- * @return QA 瀵瑰垪琛�
- */
private List<QaPair> parseQaPairs(String content) {
- // 鎵惧埌鎵�鏈変簩绾ф爣棰樹綅缃�
List<QaPair> qaPairs = new ArrayList<>();
List<Integer> headingPositions = new ArrayList<>();
List<String> questions = new ArrayList<>();
Matcher matcher = H2_PATTERN.matcher(content);
- while (matcher.find()) {
- headingPositions.add(matcher.start());
- questions.add(matcher.group(1).trim());
- }
- if (CollUtil.isEmpty(headingPositions)) {
- return qaPairs;
- }
-
- // 鎻愬彇姣忎釜 QA 瀵�
+ while (matcher.find()) { headingPositions.add(matcher.start()); questions.add(matcher.group(1).trim()); }
+ if (CollUtil.isEmpty(headingPositions)) return qaPairs;
for (int i = 0; i < headingPositions.size(); i++) {
int start = headingPositions.get(i);
- int end = (i + 1 < headingPositions.size())
- ? headingPositions.get(i + 1)
- : content.length();
- String qaText = content.substring(start, end).trim();
+ int end = (i + 1 < headingPositions.size()) ? headingPositions.get(i + 1) : content.length();
+ String fullText = content.substring(start, end).trim();
String question = questions.get(i);
- // 鎻愬彇绛旀閮ㄥ垎锛堝幓鎺夐棶棰樻爣棰橈級
- String answer = qaText.substring(qaText.indexOf('\n') + 1).trim();
- qaPairs.add(new QaPair(question, answer, qaText));
+ String answer = fullText.substring(fullText.indexOf('\n') + 1).trim();
+ qaPairs.add(new QaPair(question, answer, fullText));
}
return qaPairs;
}
- /**
- * 鍒囧垎鍗曚釜 QA 瀵�
- *
- * @param qaPair QA 瀵�
- * @return 鍒囧垎鍚庣殑鏂囨湰鐗囨鍒楄〃
- */
private List<String> splitQaPair(QaPair qaPair) {
- // 濡傛灉鏁翠釜 QA 瀵逛笉瓒呰繃闄愬埗锛屼繚鎸佸畬鏁�
+ int qaTokens = tokenEstimator.estimate(qaPair.fullText);
+ if (qaTokens <= chunkSize) return Collections.singletonList(qaPair.fullText);
List<String> chunks = new ArrayList<>();
- String fullQa = qaPair.fullText;
- int qaTokens = tokenEstimator.estimate(fullQa);
- if (qaTokens <= chunkSize) {
- chunks.add(fullQa);
- return chunks;
- }
-
- // 闀跨瓟妗堥渶瑕佸垏鍒�
- log.debug("QA 瀵硅秴杩� Token 闄愬埗 ({} > {})锛屽紑濮嬫櫤鑳藉垏鍒�: {}", qaTokens, chunkSize, qaPair.question);
List<String> answerChunks = splitLongAnswer(qaPair.answer, qaPair.question);
for (String answerChunk : answerChunks) {
- // 姣忎釜鐗囨閮藉寘鍚畬鏁撮棶棰�
- String chunkText = "## " + qaPair.question + "\n" + answerChunk;
- chunks.add(chunkText);
+ chunks.add("## " + qaPair.question + "\n" + answerChunk);
}
return chunks;
}
- /**
- * 鍒囧垎闀跨瓟妗�
- *
- * @param answer 绛旀鏂囨湰
- * @param question 闂鏂囨湰
- * @return 鍒囧垎鍚庣殑绛旀鐗囨鍒楄〃
- */
private List<String> splitLongAnswer(String answer, String question) {
List<String> chunks = new ArrayList<>();
- // 棰勭暀闂鐨� Token 绌洪棿
String questionHeader = "## " + question + "\n";
int questionTokens = tokenEstimator.estimate(questionHeader);
- int availableTokens = chunkSize - questionTokens - 10; // 棰勭暀 10 涓� Token 鐨勭紦鍐�
-
- // 鍏堟寜娈佃惤鍒囧垎
+ int availableTokens = chunkSize - questionTokens - 10;
String[] paragraphs = answer.split(PARAGRAPH_SEPARATOR);
StringBuilder currentChunk = new StringBuilder();
int currentTokens = 0;
for (String paragraph : paragraphs) {
- if (StrUtil.isEmpty(paragraph)) {
- continue;
- }
+ if (StrUtil.isEmpty(paragraph)) continue;
int paragraphTokens = tokenEstimator.estimate(paragraph);
- // 濡傛灉鍗曚釜娈佃惤灏辫秴杩囬檺鍒讹紝闇�瑕佹寜鍙ュ瓙鍒囧垎
if (paragraphTokens > availableTokens) {
- // 鍏堜繚瀛樺綋鍓嶅潡
- if (currentChunk.length() > 0) {
- chunks.add(currentChunk.toString().trim());
- currentChunk = new StringBuilder();
- currentTokens = 0;
- }
- // 鎸夊彞瀛愬垏鍒嗛暱娈佃惤
+ if (currentChunk.length() > 0) { chunks.add(currentChunk.toString().trim()); currentChunk = new StringBuilder(); currentTokens = 0; }
chunks.addAll(splitLongParagraph(paragraph, availableTokens));
continue;
}
- // 濡傛灉鍔犱笂杩欎釜娈佃惤浼氳秴杩囬檺鍒�
- if (currentTokens + paragraphTokens > availableTokens && currentChunk.length() > 0) {
- chunks.add(currentChunk.toString().trim());
- currentChunk = new StringBuilder();
- currentTokens = 0;
- }
- if (currentChunk.length() > 0) {
- currentChunk.append("\n\n");
- }
- // 娣诲姞娈佃惤
+ if (currentTokens + paragraphTokens > availableTokens && currentChunk.length() > 0) { chunks.add(currentChunk.toString().trim()); currentChunk = new StringBuilder(); currentTokens = 0; }
+ if (currentChunk.length() > 0) currentChunk.append("\n\n");
currentChunk.append(paragraph);
currentTokens += paragraphTokens;
}
-
- // 娣诲姞鏈�鍚庝竴鍧�
- if (currentChunk.length() > 0) {
- chunks.add(currentChunk.toString().trim());
- }
+ if (currentChunk.length() > 0) chunks.add(currentChunk.toString().trim());
return CollUtil.isEmpty(chunks) ? Collections.singletonList(answer) : chunks;
}
- /**
- * 鍒囧垎闀挎钀斤紙鎸夊彞瀛愶級
- *
- * @param paragraph 娈佃惤鏂囨湰
- * @param availableTokens 鍙敤鐨� Token 鏁�
- * @return 鍒囧垎鍚庣殑鏂囨湰鐗囨鍒楄〃
- */
private List<String> splitLongParagraph(String paragraph, int availableTokens) {
- // 鎸夊彞瀛愬垏鍒�
List<String> chunks = new ArrayList<>();
String[] sentences = SENTENCE_PATTERN.split(paragraph);
-
- // 鎸夊彞瀛愮疮绉垏鍒�
StringBuilder currentChunk = new StringBuilder();
int currentTokens = 0;
for (String sentence : sentences) {
- if (StrUtil.isEmpty(sentence)) {
- continue;
- }
+ if (StrUtil.isEmpty(sentence)) continue;
int sentenceTokens = tokenEstimator.estimate(sentence);
- // 濡傛灉鍗曚釜鍙ュ瓙灏辫秴杩囬檺鍒讹紝寮哄埗鍒囧垎
- if (sentenceTokens > availableTokens) {
- if (currentChunk.length() > 0) {
- chunks.add(currentChunk.toString().trim());
- currentChunk = new StringBuilder();
- currentTokens = 0;
- }
- chunks.add(sentence.trim());
- continue;
- }
- // 濡傛灉鍔犱笂杩欎釜鍙ュ瓙浼氳秴杩囬檺鍒�
- if (currentTokens + sentenceTokens > availableTokens && currentChunk.length() > 0) {
- chunks.add(currentChunk.toString().trim());
- currentChunk = new StringBuilder();
- currentTokens = 0;
- }
- // 娣诲姞鍙ュ瓙
+ if (sentenceTokens > availableTokens) { if (currentChunk.length() > 0) { chunks.add(currentChunk.toString().trim()); currentChunk = new StringBuilder(); currentTokens = 0; } chunks.add(sentence.trim()); continue; }
+ if (currentTokens + sentenceTokens > availableTokens && currentChunk.length() > 0) { chunks.add(currentChunk.toString().trim()); currentChunk = new StringBuilder(); currentTokens = 0; }
currentChunk.append(sentence);
currentTokens += sentenceTokens;
}
-
- // 娣诲姞鏈�鍚庝竴鍧�
- if (currentChunk.length() > 0) {
- chunks.add(currentChunk.toString().trim());
- }
+ if (currentChunk.length() > 0) chunks.add(currentChunk.toString().trim());
return chunks.isEmpty() ? Collections.singletonList(paragraph) : chunks;
}
- /**
- * 闄嶇骇鍒囧垎绛栫暐锛堝綋鏈瘑鍒埌 QA 鏍煎紡鏃讹級
- *
- * @param content 鏂囨湰鍐呭
- * @return 鍒囧垎鍚庣殑鏂囨湰鐗囨鍒楄〃
- */
private List<String> fallbackSplit(String content) {
- // 鎸夋钀藉垏鍒�
List<String> chunks = new ArrayList<>();
String[] paragraphs = content.split(PARAGRAPH_SEPARATOR);
-
- // 鎸夋钀界疮绉垏鍒�
StringBuilder currentChunk = new StringBuilder();
int currentTokens = 0;
for (String paragraph : paragraphs) {
- if (StrUtil.isEmpty(paragraph)) {
- continue;
- }
+ if (StrUtil.isEmpty(paragraph)) continue;
int paragraphTokens = tokenEstimator.estimate(paragraph);
- // 濡傛灉鍔犱笂杩欎釜娈佃惤浼氳秴杩囬檺鍒�
- if (currentTokens + paragraphTokens > chunkSize && currentChunk.length() > 0) {
- chunks.add(currentChunk.toString().trim());
- currentChunk = new StringBuilder();
- currentTokens = 0;
- }
- // 娣诲姞娈佃惤
- if (currentChunk.length() > 0) {
- currentChunk.append("\n\n");
- }
+ if (currentTokens + paragraphTokens > chunkSize && currentChunk.length() > 0) { chunks.add(currentChunk.toString().trim()); currentChunk = new StringBuilder(); currentTokens = 0; }
+ if (currentChunk.length() > 0) currentChunk.append("\n\n");
currentChunk.append(paragraph);
currentTokens += paragraphTokens;
}
-
- // 娣诲姞鏈�鍚庝竴鍧�
- if (currentChunk.length() > 0) {
- chunks.add(currentChunk.toString().trim());
- }
+ if (currentChunk.length() > 0) chunks.add(currentChunk.toString().trim());
return chunks.isEmpty() ? Collections.singletonList(content) : chunks;
}
- /**
- * QA 瀵规暟鎹粨鏋�
- */
@AllArgsConstructor
- private static class QaPair {
+ private static class QaPair { String question; String answer; String fullText; }
- String question;
- String answer;
- String fullText;
+ public interface TokenEstimator { int estimate(String text); }
- }
-
- /**
- * Token 浼扮畻鍣ㄦ帴鍙�
- */
- public interface TokenEstimator {
-
- int estimate(String text);
-
- }
-
- /**
- * 绠�鍗曠殑 Token 浼扮畻鍣ㄥ疄鐜�
- * 涓枃锛�1 瀛楃 鈮� 1 Token
- * 鑻辨枃锛�1 鍗曡瘝 鈮� 1.3 Token
- */
private static class SimpleTokenEstimator implements TokenEstimator {
-
@Override
public int estimate(String text) {
- if (StrUtil.isEmpty(text)) {
- return 0;
- }
-
- int chineseChars = 0;
- int englishWords = 0;
- // 绠�鍗曠粺璁′腑鑻辨枃
- for (char c : text.toCharArray()) {
- if (c >= 0x4E00 && c <= 0x9FA5) {
- chineseChars++;
- }
- }
- // 鑻辨枃鍗曡瘝浼扮畻
- String[] words = text.split("\\s+");
- for (String word : words) {
- if (word.matches(".*[a-zA-Z].*")) {
- englishWords++;
- }
- }
- return chineseChars + (int) (englishWords * 1.3);
+ if (StrUtil.isEmpty(text)) return 0;
+ int chineseChars = 0, englishWords = 0;
+ for (char c : text.toCharArray()) { if (c >= 0x4E00 && c <= 0x9FA5) chineseChars++; }
+ for (String word : text.split("\\s+")) { if (word.matches(".*[a-zA-Z].*")) englishWords++; }
+ return chineseChars + (int)(englishWords * 1.3);
}
}
-
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/splitter/SemanticTextSplitter.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/splitter/SemanticTextSplitter.java
index 4c7112e..401dc64 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/splitter/SemanticTextSplitter.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/knowledge/splitter/SemanticTextSplitter.java
@@ -3,7 +3,6 @@
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.transformer.splitter.TextSplitter;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -13,289 +12,117 @@
/**
* 璇箟鍖栨枃鏈垏鐗囧櫒
- *
- * <p>鍔熻兘鐗圭偣锛�
- * <ul>
- * <li>浼樺厛鍦ㄦ钀借竟鐣岋紙鍙屾崲琛岋級澶勫垏鍒�</li>
- * <li>鍏舵鍦ㄥ彞瀛愯竟鐣岋紙鍙ュ彿銆侀棶鍙枫�佹劅鍙瑰彿锛夊鍒囧垎</li>
- * <li>閬垮厤鍦ㄥ彞瀛愪腑闂存埅鏂紝淇濇寔璇箟瀹屾暣鎬�</li>
- * <li>鏀寔涓嫳鏂囨爣鐐圭鍙疯瘑鍒�</li>
- * </ul>
- *
- * @author runzhen
+ * 浼樺厛鍦ㄦ钀借竟鐣屽鍒囧垎锛屽叾娆″湪鍙ュ瓙杈圭晫澶勫垏鍒嗭紝淇濇寔璇箟瀹屾暣鎬�
*/
@Slf4j
public class SemanticTextSplitter extends TextSplitter {
- /**
- * 鍒嗘鐨勬渶澶� Token 鏁�
- */
private final int chunkSize;
-
- /**
- * 娈佃惤閲嶅彔澶у皬锛堢敤浜庝繚鎸佷笂涓嬫枃杩炶疮鎬э級
- */
private final int chunkOverlap;
-
- /**
- * 娈佃惤鍒嗛殧绗︼紙鎸変紭鍏堢骇鎺掑簭锛�
- */
- private static final List<String> PARAGRAPH_SEPARATORS = Arrays.asList(
- "\n\n\n", // 涓変釜鎹㈣
- "\n\n", // 鍙屾崲琛�
- "\n" // 鍗曟崲琛�
- );
-
- /**
- * 鍙ュ瓙缁撴潫鏍囪锛堜腑鑻辨枃鏍囩偣锛�
- */
- private static final Pattern SENTENCE_END_PATTERN = Pattern.compile(
- "[銆傦紒锛�.!?]+[\\s\"'锛�)銆慭\]]*"
- );
-
- /**
- * Token 浼扮畻鍣�
- */
+ private static final List<String> PARAGRAPH_SEPARATORS = Arrays.asList("\n\n\n", "\n\n", "\n");
+ private static final Pattern SENTENCE_END_PATTERN = Pattern.compile("[銆傦紒锛�.!?]+[\\s\"'锛�)銆慭\]]*");
private final MarkdownQaSplitter.TokenEstimator tokenEstimator;
public SemanticTextSplitter(int chunkSize, int chunkOverlap) {
this.chunkSize = chunkSize;
- this.chunkOverlap = Math.min(chunkOverlap, chunkSize / 2); // 閲嶅彔涓嶈秴杩囦竴鍗�
+ this.chunkOverlap = Math.min(chunkOverlap, chunkSize / 2);
this.tokenEstimator = new SimpleTokenEstimator();
}
- public SemanticTextSplitter(int chunkSize) {
- this(chunkSize, 50); // 榛樿閲嶅彔 50 涓� Token
- }
+ public SemanticTextSplitter(int chunkSize) { this(chunkSize, 50); }
@Override
protected List<String> splitText(String text) {
- if (StrUtil.isEmpty(text)) {
- return Collections.emptyList();
- }
+ if (StrUtil.isEmpty(text)) return Collections.emptyList();
return splitTextRecursive(text);
}
- /**
- * 鍒囧垎鏂囨湰锛堥�掑綊绛栫暐锛�
- *
- * @param text 寰呭垏鍒嗘枃鏈�
- * @return 鍒囧垎鍚庣殑鏂囨湰鍧楀垪琛�
- */
private List<String> splitTextRecursive(String text) {
- List<String> chunks = new ArrayList<>();
-
- // 濡傛灉鏂囨湰涓嶈秴杩囬檺鍒讹紝鐩存帴杩斿洖
int textTokens = tokenEstimator.estimate(text);
- if (textTokens <= chunkSize) {
- chunks.add(text.trim());
- return chunks;
- }
+ if (textTokens <= chunkSize) return Collections.singletonList(text.trim());
- // 灏濊瘯鎸変笉鍚屽垎闅旂鍒囧垎
List<String> splits = null;
String usedSeparator = null;
for (String separator : PARAGRAPH_SEPARATORS) {
- if (text.contains(separator)) {
- splits = Arrays.asList(text.split(Pattern.quote(separator)));
- usedSeparator = separator;
- break;
- }
+ if (text.contains(separator)) { splits = Arrays.asList(text.split(Pattern.quote(separator))); usedSeparator = separator; break; }
}
-
- // 濡傛灉娌℃湁鎵惧埌娈佃惤鍒嗛殧绗︼紝鎸夊彞瀛愬垏鍒�
- if (splits == null || splits.size() == 1) {
- splits = splitBySentences(text);
- usedSeparator = ""; // 鍙ュ瓙鍒囧垎涓嶉渶瑕佸垎闅旂
- }
-
- // 鍚堝苟灏忕墖娈�
- chunks = mergeSplits(splits, usedSeparator);
- return chunks;
+ if (splits == null || splits.size() == 1) { splits = splitBySentences(text); usedSeparator = ""; }
+ return mergeSplits(splits, usedSeparator);
}
- /**
- * 鎸夊彞瀛愬垏鍒�
- *
- * @param text 寰呭垏鍒嗘枃鏈�
- * @return 鍙ュ瓙鍒楄〃
- */
private List<String> splitBySentences(String text) {
- // 浣跨敤姝e垯琛ㄨ揪寮忓尮閰嶅彞瀛愮粨鏉熶綅缃�
List<String> sentences = new ArrayList<>();
int lastEnd = 0;
Matcher matcher = SENTENCE_END_PATTERN.matcher(text);
while (matcher.find()) {
String sentence = text.substring(lastEnd, matcher.end()).trim();
- if (StrUtil.isNotEmpty(sentence)) {
- sentences.add(sentence);
- }
+ if (StrUtil.isNotEmpty(sentence)) sentences.add(sentence);
lastEnd = matcher.end();
}
-
- // 娣诲姞鍓╀綑閮ㄥ垎
- if (lastEnd < text.length()) {
- String remaining = text.substring(lastEnd).trim();
- if (StrUtil.isNotEmpty(remaining)) {
- sentences.add(remaining);
- }
- }
+ if (lastEnd < text.length()) { String remaining = text.substring(lastEnd).trim(); if (StrUtil.isNotEmpty(remaining)) sentences.add(remaining); }
return sentences.isEmpty() ? Collections.singletonList(text) : sentences;
}
- /**
- * 鍚堝苟鍒囧垎鍚庣殑灏忕墖娈�
- *
- * @param splits 鍒囧垎鍚庣殑鐗囨鍒楄〃
- * @param separator 鐗囨闂寸殑鍒嗛殧绗�
- * @return 鍚堝苟鍚庣殑鏂囨湰鍧楀垪琛�
- */
private List<String> mergeSplits(List<String> splits, String separator) {
List<String> chunks = new ArrayList<>();
List<String> currentChunks = new ArrayList<>();
int currentLength = 0;
-
for (String split : splits) {
- if (StrUtil.isEmpty(split)) {
- continue;
- }
+ if (StrUtil.isEmpty(split)) continue;
int splitTokens = tokenEstimator.estimate(split);
- // 濡傛灉鍗曚釜鐗囨灏辫秴杩囬檺鍒讹紝杩涗竴姝ラ�掑綊鍒囧垎
if (splitTokens > chunkSize) {
- // 鍏堜繚瀛樺綋鍓嶇疮绉殑鍧�
- if (!currentChunks.isEmpty()) {
- String chunkText = String.join(separator, currentChunks);
- chunks.add(chunkText.trim());
- currentChunks.clear();
- currentLength = 0;
- }
- // 閫掑綊鍒囧垎澶х墖娈�
- if (!separator.isEmpty()) {
- // 濡傛灉鏄钀藉垎闅旂锛屽皾璇曟寜鍙ュ瓙鍒囧垎
- chunks.addAll(splitTextRecursive(split));
- } else {
- // 濡傛灉宸茬粡鏄彞瀛愮骇鍒紝寮哄埗鎸夊瓧绗﹀垏鍒�
- chunks.addAll(forceSplitLongText(split));
- }
+ if (!currentChunks.isEmpty()) { chunks.add(String.join(separator, currentChunks).trim()); currentChunks.clear(); currentLength = 0; }
+ if (!separator.isEmpty()) { chunks.addAll(splitTextRecursive(split)); } else { chunks.addAll(forceSplitLongText(split)); }
continue;
}
- // 璁$畻鍔犱笂鍒嗛殧绗︾殑 Token 鏁�
int separatorTokens = StrUtil.isEmpty(separator) ? 0 : tokenEstimator.estimate(separator);
- // 濡傛灉鍔犱笂杩欎釜鐗囨浼氳秴杩囬檺鍒�
if (!currentChunks.isEmpty() && currentLength + splitTokens + separatorTokens > chunkSize) {
- // 淇濆瓨褰撳墠鍧�
- String chunkText = String.join(separator, currentChunks);
- chunks.add(chunkText.trim());
-
- // 澶勭悊閲嶅彔锛氫繚鐣欐渶鍚庡嚑涓墖娈�
+ chunks.add(String.join(separator, currentChunks).trim());
currentChunks = getOverlappingChunks(currentChunks, separator);
currentLength = estimateTokens(currentChunks, separator);
}
- // 娣诲姞褰撳墠鐗囨
currentChunks.add(split);
currentLength += splitTokens + separatorTokens;
}
-
- // 娣诲姞鏈�鍚庝竴鍧�
- if (!currentChunks.isEmpty()) {
- String chunkText = String.join(separator, currentChunks);
- chunks.add(chunkText.trim());
- }
+ if (!currentChunks.isEmpty()) chunks.add(String.join(separator, currentChunks).trim());
return chunks;
}
- /**
- * 鑾峰彇閲嶅彔鐨勭墖娈碉紙鐢ㄤ簬淇濇寔涓婁笅鏂囷級
- *
- * @param chunks 褰撳墠鐗囨鍒楄〃
- * @param separator 鐗囨闂寸殑鍒嗛殧绗�
- * @return 閲嶅彔鐨勭墖娈靛垪琛�
- */
private List<String> getOverlappingChunks(List<String> chunks, String separator) {
- if (chunkOverlap == 0 || chunks.isEmpty()) {
- return new ArrayList<>();
- }
-
- // 浠庡悗寰�鍓嶅彇鐗囨锛岀洿鍒拌揪鍒伴噸鍙犲ぇ灏�
+ if (chunkOverlap == 0 || chunks.isEmpty()) return new ArrayList<>();
List<String> overlapping = new ArrayList<>();
int tokens = 0;
for (int i = chunks.size() - 1; i >= 0; i--) {
String chunk = chunks.get(i);
int chunkTokens = tokenEstimator.estimate(chunk);
- if (tokens + chunkTokens > chunkOverlap) {
- break;
- }
- // 娣诲姞鍒伴噸鍙犲垪琛ㄥ墠绔�
+ if (tokens + chunkTokens > chunkOverlap) break;
overlapping.add(0, chunk);
tokens += chunkTokens + (StrUtil.isEmpty(separator) ? 0 : tokenEstimator.estimate(separator));
}
return overlapping;
}
- /**
- * 浼扮畻鐗囨鍒楄〃鐨勬�� Token 鏁�
- *
- * @param chunks 鐗囨鍒楄〃
- * @param separator 鐗囨闂寸殑鍒嗛殧绗�
- * @return 鎬� Token 鏁�
- */
private int estimateTokens(List<String> chunks, String separator) {
int total = 0;
- for (int i = 0; i < chunks.size(); i++) {
- total += tokenEstimator.estimate(chunks.get(i));
- if (i < chunks.size() - 1 && StrUtil.isNotEmpty(separator)) {
- total += tokenEstimator.estimate(separator);
- }
- }
+ for (int i = 0; i < chunks.size(); i++) { total += tokenEstimator.estimate(chunks.get(i)); if (i < chunks.size() - 1 && StrUtil.isNotEmpty(separator)) total += tokenEstimator.estimate(separator); }
return total;
}
- /**
- * 寮哄埗鍒囧垎闀挎枃鏈紙褰撹涔夊垏鍒嗗け璐ユ椂锛�
- *
- * @param text 寰呭垏鍒嗘枃鏈�
- * @return 鍒囧垎鍚庣殑鏂囨湰鍧楀垪琛�
- */
private List<String> forceSplitLongText(String text) {
List<String> chunks = new ArrayList<>();
- int charsPerChunk = (int) (chunkSize * 0.8); // 淇濆畧浼拌
- for (int i = 0; i < text.length(); i += charsPerChunk) {
- int end = Math.min(i + charsPerChunk, text.length());
- String chunk = text.substring(i, end);
- chunks.add(chunk.trim());
- }
- log.warn("鏂囨湰杩囬暱锛屽凡寮哄埗鎸夊瓧绗﹀垏鍒嗭紝鍙兘褰卞搷璇箟瀹屾暣鎬�");
+ int charsPerChunk = (int)(chunkSize * 0.8);
+ for (int i = 0; i < text.length(); i += charsPerChunk) { int end = Math.min(i + charsPerChunk, text.length()); chunks.add(text.substring(i, end).trim()); }
return chunks;
}
- /**
- * 绠�鍗曠殑 Token 浼扮畻鍣ㄥ疄鐜�
- */
private static class SimpleTokenEstimator implements MarkdownQaSplitter.TokenEstimator {
-
@Override
public int estimate(String text) {
- if (StrUtil.isEmpty(text)) {
- return 0;
- }
-
- int chineseChars = 0;
- int englishWords = 0;
- // 绠�鍗曠粺璁′腑鑻辨枃
- for (char c : text.toCharArray()) {
- if (c >= 0x4E00 && c <= 0x9FA5) {
- chineseChars++;
- }
- }
- // 鑻辨枃鍗曡瘝浼扮畻
- String[] words = text.split("\\s+");
- for (String word : words) {
- if (word.matches(".*[a-zA-Z].*")) {
- englishWords++;
- }
- }
- return chineseChars + (int) (englishWords * 1.3);
+ if (StrUtil.isEmpty(text)) return 0;
+ int chineseChars = 0, englishWords = 0;
+ for (char c : text.toCharArray()) { if (c >= 0x4E00 && c <= 0x9FA5) chineseChars++; }
+ for (String word : text.split("\\s+")) { if (word.matches(".*[a-zA-Z].*")) englishWords++; }
+ return chineseChars + (int)(englishWords * 1.3);
}
}
-
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapService.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapService.java
deleted file mode 100644
index 65a5aaf..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapService.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package cn.iocoder.yudao.module.ai.service.mindmap;
-
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapGenerateReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapPageReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.mindmap.AiMindMapDO;
-import reactor.core.publisher.Flux;
-
-/**
- * AI 鎬濈淮瀵煎浘 Service 鎺ュ彛
- *
- * @author xiaoxin
- */
-public interface AiMindMapService {
-
- /**
- * 鐢熸垚鎬濈淮瀵煎浘鍐呭
- *
- * @param generateReqVO 璇锋眰鍙傛暟
- * @param userId 鐢ㄦ埛缂栧彿
- * @return 鐢熸垚缁撴灉
- */
- Flux<CommonResult<String>> generateMindMap(AiMindMapGenerateReqVO generateReqVO, Long userId);
-
- /**
- * 鍒犻櫎鎬濈淮瀵煎浘
- *
- * @param id 缂栧彿
- */
- void deleteMindMap(Long id);
-
- /**
- * 鑾峰緱鎬濈淮瀵煎浘鍒嗛〉
- *
- * @param pageReqVO 鍒嗛〉鏌ヨ
- * @return 鎬濈淮瀵煎浘鍒嗛〉
- */
- PageResult<AiMindMapDO> getMindMapPage(AiMindMapPageReqVO pageReqVO);
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapServiceImpl.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapServiceImpl.java
deleted file mode 100644
index f307dd7..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/mindmap/AiMindMapServiceImpl.java
+++ /dev/null
@@ -1,161 +0,0 @@
-package cn.iocoder.yudao.module.ai.service.mindmap;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.util.ObjUtil;
-import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.module.ai.enums.model.AiModelTypeEnum;
-import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
-import cn.iocoder.yudao.module.ai.util.AiUtils;
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
-import cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapGenerateReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.mindmap.vo.AiMindMapPageReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.mindmap.AiMindMapDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
-import cn.iocoder.yudao.module.ai.dal.mysql.mindmap.AiMindMapMapper;
-import cn.iocoder.yudao.module.ai.enums.AiChatRoleEnum;
-import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;
-import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
-import cn.iocoder.yudao.module.ai.service.model.AiModelService;
-import jakarta.annotation.Resource;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.SystemMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatModel;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.ChatOptions;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.stereotype.Service;
-import reactor.core.publisher.Flux;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
-import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
-
-/**
- * AI 鎬濈淮瀵煎浘 Service 瀹炵幇绫�
- *
- * @author xiaoxin
- */
-@Service
-@Slf4j
-public class AiMindMapServiceImpl implements AiMindMapService {
-
- @Resource
- private AiModelService modalService;
- @Resource
- private AiChatRoleService chatRoleService;
-
- @Resource
- private AiMindMapMapper mindMapMapper;
-
- @Override
- public Flux<CommonResult<String>> generateMindMap(AiMindMapGenerateReqVO generateReqVO, Long userId) {
- // 1. 鑾峰彇瀵煎浘妯″瀷銆傚皾璇曡幏鍙栨�濈淮瀵煎浘鍔╂墜瑙掕壊锛屽鏋滄病鏈夊垯浣跨敤榛樿妯″瀷
- AiChatRoleDO role = CollUtil.getFirst(
- chatRoleService.getChatRoleListByName(AiChatRoleEnum.AI_MIND_MAP_ROLE.getName()));
- // 1.1 鑾峰彇瀵煎浘鎵ц妯″瀷
- AiModelDO model = getModel(role);
- // 1.2 鑾峰彇瑙掕壊璁惧畾娑堟伅
- String systemMessage = role != null && StrUtil.isNotBlank(role.getSystemMessage())
- ? role.getSystemMessage() : AiChatRoleEnum.AI_MIND_MAP_ROLE.getSystemMessage();
- // 1.3 鏍¢獙骞冲彴
- AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
- ChatModel chatModel = modalService.getChatModel(model.getId());
-
- // 2. 鎻掑叆鎬濈淮瀵煎浘淇℃伅
- AiMindMapDO mindMapDO = BeanUtils.toBean(generateReqVO, AiMindMapDO.class, mindMap -> mindMap.setUserId(userId)
- .setPlatform(platform.getPlatform()).setModelId(model.getId()).setModel(model.getModel()));
- mindMapMapper.insert(mindMapDO);
-
- // 3.1 鏋勫缓 Prompt锛屽苟杩涜璋冪敤
- Prompt prompt = buildPrompt(generateReqVO, model, systemMessage);
- Flux<ChatResponse> streamResponse = chatModel.stream(prompt);
-
- // 3.2 娴佸紡杩斿洖
- StringBuffer contentBuffer = new StringBuffer();
- return streamResponse.map(chunk -> {
- String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null;
- newContent = StrUtil.nullToDefault(newContent, ""); // 閬垮厤 null 鐨� 鎯呭喌
- contentBuffer.append(newContent);
- // 鍝嶅簲缁撴灉
- return success(newContent);
- }).doOnComplete(() -> {
- // 蹇界暐绉熸埛锛屽洜涓� Flux 寮傛鏃犳硶閫忎紶绉熸埛
- TenantUtils.executeIgnore(() ->
- mindMapMapper.updateById(new AiMindMapDO().setId(mindMapDO.getId()).setGeneratedContent(contentBuffer.toString())));
- }).doOnError(throwable -> {
- log.error("[generateWriteContent][generateReqVO({}) 鍙戠敓寮傚父]", generateReqVO, throwable);
- // 蹇界暐绉熸埛锛屽洜涓� Flux 寮傛鏃犳硶閫忎紶绉熸埛
- TenantUtils.executeIgnore(() ->
- mindMapMapper.updateById(new AiMindMapDO().setId(mindMapDO.getId()).setErrorMessage(throwable.getMessage())));
- }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.WRITE_STREAM_ERROR)));
-
- }
-
- private Prompt buildPrompt(AiMindMapGenerateReqVO generateReqVO, AiModelDO model, String systemMessage) {
- // 1. 鏋勫缓 message 鍒楄〃
- List<Message> chatMessages = buildMessages(generateReqVO, systemMessage);
- // 2. 鏋勫缓 options 瀵硅薄
- AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
- ChatOptions options = AiUtils.buildChatOptions(platform, model.getModel(), model.getTemperature(), model.getMaxTokens());
- return new Prompt(chatMessages, options);
- }
-
- private static List<Message> buildMessages(AiMindMapGenerateReqVO generateReqVO, String systemMessage) {
- List<Message> chatMessages = new ArrayList<>();
- // 1. 瑙掕壊璁惧畾
- if (StrUtil.isNotBlank(systemMessage)) {
- chatMessages.add(new SystemMessage(systemMessage));
- }
- // 2. 鐢ㄦ埛杈撳叆
- chatMessages.add(new UserMessage(generateReqVO.getPrompt()));
- return chatMessages;
- }
-
- private AiModelDO getModel(AiChatRoleDO role) {
- AiModelDO model = null;
- if (role != null && role.getModelId() != null) {
- model = modalService.getModel(role.getModelId());
- }
- if (model == null) {
- model = modalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType());
- }
- // 鏍¢獙妯″瀷瀛樺湪銆佷笖鍚堟硶
- if (model == null) {
- throw exception(MODEL_NOT_EXISTS);
- }
- if (ObjUtil.notEqual(model.getType(), AiModelTypeEnum.CHAT.getType())) {
- throw exception(MODEL_USE_TYPE_ERROR);
- }
- return model;
- }
-
- @Override
- public void deleteMindMap(Long id) {
- // 鏍¢獙瀛樺湪
- validateMindMapExists(id);
- // 鍒犻櫎
- mindMapMapper.deleteById(id);
- }
-
- private void validateMindMapExists(Long id) {
- if (mindMapMapper.selectById(id) == null) {
- throw exception(MIND_MAP_NOT_EXISTS);
- }
- }
-
- @Override
- public PageResult<AiMindMapDO> getMindMapPage(AiMindMapPageReqVO pageReqVO) {
- return mindMapMapper.selectPage(pageReqVO);
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java
index 44da800..58446ef 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java
@@ -4,77 +4,21 @@
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
-import jakarta.validation.Valid;
-import java.util.List;
-
-/**
- * AI API 瀵嗛挜 Service 鎺ュ彛
- *
- * @author 鑺嬮亾婧愮爜
- */
public interface AiApiKeyService {
- /**
- * 鍒涘缓 API 瀵嗛挜
- *
- * @param createReqVO 鍒涘缓淇℃伅
- * @return 缂栧彿
- */
- Long createApiKey(@Valid AiApiKeySaveReqVO createReqVO);
+ Long createApiKey(AiApiKeySaveReqVO createReqVO);
- /**
- * 鏇存柊 API 瀵嗛挜
- *
- * @param updateReqVO 鏇存柊淇℃伅
- */
- void updateApiKey(@Valid AiApiKeySaveReqVO updateReqVO);
+ void updateApiKey(AiApiKeySaveReqVO updateReqVO);
- /**
- * 鍒犻櫎 API 瀵嗛挜
- *
- * @param id 缂栧彿
- */
void deleteApiKey(Long id);
- /**
- * 鑾峰緱 API 瀵嗛挜
- *
- * @param id 缂栧彿
- * @return API 瀵嗛挜
- */
AiApiKeyDO getApiKey(Long id);
- /**
- * 鏍¢獙 API 瀵嗛挜
- *
- * @param id 姣旈偅濂�
- * @return API 瀵嗛挜
- */
AiApiKeyDO validateApiKey(Long id);
- /**
- * 鑾峰緱 API 瀵嗛挜鍒嗛〉
- *
- * @param pageReqVO 鍒嗛〉鏌ヨ
- * @return API 瀵嗛挜鍒嗛〉
- */
- PageResult<AiApiKeyDO> getApiKeyPage(AiApiKeyPageReqVO pageReqVO);
-
- /**
- * 鑾峰緱 API 瀵嗛挜鍒楄〃
- *
- * @return API 瀵嗛挜鍒楄〃
- */
- List<AiApiKeyDO> getApiKeyList();
-
- /**
- * 鑾峰緱榛樿鐨� API 瀵嗛挜
- *
- * @param platform 骞冲彴
- * @param status 鐘舵��
- * @return API 瀵嗛挜
- */
AiApiKeyDO getRequiredDefaultApiKey(String platform, Integer status);
-}
\ No newline at end of file
+ PageResult<AiApiKeyDO> getApiKeyPage(AiApiKeyPageReqVO pageReqVO);
+
+}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java
index f0bac8a..e72aa23 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java
@@ -9,91 +9,59 @@
import cn.iocoder.yudao.module.ai.dal.mysql.model.AiApiKeyMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
-import org.springframework.validation.annotation.Validated;
-
-import java.util.List;
-
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.API_KEY_DISABLE;
-import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.API_KEY_NOT_EXISTS;
+import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
-/**
- * AI API 瀵嗛挜 Service 瀹炵幇绫�
- *
- * @author 鑺嬮亾婧愮爜
- */
@Service
-@Validated
public class AiApiKeyServiceImpl implements AiApiKeyService {
@Resource
- private AiApiKeyMapper apiKeyMapper;
+ private AiApiKeyMapper mapper;
@Override
public Long createApiKey(AiApiKeySaveReqVO createReqVO) {
- // 鎻掑叆
- AiApiKeyDO apiKey = BeanUtils.toBean(createReqVO, AiApiKeyDO.class);
- apiKeyMapper.insert(apiKey);
- // 杩斿洖
- return apiKey.getId();
+ AiApiKeyDO key = BeanUtils.toBean(createReqVO, AiApiKeyDO.class);
+ mapper.insert(key);
+ return key.getId();
}
@Override
public void updateApiKey(AiApiKeySaveReqVO updateReqVO) {
- // 鏍¢獙瀛樺湪
validateApiKeyExists(updateReqVO.getId());
- // 鏇存柊
- AiApiKeyDO updateObj = BeanUtils.toBean(updateReqVO, AiApiKeyDO.class);
- apiKeyMapper.updateById(updateObj);
+ mapper.updateById(BeanUtils.toBean(updateReqVO, AiApiKeyDO.class));
}
@Override
public void deleteApiKey(Long id) {
- // 鏍¢獙瀛樺湪
validateApiKeyExists(id);
- // 鍒犻櫎
- apiKeyMapper.deleteById(id);
- }
-
- private AiApiKeyDO validateApiKeyExists(Long id) {
- AiApiKeyDO apiKey = apiKeyMapper.selectById(id);
- if (apiKey == null) {
- throw exception(API_KEY_NOT_EXISTS);
- }
- return apiKey;
+ mapper.deleteById(id);
}
@Override
public AiApiKeyDO getApiKey(Long id) {
- return apiKeyMapper.selectById(id);
+ return mapper.selectById(id);
}
@Override
public AiApiKeyDO validateApiKey(Long id) {
- AiApiKeyDO apiKey = validateApiKeyExists(id);
- if (CommonStatusEnum.isDisable(apiKey.getStatus())) {
- throw exception(API_KEY_DISABLE);
- }
- return apiKey;
- }
-
- @Override
- public PageResult<AiApiKeyDO> getApiKeyPage(AiApiKeyPageReqVO pageReqVO) {
- return apiKeyMapper.selectPage(pageReqVO);
- }
-
- @Override
- public List<AiApiKeyDO> getApiKeyList() {
- return apiKeyMapper.selectList();
+ AiApiKeyDO key = mapper.selectById(id);
+ if (key == null) throw exception(API_KEY_NOT_EXISTS);
+ if (CommonStatusEnum.isDisable(key.getStatus())) throw exception(API_KEY_DISABLE);
+ return key;
}
@Override
public AiApiKeyDO getRequiredDefaultApiKey(String platform, Integer status) {
- AiApiKeyDO apiKey = apiKeyMapper.selectFirstByPlatformAndStatus(platform, status);
- if (apiKey == null) {
- throw exception(API_KEY_NOT_EXISTS);
- }
- return apiKey;
+ return mapper.selectFirstByPlatformAndStatus(platform, status);
}
-}
\ No newline at end of file
+ @Override
+ public PageResult<AiApiKeyDO> getApiKeyPage(AiApiKeyPageReqVO pageReqVO) {
+ return mapper.selectPage(pageReqVO);
+ }
+
+ private void validateApiKeyExists(Long id) {
+ if (mapper.selectById(id) == null) throw exception(API_KEY_NOT_EXISTS);
+ }
+
+}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleService.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleService.java
deleted file mode 100644
index e7fecf6..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleService.java
+++ /dev/null
@@ -1,129 +0,0 @@
-package cn.iocoder.yudao.module.ai.service.model;
-
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRolePageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveMyReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
-import jakarta.validation.Valid;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
-
-/**
- * AI 鑱婂ぉ瑙掕壊 Service 鎺ュ彛
- *
- * @author fansili
- */
-public interface AiChatRoleService {
-
- /**
- * 鍒涘缓鑱婂ぉ瑙掕壊
- *
- * @param createReqVO 鍒涘缓淇℃伅
- * @return 缂栧彿
- */
- Long createChatRole(@Valid AiChatRoleSaveReqVO createReqVO);
-
- /**
- * 鍒涘缓銆愭垜鐨勩�戣亰澶╄鑹�
- *
- * @param createReqVO 鍒涘缓淇℃伅
- * @param userId 鐢ㄦ埛缂栧彿
- * @return 缂栧彿
- */
- Long createChatRoleMy(AiChatRoleSaveMyReqVO createReqVO, Long userId);
-
- /**
- * 鏇存柊鑱婂ぉ瑙掕壊
- *
- * @param updateReqVO 鏇存柊淇℃伅
- */
- void updateChatRole(@Valid AiChatRoleSaveReqVO updateReqVO);
-
- /**
- * 鍒涘缓銆愭垜鐨勩�戣亰澶╄鑹�
- *
- * @param updateReqVO 鏇存柊淇℃伅
- * @param userId 鐢ㄦ埛缂栧彿
- */
- void updateChatRoleMy(AiChatRoleSaveMyReqVO updateReqVO, Long userId);
-
- /**
- * 鍒犻櫎鑱婂ぉ瑙掕壊
- *
- * @param id 缂栧彿
- */
- void deleteChatRole(Long id);
-
- /**
- * 鍒犻櫎銆愭垜鐨勩�戣亰澶╄鑹�
- *
- * @param id 缂栧彿
- * @param userId 鐢ㄦ埛缂栧彿
- */
- void deleteChatRoleMy(Long id, Long userId);
-
- /**
- * 鑾峰緱鑱婂ぉ瑙掕壊
- *
- * @param id 缂栧彿
- * @return AI 鑱婂ぉ瑙掕壊
- */
- AiChatRoleDO getChatRole(Long id);
-
- /**
- * 鑾峰緱鑱婂ぉ瑙掕壊鍒楄〃
- *
- * @param ids 缂栧彿鏁扮粍
- * @return 鑱婂ぉ瑙掕壊鍒楄〃
- */
- List<AiChatRoleDO> getChatRoleList(Collection<Long> ids);
-
- default Map<Long, AiChatRoleDO> getChatRoleMap(Collection<Long> ids) {
- return convertMap(getChatRoleList(ids), AiChatRoleDO::getId);
- }
-
- /**
- * 鏍¢獙鑱婂ぉ瑙掕壊鏄惁鍚堟硶
- *
- * @param id 瑙掕壊缂栧彿
- */
- AiChatRoleDO validateChatRole(Long id);
-
- /**
- * 鑾峰緱鑱婂ぉ瑙掕壊鍒嗛〉
- *
- * @param pageReqVO 鍒嗛〉鏌ヨ
- * @return 鑱婂ぉ瑙掕壊鍒嗛〉
- */
- PageResult<AiChatRoleDO> getChatRolePage(AiChatRolePageReqVO pageReqVO);
-
- /**
- * 鑾峰緱銆愭垜鐨勩�戣亰澶╄鑹插垎椤�
- *
- * @param pageReqVO 鍒嗛〉鏌ヨ
- * @param userId 鐢ㄦ埛缂栧彿
- * @return 鑱婂ぉ瑙掕壊鍒嗛〉
- */
- PageResult<AiChatRoleDO> getChatRoleMyPage(AiChatRolePageReqVO pageReqVO, Long userId);
-
- /**
- * 鑾峰緱鑱婂ぉ瑙掕壊鐨勫垎绫诲垪琛�
- *
- * @return 鍒嗙被鍒楄〃
- */
- List<String> getChatRoleCategoryList();
-
- /**
- * 鏍规嵁鍚嶅瓧鑾峰緱鑱婂ぉ瑙掕壊
- *
- * @param name 鍚嶅瓧
- * @return 鑱婂ぉ瑙掕壊鍒楄〃
- */
- List<AiChatRoleDO> getChatRoleListByName(String name);
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleServiceImpl.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleServiceImpl.java
deleted file mode 100644
index bf2abb4..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatRoleServiceImpl.java
+++ /dev/null
@@ -1,200 +0,0 @@
-package cn.iocoder.yudao.module.ai.service.model;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.util.ObjectUtil;
-import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRolePageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveMyReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
-import cn.iocoder.yudao.module.ai.dal.mysql.model.AiChatRoleMapper;
-import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService;
-import jakarta.annotation.Resource;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-
-import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
-import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_ROLE_DISABLE;
-import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.CHAT_ROLE_NOT_EXISTS;
-
-/**
- * AI 鑱婂ぉ瑙掕壊 Service 瀹炵幇绫�
- *
- * @author fansili
- */
-@Service
-@Slf4j
-public class AiChatRoleServiceImpl implements AiChatRoleService {
-
- @Resource
- private AiChatRoleMapper chatRoleMapper;
-
- @Resource
- private AiKnowledgeService knowledgeService;
- @Resource
- private AiToolService toolService;
-
- @Override
- public Long createChatRole(AiChatRoleSaveReqVO createReqVO) {
- // 鏍¢獙鏂囨。
- validateDocuments(createReqVO.getKnowledgeIds());
- // 鏍¢獙宸ュ叿
- validateTools(createReqVO.getToolIds());
-
- // 淇濆瓨瑙掕壊
- AiChatRoleDO chatRole = BeanUtils.toBean(createReqVO, AiChatRoleDO.class);
- chatRoleMapper.insert(chatRole);
- return chatRole.getId();
- }
-
- @Override
- public Long createChatRoleMy(AiChatRoleSaveMyReqVO createReqVO, Long userId) {
- // 鏍¢獙鏂囨。
- validateDocuments(createReqVO.getKnowledgeIds());
- // 鏍¢獙宸ュ叿
- validateTools(createReqVO.getToolIds());
-
- // 淇濆瓨瑙掕壊
- AiChatRoleDO chatRole = BeanUtils.toBean(createReqVO, AiChatRoleDO.class).setUserId(userId)
- .setStatus(CommonStatusEnum.ENABLE.getStatus()).setPublicStatus(false);
- chatRoleMapper.insert(chatRole);
- return chatRole.getId();
- }
-
- @Override
- public void updateChatRole(AiChatRoleSaveReqVO updateReqVO) {
- // 鏍¢獙瀛樺湪
- validateChatRoleExists(updateReqVO.getId());
- // 鏍¢獙鏂囨。
- validateDocuments(updateReqVO.getKnowledgeIds());
- // 鏍¢獙宸ュ叿
- validateTools(updateReqVO.getToolIds());
-
- // 鏇存柊瑙掕壊
- AiChatRoleDO updateObj = BeanUtils.toBean(updateReqVO, AiChatRoleDO.class);
- chatRoleMapper.updateById(updateObj);
- }
-
- @Override
- public void updateChatRoleMy(AiChatRoleSaveMyReqVO updateReqVO, Long userId) {
- // 鏍¢獙瀛樺湪
- AiChatRoleDO chatRole = validateChatRoleExists(updateReqVO.getId());
- if (ObjectUtil.notEqual(chatRole.getUserId(), userId)) {
- throw exception(CHAT_ROLE_NOT_EXISTS);
- }
- // 鏍¢獙鏂囨。
- validateDocuments(updateReqVO.getKnowledgeIds());
- // 鏍¢獙宸ュ叿
- validateTools(updateReqVO.getToolIds());
-
- // 鏇存柊
- AiChatRoleDO updateObj = BeanUtils.toBean(updateReqVO, AiChatRoleDO.class);
- chatRoleMapper.updateById(updateObj);
- }
-
- /**
- * 鏍¢獙鐭ヨ瘑搴撴槸鍚﹀瓨鍦�
- *
- * @param knowledgeIds 鐭ヨ瘑搴撶紪鍙峰垪琛�
- */
- private void validateDocuments(List<Long> knowledgeIds) {
- if (CollUtil.isEmpty(knowledgeIds)) {
- return;
- }
- // 鏍¢獙鏂囨。鏄惁瀛樺湪
- knowledgeIds.forEach(knowledgeService::validateKnowledgeExists);
- }
-
- /**
- * 鏍¢獙宸ュ叿鏄惁瀛樺湪
- *
- * @param toolIds 宸ュ叿缂栧彿鍒楄〃
- */
- private void validateTools(List<Long> toolIds) {
- if (CollUtil.isEmpty(toolIds)) {
- return;
- }
- // 閬嶅巻鏍¢獙姣忎釜宸ュ叿鏄惁瀛樺湪
- toolIds.forEach(toolService::validateToolExists);
- }
-
- @Override
- public void deleteChatRole(Long id) {
- // 鏍¢獙瀛樺湪
- validateChatRoleExists(id);
- // 鍒犻櫎
- chatRoleMapper.deleteById(id);
- }
-
- @Override
- public void deleteChatRoleMy(Long id, Long userId) {
- // 鏍¢獙瀛樺湪
- AiChatRoleDO chatRole = validateChatRoleExists(id);
- if (ObjectUtil.notEqual(chatRole.getUserId(), userId)) {
- throw exception(CHAT_ROLE_NOT_EXISTS);
- }
- // 鍒犻櫎
- chatRoleMapper.deleteById(id);
- }
-
- private AiChatRoleDO validateChatRoleExists(Long id) {
- AiChatRoleDO chatRole = chatRoleMapper.selectById(id);
- if (chatRole == null) {
- throw exception(CHAT_ROLE_NOT_EXISTS);
- }
- return chatRole;
- }
-
- @Override
- public AiChatRoleDO getChatRole(Long id) {
- return chatRoleMapper.selectById(id);
- }
-
- @Override
- public List<AiChatRoleDO> getChatRoleList(Collection<Long> ids) {
- if (CollUtil.isEmpty(ids)) {
- return Collections.emptyList();
- }
- return chatRoleMapper.selectByIds(ids);
- }
-
- @Override
- public AiChatRoleDO validateChatRole(Long id) {
- AiChatRoleDO chatRole = validateChatRoleExists(id);
- if (CommonStatusEnum.isDisable(chatRole.getStatus())) {
- throw exception(CHAT_ROLE_DISABLE, chatRole.getName());
- }
- return chatRole;
- }
-
- @Override
- public PageResult<AiChatRoleDO> getChatRolePage(AiChatRolePageReqVO pageReqVO) {
- return chatRoleMapper.selectPage(pageReqVO);
- }
-
- @Override
- public PageResult<AiChatRoleDO> getChatRoleMyPage(AiChatRolePageReqVO pageReqVO, Long userId) {
- return chatRoleMapper.selectPageByMy(pageReqVO, userId);
- }
-
- @Override
- public List<String> getChatRoleCategoryList() {
- List<AiChatRoleDO> list = chatRoleMapper.selectListGroupByCategory(CommonStatusEnum.ENABLE.getStatus());
- return convertList(list, AiChatRoleDO::getCategory,
- role -> role != null && StrUtil.isNotBlank(role.getCategory()));
- }
-
- @Override
- public List<AiChatRoleDO> getChatRoleListByName(String name) {
- return chatRoleMapper.selectListByName(name);
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java
index be44482..4beea66 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelService.java
@@ -1,143 +1,36 @@
package cn.iocoder.yudao.module.ai.service.model;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelPageReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelSaveReqVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
-import dev.tinyflow.core.Tinyflow;
import jakarta.validation.Valid;
import org.springframework.ai.chat.model.ChatModel;
-import org.springframework.ai.image.ImageModel;
import org.springframework.ai.vectorstore.VectorStore;
-
import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;
-/**
- * AI 妯″瀷 Service 鎺ュ彛
- *
- * @author fansili
- * @since 2024/4/24 19:42
- */
public interface AiModelService {
- /**
- * 鍒涘缓妯″瀷
- *
- * @param createReqVO 鍒涘缓淇℃伅
- * @return 缂栧彿
- */
Long createModel(@Valid AiModelSaveReqVO createReqVO);
- /**
- * 鏇存柊妯″瀷
- *
- * @param updateReqVO 鏇存柊淇℃伅
- */
void updateModel(@Valid AiModelSaveReqVO updateReqVO);
- /**
- * 鍒犻櫎妯″瀷
- *
- * @param id 缂栧彿
- */
void deleteModel(Long id);
- /**
- * 鑾峰緱妯″瀷
- *
- * @param id 缂栧彿
- * @return 妯″瀷
- */
AiModelDO getModel(Long id);
- /**
- * 鑾峰緱榛樿鐨勬ā鍨�
- *
- * 濡傛灉鑾峰彇涓嶅埌锛屽垯鎶涘嚭 {@link cn.iocoder.yudao.framework.common.exception.ServiceException} 涓氬姟寮傚父
- *
- * @return 妯″瀷
- */
AiModelDO getRequiredDefaultModel(Integer type);
- /**
- * 鑾峰緱妯″瀷鍒嗛〉
- *
- * @param pageReqVO 鍒嗛〉鏌ヨ
- * @return 妯″瀷鍒嗛〉
- */
PageResult<AiModelDO> getModelPage(AiModelPageReqVO pageReqVO);
- /**
- * 鏍¢獙妯″瀷鏄惁鍙娇鐢�
- *
- * @param id 缂栧彿
- * @return 妯″瀷
- */
AiModelDO validateModel(Long id);
- /**
- * 鑾峰緱妯″瀷鍒楄〃
- *
- * @param status 鐘舵��
- * @param type 绫诲瀷
- * @param platform 骞冲彴锛屽厑璁哥┖
- * @return 妯″瀷鍒楄〃
- */
- List<AiModelDO> getModelListByStatusAndType(Integer status, Integer type,
- @Nullable String platform);
+ List<AiModelDO> getModelListByStatusAndType(Integer status, Integer type, @Nullable String platform);
- // ========== 涓� Spring AI 闆嗘垚 ==========
-
- /**
- * 鑾峰緱 ChatModel 瀵硅薄
- *
- * @param id 缂栧彿
- * @return ChatModel 瀵硅薄
- */
ChatModel getChatModel(Long id);
- /**
- * 鑾峰緱 ImageModel 瀵硅薄
- *
- * @param id 缂栧彿
- * @return ImageModel 瀵硅薄
- */
- ImageModel getImageModel(Long id);
-
- /**
- * 鑾峰緱 MidjourneyApi 瀵硅薄
- *
- * @param id 缂栧彿
- * @return MidjourneyApi 瀵硅薄
- */
- MidjourneyApi getMidjourneyApi(Long id);
-
- /**
- * 鑾峰緱 SunoApi 瀵硅薄
- *
- * @return SunoApi 瀵硅薄
- */
- SunoApi getSunoApi();
-
- /**
- * 鑾峰緱 VectorStore 瀵硅薄
- *
- * @param id 缂栧彿
- * @param metadataFields 鍏冩暟鎹殑瀹氫箟
- * @return VectorStore 瀵硅薄
- */
VectorStore getOrCreateVectorStore(Long id, Map<String, Class<?>> metadataFields);
-
- /**
- * 鑾峰彇 TinyFlow 鎵�闇� LLm Provider
- *
- * @param tinyflow tinyflow
- * @param modelId AI 妯″瀷 ID
- */
- void getLLmProvider4Tinyflow(Tinyflow tinyflow, Long modelId);
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java
index 32dd6f0..5668da0 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java
@@ -1,9 +1,5 @@
package cn.iocoder.yudao.module.ai.service.model;
-import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.AiModelFactory;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
@@ -11,52 +7,36 @@
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiModelSaveReqVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO;
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
-import cn.iocoder.yudao.module.ai.dal.mysql.model.AiChatMapper;
-import com.agentsflex.llm.ollama.OllamaLlm;
-import com.agentsflex.llm.ollama.OllamaLlmConfig;
-import com.agentsflex.llm.qwen.QwenLlm;
-import com.agentsflex.llm.qwen.QwenLlmConfig;
-import dev.tinyflow.core.Tinyflow;
+import cn.iocoder.yudao.module.ai.dal.mysql.model.AiModelMapper;
+import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
+import cn.iocoder.yudao.module.ai.framework.ai.core.model.AiModelFactory;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.embedding.EmbeddingModel;
-import org.springframework.ai.image.ImageModel;
-import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
+import org.springframework.ai.vectorstore.milvus.MilvusVectorStore;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
-
import java.util.List;
import java.util.Map;
-
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
-/**
- * AI 妯″瀷 Service 瀹炵幇绫�
- *
- * @author fansili
- */
@Service
@Validated
public class AiModelServiceImpl implements AiModelService {
@Resource
private AiApiKeyService apiKeyService;
-
@Resource
- private AiChatMapper modelMapper;
-
+ private AiModelMapper modelMapper;
@Resource
private AiModelFactory modelFactory;
@Override
public Long createModel(AiModelSaveReqVO createReqVO) {
- // 1. 鏍¢獙
AiPlatformEnum.validatePlatform(createReqVO.getPlatform());
apiKeyService.validateApiKey(createReqVO.getKeyId());
-
- // 2. 鎻掑叆
AiModelDO model = BeanUtils.toBean(createReqVO, AiModelDO.class);
modelMapper.insert(model);
return model.getId();
@@ -64,30 +44,16 @@
@Override
public void updateModel(AiModelSaveReqVO updateReqVO) {
- // 1. 鏍¢獙
validateModelExists(updateReqVO.getId());
AiPlatformEnum.validatePlatform(updateReqVO.getPlatform());
apiKeyService.validateApiKey(updateReqVO.getKeyId());
-
- // 2. 鏇存柊
- AiModelDO updateObj = BeanUtils.toBean(updateReqVO, AiModelDO.class);
- modelMapper.updateById(updateObj);
+ modelMapper.updateById(BeanUtils.toBean(updateReqVO, AiModelDO.class));
}
@Override
public void deleteModel(Long id) {
- // 鏍¢獙瀛樺湪
validateModelExists(id);
- // 鍒犻櫎
modelMapper.deleteById(id);
- }
-
- private AiModelDO validateModelExists(Long id) {
- AiModelDO model = modelMapper.selectById(id);
- if (modelMapper.selectById(id) == null) {
- throw exception(MODEL_NOT_EXISTS);
- }
- return model;
}
@Override
@@ -98,9 +64,7 @@
@Override
public AiModelDO getRequiredDefaultModel(Integer type) {
AiModelDO model = modelMapper.selectFirstByStatus(type, CommonStatusEnum.ENABLE.getStatus());
- if (model == null) {
- throw exception(MODEL_DEFAULT_NOT_EXISTS);
- }
+ if (model == null) throw exception(MODEL_DISABLE);
return model;
}
@@ -112,15 +76,19 @@
@Override
public AiModelDO validateModel(Long id) {
AiModelDO model = validateModelExists(id);
- if (CommonStatusEnum.isDisable(model.getStatus())) {
- throw exception(MODEL_DISABLE);
- }
+ if (CommonStatusEnum.isDisable(model.getStatus())) throw exception(MODEL_DISABLE);
return model;
}
@Override
public List<AiModelDO> getModelListByStatusAndType(Integer status, Integer type, String platform) {
return modelMapper.selectListByStatusAndType(status, type, platform);
+ }
+
+ private AiModelDO validateModelExists(Long id) {
+ AiModelDO model = modelMapper.selectById(id);
+ if (model == null) throw exception(MODEL_NOT_EXISTS);
+ return model;
}
// ========== 涓� Spring AI 闆嗘垚 ==========
@@ -130,72 +98,19 @@
AiModelDO model = validateModel(id);
AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());
AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
- return modelFactory.getOrCreateChatModel(platform, apiKey.getApiKey(), apiKey.getUrl());
- }
-
- @Override
- public ImageModel getImageModel(Long id) {
- AiModelDO model = validateModel(id);
- AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());
- AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
- return modelFactory.getOrCreateImageModel(platform, apiKey.getApiKey(), apiKey.getUrl());
- }
-
- @Override
- public MidjourneyApi getMidjourneyApi(Long id) {
- AiModelDO model = validateModel(id);
- AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());
- return modelFactory.getOrCreateMidjourneyApi(apiKey.getApiKey(), apiKey.getUrl());
- }
-
- @Override
- public SunoApi getSunoApi() {
- AiApiKeyDO apiKey = apiKeyService.getRequiredDefaultApiKey(
- AiPlatformEnum.SUNO.getPlatform(), CommonStatusEnum.ENABLE.getStatus());
- return modelFactory.getOrCreateSunoApi(apiKey.getApiKey(), apiKey.getUrl());
+ return modelFactory.getOrCreateChatModel(platform, apiKey.getApiKey(), apiKey.getUrl(), model.getModel());
}
@Override
public VectorStore getOrCreateVectorStore(Long id, Map<String, Class<?>> metadataFields) {
- // 鑾峰彇妯″瀷 + 瀵嗛挜
AiModelDO model = validateModel(id);
AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());
AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
- // 鍒涘缓鎴栬幏鍙� EmbeddingModel 瀵硅薄
EmbeddingModel embeddingModel = modelFactory.getOrCreateEmbeddingModel(
platform, apiKey.getApiKey(), apiKey.getUrl(), model.getModel());
- // 鍒涘缓鎴栬幏鍙� VectorStore 瀵硅薄
- return modelFactory.getOrCreateVectorStore(SimpleVectorStore.class, embeddingModel, metadataFields);
-// return modelFactory.getOrCreateVectorStore(QdrantVectorStore.class, embeddingModel, metadataFields);
-// return modelFactory.getOrCreateVectorStore(RedisVectorStore.class, embeddingModel, metadataFields);
-// return modelFactory.getOrCreateVectorStore(MilvusVectorStore.class, embeddingModel, metadataFields);
- }
-
- // TODO @lesan锛氭槸涓嶆槸杩斿洖 Llm 瀵硅薄浼氬ソ鐐瑰搱锛�
- @Override
- public void getLLmProvider4Tinyflow(Tinyflow tinyflow, Long modelId) {
- AiModelDO model = validateModel(modelId);
- AiApiKeyDO apiKey = apiKeyService.validateApiKey(model.getKeyId());
- AiPlatformEnum platform = AiPlatformEnum.validatePlatform(apiKey.getPlatform());
- switch (platform) {
- // TODO @lesan 鑰冭檻鍒版湭鏉ヤ笉闇�瑕佷娇鐢╝gents-flex 鐜板湪浠呮祴璇曢�氫箟鍗冮棶
- // TODO @lesan锛氥�愰噸瑕併�戞槸涓嶆槸鍙互瀹炵幇涓�涓� SpringAiLlm锛岃繖鏍风殑璇濓紝鍐呴儴鍏ㄩ儴鐢ㄥ畠灏卞ソ浜嗐�傚彧瀹炵幇 chat 閮ㄥ垎锛涜繖鏍凤紝灏辨妸 flex 浣滀负涓�涓� agent 妗嗘灦锛屽唴閮ㄨ皟鐢紝杩樻槸 spring ai 鐩稿叧鐨勩�傛垚鏈彲鑳戒綆涓�鐐癸紵锛�
- case TONG_YI:
- QwenLlmConfig qwenLlmConfig = new QwenLlmConfig();
- qwenLlmConfig.setApiKey(apiKey.getApiKey());
- qwenLlmConfig.setModel(model.getModel());
- // TODO @lesan锛氳繖涓湁鐐瑰鎬�傘�傘�傚鏋滀竴涓摼寮忛噷锛屾湁澶氫釜妯″瀷锛屽拫鏁村憖銆傘�傘��
- tinyflow.setLlmProvider(id -> new QwenLlm(qwenLlmConfig));
- break;
- case OLLAMA:
- OllamaLlmConfig ollamaLlmConfig = new OllamaLlmConfig();
- ollamaLlmConfig.setEndpoint(apiKey.getUrl());
- ollamaLlmConfig.setModel(model.getModel());
- tinyflow.setLlmProvider(id -> new OllamaLlm(ollamaLlmConfig));
- break;
- }
+ return modelFactory.getOrCreateVectorStore(MilvusVectorStore.class, embeddingModel, metadataFields);
}
}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolService.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolService.java
deleted file mode 100644
index fb23224..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolService.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package cn.iocoder.yudao.module.ai.service.model;
-
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolSaveReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO;
-import jakarta.validation.Valid;
-
-import java.util.Collection;
-import java.util.List;
-
-/**
- * AI 宸ュ叿 Service 鎺ュ彛
- *
- * @author 鑺嬮亾婧愮爜
- */
-public interface AiToolService {
-
- /**
- * 鍒涘缓宸ュ叿
- *
- * @param createReqVO 鍒涘缓淇℃伅
- * @return 缂栧彿
- */
- Long createTool(@Valid AiToolSaveReqVO createReqVO);
-
- /**
- * 鏇存柊宸ュ叿
- *
- * @param updateReqVO 鏇存柊淇℃伅
- */
- void updateTool(@Valid AiToolSaveReqVO updateReqVO);
-
- /**
- * 鍒犻櫎宸ュ叿
- *
- * @param id 缂栧彿
- */
- void deleteTool(Long id);
-
- /**
- * 鏍¢獙宸ュ叿鏄惁瀛樺湪
- *
- * @param id 缂栧彿
- */
- void validateToolExists(Long id);
-
- /**
- * 鑾峰緱宸ュ叿
- *
- * @param id 缂栧彿
- * @return 宸ュ叿
- */
- AiToolDO getTool(Long id);
-
- /**
- * 鑾峰緱宸ュ叿鍒楄〃
- *
- * @param ids 缂栧彿鍒楄〃
- * @return 宸ュ叿鍒楄〃
- */
- List<AiToolDO> getToolList(Collection<Long> ids);
-
- /**
- * 鑾峰緱宸ュ叿鍒嗛〉
- *
- * @param pageReqVO 鍒嗛〉鏌ヨ
- * @return 宸ュ叿鍒嗛〉
- */
- PageResult<AiToolDO> getToolPage(AiToolPageReqVO pageReqVO);
-
- /**
- * 鑾峰緱宸ュ叿鍒楄〃
- *
- * @param status 鐘舵��
- * @return 宸ュ叿鍒楄〃
- */
- List<AiToolDO> getToolListByStatus(Integer status);
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolServiceImpl.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolServiceImpl.java
deleted file mode 100644
index cb25a31..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiToolServiceImpl.java
+++ /dev/null
@@ -1,102 +0,0 @@
-package cn.iocoder.yudao.module.ai.service.model;
-
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolPageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.model.vo.tool.AiToolSaveReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiToolDO;
-import cn.iocoder.yudao.module.ai.dal.mysql.model.AiToolMapper;
-import jakarta.annotation.Resource;
-import org.springframework.ai.tool.ToolCallback;
-import org.springframework.ai.tool.resolution.ToolCallbackResolver;
-import org.springframework.stereotype.Service;
-import org.springframework.validation.annotation.Validated;
-
-import java.util.Collection;
-import java.util.List;
-
-import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.TOOL_NAME_NOT_EXISTS;
-import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.TOOL_NOT_EXISTS;
-
-/**
- * AI 宸ュ叿 Service 瀹炵幇绫�
- *
- * @author 鑺嬮亾婧愮爜
- */
-@Service
-@Validated
-public class AiToolServiceImpl implements AiToolService {
-
- @Resource
- private AiToolMapper toolMapper;
-
- @Resource
- private ToolCallbackResolver toolCallbackResolver;
-
- @Override
- public Long createTool(AiToolSaveReqVO createReqVO) {
- // 鏍¢獙鍚嶇О鏄惁瀛樺湪
- validateToolNameExists(createReqVO.getName());
-
- // 鎻掑叆
- AiToolDO tool = BeanUtils.toBean(createReqVO, AiToolDO.class);
- toolMapper.insert(tool);
- return tool.getId();
- }
-
- @Override
- public void updateTool(AiToolSaveReqVO updateReqVO) {
- // 1.1 鏍¢獙瀛樺湪
- validateToolExists(updateReqVO.getId());
- // 1.2 鏍¢獙鍚嶇О鏄惁瀛樺湪
- validateToolNameExists(updateReqVO.getName());
-
- // 2. 鏇存柊
- AiToolDO updateObj = BeanUtils.toBean(updateReqVO, AiToolDO.class);
- toolMapper.updateById(updateObj);
- }
-
- @Override
- public void deleteTool(Long id) {
- // 鏍¢獙瀛樺湪
- validateToolExists(id);
- // 鍒犻櫎
- toolMapper.deleteById(id);
- }
-
- @Override
- public void validateToolExists(Long id) {
- if (toolMapper.selectById(id) == null) {
- throw exception(TOOL_NOT_EXISTS);
- }
- }
-
- private void validateToolNameExists(String name) {
- ToolCallback toolCallback = toolCallbackResolver.resolve(name);
- if (toolCallback == null) {
- throw exception(TOOL_NAME_NOT_EXISTS, name);
- }
- }
-
- @Override
- public AiToolDO getTool(Long id) {
- return toolMapper.selectById(id);
- }
-
- @Override
- public List<AiToolDO> getToolList(Collection<Long> ids) {
- return toolMapper.selectByIds(ids);
- }
-
- @Override
- public PageResult<AiToolDO> getToolPage(AiToolPageReqVO pageReqVO) {
- return toolMapper.selectPage(pageReqVO);
- }
-
- @Override
- public List<AiToolDO> getToolListByStatus(Integer status) {
- return toolMapper.selectListByStatus(status);
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicService.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicService.java
deleted file mode 100644
index 49f8332..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicService.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package cn.iocoder.yudao.module.ai.service.music;
-
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.ai.controller.admin.music.vo.*;
-import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;
-import jakarta.validation.Valid;
-
-import java.util.List;
-
-/**
- * AI 闊充箰 Service 鎺ュ彛
- *
- * @author xiaoxin
- */
-public interface AiMusicService {
-
- /**
- * 闊充箰鐢熸垚
- *
- * @param userId 鐢ㄦ埛缂栧彿
- * @param reqVO 璇锋眰鍙傛暟
- * @return 鐢熸垚鐨勯煶涔怚D
- */
- List<Long> generateMusic(Long userId, AiSunoGenerateReqVO reqVO);
-
- /**
- * 鍚屾闊充箰浠诲姟
- *
- * @return 鍚屾鏁伴噺
- */
- Integer syncMusic();
-
- /**
- * 鏇存柊闊充箰鍙戝竷鐘舵��
- *
- * @param updateReqVO 鏇存柊淇℃伅
- */
- void updateMusic(@Valid AiMusicUpdateReqVO updateReqVO);
-
- /**
- * 鏇存柊鎴戠殑闊充箰
- *
- * @param updateReqVO 鏇存柊淇℃伅
- */
- void updateMyMusic(@Valid AiMusicUpdateMyReqVO updateReqVO, Long userId);
-
- /**
- * 鍒犻櫎AI 闊充箰
- *
- * @param id 缂栧彿
- */
- void deleteMusic(Long id);
-
- /**
- * 鍒犻櫎銆愭垜鐨勩�戦煶涔愯褰�
- *
- * @param id 闊充箰缂栧彿
- * @param userId 鐢ㄦ埛缂栧彿
- */
- void deleteMusicMy(Long id, Long userId);
-
- /**
- * 鑾峰緱AI 闊充箰
- *
- * @param id 闊充箰缂栧彿
- * @return 闊充箰鍐呭
- */
- AiMusicDO getMusic(Long id);
-
- /**
- * 鑾峰緱闊充箰鍒嗛〉
- *
- * @param pageReqVO 鍒嗛〉鏌ヨ
- * @return 闊充箰鍒嗛〉
- */
- PageResult<AiMusicDO> getMusicPage(AiMusicPageReqVO pageReqVO);
-
- /**
- * 鑾峰緱銆愭垜鐨勩�戦煶涔愬垎椤�
- *
- * @param pageReqVO 鍒嗛〉鏌ヨ
- * @param userId 鐢ㄦ埛缂栧彿
- * @return 闊充箰鍒嗛〉
- */
- PageResult<AiMusicDO> getMusicMyPage(AiMusicPageReqVO pageReqVO, Long userId);
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicServiceImpl.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicServiceImpl.java
deleted file mode 100644
index 4e71c5e..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/music/AiMusicServiceImpl.java
+++ /dev/null
@@ -1,218 +0,0 @@
-package cn.iocoder.yudao.module.ai.service.music;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.text.StrPool;
-import cn.hutool.core.util.ObjUtil;
-import cn.hutool.core.util.ObjectUtil;
-import cn.hutool.core.util.StrUtil;
-import cn.hutool.http.HttpUtil;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicPageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicUpdateMyReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiMusicUpdateReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.music.vo.AiSunoGenerateReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO;
-import cn.iocoder.yudao.module.ai.dal.mysql.music.AiMusicMapper;
-import cn.iocoder.yudao.module.ai.enums.music.AiMusicGenerateModeEnum;
-import cn.iocoder.yudao.module.ai.enums.music.AiMusicStatusEnum;
-import cn.iocoder.yudao.module.ai.service.model.AiModelService;
-import cn.iocoder.yudao.module.infra.api.file.FileApi;
-import jakarta.annotation.Resource;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.util.*;
-
-import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
-import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.IMAGE_NOT_EXISTS;
-import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.MUSIC_NOT_EXISTS;
-
-/**
- * AI 闊充箰 Service 瀹炵幇绫�
- *
- * @author xiaoxin
- */
-@Service
-@Slf4j
-public class AiMusicServiceImpl implements AiMusicService {
-
- @Resource
- private AiModelService modelService;
-
- @Resource
- private AiMusicMapper musicMapper;
-
- @Resource
- private FileApi fileApi;
-
- @Override
- @Transactional(rollbackFor = Exception.class)
- public List<Long> generateMusic(Long userId, AiSunoGenerateReqVO reqVO) {
- // 1. 璋冪敤 Suno 鐢熸垚闊充箰
- SunoApi sunoApi = modelService.getSunoApi();
- List<SunoApi.MusicData> musicDataList;
- if (Objects.equals(AiMusicGenerateModeEnum.DESCRIPTION.getMode(), reqVO.getGenerateMode())) {
- // 1.1 鎻忚堪妯″紡
- SunoApi.MusicGenerateRequest generateRequest = new SunoApi.MusicGenerateRequest(
- reqVO.getPrompt(), reqVO.getModel(), reqVO.getMakeInstrumental());
- musicDataList = sunoApi.generate(generateRequest);
- } else if (Objects.equals(AiMusicGenerateModeEnum.LYRIC.getMode(), reqVO.getGenerateMode())) {
- // 1.2 姝岃瘝妯″紡
- SunoApi.MusicGenerateRequest generateRequest = new SunoApi.MusicGenerateRequest(
- reqVO.getPrompt(), reqVO.getModel(), CollUtil.join(reqVO.getTags(), StrPool.COMMA), reqVO.getTitle());
- musicDataList = sunoApi.customGenerate(generateRequest);
- } else {
- throw new IllegalArgumentException(StrUtil.format("鏈煡鐢熸垚妯″紡({})", reqVO));
- }
-
- // 2. 鎻掑叆鏁版嵁搴�
- if (CollUtil.isEmpty(musicDataList)) {
- return Collections.emptyList();
- }
- List<AiMusicDO> musicList = buildMusicDOList(musicDataList);
- musicList.forEach(music -> music.setUserId(userId).setPlatform(reqVO.getPlatform()).setGenerateMode(reqVO.getGenerateMode()));
- musicMapper.insertBatch(musicList);
- return convertList(musicList, AiMusicDO::getId);
- }
-
- @Override
- public Integer syncMusic() {
- List<AiMusicDO> streamingTask = musicMapper.selectListByStatus(AiMusicStatusEnum.IN_PROGRESS.getStatus());
- if (CollUtil.isEmpty(streamingTask)) {
- return 0;
- }
- log.info("[syncMusic][Suno 寮�濮嬪悓姝�, 鍏� ({}) 涓换鍔", streamingTask.size());
-
- // GET 璇锋眰锛屼负閬垮厤鍙傛暟杩囬暱锛屽垎鎵规澶勭悊
- SunoApi sunoApi = modelService.getSunoApi();
- CollUtil.split(streamingTask, 36).forEach(chunkList -> {
- Map<String, Long> taskIdMap = convertMap(chunkList, AiMusicDO::getTaskId, AiMusicDO::getId);
- List<SunoApi.MusicData> musicTaskList = sunoApi.getMusicList(new ArrayList<>(taskIdMap.keySet()));
- if (CollUtil.isEmpty(musicTaskList)) {
- log.warn("Suno 浠诲姟鍚屾澶辫触, 浠诲姟ID: [{}]", taskIdMap.keySet());
- return;
- }
- // 鏇存柊杩涘害
- List<AiMusicDO> updateMusicList = buildMusicDOList(musicTaskList);
- updateMusicList.forEach(music -> music.setId(taskIdMap.get(music.getTaskId())));
- musicMapper.updateBatch(updateMusicList);
- });
- return streamingTask.size();
- }
-
- @Override
- public void updateMusic(AiMusicUpdateReqVO updateReqVO) {
- // 鏍¢獙瀛樺湪
- validateMusicExists(updateReqVO.getId());
- // 鏇存柊
- musicMapper.updateById(new AiMusicDO().setId(updateReqVO.getId()).setPublicStatus(updateReqVO.getPublicStatus()));
- }
-
- @Override
- public void updateMyMusic(AiMusicUpdateMyReqVO updateReqVO, Long userId) {
- // 鏍¢獙闊充箰鏄惁瀛樺湪
- AiMusicDO musicDO = validateMusicExists(updateReqVO.getId());
- if (ObjUtil.notEqual(musicDO.getUserId(), userId)) {
- throw exception(MUSIC_NOT_EXISTS);
- }
- // 鏇存柊
- musicMapper.updateById(new AiMusicDO().setId(updateReqVO.getId()).setTitle(updateReqVO.getTitle()));
- }
-
- @Override
- public void deleteMusic(Long id) {
- // 鏍¢獙瀛樺湪
- validateMusicExists(id);
- // 鍒犻櫎
- musicMapper.deleteById(id);
- }
-
- @Override
- public void deleteMusicMy(Long id, Long userId) {
- // 1. 鏍¢獙鏄惁瀛樺湪
- AiMusicDO music = validateMusicExists(id);
- if (ObjUtil.notEqual(music.getUserId(), userId)) {
- throw exception(IMAGE_NOT_EXISTS);
- }
- // 2. 鍒犻櫎璁板綍
- musicMapper.deleteById(id);
- }
-
- @Override
- public AiMusicDO getMusic(Long id) {
- return musicMapper.selectById(id);
- }
-
- @Override
- public PageResult<AiMusicDO> getMusicPage(AiMusicPageReqVO pageReqVO) {
- return musicMapper.selectPage(pageReqVO);
- }
-
- @Override
- public PageResult<AiMusicDO> getMusicMyPage(AiMusicPageReqVO pageReqVO, Long userId) {
- return musicMapper.selectPageByMy(pageReqVO, userId);
- }
-
- /**
- * 鏋勫缓 AiMusicDO 闆嗗悎
- *
- * @param musicList suno 闊充箰浠诲姟鍒楄〃
- * @return AiMusicDO 闆嗗悎
- */
- private List<AiMusicDO> buildMusicDOList(List<SunoApi.MusicData> musicList) {
- return convertList(musicList, musicData -> {
- Integer status = Objects.equals("complete", musicData.status()) ? AiMusicStatusEnum.SUCCESS.getStatus()
- : Objects.equals("error", musicData.status()) ? AiMusicStatusEnum.FAIL.getStatus()
- : AiMusicStatusEnum.IN_PROGRESS.getStatus();
- return new AiMusicDO()
- .setTaskId(musicData.id()).setModel(musicData.modelName())
- .setDescription(musicData.gptDescriptionPrompt())
- .setAudioUrl(downloadFile(status, musicData.audioUrl()))
- .setVideoUrl(downloadFile(status, musicData.videoUrl()))
- .setImageUrl(downloadFile(status, musicData.imageUrl()))
- .setTitle(musicData.title()).setDuration(musicData.duration())
- .setLyric(musicData.lyric()).setTags(StrUtil.split(musicData.tags(), StrPool.COMMA))
- .setErrorMessage(musicData.errorMessage())
- .setStatus(status);
- });
- }
-
- /**
- * 闊充箰鐢熸垚濂藉悗锛屽皢闊抽鏂囦欢涓婁紶鍒版枃浠舵湇鍔″櫒
- *
- * @param status 闊充箰鐘舵��
- * @param url 闊抽鏂囦欢鍦板潃
- * @return 鍐呴儴鏂囦欢鍦板潃
- */
- private String downloadFile(Integer status, String url) {
- if (StrUtil.isBlank(url) || ObjectUtil.notEqual(status, AiMusicStatusEnum.SUCCESS.getStatus())) {
- return url;
- }
- try {
- byte[] bytes = HttpUtil.downloadBytes(url);
- return fileApi.createFile(bytes);
- } catch (Exception e) {
- log.error("[downloadFile][url({}) 涓嬭浇澶辫触]", url, e);
- return url;
- }
- }
-
- /**
- * 鏍¢獙闊充箰鏄惁瀛樺湪
- *
- * @param id 闊充箰缂栧彿
- * @return 闊充箰淇℃伅
- */
- private AiMusicDO validateMusicExists(Long id) {
- AiMusicDO music = musicMapper.selectById(id);
- if (music == null) {
- throw exception(MUSIC_NOT_EXISTS);
- }
- return music;
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowService.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowService.java
deleted file mode 100644
index 51a3aea..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowService.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package cn.iocoder.yudao.module.ai.service.workflow;
-
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowPageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowSaveReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowTestReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.workflow.AiWorkflowDO;
-import jakarta.validation.Valid;
-
-/**
- * AI 宸ヤ綔娴� Service 鎺ュ彛
- *
- * @author lesan
- */
-public interface AiWorkflowService {
-
- /**
- * 鍒涘缓 AI 宸ヤ綔娴�
- *
- * @param createReqVO 鍒涘缓淇℃伅
- * @return 缂栧彿
- */
- Long createWorkflow(@Valid AiWorkflowSaveReqVO createReqVO);
-
- /**
- * 鏇存柊 AI 宸ヤ綔娴�
- *
- * @param updateReqVO 鏇存柊淇℃伅
- */
- void updateWorkflow(@Valid AiWorkflowSaveReqVO updateReqVO);
-
- /**
- * 鍒犻櫎 AI 宸ヤ綔娴�
- *
- * @param id 缂栧彿
- */
- void deleteWorkflow(Long id);
-
- /**
- * 鑾峰緱 AI 宸ヤ綔娴�
- *
- * @param id 缂栧彿
- * @return AI 宸ヤ綔娴�
- */
- AiWorkflowDO getWorkflow(Long id);
-
- /**
- * 鑾峰緱 AI 宸ヤ綔娴佸垎椤�
- *
- * @param pageReqVO 鍒嗛〉鏌ヨ
- * @return AI 宸ヤ綔娴佸垎椤�
- */
- PageResult<AiWorkflowDO> getWorkflowPage(AiWorkflowPageReqVO pageReqVO);
-
- /**
- * 娴嬭瘯 AI 宸ヤ綔娴�
- *
- * @param testReqVO 娴嬭瘯鏁版嵁
- */
- Object testWorkflow(AiWorkflowTestReqVO testReqVO);
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowServiceImpl.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowServiceImpl.java
deleted file mode 100644
index ac16e87..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/workflow/AiWorkflowServiceImpl.java
+++ /dev/null
@@ -1,144 +0,0 @@
-package cn.iocoder.yudao.module.ai.service.workflow;
-
-import cn.hutool.core.util.ObjUtil;
-import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowPageReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowSaveReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.workflow.vo.AiWorkflowTestReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.workflow.AiWorkflowDO;
-import cn.iocoder.yudao.module.ai.dal.mysql.workflow.AiWorkflowMapper;
-import cn.iocoder.yudao.module.ai.service.model.AiModelService;
-import com.alibaba.fastjson.JSONArray;
-import com.alibaba.fastjson.JSONObject;
-import dev.tinyflow.core.Tinyflow;
-import jakarta.annotation.Resource;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-
-import java.util.Map;
-
-import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.WORKFLOW_CODE_EXISTS;
-import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.WORKFLOW_NOT_EXISTS;
-
-/**
- * AI 宸ヤ綔娴� Service 瀹炵幇绫�
- *
- * @author lesan
- */
-@Service
-@Slf4j
-public class AiWorkflowServiceImpl implements AiWorkflowService {
-
- @Resource
- private AiWorkflowMapper workflowMapper;
-
- @Resource
- private AiModelService apiModelService;
-
- @Override
- public Long createWorkflow(AiWorkflowSaveReqVO createReqVO) {
- // 1. 鍙傛暟鏍¢獙
- validateCodeUnique(null, createReqVO.getCode());
-
- // 2. 鎻掑叆宸ヤ綔娴侀厤缃�
- AiWorkflowDO workflow = BeanUtils.toBean(createReqVO, AiWorkflowDO.class);
- workflowMapper.insert(workflow);
- return workflow.getId();
- }
-
- @Override
- public void updateWorkflow(AiWorkflowSaveReqVO updateReqVO) {
- // 1. 鍙傛暟鏍¢獙
- validateWorkflowExists(updateReqVO.getId());
- validateCodeUnique(updateReqVO.getId(), updateReqVO.getCode());
-
- // 2. 鏇存柊宸ヤ綔娴侀厤缃�
- AiWorkflowDO workflow = BeanUtils.toBean(updateReqVO, AiWorkflowDO.class);
- workflowMapper.updateById(workflow);
- }
-
- @Override
- public void deleteWorkflow(Long id) {
- // 1. 鏍¢獙瀛樺湪
- validateWorkflowExists(id);
-
- // 2. 鍒犻櫎宸ヤ綔娴侀厤缃�
- workflowMapper.deleteById(id);
- }
-
- private AiWorkflowDO validateWorkflowExists(Long id) {
- if (ObjUtil.isNull(id)) {
- throw exception(WORKFLOW_NOT_EXISTS);
- }
- AiWorkflowDO workflow = workflowMapper.selectById(id);
- if (ObjUtil.isNull(workflow)) {
- throw exception(WORKFLOW_NOT_EXISTS);
- }
- return workflow;
- }
-
- private void validateCodeUnique(Long id, String code) {
- if (StrUtil.isBlank(code)) {
- return;
- }
- AiWorkflowDO workflow = workflowMapper.selectByCode(code);
- if (ObjUtil.isNull(workflow)) {
- return;
- }
- if (ObjUtil.isNull(id)) {
- throw exception(WORKFLOW_CODE_EXISTS);
- }
- if (ObjUtil.notEqual(workflow.getId(), id)) {
- throw exception(WORKFLOW_CODE_EXISTS);
- }
- }
-
- @Override
- public AiWorkflowDO getWorkflow(Long id) {
- return workflowMapper.selectById(id);
- }
-
- @Override
- public PageResult<AiWorkflowDO> getWorkflowPage(AiWorkflowPageReqVO pageReqVO) {
- return workflowMapper.selectPage(pageReqVO);
- }
-
- @Override
- public Object testWorkflow(AiWorkflowTestReqVO testReqVO) {
- // 鍔犺浇 graph
- String graph = testReqVO.getGraph() != null ? testReqVO.getGraph()
- : validateWorkflowExists(testReqVO.getId()).getGraph();
-
- // 鏋勫缓 TinyFlow 鎵ц閾�
- Tinyflow tinyflow = parseFlowParam(graph);
-
- // 鎵ц
- Map<String, Object> variables = testReqVO.getParams();
- return tinyflow.toChain().executeForResult(variables);
- }
-
- private Tinyflow parseFlowParam(String graph) {
- // TODO @lesan锛氬彲浠ヤ娇鐢� jackson 鍝囷紵
- JSONObject json = JSONObject.parseObject(graph);
- JSONArray nodeArr = json.getJSONArray("nodes");
- Tinyflow tinyflow = new Tinyflow(json.toJSONString());
- for (int i = 0; i < nodeArr.size(); i++) {
- JSONObject node = nodeArr.getJSONObject(i);
- switch (node.getString("type")) {
- case "llmNode":
- JSONObject data = node.getJSONObject("data");
- apiModelService.getLLmProvider4Tinyflow(tinyflow, data.getLong("llmId"));
- break;
- case "internalNode":
- break;
- default:
- break;
- }
- }
- return tinyflow;
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteService.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteService.java
deleted file mode 100644
index f2dd489..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteService.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package cn.iocoder.yudao.module.ai.service.write;
-
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWriteGenerateReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWritePageReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.write.AiWriteDO;
-import reactor.core.publisher.Flux;
-
-/**
- * AI 鍐欎綔 Service 鎺ュ彛
- *
- * @author xiaoxin
- */
-public interface AiWriteService {
-
- /**
- * 鐢熸垚鍐欎綔鍐呭
- *
- * @param generateReqVO 浣滄枃鐢熸垚璇锋眰鍙傛暟
- * @param userId 鐢ㄦ埛缂栧彿
- * @return 鐢熸垚缁撴灉
- */
- Flux<CommonResult<String>> generateWriteContent(AiWriteGenerateReqVO generateReqVO, Long userId);
-
- /**
- * 鍒犻櫎鍐欎綔
- *
- * @param id 缂栧彿
- */
- void deleteWrite(Long id);
-
- /**
- * 鑾峰緱鍐欎綔鍒嗛〉
- *
- * @param pageReqVO 鍒嗛〉鏌ヨ
- * @return AI 鍐欎綔鍒嗛〉
- */
- PageResult<AiWriteDO> getWritePage(AiWritePageReqVO pageReqVO);
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java
deleted file mode 100644
index 179188d..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/write/AiWriteServiceImpl.java
+++ /dev/null
@@ -1,178 +0,0 @@
-package cn.iocoder.yudao.module.ai.service.write;
-
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.util.ObjUtil;
-import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;
-import cn.iocoder.yudao.module.ai.enums.model.AiModelTypeEnum;
-import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
-import cn.iocoder.yudao.module.ai.util.AiUtils;
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
-import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWriteGenerateReqVO;
-import cn.iocoder.yudao.module.ai.controller.admin.write.vo.AiWritePageReqVO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
-import cn.iocoder.yudao.module.ai.dal.dataobject.write.AiWriteDO;
-import cn.iocoder.yudao.module.ai.dal.mysql.write.AiWriteMapper;
-import cn.iocoder.yudao.module.ai.enums.AiChatRoleEnum;
-import cn.iocoder.yudao.module.ai.enums.DictTypeConstants;
-import cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants;
-import cn.iocoder.yudao.module.ai.enums.write.AiWriteTypeEnum;
-import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
-import cn.iocoder.yudao.module.ai.service.model.AiModelService;
-import jakarta.annotation.Resource;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.SystemMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.model.StreamingChatModel;
-import org.springframework.ai.chat.prompt.ChatOptions;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.stereotype.Service;
-import reactor.core.publisher.Flux;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.framework.common.pojo.CommonResult.error;
-import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
-import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*;
-
-/**
- * AI 鍐欎綔 Service 瀹炵幇绫�
- *
- * @author xiaoxin
- */
-@Service
-@Slf4j
-public class AiWriteServiceImpl implements AiWriteService {
-
- @Resource
- private AiModelService modalService;
- @Resource
- private AiChatRoleService chatRoleService;
-
- @Resource
- private AiWriteMapper writeMapper;
-
- @Override
- public Flux<CommonResult<String>> generateWriteContent(AiWriteGenerateReqVO generateReqVO, Long userId) {
- // 1 鑾峰彇鍐欎綔妯″瀷銆傚皾璇曡幏鍙栧啓浣滃姪鎵嬭鑹诧紝娌℃湁鍒欎娇鐢ㄩ粯璁ゆā鍨�
- AiChatRoleDO writeRole = CollUtil.getFirst(
- chatRoleService.getChatRoleListByName(AiChatRoleEnum.AI_WRITE_ROLE.getName()));
- // 1.1 鑾峰彇鍐欎綔鎵ц妯″瀷
- AiModelDO model = getModel(writeRole);
- // 1.2 鑾峰彇瑙掕壊璁惧畾娑堟伅
- String systemMessage = Objects.nonNull(writeRole) && StrUtil.isNotBlank(writeRole.getSystemMessage())
- ? writeRole.getSystemMessage() : AiChatRoleEnum.AI_WRITE_ROLE.getSystemMessage();
- // 1.3 鏍¢獙骞冲彴
- AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
- StreamingChatModel chatModel = modalService.getChatModel(model.getId());
-
- // 2. 鎻掑叆鍐欎綔淇℃伅
- AiWriteDO writeDO = BeanUtils.toBean(generateReqVO, AiWriteDO.class, write -> write.setUserId(userId)
- .setPlatform(platform.getPlatform()).setModelId(model.getId()).setModel(model.getModel()));
- writeMapper.insert(writeDO);
-
- // 3.1 鏋勫缓 Prompt锛屽苟杩涜璋冪敤
- Prompt prompt = buildPrompt(generateReqVO, model, systemMessage);
- Flux<ChatResponse> streamResponse = chatModel.stream(prompt);
-
- // 3.2 娴佸紡杩斿洖
- StringBuffer contentBuffer = new StringBuffer();
- return streamResponse.map(chunk -> {
- String newContent = chunk.getResult() != null ? chunk.getResult().getOutput().getText() : null;
- newContent = StrUtil.nullToDefault(newContent, ""); // 閬垮厤 null 鐨� 鎯呭喌
- contentBuffer.append(newContent);
- // 鍝嶅簲缁撴灉
- return success(newContent);
- }).doOnComplete(() -> {
- // 蹇界暐绉熸埛锛屽洜涓� Flux 寮傛鏃犳硶閫忎紶绉熸埛
- TenantUtils.executeIgnore(() ->
- writeMapper.updateById(new AiWriteDO().setId(writeDO.getId()).setGeneratedContent(contentBuffer.toString())));
- }).doOnError(throwable -> {
- log.error("[generateWriteContent][generateReqVO({}) 鍙戠敓寮傚父]", generateReqVO, throwable);
- // 蹇界暐绉熸埛锛屽洜涓� Flux 寮傛鏃犳硶閫忎紶绉熸埛
- TenantUtils.executeIgnore(() ->
- writeMapper.updateById(new AiWriteDO().setId(writeDO.getId()).setErrorMessage(throwable.getMessage())));
- }).onErrorResume(error -> Flux.just(error(ErrorCodeConstants.WRITE_STREAM_ERROR)));
- }
-
- private AiModelDO getModel(AiChatRoleDO writeRole) {
- AiModelDO model = null;
- if (Objects.nonNull(writeRole) && Objects.nonNull(writeRole.getModelId())) {
- model = modalService.getModel(writeRole.getModelId());
- }
- if (model == null) {
- model = modalService.getRequiredDefaultModel(AiModelTypeEnum.CHAT.getType());
- }
- // 鏍¢獙妯″瀷瀛樺湪銆佷笖鍚堟硶
- if (model == null) {
- throw exception(MODEL_NOT_EXISTS);
- }
- if (ObjUtil.notEqual(model.getType(), AiModelTypeEnum.CHAT.getType())) {
- throw exception(MODEL_USE_TYPE_ERROR);
- }
- return model;
- }
-
- private Prompt buildPrompt(AiWriteGenerateReqVO generateReqVO, AiModelDO model, String systemMessage) {
- // 1. 鏋勫缓 message 鍒楄〃
- List<Message> chatMessages = buildMessages(generateReqVO, systemMessage);
- // 2. 鏋勫缓 options 瀵硅薄
- AiPlatformEnum platform = AiPlatformEnum.validatePlatform(model.getPlatform());
- ChatOptions options = AiUtils.buildChatOptions(platform, model.getModel(), model.getTemperature(), model.getMaxTokens());
- return new Prompt(chatMessages, options);
- }
-
- private List<Message> buildMessages(AiWriteGenerateReqVO generateReqVO, String systemMessage) {
- List<Message> chatMessages = new ArrayList<>();
- if (StrUtil.isNotBlank(systemMessage)) {
- // 1.1 瑙掕壊璁惧畾
- chatMessages.add(new SystemMessage(systemMessage));
- }
- // 1.2 鐢ㄦ埛杈撳叆
- chatMessages.add(new UserMessage(buildUserMessage(generateReqVO)));
- return chatMessages;
- }
-
- private String buildUserMessage(AiWriteGenerateReqVO generateReqVO) {
- String format = DictFrameworkUtils.parseDictDataLabel(DictTypeConstants.AI_WRITE_FORMAT, generateReqVO.getFormat());
- String tone = DictFrameworkUtils.parseDictDataLabel(DictTypeConstants.AI_WRITE_TONE, generateReqVO.getTone());
- String language = DictFrameworkUtils.parseDictDataLabel(DictTypeConstants.AI_WRITE_LANGUAGE, generateReqVO.getLanguage());
- String length = DictFrameworkUtils.parseDictDataLabel(DictTypeConstants.AI_WRITE_LENGTH, generateReqVO.getLength());
- // 鏍煎紡鍖� prompt
- String prompt = generateReqVO.getPrompt();
- if (Objects.equals(generateReqVO.getType(), AiWriteTypeEnum.WRITING.getType())) {
- return StrUtil.format(AiWriteTypeEnum.WRITING.getPrompt(), prompt, format, tone, language, length);
- } else {
- return StrUtil.format(AiWriteTypeEnum.REPLY.getPrompt(), generateReqVO.getOriginalContent(), prompt, format, tone, language, length);
- }
- }
-
- @Override
- public void deleteWrite(Long id) {
- // 鏍¢獙瀛樺湪
- validateWriteExists(id);
- // 鍒犻櫎
- writeMapper.deleteById(id);
- }
-
- private void validateWriteExists(Long id) {
- if (writeMapper.selectById(id) == null) {
- throw exception(WRITE_NOT_EXISTS);
- }
- }
-
- @Override
- public PageResult<AiWriteDO> getWritePage(AiWritePageReqVO pageReqVO) {
- return writeMapper.selectPage(pageReqVO);
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/function/DirectoryListToolFunction.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/function/DirectoryListToolFunction.java
deleted file mode 100644
index 8e75d5d..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/function/DirectoryListToolFunction.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package cn.iocoder.yudao.module.ai.tool.function;
-
-import cn.hutool.core.date.LocalDateTimeUtil;
-import cn.hutool.core.io.FileUtil;
-import cn.hutool.core.util.ArrayUtil;
-import cn.hutool.core.util.StrUtil;
-import com.fasterxml.jackson.annotation.JsonClassDescription;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonPropertyDescription;
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import org.springframework.stereotype.Component;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.function.Function;
-
-import static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN;
-import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
-
-/**
- * 宸ュ叿锛氬垪鍑烘寚瀹氱洰褰曠殑鏂囦欢鍒楄〃
- *
- * @author 鑺嬮亾婧愮爜
- */
-@Component("directory_list")
-public class DirectoryListToolFunction implements Function<DirectoryListToolFunction.Request, DirectoryListToolFunction.Response> {
-
- @Data
- @JsonClassDescription("鍒楀嚭鎸囧畾鐩綍鐨勬枃浠跺垪琛�")
- public static class Request {
-
- /**
- * 鐩綍璺緞
- */
- @JsonProperty(required = true, value = "path")
- @JsonPropertyDescription("鐩綍璺緞锛屼緥濡傝锛�/Users/yunai")
- private String path;
-
- }
-
- @Data
- @AllArgsConstructor
- @NoArgsConstructor
- public static class Response {
-
- /**
- * 鏂囦欢鍒楄〃
- */
- private List<File> files;
-
- @Data
- public static class File {
-
- /**
- * 鏄惁涓虹洰褰�
- */
- private Boolean directory;
-
- /**
- * 鍚嶇О
- */
- private String name;
-
- /**
- * 澶у皬锛屼粎瀵规枃浠舵湁鏁�
- */
- private String size;
-
- /**
- * 鏈�鍚庝慨鏀规椂闂�
- */
- private String lastModified;
-
- }
-
- }
-
- @Override
- public Response apply(Request request) {
- // 鏍¢獙鐩綍瀛樺湪
- String path = StrUtil.blankToDefault(request.getPath(), "/");
- if (!FileUtil.exist(path) || !FileUtil.isDirectory(path)) {
- return new Response(Collections.emptyList());
- }
- // 鍒楀嚭鐩綍
- File[] files = FileUtil.ls(path);
- if (ArrayUtil.isEmpty(files)) {
- return new Response(Collections.emptyList());
- }
- return new Response(convertList(Arrays.asList(files), file ->
- new Response.File().setDirectory(file.isDirectory()).setName(file.getName())
- .setLastModified(LocalDateTimeUtil.format(LocalDateTimeUtil.of(file.lastModified()), NORM_DATETIME_PATTERN))));
- }
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/function/UserProfileQueryToolFunction.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/function/UserProfileQueryToolFunction.java
deleted file mode 100644
index a4e00d6..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/function/UserProfileQueryToolFunction.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package cn.iocoder.yudao.module.ai.tool.function;
-
-import cn.iocoder.yudao.module.ai.util.AiUtils;
-import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
-import cn.iocoder.yudao.framework.security.core.LoginUser;
-import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
-import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
-import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
-import com.fasterxml.jackson.annotation.JsonClassDescription;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonPropertyDescription;
-import jakarta.annotation.Resource;
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import org.springframework.ai.chat.model.ToolContext;
-import org.springframework.stereotype.Component;
-
-import java.util.function.BiFunction;
-
-/**
- * 宸ュ叿锛氱敤鎴蜂俊鎭煡璇�
- *
- * 鍚屾椂锛屼篃鏄睍绀� ToolContext 涓婁笅鏂囩殑浣跨敤
- *
- * @author Ren
- */
-@Component("user_profile_query")
-public class UserProfileQueryToolFunction
- implements BiFunction<UserProfileQueryToolFunction.Request, ToolContext, UserProfileQueryToolFunction.Response> {
-
- @Resource
- private AdminUserApi adminUserApi;
-
- @Data
- @JsonClassDescription("鐢ㄦ埛淇℃伅鏌ヨ")
- public static class Request {
-
- /**
- * 鐢ㄦ埛缂栧彿
- */
- @JsonProperty(value = "id")
- @JsonPropertyDescription("鐢ㄦ埛缂栧彿锛屼緥濡傝锛�1銆傚鏋滄煡璇㈣嚜宸憋紝鍒� id 涓虹┖")
- private Long id;
-
- }
-
- @Data
- @AllArgsConstructor
- @NoArgsConstructor
- public static class Response {
-
- /**
- * 鐢ㄦ埛ID
- */
- private Long id;
- /**
- * 鐢ㄦ埛鏄电О
- */
- private String nickname;
-
- /**
- * 鎵嬫満鍙风爜
- */
- private String mobile;
- /**
- * 鐢ㄦ埛澶村儚
- */
- private String avatar;
-
- }
-
- @Override
- public Response apply(Request request, ToolContext toolContext) {
- Long tenantId = (Long) toolContext.getContext().get(AiUtils.TOOL_CONTEXT_TENANT_ID);
- if (tenantId == null) {
- return new Response();
- }
- if (request.getId() == null) {
- LoginUser loginUser = (LoginUser) toolContext.getContext().get(AiUtils.TOOL_CONTEXT_LOGIN_USER);
- if (loginUser == null) {
- return new Response();
- }
- request.setId(loginUser.getId());
- }
- return TenantUtils.execute(tenantId, () -> {
- AdminUserRespDTO user = adminUserApi.getUser(request.getId());
- return BeanUtils.toBean(user, Response.class);
- });
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/function/WeatherQueryToolFunction.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/function/WeatherQueryToolFunction.java
deleted file mode 100644
index 689ea00..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/function/WeatherQueryToolFunction.java
+++ /dev/null
@@ -1,118 +0,0 @@
-package cn.iocoder.yudao.module.ai.tool.function;
-
-import cn.hutool.core.date.LocalDateTimeUtil;
-import cn.hutool.core.util.RandomUtil;
-import cn.hutool.core.util.StrUtil;
-import com.fasterxml.jackson.annotation.JsonClassDescription;
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.annotation.JsonPropertyDescription;
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import org.springframework.stereotype.Component;
-
-import java.time.LocalDateTime;
-import java.util.function.Function;
-
-import static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN;
-
-/**
- * 宸ュ叿锛氭煡璇㈡寚瀹氬煄甯傜殑澶╂皵淇℃伅
- *
- * @author 鑺嬮亾婧愮爜
- */
-@Component("weather_query")
-public class WeatherQueryToolFunction
- implements Function<WeatherQueryToolFunction.Request, WeatherQueryToolFunction.Response> {
-
- private static final String[] WEATHER_CONDITIONS = { "鏅存湕", "澶氫簯", "闃村ぉ", "灏忛洦", "澶ч洦", "闆烽洦", "灏忛洩", "澶ч洩" };
-
- @Data
- @JsonClassDescription("鏌ヨ鎸囧畾鍩庡競鐨勫ぉ姘斾俊鎭�")
- public static class Request {
-
- /**
- * 鍩庡競鍚嶇О
- */
- @JsonProperty(required = true, value = "city")
- @JsonPropertyDescription("鍩庡競鍚嶇О锛屼緥濡傦細鍖椾含銆佷笂娴枫�佸箍宸�")
- private String city;
-
- }
-
- @Data
- @AllArgsConstructor
- @NoArgsConstructor
- public static class Response {
-
- /**
- * 鍩庡競鍚嶇О
- */
- private String city;
-
- /**
- * 澶╂皵淇℃伅
- */
- private WeatherInfo weatherInfo;
-
- @Data
- @AllArgsConstructor
- @NoArgsConstructor
- public static class WeatherInfo {
-
- /**
- * 娓╁害锛堟憚姘忓害锛�
- */
- private Integer temperature;
-
- /**
- * 澶╂皵鐘跺喌
- */
- private String condition;
-
- /**
- * 婀垮害鐧惧垎姣�
- */
- private Integer humidity;
-
- /**
- * 椋庨�燂紙km/h锛�
- */
- private Integer windSpeed;
-
- /**
- * 鏌ヨ鏃堕棿
- */
- private String queryTime;
-
- }
-
- }
-
- @Override
- public Response apply(Request request) {
- // 妫�鏌ュ煄甯傚悕绉版槸鍚︿负绌�
- if (StrUtil.isBlank(request.getCity())) {
- return new Response("鏈煡鍩庡競", null);
- }
-
- // 鑾峰彇澶╂皵鏁版嵁
- String city = request.getCity();
- Response.WeatherInfo weatherInfo = generateMockWeatherInfo();
- return new Response(city, weatherInfo);
- }
-
- /**
- * 鐢熸垚妯℃嫙鐨勫ぉ姘旀暟鎹�
- * 鍦ㄥ疄闄呭簲鐢ㄤ腑锛屽簲鏇挎崲涓虹湡瀹� API 璋冪敤
- */
- private Response.WeatherInfo generateMockWeatherInfo() {
- int temperature = RandomUtil.randomInt(-5, 30);
- int humidity = RandomUtil.randomInt(1, 100);
- int windSpeed = RandomUtil.randomInt(1, 30);
- String condition = RandomUtil.randomEle(WEATHER_CONDITIONS);
- return new Response.WeatherInfo(temperature, condition, humidity, windSpeed,
- LocalDateTimeUtil.format(LocalDateTime.now(), NORM_DATETIME_PATTERN));
- }
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/function/package-info.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/function/package-info.java
deleted file mode 100644
index 0b59656..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/function/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * 鍙傝�� <a href="https://docs.spring.io/spring-ai/reference/api/tools.html#_methods_as_tools">Tool Calling 鈥斺�� Methods as Tools</a>
- */
-package cn.iocoder.yudao.module.ai.tool.function;
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/method/Person.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/method/Person.java
deleted file mode 100644
index 66bab5a..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/method/Person.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package cn.iocoder.yudao.module.ai.tool.method;
-
-/**
- * 鏉ヨ嚜 Spring AI 瀹樻柟鏂囨。
- *
- * Represents a person with basic information.
- * This is an immutable record.
- */
-public record Person(
- int id,
- String firstName,
- String lastName,
- String email,
- String sex,
- String ipAddress,
- String jobTitle,
- int age
-) {
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/method/PersonService.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/method/PersonService.java
deleted file mode 100644
index 52c8954..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/method/PersonService.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package cn.iocoder.yudao.module.ai.tool.method;
-
-import java.util.List;
-import java.util.Optional;
-
-/**
- * 鏉ヨ嚜 Spring AI 瀹樻柟鏂囨。
- *
- * Service interface for managing Person data.
- * Defines the contract for CRUD operations and search/filter functionalities.
- */
-public interface PersonService {
-
- /**
- * Creates a new Person record.
- * Assigns a unique ID to the person and stores it.
- *
- * @param personData The data for the new person (ID field is ignored). Must not be null.
- * @return The created Person record, including the generated ID.
- */
- Person createPerson(Person personData);
-
- /**
- * Retrieves a Person by their unique ID.
- *
- * @param id The ID of the person to retrieve.
- * @return An Optional containing the found Person, or an empty Optional if not found.
- */
- Optional<Person> getPersonById(int id);
-
- /**
- * Retrieves all Person records currently stored.
- *
- * @return An unmodifiable List containing all Persons. Returns an empty list if none exist.
- */
- List<Person> getAllPersons();
-
- /**
- * Updates an existing Person record identified by ID.
- * Replaces the existing data with the provided data, keeping the original ID.
- *
- * @param id The ID of the person to update.
- * @param updatedPersonData The new data for the person (ID field is ignored). Must not be null.
- * @return true if the person was found and updated, false otherwise.
- */
- boolean updatePerson(int id, Person updatedPersonData);
-
- /**
- * Deletes a Person record identified by ID.
- *
- * @param id The ID of the person to delete.
- * @return true if the person was found and deleted, false otherwise.
- */
- boolean deletePerson(int id);
-
- /**
- * Searches for Persons whose job title contains the given query string (case-insensitive).
- *
- * @param jobTitleQuery The string to search for within job titles. Can be null or blank.
- * @return An unmodifiable List of matching Persons. Returns an empty list if no matches or query is invalid.
- */
- List<Person> searchByJobTitle(String jobTitleQuery);
-
- /**
- * Filters Persons by their exact sex (case-insensitive).
- *
- * @param sex The sex to filter by (e.g., "Male", "Female"). Can be null or blank.
- * @return An unmodifiable List of matching Persons. Returns an empty list if no matches or filter is invalid.
- */
- List<Person> filterBySex(String sex);
-
- /**
- * Filters Persons by their exact age.
- *
- * @param age The age to filter by.
- * @return An unmodifiable List of matching Persons. Returns an empty list if no matches.
- */
- List<Person> filterByAge(int age);
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/method/PersonServiceImpl.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/method/PersonServiceImpl.java
deleted file mode 100644
index 3b8c31b..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/method/PersonServiceImpl.java
+++ /dev/null
@@ -1,336 +0,0 @@
-package cn.iocoder.yudao.module.ai.tool.method;
-
-import jakarta.annotation.PostConstruct;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.ai.tool.annotation.Tool;
-import org.springframework.stereotype.Service;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-/**
- * 鏉ヨ嚜 Spring AI 瀹樻柟鏂囨。
- *
- * Implementation of the PersonService interface using an in-memory data store.
- * Manages a collection of Person objects loaded from embedded CSV data.
- * This class is thread-safe due to the use of ConcurrentHashMap and AtomicInteger.
- */
-@Service
-@Slf4j
-public class PersonServiceImpl implements PersonService {
-
- private final Map<Integer, Person> personStore = new ConcurrentHashMap<>();
-
- private AtomicInteger idGenerator;
-
- /**
- * Embedded CSV data for initial population
- */
- private static final String CSV_DATA = """
- Id,FirstName,LastName,Email,Sex,IpAddress,JobTitle,Age
- 1,Fons,Tollfree,ftollfree0@senate.gov,Male,55.1 Tollfree Lane,Research Associate,31
- 2,Emlynne,Tabourier,etabourier1@networksolutions.com,Female,18 Tabourier Way,Associate Professor,38
- 3,Shae,Johncey,sjohncey2@yellowpages.com,Male,1 Johncey Circle,Structural Analysis Engineer,30
- 4,Sebastien,Bradly,sbradly3@mapquest.com,Male,2 Bradly Hill,Chief Executive Officer,40
- 5,Harriott,Kitteringham,hkitteringham4@typepad.com,Female,3 Kitteringham Drive,VP Sales,47
- 6,Anallise,Parradine,aparradine5@miibeian.gov.cn,Female,4 Parradine Street,Analog Circuit Design manager,44
- 7,Gorden,Kirkbright,gkirkbright6@reuters.com,Male,5 Kirkbright Plaza,Senior Editor,40
- 8,Veradis,Ledwitch,vledwitch7@google.com.au,Female,6 Ledwitch Avenue,Computer Systems Analyst IV,44
- 9,Agnesse,Penhalurick,apenhalurick8@google.it,Female,7 Penhalurick Terrace,Automation Specialist IV,41
- 10,Bibby,Hutable,bhutable9@craigslist.org,Female,8 Hutable Place,Account Representative I,43
- 11,Karoly,Lightoller,klightollera@rakuten.co.jp,Female,9 Lightoller Parkway,Senior Developer,46
- 12,Cristine,Durrad,cdurradb@aol.com,Female,10 Durrad Center,Senior Developer,48
- 13,Aggy,Napier,anapierc@hostgator.com,Female,11 Napier Court,VP Product Management,44
- 14,Prisca,Caddens,pcaddensd@vinaora.com,Female,12 Caddens Alley,Business Systems Development Analyst,41
- 15,Khalil,McKernan,kmckernane@google.fr,Male,13 McKernan Pass,Engineer IV,44
- 16,Lorry,MacTrusty,lmactrustyf@eventbrite.com,Male,14 MacTrusty Junction,Design Engineer,42
- 17,Casandra,Worsell,cworsellg@goo.gl,Female,15 Worsell Point,Systems Administrator IV,45
- 18,Ulrikaumeko,Haveline,uhavelineh@usgs.gov,Female,16 Haveline Trail,Financial Advisor,42
- 19,Shurlocke,Albany,salbanyi@artisteer.com,Male,17 Albany Plaza,Software Test Engineer III,46
- 20,Myrilla,Brimilcombe,mbrimilcombej@accuweather.com,Female,18 Brimilcombe Road,Programmer Analyst I,48
- 21,Carlina,Scimonelli,cscimonellik@va.gov,Female,19 Scimonelli Pass,Help Desk Technician,45
- 22,Tina,Goullee,tgoulleel@miibeian.gov.cn,Female,20 Goullee Crossing,Accountant IV,43
- 23,Adriaens,Storek,astorekm@devhub.com,Female,21 Storek Avenue,Recruiting Manager,40
- 24,Tedra,Giraudot,tgiraudotn@wiley.com,Female,22 Giraudot Terrace,Speech Pathologist,47
- 25,Josiah,Soares,jsoareso@google.nl,Male,23 Soares Street,Tax Accountant,45
- 26,Kayle,Gaukrodge,kgaukrodgep@wikispaces.com,Female,24 Gaukrodge Parkway,Accountant II,43
- 27,Ardys,Chuter,achuterq@ustream.tv,Female,25 Chuter Drive,Engineer IV,41
- 28,Francyne,Baudinet,fbaudinetr@newyorker.com,Female,26 Baudinet Center,VP Accounting,48
- 29,Gerick,Bullan,gbullans@seesaa.net,Male,27 Bullan Way,Senior Financial Analyst,43
- 30,Northrup,Grivori,ngrivorit@unc.edu,Male,28 Grivori Plaza,Systems Administrator I,45
- 31,Town,Duguid,tduguidu@squarespace.com,Male,29 Duguid Pass,Safety Technician IV,46
- 32,Pierette,Kopisch,pkopischv@google.com.br,Female,30 Kopisch Lane,Director of Sales,41
- 33,Jacquenetta,Le Prevost,jleprevostw@netlog.com,Female,31 Le Prevost Trail,Senior Developer,47
- 34,Garvy,Rusted,grustedx@aboutads.info,Male,32 Rusted Junction,Senior Developer,42
- 35,Clarice,Aysh,cayshy@merriam-webster.com,Female,33 Aysh Avenue,VP Quality Control,40
- 36,Tracie,Fedorski,tfedorskiz@bloglines.com,Male,34 Fedorski Terrace,Design Engineer,44
- 37,Noelyn,Matushenko,nmatushenko10@globo.com,Female,35 Matushenko Place,VP Sales,48
- 38,Rudiger,Klaesson,rklaesson11@usnews.com,Male,36 Klaesson Road,Database Administrator IV,43
- 39,Mirella,Syddie,msyddie12@geocities.jp,Female,37 Syddie Circle,Geological Engineer,46
- 40,Donalt,O'Lunny,dolunny13@elpais.com,Male,38 O'Lunny Center,Analog Circuit Design manager,41
- 41,Guntar,Deniskevich,gdeniskevich14@google.com.hk,Male,39 Deniskevich Way,Structural Engineer,47
- 42,Hort,Shufflebotham,hshufflebotham15@about.me,Male,40 Shufflebotham Court,Structural Analysis Engineer,45
- 43,Dominique,Thickett,dthickett16@slashdot.org,Male,41 Thickett Crossing,Safety Technician I,42
- 44,Zebulen,Piscopello,zpiscopello17@umich.edu,Male,42 Piscopello Parkway,Web Developer II,40
- 45,Mellicent,Mac Giany,mmacgiany18@state.tx.us,Female,43 Mac Giany Pass,Assistant Manager,44
- 46,Merle,Bounds,mbounds19@amazon.co.jp,Female,44 Bounds Alley,Systems Administrator III,41
- 47,Madelle,Farbrace,mfarbrace1a@xinhuanet.com,Female,45 Farbrace Terrace,Quality Engineer,48
- 48,Galvin,O'Sheeryne,gosheeryne1b@addtoany.com,Male,46 O'Sheeryne Way,Environmental Specialist,43
- 49,Guillemette,Bootherstone,gbootherstone1c@nationalgeographic.com,Female,47 Bootherstone Plaza,Professor,46
- 50,Letti,Aylmore,laylmore1d@vinaora.com,Female,48 Aylmore Circle,Automation Specialist I,40
- 51,Nonie,Rivalland,nrivalland1e@weather.com,Female,49 Rivalland Avenue,Software Test Engineer IV,45
- 52,Jacquelynn,Halfacre,jhalfacre1f@surveymonkey.com,Female,50 Halfacre Pass,Geologist II,42
- 53,Anderea,MacKibbon,amackibbon1g@weibo.com,Female,51 MacKibbon Parkway,Automation Specialist II,47
- 54,Wash,Klimko,wklimko1h@slashdot.org,Male,52 Klimko Alley,Database Administrator I,40
- 55,Flori,Kynett,fkynett1i@auda.org.au,Female,53 Kynett Trail,Quality Control Specialist,46
- 56,Libbey,Penswick,lpenswick1j@google.co.uk,Female,54 Penswick Point,VP Accounting,43
- 57,Silvanus,Skellorne,sskellorne1k@booking.com,Male,55 Skellorne Drive,Account Executive,48
- 58,Carmine,Mateos,cmateos1l@plala.or.jp,Male,56 Mateos Terrace,Systems Administrator I,41
- 59,Sheffie,Blazewicz,sblazewicz1m@google.com.au,Male,57 Blazewicz Center,VP Sales,44
- 60,Leanor,Worsnop,lworsnop1n@uol.com.br,Female,58 Worsnop Plaza,Systems Administrator III,45
- 61,Caspar,Pamment,cpamment1o@google.co.jp,Male,59 Pamment Court,Senior Financial Analyst,42
- 62,Justinian,Pentycost,jpentycost1p@sciencedaily.com,Male,60 Pentycost Way,Senior Quality Engineer,47
- 63,Gerianne,Jarnell,gjarnell1q@bing.com,Female,61 Jarnell Avenue,Help Desk Operator,40
- 64,Boycie,Zanetto,bzanetto1r@about.com,Male,62 Zanetto Place,Quality Engineer,46
- 65,Camilla,Mac Giany,cmacgiany1s@state.gov,Female,63 Mac Giany Parkway,Senior Cost Accountant,43
- 66,Hadlee,Piscopiello,hpiscopiello1t@artisteer.com,Male,64 Piscopiello Street,Account Representative III,48
- 67,Bobbie,Penvarden,bpenvarden1u@google.cn,Male,65 Penvarden Lane,Help Desk Operator,41
- 68,Ali,Gowlett,agowlett1v@parallels.com,Male,66 Gowlett Pass,VP Marketing,44
- 69,Olivette,Acome,oacome1w@qq.com,Female,67 Acome Hill,VP Product Management,45
- 70,Jehanna,Brotherheed,jbrotherheed1x@google.nl,Female,68 Brotherheed Junction,Database Administrator III,42
- 71,Morgan,Berthomieu,mberthomieu1y@artisteer.com,Male,69 Berthomieu Alley,Systems Administrator II,47
- 72,Linzy,Shilladay,lshilladay1z@icq.com,Female,70 Shilladay Trail,Research Assistant IV,40
- 73,Faydra,Brimner,fbrimner20@mozilla.org,Female,71 Brimner Road,Senior Editor,46
- 74,Gwenore,Oxlee,goxlee21@devhub.com,Female,72 Oxlee Terrace,Systems Administrator II,43
- 75,Evangelin,Beinke,ebeinke22@mozilla.com,Female,73 Beinke Circle,Accountant I,48
- 76,Missy,Cockling,mcockling23@si.edu,Female,74 Cockling Way,Software Engineer I,41
- 77,Suzanne,Klimschak,sklimschak24@etsy.com,Female,75 Klimschak Plaza,Tax Accountant,44
- 78,Candide,Goricke,cgoricke25@weebly.com,Female,76 Goricke Pass,Sales Associate,45
- 79,Gerome,Pinsent,gpinsent26@google.com.au,Male,77 Pinsent Junction,Software Consultant,42
- 80,Lezley,Mac Giany,lmacgiany27@scribd.com,Male,78 Mac Giany Alley,Operator,47
- 81,Tobiah,Durn,tdurn28@state.tx.us,Male,79 Durn Court,VP Sales,40
- 82,Sherlocke,Cockshoot,scockshoot29@yelp.com,Male,80 Cockshoot Street,Senior Financial Analyst,46
- 83,Myrle,Speenden,mspeenden2a@utexas.edu,Female,81 Speenden Center,Senior Developer,43
- 84,Isidore,Gorries,igorries2b@flavors.me,Male,82 Gorries Parkway,Sales Representative,48
- 85,Isac,Kitchingman,ikitchingman2c@businessinsider.com,Male,83 Kitchingman Drive,VP Accounting,41
- 86,Benedetta,Purrier,bpurrier2d@admin.ch,Female,84 Purrier Trail,VP Accounting,44
- 87,Tera,Fitchell,tfitchell2e@fotki.com,Female,85 Fitchell Place,Software Engineer IV,45
- 88,Abbe,Pamment,apamment2f@about.com,Male,86 Pamment Avenue,VP Sales,42
- 89,Jandy,Gommowe,jgommowe2g@angelfire.com,Female,87 Gommowe Road,Financial Analyst,47
- 90,Karena,Fussey,kfussey2h@google.com.au,Female,88 Fussey Point,Assistant Professor,40
- 91,Gaspar,Pammenter,gpammenter2i@google.com.br,Male,89 Pammenter Hill,Help Desk Operator,46
- 92,Stanwood,Mac Giany,smacgiany2j@prlog.org,Male,90 Mac Giany Terrace,Research Associate,43
- 93,Byrom,Beedell,bbeedell2k@google.co.jp,Male,91 Beedell Way,VP Sales,48
- 94,Annabella,Rowbottom,arowbottom2l@google.com.au,Female,92 Rowbottom Plaza,Help Desk Operator,41
- 95,Rodolphe,Debell,rdebell2m@imageshack.us,Male,93 Debell Pass,Design Engineer,44
- 96,Tyne,Gommey,tgommey2n@joomla.org,Female,94 Gommey Junction,VP Marketing,45
- 97,Christoper,Pincked,cpincked2o@icq.com,Male,95 Pincked Alley,Human Resources Manager,42
- 98,Kore,Le Prevost,kleprevost2p@tripadvisor.com,Female,96 Le Prevost Street,VP Quality Control,47
- 99,Ceciley,Petrolli,cpetrolli2q@oaic.gov.au,Female,97 Petrolli Court,Senior Developer,40
- 100,Elspeth,Mac Giany,emacgiany2r@icio.us,Female,98 Mac Giany Parkway,Internal Auditor,46
- """;
-
- /**
- * Initializes the service after dependency injection by loading data from the CSV string.
- * Uses @PostConstruct to ensure this runs after the bean is created.
- */
- @PostConstruct
- private void initializeData() {
- log.info("Initializing PersonService data store...");
- int maxId = loadDataFromCsv();
- idGenerator = new AtomicInteger(maxId);
- log.info("PersonService initialized with {} records. Next ID: {}", personStore.size(), idGenerator.get() + 1);
- }
-
- /**
- * Parses the embedded CSV data and populates the in-memory store.
- * Calculates the maximum ID found in the data to initialize the ID generator.
- *
- * @return The maximum ID found in the loaded CSV data.
- */
- private int loadDataFromCsv() {
- final AtomicInteger currentMaxId = new AtomicInteger(0);
- // Clear existing data before loading (important for tests or re-initialization scenarios)
- personStore.clear();
- try (Stream<String> lines = CSV_DATA.lines().skip(1)) { // Skip header row
- lines.forEach(line -> {
- try {
- // Split carefully, handling potential commas within quoted fields if necessary (simple split here)
- String[] fields = line.split(",", 8); // Limit split to handle potential commas in job title
- if (fields.length == 8) {
- int id = Integer.parseInt(fields[0].trim());
- String firstName = fields[1].trim();
- String lastName = fields[2].trim();
- String email = fields[3].trim();
- String sex = fields[4].trim();
- String ipAddress = fields[5].trim();
- String jobTitle = fields[6].trim();
- int age = Integer.parseInt(fields[7].trim());
-
- Person person = new Person(id, firstName, lastName, email, sex, ipAddress, jobTitle, age);
- personStore.put(id, person);
- currentMaxId.updateAndGet(max -> Math.max(max, id));
- } else {
- log.warn("Skipping malformed CSV line (expected 8 fields, found {}): {}", fields.length, line);
- }
- } catch (NumberFormatException e) {
- log.warn("Skipping line due to parsing error (ID or Age): {} - Error: {}", line, e.getMessage());
- } catch (Exception e) {
- log.error("Skipping line due to unexpected error: {} - Error: {}", line, e.getMessage(), e);
- }
- });
- } catch (Exception e) {
- log.error("Fatal error reading embedded CSV data: {}", e.getMessage(), e);
- // In a real application, might throw a specific initialization exception
- }
- return currentMaxId.get();
- }
-
- @Override
- @Tool(
- name = "ps_create_person",
- description = "Create a new person record in the in-memory store."
- )
- public Person createPerson(Person personData) {
- if (personData == null) {
- throw new IllegalArgumentException("Person data cannot be null");
- }
- int newId = idGenerator.incrementAndGet();
- // Create a new Person record using data from the input, but with the generated ID
- Person newPerson = new Person(
- newId,
- personData.firstName(),
- personData.lastName(),
- personData.email(),
- personData.sex(),
- personData.ipAddress(),
- personData.jobTitle(),
- personData.age()
- );
- personStore.put(newId, newPerson);
- log.debug("Created person: {}", newPerson);
- return newPerson;
- }
-
- @Override
- @Tool(
- name = "ps_get_person_by_id",
- description = "Retrieve a person record by ID from the in-memory store."
- )
- public Optional<Person> getPersonById(int id) {
- Person person = personStore.get(id);
- log.debug("Retrieved person by ID {}: {}", id, person);
- return Optional.ofNullable(person);
- }
-
- @Override
- @Tool(
- name = "ps_get_all_persons",
- description = "Retrieve all person records from the in-memory store."
- )
- public List<Person> getAllPersons() {
- // Return an unmodifiable view of the values
- List<Person> allPersons = personStore.values().stream().toList();
- log.debug("Retrieved all persons (count: {})", allPersons.size());
- return allPersons;
- }
-
- @Override
- @Tool(
- name = "ps_update_person",
- description = "Update an existing person record by ID in the in-memory store."
- )
- public boolean updatePerson(int id, Person updatedPersonData) {
- if (updatedPersonData == null) {
- throw new IllegalArgumentException("Updated person data cannot be null");
- }
- // Use computeIfPresent for atomic update if the key exists
- Person result = personStore.computeIfPresent(id, (key, existingPerson) ->
- // Create a new Person record with the original ID but updated data
- new Person(
- id, // Keep original ID
- updatedPersonData.firstName(),
- updatedPersonData.lastName(),
- updatedPersonData.email(),
- updatedPersonData.sex(),
- updatedPersonData.ipAddress(),
- updatedPersonData.jobTitle(),
- updatedPersonData.age()
- )
- );
- boolean updated = result != null;
- log.debug("Update attempt for ID {}: {}", id, updated ? "Successful" : "Failed (Not Found)");
- if(updated) log.trace("Updated person data for ID {}: {}", id, result);
- return updated;
- }
-
- @Override
- @Tool(
- name = "ps_delete_person",
- description = "Delete a person record by ID from the in-memory store."
- )
- public boolean deletePerson(int id) {
- boolean removed = personStore.remove(id) != null;
- log.debug("Delete attempt for ID {}: {}", id, removed ? "Successful" : "Failed (Not Found)");
- return removed;
- }
-
- @Override
- @Tool(
- name = "ps_search_by_job_title",
- description = "Search for persons by job title in the in-memory store."
- )
- public List<Person> searchByJobTitle(String jobTitleQuery) {
- if (jobTitleQuery == null || jobTitleQuery.isBlank()) {
- log.debug("Search by job title skipped due to blank query.");
- return Collections.emptyList();
- }
- String lowerCaseQuery = jobTitleQuery.toLowerCase();
- List<Person> results = personStore.values().stream()
- .filter(person -> person.jobTitle() != null && person.jobTitle().toLowerCase().contains(lowerCaseQuery))
- .collect(Collectors.toList());
- log.debug("Search by job title '{}' found {} results.", jobTitleQuery, results.size());
- return Collections.unmodifiableList(results);
- }
-
- @Override
- @Tool(
- name = "ps_filter_by_sex",
- description = "Filters Persons by sex (case-insensitive)."
- )
- public List<Person> filterBySex(String sex) {
- if (sex == null || sex.isBlank()) {
- log.debug("Filter by sex skipped due to blank filter.");
- return Collections.emptyList();
- }
- List<Person> results = personStore.values().stream()
- .filter(person -> person.sex() != null && person.sex().equalsIgnoreCase(sex))
- .collect(Collectors.toList());
- log.debug("Filter by sex '{}' found {} results.", sex, results.size());
- return Collections.unmodifiableList(results);
- }
-
- @Override
- @Tool(
- name = "ps_filter_by_age",
- description = "Filters Persons by age."
- )
- public List<Person> filterByAge(int age) {
- if (age < 0) {
- log.debug("Filter by age skipped due to negative age: {}", age);
- return Collections.emptyList(); // Or throw IllegalArgumentException based on requirements
- }
- List<Person> results = personStore.values().stream()
- .filter(person -> person.age() == age)
- .collect(Collectors.toList());
- log.debug("Filter by age {} found {} results.", age, results.size());
- return Collections.unmodifiableList(results);
- }
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/method/package-info.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/method/package-info.java
deleted file mode 100644
index 44b53e1..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/tool/method/package-info.java
+++ /dev/null
@@ -1,4 +0,0 @@
-/**
- * 鍙傝�� <a href="https://docs.spring.io/spring-ai/reference/api/tools.html#_methods_as_tools">Tool Calling 鈥斺�� Methods as Tools</a>
- */
-package cn.iocoder.yudao.module.ai.tool.method;
\ No newline at end of file
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java
deleted file mode 100644
index d687c54..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java
+++ /dev/null
@@ -1,189 +0,0 @@
-package cn.iocoder.yudao.module.ai.util;
-
-import cn.hutool.core.map.MapUtil;
-import cn.hutool.core.util.ObjUtil;
-import cn.hutool.core.util.StrUtil;
-import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
-import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
-import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
-import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
-import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
-import cn.hutool.extra.spring.SpringUtil;
-import org.springframework.ai.anthropic.AnthropicChatOptions;
-import org.springframework.ai.chat.messages.*;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.ChatOptions;
-import org.springframework.ai.deepseek.DeepSeekAssistantMessage;
-import org.springframework.ai.deepseek.DeepSeekChatOptions;
-import org.springframework.ai.google.genai.GoogleGenAiChatOptions;
-import org.springframework.ai.ollama.api.OllamaChatOptions;
-import org.springframework.ai.openai.OpenAiChatOptions;
-import org.springframework.ai.tool.ToolCallback;
-import org.springframework.core.env.Environment;
-
-import java.util.*;
-
-import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.API_CONFIG_PLACEHOLDER_NOT_RESOLVED;
-
-/**
- * Spring AI 宸ュ叿绫�
- *
- * @author 鑺嬮亾婧愮爜
- */
-public class AiUtils {
-
- public static final String TOOL_CONTEXT_LOGIN_USER = "LOGIN_USER";
- public static final String TOOL_CONTEXT_TENANT_ID = "TENANT_ID";
-
- /**
- * 瑙f瀽 DB 绛夊姩鎬侀厤缃噷鐨� Spring 鍗犱綅绗︼紝渚嬪 ${OPENAI_API_KEY}
- *
- * @param value 寰呰В鏋愮殑閰嶇疆鍊�
- * @return 瑙f瀽鍚庣殑閰嶇疆鍊�
- */
- public static String resolveSpringPlaceholders(String value) {
- if (StrUtil.isBlank(value) || !StrUtil.contains(value, "${")) {
- return value;
- }
- try {
- return SpringUtil.getBean(Environment.class).resolveRequiredPlaceholders(value);
- } catch (IllegalArgumentException ex) {
- throw exception(API_CONFIG_PLACEHOLDER_NOT_RESOLVED, value);
- }
- }
-
- /**
- * 鏍¢獙 API Key锛岄伩鍏嶉泦鎴愭祴璇曚娇鐢ㄩ粯璁ゅ崰浣嶅�煎彂璧疯皟鐢ㄣ��
- *
- * @param apiKey API Key
- */
- public static void validateApiKey(String apiKey) {
- if (StrUtil.isBlank(apiKey) || "sk-xxxx".equals(apiKey)) {
- throw new IllegalStateException("apiKey 涓嶈兘涓虹┖");
- }
- }
-
- /**
- * 閫氫箟鍗冮棶鏀寔澶氭ā鎬佺殑妯″瀷
- *
- * @see <a href="https://bailian.console.aliyun.com/cn-beijing/?tab=model#/model-market/all?providers=qwen&capabilities=VU">妯″瀷骞垮満</a>
- * @see <a href="https://help.aliyun.com/zh/model-studio/error-code#error-url">蹇呴』寮�鍚� withMultiModel 鍙傛暟</a>
- */
- public static final Set<String> TONG_YI_MULTI_MODELS = SetUtils.asSet(
- // qwen3.5 / 3.6 / 3.7 绯诲垪锛堢粺涓�澶氭ā鎬佷富骞诧級
- "qwen3.7-max", "qwen3.7-plus", "qwen3.7-flash",
- "qwen3.6-plus", "qwen3.6-flash",
- "qwen3.5-plus", "qwen3.5-flash",
- // qwen-vl 瑙嗚鐞嗚В
- "qwen3-vl-plus", "qwen3-vl-flash",
- "qwen-vl-max", "qwen-vl-plus",
- "qwen2.5-vl-72b-instruct", "qwen2.5-vl-32b-instruct",
- "qwen2.5-vl-7b-instruct", "qwen2.5-vl-3b-instruct",
- // qvq 瑙嗚鎺ㄧ悊
- "qvq-max", "qvq-plus",
- // qwen-omni 鍏ㄦā鎬�
- "qwen3.5-omni-plus", "qwen3.5-omni-flash",
- "qwen3-omni-flash", "qwen-omni-turbo"
- );
-
- public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens) {
- return buildChatOptions(platform, model, temperature, maxTokens, null, null);
- }
-
- public static ChatOptions buildChatOptions(AiPlatformEnum platform, String model, Double temperature, Integer maxTokens,
- List<ToolCallback> toolCallbacks, Map<String, Object> toolContext) {
- toolCallbacks = ObjUtil.defaultIfNull(toolCallbacks, Collections.emptyList());
- toolContext = ObjUtil.defaultIfNull(toolContext, Collections.emptyMap());
- // noinspection EnhancedSwitchMigration
- switch (platform) {
- case TONG_YI:
- return DashScopeChatOptions.builder().model(model).temperature(temperature).maxToken(maxTokens)
- .enableThinking(true) // TODO 鑺嬭壙锛氶粯璁ら兘寮�鍚� thinking 妯″紡锛屽悗缁彲浠ヨ鐢ㄦ埛閰嶇疆
- .multiModel(TONG_YI_MULTI_MODELS.contains(model)) // 鏄惁澶氭ā鎬佹ā鍨�
- .toolCallbacks(toolCallbacks).toolContext(toolContext).build();
- case DEEP_SEEK:
- case DOU_BAO: // 澶嶇敤 DeepSeek 瀹㈡埛绔�
- case HUN_YUAN: // 澶嶇敤 DeepSeek 瀹㈡埛绔�
- case SILICON_FLOW: // 澶嶇敤 DeepSeek 瀹㈡埛绔�
- case YI_YAN: // 澶嶇敤 DeepSeek 瀹㈡埛绔�
- case ZHI_PU: // 澶嶇敤 DeepSeek 瀹㈡埛绔�
- case XING_HUO: // 澶嶇敤 DeepSeek 瀹㈡埛绔�
- case MINI_MAX: // 澶嶇敤 DeepSeek 瀹㈡埛绔�
- case MOONSHOT: // 澶嶇敤 DeepSeek 瀹㈡埛绔�
- case BAI_CHUAN: // 澶嶇敤 DeepSeek 瀹㈡埛绔�
- case STEP_FUN: // 澶嶇敤 DeepSeek 瀹㈡埛绔�
- return DeepSeekChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
- .toolCallbacks(toolCallbacks).toolContext(toolContext).build();
- case OPENAI:
- case GROK: // 澶嶇敤 OpenAI 瀹㈡埛绔�
- return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
- .toolCallbacks(toolCallbacks).toolContext(toolContext).build();
- case GEMINI:
- return GoogleGenAiChatOptions.builder().model(model).temperature(temperature).maxOutputTokens(maxTokens)
- .toolCallbacks(toolCallbacks).toolContext(toolContext).build();
- case AZURE_OPENAI:
- return OpenAiChatOptions.builder().model(model).deploymentName(model).azure(true)
- .temperature(temperature).maxTokens(maxTokens)
- .toolCallbacks(toolCallbacks).toolContext(toolContext).build();
- case ANTHROPIC:
- return AnthropicChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
- .toolCallbacks(toolCallbacks).toolContext(toolContext).build();
- case OLLAMA:
- return OllamaChatOptions.builder().model(model).temperature(temperature).numPredict(maxTokens)
- .toolCallbacks(toolCallbacks).toolContext(toolContext).build();
- default:
- throw new IllegalArgumentException(StrUtil.format("鏈煡骞冲彴({})", platform));
- }
- }
-
- public static Message buildMessage(String type, String content) {
- if (MessageType.USER.getValue().equals(type)) {
- return new UserMessage(content);
- }
- if (MessageType.ASSISTANT.getValue().equals(type)) {
- return new AssistantMessage(content);
- }
- if (MessageType.SYSTEM.getValue().equals(type)) {
- return new SystemMessage(content);
- }
- if (MessageType.TOOL.getValue().equals(type)) {
- throw new UnsupportedOperationException("鏆備笉鏀寔 tool 娑堟伅锛�" + content);
- }
- throw new IllegalArgumentException(StrUtil.format("鏈煡娑堟伅绫诲瀷({})", type));
- }
-
- public static Map<String, Object> buildCommonToolContext() {
- Map<String, Object> context = new HashMap<>();
- context.put(TOOL_CONTEXT_LOGIN_USER, SecurityFrameworkUtils.getLoginUser());
- context.put(TOOL_CONTEXT_TENANT_ID, TenantContextHolder.getTenantId());
- return context;
- }
-
- @SuppressWarnings("ConstantValue")
- public static String getChatResponseContent(ChatResponse response) {
- if (response == null
- || response.getResult() == null
- || response.getResult().getOutput() == null) {
- return null;
- }
- return response.getResult().getOutput().getText();
- }
-
- @SuppressWarnings("ConstantValue")
- public static String getChatResponseReasoningContent(ChatResponse response) {
- if (response == null
- || response.getResult() == null
- || response.getResult().getOutput() == null) {
- return null;
- }
- AssistantMessage output = response.getResult().getOutput();
- // DeepSeek 閫氳繃涓撳睘 AssistantMessage 鏆撮湶 reasoningContent
- if (output instanceof DeepSeekAssistantMessage) {
- return ((DeepSeekAssistantMessage) output).getReasoningContent();
- }
- // 閫氫箟鍗冮棶绛夐�氳繃 metadata 閫忎紶 reasoningContent
- return MapUtil.getStr(output.getMetadata(), "reasoningContent");
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/util/FileTypeUtils.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/util/FileTypeUtils.java
deleted file mode 100644
index 9c3b202..0000000
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/util/FileTypeUtils.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package cn.iocoder.yudao.module.ai.util;
-
-import cn.hutool.core.util.StrUtil;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.tika.Tika;
-
-/**
- * 鏂囦欢绫诲瀷 Utils
- *
- * @author 鑺嬮亾婧愮爜
- */
-@Slf4j
-public class FileTypeUtils {
-
- private static final Tika TIKA = new Tika();
-
- /**
- * 宸茬煡鏂囦欢鍚嶏紝鑾峰彇鏂囦欢绫诲瀷锛屽湪鏌愪簺鎯呭喌涓嬫瘮閫氳繃瀛楄妭鏁扮粍鍑嗙‘锛屼緥濡備娇鐢� jar 鏂囦欢鏃讹紝閫氳繃鍚嶅瓧鏇翠负鍑嗙‘
- *
- * @param name 鏂囦欢鍚�
- * @return mineType 鏃犳硶璇嗗埆鏃朵細杩斿洖鈥渁pplication/octet-stream鈥�
- */
- public static String getMineType(String name) {
- return TIKA.detect(name);
- }
-
- /**
- * 鍒ゆ柇鏄惁鏄浘鐗�
- *
- * @param mineType 绫诲瀷
- * @return 鏄惁鏄浘鐗�
- */
- public static boolean isImage(String mineType) {
- return StrUtil.startWith(mineType, "image/");
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/org/springframework/ai/model/tool/DefaultToolExecutionEligibilityPredicate.java b/yudao-module-ai/src/main/java/org/springframework/ai/model/tool/DefaultToolExecutionEligibilityPredicate.java
deleted file mode 100644
index 41f5d06..0000000
--- a/yudao-module-ai/src/main/java/org/springframework/ai/model/tool/DefaultToolExecutionEligibilityPredicate.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package org.springframework.ai.model.tool;
-
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.ChatOptions;
-
-import java.lang.reflect.Method;
-
-/**
- * TODO 鑺嬭壙锛歴pring-ai-alibaba 2.0.0-M1.1 浠嶄緷璧栨棫鐨� Spring AI ToolExecutionEligibilityPredicate锛�
- * 涓存椂琛ラ綈锛涘崌绾у埌鍏煎 Spring AI 2.0.0 鐨� spring-ai-alibaba 鐗堟湰鍚庯紝鍒犻櫎鏈寘涓嬬殑 shim銆�
- */
-public class DefaultToolExecutionEligibilityPredicate implements ToolExecutionEligibilityPredicate {
-
- @Override
- public boolean test(ChatOptions promptOptions, ChatResponse chatResponse) {
- return isInternalToolExecutionEnabled(promptOptions) && chatResponse != null && chatResponse.hasToolCalls();
- }
-
- private static boolean isInternalToolExecutionEnabled(ChatOptions promptOptions) {
- try {
- Method method = promptOptions.getClass().getMethod("getInternalToolExecutionEnabled");
- Object result = method.invoke(promptOptions);
- return result == null || Boolean.TRUE.equals(result);
- } catch (ReflectiveOperationException | SecurityException ignored) {
- return true;
- }
- }
-
-}
diff --git a/yudao-module-ai/src/main/java/org/springframework/ai/model/tool/ToolExecutionEligibilityPredicate.java b/yudao-module-ai/src/main/java/org/springframework/ai/model/tool/ToolExecutionEligibilityPredicate.java
deleted file mode 100644
index c267810..0000000
--- a/yudao-module-ai/src/main/java/org/springframework/ai/model/tool/ToolExecutionEligibilityPredicate.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package org.springframework.ai.model.tool;
-
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.ChatOptions;
-import org.springframework.util.Assert;
-
-import java.util.function.BiPredicate;
-
-/**
- * TODO 鑺嬭壙锛歴pring-ai-alibaba 2.0.0-M1.1 浠嶄緷璧栨棫鐨� Spring AI ToolExecutionEligibilityPredicate锛�
- * 涓存椂琛ラ綈锛涘崌绾у埌鍏煎 Spring AI 2.0.0 鐨� spring-ai-alibaba 鐗堟湰鍚庯紝鍒犻櫎鏈寘涓嬬殑 shim銆�
- */
-public interface ToolExecutionEligibilityPredicate extends BiPredicate<ChatOptions, ChatResponse> {
-
- default boolean isToolExecutionRequired(ChatOptions promptOptions, ChatResponse chatResponse) {
- Assert.notNull(promptOptions, "promptOptions cannot be null");
- Assert.notNull(chatResponse, "chatResponse cannot be null");
- return test(promptOptions, chatResponse);
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AnthropicChatModelTest.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AnthropicChatModelTest.java
deleted file mode 100644
index 0bf3a9a..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AnthropicChatModelTest.java
+++ /dev/null
@@ -1,103 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
-
-import cn.hutool.system.SystemUtil;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.anthropic.AnthropicChatModel;
-import org.springframework.ai.anthropic.AnthropicChatOptions;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.SystemMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import reactor.core.publisher.Flux;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
-
-/**
- * {@link AnthropicChatModel} 闆嗘垚娴嬭瘯绫�
- *
- * @author 鑺嬮亾婧愮爜
- */
-public class AnthropicChatModelTest {
-
- private static final String BASE_URL = SystemUtil.get("ANTHROPIC_BASE_URL",
- "https://api.teamorouter.com");
- private static final String API_KEY = SystemUtil.get("ANTHROPIC_API_KEY",
- "sk-xxxx"); // 鎸夐渶鏀规垚浣犵殑 Anthropic API Key
- private static final String MODEL = SystemUtil.get("ANTHROPIC_MODEL",
- "claude-sonnet-4-6");
-
- private final AnthropicChatModel chatModel = AnthropicChatModel.builder()
- .options(AnthropicChatOptions.builder()
- .baseUrl(BASE_URL)
- .apiKey(API_KEY)
- .model(MODEL)
- .temperature(0.7)
- .maxTokens(4096)
- .build())
- .build();
-
- @Test
- @Disabled
- public void testCall() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- ChatResponse response = chatModel.call(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }
-
- @Test
- @Disabled
- public void testStream() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }).then().block();
- }
-
- @Test
- @Disabled
- public void testStream_thinking() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new UserMessage("thkinking 涓嬶紝1+1 涓轰粈涔堢瓑浜� 2 "));
- AnthropicChatOptions options = AnthropicChatOptions.builder()
- .baseUrl(BASE_URL)
- .apiKey(API_KEY)
- .model(MODEL)
- .thinkingEnabled(1024) // https://platform.claude.com/docs/en/build-with-claude/extended-thinking
- .maxTokens(4096)
- .build();
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }).then().block();
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AzureOpenAIChatModelTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AzureOpenAIChatModelTests.java
deleted file mode 100644
index 8c5a1b8..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AzureOpenAIChatModelTests.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
-
-import cn.hutool.system.SystemUtil;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.SystemMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.openai.OpenAiChatModel;
-import org.springframework.ai.openai.OpenAiChatOptions;
-import reactor.core.publisher.Flux;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
-
-/**
- * Azure OpenAI 闆嗘垚娴嬭瘯
- *
- * @author 鑺嬮亾婧愮爜
- */
-public class AzureOpenAIChatModelTests {
-
- private static final String BASE_URL = SystemUtil.get("AZURE_OPENAI_BASE_URL",
- "https://xxx.openai.azure.com");
- private static final String API_KEY = SystemUtil.get("AZURE_OPENAI_API_KEY",
- "sk-xxxx"); // 鎸夐渶鏀规垚浣犵殑 Azure OpenAI API Key
- private static final String DEPLOYMENT_NAME = SystemUtil.get("AZURE_OPENAI_DEPLOYMENT_NAME",
- "gpt-5.4"); // Azure 涓婂垱寤虹殑妯″瀷閮ㄧ讲鍚嶇О
-
- private final OpenAiChatModel chatModel = OpenAiChatModel.builder()
- .options(OpenAiChatOptions.builder()
- .baseUrl(BASE_URL)
- .apiKey(API_KEY)
- .model(DEPLOYMENT_NAME)
- .microsoftFoundry(true)
- .deploymentName(DEPLOYMENT_NAME)
- .temperature(0.7)
- .build())
- .build();
-
- @Test
- @Disabled
- public void testCall() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- ChatResponse response = chatModel.call(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }
-
- @Test
- @Disabled
- public void testStream() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }).then().block();
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/BaiChuanChatModelTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/BaiChuanChatModelTests.java
deleted file mode 100644
index 167532a..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/BaiChuanChatModelTests.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
-
-import cn.hutool.system.SystemUtil;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.SystemMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.deepseek.DeepSeekChatModel;
-import org.springframework.ai.deepseek.DeepSeekChatOptions;
-import org.springframework.ai.deepseek.api.DeepSeekApi;
-import reactor.core.publisher.Flux;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
-
-/**
- * {@link BaiChuanChatModel} 闆嗘垚娴嬭瘯
- *
- * @author 鑺嬮亾婧愮爜
- */
-public class BaiChuanChatModelTests {
-
- private static final String API_KEY = SystemUtil.get("BAICHUAN_API_KEY",
- "sk-xxxx"); // 鎸夐渶鏀规垚浣犵殑鐧惧窛 API Key
- private static final String MODEL = SystemUtil.get("BAICHUAN_MODEL",
- BaiChuanChatModel.MODEL_DEFAULT);
-
- private final BaiChuanChatModel chatModel = new BaiChuanChatModel(DeepSeekChatModel.builder()
- .deepSeekApi(DeepSeekApi.builder()
- .baseUrl(BaiChuanChatModel.BASE_URL)
- .completionsPath(BaiChuanChatModel.COMPLETE_PATH)
- .apiKey(API_KEY)
- .build())
- .options(DeepSeekChatOptions.builder()
- .model(MODEL)
- .temperature(0.7)
- .build())
- .build());
-
- @Test
- @Disabled
- public void testCall() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- ChatResponse response = chatModel.call(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }
-
- @Test
- @Disabled
- public void testStream() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }).then().block();
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/CozeChatModelTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/CozeChatModelTests.java
deleted file mode 100644
index bc6f4a9..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/CozeChatModelTests.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
-
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.SystemMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.openai.OpenAiChatModel;
-import org.springframework.ai.openai.OpenAiChatOptions;
-import reactor.core.publisher.Flux;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * 鍩轰簬 {@link OpenAiChatModel} 闆嗘垚 Coze 娴嬭瘯
- *
- * @author 鑺嬮亾婧愮爜
- */
-public class CozeChatModelTests {
-
- private final OpenAiChatModel chatModel = OpenAiChatModel.builder()
- .options(OpenAiChatOptions.builder()
- .baseUrl("http://127.0.0.1:3000")
- .apiKey("app-4hy2d7fJauSbrKbzTKX1afuP") // apiKey
- .build())
- .build();
-
- @Test
- @Disabled
- public void testCall() {
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- ChatResponse response = chatModel.call(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }
-
- @Test
- @Disabled
- public void testStream() {
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }).then().block();
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DeepSeekChatModelTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DeepSeekChatModelTests.java
deleted file mode 100644
index 51e385a..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DeepSeekChatModelTests.java
+++ /dev/null
@@ -1,94 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
-
-import cn.hutool.system.SystemUtil;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.SystemMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.deepseek.DeepSeekChatModel;
-import org.springframework.ai.deepseek.DeepSeekChatOptions;
-import org.springframework.ai.deepseek.api.DeepSeekApi;
-import reactor.core.publisher.Flux;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
-
-/**
- * {@link DeepSeekChatModel} 闆嗘垚娴嬭瘯
- *
- * @author 鑺嬮亾婧愮爜
- */
-public class DeepSeekChatModelTests {
-
- private static final String API_KEY = SystemUtil.get("DEEPSEEK_API_KEY",
- "sk-xxxx");
- private static final String MODEL = SystemUtil.get("DEEPSEEK_MODEL",
- "deepseek-v4-flash");
-
- private final DeepSeekChatModel chatModel = DeepSeekChatModel.builder()
- .deepSeekApi(DeepSeekApi.builder()
- .apiKey(API_KEY) // apiKey
- .build())
- .options(DeepSeekChatOptions.builder()
- .model(MODEL) // 妯″瀷
- .temperature(0.7)
- .build())
- .build();
-
- @Test
- @Disabled
- public void testCall() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- ChatResponse response = chatModel.call(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }
-
- @Test
- @Disabled
- public void testStream() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(System.out::println).then().block();
- }
-
- @Test
- @Disabled
- public void testStream_thinking() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new UserMessage("璇︾粏鍒嗘瀽涓嬶紝濡備綍璁捐涓�涓數鍟嗙郴缁燂紵"));
- DeepSeekChatOptions options = DeepSeekChatOptions.builder()
- .model(MODEL)
- .build();
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(response.getResult().getOutput());
- }).then().block();
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DifyChatModelTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DifyChatModelTests.java
deleted file mode 100644
index cde8d60..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DifyChatModelTests.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
-
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.SystemMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.openai.OpenAiChatModel;
-import org.springframework.ai.openai.OpenAiChatOptions;
-import reactor.core.publisher.Flux;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * 鍩轰簬 {@link OpenAiChatModel} 闆嗘垚 Dify 娴嬭瘯
- *
- * @author 鑺嬮亾婧愮爜
- */
-public class DifyChatModelTests {
-
- private final OpenAiChatModel chatModel = OpenAiChatModel.builder()
- .options(OpenAiChatOptions.builder()
- .baseUrl("http://127.0.0.1:3000")
- .apiKey("app-4hy2d7fJauSbrKbzTKX1afuP") // apiKey
- .build())
- .build();
-
- @Test
- @Disabled
- public void testCall() {
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- ChatResponse response = chatModel.call(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }
-
- @Test
- @Disabled
- public void testStream() {
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }).then().block();
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DouBaoChatModelTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DouBaoChatModelTests.java
deleted file mode 100644
index 75ce0ad..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DouBaoChatModelTests.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
-
-import cn.hutool.system.SystemUtil;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.SystemMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.deepseek.DeepSeekChatModel;
-import org.springframework.ai.deepseek.DeepSeekChatOptions;
-import org.springframework.ai.deepseek.api.DeepSeekApi;
-import reactor.core.publisher.Flux;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
-
-/**
- * {@link DouBaoChatModel} 闆嗘垚娴嬭瘯
- *
- * @author 鑺嬮亾婧愮爜
- */
-public class DouBaoChatModelTests {
-
- private static final String API_KEY = SystemUtil.get("DOUBAO_API_KEY",
- "sk-xxxx"); // 鎸夐渶鏀规垚浣犵殑璞嗗寘 API Key
- private static final String MODEL = SystemUtil.get("DOUBAO_MODEL",
- DouBaoChatModel.MODEL_DEFAULT);
-
- private final DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
- .deepSeekApi(DeepSeekApi.builder()
- .baseUrl(DouBaoChatModel.BASE_URL)
- .completionsPath(DouBaoChatModel.COMPLETE_PATH)
- .apiKey(API_KEY)
- .build())
- .options(DeepSeekChatOptions.builder()
- .model(MODEL)
- .temperature(0.7)
- .build())
- .build();
-
- private final DouBaoChatModel chatModel = new DouBaoChatModel(openAiChatModel);
-
- @Test
- @Disabled
- public void testCall() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- ChatResponse response = chatModel.call(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }
-
- @Test
- @Disabled
- public void testStream() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("璇︾粏鎺ㄧ悊涓嬶紝甯垜璁捐涓�涓敤鎴蜂腑蹇冿紒"));
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }).then().block();
- }
-
- @Test
- @Disabled
- public void testStream_thinking() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new UserMessage("璇︾粏鍒嗘瀽涓嬶紝濡備綍璁捐涓�涓數鍟嗙郴缁燂紵"));
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }).then().block();
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/FastGPTChatModelTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/FastGPTChatModelTests.java
deleted file mode 100644
index 09e5153..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/FastGPTChatModelTests.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
-
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.SystemMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.openai.OpenAiChatModel;
-import org.springframework.ai.openai.OpenAiChatOptions;
-import reactor.core.publisher.Flux;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * 鍩轰簬 {@link OpenAiChatModel} 闆嗘垚 FastGPT 娴嬭瘯
- *
- * @author 鑺嬮亾婧愮爜
- */
-public class FastGPTChatModelTests {
-
- private final OpenAiChatModel chatModel = OpenAiChatModel.builder()
- .options(OpenAiChatOptions.builder()
- .baseUrl("https://cloud.fastgpt.cn/api")
- .apiKey("fastgpt-aqcc61kFtF8CeaglnGAfQOCIDWwjGdJVJHv6hIlMo28otFlva2aZNK") // apiKey
- .build())
- .build();
-
- @Test
- @Disabled
- public void testCall() {
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- ChatResponse response = chatModel.call(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }
-
- @Test
- @Disabled
- public void testStream() {
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }).then().block();
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/GeminiChatModelTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/GeminiChatModelTests.java
deleted file mode 100644
index 8337157..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/GeminiChatModelTests.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
-
-import cn.hutool.core.util.StrUtil;
-import cn.hutool.system.SystemUtil;
-import com.google.genai.Client;
-import com.google.genai.types.HttpOptions;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.SystemMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.google.genai.GoogleGenAiChatModel;
-import org.springframework.ai.google.genai.GoogleGenAiChatOptions;
-import reactor.core.publisher.Flux;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
-
-/**
- * {@link GoogleGenAiChatModel} 闆嗘垚娴嬭瘯
- *
- * @author 鑺嬮亾婧愮爜
- */
-public class GeminiChatModelTests {
-
- private static final String BASE_URL = SystemUtil.get("GEMINI_BASE_URL");
- private static final String API_KEY = SystemUtil.get("GEMINI_API_KEY",
- "sk-xxxx"); // 鎸夐渶鏀规垚浣犵殑 Gemini API Key
- private static final String MODEL = "gemini-3.5-flash";
-
- private final GoogleGenAiChatModel chatModel = GoogleGenAiChatModel.builder()
- .genAiClient(buildClient())
- .options(GoogleGenAiChatOptions.builder()
- .model(MODEL) // 妯″瀷
- .temperature(0.7)
- .build())
- .build();
-
- private static Client buildClient() {
- Client.Builder builder = Client.builder().apiKey(API_KEY);
- if (StrUtil.isNotBlank(BASE_URL)) {
- builder.httpOptions(HttpOptions.builder().baseUrl(BASE_URL).build());
- }
- return builder.build();
- }
-
- @Test
- @Disabled
- public void testCall() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- ChatResponse response = chatModel.call(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }
-
- @Test
- @Disabled
- public void testStream() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
- System.out.println(response);
-// System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }).then().block();
- }
-
- @Test
- @Disabled
- public void testStream_thinking() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new UserMessage("璇︾粏鍒嗘瀽涓嬶紝濡備綍璁捐涓�涓數鍟嗙郴缁燂紵"));
- GoogleGenAiChatOptions options = GoogleGenAiChatOptions.builder()
- .model(MODEL)
- .thinkingBudget(1024)
- .includeThoughts(true)
- .build();
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
- System.out.println(response);
-// System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }).then().block();
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/GrokChatModelTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/GrokChatModelTests.java
deleted file mode 100644
index 7301da9..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/GrokChatModelTests.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
-
-import cn.hutool.system.SystemUtil;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.grok.GrokChatModel;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.SystemMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.openai.OpenAiChatModel;
-import org.springframework.ai.openai.OpenAiChatOptions;
-import reactor.core.publisher.Flux;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
-
-/**
- * {@link GrokChatModel} 闆嗘垚娴嬭瘯
- *
- * @author 鑺嬮亾婧愮爜
- */
-public class GrokChatModelTests {
-
- private static final String API_KEY = SystemUtil.get("GROK_API_KEY",
- "sk-xxxx");
- private static final String MODEL = SystemUtil.get("GROK_MODEL",
- GrokChatModel.MODEL_DEFAULT);
-
- private final GrokChatModel chatModel = new GrokChatModel(OpenAiChatModel.builder()
- .options(OpenAiChatOptions.builder()
- .baseUrl(GrokChatModel.BASE_URL)
- .apiKey(API_KEY)
- .model(MODEL)
- .temperature(0.7)
- .build())
- .build());
-
- @Test
- @Disabled
- public void testCall() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- ChatResponse response = chatModel.call(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }
-
- @Test
- @Disabled
- public void testStream() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }).then().block();
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/HunYuanChatModelTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/HunYuanChatModelTests.java
deleted file mode 100644
index 3f894af..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/HunYuanChatModelTests.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
-
-import cn.hutool.system.SystemUtil;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.SystemMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.deepseek.DeepSeekChatModel;
-import org.springframework.ai.deepseek.DeepSeekChatOptions;
-import org.springframework.ai.deepseek.api.DeepSeekApi;
-import reactor.core.publisher.Flux;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
-
-/**
- * {@link HunYuanChatModel} 闆嗘垚娴嬭瘯
- *
- * @author 鑺嬮亾婧愮爜
- */
-public class HunYuanChatModelTests {
-
- private static final String API_KEY = SystemUtil.get("HUNYUAN_API_KEY",
- "sk-xxxx");
- private static final String MODEL = SystemUtil.get("HUNYUAN_MODEL",
- HunYuanChatModel.MODEL_DEFAULT);
-
- private final DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
- .deepSeekApi(DeepSeekApi.builder()
- .baseUrl(HunYuanChatModel.BASE_URL)
- .completionsPath(HunYuanChatModel.COMPLETE_PATH)
- .apiKey(API_KEY) // apiKey
- .build())
- .options(DeepSeekChatOptions.builder()
- .model(MODEL) // 妯″瀷
- .temperature(0.7)
- .build())
- .build();
-
- private final HunYuanChatModel chatModel = new HunYuanChatModel(openAiChatModel);
-
- @Test
- @Disabled
- public void testCall() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- ChatResponse response = chatModel.call(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }
-
- @Test
- @Disabled
- public void testStream() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(System.out::println).then().block();
- }
-
- @Test
- @Disabled
- public void testStream_thinking() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new UserMessage("璇︾粏鍒嗘瀽涓嬶紝濡備綍璁捐涓�涓數鍟嗙郴缁燂紵"));
- DeepSeekChatOptions options = DeepSeekChatOptions.builder()
- .model(MODEL)
- .build();
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(response.getResult().getOutput());
- }).then().block();
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/LlamaChatModelTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/LlamaChatModelTests.java
deleted file mode 100644
index cafb360..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/LlamaChatModelTests.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
-
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.SystemMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.ollama.OllamaChatModel;
-import org.springframework.ai.ollama.api.OllamaApi;
-import org.springframework.ai.ollama.api.OllamaChatOptions;
-import org.springframework.ai.ollama.api.OllamaModel;
-import reactor.core.publisher.Flux;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * {@link OllamaChatModel} 闆嗘垚娴嬭瘯
- *
- * @author 鑺嬮亾婧愮爜
- */
-public class LlamaChatModelTests {
-
- private final OllamaChatModel chatModel = OllamaChatModel.builder()
- .ollamaApi(OllamaApi.builder()
- .baseUrl("http://127.0.0.1:11434") // Ollama 鏈嶅姟鍦板潃
- .build())
- .options(OllamaChatOptions.builder()
- .model(OllamaModel.LLAMA3.getName()) // 妯″瀷
- .build())
- .build();
-
- @Test
- @Disabled
- public void testCall() {
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- ChatResponse response = chatModel.call(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- System.out.println(response);
- System.out.println(response.getResult().getOutput());
- }
-
- @Test
- @Disabled
- public void testStream() {
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(response.getResult().getOutput());
- }).then().block();
- }
-
- @Test
- @Disabled
- public void testStream_thinking() {
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new UserMessage("璇︾粏鍒嗘瀽涓嬶紝濡備綍璁捐涓�涓數鍟嗙郴缁燂紵"));
- OllamaChatOptions options = OllamaChatOptions.builder()
- .model("qwen3")
- .build();
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(response.getResult().getOutput());
- }).then().block();
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MiniMaxChatModelTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MiniMaxChatModelTests.java
deleted file mode 100644
index ceefd22..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MiniMaxChatModelTests.java
+++ /dev/null
@@ -1,98 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
-
-import cn.hutool.system.SystemUtil;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.minimax.MiniMaxChatModel;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.SystemMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.deepseek.DeepSeekChatModel;
-import org.springframework.ai.deepseek.DeepSeekChatOptions;
-import org.springframework.ai.deepseek.api.DeepSeekApi;
-import reactor.core.publisher.Flux;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
-
-/**
- * {@link MiniMaxChatModel} 鐨勯泦鎴愭祴璇�
- *
- * @author 鑺嬮亾婧愮爜
- */
-public class MiniMaxChatModelTests {
-
- private static final String API_KEY = SystemUtil.get("MINIMAX_API_KEY",
- "sk-xxxx"); // 鎸夐渶鏀规垚浣犵殑 MiniMax API Key
- private static final String MODEL = SystemUtil.get("MINIMAX_MODEL",
- MiniMaxChatModel.MODEL_DEFAULT);
-
- private final MiniMaxChatModel chatModel = new MiniMaxChatModel(DeepSeekChatModel.builder()
- .deepSeekApi(DeepSeekApi.builder()
- .baseUrl(MiniMaxChatModel.BASE_URL)
- .apiKey(API_KEY)
- .build())
- .options(DeepSeekChatOptions.builder()
- .model(MODEL)
- .build())
- .build());
- @Test
- @Disabled
- public void testCall() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- ChatResponse response = chatModel.call(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }
-
- @Test
- @Disabled
- public void testStream() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }).then().block();
- }
-
- @Test
- @Disabled
- public void testStream_thinking() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new UserMessage("璇︾粏鍒嗘瀽涓嬶紝濡備綍璁捐涓�涓數鍟嗙郴缁燂紵"));
- DeepSeekChatOptions options = DeepSeekChatOptions.builder()
- .model(MODEL)
- .build();
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }).then().block();
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MoonshotChatModelTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MoonshotChatModelTests.java
deleted file mode 100644
index 22d04bf..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MoonshotChatModelTests.java
+++ /dev/null
@@ -1,101 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
-
-import cn.hutool.system.SystemUtil;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.moonshot.MoonshotChatModel;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.SystemMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.deepseek.DeepSeekChatModel;
-import org.springframework.ai.deepseek.DeepSeekChatOptions;
-import org.springframework.ai.deepseek.api.DeepSeekApi;
-import reactor.core.publisher.Flux;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
-
-/**
- * {@link MoonshotChatModel} 鐨勯泦鎴愭祴璇�
- *
- * @author 鑺嬮亾婧愮爜
- */
-public class MoonshotChatModelTests {
-
- private static final String API_KEY = SystemUtil.get("MOONSHOT_API_KEY",
- "sk-xxxx"); // 鎸夐渶鏀规垚浣犵殑 Moonshot API Key
- private static final String MODEL = SystemUtil.get("MOONSHOT_MODEL",
- MoonshotChatModel.MODEL_DEFAULT);
-
- private final MoonshotChatModel chatModel = new MoonshotChatModel(DeepSeekChatModel.builder()
- .deepSeekApi(DeepSeekApi.builder()
- .baseUrl(MoonshotChatModel.BASE_URL)
- .completionsPath(MoonshotChatModel.COMPLETE_PATH)
- .apiKey(API_KEY)
- .build())
- .options(DeepSeekChatOptions.builder()
- .model(MODEL)
- .temperature(1D)
- .build())
- .build());
-
- @Test
- @Disabled
- public void testCall() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- ChatResponse response = chatModel.call(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }
-
- @Test
- @Disabled
- public void testStream() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }).then().block();
- }
-
- @Test
- @Disabled
- public void testStream_thinking() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new UserMessage("璇︾粏鍒嗘瀽涓嬶紝濡備綍璁捐涓�涓數鍟嗙郴缁燂紵"));
- DeepSeekChatOptions options = DeepSeekChatOptions.builder()
- .model(MODEL)
- .build();
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }).then().block();
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OllamaChatModelTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OllamaChatModelTests.java
deleted file mode 100644
index 119d569..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OllamaChatModelTests.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
-
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.SystemMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.ollama.OllamaChatModel;
-import org.springframework.ai.ollama.api.OllamaApi;
-import org.springframework.ai.ollama.api.OllamaChatOptions;
-import reactor.core.publisher.Flux;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * {@link OllamaChatModel} 闆嗘垚娴嬭瘯
- *
- * @author 鑺嬮亾婧愮爜
- */
-public class OllamaChatModelTests {
-
- private final OllamaChatModel chatModel = OllamaChatModel.builder()
- .ollamaApi(OllamaApi.builder()
- .baseUrl("http://127.0.0.1:11434") // Ollama 鏈嶅姟鍦板潃
- .build())
- .options(OllamaChatOptions.builder()
-// .model("qwen") // 妯″瀷锛坔ttps://ollama.com/library/qwen锛�
- .model("deepseek-r1") // 妯″瀷锛坔ttps://ollama.com/library/deepseek-r1锛�
- .build())
- .build();
-
- @Test
- @Disabled
- public void testCall() {
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- ChatResponse response = chatModel.call(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- System.out.println(response);
- System.out.println(response.getResult().getOutput());
- }
-
- @Test
- @Disabled
- public void testStream() {
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(response.getResult().getOutput());
- }).then().block();
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OpenAIChatModelTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OpenAIChatModelTests.java
deleted file mode 100644
index 9c5fa52..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OpenAIChatModelTests.java
+++ /dev/null
@@ -1,103 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
-
-import cn.hutool.system.SystemUtil;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.SystemMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.openai.OpenAiChatModel;
-import org.springframework.ai.openai.OpenAiChatOptions;
-import reactor.core.publisher.Flux;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
-
-/**
- * {@link OpenAiChatModel} 闆嗘垚娴嬭瘯
- *
- * @author 鑺嬮亾婧愮爜
- */
-public class OpenAIChatModelTests {
-
- private static final String BASE_URL = SystemUtil.get("OPENAI_BASE_URL",
- "https://api.teamorouter.com/v1");
- private static final String API_KEY = SystemUtil.get("OPENAI_API_KEY",
- "sk-xxxx"); // 鎸夐渶鏀规垚浣犵殑 OpenAI API Key
- private static final String MODEL = SystemUtil.get("OPENAI_MODEL",
- "gpt-5.5");
-
- private final OpenAiChatModel chatModel = OpenAiChatModel.builder()
- .options(OpenAiChatOptions.builder()
- .baseUrl(BASE_URL)
- .apiKey(API_KEY)
- .model(MODEL)
- .temperature(0.7)
- .build())
- .build();
-
- @Test
- @Disabled
- public void testCall() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- ChatResponse response = chatModel.call(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }
-
- @Test
- @Disabled
- public void testStream() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("甯垜鎺ㄧ悊涓嬶紝鎬庝箞瀹炵幇涓�涓敤鎴蜂腑蹇冿紒"));
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }).then().block();
- }
-
- // TODO @鑺嬭壙锛氭棤娉曡Е鍙戞�濊�冪殑瀛楁杩斿洖锛岄渶瑕� response api锛歨ttps://github.com/spring-projects/spring-ai/issues/2962
- @Test
- @Disabled
- public void testStream_thinking() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new UserMessage("璇︾粏鍒嗘瀽涓嬶紝濡備綍璁捐涓�涓數鍟嗙郴缁燂紵"));
- OpenAiChatOptions options = OpenAiChatOptions.builder()
- .baseUrl(BASE_URL)
- .apiKey(API_KEY)
- .model(MODEL)
- .reasoningEffort("low") // https://help.openai.com/zh-hant/articles/5072518-controlling-the-length-of-openai-model-responses
- .maxCompletionTokens(1024)
- .build();
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }).then().block();
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/SiliconFlowChatModelTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/SiliconFlowChatModelTests.java
deleted file mode 100644
index 6064cfd..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/SiliconFlowChatModelTests.java
+++ /dev/null
@@ -1,103 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
-
-import cn.hutool.system.SystemUtil;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowApiConstants;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowChatModel;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.SystemMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.deepseek.DeepSeekChatModel;
-import org.springframework.ai.deepseek.DeepSeekChatOptions;
-import org.springframework.ai.deepseek.api.DeepSeekApi;
-import reactor.core.publisher.Flux;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
-
-/**
- * {@link SiliconFlowChatModel} 闆嗘垚娴嬭瘯
- *
- * @author 鑺嬮亾婧愮爜
- */
-public class SiliconFlowChatModelTests {
-
- private static final String API_KEY = SystemUtil.get("SILICONFLOW_API_KEY",
- "sk-xxxx"); // 鎸夐渶鏀规垚浣犵殑 SiliconFlow API Key
- private static final String MODEL = SystemUtil.get("SILICONFLOW_MODEL",
- SiliconFlowApiConstants.MODEL_DEFAULT);
-
- private final DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
- .deepSeekApi(DeepSeekApi.builder()
- .baseUrl(SiliconFlowApiConstants.DEFAULT_BASE_URL)
- .apiKey(API_KEY)
- .build())
- .options(DeepSeekChatOptions.builder()
- .model(MODEL) // 妯″瀷
-// .model("deepseek-ai/DeepSeek-V4-Flash") // 妯″瀷
-// .model("moonshotai/Kimi-K2.7-Code") // 妯″瀷
-// .model("moonshotai/Kimi-K2.6") // 妯″瀷
-// .model("zai-org/GLM-5.2") // 妯″瀷
- .temperature(0.7)
- .build())
- .build();
-
- private final SiliconFlowChatModel chatModel = new SiliconFlowChatModel(openAiChatModel);
-
- @Test
- @Disabled
- public void testCall() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- ChatResponse response = chatModel.call(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- System.out.println(response);
- }
-
- @Test
- @Disabled
- public void testStream() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(System.out::println).then().block();
- }
-
- @Test
- @Disabled
- public void testStream_thinking() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new UserMessage("璇︾粏鍒嗘瀽涓嬶紝濡備綍璁捐涓�涓數鍟嗙郴缁燂紵"));
- DeepSeekChatOptions options = DeepSeekChatOptions.builder()
- .model(MODEL)
- .build();
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }).then().block();
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/StepFunChatModelTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/StepFunChatModelTests.java
deleted file mode 100644
index e84a94a..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/StepFunChatModelTests.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
-
-import cn.hutool.system.SystemUtil;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.stepfun.StepFunChatModel;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.SystemMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.deepseek.DeepSeekChatModel;
-import org.springframework.ai.deepseek.DeepSeekChatOptions;
-import org.springframework.ai.deepseek.api.DeepSeekApi;
-import reactor.core.publisher.Flux;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
-
-/**
- * {@link StepFunChatModel} 闆嗘垚娴嬭瘯
- *
- * @author 鑺嬮亾婧愮爜
- */
-public class StepFunChatModelTests {
-
- private static final String API_KEY = SystemUtil.get("STEPFUN_API_KEY",
- "sk-xxxx");
- private static final String MODEL = SystemUtil.get("STEPFUN_MODEL",
- StepFunChatModel.MODEL_DEFAULT);
-
- private final StepFunChatModel chatModel = new StepFunChatModel(DeepSeekChatModel.builder()
- .deepSeekApi(DeepSeekApi.builder()
- .baseUrl(StepFunChatModel.BASE_URL)
- .completionsPath(StepFunChatModel.COMPLETE_PATH)
- .apiKey(API_KEY)
- .build())
- .options(DeepSeekChatOptions.builder()
- .model(MODEL)
- .temperature(0.7)
- .build())
- .build());
-
- @Test
- @Disabled
- public void testCall() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- ChatResponse response = chatModel.call(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }
-
- @Test
- @Disabled
- public void testStream() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
-// System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- System.out.println(response.getResult() != null ? response.getResult().getOutput() : null);
- }).then().block();
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/TongYiChatModelTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/TongYiChatModelTests.java
deleted file mode 100644
index 8c2fb5d..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/TongYiChatModelTests.java
+++ /dev/null
@@ -1,142 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
-
-import cn.hutool.system.SystemUtil;
-import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
-import cn.iocoder.yudao.module.ai.util.AiUtils;
-import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
-import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
-import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
-import com.alibaba.cloud.ai.dashscope.rerank.DashScopeRerankModel;
-import com.alibaba.cloud.ai.dashscope.rerank.DashScopeRerankOptions;
-import com.alibaba.cloud.ai.model.RerankModel;
-import com.alibaba.cloud.ai.model.RerankOptions;
-import com.alibaba.cloud.ai.model.RerankRequest;
-import com.alibaba.cloud.ai.model.RerankResponse;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.SystemMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.document.Document;
-import reactor.core.publisher.Flux;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
-import static java.util.Arrays.asList;
-
-/**
- * {@link DashScopeChatModel} 闆嗘垚娴嬭瘯绫�
- *
- * @author fansili
- */
-public class TongYiChatModelTests {
-
- private static final String API_KEY = SystemUtil.get("DASHSCOPE_API_KEY",
- "sk-xxxx"); // 鎸夐渶鏀规垚浣犵殑 DashScope API Key
- private static final String MODEL = SystemUtil.get("DASHSCOPE_MODEL",
- "qwen3.7-plus");
-
- private final DashScopeChatModel chatModel = DashScopeChatModel.builder()
- .dashScopeApi(DashScopeApi.builder()
- .apiKey(API_KEY)
- .build())
- .defaultOptions(DashScopeChatOptions.builder()
- .multiModel(AiUtils.TONG_YI_MULTI_MODELS.contains(MODEL)) // 澶氭ā鎬佹ā鍨嬮渶瑕佽缃负 true锛屽彲瑙� https://help.aliyun.com/zh/model-studio/error-code#error-url
- .model(MODEL) // 妯″瀷
-// .model("deepseek-r1") // 妯″瀷锛坉eepseek-r1锛�
-// .model("deepseek-v3") // 妯″瀷锛坉eepseek-v3锛�
-// .model("deepseek-r1-distill-qwen-1.5b") // 妯″瀷锛坉eepseek-r1-distill-qwen-1.5b锛�
-// .enableThinking(true)
- .build())
- .build();
-
- @Test
- @Disabled
- public void testCall() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- ChatResponse response = chatModel.call(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }
-
- @Test
- @Disabled
- public void testStream() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
-// messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("甯垜鎺ㄧ悊涓嬶紝鎬庝箞瀹炵幇涓�涓敤鎴蜂腑蹇冿紒"));
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }).then().block();
- }
-
- @Test
- @Disabled
- public void testStream_thinking() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new UserMessage("璇︾粏鍒嗘瀽涓嬶紝濡備綍璁捐涓�涓數鍟嗙郴缁燂紵"));
- DashScopeChatOptions options = DashScopeChatOptions.builder()
- .model(MODEL).multiModel(AiUtils.TONG_YI_MULTI_MODELS.contains(MODEL))
-// .withModel("qwen-max-2025-01-25")
- .enableThinking(true) // 蹇呴』璁剧疆锛屽惁鍒欎細鎶ラ敊
- .build();
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }).then().block();
- }
-
- @Test
- @Disabled
- public void testRerank() {
- validateApiKey(API_KEY);
- // 鍑嗗鐜
- RerankModel rerankModel = new DashScopeRerankModel(
- DashScopeApi.builder()
- .apiKey(API_KEY)
- .build());
- // 鍑嗗鍙傛暟
- String query = "spring";
- Document document01 = new Document("abc");
- Document document02 = new Document("sapring");
- RerankOptions options = DashScopeRerankOptions.builder()
- .topN(1)
- .model("gte-rerank-v2")
- .build();
- RerankRequest rerankRequest = new RerankRequest(
- query,
- asList(document01, document02),
- options);
-
- // 璋冪敤
- RerankResponse call = rerankModel.call(rerankRequest);
- // 鎵撳嵃缁撴灉
- System.out.println(JsonUtils.toJsonPrettyString(call));
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/XingHuoChatModelTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/XingHuoChatModelTests.java
deleted file mode 100644
index 2e08cf6..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/XingHuoChatModelTests.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
-
-import cn.hutool.system.SystemUtil;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.XingHuoChatModel;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.SystemMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.deepseek.DeepSeekChatOptions;
-import reactor.core.publisher.Flux;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
-
-/**
- * {@link XingHuoChatModel} 闆嗘垚娴嬭瘯
- *
- * @author fansili
- */
-public class XingHuoChatModelTests {
-
- private static final String API_KEY = SystemUtil.get("XINGHUO_API_KEY",
- "sk-xxxx"); // 鎸夐渶鏀规垚浣犵殑璁鏄熺伀 API Key
- private static final String MODEL = SystemUtil.get("XINGHUO_MODEL",
- XingHuoChatModel.MODEL_DEFAULT);
-
- private final XingHuoChatModel chatModel = XingHuoChatModel.builder()
- .apiKey(API_KEY)
- .options(DeepSeekChatOptions.builder()
- .model(MODEL)
- .temperature(0.7)
- .build())
- .build();
-
- @Test
- @Disabled
- public void testCall() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- ChatResponse response = chatModel.call(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- System.out.println(response);
- }
-
- @Test
- @Disabled
- public void testStream() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(System.out::println).then().block();
- }
-
- @Test
- @Disabled
- public void testStream_thinking() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new UserMessage("璇︾粏鍒嗘瀽涓嬶紝濡備綍璁捐涓�涓數鍟嗙郴缁燂紵"));
- DeepSeekChatOptions options = DeepSeekChatOptions.builder()
- .model(MODEL)
- .build();
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }).then().block();
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/YiYanChatModelTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/YiYanChatModelTests.java
deleted file mode 100644
index 068a062..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/YiYanChatModelTests.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
-
-import cn.hutool.system.SystemUtil;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.yiyan.YiYanChatModel;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.deepseek.DeepSeekChatModel;
-import org.springframework.ai.deepseek.DeepSeekChatOptions;
-import org.springframework.ai.deepseek.api.DeepSeekApi;
-import reactor.core.publisher.Flux;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
-
-/**
- * {@link YiYanChatModel} 鐨勯泦鎴愭祴璇�
- *
- * @author fansili
- */
-public class YiYanChatModelTests {
-
- private static final String API_KEY = SystemUtil.get("YIYAN_API_KEY",
- "sk-xxxx"); // 鎸夐渶鏀规垚浣犵殑鏂囧績涓�瑷� API Key
- private static final String MODEL = SystemUtil.get("YIYAN_MODEL",
- YiYanChatModel.MODEL_DEFAULT);
-
- private final YiYanChatModel chatModel = new YiYanChatModel(DeepSeekChatModel.builder()
- .deepSeekApi(DeepSeekApi.builder()
- .baseUrl(YiYanChatModel.BASE_URL)
- .apiKey(API_KEY)
- .build())
- .options(DeepSeekChatOptions.builder()
- .model(MODEL)
- .build())
- .build());
-
- @Test
- @Disabled
- public void testCall() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- ChatResponse response = chatModel.call(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }
-
- @Test
- @Disabled
- public void testStream() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }).then().block();
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/ZhiPuAiChatModelTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/ZhiPuAiChatModelTests.java
deleted file mode 100644
index 1abaa43..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/ZhiPuAiChatModelTests.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
-
-import cn.hutool.system.SystemUtil;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.zhipu.ZhiPuChatModel;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.chat.messages.Message;
-import org.springframework.ai.chat.messages.SystemMessage;
-import org.springframework.ai.chat.messages.UserMessage;
-import org.springframework.ai.chat.model.ChatResponse;
-import org.springframework.ai.chat.prompt.Prompt;
-import org.springframework.ai.deepseek.DeepSeekChatModel;
-import org.springframework.ai.deepseek.DeepSeekChatOptions;
-import org.springframework.ai.deepseek.api.DeepSeekApi;
-import reactor.core.publisher.Flux;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
-
-/**
- * {@link ZhiPuChatModel} 鐨勯泦鎴愭祴璇�
- *
- * @author 鑺嬮亾婧愮爜
- */
-public class ZhiPuAiChatModelTests {
-
- private static final String API_KEY = SystemUtil.get("ZHIPU_API_KEY",
- "sk-xxxx"); // 鎸夐渶鏀规垚浣犵殑鏅鸿氨 API Key
- private static final String MODEL = SystemUtil.get("ZHIPU_MODEL",
- ZhiPuChatModel.MODEL_DEFAULT);
-
- private final ZhiPuChatModel chatModel = new ZhiPuChatModel(DeepSeekChatModel.builder()
- .deepSeekApi(DeepSeekApi.builder()
- .baseUrl(ZhiPuChatModel.BASE_URL)
- .apiKey(API_KEY)
- .build())
- .options(DeepSeekChatOptions.builder()
- .model(MODEL)
- .build()
- ).build());
-
- @Test
- @Disabled
- public void testCall() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- ChatResponse response = chatModel.call(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }
-
- @Test
- @Disabled
- public void testStream() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new SystemMessage("浣犳槸涓�涓紭璐ㄧ殑鏂囪█鏂囦綔鑰咃紝鐢ㄦ枃瑷�鏂囨弿杩扮潃鍚勫煄甯傜殑浜烘枃椋庢櫙銆�"));
- messages.add(new UserMessage("1 + 1 = 锛�"));
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }).then().block();
- }
-
- @Test
- @Disabled
- public void testStream_thinking() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- List<Message> messages = new ArrayList<>();
- messages.add(new UserMessage("璇︾粏鍒嗘瀽涓嬶紝濡備綍璁捐涓�涓數鍟嗙郴缁燂紵"));
- DeepSeekChatOptions options = DeepSeekChatOptions.builder()
- .model(MODEL)
- .build();
-
- // 璋冪敤
- Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
- // 鎵撳嵃缁撴灉
- flux.doOnNext(response -> {
-// System.out.println(response);
- System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
- }).then().block();
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/MidjourneyApiTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/MidjourneyApiTests.java
deleted file mode 100644
index 90bd297..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/MidjourneyApiTests.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.image;
-
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * {@link MidjourneyApi} 闆嗘垚娴嬭瘯
- *
- * @author 鑺嬮亾婧愮爜
- */
-public class MidjourneyApiTests {
-
- private final MidjourneyApi midjourneyApi = new MidjourneyApi(
- "https://api.holdai.top/mj", // 閾炬帴
- "sk-aN6nWn3fILjrgLFT0fC4Aa60B72e4253826c77B29dC94f17", // 瀵嗛挜
- null);
-
- @Test
- @Disabled
- public void testImagine() {
- // 鍑嗗鍙傛暟
- MidjourneyApi.ImagineRequest request = new MidjourneyApi.ImagineRequest(null,
- "鐢熸垚涓�涓皬鐚紝鍙埍鐨�", null,
- MidjourneyApi.ImagineRequest.buildState(512, 512, "6.0", MidjourneyApi.ModelEnum.MIDJOURNEY.getModel()));
-
- // 鏂规硶璋冪敤
- MidjourneyApi.SubmitResponse response = midjourneyApi.imagine(request);
- // 鎵撳嵃缁撴灉
- System.out.println(response);
- }
-
- @Test
- @Disabled
- public void testAction() {
- // 鍑嗗鍙傛暟
- MidjourneyApi.ActionRequest request = new MidjourneyApi.ActionRequest("1720277033455953",
- "MJ::JOB::upsample::1::ee267661-ee52-4ced-a530-0343ba95af3b", null);
-
- // 鏂规硶璋冪敤
- MidjourneyApi.SubmitResponse response = midjourneyApi.action(request);
- // 鎵撳嵃缁撴灉
- System.out.println(response);
- }
-
- @Test
- @Disabled
- public void testGetTaskList() {
- // 鍑嗗鍙傛暟銆傝鍙傛暟鍙互閫氳繃 MidjourneyApi.SubmitResponse 鐨� result 鑾峰彇
-// String taskId = "1720277033455953";
- String taskId = "1720277214045971";
-
- // 鏂规硶璋冪敤
- List<MidjourneyApi.Notify> taskList = midjourneyApi.getTaskList(Collections.singletonList(taskId));
- // 鎵撳嵃缁撴灉
- System.out.println(taskList);
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/OpenAiImageModelTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/OpenAiImageModelTests.java
deleted file mode 100644
index a30e7cf..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/OpenAiImageModelTests.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.image;
-
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.image.ImageOptions;
-import org.springframework.ai.image.ImagePrompt;
-import org.springframework.ai.image.ImageResponse;
-import org.springframework.ai.openai.OpenAiImageModel;
-import org.springframework.ai.openai.OpenAiImageOptions;
-
-/**
- * {@link OpenAiImageModel} 闆嗘垚娴嬭瘯绫�
- *
- * @author fansili
- */
-public class OpenAiImageModelTests {
-
- private final OpenAiImageModel imageModel = OpenAiImageModel.builder()
- .options(OpenAiImageOptions.builder()
- .baseUrl("https://api.holdai.top") // apiKey
- .apiKey("sk-xxx")
- .build())
- .build();
-
- @Test
- @Disabled
- public void testCall() {
- // 鍑嗗鍙傛暟
- ImageOptions options = OpenAiImageOptions.builder()
- .model("dall-e-2") // 杩欎釜妯″瀷姣旇緝渚垮疁
- .height(256).width(256)
- .build();
- ImagePrompt prompt = new ImagePrompt("涓浗闀垮煄!", options);
-
- // 鏂规硶璋冪敤
- ImageResponse response = imageModel.call(prompt);
- // 鎵撳嵃缁撴灉
- System.out.println(response);
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/SiliconFlowImageModelTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/SiliconFlowImageModelTests.java
deleted file mode 100644
index 887832d..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/SiliconFlowImageModelTests.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.image;
-
-import cn.hutool.system.SystemUtil;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowImageApi;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowImageModel;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowImageOptions;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.image.ImagePrompt;
-import org.springframework.ai.image.ImageResponse;
-
-import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
-
-/**
- * {@link SiliconFlowImageModel} 闆嗘垚娴嬭瘯
- */
-public class SiliconFlowImageModelTests {
-
- private static final String API_KEY = SystemUtil.get("SILICONFLOW_API_KEY",
- "sk-xxxx"); // 鎸夐渶鏀规垚浣犵殑 SiliconFlow API Key
-
- private final SiliconFlowImageModel imageModel = new SiliconFlowImageModel(
- new SiliconFlowImageApi(API_KEY)
- );
-
- @Test
- @Disabled
- public void testCall() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- SiliconFlowImageOptions imageOptions = SiliconFlowImageOptions.builder()
- .model("Kwai-Kolors/Kolors")
- .build();
- ImagePrompt prompt = new ImagePrompt("涓囬噷闀垮煄", imageOptions);
-
- // 鏂规硶璋冪敤
- ImageResponse response = imageModel.call(prompt);
- // 鎵撳嵃缁撴灉
- System.out.println(response);
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/StabilityAiImageModelTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/StabilityAiImageModelTests.java
deleted file mode 100644
index 8cf556d..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/StabilityAiImageModelTests.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.image;
-
-import cn.hutool.core.codec.Base64;
-import cn.hutool.core.thread.ThreadUtil;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.image.ImageOptions;
-import org.springframework.ai.image.ImagePrompt;
-import org.springframework.ai.image.ImageResponse;
-import org.springframework.ai.openai.OpenAiImageOptions;
-import org.springframework.ai.stabilityai.StabilityAiImageModel;
-import org.springframework.ai.stabilityai.api.StabilityAiApi;
-
-import javax.swing.*;
-import java.awt.*;
-import java.util.concurrent.TimeUnit;
-
-/**
- * {@link StabilityAiImageModel} 闆嗘垚娴嬭瘯绫�
- *
- * @author fansili
- */
-public class StabilityAiImageModelTests {
-
- private final StabilityAiImageModel imageModel = new StabilityAiImageModel(
- new StabilityAiApi("sk-e53UqbboF8QJCscYvzJscJxJXoFcFg4iJjl1oqgE7baJETmx") // 瀵嗛挜
- );
-
- @Test
- @Disabled
- public void testCall() {
- // 鍑嗗鍙傛暟
- ImageOptions options = OpenAiImageOptions.builder()
- .model("stable-diffusion-v1-6")
- .height(320).width(320)
- .build();
- ImagePrompt prompt = new ImagePrompt("great wall", options);
-
- // 鏂规硶璋冪敤
- ImageResponse response = imageModel.call(prompt);
- // 鎵撳嵃缁撴灉
- String b64Json = response.getResult().getOutput().getB64Json();
- System.out.println(response);
- viewImage(b64Json);
- }
-
- public static void viewImage(String b64Json) {
- // 鍒涘缓涓�涓� JFrame
- JFrame frame = new JFrame("Byte Image Display");
- frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- frame.setSize(800, 600);
-
- // 鍒涘缓涓�涓� JLabel 鏉ユ樉绀哄浘鐗�
- byte[] imageBytes = Base64.decode(b64Json);
- JLabel label = new JLabel(new ImageIcon(imageBytes));
-
- // 灏� JLabel 娣诲姞鍒� JFrame
- frame.getContentPane().add(label, BorderLayout.CENTER);
-
- // 鏄剧ず JFrame
- frame.setVisible(true);
- ThreadUtil.sleep(1, TimeUnit.HOURS);
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/TongYiImagesModelTest.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/TongYiImagesModelTest.java
deleted file mode 100644
index dbc3546..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/TongYiImagesModelTest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.image;
-
-import cn.hutool.system.SystemUtil;
-import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi;
-import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel;
-import com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.image.ImageOptions;
-import org.springframework.ai.image.ImagePrompt;
-import org.springframework.ai.image.ImageResponse;
-
-import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
-
-/**
- * {@link DashScopeImageModel} 闆嗘垚娴嬭瘯绫�
- *
- * TODO @鑺嬭壙锛氭敞锛歴pring-ai-alibaba-dashscope锛�1.1.2.2锛夌殑 {@code DashScopeImageApi#resolveImagePath} 鏈粰 {@code wan2.7-image} 鍔犺矾鐢卞垎鏀紝
- * 浼氳惤鍒伴粯璁ょ殑 {@code text2image/image-synthesis} 寮傛绔偣 + 鏃х増 {@code prompt} 鍏ュ弬锛�
- * 鑰岃妯″瀷瀹為檯瑕佹眰 {@code multimodal-generation/generation} 鍚屾绔偣 + {@code messages} 鍏ュ弬锛�
- * 绔偣銆佸紓姝ュご銆佸叆鍙傜粨鏋勫叏閮ㄥ涓嶄笂锛屾墍浠ヨ蛋 SDK 鐩存帴璋冪敤蹇呭け璐ャ��
- *
- * 涓存椂鏂规锛氭敼鐢� SDK 宸叉敮鎸佺殑 {@code wan2.6-image}锛堝紓姝ワ級鎴� {@code qwen-image}锛堝悓姝ワ級锛�
- * 鎴栧湪椤圭洰鍐呭悓鍖呭悓鍚嶈鐩� {@code DashScopeImageApi}锛屾妸 {@code wan2.7*} 涔熻矾鐢卞埌 {@code wan2.6-image} 閭f潯 {@code image-generation/generation} 寮傛鍒嗘敮銆�
- *
- * @author fansili
- */
-public class TongYiImagesModelTest {
-
- private static final String API_KEY = SystemUtil.get("DASHSCOPE_API_KEY",
- "sk-xxxx"); // 鎸夐渶鏀规垚浣犵殑 DashScope API Key
-
- private final DashScopeImageModel imageModel = DashScopeImageModel.builder()
- .dashScopeApi(DashScopeImageApi.builder()
- .apiKey(API_KEY)
- .build())
- .build();
-
- // TODO @鑺嬭壙锛�
- @Test
- @Disabled
- public void imageCallTest() {
- validateApiKey(API_KEY);
- // 鍑嗗鍙傛暟
- ImageOptions options = DashScopeImageOptions.builder()
- .model("wan2.7-image")
-// .withSize("2k")
- .height(768).width(768)
- .n(1)
- .build();
- ImagePrompt prompt = new ImagePrompt("涓浗闀垮煄!", options);
-
- // 鏂规硶璋冪敤
- ImageResponse response = imageModel.call(prompt);
- // 鎵撳嵃缁撴灉
- System.out.println(response);
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/mcp/DouBaoMcpTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/mcp/DouBaoMcpTests.java
deleted file mode 100644
index 3efc524..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/mcp/DouBaoMcpTests.java
+++ /dev/null
@@ -1,131 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.mcp;
-
-import cn.hutool.system.SystemUtil;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.ai.chat.client.ChatClient;
-import org.springframework.ai.openai.OpenAiChatModel;
-import org.springframework.ai.openai.OpenAiChatOptions;
-import org.springframework.ai.tool.annotation.Tool;
-import org.springframework.ai.tool.method.MethodToolCallbackProvider;
-
-import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
-
-@Disabled
-public class DouBaoMcpTests {
-
- private static final String API_KEY = SystemUtil.get("DOUBAO_API_KEY",
- "sk-xxxx"); // 鎸夐渶鏀规垚浣犵殑璞嗗寘 API Key
-
- private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
- .options(OpenAiChatOptions.builder()
- .baseUrl(DouBaoChatModel.BASE_URL)
- .apiKey(API_KEY)
- .model(DouBaoChatModel.MODEL_DEFAULT) // 妯″瀷锛坉oubao锛�
- .temperature(0.7)
- .build())
- .build();
-
- private final DouBaoChatModel chatModel = new DouBaoChatModel(openAiChatModel);
-
- private final MethodToolCallbackProvider provider = MethodToolCallbackProvider.builder()
- .toolObjects(new UserService())
- .build();
-
- private final ChatClient chatClient = ChatClient.builder(chatModel)
- .defaultTools(provider)
- .build();
-
- @Test
- public void testMcpGetUserInfo() {
- validateApiKey(API_KEY);
- // 鎵撳嵃缁撴灉
- System.out.println(chatClient.prompt()
- .user("鐩墠鏈夊摢浜涘伐鍏峰彲浠ヤ娇鐢�")
- .call()
- .content());
- System.out.println("====================================");
-
- // 鎵撳嵃缁撴灉
- System.out.println(chatClient.prompt()
- .user("灏忔柊鐨勫勾榫勬槸澶氬皯")
- .call()
- .content());
- System.out.println("====================================");
-
- // 鎵撳嵃缁撴灉
- System.out.println(chatClient.prompt()
- .user("鑾峰彇灏忔柊鐨勫熀鏈俊鎭�")
- .call()
- .content());
- System.out.println("====================================");
-
- // 鎵撳嵃缁撴灉
- System.out.println(chatClient.prompt()
- .user("灏忔柊鏄粈涔堣亴涓氱殑")
- .call()
- .content());
- System.out.println("====================================");
-
- // 鎵撳嵃缁撴灉
- System.out.println(chatClient.prompt()
- .user("灏忔柊鐨勬暀鑲茶儗鏅�")
- .call()
- .content());
- System.out.println("====================================");
-
- // 鎵撳嵃缁撴灉
- System.out.println(chatClient.prompt()
- .user("灏忔柊鐨勫叴瓒g埍濂芥槸浠�涔�")
- .call()
- .content());
- System.out.println("====================================");
- }
-
-
- static class UserService {
-
- @Tool(name = "getUserAge", description = "鑾峰彇鐢ㄦ埛骞撮緞")
- public String getUserAge(String userName) {
- return "銆�" + userName + "銆嬬殑骞撮緞涓猴細18";
- }
-
- @Tool(name = "getUserSex", description = "鑾峰彇鐢ㄦ埛鎬у埆")
- public String getUserSex(String userName) {
- return "銆�" + userName + "銆嬬殑鎬у埆涓猴細鐢�";
- }
-
- @Tool(name = "getUserBasicInfo", description = "鑾峰彇鐢ㄦ埛鍩烘湰淇℃伅锛屽寘鎷鍚嶃�佸勾榫勩�佹�у埆绛�")
- public String getUserBasicInfo(String userName) {
- return "銆�" + userName + "銆嬬殑鍩烘湰淇℃伅锛歕n濮撳悕锛�" + userName + "\n骞撮緞锛�18\n鎬у埆锛氱敺\n韬珮锛�175cm\n浣撻噸锛�65kg";
- }
-
- @Tool(name = "getUserContact", description = "鑾峰彇鐢ㄦ埛鑱旂郴鏂瑰紡锛屽寘鎷數璇濄�侀偖绠辩瓑")
- public String getUserContact(String userName) {
- return "銆�" + userName + "銆嬬殑鑱旂郴鏂瑰紡锛歕n鐢佃瘽锛�138****1234\n閭锛�" + userName.toLowerCase() + "@example.com\nQQ锛�123456789";
- }
-
- @Tool(name = "getUserAddress", description = "鑾峰彇鐢ㄦ埛鍦板潃淇℃伅")
- public String getUserAddress(String userName) {
- return "銆�" + userName + "銆嬬殑鍦板潃淇℃伅锛氬寳浜競鏈濋槼鍖虹鎶�鍥尯88鍙�";
- }
-
- @Tool(name = "getUserJob", description = "鑾峰彇鐢ㄦ埛鑱屼笟淇℃伅")
- public String getUserJob(String userName) {
- return "銆�" + userName + "銆嬬殑鑱屼笟淇℃伅锛氳蒋浠跺伐绋嬪笀锛屽氨鑱屼簬ABC绉戞妧鏈夐檺鍏徃锛屽伐浣滃勾闄�5骞�";
- }
-
- @Tool(name = "getUserHobbies", description = "鑾峰彇鐢ㄦ埛鍏磋叮鐖卞ソ")
- public String getUserHobbies(String userName) {
- return "銆�" + userName + "銆嬬殑鍏磋叮鐖卞ソ锛氱紪绋嬨�侀槄璇汇�佹梾娓搞�佹憚褰便�佹墦绡悆";
- }
-
- @Tool(name = "getUserEducation", description = "鑾峰彇鐢ㄦ埛鏁欒偛鑳屾櫙")
- public String getUserEducation(String userName) {
- return "銆�" + userName + "銆嬬殑鏁欒偛鑳屾櫙锛歕n鏈锛氳绠楁満绉戝涓庢妧鏈笓涓氾紝鍖椾含澶у\n纭曞+锛氳蒋浠跺伐绋嬩笓涓氾紝娓呭崕澶у";
- }
-
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/music/SunoApiTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/music/SunoApiTests.java
deleted file mode 100644
index 895dd83..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/music/SunoApiTests.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.music;
-
-import cn.hutool.core.collection.ListUtil;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-
-import java.util.List;
-
-/**
- * {@link SunoApi} 闆嗘垚娴嬭瘯
- *
- * @author xiaoxin
- */
-public class SunoApiTests {
-
- private final SunoApi sunoApi = new SunoApi("https://suno-3tah0ycyt-status2xxs-projects.vercel.app");
-// private final SunoApi sunoApi = new SunoApi("http://127.0.0.1:3001");
-
- @Test // 鎻忚堪妯″紡
- @Disabled
- public void testGenerate() {
- // 鍑嗗鍙傛暟
- SunoApi.MusicGenerateRequest generateRequest = new SunoApi.MusicGenerateRequest(
- "happy music",
- "chirp-v3-5",
- false);
-
- // 璋冪敤鏂规硶
- List<SunoApi.MusicData> musicList = sunoApi.generate(generateRequest);
- // 鎵撳嵃缁撴灉
- System.out.println(musicList);
- }
-
- @Test // 姝岃瘝妯″紡
- @Disabled
- public void testCustomGenerate() {
- // 鍑嗗鍙傛暟
- SunoApi.MusicGenerateRequest generateRequest = new SunoApi.MusicGenerateRequest(
- "鍒涗綔涓�棣栧甫鏈夎交鏉惧悏浠栨棆寰嬬殑娴佽姝屾洸锛孾verse] 鎻忚堪澶忔棩娴锋哗鐨勫畞闈欙紝[chorus] 鑺傚鍔犲揩锛岃〃杈惧鑷敱鐨勫悜寰�銆�",
- "Happy",
- "Happy Song",
- "chirp-v3.5",
- false,
- false);
-
- // 璋冪敤鏂规硶
- List<SunoApi.MusicData> musicList = sunoApi.customGenerate(generateRequest);
- // 鎵撳嵃缁撴灉
- System.out.println(musicList);
- }
-
- @Test
- @Disabled
- public void testGenerateLyrics() {
- // 璋冪敤鏂规硶
- SunoApi.LyricsData lyricsData = sunoApi.generateLyrics("A soothing lullaby");
- // 鎵撳嵃缁撴灉
- System.out.println(lyricsData);
- }
-
- @Test
- @Disabled
- public void testGetMusicList() {
- // 鍑嗗鍙傛暟
-// String id = "d460ddda-7c87-4f34-b751-419b08a590ca";
- String id = "584729e5-0fe9-4157-86da-1b4803ff42bf";
-
- // 璋冪敤鏂规硶
- List<SunoApi.MusicData> musicList = sunoApi.getMusicList(ListUtil.of(id));
- // 鎵撳嵃缁撴灉
- System.out.println(musicList);
- }
-
- @Test
- @Disabled
- public void testGetLimitUsage() {
- // 璋冪敤鏂规硶
- SunoApi.LimitUsageData limitUsageData = sunoApi.getLimitUsage();
- // 鎵撳嵃缁撴灉
- System.out.println(limitUsageData);
- }
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/ppt/wdd/WenDuoDuoPptApiTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/ppt/wdd/WenDuoDuoPptApiTests.java
deleted file mode 100644
index e7ed65c..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/ppt/wdd/WenDuoDuoPptApiTests.java
+++ /dev/null
@@ -1,315 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.ppt.wdd;
-
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.wenduoduo.api.WenDuoDuoPptApi;
-import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import reactor.core.publisher.Flux;
-
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * {@link WenDuoDuoPptApi} 闆嗘垚娴嬭瘯
- *
- * @author xiaoxin
- */
-@Disabled
-public class WenDuoDuoPptApiTests {
-
- private final String token = ""; // API Token
- private final WenDuoDuoPptApi wenDuoDuoPptApi = new WenDuoDuoPptApi(token);
-
- @Test
- @Disabled
- public void testCreateApiToken() {
- // 鍑嗗鍙傛暟
- String apiKey = "";
- WenDuoDuoPptApi.CreateTokenRequest request = new WenDuoDuoPptApi.CreateTokenRequest(apiKey);
- // 璋冪敤鏂规硶
- String token = wenDuoDuoPptApi.createApiToken(request);
- // 鎵撳嵃缁撴灉
- System.out.println(token);
- }
-
- /**
- * 鍒涘缓浠诲姟
- */
- @Test
- @Disabled
- public void testCreateTask() {
- WenDuoDuoPptApi.ApiResponse apiResponse = wenDuoDuoPptApi.createTask(1, "dify 浠嬬粛", null);
- System.out.println(apiResponse);
- }
-
-
- @Test // 鍒涘缓澶х翰
- @Disabled
- public void testGenerateOutlineRequest() {
- WenDuoDuoPptApi.CreateOutlineRequest request = new WenDuoDuoPptApi.CreateOutlineRequest(
- "1901539019628613632", "medium", null, null, null, null);
- // 璋冪敤
- Flux<Map<String, Object>> flux = wenDuoDuoPptApi.createOutline(request);
- StringBuffer contentBuffer = new StringBuffer();
- flux.doOnNext(chunk -> {
- contentBuffer.append(chunk.get("text"));
- if (Objects.equals(Integer.parseInt(String.valueOf(chunk.get("status"))), 4)) {
- // status 涓� 4锛屾渶缁� markdown 缁撴瀯鏍�
- System.out.println(JsonUtils.toJsonString(chunk.get("result")));
- System.out.println(" ########################################################################");
- }
- }).then().block();
- // 鎵撳嵃缁撴灉
- System.out.println(contentBuffer);
- }
-
- /**
- * 淇敼澶х翰
- */
- @Test
- @Disabled
- public void testUpdateOutlineRequest() {
- WenDuoDuoPptApi.UpdateOutlineRequest request = new WenDuoDuoPptApi.UpdateOutlineRequest(
- "1901539019628613632", TEST_OUT_LINE_CONTENT, "绮剧畝涓�鐐癸紝涓変釜绔犺妭鍗冲彲");
- // 璋冪敤
- Flux<Map<String, Object>> flux = wenDuoDuoPptApi.updateOutline(request);
- StringBuffer contentBuffer = new StringBuffer();
- flux.doOnNext(chunk -> {
- contentBuffer.append(chunk.get("text"));
- if (Objects.equals(Integer.parseInt(String.valueOf(chunk.get("status"))), 4)) {
- // status 涓� 4锛屾渶缁� markdown 缁撴瀯鏍�
- System.out.println(JsonUtils.toJsonString(chunk.get("result")));
- System.out.println(" ########################################################################");
- }
- }).then().block();
- // 鎵撳嵃缁撴灉
- System.out.println(contentBuffer);
-
- }
-
- /**
- * 鑾峰彇 PPT 妯$増鍒嗛〉
- */
- @Test
- @Disabled
- public void testGetPptTemplatePage() {
- // 鍑嗗鍙傛暟
- WenDuoDuoPptApi.TemplateQueryRequest.Filter filter = new WenDuoDuoPptApi.TemplateQueryRequest.Filter(
- 1, null, null, null);
- WenDuoDuoPptApi.TemplateQueryRequest request = new WenDuoDuoPptApi.TemplateQueryRequest(1, 10, filter);
- // 璋冪敤
- WenDuoDuoPptApi.PagePptTemplateInfo pptTemplatePage = wenDuoDuoPptApi.getTemplatePage(request);
- // 鎵撳嵃缁撴灉
- System.out.println(pptTemplatePage);
- }
-
- /**
- * 鐢熸垚 PPT
- */
- @Test
- @Disabled
- public void testGeneratePptx() {
- // 鍑嗗鍙傛暟
- WenDuoDuoPptApi.PptCreateRequest request = new WenDuoDuoPptApi.PptCreateRequest("1901539019628613632", "1805081814809960448", TEST_OUT_LINE_CONTENT);
- // 璋冪敤
- WenDuoDuoPptApi.PptInfo pptInfo = wenDuoDuoPptApi.create(request);
- // 鎵撳嵃缁撴灉
- System.out.println(pptInfo);
- }
-
- private final String TEST_OUT_LINE_CONTENT = """
- # Dify锛氭柊涓�浠I搴旂敤寮�鍙戝钩鍙�
-
- ## 1 浠�涔堟槸Dify
- ### 1.1 Dify瀹氫箟锛欰I搴旂敤寮�鍙戝钩鍙�
- #### 1.1.1 浣庝唬鐮佸紑鍙�
- Dify鏄竴涓綆浠g爜AI搴旂敤寮�鍙戝钩鍙帮紝鏃ㄥ湪绠�鍖朅I搴旂敤鐨勬瀯寤鸿繃绋嬶紝璁╁紑鍙戣�呮棤闇�缂栧啓澶ч噺浠g爜鍗冲彲蹇�熷垱寤哄悇绉嶆櫤鑳藉簲鐢ㄣ��
- #### 1.1.2 鏍稿績鍔熻兘
- Dify鐨勬牳蹇冨姛鑳藉寘鎷暟鎹泦鎴愩�佹ā鍨嬮�夋嫨銆佹祦绋嬬紪鎺掑拰搴旂敤閮ㄧ讲锛屾彁渚涗竴绔欏紡瑙e喅鏂规锛屽姞閫烝I搴旂敤鐨勮惤鍦板拰杩唬銆�
- #### 1.1.3 寮�婧愪笌鍟嗕笟
- Dify鎻愪緵寮�婧愮増鏈拰鍟嗕笟鐗堟湰锛屾弧瓒充笉鍚岀敤鎴风殑闇�姹傦紝寮�婧愮増鏈�傚悎涓汉寮�鍙戣�呭拰灏忓瀷鍥㈤槦锛屽晢涓氱増鏈垯鎻愪緵鏇村己澶х殑鍔熻兘鍜屾妧鏈敮鎸併��
-
- ### 1.2 Dify瑙e喅鐨勯棶棰橈細AI寮�鍙戠棝鐐�
- #### 1.2.1 寮�鍙戝懆鏈熼暱
- 浼犵粺AI搴旂敤寮�鍙戝懆鏈熼暱锛岄渶瑕佸ぇ閲忕殑浜哄姏鍜屾椂闂存姇鍏ワ紝Dify閫氳繃鍙鍖栫晫闈㈠拰棰勭疆缁勪欢锛屽ぇ骞呯缉鐭紑鍙戝懆鏈熴��
- #### 1.2.2 鎶�鏈棬妲涢珮
- AI鎶�鏈棬妲涢珮锛岄渶瑕佷笓涓氱殑鐭ヨ瘑鍜屾妧鑳斤紝Dify闄嶄綆鎶�鏈棬妲涳紝璁╂洿澶氬紑鍙戣�呰兘澶熷弬涓庡埌AI搴旂敤鐨勫紑鍙戜腑鏉ャ��
- #### 1.2.3 閮ㄧ讲鍜岀淮鎶ゅ鏉�
- AI搴旂敤鐨勯儴缃插拰缁存姢澶嶆潅锛岄渶瑕佷笓涓氱殑杩愮淮鍥㈤槦锛孌ify鎻愪緵鑷姩鍖栫殑閮ㄧ讲鍜岀淮鎶ゅ伐鍏凤紝绠�鍖栨祦绋嬶紝闄嶄綆鎴愭湰銆�
-
- ### 1.3 Dify鍙戝睍鍘嗙▼
- #### 1.3.1 鏃╂湡鎺㈢储
- Dify鐨勬棭鏈熺増鏈富瑕佸叧娉ㄤ簬鑷劧璇█澶勭悊棰嗗煙鐨勫簲鐢紝閫氳繃闆嗘垚鍚勭NLP妯″瀷锛屾彁渚涙枃鏈垎绫汇�佹儏鎰熷垎鏋愮瓑鍔熻兘銆�
- #### 1.3.2 鍔熻兘鎵╁睍
- 闅忕潃鐢ㄦ埛闇�姹傜殑涓嶆柇澧為暱锛孌ify鐨勫姛鑳介�愭笎鎵╁睍鍒板浘鍍忚瘑鍒�佽闊宠瘑鍒瓑棰嗗煙锛屾敮鎸佹洿澶氱被鍨嬬殑AI搴旂敤銆�
- #### 1.3.3 鐢熸�佸缓璁�
- Dify绉瀬寤鸿寮�鍙戣�呯敓鎬侊紝鎻愪緵涓板瘜鐨勬枃妗c�佹暀绋嬪拰妗堜緥锛屽府鍔╁紑鍙戣�呮洿濂藉湴浣跨敤Dify骞冲彴锛屽叡鍚屾帹鍔ˋI鎶�鏈殑鍙戝睍銆�
-
- ## 2 Dify鐨勬牳蹇冨姛鑳�
- ### 2.1 鏁版嵁闆嗘垚锛氳繛鎺ュ悇绉嶆暟鎹簮
- #### 2.1.1 鏀寔澶氱鏁版嵁婧�
- Dify鏀寔杩炴帴鍚勭鏁版嵁婧愶紝鍖呮嫭鍏崇郴鍨嬫暟鎹簱銆丯oSQL鏁版嵁搴撱�佹枃浠剁郴缁熴�佷簯瀛樺偍绛夛紝婊¤冻涓嶅悓鍦烘櫙鐨勬暟鎹渶姹傘��
- #### 2.1.2 鏁版嵁杞崲鍜屾竻娲�
- Dify鎻愪緵鏁版嵁杞崲鍜屾竻娲楀姛鑳斤紝鍙互灏嗕笉鍚屾牸寮忕殑鏁版嵁杞崲涓虹粺涓�鐨勬牸寮忥紝骞跺幓闄ゆ棤鏁堟暟鎹紝鎻愰珮鏁版嵁璐ㄩ噺銆�
- #### 2.1.3 鏁版嵁瀹夊叏
- Dify娉ㄩ噸鏁版嵁瀹夊叏锛岄噰鐢ㄥ悇绉嶅畨鍏ㄦ帾鏂戒繚鎶ょ敤鎴风殑鏁版嵁锛屽寘鎷暟鎹姞瀵嗐�佽闂帶鍒躲�佹潈闄愮鐞嗙瓑銆�
-
- ### 2.2 妯″瀷閫夋嫨锛氫赴瀵岀殑AI妯″瀷搴�
- #### 2.2.1 棰勭疆妯″瀷
- Dify棰勭疆浜嗕赴瀵岀殑AI妯″瀷锛屽寘鎷嚜鐒惰瑷�澶勭悊銆佸浘鍍忚瘑鍒�佽闊宠瘑鍒瓑棰嗗煙鐨勬ā鍨嬶紝寮�鍙戣�呭彲浠ョ洿鎺ヤ娇鐢ㄨ繖浜涙ā鍨嬶紝鏃犻渶鑷璁粌锛屾瀬澶х殑绠�鍖栦簡寮�鍙戞祦绋嬨��
- #### 2.2.2 鑷畾涔夋ā鍨�
- Dify鏀寔寮�鍙戣�呬笂浼犺嚜瀹氫箟妯″瀷锛屾弧瓒充釜鎬у寲鐨勯渶姹傘�傚紑鍙戣�呭彲浠ュ皢鑷繁璁粌鐨勬ā鍨嬮儴缃插埌Dify骞冲彴涓婏紝涓庡叾浠栧紑鍙戣�呭叡浜��
- #### 2.2.3 妯″瀷璇勪及
- Dify鎻愪緵妯″瀷璇勪及鍔熻兘锛屽彲浠ュ涓嶅悓妯″瀷杩涜璇勪及锛岄�夋嫨鏈�浼樼殑妯″瀷锛屾彁楂樺簲鐢ㄦ�ц兘銆�
-
- ### 2.3 娴佺▼缂栨帓锛氬彲瑙嗗寲娴佺▼璁捐鍣�
- #### 2.3.1 鍙鍖栫晫闈�
- Dify鎻愪緵鍙鍖栫殑娴佺▼璁捐鍣紝寮�鍙戣�呭彲浠ラ�氳繃鎷栨嫿缁勪欢鐨勬柟寮忥紝璁捐AI搴旂敤鐨勬祦绋嬶紝鏃犻渶缂栧啓浠g爜锛岀畝鍗曢珮鏁堛��
- #### 2.3.2 鐏垫椿鐨勬祦绋嬫帶鍒�
- Dify鏀寔鐏垫椿鐨勬祦绋嬫帶鍒讹紝鍙互鏍规嵁涓嶅悓鐨勬潯浠舵墽琛屼笉鍚岀殑鍒嗘敮锛屽疄鐜板鏉傜殑涓氬姟閫昏緫銆�
- #### 2.3.3 瀹炴椂璋冭瘯
- Dify鎻愪緵瀹炴椂璋冭瘯鍔熻兘锛屽彲浠ュ湪璁捐娴佺▼鐨勮繃绋嬩腑锛屽疄鏃舵煡鐪嬫祦绋嬬殑鎵ц缁撴灉锛屽強鏃跺彂鐜板拰瑙e喅闂銆�
-
- ### 2.4 搴旂敤閮ㄧ讲锛氫竴閿儴缃插拰绠$悊
- #### 2.4.1 蹇�熼儴缃�
- Dify鎻愪緵涓�閿儴缃插姛鑳斤紝鍙互灏咥I搴旂敤蹇�熼儴缃插埌鍚勭鐜锛屽寘鎷湰鍦扮幆澧冦�佷簯鐜銆佸鍣ㄧ幆澧冪瓑銆�
- #### 2.4.2 鑷姩浼哥缉
- Dify鏀寔鑷姩浼哥缉锛屽彲浠ユ牴鎹簲鐢ㄧ殑璐熻浇鑷姩璋冩暣璧勬簮锛屼繚璇佸簲鐢ㄧ殑绋冲畾鎬у拰鎬ц兘銆�
- #### 2.4.3 鐩戞帶鍜屽憡璀�
- Dify鎻愪緵鐩戞帶鍜屽憡璀﹀姛鑳斤紝鍙互瀹炴椂鐩戞帶搴旂敤鐨勭姸鎬侊紝骞跺湪鍑虹幇闂鏃跺強鏃跺憡璀︼紝鏂逛究杩愮淮浜哄憳杩涜澶勭悊銆�
-
- ## 3 Dify鐨勭壒鐐瑰拰浼樺娍
- ### 3.1 浣庝唬鐮侊細闄嶄綆寮�鍙戦棬妲�
- #### 3.1.1 鍙鍖栧紑鍙�
- Dify閲囩敤鍙鍖栧紑鍙戞ā寮忥紝寮�鍙戣�呮棤闇�缂栧啓澶ч噺浠g爜锛屽彧闇�閫氳繃鎷栨嫿缁勪欢鍗冲彲瀹屾垚AI搴旂敤鐨勫紑鍙戯紝闄嶄綆浜嗗紑鍙戦棬妲涖��
- #### 3.1.2 棰勭疆缁勪欢
- Dify棰勭疆浜嗕赴瀵岀殑缁勪欢锛屽寘鎷暟鎹簮缁勪欢銆佹ā鍨嬬粍浠躲�佹祦绋嬫帶鍒剁粍浠剁瓑锛屽紑鍙戣�呭彲浠ョ洿鎺ヤ娇鐢ㄨ繖浜涚粍浠讹紝鎻愰珮寮�鍙戞晥鐜囥��
- #### 3.1.3 鍑忓皯浠g爜閲�
- Dify鍙互鏄捐憲鍑忓皯浠g爜閲忥紝闄嶄綆寮�鍙戦毦搴︼紝璁╂洿澶氬紑鍙戣�呰兘澶熷弬涓庡埌AI搴旂敤鐨勫紑鍙戜腑鏉ャ��
-
- ### 3.2 鐏垫椿锛氭弧瓒充笉鍚屽満鏅渶姹�
- #### 3.2.1 鏀寔澶氱鏁版嵁婧�
- Dify鏀寔澶氱鏁版嵁婧愶紝鍙互杩炴帴鍚勭鏁版嵁婧愶紝婊¤冻涓嶅悓鍦烘櫙鐨勬暟鎹渶姹傘��
- #### 3.2.2 鏀寔鑷畾涔夋ā鍨�
- Dify鏀寔鑷畾涔夋ā鍨嬶紝寮�鍙戣�呭彲浠ュ皢鑷繁璁粌鐨勬ā鍨嬮儴缃插埌Dify骞冲彴涓婏紝婊¤冻涓�у寲鐨勯渶姹傘��
- #### 3.2.3 鐏垫椿鐨勬祦绋嬫帶鍒�
- Dify鏀寔鐏垫椿鐨勬祦绋嬫帶鍒讹紝鍙互鏍规嵁涓嶅悓鐨勬潯浠舵墽琛屼笉鍚岀殑鍒嗘敮锛屽疄鐜板鏉傜殑涓氬姟閫昏緫銆�
-
- ### 3.3 楂樻晥锛氬姞閫熷簲鐢ㄨ惤鍦�
- #### 3.3.1 蹇�熷紑鍙�
- Dify閫氳繃鍙鍖栫晫闈㈠拰棰勭疆缁勪欢锛屽ぇ骞呯缉鐭紑鍙戝懆鏈燂紝鍔犻�烝I搴旂敤鐨勮惤鍦般��
- #### 3.3.2 蹇�熼儴缃�
- Dify鎻愪緵涓�閿儴缃插姛鑳斤紝鍙互灏咥I搴旂敤蹇�熼儴缃插埌鍚勭鐜锛屾彁楂橀儴缃叉晥鐜囥��
- #### 3.3.3 鑷姩鍖栬繍缁�
- Dify鎻愪緵鑷姩鍖栫殑杩愮淮宸ュ叿锛岀畝鍖栬繍缁存祦绋嬶紝闄嶄綆杩愮淮鎴愭湰銆�
-
- ### 3.4 寮�鏀撅細鏋勫缓绻佽崳鐢熸��
- #### 3.4.1 寮�婧愮ぞ鍖�
- Dify鎷ユ湁娲昏穬鐨勫紑婧愮ぞ鍖猴紝寮�鍙戣�呭彲浠ュ湪绀惧尯涓氦娴佺粡楠屻�佸垎浜祫婧愩�佸叡鍚屾帹鍔―ify鐨勫彂灞曘��
- #### 3.4.2 涓板瘜鐨勬枃妗�
- Dify鎻愪緵涓板瘜鐨勬枃妗c�佹暀绋嬪拰妗堜緥锛屽府鍔╁紑鍙戣�呮洿濂藉湴浣跨敤Dify骞冲彴銆�
- #### 3.4.3 API鏀寔
- Dify鎻愪緵API鏀寔锛屽紑鍙戣�呭彲浠ラ�氳繃API灏咲ify闆嗘垚鍒拌嚜宸辩殑绯荤粺涓紝鎵╁睍Dify鐨勫姛鑳姐��
-
- ## 4 Dify鐨勪娇鐢ㄥ満鏅�
- ### 4.1 鏅鸿兘瀹㈡湇锛氭彁鍗囧鎴锋湇鍔¤川閲�
- #### 4.1.1 鑷姩鍥炲
- Dify鍙互鐢ㄤ簬鏋勫缓鏅鸿兘瀹㈡湇绯荤粺锛屽疄鐜拌嚜鍔ㄥ洖澶嶅鎴风殑甯歌闂锛屾彁楂樺鎴锋湇鍔℃晥鐜囥��
- #### 4.1.2 鎯呮劅鍒嗘瀽
- Dify鍙互瀵瑰鎴风殑璇煶鎴栨枃鏈繘琛屾儏鎰熷垎鏋愶紝鍒ゆ柇瀹㈡埛鐨勬儏缁紝骞舵牴鎹儏缁彁渚涗釜鎬у寲鐨勬湇鍔°��
- #### 4.1.3 鐭ヨ瘑搴撻棶绛�
- Dify鍙互鏋勫缓鐭ヨ瘑搴撻棶绛旂郴缁燂紝璁╁鎴烽�氳繃鎻愰棶鐨勬柟寮忚幏鍙栨墍闇�鐨勪俊鎭紝鎻愰珮瀹㈡埛婊℃剰搴︺��
-
- ### 4.2 閲戣瀺椋庢帶锛氭彁楂橀闄╄瘑鍒兘鍔�
- #### 4.2.1 娆鸿瘓妫�娴�
- Dify鍙互鐢ㄤ簬鏋勫缓閲戣瀺椋庢帶绯荤粺锛屽疄鐜版璇堟娴嬶紝璇嗗埆鍙枒浜ゆ槗锛岄檷浣庨闄┿��
- #### 4.2.2 淇$敤璇勪及
- Dify鍙互瀵圭敤鎴风殑淇$敤杩涜璇勪及锛屽苟鏍规嵁璇勪及缁撴灉鎻愪緵涓嶅悓鐨勯噾铻嶆湇鍔°��
- #### 4.2.3 鍙嶆礂閽�
- Dify鍙互鐢ㄤ簬鍙嶆礂閽憋紝璇嗗埆鍙枒璧勯噾娴佸姩锛岄槻姝㈢姱缃涓恒��
-
- ### 4.3 鏅烘収鍖荤枟锛氭彁鍗囧尰鐤楁湇鍔℃按骞�
- #### 4.3.1 鐤剧梾璇婃柇
- Dify鍙互鐢ㄤ簬杈呭姪鐤剧梾璇婃柇锛屾彁楂樿瘖鏂噯纭巼锛岀缉鐭瘖鏂椂闂淬��
- #### 4.3.2 鑽墿鐮斿彂
- Dify鍙互鐢ㄤ簬鑽墿鐮斿彂锛屽姞閫熸柊鑽殑鍙戠幇鍜屽紑鍙戙��
- #### 4.3.3 鏅鸿兘鍋ュ悍绠$悊
- Dify鍙互鏋勫缓鏅鸿兘鍋ュ悍绠$悊绯荤粺锛屼负鐢ㄦ埛鎻愪緵涓�у寲鐨勫仴搴峰缓璁拰鏈嶅姟銆�
-
- ### 4.4 鏅烘収鍩庡競锛氭彁鍗囧煄甯傜鐞嗘晥鐜�
- #### 4.4.1 浜ら�氫紭鍖�
- Dify鍙互鐢ㄤ簬浜ら�氫紭鍖栵紝鎻愰珮浜ら�氭晥鐜囷紝缂撹В浜ら�氭嫢鍫点��
- #### 4.4.2 鐜鐩戞祴
- Dify鍙互鐢ㄤ簬鐜鐩戞祴锛屽疄鏃剁洃娴嬬┖姘旇川閲忋�佹按璐ㄧ瓑鐜鎸囨爣锛屽強鏃跺彂鐜板拰瑙e喅鐜闂銆�
- #### 4.4.3 鏅鸿兘瀹夐槻
- Dify鍙互鐢ㄤ簬鏅鸿兘瀹夐槻锛屾彁楂樺煄甯傚畨鍏ㄦ按骞筹紝棰勯槻鐘姜琛屼负銆�
-
- ## 5 Dify鐨勬垚鍔熸渚�
- ### 5.1 Case 1锛氭煇鐢靛晢骞冲彴鐨勬櫤鑳藉鏈�
- #### 5.1.1 椤圭洰鑳屾櫙
- 璇ョ數鍟嗗钩鍙板鎴锋湇鍔″帇鍔涘ぇ锛屼汉宸ュ鏈嶆垚鏈珮锛岄渶瑕佷竴绉嶆櫤鑳藉寲鐨勮В鍐虫柟妗堛��
- #### 5.1.2 瑙e喅鏂规
- 浣跨敤Dify鏋勫缓鏅鸿兘瀹㈡湇绯荤粺锛屽疄鐜拌嚜鍔ㄥ洖澶嶅鎴风殑甯歌闂锛屽苟鏍规嵁瀹㈡埛鐨勬儏缁彁渚涗釜鎬у寲鐨勬湇鍔°��
- #### 5.1.3 鏁堟灉
- 瀹㈡埛鏈嶅姟鏁堢巼鎻愰珮50%锛屽鎴锋弧鎰忓害鎻愰珮20%锛屼汉宸ュ鏈嶆垚鏈檷浣�30%銆�
-
- ### 5.2 Case 2锛氭煇閾惰鐨勯噾铻嶉鎺х郴缁�
- #### 5.2.1 椤圭洰鑳屾櫙
- 璇ラ摱琛岄潰涓存棩鐩婂闀跨殑閲戣瀺椋庨櫓锛岄渶瑕佷竴绉嶆洿鏈夋晥鐨勯闄╄瘑鍒拰鎺у埗鎵嬫銆�
- #### 5.2.2 瑙e喅鏂规
- 浣跨敤Dify鏋勫缓閲戣瀺椋庢帶绯荤粺锛屽疄鐜版璇堟娴嬨�佷俊鐢ㄨ瘎浼板拰鍙嶆礂閽辩瓑鍔熻兘锛屾彁楂橀闄╄瘑鍒兘鍔涖��
- #### 5.2.3 鏁堟灉
- 娆鸿瘓浜ゆ槗璇嗗埆鐜囨彁楂�40%锛屼俊鐢ㄨ瘎浼板噯纭巼鎻愰珮30%锛屾礂閽遍闄╅檷浣�25%銆�
-
- ### 5.3 Case 3锛氭煇鍖婚櫌鐨勮緟鍔╃柧鐥呰瘖鏂郴缁�
- #### 5.3.1 椤圭洰鑳屾櫙
- 璇ュ尰闄㈠尰鐢熷伐浣滃帇鍔涘ぇ锛岀柧鐥呰瘖鏂噯纭巼鏈夊緟鎻愰珮锛岄渶瑕佷竴绉嶈緟鍔╄瘖鏂伐鍏枫��
- #### 5.3.2 瑙e喅鏂规
- 浣跨敤Dify鏋勫缓杈呭姪鐤剧梾璇婃柇绯荤粺锛屾牴鎹偅鑰呯殑鐥呭巻鍜岀棁鐘讹紝鎻愪緵璇婃柇寤鸿锛屾彁楂樿瘖鏂噯纭巼銆�
- #### 5.3.3 鏁堟灉
- 鐤剧梾璇婃柇鍑嗙‘鐜囨彁楂�20%锛岃瘖鏂椂闂寸缉鐭�15%锛屽尰鐢熷伐浣滄晥鐜囨彁楂�10%銆�
-
- ## 6 Dify鐨勬湭鏉ュ睍鏈�
- ### 6.1 鎶�鏈崌绾�
- #### 6.1.1 妯″瀷浼樺寲
- Dify灏嗕笉鏂紭鍖栭缃ā鍨嬶紝鎻愰珮妯″瀷鎬ц兘锛屽苟鏀寔鏇村绫诲瀷鐨凙I妯″瀷銆�
- #### 6.1.2 娴佺▼寮曟搸鍗囩骇
- Dify灏嗗崌绾ф祦绋嬪紩鎿庯紝鎻愰珮娴佺▼鐨勭伒娲绘�у拰鍙墿灞曟�э紝鏀寔鏇村鏉傜殑涓氬姟閫昏緫銆�
- #### 6.1.3 骞冲彴鎬ц兘浼樺寲
- Dify灏嗕笉鏂紭鍖栧钩鍙版�ц兘锛屾彁楂樺钩鍙扮殑绋冲畾鎬у拰鍙潬鎬э紝婊¤冻澶ц妯″簲鐢ㄧ殑闇�姹傘��
-
- ### 6.2 鐢熸�佸缓璁�
- #### 6.2.1 绀惧尯寤鸿
- Dify灏嗙户缁姞寮哄紑婧愮ぞ鍖哄缓璁撅紝鍚稿紩鏇村寮�鍙戣�呭弬涓庯紝鍏卞悓鎺ㄥ姩Dify鐨勫彂灞曘��
- #### 6.2.2 鍚堜綔浼欎即鎷撳睍
- Dify灏嗘嫇灞曞悎浣滀紮浼达紝涓庢洿澶氱殑浼佷笟鍜屾満鏋勫悎浣滐紝鍏卞悓鎺ㄥ姩AI鎶�鏈殑搴旂敤銆�
- #### 6.2.3 搴旂敤鍟嗗簵
- Dify灏嗘瀯寤哄簲鐢ㄥ晢搴楋紝璁╁紑鍙戣�呭彲浠ュ垎浜嚜宸辩殑搴旂敤锛岀敤鎴峰彲浠ヨ喘涔板拰浣跨敤杩欎簺搴旂敤锛屾瀯寤虹箒鑽g殑鐢熸�佺郴缁熴��
-
- ### 6.3 搴旂敤棰嗗煙鎷撳睍
- #### 6.3.1 鏅鸿兘鍒堕��
- Dify灏嗘嫇灞曞埌鏅鸿兘鍒堕�犻鍩燂紝涓轰紒涓氭彁渚涙櫤鑳藉寲鐨勭敓浜х鐞嗗拰璐ㄩ噺鎺у埗瑙e喅鏂规銆�
- #### 6.3.2 鏅烘収鍐滀笟
- Dify灏嗘嫇灞曞埌鏅烘収鍐滀笟棰嗗煙锛屼负鍐滄皯鎻愪緵鏅鸿兘鍖栫殑绉嶆鍜屽吇娈栫鐞嗚В鍐虫柟妗堛��
- #### 6.3.3 鏇村棰嗗煙
- Dify灏嗘嫇灞曞埌鏇村棰嗗煙锛屼负鍚勮鍚勪笟鎻愪緵鏅鸿兘鍖栫殑瑙e喅鏂规锛屾帹鍔ㄧぞ浼氬彂灞曘��
-
- ## 7 鎬荤粨
- ### 7.1 Dify鐨勪环鍊�
- #### 7.1.1 闄嶄綆AI寮�鍙戦棬妲�
- Dify閫氳繃浣庝唬鐮佺殑鏂瑰紡锛岃鏇村寮�鍙戣�呰兘澶熷弬涓庡埌AI搴旂敤鐨勫紑鍙戜腑鏉ャ��
- #### 7.1.2 鍔犻�烝I搴旂敤钀藉湴
- Dify鎻愪緵涓�绔欏紡瑙e喅鏂规锛屽姞閫烝I搴旂敤鐨勮惤鍦板拰杩唬銆�
- #### 7.1.3 鏋勫缓绻佽崳鐨凙I鐢熸��
- Dify閫氳繃寮�婧愮ぞ鍖哄拰搴旂敤鍟嗗簵锛屾瀯寤虹箒鑽g殑AI鐢熸�佺郴缁熴��
-
- ### 7.2 鍏卞悓鍙戝睍
- #### 7.2.1 娆㈣繋鍔犲叆Dify绀惧尯
- 娆㈣繋鏇村寮�鍙戣�呭姞鍏ify绀惧尯锛屽叡鍚屾帹鍔―ify鐨勫彂灞曘��
- #### 7.2.2 鍚堜綔鍏辫耽
- 鏈熷緟涓庢洿澶氱殑浼佷笟鍜屾満鏋勫悎浣滐紝鍏卞悓鎺ㄥ姩AI鎶�鏈殑搴旂敤銆�
- #### 7.2.3 鍏卞垱鏈潵
- 璁╂垜浠竴璧风敤AI鎶�鏈敼鍙樹笘鐣岋紝鍏卞垱缇庡ソ鏈潵銆�
- """;
-
-}
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/ppt/xunfei/XunFeiPptApiTests.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/ppt/xunfei/XunFeiPptApiTests.java
deleted file mode 100644
index 13e009f..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/ppt/xunfei/XunFeiPptApiTests.java
+++ /dev/null
@@ -1,319 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.model.ppt.xunfei;
-
-import cn.hutool.core.io.FileUtil;
-import cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.api.XunFeiPptApi;
-import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-import org.springframework.mock.web.MockMultipartFile;
-import org.springframework.web.multipart.MultipartFile;
-
-import java.io.File;
-
-/**
- * {@link XunFeiPptApi} 闆嗘垚娴嬭瘯
- *
- * @author xiaoxin
- */
-public class XunFeiPptApiTests {
-
- // 璁 API 閰嶇疆淇℃伅锛屽疄闄呬娇鐢ㄦ椂璇锋浛鎹负鎮ㄧ殑搴旂敤淇℃伅
- private static final String APP_ID = "6c8ac023";
- private static final String API_SECRET = "Y2RjM2Q1MWJjZTdkYmFiODc0OGE5NmRk";
-
- private final XunFeiPptApi xunfeiPptApi = new XunFeiPptApi(APP_ID, API_SECRET);
-
- /**
- * 鑾峰彇 PPT 妯℃澘鍒楄〃
- */
- @Test
- @Disabled
- public void testGetTemplatePage() {
- // 璋冪敤鏂规硶
- XunFeiPptApi.TemplatePageResponse response = xunfeiPptApi.getTemplatePage("鍟嗗姟", 10);
- // 鎵撳嵃缁撴灉
- System.out.println("妯℃澘鍒楄〃鍝嶅簲锛�" + JsonUtils.toJsonString(response));
-
- if (response != null && response.data() != null && response.data().records() != null) {
- System.out.println("妯℃澘鎬绘暟锛�" + response.data().total());
- System.out.println("褰撳墠椤电爜锛�" + response.data().pageNum());
- System.out.println("妯℃澘鏁伴噺锛�" + response.data().records().size());
-
- // 鎵撳嵃绗竴涓ā鏉跨殑淇℃伅锛堝鏋滃瓨鍦級
- if (!response.data().records().isEmpty()) {
- XunFeiPptApi.TemplateInfo firstTemplate = response.data().records().get(0);
- System.out.println("妯℃澘ID锛�" + firstTemplate.templateIndexId());
- System.out.println("妯℃澘椋庢牸锛�" + firstTemplate.style());
- System.out.println("妯℃澘棰滆壊锛�" + firstTemplate.color());
- System.out.println("妯℃澘琛屼笟锛�" + firstTemplate.industry());
- }
- }
- }
-
- /**
- * 鍒涘缓澶х翰锛堥�氳繃鏂囨湰锛�
- */
- @Test
- @Disabled
- public void testCreateOutline() {
- XunFeiPptApi.CreateResponse response = getCreateResponse();
- // 鎵撳嵃缁撴灉
- System.out.println("鍒涘缓澶х翰鍝嶅簲锛�" + JsonUtils.toJsonString(response));
-
- // 淇濆瓨 sid 鍜� outline 鐢ㄤ簬鍚庣画娴嬭瘯
- if (response != null && response.data() != null) {
- System.out.println("sid: " + response.data().sid());
- if (response.data().outline() != null) {
- // 浣跨敤 OutlineData 鐨� toJsonString 鏂规硶
- System.out.println("outline: " + response.data().outline().toJsonString());
- // 灏� outline 瀵硅薄杞崲涓� JSON 瀛楃涓诧紝鐢ㄤ簬鍚庣画 createPptByOutline 娴嬭瘯
- String outlineJson = response.data().outline().toJsonString();
- System.out.println("鍙敤浜� createPptByOutline 鐨� outline 瀛楃涓�: " + outlineJson);
- }
- }
- }
-
- /**
- * 鍒涘缓澶х翰锛堥�氳繃鏂囨湰锛�
- *
- * @return 鍒涘缓澶х翰鍝嶅簲
- */
- private XunFeiPptApi.CreateResponse getCreateResponse() {
- String param = "鏅鸿兘浣撳钩鍙� Dify 浠嬬粛";
- return xunfeiPptApi.createOutline(param);
- }
-
- /**
- * 閫氳繃澶х翰鍒涘缓 PPT锛堝畬鏁村弬鏁帮級
- */
- @Test
- @Disabled
- public void testCreatePptByOutlineWithFullParams() {
- // 鍒涘缓澶х翰瀵硅薄
- XunFeiPptApi.CreateResponse createResponse = getCreateResponse();
- // 璋冪敤鏂规硶
- XunFeiPptApi.CreateResponse response = xunfeiPptApi.createPptByOutline(createResponse.data().outline(), "绮剧畝涓�浜涳紝涓嶈瓒呰繃6涓珷鑺�");
- // 鎵撳嵃缁撴灉
- System.out.println("閫氳繃澶х翰鍒涘缓 PPT 鍝嶅簲锛�" + JsonUtils.toJsonString(response));
-
- // 淇濆瓨sid鐢ㄤ簬鍚庣画杩涘害鏌ヨ
- if (response != null && response.data() != null) {
- System.out.println("sid: " + response.data().sid());
- if (response.data().coverImgSrc() != null) {
- System.out.println("灏侀潰鍥剧墖: " + response.data().coverImgSrc());
- }
- }
- }
-
- /**
- * 妫�鏌� PPT 鐢熸垚杩涘害
- */
- @Test
- @Disabled
- public void testCheckProgress() {
- // 鍑嗗鍙傛暟 - 浣跨敤涔嬪墠鍒涘缓 PPT 鏃惰繑鍥炵殑 sid
- String sid = "e96dac09f2ec4ee289f029a5fb874ecd"; // 鏇挎崲涓哄疄闄呯殑sid
-
- // 璋冪敤鏂规硶
- XunFeiPptApi.ProgressResponse response = xunfeiPptApi.checkProgress(sid);
- // 鎵撳嵃缁撴灉
- System.out.println("妫�鏌ヨ繘搴﹀搷搴旓細" + JsonUtils.toJsonString(response));
-
- // 瀹夊叏鍦拌闂搷搴旀暟鎹�
- if (response != null && response.data() != null) {
- XunFeiPptApi.ProgressResponseData data = response.data();
-
- // 鎵撳嵃PPT鐢熸垚鐘舵��
- System.out.println("PPT 鏋勫缓鐘舵��: " + data.pptStatus());
- System.out.println("AI 閰嶅浘鐘舵��: " + data.aiImageStatus());
- System.out.println("婕旇澶囨敞鐘舵��: " + data.cardNoteStatus());
-
- // 鎵撳嵃杩涘害淇℃伅
- if (data.totalPages() != null && data.donePages() != null) {
- System.out.println("鎬婚〉鏁�: " + data.totalPages());
- System.out.println("宸插畬鎴愰〉鏁�: " + data.donePages());
- System.out.println("瀹屾垚杩涘害: " + data.getProgressPercent() + "%");
- } else {
- System.out.println("杩涘害: " + data.process() + "%");
- }
-
- // 妫�鏌ユ槸鍚﹀畬鎴�
- if (data.isAllDone()) {
- System.out.println("PPT 鐢熸垚宸插畬鎴�!");
- System.out.println("PPT 涓嬭浇閾炬帴: " + data.pptUrl());
- }
- // 妫�鏌ユ槸鍚﹀け璐�
- else if (data.isFailed()) {
- System.out.println("PPT 鐢熸垚澶辫触!");
- System.out.println("閿欒淇℃伅: " + data.errMsg());
- }
- // 姝e湪杩涜涓�
- else {
- System.out.println("PPT 鐢熸垚涓紝璇风◢鍚庡啀鏌ヨ...");
- }
- }
- }
-
- /**
- * 杞妫�鏌� PPT 鐢熸垚杩涘害鐩村埌瀹屾垚
- */
- @Test
- @Disabled
- public void testPollCheckProgress() throws InterruptedException {
- // 鍑嗗鍙傛暟 - 浣跨敤涔嬪墠鍒涘缓 PP T鏃惰繑鍥炵殑 sid
- String sid = "1690ef6ee0344e72b5c5434f403b8eaa"; // 鏇挎崲涓哄疄闄呯殑sid
-
- // 鏈�澶ц疆璇㈡鏁�
- int maxPolls = 20;
- // 杞闂撮殧锛堟绉掞級- 璁 API 闄愭祦涓� 3 绉掍竴娆�
- long pollInterval = 3500;
-
- for (int i = 0; i < maxPolls; i++) {
- System.out.println("绗�" + (i + 1) + "娆℃煡璇㈣繘搴�...");
-
- // 璋冪敤鏂规硶
- XunFeiPptApi.ProgressResponse response = xunfeiPptApi.checkProgress(sid);
-
- // 瀹夊叏鍦拌闂搷搴旀暟鎹�
- if (response != null && response.data() != null) {
- XunFeiPptApi.ProgressResponseData data = response.data();
-
- // 鎵撳嵃杩涘害淇℃伅
- System.out.println("PPT 鏋勫缓鐘舵��: " + data.pptStatus());
- if (data.totalPages() != null && data.donePages() != null) {
- System.out.println("瀹屾垚杩涘害: " + data.donePages() + "/" + data.totalPages()
- + " (" + data.getProgressPercent() + "%)");
- }
-
- // 妫�鏌ユ槸鍚﹀畬鎴�
- if (data.isAllDone()) {
- System.out.println("PPT 鐢熸垚宸插畬鎴�!");
- System.out.println("PPT 涓嬭浇閾炬帴: " + data.pptUrl());
- break;
- }
- // 妫�鏌ユ槸鍚﹀け璐�
- else if (data.isFailed()) {
- System.out.println("PPT 鐢熸垚澶辫触!");
- System.out.println("閿欒淇℃伅: " + data.errMsg());
- break;
- }
- // 姝e湪杩涜涓紝缁х画杞
- else {
- System.out.println("PPT 鐢熸垚涓紝绛夊緟" + (pollInterval / 1000) + "绉掑悗缁х画鏌ヨ...");
- Thread.sleep(pollInterval);
- }
- } else {
- System.out.println("鏌ヨ澶辫触锛岀瓑寰�" + (pollInterval / 1000) + "绉掑悗閲嶈瘯...");
- Thread.sleep(pollInterval);
- }
- }
- }
-
- /**
- * 鐩存帴鍒涘缓 PPT锛堥�氳繃鏂囨湰锛�
- */
- @Test
- @Disabled
- public void testCreatePptByText() {
- // 鍑嗗鍙傛暟
- String query = "鍚堣偉澶╂皵瓒嬪娍鍒嗘瀽锛屽寘鎷繎5骞寸殑姘旀俯鍙樺寲銆侀檷姘撮噺鍙樺寲銆佹瀬绔ぉ姘斾簨浠讹紝浠ュ強瀵瑰煄甯傜敓娲荤殑褰卞搷";
-
- // 璋冪敤鏂规硶
- XunFeiPptApi.CreateResponse response = xunfeiPptApi.create(query);
- // 鎵撳嵃缁撴灉
- System.out.println("鐩存帴鍒涘缓 PPT 鍝嶅簲锛�" + JsonUtils.toJsonString(response));
-
- // 淇濆瓨 sid 鐢ㄤ簬鍚庣画杩涘害鏌ヨ
- if (response != null && response.data() != null) {
- System.out.println("sid: " + response.data().sid());
- if (response.data().coverImgSrc() != null) {
- System.out.println("灏侀潰鍥剧墖: " + response.data().coverImgSrc());
- }
- System.out.println("鏍囬: " + response.data().title());
- System.out.println("鍓爣棰�: " + response.data().subTitle());
- }
- }
-
- /**
- * 鐩存帴鍒涘缓 PPT锛堥�氳繃鏂囦欢锛�
- */
- @Test
- @Disabled
- public void testCreatePptByFile() {
- // 鍑嗗鍙傛暟
- File file = new File("src/test/resources/test.txt"); // 璇风‘淇濇鏂囦欢瀛樺湪
- MultipartFile multipartFile = convertFileToMultipartFile(file);
-
- // 璋冪敤鏂规硶
- XunFeiPptApi.CreateResponse response = xunfeiPptApi.create(multipartFile, file.getName());
- // 鎵撳嵃缁撴灉
- System.out.println("閫氳繃鏂囦欢鍒涘缓PPT鍝嶅簲锛�" + JsonUtils.toJsonString(response));
-
- // 淇濆瓨 sid 鐢ㄤ簬鍚庣画杩涘害鏌ヨ
- if (response != null && response.data() != null) {
- System.out.println("sid: " + response.data().sid());
- if (response.data().coverImgSrc() != null) {
- System.out.println("灏侀潰鍥剧墖: " + response.data().coverImgSrc());
- }
- System.out.println("鏍囬: " + response.data().title());
- System.out.println("鍓爣棰�: " + response.data().subTitle());
- }
- }
-
- /**
- * 鐩存帴鍒涘缓 PPT锛堝畬鏁村弬鏁帮級
- */
- @Test
- @Disabled
- public void testCreatePptWithFullParams() {
- // 鍑嗗鍙傛暟
- String query = "鍚堣偉澶╂皵瓒嬪娍鍒嗘瀽锛屽寘鎷繎 5 骞寸殑姘旀俯鍙樺寲銆侀檷姘撮噺鍙樺寲銆佹瀬绔ぉ姘斾簨浠讹紝浠ュ強瀵瑰煄甯傜敓娲荤殑褰卞搷";
-
- // 鍒涘缓璇锋眰瀵硅薄
- XunFeiPptApi.CreatePptRequest request = XunFeiPptApi.CreatePptRequest.builder()
- .query(query)
- .language("cn")
- .isCardNote(true)
- .search(true)
- .isFigure(true)
- .aiImage("advanced")
- .author("娴嬭瘯鐢ㄦ埛")
- .build();
-
- // 璋冪敤鏂规硶
- XunFeiPptApi.CreateResponse response = xunfeiPptApi.create(request);
- // 鎵撳嵃缁撴灉
- System.out.println("浣跨敤瀹屾暣鍙傛暟鍒涘缓 PPT 鍝嶅簲锛�" + JsonUtils.toJsonString(response));
-
- // 淇濆瓨 sid 鐢ㄤ簬鍚庣画杩涘害鏌ヨ
- if (response != null && response.data() != null) {
- String sid = response.data().sid();
- System.out.println("sid: " + sid);
- if (response.data().coverImgSrc() != null) {
- System.out.println("灏侀潰鍥剧墖: " + response.data().coverImgSrc());
- }
- System.out.println("鏍囬: " + response.data().title());
- System.out.println("鍓爣棰�: " + response.data().subTitle());
-
- // 绔嬪嵆鏌ヨ涓�娆¤繘搴�
- System.out.println("绔嬪嵆鏌ヨ杩涘害...");
- XunFeiPptApi.ProgressResponse progressResponse = xunfeiPptApi.checkProgress(sid);
- if (progressResponse != null && progressResponse.data() != null) {
- XunFeiPptApi.ProgressResponseData progressData = progressResponse.data();
- System.out.println("PPT 鏋勫缓鐘舵��: " + progressData.pptStatus());
- if (progressData.totalPages() != null && progressData.donePages() != null) {
- System.out.println("瀹屾垚杩涘害: " + progressData.donePages() + "/" + progressData.totalPages()
- + " (" + progressData.getProgressPercent() + "%)");
- }
- }
- }
- }
-
- /**
- * 灏� File 杞崲涓� MultipartFile
- */
- private MultipartFile convertFileToMultipartFile(File file) {
- return new MockMultipartFile("file", file.getName(), "text/plain", FileUtil.readBytes(file));
- }
-
-}
\ No newline at end of file
diff --git a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/websearch/AiBoChaWebSearchClientTest.java b/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/websearch/AiBoChaWebSearchClientTest.java
deleted file mode 100644
index e947e0f..0000000
--- a/yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/websearch/AiBoChaWebSearchClientTest.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package cn.iocoder.yudao.module.ai.framework.ai.core.websearch;
-
-import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
-import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchRequest;
-import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchResponse;
-import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.bocha.AiBoChaWebSearchClient;
-import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.Test;
-
-/**
- * {@link AiBoChaWebSearchClient} 闆嗘垚娴嬭瘯绫�
- *
- * @author 鑺嬮亾婧愮爜
- */
-public class AiBoChaWebSearchClientTest {
-
- private final AiBoChaWebSearchClient webSearchClient = new AiBoChaWebSearchClient(
- "sk-40500e52840f4d24b956d0b1d80d9abe");
-
- @Test
- @Disabled
- public void testSearch() {
- AiWebSearchRequest request = new AiWebSearchRequest()
- .setQuery("闃块噷宸村反")
- .setCount(3);
- AiWebSearchResponse response = webSearchClient.search(request);
- System.out.println(JsonUtils.toJsonPrettyString(response));
- }
-
-}
\ No newline at end of file
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/BpmOANoticeController.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/BpmOANoticeController.java
new file mode 100644
index 0000000..e3bc0be
--- /dev/null
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/BpmOANoticeController.java
@@ -0,0 +1,79 @@
+package cn.iocoder.yudao.module.bpm.controller.admin.oa;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.OaNoticePageReqVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.OaNoticeRespVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.OaNoticeSaveReqVO;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.OaNoticeDO;
+import cn.iocoder.yudao.module.bpm.service.oa.OaNoticeService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+@Tag(name = "绠$悊鍚庡彴 - OA 閫氱煡鍏憡")
+@RestController
+@RequestMapping("/bpm/oa/notice")
+@Validated
+public class BpmOANoticeController {
+
+ @Resource
+ private OaNoticeService oaNoticeService;
+
+ @PostMapping("/create")
+ @Operation(summary = "鍒涘缓閫氱煡鍏憡")
+ @PreAuthorize("@ss.hasPermission('bpm:oa-notice:create')")
+ public CommonResult<Long> createNotice(@Valid @RequestBody OaNoticeSaveReqVO createReqVO) {
+ return success(oaNoticeService.createNotice(getLoginUserId(), createReqVO));
+ }
+
+ @PutMapping("/update")
+ @Operation(summary = "淇敼閫氱煡鍏憡")
+ @PreAuthorize("@ss.hasPermission('bpm:oa-notice:update')")
+ public CommonResult<Boolean> updateNotice(@Valid @RequestBody OaNoticeSaveReqVO updateReqVO) {
+ oaNoticeService.updateNotice(updateReqVO);
+ return success(true);
+ }
+
+ @DeleteMapping("/delete")
+ @Operation(summary = "鍒犻櫎閫氱煡鍏憡")
+ @Parameter(name = "id", description = "缂栧彿", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('bpm:oa-notice:delete')")
+ public CommonResult<Boolean> deleteNotice(@RequestParam("id") Long id) {
+ oaNoticeService.deleteNotice(id);
+ return success(true);
+ }
+
+ @GetMapping("/page")
+ @Operation(summary = "鑾峰彇閫氱煡鍏憡鍒嗛〉鍒楄〃")
+ @PreAuthorize("@ss.hasPermission('bpm:oa-notice:query')")
+ public CommonResult<PageResult<OaNoticeRespVO>> getNoticePage(@Valid OaNoticePageReqVO pageReqVO) {
+ PageResult<OaNoticeDO> pageResult = oaNoticeService.getNoticePage(getLoginUserId(), pageReqVO);
+ return success(BeanUtils.toBean(pageResult, OaNoticeRespVO.class));
+ }
+
+ @GetMapping("/get")
+ @Operation(summary = "鑾峰彇閫氱煡鍏憡璇︽儏")
+ @Parameter(name = "id", description = "缂栧彿", required = true, example = "1024")
+ @PreAuthorize("@ss.hasPermission('bpm:oa-notice:query')")
+ public CommonResult<OaNoticeRespVO> getNotice(@RequestParam("id") Long id) {
+ OaNoticeDO notice = oaNoticeService.getNotice(id, getLoginUserId());
+ return success(BeanUtils.toBean(notice, OaNoticeRespVO.class));
+ }
+
+ @GetMapping("/my-unread-count")
+ @Operation(summary = "鑾峰彇鎴戠殑鏈閫氱煡鍏憡鏁伴噺")
+ public CommonResult<Long> getMyUnreadCount() {
+ return success(oaNoticeService.getUnreadCount(getLoginUserId()));
+ }
+
+}
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/OaNoticePageReqVO.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/OaNoticePageReqVO.java
new file mode 100644
index 0000000..e14a948
--- /dev/null
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/OaNoticePageReqVO.java
@@ -0,0 +1,25 @@
+package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Schema(description = "绠$悊鍚庡彴 - 閫氱煡鍏憡鍒嗛〉 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class OaNoticePageReqVO extends PageParam {
+
+ @Schema(description = "鍏憡鏍囬锛屾ā绯婂尮閰�", example = "鏀惧亣")
+ private String title;
+
+ @Schema(description = "鍏憡绫诲瀷", example = "1")
+ private Integer type;
+
+ @Schema(description = "鐘舵��", example = "1")
+ private Integer status;
+
+ @Schema(description = "鍙戦�佽寖鍥�", example = "1")
+ private Integer sendScope;
+
+}
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/OaNoticeRespVO.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/OaNoticeRespVO.java
new file mode 100644
index 0000000..e64f18a
--- /dev/null
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/OaNoticeRespVO.java
@@ -0,0 +1,52 @@
+package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Schema(description = "绠$悊鍚庡彴 - 閫氱煡鍏憡 Response VO")
+@Data
+public class OaNoticeRespVO {
+
+ @Schema(description = "閫氱煡鍏憡缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+ private Long id;
+
+ @Schema(description = "鍏憡鏍囬", requiredMode = Schema.RequiredMode.REQUIRED, example = "鍏充簬鏀惧亣鐨勯�氱煡")
+ private String title;
+
+ @Schema(description = "鍏憡鍐呭", requiredMode = Schema.RequiredMode.REQUIRED, example = "鏍规嵁鍥藉瑙勫畾...")
+ private String content;
+
+ @Schema(description = "鍏憡绫诲瀷", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Integer type;
+
+ @Schema(description = "鐘舵��", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Integer status;
+
+ @Schema(description = "鍙戦�佽寖鍥�", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ private Integer sendScope;
+
+ @Schema(description = "鏄惁缃《", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
+ private Integer isTop;
+
+ @Schema(description = "鍙戝竷浜篒D", example = "1")
+ private Long publishUserId;
+
+ @Schema(description = "鍙戝竷鏃堕棿", example = "2025-01-01 10:00:00")
+ private LocalDateTime publishTime;
+
+ @Schema(description = "鍒涘缓鏃堕棿", requiredMode = Schema.RequiredMode.REQUIRED)
+ private LocalDateTime createTime;
+
+ @Schema(description = "鎸囧畾鐢ㄦ埛ID鍒楄〃锛堜粎sendScope=2鏃舵湁鍊硷級")
+ private List<Long> userIds;
+
+ @Schema(description = "宸茶浜烘暟", example = "10")
+ private Long readCount;
+
+ @Schema(description = "褰撳墠鐢ㄦ埛鏄惁宸茶", example = "true")
+ private Boolean isRead;
+
+}
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/OaNoticeSaveReqVO.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/OaNoticeSaveReqVO.java
new file mode 100644
index 0000000..1f759d0
--- /dev/null
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/OaNoticeSaveReqVO.java
@@ -0,0 +1,45 @@
+package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import java.util.List;
+
+@Schema(description = "绠$悊鍚庡彴 - 閫氱煡鍏憡鍒涘缓/淇敼 Request VO")
+@Data
+public class OaNoticeSaveReqVO {
+
+ @Schema(description = "閫氱煡鍏憡缂栧彿", example = "1024")
+ private Long id;
+
+ @Schema(description = "鍏憡鏍囬", requiredMode = Schema.RequiredMode.REQUIRED, example = "鍏充簬鏀惧亣鐨勯�氱煡")
+ @NotBlank(message = "鍏憡鏍囬涓嶈兘涓虹┖")
+ @Size(max = 50, message = "鍏憡鏍囬涓嶈兘瓒呰繃50涓瓧绗�")
+ private String title;
+
+ @Schema(description = "鍏憡鍐呭", requiredMode = Schema.RequiredMode.REQUIRED, example = "鏍规嵁鍥藉瑙勫畾...")
+ @NotBlank(message = "鍏憡鍐呭涓嶈兘涓虹┖")
+ private String content;
+
+ @Schema(description = "鍏憡绫诲瀷", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "鍏憡绫诲瀷涓嶈兘涓虹┖")
+ private Integer type;
+
+ @Schema(description = "鐘舵��", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "鐘舵�佷笉鑳戒负绌�")
+ private Integer status;
+
+ @Schema(description = "鍙戦�佽寖鍥�", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "鍙戦�佽寖鍥翠笉鑳戒负绌�")
+ private Integer sendScope;
+
+ @Schema(description = "鏄惁缃《", example = "0")
+ private Integer isTop;
+
+ @Schema(description = "鎸囧畾鐢ㄦ埛ID鍒楄〃锛坰endScope=2鏃跺繀濉級", example = "[1, 2, 3]")
+ private List<Long> userIds;
+
+}
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/oa/OaNoticeDO.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/oa/OaNoticeDO.java
new file mode 100644
index 0000000..cf9f379
--- /dev/null
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/oa/OaNoticeDO.java
@@ -0,0 +1,73 @@
+package cn.iocoder.yudao.module.bpm.dal.dataobject.oa;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import cn.iocoder.yudao.module.bpm.enums.oa.OaNoticeSendScopeEnum;
+import cn.iocoder.yudao.module.bpm.enums.oa.OaNoticeStatusEnum;
+import cn.iocoder.yudao.module.bpm.enums.oa.OaNoticeTypeEnum;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.time.LocalDateTime;
+
+/**
+ * OA 閫氱煡鍏憡 DO
+ */
+@TableName("oa_notice")
+@KeySequence("oa_notice_seq")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class OaNoticeDO extends BaseDO {
+
+ @TableId
+ private Long id;
+
+ /**
+ * 鍏憡鏍囬
+ */
+ private String title;
+
+ /**
+ * 鍏憡鍐呭
+ */
+ private String content;
+
+ /**
+ * 鍏憡绫诲瀷
+ *
+ * 鏋氫妇 {@link OaNoticeTypeEnum}
+ */
+ private Integer type;
+
+ /**
+ * 鐘舵��
+ *
+ * 鏋氫妇 {@link OaNoticeStatusEnum}
+ */
+ private Integer status;
+
+ /**
+ * 鍙戦�佽寖鍥�
+ *
+ * 鏋氫妇 {@link OaNoticeSendScopeEnum}
+ */
+ private Integer sendScope;
+
+ /**
+ * 鏄惁缃《
+ */
+ private Integer isTop;
+
+ /**
+ * 鍙戝竷浜篒D
+ */
+ private Long publishUserId;
+
+ /**
+ * 鍙戝竷鏃堕棿
+ */
+ private LocalDateTime publishTime;
+
+}
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/oa/OaNoticeReadDO.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/oa/OaNoticeReadDO.java
new file mode 100644
index 0000000..c67784a
--- /dev/null
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/oa/OaNoticeReadDO.java
@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.bpm.dal.dataobject.oa;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.time.LocalDateTime;
+
+/**
+ * OA 閫氱煡鍏憡宸茶璁板綍 DO
+ */
+@TableName("oa_notice_read")
+@KeySequence("oa_notice_read_seq")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class OaNoticeReadDO extends BaseDO {
+
+ @TableId
+ private Long id;
+
+ /**
+ * 閫氱煡鍏憡ID
+ */
+ private Long noticeId;
+
+ /**
+ * 鐢ㄦ埛ID
+ */
+ private Long userId;
+
+ /**
+ * 闃呰鏃堕棿
+ */
+ private LocalDateTime readTime;
+
+}
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/oa/OaNoticeUserDO.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/oa/OaNoticeUserDO.java
new file mode 100644
index 0000000..d724ebe
--- /dev/null
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/oa/OaNoticeUserDO.java
@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.module.bpm.dal.dataobject.oa;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * OA 閫氱煡鍏憡鎸囧畾浜哄叧鑱� DO
+ */
+@TableName("oa_notice_user")
+@KeySequence("oa_notice_user_seq")
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class OaNoticeUserDO extends BaseDO {
+
+ @TableId
+ private Long id;
+
+ /**
+ * 閫氱煡鍏憡ID
+ */
+ private Long noticeId;
+
+ /**
+ * 鎸囧畾鐢ㄦ埛ID
+ */
+ private Long userId;
+
+}
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/oa/OaNoticeMapper.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/oa/OaNoticeMapper.java
new file mode 100644
index 0000000..f8de619
--- /dev/null
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/oa/OaNoticeMapper.java
@@ -0,0 +1,29 @@
+package cn.iocoder.yudao.module.bpm.dal.mysql.oa;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.OaNoticePageReqVO;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.OaNoticeDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.Set;
+
+@Mapper
+public interface OaNoticeMapper extends BaseMapperX<OaNoticeDO> {
+
+ default PageResult<OaNoticeDO> selectPage(OaNoticePageReqVO reqVO, Set<Long> noticeIds) {
+ LambdaQueryWrapperX<OaNoticeDO> wrapper = new LambdaQueryWrapperX<OaNoticeDO>()
+ .likeIfPresent(OaNoticeDO::getTitle, reqVO.getTitle())
+ .eqIfPresent(OaNoticeDO::getType, reqVO.getType())
+ .eqIfPresent(OaNoticeDO::getStatus, reqVO.getStatus())
+ .eqIfPresent(OaNoticeDO::getSendScope, reqVO.getSendScope())
+ .orderByDesc(OaNoticeDO::getIsTop)
+ .orderByDesc(OaNoticeDO::getId);
+ if (noticeIds != null) {
+ wrapper.in(OaNoticeDO::getId, noticeIds);
+ }
+ return selectPage(reqVO, wrapper);
+ }
+
+}
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/oa/OaNoticeReadMapper.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/oa/OaNoticeReadMapper.java
new file mode 100644
index 0000000..feee949
--- /dev/null
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/oa/OaNoticeReadMapper.java
@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.bpm.dal.mysql.oa;
+
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.OaNoticeReadDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface OaNoticeReadMapper extends BaseMapperX<OaNoticeReadDO> {
+
+ default OaNoticeReadDO selectByNoticeIdAndUserId(Long noticeId, Long userId) {
+ return selectOne(new LambdaQueryWrapperX<OaNoticeReadDO>()
+ .eq(OaNoticeReadDO::getNoticeId, noticeId)
+ .eq(OaNoticeReadDO::getUserId, userId));
+ }
+
+ default List<OaNoticeReadDO> selectListByNoticeId(Long noticeId) {
+ return selectList(new LambdaQueryWrapperX<OaNoticeReadDO>()
+ .eq(OaNoticeReadDO::getNoticeId, noticeId));
+ }
+
+ default Long selectCountByNoticeId(Long noticeId) {
+ return selectCount(new LambdaQueryWrapperX<OaNoticeReadDO>()
+ .eq(OaNoticeReadDO::getNoticeId, noticeId));
+ }
+
+ default Long selectUnreadCountByUserId(Long userId, List<Long> noticeIds) {
+ if (noticeIds == null || noticeIds.isEmpty()) {
+ return 0L;
+ }
+ // 鎬诲叕鍛婃暟 - 宸茶鏁� = 鏈鏁�
+ return (long) noticeIds.size() - selectCount(new LambdaQueryWrapperX<OaNoticeReadDO>()
+ .in(OaNoticeReadDO::getNoticeId, noticeIds)
+ .eq(OaNoticeReadDO::getUserId, userId));
+ }
+
+}
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/oa/OaNoticeUserMapper.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/oa/OaNoticeUserMapper.java
new file mode 100644
index 0000000..ddd1811
--- /dev/null
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/dal/mysql/oa/OaNoticeUserMapper.java
@@ -0,0 +1,23 @@
+package cn.iocoder.yudao.module.bpm.dal.mysql.oa;
+
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.OaNoticeUserDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface OaNoticeUserMapper extends BaseMapperX<OaNoticeUserDO> {
+
+ default List<OaNoticeUserDO> selectListByNoticeId(Long noticeId) {
+ return selectList(new LambdaQueryWrapperX<OaNoticeUserDO>()
+ .eq(OaNoticeUserDO::getNoticeId, noticeId));
+ }
+
+ default void deleteByNoticeId(Long noticeId) {
+ delete(new LambdaQueryWrapperX<OaNoticeUserDO>()
+ .eq(OaNoticeUserDO::getNoticeId, noticeId));
+ }
+
+}
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
index bfd350d..0537fe3 100644
--- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
@@ -13,6 +13,7 @@
// ========== OA 娴佺▼妯″潡 1-009-001-000 ==========
ErrorCode OA_LEAVE_NOT_EXISTS = new ErrorCode(1_009_001_001, "璇峰亣鐢宠涓嶅瓨鍦�");
+ ErrorCode OA_NOTICE_NOT_EXISTS = new ErrorCode(1_009_001_002, "閫氱煡鍏憡涓嶅瓨鍦�");
// ========== 娴佺▼妯″瀷 1-009-002-000 ==========
ErrorCode MODEL_KEY_EXISTS = new ErrorCode(1_009_002_000, "宸茬粡瀛樺湪娴佺▼鏍囪瘑涓恒�恵}銆戠殑娴佺▼");
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/oa/OaNoticeSendScopeEnum.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/oa/OaNoticeSendScopeEnum.java
new file mode 100644
index 0000000..adc9192
--- /dev/null
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/oa/OaNoticeSendScopeEnum.java
@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.bpm.enums.oa;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * OA 閫氱煡鍏憡鍙戦�佽寖鍥存灇涓�
+ */
+@Getter
+@AllArgsConstructor
+public enum OaNoticeSendScopeEnum {
+
+ ALL(1, "閫氱煡鎵�鏈変汉"),
+ SPECIFIED(2, "鎸囧畾浜�");
+
+ private final Integer scope;
+ private final String name;
+
+}
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/oa/OaNoticeStatusEnum.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/oa/OaNoticeStatusEnum.java
new file mode 100644
index 0000000..c9ceac1
--- /dev/null
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/oa/OaNoticeStatusEnum.java
@@ -0,0 +1,20 @@
+package cn.iocoder.yudao.module.bpm.enums.oa;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * OA 閫氱煡鍏憡鐘舵�佹灇涓�
+ */
+@Getter
+@AllArgsConstructor
+public enum OaNoticeStatusEnum {
+
+ DRAFT(0, "鑽夌"),
+ PUBLISHED(1, "宸插彂甯�"),
+ REVOKED(2, "宸叉挙閿�");
+
+ private final Integer status;
+ private final String name;
+
+}
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/oa/OaNoticeTypeEnum.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/oa/OaNoticeTypeEnum.java
new file mode 100644
index 0000000..5a5585f
--- /dev/null
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/oa/OaNoticeTypeEnum.java
@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.bpm.enums.oa;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * OA 閫氱煡鍏憡绫诲瀷鏋氫妇
+ */
+@Getter
+@AllArgsConstructor
+public enum OaNoticeTypeEnum {
+
+ NOTIFICATION(1, "閫氱煡"),
+ ANNOUNCEMENT(2, "鍏憡");
+
+ private final Integer type;
+ private final String name;
+
+}
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/OaNoticeService.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/OaNoticeService.java
new file mode 100644
index 0000000..2b9b5f7
--- /dev/null
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/OaNoticeService.java
@@ -0,0 +1,62 @@
+package cn.iocoder.yudao.module.bpm.service.oa;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.OaNoticePageReqVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.OaNoticeSaveReqVO;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.OaNoticeDO;
+
+/**
+ * OA 閫氱煡鍏憡 Service 鎺ュ彛
+ */
+public interface OaNoticeService {
+
+ /**
+ * 鍒涘缓閫氱煡鍏憡
+ *
+ * @param userId 褰撳墠鐢ㄦ埛ID
+ * @param createReqVO 鍒涘缓璇锋眰
+ * @return 閫氱煡鍏憡ID
+ */
+ Long createNotice(Long userId, OaNoticeSaveReqVO createReqVO);
+
+ /**
+ * 鏇存柊閫氱煡鍏憡
+ *
+ * @param updateReqVO 鏇存柊璇锋眰
+ */
+ void updateNotice(OaNoticeSaveReqVO updateReqVO);
+
+ /**
+ * 鍒犻櫎閫氱煡鍏憡
+ *
+ * @param id 缂栧彿
+ */
+ void deleteNotice(Long id);
+
+ /**
+ * 鑾峰緱閫氱煡鍏憡鍒嗛〉鍒楄〃锛堟寜鍙鎬ц繃婊わ級
+ *
+ * @param userId 褰撳墠鐢ㄦ埛ID
+ * @param reqVO 鍒嗛〉鏉′欢
+ * @return 鍒嗛〉鍒楄〃
+ */
+ PageResult<OaNoticeDO> getNoticePage(Long userId, OaNoticePageReqVO reqVO);
+
+ /**
+ * 鑾峰緱閫氱煡鍏憡璇︽儏锛堝悓鏃舵爣璁板凡璇伙級
+ *
+ * @param id 缂栧彿
+ * @param userId 褰撳墠鐢ㄦ埛ID
+ * @return 閫氱煡鍏憡
+ */
+ OaNoticeDO getNotice(Long id, Long userId);
+
+ /**
+ * 鑾峰彇褰撳墠鐢ㄦ埛鏈閫氱煡鍏憡鏁伴噺
+ *
+ * @param userId 褰撳墠鐢ㄦ埛ID
+ * @return 鏈鏁伴噺
+ */
+ Long getUnreadCount(Long userId);
+
+}
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/OaNoticeServiceImpl.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/OaNoticeServiceImpl.java
new file mode 100644
index 0000000..8769325
--- /dev/null
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/OaNoticeServiceImpl.java
@@ -0,0 +1,256 @@
+package cn.iocoder.yudao.module.bpm.service.oa;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.OaNoticePageReqVO;
+import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.OaNoticeSaveReqVO;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.OaNoticeDO;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.OaNoticeReadDO;
+import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.OaNoticeUserDO;
+import cn.iocoder.yudao.module.bpm.dal.mysql.oa.OaNoticeMapper;
+import cn.iocoder.yudao.module.bpm.dal.mysql.oa.OaNoticeReadMapper;
+import cn.iocoder.yudao.module.bpm.dal.mysql.oa.OaNoticeUserMapper;
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.module.bpm.enums.oa.OaNoticeSendScopeEnum;
+import cn.iocoder.yudao.module.bpm.enums.oa.OaNoticeStatusEnum;
+import cn.iocoder.yudao.module.system.dal.mysql.notify.NotifyMessageMapper;
+import cn.iocoder.yudao.module.system.service.notify.NotifySendService;
+import cn.iocoder.yudao.module.system.service.user.AdminUserService;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.OA_NOTICE_NOT_EXISTS;
+
+/**
+ * OA 閫氱煡鍏憡 Service 瀹炵幇绫�
+ */
+@Service
+@Slf4j
+public class OaNoticeServiceImpl implements OaNoticeService {
+
+ @Resource
+ private OaNoticeMapper oaNoticeMapper;
+
+ @Resource
+ private OaNoticeUserMapper oaNoticeUserMapper;
+
+ @Resource
+ private OaNoticeReadMapper oaNoticeReadMapper;
+
+ @Resource
+ private NotifySendService notifySendService;
+
+ @Resource
+ private AdminUserService adminUserService;
+
+ @Resource
+ private NotifyMessageMapper notifyMessageMapper;
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public Long createNotice(Long userId, OaNoticeSaveReqVO createReqVO) {
+ // 鍒涘缓閫氱煡鍏憡
+ OaNoticeDO notice = BeanUtils.toBean(createReqVO, OaNoticeDO.class);
+ notice.setPublishUserId(userId);
+ if (OaNoticeStatusEnum.PUBLISHED.getStatus().equals(createReqVO.getStatus())) {
+ notice.setPublishTime(LocalDateTime.now());
+ }
+ oaNoticeMapper.insert(notice);
+
+ // 鎸囧畾浜哄啓鍏ュ叧鑱旇〃
+ if (OaNoticeSendScopeEnum.SPECIFIED.getScope().equals(createReqVO.getSendScope())
+ && CollUtil.isNotEmpty(createReqVO.getUserIds())) {
+ saveNoticeUsers(notice.getId(), createReqVO.getUserIds());
+ }
+
+ // 宸插彂甯冨垯鍙戦�佺郴缁熼�氱煡
+ if (OaNoticeStatusEnum.PUBLISHED.getStatus().equals(notice.getStatus())) {
+ sendPublishNotify(notice);
+ }
+
+ return notice.getId();
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void updateNotice(OaNoticeSaveReqVO updateReqVO) {
+ // 鏍¢獙鏄惁瀛樺湪
+ OaNoticeDO oldNotice = validateNoticeExists(updateReqVO.getId());
+
+ // 鏇存柊閫氱煡鍏憡
+ OaNoticeDO updateObj = BeanUtils.toBean(updateReqVO, OaNoticeDO.class);
+ // 浠庢湭鍙戝竷鍙樹负宸插彂甯冩椂锛岃缃彂甯冩椂闂�
+ if (!OaNoticeStatusEnum.PUBLISHED.getStatus().equals(oldNotice.getStatus())
+ && OaNoticeStatusEnum.PUBLISHED.getStatus().equals(updateReqVO.getStatus())) {
+ updateObj.setPublishTime(LocalDateTime.now());
+ }
+ oaNoticeMapper.updateById(updateObj);
+
+ // 鏇存柊鎸囧畾浜哄叧鑱�
+ oaNoticeUserMapper.deleteByNoticeId(updateReqVO.getId());
+ if (OaNoticeSendScopeEnum.SPECIFIED.getScope().equals(updateReqVO.getSendScope())
+ && CollUtil.isNotEmpty(updateReqVO.getUserIds())) {
+ saveNoticeUsers(updateReqVO.getId(), updateReqVO.getUserIds());
+ }
+
+ // 鏂板彂甯冩椂鍙戦�佺郴缁熼�氱煡
+ if (!OaNoticeStatusEnum.PUBLISHED.getStatus().equals(oldNotice.getStatus())
+ && OaNoticeStatusEnum.PUBLISHED.getStatus().equals(updateReqVO.getStatus())) {
+ sendPublishNotify(updateObj);
+ }
+
+ // 鎾ら攢鏃跺垹闄ゅ凡鍙戦�佺殑绯荤粺閫氱煡
+ if (OaNoticeStatusEnum.REVOKED.getStatus().equals(updateReqVO.getStatus())
+ && OaNoticeStatusEnum.PUBLISHED.getStatus().equals(oldNotice.getStatus())) {
+ notifyMessageMapper.deleteByTemplateCodeAndNoticeId(
+ "oa-notice-publish", updateReqVO.getId());
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteNotice(Long id) {
+ validateNoticeExists(id);
+ oaNoticeMapper.deleteById(id);
+ oaNoticeUserMapper.deleteByNoticeId(id);
+ }
+
+ @Override
+ public PageResult<OaNoticeDO> getNoticePage(Long userId, OaNoticePageReqVO reqVO) {
+ // 鏋勫缓鍙鐨勯�氱煡ID闆嗗悎
+ Set<Long> visibleIds = buildVisibleNoticeIds(userId);
+ if (visibleIds != null && visibleIds.isEmpty()) {
+ return PageResult.empty();
+ }
+ return oaNoticeMapper.selectPage(reqVO, visibleIds);
+ }
+
+ @Override
+ public OaNoticeDO getNotice(Long id, Long userId) {
+ OaNoticeDO notice = validateNoticeExists(id);
+ // 鏍囪宸茶
+ markAsRead(id, userId);
+ return notice;
+ }
+
+ @Override
+ public Long getUnreadCount(Long userId) {
+ // 鑾峰彇鎵�鏈夊凡鍙戝竷涓斿綋鍓嶇敤鎴峰彲瑙佺殑鍏憡ID
+ Set<Long> visibleIds = buildVisibleNoticeIds(userId);
+ if (CollUtil.isEmpty(visibleIds)) {
+ return 0L;
+ }
+ List<Long> visibleIdList = new ArrayList<>(visibleIds);
+ return oaNoticeReadMapper.selectUnreadCountByUserId(userId, visibleIdList);
+ }
+
+ /**
+ * 鏋勫缓褰撳墠鐢ㄦ埛鍙鐨勯�氱煡鍏憡ID闆嗗悎
+ *
+ * @param userId 褰撳墠鐢ㄦ埛ID
+ * @return null琛ㄧず鍏ㄩ儴鍙锛岀┖闆嗗悎琛ㄧず鏃犲彲鐪�
+ */
+ private Set<Long> buildVisibleNoticeIds(Long userId) {
+ // 鎵�鏈夐�氱煡鎵�鏈変汉鍏憡ID
+ List<OaNoticeDO> allPublishedNotices = oaNoticeMapper.selectList(
+ new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<OaNoticeDO>()
+ .eq(OaNoticeDO::getSendScope, OaNoticeSendScopeEnum.ALL.getScope()));
+ Set<Long> allNoticeIds = allPublishedNotices.stream()
+ .map(OaNoticeDO::getId).collect(Collectors.toSet());
+
+ // 鎸囧畾浜鸿兘鐪嬪埌鐨勬湰鐢ㄦ埛鐩稿叧鍏憡ID
+ List<OaNoticeDO> specifiedNotices = oaNoticeMapper.selectList(
+ new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<OaNoticeDO>()
+ .eq(OaNoticeDO::getSendScope, OaNoticeSendScopeEnum.SPECIFIED.getScope()));
+ for (OaNoticeDO notice : specifiedNotices) {
+ // 鍒涘缓鑰呭彲瑙�
+ if (userId.equals(notice.getPublishUserId())) {
+ allNoticeIds.add(notice.getId());
+ continue;
+ }
+ // 琚寚瀹氱敤鎴峰彲瑙�
+ Long count = oaNoticeUserMapper.selectCount(
+ new cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX<OaNoticeUserDO>()
+ .eq(OaNoticeUserDO::getNoticeId, notice.getId())
+ .eq(OaNoticeUserDO::getUserId, userId));
+ if (count > 0) {
+ allNoticeIds.add(notice.getId());
+ }
+ }
+
+ return allNoticeIds;
+ }
+
+ /**
+ * 鏍囪宸茶
+ */
+ private void markAsRead(Long noticeId, Long userId) {
+ OaNoticeReadDO existing = oaNoticeReadMapper.selectByNoticeIdAndUserId(noticeId, userId);
+ if (existing == null) {
+ OaNoticeReadDO readRecord = new OaNoticeReadDO();
+ readRecord.setNoticeId(noticeId);
+ readRecord.setUserId(userId);
+ readRecord.setReadTime(LocalDateTime.now());
+ oaNoticeReadMapper.insert(readRecord);
+ }
+ }
+
+ /**
+ * 淇濆瓨鎸囧畾浜哄叧鑱�
+ */
+ private void saveNoticeUsers(Long noticeId, List<Long> userIds) {
+ List<OaNoticeUserDO> userList = userIds.stream().map(uid -> {
+ OaNoticeUserDO user = new OaNoticeUserDO();
+ user.setNoticeId(noticeId);
+ user.setUserId(uid);
+ return user;
+ }).collect(Collectors.toList());
+ oaNoticeUserMapper.insertBatch(userList);
+ }
+
+ /**
+ * 鍙戦�佸彂甯冮�氱煡缁欑洰鏍囩敤鎴�
+ */
+ private void sendPublishNotify(OaNoticeDO notice) {
+ Map<String, Object> templateParams = new HashMap<>();
+ templateParams.put("title", notice.getTitle());
+ templateParams.put("content", notice.getContent());
+ templateParams.put("noticeId", notice.getId());
+ List<Long> targetUserIds;
+ if (OaNoticeSendScopeEnum.ALL.getScope().equals(notice.getSendScope())) {
+ // 閫氱煡鎵�鏈変汉锛氭煡璇㈡墍鏈夊惎鐢ㄧ殑绠$悊鍛樼敤鎴�
+ targetUserIds = adminUserService.getUserListByStatus(CommonStatusEnum.ENABLE.getStatus())
+ .stream().map(cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO::getId)
+ .collect(Collectors.toList());
+ } else {
+ // 鎸囧畾浜�
+ targetUserIds = oaNoticeUserMapper.selectListByNoticeId(notice.getId()).stream()
+ .map(OaNoticeUserDO::getUserId)
+ .collect(Collectors.toList());
+ }
+ for (Long userId : targetUserIds) {
+ try {
+ notifySendService.sendSingleNotifyToAdmin(userId, "oa-notice-publish", templateParams);
+ } catch (Exception e) {
+ log.warn("鍙戦�侀�氱煡鍏憡[{}]缁欑敤鎴穂{}]澶辫触", notice.getId(), userId, e);
+ }
+ }
+ }
+
+ private OaNoticeDO validateNoticeExists(Long id) {
+ OaNoticeDO notice = oaNoticeMapper.selectById(id);
+ if (notice == null) {
+ throw exception(OA_NOTICE_NOT_EXISTS);
+ }
+ return notice;
+ }
+
+}
diff --git a/yudao-module-crm/pom.xml b/yudao-module-crm/pom.xml
index 982d2b3..5bcc084 100644
--- a/yudao-module-crm/pom.xml
+++ b/yudao-module-crm/pom.xml
@@ -99,5 +99,19 @@
<version>2026.06-jdk25-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
+
+ <!-- AI 澶фā鍨� API -->
+ <dependency>
+ <groupId>cn.iocoder.boot</groupId>
+ <artifactId>yudao-module-ai-api</artifactId>
+ <version>${revision}</version>
+ </dependency>
+
+ <!-- PDF 瑙f瀽 -->
+ <dependency>
+ <groupId>org.apache.pdfbox</groupId>
+ <artifactId>pdfbox</artifactId>
+ <version>3.0.4</version>
+ </dependency>
</dependencies>
</project>
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/ai/CrmSaleQuotationAiController.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/ai/CrmSaleQuotationAiController.java
new file mode 100644
index 0000000..e07a339
--- /dev/null
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/ai/CrmSaleQuotationAiController.java
@@ -0,0 +1,33 @@
+package cn.iocoder.yudao.module.crm.controller.admin.quotation.ai;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.module.crm.controller.admin.quotation.ai.vo.CrmSaleQuotationOcrReqVO;
+import cn.iocoder.yudao.module.crm.controller.admin.quotation.ai.vo.CrmSaleQuotationOcrRespVO;
+import cn.iocoder.yudao.module.crm.service.quotation.ai.CrmSaleQuotationAiService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "绠$悊鍚庡彴 - CRM 閿�鍞姤浠峰崟 AI OCR")
+@RestController
+@RequestMapping("/crm/sale-quotation/ai")
+@Validated
+public class CrmSaleQuotationAiController {
+
+ @Resource
+ private CrmSaleQuotationAiService saleQuotationAiService;
+
+ @PostMapping("/ocr")
+ @Operation(summary = "AI OCR 璇嗗埆鎶ヤ环鏂囦欢")
+ @PreAuthorize("@ss.hasPermission('crm:sale-quotation:create')")
+ public CommonResult<CrmSaleQuotationOcrRespVO> ocrQuotation(@Valid @RequestBody CrmSaleQuotationOcrReqVO reqVO) {
+ return success(saleQuotationAiService.ocrQuotation(reqVO));
+ }
+
+}
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/ai/vo/CrmSaleQuotationOcrReqVO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/ai/vo/CrmSaleQuotationOcrReqVO.java
new file mode 100644
index 0000000..11ff2de
--- /dev/null
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/ai/vo/CrmSaleQuotationOcrReqVO.java
@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.module.crm.controller.admin.quotation.ai.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "绠$悊鍚庡彴 - AI OCR 璇嗗埆鎶ヤ环鏂囦欢璇锋眰 VO")
+@Data
+public class CrmSaleQuotationOcrReqVO {
+
+ @Schema(description = "宸蹭笂浼犳枃浠剁殑 blob ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "blob ID 涓嶈兘涓虹┖")
+ private Long blobId;
+
+}
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/ai/vo/CrmSaleQuotationOcrRespVO.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/ai/vo/CrmSaleQuotationOcrRespVO.java
new file mode 100644
index 0000000..7999fd7
--- /dev/null
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/quotation/ai/vo/CrmSaleQuotationOcrRespVO.java
@@ -0,0 +1,58 @@
+package cn.iocoder.yudao.module.crm.controller.admin.quotation.ai.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.util.List;
+
+@Schema(description = "绠$悊鍚庡彴 - AI OCR 璇嗗埆鎶ヤ环鏂囦欢鍝嶅簲 VO")
+@Data
+public class CrmSaleQuotationOcrRespVO {
+
+ @Schema(description = "鎶ヤ环鍗曞悕绉�", example = "XX鍏徃鎶ヤ环鍗�")
+ private String name;
+
+ @Schema(description = "瀹㈡埛鍚嶇О", example = "XX绉戞妧鏈夐檺鍏徃")
+ private String customerName;
+
+ @Schema(description = "鎶ヤ环鏃ユ湡", example = "2026-07-31")
+ private LocalDate quotationTime;
+
+ @Schema(description = "鏈夋晥鏈熻嚦", example = "2026-08-30")
+ private LocalDate validUntil;
+
+ @Schema(description = "绋庣巼锛堢櫨鍒嗘瘮锛�", example = "13.0")
+ private BigDecimal taxRate;
+
+ @Schema(description = "鎶樻墸鐜囷紙鐧惧垎姣旓級", example = "5.0")
+ private BigDecimal discountPercent;
+
+ @Schema(description = "澶囨敞", example = "鍚◣鍚繍璐�")
+ private String remark;
+
+ @Schema(description = "璇嗗埆鍑虹殑鐗╂枡鏄庣粏")
+ private List<Item> items;
+
+ @Schema(description = "鍘熷璇嗗埆鏂囨湰锛堝墠绔彲閫夊睍绀猴級")
+ private String rawText;
+
+ @Schema(description = "鐗╂枡鏄庣粏椤�")
+ @Data
+ public static class Item {
+
+ @Schema(description = "鐗╂枡鍚嶇О", example = "浼烘湇鐢垫満")
+ private String itemName;
+
+ @Schema(description = "鐗╂枡瑙勬牸", example = "SV-1000W")
+ private String itemSpec;
+
+ @Schema(description = "鏁伴噺", example = "10")
+ private BigDecimal count;
+
+ @Schema(description = "鍗曚环", example = "1500.00")
+ private BigDecimal quotationPrice;
+ }
+
+}
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/ai/CrmSaleQuotationAiService.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/ai/CrmSaleQuotationAiService.java
new file mode 100644
index 0000000..7decd24
--- /dev/null
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/ai/CrmSaleQuotationAiService.java
@@ -0,0 +1,10 @@
+package cn.iocoder.yudao.module.crm.service.quotation.ai;
+
+import cn.iocoder.yudao.module.crm.controller.admin.quotation.ai.vo.CrmSaleQuotationOcrReqVO;
+import cn.iocoder.yudao.module.crm.controller.admin.quotation.ai.vo.CrmSaleQuotationOcrRespVO;
+
+public interface CrmSaleQuotationAiService {
+
+ CrmSaleQuotationOcrRespVO ocrQuotation(CrmSaleQuotationOcrReqVO reqVO);
+
+}
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/ai/CrmSaleQuotationAiServiceImpl.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/ai/CrmSaleQuotationAiServiceImpl.java
new file mode 100644
index 0000000..b82c22d
--- /dev/null
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/ai/CrmSaleQuotationAiServiceImpl.java
@@ -0,0 +1,281 @@
+package cn.iocoder.yudao.module.crm.service.quotation.ai;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import cn.iocoder.yudao.module.ai.api.chat.AiChatApi;
+import cn.iocoder.yudao.module.crm.controller.admin.quotation.ai.vo.CrmSaleQuotationOcrReqVO;
+import cn.iocoder.yudao.module.crm.controller.admin.quotation.ai.vo.CrmSaleQuotationOcrRespVO;
+import cn.iocoder.yudao.module.system.dal.dataobject.storage.SystemStorageBlobDO;
+import cn.iocoder.yudao.module.system.service.storage.SystemStorageBlobService;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.pdfbox.Loader;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.text.PDFTextStripper;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.xwpf.extractor.XWPFWordExtractor;
+import org.apache.poi.xwpf.usermodel.XWPFDocument;
+import org.springframework.stereotype.Service;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.nio.file.Files;
+import java.time.LocalDate;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+public class CrmSaleQuotationAiServiceImpl implements CrmSaleQuotationAiService {
+
+ private static final Set<String> SUPPORTED_EXTENSIONS = Set.of(
+ "pdf", "doc", "docx", "xls", "xlsx", "png", "jpg", "jpeg", "gif", "bmp", "txt", "csv"
+ );
+
+ private static final Set<String> IMAGE_EXTENSIONS = Set.of("png", "jpg", "jpeg", "gif", "bmp");
+
+ private static final String SYSTEM_PROMPT = """
+ 浣犳槸涓�浣嶄笓涓氱殑閿�鍞崟鎹暟鎹彁鍙栦笓瀹躲�備綘闇�瑕佷粠鎶ヤ环鏂囦欢涓彁鍙栫粨鏋勫寲淇℃伅銆�
+
+ ## 鎻愬彇瑙勫垯
+ 1. 浠旂粏璇嗗埆鏂囨。涓殑鎶ヤ环鍗曚俊鎭細瀹㈡埛鍚嶇О銆佹姤浠锋棩鏈熴�佹湁鏁堟湡銆佺墿鏂欐槑缁嗙瓑
+ 2. 瀵逛簬鐗╂枡鏄庣粏锛屾瘡涓�琛屾彁鍙栵細鐗╂枡鍚嶇О銆佽鏍煎瀷鍙枫�佹暟閲忋�佸崟浠�
+ 3. 绋庣巼銆佹姌鎵g巼绛夊鏂囦欢涓湭鏄庣‘鏍囨敞锛屼笉濉�
+ 4. 鏃ユ湡缁熶竴杞崲涓� yyyy-MM-dd 鏍煎紡
+ 5. 鏁板瓧瀛楁浠呬繚鐣欐暟瀛楋紝鍘绘帀鍗曚綅
+
+ ## 杈撳嚭鏍煎紡
+ 璇锋寜浠ヤ笅JSON鏍煎紡杩斿洖锛堜笉瑕佸寘鍚叾浠栧唴瀹癸級锛�
+ {"name": "鎶ヤ环鍗曞悕绉�", "customerName": "瀹㈡埛鍚嶇О", "quotationTime": "2026-01-01", "validUntil": "2026-12-31", "taxRate": 13.0, "discountPercent": 5.0, "remark": "澶囨敞淇℃伅", "items": [{"itemName": "鐗╂枡鍚嶇О", "itemSpec": "瑙勬牸鍨嬪彿", "count": 10.0, "quotationPrice": 100.0}]}
+
+ 鎵�鏈夊瓧娈靛潎涓哄彲閫夛紝鏈彁鍙栧埌鐨勫瓧娈典笉瑕佽繑鍥炪��
+ 濡傛灉鏂囦欢鍐呭鏃犳硶璇嗗埆涓烘姤浠峰崟锛岃繑鍥炵┖JSON锛歿}
+ """;
+
+ private static final String SYSTEM_PROMPT_IMAGE = """
+ 浣犳槸涓�浣嶄笓涓氱殑閿�鍞崟鎹甇CR璇嗗埆涓撳銆備綘闇�瑕佷粠鎶ヤ环鏂囦欢鍥剧墖涓彁鍙栫粨鏋勫寲淇℃伅銆�
+
+ ## 鎻愬彇瑙勫垯
+ 1. 浠旂粏璇嗗埆鍥剧墖涓殑鏂囧瓧淇℃伅
+ 2. 鎻愬彇鎶ヤ环鍗曠浉鍏充俊鎭細瀹㈡埛鍚嶇О銆佹姤浠锋棩鏈熴�佹湁鏁堟湡銆佺墿鏂欐槑缁嗙瓑
+ 3. 瀵逛簬鐗╂枡鏄庣粏锛屾瘡涓�琛屾彁鍙栵細鐗╂枡鍚嶇О銆佽鏍煎瀷鍙枫�佹暟閲忋�佸崟浠�
+ 4. 绋庣巼銆佹姌鎵g巼绛夊鍥剧墖涓湭鏄庣‘鏍囨敞锛屼笉濉�
+ 5. 鏃ユ湡缁熶竴杞崲涓� yyyy-MM-dd 鏍煎紡
+
+ ## 杈撳嚭鏍煎紡
+ 璇锋寜浠ヤ笅JSON鏍煎紡杩斿洖锛堜笉瑕佸寘鍚叾浠栧唴瀹癸級锛�
+ {"name": "鎶ヤ环鍗曞悕绉�", "customerName": "瀹㈡埛鍚嶇О", "quotationTime": "2026-01-01", "validUntil": "2026-12-31", "taxRate": 13.0, "discountPercent": 5.0, "remark": "澶囨敞淇℃伅", "items": [{"itemName": "鐗╂枡鍚嶇О", "itemSpec": "瑙勬牸鍨嬪彿", "count": 10.0, "quotationPrice": 100.0}]}
+
+ 鎵�鏈夊瓧娈靛潎涓哄彲閫夛紝鏈彁鍙栧埌鐨勫瓧娈典笉瑕佽繑鍥炪��
+ 濡傛灉鍥剧墖鍐呭鏃犳硶璇嗗埆涓烘姤浠峰崟锛岃繑鍥炵┖JSON锛歿}
+ """;
+
+ @Resource
+ private SystemStorageBlobService blobService;
+ @Resource
+ private AiChatApi aiChatApi;
+
+ @Override
+ public CrmSaleQuotationOcrRespVO ocrQuotation(CrmSaleQuotationOcrReqVO reqVO) {
+ File tempFile = null;
+ try {
+ // 1. 涓嬭浇鏂囦欢
+ SystemStorageBlobDO blob = blobService.getStorageBlob(reqVO.getBlobId());
+ if (blob == null) {
+ CrmSaleQuotationOcrRespVO error = new CrmSaleQuotationOcrRespVO();
+ error.setRawText("鏂囦欢涓嶅瓨鍦�");
+ return error;
+ }
+
+ // 2. 鏍¢獙鏂囦欢绫诲瀷
+ String ext = FileUtil.extName(blob.getOriginalFilename()).toLowerCase();
+ if (!SUPPORTED_EXTENSIONS.contains(ext)) {
+ CrmSaleQuotationOcrRespVO error = new CrmSaleQuotationOcrRespVO();
+ error.setRawText("涓嶆敮鎸佺殑鏂囦欢绫诲瀷锛岃涓婁紶 PDF/Word/Excel/鍥剧墖鏂囦欢");
+ return error;
+ }
+
+ // 3. 鑾峰彇鏂囦欢瀛楄妭
+ tempFile = blobService.getPublicFile(blob.getUidFilename(), blob.getResourceKey());
+ if (tempFile == null || !tempFile.exists()) {
+ CrmSaleQuotationOcrRespVO error = new CrmSaleQuotationOcrRespVO();
+ error.setRawText("鏂囦欢璇诲彇澶辫触");
+ return error;
+ }
+ byte[] fileBytes = Files.readAllBytes(tempFile.toPath());
+
+ // 4. AI 璇嗗埆锛堝浘鐗囪蛋澶氭ā鎬侊紝鏂囨湰璧扮函鏂囨湰锛�
+ boolean isImage = IMAGE_EXTENSIONS.contains(ext);
+ String systemPrompt = isImage ? SYSTEM_PROMPT_IMAGE : SYSTEM_PROMPT;
+ String aiResponse;
+ if (isImage) {
+ String imageBase64 = Base64.getEncoder().encodeToString(fileBytes);
+ String mimeType = getMimeType(ext);
+ aiResponse = aiChatApi.chatWithImage(systemPrompt, "璇疯瘑鍒浘鐗囦腑鐨勬姤浠蜂俊鎭�", imageBase64, mimeType);
+ } else {
+ String extractedText = extractTextByType(ext, fileBytes);
+ if (StrUtil.isEmpty(extractedText)) {
+ CrmSaleQuotationOcrRespVO error = new CrmSaleQuotationOcrRespVO();
+ error.setRawText("鏂囦欢鍐呭涓虹┖锛屾棤娉曡瘑鍒�");
+ return error;
+ }
+ aiResponse = aiChatApi.chat(systemPrompt, extractedText);
+ }
+ log.info("AI OCR 璇嗗埆缁撴灉: blobId={}, file={}, response={}",
+ reqVO.getBlobId(), blob.getOriginalFilename(), aiResponse);
+
+ // 5. 瑙f瀽鍝嶅簲
+ CrmSaleQuotationOcrRespVO result = parseOcrResponse(aiResponse);
+ return result;
+
+ } catch (Exception e) {
+ log.warn("AI OCR 璇嗗埆澶辫触锛宐lobId={}", reqVO.getBlobId(), e);
+ CrmSaleQuotationOcrRespVO fallback = new CrmSaleQuotationOcrRespVO();
+ fallback.setRawText("AI 璇嗗埆鏆傛椂涓嶅彲鐢紝璇锋墜鍔ㄥ綍鍏�");
+ return fallback;
+ } finally {
+ if (tempFile != null && tempFile.exists()) {
+ tempFile.delete();
+ }
+ }
+ }
+
+ private String extractTextByType(String ext, byte[] bytes) throws IOException {
+ return switch (ext) {
+ case "pdf" -> extractPdfText(bytes);
+ case "docx", "doc" -> extractDocxText(bytes);
+ case "xlsx", "xls" -> extractExcelText(bytes);
+ case "txt", "csv" -> new String(bytes, java.nio.charset.StandardCharsets.UTF_8);
+ default -> "";
+ };
+ }
+
+ private String extractPdfText(byte[] bytes) throws IOException {
+ try (PDDocument doc = Loader.loadPDF(bytes)) {
+ PDFTextStripper stripper = new PDFTextStripper();
+ stripper.setSortByPosition(true);
+ String text = stripper.getText(doc);
+ return StrUtil.isNotBlank(text) ? text.trim() : "";
+ }
+ }
+
+ private String extractDocxText(byte[] bytes) throws IOException {
+ try (XWPFDocument doc = new XWPFDocument(new ByteArrayInputStream(bytes))) {
+ try (XWPFWordExtractor extractor = new XWPFWordExtractor(doc)) {
+ String text = extractor.getText();
+ return StrUtil.isNotBlank(text) ? text.trim() : "";
+ }
+ }
+ }
+
+ private String extractExcelText(byte[] bytes) throws IOException {
+ StringBuilder sb = new StringBuilder();
+ try (Workbook wb = WorkbookFactory.create(new ByteArrayInputStream(bytes))) {
+ for (int i = 0; i < wb.getNumberOfSheets(); i++) {
+ Sheet sheet = wb.getSheetAt(i);
+ if (i > 0) sb.append("\n--- Sheet: ").append(sheet.getSheetName()).append(" ---\n");
+ for (Row row : sheet) {
+ List<String> cellValues = new ArrayList<>();
+ for (Cell cell : row) {
+ cellValues.add(getCellStringValue(cell));
+ }
+ sb.append(String.join("\t", cellValues)).append("\n");
+ }
+ }
+ }
+ return sb.toString().trim();
+ }
+
+ private String getCellStringValue(Cell cell) {
+ return switch (cell.getCellType()) {
+ case STRING -> cell.getStringCellValue().trim();
+ case NUMERIC -> {
+ if (DateUtil.isCellDateFormatted(cell)) {
+ yield cell.getLocalDateTimeCellValue().toLocalDate().toString();
+ }
+ double v = cell.getNumericCellValue();
+ if (v == Math.floor(v) && !Double.isInfinite(v)) {
+ yield String.valueOf((long) v);
+ }
+ yield String.valueOf(v);
+ }
+ case BOOLEAN -> String.valueOf(cell.getBooleanCellValue());
+ case FORMULA -> {
+ try {
+ yield String.valueOf(cell.getNumericCellValue());
+ } catch (Exception e) {
+ yield cell.getStringCellValue().trim();
+ }
+ }
+ default -> "";
+ };
+ }
+
+ private String getMimeType(String ext) {
+ return switch (ext) {
+ case "jpg", "jpeg" -> "image/jpeg";
+ case "gif" -> "image/gif";
+ case "bmp" -> "image/bmp";
+ default -> "image/png";
+ };
+ }
+
+ private CrmSaleQuotationOcrRespVO parseOcrResponse(String aiResponse) {
+ CrmSaleQuotationOcrRespVO respVO = new CrmSaleQuotationOcrRespVO();
+ try {
+ String json = aiResponse;
+ int start = json.indexOf("{");
+ int end = json.lastIndexOf("}");
+ if (start >= 0 && end > start) {
+ json = json.substring(start, end + 1);
+ }
+ Map<String, Object> parsed = JSONUtil.toBean(json, Map.class);
+
+ respVO.setName((String) parsed.get("name"));
+ respVO.setCustomerName((String) parsed.get("customerName"));
+ respVO.setRemark((String) parsed.get("remark"));
+
+ Object qt = parsed.get("quotationTime");
+ if (qt instanceof String s && StrUtil.isNotBlank(s)) {
+ try { respVO.setQuotationTime(LocalDate.parse(s)); } catch (Exception ignored) {}
+ }
+ Object vu = parsed.get("validUntil");
+ if (vu instanceof String s && StrUtil.isNotBlank(s)) {
+ try { respVO.setValidUntil(LocalDate.parse(s)); } catch (Exception ignored) {}
+ }
+ Object tr = parsed.get("taxRate");
+ if (tr instanceof Number n) respVO.setTaxRate(BigDecimal.valueOf(n.doubleValue()));
+ Object dp = parsed.get("discountPercent");
+ if (dp instanceof Number n) respVO.setDiscountPercent(BigDecimal.valueOf(n.doubleValue()));
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> items = (List<Map<String, Object>>) parsed.get("items");
+ if (CollUtil.isNotEmpty(items)) {
+ respVO.setItems(items.stream().map(m -> {
+ CrmSaleQuotationOcrRespVO.Item item = new CrmSaleQuotationOcrRespVO.Item();
+ item.setItemName((String) m.get("itemName"));
+ item.setItemSpec((String) m.get("itemSpec"));
+ Object count = m.get("count");
+ if (count instanceof Number n) item.setCount(BigDecimal.valueOf(n.doubleValue()));
+ Object price = m.get("quotationPrice");
+ if (price instanceof Number n) item.setQuotationPrice(BigDecimal.valueOf(n.doubleValue()));
+ return item;
+ }).collect(Collectors.toList()));
+ }
+ } catch (Exception e) {
+ log.warn("瑙f瀽 AI OCR 鍝嶅簲澶辫触: {}", aiResponse, e);
+ respVO.setRawText(aiResponse);
+ }
+ return respVO;
+ }
+
+ private String truncate(String text, int maxLen) {
+ if (text == null) return "";
+ return text.length() > maxLen ? text.substring(0, maxLen) + "..." : text;
+ }
+
+}
diff --git a/yudao-module-mes/pom.xml b/yudao-module-mes/pom.xml
index c55c90d..8cf5b36 100644
--- a/yudao-module-mes/pom.xml
+++ b/yudao-module-mes/pom.xml
@@ -66,6 +66,13 @@
<version>${revision}</version>
</dependency>
+ <!-- AI 澶фā鍨� API -->
+ <dependency>
+ <groupId>cn.iocoder.boot</groupId>
+ <artifactId>yudao-module-ai-api</artifactId>
+ <version>${revision}</version>
+ </dependency>
+
<!-- 涓氬姟缁勪欢 -->
<!-- Web 鐩稿叧 -->
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/MesProAiController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/MesProAiController.java
new file mode 100644
index 0000000..2377623
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/MesProAiController.java
@@ -0,0 +1,51 @@
+package cn.iocoder.yudao.module.mes.controller.admin.pro.ai;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.ai.vo.*;
+import cn.iocoder.yudao.module.mes.service.pro.ai.MesProAiService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "绠$悊鍚庡彴 - MES 鐢熶骇 AI 杈呭姪鍒嗘瀽")
+@RestController
+@RequestMapping("/mes/pro/ai")
+public class MesProAiController {
+
+ @Resource
+ private MesProAiService proAiService;
+
+ @PostMapping("/predict-material")
+ @Operation(summary = "AI 鐗╂枡鐭己棰勬祴")
+ @PreAuthorize("@ss.hasPermission('mes:pro-work-order:query')")
+ public CommonResult<MesProAiMaterialPredictRespVO> predictMaterial(@Valid @RequestBody MesProAiMaterialPredictReqVO reqVO) {
+ return success(proAiService.predictMaterial(reqVO));
+ }
+
+ @PostMapping("/predict-duration")
+ @Operation(summary = "AI 鐢熶骇璁″垝鏃堕暱棰勬祴")
+ @PreAuthorize("@ss.hasPermission('mes:pro-work-order:query')")
+ public CommonResult<MesProAiDurationPredictRespVO> predictDuration(@Valid @RequestBody MesProAiDurationPredictReqVO reqVO) {
+ return success(proAiService.predictDuration(reqVO));
+ }
+
+ @PostMapping("/predict-risk")
+ @Operation(summary = "AI 鐢熶骇椋庨櫓棰勬祴")
+ @PreAuthorize("@ss.hasPermission('mes:pro-work-order:query')")
+ public CommonResult<MesProAiRiskPredictRespVO> predictRisk(@Valid @RequestBody MesProAiRiskPredictReqVO reqVO) {
+ return success(proAiService.predictRisk(reqVO));
+ }
+
+ @PostMapping("/predict-delivery")
+ @Operation(summary = "AI 鏄惁鑳芥寜鏃朵氦浠�")
+ @PreAuthorize("@ss.hasPermission('mes:pro-work-order:query')")
+ public CommonResult<MesProAiDeliveryPredictRespVO> predictDelivery(@Valid @RequestBody MesProAiDeliveryPredictReqVO reqVO) {
+ return success(proAiService.predictDelivery(reqVO));
+ }
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiDeliveryPredictReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiDeliveryPredictReqVO.java
new file mode 100644
index 0000000..79bcffe
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiDeliveryPredictReqVO.java
@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.module.mes.controller.admin.pro.ai.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "绠$悊鍚庡彴 - AI 鎸夋椂浜や粯棰勬祴璇锋眰 VO")
+@Data
+public class MesProAiDeliveryPredictReqVO {
+
+ @Schema(description = "宸ュ崟 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "宸ュ崟 ID 涓嶈兘涓虹┖")
+ private Long workOrderId;
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiDeliveryPredictRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiDeliveryPredictRespVO.java
new file mode 100644
index 0000000..31459c7
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiDeliveryPredictRespVO.java
@@ -0,0 +1,31 @@
+package cn.iocoder.yudao.module.mes.controller.admin.pro.ai.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+@Schema(description = "绠$悊鍚庡彴 - AI 鎸夋椂浜や粯棰勬祴鍝嶅簲 VO")
+@Data
+public class MesProAiDeliveryPredictRespVO {
+
+ @Schema(description = "鏄惁鑳芥寜鏃朵氦浠�", example = "true")
+ private Boolean onTime;
+
+ @Schema(description = "缃俊搴︼紙0-100锛�", example = "85.5")
+ private Double confidence;
+
+ @Schema(description = "AI 鎺ㄧ悊鍒嗘瀽")
+ private String reasoning;
+
+ @Schema(description = "寤舵湡鍥犵礌")
+ private List<String> delayFactors;
+
+ @Schema(description = "鐢熶骇杩涘害鐧惧垎姣�", example = "60.0")
+ private BigDecimal progressPercent;
+
+ @Schema(description = "璺濈闇�姹傛棩鏈熷墿浣欏ぉ鏁�", example = "10")
+ private Integer remainingDays;
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiDurationPredictReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiDurationPredictReqVO.java
new file mode 100644
index 0000000..4de3df9
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiDurationPredictReqVO.java
@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.module.mes.controller.admin.pro.ai.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "绠$悊鍚庡彴 - AI 鐢熶骇鏃堕暱棰勬祴璇锋眰 VO")
+@Data
+public class MesProAiDurationPredictReqVO {
+
+ @Schema(description = "宸ュ崟 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "宸ュ崟 ID 涓嶈兘涓虹┖")
+ private Long workOrderId;
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiDurationPredictRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiDurationPredictRespVO.java
new file mode 100644
index 0000000..4f019ef
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiDurationPredictRespVO.java
@@ -0,0 +1,51 @@
+package cn.iocoder.yudao.module.mes.controller.admin.pro.ai.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+@Schema(description = "绠$悊鍚庡彴 - AI 鐢熶骇鏃堕暱棰勬祴鍝嶅簲 VO")
+@Data
+public class MesProAiDurationPredictRespVO {
+
+ @Schema(description = "棰勬祴鎬绘椂闀匡紙灏忔椂锛�", example = "48.5")
+ private BigDecimal predictedTotalHours;
+
+ @Schema(description = "鏍囧噯鎬绘椂闀匡紙灏忔椂锛�", example = "40.0")
+ private BigDecimal standardTotalHours;
+
+ @Schema(description = "AI 鎺ㄧ悊鍒嗘瀽")
+ private String reasoning;
+
+ @Schema(description = "鍏抽敭鍏虫敞鐐�")
+ private List<String> keyPoints;
+
+ @Schema(description = "鍚勫伐搴忔椂闀挎槑缁�")
+ private List<ProcessTimeBreakdown> processBreakdown;
+
+ @Schema(description = "宸ュ簭鏃堕暱鏄庣粏")
+ @Data
+ public static class ProcessTimeBreakdown {
+
+ @Schema(description = "宸ュ簭鍚嶇О", example = "杞﹀墛")
+ private String processName;
+
+ @Schema(description = "宸ュ簭椤哄簭", example = "1")
+ private Integer sort;
+
+ @Schema(description = "鏍囧噯鍑嗗鏃堕棿锛堝垎閽燂級", example = "30")
+ private Integer standardPrepareTime;
+
+ @Schema(description = "鏍囧噯绛夊緟鏃堕棿锛堝垎閽燂級", example = "15")
+ private Integer standardWaitTime;
+
+ @Schema(description = "鏍囧噯鐢熶骇鏃堕棿锛堝皬鏃讹級", example = "8.0")
+ private BigDecimal standardProductionTime;
+
+ @Schema(description = "棰勬祴鐢熶骇鏃堕棿锛堝皬鏃讹級", example = "10.5")
+ private BigDecimal predictedProductionTime;
+ }
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiMaterialPredictReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiMaterialPredictReqVO.java
new file mode 100644
index 0000000..201eef5
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiMaterialPredictReqVO.java
@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.module.mes.controller.admin.pro.ai.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "绠$悊鍚庡彴 - AI 鐗╂枡鐭己棰勬祴璇锋眰 VO")
+@Data
+public class MesProAiMaterialPredictReqVO {
+
+ @Schema(description = "宸ュ崟 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "宸ュ崟 ID 涓嶈兘涓虹┖")
+ private Long workOrderId;
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiMaterialPredictRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiMaterialPredictRespVO.java
new file mode 100644
index 0000000..64eab5f
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiMaterialPredictRespVO.java
@@ -0,0 +1,48 @@
+package cn.iocoder.yudao.module.mes.controller.admin.pro.ai.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+@Schema(description = "绠$悊鍚庡彴 - AI 鐗╂枡鐭己棰勬祴鍝嶅簲 VO")
+@Data
+public class MesProAiMaterialPredictRespVO {
+
+ @Schema(description = "鏁翠綋椋庨櫓绛夌骇锛�1=浣庨闄� 2=涓闄� 3=楂橀闄�", example = "2")
+ private Integer riskLevel;
+
+ @Schema(description = "AI 缁煎悎鍒嗘瀽")
+ private String reasoning;
+
+ @Schema(description = "鐗╂枡椋庨櫓鏄庣粏")
+ private List<RiskItem> riskItems;
+
+ @Schema(description = "鐗╂枡椋庨櫓椤�")
+ @Data
+ public static class RiskItem {
+
+ @Schema(description = "鐗╂枡缂栫爜", example = "MAT-001")
+ private String itemCode;
+
+ @Schema(description = "鐗╂枡鍚嶇О", example = "铻轰笣")
+ private String itemName;
+
+ @Schema(description = "闇�姹傞噺", example = "1000")
+ private BigDecimal requiredQuantity;
+
+ @Schema(description = "搴撳瓨鍙敤閲�", example = "500")
+ private BigDecimal availableQuantity;
+
+ @Schema(description = "鐭己鏁伴噺", example = "500")
+ private BigDecimal shortageQuantity;
+
+ @Schema(description = "椋庨櫓绛夌骇锛氬厖瓒�/绱у紶/鐭己", example = "鐭己")
+ private String riskLevel;
+
+ @Schema(description = "寤鸿鎺柦", example = "寤鸿绱ф�ラ噰璐�")
+ private String suggestion;
+ }
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiRiskPredictReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiRiskPredictReqVO.java
new file mode 100644
index 0000000..ef1e7e3
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiRiskPredictReqVO.java
@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.module.mes.controller.admin.pro.ai.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "绠$悊鍚庡彴 - AI 鐢熶骇椋庨櫓棰勬祴璇锋眰 VO")
+@Data
+public class MesProAiRiskPredictReqVO {
+
+ @Schema(description = "宸ュ崟 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "宸ュ崟 ID 涓嶈兘涓虹┖")
+ private Long workOrderId;
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiRiskPredictRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiRiskPredictRespVO.java
new file mode 100644
index 0000000..a525a74
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/ai/vo/MesProAiRiskPredictRespVO.java
@@ -0,0 +1,41 @@
+package cn.iocoder.yudao.module.mes.controller.admin.pro.ai.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.List;
+
+@Schema(description = "绠$悊鍚庡彴 - AI 鐢熶骇椋庨櫓棰勬祴鍝嶅簲 VO")
+@Data
+public class MesProAiRiskPredictRespVO {
+
+ @Schema(description = "鏁翠綋椋庨櫓绛夌骇锛�1=浣庨闄� 2=涓闄� 3=楂橀闄�", example = "2")
+ private Integer overallRiskLevel;
+
+ @Schema(description = "AI 缁煎悎鍒嗘瀽")
+ private String reasoning;
+
+ @Schema(description = "鍏抽敭鍏虫敞鐐�")
+ private List<String> keyPoints;
+
+ @Schema(description = "椋庨櫓鏄庣粏鍒楄〃")
+ private List<RiskItem> risks;
+
+ @Schema(description = "椋庨櫓椤�")
+ @Data
+ public static class RiskItem {
+
+ @Schema(description = "椋庨櫓绫诲埆锛氳川閲�/璁惧/鐗╂枡/宸ヨ壓鐡堕", example = "璁惧")
+ private String category;
+
+ @Schema(description = "椋庨櫓鎻忚堪", example = "璁惧A涓婃淇濆吇璺濅粖宸茶秴杩�30澶�")
+ private String description;
+
+ @Schema(description = "涓ラ噸绋嬪害锛氶珮/涓�/浣�", example = "涓�")
+ private String severity;
+
+ @Schema(description = "寤鸿鎺柦", example = "寤鸿鍦ㄥ紑濮嬬敓浜у墠杩涜璁惧淇濆吇")
+ private String suggestion;
+ }
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ai/MesQcAiController.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ai/MesQcAiController.java
new file mode 100644
index 0000000..98c9319
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ai/MesQcAiController.java
@@ -0,0 +1,31 @@
+package cn.iocoder.yudao.module.mes.controller.admin.qc.ai;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.ai.vo.MesQcAiSuggestReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.ai.vo.MesQcAiSuggestRespVO;
+import cn.iocoder.yudao.module.mes.service.qc.ai.MesQcAiService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "绠$悊鍚庡彴 - MES QC AI 杈呭姪鍒ゅ畾")
+@RestController
+@RequestMapping("/mes/qc/ai")
+public class MesQcAiController {
+
+ @Resource
+ private MesQcAiService qcAiService;
+
+ @PostMapping("/suggest-result")
+ @Operation(summary = "AI 杈呭姪鍒ゅ畾妫�楠岀粨鏋�")
+ @PreAuthorize("@ss.hasPermission('mes:qc:update')")
+ public CommonResult<MesQcAiSuggestRespVO> suggestCheckResult(@Valid @RequestBody MesQcAiSuggestReqVO reqVO) {
+ return success(qcAiService.suggestCheckResult(reqVO));
+ }
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ai/vo/MesQcAiSuggestReqVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ai/vo/MesQcAiSuggestReqVO.java
new file mode 100644
index 0000000..dc2fd21
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ai/vo/MesQcAiSuggestReqVO.java
@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.mes.controller.admin.qc.ai.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+@Schema(description = "绠$悊鍚庡彴 - AI 杈呭姪鍒ゅ畾璇锋眰 VO")
+@Data
+public class MesQcAiSuggestReqVO {
+
+ @Schema(description = "璐ㄦ鍗� ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "璐ㄦ鍗� ID 涓嶈兘涓虹┖")
+ private Long qcId;
+
+ @Schema(description = "璐ㄦ绫诲瀷", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+ @NotNull(message = "璐ㄦ绫诲瀷涓嶈兘涓虹┖")
+ private Integer qcType;
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ai/vo/MesQcAiSuggestRespVO.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ai/vo/MesQcAiSuggestRespVO.java
new file mode 100644
index 0000000..b39e639
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ai/vo/MesQcAiSuggestRespVO.java
@@ -0,0 +1,24 @@
+package cn.iocoder.yudao.module.mes.controller.admin.qc.ai.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.List;
+
+@Schema(description = "绠$悊鍚庡彴 - AI 杈呭姪鍒ゅ畾鍝嶅簲 VO")
+@Data
+public class MesQcAiSuggestRespVO {
+
+ @Schema(description = "寤鸿鐨勬楠岀粨鏋滅紪鐮�", example = "1")
+ private Integer suggestedResult;
+
+ @Schema(description = "寤鸿鐨勬楠岀粨鏋滃悕绉�", example = "鍚堟牸")
+ private String suggestedResultName;
+
+ @Schema(description = "AI 鎺ㄧ悊渚濇嵁")
+ private String reasoning;
+
+ @Schema(description = "鍏抽敭鍏虫敞鐐�")
+ private List<String> keyPoints;
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/indicatorresult/MesQcIndicatorResultMapper.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/indicatorresult/MesQcIndicatorResultMapper.java
index b134d96..4c5f513 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/indicatorresult/MesQcIndicatorResultMapper.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/indicatorresult/MesQcIndicatorResultMapper.java
@@ -7,6 +7,8 @@
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicatorresult.MesQcIndicatorResultDO;
import org.apache.ibatis.annotations.Mapper;
+import java.util.List;
+
/**
* MES 妫�楠岀粨鏋滆褰� Mapper
*
@@ -30,4 +32,10 @@
.eq(MesQcIndicatorResultDO::getQcType, qcType));
}
+ default List<MesQcIndicatorResultDO> selectListByQcIdAndType(Long qcId, Integer qcType) {
+ return selectList(new LambdaQueryWrapperX<MesQcIndicatorResultDO>()
+ .eq(MesQcIndicatorResultDO::getQcId, qcId)
+ .eq(MesQcIndicatorResultDO::getQcType, qcType));
+ }
+
}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/ai/MesProAiService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/ai/MesProAiService.java
new file mode 100644
index 0000000..0b370c4
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/ai/MesProAiService.java
@@ -0,0 +1,15 @@
+package cn.iocoder.yudao.module.mes.service.pro.ai;
+
+import cn.iocoder.yudao.module.mes.controller.admin.pro.ai.vo.*;
+
+public interface MesProAiService {
+
+ MesProAiMaterialPredictRespVO predictMaterial(MesProAiMaterialPredictReqVO reqVO);
+
+ MesProAiDurationPredictRespVO predictDuration(MesProAiDurationPredictReqVO reqVO);
+
+ MesProAiRiskPredictRespVO predictRisk(MesProAiRiskPredictReqVO reqVO);
+
+ MesProAiDeliveryPredictRespVO predictDelivery(MesProAiDeliveryPredictReqVO reqVO);
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/ai/MesProAiServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/ai/MesProAiServiceImpl.java
new file mode 100644
index 0000000..f76db29
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/ai/MesProAiServiceImpl.java
@@ -0,0 +1,782 @@
+package cn.iocoder.yudao.module.mes.service.pro.ai;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import cn.iocoder.yudao.module.ai.api.chat.AiChatApi;
+import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
+import cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO;
+import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
+import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
+import cn.iocoder.yudao.module.mes.controller.admin.pro.ai.vo.*;
+import cn.iocoder.yudao.module.mes.dal.dataobject.dv.machinery.MesDvMachineryDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationMachineDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.mps.MesProMpsDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskIssueDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderBomDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.qc.ipqc.MesQcIpqcDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
+import cn.iocoder.yudao.module.mes.dal.mysql.dv.machinery.MesDvMachineryMapper;
+import cn.iocoder.yudao.module.mes.dal.mysql.md.workstation.MesMdWorkstationMachineMapper;
+import cn.iocoder.yudao.module.mes.dal.mysql.pro.feedback.MesProFeedbackMapper;
+import cn.iocoder.yudao.module.mes.dal.mysql.pro.mps.MesProMpsMapper;
+import cn.iocoder.yudao.module.mes.dal.mysql.pro.task.MesProTaskIssueMapper;
+import cn.iocoder.yudao.module.mes.dal.mysql.pro.task.MesProTaskMapper;
+import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderBomMapper;
+import cn.iocoder.yudao.module.mes.dal.mysql.qc.ipqc.MesQcIpqcMapper;
+import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstock.MesWmMaterialStockMapper;
+import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService;
+import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderProcessService;
+import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.temporal.ChronoUnit;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_WORK_ORDER_NOT_EXISTS;
+
+@Slf4j
+@Service
+public class MesProAiServiceImpl implements MesProAiService {
+
+ // ==================== System Prompts ====================
+
+ private static final String SYSTEM_PROMPT_MATERIAL = """
+ 浣犳槸涓�浣嶈祫娣辩殑鍒堕�犱笟鐗╂枡绠$悊涓撳锛屾搮闀垮垎鏋愮敓浜у伐鍗曠殑鐗╂枡榻愬鎬у拰鐭己椋庨櫓銆�
+ 浣犻渶瑕佺患鍚堝垎鏋愪互涓嬩俊鎭紝缁欏嚭涓撲笟鐨勭墿鏂欓娴嬪缓璁細
+
+ ## 鍒嗘瀽缁村害
+ 1. BOM 闇�姹� vs 搴撳瓨鍙敤閲忥細瀵规瘮姣忎釜鐗╂枡鐨勯璁′娇鐢ㄩ噺涓庡綋鍓嶅簱瀛樺彲鐢ㄩ噺
+ 2. 搴撳瓨鍙敤閲� = 鍦ㄥ簱鏁伴噺 - 鍐荤粨鏁伴噺 - 宸插崰鐢ㄦ暟閲�
+ 3. 宸叉姇鏂欐儏鍐碉細宸茬粡鍙戞斁鍒板伐浣嶇殑鐗╂枡鏄惁鍏呰冻
+ 4. 椋庨櫓璇勪及锛氬鏈夌煭缂洪闄╃殑鐗╂枡鏍囨敞椋庨櫓绛夌骇
+
+ ## 椋庨櫓绛夌骇瀹氫箟
+ - 鍏呰冻锛氬彲鐢ㄩ噺 >= 闇�姹傞噺
+ - 绱у紶锛氬彲鐢ㄩ噺 >= 闇�姹傞噺鐨�50% 浣� < 闇�姹傞噺
+ - 鐭己锛氬彲鐢ㄩ噺 < 闇�姹傞噺鐨�50% 鎴栧彲鐢ㄩ噺涓�0
+
+ ## 杈撳嚭鏍煎紡
+ 璇锋寜浠ヤ笅JSON鏍煎紡杩斿洖锛堜笉瑕佸寘鍚叾浠栧唴瀹癸級锛�
+ {"riskLevel": 1, "reasoning": "缁煎悎鍒嗘瀽...", "riskItems": [{"itemCode": "...", "itemName": "...", "requiredQuantity": 100.0, "availableQuantity": 50.0, "shortageQuantity": 50.0, "riskLevel": "绱у紶", "suggestion": "寤鸿绱ф�ラ噰璐垨璋冩嫧"}]}
+
+ riskLevel 鍙栧�硷細1=浣庨闄� 2=涓闄� 3=楂橀闄�
+ """;
+
+ private static final String SYSTEM_PROMPT_DURATION = """
+ 浣犳槸涓�浣嶈祫娣辩殑鐢熶骇璁″垝涓撳锛屾搮闀块娴嬬敓浜у伐鍗曠殑瀹為檯鐢熶骇鏃堕暱銆�
+ 浣犻渶瑕佺患鍚堝垎鏋愪互涓嬩俊鎭紝缁欏嚭涓撲笟鐨勬椂闀块娴嬪缓璁細
+
+ ## 鍒嗘瀽渚濇嵁
+ 1. 鏍囧噯宸ユ椂锛氬伐鑹鸿矾绾胯瀹氱殑鏍囧噯鍑嗗鏃堕棿銆佺瓑寰呮椂闂淬�佺敓浜ф椂闂�
+ 2. 璁惧鏁堢巼锛氬叧鑱旇澶囩殑姣忓皬鏃朵骇閲忥紝浼犻�佸饱甯﹀�嶇巼
+ 3. 鐢熶骇鏁伴噺锛氬伐鍗曡鍒掓暟閲�
+ 4. 瀹為檯鍘嗗彶鏁版嵁锛氬凡瀹屾垚宸ュ簭鐨勫疄闄呯敓浜ф椂闀�
+ 5. 璁$畻鍏紡锛氶娴嬫椂闀� = 鍑嗗鏃堕棿 + 绛夊緟鏃堕棿 + (璁″垝鏁伴噺 / (璁惧鏁堢巼 * 浼犻�佸饱甯︾郴鏁�))锛堟晥鐜囧崟浣嶄负姣忓皬鏃朵骇閲忥級
+
+ ## 杈撳嚭鏍煎紡
+ 璇锋寜浠ヤ笅JSON鏍煎紡杩斿洖锛堜笉瑕佸寘鍚叾浠栧唴瀹癸級锛�
+ {"predictedTotalHours": 48.5, "standardTotalHours": 40.0, "reasoning": "...", "keyPoints": ["鍏抽敭宸ュ簭A鏄摱棰�"], "processBreakdown": [{"processName": "...", "sort": 1, "standardPrepareTime": 30, "standardWaitTime": 15, "standardProductionTime": 8.0, "predictedProductionTime": 10.5}]}
+
+ predictedTotalHours 鍜� standardTotalHours 涓哄皬鏁�
+ """;
+
+ private static final String SYSTEM_PROMPT_RISK = """
+ 浣犳槸涓�浣嶈祫娣辩殑鍒堕�犻闄╃鐞嗕笓瀹讹紝鎿呴暱璇嗗埆鐢熶骇杩囩▼涓殑鍚勭被椋庨櫓銆�
+ 浣犻渶瑕佺患鍚堝垎鏋愪互涓嬩俊鎭紝缁欏嚭涓撲笟鐨勯闄╄瘎浼帮細
+
+ ## 鍒嗘瀽缁村害
+ 1. 璐ㄩ噺椋庨櫓锛氳繃绋嬫楠�(IPQC)鐨勭己闄风巼鏁版嵁锛堣嚧鍛�/涓ラ噸/杞诲井缂洪櫡鐜囷級
+ 2. 璁惧椋庨櫓锛氳澶囩姸鎬併�佷笂娆′繚鍏绘椂闂淬�佷笂娆$偣妫�鏃堕棿銆佺敓浜ф晥鐜�
+ 3. 鐗╂枡椋庨櫓锛氱墿鏂欏簱瀛樺彲鐢ㄩ噺涓庨渶姹傞噺鐨勫姣�
+ 4. 宸ヨ壓鐡堕锛氬叧閿伐搴忕殑璁惧浜ц兘銆佷换鍔℃帓闃熸儏鍐�
+
+ ## 椋庨櫓绛夌骇瀹氫箟
+ - 楂橈細瀛樺湪鑷村懡缂洪櫡鐜�>5% 鎴� 璁惧鏁呴殰 鎴� 鍏抽敭鐗╂枡鐭己>50% 鎴� 鍏抽敭宸ュ簭涓ラ噸绉帇
+ - 涓細瀛樺湪涓ラ噸缂洪櫡鐜�>10% 鎴� 璁惧闀挎湡鏈繚鍏� 鎴� 鐗╂枡绱у紶
+ - 浣庯細鍚勯」鎸囨爣姝e父
+
+ ## 杈撳嚭鏍煎紡
+ 璇锋寜浠ヤ笅JSON鏍煎紡杩斿洖锛堜笉瑕佸寘鍚叾浠栧唴瀹癸級锛�
+ {"overallRiskLevel": 2, "reasoning": "...", "keyPoints": ["鍏虫敞鐐�1"], "risks": [{"category": "璁惧", "description": "璁惧A涓婃淇濆吇璺濅粖宸茶秴30澶�", "severity": "涓�", "suggestion": "寤鸿鐢熶骇鍓嶄繚鍏�"}]}
+
+ overallRiskLevel 鍙栧�硷細1=浣庨闄� 2=涓闄� 3=楂橀闄�
+ riskLevel 鍙栧�硷細鍏呰冻/绱у紶/鐭己
+ severity 鍙栧�硷細楂�/涓�/浣�
+ category 鍙栧�硷細璐ㄩ噺/璁惧/鐗╂枡/宸ヨ壓鐡堕
+ """;
+
+ private static final String SYSTEM_PROMPT_DELIVERY = """
+ 浣犳槸涓�浣嶈祫娣辩殑璁㈠崟浜や粯绠$悊涓撳锛屾搮闀胯瘎浼板伐鍗曟槸鍚﹁兘鎸夋椂浜や粯銆�
+ 浣犻渶瑕佺患鍚堝垎鏋愪互涓嬩俊鎭紝缁欏嚭涓撲笟鐨勪氦浠樿瘎浼帮細
+
+ ## 鍒嗘瀽渚濇嵁
+ 1. 褰撳墠杩涘害锛氬凡鐢熶骇鏁伴噺 / 璁″垝鏁伴噺鐨勭櫨鍒嗘瘮
+ 2. 鍓╀綑鏃堕棿锛氳窛绂婚渶姹傛棩鏈熺殑澶╂暟锛堝伐浣滄棩鎸夋瘡澶�8灏忔椂璁$畻锛�
+ 3. 棰勮鍓╀綑宸ユ椂锛氭牴鎹伐鑹鸿矾绾垮拰璁惧鏁堢巼棰勬祴鐨勫墿浣欑敓浜ф椂闀�
+ 4. 鐢熶骇閫熷害锛氭牴鎹凡瀹屾垚浠诲姟鐨勫疄闄呯敓浜ч�熺巼
+ 5. 浠诲姟鐘舵�侊細鍚勫伐搴忕殑瀹屾垚鎯呭喌
+
+ ## 鍒ゅ畾閫昏緫
+ - 鎸夋椂浜や粯锛氬墿浣欏ぉ鏁� * 8灏忔椂 >= 棰勮鍓╀綑宸ユ椂锛屼笖杩涘害姝e父
+ - 鏈夊欢鏈熼闄╋細鍓╀綑澶╂暟 * 8灏忔椂 >= 棰勮鍓╀綑宸ユ椂鐨�80%锛屼絾涓嶈冻100%
+ - 鏃犳硶鎸夋椂浜や粯锛氬墿浣欏ぉ鏁� * 8灏忔椂 < 棰勮鍓╀綑宸ユ椂鐨�80%锛屾垨鍏抽敭宸ュ簭鍗¢樆
+
+ ## 杈撳嚭鏍煎紡
+ 璇锋寜浠ヤ笅JSON鏍煎紡杩斿洖锛堜笉瑕佸寘鍚叾浠栧唴瀹癸級锛�
+ {"onTime": true, "confidence": 85.5, "reasoning": "...", "delayFactors": [], "progressPercent": 60.0, "remainingDays": 10}
+
+ onTime: true=鍙寜鏃朵氦浠� false=鏃犳硶鎸夋椂浜や粯
+ confidence: 0-100鐨勬暟瀛楋紝琛ㄧず璇勪及缃俊搴�
+ """;
+
+ // ==================== Dependencies ====================
+
+ @Resource
+ private MesProWorkOrderService workOrderService;
+ @Resource
+ private MesProWorkOrderBomMapper workOrderBomMapper;
+ @Resource
+ private MesProWorkOrderProcessService workOrderProcessService;
+ @Resource
+ private MesProTaskMapper taskMapper;
+ @Resource
+ private MesProTaskIssueMapper taskIssueMapper;
+ @Resource
+ private MesProFeedbackMapper feedbackMapper;
+ @Resource
+ private MesQcIpqcMapper ipqcMapper;
+ @Resource
+ private MesWmMaterialStockMapper materialStockMapper;
+ @Resource
+ private MesMdWorkstationMachineMapper workstationMachineMapper;
+ @Resource
+ private MesDvMachineryMapper machineryMapper;
+ @Resource
+ private MesProMpsMapper mpsMapper;
+ @Resource
+ private MesProProcessService processService;
+ @Resource
+ private MdmItemApi mdmItemApi;
+ @Resource
+ private CrmCustomerApi crmCustomerApi;
+ @Resource
+ private AiChatApi aiChatApi;
+
+ // ==================== 1. 鐗╂枡鐭己棰勬祴 ====================
+
+ @Override
+ public MesProAiMaterialPredictRespVO predictMaterial(MesProAiMaterialPredictReqVO reqVO) {
+ try {
+ Map<String, Object> data = gatherMaterialData(reqVO.getWorkOrderId());
+ String aiResponse = aiChatApi.chat(SYSTEM_PROMPT_MATERIAL, JSONUtil.toJsonPrettyStr(data));
+ log.info("AI 鐗╂枡棰勬祴: workOrderId={}, response={}", reqVO.getWorkOrderId(), aiResponse);
+ return parseMaterialResponse(aiResponse);
+ } catch (Exception e) {
+ log.warn("AI 鐗╂枡棰勬祴澶辫触锛寃orkOrderId={}", reqVO.getWorkOrderId(), e);
+ MesProAiMaterialPredictRespVO fallback = new MesProAiMaterialPredictRespVO();
+ fallback.setReasoning("AI 鍒嗘瀽鏆傛椂涓嶅彲鐢紝璇风◢鍚庨噸璇�");
+ return fallback;
+ }
+ }
+
+ private Map<String, Object> gatherMaterialData(Long workOrderId) {
+ Map<String, Object> data = new LinkedHashMap<>();
+ MesProWorkOrderDO workOrder = workOrderService.getWorkOrder(workOrderId);
+ if (workOrder == null) throw exception(PRO_WORK_ORDER_NOT_EXISTS);
+
+ data.put("宸ュ崟缂栧彿", workOrder.getCode());
+ data.put("宸ュ崟鍚嶇О", workOrder.getName());
+ data.put("璁″垝鏁伴噺", workOrder.getQuantity());
+ data.put("宸茬敓浜ф暟閲�", workOrder.getQuantityProduced());
+ fillItemInfo(data, workOrder.getProductId());
+
+ // BOM 鐗╂枡闇�姹�
+ List<MesProWorkOrderBomDO> bomList = workOrderBomMapper.selectListByWorkOrderId(workOrderId);
+ if (CollUtil.isNotEmpty(bomList)) {
+ List<Map<String, Object>> materials = new ArrayList<>();
+ for (MesProWorkOrderBomDO bom : bomList) {
+ Map<String, Object> item = new LinkedHashMap<>();
+ item.put("鐗╂枡ID", bom.getItemId());
+ String itemName = resolveItemName(bom.getItemId());
+ item.put("鐗╂枡鍚嶇О", itemName);
+ item.put("闇�姹傞噺", bom.getQuantity());
+
+ // 鏌ヨ搴撳瓨鍙敤閲�
+ List<MesWmMaterialStockDO> stocks = materialStockMapper.selectList(
+ new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<MesWmMaterialStockDO>()
+ .eq(MesWmMaterialStockDO::getItemId, bom.getItemId())
+ .ne(MesWmMaterialStockDO::getQuantity, BigDecimal.ZERO));
+ BigDecimal available = BigDecimal.ZERO;
+ if (CollUtil.isNotEmpty(stocks)) {
+ for (MesWmMaterialStockDO stock : stocks) {
+ BigDecimal frozen = stock.getFrozenQuantity() != null ? stock.getFrozenQuantity() : BigDecimal.ZERO;
+ BigDecimal reserved = stock.getReservedQuantity() != null ? stock.getReservedQuantity() : BigDecimal.ZERO;
+ available = available.add(stock.getQuantity().subtract(frozen).subtract(reserved));
+ }
+ }
+ item.put("搴撳瓨鍙敤閲�", available.compareTo(BigDecimal.ZERO) > 0 ? available : BigDecimal.ZERO);
+ materials.add(item);
+ }
+ data.put("鐗╂枡闇�姹傛竻鍗�", materials);
+ }
+
+ // 宸叉姇鏂欐儏鍐�
+ List<MesProTaskDO> tasks = taskMapper.selectListByWorkOrderId(workOrderId);
+ if (CollUtil.isNotEmpty(tasks)) {
+ List<Map<String, Object>> issuedMaterials = new ArrayList<>();
+ for (MesProTaskDO task : tasks) {
+ List<MesProTaskIssueDO> issues = taskIssueMapper.selectListByTaskId(task.getId());
+ for (MesProTaskIssueDO issue : issues) {
+ Map<String, Object> issued = new LinkedHashMap<>();
+ issued.put("鐗╂枡ID", issue.getItemId());
+ issued.put("宸叉姇鏂欐暟閲�", issue.getIssuedQuantity());
+ issued.put("宸蹭娇鐢ㄦ暟閲�", issue.getUsedQuantity());
+ issued.put("鍙敤鏁伴噺", issue.getAvailableQuantity());
+ issuedMaterials.add(issued);
+ }
+ }
+ if (!issuedMaterials.isEmpty()) {
+ data.put("宸叉姇鏂欒褰�", issuedMaterials);
+ }
+ }
+
+ return data;
+ }
+
+ private MesProAiMaterialPredictRespVO parseMaterialResponse(String aiResponse) {
+ MesProAiMaterialPredictRespVO respVO = new MesProAiMaterialPredictRespVO();
+ try {
+ String json = extractJson(aiResponse);
+ Map<String, Object> parsed = JSONUtil.toBean(json, Map.class);
+ Object riskLevel = parsed.get("riskLevel");
+ if (riskLevel instanceof Number num) respVO.setRiskLevel(num.intValue());
+ respVO.setReasoning((String) parsed.get("reasoning"));
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> items = (List<Map<String, Object>>) parsed.get("riskItems");
+ if (CollUtil.isNotEmpty(items)) {
+ respVO.setRiskItems(items.stream().map(m -> {
+ MesProAiMaterialPredictRespVO.RiskItem ri = new MesProAiMaterialPredictRespVO.RiskItem();
+ ri.setItemCode((String) m.get("itemCode"));
+ ri.setItemName((String) m.get("itemName"));
+ ri.setRequiredQuantity(toBigDecimal(m.get("requiredQuantity")));
+ ri.setAvailableQuantity(toBigDecimal(m.get("availableQuantity")));
+ ri.setShortageQuantity(toBigDecimal(m.get("shortageQuantity")));
+ ri.setRiskLevel((String) m.get("riskLevel"));
+ ri.setSuggestion((String) m.get("suggestion"));
+ return ri;
+ }).collect(Collectors.toList()));
+ }
+ } catch (Exception e) {
+ log.warn("瑙f瀽 AI 鐗╂枡棰勬祴鍝嶅簲澶辫触: {}", aiResponse, e);
+ respVO.setReasoning(aiResponse);
+ }
+ return respVO;
+ }
+
+ // ==================== 2. 鐢熶骇鏃堕暱棰勬祴 ====================
+
+ @Override
+ public MesProAiDurationPredictRespVO predictDuration(MesProAiDurationPredictReqVO reqVO) {
+ try {
+ Map<String, Object> data = gatherDurationData(reqVO.getWorkOrderId());
+ String aiResponse = aiChatApi.chat(SYSTEM_PROMPT_DURATION, JSONUtil.toJsonPrettyStr(data));
+ log.info("AI 鏃堕暱棰勬祴: workOrderId={}, response={}", reqVO.getWorkOrderId(), aiResponse);
+ return parseDurationResponse(aiResponse);
+ } catch (Exception e) {
+ log.warn("AI 鏃堕暱棰勬祴澶辫触锛寃orkOrderId={}", reqVO.getWorkOrderId(), e);
+ MesProAiDurationPredictRespVO fallback = new MesProAiDurationPredictRespVO();
+ fallback.setReasoning("AI 鍒嗘瀽鏆傛椂涓嶅彲鐢紝璇风◢鍚庨噸璇�");
+ return fallback;
+ }
+ }
+
+ private Map<String, Object> gatherDurationData(Long workOrderId) {
+ Map<String, Object> data = new LinkedHashMap<>();
+ MesProWorkOrderDO workOrder = workOrderService.getWorkOrder(workOrderId);
+ if (workOrder == null) throw exception(PRO_WORK_ORDER_NOT_EXISTS);
+
+ data.put("宸ュ崟缂栧彿", workOrder.getCode());
+ data.put("璁″垝鏁伴噺", workOrder.getQuantity());
+ data.put("宸茬敓浜ф暟閲�", workOrder.getQuantityProduced());
+ fillItemInfo(data, workOrder.getProductId());
+
+ // 宸ュ簭淇℃伅
+ List<MesProWorkOrderProcessDO> processes = workOrderProcessService.getWorkOrderProcessListByWorkOrderId(workOrderId);
+ List<MesProTaskDO> tasks = taskMapper.selectListByWorkOrderId(workOrderId);
+ Map<Long, MesProTaskDO> taskByProcessId = new HashMap<>();
+ if (CollUtil.isNotEmpty(tasks)) {
+ for (MesProTaskDO task : tasks) {
+ if (task.getProcessId() != null) {
+ taskByProcessId.put(task.getProcessId(), task);
+ }
+ }
+ }
+
+ List<Map<String, Object>> processList = new ArrayList<>();
+ BigDecimal standardTotalHours = BigDecimal.ZERO;
+ for (MesProWorkOrderProcessDO proc : processes) {
+ Map<String, Object> item = new LinkedHashMap<>();
+ String processName = resolveProcessName(proc.getProcessId());
+ item.put("宸ュ簭鍚嶇О", processName);
+ item.put("椤哄簭", proc.getSort());
+ item.put("鏍囧噯鍑嗗鏃堕棿(鍒嗛挓)", proc.getPrepareTime() != null ? proc.getPrepareTime() : 0);
+ item.put("鏍囧噯绛夊緟鏃堕棿(鍒嗛挓)", proc.getWaitTime() != null ? proc.getWaitTime() : 0);
+ item.put("鏄惁鍏抽敭宸ュ簭", proc.getKeyFlag() != null && proc.getKeyFlag());
+ item.put("鏄惁璐ㄦ宸ュ簭", proc.getCheckFlag() != null && proc.getCheckFlag());
+
+ // 璁惧鏁堢巼
+ MesProTaskDO task = taskByProcessId.get(proc.getProcessId());
+ if (task != null && task.getWorkstationId() != null) {
+ BigDecimal totalEfficiency = getWorkstationEfficiency(task.getWorkstationId());
+ item.put("璁惧鎬绘晥鐜�(浠�/灏忔椂)", totalEfficiency);
+ if (totalEfficiency.compareTo(BigDecimal.ZERO) > 0) {
+ BigDecimal productionHours = workOrder.getQuantity()
+ .subtract(workOrder.getQuantityProduced() != null ? workOrder.getQuantityProduced() : BigDecimal.ZERO)
+ .divide(totalEfficiency, 4, RoundingMode.HALF_UP);
+ item.put("棰勮鐢熶骇鏃堕棿(灏忔椂)", productionHours);
+ }
+ item.put("浠诲姟鐘舵��", task.getStatus());
+ if (task.getDuration() != null) item.put("宸茬敤鏃堕暱(灏忔椂)", task.getDuration());
+ } else {
+ item.put("璁惧鎬绘晥鐜�(浠�/灏忔椂)", "鏈帓浜ф棤璁惧鏁版嵁");
+ }
+
+ // 鏍囧噯鏃堕棿
+ int prepareMin = proc.getPrepareTime() != null ? proc.getPrepareTime() : 0;
+ int waitMin = proc.getWaitTime() != null ? proc.getWaitTime() : 0;
+ BigDecimal standardHours = BigDecimal.valueOf(prepareMin + waitMin).divide(BigDecimal.valueOf(60), 4, RoundingMode.HALF_UP);
+ standardTotalHours = standardTotalHours.add(standardHours);
+ item.put("鏍囧噯鍑嗗+绛夊緟鏃堕棿(灏忔椂)", standardHours);
+
+ processList.add(item);
+ }
+ data.put("宸ュ簭鏄庣粏", processList);
+ data.put("鏍囧噯鎬诲噯澶�+绛夊緟鏃堕棿(灏忔椂)", standardTotalHours);
+
+ return data;
+ }
+
+ private BigDecimal getWorkstationEfficiency(Long workstationId) {
+ BigDecimal totalEfficiency = BigDecimal.ZERO;
+ List<MesMdWorkstationMachineDO> machines = workstationMachineMapper.selectListByWorkstationId(workstationId);
+ if (CollUtil.isNotEmpty(machines)) {
+ for (MesMdWorkstationMachineDO wm : machines) {
+ MesDvMachineryDO machinery = machineryMapper.selectById(wm.getMachineryId());
+ if (machinery != null && machinery.getProductionEfficiency() != null) {
+ BigDecimal efficiency = machinery.getProductionEfficiency();
+ if (machinery.getConveyorBelt() != null && machinery.getConveyorBelt() > 0) {
+ efficiency = efficiency.multiply(BigDecimal.valueOf(machinery.getConveyorBelt()));
+ }
+ totalEfficiency = totalEfficiency.add(efficiency);
+ }
+ }
+ }
+ return totalEfficiency;
+ }
+
+ private MesProAiDurationPredictRespVO parseDurationResponse(String aiResponse) {
+ MesProAiDurationPredictRespVO respVO = new MesProAiDurationPredictRespVO();
+ try {
+ String json = extractJson(aiResponse);
+ Map<String, Object> parsed = JSONUtil.toBean(json, Map.class);
+ respVO.setPredictedTotalHours(toBigDecimal(parsed.get("predictedTotalHours")));
+ respVO.setStandardTotalHours(toBigDecimal(parsed.get("standardTotalHours")));
+ respVO.setReasoning((String) parsed.get("reasoning"));
+ @SuppressWarnings("unchecked")
+ List<String> keyPoints = (List<String>) parsed.get("keyPoints");
+ respVO.setKeyPoints(keyPoints);
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> breakdown = (List<Map<String, Object>>) parsed.get("processBreakdown");
+ if (CollUtil.isNotEmpty(breakdown)) {
+ respVO.setProcessBreakdown(breakdown.stream().map(m -> {
+ MesProAiDurationPredictRespVO.ProcessTimeBreakdown pt = new MesProAiDurationPredictRespVO.ProcessTimeBreakdown();
+ pt.setProcessName((String) m.get("processName"));
+ pt.setSort(toInteger(m.get("sort")));
+ pt.setStandardPrepareTime(toInteger(m.get("standardPrepareTime")));
+ pt.setStandardWaitTime(toInteger(m.get("standardWaitTime")));
+ pt.setStandardProductionTime(toBigDecimal(m.get("standardProductionTime")));
+ pt.setPredictedProductionTime(toBigDecimal(m.get("predictedProductionTime")));
+ return pt;
+ }).collect(Collectors.toList()));
+ }
+ } catch (Exception e) {
+ log.warn("瑙f瀽 AI 鏃堕暱棰勬祴鍝嶅簲澶辫触: {}", aiResponse, e);
+ respVO.setReasoning(aiResponse);
+ }
+ return respVO;
+ }
+
+ // ==================== 3. 鐢熶骇椋庨櫓棰勬祴 ====================
+
+ @Override
+ public MesProAiRiskPredictRespVO predictRisk(MesProAiRiskPredictReqVO reqVO) {
+ try {
+ Map<String, Object> data = gatherRiskData(reqVO.getWorkOrderId());
+ String aiResponse = aiChatApi.chat(SYSTEM_PROMPT_RISK, JSONUtil.toJsonPrettyStr(data));
+ log.info("AI 椋庨櫓棰勬祴: workOrderId={}, response={}", reqVO.getWorkOrderId(), aiResponse);
+ return parseRiskResponse(aiResponse);
+ } catch (Exception e) {
+ log.warn("AI 椋庨櫓棰勬祴澶辫触锛寃orkOrderId={}", reqVO.getWorkOrderId(), e);
+ MesProAiRiskPredictRespVO fallback = new MesProAiRiskPredictRespVO();
+ fallback.setReasoning("AI 鍒嗘瀽鏆傛椂涓嶅彲鐢紝璇风◢鍚庨噸璇�");
+ return fallback;
+ }
+ }
+
+ private Map<String, Object> gatherRiskData(Long workOrderId) {
+ Map<String, Object> data = new LinkedHashMap<>();
+ MesProWorkOrderDO workOrder = workOrderService.getWorkOrder(workOrderId);
+ if (workOrder == null) throw exception(PRO_WORK_ORDER_NOT_EXISTS);
+
+ data.put("宸ュ崟缂栧彿", workOrder.getCode());
+ data.put("宸ュ崟鍚嶇О", workOrder.getName());
+ data.put("璁″垝鏁伴噺", workOrder.getQuantity());
+ data.put("宸茬敓浜ф暟閲�", workOrder.getQuantityProduced());
+ fillItemInfo(data, workOrder.getProductId());
+
+ // 璐ㄩ噺椋庨櫓 - IPQC
+ List<MesQcIpqcDO> ipqcList = ipqcMapper.selectList(
+ new LambdaQueryWrapper<MesQcIpqcDO>()
+ .eq(MesQcIpqcDO::getWorkOrderId, workOrderId));
+ if (CollUtil.isNotEmpty(ipqcList)) {
+ List<Map<String, Object>> qcData = ipqcList.stream().map(ipqc -> {
+ Map<String, Object> item = new LinkedHashMap<>();
+ item.put("妫�楠屽崟缂栧彿", ipqc.getCode());
+ item.put("鑷村懡缂洪櫡鐜�", ipqc.getCriticalRate() != null ? ipqc.getCriticalRate() + "%" : "0%");
+ item.put("涓ラ噸缂洪櫡鐜�", ipqc.getMajorRate() != null ? ipqc.getMajorRate() + "%" : "0%");
+ item.put("杞诲井缂洪櫡鐜�", ipqc.getMinorRate() != null ? ipqc.getMinorRate() + "%" : "0%");
+ item.put("鑷村懡缂洪櫡鏁�", ipqc.getCriticalQuantity() != null ? ipqc.getCriticalQuantity() : 0);
+ item.put("涓ラ噸缂洪櫡鏁�", ipqc.getMajorQuantity() != null ? ipqc.getMajorQuantity() : 0);
+ item.put("杞诲井缂洪櫡鏁�", ipqc.getMinorQuantity() != null ? ipqc.getMinorQuantity() : 0);
+ item.put("妫�楠岀粨鏋�", ipqc.getCheckResult());
+ return item;
+ }).collect(Collectors.toList());
+ data.put("杩囩▼妫�楠�(IPQC)璁板綍", qcData);
+ }
+
+ // 璁惧椋庨櫓
+ List<MesProTaskDO> tasks = taskMapper.selectListByWorkOrderId(workOrderId);
+ if (CollUtil.isNotEmpty(tasks)) {
+ Set<Long> machineryIds = new HashSet<>();
+ for (MesProTaskDO task : tasks) {
+ if (task.getWorkstationId() != null) {
+ List<MesMdWorkstationMachineDO> wms = workstationMachineMapper.selectListByWorkstationId(task.getWorkstationId());
+ for (MesMdWorkstationMachineDO wm : wms) {
+ machineryIds.add(wm.getMachineryId());
+ }
+ }
+ }
+ if (!machineryIds.isEmpty()) {
+ List<Map<String, Object>> equipmentData = new ArrayList<>();
+ for (Long mid : machineryIds) {
+ MesDvMachineryDO machinery = machineryMapper.selectById(mid);
+ if (machinery != null) {
+ Map<String, Object> eq = new LinkedHashMap<>();
+ eq.put("璁惧缂栧彿", machinery.getCode());
+ eq.put("璁惧鍚嶇О", machinery.getName());
+ eq.put("璁惧鐘舵��", machinery.getStatus());
+ eq.put("鐢熶骇鏁堢巼(浠�/灏忔椂)", machinery.getProductionEfficiency());
+ eq.put("浼犻�佸饱甯﹀�嶆暟", machinery.getConveyorBelt());
+ eq.put("涓婃淇濆吇鏃堕棿", machinery.getLastMaintenTime() != null
+ ? machinery.getLastMaintenTime().toString() : "鏃犺褰�");
+ eq.put("涓婃鐐规鏃堕棿", machinery.getLastCheckTime() != null
+ ? machinery.getLastCheckTime().toString() : "鏃犺褰�");
+ equipmentData.add(eq);
+ }
+ }
+ data.put("鍏宠仈璁惧淇℃伅", equipmentData);
+ }
+ }
+
+ // 鐗╂枡椋庨櫓锛堣交閲忕骇锛氫粎鏍囪鍙敤閲� < 20%鐨勭墿鏂欙級
+ List<MesProWorkOrderBomDO> bomList = workOrderBomMapper.selectListByWorkOrderId(workOrderId);
+ if (CollUtil.isNotEmpty(bomList)) {
+ List<Map<String, Object>> materialRisks = new ArrayList<>();
+ for (MesProWorkOrderBomDO bom : bomList) {
+ List<MesWmMaterialStockDO> stocks = materialStockMapper.selectList(
+ new LambdaQueryWrapper<MesWmMaterialStockDO>()
+ .eq(MesWmMaterialStockDO::getItemId, bom.getItemId())
+ .ne(MesWmMaterialStockDO::getQuantity, BigDecimal.ZERO));
+ BigDecimal available = BigDecimal.ZERO;
+ if (CollUtil.isNotEmpty(stocks)) {
+ for (MesWmMaterialStockDO stock : stocks) {
+ BigDecimal frozen = stock.getFrozenQuantity() != null ? stock.getFrozenQuantity() : BigDecimal.ZERO;
+ BigDecimal reserved = stock.getReservedQuantity() != null ? stock.getReservedQuantity() : BigDecimal.ZERO;
+ available = available.add(stock.getQuantity().subtract(frozen).subtract(reserved));
+ }
+ }
+ if (bom.getQuantity() != null && bom.getQuantity().compareTo(BigDecimal.ZERO) > 0
+ && available.compareTo(bom.getQuantity().multiply(BigDecimal.valueOf(0.2))) < 0) {
+ Map<String, Object> risk = new LinkedHashMap<>();
+ risk.put("鐗╂枡ID", bom.getItemId());
+ risk.put("鐗╂枡鍚嶇О", resolveItemName(bom.getItemId()));
+ risk.put("闇�姹傞噺", bom.getQuantity());
+ risk.put("鍙敤閲�", available);
+ risk.put("缂哄彛姣斾緥", available.compareTo(BigDecimal.ZERO) > 0
+ ? BigDecimal.ONE.subtract(available.divide(bom.getQuantity(), 2, RoundingMode.HALF_UP)).multiply(BigDecimal.valueOf(100)) + "%"
+ : "100%");
+ materialRisks.add(risk);
+ }
+ }
+ if (!materialRisks.isEmpty()) {
+ data.put("鐗╂枡鐭己椋庨櫓", materialRisks);
+ }
+ }
+
+ // 宸ヨ壓鐡堕 - 鍏抽敭宸ュ簭淇℃伅
+ List<MesProWorkOrderProcessDO> processes = workOrderProcessService.getWorkOrderProcessListByWorkOrderId(workOrderId);
+ if (CollUtil.isNotEmpty(processes)) {
+ List<Map<String, Object>> keyProcesses = new ArrayList<>();
+ for (MesProWorkOrderProcessDO proc : processes) {
+ if (proc.getKeyFlag() != null && proc.getKeyFlag()) {
+ Map<String, Object> kp = new LinkedHashMap<>();
+ kp.put("宸ュ簭鍚嶇О", resolveProcessName(proc.getProcessId()));
+ kp.put("椤哄簭", proc.getSort());
+ kp.put("鏍囧噯鍑嗗鏃堕棿(鍒嗛挓)", proc.getPrepareTime());
+ kp.put("鏍囧噯绛夊緟鏃堕棿(鍒嗛挓)", proc.getWaitTime());
+ keyProcesses.add(kp);
+ }
+ }
+ if (!keyProcesses.isEmpty()) {
+ data.put("鍏抽敭宸ュ簭", keyProcesses);
+ }
+ }
+
+ return data;
+ }
+
+ private MesProAiRiskPredictRespVO parseRiskResponse(String aiResponse) {
+ MesProAiRiskPredictRespVO respVO = new MesProAiRiskPredictRespVO();
+ try {
+ String json = extractJson(aiResponse);
+ Map<String, Object> parsed = JSONUtil.toBean(json, Map.class);
+ Object riskLevel = parsed.get("overallRiskLevel");
+ if (riskLevel instanceof Number num) respVO.setOverallRiskLevel(num.intValue());
+ respVO.setReasoning((String) parsed.get("reasoning"));
+ @SuppressWarnings("unchecked")
+ List<String> keyPoints = (List<String>) parsed.get("keyPoints");
+ respVO.setKeyPoints(keyPoints);
+ @SuppressWarnings("unchecked")
+ List<Map<String, Object>> risks = (List<Map<String, Object>>) parsed.get("risks");
+ if (CollUtil.isNotEmpty(risks)) {
+ respVO.setRisks(risks.stream().map(m -> {
+ MesProAiRiskPredictRespVO.RiskItem ri = new MesProAiRiskPredictRespVO.RiskItem();
+ ri.setCategory((String) m.get("category"));
+ ri.setDescription((String) m.get("description"));
+ ri.setSeverity((String) m.get("severity"));
+ ri.setSuggestion((String) m.get("suggestion"));
+ return ri;
+ }).collect(Collectors.toList()));
+ }
+ } catch (Exception e) {
+ log.warn("瑙f瀽 AI 椋庨櫓棰勬祴鍝嶅簲澶辫触: {}", aiResponse, e);
+ respVO.setReasoning(aiResponse);
+ }
+ return respVO;
+ }
+
+ // ==================== 4. 鏄惁鑳芥寜鏃朵氦浠� ====================
+
+ @Override
+ public MesProAiDeliveryPredictRespVO predictDelivery(MesProAiDeliveryPredictReqVO reqVO) {
+ try {
+ Map<String, Object> data = gatherDeliveryData(reqVO.getWorkOrderId());
+ String aiResponse = aiChatApi.chat(SYSTEM_PROMPT_DELIVERY, JSONUtil.toJsonPrettyStr(data));
+ log.info("AI 浜や粯棰勬祴: workOrderId={}, response={}", reqVO.getWorkOrderId(), aiResponse);
+ return parseDeliveryResponse(aiResponse);
+ } catch (Exception e) {
+ log.warn("AI 浜や粯棰勬祴澶辫触锛寃orkOrderId={}", reqVO.getWorkOrderId(), e);
+ MesProAiDeliveryPredictRespVO fallback = new MesProAiDeliveryPredictRespVO();
+ fallback.setReasoning("AI 鍒嗘瀽鏆傛椂涓嶅彲鐢紝璇风◢鍚庨噸璇�");
+ return fallback;
+ }
+ }
+
+ private Map<String, Object> gatherDeliveryData(Long workOrderId) {
+ Map<String, Object> data = new LinkedHashMap<>();
+ MesProWorkOrderDO workOrder = workOrderService.getWorkOrder(workOrderId);
+ if (workOrder == null) throw exception(PRO_WORK_ORDER_NOT_EXISTS);
+
+ data.put("宸ュ崟缂栧彿", workOrder.getCode());
+ data.put("宸ュ崟鍚嶇О", workOrder.getName());
+ data.put("璁″垝鏁伴噺", workOrder.getQuantity());
+ data.put("宸茬敓浜ф暟閲�", workOrder.getQuantityProduced());
+ data.put("宸ュ崟鐘舵��", workOrder.getStatus());
+ data.put("闇�姹傛棩鏈�", workOrder.getRequestDate() != null ? workOrder.getRequestDate().toString() : "鏈缃�");
+ data.put("瀹屾垚鏃ユ湡", workOrder.getFinishDate() != null ? workOrder.getFinishDate().toString() : "鏈畬鎴�");
+ fillItemInfo(data, workOrder.getProductId());
+
+ // 杩涘害璁$畻
+ BigDecimal progressPercent = BigDecimal.ZERO;
+ if (workOrder.getQuantity() != null && workOrder.getQuantity().compareTo(BigDecimal.ZERO) > 0
+ && workOrder.getQuantityProduced() != null) {
+ progressPercent = workOrder.getQuantityProduced()
+ .divide(workOrder.getQuantity(), 4, RoundingMode.HALF_UP)
+ .multiply(BigDecimal.valueOf(100));
+ }
+ data.put("鐢熶骇杩涘害(%)", progressPercent.setScale(1, RoundingMode.HALF_UP));
+
+ // 鍓╀綑澶╂暟
+ int remainingDays = 0;
+ if (workOrder.getRequestDate() != null) {
+ remainingDays = (int) ChronoUnit.DAYS.between(LocalDate.now(), workOrder.getRequestDate().toLocalDate());
+ }
+ data.put("璺濈闇�姹傛棩鏈熷墿浣欏ぉ鏁�", Math.max(0, remainingDays));
+
+ // 浠诲姟瀹屾垚鎯呭喌
+ List<MesProTaskDO> tasks = taskMapper.selectListByWorkOrderId(workOrderId);
+ if (CollUtil.isNotEmpty(tasks)) {
+ List<Map<String, Object>> taskData = new ArrayList<>();
+ long finishedTasks = 0;
+ long totalDuration = 0;
+ for (MesProTaskDO task : tasks) {
+ Map<String, Object> t = new LinkedHashMap<>();
+ t.put("浠诲姟缂栧彿", task.getCode());
+ t.put("浠诲姟鍚嶇О", task.getName());
+ t.put("浠诲姟鐘舵��", task.getStatus());
+ t.put("璁″垝鏁伴噺", task.getQuantity());
+ t.put("宸茬敓浜ф暟閲�", task.getProducedQuantity());
+ t.put("瀹為檯鏃堕暱(灏忔椂)", task.getDuration());
+ if (task.getStartTime() != null) t.put("寮�濮嬫椂闂�", task.getStartTime().toString());
+ if (task.getEndTime() != null) t.put("缁撴潫鏃堕棿", task.getEndTime().toString());
+ if (task.getLastFeedbackTime() != null)
+ t.put("鏈�鍚庢姤宸ユ椂闂�", task.getLastFeedbackTime().toString());
+ taskData.add(t);
+ if (task.getStatus() != null && task.getStatus() >= 3) finishedTasks++;
+ if (task.getDuration() != null) totalDuration += task.getDuration();
+ }
+ data.put("浠诲姟鍒楄〃", taskData);
+ data.put("宸插畬鎴愪换鍔℃暟", finishedTasks);
+ data.put("鎬讳换鍔℃暟", tasks.size());
+ data.put("宸插畬鎴愭�绘椂闀�(灏忔椂)", totalDuration);
+ }
+
+ // MPS 鍜屽鎴蜂俊鎭�
+ MesProMpsDO mps = mpsMapper.selectOne(
+ new LambdaQueryWrapper<MesProMpsDO>()
+ .eq(MesProMpsDO::getWorkOrderId, workOrderId));
+ if (mps != null) {
+ data.put("MPS 缂栧彿", mps.getCode());
+ data.put("閿�鍞鍗曞彿", mps.getSaleOrderNo());
+ data.put("MPS 闇�姹傛棩鏈�", mps.getRequestDate() != null ? mps.getRequestDate().toString() : "");
+ data.put("MPS 涓嬪彂鏃ユ湡", mps.getIssueDate() != null ? mps.getIssueDate().toString() : "");
+ fillClientInfo(data, mps.getClientId());
+ }
+
+ return data;
+ }
+
+ private MesProAiDeliveryPredictRespVO parseDeliveryResponse(String aiResponse) {
+ MesProAiDeliveryPredictRespVO respVO = new MesProAiDeliveryPredictRespVO();
+ try {
+ String json = extractJson(aiResponse);
+ Map<String, Object> parsed = JSONUtil.toBean(json, Map.class);
+ Object onTime = parsed.get("onTime");
+ if (onTime instanceof Boolean b) respVO.setOnTime(b);
+ Object confidence = parsed.get("confidence");
+ if (confidence instanceof Number num) respVO.setConfidence(num.doubleValue());
+ respVO.setReasoning((String) parsed.get("reasoning"));
+ @SuppressWarnings("unchecked")
+ List<String> delayFactors = (List<String>) parsed.get("delayFactors");
+ respVO.setDelayFactors(delayFactors);
+ respVO.setProgressPercent(toBigDecimal(parsed.get("progressPercent")));
+ respVO.setRemainingDays(toInteger(parsed.get("remainingDays")));
+ } catch (Exception e) {
+ log.warn("瑙f瀽 AI 浜や粯棰勬祴鍝嶅簲澶辫触: {}", aiResponse, e);
+ respVO.setReasoning(aiResponse);
+ }
+ return respVO;
+ }
+
+ // ==================== Shared Utilities ====================
+
+ private String extractJson(String aiResponse) {
+ int start = aiResponse.indexOf("{");
+ int end = aiResponse.lastIndexOf("}");
+ if (start >= 0 && end > start) {
+ return aiResponse.substring(start, end + 1);
+ }
+ return aiResponse;
+ }
+
+ private BigDecimal toBigDecimal(Object value) {
+ if (value == null) return BigDecimal.ZERO;
+ if (value instanceof BigDecimal v) return v;
+ if (value instanceof Number n) return BigDecimal.valueOf(n.doubleValue());
+ try {
+ return new BigDecimal(value.toString());
+ } catch (Exception e) {
+ return BigDecimal.ZERO;
+ }
+ }
+
+ private Integer toInteger(Object value) {
+ if (value == null) return 0;
+ if (value instanceof Integer v) return v;
+ if (value instanceof Number n) return n.intValue();
+ try {
+ return Integer.valueOf(value.toString());
+ } catch (Exception e) {
+ return 0;
+ }
+ }
+
+ private String resolveItemName(Long itemId) {
+ if (itemId == null) return "";
+ try {
+ MdmItemRespDTO item = mdmItemApi.getItem(itemId).getCheckedData();
+ if (item != null && StrUtil.isNotBlank(item.getName())) return item.getName();
+ } catch (Exception ignored) {}
+ return "鐗╂枡" + itemId;
+ }
+
+ private String resolveProcessName(Long processId) {
+ if (processId == null) return "";
+ try {
+ MesProProcessDO process = processService.getProcess(processId);
+ if (process != null && StrUtil.isNotBlank(process.getName())) return process.getName();
+ } catch (Exception ignored) {}
+ return "宸ュ簭" + processId;
+ }
+
+ private void fillItemInfo(Map<String, Object> data, Long itemId) {
+ if (itemId == null) return;
+ try {
+ MdmItemRespDTO item = mdmItemApi.getItem(itemId).getCheckedData();
+ if (item != null) {
+ data.put("鐗╂枡缂栫爜", item.getCode() != null ? item.getCode() : itemId);
+ data.put("鐗╂枡鍚嶇О", item.getName() != null ? item.getName() : "");
+ }
+ } catch (Exception e) {
+ data.put("鐗╂枡ID", itemId);
+ }
+ }
+
+ private void fillClientInfo(Map<String, Object> data, Long clientId) {
+ if (clientId == null) return;
+ try {
+ CrmCustomerRespDTO customer = crmCustomerApi.getCustomer(clientId);
+ if (customer != null) {
+ data.put("瀹㈡埛鍚嶇О", customer.getName() != null ? customer.getName() : "");
+ }
+ } catch (Exception e) {
+ data.put("瀹㈡埛ID", clientId);
+ }
+ }
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ai/MesQcAiService.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ai/MesQcAiService.java
new file mode 100644
index 0000000..2d920c9
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ai/MesQcAiService.java
@@ -0,0 +1,19 @@
+package cn.iocoder.yudao.module.mes.service.qc.ai;
+
+import cn.iocoder.yudao.module.mes.controller.admin.qc.ai.vo.MesQcAiSuggestReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.ai.vo.MesQcAiSuggestRespVO;
+
+/**
+ * MES QC AI 杈呭姪鍒ゅ畾 Service 鎺ュ彛
+ */
+public interface MesQcAiService {
+
+ /**
+ * AI 杈呭姪鍒ゅ畾妫�楠岀粨鏋�
+ *
+ * @param reqVO 璇锋眰鍙傛暟
+ * @return AI 鍒ゅ畾寤鸿
+ */
+ MesQcAiSuggestRespVO suggestCheckResult(MesQcAiSuggestReqVO reqVO);
+
+}
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ai/MesQcAiServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ai/MesQcAiServiceImpl.java
new file mode 100644
index 0000000..2005d29
--- /dev/null
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ai/MesQcAiServiceImpl.java
@@ -0,0 +1,284 @@
+package cn.iocoder.yudao.module.mes.service.qc.ai;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.json.JSONUtil;
+import cn.iocoder.yudao.module.ai.api.chat.AiChatApi;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.ai.vo.MesQcAiSuggestReqVO;
+import cn.iocoder.yudao.module.mes.controller.admin.qc.ai.vo.MesQcAiSuggestRespVO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defectrecord.MesQcDefectRecordDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicatorresult.MesQcIndicatorResultDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicatorresult.MesQcIndicatorResultDetailDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.qc.ipqc.MesQcIpqcDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.qc.oqc.MesQcOqcDO;
+import cn.iocoder.yudao.module.mes.dal.dataobject.qc.rqc.MesQcRqcDO;
+import cn.iocoder.yudao.module.mes.dal.mysql.qc.indicatorresult.MesQcIndicatorResultMapper;
+import cn.iocoder.yudao.module.mes.dal.mysql.qc.indicatorresult.MesQcIndicatorResultDetailMapper;
+import cn.iocoder.yudao.module.mes.dal.mysql.qc.defectrecord.MesQcDefectRecordMapper;
+import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
+import cn.iocoder.yudao.module.mes.enums.qc.MesQcCheckResultEnum;
+import cn.iocoder.yudao.module.mes.service.qc.indicator.MesQcIndicatorService;
+import cn.iocoder.yudao.module.mes.service.qc.iqc.MesQcIqcService;
+import cn.iocoder.yudao.module.mes.service.qc.ipqc.MesQcIpqcService;
+import cn.iocoder.yudao.module.mes.service.qc.oqc.MesQcOqcService;
+import cn.iocoder.yudao.module.mes.service.qc.rqc.MesQcRqcService;
+import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
+import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
+import cn.iocoder.yudao.module.srm.api.supplier.SrmSupplierApi;
+import cn.iocoder.yudao.module.srm.api.supplier.dto.SrmSupplierRespDTO;
+import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
+import cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
+
+@Service
+@Slf4j
+public class MesQcAiServiceImpl implements MesQcAiService {
+
+ private static final String SYSTEM_PROMPT = """
+ 浣犳槸涓�浣嶈祫娣辩殑璐ㄦ涓撳锛屾搮闀挎牴鎹楠屾暟鎹垽瀹氫骇鍝佹槸鍚﹀悎鏍笺��
+ 浣犻渶瑕佺患鍚堝垎鏋愪互涓嬩俊鎭紝缁欏嚭涓撲笟鐨勫垽瀹氬缓璁細
+
+ ## 鍒ゅ畾瑙勫垯
+ 1. 鍚堟牸锛堢紪鐮�1锛夛細鎵�鏈夋寚鏍囧潎鍦ㄩ槇鍊艰寖鍥村唴锛屾棤鑷村懡/涓ラ噸缂洪櫡
+ 2. 鐗归噰锛堢紪鐮�2锛夛細涓埆鎸囨爣杞诲井瓒呭樊锛屽彲璁╂鎺ユ敹
+ 3. 涓嶅悎鏍奸��璐э紙缂栫爜3锛夛細澶氶」鎸囨爣瓒呭樊鎴栧瓨鍦ㄤ弗閲嶇己闄凤紝涓嶅彲鎺ユ敹
+ 4. 涓嶅悎鏍兼姤搴燂紙缂栫爜4锛夛細瀛樺湪鑷村懡缂洪櫡鎴栧椤逛弗閲嶈秴宸紝鏃犳硶浣跨敤
+
+ ## 杈撳嚭鏍煎紡
+ 璇锋寜浠ヤ笅JSON鏍煎紡杩斿洖锛堜笉瑕佸寘鍚叾浠栧唴瀹癸級锛�
+ {"suggestedResult": 1, "reasoning": "鍒ゅ畾鐞嗙敱...", "keyPoints": ["鍏虫敞鐐�1", "鍏虫敞鐐�2"]}
+
+ suggestedResult 鍙栧�硷細1鍚堟牸 2鐗归噰 3涓嶅悎鏍奸��璐� 4涓嶅悎鏍兼姤搴�
+ reasoning锛氱畝鏄庢壖瑕佺殑鍒ゅ畾鐞嗙敱
+ keyPoints锛氶渶瑕侀噸鐐瑰叧娉ㄧ殑鎸囨爣鎴栫己闄锋竻鍗�""";
+
+ @Resource
+ private MesQcIqcService iqcService;
+ @Resource
+ private MesQcIpqcService ipqcService;
+ @Resource
+ private MesQcOqcService oqcService;
+ @Resource
+ private MesQcRqcService rqcService;
+
+ @Resource
+ private MesQcIndicatorResultMapper indicatorResultMapper;
+ @Resource
+ private MesQcIndicatorResultDetailMapper indicatorResultDetailMapper;
+ @Resource
+ private MesQcDefectRecordMapper defectRecordMapper;
+ @Resource
+ private MesQcIndicatorService indicatorService;
+
+ @Resource
+ private MdmItemApi mdmItemApi;
+ @Resource
+ private SrmSupplierApi srmSupplierApi;
+ @Resource
+ private CrmCustomerApi crmCustomerApi;
+
+ @Resource
+ private AiChatApi aiChatApi;
+
+ @Override
+ public MesQcAiSuggestRespVO suggestCheckResult(MesQcAiSuggestReqVO reqVO) {
+ try {
+ // 1. 鏀堕泦璐ㄦ鏁版嵁
+ Map<String, Object> qcData = gatherQcData(reqVO.getQcId(), reqVO.getQcType());
+
+ // 2. 鏋勫缓 Prompt 骞惰皟鐢� AI
+ String userMessage = JSONUtil.toJsonPrettyStr(qcData);
+ String aiResponse = aiChatApi.chat(SYSTEM_PROMPT, userMessage);
+ log.info("AI 鍒ゅ畾缁撴灉: qcId={}, qcType={}, response={}",
+ reqVO.getQcId(), reqVO.getQcType(), aiResponse);
+
+ // 3. 瑙f瀽 AI 杩斿洖
+ return parseAiResponse(aiResponse);
+ } catch (Exception e) {
+ log.warn("AI 杈呭姪鍒ゅ畾澶辫触锛宷cId={}, qcType={}", reqVO.getQcId(), reqVO.getQcType(), e);
+ MesQcAiSuggestRespVO fallback = new MesQcAiSuggestRespVO();
+ fallback.setReasoning("AI 鍒ゅ畾鏆傛椂涓嶅彲鐢紝璇蜂汉宸ュ垽瀹�");
+ return fallback;
+ }
+ }
+
+ private Map<String, Object> gatherQcData(Long qcId, Integer qcType) {
+ Map<String, Object> data = new LinkedHashMap<>();
+
+ // 璐ㄦ鍗曞ご淇℃伅
+ if (MesBizTypeConstants.QC_IQC == qcType) {
+ data.put("璐ㄦ绫诲瀷", "鏉ユ枡妫�楠�");
+ MesQcIqcDO iqc = iqcService.getIqc(qcId);
+ if (iqc == null) throw exception(QC_IQC_NOT_EXISTS);
+ data.put("妫�楠屽崟缂栧彿", iqc.getCode());
+ data.put("鎺ユ敹鏁伴噺", iqc.getReceivedQuantity());
+ data.put("妫�娴嬫暟閲�", iqc.getCheckQuantity());
+ data.put("鍚堟牸鏁伴噺", iqc.getQualifiedQuantity());
+ data.put("涓嶅悎鏍兼暟閲�", iqc.getUnqualifiedQuantity());
+ data.put("缂洪櫡缁熻", Map.of(
+ "鑷村懡缂洪櫡鐜�", iqc.getCriticalRate() != null ? iqc.getCriticalRate() + "%" : "0%",
+ "涓ラ噸缂洪櫡鐜�", iqc.getMajorRate() != null ? iqc.getMajorRate() + "%" : "0%",
+ "杞诲井缂洪櫡鐜�", iqc.getMinorRate() != null ? iqc.getMinorRate() + "%" : "0%",
+ "鑷村懡缂洪櫡鏁�", iqc.getCriticalQuantity() != null ? iqc.getCriticalQuantity() : 0,
+ "涓ラ噸缂洪櫡鏁�", iqc.getMajorQuantity() != null ? iqc.getMajorQuantity() : 0,
+ "杞诲井缂洪櫡鏁�", iqc.getMinorQuantity() != null ? iqc.getMinorQuantity() : 0
+ ));
+ fillItemInfo(data, iqc.getItemId());
+ fillVendorInfo(data, iqc.getVendorId());
+ } else if (MesBizTypeConstants.QC_IPQC == qcType) {
+ data.put("璐ㄦ绫诲瀷", "杩囩▼妫�楠�");
+ MesQcIpqcDO ipqc = ipqcService.getIpqc(qcId);
+ if (ipqc == null) throw exception(QC_IPQC_NOT_EXISTS);
+ data.put("妫�楠屽崟缂栧彿", ipqc.getCode());
+ data.put("妫�娴嬫暟閲�", ipqc.getCheckQuantity());
+ data.put("鍚堟牸鏁伴噺", ipqc.getQualifiedQuantity());
+ data.put("涓嶅悎鏍兼暟閲�", ipqc.getUnqualifiedQuantity());
+ data.put("宸ュ崟ID", ipqc.getWorkOrderId());
+ data.put("宸ュ簭ID", ipqc.getProcessId());
+ fillItemInfo(data, ipqc.getItemId());
+ } else if (MesBizTypeConstants.QC_OQC == qcType) {
+ data.put("璐ㄦ绫诲瀷", "鍑鸿揣妫�楠�");
+ MesQcOqcDO oqc = oqcService.getOqc(qcId);
+ if (oqc == null) throw exception(QC_OQC_NOT_EXISTS);
+ data.put("妫�楠屽崟缂栧彿", oqc.getCode());
+ data.put("鍑鸿揣鏁伴噺", oqc.getOutQuantity());
+ data.put("妫�娴嬫暟閲�", oqc.getCheckQuantity());
+ data.put("鍚堟牸鏁伴噺", oqc.getQualifiedQuantity());
+ data.put("涓嶅悎鏍兼暟閲�", oqc.getUnqualifiedQuantity());
+ data.put("鏈�灏忔娴嬫暟閲�", oqc.getMinCheckQuantity());
+ data.put("鏈�澶т笉鍚堟牸鏁伴噺", oqc.getMaxUnqualifiedQuantity());
+ fillItemInfo(data, oqc.getItemId());
+ fillClientInfo(data, oqc.getClientId());
+ } else if (MesBizTypeConstants.QC_RQC == qcType) {
+ data.put("璐ㄦ绫诲瀷", "閫�璐ф楠�");
+ MesQcRqcDO rqc = rqcService.getRqc(qcId);
+ if (rqc == null) throw exception(QC_RQC_NOT_EXISTS);
+ data.put("妫�楠屽崟缂栧彿", rqc.getCode());
+ data.put("妫�娴嬫暟閲�", rqc.getCheckQuantity());
+ data.put("鍚堟牸鏁伴噺", rqc.getQualifiedQuantity());
+ data.put("涓嶅悎鏍兼暟閲�", rqc.getUnqualifiedQuantity());
+ fillItemInfo(data, rqc.getItemId());
+ }
+
+ // 鎸囨爣妫�楠岀粨鏋�
+ List<MesQcIndicatorResultDO> results = indicatorResultMapper.selectListByQcIdAndType(qcId, qcType);
+ if (CollUtil.isNotEmpty(results)) {
+ List<Map<String, Object>> indicatorData = new ArrayList<>();
+ for (MesQcIndicatorResultDO result : results) {
+ List<MesQcIndicatorResultDetailDO> details =
+ indicatorResultDetailMapper.selectListByResultId(result.getId());
+ if (CollUtil.isEmpty(details)) continue;
+
+ Map<String, Object> sample = new LinkedHashMap<>();
+ sample.put("鏍峰搧缂栧彿", result.getCode());
+ sample.put("SN", result.getSn() != null ? result.getSn() : "");
+ List<Map<String, String>> values = new ArrayList<>();
+ for (MesQcIndicatorResultDetailDO detail : details) {
+ Map<String, String> item = new LinkedHashMap<>();
+ String indicatorName = indicatorService.getIndicator(detail.getIndicatorId()).getName();
+ item.put("鎸囨爣", indicatorName);
+ item.put("瀹炴祴鍊�", detail.getValue());
+ values.add(item);
+ }
+ sample.put("妫�娴嬪��", values);
+ indicatorData.add(sample);
+ }
+ data.put("妫�楠屾寚鏍囩粨鏋�", indicatorData);
+ }
+
+ // 缂洪櫡璁板綍
+ List<MesQcDefectRecordDO> defects = defectRecordMapper.selectListByQcTypeAndQcId(qcType, qcId);
+ if (CollUtil.isNotEmpty(defects)) {
+ List<Map<String, Object>> defectData = defects.stream().map(d -> {
+ Map<String, Object> item = new LinkedHashMap<>();
+ item.put("缂洪櫡鍚嶇О", d.getName());
+ item.put("缂洪櫡绛夌骇", d.getLevel());
+ item.put("缂洪櫡鏁伴噺", d.getQuantity());
+ item.put("澶囨敞", d.getRemark() != null ? d.getRemark() : "");
+ return item;
+ }).collect(Collectors.toList());
+ data.put("缂洪櫡璁板綍", defectData);
+ }
+
+ return data;
+ }
+
+ private void fillItemInfo(Map<String, Object> data, Long itemId) {
+ if (itemId == null) return;
+ try {
+ MdmItemRespDTO item = mdmItemApi.getItem(itemId).getCheckedData();
+ if (item != null) {
+ data.put("鐗╂枡缂栫爜", item.getCode() != null ? item.getCode() : itemId);
+ data.put("鐗╂枡鍚嶇О", item.getName() != null ? item.getName() : "");
+ }
+ } catch (Exception e) {
+ data.put("鐗╂枡ID", itemId);
+ }
+ }
+
+ private void fillVendorInfo(Map<String, Object> data, Long vendorId) {
+ if (vendorId == null) return;
+ try {
+ SrmSupplierRespDTO vendor = srmSupplierApi.getSupplier(vendorId).getCheckedData();
+ if (vendor != null) {
+ data.put("渚涘簲鍟嗗悕绉�", vendor.getName() != null ? vendor.getName() : "");
+ }
+ } catch (Exception e) {
+ data.put("渚涘簲鍟咺D", vendorId);
+ }
+ }
+
+ private void fillClientInfo(Map<String, Object> data, Long clientId) {
+ if (clientId == null) return;
+ try {
+ CrmCustomerRespDTO customer = crmCustomerApi.getCustomer(clientId);
+ if (customer != null) {
+ data.put("瀹㈡埛鍚嶇О", customer.getName() != null ? customer.getName() : "");
+ }
+ } catch (Exception e) {
+ data.put("瀹㈡埛ID", clientId);
+ }
+ }
+
+ private MesQcAiSuggestRespVO parseAiResponse(String aiResponse) {
+ MesQcAiSuggestRespVO respVO = new MesQcAiSuggestRespVO();
+ try {
+ // 鎻愬彇 JSON 閮ㄥ垎锛圓I 鍙兘鍖呰9鍦� markdown 浠g爜鍧椾腑锛�
+ String json = aiResponse;
+ int start = json.indexOf("{");
+ int end = json.lastIndexOf("}");
+ if (start >= 0 && end > start) {
+ json = json.substring(start, end + 1);
+ }
+ Map<String, Object> parsed = JSONUtil.toBean(json, Map.class);
+ Object result = parsed.get("suggestedResult");
+ if (result instanceof Number num) {
+ respVO.setSuggestedResult(num.intValue());
+ // 鏄犲皠缁撴灉鍚嶇О
+ for (MesQcCheckResultEnum e : MesQcCheckResultEnum.values()) {
+ if (e.getType().equals(num.intValue())) {
+ respVO.setSuggestedResultName(e.getName());
+ break;
+ }
+ }
+ }
+ respVO.setReasoning((String) parsed.get("reasoning"));
+ @SuppressWarnings("unchecked")
+ List<String> keyPoints = (List<String>) parsed.get("keyPoints");
+ respVO.setKeyPoints(keyPoints);
+ } catch (Exception e) {
+ log.warn("瑙f瀽 AI 鍝嶅簲澶辫触锛岃繑鍥炲師濮嬪唴瀹�: {}", aiResponse, e);
+ respVO.setReasoning(aiResponse);
+ }
+ return respVO;
+ }
+
+}
diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notify/NotifyMessageMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notify/NotifyMessageMapper.java
index e9ce6d7..009a9f5 100644
--- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notify/NotifyMessageMapper.java
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notify/NotifyMessageMapper.java
@@ -67,4 +67,10 @@
.eq(NotifyMessageDO::getUserType, userType));
}
+ default int deleteByTemplateCodeAndNoticeId(String templateCode, Long noticeId) {
+ return delete(new QueryWrapperX<NotifyMessageDO>()
+ .eq("template_code", templateCode)
+ .apply("JSON_EXTRACT(template_params, '$.noticeId') = {0}", noticeId));
+ }
+
}
diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml
index d275fc8..697065d 100644
--- a/yudao-server/pom.xml
+++ b/yudao-server/pom.xml
@@ -102,11 +102,11 @@
</dependency>
<!-- AI 澶фā鍨嬬浉鍏虫ā鍧椼�傞粯璁ゆ敞閲婏紝淇濊瘉缂栬瘧閫熷害 -->
-<!-- <dependency>-->
-<!-- <groupId>cn.iocoder.boot</groupId>-->
-<!-- <artifactId>yudao-module-ai</artifactId>-->
-<!-- <version>${revision}</version>-->
-<!-- </dependency>-->
+ <dependency>
+ <groupId>cn.iocoder.boot</groupId>
+ <artifactId>yudao-module-ai</artifactId>
+ <version>${revision}</version>
+ </dependency>
<!-- IoT 鐗╄仈缃戠浉鍏虫ā鍧椼�傞粯璁ゆ敞閲婏紝淇濊瘉缂栬瘧閫熷害 -->
<!-- <dependency>-->
diff --git a/yudao-server/src/main/resources/application-dev.yaml b/yudao-server/src/main/resources/application-dev.yaml
index 84855ce..1f1fdf4 100644
--- a/yudao-server/src/main/resources/application-dev.yaml
+++ b/yudao-server/src/main/resources/application-dev.yaml
@@ -11,17 +11,6 @@
- org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreAutoConfiguration # 绂佺敤 AI 妯″潡鐨� Redis锛屾墜鍔ㄥ垱寤�
- org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreAutoConfiguration # 绂佺敤 AI 妯″潡鐨� Milvus锛屾墜鍔ㄥ垱寤�
- org.dromara.trans.config.TransServiceConfig # TODO 鑺嬭壙锛歟asy-trans 鍏煎 Spring Boot 4 鍚庣Щ闄わ紙TransServiceConfig 寮曠敤浜嗗凡绉婚櫎鐨� RestTemplateBuilder锛�
- # TODO 鑺嬭壙锛歴pring-ai-alibaba 2.0.0-M1.1 鐨� DashScope 鑷姩閰嶇疆鏆備笉鍏煎 Spring AI 2.0.0
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeChatAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAgentAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeImageAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeVideoAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAudioSpeechAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAudioTranscriptionAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeRerankAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeEmbeddingAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeMultimodalEmbeddingAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAsyncToolCallingManagerAutoConfiguration
# 鏁版嵁婧愰厤缃」
datasource:
druid: # Druid 銆愮洃鎺с�戠浉鍏崇殑鍏ㄥ眬閰嶇疆
diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml
index c9c3a18..66b7e46 100644
--- a/yudao-server/src/main/resources/application-local.yaml
+++ b/yudao-server/src/main/resources/application-local.yaml
@@ -11,17 +11,22 @@
- org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreAutoConfiguration # 绂佺敤 AI 妯″潡鐨� Redis锛屾墜鍔ㄥ垱寤�
- org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreAutoConfiguration # 绂佺敤 AI 妯″潡鐨� Milvus锛屾墜鍔ㄥ垱寤�
- org.dromara.trans.config.TransServiceConfig # TODO 鑺嬭壙锛歟asy-trans 鍏煎 Spring Boot 4 鍚庣Щ闄わ紙TransServiceConfig 寮曠敤浜嗗凡绉婚櫎鐨� RestTemplateBuilder锛�
- # TODO 鑺嬭壙锛歴pring-ai-alibaba 2.0.0-M1.1 鐨� DashScope 鑷姩閰嶇疆鏆備笉鍏煎 Spring AI 2.0.0
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeChatAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAgentAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeImageAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeVideoAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAudioSpeechAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAudioTranscriptionAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeRerankAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeEmbeddingAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeMultimodalEmbeddingAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAsyncToolCallingManagerAutoConfiguration
+ # 绂佺敤鏈厤缃� API Key 鐨勬ā鍨嬭嚜鍔ㄩ厤缃紙鍙繚鐣欓�氫箟鍗冮棶 Embedding 鐨勬墜鍔ㄩ厤缃級
+ - org.springframework.ai.model.openai.autoconfigure.OpenAiChatAutoConfiguration
+ - org.springframework.ai.model.openai.autoconfigure.OpenAiEmbeddingAutoConfiguration
+ - org.springframework.ai.model.openai.autoconfigure.OpenAiImageAutoConfiguration
+ - org.springframework.ai.model.openai.autoconfigure.OpenAiAudioSpeechAutoConfiguration
+ - org.springframework.ai.model.openai.autoconfigure.OpenAiAudioTranscriptionAutoConfiguration
+ - org.springframework.ai.model.openai.autoconfigure.OpenAiModerationAutoConfiguration
+ - org.springframework.ai.model.deepseek.autoconfigure.DeepSeekChatAutoConfiguration
+ - org.springframework.ai.model.anthropic.autoconfigure.AnthropicChatAutoConfiguration
+ - org.springframework.ai.model.google.genai.autoconfigure.chat.GoogleGenAiChatAutoConfiguration
+ - org.springframework.ai.model.google.genai.autoconfigure.embedding.GoogleGenAiEmbeddingConnectionAutoConfiguration
+ - org.springframework.ai.model.google.genai.autoconfigure.embedding.GoogleGenAiTextEmbeddingAutoConfiguration
+ - org.springframework.ai.model.ollama.autoconfigure.OllamaApiAutoConfiguration
+ - org.springframework.ai.model.ollama.autoconfigure.OllamaChatAutoConfiguration
+ - org.springframework.ai.model.ollama.autoconfigure.OllamaEmbeddingAutoConfiguration
+ - org.springframework.ai.model.stabilityai.autoconfigure.StabilityAiImageAutoConfiguration
# 鏁版嵁婧愰厤缃」
datasource:
druid: # Druid 銆愮洃鎺с�戠浉鍏崇殑鍏ㄥ眬閰嶇疆
@@ -213,6 +218,8 @@
cn.iocoder.yudao.module.im.dal.mysql: debug
org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 鑺嬭壙锛氬厛绂佺敤锛孲pring Boot 3.X 瀛樺湪閮ㄥ垎閿欒鐨� WARN 鎻愮ず
+--- #################### AI 鐩稿叧閰嶇疆 ####################
+
debug: false
--- #################### 寰俊鍏紬鍙枫�佸皬绋嬪簭鐩稿叧閰嶇疆 ####################
diff --git a/yudao-server/src/main/resources/application-test.yaml b/yudao-server/src/main/resources/application-test.yaml
index fd03e38..4bf8185 100644
--- a/yudao-server/src/main/resources/application-test.yaml
+++ b/yudao-server/src/main/resources/application-test.yaml
@@ -11,17 +11,6 @@
- org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreAutoConfiguration # 绂佺敤 AI 妯″潡鐨� Redis锛屾墜鍔ㄥ垱寤�
- org.springframework.ai.vectorstore.milvus.autoconfigure.MilvusVectorStoreAutoConfiguration # 绂佺敤 AI 妯″潡鐨� Milvus锛屾墜鍔ㄥ垱寤�
- org.dromara.trans.config.TransServiceConfig # TODO 鑺嬭壙锛歟asy-trans 鍏煎 Spring Boot 4 鍚庣Щ闄わ紙TransServiceConfig 寮曠敤浜嗗凡绉婚櫎鐨� RestTemplateBuilder锛�
- # TODO 鑺嬭壙锛歴pring-ai-alibaba 2.0.0-M1.1 鐨� DashScope 鑷姩閰嶇疆鏆備笉鍏煎 Spring AI 2.0.0
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeChatAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAgentAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeImageAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeVideoAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAudioSpeechAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAudioTranscriptionAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeRerankAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeEmbeddingAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeMultimodalEmbeddingAutoConfiguration
- - com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeAsyncToolCallingManagerAutoConfiguration
# 鏁版嵁婧愰厤缃」
datasource:
druid: # Druid 銆愮洃鎺с�戠浉鍏崇殑鍏ㄥ眬閰嶇疆
diff --git a/yudao-server/src/main/resources/application.yaml b/yudao-server/src/main/resources/application.yaml
index 17509ee..95e3241 100644
--- a/yudao-server/src/main/resources/application.yaml
+++ b/yudao-server/src/main/resources/application.yaml
@@ -151,34 +151,10 @@
database-name: default # Milvus 涓暟鎹簱鐨勫悕绉�
collection-name: knowledge_segment # Milvus 涓泦鍚堢殑鍚嶇О锛氱敤浜庡瓨鍌ㄥ悜閲忔暟鎹殑闆嗗悎鏍囪瘑绗︼紝鎵�鏈夌浉鍏崇殑鍚戦噺鎿嶄綔閮戒細鍦ㄨ繖涓泦鍚堜腑杩涜
client:
- host: 127.0.0.1
+ host: 36.213.180.217
port: 19530
- openai: # OpenAI 瀹樻柟
- base-url: ${OPENAI_BASE_URL}
- api-key: ${OPENAI_API_KEY}
- anthropic: # Anthropic Claude
- base-url: ${ANTHROPIC_BASE_URL}
- api-key: ${ANTHROPIC_API_KEY}
- ollama:
- base-url: http://127.0.0.1:11434
- chat:
- model: llama3
- stabilityai:
- api-key: sk-e53UqbboF8QJCscYvzJscJxJXoFcFg4iJjl1oqgE7baJETmx
- dashscope: # 閫氫箟鍗冮棶
- api-key: ${DASHSCOPE_API_KEY}
- deepseek: # DeepSeek
- api-key: ${DEEPSEEK_API_KEY}
- chat:
- options:
- model: deepseek-v4-flash
- google:
- genai: # 璋锋瓕 Gemini
- api-key: ${GEMINI_API_KEY}
- chat:
- model: gemini-2.5-flash
model:
- rerank: false # 鏄惁寮�鍚�滈�氫箟鍗冮棶鈥濈殑 Rerank 妯″瀷锛屽~鍐� dashscope 寮�鍚�
+ rerank: false # 鏄惁寮�鍚� Rerank 妯″瀷
mcp:
server:
enabled: false
@@ -199,60 +175,6 @@
yudao:
ai:
- doubao: # 瀛楄妭璞嗗寘
- enable: true
- api-key: ${DOUBAO_API_KEY}
- model: doubao-seed-2-1-turbo-260628
- hunyuan: # 鑵捐娣峰厓
- enable: true
- api-key: ${HUNYUAN_API_KEY}
- model: hy3-preview
- siliconflow: # 纭呭熀娴佸姩
- enable: true
- api-key: ${SILICONFLOW_API_KEY}
- model: deepseek-ai/DeepSeek-V4-Pro
- xinghuo: # 璁鏄熺伀
- enable: true
- api-key: ${XINGHUO_API_KEY}
- model: x2-flash
- baichuan: # 鐧惧窛鏅鸿兘
- enable: true
- api-key: ${BAICHUAN_API_KEY}
- model: Baichuan-M3
- yiyan: # 鏂囧績涓�瑷�
- enable: false
- api-key: ${YIYAN_API_KEY}
- model: ernie-5.1
- zhipu: # 鏅鸿氨 AI锛圙LM锛�
- enable: false
- api-key: ${ZHIPU_API_KEY}
- model: glm-5.2
- minimax: # MiniMax
- enable: false
- api-key: ${MINIMAX_API_KEY}
- model: MiniMax-M3
- moonshot: # 鏈堜箣鏆楅潰锛圞IMI锛�
- enable: false
- api-key: ${MOONSHOT_API_KEY}
- model: kimi-k2.6
- stepfun: # 闃惰穬鏄熻景
- enable: false
- api-key: ${STEPFUN_API_KEY}
- model: step-3.7-flash
- grok: # Grok
- enable: false
- api-key: ${GROK_API_KEY}
- model: grok-4.3
- midjourney:
- enable: true
- # base-url: https://api.holdai.top/mj-relax/mj
- base-url: https://api.holdai.top/mj
- api-key: sk-dZEPiVaNcT3FHhef51996bAa0bC74806BeAb620dA5Da10Bf
- notify-url: http://java.nat300.top/admin-api/ai/image/midjourney/notify
- suno:
- enable: true
- # base-url: https://suno-55ishh05u-status2xxs-projects.vercel.app
- base-url: http://127.0.0.1:3001
web-search:
enable: true
api-key: sk-40500e52840f4d24b956d0b1d80d9abe
--
Gitblit v1.9.3