629 Commits

Author SHA1 Message Date
ageerle
13800dc389 Merge pull request #277 from XiaoEns/v3.0.0
fix: 修复知识库附件上传乱码问题
2026-03-17 22:07:32 +08:00
xiaoen
619d9b1e84 fix: 兼容旧代码,添加deepseek服务调用 2026-03-17 21:30:56 +08:00
xiaoen
556cc93f14 fix: 修复知识库附件上传乱码问题 2026-03-17 18:57:30 +08:00
ageerle
a50375616e Merge pull request #276 from LM20230311/v3.0.0
V3.0.0
2026-03-17 14:09:52 +08:00
LM20230311
e33447d023 Merge branch 'v3.0.0' of https://github.com/ageerle/ruoyi-ai into v3.0.0 2026-03-17 13:48:21 +08:00
LM20230311
7be277b3e6 Merge branch 'v3.0.0-deploy' into v3.0.0 2026-03-17 13:47:27 +08:00
LM20230311
5fa385e90b docs: add Docker deployment instructions and service port mappings to README; update sys_client table in MySQL script 2026-03-17 11:09:13 +08:00
LM20230311
0a78966737 chore: update UPSTREAM_HOST configuration in docker-compose-all.yaml to remove http:// prefix 2026-03-17 10:38:43 +08:00
LM20230311
0eb7f00867 chore: add environment variables for backend API configuration in docker-compose-all.yaml 2026-03-16 17:14:01 +08:00
LM20230311
00ce7f2d98 chore: update port mappings in docker-compose.yaml for MySQL, Weaviate, and backend services 2026-03-16 15:32:27 +08:00
ageerle
003c066361 chore(sql): 更新数据库脚本,移除chat_config表
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 23:57:55 +08:00
ageerle
a5e7c59fd4 refactor: 重构项目架构,优化向量服务
- 移除 Graph 知识图谱相关模块(Neo4j、GraphRAG等)
- 移除 demo、job、wechat 示例模块,简化项目结构
- 修复向量维度获取方式,改为从数据库配置读取
- 添加 gRPC BOM 依赖管理,解决 Milvus SDK 版本冲突
- 新增 PPIO 服务和 Embedding 提供者支持
- 清理冗余代码和未使用的依赖

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-15 23:39:30 +08:00
ageerle
accac603cf chore(sql): 更新数据库脚本v3版本
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 14:16:45 +08:00
ageerle
7c96c730e6 refactor(chat): 重构聊天服务架构,引入Handler模式
主要变更:
1. 移除ruoyi-ai-copilot模块
2. 重构docker配置目录结构,统一迁移至docs/docker/
3. 聊天服务引入Handler模式:
   - 新增ChatHandler接口及多种实现
   - DefaultChatHandler: 默认聊天处理
   - AgentChatHandler: Agent模式处理
   - WorkflowChatHandler: 工作流处理
   - ResumeChatHandler: 恢复会话处理
   - ChatContextBuilder: 上下文构建器
4. 简化AbstractStreamingChatService和ChatServiceFacade代码
5. 优化各Provider实现,统一代码风格

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 14:14:56 +08:00
LM20230311
a2dbdb30ff fix: remove unnecessary COPY command for ruoyi-modules-api in Dockerfile.backend 2026-03-11 09:57:47 +08:00
LM20230311
772e4af9bf Merge remote-tracking branch 'upstream/v3.0.0' into v3.0.0-deploy 2026-03-11 09:35:48 +08:00
ageerle
e601eb6db5 feat(wechat): 修复微信公众号无法登录 2026-03-09 10:27:53 +08:00
evo
84dbc2cfbf feat:恢复mcp模块 动态agent 2026-03-08 22:41:24 +08:00
evo
f160ec714b feat-恢复mcp模块 2026-03-07 15:53:06 +08:00
ageerle
6c4c661516 chore: 移除项目文档和技术交流链接
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 11:43:54 +08:00
ageerle
b85c17a126 refactor: 重构Issue模板为通用格式
- 删除企业合作登记模板
- 新增漏洞报告模板
- 新增想法建议模板
- 新增自定义模板

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 11:39:19 +08:00
ageerle
0690156362 refactor: 优化企业合作登记模板格式
- 改为评论登记模式,用户在Issue下评论填写
- 提供格式预览和可复制模板
- 新增公司Logo和项目Logo字段
- 移除联系方式模块,保护隐私

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 11:19:27 +08:00
ageerle
dc1e84bc52 feat: 添加企业AI应用合作登记Issue模板
- 新增企业合作登记模板,用于收集企业AI应用需求
- 包含基本信息、AI应用需求、联系方式三个模块
- 预设筛选字段便于评估合作匹配度

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 10:57:53 +08:00
ageerle
4977df0ba8 feat: 更新项目文档 2026-03-06 10:27:45 +08:00
ageerle
27211b67f9 feat: 更新项目文档 2026-03-06 10:20:15 +08:00
LM20230311
1600ab384e feat: 支持手动挂载数据卷; 2026-03-05 09:35:27 +08:00
LM20230311
418805a1ef feat: 源码docker部署完成; 2026-03-05 09:27:34 +08:00
LM20230311
4c4b52bca7 feat: 忽略data目录; 2026-03-05 09:07:10 +08:00
LM20230311
7245259bc4 fix: 忽略所有错误以确保SQL脚本继续执行 2026-03-04 17:57:08 +08:00
LM20230311
6690c8204c fix: 修复表数量异常问题; 2026-03-04 17:56:20 +08:00
LM20230311
d8fc597f85 fix: 解决mysql容器长时间建表导致失败问题; 2026-03-04 17:34:38 +08:00
evo
7f1146ecae Merge pull request #272 from MuSan-Li/v3.0.0
fix: 还原 McpAgent 为通用工具调用版本
2026-03-04 16:37:28 +08:00
evo
0130028952 fix: 还原 McpAgent 为通用工具调用版本 2026-03-04 16:34:56 +08:00
LM20230311
5a716da5a6 feat: 添加docker部署文件; 2026-03-04 16:31:54 +08:00
evo
2470ec7573 Merge pull request #271 from MuSan-Li/feature-20260218-add-mcp-model
增加McpAgent空格
2026-03-04 14:36:32 +08:00
evo
e3fb25fba6 增加McpAgent空格 2026-03-04 14:32:41 +08:00
ageerle
2aa7e0f4f1 Merge pull request #267 from StevenJack666/v3.0.0
V3.0.0
2026-03-02 09:10:12 +08:00
steve
b2d2fb34b8 Merge branch 'ageerle:v3.0.0' into v3.0.0 2026-03-01 11:37:10 +08:00
zhang
c1e2a03178 解决冲突 2026-03-01 11:29:58 +08:00
zhang
fd38d2030e 修改异常节点 2026-02-28 11:53:36 +08:00
zhang
a42f881b67 Merge branch 'vFuture3.0.0' into v3.0.0 2026-02-27 17:35:42 +08:00
zengxb
20d531c0db context:新增工作流节点提供模板消息输出以及智谱大模型Chat对话接入 2026-02-27 14:53:07 +08:00
ageerle
1e6046cf52 feat: 移除数字人模块 2026-02-27 14:00:57 +08:00
zhang
495f2de6ef Merge remote-tracking branch 'origin/v3.0.0' into v3.0.0 2026-02-27 10:30:33 +08:00
zhang
e97bd4e201 去掉分号 2026-02-27 10:14:53 +08:00
zhang
70e5e393ef Merge branch 'work_flow_v3.0' into v3.0.0
# Conflicts:
#	ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/ChatServiceFacade.java
2026-02-26 14:48:19 +08:00
zengxb
8954f59cd7 context:工作流和Ai Chat对话消息功能整合 2026-02-26 14:36:33 +08:00
ageerle
c78b1a14a9 feat: 增加ppio厂商支持 2026-02-25 21:20:22 +08:00
ageerle
e768c4b356 feat: 调整默认sql脚本 2026-02-24 22:46:47 +08:00
zhang
d059f50ba6 配置信息修改 2026-02-24 16:09:37 +08:00
zhang
26bcfbba8a 修改数据库读取工具 2026-02-24 16:07:18 +08:00
zengxb
d6e4a50d6e context:根据模型分类找到实现类修改为由模型供应商找到实现类(保持工作流和对话流程一致) 2026-02-24 10:58:11 +08:00
zengxb
0a115f289e context:通义万相文生图节点功能以及发送邮箱和HTTP请求节点调研 2026-02-24 10:34:26 +08:00
ageerle
31e8df8bc0 Merge pull request #262 from MuSan-Li/feature-20260218-add-mcp-model
add mcp model
2026-02-23 22:57:44 +08:00
evo
ee477213e0 优化mcp模块功能 2026-02-23 18:21:31 +08:00
evo
1e6e236d3c 优化导出报错提示 2026-02-23 16:07:38 +08:00
evo
593a0d0049 增加mcp工具模块 2026-02-23 16:07:13 +08:00
ageerle
d4f8f91893 Merge pull request #259 from StevenJack666/v3.0.0
chat对话接口如参改为ChatContext,方便后续扩展
2026-02-15 15:54:21 +08:00
zhang
f25ebdf9ec 去掉多余字符 2026-02-14 22:07:09 +08:00
zhang
32b8144b56 Merge branch 'work_flow_v3.0' into v3.0.0
# Conflicts:
#	ruoyi-common/ruoyi-common-chat/src/main/java/org/ruoyi/common/chat/Service/IChatService.java
#	ruoyi-modules/ruoyi-aiflow/src/main/java/org/ruoyi/workflow/workflow/WorkflowUtil.java
#	ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/AbstractStreamingChatService.java
#	ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/ChatServiceFacade.java
#	ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/service/chat/impl/provider/QianWenChatServiceImpl.java
2026-02-14 21:14:50 +08:00
zengxb
b40805cb57 context:公共聊天接口调整为对话上下文对象传输 2026-02-14 10:55:53 +08:00
steve
008f64617f Merge pull request #4 from StevenJack666/feature-v3.0.0
Feature v3.0.0
2026-02-14 10:49:52 +08:00
zhang
3539e222d2 init 2026-02-14 10:48:43 +08:00
zhang
c4d1ea974d init 2026-02-14 10:48:33 +08:00
ageerle
ace7e961a4 Merge pull request #258 from StevenJack666/v3.0.0
修改千问模型调用逻辑
2026-02-14 10:47:14 +08:00
steve
5dc21653c0 Merge pull request #3 from StevenJack666/feature-v3.0.0
Feature v3.0.0
2026-02-14 10:44:13 +08:00
zhang
d0d4b2229f 合并v3.0.0使用 2026-02-14 10:08:29 +08:00
zhang
07c9727345 去掉空格 2026-02-14 09:54:24 +08:00
zhang
689adf079d 修改千问模型调用逻辑 2026-02-13 22:41:08 +08:00
ageerle
1d1b72c6e2 Merge pull request #257 from StevenJack666/v3.0.0
V3.0.0
2026-02-13 19:02:16 +08:00
zhang
d9bdce1d8a init 参数调整 2026-02-13 18:37:33 +08:00
zhang
885d46a4aa AI工作流优化 2026-02-13 18:14:39 +08:00
zengxb
420e05ecf3 context:工作流与大模型聊天对话整合(新增Common-Chat公共对话接口) 2026-02-13 17:56:55 +08:00
ageerle
afa3c78180 Merge pull request #254 from StevenJack666/v3.0.0
修复异常场景,请求链接无法关闭问题
2026-02-11 17:03:44 +08:00
zhang
91a44e1ba8 非推理模式下,大模型调用失败。连接未关闭,且没有返回错误信息 2026-02-09 15:33:11 +08:00
zhang
4a36aaa780 修复异常场景,请求链接无法关闭问题 2026-02-09 14:58:46 +08:00
ageerle
c78b456dbf feat: 提交证书 2026-02-06 03:14:13 +08:00
ageerle
e8c187125f feat: 更新功能建议&bug提交文档 2026-02-06 03:08:33 +08:00
ageerle
7b8cfe02a1 v3.0.0 init 2026-02-06 03:00:23 +08:00
ageerle
eb2e8f3ff8 Merge pull request #250 from RobustH/main
feat: 对接ragflow的聊天助手
2025-12-25 04:56:05 +08:00
RobustH
2f7bff0bf0 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/RagFlowServiceImpl.java
2025-12-22 21:06:51 +08:00
Robust_H
c89f9657f3 feat: 添加RAGFLOW聊天服务实现 2025-12-22 21:06:13 +08:00
Robust_H
588c1730a6 feat: 添加RAGFLOW聊天服务实现 2025-12-19 21:08:19 +08:00
ageerle
3f1506b34b Merge pull request #249 from OpenX123/feature/添加多个实用MCP工具
Feature/添加多个实用mcp工具
2025-12-12 22:51:00 +08:00
OpenX123
deea428bfa 动态加载文件操作、图片搜索、PlantUML生成、网页搜索、终端命令、文档解析等实用MCP工具 2025-12-12 13:09:34 +08:00
OpenX123
66f133f089 Revert "为MCP服务器添加文件操作、图片搜索、PlantUML生成、网页搜索、终端命令、文档解析等实用功能"
This reverts commit 77aeabb4be.
2025-12-12 12:00:46 +08:00
ageerle
e5ed5d0ef6 Merge pull request #241 from stageluo/main
新增知识库、http分支工作流节点
2025-12-12 11:46:29 +08:00
ageerle
f04842ae12 Merge branch 'main' into main 2025-12-12 11:46:19 +08:00
OpenX123
77aeabb4be 为MCP服务器添加文件操作、图片搜索、PlantUML生成、网页搜索、终端命令、文档解析等实用功能 2025-12-11 20:41:04 +08:00
evo
f63ccbe7bd Merge pull request #247 from MuSan-Li/feature_20251207_add_ali_emb
feat: 添加阿里向量模型
2025-12-07 22:35:47 +08:00
evo
1087f86259 feat: 添加阿里向量模型 2025-12-07 22:32:42 +08:00
evo
a041acfdbb Merge pull request #246 from MuSan-Li/feature_20251205_format_code
feat: fix_database
2025-12-06 14:47:48 +08:00
evo
b854885c99 feat: fix_database 2025-12-06 14:44:55 +08:00
evo
777c899651 Merge pull request #245 from MuSan-Li/feature_20251205_format_code
feat: 全局格式化代码
2025-12-06 14:39:41 +08:00
evo
7c7d5838cd feat: 全局格式化代码 2025-12-06 14:38:41 +08:00
stageluo
995507e757 支持本地ollama集成LLM问答分支工作流 2025-11-25 09:28:14 +08:00
stageluo
f155bc284d 新增http分支工作流节点 2025-11-25 09:27:06 +08:00
stageluo
c22c5eac7f 新增知识库分支工作流节点 2025-11-25 09:26:39 +08:00
stageluo
9df246321e 条件分支工作流节点优化 2025-11-24 18:20:10 +08:00
ageerle
96c53390aa Merge pull request #239 from stageluo/main
关键词提取工作流节点
2025-11-20 21:48:34 +08:00
stageluo
8a1ac2264e 优化工作流注册节点以及相关其他代码优化 2025-11-20 10:09:48 +08:00
stageluo
35194457e1 条件分支工作流节点 2025-11-20 10:07:50 +08:00
stageluo
cf9eca34d3 邮件工作流节点 2025-11-19 17:44:51 +08:00
stageluo
f04db38d6c 关键词提取工作流节点 2025-11-19 15:03:24 +08:00
ageerle
53c5076212 feat: 修改默认配置 2025-11-18 10:09:16 +08:00
ageerle
0acee56149 feat: 演示模式下,过滤搜索操作 2025-11-12 11:08:10 +08:00
ageerle
be12ebadbe feat: 更新项目介绍 2025-11-07 18:37:56 +08:00
ageerle
a5fc55f50a feat: 更新项目介绍 2025-11-07 17:15:36 +08:00
ageerle
7186afbbc4 feat(yml): 默认关闭知识图谱功能,移除无用配置 2025-11-07 16:56:40 +08:00
ageerle
1fba109cce Merge pull request #237 from xiaonieli7/main
fix(billing): 当 knowledge.graph.enabled=false 时:所有知识图谱相关的 Bean 都不会被创建…
2025-11-07 16:53:17 +08:00
ageerle
d8382bfb44 feat(sql): 合并sql文件 2025-11-07 16:50:54 +08:00
xiaonieli7
2353b0adf6 Merge branch 'ageerle:main' into main 2025-11-07 16:26:46 +08:00
Administrator
dcacd8753f fix(billing): 当 knowledge.graph.enabled=false 时:所有知识图谱相关的 Bean 都不会被创建Neo4j 配置不会被加载即使没有配置 Neo4j 连接信息,默认禁用了知识图谱功能 2025-11-07 16:15:35 +08:00
ageerle
aca72a5892 Merge pull request #234 from seven-ocean/feature/aihuman
add:添加真人数字人
2025-11-07 14:42:18 +08:00
Maxchen
362307e4ba Merge remote-tracking branch 'origin/feature/aihuman' into feature/aihuman 2025-11-07 14:40:53 +08:00
Maxchen
cb26e452bb 完成火山引擎适配 2025-11-07 11:54:11 +08:00
Maxchen
e402330692 add:添加火山引擎语音合成 2025-11-06 19:43:52 +08:00
Maxchen
f3e1aa6cdd 从Git跟踪中移除application-dev.yml文件,使其不再被提交 2025-10-28 11:36:29 +08:00
Maxchen
c1d830bfd6 Resolve conflicts in application-dev.yml 2025-10-28 11:25:33 +08:00
Maxchen
c06ae8271d 排除generator.yml文件并更新.gitignore 2025-10-28 10:42:52 +08:00
Maxchen
e71d9faf8d 排除application-dev.yml文件并更新.gitignore 2025-10-28 10:32:55 +08:00
evo
f5e22d4c05 Merge pull request #235 from MuSan-Li/main
feat: 更新sql文件
2025-10-28 10:27:04 +08:00
lihao05
1aa5535769 feat: 更新sql文件 2025-10-28 10:17:15 +08:00
Maxchen
0d403b6725 add:添加真人数字人 2025-10-27 14:04:42 +08:00
ageerle
d9a9a7f0f0 feat(docs): 更新docker教程 2025-10-24 10:32:28 +08:00
ageerle
a36d306f40 Update README.md 2025-10-24 10:18:03 +08:00
evo
3164eb0bc9 Merge pull request #233 from MuSan-Li/main
feat: 更新sql文件 添加工作流样式接口
2025-10-24 10:17:11 +08:00
evo
adf04d9a60 Merge branch 'main' into main 2025-10-24 10:17:03 +08:00
lihao05
73e588ac60 feat: 更新sql文件 添加工作流样式接口 2025-10-24 10:13:01 +08:00
ageerle
3235b43a0c Merge pull request #232 from xiaonieli7/main
fix(billing): 解决pom文件冲突
2025-10-23 15:28:05 +08:00
Administrator
ae6edadf4b fix(billing): 解决pom文件冲突 2025-10-23 15:26:12 +08:00
ageerle
0f866ec91b Merge pull request #230 from xiaonieli7/main
fix(billing): 新增知识图谱构
2025-10-23 14:16:11 +08:00
ageerle
8f543380d7 Merge branch 'main' into main 2025-10-23 14:15:54 +08:00
Cyclone
ac570fd45c Merge pull request #231 from Cyclones-Y/main
feat(config): 修改默认向量库为weaviate,并更新数据库连接信息
2025-10-23 11:27:47 +08:00
Yzm
beef9e946a feat(config): 修改默认向量库为weaviate,并更新数据库连接信息 2025-10-23 11:24:47 +08:00
Administrator
3610899f2b fix(billing): 新增知识图谱构
1. 从非结构化文本中自动抽取实体和关系
2. 构建和管理知识图谱
3. 基于图谱的检索增强生成(GraphRAG)
4. 交互式图谱可视化
2025-10-23 09:48:49 +08:00
evo
65d59f4acf Merge pull request #229 from radish15/main
fix/修复工作流的问题
2025-10-22 13:59:55 +08:00
evo
cd4fc4b216 Merge pull request #228 from MuSan-Li/main
feat: 覆盖sql
2025-10-21 19:28:46 +08:00
lihao05
0ce0ce1262 feat: 覆盖sql 2025-10-21 18:49:48 +08:00
radish@2020
6b5fea27e0 fix: 修复工作流执行报错,没有用户消息 2025-10-21 18:00:46 +08:00
radish@2020
35c848b719 fix: 修复sql,mysql5.7的TEXT类型不支持默认值 2025-10-21 16:38:50 +08:00
evo
beaf384f79 Merge pull request #227 from MuSan-Li/feature_20250930_work_flow
Feature 20250930 work flow
2025-10-21 11:20:45 +08:00
lihao05
117faeac10 feat: 修改sql提交名称 2025-10-21 11:08:14 +08:00
lihao05
95951efe7a Merge branch 'feature_20250930_work_flow' of https://github.com/MuSan-Li/ruoyi-ai into main
# Conflicts:
#	pom.xml
#	ruoyi-admin/pom.xml
#	ruoyi-modules/pom.xml
#	ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/DeepSeekChatImpl.java
2025-10-21 10:17:50 +08:00
lihao05
da2000b4f4 feat: 添加工作流菜单表 2025-10-21 10:11:10 +08:00
lihao05
3c21bf6fd3 feat: 工作流第一版提交 2025-10-21 09:52:33 +08:00
evo
63f6df8af0 Merge pull request #224 from radish15/main
fix(chat):解决依赖冲突导致方法不存在的问题
2025-10-20 20:25:14 +08:00
lihao05
e7d7de79fe feat: 功能优化 2025-10-20 10:12:44 +08:00
ageerle
34a71cfc55 Merge pull request #226 from Cyclones-Y/main
完善Milvus向量存储策略实现
2025-10-18 13:42:32 +08:00
Yzm
e242a67c74 feat(embedding): 添加模型维度支持并重构向量存储策略
- 在ChatModelVo中添加dimension字段用于存储模型维度
- 重构EmbeddingModelFactory以支持按模型名称和维度创建实例
- 修改向量存储策略接口参数顺序并统一维度处理
- 为OpenAI和ZhiPuAI嵌入提供者添加维度配置支持
- 优化知识库服务中模型选择逻辑,添加回退机制
2025-10-17 19:55:24 +08:00
Yzm
9d4a0e0b36 refactor(vector-store): 优化Milvus向量存储策略实现
重构Milvus向量存储策略,引入连接缓存机制减少重复创建连接的开销
将vectorModelName重命名为vectorStoreName以更准确表达用途
移除默认配置值,改为必须显式配置
优化代码结构,减少重复代码
2025-10-17 17:19:01 +08:00
Yzm
962c2b693c refactor(vector-store): 移除VectorStoreStrategy接口并简化策略模式实现
移除VectorStoreStrategy接口,直接使用VectorStoreService作为策略接口
简化VectorStoreStrategyFactory实现,移除冗余方法
更新相关实现类以适配新的接口结构
2025-10-17 16:31:09 +08:00
Yzm
766f6ad266 refactor(milvus): 重构Milvus向量存储策略使用LangChain4j
将原有的直接Milvus客户端调用重构为使用LangChain4j的MilvusEmbeddingStore
简化了集合创建、数据存储和查询的实现逻辑
更新了相关依赖
2025-10-17 15:39:26 +08:00
Yzm
c85deba6a6 refactor(vector): 简化createSchema接口参数并更新相关实现
移除createSchema方法中冗余的modelName参数
更新Milvus向量维度为2048以匹配新模型
添加对embeddingModelName参数的支持
2025-10-17 14:29:29 +08:00
lihao05
9f6d363d55 feat: 代码格式化 2025-10-16 21:39:20 +08:00
lihao05
77ddd169c7 feat: 流程编排init 2025-10-16 21:38:00 +08:00
radish@2020
f384601933 fix(chat):解决依赖冲突导致方法不存在的问题 2025-10-14 10:30:43 +08:00
ageerle
0b1925cc62 fix(sql): 修复sql脚本 2025-10-13 14:50:28 +08:00
ageerle
3c237f45ad fix(chat): 修复依赖版本升级导致缺少类错误 2025-10-13 14:40:05 +08:00
ageerle
9500304b77 fix(chat): 修复依赖版本升级导致缺少类错误 2025-10-13 14:37:44 +08:00
ageerle
dbdacdad5c Merge pull request #165 from Code-Mr-Jiu/main
MCP相关功能----进程管理及mcp_info  CRUD
2025-10-12 19:06:27 +08:00
ageerle
ce52402e4c Merge pull request #196 from zhangyue-mars/add-deepseek-java-files
feat: update ChatRequest and DeepSeekChatImpl for DeepSeek integration
2025-10-12 19:06:09 +08:00
ageerle
559661f498 Merge pull request #221 from seven-ocean/feature/aihuman
Feature/aihuman
2025-10-12 19:04:50 +08:00
ageerle
4bacb4bf27 Merge pull request #222 from Cyclones-Y/main
通过策略模式扩展milvus向量库
2025-10-12 19:04:35 +08:00
Yzm
72337563ea feat(chat): 添加根据会话ID查询聊天消息列表接口,优化会话ID设置逻辑 2025-10-12 18:15:11 +08:00
Yzm
77f7ac0af1 refactor(knowledge): 标记向量存储服务为首选实现
- 添加 @Primary 注解以指定为主要 Bean 实现
- 确保在多个实现存在时优先使用该服务
2025-10-11 20:09:15 +08:00
Yzm
c995c94fca Merge remote-tracking branch 'upstream/main' into feat_vectorStore
# Conflicts:
#	ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/VectorStoreServiceImpl.java
2025-10-10 18:29:58 +08:00
Maxchen
e12e4c4669 Merge remote-tracking branch 'origin/feature/aihuman' into feature/aihuman 2025-10-10 14:11:43 +08:00
Maxchen
3cbbfdf771 Merge remote-tracking branch 'origin/feature/aihuman' into feature/aihuman 2025-10-10 14:11:30 +08:00
ageerle
fdddec2f14 Merge pull request #219 from seven-ocean/feature/aihuman
Feature/aihuman
2025-10-10 12:41:19 +08:00
ageerle
57e23be82f Merge branch 'main' into feature/aihuman 2025-10-10 12:41:10 +08:00
ageerle
89f4976b7c Merge pull request #220 from seven-ocean/feature/aihuamn
Feature/aihuamn
2025-10-10 12:39:03 +08:00
Maxchen
a0d93b1ca8 Merge remote-tracking branch 'origin/feature/aihuman' into feature/aihuman 2025-10-10 12:23:55 +08:00
Maxchen
ca8ba6fe48 Merge remote-tracking branch 'origin/feature/aihuman' into feature/aihuman
# Conflicts:
#	script/sql/update/2025-10-10-实时交互数字人集成.sql
2025-10-10 12:23:41 +08:00
Maxchen
7994704c11 Merge remote-tracking branch 'origin/feature/aihuman' into feature/aihuman
# Conflicts:
#	script/sql/update/2025-10-10-实时交互数字人集成.sql
2025-10-10 12:09:17 +08:00
Maxchen
8d4fadc9a2 修改了sql文件 2025-10-10 12:06:10 +08:00
Maxchen
bd8dccf7b2 提交 菜单+字典+字典编码 sql 2025-10-10 12:00:17 +08:00
Maxchen
584cead6bf 提交 pom.xml 模块修改 2025-10-10 11:53:51 +08:00
Maxchen
a52529eefd 编码统一为utf8mb4_general_ci 2025-10-10 11:32:54 +08:00
ageerle
6e433237e0 Merge pull request #218 from seven-ocean/feature/aihuamn
修改了sql文件
2025-10-10 11:23:06 +08:00
Maxchen
abd4f01b69 修改了sql文件 2025-10-10 11:16:42 +08:00
Maxchen
5c430ee1d2 修改了sql文件 2025-10-10 11:10:33 +08:00
ageerle
e23e295d68 Merge pull request #217 from seven-ocean/feature/aihuman
添加数字人菜单sql,建表语句sql
2025-10-10 11:06:38 +08:00
Maxchen
3d6bbad616 添加数字人菜sql,建表语句sql 2025-10-10 11:03:39 +08:00
ageerle
5088c0e6d7 Merge pull request #216 from fangzhh/main
feat: 优化嵌入模型业务,使用策略模型加工厂模式动态加载嵌入模型,支持多供应商多嵌入模型动态接入;
2025-10-09 20:11:40 +08:00
Ariel Overcast
2995b6ddde Merge branch 'ageerle:main' into main 2025-10-09 20:04:50 +08:00
Robust_H
5475776caa feat: 优化通过知识库获取模型配置逻辑,修改为通过模型id查找模型配置,避免多供应商同模型映射错误。 2025-10-09 20:03:34 +08:00
ageerle
08d4977263 Merge pull request #215 from seven-ocean/feature/aihuman
数字人后端
2025-10-09 19:55:09 +08:00
Maxchen
31602cb85e 数字人后端 2025-10-09 18:53:28 +08:00
ageerle
5adc5f0006 Update README.md 2025-10-09 17:40:29 +08:00
ageerle
307d095cf1 Update README.md 2025-10-09 17:38:20 +08:00
Robust_H
2cef4e17dc perf: 优化‘嵌入模型’工厂,添加缓存机制 2025-10-04 18:27:54 +08:00
Robust_H
b47da3f438 feat: 初始化多供应商多嵌入模型集成,采用策略模式和工厂模式实现 2025-10-04 04:50:12 +08:00
Yzm
17c52e9048 refactor(VectorStoreServiceImpl): 添加@Primary注解以指定主要实现
在多个实现存在时,明确指定VectorStoreServiceImpl作为主要实现类
2025-09-29 21:49:27 +08:00
Yzm
f71cf85dc8 feat(knowledge): 实现Milvus向量库策略并重构配置管理
- 新增Milvus向量库策略实现类MilvusVectorStoreStrategy
- 重构向量库配置管理,使用VectorStoreProperties统一配置
- 修改AbstractVectorStoreStrategy抽象类依赖注入方式
- 更新Weaviate策略实现类适配新的配置方式
- 移除旧的ConfigService配置读取方式
- 添加向量库类型配置项,默认使用weaviate
- 实现Milvus集合创建、数据存储、向量搜索和删除功能
- 优化向量库策略工厂类VectorStoreStrategyFactory初始化逻辑
- 删除已废弃的Milvus实现指南文档
- 升级Milvus SDK版本并调整相关API调用方式
2025-09-29 21:45:01 +08:00
Yzm
ef49429543 feat(milvus): 实现Milvus向量数据库集成
- 添加Milvus Java SDK依赖
- 实现MilvusVectorStoreStrategy核心功能
- 支持集合管理、数据存储、向量搜索和数据删除
- 添加Milvus实现指南文档
- 更新数据库连接配置
- 修改VectorStoreService接口添加异常声明
2025-09-29 18:36:48 +08:00
Yzm
39fe2cc48f Merge remote-tracking branch 'upstream/main' into feat_vectorStore 2025-09-28 16:51:16 +08:00
evo
827ac48826 Merge pull request #200 from 20suiWXJB/fix/在-ruoyi-extend-中添加缺失的-ruoyi-ai-copilot-模块
fix(pom): 在 ruoyi-extend 中添加缺失的 ruoyi-ai-copilot 模块
2025-09-27 19:11:32 +08:00
evo
f906645708 Merge pull request #198 from wenxwang/fix/admin-knowledge-remove-error
fix(Knowledge): 知识库删除失败
2025-09-27 19:08:17 +08:00
evo
c17e16dd0f Merge pull request #181 from LM20230311/feat-model-priority
解决问答实现类中重新查询模型逻辑可能导致自动选择的模型被重置问题
2025-09-27 19:07:34 +08:00
evo
837236f1cc Merge pull request #180 from LM20230311/fix-upload-bucket
Fix upload bucket: 修复问答页面文件上传问题;
2025-09-27 19:07:15 +08:00
evo
60793b957a Merge pull request #179 from violateer/featur/remove_limit1_compat
feature: 移除limit 1写法,兼容不同数据库
2025-09-27 19:06:48 +08:00
evo
fa4dc87e76 Merge pull request #212 from MuSan-Li/feature_20250927_fix_oss_logic
feat: 删除oss创建桶配置
2025-09-27 19:06:14 +08:00
evo
4ac63c3268 feat: 删除oss创建桶配置 2025-09-27 19:05:27 +08:00
evo
54e7999fe3 Merge pull request #210 from MuSan-Li/feature_20250926_add_swagger
feat: 添加接口文档测试
2025-09-26 15:07:22 +08:00
lihao05
32fd910584 feat: 添加接口文档测试 2025-09-26 15:06:43 +08:00
evo
25e659dffa Merge pull request #208 from LM20230311/upgrade/upgrade-spring-ai-1.0.0
Upgrade/upgrade spring ai 1.0.0
2025-09-26 13:59:49 +08:00
Yzm
aa1c771e72 feat(knowledge): 实现向量库策略模式支持多向量库
- 新增向量库策略接口及抽象基类
- 实现Weaviate向量库策略- 实现Milvus向量库策略(占位实现)
- 添加向量库策略工厂类动态选择实现
- 修改向量存储服务使用策略模式
- 更新知识信息service调用参数顺序
- 添加文档分段和知识片段ID生成注释
- 修改dev环境数据库配置为github版本
2025-09-25 18:44:19 +08:00
LM20230311
585e5ff0f8 fix: 解决knife4j访问问题-暂未解决; 2025-09-24 20:47:22 +08:00
LM20230311
bd346f1e85 fix: 解决不登陆无法问答问题;修复余额不足后流不关闭问题; 2025-09-24 20:27:55 +08:00
LM20230311
2caf9a47ed upgrade: 放行knife4j需要的路径; 2025-09-24 19:28:34 +08:00
LM20230311
f10f44158c upgrade: 升级knife4j版本为3,4.5.0; 2025-09-24 19:01:33 +08:00
ageerle
76acd4a40b Update README.md 2025-09-24 17:32:19 +08:00
ageerle
6467af1d73 Update README.md 2025-09-24 17:31:44 +08:00
ageerle
35146f3495 Update README.md 2025-09-24 17:31:00 +08:00
likunlong
9e23587fb1 upgrade: 升级spring ai版本为正式1.0.0; 2025-09-24 10:08:51 +08:00
evo
a61bd57e22 Merge pull request #206 from MuSan-Li/feature_20250923_fix_wx_logic
feat:修复缺少的微信逻辑部分
2025-09-23 13:58:38 +08:00
lihao05
6bb7bc6eb5 feat:修复缺少的微信逻辑部分 2025-09-23 13:57:18 +08:00
ageerle
3f9e83a767 Merge pull request #204 from Cyclones-Y/main
feat(wechat): 添加企业微信SDK依赖引入weixin-java-cp4.4.0版本以支持企业微信功能开发
2025-09-21 16:08:12 +08:00
Yzm
0d711b1842 feat(wechat): 添加企业微信SDK依赖引入weixin-java-cp4.4.0版本以支持企业微信功能开发 2025-09-21 15:39:06 +08:00
ageerle
a33159f9a3 移除不必要配置 2025-09-19 14:53:35 +08:00
ageerle
6462752fd6 恢复微信模块,优化知识库切片功能 2025-09-19 14:50:02 +08:00
ageerle
afc1272ff5 恢复微信模块,优化知识库切片功能 2025-09-19 11:15:37 +08:00
ageerle
fa5dc80a93 Merge remote-tracking branch 'origin/mineru/dev'
# Conflicts:
#	ruoyi-admin/src/main/resources/application-dev.yml
#	ruoyi-admin/src/main/resources/application-prod.yml
#	ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/chain/loader/ResourceLoaderFactory.java
2025-09-19 10:59:52 +08:00
ageerle
acc2d5d1a8 Merge remote-tracking branch 'origin/pdf-image'
# Conflicts:
#	ruoyi-admin/src/main/resources/application-dev.yml
#	ruoyi-admin/src/main/resources/application-prod.yml
#	ruoyi-modules-api/ruoyi-knowledge-api/pom.xml
#	ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/PdfImageExtractService.java
#	ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/PdfImageExtractServiceImpl.java
#	ruoyi-modules-api/ruoyi-knowledge-api/src/main/java/org/ruoyi/service/impl/VectorStoreServiceImpl.java
#	ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/controller/knowledge/KnowledgeController.java
#	ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/DealFileService.java
#	ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/KnowledgeInfoServiceImpl.java
#	script/sql/update/202505141010.sql
2025-09-19 10:56:26 +08:00
马宏跃
c22b9fdb9c fix(pom): 在 ruoyi-extend 中添加缺失的 ruoyi-ai-copilot 模块 2025-09-18 09:03:07 +08:00
ageerle
2a45776aeb 新增数据库开发规范 2025-09-17 17:35:51 +08:00
w
ff0a3d1016 fix(Knowledge): 知识库删除失败 2025-09-17 12:00:27 +08:00
ageerle
b2a589ed9c Update README.md 2025-09-16 13:45:27 +08:00
ZhangYue
2ac34e313a fix:修复切换知识库的时候只有ID为1L才能查看所有知识库,改为可以查看自己创建的知识库 2025-09-16 10:26:39 +08:00
ZhangYue
4baa970118 feat: update ChatRequest and DeepSeekChatImpl for DeepSeek integration 2025-09-10 09:55:47 +08:00
evo
13da60e151 Merge pull request #189 from MuSan-Li/feature_20250904_fix_sql
feat: 添加session表会话ID
2025-09-04 17:02:06 +08:00
l90215
1f0c0ba0a9 feat: 添加session表会话ID 2025-09-04 17:01:13 +08:00
ageerle
ef3541fe77 Merge pull request #188 from xiaonieli7/feature_20250813_fix_codeOptimization
补充统一计费代理类BillingChatServiceProxy
2025-09-04 16:44:52 +08:00
Administrator
2b5fd810a4 fix(billing): 统一计费代理类BillingChatServiceProxy 2025-09-04 16:41:14 +08:00
Administrator
4a8d21a742 fix(billing): 1. 新增统一计费代理 BillingChatServiceProxy位置:ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/proxy/BillingChatServiceProxy.java 作用:为所有ChatService实现类提供透明的计费代理包装
核心功能:
  AI回复前余额预检查,避免无效消耗
  自动收集AI回复内容
  统一处理AI回复的保存和计费
   适配多种AI服务的数据格式
  2. 重构工厂类
  ChatServiceFactory
  改进:自动为所有ChatService包装计费代理
 新增方法:getOriginalService() 用于获取未包装的原始服务优势:调用方无需关心计费逻辑,完全透明
 3. 增强计费服务 IChatCostService 接口
   新增方法:checkBalanceSufficient() - 余额预检查
   分离关注点:saveMessage() - 仅保存消息
    publishBillingEvent() - 仅发布计费事件
    deductToken() - 仅执行计费扣费
