Compare commits

...

364 Commits

Author SHA1 Message Date
17d609cf34 只保留AI的相关介绍 2025-12-04 14:52:34 +08:00
bfcd2797f1 变更数据库名 jeecgai 2025-12-04 14:46:39 +08:00
e112f4eea3 【制作独立jeecg-ai】删除微服务模块,简化使用 2025-12-04 14:39:06 +08:00
3b36f1909a 【制作独立jeecg-ai】只保留mysql库 2025-12-04 14:39:01 +08:00
5e5e11f952 【制作独立jeecg-ai】变更数据库为jeecgai 2025-12-04 14:38:55 +08:00
d6051addeb 【制作独立jeecg-ai】删除示例代码 2025-12-04 14:38:45 +08:00
281c3ff3c8 措辞优化 2025-12-04 13:55:53 +08:00
38d44c2487 修改一个参数,就实现默认的四个系统主题快速切换 2025-12-03 19:55:13 +08:00
8c88f8adf5 【issues/9098】tabs标签页关闭异常 2025-12-03 19:06:21 +08:00
526734c5a5 v3.9.0 其他数据库脚本,增加aiflow调试接口权限 2025-12-02 12:19:35 +08:00
44b48ad916 AI流程调试接口加权限,存在命令执行漏洞 #9144 2025-12-01 15:18:34 +08:00
1a3ae4f61c 解决AI流程两个严重问题
1、AI流程调试接口,存在命令执行漏洞 #9144
2、AI流程导入后缀改成 *.jeecgai
2025-12-01 15:13:18 +08:00
859c509f08 解决AI流程两个严重问题
1、AI流程调试接口,存在命令执行漏洞 #9144
2、AI流程导入后缀改成 *.jeecgai
2025-12-01 15:13:11 +08:00
0704f187af 遗漏方法提交 2025-12-01 13:52:23 +08:00
199d2b439e GUI代码生成,更新文档地址 2025-11-28 15:59:10 +08:00
5f898ed034 V3.9.0 微服务启动失败 缺少配置application-liteflow.yml 2025-11-28 15:06:06 +08:00
5a9cb05c86 V3.9.0 打包太大,默认不集成积木报表的nosql支持包 2025-11-28 11:47:44 +08:00
98936680d5 V3.9.0 打包太大,删除demo模块无用代码 2025-11-28 11:42:58 +08:00
fc043fd5f3 V3.9.0 打包太大,删除非必须依赖 2025-11-28 11:35:15 +08:00
54531002a7 v3.9.0 oracle、SqlServer、postgresql初始化脚本 2025-11-28 11:04:50 +08:00
728a95c00d 描述 2025-11-28 10:09:04 +08:00
13c9951c1f 优化描述 2025-11-28 10:05:36 +08:00
8dfaa3c3e1 优化 2025-11-28 10:02:23 +08:00
050c478dce 产品描述优化 2025-11-28 10:00:44 +08:00
2688e8b6e2 解决yarn安装,启动报错问题 2025-11-27 22:32:09 +08:00
a9f30f0ca5 解决yarn安装,启动报错问题 2025-11-27 22:25:57 +08:00
1bf4a0595a V3.9.0 修复部门老数据,类型不对 2025-11-27 18:50:13 +08:00
74cd57fd99 V3.9.0 Oracle11g 数据库 登录提示 无效的列类型: 1111 #9145 2025-11-27 18:44:35 +08:00
c9ac4c9945 v3.9.0 修复历史机构的部门类型 2025-11-27 18:42:27 +08:00
3b3371ee1a v3.9.0 更新数据库 2025-11-27 18:21:31 +08:00
1d3bde9fe7 MCP示例 2025-11-27 15:46:51 +08:00
90b50a51a7 简化MCP示例 2025-11-27 15:46:40 +08:00
668ac59a5c AI聊天创建用户等功能,移除工号和邮箱的唯一性要求 2025-11-27 15:36:57 +08:00
5f01bdd29b v3.9.0 增加MCP插件示例数据 2025-11-27 13:23:56 +08:00
82bfcc7b14 启动脚本,host检查失败,不影响启动 2025-11-27 11:59:04 +08:00
ef210a2242 前端依赖包有问题,导致build报错 2025-11-27 11:53:01 +08:00
435445cb4e v3.9.0 APP推送方法优化 2025-11-26 22:22:25 +08:00
fdf7cd1f6b v3.9.0 数据库更新,解决多屏大屏示例不展示问题 2025-11-26 13:42:36 +08:00
92a38f41b0 v3.9.0 数据库升级 2025-11-26 12:11:25 +08:00
7ed3bcc912 分支名称调整 2025-11-26 11:41:35 +08:00
2706c0a519 v3.9.0 支持MCP和插件,实现聊天式业务操作(如 “一句话创建用户”)! 2025-11-26 11:38:16 +08:00
f4b80365a9 描述修改 2025-11-26 11:36:56 +08:00
1108aa5288 更新版本号至v3.9.0 2025-11-26 11:29:52 +08:00
9919ae2bc5 v3.9.0 里程碑版本发布 2025-11-26 11:25:35 +08:00
1f73837b7d uniapp推送设置为空 2025-11-26 11:25:30 +08:00
9571e0b169 v3.9.0 里程碑版本发布 2025-11-26 11:14:47 +08:00
1a923596db 显示说明增量sql位置 2025-11-25 17:11:47 +08:00
62549e0a1c 打开flyway 数据库自动升级 2025-11-25 17:11:28 +08:00
2740a2f419 升级积木报表和积木BI到v2.2.0、poi升级到5 2025-11-24 22:04:12 +08:00
899264250c 请求中附带非法或过期 Token 时,返回重复的 401 请求 #9107 2025-11-17 11:02:43 +08:00
0be7d00eb2 集成vite-plugin-pwa实现渐进式Web应用,首屏很快,同时异步加载了系统的资源,点击菜单更快 2025-11-17 10:24:38 +08:00
7152ae9e49 jeecg-boot-starter项目说明 2025-11-12 23:14:04 +08:00
58b41db786 Online报表(带参数)预览后台报错 #9000 2025-11-11 15:09:15 +08:00
d715c7a0ac rollup版本号固定4.52.5,rollup-plugin-visualizer固定5.14.0,导致打包失败 2025-11-10 15:54:36 +08:00
aca407e1ce AI实战编程教程:JEECG低代码与Cursor+GitHub Copilot实现AI高效编程实战 2025-10-31 11:31:10 +08:00
cfea79a187 修复功能菜单“系统管理”-“部门管理”,用户列表中,编辑用户界面-用户账号为空 #9032 2025-10-30 22:26:55 +08:00
7848d1fb33 lock版本更新 2025-10-28 22:59:47 +08:00
91fa645878 3.8.3-master分支:租户用户 菜单下 新增用户报错 #9039 2025-10-28 13:40:27 +08:00
c9fc948658 更新jimureport和jimubi的版本号至2.1.5 2025-10-21 17:44:28 +08:00
b97d041e7f 更新springboot3版本号 2025-10-17 19:31:53 +08:00
6492f2c99a 更新README.md,修正Sa-Token下载链接格式 2025-10-16 19:13:47 +08:00
bf32385a06 更新README.md,调整下载链接顺序 2025-10-16 19:12:11 +08:00
6ef637c46f 提供SpringBoot3.3 + Sa-Token版本 2025-10-16 19:05:59 +08:00
bc6f336745 issues/8972 通义千问的多模态模型保存激活报错 #55 2025-10-14 22:46:46 +08:00
0d86df8e9e 1 2025-10-14 18:03:51 +08:00
3db673b67d issue格式 2025-10-14 18:03:01 +08:00
3ba5395d33 优化gateway启动报警告 2025-10-14 16:48:17 +08:00
e7eed37470 升级shardingsphere-jdbc版本到5.5.0,需要手工配置ShardingSphere数据源到spring.datasource.dynamic.datasource中,用法更明确 2025-10-14 16:47:05 +08:00
30ac3f7c72 升级shardingsphere-jdbc版本到5.5.0,需要手工配置ShardingSphere数据源到spring.datasource.dynamic.datasource中,用法更明确 2025-10-14 16:02:15 +08:00
03e6c97d80 重构JeecgBizToolsProvider.java,使用JsonObjectSchema替代JsonSchemaProperty,优化参数定义 2025-10-13 14:10:52 +08:00
b9f6f6dc53 升级langchain4j到1.3.0,解决很多模型不支持问题和MCP支持 2025-10-13 11:27:09 +08:00
107e13c8af [issues/8859]online表单java增强失效-- 2025-10-11 11:35:36 +08:00
0512b41b2b 更新README.md,增加对Node.js版本要求的说明,强调不再支持EOL的Node.js 18 2025-10-10 17:34:00 +08:00
d6d880f887 更新说明 2025-10-10 10:20:09 +08:00
b0e974a418 更新README.md,优化平台介绍和技术架构信息,增强AI应用平台描述 2025-10-09 11:15:45 +08:00
388fa9b8c2 v3.8.3大版本发布,全面迈向 SpringBoot3 2025-10-09 11:06:32 +08:00
bc04bd1433 --author:scott--date:20250930--for:使用@PostConstruct注解初始化PrometheusMeterRegistry配置,避免启动后配置延迟 2025-09-30 15:43:24 +08:00
35aba0784d Path Traversal Vulnerability /sys/comment/addFile /sys/upload/uploadMinio endpoint (notice the uploadlocal function is different from the /sys/common/upload ) #8827 2025-09-29 18:24:33 +08:00
c3822ab702 3.8.3版本能正常连接sqlserver数据库,但是无法解析查询代码 #8900 2025-09-29 11:55:37 +08:00
d4487356f0 更新 JeecgSystemApplication.java,排除 MongoAutoConfiguration 以避免未集成 mongo 的报错 2025-09-28 22:34:10 +08:00
ae4363dc72 仪表盘大屏分享,提示需要token错误 2025-09-28 18:12:03 +08:00
3e6c7651ee 提供v3.8.3版本数据库脚本 2025-09-28 14:29:52 +08:00
c0ffd14b7a 更新 pom.xml,修改 jimureport 依赖的 artifactId 2025-09-26 16:26:35 +08:00
914875d6a1 更新 pom.xml,升级 jimubi-spring-boot-starter 和 jimureport-nosql-starter 版本 2025-09-26 15:26:30 +08:00
2298ee3eed 更新 docker-compose-cloud.yml,添加 jeecg-boot 网络配置到 sentinel 和 xxljob 服务 2025-09-25 13:47:33 +08:00
2a8853b353 docker微服务启动 docker-compose 增加xxljob和sentinel配置 2025-09-25 12:48:25 +08:00
b920c5b794 Oracle11g数据库 多租户管理>>添加租户 报错 #8897 2025-09-25 12:42:53 +08:00
d3fa38a9e6 更新 application-docker.yml,修改 Redis 和 XXL-JOB 配置为使用服务名称 2025-09-25 11:17:53 +08:00
b0df78b06c 配置文件升级到v3.8.3 切换tomcat 2025-09-25 10:23:51 +08:00
80749098bd 添加 JustAuth 自动配置支持,更新 pom.xml 以包含 jeecg-boot-starter-job 依赖,并修改 application.yml 中的 nacos 服务器地址配置 2025-09-24 16:06:54 +08:00
19b7f2cb29 springboot3 支持 jdk17、jdk21、jdk24 2025-09-24 15:18:32 +08:00
39f5c3a5be 支持jdk21 2025-09-24 13:18:52 +08:00
9ee3a36fbb 版本发布日期更新 2025-09-24 13:17:21 +08:00
8c5cf3a0d9 【v3.8.3开源版本发布】更改groupId从org.jeecgframework.boot为org.jeecgframework.boot3,与springboot2区分 2025-09-24 12:19:01 +08:00
053552c123 【v3.8.3开源版本发布】
Merge remote-tracking branch 'origin/master' into springboot3

# Conflicts:
#	README.md
#	jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/JeecgBootExceptionHandler.java
#	jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/FileDownloadUtils.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/SysUserAgentController.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/ISysDepartService.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/ISysUserService.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/impl/SysAnnouncementServiceImpl.java
#	jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-sentinel/pom.xml
#	jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-test/jeecg-cloud-test-seata/pom.xml
#	jeecg-boot/pom.xml
2025-09-24 12:01:11 +08:00
fc44deca83 Merge remote-tracking branch 'origin/springboot3' into springboot3 2025-09-24 11:18:36 +08:00
5d5d9fc53d Merge branch 'master' of https://github.com/zhangdaiscott/jeecg-boot 2025-09-24 11:16:50 +08:00
002bfe25f8 【v3.8.3】升级数据库,暂时先删除 2025-09-24 11:06:54 +08:00
7cb2dc4fde Update @vitejs/plugin-vue-jsx version
Updated version of @vitejs/plugin-vue-jsx to 4.1.1.
自动下载的依赖 @vitejs/plugin-vue-jsx@4.2.0 是 CJS 格式 , 1.0.0-beta.38 版本的 @rolldown/pluginutils 是 ESM 格式模块不兼容
2025-09-18 17:32:11 +08:00
9c244bd266 Update @vitejs/plugin-vue-jsx version
Updated version of @vitejs/plugin-vue-jsx to 4.1.1.

 自动下载的依赖 @vitejs/plugin-vue-jsx@4.2.0 是 CJS 格式 , 1.0.0-beta.38 版本的 @rolldown/pluginutils 是 ESM 格式
模块不兼容
2025-09-18 17:31:14 +08:00
ab151879b3 XXL-JOB版本号错误,修改至v2.4.0 2025-09-14 16:03:45 +08:00
000ae1db30 升级前端pnpm lock 2025-09-14 15:52:27 +08:00
63e066180d 【v3.8.3】更新pom.xml,升级Kotlin和Liteflow版本 2025-09-14 12:57:48 +08:00
44c1079f87 【v3.8.3】优化顶部导航风格菜单的样式,支持外部链接打开及菜单重定向 2025-09-14 11:59:09 +08:00
e825e0f912 【v3.8.3】升级数据库 2025-09-14 11:58:05 +08:00
132e89b0e1 【v3.8.3】底层core的一些功能修改 2025-09-14 11:57:47 +08:00
881a637285 【v3.8.3】AI助手调用系统业务扩展接口,支持添加用户、查询用户、查询角色及授予角色功能 2025-09-14 11:57:37 +08:00
02e9f8984f 【v3.8.3】更新EnumDict.java,添加使用说明和配置要求 2025-09-14 11:55:45 +08:00
a4343fc2cb 【v3.8.3】底层core的一些功能修改 2025-09-14 11:55:31 +08:00
152e8c7aaa 【v3.8.3】优化枚举字典数据加载,支持多包路径扫描,提升初始化性能 2025-09-14 11:54:01 +08:00
d7dc81455d 【v3.8.3】用户组织机构大改造(新增主岗位、主岗位、用户签名) 2025-09-14 11:53:36 +08:00
aefdcd6315 【v3.8.3】大数据导出共通类 2025-09-14 11:50:56 +08:00
1cf4054e76 【v3.8.3】租户大改造 2025-09-14 11:50:37 +08:00
7829cf18d7 解决升级mybatisPlus后SqlServer分页使用OFFSET 2025-09-14 11:49:55 +08:00
69c3a9da9a 【v3.8.3】升级版本号至3.8.3,更新依赖项和排除项 2025-09-14 11:49:42 +08:00
4d34150479 升级kingbase8驱动和切换回tomcat 2025-09-14 11:48:00 +08:00
29687c8908 【v3.8.3】升级版本号3.8.3和docker配置 2025-09-14 10:48:13 +08:00
2e93a92dde 【v3.8.3】修改配置文件,删除undertow 2025-09-14 10:45:32 +08:00
d383f7458d 【v3.8.3】企业微信通知采用卡片 2025-09-14 10:44:08 +08:00
700318e1c1 【v3.8.3】首页配置功能改造 2025-09-14 10:43:21 +08:00
d728d6b090 【v3.8.3】升级代码生成器模板 2025-09-14 10:43:00 +08:00
7abc2e4c9c 【v3.8.3】大数据导出示例 2025-09-14 10:42:45 +08:00
da2b0cc354 【v3.8.3】升级aiflow 2025-09-14 10:41:53 +08:00
a6751c22be 【v3.8.3】功能性能优化 2025-09-14 10:41:25 +08:00
f087525a75 【v3.8.3】undertow不稳定切换回tomcat 2025-09-14 10:40:21 +08:00
4f46213df6 【v3.8.3】前端小改动汇总集合 2025-09-14 10:39:48 +08:00
d76842ae07 【v3.8.3】airag优化体验升级 2025-09-14 10:38:41 +08:00
8c64db46e5 【v3.8.3】默认首页改造 2025-09-14 10:37:29 +08:00
81fb2ac3b2 【v3.8.3】切换回Tomcat信息 2025-09-14 10:37:14 +08:00
fa98817aeb 【v3.8.3】部门大改造关联修改 2025-09-14 10:37:05 +08:00
1158977826 更新版本号至3.8.3,修改README.md以反映最新框架和工具版本 2025-09-14 10:27:07 +08:00
8b6def0ee3 导出excel总提示格式不匹配和日志大数据导出示例 2025-09-14 10:26:52 +08:00
39c0d5b3f5 【v3.8.3】组织机构部门大改造(支持子公司、岗位与不能功能划分更清晰,岗位可以设置上下级,岗位可以设置职级,支持回报关系) 2025-09-14 10:26:24 +08:00
adeebee840 【v3.8.3】用户大改造,取消原职位换成职位字典、原职位改成岗位职级、新增主岗位和兼职岗位 2025-09-14 10:24:02 +08:00
862aaa8632 【v3.8.3】我的租户大改造 2025-09-14 10:22:26 +08:00
6a11ff8a64 【v3.8.3】升级版本号 2025-09-14 10:16:56 +08:00
73059b8a53 优化AIRG 2025-09-13 16:15:23 +08:00
e377bf6990 升级版本号 2025-09-05 18:49:20 +08:00
434b42e9ed 部分配置丢失,修复 2025-09-04 10:47:50 +08:00
f1ceb08e16 Merge remote-tracking branch 'origin/master' into springboot3
# Conflicts:
#	README.md
#	jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-test/jeecg-cloud-test-seata/pom.xml
#	jeecg-boot/pom.xml
#	jeecgboot-vue3/src/components/jeecg/JVxeTable/src/hooks/useFinallyProps.ts
2025-09-04 09:40:57 +08:00
d2eedacc85 优化项目介绍 2025-09-03 19:58:13 +08:00
8791384791 优化项目介绍 2025-09-03 19:21:56 +08:00
fd60e49f5b 升级积木报表和积木BI到2.1.3\升级minidao解决SqlServer兼容问题 2025-09-03 17:44:01 +08:00
5f1dc06067 升级积木报表和积木BI到2.1.3\升级minidao解决SqlServer兼容问题 2025-09-03 17:43:57 +08:00
b67770ff14 升级miniao到1.10.14,解决SqlServer分页兼容问题 2025-09-03 17:36:32 +08:00
1dae808cf1 升级积木报表和积木BI到最新版 v2.1.3 2025-09-03 16:34:26 +08:00
70d8353219 升级springBoot 3.5.5 2025-09-03 15:53:53 +08:00
208d9990ae 主干默认springboot3版本 2025-09-03 15:41:30 +08:00
3e208de18e 默认主干切换springboot3分支 2025-09-03 15:37:26 +08:00
4f3c71af5b 主干默认springboot3版本 2025-09-03 15:30:19 +08:00
70bd639206 【issues/8738】componentProps是函数时获取不到valueType 2025-08-26 13:29:16 +08:00
d245ef3037 【JVXETable】修复首屏加载速度 2025-08-26 13:29:05 +08:00
7af8346b79 【#8695】修复JVxeTable卡顿问题 2025-08-22 17:51:42 +08:00
1b8a31f0d3 代码生成器还原到 1.5.1 2025-08-22 14:44:57 +08:00
294ad5a6c9 代码生成,把角色授权sql菜单的,也生成出来 2025-08-20 19:34:01 +08:00
065b255d90 设置代码生成,FreeMarker空值处理不报错 2025-08-20 15:12:59 +08:00
56976e68b4 升级spring-boot到3.5.4、升级spring-cloud到2025.0.0、升级spring-cloud-alibaba到2023.0.3.3 2025-08-20 10:42:43 +08:00
db1ff0268b Squashed commit of the following:
commit b7519d7199
Author: JEECG <445654970@qq.com>
Date:   Tue Aug 19 15:18:52 2025 +0800

    中文乱码

commit 81ba07c853
Author: JEECG <445654970@qq.com>
Date:   Tue Aug 19 15:09:21 2025 +0800

    增加代码生成用法文档

commit 92ed296e63
Author: JEECG <445654970@qq.com>
Date:   Tue Aug 19 14:04:22 2025 +0800

    【issues/8709】LayoutContent样式多出1px

commit c2aff84914
Author: JEECG <445654970@qq.com>
Date:   Tue Aug 19 14:04:09 2025 +0800

    【issues/8683】DatePicker组件的componentProps使用函数形式时初始值获取不对

commit e002cd3bf3
Author: JEECG <445654970@qq.com>
Date:   Tue Aug 19 14:03:51 2025 +0800

    【issues/8680】editComponentProps 可接受一个函数传入record

commit 1de07ff3ff
Author: JEECG <445654970@qq.com>
Date:   Tue Aug 19 14:03:32 2025 +0800

    -- author:liaozhiyang---date:20250813--for:【issues/8690】BasicTable的rowSelection新增onSelect方法 ---

commit 35852d41f1
Author: JEECG <445654970@qq.com>
Date:   Tue Aug 19 14:03:04 2025 +0800

    jvxeTable表格切换disabled属性时,相邻的两个枚举下拉,如果值是一样的,但是label不一样,会把第二个下拉的显示值渲染到第一个下拉中 #8593

commit a2cb1d9f25
Author: JEECG <445654970@qq.com>
Date:   Tue Aug 19 14:00:01 2025 +0800

    【issues/8529】setColumns将原本隐藏的列展示后,列配置里却没有勾选该列

commit 2002af54d0
Author: JEECG <445654970@qq.com>
Date:   Tue Aug 19 13:59:42 2025 +0800

    JVxeTypes.image组件action字段只能定义第1张图片的上传接口,后面图片的接口还是使用公共上传接口 #8628

commit 89747403a2
Author: JEECG <445654970@qq.com>
Date:   Tue Aug 19 13:59:28 2025 +0800

    JVxeTable组件全选翻页后会被取消选中 #8630

commit 3db0995c3f
Author: JEECG <445654970@qq.com>
Date:   Tue Aug 19 11:23:19 2025 +0800

    [代码生成]前端代码支持直接生成到前端项目、菜单sql会自动生成到start项目的flyway目录

commit 950621dd88
Author: JEECG <445654970@qq.com>
Date:   Mon Aug 18 23:04:59 2025 +0800

    升级代码生成器,一键生成代码,vue3代码会生成到前端项目、菜单升级sql自动迁移到flyway目录重启自动执行(不需要手工迁移前端代码和手工执行升级sql)

commit 033cf51d69
Author: JEECG <445654970@qq.com>
Date:   Mon Aug 18 23:04:52 2025 +0800

    升级代码生成器,一键生成代码,vue3代码会生成到前端项目、菜单升级sql自动迁移到flyway目录重启自动执行(不需要手工迁移前端代码和手工执行升级sql)

commit fb9f367517
Author: JEECG <445654970@qq.com>
Date:   Mon Aug 18 23:02:53 2025 +0800

    代码生成,online自定义按钮无排序报错

commit b2da45d803
Author: JEECG <445654970@qq.com>
Date:   Mon Aug 18 16:23:22 2025 +0800

    演示地址

commit 2840f0d325
Author: JEECG <445654970@qq.com>
Date:   Mon Aug 18 15:37:10 2025 +0800

    默认账号密码

commit 6ace7eae8a
Author: JEECG <445654970@qq.com>
Date:   Sun Aug 17 15:11:45 2025 +0800

    开发环境关闭日志生成,项目启动快;生产环境请按需打开注释

commit 3d88147c59
Author: JEECG <445654970@qq.com>
Date:   Thu Aug 14 23:03:46 2025 +0800

    调整微服务启动文档

commit ba0052d452
Author: JEECG <445654970@qq.com>
Date:   Wed Aug 13 13:55:13 2025 +0800

    支持lazy-initialization启动,项目大了后启动会更快

commit 69fca254f0
Author: JEECG <445654970@qq.com>
Date:   Wed Aug 13 11:47:27 2025 +0800

    补充注释

commit b3de596199
Author: JEECG <445654970@qq.com>
Date:   Wed Aug 13 11:47:16 2025 +0800

    彻底关闭 prettier 校验规则

commit f46273d15e
Author: JEECG <445654970@qq.com>
Date:   Wed Aug 13 10:26:23 2025 +0800

    设置ESLint 的 vue/html-self-closing 自闭合标签警告配置

commit 0fe258dbc2
Author: JEECG <445654970@qq.com>
Date:   Wed Aug 13 09:26:16 2025 +0800

    修复 onExportXls defSort 不生效问题 #7570

commit de7f23c555
Merge: d97e56b2 444c7140
Author: JEECG <zhangdaiscott@163.com>
Date:   Wed Aug 13 09:20:31 2025 +0800

    Merge pull request #8496 from lileiAimee/developer

    解决TableAction中自定义图标颜色不起作用的问题

commit d97e56b2f0
Author: JEECG <445654970@qq.com>
Date:   Tue Aug 12 19:08:43 2025 +0800

    多租户模式下系统系统会给租户默认增加上测试的角色菜单,但是后台获取菜单时异常,无法打开相关页面 #8667

commit c868496b78
Author: JEECG <445654970@qq.com>
Date:   Tue Aug 12 19:02:49 2025 +0800

    映射警告

commit c5150baa69
Author: JEECG <445654970@qq.com>
Date:   Tue Aug 12 18:18:34 2025 +0800

    支持通过用户账号邀请加入租户

commit 3d9f59c69b
Author: JEECG <445654970@qq.com>
Date:   Tue Aug 12 18:17:55 2025 +0800

    邀请用户加入租户,支持通过用户账号

commit 420d6db3fb
Author: JEECG <445654970@qq.com>
Date:   Tue Aug 12 18:06:20 2025 +0800

    登录用户没有部门,不提示警告

commit 473a626039
Author: JEECG <445654970@qq.com>
Date:   Tue Aug 12 14:10:02 2025 +0800

    增加JPopup组件带参数示例

commit 0308b0597c
Author: JEECG <445654970@qq.com>
Date:   Tue Aug 12 14:08:18 2025 +0800

    【issues/8426】解决JPopup组件传参不能接收

commit 2191f5d48c
Author: JEECG <445654970@qq.com>
Date:   Mon Aug 11 22:43:07 2025 +0800

    调整位置

commit 1158b0b6e7
Author: JEECG <445654970@qq.com>
Date:   Mon Aug 11 22:30:39 2025 +0800

    升级seata到1.7.0;升级dynamic-datasource-spring-boot-starter到3.5.2

commit ead2cef1f4
Author: JEECG <445654970@qq.com>
Date:   Mon Aug 11 18:47:48 2025 +0800

    支持多字段默认排序defSort数组、解决多列排序无效 #8659

commit 83bb0a0a6a
Author: JEECG <445654970@qq.com>
Date:   Mon Aug 11 18:47:43 2025 +0800

    支持多字段默认排序defSort数组

commit b474e9e5a5
Author: JEECG <445654970@qq.com>
Date:   Sun Aug 10 17:06:01 2025 +0800

    开发环境安装

commit 422373e300
Author: JEECG <445654970@qq.com>
Date:   Sun Aug 10 16:30:13 2025 +0800

    提供JeecgBoot 运行环境python检查脚本

commit 1cf11a4c2a
Author: JEECG <445654970@qq.com>
Date:   Sat Aug 9 09:41:57 2025 +0800

    提供jeecgboot-oracle11g.dmp

commit 925f163784
Author: JEECG <445654970@qq.com>
Date:   Fri Aug 8 22:07:24 2025 +0800

    引入jeecg-boot-starter-job依赖启动报错 #8694

commit d01c1d7d47
Author: JEECG <445654970@qq.com>
Date:   Thu Aug 7 15:41:21 2025 +0800

    支持lazy-initialization

commit 3576b54945
Author: JEECG <445654970@qq.com>
Date:   Thu Aug 7 15:36:51 2025 +0800

    升级积木报表和积木BI到最新版v2.1.2

commit 444c7140f6
Author: lileiAimee <345697385@qq.com>
Date:   Wed Jun 25 09:55:07 2025 +0800

    解决TableAction中自定义图标颜色不起作用的问题

# Conflicts:
#	README-EN.md
#	README.md
#	jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-test/jeecg-cloud-test-seata/pom.xml
#	jeecg-boot/pom.xml
2025-08-19 22:58:12 +08:00
b7519d7199 中文乱码 2025-08-19 15:18:52 +08:00
81ba07c853 增加代码生成用法文档 2025-08-19 15:09:21 +08:00
92ed296e63 【issues/8709】LayoutContent样式多出1px 2025-08-19 14:04:22 +08:00
c2aff84914 【issues/8683】DatePicker组件的componentProps使用函数形式时初始值获取不对 2025-08-19 14:04:09 +08:00
e002cd3bf3 【issues/8680】editComponentProps 可接受一个函数传入record 2025-08-19 14:03:51 +08:00
1de07ff3ff -- author:liaozhiyang---date:20250813--for:【issues/8690】BasicTable的rowSelection新增onSelect方法 --- 2025-08-19 14:03:32 +08:00
35852d41f1 jvxeTable表格切换disabled属性时,相邻的两个枚举下拉,如果值是一样的,但是label不一样,会把第二个下拉的显示值渲染到第一个下拉中 #8593 2025-08-19 14:03:04 +08:00
a2cb1d9f25 【issues/8529】setColumns将原本隐藏的列展示后,列配置里却没有勾选该列 2025-08-19 14:00:01 +08:00
2002af54d0 JVxeTypes.image组件action字段只能定义第1张图片的上传接口,后面图片的接口还是使用公共上传接口 #8628 2025-08-19 13:59:42 +08:00
89747403a2 JVxeTable组件全选翻页后会被取消选中 #8630 2025-08-19 13:59:28 +08:00
3db0995c3f [代码生成]前端代码支持直接生成到前端项目、菜单sql会自动生成到start项目的flyway目录 2025-08-19 11:23:19 +08:00
950621dd88 升级代码生成器,一键生成代码,vue3代码会生成到前端项目、菜单升级sql自动迁移到flyway目录重启自动执行(不需要手工迁移前端代码和手工执行升级sql) 2025-08-18 23:04:59 +08:00
033cf51d69 升级代码生成器,一键生成代码,vue3代码会生成到前端项目、菜单升级sql自动迁移到flyway目录重启自动执行(不需要手工迁移前端代码和手工执行升级sql) 2025-08-18 23:04:52 +08:00
fb9f367517 代码生成,online自定义按钮无排序报错 2025-08-18 23:02:53 +08:00
b2da45d803 演示地址 2025-08-18 16:23:22 +08:00
2840f0d325 默认账号密码 2025-08-18 15:37:10 +08:00
6ace7eae8a 开发环境关闭日志生成,项目启动快;生产环境请按需打开注释 2025-08-17 15:11:45 +08:00
3d88147c59 调整微服务启动文档 2025-08-14 23:03:46 +08:00
08f245bdf9 修改遗漏 swagger上选择的接口和实际接口不对应#8705 2025-08-13 18:15:19 +08:00
8cc033b86f swagger上选择的接口和实际接口不对应 #8705 2025-08-13 17:22:42 +08:00
6b7542620b swagger上选择的接口和实际接口不对应 #8705 2025-08-13 16:44:16 +08:00
ba0052d452 支持lazy-initialization启动,项目大了后启动会更快 2025-08-13 13:55:13 +08:00
69fca254f0 补充注释 2025-08-13 11:47:27 +08:00
b3de596199 彻底关闭 prettier 校验规则 2025-08-13 11:47:16 +08:00
f46273d15e 设置ESLint 的 vue/html-self-closing 自闭合标签警告配置 2025-08-13 10:26:23 +08:00
0fe258dbc2 修复 onExportXls defSort 不生效问题 #7570 2025-08-13 09:26:16 +08:00
de7f23c555 Merge pull request #8496 from lileiAimee/developer
解决TableAction中自定义图标颜色不起作用的问题
2025-08-13 09:20:31 +08:00
67d9865861 Merge pull request #8550 from TsuGit/fix/xxljob-startup-error
fix(xxljob): 修复因 factoryBeanObjectType 导致的启动失败
2025-08-13 00:02:30 +08:00
d97e56b2f0 多租户模式下系统系统会给租户默认增加上测试的角色菜单,但是后台获取菜单时异常,无法打开相关页面 #8667 2025-08-12 19:08:43 +08:00
c868496b78 映射警告 2025-08-12 19:02:49 +08:00
c5150baa69 支持通过用户账号邀请加入租户 2025-08-12 18:18:34 +08:00
3d9f59c69b 邀请用户加入租户,支持通过用户账号 2025-08-12 18:17:55 +08:00
420d6db3fb 登录用户没有部门,不提示警告 2025-08-12 18:06:20 +08:00
473a626039 增加JPopup组件带参数示例 2025-08-12 14:10:02 +08:00
0308b0597c 【issues/8426】解决JPopup组件传参不能接收 2025-08-12 14:08:18 +08:00
cd809a6573 Squashed commit of the following:
升级seata到1.7.0;升级dynamic-datasource-spring-boot-starter到3.5.2
    支持多字段默认排序defSort数组、解决多列排序无效 #8659
    支持多字段默认排序defSort数组
    提供JeecgBoot 运行环境python检查脚本
    提供jeecgboot-oracle11g.dmp
2025-08-12 09:25:01 +08:00
2191f5d48c 调整位置 2025-08-11 22:43:07 +08:00
1158b0b6e7 升级seata到1.7.0;升级dynamic-datasource-spring-boot-starter到3.5.2 2025-08-11 22:30:39 +08:00
ead2cef1f4 支持多字段默认排序defSort数组、解决多列排序无效 #8659 2025-08-11 18:47:48 +08:00
83bb0a0a6a 支持多字段默认排序defSort数组 2025-08-11 18:47:43 +08:00
b474e9e5a5 开发环境安装 2025-08-10 17:06:01 +08:00
422373e300 提供JeecgBoot 运行环境python检查脚本 2025-08-10 16:30:13 +08:00
1cf11a4c2a 提供jeecgboot-oracle11g.dmp 2025-08-09 09:41:57 +08:00
ac446691c4 Squashed commit of the following:
commit 925f163784
Author: JEECG <445654970@qq.com>
Date:   Fri Aug 8 22:07:24 2025 +0800

    引入jeecg-boot-starter-job依赖启动报错 #8694

commit d01c1d7d47
Author: JEECG <445654970@qq.com>
Date:   Thu Aug 7 15:41:21 2025 +0800

    支持lazy-initialization

commit 3576b54945
Author: JEECG <445654970@qq.com>
Date:   Thu Aug 7 15:36:51 2025 +0800

    升级积木报表和积木BI到最新版v2.1.2

# Conflicts:
#	jeecg-boot/pom.xml
2025-08-08 22:22:30 +08:00
925f163784 引入jeecg-boot-starter-job依赖启动报错 #8694 2025-08-08 22:07:24 +08:00
0feb307e8d Merge remote-tracking branch 'origin/master' into springboot3 2025-08-07 18:11:58 +08:00
781d61e96e swagger请求头部没有X-Access-Token #8676 2025-08-07 18:02:27 +08:00
d01c1d7d47 支持lazy-initialization 2025-08-07 15:41:21 +08:00
3576b54945 升级积木报表和积木BI到最新版v2.1.2 2025-08-07 15:36:51 +08:00
a760f94b94 解决pgvector向量数据库不支持2000维度的问题 2025-08-07 13:51:46 +08:00
e795e03365 【issues/8666】升级mybatisPlus后SqlServer分页使用OFFSET ? ROWS FETCH NEXT ? ROWS ONLY,导致online报表报错 2025-08-04 18:39:35 +08:00
342bdd2e38 【issues/8666】升级mybatisPlus后SqlServer分页使用OFFSET ? ROWS FETCH NEXT ? ROWS ONLY,无默认排序字段导致online报表报错 2025-08-04 18:14:11 +08:00
59ece16059 修改springboot3的配置yml 2025-08-03 12:51:32 +08:00
91208a4968 Merge remote-tracking branch 'origin/master' into springboot3 2025-08-03 12:49:26 +08:00
419e2bea0b 其他数据库配置 2025-08-03 12:47:42 +08:00
443abc3ede 其他数据库配置 2025-08-03 12:45:01 +08:00
358e46559f 其他数据库配置 2025-08-03 12:44:55 +08:00
128c2c97f6 修改springboot3的配置yml 2025-08-03 10:32:18 +08:00
424dc33bba 修改springboot3的配置yml 2025-08-03 10:13:47 +08:00
1cb48b4f0c 【解决SqlServer兼容问题,提供oracle和SqlServer的数据库脚本】
Merge remote-tracking branch 'origin/master' into springboot3

# Conflicts:
#	jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisPlusSaasConfig.java
2025-08-03 10:07:04 +08:00
ea59454f51 oracle初始脚本 2025-08-03 10:04:37 +08:00
615a1bc4ff sqlserver初始脚本 2025-08-03 09:52:13 +08:00
d976f12c8f sqlserver初始脚本 2025-08-03 09:51:49 +08:00
3783765161 sqlserver初始脚本 2025-08-03 09:43:21 +08:00
8cc6810fdd 提交oracle配置yml 2025-08-03 09:43:07 +08:00
a902d9af19 提交SqlServer配置yml 2025-08-03 09:40:00 +08:00
37a116f2fb 提交oracle配置yml 2025-08-03 09:39:20 +08:00
1564831f7e 提交oracle配置yml 2025-08-03 09:37:36 +08:00
18c1cd00c1 提交SqlServer配置yml 2025-08-03 09:37:24 +08:00
a988b05e72 提交oracle配置yml 2025-08-03 09:31:37 +08:00
a9a6fd529d 提交SqlServer配置yml 2025-08-03 09:31:11 +08:00
6586d3a880 【issues/8666】升级mybatisPlus后SqlServer分页使用OFFSET ? ROWS FETCH NEXT ? ROWS ONLY,导致online报表报错--- 2025-08-02 19:45:36 +08:00
af354f9f5e 【issues/8666】升级mybatisPlus后SqlServer分页使用OFFSET ? ROWS FETCH NEXT ? ROWS ONLY,导致online报表报错--- 2025-08-02 19:10:24 +08:00
79b182819b Merge remote-tracking branch 'origin/master' into springboot3 2025-08-01 17:20:53 +08:00
a638a93b65 一键docker启动脚本 2025-08-01 17:13:28 +08:00
621781d336 JEECG Boot 一键docker启动脚本 2025-08-01 17:04:39 +08:00
f1cad333da 提供一键构建docker镜像的脚本,省掉手工操作步骤 2025-08-01 12:39:28 +08:00
e70844ce61 【合并v3.8.2 docker-compose优化配置】
Merge remote-tracking branch 'origin/master' into springboot3

# Conflicts:
#	jeecg-boot/jeecg-boot-base-core/pom.xml
#	jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/Swagger3Config.java
#	jeecg-boot/jeecg-module-system/jeecg-system-start/Dockerfile
#	jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/java/org/jeecg/JeecgSystemApplication.java
#	jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/application-docker.yml
#	jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-sentinel/pom.xml
#	jeecg-boot/pom.xml
2025-08-01 10:54:33 +08:00
e00ffa2670 升级autopoi到1.4.14最新版 2025-08-01 10:46:07 +08:00
676fffa2c8 docker-compose增加jeecg-boot-pgvector 2025-07-31 22:47:01 +08:00
23cc569a47 docker system日志中文乱码解决 2025-07-31 18:12:57 +08:00
dafacf153b 升级版本号到3.8.2 2025-07-31 18:12:41 +08:00
7a9f357510 更新v3.8.2 功能清单 2025-07-31 17:56:40 +08:00
6c15b45a8c 【合并v3.8.2最新版代码】
Squashed commit of the following:

commit f30a8c658a
Author: JEECG <445654970@qq.com>
Date:   Thu Jul 31 11:35:16 2025 +0800

    数据库缺少openapi微服务网关配置

commit e84d7726d2
Author: JEECG <445654970@qq.com>
Date:   Thu Jul 31 10:20:09 2025 +0800

    后台接口地址修改

commit 0f39802698
Author: JEECG <445654970@qq.com>
Date:   Thu Jul 31 09:56:24 2025 +0800

    docker自动化部署命令

commit a014a3ed0e
Author: JEECG <445654970@qq.com>
Date:   Wed Jul 30 21:55:16 2025 +0800

    v3.8.2 优化一键docker启动前后端

commit 5720d1a01e
Author: JEECG <445654970@qq.com>
Date:   Wed Jul 30 19:26:38 2025 +0800

    升级版本号到3.8.2

commit 5eed6ac6d2
Author: JEECG <445654970@qq.com>
Date:   Wed Jul 30 18:49:29 2025 +0800

    升级版本号到3.8.2

commit 0cfa1e223a
Author: JEECG <445654970@qq.com>
Date:   Wed Jul 30 18:28:10 2025 +0800

    v3.8.2 系统通知改造支持分类

commit 219869f4c0
Author: JEECG <445654970@qq.com>
Date:   Wed Jul 30 18:25:58 2025 +0800

    v3.8.2 版本前端代码

commit e6edde963a
Author: JEECG <445654970@qq.com>
Date:   Wed Jul 30 18:25:46 2025 +0800

    v3.8.2 版本后端代码

commit c44b66128e
Author: JEECG <445654970@qq.com>
Date:   Wed Jul 30 18:23:09 2025 +0800

    XXL-JOB(2.4.0 及以上)已被移除,分片参数获取方式变更。

commit 9356b04741
Author: JEECG <445654970@qq.com>
Date:   Wed Jul 30 10:57:52 2025 +0800

    升级online到3.8.2-beta

commit d0a094f9a3
Author: JEECG <445654970@qq.com>
Date:   Wed Jul 30 10:57:31 2025 +0800

    升级mybatis-plus到3.5.12、升级jsqlparser到4.9

commit 73eb625737
Author: JEECG <445654970@qq.com>
Date:   Wed Jul 30 09:51:34 2025 +0800

    升级jimureport到v2.1.1

commit 74880705b8
Author: JEECG <445654970@qq.com>
Date:   Wed Jul 30 09:18:46 2025 +0800

    升级online到3.8.2-beta

# Conflicts:
#	jeecg-boot/jeecg-boot-base-core/pom.xml
#	jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/Swagger3Config.java
#	jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/java/org/jeecg/JeecgSystemApplication.java
#	jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-sentinel/pom.xml
#	jeecg-boot/pom.xml
2025-07-31 14:23:45 +08:00
f30a8c658a 数据库缺少openapi微服务网关配置 2025-07-31 11:35:16 +08:00
e84d7726d2 后台接口地址修改 2025-07-31 10:20:09 +08:00
0f39802698 docker自动化部署命令 2025-07-31 09:56:24 +08:00
a014a3ed0e v3.8.2 优化一键docker启动前后端 2025-07-30 21:55:16 +08:00
5720d1a01e 升级版本号到3.8.2 2025-07-30 19:26:38 +08:00
5eed6ac6d2 升级版本号到3.8.2 2025-07-30 18:49:29 +08:00
0cfa1e223a v3.8.2 系统通知改造支持分类 2025-07-30 18:28:10 +08:00
219869f4c0 v3.8.2 版本前端代码 2025-07-30 18:25:58 +08:00
e6edde963a v3.8.2 版本后端代码 2025-07-30 18:25:46 +08:00
c44b66128e XXL-JOB(2.4.0 及以上)已被移除,分片参数获取方式变更。 2025-07-30 18:23:09 +08:00
9356b04741 升级online到3.8.2-beta 2025-07-30 10:57:52 +08:00
d0a094f9a3 升级mybatis-plus到3.5.12、升级jsqlparser到4.9 2025-07-30 10:57:31 +08:00
73eb625737 升级jimureport到v2.1.1 2025-07-30 09:51:34 +08:00
74880705b8 升级online到3.8.2-beta 2025-07-30 09:18:46 +08:00
f67cfa1bfb 删除重复依赖 2025-07-29 12:00:05 +08:00
8d91caa4e6 升级积木报表和积木BI到最新版 2025-07-28 18:54:21 +08:00
0d9f9a04cc 升级knife4j-openapi3-jakarta-spring-boot-starter到4.6.0解决knife4j-production不生效问题 2025-07-25 14:08:00 +08:00
90565fcf79 Merge remote-tracking branch 'origin/master' into springboot3
# Conflicts:
#	jeecg-boot/db/tables_nacos.sql
2025-07-25 13:23:47 +08:00
118775cf79 【swagger问题】不带/doc.html访问后台项目swaager,接口测试实际请求后台接口地址少了项目前缀 2025-07-25 13:14:11 +08:00
7b80ae3e68 【swagger文档】application/json变成了application/x-www-form-urlencoded 2025-07-25 13:01:36 +08:00
cf4d888839 TODO 暂时注释掉,for:【issues/8638】springboot3分支,knife4j不能正确显示文档,但是swagger-ui和v3/api-docs正常 #8638 2025-07-24 18:34:25 +08:00
336e7851aa 【online】下拉搜索+带条件表字典,报错400 2025-07-22 10:08:40 +08:00
56b9131675 【online】下拉搜索+带条件表字典,报错400 2025-07-22 09:50:22 +08:00
3c0cc49f0c 【issues/8317】菜单管理页首页国际化报错 --- 2025-07-19 09:34:17 +08:00
69b2e97935 【issues/8552】useScript的isLoading默认值应该是true 2025-07-19 09:30:23 +08:00
1c2a49d371 【issues/8564】basicTale的TableLayout换成auto不生效 2025-07-19 09:29:37 +08:00
967197d224 有数十个字段时只展示2个字段,其余字段为ifShow:false会有滚动条 2025-07-19 09:28:16 +08:00
396718bc5e 解决问题:[issues/8558]批量删除优化 2025-07-17 15:07:33 +08:00
37c62c3962 启动democloud服务时出现循环依赖报错 #8573 2025-07-11 10:44:23 +08:00
f510578cb7 启动democloud服务时出现循环依赖报错 #8573 2025-07-11 10:35:17 +08:00
96b378bb7a AI视频介绍 2025-07-09 09:49:56 +08:00
2d7c51eadc 【合并升级v3.8.1】
Merge remote-tracking branch 'origin/master' into springboot3

# Conflicts:
#	jeecg-boot/README.md
#	jeecg-boot/db/tables_nacos.sql
#	jeecg-boot/jeecg-boot-base-core/pom.xml
#	jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/JeecgBootExceptionHandler.java
#	jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/encryption/AesEncryptUtil.java
#	jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/WebMvcConfiguration.java
#	jeecg-boot/jeecg-boot-module/jeecg-boot-module-airag/pom.xml
#	jeecg-boot/jeecg-boot-module/jeecg-boot-module-airag/src/main/java/org/jeecg/modules/airag/app/controller/AiragAppController.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/monitor/actuator/httptrace/CustomInMemoryHttpTraceRepository.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/openapi/service/impl/OpenApiPermissionServiceImpl.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/SysRoleIndexController.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/ISysUserService.java
#	jeecg-boot/jeecg-module-system/jeecg-system-start/pom.xml
#	jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml
#	jeecg-boot/jeecg-server-cloud/jeecg-system-cloud-start/src/main/java/org/jeecg/JeecgSystemCloudApplication.java
#	jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-sentinel/pom.xml
#	jeecg-boot/pom.xml
#	jeecgboot-vue3/pnpm-lock.yaml
2025-07-08 16:33:51 +08:00
9d440a4261 升级积木报表到最新版 v2.1.0 2025-07-08 14:02:50 +08:00
4870c43f39 解决【issues/8527】apiSelect分页加载重复请求 2025-07-08 13:44:47 +08:00
9c21f621c0 开源协议 2025-07-08 11:17:34 +08:00
8d382b76ad 开源协议说明 2025-07-08 11:13:57 +08:00
410f2539e1 更新地址 2025-07-08 10:01:02 +08:00
baee00921f 修复问题:低代码开发Online表单开发主子表ERP显示问题 #8532 2025-07-07 14:51:33 +08:00
216d4b9a1f fix: 解决 xxljob 因 factoryBeanObjectType 导致的启动失败 2025-07-06 03:10:33 +08:00
e00b25af42 增加系统功能架构图 2025-07-02 18:57:50 +08:00
44d6b37873 增加博客地址 2025-07-02 16:53:06 +08:00
271712b050 增加博客地址 2025-07-02 16:51:53 +08:00
3482b3a0db 更新项目介绍 2025-07-02 10:29:22 +08:00
10bf5cf127 V3.8.1_2__openapi.sql数据重复 #8531 2025-07-02 10:13:41 +08:00
6cc74699aa 完善项目介绍 2025-07-02 09:57:15 +08:00
0e8fdc2c5a 完善项目介绍 2025-07-02 09:29:45 +08:00
bd6051d972 完善项目介绍 2025-07-02 09:24:30 +08:00
9cfb9dd2b7 完善项目介绍 2025-07-02 09:22:03 +08:00
87b41ab36f 完善项目介绍 2025-07-01 20:26:43 +08:00
378f42bcb7 完善项目介绍 2025-07-01 20:16:38 +08:00
6838888f5f 完善项目介绍 2025-07-01 20:06:27 +08:00
bb8281107e 完善项目介绍 2025-07-01 19:39:35 +08:00
e9d44bfc80 完善项目介绍 2025-07-01 18:53:40 +08:00
8057172f8c 精简项目介绍 2025-07-01 18:45:13 +08:00
c6dd83a262 精简项目介绍 2025-07-01 18:40:42 +08:00
7469474124 项目版本介绍 2025-07-01 18:29:53 +08:00
dc9f36905d 项目版本介绍 2025-07-01 18:23:33 +08:00
30486c5358 项目版本介绍 2025-07-01 18:20:42 +08:00
5b7a90c8bc 项目版本介绍 2025-07-01 15:30:27 +08:00
ac40596ad6 项目版本介绍 2025-07-01 15:25:32 +08:00
6e0edc093b 项目版本介绍 2025-07-01 15:11:14 +08:00
2831996dd4 项目版本介绍 2025-07-01 13:59:06 +08:00
130793fffe 分支说明 2025-07-01 13:54:14 +08:00
5cabfe1655 分支说明 2025-07-01 13:53:02 +08:00
e99e29a523 分支说明 2025-07-01 13:49:35 +08:00
d64e8b0adb 代码生成器模板小优化:生成的列表默认吸底、代码生成年、月、日存储格式修改 2025-07-01 10:15:32 +08:00
4108ba54c3 代码严谨处理:解决部分用户反馈Cannot invoke "org.jeecg.common.api.CommonAPI.queryUserRolesById(String)" because "this.commonAPI" is null 2025-07-01 09:37:57 +08:00
e8ad887096 代码严谨处理:解决部分用户反馈Cannot invoke "org.jeecg.common.api.CommonAPI.queryUserRolesById(String)" because "this.commonAPI" is null 2025-07-01 09:24:04 +08:00
6c24b3cf30 打包electron:build-all 报错少个文件copyChat.ts 2025-06-30 16:02:29 +08:00
208861f808 新增QQ交流群:964611995 2025-06-30 14:24:28 +08:00
f7fe1dfc58 优化初始化脚本jeecgboot-mysql-5.7.sql大小 2025-06-30 09:38:46 +08:00
e2805050cf v3.8.1发布,代码生成支持关联记录组件 2025-06-26 11:50:22 +08:00
b34bb84fc9 v3.8.1发布,更新数据库 2025-06-26 11:30:27 +08:00
11beca16da v3.8.1发布,更新前端依赖 2025-06-26 10:56:18 +08:00
b0c9a2fd9e v3.8.1发布,升级Aiflow后端解决打包过大的问题 2025-06-26 10:56:02 +08:00
6b4e8695ae v3.8.1发布,暂时注释掉报错代码 2025-06-26 10:55:41 +08:00
e8ca74f2ab v3.8.1发布,升级Aiflow前端依赖版本号 2025-06-26 10:55:25 +08:00
eabf98533a v3.8.1发布,上传前端缺少文件 2025-06-26 09:53:12 +08:00
7848881dcb v3.8.1发布,微服务启动system提示mogodb提示错误 2025-06-25 17:51:51 +08:00
553bee57f1 v3.8.1发布,修改版本号 2025-06-25 17:37:33 +08:00
b751570789 v3.8.1发布,上传新版数据库脚本 2025-06-25 16:55:31 +08:00
e45d29c4ff v3.8.1发布,上传前端代码(暂时注释仪表盘缓存刷新功能) 2025-06-25 16:44:00 +08:00
8c8a448a71 v3.8.1发布,上传后台java代码 2025-06-25 16:34:26 +08:00
0148f45979 v3.8.1发布,上传前端代码 2025-06-25 16:04:02 +08:00
3d414aaec8 v3.8.1发布,上传代码 2025-06-25 15:16:00 +08:00
7a4c66af57 v3.8.1发布,上传代码 2025-06-25 13:34:13 +08:00
444c7140f6 解决TableAction中自定义图标颜色不起作用的问题 2025-06-25 09:55:07 +08:00
0fda30b5d2 修改接口地址,验证码404 2025-06-16 15:00:39 +08:00
c8e33d43cb 体验地址更改 2025-06-13 13:56:47 +08:00
6c6af870ae jeecgboot vs 敲敲云 2025-06-13 13:53:33 +08:00
79107599a6 增加jeecgboot vs 敲敲云 说明 2025-06-13 13:51:56 +08:00
b6e8b37aee 禁用不好使 2025-06-12 17:00:35 +08:00
195ddd0421 降低shiro版本号,导致不兼容jdk8 2025-06-11 22:18:48 +08:00
9ddad931ff 【issues/8374】分页始终显示在底部 2025-06-10 09:52:27 +08:00
fc05fe1aff 补充.npmrc 2025-06-09 09:22:21 +08:00
7e325f68ca Merge pull request #8389 from 1298191366/master
open api功能优化
2025-06-06 15:22:11 +08:00
f74da23d06 升级积木报表到最新版2.0.0 2025-06-06 11:21:50 +08:00
e279915ba2 --author:wangshuai---date:2025-06-06---for:【QQYUN-12667】open api功能优化--- 2025-06-06 09:54:55 +08:00
bb6f077a95 Revert "//update---author:wangshuai---date:2025-06-05---for:open api功能优化:ak接口返回未明确提示错误---"
This reverts commit 7a031e6135.
2025-06-06 09:35:43 +08:00
7a031e6135 //update---author:wangshuai---date:2025-06-05---for:open api功能优化:ak接口返回未明确提示错误--- 2025-06-06 09:35:27 +08:00
5972c74b43 解决积木报表springboot3 找不到类base64utils #3834和SqlServer兼容问题 2025-06-05 15:51:05 +08:00
157877f9a6 【issues/8233】resetFields时无法重置 2025-06-05 09:34:54 +08:00
25a71fa66c 【issues/8232】代码设置JSelectDept组件值没翻译 2025-06-05 09:34:11 +08:00
fdc02fa68a [-- author: liusq---date:20250604--for: issue【日志管理】的异常日志列表显示不正常 #8295 --] 2025-06-05 09:33:24 +08:00
ec3c34969a ---author:chenrui---date:2025/6/4-----for:[issues/8309]系统监控>请求追踪,列表每刷新一下,总数据就减一#8309 --- 2025-06-05 09:31:32 +08:00
5a215525d5 调整aksk测试数据 2025-05-29 16:07:30 +08:00
450b93d916 Merge pull request #8358 from xlh12306/master
使用原生方式对接口进行测试 修复授权管理页面重置AK,SK未写入到数据库中的异常
2025-05-29 15:56:13 +08:00
2d62bad2a9 使用原生方式对接口进行测试 修复授权管理页面重置AK,SK未写入到数据库中的异常
使用原生方式对接口进行测试
修复授权管理页面重置AK,SK未写入到数据库中的异常
2025-05-28 22:22:39 +08:00
0b10096f1c 修复微服务启动失败报错 2025-05-28 15:49:56 +08:00
431ddb8fcb 系统监控的头两个tab不好使,接口404--- 2025-05-27 22:51:25 +08:00
ddf0f61ae5 修复v3.8.0 存在绕过sql黑名单限制sql注入漏洞 #8335
修复minidao `getQueryTableInfo`无法解析带括号的表名、增加数据库名解析能力
2025-05-27 18:40:34 +08:00
4042579167 提供更新的数据库脚本,加入openapi的表和放开AI流程设计菜单 2025-05-27 09:51:52 +08:00
bd5fda5968 访问Swagger接口不带doc.html后缀,会丢失项目前缀/jeecg-boot/导致测试接口,返回下载文件 2025-05-26 19:03:29 +08:00
fdbd9c30ac 解决Swagger3Config 接口文档参数显示问题 #8325 2025-05-25 22:49:07 +08:00
b8b4d3f29d JVxetable升级到最新版 2025-05-25 18:09:19 +08:00
78212aa7c0 通讯录手机号码没有显示出来 #8282 2025-05-25 16:53:33 +08:00
6dc3c6af2a nacos增加aigc配置 2025-05-22 11:01:06 +08:00
b7a6812140 更新pom.xml,添加jeecg-boot-module-airag依赖 2025-05-22 09:25:39 +08:00
7efc51e30e 更新README.md,修改新手指南和在线体验部分的描述 2025-05-19 14:14:18 +08:00
bd83b994bc Merge pull request #8298 from xlh12306/master
openapi相关测试项修改
2025-05-18 17:01:28 +08:00
8fb81f331c 提交openAPiModel 2025-05-16 23:13:11 +08:00
fb188a83a1 提交openapi单元测试代码
调整接口管理的字段位置
授权管理删掉无用配置
授权管理编辑改为重置生成ak sk功能
授权页面应该改成跟角色授权类型的效果
2025-05-16 23:12:28 +08:00
1646 changed files with 44831 additions and 263599 deletions

View File

@ -10,6 +10,9 @@ assignees: getActivity
##### 版本号:
##### 分支:
##### 问题描述:

View File

@ -6,10 +6,12 @@ assignees: getActivity
---
##### 版本号:
##### 分支:
##### 问题描述:

2
.gitignore vendored
View File

@ -13,3 +13,5 @@ os_del.cmd
os_del_doc.cmd
.svn
derby.log
.cursor
.history

12
LICENSE
View File

@ -200,14 +200,4 @@
See the License for the specific language governing permissions and
limitations under the License.
In any case, you must not make any such use of this software as to develop software which may be considered competitive with this software.
JeecgBoot 是由 北京国炬信息技术有限公司 发行的软件。 总部位于北京地址中国·北京·朝阳区科荟前街1号院奥林佳泰大厦。邮箱jeecgos@163.com
本软件受适用的国家软件著作权法(包括国际条约)和开源协议 双重保护许可。
开源协议中文释意如下:
1.JeecgBoot开源版本无任何限制在遵循本开源协议条款下允许商用使用不会造成侵权行为。
2.允许基于本平台软件开展业务系统开发。
3.在任何情况下,您不得使用本软件开发可能被认为与本软件竞争的软件。
最终解释权归http://www.jeecg.com
In any case, you must not make any such use of this software as to develop software which may be considered competitive with this software.

View File

@ -1,164 +0,0 @@
AIGC应用平台介绍
===============
一个全栈式 AI 开发平台,旨在帮助开发者快速构建和部署个性化的 AI 应用。
> JDK说明AI流程编排引擎暂时不支持jdk21所以目前只能使用jdk8或者jdk17启动项目。
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流程 |
| 实现语言 | python + react | JAVA + vue3 |
| 上传markdown文档库(支持图片) | 不支持 | 支持 |
| AI对话支持发图和展示图片 | 支持 | 支持 |
### 安装向量库 pgvector
- https://help.jeecg.com/aigc/config
## 功能特点
- 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聊天助手快速嵌入到第三方系统让系统快速拥有智能问答能力提高用户满意度。
#### 在线体验
- JeecgBoot演示 https://boot3.jeecg.com
- 敲敲云在线搭建AI知识库https://app.qiaoqiaoyun.com
## 技术交流
- 开发文档https://help.jeecg.com/aigc
- QQ群716488839
## 功能列表
- 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

@ -1,420 +0,0 @@
![JEECG](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/logov3.png "JeecgBoot低代码开发平台")
JEECG BOOT AI Low Code Platform
===============
Current version: 3.8.0 (Release date: 2025-04-18)
[![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.8.0-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)
Project introduction
-----------------------------------
<h3 align="center">Java AI Low Code Platform</h3>
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!
The purpose of JEECG is: simple functions are implemented by OnlineCoding configuration, so that zero code development; Complex functions are generated by code generator and manually Merge to achieve low code development, which ensures both intelligence and flexibility. The implementation of low code development and support flexible coding at the same time, to solve the current low code products are generally not flexible drawbacks!
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?template=bug_report.md)
##### Project description
| Project | description |
|--------------------|------------------------|
| `jeecg-boot` | SpringBoot background source code (support microservices) |
| `jeecgboot-vue3` | Vue3+TS new front-end source code|
| `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
-----------------------------------
- APP SourceCodehttps://github.com/jeecgboot/jeecg-uniapp
For the project
-----------------------------------
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)
- [Docker Quick start](https://help.jeecg.com/java/docker/quick)
Technical documentation
-----------------------------------
- Website [http://www.jeecg.com](http://www.jeecg.com)
- Demo [OnlineDemo](http://boot3.jeecg.com) | [APP](http://jeecg.com/appIndex)
- 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
Star charts
-----------------------------------
[![Star History Chart](https://api.star-history.com/svg?repos=jeecgboot/jeecg-boot&type=Date)](https://star-history.com/#jeecgboot/jeecg-boot)
Background directory Structure
-----------------------------------
```
project structure
├─jeecg-boot-parent
│ ├─jeecg-boot-base-core
│ ├─jeecg-module-demo
│ ├─jeecg-module-system
│ │ ├─jeecg-system-biz
│ │ ├─jeecg-system-start system (8080
│ │ ├─jeecg-system-api
│ │ │ ├─jeecg-system-cloud-api
│ │ │ ├─jeecg-system-local-api
│ ├─jeecg-server-cloud
├─jeecg-cloud-gateway (9999)
├─jeecg-cloud-nacos --Nacos(8848)
├─jeecg-system-cloud-start --System(7001)
├─jeecg-demo-cloud-start --Demo(7002)
├─jeecg-visual
├─jeecg-cloud-monitor -- (9111)
├─jeecg-cloud-xxljob -- (9080)
├─jeecg-cloud-sentinel --sentinel (9000)
├─jeecg-cloud-test
├─jeecg-cloud-test-more
├─jeecg-cloud-test-rabbitmq
├─jeecg-cloud-test-seata
├─jeecg-cloud-test-shardingsphere
```
Why JeecgBoot?
-----------------------------------
* Adopt the latest mainstream front and back separation framework (Springboot+Mybatis+antd), easy to use; Code generator has low dependency, flexible expansion ability, and can quickly realize secondary development;
* Support microservices SpringCloud Alibaba(Nacos, Gateway, Sentinel, Skywalking), and provide switching mechanism to support free switching between single and microservices
* High development efficiency, using code generator, single table, tree list, one-to-many, one-to-one and other data models, add, delete, change and search function one-key generation, menu configuration directly use;
* Code generator provides powerful template mechanism, support custom template, currently provide four sets of style template (single table two sets, tree model one set, one to many three sets)
* Code generator is very intelligent, online business modeling, online configuration, WYSIWYG support 23 kinds of controls, a key to generate front and back end code, greatly improve the development efficiency, no longer worry about repeated work.
* Low code ability: Online online form (without coding, through online configuration of the form, to achieve the addition, deletion, change and check of the form, support single table, tree, one-to-many, one-to-one model, to achieve everyone can code)
* Low code ability: Online online report (without coding, through online configuration, to achieve data report, can quickly extract data, reduce development pressure, to achieve everyone can code)
* Low code ability: Online online chart (without coding, through online configuration, to achieve graphs, bar graphs, data reports, etc., support custom layout, to achieve everyone can code)
* Complete encapsulation of user, role, menu, organization, data dictionary, online scheduled tasks and other basic functions, support access authorization, button permission, data permission and other functions
* Commonly used common package, various tools (scheduled task, SMS interface, email sending,Excel import and export, etc.), basically meeting 80% of project requirements
* Easy Excel import and export, support single table export and one-to-many table mode export, generated code with import and export function
* Integrated simple report tools, image report and data export is very convenient, can be extremely convenient to generate graphical reports, pdf, excel, word and other reports;
* Before and after the separation technology, the page UI style is exquisite, for the commonly used components to do the encapsulation: time, row table control, interception display control, report component, editor and so on
* Query filter: query function automatically generated, the background dynamic spell SQL additional query conditions; Supports multiple matching modes (full matching, fuzzy query, included query, and unmatched query).
* Data permission (fine data permission control, control to row level, list level, form field level, realize different people see different data, different people operate different fields on the same page
* Page verification automatically generated (must be input, digital verification, amount verification, time and space, etc.);
* Support SAAS service model and provide SaaS multi-tenant architecture solution.
* Distributed file service, integration of minio, Ali OSS and other excellent third parties, to provide convenient file upload and management, but also support local storage.
* Mainstream database compatibility, a set of code is fully compatible with Mysql, Postgresql, Oracle, Sqlserver, MariaDB, dream and other mainstream databases.
* Integrate workflow flowable and realize only the configuration of flow direction in the page, which can greatly simplify the development of bpm workflow; Using bpm's process designer to draw the flow direction, a workflow is basically complete with a small amount of java code;
* Low code ability: online process design, using open source Activiti process engine, to achieve online drawing process, custom form, form attachment, business flow
* Multi-data source: its simple way of use, online configuration of data source configuration, convenient to grab data from other data;
* Provide single sign-on CAS integration solution, and complete docking code has been provided in the project
* Low code ability: form designer, support user custom form layout, support single table, one to many forms, support select, radio, checkbox, textarea, date, popup, list, macro and other controls
* Professional interface docking mechanism, unified using restful interface, integrated swagger-ui online interface documentation, Jwt token security verification, convenient client docking
* Interface security mechanism, can be refined control interface authorization, very simple to realize different clients only see their own data control
* Advanced combination query function, online configuration support primary and sub-table associated query, can save the query history
* Provide a variety of system monitoring, real-time tracking system running conditions (monitoring Redis, Tomcat, jvm, server information, request tracking, SQL monitoring)
* Message center (support SMS, email, wechat push, etc.)
* Integrate Websocket message notification mechanism
* Excellent mobile adaptive effect, providing APP release scheme:
* Support multiple languages and provide internationalization solutions;
* Data change record log, can record each change of data content, through the version comparison function to view historical changes
* The platform UI is powerful and mobile adaptation is implemented
* Platform home page style, provide a variety of combination mode, support custom style
* Provide easy to use print plug-in, support Google, Firefox, IE11+ and other browsers
* Rich sample code, provide a lot of learning case reference
* Using maven module development method
* Support dynamic menu routing
* RBAC (Role-Based Access Control) is used for permission control.
* Provide new row edit table JVXETable, easily meet a variety of complex ERP layout, with higher performance, more flexible extension, more powerful functions
Technical Architecture:
-----------------------------------
#### Development Environment
- Language: Java Default Jdk17(support jdk8、jdk21)
- IDE(JAVA) : IDEA (lombok plug-in must be installed)
- IDE(front-end) : Vscode, WebStorm, IDEA
- Dependency management: Maven
- Cache: Redis
- Database: MySQL5.7 + [More Databases](https://my.oschina.net/jeecg/blog/4905722)
#### backend
- Basic framework: Spring Boot 2.7.18
- Microservice framework: Spring Cloud Alibaba 2021.0.6.2
- Persistence layer framework: MybatisPlus 3.5.3.2
- Report tool: JimuReport 1.9.5
- 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.24
- Log printing: logback
- Others: autopoi, fastjson, poi, Swagger-ui, quartz, lombok (simplified code), etc.
#### The front end
- TechnologyStack`Vue3.0+TypeScript+Vite+AntDesignVue+pinia+echarts`
#### Front-end environment requirements
* `Node.js 、npm 、pnpm`
* pnpm `v9+` is now required.
* Node.js Version suggestion: `v20.15.0`
` ( Since Vite6 Node.js 18/20 + is now required )`
#### Support library
| database | support |
| --- | --- |
| MySQL | √ |
| Oracle11g | √ |
| Sqlserver2017 | √ |
| PostgreSQL | √ |
| MariaDB | √ |
| 达梦 | √ |
| 人大金仓 | √ |
| 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 √
- 2. Nacos √
- 3. Route gateway gateway(Three loading modes) √
- 4. Distributed http feign √
- 5. fuse degrade current limiting Sentinel √
- 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)
- 10. Messaging middleware RabbitMQ √
- 11. Distributed task xxl-job √
- 12. Distributed Transaction Seata
- 13. Distributed log Loki+grafana
- 14. Support docker-compose, k8s, jenkins
- 15. CAS SSO √
- 16. Route traffic limiting √
#### Microservice architecture diagram
![微服务架构图](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/jeecgboot_springcloud2022.png "在这里输入图片标题")
### Jeecg Boot product functionality blueprint
![功能蓝图](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)
- [Docker starts the micro-service background](https://help.jeecg.com/java/docker/springcloud)
### Effect of system
##### ChatGPT AI Dialog
> Go to the JeecgBoot background home page and click "AI Assistant" in the middle of the right side of the home page. The AI Assistant dialog screen is displayed.
![](https://oscimg.oschina.net/oscnet/up-7c6405641a40f56638999d52da0cb5b4343.png)
##### PC
![](https://oscimg.oschina.net/oscnet/up-000530d95df337b43089ac77e562494f454.png)
![输入图片说明](https://static.oschina.net/uploads/img/201904/14155402_AmlV.png "在这里输入图片标题")
![](https://oscimg.oschina.net/oscnet/up-9d6f36f251e71a0b515a01323474b03004c.png)
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160813_KmXS.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160935_Nibs.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14161004_bxQ4.png "在这里输入图片标题")
##### interactive
![](https://oscimg.oschina.net/oscnet/up-78b151fc888d4319377bf1cc311fe826871.png)
![](https://oscimg.oschina.net/oscnet/up-16c07e000278329b69b228ae3189814b8e9.png)
##### process Designer
![](https://oscimg.oschina.net/oscnet/up-981ce174e4fbb48c8a2ce4ccfd7372e2994.png)
![输入图片说明](https://static.oschina.net/uploads/img/201907/05165142_yyQ7.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160917_9Ftz.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160633_u59G.png "在这里输入图片标题")
##### min process
![](https://oscimg.oschina.net/oscnet/up-1dc0d052149ec675f3e4fad632b82b48add.png)
![](https://oscimg.oschina.net/oscnet/up-de31bc2f9d9b8332c554b0954cc73d79593.png)
![](https://oscimg.oschina.net/oscnet/up-7f83b25159663686d67ed080eb16068c3b4.png)
##### dashboard Designer
![](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/darg20240726105556.png)
![](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/drag20240724135626.png)
![](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/drag20240724135619.png)
![](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/drag20240724135630.png)
![](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/drag20240726105547.png)
![](https://oscimg.oschina.net/oscnet/up-fad98d42b2cf92f92a903c9cff7579f18ec.png)
##### report Designer
![](https://oscimg.oschina.net/oscnet/up-64648de000851f15f6c7b9573d107ebb5f8.png)
![](https://oscimg.oschina.net/oscnet/up-fa52b44445db281c51d3f267dce7450d21b.gif)
![](https://oscimg.oschina.net/oscnet/up-68a19149d640f1646c8ed89ed4375e3326c.png)
![](https://oscimg.oschina.net/oscnet/up-f7e9cb2e3740f2d19ff63b40ec2dd554f96.png)
##### form Designer
![](https://oscimg.oschina.net/oscnet/up-5f8cb657615714b02190b355e59f60c5937.png)
![](https://oscimg.oschina.net/oscnet/up-d9659b2f324e33218476ec98c9b400e6508.png)
![](https://oscimg.oschina.net/oscnet/up-4868615395272d3206dbb960ade02dbc291.png)
##### bigscreen Designer
![](https://oscimg.oschina.net/oscnet/up-402a6034124474bfef8dfc5b4b2bac1ce5c.png)
![](https://oscimg.oschina.net/oscnet/up-6f7ba2e2ebbeea0d203db8d69fd87644c9f.png)
![](https://oscimg.oschina.net/oscnet/up-ee8d34f318da466b8a6070a6e3111d12ce7.png)
![](https://oscimg.oschina.net/oscnet/up-6b81781b43086819049c4421206810667c5.png)
##### uniapp
![](https://oscimg.oschina.net/oscnet/up-aac943fbd26561879c57a41f7a406edf274.png)
![](https://oscimg.oschina.net/oscnet/up-9a44ba2e82b09c750629d12fafd7f60f553.png)
##### low app
![](https://oscimg.oschina.net/oscnet/up-4be29ae761b2615c8c54b3f668cd8432d9b.png)
![](https://oscimg.oschina.net/oscnet/up-787e76bc24b38ecc7ed19f338808d128255.png)
![](https://oscimg.oschina.net/oscnet/up-99d24a236c483362868523ad0d90f611487.png)
![](https://oscimg.oschina.net/oscnet/up-339a0f29d10449abc7724e3bcda802761c1.png)
![](https://oscimg.oschina.net/oscnet/up-b356670cdc14c609958c7619a537397c4b9.png)
##### app
![](https://oscimg.oschina.net/oscnet/da543c5d0d57baab0cecaa4670c8b68c521.jpg)
![](https://oscimg.oschina.net/oscnet/fda4bd82cab9d682de1c1fbf2060bf14fa6.jpg)
##### PAD
![](https://oscimg.oschina.net/oscnet/e90fef970a8c33790ab03ffd6c4c7cec225.jpg)
![](https://oscimg.oschina.net/oscnet/d78218803a9e856a0aa82b45efc49849a0c.jpg)
![](https://oscimg.oschina.net/oscnet/59c23b230f52384e588ee16309b44fa20de.jpg)
##### chart
![](https://oscimg.oschina.net/oscnet/up-218bc6a1669496b241ebb23506440c0083e.png)
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160834_Lo23.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160842_QK7B.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160849_GBm5.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160858_6RAM.png "在这里输入图片标题")
##### swagger
![输入图片说明](https://static.oschina.net/uploads/img/201908/27095258_M2Xq.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160957_hN3X.png "在这里输入图片标题")
## donation
If so, buy the author a cup of coffee ☺
![](https://static.oschina.net/uploads/img/201903/08155608_0EFX.png)

665
README.md
View File

@ -1,125 +1,30 @@
JeecgBoot AI低代码平台
Jeecg AI应用平台
===============
当前最新版本: 3.8.0发布日期2025-04-18
当前最新版本: 1.0.0发布日期2025-12-05
[![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.8.0-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)
[![](https://img.shields.io/badge/Author-北京国炬软件-orange.svg)](https://jeecg.com)
[![](https://img.shields.io/badge/blog-技术博客-orange.svg)](https://jeecg.blog.csdn.net)
[![](https://img.shields.io/badge/version-1.0.0-brightgreen.svg)](https://github.com/jeecgboot/jeecg-ai)
[![GitHub stars](https://img.shields.io/github/stars/jeecgboot/jeecg-ai.svg?style=social&label=Stars)](https://github.com/jeecgboot/jeecg-ai)
[![GitHub forks](https://img.shields.io/github/forks/jeecgboot/jeecg-ai.svg?style=social&label=Fork)](https://github.com/jeecgboot/jeecg-ai)
项目介绍
-----------------------------------
一个全栈式 AI 开发平台,旨在帮助开发者快速构建和部署个性化的 AI 应用。
<h3 align="center">Java AI Low Code Platform</h3>
JeecgBoot是一款基于AIGC和低代码引擎的AI低代码平台旨在帮助开发者快速实现低代码开发和构建、部署个性化的 AI 应用。
前后端分离架构Ant Design&Vue3SpringBootSpringCloud AlibabaMybatis-plusShiro强大的代码生成器让前后端代码一键生成无需写任何代码
成套AI大模型功能: AI模型管理、AI应用、知识库、AI流程编排、AI对话助手等
引领AI低代码开发模式: AIGC生成->OnlineCoding-> 代码生成-> 手工MERGE 帮助Java项目解决80%的重复工作,让开发更多关注业务,快速提高效率 节省成本,同时又不失灵活性!
JeecgBoot 提供了一系列 `低代码能力`,实现`真正的零代码`在线开发Online表单开发、Online报表、复杂报表设计、打印设计、在线图表设计、仪表盘设计、大屏设计、移动图表能力、表单设计器、在线设计流程、流程自动化配置、插件能力可插拔
`AI赋能低代码:` 目前提供了AI应用、AI模型管理、AI流程编排、AI对话助手AI建表、AI写文章、AI知识库问答、AI字段建议等功能;支持各种AI大模型ChatGPT、DeepSeek、Ollama、智普、千问等.
`JEECG宗旨是:` 简单功能由OnlineCoding配置实现做到`零代码开发`复杂功能由代码生成器生成进行手工Merge 实现`低代码开发`,既保证了`智能`又兼顾`灵活`;实现了低代码开发的同时又支持灵活编码,解决了当前低代码产品普遍不灵活的弊端!
`JEECG业务流程:` 采用工作流来实现、扩展出任务接口,供开发编写业务逻辑,表单提供多种解决方案: 表单设计器、online配置表单、编码表单。同时实现了流程与表单的分离设计松耦合、并支持任务节点灵活配置既保证了公司流程的保密性又减少了开发人员的工作量。
### 视频介绍
[![](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/flow_video.png)](https://www.bilibili.com/video/BV1Nk4y1o7Qc)
适用项目
-----------------------------------
JeecgBoot AI低代码平台可以应用在任何J2EE项目的开发中支持信创国产化。尤其适合SAAS项目、企业信息管理系统MIS、内部办公系统OA、企业资源计划系统ERP、客户关系管理系统CRM其半智能手工Merge的开发方式可以显著提高开发效率70%以上,极大降低开发成本。
又是一个全栈式 AI 开发平台,快速帮助企业构建和部署个性化的 AI 应用。
信创国产化
-----------------------------------
JeecgBoot 是一个开源低代码开发平台,支持全信创环境。它兼容多种国产操作系统和数据库,包括:
- 操作系统:国产麒麟、银河麒麟等国产系统几乎都是基于 Linux 内核,因此它们具有良好的兼容性。
- 数据库达梦、人大金仓、TiDB , [转库文档](https://my.oschina.net/jeecg/blog/4905722)
- 中间件:东方通 TongWeb、TongRDS宝兰德 AppServer、CacheDB, [信创配置文档](https://help.jeecg.com/java/tongweb-deploy/)
通过这些适配JeecgBoot 为使用国产软件和硬件的用户提供了高效的开发解决方案。
项目说明
-----------------------------------
| 项目名 | 说明 |
|--------------------|------------------------|
| `jeecg-boot` | 后端源码JAVASpringBoot微服务架构 |
| `jeecgboot-vue3` | 前端源码VUE3vue3+vite6+ts最新技术栈 |
| `JeecgUniapp` | [配套APP框架](https://github.com/jeecgboot/JeecgUniapp) 适配多个终端支持APP、小程序、H5 |
技术文档
-----------------------------------
- 官方网站: [http://www.jeecg.com](http://www.jeecg.com)
- 在线演示 [平台演示](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)
- [Docker一键启动前后端](https://help.jeecg.com/java/docker/quick)
AIGC应用平台介绍
-----------------------------------
JeecgBoot 平台的AIGC功能模块是一套类似`Dify``AIGC应用开发平台`+`知识库问答`是一款基于LLM大语言模型AI应用平台和 RAG 的知识库问答系统。
JeecgBoot平台的AIGC功能模块是一套类似`Dify``AIGC应用开发平台`+`知识库问答`是一款基于LLM大语言模型AI应用平台和 RAG 的知识库问答系统。
其直观的界面结合了 AI 流程编排、RAG 管道、知识库管理、模型管理、对接向量库、实时运行可观察等让您可以快速从原型到生产拥有AI服务能力。
> JDK说明AI流程编排引擎暂时不支持jdk21所以目前只能使用jdk8或者jdk17启动项目。
- [AIGC专题介绍页](README-AI.md)
- [AIGC开发文档](https://help.jeecg.com/aigc)
- [配置向量库PGVector](https://help.jeecg.com/aigc/config)
##### AI视频介绍
### AI视频介绍
[![](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/jeecg_aivideo.png)](https://www.bilibili.com/video/BV1zmd7YFE4w)
##### 在线体验
- JeecgBoot演示 https://boot3.jeecg.com
- 敲敲云在线搭建AI知识库https://app.qiaoqiaoyun.com
##### Dify `VS` JEECG AI
> JEECG AI与Dify相比在多个方面展现出显著的优势特别是在文档处理、格式和图片保持方面。以下是一些具体的优点
> - Markdown文档库导入
> JEECG AI允许用户直接导入整个Markdown文档库这不仅保留markdown格式还支持图片的导入确保文档内容的完整性和可视化效果。
> - 对话回复格式美观:
> 在对话过程中JEECG AI能够保持回复内容的原格式也不丢失图片使得输出的文章更加美观不会出现格式错乱的情况还支持图片的渲染。
> - PDF文档导入与格式转换
> JEECG AI在处理PDF文档时能够更好地保持原始格式和图片确保转换后的内容与原始文档一致。这个功能在许多AI产品中表现不佳而JEECG AI在这方面做出了显著的优化
##### 功能大模块
- AI应用开发平台
@ -132,459 +37,183 @@ JeecgBoot 平台的AIGC功能模块是一套类似`Dify`的`AIGC应用开发
- AI写文章CMS
- AI表单字段建议表单设计器
##### AI大模型支持
| AI大模型 | 支持 |
| --- | --- |
| DeepSeek | √ |
| ChatGTP | √ |
| Qwq | √ |
| 智库 | √ |
| Ollama本地模型 | √ |
| 等等。。 | √ |
#### 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流程 |
| 实现语言 | python + react | JAVA + vue3 |
| 上传markdown文档库(支持图片) | 不支持 | 支持 |
| AI对话支持发图和展示图片 | 支持 | 支持 |
### 技术文档
- [AIGC开发文档](https://help.jeecg.com/aigc)
- [安装向量库 pgvector](https://help.jeecg.com/aigc/config)
## 功能特点
- 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聊天助手快速嵌入到第三方系统让系统快速拥有智能问答能力提高用户满意度。
#### 在线体验
- JeecgBoot演示 https://boot3.jeecg.com
- 敲敲云在线搭建AI知识库https://app.qiaoqiaoyun.com
## 技术交流
- 开发文档https://help.jeecg.com/aigc
- QQ群964611995、716488839(满)
## 功能列表
- 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脚本实现操作电脑文件等。
技术架构:
-----------------------------------
#### 前端
- 前端环境要求Node.js要求`Node 20+` 版本以上、pnpm 要求`9+` 版本以上
` ( Vite 不再支持已结束生命周期EOL的 Node.js 18。现在需要使用 Node.js 20.19+ 或 22.12+)`
- 依赖管理node、npm、pnpm
- 前端IDE建议IDEA、WebStorm、Vscode
- 采用 Vue3.0+TypeScript+Vite6+Ant-Design-Vue4等新技术方案包括二次封装组件、utils、hooks、动态菜单、权限校验、按钮级别权限控制等功能
- 最新技术栈Vue3.0 + TypeScript + Vite6 + ant-design-vue4 + pinia + echarts + unocss + vxe-table + qiankun + es6
#### 后端
- IDE建议 IDEA (必须安装lombok插件 )
- 语言Java 默认jdk17(支持jdk8、jdk21)
- 语言Java 默认jdk17(jdk21、jdk24)
- 依赖管理Maven
- 基础框架Spring Boot 2.7.18
- 微服务框架: Spring Cloud Alibaba 2021.0.6.2
- 持久层框架MybatisPlus 3.5.3.2
- 报表工具: JimuReport 1.9.5
- 安全框架Apache Shiro 1.13.0Jwt 4.5.0
- 基础框架Spring Boot 3.5.5
- 微服务框架: Spring Cloud Alibaba 2023.0.3.3
- 持久层框架MybatisPlus 3.5.12
- 报表工具: JimuReport 2.1.3
- 安全框架Apache Shiro 2.0.4Jwt 4.5.0
- 微服务技术栈Spring Cloud Alibaba、Nacos、Gateway、Sentinel、Skywalking
- 数据库连接池阿里巴巴Druid 1.1.24
- AI大模型支持 `ChatGPT` `DeepSeek`切换
- 数据库连接池阿里巴巴Druid 1.2.24
- AI大模型支持 `ChatGPT` `DeepSeek` `千问`等各种常规模式
- 日志打印logback
- 缓存Redis
- 其他autopoi, fastjsonpoiSwagger-uiquartz, lombok简化代码等。
- 默认数据库脚本:MySQL5.7+
- [其他数据库,需要自己转](https://my.oschina.net/jeecg/blog/4905722)
- 默认提供MySQL5.7+数据库脚本
#### 前端
## AI应用平台功能展示
- 前端IDE建议WebStorm、Vscode
- 采用 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
AI模型列表
![](https://oscimg.oschina.net/oscnet//a5fb3e0d69ca1706b0de221535c7acaa.png)
#### 前端环境要求
选择AI模型配置你的参数
* 本地环境安装 `Node.js 、npm 、pnpm`
* pnpm 要求`9+` 版本以上
* Node.js 版本建议`v20.15.0`,要求`Node 20+` 版本以上
![](https://oscimg.oschina.net/oscnet//1f941472758a5fc227f54f2683953b8e.png)
` ( 因为Vite6 需要 Node.js 18 / 20+ )`
AI知识库支持手工录入文本导入pdf\\word\\excel等文档支持问答对训练
#### 平台支持数据库
![](https://oscimg.oschina.net/oscnet//150bb33f48d6c8e2ae059e2a58f4200b.png)
> jeecgboot平台支持以下数据库默认我们只提供mysql脚本其他数据库可以参考[转库文档](https://my.oschina.net/jeecg/blog/4905722)自己转。
![](https://oscimg.oschina.net/oscnet//032d16c915b0f79318935484c81df260.png)
| 数据库 | 支持 |
| --- | --- |
| MySQL | √ |
| Oracle11g | √ |
| Sqlserver2017 | √ |
| PostgreSQL | √ |
| MariaDB | √ |
| 达梦 | √ |
| 人大金仓 | √ |
| TiDB | √ |
| kingbase8 | √ |
## 微服务解决方案
AI流程提供强大的AI流程设计器引擎支持编排 AI 工作过程,满足复杂业务场景,支持画布上构建和实时运行查看 AI流程运行情况。
- 1、服务注册和发现 Nacos √
- 2、统一配置中心 Nacos √
- 3、路由网关 gateway(三种加载方式) √
- 4、分布式 http feign √
- 5、熔断降级限流 Sentinel √
- 6、分布式文件 Minio、阿里OSS √
- 7、统一权限控制 JWT + Shiro √
- 8、服务监控 SpringBootAdmin√
- 9、链路跟踪 Skywalking [参考文档](https://help.jeecg.com/java/springcloud/super/skywarking)
- 10、消息中间件 RabbitMQ √
- 11、分布式任务 xxl-job √
- 12、分布式事务 Seata
- 13、轻量分布式日志 Loki+grafana套件
- 14、支持 docker-compose、k8s、jenkins
- 15、CAS 单点登录 √
- 16、路由限流 √
![](https://oscimg.oschina.net/oscnet//f40f9aa275cd4aea94e1c209513151e2.png)
#### 微服务方式启动
目前支持的节点有开始、结束、AI知识库节点、AI节点、分类节点、分支节点、JAVA节点、脚本节点、子流程节点、http请求节点、直接回复节点等节点
- [单体快速切换微服务](https://help.jeecg.com/java/springcloud/switchcloud/monomer)
- [Docker一键启动微服务前后端](https://help.jeecg.com/java/docker/quickcloud)
![](https://oscimg.oschina.net/oscnet//6d86480ab1bbfab5b2e6992b416b2152.png)
节点项配置
#### 微服务架构图
![微服务架构图](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/jeecgboot_springcloud2022.png "在这里输入图片标题")
![](https://oscimg.oschina.net/oscnet//90a5f76b6b4fc406e2e2b87245b35459.png)
在线运行看结果
![](https://oscimg.oschina.net/oscnet//bc9817a7bbd94936a5a3e885abe3cb38.png)
为什么选择JeecgBoot?
-----------------------------------
* 1.采用最新主流前后分离框架Springboot+Mybatis+antd+vue3容易上手; 代码生成器依赖性低,灵活的扩展能力,可快速实现二次开发;
* 2.支持微服务SpringCloud Alibaba(Nacos、Gateway、Sentinel、Skywalking),提供切换机制支持单体和微服务自由切换
* 3.开发效率高,采用代码生成器单表、树列表、一对多、一对一等数据模型增删改查功能一键生成菜单配置直接使用引入AI能力支持自动建表等功能
* 4.代码生成器提供强大模板机制,支持自定义模板,目前提供四套风格模板(单表两套、树模型一套、一对多三套)
* 5.代码生成器非常智能在线业务建模、在线配置、所见即所得支持23种类控件一键生成前后端代码大幅度提升开发效率不再为重复工作发愁。
* 6.AI能力目前JeecgBoot支持AI大模型chatgpt和deepseek现在最新版默认使用deepseek速度更快质量更高。目前提供了AI对话助手、AI建表、AI报表等功能。
* 6.低代码能力Online在线表单无需编码通过在线配置表单实现表单的增删改查支持单表、树、一对多、一对一等模型实现人人皆可编码
* 7.低代码能力Online在线报表、Online在线图表无需编码通过在线配置方式实现数据报表和图形报表可以快速抽取数据减轻开发压力实现人人皆可编码
* 9.封装完善的用户、角色、菜单、组织机构、数据字典、在线定时任务等基础功能,支持访问授权、按钮权限、数据权限等功能
* 10.常用共通封装,各种工具类(定时任务,短信接口,邮件发送,Excel导入导出等),基本满足80%项目需求
* 11.简易Excel导入导出支持单表导出和一对多表模式导出生成的代码自带导入导出功能
* 12.集成简易报表工具图像报表和数据导出非常方便可极其方便的生成图形报表、pdf、excel、word等报表
* 13.采用前后分离技术页面UI风格精美针对常用组件做了封装时间、行表格控件、截取显示控件、报表组件编辑器等等
* 14.查询过滤器查询功能自动生成后台动态拼SQL追加查询条件支持多种匹配方式全匹配/模糊查询/包含查询/不匹配查询);
* 15.数据权限(精细化数据权限控制,控制到行级,列表级,表单字段级,实现不同人看不同数据,不同人对同一个页面操作不同字段
* 16.页面校验自动生成(必须输入、数字校验、金额校验、时间空间等);
* 17.支持SAAS服务模式提供SaaS多租户架构方案。
* 18.分布式文件服务集成minio、阿里OSS等优秀的第三方提供便捷的文件上传与管理同时也支持本地存储。
* 19.主流数据库兼容一套代码完全兼容Mysql、Postgresql、Oracle、Sqlserver、MariaDB、达梦、人大金仓等主流数据库。
* 20.集成工作流flowable并实现了只需在页面配置流程转向可极大的简化bpm工作流的开发用bpm的流程设计器画出了流程走向一个工作流基本就完成了只需写很少量的java代码
* 21.低代码能力在线流程设计采用开源flowable流程引擎实现在线画流程,自定义表单,表单挂靠,业务流转
* 22.多数据源:及其简易的使用方式,在线配置数据源配置,便捷的从其他数据抓取数据;
* 23.提供单点登录CAS集成方案项目中已经提供完善的对接代码
* 24.低代码能力表单设计器支持用户自定义表单布局支持单表一对多表单、支持select、radio、checkbox、textarea、date、popup、列表、宏等控件
* 25.专业接口对接机制统一采用restful接口方式集成swagger-ui在线接口文档Jwt token安全验证方便客户端对接
* 26.接口安全机制,可细化控制接口授权,非常简便实现不同客户端只看自己数据等控制
* 27.高级组合查询功能,在线配置支持主子表关联查询,可保存查询历史
* 28.提供各种系统监控,实时跟踪系统运行情况(监控 Redis、Tomcat、jvm、服务器信息、请求追踪、SQL监控
* 29.消息中心(支持短信、邮件、微信推送等等)
* 30.集成Websocket消息通知机制
* 31.移动自适应效果优秀提供APP发布方案
* 32.支持多语言,提供国际化方案;
* 33.数据变更记录日志,可记录数据每次变更内容,通过版本对比功能查看历史变化
* 34.平台UI强大实现了移动自适应
* 35.平台首页风格,提供多种组合模式,支持自定义风格
* 36.提供简单易用的打印插件支持谷歌、火狐、IE11+ 等各种浏览器
* 37.示例代码丰富,提供很多学习案例参考
* 38.采用maven分模块开发方式
* 39.支持菜单动态路由
* 40.权限控制采用 RBACRole-Based Access Control基于角色的访问控制
* 41.提供新行编辑表格JVXETable轻松满足各种复杂ERP布局拥有更高的性能、更灵活的扩展、更强大的功能
* 42.提供仪表盘设计器,类大屏设计支持移动端,免费的数据可视化设计工具,支持丰富的数据源连接,能够通过拖拉拽方式快速制作图表和门户设计;目前支持多种图表类型:柱形图、折线图、散点图、饼图、环形图、面积图、漏斗图、进度图、仪表盘、雷达图、地图等等;
AI应用配置支持AI流程配置和简单的AI配置
### Jeecg Boot 产品功能蓝图
![功能蓝图](https://jeecgos.oss-cn-beijing.aliyuncs.com/upload/test/Jeecg-Boot-lantu202005_1590912449914.jpg "在这里输入图片标题")
![](https://oscimg.oschina.net/oscnet//a853d9be4d3756806799ad025e722df8.png)![](https://oscimg.oschina.net/oscnet//d3bcbf5977c6fb75a8f996e1e40590be.png)
可以关联多个知识库右侧是AI智能回复你可以搭建自己的智能体比如搭建一个 “诗词达人” “翻译助手”
![](https://oscimg.oschina.net/oscnet//c26a848136be3e22ec1e0651e78976c2.png)
可以将创建的聊天应用,集成到第三方系统中
### 分支说明
> 主干master更稳定如果你对最新技术栈无要求建议采用主干
#### springboot3分支
- 源码地址https://github.com/jeecgboot/JeecgBoot/tree/springboot3
- 架构说明升级Spring Boot3 & JDK 17 + Undertow + springdoc + fastjson2
#### springboot3_sas分支
- 源码地址https://github.com/jeecgboot/JeecgBoot/tree/springboot3_sas
- 架构说明在springboot3分支基础上采用SpringAuthorizationServer替换Shiro
### 功能模块
```
├─AI开发
│ ├─支持AI大模型ChatGPT和DeepSeek
│ ├─AI对话助手
│ ├─AI建表
│ ├─AI写文章
│ ├─AI流程编排
│ ├─AI知识库问答系统
│ ├─AI应用开发平台
│ ├─AI聊天窗口支持嵌入第三方
├─Online在线开发(低代码)
│ ├─Online在线表单
│ ├─Online代码生成器
│ ├─Online在线报表
│ ├─仪表盘设计器
│ ├─系统编码规则
│ ├─系统校验规则
├─积木报表设计器
│ ├─打印设计器
│ ├─数据报表设计
│ ├─图形报表设计支持echart
├─系统管理
│ ├─用户管理
│ ├─角色管理
│ ├─菜单管理
│ ├─权限设置(支持按钮权限、数据权限)
│ ├─表单权限(控制字段禁用、隐藏)
│ ├─部门管理
│ ├─我的部门(二级管理员)
│ └─字典管理
│ └─分类字典
│ └─系统公告
│ └─职务管理
│ └─通讯录
│ ├─多数据源管理
│ └─多租户管理(租户管理、租户角色、我的租户)
├─消息中心
│ ├─消息管理
│ ├─模板管理
├─代码生成器(低代码)
│ ├─代码生成器功能(一键生成前后端代码,生成后无需修改直接用,绝对是后端开发福音)
│ ├─代码生成器模板提供4套模板分别支持单表和一对多模型不同风格选择
│ ├─代码生成器模板生成代码自带excel导入导出
│ ├─查询过滤器(查询逻辑无需编码,系统根据页面配置自动生成)
│ ├─高级查询器(弹窗自动组合查询条件)
│ ├─Excel导入导出工具集成支持单表一对多 导入导出)
│ ├─平台移动自适应支持
├─系统监控
│ ├─Gateway路由网关
│ ├─性能扫描监控
│ │ ├─监控 Redis
│ │ ├─Tomcat
│ │ ├─jvm
│ │ ├─服务器信息
│ │ ├─请求追踪
│ │ ├─磁盘监控
│ ├─定时任务
│ ├─系统日志
│ ├─消息中心(支持短信、邮件、微信推送等等)
│ ├─数据日志(记录数据快照,可对比快照,查看数据变更情况)
│ ├─系统通知
│ ├─SQL监控
│ ├─swagger-ui(在线接口文档)
│─报表示例
│ ├─曲线图
│ └─饼状图
│ └─柱状图
│ └─折线图
│ └─面积图
│ └─雷达图
│ └─仪表图
│ └─进度条
│ └─排名列表
│ └─等等
│─大屏模板
│ ├─作战指挥中心大屏
│ └─物流服务中心大屏
│─常用示例
│ ├─自定义组件
│ ├─对象存储(对接阿里云)
│ ├─JVXETable示例各种复杂ERP布局示例
│ ├─单表模型例子
│ └─一对多模型例子
│ └─打印例子
│ └─一对多TAB例子
│ └─内嵌table例子
│ └─常用选择组件
│ └─异步树table
│ └─接口模拟测试
│ └─表格合计示例
│ └─异步树列表示例
│ └─一对多JEditable
│ └─JEditable组件示例
│ └─图片拖拽排序
│ └─图片翻页
│ └─图片预览
│ └─PDF预览
│ └─分屏功能
│─封装通用组件
│ ├─行编辑表格JEditableTable
│ └─省略显示组件
│ └─时间控件
│ └─高级查询
│ └─用户选择组件
│ └─报表组件封装
│ └─字典组件
│ └─下拉多选组件
│ └─选人组件
│ └─选部门组件
│ └─通过部门选人组件
│ └─封装曲线、柱状图、饼状图、折线图等等报表的组件(经过封装,使用简单)
│ └─在线code编辑器
│ └─上传文件组件
│ └─验证码组件
│ └─树列表组件
│ └─表单禁用组件
│ └─等等
│─更多页面模板
│ ├─各种高级表单
│ ├─各种列表效果
│ └─结果页面
│ └─异常页面
│ └─个人页面
├─高级功能
│ ├─提供单点登录CAS集成方案
│ ├─提供APP发布方案
│ ├─集成Websocket消息通知机制
│─更多商业功能
│ ├─流程设计器
│ ├─表单设计器
│ ├─大屏设计器
│ └─我的任务
│ └─历史流程
│ └─历史流程
│ └─流程实例管理
│ └─流程监听管理
│ └─流程表达式
│ └─我发起的流程
│ └─我的抄送
│ └─流程委派、抄送、跳转
│ └─OA办公组件
│ └─。。。
```
### 系统效果
##### 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)
![输入图片说明](https://static.oschina.net/uploads/img/201904/14155402_AmlV.png "在这里输入图片标题")
![](https://oscimg.oschina.net/oscnet/up-9d6f36f251e71a0b515a01323474b03004c.png)
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160813_KmXS.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160935_Nibs.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14161004_bxQ4.png "在这里输入图片标题")
##### 系统交互
![](https://oscimg.oschina.net/oscnet/up-78b151fc888d4319377bf1cc311fe826871.png)
![](https://oscimg.oschina.net/oscnet/up-16c07e000278329b69b228ae3189814b8e9.png)
##### 仪表盘设计器
![](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/darg20240726105556.png)
![](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/drag20240724135626.png)
![](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/drag20240724135619.png)
![](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/drag20240724135630.png)
![](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/drag20240726105547.png)
![](https://oscimg.oschina.net/oscnet/up-fad98d42b2cf92f92a903c9cff7579f18ec.png)
##### 报表设计器
![](https://oscimg.oschina.net/oscnet/up-64648de000851f15f6c7b9573d107ebb5f8.png)
![](https://oscimg.oschina.net/oscnet/up-fa52b44445db281c51d3f267dce7450d21b.gif)
![](https://oscimg.oschina.net/oscnet/up-68a19149d640f1646c8ed89ed4375e3326c.png)
![](https://oscimg.oschina.net/oscnet/up-f7e9cb2e3740f2d19ff63b40ec2dd554f96.png)
##### 手机端
![](https://oscimg.oschina.net/oscnet/da543c5d0d57baab0cecaa4670c8b68c521.jpg)
![](https://oscimg.oschina.net/oscnet/fda4bd82cab9d682de1c1fbf2060bf14fa6.jpg)
##### PAD端
![](https://oscimg.oschina.net/oscnet/e90fef970a8c33790ab03ffd6c4c7cec225.jpg)
![](https://oscimg.oschina.net/oscnet/d78218803a9e856a0aa82b45efc49849a0c.jpg)
![](https://oscimg.oschina.net/oscnet/59c23b230f52384e588ee16309b44fa20de.jpg)
##### 图表示例
![](https://oscimg.oschina.net/oscnet/up-218bc6a1669496b241ebb23506440c0083e.png)
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160834_Lo23.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160842_QK7B.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160849_GBm5.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160858_6RAM.png "在这里输入图片标题")
##### 在线接口文档
![输入图片说明](https://static.oschina.net/uploads/img/201908/27095258_M2Xq.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160957_hN3X.png "在这里输入图片标题")
##### UNIAPP效果
![](https://oscimg.oschina.net/oscnet/up-aac943fbd26561879c57a41f7a406edf274.png)
![](https://oscimg.oschina.net/oscnet/up-9a44ba2e82b09c750629d12fafd7f60f553.png)
##### 大屏设计器
![](https://oscimg.oschina.net/oscnet/up-402a6034124474bfef8dfc5b4b2bac1ce5c.png)
![](https://oscimg.oschina.net/oscnet/up-6f7ba2e2ebbeea0d203db8d69fd87644c9f.png)
![](https://oscimg.oschina.net/oscnet/up-ee8d34f318da466b8a6070a6e3111d12ce7.png)
![](https://oscimg.oschina.net/oscnet/up-6b81781b43086819049c4421206810667c5.png)
##### 流程设计
![](https://oscimg.oschina.net/oscnet/up-981ce174e4fbb48c8a2ce4ccfd7372e2994.png)
![](https://oscimg.oschina.net/oscnet/up-1dc0d052149ec675f3e4fad632b82b48add.png)
![](https://oscimg.oschina.net/oscnet/up-de31bc2f9d9b8332c554b0954cc73d79593.png)
![输入图片说明](https://static.oschina.net/uploads/img/201907/05165142_yyQ7.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160917_9Ftz.png "在这里输入图片标题")
![输入图片说明](https://static.oschina.net/uploads/img/201904/14160633_u59G.png "在这里输入图片标题")
##### 表单设计器
![](https://oscimg.oschina.net/oscnet/up-5f8cb657615714b02190b355e59f60c5937.png)
![](https://oscimg.oschina.net/oscnet/up-d9659b2f324e33218476ec98c9b400e6508.png)
![](https://oscimg.oschina.net/oscnet/up-4868615395272d3206dbb960ade02dbc291.png)
## 捐赠
如果觉得还不错,请作者喝杯咖啡吧 ☺
![](https://static.oschina.net/uploads/img/201903/08155608_0EFX.png)
![](https://oscimg.oschina.net/oscnet//39c6f589ef46f0454b229915ffa263f4.png)

216
check_jeecgenv.py Normal file
View File

@ -0,0 +1,216 @@
import os
import subprocess
import re
import sys
from typing import Tuple, Optional
def run_command(cmd: str) -> Tuple[int, str]:
"""执行命令并返回退出码和输出"""
try:
result = subprocess.run(cmd, shell=True, check=False,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
text=True)
return result.returncode, result.stdout.strip()
except Exception as e:
return -1, str(e)
def check_java() -> bool:
"""检查JDK 17+是否安装"""
print("\n检查JDK 17+...")
rc, output = run_command("java -version 2>&1")
if rc != 0:
print("❌ 未检测到Java请安装JDK 17+")
return False
version_pattern = r'"(\d+)(?:\.\d+)*(?:_\d+)?'
match = re.search(version_pattern, output)
if not match:
print("❌ 无法解析Java版本")
return False
version = int(match.group(1))
if version >= 17:
print(f"✅ JDK版本 {version} (满足17+要求)")
return True
else:
print(f"❌ JDK版本 {version} (需要17+)")
return False
def check_maven() -> bool:
"""检查Maven是否安装"""
print("\n检查Maven...")
rc, output = run_command("mvn -v")
if rc == 0:
print("✅ Maven已安装")
return True
else:
print("❌ Maven未安装")
return False
def check_node() -> bool:
"""检查Node.js 20+是否安装"""
print("\n检查Node.js 20+...")
rc, output = run_command("node -v")
if rc != 0:
print("❌ Node.js未安装")
return False
version_pattern = r'v(\d+)\.\d+\.\d+'
match = re.search(version_pattern, output)
if not match:
print("❌ 无法解析Node.js版本")
return False
version = int(match.group(1))
if version >= 20:
print(f"✅ Node.js版本 {version} (满足20+要求)")
return True
else:
print(f"❌ Node.js版本 {version} (需要20+)")
return False
def check_pnpm() -> bool:
"""检查PNPM 9+是否安装"""
print("\n检查PNPM 9+...")
rc, output = run_command("pnpm -v")
if rc != 0:
print("❌ PNPM未安装")
return False
try:
# 处理可能的版本号格式v9.0.0 或 9.0.0 或 9
version_str = output.strip().lstrip('v').split('.')[0]
version = int(version_str)
if version >= 9:
print(f"✅ PNPM版本 {output.strip()} (满足9+要求)")
return True
else:
print(f"❌ PNPM版本 {output.strip()} (需要9+)")
return False
except (ValueError, IndexError):
print(f"❌ 无法解析PNPM版本: {output.strip()}")
return False
def check_redis_connection() -> bool:
"""检查Redis连接"""
print("\n检查Redis连接...")
print("⚠️ 请确保已配置Redis连接信息并在jeecg-boot项目中正确配置")
print("⚠️ 此检查需要根据实际项目配置进行验证")
print("⚠️ 配置文件位置: jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml")
return True
def check_mysql_connection() -> bool:
"""检查MySQL连接"""
print("\n检查MySQL连接...")
print("⚠️ 请确保已配置MySQL连接信息并在jeecg-boot项目中正确配置")
print("⚠️ 此检查需要根据实际项目配置进行验证")
print("⚠️ 配置文件位置: jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml")
return True
def print_mysql_config():
"""打印MySQL配置并提示需要修改的位置"""
print("\nMySQL配置参考 (请检查以下配置是否正确):")
print("""
spring.datasource.dynamic.datasource:
master:
url: jdbc:mysql://127.0.0.1:3306/jeecg-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root # ← 可能需要修改
password: root # ← 可能需要修改
driver-class-name: com.mysql.cj.jdbc.Driver
""")
def check_ai_vector_db() -> bool:
"""检查AI向量库(pgvector)配置"""
print("\n检查AI知识库向量库配置...")
print("⚠️ 如果需要使用AI知识库功能请配置pgvector向量库")
print("⚠️ 配置文件位置: jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml")
print("\n配置参考:")
print("""
jeecg.ai-rag:
embed-store:
host: 127.0.0.1 # ← 可能需要修改
port: 5432 # ← 可能需要修改
database: postgres # ← 可能需要修改
user: postgres # ← 可能需要修改
password: postgres # ← 可能需要修改
table: embeddings # ← 可能需要修改
""")
print("⚠️ 注意: 请确保已安装PostgreSQL并添加pgvector扩展docker安装参考https://help.jeecg.com/aigc/config")
return True
def check_ai_config() -> bool:
"""检查AI账号配置"""
print("\n检查AI功能配置...")
print("⚠️ 如果需要使用AI聊天功能请配置AI账号信息")
print("⚠️ 配置文件位置: jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml")
print("\n配置参考:")
print("""
jeecg:
# AI集成
ai-chat:
enabled: true # ← 启用AI功能
model: deepseek-chat # ← 模型名称
apiKey: ?? # ← 必须修改为您的API Key
apiHost: https://api.deepseek.com/v1 # ← API地址
timeout: 60 # ← 超时时间(秒)
""")
print("⚠️ 注意: 请确保已获取有效的API Key并正确配置AI账号注册获取参考 https://help.jeecg.com/java/deepSeekSupport")
return True
def print_redis_config():
"""打印Redis配置并提示需要修改的位置"""
print("\nRedis配置参考 (请检查以下配置是否正确):")
print("""
spring.redis:
database: 0
host: 127.0.0.1 # ← 可能需要修改
port: 6379 # ← 可能需要修改
password: '' # ← 如果需要密码请修改
""")
def main():
print("="*50)
print("JeecgBoot 运行环境检查脚本")
print("="*50)
all_checks_passed = True
# 检查各项依赖
if not check_java():
all_checks_passed = False
if not check_maven():
all_checks_passed = False
if not check_node():
all_checks_passed = False
if not check_pnpm():
all_checks_passed = False
# 数据库提示
print("="*50)
check_redis_connection()
print_redis_config()
print("="*50)
check_mysql_connection()
print_mysql_config()
print("="*50)
check_ai_config()
print("="*50)
check_ai_vector_db()
print("\n" + "="*50)
if all_checks_passed:
print("✅ 所有基础环境检查通过")
print("⚠️ 注意: 请确保Redis和MySQL、AI账号、向量库pgvector 已正确配置并连接成功")
else:
print("❌ 部分环境检查未通过,请根据上述提示解决问题")
print("="*50)
if __name__ == "__main__":
main()
input("\n按回车键退出...") # 等待用户输入

View File

@ -1,135 +0,0 @@
version: '2'
services:
jeecg-boot-mysql:
build:
context: ./jeecg-boot/db
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_ROOT_HOST: '%'
TZ: Asia/Shanghai
restart: always
container_name: jeecg-boot-mysql
image: jeecg-boot-mysql
command:
--character-set-server=utf8mb4
--collation-server=utf8mb4_general_ci
--explicit_defaults_for_timestamp=true
--lower_case_table_names=1
--max_allowed_packet=128M
--default-authentication-plugin=caching_sha2_password
ports:
- 3306:3306
networks:
- jeecg-boot
jeecg-boot-redis:
image: registry.cn-hangzhou.aliyuncs.com/jeecgdocker/redis:5.0
ports:
- 6379:6379
restart: always
hostname: jeecg-boot-redis
container_name: jeecg-boot-redis
networks:
- jeecg-boot
jeecg-boot-nacos:
restart: always
build:
context: ./jeecg-boot/jeecg-server-cloud/jeecg-cloud-nacos
ports:
- 8848:8848
container_name: jeecg-boot-nacos
depends_on:
- jeecg-boot-mysql
hostname: jeecg-boot-nacos
networks:
- jeecg-boot
jeecg-boot-system:
depends_on:
- jeecg-boot-nacos
build:
context: ./jeecg-boot/jeecg-server-cloud/jeecg-system-cloud-start
container_name: jeecg-system-start
hostname: jeecg-boot-system
restart: on-failure
environment:
- TZ=Asia/Shanghai
networks:
- jeecg-boot
jeecg-boot-demo:
depends_on:
- jeecg-boot-nacos
build:
context: ./jeecg-boot/jeecg-server-cloud/jeecg-demo-cloud-start
container_name: jeecg-demo-start
hostname: jeecg-boot-demo
restart: on-failure
environment:
- TZ=Asia/Shanghai
networks:
- jeecg-boot
jeecg-boot-gateway:
restart: on-failure
build:
context: ./jeecg-boot/jeecg-server-cloud/jeecg-cloud-gateway
ports:
- 9999:9999
depends_on:
- jeecg-boot-nacos
- jeecg-boot-system
container_name: jeecg-boot-gateway
hostname: jeecg-boot-gateway
networks:
- jeecg-boot
# jeecg-boot-rabbitmq:
# image: rabbitmq:3.7.7-management
# ports:
# - 5672:5672
# - 15672:15672
# restart: always
# container_name: jeecg-boot-rabbitmq
# hostname: jeecg-boot-rabbitmq
# environment:
# RABBITMQ_DEFAULT_USER: guest
# RABBITMQ_DEFAULT_PASS: guest
# jeecg-boot-sentinel:
# restart: on-failure
# build:
# context: ./jeecg-visual/jeecg-cloud-sentinel
# ports:
# - 9000:9000
# depends_on:
# - jeecg-boot-nacos
# - jeecg-boot-demo
# - jeecg-boot-system
# - jeecg-boot-gateway
# container_name: jeecg-boot-sentinel
# hostname: jeecg-boot-sentinel
#
# jeecg-boot-xxljob:
# build:
# context: ./jeecg-visual/jeecg-cloud-xxljob
# ports:
# - 9080:9080
# container_name: jeecg-boot-xxljob
# hostname: jeecg-boot-xxljob
jeecg-vue:
build:
context: ./jeecgboot-vue3
container_name: jeecgboot-vue3-nginx
image: jeecgboot-vue3
depends_on:
- jeecg-boot-system
networks:
- jeecg-boot
ports:
- 80:80
networks:
jeecg-boot:
name: jeecg_boot

View File

@ -18,20 +18,33 @@ services:
--max_allowed_packet=128M
--default-authentication-plugin=caching_sha2_password
ports:
- 3306:3306
- 13306:3306
networks:
- jeecg-boot
jeecg-boot-redis:
image: registry.cn-hangzhou.aliyuncs.com/jeecgdocker/redis:5.0
ports:
- 6379:6379
# ports:
# - 3792:6379
restart: always
hostname: jeecg-boot-redis
container_name: jeecg-boot-redis
networks:
- jeecg-boot
jeecg-boot-pgvector:
image: registry.cn-hangzhou.aliyuncs.com/jeecgdocker/pgvector
container_name: jeecg-boot-pgvector
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: vector_db
ports:
- 5432:5432
restart: always
networks:
- jeecg-boot
jeecg-boot-system:
build:
context: ./jeecg-boot/jeecg-module-system/jeecg-system-start

View File

@ -198,16 +198,4 @@
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
In any case, you must not make any such use of this software as to develop software which may be considered competitive with this software.
JeecgBoot 是由 北京国炬信息技术有限公司 发行的软件。 总部位于北京地址中国·北京·朝阳区科荟前街1号院奥林佳泰大厦。邮箱jeecgos@163.com
本软件受适用的国家软件著作权法(包括国际条约)和开源协议 双重保护许可。
开源协议中文释意如下:
1.JeecgBoot开源版本无任何限制在遵循本开源协议条款下允许商用使用不会造成侵权行为。
2.允许基于本平台软件开展业务系统开发。
3.在任何情况下,您不得使用本软件开发可能被认为与本软件竞争的软件。
最终解释权归http://www.jeecg.com
limitations under the License.

View File

@ -1,165 +0,0 @@
JeecgBoot 低代码开发平台
===============
当前最新版本: 3.8.0发布日期2025-05-16
[![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.8.0-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)
项目介绍
-----------------------------------
<h3 align="center">Java Low Code Platform for Enterprise web applications</h3>
JeecgBoot 是一款基于代码生成器的`低代码开发平台`!前后端分离架构 SpringBoot2.x和3.xSpringCloudAnt Design Vue3Mybatis-plusShiroJWT支持微服务。强大的代码生成器让前后端代码一键生成实现低代码开发! JeecgBoot 引领新的低代码开发模式(OnlineCoding-> 代码生成器-> 手工MERGE) 帮助解决Java项目70%的重复工作,让开发更多关注业务。既能快速提高效率,节省研发成本,同时又不失灵活性!
#### 项目说明
| 项目名 | 说明 |
|--------------------|------------------------|
| `jeecg-boot` | 后端源码JAVASpringBoot微服务架构 |
| `jeecgboot-vue3` | 前端源码VUE3vue3+vite5+ts最新技术栈 |
技术文档
-----------------------------------
- 官方网站: [http://www.jeecg.com](http://www.jeecg.com)
- 新手指南: [快速入门](http://www.jeecg.com/doc/quickstart)
- QQ交流群 ⑩716488839、⑨808791225、其他(满)
- 在线演示 [在线演示](http://boot3.jeecg.com) | [APP演示](http://jeecg.com/appIndex)
> 演示系统的登录账号密码,请点击 [获取账号密码](http://jeecg.com/doc/demo) 获取
启动项目
-----------------------------------
- [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)
- [Docker启动微服务后台](https://help.jeecg.com/java/docker/springcloud)
技术架构:
-----------------------------------
#### 后端
- IDE建议 IDEA (必须安装lombok插件 )
- 语言Java 8+ (支持17)
- 依赖管理Maven
- 基础框架Spring Boot 2.7.18
- 微服务框架: Spring Cloud Alibaba 2021.0.1.0
- 持久层框架MybatisPlus 3.5.3.2
- 报表工具: JimuReport 1.9.4
- 安全框架Apache Shiro 1.12.0Jwt 3.11.0
- 微服务技术栈Spring Cloud Alibaba、Nacos、Gateway、Sentinel、Skywalking
- 数据库连接池阿里巴巴Druid 1.1.24
- 日志打印logback
- 缓存Redis
- 其他autopoi, fastjsonpoiSwagger-uiquartz, lombok简化代码等。
- 默认数据库脚本MySQL5.7+
- [其他数据库,需要自己转](https://my.oschina.net/jeecg/blog/4905722)
#### 前端
- 前端IDE建议WebStorm、Vscode
- 采用 Vue3.0+TypeScript+Vite+Ant-Design-Vue等新技术方案包括二次封装组件、utils、hooks、动态菜单、权限校验、按钮级别权限控制等功能
- 最新技术栈Vue3.0 + TypeScript + Vite5 + ant-design-vue4 + pinia + echarts + unocss + vxe-table + qiankun + es6
- 依赖管理node、npm、pnpm
#### 支持库
| 数据库 | 支持 |
| --- | --- |
| MySQL | √ |
| Oracle11g | √ |
| Sqlserver2017 | √ |
| PostgreSQL | √ |
| MariaDB | √ |
| 达梦 | √ |
| 人大金仓 | √ |
| TiDB | √ |
## 微服务解决方案
- 1、服务注册和发现 Nacos √
- 2、统一配置中心 Nacos √
- 3、路由网关 gateway(三种加载方式) √
- 4、分布式 http feign √
- 5、熔断降级限流 Sentinel √
- 6、分布式文件 Minio、阿里OSS √
- 7、统一权限控制 JWT + Shiro √
- 8、服务监控 SpringBootAdmin√
- 9、链路跟踪 Skywalking [参考文档](https://help.jeecg.com/java/springcloud/super/skywarking)
- 10、消息中间件 RabbitMQ √
- 11、分布式任务 xxl-job √
- 12、分布式事务 Seata
- 13、轻量分布式日志 Loki+grafana套件
- 14、支持 docker-compose、k8s、jenkins
- 15、CAS 单点登录 √
- 16、路由限流 √
后台目录结构
-----------------------------------
```
项目结构
├─jeecg-boot-parent父POM 项目依赖、modules组织
│ ├─jeecg-boot-base-core共通模块 工具类、config、权限、查询过滤器、注解等
│ ├─jeecg-module-demo 示例代码
│ ├─jeecg-module-system System系统管理目录
│ │ ├─jeecg-system-biz System系统管理权限等功能
│ │ ├─jeecg-system-start System单体启动项目(8080
│ │ ├─jeecg-system-api System系统管理模块对外api
│ │ │ ├─jeecg-system-cloud-api System模块对外提供的微服务接口
│ │ │ ├─jeecg-system-local-api System模块对外提供的单体接口
│ ├─jeecg-server-cloud --微服务模块
├─jeecg-cloud-gateway --微服务网关模块(9999)
├─jeecg-cloud-nacos --Nacos服务模块(8848)
├─jeecg-system-cloud-start --System微服务启动项目(7001)
├─jeecg-demo-cloud-start --Demo微服务启动项目(7002)
├─jeecg-visual
├─jeecg-cloud-monitor --微服务监控模块 (9111)
├─jeecg-cloud-xxljob --微服务xxljob定时任务服务端 (9080)
├─jeecg-cloud-sentinel --sentinel服务端 (9000)
├─jeecg-cloud-test -- 微服务测试示例(各种例子)
├─jeecg-cloud-test-more -- 微服务测试示例feign、熔断降级、xxljob、分布式锁
├─jeecg-cloud-test-rabbitmq -- 微服务测试示例rabbitmq
├─jeecg-cloud-test-seata -- 微服务测试示例seata分布式事务
├─jeecg-cloud-test-shardingsphere -- 微服务测试示例(分库分表)
```
#### 微服务架构图
![微服务架构图](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/jeecgboot_springcloud2022.png "在这里输入图片标题")

View File

@ -8,6 +8,6 @@ RUN ln -sf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
COPY ./tables_nacos.sql /docker-entrypoint-initdb.d
COPY ./jeecgboot-mysql-5.7.sql /docker-entrypoint-initdb.d
COPY ./jeecgai-mysql-5.7.sql /docker-entrypoint-initdb.d
COPY ./tables_xxl_job.sql /docker-entrypoint-initdb.d

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,360 +0,0 @@
#
# XXL-JOB v2.2.0
# Copyright (c) 2015-present, xuxueli.
CREATE database if NOT EXISTS `xxl_job` default character set utf8mb4 collate utf8mb4_general_ci;
use `xxl_job`;
/*
Navicat Premium Data Transfer
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_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;
-- ----------------------------
-- 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) 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 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 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;
-- ----------------------------
-- 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 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;
-- ----------------------------
-- 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) 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;
-- ----------------------------
-- Records of xxl_job_logglue
-- ----------------------------
-- ----------------------------
-- 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,
`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;
-- ----------------------------
-- 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) 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) 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;
-- ----------------------------
-- Records of xxl_job_user
-- ----------------------------
INSERT INTO `xxl_job_user` VALUES (1, 'admin', 'e10adc3949ba59abbe56e057f20f883e', 1, NULL);
SET FOREIGN_KEY_CHECKS = 1;

View File

@ -1,170 +0,0 @@
/*
Navicat Premium Data Transfer
Source Server : mysql5.7
Source Server Type : MySQL
Source Server Version : 50738 (5.7.38)
Source Host : 127.0.0.1:3306
Source Schema : jeecg-boot
Target Server Type : MySQL
Target Server Version : 50738 (5.7.38)
File Encoding : 65001
Date: 15/05/2025 10:18:36
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for open_api
-- ----------------------------
DROP TABLE IF EXISTS `open_api`;
CREATE TABLE `open_api` (
`id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '接口名称',
`request_method` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '请求方法',
`request_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '接口地址',
`black_list` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'IP 黑名单',
`body` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '请求体内容',
`origin_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '原始地址',
`status` int(10) NULL DEFAULT NULL COMMENT '状态',
`del_flag` int(10) NULL DEFAULT NULL COMMENT '删除标识',
`create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '修改人',
`update_time` datetime NULL DEFAULT NULL COMMENT '修改时间',
`headers_json` json NULL COMMENT '请求头json',
`params_json` json NULL COMMENT '请求参数json',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '接口表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of open_api
-- ----------------------------
INSERT INTO `open_api` VALUES ('1922132683346649090', '根据部门查询用户', 'GET', 'TEwcXBlr', NULL, NULL, '/sys/user/queryUserByDepId', 1, 0, 'admin', '2025-05-13 11:31:58', 'admin', '2025-05-15 10:10:01', '[]', '[{\"id\": \"row_24\", \"note\": \"\", \"paramKey\": \"id\", \"required\": \"1\", \"defaultValue\": \"\"}]');
-- ----------------------------
-- Table structure for open_api_auth
-- ----------------------------
DROP TABLE IF EXISTS `open_api_auth`;
CREATE TABLE `open_api_auth` (
`id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '授权名称',
`ak` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'AK',
`sk` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT 'SK',
`create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '修改人',
`update_time` datetime NULL DEFAULT NULL COMMENT '修改时间',
`system_user_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '关联系统用户名',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '权限表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of open_api_auth
-- ----------------------------
INSERT INTO `open_api_auth` VALUES ('1922164194775056386', 'scott', 'ak-pFjyNHWRsJEFWlu6', '4hV5dBrZtmGAtPdbA5yseaeKRYNpzGsS', 'admin', '2025-05-13 13:37:11', NULL, NULL, 'e9ca23d68d884d4ebb19d07889727dae');
-- ----------------------------
-- Table structure for open_api_log
-- ----------------------------
DROP TABLE IF EXISTS `open_api_log`;
CREATE TABLE `open_api_log` (
`id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`api_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '接口ID',
`call_auth_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '调用ID',
`call_time` datetime NULL DEFAULT NULL COMMENT '调用时间',
`used_time` bigint(20) NULL DEFAULT NULL COMMENT '耗时',
`response_time` datetime NULL DEFAULT NULL COMMENT '响应时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '调用记录表' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of open_api_log
-- ----------------------------
INSERT INTO `open_api_log` VALUES ('1922175238557913090', '1922132683346649090', '1922164194775056386', '2025-05-13 14:21:04', 94, '2025-05-13 14:21:04');
INSERT INTO `open_api_log` VALUES ('1922175436256432130', '1922132683346649090', '1922164194775056386', '2025-05-13 14:21:51', 38, '2025-05-13 14:21:51');
INSERT INTO `open_api_log` VALUES ('1922175487921868802', '1922132683346649090', '1922164194775056386', '2025-05-13 14:22:03', 31, '2025-05-13 14:22:03');
INSERT INTO `open_api_log` VALUES ('1922176033789562883', '1922132683346649090', '1922164194775056386', '2025-05-13 14:24:13', 27, '2025-05-13 14:24:13');
INSERT INTO `open_api_log` VALUES ('1922176583943835650', '1922132683346649090', '1922164194775056386', '2025-05-13 14:26:25', 39, '2025-05-13 14:26:25');
INSERT INTO `open_api_log` VALUES ('1922177249969934337', '1922132683346649090', '1922164194775056386', '2025-05-13 14:28:08', 55250, '2025-05-13 14:29:03');
INSERT INTO `open_api_log` VALUES ('1922180212645941249', '1922132683346649090', '1922164194775056386', '2025-05-13 14:40:46', 4162, '2025-05-13 14:40:50');
INSERT INTO `open_api_log` VALUES ('1922180441692688385', '1922132683346649090', '1922164194775056386', '2025-05-13 14:41:11', 33346, '2025-05-13 14:41:44');
INSERT INTO `open_api_log` VALUES ('1922180521686454273', '1922132683346649090', '1922164194775056386', '2025-05-13 14:42:00', 3570, '2025-05-13 14:42:03');
INSERT INTO `open_api_log` VALUES ('1922180965825499138', '1922132683346649090', '1922164194775056386', '2025-05-13 14:42:10', 99211, '2025-05-13 14:43:49');
INSERT INTO `open_api_log` VALUES ('1922181034515615746', '1922132683346649090', '1922164194775056386', '2025-05-13 14:43:52', 14005, '2025-05-13 14:44:06');
INSERT INTO `open_api_log` VALUES ('1922183171307982850', '1922132683346649090', '1922164194775056386', '2025-05-13 14:52:15', 19834, '2025-05-13 14:52:35');
INSERT INTO `open_api_log` VALUES ('1922184177068523521', '1922132683346649090', '1922164194775056386', '2025-05-13 14:56:34', 748, '2025-05-13 14:56:35');
INSERT INTO `open_api_log` VALUES ('1922184729043107841', '1922132683346649090', '1922164194775056386', '2025-05-13 14:58:46', 1031, '2025-05-13 14:58:47');
INSERT INTO `open_api_log` VALUES ('1922184806453182465', '1922132683346649090', '1922164194775056386', '2025-05-13 14:59:05', 68, '2025-05-13 14:59:05');
INSERT INTO `open_api_log` VALUES ('1922184918382379009', '1922132683346649090', '1922164194775056386', '2025-05-13 14:59:10', 22155, '2025-05-13 14:59:32');
INSERT INTO `open_api_log` VALUES ('1922185292635844610', '1922132683346649090', '1922164194775056386', '2025-05-13 15:00:55', 6267, '2025-05-13 15:01:01');
INSERT INTO `open_api_log` VALUES ('1922186002672791554', '1922132683346649090', '1922164194775056386', '2025-05-13 15:03:23', 27554, '2025-05-13 15:03:50');
INSERT INTO `open_api_log` VALUES ('1922187506582425601', '1922132683346649090', '1922164194775056386', '2025-05-13 15:09:45', 3464, '2025-05-13 15:09:49');
INSERT INTO `open_api_log` VALUES ('1922187586597163011', '1922132683346649090', '1922164194775056386', '2025-05-13 15:10:08', 82, '2025-05-13 15:10:08');
INSERT INTO `open_api_log` VALUES ('1922187924741951490', '1922132683346649090', '1922164194775056386', '2025-05-13 15:10:49', 39590, '2025-05-13 15:11:28');
INSERT INTO `open_api_log` VALUES ('1922188138710261761', '1922132683346649090', '1922164194775056386', '2025-05-13 15:12:19', 758, '2025-05-13 15:12:19');
INSERT INTO `open_api_log` VALUES ('1922188290661507073', '1922132683346649090', '1922164194775056386', '2025-05-13 15:12:29', 26527, '2025-05-13 15:12:56');
INSERT INTO `open_api_log` VALUES ('1922189701755424769', '1922132683346649090', '1922164194775056386', '2025-05-13 15:18:28', 3619, '2025-05-13 15:18:32');
INSERT INTO `open_api_log` VALUES ('1922190076784803841', '1922132683346649090', '1922164194775056386', '2025-05-13 15:20:01', 741, '2025-05-13 15:20:02');
INSERT INTO `open_api_log` VALUES ('1922836671113101313', '1922132683346649090', '1922164194775056386', '2025-05-15 10:09:21', 186, '2025-05-15 10:09:22');
INSERT INTO `open_api_log` VALUES ('1922836856287428610', '1922132683346649090', '1922164194775056386', '2025-05-15 10:10:06', 145, '2025-05-15 10:10:06');
-- ----------------------------
-- Table structure for open_api_permission
-- ----------------------------
DROP TABLE IF EXISTS `open_api_permission`;
CREATE TABLE `open_api_permission` (
`id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL,
`api_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '接口ID',
`api_auth_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '认证ID',
`create_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '创建人',
`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL COMMENT '更新人',
`update_time` datetime NULL DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'openapi授权' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of open_api_permission
-- ----------------------------
INSERT INTO `open_api_permission` VALUES ('1922164225875820545', '1922132683346649090', '1922164194775056386', 'admin', '2025-05-13 13:37:18', NULL, NULL);
SET FOREIGN_KEY_CHECKS = 1;
INSERT INTO sys_permission (id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) VALUES ('1917957565728198657', '1922109301837606914', '接口文档', '/openapi/SwaggerUI', 'openapi/SwaggerUI', 1, '', null, 1, null, '0', 1, 0, null, 1, 0, 0, 0, null, 'admin', '2025-05-01 23:01:32', 'admin', '2025-05-13 09:59:46', 0, 0, null, 0);
INSERT INTO sys_permission (id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) VALUES ('1922109301837606914', '', 'OpenApi管理', '/openapi', 'layouts/RouteView', 1, '', null, 0, null, '0', 12.1, 0, 'ant-design:swap-outlined', 0, 0, 0, 0, null, 'admin', '2025-05-13 09:59:03', 'admin', '2025-05-13 10:02:43', 0, 0, null, 0);
INSERT INTO sys_permission (id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) VALUES ('2025050104193340030', '1922109301837606914', '接口管理', '/openapi/openApiList', 'openapi/OpenApiList', 1, null, null, 1, null, '1', 0, 0, null, 0, 0, 0, 0, null, 'admin', '2025-05-01 16:19:03', 'admin', '2025-05-13 09:59:24', 0, 0, '1', 0);
INSERT INTO sys_permission (id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) VALUES ('2025050104193350031', '2025050104193340030', '添加接口管理', null, null, 0, null, null, 2, 'openapi:open_api:add', '1', null, 0, null, 1, 0, 0, 0, null, 'admin', '2025-05-01 16:19:03', null, null, 0, 0, '1', 0);
INSERT INTO sys_permission (id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) VALUES ('2025050104193350032', '2025050104193340030', '编辑接口管理', null, null, 0, null, null, 2, 'openapi:open_api:edit', '1', null, 0, null, 1, 0, 0, 0, null, 'admin', '2025-05-01 16:19:03', null, null, 0, 0, '1', 0);
INSERT INTO sys_permission (id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) VALUES ('2025050104193350033', '2025050104193340030', '删除接口管理', null, null, 0, null, null, 2, 'openapi:open_api:delete', '1', null, 0, null, 1, 0, 0, 0, null, 'admin', '2025-05-01 16:19:03', null, null, 0, 0, '1', 0);
INSERT INTO sys_permission (id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) VALUES ('2025050104193350034', '2025050104193340030', '批量删除接口管理', null, null, 0, null, null, 2, 'openapi:open_api:deleteBatch', '1', null, 0, null, 1, 0, 0, 0, null, 'admin', '2025-05-01 16:19:03', null, null, 0, 0, '1', 0);
INSERT INTO sys_permission (id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) VALUES ('2025050104193350035', '2025050104193340030', '导出excel_接口管理', null, null, 0, null, null, 2, 'openapi:open_api:exportXls', '1', null, 0, null, 1, 0, 0, 0, null, 'admin', '2025-05-01 16:19:03', null, null, 0, 0, '1', 0);
INSERT INTO sys_permission (id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) VALUES ('2025050104193350036', '2025050104193340030', '导入excel_接口管理', null, null, 0, null, null, 2, 'openapi:open_api:importExcel', '1', null, 0, null, 1, 0, 0, 0, null, 'admin', '2025-05-01 16:19:03', null, null, 0, 0, '1', 0);
INSERT INTO sys_permission (id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) VALUES ('2025050105554940200', '1922109301837606914', '授权管理', '/openapi/openApiAuthList', 'openapi/OpenApiAuthList', 1, null, null, 1, null, '1', 0, 0, null, 0, 0, 0, 0, null, 'admin', '2025-05-01 17:55:20', 'admin', '2025-05-13 09:59:35', 0, 0, '1', 0);
INSERT INTO sys_permission (id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) VALUES ('2025050105554940201', '2025050105554940200', '添加授权管理', null, null, 0, null, null, 2, 'openapi:open_api_auth:add', '1', null, 0, null, 1, 0, 0, 0, null, 'admin', '2025-05-01 17:55:20', null, null, 0, 0, '1', 0);
INSERT INTO sys_permission (id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) VALUES ('2025050105554940202', '2025050105554940200', '编辑授权管理', null, null, 0, null, null, 2, 'openapi:open_api_auth:edit', '1', null, 0, null, 1, 0, 0, 0, null, 'admin', '2025-05-01 17:55:20', null, null, 0, 0, '1', 0);
INSERT INTO sys_permission (id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) VALUES ('2025050105554940203', '2025050105554940200', '删除授权管理', null, null, 0, null, null, 2, 'openapi:open_api_auth:delete', '1', null, 0, null, 1, 0, 0, 0, null, 'admin', '2025-05-01 17:55:20', null, null, 0, 0, '1', 0);
INSERT INTO sys_permission (id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) VALUES ('2025050105554940204', '2025050105554940200', '批量删除授权管理', null, null, 0, null, null, 2, 'openapi:open_api_auth:deleteBatch', '1', null, 0, null, 1, 0, 0, 0, null, 'admin', '2025-05-01 17:55:20', null, null, 0, 0, '1', 0);
INSERT INTO sys_permission (id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) VALUES ('2025050105554940205', '2025050105554940200', '导出excel_授权管理', null, null, 0, null, null, 2, 'openapi:open_api_auth:exportXls', '1', null, 0, null, 1, 0, 0, 0, null, 'admin', '2025-05-01 17:55:20', null, null, 0, 0, '1', 0);
INSERT INTO sys_permission (id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) VALUES ('2025050105554940206', '2025050105554940200', '导入excel_授权管理', null, null, 0, null, null, 2, 'openapi:open_api_auth:importExcel', '1', null, 0, null, 1, 0, 0, 0, null, 'admin', '2025-05-01 17:55:20', null, null, 0, 0, '1', 0);
INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip) VALUES ('1917957659860963330', 'f6817f48af4fb3af11b9e8bf182f618b', '1917957565728198657', null, '2025-05-01 23:01:55', '0:0:0:0:0:0:0:1');
INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip) VALUES ('1922109760551858178', 'f6817f48af4fb3af11b9e8bf182f618b', '1922109301837606914', null, '2025-05-13 10:00:53', '0:0:0:0:0:0:0:1');
INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip) VALUES ('1917857071739539457', 'f6817f48af4fb3af11b9e8bf182f618b', '2025050104193340030', null, '2025-05-01 16:22:13', '0:0:0:0:0:0:0:1');
INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip) VALUES ('1917857071806648321', 'f6817f48af4fb3af11b9e8bf182f618b', '2025050104193350031', null, '2025-05-01 16:22:13', '0:0:0:0:0:0:0:1');
INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip) VALUES ('1917857071806648322', 'f6817f48af4fb3af11b9e8bf182f618b', '2025050104193350032', null, '2025-05-01 16:22:13', '0:0:0:0:0:0:0:1');
INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip) VALUES ('1917857071806648323', 'f6817f48af4fb3af11b9e8bf182f618b', '2025050104193350033', null, '2025-05-01 16:22:13', '0:0:0:0:0:0:0:1');
INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip) VALUES ('1917857071806648324', 'f6817f48af4fb3af11b9e8bf182f618b', '2025050104193350034', null, '2025-05-01 16:22:13', '0:0:0:0:0:0:0:1');
INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip) VALUES ('1917857071806648325', 'f6817f48af4fb3af11b9e8bf182f618b', '2025050104193350035', null, '2025-05-01 16:22:13', '0:0:0:0:0:0:0:1');
INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip) VALUES ('1917857071806648326', 'f6817f48af4fb3af11b9e8bf182f618b', '2025050104193350036', null, '2025-05-01 16:22:13', '0:0:0:0:0:0:0:1');
INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip) VALUES ('1917881149426864129', 'f6817f48af4fb3af11b9e8bf182f618b', '2025050105554940200', null, '2025-05-01 17:57:53', '0:0:0:0:0:0:0:1');
INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip) VALUES ('1917881149431058436', 'f6817f48af4fb3af11b9e8bf182f618b', '2025050105554940203', null, '2025-05-01 17:57:53', '0:0:0:0:0:0:0:1');
INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip) VALUES ('1917881149431058437', 'f6817f48af4fb3af11b9e8bf182f618b', '2025050105554940204', null, '2025-05-01 17:57:53', '0:0:0:0:0:0:0:1');
INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip) VALUES ('1917881149431058438', 'f6817f48af4fb3af11b9e8bf182f618b', '2025050105554940205', null, '2025-05-01 17:57:53', '0:0:0:0:0:0:0:1');
INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip) VALUES ('1917881149431058439', 'f6817f48af4fb3af11b9e8bf182f618b', '2025050105554940206', null, '2025-05-01 17:57:53', '0:0:0:0:0:0:0:1');
INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip) VALUES ('1917957659860963330', 'f6817f48af4fb3af11b9e8bf182f618b', '1917957565728198657', null, '2025-05-01 23:01:55', '0:0:0:0:0:0:0:1');
INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, operate_date, operate_ip) VALUES ('1922109760551858178', 'f6817f48af4fb3af11b9e8bf182f618b', '1922109301837606914', null, '2025-05-13 10:00:53', '0:0:0:0:0:0:0:1');

View File

@ -1,23 +0,0 @@
# 版本升级方法
> JeecgBoot属于平台级产品每次升级改动较大目前做不到平滑升级。
### 增量升级方案
#### 1.代码合并
本地通过svn或git做好主干在分支上做业务开发jeecg每次版本发布可以手工覆盖主干的代码对比合并代码
#### 2.数据库升级
- 从3.6.2+版本增加flyway自动升级数据库机制支持 mysql5.7、mysql8;
- 其他库请手工执行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.兼容问题
每次发版,会针对不兼容地方重点说明。

View File

@ -18,24 +18,52 @@ services:
--max_allowed_packet=128M
--default-authentication-plugin=caching_sha2_password
ports:
- 3306:3306
- 13306:3306
networks:
- jeecg-boot
jeecg-boot-redis:
image: registry.cn-hangzhou.aliyuncs.com/jeecgdocker/redis:5.0
ports:
- 6379:6379
# ports:
# - 6379:6379
restart: always
hostname: jeecg-boot-redis
container_name: jeecg-boot-redis
networks:
- jeecg-boot
jeecg-boot-pgvector:
image: registry.cn-hangzhou.aliyuncs.com/jeecgdocker/pgvector
container_name: jeecg-boot-pgvector
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: vector_db
# ports:
# - 5432:5432
restart: always
networks:
- jeecg-boot
# jeecg-boot-rabbitmq:
# image: rabbitmq:3.7.7-management
## ports:
## - 5672:5672
## - 15672:15672
# restart: always
# container_name: jeecg-boot-rabbitmq
# hostname: jeecg-boot-rabbitmq
# environment:
# RABBITMQ_DEFAULT_USER: guest
# RABBITMQ_DEFAULT_PASS: guest
# networks:
# - jeecg-boot
jeecg-boot-system:
build:
context: ./jeecg-module-system/jeecg-system-start
restart: on-failure
mac_address: 02:42:ac:11:00:02
depends_on:
- jeecg-boot-mysql
- jeecg-boot-redis
@ -46,6 +74,8 @@ services:
- 8080:8080
networks:
- jeecg-boot
volumes:
- ./config:/jeecg-boot/config
networks:
jeecg-boot:

View File

@ -2,9 +2,9 @@
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.jeecgframework.boot</groupId>
<groupId>org.jeecgframework.boot3</groupId>
<artifactId>jeecg-boot-parent</artifactId>
<version>3.8.1</version>
<version>3.9.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>jeecg-boot-base-core</artifactId>
@ -42,23 +42,19 @@
<dependencies>
<!--jeecg-tools-->
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-common3</artifactId>
<groupId>org.jeecgframework.boot3</groupId>
<artifactId>jeecg-boot-common</artifactId>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--集成springmvc框架并实现自动配置 -->
<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>
@ -117,11 +113,10 @@
<version>${mybatis-plus.version}</version>
</dependency>
<!-- minidao -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-jsqlparser-4.9</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>org.jeecgframework.boot3</groupId>
<artifactId>minidao-spring-boot-starter-jsqlparser-4.9</artifactId>
</dependency>
<!-- druid -->
<dependency>
@ -148,7 +143,7 @@
<!-- sqlserver-->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>sqljdbc4</artifactId>
<artifactId>mssql-jdbc</artifactId>
<version>${sqljdbc4.version}</version>
<scope>runtime</scope>
</dependency>
@ -170,13 +165,13 @@
<dependency>
<groupId>org.jeecgframework</groupId>
<artifactId>kingbase8</artifactId>
<version>9.0.0</version>
<version>${kingbase8.version}</version>
<scope>runtime</scope>
</dependency>
<!--达梦数据库驱动 版本号1-3-26-2023.07.26-197096-20046-ENT -->
<dependency>
<groupId>com.dameng</groupId>
<artifactId>Dm8JdbcDriver18</artifactId>
<artifactId>DmJdbcDriver18</artifactId>
<version>${dm8.version}</version>
</dependency>
<dependency>
@ -184,7 +179,6 @@
<artifactId>DmDialect-for-hibernate5.0</artifactId>
<version>${dm8.version}</version>
</dependency>
<!-- Quartz定时任务 -->
<dependency>
<groupId>org.springframework.boot</groupId>
@ -234,6 +228,12 @@
<artifactId>shiro-core</artifactId>
<classifier>jakarta</classifier>
<version>${shiro.version}</version>
<exclusions>
<exclusion>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
@ -264,13 +264,6 @@
</exclusions>
</dependency>
<!-- <dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>${knife4j-spring-boot-starter.version}</version>
</dependency>-->
<!-- knife4j 升级springboot3.4.5报错 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-ui</artifactId>
@ -302,19 +295,8 @@
<!-- AutoPoi Excel工具类-->
<dependency>
<groupId>org.jeecgframework.boot3</groupId>
<artifactId>autopoi-web</artifactId>
<version>${autopoi-web.version}</version>
<exclusions>
<exclusion>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</exclusion>
<exclusion>
<artifactId>xercesImpl</artifactId>
<groupId>xerces</groupId>
</exclusion>
</exclusions>
<groupId>org.jeecgframework</groupId>
<artifactId>autopoi-spring-boot-3-starter</artifactId>
</dependency>
<dependency>
<groupId>xerces</groupId>
@ -332,6 +314,14 @@
<artifactId>checker-qual</artifactId>
<groupId>org.checkerframework</groupId>
</exclusion>
<exclusion>
<groupId>com.google.errorprone</groupId>
<artifactId>error_prone_annotations</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
</exclusion>
</exclusions>
</dependency>
@ -385,14 +375,25 @@
<artifactId>hutool-crypto</artifactId>
</dependency>
<!-- chatgpt -->
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-starter3-chatgpt</artifactId>
</dependency>
<!-- minidao -->
<dependency>
<groupId>org.jeecgframework.boot3</groupId>
<artifactId>minidao-spring-boot-starter-jsqlparser-4.9</artifactId>
<artifactId>jeecg-boot-starter-chatgpt</artifactId>
</dependency>
<!-- 腾讯云 -->
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java-sms</artifactId>
<version>${tencentcloud-sdk-java-sms.version}</version>
<exclusions>
<exclusion>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</exclusion>
<exclusion>
<groupId>com.squareup.okio</groupId>
<artifactId>okio</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>

View File

@ -1,5 +1,6 @@
package org.jeecg.common.api;
import org.jeecg.common.api.dto.AiragFlowDTO;
import org.jeecg.common.system.vo.*;
import java.util.List;
@ -131,7 +132,6 @@ public interface CommonAPI {
*/
Map<String, List<DictModel>> translateManyDict(String dictCodes, String keys);
//update-begin---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
/**
* 15 字典表的 翻译,可批量
* @param table
@ -142,6 +142,16 @@ public interface CommonAPI {
* @return
*/
List<DictModel> translateDictFromTableByKeys(String table, String text, String code, String keys, String dataSource);
//update-end---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
/**
* 16 运行AIRag流程
* for [QQYUN-13634]在baseapi里面封装方法方便其他模块调用
*
* @param airagFlowDTO
* @return 流程执行结果,可能是String或者Map
* @author chenrui
* @date 2025/9/2 11:43
*/
Object runAiragFlow(AiragFlowDTO airagFlowDTO);
}

View File

@ -0,0 +1,36 @@
package org.jeecg.common.api.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Map;
/**
* 调用AI流程入参
* for [QQYUN-13634]在baseapi里面封装方法方便其他模块调用
* @author chenrui
* @date 2025/9/2 14:11
*/
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class AiragFlowDTO implements Serializable {
private static final long serialVersionUID = 7431775881170684867L;
/**
* 流程id
*/
private String flowId;
/**
* 输入参数
*/
private Map<String, Object> inputParams;
}

View File

@ -0,0 +1,48 @@
package org.jeecg.common.api.dto;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 流程审批意见DTO
* @author scott
* @date 2025-01-29
*/
@Data
public class ApprovalCommentDTO implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 任务ID
*/
private String taskId;
/**
* 任务名称
*/
private String taskName;
/**
* 审批人ID
*/
private String approverId;
/**
* 审批人姓名
*/
private String approverName;
/**
* 审批意见
*/
private String approvalComment;
/**
* 审批时间
*/
private Date approvalTime;
}

View File

@ -30,12 +30,10 @@ public class OnlineAuthDTO implements Serializable {
*/
private String onlineFormUrl;
//update-begin---author:chenrui ---date:20240123 for[QQYUN-7992]【online】工单申请下的online表单未配置online表单开发菜单操作报错无权限------------
/**
* online工单的地址
*/
private String onlineWorkOrderUrl;
//update-end---author:chenrui ---date:20240123 for[QQYUN-7992]【online】工单申请下的online表单未配置online表单开发菜单操作报错无权限------------
public OnlineAuthDTO(){

View File

@ -0,0 +1,55 @@
package org.jeecg.common.api.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
/**
* 移动端消息推送
* @author liusq
* @date 2025/11/12 14:11
*/
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class PushMessageDTO implements Serializable {
private static final long serialVersionUID = 7431775881170684867L;
/**
* 消息标题
*/
private String title;
/**
* 消息内容
*/
private String content;
/**
* 推送形式all全推送 single单用户推送
*/
private String pushType;
/**
* 用户名usernameList
*/
List<String> usernames;
/**
* 用户名idList
*/
List<String> userIds;
/**
* 消息附加参数
*/
Map<String,Object> payload;
}

View File

@ -91,6 +91,12 @@ public class MessageDTO implements Serializable {
private Boolean isTimeJob = false;
//---【邮件相关参数】-------------------------------------------------------------
/**
* 枚举org.jeecg.common.constant.enums.NoticeTypeEnum
* 通知类型(system:系统消息、file:知识库、flow:流程、plan:日程计划、meeting:会议)
*/
private String noticeType;
public MessageDTO(){
}

View File

@ -1,6 +1,7 @@
package org.jeecg.common.api.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.jeecg.common.constant.CommonConstant;

View File

@ -121,9 +121,8 @@ public class AutoLogAspect {
if (operateType > 0) {
return operateType;
}
//update-begin---author:wangshuai ---date:20220331 for阿里云代码扫描规范(不允许任何魔法值出现在代码中)------------
// 代码逻辑说明: 阿里云代码扫描规范(不允许任何魔法值出现在代码中)------------
return OperateTypeEnum.getTypeByMethodName(methodName);
//update-end---author:wangshuai ---date:20220331 for阿里云代码扫描规范(不允许任何魔法值出现在代码中)------------
}
/**
@ -143,14 +142,15 @@ public class AutoLogAspect {
// https://my.oschina.net/mengzhang6/blog/2395893
Object[] arguments = new Object[paramsArray.length];
for (int i = 0; i < paramsArray.length; i++) {
if (paramsArray[i] instanceof BindingResult || paramsArray[i] instanceof ServletRequest || paramsArray[i] instanceof ServletResponse || paramsArray[i] instanceof MultipartFile) {
if (paramsArray[i] instanceof BindingResult || paramsArray[i] instanceof ServletRequest || paramsArray[i] instanceof ServletResponse || paramsArray[i] instanceof MultipartFile || paramsArray[i] instanceof MultipartFile[]) {
//ServletRequest不能序列化从入参里排除否则报异常java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode (i.e. isAsyncStarted() returns false)
//ServletResponse不能序列化 从入参里排除否则报异常java.lang.IllegalStateException: getOutputStream() has already been called for this response
//MultipartFile和MultipartFile[]不能序列化,从入参里排除
continue;
}
arguments[i] = paramsArray[i];
}
//update-begin-author:taoyan date:20200724 for:日志数据太长的直接过滤掉
// 代码逻辑说明: 日志数据太长的直接过滤掉
PropertyFilter profilter = new PropertyFilter() {
@Override
public boolean apply(Object o, String name, Object value) {
@ -165,14 +165,13 @@ public class AutoLogAspect {
}
};
params = JSONObject.toJSONString(arguments, profilter);
//update-end-author:taoyan date:20200724 for:日志数据太长的直接过滤掉
} else {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
// 请求的方法参数值
Object[] args = joinPoint.getArgs();
// 请求的方法参数名称
StandardReflectionParameterNameDiscoverer u=new StandardReflectionParameterNameDiscoverer();
StandardReflectionParameterNameDiscoverer u= new StandardReflectionParameterNameDiscoverer();
String[] paramNames = u.getParameterNames(method);
if (args != null && paramNames != null) {
for (int i = 0; i < args.length; i++) {

View File

@ -105,29 +105,24 @@ public class DictAspect {
Map<String, List<String>> dataListMap = new HashMap<>(5);
//取出结果集
List<Object> records=((IPage) ((Result) result).getResult()).getRecords();
//update-begin--Author:zyf -- Date:20220606 ----for【VUEN-1230】 判断是否含有字典注解,没有注解返回-----
// 代码逻辑说明: 【VUEN-1230】 判断是否含有字典注解,没有注解返回-----
Boolean hasDict= checkHasDict(records);
if(!hasDict){
return result;
}
log.debug(" __ 进入字典翻译切面 DictAspect —— " );
//update-end--Author:zyf -- Date:20220606 ----for【VUEN-1230】 判断是否含有字典注解,没有注解返回-----
for (Object record : records) {
String json="{}";
try {
//update-begin--Author:zyf -- Date:20220531 ----for【issues/#3629】 DictAspect Jackson序列化报错-----
//解决@JsonFormat注解解析不了的问题详见SysAnnouncement类的@JsonFormat
json = objectMapper.writeValueAsString(record);
//update-end--Author:zyf -- Date:20220531 ----for【issues/#3629】 DictAspect Jackson序列化报错-----
} catch (JsonProcessingException e) {
log.error("json解析失败"+e.getMessage(),e);
}
//update-begin--Author:scott -- Date:20211223 ----for【issues/3303】restcontroller返回json数据后key顺序错乱 -----
// 代码逻辑说明: 【issues/3303】restcontroller返回json数据后key顺序错乱 -----
JSONObject item = JSONObject.parseObject(json, Feature.OrderedField);
//update-end--Author:scott -- Date:20211223 ----for【issues/3303】restcontroller返回json数据后key顺序错乱 -----
//update-begin--Author:scott -- Date:20190603 ----for解决继承实体字段无法翻译问题------
//for (Field field : record.getClass().getDeclaredFields()) {
// 遍历所有字段把字典Code取出来放到 map 里
for (Field field : oConvertUtils.getAllFields(record)) {
@ -135,7 +130,6 @@ public class DictAspect {
if (oConvertUtils.isEmpty(value)) {
continue;
}
//update-end--Author:scott -- Date:20190603 ----for解决继承实体字段无法翻译问题------
if (field.getAnnotation(Dict.class) != null) {
if (!dictFieldList.contains(field)) {
dictFieldList.add(field);
@ -143,26 +137,22 @@ public class DictAspect {
String code = field.getAnnotation(Dict.class).dicCode();
String text = field.getAnnotation(Dict.class).dicText();
String table = field.getAnnotation(Dict.class).dictTable();
//update-begin---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
// 代码逻辑说明: [issues/#5643]解决分布式下表字典跨库无法查询问题------------
String dataSource = field.getAnnotation(Dict.class).ds();
//update-end---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
List<String> dataList;
String dictCode = code;
if (!StringUtils.isEmpty(table)) {
//update-begin---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
// 代码逻辑说明: [issues/#5643]解决分布式下表字典跨库无法查询问题------------
dictCode = String.format("%s,%s,%s,%s", table, text, code, dataSource);
//update-end---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
}
dataList = dataListMap.computeIfAbsent(dictCode, k -> new ArrayList<>());
this.listAddAllDeduplicate(dataList, Arrays.asList(value.split(",")));
}
//date类型默认转换string格式化日期
//update-begin--Author:zyf -- Date:20220531 ----for【issues/#3629】 DictAspect Jackson序列化报错-----
//if (JAVA_UTIL_DATE.equals(field.getType().getName())&&field.getAnnotation(JsonFormat.class)==null&&item.get(field.getName())!=null){
//SimpleDateFormat aDate=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// item.put(field.getName(), aDate.format(new Date((Long) item.get(field.getName()))));
//}
//update-end--Author:zyf -- Date:20220531 ----for【issues/#3629】 DictAspect Jackson序列化报错-----
}
items.add(item);
}
@ -176,15 +166,12 @@ public class DictAspect {
String code = field.getAnnotation(Dict.class).dicCode();
String text = field.getAnnotation(Dict.class).dicText();
String table = field.getAnnotation(Dict.class).dictTable();
//update-begin---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
// 自定义的字典表数据源
String dataSource = field.getAnnotation(Dict.class).ds();
//update-end---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
String fieldDictCode = code;
if (!StringUtils.isEmpty(table)) {
//update-begin---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
// 代码逻辑说明: [issues/#5643]解决分布式下表字典跨库无法查询问题------------
fieldDictCode = String.format("%s,%s,%s,%s", table, text, code, dataSource);
//update-end---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
}
String value = record.getString(field.getName());
@ -286,25 +273,20 @@ public class DictAspect {
String[] arr = dictCode.split(",");
String table = arr[0], text = arr[1], code = arr[2];
String values = String.join(",", needTranslDataTable);
//update-begin---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
// 自定义的数据源
String dataSource = null;
if (arr.length > 3) {
dataSource = arr[3];
}
//update-end---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
log.debug("translateDictFromTableByKeys.dictCode:" + dictCode);
log.debug("translateDictFromTableByKeys.values:" + values);
//update-begin---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
//update-begin---author:wangshuai---date:2024-01-09---for:微服务下为空报错没有参数需要传递空字符串---
// 代码逻辑说明: 微服务下为空报错没有参数需要传递空字符串---
if(null == dataSource){
dataSource = "";
}
//update-end---author:wangshuai---date:2024-01-09---for:微服务下为空报错没有参数需要传递空字符串---
List<DictModel> texts = commonApi.translateDictFromTableByKeys(table, text, code, values, dataSource);
//update-end---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
log.debug("translateDictFromTableByKeys.result:" + texts);
List<DictModel> list = translText.computeIfAbsent(dictCode, k -> new ArrayList<>());
list.addAll(texts);
@ -313,10 +295,8 @@ public class DictAspect {
for (DictModel dict : texts) {
String redisKey = String.format("sys:cache:dictTable::SimpleKey [%s,%s]", dictCode, dict.getValue());
try {
// update-begin-author:taoyan date:20211012 for: 字典表翻译注解缓存未更新 issues/3061
// 保留5分钟
redisTemplate.opsForValue().set(redisKey, dict.getText(), 300, TimeUnit.SECONDS);
// update-end-author:taoyan date:20211012 for: 字典表翻译注解缓存未更新 issues/3061
} catch (Exception e) {
log.warn(e.getMessage(), e);
}
@ -400,7 +380,7 @@ public class DictAspect {
if (k.trim().length() == 0) {
continue; //跳过循环
}
//update-begin--Author:scott -- Date:20210531 ----for !56 优化微服务应用下存在表字段需要字典翻译时加载缓慢问题-----
// 代码逻辑说明: !56 优化微服务应用下存在表字段需要字典翻译时加载缓慢问题-----
if (!StringUtils.isEmpty(table)){
log.debug("--DictAspect------dicTable="+ table+" ,dicText= "+text+" ,dicCode="+code);
String keyString = String.format("sys:cache:dictTable::SimpleKey [%s,%s,%s,%s]",table,text,code,k.trim());
@ -425,7 +405,6 @@ public class DictAspect {
tmpValue = commonApi.translateDict(code, k.trim());
}
}
//update-end--Author:scott -- Date:20210531 ----for !56 优化微服务应用下存在表字段需要字典翻译时加载缓慢问题-----
if (tmpValue != null) {
if (!"".equals(textValue.toString())) {

View File

@ -57,7 +57,6 @@ public class PermissionDataAspect {
String requestMethod = request.getMethod();
String requestPath = request.getRequestURI().substring(request.getContextPath().length());
requestPath = filterUrl(requestPath);
//update-begin-author:taoyan date:20211027 for:JTC-132【online报表权限】online报表带参数的菜单配置数据权限无效
//先判断是否online报表请求
if(requestPath.indexOf(UrlMatchEnum.CGREPORT_DATA.getMatchUrl())>=0 || requestPath.indexOf(UrlMatchEnum.CGREPORT_ONLY_DATA.getMatchUrl())>=0){
// 获取地址栏参数
@ -66,7 +65,6 @@ public class PermissionDataAspect {
requestPath+="?"+urlParamString;
}
}
//update-end-author:taoyan date:20211027 for:JTC-132【online报表权限】online报表带参数的菜单配置数据权限无效
log.debug("拦截请求 >> {} ; 请求类型 >> {} . ", requestPath, requestMethod);
String username = JwtUtil.getUserNameByToken(request);
//查询数据权限信息

View File

@ -41,7 +41,6 @@ public @interface Dict {
String dictTable() default "";
//update-begin---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
/**
* 方法描述: 数据字典表所在数据源名称
* 作 者: chenrui
@ -50,5 +49,4 @@ public @interface Dict {
* @return 返回类型: String
*/
String ds() default "";
//update-end---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
}

View File

@ -91,6 +91,23 @@ public interface CommonConstant {
public static String PREFIX_USER_SHIRO_CACHE = "shiro:cache:org.jeecg.config.shiro.ShiroRealm.authorizationCache:";
/** 登录用户Token令牌缓存KEY前缀 */
String PREFIX_USER_TOKEN = "prefix_user_token:";
/** 登录用户Token令牌作废提示信息比如 “不允许同一账号多地同时登录,会往这个变量存提示信息” */
String PREFIX_USER_TOKEN_ERROR_MSG = "prefix_user_token:error:msg_";
/**============================== 【是否允许同一账号多地同时登录】登录客户端类型常量 ==============================*/
/** 客户端类型PC端 */
String CLIENT_TYPE_PC = "PC";
/** 客户端类型APP端 */
String CLIENT_TYPE_APP = "APP";
/** 客户端类型:手机号登录 */
String CLIENT_TYPE_PHONE = "PHONE";
String PREFIX_USER_TOKEN_PC = "prefix_user_token:single_login:pc:";
/** 单点登录用户在APP端的Token缓存KEY前缀 (username -> token) */
String PREFIX_USER_TOKEN_APP = "prefix_user_token:single_login:app:";
/** 单点登录用户在手机号登录的Token缓存KEY前缀 (username -> token) */
String PREFIX_USER_TOKEN_PHONE = "prefix_user_token:single_login:phone:";
/**============================== 【是否允许同一账号多地同时登录】登录客户端类型常量 ==============================*/
// /** Token缓存时间3600秒即一小时 */
// int TOKEN_EXPIRE_TIME = 3600;
@ -303,6 +320,15 @@ public interface CommonConstant {
*/
String SYS_USER_ID_MAPPING_CACHE = "sys:cache:user:id_mapping";
/**
* 系统角色管理员编码
*/
String SYS_ROLE_ADMIN = "admin";
/**
* 考勤补卡业务状态 0处理中
*/
String SIGN_PATCH_BIZ_STATUS_0 = "0";
/**
* 考勤补卡业务状态 1同意 2不同意
*/
@ -428,6 +454,11 @@ public interface CommonConstant {
*/
String NOTICE_MSG_BUS_TYPE = "NOTICE_MSG_BUS_TYPE";
/**
* 通知类型,用于区分来源 file 知识 flow 流程 plan 日程 system 系统消息
*/
String NOTICE_TYPE = "noticeType";
/**
* 邮箱消息中地址登录时地址后携带的token,需要替换成真实的token值
*/
@ -581,7 +612,6 @@ public interface CommonConstant {
String ORDER_TYPE_DESC = "DESC";
//update-begin---author:scott ---date:2023-09-10 for积木报表常量----
/**
* 报表允许设计开发的角色
*/
@ -596,9 +626,7 @@ public interface CommonConstant {
* 数据隔离模式: 按照租户隔离
*/
public static final String SAAS_MODE_TENANT = "tenant";
//update-end---author:scott ---date::2023-09-10 for积木报表常量----
//update-begin---author:wangshuai---date:2024-04-07---for:修改手机号常量---
/**
* 修改手机号短信验证码redis-key的前缀
*/
@ -623,10 +651,94 @@ public interface CommonConstant {
* 修改手机号
*/
String UPDATE_PHONE = "updatePhone";
//update-end---author:wangshuai---date:2024-04-07---for:修改手机号常量---
/**
* 修改手机号验证码请求次数超出
*/
Integer PHONE_SMS_FAIL_CODE = 40002;
/**
* 自定义首页关联关系(ROLE:表示角色 USER:表示用户 DEFAULT:默认首页)
*
*/
String HOME_RELATION_ROLE = "ROLE";
String HOME_RELATION_USER = "USER";
String HOME_RELATION_DEFAULT = "DEFAULT";
/**
* 是否置顶(0否 1是)
*/
Integer IZ_TOP_1 = 1;
Integer IZ_TOP_0 = 0;
//关注流程缓存前缀
String FLOW_FOCUS_NOTICE_PREFIX = "flow:runtimeData:focus:notice:";
//任务缓办时间缓存前缀
String FLOW_TASK_DELAY_PREFIX = "flow:runtimeData:task:delay:";
/**
* 用户代理类型离职quit 代理agent
*/
String USER_AGENT_TYPE_QUIT = "quit";
String USER_AGENT_TYPE_AGENT = "agent";
/**
* 督办流程首节点任务taskKey
*/
String SUPERVISE_FIRST_TASK_KEY = "Task_1bhxpt0";
/**
* wps模板预览数据缓存前缀
*/
String EOA_WPS_TEMPLATE_VIEW_DATA ="eoa:wps:templateViewData:";
/**
* wps模板预览版本号缓存前缀
*/
String EOA_WPS_TEMPLATE_VIEW_VERSION ="eoa:wps:templateViewVersion:";
/**
* 表单设计器oa新增字段
* x_oa_timeout_date:逾期时间
* x_oa_archive_status:归档状态
*/
String X_OA_TIMEOUT_DATE ="x_oa_timeout_date";
String X_OA_ARCHIVE_STATUS ="x_oa_archive_status";
/**
* 流程状态
* 待提交: 1
* 处理中: 2
* 已完成: 3
* 已作废: 4
* 已挂起: 5
*/
String BPM_STATUS_1 ="1";
String BPM_STATUS_2 ="2";
String BPM_STATUS_3 ="3";
String BPM_STATUS_4 ="4";
String BPM_STATUS_5 ="5";
/**
* 默认租户产品包
*/
String TENANT_PACK_DEFAULT = "default";
/**
* 部门名称redisKey(全路径)
*/
String DEPART_NAME_REDIS_KEY_PRE = "sys:cache:departPathName:";
/**
* 默认用户排序值
*/
Integer DEFAULT_USER_SORT = 1000;
/**
* 发送短信方式:腾讯
*/
String SMS_SEND_TYPE_TENCENT = "tencent";
/**
* 发送短信方式:阿里云
*/
String SMS_SEND_TYPE_ALI_YUN = "aliyun";
}

View File

@ -4,6 +4,20 @@ package org.jeecg.common.constant;
* @author: jeecg-boot
*/
public interface DataBaseConstant {
/**
* 内置的系统变量键列表
*/
public static final String[] SYSTEM_KEYS = {
DataBaseConstant.SYS_ORG_CODE, DataBaseConstant.SYS_ORG_CODE_TABLE, DataBaseConstant.SYS_MULTI_ORG_CODE,
DataBaseConstant.SYS_MULTI_ORG_CODE_TABLE, DataBaseConstant.SYS_ORG_ID, DataBaseConstant.SYS_ORG_ID_TABLE,
DataBaseConstant.SYS_ROLE_CODE, DataBaseConstant.SYS_ROLE_CODE_TABLE, DataBaseConstant.SYS_USER_CODE,
DataBaseConstant.SYS_USER_CODE_TABLE, DataBaseConstant.SYS_USER_ID, DataBaseConstant.SYS_USER_ID_TABLE,
DataBaseConstant.SYS_USER_NAME, DataBaseConstant.SYS_USER_NAME_TABLE, DataBaseConstant.SYS_DATE,
DataBaseConstant.SYS_DATE_TABLE, DataBaseConstant.SYS_TIME, DataBaseConstant.SYS_TIME_TABLE,
DataBaseConstant.SYS_BASE_PATH
};
//*********数据库类型****************************************
/**MYSQL数据库*/

View File

@ -0,0 +1,15 @@
package org.jeecg.common.constant;
/**
* @Description: 密码常量类
*
* @author: wangshuai
* @date: 2025/8/27 20:10
*/
public interface PasswordConstant {
/**
* 导入用户默认密码
*/
String DEFAULT_PASSWORD = "123456";
}

View File

@ -39,20 +39,18 @@ public class ProvinceCityArea {
this.initAreaList();
if(areaList!=null && areaList.size()>0){
for(int i=areaList.size()-1;i>=0;i--){
//update-begin-author:taoyan date:2022-5-24 for:VUEN-1088 online 导入 省市区导入后 导入数据错乱 北京市/市辖区/西城区-->山西省/晋城市/城区
// 代码逻辑说明: VUEN-1088 online 导入 省市区导入后 导入数据错乱 北京市/市辖区/西城区-->山西省/晋城市/城区
String areaText = areaList.get(i).getText();
String cityText = areaList.get(i).getAheadText();
if(text.indexOf(areaText)>=0 && (cityText!=null && text.indexOf(cityText)>=0)){
return areaList.get(i).getId();
}
//update-end-author:taoyan date:2022-5-24 for:VUEN-1088 online 导入 省市区导入后 导入数据错乱 北京市/市辖区/西城区-->山西省/晋城市/城区
}
}
}
return null;
}
// update-begin-author:sunjianlei date:20220121 for:【JTC-704】数据导入错误 省市区组件,文件中为北京市,导入后,导为了山西省
/**
* 获取省市区code精准匹配
* @param texts 文本数组,省,市,区
@ -117,11 +115,10 @@ public class ProvinceCityArea {
}
return null;
}
// update-end-author:sunjianlei date:20220121 for:【JTC-704】数据导入错误 省市区组件,文件中为北京市,导入后,导为了山西省
public void getAreaByCode(String code,List<String> ls){
for(Area area: areaList){
if(area.getId().equals(code)){
if(null != area && area.getId().equals(code)){
String pid = area.getPid();
ls.add(0,area.getText());
getAreaByCode(pid,ls);
@ -154,9 +151,8 @@ public class ProvinceCityArea {
for(String areaKey:areaJson.keySet()){
//System.out.println("········"+areaKey);
Area area = new Area(areaKey,areaJson.getString(areaKey),cityKey);
//update-begin-author:taoyan date:2022-5-24 for:VUEN-1088 online 导入 省市区导入后 导入数据错乱 北京市/市辖区/西城区-->山西省/晋城市/城区
// 代码逻辑说明: VUEN-1088 online 导入 省市区导入后 导入数据错乱 北京市/市辖区/西城区-->山西省/晋城市/城区
area.setAheadText(cityJson.getString(cityKey));
//update-end-author:taoyan date:2022-5-24 for:VUEN-1088 online 导入 省市区导入后 导入数据错乱 北京市/市辖区/西城区-->山西省/晋城市/城区
this.areaList.add(area);
}
}

View File

@ -0,0 +1,97 @@
package org.jeecg.common.constant.enums;
import org.jeecg.common.util.oConvertUtils;
/**
* @Description: 部门类型枚举类
*
* @author: wangshuai
* @date: 2025/8/19 21:37
*/
public enum DepartCategoryEnum {
DEPART_CATEGORY_COMPANY("部门类型:公司","公司","1"),
DEPART_CATEGORY_DEPART("部门类型:部门","部门","2"),
DEPART_CATEGORY_POST("部门类型:岗位","岗位","3"),
DEPART_CATEGORY_SUB_COMPANY("部门类型:子公司","子公司","4");
DepartCategoryEnum(String described, String name, String value) {
this.value = value;
this.name = name;
this.described = described;
}
/**
* 描述
*/
private String described;
/**
* 值
*/
private String value;
/**
* 名称
*/
private String name;
public String getDescribed() {
return described;
}
public void setDescribed(String described) {
this.described = described;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 根据值获取名称
*
* @param value
* @return
*/
public static String getNameByValue(String value){
if (oConvertUtils.isEmpty(value)) {
return null;
}
for (DepartCategoryEnum val : values()) {
if (val.getValue().equals(value)) {
return val.getName();
}
}
return value;
}
/**
* 根据名称获取值
*
* @param name
* @return
*/
public static String getValueByName(String name){
if (oConvertUtils.isEmpty(name)) {
return null;
}
for (DepartCategoryEnum val : values()) {
if (val.getName().equals(name)) {
return val.getValue();
}
}
return name;
}
}

View File

@ -9,14 +9,14 @@ import org.apache.commons.lang3.StringUtils;
public enum DySmsEnum {
/**登录短信模板编码*/
LOGIN_TEMPLATE_CODE("SMS_175435174","敲敲云","code"),
LOGIN_TEMPLATE_CODE("SMS_175435174","敲敲云","code"),
/**忘记密码短信模板编码*/
FORGET_PASSWORD_TEMPLATE_CODE("SMS_175435174","敲敲云","code"),
/**修改密码短信模板编码*/
CHANGE_PASSWORD_TEMPLATE_CODE("SMS_465391221","敲敲云","code"),
/**注册账号短信模板编码*/
REGISTER_TEMPLATE_CODE("SMS_175430166","敲敲云","code");
FORGET_PASSWORD_TEMPLATE_CODE("SMS_175435174","敲敲云","code"),
/**修改密码短信模板编码*/
CHANGE_PASSWORD_TEMPLATE_CODE("SMS_465391221","敲敲云","code"),
/**注册账号短信模板编码*/
REGISTER_TEMPLATE_CODE("SMS_175430166","敲敲云","code");
/**
* 短信模板编码
*/

View File

@ -13,6 +13,10 @@ public enum EmailTemplateEnum {
* 流程催办
*/
BPM_CUIBAN_EMAIL("bpm_cuiban_email", "/templates/email/bpm_cuiban_email.ftl"),
/**
* 流程抄送
*/
BPM_CC_EMAIL("bpm_cc_email", "/templates/email/bpm_cc_email.ftl"),
/**
* 流程新任务
*/

View File

@ -8,21 +8,30 @@ import java.util.List;
/**
* 消息类型
*
* @author: jeecg-boot
*/
@EnumDict("messageType")
public enum MessageTypeEnum {
/** 系统消息 */
XT("system", "系统消息"),
/** 邮件消息 */
YJ("email", "邮件消息"),
/** 钉钉消息 */
/**
* 系统消息
*/
XT("system", "系统消息"),
/**
* 邮件消息
*/
YJ("email", "邮件消息"),
/**
* 钉钉消息
*/
DD("dingtalk", "钉钉消息"),
/** 企业微信 */
/**
* 企业微信
*/
QYWX("wechat_enterprise", "企业微信");
MessageTypeEnum(String type, String note){
MessageTypeEnum(String type, String note) {
this.type = type;
this.note = note;
}
@ -56,12 +65,13 @@ public enum MessageTypeEnum {
/**
* 获取字典数据
*
* @return
*/
public static List<DictModel> getDictList(){
public static List<DictModel> getDictList() {
List<DictModel> list = new ArrayList<>();
DictModel dictModel = null;
for(MessageTypeEnum e: MessageTypeEnum.values()){
for (MessageTypeEnum e : MessageTypeEnum.values()) {
dictModel = new DictModel();
dictModel.setValue(e.getType());
dictModel.setText(e.getNote());

View File

@ -0,0 +1,88 @@
package org.jeecg.common.constant.enums;
/**
* @Description: 文件类型枚举类
*
* @author: wangshuai
* @date: 2025/6/26 17:29
*/
public enum NoticeTypeEnum {
//VUE3专用
NOTICE_TYPE_FILE("知识库消息","file"),
NOTICE_TYPE_FLOW("工作流消息","flow"),
NOTICE_TYPE_PLAN("日程消息","plan"),
//暂时没用到
NOTICE_TYPE_MEETING("会议消息","meeting"),
NOTICE_TYPE_SYSTEM("系统消息","system"),
/**
* 协同工作
* for [JHHB-136]【vue3】协同工作系统消息需要添加一个类型
*/
NOTICE_TYPE_COLLABORATION("协同工作", "collab"),
/**
* 督办
*/
NOTICE_TYPE_SUPERVISE("督办管理", "supe"),
/**
* 考勤
*/
NOTICE_TYPE_ATTENDANCE("考勤消息", "attendance");
/**
* 文件类型名称
*/
private String name;
/**
* 文件类型值
*/
private String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
NoticeTypeEnum(String name, String value) {
this.name = name;
this.value = value;
}
/**
* 获取聊天通知类型
*
* @param value
* @return
*/
public static String getChatNoticeType(String value){
return value + "Notice";
}
/**
* 获取通知名称
*
* @param value
* @return
*/
public static String getNoticeNameByValue(String value){
value = value.replace("Notice","");
for (NoticeTypeEnum e : NoticeTypeEnum.values()) {
if (e.getValue().equals(value)) {
return e.getName();
}
}
return "系统消息";
}
}

View File

@ -0,0 +1,180 @@
package org.jeecg.common.constant.enums;
import java.util.Arrays;
import java.util.List;
/**
* 职级枚举类
*
* 注意此枚举仅适用于天津临港控股OA项目,职级的名称和等级均为写死(需要与数据库配置一致)
* @date 2025-08-26
* @author scott
*/
public enum PositionLevelEnum {
// 领导层级等级1-3
CHAIRMAN("董事长", 1, PositionType.LEADER),
GENERAL_MANAGER("总经理", 2, PositionType.LEADER),
VICE_GENERAL_MANAGER("副总经理", 3, PositionType.LEADER),
// 职员层级等级4-6
MINISTER("部长", 4, PositionType.STAFF),
VICE_MINISTER("副部长", 5, PositionType.STAFF),
STAFF("职员", 6, PositionType.STAFF);
private final String name;
private final int level;
private final PositionType type;
PositionLevelEnum(String name, int level, PositionType type) {
this.name = name;
this.level = level;
this.type = type;
}
public String getName() {
return name;
}
public int getLevel() {
return level;
}
public PositionType getType() {
return type;
}
/**
* 职级类型枚举
*/
public enum PositionType {
STAFF("职员层级"),
LEADER("领导层级");
private final String desc;
PositionType(String desc) {
this.desc = desc;
}
public String getDesc() {
return desc;
}
}
/**
* 根据职级名称获取枚举
* @param name 职级名称
* @return 职级枚举
*/
public static PositionLevelEnum getByName(String name) {
for (PositionLevelEnum position : values()) {
if (position.getName().equals(name)) {
return position;
}
}
return null;
}
/**
* 根据职级等级获取枚举
* @param level 职级等级
* @return 职级枚举
*/
public static PositionLevelEnum getByLevel(int level) {
for (PositionLevelEnum position : values()) {
if (position.getLevel() == level) {
return position;
}
}
return null;
}
/**
* 根据职级名称判断是否为职员层级
* @param name 职级名称
* @return true-职员层级false-非职员层级
*/
public static boolean isStaffLevel(String name) {
PositionLevelEnum position = getByName(name);
return position != null && position.getType() == PositionType.STAFF;
}
/**
* 根据职级名称判断是否为领导层级
* @param name 职级名称
* @return true-领导层级false-非领导层级
*/
public static boolean isLeaderLevel(String name) {
PositionLevelEnum position = getByName(name);
return position != null && position.getType() == PositionType.LEADER;
}
/**
* 比较两个职级的等级高低
* @param name1 职级名称1
* @param name2 职级名称2
* @return 正数表示name1等级更高负数表示name2等级更高0表示等级相同
*/
public static int compareLevel(String name1, String name2) {
PositionLevelEnum pos1 = getByName(name1);
PositionLevelEnum pos2 = getByName(name2);
if (pos1 == null || pos2 == null) {
return 0;
}
// 等级数字越小代表职级越高
return pos2.getLevel() - pos1.getLevel();
}
/**
* 判断是否为更高等级
* @param currentName 当前职级名称
* @param targetName 目标职级名称
* @return true-目标职级更高false-目标职级不高于当前职级
*/
public static boolean isHigherLevel(String currentName, String targetName) {
return compareLevel(targetName, currentName) > 0;
}
/**
* 获取所有职员层级名称
* @return 职员层级名称列表
*/
public static List<String> getStaffLevelNames() {
return Arrays.asList(MINISTER.getName(), VICE_MINISTER.getName(), STAFF.getName());
}
/**
* 获取所有领导层级名称
* @return 领导层级名称列表
*/
public static List<String> getLeaderLevelNames() {
return Arrays.asList(CHAIRMAN.getName(), GENERAL_MANAGER.getName(), VICE_GENERAL_MANAGER.getName());
}
/**
* 获取所有职级名称(按等级排序)
* @return 所有职级名称列表
*/
public static List<String> getAllPositionNames() {
return Arrays.asList(
CHAIRMAN.getName(), GENERAL_MANAGER.getName(), VICE_GENERAL_MANAGER.getName(),
MINISTER.getName(), VICE_MINISTER.getName(), STAFF.getName()
);
}
/**
* 获取指定等级范围的职级
* @param minLevel 最小等级
* @param maxLevel 最大等级
* @return 职级名称列表
*/
public static List<String> getPositionsByLevelRange(int minLevel, int maxLevel) {
return Arrays.stream(values())
.filter(p -> p.getLevel() >= minLevel && p.getLevel() <= maxLevel)
.map(PositionLevelEnum::getName)
.collect(java.util.stream.Collectors.toList());
}
}

View File

@ -23,7 +23,25 @@ public enum SysAnnmentTypeEnum {
/**
* 邀请用户跳转到个人设置
*/
TENANT_INVITE("tenant_invite", "url", "/system/usersetting");
TENANT_INVITE("tenant_invite", "url", "/system/usersetting"),
/**
* 协同工作-待办通知
* for [JHHB-136]【vue3】协同工作系统消息需要添加一个类型
*/
EOA_CO_NOTIFY("eoa_co_notify", "url", "/collaboration/pending"),
/**
* 协同工作-催办通知
* for [JHHB-136]【vue3】协同工作系统消息需要添加一个类型
*/
EOA_CO_REMIND("eoa_co_remind", "url", "/collaboration/pending"),
/**
* 督办管理-催办
*/
EOA_SUP_REMIND("eoa_sup_remind", "url", "/superivse/list"),
/**
* 督办管理-通知
*/
EOA_SUP_NOTIFY("eoa_sup_notify", "url", "/superivse/list");
/**
* 业务类型(email:邮件 bpm:流程)

View File

@ -0,0 +1,82 @@
package org.jeecg.common.constant.enums;
import org.jeecg.common.util.oConvertUtils;
/**
* UniPush 消息推送枚举
* @author: jeecg-boot
*/
public enum UniPushTypeEnum {
/**
* 聊天
*/
CHAT("chat", "聊天消息", "收到%s发来的聊天消息"),
/**
* 流程跳转到我的任务
*/
BPM("bpm_task", "待办任务", "收到%s待办任务"),
/**
* 流程抄送任务
*/
BPM_VIEW("bpm_cc", "知会任务", "收到%s知会任务"),
/**
* 系统消息
*/
SYS_MSG("system", "系统消息", "收到一条系统通告");
/**
* 业务类型(chat:聊天 bpm_task:流程 bpm_cc:流程抄送)
*/
private String type;
/**
* 消息标题
*/
private String title;
/**
* 消息内容
*/
private String content;
UniPushTypeEnum(String type, String title, String content) {
this.type = type;
this.title = title;
this.content = content;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getTitle() {
return title ;
}
public void setTitle(String openType) {
this.title = openType;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public static UniPushTypeEnum getByType(String type) {
if (oConvertUtils.isEmpty(type)) {
return null;
}
for (UniPushTypeEnum val : values()) {
if (val.getType().equals(type)) {
return val;
}
}
return null;
}
}

View File

@ -73,7 +73,7 @@ public class SensitiveDataAspect {
SensitiveInfoUtil.handleNestedObject(result, entity, isEncode);
}
long endTime=System.currentTimeMillis();
log.info((isEncode ? "加密操作," : "解密操作,") + "Aspect程序耗时" + (endTime - startTime) + "ms");
log.debug((isEncode ? "加密操作," : "解密操作,") + "Aspect程序耗时" + (endTime - startTime) + "ms");
return result;
}

View File

@ -387,7 +387,7 @@ public class JeecgElasticsearchTemplate {
data.remove("id");
bodySb.append(data.toJSONString()).append("\n");
}
System.out.println("+-+-+-: bodySb.toString(): " + bodySb.toString());
//System.out.println("+-+-+-: bodySb.toString(): " + bodySb.toString());
HttpHeaders headers = RestUtil.getHeaderApplicationJson();
RestUtil.request(url, HttpMethod.PUT, headers, null, bodySb, JSONObject.class);
return true;

View File

@ -32,6 +32,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.servlet.NoHandlerFoundException;
import java.util.Map;
@ -56,7 +57,7 @@ public class JeecgBootExceptionHandler {
addSysLog(e);
return Result.error("校验失败!" + e.getBindingResult().getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.joining(",")));
}
/**
* 处理自定义异常
*/
@ -120,13 +121,12 @@ public class JeecgBootExceptionHandler {
@ExceptionHandler(Exception.class)
public Result<?> handleException(Exception e){
log.error(e.getMessage(), e);
//update-begin---author:zyf ---date:20220411 for处理Sentinel限流自定义异常
// 代码逻辑说明: 处理Sentinel限流自定义异常
Throwable throwable = e.getCause();
SentinelErrorInfoEnum errorInfoEnum = SentinelErrorInfoEnum.getErrorByException(throwable);
if (ObjectUtil.isNotEmpty(errorInfoEnum)) {
return Result.error(errorInfoEnum.getError());
}
//update-end---author:zyf ---date:20220411 for处理Sentinel限流自定义异常
addSysLog(e);
return Result.error("操作失败,"+e.getMessage());
}
@ -166,6 +166,27 @@ public class JeecgBootExceptionHandler {
return Result.error("文件大小超出10MB限制, 请压缩或降低文件质量! ");
}
/**
* 处理文件过大异常.
* jdk17中的MultipartException异常类已经被拆分成了MultipartException和MaxUploadSizeExceededException
* for [QQYUN-11716]上传大图片失败没有精确提示
* @param e
* @return
* @author chenrui
* @date 2025/4/8 16:13
*/
@ExceptionHandler(MultipartException.class)
public Result<?> handleMaxUploadSizeExceededException(MultipartException e) {
Throwable cause = e.getCause();
if (cause instanceof IllegalStateException) {
log.error("文件大小超出限制: {}", cause.getMessage(), e);
addSysLog(e);
return Result.error("文件大小超出限制, 请压缩或降低文件质量!");
} else {
return handleException(e);
}
}
@ExceptionHandler(DataIntegrityViolationException.class)
public Result<?> handleDataIntegrityViolationException(DataIntegrityViolationException e) {
log.error(e.getMessage(), e);
@ -202,7 +223,6 @@ public class JeecgBootExceptionHandler {
return Result.error("校验失败存在SQL注入风险" + msg);
}
//update-begin---author:chenrui ---date:20240423 for[QQYUN-8732]把错误的日志都抓取了 方便后续处理,单独弄个日志类型------------
/**
* 添加异常新系统日志
* @param e 异常
@ -222,9 +242,12 @@ public class JeecgBootExceptionHandler {
}
if (null != request) {
//请求的参数
Map<String, String[]> parameterMap = request.getParameterMap();
if(!CollectionUtils.isEmpty(parameterMap)){
log.setMethod(oConvertUtils.mapToString(request.getParameterMap()));
if (!isTooBigException(e)) {
// 文件上传过大异常时不能获取参数,否则会报错
Map<String, String[]> parameterMap = request.getParameterMap();
if(!CollectionUtils.isEmpty(parameterMap)) {
log.setMethod(oConvertUtils.mapToString(request.getParameterMap()));
}
}
// 请求地址
log.setRequestUrl(request.getRequestURI());
@ -249,6 +272,27 @@ public class JeecgBootExceptionHandler {
baseCommonService.addLog(log);
}
//update-end---author:chenrui ---date:20240423 for[QQYUN-8732]把错误的日志都抓取了 方便后续处理,单独弄个日志类型------------
/**
* 是否文件过大异常
* for [QQYUN-11716]上传大图片失败没有精确提示
* @param e
* @return
* @author chenrui
* @date 2025/4/8 20:21
*/
private static boolean isTooBigException(Throwable e) {
boolean isTooBigException = false;
if(e instanceof MultipartException){
Throwable cause = e.getCause();
if (cause instanceof IllegalStateException){
isTooBigException = true;
}
}
if(e instanceof MaxUploadSizeExceededException){
isTooBigException = true;
}
return isTooBigException;
}
}

View File

@ -4,6 +4,11 @@ import java.lang.annotation.*;
/**
* 将枚举类转化成字典数据
*
* <<使用说明>>
* 1. 枚举类需以 `Enum` 结尾,并且在类上添加 `@EnumDict` 注解。
* 2. 需要手动将枚举类所在包路径** 添加到 `org.jeecg.common.system.util.ResourceUtil.BASE_SCAN_PACKAGES` 配置数组中。
*
* @Author taoYan
* @Date 2022/7/8 10:34
**/

View File

@ -18,6 +18,7 @@ import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.entity.enmus.ExcelType;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecgframework.poi.handler.inter.IExcelExportServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
@ -67,12 +68,16 @@ public class JeecgController<T, S extends IService<T>> {
//此处设置的filename无效 ,前端会重更新设置一下
mv.addObject(NormalExcelConstants.FILE_NAME, title);
mv.addObject(NormalExcelConstants.CLASS, clazz);
//update-begin--Author:liusq Date:20210126 for图片导出报错ImageBasePath未设置--------------------
ExportParams exportParams=new ExportParams(title + "报表", "导出人:" + sysUser.getRealname(), title);
// 代码逻辑说明: 【QQYUN-13930】统一改成导出xlsx格式---
ExportParams exportParams=new ExportParams(title + "报表", "导出人:" + sysUser.getRealname(), title, ExcelType.XSSF);
exportParams.setImageBasePath(jeecgBaseConfig.getPath().getUpload());
//update-end--Author:liusq Date:20210126 for图片导出报错ImageBasePath未设置----------------------
mv.addObject(NormalExcelConstants.PARAMS,exportParams);
mv.addObject(NormalExcelConstants.DATA_LIST, exportList);
// 代码逻辑说明: 【issues/9052】BasicTable列表页导出excel可以指定列---
String exportFields = request.getParameter(NormalExcelConstants.EXPORT_FIELDS);
if(oConvertUtils.isNotEmpty(exportFields)){
mv.addObject(NormalExcelConstants.EXPORT_FIELDS, exportFields);
}
return mv;
}
/**
@ -93,14 +98,12 @@ public class JeecgController<T, S extends IService<T>> {
// Step.2 计算分页sheet数据
double total = service.count();
int count = (int)Math.ceil(total/pageNum);
//update-begin-author:liusq---date:20220629--for: 多sheet导出根据选择导出写法调整 ---
// Step.3 过滤选中数据
String selections = request.getParameter("selections");
if (oConvertUtils.isNotEmpty(selections)) {
List<String> selectionList = Arrays.asList(selections.split(","));
queryWrapper.in("id",selectionList);
}
//update-end-author:liusq---date:20220629--for: 多sheet导出根据选择导出写法调整 ---
// Step.4 多sheet处理
List<Map<String, Object>> listMap = new ArrayList<Map<String, Object>>();
for (int i = 1; i <=count ; i++) {
@ -127,6 +130,53 @@ public class JeecgController<T, S extends IService<T>> {
return mv;
}
/**
* 大数据导出
* @param request
* @param object
* @param clazz
* @param title
* @param pageSize 每次查询的数据量
* @return
* @author chenrui
* @date 2025/8/11 16:11
*/
protected ModelAndView exportXlsForBigData(HttpServletRequest request, T object, Class<T> clazz, String title,Integer pageSize) {
// 组装查询条件
QueryWrapper<T> queryWrapper = QueryGenerator.initQueryWrapper(object, request.getParameterMap());
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
// 计算分页数
double total = service.count();
int count = (int) Math.ceil(total / pageSize);
// 过滤选中数据
String selections = request.getParameter("selections");
if (oConvertUtils.isNotEmpty(selections)) {
List<String> selectionList = Arrays.asList(selections.split(","));
queryWrapper.in("id", selectionList);
}
// 定义IExcelExportServer
IExcelExportServer excelExportServer = (queryParams, pageNum) -> {
if (pageNum > count) {
return null;
}
Page<T> page = new Page<T>(pageNum, pageSize);
IPage<T> pageList = service.page(page, (QueryWrapper<T>) queryParams);
return new ArrayList<>(pageList.getRecords());
};
// AutoPoi 导出Excel
ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
//此处设置的filename无效 ,前端会重更新设置一下
mv.addObject(NormalExcelConstants.FILE_NAME, title);
mv.addObject(NormalExcelConstants.CLASS, clazz);
ExportParams exportParams = new ExportParams(title + "报表", "导出人:" + sysUser.getRealname(), title, jeecgBaseConfig.getPath().getUpload());
mv.addObject(NormalExcelConstants.PARAMS, exportParams);
mv.addObject(NormalExcelConstants.EXPORT_SERVER, excelExportServer);
mv.addObject(NormalExcelConstants.QUERY_PARAMS, queryWrapper);
return mv;
}
/**
* 根据权限导出excel传入导出字段参数
@ -172,16 +222,15 @@ public class JeecgController<T, S extends IService<T>> {
params.setNeedSave(true);
try {
List<T> list = ExcelImportUtil.importExcel(file.getInputStream(), clazz, params);
//update-begin-author:taoyan date:20190528 for:批量插入数据
// 代码逻辑说明: 批量插入数据
long start = System.currentTimeMillis();
service.saveBatch(list);
//400条 saveBatch消耗时间1592毫秒 循环插入消耗时间1947毫秒
//1200条 saveBatch消耗时间3687毫秒 循环插入消耗时间5212毫秒
log.info("消耗时间" + (System.currentTimeMillis() - start) + "毫秒");
//update-end-author:taoyan date:20190528 for:批量插入数据
return Result.ok("文件导入成功!数据行数:" + list.size());
} catch (Exception e) {
//update-begin-author:taoyan date:20211124 for: 导入数据重复增加提示
// 代码逻辑说明: 导入数据重复增加提示
String msg = e.getMessage();
log.error(msg, e);
if(msg!=null && msg.indexOf("Duplicate entry")>=0){
@ -189,7 +238,6 @@ public class JeecgController<T, S extends IService<T>> {
}else{
return Result.error("文件导入失败:" + e.getMessage());
}
//update-end-author:taoyan date:20211124 for: 导入数据重复增加提示
} finally {
try {
file.getInputStream().close();

View File

@ -2,7 +2,6 @@ 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;
@ -10,10 +9,10 @@ import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import io.swagger.v3.oas.annotations.media.Schema;
/**
* @Description: Entity基类

View File

@ -11,6 +11,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import org.apache.commons.beanutils.PropertyUtils;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.DataBaseConstant;
@ -96,7 +97,6 @@ public class QueryGenerator {
return queryWrapper;
}
//update-begin---author:chenrui ---date:20240527 for[TV360X-378]增加自定义字段查询规则功能------------
/**
* 获取查询条件构造器QueryWrapper实例 通用查询条件已被封装完成
* @param searchObj 查询实体
@ -111,7 +111,6 @@ public class QueryGenerator {
log.debug("---查询条件构造器初始化完成,耗时:"+(System.currentTimeMillis()-start)+"毫秒----");
return queryWrapper;
}
//update-end---author:chenrui ---date:20240527 for[TV360X-378]增加自定义字段查询规则功能------------
/**
* 组装Mybatis Plus 查询条件
@ -141,7 +140,6 @@ public class QueryGenerator {
}
String name, type, column;
// update-begin--Author:taoyan Date:20200923 forissues/1671 如果字段加注解了@TableField(exist = false),不走DB查询-------
//定义实体字段和数据库字段名称的映射 高级查询中 只能获取实体字段 如果设置TableField注解 那么查询条件会出问题
Map<String,String> fieldColumnMap = new HashMap<>(5);
for (int i = 0; i < origDescriptors.length; i++) {
@ -187,7 +185,7 @@ public class QueryGenerator {
queryWrapper.and(j -> j.like(field,vals[0]));
}
}else {
//update-begin---author:chenrui ---date:20240527 for[TV360X-378]增加自定义字段查询规则功能------------
// 代码逻辑说明: [TV360X-378]增加自定义字段查询规则功能------------
QueryRuleEnum rule;
if(null != customRuleMap && customRuleMap.containsKey(name)) {
// 有自定义规则,使用自定义规则.
@ -196,7 +194,6 @@ public class QueryGenerator {
//根据参数值带什么关键字符串判断走什么类型的查询
rule = convert2Rule(value);
}
//update-end---author:chenrui ---date:20240527 for[TV360X-378]增加自定义字段查询规则功能------------
value = replaceValue(rule,value);
// add -begin 添加判断为字符串时设为全模糊查询
//if( (rule==null || QueryRuleEnum.EQ.equals(rule)) && "class java.lang.String".equals(type)) {
@ -216,7 +213,6 @@ public class QueryGenerator {
//高级查询
doSuperQuery(queryWrapper, parameterMap, fieldColumnMap);
// update-end--Author:taoyan Date:20200923 forissues/1671 如果字段加注解了@TableField(exist = false),不走DB查询-------
}
@ -257,15 +253,74 @@ public class QueryGenerator {
if(parameterMap!=null&& parameterMap.containsKey(ORDER_TYPE)) {
order = parameterMap.get(ORDER_TYPE)[0];
}
log.debug("排序规则>>列:" + column + ",排序方式:" + order);
if(oConvertUtils.isNotEmpty(column)){
log.debug("单字段排序规则>> column:" + column + ",排序方式:" + order);
}
//update-begin-author:scott date:2022-11-07 for:避免用户自定义表无默认字段{创建时间},导致排序报错
// 1. 列表多字段排序优先
if(parameterMap!=null&& parameterMap.containsKey("sortInfoString")) {
// 多字段排序
String sortInfoString = parameterMap.get("sortInfoString")[0];
log.debug("多字段排序规则>> sortInfoString:" + sortInfoString);
List<OrderItem> orderItemList = SqlConcatUtil.getQueryConditionOrders(column, order, sortInfoString);
log.debug(orderItemList.toString());
if (orderItemList != null && !orderItemList.isEmpty()) {
for (OrderItem item : orderItemList) {
// 一、获取排序数据库字段
String columnName = item.getColumn();
// 1.字典字段,去掉字典翻译文本后缀
if(columnName.endsWith(CommonConstant.DICT_TEXT_SUFFIX)) {
columnName = columnName.substring(0, column.lastIndexOf(CommonConstant.DICT_TEXT_SUFFIX));
}
// 2.实体驼峰字段转为数据库字段
columnName = SqlInjectionUtil.getSqlInjectSortField(columnName);
// 二、设置字段排序规则
if (item.isAsc()) {
queryWrapper.orderByAsc(columnName);
} else {
queryWrapper.orderByDesc(columnName);
}
}
}
return;
}
// 2. 列表单字段默认排序
if(oConvertUtils.isEmpty(column) && parameterMap!=null&& parameterMap.containsKey("defSortString")) {
// 多字段排序
String sortInfoString = parameterMap.get("defSortString")[0];
log.info("默认多字段排序规则>> defSortString:" + sortInfoString);
List<OrderItem> orderItemList = SqlConcatUtil.getQueryConditionOrders(column, order, sortInfoString);
log.info(orderItemList.toString());
if (orderItemList != null && !orderItemList.isEmpty()) {
for (OrderItem item : orderItemList) {
// 一、获取排序数据库字段
String columnName = item.getColumn();
// 1.字典字段,去掉字典翻译文本后缀
if(columnName.endsWith(CommonConstant.DICT_TEXT_SUFFIX)) {
columnName = columnName.substring(0, column.lastIndexOf(CommonConstant.DICT_TEXT_SUFFIX));
}
// 2.实体驼峰字段转为数据库字段
columnName = SqlInjectionUtil.getSqlInjectSortField(columnName);
// 二、设置字段排序规则
if (item.isAsc()) {
queryWrapper.orderByAsc(columnName);
} else {
queryWrapper.orderByDesc(columnName);
}
}
}
return;
}
//TODO 避免用户自定义表无默认字段创建时间,导致排序报错
if(DataBaseConstant.CREATE_TIME.equals(column) && !fieldColumnMap.containsKey(DataBaseConstant.CREATE_TIME)){
column = "id";
log.warn("检测到实体里没有字段createTime改成采用ID排序");
}
//update-end-author:scott date:2022-11-07 for:避免用户自定义表无默认字段{创建时间},导致排序报错
if (oConvertUtils.isNotEmpty(column) && oConvertUtils.isNotEmpty(order)) {
//字典字段,去掉字典翻译文本后缀
@ -273,15 +328,12 @@ public class QueryGenerator {
column = column.substring(0, column.lastIndexOf(CommonConstant.DICT_TEXT_SUFFIX));
}
//update-begin-author:taoyan date:2022-5-16 for: issues/3676 获取系统用户列表时使用SQL注入生效
//判断column是不是当前实体的
log.debug("当前字段有:"+ allFields);
if (!allColumnExist(column, allFields)) {
throw new JeecgBootException("请注意,将要排序的列字段不存在:" + column);
}
//update-end-author:taoyan date:2022-5-16 for: issues/3676 获取系统用户列表时使用SQL注入生效
//update-begin-author:scott date:2022-10-10 for:【jeecg-boot/issues/I5FJU6】doMultiFieldsOrder() 多字段排序方法存在问题
//多字段排序方法没有读取 MybatisPlus 注解 @TableField 里 value 的值
if (column.contains(",")) {
List<String> columnList = Arrays.asList(column.split(","));
@ -292,12 +344,10 @@ public class QueryGenerator {
}else{
column = fieldColumnMap.get(column);
}
//update-end-author:scott date:2022-10-10 for:【jeecg-boot/issues/I5FJU6】doMultiFieldsOrder() 多字段排序方法存在问题
//SQL注入check
SqlInjectionUtil.filterContentMulti(column);
//update-begin--Author:scott Date:20210531 for36 多条件排序无效问题修正-------
// 排序规则修改
// 将现有排序 _ 前端传递排序条件{....,column: 'column1,column2',order: 'desc'} 翻译成sql "column1,column2 desc"
// 修改为 _ 前端传递排序条件{....,column: 'column1,column2',order: 'desc'} 翻译成sql "column1 desc,column2 desc"
@ -306,11 +356,9 @@ public class QueryGenerator {
} else {
queryWrapper.orderByDesc(SqlInjectionUtil.getSqlInjectSortFields(column.split(",")));
}
//update-end--Author:scott Date:20210531 for36 多条件排序无效问题修正-------
}
}
//update-begin-author:taoyan date:2022-5-23 for: issues/3676 获取系统用户列表时使用SQL注入生效
/**
* 多字段排序 判断所传字段是否存在
* @return
@ -330,7 +378,6 @@ public class QueryGenerator {
}
return exist;
}
//update-end-author:taoyan date:2022-5-23 for: issues/3676 获取系统用户列表时使用SQL注入生效
/**
* 高级查询
@ -343,42 +390,46 @@ public class QueryGenerator {
String superQueryParams = parameterMap.get(SUPER_QUERY_PARAMS)[0];
String superQueryMatchType = parameterMap.get(SUPER_QUERY_MATCH_TYPE) != null ? parameterMap.get(SUPER_QUERY_MATCH_TYPE)[0] : MatchTypeEnum.AND.getValue();
MatchTypeEnum matchType = MatchTypeEnum.getByValue(superQueryMatchType);
// update-begin--Author:sunjianlei Date:20200325 for高级查询的条件要用括号括起来,防止和用户的其他条件冲突 -------
// 代码逻辑说明: 高级查询的条件要用括号括起来,防止和用户的其他条件冲突 -------
try {
superQueryParams = URLDecoder.decode(superQueryParams, "UTF-8");
List<QueryCondition> conditions = JSON.parseArray(superQueryParams, QueryCondition.class);
if (conditions == null || conditions.size() == 0) {
return;
}
// update-begin-author:sunjianlei date:20220119 for: 【JTC-573】 过滤空条件查询,防止 sql 拼接多余的 and
// 代码逻辑说明: 【JTC-573】 过滤空条件查询,防止 sql 拼接多余的 and
List<QueryCondition> filterConditions = conditions.stream().filter(
rule -> oConvertUtils.isNotEmpty(rule.getField())
&& oConvertUtils.isNotEmpty(rule.getRule())
&& oConvertUtils.isNotEmpty(rule.getVal())
rule -> (oConvertUtils.isNotEmpty(rule.getField())
&& oConvertUtils.isNotEmpty(rule.getRule())
&& oConvertUtils.isNotEmpty(rule.getVal())
)
|| "empty".equals(rule.getRule())
).collect(Collectors.toList());
if (filterConditions.size() == 0) {
return;
}
// update-end-author:sunjianlei date:20220119 for: 【JTC-573】 过滤空条件查询,防止 sql 拼接多余的 and
log.debug("---高级查询参数-->" + filterConditions);
queryWrapper.and(andWrapper -> {
for (int i = 0; i < filterConditions.size(); i++) {
QueryCondition rule = filterConditions.get(i);
if (oConvertUtils.isNotEmpty(rule.getField())
&& oConvertUtils.isNotEmpty(rule.getRule())
&& oConvertUtils.isNotEmpty(rule.getVal())) {
if (
(
oConvertUtils.isNotEmpty(rule.getField()) && oConvertUtils.isNotEmpty(rule.getRule()) && oConvertUtils.isNotEmpty(rule.getVal())
)
|| "empty".equals(rule.getRule())
) {
log.debug("SuperQuery ==> " + rule.toString());
//update-begin-author:taoyan date:20201228 for: 【高级查询】 oracle 日期等于查询报错
// 代码逻辑说明: 【高级查询】 oracle 日期等于查询报错
Object queryValue = rule.getVal();
if("date".equals(rule.getType())){
queryValue = DateUtils.str2Date(rule.getVal(),DateUtils.date_sdf.get());
}else if("datetime".equals(rule.getType())){
queryValue = DateUtils.str2Date(rule.getVal(), DateUtils.datetimeFormat.get());
}
// update-begin--author:sunjianlei date:20210702 for【/issues/I3VR8E】高级查询没有类型转换查询参数都是字符串类型 ----
// 代码逻辑说明: 【/issues/I3VR8E】高级查询没有类型转换查询参数都是字符串类型 ----
String dbType = rule.getDbType();
if (oConvertUtils.isNotEmpty(dbType)) {
try {
@ -411,9 +462,8 @@ public class QueryGenerator {
log.error("高级查询值转换失败:", e);
}
}
// update-begin--author:sunjianlei date:20210702 for【/issues/I3VR8E】高级查询没有类型转换查询参数都是字符串类型 ----
// 代码逻辑说明: 【/issues/I3VR8E】高级查询没有类型转换查询参数都是字符串类型 ----
addEasyQuery(andWrapper, fieldColumnMap.get(rule.getField()), QueryRuleEnum.getByValue(rule.getRule()), queryValue);
//update-end-author:taoyan date:20201228 for: 【高级查询】 oracle 日期等于查询报错
// 如果拼接方式是OR就拼接OR
if (MatchTypeEnum.OR == matchType && i < (filterConditions.size() - 1)) {
@ -429,7 +479,6 @@ public class QueryGenerator {
log.error("--高级查询拼接失败:" + e.getMessage());
e.printStackTrace();
}
// update-end--Author:sunjianlei Date:20200325 for高级查询的条件要用括号括起来防止和用户的其他条件冲突 -------
}
//log.info(" superQuery getCustomSqlSegment: "+ queryWrapper.getCustomSqlSegment());
}
@ -441,7 +490,7 @@ public class QueryGenerator {
*/
public static QueryRuleEnum convert2Rule(Object value) {
// 避免空数据
// update-begin-author:taoyan date:20210629 for: 查询条件输入空格导致return null后续判断导致抛出null异常
// 代码逻辑说明: 查询条件输入空格导致return null后续判断导致抛出null异常
if (value == null) {
return QueryRuleEnum.EQ;
}
@ -449,10 +498,8 @@ public class QueryGenerator {
if (val.length() == 0) {
return QueryRuleEnum.EQ;
}
// update-end-author:taoyan date:20210629 for: 查询条件输入空格导致return null后续判断导致抛出null异常
QueryRuleEnum rule =null;
//update-begin--Author:scott Date:20190724 forinitQueryWrapper组装sql查询条件错误 #284-------------------
//TODO 此处规则,只适用于 le lt ge gt
// step 2 .>= =<
int length2 = 2;
@ -468,14 +515,12 @@ public class QueryGenerator {
rule = QueryRuleEnum.getByValue(val.substring(0, 1));
}
}
//update-end--Author:scott Date:20190724 forinitQueryWrapper组装sql查询条件错误 #284---------------------
// step 3 like
//update-begin-author:taoyan for: /issues/3382 默认带*就走模糊,但是如果只有一个*,那么走等于查询
// 代码逻辑说明: /issues/3382 默认带*就走模糊,但是如果只有一个*,那么走等于查询
if(rule == null && val.equals(STAR)){
rule = QueryRuleEnum.EQ;
}
//update-end-author:taoyan for: /issues/3382 默认带*就走模糊,但是如果只有一个*,那么走等于查询
if (rule == null && val.contains(STAR)) {
if (val.startsWith(STAR) && val.endsWith(STAR)) {
rule = QueryRuleEnum.LIKE;
@ -500,12 +545,10 @@ public class QueryGenerator {
rule = QueryRuleEnum.EQ_WITH_ADD;
}
//update-begin--Author:taoyan Date:20201229 forinitQueryWrapper组装sql查询条件错误 #284---------------------
//特殊处理Oracle的表达式to_date('xxx','yyyy-MM-dd')含有逗号会被识别为in查询转为等于查询
if(rule == QueryRuleEnum.IN && val.indexOf(YYYY_MM_DD)>=0 && val.indexOf(TO_DATE)>=0){
rule = QueryRuleEnum.EQ;
}
//update-end--Author:taoyan Date:20201229 forinitQueryWrapper组装sql查询条件错误 #284---------------------
return rule != null ? rule : QueryRuleEnum.EQ;
}
@ -525,11 +568,10 @@ public class QueryGenerator {
return value;
}
String val = (value + "").toString().trim();
//update-begin-author:taoyan date:20220302 for: 查询条件的值为等号(=bug #3443
// 代码逻辑说明: 查询条件的值为等号(=bug #3443
if(QueryRuleEnum.EQ.getValue().equals(val)){
return val;
}
//update-end-author:taoyan date:20220302 for: 查询条件的值为等号(=bug #3443
if (rule == QueryRuleEnum.LIKE) {
value = val.substring(1, val.length() - 1);
//mysql 模糊查询之特殊字符下划线 _、\
@ -547,21 +589,19 @@ public class QueryGenerator {
} else if (rule == QueryRuleEnum.EQ_WITH_ADD) {
value = val.replaceAll("\\+\\+", COMMA);
}else {
//update-begin--Author:scott Date:20190724 forinitQueryWrapper组装sql查询条件错误 #284-------------------
// 代码逻辑说明: initQueryWrapper组装sql查询条件错误 #284-------------------
if(val.startsWith(rule.getValue())){
//TODO 此处逻辑应该注释掉-> 如果查询内容中带有查询匹配规则符号,就会被截取的(比如:>=您好)
value = val.replaceFirst(rule.getValue(),"");
}else if(val.startsWith(rule.getCondition()+QUERY_SEPARATE_KEYWORD)){
value = val.replaceFirst(rule.getCondition()+QUERY_SEPARATE_KEYWORD,"").trim();
}
//update-end--Author:scott Date:20190724 forinitQueryWrapper组装sql查询条件错误 #284-------------------
}
return value;
}
private static void addQueryByRule(QueryWrapper<?> queryWrapper,String name,String type,String value,QueryRuleEnum rule) throws ParseException {
if(oConvertUtils.isNotEmpty(value)) {
//update-begin--Author:sunjianlei Date:20220104 for【JTC-409】修复逗号分割情况下没有转换类型导致类型严格的数据库查询报错 -------------------
// 针对数字类型字段,多值查询
if(value.contains(COMMA)){
Object[] temp = Arrays.stream(value.split(COMMA)).map(v -> {
@ -577,7 +617,6 @@ public class QueryGenerator {
}
Object temp = QueryGenerator.parseByType(value, type, rule);
addEasyQuery(queryWrapper, name, rule, temp);
//update-end--Author:sunjianlei Date:20220104 for【JTC-409】修复逗号分割情况下没有转换类型导致类型严格的数据库查询报错 -------------------
}
}
@ -654,7 +693,11 @@ public class QueryGenerator {
* @param value 查询条件值
*/
public static void addEasyQuery(QueryWrapper<?> queryWrapper, String name, QueryRuleEnum rule, Object value) {
if (name==null || value == null || rule == null || oConvertUtils.isEmpty(value)) {
if (
(
name==null || value == null || rule == null || oConvertUtils.isEmpty(value)
)
&& !QueryRuleEnum.EMPTY.equals(rule)) {
return;
}
name = oConvertUtils.camelToUnderline(name);
@ -666,6 +709,9 @@ public class QueryGenerator {
case GE:
queryWrapper.ge(name, value);
break;
case EMPTY:
queryWrapper.isNull(name);
break;
case LT:
queryWrapper.lt(name, value);
break;
@ -685,13 +731,12 @@ public class QueryGenerator {
}else if(value instanceof String[]) {
queryWrapper.in(name, (Object[]) value);
}
//update-begin-author:taoyan date:20200909 for:【bug】in 类型多值查询 不适配postgresql #1671
// 代码逻辑说明: 【bug】in 类型多值查询 不适配postgresql #1671
else if(value.getClass().isArray()) {
queryWrapper.in(name, (Object[])value);
}else {
queryWrapper.in(name, value);
}
//update-end-author:taoyan date:20200909 for:【bug】in 类型多值查询 不适配postgresql #1671
break;
case LIKE:
queryWrapper.like(name, value);
@ -708,7 +753,7 @@ public class QueryGenerator {
case NOT_RIGHT_LIKE:
queryWrapper.notLikeRight(name, value);
break;
//update-begin---author:chenrui ---date:20240527 for[TV360X-378]下拉多框根据条件查询不出来:增加自定义字段查询规则功能------------
// 代码逻辑说明: [TV360X-378]下拉多框根据条件查询不出来:增加自定义字段查询规则功能------------
case LIKE_WITH_OR:
final String nameFinal = name;
Object[] vals;
@ -717,7 +762,7 @@ public class QueryGenerator {
} else if (value instanceof String[]) {
vals = (Object[]) value;
}
//update-begin-author:taoyan date:20200909 for:【bug】in 类型多值查询 不适配postgresql #1671
// 代码逻辑说明: 【bug】in 类型多值查询 不适配postgresql #1671
else if (value.getClass().isArray()) {
vals = (Object[]) value;
} else {
@ -732,7 +777,6 @@ public class QueryGenerator {
}
});
break;
//update-end---author:chenrui ---date:20240527 for[TV360X-378]下拉多框根据条件查询不出来:增加自定义字段查询规则功能------------
default:
log.info("--查询规则未匹配到---");
break;
@ -746,10 +790,8 @@ public class QueryGenerator {
private static boolean judgedIsUselessField(String name) {
return "class".equals(name) || "ids".equals(name)
|| "page".equals(name) || "rows".equals(name)
//// update-begin--author:sunjianlei date:20240808 for【TV360X-2009】取消过滤 sort、order 字段,防止前端排序报错 ------
//// https://github.com/jeecgboot/JeecgBoot/issues/6937
// || "sort".equals(name) || "order".equals(name)
//// update-end----author:sunjianlei date:20240808 for【TV360X-2009】取消过滤 sort、order 字段,防止前端排序报错 ------
;
}
@ -762,13 +804,12 @@ public class QueryGenerator {
public static Map<String, SysPermissionDataRuleModel> getRuleMap() {
Map<String, SysPermissionDataRuleModel> ruleMap = new HashMap<>(5);
List<SysPermissionDataRuleModel> list = null;
//update-begin-author:taoyan date:2023-6-1 for:QQYUN-5441 【简流】获取多个用户/部门/角色 设置部门查询 报错
// 代码逻辑说明: QQYUN-5441 【简流】获取多个用户/部门/角色 设置部门查询 报错
try {
list = JeecgDataAutorUtils.loadDataSearchConditon();
}catch (Exception e){
log.error("根据request对象获取权限数据失败可能是定时任务中执行的。", e);
}
//update-end-author:taoyan date:2023-6-1 for:QQYUN-5441 【简流】获取多个用户/部门/角色 设置部门查询 报错
if(list != null&&list.size()>0){
if(list.get(0)==null){
return ruleMap;
@ -805,9 +846,8 @@ public class QueryGenerator {
addEasyQuery(queryWrapper, name, rule, DateUtils.str2Date(dateStr,DateUtils.datetimeFormat.get()));
}
}else {
//update-begin---author:chenrui ---date:20241125 for[issues/7481]多租户模式下 数据权限使用变量:#{tenant_id} 报错------------
// 代码逻辑说明: [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} 报错------------
}
}
}
@ -861,9 +901,8 @@ public class QueryGenerator {
return null;
}
Set<String> varParams = new HashSet<String>();
//update-begin---author:chenrui ---date:20250108 for[QQYUN-10785]数据权限,查看自己拥有部门的权限中存在问题 #7288------------
// 代码逻辑说明: [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);
@ -919,9 +958,8 @@ public class QueryGenerator {
Class propType = origDescriptors[i].getPropertyType();
boolean isString = propType.equals(String.class);
Object value;
//update-begin---author:chenrui ---date:20240527 for[TV360X-539]数据权限,配置日期等于条件时后端报转换错误------------
// 代码逻辑说明: [TV360X-539]数据权限,配置日期等于条件时后端报转换错误------------
if(isString || Date.class.equals(propType)) {
//update-end---author:chenrui ---date:20240527 for[TV360X-539]数据权限,配置日期等于条件时后端报转换错误------------
value = converRuleValue(dataRule.getRuleValue());
}else {
value = NumberUtils.parseNumber(dataRule.getRuleValue(),propType);

View File

@ -41,8 +41,10 @@ import org.jeecg.common.util.oConvertUtils;
@Slf4j
public class JwtUtil {
/**Token有效期为7天Token在reids中缓存时间为两倍*/
public static final long EXPIRE_TIME = (7 * 12) * 60 * 60 * 1000;
/**PC端Token有效期为7天Token在reids中缓存时间为两倍*/
public static final long EXPIRE_TIME = (7 * 12) * 60 * 60 * 1000L;
/**APP端Token有效期为30天Token在reids中缓存时间为两倍*/
public static final long APP_EXPIRE_TIME = (30 * 12) * 60 * 60 * 1000L;
static final String WELL_NUMBER = SymbolConstant.WELL_NUMBER + SymbolConstant.LEFT_CURLY_BRACKET;
/**
@ -51,24 +53,24 @@ public class JwtUtil {
* @param code
* @param errorMsg
*/
public static void responseError(ServletResponse response, Integer code, String errorMsg) {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
// issues/I4YH95浏览器显示乱码问题
httpServletResponse.setHeader("Content-type", "text/html;charset=UTF-8");
Result jsonResult = new Result(code, errorMsg);
jsonResult.setSuccess(false);
OutputStream os = null;
try {
os = httpServletResponse.getOutputStream();
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setStatus(code);
os.write(new ObjectMapper().writeValueAsString(jsonResult).getBytes("UTF-8"));
os.flush();
os.close();
} catch (IOException e) {
public static void responseError(HttpServletResponse response, Integer code, String errorMsg) {
try {
Result jsonResult = new Result(code, errorMsg);
jsonResult.setSuccess(false);
// 设置响应头和内容类型
response.setStatus(code);
response.setHeader("Content-type", "text/html;charset=UTF-8");
response.setContentType("application/json;charset=UTF-8");
// 使用 ObjectMapper 序列化为 JSON 字符串
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(jsonResult);
response.getWriter().write(json);
response.getWriter().flush();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
}
}
/**
* 校验token是否正确
@ -86,7 +88,7 @@ public class JwtUtil {
DecodedJWT jwt = verifier.verify(token);
return true;
} catch (Exception e) {
log.error(e.getMessage(), e);
log.warn("Token验证失败" + e.getMessage(),e);
return false;
}
}
@ -101,7 +103,7 @@ public class JwtUtil {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("username").asString();
} catch (JWTDecodeException e) {
log.warn(e.getMessage(), e);
log.error(e.getMessage(), e);
return null;
}
}
@ -112,7 +114,9 @@ public class JwtUtil {
* @param username 用户名
* @param secret 用户的密码
* @return 加密的token
* @deprecated 请使用sign(String username, String secret, String clientType)方法代替
*/
@Deprecated
public static String sign(String username, String secret) {
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
Algorithm algorithm = Algorithm.HMAC256(secret);
@ -121,6 +125,68 @@ public class JwtUtil {
}
/**
* 生成签名,5min后过期
*
* @param username 用户名
* @param secret 用户的密码
* @param expireTime 过期时间
* @return 加密的token
* @deprecated 请使用sign(String username, String secret, String clientType)方法代替
*/
@Deprecated
public static String sign(String username, String secret, Long expireTime) {
Date date = new Date(System.currentTimeMillis() + expireTime);
Algorithm algorithm = Algorithm.HMAC256(secret);
// 附带username信息
return JWT.create().withClaim("username", username).withExpiresAt(date).sign(algorithm);
}
/**
* 生成签名,根据客户端类型自动选择过期时间
* for [JHHB-1030]【鉴权】移动端用户token到期后续期时间变成pc端时长
*
* @param username 用户名
* @param secret 用户的密码
* @param clientType 客户端类型PC或APP
* @return 加密的token
*/
public static String sign(String username, String secret, String clientType) {
// 根据客户端类型选择对应的过期时间
long expireTime = CommonConstant.CLIENT_TYPE_APP.equalsIgnoreCase(clientType)
? APP_EXPIRE_TIME
: EXPIRE_TIME;
Date date = new Date(System.currentTimeMillis() + expireTime);
Algorithm algorithm = Algorithm.HMAC256(secret);
// 附带username和clientType信息
return JWT.create()
.withClaim("username", username)
.withClaim("clientType", clientType)
.withExpiresAt(date)
.sign(algorithm);
}
/**
* 从token中获取客户端类型
* for [JHHB-1030]【鉴权】移动端用户token到期后续期时间变成pc端时长
*
* @param token JWT token
* @return 客户端类型如果不存在则返回PC兼容旧token
*/
public static String getClientType(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
String clientType = jwt.getClaim("clientType").asString();
// 如果clientType为空返回默认值PC兼容旧token
return oConvertUtils.isNotEmpty(clientType) ? clientType : CommonConstant.CLIENT_TYPE_PC;
} catch (JWTDecodeException e) {
log.warn("解析token中的clientType失败使用默认值PC" + e.getMessage());
return CommonConstant.CLIENT_TYPE_PC;
}
}
/**
* 根据request中的token获取用户账号
*
@ -200,7 +266,6 @@ 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+]$")){
@ -209,7 +274,6 @@ public class JwtUtil {
} 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();
@ -278,20 +342,17 @@ public class JwtUtil {
if(user==null){
//TODO 暂时使用用户登录部门,存在逻辑缺陷,不是用户所拥有的部门
returnValue = sysUser.getOrgCode();
//update-begin---author:chenrui ---date:20250107 for[QQYUN-10785]数据权限,查看自己拥有部门的权限中存在问题 #7288------------
// 代码逻辑说明: [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------------
// 代码逻辑说明: [QQYUN-10785]数据权限,查看自己拥有部门的权限中存在问题 #7288------------
returnValue = multiStr ? "'" + returnValue + "'" : returnValue;
//update-end---author:chenrui ---date:20250107 for[QQYUN-10785]数据权限,查看自己拥有部门的权限中存在问题 #7288------------
}else {
//update-begin---author:chenrui ---date:20250107 for[QQYUN-10785]数据权限,查看自己拥有部门的权限中存在问题 #7288------------
// 代码逻辑说明: [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 + "'";
@ -299,9 +360,7 @@ public class JwtUtil {
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------------
}
}
}
@ -315,7 +374,7 @@ public class JwtUtil {
}
}
//update-begin-author:taoyan date:20210330 for:多租户ID作为系统变量
// 代码逻辑说明: 多租户ID作为系统变量
else if (key.equals(TenantConstant.TENANT_ID) || key.toLowerCase().equals(TenantConstant.TENANT_ID_TABLE)){
try {
returnValue = SpringContextUtils.getHttpServletRequest().getHeader(CommonConstant.TENANT_ID);
@ -323,7 +382,6 @@ public class JwtUtil {
log.warn("获取系统租户异常:" + e.getMessage());
}
}
//update-end-author:taoyan date:20210330 for:多租户ID作为系统变量
if(returnValue!=null){returnValue = returnValue + moshi;}
return returnValue;
}

View File

@ -13,31 +13,33 @@ import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.lang.reflect.Method;
import java.util.*;
/**
* 资源加载工具类
* 枚举字典数据 资源加载工具类
*
* @Author taoYan
* @Date 2022/7/8 10:40
**/
@Slf4j
public class ResourceUtil {
/**
* 多个包扫描根路径
*
* 之所以让用户手工配置扫描路径,是为了避免不必要的类加载开销,提升启动性能。
* 请务必将所有枚举类所在包路径添加到此配置中。
*/
private final static String[] BASE_SCAN_PACKAGES = {
"org.jeecg.common.constant.enums",
"org.jeecg.modules.message.enums"
};
/**
* 枚举字典数据
*/
private final static Map<String, List<DictModel>> enumDictData = new HashMap<>(5);
/**
* 所有java类
*/
private final static String CLASS_PATTERN="/**/*.class";
/**
* 所有枚举java类
*/
@ -45,9 +47,9 @@ public class ResourceUtil {
private final static String CLASS_ENUM_PATTERN="/**/*Enum.class";
/**
* 包路径 org.jeecg
* 初始化状态标识
*/
private final static String BASE_PACKAGE = "org.jeecg";
private static volatile boolean initialized = false;
/**
* 枚举类中获取字典数据的方法名
@ -55,59 +57,135 @@ public class ResourceUtil {
private final static String METHOD_NAME = "getDictList";
/**
* 获取枚举字典数据
* 获取枚举类对应的字典数据 SysDictServiceImpl#queryAllDictItems()
* @return
*
* @return 枚举字典数据
*/
public static Map<String, List<DictModel>> getEnumDictData(){
if(enumDictData.keySet().size()>0){
return enumDictData;
}
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(BASE_PACKAGE) + CLASS_ENUM_PATTERN;
try {
Resource[] resources = resourcePatternResolver.getResources(pattern);
MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
for (Resource resource : resources) {
MetadataReader reader = readerFactory.getMetadataReader(resource);
String classname = reader.getClassMetadata().getClassName();
Class<?> clazz = Class.forName(classname);
EnumDict enumDict = clazz.getAnnotation(EnumDict.class);
if (enumDict != null) {
EnumDict annotation = clazz.getAnnotation(EnumDict.class);
String key = annotation.value();
if(oConvertUtils.isNotEmpty(key)){
List<DictModel> list = (List<DictModel>) clazz.getDeclaredMethod(METHOD_NAME).invoke(null);
enumDictData.put(key, list);
}
public static Map<String, List<DictModel>> getEnumDictData() {
if (!initialized) {
synchronized (ResourceUtil.class) {
if (!initialized) {
long startTime = System.currentTimeMillis();
log.debug("【枚举字典加载】开始初始化枚举字典数据...");
initEnumDictData();
initialized = true;
long endTime = System.currentTimeMillis();
log.debug("【枚举字典加载】枚举字典数据初始化完成,共加载 {} 个字典,总耗时: {}ms", enumDictData.size(), endTime - startTime);
}
}
}catch (Exception e){
log.error("获取枚举类字典数据异常", e.getMessage());
// e.printStackTrace();
}
return enumDictData;
}
/**
* 用于后端字典翻译 SysDictServiceImpl#queryManyDictByKeys(java.util.List, java.util.List)
* @param dictCodeList
* @param keys
* @return
* 使用多包路径扫描方式初始化枚举字典数据
*/
public static Map<String, List<DictModel>> queryManyDictByKeys(List<String> dictCodeList, List<String> keys){
if(enumDictData.keySet().size()==0){
getEnumDictData();
private static void initEnumDictData() {
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
long scanStartTime = System.currentTimeMillis();
List<Resource> allResources = new ArrayList<>();
// 扫描多个包路径
for (String basePackage : BASE_SCAN_PACKAGES) {
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(basePackage) + CLASS_ENUM_PATTERN;
try {
Resource[] resources = resourcePatternResolver.getResources(pattern);
allResources.addAll(Arrays.asList(resources));
log.debug("【枚举字典加载】扫描包 {} 找到 {} 个枚举类文件", basePackage, resources.length);
} catch (Exception e) {
log.warn("【枚举字典加载】扫描包 {} 时出现异常: {}", basePackage, e.getMessage());
}
}
long scanEndTime = System.currentTimeMillis();
log.debug("【枚举字典加载】文件扫描完成,总共找到 {} 个枚举类文件,扫描耗时: {}ms", allResources.size(), scanEndTime - scanStartTime);
MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
long processStartTime = System.currentTimeMillis();
int processedCount = 0;
for (Resource resource : allResources) {
try {
MetadataReader reader = readerFactory.getMetadataReader(resource);
String classname = reader.getClassMetadata().getClassName();
// 提前检查是否有@EnumDict注解避免不必要的Class.forName
if (hasEnumDictAnnotation(reader)) {
processEnumClass(classname);
processedCount++;
}
} catch (Exception e) {
log.debug("处理资源异常: {} - {}", resource.getFilename(), e.getMessage());
}
}
long processEndTime = System.currentTimeMillis();
log.debug("【枚举字典加载】处理完成,实际处理 {} 个带注解的枚举类,处理耗时: {}ms", processedCount, processEndTime - processStartTime);
}
/**
* 检查类是否有EnumDict注解通过元数据避免类加载
*/
private static boolean hasEnumDictAnnotation(MetadataReader reader) {
try {
return reader.getAnnotationMetadata().hasAnnotation(EnumDict.class.getName());
} catch (Exception e) {
return false;
}
}
/**
* 处理单个枚举类
*/
private static void processEnumClass(String classname) {
try {
Class<?> clazz = Class.forName(classname);
EnumDict enumDict = clazz.getAnnotation(EnumDict.class);
if (enumDict != null) {
String key = enumDict.value();
if (oConvertUtils.isNotEmpty(key)) {
Method method = clazz.getDeclaredMethod(METHOD_NAME);
List<DictModel> list = (List<DictModel>) method.invoke(null);
enumDictData.put(key, list);
log.debug("成功加载枚举字典: {} -> {}", key, classname);
}
}
} catch (Exception e) {
log.debug("处理枚举类异常: {} - {}", classname, e.getMessage());
}
}
/**
* 用于后端字典翻译 SysDictServiceImpl#queryManyDictByKeys(java.util.List, java.util.List)
*
* @param dictCodeList 字典编码列表
* @param keys 键值列表
* @return 字典数据映射
*/
public static Map<String, List<DictModel>> queryManyDictByKeys(List<String> dictCodeList, List<String> keys) {
Map<String, List<DictModel>> enumDict = getEnumDictData();
Map<String, List<DictModel>> map = new HashMap<>();
for (String code : enumDictData.keySet()) {
if(dictCodeList.indexOf(code)>=0){
List<DictModel> dictItemList = enumDictData.get(code);
for(DictModel dm: dictItemList){
// 使用更高效的查找方式
Set<String> dictCodeSet = new HashSet<>(dictCodeList);
Set<String> keySet = new HashSet<>(keys);
for (String code : enumDict.keySet()) {
if (dictCodeSet.contains(code)) {
List<DictModel> dictItemList = enumDict.get(code);
for (DictModel dm : dictItemList) {
String value = dm.getValue();
if(keys.indexOf(value)>=0){
if (keySet.contains(value)) {
List<DictModel> list = new ArrayList<>();
list.add(new DictModel(value, dm.getText()));
map.put(code,list);
map.put(code, list);
break;
}
}
@ -115,22 +193,5 @@ public class ResourceUtil {
}
return map;
}
/**
* 获取实现类
*
* @param classPath
*/
public static Object getImplementationClass(String classPath){
try {
Class<?> aClass = Class.forName(classPath);
return SpringContextUtils.getBean(aClass);
} catch (ClassNotFoundException e) {
log.error("类没有找到",e);
return null;
} catch (NoSuchBeanDefinitionException e){
log.error(classPath + "没有实现",e);
return null;
}
}
}
}

View File

@ -1,13 +1,22 @@
package org.jeecg.common.system.util;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.DataBaseConstant;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.query.QueryRuleEnum;
import org.jeecg.common.util.CommonUtils;
import org.jeecg.common.util.oConvertUtils;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
@ -141,7 +150,7 @@ public class SqlConcatUtil {
}
private static String getInConditionValue(Object value,boolean isString) {
//update-begin-author:taoyan date:20210628 for: 查询条件如果输入,导致sql报错
// 代码逻辑说明: 查询条件如果输入,导致sql报错
String[] temp = value.toString().split(",");
if(temp.length==0){
return "('')";
@ -159,7 +168,6 @@ public class SqlConcatUtil {
}else {
return "("+value.toString()+")";
}
//update-end-author:taoyan date:20210628 for: 查询条件如果输入,导致sql报错
}
/**
@ -206,7 +214,6 @@ public class SqlConcatUtil {
}
}else {
//update-begin-author:taoyan date:2022-6-30 for: issues/3810 数据权限规则问题
// 走到这里说明 value不带有任何模糊查询的标识(*或者%)
if (ruleEnum == QueryRuleEnum.LEFT_LIKE) {
if (DataBaseConstant.DB_TYPE_SQLSERVER.equals(getDbType())) {
@ -227,7 +234,6 @@ public class SqlConcatUtil {
return "'%" + str + "%'";
}
}
//update-end-author:taoyan date:2022-6-30 for: issues/3810 数据权限规则问题
}
}
@ -239,5 +245,47 @@ public class SqlConcatUtil {
private static String getDbType() {
return CommonUtils.getDatabaseType();
}
/**
* 获取前端传过来的 "多字段排序信息: sortInfoString"
* @return
*/
public static List<OrderItem> getQueryConditionOrders(String column, String order, String queryInfoString){
List<OrderItem> list = new ArrayList<>();
if(oConvertUtils.isEmpty(queryInfoString)){
//默认以创建时间倒序查询
if(CommonConstant.ORDER_TYPE_DESC.equalsIgnoreCase(order)){
list.add(OrderItem.desc(column));
}else{
list.add(OrderItem.asc(column));
}
}else{
// 【TV360X-967】URL解码微服务下需要
if (queryInfoString.contains("%22column%22")) {
log.info("queryInfoString 原生 = {}", queryInfoString);
try {
queryInfoString = URLDecoder.decode(queryInfoString, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new JeecgBootException(e);
}
log.info("queryInfoString 解码 = {}", queryInfoString);
}
JSONArray array = JSONArray.parseArray(queryInfoString);
Iterator it = array.iterator();
while(it.hasNext()){
JSONObject json = (JSONObject)it.next();
String tempColumn = json.getString("column");
if(oConvertUtils.isNotEmpty(tempColumn)){
String tempOrder = json.getString("order");
if(CommonConstant.ORDER_TYPE_DESC.equalsIgnoreCase(tempOrder)){
list.add(OrderItem.desc(tempColumn));
}else{
list.add(OrderItem.asc(tempColumn));
}
}
}
}
return list;
}
}

View File

@ -68,6 +68,12 @@ public class LoginUser {
@SensitiveField
private String avatar;
/**
* 工号
*/
@SensitiveField
private String workNo;
/**
* 生日
*/
@ -138,4 +144,8 @@ public class LoginUser {
/**设备id uniapp推送用*/
private String clientId;
/**
* 主岗位
*/
private String mainDepPostId;
}

View File

@ -25,6 +25,7 @@ import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
@ -152,9 +153,9 @@ public class CommonUtils {
*/
public static String uploadLocal(MultipartFile mf,String bizPath,String uploadpath){
try {
//update-begin-author:liusq date:20210809 for: 过滤上传文件类型
SsrfFileTypeFilter.checkUploadFileType(mf);
//update-end-author:liusq date:20210809 for: 过滤上传文件类型
// 文件安全校验,防止上传漏洞文件
SsrfFileTypeFilter.checkUploadFileType(mf, bizPath);
String fileName = null;
File file = new File(uploadpath + File.separator + bizPath + File.separator );
if (!file.exists()) {
@ -163,6 +164,10 @@ public class CommonUtils {
}
// 获取文件名
String orgName = mf.getOriginalFilename();
// 无中文情况下进行转码
if (orgName != null && !CommonUtils.ifContainChinese(orgName)) {
orgName = new String(orgName.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
}
orgName = CommonUtils.getFileName(orgName);
if(orgName.indexOf(SymbolConstant.SPOT)!=-1){
fileName = orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.lastIndexOf("."));
@ -242,6 +247,10 @@ public class CommonUtils {
try {
DataSource dataSource = SpringContextUtils.getApplicationContext().getBean(DataSource.class);
dbTypeEnum = JdbcUtils.getDbType(dataSource.getConnection().getMetaData().getURL());
//【采用SQL_SERVER2005引擎】QQYUN-13298 解决升级mybatisPlus后SqlServer分页使用OFFSET无排序字段报错问题
if (dbTypeEnum == DbType.SQL_SERVER) {
dbTypeEnum = DbType.SQL_SERVER2005;
}
return dbTypeEnum;
} catch (SQLException e) {
log.warn(e.getMessage(), e);

View File

@ -13,6 +13,8 @@ import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
@ -814,4 +816,44 @@ public class DateUtils extends PropertyEditorSupport {
return calendar1.get(Calendar.YEAR) == calendar2.get(Calendar.YEAR);
}
/**
* 获取两个日期之间的所有日期列表,包含开始和结束日期
*
* @param begin
* @param end
* @return
*/
public static List<Date> getDateRangeList(Date begin, Date end) {
List<Date> dateList = new ArrayList<>();
if (begin == null || end == null) {
return dateList;
}
// 清除时间部分,只比较日期
Calendar beginCal = Calendar.getInstance();
beginCal.setTime(begin);
beginCal.set(Calendar.HOUR_OF_DAY, 0);
beginCal.set(Calendar.MINUTE, 0);
beginCal.set(Calendar.SECOND, 0);
beginCal.set(Calendar.MILLISECOND, 0);
Calendar endCal = Calendar.getInstance();
endCal.setTime(end);
endCal.set(Calendar.HOUR_OF_DAY, 0);
endCal.set(Calendar.MINUTE, 0);
endCal.set(Calendar.SECOND, 0);
endCal.set(Calendar.MILLISECOND, 0);
if (endCal.before(beginCal)) {
return dateList;
}
dateList.add(beginCal.getTime());
while (beginCal.before(endCal)) {
beginCal.add(Calendar.DAY_OF_YEAR, 1);
dateList.add(beginCal.getTime());
}
return dateList;
}
}

View File

@ -10,7 +10,9 @@ 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.CommonConstant;
import org.jeecg.common.constant.enums.DySmsEnum;
import org.jeecg.config.JeecgBaseConfig;
import org.jeecg.config.JeecgSmsTemplateConfig;
import org.jeecg.config.StaticConfig;
import org.slf4j.Logger;
@ -61,17 +63,21 @@ public class DySmsHelper {
public static boolean sendSms(String phone, JSONObject templateParamJson, DySmsEnum dySmsEnum) throws ClientException {
//可自助调整超时时间
JeecgBaseConfig config = SpringContextUtils.getBean(JeecgBaseConfig.class);
String smsSendType = config.getSmsSendType();
if(oConvertUtils.isNotEmpty(smsSendType) && CommonConstant.SMS_SEND_TYPE_TENCENT.equals(smsSendType)){
return TencentSms.sendTencentSms(phone, templateParamJson, config.getTencent(), dySmsEnum);
}
//可自助调整超时时间
System.setProperty("sun.net.client.defaultConnectTimeout", "10000");
System.setProperty("sun.net.client.defaultReadTimeout", "10000");
//update-begin-authortaoyan date:20200811 for:配置类数据获取
// 代码逻辑说明: 配置类数据获取
StaticConfig staticConfig = SpringContextUtils.getBean(StaticConfig.class);
//logger.info("阿里大鱼短信秘钥 accessKeyId" + staticConfig.getAccessKeyId());
//logger.info("阿里大鱼短信秘钥 accessKeySecret"+ staticConfig.getAccessKeySecret());
setAccessKeyId(staticConfig.getAccessKeyId());
setAccessKeySecret(staticConfig.getAccessKeySecret());
//update-end-authortaoyan date:20200811 for:配置类数据获取
//初始化acsClient,暂不支持region化
IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
@ -81,7 +87,7 @@ public class DySmsHelper {
//验证json参数
validateParam(templateParamJson,dySmsEnum);
//update-begin---author:wangshuai---date:2024-11-05---for:【QQYUN-9422】短信模板管理阿里云---
// 代码逻辑说明: 【QQYUN-9422】短信模板管理阿里云---
String templateCode = dySmsEnum.getTemplateCode();
JeecgSmsTemplateConfig baseConfig = SpringContextUtils.getBean(JeecgSmsTemplateConfig.class);
if(baseConfig != null && CollectionUtil.isNotEmpty(baseConfig.getTemplateCode())){
@ -97,7 +103,6 @@ public class DySmsHelper {
logger.info("yml中读取签名名称{}",baseConfig.getSignature());
signName = baseConfig.getSignature();
}
//update-end---author:wangshuai---date:2024-11-05---for:【QQYUN-9422】短信模板管理阿里云---
//组装请求对象-具体描述见控制台-文档部分内容
SendSmsRequest request = new SendSmsRequest();

View File

@ -1,14 +1,22 @@
package org.jeecg.common.util;
import cn.hutool.core.io.IoUtil;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.util.filter.SsrfFileTypeFilter;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.List;
import java.util.zip.ZipEntry;
@ -203,4 +211,150 @@ public class FileDownloadUtils {
dir.mkdirs();
}
}
/**
* 下载单个文件到ZIP流
* 核心功能获取文件流写入ZIP条目
* @param fileUrl 文件URL可以是HTTP URL或本地路径
* @param fileName ZIP内的文件名
* @param zous ZIP输出流
*/
public static void downLoadSingleFile(String fileUrl, String fileName, String uploadUrl,ZipArchiveOutputStream zous) {
InputStream inputStream = null;
try {
// 创建ZIP条目每个文件在ZIP中都是一个独立条目
ZipArchiveEntry entry = new ZipArchiveEntry(fileName);
zous.putArchiveEntry(entry);
// 获取文件输入流:区分普通文件和快捷方式
if (fileUrl.endsWith(".url")) {
// 处理快捷方式:生成.url文件内容
inputStream = FileDownloadUtils.createInternetShortcut(fileName, fileUrl, "");
} else {
// 普通文件下载从URL或本地路径获取流
inputStream = getDownInputStream(fileUrl,uploadUrl);
}
if (inputStream != null) {
// 将文件流写入ZIP
IOUtils.copy(inputStream, zous);
}
// 关闭当前ZIP条目
zous.closeArchiveEntry();
} catch (IOException e) {
log.error("文件下载失败: {}", e);
} finally {
// 确保输入流关闭
IoUtil.close(inputStream);
}
}
/**
* 获取下载文件输入流
* 功能根据URL类型HTTP或本地获取文件流
* @param fileUrl 文件URL支持HTTP和本地路径
* @return 文件输入流失败返回null
*/
public static InputStream getDownInputStream(String fileUrl, String uploadUrl) {
try {
// 处理HTTP URL通过网络下载
if (oConvertUtils.isNotEmpty(fileUrl) && fileUrl.startsWith(CommonConstant.STR_HTTP)) {
URL url = new URL(fileUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000); // 连接超时5秒
connection.setReadTimeout(30000); // 读取超时30秒
return connection.getInputStream();
} else {
// 处理本地文件:直接读取文件系统
String downloadFilePath = uploadUrl + File.separator + fileUrl;
// 安全检查:防止下载危险文件类型
SsrfFileTypeFilter.checkDownloadFileType(downloadFilePath);
return new BufferedInputStream(new FileInputStream(downloadFilePath));
}
} catch (IOException e) {
// 异常时返回null上层会处理空流情况
return null;
}
}
/**
* 获取文件扩展名
* 功能:从文件名中提取扩展名
* @param fileName 文件名
* @return 文件扩展名(不含点),如"txt"、"png"
*/
public static String getFileExtension(String fileName) {
int dotIndex = fileName.lastIndexOf('.');
return (dotIndex == -1) ? "" : fileName.substring(dotIndex + 1);
}
/**
* 创建快捷方式(.url文件内容
* 功能生成Internet快捷方式文件内容
* @param name 快捷方式名称
* @param url 目标URL地址
* @param icon 图标路径(可选)
* @return 包含.url文件内容的输入流
*/
public static InputStream createInternetShortcut(String name, String url, String icon) {
StringWriter sw = new StringWriter();
try {
// 按照Windows快捷方式格式写入内容
sw.write("[InternetShortcut]\n");
sw.write("URL=" + url + "\n");
if (oConvertUtils.isNotEmpty(icon)) {
sw.write("IconFile=" + icon + "\n");
}
// 将字符串内容转换为输入流
return new ByteArrayInputStream(sw.toString().getBytes(StandardCharsets.UTF_8));
} finally {
IoUtil.close(sw);
}
}
/**
* 从URL中提取文件名
* 功能从HTTP URL或本地路径中提取纯文件名
* @param fileUrl 文件URL
* @return 文件名(不含路径)
*/
public static String getFileNameFromUrl(String fileUrl) {
try {
// 处理HTTP URL从路径部分提取文件名
if (fileUrl.startsWith(CommonConstant.STR_HTTP)) {
URL url = new URL(fileUrl);
String path = url.getPath();
return path.substring(path.lastIndexOf('/') + 1);
}
// 处理本地文件路径:从文件路径提取文件名
return fileUrl.substring(fileUrl.lastIndexOf(File.separator) + 1);
} catch (Exception e) {
// 如果解析失败,使用时间戳作为文件名
return "file_" + System.currentTimeMillis();
}
}
/**
* 生成ZIP中的文件名
* 功能:避免文件名冲突,为多个文件添加序号
* @param fileUrl 文件URL用于提取原始文件名
* @param index 文件序号从0开始
* @param total 文件总数
* @return 处理后的文件名(带序号)
*/
public static String generateFileName(String fileUrl, int index, int total) {
// 从URL中提取原始文件名
String originalFileName = getFileNameFromUrl(fileUrl);
// 如果只有一个文件,直接使用原始文件名
if (total == 1) {
return originalFileName;
}
// 多个文件时,使用序号+原始文件名
String extension = getFileExtension(originalFileName);
String nameWithoutExtension = originalFileName.replace("." + extension, "");
return String.format("%s_%d.%s", nameWithoutExtension, index + 1, extension);
}
}

View File

@ -38,14 +38,12 @@ public class HTMLUtils {
* @return
*/
public static String parseMarkdown(String markdownContent) {
//update-begin---author:wangshuai---date:2024-06-26---for:【TV360X-1344】JDK17 邮箱发送失败,需要换写法---
/*PegDownProcessor pdp = new PegDownProcessor();
return pdp.markdownToHtml(markdownContent);*/
Parser parser = Parser.builder().build();
Node document = parser.parse(markdownContent);
HtmlRenderer renderer = HtmlRenderer.builder().build();
return renderer.render(document);
//update-end---author:wangshuai---date:2024-06-26---for:【TV360X-1344】JDK17 邮箱发送失败,需要换写法---
}
}

View File

@ -55,13 +55,11 @@ public class MinioUtil {
*/
public static String upload(MultipartFile file, String bizPath, String customBucket) throws Exception {
String fileUrl = "";
//update-begin-author:wangshuai date:20201012 for: 过滤上传文件夹名特殊字符,防止攻击
// 业务路径过滤,防止攻击
bizPath = StrAttackFilter.filter(bizPath);
//update-end-author:wangshuai date:20201012 for: 过滤上传文件夹名特殊字符,防止攻击
//update-begin-author:liusq date:20210809 for: 过滤上传文件类型
SsrfFileTypeFilter.checkUploadFileType(file);
//update-end-author:liusq date:20210809 for: 过滤上传文件类型
// 文件安全校验,防止上传漏洞文件
SsrfFileTypeFilter.checkUploadFileType(file, bizPath);
String newBucket = bucketName;
if(oConvertUtils.isNotEmpty(customBucket)){
@ -163,11 +161,10 @@ public class MinioUtil {
public static String getObjectUrl(String bucketName, String objectName, Integer expires) {
initMinio(minioUrl, minioName,minioPass);
try{
//update-begin---author:liusq Date:20220121 for获取文件外链报错提示method不能为空导致文件下载和预览失败----
// 代码逻辑说明: 获取文件外链报错提示method不能为空导致文件下载和预览失败----
GetPresignedObjectUrlArgs objectArgs = GetPresignedObjectUrlArgs.builder().object(objectName)
.bucket(bucketName)
.expiry(expires).method(Method.GET).build();
//update-begin---author:liusq Date:20220121 for获取文件外链报错提示method不能为空导致文件下载和预览失败----
String url = minioClient.getPresignedObjectUrl(objectArgs);
return URLDecoder.decode(url,"UTF-8");
}catch (Exception e){

View File

@ -0,0 +1,78 @@
package org.jeecg.common.util;
import org.apache.commons.fileupload.FileItem;
import org.springframework.web.multipart.MultipartFile;
import java.io.*;
/**
* @date 2025-09-04
* @author scott
*
* 升级springboot3 无法使用 CommonsMultipartFile
* 自定义 MultipartFile 实现类,支持从 FileItem 构造
*/
public class MyCommonsMultipartFile implements MultipartFile {
private final byte[] fileContent;
private final String fileName;
private final String contentType;
// 新增构造方法,支持 FileItem 参数
public MyCommonsMultipartFile(FileItem fileItem) throws IOException {
this.fileName = fileItem.getName();
this.contentType = fileItem.getContentType();
try (InputStream inputStream = fileItem.getInputStream()) {
this.fileContent = inputStream.readAllBytes();
}
}
// 现有构造方法
public MyCommonsMultipartFile(InputStream inputStream, String fileName, String contentType) throws IOException {
this.fileName = fileName;
this.contentType = contentType;
this.fileContent = inputStream.readAllBytes();
}
@Override
public String getName() {
return fileName;
}
@Override
public String getOriginalFilename() {
return fileName;
}
@Override
public String getContentType() {
return contentType;
}
@Override
public boolean isEmpty() {
return fileContent.length == 0;
}
@Override
public long getSize() {
return fileContent.length;
}
@Override
public byte[] getBytes() {
return fileContent;
}
@Override
public InputStream getInputStream() {
return new ByteArrayInputStream(fileContent);
}
@Override
public void transferTo(File dest) throws IOException {
try (OutputStream os = new FileOutputStream(dest)) {
os.write(fileContent);
}
}
}

View File

@ -99,9 +99,8 @@ public class PasswordUtil {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key, parameterSpec);
//update-begin-author:sccott date:20180815 for:中文作为用户名时加密的密码windows和linux会得到不同的结果 gitee/issues/IZUD7
// 代码逻辑说明: 中文作为用户名时加密的密码windows和linux会得到不同的结果 gitee/issues/IZUD7
encipheredData = cipher.doFinal(plaintext.getBytes("utf-8"));
//update-end-author:sccott date:20180815 for:中文作为用户名时加密的密码windows和linux会得到不同的结果 gitee/issues/IZUD7
} catch (Exception e) {
}
return bytesToHexString(encipheredData);

View File

@ -2,6 +2,7 @@ package org.jeecg.common.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import java.io.BufferedWriter;
@ -16,6 +17,7 @@ import java.util.List;
*/
@Slf4j
@Component
@Lazy(false)
public class PmsUtil {

View File

@ -4,7 +4,7 @@ import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.*;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
@ -56,12 +56,19 @@ public class RestUtil {
private final static RestTemplate RT;
static {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
// 解决[issues/8859]online表单java增强失效------------
// 使用 Apache HttpClient 避免 JDK HttpURLConnection 的 too many bytes written 问题
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setConnectTimeout(30000);
requestFactory.setReadTimeout(30000);
RT = new RestTemplate(requestFactory);
// 解决乱码问题
RT.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
// 解决乱码问题(替换 StringHttpMessageConverter 为 UTF-8
for (int i = 0; i < RT.getMessageConverters().size(); i++) {
if (RT.getMessageConverters().get(i) instanceof StringHttpMessageConverter) {
RT.getMessageConverters().set(i, new StringHttpMessageConverter(StandardCharsets.UTF_8));
break;
}
}
}
public static RestTemplate getRestTemplate() {
@ -216,11 +223,94 @@ public class RestUtil {
if (variables != null && !variables.isEmpty()) {
url += ("?" + asUrlVariables(variables));
}
// 解决[issues/8951]从jeecgboot 3.8.2 升级到 3.8.3 在线表单java增强功能报错------------
// Content-Length 强制设置(解决可能出现的截断问题)
if (StringUtils.isNotEmpty(body)) {
int contentLength = body.getBytes(StandardCharsets.UTF_8).length;
String current = headers.getFirst(HttpHeaders.CONTENT_LENGTH);
if (current == null || !current.equals(String.valueOf(contentLength))) {
headers.setContentLength(contentLength);
log.info(" RestUtil --- request --- 修正/设置 Content-Length = " + contentLength + (current!=null?" (原值="+current+")":""));
}
}
// 发送请求
HttpEntity<String> request = new HttpEntity<>(body, headers);
return RT.exchange(url, method, request, responseType);
}
/**
* 发送请求(支持自定义超时时间)
*
* @param url 请求地址
* @param method 请求方式
* @param headers 请求头 可空
* @param variables 请求url参数 可空
* @param params 请求body参数 可空
* @param responseType 返回类型
* @param timeout 超时时间毫秒如果为0或负数则使用默认超时
* @return ResponseEntity<responseType>
*/
public static <T> ResponseEntity<T> request(String url, HttpMethod method, HttpHeaders headers,
JSONObject variables, Object params, Class<T> responseType, int timeout) {
log.info(" RestUtil --- request --- url = "+ url + ", timeout = " + timeout);
if (StringUtils.isEmpty(url)) {
throw new RuntimeException("url 不能为空");
}
if (method == null) {
throw new RuntimeException("method 不能为空");
}
if (headers == null) {
headers = new HttpHeaders();
}
// 创建自定义RestTemplate如果需要设置超时
RestTemplate restTemplate = RT;
if (timeout > 0) {
// 代码逻辑说明: [issues/8859]online表单java增强失效------------
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setConnectTimeout(timeout);
requestFactory.setReadTimeout(timeout);
restTemplate = new RestTemplate(requestFactory);
// 解决乱码问题(替换 StringHttpMessageConverter 为 UTF-8
for (int i = 0; i < restTemplate.getMessageConverters().size(); i++) {
if (restTemplate.getMessageConverters().get(i) instanceof StringHttpMessageConverter) {
restTemplate.getMessageConverters().set(i, new StringHttpMessageConverter(StandardCharsets.UTF_8));
break;
}
}
}
// 请求体
String body = "";
if (params != null) {
if (params instanceof JSONObject) {
body = ((JSONObject) params).toJSONString();
} else {
body = params.toString();
}
}
// 拼接 url 参数
if (variables != null && !variables.isEmpty()) {
url += ("?" + asUrlVariables(variables));
}
// Content-Length 强制设置(解决可能出现的截断问题)
if (StringUtils.isNotEmpty(body) && !headers.containsKey(HttpHeaders.CONTENT_LENGTH)) {
int contentLength = body.getBytes(StandardCharsets.UTF_8).length;
String current = headers.getFirst(HttpHeaders.CONTENT_LENGTH);
if (current == null || !current.equals(String.valueOf(contentLength))) {
headers.setContentLength(contentLength);
log.info(" RestUtil --- request(timeout) --- 修正/设置 Content-Length = " + contentLength + (current!=null?" (原值="+current+")":""));
}
}
// 发送请求
HttpEntity<String> request = new HttpEntity<>(body, headers);
return restTemplate.exchange(url, method, request, responseType);
}
/**
* 获取JSON请求头
*/

View File

@ -0,0 +1,37 @@
package org.jeecg.common.util;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ThreadContext;
import java.util.concurrent.*;
/**
* @date 2025-09-04
* @author scott
*
* @Description: 支持shiro的API获取当前登录人方法的线程池
*/
public class ShiroThreadPoolExecutor extends ThreadPoolExecutor {
public ShiroThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
@Override
public void execute(Runnable command) {
Subject subject = SecurityUtils.getSubject();
SecurityManager securityManager = SecurityUtils.getSecurityManager();
super.execute(() -> {
try {
ThreadContext.bind(securityManager);
ThreadContext.bind(subject);
command.run();
} finally {
ThreadContext.unbindSubject();
ThreadContext.unbindSecurityManager();
}
});
}
}

View File

@ -8,6 +8,7 @@ import org.jeecg.common.constant.ServiceNameConstants;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
@ -16,6 +17,7 @@ import org.springframework.web.context.request.ServletRequestAttributes;
* @Description: spring上下文工具类
* @author: jeecg-boot
*/
@Lazy(false)
@Component
public class SpringContextUtils implements ApplicationContextAware {

View File

@ -16,7 +16,13 @@ import java.util.regex.Pattern;
* @author zhoujf
*/
@Slf4j
public class SqlInjectionUtil {
public class SqlInjectionUtil {
/**
* sql注入黑名单数据库名
*/
public final static String XSS_STR_TABLE = "peformance_schema|information_schema";
/**
* 默认—sql注入关键词
*/
@ -167,7 +173,28 @@ public class SqlInjectionUtil {
}
return false;
}
/**
* 判断是否存在SQL注入关键词字符串
*
* @param keyword
* @return
*/
@SuppressWarnings("AlibabaUndefineMagicConstant")
private static boolean isExistSqlInjectTableKeyword(String sql, String keyword) {
// 需要匹配的sql注入关键词
String[] matchingTexts = new String[]{"`" + keyword, "(" + keyword, "(`" + keyword};
for (String matchingText : matchingTexts) {
String[] checkTexts = new String[]{" " + matchingText, "from" + matchingText};
for (String checkText : checkTexts) {
if (sql.contains(checkText)) {
return true;
}
}
}
return false;
}
/**
* sql注入过滤处理遇到注入关键字抛异常
*
@ -208,6 +235,14 @@ public class SqlInjectionUtil {
throw new JeecgSqlInjectionException(SqlInjectionUtil.SQL_INJECTION_TIP + value);
}
}
String[] xssTableArr = XSS_STR_TABLE.split("\\|");
for (String xssTableStr : xssTableArr) {
if (isExistSqlInjectTableKeyword(value, xssTableStr)) {
log.error(SqlInjectionUtil.SQL_INJECTION_KEYWORD_TIP, xssTableStr);
log.error(SqlInjectionUtil.SQL_INJECTION_TIP_VARIABLE, value);
throw new JeecgSqlInjectionException(SqlInjectionUtil.SQL_INJECTION_TIP + value);
}
}
// 三、SQL注入检测存在绕过风险 (正则校验)
for (String regularOriginal : XSS_REGULAR_STR_ARRAY) {
@ -244,6 +279,14 @@ public class SqlInjectionUtil {
throw new JeecgSqlInjectionException(SqlInjectionUtil.SQL_INJECTION_TIP + value);
}
}
String[] xssTableArr = XSS_STR_TABLE.split("\\|");
for (String xssTableStr : xssTableArr) {
if (isExistSqlInjectTableKeyword(value, xssTableStr)) {
log.error(SqlInjectionUtil.SQL_INJECTION_KEYWORD_TIP, xssTableStr);
log.error(SqlInjectionUtil.SQL_INJECTION_TIP_VARIABLE, value);
throw new JeecgSqlInjectionException(SqlInjectionUtil.SQL_INJECTION_TIP + value);
}
}
// 三、SQL注入检测存在绕过风险 (正则校验)
for (String regularOriginal : XSS_REGULAR_STR_ARRAY) {
@ -292,13 +335,12 @@ public class SqlInjectionUtil {
return table;
}
//update-begin---author:scott ---date:2024-05-28 for表单设计器列表翻译存在表名带条件,导致翻译出问题----
// 代码逻辑说明: 表单设计器列表翻译存在表名带条件,导致翻译出问题----
int index = table.toLowerCase().indexOf(" where ");
if (index != -1) {
table = table.substring(0, index);
log.info("截掉where之后的新表名" + table);
}
//update-end---author:scott ---date::2024-05-28 for表单设计器列表翻译存在表名带条件导致翻译出问题----
table = table.trim();
/**

View File

@ -0,0 +1,184 @@
package org.jeecg.common.util;
import com.alibaba.fastjson.JSONObject;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.sms.v20210111.SmsClient;
import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest;
import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse;
import com.tencentcloudapi.sms.v20210111.models.SendStatus;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.constant.enums.DySmsEnum;
import org.jeecg.config.JeecgSmsTemplateConfig;
import org.jeecg.config.tencent.JeecgTencent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @Description: 腾讯发送短信
* @author: wangshuai
* @date: 2025/11/4 19:27
*/
public class TencentSms {
private final static Logger logger = LoggerFactory.getLogger(TencentSms.class);
/**
* 发送腾讯短信
*
* @param phone
* @param templateParamJson
* @param tencent
* @param dySmsEnum
* @return
*/
public static boolean sendTencentSms(String phone, JSONObject templateParamJson, JeecgTencent tencent, DySmsEnum dySmsEnum) {
//获取客户端链接
SmsClient client = getSmsClient(tencent);
//构建腾讯云短信发送请求
SendSmsRequest req = buildSendSmsRequest(phone, templateParamJson, dySmsEnum, tencent);
try {
//发送短信
SendSmsResponse resp = client.SendSms(req);
// 处理响应
SendStatus[] statusSet = resp.getSendStatusSet();
if (statusSet != null && statusSet.length > 0) {
SendStatus status = statusSet[0];
if ("Ok".equals(status.getCode())) {
logger.info("短信发送成功,手机号:{}", phone);
return true;
} else {
logger.error("短信发送失败,手机号:{},错误码:{},错误信息:{}",
phone, status.getCode(), status.getMessage());
}
}
} catch (TencentCloudSDKException e) {
logger.error("短信发送失败{}", e.getMessage());
}
return false;
}
/**
* 获取sms客户端
*
* @param tencent 腾讯云配置
* @return SmsClient对象
*/
private static SmsClient getSmsClient(JeecgTencent tencent) {
Credential cred = new Credential(tencent.getSecretId(), tencent.getSecretKey());
// 实例化一个http选项可选的没有特殊需求可以跳过
HttpProfile httpProfile = new HttpProfile();
//指定接入地域域名*/
httpProfile.setEndpoint(tencent.getEndpoint());
//实例化一个客户端配置对象
ClientProfile clientProfile = new ClientProfile();
clientProfile.setHttpProfile(httpProfile);
//实例化要请求产品的client对象第二个参数是地域信息
return new SmsClient(cred, tencent.getRegion(), clientProfile);
}
/**
* 构建腾讯云短信发送请求
*
* @param phone 手机号码
* @param templateParamJson 模板参数JSON对象
* @param dySmsEnum 短信枚举配置
* @param tencent 腾讯云配置
* @return 构建好的SendSmsRequest对象
*/
private static SendSmsRequest buildSendSmsRequest(
String phone,
JSONObject templateParamJson,
DySmsEnum dySmsEnum,
JeecgTencent tencent) {
SendSmsRequest req = new SendSmsRequest();
// 1. 设置短信应用ID
String sdkAppId = tencent.getSdkAppId();
req.setSmsSdkAppId(sdkAppId);
// 2. 设置短信签名
String signName = getSmsSignName(dySmsEnum);
req.setSignName(signName);
// 3. 设置模板ID
String templateId = getSmsTemplateId(dySmsEnum);
req.setTemplateId(templateId);
// 4. 设置模板参数
String[] templateParams = extractTemplateParams(templateParamJson);
req.setTemplateParamSet(templateParams);
// 5. 设置手机号码
String[] phoneNumberSet = { phone };
req.setPhoneNumberSet(phoneNumberSet);
logger.debug("构建短信请求完成 - 应用ID: {}, 签名: {}, 模板ID: {}, 手机号: {}",
sdkAppId, signName, templateId, phone);
return req;
}
/**
* 获取短信签名名称
*
* @param dySmsEnum 腾讯云对象
*/
private static String getSmsSignName(DySmsEnum dySmsEnum) {
JeecgSmsTemplateConfig baseConfig = SpringContextUtils.getBean(JeecgSmsTemplateConfig.class);
String signName = dySmsEnum.getSignName();
if (StringUtils.isNotEmpty(baseConfig.getSignature())) {
logger.debug("yml中读取签名名称: {}", baseConfig.getSignature());
signName = baseConfig.getSignature();
}
return signName;
}
/**
* 获取短信模板ID
*
* @param dySmsEnum 腾讯云对象
*/
private static String getSmsTemplateId(DySmsEnum dySmsEnum) {
JeecgSmsTemplateConfig baseConfig = SpringContextUtils.getBean(JeecgSmsTemplateConfig.class);
String templateCode = dySmsEnum.getTemplateCode();
if (StringUtils.isNotEmpty(baseConfig.getSignature())) {
Map<String, String> smsTemplate = baseConfig.getTemplateCode();
if (smsTemplate.containsKey(templateCode) &&
StringUtils.isNotEmpty(smsTemplate.get(templateCode))) {
templateCode = smsTemplate.get(templateCode);
logger.debug("yml中读取短信模板ID: {}", templateCode);
}
}
return templateCode;
}
/**
* 从JSONObject中提取模板参数按原始顺序
*
* @param templateParamJson 模板参数
*/
private static String[] extractTemplateParams(JSONObject templateParamJson) {
if (templateParamJson == null || templateParamJson.isEmpty()) {
return new String[0];
}
List<String> params = new ArrayList<>();
for (String key : templateParamJson.keySet()) {
Object value = templateParamJson.get(key);
if (value != null) {
params.add(value.toString());
} else {
// 处理null值
params.add("");
}
}
logger.debug("提取模板参数: {}", params);
return params.toArray(new String[0]);
}
}

View File

@ -65,6 +65,10 @@ public class TokenUtils {
if (tenantId == null) {
tenantId = oConvertUtils.getString(request.getHeader(CommonConstant.TENANT_ID));
}
if (oConvertUtils.isNotEmpty(tenantId) && "undefined".equals(tenantId)) {
return null;
}
return tenantId;
}
@ -117,7 +121,9 @@ public class TokenUtils {
}
// 校验token是否超时失效 & 或者账号密码是否错误
if (!jwtTokenRefresh(token, username, user.getPassword(), redisUtil)) {
throw new JeecgBoot401Exception(CommonConstant.TOKEN_IS_INVALID_MSG);
// 用户登录Token过期提示信息
String userLoginTokenErrorMsg = oConvertUtils.getString(redisUtil.get(CommonConstant.PREFIX_USER_TOKEN_ERROR_MSG + token));
throw new JeecgBoot401Exception(oConvertUtils.isEmpty(userLoginTokenErrorMsg)? CommonConstant.TOKEN_IS_INVALID_MSG: userLoginTokenErrorMsg);
}
return true;
}
@ -135,10 +141,15 @@ public class TokenUtils {
if (oConvertUtils.isNotEmpty(cacheToken)) {
// 校验token有效性
if (!JwtUtil.verify(cacheToken, userName, passWord)) {
String newAuthorization = JwtUtil.sign(userName, passWord);
// 设置Toekn缓存有效时间
// 从token中解析客户端类型保持续期时使用相同的客户端类型
String clientType = JwtUtil.getClientType(token);
String newAuthorization = JwtUtil.sign(userName, passWord, clientType);
// 根据客户端类型设置对应的缓存有效时间
long expireTime = CommonConstant.CLIENT_TYPE_APP.equalsIgnoreCase(clientType)
? JwtUtil.APP_EXPIRE_TIME * 2 / 1000
: JwtUtil.EXPIRE_TIME * 2 / 1000;
redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, newAuthorization);
redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME * 2 / 1000);
redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, expireTime);
}
return true;
}

View File

@ -68,6 +68,13 @@ public class DbTypeUtils {
return dbTypeIf(dbType, DbType.ORACLE, DbType.ORACLE_12C, DbType.DM);
}
/**
* 是否是达梦
*/
public static boolean dbTypeIsDm(DbType dbType) {
return dbTypeIf(dbType, DbType.DM);
}
public static boolean dbTypeIsSqlServer(DbType dbType) {
return dbTypeIf(dbType, DbType.SQL_SERVER, DbType.SQL_SERVER2005);
}

View File

@ -54,11 +54,10 @@ public class FreemarkerParseFactory {
//classic_compatible设置解决报空指针错误
SQL_CONFIG.setClassicCompatible(true);
//update-begin-author:taoyan date:2022-8-10 for: freemarker模板注入问题 禁止解析ObjectConstructorExecute和freemarker.template.utility.JythonRuntime。
// 解决freemarker模板注入问题 禁止解析ObjectConstructorExecute和freemarker.template.utility.JythonRuntime。
//https://ackcent.com/in-depth-freemarker-template-injection/
TPL_CONFIG.setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER);
SQL_CONFIG.setNewBuiltinClassResolver(TemplateClassResolver.SAFER_RESOLVER);
//update-end-author:taoyan date:2022-8-10 for: freemarker模板注入问题 禁止解析ObjectConstructorExecute和freemarker.template.utility.JythonRuntime。
}
/**
@ -73,13 +72,12 @@ public class FreemarkerParseFactory {
return false;
}
} catch (Exception e) {
//update-begin--Author:scott Date:20180320 for解决问题 - 错误提示sql文件不存在实际问题是sql freemarker用法错误-----
// 代码逻辑说明: 解决问题 - 错误提示sql文件不存在实际问题是sql freemarker用法错误-----
if (e instanceof ParseException) {
log.error(e.getMessage(), e.fillInStackTrace());
throw new Exception(e);
}
log.debug("----isExistTemplate----" + e.toString());
//update-end--Author:scott Date:20180320 for解决问题 - 错误提示sql文件不存在实际问题是sql freemarker用法错误------
return false;
}
return true;

View File

@ -1,123 +1,107 @@
package org.jeecg.common.util.encryption;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.lang.codec.Base64;
import org.jeecg.common.util.oConvertUtils;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
/**
* @Description: AES 加密
* @author: jeecg-boot
* @date: 2022/3/30 11:48
* AES 工具 (兼容历史 NoPadding + 新 PKCS5Padding)
*/
@Slf4j
public class AesEncryptUtil {
/**
* 使用AES-128-CBC加密模式 key和iv可以相同
*/
private static String KEY = EncryptedString.key;
private static String IV = EncryptedString.iv;
private static final String KEY = EncryptedString.key;
private static final String IV = EncryptedString.iv;
/**
* 加密方法
* @param data 要加密的数据
* @param key 加密key
* @param iv 加密iv
* @return 加密的结果
* @throws Exception
*/
public static String encrypt(String data, String key, String iv) throws Exception {
try {
/* -------- 新版CBC + PKCS5Padding (与前端 CryptoJS Pkcs7 兼容) -------- */
private static String decryptPkcs5(String cipherBase64) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec ks = new SecretKeySpec(KEY.getBytes(StandardCharsets.UTF_8), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(IV.getBytes(StandardCharsets.UTF_8));
cipher.init(Cipher.DECRYPT_MODE, ks, ivSpec);
byte[] plain = cipher.doFinal(Base64.decode(cipherBase64));
return new String(plain, StandardCharsets.UTF_8);
}
//"算法/模式/补码方式"NoPadding PkcsPadding
/* -------- 旧版CBC + NoPadding (手工补 0) -------- */
private static String decryptLegacyNoPadding(String cipherBase64) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec ks = new SecretKeySpec(KEY.getBytes(StandardCharsets.UTF_8), "AES");
IvParameterSpec ivSpec = new IvParameterSpec(IV.getBytes(StandardCharsets.UTF_8));
cipher.init(Cipher.DECRYPT_MODE, ks, ivSpec);
byte[] data = cipher.doFinal(Base64.decode(cipherBase64));
return new String(data, StandardCharsets.UTF_8)
.replace("\u0000",""); // 旧填充 0
}
/* -------- 兼容入口:登录使用 -------- */
public static String resolvePassword(String input){
if(oConvertUtils.isEmpty(input)){
return input;
}
// 1. 先尝试新版
try{
String p = decryptPkcs5(input);
return clean(p);
}catch(Exception ignore){
//log.debug("【AES解密】Password not AES PKCS5 cipher, try legacy.");
}
// 2. 回退旧版
try{
String legacy = decryptLegacyNoPadding(input);
return clean(legacy);
}catch(Exception e){
log.debug("【AES解密】Password not AES cipher, raw used.");
}
// 3. 视为明文
return input;
}
/* -------- 可选:统一清理尾部不可见控制字符 -------- */
private static String clean(String s){
if(s==null) return null;
// 去除结尾控制符/空白(不影响中间合法空格)
return s.replaceAll("[\\p{Cntrl}]+","").trim();
}
/* -------- 若仍需要旧接口,可保留 (不建议再用于新前端) -------- */
@Deprecated
public static String desEncrypt(String data) throws Exception {
return decryptLegacyNoPadding(data);
}
/* 加密(若前端不再使用,可忽略;保留旧实现避免影响历史) */
@Deprecated
public static String encrypt(String data) throws Exception {
try{
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
int blockSize = cipher.getBlockSize();
byte[] dataBytes = data.getBytes();
byte[] dataBytes = data.getBytes(StandardCharsets.UTF_8);
int plaintextLength = dataBytes.length;
if (plaintextLength % blockSize != 0) {
plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
plaintextLength += (blockSize - (plaintextLength % blockSize));
}
byte[] plaintext = new byte[plaintextLength];
System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
SecretKeySpec keyspec = new SecretKeySpec(KEY.getBytes(StandardCharsets.UTF_8), "AES");
IvParameterSpec ivspec = new IvParameterSpec(IV.getBytes(StandardCharsets.UTF_8));
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
byte[] encrypted = cipher.doFinal(plaintext);
return Base64.encodeToString(encrypted);
} catch (Exception e) {
e.printStackTrace();
return null;
}catch(Exception e){
throw new IllegalStateException("legacy encrypt error", e);
}
}
/**
* 解密方法
* @param data 要解密的数据
* @param key 解密key
* @param iv 解密iv
* @return 解密的结果
* @throws Exception
*/
public static String desEncrypt(String data, String key, String iv) throws Exception {
//update-begin-author:taoyan date:2022-5-23 for:VUEN-1084 【vue3】online表单测试发现的新问题 6、解密报错 ---解码失败应该把异常抛出去,在外面处理
byte[] encrypted1 = Base64.decode(data);
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original);
//加密解码后的字符串会出现\u0000
return originalString.replaceAll("\\u0000", "");
//update-end-author:taoyan date:2022-5-23 for:VUEN-1084 【vue3】online表单测试发现的新问题 6、解密报错 ---解码失败应该把异常抛出去,在外面处理
}
/**
* 使用默认的key和iv加密
* @param data
* @return
* @throws Exception
*/
public static String encrypt(String data) throws Exception {
return encrypt(data, KEY, IV);
}
/**
* 使用默认的key和iv解密
* @param data
* @return
* @throws Exception
*/
public static String desEncrypt(String data) throws Exception {
return desEncrypt(data, KEY, IV);
}
// /**
// * 测试
// */
// public static void main(String args[]) throws Exception {
// String test1 = "sa";
// String test =new String(test1.getBytes(),"UTF-8");
// String data = null;
// String key = KEY;
// String iv = IV;
// // /g2wzfqvMOeazgtsUVbq1kmJawROa6mcRAzwG1/GeJ4=
// data = encrypt(test, key, iv);
// System.out.println("数据:"+test);
// System.out.println("加密:"+data);
// String jiemi =desEncrypt(data, key, iv).trim();
// System.out.println("解密:"+jiemi);
// public static void main(String[] args) throws Exception {
// // 前端 CBC/Pkcs7 密文测试
// String frontCipher = encrypt("sa"); // 仅验证管道是否可用(旧方式)
// System.out.println(resolvePassword(frontCipher));
// }
}
}

View File

@ -2,6 +2,7 @@ package org.jeecg.common.util.filter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.exception.JeecgBootException;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
@ -34,6 +35,7 @@ public class SsrfFileTypeFilter {
FILE_TYPE_WHITE_LIST.add("bmp");
FILE_TYPE_WHITE_LIST.add("svg");
FILE_TYPE_WHITE_LIST.add("ico");
FILE_TYPE_WHITE_LIST.add("heic");
//文本文件
FILE_TYPE_WHITE_LIST.add("txt");
@ -148,29 +150,38 @@ public class SsrfFileTypeFilter {
public static void checkDownloadFileType(String filePath) throws IOException {
//文件后缀
String suffix = getFileTypeBySuffix(filePath);
log.info("suffix:{}", suffix);
log.debug(" 【文件下载校验】文件后缀 suffix: {}", suffix);
boolean isAllowExtension = FILE_TYPE_WHITE_LIST.contains(suffix.toLowerCase());
//是否允许下载的文件
if (!isAllowExtension) {
throw new IOException("下载失败,存在非法文件类型:" + suffix);
throw new JeecgBootException("下载失败,存在非法文件类型:" + suffix);
}
}
/**
* 上传文件类型过滤
*
* @param file
*/
public static void checkUploadFileType(MultipartFile file) throws Exception {
//获取文件真是后缀
String suffix = getFileType(file);
log.info("suffix:{}", suffix);
checkUploadFileType(file, null);
}
/**
* 上传文件类型过滤
*
* @param file
*/
public static void checkUploadFileType(MultipartFile file, String customPath) throws Exception {
//1. 路径安全校验
validatePathSecurity(customPath);
//2. 校验文件后缀和头
String suffix = getFileType(file, customPath);
log.info("【文件上传校验】文件后缀 suffix: {}customPath{}", suffix, customPath);
boolean isAllowExtension = FILE_TYPE_WHITE_LIST.contains(suffix.toLowerCase());
//是否允许下载的文件
if (!isAllowExtension) {
throw new Exception("上传失败,存在非法文件类型:" + suffix);
throw new JeecgBootException("上传失败,存在非法文件类型:" + suffix);
}
}
@ -182,8 +193,8 @@ public class SsrfFileTypeFilter {
* @throws Exception
*/
private static String getFileType(MultipartFile file) throws Exception {
//update-begin-author:liusq date:20230404 for: [issue/4672]方法造成的文件被占用注释掉此方法tomcat就能自动清理掉临时文件
private static String getFileType(MultipartFile file, String customPath) throws Exception {
// 代码逻辑说明: [issue/4672]方法造成的文件被占用注释掉此方法tomcat就能自动清理掉临时文件
String fileExtendName = null;
InputStream is = null;
try {
@ -202,7 +213,7 @@ public class SsrfFileTypeFilter {
break;
}
}
log.info("-----获取到的指定文件类型------"+fileExtendName);
log.debug("-----获取到的指定文件类型------"+fileExtendName);
// 如果不是上述类型,则判断扩展名
if (StringUtils.isBlank(fileExtendName)) {
String fileName = file.getOriginalFilename();
@ -213,7 +224,6 @@ public class SsrfFileTypeFilter {
// 如果有扩展名,则返回扩展名
return getFileTypeBySuffix(fileName);
}
log.info("-----最終的文件类型------"+fileExtendName);
is.close();
return fileExtendName;
} catch (Exception e) {
@ -224,7 +234,6 @@ public class SsrfFileTypeFilter {
is.close();
}
}
//update-end-author:liusq date:20230404 for: [issue/4672]方法造成的文件被占用注释掉此方法tomcat就能自动清理掉临时文件
}
/**
@ -248,4 +257,34 @@ public class SsrfFileTypeFilter {
}
return stringBuilder.toString();
}
/**
* 路径安全校验
*/
private static void validatePathSecurity(String customPath) throws JeecgBootException {
if (customPath == null || customPath.trim().isEmpty()) {
return;
}
// 统一分隔符为 /
String normalized = customPath.replace("\\", "/");
// 1. 防止路径遍历攻击
if (normalized.contains("..") || normalized.contains("~")) {
throw new JeecgBootException("上传业务路径包含非法字符!");
}
// 2. 限制路径深度
int depth = normalized.split("/").length;
if (depth > 5) {
throw new JeecgBootException("上传业务路径深度超出限制!");
}
// 3. 限制字符集(只允许字母、数字、下划线、横线、斜杠)
if (!normalized.matches("^[a-zA-Z0-9/_-]+$")) {
throw new JeecgBootException("上传业务路径包含非法字符!");
}
}
}

View File

@ -7,9 +7,14 @@ import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
import org.springframework.beans.BeanUtils;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
@ -22,6 +27,7 @@ import java.sql.Date;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
/**
*
@ -473,6 +479,23 @@ public class oConvertUtils {
return true;
}
/**
* 判断字符串是否为JSON格式
* @param str
* @return
*/
public static boolean isJson(String str) {
if (str == null || str.trim().isEmpty()) {
return false;
}
try {
com.alibaba.fastjson.JSON.parse(str);
return true;
} catch (Exception e) {
return false;
}
}
/**
* 获取Map对象
*/
@ -545,10 +568,8 @@ public class oConvertUtils {
return "";
} else if (!name.contains(SymbolConstant.UNDERLINE)) {
// 不含下划线,仅将首字母小写
//update-begin--Author:zhoujf Date:20180503 forTASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
//update-begin--Author:zhoujf Date:20180503 forTASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
// 代码逻辑说明: TASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
return name.substring(0, 1).toLowerCase() + name.substring(1).toLowerCase();
//update-end--Author:zhoujf Date:20180503 forTASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
}
// 用下划线将原始字符串分割
String[] camels = name.split("_");
@ -593,7 +614,6 @@ public class oConvertUtils {
return result.substring(0, result.length() - 1);
}
//update-begin--Author:zhoujf Date:20180503 forTASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
/**
* 将下划线大写方式命名的字符串转换为驼峰式。(首字母写)
* 如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。</br>
@ -626,7 +646,6 @@ public class oConvertUtils {
}
return result.toString();
}
//update-end--Author:zhoujf Date:20180503 forTASK #2500 【代码生成器】代码生成器开发一通用模板生成功能
/**
* 将驼峰命名转化成下划线
@ -964,17 +983,18 @@ public class oConvertUtils {
/**
* 判断 list1中的元素是否在list2中出现
* 判断 sourceList中的元素是否在targetList中出现
*
* QQYUN-5326【简流】获取组织人员 单/多 筛选条件 没有部门筛选
* @param list1
* @param list2
* @return
* @param sourceList 源列表,要检查的元素列表
* @param targetList 目标列表,用于匹配的列表
* @return 如果sourceList中有任何元素在targetList中存在则返回true否则返回false
*/
public static boolean isInList(List<String> list1, List<String> list2){
for(String str1: list1){
public static boolean isInList(List<String> sourceList, List<String> targetList){
for(String sourceItem: sourceList){
boolean flag = false;
for(String str2: list2){
if(str1.equals(str2)){
for(String targetItem: targetList){
if(sourceItem.equals(targetItem)){
flag = true;
break;
}
@ -986,6 +1006,35 @@ public class oConvertUtils {
return false;
}
/**
* 判断 sourceList中的所有元素是否都在targetList中存在
* @param sourceList 源列表,要检查的元素列表
* @param targetList 目标列表,用于匹配的列表
* @return 如果sourceList中的所有元素都在targetList中存在则返回true否则返回false
*/
public static boolean isAllInList(List<String> sourceList, List<String> targetList){
if(sourceList == null || sourceList.isEmpty()){
return true; // 空列表视为所有元素都存在
}
if(targetList == null || targetList.isEmpty()){
return false; // 目标列表为空源列表非空时返回false
}
for(String sourceItem: sourceList){
boolean found = false;
for(String targetItem: targetList){
if(sourceItem.equals(targetItem)){
found = true;
break;
}
}
if(!found){
return false; // 有任何一个元素不在目标列表中返回false
}
}
return true; // 所有元素都找到了
}
/**
* 计算文件大小转成MB
* @param uploadCount
@ -1131,7 +1180,56 @@ public class oConvertUtils {
* @date 2020/9/12 15:50
*/
public static <T> boolean isIn(T obj, T... objs) {
return isIn(obj, objs);
if (isEmpty(objs)) {
return false;
}
for (T obj1 : objs) {
if (isEqual(obj, obj1)) {
return true;
}
}
return false;
}
/**
* 判断租户ID是否有效
* @param tenantId
* @return
*/
public static boolean isEffectiveTenant(String tenantId) {
return MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL && isNotEmpty(tenantId) && !("0").equals(tenantId);
}
/**
* 复制源对象的非空属性到目标对象(同名属性)
*
* @param source 源对象(页面)
* @param target 目标对象(数据库实体)
*/
public static void copyNonNullFields(Object source, Object target) {
if (source == null || target == null) {
return;
}
// 获取源对象的非空属性名数组
String[] nullPropertyNames = getNullPropertyNames(source);
// 复制:忽略源对象的空属性,仅覆盖目标对象的对应非空属性
BeanUtils.copyProperties(source, target, nullPropertyNames);
}
/**
* 获取源对象中值为 null 的属性名数组
*
* @param source
*/
private static String[] getNullPropertyNames(Object source) {
BeanWrapper beanWrapper = new BeanWrapperImpl(source);
//获取类的属性
PropertyDescriptor[] propertyDescriptors = beanWrapper.getPropertyDescriptors();
// 过滤出值为 null 的属性名
return Stream.of(propertyDescriptors)
.map(PropertyDescriptor::getName)
.filter(name -> beanWrapper.getPropertyValue(name) == null)
.toArray(String[]::new);
}
}

View File

@ -97,9 +97,8 @@ public class OssBootUtil {
* @return oss 中的相对文件路径
*/
public static String upload(MultipartFile file, String fileDir,String customBucket) throws Exception {
//update-begin-author:liusq date:20210809 for: 过滤上传文件类型
// 文件安全校验,防止上传漏洞文件
SsrfFileTypeFilter.checkUploadFileType(file);
//update-end-author:liusq date:20210809 for: 过滤上传文件类型
String filePath = null;
initOss(endPoint, accessKeyId, accessKeySecret);
@ -125,9 +124,8 @@ public class OssBootUtil {
if (!fileDir.endsWith(SymbolConstant.SINGLE_SLASH)) {
fileDir = fileDir.concat(SymbolConstant.SINGLE_SLASH);
}
//update-begin-author:wangshuai date:20201012 for: 过滤上传文件夹名特殊字符,防止攻击
// 代码逻辑说明: 过滤上传文件夹名特殊字符,防止攻击
fileDir=StrAttackFilter.filter(fileDir);
//update-end-author:wangshuai date:20201012 for: 过滤上传文件夹名特殊字符,防止攻击
fileUrl = fileUrl.append(fileDir + fileName);
if (oConvertUtils.isNotEmpty(staticDomain) && staticDomain.toLowerCase().startsWith(CommonConstant.STR_HTTP)) {
@ -264,9 +262,8 @@ public class OssBootUtil {
newBucket = bucket;
}
initOss(endPoint, accessKeyId, accessKeySecret);
//update-begin---author:liusq Date:20220120 for替换objectName前缀防止key不一致导致获取不到文件----
// 代码逻辑说明: 替换objectName前缀防止key不一致导致获取不到文件----
objectName = OssBootUtil.replacePrefix(objectName,bucket);
//update-end---author:liusq Date:20220120 for替换objectName前缀防止key不一致导致获取不到文件----
OSSObject ossObject = ossClient.getObject(newBucket,objectName);
inputStream = new BufferedInputStream(ossObject.getObjectContent());
}catch (Exception e){
@ -294,9 +291,8 @@ public class OssBootUtil {
public static String getObjectUrl(String bucketName, String objectName, Date expires) {
initOss(endPoint, accessKeyId, accessKeySecret);
try{
//update-begin---author:liusq Date:20220120 for替换objectName前缀防止key不一致导致获取不到文件----
// 代码逻辑说明: 替换objectName前缀防止key不一致导致获取不到文件----
objectName = OssBootUtil.replacePrefix(objectName,bucketName);
//update-end---author:liusq Date:20220120 for替换objectName前缀防止key不一致导致获取不到文件----
if(ossClient.doesObjectExist(bucketName,objectName)){
URL url = ossClient.generatePresignedUrl(bucketName,objectName,expires);
//log.info("原始url : {}", url.toString());

View File

@ -3,6 +3,8 @@ package org.jeecg.common.util.security;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.jeecg.common.exception.JeecgSqlInjectionException;
import org.jeecg.common.util.SqlInjectionUtil;
import org.jeecg.common.util.oConvertUtils;
import java.util.*;
import java.util.regex.Matcher;
@ -61,11 +63,13 @@ public abstract class AbstractQueryBlackListHandler {
if(list==null){
return true;
}
log.info(" 获取sql信息 {} ", list.toString());
log.debug(" 获取sql信息 {} ", list.toString());
boolean flag = checkTableAndFieldsName(list);
if(flag == false){
return false;
}
Set<String> xssTableSet = new HashSet<>(Arrays.asList(SqlInjectionUtil.XSS_STR_TABLE.split("\\|")));
for (QueryTable table : list) {
String name = table.getName();
String fieldRule = ruleMap.get(name);
@ -81,6 +85,16 @@ public abstract class AbstractQueryBlackListHandler {
}
}
// 判断是否调用了黑名单数据库
String dbName = table.getDbName();
if (oConvertUtils.isNotEmpty(dbName)) {
dbName = dbName.toLowerCase().trim();
if (xssTableSet.contains(dbName)) {
flag = false;
log.warn("sql黑名单校验数据库【" + dbName + "】禁止查询");
break;
}
}
}
// 返回黑名单校验结果(不合法直接抛出异常)
@ -135,6 +149,8 @@ public abstract class AbstractQueryBlackListHandler {
* 查询的表的信息
*/
protected class QueryTable {
//数据库名
private String dbName;
//表名
private String name;
//表的别名
@ -158,6 +174,14 @@ public abstract class AbstractQueryBlackListHandler {
this.fields.add(field);
}
public String getDbName() {
return dbName;
}
public void setDbName(String dbName) {
this.dbName = dbName;
}
public String getName() {
return name;
}

View File

@ -3,13 +3,14 @@ package org.jeecg.config;
import org.jeecgframework.core.util.ApplicationContextUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
/**
* @Author: Scott
* @Date: 2018/2/7
* @description: autopoi 配置类
*/
@Lazy(false)
@Configuration
public class AutoPoiConfig {

View File

@ -25,6 +25,7 @@ import lombok.extern.slf4j.Slf4j;
* @Version:1.0
*/
@Slf4j
@Lazy(false)
@Service
public class AutoPoiDictConfig implements AutoPoiDictServiceI {
final static String EXCEL_SPLIT_TAG = "_";
@ -59,17 +60,15 @@ public class AutoPoiDictConfig implements AutoPoiDictServiceI {
for (DictModel t : dictList) {
//update-begin---author:liusq Date:20230517 for[issues/4917]excel 导出异常---
// 代码逻辑说明: [issues/4917]excel 导出异常---
if(t!=null && t.getText()!=null && t.getValue()!=null){
//update-end---author:liusq Date:20230517 for[issues/4917]excel 导出异常---
//update-begin---author:scott Date:20211220 for[issues/I4MBB3]@Excel dicText字段的值有下划线时导入功能不能正确解析---
// 代码逻辑说明: [issues/I4MBB3]@Excel dicText字段的值有下划线时导入功能不能正确解析---
if(t.getValue().contains(EXCEL_SPLIT_TAG)){
String val = t.getValue().replace(EXCEL_SPLIT_TAG,TEMP_EXCEL_SPLIT_TAG);
dictReplaces.add(t.getText() + EXCEL_SPLIT_TAG + val);
}else{
dictReplaces.add(t.getText() + EXCEL_SPLIT_TAG + t.getValue());
}
//update-end---author:20211220 Date:20211220 for[issues/I4MBB3]@Excel dicText字段的值有下划线时导入功能不能正确解析---
}
}
if (dictReplaces != null && dictReplaces.size() != 0) {

View File

@ -1,9 +1,11 @@
package org.jeecg.config;
import lombok.Getter;
import lombok.Setter;
import org.jeecg.config.tencent.JeecgTencent;
import org.jeecg.config.vo.*;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Role;
import org.springframework.stereotype.Component;
@ -76,6 +78,34 @@ public class JeecgBaseConfig {
*/
private BaiduApi baiduApi;
/**
* minio配置
*/
@Getter
@Setter
private JeecgMinio minio;
/**
* oss配置
*/
@Getter
@Setter
private JeecgOSS oss;
/**
* 短信发送方式 aliyun阿里云短信 tencent腾讯云短信
*/
@Getter
@Setter
private String smsSendType = "aliyun";
/**
* 腾讯配置
*/
@Getter
@Setter
private JeecgTencent tencent;
public String getCustomResourcePrefixPath() {
return customResourcePrefixPath;
}

View File

@ -0,0 +1,29 @@
package org.jeecg.config;
import org.jeecg.config.vo.GaoDeApi;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
/**
* 高德账号配置
*/
@Lazy(false)
@Configuration("jeecgGaodeBaseConfig")
@ConfigurationProperties(prefix = "jeecg.jmreport")
public class JeecgGaodeBaseConfig {
/**
* 高德开放API配置
*/
private GaoDeApi gaoDeApi;
public GaoDeApi getGaoDeApi() {
return gaoDeApi;
}
public void setGaoDeApi(GaoDeApi gaoDeApi) {
this.gaoDeApi = gaoDeApi;
}
}

View File

@ -2,12 +2,14 @@ package org.jeecg.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
/**
* 设置静态参数初始化
* @author: jeecg-boot
*/
@Lazy(false)
@Component
@Data
public class StaticConfig {

View File

@ -1,8 +1,9 @@
//package org.jeecg.config;
//
//
//import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
//import io.swagger.v3.oas.annotations.Operation;
//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;
@ -18,15 +19,13 @@
//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 springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
//
//import java.lang.reflect.Field;
//import java.util.ArrayList;
@ -38,8 +37,7 @@
// * @Author scott
// */
//@Configuration
//@EnableSwagger2 //开启 Swagger2
//@EnableKnife4j //开启 knife4j可以不写
//@EnableSwagger2WebMvc
//@Import(BeanValidatorPluginsConfiguration.class)
//public class Swagger2Config implements WebMvcConfigurer {
//
@ -97,6 +95,12 @@
// 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());
// 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());
// }
//
// return pars;
// }
//
@ -151,7 +155,7 @@
//
// @Override
// public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
// if (bean instanceof WebMvcRequestHandlerProvider) {
// customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
// }
// return bean;

View File

@ -10,28 +10,37 @@ 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.customizers.OperationCustomizer;
import org.springdoc.core.filters.GlobalOpenApiMethodFilter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.method.HandlerMethod;
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.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author eightmonth
*/
@Slf4j
@Configuration
@ConditionalOnProperty(prefix = "knife4j", name = "production", havingValue = "false", matchIfMissing = true)
@PropertySource("classpath:config/default-spring-doc.properties")
public class Swagger3Config implements WebMvcConfigurer {
// 路径匹配结果缓存,避免重复计算
private static final Map<String, Boolean> EXCLUDED_PATHS_CACHE = new ConcurrentHashMap<>();
// 定义不需要注入安全要求的路径集合
Set<String> excludedPaths = new HashSet<>(Arrays.asList(
"/sys/randomImage/{key}",
private static final Set<String> excludedPaths = new HashSet<>(Arrays.asList(
"/sys/randomImage/**",
"/sys/login",
"/sys/phoneLogin",
"/sys/mLogin",
@ -41,7 +50,20 @@ public class Swagger3Config implements WebMvcConfigurer {
"/sys/thirdLogin/**",
"/sys/user/register"
));
// 预处理通配符模式,提高匹配效率
private static final Set<String> wildcardPatterns = new HashSet<>();
private static final Set<String> exactPatterns = new HashSet<>();
static {
// 初始化时分离精确匹配和通配符匹配
for (String pattern : excludedPaths) {
if (pattern.endsWith("/**")) {
wildcardPatterns.add(pattern.substring(0, pattern.length() - 3));
} else {
exactPatterns.add(pattern);
}
}
}
/**
*
* 显示swagger-ui.html文档展示页还必须注入swagger资源
@ -61,42 +83,70 @@ public class Swagger3Config implements WebMvcConfigurer {
}
@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))
);
}
});
public OperationCustomizer operationCustomizer() {
return (operation, handlerMethod) -> {
String path = getFullPath(handlerMethod);
if (!isExcludedPath(path)) {
operation.addSecurityItem(new SecurityRequirement().addList(CommonConstant.X_ACCESS_TOKEN));
}else{
log.info("忽略加入 X_ACCESS_TOKEN 的 PATH:" + path);
}
return operation;
};
}
private String getFullPath(HandlerMethod handlerMethod) {
StringBuilder fullPath = new StringBuilder();
// 获取类级别的路径
RequestMapping classMapping = handlerMethod.getBeanType().getAnnotation(RequestMapping.class);
if (classMapping != null && classMapping.value().length > 0) {
fullPath.append(classMapping.value()[0]);
}
// 获取方法级别的路径
RequestMapping methodMapping = handlerMethod.getMethodAnnotation(RequestMapping.class);
if (methodMapping != null && methodMapping.value().length > 0) {
String methodPath = methodMapping.value()[0];
// 确保路径正确拼接,处理斜杠
if (!fullPath.toString().endsWith("/") && !methodPath.startsWith("/")) {
fullPath.append("/");
}
fullPath.append(methodPath);
}
return fullPath.toString();
}
private boolean isExcludedPath(String path) {
// 使用缓存避免重复计算
return EXCLUDED_PATHS_CACHE.computeIfAbsent(path, p -> {
// 精确匹配
if (exactPatterns.contains(p)) {
return true;
}
// 通配符匹配
return wildcardPatterns.stream().anyMatch(p::startsWith);
});
}
@Bean
public OpenAPI customOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("JeecgBoot 后台服务API接口文档")
.version("3.8.0")
.version("3.9.0")
.contact(new Contact().name("北京国炬信息技术有限公司").url("www.jeccg.com").email("jeecgos@163.com"))
.description( "后台API接口")
.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)));
new SecurityScheme()
.name(CommonConstant.X_ACCESS_TOKEN)
.type(SecurityScheme.Type.APIKEY)
.in(SecurityScheme.In.HEADER) // 关键:指定为 header
));
}
}

View File

@ -1,19 +0,0 @@
package org.jeecg.config;
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.stereotype.Component;
@Component
public class UndertowCustomizer implements WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
@Override
public void customize(UndertowServletWebServerFactory factory) {
factory.addDeploymentInfoCustomizers(deploymentInfo -> {
WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();
webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(false, 1024));
deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo);
});
}
}

View File

@ -11,22 +11,19 @@ import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import io.micrometer.prometheusmetrics.PrometheusMeterRegistry;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectProvider;
import lombok.extern.slf4j.Slf4j;
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.web.exchanges.InMemoryHttpExchangeRepository;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.jackson.JacksonProperties;
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.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
@ -50,6 +47,7 @@ import java.util.concurrent.TimeUnit;
* @Author qinfeng
*
*/
@Slf4j
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@ -61,6 +59,14 @@ public class WebMvcConfiguration implements WebMvcConfigurer {
@Autowired(required = false)
private PrometheusMeterRegistry prometheusMeterRegistry;
/**
* meterRegistryPostProcessor
* for [QQYUN-12558]【监控】系统监控的头两个tab不好使接口404
*/
@Autowired(required = false)
@Qualifier("meterRegistryPostProcessor")
private BeanPostProcessor meterRegistryPostProcessor;
/**
* 静态资源的配置 - 使得可以从磁盘中读取 Html、图片、视频、音频等
*/
@ -83,7 +89,7 @@ public class WebMvcConfiguration implements WebMvcConfigurer {
*/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("doc.html");
registry.addViewController("/").setViewName("redirect:/doc.html");
}
@Bean
@ -136,7 +142,6 @@ public class WebMvcConfiguration implements WebMvcConfigurer {
return objectMapper;
}
//update-begin---author:chenrui ---date:20240514 for[QQYUN-9247]系统监控功能优化------------
// /**
// * SpringBootAdmin的Httptrace不见了
// * https://blog.csdn.net/u013810234/article/details/110097201
@ -145,16 +150,21 @@ public class WebMvcConfiguration implements WebMvcConfigurer {
// public InMemoryHttpTraceRepository getInMemoryHttpTrace(){
// return new InMemoryHttpTraceRepository();
// }
//update-end---author:chenrui ---date:20240514 for[QQYUN-9247]系统监控功能优化------------
/**
* 解决metrics端点不显示jvm信息的问题(zyf)
* 在Bean初始化完成后立即配置PrometheusMeterRegistry避免在Meter注册后才配置MeterFilter
* for [QQYUN-12558]【监控】系统监控的头两个tab不好使接口404
* @author chenrui
* @date 2025/5/26 16:46
*/
@Bean
@ConditionalOnBean(name = "meterRegistryPostProcessor")
InitializingBean forcePrometheusPostProcessor(BeanPostProcessor meterRegistryPostProcessor) {
return () -> meterRegistryPostProcessor.postProcessAfterInitialization(prometheusMeterRegistry, "");
@PostConstruct
public void initPrometheusMeterRegistry() {
// 确保在应用启动早期就配置MeterFilter避免警告
if (null != meterRegistryPostProcessor && null != prometheusMeterRegistry) {
meterRegistryPostProcessor.postProcessAfterInitialization(prometheusMeterRegistry, "prometheusMeterRegistry");
log.info("PrometheusMeterRegistry 配置完成");
}
}
// /**

View File

@ -8,12 +8,14 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class LowCodeModeConfiguration implements WebMvcConfigurer {
public LowCodeModeInterceptor payInterceptor() {
return new LowCodeModeInterceptor();
private final LowCodeModeInterceptor lowCodeModeInterceptor;
public LowCodeModeConfiguration(LowCodeModeInterceptor lowCodeModeInterceptor) {
this.lowCodeModeInterceptor = lowCodeModeInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(payInterceptor()).addPathPatterns(LowCodeUrlsEnum.getLowCodeInterceptUrls());
registry.addInterceptor(lowCodeModeInterceptor).addPathPatterns(LowCodeUrlsEnum.getLowCodeInterceptUrls());
}
}

View File

@ -11,7 +11,7 @@ import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.CommonUtils;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.config.JeecgBaseConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import jakarta.annotation.Resource;
@ -38,6 +38,7 @@ import java.util.Set;
* @date 20230904
*/
@Slf4j
@Component
public class LowCodeModeInterceptor implements HandlerInterceptor {
/**
* 低代码开发模式
@ -47,18 +48,22 @@ public class LowCodeModeInterceptor implements HandlerInterceptor {
@Resource
private JeecgBaseConfig jeecgBaseConfig;
@Autowired
private CommonAPI commonAPI;
/**
* 在请求处理之前进行调用
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
CommonAPI commonAPI = null;
log.info("低代码模式,拦截请求路径:" + request.getRequestURI());
//1、验证是否开启低代码开发模式控制
if (jeecgBaseConfig == null) {
jeecgBaseConfig = SpringContextUtils.getBean(JeecgBaseConfig.class);
}
if (commonAPI == null) {
commonAPI = SpringContextUtils.getBean(CommonAPI.class);
}
if (jeecgBaseConfig.getFirewall()!=null && LowCodeModeInterceptor.LOW_CODE_MODE_PROD.equals(jeecgBaseConfig.getFirewall().getLowCodeMode())) {
String requestURI = request.getRequestURI().substring(request.getContextPath().length());

View File

@ -129,20 +129,18 @@ public class MybatisInterceptor implements Interceptor {
Field[] fields = null;
if (parameter instanceof ParamMap) {
ParamMap<?> p = (ParamMap<?>) parameter;
//update-begin-author:scott date:20190729 for:批量更新报错issues/IZA3Q--
// 代码逻辑说明: 批量更新报错issues/IZA3Q--
String et = "et";
if (p.containsKey(et)) {
parameter = p.get(et);
} else {
parameter = p.get("param1");
}
//update-end-author:scott date:20190729 for:批量更新报错issues/IZA3Q-
//update-begin-author:scott date:20190729 for:更新指定字段时报错 issues/#516-
// 代码逻辑说明: 更新指定字段时报错 issues/#516-
if (parameter == null) {
return invocation.proceed();
}
//update-end-author:scott date:20190729 for:更新指定字段时报错 issues/#516-
fields = oConvertUtils.getAllFields(parameter);
} else {
@ -184,7 +182,6 @@ public class MybatisInterceptor implements Interceptor {
// TODO Auto-generated method stub
}
//update-begin--Author:scott Date:20191213 for关于使用Quzrtz 开启线程任务, #465
/**
* 获取登录用户
* @return
@ -199,6 +196,5 @@ public class MybatisInterceptor implements Interceptor {
}
return sysUser;
}
//update-end--Author:scott Date:20191213 for关于使用Quzrtz 开启线程任务, #465
}

View File

@ -1,11 +1,16 @@
package org.jeecg.config.mybatis;
import java.util.ArrayList;
import java.util.List;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.log.Log;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import org.jeecg.common.config.TenantContext;
@ -15,24 +20,27 @@ import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.TokenUtils;
import org.jeecg.common.util.oConvertUtils;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.handler.TenantLineHandler;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
/**
* 单数据源配置jeecg.datasource.open = false时生效
* @Author zhoujf
*
*/
@Slf4j
@Configuration
@MapperScan(value={"org.jeecg.**.mapper*"})
public class MybatisPlusSaasConfig {
@Autowired
private DataSource dataSource;
/**
* 是否开启系统模块的租户隔离
* 控制范围:用户、角色、部门、我的部门、字典、分类字典、多数据源、职务、通知公告
@ -122,7 +130,23 @@ public class MybatisPlusSaasConfig {
//update-begin-author:zyf date:20220425 for:【VUEN-606】注入动态表名适配拦截器解决多表名问题
interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor());
//update-end-author:zyf date:20220425 for:【VUEN-606】注入动态表名适配拦截器解决多表名问题
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
//update-begin---author:scott ---date:2025-08-02 for【issues/8666】升级mybatisPlus后SqlServer分页使用OFFSET ROWS FETCH NEXT ROWS ONLY导致online报表报错---
DbType dbType = null;
try {
dbType = JdbcUtils.getDbType(dataSource.getConnection().getMetaData().getURL());
log.info("当前数据库类型: {}", dbType);
} catch (SQLException e) {
Log.error(e.getMessage(), e);
}
if (dbType!=null && (dbType == DbType.SQL_SERVER || dbType == DbType.SQL_SERVER2005)) {
// 如果是SQL Server则覆盖为2005分页方式
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.SQL_SERVER2005));
} else {
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
}
//update-end---author:scott ---date::2025-08-02 for【issues/8666】升级mybatisPlus后SqlServer分页使用OFFSET ROWS FETCH NEXT ROWS ONLY导致online报表报错---
//【jeecg-boot/issues/3847】增加@Version乐观锁支持
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;

View File

@ -9,11 +9,13 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
/**
* Minio文件上传配置文件
* @author: jeecg-boot
*/
@Lazy(false)
@Slf4j
@Configuration
@ConditionalOnProperty(prefix = "jeecg.minio", name = "minio_url")

View File

@ -6,11 +6,13 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
/**
* 云存储 配置
* @author: jeecg-boot
*/
@Lazy(false)
@Configuration
@ConditionalOnProperty(prefix = "jeecg.oss", name = "endpoint")
public class OssConfiguration {

View File

@ -21,20 +21,23 @@ 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.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
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 org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import java.lang.reflect.Method;
import java.util.*;
/**
@ -45,7 +48,6 @@ import java.util.*;
@Slf4j
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ShiroConfig {
@Resource
@ -109,9 +111,9 @@ public class ShiroConfig {
filterChainDefinitionMap.put("/sys/getLoginQrcode/**", "anon"); //登录二维码
filterChainDefinitionMap.put("/sys/getQrcodeToken/**", "anon"); //监听扫码
filterChainDefinitionMap.put("/sys/checkAuth", "anon"); //授权接口排除
filterChainDefinitionMap.put("/openapi/call/**", "anon"); // 开放平台接口排除
//update-begin--Author:scott Date:20221116 for排除静态资源后缀
// 代码逻辑说明: 排除静态资源后缀
filterChainDefinitionMap.put("/", "anon");
filterChainDefinitionMap.put("/doc.html", "anon");
filterChainDefinitionMap.put("/**/*.js", "anon");
@ -126,9 +128,9 @@ 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排除静态资源后缀
filterChainDefinitionMap.put("/druid/**", "anon");
filterChainDefinitionMap.put("/swagger-ui.html", "anon");
@ -136,9 +138,7 @@ public class ShiroConfig {
filterChainDefinitionMap.put("/webjars/**", "anon");
filterChainDefinitionMap.put("/v3/**", "anon");
// update-begin--Author:sunjianlei Date:20210510 for排除消息通告查看详情页面用于第三方APP
filterChainDefinitionMap.put("/sys/annountCement/show/**", "anon");
// update-end--Author:sunjianlei Date:20210510 for排除消息通告查看详情页面用于第三方APP
//积木报表排除
filterChainDefinitionMap.put("/jmreport/**", "anon");
@ -153,7 +153,11 @@ public class ShiroConfig {
filterChainDefinitionMap.put("/drag/share/view/**", "anon");
filterChainDefinitionMap.put("/drag/onlDragDatasetHead/getAllChartData", "anon");
filterChainDefinitionMap.put("/drag/onlDragDatasetHead/getTotalData", "anon");
filterChainDefinitionMap.put("/drag/onlDragDatasetHead/getMapDataByCode", "anon");
filterChainDefinitionMap.put("/drag/onlDragDatasetHead/getTotalDataByCompId", "anon");
filterChainDefinitionMap.put("/drag/mock/json/**", "anon");
filterChainDefinitionMap.put("/drag/onlDragDatasetHead/getDictByCodes", "anon");
filterChainDefinitionMap.put("/drag/onlDragDatasetHead/queryAllById", "anon");
filterChainDefinitionMap.put("/jimubi/view", "anon");
filterChainDefinitionMap.put("/jimubi/share/view/**", "anon");
@ -169,6 +173,10 @@ public class ShiroConfig {
filterChainDefinitionMap.put("/websocket/**", "anon");//系统通知和公告
filterChainDefinitionMap.put("/newsWebsocket/**", "anon");//CMS模块
filterChainDefinitionMap.put("/vxeSocket/**", "anon");//JVxeTable无痕刷新示例
//App vue3版本查询版本接口
filterChainDefinitionMap.put("/sys/version/app3version", "anon");
//仪表盘(按钮通信)
filterChainDefinitionMap.put("/dragChannelSocket/**","anon");
//性能监控——安全隐患泄露TOEKNdurid连接池也有
//filterChainDefinitionMap.put("/actuator/**", "anon");
@ -180,8 +188,6 @@ public class ShiroConfig {
// 企业微信证书排除
filterChainDefinitionMap.put("/WW_verify*", "anon");
filterChainDefinitionMap.put("/openapi/call/**", "anon");
// 添加自己的过滤器并且取名为jwt
Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
//如果cloudServer为空 则说明是单体 需要加载跨域配置【微服务跨域切换】
@ -198,7 +204,6 @@ public class ShiroConfig {
return shiroFilterFactoryBean;
}
//update-begin---author:chenrui ---date:20240126 for【QQYUN-7932】AI助手------------
/**
* spring过滤装饰器 <br/>
@ -214,19 +219,19 @@ public class ShiroConfig {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new DelegatingFilterProxy("shiroFilterFactoryBean"));
registration.setEnabled(true);
//update-begin---author:chenrui ---date:20241202 for[issues/7491]运行时间好长,效率慢 ------------
// 代码逻辑说明: [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.addUrlPatterns("/airag/app/prompt/generate");
registration.addUrlPatterns("/airag/chat/receive/**");
//支持异步
registration.setAsyncSupported(true);
registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC);
return registration;
}
//update-end---author:chenrui ---date:20240126 for【QQYUN-7932】AI助手------------
@Bean("securityManager")
public DefaultWebSecurityManager securityManager(ShiroRealm myRealm) {
@ -320,7 +325,7 @@ public class ShiroConfig {
return sentinelManager;
}
// redis 单机支持,在集群为空,或者集群无机器时候使用 add by jzyadmin@163.com
if (lettuceConnectionFactory.getClusterConfiguration() == null || lettuceConnectionFactory.getClusterConfiguration().getClusterNodes().isEmpty()) {
RedisManager redisManager = new RedisManager();
@ -347,7 +352,6 @@ public class ShiroConfig {
JedisCluster jedisCluster = new JedisCluster(portSet);
redisManager.setJedisCluster(jedisCluster);
}
//update-end--Author:scott Date:20210531 for修改集群模式下未设置redis密码的bug issues/I3QNIC
manager = redisManager;
}
return manager;
@ -364,7 +368,7 @@ public class ShiroConfig {
mapping.setUrlPathHelper(new ShiroUrlPathHelper());
return mapping;
}
private List<String> rebuildUrl(String[] bases, String[] uris) {
List<String> urls = new ArrayList<>();
for (String base : bases) {

View File

@ -83,7 +83,7 @@ public class ShiroRealm extends AuthorizingRealm {
Set<String> permissionSet = commonApi.queryUserAuths(userId);
info.addStringPermissions(permissionSet);
//System.out.println(permissionSet);
log.info("===============Shiro权限认证成功==============");
log.debug("===============Shiro权限认证成功==============");
return info;
}
@ -109,9 +109,9 @@ public class ShiroRealm extends AuthorizingRealm {
try {
loginUser = this.checkUserTokenIsEffect(token);
} catch (AuthenticationException e) {
JwtUtil.responseError(SpringContextUtils.getHttpServletResponse(),401,e.getMessage());
e.printStackTrace();
return null;
log.error("—————校验 check token 失败——————————"+ e.getMessage(), e);
// 重新抛出异常让JwtFilter统一处理避免返回两次错误响应
throw e;
}
return new SimpleAuthenticationInfo(loginUser, token, getName());
}
@ -125,7 +125,7 @@ public class ShiroRealm extends AuthorizingRealm {
// 解密获得username用于和数据库进行对比
String username = JwtUtil.getUsername(token);
if (username == null) {
throw new AuthenticationException("token非法无效!");
throw new AuthenticationException("Token非法无效!");
}
// 查询用户信息
@ -141,9 +141,11 @@ public class ShiroRealm extends AuthorizingRealm {
}
// 校验token是否超时失效 & 或者账号密码是否错误
if (!jwtTokenRefresh(token, username, loginUser.getPassword())) {
throw new AuthenticationException(CommonConstant.TOKEN_IS_INVALID_MSG);
// 用户登录Token过期提示信息
String userLoginTokenErrorMsg = oConvertUtils.getString(redisUtil.get(CommonConstant.PREFIX_USER_TOKEN_ERROR_MSG + token));
throw new AuthenticationException(oConvertUtils.isEmpty(userLoginTokenErrorMsg)? CommonConstant.TOKEN_IS_INVALID_MSG: userLoginTokenErrorMsg);
}
//update-begin-author:taoyan date:20210609 for:校验用户的tenant_id和前端传过来的是否一致
// 代码逻辑说明: 校验用户的tenant_id和前端传过来的是否一致
String userTenantIds = loginUser.getRelTenantIds();
if(MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL && oConvertUtils.isNotEmpty(userTenantIds)){
String contextTenantId = TenantContext.getTenant();
@ -152,7 +154,7 @@ public class ShiroRealm extends AuthorizingRealm {
//登录用户无租户前端header中租户ID值为 0
String str ="0";
if(oConvertUtils.isNotEmpty(contextTenantId) && !str.equals(contextTenantId)){
//update-begin-author:taoyan date:20211227 for: /issues/I4O14W 用户租户信息变更判断漏洞
// 代码逻辑说明: /issues/I4O14W 用户租户信息变更判断漏洞
String[] arr = userTenantIds.split(",");
if(!oConvertUtils.isIn(contextTenantId, arr)){
boolean isAuthorization = false;
@ -177,10 +179,8 @@ public class ShiroRealm extends AuthorizingRealm {
}
//*********************************************
}
//update-end-author:taoyan date:20211227 for: /issues/I4O14W 用户租户信息变更判断漏洞
}
}
//update-end-author:taoyan date:20210609 for:校验用户的tenant_id和前端传过来的是否一致
return loginUser;
}
@ -202,19 +202,22 @@ public class ShiroRealm extends AuthorizingRealm {
if (oConvertUtils.isNotEmpty(cacheToken)) {
// 校验token有效性
if (!JwtUtil.verify(cacheToken, userName, passWord)) {
String newAuthorization = JwtUtil.sign(userName, passWord);
// 设置超时时间
// 从token中解析客户端类型保持续期时使用相同的客户端类型
String clientType = JwtUtil.getClientType(token);
String newAuthorization = JwtUtil.sign(userName, passWord, clientType);
// 根据客户端类型设置对应的缓存有效时间
long expireTime = CommonConstant.CLIENT_TYPE_APP.equalsIgnoreCase(clientType)
? JwtUtil.APP_EXPIRE_TIME * 2 / 1000
: JwtUtil.EXPIRE_TIME * 2 / 1000;
redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, newAuthorization);
redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME *2 / 1000);
redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, expireTime);
log.debug("——————————用户在线操作更新token保证不掉线—————————jwtTokenRefresh——————— "+ token);
}
//update-begin--Author:scott Date:20191005 for解决每次请求都重写redis中 token缓存问题
// else {
// // 设置超时时间
// redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, cacheToken);
// redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME / 1000);
// }
//update-end--Author:scott Date:20191005 for解决每次请求都重写redis中 token缓存问题
return true;
}
@ -230,8 +233,7 @@ public class ShiroRealm extends AuthorizingRealm {
@Override
public void clearCache(PrincipalCollection principals) {
super.clearCache(principals);
//update-begin---author:scott ---date::2024-06-18 for【TV360X-1320】分配权限必须退出重新登录才生效造成很多用户困扰---
// 代码逻辑说明: 【TV360X-1320】分配权限必须退出重新登录才生效造成很多用户困扰---
super.clearCachedAuthorizationInfo(principals);
//update-end---author:scott ---date::2024-06-18 for【TV360X-1320】分配权限必须退出重新登录才生效造成很多用户困扰---
}
}

View File

@ -56,9 +56,13 @@ public class JwtFilter extends BasicHttpAuthenticationFilter {
executeLogin(request, response);
return true;
} catch (Exception e) {
JwtUtil.responseError(response,401,CommonConstant.TOKEN_IS_INVALID_MSG);
// 使用异常中的具体错误信息,保留"不允许同一账号多地同时登录"等具体提示
String errorMsg = e.getMessage();
if (oConvertUtils.isEmpty(errorMsg)) {
errorMsg = CommonConstant.TOKEN_IS_INVALID_MSG;
}
JwtUtil.responseError((HttpServletResponse)response, 401, errorMsg);
return false;
//throw new AuthenticationException("Token失效请重新登录", e);
}
}
@ -69,11 +73,10 @@ public class JwtFilter extends BasicHttpAuthenticationFilter {
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String token = httpServletRequest.getHeader(CommonConstant.X_ACCESS_TOKEN);
// update-begin--Author:lvdandan Date:20210105 forJT-355 OA聊天添加token验证获取token参数
// 代码逻辑说明: JT-355 OA聊天添加token验证获取token参数
if (oConvertUtils.isEmpty(token)) {
token = httpServletRequest.getParameter("token");
}
// update-end--Author:lvdandan Date:20210105 forJT-355 OA聊天添加token验证获取token参数
JwtToken jwtToken = new JwtToken(token);
// 提交给realm进行登入如果错误他会抛出异常并被捕获
@ -106,10 +109,9 @@ public class JwtFilter extends BasicHttpAuthenticationFilter {
httpServletResponse.setStatus(HttpStatus.OK.value());
return false;
}
//update-begin-author:taoyan date:20200708 for:多租户用到
// 代码逻辑说明: 多租户用到
String tenantId = httpServletRequest.getHeader(CommonConstant.TENANT_ID);
TenantContext.setTenant(tenantId);
//update-end-author:taoyan date:20200708 for:多租户用到
return super.preHandle(request, response);
}

View File

@ -4,6 +4,7 @@ import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.config.shiro.IgnoreAuth;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
@ -20,6 +21,7 @@ import java.util.stream.Collectors;
* @date 2024/4/18 11:35
*/
@Slf4j
@Lazy(false)
@Component
@AllArgsConstructor
public class IgnoreAuthPostProcessor implements InitializingBean {
@ -33,10 +35,15 @@ public class IgnoreAuthPostProcessor implements InitializingBean {
long startTime = System.currentTimeMillis();
List<String> ignoreAuthUrls = new ArrayList<>();
Set<Class<?>> restControllers = requestMappingHandlerMapping.getHandlerMethods().values().stream().map(HandlerMethod::getBeanType).collect(Collectors.toSet());
for (Class<?> restController : restControllers) {
ignoreAuthUrls.addAll(postProcessRestController(restController));
}
// 优化直接从HandlerMethod过滤避免重复扫描
requestMappingHandlerMapping.getHandlerMethods().values().stream()
.filter(handlerMethod -> handlerMethod.getMethod().isAnnotationPresent(IgnoreAuth.class))
.forEach(handlerMethod -> {
Class<?> clazz = handlerMethod.getBeanType();
Method method = handlerMethod.getMethod();
ignoreAuthUrls.addAll(processIgnoreAuthMethod(clazz, method));
});
log.info("Init Token ignoreAuthUrls Config [ 集合 ] {}", ignoreAuthUrls);
if (!CollectionUtils.isEmpty(ignoreAuthUrls)) {
@ -46,44 +53,30 @@ public class IgnoreAuthPostProcessor implements InitializingBean {
// 计算方法的耗时
long endTime = System.currentTimeMillis();
long elapsedTime = endTime - startTime;
log.info("Init Token ignoreAuthUrls Config [ 耗时 ] " + elapsedTime + "毫秒");
log.info("Init Token ignoreAuthUrls Config [ 耗时 ] " + elapsedTime + "ms");
}
private List<String> postProcessRestController(Class<?> clazz) {
List<String> ignoreAuthUrls = new ArrayList<>();
// 优化:新方法处理单个@IgnoreAuth方法减少重复注解检查
private List<String> processIgnoreAuthMethod(Class<?> clazz, Method method) {
RequestMapping base = clazz.getAnnotation(RequestMapping.class);
String[] baseUrl = Objects.nonNull(base) ? base.value() : new String[]{};
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(RequestMapping.class)) {
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
String[] uri = requestMapping.value();
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
} else if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(GetMapping.class)) {
GetMapping requestMapping = method.getAnnotation(GetMapping.class);
String[] uri = requestMapping.value();
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
} else if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(PostMapping.class)) {
PostMapping requestMapping = method.getAnnotation(PostMapping.class);
String[] uri = requestMapping.value();
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
} else if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(PutMapping.class)) {
PutMapping requestMapping = method.getAnnotation(PutMapping.class);
String[] uri = requestMapping.value();
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
} else if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(DeleteMapping.class)) {
DeleteMapping requestMapping = method.getAnnotation(DeleteMapping.class);
String[] uri = requestMapping.value();
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
} else if (method.isAnnotationPresent(IgnoreAuth.class) && method.isAnnotationPresent(PatchMapping.class)) {
PatchMapping requestMapping = method.getAnnotation(PatchMapping.class);
String[] uri = requestMapping.value();
ignoreAuthUrls.addAll(rebuildUrl(baseUrl, uri));
}
String[] uri = null;
if (method.isAnnotationPresent(RequestMapping.class)) {
uri = method.getAnnotation(RequestMapping.class).value();
} else if (method.isAnnotationPresent(GetMapping.class)) {
uri = method.getAnnotation(GetMapping.class).value();
} else if (method.isAnnotationPresent(PostMapping.class)) {
uri = method.getAnnotation(PostMapping.class).value();
} else if (method.isAnnotationPresent(PutMapping.class)) {
uri = method.getAnnotation(PutMapping.class).value();
} else if (method.isAnnotationPresent(DeleteMapping.class)) {
uri = method.getAnnotation(DeleteMapping.class).value();
} else if (method.isAnnotationPresent(PatchMapping.class)) {
uri = method.getAnnotation(PatchMapping.class).value();
}
return ignoreAuthUrls;
return uri != null ? rebuildUrl(baseUrl, uri) : Collections.emptyList();
}
private List<String> rebuildUrl(String[] bases, String[] uris) {

View File

@ -41,7 +41,7 @@ public class SignAuthConfiguration implements WebMvcConfigurer {
registry.addInterceptor(signAuthInterceptor()).addPathPatterns(signUrlsArray);
}
//update-begin-author:taoyan date:20220427 for: issues/I53J5E post请求X_SIGN签名拦截校验后报错, request body 为空
// 代码逻辑说明: issues/I53J5E post请求X_SIGN签名拦截校验后报错, request body 为空
@Bean
public RequestBodyReserveFilter requestBodyReserveFilter(){
return new RequestBodyReserveFilter();
@ -66,6 +66,5 @@ public class SignAuthConfiguration implements WebMvcConfigurer {
registration.addUrlPatterns(signUrlsArray);
return registration;
}
//update-end-author:taoyan date:20220427 for: issues/I53J5E post请求X_SIGN签名拦截校验后报错, request body 为空
}

View File

@ -33,7 +33,7 @@ public class SignAuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("Sign Interceptor request URI = " + request.getRequestURI());
log.debug("Sign Interceptor request URI = " + request.getRequestURI());
HttpServletRequest requestWrapper = new BodyReaderHttpServletRequestWrapper(request);
//获取全部参数(包括URL和body上的)
SortedMap<String, String> allParams = HttpUtils.getAllParams(requestWrapper);
@ -79,7 +79,7 @@ public class SignAuthInterceptor implements HandlerInterceptor {
log.debug("Sign 签名通过Header Sign : {}",headerSign);
return true;
} else {
log.info("sign allParams: {}", allParams);
log.debug("sign allParams: {}", allParams);
log.error("request URI = " + request.getRequestURI());
log.error("Sign 签名校验失败Header Sign : {}",headerSign);
//校验失败返回前端

View File

@ -41,19 +41,19 @@ public class HttpUtils {
// 获取URL上最后带逗号的参数变量 sys/dict/getDictItems/sys_user,realname,username
String pathVariable = request.getRequestURI().substring(request.getRequestURI().lastIndexOf("/") + 1);
if (pathVariable.contains(SymbolConstant.COMMA)) {
log.info(" pathVariable: {}",pathVariable);
log.debug(" pathVariable: {}",pathVariable);
String deString = URLDecoder.decode(pathVariable, "UTF-8");
//https://www.52dianzi.com/category/article/37/565371.html
if(deString.contains("%")){
try {
deString = URLDecoder.decode(deString, "UTF-8");
log.info("存在%情况下,执行两次解码 — pathVariable decode: {}",deString);
log.debug("存在%情况下,执行两次解码 — pathVariable decode: {}",deString);
} catch (Exception e) {
//e.printStackTrace();
}
}
log.info(" pathVariable decode: {}",deString);
log.debug(" pathVariable decode: {}",deString);
result.put(SignUtil.X_PATH_VARIABLE, deString);
}
// 获取URL上的参数
@ -91,15 +91,15 @@ public class HttpUtils {
// 获取URL上最后带逗号的参数变量 sys/dict/getDictItems/sys_user,realname,username
String pathVariable = url.substring(url.lastIndexOf("/") + 1);
if (pathVariable.contains(SymbolConstant.COMMA)) {
log.info(" pathVariable: {}",pathVariable);
log.debug(" pathVariable: {}",pathVariable);
String deString = URLDecoder.decode(pathVariable, "UTF-8");
//https://www.52dianzi.com/category/article/37/565371.html
if(deString.contains("%")){
deString = URLDecoder.decode(deString, "UTF-8");
log.info("存在%情况下,执行两次解码 — pathVariable decode: {}",deString);
log.debug("存在%情况下,执行两次解码 — pathVariable decode: {}",deString);
}
log.info(" pathVariable decode: {}",deString);
log.debug(" pathVariable decode: {}",deString);
result.put(SignUtil.X_PATH_VARIABLE, deString);
}
// 获取URL上的参数
@ -174,11 +174,10 @@ public class HttpUtils {
String[] params = param.split("&");
for (String s : params) {
int index = s.indexOf("=");
//update-begin---author:chenrui ---date:20240222 for[issues/5879]数据查询传ds=“”造成的异常------------
// 代码逻辑说明: [issues/5879]数据查询传ds=“”造成的异常------------
if (index != -1) {
result.put(s.substring(0, index), s.substring(index + 1));
}
//update-end---author:chenrui ---date:20240222 for[issues/5879]数据查询传ds=“”造成的异常------------
}
return result;
}
@ -202,11 +201,10 @@ public class HttpUtils {
String[] params = param.split("&");
for (String s : params) {
int index = s.indexOf("=");
//update-begin---author:chenrui ---date:20240222 for[issues/5879]数据查询传ds=“”造成的异常------------
// 代码逻辑说明: [issues/5879]数据查询传ds=“”造成的异常------------
if (index != -1) {
result.put(s.substring(0, index), s.substring(index + 1));
}
//update-end---author:chenrui ---date:20240222 for[issues/5879]数据查询传ds=“”造成的异常------------
}
return result;
}

View File

@ -34,7 +34,7 @@ public class SignUtil {
}
// 把参数加密
String paramsSign = getParamsSign(params);
log.info("Param Sign : {}", paramsSign);
log.debug("Param Sign : {}", paramsSign);
return !StringUtils.isEmpty(paramsSign) && headerSign.equals(paramsSign);
}
@ -47,7 +47,7 @@ public class SignUtil {
//去掉 Url 里的时间戳
params.remove("_t");
String paramsJsonStr = JSONObject.toJSONString(params);
log.info("Param paramsJsonStr : {}", paramsJsonStr);
log.debug("Param paramsJsonStr : {}", paramsJsonStr);
//设置签名秘钥
JeecgBaseConfig jeecgBaseConfig = SpringContextUtils.getBean(JeecgBaseConfig.class);
String signatureSecret = jeecgBaseConfig.getSignatureSecret();

View File

@ -0,0 +1,38 @@
package org.jeecg.config.tencent;
import lombok.Data;
/**
* @Description: 腾讯短信配置
*
* @author: wangshuai
* @date: 2025/10/30 18:22
*/
@Data
public class JeecgTencent {
/**
* 接入域名
*/
private String endpoint;
/**
* api秘钥id
*/
private String secretId;
/**
* api秘钥key
*/
private String secretKey;
/**
* 应用id
*/
private String sdkAppId;
/**
* 地域信息
*/
private String region;
}

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