Compare commits

...

859 Commits

Author SHA1 Message Date
YunaiV
e68b294ea1 (〃'▽'〃) v2.5.0 发布:又熬过 30 个夜,头发还在 2025-05-13 20:10:01 +08:00
YunaiV
51d8346733 application-local 默认切到 Kongdy 2025-05-13 20:01:19 +08:00
YunaiV
2cb4c7ddb4 fix:修复工作流的发起时间未返回 2025-05-13 19:23:09 +08:00
YunaiV
0cd831d5f5 fix:【CRM】receiveContactLog 记录日志不正确的问题 2025-05-13 09:55:19 +08:00
YunaiV
69da106b9e reactor:增强MPJLambdaWrapperX 类,左连接后可以使用betweenIfPresent等 2025-05-12 21:21:52 +08:00
YunaiV
1cf079fec3 fix:【BPM 工作流】项目重启后,审批重启前的流程会出现生成的下一待办任务所属人出现缺失 2025-05-12 20:56:31 +08:00
YunaiV
ee5cb23730 fix(bpm):修复自选审批人时存在多个并行节点时审批人覆盖问题
多个并行节点中只有最后一个节点的审批人会被保存,其他都为空
2025-05-12 20:48:33 +08:00
芋道源码
edb302521f !1340 fix(bpm):修复自选审批人时存在多个并行节点时审批人覆盖问题
Merge pull request !1340 from whc/master-jdk17
2025-05-12 12:45:55 +00:00
YunaiV
5421299b09 Merge branch 'develop' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into master-jdk17 2025-05-12 20:21:52 +08:00
芋道源码
47755e9352 !1339 perf: vben5 代码生成
Merge pull request !1339 from puhui999/master-jdk17
2025-05-12 12:21:06 +00:00
芋道源码
a1ebef07d1 !1338 【功能完善】商城:【会员】发送客服消息时也给自己发送一条 websocket 消息
Merge pull request !1338 from puhui999/develop-new
2025-05-12 12:20:49 +00:00
吴焕超
03c804ee10 fix(bpm):修复自选审批人时存在多个并行节点时审批人覆盖问题
多个并行节点中只有最后一个节点的审批人会被保存,其他都为空
2025-05-12 18:24:35 +08:00
YunaiV
50e31f1bec feat:同步最新菜单 sql 2025-05-12 09:10:21 +08:00
YunaiV
51c1091430 fix:【PAY 支付】PayDemoWithdrawServiceImpl 拼写错误 2025-05-11 20:48:51 +08:00
YunaiV
7c38a32fc9 fix:【MALL 商城】小程序发货,uploadTime 不正确的问题 2025-05-11 17:11:38 +08:00
YunaiV
44548ee03f feat:【MALL 商城】商城分佣提现,完成对微信转账(小程序)的对接 2025-05-11 12:39:05 +08:00
YunaiV
fd8567f0fa fix:【PAY 支付】wxjava v3 微信支付,connection pool shutdown 问题 2025-05-11 08:52:52 +08:00
YunaiV
4d25e810e3 feat:【MALL 商城】商城分佣提现,初步对接成功微信支付 2025-05-11 08:12:42 +08:00
YunaiV
b91a30dd3e feat:【PAY 支付】增加 channelPackageInfo 字段,对接微信新的转账 API(太难了!!!!!!!!!!!!!!!!!!!!) 2025-05-10 20:05:17 +08:00
YunaiV
0e7ce63719 feat:【PAY 支付】转账单,支持导出功能 2025-05-10 17:15:30 +08:00
YunaiV
288d8e3132 reactor:【PAY 支付】优化退款回调接口,增加 merchantRefundId 退款编号参数
reactor:【MALL 商城】售后退款时,基于回调处理
2025-05-10 16:43:55 +08:00
YunaiV
fe8871b5f1 feat:【MALL 商城】佣金提现,支持提现失败时,重新发起 2025-05-10 11:48:29 +08:00
YunaiV
423c0b7ea7 feat:【MALL 商城】佣金提现,优化字段,以及支持更多 API 自动打款 2025-05-10 10:07:11 +08:00
YunaiV
d2b668a676 feat:【PAY 支付】提现示例,拆分 create 和 transfer 两个状态,更符合实际场景 2025-05-09 22:03:01 +08:00
puhui999
74712db1a8 perf: vben5 代码生成 2025-05-09 16:38:47 +08:00
YunaiV
38c76806a3 feat:【PAY 支付】微信支付的转账,接入新的 API(需要继续测试,= = 真麻烦)
feat:【PAY 支付】钱包支持转账功能
2025-05-08 23:31:23 +08:00
puhui999
99803d1ebf 【功能完善】商城:【会员】发送客服消息时也给自己发送一条 websocket 消息 2025-05-08 17:08:37 +08:00
YunaiV
f81dc105a2 feat:【PAY 支付】AbstractAlipayPayClient,支付宝增加回调解析 2025-05-08 13:57:49 +08:00
YunaiV
7653be9d48 feat:【PAY 支付】示例转账单,改成示例提现单,理解成本更低 2025-05-08 13:09:14 +08:00
YunaiV
1e7f22cde0 feat:【PAY 支付】完善支付转账示例,以及转账功能(支付宝) 2025-05-07 23:37:24 +08:00
YunaiV
ffee189589 reactor:移除 PayClient 获取转账时的 type 字段,只有支付宝会区分,后续通过 channelExtras 区分 2025-05-07 09:21:29 +08:00
xingyu4j
5327d3a494 perf: vben5 代码生成 2025-05-06 23:26:44 +08:00
xingyu4j
6ba0484f33 perf: vben5 代码生成 2025-05-06 22:14:10 +08:00
YunaiV
a34de9f223 feat:【MALL 商城】增加微信物流的对接(和社区同学,一起测试中。。。) 2025-05-06 20:49:13 +08:00
YunaiV
9847d4cdb1 fix:【PAY 支付】支付宝回调时,区分公钥模式、证书模式 2025-05-05 23:52:36 +08:00
YunaiV
be86acb9b6 fix:【PAY 支付】支付宝回调时,区分公钥模式、证书模式 2025-05-05 23:37:40 +08:00
YunaiV
a2bfa7a95a feat:weixin-java from 4.7.2.B => 4.7.4.B
feat:微信支付 v3 从平台证书切换成微信支付公钥
feat:微信支付 v3 增加 header 解析
2025-05-05 22:35:49 +08:00
YunaiV
59234e1eea feat:增加租户切换的能力 2025-05-05 17:27:40 +08:00
YunaiV
8500575f44 fix:TenantDatabaseInterceptor 针对 TenantBaseDO 忽略不正确 2025-05-05 15:31:29 +08:00
YunaiV
1c0909d970 reactor:@TenantIgnore 增加 enable 属性,用于是否开启 2025-05-05 11:33:39 +08:00
YunaiV
70cbb82c84 reactor:@TenantIgnore 添加在 DO 实体类上,该表自动忽略租户 2025-05-05 11:18:19 +08:00
YunaiV
112f63bb7e reactor:@TenantIgnore 添加在 Controller 时,自动添加到 TenantProperties 中 2025-05-05 10:23:44 +08:00
YunaiV
152c24e8b8 reactor:@TenantIgnore 添加在 Controller 时,自动添加到 TenantProperties 中 2025-05-05 09:34:02 +08:00
YunaiV
be86cdfd51 reactor:@TenantIgnore 添加在 Controller 时,自动添加到 TenantProperties 中 2025-05-05 09:33:34 +08:00
YunaiV
5a87b33df2 fix:【MP 公众号】receiveMessage 记录消息时,兜底 MpUserDO 的创建边界 2025-05-04 21:26:40 +08:00
YunaiV
0343c4d2ba fix:【MALL 商城】秒杀、积分商品库存剩余 1 时,无法下单 2025-05-04 11:39:47 +08:00
芋道源码
8b70307ae7 !1336 【功能完善】INFRA:代码生成 vben5 antd 树表、主子表模版
Merge pull request !1336 from puhui999/master-jdk17
2025-05-04 03:14:10 +00:00
YunaiV
5af9496d66 fix:application-local.yaml 配置文件的,spring.autoconfigure.exclude 增加 noinspection SpringBootApplicationYaml,避免出现很难看的报错提醒!!! 2025-05-04 11:13:09 +08:00
YunaiV
fe0840cde8 fix:【MALL 商城】linux 部署时,express.client 配置项,因为大写无法识别 2025-05-04 10:51:53 +08:00
YunaiV
1138376f7a fix:【PAY 支付】支付示例订单退款时,校验 payRefundId 不正确的问题 2025-05-04 10:39:28 +08:00
YunaiV
5f58dfdcd5 fix:AOP 拼接参数时,servlet 相关 request、response 可能无法序列化的问题 2025-05-04 10:36:23 +08:00
YunaiV
2c82556db7 chore:mybatis from 3.5.11 -> 3.5.19 2025-05-04 10:08:09 +08:00
YunaiV
2ee64635f3 chore:mybatis from 3.5.11 -> 3.5.19 2025-05-04 10:06:27 +08:00
YunaiV
d3963644b8 chore:guava from 33.4.0-jre => 33.4.8-jre 2025-05-04 09:58:49 +08:00
YunaiV
163839926a fix:justauth-spring-boot-starter 引入的 hutool-core 冲突 2025-05-04 09:53:34 +08:00
YunaiV
d695dc3cd9 update:AI 功能说明 2025-05-03 22:11:31 +08:00
YunaiV
b66a753226 feat:【AI 大模型】新增 Coze 智能体的接入 2025-05-03 21:56:31 +08:00
YunaiV
50f86f1e0a fix:【AI 大模型】logback 和 slf4j-simple 的日志冲突 2025-05-03 20:05:20 +08:00
YunaiV
8b958cdc9b feat:【AI 大模型】工作流增加 OLLAMA 的接入 2025-05-03 19:50:29 +08:00
puhui999
c6582b2a61 【功能完善】INFRA:代码生成 vben5 antd 主子表 erp 模版 2025-05-03 18:24:12 +08:00
YunaiV
04b8aa0422 reactor:【AI 大模型】优化消息发送后,读取文档的逻辑,避免 N 次读取 2025-05-03 18:09:28 +08:00
puhui999
515cbbb285 【功能完善】INFRA:代码生成 vben5 antd 主子表 inner 模版 2025-05-03 17:31:09 +08:00
YunaiV
3e8596ee4b feat:【AI 大模型】增加知识库的删除功能 2025-05-03 16:51:17 +08:00
puhui999
57138d7995 【功能完善】INFRA:代码生成 vben5 antd 主子表 normal 模版 2025-05-03 16:39:55 +08:00
YunaiV
e11ee654ef feat:【AI 大模型】增加 AI ToolContext 上下文 2025-05-03 16:37:50 +08:00
芋道源码
3c87f953ee !1332 feat: 完善AI工作流运行测试(通义千问)
Merge pull request !1332 from Lesan/feature/ai-workflow
2025-05-03 08:27:39 +00:00
YunaiV
282e87b17e fix:【AI 大模型】写作时,获取聊天模型错误 2025-05-03 16:10:37 +08:00
芋道源码
fe2122d3be !1323 feat:AI工具新增ToolContext
Merge pull request !1323 from Ren/feature/ai
2025-05-03 07:39:08 +00:00
puhui999
b0f4a252c7 【功能完善】INFRA:代码生成 vben5 antd 树表模版 2025-05-03 15:06:09 +08:00
puhui999
28c818e9bc Merge remote-tracking branch 'yudao/master-jdk17' into master-jdk17
# Conflicts:
#	yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java
2025-05-03 13:34:23 +08:00
puhui999
ed920f3fd3 【代码优化】INFRA:代码生成 vben5 antd 模版 2025-05-03 13:32:53 +08:00
YunaiV
4fb4af2c5c feat:集成积木仪表盘 2025-05-03 10:26:14 +08:00
YunaiV
0b98ef4ecf chore:jimureport 1.8.1 to 1.9.4 2025-05-03 09:06:36 +08:00
YunaiV
bd057ce14d chore:mybatis 相关依赖升级 2025-05-03 08:45:36 +08:00
YunaiV
585a124d34 chore:spring 相关依赖升级 2025-05-03 08:35:31 +08:00
YunaiV
cd263022d4 fix:【全局】sqlserver 在分页的兼容性 2025-05-02 22:47:25 +08:00
YunaiV
387c5cd6ff Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into master-jdk17 2025-05-02 22:21:41 +08:00
YunaiV
c8a14f11ec Merge branch 'develop' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into master-jdk17 2025-05-02 21:09:43 +08:00
芋道源码
c2db16cc15 !1335 reactor:【INFRA】文件上传 api,增加 directory 参数,去除 path 参数,并支持按照日期分目录、文件名不再使用 …
Merge pull request !1335 from 芋道源码/develop
2025-05-02 13:00:17 +00:00
YunaiV
cce09044c1 reactor:【INFRA】文件上传 api,增加 directory 参数,去除 path 参数,并支持按照日期分目录、文件名不再使用 sha256 而是时间戳 2025-05-02 18:22:00 +08:00
xingyu4j
bf05e2d277 fix: vben5 Codegen Front Type 2025-05-02 13:46:36 +08:00
YunaiV
d778184213 feat:【MALL】增加优惠模版的领取信息的返回 2025-05-01 09:01:57 +08:00
YunaiV
95b8cf00fd fix:【商城】已删除的商品,无法评论,导致 TradeOrderAutoCommentJob 重复持续运行 2025-04-30 21:29:50 +08:00
YunaiV
361e50e5de feat:Redis Stream 增加清理 Job,避免占用内存过多 2025-04-30 19:41:24 +08:00
YunaiV
d7656535a2 reactor:使用 aj-captcha 官方 1.4.0 2025-04-30 15:01:19 +08:00
YunaiV
4cebe4af14 reactor:使用 justauth 官方 1.16.7 2025-04-30 00:05:54 +08:00
xingyu4j
d591e9a01e feat: 使用 modalApi.unlock() 替换 modalApi.lock(false) 2025-04-29 22:53:30 +08:00
YunaiV
dad0cc4cb1 Merge branch 'develop' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into master-jdk17 2025-04-29 22:12:35 +08:00
YunaiV
cb701d7d7f fix:【INFRA】阿里云 S3 上传时,会 aws-chunked encoding is not supported 报错的问题 2025-04-29 19:11:50 +08:00
xingyu4j
5a906a7b20 fix: requiredMode 调成为 Schema.RequiredMode.REQUIRED 的方式,解决提示 REQUIRED 找不到的问题 2025-04-29 14:26:37 +08:00
Lesan
bce63cd04f feat: 完善AI工作流运行测试(通义千问) 2025-04-29 13:59:33 +08:00
YunaiV
4dde700f35 feat:【MALL】App 售后筛选时,增加 status 过滤 2025-04-28 23:16:54 +08:00
xingyu4j
aa48a6f533 fix: vben 代码生成默认vbenForm 样式 2025-04-28 15:10:30 +08:00
xingyu4j
1080972e36 fix: VxeTableGridOptions 从 #/adapter/vxe-table 导入 2025-04-28 15:06:48 +08:00
YunaiV
00a08d6cb6 reactor:【MALL】修改购物车查询接口,返回库存、状态等状态,默认不自动取消 selected 状态 2025-04-28 00:24:59 +08:00
YunaiV
ec76c1ae6e fix:【MALL】修复 addBrokerage 创建分销记录时,每个订单项,生成分销记录 2025-04-27 09:44:27 +08:00
YunaiV
3395a3d86b feat:【MALL】商城订单取消时,额外校验支付单的状态,进一步兜底回调情况 2025-04-27 08:58:54 +08:00
YunaiV
03e510adfd fix:【MALL】修复了一些问题 https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1315 2025-04-26 17:34:17 +08:00
芋道源码
f6acc9dea5 !1315 修复了一些问题
Merge pull request !1315 from puhui999/develop-tmp
2025-04-26 09:26:25 +00:00
YunaiV
abfbe1ea83 feat:【SYSTEM】通过系统配置管理来设置系统是否可注册用户 2025-04-26 17:22:42 +08:00
YunaiV
4a9e9961b0 fix:【SYSTEM】AdminUserServiceImplTest 的 testUpdateUserProfile_success 单测报错 2025-04-26 17:04:20 +08:00
YunaiV
cca592a15b fix:【SYSTEM】修复创建租户管理员时,select/update 自动拼接user_id 问题 2025-04-26 16:51:41 +08:00
YunaiV
dd068eb94f refactor:【INFRA】优化 vben5.0 标准代码生成的模版 2025-04-26 16:48:55 +08:00
YunaiV
3d45f92852 Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into develop 2025-04-26 16:44:38 +08:00
芋道源码
380cc87ea4 !1329 【代码优化】INFRA:代码生成 vben5 schema data.ts 模版优化
Merge pull request !1329 from puhui999/master-jdk17
2025-04-26 08:44:31 +00:00
YunaiV
df8593d27b Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into develop 2025-04-26 11:23:03 +08:00
芋道源码
24b48f3826 !1325 【fix】bpm 回退操作参数取值
Merge pull request !1325 from aho/master-jdk17
2025-04-26 03:22:57 +00:00
YunaiV
cf3a13d4ff fix:【BPM】修复多实例使用 LinkedHashSet 保持顺序 2025-04-26 11:19:31 +08:00
YunaiV
a9bb55340b feat:使用 aws s3 2.0 客户端 2025-04-26 10:00:53 +08:00
YunaiV
46676a439d feat:【INFRA】文件存储,增加 enablePathStyleAccess 选项 2025-04-25 21:48:37 +08:00
puhui999
03d3239463 【功能完善】INFRA:代码生成 vben5 antd 单表表单模版 2025-04-25 18:08:24 +08:00
puhui999
ff39a2b57b 【功能完善】INFRA:代码生成 vben5 antd 单表表单模版 2025-04-25 11:15:57 +08:00
芋道源码
0885eb7c70 !1219 【依赖升级】AWS SDK for Java 1.x to 2.x
Merge pull request !1219 from puhui999/develop
2025-04-24 14:20:39 +00:00
puhui999
ef2d0b354a 【功能新增】INFRA:代码生成 vben5 antd 模版 50% 2025-04-24 18:25:04 +08:00
puhui999
c26a46da66 【功能新增】INFRA:代码生成 vben5 antd 模版 50% 2025-04-24 11:37:56 +08:00
YunaiV
bbc59e44d0 【功能优化】VBEN5.0 接入的 SQL 同步 2025-04-23 23:08:07 +08:00
puhui999
32bdc20a4b 【代码优化】INFRA:代码生成 vben5 schema data.ts 模版优化 2025-04-22 16:12:12 +08:00
YunaiV
11402e1412 【功能优化】SYSTEM:updateUserProfile 接口,增加 avatar 参数,逐步替代掉 updateUserAvatar 接口 2025-04-22 09:11:47 +08:00
YunaiV
10397aa480 【代码优化】SYSTEM:profile 接口,不在加载 socialuser,而是提供独立接口 2025-04-21 20:15:00 +08:00
YunaiV
1c854bb547 【代码评审】INFRA:代码生成在 vben5 的模版 2025-04-21 20:14:15 +08:00
芋道源码
6d179320c9 !1328 【功能新增】代码生成: vue3_vben5_antd schema 主子表 erp 模板
Merge pull request !1328 from puhui999/master-jdk17
2025-04-21 12:12:15 +00:00
puhui999
2d0f989968 【功能新增】代码生成: vue3_vben5_antd schema 主子表 erp 模板 2025-04-21 19:10:33 +08:00
puhui999
b3a40af214 【代码优化】代码生成: vue3_vben5_antd schema 主子表模板优化 2025-04-21 16:03:36 +08:00
YunaiV
436eb29d30 【功能新增】SYSTEM:返回人信息时,增加 username、email,用于 vben 界面的展示 2025-04-19 19:45:07 +08:00
芋道源码
08eb75ea0d !1326 代码生成: vue3_vben5_antd schema 主子表标准模式和内嵌模式模板完成
Merge pull request !1326 from puhui999/master-jdk17
2025-04-19 03:58:28 +00:00
puhui999
e1fa4e1a70 【代码优化】代码生成: vue3_vben5_antd schema 主子表标准模式和内嵌模式模板完成 2025-04-17 18:42:05 +08:00
puhui999
dfcbcf09d4 【代码优化】代码生成: vue3_vben5_antd schema 主子表标准模式和内嵌模式模板完成 2025-04-17 16:25:13 +08:00
puhui999
f3aa501e94 【代码优化】代码生成: vue3_vben5_antd schema 主子表模版优化 2025-04-17 11:19:30 +08:00
puhui999
da248cd126 【代码优化】代码生成: vue3_vben5_antd schema 主子表模板优化(标准和内嵌模式) 2025-04-16 23:53:33 +08:00
puhui999
1f7f06549f 【代码优化】代码生成: vue3_vben5_antd schema 主子表 api 和 data.ts 模板优化 2025-04-16 22:29:32 +08:00
puhui999
3923189887 【功能新增】代码生成: vue3_vben5_antd schema 主子表 2025-04-16 18:47:47 +08:00
aho
d67ca05621 【fix】bpm 回退操作参数取值 2025-04-16 17:26:08 +08:00
puhui999
e704f4c755 【代码优化】代码生成: vue3_vben5_antd 模版目录调整 2025-04-16 15:52:20 +08:00
puhui999
24aea3e30a 【代码优化】INFRA: vue3_vben5_antd schema 单表和树表代码生成模版优化 2025-04-16 15:26:01 +08:00
Ren
dda2b56bbf feat:AI工具新增ToolContext 2025-04-12 23:52:24 +08:00
YunaiV
7c94085499 2.4.2 版本发布 2025-04-12 12:19:56 +08:00
YunaiV
fdd15424fc 171 fix(protection): 修复HTTP接口签名 API重复请求问题 2025-04-12 12:01:00 +08:00
YunaiV
f6601ab639 171 fix(protection): 修复HTTP接口签名 API重复请求问题 2025-04-12 11:32:18 +08:00
YunaiV
4d88cfdf86 【代码修复】BPM:BpmProcessIdRedisDAO 的时间处理不对 2025-04-12 10:47:32 +08:00
YunaiV
42dbd4e21a 【代码评审】INFRA:代码生成在 vben5 的模版 2025-04-12 09:48:15 +08:00
芋道源码
f83d93bd5b !1321 【功能新增】INFRA: vben next schema 单表和树表代码生成
Merge pull request !1321 from puhui999/master-jdk17
2025-04-12 01:26:11 +00:00
YunaiV
5d77e3751b 【缺陷修复】AI:临时修复 tinyflow 在 groovy 冲突的问题 2025-04-12 09:22:00 +08:00
YunaiV
160771264f Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into master-jdk17 2025-04-12 08:58:22 +08:00
YunaiV
e56b99f2a7 Merge branch 'feature/ai' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into master-jdk17 2025-04-12 08:57:32 +08:00
芋道源码
e2f158996b !1318 排除TinyFlow的AI工作流中的agents-flex-store-elasticsearch, 在没用到es的时候一直检测es存活
Merge pull request !1318 from neviabit/N/A
2025-04-12 00:57:06 +00:00
YunaiV
911f5f8bf3 【代码评审】AI:PPT 相关 2025-04-12 08:54:35 +08:00
芋道源码
c357e7ae55 !1317 【优化代码】AI PPT:优化文多多、讯飞 API
Merge pull request !1317 from 小新/feature/ai
2025-04-12 00:48:47 +00:00
puhui999
37e5152a35 【代码优化】INFRA: vben next 树表代码生成 2025-04-11 21:38:39 +08:00
puhui999
015565cc9a 【功能新增】INFRA: vben next 树表代码生成 2025-04-11 18:21:42 +08:00
puhui999
69486939d5 【功能新增】INFRA: vben next 单表代码生成 2025-04-11 16:47:11 +08:00
xiaoxin
47df4bb21f feat: mcp demo 2025-04-09 13:16:29 +08:00
neviabit
8b49e5a94d 排除TinyFlow的AI工作流中的agents-flex-store-elasticsearch, 防止一直检测失败导致的Elasticsearch health check failed, 这导致了日志logs被这些无意义的异常给占满
Signed-off-by: neviabit <10192451+neviabit@user.noreply.gitee.com>
2025-04-08 09:20:56 +00:00
xiaoxin
3445baf926 Merge remote-tracking branch 'yd_origin/feature/ai' into feature/ai 2025-04-07 15:45:59 +08:00
YunaiV
be416b7d78 【代码优化】MALL:在线客服的注释 2025-04-03 23:33:21 +08:00
puhui999
d5c1a2ff9f 【缺陷修复】商城:订单取消收回优惠券失败导致管理员确认退款不成功的问题 2025-04-02 17:48:10 +08:00
puhui999
09aa6b2567 【缺陷修复】商城:优惠券在领取类型为指定发放和新人券时无法发送的问题 2025-04-02 11:35:36 +08:00
YunaiV
1e4e02ec2f Merge branch 'develop' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into master-jdk17 2025-04-01 19:16:02 +08:00
芋道源码
a484007d97 !1312 修复了一些问题
Merge pull request !1312 from puhui999/develop-tmp
2025-04-01 11:15:55 +00:00
puhui999
40b6e5a3bb 【缺陷修复】商城:检查是否包邮 2025-04-01 18:04:17 +08:00
puhui999
81124292fd 【代码优化】商城:客服消息携带用户头像信息 2025-04-01 16:28:02 +08:00
YunaiV
fd2d067324 Merge branch 'feature/ai' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into master-jdk17 2025-03-30 10:53:39 +08:00
YunaiV
a53c7dafd3 Merge branch 'feature/bpm' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into master-jdk17 2025-03-30 10:53:17 +08:00
芋道源码
7179786a45 !1305 feat: AI工作流优化
Merge pull request !1305 from Lesan/feature/ai-工作流
2025-03-30 02:19:03 +00:00
jason
d7a785d1de 【问题修复】 并行网关保存问题 2025-03-28 21:41:40 +08:00
Lesan
2aff972600 feat: AI工作流优化 2025-03-28 15:54:46 +08:00
芋道源码
743d88a4fc !1301 条件判断添加支持JOIN表的属性
Merge pull request !1301 from QINY/master-jdk17
2025-03-25 13:09:01 +00:00
YunaiV
f0c26d5b5a 【代码评审】BPM:子流程的代码 2025-03-25 12:49:34 +08:00
芋道源码
2cf5e17f4b !1303 子流程优化
Merge pull request !1303 from Lesan/feature/bpm-子流程
2025-03-25 04:44:01 +00:00
Lesan
c9a8548920 feat: 流程时间线适配子流程节点 2025-03-25 11:07:24 +08:00
Lesan
b471dc55c3 fix: 存在子流程情况下的取消逻辑优化 2025-03-25 10:37:38 +08:00
Lesan
e14716a307 fix: 存在子流程情况下的取消逻辑优化 2025-03-25 10:24:34 +08:00
YunaiV
4bb52bb37d 【代码评审】AI:工作流 2025-03-25 09:55:05 +08:00
芋道源码
c8e63f4a8c !1300 feat: AI工作流
Merge pull request !1300 from Lesan/feature/ai-工作流
2025-03-25 01:21:55 +00:00
qiny
ba4d4540ab 条件判断添加支持JOIN表的属性 2025-03-24 16:58:42 +08:00
Lesan
587504c36a feat: AI工作流 2025-03-24 15:15:27 +08:00
YunaiV
4fbe9fa480 【代码优化】BPM:流程模型->基本信息->谁可以发起,支持指定多个部门 2025-03-23 17:28:08 +08:00
芋道源码
af842c70af Merge pull request #777 from minivv/master-jdk17
【Simple设计器】流程模型->基本信息->谁可以发起,支持指定多个部门
2025-03-23 17:08:52 +08:00
YunaiV
3991647560 Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/bpm 2025-03-23 17:06:54 +08:00
YunaiV
b634a1e97b 【缺陷修复】CRM:修复跟进日志的操作日志记录失败 2025-03-23 16:54:48 +08:00
YunaiV
138239324c 【功能新增】AI:百川模型的接入 2025-03-23 12:48:13 +08:00
YunaiV
51d054c586 Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/ai 2025-03-23 12:18:44 +08:00
YunaiV
cd4813f7dd 【功能新增】AI:百川模型的接入 2025-03-23 12:18:37 +08:00
YunaiV
ef5e56d560 【代码优化】AI:硅基流动的图片生成 2025-03-23 11:22:46 +08:00
YunaiV
59c744520a 【代码优化】AI:硅基流动的图片生成 2025-03-23 10:41:51 +08:00
YunaiV
813e7af846 【代码评审】AI:硅基流动的图片生成 2025-03-23 10:27:01 +08:00
芋道源码
5dc93cb872 !1294 【功能新增】AI:画图通用功能增加硅基流动平台
Merge pull request !1294 from 拽拽的哥/feature/ai
2025-03-23 01:41:53 +00:00
YunaiV
0c9dd34981 【功能修复】IOT:多个 ProductCategoryMapper 的名字冲突 2025-03-23 09:00:33 +08:00
YunaiV
113dfae732 【代码优化】BPM:1297 fix: 修复simple设计器第一个发起人节点,审批时校验是否存在审批人导致流程异常 2025-03-22 23:20:31 +08:00
芋道源码
26c1a10b0c !1297 fix: 修复simple设计器第一个发起人节点,审批时校验是否存在审批人导致流程异常
Merge pull request !1297 from SamllNorth_Lee/feature/bpm
2025-03-22 14:51:58 +00:00
YunaiV
9b11199665 【功能新增】system:增加租户的下拉选择,用于登录界面 2025-03-22 09:41:22 +08:00
xiaoxin
b2ec4d38a0 【优化代码】AI PPT:优化文多多、讯飞 API 2025-03-21 14:41:02 +08:00
lizhixian
629bf28495 fix:修复审批接口,校验任务规则为发起人自选时,先从历史变量中查询审批人。 2025-03-21 14:08:24 +08:00
zzt
ecf4df8620 【功能新增】AI:腾讯图像创作 2025-03-20 08:04:32 +08:00
lizhixian
f70a50472f fix:修复simple设计器第一个发起人节点,审批时校验是否存在审批人导致流程异常 2025-03-19 17:23:12 +08:00
lizhixian
b6c700af6b fix:修复simple设计器第一个发起人节点,审批时校验是否存在审批人导致流程异常 2025-03-19 17:21:29 +08:00
smallNorthLee
41639d5dd7 review: 重构组装下一个节点审批人方法 2025-03-18 19:12:39 +08:00
xiaoxin
270fea0c5d 【解决 TODO 】AI PPT:解决一些 TODO 2025-03-18 14:41:06 +08:00
zzt
7b3401e216 【功能新增】AI:画图通用功能增加硅基流动平台 2025-03-18 00:22:15 +08:00
YunaiV
acf68b1cec 【代码评审】AI:PPT API 的接入 2025-03-17 22:15:57 +08:00
芋道源码
1db3b867aa !1293 【功能新增】AI:讯飞、文多多 PPT API 对接
Merge pull request !1293 from 小新/feature/ai
2025-03-17 13:52:02 +00:00
YunaiV
67e548d545 【代码评审】BPM:任务前置、后置通知 2025-03-17 21:37:15 +08:00
芋道源码
979485027d !1291 review: 重构组装下一个节点审批人方法
Merge pull request !1291 from SamllNorth_Lee/feature/bpm
2025-03-17 13:28:25 +00:00
芋道源码
b2dd2148d3 !1292 feat: 任务前后置通知
Merge pull request !1292 from Lesan/feature/bpm-任务前后置通知
2025-03-17 13:26:27 +00:00
YunaiV
71add4b058 【代码评审】IoT:整体实现 2025-03-17 20:45:26 +08:00
YunaiV
6639d37132 【代码评审】IoT:整体实现 2025-03-17 18:50:12 +08:00
xiaoxin
ecc3bd281c 【功能新增】AI:讯飞 PPT API 对接,测试用例完善 2025-03-17 15:44:17 +08:00
郑威
6fbcac3c13 【Simple设计器】流程模型->基本信息->谁可以发起,支持指定多个部门
指定部门可以在部门新增成员后无需重新修改相关流程
2025-03-17 15:41:28 +08:00
YunaiV
e9a99c1e27 【代码评审】IoT:整体实现 2025-03-17 13:17:29 +08:00
YunaiV
7c84ab9919 【代码评审】IoT:整体实现 2025-03-17 13:14:54 +08:00
xiaoxin
c79273c5d6 【测试用例新增】AI:文多多 API 测试完善 2025-03-17 09:46:47 +08:00
Lesan
58c667f728 feat: 任务前后置通知 2025-03-17 08:54:34 +08:00
YunaiV
eb7269083a Merge branch 'feature/iot' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into master-jdk17 2025-03-17 08:54:10 +08:00
YunaiV
638976dac8 【代码评审】IoT:整体实现 2025-03-16 23:15:32 +08:00
YunaiV
a9733b4d2a 【代码评审】IoT:整体实现 2025-03-16 23:11:04 +08:00
YunaiV
b6c7937aeb 【代码评审】IoT:OTA 的实现 2025-03-16 22:40:41 +08:00
smallNorthLee
7ed96c4c6c review: 重构组装下一个节点审批人方法 2025-03-16 22:32:42 +08:00
YunaiV
8203e074ac 【代码评审】IoT:OTA 的实现 2025-03-16 22:28:10 +08:00
smallNorthLee
8ed3066506 review: 重构组装下一个节点审批人方法 2025-03-16 21:41:47 +08:00
YunaiV
9dfe2f6fdf 【代码评审】IoT:OTA 的实现 2025-03-16 21:33:47 +08:00
YunaiV
44d7d623b3 【代码评审】IoT:OTA 的实现 2025-03-16 21:26:51 +08:00
芋道源码
08ff69d554 !1282 refactor(iot): 移除升级任务中的设备名称设置
Merge pull request !1282 from 陈玄礼/feature/iot-shelly
2025-03-16 13:03:05 +00:00
YunaiV
3b85adc754 【代码评审】IoT:数据桥梁的接入 2025-03-16 20:51:00 +08:00
芋道源码
3191d1bd1a !1284 【代码优化】IoT: 数据桥梁代码优化
Merge pull request !1284 from puhui999/iot
2025-03-16 12:43:51 +00:00
YunaiV
0bdd000226 【代码评审】IoT:mqtt 协议的接入 2025-03-16 20:43:11 +08:00
YunaiV
d597d0057e 【缺陷修复】全局:application-dev 配置文件,多了一个 spring 前缀 2025-03-16 17:04:04 +08:00
YunaiV
c95f0c152d 【功能优化】全局:增加 methodArgumentNotValidExceptionExceptionHandler 对「组合校验」场景的兼容 2025-03-16 16:58:13 +08:00
YunaiV
8ccc55d1aa 【功能优化】商城:KdNiaoExpressClient 增加 requestType 配置,使用免费版,还是增值版 2025-03-16 16:45:48 +08:00
YunaiV
2dc8071faa 【功能优化】全局:增加 selectFirstOne 方法,解决容易出现并发场景下的查询 2025-03-16 16:31:36 +08:00
YunaiV
36165e72fc Merge branch 'feature/bpm' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into master-jdk17 2025-03-16 16:19:10 +08:00
YunaiV
dc1c824749 【缺陷修复】BPM:processTaskAssigned 忽略数据权限,避免分配给 leader 的时候,因为没数据权限,导致查询不到 2025-03-16 15:29:50 +08:00
YunaiV
2ddf9d05e6 【缺陷修复】BPM:使用 DataPermissionUtils 替代 DataPermission,避免 this 调用无法生效的问题 2025-03-16 15:24:56 +08:00
YunaiV
fc8e4662bb 【功能优化】限流的配置,增加 expire 过期,原因参见 https://t.zsxq.com/lcR0W 场景 2025-03-16 14:45:09 +08:00
YunaiV
7516738330 【缺陷修复】BPM:修复 task 的 category 不正确的问题 2025-03-16 13:52:33 +08:00
YunaiV
bd93257a26 【缺陷修复】PAY:WxAppPayClient 返回结果不读id问题 2025-03-16 10:42:10 +08:00
YunaiV
2511f2c55b 【缺陷修复】BPM:“结束节点不能未空”文案部正确的问题 2025-03-15 22:40:31 +08:00
xiaoxin
a82abed2b5 【功能新增】AI:对接文多多 v2 接口 2025-03-15 22:38:09 +08:00
YunaiV
803da9ed9e 【功能优化】System:componentName 重复校验,避免 vue3 router 初始化有问题 2025-03-15 21:37:18 +08:00
芋道源码
0debd8e069 !1247 【bugfix】新人券已关闭但是注册时仍然发放了update module/promotion/dal/mysql/coupon/CouponTemplateMapper.java.
Merge pull request !1247 from 山野羡民/N/A
2025-03-15 13:18:36 +00:00
YunaiV
a4815b30d0 【缺陷修复】商城:修正商城中将会员等级关闭,还继续计算折扣价格 2025-03-15 21:16:53 +08:00
YunaiV
5aacefc00e 【缺陷修复】AI:新增对话进行问答时,toolNames cannot be null的问题 2025-03-15 21:07:34 +08:00
YunaiV
075545c11e 【依赖升级】weixin-java from 4.6.0 to 4.7.2.B 2025-03-15 20:50:19 +08:00
YunaiV
dd0cadd426 【缺陷修复】关闭 knife4j 增强,存在兼容性问题,悲伤 2025-03-15 18:58:02 +08:00
安浩浩
86e4379e62 【功能完善】IoT:新增 TDengine 相关依赖 2025-03-15 18:08:05 +08:00
安浩浩
81739186c9 【功能优化】IoT:重构上行请求处理逻辑,合并属性和事件上报处理,简化代码结构,删除冗余处理器 2025-03-15 17:56:45 +08:00
YunaiV
0ab54a9fe4 【代码评审】BPM:fix: 解决审批节点表单无可编辑字段时,variables流程变量值为空,流程节点流转异常问题 2025-03-15 16:48:05 +08:00
芋道源码
4e2ebe0c66 !1280 fix: 解决审批节点表单无可编辑字段时,variables流程变量值为空,流程节点流转异常问题
Merge pull request !1280 from SamllNorth_Lee/feature/bpm
2025-03-15 08:36:14 +00:00
芋道源码
b6f74f3b9b !1285 fix: 代码评审修改
Merge pull request !1285 from Lesan/feature/bpm-流程前后置通知
2025-03-15 08:09:23 +00:00
芋道源码
367e3b9880 !1286 2.4.2:工作流的更新
Merge pull request !1286 from 芋道源码/feature/bpm
2025-03-15 05:22:46 +00:00
YunaiV
a193322373 更新 README:工作流(增加钉钉、飞书)能力 2025-03-15 13:16:31 +08:00
YunaiV
71216a50bf 更新 README:工作流(增加钉钉、飞书)能力 2025-03-15 13:15:36 +08:00
YunaiV
045fb584d2 【代码评审】BPM:子流程的想法 2025-03-15 11:21:29 +08:00
LesanOuO
25899d9988 fix: 代码评审修改 2025-03-15 10:45:45 +08:00
YunaiV
4364ef09c5 【代码评审】BPM:流程前后置通知 2025-03-15 08:24:57 +08:00
芋道源码
6f2e538927 !1283 feat: 流程前后置通知
Merge pull request !1283 from Lesan/feature/bpm-流程前后置通知
2025-03-15 00:04:55 +00:00
YunaiV
d94e577943 【依赖管理】全局:tika-core 使用 3.1.0 确认 2025-03-15 07:00:39 +08:00
安浩浩
348c138749 【功能完善】IoT:引入 IotStandardResponse 实体类,统一处理器的响应格式,优化错误处理逻辑 2025-03-15 00:26:44 +08:00
芋道源码
a8d7da329f !1256 Spring AI 1.0.0 M6 适配:增加知识库、工具调用(function calling)、工作流、豆包/混元/硅基流动等模型的接入
Merge pull request !1256 from 芋道源码/feature/ai
2025-03-14 14:54:11 +00:00
YunaiV
dd0f8a71c7 【功能新增】AI:更新菜单 SQL 2025-03-14 22:53:29 +08:00
YunaiV
9dbadbbb5d 【功能新增】AI:更新最新的 AI 介绍图 2025-03-14 22:12:31 +08:00
YunaiV
1e8845ce6d 【功能新增】AI:增加 AI 对话,与 tool 的打通 2025-03-14 21:11:28 +08:00
YunaiV
e869aaee83 【功能新增】AI:增加函数的管理 2025-03-14 19:29:36 +08:00
puhui999
acd32f7b4e 【代码优化】IoT: 数据桥梁代码优化 2025-03-14 17:41:12 +08:00
puhui999
3756830b9c 【代码优化】IoT: 数据桥梁代码优化 2025-03-14 17:40:19 +08:00
puhui999
966357b44e 【代码优化】IoT: 数据桥梁测试代码抽离到测试类 2025-03-14 17:34:16 +08:00
lizhixian
d3db32b44e fix:解决校验流程画布数据,极端情况下无用户任务节点导致的异常问题 2025-03-14 16:58:12 +08:00
YunaiV
ffe4afaaaf 【功能新增】AI:新增 function call 示例,完成所有模型的测试 = = 累 2025-03-14 13:26:23 +08:00
Lesan
e114a35451 feat: 流程前后置通知 2025-03-14 09:29:58 +08:00
陈玄礼
37c725c1a3 refactor(iot): 移除升级任务中的设备名称设置
- 删除了创建升级任务时设置设备名称的代码逻辑
- 优化了升级任务初始化过程,减少了不必要的设备名称查询和设置操作
2025-03-13 22:23:28 +08:00
陈玄礼
b87a583842 refactor(iot): 重构 OTA 升级模块
- 更新错误码定义,调整 OTA 相关错误码的编号和描述
- 移除未使用的 IotOtaFirmwareCommonReqVO 类- 优化 IotOtaFirmwareCreateReqVO 和 IotOtaFirmwareUpdateReqVO 的结构
- 删除冗余的 IotOtaUpgradeRecordCreateReqBO 类
- 重构 IotOtaUpgradeRecordMapper 的查询方法
- 更新 IotOtaUpgradeRecordService 接口,简化升级记录创建方法
- 删除未使用的 IotOtaUpgradeRecordJob 类
- 优化 IotOtaUpgradeRecordController 的重试接口,使用 PUT 方法
2025-03-13 22:16:45 +08:00
lizhixian
be608b26e6 review: 代码重构 2025-03-13 16:22:58 +08:00
lizhixian
c9690e144c fix: 解决审批节点表单无可编辑字段时,variables流程变量值为空,流程节点流转异常问题 2025-03-13 16:05:13 +08:00
YunaiV
25a0fe908a 【功能新增】AI:新增 function call 示例。会继续完善! 2025-03-13 12:51:50 +08:00
lizhixian
575e7a38f3 fix: 解决审批节点表单无可编辑字段时,variables流程变量值为空,流程节点流转异常问题 2025-03-13 09:38:15 +08:00
lizhixian
149cab1dac Merge remote-tracking branch 'origin/feature/bpm' into feature/bpm
# Conflicts:
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
2025-03-13 09:03:31 +08:00
YunaiV
34453a3f70 【代码评审】IoT:数据桥梁的维护 2025-03-13 08:30:27 +08:00
芋道源码
27e08266e0 !1269 【代码优化】IoT: 优化数据桥梁
Merge pull request !1269 from puhui999/iot
2025-03-13 00:18:30 +00:00
YunaiV
569d651481 【功能完善】IoT:增加 device config 配置 2025-03-13 08:17:31 +08:00
YunaiV
24261cf767 【代码评审】BPM:计算下一个节点的审批人(审批人自选) 2025-03-13 06:44:41 +08:00
YunaiV
9ae18fe53d 【问题修复】BPM:formConf、formFields 在 BpmProcessDefinitionRespVO 缺少的问题,导致无法发起流程 2025-03-13 06:25:16 +08:00
smallNorthLee
c2789f628c fix: 解决审批节点表单无可编辑字段时,variables流程变量值为空,流程节点流转异常问题 2025-03-12 22:46:31 +08:00
smallNorthLee
23844b930c fix: 解决审批节点表单无可编辑字段时,variables流程变量值为空,流程节点流转异常问题 2025-03-12 22:42:32 +08:00
YunaiV
f7ab30c50a 【功能新增】AI:新增 Dify、FastGPT 的接入例子 2025-03-12 21:50:07 +08:00
芋道源码
44083c96c3 !1276 fix: 发起流程报错,返回的VO缺少字段,icon为null时,copyTo转换异常问题
Merge pull request !1276 from SamllNorth_Lee/feature/bpm
2025-03-12 01:56:06 +00:00
smallNorthLee
42eb89b243 review:重构代码,校验选择的下一个节点的审批人,是否合法 2025-03-11 22:40:53 +08:00
smallNorthLee
cc61bb1a61 review: 校验流程设计器第一个用户任务节点的规则类型是否为“审批人自选” 2025-03-11 22:37:23 +08:00
smallNorthLee
2123d7c067 remove: 删除错误代码 2025-03-11 21:28:29 +08:00
smallNorthLee
27ae2a4761 review: 父子类字段调整 2025-03-11 21:23:19 +08:00
lizhixian
494b80d1eb review: 优化审批时,校验选择的下一个节点的审批人,是否合法 2025-03-11 17:34:13 +08:00
lizhixian
26dd8b6670 fix:获取下一个节点审批人为空时,返回new HashMap<>(),避免下级使用空指针
review: 优化审批时,校验选择的下一个节点的审批人,是否合法
2025-03-11 17:11:41 +08:00
YunaiV
07fe6167e0 【功能新增】AI:接入 minimax 和 moonshot 月之暗灭(kimi) 2025-03-11 13:20:59 +08:00
YunaiV
99d5e7c509 【依赖升级】AI:spring-ai-alibaba-starter 升级到最新,解决流式返回报错的问题 2025-03-11 12:53:34 +08:00
lizhixian
b6a9b5dda9 fix: 校验第一个用户任务节点的规则类型是否为“审批人自选” 2025-03-11 10:22:21 +08:00
YunaiV
b9e8495712 【功能新增】AI:新增 AzureOpenAiEmbeddingModel、OpenAiEmbeddingModel 向量模型 2025-03-11 09:42:24 +08:00
YunaiV
20eb3013f2 【功能新增】AI:新增 QianFanEmbeddingModel 向量模型 2025-03-11 07:57:14 +08:00
YunaiV
3a3607e1cb 【功能新增】AI:新增 ZhiPuAiEmbeddingModel 向量模型 2025-03-11 07:49:54 +08:00
YunaiV
d7e801c438 【功能新增】AI:知识库文档的模型变化时,触发重索引(异步) 2025-03-10 09:12:24 +08:00
smallNorthLee
4054e5fdec fix: 发起流程报错,返回的VO缺少字段 2025-03-09 22:28:13 +08:00
xiaoxin
ea1d9f0075 【功能新增】AI:文多多对接 2025-03-09 21:43:45 +08:00
YunaiV
32e1ef4da8 【功能新增】AI:聊天记录返回时,增加 segments 2025-03-09 21:19:41 +08:00
YunaiV
b709af11a1 【功能新增】AI:聊天记录返回时,增加 segments 2025-03-09 21:00:34 +08:00
YunaiV
cddaca5863 【功能新增】AI:聊天时,增加知识库的拼接 2025-03-09 19:02:17 +08:00
puhui999
cdf316e778 【代码优化】IoT: 数据桥梁 config 优化 2025-03-09 18:04:46 +08:00
puhui999
b1d3b73b6d 【代码优化】IoT: 数据桥梁 config 优化 2025-03-09 13:29:12 +08:00
puhui999
ff9267ad75 【功能新增】IoT: 数据桥梁 CRUD 2025-03-09 12:53:54 +08:00
YunaiV
3bb57edc85 【功能新增】AI:聊天角色,新增 knowledge 的引用绑定 2025-03-09 12:00:04 +08:00
YunaiV
9126691ac0 【功能新增】AI:聊天角色,新增 document 的引用绑定 2025-03-09 11:51:19 +08:00
YunaiV
f286a81392 【功能修复】AI:升级 tika 版本,解决 pdf 读取问题 2025-03-09 09:43:12 +08:00
YunaiV
f2ee2008e6 【功能新增】AI:增加 MilvusVectorStore 向量库的接入 2025-03-09 09:27:33 +08:00
YunaiV
588c9fe323 【功能新增】AI:增加 RedisVectorStore 向量库的接入 2025-03-08 22:09:16 +08:00
安浩浩
e66c69932f 【功能完善】IoT: 更新设备属性映射逻辑,新增对 device_key 字段的处理 2025-03-08 21:59:54 +08:00
puhui999
415dd435f3 【代码优化】IoT: 数据桥梁的执行器根据引入的消息队列动态加载 2025-03-08 11:25:03 +08:00
puhui999
831970233c 【代码优化】IoT: 数据桥梁的执行器根据引入的消息队列动态加载 2025-03-08 10:50:22 +08:00
smallNorthLee
e5c8be2b05 review: 优化TODO 2025-03-07 22:53:35 +08:00
安浩浩
824a801b39 【功能完善】IoT: 添加 Webhook 处理器以处理设备连接和断开事件,更新设备状态管理逻辑 2025-03-07 22:36:38 +08:00
YunaiV
44bcc9476d 【功能新增】AI:增加 QdrantVectorStore 向量库的接入 2025-03-06 22:22:22 +08:00
YunaiV
40b3c49495 【问题修复】 同一个人多个待办任务,获取待办任务,优先查询传递的 taskId 2025-03-06 08:01:23 +08:00
YunaiV
9ff56403d6 【代码评审】BPM:下一个审批人 2025-03-06 07:51:20 +08:00
芋道源码
8a5638bdea !1271 feat:审批通过时,查询下一个执行节点,校验流程执行正确与否
Merge pull request !1271 from SamllNorth_Lee/feature/bpm
2025-03-05 23:14:07 +00:00
smallNorthLee
1b678bd7a9 feat: 新增校验 流程模型创建时第一个用户任务的规则类型不能是审批人自选 2025-03-05 21:41:01 +08:00
smallNorthLee
847e51269b feat: 新增校验 流程模型创建时第一个用户任务的规则类型不能是审批人自选 2025-03-05 21:40:44 +08:00
lizhixian
a23ce60041 review: 代码审查 2025-03-05 12:39:43 +08:00
lizhixian
ead8e94deb review: 代码审查 2025-03-05 12:37:32 +08:00
lizhixian
672a5ef538 review: 代码审查 2025-03-05 12:30:38 +08:00
lizhixian
1e2b56256c review: 代码审查 2025-03-05 09:25:00 +08:00
smallNorthLee
1f9769a432 review: 拆分发起人自选审批人 和 审批人选择下一个节点审批人 2025-03-05 00:36:48 +08:00
smallNorthLee
1e3fd24d65 review: 流程发起时,只预测类型为发起人自选的节点信息 2025-03-04 23:18:19 +08:00
jason
d1fead11da Merge remote-tracking branch 'origin/feature/bpm' into feature/bpm 2025-03-04 23:17:52 +08:00
jason
ffa7c246cf 【问题修复】 同一个人多个待办任务,获取待办任务,优先查询传递的 taskId 2025-03-04 23:17:35 +08:00
YunaiV
6ccd0ca61e 【代码优化】AI:优化 pom 依赖的说明 2025-03-04 23:12:24 +08:00
smallNorthLee
ebd722cb41 review: 修改获取下一个执行的流程节点接口地址和方法 2025-03-04 22:59:22 +08:00
smallNorthLee
c16f7b8154 review: 24行 2025-03-04 22:54:44 +08:00
smallNorthLee
9e151b3966 review: 278 281行 2025-03-04 22:28:25 +08:00
smallNorthLee
4e57bd157f review: 27行 2025-03-04 22:09:11 +08:00
smallNorthLee
dadd43677e review: 51行 2025-03-04 22:07:58 +08:00
YunaiV
cc24eca470 【代码重构】AI:“聊天模型”重构为“模型”,支持 type 模型类型 2025-03-04 20:19:30 +08:00
YunaiV
71b45a29a3 【代码评审】IoT:MQTT 插件 2025-03-04 20:13:19 +08:00
smallNorthLee
6e2d00d561 feat:审批通过时,查询下一个执行节点,校验流程执行正确与否 2025-03-03 22:38:05 +08:00
YunaiV
433e91da8e 【代码重构】AI:“聊天模型”重构为“模型”,支持 type 模型类型 2025-03-03 21:55:49 +08:00
puhui999
7ab61b6d06 【代码优化】IoT: kafka 数据桥梁消息等待的最大时间调整为 10 秒 2025-03-03 21:50:52 +08:00
YunaiV
1c9c9790cd 【代码重构】AI:“聊天模型”重构为“模型”,支持 type 模型类型 2025-03-03 21:48:22 +08:00
YunaiV
89d079349c 【代码重构】AI:“聊天模型”重构为“模型”,支持 type 模型类型 2025-03-03 21:26:31 +08:00
smallNorthLee
d41cce94cd feat:审批通过时,查询下一个执行节点,校验流程执行正确与否 2025-03-03 21:24:13 +08:00
lizhixian
33ab961629 feat:审批通过时,查询下一个执行的任务节点 2025-03-03 17:36:13 +08:00
puhui999
ce5e64e0aa 【代码优化】IoT: 优化数据桥梁的执行器减少子类代码冗余 2025-03-03 13:03:41 +08:00
puhui999
3b54deb989 【代码优化】IoT: 优化数据桥梁的执行器减少子类代码冗余 2025-03-03 12:48:07 +08:00
puhui999
61ea09488e 【代码优化】IoT: 优化数据桥梁执行器抽象类增加泛型,减少子类类型强转 2025-03-03 12:22:19 +08:00
smallNorthLee
1b08e6828f feat:新增获取下一个执行的流程节点接口 2025-03-02 22:31:06 +08:00
YunaiV
3f460dc620 【功能新增】AI:新增知识库分段的新增 2025-03-02 21:52:31 +08:00
smallNorthLee
9dfed5365b feat:
新增:审批人自选标识,
新增:审批人自选策略,
新增:获得流程实例的审批用户选择的下一个节点的审批人
新增:错误码1_009_004_007,下一个节点审批人未配置
2025-03-02 21:06:28 +08:00
YunaiV
5f5e77a392 【功能新增】AI:新增 document 向量的进度查询 2025-03-02 20:54:02 +08:00
安浩浩
3c9985978b 【功能完善】IoT: 更新 MQTT 主题配置为数组,重构 EMQX 认证逻辑,优化异常处理和响应格式 2025-03-02 20:47:50 +08:00
YunaiV
d8a7d668a4 【功能新增】BPM:支持通过“历史”进行恢复 2025-03-02 09:56:54 +08:00
YunaiV
c6b58b0ebf 【代码评审】IoT:数据桥梁实现 2025-03-01 23:58:16 +08:00
芋道源码
6a1798bf6a !1255 【功能新增】IoT: 新增 Kafka、RabbitMQ、RedisStreamMQ 数据桥梁实现
Merge pull request !1255 from puhui999/iot
2025-03-01 15:36:14 +00:00
YunaiV
53693529e1 【代码评审】IoT:首页统计 2025-03-01 23:30:47 +08:00
芋道源码
8bcfd40847 !1266 对IOT首页功能的code review的修改
Merge pull request !1266 from alwayssuper/feature/iot
2025-03-01 14:40:50 +00:00
smallNorthLee
4d35a272c3 feat: 新增常量PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES 2025-03-01 22:30:40 +08:00
YunaiV
9927dd4439 【功能新增】BPM:增加流程定义的 simple 接口 2025-03-01 20:39:23 +08:00
smallNorthLee
3cf0708f64 review: 代码审查,方法抽取 2025-03-01 20:36:01 +08:00
smallNorthLee
8a3264cfd3 review: 代码审查,方法抽取 2025-03-01 20:22:50 +08:00
smallNorthLee
5e15a100cb review: 代码审查 2025-03-01 19:47:16 +08:00
puhui999
cb16539b66 【功能新增】IoT: 新增 Redis Stream MQ 数据桥梁实现 2025-03-01 17:43:46 +08:00
YunaiV
fedb9242b5 【代码评审】BPM:下一个审批人 2025-03-01 16:34:30 +08:00
芋道源码
10d7cbb9af !1267 feat: 审批通过时,校验节点是否为下一个执行节点
Merge pull request !1267 from SamllNorth_Lee/fix/bpm
2025-03-01 07:48:40 +00:00
YunaiV
ebd93514b3 【功能新增】AI:新增 document 向量的进度查询 2025-03-01 13:35:00 +08:00
YunaiV
a998168b3b 【功能新增】AI:新增知识库文档的更新信息与状态 2025-03-01 11:42:11 +08:00
YunaiV
d1e207899a 【功能新增】AI:知识库,新增切片接口 2025-03-01 07:48:16 +08:00
smallNorthLee
ff555b5136 feat: 审批通过时,校验节点是否为下一个执行节点 2025-02-28 22:37:57 +08:00
YunaiV
e5cc9d2ad8 【代码优化】AI:知识库的索引过程 2025-02-28 18:57:17 +08:00
puhui999
7b449b81e7 【功能新增】IoT: 新增 RabbitMQ 数据桥梁实现 2025-02-28 18:03:34 +08:00
lizhixian
deef88f56f feat:审批通过时,校验节点是否为下一个执行节点 2025-02-28 17:10:59 +08:00
alwayssuper
0f0ebda469 [fix]:iot home count 2025-02-28 15:28:38 +08:00
puhui999
69a27b1ee2 【功能新增】IoT: 新增 Kafka 数据桥梁实现 2025-02-28 14:46:02 +08:00
芋道源码
79d5c5e2df !1264 fix: 代码评审修改
Merge pull request !1264 from Lesan/feature/bpm-子流程
2025-02-28 05:28:23 +00:00
Lesan
5b88d88177 fix: 代码评审修改 2025-02-28 09:35:43 +08:00
Lesan
8df3a2d950 fix: 代码评审修改 2025-02-28 09:29:56 +08:00
YunaiV
7918ba7d29 【功能新增】AI:新增知识库文档的批量添加 2025-02-28 08:16:44 +08:00
YunaiV
0a8c75625a 【代码重构】AI:知识库相关的表结构 2025-02-28 07:43:43 +08:00
smallNorthLee
093e563b80 feat: add TODO 2025-02-27 23:33:16 +08:00
陈玄礼
4e33cd2bde refactor(iot): 优化 OTA 相关 Mapper 接口的文档注释
- 移除了多余的 TODO 注释
- 更新了 IotOtaFirmwareMapper、IotOtaUpgradeRecordMapper 和 IotOtaUpgradeTaskMapper 的类注释
- 统一了注释格式,增加了作者信息
2025-02-27 23:28:04 +08:00
陈玄礼
4629084c1b refactor(iot): 重构升级记录状态更新逻辑
- 修改 IotOtaUpgradeRecordMapper 中的 cancelUpgradeRecordByTaskId 方法- 新增 updateUpgradeRecordStatusByTaskIdAndStatus 方法,用于更通用的状态更新
- 在 IotOtaUpgradeRecordServiceImpl 中调用新方法来取消升级记录
2025-02-27 23:18:25 +08:00
陈玄礼
0302ebee99 refactor(iot): 优化 OTA 升级记录查询方法
- 重写 getOtaUpgradeRecordCount 和 getOtaUpgradeRecordStatistics 方法,使用 MyBatis-Plus 的 LambdaQueryWrapperX
-移除 XML 中对应的 SQL 查询语句
- 提高代码可维护性和数据库兼容性
2025-02-27 23:07:09 +08:00
lizhixian
6c6992c86a feat:TODO List 2025-02-27 17:28:22 +08:00
lizhixian
db6d7a7430 feat:添加TODO描述 2025-02-27 15:17:30 +08:00
YunaiV
6e1ec8b3eb 【代码评审】IoT:首页统计 2025-02-27 13:30:39 +08:00
YunaiV
2a65e3bd2e Merge branch 'feature/iot' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/iot 2025-02-27 12:50:33 +08:00
芋道源码
3aef51f811 !1263 【新增】:IOT首页的数据统计
Merge pull request !1263 from alwayssuper/feature/iot
2025-02-27 04:50:24 +00:00
YunaiV
36dd18d41f 【代码评审】IoT:MQTT 插件 2025-02-27 12:45:42 +08:00
alwayssuper
6cf7a67406 [fix]:iot home count 2025-02-27 10:52:28 +08:00
YunaiV
074146c991 【代码评审】BPM:触发器 HTTP 异步 2025-02-27 09:56:34 +08:00
jason
62a1fa8296 Merge remote-tracking branch 'origin/feature/bpm' into feature/bpm 2025-02-27 09:03:06 +08:00
jason
0565db2f2d 【代码评审修改】 异步 http 触发器修改为 Http 回调触发器 2025-02-27 06:40:02 +08:00
smallNorthLee
b030257466 review: 代码审查 修改方法名称validateNextAssignees 2025-02-26 23:24:21 +08:00
安浩浩
006ef40c4b 【功能完善】IoT: 添加 MQTT 主题非法错误码,重构设备服务调用和属性设置逻辑,优化 MQTT 消息处理流程 2025-02-26 22:54:44 +08:00
smallNorthLee
357f4966d3 review: 代码审查 修改方法名称validateNextAssignees 2025-02-26 22:38:31 +08:00
YunaiV
e892dcabac 【代码评审】BPM:子流程的多实例 2025-02-26 21:45:47 +08:00
YunaiV
3c0b9262d7 【代码评审】BPM:触发器 HTTP 异步 2025-02-26 21:34:52 +08:00
芋道源码
35f0d689f1 !1262 feat: 子流程-多实例
Merge pull request !1262 from Lesan/feature/bpm-子流程
2025-02-26 13:30:40 +00:00
lizhixian
13c2d36eee feat:添加流程审批时校验,是否为下一个流程审批的节点 2025-02-26 17:45:35 +08:00
alwayssuper
8daa2131ba [fix]:iot home count 2025-02-26 16:49:29 +08:00
lizhixian
37b2fd4789 feat:调整字段名称 2025-02-26 15:59:42 +08:00
Lesan
8a9d64bbaf feat: 子流程-多实例 2025-02-26 15:18:34 +08:00
Lesan
9c9f2812e9 fix: 添加buildTimeoutBoundaryEvent方法 2025-02-26 08:59:54 +08:00
alwayssuper
9f8c6a944c Merge branch 'feature/iot' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into origin/feature/iot
# Conflicts:
#	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceService.java
2025-02-26 08:36:27 +08:00
Lesan
0d886dc42b fix: 添加必要注释 2025-02-26 08:29:09 +08:00
jason
3f1cd80573 Merge remote-tracking branch 'origin/feature/bpm' into feature/bpm
# Conflicts:
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
2025-02-25 22:26:25 +08:00
jason
5e31062d6b 【代码评审修改】 异步 http 触发器代码评审修改 2025-02-25 22:23:12 +08:00
lizhixian
b2ca263067 feat:添加描述 2025-02-25 16:29:35 +08:00
lizhixian
f2909d6bb6 feat:新增自选节点审批人参数,由前端传递下个节点选择的审批人 2025-02-25 16:27:05 +08:00
YunaiV
197c4ad9bf 【功能修复】INFRA:错误日志、访问日志,params 过长的问题 2025-02-25 13:17:27 +08:00
YunaiV
48c976cf0a 【代码评审】BPM:子流程的超时 2025-02-25 12:41:30 +08:00
安浩浩
4cefea6880 【功能完善】IoT: 添加 MQTT 消息处理器,重构设备属性和事件上报逻辑,优化消息处理流程 2025-02-25 09:51:39 +08:00
芋道源码
f7e4293ed2 !1254 BPM-子流程
Merge pull request !1254 from Lesan/feature/bpm-子流程
2025-02-25 01:46:09 +00:00
安浩浩
4746281df9 【功能完善】IoT: 更新 MQTT 主题配置,重构设备属性和事件上报处理逻辑,优化消息处理流程 2025-02-25 08:50:02 +08:00
安浩浩
4015e7905f 【功能完善】IoT: 更新 MQTT 客户端逻辑,重构消息处理和重连机制,优化配置文件 2025-02-25 08:14:19 +08:00
YunaiV
d0fbb7677c 【代码评审】BPM:触发器 HTTP 异步 2025-02-25 07:56:05 +08:00
smallNorthLee
e11529375e feat: 审批时查询下一个节点的类型是否为自选审批人是否已选择了审批人,否则由前端传递 2025-02-24 22:56:18 +08:00
YunaiV
deca69ada6 【功能优化】AI:DispatcherType.ASYNC 时,不进行认证,解决 Spring AI SSE 的认证问题
https://github.com/YunaiV/yudao-cloud/issues/197
2025-02-24 21:28:15 +08:00
YunaiV
a7e5aaec3b 【代码新增】AI:轨迹流动的接入 2025-02-24 21:19:44 +08:00
lizhixian
45a72f4cd8 fix:审批接口添加审批人参数 2025-02-24 17:15:17 +08:00
lizhixian
5d16355042 fix:
1、预测节点审批人查询,如果不存在,则直接返回空,避免类型转换异常
2、如果processVariables不为空,则使用前端传递的参数值
2025-02-24 16:31:03 +08:00
Lesan
15d0a11bad feat: 子流程-超时设置 2025-02-24 15:30:15 +08:00
jason
fda6aff8af 【功能优化】 异步 http 请求触发器外部回调 controller 接口 2025-02-24 13:25:22 +08:00
jason
a42ad08bc9 【功能优化】 异步 http 请求触发器外部回调 controller 接口 2025-02-24 13:20:52 +08:00
YunaiV
117470998e 【代码新增】AI:增加混元、通义的 deepseek 测试案例 2025-02-24 12:41:14 +08:00
puhui999
1b15fb3845 【代码优化】IoT: 优化数据桥接缓存实现 2025-02-24 10:46:38 +08:00
Lesan
dee2b7cb96 fix: 代码审查修改 2025-02-24 09:38:37 +08:00
YunaiV
a1d5602c40 【代码新增】AI:适配腾讯混元大模型、知识引擎(deepseek) 2025-02-23 21:14:53 +08:00
smallNorthLee
f45758b8fd feat: 审批校验自选审批人节点是否存在审批人 2025-02-23 20:12:05 +08:00
YunaiV
46726fe67c 【代码新增】AI:适配字节豆包 doubao、deepseek 2025-02-23 20:00:54 +08:00
YunaiV
875cfa10c6 【代码新增】AI:适配 提供 OllamaChatModelTests,用于本地 ollma 模型的调用 2025-02-23 19:22:48 +08:00
YunaiV
4ee638db87 【代码优化】AI:适配 Spring AI 1.0.6 对 sd、mj 的兼容 2025-02-23 18:47:35 +08:00
YunaiV
430dc99d12 【代码优化】AI:适配 Spring AI 1.0.6 对 ZhiPu 的兼容 2025-02-23 18:29:33 +08:00
YunaiV
c96d966a41 【代码优化】AI:无法适配 Spring AI 1.0.6 对百度 QianFan 的逻辑:无语!!! 2025-02-23 17:57:59 +08:00
YunaiV
d05a7bd59a 【代码优化】AI:适配 Spring AI 1.0.6 对 Ollama 的逻辑 2025-02-23 17:33:53 +08:00
YunaiV
5655ae925c 【代码优化】AI:适配 Spring AI 1.0.6 对 OpenAI 的逻辑 2025-02-23 17:19:19 +08:00
jason
8c681a77f5 Merge remote-tracking branch 'origin/feature/bpm' into feature/bpm
# Conflicts:
#	yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java
2025-02-22 22:53:49 +08:00
jason
3f8221ba84 【功能新增】 新增异步 http 请求触发器 2025-02-22 22:51:56 +08:00
安浩浩
53697b55c2 【功能完善】IoT: 更新 EMQX 插件配置,添加 MQTT 连接参数,重构相关逻辑 2025-02-22 22:50:37 +08:00
YunaiV
8cf8af1f6d 【代码评审】IoT:固件管理 2025-02-22 19:51:13 +08:00
芋道源码
ebfe35c787 !1243 feat(iot): 添加 OTA 固件管理功能
Merge pull request !1243 from 陈玄礼/feature/iot-ota-shelly
2025-02-22 10:36:03 +00:00
YunaiV
f76843573e 【代码评审】IoT:数据桥梁的执行器抽离 2025-02-22 18:31:09 +08:00
芋道源码
672247dbe4 !1248 【代码优化】IoT: 基于 guava 对 producer 做 cache
Merge pull request !1248 from puhui999/iot
2025-02-22 10:22:16 +00:00
YunaiV
fa40ae1dbd 【代码评审】IoT:MQTT 连接参数 2025-02-22 18:21:18 +08:00
芋道源码
2a9dffb6ed !1246 【性能优化】循环中减少不必要的查询 update r/yudao/module/promotion/service/coupon/CouponServiceImpl.java.
Merge pull request !1246 from 山野羡民/N/A
2025-02-22 09:42:57 +00:00
YunaiV
254b55778f 【代码评审】BPM:子流程 2025-02-22 17:17:27 +08:00
alwayssuper
6d059eae61 [fix]:statistics 2025-02-22 16:58:47 +08:00
芋道源码
10bbe741b0 !1249 feat: 子流程
Merge pull request !1249 from Lesan/feature/bpm-子流程
2025-02-22 08:39:26 +00:00
YunaiV
7a6d1bdd79 【代码评审】BPM:删除表单数据触发器 2025-02-22 16:20:04 +08:00
YunaiV
7ef73b7d09 【代码重构】AI:spring ai 依赖,升级到 1.0.0-M6 2025-02-22 10:47:18 +08:00
YunaiV
3b7b81829d 【代码重构】AI:使用 alibaba ai 替代 tongyi 的实现 2025-02-22 09:22:58 +08:00
jason
b88c09f48d Merge remote-tracking branch 'origin/feature/bpm' into feature/bpm 2025-02-21 22:42:10 +08:00
jason
cbdc081cfe 【功能优化】 删除表单数据触发器 2025-02-21 22:41:49 +08:00
Lesan
11858ca0dd feat: 子流程-子流程发起人 2025-02-21 15:40:23 +08:00
Lesan
0e48458ef7 feat: 子流程-添加高亮 2025-02-21 14:04:00 +08:00
YunaiV
f582c9cfa3 【代码重构】AI:使用 OpenAiApi 接入 deepseek 2025-02-21 13:46:54 +08:00
Lesan
9698fee364 feat: 子流程 2025-02-21 09:52:43 +08:00
YunaiV
22801802ac 【代码重构】AI:使用 OpenAiApi 接入星火 2025-02-21 09:50:28 +08:00
安浩浩
8043ce612f 【功能新增】IoT: 添加 IoT 设备 MQTT 连接参数响应 VO,包含客户端 ID、用户名和密码字段 2025-02-20 18:31:34 +08:00
安浩浩
ca95752266 【功能新增】IoT: 添加 EMQX 插件,支持设备连接认证和 MQTT 连接参数获取,优化配置文件 2025-02-20 18:30:57 +08:00
puhui999
4be18af236 【代码优化】IoT: 基于 guava 对 producer 做 cache 2025-02-20 18:21:52 +08:00
puhui999
0400932260 【代码优化】IoT: 数据桥梁的执行器抽离 2025-02-20 17:44:06 +08:00
YunaiV
c8485a1d6d 【代码评审】IoT:移除 tongyi 库,准备换到 alibaba ai 库 2025-02-20 12:49:06 +08:00
山野羡民
db9534073b 【bugfix】新人券已关闭但是注册时仍然发放了update module/promotion/dal/mysql/coupon/CouponTemplateMapper.java.
Signed-off-by: 山野羡民 <liyujiang_tk@yeah.net>
2025-02-20 02:43:13 +00:00
山野羡民
12170402e2 【性能优化】循环中减少不必要的查询 update r/yudao/module/promotion/service/coupon/CouponServiceImpl.java.
Signed-off-by: 山野羡民 <liyujiang_tk@yeah.net>
2025-02-20 02:39:48 +00:00
Shelly Chan
54381e29a7 feat(iot): 添加 OTA 固件管理功能
- 新增 OTA 固件相关错误码
- 实现 OTA 固件创建、更新和查询接口
- 添加 OTA 升级记录相关功能
- 实现 OTA 固件升级任务定时处理
2025-02-20 01:05:41 +08:00
YunaiV
8e7bbfe0da 【代码评审】IoT:rocketmq 数据桥接的接入 2025-02-19 22:42:19 +08:00
芋道源码
41c41bc6c3 !1241 【功能新增】IoT: 数据桥梁增加 RocketMQConfig 配置,实现executeRocketMQ 发送消息
Merge pull request !1241 from puhui999/iot
2025-02-19 14:38:10 +00:00
YunaiV
ceba5b8cec 【代码评审】BPM:优化流程发起预测节点审批人是否配置在后端逻辑中实现 2025-02-19 22:35:39 +08:00
YunaiV
24f1ce16c7 【代码评审】BPM:优化流程发起预测节点审批人是否配置在后端逻辑中实现 2025-02-19 22:20:12 +08:00
芋道源码
b496ec3fd0 !1238 fix: 修复流程预测节点错误问题
Merge pull request !1238 from SamllNorth_Lee/fix/bpm
2025-02-19 14:03:56 +00:00
smallNorthLee
eaa15b24ff review: 代码审查 2025-02-19 21:05:14 +08:00
smallNorthLee
248127a941 review: 代码审查 2025-02-19 20:01:38 +08:00
puhui999
add90365df 【功能新增】IoT: 数据桥梁增加 RocketMQConfig 配置,实现executeRocketMQ 发送消息 2025-02-19 15:51:34 +08:00
lizhixian
642e72ae7a review: 代码审查 2025-02-19 15:02:04 +08:00
lizhixian
29a902f37e review: 代码审查 2025-02-19 15:00:28 +08:00
lizhixian
4b89f68936 review: 代码审查 2025-02-19 14:13:08 +08:00
lizhixian
cb3467ada2 review: 代码审查 2025-02-19 14:11:42 +08:00
芋道源码
243ce1ff4b !1240 fix: Simple模型抄送节点在流程预测时未显示抄送人
Merge pull request !1240 from Lesan/feature/bpm-fix
2025-02-19 05:38:13 +00:00
YunaiV
009f1889a9 【代码评审】BPM:触发器,修改节点的评审 2025-02-19 12:55:17 +08:00
Lesan
3017933710 fix: Simple模型抄送节点在流程预测时未显示抄送人 2025-02-19 10:04:05 +08:00
lizhixian
9a83515c05 fix: 流程发起预测节点审批人是否配置在后端逻辑中实现 2025-02-18 16:06:51 +08:00
lizhixian
23ed5b780f fix: 移除节点参数,预测节点审批人是否配置在后端逻辑中实现 2025-02-18 14:13:49 +08:00
lizhixian
1f6f00164a fix: 修复流程预测错误问题 2025-02-18 10:00:36 +08:00
smallNorthLee
3265df7548 fix: 修复流程预测节点错误问题
fix: 补充校验规则,流程创建时只校验预测节点的审批人是否配置
2025-02-17 22:41:15 +08:00
smallNorthLee
c68ab33dfd fix: 修复流程预测节点错误问题
fix: 补充校验规则,流程创建时只校验预测节点的审批人是否配置
2025-02-17 22:13:42 +08:00
lizhixian
5e277e020f fix: 解决流程预测审批节点错误问题 2025-02-17 16:11:00 +08:00
lizhixian
296e6ab3ad fix: 调整获得审批详情接口请求方式和参数类型,流程变量参数使用get请求传递错误 2025-02-17 16:04:52 +08:00
lizhixian
8651f2f649 fix: 添加nodeIds参数,只校验预测轨迹下的节点审批人 2025-02-17 16:03:14 +08:00
lizhixian
1023afda40 fix: 修复流程分支节点预测问题 2025-02-17 10:28:11 +08:00
lizhixian
3d20ce1a9b fix: 修复流程分支节点预测问题 2025-02-17 09:52:09 +08:00
jason
1f2222cf83 Merge remote-tracking branch 'origin/feature/bpm' into feature/bpm 2025-02-16 08:47:31 +08:00
jason
9a4bb60a78 【功能优化】 修改表单触发器增加条件设置 2025-02-16 08:47:13 +08:00
YunaiV
3c10dcf392 【功能修复】MALL:管理后台,无法查询到订单的情况 2025-02-15 10:02:48 +08:00
YunaiV
a4e090383b 【功能新增】BPM:已完成的列表,强制移除自动完成的“发起人”节点 2025-02-15 09:40:02 +08:00
YunaiV
56ea9d2381 【代码优化】BPM:修复停用状态下的流程删除报错问题 2025-02-15 09:21:44 +08:00
芋道源码
64ea9906ef !1235 fix: 修复停用状态下的流程删除报错问题
Merge pull request !1235 from SamllNorth_Lee/fix/bpm
2025-02-15 01:18:50 +00:00
YunaiV
e9a3773b72 【代码评审】BPM:办理人的逻辑 2025-02-15 08:48:51 +08:00
芋道源码
04fc7404e1 !1234 BPM-办理人
Merge pull request !1234 from Lesan/feature/bpm-办理人
2025-02-15 00:37:30 +00:00
YunaiV
75bca650da 【代码评审】IoT:增加相关待定项 2025-02-15 08:29:50 +08:00
lizhixian
045a224c22 fix: 修复停用状态下的流程删除报错问题 2025-02-14 15:51:57 +08:00
安浩浩
3ab7ad484a 【功能完善】IoT: 增强插件启动和停止逻辑,添加异常处理,更新错误码,优化配置文件 2025-02-14 09:34:25 +08:00
Lesan
00eea85a69 chore: 添加必要注释 2025-02-14 08:31:15 +08:00
Lesan
ef29682230 feat: 添加节点类型以区分不同节点 2025-02-14 08:22:04 +08:00
YunaiV
8826f92d9d 【代码评审】BPM:移除流程图标非空校验,还是需要 URL 格式的校验 2025-02-13 19:59:01 +08:00
芋道源码
e79494a429 !1233 fix: 移除流程图标非空校验
Merge pull request !1233 from SamllNorth_Lee/fix/bpm
2025-02-13 11:58:15 +00:00
smallNorthLee
94499c0e5f fix: 移除流程图标非空校验 2025-02-13 19:49:49 +08:00
Lesan
7cf55c5300 fix: 添加节点类型以区分不同节点 2025-02-13 09:33:12 +08:00
芋道源码
ec71cd94e8 !1226 替换javax为jakarta
Merge pull request !1226 from 冰是睡着的水/N/A
2025-02-12 10:53:57 +00:00
YunaiV
f289432890 【代码评审】BPM:办理人的逻辑 2025-02-12 18:46:40 +08:00
芋道源码
25c277fbf0 !1228 BPM设计器-办理人节点
Merge pull request !1228 from Lesan/feature/bpm-办理人
2025-02-12 10:21:58 +00:00
YunaiV
c27b02beb6 【代码评审】IoT:增加相关待定项 2025-02-12 18:19:53 +08:00
Lesan
e5aede6265 feat: 办理人节点高亮处理 2025-02-12 14:10:20 +08:00
Lesan
0085b42518 feat: 办理人节点 2025-02-12 13:53:15 +08:00
冰是睡着的水
2ab2e45465 替换javax为jakarta
Signed-off-by: 冰是睡着的水 <850083043@qq.com>
2025-02-11 06:15:30 +00:00
YunaiV
542beaf47b Merge remote-tracking branch 'origin/master-jdk17' into master-jdk17 2025-02-09 12:33:30 +08:00
YunaiV
2d18f593ae V2.4.1 发布~ 2025-02-09 12:33:24 +08:00
YunaiV
cf7ff1ca8a 【功能修复】CRM:CrmBusinessController 部分权限标识不正确的问题 2025-02-09 08:11:52 +08:00
YunaiV
d75d71d7d0 1218 如果没查到用户也有可能是空结果集,不一定是null 2025-02-09 07:26:30 +08:00
YunaiV
d116e5eec1 【代码评审】IoT:物模型的维护 2025-02-09 07:26:01 +08:00
芋道源码
88ec5269d9 !1223 【代码优化】IoT: 物模型
Merge pull request !1223 from puhui999/iot
2025-02-08 23:23:34 +00:00
YunaiV
bb11fdd3fa Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/iot
# Conflicts:
#	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thinkmodelfunction/IotThinkModelFunctionController.http
2025-02-09 07:23:17 +08:00
YunaiV
949fa22509 添加项目前缀,日志无法插入 #731 2025-02-09 07:15:59 +08:00
YunaiV
19b5582a78 Merge branch 'master-jdk17' of https://github.com/YunaiV/ruoyi-vue-pro into master-jdk17 2025-02-09 07:14:42 +08:00
YunaiV
4e9583fae4 Merge branch 'feature/bpm' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into master-jdk17 2025-02-09 07:14:23 +08:00
芋道源码
b00d22a907 Merge pull request #747 from mcwindy/master-jdk17
fix typo: adminTenentId -> adminTenantId, appTenentId -> appTenantId
2025-02-09 07:13:44 +08:00
YunaiV
129e9868fb 【代码评审】BPM:ID 编码器 2025-02-09 07:04:06 +08:00
芋道源码
3cbe59ace3 !1217 fix: 无时间中缀不设置Redis过期时间
Merge pull request !1217 from Lesan/feature/bpm-new
2025-02-08 22:58:03 +00:00
YunaiV
9569c3fbed 【代码评审】BPM:触发器 - 表单修改 2025-02-09 06:55:56 +08:00
YunaiV
bc9b3715b1 【功能新增】IoT:设备拓扑图的添加 2025-02-08 21:44:49 +08:00
YunaiV
4254c06c37 【功能新增】IoT:设备注册 sub register 逻辑 2025-02-08 20:56:16 +08:00
YunaiV
5f7bb8041f 【功能新增】IoT:设备注册 register 逻辑 2025-02-08 19:31:50 +08:00
puhui999
6abd67a38c 【代码优化】IoT: 物模型 2025-02-08 17:31:11 +08:00
YunaiV
d718f80108 【代码评审】IoT:plugin 相关的实现 2025-02-08 07:39:48 +08:00
YunaiV
724512399a 【功能新增】IoT:动态脚本 js、groovy demo 2025-02-08 07:29:17 +08:00
jason
f5050807e1 【功能优化】 优化更新流程表单数据触发器 2025-02-07 22:45:03 +08:00
YunaiV
4919439b96 【功能新增】IoT:OTA 升级的下行消息的实现 2025-02-07 21:18:57 +08:00
YunaiV
795e06bc8f 【功能修复】IoT:插件还是考虑支持多租户,因此需要忽略部分场景下的租户,避免报错 2025-02-07 21:06:03 +08:00
puhui999
cf40cce552 【代码优化】S3FileClient 2025-02-07 18:10:03 +08:00
YunaiV
8fac009d4b 【功能优化】IoT:基于 review 修改 ota 的表结构设计 2025-02-07 09:44:41 +08:00
YunaiV
8ced4a0a2c 【功能新增】IoT:增加 ota 的表结构设计(100%) 2025-02-06 22:00:34 +08:00
puhui999
f141d64eb2 【依赖升级】AWS SDK for Java 1.x to 2.x 2025-02-06 16:05:56 +08:00
jason
a23b0480f1 【功能新增】 新增修改流程表单数据触发器 2025-02-06 14:14:14 +08:00
YunaiV
7bcbe9a243 Merge branch 'feature/iot' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/iot 2025-02-05 23:17:20 +08:00
YunaiV
00edd5a724 【功能新增】IoT:增加 ota 的表结构设计(90%) 2025-02-05 23:15:43 +08:00
安浩浩
6eadbba345 【代码重构】IoT:重构插件配置管理,替换 PluginInfo 为 PluginConfig 2025-02-05 21:44:23 +08:00
mcwindy
4e43958fe3 fix typo: adminTenentId -> adminTenantId, appTenentId -> appTenantId 2025-02-05 11:42:59 +08:00
YunaiV
b46e630912 【代码评审】IoT:plugin 相关的实现 2025-02-04 17:34:04 +08:00
YunaiV
d23be86164 【代码评审】IoT:物模型的实现 2025-02-04 16:35:55 +08:00
YunaiV
d24e3ad773 【功能新增】IoT:增加 alert 告警相关的表结构 2025-02-04 13:41:52 +08:00
YunaiV
f6f162ad2f 【功能新增】IoT:增加 IotRuleSceneJob 执行定时任务 2025-02-04 12:39:56 +08:00
YunaiV
8d0caaa16c 【功能新增】IoT:基于 Quartz 实现 IotSchedulerManager!为了兼容 boot 和 cloud! 2025-02-04 00:12:08 +08:00
YunaiV
2109449a89 【功能优化】IoT:增加有状态的 Bridge 实现思路 2025-02-03 20:06:18 +08:00
YunaiV
7168e60fdd 【功能新增】IoT:实现 IotRuleSceneDataBridgeAction 的 http 部分的逻辑 2025-02-03 18:33:43 +08:00
YunaiV
5e71d1fc85 【代码优化】IoT:增加 IotDataBridgeDO 数据桥梁的定义 2025-02-03 13:25:47 +08:00
YunaiV
48cfcdadc1 【代码优化】IoT:实现规则 IotRuleSceneDeviceControlAction 执行器 2025-02-03 12:05:13 +08:00
YunaiV
4f84182dab 【代码优化】IoT:简化 isTriggerConditionParameterMatched 方法,完善注释,提升可读性(虽然多了一些计算) 2025-02-02 22:21:53 +08:00
YunaiV
910bb6ca3c 【代码新增】IoT:完善 IotRuleSceneServiceImpl 的规则匹配计算,isTriggerConditionParameterMatched 函数有点长,= = 捉摸咋优化下 2025-02-02 22:08:34 +08:00
YunaiV
a4be3bb84d 【代码新增】IoT:增加 IotRuleSceneMessageHandler 处理规则场景,尝试基于 Spring El 表达式实现初步计算(部分场景) trigger 条件匹配 2025-02-02 19:44:38 +08:00
YunaiV
06749a18fc 【代码新增】IoT:增加规则引擎的 IotRuleSceneDO 场景联动的实体 2025-02-01 20:35:41 +08:00
YunaiV
f46a2fb011 【代码新增】IoT:增加 device 配置下发(设置)实现 2025-01-31 23:14:09 +08:00
YunaiV
47c281d933 【代码优化】IoT:去除 Simulation 关键字,定义更清晰 2025-01-31 22:47:04 +08:00
YunaiV
252366781d 【代码优化】IoT:优化插件 common 和 http 的配置类 2025-01-31 21:51:06 +08:00
YunaiV
a74459e94e 【代码优化】IoT:实现 IotDeviceEventReportVertxHandler 事件上行 2025-01-31 21:16:01 +08:00
YunaiV
2512f2dde8 【代码优化】IoT:优化 http 插件 IotDevicePropertyReportVertxHandler 的代码 2025-01-31 19:05:42 +08:00
YunaiV
7f0de1e34e 【代码新增】IoT:server 实现事件上行的逻辑(不包括 http 插件部分) 2025-01-31 18:04:55 +08:00
YunaiV
45b8172a61 【代码新增】IoT:实现 device 下行属性获取、设置的下行消息 2025-01-31 17:51:39 +08:00
YunaiV
b454069897 【代码新增】IoT:实现 device 下行服务调用的逻辑 2025-01-31 11:10:20 +08:00
YunaiV
7670ac19e5 【代码新增】IoT:增加 plugin 插件的心跳机制,以及 Job 超时离线 2025-01-30 23:36:18 +08:00
YunaiV
e650e75271 【代码优化】IoT:设备上行时,增加 processId,用于设备与插件的映射 2025-01-30 21:06:47 +08:00
YunaiV
30ae986c1a 【代码优化】IoT:整理 plugins 的依赖,以及对应 server 的启动逻辑 2025-01-30 20:00:54 +08:00
YunaiV
4a251b19c4 【代码新增】IoT:优化 plugins 相关的代码,包拆成分 upstream、downstream、config 三个,职责更明确 2025-01-30 18:44:50 +08:00
YunaiV
2d18e218c7 【代码新增】IoT:增加 IotDeviceDownstreamHandler 接口的定义,以及 IotDeviceDownstreamServer 的部分实现 2025-01-30 18:10:46 +08:00
YunaiV
6a7aa3c3fc 【代码优化】IoT:重构设备 message 为 control,包括上行 + 下行,更合适 2025-01-30 09:20:54 +08:00
YunaiV
2f1598a5da 【代码优化】IoT:重构设备 upstream 为 message,包括上行 + 下行 2025-01-30 08:43:41 +08:00
YunaiV
5110948db8 【代码优化】IoT:设备下行实现前,相关代码的整理 2025-01-30 07:48:28 +08:00
YunaiV
911c8c7461 【功能修改】IoT:完善设备详情的属性展示 2025-01-29 21:47:34 +08:00
YunaiV
39aaeaa298 【功能新增】IoT:增加 IotDeviceOfflineCheckJob,处理设备超时下线 2025-01-29 21:18:38 +08:00
YunaiV
eb74f753a8 【功能新增】IoT:增加 IotDeviceOnlineMessageConsumer,处理设备自动上线 2025-01-29 19:09:21 +08:00
YunaiV
7fe4dd2368 【功能新增】IoT:设备模拟更新状态 2025-01-29 15:13:41 +08:00
YunaiV
f5f8c418dc 【功能新增】IoT:设备状态上传的部分实现 2025-01-29 11:38:51 +08:00
YunaiV
f6366d9b55 【功能修改】IoT:设备状态从 status 到 state,移除已禁用 2025-01-29 00:17:08 +08:00
YunaiV
f14cc470aa 【功能修复】IoT:解决物模型的 identifier 存在驼峰情况下,无法插入和查询的情况 2025-01-28 23:16:30 +08:00
YunaiV
76ab64a255 【代码评审】IoT:物模型的管理 2025-01-28 22:24:28 +08:00
YunaiV
0b16f1678c 【功能优化】Bpm:完善设备属性的历史值 2025-01-28 12:04:59 +08:00
YunaiV
dfa03d24fd 【功能优化】Bpm:完善设备属性的历史值 2025-01-28 10:23:47 +08:00
YunaiV
8c90448670 【功能优化】Bpm:完善最新数据的接口 2025-01-28 09:23:28 +08:00
YunaiV
6071afeae8 【功能优化】Bpm:设备日志的展示 2025-01-28 08:35:07 +08:00
YunaiV
5fbfe49305 【功能优化】Bpm:设备属性上报 2025-01-28 04:56:03 +08:00
YunaiV
eb2d4fdbc0 【功能优化】IoT:同步最新代码 2025-01-28 03:58:24 +08:00
YunaiV
8236154ae8 Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/iot
# Conflicts:
#	yudao-dependencies/pom.xml
#	yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/product/IotProductStatusEnum.java
#	yudao-module-iot/yudao-module-iot-api/src/main/java/cn/iocoder/yudao/module/iot/enums/thingmodel/IotThingModelTypeEnum.java
2025-01-28 03:54:16 +08:00
YunaiV
a364153d4a 【功能优化】IoT:device 和 thingmodel 读取增加缓存 2025-01-27 22:23:31 +08:00
YunaiV
7745035fa4 【功能优化】IoT:设备属性日志表,增加 report_time 上报时间 2025-01-27 21:32:33 +08:00
YunaiV
043d82e5b6 【功能优化】IoT:完整实现 saveDeviceProperty 的设备属性日志的保存 2025-01-27 17:10:59 +08:00
YunaiV
8e80a53a8b 【功能优化】IoT:部分实现 IotDevicePropertyMessageConsumer,支持缓存的记录(差设备属性的日志记录) 2025-01-27 16:50:10 +08:00
YunaiV
b319485ca6 【功能优化】IoT:清理通用 TDengine 封装,使用 SQL 查询 2025-01-27 14:23:34 +08:00
YunaiV
8089f3a319 【功能优化】IoT:
1. DeviceDataApi => IotDeviceUpstreamApi,并新建 upstream 包
2. ThingModelMessage => IotDeviceMessage 设备消息
3. 基于 spring event 异步消费 IotDeviceMessage,并实现 IotDeviceLogMessageConsumer 记录日志
2025-01-27 14:15:07 +08:00
LesanOuO
b4f93e832f fix: 无时间中缀不设置Redis过期时间 2025-01-27 09:25:57 +08:00
YunaiV
f4ad3e9d2d 【代码评审】IoT:插件体系 2025-01-26 17:55:04 +08:00
安浩浩
7bfa830628 【代码优化】重构 HTTP插件并添加自动配置 2025-01-26 17:29:03 +08:00
jason
362b872aa8 Merge remote-tracking branch 'origin/feature/bpm' into feature/bpm 2025-01-26 15:36:48 +08:00
jason
44486afd95 【代码评审修复】 Http 触发器返回值处理修改 2025-01-26 15:36:14 +08:00
YunaiV
f47f6d934e 【代码评审】Bpm:数据报表 2025-01-26 13:53:02 +08:00
YunaiV
f8d6f1e2c4 【代码评审】Bpm:触发器的实现 2025-01-26 13:52:01 +08:00
jason
0723e4571d Merge remote-tracking branch 'origin/feature/bpm' into feature/bpm 2025-01-26 13:00:54 +08:00
jason
53789d9b80 【功能新增】 解析 Http 请求触发器返回值,修改流程变量 2025-01-26 13:00:20 +08:00
芋道源码
805e570406 !1216 feat: BPM-数据报表
Merge pull request !1216 from Lesan/feature/bpm-报表
2025-01-26 04:59:55 +00:00
LesanOuO
86f568280a feat: BPM-数据报表 2025-01-26 11:06:29 +08:00
YunaiV
8f16786471 【功能修复】Bpm:获取流程列表的时候,过滤租户 2025-01-25 17:52:50 +08:00
芋道源码
b8e56a6bde !1214 雪花ID溢出了 update apierrorlog/ApiErrorLogRespVO.java.
Merge pull request !1214 from 山野羡民/N/A
2025-01-25 09:17:14 +00:00
YunaiV
86dc3763fc 【代码评审】Bpm:数据报表 2025-01-25 17:16:57 +08:00
芋道源码
623a41d48c !1215 feat: BPM-报表
Merge pull request !1215 from Lesan/feature/bpm-报表
2025-01-25 09:08:45 +00:00
芋道源码
1ae51bde20 !1213 代码审查修改
Merge pull request !1213 from Lesan/feature/bpm-代码审查
2025-01-25 09:04:13 +00:00
LesanOuO
3342ae1be8 feat: BPM-报表 2025-01-25 13:27:37 +08:00
YunaiV
269dec1b2e 【代码评审】IoT:模拟设备数据 2025-01-25 11:46:26 +08:00
芋道源码
03462a103c !1211 [feat]:模拟设备加入 spring event
Merge pull request !1211 from alwayssuper/feature/iot
2025-01-25 03:27:53 +00:00
YunaiV
5264de077d 【代码评审】IoT:插件体系 2025-01-25 11:26:10 +08:00
山野羡民
69efe91bde 雪花ID溢出了 update apierrorlog/ApiErrorLogRespVO.java.
Signed-off-by: 山野羡民 <liyujiang_tk@yeah.net>
2025-01-25 02:47:03 +00:00
LesanOuO
f409a67d2f feat: 抄送添加摘要 2025-01-25 10:41:27 +08:00
LesanOuO
0ef8467546 feat: 摘要设置代码评审修改 2025-01-25 10:26:41 +08:00
LesanOuO
f60a4dfa6b feat: 添加Task取消理由 2025-01-25 10:24:34 +08:00
YunaiV
06634a4265 【单测修复】system:AdminAuthServiceImplTest 2025-01-25 10:09:39 +08:00
YunaiV
534c64709f 【代码评审】Bpm:触发器的实现 2025-01-25 09:49:25 +08:00
安浩浩
88ef8ba2e3 【功能完善】IoT: 删除旧版 HTTP 插件,重构 HttpPlugin 以支持独立启动,新增 VertxService 管理 HTTP 服务器逻辑,优化代码结构和异常处理,更新相关配置以提升插件性能和可维护性。 2025-01-25 00:12:06 +08:00
安浩浩
698cec92bd 【功能完善】IoT: 重命名插件模块,重构插件管理逻辑,优化代码结构,更新配置文件以支持新插件架构。 2025-01-24 23:17:26 +08:00
YunaiV
d63e315876 【代码优化】Bpm:调整 IntArrayValuable 到 ArrayValuable 2025-01-24 22:55:21 +08:00
YunaiV
bb798681f7 Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/bpm
# Conflicts:
#	yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModeConditionTypeEnum.java
#	yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java
#	yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmUserTaskRejectHandlerTypeEnum.java
2025-01-24 22:48:37 +08:00
YunaiV
0e07da807a Merge branch 'master-jdk17' of https://github.com/YunaiV/ruoyi-vue-pro into master-jdk17 2025-01-24 20:17:46 +08:00
YunaiV
295fad7d97 Merge branch 'develop' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into master-jdk17 2025-01-24 20:17:17 +08:00
芋道源码
a78e536906 Merge pull request #745 from mcwindy/desensitization_fix
【代码优化】 简化滑动脱敏处理器实现
2025-01-24 20:15:42 +08:00
芋道源码
a33fb12799 !1212 【代码优化】InEnum 不必须指定 int 类型,通过泛型指定
Merge pull request !1212 from puhui999/develop
2025-01-24 12:12:08 +00:00
YunaiV
9d9351d066 【代码评审】Bpm:更多设置-摘要设置 2025-01-24 19:59:18 +08:00
芋道源码
d11684c2ae !1210 feat: BPM-更多设置-摘要设置
Merge pull request !1210 from Lesan/feature/bpm-摘要设置
2025-01-24 11:39:15 +00:00
puhui999
d83b7cd5b9 【代码优化】InEnum 不必须指定 int 类型,通过泛型指定 2025-01-24 17:31:41 +08:00
alwayssuper
03d4f60e80 [fix]:code review 2025-01-24 15:56:01 +08:00
LesanOuO
39e68af120 fix: 取消摘要数组倒序 2025-01-24 13:22:57 +08:00
LesanOuO
b5ba500b60 feat: BPM-更多设置-摘要设置 2025-01-24 13:11:41 +08:00
LesanOuO
8f4543a270 fix: 通过new HashMap替换clone 2025-01-24 10:30:28 +08:00
LesanOuO
bcfa08f8ef fix: 类更名CustomTitleSetting->TitleSetting 2025-01-24 10:28:49 +08:00
mcwindy
408f88da54 【代码优化】 简化滑动脱敏处理器实现 2025-01-24 02:05:46 +08:00
YunaiV
bd7f9761d8 https://gitee.com/zhijiantianya/yudao-cloud/pulls/165/files 2025-01-23 20:16:19 +08:00
YunaiV
846d0605d8 【代码评审】IoT:清理流程的实例们 2025-01-23 20:08:31 +08:00
芋道源码
281d3d1d53 !1194 BPM:流程清理(添加任务和抄送)
Merge pull request !1194 from Lesan/feature/bpm-流程清理
2025-01-23 12:06:17 +00:00
Dincat
dcf55c3533 !1196 修复角色更新时,记录的操作日志将更新前的数据记录为空的问题
* 修复角色更新时,记录的操作日志将更新前的数据记录为空的问题
2025-01-23 12:05:09 +00:00
YunaiV
a0ff1244e5 【代码评审】Bpm:更多设置-自定义标题 2025-01-23 19:19:30 +08:00
alwayssuper
4f962bd1f7 [fix]:code review 2025-01-23 16:54:45 +08:00
芋道源码
e99eb5c813 !1206 feat: BPM-更多设置-标题设置
Merge pull request !1206 from Lesan/feature/bpm-标题设置
2025-01-23 04:58:00 +00:00
芋道源码
2ceca1a20d !1207 【缺陷修复】商城
Merge pull request !1207 from puhui999/develop
2025-01-23 04:41:00 +00:00
puhui999
e89e3c946d 【缺陷修复】商城:虚拟成团后所有成员添加虚拟成团标记 2025-01-23 12:08:05 +08:00
LesanOuO
de297e3a78 feat: BPM-更多设置-标题设置 2025-01-23 12:06:12 +08:00
puhui999
30a24601cd 【缺陷修复】商城:修复积分不够还能兑换商品的 bug 2025-01-23 11:55:30 +08:00
puhui999
d3352308d4 【代码优化】积分商城 2025-01-23 10:49:08 +08:00
YunaiV
61e3275231 【代码评审】Bpm:触发器的实现 2025-01-23 08:13:06 +08:00
芋道源码
808f23ec49 !1204 BPM:更多设置-自动去重
Merge pull request !1204 from Lesan/feature/bpm-自动去重
2025-01-22 23:59:12 +00:00
jason
8a8dc67d72 【功能新增】新增Simple 设计器触发器节点 2025-01-22 22:18:37 +08:00
alwayssuper
2b27085ec2 feat:simulator2 2025-01-22 22:08:10 +08:00
alwayssuper
13ced8a114 Merge branch 'feature/iot' of https://gitee.com/alwayssuper/ruoyi-vue-pro into feature/iot
 Conflicts:
	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/ExampleService.java