2025-09-04 16:35:55 +08:00
ageerle
c62530176f Merge pull request #187 from xiaonieli7/feature_20250813_fix_codeOptimization
修改了计费逻辑
2025-09-04 15:43:48 +08:00
Administrator
c7554d7e35 fix(billing): 1. 新增统一计费代理 BillingChatServiceProxy位置:ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/proxy/BillingChatServiceProxy.java 作用:为所有ChatService实现类提供透明的计费代理包装
核心功能:
  AI回复前余额预检查,避免无效消耗
  自动收集AI回复内容
  统一处理AI回复的保存和计费
   适配多种AI服务的数据格式
  2. 重构工厂类
  ChatServiceFactory
  改进:自动为所有ChatService包装计费代理
 新增方法:getOriginalService() 用于获取未包装的原始服务优势:调用方无需关心计费逻辑,完全透明
 3. 增强计费服务 IChatCostService 接口
   新增方法:checkBalanceSufficient() - 余额预检查
   分离关注点:saveMessage() - 仅保存消息
    publishBillingEvent() - 仅发布计费事件
    deductToken() - 仅执行计费扣费
2025-09-04 15:37:52 +08:00
Administrator
1e4af3d01b fix(billing): 修复Token计费逻辑和消息更新机制
* 修复Token计费算法:按批次计费而非Token数量计费
* 添加ChatRequest.messageId字段支持消息关联更新
* 优化消息保存流程:分离基础信息保存和计费信息更新
* 修复预检查逻辑:统一预检查和实际扣费计算方式
* 调整Token阈值:100 → 1000,减少扣费频次
* 完善事件传递:ChatMessageCreatedEvent增加messageId

Fixes: 余额预检查误判、消息计费信息缺失、Token计费不准确
2025-08-29 15:19:37 +08:00
Administrator
1e3b49c9b8 用户发送消息 → 预检查余额 → 保存用户消息 → 发布计费事件 → 异步扣费 → 保存账单记录
添加了billingType计费类型字段消息保存的时候写入进去
2025-08-27 16:48:48 +08:00
Administrator
9f7f00e50c 用户发送消息 → 预检查余额 → 保存用户消息 → 发布计费事件 → 异步扣费 → 保存账单记录
添加了billingType计费类型字段消息保存的时候写入进去
2025-08-27 15:30:59 +08:00
Administrator
1c721981db Merge remote-tracking branch 'origin/main'
# Conflicts:
#	ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/chat/impl/DifyServiceImpl.java
#	ruoyi-modules/ruoyi-generator/src/main/java/org/ruoyi/generator/impl/GenTableServiceImpl.java
2025-08-27 10:49:37 +08:00
AmAzing129
6e6ba84fd2 docs: add contributors bubble 2025-08-27 10:47:34 +08:00
likunlong
ef69778bb7 feat: 下掉模型能力逻辑代码; 2025-08-27 10:47:34 +08:00
l90215
7a374d877b feat: 调整知识库问答接入提示词模板 2025-08-27 10:47:34 +08:00
likunlong
43426054ec feat: 兼容不选自动模型时的原先逻辑;封装通用方法,简化创建有监控的SSE,简化流式错误输出并通知重试; 2025-08-27 10:47:31 +08:00
likunlong
ccdbb20935 feat: 不选择模型自动选择时走原始默认逻辑; 2025-08-27 10:47:29 +08:00
likunlong
4b37cfe97d feat: 失败回调器中使用emitter对象的唯一hash作为key,不再使用session,不与业务进行绑定,同时也保证跨线程调用的正确性; 2025-08-27 10:47:26 +08:00
likunlong
c43d4784de feat: 处理在非Web线程中获取Request中token失败的问题; 2025-08-27 10:47:25 +08:00
likunlong
359cee28d5 feat: 修改目前实现类使用统一重试降级逻辑; 2025-08-27 10:47:22 +08:00
likunlong
aa11c1f233 feat: 问答时添加统一重试和降级逻辑; 2025-08-27 10:47:20 +08:00
likunlong
a0d029c142 feat: 自动设置请求参数中的模型名称; 2025-08-27 10:47:20 +08:00
likunlong
6ce52befe2 feat: 根据是否有附件和是否自动,自动选择模型并且获取服务; 2025-08-27 10:47:19 +08:00
likunlong
330bdc3761 feat: 数据库chat_model添加优先级字段; 2025-08-27 10:47:19 +08:00
likunlong
4f7ad59e46 feat: 添加自动获取高优先级模型和服务的逻辑; 2025-08-27 10:47:19 +08:00
l90215
b696fde881 feat: 合并代码 删除不需要的文件 2025-08-27 10:47:19 +08:00
fy53888
00f9a1a55b 修改字典下拉带查找功能 2025-08-27 10:47:19 +08:00
fy53888
a1c7b86e72 备分一下2 2025-08-27 10:47:19 +08:00
fy53888
62676a54fb 备分一下2 2025-08-27 10:47:19 +08:00
fy53888
e51425a951 备分一下 2025-08-27 10:47:17 +08:00
fy53888
268be2d9ec 更新后端生成類型 Integer出錯的問 2025-08-27 10:47:09 +08:00
fy53888
f448a18e44 更新后端生成類型 Integer出錯的問題 2025-08-27 10:47:07 +08:00
lixiang
22e59fe5a1 向量库sql查询去除匹配分值字段 2025-08-27 10:47:01 +08:00
violateer
0780e3b8c9 fix: 修改krole_group_ids字段名 2025-08-27 10:47:01 +08:00
violateer
0cdba56a07 feature: 添加开启知识库角色,用户可见个人知识库及角色分配知识库 2025-08-27 10:47:01 +08:00
l90215
ebc13c06af feat: 更新只是库角色默认不开启 2025-08-27 10:47:00 +08:00
l90215
416f011c73 feat: fix代码生成类型问题 2025-08-27 10:47:00 +08:00
likunlong
bfeb389171 feat: 获取模型接口支持返回模型能力;模型表增加模型能力字段; 2025-08-27 10:47:00 +08:00
violateer
caf7f14781 feature: 新增生成前端文件模板接口 2025-08-27 10:46:51 +08:00
l90215
a6eb98daab feat: fix代码生成类型问题 2025-08-27 10:46:37 +08:00
l90215
4834b615a6 feat: fix代码生成类型问题 2025-08-27 10:46:37 +08:00
l90215
42aabeed96 feat: 添加空格 2025-08-27 10:46:37 +08:00
likunlong
67303cf5be feat: 上传已经确定模型的选择,这里只需要根据名字获取模型直接使用就好; 2025-08-26 09:54:08 +08:00
likunlong
5bd95b496a feat: 默认上传服务修改为本地minio;添加部署时自启动minio服务;添加minio的服务启动docker-compose脚本; 2025-08-25 11:04:52 +08:00
likunlong
8fcaa7c90c feat: 默认上传服务修改为本地minio;添加部署时自启动minio服务;添加minio的服务启动docker-compose脚本; 2025-08-25 10:53:48 +08:00
violateer
27398c1000 feature: 移除limit 1写法,兼容不同数据库 2025-08-23 19:02:51 +08:00
ageerle
bd9ffb10a9 Merge pull request #178 from AmAzing129/main
docs: add contributors bubble
2025-08-22 14:33:27 +08:00
AmAzing129
bb9c85ac3c docs: add contributors bubble 2025-08-22 14:29:03 +08:00
evo
70ca78d935 Merge pull request #176 from LM20230311/feat-model-priority
feat: 下掉模型能力逻辑代码;
2025-08-20 17:55:15 +08:00
evo
1af8c4ee50 Merge pull request #177 from MuSan-Li/feature_20250820_fix_prompt_temp
feat: 调整知识库问答接入提示词模板
2025-08-20 17:55:05 +08:00
l90215
b9276c5dcc feat: 调整知识库问答接入提示词模板 2025-08-20 17:52:51 +08:00
likunlong
d1e98a2001 feat: 下掉模型能力逻辑代码; 2025-08-20 16:09:53 +08:00
evo
baf065a294 Merge pull request #175 from LM20230311/feat-model-priority
Feat model priority:支持自动选择模型;支持模型的重试;
2025-08-20 14:04:17 +08:00
likunlong
842a39d6d2 feat: 兼容不选自动模型时的原先逻辑;封装通用方法,简化创建有监控的SSE,简化流式错误输出并通知重试; 2025-08-19 20:28:53 +08:00
likunlong
9fba91c35f feat: 不选择模型自动选择时走原始默认逻辑; 2025-08-19 18:00:20 +08:00
likunlong
498135b7fd feat: 失败回调器中使用emitter对象的唯一hash作为key,不再使用session,不与业务进行绑定,同时也保证跨线程调用的正确性; 2025-08-19 17:53:27 +08:00
likunlong
c3ab13ae67 feat: 处理在非Web线程中获取Request中token失败的问题; 2025-08-19 17:39:20 +08:00
likunlong
1638b9dd75 feat: 修改目前实现类使用统一重试降级逻辑; 2025-08-19 16:51:51 +08:00
likunlong
4434d8346c feat: 问答时添加统一重试和降级逻辑; 2025-08-19 16:46:25 +08:00
likunlong
119483df86 feat: 自动设置请求参数中的模型名称; 2025-08-19 15:12:24 +08:00
evo
98f7e3ada2 Merge pull request #174 from MuSan-Li/feature_202500819_fix_merge
feat: 合并代码 删除不需要的文件
2025-08-19 12:44:59 +08:00
l90215
50d9e0e843 feat: 合并代码 删除不需要的文件 2025-08-19 12:43:34 +08:00
evo
2871cf7630 Merge pull request #171 from fy53888/main
修改字典功能 和模板生成id 太长19位 改为1,2,3
2025-08-19 12:33:40 +08:00
likunlong
07cb351807 feat: 根据是否有附件和是否自动,自动选择模型并且获取服务; 2025-08-19 10:32:17 +08:00
fy53888
951ee6bd8a 修改字典下拉带查找功能 2025-08-19 09:48:35 +08:00
fy53888
5b6605c345 备分一下2 2025-08-18 22:04:15 +08:00
fy53888
5db116ec88 备分一下2 2025-08-18 22:03:51 +08:00
fy53888
645c754dd0 备分一下 2025-08-18 19:56:26 +08:00
likunlong
8751bb5104 feat: 数据库chat_model添加优先级字段; 2025-08-18 14:49:56 +08:00
likunlong
8d0c557bdb feat: 添加自动获取高优先级模型和服务的逻辑; 2025-08-18 14:30:08 +08:00
evo
5ac785c570 Merge pull request #173 from lixiang-cell/lixiang8.18
向量库sql查询去除匹配分值字段
2025-08-18 12:30:26 +08:00
evo
60145f9291 Merge pull request #170 from violateer/feature_fix_k_role_20280815
feature: 添加开启知识库角色,用户可见个人知识库及角色分配知识库
2025-08-18 12:30:09 +08:00
lixiang
d7b89cd1b3 向量库sql查询去除匹配分值字段 2025-08-18 11:12:06 +08:00
fy53888
5264b47c2f 修改字典功能 和模板生成id 太长19位 改为1,2,3 2025-08-17 09:14:19 +08:00
violateer
e9cd9e84d4 fix: 修改krole_group_ids字段名 2025-08-15 20:43:14 +08:00
violateer
b52f7a7112 feature: 添加开启知识库角色,用户可见个人知识库及角色分配知识库 2025-08-15 20:41:59 +08:00
evo
2abdf762c1 Merge pull request #168 from MuSan-Li/feature_20250815_update_k_role_config
feat: 更新只是库角色默认不开启
2025-08-15 09:58:32 +08:00
l90215
099c94e3cb feat: 更新只是库角色默认不开启 2025-08-15 09:56:47 +08:00
Administrator
affdc5e3a6 问题概述
1.保存消息和计费逻辑存在耦合
2.修改计费逻辑:
按次计费被阈值限制:旧逻辑把 TIMES 分支放在 totalTokens ≥ 100 的大分支里,导致没到100 token时不扣费,违背“每次调用就扣费”的语义。
token累计不当:TIMES 分支只扣费不处理累计,同时在 totalTokens < 100 时不会进入任何TIMES逻辑,累计会无意义增长。
粒度不稳定:TOKEN 计费一旦达阈值就把 total 全扣完并清零,不利于对账与用户体验。
打印方式:使用 System.out.println,不利于生产追踪。

改动要点
1.新增独立方法
saveMessage(ChatRequest): 只落库。
publishBillingEvent(ChatRequest): 只发布异步计费事件。
保留组合方法 saveMessageAndPublishEvent(ChatRequest) 以便需要一行调用时使用。
调用处已改为“先保存,再发布事件”
SseServiceImpl: 先 saveMessage,再 publishBillingEvent。
SSEEventSourceListener: 同上。
DifyServiceImpl: 同上。

2.计费模式分流:
TIMES:每次调用直接扣费,不累计。
TOKEN:按阈值(100)批量扣费,保留余数,账单颗粒稳定。
保留余数:total = prev + delta;billable = floor(total/threshold)threshold;remainder = total % threshold。
日志替换:统一使用 log.debug。
结构更清晰、可维护。
所有金额计算统一用 BigDecimal,保留两位小数,RoundingMode.HALF_UP
按次计费:每次直接扣费(BigDecimal),边界转 Double
按 token 计费:按阈值批量结算,保留余数;费用=单价(BigDecimal)×可结算token数
1. 消息分类存储
用户消息:role="user", deductCost=null, totalTokens=本次token数, remark="用户消息"
系统账单:role="system", deductCost=实际扣费, totalTokens=计费token数, remark="TIMES_BILLING/TOKEN_BILLING"
2. 数据流程
用户发送消息 → 预检查余额 → 保存用户消息 → 发布计费事件 → 异步扣费 → 保存账单记录
2025-08-14 14:00:48 +08:00
evo
047044eb06 Merge pull request #166 from MuSan-Li/feature_202500807_fix_code_generate
feat: fix代码生成类型问题
2025-08-13 12:39:24 +08:00
l90215
7108727395 feat: fix代码生成类型问题 2025-08-13 12:37:10 +08:00
酒亦
5631fe92c6 Merge branch 'ageerle:main' into main 2025-08-12 15:25:41 +08:00
酒亦
43dc0f419f feat:基于sse模式 启动mcp服务器 (未测试) 2025-08-12 14:00:18 +08:00
evo
d3732a155d Merge pull request #164 from MuSan-Li/feature_20250811_fix_code_generator
Feature 20250811 fix code generator
2025-08-11 22:00:20 +08:00
l90215
a0db91ebe6 Merge branch 'main' of https://github.com/MuSan-Li/ruoyi-ai into feature_20250811_fix_code_generator 2025-08-11 21:59:20 +08:00
l90215
e83d70e9c3 feat: fix代码生成类型问题 2025-08-11 21:59:02 +08:00
evo
5eb166839a Merge pull request #163 from LM20230311/feat-model-switching-association
feat: 获取模型接口支持返回模型能力;模型表增加模型能力字段;
2025-08-11 21:54:08 +08:00
evo
e27a6cb738 Merge pull request #162 from violateer/feature_gen_frontend_model_files
feature: 新增生成前端文件模板接口
2025-08-11 21:53:56 +08:00
酒亦
bc2eb8fdb9 feat:基于stdio模式 启动mcp服务器 2025-08-11 21:22:12 +08:00
likunlong
d964e86b23 feat: 获取模型接口支持返回模型能力;模型表增加模型能力字段; 2025-08-11 09:33:35 +08:00
酒亦
9891259452 mcp 信息 增删改查 完成 2025-08-10 20:50:04 +08:00
violateer
86d7eab5b5 解决冲突 2025-08-10 17:22:48 +08:00
violateer
fa5ad8caf6 Merge remote-tracking branch 'origin/feature_gen_frontend_model_files' into feature_gen_frontend_model_files 2025-08-10 17:20:14 +08:00
violateer
e5011e0dd9 feature: 新增生成前端文件模板接口 2025-08-10 17:19:59 +08:00
violateer
5fe8bd7706 feature: 新增生成前端文件模板接口 2025-08-10 17:06:32 +08:00
evo
0f28e1f3f6 Merge pull request #161 from MuSan-Li/feature_202500807_fix_xx
fix 代码生成逻辑bug
2025-08-10 00:27:56 +08:00
l90215
503f86644e feat: fix代码生成类型问题 2025-08-10 00:25:49 +08:00
fy53888
579beb6833 修复后端生成类型 Integer 出错的问题 2025-08-09 22:03:31 +08:00
fy53888
22c0c733f6 更新获取Java类型后端生成類型 Integer出錯的問 2025-08-09 22:01:29 +08:00
fy53888
9f4a2256b4 更新后端生成類型 Integer出錯的問 2025-08-09 21:56:16 +08:00
fy53888
5a4d76ac09 更新后端生成類型 Integer出錯的問題 2025-08-09 21:55:23 +08:00
Administrator
5a2e08f87d 问题概述
1.保存消息和计费逻辑存在耦合
2.修改计费逻辑:
按次计费被阈值限制:旧逻辑把 TIMES 分支放在 totalTokens ≥ 100 的大分支里,导致没到100 token时不扣费,违背“每次调用就扣费”的语义。
token累计不当:TIMES 分支只扣费不处理累计,同时在 totalTokens < 100 时不会进入任何TIMES逻辑,累计会无意义增长。
粒度不稳定:TOKEN 计费一旦达阈值就把 total 全扣完并清零,不利于对账与用户体验。
打印方式:使用 System.out.println,不利于生产追踪。
3.建议数据库不要存扣除金额和累计消耗token,消息表里不需要存“累计到目前为止多少”,否则每条消息都变成快照,既冗余又易不一致

改动要点
1.新增独立方法
saveMessage(ChatRequest): 只落库。
publishBillingEvent(ChatRequest): 只发布异步计费事件。
保留组合方法 saveMessageAndPublishEvent(ChatRequest) 以便需要一行调用时使用。
调用处已改为“先保存,再发布事件”
SseServiceImpl: 先 saveMessage,再 publishBillingEvent。
SSEEventSourceListener: 同上。
DifyServiceImpl: 同上。

2.计费模式分流:
TIMES:每次调用直接扣费,不累计。
TOKEN:按阈值(100)批量扣费,保留余数,账单颗粒稳定。
保留余数:total = prev + delta;billable = floor(total/threshold)threshold;remainder = total % threshold。
日志替换:统一使用 log.debug。
结构更清晰、可维护。
所有金额计算统一用 BigDecimal,保留两位小数,RoundingMode.HALF_UP
按次计费:每次直接扣费(BigDecimal),边界转 Double
按 token 计费:按阈值批量结算,保留余数;费用=单价(BigDecimal)×可结算token数
2025-08-08 13:39:37 +08:00
l90215
838a393abc feat: 添加空格 2025-08-07 21:13:02 +08:00
evo
210a9d9b14 Merge pull request #159 from MuSan-Li/feature_20250807_fix_code_generator
feat: 代码生成模板优化
2025-08-07 09:03:08 +08:00
l90215
66518925c1 feat: 代码生成模板优化 2025-08-07 09:02:16 +08:00
evo
19d3f018b8 Merge pull request #158 from MuSan-Li/feature_20250805_fix_data_model_del
feat: 优化删除数据模型也删除模型字段sql条件
2025-08-05 22:11:00 +08:00
l90215
450ec6db44 feat: 优化删除数据模型也删除模型字段sql条件 2025-08-05 22:06:05 +08:00
evo
0bb897de1f Merge pull request #157 from MuSan-Li/feature_20250805_fix_data_model_del
feat: 优化删除数据模型也删除模型字段&添加mapper文件注解
2025-08-05 19:51:28 +08:00
l90215
ab9ff52200 feat: 优化删除数据模型也删除模型字段&添加mapper文件注解 2025-08-05 19:50:29 +08:00
evo
d94bcf250e Merge pull request #156 from MuSan-Li/feature_20250804_fix_bug
feat: 优化空指针bug&格式化代码&初始化用户知识库角色配置
2025-08-04 20:54:27 +08:00
l90215
10708b7625 feat: 优化空指针bug&格式化代码&初始化用户知识库角色配置 2025-08-04 20:50:49 +08:00
ageerle
5e032a68d5 Merge pull request #154 from violateer/feature/knowledge-role
添加删除知识库角色组时关联删除数据
2025-08-03 09:59:50 +08:00
violateer
bd41ff0f28 添加删除知识库角色组时关联删除数据 2025-08-02 22:29:02 +08:00
evo
3ffba456f4 Merge pull request #153 from MuSan-Li/feature_20250802_fix_k_sql
feat: 调整知识库角色sql
2025-08-02 21:25:52 +08:00
l90215
993346534e feat: 调整知识库角色sql 2025-08-02 21:25:16 +08:00
evo
38169ba90d 修复查询字段名称
feat: 修复一些逻辑问题
2025-08-02 19:39:23 +08:00
l90215
b003e1b3bb feat: 修复查询字段名称 2025-08-02 19:38:50 +08:00
l90215
f36dda74a1 feat: 修复一些逻辑问题 2025-08-02 19:37:07 +08:00
evo
c609095c69 Merge pull request #151 from MuSan-Li/feature_20250801_fix_menu_sql
feat: 调整菜单结构sql
2025-08-02 18:37:47 +08:00
l90215
93e34b3f03 feat: 调整菜单结构sql 2025-08-02 18:36:50 +08:00
ageerle
cc4ca69640 Merge pull request #143 from violateer/feature/knowledge-role
添加知识库权限控制功能
2025-08-02 15:56:42 +08:00
ageerle
85930f4b12 Merge pull request #132 from HHANG52/main
feat(chat): 修复仅非管理员设置根据用户 ID查询聊天记录
2025-08-02 15:56:31 +08:00
ageerle
3c19a2b7a4 Merge pull request #142 from keke-cxn/main
修复:请求地址'/system/role/authUser/selectAll',发生未知异常:cn.dev33.satoken.exce…
2025-08-02 15:56:16 +08:00
keke
21c390c4d6 修复:1.在使用dify的时候,发送1+1=?模型返回2,但是第二次问他为什么等于2的时候,模型无法获取上下文信息,经过排查,发现缺少conversationId,session表需要添加conversationId字段
2.在使用dify的时候message表中并没有存储模型返回的消息,并且扣除费用、
2025-08-02 13:18:30 +08:00
ageerle
cdef9e1c89 feat: 增加演示模式 2025-07-31 14:58:29 +08:00
evo
3c60f43daa Merge pull request #147 from MuSan-Li/feature_20250721_generate_code
feat: 调整生成代码结构
2025-07-30 09:31:04 +08:00
l90215
a24fa7a2b4 feat: 调整生成代码结构 2025-07-30 09:24:31 +08:00
ageerle
21f8462ebb Merge pull request #146 from MuSan-Li/feature_20250721_generate_code
feat: 调整生成代码结构
2025-07-30 09:10:32 +08:00
l90215
897222c41c feat: 调整生成代码结构 2025-07-29 23:34:39 +08:00
ageerle
227670df9d Merge pull request #145 from MuSan-Li/feature_20250721_generate_code
feat: 提交替换代码生成sql
2025-07-29 09:43:29 +08:00
l90215
6b2cf1a3c3 feat: 提交替换代码生成sql 2025-07-28 17:47:12 +08:00
ageerle
37f832c31c Merge pull request #144 from MuSan-Li/feature_20250721_generate_code
替换代码生成
2025-07-28 09:32:19 +08:00
l90215
25cf1f9744 feat: 更新数据库密码... 2025-07-27 22:47:55 +08:00
l90215
a48178685c feat: 更新代码生成功能-优化逻辑 2025-07-27 22:37:26 +08:00
l90215
7e60cb357f feat: 更新代码生成功能-优化逻辑 2025-07-27 00:33:33 +08:00
l90215
915a393427 feat: 更新代码生成功能-优化取数逻辑 2025-07-24 12:36:26 +08:00
l90215
de323d8c45 feat: 更新代码生成功能-添加数据模型批量添加字段数据 2025-07-23 10:46:29 +08:00
l90215
ffe4867d40 feat: 更新代码生成功能-二阶段 2025-07-22 18:50:37 +08:00
l90215
8b3c0b4134 feat: 更新代码生成功能-一阶段 2025-07-21 16:03:08 +08:00
violateer
999282210e 添加知识库权限控制功能 2025-07-20 10:17:50 +08:00
violateer
a99344813f 添加知识库权限控制功能 2025-07-20 10:05:38 +08:00
ageerle
63ec00cd71 Merge pull request #136 from Administratos-User/main
修复token不扣费和扣费异常的问题
2025-07-16 15:53:56 +08:00
ageerle
7eebd87cc8 Merge pull request #141 from lixiang-cell/lixiang
存储向量库同时存储元数据的fid和docId,提供根据fid docId删除
2025-07-16 15:52:19 +08:00
keke
9a816bb0c7 修复:当前用户【如果没有绑定任何部门的情况下】在查询用户列表分页的时候 调用 SysUserMapper 的 selectPageUserList() 时候报错:sql错误 2025-07-15 00:02:44 +08:00
keke
bc6ed508b0 修复:1.用户原本绑定了岗位,若想要取消所有的岗位,没有进行逻辑实现
2.用户原本绑定了角色,若想要取消所有的角色,没有进行逻辑实现
2025-07-14 23:47:47 +08:00
keke
d64abaaed3 修复:请求地址'/system/role/authUser/selectAll',发生未知异常:cn.dev33.satoken.exception.NotWebContextException: 非Web上下文无法获取Request 2025-07-14 23:39:47 +08:00
lixiang
285aa2ae62 存储向量库同时存储元数据的fid和docId,提供根据fid docId删除 2025-07-14 17:15:29 +08:00
Administratos-User
99114d3301 修复token不扣费和扣费异常的问题
1.本次提交的token数+未付费token数 判断大于100token的时候就进行扣费,当然这里还可以改成更多,我觉得100合适。
2.不需要进行扣费的地方屏蔽了相关代码。
3.SessionId传递异常 建议前端传递uuid,也就是每次会话的id。
2025-07-11 11:59:20 +08:00
HHANG
e857f0345b Merge branch 'ageerle:main' into main 2025-07-11 10:32:12 +08:00
ageerle
138fa5f0e9 Merge pull request #133 from MuSan-Li/feature_20250516_add_dept_error
feat: 修复打开岗位管理爆错
2025-07-11 09:49:55 +08:00
l90215
241c6dc57a feat: 修复打开岗位管理爆粗 2025-07-10 23:53:17 +08:00
ageerle
6005339ec8 upadate md 2025-07-10 23:00:04 +08:00
ageerle
db892d35fb feat: 更新项目说明 2025-07-10 17:12:20 +08:00
HHANG
605b223985 feat(chat): 修复仅非管理员设置根据用户 ID查询聊天记录
- 在 ChatMessageServiceImpl 类中,仅当用户不是超级管理员时,才自动设置消息的用户 ID,确保了超级管理员可以查看对话聊天
2025-07-09 16:19:48 +08:00
ageerle
e117ec8e27 feat: 更新项目说明 2025-07-09 14:27:55 +08:00
ageerle
5e8db861ea feat: 更新项目说明 2025-07-08 15:30:25 +08:00
ageerle
fa2791e2e3 feat: 更新项目说明 2025-07-08 14:31:01 +08:00
ageerle
f1f7cb1084 feat: 更新项目说明 2025-07-08 12:26:24 +08:00
ageerle
acb1f27c37 feat: 更新项目说明 2025-07-08 12:10:03 +08:00
ageerle
e46245d97d feat: 更新项目说明 2025-07-07 13:44:55 +08:00
ageerle
0eff37fa51 ai编程助手 2025-07-07 12:36:53 +08:00
ageerle
c1a178c0be Merge remote-tracking branch 'origin/main' 2025-07-07 12:33:21 +08:00
ageerle
94d4446321 feat: 编程助手 2025-07-07 12:33:06 +08:00
lindaxia
d7930ad713 修复导包问题 2025-07-06 08:12:38 +08:00
lindaxia
48270add01 Merge branch 'main' of https://github.com/ageerle/ruoyi-ai 2025-07-06 07:52:25 +08:00
lindaxia
3786644a25 新增小程序接口 2025-07-06 07:51:38 +08:00
ageerle
53532681d3 Merge pull request #131 from Cyclones-Y/main
[fix][feat](chat): 修复“自动注入警告 => 用户未登录”; 添加会话中SaToken-token值传递和使用;
2025-07-05 21:29:24 +08:00
Yzm
daa7b7315b [fix][feat](chat): 添加会话中SaToken-token值传递和使用;修复“自动注入警告 => 用户未登录”
- 新增 BaseContext 类,用于保存和获取当前登录用户的 token
- 在 OpenAIServiceImpl 和 imageServiceImpl 中获取当前会话 token
- 将 token 作为参数传递给 SSEEventSourceListener
- 在 SSEEventSourceListener 中使用 token 进行用户身份验证和权限控制
- 修改 LoginHelper,增加根据 token 获取登录用户信息的方法
- 更新 InjectionMetaObjectHandler,使用 BaseContext 获取当前 token
- 修复对话时出先”自动注入警告 => 用户未登录“
2025-07-05 19:12:33 +08:00
ageerle
df3b687be4 Merge pull request #129 from MuSan-Li/feature_20250703_add_model_category_select
feat: 模型管理增加模型分类下拉框
2025-07-03 10:48:43 +08:00
l90215
ecb5ef32fc feat: 模型管理增加模型分类下拉框 2025-07-03 10:29:25 +08:00
evo
e5116472ed Merge branch 'ageerle:main' into main 2025-07-03 09:50:59 +08:00
lindaxia
e58aeb5361 更新README.md 2025-07-02 11:04:35 +08:00
lindaxia
b4306289f0 fix weaviate向量库根据数据类进行删除 2025-07-01 18:50:12 +08:00
lindaxia
d9c47bd983 修复删除知识库清空相关表 2025-07-01 18:06:49 +08:00
evo
8ddbb43dde Merge branch 'ageerle:main' into main 2025-07-01 10:02:32 +08:00
ageerle
dfe8c7dc85 Merge pull request #127 from Cyclones-Y/main
feat(chat): 集成 FastGPT 聊天模型
2025-06-30 23:21:31 +08:00
Yzm
fd94a1772f feat(chat): 集成 FastGPT 聊天模型- 在 ChatModeType 枚举中添加 FASTGPT 选项- 新增 FastGPT 相关的实体类和请求响应类
- 实现 FastGPT聊天服务接口
- 添加 FastGPT SSE 事件监听器
2025-06-30 22:03:31 +08:00
ageerle
c105d47d99 Merge pull request #125 from MuSan-Li/feature_20250626_fix_update_role
修复修改角色时候报错
2025-06-27 10:45:32 +08:00
l90215
4e2ec2dc82 feat: 修复修改角色时候报错&优化一些代码风格 2025-06-26 10:19:08 +08:00
evo
614280d8ea Merge branch 'ageerle:main' into main 2025-06-25 12:57:49 +08:00
ageerle
2fae8d0ad0 feat: 更新任务规划演示 2025-06-24 14:21:43 +08:00
ageerle
d7c2d1bcf3 feat: 更新任务规划演示 2025-06-24 14:16:40 +08:00
ageerle
122f63dfbd Merge remote-tracking branch 'origin/main' 2025-06-24 13:52:01 +08:00
ageerle
719e968192 feat: 更新任务规划演示 2025-06-24 13:51:51 +08:00
evo
bf790ceb51 Merge branch 'ageerle:main' into main 2025-06-24 11:43:08 +08:00
ageerle
de5488bd8c Merge pull request #123 from abin0515/one-step-script
one-step-script: 修改MacOS上运行快速启动脚本遇到的bug
2025-06-24 10:30:49 +08:00
GH Action - Upstream Sync
c77a245a4d Merge branch 'main' of https://github.com/ageerle/ruoyi-ai 2025-06-24 01:57:42 +00:00
Bin Xiao
6dcd8823cd one-step-script: 修改MacOS上运行快速启动脚本遇到的bug 2025-06-23 18:21:01 -04:00
ageerle
8be480e06c Update README.md 2025-06-23 16:49:13 +08:00
ageerle
11286de676 Merge pull request #118 from MuSan-Li/feature_20250610_add_prompt_template
Feature 20250610 add prompt template
2025-06-23 16:36:40 +08:00
MuSan-Li
5aaf0a672c feat: 删除fork文件 2025-06-12 17:55:19 +08:00
MuSan-Li
0089706336 添加提示词模板字典相关SQL 2025-06-12 17:37:04 +08:00
MuSan-Li
cc129801b9 添加提示词模板 2025-06-12 17:06:06 +08:00
zhouweiyi
6a73e09ac7 refactor(DashscopeServiceImpl): 移除静态变量并改进日志输出逻辑
将静态变量改为实例变量以避免并发问题
重构日志输出逻辑,仅在最后响应时输出完整内容
添加异常堆栈打印以方便调试
2025-06-09 18:01:51 +08:00
GH Action - Upstream Sync
e1dc22348c Merge branch 'main' of https://github.com/ageerle/ruoyi-ai 2025-06-07 01:53:26 +00:00
ageerle
f37e4da669 feat: 更新二维码 2025-06-06 10:13:02 +08:00
GH Action - Upstream Sync
3e097d9a68 Merge branch 'main' of https://github.com/ageerle/ruoyi-ai 2025-06-06 01:54:02 +00:00
ageerle
97ae5a46cd feat: 调整sql脚本 2025-06-05 16:16:32 +08:00
ageerle
baa664ac4f feat: 图片识别功能优化 2025-06-05 16:00:06 +08:00
ageerle
353fbf26b8 Merge pull request #115 from Code-Mr-Jiu/main
上传图片支持使用后台image分类下通义千问模型
2025-06-05 13:53:20 +08:00
ageerle
f79b4ec012 Merge pull request #114 from Code-Mr-Jiu/jiuyi-dev
上传图片支持使用后台image分类模型
2025-06-05 13:53:09 +08:00
酒亦
0a73cb4e17 上传图片支持使用后台image分类下通义千问模型 2025-06-05 12:05:28 +08:00
zhouweiyi
cbe882af66 chore: 更新百炼模型配置的API密钥
将开发环境和本地环境的百炼模型配置中的API密钥统一更新为占位符'sk-xxxx'
2025-06-04 18:02:26 +08:00
zhouweiyi
1d51a103d0 feat: 集成阿里百炼API实现图片内容识别功能
添加DashscopeService接口及实现,用于调用阿里百炼API进行图片内容识别
修改PdfImageExtractService增加基于百炼API的图片处理逻辑
新增OSS服务方法支持临时文件处理和删除
更新配置文件添加百炼模型相关配置
2025-06-04 17:55:47 +08:00
酒亦
d635e30b4a 上传图片支持使用后台image分类模型 2025-06-02 08:11:22 +08:00
evo
ca50d1ddfb Create main.yml 2025-05-30 16:05:04 +08:00
ageerle
bd94a3eae8 Merge remote-tracking branch 'origin/main' 2025-05-27 17:55:44 +08:00
ageerle
fcdcf534f1 fix: 修复脱敏后的apikey也被更新了 2025-05-27 17:55:34 +08:00
lindaxia
deefb6cc0b Merge branch 'main' of https://github.com/ageerle/ruoyi-ai 2025-05-26 22:45:18 +08:00
lindaxia
fa96c3d12f feat: 更新deploy脚本 2025-05-26 22:43:54 +08:00
ageerle
77069fdecd feat: 移除聊天、会话角色校验 2025-05-26 22:41:55 +08:00
ageerle
db4a264a52 Merge remote-tracking branch 'origin/main' 2025-05-26 22:40:17 +08:00
ageerle
70ae7ea8f1 feat: 向量库查询删除逻辑调整 2025-05-26 22:40:01 +08:00
lindaxia
6a93856d90 feat: 更新deploy脚本 2025-05-26 22:24:17 +08:00
lindaxia
71cae94815 Revert "lindaxia"
This reverts commit a2905d08f9.
2025-05-26 22:20:51 +08:00
lindaxia
a2905d08f9 lindaxia
feat: 更新deploy
2025-05-26 22:16:23 +08:00
ageerle
463ad6c583 Merge remote-tracking branch 'origin/main' 2025-05-26 12:56:45 +08:00
ageerle
abcde9e36e refactor: 1. 移除图片附件模块 2. 优化import 2025-05-26 12:56:28 +08:00
ageerle
8e723f5b78 Update README.md 2025-05-26 10:21:09 +08:00
ageerle
2021fb5071 feat: 修改项目介绍 2025-05-26 10:15:19 +08:00
ageerle
ecbab7df7b Update README.md 2025-05-26 10:10:00 +08:00
ageerle
d6fcaa0a11 Update README.md 2025-05-26 10:09:28 +08:00
ageerle
58faded4ff Update README.md 2025-05-26 10:09:02 +08:00
ageerle
7cc7af70f0 Update README.md 2025-05-26 10:07:47 +08:00
ageerle
4312a729a2 feat: 会话管理返回创建时间 2025-05-26 09:32:53 +08:00
ageer
5d972d66f4 fix: 代码优化 2025-05-25 14:42:22 +08:00
ageerle
1bff2791a6 Merge pull request #108 from liudalian/20250520
集成国产大模型 ds、zhipu、qianwen
2025-05-25 14:36:23 +08:00
菩提取一叶
a76769e540 add:集成国产大模型 ds、zhipu、qianwen 2025-05-25 14:30:05 +08:00
ageer
bedffffd86 fix: 修复对话时无法上传文件 2025-05-24 23:59:40 +08:00
ageer
2884c37599 feat: 移除微信支付 2025-05-24 23:07:59 +08:00
ageer
c25749c9ca feat: 更新sql脚本 2025-05-24 23:02:59 +08:00
ageer
147b3fd8f5 feat: 查询ppt apikey 2025-05-24 20:06:19 +08:00
ageerle
373424bd01 feat(更新日志):
更新日志

1. 移除个人微信模块
2. 移除直播模块
3. 移除gpts模块
4. 移除应用商店模块
5. 移除套餐管理模块
6. 移除兑换管理模块

