Compare commits

..

180 Commits

Author SHA1 Message Date
e877929a42 Merge branch 'HEAD' of https://github.com/jeecgboot/JeecgBoot.git 2025-04-08 22:05:42 +08:00
5f5970d7d5 解决钉钉登录报错 class com.alibaba.fastjson2.JSONObject cannot be cast to class com.alibaba.fastjson.JSONObject 2025-04-08 22:05:20 +08:00
e05c9ddc08 Merge pull request #8073 from EightMonth/master
新增controller测试用例
2025-04-08 16:27:43 +08:00
b77e9e25a3 AI功能介绍 2025-04-08 15:51:11 +08:00
8c406dcdea 项目介绍 2025-04-08 11:44:45 +08:00
215b263d1a 项目介绍 2025-04-08 11:41:21 +08:00
4247ffd082 AIGC专题介绍页 2025-04-08 11:04:32 +08:00
ec87621858 AI流程大模型功能 2025-04-08 10:48:24 +08:00
bc6cf0e7b4 Merge branch 'HEAD' of https://github.com/jeecgboot/JeecgBoot.git 2025-04-08 09:56:48 +08:00
353ad058d1 AIGC 功能视频介绍 2025-04-08 09:56:32 +08:00
cf63b056d5 新增controller测试用例 2025-04-08 09:20:00 +08:00
f743622655 Merge pull request #8066 from EightMonth/master
编写单个controller的测试用例
2025-04-07 16:08:30 +08:00
70a1f0b7db 编写单个controller的测试用例 2025-04-07 15:59:46 +08:00
bb4cdf93b1 Merge pull request #8059 from EightMonth/master
优化swagger文档架构改造
2025-04-04 23:05:37 +08:00
7d7fde63ec 【issues/7996】表格列组件取消所有或者只勾选中间,显示非预期 2025-04-04 22:46:30 +08:00
c22fd21233 【issues/7812】linkageConfig改变了,vxetable没更新 2025-04-04 22:45:00 +08:00
d4d0c884f0 归集spring-doc默认配置 2025-04-03 22:31:26 +08:00
e6fe3459e4 配置调整 2025-04-03 18:17:43 +08:00
9ebc3c49ec 优化swagger文档架构改造 2025-04-03 17:54:51 +08:00
45e9b03e2d app地址修改 2025-04-03 12:05:47 +08:00
696b65240f AI大模型支持 2025-04-03 12:03:34 +08:00
f4339677e7 版本发布日期调整 2025-04-03 11:50:05 +08:00
0aaf0ad3ad AIGC应用平台介绍 2025-04-03 11:33:45 +08:00
0e5cd5bb3e AIGC介绍 2025-04-03 11:29:26 +08:00
24406a4cac AI介绍 2025-04-03 11:27:53 +08:00
1a1c840a3b AI功能展示 2025-04-03 11:25:29 +08:00
2e257343a4 aigc独立专题介绍页面 2025-04-03 11:20:05 +08:00
7871c465c8 ERP风格内嵌子表生成源代码,展开子表的会根据展开的次数进行创建销毁子表的组件 2025-04-03 10:09:36 +08:00
714d9dc244 解决启动警告 WARN io.undertow.websockets.jsr:68 - UT026010: Buffer pool was not set on WebSocketDeploymentInfo, the default pool will be used 2025-04-03 00:00:18 +08:00
f23b176cf4 fix: update link text for GitHub URL in header 2025-04-02 22:45:42 +08:00
494f5e21f5 undertow配置、增加springdoc配置、简化flyway配置 2025-04-01 22:02:39 +08:00
dea260cae3 注释掉日志打印 2025-04-01 21:35:45 +08:00
0fcc08c204 fix: update import path for AiChatProperties in ChatServiceImpl 2025-04-01 21:33:08 +08:00
608d2e1318 Merge pull request #8038 from EightMonth/knife_biz
Knife4j升级4.4.0 业务代码改动
2025-04-01 21:28:35 +08:00
7e436f2efd Merge pull request #8039 from EightMonth/knife_arch
Knife4j升级4.4.0 架构代码改动
2025-04-01 21:28:23 +08:00
c18fb68e81 接口文档调整,为免登录接口排除token校验请求头 2025-04-01 17:01:58 +08:00
299f63c6e9 修改token验证请求头信息及排除不需要的接口信息 2025-04-01 15:05:08 +08:00
20037ae02b 添加误移除内容 2025-03-31 16:36:43 +08:00
dcdbb5ac7a 3.7.4 版本数据库脚本 2025-03-31 16:11:25 +08:00
cd9ba3bac9 新增改动 2025-03-31 15:58:26 +08:00
060bf2282c 更新readme 2025-03-31 15:15:17 +08:00
d88f2f81e9 knife4j升级4.4.0注解改造 2025-03-31 14:54:06 +08:00
18cc746fcf 升级java-jwt到4.5.0 2025-03-31 14:35:47 +08:00
fcc7842e89 修改自动生成接口文档范围
(cherry picked from commit ff58a1dd26)
2025-03-31 14:32:33 +08:00
e91af8cfd3 knife4j升级4.4.0架构改造 2025-03-31 14:25:56 +08:00
a57cc1fa3d 3.7.4 版本升级online在线开发模块 2025-03-31 14:23:05 +08:00
80d37f9a03 3.7.4 版本升级online在线开发模块 2025-03-31 11:58:30 +08:00
502ef2f65d 【同步3.7.4版本代码】新增全局布局隐藏配置,优化多个组件的属性和逻辑 2025-03-30 19:09:07 +08:00
62daec9c16 3.7.4 版本增量升级SQL 2025-03-30 18:40:49 +08:00
9191a8b620 【3.7.4 开源代码同步】新增断言异常类和断言工具类,优化JWT工具类,更新文件类型白名单,切换undertow配置,修改多个YAML配置文件 2025-03-30 17:51:55 +08:00
4fb53637aa 支持数据库说明 2025-03-28 18:32:58 +08:00
178a2368cb 3.7.4(预计发布日期:2025-04-10,代码逐步提交) 2025-03-28 09:35:57 +08:00
0813dee0d4 优化oConvertUtils中childArray转换逻辑 2025-03-27 19:47:09 +08:00
9579a60abd jdk支持说明:支持jdk8、17、21 2025-03-27 19:38:46 +08:00
5cd5bf7a0b 移除冗余Flyway配置,优化application.yml文件 2025-03-27 18:48:03 +08:00
c0265981d1 视频介绍 2025-03-27 18:43:18 +08:00
2ec13165d3 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	jeecg-boot/pom.xml
2025-03-27 18:26:20 +08:00
ac5ee60364 Merge pull request #8027 from EightMonth/master
移除junit4,使用默认junit5
2025-03-27 18:09:34 +08:00
5ff1b56fe2 移除junit4,使用默认junit5 2025-03-27 16:58:35 +08:00
ea914be3a6 升级至3.7.4,更新Dockerfile及jdk依赖 2025-03-27 15:19:41 +08:00
cb65e1796c 【3.7.4版本底层依赖大升级】
默认改成JDK17;fastjson升级至2.0.56;升级spring-cloud小版本;升级jimureport到最新版1.9.5;升级weixin4j到2.0.1;
spring-cloud >2021.0.8
spring-cloud-alibaba >2021.0.6.2
2025-03-27 15:19:00 +08:00
ff815f1ae6 更新版本至3.7.4并调整发布时间 2025-03-27 15:17:48 +08:00
c8ebdc162f 更新版本至3.7.4并调整发布时间 2025-03-27 15:17:08 +08:00
76df4f8600 Merge pull request #8006 from EightMonth/master
回退 "fastjson升级至2.0.43"
2025-03-25 15:04:30 +08:00
a27b94c4c6 Revert "fastjson升级至2.0.43"
This reverts commit d87ffc11e7.
2025-03-25 14:55:36 +08:00
5ad06274cb 升级shiro至1.13.0及shiro-redis至3.2.3 2025-03-25 14:34:31 +08:00
d38fa1901e Merge pull request #8003 from EightMonth/master
fastjson升级至2.0.43
2025-03-25 14:29:10 +08:00
b4aab76057 Merge branch 'master' of https://github.com/EightMonth/jeecg-boot 2025-03-25 14:00:58 +08:00
d87ffc11e7 fastjson升级至2.0.43 2025-03-25 14:00:43 +08:00
a67b3409b1 Merge pull request #7981 from EightMonth/master
修复 #7951 CVE-2023-6378
2025-03-21 23:38:08 +08:00
e11d831114 修复 #7951 CVE-2023-6378 2025-03-20 16:50:37 +08:00
ec410456b2 Merge pull request #7879 from yangyang122/feature/tenant_edit_role
in tenant mode, edit role constraints change
2025-03-20 10:46:46 +08:00
e9326cab8c Merge pull request #7651 from testnet0/master
修复flyway自动升级失败
2025-03-20 10:39:08 +08:00
61d2857ca7 【issues/7940】componentProps写成函数形式时,updateSchema写成对象时,参数没合并 --- 2025-03-20 10:09:48 +08:00
cba4847413 【issues/7948】修复JselectRole组件不支持双向绑定 --- 2025-03-20 10:09:06 +08:00
bc56384325 【issues/7954】BasicUpload组件上传文件,限制上传格式校验出错 --- 2025-03-20 10:08:20 +08:00
d7dc1b8dc7 【issues/7956】修复showSummary: false时且有内嵌子表时合计栏错位 2025-03-20 10:06:57 +08:00
11d9f2b4e4 视频介绍 2025-03-18 10:10:33 +08:00
64fcf7fcfc 提供完整的初始化sql 含积木报表最新增量sql 2025-03-16 16:04:43 +08:00
e6ddeef18c 默认关闭AI助手悬浮按钮 2025-03-13 09:53:05 +08:00
47a074d9cb 解决严重bug,War包方式部署,服务启动报错 2025-03-11 09:43:39 +08:00
c32f0407e8 更新README.md,添加AI大模型文档链接 2025-03-09 16:38:00 +08:00
22bbc23069 更新README.md,优化AI功能列表及支持模型 2025-03-07 18:25:33 +08:00
b2078d502a 更新README.md,添加AI平台支持及功能介绍 2025-03-07 18:20:10 +08:00
ed165464c8 升级druid版本到1.2.24 #7869
升级jeewx-api版本到weixin4j 2.0.0
2025-03-07 15:55:09 +08:00
9cf66e7026 Merge branch 'master' of https://github.com/zhangdaiscott/jeecg-boot 2025-03-06 22:04:19 +08:00
11f8038196 AIGC功能模块即将出炉 2025-03-06 22:03:29 +08:00
d2105dc095 in tenant mode, edit role constraints change 2025-03-04 14:58:38 +08:00
32287d98ac Merge pull request #7833 from hoperunChen/QQYUN/QQYUN-10958
[QQYUN-10958]删除jeecgboot中的MongoAutoConfiguration,因为jm-nosql中已经有了
2025-03-03 17:19:27 +08:00
31cf94ac7b Merge pull request #7850 from EightMonth/master
修复 #7702
2025-03-03 16:38:39 +08:00
0d28c92c83 master分支默认jdk8 2025-03-02 17:57:26 +08:00
1cc5cee92d 升级项目介绍 2025-03-02 17:53:04 +08:00
26246eda7a 升级druid版本到1.2.24 #7869
升级jeewx-api版本到weixin4j 2.0.0
2025-03-02 17:49:29 +08:00
10dd2a2dd0 反馈bug 2025-02-28 16:05:08 +08:00
5abc745496 jeecg官方文档更新 2025-02-26 10:30:57 +08:00
353e8e2fb6 [issues/7288]数据权限,查看自己拥有部门的权限中存在问题 #7288--- 2025-02-24 22:49:58 +08:00
56661815ec 【issues/7830】合计小数计算精度 --- 2025-02-24 22:48:45 +08:00
03b6d20fcb 修复 #7702 2025-02-24 17:33:52 +08:00
2388c8c46d [QQYUN-10958]删除jeecgboot中的MongoAutoConfiguration,因为jm-nosql中已经有了 2025-02-20 10:43:20 +08:00
45f905ddac Merge branch 'master' of https://github.com/zhangdaiscott/jeecg-boot 2025-02-19 18:45:26 +08:00
55696efcaa jimureport、jimubi升级到最新版1.9.4,补充权限注解 2025-02-19 18:45:14 +08:00
3671b81f01 Merge pull request #7564 from EightMonth/master
keys替换scan问题 修复 #6918 #6876
2025-02-19 18:42:09 +08:00
74ad7e6f92 jimureport、jimubi升级到最新版1.9.4 2025-02-19 15:03:07 +08:00
49d55a79f4 当用户没有角色保存操作权限时候操作角色保存仅保存和保存并关闭按钮会一直转圈不可用 2025-02-19 10:24:00 +08:00
8395d106a2 【issues/7773】合计没有跟着左右滚动条滚动 2025-02-19 10:23:10 +08:00
d716111ded 生成代码的时候,选择原生表单(form) 就会出错 #7817 2025-02-19 10:00:45 +08:00
81b4cbbe35 修复问题:线上ai助手后端内容一次性返回,消息不显示 2025-02-13 20:00:15 +08:00
86cfc18fe8 优化AI图标 2025-02-12 20:10:21 +08:00
98ba72ae4b 关注公众号了解官方动态 2025-02-12 10:45:52 +08:00
29248561d6 修复第三方登录接口通过token获取用户信息漏洞 2025-02-11 11:37:55 +08:00
ad74b78134 JSelectUserByDept组件的params可选参数似乎不起作用 #7758 2025-02-11 11:32:10 +08:00
bd716417e8 【issues/7738】文件中带"success"导出报错 2025-02-11 11:30:10 +08:00
1acb773adf 完善OpenApi模块文档 2025-02-10 15:51:17 +08:00
5753f3d1d9 xxl_job的sql脚本缺少字段 2025-02-10 13:51:39 +08:00
46fb1cce91 3.7.3版本启动提示连接不上mongodb #7787 2025-02-10 13:43:42 +08:00
d0487e7a9d 语言优化 2025-02-10 11:40:00 +08:00
b8c096eee5 语言调整 2025-02-10 11:26:13 +08:00
c84f62e418 升级vite6,要求必须node20+ 2025-02-08 17:28:49 +08:00
c2107ee74a 【issues/7781】修复系统设置icon点击显示错误 2025-02-08 11:34:44 +08:00
48a9ba6253 兼容deepseek-r1模型,ai助手支持think标签 2025-02-08 10:12:02 +08:00
1a4abfc89c 关注公众号了解最新资讯 2025-02-08 10:00:08 +08:00
9550c15d56 关注公众号新版抢先看 2025-02-08 09:48:00 +08:00
229256738a v3.7.3 DeepSeek版本发布 2025-02-07 18:31:15 +08:00
781432c678 AIGC功能清单 2025-02-07 18:24:56 +08:00
a4fba72da5 3.7.3 DeepSeek版本发布 2025-02-07 17:46:33 +08:00
65f90ea84a 3.7.3版本发布 2025-02-07 17:27:43 +08:00
b8ef121fe7 升级版本号3.7.3 2025-02-07 16:52:19 +08:00
d6c6a1c01f 租户套餐管理优化体验 2025-02-07 16:38:02 +08:00
7257dfe5ba AutoPoi Excel表格导入有问题,还会报个错。 #7703--- 2025-02-07 16:34:51 +08:00
18166a3c69 官网演示版本中“我的部门”功能数据展示异常 #7658--- 2025-02-06 22:47:55 +08:00
54abb67303 数据权限,查看自己拥有部门的权限中存在问题 #7288--- 2025-02-06 22:34:24 +08:00
84f46c2142 加上AI助手两个字 2025-02-06 22:24:06 +08:00
63d60092dc 【issues/7709】当dataSource是响应式时,单元格编辑输入会自动关闭 --- 2025-02-06 22:21:09 +08:00
dd9ec47cdb 【issues/7549】Online 表单开发 页面属性 查询选择模糊查询 结果生成的代码是 JRangeNumber 而且页面中不显示:父子表--] 2025-02-06 19:21:26 +08:00
49ce9e0763 AI功能效果图 2025-02-05 19:19:38 +08:00
a7089b7b16 AI新功能安排研发中(AI应用开发平台) 2025-02-05 18:50:20 +08:00
53f8c0c076 支持DeepSeek-R1本地化 2025-01-28 09:03:42 +08:00
6ad8c3d824 issues格式 2025-01-25 10:00:59 +08:00
6947f47bd3 v3.7.2发布,提供免费大屏 2025-01-25 09:57:06 +08:00
973804189e Update issue templates 2025-01-25 09:51:58 +08:00
1942980154 AI低代码平台 2025-01-20 13:49:08 +08:00
64b852e1c6 AI 低代码 2025-01-20 13:47:34 +08:00
0277a968f2 AI低代码平台 2025-01-20 13:41:51 +08:00
79ca5ebf96 JeecgBoot AI低代码平台 2025-01-20 12:24:02 +08:00
84a7ce9b7a 【issues/7706】顶部栏导航内部路由也可以支持采用新浏览器tab打开 2025-01-16 15:00:24 +08:00
ab2469ff98 AI模型支持DeepSeek 2025-01-14 21:57:37 +08:00
8fc27bb261 AI默认切换成DeepSeek v3模型,提供更优的速度和质量 2025-01-14 21:51:39 +08:00
d9737c891f 【issues/7658】我的部门无部门列表数据时,点击查询或者重置能查出数据 --- 2025-01-13 20:25:19 +08:00
17dc1916aa 【issues/7664】JVxeTable的textarea组件@blur会重复发生 --- 2025-01-13 20:23:56 +08:00
a00e4684b5 【issues/7661】vxetable用户组件maxTagCount通过业务传递 2025-01-13 20:23:14 +08:00
a2a3ff6709 【issues/7631】JVxeTable组件的getValues回调函数参数修正 ---
【issues/7624】JVxeTable 单选删除不生效 ---
2025-01-13 20:22:13 +08:00
266f49e72f 【issues/7588】选择后自动刷新表格 2025-01-13 20:20:59 +08:00
6be0f94ddc 项目说明 2025-01-12 17:55:39 +08:00
7159df8d18 默认放开AI助手,方便用户看到 2025-01-12 17:45:47 +08:00
b4cd341abc JimuReport 1.9.3版本支持权限,将平台权限传递给积木报表 2025-01-12 17:42:08 +08:00
327f034640 JimuReport和JimuBI大屏,升级到1.9.3版本 2025-01-12 17:23:15 +08:00
00b1250901 升级积木报表到1.9.3版本,解决v1.9.2严重权限BUG 2025-01-09 11:12:48 +08:00
964e9b3851 升级积木报表到1.9.3版本,解决v1.9.2严重权限BUG 2025-01-09 10:38:50 +08:00
9c5e71476f 升级积木报表到1.9.2版本,支持文件数据集 2025-01-08 16:02:03 +08:00
80a54746b1 升级vite6后要求node20+,pnpm9+ 2025-01-08 11:39:34 +08:00
9861e1a867 【issues/7601】ant-design-vue@4.2.6后弹窗全屏底部有空隙 2025-01-06 20:14:27 +08:00
5b2ac82e94 Merge pull request #7691 from liaozhiyang/up-vite6
升级到vite6且升级其它包
2025-01-06 19:51:19 +08:00
c5909cae34 升级到vite6且升级其它包 2025-01-06 19:40:57 +08:00
b0880c373b 缺少修改手机号接口 2025-01-03 11:32:56 +08:00
2b0bedd263 完善openapi,移除过期sql 2024-12-27 14:23:01 +08:00
ac30ed67bc 修复flyway自动升级失败 2024-12-25 15:38:39 +08:00
ee382c0a5b 全流程跑通 2024-12-21 23:50:07 +08:00
f7f6d0ff88 openapi全量功能 2024-12-20 17:53:45 +08:00
4d058e1dce 【issues/7561】主题切换为顶部混合模式时,页面顶部内容显示不出来,被遮盖 2024-12-17 09:43:18 +08:00
5c9c2dbebe 【issues/7548】侧边栏导航模式时会导致下面菜单滚动显示不全 2024-12-17 09:43:00 +08:00
44f7075316 账户设置->修改手机号:获取验证码接口 404 错误 #7587 2024-12-16 15:42:36 +08:00
c88bfcf35a v3.7.2发布,提供免费大屏 2024-12-12 19:48:26 +08:00
11d13c8305 v3.7.2 数据库脚本 2024-12-12 12:17:47 +08:00
0582436b9c v3.7.2 数据库脚本 2024-12-11 16:22:30 +08:00
00252f392f keys替换scan问题 修复 #6918 #6876 2024-12-10 16:24:31 +08:00
ef16814216 v3.7.2 删除报错代码暂时无用 2024-12-10 15:42:10 +08:00
a626b6a4d0 快速体验入口 2024-12-09 20:02:38 +08:00
fca5d0e54e 新版发布 3.7.2 2024-12-09 17:56:20 +08:00
1c3b9a10f1 新版发布,升级3.7.2版本号 2024-12-09 17:07:38 +08:00
b0c4194602 v3.7.2 版本代码合并 2024-12-09 15:10:46 +08:00
431 changed files with 28646 additions and 15189 deletions

1
.gitattributes vendored
View File

@ -3,3 +3,4 @@
*.ts linguist-language=vue
*.html linguist-language=vue
*.sql linguist-language=Java
*.ftl linguist-language=Java

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

@ -0,0 +1,25 @@
---
name: 提交 Bug
about: 请告诉我框架存在的问题,我会协助你解决此问题!
labels: bug
assignees: getActivity
---
##### 版本号:
##### 问题描述:
##### 错误截图:
#### 友情提示:
- 未按格式要求发帖、描述过于简单的,会被直接删掉;
- 描述问题请图文并茂,方便我们理解并快速定位问题;
- 如果使用的不是master请说明你使用的分支;

View File

@ -1,3 +1,12 @@
---
name: 提交建议
about: 请告诉我框架的不足之处,让我做得更好!
labels: help wanted
assignees: getActivity
---
##### 版本号:
@ -13,4 +22,4 @@
#### 友情提示:
- 未按格式要求发帖、描述过于简单的,会被直接删掉;
- 描述问题请图文并茂,方便我们理解并快速定位问题;
- 如果使用的不是master请说明你使用的分支;
- 如果使用的不是master请说明你使用的分支;

163
README-AI.md Normal file
View File