2025-01-22 20:30:19 +08:00
alwayssuper
0707792755 [fix]:code review 2025-01-22 16:59:00 +08:00
LesanOuO
e548d4454e fix: 优化代码逻辑 2025-01-22 10:08:58 +08:00
LesanOuO
6317a4f361 fix: 自动通过枚举变更 2025-01-22 09:52:06 +08:00
alwayssuper
e7999749fb Merge branch 'feature/iot' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into origin/feature/iot
# Conflicts:
#	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/ExampleService.java
2025-01-22 08:32:56 +08:00
alwayssuper
3c3a12acc1 Merge branch 'feature/iot' of https://gitee.com/alwayssuper/ruoyi-vue-pro into feature/iot
 Conflicts:
	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/plugin/ExampleService.java
2025-01-21 20:48:30 +08:00
alwayssuper
5b75a4fb8a Merge branch 'feature/iot' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/iot 2025-01-21 20:46:32 +08:00
YunaiV
916024b891 【功能新增】IoT:验证通过独立、内嵌模式的调用 2025-01-21 19:38:41 +08:00
安浩浩
a152f6d98f 【功能完善】IoT: 删除旧版 HTTP 插件,新增 HTTP 和 MQTT 插件,重构插件管理逻辑,优化代码结构,支持 EMQX 插件,更新相关配置文件。 2025-01-21 18:18:28 +08:00
YunaiV
ca1d9e6896 【代码评审】Bpm:更多设置-自动去重(审批) 2025-01-21 09:37:38 +08:00
芋道源码
c55f77f001 !1202 BPM-更多设置-自动去重
Merge pull request !1202 from Lesan/feature/bpm-自动去重
2025-01-21 01:21:08 +00:00
芋道源码
3ec5b19a77 !1200 BPM:更多设置-流程编码
Merge pull request !1200 from Lesan/feature/bpm-流程编码
2025-01-21 01:18:45 +00:00
Lesan
0642c5ebe4 fix: 通过理由更改 2025-01-21 08:39:32 +08:00
Lesan
d8bc3a46e5 feat: BPM-更多设置-自动去重 2025-01-21 08:24:42 +08:00
alwayssuper
0cb148a33d Merge branch 'feature/iot' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/iot 2025-01-20 20:31:08 +08:00
YunaiV
d608c4b984 【功能实现】IoT:增加 HttpPlugin 独立启动的 demo 2025-01-20 20:02:46 +08:00
YunaiV
3647fd3686 【代码优化】IoT:移除 ServiceRegistry,使用 SpringUtils 替代 2025-01-20 19:28:03 +08:00
安浩浩
a85890d958 【功能完善】IoT: 添加插件目录配置,重构 SpringPluginManager 实例化逻辑,删除不再使用的 ExampleService 类以优化代码结构。 2025-01-20 17:03:27 +08:00
alwayssuper
9f3730d5d9 [fix]:code review 2025-01-20 16:31:37 +08:00
Lesan
4a1fe1f307 feat: BPM-更多设置-流程编码 2025-01-20 13:35:45 +08:00
YunaiV
f486790def 【代码评审】Bpm:更多设置-流程编码 2025-01-20 12:41:33 +08:00
芋道源码
4afdcb4699 !1199 BPM-更多设置-流程编码
Merge pull request !1199 from Lesan/feature/bpm-流程编码
2025-01-20 04:34:09 +00:00
Lesan
daa718a444 feat: BPM-更多设置-流程编码 2025-01-20 11:03:39 +08:00
YunaiV
c4fcd0564c 【功能评审】Bpm:将 routerDefaultFlowId 重命名,更好理解 2025-01-20 09:18:59 +08:00
芋道源码
5c5448b959 !1198 BPM:重命名RouterCondition->RouterSetting
Merge pull request !1198 from Lesan/feature/bpm-路由分支
2025-01-20 01:17:25 +00:00
Lesan
e55be75357 fix: 重命名RouterCondition->RouterSetting 2025-01-20 08:39:37 +08:00
alwayssuper
495ae4526b Merge branch 'feature/iot' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into origin/feature/iot 2025-01-20 08:02:57 +08:00
jason
d36fc98f01 Merge remote-tracking branch 'origin/feature/bpm' into feature/bpm 2025-01-19 18:33:32 +08:00
jason
96875b1e02 【代码评审修改】 条件节点设置修改 2025-01-19 18:33:02 +08:00
YunaiV
081e716e72 【功能评审】Bpm:是否填写审批意见 2025-01-19 18:27:50 +08:00
芋道源码
89728fe6ff !1195 BPM:审批意见
Merge pull request !1195 from Lesan/feature/bpm-审批意见
2025-01-19 10:26:14 +00:00
YunaiV
fcf0b562fd 【功能评审】Bpm:发起人是否允许撤销 2025-01-19 18:22:34 +08:00
芋道源码
480ed8db80 !1197 BPM:更多设置-提交人权限
Merge pull request !1197 from Lesan/feature/bpm-提交人权限
2025-01-19 10:20:48 +00:00
Lesan
68b7a9a954 feat: 提交人权限-允许撤销审批中的申请 2025-01-19 16:42:21 +08:00
Lesan
d758da489c feat: 审批意见 2025-01-19 10:14:56 +08:00
安浩浩
5524324554 【功能完善】IoT: 删除不再使用的 MQTT RPC 相关配置和实现,包括 MqttConfig、RpcServer、RpcClient 和 RpcController 类,优化代码结构以清理未使用的组件。 2025-01-19 09:53:57 +08:00
Lesan
6c3afa7c96 feat: 添加清理抄送和任务 2025-01-19 08:39:32 +08:00
YunaiV
2725376b91 【缺陷修复】商城:分销用户累计已提现查询使用 WITHDRAW_SUCCESS 体现成功状态查询 2025-01-17 20:04:09 +08:00
芋道源码
5c2af8e32d !1188 商城缺陷修复
Merge pull request !1188 from puhui999/develop
2025-01-17 12:02:39 +00:00
YunaiV
25c86c05b3 【拼写修正】全局:遇到改成芋道,拼写错误 2025-01-17 19:57:33 +08:00
YunaiV
a985ba923e 【功能优化】全局:清理掉多余重复的 "/*.html" 2025-01-17 19:56:09 +08:00
YunaiV
70538320c8 【功能评审】Bpm:清理流程 2025-01-17 19:29:07 +08:00
芋道源码
a16f1120e0 !1193 BPM:流程清理
Merge pull request !1193 from Lesan/feature/bpm-流程清理
2025-01-17 11:25:47 +00:00
YunaiV
bcc535e034 【功能评审】Bpm:跳过表达式 2025-01-17 19:20:22 +08:00
芋道源码
8f994af718 !1192 BPMN设计器:跳过表达式
Merge pull request !1192 from Lesan/feature/bpm-跳过表达式
2025-01-17 11:15:16 +00:00
YunaiV
9c4648e545 【功能评审】Bpm:审批签名 2025-01-17 19:07:58 +08:00
芋道源码
40082c9b49 !1191 BPM:签名功能完善
Merge pull request !1191 from Lesan/feature/bpm-n
2025-01-17 11:03:49 +00:00
Lesan
62b271fbf8 feat: 模型的流程数据清理功能 2025-01-17 14:01:02 +08:00
Lesan
93eb75552a feat: bpmn跳过表达式 2025-01-17 09:57:06 +08:00
jason
1784aab186 Merge remote-tracking branch 'origin/feature/bpm' into feature/bpm 2025-01-16 21:03:34 +08:00
jason
610f39de05 【缺陷修复】 解决并行分支拒绝任务时,流程未结束问题 2025-01-16 21:02:49 +08:00
Lesan
aba9626384 feat: 审批签名代码评审 2025-01-16 13:56:37 +08:00
YunaiV
31c2e81dd2 【功能评审】Bpm:审批签名 2025-01-16 13:12:23 +08:00
芋道源码
56f3177a81 !1190 Simple设计器完善及优化
Merge pull request !1190 from Lesan/feature/bpm-n
2025-01-16 05:04:50 +00:00
安浩浩
890d304340 【功能完善】IoT: 更新 Vert.x 版本至 4.5.1,新增 EMQX 插件及其相关配置,重构 MQTT 插件以支持 Vert.x MQTT 服务器,优化插件启动和停止逻辑,更新插件描述信息。 2025-01-15 22:37:07 +08:00
alwayssuper
62a868f497 feat:simulator1 2025-01-12 20:13:41 +08:00
alwayssuper
f1d887d0e0 Merge branch 'feature/iot' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into feature/iot
 Conflicts:
	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/tdengine/IotDeviceLogDataMapper.java
	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/IotDeviceLogDataService.java
	yudao-server/src/main/resources/application-local.yaml