## 微信相关
小程序相关功能迁移至企业版
微信公众号/微信机器人迁移至企业版
微信支付迁移至企业版

## 功能模块
智能体模块迁移至企业版
插件管理改为MCP应用并迁移至企业版

知识库:
excel解析迁移至企业版
pdf图片解析迁移至企业版
milvus qdrant扩展 迁移至企业版
2025-05-24 16:18:18 +08:00
ageerle
287a0b3d70 Update README.md 2025-05-23 10:28:42 +08:00
ageerle
cd7e07bea6 Update README.md 2025-05-23 10:26:48 +08:00
ageerle
7edfae09bc Update README.md 2025-05-23 10:24:32 +08:00
ageer
a1759ddf5a perf(wechat,live): 移除个人/企业微信,直播模块
1. 个人微信移除原因:功能不稳定,且有封号风险 2. 直播模块移除原因:使用频率低
2025-05-22 18:36:35 +08:00
ageerle
5517a5d19b feat: 新增聊天时返回消息id 2025-05-22 17:24:56 +08:00
ageerle
8fb72ba4fe feat: 修改配置 2025-05-22 17:19:00 +08:00
ageerle
24511fac8f refactor: 移除个人微信模块,移除直播模 2025-05-22 17:13:49 +08:00
ageerle
ce9f2a82da feat: 新增会话返回会话id 2025-05-22 17:11:39 +08:00
张鹏翔
0f82711199 perf: 1.优化文件输出路径,避免其他盘符权限问题。
2.采用线程池异步调用多模板OCR图片识别。
2025-05-22 14:05:21 +08:00
ageerle
b81fb7cb3f feat: 修改项目介绍 2025-05-22 11:37:45 +08:00
ageerle
676a63c0d6 feat: 修改项目介绍 2025-05-22 11:30:34 +08:00
ageerle
427736271c feat: 修改项目介绍 2025-05-22 11:28:32 +08:00
ageerle
c39dd7a917 feat: 修改项目介绍 2025-05-22 11:24:17 +08:00
ageerle
ed85fef0de Merge pull request #97 from zhangpengxiang/mineru/dev
feat:结合mineru新增pdf转换结构化数据功能
2025-05-21 14:42:47 +08:00
张鹏翔
22d9d9ba85 新增pdf转md后是否进行图片OCR判断 2025-05-21 14:37:51 +08:00
张鹏翔
86825eeb2e 结合mineru新增pdf转换结构化数据功能 2025-05-21 14:25:44 +08:00
ageerle
c491a98dc7 Merge pull request #95 from MuSan-Li/feature_20250520_fix_email_config
修复用户端注册发送邮件报错
2025-05-21 09:40:11 +08:00
MuSan-Li
2a4978c668 修复用户端注册发送邮件报错 2025-05-20 23:10:27 +08:00
lh
5b4eeee756 更新readme.md 2025-05-20 11:00:21 +08:00
ageerle
53e3180658 Merge pull request #93 from janzhou123/pdf-image
feat:增加knowledge_attach_pic表结构sql
2025-05-20 09:29:40 +08:00
zhouweiyi
e43e14454d feat:增加knowledge_attach_pic表结构sql 2025-05-20 09:22:41 +08:00
ageerle
93ddcd53b1 feat: 对话时记录对话角色 2025-05-19 16:41:55 +08:00
ageerle
0ee47a5c00 feat: 对话时记录对话角色 2025-05-19 16:35:21 +08:00
ageerle
3ceef41ab0 feat: 知识库上传逻辑调整 2025-05-19 16:11:37 +08:00
ageerle
a4e995d46c feat: 新增pdf图片解析分支 2025-05-19 15:33:29 +08:00
ageerle
57693059a1 Merge pull request #91 from xingjisen/main
fix: 自动上传向量时间改为三分钟;修复根据条件删除向量数据报错问题;
2025-05-19 09:21:35 +08:00
邢继森
d18e307e10 fix: 自动上传向量时间改为三分钟;修复根据条件删除向量数据报错问题; 2025-05-19 00:45:32 +08:00
ageerle
eed708c6d3 Merge pull request #90 from alanpeng/main
Add one-step auto-deployment script
2025-05-18 22:11:01 +08:00
root
1093888889 fix the deploy-en.sh by dos2unix command 2025-05-18 15:58:34 +08:00
Alan Peng
e58a420f9f Update README.md 2025-05-18 21:48:36 +08:00
root
4a0c3d132a Update README.md 2025-05-18 15:48:47 +08:00
root
0d81aa640e Add one-step auto-deployment script 2025-05-18 15:48:20 +08:00
ageer
951524bff7 Merge remote-tracking branch 'origin/main' 2025-05-17 12:51:54 +08:00
ageer
c7d0e9337a fix: 修复聊天记录保存异常 2025-05-17 12:51:29 +08:00
ageerle
6955f3c7dc Merge pull request #87 from alanpeng/main
Add weaviate database service
2025-05-17 11:20:10 +08:00
root
a52b017e1a Add weaviate database service 2025-05-17 10:11:16 +08:00
ageer
158a0190b5 feat: 优化会话管理查询逻辑 2025-05-16 23:52:43 +08:00
ageerle
096fc11313 Merge pull request #86 from alanpeng/main
Containerized service package/imagese build and deploy
2025-05-16 22:36:21 +08:00
root
e9ac795d23 Add manual 2025-05-16 22:31:14 +08:00
root
32f1a6bab1 Containerized service package/imagese build and deploy 2025-05-16 22:29:36 +08:00
ageerle
052069a2df Merge pull request #85 from MuSan-Li/feature_20250516_fix_auth
修复未登录也可以访问接口
2025-05-16 22:11:41 +08:00
MuSan-Li
e1c997883f 修复未登录也可以访问接口 2025-05-16 21:01:37 +08:00
ageerle
031b7da198 Merge pull request #84 from janzhou123/main
feat:pdf文件解析图片和分析图片,上传向量数据库都修改成 成异步处理
2025-05-16 16:29:21 +08:00
zhouweiyi
931af963b1 feat:pdf文件解析图片和分析图片,上传向量数据库都修改成 成异步处理 2025-05-16 13:50:12 +08:00
Albert
f5fa7d5da8 Merge branch 'ageerle:main' into main 2025-05-15 17:49:09 +08:00
zhouweiyi
c6ffbcb3cf feat:pdf文件解析图片和分析图片,上传向量数据库都修改成 成异步处理 2025-05-15 17:46:38 +08:00
ageerle
05ae200ff5 Merge pull request #82 from chenwei1015/fix-20250514-knowledge_info
fix- 20250514.sql, alter单词错误
2025-05-14 17:42:21 +08:00
weizhangfz
e745f772ef fix- 20250514.sql, alter单词错误 2025-05-14 17:23:34 +08:00
zhouweiyi
584212c569 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	ruoyi-modules/ruoyi-chat/src/main/java/org/ruoyi/chat/service/knowledge/KnowledgeInfoServiceImpl.java
2025-05-14 16:13:07 +08:00
zhouweiyi
dc9bf3e25d pdf文件解析成异步处理 2025-05-14 15:41:57 +08:00
ageerle
da84a26c47 feat: 更新sql脚本 2025-05-14 13:56:09 +08:00
ageerle
7d3282c347 feat: 修复知识库上传失败 2025-05-14 13:53:49 +08:00
ageerle
4454be44c3 Update README.md 2025-05-13 14:40:27 +08:00
zhouweiyi
52e0feda01 修改方法描述 2025-05-13 13:44:31 +08:00
ageerle
c89f5d07fb Merge pull request #80 from janzhou123/main
Extract the image from the PDF and call the large model to identify the image content and return it
2025-05-13 11:08:20 +08:00
zhouweiyi
778a7bc21b 提取PDF中的图片并调用大模型,识别图片内容并返回 2025-05-13 11:01:33 +08:00
zhouweiyi
50f5f38996 提取PDF中的图片并调用大模型,识别图片内容并返回 2025-05-13 10:56:05 +08:00
zhouweiyi
32da85daab 提取PDF中的图片并调用大模型,识别图片内容并返回 2025-05-13 10:55:39 +08:00
Albert
3666157d14 Merge pull request #1 from ageerle/main
new
2025-05-13 09:59:36 +08:00
ageer
1a178d0ef8 Merge remote-tracking branch 'origin/main' 2025-05-11 21:38:01 +08:00
ageer
b64b758263 feat: 小助手wx 2025-05-11 21:37:55 +08:00
ageerle
a68c6481ba Update README.md 2025-05-11 21:37:09 +08:00
ageerle
3cbacb5ed9 Update README.md 2025-05-11 21:35:53 +08:00
ageerle
38667797d3 Update README.md 2025-05-11 21:34:42 +08:00
ageerle
9a6acb84a4 Update README.md 2025-05-11 21:33:59 +08:00
ageerle
e3c8a84849 Update README.md 2025-05-11 21:32:39 +08:00
ageerle
cc349f5954 Update README.md 2025-05-11 21:31:01 +08:00
ageerle
e4b78e0510 Update README.md 2025-05-11 21:30:08 +08:00
ageerle
15c6cd272e Update README.md 2025-05-11 21:28:59 +08:00
ageerle
1614eff008 Update README.md 2025-05-11 21:27:49 +08:00
ageerle
c86e88ebd1 Update README.md 2025-05-11 21:26:57 +08:00
ageerle
39ebd85f58 Update README.md 2025-05-11 21:25:50 +08:00
ageer
bde82fe516 feat: 忽略README.md 2025-05-11 19:45:57 +08:00
ageer
2ca650db76 feat: 代码优化 2025-05-11 19:37:52 +08:00
ageer
e3da640737 feat: 代码优化 2025-05-11 18:37:51 +08:00
ageer
e1dea1d9e2 Merge remote-tracking branch 'origin/main' 2025-05-11 17:25:08 +08:00
ageer
84b8d6f675 feat: 支持coze,dify,派欧云等三方模型调用 2025-05-11 17:25:02 +08:00
ageerle
8563428bf9 Merge pull request #77 from zhangpengxiang/ExcelFileLader/dev
feat:补充Excel文档加载类
2025-05-11 14:55:44 +08:00
zhangpengxiang
7b3b727c0e 补充Excel文档加载类 2025-05-11 14:50:11 +08:00
ageerle
08098ff3f3 Merge pull request #76 from zhangpengxiang/ExcelFileLader/dev
增加Excel文档加载功能,处理向量库管理createSchema方法报错问题
2025-05-11 14:31:55 +08:00
zhangpengxiang
fb492d41f3 增加Excel文档加载功能,处理向量库管理createSchema方法WeaviateEmbeddingStore.builder().build();直接构建报错问题。 2025-05-11 10:55:33 +08:00
ageerle
9c2586ab43 Update README.md 2025-05-09 17:24:55 +08:00
ageerle
1bddf5df3b feat: 兼容多平台模型 2025-05-09 16:45:41 +08:00
ageer
57b2f833f8 feat: 修复向量库初始化失败导致程序启动失败 2025-05-08 23:56:40 +08:00
ageer
7907cbeb7f feat: 修复向量库初始化失败导致程序启动失败 2025-05-08 19:31:46 +08:00
ageer
9cfcdd2b9b feat: 代码优化 2025-05-08 19:00:48 +08:00
ageerle
980df20752 feat: 支持milvus、qdrant向量库 2025-05-08 16:09:02 +08:00
ageerle
aa92d232bb feat: Weaviate操作向量库功能优化 2025-05-08 10:41:01 +08:00
ageer
81c0bb5738 feat: Weaviate改为langchain4j方式调用 2025-05-07 22:53:21 +08:00
ageerle
1a645c6e10 feat: 接入langchain4j操作向量库 2025-05-07 17:33:22 +08:00
ageer
731f6ceb6e feat: sql脚本 2025-05-05 15:10:53 +08:00
ageer
8e21245348 feat: sql脚本 2025-05-05 15:04:02 +08:00
ageer
936e157e4a feat: sql脚本 2025-05-05 15:02:57 +08:00
ageer
256f72c487 feat: 支付模块引用 2025-05-03 15:37:56 +08:00
ageer
009aa5f1a5 feat: 添加接口说明 2025-05-03 15:27:46 +08:00
ageer
d22d2cb708 fix(接口文档): 修复接口文档/v3/api-docs无法正常访问 2025-05-03 14:31:20 +08:00
ageer
59104028b6 feat: 会话管理 2025-05-03 10:45:29 +08:00
ageer
c2f6a8321a feat: 会话管理 2025-05-03 10:41:50 +08:00
ageer
5476a4b0b7 fix: 公众号登录功能同步 2025-04-30 19:34:09 +08:00
ageerle
cd490aa0e5 feat: 新增wechat模块 2025-04-30 11:29:23 +08:00
ageerle
b1ff44df4b feat: sse对话样式优化 2025-04-29 11:44:47 +08:00
ageerle
dc6d00f0fc feat: 查询gpts无需登录 2025-04-29 11:00:34 +08:00
ageerle
990da8da6f feat: 增加工具调用超时时间配置 2025-04-24 17:09:52 +08:00
ageerle
8fc7ad0359 feat: mcp开启后才执行工具调用 2025-04-24 10:31:58 +08:00
ageer
8a89d9eb9c fix: 1.合并知识库对话接口 2.修复web无法查询套餐信息 2025-04-23 23:10:31 +08:00
ageer
abfa9ae97b fix: 1.合并知识库对话接口 2.修复web无法查询套餐信息 2025-04-23 23:09:56 +08:00
ageer
bde2e8ff8e fix: 1.合并知识库对话接口 2.修复web无法查询套餐信息 2025-04-23 23:09:22 +08:00
ageer
4d8eb1850f feat: 附件上传逻辑调整 2025-04-22 21:43:40 +08:00
ageer
5aaa3a5662 fix: 1. 修复No static resource tool/gen/list. 2. 修复新增数据库校验异常 2025-04-22 21:24:00 +08:00
ageerle
8fec96f5b2 feat: 默认关闭mcp功能 2025-04-22 18:01:40 +08:00
ageerle
15c306eca2 fix: 根据不同的模型构建对话客户端 2025-04-22 11:28:10 +08:00
ageerle
620ea1fc76 fix: 系统提示词非必填 2025-04-22 11:06:38 +08:00
ageerle
c5c375dc6d fix: 扣费时无法获取用户id 2025-04-22 10:43:54 +08:00
ageerle
1b793e822a fix: 扣费时无法获取用户id 2025-04-22 10:40:36 +08:00
ageerle
6281840f36 fix: 修复后台管理系统登录异常 2025-04-22 09:12:56 +08:00
ageer
f5daa7eb78 feat: 设置server-filesystem默认路径为D盘 防止启动报错 2025-04-21 20:34:05 +08:00
ageer
52cb563383 fix(1.修复/store/appList404 2.修复无法查询数据库 3.修复请求地址'/chat/config/configKey/logoImage',发生系统异常): 2025-04-21 20:16:53 +08:00
ageerle
69efc3261e Merge branch 'dev' 2025-04-21 09:44:09 +08:00
ageerle
fb19fb29cc feat: 移除文件 2025-04-21 09:43:09 +08:00
ageer
9f257cf712 feat: mcp升级依赖 2025-04-20 22:52:09 +08:00
ageer
4af26fe4b4 feat: mcp升级依赖 2025-04-20 22:52:00 +08:00
ageerle
ab2a118ee9 feat: 更新项目说明 2025-04-18 11:11:48 +08:00
ageerle
9cd97a4dc5 feat: mcp支持远程调用 2025-04-18 10:55:42 +08:00
ageerle
788b372e32 feat: mcp支持远程调用 2025-04-17 16:23:32 +08:00
lh
744f9b6c7f 更新readme.md 2025-04-17 11:09:00 +08:00
ageer
761d954ef1 feat: mcp 1.0.0 2025-04-16 21:56:05 +08:00
ageerle
d1006f50ad feat: mcp-1.0.0 2025-04-16 10:25:10 +08:00
ageerle
f24ff5bbdd feat: mcp 测试版本 2025-04-15 17:34:21 +08:00
ageer
a4314dbbde feat: mcp测试版 2025-04-14 23:10:17 +08:00
ageerle
188dc1e55e feat: mcp 测试版本 2025-04-14 17:23:33 +08:00
ageer
731352fd04 feat: mcp测试版 2025-04-14 00:22:21 +08:00
ageer
4b53939002 feat: mcp支持,模块重构 2025-04-12 15:18:16 +08:00
lh
b50c15755d 更新readme.md 2025-04-12 14:19:51 +08:00
ageer
0dc6262b39 feat: mcp支持 2025-04-12 10:09:01 +08:00
ageer
9b32e3b3b2 feat: mcp支持 2025-04-12 10:08:51 +08:00
ageerle
37a8b7dad3 feat: 对话模块重构 2025-04-11 15:47:05 +08:00
ageerle
efeb0bd6fb fix: ollama兼容联网查询 知识库检索 2025-04-11 12:05:13 +08:00
ageerle
af33040117 fix: 语音功能无法使用 2025-04-11 10:45:02 +08:00
ageerle
ac4c037634 feat: 重构模块 2025-04-11 09:51:23 +08:00
ageerle
9734ec53f7 feat: 重构模块 2025-04-11 09:34:37 +08:00
ageer
2b8a92c7d6 fix: 重构模块 2025-04-10 22:18:44 +08:00
ageer
adb4538317 fix: 修复使用本地向量模型只能检索到一条知识内容 2025-04-10 21:39:31 +08:00
ageerle
2509099146 feat: 重构模块 2025-04-10 17:25:23 +08:00
ageerle
3be9005f95 feat: 调整知识库模块 2025-04-09 17:41:29 +08:00
ageerle
be6d027cad feat: 删除代码生成脚本 2025-04-09 09:42:14 +08:00
ageerle
3d679f8749 Merge remote-tracking branch 'origin/dev' into dev 2025-04-09 09:40:28 +08:00
ageer
5a5a48e153 feat: ruoyi-chat-api模块调整 2025-04-09 09:40:17 +08:00
ageer
e5da648941 feat: 代码生成脚本 2025-04-08 21:30:01 +08:00
ageerle
d2755f00bc feat: 测试版本提交 2025-04-08 16:48:06 +08:00
lh
09abc0b5af 新增讨论群信息 2025-04-08 14:05:05 +08:00
ageerle
00f362acf1 Merge remote-tracking branch 'origin/main' 2025-04-08 11:11:10 +08:00
ageerle
65d479458e fix: 修复部门树无法加载 2025-04-08 11:11:00 +08:00
ageerle
57e17e0dda Merge pull request #58 from winkeylucky/winkey-0407-02
修改应用使用,通过配置的应用的appid modelname字段关联 0407 02
2025-04-08 09:12:50 +08:00
winkey
691d1735fc 数据库更新 2025-04-07 22:42:06 +08:00
winkey
360984bc4b 应用增加系统角色提示词 2025-04-07 22:41:56 +08:00
winkey
0153f004f4 修改应用使用,通过配置的应用的appid modelname字段关联 2025-04-07 22:41:33 +08:00
ageerle
cc23508527 Merge pull request #57 from winkeylucky/winkey_0407
向量模型通过模型管理获取配置
2025-04-07 16:00:33 +08:00
winkey
c884f4f2d3 向量模型插入模型库sql 2025-04-07 15:27:20 +08:00
winkey
fab6de1f5c 知识库对话修改 2025-04-07 15:16:50 +08:00
winkey
c02f66636d 向量模型通过模型管理获取配置 2025-04-07 15:16:31 +08:00
ageerle
c1162148b1 Merge pull request #50 from winkeylucky/winkey-0502
解决登录异常_和公共资源例外 0502
2025-04-07 14:02:35 +08:00
ageerle
f76fdbf3ad Merge pull request #46 from winkeylucky/winkey-main
增加deepseek深度思考返回
2025-04-07 14:02:18 +08:00
winkey
feca08b3ec 获取公告和版权信息权限例外 2025-04-02 15:13:53 +08:00
winkey
72675b17c4 解决登录异常问题 2025-04-02 15:13:39 +08:00
ageerle
9ea5186f49 feat: 微信机器人应用脚本 2025-04-02 10:51:53 +08:00
winkey
d0a2eadc38 增加deepseek深度思考返回 2025-04-01 13:34:31 +08:00
1966 changed files with 59040 additions and 201921 deletions

32
.editorconfig Normal file
View File

@@ -0,0 +1,32 @@
# http://editorconfig.org
root = true
# 所有文件 ([*])
# 使用空格缩进,每级 4 个空格
# UTF-8 编码
# Unix 风格换行符 (LF)
# 自动删除行尾空格
# 文件末尾自动添加空行
# JSON/YAML 文件:
# 缩进改为 2 个空格
# Markdown 文件:
# 不在末尾添加空行
# 保留行尾空格Markdown 语法需要)
# 作用:
# 支持此标准的编辑器VS Code、IDEA、Sublime 等)会自动读取并应用这些规则,确保不同开发者使用不同编辑器时,代码格式保持一致。
[*]
indent_style = space
indent_size = 4
charset = utf-8
end_of_line = lf
trim_trailing_whitespace = true
insert_final_newline = true
[*.{json,yml,yaml}]
indent_size = 2
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false

41
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,41 @@
---
name: 漏洞报告
about: 报告项目中的Bug或安全问题
title: '[Bug] '
labels: 'bug'
assignees: ''
---
## 问题描述
简要描述遇到的问题:
## 复现步骤
1.
2.
3.
## 期望行为
描述你期望发生的情况:
## 实际行为
描述实际发生的情况:
## 环境信息
| 项目 | 信息 |
|:---|:---|
| 操作系统 | |
| JDK版本 | |
| 项目版本 | |
## 截图/日志
如有相关信息,请在此粘贴:
## 补充说明
其他补充信息:

1
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1 @@
blank_issues_enabled: true

15
.github/ISSUE_TEMPLATE/custom.md vendored Normal file
View File

@@ -0,0 +1,15 @@
---
name: 自定义
about: 其他问题或讨论
title: ''
labels: ''
assignees: ''
---
## 描述
请详细描述你的问题或需求:
## 补充信息
如有其他补充,请在此填写:

View File

@@ -0,0 +1,57 @@
---
name: 企业AI应用合作登记
about: 登记企业信息与合作意向,获取免费技术支持
title: '[合作登记] 企业AI应用需求登记'
labels: '合作登记'
assignees: ''
---
## 登记说明
感谢您关注 RuoYi AI我们团队今年的重点是帮助企业落地AI应用如果贵公司符合要求我们可以提供**免费的技术支持**。
请在下方评论中填写登记信息,格式如下:
---
## 登记格式预览
| 字段 | 内容 |
|:---|:---|
| 公司名称 | (必填) |
| 公司Logo地址 | (可选) |
| 所属行业 | (必填) |
| 公司所在地 | (必填) |
| 项目名称 | (必填) |
| 项目Logo地址 | (可选) |
| 项目简介 | (必填) |
| 当前AI应用状态 | □ 尚未开始 □ 规划中 □ 已有初步应用 □ 已有成熟应用 |
| 计划落地时间 | □ 1个月内 □ 1-3个月 □ 3-6个月 □ 6个月以上 |
| 当前痛点或挑战 | (可选) |
| 公司简介 | (可选) |
---
## 可复制格式
复制下方内容并在评论中填写:
```
| 字段 | 内容 |
|:---|:---|
| 公司名称 | |
| 公司Logo地址 | |
| 所属行业 | |
| 公司所在地 | |
| 项目名称 | |
| 项目Logo地址 | |
| 项目简介 | |
| 当前AI应用状态 | |
| 计划落地时间 | |
| 当前痛点或挑战 | |
| 公司简介 | |
```
---
> **温馨提示**:提交的信息仅用于合作沟通,不会对外公开。

View File

@@ -0,0 +1,31 @@
---
name: 想法建议
about: 提出新功能建议或改进想法
title: '[Feature] '
labels: 'enhancement'
assignees: ''
---
## 建议类型
□ 新功能 □ 功能改进 □ 文档完善 □ 其他
## 建议描述
清晰描述你的建议内容:
## 使用场景
描述这个功能在什么场景下会用到:
## 期望效果
描述你期望的效果:
## 参考示例
如有类似的参考实现或产品,请提供链接:
## 补充说明
其他补充信息:

11
.gitignore vendored
View File