@ -0,0 +1,163 @@
AIGC应用平台介绍
===============
即将发布:`最新版本 V3.8.0发布提供Jeecg AIGC 提供AI应用平台+知识库问答`
> JeecgBoot 平台的AIGC功能模块是一套类似`Dify`的`AIGC应用开发平台`+`知识库问答`是一款基于LLM大语言模型AI应用平台和 RAG 的知识库问答系统。
其直观的界面结合了 AI 流程编排、RAG 管道、知识库管理、模型管理、对接向量库、实时运行可观察等让您可以快速从原型到生产拥有AI服务能力。
### AI视频介绍
[![](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/jeecg_aivideo.png)](https://www.bilibili.com/video/BV1zmd7YFE4w)
#### Dify `VS` JEECG AI
> JEECG AI与Dify相比在多个方面展现出显著的优势特别是在文档处理、格式和图片保持方面。以下是一些具体的优点
> - Markdown文档库导入
> JEECG AI允许用户直接导入整个Markdown文档库这不仅保留markdown格式还支持图片的导入确保文档内容的完整性和可视化效果。
> - 对话回复格式美观:
> 在对话过程中JEECG AI能够保持回复内容的原格式也不丢失图片使得输出的文章更加美观不会出现格式错乱的情况还支持图片的渲染。
> - PDF文档导入与格式转换
> JEECG AI在处理PDF文档时能够更好地保持原始格式和图片确保转换后的内容与原始文档一致。这哥功能在许多AI产品中表现不佳而JEECG AI在这方面做出了显著的优化
| 功能 | Dify | Jeecg AI |
| --- | --- | --- |
| AI工作流 | 有 | 有 |
| RAG 管道向量搜索 | 有 | 有 |
| AI模型管理 | 有 | 有 |
| AI应用管理 | 有 | 有 |
| AI知识库 | 有 | 有 |
| 产品方向 | 一款独立的 LLM 应用开发平台 | 低代码与AIGC应用二者结合的平台 |
| 业务集成 | 业务集成能力弱 | 更方便与业务系统集成,调用系统接口和逻辑更加方便 |
| AI业务流 | 侧重AI逻辑流程 | AI流程编排作为低代码的业务引擎用户可以通过AI流程配置各种业务流和AI流程 |
| 上传markdown文档库(支持图片) | 不支持 | 支持 |
| AI对话支持发图和展示图片 | 支持 | 支持 |
| 实现语言 | python + react | JAVA + vue3 |
| 功能 | Dify | Jeecg AI |
|------------|------------------|-----------------------------------------|
| AI工作流 | 有 | 有 |
| RAG 管道向量搜索 | 有 | 有 |
| AI模型管理 | 有 | 有 |
| AI应用管理 | 有 | 有 |
| AI知识库 | 有 | 有 |
| 产品方向 | 一款独立的 LLM 应用开发平台 | 低代码与AIGC应用二者结合的平台 |
| 业务集成 | 业务集成能力弱 | 更方便与业务系统集成,调用系统接口和逻辑更加方便 |
| AI业务流 | 侧重AI逻辑流程 | AI流程编排作为低代码的业务引擎用户可以通过AI流程配置各种业务流和AI流程 |
| 实现语言 | python + react | JAVA + vue3 |
| 上传markdown文档库(支持图片) | 不支持 | 支持 |
| AI对话支持发图和展示图片 | 支持 | 支持 |
## 功能特点
- AI流程: 提供强大的AI流程设计器引擎支持编排 AI 工作过程,满足复杂业务场景,支持画布上构建和实时运行查看 AI流程运行情况。
- AI流程即服务: 通过AI流程编排你需要的智能体结合AI+自定义开发节点 实现功能性 API让你瞬间拥有各种智能体API。
- AI助手对话功能: 集成 ChatGPT、Deepseek、智普、私有大模型 等 AI 模型,提供智能对话和生成式 AI 功能,深度与知识库结合提供更精准的知识。
- RAG 功能: 涵盖从文档摄入到检索的所有内容,支持从 PDF、PPT 和其他常见文档格式中提取文本支持检索增强生成RAG将未训练数据与 AI 模型集成,提升智能交互能力。
- AI 知识库: 通过导入文档或已有问答对进行训练,让 AI 模型能根据文档以交互式对话方式回答问题。
- 模型管理支持对接各种大模型包括本地私有大模型Deepseek/ Llama 3 / Qwen 2 等)、国内公共大模型(通义千问 / 腾讯混元 / 字节豆包 / 百度千帆 / 智谱 AI / Kimi 等和国外公共大模型OpenAI / Claude / Gemini 等);
- 无缝嵌入Iframe一键嵌入,支持将AI聊天助手快速嵌入到第三方系统让系统快速拥有智能问答能力提高用户满意度。
## 产品体验
- 使用手册https://help.jeecg.com/aigc
- 演示地址https://boot3.jeecg.com
## 功能列表
- AI应用管理(普通应用、高级流程应用)
- AI模型管理
- AI知识库
- AI应用平台(普通、对接AI流程)
- AI流程编排
- AI聊天支持嵌入第三方
- AI向量库对接
## 支持AI模型
| AI大模型 | 支持 |
|---------------| --- |
| DeepSeek | √ |
| ChatGTP | √ |
| Qwq | √ |
| 智库 | √ |
| Ollama本地搭建大模型 | √ |
| 等等。。 | √ |
## AIGC能做什么
AIGC模块是一个基于AI的自动化流程编排工具和聊天应用搭建平台它可以帮助用户快速生成AI流程接口和聊天应用提高效率。
以下是一些具体的应用场景和示例:
- 你可能需要一个翻译接口可以通过AI流程编排搭建出来。
- 你可能需要一个接口转换工具可以通过AI流程编排搭建出来。比如jimureport所需要接口返回格式与你的系统不同你通过AI接口实现自动转换
- 你可能需要一个聊天机器人可以通过AI流程编排搭建出来。
- 你可能需要一个自动化流程可以通过AI流程编排搭建出来。
- 你可能需要一个自动化处理文件的流程可以通过AI流程结合python脚本实现操作电脑文件等。
## AI应用平台功能展示
AI模型列表
![](https://oscimg.oschina.net/oscnet//a5fb3e0d69ca1706b0de221535c7acaa.png)
选择AI模型配置你的参数
![](https://oscimg.oschina.net/oscnet//1f941472758a5fc227f54f2683953b8e.png)
AI知识库支持手工录入文本导入pdf\\word\\excel等文档支持问答对训练
![](https://oscimg.oschina.net/oscnet//150bb33f48d6c8e2ae059e2a58f4200b.png)
![](https://oscimg.oschina.net/oscnet//032d16c915b0f79318935484c81df260.png)
AI流程提供强大的AI流程设计器引擎支持编排 AI 工作过程,满足复杂业务场景,支持画布上构建和实时运行查看 AI流程运行情况。
![](https://oscimg.oschina.net/oscnet//f40f9aa275cd4aea94e1c209513151e2.png)
目前支持的节点有开始、结束、AI知识库节点、AI节点、分类节点、分支节点、JAVA节点、脚本节点、子流程节点、http请求节点、直接回复节点等节点
![](https://oscimg.oschina.net/oscnet//6d86480ab1bbfab5b2e6992b416b2152.png)
节点项配置
![](https://oscimg.oschina.net/oscnet//90a5f76b6b4fc406e2e2b87245b35459.png)
在线运行看结果
![](https://oscimg.oschina.net/oscnet//bc9817a7bbd94936a5a3e885abe3cb38.png)
AI应用配置支持AI流程配置和简单的AI配置
![](https://oscimg.oschina.net/oscnet//a853d9be4d3756806799ad025e722df8.png)![](https://oscimg.oschina.net/oscnet//d3bcbf5977c6fb75a8f996e1e40590be.png)
可以关联多个知识库右侧是AI智能回复你可以搭建自己的智能体比如搭建一个 “诗词达人” “翻译助手”
![](https://oscimg.oschina.net/oscnet//c26a848136be3e22ec1e0651e78976c2.png)
可以将创建的聊天应用,集成到第三方系统中
![](https://oscimg.oschina.net/oscnet//39c6f589ef46f0454b229915ffa263f4.png)

View File

@ -4,15 +4,15 @@
JEECG BOOT Low Code Development Platform
JEECG BOOT AI Low Code Platform
===============
Current version: 3.7.1 (Release date: 2024-09-12)
Current version: 3.7.4 (Release date: 2025-04-07)
[![AUR](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://github.com/zhangdaiscott/jeecg-boot/blob/master/LICENSE)
[![](https://img.shields.io/badge/Author-guojusoft-orange.svg)](http://www.jeecg.com)
[![](https://img.shields.io/badge/version-3.7.1-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot)
[![](https://img.shields.io/badge/version-3.7.4-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot)
[![GitHub stars](https://img.shields.io/github/stars/zhangdaiscott/jeecg-boot.svg?style=social&label=Stars)](https://github.com/zhangdaiscott/jeecg-boot)
[![GitHub forks](https://img.shields.io/github/forks/zhangdaiscott/jeecg-boot.svg?style=social&label=Fork)](https://github.com/zhangdaiscott/jeecg-boot)
@ -21,9 +21,9 @@ Current version: 3.7.1 (Release date: 2024-09-12)
Project introduction
-----------------------------------
<h3 align="center">Java Low Code Platform for Enterprise web applications</h3>
<h3 align="center">Java AI Low Code Platform for Enterprise web applications</h3>
JeecgBoot is a `low code development platform` based on code `generators`! Front and back end separation architecture SpringBoot2.x, SpringCloud, Ant Design&Vue, Mybatis plus, Shiro, JWT, support for microservices. The powerful code generator makes the front and back end of the code generation, low code development! JeecgBoot leads a new low-code development paradigm (OnlineCoding-> Code Generator -> Manual MERGE) that helps resolve 70% of the duplication in Java projects and makes development more business-focused. Not only can quickly improve efficiency, save research and development costs, but also do not lose flexibility!
JeecgBoot is a `AI low code platform` based on code `generators`! Front and back end separation architecture SpringBoot2.x, SpringCloud, Ant Design&Vue, Mybatis plus, Shiro, JWT, support for microservices. The powerful code generator makes the front and back end of the code generation, low code development! JeecgBoot leads a new low-code development paradigm (OnlineCoding-> Code Generator -> Manual MERGE) that helps resolve 70% of the duplication in Java projects and makes development more business-focused. Not only can quickly improve efficiency, save research and development costs, but also do not lose flexibility!
JeecgBoot provides a series of low code modules to make Online development truly zero code: Online form development, online reports, report configuration capabilities, online chart design, large screen design, mobile configuration capabilities, form designer, online design flow, process automation configuration, plug-in capabilities (pluggable) and more!
@ -32,11 +32,12 @@ The purpose of JEECG is: simple functions are implemented by OnlineCoding config
JEECG Business process: Using workflow to implement and extend the task interface for developing and writing business logic, forms provides a variety of solutions: form designer, online configuration form, and coding form. At the same time, the separation design of process and form (loose coupling) is realized, and the flexible configuration of task nodes is supported, which not only ensures the confidentiality of the company's process, but also reduces the workload of developers.
AI Empowering Low-Code: Currently, JeecgBoot supports AI large models such as ChatGPT and DeepSeek. The latest version defaults to using DeepSeek, which offers faster speed and higher quality. It now provides features such as AI chat assistant, AI table creation, and AI report generation.
Technical support
-----------------------------------
Problems or bugs in use can be found in [Making on the Issues](https://github.com/jeecgboot/JeecgBoot/issues/new)
Problems or bugs in use can be found in [Making on the Issues](https://github.com/jeecgboot/JeecgBoot/issues/new?template=bug_report.md)
##### Project description
@ -48,6 +49,11 @@ Problems or bugs in use can be found in [Making on the Issues](https://github.co
| `jeecg-uniapp` | [APP development framework, a code multi terminal adaptation, and support APP, small program, H5](https://github.com/jeecgboot/jeecg-uniapp) |
### Video Introduction
[![](https://upload.jeecg.com/jeecg/qiaoqiaoyunsite/jeecgvideo02.png)](https://www.bilibili.com/video/BV1Nk4y1o7Qc)
Download other source code
-----------------------------------
@ -57,14 +63,14 @@ Download other source code
For the project
-----------------------------------
Jeecg-Boot low code development platform can be applied in the development of any J2EE project, especially for SAAS projects, enterprise information management system (MIS), internal office system (OA), enterprise resource planning system (ERP), customer relationship management system (CRM), etc. Its semi-intelligent manual Merge development method, Can significantly improve the development efficiency of more than 70%, greatly reduce the development cost.
Jeecg-Boot AI low code platform can be applied in the development of any J2EE project, especially for SAAS projects, enterprise information management system (MIS), internal office system (OA), enterprise resource planning system (ERP), customer relationship management system (CRM), etc. Its semi-intelligent manual Merge development method, Can significantly improve the development efficiency of more than 70%, greatly reduce the development cost.
Starts the project
-----------------------------------
- [IDEA Quick start](https://help.jeecg.com/java/setup/idea/startup.html)
- [Docker Quick start](https://help.jeecg.com/java/docker/quick.html)
- [IDEA Quick start](https://help.jeecg.com/java/setup/idea/startup)
- [Docker Quick start](https://help.jeecg.com/java/docker/quick)
@ -73,9 +79,9 @@ Technical documentation
- Website [http://www.jeecg.com](http://www.jeecg.com)
- Demo [OnlineDemo](http://boot3.jeecg.com) | [APP](http://jeecg.com/appIndex)
- Doc [http://help.jeecg.com](http://help.jeecg.com)
- Doc [DocumentCenter](http://help.jeecg.com) | [AI Config](https://help.jeecg.com/java/ai/aichat)
- Newbie guide [Quick start](http://www.jeecg.com/doc/quickstart) | [Q&A ](http://www.jeecg.com/doc/qa) | [1 minute experience](https://my.oschina.net/jeecg/blog/3083313)
- QQ group ⑩716488839、⑨808791225、⑧825232878、⑦791696430、⑥730954414(full)、683903138(full)、⑤860162132(full)、④774126647(full)、③816531124(full)、②769925425(full)、①284271917(full)
- QQ group ⑩716488839、⑨808791225
@ -175,7 +181,7 @@ Technical Architecture:
#### Development Environment
- Language: Java 8+ (17)
- Language: Java Default Jdk17(support jdk8、jdk21)
- IDE(JAVA) : IDEA (lombok plug-in must be installed)
@ -192,17 +198,17 @@ Technical Architecture:
- Basic framework: Spring Boot 2.7.18
- Microservice framework: Spring Cloud Alibaba 2021.0.1.0
- Microservice framework: Spring Cloud Alibaba 2021.0.6.2
- Persistence layer framework: MybatisPlus 3.5.3.2
- Report tool: JimuReport 1.8.1
- Report tool: JimuReport 1.9.5
- Security framework: Apache Shiro 1.12.0, Jwt 3.11.0
- Security framework: Apache Shiro 1.13.0, Jwt 4.5.0
- Microservice technology stack: Spring Cloud Alibaba, Nacos, Gateway, Sentinel, Skywalking
- Database connection pool: Alibaba Druid 1.1.22
- Database connection pool: Alibaba Druid 1.1.24
- Log printing: logback
@ -216,9 +222,11 @@ Technical Architecture:
#### Front-end environment requirements
* `Node.js 、npm 、pnpm`
* pnpm `v9+` is now required.
* Node.js Version suggestion: `v20.15.0`
` ( Since Vite5 no longer supports EOL Node.js 14/16/17/19, Node.js 18/20 + is now required )`
` ( Since Vite6 Node.js 18/20 + is now required )`
#### Support library
| database | support |
@ -233,6 +241,23 @@ Technical Architecture:
| TiDB | √ |
#### AI Support
| AI Model | Supported |
| --- | --- |
| DeepSeek | √ |
| ChatGPT | √ |
| Qwq | √ |
| 智库 | √ |
| Ollama本地搭建大模型 | √ |
| 等等。。 | √ |
AI Config https://help.jeecg.com/java/ai/aichat
AI APP: https://help.jeecg.com/aigc
## Microservice solutions
- 1. Service registration and discovery Nacos √
@ -243,7 +268,7 @@ Technical Architecture:
- 6. Distributed files Minio and Alioss √
- 7. Unified permission control
- 8. Service monitoring SpringBootAdmin√
- 9. link tracking Skywalking [reference document](https://help.jeecg.com/java/springcloud/super/skywarking.html)
- 9. link tracking Skywalking [reference document](https://help.jeecg.com/java/springcloud/super/skywarking)
- 10. Messaging middleware RabbitMQ √
- 11. Distributed task xxl-job √
- 12. Distributed Transaction Seata
@ -260,8 +285,8 @@ Technical Architecture:
![功能蓝图](https://jeecgos.oss-cn-beijing.aliyuncs.com/upload/test/Jeecg-Boot-lantu202005_1590912449914.jpg "在这里输入图片标题")
### quick start
- Microservice Development [Monomer upgrade to microservice](https://help.jeecg.com/java/springcloud/switchcloud/monomer.html)
- [Docker starts the micro-service background](https://help.jeecg.com/java/docker/springcloud.html)
- Microservice Development [Monomer upgrade to microservice](https://help.jeecg.com/java/springcloud/switchcloud/monomer)
- [Docker starts the micro-service background](https://help.jeecg.com/java/docker/springcloud)
### Effect of system

168
README.md
View File

@ -1,13 +1,13 @@
JeecgBoot 低代码开发平台
JeecgBoot AI低代码平台
===============
当前最新版本: 3.7.1发布日期2024-09-12
当前最新版本: 3.7.4发布日期2025-04-07
[![AUR](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://github.com/jeecgboot/JeecgBoot/blob/master/LICENSE)
[![](https://img.shields.io/badge/Author-北京国炬软件-orange.svg)](http://guojusoft.com)
[![](https://img.shields.io/badge/version-3.7.1-brightgreen.svg)](https://github.com/jeecgboot/JeecgBoot)
[![](https://img.shields.io/badge/version-3.7.4-brightgreen.svg)](https://github.com/jeecgboot/JeecgBoot)
[![GitHub stars](https://img.shields.io/github/stars/zhangdaiscott/jeecg-boot.svg?style=social&label=Stars)](https://github.com/jeecgboot/JeecgBoot)
[![GitHub forks](https://img.shields.io/github/forks/zhangdaiscott/jeecg-boot.svg?style=social&label=Fork)](https://github.com/jeecgboot/JeecgBoot)
@ -16,61 +16,110 @@ JeecgBoot 低代码开发平台
项目介绍
-----------------------------------
<h3 align="center">Java Low Code Platform for Enterprise web applications</h3>
<h3 align="center">Java AI Low Code Platform for Enterprise web applications</h3>
JeecgBoot 是一款基于代码生成器的`低代码开发平台`!前后端分离架构 SpringBoot2.x3.xSpringCloudAnt Design Vue3Mybatis-plusShiroJWT支持微服务强大的代码生成器让前后端代码一键生成,实现低代码开发! JeecgBoot集成AI模型能力引领新的低代码开发模式(OnlineCoding-> 代码生成器-> 手工MERGE) 帮助解决Java项目70%的重复工作,让开发更多关注业务。既能快速提高效率,节省研发成本,同时又不失灵活性!
JeecgBoot 是一款基于`BPM``代码生成器`的 AI低代码平台!前后端分离架构 SpringBoot2.x/3.xSpringCloudAnt Design Vue3Mybatis-plusShiroJWT支持微服务、多租户;支持 AI 大模型 DeepSeek 和 ChatGPT、Ollama本地模型; 强大的代码生成器让前后端代码一键生成,无需写任何代码! JeecgBoot 引领 AI 低代码开发模式(AI生成-> OnlineCoding-> 代码生成器-> 手工MERGE) 帮助解决Java项目80%的重复工作,让开发更多关注业务。既能快速提高效率,节省成本,同时又不失灵活性!AIGC能力AI对话助手、AI建表、AI写文章、AI流程编排、AI知识库问答等等.
JeecgBoot 提供了一系列`低代码模块`,实现在线开发`真正的零代码`Online表单开发、Online报表、报表配置能力、在线图表设计、仪表盘设计、大屏设计、移动配置能力、表单设计器、在线设计流程、流程自动化配置、插件能力可插拔等等
JeecgBoot 提供了一系列 `AI能力` `低代码模块`,实现在线开发`真正的零代码`Online表单开发、Online报表、报表配置能力、在线图表设计、仪表盘设计、大屏设计、移动配置能力、表单设计器、在线设计流程、流程自动化配置、插件能力可插拔、AI对话助手AI建表、AI写文章、AI流程编排、AI知识库问答、AI赋能低代码等等!
`JEECG宗旨是:` 简单功能由OnlineCoding配置实现做到`零代码开发`复杂功能由代码生成器生成进行手工Merge 实现`低代码开发`,既保证了`智能`又兼顾`灵活`;实现了低代码开发的同时又支持灵活编码,解决了当前低代码产品普遍不灵活的弊端!
`JEECG业务流程:` 采用工作流来实现、扩展出任务接口,供开发编写业务逻辑,表单提供多种解决方案: 表单设计器、online配置表单、编码表单。同时实现了流程与表单的分离设计松耦合、并支持任务节点灵活配置既保证了公司流程的保密性又减少了开发人员的工作量。
`AI赋能低代码:` 目前JeecgBoot支持AI大模型`ChatGPT``DeepSeek`,现在最新版默认使用`DeepSeek`速度更快质量更高。目前提供了AI对话助手、AI建表、AI报表、AI写文章、AI流程编排、AI知识库问答等功能。
### 视频介绍
[![](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/flow_video.png)](https://www.bilibili.com/video/BV1Nk4y1o7Qc)
适用项目
-----------------------------------
Jeecg-Boot低代码开发平台可以应用在任何J2EE项目的开发中支持信创国产化默认适配达梦和人大金仓。尤其适合SAAS项目、企业信息管理系统MIS、内部办公系统OA、企业资源计划系统ERP、客户关系管理系统CRM其半智能手工Merge的开发方式可以显著提高开发效率70%以上,极大降低开发成本。
JeecgBoot AI低代码平台可以应用在任何J2EE项目的开发中支持信创国产化默认适配达梦和人大金仓。尤其适合SAAS项目、企业信息管理系统MIS、内部办公系统OA、企业资源计划系统ERP、客户关系管理系统CRM其半智能手工Merge的开发方式可以显著提高开发效率70%以上,极大降低开发成本。
#### 项目说明
项目说明
-----------------------------------
| 项目名 | 说明 |
|--------------------|------------------------|
| `jeecg-boot` | 后端源码JAVASpringBoot微服务架构 |
| `jeecgboot-vue3` | 前端源码VUE3vue3+vite5+ts最新技术栈 |
| `jeecg-uniapp` | [配套APP框架](https://github.com/jeecgboot/jeecg-uniapp) 适配多个终端支持APP、小程序、H5 |
| `jeecgboot-vue3` | 前端源码VUE3vue3+vite6+ts最新技术栈 |
| `JeecgUniapp` | [配套APP框架](https://github.com/jeecgboot/JeecgUniapp) 适配多个终端支持APP、小程序、H5 |
开源协议说明
-----------------------------------
JeecgBoot开源版本底层完全开源可以自主开发遵循Apache2.0协议,详细见 https://github.com/jeecgboot/JeecgBoot#Apache-2.0-1-ov-file
开源协议中文释意如下:
- 1.JeecgBoot开源版本无任何限制在遵循本开源协议条款下允许商用使用不会造成侵权行为。
- 2.允许基于本平台软件开展业务系统开发。
- 3.在任何情况下,您不得使用本软件开发可能被认为与本软件竞争的软件。
- 4.针对企业用户我们也提供“企业级版本”,详细见 https://jeecg.com/vip
技术文档
-----------------------------------
- 官方网站: [http://www.jeecg.com](http://www.jeecg.com)
- 在线演示 [在线演示](http://boot3.jeecg.com) | [APP演示](http://jeecg.com/appIndex)
- 开发文档: [https://help.jeecg.com](https://help.jeecg.com)
- 反馈问题 [在Github上提Issues](https://github.com/jeecgboot/JeecgBoot/issues/new)
- 新手指南: [快速入门](http://www.jeecg.com/doc/quickstart) | [入门视频](http://jeecg.com/doc/video)
- 在线演示 [平台演示](http://boot3.jeecg.com) | [APP演示](http://jeecg.com/appIndex) | [体验低代码](https://jeecg.blog.csdn.net/article/details/106079007) | [体验零代码](https://app.qiaoqiaoyun.com/myapps/index)
- 开发文档: [文档中心](https://help.jeecg.com) | [AIGC大模块](https://help.jeecg.com/aigc)
- 新手指南: [快速入门](http://www.jeecg.com/doc/quickstart) | [入门视频](http://jeecg.com/doc/video) | [如何反馈问题](https://github.com/jeecgboot/JeecgBoot/issues/new?template=bug_report.md)
- QQ交流群 ⑩716488839、⑨808791225(满)、其他(满)
启动项目
-----------------------------------
- [IDEA启动前后端项目](https://help.jeecg.com/java/setup/idea/startup.html)
- [Docker一键启动前后端](https://help.jeecg.com/java/docker/quick.html)
- [IDEA启动前后端项目](https://help.jeecg.com/java/setup/idea/startup)
- [Docker一键启动前后端](https://help.jeecg.com/java/docker/quick)
AIGC应用平台介绍
-----------------------------------
> JeecgBoot 平台的AIGC功能模块是一套类似`Dify`的`AIGC应用开发平台`+`知识库问答`是一款基于LLM大语言模型AI应用平台和 RAG 的知识库问答系统。
其直观的界面结合了 AI 流程编排、RAG 管道、知识库管理、模型管理、对接向量库、实时运行可观察等让您可以快速从原型到生产拥有AI服务能力。
- [AIGC专题介绍页](README-AI.md)
- [AIGC开发文档](https://help.jeecg.com/aigc)
##### AI视频介绍
[![](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/jeecg_aivideo.png)](https://www.bilibili.com/video/BV1zmd7YFE4w)
##### Dify `VS` JEECG AI
> JEECG AI与Dify相比在多个方面展现出显著的优势特别是在文档处理、格式和图片保持方面。以下是一些具体的优点
> - Markdown文档库导入
> JEECG AI允许用户直接导入整个Markdown文档库这不仅保留markdown格式还支持图片的导入确保文档内容的完整性和可视化效果。
> - 对话回复格式美观:
> 在对话过程中JEECG AI能够保持回复内容的原格式也不丢失图片使得输出的文章更加美观不会出现格式错乱的情况还支持图片的渲染。
> - PDF文档导入与格式转换
> JEECG AI在处理PDF文档时能够更好地保持原始格式和图片确保转换后的内容与原始文档一致。这哥功能在许多AI产品中表现不佳而JEECG AI在这方面做出了显著的优化
##### 功能大模块
- AI应用开发平台
- AI知识库系统
- AI大模型管理
- AI流程编排
- AI对话支持图片
- AI对话助手(智能问答)
- AI建表Online表单
- AI写文章CMS
- AI表单字段建议表单设计器
##### AI大模型支持
| AI大模型 | 支持 |
| --- | --- |
| DeepSeek | √ |
| ChatGTP | √ |
| Qwq | √ |
| 智库 | √ |
| Ollama本地模型 | √ |
| 等等。。 | √ |
技术架构:
@ -79,15 +128,16 @@ JeecgBoot开源版本底层完全开源可以自主开发遵循Apache2.0
#### 后端
- IDE建议 IDEA (必须安装lombok插件 )
- 语言Java 8+ (支持17)
- 语言Java 默认jdk17(支持jdk8、jdk21)
- 依赖管理Maven
- 基础框架Spring Boot 2.7.18
- 微服务框架: Spring Cloud Alibaba 2021.0.1.0
- 微服务框架: Spring Cloud Alibaba 2021.0.6.2
- 持久层框架MybatisPlus 3.5.3.2
- 报表工具: JimuReport 1.8.1
- 安全框架Apache Shiro 1.12.0Jwt 3.11.0
- 报表工具: JimuReport 1.9.5
- 安全框架Apache Shiro 1.13.0Jwt 4.5.0
- 微服务技术栈Spring Cloud Alibaba、Nacos、Gateway、Sentinel、Skywalking
- 数据库连接池阿里巴巴Druid 1.1.22
- 数据库连接池阿里巴巴Druid 1.1.24
- AI大模型支持 `ChatGPT` `DeepSeek`切换
- 日志打印logback
- 缓存Redis
- 其他autopoi, fastjsonpoiSwagger-uiquartz, lombok简化代码等。
@ -98,20 +148,23 @@ JeecgBoot开源版本底层完全开源可以自主开发遵循Apache2.0
#### 前端
- 前端IDE建议WebStorm、Vscode
- 采用 Vue3.0+TypeScript+Vite5+Ant-Design-Vue等新技术方案包括二次封装组件、utils、hooks、动态菜单、权限校验、按钮级别权限控制等功能
- 最新技术栈Vue3.0 + TypeScript + Vite5 + ant-design-vue4 + pinia + echarts + unocss + vxe-table + qiankun + es6
- 采用 Vue3.0+TypeScript+Vite6+Ant-Design-Vue等新技术方案包括二次封装组件、utils、hooks、动态菜单、权限校验、按钮级别权限控制等功能
- 最新技术栈Vue3.0 + TypeScript + Vite6 + ant-design-vue4 + pinia + echarts + unocss + vxe-table + qiankun + es6
- 依赖管理node、npm、pnpm
#### 前端环境要求
* 本地环境安装 `Node.js 、npm 、pnpm`
* pnpm 要求`9+` 版本以上
* Node.js 版本建议`v20.15.0`,要求`Node 20+` 版本以上
` ( 因为Vite5 不再支持已 EOL 的 Node.js 14 / 16 / 17 / 19现在需要 Node.js 18 / 20+ )`
` ( 因为Vite6 需要 Node.js 18 / 20+ )`
#### 支持
#### 平台支持数据
> jeecgboot平台支持以下数据库默认我们只提供mysql脚本其他数据库可以参考[转库文档](https://my.oschina.net/jeecg/blog/4905722)自己转。
| 数据库 | 支持 |
| --- | --- |
@ -120,10 +173,11 @@ JeecgBoot开源版本底层完全开源可以自主开发遵循Apache2.0
| Sqlserver2017 | √ |
| PostgreSQL | √ |
| MariaDB | √ |
| MariaDB | √ |
| 达梦 | √ |
| 人大金仓 | √ |
| TiDB | √ |
| kingbase8 | √ |
## 微服务解决方案
@ -137,7 +191,7 @@ JeecgBoot开源版本底层完全开源可以自主开发遵循Apache2.0
- 6、分布式文件 Minio、阿里OSS √
- 7、统一权限控制 JWT + Shiro √
- 8、服务监控 SpringBootAdmin√
- 9、链路跟踪 Skywalking [参考文档](https://help.jeecg.com/java/springcloud/super/skywarking.html)
- 9、链路跟踪 Skywalking [参考文档](https://help.jeecg.com/java/springcloud/super/skywarking)
- 10、消息中间件 RabbitMQ √
- 11、分布式任务 xxl-job √
- 12、分布式事务 Seata
@ -149,8 +203,8 @@ JeecgBoot开源版本底层完全开源可以自主开发遵循Apache2.0
#### 微服务方式启动
- [单体快速切换微服务](https://help.jeecg.com/java/springcloud/switchcloud/monomer.html)
- [Docker一键启动微服务前后端](https://help.jeecg.com/java/docker/quickcloud.html)
- [单体快速切换微服务](https://help.jeecg.com/java/springcloud/switchcloud/monomer)
- [Docker一键启动微服务前后端](https://help.jeecg.com/java/docker/quickcloud)
#### 微服务架构图
@ -165,9 +219,9 @@ JeecgBoot开源版本底层完全开源可以自主开发遵循Apache2.0
* 3.开发效率高,采用代码生成器单表、树列表、一对多、一对一等数据模型增删改查功能一键生成菜单配置直接使用引入AI能力支持自动建表等功能
* 4.代码生成器提供强大模板机制,支持自定义模板,目前提供四套风格模板(单表两套、树模型一套、一对多三套)
* 5.代码生成器非常智能在线业务建模、在线配置、所见即所得支持23种类控件一键生成前后端代码大幅度提升开发效率不再为重复工作发愁。
* 6.AI能力目前JeecgBoot支持AI大模型chatgpt和deepseek现在最新版默认使用deepseek速度更快质量更高。目前提供了AI对话助手、AI建表、AI报表等功能。
* 6.低代码能力Online在线表单无需编码通过在线配置表单实现表单的增删改查支持单表、树、一对多、一对一等模型实现人人皆可编码
* 7.低代码能力Online在线报表无需编码通过在线配置方式实现数据报表可以快速抽取数据减轻开发压力实现人人皆可编码
* 8.低代码能力Online在线图表无需编码通过在线配置方式实现曲线图柱状图数据报表等支持自定义排版布局实现人人皆可编码
* 7.低代码能力Online在线报表、Online在线图表(无需编码,通过在线配置方式,实现数据报表和图形报表,可以快速抽取数据,减轻开发压力,实现人人皆可编码)
* 9.封装完善的用户、角色、菜单、组织机构、数据字典、在线定时任务等基础功能,支持访问授权、按钮权限、数据权限等功能
* 10.常用共通封装,各种工具类(定时任务,短信接口,邮件发送,Excel导入导出等),基本满足80%项目需求
* 11.简易Excel导入导出支持单表导出和一对多表模式导出生成的代码自带导入导出功能
@ -226,12 +280,20 @@ JeecgBoot开源版本底层完全开源可以自主开发遵循Apache2.0
### 功能模块
```
├─AI开发
│ ├─支持AI大模型ChatGPT和DeepSeek
│ ├─AI对话助手
│ ├─AI建表
│ ├─AI写文章
│ ├─AI流程编排研发中
│ ├─AI知识库问答系统研发中
│ ├─AI应用开发平台研发中
│ ├─AI聊天窗口支持嵌入第三方研发中
├─Online在线开发(低代码)
│ ├─Online在线表单
│ ├─Online代码生成器
│ ├─Online在线报表
│ ├─仪表盘设计器
│ ├─AI助手
│ ├─系统编码规则
│ ├─系统校验规则
├─积木报表设计器
@ -366,6 +428,24 @@ JeecgBoot开源版本底层完全开源可以自主开发遵循Apache2.0
### 系统效果
##### AI功能
AI聊天助手
![](https://oscimg.oschina.net/oscnet//65298d5710b4e6039a5f802b5f8505c5.png)
AI建表
![](https://oscimg.oschina.net/oscnet/up-381423599f219a67def45dfd9a99df8ef3f.png)
![](https://oscimg.oschina.net/oscnet/up-1508c2b0708c365605f68893044ee11f20d.png)
AI写文章
![](https://oscimg.oschina.net/oscnet/up-e3ee5b1fe497308805aa5e324b72994af79.png)
##### PC端
![](https://oscimg.oschina.net/oscnet/up-000530d95df337b43089ac77e562494f454.png)
@ -384,8 +464,6 @@ JeecgBoot开源版本底层完全开源可以自主开发遵循Apache2.0
![](https://oscimg.oschina.net/oscnet/up-16c07e000278329b69b228ae3189814b8e9.png)
##### AI助手
![](https://oscimg.oschina.net/oscnet/up-7c6405641a40f56638999d52da0cb5b4343.png)
##### 仪表盘设计器

View File

@ -2,12 +2,12 @@
JeecgBoot 低代码开发平台
===============
当前最新版本: 3.7.1发布日期2024-09-12
当前最新版本: 3.7.4发布日期2025-04-10
[![AUR](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](https://github.com/zhangdaiscott/jeecg-boot/blob/master/LICENSE)
[![](https://img.shields.io/badge/Author-北京国炬软件-orange.svg)](http://jeecg.com/aboutusIndex)
[![](https://img.shields.io/badge/version-3.7.1-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot)
[![](https://img.shields.io/badge/version-3.7.4-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot)
[![GitHub stars](https://img.shields.io/github/stars/zhangdaiscott/jeecg-boot.svg?style=social&label=Stars)](https://github.com/zhangdaiscott/jeecg-boot)
[![GitHub forks](https://img.shields.io/github/forks/zhangdaiscott/jeecg-boot.svg?style=social&label=Fork)](https://github.com/zhangdaiscott/jeecg-boot)
@ -44,14 +44,14 @@ JeecgBoot 是一款基于代码生成器的`低代码开发平台`!前后端
启动项目
-----------------------------------
- [IDEA启动前后端项目](https://help.jeecg.com/java/setup/idea/startup.html)
- [Docker一键启动前后端](https://help.jeecg.com/java/docker/quick.html)
- [IDEA启动前后端项目](https://help.jeecg.com/java/setup/idea/startup)
- [Docker一键启动前后端](https://help.jeecg.com/java/docker/quick)
微服务启动
-----------------------------------
- [单体快速切换微服务](https://help.jeecg.com/java/springcloud/switchcloud/monomer.html)
- [Docker启动微服务后台](https://help.jeecg.com/java/docker/springcloud.html)
- [单体快速切换微服务](https://help.jeecg.com/java/springcloud/switchcloud/monomer)
- [Docker启动微服务后台](https://help.jeecg.com/java/docker/springcloud)
@ -66,10 +66,10 @@ JeecgBoot 是一款基于代码生成器的`低代码开发平台`!前后端
- 基础框架Spring Boot 2.7.18
- 微服务框架: Spring Cloud Alibaba 2021.0.1.0
- 持久层框架MybatisPlus 3.5.3.2
- 报表工具: JimuReport 1.8.1
- 报表工具: JimuReport 1.9.4
- 安全框架Apache Shiro 1.12.0Jwt 3.11.0
- 微服务技术栈Spring Cloud Alibaba、Nacos、Gateway、Sentinel、Skywalking
- 数据库连接池阿里巴巴Druid 1.1.22
- 数据库连接池阿里巴巴Druid 1.1.24
- 日志打印logback
- 缓存Redis
- 其他autopoi, fastjsonpoiSwagger-uiquartz, lombok简化代码等。
@ -113,7 +113,7 @@ JeecgBoot 是一款基于代码生成器的`低代码开发平台`!前后端
- 6、分布式文件 Minio、阿里OSS √
- 7、统一权限控制 JWT + Shiro √
- 8、服务监控 SpringBootAdmin√
- 9、链路跟踪 Skywalking [参考文档](https://help.jeecg.com/java/springcloud/super/skywarking.html)
- 9、链路跟踪 Skywalking [参考文档](https://help.jeecg.com/java/springcloud/super/skywarking)
- 10、消息中间件 RabbitMQ √
- 11、分布式任务 xxl-job √
- 12、分布式事务 Seata

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -5,115 +5,356 @@
CREATE database if NOT EXISTS `xxl_job` default character set utf8mb4 collate utf8mb4_general_ci;
use `xxl_job`;
SET NAMES utf8mb4;
/*
Navicat Premium Data Transfer
CREATE TABLE `xxl_job_info` (
Source Server : mysql5.7
Source Server Type : MySQL
Source Server Version : 50738 (5.7.38)
Source Host : 127.0.0.1:3306
Source Schema : xxl_job
Target Server Type : MySQL
Target Server Version : 50738 (5.7.38)
File Encoding : 65001
Date: 10/02/2025 13:49:31
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for xxl_job_group
-- ----------------------------
DROP TABLE IF EXISTS `xxl_job_group`;
CREATE TABLE `xxl_job_group` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`app_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '执行器AppName',
`title` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '执行器名称',
`address_type` tinyint(4) NOT NULL DEFAULT 0 COMMENT '执行器地址类型0=自动注册1=手动录入',
`address_list` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '执行器地址列表多地址逗号分隔',
`update_time` datetime NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of xxl_job_group
-- ----------------------------
INSERT INTO `xxl_job_group` VALUES (1, 'xxl-job-executor-sample', '示例执行器', 0, NULL, '2025-02-10 13:49:04');
INSERT INTO `xxl_job_group` VALUES (2, 'jeecg-demo', '测试Demo模块', 0, NULL, '2025-02-10 13:49:04');
INSERT INTO `xxl_job_group` VALUES (3, 'jeecg-system', '系统System模块', 0, NULL, '2025-02-10 13:49:04');
-- ----------------------------
-- Table structure for xxl_job_info
-- ----------------------------
DROP TABLE IF EXISTS `xxl_job_info`;
CREATE TABLE `xxl_job_info` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`job_group` int(11) NOT NULL COMMENT '执行器主键ID',
`job_cron` varchar(128) NOT NULL COMMENT '任务执行CRON',
`job_desc` varchar(255) NOT NULL,
`add_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
`author` varchar(64) DEFAULT NULL COMMENT '作者',
`alarm_email` varchar(255) DEFAULT NULL COMMENT '报警邮件',
`executor_route_strategy` varchar(50) DEFAULT NULL COMMENT '执行器路由策略',
`executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler',
`executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数',
`executor_block_strategy` varchar(50) DEFAULT NULL COMMENT '阻塞处理策略',
`executor_timeout` int(11) NOT NULL DEFAULT '0' COMMENT '任务执行超时时间单位秒',
`executor_fail_retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '失败重试次数',
`glue_type` varchar(50) NOT NULL COMMENT 'GLUE类型',
`glue_source` mediumtext COMMENT 'GLUE源代码',
`glue_remark` varchar(128) DEFAULT NULL COMMENT 'GLUE备注',
`glue_updatetime` datetime DEFAULT NULL COMMENT 'GLUE更新时间',
`child_jobid` varchar(255) DEFAULT NULL COMMENT '子任务ID多个逗号分隔',
`trigger_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '调度状态0-停止1-运行',
`trigger_last_time` bigint(13) NOT NULL DEFAULT '0' COMMENT '上次调度时间',
`trigger_next_time` bigint(13) NOT NULL DEFAULT '0' COMMENT '下次调度时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
`job_desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`add_time` datetime NULL DEFAULT NULL,
`update_time` datetime NULL DEFAULT NULL,
`author` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '作者',
`alarm_email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '报警邮件',
`schedule_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'NONE' COMMENT '调度类型',
`schedule_conf` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '调度配置值含义取决于调度类型',
`misfire_strategy` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'DO_NOTHING' COMMENT '调度过期策略',
`executor_route_strategy` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '执行器路由策略',
`executor_handler` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '执行器任务handler',
`executor_param` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '执行器任务参数',
`executor_block_strategy` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '阻塞处理策略',
`executor_timeout` int(11) NOT NULL DEFAULT 0 COMMENT '任务执行超时时间单位秒',
`executor_fail_retry_count` int(11) NOT NULL DEFAULT 0 COMMENT '失败重试次数',
`glue_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'GLUE类型',
`glue_source` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT 'GLUE源代码',
`glue_remark` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'GLUE备注',
`glue_updatetime` datetime NULL DEFAULT NULL COMMENT 'GLUE更新时间',
`child_jobid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '子任务ID多个逗号分隔',
`trigger_status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '调度状态0-停止1-运行',
`trigger_last_time` bigint(13) NOT NULL DEFAULT 0 COMMENT '上次调度时间',
`trigger_next_time` bigint(13) NOT NULL DEFAULT 0 COMMENT '下次调度时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
CREATE TABLE `xxl_job_log` (
-- ----------------------------
-- Records of xxl_job_info
-- ----------------------------
INSERT INTO `xxl_job_info` VALUES (1, 1, '测试任务1', '2018-11-03 22:21:31', '2024-08-21 22:30:30', 'XXL', '', 'CRON', '0 0 0 * * ? *', 'DO_NOTHING', 'FIRST', 'demoJob', '', 'SERIAL_EXECUTION', 0, 0, 'BEAN', '', 'GLUE代码初始化', '2018-11-03 22:21:31', '', 1, 1729353600000, 1739203200000);
INSERT INTO `xxl_job_info` VALUES (2, 3, '测试jeecg xxljob', '2024-08-21 22:41:10', '2024-08-21 22:41:30', 'JEECG', '', 'CRON', '* * * * * ?', 'DO_NOTHING', 'FIRST', 'demoJob', '', 'SERIAL_EXECUTION', 0, 0, 'BEAN', '', 'GLUE代码初始化', '2024-08-21 22:41:10', '', 1, 1739166572000, 1739166573000);
-- ----------------------------
-- Table structure for xxl_job_lock
-- ----------------------------
DROP TABLE IF EXISTS `xxl_job_lock`;
CREATE TABLE `xxl_job_lock` (
`lock_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '锁名称',
PRIMARY KEY (`lock_name`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of xxl_job_lock
-- ----------------------------
INSERT INTO `xxl_job_lock` VALUES ('schedule_lock');
-- ----------------------------
-- Table structure for xxl_job_log
-- ----------------------------
DROP TABLE IF EXISTS `xxl_job_log`;
CREATE TABLE `xxl_job_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`job_group` int(11) NOT NULL COMMENT '执行器主键ID',
`job_id` int(11) NOT NULL COMMENT '任务主键ID',
`executor_address` varchar(255) DEFAULT NULL COMMENT '执行器地址本次执行的地址',
`executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器任务handler',
`executor_param` varchar(512) DEFAULT NULL COMMENT '执行器任务参数',
`executor_sharding_param` varchar(20) DEFAULT NULL COMMENT '执行器任务分片参数格式如 1/2',
`executor_fail_retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '失败重试次数',
`trigger_time` datetime DEFAULT NULL COMMENT '调度-时间',
`executor_address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '执行器地址本次执行的地址',
`executor_handler` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '执行器任务handler',
`executor_param` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '执行器任务参数',
`executor_sharding_param` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '执行器任务分片参数格式如 1/2',
`executor_fail_retry_count` int(11) NOT NULL DEFAULT 0 COMMENT '失败重试次数',
`trigger_time` datetime NULL DEFAULT NULL COMMENT '调度-时间',
`trigger_code` int(11) NOT NULL COMMENT '调度-结果',
`trigger_msg` text COMMENT '调度-日志',
`handle_time` datetime DEFAULT NULL COMMENT '执行-时间',
`trigger_msg` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '调度-日志',
`handle_time` datetime NULL DEFAULT NULL COMMENT '执行-时间',
`handle_code` int(11) NOT NULL COMMENT '执行-状态',
`handle_msg` text COMMENT '执行-日志',
`alarm_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '告警状态0-默认1-无需告警2-告警成功3-告警失败',
PRIMARY KEY (`id`),
KEY `I_trigger_time` (`trigger_time`),
KEY `I_handle_code` (`handle_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
`handle_msg` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '执行-日志',
`alarm_status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '告警状态0-默认1-无需告警2-告警成功3-告警失败',
PRIMARY KEY (`id`) USING BTREE,
INDEX `I_trigger_time`(`trigger_time`) USING BTREE,
INDEX `I_handle_code`(`handle_code`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 6761 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
CREATE TABLE `xxl_job_log_report` (
-- ----------------------------
-- Records of xxl_job_log
-- ----------------------------
INSERT INTO `xxl_job_log` VALUES (6618, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:09', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式自动注册<br>执行器-地址列表null<br>路由策略第一个<br>阻塞处理策略单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6619, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:10', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6620, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:11', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6621, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:12', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6622, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:13', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6623, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:14', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6624, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:15', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6625, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:16', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6626, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:17', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6627, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:18', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6628, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:19', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6629, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:20', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6630, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:21', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6631, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:22', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6632, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:23', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6633, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:24', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6634, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:25', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6635, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:26', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6636, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:27', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6637, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:28', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6638, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:29', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6639, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:30', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6640, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:31', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6641, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:32', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6642, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:33', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6643, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:34', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6644, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:35', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6645, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:36', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6646, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:37', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6647, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:38', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6648, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:39', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6649, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:40', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6650, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:41', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6651, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:42', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6652, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:43', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6653, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:44', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6654, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:45', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6655, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:46', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6656, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:47', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6657, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:48', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6658, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:49', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6659, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:50', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6660, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:51', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6661, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:52', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6662, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:53', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6663, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:54', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6664, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:55', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6665, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:56', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6666, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:57', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6667, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:58', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6668, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:47:59', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6669, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:00', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6670, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:01', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6671, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:02', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6672, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:03', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6673, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:04', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6674, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:05', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6675, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:06', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6676, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:07', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6677, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:08', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6678, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:09', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6679, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:10', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6680, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:11', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6681, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:12', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6682, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:13', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6683, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:14', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6684, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:15', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6685, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:16', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6686, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:17', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6687, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:18', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6688, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:19', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6689, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:20', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6690, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:21', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6691, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:22', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6692, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:23', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6693, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:24', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6694, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:25', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6695, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:26', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6696, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:27', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6697, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:28', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6698, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:29', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6699, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:30', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6700, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:31', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6701, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:32', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6702, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:33', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6703, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:34', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6704, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:35', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6705, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:36', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6706, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:37', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6707, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:38', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6708, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:39', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6709, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:40', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6710, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:41', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6711, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:42', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6712, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:43', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6713, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:44', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6714, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:45', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6715, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:46', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6716, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:47', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6717, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:48', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6718, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:49', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6719, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:50', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6720, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:51', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6721, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:52', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6722, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:53', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6723, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:54', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6724, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:55', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6725, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:56', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6726, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:57', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6727, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:58', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6728, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:48:59', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6729, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:00', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6730, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:01', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6731, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:02', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6732, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:03', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6733, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:04', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6734, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:05', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6735, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:06', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6736, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:07', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6737, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:08', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6738, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:09', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6739, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:10', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6740, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:11', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6741, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:12', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6742, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:13', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6743, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:14', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6744, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:15', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6745, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:16', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6746, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:17', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6747, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:18', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6748, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:19', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6749, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:20', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6750, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:21', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6751, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:22', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6752, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:23', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6753, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:24', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 2);
INSERT INTO `xxl_job_log` VALUES (6754, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:25', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 0);
INSERT INTO `xxl_job_log` VALUES (6755, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:26', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 0);
INSERT INTO `xxl_job_log` VALUES (6756, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:27', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 0);
INSERT INTO `xxl_job_log` VALUES (6757, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:28', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 0);
INSERT INTO `xxl_job_log` VALUES (6758, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:29', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 0);
INSERT INTO `xxl_job_log` VALUES (6759, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:30', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 0);
INSERT INTO `xxl_job_log` VALUES (6760, 3, 2, NULL, 'demoJob', '', NULL, 0, '2025-02-10 13:49:31', 500, '任务触发类型Cron触发<br>调度机器192.168.1.11<br>执行器-注册方式:自动注册<br>执行器-地址列表null<br>路由策略:第一个<br>阻塞处理策略:单机串行<br>任务超时时间0<br>失败重试次数0<br><br><span style=\"color:#00c0ef;\" > >>>>>>>>>>>触发调度<<<<<<<<<<< </span><br>调度失败:执行器地址为空<br><br>', NULL, 0, NULL, 0);
-- ----------------------------
-- Table structure for xxl_job_log_report
-- ----------------------------
DROP TABLE IF EXISTS `xxl_job_log_report`;
CREATE TABLE `xxl_job_log_report` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`trigger_day` datetime DEFAULT NULL COMMENT '调度-时间',
`running_count` int(11) NOT NULL DEFAULT '0' COMMENT '运行中-日志数量',
`suc_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行成功-日志数量',
`fail_count` int(11) NOT NULL DEFAULT '0' COMMENT '执行失败-日志数量',
PRIMARY KEY (`id`),
UNIQUE KEY `i_trigger_day` (`trigger_day`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
`trigger_day` datetime NULL DEFAULT NULL COMMENT '调度-时间',
`running_count` int(11) NOT NULL DEFAULT 0 COMMENT '运行中-日志数量',
`suc_count` int(11) NOT NULL DEFAULT 0 COMMENT '执行成功-日志数量',
`fail_count` int(11) NOT NULL DEFAULT 0 COMMENT '执行失败-日志数量',
`update_time` datetime NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `i_trigger_day`(`trigger_day`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
CREATE TABLE `xxl_job_logglue` (
-- ----------------------------
-- Records of xxl_job_log_report
-- ----------------------------
INSERT INTO `xxl_job_log_report` VALUES (1, '2024-08-21 00:00:00', 70, 0, 5, NULL);
INSERT INTO `xxl_job_log_report` VALUES (2, '2024-08-20 00:00:00', 0, 0, 0, NULL);
INSERT INTO `xxl_job_log_report` VALUES (3, '2024-08-19 00:00:00', 0, 0, 0, NULL);
INSERT INTO `xxl_job_log_report` VALUES (4, '2024-09-10 00:00:00', 0, 0, 56, NULL);
INSERT INTO `xxl_job_log_report` VALUES (5, '2024-09-09 00:00:00', 0, 0, 0, NULL);
INSERT INTO `xxl_job_log_report` VALUES (6, '2024-09-08 00:00:00', 0, 0, 0, NULL);
INSERT INTO `xxl_job_log_report` VALUES (7, '2024-10-19 00:00:00', 0, 0, 6391, NULL);
INSERT INTO `xxl_job_log_report` VALUES (8, '2024-10-18 00:00:00', 0, 0, 0, NULL);
INSERT INTO `xxl_job_log_report` VALUES (9, '2024-10-17 00:00:00', 0, 0, 0, NULL);
INSERT INTO `xxl_job_log_report` VALUES (10, '2025-02-10 00:00:00', 0, 0, 116, NULL);
INSERT INTO `xxl_job_log_report` VALUES (11, '2025-02-09 00:00:00', 0, 0, 0, NULL);
INSERT INTO `xxl_job_log_report` VALUES (12, '2025-02-08 00:00:00', 0, 0, 0, NULL);
-- ----------------------------
-- Table structure for xxl_job_logglue
-- ----------------------------
DROP TABLE IF EXISTS `xxl_job_logglue`;
CREATE TABLE `xxl_job_logglue` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`job_id` int(11) NOT NULL COMMENT '任务主键ID',
`glue_type` varchar(50) DEFAULT NULL COMMENT 'GLUE类型',
`glue_source` mediumtext COMMENT 'GLUE源代码',
`glue_remark` varchar(128) NOT NULL COMMENT 'GLUE备注',
`add_time` datetime DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
`glue_type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT 'GLUE类型',
`glue_source` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT 'GLUE源代码',
`glue_remark` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT 'GLUE备注',
`add_time` datetime NULL DEFAULT NULL,
`update_time` datetime NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
CREATE TABLE `xxl_job_registry` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`registry_group` varchar(50) NOT NULL,
`registry_key` varchar(255) NOT NULL,
`registry_value` varchar(255) NOT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `i_g_k_v` (`registry_group`,`registry_key`,`registry_value`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Records of xxl_job_logglue
-- ----------------------------
CREATE TABLE `xxl_job_group` (
-- ----------------------------
-- Table structure for xxl_job_registry
-- ----------------------------
DROP TABLE IF EXISTS `xxl_job_registry`;
CREATE TABLE `xxl_job_registry` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`app_name` varchar(64) NOT NULL COMMENT '执行器AppName',
`title` varchar(12) NOT NULL COMMENT '执行器名称',
`address_type` tinyint(4) NOT NULL DEFAULT '0' COMMENT '执行器地址类型0=自动注册1=手动录入',
`address_list` varchar(512) DEFAULT NULL COMMENT '执行器地址列表多地址逗号分隔',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
`registry_group` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`registry_key` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`registry_value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`update_time` datetime NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `i_g_k_v`(`registry_group`, `registry_key`, `registry_value`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
CREATE TABLE `xxl_job_user` (
-- ----------------------------
-- Records of xxl_job_registry
-- ----------------------------
-- ----------------------------
-- Table structure for xxl_job_user
-- ----------------------------
DROP TABLE IF EXISTS `xxl_job_user`;
CREATE TABLE `xxl_job_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '账号',
`password` varchar(50) NOT NULL COMMENT '密码',
`username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '账号',
`password` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '密码',
`role` tinyint(4) NOT NULL COMMENT '角色0-普通用户、1-管理员',
`permission` varchar(255) DEFAULT NULL COMMENT '权限执行器ID列表多个逗号分割',
PRIMARY KEY (`id`),
UNIQUE KEY `i_username` (`username`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
`permission` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '权限:执行器ID列表多个逗号分割',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `i_username`(`username`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
CREATE TABLE `xxl_job_lock` (
`lock_name` varchar(50) NOT NULL COMMENT '锁名称',
PRIMARY KEY (`lock_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- ----------------------------
-- Records of xxl_job_user
-- ----------------------------
INSERT INTO `xxl_job_user` VALUES (1, 'admin', 'e10adc3949ba59abbe56e057f20f883e', 1, NULL);
INSERT INTO `xxl_job_group`(`id`, `app_name`, `title`, `address_type`, `address_list`) VALUES (1, 'xxl-job-executor-sample', '示例执行器', 0, NULL);
INSERT INTO `xxl_job_info`(`id`, `job_group`, `job_cron`, `job_desc`, `add_time`, `update_time`, `author`, `alarm_email`, `executor_route_strategy`, `executor_handler`, `executor_param`, `executor_block_strategy`, `executor_timeout`, `executor_fail_retry_count`, `glue_type`, `glue_source`, `glue_remark`, `glue_updatetime`, `child_jobid`) VALUES (1, 1, '0 0 0 * * ? *', '测试任务1', '2018-11-03 22:21:31', '2018-11-03 22:21:31', 'XXL', '', 'FIRST', 'demoJobHandler', '', 'SERIAL_EXECUTION', 0, 0, 'BEAN', '', 'GLUE代码初始化', '2018-11-03 22:21:31', '');
INSERT INTO `xxl_job_user`(`id`, `username`, `password`, `role`, `permission`) VALUES (1, 'admin', 'e10adc3949ba59abbe56e057f20f883e', 1, NULL);
INSERT INTO `xxl_job_lock` ( `lock_name`) VALUES ( 'schedule_lock');
commit;
SET FOREIGN_KEY_CHECKS = 1;

View File

@ -3,7 +3,6 @@
> JeecgBoot属于平台级产品每次升级改动较大目前做不到平滑升级。
### 增量升级方案
#### 1.代码合并
本地通过svn或git做好主干在分支上做业务开发jeecg每次版本发布可以手工覆盖主干的代码对比合并代码
@ -12,12 +11,5 @@
- 其他库请手工执行SQL, 目录: `jeecg-module-system\jeecg-system-start\src\main\resources\flyway\sql\mysql`
> 注意: 升级sql只提供mysql版本如果有权限升级, 还需要手工角色授权,退出重新登录才好使。
#### 3.其他数据库脚本说明
原先官方默认提供oracle和SqlServer的脚本但是维护成本太高未提供脚本的数据库可以参考下面的文档自己转
https://my.oschina.net/jeecg/blog/4905722
注意定时任务的表qrtz_*,需要删掉用原始的脚本重新执行一下)
quartz-2.2.3-distribution.tar.gz放到百度网盘中大家自己下载执行所需数据库脚本
https://pan.baidu.com/s/1WrmZdUuAPg3iBwJ-LoHWyg?pwd=8mdz
#### 4.兼容问题
#### 3.兼容问题
每次发版,会针对不兼容地方重点说明。

View File

@ -4,7 +4,7 @@
<parent>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-parent</artifactId>
<version>3.7.1</version>
<version>3.7.4</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jeecg-boot-base-core</artifactId>
@ -49,6 +49,16 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<!-- websocket -->
<dependency>
@ -163,7 +173,7 @@
<artifactId>DmDialect-for-hibernate5.0</artifactId>
<version>${dm8.version}</version>
</dependency>
<!-- Quartz定时任务 -->
<dependency>
<groupId>org.springframework.boot</groupId>
@ -201,14 +211,9 @@
</dependency>
<!-- knife4j -->
<!-- <dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>-->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
<artifactId>knife4j-openapi3-spring-boot-starter</artifactId>
<version>${knife4j-spring-boot-starter.version}</version>
</dependency>
@ -218,6 +223,16 @@
<groupId>org.jeecgframework.boot</groupId>
<artifactId>codegenerate</artifactId>
<version>${codegenerate.version}</version>
<exclusions>
<exclusion>
<artifactId>commons-io</artifactId>
<groupId>commons-io</groupId>
</exclusion>
<exclusion>
<artifactId>mysql-connector-java</artifactId>
<groupId>mysql</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- AutoPoi Excel工具类-->
@ -247,6 +262,12 @@
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<exclusions>
<exclusion>
<artifactId>checker-qual</artifactId>
<groupId>org.checkerframework</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- 阿里云短信 -->
@ -288,7 +309,6 @@
<groupId>cn.hutool</groupId>
<artifactId>hutool-crypto</artifactId>
</dependency>
<!-- chatgpt -->
<dependency>
<groupId>org.jeecgframework.boot</groupId>

View File

@ -84,6 +84,12 @@ public class MessageDTO implements Serializable {
* 邮件抄送地址
*/
protected Set<String> ccEmailList;
/**
* 是否为定时任务推送email
*/
private Boolean isTimeJob = false;
//---【邮件相关参数】-------------------------------------------------------------
public MessageDTO(){

View File

@ -1,8 +1,7 @@
package org.jeecg.common.api.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.jeecg.common.constant.CommonConstant;
@ -15,7 +14,7 @@ import java.io.Serializable;
* @date 2019年1月19日
*/
@Data
@ApiModel(value="接口返回对象", description="接口返回对象")
@Schema(description="接口返回对象")
public class Result<T> implements Serializable {
private static final long serialVersionUID = 1L;
@ -23,31 +22,31 @@ public class Result<T> implements Serializable {
/**
* 成功标志
*/
@ApiModelProperty(value = "成功标志")
@Schema(description = "成功标志")
private boolean success = true;
/**
* 返回处理消息
*/
@ApiModelProperty(value = "返回处理消息")
@Schema(description = "返回处理消息")
private String message = "";
/**
* 返回代码
*/
@ApiModelProperty(value = "返回代码")
@Schema(description = "返回代码")
private Integer code = 0;
/**
* 返回数据对象 data
*/
@ApiModelProperty(value = "返回数据对象")
@Schema(description = "返回数据对象")
private T result;
/**
* 时间戳
*/
@ApiModelProperty(value = "时间戳")
@Schema(description = "时间戳")
private long timestamp = System.currentTimeMillis();
public Result() {

View File

@ -604,6 +604,11 @@ public interface CommonConstant {
*/
String CHANGE_PHONE_REDIS_KEY_PRE = "sys:cache:phone:change_phone_msg:";
/**
* 手机号短信验证码redis-key的前缀
*/
String LOG_OFF_PHONE_REDIS_KEY_PRE = "sys:cache:phone:qqy_log_off_user_msg:";
/**
* 缓存用户最后一次收到消息通知的时间 KEY
*/

View File

@ -15,15 +15,7 @@ public enum DySmsEnum {
/**修改密码短信模板编码*/
CHANGE_PASSWORD_TEMPLATE_CODE("SMS_465391221","敲敲云","code"),
/**注册账号短信模板编码*/
REGISTER_TEMPLATE_CODE("SMS_175430166","敲敲云","code"),
/**会议通知*/
MEET_NOTICE_TEMPLATE_CODE("SMS_201480469","JEECG","username,title,minute,time"),
/**我的计划通知*/
PLAN_NOTICE_TEMPLATE_CODE("SMS_201470515","JEECG","username,title,time"),
/**支付成功短信通知*/
PAY_SUCCESS_NOTICE_CODE("SMS_461735163","敲敲云","realname,money,endTime"),
/**会员到期通知提醒*/
VIP_EXPIRE_NOTICE_CODE("SMS_461885023","敲敲云","realname,endTime");
REGISTER_TEMPLATE_CODE("SMS_175430166","敲敲云","code");
/**
* 短信模板编码

View File

@ -0,0 +1,21 @@
package org.jeecg.common.exception;
/**
* jeecgboot断言异常
* for [QQYUN-10990]AIRAG
* @author chenrui
* @date 2025/2/14 14:31
*/
public class JeecgBootAssertException extends JeecgBootException {
private static final long serialVersionUID = 1L;
public JeecgBootAssertException(String message) {
super(message);
}
public JeecgBootAssertException(String message, int errCode) {
super(message, errCode);
}
}

View File

@ -23,7 +23,9 @@ import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.connection.PoolException;
import org.springframework.http.HttpStatus;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@ -33,6 +35,7 @@ import org.springframework.web.servlet.NoHandlerFoundException;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 异常处理器
@ -47,6 +50,13 @@ public class JeecgBootExceptionHandler {
@Resource
BaseCommonService baseCommonService;
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<?> handleValidationExceptions(MethodArgumentNotValidException e) {
log.error(e.getMessage(), e);
addSysLog(e);
return Result.error("校验失败!" + e.getBindingResult().getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.joining(",")));
}
/**
* 处理自定义异常
*/

View File

@ -2,6 +2,7 @@ package org.jeecg.common.system.base.entity;
import java.io.Serializable;
import io.swagger.v3.oas.annotations.media.Schema;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.springframework.format.annotation.DateTimeFormat;
@ -9,7 +10,6 @@ import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
@ -30,20 +30,20 @@ public class JeecgEntity implements Serializable {
* ID
*/
@TableId(type = IdType.ASSIGN_ID)
@ApiModelProperty(value = "ID")
@Schema(description = "ID")
private java.lang.String id;
/**
* 创建人
*/
@ApiModelProperty(value = "创建人")
@Schema(description = "创建人")
@Excel(name = "创建人", width = 15)
private java.lang.String createBy;
/**
* 创建时间
*/
@ApiModelProperty(value = "创建时间")
@Schema(description = "创建时间")
@Excel(name = "创建时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ -52,14 +52,14 @@ public class JeecgEntity implements Serializable {
/**
* 更新人
*/
@ApiModelProperty(value = "更新人")
@Schema(description = "更新人")
@Excel(name = "更新人", width = 15)
private java.lang.String updateBy;
/**
* 更新时间
*/
@ApiModelProperty(value = "更新时间")
@Schema(description = "更新时间")
@Excel(name = "更新时间", width = 20, format = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")

View File

@ -805,7 +805,9 @@ public class QueryGenerator {
addEasyQuery(queryWrapper, name, rule, DateUtils.str2Date(dateStr,DateUtils.datetimeFormat.get()));
}
}else {
addEasyQuery(queryWrapper, name, rule, NumberUtils.parseNumber(dataRule.getRuleValue(), propertyType));
//update-begin---author:chenrui ---date:20241125 for[issues/7481]多租户模式下 数据权限使用变量:#{tenant_id} 报错------------
addEasyQuery(queryWrapper, name, rule, NumberUtils.parseNumber(converRuleValue(dataRule.getRuleValue()), propertyType));
//update-end---author:chenrui ---date:20241125 for[issues/7481]多租户模式下 数据权限使用变量:#{tenant_id} 报错------------
}
}
}
@ -859,7 +861,9 @@ public class QueryGenerator {
return null;
}
Set<String> varParams = new HashSet<String>();
String regex = "\\#\\{\\w+\\}";
//update-begin---author:chenrui ---date:20250108 for[QQYUN-10785]数据权限,查看自己拥有部门的权限中存在问题 #7288------------
String regex = "#\\{\\[*\\w+]*}";
//update-end---author:chenrui ---date:20250108 for[QQYUN-10785]数据权限,查看自己拥有部门的权限中存在问题 #7288------------
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(sql);

View File

@ -54,8 +54,10 @@ public enum QueryRuleEnum {
NOT_EMPTY("NOT_EMPTY","not_empty","值不为空"),
/**查询规则 不包含*/
NOT_IN("NOT_IN","not_in","不包含"),
/**查询规则 多词匹配*/
/**查询规则 多词精确匹配*/
ELE_MATCH("ELE_MATCH","elemMatch","多词匹配"),
/**查询规则 多词精确不匹配*/
ELE_NOT_MATCH("ELE_NOT_MATCH","elemNotMatch","多词精确不匹配"),
/**查询规则 范围查询*/
RANGE("RANGE","range","范围查询"),
/**查询规则 不在范围内查询*/

View File

@ -6,11 +6,12 @@ import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Joiner;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -63,7 +64,7 @@ public class JwtUtil {
os.flush();
os.close();
} catch (IOException e) {
e.printStackTrace();
log.error(e.getMessage(), e);
}
}
@ -82,7 +83,8 @@ public class JwtUtil {
// 效验TOKEN
DecodedJWT jwt = verifier.verify(token);
return true;
} catch (Exception exception) {
} catch (Exception e) {
log.error(e.getMessage(), e);
return false;
}
}
@ -97,6 +99,7 @@ public class JwtUtil {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("username").asString();
} catch (JWTDecodeException e) {
log.warn(e.getMessage(), e);
return null;
}
}
@ -195,6 +198,16 @@ public class JwtUtil {
} else {
key = key;
}
//update-begin---author:chenrui ---date:20250107 for[QQYUN-10785]数据权限,查看自己拥有部门的权限中存在问题 #7288------------
// 是否存在字符串标志
boolean multiStr;
if(oConvertUtils.isNotEmpty(key) && key.trim().matches("^\\[\\w+]$")){
key = key.substring(1,key.length()-1);
multiStr = true;
} else {
multiStr = false;
}
//update-end---author:chenrui ---date:20250107 for[QQYUN-10785]数据权限,查看自己拥有部门的权限中存在问题 #7288------------
//替换为当前系统时间(年月日)
if (key.equals(DataBaseConstant.SYS_DATE)|| key.toLowerCase().equals(DataBaseConstant.SYS_DATE_TABLE)) {
returnValue = DateUtils.formatDate();
@ -263,11 +276,30 @@ public class JwtUtil {
if(user==null){
//TODO 暂时使用用户登录部门,存在逻辑缺陷,不是用户所拥有的部门
returnValue = sysUser.getOrgCode();
//update-begin---author:chenrui ---date:20250107 for[QQYUN-10785]数据权限,查看自己拥有部门的权限中存在问题 #7288------------
returnValue = multiStr ? "'" + returnValue + "'" : returnValue;
//update-end---author:chenrui ---date:20250107 for[QQYUN-10785]数据权限,查看自己拥有部门的权限中存在问题 #7288------------
}else{
if(user.isOneDepart()) {
returnValue = user.getSysMultiOrgCode().get(0);
//update-begin---author:chenrui ---date:20250107 for[QQYUN-10785]数据权限,查看自己拥有部门的权限中存在问题 #7288------------
returnValue = multiStr ? "'" + returnValue + "'" : returnValue;
//update-end---author:chenrui ---date:20250107 for[QQYUN-10785]数据权限,查看自己拥有部门的权限中存在问题 #7288------------
}else {
returnValue = Joiner.on(",").join(user.getSysMultiOrgCode());
//update-begin---author:chenrui ---date:20250107 for[QQYUN-10785]数据权限,查看自己拥有部门的权限中存在问题 #7288------------
returnValue = user.getSysMultiOrgCode().stream()
.filter(Objects::nonNull)
//update-begin---author:chenrui ---date:20250224 for[issues/7288]数据权限,查看自己拥有部门的权限中存在问题 #7288------------
.map(orgCode -> {
if (multiStr) {
return "'" + orgCode + "'";
} else {
return orgCode;
}
})
//update-end---author:chenrui ---date:20250224 for[issues/7288]数据权限,查看自己拥有部门的权限中存在问题 #7288------------
.collect(Collectors.joining(", "));
//update-end---author:chenrui ---date:20250107 for[QQYUN-10785]数据权限,查看自己拥有部门的权限中存在问题 #7288------------
}
}
}

View File

@ -0,0 +1,239 @@
package org.jeecg.common.util;
import org.jeecg.common.exception.JeecgBootAssertException;
/**
* 断言检查工具
* for for [QQYUN-10990]AIRAG
* @author chenrui
* @date 2017-06-22 10:05:56
*/
public class AssertUtils {
/**
* 确保对象为空,如果不为空抛出异常
*
* @param msg
* @param obj
* @throws JeecgBootAssertException
* @author chenrui
* @date 2017-06-22 10:05:56
*/
public static void assertEmpty(String msg, Object obj) {
if (oConvertUtils.isObjectNotEmpty(obj)) {
throw new JeecgBootAssertException(msg);
}
}
/**
* 确保对象不为空,如果为空抛出异常
*
* @param msg
* @param obj
* @throws JeecgBootAssertException
* @author chenrui
* @date 2017-06-22 10:05:56
*/
public static void assertNotEmpty(String msg, Object obj) {
if (oConvertUtils.isObjectEmpty(obj)) {
throw new JeecgBootAssertException(msg);
}
}
/**
* 验证对象是否相同
*
* @param message
* @param expected
* @param actual
* @author chenrui
* @date 2018/9/12 15:45
*/
public static void assertEquals(String message, Object expected,
Object actual) {
if (oConvertUtils.isEqual(expected, actual)) {
return;
}
throw new JeecgBootAssertException(message);
}
/**
* 验证不相同
*
* @param message
* @param expected
* @param actual
* @author chenrui
* @date 2018/9/12 15:45
*/
public static void assertNotEquals(String message, Object expected,
Object actual) {
if (oConvertUtils.isEqual(expected, actual)) {
throw new JeecgBootAssertException(message);
}
}
/**
* 验证是否相等
*
* @param message
* @param expected
* @param actual
* @author chenrui
* @date 2018/9/12 15:45
*/
public static void assertSame(String message, Object expected,
Object actual) {
if (expected == actual) {
return;
}
throw new JeecgBootAssertException(message);
}
/**
* 验证不相等
*
* @param message
* @param unexpected
* @param actual
* @author chenrui
* @date 2018/9/12 15:45
*/
public static void assertNotSame(String message, Object unexpected,
Object actual) {
if (unexpected == actual) {
throw new JeecgBootAssertException(message);
}
}
/**
* 验证是否为真
*
* @param message
* @param condition
*/
public static void assertTrue(String message, boolean condition) {
if (!condition) {
throw new JeecgBootAssertException(message);
}
}
/**
* 验证 condition是否为false
*
* @param message
* @param condition
*/
public static void assertFalse(String message, boolean condition) {
assertTrue(message, !condition);
}
/**
* 验证是否存在
*
* @param message
* @param obj
* @param objs
* @param <T>
* @throws JeecgBootAssertException
* @author chenrui
* @date 2018/1/31 22:14
*/
public static <T> void assertIn(String message, T obj, T... objs) {
assertNotEmpty(message, obj);
assertNotEmpty(message, objs);
if (!oConvertUtils.isIn(obj, objs)) {
throw new JeecgBootAssertException(message);
}
}
/**
* 验证是否不存在
*
* @param message
* @param obj
* @param objs
* @param <T>
* @throws JeecgBootAssertException
* @author chenrui
* @date 2018/1/31 22:14
*/
public static <T> void assertNotIn(String message, T obj, T... objs) {
assertNotEmpty(message, obj);
assertNotEmpty(message, objs);
if (oConvertUtils.isIn(obj, objs)) {
throw new JeecgBootAssertException(message);
}
}
/**
* 确保src大于des
*
* @param message
* @param src
* @param des
* @author chenrui
* @date 2018/9/19 15:30
*/
public static void assertGt(String message, Number src, Number des) {
if (oConvertUtils.isGt(src, des)) {
return;
}
throw new JeecgBootAssertException(message);
}
/**
* 确保src大于等于des
*
* @param message
* @param src
* @param des
* @author chenrui
* @date 2018/9/19 15:30
*/
public static void assertGe(String message, Number src, Number des) {
if (oConvertUtils.isGe(src, des)) {
return;
}
throw new JeecgBootAssertException(message);
}
/**
* 确保src小于des
*
* @param message
* @param src
* @param des
* @author chenrui
* @date 2018/9/19 15:30
*/
public static void assertLt(String message, Number src, Number des) {
if (oConvertUtils.isGe(src, des)) {
throw new JeecgBootAssertException(message);
}
}
/**
* 确保src小于等于des
*
* @param message
* @param src
* @param des
* @author chenrui
* @date 2018/9/19 15:30
*/
public static void assertLe(String message, Number src, Number des) {
if (oConvertUtils.isGt(src, des)) {
throw new JeecgBootAssertException(message);
}
}
}

View File

@ -1,5 +1,6 @@
package org.jeecg.common.util;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
@ -8,11 +9,15 @@ import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.constant.enums.DySmsEnum;
import org.jeecg.config.JeecgSmsTemplateConfig;
import org.jeecg.config.StaticConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
/**
* Created on 17/6/7.
* 短信API产品的DEMO程序,工程中包含了一个SmsDemo类直接通过
@ -75,15 +80,33 @@ public class DySmsHelper {
//验证json参数
validateParam(templateParamJson,dySmsEnum);
//update-begin---author:wangshuai---date:2024-11-05---for:【QQYUN-9422】短信模板管理阿里云---
String templateCode = dySmsEnum.getTemplateCode();
JeecgSmsTemplateConfig baseConfig = SpringContextUtils.getBean(JeecgSmsTemplateConfig.class);
if(baseConfig != null && CollectionUtil.isNotEmpty(baseConfig.getTemplateCode())){
Map<String, String> smsTemplate = baseConfig.getTemplateCode();
if(smsTemplate.containsKey(templateCode) && StringUtils.isNotEmpty(smsTemplate.get(templateCode))){
templateCode = smsTemplate.get(templateCode);
logger.info("yml中读取短信code{}",templateCode);
}
}
//签名名称
String signName = dySmsEnum.getSignName();
if(baseConfig != null && StringUtils.isNotEmpty(baseConfig.getSignature())){
logger.info("yml中读取签名名称{}",baseConfig.getSignature());
signName = baseConfig.getSignature();
}
//update-end---author:wangshuai---date:2024-11-05---for:【QQYUN-9422】短信模板管理阿里云---
//组装请求对象-具体描述见控制台-文档部分内容
SendSmsRequest request = new SendSmsRequest();
//必填:待发送手机号
request.setPhoneNumbers(phone);
//必填:短信签名-可在短信控制台中找到
request.setSignName(dySmsEnum.getSignName());
request.setSignName(signName);
//必填:短信模板-可在短信控制台中找到
request.setTemplateCode(dySmsEnum.getTemplateCode());
request.setTemplateCode(templateCode);
//可选:模板中的变量替换JSON串,如模板内容为"亲爱的${name},您的验证码为${code}"时,此处的值为
request.setTemplateParam(templateParamJson.toJSONString());

View File

@ -6,7 +6,11 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.handler.IFillRuleHandler;
import org.jeecg.common.system.query.QueryGenerator;
import javax.servlet.http.HttpServletRequest;
/**
@ -42,6 +46,30 @@ public class FillRuleUtil {
if (params == null) {
params = new JSONObject();
}
HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
// 解析 params 中的变量
// 优先级queryString > 系统变量 > 默认值
for (String key : params.keySet()) {
// 1. 判断 queryString 中是否有该参数,如果有就优先取值
//noinspection ConstantValue
if (request != null) {
String parameter = request.getParameter(key);
if (oConvertUtils.isNotEmpty(parameter)) {
params.put(key, parameter);
continue;
}
}
String value = params.getString(key);
// 2. 用于替换 系统变量的值 #{sys_user_code}
if (value != null && value.contains(SymbolConstant.SYS_VAR_PREFIX)) {
value = QueryGenerator.getSqlRuleValue(value);
params.put(key, value);
}
}
if (formData == null) {
formData = new JSONObject();
}

View File

@ -57,8 +57,8 @@ public class RestUtil {
static {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(3000);
requestFactory.setReadTimeout(3000);
requestFactory.setConnectTimeout(30000);
requestFactory.setReadTimeout(30000);
RT = new RestTemplate(requestFactory);
// 解决乱码问题
RT.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));

View File

@ -84,6 +84,10 @@ public class DynamicDBUtil {
} else {
DruidDataSource dataSource = getJdbcDataSource(dbSource);
if(dataSource!=null && dataSource.isEnable()){
// 【TV360X-2060】设置超时时间 6秒
dataSource.setMaxWait(6000);
DataSourceCachePool.putCacheBasicDataSource(dbKey, dataSource);
}else{
throw new JeecgBootException("动态数据源连接失败dbKey"+dbKey);
@ -109,7 +113,7 @@ public class DynamicDBUtil {
DataSourceCachePool.removeCache(dbKey);
}
} catch (SQLException e) {
log.info(e.getMessage(), e);
log.warn(e.getMessage(), e);
}
}

View File

@ -42,6 +42,7 @@ public class SsrfFileTypeFilter {
FILE_TYPE_WHITE_LIST.add("pdf");
FILE_TYPE_WHITE_LIST.add("csv");
// FILE_TYPE_WHITE_LIST.add("xml");
FILE_TYPE_WHITE_LIST.add("md");
//音视频文件
FILE_TYPE_WHITE_LIST.add("mp4");
@ -65,6 +66,10 @@ public class SsrfFileTypeFilter {
FILE_TYPE_WHITE_LIST.add("apk");
FILE_TYPE_WHITE_LIST.add("wgt");
//幻灯片文件后缀
FILE_TYPE_WHITE_LIST.add("ppt");
FILE_TYPE_WHITE_LIST.add("pptx");
//设置禁止文件的头部标记
FILE_TYPE_MAP.put("3c25402070616765206c", "jsp");
FILE_TYPE_MAP.put("3c3f7068700a0a2f2a2a0a202a205048", "php");

View File

@ -13,6 +13,7 @@ import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
@ -463,7 +464,7 @@ public class oConvertUtils {
return false;
}
String[] childs = childArray.toArray(new String[]{});
List<String> childs = childArray.toJavaList(String.class);
for (String v : childs) {
if (!isIn(v, all)) {
return false;
@ -1028,5 +1029,109 @@ public class oConvertUtils {
}
return result;
}
/**
* 判断对象是否为空 <br/>
* 支持各种类型的对象
* for for [QQYUN-10990]AIRAG
* @param obj
* @return
* @author chenrui
* @date 2025/2/13 18:34
*/
public static boolean isObjectEmpty(Object obj) {
if (null == obj) {
return true;
}
if (obj instanceof CharSequence) {
return isEmpty(obj);
} else if (obj instanceof Map) {
return ((Map<?, ?>) obj).isEmpty();
} else if (obj instanceof Iterable) {
return isObjectEmpty(((Iterable<?>) obj).iterator());
} else if (obj instanceof Iterator) {
return !((Iterator<?>) obj).hasNext();
} else if (isArray(obj)) {
return 0 == Array.getLength(obj);
}
return false;
}
/**
* iterator 是否为空
* for for [QQYUN-10990]AIRAG
* @param iterator Iterator对象
* @return 是否为空
*/
public static boolean isEmptyIterator(Iterator<?> iterator) {
return null == iterator || false == iterator.hasNext();
}
/**
* 判断对象是否不为空
* for for [QQYUN-10990]AIRAG
* @param object
* @return
* @author chenrui
* @date 2025/2/13 18:35
*/
public static boolean isObjectNotEmpty(Object object) {
return !isObjectEmpty(object);
}
/**
* 如果src大于des返回true
* for [QQYUN-10990]AIRAG
* @param src
* @param des
* @return
* @author: chenrui
* @date: 2018/9/19 15:30
*/
public static boolean isGt(Number src, Number des) {
if (null == src || null == des) {
throw new IllegalArgumentException("参数不能为空");
}
if (src.doubleValue() > des.doubleValue()) {
return true;
}
return false;
}
/**
* 如果src大于等于des返回true
* for [QQYUN-10990]AIRAG
* @param src
* @param des
* @return
* @author: chenrui
* @date: 2018/9/19 15:30
*/
public static boolean isGe(Number src, Number des) {
if (null == src || null == des) {
throw new IllegalArgumentException("参数不能为空");
}
if (src.doubleValue() < des.doubleValue()) {
return false;
}
return true;
}
/**
* 判断是否存在
* for [QQYUN-10990]AIRAG
* @param obj
* @param objs
* @param <T>
* @return
* @author chenrui
* @date 2020/9/12 15:50
*/
public static <T> boolean isIn(T obj, T... objs) {
return isIn(obj, objs);
}
}

View File

@ -73,6 +73,12 @@ public class JSqlParserUtils {
* @return
*/
private static SelectSqlInfo parseBySelectBody(SelectBody selectBody) {
// 判断是否使用了union等操作
if (selectBody instanceof SetOperationList) {
// 如果使用了union等操作则只解析第一个查询
List<SelectBody> selectBodyList = ((SetOperationList) selectBody).getSelects();
return JSqlParserUtils.parseBySelectBody(selectBodyList.get(0));
}
// 简单的select查询
if (selectBody instanceof PlainSelect) {
SelectSqlInfo sqlInfo = new SelectSqlInfo(selectBody);

View File

@ -17,6 +17,10 @@ public class JeecgBaseConfig {
* @TODO 降低使用成本加的默认值,实际以 yml配置 为准
*/
private String signatureSecret = "dd05f1c54d63749eda95f9fa6d49v442a";
/**
* 自定义后台资源前缀解决表单设计器无法通过前端nginx转发访问
*/
private String customResourcePrefixPath;
/**
* 需要加强校验的接口清单
*/
@ -68,6 +72,14 @@ public class JeecgBaseConfig {
*/
private BaiduApi baiduApi;
public String getCustomResourcePrefixPath() {
return customResourcePrefixPath;
}
public void setCustomResourcePrefixPath(String customResourcePrefixPath) {
this.customResourcePrefixPath = customResourcePrefixPath;
}
public Elasticsearch getElasticsearch() {
return elasticsearch;
}

View File

@ -0,0 +1,33 @@
package org.jeecg.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* @Description: 短信模板
*
* @author: wangshuai
* @date: 2024/11/5 下午3:44
*/
@Data
@Component("jeecgSmsTemplateConfig")
@ConfigurationProperties(prefix = "jeecg.oss.sms-template")
public class JeecgSmsTemplateConfig {
/**
* 短信签名
*/
private String signature;
/**
* 短信模板code
*
* @return
*/
private Map<String,String> templateCode;
}

View File

@ -21,6 +21,12 @@ public class StaticConfig {
@Value(value = "${spring.mail.username:}")
private String emailFrom;
/**
* 是否开启定时发送
*/
@Value(value = "${spring.mail.timeJobSend:false}")
private Boolean timeJobSend;
// /**
// * 签名密钥串
// */

View File

@ -1,188 +1,182 @@
package org.jeecg.config;
import io.swagger.annotations.ApiOperation;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* @Author scott
*/
@Configuration
@EnableSwagger2WebMvc
@Import(BeanValidatorPluginsConfiguration.class)
public class Swagger2Config implements WebMvcConfigurer {
/**
*
* 显示swagger-ui.html文档展示页还必须注入swagger资源
*
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
/**
* swagger2的配置文件这里可以配置swagger2的一些基本的内容比如扫描的包等等
*
* @return Docket
*/
@Bean(value = "defaultApi2")
public Docket defaultApi2() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
//此包路径下的类,才生成接口文档
.apis(RequestHandlerSelectors.basePackage("org.jeecg"))
//加了ApiOperation注解的类,才生成接口文档
.apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build()
.securitySchemes(Collections.singletonList(securityScheme()))
.securityContexts(securityContexts())
.globalOperationParameters(setHeaderToken());
}
/***
* oauth2配置
* 需要增加swagger授权回调地址
* http://localhost:8888/webjars/springfox-swagger-ui/o2c.html
* @return
*/
@Bean
SecurityScheme securityScheme() {
return new ApiKey(CommonConstant.X_ACCESS_TOKEN, CommonConstant.X_ACCESS_TOKEN, "header");
}
/**
* JWT token
* @return
*/
private List<Parameter> setHeaderToken() {
ParameterBuilder tokenPar = new ParameterBuilder();
List<Parameter> pars = new ArrayList<>();
tokenPar.name(CommonConstant.X_ACCESS_TOKEN).description("token").modelRef(new ModelRef("string")).parameterType("header").required(false).build();
pars.add(tokenPar.build());
//update-begin-author:liusq---date:2024-08-15--for: 开启多租户时全局参数增加租户id
if(MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL){
ParameterBuilder tenantPar = new ParameterBuilder();
tenantPar.name(CommonConstant.TENANT_ID).description("租户ID").modelRef(new ModelRef("string")).parameterType("header").required(false).build();
pars.add(tenantPar.build());
}
//update-end-author:liusq---date:2024-08-15--for: 开启多租户时全局参数增加租户id
return pars;
}
/**
* api文档的详细信息函数,注意这里的注解引用的是哪个
*
* @return
*/
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
// //大标题
.title("JeecgBoot 后台服务API接口文档")
// 版本号
.version("1.0")
// .termsOfServiceUrl("NO terms of service")
// 描述
.description("后台API接口")
// 作者
.contact(new Contact("北京国炬信息技术有限公司","www.jeccg.com","jeecgos@163.com"))
.license("The Apache License, Version 2.0")
.licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
.build();
}
/**
* 新增 securityContexts 保持登录状态
*/
private List<SecurityContext> securityContexts() {
return new ArrayList(
Collections.singleton(SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.regex("^(?!auth).*$"))
.build())
);
}
private List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return new ArrayList(
Collections.singleton(new SecurityReference(CommonConstant.X_ACCESS_TOKEN, authorizationScopes)));
}
/**
* 解决springboot2.6 和springfox不兼容问题
* @return
*/
@Bean
public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
return new BeanPostProcessor() {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebMvcRequestHandlerProvider) {
customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
}
return bean;
}
private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
List<T> copy = mappings.stream()
.filter(mapping -> mapping.getPatternParser() == null)
.collect(Collectors.toList());
mappings.clear();
mappings.addAll(copy);
}
@SuppressWarnings("unchecked")
private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
try {
Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
field.setAccessible(true);
return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
} catch (IllegalArgumentException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
};
}
}
//package org.jeecg.config;
//
//
//import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
//import org.jeecg.common.constant.CommonConstant;
//import org.springframework.beans.BeansException;
//import org.springframework.beans.factory.config.BeanPostProcessor;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.context.annotation.Import;
//import org.springframework.util.ReflectionUtils;
//import org.springframework.web.bind.annotation.RestController;
//import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
//import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
//import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
//import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
//import springfox.documentation.builders.ApiInfoBuilder;
//import springfox.documentation.builders.ParameterBuilder;
//import springfox.documentation.builders.PathSelectors;
//import springfox.documentation.builders.RequestHandlerSelectors;
//import springfox.documentation.oas.annotations.EnableOpenApi;
//import springfox.documentation.schema.ModelRef;
//import springfox.documentation.service.*;
//import springfox.documentation.spi.DocumentationType;
//import springfox.documentation.spi.service.contexts.SecurityContext;
//import springfox.documentation.spring.web.plugins.Docket;
//import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider;
//import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;
//import springfox.documentation.swagger2.annotations.EnableSwagger2;
//
//import java.lang.reflect.Field;
//import java.util.ArrayList;
//import java.util.Collections;
//import java.util.List;
//import java.util.stream.Collectors;
//
///**
// * @Author scott
// */
//@Configuration
//@EnableSwagger2 //开启 Swagger2
//@EnableKnife4j //开启 knife4j可以不写
//@Import(BeanValidatorPluginsConfiguration.class)
//public class Swagger2Config implements WebMvcConfigurer {
//
// /**
// *
// * 显示swagger-ui.html文档展示页还必须注入swagger资源
// *
// * @param registry
// */
// @Override
// public void addResourceHandlers(ResourceHandlerRegistry registry) {
// registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
// registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
// registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
// }
//
// /**
// * swagger2的配置文件这里可以配置swagger2的一些基本的内容比如扫描的包等等
// *
// * @return Docket
// */
// @Bean(value = "defaultApi2")
// public Docket defaultApi2() {
// return new Docket(DocumentationType.SWAGGER_2)
// .apiInfo(apiInfo())
// .select()
// //此包路径下的类,才生成接口文档
// .apis(RequestHandlerSelectors.basePackage("org.jeecg"))
// //加了ApiOperation注解的类才生成接口文档
// .apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
// .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
// .paths(PathSelectors.any())
// .build()
// .securitySchemes(Collections.singletonList(securityScheme()))
// .securityContexts(securityContexts())
// .globalOperationParameters(setHeaderToken());
// }
//
// /***
// * oauth2配置
// * 需要增加swagger授权回调地址
// * http://localhost:8888/webjars/springfox-swagger-ui/o2c.html
// * @return
// */
// @Bean
// SecurityScheme securityScheme() {
// return new ApiKey(CommonConstant.X_ACCESS_TOKEN, CommonConstant.X_ACCESS_TOKEN, "header");
// }
// /**
// * JWT token
// * @return
// */
// private List<Parameter> setHeaderToken() {
// ParameterBuilder tokenPar = new ParameterBuilder();
// List<Parameter> pars = new ArrayList<>();
// tokenPar.name(CommonConstant.X_ACCESS_TOKEN).description("token").modelRef(new ModelRef("string")).parameterType("header").required(false).build();
// pars.add(tokenPar.build());
// return pars;
// }
//
// /**
// * api文档的详细信息函数,注意这里的注解引用的是哪个
// *
// * @return
// */
// private ApiInfo apiInfo() {
// return new ApiInfoBuilder()
// // //大标题
// .title("JeecgBoot 后台服务API接口文档")
// // 版本号
// .version("1.0")
//// .termsOfServiceUrl("NO terms of service")
// // 描述
// .description("后台API接口")
// // 作者
// .contact(new Contact("北京国炬信息技术有限公司","www.jeccg.com","jeecgos@163.com"))
// .license("The Apache License, Version 2.0")
// .licenseUrl("http://www.apache.org/licenses/LICENSE-2.0.html")
// .build();
// }
//
// /**
// * 新增 securityContexts 保持登录状态
// */
// private List<SecurityContext> securityContexts() {
// return new ArrayList(
// Collections.singleton(SecurityContext.builder()
// .securityReferences(defaultAuth())
// .forPaths(PathSelectors.regex("^(?!auth).*$"))
// .build())
// );
// }
//
// private List<SecurityReference> defaultAuth() {
// AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
// AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
// authorizationScopes[0] = authorizationScope;
// return new ArrayList(
// Collections.singleton(new SecurityReference(CommonConstant.X_ACCESS_TOKEN, authorizationScopes)));
// }
//
// /**
// * 解决springboot2.6 和springfox不兼容问题
// * @return
// */
// @Bean
// public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
// return new BeanPostProcessor() {
//
// @Override
// public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
// customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
// }
// return bean;
// }
//
// private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
// List<T> copy = mappings.stream()
// .filter(mapping -> mapping.getPatternParser() == null)
// .collect(Collectors.toList());
// mappings.clear();
// mappings.addAll(copy);
// }
//
// @SuppressWarnings("unchecked")
// private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
// try {
// Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
// field.setAccessible(true);
// return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
// } catch (IllegalArgumentException | IllegalAccessException e) {
// throw new IllegalStateException(e);
// }
// }
// };
// }
//
//
//}

View File

@ -0,0 +1,102 @@
package org.jeecg.config;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.constant.CommonConstant;
import org.springdoc.core.customizers.GlobalOpenApiCustomizer;
import org.springdoc.core.filters.GlobalOpenApiMethodFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* @author eightmonth
*/
@Slf4j
@Configuration
@PropertySource("classpath:config/default-spring-doc.properties")
public class Swagger3Config implements WebMvcConfigurer {
// 定义不需要注入安全要求的路径集合
Set<String> excludedPaths = new HashSet<>(Arrays.asList(
"/sys/randomImage/{key}",
"/sys/login",
"/sys/phoneLogin",
"/sys/mLogin",
"/sys/sms",
"/sys/cas/client/validateLogin",
"/test/jeecgDemo/demo3",
"/sys/thirdLogin/**",
"/sys/user/register"
));
/**
*
* 显示swagger-ui.html文档展示页还必须注入swagger资源
*
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
@Bean
public GlobalOpenApiMethodFilter globalOpenApiMethodFilter() {
return method -> method.isAnnotationPresent(Operation.class);
}
@Bean
public GlobalOpenApiCustomizer globalOpenApiCustomizer() {
return openApi -> {
// 全局添加鉴权参数
if (openApi.getPaths() != null) {
openApi.getPaths().forEach((path, pathItem) -> {
//log.debug("path: {}", path);
// 检查当前路径是否在排除列表中
boolean isExcluded = excludedPaths.stream().anyMatch(excludedPath ->
excludedPath.equals(path) ||
(excludedPath.endsWith("**") && path.startsWith(excludedPath.substring(0, excludedPath.length() - 2)))
);
if (!isExcluded) {
// 接口添加鉴权参数
pathItem.readOperations()
.forEach(operation ->
operation.addSecurityItem(new SecurityRequirement().addList(CommonConstant.X_ACCESS_TOKEN))
);
}
});
}
};
}
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("JeecgBoot 后台服务API接口文档")
.version("3.7.4")
.contact(new Contact().name("北京国炬信息技术有限公司").url("www.jeccg.com").email("jeecgos@163.com"))
.description( "后台API接口")
.termsOfService("NO terms of service")
.license(new License().name("Apache 2.0").url("http://www.apache.org/licenses/LICENSE-2.0.html")))
.addSecurityItem(new SecurityRequirement().addList(CommonConstant.X_ACCESS_TOKEN))
.components(new Components().addSecuritySchemes(CommonConstant.X_ACCESS_TOKEN,
new SecurityScheme().name(CommonConstant.X_ACCESS_TOKEN).type(SecurityScheme.Type.HTTP)));
}
}

View File

@ -13,13 +13,16 @@ import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.actuate.trace.http.InMemoryHttpTraceRepository;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.CacheControl;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.cors.CorsConfiguration;
@ -37,6 +40,7 @@ import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Spring Boot 2.0 解决跨域问题
@ -67,6 +71,8 @@ public class WebMvcConfiguration implements WebMvcConfigurer {
.addResourceLocations("file:" + jeecgBaseConfig.getPath().getWebapp() + "//");
}
resourceHandlerRegistration.addResourceLocations(staticLocations.split(","));
// 设置缓存控制标头 Cache-Control有效期为30天
resourceHandlerRegistration.setCacheControl(CacheControl.maxAge(30, TimeUnit.DAYS));
}
/**
@ -144,6 +150,7 @@ public class WebMvcConfiguration implements WebMvcConfigurer {
* 解决metrics端点不显示jvm信息的问题(zyf)
*/
@Bean
@ConditionalOnBean(name = "meterRegistryPostProcessor")
InitializingBean forcePrometheusPostProcessor(BeanPostProcessor meterRegistryPostProcessor) {
return () -> meterRegistryPostProcessor.postProcessAfterInitialization(prometheusMeterRegistry, "");
}

View File

@ -31,7 +31,7 @@ public class WebSocketConfig {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(websocketFilter());
//TODO 临时注释掉测试下线上socket总断的问题
bean.addUrlPatterns("/taskCountSocket/*", "/websocket/*","/eoaSocket/*","/eoaNewChatSocket/*", "/newsWebsocket/*", "/vxeSocket/*");
bean.addUrlPatterns("/taskCountSocket/*", "/websocket/*","/eoaSocket/*","/eoaNewChatSocket/*", "/newsWebsocket/*", "/dragChannelSocket/*", "/vxeSocket/*");
return bean;
}

View File

@ -60,7 +60,18 @@ public class MybatisPlusSaasConfig {
TENANT_TABLE.add("sys_category");
TENANT_TABLE.add("sys_data_source");
TENANT_TABLE.add("sys_position");
//TENANT_TABLE.add("sys_announcement");
//b-2.仪表盘
TENANT_TABLE.add("onl_drag_page");
TENANT_TABLE.add("onl_drag_dataset_head");
TENANT_TABLE.add("jimu_report_data_source");
TENANT_TABLE.add("jimu_report");
TENANT_TABLE.add("jimu_dict");
//b-4.AIRAG
TENANT_TABLE.add("airag_app");
TENANT_TABLE.add("airag_flow");
TENANT_TABLE.add("airag_knowledge");
TENANT_TABLE.add("airag_knowledge_doc");
TENANT_TABLE.add("airag_model");
}
//2.示例测试

View File

@ -17,15 +17,17 @@ import org.jeecg.config.shiro.filters.CustomShiroFilterFactoryBean;
import org.jeecg.config.shiro.filters.JwtFilter;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.*;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.Environment;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.filter.DelegatingFilterProxy;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
@ -33,6 +35,7 @@ import redis.clients.jedis.JedisCluster;
import javax.annotation.Resource;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import java.lang.reflect.Method;
import java.util.*;
/**
@ -123,7 +126,6 @@ public class ShiroConfig {
filterChainDefinitionMap.put("/**/*.ttf", "anon");
filterChainDefinitionMap.put("/**/*.woff", "anon");
filterChainDefinitionMap.put("/**/*.woff2", "anon");
filterChainDefinitionMap.put("/**/*.glb", "anon");
filterChainDefinitionMap.put("/**/*.wasm", "anon");
//update-end--Author:scott Date:20221116 for排除静态资源后缀
@ -132,7 +134,7 @@ public class ShiroConfig {
filterChainDefinitionMap.put("/swagger-ui.html", "anon");
filterChainDefinitionMap.put("/swagger**/**", "anon");
filterChainDefinitionMap.put("/webjars/**", "anon");
filterChainDefinitionMap.put("/v2/**", "anon");
filterChainDefinitionMap.put("/v3/**", "anon");
// update-begin--Author:sunjianlei Date:20210510 for排除消息通告查看详情页面用于第三方APP
filterChainDefinitionMap.put("/sys/annountCement/show/**", "anon");
@ -143,12 +145,18 @@ public class ShiroConfig {
filterChainDefinitionMap.put("/**/*.js.map", "anon");
filterChainDefinitionMap.put("/**/*.css.map", "anon");
//拖拽仪表盘设计器排除
//积木BI大屏和仪表盘排除
filterChainDefinitionMap.put("/drag/view", "anon");
filterChainDefinitionMap.put("/drag/page/queryById", "anon");
filterChainDefinitionMap.put("/drag/page/addVisitsNumber", "anon");
filterChainDefinitionMap.put("/drag/page/queryTemplateList", "anon");
filterChainDefinitionMap.put("/drag/share/view/**", "anon");
filterChainDefinitionMap.put("/drag/onlDragDatasetHead/getAllChartData", "anon");
filterChainDefinitionMap.put("/drag/onlDragDatasetHead/getTotalData", "anon");
filterChainDefinitionMap.put("/drag/mock/json/**", "anon");
filterChainDefinitionMap.put("/jimubi/view", "anon");
filterChainDefinitionMap.put("/jimubi/share/view/**", "anon");
//大屏模板例子
filterChainDefinitionMap.put("/test/bigScreen/**", "anon");
filterChainDefinitionMap.put("/bigscreen/template1/**", "anon");
@ -172,6 +180,8 @@ public class ShiroConfig {
// 企业微信证书排除
filterChainDefinitionMap.put("/WW_verify*", "anon");
filterChainDefinitionMap.put("/openapi/call/**", "anon");
// 添加自己的过滤器并且取名为jwt
Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
//如果cloudServer为空 则说明是单体 需要加载跨域配置【微服务跨域切换】
@ -207,6 +217,10 @@ public class ShiroConfig {
//update-begin---author:chenrui ---date:20241202 for[issues/7491]运行时间好长,效率慢 ------------
registration.addUrlPatterns("/test/ai/chat/send");
//update-end---author:chenrui ---date:20241202 for[issues/7491]运行时间好长,效率慢 ------------
registration.addUrlPatterns("/airag/flow/run");
registration.addUrlPatterns("/airag/flow/debug");
registration.addUrlPatterns("/airag/chat/send");
registration.addUrlPatterns("/airag/app/debug");
//支持异步
registration.setAsyncSupported(true);
registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC);
@ -306,7 +320,7 @@ public class ShiroConfig {
return sentinelManager;
}
// redis 单机支持,在集群为空,或者集群无机器时候使用 add by jzyadmin@163.com
if (lettuceConnectionFactory.getClusterConfiguration() == null || lettuceConnectionFactory.getClusterConfiguration().getClusterNodes().isEmpty()) {
RedisManager redisManager = new RedisManager();

View File

@ -168,8 +168,8 @@ public class ShiroRealm extends AuthorizingRealm {
//*********************************************
if(!isAuthorization){
log.warn("租户异常——当前登录租户" + contextTenantId);
log.warn("租户异常——用户拥有租户" + userTenantIds);
log.info("租户异常——登录租户:" + contextTenantId);
log.info("租户异常——用户拥有租户" + userTenantIds);
throw new AuthenticationException("登录租户授权变更,请重新登陆!");
}
//*********************************************

View File

@ -91,6 +91,10 @@ public class IgnoreAuthPostProcessor implements InitializingBean {
if (bases.length > 0) {
for (String base : bases) {
for (String uri : uris) {
// 如果uri包含路径占位符, 则需要将其替换为*
if (uri.matches(".*\\{.*}.*")) {
uri = uri.replaceAll("\\{.*?}", "*");
}
urls.add(prefix(base) + prefix(uri));
}
}

View File

@ -1,5 +1,7 @@
package org.jeecg.config.shiro.ignore;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import java.util.ArrayList;
import java.util.List;
@ -12,6 +14,7 @@ import java.util.List;
public class InMemoryIgnoreAuth {
private static final List<String> IGNORE_AUTH_LIST = new ArrayList<>();
private static PathMatcher MATCHER = new AntPathMatcher();
public InMemoryIgnoreAuth() {}
public static void set(List<String> list) {
@ -28,7 +31,7 @@ public class InMemoryIgnoreAuth {
public static boolean contains(String url) {
for (String ignoreAuth : IGNORE_AUTH_LIST) {
if (url.endsWith(ignoreAuth)) {
if(MATCHER.match(ignoreAuth,url)){
return true;
}
}

View File

@ -0,0 +1,2 @@
springdoc.auto-tag-classes: false
springdoc.packages-to-scan: org.jeecg

View File

@ -3,10 +3,10 @@ package org.jeecg.test.sqlinjection;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import org.jeecg.common.util.SqlInjectionUtil;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**

View File

@ -3,10 +3,10 @@ package org.jeecg.test.sqlinjection;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import org.jeecg.common.util.SqlInjectionUtil;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**

View File

@ -3,10 +3,10 @@ package org.jeecg.test.sqlinjection;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.JSQLParserException;
import org.jeecg.common.util.SqlInjectionUtil;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**

View File

@ -2,7 +2,7 @@ package org.jeecg.test.sqlinjection;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlInjectionUtils;
import org.jeecg.common.util.SqlInjectionUtil;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;

View File

@ -4,7 +4,7 @@ import net.sf.jsqlparser.JSQLParserException;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.common.util.sqlparse.JSqlParserUtils;
import org.jeecg.common.util.sqlparse.vo.SelectSqlInfo;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import java.util.Map;

View File

@ -1,17 +1,12 @@
package org.jeecg.test.sqlparse;
import net.sf.jsqlparser.JSQLParserException;
import org.jeecg.common.util.IpUtils;
import org.jeecg.common.util.oConvertUtils;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author: scott
* @date: 2024年04月29日 16:48

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>jeecg-boot-parent</artifactId>
<groupId>org.jeecgframework.boot</groupId>
<version>3.7.1</version>
<version>3.7.4</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -14,8 +14,10 @@ import org.jetbrains.annotations.NotNull;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.util.Objects;
import java.util.function.Consumer;
//update-begin---author:chenrui ---date:20240126 for【QQYUN-7932】AI助手------------
/**
* OpenAI的SSE监听
* @author chenrui
@ -29,22 +31,47 @@ public class OpenAISSEEventSourceListener extends EventSourceListener {
private SseEmitter sseEmitter;
private String topicId;
/**
* 回复消息内容
*/
private String messageContent = "";
/**
* 完成回复回调
*/
private Consumer<String> doneCallback;
/**
* 是否正在思考
*/
private boolean isThinking = false;
public OpenAISSEEventSourceListener(SseEmitter sseEmitter) {
this.sseEmitter = sseEmitter;
}
public OpenAISSEEventSourceListener(String topicId,SseEmitter sseEmitter){
public OpenAISSEEventSourceListener(String topicId, SseEmitter sseEmitter){
this.topicId = topicId;
this.sseEmitter = sseEmitter;
}
/**
* 设置消息完成响应时的回调
* for [QQYUN-11102/QQYUN-11109]兼容deepseek模型,支持tink标签
* @param doneCallback
* @author chenrui
* @date 2025/2/7 18:14
*/
public void onDone(Consumer<String> doneCallback){
this.doneCallback = doneCallback;
}
/**
* {@inheritDoc}
*/
@Override
public void onOpen(@NotNull EventSource eventSource, @NotNull Response response) {
log.info("OpenAI建立sse连接...");
log.info("ai-chat建立sse连接...");
}
/**
@ -53,10 +80,12 @@ public class OpenAISSEEventSourceListener extends EventSourceListener {
@SneakyThrows
@Override
public void onEvent(@NotNull EventSource eventSource, String id, String type, @NotNull String data) {
log.debug("OpenAI返回数据:{}", data);
log.debug("ai-chat返回数据:{}", data);
tokens += 1;
if (data.equals("[DONE]")) {
log.info("OpenAI返回数据结束了");
log.info("ai-chat返回数据结束了");
this.doneCallback.accept(messageContent);
messageContent = "";
sseEmitter.send(SseEmitter.event()
.id("[TOKENS]")
.data("<br/><br/>tokens" + tokens())
@ -72,12 +101,46 @@ public class OpenAISSEEventSourceListener extends EventSourceListener {
ObjectMapper mapper = new ObjectMapper();
ChatCompletionResponse completionResponse = mapper.readValue(data, ChatCompletionResponse.class); // 读取Json
try {
sseEmitter.send(SseEmitter.event()
.id(this.topicId)
.data(completionResponse.getChoices().get(0).getDelta())
.reconnectTime(3000));
//update-begin---author:chenrui ---date:20250207 for[QQYUN-11102/QQYUN-11109]兼容deepseek模型,支持think标签------------
// 兼容think标签
//update-begin---author:chenrui ---date:20250210 for判断空,防止反悔的内容为空报错.------------
if(null != completionResponse.getChoices()
&& !completionResponse.getChoices().isEmpty()
&& null != completionResponse.getChoices().get(0)) {
//update-end---author:chenrui ---date:20250210 for判断空,防止反悔的内容为空报错.------------
Message delta = completionResponse.getChoices().get(0).getDelta();
if (null != delta) {
String content = delta.getContent();
if ("<think>".equals(content)) {
isThinking = true;
content = "> ";
delta.setContent(content);
}
if ("</think>".equals(content)) {
isThinking = false;
content = "\n\n";
delta.setContent(content);
}
if (isThinking) {
if (null != content && content.contains("\n")) {
content = "\n> ";
delta.setContent(content);
}
} else {
// 响应消息体不记录思考过程
messageContent += null == content ? "" : content;
}
log.info("ai-chat返回数据,发送给前端:" + content);
sseEmitter.send(SseEmitter.event()
.id(this.topicId)
.data(delta)
.reconnectTime(3000));
}
}
//update-end---author:chenrui ---date:20250207 for[QQYUN-11102/QQYUN-11109]兼容deepseek模型,支持think标签------------
} catch (Exception e) {
log.error(e.getMessage(),e);
log.error("ai-chat返回数据,发生异常"+e.getMessage(),e);
sseEmitter.completeWithError(e);
eventSource.cancel();
}
}
@ -86,7 +149,7 @@ public class OpenAISSEEventSourceListener extends EventSourceListener {
@Override
public void onClosed(@NotNull EventSource eventSource) {
log.info("流式输出返回值总共{}tokens", tokens() - 2);
log.info("OpenAI关闭sse连接...");
log.info("ai-chat关闭sse连接...");
}
@ -96,10 +159,10 @@ public class OpenAISSEEventSourceListener extends EventSourceListener {
String errMsg = "";
ResponseBody body = null == response ? null:response.body();
if (Objects.nonNull(body)) {
log.error("OpenAI sse连接异常data{},异常:{}", body.string(), t.getMessage());
log.error("ai-chat sse连接异常data{},异常:{}", body.string(), t.getMessage());
errMsg = body.string();
} else {
log.error("OpenAI sse连接异常data{},异常:{}", response, t.getMessage());
log.error("ai-chat sse连接异常data{},异常:{}", response, t.getMessage());
errMsg = t.getMessage();
}
eventSource.cancel();

View File

@ -9,6 +9,7 @@ import com.unfbx.chatgpt.entity.chat.Message;
import com.unfbx.chatgpt.exception.BaseException;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.jeecg.chatgpt.prop.AiChatProperties;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.vo.LoginUser;
@ -58,6 +59,11 @@ public class ChatServiceImpl implements ChatService {
private OpenAiStreamClient openAiStreamClient = null;
/**
* ai聊天配置
* for [QQYUN-10943]【AI 重要】jeecg-boot-starter-chatgpt 支持deepseek等国产模型
*/
private AiChatProperties aiChatProperties;
//update-begin---author:chenrui ---date:20240131 for[QQYUN-8212]fix 没有配置启动报错------------
/**
@ -66,17 +72,17 @@ public class ChatServiceImpl implements ChatService {
* @author chenrui
* @date 2024/2/3 23:08
*/
private OpenAiStreamClient ensureClient(){
private void ensureClient(){
if (null == this.openAiStreamClient){
//update-begin---author:chenrui ---date:20240625 for[TV360X-1570]给于更友好的提示提示未配置ai------------
try {
this.openAiStreamClient = SpringContextUtils.getBean(OpenAiStreamClient.class);
this.aiChatProperties = SpringContextUtils.getBean(AiChatProperties.class);
} catch (Exception ignored) {
sendErrorMsg("如果您想使用AI助手请先设置相应配置!");
}
//update-end---author:chenrui ---date:20240625 for[TV360X-1570]给于更友好的提示提示未配置ai------------
}
return this.openAiStreamClient;
}
//update-end---author:chenrui ---date:20240131 for[QQYUN-8212]fix 没有配置启动报错------------
@ -98,6 +104,7 @@ public class ChatServiceImpl implements ChatService {
//超时回调
sseEmitter.onTimeout(() -> {
log.info("[{}]连接超时...................", uid);
LocalCache.CACHE.remove(uid);
});
//异常回调
sseEmitter.onError(
@ -109,7 +116,7 @@ public class ChatServiceImpl implements ChatService {
.name("发生异常!")
.data(Message.builder().content("发生异常请重试!").build())
.reconnectTime(3000));
LocalCache.CACHE.put(uid, sseEmitter);
LocalCache.CACHE.remove(uid);
} catch (IOException e) {
log.error(e.getMessage(),e);
}
@ -138,6 +145,7 @@ public class ChatServiceImpl implements ChatService {
@Override
public void sendMessage(String topicId, String message) {
ensureClient();
String uid = getUserId();
if (StrUtil.isBlank(message)) {
log.info("参数异常message为null");
@ -164,18 +172,22 @@ public class ChatServiceImpl implements ChatService {
throw new JeecgBootException("聊天消息推送失败uid:[{}],没有创建连接,请重试。~");
}
//update-begin---author:chenrui ---date:20240625 for[TV360X-1570]给于更友好的提示提示未配置ai------------
OpenAiStreamClient client = ensureClient();
if (null != client) {
if (null != openAiStreamClient) {
OpenAISSEEventSourceListener openAIEventSourceListener = new OpenAISSEEventSourceListener(topicId, sseEmitter);
List<Message> finalMsgHistory = msgHistory;
openAIEventSourceListener.onDone(respMessage -> {
Message tempMessage = Message.builder().content(respMessage).role(Message.Role.ASSISTANT).build();
finalMsgHistory.add(tempMessage);
redisTemplate.opsForHash().put(cacheKey, CACHE_KEY_MSG_CONTEXT, JSONUtil.toJsonStr(finalMsgHistory));
});
log.info("话题:{},开始发送消息~~~", topicId);
ChatCompletion completion = ChatCompletion
.builder()
.messages(msgHistory)
.model(ChatCompletion.Model.GPT_3_5_TURBO.getName())
.model(aiChatProperties.getModel())
.build();
client.streamChatCompletion(completion, openAIEventSourceListener);
redisTemplate.opsForHash().put(cacheKey, CACHE_KEY_MSG_CONTEXT, JSONUtil.toJsonStr(msgHistory));
openAiStreamClient.streamChatCompletion(completion, openAIEventSourceListener);
//update-end---author:chenrui ---date:20240223 for[QQYUN-8225]聊天记录保存------------
Result.ok(completion.tokens());
}
//update-end---author:chenrui ---date:20240625 for[TV360X-1570]给于更友好的提示提示未配置ai------------
}

View File

@ -8,9 +8,9 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.mgt.DefaultSecurityManager;
@ -45,7 +45,7 @@ import java.util.List;
* @Version:V2.0
*/
@Slf4j
@Api(tags = "单表DEMO")
@Tag(name = "单表DEMO")
@RestController
@RequestMapping("/test/jeecgDemo")
public class JeecgDemoController extends JeecgController<JeecgDemo, IJeecgDemoService> {
@ -64,7 +64,7 @@ public class JeecgDemoController extends JeecgController<JeecgDemo, IJeecgDemoSe
* @param req
* @return
*/
@ApiOperation(value = "获取Demo数据列表", notes = "获取所有Demo数据列表")
@Operation(summary = "获取所有Demo数据列表")
@GetMapping(value = "/list")
@PermissionData(pageComponent = "jeecg/JeecgDemoList")
public Result<?> list(JeecgDemo jeecgDemo, @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo, @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
@ -89,7 +89,7 @@ public class JeecgDemoController extends JeecgController<JeecgDemo, IJeecgDemoSe
*/
@PostMapping(value = "/add")
@AutoLog(value = "添加测试DEMO")
@ApiOperation(value = "添加DEMO", notes = "添加DEMO")
@Operation(summary = "添加DEMO")
public Result<?> add(@RequestBody JeecgDemo jeecgDemo) {
jeecgDemoService.save(jeecgDemo);
return Result.OK("添加成功!");
@ -102,7 +102,7 @@ public class JeecgDemoController extends JeecgController<JeecgDemo, IJeecgDemoSe
* @return
*/
@AutoLog(value = "编辑DEMO", operateType = CommonConstant.OPERATE_TYPE_3)
@ApiOperation(value = "编辑DEMO", notes = "编辑DEMO")
@Operation(summary = "编辑DEMO")
@RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST})
public Result<?> edit(@RequestBody JeecgDemo jeecgDemo) {
jeecgDemoService.updateById(jeecgDemo);
@ -117,7 +117,7 @@ public class JeecgDemoController extends JeecgController<JeecgDemo, IJeecgDemoSe
*/
@AutoLog(value = "删除测试DEMO")
@DeleteMapping(value = "/delete")
@ApiOperation(value = "通过ID删除DEMO", notes = "通过ID删除DEMO")
@Operation(summary = "通过ID删除DEMO")
public Result<?> delete(@RequestParam(name = "id", required = true) String id) {
jeecgDemoService.removeById(id);
return Result.OK("删除成功!");
@ -130,7 +130,7 @@ public class JeecgDemoController extends JeecgController<JeecgDemo, IJeecgDemoSe
* @return
*/
@DeleteMapping(value = "/deleteBatch")
@ApiOperation(value = "批量删除DEMO", notes = "批量删除DEMO")
@Operation(summary = "批量删除DEMO")
public Result<?> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
this.jeecgDemoService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
@ -143,8 +143,8 @@ public class JeecgDemoController extends JeecgController<JeecgDemo, IJeecgDemoSe
* @return
*/
@GetMapping(value = "/queryById")
@ApiOperation(value = "通过ID查询DEMO", notes = "通过ID查询DEMO")
public Result<?> queryById(@ApiParam(name = "id", value = "示例id", required = true) @RequestParam(name = "id", required = true) String id) {
@Operation(summary = "通过ID查询DEMO")
public Result<?> queryById(@Parameter(name = "id", description = "示例id", required = true) @RequestParam(name = "id", required = true) String id) {
JeecgDemo jeecgDemo = jeecgDemoService.getById(id);
return Result.OK(jeecgDemo);
}
@ -477,7 +477,7 @@ public class JeecgDemoController extends JeecgController<JeecgDemo, IJeecgDemoSe
* 测试Mono对象
* @return
*/
@ApiOperation("Mono测试")
@Operation(summary = "Mono测试")
@GetMapping(value ="/test")
public Mono<String> test() {
//解决shiro报错No SecurityManager accessible to the calling code, either bound to the org.apache.shiro

View File

@ -1,8 +1,7 @@
package org.jeecg.modules.demo.test.controller;
import io.lettuce.core.dynamic.annotation.Param;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
@ -21,7 +20,7 @@ import java.util.List;
* @Date:2020-04-21
*/
@Slf4j
@Api(tags = "动态数据源测试")
@Tag(name = "动态数据源测试")
@RestController
@RequestMapping("/test/dynamic")
public class JeecgDynamicDataController extends JeecgController<JeecgDemo, IJeecgDemoService> {
@ -37,7 +36,7 @@ public class JeecgDynamicDataController extends JeecgController<JeecgDemo, IJeec
*/
@PostMapping(value = "/test1")
@AutoLog(value = "动态切换数据源")
@ApiOperation(value = "动态切换数据源", notes = "动态切换数据源")
@Operation(summary = "动态切换数据源")
public Result<List<JeecgDemo>> selectSpelByKey(@RequestParam(required = false) String dsName) {
List<JeecgDemo> list = jeecgDynamicDataService.selectSpelByKey(dsName);
return Result.OK(list);

View File

@ -3,6 +3,7 @@ package org.jeecg.modules.demo.test.entity;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.Version;
import io.swagger.v3.oas.annotations.media.Schema;
import org.jeecg.common.system.base.entity.JeecgEntity;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.springframework.format.annotation.DateTimeFormat;
@ -10,8 +11,6 @@ import org.springframework.format.annotation.DateTimeFormat;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
@ -25,59 +24,59 @@ import lombok.experimental.Accessors;
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value="测试DEMO对象", description="测试DEMO")
@Schema(description="测试DEMO")
@TableName("demo")
public class JeecgDemo extends JeecgEntity implements Serializable {
private static final long serialVersionUID = 1L;
/** 姓名 */
@Excel(name="姓名",width=25)
@ApiModelProperty(value = "姓名")
@Schema(description = "姓名")
private java.lang.String name;
/** 关键词 */
@ApiModelProperty(value = "关键词")
@Schema(description = "关键词")
@Excel(name="关键词",width=15)
private java.lang.String keyWord;
/** 打卡时间 */
@ApiModelProperty(value = "打卡时间")
@Schema(description = "打卡时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name="打卡时间",width=20,format="yyyy-MM-dd HH:mm:ss")
private java.util.Date punchTime;
/** 工资 */
@ApiModelProperty(value = "工资",example = "0")
@Schema(description = "工资",example = "0")
@Excel(name="工资",type = 4,width=15)
private java.math.BigDecimal salaryMoney;
/** 奖金 */
@ApiModelProperty(value = "奖金",example = "0")
@Schema(description = "奖金",example = "0")
@Excel(name="奖金",type = 4,width=15)
private java.lang.Double bonusMoney;
/** 性别 {男:1,女:2} */
@ApiModelProperty(value = "性别")
@Schema(description = "性别")
@Excel(name = "性别", width = 15, dicCode = "sex")
private java.lang.String sex;
/** 年龄 */
@ApiModelProperty(value = "年龄",example = "0")
@Schema(description = "年龄",example = "0")
@Excel(name="年龄",type = 4,width=15)
private java.lang.Integer age;
/** 生日 */
@ApiModelProperty(value = "生日")
@Schema(description = "生日")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
@Excel(name="生日",format="yyyy-MM-dd")
private java.util.Date birthday;
/** 邮箱 */
@ApiModelProperty(value = "邮箱")
@Schema(description = "邮箱")
@Excel(name="邮箱",width=30)
private java.lang.String email;
/** 个人简介 */
@ApiModelProperty(value = "个人简介")
@Schema(description = "个人简介")
private java.lang.String content;
/** 部门编码 */
@Excel(name="部门编码",width=25)
@ApiModelProperty(value = "部门编码")
@Schema(description = "部门编码")
private java.lang.String sysOrgCode;
@ApiModelProperty(value = "租户ID")
@Schema(description = "租户ID")
private java.lang.Integer tenantId;
/** 乐观锁字段 */
@Version

View File

@ -26,6 +26,12 @@ public interface IJeecgDemoService extends JeecgService<JeecgDemo> {
* @return demo对象
*/
public JeecgDemo getByIdCacheable(String id);
/**
* 通过id过去demo数据先读缓存在读数据库
* @param id 数据库id
* @return demo对象
*/
public JeecgDemo getByIdCacheableTTL(String id);
/**
* 查询列表数据 在service中获取数据权限sql信息

View File

@ -71,6 +71,22 @@ public class JeecgDemoServiceImpl extends ServiceImpl<JeecgDemoMapper, JeecgDemo
}
/**
* @Cacheable自定义TTL#60单位是秒目前只支持这一种格式
* 通过注解方式指定缓存有效期60秒
*
* 参考博客https://www.cnblogs.com/h2285409/p/18324396
*/
@Override
@Cacheable(cacheNames = "ceshi:redis:ttl#60", key = "#id")
public JeecgDemo getByIdCacheableTTL(String id) {
JeecgDemo t = jeecgDemoMapper.selectById(id);
System.err.println("---未读缓存,读取数据库---");
System.err.println(t);
return t;
}
@Override
public IPage<JeecgDemo> queryListWithPermission(int pageSize,int pageNo) {
Page<JeecgDemo> page = new Page<>(pageNo, pageSize);

View File

@ -0,0 +1,370 @@
{
"list": [
{
"key": "1717072932495_439966",
"type": "card",
"isAutoGrid": true,
"isContainer": true,
"list": [
{
"type": "input",
"name": "名称",
"className": "form-input",
"icon": "icon-input",
"hideTitle": false,
"options": {
"width": "100%",
"defaultValue": "",
"required": true,
"dataType": null,
"pattern": "",
"placeholder": "",
"clearable": false,
"readonly": false,
"disabled": false,
"fillRuleCode": "",
"showPassword": false,
"unique": false,
"hidden": false,
"hiddenOnAdd": false,
"fieldNote": "",
"autoWidth": 50
},
"advancedSetting": {
"defaultValue": {
"type": "compose",
"value": "",
"format": "string",
"allowFunc": true,
"valueSplit": "",
"customConfig": false
}
},
"remoteAPI": {
"url": "",
"executed": false
},
"key": "1717072932495_556479",
"model": "input_1717072932495_556479",
"modelType": "main",
"rules": [
{
"required": true,
"message": "${title}必须填写"
}
],
"isSubItem": false
},
{
"type": "number",
"name": "数字",
"className": "form-number",
"icon": "icon-number",
"hideTitle": false,
"options": {
"width": "",
"required": false,
"defaultValue": 0,
"placeholder": "",
"controls": false,
"min": 0,
"minUnlimited": true,
"max": 100,
"maxUnlimited": true,
"step": 1,
"disabled": false,
"controlsPosition": "right",
"unitText": "",
"unitPosition": "suffix",
"showPercent": false,
"align": "left",
"hidden": false,
"hiddenOnAdd": false,
"fieldNote": "",
"autoWidth": 50
},
"advancedSetting": {
"defaultValue": {
"type": "compose",
"value": "",
"format": "number",
"allowFunc": true,
"valueSplit": "",
"customConfig": false
}
},
"remoteAPI": {
"url": "",
"executed": false
},
"key": "1717072985868_606195",
"model": "number_1717072985868_606195",
"modelType": "main",
"rules": [],
"isSubItem": false
}
],
"options": {
"required": false,
"hiddenOnAdd": false,
"hidden": false,
"fieldNote": ""
},
"model": "card_1717072932495_439966",
"hideTitle": false,
"modelType": "main"
},
{
"key": "1717072988159_545097",
"type": "card",
"isAutoGrid": true,
"isContainer": true,
"list": [
{
"type": "money",
"name": "金额",
"className": "form-money",
"icon": "icon-money",
"hideTitle": false,
"options": {
"width": "180px",
"placeholder": "请输入金额",
"required": false,
"unitText": "元",
"unitPosition": "suffix",
"precision": 2,
"hidden": false,
"disabled": false,
"hiddenOnAdd": false,
"fieldNote": "",
"autoWidth": 50
},
"advancedSetting": {
"defaultValue": {
"type": "compose",
"value": "",
"format": "number",
"allowFunc": true,
"valueSplit": "",
"customConfig": false
}
},
"remoteAPI": {
"url": "",
"executed": false
},
"key": "1717072988159_568693",
"model": "money_1717072988159_568693",
"modelType": "main",
"rules": [],
"isSubItem": false
},
{
"type": "select",
"name": "下拉选择框",
"className": "form-select",
"icon": "icon-select",
"hideTitle": false,
"options": {
"defaultValue": "",
"multiple": false,
"disabled": false,
"clearable": true,
"placeholder": "",
"required": false,
"showLabel": false,
"showType": "default",
"width": "",
"useColor": false,
"colorIteratorIndex": 3,
"options": [
{
"value": "下拉框1",
"itemColor": "#2196F3"
},
{
"value": "下拉框2",
"itemColor": "#08C9C9"
},
{
"value": "下拉框3",
"itemColor": "#00C345"
}
],
"remote": false,
"filterable": false,
"remoteOptions": [],
"props": {
"value": "value",
"label": "label"
},
"remoteFunc": "",
"hidden": false,
"hiddenOnAdd": false,
"fieldNote": "",
"autoWidth": 50
},
"advancedSetting": {
"defaultValue": {
"type": "compose",
"value": "",
"format": "string",
"allowFunc": true,
"valueSplit": ",",
"customConfig": true
}
},
"remoteAPI": {
"url": "",
"executed": false
},
"key": "1717072991431_622198",
"model": "select_1717072991431_622198",
"modelType": "main",
"rules": [],
"isSubItem": false
}
],
"options": {
"required": false,
"hiddenOnAdd": false,
"hidden": false,
"fieldNote": ""
},
"model": "card_1717072988159_545097",
"hideTitle": false,
"modelType": "main"
},
{
"key": "1717072932495_382575",
"type": "card",
"isAutoGrid": true,
"isContainer": true,
"list": [
{
"type": "imgupload",
"name": "图片上传",
"className": "form-tupian",
"icon": "icon-tupian",
"hideTitle": false,
"options": {
"defaultValue": [],
"size": {
"width": 100,
"height": 100
},
"width": "",
"tokenFunc": "funcGetToken",
"token": "",
"domain": "http://img.h5huodong.com",
"disabled": false,
"length": 9,
"multiple": true,
"hidden": false,
"hiddenOnAdd": false,
"required": false,
"fieldNote": "",
"autoWidth": 50
},
"key": "1717072996509_795340",
"model": "imgupload_1717072996509_795340",
"modelType": "main",
"rules": [],
"isSubItem": false
},
{
"type": "file-upload",
"name": "附件",
"className": "form-file-upload",
"icon": "icon-shangchuan",
"hideTitle": false,
"options": {
"defaultValue": [],
"token": "",
"length": 1,
"drag": false,
"multiple": false,
"disabled": false,
"buttonText": "添加附件",
"tokenFunc": "funcGetToken",
"hidden": false,
"hiddenOnAdd": false,
"required": false,
"fieldNote": "",
"autoWidth": 50
},
"key": "1717072932495_669325",
"model": "file_upload_1717072932495_669325",
"modelType": "main",
"rules": [],
"isSubItem": false
}
],
"options": {
"required": false,
"hiddenOnAdd": false,
"hidden": false,
"fieldNote": ""
},
"model": "card_1717072932495_382575",
"hideTitle": false,
"modelType": "main"
}
],
"config": {
"titleField": "input_1717072932495_556479",
"showHeaderTitle": true,
"labelWidth": 100,
"labelPosition": "top",
"size": "small",
"dialogOptions": {
"top": 20,
"width": 1000,
"padding": {
"top": 25,
"right": 25,
"bottom": 30,
"left": 25
}
},
"disabledAutoGrid": false,
"designMobileView": false,
"enableComment": true,
"hasWidgets": [
"input",
"number",
"card",
"money",
"select",
"imgupload",
"file-upload"
],
"expand": {
"js": "",
"css": "",
"url": {
"js": "",
"css": ""
}
},
"transactional": true,
"customRequestURL": [
{
"url": ""
}
],
"allowExternalLink": false,
"externalLinkShowData": false,
"headerImgUrl": "",
"externalTitle": "",
"enableNotice": false,
"noticeMode": "external",
"noticeType": "system",
"noticeReceiver": "",
"allowPrint": false,
"allowJmReport": false,
"jmReportURL": "",
"bizRuleConfig": [],
"bigDataMode": false
}
}

View File

@ -0,0 +1,496 @@
{
"list": [
{
"hideTitle": false,
"options": {
"hidden": false,
"hiddenOnAdd": false,
"fieldNote": "",
"required": false
},
"isContainer": true,
"model": "card_1717072902303_783177",
"modelType": "main",
"type": "card",
"isAutoGrid": true,
"list": [
{
"isSubItem": false,
"remoteAPI": {
"executed": false,
"url": ""
},
"icon": "icon-input",
"className": "form-input",
"rules": [
{
"required": true,
"message": "${title}必须填写"
}
],
"modelType": "main",
"type": "input",
"hideTitle": false,
"name": "名称",
"options": {
"clearable": false,
"hidden": false,
"defaultValue": "",
"pattern": "",
"fillRuleCode": "",
"fieldNote": "",
"required": true,
"readonly": false,
"unique": false,
"hiddenOnAdd": false,
"width": "100%",
"autoWidth": 100,
"showPassword": false,
"disabled": false,
"placeholder": ""
},
"model": "input_1717072902303_477529",
"advancedSetting": {
"defaultValue": {
"type": "compose",
"value": "",
"format": "string",
"allowFunc": true,
"valueSplit": "",
"customConfig": false
}
},
"key": "1717072902303_477529"
}
],
"key": "1717072902303_783177"
},
{
"options": {
"required": false,
"hiddenOnAdd": false,
"hidden": false,
"fieldNote": ""
},
"isContainer": true,
"model": "card_1717073019436_526262",
"type": "card",
"isAutoGrid": true,
"list": [
{
"isSubItem": false,
"remoteAPI": {
"executed": false,
"url": ""
},
"icon": "icon-number",
"className": "form-number",
"rules": [],
"modelType": "main",
"type": "number",
"hideTitle": false,
"name": "数字",
"options": {
"controls": false,
"showPercent": false,
"hidden": false,
"max": 100,
"defaultValue": 0,
"unitPosition": "suffix",
"fieldNote": "",
"maxUnlimited": true,
"align": "left",
"required": false,
"min": 0,
"minUnlimited": true,
"hiddenOnAdd": false,
"width": "",
"autoWidth": 50,
"step": 1,
"disabled": false,
"placeholder": "",
"controlsPosition": "right",
"unitText": ""
},
"model": "number_1717073019436_586474",
"advancedSetting": {
"defaultValue": {
"type": "compose",
"value": "",
"format": "number",
"allowFunc": true,
"valueSplit": "",
"customConfig": false
}
},
"key": "1717073019436_586474"
},
{
"isSubItem": false,
"remoteAPI": {
"executed": false,
"url": ""
},
"icon": "icon-money",
"className": "form-money",
"rules": [],
"modelType": "main",
"type": "money",
"hideTitle": false,
"name": "金额",
"options": {
"hidden": false,
"precision": 2,
"hiddenOnAdd": false,
"width": "180px",
"autoWidth": 50,
"unitPosition": "suffix",
"disabled": false,
"fieldNote": "",
"placeholder": "请输入金额",
"required": false,
"unitText": "元"
},
"model": "money_1717073021100_526660",
"advancedSetting": {
"defaultValue": {
"type": "compose",
"value": "",
"format": "number",
"allowFunc": true,
"valueSplit": "",
"customConfig": false
}
},
"key": "1717073021100_526660"
}
],
"key": "1717073019436_526262",
"hideTitle": false,
"modelType": "main"
},
{
"hideTitle": false,
"options": {
"hidden": false,
"hiddenOnAdd": false,
"fieldNote": "",
"required": false
},
"isContainer": true,
"model": "card_1717072902303_118977",
"modelType": "main",
"type": "card",
"isAutoGrid": true,
"list": [
{
"isSubItem": false,
"remoteAPI": {
"executed": false,
"url": ""
},
"icon": "icon-select",
"className": "form-select",
"rules": [],
"modelType": "main",
"type": "select",
"hideTitle": false,
"name": "下拉选择框",
"options": {
"remoteFunc": "",
"filterable": false,
"clearable": true,
"hidden": false,
"defaultValue": "",
"remoteOptions": [],
"multiple": false,
"fieldNote": "",
"remote": false,
"required": false,
"showLabel": false,
"useColor": false,
"props": {
"label": "label",
"value": "value"
},
"colorIteratorIndex": 3,
"hiddenOnAdd": false,
"width": "",
"options": [
{
"itemColor": "#2196F3",
"value": "下拉框1"
},
{
"itemColor": "#08C9C9",
"value": "下拉框2"
},
{
"itemColor": "#00C345",
"value": "下拉框3"
}
],
"autoWidth": 50,
"showType": "default",
"disabled": false,
"placeholder": ""
},
"model": "select_1717073033259_273399",
"advancedSetting": {
"defaultValue": {
"type": "compose",
"value": "",
"format": "string",
"allowFunc": true,
"valueSplit": ",",
"customConfig": true
}
},
"key": "1717073033259_273399"
},
{
"isSubItem": false,
"remoteAPI": {
"executed": false,
"url": ""
},
"icon": "icon-textarea",
"className": "form-textarea",
"rules": [],
"modelType": "main",
"type": "textarea",
"hideTitle": false,
"name": "描述",
"options": {
"readonly": false,
"hidden": false,
"defaultValue": "",
"unique": false,
"hiddenOnAdd": false,
"width": "100%",
"pattern": "",
"autoWidth": 50,
"disabled": false,
"fieldNote": "",
"placeholder": "",
"required": false
},
"model": "textarea_1717072902303_129466",
"advancedSetting": {
"defaultValue": {
"type": "compose",
"value": "",
"format": "string",
"allowFunc": true,
"valueSplit": "",
"customConfig": false
}
},
"key": "1717072902303_129466"
}
],
"key": "1717072902303_118977"
},
{
"hideTitle": false,
"options": {
"hidden": false,
"hiddenOnAdd": false,
"fieldNote": "",
"required": false
},
"isContainer": true,
"model": "card_1717072902304_736053",
"modelType": "main",
"type": "card",
"isAutoGrid": true,
"list": [
{
"hideTitle": false,
"isSubItem": false,
"name": "图片上传",
"icon": "icon-tupian",
"options": {
"hidden": false,
"defaultValue": [],
"length": 9,
"multiple": true,
"fieldNote": "",
"required": false,
"token": "",
"size": {
"width": 100,
"height": 100
},
"tokenFunc": "funcGetToken",
"domain": "http://img.h5huodong.com",
"hiddenOnAdd": false,
"width": "",
"autoWidth": 50,
"disabled": false
},
"className": "form-tupian",
"model": "imgupload_1717073025137_563739",
"rules": [],
"modelType": "main",
"type": "imgupload",
"key": "1717073025137_563739"
},
{
"hideTitle": false,
"isSubItem": false,
"name": "附件",
"icon": "icon-shangchuan",
"options": {
"buttonText": "添加附件",
"hidden": false,
"defaultValue": [],
"length": 1,
"multiple": false,
"fieldNote": "",
"required": false,
"token": "",
"tokenFunc": "funcGetToken",
"hiddenOnAdd": false,
"autoWidth": 50,
"disabled": false,
"drag": false
},
"className": "form-file-upload",
"model": "file_upload_1717072902304_442777",
"rules": [],
"modelType": "main",
"type": "file-upload",
"key": "1717072902304_442777"
}
],
"key": "1717072902304_736053"
},
{
"isSubItem": false,
"remoteAPI": {
"url": "",
"executed": false
},
"icon": "icon-link",
"className": "form-link-record",
"rules": [],
"modelType": "main",
"type": "link-record",
"hideTitle": false,
"name": "主表@表单控件",
"options": {
"sourceCode": "ai_control_main",
"showMode": "single",
"showType": "card",
"titleField": "wen_ben",
"showFields": [],
"allowView": true,
"allowEdit": true,
"allowAdd": true,
"allowSelect": true,
"buttonText": "添加记录",
"twoWayModel": "sub_table_design_1717137038626_791984",
"dataSelectAuth": "all",
"filters": [
{
"matchType": "AND",
"rules": []
}
],
"search": {
"enabled": false,
"field": "",
"rule": "like",
"afterShow": false,
"fields": []
},
"createMode": {
"add": true,
"select": false,
"params": {
"selectLinkModel": ""
}
},
"width": "100%",
"defaultValue": "",
"defaultValType": "none",
"required": false,
"disabled": false,
"hidden": false,
"hiddenOnAdd": false,
"fieldNote": ""
},
"model": "link_record_1717137044235_306956",
"advancedSetting": {
"defaultValue": {
"type": "compose",
"value": "",
"format": "string",
"allowFunc": true,
"valueSplit": "",
"customConfig": true
}
},
"key": "1717137044235_306956"
}
],
"config": {
"jmReportURL": "",
"enableComment": true,
"dialogOptions": {
"padding": {
"top": 25,
"left": 25,
"bottom": 30,
"right": 25
},
"top": 20,
"width": 1000
},
"allowJmReport": false,
"labelWidth": 100,
"headerImgUrl": "",
"noticeMode": "external",
"noticeReceiver": "",
"designMobileView": false,
"labelPosition": "top",
"allowPrint": false,
"enableNotice": false,
"bizRuleConfig": [],
"showHeaderTitle": true,
"bigDataMode": false,
"titleField": "input_1717072902303_477529",
"externalTitle": "",
"noticeType": "system",
"customRequestURL": [
{
"url": ""
}
],
"hasWidgets": [
"input",
"card",
"number",
"money",
"select",
"textarea",
"imgupload",
"file-upload",
"link-record"
],
"expand": {
"css": "",
"js": "",
"url": {
"css": "",
"js": ""
}
},
"size": "small",
"disabledAutoGrid": false,
"allowExternalLink": false,
"externalLinkShowData": false,
"transactional": true
}
}

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>jeecg-system-api</artifactId>
<groupId>org.jeecgframework.boot</groupId>
<version>3.7.1</version>
<version>3.7.4</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>jeecg-system-api</artifactId>
<groupId>org.jeecgframework.boot</groupId>
<version>3.7.1</version>
<version>3.7.4</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>jeecg-module-system</artifactId>
<groupId>org.jeecgframework.boot</groupId>
<version>3.7.1</version>
<version>3.7.4</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-module-system</artifactId>
<version>3.7.1</version>
<version>3.7.4</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@ -27,23 +27,22 @@
<!-- 企业微信/钉钉 api -->
<dependency>
<groupId>org.jeecgframework</groupId>
<artifactId>jeewx-api</artifactId>
<artifactId>weixin4j</artifactId>
</dependency>
<!-- 积木报表 -->
<dependency>
<groupId>org.jeecgframework.jimureport</groupId>
<artifactId>jimureport-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.jeecgframework.jimureport</groupId>
<artifactId>jimureport-nosql-starter</artifactId>
</dependency>
<!-- 积木BI -->
<dependency>
<groupId>org.jeecgframework.jimureport</groupId>
<artifactId>jimubi-spring-boot-starter</artifactId>
</dependency>
<!-- 积木报表 mongo redis 支持包
<dependency>
<groupId>org.jeecgframework.jimureport</groupId>
<artifactId>jimureport-nosql-starter</artifactId>
</dependency>-->
</dependencies>
</project>

View File

@ -26,8 +26,11 @@ public class CodeTemplateInitListener implements ApplicationListener<Application
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
try {
log.info(" Init Code Generate Template [ 检测如果是JAR启动环境Copy模板到config目录 ] ");
long startTime = System.currentTimeMillis(); // 记录开始时间
log.info(" Init Code Generate Template [ 检测如果是JAR启动Copy模板到config目录 ] ");
this.initJarConfigCodeGeneratorTemplate();
long endTime = System.currentTimeMillis(); // 记录结束时间
log.info(" Init Code Generate Template completed in " + (endTime - startTime) + " ms"); // 计算并记录耗时
} catch (Exception e) {
e.printStackTrace();
}

View File

@ -1,33 +1,33 @@
package org.jeecg.config.init;
import org.apache.catalina.Context;
import org.apache.tomcat.util.scan.StandardJarScanner;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Description: TomcatFactoryConfig
* @author: scott
* @date: 2021年01月25日 11:40
*/
@Configuration
public class TomcatFactoryConfig {
/**
* tomcat-embed-jasper引用后提示jar找不到的问题
*/
@Bean
public TomcatServletWebServerFactory tomcatFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory() {
@Override
protected void postProcessContext(Context context) {
((StandardJarScanner) context.getJarScanner()).setScanManifest(false);
}
};
factory.addConnectorCustomizers(connector -> {
connector.setProperty("relaxedPathChars", "[]{}");
connector.setProperty("relaxedQueryChars", "[]{}");
});
return factory;
}
}
//package org.jeecg.config.init;
//
//import org.apache.catalina.Context;
//import org.apache.tomcat.util.scan.StandardJarScanner;
//import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//
///**
// * @Description: TomcatFactoryConfig
// * @author: scott
// * @date: 2021年01月25日 11:40
// */
//@Configuration
//public class TomcatFactoryConfig {
// /**
// * tomcat-embed-jasper引用后提示jar找不到的问题
// */
// @Bean
// public TomcatServletWebServerFactory tomcatFactory() {
// TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory() {
// @Override
// protected void postProcessContext(Context context) {
// ((StandardJarScanner) context.getJarScanner()).setScanManifest(false);
// }
// };
// factory.addConnectorCustomizers(connector -> {
// connector.setProperty("relaxedPathChars", "[]{}");
// connector.setProperty("relaxedQueryChars", "[]{}");
// });
// return factory;
// }
//}

View File

@ -0,0 +1,29 @@
package org.jeecg.config.init;
import io.undertow.server.DefaultByteBufferPool;
import io.undertow.websockets.jsr.WebSocketDeploymentInfo;
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Configuration;
/**
* Undertow配置
*
* 解决启动提示: WARN io.undertow.websockets.jsr:68 - UT026010: Buffer pool was not set on WebSocketDeploymentInfo, the default pool will be used
*/
@Configuration
public class UndertowConfiguration implements WebServerFactoryCustomizer<UndertowServletWebServerFactory>{
@Override
public void customize(UndertowServletWebServerFactory factory) {
factory.addDeploymentInfoCustomizers(deploymentInfo -> {
WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();
// 设置合理的参数
webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(true, 8192));
deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo);
});
}
}

View File

@ -82,4 +82,30 @@ public class JimuReportTokenService implements JmReportTokenServiceI {
// 将所有信息存放至map 解析sql/api会根据map的键值解析
return map;
}
/**
* 将jeecgboot平台的权限传递给积木报表
* @param token
* @return
*/
@Override
public String[] getPermissions(String token) {
// 获取用户信息
String username = JwtUtil.getUsername(token);
SysUserCacheInfo userInfo = null;
try {
userInfo = sysBaseApi.getCacheUser(username);
} catch (Exception e) {
log.error("获取用户信息异常:"+ e.getMessage());
}
if(userInfo == null){
return null;
}
// 查询权限
Set<String> userPermissions = sysBaseApi.getUserPermissionSet(userInfo.getSysUserId());
if(CollectionUtils.isEmpty(userPermissions)){
return null;
}
return userPermissions.toArray(new String[0]);
}
}

View File

@ -5,12 +5,15 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.jeecg.common.api.dto.message.MessageDTO;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.enums.MessageTypeEnum;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.StaticConfig;
import org.jeecg.modules.message.entity.SysMessage;
import org.jeecg.modules.message.handle.ISendMsgHandle;
import org.jeecg.modules.message.mapper.SysMessageMapper;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.mapper.SysUserMapper;
import org.springframework.beans.factory.annotation.Autowired;
@ -43,6 +46,9 @@ public class EmailSendMsgHandle implements ISendMsgHandle {
@Autowired
private RedisUtil redisUtil;
@Autowired
private SysMessageMapper sysMessageMapper;
/**
* 真实姓名变量
@ -78,6 +84,23 @@ public class EmailSendMsgHandle implements ISendMsgHandle {
@Override
public void sendMessage(MessageDTO messageDTO) {
String content = messageDTO.getContent();
String title = messageDTO.getTitle();
//update-begin---author:wangshuai---date:2024-11-20---for:【QQYUN-8523】敲敲云发邮件通知不稳定---
boolean timeJobSendEmail = this.isTimeJobSendEmail(messageDTO.getToUser(), title, content);
if(timeJobSendEmail){
return;
}
//update-end---author:wangshuai---date:2024-11-20---for:【QQYUN-8523】敲敲云发邮件通知不稳定---
this.sendEmailMessage(messageDTO);
}
/**
* 直接发送邮件
*
* @param messageDTO
*/
public void sendEmailMessage(MessageDTO messageDTO) {
String[] arr = messageDTO.getToUser().split(",");
LambdaQueryWrapper<SysUser> query = new LambdaQueryWrapper<SysUser>().in(SysUser::getUsername, arr);
List<SysUser> list = sysUserMapper.selectList(query);
@ -213,4 +236,35 @@ public class EmailSendMsgHandle implements ISendMsgHandle {
redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME * 1 / 1000);
return token;
}
/**
* 是否定时发送邮箱
* @param toUser
* @param title
* @param content
* @return
*/
private boolean isTimeJobSendEmail(String toUser, String title, String content) {
StaticConfig staticConfig = SpringContextUtils.getBean(StaticConfig.class);
Boolean timeJobSend = staticConfig.getTimeJobSend();
if(null != timeJobSend && timeJobSend){
this.addSysSmsSend(toUser,title,content);
return true;
}
return false;
}
/**
* 保存到短信发送表
*/
private void addSysSmsSend(String toUser, String title, String content) {
SysMessage sysMessage = new SysMessage();
sysMessage.setEsTitle(title);
sysMessage.setEsContent(content);
sysMessage.setEsReceiver(toUser);
sysMessage.setEsSendStatus("0");
sysMessage.setEsSendNum(0);
sysMessage.setEsType(MessageTypeEnum.YJ.getType());
sysMessageMapper.insert(sysMessage);
}
}

View File

@ -51,6 +51,9 @@ public class SendMsgJob implements Job {
md.setToUser(sysMessage.getEsReceiver());
md.setType(sysMessage.getEsType());
md.setToAll(false);
//update-begin---author:wangshuai---date:2024-11-12---for:【QQYUN-8523】敲敲云发邮件通知不稳定---
md.setIsTimeJob(true);
//update-end---author:wangshuai---date:2024-11-12---for:【QQYUN-8523】敲敲云发邮件通知不稳定---
sysBaseAPI.sendTemplateMessage(md);
//发送消息成功
sysMessage.setEsSendStatus(SendMsgStatusEnum.SUCCESS.getCode());

View File

@ -18,7 +18,7 @@ import static org.springframework.boot.actuate.endpoint.annotation.Selector.Matc
* @Date: 2024/5/13 17:02
*/
@Component
@Endpoint(id = "httptrace-new")
@Endpoint(id = "jeecghttptrace")
public class CustomHttpTraceEndpoint{
private final CustomInMemoryHttpTraceRepository repository;

View File

@ -0,0 +1,112 @@
package org.jeecg.modules.openapi.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.modules.openapi.entity.OpenApiAuth;
import org.jeecg.modules.openapi.generator.AKSKGenerator;
import org.jeecg.modules.openapi.service.OpenApiAuthService;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
/**
* @date 2024/12/10 9:54
*/
@RestController
@RequestMapping("/openapi/auth")
public class OpenApiAuthController extends JeecgController<OpenApiAuth, OpenApiAuthService> {
/**
* 分页列表查询
*
* @param openApiAuth
* @param pageNo
* @param pageSize
* @param req
* @return
*/
@GetMapping(value = "/list")
public Result<?> queryPageList(OpenApiAuth openApiAuth, @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize, HttpServletRequest req) {
QueryWrapper<OpenApiAuth> queryWrapper = QueryGenerator.initQueryWrapper(openApiAuth, req.getParameterMap());
Page<OpenApiAuth> page = new Page<>(pageNo, pageSize);
IPage<OpenApiAuth> pageList = service.page(page, queryWrapper);
return Result.ok(pageList);
}
/**
* 添加
*
* @param openApiAuth
* @return
*/
@PostMapping(value = "/add")
public Result<?> add(@RequestBody OpenApiAuth openApiAuth) {
service.save(openApiAuth);
return Result.ok("添加成功!");
}
/**
* 编辑
*
* @param openApiAuth
* @return
*/
@PutMapping(value = "/edit")
public Result<?> edit(@RequestBody OpenApiAuth openApiAuth) {
service.updateById(openApiAuth);
return Result.ok("修改成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@DeleteMapping(value = "/delete")
public Result<?> delete(@RequestParam(name = "id", required = true) String id) {
service.removeById(id);
return Result.ok("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@DeleteMapping(value = "/deleteBatch")
public Result<?> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
this.service.removeByIds(Arrays.asList(ids.split(",")));
return Result.ok("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
@GetMapping(value = "/queryById")
public Result<?> queryById(@RequestParam(name = "id", required = true) String id) {
OpenApiAuth openApiAuth = service.getById(id);
return Result.ok(openApiAuth);
}
/**
* 生成AKSK
* @return
*/
@GetMapping("genAKSK")
public Result<String[]> genAKSK() {
return Result.ok(AKSKGenerator.genAKSKPair());
}
}

View File

@ -0,0 +1,386 @@
package org.jeecg.modules.openapi.controller;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.modules.openapi.entity.OpenApi;
import org.jeecg.modules.openapi.entity.OpenApiAuth;
import org.jeecg.modules.openapi.entity.OpenApiHeader;
import org.jeecg.modules.openapi.entity.OpenApiParam;
import org.jeecg.modules.openapi.generator.PathGenerator;
import org.jeecg.modules.openapi.service.OpenApiAuthService;
import org.jeecg.modules.openapi.service.OpenApiHeaderService;
import org.jeecg.modules.openapi.service.OpenApiParamService;
import org.jeecg.modules.openapi.service.OpenApiService;
import org.jeecg.modules.openapi.swagger.*;
import org.jeecg.modules.system.entity.SysUser;
import org.jeecg.modules.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
/**
* @date 2024/12/10 9:11
*/
@RestController
@RequestMapping("/openapi")
public class OpenApiController extends JeecgController<OpenApi, OpenApiService> {
@Autowired
private RestTemplate restTemplate;
@Autowired
private OpenApiParamService openApiParamService;
@Autowired
private OpenApiHeaderService openApiHeaderService;
@Autowired
private ISysUserService sysUserService;
@Autowired
private OpenApiAuthService openApiAuthService;
/**
* 分页列表查询
*
* @param openApi
* @param pageNo
* @param pageSize
* @param req
* @return
*/
@GetMapping(value = "/list")
public Result<?> queryPageList(OpenApi openApi, @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize, HttpServletRequest req) {
QueryWrapper<OpenApi> queryWrapper = QueryGenerator.initQueryWrapper(openApi, req.getParameterMap());
Page<OpenApi> page = new Page<>(pageNo, pageSize);
IPage<OpenApi> pageList = service.page(page, queryWrapper);
for (OpenApi api : pageList.getRecords()) {
api.setParams(openApiParamService.findByApiId(api.getId()));
api.setHeaders(openApiHeaderService.findByApiId(api.getId()));
}
return Result.ok(pageList);
}
/**
* 添加
*
* @param openApi
* @return
*/
@PostMapping(value = "/add")
public Result<?> add(@RequestBody OpenApi openApi) {
if (service.save(openApi)) {
if (!CollectionUtils.isEmpty(openApi.getHeaders())) {
openApiHeaderService.saveBatch(openApi.getHeaders());
}
if (!CollectionUtils.isEmpty(openApi.getParams())) {
openApiParamService.saveBatch(openApi.getParams());
}
}
return Result.ok("添加成功!");
}
/**
* 编辑
*
* @param openApi
* @return
*/
@PutMapping(value = "/edit")
public Result<?> edit(@RequestBody OpenApi openApi) {
if (service.updateById(openApi)) {
openApiHeaderService.deleteByApiId(openApi.getId());
openApiParamService.deleteByApiId(openApi.getId());
if (!CollectionUtils.isEmpty(openApi.getHeaders())) {
openApiHeaderService.saveBatch(openApi.getHeaders());
}
if (!CollectionUtils.isEmpty(openApi.getParams())) {
openApiParamService.saveBatch(openApi.getParams());
}
}
return Result.ok("修改成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@DeleteMapping(value = "/delete")
public Result<?> delete(@RequestParam(name = "id", required = true) String id) {
service.removeById(id);
return Result.ok("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@DeleteMapping(value = "/deleteBatch")
public Result<?> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
this.service.removeByIds(Arrays.asList(ids.split(",")));
return Result.ok("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
@GetMapping(value = "/queryById")
public Result<?> queryById(@RequestParam(name = "id", required = true) String id) {
OpenApi OpenApi = service.getById(id);
return Result.ok(OpenApi);
}
/**
* 接口调用
* @param path
* @return
*/
@RequestMapping(value = "/call/{path}", method = {RequestMethod.GET,RequestMethod.POST})
public Result<?> call(@PathVariable String path, @RequestBody(required = false) String json, HttpServletRequest request) {
OpenApi openApi = service.findByPath(path);
if (Objects.isNull(openApi)) {
Map<String, Object> result = new HashMap<>();
result.put("code", 404);
result.put("data", null);
return Result.error("失败", result);
}
List<OpenApiHeader> headers = openApiHeaderService.findByApiId(openApi.getId());
String url = openApi.getOriginUrl();
String method = openApi.getRequestMethod();
HttpHeaders httpHeaders = new HttpHeaders();
for (OpenApiHeader header : headers) {
httpHeaders.put(header.getHeaderKey(), Lists.newArrayList(request.getHeader(header.getHeaderKey())));
}
String appkey = request.getHeader("appkey");
OpenApiAuth openApiAuth = openApiAuthService.getByAppkey(appkey);
SysUser systemUser = sysUserService.getById(openApiAuth.getSystemUserId());
String token = JwtUtil.sign(systemUser.getUsername(), systemUser.getPassword());
httpHeaders.put("X-Access-Token", Lists.newArrayList(token));
HttpEntity<String> httpEntity = new HttpEntity<>(json, httpHeaders);
return restTemplate.exchange(url, HttpMethod.resolve(method), httpEntity, Result.class, request.getParameterMap()).getBody();
}
@GetMapping("/json")
public SwaggerModel swaggerModel() {
SwaggerModel swaggerModel = new SwaggerModel();
swaggerModel.setSwagger("2.0");
swaggerModel.setInfo(swaggerInfo());
swaggerModel.setHost("jeecg.com");
swaggerModel.setBasePath("/jeecg-boot");
swaggerModel.setSchemes(Lists.newArrayList("http", "https"));
SwaggerTag swaggerTag = new SwaggerTag();
swaggerTag.setName("openapi");
swaggerModel.setTags(Lists.newArrayList(swaggerTag));
pathsAndDefinitions(swaggerModel);
return swaggerModel;
}
private void pathsAndDefinitions(SwaggerModel swaggerModel) {
Map<String, Map<String, SwaggerOperation>> paths = new HashMap<>();
Map<String, SwaggerDefinition> definitions = new HashMap<>();
List<OpenApi> openapis = service.list();
for (OpenApi openApi : openapis) {
Map<String, SwaggerOperation> operations = new HashMap<>();
SwaggerOperation operation = new SwaggerOperation();
operation.setTags(Lists.newArrayList("openapi"));
operation.setSummary(openApi.getName());
operation.setDescription(openApi.getName());
operation.setOperationId(openApi.getRequestUrl()+"Using"+openApi.getRequestMethod());
operation.setProduces(Lists.newArrayList("application/json"));
parameters(operation, openApi);
// body入参
if (StringUtils.hasText(openApi.getBody())) {
SwaggerDefinition definition = new SwaggerDefinition();
definition.setType("object");
Map<String, SwaggerDefinitionProperties> definitionProperties = new HashMap<>();
definition.setProperties(definitionProperties);
JSONObject jsonObject = JSONObject.parseObject(openApi.getBody());
for (Map.Entry<String, Object> properties : jsonObject.entrySet()) {
SwaggerDefinitionProperties swaggerDefinitionProperties = new SwaggerDefinitionProperties();
swaggerDefinitionProperties.setType("string");
swaggerDefinitionProperties.setDescription(properties.getValue()+"");
definitionProperties.put(properties.getKey(), swaggerDefinitionProperties);
}
// body的definition构建完成
definitions.put(openApi.getRequestUrl()+"Using"+openApi.getRequestMethod()+"body", definition);
SwaggerOperationParameter bodyParameter = new SwaggerOperationParameter();
bodyParameter.setDescription(openApi.getName() + " body");
bodyParameter.setIn("body");
bodyParameter.setName(openApi.getName() + " body");
bodyParameter.setRequired(true);
Map<String, String> bodySchema = new HashMap<>();
bodySchema.put("$ref", "#/definitions/" + openApi.getRequestUrl()+"Using"+openApi.getRequestMethod()+"body");
bodyParameter.setSchema(bodySchema);
// 构建参数构建完成
operation.getParameters().add(bodyParameter);
}
// 响应
Map<String, SwaggerOperationResponse> responses = new HashMap<>();
SwaggerOperationResponse resp200 = new SwaggerOperationResponse();
resp200.setDescription("OK");
Map<String, String> respSchema = new HashMap<>();
respSchema.put("$ref", "#/definitions/OpenApiResult");
resp200.setSchema(respSchema);
responses.put("200", resp200);
Map<String, String> emptySchema = new HashMap<>();
SwaggerOperationResponse resp201 = new SwaggerOperationResponse();
resp201.setDescription("Created");
resp201.setSchema(emptySchema);
responses.put("201", resp201);
SwaggerOperationResponse resp401 = new SwaggerOperationResponse();
resp401.setDescription("Unauthorized");
resp401.setSchema(emptySchema);
responses.put("401", resp401);
SwaggerOperationResponse resp403 = new SwaggerOperationResponse();
resp403.setDescription("Forbidden");
resp403.setSchema(emptySchema);
responses.put("403", resp403);
SwaggerOperationResponse resp404 = new SwaggerOperationResponse();
resp404.setDescription("Not Found");
resp404.setSchema(emptySchema);
responses.put("404", resp404);
// 构建响应definition
SwaggerDefinition respDefinition = new SwaggerDefinition();
respDefinition.setType("object");
Map<String, SwaggerDefinitionProperties> definitionProperties = new HashMap<>();
respDefinition.setProperties(definitionProperties);
SwaggerDefinitionProperties codeProperties = new SwaggerDefinitionProperties();
codeProperties.setType("integer");
codeProperties.setDescription("返回代码");
definitionProperties.put("code", codeProperties);
SwaggerDefinitionProperties messageProperties = new SwaggerDefinitionProperties();
messageProperties.setType("string");
messageProperties.setDescription("返回处理消息");
definitionProperties.put("message", messageProperties);
SwaggerDefinitionProperties resultProperties = new SwaggerDefinitionProperties();
resultProperties.setType("object");
resultProperties.setDescription("返回数据对象");
definitionProperties.put("result", resultProperties);
SwaggerDefinitionProperties successProperties = new SwaggerDefinitionProperties();
successProperties.setType("boolean");
successProperties.setDescription("成功标志");
definitionProperties.put("success", successProperties);
SwaggerDefinitionProperties timestampProperties = new SwaggerDefinitionProperties();
timestampProperties.setType("integer");
timestampProperties.setDescription("时间戳");
definitionProperties.put("timestamp", timestampProperties);
definitions.put("OpenApiResult", respDefinition);
operation.setResponses(responses);
operations.put(openApi.getRequestMethod().toLowerCase(), operation);
paths.put("/openapi/call/"+openApi.getRequestUrl(), operations);
}
swaggerModel.setDefinitions(definitions);
swaggerModel.setPaths(paths);
}
private void parameters(SwaggerOperation operation, OpenApi openApi) {
List<SwaggerOperationParameter> parameters = new ArrayList<>();
for (OpenApiParam openApiParam : openApiParamService.findByApiId(openApi.getId())) {
SwaggerOperationParameter parameter = new SwaggerOperationParameter();
parameter.setIn("path");
parameter.setName(openApiParam.getParamKey());
parameter.setRequired(openApiParam.getRequired() == 1);
parameter.setDescription(openApiParam.getNote());
parameters.add(parameter);
}
for (OpenApiHeader openApiHeader : openApiHeaderService.findByApiId(openApi.getId())) {
SwaggerOperationParameter parameter = new SwaggerOperationParameter();
parameter.setIn("header");
parameter.setName(openApiHeader.getHeaderKey());
parameter.setRequired(openApiHeader.getRequired() == 1);
parameter.setDescription(openApiHeader.getNote());
parameters.add(parameter);
}
operation.setParameters(parameters);
}
private SwaggerInfo swaggerInfo() {
SwaggerInfo info = new SwaggerInfo();
info.setDescription("OpenAPI 接口列表");
info.setVersion("3.7.1");
info.setTitle("OpenAPI 接口列表");
info.setTermsOfService("https://jeecg.com");
SwaggerInfoContact contact = new SwaggerInfoContact();
contact.setName("jeecg@qq.com");
info.setContact(contact);
SwaggerInfoLicense license = new SwaggerInfoLicense();
license.setName("Apache 2.0");
license.setUrl("http://www.apache.org/licenses/LICENSE-2.0.html");
info.setLicense(license);
return info;
}
/**
* 生成接口路径
* @return
*/
@GetMapping("genPath")
public Result<String> genPath() {
Result<String> r = new Result<String>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
r.setResult(PathGenerator.genPath());
return r;
}
}

View File

@ -0,0 +1,26 @@
package org.jeecg.modules.openapi.controller;
import org.jeecg.common.api.vo.Result;
import org.jeecg.config.shiro.IgnoreAuth;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
/**
* @date 2024/12/20 14:04
*/
@RestController
@RequestMapping("/openapi/demo")
public class OpenApiIndexController {
@GetMapping("index")
@IgnoreAuth
public Result<Map<String, String>> index() {
Map<String, String> result = new HashMap<>();
result.put("first", "Hello World");
return Result.ok(result);
}
}

View File

@ -0,0 +1,20 @@
package org.jeecg.modules.openapi.controller;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.modules.openapi.entity.OpenApiPermission;
import org.jeecg.modules.openapi.service.OpenApiPermissionService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/openapi/permission")
public class OpenApiPermissionController extends JeecgController<OpenApiPermission, OpenApiPermissionService> {
@PostMapping("add")
public Result add(@RequestBody OpenApiPermission openApiPermission) {
return Result.ok(service.save(openApiPermission));
}
}

View File

@ -0,0 +1,102 @@
package org.jeecg.modules.openapi.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.modules.openapi.entity.OpenApiRecord;
import org.jeecg.modules.openapi.service.OpenApiRecordService;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
/**
* @date 2024/12/10 9:57
*/
@RestController
@RequestMapping("/openapi/record")
public class OpenApiRecordController extends JeecgController<OpenApiRecord, OpenApiRecordService> {
/**
* 分页列表查询
*
* @param openApiRecord
* @param pageNo
* @param pageSize
* @param req
* @return
*/
@GetMapping(value = "/list")
public Result<?> queryPageList(OpenApiRecord openApiRecord, @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize, HttpServletRequest req) {
QueryWrapper<OpenApiRecord> queryWrapper = QueryGenerator.initQueryWrapper(openApiRecord, req.getParameterMap());
Page<OpenApiRecord> page = new Page<>(pageNo, pageSize);
IPage<OpenApiRecord> pageList = service.page(page, queryWrapper);
return Result.ok(pageList);
}
/**
* 添加
*
* @param openApiRecord
* @return
*/
@PostMapping(value = "/add")
public Result<?> add(@RequestBody OpenApiRecord openApiRecord) {
service.save(openApiRecord);
return Result.ok("添加成功!");
}
/**
* 编辑
*
* @param openApiRecord
* @return
*/
@PutMapping(value = "/edit")
public Result<?> edit(@RequestBody OpenApiRecord openApiRecord) {
service.updateById(openApiRecord);
return Result.ok("修改成功!");
}
/**
* 通过id删除
*
* @param id
* @return
*/
@DeleteMapping(value = "/delete")
public Result<?> delete(@RequestParam(name = "id", required = true) String id) {
service.removeById(id);
return Result.ok("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@DeleteMapping(value = "/deleteBatch")
public Result<?> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
this.service.removeByIds(Arrays.asList(ids.split(",")));
return Result.ok("批量删除成功!");
}
/**
* 通过id查询
*
* @param id
* @return
*/
@GetMapping(value = "/queryById")
public Result<?> queryById(@RequestParam(name = "id", required = true) String id) {
OpenApiRecord openApiRecord = service.getById(id);
return Result.ok(openApiRecord);
}
}

View File

@ -0,0 +1,103 @@
package org.jeecg.modules.openapi.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* 接口表
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class OpenApi implements Serializable {
private static final long serialVersionUID = 1L;
/**
* id
*/
@TableId(type = IdType.ASSIGN_ID)
private String id;
/**
* 接口名称
*/
private String name;
/**
* 请求方式如POST、GET
*/
private String requestMethod;
/**
* 对外开放的相对接口路径
*/
private String requestUrl;
/**
* IP 黑名单
*/
private String blackList;
/**
* 请求头列表
*/
@TableField(exist = false)
private List<OpenApiHeader> headers;
/**
* 请求参数列表
*/
@TableField(exist = false)
private List<OpenApiParam> params;
/**
* 目前仅支持json
*/
private String body;
/**
* 原始接口路径
*/
private String originUrl;
/**
* 状态(1正常 2废弃
*/
private Integer status;
/**
* 删除状态0正常1已删除
*/
@TableLogic
private Integer delFlag;
/**
* 创建人
*/
private String createBy;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新人
*/
private String updateBy;
/**
* 更新时间
*/
private Date updateTime;
}

View File

@ -0,0 +1,68 @@
package org.jeecg.modules.openapi.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
* 权限表
* @date 2024/12/10 9:38
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class OpenApiAuth implements Serializable {
private static final long serialVersionUID = -5933153354153738498L;
/**
* id
*/
@TableId(type = IdType.ASSIGN_ID)
private String id;
/**
* 受权名称
*/
private String name;
/**
* access key
*/
private String ak;
/**
* secret key
*/
private String sk;
/**
* 系统用户ID
*/
private String systemUserId;
/**
* 创建人
*/
private String createBy;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新人
*/
private String updateBy;
/**
* 更新时间
*/
private Date updateTime;
}

View File

@ -0,0 +1,51 @@
package org.jeecg.modules.openapi.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* 请求头表
* @date 2024/12/10 14:37
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class OpenApiHeader implements Serializable {
private static final long serialVersionUID = 5032708503120184683L;
/**
* id
*/
@TableId(type = IdType.ASSIGN_ID)
private String id;
/**
* 接口ID
*/
private String apiId;
/**
* key
*/
private String headerKey;
/**
* 是否必填(0:否1是)
*/
private Integer required;
/**
* 默认值
*/
private String defaultValue;
/**
* 说明
*/
private String note;
}

View File

@ -0,0 +1,50 @@
package org.jeecg.modules.openapi.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* query部分参数表
* @date 2024/12/10 14:37
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class OpenApiParam implements Serializable {
private static final long serialVersionUID = -6174831468578022357L;
/**
* id
*/
@TableId(type = IdType.ASSIGN_ID)
private String id;
/**
* 接口ID
*/
private String apiId;
/**
* key
*/
private String paramKey;
/**
* 是否必填(0:否1是)
*/
private Integer required;
/**
* 默认值
*/
private String defaultValue;
/**
* 说明
*/
private String note;
}

View File

@ -0,0 +1,55 @@
package org.jeecg.modules.openapi.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
*
* @date 2024/12/19 17:41
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class OpenApiPermission implements Serializable {
/**
* id
*/
@TableId(type = IdType.ASSIGN_ID)
private String id;
/**
* 接口ID
*/
private String apiId;
/**
* 认证ID
*/
private String apiAuthId;
/**
* 创建人
*/
private String createBy;
/**
* 创建时间
*/
private Date createTime;
/**
* 更新人
*/
private String updateBy;
/**
* 更新时间
*/
private Date updateTime;
}

View File

@ -0,0 +1,52 @@
package org.jeecg.modules.openapi.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
* 调用记录表
* @date 2024/12/10 9:41
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class OpenApiRecord implements Serializable {
private static final long serialVersionUID = -5870384488947863579L;
/**
* id
*/
@TableId(type = IdType.ASSIGN_ID)
private String id;
/**
* 接口ID
*/
private String apiId;
/**
* 调用ID
*/
private String callAuthId;
/**
* 调用时间
*/
private Date callTime;
/**
* 耗时
*/
private Long usedTime;
/**
* 响应时间
*/
private Date responseTime;
}

View File

@ -0,0 +1,204 @@
package org.jeecg.modules.openapi.filter;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.modules.openapi.entity.OpenApi;
import org.jeecg.modules.openapi.entity.OpenApiAuth;
import org.jeecg.modules.openapi.entity.OpenApiPermission;
import org.jeecg.modules.openapi.entity.OpenApiRecord;
import org.jeecg.modules.openapi.service.OpenApiAuthService;
import org.jeecg.modules.openapi.service.OpenApiPermissionService;
import org.jeecg.modules.openapi.service.OpenApiRecordService;
import org.jeecg.modules.openapi.service.OpenApiService;
import org.springframework.util.StringUtils;
import org.springframework.web.context.WebApplicationContext;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
/**
* @date 2024/12/19 16:55
*/
@Slf4j
public class ApiAuthFilter implements Filter {
private OpenApiRecordService openApiRecordService;
private OpenApiAuthService openApiAuthService;
private OpenApiPermissionService openApiPermissionService;
private OpenApiService openApiService;
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
long startTime = System.currentTimeMillis();
Date callTime = new Date();
HttpServletRequest request = (HttpServletRequest)servletRequest;
String ip = request.getRemoteAddr();
String appkey = request.getHeader("appkey");
String signature = request.getHeader("signature");
String timestamp = request.getHeader("timestamp");
OpenApi openApi = findOpenApi(request);
// IP 黑名单核验
checkBlackList(openApi, ip);
// 签名核验
checkSignValid(appkey, signature, timestamp);
OpenApiAuth openApiAuth = openApiAuthService.getByAppkey(appkey);
// 认证信息核验
checkSignature(appkey, signature, timestamp, openApiAuth);
// 业务核验
checkPermission(openApi, openApiAuth);
filterChain.doFilter(servletRequest, servletResponse);
long endTime = System.currentTimeMillis();
OpenApiRecord record = new OpenApiRecord();
record.setApiId(openApi.getId());
record.setCallAuthId(openApiAuth.getId());
record.setCallTime(callTime);
record.setUsedTime(endTime - startTime);
record.setResponseTime(new Date());
openApiRecordService.save(record);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
ServletContext servletContext = filterConfig.getServletContext();
WebApplicationContext applicationContext = (WebApplicationContext)servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
this.openApiService = applicationContext.getBean(OpenApiService.class);
this.openApiRecordService = applicationContext.getBean(OpenApiRecordService.class);
this.openApiAuthService = applicationContext.getBean(OpenApiAuthService.class);
this.openApiPermissionService = applicationContext.getBean(OpenApiPermissionService.class);
}
/**
* IP 黑名单核验
* @param openApi
* @param ip
*/
protected void checkBlackList(OpenApi openApi, String ip) {
if (!StringUtils.hasText(openApi.getBlackList())) {
return;
}
List<String> blackList = Arrays.asList(openApi.getBlackList().split(","));
if (blackList.contains(ip)) {
throw new JeecgBootException("目标接口限制IP[" + ip + "]进行访问IP已记录请停止访问");
}
}
/**
* 签名验证
* @param appkey
* @param signature
* @param timestamp
* @return
*/
protected void checkSignValid(String appkey, String signature, String timestamp) {
if (!StringUtils.hasText(appkey)) {
throw new JeecgBootException("appkey为空");
}
if (!StringUtils.hasText(signature)) {
throw new JeecgBootException("signature为空");
}
if (!StringUtils.hasText(timestamp)) {
throw new JeecgBootException("timastamp时间戳为空");
}
if (!timestamp.matches("[0-9]*")) {
throw new JeecgBootException("timastamp时间戳不合法");
}
if (System.currentTimeMillis() - Long.parseLong(timestamp) > 5 * 60 * 1000) {
throw new JeecgBootException("signature签名已过期(超过五分钟)");
}
}
/**
* 认证信息核验
* @param appKey
* @param signature
* @param timestamp
* @param openApiAuth
* @return
* @throws Exception
*/
protected void checkSignature(String appKey, String signature, String timestamp, OpenApiAuth openApiAuth) {
if(openApiAuth==null){
throw new JeecgBootException("不存在认证信息");
}
if(!appKey.equals(openApiAuth.getAk())){
throw new JeecgBootException("appkey错误");
}
if (!signature.equals(md5(appKey + openApiAuth.getSk() + timestamp))) {
throw new JeecgBootException("signature签名错误");
}
}
protected void checkPermission(OpenApi openApi, OpenApiAuth openApiAuth) {
List<OpenApiPermission> permissionList = openApiPermissionService.findByAuthId(openApiAuth.getId());
boolean hasPermission = false;
for (OpenApiPermission permission : permissionList) {
if (permission.getApiId().equals(openApi.getId())) {
hasPermission = true;
break;
}
}
if (!hasPermission) {
throw new JeecgBootException("该appKey未授权当前接口");
}
}
/**
* @return String 返回类型
* @Title: MD5
* @Description: 【MD5加密】
*/
protected static String md5(String sourceStr) {
String result = "";
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(sourceStr.getBytes("utf-8"));
byte[] hash = md.digest();
int i;
StringBuffer buf = new StringBuffer(32);
for (int offset = 0; offset < hash.length; offset++) {
i = hash[offset];
if (i < 0) {
i += 256;
}
if (i < 16) {
buf.append("0");
}
buf.append(Integer.toHexString(i));
}
result = buf.toString();
} catch (Exception e) {
log.error("sign签名错误", e);
}
return result;
}
protected OpenApi findOpenApi(HttpServletRequest request) {
String uri = request.getRequestURI();
String path = uri.substring(uri.lastIndexOf("/") + 1);
return openApiService.findByPath(path);
}
public static void main(String[] args) {
long timestamp = System.currentTimeMillis();
System.out.println("timestamp:" + timestamp);
System.out.println("signature:" + md5("ak-eAU25mrMxhtaZsyS" + "rjxMqB6YyUXpSHAz4DCIz8vZ5aozQQiV" + timestamp));
}
}

View File

@ -0,0 +1,25 @@
package org.jeecg.modules.openapi.filter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @date 2024/12/19 17:09
*/
@Configuration
public class ApiFilterConfig {
/**
*
* @Description: 【注册api加密过滤器】
*/
@Bean
public FilterRegistrationBean<ApiAuthFilter> authFilter() {
FilterRegistrationBean<ApiAuthFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new ApiAuthFilter());
registration.setName("apiAuthFilter");
registration.addUrlPatterns("/openapi/call/*");
return registration;
}
}

View File

@ -0,0 +1,36 @@
package org.jeecg.modules.openapi.generator;
import java.security.SecureRandom;
/**
* AK/SK生成器
*/
public class AKSKGenerator {
private static final String CHAR_POOL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
private static final int AK_LENGTH = 16; // Adjust as per requirements
private static final int SK_LENGTH = 32;
public static String[] genAKSKPair() {
return new String[]{genAK(), genSK()};
}
public static String genAK() {
return "ak-" + generateRandomString(AK_LENGTH);
}
public static String genSK() {
return generateRandomString(SK_LENGTH);
}
private static String generateRandomString(int length) {
SecureRandom random = new SecureRandom();
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++) {
sb.append(CHAR_POOL.charAt(random.nextInt(CHAR_POOL.length())));
}
return sb.toString();
}
}

View File

@ -0,0 +1,28 @@
package org.jeecg.modules.openapi.generator;
import lombok.experimental.UtilityClass;
import java.util.Random;
/**
* @date 2024/12/10 10:00
*/
@UtilityClass
public class PathGenerator {
// Base62字符集
private static final String BASE62 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
/**
* 生成随机路径
* @return
*/
public static String genPath() {
StringBuilder result = new StringBuilder();
Random random = new Random();
for (int i=0; i<8; i++) {
result.append(BASE62.charAt(random.nextInt(62)));
}
return result.toString();
}
}

View File

@ -0,0 +1,12 @@
package org.jeecg.modules.openapi.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.jeecg.modules.openapi.entity.OpenApiAuth;
/**
* @date 2024/12/10 9:49
*/
@Mapper
public interface OpenApiAuthMapper extends BaseMapper<OpenApiAuth> {
}

View File

@ -0,0 +1,12 @@
package org.jeecg.modules.openapi.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.jeecg.modules.openapi.entity.OpenApiHeader;
/**
* @date 2024/12/10 14:47
*/
@Mapper
public interface OpenApiHeaderMapper extends BaseMapper<OpenApiHeader> {
}

View File

@ -0,0 +1,9 @@
package org.jeecg.modules.openapi.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.jeecg.modules.openapi.entity.OpenApi;
@Mapper
public interface OpenApiMapper extends BaseMapper<OpenApi> {
}

View File

@ -0,0 +1,12 @@
package org.jeecg.modules.openapi.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.jeecg.modules.openapi.entity.OpenApiParam;
/**
* @date 2024/12/10 14:48
*/
@Mapper
public interface OpenApiParamMapper extends BaseMapper<OpenApiParam> {
}

View File

@ -0,0 +1,12 @@
package org.jeecg.modules.openapi.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.jeecg.modules.openapi.entity.OpenApiPermission;
/**
* @date 2024/12/19 17:43
*/
@Mapper
public interface OpenApiPermissionMapper extends BaseMapper<OpenApiPermission> {
}

View File

@ -0,0 +1,12 @@
package org.jeecg.modules.openapi.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.jeecg.modules.openapi.entity.OpenApiRecord;
/**
* @date 2024/12/10 9:50
*/
@Mapper
public interface OpenApiRecordMapper extends BaseMapper<OpenApiRecord> {
}

View File

@ -0,0 +1,11 @@
package org.jeecg.modules.openapi.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.openapi.entity.OpenApiAuth;
/**
* @date 2024/12/10 9:50
*/
public interface OpenApiAuthService extends IService<OpenApiAuth> {
OpenApiAuth getByAppkey(String appkey);
}

View File

@ -0,0 +1,16 @@
package org.jeecg.modules.openapi.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.openapi.entity.OpenApiHeader;
import java.util.List;
/**
* @date 2024/12/10 14:48
*/
public interface OpenApiHeaderService extends IService<OpenApiHeader> {
boolean deleteByApiId(String apiId);
List<OpenApiHeader> findByApiId(String apiId);
}

View File

@ -0,0 +1,15 @@
package org.jeecg.modules.openapi.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.openapi.entity.OpenApiParam;
import java.util.List;
/**
* @date 2024/12/10 14:49
*/
public interface OpenApiParamService extends IService<OpenApiParam> {
boolean deleteByApiId(String apiId);
List<OpenApiParam> findByApiId(String apiId);
}

View File

@ -0,0 +1,13 @@
package org.jeecg.modules.openapi.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.openapi.entity.OpenApiPermission;
import java.util.List;
/**
* @date 2024/12/19 17:44
*/
public interface OpenApiPermissionService extends IService<OpenApiPermission> {
List<OpenApiPermission> findByAuthId(String authId);
}

View File

@ -0,0 +1,10 @@
package org.jeecg.modules.openapi.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.openapi.entity.OpenApiRecord;
/**
* @date 2024/12/10 9:51
*/
public interface OpenApiRecordService extends IService<OpenApiRecord> {
}

View File

@ -0,0 +1,8 @@
package org.jeecg.modules.openapi.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.modules.openapi.entity.OpenApi;
public interface OpenApiService extends IService<OpenApi> {
OpenApi findByPath(String path);
}

View File

@ -0,0 +1,19 @@
package org.jeecg.modules.openapi.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jeecg.modules.openapi.entity.OpenApiAuth;
import org.jeecg.modules.openapi.mapper.OpenApiAuthMapper;
import org.jeecg.modules.openapi.service.OpenApiAuthService;
import org.springframework.stereotype.Service;
/**
* @date 2024/12/10 9:51
*/
@Service
public class OpenApiAuthServiceImpl extends ServiceImpl<OpenApiAuthMapper, OpenApiAuth> implements OpenApiAuthService {
@Override
public OpenApiAuth getByAppkey(String appkey) {
return baseMapper.selectOne(Wrappers.lambdaUpdate(OpenApiAuth.class).eq(OpenApiAuth::getAk, appkey), false);
}
}

View File

@ -0,0 +1,26 @@
package org.jeecg.modules.openapi.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.jeecg.modules.openapi.entity.OpenApiHeader;
import org.jeecg.modules.openapi.mapper.OpenApiHeaderMapper;
import org.jeecg.modules.openapi.service.OpenApiHeaderService;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @date 2024/12/10 14:49
*/
@Service
public class OpenApiHeaderServiceImpl extends ServiceImpl<OpenApiHeaderMapper, OpenApiHeader> implements OpenApiHeaderService {
@Override
public boolean deleteByApiId(String apiId) {
return baseMapper.delete(Wrappers.lambdaUpdate(OpenApiHeader.class).eq(OpenApiHeader::getApiId, apiId)) > 0;
}
@Override
public List<OpenApiHeader> findByApiId(String apiId) {
return baseMapper.selectList(Wrappers.lambdaQuery(OpenApiHeader.class).eq(OpenApiHeader::getApiId, apiId));
}
}

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