2025-01-10 20:46:43 +08:00
alwayssuper
d9dda54cce local 2025-01-10 20:42:00 +08:00
puhui999
2451942cf2 【代码优化】商城:分销用户绑定移除 try catch,错误消息由前端进行特殊处理 2025-01-10 16:24:36 +08:00
puhui999
b1c115d221 【代码优化】商城:分销用户累计已提现查询 2025-01-10 15:50:15 +08:00
LesanOuO
b2d81e0763 fix: 通过FixedValue属性方式获取监听器扩展字段 2025-01-10 12:47:31 +08:00
Lesan
71b86e6207 feat: 完善UserTask审批签名 2025-01-09 13:49:11 +08:00
YunaiV
deab8c1cc6 【代码评审】IoT:设备日志 TDengine 表与模拟设备 2025-01-09 12:36:30 +08:00
Lesan
267435c90f chore: 删除TODO 2025-01-09 09:37:48 +08:00
Lesan
f1296c59eb fix: restTemplate请求添加try-catch 2025-01-09 09:36:44 +08:00
Lesan
533e5c3bf5 refactor: 优化ListenerHandler写入及解析 2025-01-09 09:33:16 +08:00
Lesan
a85d51cc8c fix: 添加addListenerFieldExtension方法 2025-01-09 09:26:07 +08:00
Lesan
343991445f fix: addSignEnable修改为表达式写法 2025-01-09 09:21:23 +08:00
Lesan
37cb20d964 fix: 检查所有route->router 2025-01-09 09:17:49 +08:00
Lesan
fcbf1d0765 fix: defaultFlowId由后端生成 2025-01-09 09:04:39 +08:00
YunaiV
16120820a0 Merge branch 'feature/iot' of https://gitee.com/alwayssuper/ruoyi-vue-pro into feature/iot
# Conflicts:
#	yudao-server/src/main/resources/application-local.yaml
2025-01-08 22:37:33 +08:00
YunaiV
aad0581777 【代码评审】IoT:插件机制 2025-01-08 22:36:38 +08:00
YunaiV
62aabc633c 【代码评审】BPM:Simple 设计器 - 签名实现 2025-01-08 22:11:52 +08:00
芋道源码
adc05cad66 !1189 Simple设计器功能完善
Merge pull request !1189 from Lesan/feature/bpm-n
2025-01-08 13:59:48 +00:00
安浩浩
0af6d5a758 【功能完善】IoT: 新增插件启动逻辑,重构插件状态管理,删除不再使用的状态文件更新方法 2025-01-08 17:59:32 +08:00
Lesan
fd4b6ef9e4 feat: Simple设计器-userTask-添加是否需要签名字段 2025-01-08 16:55:23 +08:00
Lesan
1fb8c9238a feat: Simple设计器-监听器完善 2025-01-08 14:22:52 +08:00
Lesan
60d1f1199d chore: 部分文件更名 2025-01-08 10:50:36 +08:00
Lesan
735ec9e3ac Merge remote-tracking branch 'yudao/feature/bpm' into feature/bpm-n 2025-01-08 10:26:01 +08:00
YunaiV
4b09bff64b 【代码评审】BPM:Simple 设计器 - 路由节点 2025-01-08 09:55:50 +08:00
YunaiV
4acb01c3b0 【代码评审】BPM:Simple 设计器 - userTask 监听器 2025-01-08 09:40:38 +08:00
Lesan
d33afd7a9a fix: routeGroups命名问题 2025-01-08 08:52:24 +08:00
安浩浩
d39e2c1bc4 【代码优化】修改配置文件 2025-01-07 23:21:49 +08:00
安浩浩
77b89aad77 【功能完善】IoT: 集成 Vert.x 支持,重构 HTTP 插件为 Vert.x 插件 2025-01-07 23:13:57 +08:00
芋道源码
a008b313ec !1187 Simple设计器优化
Merge pull request !1187 from Lesan/feature/bpm-n
2025-01-07 14:28:24 +00:00
YunaiV
ae3c63eb29 【代码评审】BPM:Simple 设计器 - 路由分支 2025-01-07 22:27:06 +08:00
Lesan
da9f6ab76e feat: Simple设计器-监听器-添加请求默认参数 2025-01-07 19:02:20 +08:00
安浩浩
cde6ebf921 【功能完善】IoT: 更新设备数据 API,重构保存设备数据方法以使用 DTO,新增参数校验依赖,优化插件管理功能,添加插件实例上报和状态更新接口,同时更新插件信息获取逻辑,删除不再使用的文件和配置。 2025-01-07 17:44:55 +08:00
puhui999
adff185716 【缺陷修复】商城:分销用户累计已提现查询使用 WITHDRAW_SUCCESS 体现成功状态查询 2025-01-07 17:00:01 +08:00
Lesan
955ad86db4 feat: Simple设计器-监听器 2025-01-07 16:31:44 +08:00
芋道源码
34c8f4cae1 !1183 Simple设计器-路由分支
Merge pull request !1183 from Lesan/feature/bpm-路由分支
2025-01-07 04:53:55 +00:00
YunaiV
b5f6545d5a 【代码评审】BPM:Simple 设计器 - userTask 监听器 2025-01-07 12:52:50 +08:00
芋道源码
fdc6f6ed65 !1186 Simple设计器-监听器
Merge pull request !1186 from Lesan/feature/bpm-监听器
2025-01-07 04:31:57 +00:00
puhui999
1d597403c9 【缺陷修复】商城:绑定分享人失败记录 log error 日志, 前端不提示错误信息 2025-01-07 11:05:38 +08:00
YunaiV
b5856c4cfc 【代码评审】IoT:插件机制 2025-01-06 20:24:47 +08:00
安浩浩
603649d248 【功能完善】IoT: 新增 MQTT RPC 支持,包含请求和响应模型、序列化工具、MQTT 配置及客户端/服务器实现,提供示例服务和控制器接口,优化插件结构以支持 HTTP 插件的集成。 2025-01-06 18:59:26 +08:00
Lesan
46c125b030 feat: Simple设计器-监听器 2025-01-06 17:24:59 +08:00
alwayssuper
6aad4545a8 [功能添加]:物模型日志表查询与创建 模拟设备基础逻辑 2025-01-06 16:43:37 +08:00
Lesan
a13b582009 feat: Simple设计器-路由分支 2025-01-06 11:24:37 +08:00
alwayssuper
0249ca9929 [功能添加]:物模型日志表创建.1 2025-01-06 11:04:39 +08:00
Lesan
8b91b471d5 fix: 处理延迟器命名 2025-01-06 08:42:03 +08:00
alwayssuper
4f59ebf462 Merge branch 'feature/iot' of https://gitee.com/alwayssuper/ruoyi-vue-pro into origin/feature/iot 2025-01-06 07:48:03 +08:00
alwayssuper
8a7e146445 feat:simulator 2025-01-05 22:56:13 +08:00
芋道源码
44bba85a34 !1182 【缺陷修复】编辑用户时候提示用户账号由 数字、字母 组成
Merge pull request !1182 from 壁虎在家/master-jdk17
2025-01-05 10:42:57 +00:00
壁虎
92a4831644 正则表达式补上次数规则 2025-01-05 18:30:44 +08:00
alwayssuper
51b3e31e40 feat:SQLinit 2025-01-05 18:09:19 +08:00
芋道源码
0e9827e5a0 !1181 【缺陷修复】退款回收优惠券错误
Merge pull request !1181 from 壁虎在家/master-jdk17
2025-01-05 08:38:09 +00:00
壁虎
39ac9053fd 【缺陷修复】退款回收优惠券错误 2025-01-05 16:19:40 +08:00
YunaiV
92d6b8ad88 【功能修复】AI:spring-ai-tika-document-reader 引入了 cloud 依赖,导致启动报错 2025-01-04 16:00:42 +08:00
jason
6a7e7e3e44 【缺陷修复】 延迟器 trigger 时,租户 id 丢失的问题 2025-01-04 15:13:54 +08:00
YunaiV
f8fc3101c6 【代码优化】支持通过短信重置后台密码 2025-01-04 11:41:31 +08:00
杨宇庆
8d22ed8bea !1156 feat: 支持短信重置后台密码
* feat: 支持短信重置后台密码
* 重构管理后台验证码的验证流程
2025-01-04 03:39:33 +00:00
YunaiV
182511b65b !162 移除用户账号正则检验中长度限制 2025-01-04 08:39:39 +08:00
alwayssuper
c31dfcc25f feat:物模型列表 2025-01-03 22:30:06 +08:00
YunaiV
686a64ccda 【代码优化】工作流:延迟器的支持 2025-01-03 19:53:11 +08:00
YunaiV
9512dcf812 【代码优化】工作流:延迟器的支持 2025-01-03 19:53:06 +08:00
YunaiV
e380bc34f3 【功能修复】工作流:延迟器 trigger 时,租户 id 丢失的问题 2025-01-03 19:52:51 +08:00
芋道源码
b48052a5f5 !1175 Simple设计器-延迟器
Merge pull request !1175 from Lesan/feature/bpm-延迟器
2025-01-03 11:09:34 +00:00
Lesan
4c8b83d46f feat: Simple设计器-延时器 2025-01-03 16:18:46 +08:00
alwayssuper
977ddcf02f Merge branch 'feature/iot' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into origin/feature/iot 2025-01-03 07:59:14 +08:00
安浩浩
dc1f9338f1 【功能完善】IoT: 添加 MQTT 插件支持,重构插件管理,新增获取运行状态插件信息接口,优化插件信息存储逻辑,移除不必要的 Spring 注解。 2025-01-01 22:34:26 +08:00
alwayssuper
eaee4642d6 [功能添加]:物模型日志表创建 2024-12-31 17:00:25 +08:00
alwayssuper
9c3aa5d95f Merge branch 'feature/iot' of https://gitee.com/alwayssuper/ruoyi-vue-pro into origin/feature/iot 2024-12-31 14:22:26 +08:00
alwayssuper
1ce9420a8d Merge branch 'feature/iot' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into origin/feature/iot 2024-12-31 14:17:50 +08:00
alwayssuper
c4c63a8fd6 feat:iotmessage 2024-12-30 22:46:24 +08:00
安浩浩
24a660b5c2 【功能完善】IoT: 更新插件管理功能,重构插件标识符为 pluginKey,删除 PluginInstanceController,添加插件实例定时更新任务,优化插件信息获取接口。 2024-12-30 18:29:46 +08:00
安浩浩
cbfbc55cd8 【功能完善】IoT: 添加插件和插件实例管理功能,包括插件信息的增删改查接口,支持文件上传和状态更新,同时优化了枚举类型的处理逻辑。 2024-12-30 12:01:58 +08:00
安浩浩
8ca9bebfd1 【代码优化】IoT: 更新 HttpPlugin,重构线程池初始化逻辑以确保线程池活跃 2024-12-30 09:33:56 +08:00
安浩浩
1a3c6756ab 【代码优化】IoT: HTTP 插件模块,删除测试代码 2024-12-29 22:33:16 +08:00
安浩浩
0e20ca342f 【代码优化】IoT: 插件管理 2024-12-29 22:31:58 +08:00
YunaiV
c9904f0994 【代码评审】IoT:ThingModel 维护 2024-12-28 21:06:19 +08:00
芋道源码
4014e1b025 !1172 【代码优化】IOT: ThingModel
Merge pull request !1172 from puhui999/iot
2024-12-28 13:02:00 +00:00
puhui999
f623a57862 Merge remote-tracking branch 'yudao/feature/iot' into iot
# Conflicts:
#	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelEvent.java
#	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/ThingModelService.java
#	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java
2024-12-27 10:54:52 +08:00
puhui999
8b7f329183 【代码优化】IOT: ThingModel 2024-12-27 10:37:16 +08:00
YunaiV
b4288bc393 【代码评审】IoT:ThingModel 维护 2024-12-26 13:56:38 +08:00
芋道源码
4075fde765 !1171 【功能完善】IOT: ThingModel 服务和事件
Merge pull request !1171 from puhui999/iot
2024-12-26 05:50:02 +00:00
puhui999
fae17e9125 【功能完善】IOT: ThingModel 服务和事件 2024-12-26 13:27:02 +08:00
puhui999
3524bfd3b3 Merge remote-tracking branch 'yudao/feature/iot' into iot
# Conflicts:
#	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotProductThingModelServiceImpl.java
2024-12-26 13:14:53 +08:00
YunaiV
7b64b7fc69 【功能优化】IoT:跨租户校验 ProductKey 和 DeviceKey,避免跨租户的 Tdengine 表冲突 2024-12-26 12:57:56 +08:00
YunaiV
09a26666ec 【功能修复】IoT:解决 device 建表的时候,tdengine 默认字段都是小写的问题,通过 _ 解决 2024-12-26 12:51:36 +08:00
YunaiV
245ab4e62d 【代码优化】IoT:修复 device 建表时,tdengine 分成 length 和 type 的情况 2024-12-26 09:55:19 +08:00
YunaiV
064b3381df 【代码重构】IoT:弱化 TdEngineDDLMapper 封装,由每个业务独立实现 2024-12-26 07:55:15 +08:00
puhui999
1f9af15e71 【功能完善】IOT: ThingModel 服务和事件 2024-12-26 00:15:57 +08:00
puhui999
38796cc4d4 【功能完善】IOT: ThingModel 服务和事件 2024-12-25 18:36:22 +08:00
puhui999
f4e9a586e3 【功能完善】IOT: ThingModel 服务和事件 2024-12-25 15:47:24 +08:00
puhui999
94cfc4a1b1 【功能完善】IOT: ThingModel 服务和事件 2024-12-25 12:15:58 +08:00
芋道源码
39896555f0 !1170 【代码优化】IOT: ThingModel 评审优化
Merge pull request !1170 from puhui999/iot
2024-12-24 01:28:58 +00:00
puhui999
ed901bc97f Merge remote-tracking branch 'yudao/feature/iot' into iot
# Conflicts:
#	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java
2024-12-23 09:47:59 +08:00
YunaiV
e01d03eefb 【代码评审】IoT:物模型日志 2024-12-21 16:33:50 +08:00
芋道源码
f580383267 !1167 【代码优化】IOT:物模型日志评审优化
Merge pull request !1167 from alwayssuper/feature/iot
2024-12-21 08:29:40 +00:00
YunaiV
e998b0c7eb 【代码评审】IoT:评审 plugin 实现 2024-12-21 16:28:25 +08:00
alwayssuper
f68f0de05d Merge branch 'feature/iot' of https://gitee.com/zhijiantianya/ruoyi-vue-pro into origin/feature/iot 2024-12-21 08:00:59 +08:00
安浩浩
a2532013ec 【新增功能】IoT: HTTP 插件模块 2024-12-20 18:57:40 +08:00
alwayssuper
8ff09fea01 [代码优化]:物模型日志评审优化 2024-12-20 14:44:46 +08:00
puhui999
767a26dd70 【代码优化】IOT: ThingModel 评审优化 2024-12-20 10:18:02 +08:00
YunaiV
b2434b7b41 【代码评审】IoT:模拟设备发送的 review 2024-12-19 21:23:07 +08:00
YunaiV
95067fd6c6 【代码评审】IoT:评审 ThingModel 的实现 2024-12-19 21:12:07 +08:00
YunaiV
92de5b1f09 Merge branch 'feature/iot' of https://gitee.com/alwayssuper/ruoyi-vue-pro into feature/iot
# Conflicts:
#	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/thingmodel/model/dataType/ThingModelArgument.java
#	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/product/IotProductServiceImpl.java
#	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageService.java
#	yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/tdengine/IotThingModelMessageServiceImpl.java
2024-12-19 21:07:25 +08:00
YunaiV
caa7198e8a 【代码评审】IoT:评审 ThingModel 的实现 2024-12-19 21:02:36 +08:00
芋道源码
666f7dbc92 !1165 【代码优化】IOT: ThingModel 评审优化
Merge pull request !1165 from puhui999/iot
2024-12-19 12:58:31 +00:00
alwayssuper
067130ecde 新增:物模型日志建表 2024-12-19 16:43:56 +08:00
puhui999
8454a10cea 【代码优化】IOT: ThingModel 评审优化 2024-12-19 11:19:10 +08:00
YunaiV
de78cc9258 【代码评审】IoT:评审 ThingModel 的实现 2024-12-18 20:41:31 +08:00
芋道源码
cb3275b405 !1164 【代码优化】IOT: 产品物模型代码评审问题优化
Merge pull request !1164 from puhui999/iot
2024-12-18 12:32:46 +00:00
puhui999
0352dda469 【代码优化】IOT: 优化一些 TODO 2024-12-18 18:07:49 +08:00
puhui999
10da1e095b 【代码优化】IOT: 产品物模型代码评审问题优化 2024-12-17 16:53:42 +08:00
安浩浩
91b817a9ec 【功能修改】IoT:删除 WelcomePlugin,新增 HttpPlugin 以支持 HTTP 服务器功能 2024-12-16 22:25:01 +08:00
YunaiV
0245aac530 【代码评审】IoT:评审 thinkmodel 的实现 2024-12-16 20:42:07 +08:00
芋道源码
0ce665ea27 !1162 【功能完善】IoT: 产品物模型
Merge pull request !1162 from puhui999/iot
2024-12-16 12:23:04 +00:00
安浩浩
ce49123043 【功能修改】IoT:更新问候语打印方法,返回问候语数量;删除不再使用的插件控制器和配置类 2024-12-16 18:43:08 +08:00
安浩浩
290fcd94d5 【功能新增】IoT:增加插件支持,包含插件API和示例控制器 2024-12-16 18:18:53 +08:00
puhui999
9e98768022 【功能完善】IoT: 产品物模型 CRUD 接口测试 2024-12-16 16:41:28 +08:00
puhui999
f930f31fab 【功能完善】IoT: 产品物模型属性相关 2024-12-16 12:12:22 +08:00
puhui999
c5894765b5 Merge remote-tracking branch 'yudao/feature/iot' into iot 2024-12-16 10:51:02 +08:00
puhui999
741096e208 【功能完善】IoT: 产品物模型 2024-12-16 10:50:48 +08:00
YunaiV
92c2717d46 【功能新增】IoT:设备管理,增加批量导入 2024-12-15 10:46:33 +08:00
YunaiV
dea8883f82 【代码评审】IoT:插件管理相关的逻辑 2024-12-15 08:38:12 +08:00
安浩浩
555310de66 【功能新增】IoT:增加插件管理功能,包含插件实例和类型的定义及相关配置 2024-12-14 21:51:17 +08:00
YunaiV
39ba4e72da 【功能新增】IoT:设备管理界面增加设备分组功能 2024-12-14 19:43:22 +08:00
YunaiV
b02e396aff 【功能新增】IoT:设备管理界面增加批量删除功能 2024-12-14 19:12:42 +08:00
YunaiV
3450658159 【功能新增】IoT:设备管理界面增加导出设备功能 2024-12-14 18:56:51 +08:00
YunaiV
b5ac526139 【功能新增】IoT:设备管理界面增加设备分组选择功能 2024-12-14 18:41:46 +08:00
YunaiV
b143bc177f 【功能新增】IoT:设备分组的管理 2024-12-14 17:00:58 +08:00
YunaiV
9041de2da5 【功能完善】IoT:设备分页筛选去除多余字段 2024-12-14 16:34:26 +08:00
YunaiV
afaf98c44f 【功能完善】IoT:设备新增、修改支持更多字段 2024-12-14 16:12:24 +08:00
YunaiV
db9c485285 【功能新增】IoT:产品导出功能 2024-12-07 20:48:19 +08:00
YunaiV
9841c869a2 【功能新增】IoT:产品详情,优化相关展示 2024-12-07 20:17:11 +08:00
YunaiV
a8c87d168a 【功能新增】IoT:产品新增时,productKey 由前端生成;同时增加 icon、picUrl 字段 2024-12-07 19:50:07 +08:00
YunaiV
9b9fd30c90 【代码重构】IoT:产品放到 product 子目录下 2024-12-07 18:50:31 +08:00
YunaiV
3a2c691af0 【功能新增】IoT:产品分类的维护 2024-12-07 16:42:56 +08:00
YunaiV
ce919d12d1 【代码评审】IoT:tdengine 操作 2024-12-05 21:45:48 +08:00
安浩浩
89fb71e857 【优化功能】 优化 tdengine 操作数据库相关代码 2024-11-09 23:44:05 +08:00
YunaiV
9b30d5d355 【代码评审】IoT:tdengine 封装的 review 2024-11-09 13:39:51 +08:00
安浩浩
e3dcea9cb3 【优化功能】 优化 tdengine 操作数据库相关代码 2024-11-08 23:07:37 +08:00
安浩浩
d7b8cf547f 【新增功能】 设备历史数据展示 2024-11-05 23:26:34 +08:00
安浩浩
624f5283b3 【新增功能】 设备数据存储和展示 2024-11-03 00:16:46 +08:00
安浩浩
3dafd31da6 【新增功能】 数据接收,JSON 标准格式数据接收 2024-10-31 21:47:54 +08:00
安浩浩
8c84ac9d8a 【新增功能】 mqtt 数据接收 2024-10-27 21:26:35 +08:00
安浩浩
88088c7987 【功能优化】 产品发布后物模型不可操作优化 2024-10-26 23:26:26 +08:00
安浩浩
7b5aa23d5c 【功能优化】 产品发布创建超级表优化 2024-10-26 23:15:31 +08:00
安浩浩
ea8dd67e9e 【代码优化】增加注解 2024-10-23 23:38:55 +08:00
安浩浩
61a0c05279 【新增功能】 集成 tdengine 2024-10-23 23:26:24 +08:00
安浩浩
05c5482715 【新增功能】 集成 tdengine 2024-10-23 23:23:40 +08:00
1097 changed files with 39729 additions and 13370 deletions