@@ -1,9 +1,6 @@
######################################################################
# Build Tools
.gradle
/build/
!gradle/wrapper/gradle-wrapper.jar
@@ -11,8 +8,6 @@
target/
!.mvn/wrapper/maven-wrapper.jar
ruoyi-modules/ruoyi-generator/src/main/resources/vm/vben5
######################################################################
# IDE
@@ -26,10 +21,13 @@ ruoyi-modules/ruoyi-generator/src/main/resources/vm/vben5
### IntelliJ IDEA ###
.idea
.claude
.github
*.iws
*.iml
*.ipr
### JRebel ###
rebel.xml
@@ -43,11 +41,14 @@ nbdist/
######################################################################
# Others
*.log
*.log.gz
*.xml.versionsBackup
*.swp
data/
!*/build/*.java
!*/build/*.html
!*/build/*.xml
.flattened-pom.xml
/.claude/settings.local.json

36
Dockerfile.backend Normal file
View File

@@ -0,0 +1,36 @@
# RuoYi-AI 后端 Dockerfile
# 基于 Maven + OpenJDK 17
FROM maven:3.9-eclipse-temurin-17 AS builder
# 设置工作目录
WORKDIR /build
# 复制 pom.xml 和源码
COPY pom.xml .
COPY ruoyi-admin ./ruoyi-admin
COPY ruoyi-common ./ruoyi-common
COPY ruoyi-modules ./ruoyi-modules
COPY ruoyi-extend ./ruoyi-extend
# 构建项目 (使用 prod profile)
RUN mvn clean package -Pprod -DskipTests
# 最终运行镜像
FROM eclipse-temurin:17-jre-alpine
# 设置工作目录
WORKDIR /app
# 从构建阶段复制 jar 包
COPY --from=builder /build/ruoyi-admin/target/ruoyi-admin.jar ./ruoyi-admin.jar
# 创建日志目录
RUN mkdir -p /ruoyi/server/logs
# 暴露端口
EXPOSE 6039
# 启动命令
ENTRYPOINT ["java", "-jar", "ruoyi-admin.jar", "--spring.profiles.active=prod"]

21
Dockerfile.mysql Normal file
View File

@@ -0,0 +1,21 @@
# 基于官方MySQL 8.0镜像构建自定义镜像
# 构建命令: docker build -t registry.cn-hangzhou.aliyuncs.com/ruoyi-ai/mysql:v3 -f Dockerfile.mysql .
FROM mysql:8.0.33
# 设置时区
ENV TZ=Asia/Shanghai
# 复制初始化脚本和SQL文件到镜像中
COPY docs/script/docker/mysql/init/init-db.sh /docker-entrypoint-initdb.d/init-db.sh
COPY docs/script/sql/ruoyi-ai-v3_mysql8.sql /docker-entrypoint-initdb.d/ruoyi-ai-v3_mysql8.sql
# 设置脚本可执行权限
RUN chmod +x /docker-entrypoint-initdb.d/init-db.sh
# MySQL启动参数
CMD ["--default-authentication-plugin=mysql_native_password", \
"--character-set-server=utf8mb4", \
"--collation-server=utf8mb4_general_ci", \
"--explicit_defaults_for_timestamp=true", \
"--lower_case_table_names=1", \
"--skip-ssl"]

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2023 ruoyi-ai
Copyright (c) 2026 ruoyi-ai
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in

427
README.md
View File

@@ -1,10 +1,6 @@
# RuoYi AI
<!-- PROJECT SHIELDS -->
<div align="center">
[![Contributors][contributors-shield]][contributors-url]
[![Forks][forks-shield]][forks-url]
@@ -13,222 +9,293 @@
[![MIT License][license-shield]][license-url]
<!-- PROJECT LOGO -->
<br />
<img style="text-align: center;" src="image/00.png" alt="Logo" width="150" height="150">
<h3 style="text-align: center;">快速搭建属于自己的 AI 助手平台</h3>
<p style="text-align: center;">
全新升级,开箱即用,简单高效
<br />
<a href="https://doc.pandarobot.chat"><strong>探索本项目的文档 »</strong></a>
<br />
<br />
<a href="https://web.pandarobot.chat">项目预览</a>
·
<a href="https://github.com/ageerle/ruoyi-ai/issues">报告Bug</a>
·
<a href="https://github.com/ageerle/ruoyi-ai/issues">提出新特性</a>
<p align="center">
<a href="https://trendshift.io/repositories/13209">
<img src="https://trendshift.io/api/badge/repositories/13209" alt="GitHub Trending">
</a>
</p>
## 目录
<img src="docs/image/logo.png" alt="RuoYi AI Logo" width="120" height="120">
- [源码地址](#源码地址)
- [特色功能](#特色功能)
- [项目演示](#项目演示)
- [后台管理](#后台管理)
- [用户端](#用户端)
- [小程序端](#小程序端)
- [开发前的配置要求](#开发前的配置要求)
- [文件目录说明](#文件目录说明)
- [使用到的框架](#使用到的框架)
- [贡献者](#贡献者)
- [如何参与开源项目](#如何参与开源项目)
- [版本控制](#版本控制)
- [作者](#作者)
- [鸣谢](#鸣谢)
### 企业级AI助手平台
### 源码地址
- 项目文档: https://doc.pandarobot.chat
- 前端-后台管理: https://github.com/ageerle/ruoyi-admin
- 前端-用户端: https://github.com/ageerle/ruoyi-web
- 小程序端: https://github.com/ageerle/ruoyi-uniapp
- 演示地址: https://web.pandarobot.chat
- 后台管理: https://admin.pandarobot.chat
- 用户名: admin 密码admin123
*开箱即用的全栈AI平台支持多智能体协同、Supervisor模式编排、多种决策模型提供先进的RAG技术和可视化流程编排能力*
### gitcode源码地址
- https://gitcode.com/ageerle/ruoyi-ai
- https://gitcode.com/ageerle/ruoyi-web
- https://gitcode.com/ageerle/ruoyi-admin
- https://gitcode.com/ageerle/ruoyi-uniapp
**[English](README_EN.md)** | **[📖 使用文档](https://doc.pandarobot.chat)** |
**[🚀 在线体验](https://web.pandarobot.chat)** | **[🐛 问题反馈](https://github.com/ageerle/ruoyi-ai/issues)** | **[💡 功能建议](https://github.com/ageerle/ruoyi-ai/issues)**
### 特色功能
1. 全套开源系统提供完整的前端应用、后台管理以及小程序应用基于MIT协议开箱即用。
2. 本地RAG方案集成Milvus/Weaviate向量库、本地向量化模型与Ollama实现本地化RAG
3. 丰富插件功能支持联网、SQL查询插件及Text2API插件扩展系统能力与应用场景。
4. 内置SSE、websocket等网络协议支持对接多种大语言模型同时还集成了MidJourney和DALLE AI绘画功能
5. 强大的多媒体功能支持AI翻译、PPT制作、语音克隆和翻唱等
6. 扩展功能:支持将大模型接入个人或企业微信
7. 支付功能:支持易支付、微信支付等多种支付方式
### 项目演示
#### 后台管理
<div style="display: flex; flex-wrap: wrap; gap: 20px; justify-content: center;">
<img src="image/02.png" alt="drawing" style="width: 600px; height: 300px; border: 2px solid #ddd; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1);"/>
<img src="image/03.png" alt="drawing" style="width: 600px; height: 300px; border: 2px solid #ddd; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1);"/>
<img src="image/04.png" alt="drawing" style="width: 600px; height: 300px; border: 2px solid #ddd; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1);"/>
<img src="image/05.png" alt="drawing" style="width: 600px; height: 300px; border: 2px solid #ddd; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1);"/>
</div>
#### 用户端
<div style="display: flex; flex-wrap: wrap; gap: 20px; justify-content: center;">
<img src="image/08.png" alt="drawing" style="width: 600px; height: 300px; border: 2px solid #ddd; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1);"/>
<img src="image/09.png" alt="drawing" style="width: 600px; height: 300px; border: 2px solid #ddd; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1);"/>
<img src="image/10.png" alt="drawing" style="width: 600px; height: 300px; border: 2px solid #ddd; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1);"/>
<img src="image/11.png" alt="drawing" style="width: 600px; height: 300px; border: 2px solid #ddd; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1);"/>
</div>
#### 小程序端
<div style="display: flex; flex-wrap: wrap; gap: 20px; justify-content: flex-start;">
<img src="image/06.png" alt="drawing" style="width: 320px; height: 600px; border: 2px solid #ddd; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1);"/>
<img src="image/07.png" alt="drawing" style="width: 320px; height: 600px; border: 2px solid #ddd; border-radius: 8px; box-shadow: 0 4px 8px rgba(0,0,0,0.1);"/>
</div>
### 开发前的配置要求
1. jdk 17
2. mysql 5.7、8.0
3. redis 版本必须 >= 5.X
4. maven 3.8+
5. nodejs 20+ & pnpm
### 文件目录说明
RuoYi-AI
```
├─ ruoyi-admin // 管理模块
│ └─ RuoYiApplication // 启动类
│ └─ RuoYiServletInitializer // 容器部署初始化类
│ └─ resources // 资源文件
│ └─ i18n/messages.properties // 国际化配置文件
│ └─ application.yml // 框架总配置文件
│ └─ application-dev.yml // 开发环境配置文件
│ └─ application-prod.yml // 生产环境配置文件
│ └─ banner.txt // 框架启动图标
│ └─ logback-plus.xml // 日志配置文件
│ └─ ip2region.xdb // IP区域地址库
├─ ruoyi-common // 通用模块
│ └─ ruoyi-common-bom // common依赖包管理
└─ ruoyi-common-chat // 聊天模块
│ └─ ruoyi-common-core // 核心模块
│ └─ ruoyi-common-doc // 系统接口模块
│ └─ ruoyi-common-encrypt // 数据加解密模块
│ └─ ruoyi-common-excel // excel模块
│ └─ ruoyi-common-idempotent // 幂等功能模块
│ └─ ruoyi-common-json // 序列化模块
│ └─ ruoyi-common-log // 日志模块
│ └─ ruoyi-common-mail // 邮件模块
│ └─ ruoyi-common-mybatis // 数据库模块
│ └─ ruoyi-common-oss // oss服务模块
│ └─ ruoyi-common-pay // 支付模块
│ └─ ruoyi-common-ratelimiter // 限流功能模块
│ └─ ruoyi-common-redis // 缓存服务模块
│ └─ ruoyi-common-satoken // satoken模块
│ └─ ruoyi-common-security // 安全模块
│ └─ ruoyi-common-sensitive // 脱敏模块
│ └─ ruoyi-common-sms // 短信模块
│ └─ ruoyi-common-tenant // 租户模块
│ └─ ruoyi-common-translation // 通用翻译模块
│ └─ ruoyi-common-web // web模块
├─ ruoyi-modules // 模块组
│ └─ ruoyi-demo // 演示模块
│ └─ ruoyi-system // 业务模块
├─ .run // 执行脚本文件
├─ .editorconfig // 编辑器编码格式配置
├─ LICENSE // 开源协议
├─ pom.xml // 公共依赖
├─ README.md // 框架说明文件
## ✨ 核心亮点
| 模块 | 现有能力 | 扩展方向 |
|:----------:|---|------------------------|
| **模型管理** | 多模型接入(OpenAI/DeepSeek/通义/智谱)、多模态理解、Coze/DIFY/FastGPT平台集成 | 自动模式、容错机制、计费管理 |
| **知识管理** | 本地RAG + 向量库(Milvus/Weaviate) + 文档解析 | 多模态、知识出处、知识图谱、重排序 |
| **工具管理** | Mcp协议集成、Skills能力 + 可扩展工具生态 | 工具插件市场、 |
| **流程编排** | 可视化工作流设计器、节点拖拽编排、SSE流式执行,目前已经支持模型调用,邮件发送,人工审核等节点 | 更多节点类型 |
| **多智能体** | 基于Langchain4j的Agent框架、Supervisor模式编排,支持多种决策模型 | 智能体可配置 |
## 🚀 快速体验
### 在线演示
| 平台 | 地址 | 账号 |
|:------:|---|---|
| 用户端 | [web.pandarobot.chat](https://web.pandarobot.chat) | admin / admin123 |
| 管理后台 | [admin.pandarobot.chat](https://admin.pandarobot.chat) | admin / admin123 |
### 项目源码
| 项目模块 | GitHub 仓库 | Gitee 仓库 | GitCode 仓库 |
|----------|-------------------------------------------------------|------------------------------------------------------|--------------------------------------------------------|
| 🔧 后端服务 | [ruoyi-ai](https://github.com/ageerle/ruoyi-ai) | [ruoyi-ai](https://gitee.com/ageerle/ruoyi-ai) | [ruoyi-ai](https://gitcode.com/ageerle/ruoyi-ai) |
| 🎨 用户前端 | [ruoyi-web](https://github.com/ageerle/ruoyi-web) | [ruoyi-web](https://gitee.com/ageerle/ruoyi-web) | [ruoyi-web](https://gitcode.com/ageerle/ruoyi-web) |
| 🛠️ 管理后台 | [ruoyi-admin](https://github.com/ageerle/ruoyi-admin) | [ruoyi-admin](https://gitee.com/ageerle/ruoyi-admin) | [ruoyi-admin](https://gitcode.com/ageerle/ruoyi-admin) |
### 合作项目
| 项目名称 | GitHub 仓库 | Gitee 仓库
|----------------|-------------------------------------------------------|------------------------------------------------------|
| element-plus-x | [element-plus-x](https://github.com/element-plus-x/Element-Plus-X) | [element-plus-x](https://gitee.com/he-jiayue/element-plus-x) |
## 🛠️ 技术架构
### 核心框架
- **后端架构**Spring Boot 4.0 + Spring ai 2.0 + Langchain4j
- **数据存储**MySQL 8.0 + Redis + 向量数据库Milvus/Weaviate
- **前端技术**Vue 3 + Vben Admin + element-plus-x
- **安全认证**Sa-Token + JWT 双重保障
## 🐳 Docker 部署
本项目提供两种 Docker 部署方式:
### 方式一:一键启动所有服务(推荐)
使用 `docker-compose-all.yaml` 可以一键启动所有服务(包括后端、管理端、用户端及依赖服务):
```bash
# 克隆仓库
git clone https://github.com/ageerle/ruoyi-ai.git
cd ruoyi-ai
# 启动所有服务(从镜像仓库拉取预构建镜像)
docker-compose -f docker-compose-all.yaml up -d
# 查看服务状态
docker-compose -f docker-compose-all.yaml ps
# 访问服务
# 管理端: http://localhost:25666 (admin / admin123)
# 用户端: http://localhost:25137
# 后端API: http://localhost:26039
```
### 版本控制
### 方式二:分步部署(源码编译)
该项目使用Git进行版本管理。您可以在repository参看当前可用版本。
如果您需要从源码构建后端服务,请按照以下步骤操作:
#### 第一步:部署后端服务
```bash
# 进入后端项目目录
cd ruoyi-ai
### 版权说明
# 启动后端服务(源码编译构建)
docker-compose up -d --build
该项目使用了MIT授权许可详情请参阅 [LICENSE.txt](https://github.com/ageerle/ruoyi-ai/blob/main/LICENSE)
# 等待后端服务启动完成
docker-compose logs -f backend
```
### 项目现状
#### 第二步:部署管理端
目前项目还处于早期阶段距离成熟还有很长的路要走。由于个人精力有限项目的发展速度受到了一定的限制。为了加快项目的进度我真诚地希望更多人能够参与到项目中来。无论是经验丰富的开发者还是刚刚入门的小白我都热烈欢迎你们提交Pull RequestPR。即使代码修改得很少或者存在一些错误都没有关系。我会认真审核每一位贡献者的代码并和大家一起完善项目。
```bash
# 进入管理端项目目录
cd ruoyi-admin
### 开发计划
# 构建并启动管理端
docker-compose up -d --build
- 智能体管理
# 访问管理端
# 地址: http://localhost:5666
```
#### 第三步:部署用户端(可选)
```bash
# 进入用户端项目目录
cd ruoyi-web
# 构建并启动用户端
docker-compose up -d --build
# 访问用户端
# 地址: http://localhost:5137
```
### 服务端口说明
| 服务 | 一键启动端口 | 分步部署端口 | 说明 |
|------|-------------|-------------|------|
| 管理端 | 25666 | 5666 | 管理后台访问地址 |
| 用户端 | 25137 | 5137 | 用户前端访问地址 |
| 后端服务 | 26039 | 6039 | 后端 API 服务 |
| MySQL | 23306 | 23306 | 数据库服务 |
| Redis | 26379 | 6379 | 缓存服务 |
| Weaviate | 28080 | 28080 | 向量数据库 |
| MinIO API | 29000 | 9000 | 对象存储 API |
| MinIO Console | 29090 | 9090 | 对象存储控制台 |
### 镜像仓库
所有镜像托管在阿里云容器镜像服务:
```
crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai
```
可用镜像:
- `mysql:v3` - MySQL 数据库(包含初始化 SQL
- `redis:6.2` - Redis 缓存
- `weaviate:1.30.0` - 向量数据库
- `minio:latest` - 对象存储
- `ruoyi-ai-backend:latest` - 后端服务
- `ruoyi-ai-admin:latest` - 管理端前端
- `ruoyi-ai-web:latest` - 用户端前端
### 常用命令
```bash
# 停止所有服务
docker-compose -f docker-compose-all.yaml down
# 查看服务日志
docker-compose -f docker-compose-all.yaml logs -f [服务名]
# 重启某个服务
docker-compose -f docker-compose-all.yaml restart [服务名]
```
## 📚 使用文档
想要深入了解安装部署、功能配置和二次开发?
**👉 [完整使用文档](https://doc.pandarobot.chat)**
## 🤝 参与贡献
我们热烈欢迎社区贡献!无论您是资深开发者还是初学者,都可以为项目贡献力量 💪
### 贡献方式
1. **Fork** 项目到您的账户
2. **创建分支** (`git checkout -b feature/新功能名称`)
3. **提交代码** (`git commit -m '添加某某功能'`)
4. **推送分支** (`git push origin feature/新功能名称`)
5. **发起 Pull Request**
> 💡 **小贴士**:建议将 PR 提交到 GitHub我们会自动同步到其他代码托管平台
## 📄 开源协议
本项目采用 **MIT 开源协议**,详情请查看 [LICENSE](LICENSE) 文件。
## 🙏 特别鸣谢
感谢以下优秀的开源项目为本项目提供支持:
- [Spring AI Alibaba Copilot](https://github.com/spring-ai-alibaba/copilot) - 基于spring-ai-alibaba
的智能编码助手
- [Langchain4j](https://github.com/langchain4j/langchain4j) - 强大的 Java LLM 开发框架
- [RuoYi-Vue-Plus](https://gitee.com/dromara/RuoYi-Vue-Plus) - 成熟的企业级快速开发框架
- [Vben Admin](https://github.com/vbenjs/vue-vben-admin) - 现代化的 Vue 后台管理模板
## 🌐 生态伙伴
- [PPIO 派欧云](https://ppinfra.com/user/register?invited_by=P8QTUY&utm_source=github_ruoyi-ai) - 提供高性价比的 GPU
算力和模型 API 服务
- [优云智算](https://www.compshare.cn/?ytag=GPU_YY-gh_ruoyi) - 万卡RTX40系GPU+海内外主流模型API服务秒级响应按量计费新客免费用。
## 优秀开源项目及社区推荐
- [imaiwork](https://gitee.com/tsinghua-open/imaiwork) - AI手机开源版AI获客手机项目基于无障碍模式RPA比豆包AI手机更强大。
## 💬 社区交流
<div align="center">
<table>
<tr>
<td align="center">
<img src="docs/image/wx.png" alt="微信二维码" width="200" height="200"><br>
<strong>扫码添加作者微信</strong><br>
<em>邀请进群学习</em>
</td>
<td align="center">
<img src="docs/image/qq.png" alt="QQ群二维码" width="200" height="200"><br>
<strong>QQ技术交流群</strong><br>
<em>技术讨论</em>
</td>
</tr>
</table>
通过设置提示词、插件、知识库等用户可以快速构建一个AI应用。这将极大地简化AI应用的开发流程降低开发门槛使更多企业能够轻松地利用AI技术。
<div>
<img src="image/13.png" alt="drawing" width="600px" height="300px"/>
</div>
- 流程编排
---
通过流程编排功能,用户可以将不同的模型按照业务逻辑进行有序连接。这将解决单一模型能力不足的问题,充分发挥多个模型的协同作用,从而更好地满足企业的复杂业务需求。
- 感谢
## 📺 视频教程
最后我要感谢RuoYi-Vue-Plus、chatgpt-java、chatgpt-web-midjourney-proxy等优秀框架。正是因为这些项目的开源和共享我才能够在这个基础上进行开发使我们的项目能够取得今天的成果。再次感谢这些项目及其背后的开发者们
<div align="center">
希望更多志同道合的朋友能够加入我们共同推动这个项目的发展。让我们一起努力将这个项目打造成一个真正成熟、实用的AI平台
<table>
<tr>
<td align="center">
<img src="docs/image/dy.png" alt="微信二维码" width="200" height="200"><br>
<strong>打开抖音扫一扫</strong><br>
<em>获取免费视频教程</em>
</td>
<td align="center">
<img src="docs/image/bibi.png" alt="QQ群二维码" width="200" height="200"><br>
<strong>打开B站扫一扫</strong><br>
<em>获取免费视频教程</em>
</td>
#### 如何参与开源项目
</tr>
</table>
贡献使开源社区成为一个学习、激励和创造的绝佳场所。你所作的任何贡献都是**非常感谢**的。
</div>
1. Fork 这个项目
2. 创建你的功能分支 (`git checkout -b feature/dev`)
3. 提交你的更改 (`git commit -m 'Add some dev'`)
4. 推送到分支 (`git push origin feature/dev`)
5. 打开拉取请求
6. pr请提交到GitHub上会定时同步到gitee
<div align="center">
#### 项目文档
1. 项目文档基于vitepress构建
2. 按照[如何参与开源项目](#如何参与开源项目)拉取 https://github.com/ageerle/ruoyi-doc
3. 安装依赖npm install
4. 启动项目npm run docs:dev
5. 主页路径docs/guide/introduction/index.md
**[⭐ 点个Star支持一下](https://github.com/ageerle/ruoyi-ai)** • **[ Fork 开始贡献](https://github.com/ageerle/ruoyi-ai/fork)** • **[📚 English](README_EN.md)** • **[📖 查看完整文档](https://doc.pandarobot.chat)**
### 鸣谢
- [chatgpt-java](https://github.com/Grt1228/chatgpt-java)
- [RuoYi-Vue-Plus](https://gitee.com/dromara/RuoYi-Vue-Plus)
- [chatgpt-web-midjourney-proxy](https://github.com/Dooy/chatgpt-web-midjourney-proxy)
- [Vben Admin](https://github.com/vbenjs/vue-vben-admin)
- [Naive UI](https://www.naiveui.com)
*用 ❤️ 打造,由 RuoYi AI 开源社区维护*
</div>
<!-- Badge Links -->
<!-- links -->
[your-project-path]:https://github.com/ageerle/ruoyi-ai
[contributors-shield]: https://img.shields.io/github/contributors/ageerle/ruoyi-ai.svg?style=flat-square
[contributors-url]: https://github.com/ageerle/ruoyi-ai/graphs/contributors
[forks-shield]: https://img.shields.io/github/forks/ageerle/ruoyi-ai.svg?style=flat-square
[forks-url]: https://github.com/ageerle/ruoyi-ai/network/members
[stars-shield]: https://img.shields.io/github/stars/ageerle/ruoyi-ai.svg?style=flat-square
[stars-url]: https://github.com/ageerle/ruoyi-ai/stargazers
[issues-shield]: https://img.shields.io/github/issues/ageerle/ruoyi-ai.svg?style=flat-square
[issues-url]: https://img.shields.io/github/issues/ageerle/ruoyi-ai.svg
[issues-url]: https://github.com/ageerle/ruoyi-ai/issues
[license-shield]: https://img.shields.io/github/license/ageerle/ruoyi-ai.svg?style=flat-square
[license-url]: https://github.com/ageerle/ruoyi-ai/blob/master/LICENSE.txt
[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=flat-square&logo=linkedin&colorB=555
[license-url]: https://github.com/ageerle/ruoyi-ai/blob/main/LICENSE

153
README_EN.md Normal file
View File

@@ -0,0 +1,153 @@
# RuoYi AI
<div align="center">
[![Contributors][contributors-shield]][contributors-url]
[![Forks][forks-shield]][forks-url]
[![Stargazers][stars-shield]][stars-url]
[![Issues][issues-shield]][issues-url]
[![MIT License][license-shield]][license-url]
<p align="center">
<a href="https://trendshift.io/repositories/13209">
<img src="https://trendshift.io/api/badge/repositories/13209" alt="GitHub Trending">
</a>
</p>
<img src="docs/image/logo.png" alt="RuoYi AI Logo" width="120" height="120">
### Enterprise-Grade AI Assistant Platform
*An out-of-the-box full-stack AI platform supporting multi-agent collaboration, Supervisor mode orchestration, and multiple decision models, with advanced RAG technology and visual workflow orchestration capabilities*
**[中文](README.md)** | **[📖 Documentation](https://doc.pandarobot.chat)** |
**[🚀 Live Demo](https://web.pandarobot.chat)** | **[🐛 Report Issues](https://github.com/ageerle/ruoyi-ai/issues)** | **[💡 Feature Requests](https://github.com/ageerle/ruoyi-ai/issues)**
</div>
## ✨ Core Features
| Module | Current Capabilities | Extension Direction |
|:---:|---|---|
| **Model Management** | Multi-model integration (OpenAI/DeepSeek/Tongyi/Zhipu), multi-modal understanding, Coze/DIFY/FastGPT platform integration | Auto mode, fault tolerance |
| **Knowledge Base** | Local RAG + Vector DB (Milvus/Weaviate) + Knowledge Graph + Document parsing + Reranking | Audio/video parsing, knowledge source |
| **Tool Management** | MCP protocol integration, Skills capability + Extensible tool ecosystem | Tool plugin marketplace, toolAgent auto-loading |
| **Workflow Orchestration** | Visual workflow designer, drag-and-drop node orchestration, SSE streaming execution, currently supports model (with RAG) calls, email sending, manual review nodes | More node types |
| **Multi-Agent** | Agent framework based on Langchain4j, Supervisor mode orchestration, supports multiple decision models | Configurable agents |
| **AI Coding** | Intelligent code analysis, project scaffolding generation, Copilot assistant | Code generation optimization |
## 🚀 Quick Start
### Live Demo
| Platform | URL | Account |
|:------:|---|---|
| User Frontend | [web.pandarobot.chat](https://web.pandarobot.chat) | admin / admin123 |
| Admin Panel | [admin.pandarobot.chat](https://admin.pandarobot.chat) | admin / admin123 |
### Project Repositories
| Module | GitHub Repository | Gitee Repository | GitCode Repository |
|----------|-------------------------------------------------------|------------------------------------------------------|--------------------------------------------------------|
| 🔧 Backend | [ruoyi-ai](https://github.com/ageerle/ruoyi-ai) | [ruoyi-ai](https://gitee.com/ageerle/ruoyi-ai) | [ruoyi-ai](https://gitcode.com/ageerle/ruoyi-ai) |
| 🎨 User Frontend | [ruoyi-web](https://github.com/ageerle/ruoyi-web) | [ruoyi-web](https://gitee.com/ageerle/ruoyi-web) | [ruoyi-web](https://gitcode.com/ageerle/ruoyi-web) |
| 🛠️ Admin Panel | [ruoyi-admin](https://github.com/ageerle/ruoyi-admin) | [ruoyi-admin](https://gitee.com/ageerle/ruoyi-admin) | [ruoyi-admin](https://gitcode.com/ageerle/ruoyi-admin) |
### Partner Projects
| Project Name | GitHub Repository | Gitee Repository
|----------------|-------------------------------------------------------|------------------------------------------------------|
| element-plus-x | [element-plus-x](https://github.com/element-plus-x/Element-Plus-X) | [element-plus-x](https://gitee.com/he-jiayue/element-plus-x) |
## 🛠️ Technical Architecture
### Core Framework
- **Backend**: Spring Boot 4.0 + Spring AI 2.0 + Langchain4j
- **Data Storage**: MySQL 8.0 + Redis + Vector Databases (Milvus/Weaviate)
- **Frontend**: Vue 3 + Vben Admin + element-plus-x
- **Security**: Sa-Token + JWT dual-layer security
## 📚 Documentation
Want to learn more about installation, deployment, configuration, and secondary development?
**👉 [Complete Documentation](https://doc.pandarobot.chat)**
## 🤝 Contributing
We warmly welcome community contributions! Whether you are a seasoned developer or just getting started, you can contribute to the project 💪
### How to Contribute
1. **Fork** the project to your account
2. **Create a branch** (`git checkout -b feature/new-feature-name`)
3. **Commit your changes** (`git commit -m 'Add new feature'`)
4. **Push to the branch** (`git push origin feature/new-feature-name`)
5. **Create a Pull Request**
> 💡 **Tip**: We recommend submitting PRs to GitHub, we will automatically sync to other code hosting platforms
## 📄 License
This project is licensed under the **MIT License**. See the [LICENSE](LICENSE) file for details.
## 🙏 Acknowledgments
Thanks to the following excellent open-source projects for their support:
- [Spring AI Alibaba Copilot](https://github.com/spring-ai-alibaba/copilot) - Intelligent coding assistant based on spring-ai-alibaba
- [Langchain4j](https://github.com/langchain4j/langchain4j) - Powerful Java LLM development framework
- [RuoYi-Vue-Plus](https://gitee.com/dromara/RuoYi-Vue-Plus) - Mature enterprise-level rapid development framework
- [Vben Admin](https://github.com/vbenjs/vue-vben-admin) - Modern Vue admin template
## 🌐 Ecosystem Partners
- [PPIO Cloud](https://ppinfra.com/user/register?invited_by=P8QTUY&utm_source=github_ruoyi-ai) - Provides cost-effective GPU computing and model API services
- [Youyun Intelligent Computing](https://www.compshare.cn/?ytag=GPU_YY-gh_ruoyi) - Thousands of RTX40 series GPUs + mainstream models API services, second-level response, pay-per-use, free for new customers.
## Outstanding Open-Source Projects and Community Recommendations
- [imaiwork](https://gitee.com/tsinghua-open/imaiwork) - Open-source AI phone, AI customer acquisition phone project, based on accessibility mode and RPA, more powerful than Doubao AI phone.
## 💬 Community Chat
<div align="center">
**[📱 Join Telegram Group](https://t.me/+LqooQAc5HxRmYmE1)**
</div>
---
<div align="center">
**[⭐ Star to Support](https://github.com/ageerle/ruoyi-ai)** • **[Fork to Contribute](https://github.com/ageerle/ruoyi-ai/fork)** • **[📚 中文](README.md)** • **[📖 Complete Documentation](https://doc.pandarobot.chat)**
*Built with ❤️, maintained by the RuoYi AI open-source community*
</div>
<!-- Badge Links -->
[contributors-shield]: https://img.shields.io/github/contributors/ageerle/ruoyi-ai.svg?style=flat-square
[contributors-url]: https://github.com/ageerle/ruoyi-ai/graphs/contributors
[forks-shield]: https://img.shields.io/github/forks/ageerle/ruoyi-ai.svg?style=flat-square
[forks-url]: https://github.com/ageerle/ruoyi-ai/network/members
[stars-shield]: https://img.shields.io/github/stars/ageerle/ruoyi-ai.svg?style=flat-square
[stars-url]: https://github.com/ageerle/ruoyi-ai/stargazers
[issues-shield]: https://img.shields.io/github/issues/ageerle/ruoyi-ai.svg?style=flat-square
[issues-url]: https://github.com/ageerle/ruoyi-ai/issues
[license-shield]: https://img.shields.io/github/license/ageerle/ruoyi-ai.svg?style=flat-square
[license-url]: https://github.com/ageerle/ruoyi-ai/blob/main/LICENSE

180
docker-compose-all.yaml Normal file
View File

@@ -0,0 +1,180 @@
# RuoYi-AI 一键启动全部服务
# 使用方式: docker-compose up -d
#
# 包含服务:
# - MySQL 8.0 (数据库包含初始化SQL)
# - Redis 6.2 (缓存)
# - Weaviate (向量数据库)
# - MinIO (对象存储)
# - RuoYi-Backend (后端服务)
# - RuoYi-Admin (管理端前端)
# - RuoYi-Web (用户端前端)
#
# 镜像仓库地址: crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai
version: '3.8'
services:
# ==================== MySQL 数据库 ====================
mysql:
# 阿里云镜像地址包含初始化SQL
image: crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai/mysql:v3
container_name: ruoyi-ai-mysql
restart: always
ports:
- "23306:3306"
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: ruoyi-ai-agent
TZ: Asia/Shanghai
volumes:
- mysql-data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-proot"]
interval: 15s
timeout: 10s
retries: 10
start_period: 60s
networks:
- ruoyi-net
# ==================== Redis 缓存 ====================
redis:
image: crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai/redis:6.2
container_name: ruoyi-ai-redis
restart: always
ports:
- "26379:6379"
volumes:
- redis-data:/data
command: redis-server --appendonly yes
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
- ruoyi-net
# ==================== Weaviate 向量数据库 ====================
weaviate:
image: crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai/weaviate:1.30.0
container_name: ruoyi-ai-weaviate
restart: always
ports:
- "28080:8080"
environment:
QUERY_DEFAULTS_LIMIT: 25
AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: true
PERSISTENCE_DATA_PATH: /var/lib/weaviate
DEFAULT_VECTORIZER_MODULE: none
ENABLE_MODULES: text2vec-cohere,text2vec-huggingface,text2vec-palm,text2vec-openai,generative-openai,generative-cohere,generative-palm,ref2vec-centroid,reranker-cohere,qna-openai
CLUSTER_HOSTNAME: node1
volumes:
- weaviate-data:/var/lib/weaviate
networks:
- ruoyi-net
# ==================== MinIO 对象存储 ====================
minio:
image: crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai/minio:latest
container_name: ruoyi-ai-minio
restart: always
ports:
- "29000:9000"
- "29090:9090"
environment:
MINIO_ROOT_USER: ruoyi
MINIO_ROOT_PASSWORD: ruoyi123
volumes:
- minio-data:/data
command: server /data --console-address ":9090"
networks:
- ruoyi-net
# ==================== RuoYi-AI 后端服务 ====================
backend:
image: crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai/ruoyi-ai-backend:latest
container_name: ruoyi-ai-backend
restart: always
ports:
- "26039:6039"
environment:
TZ: Asia/Shanghai
# MySQL 配置
SPRING_DATASOURCE_DYNAMIC_PRIMARY: master
SPRING_DATASOURCE_DYNAMIC_DATASOURCE_MASTER_DRIVERCLASSNAME: com.mysql.cj.jdbc.Driver
SPRING_DATASOURCE_DYNAMIC_DATASOURCE_MASTER_URL: jdbc:mysql://mysql:3306/ruoyi-ai-agent?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
SPRING_DATASOURCE_DYNAMIC_DATASOURCE_MASTER_USERNAME: root
SPRING_DATASOURCE_DYNAMIC_DATASOURCE_MASTER_PASSWORD: root
# Redis 配置
SPRING_DATA_REDIS_HOST: redis
SPRING_DATA_REDIS_PORT: 6379
SPRING_DATA_REDIS_DATABASE: 0
# 日志配置
LOGGING_LEVEL_ORG_RUOYI: info
LOGGING_LEVEL_ORG_SPRINGFRAMEWORK: warn
SYS_UPLOAD_PATH: /ruoyi/upload
volumes:
- logs-data:/ruoyi/server/logs
- upload-data:/ruoyi/upload
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_started
networks:
- ruoyi-net
# ==================== RuoYi-AI 管理端前端 ====================
admin-frontend:
image: crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai/ruoyi-ai-admin:latest
container_name: ruoyi-ai-admin
restart: always
ports:
- "25666:5666"
environment:
# 后端 API 地址 - 运行时动态配置(无需重新构建镜像)
# nginx upstream 配置不需要 http:// 前缀,直接使用 host:port
UPSTREAM_HOST: backend:6039
# 资源限制 - 防止 CPU 和内存耗尽
deploy:
resources:
limits:
cpus: '2'
memory: 3G
reservations:
cpus: '1'
memory: 1G
depends_on:
- backend
networks:
- ruoyi-net
# ==================== RuoYi-AI 用户端前端 ====================
web-frontend:
image: crpi-31mraxd99y2gqdgr.cn-beijing.personal.cr.aliyuncs.com/ruoyi_ai/ruoyi-ai-web:latest
container_name: ruoyi-ai-web
restart: always
ports:
- "25137:5137"
environment:
UPSTREAM_URL: http://backend:6039
depends_on:
- backend
networks:
- ruoyi-net
# ==================== 网络配置 ====================
networks:
ruoyi-net:
driver: bridge
# ==================== 数据卷配置 ====================
volumes:
mysql-data:
redis-data:
weaviate-data:
minio-data:
logs-data:
upload-data:

144
docker-compose.yaml Normal file
View File

@@ -0,0 +1,144 @@
# RuoYi-AI 一键启动后端服务
# 使用方式: docker-compose up -d --build
#
# 包含服务:
# - MySQL 8.0 (数据库)
# - Redis 6.2 (缓存)
# - Weaviate (向量数据库)
# - MinIO (对象存储)
# - RuoYi-Backend (后端服务,源码编译)
services:
# MySQL 数据库
mysql:
image: mysql:8.0.33
container_name: ruoyi-ai-mysql
restart: always
ports:
- "23306:3306"
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: ruoyi-ai-agent
TZ: Asia/Shanghai
volumes:
- ./docs/script/docker/mysql/init/init-db.sh:/docker-entrypoint-initdb.d/init-db.sh:ro
- ./docs/script/sql/ruoyi-ai-v3_mysql8.sql:/docker-entrypoint-initdb.d/ruoyi-ai-v3_mysql8.sql:ro
- mysql-data:/var/lib/mysql
command:
--default-authentication-plugin=mysql_native_password
--character-set-server=utf8mb4
--collation-server=utf8mb4_general_ci
--explicit_defaults_for_timestamp=true
--lower_case_table_names=1
--skip-ssl
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-proot"]
interval: 15s
timeout: 10s
retries: 10
start_period: 60s
networks:
- ruoyi-net
# Redis 缓存
redis:
image: redis:6.2
container_name: ruoyi-ai-redis
restart: always
ports:
- "6379:6379"
volumes:
- redis-data:/data
command: redis-server --appendonly yes
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
- ruoyi-net
# Weaviate 向量数据库
weaviate:
image: semitechnologies/weaviate:1.30.0
container_name: ruoyi-ai-weaviate
restart: always
ports:
- "28080:8080"
environment:
QUERY_DEFAULTS_LIMIT: 25
AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: true
PERSISTENCE_DATA_PATH: /var/lib/weaviate
DEFAULT_VECTORIZER_MODULE: none
ENABLE_MODULES: text2vec-cohere,text2vec-huggingface,text2vec-palm,text2vec-openai,generative-openai,generative-cohere,generative-palm,ref2vec-centroid,reranker-cohere,qna-openai
CLUSTER_HOSTNAME: node1
volumes:
- weaviate-data:/var/lib/weaviate
networks:
- ruoyi-net
# MinIO 对象存储
minio:
image: minio/minio
container_name: ruoyi-ai-minio
restart: always
ports:
- "9000:9000"
- "9090:9090"
environment:
MINIO_ROOT_USER: ruoyi
MINIO_ROOT_PASSWORD: ruoyi123
volumes:
- minio-data:/data
command: server /data --console-address ":9090"
networks:
- ruoyi-net
# RuoYi-AI 后端服务 (源码编译)
backend:
build:
context: .
dockerfile: Dockerfile.backend
container_name: ruoyi-ai-backend
restart: always
ports:
- "26039:6039"
environment:
TZ: Asia/Shanghai
# MySQL 配置
SPRING_DATASOURCE_DYNAMIC_PRIMARY: master
SPRING_DATASOURCE_DYNAMIC_DATASOURCE_MASTER_DRIVERCLASSNAME: com.mysql.cj.jdbc.Driver
SPRING_DATASOURCE_DYNAMIC_DATASOURCE_MASTER_URL: jdbc:mysql://mysql:3306/ruoyi-ai-agent?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
SPRING_DATASOURCE_DYNAMIC_DATASOURCE_MASTER_USERNAME: root
SPRING_DATASOURCE_DYNAMIC_DATASOURCE_MASTER_PASSWORD: root
# Redis 配置
SPRING_DATA_REDIS_HOST: redis
SPRING_DATA_REDIS_PORT: 6379
SPRING_DATA_REDIS_DATABASE: 0
# 日志配置
LOGGING_LEVEL_ORG_RUOYI: info
LOGGING_LEVEL_ORG_SPRINGFRAMEWORK: warn
SYS_UPLOAD_PATH: /ruoyi/upload # 新增:对应 sys.upload.path
volumes:
- logs-data:/ruoyi/server/logs
- upload-data:/ruoyi/upload
depends_on:
mysql:
condition: service_healthy
redis:
condition: service_started
networks:
- ruoyi-net
networks:
ruoyi-net:
driver: bridge
# 数据卷 支持手动指定 空为默认值
volumes:
mysql-data:
redis-data:
weaviate-data:
minio-data:
logs-data:
upload-data:

View File

@@ -0,0 +1,28 @@
---
version: '3.8'
services:
minio:
image: minio/minio
container_name: minio
ports:
- "9000:9000"
- "9090:9090"
environment:
- MINIO_ACCESS_KEY=ruoyi
- MINIO_SECRET_KEY=ruoyi123
volumes:
- minio_data:/data
- minio_config:/root/.minio
command: server /data --console-address ":9090"
restart: always
networks:
- minio-net
networks:
minio-net:
driver: bridge
volumes:
minio_data:
minio_config:

View File

@@ -0,0 +1,65 @@
version: '3.8'
services:
neo4j:
image: neo4j:5.15.0
container_name: ruoyi-neo4j
restart: unless-stopped
ports:
# HTTP端口
- "7474:7474"
# HTTPS端口
- "7473:7473"
# Bolt端口
- "7687:7687"
environment:
# 初始密码设置(首次启动后需要修改)
- NEO4J_AUTH=neo4j/your_password
# 接受许可协议
- NEO4J_ACCEPT_LICENSE_AGREEMENT=yes
# 内存配置(根据服务器配置调整)
- NEO4J_dbms_memory_heap_initial__size=512m
- NEO4J_dbms_memory_heap_max__size=2g
- NEO4J_dbms_memory_pagecache_size=1g
# 事务日志配置
- NEO4J_dbms_tx__log_rotation_retention__policy=3 days
# 允许从任何主机连接
- NEO4J_dbms_default__listen__address=0.0.0.0
# 启用APOC插件
- NEO4J_dbms_security_procedures_unrestricted=apoc.*
- NEO4J_dbms_security_procedures_allowlist=apoc.*
# 日志级别
- NEO4J_dbms_logs_debug_level=INFO
volumes:
# 数据持久化
- neo4j_data:/data
# 日志持久化
- neo4j_logs:/logs
# 导入目录
- neo4j_import:/var/lib/neo4j/import
# 插件目录
- neo4j_plugins:/plugins
networks:
- ruoyi-network
healthcheck:
test: [ "CMD-SHELL", "wget --no-verbose --tries=1 --spider localhost:7474 || exit 1" ]
interval: 30s
timeout: 10s
retries: 5
start_period: 40s
volumes:
neo4j_data:
name: ruoyi-neo4j-data
neo4j_logs:
name: ruoyi-neo4j-logs
neo4j_import:
name: ruoyi-neo4j-import
neo4j_plugins:
name: ruoyi-neo4j-plugins
networks:
ruoyi-network:
name: ruoyi-network
driver: bridge

View File

@@ -1,9 +1,9 @@
version: '3.5'
services:
etcd:
container_name: milvus-etcd
image: quay.io/coreos/etcd:v3.5.5
image: quay.io/coreos/etcd:v3.5.18
environment:
- ETCD_AUTO_COMPACTION_MODE=revision
- ETCD_AUTO_COMPACTION_RETENTION=1000
@@ -11,13 +11,13 @@ services:
- ETCD_SNAPSHOT_COUNT=50000
volumes:
- ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/etcd:/etcd
command: etcd -advertise-client-urls=http://127.0.0.1:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd
command: etcd -advertise-client-urls=http://etcd:2379 -listen-client-urls http://0.0.0.0:2379 --data-dir /etcd
healthcheck:
test: ["CMD", "etcdctl", "endpoint", "health"]
interval: 30s
timeout: 20s
retries: 3
minio:
container_name: milvus-minio
image: minio/minio:RELEASE.2023-03-20T20-16-18Z
@@ -35,11 +35,13 @@ services:
interval: 30s
timeout: 20s
retries: 3
standalone:
container_name: milvus-standalone
image: milvusdb/milvus:v2.3.1
image: milvusdb/milvus:v2.5.7
command: ["milvus", "run", "standalone"]
security_opt:
- seccomp:unconfined
environment:
ETCD_ENDPOINTS: etcd:2379
MINIO_ADDRESS: minio:9000
@@ -57,7 +59,17 @@ services:
depends_on:
- "etcd"
- "minio"
attu:
container_name: attu
image: zilliz/attu:v2.5.7
environment:
MILVUS_URL: milvus-standalone:19530
ports:
- "19500:3000" # 外部端口19500可以自定义
depends_on:
- "standalone"
networks:
default:
name: milvus
name: milvus

View File

@@ -1,5 +1,4 @@
---
version: '3.4'
services:
weaviate:
command:
@@ -9,19 +8,17 @@ services:
- '6038'
- --scheme
- http
image: cr.weaviate.io/semitechnologies/weaviate:1.19.7
image: semitechnologies/weaviate:1.19.7
ports:
- 6038:6038
- 50051:50051
volumes:
- weaviate_data:/var/lib/weaviate
restart: on-failure:0
environment:
QUERY_DEFAULTS_LIMIT: 25
AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'true'
PERSISTENCE_DATA_PATH: '/var/lib/weaviate'
DEFAULT_VECTORIZER_MODULE: 'none'
ENABLE_MODULES: 'text2vec-cohere,text2vec-huggingface,text2vec-palm,text2vec-openai,generative-openai,generative-cohere,generative-palm,ref2vec-centroid,reranker-cohere,qna-openai'
CLUSTER_HOSTNAME: 'node1'
volumes:
weaviate_data:

BIN
docs/image/bibi.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
docs/image/dy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

BIN
docs/image/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

BIN
docs/image/qq.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

BIN
docs/image/wx.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

View File

@@ -0,0 +1,10 @@
#!/bin/bash
# 数据库初始化脚本
# 使用 --force 参数确保即使出错也继续执行
echo "开始初始化数据库..."
# 使用 --force 参数忽略错误继续执行
mysql -uroot -proot ruoyi-ai-agent --force < /docker-entrypoint-initdb.d/ruoyi-ai-v3_mysql8.sql
echo "数据库初始化完成"

View File

@@ -0,0 +1,129 @@
{
"nodeList": [
{
"nodeType": "0",
"nodeCode": "d5ee3ddf-3968-4379-a86f-9ceabde5faac",
"nodeName": "开始",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "200,200|200,200",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "d5ee3ddf-3968-4379-a86f-9ceabde5faac",
"nextNodeCode": "dd515cdd-59f6-446f-94ca-25ca062afb42",
"coordinate": "220,200;310,200"
}
]
},
{
"nodeType": "1",
"nodeCode": "dd515cdd-59f6-446f-94ca-25ca062afb42",
"nodeName": "申请人",
"permissionFlag": "",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,copy\"}]",
"coordinate": "360,200|360,200",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "dd515cdd-59f6-446f-94ca-25ca062afb42",
"nextNodeCode": "78fa8e5b-e809-44ed-978a-41092409ebcf",
"coordinate": "410,200;490,200"
}
]
},
{
"nodeType": "1",
"nodeCode": "78fa8e5b-e809-44ed-978a-41092409ebcf",
"nodeName": "组长",
"permissionFlag": "role:1",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,copy,transfer,trust,file\"}]",
"coordinate": "540,200|540,200",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "78fa8e5b-e809-44ed-978a-41092409ebcf",
"nextNodeCode": "a8abf15f-b83e-428a-86cc-033555ea9bbe",
"coordinate": "590,200;670,200"
}
]
},
{
"nodeType": "1",
"nodeCode": "a8abf15f-b83e-428a-86cc-033555ea9bbe",
"nodeName": "部门主管",
"permissionFlag": "role:3@@role:4",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,copy,transfer,trust,file\"}]",
"coordinate": "720,200|720,200",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "a8abf15f-b83e-428a-86cc-033555ea9bbe",
"nextNodeCode": "8b82b7d7-8660-455e-b880-d6d22ea3eb6d",
"coordinate": "770,200;880,200"
}
]
},
{
"nodeType": "2",
"nodeCode": "8b82b7d7-8660-455e-b880-d6d22ea3eb6d",
"nodeName": "结束",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "900,200|900,200",
"version": "1",
"skipList": []
}
],
"flowCode": "leave1",
"flowName": "请假申请-普通",
"modelValue": "CLASSICS",
"category": "103",
"version": "1",
"formCustom": "N",
"formPath": "/workflow/leaveEdit/index",
"listenerType": null,
"listenerPath": null
}

View File

@@ -0,0 +1,187 @@
{
"nodeList": [
{
"nodeType": "0",
"nodeCode": "cef3895c-f7d8-4598-8bf3-8ec2ef6ce84a",
"nodeName": "开始",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "300,240|300,240",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "cef3895c-f7d8-4598-8bf3-8ec2ef6ce84a",
"nextNodeCode": "fdcae93b-b69c-498a-b231-09255e74bcbd",
"coordinate": "320,240;390,240"
}
]
},
{
"nodeType": "1",
"nodeCode": "fdcae93b-b69c-498a-b231-09255e74bcbd",
"nodeName": "申请人",
"permissionFlag": "",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file\"}]",
"coordinate": "440,240|440,240",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "fdcae93b-b69c-498a-b231-09255e74bcbd",
"nextNodeCode": "7b8c7ead-7dc8-4951-a7f3-f0c41995909e",
"coordinate": "490,240;535,240"
}
]
},
{
"nodeType": "3",
"nodeCode": "7b8c7ead-7dc8-4951-a7f3-f0c41995909e",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "560,240",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": "le@@leaveDays|2",
"skipName": null,
"nowNodeCode": "7b8c7ead-7dc8-4951-a7f3-f0c41995909e",
"nextNodeCode": "b3528155-dcb7-4445-bbdf-3d00e3499e86",
"coordinate": "560,265;560,320;670,320"
},
{
"skipType": "PASS",
"skipCondition": "gt@@leaveDays|2",
"skipName": "大于两天",
"nowNodeCode": "7b8c7ead-7dc8-4951-a7f3-f0c41995909e",
"nextNodeCode": "5ed2362b-fc0c-4d52-831f-95208b830605",
"coordinate": "560,215;560,160;670,160|560,187"
}
]
},
{
"nodeType": "1",
"nodeCode": "b3528155-dcb7-4445-bbdf-3d00e3499e86",
"nodeName": "组长",
"permissionFlag": "3@@4",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,transfer,trust,copy\"}]",
"coordinate": "720,320|720,320",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "b3528155-dcb7-4445-bbdf-3d00e3499e86",
"nextNodeCode": "c9fa6d7d-2a74-4e78-b947-0cad8a6af869",
"coordinate": "770,320;860,320;860,280"
}
]
},
{
"nodeType": "1",
"nodeCode": "c9fa6d7d-2a74-4e78-b947-0cad8a6af869",
"nodeName": "总经理",
"permissionFlag": "role:1",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,transfer,trust,copy\"}]",
"coordinate": "860,240|860,240",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "c9fa6d7d-2a74-4e78-b947-0cad8a6af869",
"nextNodeCode": "40aa65fd-0712-4d23-b6f7-d0432b920fd1",
"coordinate": "910,240;980,240"
}
]
},
{
"nodeType": "2",
"nodeCode": "40aa65fd-0712-4d23-b6f7-d0432b920fd1",
"nodeName": "结束",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "1000,240|1000,240",
"version": "1",
"skipList": []
},
{
"nodeType": "1",
"nodeCode": "5ed2362b-fc0c-4d52-831f-95208b830605",
"nodeName": "部门领导",
"permissionFlag": "role:1",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,transfer,trust,copy\"}]",
"coordinate": "720,160|720,160",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "5ed2362b-fc0c-4d52-831f-95208b830605",
"nextNodeCode": "c9fa6d7d-2a74-4e78-b947-0cad8a6af869",
"nextNodeType": "1",
"coordinate": "770,160;860,160;860,200"
}
]
}
],
"flowCode": "leave2",
"flowName": "请假申请-排他网关",
"modelValue": "CLASSICS",
"category": "103",
"version": "1",
"formCustom": "N",
"formPath": "/workflow/leaveEdit/index",
"listenerType": null,
"listenerPath": null
}

View File

@@ -0,0 +1,211 @@
{
"nodeList": [
{
"nodeType": "0",
"nodeCode": "a80ecf9f-f465-4ae5-a429-e30ec5d0f957",
"nodeName": "开始",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "380,220|380,220",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "a80ecf9f-f465-4ae5-a429-e30ec5d0f957",
"nextNodeCode": "b7bbb571-06de-455c-8083-f83c07bf0b99",
"coordinate": "400,220;470,220"
}
]
},
{
"nodeType": "1",
"nodeCode": "b7bbb571-06de-455c-8083-f83c07bf0b99",
"nodeName": "申请人",
"permissionFlag": "",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file\"}]",
"coordinate": "520,220|520,220",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "b7bbb571-06de-455c-8083-f83c07bf0b99",
"nextNodeCode": "84d7ed24-bb44-4ba1-bf1f-e6f5092d3f0a",
"coordinate": "570,220;655,220"
}
]
},
{
"nodeType": "4",
"nodeCode": "84d7ed24-bb44-4ba1-bf1f-e6f5092d3f0a",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "680,220",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "84d7ed24-bb44-4ba1-bf1f-e6f5092d3f0a",
"nextNodeCode": "4b7743cd-940c-431b-926f-e7b614fbf1fe",
"coordinate": "680,195;680,140;750,140"
},
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "84d7ed24-bb44-4ba1-bf1f-e6f5092d3f0a",
"nextNodeCode": "762cb975-37d8-4276-b6db-79a4c3606394",
"coordinate": "680,245;680,300;750,300"
}
]
},
{
"nodeType": "1",
"nodeCode": "4b7743cd-940c-431b-926f-e7b614fbf1fe",
"nodeName": "市场部",
"permissionFlag": "role:1",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,transfer,trust,copy\"}]",
"coordinate": "800,140|800,140",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "4b7743cd-940c-431b-926f-e7b614fbf1fe",
"nextNodeCode": "b66b6563-f9fe-41cc-a782-f7837bb6f3d2",
"coordinate": "850,140;920,140;920,195"
}
]
},
{
"nodeType": "4",
"nodeCode": "b66b6563-f9fe-41cc-a782-f7837bb6f3d2",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "920,220",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "b66b6563-f9fe-41cc-a782-f7837bb6f3d2",
"nextNodeCode": "23e7429e-2b47-4431-b93e-40db7c431ce6",
"coordinate": "945,220;975,220;975,220;960,220;960,220;990,220"
}
]
},
{
"nodeType": "1",
"nodeCode": "23e7429e-2b47-4431-b93e-40db7c431ce6",
"nodeName": "CEO",
"permissionFlag": "1",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,transfer,trust,copy\"}]",
"coordinate": "1040,220|1040,220",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "23e7429e-2b47-4431-b93e-40db7c431ce6",
"nextNodeCode": "f5ace37f-5a5e-4e64-a6f6-913ab9a71cd1",
"coordinate": "1090,220;1140,220"
}
]
},
{
"nodeType": "2",
"nodeCode": "f5ace37f-5a5e-4e64-a6f6-913ab9a71cd1",
"nodeName": "结束",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "1160,220|1160,220",
"version": "1",
"skipList": []
},
{
"nodeType": "1",
"nodeCode": "762cb975-37d8-4276-b6db-79a4c3606394",
"nodeName": "综合部",
"permissionFlag": "role:3@@role:4",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,transfer,trust,copy\"}]",
"coordinate": "800,300|800,300",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "762cb975-37d8-4276-b6db-79a4c3606394",
"nextNodeCode": "b66b6563-f9fe-41cc-a782-f7837bb6f3d2",
"nextNodeType": "4",
"coordinate": "850,300;920,300;920,245"
}
]
}
],
"flowCode": "leave3",
"flowName": "请假申请-并行网关",
"modelValue": "CLASSICS",
"category": "103",
"version": "1",
"formCustom": "N",
"formPath": "/workflow/leaveEdit/index",
"listenerType": null,
"listenerPath": null
}

View File

@@ -0,0 +1,154 @@
{
"nodeList": [
{
"nodeType": "0",
"nodeCode": "9ce8bf00-f25b-4fc6-91b8-827082fc4876",
"nodeName": "开始",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "320,240|320,240",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "9ce8bf00-f25b-4fc6-91b8-827082fc4876",
"nextNodeCode": "e90b98ef-35b4-410c-a663-bae8b7624b9f",
"coordinate": "340,240;410,240"
}
]
},
{
"nodeType": "1",
"nodeCode": "e90b98ef-35b4-410c-a663-bae8b7624b9f",
"nodeName": "申请人",
"permissionFlag": "",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file\"}]",
"coordinate": "460,240|460,240",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "e90b98ef-35b4-410c-a663-bae8b7624b9f",
"nextNodeCode": "768b5b1a-6726-4d67-8853-4cc70d5b1045",
"coordinate": "510,240;590,240"
}
]
},
{
"nodeType": "1",
"nodeCode": "768b5b1a-6726-4d67-8853-4cc70d5b1045",
"nodeName": "百分之60通过",
"permissionFlag": "${userList}",
"nodeRatio": "60.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,addSign,subSign\"}]",
"coordinate": "640,240|640,240",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "768b5b1a-6726-4d67-8853-4cc70d5b1045",
"nextNodeCode": "2f9f2e21-9bcf-42a3-a07c-13037aad22d1",
"coordinate": "690,240;770,240"
}
]
},
{
"nodeType": "1",
"nodeCode": "2f9f2e21-9bcf-42a3-a07c-13037aad22d1",
"nodeName": "全部审批通过",
"permissionFlag": "role:1@@role:3",
"nodeRatio": "100.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,addSign,subSign\"}]",
"coordinate": "820,240|820,240",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "2f9f2e21-9bcf-42a3-a07c-13037aad22d1",
"nextNodeCode": "27461e01-3d9f-4530-8fe3-bd5ec7f9571f",
"coordinate": "870,240;950,240"
}
]
},
{
"nodeType": "1",
"nodeCode": "27461e01-3d9f-4530-8fe3-bd5ec7f9571f",
"nodeName": "CEO",
"permissionFlag": "1",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,transfer,trust,copy\"}]",
"coordinate": "1000,240|1000,240",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "27461e01-3d9f-4530-8fe3-bd5ec7f9571f",
"nextNodeCode": "b62b88c3-8d8d-4969-911e-2aaea219e7fc",
"coordinate": "1050,240;1080,240;1080,240;1070,240;1070,240;1100,240"
}
]
},
{
"nodeType": "2",
"nodeCode": "b62b88c3-8d8d-4969-911e-2aaea219e7fc",
"nodeName": "结束",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "1120,240|1120,240",
"version": "1",
"skipList": []
}
],
"flowCode": "leave4",
"flowName": "请假申请-会签",
"modelValue": "CLASSICS",
"category": "103",
"version": "1",
"formCustom": "N",
"formPath": "/workflow/leaveEdit/index",
"listenerType": null,
"listenerPath": null
}

View File

@@ -0,0 +1,211 @@
{
"nodeList": [
{
"nodeType": "0",
"nodeCode": "ebebaf26-9cb6-497e-8119-4c9fed4c597c",
"nodeName": "开始",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "300,220|300,220",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "ebebaf26-9cb6-497e-8119-4c9fed4c597c",
"nextNodeCode": "e1b04e96-dc81-4858-a309-2fe945d2f374",
"coordinate": "320,220;350,220;350,220;340,220;340,220;370,220"
}
]
},
{
"nodeType": "1",
"nodeCode": "e1b04e96-dc81-4858-a309-2fe945d2f374",
"nodeName": "申请人",
"permissionFlag": "",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file\"}]",
"coordinate": "420,220|420,220",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "e1b04e96-dc81-4858-a309-2fe945d2f374",
"nextNodeCode": "3e743f4f-51ca-41d4-8e94-21f5dd9b59c9",
"coordinate": "470,220;535,220"
}
]
},
{
"nodeType": "4",
"nodeCode": "3e743f4f-51ca-41d4-8e94-21f5dd9b59c9",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "560,220",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "3e743f4f-51ca-41d4-8e94-21f5dd9b59c9",
"nextNodeCode": "c80f273e-1f17-4bd8-9ad1-04a4a94ea862",
"coordinate": "560,245;560,320;650,320"
},
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "3e743f4f-51ca-41d4-8e94-21f5dd9b59c9",
"nextNodeCode": "1e3e8d3b-18ae-4d6c-a814-ce0d724adfa4",
"coordinate": "560,195;560,120;650,120"
}
]
},
{
"nodeType": "1",
"nodeCode": "c80f273e-1f17-4bd8-9ad1-04a4a94ea862",
"nodeName": "会签",
"permissionFlag": "role:1@@role:3",
"nodeRatio": "100.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,addSign,subSign\"}]",
"coordinate": "700,320|700,320",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "c80f273e-1f17-4bd8-9ad1-04a4a94ea862",
"nextNodeCode": "1a20169e-3d82-4926-a151-e2daad28de1b",
"coordinate": "750,320;860,320;860,245"
}
]
},
{
"nodeType": "4",
"nodeCode": "1a20169e-3d82-4926-a151-e2daad28de1b",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "860,220",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "1a20169e-3d82-4926-a151-e2daad28de1b",
"nextNodeCode": "7a8f0473-e409-442e-a843-5c2b813d00e9",
"coordinate": "885,220;950,220"
}
]
},
{
"nodeType": "1",
"nodeCode": "7a8f0473-e409-442e-a843-5c2b813d00e9",
"nodeName": "CEO",
"permissionFlag": "1",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,transfer,trust,copy\"}]",
"coordinate": "1000,220|1000,220",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "7a8f0473-e409-442e-a843-5c2b813d00e9",
"nextNodeCode": "03c4d2bc-58b5-4408-a2e4-65afb046f169",
"coordinate": "1050,220;1120,220"
}
]
},
{
"nodeType": "2",
"nodeCode": "03c4d2bc-58b5-4408-a2e4-65afb046f169",
"nodeName": "结束",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "1140,220|1140,220",
"version": "1",
"skipList": []
},
{
"nodeType": "1",
"nodeCode": "1e3e8d3b-18ae-4d6c-a814-ce0d724adfa4",
"nodeName": "百分之60票签",
"permissionFlag": "${userList}",
"nodeRatio": "60.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file,addSign,subSign\"}]",
"coordinate": "700,120|700,120",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "1e3e8d3b-18ae-4d6c-a814-ce0d724adfa4",
"nextNodeCode": "1a20169e-3d82-4926-a151-e2daad28de1b",
"nextNodeType": "4",
"coordinate": "750,120;860,120;860,195"
}
]
}
],
"flowCode": "leave5",
"flowName": "请假申请-并行会签网关",
"modelValue": "CLASSICS",
"category": "103",
"version": "1",
"formCustom": "N",
"formPath": "/workflow/leaveEdit/index",
"listenerType": null,
"listenerPath": null
}

View File

@@ -0,0 +1,368 @@
{
"nodeList": [
{
"nodeType": "0",
"nodeCode": "122b89a5-7c6f-40a3-aa09-7a263f902054",
"nodeName": "开始",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "240,300|240,300",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "122b89a5-7c6f-40a3-aa09-7a263f902054",
"nextNodeCode": "c25a0e86-fdd1-4f03-8e22-14db70389dbd",
"coordinate": "260,300;350,300"
}
]
},
{
"nodeType": "1",
"nodeCode": "c25a0e86-fdd1-4f03-8e22-14db70389dbd",
"nodeName": "申请人",
"permissionFlag": "",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,file\"}]",
"coordinate": "400,300|400,300",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "c25a0e86-fdd1-4f03-8e22-14db70389dbd",
"nextNodeCode": "07ecda1d-7a0a-47b5-8a91-6186c9473742",
"coordinate": "450,300;510,300"
}
]
},
{
"nodeType": "1",
"nodeCode": "2bfa3919-78cf-4bc1-b59b-df463a4546f9",
"nodeName": "副经理",
"permissionFlag": "role:1@@role:3@@role:4",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination\"}]",
"coordinate": "860,200|860,200",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "2bfa3919-78cf-4bc1-b59b-df463a4546f9",
"nextNodeCode": "394e1cc8-b8b2-4189-9f81-44448e88ac32",
"coordinate": "910,200;1000,200;1000,275"
}
]
},
{
"nodeType": "1",
"nodeCode": "ec17f60e-94e0-4d96-a3ce-3417e9d32d60",
"nodeName": "组长",
"permissionFlag": "1",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination\"}]",
"coordinate": "860,400|860,400",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "ec17f60e-94e0-4d96-a3ce-3417e9d32d60",
"nextNodeCode": "394e1cc8-b8b2-4189-9f81-44448e88ac32",
"coordinate": "910,400;1000,400;1000,325"
}
]
},
{
"nodeType": "1",
"nodeCode": "07ecda1d-7a0a-47b5-8a91-6186c9473742",
"nodeName": "副组长",
"permissionFlag": "1",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,transfer,copy,pop\"}]",
"coordinate": "560,300|560,300",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "07ecda1d-7a0a-47b5-8a91-6186c9473742",
"nextNodeCode": "48117e2c-6328-406b-b102-c4a9d115bb13",
"coordinate": "610,300;675,300"
}
]
},
{
"nodeType": "3",
"nodeCode": "48117e2c-6328-406b-b102-c4a9d115bb13",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "700,300",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": "default@@${leaveDays > 2}",
"skipName": "大于两天",
"nowNodeCode": "48117e2c-6328-406b-b102-c4a9d115bb13",
"nextNodeCode": "2bfa3919-78cf-4bc1-b59b-df463a4546f9",
"nextNodeType": "1",
"coordinate": "700,275;700,200;810,200|700,237"
},
{
"skipType": "PASS",
"skipCondition": "spel@@#{@testLeaveServiceImpl.eval(#leaveDays)}",
"skipName": null,
"nowNodeCode": "48117e2c-6328-406b-b102-c4a9d115bb13",
"nextNodeCode": "ec17f60e-94e0-4d96-a3ce-3417e9d32d60",
"nextNodeType": "1",
"coordinate": "700,325;700,400;810,400"
}
]
},
{
"nodeType": "3",
"nodeCode": "394e1cc8-b8b2-4189-9f81-44448e88ac32",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "1000,300",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "394e1cc8-b8b2-4189-9f81-44448e88ac32",
"nextNodeCode": "9c93a195-cff2-4e17-ab0a-a4f264191496",
"coordinate": "1025,300;1130,300"
}
]
},
{
"nodeType": "1",
"nodeCode": "9c93a195-cff2-4e17-ab0a-a4f264191496",
"nodeName": "经理会签",
"permissionFlag": "1@@3",
"nodeRatio": "100.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination,pop,addSign,subSign\"}]",
"coordinate": "1180,300|1180,300",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "9c93a195-cff2-4e17-ab0a-a4f264191496",
"nextNodeCode": "a1a42056-afd1-4e90-88bc-36cbf5a66992",
"coordinate": "1230,300;1315,300"
}
]
},
{
"nodeType": "4",
"nodeCode": "a1a42056-afd1-4e90-88bc-36cbf5a66992",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "1340,300",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "a1a42056-afd1-4e90-88bc-36cbf5a66992",
"nextNodeCode": "fcfdd9f6-f526-4c1a-b71d-88afa31aebc5",
"coordinate": "1340,325;1340,400;1430,400"
},
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "a1a42056-afd1-4e90-88bc-36cbf5a66992",
"nextNodeCode": "350dfa0c-a77c-4efa-8527-10efa02d8be4",
"coordinate": "1340,275;1340,200;1430,200"
}
]
},
{
"nodeType": "1",
"nodeCode": "350dfa0c-a77c-4efa-8527-10efa02d8be4",
"nodeName": "总经理",
"permissionFlag": "3@@1",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination\"}]",
"coordinate": "1480,200|1480,200",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "350dfa0c-a77c-4efa-8527-10efa02d8be4",
"nextNodeCode": "c36a46ef-04f9-463f-bad7-4b395c818519",
"coordinate": "1530,200;1640,200;1640,275"
}
]
},
{
"nodeType": "1",
"nodeCode": "fcfdd9f6-f526-4c1a-b71d-88afa31aebc5",
"nodeName": "副总经理",
"permissionFlag": "1@@3",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination\"}]",
"coordinate": "1480,400|1480,400",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "fcfdd9f6-f526-4c1a-b71d-88afa31aebc5",
"nextNodeCode": "c36a46ef-04f9-463f-bad7-4b395c818519",
"coordinate": "1530,400;1640,400;1640,325"
}
]
},
{
"nodeType": "4",
"nodeCode": "c36a46ef-04f9-463f-bad7-4b395c818519",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "1640,300",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "c36a46ef-04f9-463f-bad7-4b395c818519",
"nextNodeCode": "3fcea762-b53a-4ae1-8365-7bec90444828",
"coordinate": "1665,300;1770,300"
}
]
},
{
"nodeType": "1",
"nodeCode": "3fcea762-b53a-4ae1-8365-7bec90444828",
"nodeName": "董事",
"permissionFlag": "1",
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": "",
"listenerPath": "",
"formCustom": "N",
"formPath": null,
"ext": "[{\"code\":\"ButtonPermissionEnum\",\"value\":\"back,termination\"}]",
"coordinate": "1820,300|1820,300",
"version": "1",
"skipList": [
{
"skipType": "PASS",
"skipCondition": null,
"skipName": null,
"nowNodeCode": "3fcea762-b53a-4ae1-8365-7bec90444828",
"nextNodeCode": "9cfbfd3e-6c04-41d6-9fc2-6787a7d2cd31",
"coordinate": "1870,300;1960,300"
}
]
},
{
"nodeType": "2",
"nodeCode": "9cfbfd3e-6c04-41d6-9fc2-6787a7d2cd31",
"nodeName": "结束",
"permissionFlag": null,
"nodeRatio": "0.000",
"anyNodeSkip": null,
"listenerType": null,
"listenerPath": null,
"formCustom": "N",
"formPath": null,
"ext": "[]",
"coordinate": "1980,300|1980,300",
"version": "1",
"skipList": []
}
],
"flowCode": "leave6",
"flowName": "请假申请-排他并行会签",
"modelValue": "CLASSICS",
"category": "103",
"version": "1",
"formCustom": "N",
"formPath": "/workflow/leaveEdit/index",
"listenerType": null,
"listenerPath": null
}

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 329 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 284 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 674 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

299
pom.xml
View File

@@ -9,57 +9,77 @@
<version>${revision}</version>
<name>ruoyi-ai</name>
<url>https://gitee.com/ageerle/ruoyi-ai</url>
<description>AI助手</description>
<url>>https://gitee.com/ageerle/ruoyi-ai</url>
<description>全栈式AI开发平台</description>
<properties>
<revision>1.0.0</revision>
<spring-boot.version>3.0.6</spring-boot.version>
<revision>3.0.0</revision>
<spring-boot.version>3.5.8</spring-boot.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
<spring-boot.mybatis>3.0.1</spring-boot.mybatis>
<springdoc.version>2.1.0</springdoc.version>
<mybatis.version>3.5.16</mybatis.version>
<springdoc.version>2.8.13</springdoc.version>
<therapi-javadoc.version>0.15.0</therapi-javadoc.version>
<poi.version>5.2.3</poi.version>
<easyexcel.version>3.2.1</easyexcel.version>
<fastexcel.version>1.3.0</fastexcel.version>
<velocity.version>2.3</velocity.version>
<satoken.version>1.34.0</satoken.version>
<mybatis-plus.version>3.5.3.1</mybatis-plus.version>
<satoken.version>1.44.0</satoken.version>
<mybatis-plus.version>3.5.14</mybatis-plus.version>
<p6spy.version>3.9.1</p6spy.version>
<hutool.version>5.8.18</hutool.version>
<okhttp.version>4.10.0</okhttp.version>
<spring-boot-admin.version>3.0.3</spring-boot-admin.version>
<redisson.version>3.20.1</redisson.version>
<lock4j.version>2.2.4</lock4j.version>
<dynamic-ds.version>3.6.1</dynamic-ds.version>
<alibaba-ttl.version>2.14.2</alibaba-ttl.version>
<xxl-job.version>2.4.0</xxl-job.version>
<mapstruct-plus.version>1.2.1</mapstruct-plus.version>
<hutool.version>5.8.40</hutool.version>
<spring-boot-admin.version>3.5.5</spring-boot-admin.version>
<redisson.version>3.51.0</redisson.version>
<lock4j.version>2.2.7</lock4j.version>
<dynamic-ds.version>4.3.1</dynamic-ds.version>
<snailjob.version>1.8.0</snailjob.version>
<mapstruct-plus.version>1.5.0</mapstruct-plus.version>
<mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
<lombok.version>1.18.26</lombok.version>
<bouncycastle.version>1.72</bouncycastle.version>
<lombok.version>1.18.40</lombok.version>
<bouncycastle.version>1.80</bouncycastle.version>
<justauth.version>1.16.7</justauth.version>
<!-- 离线IP地址定位库 -->
<ip2region.version>2.7.0</ip2region.version>
<!-- 临时修复 snakeyaml 漏洞 -->
<snakeyaml.version>1.33</snakeyaml.version>
<!-- OSS 配置 -->
<aws-java-sdk-s3.version>1.12.400</aws-java-sdk-s3.version>
<aws.sdk.version>2.28.22</aws.sdk.version>
<!-- SMS 配置 -->
<aliyun.sms.version>2.0.23</aliyun.sms.version>
<tencent.sms.version>3.1.687</tencent.sms.version>
<sms4j.version>3.3.5</sms4j.version>
<!-- 限制框架中的fastjson版本 -->
<fastjson.version>1.2.83</fastjson.version>
<!-- 面向运行时的D-ORM依赖 -->
<anyline.version>8.7.2-20250603</anyline.version>
<!-- 工作流配置 -->
<warm-flow.version>1.8.2</warm-flow.version>
<!-- 企业微信SDK -->
<weixin-java-cp.version>4.6.0</weixin-java-cp.version>
<!-- Jackson XML -->
<jackson-dataformat-xml.version>2.18.2</jackson-dataformat-xml.version>
<!-- AI 相关依赖 -->
<langchain4j.version>1.11.0</langchain4j.version>
<langchain4j.community.version>1.11.0-beta19</langchain4j.community.version>
<langgraph4j.version>1.5.3</langgraph4j.version>
<weaviate.version>1.19.6</weaviate.version>
<dify.version>1.0.7</dify.version>
<!-- gRPC 版本 - 解决 Milvus SDK 依赖冲突 -->
<grpc.version>1.62.2</grpc.version>
<!-- Apache Commons Compress - 用于POI处理ZIP格式 -->
<commons-compress.version>1.27.1</commons-compress.version>
<avatar-generator.version>1.1.0</avatar-generator.version>
<jsoup.version>1.21.2</jsoup.version>
<knife4j.version>4.4.0</knife4j.version>
<swagger-annotations.version>2.2.8</swagger-annotations.version>
<google-api-client.version>2.6.0</google-api-client.version>
<commons-collections4.version>4.5.0</commons-collections4.version>
<!-- 插件版本 -->
<maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
<maven-war-plugin.version>3.2.2</maven-war-plugin.version>
<maven-compiler-plugin.verison>3.11.0</maven-compiler-plugin.verison>
<maven-surefire-plugin.version>3.0.0</maven-surefire-plugin.version>
<maven-jar-plugin.version>3.4.2</maven-jar-plugin.version>
<maven-war-plugin.version>3.4.0</maven-war-plugin.version>
<maven-compiler-plugin.version>3.14.0</maven-compiler-plugin.version>
<maven-surefire-plugin.version>3.5.3</maven-surefire-plugin.version>
<flatten-maven-plugin.version>1.3.0</flatten-maven-plugin.version>
<weixin-java-miniapp.version>4.5.0</weixin-java-miniapp.version>
<weixin-java-pay.version>4.6.0</weixin-java-pay.version>
<weixin-java-cp.version>4.6.0</weixin-java-cp.version>
<!-- 打包默认跳过测试 -->
<skipTests>true</skipTests>
</properties>
<profiles>
@@ -68,7 +88,9 @@
<properties>
<!-- 环境标识,需要与配置文件的名称相对应 -->
<profiles.active>local</profiles.active>
<logging.level>debug</logging.level>
<logging.level>info</logging.level>
<monitor.username>ruoyi</monitor.username>
<monitor.password>123456</monitor.password>
</properties>
</profile>
<profile>
@@ -76,7 +98,9 @@
<properties>
<!-- 环境标识,需要与配置文件的名称相对应 -->
<profiles.active>dev</profiles.active>
<logging.level>debug</logging.level>
<logging.level>info</logging.level>
<monitor.username>ruoyi</monitor.username>
<monitor.password>123456</monitor.password>
</properties>
<activation>
<!-- 默认环境 -->
@@ -88,6 +112,8 @@
<properties>
<profiles.active>prod</profiles.active>
<logging.level>warn</logging.level>
<monitor.username>ruoyi</monitor.username>
<monitor.password>123456</monitor.password>
</properties>
</profile>
</profiles>
@@ -105,6 +131,15 @@
<scope>import</scope>
</dependency>
<!-- gRPC BOM - 解决 Milvus SDK 依赖冲突,强制统一版本 -->
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-bom</artifactId>
<version>${grpc.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- hutool 的依赖配置-->
<dependency>
<groupId>cn.hutool</groupId>
@@ -142,25 +177,9 @@
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>${easyexcel.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
</exclusion>
</exclusions>
<groupId>cn.idev.excel</groupId>
<artifactId>fastexcel</artifactId>
<version>${fastexcel.version}</version>
</dependency>
<!-- velocity代码生成使用模板 -->
@@ -197,19 +216,25 @@
<!-- dynamic-datasource 多数据源-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
<version>${dynamic-ds.version}</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${spring-boot.mybatis}</version>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-jsqlparser</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
@@ -226,35 +251,42 @@
<version>${p6spy.version}</version>
</dependency>
<!-- AWS SDK for Java 2.x -->
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>${aws-java-sdk-s3.version}</version>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
<version>${aws.sdk.version}</version>
</dependency>
<!-- 基于 AWS CRT 的 S3 客户端的性能增强的 S3 传输管理器 -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3-transfer-manager</artifactId>
<version>${aws.sdk.version}</version>
</dependency>
<!-- 将基于 Netty 的 HTTP 客户端从类路径中移除 -->
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>netty-nio-client</artifactId>
<version>${aws.sdk.version}</version>
</dependency>
<!--短信sms4j-->
<dependency>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-spring-boot-starter</artifactId>
<version>${sms4j.version}</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
<version>${aliyun.sms.version}</version>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>${spring-boot-admin.version}</version>
</dependency>
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java-sms</artifactId>
<version>${tencent.sms.version}</version>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>${spring-boot-admin.version}</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>de.codecentric</groupId>-->
<!-- <artifactId>spring-boot-admin-starter-server</artifactId>-->
<!-- <version>${spring-boot-admin.version}</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>de.codecentric</groupId>-->
<!-- <artifactId>spring-boot-admin-starter-client</artifactId>-->
<!-- <version>${spring-boot-admin.version}</version>-->
<!-- </dependency>-->
<!--redisson-->
<dependency>
<groupId>org.redisson</groupId>
@@ -268,24 +300,16 @@
<version>${lock4j.version}</version>
</dependency>
<!-- xxl-job-core -->
<!-- SnailJob Client -->
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>${xxl-job.version}</version>
<groupId>com.aizuda</groupId>
<artifactId>snail-job-client-starter</artifactId>
<version>${snailjob.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>${alibaba-ttl.version}</version>
</dependency>
<!-- 临时修复 snakeyaml 漏洞 -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>${snakeyaml.version}</version>
<groupId>com.aizuda</groupId>
<artifactId>snail-job-client-job-core</artifactId>
<version>${snailjob.version}</version>
</dependency>
<!-- 加密包引入 -->
@@ -301,6 +325,25 @@
<version>${mapstruct-plus.version}</version>
</dependency>
<!-- Warm-Flow国产工作流引擎, 在线文档http://warm-flow.cn/ -->
<dependency>
<groupId>org.dromara.warm</groupId>
<artifactId>warm-flow-mybatis-plus-sb3-starter</artifactId>
<version>${warm-flow.version}</version>
</dependency>
<dependency>
<groupId>org.dromara.warm</groupId>
<artifactId>warm-flow-plugin-ui-sb-web</artifactId>
<version>${warm-flow.version}</version>
</dependency>
<!-- JustAuth 的依赖配置-->
<dependency>
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId>
<version>${justauth.version}</version>
</dependency>
<!-- 离线IP地址定位库 ip2region -->
<dependency>
<groupId>org.lionsoul</groupId>
@@ -308,26 +351,18 @@
<version>${ip2region.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-system</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-chat</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-knowledge</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-generator</artifactId>
@@ -336,16 +371,45 @@
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-demo</artifactId>
<artifactId>ruoyi-chat</artifactId>
<version>${revision}</version>
</dependency>
<!-- 工作流模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-workflow</artifactId>
<version>${revision}</version>
</dependency>
<!-- AI流程编排模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-aiflow</artifactId>
<version>${revision}</version>
</dependency>
<!-- Jackson XML -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>${jackson-dataformat-xml.version}</version>
</dependency>
<!-- Apache Commons Compress - 用于POI处理ZIP格式解决导出Excel时的NoSuchMethodError -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>${commons-compress.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<modules>
<module>ruoyi-admin</module>
<module>ruoyi-common</module>
<module>ruoyi-extend</module>
<module>ruoyi-modules</module>
</modules>
<packaging>pom</packaging>
@@ -355,7 +419,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.verison}</version>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
@@ -398,6 +462,7 @@
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
<argLine>-Dfile.encoding=UTF-8</argLine>
<!-- 根据打包环境执行对应的@Tag测试方法 -->
<groups>${profiles.active}</groups>
<!-- 排除标签 -->
@@ -454,8 +519,8 @@
<repositories>
<repository>
<id>public</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public/</url>
<name>huawei nexus</name>
<url>https://mirrors.huaweicloud.com/repository/maven/</url>
<releases>
<enabled>true</enabled>
</releases>
@@ -465,8 +530,8 @@
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public/</url>
<name>huawei nexus</name>
<url>https://mirrors.huaweicloud.com/repository/maven/</url>
<releases>
<enabled>true</enabled>
</releases>

View File

@@ -1,35 +1,31 @@
#基础镜像
FROM findepi/graalvm:java17-native
# 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/
FROM bellsoft/liberica-openjdk-rocky:17.0.16-cds
#FROM bellsoft/liberica-openjdk-rocky:21.0.8-cds
#FROM findepi/graalvm:java17-native
# 设置环境变量
ENV LANG C.UTF-8
ENV LANGUAGE C.UTF-8
ENV LC_ALL C.UTF-8
ENV SERVER_PORT=6039
MAINTAINER ageerle
LABEL maintainer="Lion Li"
RUN mkdir -p /ruoyi/server/logs \
/ruoyi/server/temp \
/ruoyi/skywalking/agent
#工作空间
WORKDIR /ruoyi/server
ENV SERVER_PORT=8080 SNAIL_PORT=28080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS=""
EXPOSE ${SERVER_PORT}
# 暴露 snail job 客户端端口 用于定时任务调度中心通信
EXPOSE ${SNAIL_PORT}
ADD ./target/ruoyi-admin.jar ./app.jar
SHELL ["/bin/bash", "-c"]
ENTRYPOINT ["java", \
"-Djava.security.egd=file:/dev/./urandom", \
"-Dserver.port=${SERVER_PORT}", \
# 应用名称 如果想区分集群节点监控 改成不同的名称即可
# "-Dskywalking.agent.service_name=ruoyi-server", \
# "-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar", \
"-jar", "app.jar"]
ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} \
-Dsnail-job.port=${SNAIL_PORT} \
# 应用名称 如果想区分集群节点监控 改成不同的名称即可
#-Dskywalking.agent.service_name=ruoyi-server \
#-javaagent:/ruoyi/skywalking/agent/skywalking-agent.jar \
-XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC ${JAVA_OPTS} \
-jar app.jar

View File

@@ -6,7 +6,6 @@
<artifactId>ruoyi-ai</artifactId>
<groupId>org.ruoyi</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
@@ -24,53 +23,80 @@
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!-- Oracle -->
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
</dependency>
<!-- PostgreSql -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
<!-- SqlServer -->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
</dependency>
<!-- &lt;!&ndash; mp支持的数据库均支持 只需要增加对应的jdbc依赖即可 &ndash;&gt;-->
<!-- &lt;!&ndash; Oracle &ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>com.oracle.database.jdbc</groupId>-->
<!-- <artifactId>ojdbc8</artifactId>-->
<!-- </dependency>-->
<!-- &lt;!&ndash; 兼容oracle低版本 &ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>com.oracle.database.nls</groupId>-->
<!-- <artifactId>orai18n</artifactId>-->
<!-- </dependency>-->
<!-- &lt;!&ndash; PostgreSql &ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>org.postgresql</groupId>-->
<!-- <artifactId>postgresql</artifactId>-->
<!-- </dependency>-->
<!-- &lt;!&ndash; SqlServer &ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>com.microsoft.sqlserver</groupId>-->
<!-- <artifactId>mssql-jdbc</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-doc</artifactId>
</dependency>
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-social</artifactId>
</dependency>
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-ratelimiter</artifactId>
</dependency>
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-mail</artifactId>
</dependency>
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-system</artifactId>
</dependency>
<!-- 代码生成-->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-generator</artifactId>
</dependency>
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-chat</artifactId>
</dependency>
<!-- 工作流模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-knowledge</artifactId>
<artifactId>ruoyi-workflow</artifactId>
</dependency>
<!-- AI流程编排模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-generator</artifactId>
<artifactId>ruoyi-aiflow</artifactId>
</dependency>
<!-- demo模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-demo</artifactId>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>
<dependency>
@@ -79,18 +105,17 @@
<scope>test</scope>
</dependency>
<!-- 添加thumbnailator依赖 -->
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.11</version>
</dependency>
<dependency>
<groupId>io.github.ollama4j</groupId>
<artifactId>ollama4j</artifactId>
<version>1.0.79</version>
<scope>compile</scope>
</dependency>
<!-- skywalking 整合 logback -->
<!-- <dependency>-->
<!-- <groupId>org.apache.skywalking</groupId>-->
<!-- <artifactId>apm-toolkit-logback-1.x</artifactId>-->
<!-- <version>${与你的agent探针版本保持一致}</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.apache.skywalking</groupId>-->
<!-- <artifactId>apm-toolkit-trace</artifactId>-->
<!-- <version>${与你的agent探针版本保持一致}</version>-->
<!-- </dependency>-->
</dependencies>

View File

@@ -16,6 +16,7 @@ public class RuoYiAIApplication {
SpringApplication application = new SpringApplication(RuoYiAIApplication.class);
application.setApplicationStartup(new BufferingApplicationStartup(2048));
application.run(args);
System.out.println("(♥◠‿◠)ノ゙ RuoYiAI启动成功 ლ(´ڡ`ლ)゙");
System.out.println("(♥◠‿◠)ノ゙ RuoYi-AI启动成功 ლ(´ڡ`ლ)゙");
}
}

View File

@@ -0,0 +1,48 @@
package org.ruoyi.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.Configuration;
/**
* BeanDefinitionRegistry后置处理器
* 解决 MapStruct Plus 生成的 mapper 冲突问题
*
* @author ruoyi team
*/
@Configuration
public class MapperConflictResolver implements BeanDefinitionRegistryPostProcessor {
private static final Logger log = LoggerFactory.getLogger(MapperConflictResolver.class);
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
String[] beanNames = registry.getBeanDefinitionNames();
// 查找冲突的 mapper bean
for (String beanName : beanNames) {
if (beanName.equals("chatMessageBoToChatMessageMapperImpl")) {
BeanDefinition beanDefinition = registry.getBeanDefinition(beanName);
String beanClassName = beanDefinition.getBeanClassName();
log.info("Found mapper bean: {} -> {}", beanName, beanClassName);
// 如果是 org.ruoyi.domain.bo.chat 包下的(冲突的),移除它
if (beanClassName != null && beanClassName.startsWith("org.ruoyi.domain.bo.chat")) {
log.warn("Removing conflicting bean definition: {} ({})", beanName, beanClassName);
registry.removeBeanDefinition(beanName);
}
}
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 不需要实现
}
}