1
.gitignore vendored
View File

@@ -51,3 +51,4 @@ rebel.xml
application-my.yaml
/yudao-ui-app/unpackage/
**/.DS_Store

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -1,5 +1,5 @@
<p align="center">
<img src="https://img.shields.io/badge/Spring%20Boot-3.4.1-blue.svg" alt="Downloads">
<img src="https://img.shields.io/badge/Spring%20Boot-3.4.5-blue.svg" alt="Downloads">
<img src="https://img.shields.io/badge/Vue-3.2-blue.svg" alt="Downloads">
<img src="https://img.shields.io/github/license/YunaiV/ruoyi-vue-pro" alt="Downloads" />
</p>
@@ -149,22 +149,45 @@
### 工作流程
| | 功能 | 描述 |
|----|-------|-----------------------------------------|
| 🚀 | 流程模型 | 配置工作流的流程模型,支持 BPMN 和仿钉钉/飞书设计器 |
| 🚀 | 流程表单 | 拖动表单元素生成相应的工作流表单,覆盖 Element UI 所有的表单组件 |
| 🚀 | 用户分组 | 自定义用户分组,可用于工作流的审批分组 |
| 🚀 | 我的流程 | 查看我发起的工作流程,支持新建、取消流程等操作,高亮流程图、审批时间线 |
| 🚀 | 待办任务 | 查看自己【未】审批的工作任务,支持通过、不通过、转派、委派、退回、加减签等操作 |
| 🚀 | 已办任务 | 查看自己【已】审批的工作任务,支持流程预测,展示未来审批人信息 |
| 🚀 | OA 请假 | 作为业务自定义接入工作流的使用示例,只需创建请求对应的工作流程,即可进行审批 |
![功能图](/.image/common/bpm-feature.png)
基于 Flowable 构建,可支持信创(国产)数据库,满足中国特色流程操作:
| BPMN 设计器 | 钉钉/飞书设计器 |
|------------------------------|--------------------------------|
| ![](/.image/工作流设计器-bpmn.jpg) | ![](/.image/工作流设计器-simple.jpg) |
> 历经头部企业生产验证,工作流引擎须标配仿钉钉/飞书 + BPMN 双设计器!!!
>
> 前者支持轻量配置简单流程,后者实现复杂场景深度编排
| 功能列表 | 功能描述 | 是否完成 |
|------------|-------------------------------------------------------------------------------------|------|
| SIMPLE 设计器 | 仿钉钉/飞书设计器支持拖拽搭建表单流程10 分钟快速完成审批流程配置 | ✅ |
| BPMN 设计器 | 基于 BPMN 标准开发,适配复杂业务场景,满足多层级审批及流程自动化需求 | ✅ |
| 会签 | 同一个审批节点设置多个人(如 A、B、C 三人,三人会同时收到待办任务),需全部同意之后,审批才可到下一审批节点 | ✅ |
| 或签 | 同一个审批节点设置多个人,任意一个人处理后,就能进入下一个节点 | ✅ |
| 依次审批 | (顺序会签)同一个审批节点设置多个人(如 A、B、C 三人),三人按顺序依次收到待办,即 A 先审批A 提交后 B 才能审批,需全部同意之后,审批才可到下一审批节点 | ✅ |
| 抄送 | 将审批结果通知给抄送人,同一个审批默认排重,不重复抄送给同一人 | ✅ |
| 驳回 | (退回)将审批重置发送给某节点,重新审批。可驳回至发起人、上一节点、任意节点 | ✅ |
| 转办 | A 转给其 B 审批B 审批后,进入下一节点 | ✅ |
| 委派 | A 转给其 B 审批B 审批后,转给 AA 继续审批后进入下一节点 | ✅ |
| 加签 | 允许当前审批人根据需要,自行增加当前节点的审批人,支持向前、向后加签 | ✅ |
| 减签 | (取消加签)在当前审批人操作之前,减少审批人 | ✅ |
| 撤销 | (取消流程)流程发起人,可以对流程进行撤销处理 | ✅ |
| 终止 | 系统管理员,在任意节点终止流程实例 | ✅ |
| 表单权限 | 支持拖拉拽配置表单,每个审批节点可配置只读、编辑、隐藏权限 | ✅ |
| 超时审批 | 配置超时审批时间,超时后自动触发审批通过、不通过、驳回等操作 | ✅ |
| 自动提醒 | 配置提醒时间,到达时间后自动触发短信、邮箱、站内信等通知提醒,支持自定义重复提醒频次 | ✅ |
| 父子流程 | 主流程设置子流程节点,子流程节点会自动触发子流程。子流程结束后,主流程才会执行(继续往下下执行),支持同步子流程、异步子流程 | ✅ |
| 条件分支 | (排它分支)用于在流程中实现决策,即根据条件选择一个分支执行 | ✅ |
| 并行分支 | 允许将流程分成多条分支,不进行条件判断,所有分支都会执行 | ✅ |
| 包容分支 | (条件分支 + 并行分支的结合体)允许基于条件选择多条分支执行,但如果没有任何一个分支满足条件,则可以选择默认分支 | ✅ |
| 路由分支 | 根据条件选择一个分支执行(重定向到指定配置节点),也可以选择默认分支执行(继续往下执行) | ✅ |
| 触发节点 | 执行到该节点,触发 HTTP 请求、HTTP 回调、更新数据、删除数据等 | ✅ |
| 延迟节点 | 执行到该节点,审批等待一段时间再执行,支持固定时长、固定日期等 | ✅ |
| 拓展设置 | 流程前置/后置通知,节点(任务)前置、后置通知,流程报表,自动审批去重,自定流程编号、标题、摘要,流程报表等 | ✅ |
### 支付系统
| | 功能 | 描述 |
@@ -285,7 +308,7 @@
| 框架 | 说明 | 版本 | 学习指南 |
|---------------------------------------------------------------------------------------------|------------------|----------------|----------------------------------------------------------------|
| [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 3.4.1 | [文档](https://github.com/YunaiV/SpringBoot-Labs) |
| [Spring Boot](https://spring.io/projects/spring-boot) | 应用开发框架 | 3.4.5 | [文档](https://github.com/YunaiV/SpringBoot-Labs) |
| [MySQL](https://www.mysql.com/cn/) | 数据库服务器 | 5.7 / 8.0+ | |
| [Druid](https://github.com/alibaba/druid) | JDBC 连接池、监控组件 | 1.2.23 | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
| [MyBatis Plus](https://mp.baomidou.com/) | MyBatis 增强工具包 | 3.5.7 | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao) |

View File

@@ -32,7 +32,7 @@
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<properties>
<revision>2.4.0-SNAPSHOT</revision>
<revision>2.5.0-SNAPSHOT</revision>
<!-- Maven 相关 -->
<java.version>17</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
@@ -42,7 +42,7 @@
<flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version>
<!-- 看看咋放到 bom 里 -->
<lombok.version>1.18.36</lombok.version>
<spring.boot.version>3.4.1</spring.boot.version>
<spring.boot.version>3.4.5</spring.boot.version>
<mapstruct.version>1.6.3</mapstruct.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

View File

@@ -2,16 +2,16 @@
"local": {
"baseUrl": "http://127.0.0.1:48080/admin-api",
"token": "test1",
"adminTenentId": "1",
"adminTenantId": "1",
"appApi": "http://127.0.0.1:48080/app-api",
"appToken": "test247",
"appTenentId": "1"
"appTenantId": "1"
},
"gateway": {
"baseUrl": "http://127.0.0.1:8888/admin-api",
"token": "test1",
"adminTenentId": "1",
"adminTenantId": "1",
"appApi": "http://127.0.0.1:8888/app-api",
"appToken": "test1",

File diff suppressed because it is too large Load Diff

View File

@@ -14,17 +14,17 @@
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
<properties>
<revision>2.4.0-SNAPSHOT</revision>
<revision>2.5.0-SNAPSHOT</revision>
<flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version>
<!-- 统一依赖管理 -->
<spring.boot.version>3.4.1</spring.boot.version>
<spring.boot.version>3.4.5</spring.boot.version>
<!-- Web 相关 -->
<springdoc.version>2.7.0</springdoc.version>
<springdoc.version>2.8.3</springdoc.version>
<knife4j.version>4.6.0</knife4j.version>
<!-- DB 相关 -->
<druid.version>1.2.24</druid.version>
<mybatis.version>3.5.17</mybatis.version>
<mybatis-plus.version>3.5.9</mybatis-plus.version>
<mybatis.version>3.5.19</mybatis.version>
<mybatis-plus.version>3.5.10.1</mybatis-plus.version>
<dynamic-datasource.version>4.3.1</dynamic-datasource.version>
<mybatis-plus-join.version>1.4.13</mybatis-plus-join.version>
<easy-trans.version>3.0.6</easy-trans.version>
@@ -32,13 +32,14 @@
<dm8.jdbc.version>8.1.3.140</dm8.jdbc.version>
<kingbase.jdbc.version>8.6.0</kingbase.jdbc.version>
<opengauss.jdbc.version>5.1.0</opengauss.jdbc.version>
<taos.version>3.3.3</taos.version>
<!-- 消息队列 -->
<rocketmq-spring.version>2.3.1</rocketmq-spring.version>
<rocketmq-spring.version>2.3.2</rocketmq-spring.version>
<!-- 服务保障相关 -->
<lock4j.version>2.2.7</lock4j.version>
<!-- 监控相关 -->
<skywalking.version>9.0.0</skywalking.version>
<spring-boot-admin.version>3.4.1</spring-boot-admin.version>
<spring-boot-admin.version>3.4.5</spring-boot-admin.version>
<opentracing.version>0.33.0</opentracing.version>
<!-- Test 测试相关 -->
<podam.version>8.0.2.RELEASE</podam.version>
@@ -47,31 +48,34 @@
<!-- Bpm 工作流相关 -->
<flowable.version>7.0.1</flowable.version>
<!-- 工具类相关 -->
<captcha-plus.version>2.0.3</captcha-plus.version>
<anji-plus-captcha.version>1.4.0</anji-plus-captcha.version>
<jsoup.version>1.18.3</jsoup.version>
<lombok.version>1.18.36</lombok.version>
<mapstruct.version>1.6.3</mapstruct.version>
<hutool-5.version>5.8.35</hutool-5.version>
<hutool-6.version>6.0.0-M19</hutool-6.version>
<easyexcel.verion>4.0.3</easyexcel.verion>
<easyexcel.version>4.0.3</easyexcel.version>
<velocity.version>2.4.1</velocity.version>
<fastjson.version>1.2.83</fastjson.version>
<guava.version>33.4.0-jre</guava.version>
<guava.version>33.4.8-jre</guava.version>
<transmittable-thread-local.version>2.14.5</transmittable-thread-local.version>
<commons-net.version>3.11.1</commons-net.version>
<jsch.version>0.1.55</jsch.version>
<tika-core.version>2.9.2</tika-core.version>
<tika-core.version>3.1.0</tika-core.version>
<ip2region.version>2.7.0</ip2region.version>
<bizlog-sdk.version>3.0.6</bizlog-sdk.version>
<netty.version>4.1.116.Final</netty.version>
<mqtt.version>1.2.5</mqtt.version>
<pf4j-spring.version>0.9.0</pf4j-spring.version>
<vertx.version>4.5.13</vertx.version>
<!-- 三方云服务相关 -->
<commons-io.version>2.17.0</commons-io.version>
<commons-compress.version>1.27.1</commons-compress.version>
<aws-java-sdk-s3.version>1.12.777</aws-java-sdk-s3.version>
<justauth.version>2.0.5</justauth.version>
<jimureport.version>1.8.1</jimureport.version>
<weixin-java.version>4.6.0</weixin-java.version>
<awssdk.version>2.30.14</awssdk.version>
<justauth.version>1.16.7</justauth.version>
<justauth-starter.version>1.4.0</justauth-starter.version>
<jimureport.version>1.9.4</jimureport.version>
<weixin-java.version>4.7.5.B</weixin-java.version>
</properties>
<dependencyManagement>
@@ -171,6 +175,7 @@
<version>${druid.version}</version>
</dependency>
<dependency>
<!-- 注意:必须声明,避免 flowable 和 mybatis-plus 引入的 mybatis 版本不一致!!! -->
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
@@ -263,6 +268,12 @@
<version>${kingbase.jdbc.version}</version>
</dependency>
<dependency>
<groupId>com.taosdata.jdbc</groupId>
<artifactId>taos-jdbcdriver</artifactId>
<version>${taos.version}</version>
</dependency>
<!-- Job 定时任务相关 -->
<dependency>
<groupId>cn.iocoder.boot</groupId>
@@ -276,7 +287,6 @@
<artifactId>yudao-spring-boot-starter-mq</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
@@ -471,7 +481,7 @@
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>${easyexcel.verion}</version>
<version>${easyexcel.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
@@ -526,9 +536,9 @@
</dependency>
<dependency>
<groupId>com.xingyuv</groupId>
<artifactId>spring-boot-starter-captcha-plus</artifactId>
<version>${captcha-plus.version}</version>
<groupId>com.anji-plus</groupId>
<artifactId>captcha-spring-boot-starter</artifactId> <!-- 验证码,一般用于登录使用 -->
<version>${anji-plus-captcha.version}</version>
</dependency>
<dependency>
@@ -545,16 +555,22 @@
<!-- 三方云服务相关 -->
<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>${awssdk.version}</version>
</dependency>
<dependency>
<groupId>com.xingyuv</groupId>
<artifactId>spring-boot-starter-justauth</artifactId> <!-- 社交登陆(例如说,个人微信、企业微信等等) -->
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId> <!-- 社交登陆(例如说,个人微信、企业微信等等) -->
<version>${justauth.version}</version>
</dependency>
<dependency>
<groupId>com.xkcoding.justauth</groupId>
<artifactId>justauth-spring-boot-starter</artifactId>
<version>${justauth-starter.version}</version>
<exclusions>
<!-- 移除,避免和项目里的 hutool-all 冲突 -->
<exclusion>
<groupId>cn.hutool</groupId>
<artifactId>hutool-core</artifactId>
@@ -583,14 +599,49 @@
<groupId>org.jeecgframework.jimureport</groupId>
<artifactId>jimureport-spring-boot3-starter-fastjson2</artifactId>
<version>${jimureport.version}</version>
</dependency>
<dependency>
<groupId>org.jeecgframework.jimureport</groupId>
<artifactId>jimubi-spring-boot3-starter</artifactId>
<version>${jimureport.version}</version>
<exclusions>
<exclusion>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- PF4J -->
<dependency>
<groupId>org.pf4j</groupId>
<artifactId>pf4j-spring</artifactId>
<version>${pf4j-spring.version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Vert.x -->
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-core</artifactId>
<version>${vertx.version}</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web</artifactId>
<version>${vertx.version}</version>
</dependency>
<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-mqtt</artifactId>
<version>${vertx.version}</version>
</dependency>
<!-- MQTT -->
<dependency>
<groupId>org.eclipse.paho</groupId>

View File

@@ -0,0 +1,15 @@
package cn.iocoder.yudao.framework.common.core;
/**
* 可生成 T 数组的接口
*
* @author HUIHUI
*/
public interface ArrayValuable<T> {
/**
* @return 数组
*/
T[] array();
}

View File

@@ -1,15 +0,0 @@
package cn.iocoder.yudao.framework.common.core;
/**
* 可生成 Int 数组的接口
*
* @author 芋道源码
*/
public interface IntArrayValuable {
/**
* @return int 数组
*/
int[] array();
}

View File

@@ -1,7 +1,7 @@
package cn.iocoder.yudao.framework.common.enums;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@@ -14,12 +14,12 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum CommonStatusEnum implements IntArrayValuable {
public enum CommonStatusEnum implements ArrayValuable<Integer> {
ENABLE(0, "开启"),
DISABLE(1, "关闭");
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CommonStatusEnum::getStatus).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(CommonStatusEnum::getStatus).toArray(Integer[]::new);
/**
* 状态值
@@ -31,7 +31,7 @@ public enum CommonStatusEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@@ -1,7 +1,7 @@
package cn.iocoder.yudao.framework.common.enums;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@@ -14,7 +14,7 @@ import java.util.Arrays;
*/
@Getter
@AllArgsConstructor
public enum DateIntervalEnum implements IntArrayValuable {
public enum DateIntervalEnum implements ArrayValuable<Integer> {
DAY(1, ""),
WEEK(2, ""),
@@ -23,7 +23,7 @@ public enum DateIntervalEnum implements IntArrayValuable {
YEAR(5, "")
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(DateIntervalEnum::getInterval).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(DateIntervalEnum::getInterval).toArray(Integer[]::new);
/**
* 类型
@@ -35,7 +35,7 @@ public enum DateIntervalEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@@ -0,0 +1,17 @@
package cn.iocoder.yudao.framework.common.enums;
/**
* RPC 相关的枚举
*
* 虽然放在 yudao-spring-boot-starter-rpc 会相对合适,但是每个 API 模块需要使用到,所以暂时只好放在此处
*
* @author 芋道源码
*/
public class RpcConstants {
/**
* RPC API 的前缀
*/
public static final String RPC_API_PREFIX = "/rpc-api";
}

View File

@@ -1,6 +1,6 @@
package cn.iocoder.yudao.framework.common.enums;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@@ -13,7 +13,7 @@ import java.util.Arrays;
*/
@RequiredArgsConstructor
@Getter
public enum TerminalEnum implements IntArrayValuable {
public enum TerminalEnum implements ArrayValuable<Integer> {
UNKNOWN(0, "未知"), // 目的:在无法解析到 terminal 时,使用它
WECHAT_MINI_PROGRAM(10, "微信小程序"),
@@ -22,7 +22,7 @@ public enum TerminalEnum implements IntArrayValuable {
APP(31, "手机 App"),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(TerminalEnum::getTerminal).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(TerminalEnum::getTerminal).toArray(Integer[]::new);
/**
* 终端
@@ -34,7 +34,7 @@ public enum TerminalEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}
}

View File

@@ -1,7 +1,7 @@
package cn.iocoder.yudao.framework.common.enums;
import cn.hutool.core.util.ArrayUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@@ -12,12 +12,12 @@ import java.util.Arrays;
*/
@AllArgsConstructor
@Getter
public enum UserTypeEnum implements IntArrayValuable {
public enum UserTypeEnum implements ArrayValuable<Integer> {
MEMBER(1, "会员"), // 面向 c 端,普通用户
ADMIN(2, "管理员"); // 面向 b 端,管理后台
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(UserTypeEnum::getValue).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(UserTypeEnum::getValue).toArray(Integer[]::new);
/**
* 类型
@@ -33,7 +33,7 @@ public enum UserTypeEnum implements IntArrayValuable {
}
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}
}

View File

@@ -11,6 +11,7 @@ import java.util.function.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static cn.hutool.core.convert.Convert.toCollection;
import static java.util.Arrays.asList;
/**
@@ -335,4 +336,17 @@ public class CollectionUtils {
return list.stream().filter(Objects::nonNull).flatMap(Collection::stream).collect(Collectors.toList());
}
/**
* 转换为 LinkedHashSet
*
* @param <T> 元素类型
* @param elementType 集合中元素类型
* @param value 被转换的值
* @return {@link LinkedHashSet}
*/
@SuppressWarnings("unchecked")
public static <T> LinkedHashSet<T> toLinkedHashSet(Class<T> elementType, Object value) {
return (LinkedHashSet<T>) toCollection(LinkedHashSet.class, elementType, value);
}
}

View File

@@ -8,12 +8,16 @@ import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.List;
import static cn.hutool.core.date.DatePattern.UTC_MS_WITH_XXX_OFFSET_PATTERN;
import static cn.hutool.core.date.DatePattern.createFormatter;
/**
* 时间工具类,用于 {@link java.time.LocalDateTime}
*
@@ -26,6 +30,8 @@ public class LocalDateTimeUtils {
*/
public static LocalDateTime EMPTY = buildTime(1970, 1, 1);
public static DateTimeFormatter UTC_MS_WITH_XXX_OFFSET_FORMATTER = createFormatter(UTC_MS_WITH_XXX_OFFSET_PATTERN);
/**
* 解析时间
*

View File

@@ -7,13 +7,15 @@ import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import jakarta.servlet.http.HttpServletRequest;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Map;
/**
@@ -23,6 +25,16 @@ import java.util.Map;
*/
public class HttpUtils {
/**
* 编码 URL 参数
*
* @param value 参数
* @return 编码后的参数
*/
public static String encodeUtf8(String value) {
return URLEncoder.encode(value, StandardCharsets.UTF_8);
}
@SuppressWarnings("unchecked")
public static String replaceUrlQuery(String url, String key, String value) {
UrlBuilder builder = UrlBuilder.of(url, Charset.defaultCharset());

View File

@@ -1,14 +1,9 @@
package cn.iocoder.yudao.framework.common.util.io;
import cn.hutool.core.io.FileTypeUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.file.FileNameUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import lombok.SneakyThrows;
import java.io.ByteArrayInputStream;
import java.io.File;
/**
@@ -63,22 +58,4 @@ public class FileUtils {
return file;
}
/**
* 生成文件路径
*
* @param content 文件内容
* @param originalName 原始文件名
* @return path唯一不可重复
*/
public static String generatePath(byte[] content, String originalName) {
String sha256Hex = DigestUtil.sha256Hex(content);
// 情况一:如果存在 name则优先使用 name 的后缀
if (StrUtil.isNotBlank(originalName)) {
String extName = FileNameUtil.extName(originalName);
return StrUtil.isBlank(extName) ? sha256Hex : sha256Hex + "." + extName;
}
// 情况二:基于 content 计算
return sha256Hex + '.' + FileTypeUtil.getType(new ByteArrayInputStream(content));
}
}

View File

@@ -199,4 +199,12 @@ public class JsonUtils {
return JSONUtil.isTypeJSON(text);
}
/**
* 判断字符串是否为 JSON 类型的字符串
* @param str 字符串
*/
public static boolean isJsonObject(String str) {
return JSONUtil.isTypeJSONObject(str);
}
}

View File

@@ -1,9 +1,11 @@
package cn.iocoder.yudao.framework.common.util.number;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import java.math.BigDecimal;
import java.util.List;
/**
* 数字的工具类,补全 {@link cn.hutool.core.util.NumberUtil} 的功能
@@ -20,6 +22,18 @@ public class NumberUtils {
return StrUtil.isNotEmpty(str) ? Integer.valueOf(str) : null;
}
public static boolean isAllNumber(List<String> values) {
if (CollUtil.isEmpty(values)) {
return false;
}
for (String value : values) {
if (!NumberUtil.isNumber(value)) {
return false;
}
}
return true;
}
/**
* 通过经纬度获取地球上两点之间的距离
*

View File

@@ -98,4 +98,8 @@ public class ServletUtils {
return JakartaServletUtil.getParamMap(request);
}
public static Map<String, String> getHeaderMap(HttpServletRequest request) {
return JakartaServletUtil.getHeaderMap(request);
}
}

View File

@@ -97,12 +97,26 @@ public class SpringExpressionUtils {
* @return 执行界面
*/
public static Object parseExpression(String expressionString) {
return parseExpression(expressionString, null);
}
/**
* 从 Bean 工厂,解析 EL 表达式的结果
*
* @param expressionString EL 表达式
* @param variables 变量
* @return 执行界面
*/
public static Object parseExpression(String expressionString, Map<String, Object> variables) {
if (StrUtil.isBlank(expressionString)) {
return null;
}
Expression expression = EXPRESSION_PARSER.parseExpression(expressionString);
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new BeanFactoryResolver(SpringUtil.getApplicationContext()));
if (MapUtil.isNotEmpty(variables)) {
context.setVariables(variables);
}
return expression.getValue(context);
}

View File

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.common.util.string;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import org.aspectj.lang.JoinPoint;
import java.util.Arrays;
import java.util.Collection;
@@ -77,4 +78,30 @@ public class StrUtils {
.collect(Collectors.joining("\n"));
}
/**
* 拼接方法的参数
*
* 特殊:排除一些无法序列化的参数,如 ServletRequest、ServletResponse、MultipartFile
*
* @param joinPoint 连接点
* @return 拼接后的参数
*/
public static String joinMethodArgs(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
if (ArrayUtil.isEmpty(args)) {
return "";
}
return ArrayUtil.join(args, ",", item -> {
if (item == null) {
return "";
}
// 讨论可见https://t.zsxq.com/XUJVk、https://t.zsxq.com/MnKcL
String clazzName = item.getClass().getName();
if (StrUtil.startWithAny(clazzName, "javax.servlet", "jakarta.servlet", "org.springframework.web")) {
return "";
}
return item;
});
}
}

View File

@@ -1,9 +1,9 @@
package cn.iocoder.yudao.framework.common.validation;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.*;
@Target({
@@ -22,9 +22,9 @@ import java.lang.annotation.*;
public @interface InEnum {
/**
* @return 实现 EnumValuable 接口的
* @return 实现 ArrayValuable 接口的
*/
Class<? extends IntArrayValuable> value();
Class<? extends ArrayValuable<?>> value();
String message() default "必须在指定范围 {value}";

View File

@@ -1,37 +1,39 @@
package cn.iocoder.yudao.framework.common.validation;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class InEnumCollectionValidator implements ConstraintValidator<InEnum, Collection<Integer>> {
public class InEnumCollectionValidator implements ConstraintValidator<InEnum, Collection<?>> {
private List<Integer> values;
private List<?> values;
@Override
public void initialize(InEnum annotation) {
IntArrayValuable[] values = annotation.value().getEnumConstants();
ArrayValuable<?>[] values = annotation.value().getEnumConstants();
if (values.length == 0) {
this.values = Collections.emptyList();
} else {
this.values = Arrays.stream(values[0].array()).boxed().collect(Collectors.toList());
this.values = Arrays.asList(values[0].array());
}
}
@Override
public boolean isValid(Collection<Integer> list, ConstraintValidatorContext context) {
public boolean isValid(Collection<?> list, ConstraintValidatorContext context) {
if (list == null) {
return true;
}
// 校验通过
if (CollUtil.containsAll(values, list)) {
return true;
}
// 校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值)
// 校验不通过,自定义提示语句
context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
.replaceAll("\\{value}", CollUtil.join(list, ","))).addConstraintViolation(); // 重新添加错误提示语句

View File

@@ -1,30 +1,29 @@
package cn.iocoder.yudao.framework.common.validation;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class InEnumValidator implements ConstraintValidator<InEnum, Integer> {
public class InEnumValidator implements ConstraintValidator<InEnum, Object> {
private List<Integer> values;
private List<?> values;
@Override
public void initialize(InEnum annotation) {
IntArrayValuable[] values = annotation.value().getEnumConstants();
ArrayValuable<?>[] values = annotation.value().getEnumConstants();
if (values.length == 0) {
this.values = Collections.emptyList();
} else {
this.values = Arrays.stream(values[0].array()).boxed().collect(Collectors.toList());
this.values = Arrays.asList(values[0].array());
}
}
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
public boolean isValid(Object value, ConstraintValidatorContext context) {
// 为空时,默认不校验,即认为通过
if (value == null) {
return true;
@@ -33,7 +32,7 @@ public class InEnumValidator implements ConstraintValidator<InEnum, Integer> {
if (values.contains(value)) {
return true;
}
// 校验不通过,自定义提示语句(因为,注解上的 value 是枚举类,无法获得枚举类的实际值)
// 校验不通过,自定义提示语句
context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
context.buildConstraintViolationWithTemplate(context.getDefaultConstraintMessageTemplate()
.replaceAll("\\{value}", values.toString())).addConstraintViolation(); // 重新添加错误提示语句

View File

@@ -12,6 +12,8 @@ import net.sf.jsqlparser.schema.Table;
import java.util.List;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.skipPermissionCheck;
/**
* 基于 {@link DataPermissionRule} 的数据权限处理器
*
@@ -27,6 +29,11 @@ public class DataPermissionRuleHandler implements MultiDataPermissionHandler {
@Override
public Expression getSqlSegment(Table table, Expression where, String mappedStatementId) {
// 特殊:跨租户访问
if (skipPermissionCheck()) {
return null;
}
// 获得 Mapper 对应的数据权限的规则
List<DataPermissionRule> rules = ruleFactory.getDataPermissionRule(mappedStatementId);
if (CollUtil.isEmpty(rules)) {

View File

@@ -32,13 +32,12 @@ public class DataPermissionUtils {
* @param runnable 逻辑
*/
public static void executeIgnore(Runnable runnable) {
DataPermission dataPermission = getDisableDataPermissionDisable();
DataPermissionContextHolder.add(dataPermission);
addDisableDataPermission();
try {
// 执行 runnable
runnable.run();
} finally {
DataPermissionContextHolder.remove();
removeDataPermission();
}
}
@@ -50,14 +49,25 @@ public class DataPermissionUtils {
*/
@SneakyThrows
public static <T> T executeIgnore(Callable<T> callable) {
DataPermission dataPermission = getDisableDataPermissionDisable();
DataPermissionContextHolder.add(dataPermission);
addDisableDataPermission();
try {
// 执行 callable
return callable.call();
} finally {
DataPermissionContextHolder.remove();
removeDataPermission();
}
}
/**
* 添加忽略数据权限
*/
public static void addDisableDataPermission(){
DataPermission dataPermission = getDisableDataPermissionDisable();
DataPermissionContextHolder.add(dataPermission);
}
public static void removeDataPermission(){
DataPermissionContextHolder.remove();
}
}

View File

@@ -1,6 +1,6 @@
package cn.iocoder.yudao.framework.ip.core.enums;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@@ -13,7 +13,7 @@ import java.util.Arrays;
*/
@AllArgsConstructor
@Getter
public enum AreaTypeEnum implements IntArrayValuable {
public enum AreaTypeEnum implements ArrayValuable<Integer> {
COUNTRY(1, "国家"),
PROVINCE(2, "省份"),
@@ -21,7 +21,7 @@ public enum AreaTypeEnum implements IntArrayValuable {
DISTRICT(4, "地区"), // 县、镇、区等
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AreaTypeEnum::getType).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(AreaTypeEnum::getType).toArray(Integer[]::new);
/**
* 类型
@@ -33,7 +33,7 @@ public enum AreaTypeEnum implements IntArrayValuable {
private final String name;
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}
}

View File

@@ -4,6 +4,7 @@ import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
@@ -30,7 +31,14 @@ public class TenantProperties {
*
* 默认情况下,每个请求需要带上 tenant-id 的请求头。但是,部分请求是无需带上的,例如说短信回调、支付回调等 Open API
*/
private Set<String> ignoreUrls = Collections.emptySet();
private Set<String> ignoreUrls = new HashSet<>();
/**
* 需要忽略跨(切换)租户访问的请求
*
* 原因是:某些接口,访问的是个人信息,在跨租户是获取不到的!
*/
private Set<String> ignoreVisitUrls = Collections.emptySet();
/**
* 需要忽略多租户的表

View File

@@ -3,6 +3,8 @@ package cn.iocoder.yudao.framework.tenant.config;
import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import cn.iocoder.yudao.framework.redis.config.YudaoCacheProperties;
import cn.iocoder.yudao.framework.security.core.service.SecurityFrameworkService;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnoreAspect;
import cn.iocoder.yudao.framework.tenant.core.db.TenantDatabaseInterceptor;
import cn.iocoder.yudao.framework.tenant.core.job.TenantJobAspect;
@@ -14,16 +16,19 @@ import cn.iocoder.yudao.framework.tenant.core.security.TenantSecurityWebFilter;
import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService;
import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkServiceImpl;
import cn.iocoder.yudao.framework.tenant.core.web.TenantContextWebFilter;
import cn.iocoder.yudao.framework.tenant.core.web.TenantVisitContextInterceptor;
import cn.iocoder.yudao.framework.web.config.WebProperties;
import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler;
import cn.iocoder.yudao.module.system.api.tenant.TenantApi;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import jakarta.annotation.Resource;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.BatchStrategies;
@@ -32,14 +37,26 @@ import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.util.pattern.PathPattern;
import java.util.Map;
import java.util.Objects;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@AutoConfiguration
@ConditionalOnProperty(prefix = "yudao.tenant", value = "enable", matchIfMissing = true) // 允许使用 yudao.tenant.enable=false 禁用多租户
@EnableConfigurationProperties(TenantProperties.class)
public class YudaoTenantAutoConfiguration {
@Resource
private ApplicationContext applicationContext;
@Bean
public TenantFrameworkService tenantFrameworkService(TenantApi tenantApi) {
return new TenantFrameworkServiceImpl(tenantApi);
@@ -67,13 +84,60 @@ public class YudaoTenantAutoConfiguration {
// ========== WEB ==========
@Bean
public FilterRegistrationBean<TenantContextWebFilter> tenantContextWebFilter() {
public FilterRegistrationBean<TenantContextWebFilter> tenantContextWebFilter(TenantProperties tenantProperties) {
FilterRegistrationBean<TenantContextWebFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new TenantContextWebFilter());
registrationBean.setOrder(WebFilterOrderEnum.TENANT_CONTEXT_FILTER);
addIgnoreUrls(tenantProperties);
return registrationBean;
}
/**
* 如果 Controller 接口上,有 {@link TenantIgnore} 注解,那么添加到忽略的 URL 中
*
* @param tenantProperties 租户配置
*/
private void addIgnoreUrls(TenantProperties tenantProperties) {
// 获得接口对应的 HandlerMethod 集合
RequestMappingHandlerMapping requestMappingHandlerMapping = (RequestMappingHandlerMapping)
applicationContext.getBean("requestMappingHandlerMapping");
Map<RequestMappingInfo, HandlerMethod> handlerMethodMap = requestMappingHandlerMapping.getHandlerMethods();
// 获得有 @TenantIgnore 注解的接口
for (Map.Entry<RequestMappingInfo, HandlerMethod> entry : handlerMethodMap.entrySet()) {
HandlerMethod handlerMethod = entry.getValue();
if (!handlerMethod.hasMethodAnnotation(TenantIgnore.class)) {
continue;
}
// 添加到忽略的 URL 中
if (entry.getKey().getPatternsCondition() != null) {
tenantProperties.getIgnoreUrls().addAll(entry.getKey().getPatternsCondition().getPatterns());
}
if (entry.getKey().getPathPatternsCondition() != null) {
tenantProperties.getIgnoreUrls().addAll(
convertList(entry.getKey().getPathPatternsCondition().getPatterns(), PathPattern::getPatternString));
}
}
}
@Bean
public TenantVisitContextInterceptor tenantVisitContextInterceptor(TenantProperties tenantProperties,
SecurityFrameworkService securityFrameworkService) {
return new TenantVisitContextInterceptor(tenantProperties, securityFrameworkService);
}
@Bean
public WebMvcConfigurer tenantWebMvcConfigurer(TenantProperties tenantProperties,
TenantVisitContextInterceptor tenantVisitContextInterceptor) {
return new WebMvcConfigurer() {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(tenantVisitContextInterceptor)
.excludePathPatterns(tenantProperties.getIgnoreVisitUrls().toArray(new String[0]));
}
};
}
// ========== Security ==========
@Bean

View File

@@ -1,5 +1,7 @@
package cn.iocoder.yudao.framework.tenant.core.aop;
import cn.iocoder.yudao.framework.tenant.config.TenantProperties;
import java.lang.annotation.*;
/**
@@ -9,10 +11,22 @@ import java.lang.annotation.*;
* 1、Redis 场景:因为是基于 Key 实现多租户的能力,所以忽略没有意义,不像 DB 是一个 column 实现的
* 2、MQ 场景:有点难以抉择,目前可以通过 Consumer 手动在消费的方法上,添加 @TenantIgnore 进行忽略
*
* 特殊:
* 1、如果添加到 Controller 类上,则该 URL 自动添加到 {@link TenantProperties#getIgnoreUrls()} 中
* 2、如果添加到 DO 实体类上,则它对应的表名“相当于”自动添加到 {@link TenantProperties#getIgnoreTables()} 中
*
* @author 芋道源码
*/
@Target({ElementType.METHOD})
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface TenantIgnore {
/**
* 是否开启忽略租户,默认为 true 开启
*
* 支持 Spring EL 表达式,如果返回 true 则满足条件,进行租户的忽略
*/
String enable() default "true";
}

View File

@@ -1,5 +1,6 @@
package cn.iocoder.yudao.framework.tenant.core.aop;
import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
import lombok.extern.slf4j.Slf4j;
@@ -24,7 +25,12 @@ public class TenantIgnoreAspect {
public Object around(ProceedingJoinPoint joinPoint, TenantIgnore tenantIgnore) throws Throwable {
Boolean oldIgnore = TenantContextHolder.isIgnore();
try {
TenantContextHolder.setIgnore(true);
// 计算条件,满足的情况下,才进行忽略
Object enable = SpringExpressionUtils.parseExpression(tenantIgnore.enable());
if (Boolean.TRUE.equals(enable)) {
TenantContextHolder.setIgnore(true);
}
// 执行逻辑
return joinPoint.proceed();
} finally {

View File

@@ -1,15 +1,17 @@
package cn.iocoder.yudao.framework.tenant.core.db;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.tenant.config.TenantProperties;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.toolkit.SqlParserUtils;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import java.util.HashSet;
import java.util.Set;
import java.util.HashMap;
import java.util.Map;
/**
* 基于 MyBatis Plus 多租户的功能,实现 DB 层面的多租户的功能
@@ -18,16 +20,21 @@ import java.util.Set;
*/
public class TenantDatabaseInterceptor implements TenantLineHandler {
private final Set<String> ignoreTables = new HashSet<>();
/**
* 忽略的表
*
* KEY表名
* VALUE是否忽略
*/
private final Map<String, Boolean> ignoreTables = new HashMap<>();
public TenantDatabaseInterceptor(TenantProperties properties) {
// 不同 DB 下,大小写的习惯不同,所以需要都添加进去
properties.getIgnoreTables().forEach(table -> {
ignoreTables.add(table.toLowerCase());
ignoreTables.add(table.toUpperCase());
addIgnoreTable(table, true);
});
// 在 OracleKeyGenerator 中,生成主键时,会查询这个表,查询这个表后,会自动拼接 TENANT_ID 导致报错
ignoreTables.add("DUAL");
addIgnoreTable("DUAL", true);
}
@Override
@@ -37,8 +44,40 @@ public class TenantDatabaseInterceptor implements TenantLineHandler {
@Override
public boolean ignoreTable(String tableName) {
return TenantContextHolder.isIgnore() // 情况一,全局忽略多租户
|| CollUtil.contains(ignoreTables, SqlParserUtils.removeWrapperSymbol(tableName)); // 情况二,忽略多租户的表
// 情况一,全局忽略多租户
if (TenantContextHolder.isIgnore()) {
return true;
}
// 情况二,忽略多租户的表
tableName = SqlParserUtils.removeWrapperSymbol(tableName);
Boolean ignore = ignoreTables.get(tableName.toLowerCase());
if (ignore == null) {
ignore = computeIgnoreTable(tableName);
synchronized (ignoreTables) {
addIgnoreTable(tableName, ignore);
}
}
return ignore;
}
private void addIgnoreTable(String tableName, boolean ignore) {
ignoreTables.put(tableName.toLowerCase(), ignore);
ignoreTables.put(tableName.toUpperCase(), ignore);
}
private boolean computeIgnoreTable(String tableName) {
// 找不到的表,说明不是 yudao 项目里的,不进行拦截(忽略租户)
TableInfo tableInfo = TableInfoHelper.getTableInfo(tableName);
if (tableInfo == null) {
return true;
}
// 如果继承了 TenantBaseDO 基类,显然不忽略租户
if (TenantBaseDO.class.isAssignableFrom(tableInfo.getEntityType())) {
return false;
}
// 如果添加了 @TenantIgnore 注解,显然也不忽略租户
TenantIgnore tenantIgnore = tableInfo.getEntityType().getAnnotation(TenantIgnore.class);
return tenantIgnore != null;
}
}

View File

@@ -45,6 +45,7 @@ public class TenantUtils {
*
* @param tenantId 租户编号
* @param callable 逻辑
* @return 结果
*/
public static <V> V execute(Long tenantId, Callable<V> callable) {
Long oldTenantId = TenantContextHolder.getTenantId();
@@ -78,6 +79,25 @@ public class TenantUtils {
}
}
/**
* 忽略租户,执行对应的逻辑
*
* @param callable 逻辑
* @return 结果
*/
public static <V> V executeIgnore(Callable<V> callable) {
Boolean oldIgnore = TenantContextHolder.isIgnore();
try {
TenantContextHolder.setIgnore(true);
// 执行逻辑
return callable.call();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
TenantContextHolder.setIgnore(oldIgnore);
}
}
/**
* 将多租户编号,添加到 header 中
*

View File

@@ -0,0 +1,65 @@
package cn.iocoder.yudao.framework.tenant.core.web;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.security.core.service.SecurityFrameworkService;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.framework.tenant.config.TenantProperties;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
@RequiredArgsConstructor
@Slf4j
public class TenantVisitContextInterceptor implements HandlerInterceptor {
private static final String PERMISSION = "system:tenant:visit";
private final TenantProperties tenantProperties;
private final SecurityFrameworkService securityFrameworkService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 如果和当前租户编号一致,则直接跳过
Long visitTenantId = WebFrameworkUtils.getVisitTenantId(request);
if (visitTenantId == null) {
return true;
}
if (ObjUtil.equal(visitTenantId, TenantContextHolder.getTenantId())) {
return true;
}
// 必须是登录用户
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
if (loginUser == null) {
return true;
}
// 校验用户是否可切换租户
if (!securityFrameworkService.hasAnyPermissions(PERMISSION)) {
throw exception0(GlobalErrorCodeConstants.FORBIDDEN.getCode(), "您无权切换租户");
}
// 【重点】切换租户编号
loginUser.setVisitTenantId(visitTenantId);
TenantContextHolder.setTenantId(visitTenantId);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
// 【重点】清理切换,换回原租户编号
LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
if (loginUser != null && loginUser.getTenantId() != null) {
TenantContextHolder.setTenantId(loginUser.getTenantId());
}
}
}

View File

@@ -1,5 +1,6 @@
package cn.iocoder.yudao.framework.excel.core.util;
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
import cn.iocoder.yudao.framework.excel.core.handler.SelectSheetWriteHandler;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.converters.longconverter.LongStringConverter;
@@ -8,8 +9,6 @@ import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
/**
@@ -40,7 +39,7 @@ public class ExcelUtils {
.registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度
.sheet(sheetName).doWrite(data);
// 设置 header 和 contentType。写在最后的原因是避免报错时响应 contentType 已经被修改了
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, StandardCharsets.UTF_8.name()));
response.addHeader("Content-Disposition", "attachment;filename=" + HttpUtils.encodeUtf8(filename));
response.setContentType("application/vnd.ms-excel;charset=UTF-8");
}

View File

@@ -6,6 +6,7 @@ import cn.hutool.system.SystemUtil;
import cn.iocoder.yudao.framework.common.enums.DocumentEnum;
import cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate;
import cn.iocoder.yudao.framework.mq.redis.core.job.RedisPendingMessageResendJob;
import cn.iocoder.yudao.framework.mq.redis.core.job.RedisStreamMessageCleanupJob;
import cn.iocoder.yudao.framework.mq.redis.core.pubsub.AbstractRedisChannelMessageListener;
import cn.iocoder.yudao.framework.mq.redis.core.stream.AbstractRedisStreamMessageListener;
import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;
@@ -73,6 +74,17 @@ public class YudaoRedisMQConsumerAutoConfiguration {
return new RedisPendingMessageResendJob(listeners, redisTemplate, groupName, redissonClient);
}
/**
* 创建 Redis Stream 消息清理任务
*/
@Bean
@ConditionalOnBean(AbstractRedisStreamMessageListener.class)
public RedisStreamMessageCleanupJob redisStreamMessageCleanupJob(List<AbstractRedisStreamMessageListener<?>> listeners,
RedisMQTemplate redisTemplate,
RedissonClient redissonClient) {
return new RedisStreamMessageCleanupJob(listeners, redisTemplate, redissonClient);
}
/**
* 创建 Redis Stream 集群消费的容器
*

View File

@@ -23,13 +23,13 @@ import java.util.Objects;
@AllArgsConstructor
public class RedisPendingMessageResendJob {
private static final String LOCK_KEY = "redis:pending:msg:lock";
private static final String LOCK_KEY = "redis:stream:pending-message-resend:lock";
/**
* 消息超时时间,默认 5 分钟
*
* 1. 超时的消息才会被重新投递
* 2. 由于定时任务 1 分钟一次,消息超时后不会被立即重投,极端情况下消息5分钟过期后,再等 1 分钟才会被扫瞄到
* 2. 由于定时任务 1 分钟一次,消息超时后不会被立即重投,极端情况下消息 5 分钟过期后,再等 1 分钟才会被扫瞄到
*/
private static final int EXPIRE_TIME = 5 * 60;
@@ -39,7 +39,7 @@ public class RedisPendingMessageResendJob {
private final RedissonClient redissonClient;
/**
* 一分钟执行一次,这里选择每分钟的35秒执行是为了避免整点任务过多的问题
* 一分钟执行一次,这里选择每分钟的 35 秒执行,是为了避免整点任务过多的问题
*/
@Scheduled(cron = "35 * * * * ?")
public void messageResend() {

View File

@@ -0,0 +1,72 @@
package cn.iocoder.yudao.framework.mq.redis.core.job;
import cn.iocoder.yudao.framework.mq.redis.core.RedisMQTemplate;
import cn.iocoder.yudao.framework.mq.redis.core.stream.AbstractRedisStreamMessageListener;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.StreamOperations;
import org.springframework.scheduling.annotation.Scheduled;
import java.util.List;
/**
* Redis Stream 消息清理任务
* 用于定期清理已消费的消息,防止内存占用过大
*
* @see <a href="https://www.cnblogs.com/nanxiang/p/16179519.html">记一次 redis stream 数据类型内存不释放问题</a>
*
* @author 芋道源码
*/
@Slf4j
@AllArgsConstructor
public class RedisStreamMessageCleanupJob {
private static final String LOCK_KEY = "redis:stream:message-cleanup:lock";
/**
* 保留的消息数量,默认保留最近 10000 条消息
*/
private static final long MAX_COUNT = 10000;
private final List<AbstractRedisStreamMessageListener<?>> listeners;
private final RedisMQTemplate redisTemplate;
private final RedissonClient redissonClient;
/**
* 每小时执行一次清理任务
*/
@Scheduled(cron = "0 0 * * * ?")
public void cleanup() {
RLock lock = redissonClient.getLock(LOCK_KEY);
// 尝试加锁
if (lock.tryLock()) {
try {
execute();
} catch (Exception ex) {
log.error("[cleanup][执行异常]", ex);
} finally {
lock.unlock();
}
}
}
/**
* 执行清理逻辑
*/
private void execute() {
StreamOperations<String, Object, Object> ops = redisTemplate.getRedisTemplate().opsForStream();
listeners.forEach(listener -> {
try {
// 使用 XTRIM 命令清理消息,只保留最近的 MAX_LEN 条消息
Long trimCount = ops.trim(listener.getStreamKey(), MAX_COUNT, true);
if (trimCount != null && trimCount > 0) {
log.info("[execute][Stream({}) 清理消息数量({})]", listener.getStreamKey(), trimCount);
}
} catch (Exception ex) {
log.error("[execute][Stream({}) 清理异常]", listener.getStreamKey(), ex);
}
});
}
}

View File

@@ -63,6 +63,11 @@
<artifactId>opengauss-jdbc</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.taosdata.jdbc</groupId>
<artifactId>taos-jdbcdriver</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>

View File

@@ -92,10 +92,36 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> {
default T selectOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2,
SFunction<T, ?> field3, Object value3) {
return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2)
.eq(field3, value3));
return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2).eq(field3, value3));
}
/**
* 获取满足条件的第 1 条记录
*
* 目的:解决并发场景下,插入多条记录后,使用 selectOne 会报错的问题
*
* @param field 字段名
* @param value 字段值
* @return 实体
*/
default T selectFirstOne(SFunction<T, ?> field, Object value) {
// 如果明确使用 MySQL 等场景,可以考虑使用 LIMIT 1 进行优化
List<T> list = selectList(new LambdaQueryWrapper<T>().eq(field, value));
return CollUtil.getFirst(list);
}
default T selectFirstOne(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2) {
List<T> list = selectList(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2));
return CollUtil.getFirst(list);
}
default T selectFirstOne(SFunction<T,?> field1, Object value1, SFunction<T,?> field2, Object value2,
SFunction<T,?> field3, Object value3) {
List<T> list = selectList(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2).eq(field3, value3));
return CollUtil.getFirst(list);
}
default Long selectCount() {
return selectCount(new QueryWrapper<>());
}

View File

@@ -4,7 +4,6 @@ import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.github.yulichang.toolkit.MPJWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import org.springframework.util.StringUtils;
@@ -15,94 +14,94 @@ import java.util.function.Consumer;
* 拓展 MyBatis Plus Join QueryWrapper 类,主要增加如下功能:
* <p>
* 1. 拼接条件的方法,增加 xxxIfPresent 方法,用于判断值不存在的时候,不要拼接到条件中。
*
* 2. SFunction<S, ?> column + <S> 泛型:支持任意类字段(主表、子表、三表),推荐写法, 让编译器自动推断 S 类型
* @param <T> 数据类型
*/
public class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> {
public MPJLambdaWrapperX<T> likeIfPresent(SFunction<T, ?> column, String val) {
MPJWrappers.lambdaJoin().like(column, val);
public <S> MPJLambdaWrapperX<T> likeIfPresent(SFunction<S, ?> column, String val) {
if (StringUtils.hasText(val)) {
return (MPJLambdaWrapperX<T>) super.like(column, val);
}
return this;
}
public MPJLambdaWrapperX<T> inIfPresent(SFunction<T, ?> column, Collection<?> values) {
public <S> MPJLambdaWrapperX<T> inIfPresent(SFunction<S, ?> column, Collection<?> values) {
if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) {
return (MPJLambdaWrapperX<T>) super.in(column, values);
}
return this;
}
public MPJLambdaWrapperX<T> inIfPresent(SFunction<T, ?> column, Object... values) {
public <S> MPJLambdaWrapperX<T> inIfPresent(SFunction<S, ?> column, Object... values) {
if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) {
return (MPJLambdaWrapperX<T>) super.in(column, values);
}
return this;
}
public MPJLambdaWrapperX<T> eqIfPresent(SFunction<T, ?> column, Object val) {
public <S> MPJLambdaWrapperX<T> eqIfPresent(SFunction<S, ?> column, Object val) {
if (ObjectUtil.isNotEmpty(val)) {
return (MPJLambdaWrapperX<T>) super.eq(column, val);
}
return this;
}
public MPJLambdaWrapperX<T> neIfPresent(SFunction<T, ?> column, Object val) {
public <S> MPJLambdaWrapperX<T> neIfPresent(SFunction<S, ?> column, Object val) {
if (ObjectUtil.isNotEmpty(val)) {
return (MPJLambdaWrapperX<T>) super.ne(column, val);
}
return this;
}
public MPJLambdaWrapperX<T> gtIfPresent(SFunction<T, ?> column, Object val) {
public <S> MPJLambdaWrapperX<T> gtIfPresent(SFunction<S, ?> column, Object val) {
if (val != null) {
return (MPJLambdaWrapperX<T>) super.gt(column, val);
}
return this;
}
public MPJLambdaWrapperX<T> geIfPresent(SFunction<T, ?> column, Object val) {
public <S> MPJLambdaWrapperX<T> geIfPresent(SFunction<S, ?> column, Object val) {
if (val != null) {
return (MPJLambdaWrapperX<T>) super.ge(column, val);
}
return this;
}
public MPJLambdaWrapperX<T> ltIfPresent(SFunction<T, ?> column, Object val) {
public <S> MPJLambdaWrapperX<T> ltIfPresent(SFunction<S, ?> column, Object val) {
if (val != null) {
return (MPJLambdaWrapperX<T>) super.lt(column, val);
}
return this;
}
public MPJLambdaWrapperX<T> leIfPresent(SFunction<T, ?> column, Object val) {
public <S> MPJLambdaWrapperX<T> leIfPresent(SFunction<S, ?> column, Object val) {
if (val != null) {
return (MPJLambdaWrapperX<T>) super.le(column, val);
}
return this;
}
public MPJLambdaWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object val1, Object val2) {
if (val1 != null && val2 != null) {
return (MPJLambdaWrapperX<T>) super.between(column, val1, val2);
}
if (val1 != null) {
return (MPJLambdaWrapperX<T>) ge(column, val1);
}
if (val2 != null) {
return (MPJLambdaWrapperX<T>) le(column, val2);
}
return this;
}
public MPJLambdaWrapperX<T> betweenIfPresent(SFunction<T, ?> column, Object[] values) {
public <S> MPJLambdaWrapperX<T> betweenIfPresent(SFunction<S, ?> column, Object[] values) {
Object val1 = ArrayUtils.get(values, 0);
Object val2 = ArrayUtils.get(values, 1);
return betweenIfPresent(column, val1, val2);
}
public <S> MPJLambdaWrapperX<T> betweenIfPresent(SFunction<S, ?> column, Object val1, Object val2) {
if (val1 != null && val2 != null) {
return (MPJLambdaWrapperX<T>) super.between(column, val1, val2);
}
if (val1 != null) {
return (MPJLambdaWrapperX<T>) super.ge(column, val1);
}
if (val2 != null) {
return (MPJLambdaWrapperX<T>) super.le(column, val2);
}
return this;
}
// ========== 重写父类方法,方便链式调用 ==========
@Override
@@ -310,4 +309,41 @@ public class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> {
return this;
}
// ========== 关键重写:使 leftJoin 返回当前类型 this ==========
@Override
public <A, B> MPJLambdaWrapperX<T> leftJoin(Class<A> clazz, SFunction<A, ?> left, SFunction<B, ?> right) {
super.leftJoin(clazz, left, right);
return this;
}
@Override
public <A, B> MPJLambdaWrapperX<T> rightJoin(Class<A> clazz, SFunction<A, ?> left, SFunction<B, ?> right) {
super.rightJoin(clazz, left, right);
return this;
}
@Override
public <A, B> MPJLambdaWrapperX<T> innerJoin(Class<A> clazz, SFunction<A, ?> left, SFunction<B, ?> right) {
super.innerJoin(clazz, left, right);
return this;
}
// ========== 添加扩展 Join 支持 ext 函数式参数 ==========
public <A, B> MPJLambdaWrapperX<T> leftJoin(Class<A> clazz, SFunction<A, ?> left, SFunction<B, ?> right, Consumer<MPJLambdaWrapperX<T>> ext) {
super.leftJoin(clazz, left, right);
if (ext != null) ext.accept(this);
return this;
}
public <A, B> MPJLambdaWrapperX<T> rightJoin(Class<A> clazz, SFunction<A, ?> left, SFunction<B, ?> right, Consumer<MPJLambdaWrapperX<T>> ext) {
super.rightJoin(clazz, left, right);
if (ext != null) ext.accept(this);
return this;
}
public <A, B> MPJLambdaWrapperX<T> innerJoin(Class<A> clazz, SFunction<A, ?> left, SFunction<B, ?> right, Consumer<MPJLambdaWrapperX<T>> ext) {
super.innerJoin(clazz, left, right);
if (ext != null) ext.accept(this);
return this;
}
}

View File

@@ -13,7 +13,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
/**
* 字段字段的 TypeHandler 实现类,基于 {@link cn.hutool.crypto.symmetric.AES} 实现
* 字段字段的 TypeHandler 实现类,基于 {@link AES} 实现
* 可通过 jasypt.encryptor.password 配置项,设置密钥
*
* @author 芋道源码

View File

@@ -0,0 +1,58 @@
package cn.iocoder.yudao.framework.mybatis.core.type;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import org.apache.ibatis.type.TypeHandler;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Set;
/**
* Set<Long> 的类型转换器实现类,对应数据库的 varchar 类型
*
* @author 芋道源码
*/
@MappedJdbcTypes(JdbcType.VARCHAR)
@MappedTypes(List.class)
public class LongSetTypeHandler implements TypeHandler<Set<Long>> {
private static final String COMMA = ",";
@Override
public void setParameter(PreparedStatement ps, int i, Set<Long> strings, JdbcType jdbcType) throws SQLException {
// 设置占位符
ps.setString(i, CollUtil.join(strings, COMMA));
}
@Override
public Set<Long> getResult(ResultSet rs, String columnName) throws SQLException {
String value = rs.getString(columnName);
return getResult(value);
}
@Override
public Set<Long> getResult(ResultSet rs, int columnIndex) throws SQLException {
String value = rs.getString(columnIndex);
return getResult(value);
}
@Override
public Set<Long> getResult(CallableStatement cs, int columnIndex) throws SQLException {
String value = cs.getString(columnIndex);
return getResult(value);
}
private Set<Long> getResult(String value) {
if (value == null) {
return null;
}
return StrUtils.splitToLongSet(value, COMMA);
}
}

View File

@@ -1,7 +1,7 @@
package cn.iocoder.yudao.framework.idempotent.core.keyresolver.impl;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.framework.idempotent.core.annotation.Idempotent;
import cn.iocoder.yudao.framework.idempotent.core.keyresolver.IdempotentKeyResolver;
import org.aspectj.lang.JoinPoint;
@@ -18,7 +18,7 @@ public class DefaultIdempotentKeyResolver implements IdempotentKeyResolver {
@Override
public String resolver(JoinPoint joinPoint, Idempotent idempotent) {
String methodName = joinPoint.getSignature().toString();
String argsStr = StrUtil.join(",", joinPoint.getArgs());
String argsStr = StrUtils.joinMethodArgs(joinPoint);
return SecureUtil.md5(methodName + argsStr);
}

View File

@@ -1,7 +1,7 @@
package cn.iocoder.yudao.framework.idempotent.core.keyresolver.impl;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.framework.idempotent.core.annotation.Idempotent;
import cn.iocoder.yudao.framework.idempotent.core.keyresolver.IdempotentKeyResolver;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
@@ -19,7 +19,7 @@ public class UserIdempotentKeyResolver implements IdempotentKeyResolver {
@Override
public String resolver(JoinPoint joinPoint, Idempotent idempotent) {
String methodName = joinPoint.getSignature().toString();
String argsStr = StrUtil.join(",", joinPoint.getArgs());
String argsStr = StrUtils.joinMethodArgs(joinPoint);
Long userId = WebFrameworkUtils.getLoginUserId();
Integer userType = WebFrameworkUtils.getLoginUserType();
return SecureUtil.md5(methodName + argsStr + userId + userType);

View File

@@ -1,8 +1,8 @@
package cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.impl;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.framework.ratelimiter.core.annotation.RateLimiter;
import cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver;
import org.aspectj.lang.JoinPoint;
@@ -19,7 +19,7 @@ public class ClientIpRateLimiterKeyResolver implements RateLimiterKeyResolver {
@Override
public String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) {
String methodName = joinPoint.getSignature().toString();
String argsStr = StrUtil.join(",", joinPoint.getArgs());
String argsStr = StrUtils.joinMethodArgs(joinPoint);
String clientIp = ServletUtils.getClientIP();
return SecureUtil.md5(methodName + argsStr + clientIp);
}

View File

@@ -1,7 +1,7 @@
package cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.impl;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.framework.ratelimiter.core.annotation.RateLimiter;
import cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver;
import org.aspectj.lang.JoinPoint;
@@ -18,7 +18,7 @@ public class DefaultRateLimiterKeyResolver implements RateLimiterKeyResolver {
@Override
public String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) {
String methodName = joinPoint.getSignature().toString();
String argsStr = StrUtil.join(",", joinPoint.getArgs());
String argsStr = StrUtils.joinMethodArgs(joinPoint);
return SecureUtil.md5(methodName + argsStr);
}

View File

@@ -1,8 +1,8 @@
package cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.impl;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.system.SystemUtil;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.framework.ratelimiter.core.annotation.RateLimiter;
import cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver;
import org.aspectj.lang.JoinPoint;
@@ -19,7 +19,7 @@ public class ServerNodeRateLimiterKeyResolver implements RateLimiterKeyResolver
@Override
public String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) {
String methodName = joinPoint.getSignature().toString();
String argsStr = StrUtil.join(",", joinPoint.getArgs());
String argsStr = StrUtils.joinMethodArgs(joinPoint);
String serverNode = String.format("%s@%d", SystemUtil.getHostInfo().getAddress(), SystemUtil.getCurrentPID());
return SecureUtil.md5(methodName + argsStr + serverNode);
}

View File

@@ -1,7 +1,7 @@
package cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.impl;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
import cn.iocoder.yudao.framework.ratelimiter.core.annotation.RateLimiter;
import cn.iocoder.yudao.framework.ratelimiter.core.keyresolver.RateLimiterKeyResolver;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
@@ -19,7 +19,7 @@ public class UserRateLimiterKeyResolver implements RateLimiterKeyResolver {
@Override
public String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) {
String methodName = joinPoint.getSignature().toString();
String argsStr = StrUtil.join(",", joinPoint.getArgs());
String argsStr = StrUtils.joinMethodArgs(joinPoint);
Long userId = WebFrameworkUtils.getLoginUserId();
Integer userType = WebFrameworkUtils.getLoginUserType();
return SecureUtil.md5(methodName + argsStr + userId + userType);

View File

@@ -44,6 +44,7 @@ public class RateLimiterRedisDAO {
RateLimiterConfig config = rateLimiter.getConfig();
if (config == null) {
rateLimiter.trySetRate(RateType.OVERALL, count, rateInterval, RateIntervalUnit.SECONDS);
rateLimiter.expire(rateInterval, TimeUnit.SECONDS); // 原因参见 https://t.zsxq.com/lcR0W
return rateLimiter;
}
// 2. 如果存在,并且配置相同,则直接返回
@@ -54,6 +55,7 @@ public class RateLimiterRedisDAO {
}
// 3. 如果存在,并且配置不同,则进行新建
rateLimiter.setRate(RateType.OVERALL, count, rateInterval, RateIntervalUnit.SECONDS);
rateLimiter.expire(rateInterval, TimeUnit.SECONDS); // 原因参见 https://t.zsxq.com/lcR0W
return rateLimiter;
}

View File

@@ -2,10 +2,12 @@ package cn.iocoder.yudao.framework.signature.core.aop;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import cn.iocoder.yudao.framework.signature.core.annotation.ApiSignature;
import cn.iocoder.yudao.framework.signature.core.redis.ApiSignatureRedisDAO;
@@ -69,13 +71,17 @@ public class ApiSignatureAspect {
// 3. 将 nonce 记入缓存,防止重复使用(重点二:此处需要将 ttl 设定为允许 timestamp 时间差的值 x 2
String nonce = request.getHeader(signature.nonce());
signatureRedisDAO.setNonce(appId, nonce, signature.timeout() * 2, signature.timeUnit());
if (BooleanUtil.isFalse(signatureRedisDAO.setNonce(appId, nonce, signature.timeout() * 2, signature.timeUnit()))) {
String timestamp = request.getHeader(signature.timestamp());
log.info("[verifySignature][appId({}) timestamp({}) nonce({}) sign({}) 存在重复请求]", appId, timestamp, nonce, clientSignature);
throw new ServiceException(GlobalErrorCodeConstants.REPEATED_REQUESTS.getCode(), "存在重复请求");
}
return true;
}
/**
* 校验请求头加签参数
*
* <p>
* 1. appId 是否为空
* 2. timestamp 是否为空,请求是否已经超时,默认 10 分钟
* 3. nonce 是否为空,随机数是否 10 位以上,是否在规定时间内已经访问过了
@@ -118,7 +124,7 @@ public class ApiSignatureAspect {
/**
* 构建签名字符串
*
* <p>
* 格式为 = 请求参数 + 请求体 + 请求头 + 密钥
*
* @param signature signature
@@ -139,7 +145,7 @@ public class ApiSignatureAspect {
/**
* 获取请求头加签参数 Map
*
* @param request 请求
* @param request 请求
* @param signature 签名注解
* @return signature params
*/

View File

@@ -17,7 +17,7 @@ public class ApiSignatureRedisDAO {
/**
* 验签随机数
*
* <p>
* KEY 格式signature_nonce:%s // 参数为 随机数
* VALUE 格式String
* 过期时间:不固定
@@ -26,7 +26,7 @@ public class ApiSignatureRedisDAO {
/**
* 签名密钥
*
* <p>
* HASH 结构
* KEY 格式:%s // 参数为 appid
* VALUE 格式String
@@ -40,8 +40,8 @@ public class ApiSignatureRedisDAO {
return stringRedisTemplate.opsForValue().get(formatNonceKey(appId, nonce));
}
public void setNonce(String appId, String nonce, int time, TimeUnit timeUnit) {
stringRedisTemplate.opsForValue().set(formatNonceKey(appId, nonce), "", time, timeUnit);
public Boolean setNonce(String appId, String nonce, int time, TimeUnit timeUnit) {
return stringRedisTemplate.opsForValue().setIfAbsent(formatNonceKey(appId, nonce), "", time, timeUnit);
}
private static String formatNonceKey(String appId, String nonce) {

View File

@@ -63,13 +63,12 @@ public class ApiSignatureTest {
when(request.getReader()).thenReturn(new BufferedReader(new StringReader("test")));
// mock 方法
when(signatureRedisDAO.getAppSecret(eq(appId))).thenReturn(appSecret);
when(signatureRedisDAO.setNonce(eq(appId), eq(nonce), eq(120), eq(TimeUnit.SECONDS))).thenReturn(true);
// 调用
boolean result = apiSignatureAspect.verifySignature(apiSignature, request);
// 断言结果
assertTrue(result);
// 断言调用
verify(signatureRedisDAO).setNonce(eq(appId), eq(nonce), eq(120), eq(TimeUnit.SECONDS));
}
}

View File

@@ -7,6 +7,7 @@ import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import jakarta.servlet.DispatcherType;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.context.ApplicationContext;
@@ -128,7 +129,7 @@ public class YudaoWebSecurityConfigurerAdapter {
// ①:全局共享规则
.authorizeHttpRequests(c -> c
// 1.1 静态资源,可匿名访问
.requestMatchers(HttpMethod.GET, "/*.html", "/*.html", "/*.css", "/*.js").permitAll()
.requestMatchers(HttpMethod.GET, "/*.html", "/*.css", "/*.js").permitAll()
// 1.2 设置 @PermitAll 无需认证
.requestMatchers(HttpMethod.GET, permitAllUrls.get(HttpMethod.GET).toArray(new String[0])).permitAll()
.requestMatchers(HttpMethod.POST, permitAllUrls.get(HttpMethod.POST).toArray(new String[0])).permitAll()
@@ -142,7 +143,9 @@ public class YudaoWebSecurityConfigurerAdapter {
// ②:每个项目的自定义规则
.authorizeHttpRequests(c -> authorizeRequestsCustomizers.forEach(customizer -> customizer.customize(c)))
// ③:兜底规则,必须认证
.authorizeHttpRequests(c -> c.anyRequest().authenticated());
.authorizeHttpRequests(c -> c
.dispatcherTypeMatchers(DispatcherType.ASYNC).permitAll() // WebFlux 异步请求无需认证目的SSE 场景
.anyRequest().authenticated());
// 添加 Token Filter
httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

View File

@@ -56,6 +56,10 @@ public class LoginUser {
*/
@JsonIgnore
private Map<String, Object> context;
/**
* 访问的租户编号
*/
private Long visitTenantId;
public void setContext(String key, Object value) {
if (context == null) {

View File

@@ -9,6 +9,7 @@ import lombok.AllArgsConstructor;
import java.util.Arrays;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.skipPermissionCheck;
/**
* 默认的 {@link SecurityFrameworkService} 实现类
@@ -27,6 +28,12 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
@Override
public boolean hasAnyPermissions(String... permissions) {
// 特殊:跨租户访问
if (skipPermissionCheck()) {
return true;
}
// 权限校验
Long userId = getLoginUserId();
if (userId == null) {
return false;
@@ -41,6 +48,12 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
@Override
public boolean hasAnyRoles(String... roles) {
// 特殊:跨租户访问
if (skipPermissionCheck()) {
return true;
}
// 权限校验
Long userId = getLoginUserId();
if (userId == null) {
return false;
@@ -55,6 +68,12 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
@Override
public boolean hasAnyScopes(String... scope) {
// 特殊:跨租户访问
if (skipPermissionCheck()) {
return true;
}
// 权限校验
LoginUser user = SecurityFrameworkUtils.getLoginUser();
if (user == null) {
return false;

View File

@@ -1,6 +1,7 @@
package cn.iocoder.yudao.framework.security.core.util;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.security.core.LoginUser;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
@@ -137,4 +138,21 @@ public class SecurityFrameworkUtils {
return authenticationToken;
}
/**
* 是否条件跳过权限校验,包括数据权限、功能权限
*
* @return 是否跳过
*/
public static boolean skipPermissionCheck() {
LoginUser loginUser = getLoginUser();
if (loginUser == null) {
return false;
}
if (loginUser.getVisitTenantId() == null) {
return false;
}
// 重点:跨租户访问时,无法进行权限校验
return ObjUtil.notEqual(loginUser.getVisitTenantId(), loginUser.getTenantId());
}
}

View File

@@ -146,9 +146,11 @@ public class ApiAccessLogFilter extends ApiRequestFilter {
if (handlerMethod != null) {
Tag tagAnnotation = handlerMethod.getBeanType().getAnnotation(Tag.class);
Operation operationAnnotation = handlerMethod.getMethodAnnotation(Operation.class);
String operateModule = accessLogAnnotation != null ? accessLogAnnotation.operateModule() :
String operateModule = accessLogAnnotation != null && StrUtil.isNotBlank(accessLogAnnotation.operateModule()) ?
accessLogAnnotation.operateModule() :
tagAnnotation != null ? StrUtil.nullToDefault(tagAnnotation.name(), tagAnnotation.description()) : null;
String operateName = accessLogAnnotation != null ? accessLogAnnotation.operateName() :
String operateName = accessLogAnnotation != null && StrUtil.isNotBlank(accessLogAnnotation.operateName()) ?
accessLogAnnotation.operateName() :
operationAnnotation != null ? operationAnnotation.summary() : null;
OperateTypeEnum operateType = accessLogAnnotation != null && accessLogAnnotation.operateType().length > 0 ?
accessLogAnnotation.operateType()[0] : parseOperateLogType(request);

View File

@@ -62,9 +62,9 @@ public class BannerApplicationRunner implements ApplicationRunner {
if (isNotPresent("cn.iocoder.yudao.module.ai.framework.web.config.AiWebConfiguration")) {
System.out.println("[AI 大模型 yudao-module-ai - 已禁用][参考 https://doc.iocoder.cn/ai/build/ 开启]");
}
// IOT 物联网
// IoT 物联网
if (isNotPresent("cn.iocoder.yudao.module.iot.framework.web.config.IotWebConfiguration")) {
System.out.println("[IOT 物联网 yudao-module-iot - 已禁用][参考 https://doc.iocoder.cn/iot/build/ 开启]");
System.out.println("[IoT 物联网 yudao-module-iot - 已禁用][参考 https://doc.iocoder.cn/iot/build/ 开启]");
}
});
}

View File

@@ -26,19 +26,14 @@ public abstract class AbstractSliderDesensitizationHandler<T extends Annotation>
int suffixKeep = getSuffixKeep(annotation);
String replacer = getReplacer(annotation);
int length = origin.length();
// 情况一:原始字符串长度小于等于保留长度,则原始字符串全部替换
if (prefixKeep >= length || suffixKeep >= length) {
return buildReplacerByLength(replacer, length);
}
// 情况二:原始字符串长度小于等于前后缀保留字符串长度,则原始字符串全部替换
if ((prefixKeep + suffixKeep) >= length) {
return buildReplacerByLength(replacer, length);
}
// 情况三:原始字符串长度大于前后缀保留字符串长度,则替换中间字符串
int interval = length - prefixKeep - suffixKeep;
// 情况一:原始字符串长度小于等于前后缀保留字符串长度,则原始字符串全部替换
if (interval <= 0) {
return buildReplacerByLength(replacer, length);
}
// 情况二:原始字符串长度大于前后缀保留字符串长度,则替换中间字符串
return origin.substring(0, prefixKeep) +
buildReplacerByLength(replacer, interval) +
origin.substring(prefixKeep + interval);
@@ -52,11 +47,7 @@ public abstract class AbstractSliderDesensitizationHandler<T extends Annotation>
* @return 构建后的替换符
*/
private String buildReplacerByLength(String replacer, int length) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < length; i++) {
builder.append(replacer);
}
return builder.toString();
return replacer.repeat(length);
}
/**

View File

@@ -20,8 +20,8 @@ public abstract class ApiRequestFilter extends OncePerRequestFilter {
@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
// 只过滤 API 请求的地址
return !StrUtil.startWithAny(request.getRequestURI(), webProperties.getAdminApi().getPrefix(),
webProperties.getAppApi().getPrefix());
String apiUri = request.getRequestURI().substring(request.getContextPath().length());
return !StrUtil.startWithAny(apiUri, webProperties.getAdminApi().getPrefix(), webProperties.getAppApi().getPrefix());
}
}

View File

@@ -1,5 +1,6 @@
package cn.iocoder.yudao.framework.web.core.handler;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjUtil;
@@ -27,6 +28,7 @@ import org.springframework.security.access.AccessDeniedException;
import org.springframework.util.Assert;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
@@ -37,6 +39,7 @@ import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.resource.NoResourceFoundException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -135,9 +138,23 @@ public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public CommonResult<?> methodArgumentNotValidExceptionExceptionHandler(MethodArgumentNotValidException ex) {
log.warn("[methodArgumentNotValidExceptionExceptionHandler]", ex);
// 获取 errorMessage
String errorMessage = null;
FieldError fieldError = ex.getBindingResult().getFieldError();
assert fieldError != null; // 断言,避免告警
return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求参数不正确:%s", fieldError.getDefaultMessage()));
if (fieldError == null) {
// 组合校验,参考自 https://t.zsxq.com/3HVTx
List<ObjectError> allErrors = ex.getBindingResult().getAllErrors();
if (CollUtil.isNotEmpty(allErrors)) {
errorMessage = allErrors.get(0).getDefaultMessage();
}
} else {
errorMessage = fieldError.getDefaultMessage();
}
// 转换 CommonResult
if (StrUtil.isEmpty(errorMessage)) {
return CommonResult.error(BAD_REQUEST);
}
return CommonResult.error(BAD_REQUEST.getCode(), String.format("请求参数不正确:%s", errorMessage));
}
/**
@@ -378,11 +395,11 @@ public class GlobalExceptionHandler {
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
"[AI 大模型 yudao-module-ai - 表结构未导入][参考 https://cloud.iocoder.cn/ai/build/ 开启]");
}
// 9. IOT 物联网
// 9. IoT 物联网
if (message.contains("iot_")) {
log.error("[IOT 物联网 yudao-module-iot - 表结构未导入][参考 https://doc.iocoder.cn/iot/build/ 开启]");
log.error("[IoT 物联网 yudao-module-iot - 表结构未导入][参考 https://doc.iocoder.cn/iot/build/ 开启]");
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
"[IOT 物联网 yudao-module-iot - 表结构未导入][参考 https://doc.iocoder.cn/iot/build/ 开启]");
"[IoT 物联网 yudao-module-iot - 表结构未导入][参考 https://doc.iocoder.cn/iot/build/ 开启]");
}
return null;
}

View File

@@ -1,19 +1,16 @@
package cn.iocoder.yudao.framework.web.core.util;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.iocoder.yudao.framework.common.enums.TerminalEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import cn.iocoder.yudao.framework.web.config.WebProperties;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.http.HttpServletRequest;
/**
* 专属于 web 包的工具类
*
@@ -27,6 +24,7 @@ public class WebFrameworkUtils {
private static final String REQUEST_ATTRIBUTE_COMMON_RESULT = "common_result";
public static final String HEADER_TENANT_ID = "tenant-id";
public static final String HEADER_VISIT_TENANT_ID = "visit-tenant-id";
/**
* 终端的 Header
@@ -53,6 +51,18 @@ public class WebFrameworkUtils {
return NumberUtil.isNumber(tenantId) ? Long.valueOf(tenantId) : null;
}
/**
* 获得访问的租户编号,从 header 中
* 考虑到其它 framework 组件也会使用到租户编号,所以不得不放在 WebFrameworkUtils 统一提供
*
* @param request 请求
* @return 租户编号
*/
public static Long getVisitTenantId(HttpServletRequest request) {
String tenantId = request.getHeader(HEADER_VISIT_TENANT_ID);
return NumberUtil.isNumber(tenantId)? Long.valueOf(tenantId) : null;
}
public static void setLoginUserId(ServletRequest request, Long userId) {
request.setAttribute(REQUEST_ATTRIBUTE_LOGIN_USER_ID, userId);
}

View File

@@ -35,11 +35,7 @@ public enum AiChatRoleEnum {
### 微信
除此之外不要任何解释性语句。
"""),
AI_KNOWLEDGE_ROLE("知识库助手", """
给你提供一些数据参考:{info},请回答我的问题。
请你跟进数据参考与工具返回结果回复用户的请求。
""");
;
/**
* 角色名

View File

@@ -12,49 +12,57 @@ public interface ErrorCodeConstants {
// ========== API 密钥 1-040-000-000 ==========
ErrorCode API_KEY_NOT_EXISTS = new ErrorCode(1_040_000_000, "API 密钥不存在");
ErrorCode API_KEY_DISABLE = new ErrorCode(1_040_000_001, "API 密钥已禁用!");
ErrorCode API_KEY_MIDJOURNEY_NOT_FOUND = new ErrorCode(1_040_000_900, "Midjourney 模型不存在");
ErrorCode API_KEY_SUNO_NOT_FOUND = new ErrorCode(1_040_000_901, "Suno 模型不存在");
ErrorCode API_KEY_IMAGE_NODE_FOUND = new ErrorCode(1_040_000_902, "平台({}) 图片模型未配置");
// ========== API 聊天模型 1-040-001-000 ==========
ErrorCode CHAT_MODEL_NOT_EXISTS = new ErrorCode(1_040_001_000, "模型不存在!");
ErrorCode CHAT_MODEL_DISABLE = new ErrorCode(1_040_001_001, "模型({})已禁用!");
ErrorCode CHAT_MODEL_DEFAULT_NOT_EXISTS = new ErrorCode(1_040_001_002, "操作失败,找不到默认聊天模型");
// ========== API 模型 1-040-001-000 ==========
ErrorCode MODEL_NOT_EXISTS = new ErrorCode(1_040_001_000, "模型不存在!");
ErrorCode MODEL_DISABLE = new ErrorCode(1_040_001_001, "模型({})已禁用!");
ErrorCode MODEL_DEFAULT_NOT_EXISTS = new ErrorCode(1_040_001_002, "操作失败,找不到默认模型");
ErrorCode MODEL_USE_TYPE_ERROR = new ErrorCode(1_040_001_003, "操作失败,该模型的模型类型不正确");
// ========== API 聊天模型 1-040-002-000 ==========
// ========== API 聊天角色 1-040-002-000 ==========
ErrorCode CHAT_ROLE_NOT_EXISTS = new ErrorCode(1_040_002_000, "聊天角色不存在");
ErrorCode CHAT_ROLE_DISABLE = new ErrorCode(1_040_001_001, "聊天角色({})已禁用!");
// ========== API 聊天会话 1-040-003-000 ==========
ErrorCode CHAT_CONVERSATION_NOT_EXISTS = new ErrorCode(1_040_003_000, "对话不存在!");
ErrorCode CHAT_CONVERSATION_MODEL_ERROR = new ErrorCode(1_040_003_001, "操作失败,该聊天模型的配置不完整");
// ========== API 聊天消息 1-040-004-000 ==========
ErrorCode CHAT_MESSAGE_NOT_EXIST = new ErrorCode(1_040_004_000, "消息不存在!");
ErrorCode CHAT_STREAM_ERROR = new ErrorCode(1_040_004_001, "对话生成异常!");
// ========== API 绘画 1-040-005-000 ==========
ErrorCode IMAGE_NOT_EXISTS = new ErrorCode(1_022_005_000, "图片不存在!");
ErrorCode IMAGE_MIDJOURNEY_SUBMIT_FAIL = new ErrorCode(1_022_005_001, "Midjourney 提交失败!原因:{}");
ErrorCode IMAGE_CUSTOM_ID_NOT_EXISTS = new ErrorCode(1_022_005_002, "Midjourney 按钮 customId 不存在! {}");
ErrorCode IMAGE_FAIL = new ErrorCode(1_022_005_002, "图片绘画失败! {}");
ErrorCode IMAGE_NOT_EXISTS = new ErrorCode(1_040_005_000, "图片不存在!");
ErrorCode IMAGE_MIDJOURNEY_SUBMIT_FAIL = new ErrorCode(1_040_005_001, "Midjourney 提交失败!原因:{}");
ErrorCode IMAGE_CUSTOM_ID_NOT_EXISTS = new ErrorCode(1_040_005_002, "Midjourney 按钮 customId 不存在! {}");
// ========== API 音乐 1-040-006-000 ==========
ErrorCode MUSIC_NOT_EXISTS = new ErrorCode(1_022_006_000, "音乐不存在!");
ErrorCode MUSIC_NOT_EXISTS = new ErrorCode(1_040_006_000, "音乐不存在!");
// ========== API 写作 1-022-007-000 ==========
ErrorCode WRITE_NOT_EXISTS = new ErrorCode(1_022_007_000, "作文不存在!");
ErrorCode WRITE_STREAM_ERROR = new ErrorCode(1_022_07_001, "写作生成异常!");
// ========== API 写作 1-040-007-000 ==========
ErrorCode WRITE_NOT_EXISTS = new ErrorCode(1_040_007_000, "作文不存在!");
ErrorCode WRITE_STREAM_ERROR = new ErrorCode(1_040_07_001, "写作生成异常!");
// ========== API 思维导图 1-040-008-000 ==========
ErrorCode MIND_MAP_NOT_EXISTS = new ErrorCode(1_040_008_000, "思维导图不存在!");
// ========== API 知识库 1-022-008-000 ==========
ErrorCode KNOWLEDGE_NOT_EXISTS = new ErrorCode(1_022_008_000, "知识库不存在!");
ErrorCode KNOWLEDGE_DOCUMENT_NOT_EXISTS = new ErrorCode(1_022_008_001, "文档不存在!");
ErrorCode KNOWLEDGE_SEGMENT_NOT_EXISTS = new ErrorCode(1_022_008_002, "段落不存在!");
// ========== API 知识库 1-040-009-000 ==========
ErrorCode KNOWLEDGE_NOT_EXISTS = new ErrorCode(1_040_009_000, "知识库不存在!");
ErrorCode KNOWLEDGE_DOCUMENT_NOT_EXISTS = new ErrorCode(1_040_009_101, "文档不存在!");
ErrorCode KNOWLEDGE_DOCUMENT_FILE_EMPTY = new ErrorCode(1_040_009_102, "文档内容为空!");
ErrorCode KNOWLEDGE_DOCUMENT_FILE_DOWNLOAD_FAIL = new ErrorCode(1_040_009_102, "文件下载失败!");
ErrorCode KNOWLEDGE_DOCUMENT_FILE_READ_FAIL = new ErrorCode(1_040_009_102, "文档加载失败!");
ErrorCode KNOWLEDGE_SEGMENT_NOT_EXISTS = new ErrorCode(1_040_009_202, "段落不存在!");
ErrorCode KNOWLEDGE_SEGMENT_CONTENT_TOO_LONG = new ErrorCode(1_040_009_203, "内容 Token 数为 {},超过最大限制 {}");
// ========== AI 工具 1-040-010-000 ==========
ErrorCode TOOL_NOT_EXISTS = new ErrorCode(1_040_010_000, "工具不存在");
ErrorCode TOOL_NAME_NOT_EXISTS = new ErrorCode(1_040_010_001, "工具({})找不到 Bean");
// ========== AI 工作流 1-040-011-000 ==========
ErrorCode WORKFLOW_NOT_EXISTS = new ErrorCode(1_040_011_000, "工作流不存在");
ErrorCode WORKFLOW_CODE_EXISTS = new ErrorCode(1_040_011_001, "工作流标识已存在");
}

View File

@@ -1,39 +0,0 @@
package cn.iocoder.yudao.module.ai.enums.knowledge;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
* AI 知识库-文档状态的枚举
*
* @author xiaoxin
*/
@AllArgsConstructor
@Getter
public enum AiKnowledgeDocumentStatusEnum implements IntArrayValuable {
IN_PROGRESS(10, "索引中"),
SUCCESS(20, "可用"),
FAIL(30, "失败");
/**
* 状态
*/
private final Integer status;
/**
* 状态名
*/
private final String name;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AiKnowledgeDocumentStatusEnum::getStatus).toArray();
@Override
public int[] array() {
return ARRAYS;
}
}

View File

@@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.ai.enums.music;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@@ -13,7 +13,7 @@ import java.util.Arrays;
*/
@AllArgsConstructor
@Getter
public enum AiMusicGenerateModeEnum implements IntArrayValuable {
public enum AiMusicGenerateModeEnum implements ArrayValuable<Integer> {
DESCRIPTION(1, "描述模式"),
LYRIC(2, "歌词模式");
@@ -27,10 +27,10 @@ public enum AiMusicGenerateModeEnum implements IntArrayValuable {
*/
private final String name;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AiMusicGenerateModeEnum::getMode).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(AiMusicGenerateModeEnum::getMode).toArray(Integer[]::new);
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.ai.enums.music;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@@ -13,7 +13,7 @@ import java.util.Arrays;
*/
@AllArgsConstructor
@Getter
public enum AiMusicStatusEnum implements IntArrayValuable {
public enum AiMusicStatusEnum implements ArrayValuable<Integer> {
IN_PROGRESS(10, "进行中"),
SUCCESS(20, "已完成"),
@@ -29,10 +29,10 @@ public enum AiMusicStatusEnum implements IntArrayValuable {
*/
private final String name;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AiMusicStatusEnum::getStatus).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(AiMusicStatusEnum::getStatus).toArray(Integer[]::new);
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.ai.enums.write;
import cn.iocoder.yudao.framework.common.core.IntArrayValuable;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
@@ -13,7 +13,7 @@ import java.util.Arrays;
*/
@AllArgsConstructor
@Getter
public enum AiWriteTypeEnum implements IntArrayValuable {
public enum AiWriteTypeEnum implements ArrayValuable<Integer> {
WRITING(1, "撰写", "请撰写一篇关于 [{}] 的文章。文章的内容格式:{},语气:{},语言:{},长度:{}。请确保涵盖主要内容,不需要除了正文内容外的其他回复,如标题、额外的解释或道歉。"),
REPLY(2, "回复", "请针对如下内容:[{}] 做个回复。回复内容参考:[{}], 回复格式:{},语气:{},语言:{},长度:{}。不需要除了正文内容外的其他回复,如标题、开头、额外的解释或道歉。");
@@ -32,10 +32,10 @@ public enum AiWriteTypeEnum implements IntArrayValuable {
*/
private final String prompt;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(AiWriteTypeEnum::getType).toArray();
public static final Integer[] ARRAYS = Arrays.stream(values()).map(AiWriteTypeEnum::getType).toArray(Integer[]::new);
@Override
public int[] array() {
public Integer[] array() {
return ARRAYS;
}

View File

@@ -2,7 +2,7 @@
POST {{baseUrl}}/ai/chat/message/send
Content-Type: application/json
Authorization: {{token}}
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
{
"conversationId": "1781604279872581724",
@@ -13,7 +13,7 @@ tenant-id: {{adminTenentId}}
POST {{baseUrl}}/ai/chat/message/send-stream
Content-Type: application/json
Authorization: {{token}}
tenant-id: {{adminTenentId}}
tenant-id: {{adminTenantId}}
{
"conversationId": "1781604279872581724",

View File

@@ -12,15 +12,18 @@ import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessage
import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO;
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
import cn.iocoder.yudao.module.ai.service.chat.AiChatConversationService;
import cn.iocoder.yudao.module.ai.service.chat.AiChatMessageService;
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService;
import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
@@ -33,7 +36,7 @@ import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 聊天消息")
@@ -48,6 +51,10 @@ public class AiChatMessageController {
private AiChatConversationService chatConversationService;
@Resource
private AiChatRoleService chatRoleService;
@Resource
private AiKnowledgeSegmentService knowledgeSegmentService;
@Resource
private AiKnowledgeDocumentService knowledgeDocumentService;
@Operation(summary = "发送消息(段式)", description = "一次性返回,响应较慢")
@PostMapping("/send")
@@ -57,7 +64,6 @@ public class AiChatMessageController {
@Operation(summary = "发送消息(流式)", description = "流式返回,响应较快")
@PostMapping(value = "/send-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
@PermitAll // 解决 SSE 最终响应的时候,会被 Access Denied 拦截的问题
public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(@Valid @RequestBody AiChatMessageSendReqVO sendReqVO) {
return chatMessageService.sendChatMessageStream(sendReqVO, getLoginUserId());
}
@@ -71,8 +77,38 @@ public class AiChatMessageController {
if (conversation == null || ObjUtil.notEqual(conversation.getUserId(), getLoginUserId())) {
return success(Collections.emptyList());
}
// 1. 获取消息列表
List<AiChatMessageDO> messageList = chatMessageService.getChatMessageListByConversationId(conversationId);
return success(BeanUtils.toBean(messageList, AiChatMessageRespVO.class));
if (CollUtil.isEmpty(messageList)) {
return success(Collections.emptyList());
}
// 2. 拼接数据,主要是知识库段落信息
Map<Long, AiKnowledgeSegmentDO> segmentMap = knowledgeSegmentService.getKnowledgeSegmentMap(convertListByFlatMap(messageList,
message -> CollUtil.isEmpty(message.getSegmentIds()) ? null : message.getSegmentIds().stream()));
Map<Long, AiKnowledgeDocumentDO> documentMap = knowledgeDocumentService.getKnowledgeDocumentMap(
convertList(segmentMap.values(), AiKnowledgeSegmentDO::getDocumentId));
List<AiChatMessageRespVO> messageVOList = BeanUtils.toBean(messageList, AiChatMessageRespVO.class);
for (int i = 0; i < messageList.size(); i++) {
AiChatMessageDO message = messageList.get(i);
if (CollUtil.isEmpty(message.getSegmentIds())) {
continue;
}
// 设置知识库段落信息
messageVOList.get(i).setSegments(convertList(message.getSegmentIds(), segmentId -> {
AiKnowledgeSegmentDO segment = segmentMap.get(segmentId);
if (segment == null) {
return null;
}
AiKnowledgeDocumentDO document = documentMap.get(segment.getDocumentId());
if (document == null) {
return null;
}
return new AiChatMessageRespVO.KnowledgeSegment().setId(segment.getId()).setContent(segment.getContent())
.setDocumentId(segment.getDocumentId()).setDocumentName(document.getName());
}));
}
return success(messageVOList);
}
@Operation(summary = "删除消息")
@@ -105,7 +141,8 @@ public class AiChatMessageController {
Map<Long, AiChatRoleDO> roleMap = chatRoleService.getChatRoleMap(
convertSet(pageResult.getList(), AiChatMessageDO::getRoleId));
return success(BeanUtils.toBean(pageResult, AiChatMessageRespVO.class,
respVO -> MapUtils.findAndThen(roleMap, respVO.getRoleId(), role -> respVO.setRoleName(role.getName()))));
respVO -> MapUtils.findAndThen(roleMap, respVO.getRoleId(),
role -> respVO.setRoleName(role.getName()))));
}
@Operation(summary = "管理员删除消息")

View File

@@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation;
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO;
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiModelDO;
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
import com.fhs.core.trans.anno.Trans;
import com.fhs.core.trans.constant.TransType;
@@ -31,7 +31,7 @@ public class AiChatConversationRespVO implements VO {
private Long roleId;
@Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@Trans(type = TransType.SIMPLE, target = AiChatModelDO.class, fields = "name", ref = "modelName")
@Trans(type = TransType.SIMPLE, target = AiModelDO.class, fields = "name", ref = "modelName")
private Long modelId;
@Schema(description = "模型标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "ERNIE-Bot-turbo-0922")

View File

@@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - AI 聊天消息 Response VO")
@Data
@@ -39,6 +40,12 @@ public class AiChatMessageRespVO {
@Schema(description = "是否携带上下文", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
private Boolean useContext;
@Schema(description = "知识库段落编号数组", example = "[1,2,3]")
private List<Long> segmentIds;
@Schema(description = "知识库段落数组")
private List<KnowledgeSegment> segments;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-05-12 12:51")
private LocalDateTime createTime;
@@ -47,4 +54,22 @@ public class AiChatMessageRespVO {
@Schema(description = "角色名字", example = "小黄")
private String roleName;
@Schema(description = "知识库段落", example = "Java 开发手册")
@Data
public static class KnowledgeSegment {
@Schema(description = "段落编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
private String content;
@Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
private Long documentId;
@Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品使用手册")
private String documentName;
}
}

View File

@@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - AI 聊天消息发送 Response VO")
@Data
@@ -28,6 +29,12 @@ public class AiChatMessageSendRespVO {
@Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊")
private String content;
@Schema(description = "知识库段落编号数组", example = "[1,2,3]")
private List<Long> segmentIds;
@Schema(description = "知识库段落数组")
private List<AiChatMessageRespVO.KnowledgeSegment> segments;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;

View File

@@ -14,18 +14,15 @@ import java.util.Map;
@Data
public class AiImageDrawReqVO {
@Schema(description = "模型平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI")
private String platform; // 参见 AiPlatformEnum 枚举
@Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "模型编号不能为空")
private Long modelId;
@Schema(description = "提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "画一个长城")
@NotEmpty(message = "提示词不能为空")
@Size(max = 1200, message = "提示词最大 1200")
private String prompt;
@Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "stable-diffusion-v1-6")
@NotEmpty(message = "模型不能为空")
private String model;
/**
* 1. dall-e-2 模型256x256、512x512、1024x1024
* 2. dall-e-3 模型1024x1024, 1792x1024, 或 1024x1792

View File

@@ -13,9 +13,9 @@ public class AiMidjourneyImagineReqVO {
@NotEmpty(message = "提示词不能为空!")
private String prompt;
@Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "midjourney")
@NotEmpty(message = "模型不能为空")
private String model; // 参考 MidjourneyApi.ModelEnum
@Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "模型编号不能为空")
private Long modelId;
@Schema(description = "图片宽度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "图片宽度不能为空")

View File

@@ -0,0 +1,35 @@
### 创建知识库
POST {{baseUrl}}/ai/knowledge/create
Content-Type: application/json
Authorization: {{token}}
tenant-id: {{adminTenantId}}
{
"name": "测试标题",
"description": "测试描述",
"embeddingModelId": 30,
"topK": 3,
"similarityThreshold": 0.5,
"status": 0
}
### 更新知识库
PUT {{baseUrl}}/ai/knowledge/update
Content-Type: application/json
Authorization: {{token}}
tenant-id: {{adminTenantId}}
{
"id": 1,
"name": "测试标题(更新)",
"description": "测试描述",
"embeddingModelId": 30,
"topK": 5,
"similarityThreshold": 0.6,
"status": 0
}
### 获取知识库分页
GET {{baseUrl}}/ai/knowledge/page?pageNo=1&pageSize=10
Authorization: {{token}}
tenant-id: {{adminTenantId}}

View File

@@ -1,23 +1,27 @@
package cn.iocoder.yudao.module.ai.controller.admin.knowledge;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeCreateReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgePageReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeRespVO;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeUpdateReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeSaveReqVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDO;
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@Tag(name = "管理后台 - AI 知识库")
@RestController
@@ -30,21 +34,51 @@ public class AiKnowledgeController {
@GetMapping("/page")
@Operation(summary = "获取知识库分页")
@PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
public CommonResult<PageResult<AiKnowledgeRespVO>> getKnowledgePage(@Valid AiKnowledgePageReqVO pageReqVO) {
PageResult<AiKnowledgeDO> pageResult = knowledgeService.getKnowledgePage(getLoginUserId(), pageReqVO);
PageResult<AiKnowledgeDO> pageResult = knowledgeService.getKnowledgePage(pageReqVO);
return success(BeanUtils.toBean(pageResult, AiKnowledgeRespVO.class));
}
@GetMapping("/get")
@Operation(summary = "获得知识库")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
public CommonResult<AiKnowledgeRespVO> getKnowledge(@RequestParam("id") Long id) {
AiKnowledgeDO knowledge = knowledgeService.getKnowledge(id);
return success(BeanUtils.toBean(knowledge, AiKnowledgeRespVO.class));
}
@PostMapping("/create")
@Operation(summary = "创建知识库")
public CommonResult<Long> createKnowledge(@RequestBody @Valid AiKnowledgeCreateReqVO createReqVO) {
return success(knowledgeService.createKnowledge(createReqVO, getLoginUserId()));
@PreAuthorize("@ss.hasPermission('ai:knowledge:create')")
public CommonResult<Long> createKnowledge(@RequestBody @Valid AiKnowledgeSaveReqVO createReqVO) {
return success(knowledgeService.createKnowledge(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新知识库")
public CommonResult<Boolean> updateKnowledge(@RequestBody @Valid AiKnowledgeUpdateReqVO updateReqVO) {
knowledgeService.updateKnowledge(updateReqVO, getLoginUserId());
@PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
public CommonResult<Boolean> updateKnowledge(@RequestBody @Valid AiKnowledgeSaveReqVO updateReqVO) {
knowledgeService.updateKnowledge(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除知识库")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('ai:knowledge:delete')")
public CommonResult<Boolean> deleteKnowledge(@RequestParam("id") Long id) {
knowledgeService.deleteKnowledge(id);
return success(true);
}
@GetMapping("/simple-list")
@Operation(summary = "获得知识库的精简列表")
public CommonResult<List<AiKnowledgeRespVO>> getKnowledgeSimpleList() {
List<AiKnowledgeDO> list = knowledgeService.getKnowledgeSimpleListByStatus(CommonStatusEnum.ENABLE.getStatus());
return success(convertList(list, knowledge -> new AiKnowledgeRespVO()
.setId(knowledge.getId()).setName(knowledge.getName())));
}
}

View File

@@ -0,0 +1,35 @@
### 创建知识文档
POST {{baseUrl}}/ai/knowledge/document/create
Content-Type: application/json
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}
{
"knowledgeId": 2,
"name": "测试文档",
"url": "https://static.iocoder.cn/README.md",
"segmentMaxTokens": 800
}
### 批量创建知识文档
POST {{baseUrl}}/ai/knowledge/document/create-list
Content-Type: application/json
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}
{
"knowledgeId": 1,
"list": [
{
"name": "测试文档1",
"url": "https://static.iocoder.cn/README.md",
"segmentMaxTokens": 800
},
{
"name": "测试文档2",
"url": "https://static.iocoder.cn/README_yudao.md",
"segmentMaxTokens": 400
}
]
}

View File

@@ -3,9 +3,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentPageReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentRespVO;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.AiKnowledgeDocumentUpdateReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document.*;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge.AiKnowledgeDocumentCreateReqVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;
@@ -13,9 +11,12 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@Tag(name = "管理后台 - AI 知识库文档")
@@ -27,25 +28,63 @@ public class AiKnowledgeDocumentController {
@Resource
private AiKnowledgeDocumentService documentService;
@PostMapping("/create")
@Operation(summary = "新建文档")
public CommonResult<Long> createKnowledgeDocument(@Valid AiKnowledgeDocumentCreateReqVO reqVO) {
Long knowledgeDocumentId = documentService.createKnowledgeDocument(reqVO);
return success(knowledgeDocumentId);
}
@GetMapping("/page")
@Operation(summary = "获取文档分页")
public CommonResult<PageResult<AiKnowledgeDocumentRespVO>> getKnowledgeDocumentPage(@Valid AiKnowledgeDocumentPageReqVO pageReqVO) {
@PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
public CommonResult<PageResult<AiKnowledgeDocumentRespVO>> getKnowledgeDocumentPage(
@Valid AiKnowledgeDocumentPageReqVO pageReqVO) {
PageResult<AiKnowledgeDocumentDO> pageResult = documentService.getKnowledgeDocumentPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, AiKnowledgeDocumentRespVO.class));
}
@GetMapping("/get")
@Operation(summary = "获取文档详情")
@PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
public CommonResult<AiKnowledgeDocumentRespVO> getKnowledgeDocument(@RequestParam("id") Long id) {
AiKnowledgeDocumentDO document = documentService.getKnowledgeDocument(id);
return success(BeanUtils.toBean(document, AiKnowledgeDocumentRespVO.class));
}
@PostMapping("/create")
@Operation(summary = "新建文档(单个)")
@PreAuthorize("@ss.hasPermission('ai:knowledge:create')")
public CommonResult<Long> createKnowledgeDocument(@RequestBody @Valid AiKnowledgeDocumentCreateReqVO reqVO) {
Long id = documentService.createKnowledgeDocument(reqVO);
return success(id);
}
@PostMapping("/create-list")
@Operation(summary = "新建文档(多个)")
@PreAuthorize("@ss.hasPermission('ai:knowledge:create')")
public CommonResult<List<Long>> createKnowledgeDocumentList(
@RequestBody @Valid AiKnowledgeDocumentCreateListReqVO reqVO) {
List<Long> ids = documentService.createKnowledgeDocumentList(reqVO);
return success(ids);
}
@PutMapping("/update")
@Operation(summary = "更新文档")
@PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
public CommonResult<Boolean> updateKnowledgeDocument(@Valid @RequestBody AiKnowledgeDocumentUpdateReqVO reqVO) {
documentService.updateKnowledgeDocument(reqVO);
return success(true);
}
@PutMapping("/update-status")
@Operation(summary = "更新文档状态")
@PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
public CommonResult<Boolean> updateKnowledgeDocumentStatus(
@Valid @RequestBody AiKnowledgeDocumentUpdateStatusReqVO reqVO) {
documentService.updateKnowledgeDocumentStatus(reqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除文档")
@PreAuthorize("@ss.hasPermission('ai:knowledge:delete')")
public CommonResult<Boolean> deleteKnowledgeDocument(@RequestParam("id") Long id) {
documentService.deleteKnowledgeDocument(id);
return success(true);
}
}

View File

@@ -0,0 +1,17 @@
### 切片内容
GET {{baseUrl}}/ai/knowledge/segment/split?url=https://static.iocoder.cn/README_yudao.md&segmentMaxTokens=800
Content-Type: application/json
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}
### 搜索段落内容
GET {{baseUrl}}/ai/knowledge/segment/search?knowledgeId=2&content=如何使用这个产品&topK=5&similarityThreshold=0.1
Content-Type: application/json
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}
### 获取文档处理列表
GET {{baseUrl}}/ai/knowledge/segment/get-process-list?documentIds=1,2,3
Content-Type: application/json
Authorization: Bearer {{token}}
tenant-id: {{adminTenantId}}

View File

@@ -1,22 +1,34 @@
package cn.iocoder.yudao.module.ai.controller.admin.knowledge;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentPageReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentRespVO;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.AiKnowledgeSegmentUpdateStatusReqVO;
import cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment.*;
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
import cn.iocoder.yudao.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeDocumentService;
import cn.iocoder.yudao.module.ai.service.knowledge.AiKnowledgeSegmentService;
import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchReqBO;
import cn.iocoder.yudao.module.ai.service.knowledge.bo.AiKnowledgeSegmentSearchRespBO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.hibernate.validator.constraints.URL;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - AI 知识库段落")
@RestController
@@ -26,26 +38,93 @@ public class AiKnowledgeSegmentController {
@Resource
private AiKnowledgeSegmentService segmentService;
@Resource
private AiKnowledgeDocumentService documentService;
@GetMapping("/get")
@Operation(summary = "获取段落详情")
@Parameter(name = "id", description = "段落编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
public CommonResult<AiKnowledgeSegmentRespVO> getKnowledgeSegment(@RequestParam("id") Long id) {
AiKnowledgeSegmentDO segment = segmentService.getKnowledgeSegment(id);
return success(BeanUtils.toBean(segment, AiKnowledgeSegmentRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获取段落分页")
public CommonResult<PageResult<AiKnowledgeSegmentRespVO>> getKnowledgeSegmentPage(@Valid AiKnowledgeSegmentPageReqVO pageReqVO) {
@PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
public CommonResult<PageResult<AiKnowledgeSegmentRespVO>> getKnowledgeSegmentPage(
@Valid AiKnowledgeSegmentPageReqVO pageReqVO) {
PageResult<AiKnowledgeSegmentDO> pageResult = segmentService.getKnowledgeSegmentPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, AiKnowledgeSegmentRespVO.class));
}
@PostMapping("/create")
@Operation(summary = "创建段落")
@PreAuthorize("@ss.hasPermission('ai:knowledge:create')")
public CommonResult<Long> createKnowledgeSegment(@Valid @RequestBody AiKnowledgeSegmentSaveReqVO createReqVO) {
return success(segmentService.createKnowledgeSegment(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新段落内容")
public CommonResult<Boolean> updateKnowledgeSegment(@Valid @RequestBody AiKnowledgeSegmentUpdateReqVO reqVO) {
@PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
public CommonResult<Boolean> updateKnowledgeSegment(@Valid @RequestBody AiKnowledgeSegmentSaveReqVO reqVO) {
segmentService.updateKnowledgeSegment(reqVO);
return success(true);
}
@PutMapping("/update-status")
@Operation(summary = "启禁用段落内容")
public CommonResult<Boolean> updateKnowledgeSegmentStatus(@Valid @RequestBody AiKnowledgeSegmentUpdateStatusReqVO reqVO) {
@PreAuthorize("@ss.hasPermission('ai:knowledge:update')")
public CommonResult<Boolean> updateKnowledgeSegmentStatus(
@Valid @RequestBody AiKnowledgeSegmentUpdateStatusReqVO reqVO) {
segmentService.updateKnowledgeSegmentStatus(reqVO);
return success(true);
}
@GetMapping("/split")
@Operation(summary = "切片内容")
@Parameters({
@Parameter(name = "url", description = "文档 URL", required = true),
@Parameter(name = "segmentMaxTokens", description = "分段的最大 Token 数", required = true)
})
@PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
public CommonResult<List<AiKnowledgeSegmentRespVO>> splitContent(
@RequestParam("url") @URL String url,
@RequestParam(value = "segmentMaxTokens") Integer segmentMaxTokens) {
List<AiKnowledgeSegmentDO> segments = segmentService.splitContent(url, segmentMaxTokens);
return success(BeanUtils.toBean(segments, AiKnowledgeSegmentRespVO.class));
}
@GetMapping("/get-process-list")
@Operation(summary = "获取文档处理列表")
@Parameter(name = "documentIds", description = "文档编号列表", required = true, example = "1,2,3")
@PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
public CommonResult<List<AiKnowledgeSegmentProcessRespVO>> getKnowledgeSegmentProcessList(
@RequestParam("documentIds") List<Long> documentIds) {
List<AiKnowledgeSegmentProcessRespVO> list = segmentService.getKnowledgeSegmentProcessList(documentIds);
return success(list);
}
@GetMapping("/search")
@Operation(summary = "搜索段落内容")
@PreAuthorize("@ss.hasPermission('ai:knowledge:query')")
public CommonResult<List<AiKnowledgeSegmentSearchRespVO>> searchKnowledgeSegment(
@Valid AiKnowledgeSegmentSearchReqVO reqVO) {
// 1. 搜索段落
List<AiKnowledgeSegmentSearchRespBO> segments = segmentService
.searchKnowledgeSegment(BeanUtils.toBean(reqVO, AiKnowledgeSegmentSearchReqBO.class));
if (CollUtil.isEmpty(segments)) {
return success(Collections.emptyList());
}
// 2. 拼接 VO
Map<Long, AiKnowledgeDocumentDO> documentMap = documentService.getKnowledgeDocumentMap(convertSet(
segments, AiKnowledgeSegmentSearchRespBO::getDocumentId));
return success(BeanUtils.toBean(segments, AiKnowledgeSegmentSearchRespVO.class,
segment -> MapUtils.findAndThen(documentMap, segment.getDocumentId(),
document -> segment.setDocumentName(document.getName()))));
}
}

View File

@@ -0,0 +1,42 @@
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.hibernate.validator.constraints.URL;
import java.util.List;
@Schema(description = "管理后台 - AI 知识库文档批量创建 Request VO")
@Data
public class AiKnowledgeDocumentCreateListReqVO {
@Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1204")
@NotNull(message = "知识库编号不能为空")
private Long knowledgeId;
@Schema(description = "分段的最大 Token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "800")
@NotNull(message = "分段的最大 Token 数不能为空")
private Integer segmentMaxTokens;
@Schema(description = "文档列表", requiredMode = Schema.RequiredMode.REQUIRED)
@NotEmpty(message = "文档列表不能为空")
private List<Document> list;
@Schema(description = "文档")
@Data
public static class Document {
@Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "三方登陆")
@NotBlank(message = "文档名称不能为空")
private String name;
@Schema(description = "文档 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn")
@URL(message = "文档 URL 格式不正确")
private String url;
}
}

View File

@@ -8,6 +8,9 @@ import lombok.Data;
@Data
public class AiKnowledgeDocumentPageReqVO extends PageParam {
@Schema(description = "知识库编号", example = "1")
private Long knowledgeId;
@Schema(description = "文档名称", example = "Java 开发手册")
private String name;

View File

@@ -1,38 +1,45 @@
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - AI 知识库-文档 Response VO")
@Data
public class AiKnowledgeDocumentRespVO extends PageParam {
import java.time.LocalDateTime;
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
@Schema(description = "管理后台 - AI 知识库文档 Response VO")
@Data
public class AiKnowledgeDocumentRespVO {
@Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
private Long id;
@Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
private Long knowledgeId;
@Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
@Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
private String name;
@Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 是一门面向对象的语言.....")
private String content;
@Schema(description = "文档 url", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn")
@Schema(description = "文档 URL", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://doc.iocoder.cn")
private String url;
@Schema(description = "token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@Schema(description = "文档内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 是一门面向对象的语言.....")
private String content;
@Schema(description = "文档内容长度", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private Integer contentLength;
@Schema(description = "文档 Token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer tokens;
@Schema(description = "字符", requiredMode = Schema.RequiredMode.REQUIRED, example = "1008")
private Integer wordCount;
@Schema(description = "分片最大 Token ", requiredMode = Schema.RequiredMode.REQUIRED, example = "512")
private Integer segmentMaxTokens;
@Schema(description = "切片状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer sliceStatus;
@Schema(description = "召回次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private Integer retrievalCount;
@Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
private Integer status;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@@ -1,26 +1,21 @@
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - AI 更新 知识库-文档 Request VO")
@Schema(description = "管理后台 - AI 知识库文档更新 Request VO")
@Data
public class AiKnowledgeDocumentUpdateReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583")
@NotNull(message = "编号不能为空")
private Long id;
@Schema(description = "是否启用", example = "1")
@InEnum(CommonStatusEnum.class)
private Integer status;
@Schema(description = "名称", example = "Java 开发手册")
private String name;
@Schema(description = "分片最大 Token 数", example = "1000")
private Integer segmentMaxTokens;
}

View File

@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.document;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - AI 知识库文档更新状态 Request VO")
@Data
public class AiKnowledgeDocumentUpdateStatusReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15583")
@NotNull(message = "编号不能为空")
private Long id;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
@NotNull(message = "状态不能为空")
@InEnum(CommonStatusEnum.class)
private Integer status;
}

View File

@@ -23,24 +23,8 @@ public class AiKnowledgeDocumentCreateReqVO {
@URL(message = "文档 URL 格式不正确")
private String url;
@Schema(description = "每个段落的目标 token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "800")
@NotNull(message = "每个段落的目标 token 数不能为空")
private Integer defaultSegmentTokens;
@Schema(description = "每个段落的最小字符数", requiredMode = Schema.RequiredMode.REQUIRED, example = "350")
@NotNull(message = "每个段落的最小字符数不能为空")
private Integer minSegmentWordCount;
@Schema(description = "丢弃阈值:低于此阈值的段落会被丢弃", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
@NotNull(message = "丢弃阈值不能为空")
private Integer minChunkLengthToEmbed;
@Schema(description = "最大段落数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000")
@NotNull(message = "最大段落数不能为空")
private Integer maxNumSegments;
@Schema(description = "分块是否保留分隔符", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "分块是否保留分隔符不能为空")
private Boolean keepSeparator;
@Schema(description = "分段的最大 Token 数", requiredMode = Schema.RequiredMode.REQUIRED, example = "800")
@NotNull(message = "分段的最大 Token 数不能为空")
private Integer segmentMaxTokens;
}

View File

@@ -1,14 +1,29 @@
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - AI 知识库的分页 Request VO")
@Data
public class AiKnowledgePageReqVO extends PageParam {
@Schema(description = "知识库名称", example = "Java 开发手册")
@Schema(description = "知识库名称", example = "芋艿")
private String name;
@Schema(description = "是否启用", example = "1")
@InEnum(CommonStatusEnum.class)
private Integer status;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - AI 知识库 Response VO")
@Data
@@ -17,10 +18,22 @@ public class AiKnowledgeRespVO {
@Schema(description = "知识库描述", example = "帮助你快速构建系统")
private String description;
@Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "14")
private Long modelId;
@Schema(description = "向量模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "14")
private Long embeddingModelId;
@Schema(description = "模型标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "qwen-72b-chat")
private String model;
@Schema(description = "向量模型标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "qwen-72b-chat")
private String embeddingModel;
@Schema(description = "topK", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
private Integer topK;
@Schema(description = "相似度阈值", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.7")
private Double similarityThreshold;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@@ -1,15 +1,18 @@
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - AI 知识库创建 Request VO")
@Schema(description = "管理后台 - AI 知识库新增/修改 Request VO")
@Data
public class AiKnowledgeCreateReqVO {
public class AiKnowledgeSaveReqVO {
@Schema(description = "对话编号", example = "1204")
private Long id;
@Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ruoyi-vue-pro 用户指南")
@NotBlank(message = "知识库名称不能为空")
@@ -18,19 +21,21 @@ public class AiKnowledgeCreateReqVO {
@Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "存储 ruoyi-vue-pro 操作文档")
private String description;
@Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2,3]")
private List<Long> visibilityPermissions;
@Schema(description = "嵌入模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "嵌入模型不能为空")
private Long modelId;
@Schema(description = "相似性阈值", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.5")
@NotNull(message = "相似性阈值不能为空")
private Double similarityThreshold;
@Schema(description = "向量模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "向量模型不能为空")
private Long embeddingModelId;
@Schema(description = "topK", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
@NotNull(message = "topK 不能为空")
private Integer topK;
@Schema(description = "相似性阈值", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.5")
@NotNull(message = "相似性阈值不能为空")
private Double similarityThreshold;
@Schema(description = "是否启用", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "是否启用不能为空")
@InEnum(CommonStatusEnum.class)
private Integer status;
}

View File

@@ -1,32 +0,0 @@
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.knowledge;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - AI 知识库更新【我的】 Request VO")
@Data
public class AiKnowledgeUpdateReqVO {
@Schema(description = "对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1204")
@NotNull(message = "知识库编号不能为空")
private Long id;
@Schema(description = "知识库名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "")
@NotBlank(message = "知识库名称不能为空")
private String name;
@Schema(description = "知识库描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "")
private String description;
@Schema(description = "可见权限,只能选择哪些人可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3")
private List<Long> visibilityPermissions;
@Schema(description = "嵌入模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "嵌入模型不能为空")
private Long modelId;
}

View File

@@ -1,6 +1,8 @@
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -8,13 +10,14 @@ import lombok.Data;
@Data
public class AiKnowledgeSegmentPageReqVO extends PageParam {
@Schema(description = "分段状态", example = "1")
private Integer status;
@Schema(description = "文档编号", example = "1")
private Integer documentId;
@Schema(description = "分段内容关键字", example = "Java 开发")
private String keyword;
private String content;
@Schema(description = "分段状态", example = "1")
@InEnum(CommonStatusEnum.class)
private Integer status;
}

View File

@@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - AI 知识库段落向量进度 Response VO")
@Data
public class AiKnowledgeSegmentProcessRespVO {
@Schema(description = "文档编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long documentId;
@Schema(description = "总段落数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private Long count;
@Schema(description = "已向量化段落数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
private Long embeddingCount;
}

View File

@@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - AI 知识库-文档 Response VO")
@Schema(description = "管理后台 - AI 知识库文档分片 Response VO")
@Data
public class AiKnowledgeSegmentRespVO {
@@ -22,13 +22,19 @@ public class AiKnowledgeSegmentRespVO {
@Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
private String content;
@Schema(description = "切片内容长度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer contentLength;
@Schema(description = "token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Integer tokens;
@Schema(description = "字符", requiredMode = Schema.RequiredMode.REQUIRED, example = "1008")
private Integer wordCount;
@Schema(description = "召回次", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private Integer retrievalCount;
@Schema(description = "文档状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private Long createTime;
}

View File

@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import lombok.Data;
@Schema(description = "管理后台 - AI 新增/修改知识库段落 request VO")
@Data
public class AiKnowledgeSegmentSaveReqVO {
@Schema(description = "编号", example = "24790")
private Long id;
@Schema(description = "知识库文档编号", example = "1024")
private Long documentId;
@Schema(description = "切片内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 开发手册")
@NotEmpty(message = "切片内容不能为空")
private String content;
}

View File

@@ -3,15 +3,25 @@ package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
@Schema(description = "管理后台 - AI 知识库段落召回 Request VO")
@Schema(description = "管理后台 - AI 知识库段落搜索 Request VO")
@Data
public class AiKnowledgeSegmentSearchReqVO {
@Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24790")
@Schema(description = "知识库编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "知识库编号不能为空")
private Long knowledgeId;
@Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "Java 学习路线")
@Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "如何使用这个产品")
@NotEmpty(message = "内容不能为空")
private String content;
@Schema(description = "最大返回数量", example = "5")
private Integer topK;
@Schema(description = "相似度阈值", example = "0.7")
private Double similarityThreshold;
}

View File

@@ -0,0 +1,16 @@
package cn.iocoder.yudao.module.ai.controller.admin.knowledge.vo.segment;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - AI 知识库段落搜索 Response VO")
@Data
public class AiKnowledgeSegmentSearchRespVO extends AiKnowledgeSegmentRespVO {
@Schema(description = "文档名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品使用手册")
private String documentName;
@Schema(description = "相似度分数", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.95")
private Double score;
}

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