View File

@@ -1,127 +1,174 @@
package org.ruoyi.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import me.chanjar.weixin.common.error.WxErrorException;
import org.ruoyi.common.core.constant.Constants;
import org.ruoyi.common.core.domain.R;
import org.ruoyi.common.core.domain.model.EmailLoginBody;
import org.ruoyi.common.core.domain.model.LoginBody;
import org.ruoyi.common.core.domain.model.RegisterBody;
import org.ruoyi.common.core.domain.model.SmsLoginBody;
import org.ruoyi.common.core.domain.model.VisitorLoginBody;
import org.ruoyi.common.core.utils.MapstructUtils;
import org.ruoyi.common.core.utils.StreamUtils;
import org.ruoyi.common.core.utils.StringUtils;
import org.ruoyi.common.satoken.utils.LoginHelper;
import org.ruoyi.common.tenant.helper.TenantHelper;
import org.ruoyi.system.domain.bo.SysTenantBo;
import org.ruoyi.system.domain.vo.LoginTenantVo;
import org.ruoyi.system.domain.vo.SysTenantVo;
import org.ruoyi.system.domain.vo.TenantListVo;
import org.ruoyi.system.service.ISysTenantService;
import org.ruoyi.system.service.SysLoginService;
import org.ruoyi.system.service.SysRegisterService;
import org.ruoyi.system.domain.vo.LoginVo;
import cn.hutool.core.util.ObjectUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.ruoyi.common.core.constant.SystemConstants;
import org.ruoyi.common.core.domain.R;
import org.ruoyi.common.core.domain.model.LoginBody;
import org.ruoyi.common.core.domain.model.RegisterBody;
import org.ruoyi.common.core.domain.model.SocialLoginBody;
import org.ruoyi.common.core.utils.*;
import org.ruoyi.common.encrypt.annotation.ApiEncrypt;
import org.ruoyi.common.json.utils.JsonUtils;
import org.ruoyi.common.ratelimiter.annotation.RateLimiter;
import org.ruoyi.common.ratelimiter.enums.LimitType;
import org.ruoyi.common.satoken.utils.LoginHelper;
import org.ruoyi.common.social.config.properties.SocialLoginConfigProperties;
import org.ruoyi.common.social.config.properties.SocialProperties;
import org.ruoyi.common.social.utils.SocialUtils;
import org.ruoyi.common.sse.dto.SseMessageDto;
import org.ruoyi.common.sse.utils.SseMessageUtils;
import org.ruoyi.common.tenant.helper.TenantHelper;
import org.ruoyi.system.domain.bo.SysTenantBo;
import org.ruoyi.system.domain.vo.*;
import org.ruoyi.system.service.*;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* 认证
*
* @author Lion Li
*/
@Slf4j
@SaIgnore
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/auth")
public class AuthController {
private final SocialProperties socialProperties;
private final SysLoginService loginService;
private final SysRegisterService registerService;
private final ISysConfigService configService;
private final ISysTenantService tenantService;
private final ISysSocialService socialUserService;
private final ISysClientService clientService;
private final ScheduledExecutorService scheduledExecutorService;
@PostMapping("/xcxLogin")
public R<LoginVo> login(@Validated @RequestBody String xcxCode) throws WxErrorException {
String openidFromCode = loginService.getOpenidFromCode((String) JSONUtil.parseObj(xcxCode).get("xcxCode"));
LoginVo loginVo = loginService.mpLogin(openidFromCode);
return R.ok(loginVo);
}
/**
* 登录方法
*
* @param body 登录信息
* @return 结果
*/
@ApiEncrypt
@PostMapping("/login")
public R<LoginVo> login(@Validated @RequestBody LoginBody body) {
body.setTenantId(Constants.TENANT_ID);
LoginVo loginVo = new LoginVo();
// 生成令牌
String token = loginService.login(
body.getTenantId(),
body.getUsername(), body.getPassword(),
body.getCode(), body.getUuid());
loginVo.setToken(token);
loginVo.setAccess_token(token);
loginVo.setUserInfo(LoginHelper.getLoginUser());
public R<LoginVo> login(@RequestBody String body) {
LoginBody loginBody = JsonUtils.parseObject(body, LoginBody.class);
ValidatorUtils.validate(loginBody);
// 授权类型和客户端id
String clientId = loginBody.getClientId();
String grantType = loginBody.getGrantType();
log.info("登录请求 - clientId: {}, grantType: {}", clientId, grantType);
SysClientVo client = clientService.queryByClientId(clientId);
log.info("查询客户端结果 - client: {}, grantType: {}", client, client != null ? client.getGrantType() : "null");
// 查询不到 client 或 client 内不包含 grantType
if (ObjectUtil.isNull(client)) {
log.info("客户端id: {} 不存在!", clientId);
return R.fail(MessageUtils.message("auth.grant.type.error"));
}
if (!StringUtils.contains(client.getGrantType(), grantType)) {
log.info("客户端id: {} 认证类型:{} 不匹配! 数据库grantType: {}", clientId, grantType, client.getGrantType());
return R.fail(MessageUtils.message("auth.grant.type.error"));
} else if (!SystemConstants.NORMAL.equals(client.getStatus())) {
return R.fail(MessageUtils.message("auth.grant.type.blocked"));
}
// 校验租户
loginService.checkTenant(loginBody.getTenantId());
// 登录
LoginVo loginVo = IAuthStrategy.login(body, client, grantType);
Long userId = LoginHelper.getUserId();
scheduledExecutorService.schedule(() -> {
SseMessageDto dto = new SseMessageDto();
dto.setMessage("欢迎登录ruoyi-ai后台管理系统");
dto.setUserIds(List.of(userId));
SseMessageUtils.publishMessage(dto);
}, 5, TimeUnit.SECONDS);
return R.ok(loginVo);
}
/**
* 短信登录
* 获取跳转URL
*
* @param body 登录信息
* @param source 登录来源
* @return 结果
*/
@PostMapping("/smsLogin")
public R<LoginVo> smsLogin(@Validated @RequestBody SmsLoginBody body) {
LoginVo loginVo = new LoginVo();
// 生成令牌
String token = loginService.smsLogin(body.getTenantId(), body.getPhonenumber(), body.getSmsCode());
loginVo.setToken(token);
return R.ok(loginVo);
@GetMapping("/binding/{source}")
public R<String> authBinding(@PathVariable("source") String source,
@RequestParam String tenantId, @RequestParam String domain) {
SocialLoginConfigProperties obj = socialProperties.getType().get(source);
if (ObjectUtil.isNull(obj)) {
return R.fail(source + "平台账号暂不支持");
}
AuthRequest authRequest = SocialUtils.getAuthRequest(source, socialProperties);
Map<String, String> map = new HashMap<>();
map.put("tenantId", tenantId);
map.put("domain", domain);
map.put("state", AuthStateUtils.createState());
String authorizeUrl = authRequest.authorize(Base64.encode(JsonUtils.toJsonString(map), StandardCharsets.UTF_8));
return R.ok("操作成功", authorizeUrl);
}
/**
* 访客登录
* 前端回调绑定授权(需要token)
*
* @param loginBody 登录信息
* @return token信息
*/
@PostMapping("/visitorLogin")
public R<LoginVo> visitorLogin(@RequestBody VisitorLoginBody loginBody) {
LoginVo loginVo = new LoginVo();
return R.ok(loginVo);
}
/**
* 邮件登录
*
* @param body 登录信息
* @param loginBody 请求体
* @return 结果
*/
@PostMapping("/emailLogin")
public R<LoginVo> emailLogin(@Validated @RequestBody EmailLoginBody body) {
LoginVo loginVo = new LoginVo();
// 生成令牌
String token = loginService.emailLogin(body.getTenantId(), body.getEmail(), body.getEmailCode());
loginVo.setToken(token);
return R.ok(loginVo);
@PostMapping("/social/callback")
public R<Void> socialCallback(@RequestBody SocialLoginBody loginBody) {
// 校验token
StpUtil.checkLogin();
// 获取第三方登录信息
AuthResponse<AuthUser> response = SocialUtils.loginAuth(
loginBody.getSource(), loginBody.getSocialCode(),
loginBody.getSocialState(), socialProperties);
AuthUser authUserData = response.getData();
// 判断授权响应是否成功
if (!response.ok()) {
return R.fail(response.getMsg());
}
loginService.socialRegister(authUserData);
return R.ok();
}
/**
* 取消授权(需要token)
*
* @param socialId socialId
*/
@DeleteMapping(value = "/unlock/{socialId}")
public R<Void> unlockSocial(@PathVariable Long socialId) {
// 校验token
StpUtil.checkLogin();
Boolean rows = socialUserService.deleteWithValidById(socialId);
return rows ? R.ok() : R.fail("取消授权失败");
}
/**
* 退出登录
*/
@@ -134,42 +181,58 @@ public class AuthController {
/**
* 用户注册
*/
@ApiEncrypt
@PostMapping("/register")
public R<Void> register(@Validated @RequestBody RegisterBody user, HttpServletRequest request) {
String domainName = request.getServerName();
user.setDomainName(domainName);
public R<Void> register(@Validated @RequestBody RegisterBody user) {
if (!configService.selectRegisterEnabled(user.getTenantId())) {
return R.fail("当前系统没有开启注册功能!");
}
registerService.register(user);
return R.ok();
}
/**
* 重置密码
*/
@PostMapping("/reset/password")
@SaIgnore
public R<Void> resetPassWord(@Validated @RequestBody RegisterBody user) {
registerService.resetPassWord(user);
return R.ok();
}
/**
* 登录页面租户下拉框
*
* @return 租户列表
*/
@RateLimiter(time = 60, count = 20, limitType = LimitType.IP)
@GetMapping("/tenant/list")
public R<LoginTenantVo> tenantList(HttpServletRequest request) throws Exception {
// 返回对象
LoginTenantVo result = new LoginTenantVo();
boolean enable = TenantHelper.isEnable();
result.setTenantEnabled(enable);
// 如果未开启租户这直接返回
if (!enable) {
return R.ok(result);
}
List<SysTenantVo> tenantList = tenantService.queryList(new SysTenantBo());
List<TenantListVo> voList = MapstructUtils.convert(tenantList, TenantListVo.class);
try {
// 如果只超管返回所有租户
if (LoginHelper.isSuperAdmin()) {
result.setVoList(voList);
return R.ok(result);
}
} catch (NotLoginException ignored) {
}
// 获取域名
String host = new URL(request.getRequestURL().toString()).getHost();
String host;
String referer = request.getHeader("referer");
if (StringUtils.isNotBlank(referer)) {
// 这里从referer中取值是为了本地使用hosts添加虚拟域名方便本地环境调试
host = referer.split("//")[1].split("/")[0];
} else {
host = new URL(request.getRequestURL().toString()).getHost();
}
// 根据域名进行筛选
List<TenantListVo> list = StreamUtils.filter(voList, vo -> StringUtils.equals(vo.getDomain(), host));
// 返回对象
LoginTenantVo vo = new LoginTenantVo();
vo.setVoList(CollUtil.isNotEmpty(list) ? list : voList);
vo.setTenantEnabled(TenantHelper.isEnable());
return R.ok(vo);
List<TenantListVo> list = StreamUtils.filter(voList, vo ->
StringUtils.equalsIgnoreCase(vo.getDomain(), host));
result.setVoList(CollUtil.isNotEmpty(list) ? list : voList);
return R.ok(result);
}
}

View File

@@ -5,38 +5,36 @@ import cn.hutool.captcha.AbstractCaptcha;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.ruoyi.common.core.constant.Constants;
import org.ruoyi.common.core.constant.GlobalConstants;
import org.ruoyi.common.core.domain.R;
import org.ruoyi.common.core.exception.ServiceException;
import org.ruoyi.common.core.service.ConfigService;
import org.ruoyi.common.core.utils.SpringUtils;
import org.ruoyi.common.core.utils.StringUtils;
import org.ruoyi.common.core.utils.reflect.ReflectUtils;
import org.ruoyi.common.mail.config.properties.MailProperties;
import org.ruoyi.common.mail.utils.MailUtils;
import org.ruoyi.common.ratelimiter.annotation.RateLimiter;
import org.ruoyi.common.ratelimiter.enums.LimitType;
import org.ruoyi.common.redis.utils.RedisUtils;
import org.ruoyi.common.sms.config.properties.SmsProperties;
import org.ruoyi.common.sms.core.SmsTemplate;
import org.ruoyi.common.sms.entity.SmsResult;
import org.ruoyi.common.web.config.properties.CaptchaProperties;
import org.ruoyi.common.web.enums.CaptchaType;
import org.ruoyi.system.domain.request.EmailRequest;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.api.entity.SmsResponse;
import org.dromara.sms4j.core.factory.SmsFactory;
import org.ruoyi.system.domain.vo.CaptchaVo;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.time.Duration;
import java.util.*;
import java.util.LinkedHashMap;
/**
* 验证码操作处理
@@ -51,31 +49,28 @@ import java.util.*;
public class CaptchaController {
private final CaptchaProperties captchaProperties;
private final SmsProperties smsProperties;
private final ConfigService configService;
private final MailProperties mailProperties;
/**
* 短信验证码
*
* @param phonenumber 用户手机号
*/
@RateLimiter(key = "#phonenumber", time = 60, count = 1)
@GetMapping("/resource/sms/code")
public R<Void> smsCode(@NotBlank(message = "{user.phonenumber.not.blank}") String phonenumber) {
if (!smsProperties.getEnabled()) {
return R.fail("当前系统没有开启短信功能!");
}
String key = GlobalConstants.CAPTCHA_CODE_KEY + phonenumber;
String code = RandomUtil.randomNumbers(4);
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
// 验证码模板id 自行处理 (查数据库或写死均可)
String templateId = "";
Map<String, String> map = new HashMap<>(1);
LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
map.put("code", code);
SmsTemplate smsTemplate = SpringUtils.getBean(SmsTemplate.class);
SmsResult result = smsTemplate.send(phonenumber, templateId, map);
if (!result.isSuccess()) {
log.error("验证码短信发送异常 => {}", result);
return R.fail(result.getMessage());
SmsBlend smsBlend = SmsFactory.getSmsBlend("config1");
SmsResponse smsResponse = smsBlend.sendMessage(phonenumber, templateId, map);
if (!smsResponse.isSuccess()) {
log.error("验证码短信发送异常 => {}", smsResponse);
return R.fail(smsResponse.getData().toString());
}
return R.ok();
}
@@ -83,36 +78,32 @@ public class CaptchaController {
/**
* 邮箱验证码
*
* @param emailRequest 用户邮箱
* @param email 邮箱
*/
@PostMapping("/resource/email/code")
public R<Void> emailCode(@RequestBody @Valid EmailRequest emailRequest) {
String key = GlobalConstants.CAPTCHA_CODE_KEY + emailRequest.getUsername();
@GetMapping("/resource/email/code")
public R<Void> emailCode(@NotBlank(message = "{user.email.not.blank}") String email) {
if (!mailProperties.getEnabled()) {
return R.fail("当前系统没有开启邮箱功能!");
}
SpringUtils.getAopProxy(this).emailCodeImpl(email);
return R.ok();
}
/**
* 邮箱验证码
* 独立方法避免验证码关闭之后仍然走限流
*/
@RateLimiter(key = "#email", time = 60, count = 1)
public void emailCodeImpl(String email) {
String key = GlobalConstants.CAPTCHA_CODE_KEY + email;
String code = RandomUtil.randomNumbers(4);
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
// 检验邮箱后缀
String suffix = configService.getConfigValue("mail", "suffix");
String prompt = configService.getConfigValue("mail", "prompt");
if(StringUtils.isNotEmpty(suffix)){
// 动态的域名列表
String[] invalidDomains = suffix.split(",");
for (String domain : invalidDomains) {
if (emailRequest.getUsername().endsWith(domain)) {
throw new ServiceException(prompt);
}
}
}
// 自定义邮箱模板
String model = configService.getConfigValue("mail", "mailModel");
String mailTitle = configService.getConfigValue("mail", "mailTitle");
String replacedModel = model.replace("{code}", code);
try {
MailUtils.sendHtml(emailRequest.getUsername(), mailTitle, replacedModel);
MailUtils.sendText(email, "登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。");
} catch (Exception e) {
log.error("邮箱验证码发送异常 => {}", e.getMessage());
return R.fail(e.getMessage());
log.error("验证码短信发送异常 => {}", e.getMessage());
throw new ServiceException(e.getMessage());
}
return R.ok();
}
/**
@@ -120,33 +111,47 @@ public class CaptchaController {
*/
@GetMapping("/auth/code")
public R<CaptchaVo> getCode() {
CaptchaVo captchaVo = new CaptchaVo();
boolean captchaEnabled = captchaProperties.getEnable();
if (!captchaEnabled) {
CaptchaVo captchaVo = new CaptchaVo();
captchaVo.setCaptchaEnabled(false);
return R.ok(captchaVo);
}
return R.ok(SpringUtils.getAopProxy(this).getCodeImpl());
}
/**
* 生成验证码
* 独立方法避免验证码关闭之后仍然走限流
*/
@RateLimiter(time = 60, count = 10, limitType = LimitType.IP)
public CaptchaVo getCodeImpl() {
// 保存验证码信息
String uuid = IdUtil.simpleUUID();
String verifyKey = GlobalConstants.CAPTCHA_CODE_KEY + uuid;
// 生成验证码
CaptchaType captchaType = captchaProperties.getType();
boolean isMath = CaptchaType.MATH == captchaType;
Integer length = isMath ? captchaProperties.getNumberLength() : captchaProperties.getCharLength();
CodeGenerator codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), length);
CodeGenerator codeGenerator;
if (CaptchaType.MATH == captchaType) {
codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), captchaProperties.getNumberLength(), false);
} else {
codeGenerator = ReflectUtils.newInstance(captchaType.getClazz(), captchaProperties.getCharLength());
}
AbstractCaptcha captcha = SpringUtils.getBean(captchaProperties.getCategory().getClazz());
captcha.setGenerator(codeGenerator);
captcha.createCode();
// 如果是数学验证码使用SpEL表达式处理验证码结果
String code = captcha.getCode();
if (isMath) {
if (CaptchaType.MATH == captchaType) {
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression(StringUtils.remove(code, "="));
code = exp.getValue(String.class);
}
RedisUtils.setCacheObject(verifyKey, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
CaptchaVo captchaVo = new CaptchaVo();
captchaVo.setUuid(uuid);
captchaVo.setImg(captcha.getImageBase64());
return R.ok(captchaVo);
return captchaVo;
}
}

View File

@@ -2,6 +2,8 @@ package org.ruoyi.controller;
import cn.dev33.satoken.annotation.SaIgnore;
import lombok.RequiredArgsConstructor;
import org.ruoyi.common.core.utils.SpringUtils;
import org.ruoyi.common.core.utils.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -20,7 +22,7 @@ public class IndexController {
*/
@GetMapping("/")
public String index() {
return "RuoYi-AI 启动成功";
return StringUtils.format("欢迎使用{}后台管理框架,请通过前端地址访问。", SpringUtils.getApplicationName());
}
}

View File

@@ -1,208 +0,0 @@
package org.ruoyi.controller;
import cn.dev33.satoken.stp.StpUtil;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.ruoyi.common.chat.config.ChatConfig;
import org.ruoyi.common.chat.domain.request.ChatRequest;
import org.ruoyi.common.chat.entity.chat.ChatCompletion;
import org.ruoyi.common.chat.entity.chat.Message;
import org.ruoyi.common.chat.openai.OpenAiStreamClient;
import org.ruoyi.common.core.domain.R;
import org.ruoyi.common.core.validate.AddGroup;
import org.ruoyi.common.excel.utils.ExcelUtil;
import org.ruoyi.common.log.annotation.Log;
import org.ruoyi.common.log.enums.BusinessType;
import org.ruoyi.common.mybatis.core.page.PageQuery;
import org.ruoyi.common.mybatis.core.page.TableDataInfo;
import org.ruoyi.common.satoken.utils.LoginHelper;
import org.ruoyi.common.web.core.BaseController;
import org.ruoyi.knowledge.domain.bo.KnowledgeAttachBo;
import org.ruoyi.knowledge.domain.bo.KnowledgeFragmentBo;
import org.ruoyi.knowledge.domain.bo.KnowledgeInfoBo;
import org.ruoyi.knowledge.domain.req.KnowledgeInfoUploadRequest;
import org.ruoyi.knowledge.domain.vo.KnowledgeAttachVo;
import org.ruoyi.knowledge.domain.vo.KnowledgeFragmentVo;
import org.ruoyi.knowledge.domain.vo.KnowledgeInfoVo;
import org.ruoyi.knowledge.service.EmbeddingService;
import org.ruoyi.knowledge.service.IKnowledgeAttachService;
import org.ruoyi.knowledge.service.IKnowledgeFragmentService;
import org.ruoyi.knowledge.service.IKnowledgeInfoService;
import org.ruoyi.system.listener.SSEEventSourceListener;
import org.ruoyi.system.service.ISseService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.ruoyi.knowledge.chain.vectorstore.VectorStore;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.util.List;
/**
* 知识库
*
* @author Lion Li
* @date 2024-10-21
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/knowledge")
public class KnowledgeController extends BaseController {
private final IKnowledgeInfoService knowledgeInfoService;
private final VectorStore vectorStore;
private final IKnowledgeAttachService attachService;
private final IKnowledgeFragmentService fragmentService;
private final EmbeddingService embeddingService;
private OpenAiStreamClient openAiStreamClient;
private final ChatConfig chatConfig;
private final ISseService sseService;
/**
* 知识库对话
*/
@PostMapping("/send")
public SseEmitter send(@RequestBody @Valid ChatRequest chatRequest) {
openAiStreamClient = chatConfig.getOpenAiStreamClient();
SseEmitter sseEmitter = new SseEmitter(0L);
SSEEventSourceListener openAIEventSourceListener = new SSEEventSourceListener(sseEmitter);
List<Message> messages = chatRequest.getMessages();
String content = messages.get(messages.size() - 1).getContent().toString();
List<String> nearestList;
List<Double> queryVector = embeddingService.getQueryVector(content, chatRequest.getKid());
nearestList = vectorStore.nearest(queryVector,chatRequest.getKid());
for (String prompt : nearestList) {
Message sysMessage = Message.builder().content(prompt).role(Message.Role.USER).build();
messages.add(sysMessage);
}
Message userMessage = Message.builder().content(content + (nearestList.size() > 0 ? "\n\n注意回答问题时须严格根据我给你的系统上下文内容原文进行回答请不要自己发挥,回答时保持原来文本的段落层级" : "") ).role(Message.Role.USER).build();
messages.add(userMessage);
if (chatRequest.getModel().startsWith("ollama")) {
return sseService.ollamaChat(chatRequest);
}
ChatCompletion completion = ChatCompletion
.builder()
.messages(messages)
.model(chatRequest.getModel())
.temperature(chatRequest.getTemperature())
.topP(chatRequest.getTop_p())
.stream(true)
.build();
openAiStreamClient.streamChatCompletion(completion, openAIEventSourceListener);
return sseEmitter;
}
/**
* 根据用户信息查询本地知识库
*/
@GetMapping("/list")
public TableDataInfo<KnowledgeInfoVo> list(KnowledgeInfoBo bo, PageQuery pageQuery) {
if(!StpUtil.isLogin()){
return null;
}
bo.setUid(LoginHelper.getUserId());
return knowledgeInfoService.queryPageList(bo, pageQuery);
}
/**
* 新增知识库
*/
@Log(title = "知识库", businessType = BusinessType.INSERT)
@PostMapping("/save")
public R<Void> save(@Validated(AddGroup.class) @RequestBody KnowledgeInfoBo bo) {
knowledgeInfoService.saveOne(bo);
return R.ok();
}
/**
* 删除知识库
*/
@PostMapping("/remove/{id}")
public R<String> remove(@PathVariable String id){
knowledgeInfoService.removeKnowledge(id);
return R.ok("删除知识库成功!");
}
/**
* 修改知识库
*/
@Log(title = "知识库", businessType = BusinessType.UPDATE)
@PostMapping("/edit")
public R<Void> edit( @RequestBody KnowledgeInfoBo bo) {
return toAjax(knowledgeInfoService.updateByBo(bo));
}
/**
* 导出知识库列表
*/
@Log(title = "知识库", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(KnowledgeInfoBo bo, HttpServletResponse response) {
List<KnowledgeInfoVo> list = knowledgeInfoService.queryList(bo);
ExcelUtil.exportExcel(list, "知识库", KnowledgeInfoVo.class, response);
}
/**
* 查询知识附件信息
*/
@GetMapping("/detail/{kid}")
public TableDataInfo<KnowledgeAttachVo> attach(KnowledgeAttachBo bo, PageQuery pageQuery,@PathVariable String kid){
bo.setKid(kid);
return attachService.queryPageList(bo, pageQuery);
}
/**
* 上传知识库附件
*/
@PostMapping(value = "/attach/upload")
public R<String> upload(KnowledgeInfoUploadRequest request){
knowledgeInfoService.upload(request);
return R.ok("上传知识库附件成功!");
}
/**
* 获取知识库附件详细信息
*
* @param id 主键
*/
@GetMapping("attach/info/{id}")
public R<KnowledgeAttachVo> getAttachInfo(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(attachService.queryById(id));
}
/**
* 删除知识库附件
*
*/
@PostMapping("attach/remove/{docId}")
public R<Void> removeAttach(@NotEmpty(message = "主键不能为空") @PathVariable String docId) {
attachService.removeKnowledgeAttach(docId);
return R.ok();
}
/**
* 查询知识片段
*/
@GetMapping("/fragment/list/{docId}")
public TableDataInfo<KnowledgeFragmentVo> fragmentList(KnowledgeFragmentBo bo, PageQuery pageQuery, @PathVariable String docId) {
bo.setDocId(docId);
return fragmentService.queryPageList(bo, pageQuery);
}
}

View File

@@ -5,8 +5,39 @@ spring.boot.admin.client:
url: http://localhost:9090/admin
instance:
service-host-type: IP
username: ruoyi
password: 123456
metadata:
username: ${spring.boot.admin.client.username}
userpassword: ${spring.boot.admin.client.password}
username: @monitor.username@
password: @monitor.password@
--- # mcp配置信息
mcp:
sse:
enabled: false
url: http://localhost:8085/sse
--- # 上传文件地址
sys:
upload:
path: D:\\DownLoad
--- # snail-job 配置
snail-job:
enabled: false
# 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
group: "ruoyi_group"
# SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config` 表
token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
server:
host: 127.0.0.1
port: 17888
# 命名空间UUID 详见 script/sql/ry_job.sql `sj_namespace`表`unique_id`字段
namespace: ${spring.profiles.active}
# 随主应用端口漂移
port: 2${server.port}
# 客户端ip指定
host:
--- # 数据源配置
spring:
@@ -25,10 +56,17 @@ spring:
master:
type: ${spring.datasource.type}
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://43.139.70.230:3306/ruoyi-ai?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
username: ruoyi-ai
password: ruoyi-ai
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
url: jdbc:mysql://127.0.0.1:3306/ruoyi-ai-agent?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: root
password: root
# agent:
# url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
# # url: jdbc:mysql://localhost:3306/agent_db
# username: root
# password: root
# driverClassName: com.mysql.cj.jdbc.Driver
hikari:
# 最大连接池数量
@@ -43,27 +81,33 @@ spring:
idleTimeout: 600000
# 此属性控制池中连接的最长生命周期值0表示无限生命周期默认30分钟
maxLifetime: 1800000
# 连接测试query配置检测连接是否有效
connectionTestQuery: SELECT 1
# 多久检查一次连接的活性
keepaliveTime: 30000
--- # 上传文件地址
sys:
upload:
path: D:\\DownLoad
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
spring.data:
redis:
# 地址
host: 127.0.0.1
host: localhost
# 端口默认为6379
port: 6379
# 数据库索引
database: 0
# 密码(如没有密码请注释掉)
# password: 123456
# redis 密码必须配置
# password: 123456
# 连接超时时间
timeout: 10S
timeout: 10s
# 是否开启ssl
ssl: false
ssl.enabled: false
# redisson 配置
redisson:
# redis key前缀
keyPrefix:
@@ -73,8 +117,8 @@ redisson:
nettyThreads: 8
# 单节点配置
singleServerConfig:
# 客户端名称
clientName: ${ruoyi.name}
# 客户端名称 不能用中文
clientName: ruoyi-ai
# 最小空闲连接数
connectionMinimumIdleSize: 8
# 连接池大小
@@ -86,14 +130,142 @@ redisson:
# 发布和订阅连接池大小
subscriptionConnectionPoolSize: 50
--- # sms 短信
sms:
--- # mail 邮件发送
mail:
enabled: false
# 阿里云 dysmsapi.aliyuncs.com
# 腾讯云 sms.tencentcloudapi.com
endpoint: "dysmsapi.aliyuncs.com"
accessKeyId: xxxxxxx
accessKeySecret: xxxxxx
signName: 测试
# 腾讯专
sdkAppId:
host: smtp.163.com
port: 465
# 是否需要用户名密码验证
auth: true
# 发送方遵循RFC-822标准
from: xxx@163.com
# 用户名注意如果使用foxmail邮箱此处user为qq号
user: xxx@163.com
# 密码注意某些邮箱需要为SMTP服务单独设置密码详情查看相关帮助
pass: xxxxxxxxxx
# 使用 STARTTLS安全连接STARTTLS是对纯文本通信协议的扩展。
starttlsEnable: true
# 使用SSL安全连接
sslEnable: true
# SMTP超时时长单位毫秒缺省值不超时
timeout: 0
# Socket连接超时值单位毫秒缺省值不超时
connectionTimeout: 0
--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商
# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用
sms:
# 配置源类型用于标定配置来源(interface,yaml)
config-type: yaml
# 用于标定yml中的配置是否开启短信拦截接口配置不受此限制
restricted: true
# 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效
minute-max: 1
# 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效
account-max: 30
# 以下配置来自于 org.dromara.sms4j.provider.config.BaseConfig类中
blends:
# 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可
# 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户
config1:
# 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
supplier: alibaba
# 有些称为accessKey有些称之为apiKey也有称为sdkKey或者appId。
access-key-id: 您的accessKey
# 称为accessSecret有些称之为apiSecret
access-key-secret: 您的accessKeySecret
signature: 您的短信签名
sdk-app-id: 您的sdkAppId
config2:
# 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
supplier: tencent
access-key-id: 您的accessKey
access-key-secret: 您的accessKeySecret
signature: 您的短信签名
sdk-app-id: 您的sdkAppId
--- # 三方授权
justauth:
# 前端外网访问地址
address: http://localhost:80
type:
maxkey:
# maxkey 服务器地址
# 注意 如下均配置均不需要修改 maxkey 已经内置好了数据
server-url: http://sso.maxkey.top
client-id: 876892492581044224
client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
redirect-uri: ${justauth.address}/social-callback?source=maxkey
topiam:
# topiam 服务器地址
server-url: http://127.0.0.1:1898/api/v1/authorize/y0q************spq***********8ol
client-id: 449c4*********937************759
client-secret: ac7***********1e0************28d
redirect-uri: ${justauth.address}/social-callback?source=topiam
scopes: [openid, email, phone, profile]
qq:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=qq
union-id: false
weibo:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=weibo
gitee:
client-id: 91436b7940090d09c72c7daf85b959cfd5f215d67eea73acbf61b6b590751a98
client-secret: 02c6fcfd70342980cd8dd2f2c06c1a350645d76c754d7a264c4e125f9ba915ac
redirect-uri: ${justauth.address}/social-callback?source=gitee
dingtalk:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=dingtalk
baidu:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=baidu
csdn:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=csdn
coding:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=coding
coding-group-name: xx
oschina:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=oschina
alipay_wallet:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=alipay_wallet
alipay-public-key: MIIB**************DAQAB
wechat_open:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=wechat_open
wechat_mp:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=wechat_mp
wechat_enterprise:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=wechat_enterprise
agent-id: 1000002
gitlab:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitlab
gitea:
# 前端改动 https://gitee.com/JavaLionLi/plus-ui/pulls/204
# gitea 服务器地址
server-url: https://demo.gitea.com
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitea
AGENT_ALLOWED_TABLES: ""

View File

@@ -1,6 +1,3 @@
--- # 临时文件存储位置 避免临时文件被系统清理报错
spring.servlet.multipart.location: /ruoyi/server/temp
--- # 监控中心配置
spring.boot.admin.client:
# 增加客户端开关
@@ -8,30 +5,39 @@ spring.boot.admin.client:
url: http://localhost:9090/admin
instance:
service-host-type: IP
username: ruoyi
password: 123456
metadata:
username: ${spring.boot.admin.client.username}
userpassword: ${spring.boot.admin.client.password}
username: @monitor.username@
password: @monitor.password@
--- # xxl-job 配置
xxl.job:
# 执行器开关
--- # mcp配置信息
mcp:
sse:
enabled: false
url: http://localhost:8085/sse
--- # 上传文件地址
sys:
upload:
path: D:\\DownLoad
--- # snail-job 配置
snail-job:
enabled: false
# 调度中心地址:如调度中心集群部署存在多个地址则用逗号分隔。
admin-addresses: http://localhost:9100/xxl-job-admin
# 执行器通讯TOKEN非空时启用
access-token: xxl-job
executor:
# 执行器AppName执行器心跳注册分组依据为空则关闭自动注册
appname: xxl-job-executor
# 执行器端口号 执行器从9101开始往后写
port: 9101
# 执行器注册默认IP:PORT
address:
# 执行器IP默认自动获取IP
ip:
# 执行器运行日志文件存储磁盘路径
logpath: ./logs/xxl-job
# 执行器日志文件保存天数大于3生效
logretentiondays: 30
# 需要在 SnailJob 后台组管理创建对应名称的组,然后创建任务的时候选择对应的组,才能正确分派任务
group: "ruoyi_group"
# SnailJob 接入验证令牌 详见 script/sql/ry_job.sql `sj_group_config` 表
token: "SJ_cKqBTPzCsWA3VyuCfFoccmuIEGXjr5KT"
server:
host: 127.0.0.1
port: 17888
# 命名空间UUID 详见 script/sql/ry_job.sql `sj_namespace`表`unique_id`字段
namespace: ${spring.profiles.active}
# 随主应用端口漂移
port: 2${server.port}
# 客户端ip指定
host:
--- # 数据源配置
spring:
@@ -40,7 +46,7 @@ spring:
# 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
dynamic:
# 性能分析插件(有性能损耗 不建议生产环境使用)
p6spy: false
p6spy: true
# 设置默认的数据源或者数据源组,默认值即为 master
primary: master
# 严格模式 匹配不到数据源则报错
@@ -52,37 +58,16 @@ spring:
driverClassName: com.mysql.cj.jdbc.Driver
# jdbc 所有参数配置参考 https://lionli.blog.csdn.net/article/details/122018562
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
url: jdbc:mysql://127.0.0.1:3306/ruoyi-ai-agent?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
username: root
password: root
# 从库数据源
slave:
lazy: true
type: ${spring.datasource.type}
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
username:
password:
# oracle:
# type: ${spring.datasource.type}
# driverClassName: oracle.jdbc.OracleDriver
# url: jdbc:oracle:thin:@//localhost:1521/XE
# username: ROOT
# password: root
# hikari:
# connectionTestQuery: SELECT 1 FROM DUAL
# postgres:
# type: ${spring.datasource.type}
# driverClassName: org.postgresql.Driver
# url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true
# agent:
# url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
# # url: jdbc:mysql://localhost:3306/agent_db
# username: root
# password: root
# sqlserver:
# type: ${spring.datasource.type}
# driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
# url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true
# username: SA
# password: root
# driverClassName: com.mysql.cj.jdbc.Driver
hikari:
# 最大连接池数量
maxPoolSize: 20
@@ -96,11 +81,16 @@ spring:
idleTimeout: 600000
# 此属性控制池中连接的最长生命周期值0表示无限生命周期默认30分钟
maxLifetime: 1800000
# 连接测试query配置检测连接是否有效
connectionTestQuery: SELECT 1
# 多久检查一次连接的活性
keepaliveTime: 30000
--- # 上传文件地址
sys:
upload:
path: D:\\DownLoad
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
spring.data:
redis:
@@ -110,28 +100,29 @@ spring.data:
port: 6379
# 数据库索引
database: 0
# 密码(如没有密码请注释掉)
# password:
# redis 密码必须配置
# password: 123456
# 连接超时时间
timeout: 10s
# 是否开启ssl
ssl: false
ssl.enabled: false
# redisson 配置
redisson:
# redis key前缀
keyPrefix:
# 线程池数量
threads: 16
threads: 4
# Netty线程池数量
nettyThreads: 32
nettyThreads: 8
# 单节点配置
singleServerConfig:
# 客户端名称
clientName: ${ruoyi.name}
# 客户端名称 不能用中文
clientName: ruoyi-ai
# 最小空闲连接数
connectionMinimumIdleSize: 32
connectionMinimumIdleSize: 8
# 连接池大小
connectionPoolSize: 64
connectionPoolSize: 32
# 连接空闲超时,单位:毫秒
idleConnectionTimeout: 10000
# 命令等待超时,单位:毫秒
@@ -161,14 +152,120 @@ mail:
# Socket连接超时值单位毫秒缺省值不超时
connectionTimeout: 0
--- # sms 短信
--- # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商
# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用
sms:
enabled: false
# 阿里云 dysmsapi.aliyuncs.com
# 腾讯云 sms.tencentcloudapi.com
endpoint: "dysmsapi.aliyuncs.com"
accessKeyId: xxxxxxx
accessKeySecret: xxxxxx
signName: 测试
# 腾讯专用
sdkAppId:
# 配置源类型用于标定配置来源(interface,yaml)
config-type: yaml
# 用于标定yml中的配置是否开启短信拦截接口配置不受此限制
restricted: true
# 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效
minute-max: 1
# 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效
account-max: 30
# 以下配置来自于 org.dromara.sms4j.provider.config.BaseConfig类中
blends:
# 唯一ID 用于发送短信寻找具体配置 随便定义别用中文即可
# 可以同时存在两个相同厂商 例如: ali1 ali2 两个不同的阿里短信账号 也可用于区分租户
config1:
# 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
supplier: alibaba
# 有些称为accessKey有些称之为apiKey也有称为sdkKey或者appId。
access-key-id: 您的accessKey
# 称为accessSecret有些称之为apiSecret
access-key-secret: 您的accessKeySecret
signature: 您的短信签名
sdk-app-id: 您的sdkAppId
config2:
# 厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
supplier: tencent
access-key-id: 您的accessKey
access-key-secret: 您的accessKeySecret
signature: 您的短信签名
sdk-app-id: 您的sdkAppId
--- # 三方授权
justauth:
# 前端外网访问地址
address: http://localhost:80
type:
maxkey:
# maxkey 服务器地址
# 注意 如下均配置均不需要修改 maxkey 已经内置好了数据
server-url: http://sso.maxkey.top
client-id: 876892492581044224
client-secret: x1Y5MTMwNzIwMjMxNTM4NDc3Mzche8
redirect-uri: ${justauth.address}/social-callback?source=maxkey
topiam:
# topiam 服务器地址
server-url: http://127.0.0.1:1898/api/v1/authorize/y0q************spq***********8ol
client-id: 449c4*********937************759
client-secret: ac7***********1e0************28d
redirect-uri: ${justauth.address}/social-callback?source=topiam
scopes: [openid, email, phone, profile]
qq:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=qq
union-id: false
weibo:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=weibo
gitee:
client-id: 91436b7940090d09c72c7daf85b959cfd5f215d67eea73acbf61b6b590751a98
client-secret: 02c6fcfd70342980cd8dd2f2c06c1a350645d76c754d7a264c4e125f9ba915ac
redirect-uri: ${justauth.address}/social-callback?source=gitee
dingtalk:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=dingtalk
baidu:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=baidu
csdn:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=csdn
coding:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=coding
coding-group-name: xx
oschina:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=oschina
alipay_wallet:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=alipay_wallet
alipay-public-key: MIIB**************DAQAB
wechat_open:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=wechat_open
wechat_mp:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=wechat_mp
wechat_enterprise:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=wechat_enterprise
agent-id: 1000002
gitlab:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitlab
gitea:
# 前端改动 https://gitee.com/JavaLionLi/plus-ui/pulls/204
# gitea 服务器地址
server-url: https://demo.gitea.com
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitea
AGENT_ALLOWED_TABLES: "abtest_rule,abtest_project,agent_ban_log,agent_ban_logs,agent_install_sub_task,agent_install_sum_task,agent_install_task"

View File

@@ -1,32 +1,6 @@
# 项目相关配置
ruoyi:
# 名称
name: "ruoyi"
# 版本
version: ${revision}
# 版权年份
copyrightYear: 2025
# 实例演示开关
demoEnabled: true
# 获取ip地址开关
addressEnabled: false
captcha:
enable: false
# 页面 <参数设置> 可开启关闭 验证码校验
# 验证码类型 math 数组计算 char 字符验证
type: MATH
# line 线段干扰 circle 圆圈干扰 shear 扭曲干扰
category: CIRCLE
# 数字验证码位数
numberLength: 1
# 字符验证码长度
charLength: 4
# 开发环境配置
server:
# 服务器的HTTP端口默认为8080
# 服务器的HTTP端口默认为6039
port: 6039
servlet:
# 应用的访问路径
@@ -46,11 +20,25 @@ server:
# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
worker: 256
captcha:
# 是否启用验证码校验
enable: false
# 验证码类型 math 数组计算 char 字符验证
type: MATH
# line 线段干扰 circle 圆圈干扰 shear 扭曲干扰
category: CIRCLE
# 数字验证码位数
numberLength: 1
# 字符验证码长度
charLength: 4
# 日志配置
logging:
level:
org.ruoyi: @logging.level@
org.springframework: warn
org.mybatis.spring.mapper: error
org.apache.fury: warn
config: classpath:logback-plus.xml
# 用户配置
@@ -64,7 +52,24 @@ user:
# Spring配置
spring:
application:
name: ${ruoyi.name}
name: ruoyi-ai
# ⚠️ 禁用 Spring Boot 的 Neo4j 自动配置
# 默认情况下,如果类路径上存在 neo4j-java-driverSpring Boot 会尝试自动配置
# 这会导致应用在启动时尝试连接到 Neo4j即使我们没有需要它
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.neo4j.Neo4jAutoConfiguration
threads:
# 开启虚拟线程 仅jdk21可用
virtual:
enabled: false
task:
execution:
# 从 springboot 3.5 开始 spring自带线程池
# 不再需要 AsyncConfig与ThreadPoolConfig 可直接注入线程池使用
thread-name-prefix: async-
# 由spring自己初始化线程池
mode: force
# 资源信息
messages:
# 国际化资源文件路径
@@ -75,10 +80,12 @@ spring:
servlet:
multipart:
# 单个文件大小
max-file-size: 50MB
max-file-size: 10MB
# 设置总上传的文件大小
max-request-size: 200MB
max-request-size: 20MB
mvc:
# 设置静态资源路径 防止所有请求都去查静态资源
static-path-pattern: /static/**
format:
date-time: yyyy-MM-dd HH:mm:ss
jackson:
@@ -97,20 +104,10 @@ spring:
sa-token:
# token名称 (同时也是cookie名称)
token-name: Authorization
# token有效期 设为7天 (必定过期) 单位: 秒
timeout: 604800
# token临时有效期 (指定时间无操作就过期) 单位: 秒
activity-timeout: 604800
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
is-concurrent: true
# 在多人登录同一账号时是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
is-share: false
# 是否尝试从header里读取token
is-read-header: true
# 是否尝试从cookie里读取token
is-read-cookie: false
# token前缀
token-prefix: "Bearer"
# jwt秘钥
jwt-secret-key: abcdefghijklmnopqrstuvwxyz
@@ -118,33 +115,20 @@ sa-token:
security:
# 排除路径
excludes:
# 支付回调
- /pay/returnUrl
- /pay/notifyUrl
# 上传文件
- /resource/oss/upload
# 重置密码
- /auth/reset/password
# 聊天接口
- /chat
# 静态资源
- /*.html
- /**/*.html
- /**/*.css
- /**/*.js
# 公共路径
- /favicon.ico
- /error
# swagger 文档配置
- /*/api-docs
- /*/api-docs/**
# actuator 监控配置
- /actuator
- /actuator/**
- /warm-flow-ui/config
- /workflow/run
# 多租户配置
tenant:
# 是否开启
enable: false
enable: true
# 排除表
excludes:
- sys_menu
@@ -154,50 +138,27 @@ tenant:
- sys_role_menu
- sys_user_post
- sys_user_role
- sys_client
- sys_oss_config
- flow_spel
# MyBatisPlus配置
# https://baomidou.com/config/
mybatis-plus:
# 不支持多包, 如有需要可在注解配置 或 提升扫包等级
# 例如 com.**.**.mapper
# 自定义配置 是否全局开启逻辑删除 关闭后 所有逻辑删除功能将失效
enableLogicDelete: true
# 多包名使用 例如 org.ruoyi.**.mapper,org.xxx.**.mapper
mapperPackage: org.ruoyi.**.mapper
# 对应的 XML 文件位置
mapperLocations: classpath*:mapper/**/*Mapper.xml
# 实体扫描多个package用逗号或者分号分隔
typeAliasesPackage: org.ruoyi.**.domain
# 启动时是否检查 MyBatis XML 文件的存在,默认不检查
checkConfigLocation: false
configuration:
# 自动驼峰命名规则camel case映射
mapUnderscoreToCamelCase: true
# MyBatis 自动映射策略
# NONE不启用 PARTIAL只对非嵌套 resultMap 自动映射 FULL对所有 resultMap 自动映射
autoMappingBehavior: FULL
# MyBatis 自动映射时未知列或未知属性处理策
# NONE不做处理 WARNING打印相关警告 FAILING抛出异常和详细信息
autoMappingUnknownColumnBehavior: NONE
# 更详细的日志输出 会有性能损耗 org.apache.ibatis.logging.stdout.StdOutImpl
# 关闭日志记录 (可单纯使用 p6spy 分析) org.apache.ibatis.logging.nologging.NoLoggingImpl
# 默认日志输出 org.apache.ibatis.logging.slf4j.Slf4jImpl
logImpl: org.apache.ibatis.logging.nologging.NoLoggingImpl
global-config:
# 是否打印 Logo banner
banner: true
dbConfig:
# 主键类型
# AUTO 自增 NONE 空 INPUT 用户输入 ASSIGN_ID 雪花 ASSIGN_UUID 唯一 UUID
# 如需改为自增 需要将数据库表全部设置为自增
idType: ASSIGN_ID
# 逻辑已删除值
logicDeleteValue: 2
# 逻辑未删除值
logicNotDeleteValue: 0
# 字段验证策略之 insert,在 insert 的时候的字段验证策略
# IGNORED 忽略 NOT_NULL 非NULL NOT_EMPTY 非空 DEFAULT 默认 NEVER 不加入 SQL
insertStrategy: NOT_NULL
# 字段验证策略之 update,在 update 的时候的字段验证策略
updateStrategy: NOT_NULL
# 字段验证策略之 select,在 select 的时候的字段验证策略既 wrapper 根据内部 entity 生成的 where 条件
where-strategy: NOT_NULL
# 数据加密
mybatis-encryptor:
@@ -213,35 +174,36 @@ mybatis-encryptor:
publicKey:
privateKey:
# Swagger配置
swagger:
info:
# 标题
title: '标题:${ruoyi.name}多租户管理系统_接口文档'
# 描述
description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'
# 版本
version: '版本号: ${ruoyi.version}'
# 作者信息
contact:
name: ageerle
email: ageerle@163.com
url: https://gitee.com/ageerle/ruoyi-ai
components:
# 鉴权方式配置
security-schemes:
apiKey:
type: APIKEY
in: HEADER
name: ${sa-token.token-name}
# api接口加密
api-decrypt:
# 是否开启全局接口加密
enabled: false
# AES 加密头标识
headerFlag: encrypt-key
# 响应加密公钥 非对称算法的公私钥 如SM2RSA 使用者请自行更换
# 对应前端解密私钥 MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAmc3CuPiGL/LcIIm7zryCEIbl1SPzBkr75E2VMtxegyZ1lYRD+7TZGAPkvIsBcaMs6Nsy0L78n2qh+lIZMpLH8wIDAQABAkEAk82Mhz0tlv6IVCyIcw/s3f0E+WLmtPFyR9/WtV3Y5aaejUkU60JpX4m5xNR2VaqOLTZAYjW8Wy0aXr3zYIhhQQIhAMfqR9oFdYw1J9SsNc+CrhugAvKTi0+BF6VoL6psWhvbAiEAxPPNTmrkmrXwdm/pQQu3UOQmc2vCZ5tiKpW10CgJi8kCIFGkL6utxw93Ncj4exE/gPLvKcT+1Emnoox+O9kRXss5AiAMtYLJDaLEzPrAWcZeeSgSIzbL+ecokmFKSDDcRske6QIgSMkHedwND1olF8vlKsJUGK3BcdtM8w4Xq7BpSBwsloE=
publicKey: MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJnNwrj4hi/y3CCJu868ghCG5dUj8wZK++RNlTLcXoMmdZWEQ/u02RgD5LyLAXGjLOjbMtC+/J9qofpSGTKSx/MCAwEAAQ==
# 请求解密私钥 非对称算法的公私钥 如SM2RSA 使用者请自行更换
# 对应前端加密公钥 MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ==
privateKey: MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAqhHyZfSsYourNxaY7Nt+PrgrxkiA50efORdI5U5lsW79MmFnusUA355oaSXcLhu5xxB38SMSyP2KvuKNPuH3owIDAQABAkAfoiLyL+Z4lf4Myxk6xUDgLaWGximj20CUf+5BKKnlrK+Ed8gAkM0HqoTt2UZwA5E2MzS4EI2gjfQhz5X28uqxAiEA3wNFxfrCZlSZHb0gn2zDpWowcSxQAgiCstxGUoOqlW8CIQDDOerGKH5OmCJ4Z21v+F25WaHYPxCFMvwxpcw99EcvDQIgIdhDTIqD2jfYjPTY8Jj3EDGPbH2HHuffvflECt3Ek60CIQCFRlCkHpi7hthhYhovyloRYsM+IS9h/0BzlEAuO0ktMQIgSPT3aFAgJYwKpqRYKlLDVcflZFCKY7u3UP8iWi1Qw0Y=
springdoc:
api-docs:
# 是否开启接口文档
enabled: true
swagger-ui:
# 持久化认证数据
persistAuthorization: true
info:
# 标题
title: '标题ruoyi-ai管理系统_接口文档'
# 描述
description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'
# 版本
version: '版本号: ${project.version}'
# 作者信息
contact:
name: ageerle
email: ageerle@163.com
url: https://gitee.com/ageerle/ruoyi-ai
#这里定义了两个分组,可定义多个,也可以不定义
group-configs:
- group: 1.演示模块
@@ -252,24 +214,18 @@ springdoc:
packages-to-scan: org.ruoyi.system
- group: 4.代码生成模块
packages-to-scan: org.ruoyi.generator
- group: 5.工作流模块
packages-to-scan: org.ruoyi.workflow
- group: 6.MCP模块
packages-to-scan: org.ruoyi.mcp
# 防止XSS攻击
xss:
# 过滤开关
enabled: true
# 排除链接(多个用逗号分隔)
excludes: /system/notice
# 匹配链接
urlPatterns: /system/*,/monitor/*,/tool/*
# 全局线程池相关配置
thread-pool:
# 是否开启线程池
enabled: false
# 队列最大长度
queueCapacity: 128
# 线程池维护线程所允许的空闲时间
keepAliveSeconds: 300
# 排除链接
excludeUrls:
- /system/notice
--- # 分布式锁 lock4j 全局配置
lock4j:
@@ -290,33 +246,44 @@ management:
logfile:
external-file: ./logs/sys-console.log
# websocket
websocket:
--- # 默认/推荐使用sse推送
sse:
enabled: true
path: /resource/sse
--- # websocket
websocket:
# 如果关闭 需要和前端开关一起关闭
enabled: false
# 路径
path: '/resource/websocket'
path: /resource/websocket
# 设置访问源地址
allowedOrigins: '*'
# 微信小程序配置信息
wx:
miniapp:
configs:
- appid: # 你的appid
secret: # 你的secret
token: #微信小程序消息服务器配置的token
aesKey: #微信小程序消息服务器配置的EncodingAESKey
msgDataFormat: JSON
# 企业微信应用
wechat:
cp:
corpId:
appConfigs:
- agentId:
secret: ''
token: ''
aesKey: ''
--- # warm-flow工作流配置
warm-flow:
# 是否开启工作流默认true
enabled: true
# 是否开启设计器ui
ui: true
# 是否显示流程图顶部文字
top-text-show: true
# 是否渲染节点悬浮提示默认true
node-tooltip: true
# 默认Authorization如果有多个token用逗号分隔
token-name: ${sa-token.token-name},clientid
# 向量库配置
vector-store:
# 向量存储类型 可选(weaviate/milvus)
# 如需修改向量库类型,请修改此配置值!
type: milvus
# Weaviate配置
weaviate:
protocol: http
host: 127.0.0.1:6038
classname: LocalKnowledge
# Milvus配置
milvus:
url: http://localhost:19530
collectionname: LocalKnowledge

View File

@@ -1,2 +1,8 @@
Application Version: ${revision}
Spring Boot Version: ${spring-boot.version}
__________ _____.___.__ _____ .___
\______ \__ __ ____\__ | |__| / _ \ | |
| _/ | \/ _ \/ | | | ______ / /_\ \| |
| | \ | ( <_> )____ | | /_____/ / | \ |
|____|_ /____/ \____// ______|__| \____|__ /___|
\/ \/ \/

View File

@@ -17,6 +17,7 @@ user.username.length.valid=账户长度必须在{min}到{max}个字符之间
user.password.not.blank=用户密码不能为空
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
user.password.not.valid=* 5-50个字符
user.password.format.valid=密码必须包含大写字母、小写字母、数字和特殊字符
user.email.not.valid=邮箱格式错误
user.email.not.blank=邮箱不能为空
user.phonenumber.not.blank=用户手机号不能为空
@@ -28,6 +29,10 @@ user.register.error=注册失败,请联系系统管理人员
user.notfound=请重新登录
user.forcelogout=管理员强制退出,请重新登录
user.unknown.error=未知错误,请重新登录
auth.grant.type.error=认证权限类型错误
auth.grant.type.blocked=认证权限类型已禁用
auth.grant.type.not.blank=认证权限类型不能为空
auth.clientid.not.blank=认证客户端id不能为空
##文件上传消息
upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB
upload.filename.exceed.length=上传的文件名最长{0}个字符
@@ -46,7 +51,10 @@ sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}
email.code.not.blank=邮箱验证码不能为空
email.code.retry.limit.count=邮箱验证码输入错误{0}次
email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟
xcx.code.not.blank=小程序code不能为空
xcx.code.not.blank=小程序[code]不能为空
social.source.not.blank=第三方登录平台[source]不能为空
social.code.not.blank=第三方登录平台[code]不能为空
social.state.not.blank=第三方登录平台[state]不能为空
##租户
tenant.number.not.blank=租户编号不能为空
tenant.not.exists=对不起, 您的租户不存在,请联系管理员

View File

@@ -17,6 +17,7 @@ user.username.length.valid=Account length must be between {min} and {max} charac
user.password.not.blank=Password cannot be empty
user.password.length.valid=Password length must be between {min} and {max} characters
user.password.not.valid=* 5-50 characters
user.password.format.valid=Password must contain uppercase, lowercase, digit, and special character
user.email.not.valid=Mailbox format error
user.email.not.blank=Mailbox cannot be blank
user.phonenumber.not.blank=Phone number cannot be blank
@@ -28,6 +29,10 @@ user.register.error=Register failed, please contact system administrator
user.notfound=Please login again
user.forcelogout=The administrator is forced to exitplease login again
user.unknown.error=Unknown error, please login again
auth.grant.type.error=Auth grant type error
auth.grant.type.blocked=Auth grant type disabled
auth.grant.type.not.blank=Auth grant type cannot be blank
auth.clientid.not.blank=Auth clientid cannot be blank
##文件上传消息
upload.exceed.maxSize=The uploaded file size exceeds the limit file size<br/>the maximum allowed file size is{0}MB
upload.filename.exceed.length=The maximum length of uploaded file name is {0} characters
@@ -46,7 +51,10 @@ sms.code.retry.limit.exceed=Sms code input error {0} times, account locked for {
email.code.not.blank=Email code cannot be blank
email.code.retry.limit.count=Email code input error {0} times
email.code.retry.limit.exceed=Email code input error {0} times, account locked for {1} minutes
xcx.code.not.blank=Mini program code cannot be blank
xcx.code.not.blank=Mini program [code] cannot be blank
social.source.not.blank=Social login platform [source] cannot be blank
social.code.not.blank=Social login platform [code] cannot be blank
social.state.not.blank=Social login platform [state] cannot be blank
##租户
tenant.number.not.blank=Tenant number cannot be blank
tenant.not.exists=Sorry, your tenant does not exist. Please contact the administrator

View File

@@ -17,6 +17,7 @@ user.username.length.valid=账户长度必须在{min}到{max}个字符之间
user.password.not.blank=用户密码不能为空
user.password.length.valid=用户密码长度必须在{min}到{max}个字符之间
user.password.not.valid=* 5-50个字符
user.password.format.valid=密码必须包含大写字母、小写字母、数字和特殊字符
user.email.not.valid=邮箱格式错误
user.email.not.blank=邮箱不能为空
user.phonenumber.not.blank=用户手机号不能为空
@@ -28,6 +29,10 @@ user.register.error=注册失败,请联系系统管理人员
user.notfound=请重新登录
user.forcelogout=管理员强制退出,请重新登录
user.unknown.error=未知错误,请重新登录
auth.grant.type.error=认证权限类型错误
auth.grant.type.blocked=认证权限类型已禁用
auth.grant.type.not.blank=认证权限类型不能为空
auth.clientid.not.blank=认证客户端id不能为空
##文件上传消息
upload.exceed.maxSize=上传的文件大小超出限制的文件大小!<br/>允许的文件最大大小是:{0}MB
upload.filename.exceed.length=上传的文件名最长{0}个字符
@@ -46,7 +51,10 @@ sms.code.retry.limit.exceed=短信验证码输入错误{0}次,帐户锁定{1}
email.code.not.blank=邮箱验证码不能为空
email.code.retry.limit.count=邮箱验证码输入错误{0}次
email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,帐户锁定{1}分钟
xcx.code.not.blank=小程序code不能为空
xcx.code.not.blank=小程序[code]不能为空
social.source.not.blank=第三方登录平台[source]不能为空
social.code.not.blank=第三方登录平台[code]不能为空
social.state.not.blank=第三方登录平台[state]不能为空
##租户
tenant.number.not.blank=租户编号不能为空
tenant.not.exists=对不起, 您的租户不存在,请联系管理员

View File

@@ -2,7 +2,7 @@
<configuration>
<property name="log.path" value="./logs"/>
<property name="console.log.pattern"
value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
value="%cyan(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
<property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/>
<!-- 控制台输出 -->
@@ -38,7 +38,7 @@
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log</fileNamePattern>
<fileNamePattern>${log.path}/sys-info.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>
@@ -60,7 +60,7 @@
<!-- 循环政策:基于时间创建日志文件 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log</fileNamePattern>
<fileNamePattern>${log.path}/sys-error.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
<!-- 日志最大的历史 60天 -->
<maxHistory>60</maxHistory>
</rollingPolicy>

View File

@@ -1 +0,0 @@
exclude=SELECT 1

View File

@@ -6,16 +6,18 @@
<artifactId>ruoyi-ai</artifactId>
<groupId>org.ruoyi</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<modules>
<module>ruoyi-common-bom</module>
<module>ruoyi-common-chat</module>
<module>ruoyi-common-social</module>
<module>ruoyi-common-core</module>
<module>ruoyi-common-doc</module>
<module>ruoyi-common-excel</module>
<module>ruoyi-common-idempotent</module>
<module>ruoyi-common-job</module>
<module>ruoyi-common-log</module>
<module>ruoyi-common-mail</module>
<module>ruoyi-common-mybatis</module>
@@ -31,10 +33,8 @@
<module>ruoyi-common-json</module>
<module>ruoyi-common-encrypt</module>
<module>ruoyi-common-tenant</module>
<module>ruoyi-common-chat</module>
<module>ruoyi-common-pay</module>
<module>ruoyi-common-wechat</module>
<module>ruoyi-common-live</module>
<module>ruoyi-common-websocket</module>
<module>ruoyi-common-sse</module>
</modules>
<artifactId>ruoyi-common</artifactId>

View File

@@ -14,7 +14,7 @@
</description>
<properties>
<revision>1.0.0</revision>
<revision>3.0.0</revision>
</properties>
<dependencyManagement>
@@ -26,6 +26,13 @@
<version>${revision}</version>
</dependency>
<!-- 对话模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-chat</artifactId>
<version>${revision}</version>
</dependency>
<!-- 接口模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
@@ -47,6 +54,13 @@
<version>${revision}</version>
</dependency>
<!-- 调度模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-job</artifactId>
<version>${revision}</version>
</dependency>
<!-- 日志记录 -->
<dependency>
<groupId>org.ruoyi</groupId>
@@ -110,6 +124,12 @@
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-social</artifactId>
<version>${revision}</version>
</dependency>
<!-- web服务 -->
<dependency>
<groupId>org.ruoyi</groupId>
@@ -152,35 +172,20 @@
<version>${revision}</version>
</dependency>
<!-- chat模块 -->
<!-- WebSocket模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-chat</artifactId>
<artifactId>ruoyi-common-websocket</artifactId>
<version>${revision}</version>
</dependency>
<!-- 微信模块 -->
<!-- SSE模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-wechat</artifactId>
<artifactId>ruoyi-common-sse</artifactId>
<version>${revision}</version>
</dependency>
<!-- AI绘画 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-chat</artifactId>
<version>${revision}</version>
</dependency>
<!-- 支付模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-pay</artifactId>
<version>${revision}</version>
</dependency>
</dependencies>
</dependencyManagement>

View File

@@ -6,113 +6,68 @@
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-common-chat</artifactId>
<description>
ruoyi-common-chat 模块
ruoyi-common-chat chat服务
</description>
<properties>
<retrofit2.version>2.9.0</retrofit2.version>
</properties>
<dependencies>
<!-- SpringWeb模块 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-core</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-ai-openai</artifactId>
<version>1.0.0-beta.12</version>
</dependency>
<dependency>
<groupId>io.github.ollama4j</groupId>
<artifactId>ollama4j</artifactId>
<version>1.0.79</version>
</dependency>
<!-- 序列化模块 -->
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-json</artifactId>
<version>1.0.0</version>
<artifactId>ruoyi-common-sse</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-redis</artifactId>
<artifactId>ruoyi-common-excel</artifactId>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<dependency>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-satoken</artifactId>
<artifactId>ruoyi-common-mybatis</artifactId>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>retrofit</artifactId>
<version>${retrofit2.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>converter-jackson</artifactId>
<version>${retrofit2.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.retrofit2</groupId>
<artifactId>adapter-rxjava2</artifactId>
<version>${retrofit2.version}</version>
<groupId>org.ruoyi</groupId>
<artifactId>ruoyi-common-tenant</artifactId>
</dependency>
<dependency>
<groupId>com.knuddels</groupId>
<artifactId>jtokkit</artifactId>
<version>0.5.0</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.12</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>cn.bigmodel.openapi</groupId>
<artifactId>oapi-java-sdk</artifactId>
<version>release-V4-2.3.0</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp</groupId>
<artifactId>okhttp</artifactId>
<version>2.7.5</version>
<scope>compile</scope>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations</artifactId>
<version>${swagger-annotations.version}</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,121 @@
package org.ruoyi.common.chat.base;
import cn.dev33.satoken.stp.StpUtil;
import org.apache.commons.lang3.StringUtils;
import org.ruoyi.common.chat.entity.User;
import org.ruoyi.common.chat.enums.UserStatusEnum;
import org.ruoyi.common.core.domain.model.LoginUser;
import org.ruoyi.common.core.exception.base.BaseException;
import org.ruoyi.common.satoken.utils.LoginHelper;
import static org.ruoyi.common.chat.enums.ErrorEnum.A_USER_NOT_FOUND;
/**
* 线程上下文适配器,统一接入 Sa-Token 登录态。
*/
public class ThreadContext {
private static final ThreadLocal<User> CURRENT_USER = new ThreadLocal<>();
private static final ThreadLocal<String> CURRENT_TOKEN = new ThreadLocal<>();
private ThreadContext() {
}
/**
* 获取当前登录的工作流用户。
*/
public static User getCurrentUser() {
User cached = CURRENT_USER.get();
if (cached != null) {
return cached;
}
LoginUser loginUser = LoginHelper.getLoginUser();
if (loginUser == null) {
throw new BaseException(A_USER_NOT_FOUND.getInfo());
}
User mapped = mapToWorkflowUser(loginUser);
CURRENT_USER.set(mapped);
return mapped;
}
/**
* 允许在测试或特殊场景下显式设置当前用户。
*/
public static void setCurrentUser(User user) {
if (user == null) {
CURRENT_USER.remove();
} else {
CURRENT_USER.set(user);
}
}
/**
* 获取当前登录用户 ID。
*/
public static Long getCurrentUserId() {
Long userId = LoginHelper.getUserId();
if (userId != null) {
return userId;
}
return getCurrentUser().getId();
}
/**
* 获取当前访问 token。
*/
public static String getToken() {
String token = CURRENT_TOKEN.get();
if (StringUtils.isNotBlank(token)) {
return token;
}
try {
token = StpUtil.getTokenValue();
} catch (Exception ignore) {
token = null;
}
if (StringUtils.isNotBlank(token)) {
CURRENT_TOKEN.set(token);
}
return token;
}
public static void setToken(String token) {
if (StringUtils.isBlank(token)) {
CURRENT_TOKEN.remove();
} else {
CURRENT_TOKEN.set(token);
}
}
public static boolean isLogin() {
return LoginHelper.isLogin();
}
public static User getExistCurrentUser() {
return getCurrentUser();
}
public static void unload() {
CURRENT_USER.remove();
CURRENT_TOKEN.remove();
}
private static User mapToWorkflowUser(LoginUser loginUser) {
User user = new User();
user.setId(loginUser.getUserId());
user.setName(loginUser.getUsername());
user.setEmail(loginUser.getUsername());
user.setUuid(String.valueOf(loginUser.getUserId()));
user.setUserStatus(UserStatusEnum.NORMAL);
user.setIsAdmin(LoginHelper.isSuperAdmin(loginUser.getUserId()));
user.setUnderstandContextMsgPairNum(0);
user.setQuotaByTokenDaily(0);
user.setQuotaByTokenMonthly(0);
user.setQuotaByRequestDaily(0);
user.setQuotaByRequestMonthly(0);
user.setQuotaByImageDaily(0);
user.setQuotaByImageMonthly(0);
user.setIsDeleted(false);
return user;
}
}

View File

@@ -1,56 +0,0 @@
package org.ruoyi.common.chat.config;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import org.ruoyi.common.chat.openai.OpenAiStreamClient;
import org.ruoyi.common.chat.openai.function.KeyRandomStrategy;
import org.ruoyi.common.chat.openai.interceptor.OpenAILogger;
import org.ruoyi.common.core.service.ConfigService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
/**
* Chat配置类
*
* @date: 2023/5/16
*/
@Configuration
@RequiredArgsConstructor
public class ChatConfig {
@Getter
private OpenAiStreamClient openAiStreamClient;
private final ConfigService configService;
// 重启才会生效
@Bean
public OpenAiStreamClient openAiStreamClient() {
String apiHost = configService.getConfigValue("chat", "apiHost");
String apiKey = configService.getConfigValue("chat", "apiKey");
openAiStreamClient = createOpenAiStreamClient(apiHost,apiKey);
return openAiStreamClient;
}
public OpenAiStreamClient createOpenAiStreamClient(String apiHost, String apiKey) {
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(new OpenAILogger());
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.addInterceptor(httpLoggingInterceptor)
.connectTimeout(30, TimeUnit.SECONDS)
.writeTimeout(600, TimeUnit.SECONDS)
.readTimeout(600, TimeUnit.SECONDS)
.build();
return OpenAiStreamClient.builder()
.apiHost(apiHost)
.apiKey(Collections.singletonList(apiKey))
.keyStrategy(new KeyRandomStrategy())
.okHttpClient(okHttpClient)
.build();
}
}

View File

@@ -1,36 +0,0 @@
package org.ruoyi.common.chat.config;
import cn.hutool.cache.CacheUtil;
import cn.hutool.cache.impl.TimedCache;
import cn.hutool.core.date.DateUnit;
import lombok.extern.slf4j.Slf4j;
/**
* 描述:
*
* @author https:www.unfbx.com
* @date 2023-03-10
*/
@Slf4j
public class LocalCache {
/**
* 缓存时长
*/
public static final long TIMEOUT = 30 * DateUnit.MINUTE.getMillis();
/**
* 清理间隔
*/
private static final long CLEAN_TIMEOUT = 30 * DateUnit.MINUTE.getMillis();
/**
* 缓存对象
*/
public static final TimedCache<String, Object> CACHE = CacheUtil.newTimedCache(TIMEOUT);
static {
//启动定时任务
CACHE.schedulePrune(CLEAN_TIMEOUT);
}
}

View File

@@ -1,15 +0,0 @@
package org.ruoyi.common.chat.constant;
/**
* 描述:
*
* @author https:www.unfbx.com
* @since 2023-03-06
*/
public class OpenAIConst {
public final static String OPENAI_HOST = "https://api.openai.com/";
public final static int SUCCEED_CODE = 200;
}

View File

@@ -1,73 +0,0 @@
package org.ruoyi.common.chat.demo;
import cn.hutool.json.JSONUtil;
import lombok.Getter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.sse.EventSource;
import okhttp3.sse.EventSourceListener;
import org.ruoyi.common.chat.entity.chat.ChatCompletionResponse;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
/**
* 描述: sse
*
* @author https:www.unfbx.com
* 2023-06-15
*/
@Slf4j
public class ConsoleEventSourceListenerV2 extends EventSourceListener {
@Getter
String args = "";
final CountDownLatch countDownLatch;
public ConsoleEventSourceListenerV2(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void onOpen(EventSource eventSource, Response response) {
log.info("OpenAI建立sse连接...");
}
@Override
public void onEvent(EventSource eventSource, String id, String type, String data) {
log.info("OpenAI返回数据{}", data);
if (data.equals("[DONE]")) {
log.info("OpenAI返回数据结束了");
countDownLatch.countDown();
return;
}
ChatCompletionResponse chatCompletionResponse = JSONUtil.toBean(data, ChatCompletionResponse.class);
if(Objects.nonNull(chatCompletionResponse.getChoices().get(0).getDelta().getFunctionCall())){
args += chatCompletionResponse.getChoices().get(0).getDelta().getFunctionCall().getArguments();
}
}
@Override
public void onClosed(EventSource eventSource) {
log.info("OpenAI关闭sse连接...");
}
@SneakyThrows
@Override
public void onFailure(EventSource eventSource, Throwable t, Response response) {
if(Objects.isNull(response)){
log.error("OpenAI sse连接异常:{}", t);
eventSource.cancel();
return;
}
ResponseBody body = response.body();
if (Objects.nonNull(body)) {
log.error("OpenAI sse连接异常data{},异常:{}", body.string(), t);
} else {
log.error("OpenAI sse连接异常data{},异常:{}", response, t);
}
eventSource.cancel();
}
}

View File

@@ -1,92 +0,0 @@
package org.ruoyi.common.chat.demo;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import lombok.Getter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okhttp3.sse.EventSource;
import okhttp3.sse.EventSourceListener;
import org.ruoyi.common.chat.entity.chat.ChatCompletionResponse;
import org.ruoyi.common.chat.entity.chat.Message;
import org.ruoyi.common.chat.entity.chat.tool.ToolCallFunction;
import org.ruoyi.common.chat.entity.chat.tool.ToolCalls;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
/**
* 描述: demo测试实现类仅供思路参考
*
* @author https:www.unfbx.com
* 2023-11-12
*/
@Slf4j
public class ConsoleEventSourceListenerV3 extends EventSourceListener {
@Getter
List<ToolCalls> choices = new ArrayList<>();
@Getter
ToolCalls toolCalls = new ToolCalls();
@Getter
ToolCallFunction toolCallFunction = ToolCallFunction.builder().name("").arguments("").build();
final CountDownLatch countDownLatch;
public ConsoleEventSourceListenerV3(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void onOpen(EventSource eventSource, Response response) {
log.info("OpenAI建立sse连接...");
}
@Override
public void onEvent(EventSource eventSource, String id, String type, String data) {
log.info("OpenAI返回数据{}", data);
if (data.equals("[DONE]")) {
log.info("OpenAI返回数据结束了");
return;
}
ChatCompletionResponse chatCompletionResponse = JSONUtil.toBean(data, ChatCompletionResponse.class);
Message delta = chatCompletionResponse.getChoices().get(0).getDelta();
if (CollectionUtil.isNotEmpty(delta.getToolCalls())) {
choices.addAll(delta.getToolCalls());
}
}
@Override
public void onClosed(EventSource eventSource) {
if(CollectionUtil.isNotEmpty(choices)){
toolCalls.setId(choices.get(0).getId());
toolCalls.setType(choices.get(0).getType());
choices.forEach(e -> {
toolCallFunction.setName(e.getFunction().getName());
toolCallFunction.setArguments(toolCallFunction.getArguments() + e.getFunction().getArguments());
toolCalls.setFunction(toolCallFunction);
});
}
log.info("OpenAI关闭sse连接...");
countDownLatch.countDown();
}
@SneakyThrows
@Override
public void onFailure(EventSource eventSource, Throwable t, Response response) {
if(Objects.isNull(response)){
log.error("OpenAI sse连接异常:{}", t);
eventSource.cancel();
return;
}
ResponseBody body = response.body();
if (Objects.nonNull(body)) {
log.error("OpenAI sse连接异常data{},异常:{}", body.string(), t);
} else {
log.error("OpenAI sse连接异常data{},异常:{}", response, t);
}
eventSource.cancel();
}
}

View File

@@ -1,417 +0,0 @@
package org.ruoyi.common.chat.demo;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import org.junit.Before;
import org.junit.Test;
import org.ruoyi.common.chat.entity.chat.*;
import org.ruoyi.common.chat.entity.chat.tool.ToolCallFunction;
import org.ruoyi.common.chat.entity.chat.tool.ToolCalls;
import org.ruoyi.common.chat.entity.chat.tool.Tools;
import org.ruoyi.common.chat.entity.chat.tool.ToolsFunction;
import org.ruoyi.common.chat.openai.OpenAiClient;
import org.ruoyi.common.chat.openai.OpenAiStreamClient;
import org.ruoyi.common.chat.openai.function.KeyRandomStrategy;
import org.ruoyi.common.chat.openai.interceptor.DynamicKeyOpenAiAuthInterceptor;
import org.ruoyi.common.chat.openai.interceptor.OpenAILogger;
import org.ruoyi.common.chat.openai.interceptor.OpenAiResponseInterceptor;
import org.ruoyi.common.chat.openai.plugin.PluginAbstract;
import org.ruoyi.common.chat.plugin.CmdPlugin;
import org.ruoyi.common.chat.plugin.CmdReq;
import org.ruoyi.common.chat.sse.ConsoleEventSourceListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* 描述:
*
* @author ageerle@163.com
* date 2025/3/8
*/
@Slf4j
public class PluginTest {
private OpenAiClient openAiClient;
private OpenAiStreamClient openAiStreamClient;
@Before
public void before() {
//可以为null
// Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", 7890));
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(new OpenAILogger());
//千万别再生产或者测试环境打开BODY级别日志
//生产或者测试环境建议设置为这三种级别NONE,BASIC,HEADERS,
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
OkHttpClient okHttpClient = new OkHttpClient
.Builder()
// .proxy(proxy)
.addInterceptor(httpLoggingInterceptor)
.addInterceptor(new OpenAiResponseInterceptor())
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build();
openAiClient = OpenAiClient.builder()
//支持多key传入请求时候随机选择
.apiKey(Arrays.asList("sk-xx"))
//自定义key的获取策略默认KeyRandomStrategy
//.keyStrategy(new KeyRandomStrategy())
.keyStrategy(new KeyRandomStrategy())
.okHttpClient(okHttpClient)
//自己做了代理就传代理地址,没有可不不传,(关注公众号回复openai ,获取免费的测试代理地址)
.apiHost("https://api.pandarobot.chat/")
.build();
openAiStreamClient = OpenAiStreamClient.builder()
//支持多key传入请求时候随机选择
.apiKey(Arrays.asList("sk-xx"))
//自定义key的获取策略默认KeyRandomStrategy
.keyStrategy(new KeyRandomStrategy())
.authInterceptor(new DynamicKeyOpenAiAuthInterceptor())
.okHttpClient(okHttpClient)
//自己做了代理就传代理地址,没有可不不传,(关注公众号回复openai ,获取免费的测试代理地址)
.apiHost("https://api.pandarobot.chat/")
.build();
}
@Test
public void chatFunction() {
//模型GPT_3_5_TURBO_16K_0613
Message message = Message.builder().role(Message.Role.USER).content("给我输出一个长度为2的中文词语并解释下词语对应物品的用途").build();
//属性一
JSONObject wordLength = new JSONObject();
wordLength.put("type", "number");
wordLength.put("description", "词语的长度");
//属性二
JSONObject language = new JSONObject();
language.put("type", "string");
language.put("enum", Arrays.asList("zh", "en"));
language.put("description", "语言类型例如zh代表中文、en代表英语");
//参数
JSONObject properties = new JSONObject();
properties.put("wordLength", wordLength);
properties.put("language", language);
Parameters parameters = Parameters.builder()
.type("object")
.properties(properties)
.required(Collections.singletonList("wordLength")).build();
Functions functions = Functions.builder()
.name("getOneWord")
.description("获取一个指定长度和语言类型的词语")
.parameters(parameters)
.build();
ChatCompletion chatCompletion = ChatCompletion
.builder()
.messages(Collections.singletonList(message))
.functions(Collections.singletonList(functions))
.functionCall("auto")
.model(ChatCompletion.Model.GPT_4_1106_PREVIEW.getName())
.build();
ChatCompletionResponse chatCompletionResponse = openAiClient.chatCompletion(chatCompletion);
ChatChoice chatChoice = chatCompletionResponse.getChoices().get(0);
log.info("构造的方法值:{}", chatChoice.getMessage().getFunctionCall());
log.info("构造的方法名称:{}", chatChoice.getMessage().getFunctionCall().getName());
log.info("构造的方法参数:{}", chatChoice.getMessage().getFunctionCall().getArguments());
WordParam wordParam = JSONUtil.toBean(chatChoice.getMessage().getFunctionCall().getArguments(), WordParam.class);
String oneWord = getOneWord(wordParam);
FunctionCall functionCall = FunctionCall.builder()
.arguments(chatChoice.getMessage().getFunctionCall().getArguments())
.name("getOneWord")
.build();
Message message2 = Message.builder().role(Message.Role.ASSISTANT).content("方法参数").functionCall(functionCall).build();
String content
= "{ " +
"\"wordLength\": \"3\", " +
"\"language\": \"zh\", " +
"\"word\": \"" + oneWord + "\"," +
"\"用途\": [\"直接吃\", \"做沙拉\", \"售卖\"]" +
"}";
Message message3 = Message.builder().role(Message.Role.FUNCTION).name("getOneWord").content(content).build();
List<Message> messageList = Arrays.asList(message, message2, message3);
ChatCompletion chatCompletionV2 = ChatCompletion
.builder()
.messages(messageList)
.model(ChatCompletion.Model.GPT_4_1106_PREVIEW.getName())
.build();
ChatCompletionResponse chatCompletionResponseV2 = openAiClient.chatCompletion(chatCompletionV2);
log.info("自定义的方法返回值:{}",chatCompletionResponseV2.getChoices().get(0).getMessage().getContent());
}
@Test
public void plugin() {
CmdPlugin plugin = new CmdPlugin(CmdReq.class);
// 插件名称
plugin.setName("命令行工具");
// 方法名称
plugin.setFunction("openCmd");
// 方法说明
plugin.setDescription("提供一个命令行指令,比如<记事本>,指令使用中文,以function返回结果为准");
PluginAbstract.Arg arg = new PluginAbstract.Arg();
// 参数名称
arg.setName("cmd");
// 参数说明
arg.setDescription("命令行指令");
// 参数类型
arg.setType("string");
arg.setRequired(true);
plugin.setArgs(Collections.singletonList(arg));
Message message2 = Message.builder().role(Message.Role.USER).content("帮我打开计算器,结合上下文判断指令是否执行成功,只用回复成功或者失败").build();
List<Message> messages = new ArrayList<>();
messages.add(message2);
//有四个重载方法,都可以使用
ChatCompletionResponse response = openAiClient.chatCompletionWithPlugin(messages,"gpt-4o-mini",plugin);
log.info("自定义的方法返回值:{}", response.getChoices().get(0).getMessage().getContent());
}
/**
* 自定义返回数据格式
*/
@Test
public void diyReturnDataModelChat() {
Message message = Message.builder().role(Message.Role.USER).content("随机输出10个单词使用json输出").build();
ChatCompletion chatCompletion = ChatCompletion
.builder()
.messages(Collections.singletonList(message))
.responseFormat(ResponseFormat.builder().type(ResponseFormat.Type.JSON_OBJECT.getName()).build())
.model(ChatCompletion.Model.GPT_4_1106_PREVIEW.getName())
.build();
ChatCompletionResponse chatCompletionResponse = openAiClient.chatCompletion(chatCompletion);
chatCompletionResponse.getChoices().forEach(e -> System.out.println(e.getMessage()));
}
@Test
public void streamPlugin() {
WeatherPlugin plugin = new WeatherPlugin(WeatherReq.class);
plugin.setName("知心天气");
plugin.setFunction("getLocationWeather");
plugin.setDescription("提供一个地址,方法将会获取该地址的天气的实时温度信息。");
PluginAbstract.Arg arg = new PluginAbstract.Arg();
arg.setName("location");
arg.setDescription("地名");
arg.setType("string");
arg.setRequired(true);
plugin.setArgs(Collections.singletonList(arg));
// Message message1 = Message.builder().role(Message.Role.USER).content("秦始皇统一了哪六国。").build();
Message message2 = Message.builder().role(Message.Role.USER).content("获取上海市的天气现在多少度然后再给出3个推荐的户外运动。").build();
List<Message> messages = new ArrayList<>();
// messages.add(message1);
messages.add(message2);
//默认模型GPT_3_5_TURBO_16K_0613
//有四个重载方法,都可以使用
openAiStreamClient.streamChatCompletionWithPlugin(messages, ChatCompletion.Model.GPT_4_1106_PREVIEW.getName(), new ConsoleEventSourceListener(), plugin);
CountDownLatch countDownLatch = new CountDownLatch(1);
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* tools使用示例
*/
@Test
public void toolsChat() {
Message message = Message.builder().role(Message.Role.USER).content("给我输出一个长度为2的中文词语并解释下词语对应物品的用途").build();
//属性一
JSONObject wordLength = new JSONObject();
wordLength.put("type", "number");
wordLength.put("description", "词语的长度");
//属性二
JSONObject language = new JSONObject();
language.put("type", "string");
language.put("enum", Arrays.asList("zh", "en"));
language.put("description", "语言类型例如zh代表中文、en代表英语");
//参数
JSONObject properties = new JSONObject();
properties.put("wordLength", wordLength);
properties.put("language", language);
Parameters parameters = Parameters.builder()
.type("object")
.properties(properties)
.required(Collections.singletonList("wordLength")).build();
Tools tools = Tools.builder()
.type(Tools.Type.FUNCTION.getName())
.function(ToolsFunction.builder().name("getOneWord").description("获取一个指定长度和语言类型的词语").parameters(parameters).build())
.build();
ChatCompletion chatCompletion = ChatCompletion
.builder()
.messages(Collections.singletonList(message))
.tools(Collections.singletonList(tools))
.model(ChatCompletion.Model.GPT_4_1106_PREVIEW.getName())
.build();
ChatCompletionResponse chatCompletionResponse = openAiClient.chatCompletion(chatCompletion);
ChatChoice chatChoice = chatCompletionResponse.getChoices().get(0);
log.info("构造的方法值:{}", chatChoice.getMessage().getToolCalls());
ToolCalls openAiReturnToolCalls = chatChoice.getMessage().getToolCalls().get(0);
WordParam wordParam = JSONUtil.toBean(openAiReturnToolCalls.getFunction().getArguments(), WordParam.class);
String oneWord = getOneWord(wordParam);
ToolCallFunction tcf = ToolCallFunction.builder().name("getOneWord").arguments(openAiReturnToolCalls.getFunction().getArguments()).build();
ToolCalls tc = ToolCalls.builder().id(openAiReturnToolCalls.getId()).type(ToolCalls.Type.FUNCTION.getName()).function(tcf).build();
//构造tool call
Message message2 = Message.builder().role(Message.Role.ASSISTANT).content("方法参数").toolCalls(Collections.singletonList(tc)).build();
String content
= "{ " +
"\"wordLength\": \"3\", " +
"\"language\": \"zh\", " +
"\"word\": \"" + oneWord + "\"," +
"\"用途\": [\"直接吃\", \"做沙拉\", \"售卖\"]" +
"}";
Message message3 = Message.builder().toolCallId(openAiReturnToolCalls.getId()).role(Message.Role.TOOL).name("getOneWord").content(content).build();
List<Message> messageList = Arrays.asList(message, message2, message3);
ChatCompletion chatCompletionV2 = ChatCompletion
.builder()
.messages(messageList)
.model(ChatCompletion.Model.GPT_4_1106_PREVIEW.getName())
.build();
ChatCompletionResponse chatCompletionResponseV2 = openAiClient.chatCompletion(chatCompletionV2);
log.info("自定义的方法返回值:{}", chatCompletionResponseV2.getChoices().get(0).getMessage().getContent());
}
/**
* tools流式输出使用示例
*/
@Test
public void streamToolsChat() {
CountDownLatch countDownLatch = new CountDownLatch(1);
ConsoleEventSourceListenerV3 eventSourceListener = new ConsoleEventSourceListenerV3(countDownLatch);
Message message = Message.builder().role(Message.Role.USER).content("给我输出一个长度为2的中文词语并解释下词语对应物品的用途").build();
//属性一
JSONObject wordLength = new JSONObject();
wordLength.put("type", "number");
wordLength.put("description", "词语的长度");
//属性二
JSONObject language = new JSONObject();
language.put("type", "string");
language.put("enum", Arrays.asList("zh", "en"));
language.put("description", "语言类型例如zh代表中文、en代表英语");
//参数
JSONObject properties = new JSONObject();
properties.put("wordLength", wordLength);
properties.put("language", language);
Parameters parameters = Parameters.builder()
.type("object")
.properties(properties)
.required(Collections.singletonList("wordLength")).build();
Tools tools = Tools.builder()
.type(Tools.Type.FUNCTION.getName())
.function(ToolsFunction.builder().name("getOneWord").description("获取一个指定长度和语言类型的词语").parameters(parameters).build())
.build();
ChatCompletion chatCompletion = ChatCompletion
.builder()
.messages(Collections.singletonList(message))
.tools(Collections.singletonList(tools))
.model(ChatCompletion.Model.GPT_4_1106_PREVIEW.getName())
.build();
openAiStreamClient.streamChatCompletion(chatCompletion, eventSourceListener);
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
ToolCalls openAiReturnToolCalls = eventSourceListener.getToolCalls();
WordParam wordParam = JSONUtil.toBean(openAiReturnToolCalls.getFunction().getArguments(), WordParam.class);
String oneWord = getOneWord(wordParam);
ToolCallFunction tcf = ToolCallFunction.builder().name("getOneWord").arguments(openAiReturnToolCalls.getFunction().getArguments()).build();
ToolCalls tc = ToolCalls.builder().id(openAiReturnToolCalls.getId()).type(ToolCalls.Type.FUNCTION.getName()).function(tcf).build();
//构造tool call
Message message2 = Message.builder().role(Message.Role.ASSISTANT).content("方法参数").toolCalls(Collections.singletonList(tc)).build();
String content
= "{ " +
"\"wordLength\": \"3\", " +
"\"language\": \"zh\", " +
"\"word\": \"" + oneWord + "\"," +
"\"用途\": [\"直接吃\", \"做沙拉\", \"售卖\"]" +
"}";
Message message3 = Message.builder().toolCallId(openAiReturnToolCalls.getId()).role(Message.Role.TOOL).name("getOneWord").content(content).build();
List<Message> messageList = Arrays.asList(message, message2, message3);
ChatCompletion chatCompletionV2 = ChatCompletion
.builder()
.messages(messageList)
.model(ChatCompletion.Model.GPT_4_1106_PREVIEW.getName())
.build();
CountDownLatch countDownLatch1 = new CountDownLatch(1);
openAiStreamClient.streamChatCompletion(chatCompletionV2, new ConsoleEventSourceListenerV3(countDownLatch));
try {
countDownLatch1.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
countDownLatch1.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Data
@Builder
static class WordParam {
private int wordLength;
@Builder.Default
private String language = "zh";
}
/**
* 获取一个词语(根据语言和字符长度查询)
* @param wordParam
* @return
*/
public String getOneWord(WordParam wordParam) {
List<String> zh = Arrays.asList("大香蕉", "哈密瓜", "苹果");
List<String> en = Arrays.asList("apple", "banana", "cantaloupe");
if (wordParam.getLanguage().equals("zh")) {
for (String e : zh) {
if (e.length() == wordParam.getWordLength()) {
return e;
}
}
}
if (wordParam.getLanguage().equals("en")) {
for (String e : en) {
if (e.length() == wordParam.getWordLength()) {
return e;
}
}
}
return "西瓜";
}
}

View File

@@ -1,24 +0,0 @@
package org.ruoyi.common.chat.demo;
import org.ruoyi.common.chat.openai.plugin.PluginAbstract;
public class WeatherPlugin extends PluginAbstract<WeatherReq, WeatherResp> {
public WeatherPlugin(Class<?> r) {
super(r);
}
@Override
public WeatherResp func(WeatherReq args) {
WeatherResp weatherResp = new WeatherResp();
weatherResp.setTemp("25到28摄氏度");
weatherResp.setLevel(3);
return weatherResp;
}
@Override
public String content(WeatherResp weatherResp) {
return "当前天气温度:" + weatherResp.getTemp() + ",风力等级:" + weatherResp.getLevel();
}
}

View File

@@ -1,13 +0,0 @@
package org.ruoyi.common.chat.demo;
import lombok.Data;
import org.ruoyi.common.chat.openai.plugin.PluginParam;
@Data
public class WeatherReq extends PluginParam {
/**
* 城市
*/
private String location;
}

View File

@@ -1,15 +0,0 @@
package org.ruoyi.common.chat.demo;
import lombok.Data;
@Data
public class WeatherResp {
/**
* 温度
*/
private String temp;
/**
* 风力等级
*/
private Integer level;
}

View File

@@ -1,223 +0,0 @@
package org.ruoyi.common.chat.demo;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zhipu.oapi.ClientV4;
import com.zhipu.oapi.Constants;
import com.zhipu.oapi.service.v4.tools.*;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import com.zhipu.oapi.service.v4.model.*;
import io.reactivex.Flowable;
import java.util.HashMap;
import java.util.Map;
public class WebSearchToolsTest {
private final static Logger logger = LoggerFactory.getLogger(WebSearchToolsTest.class);
private static final String API_SECRET_KEY = "xx";
private static final ClientV4 client = new ClientV4.Builder(API_SECRET_KEY)
.networkConfig(300, 100, 100, 100, TimeUnit.SECONDS)
.connectionPool(new okhttp3.ConnectionPool(8, 1, TimeUnit.SECONDS))
.build();
private static final ObjectMapper mapper = new ObjectMapper();
// 请自定义自己的业务id
private static final String requestIdTemplate = "mycompany-%d";
@Test
public void test1() throws JsonProcessingException {
// json 转换 ArrayList<SearchChatMessage>
String jsonString = "[\n" +
" {\n" +
" \"content\": \"今天武汉天气怎么样\",\n" +
" \"role\": \"user\"\n" +
" }\n" +
" ]";
ArrayList<SearchChatMessage> messages = new ObjectMapper().readValue(jsonString, new TypeReference<ArrayList<SearchChatMessage>>() {
});
String requestId = String.format(requestIdTemplate, System.currentTimeMillis());
WebSearchParamsRequest chatCompletionRequest = WebSearchParamsRequest.builder()
.model("web-search-pro")
.stream(Boolean.TRUE)
.messages(messages)
.requestId(requestId)
.build();
WebSearchApiResponse webSearchApiResponse = client.webSearchProStreamingInvoke(chatCompletionRequest);
if (webSearchApiResponse.isSuccess()) {
AtomicBoolean isFirst = new AtomicBoolean(true);
List<ChoiceDelta> choices = new ArrayList<>();
AtomicReference<WebSearchPro> lastAccumulator = new AtomicReference<>();
webSearchApiResponse.getFlowable().map(result -> result)
.doOnNext(accumulator -> {
{
if (isFirst.getAndSet(false)) {
logger.info("Response: ");
}
ChoiceDelta delta = accumulator.getChoices().get(0).getDelta();
if (delta != null && delta.getToolCalls() != null) {
logger.info("tool_calls: {}", mapper.writeValueAsString(delta.getToolCalls()));
}
choices.add(delta);
lastAccumulator.set(accumulator);
}
})
.doOnComplete(() -> System.out.println("Stream completed."))
.doOnError(throwable -> System.err.println("Error: " + throwable)) // Handle errors
.blockingSubscribe();// Use blockingSubscribe instead of blockingGet()
WebSearchPro chatMessageAccumulator = lastAccumulator.get();
webSearchApiResponse.setFlowable(null);// 打印前置空
webSearchApiResponse.setData(chatMessageAccumulator);
}
logger.info("model output: {}", mapper.writeValueAsString(webSearchApiResponse));
client.getConfig().getHttpClient().dispatcher().executorService().shutdown();
client.getConfig().getHttpClient().connectionPool().evictAll();
// List all active threads
for (Thread t : Thread.getAllStackTraces().keySet()) {
logger.info("Thread: " + t.getName() + " State: " + t.getState());
}
}
@Test
public void test2() throws JsonProcessingException {
// json 转换 ArrayList<SearchChatMessage>
String jsonString = "[\n" +
" {\n" +
" \"content\": \"今天天气怎么样\",\n" +
" \"role\": \"user\"\n" +
" }\n" +
" ]";
ArrayList<SearchChatMessage> messages = new ObjectMapper().readValue(jsonString, new TypeReference<ArrayList<SearchChatMessage>>() {
});
String requestId = String.format(requestIdTemplate, System.currentTimeMillis());
WebSearchParamsRequest chatCompletionRequest = WebSearchParamsRequest.builder()
.model("web-search-pro")
.stream(Boolean.FALSE)
.messages(messages)
.requestId(requestId)
.build();
WebSearchApiResponse webSearchApiResponse = client.invokeWebSearchPro(chatCompletionRequest);
logger.info("model output: {}", mapper.writeValueAsString(webSearchApiResponse));
}
@Test
public void testFunctionSSE() throws JsonProcessingException {
List<ChatMessage> messages = new ArrayList<>();
ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), "成都到北京要多久,天气如何");
messages.add(chatMessage);
String requestId = String.format(requestIdTemplate, System.currentTimeMillis());
// 函数调用参数构建部分
List<ChatTool> chatToolList = new ArrayList<>();
ChatTool chatTool = new ChatTool();
chatTool.setType(ChatToolType.FUNCTION.value());
ChatFunctionParameters chatFunctionParameters = new ChatFunctionParameters();
chatFunctionParameters.setType("object");
Map<String, Object> properties = new HashMap<>();
properties.put("location", new HashMap<String, Object>() {{
put("type", "string");
put("description", "城市,如:北京");
}});
properties.put("unit", new HashMap<String, Object>() {{
put("type", "string");
put("enum", new ArrayList<String>() {{
add("celsius");
add("fahrenheit");
}});
}});
chatFunctionParameters.setProperties(properties);
ChatFunction chatFunction = ChatFunction.builder()
.name("get_weather")
.description("Get the current weather of a location")
.parameters(chatFunctionParameters)
.build();
chatTool.setFunction(chatFunction);
chatToolList.add(chatTool);
HashMap<String, Object> extraJson = new HashMap<>();
extraJson.put("temperature", 0.5);
extraJson.put("max_tokens", 50);
ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder()
.model(Constants.ModelChatGLM4)
.stream(Boolean.TRUE)
.messages(messages)
.requestId(requestId)
.tools(chatToolList)
.toolChoice("auto")
.extraJson(extraJson)
.build();
ModelApiResponse sseModelApiResp = client.invokeModelApi(chatCompletionRequest);
if (sseModelApiResp.isSuccess()) {
AtomicBoolean isFirst = new AtomicBoolean(true);
List<Choice> choices = new ArrayList<>();
ChatMessageAccumulator chatMessageAccumulator = mapStreamToAccumulator(sseModelApiResp.getFlowable())
.doOnNext(accumulator -> {
{
if (isFirst.getAndSet(false)) {
logger.info("Response: ");
}
if (accumulator.getDelta() != null && accumulator.getDelta().getTool_calls() != null) {
String jsonString = mapper.writeValueAsString(accumulator.getDelta().getTool_calls());
logger.info("tool_calls: {}", jsonString);
}
if (accumulator.getDelta() != null && accumulator.getDelta().getContent() != null) {
logger.info(accumulator.getDelta().getContent());
}
choices.add(accumulator.getChoice());
}
})
.doOnComplete(System.out::println)
.lastElement()
.blockingGet();
ModelData data = new ModelData();
data.setChoices(choices);
data.setUsage(chatMessageAccumulator.getUsage());
data.setId(chatMessageAccumulator.getId());
data.setCreated(chatMessageAccumulator.getCreated());
data.setRequestId(chatCompletionRequest.getRequestId());
sseModelApiResp.setFlowable(null);// 打印前置空
sseModelApiResp.setData(data);
}
logger.info("model output: {}", mapper.writeValueAsString(sseModelApiResp));
}
public static Flowable<ChatMessageAccumulator> mapStreamToAccumulator(Flowable<ModelData> flowable) {
return flowable.map(chunk -> {
return new ChatMessageAccumulator(chunk.getChoices().get(0).getDelta(), null, chunk.getChoices().get(0), chunk.getUsage(), chunk.getCreated(), chunk.getId());
});
}
}

View File

@@ -1,20 +1,20 @@
package org.ruoyi.system.domain.bo;
package org.ruoyi.common.chat.domain.bo.chat;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.ruoyi.common.chat.entity.chat.ChatMessage;
import org.ruoyi.common.core.validate.AddGroup;
import org.ruoyi.common.core.validate.EditGroup;
import org.ruoyi.common.mybatis.core.domain.BaseEntity;
import org.ruoyi.system.domain.ChatMessage;
/**
* 聊天消息业务对象 chat_message
*
* @author Lion Li
* @date 2023-11-26
* @author ageerle
* @date 2025-12-14
*/
@Data
@EqualsAndHashCode(callSuper = true)
@@ -28,35 +28,30 @@ public class ChatMessageBo extends BaseEntity {
private Long id;
/**
* 用户ID
* 会话id
*/
private Long sessionId;
/**
* 用户id
*/
@NotNull(message = "用户id不能为空", groups = { AddGroup.class, EditGroup.class })
private Long userId;
/**
* 用户名称
* 消息内容
*/
private String userName;
private String content;
/**
* 对话角色
*/
private String role;
/**
* 消息内容
*/
@NotBlank(message = "消息内容不能为空", groups = { AddGroup.class, EditGroup.class })
private String content;
/**
* 扣除费用
*/
private Double deductCost;
/**
* 累计 Tokens
*/
private Integer totalTokens;
private Long totalTokens;
/**
* 模型名称
@@ -68,4 +63,5 @@ public class ChatMessageBo extends BaseEntity {
*/
private String remark;
}

View File

@@ -0,0 +1,74 @@
package org.ruoyi.common.chat.domain.bo.chat;
import io.github.linpeilie.annotations.AutoMapper;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.ruoyi.common.chat.entity.chat.ChatModel;
import org.ruoyi.common.core.validate.EditGroup;
import org.ruoyi.common.mybatis.core.domain.BaseEntity;
/**
* 模型管理业务对象 chat_model
*
* @author ageerle
* @date 2025-12-14
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = ChatModel.class, reverseConvertGenerate = false)
public class ChatModelBo extends BaseEntity {
/**
* 主键
*/
@NotNull(message = "主键不能为空", groups = { EditGroup.class })
private Long id;
/**
* 模型分类
*/
private String category;
/**
* 模型名称
*/
private String modelName;
/**
* 模型供应商
*/
private String providerCode;
/**
* 模型描述
*/
private String modelDescribe;
/**
* 是否显示
*/
private String modelShow;
/**
* 向量维度
*/
private Integer modelDimension;
/**
* 请求地址
*/
private String apiHost;
/**
* 密钥
*/
private String apiKey;
/**
* 备注
*/
private String remark;
}

View File

@@ -0,0 +1,45 @@
package org.ruoyi.common.chat.domain.dto;
import lombok.Data;
/**
* 聊天消息DTO - 用于上下文传递
*
* @author ageerle@163.com
* @date 2025/12/13
*/
@Data
public class ChatMessageDTO {
/**
* 消息角色: system/user/assistant
*/
private String role;
/**
* 消息内容
*/
private String content;
public static ChatMessageDTO system(String content) {
ChatMessageDTO msg = new ChatMessageDTO();
msg.role = "system";
msg.content = content;
return msg;
}
public static ChatMessageDTO user(String content) {
ChatMessageDTO msg = new ChatMessageDTO();
msg.role = "user";
msg.content = content;
return msg;
}
public static ChatMessageDTO assistant(String content) {
ChatMessageDTO msg = new ChatMessageDTO();
msg.role = "assistant";
msg.content = content;
return msg;
}
}

View File

@@ -0,0 +1,89 @@
package org.ruoyi.common.chat.domain.dto.request;
import dev.langchain4j.data.message.ChatMessage;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import org.ruoyi.common.chat.domain.dto.ChatMessageDTO;
import java.util.List;
/**
* 对话请求对象
*
* @author ageerle
* @date 2023-04-08
*/
@Data
public class ChatRequest {
@NotEmpty(message = "对话消息不能为空")
private List<ChatMessageDTO> messages;
@NotEmpty(message = "传入的模型不能为空")
private String model;
/**
* 工作流请求体
*/
private WorkFlowRunner workFlowRunner;
/**
* 人机交互信息体
*/
private ReSumeRunner reSumeRunner;
/**
* 是否启用工作流
*/
private Boolean enableWorkFlow;
/**
* 会话id
*/
private Long sessionId;
/**
* 应用ID
*/
private String appId;
/**
* 知识库id
*/
private String knowledgeId;
/**
* 对话id(每个聊天窗口都不一样)
*/
private Long uuid;
/**
* 是否为人机交互用户继续输入
*/
private Boolean isResume;
/**
* 是否启用深度思考
*/
private Boolean enableThinking;
/**
* 是否自动切换模型
*/
private Boolean autoSelectModel;
/**
* 是否支持联网
*/
private Boolean enableInternet;
/**
* 会话令牌为避免在非Web线程中获取Request入口处注入
*/
private String token;
/**
* 原生对话对象
*/
private List<ChatMessage> chatMessages;
}

View File

@@ -0,0 +1,19 @@
package org.ruoyi.common.chat.domain.dto.request;
import lombok.Data;
/**
* 人机交互输入信息
*/
@Data
public class ReSumeRunner {
/**
* 运行节点UUID
*/
private String runtimeUuid;
/**
* 人机交互用户输入信息
*/
private String feedbackContent;
}

View File

@@ -0,0 +1,15 @@
package org.ruoyi.common.chat.domain.dto.request;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.Data;
import java.util.List;
/**
* 工作流请求体信息
*/
@Data
public class WorkFlowRunner {
private List<ObjectNode> inputs;
private String uuid;
}

View File

@@ -1,65 +0,0 @@
package org.ruoyi.common.chat.domain.request;
import org.ruoyi.common.chat.entity.chat.Message;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
import java.util.List;
/**
* 描述:
*
* @author https:www.unfbx.com
* @sine 2023-04-08
*/
@Data
public class ChatRequest {
private String frequency_penalty;
private String max_tokens;
@NotEmpty(message = "对话消息不能为空")
List<Message> messages;
@NotEmpty(message = "传入的模型不能为空")
private String model;
private String presence_penalty;
private String stream;
private double temperature;
private double top_p = 1;
/**
* 知识库id
*/
private String kid;
private String userId;
//
//
// /**
// * gpt的默认设置
// */
// private String systemMessage = "";
//
//
//
// private double temperature = 0.2;
//
// /**
// * 上下文的条数
// */
// private Integer contentNumber = 10;
//
// /**
// * 是否携带上下文
// */
// private Boolean usingContext = Boolean.TRUE;
}

View File

@@ -1,33 +0,0 @@
package org.ruoyi.common.chat.domain.request;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
/**
* 描述:
*
* @author https:www.unfbx.com
* @sine 2023-04-08
*/
@Data
public class Dall3Request {
@NotEmpty(message = "传入的模型不能为空")
private String model;
@NotEmpty(message = "提示词不能为空")
private String prompt;
/** 图片大小 */
@NotEmpty(message = "图片大小不能为空")
private String size ;
/** 图片质量 */
@NotEmpty(message = "图片质量不能为空")
private String quality;
/** 图片风格 */
@NotEmpty(message = "图片风格不能为空")
private String style;
}

View File

@@ -0,0 +1,79 @@
package org.ruoyi.common.chat.domain.vo.chat;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.ruoyi.common.chat.entity.chat.ChatMessage;
import org.ruoyi.common.excel.annotation.ExcelDictFormat;
import org.ruoyi.common.excel.convert.ExcelDictConvert;
import java.io.Serial;
import java.io.Serializable;
/**
* 聊天消息视图对象 chat_message
*
* @author ageerle
* @date 2025-12-14
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = ChatMessage.class)
public class ChatMessageVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@ExcelProperty(value = "主键")
private Long id;
/**
* 会话id
*/
@ExcelProperty(value = "会话id")
private Long sessionId;
/**
* 用户id
*/
@ExcelProperty(value = "用户id")
private Long userId;
/**
* 消息内容
*/
@ExcelProperty(value = "消息内容")
private String content;
/**
* 对话角色
*/
@ExcelProperty(value = "对话角色")
private String role;
/**
* 累计 Tokens
*/
@ExcelProperty(value = "累计 Tokens")
private Long totalTokens;
/**
* 模型名称
*/
@ExcelProperty(value = "模型名称")
private String modelName;
/**
* 备注
*/
@ExcelProperty(value = "备注")
private String remark;
}

View File

@@ -1,27 +1,25 @@
package org.ruoyi.system.domain.vo;
package org.ruoyi.common.chat.domain.vo.chat;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.ruoyi.common.sensitive.annotation.Sensitive;
import org.ruoyi.common.sensitive.core.SensitiveStrategy;
import org.ruoyi.system.domain.SysModel;
import org.ruoyi.common.chat.entity.chat.ChatModel;
import java.io.Serial;
import java.io.Serializable;
/**
* 系统模型视图对象 sys_model
* 模型管理视图对象 chat_model
*
* @author Lion Li
* @date 2024-04-04
* @author ageerle
* @date 2025-12-14
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = SysModel.class)
public class SysModelVo implements Serializable {
@AutoMapper(target = ChatModel.class)
public class ChatModelVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@@ -44,6 +42,11 @@ public class SysModelVo implements Serializable {
@ExcelProperty(value = "模型名称")
private String modelName;
/**
* 模型供应商
*/
@ExcelProperty(value = "模型供应商")
private String providerCode;
/**
* 模型描述
@@ -51,38 +54,29 @@ public class SysModelVo implements Serializable {
@ExcelProperty(value = "模型描述")
private String modelDescribe;
/**
* 模型价格
*/
@ExcelProperty(value = "模型价格")
private double modelPrice;
/**
* 计费类型
*/
@ExcelProperty(value = "计费类型")
private String modelType;
/**
* 是否显示
*/
@ExcelProperty(value = "是否显示")
private String modelShow;
/**
* 系统提示词
* 向量维度
*/
private String systemPrompt;
@ExcelProperty(value = "向量维度")
private Integer modelDimension;
/**
* 请求地址
*/
@ExcelProperty(value = "请求地址")
private String apiHost;
/**
* 模型名称
* 密钥
*/
@Sensitive(strategy = SensitiveStrategy.ID_CARD)
@ExcelProperty(value = "密钥")
private String apiKey;
/**
@@ -92,3 +86,4 @@ public class SysModelVo implements Serializable {
private String remark;
}

View File

@@ -0,0 +1,29 @@
package org.ruoyi.common.chat.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
public class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.AUTO)
private Long id;
@TableField(value = "create_time")
private LocalDateTime createTime;
@TableField(value = "update_time")
private LocalDateTime updateTime;
@Schema(title = "是否删除0未删除1已删除")
@TableField(value = "is_deleted")
private Boolean isDeleted;
}

View File

@@ -1,48 +0,0 @@
package org.ruoyi.common.chat.entity.Tts;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.*;
@Data
@Builder
@JsonInclude(JsonInclude.Include.NON_NULL)
@NoArgsConstructor
@AllArgsConstructor
public class TextToSpeech {
@Builder.Default
private String model = Model.TTS_1.getName();
/**
* 音频声音源
*
* @see TtsVoice
*/
private String voice;
/**
* 输入内容
*/
private String input;
/**
* 输出音频文件格式
*
* @see TtsFormat
*/
@JsonProperty("response_format")
private String responseFormat;
/**
* 速度调节默认是1取值范围0.25——4.0
*/
private Double speed;
@Getter
@AllArgsConstructor
public enum Model {
TTS_1("tts-1"),
TTS_1_HD("tts-1-hd"),
;
private final String name;
}
}

View File

@@ -1,15 +0,0 @@
package org.ruoyi.common.chat.entity.Tts;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum TtsFormat {
MP3("mp3"),
OPUS("opus"),
AAC("aac"),
FLAC("flac"),
;
private final String name;
}

View File

@@ -1,23 +0,0 @@
package org.ruoyi.common.chat.entity.Tts;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 生成不同声音的音频
* <p>具体语音效果参考https://platform.openai.com/docs/guides/text-to-speech</p>
*/
@Getter
@AllArgsConstructor
public enum TtsVoice {
ALLOY("alloy"),
ECHO("echo"),
FABLE("fable"),
ONYX("onyx"),
NOVA("nova"),
SHIMMER("shimmer"),
;
private final String name;
}

View File

@@ -0,0 +1,66 @@
package org.ruoyi.common.chat.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.ruoyi.common.chat.enums.UserStatusEnum;
import java.time.LocalDateTime;
@EqualsAndHashCode(callSuper = true)
@Data
@TableName("adi_user")
@Schema(title = "User对象")
public class User extends BaseEntity {
@Schema(name = "用户名称")
@TableField("name")
private String name;
@TableField("email")
private String email;
@TableField("password")
private String password;
@TableField("uuid")
private String uuid;
@Schema(name = "上下文理解中需要携带的消息对数量(提示词及回复)")
@TableField("understand_context_msg_pair_num")
private Integer understandContextMsgPairNum;
@Schema(name = "token quota in one day")
@TableField("quota_by_token_daily")
private Integer quotaByTokenDaily;
@Schema(name = "token quota in one month")
@TableField("quota_by_token_monthly")
private Integer quotaByTokenMonthly;
@Schema(name = "request quota in one day")
@TableField("quota_by_request_daily")
private Integer quotaByRequestDaily;
@Schema(name = "request quota in one month")
@TableField("quota_by_request_monthly")
private Integer quotaByRequestMonthly;
@TableField("quota_by_image_daily")
private Integer quotaByImageDaily;
@TableField("quota_by_image_monthly")
private Integer quotaByImageMonthly;
@TableField("user_status")
private UserStatusEnum userStatus;
@TableField("active_time")
private LocalDateTime activeTime;
@Schema(title = "是否管理员01")
@TableField(value = "is_admin")
private Boolean isAdmin;
}

View File

@@ -1,33 +0,0 @@
package org.ruoyi.common.chat.entity.billing;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.List;
/**
* 描述:金额消耗信息
*
* @author https:www.unfbx.com
* @since 2023-04-08
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class BillingUsage {
@JsonProperty("object")
private String object;
/**
* 账号金额消耗明细
*/
@JsonProperty("daily_costs")
private List<DailyCost> dailyCosts;
/**
* 总使用金额:美分
*/
@JsonProperty("total_usage")
private BigDecimal totalUsage;
}

View File

@@ -1,39 +0,0 @@
package org.ruoyi.common.chat.entity.billing;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 描述:余额查询接口返回值
*
* @author https:www.unfbx.com
* @since 2023-03-18
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class CreditGrantsResponse implements Serializable {
private String object;
/**
* 总金额:美元
*/
@JsonProperty("total_granted")
private BigDecimal totalGranted;
/**
* 总使用金额:美元
*/
@JsonProperty("total_used")
private BigDecimal totalUsed;
/**
* 总剩余金额:美元
*/
@JsonProperty("total_available")
private BigDecimal totalAvailable;
/**
* 余额明细
*/
private Grants grants;
}

View File

@@ -1,28 +0,0 @@
package org.ruoyi.common.chat.entity.billing;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
* 描述:金额消耗列表
*
* @author https:www.unfbx.com
* @since 2023-04-08
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class DailyCost {
/**
* 时间戳
*/
@JsonProperty("timestamp")
private long timestamp;
/**
* 模型消耗金额详情
*/
@JsonProperty("line_items")
private List<LineItem> lineItems;
}

View File

@@ -1,40 +0,0 @@
package org.ruoyi.common.chat.entity.billing;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.math.BigDecimal;
/**
* 描述:
*
* @author https:www.unfbx.com
* @since 2023-03-18
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class Datum {
private String object;
private String id;
/**
* 赠送金额:美元
*/
@JsonProperty("grant_amount")
private BigDecimal grantAmount;
/**
* 使用金额:美元
*/
@JsonProperty("used_amount")
private BigDecimal usedAmount;
/**
* 生效时间戳
*/
@JsonProperty("effective_at")
private Long effectiveAt;
/**
* 过期时间戳
*/
@JsonProperty("expires_at")
private Long expiresAt;
}

View File

@@ -1,21 +0,0 @@
package org.ruoyi.common.chat.entity.billing;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
* 描述:
*
* @author https:www.unfbx.com
* @since 2023-03-18
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class Grants {
private String object;
@JsonProperty("data")
private List<Datum> data;
}

View File

@@ -1,56 +0,0 @@
package org.ruoyi.common.chat.entity.billing;
import lombok.*;
import java.time.LocalDate;
/**
* openKey信息
*
* @author admin
* @date 2023/6/15
*/
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class KeyInfo {
/**
* 订阅类型
*/
private String planTitle;
/**
* key值
*/
private String keyValue;
/**
* 剩余额度
*/
private Double remaining;
/**
* 账户总余额
*/
private Double totalAmount;
/**
* 已使用的额度
*/
private Double totalUsage;
/**
* 截至日期
*/
private LocalDate limitDate;
/**
* 是否绑卡
*/
private Boolean isHasPaymentMethod;
/**
* 最高可用模型
*/
private String model;
}

View File

@@ -1,25 +0,0 @@
package org.ruoyi.common.chat.entity.billing;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import java.math.BigDecimal;
/**
* 描述:金额消耗列表
*
* @author https:www.unfbx.com
* @since 2023-04-08
*/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class LineItem {
/**
* 模型名称
*/
private String name;
/**
* 消耗金额
*/
private BigDecimal cost;
}

Some files were not shown because too many files have changed in this diff Show More