Compare commits

..

150 Commits

Author SHA1 Message Date
5972c74b43 解决积木报表springboot3 找不到类base64utils #3834和SqlServer兼容问题 2025-06-05 15:51:05 +08:00
d69cb121fc 解决AI脚本节点执行报错问题 2025-05-28 11:07:33 +08:00
10a9edd10b ai⼯作流使⽤知识库报错 "白名单校验未通过" 2025-05-27 17:56:46 +08:00
c71ff3fbcc 访问Swagger接口不带doc.html后缀,会丢失项目前缀/jeecg-boot/导致测试接口,返回下载文件 2025-05-26 19:14:02 +08:00
08612d5bfa springboot3分支的redis配置格式改了 2025-05-22 11:12:27 +08:00
2ecce8f02d 更新nacos配置,增加aigc的配置 2025-05-22 10:59:57 +08:00
62937f14fb 升级shiro到2.0.4 2025-05-22 10:55:16 +08:00
d6ccc4a326 还原库名 2025-05-22 10:50:17 +08:00
1893108136 升级版本号到3.8.1(springboot3升级到3.4.5) 2025-05-22 10:18:25 +08:00
7980915bdc 移除AI大模型管理依赖并更新pom.xml 2025-05-22 09:28:04 +08:00
550997268b 最新版 2025-05-20 10:24:34 +08:00
9e7d40a080 升级springboot3.4.5 2025-05-19 16:09:09 +08:00
2c38db456b 合并redis配置错误 2025-05-19 13:58:09 +08:00
e52538d304 升级spring3.4.5后,会有很多警告BeanPostProcessorChecker:437 - Bean 'org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration' of type [org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying). Is this bean getting eagerly injected/applied to a currently created BeanPostProcessor [defaultAdvisorAutoProxyCreator]? Check the corresponding BeanPostProcessor declaration and its dependencies/advisors. If this bean does not have to be post-processed, declare it with ROLE_INFRASTRUCTURE. 2025-05-18 16:40:55 +08:00
e91cbd5cd8 Merge pull request #8297 from MuShan-bit/springboot3_upgrade344-fix-warn
logging(level): 设置 PostProcessorRegistrationDelegate 日志级别为 error
2025-05-18 16:33:14 +08:00
70cec8b5c6 logging(level): 设置 PostProcessorRegistrationDelegate 日志级别为 error
- 在 application-dev.yml 文件中添加了 org.springframework.context.support.PostProcessorRegistrationDelegate 的日志级别配置
- 此修改旨在减少不必要的日志输出,提高日志的可读性和性能
2025-05-16 22:29:00 +08:00
d2365088ce AIGC大模型应用功能 2025-05-16 11:27:48 +08:00
a679571a5a 基于AK和SK认证鉴权OpenAPI 2025-05-16 10:41:24 +08:00
b9c74e549f 移除javax.annotation.Resource导入,改为使用jakarta.annotation.Resource 2025-05-16 10:31:49 +08:00
81c1724016 升级online到v3.8.0版本 2025-05-16 10:31:39 +08:00
56d59eb589 修改springboot3 v3.8.0发布时间 2025-05-16 09:58:36 +08:00
a00fcae3a3 【v3.8.0 合并】Merge remote-tracking branch 'origin/master' into springboot3
# Conflicts:
#	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/system/util/JwtUtil.java
#	jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/oConvertUtils.java
#	jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/Swagger2Config.java
#	jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/Swagger3Config.java
#	jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/WebMvcConfiguration.java
#	jeecg-boot/jeecg-boot-module/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/test/controller/JeecgDemoController.java
#	jeecg-boot/jeecg-boot-module/jeecg-module-demo/src/main/java/org/jeecg/modules/demo/test/entity/JeecgDemo.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/openapi/controller/OpenApiController.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/openapi/controller/OpenApiLogController.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/openapi/controller/OpenApiPermissionController.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/openapi/entity/OpenApi.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/openapi/entity/OpenApiAuth.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/openapi/entity/OpenApiHeader.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/openapi/entity/OpenApiLog.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/openapi/entity/OpenApiParam.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/openapi/filter/ApiAuthFilter.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/openapi/mapper/OpenApiLogMapper.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/openapi/service/OpenApiLogService.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/DuplicateCheckController.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/LoginController.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/SysCommentController.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/SysDataSourceController.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/SysDepartPermissionController.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/SysDepartRoleController.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/SysDictItemController.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/SysGatewayRouteController.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/controller/SysTableWhiteListController.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/entity/SysCheckRule.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/entity/SysComment.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/entity/SysDataSource.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/entity/SysDepartPermission.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/entity/SysDepartRole.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/entity/SysDepartRolePermission.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/entity/SysDepartRoleUser.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/entity/SysFillRule.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/entity/SysFormFile.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/entity/SysGatewayRoute.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/entity/SysPackPermission.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/entity/SysPosition.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/entity/SysRoleIndex.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/entity/SysTableWhiteList.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/entity/SysTenantPack.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/entity/SysTenantPackUser.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/entity/SysThirdAccount.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/entity/SysThirdAppConfig.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/entity/SysUserPosition.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/entity/SysUserTenant.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/model/DuplicateCheckVo.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/springframework/boot/autoconfigure/mongo/MongoAutoConfiguration.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template-online/default/one/java/${bussiPackage}/${entityPackage}/controller/${entityName}Controller.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template-online/default/onetomany/java/${bussiPackage}/${entityPackage}/controller/${entityName}Controller.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template-online/default/tree/java/${bussiPackage}/${entityPackage}/controller/${entityName}Controller.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template-online/default/tree/java/${bussiPackage}/${entityPackage}/entity/${entityName}.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template-online/erp/onetomany/java/${bussiPackage}/${entityPackage}/controller/${entityName}Controller.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template-online/erp/onetomany/java/${bussiPackage}/${entityPackage}/entity/${entityName}.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template-online/erp/onetomany/java/${bussiPackage}/${entityPackage}/entity/[1-n]Entity.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template-online/inner-table/onetomany/java/${bussiPackage}/${entityPackage}/controller/${entityName}Controller.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template-online/inner-table/onetomany/java/${bussiPackage}/${entityPackage}/entity/${entityName}.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template-online/inner-table/onetomany/java/${bussiPackage}/${entityPackage}/entity/[1-n]Entity.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template-online/inner-table/onetomany/java/${bussiPackage}/${entityPackage}/vo/${entityName}Page.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/controller/${entityName}Controller.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/entity/${entityName}.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template-online/jvxe/onetomany/java/${bussiPackage}/${entityPackage}/vo/${entityName}Page.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template-online/tab/onetomany/java/${bussiPackage}/${entityPackage}/controller/${entityName}Controller.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template-online/tab/onetomany/java/${bussiPackage}/${entityPackage}/entity/${entityName}.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template-online/tab/onetomany/java/${bussiPackage}/${entityPackage}/entity/[1-n]Entity.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template-online/tab/onetomany/java/${bussiPackage}/${entityPackage}/vo/${entityName}Page.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template/one/java/${bussiPackage}/${entityPackage}/controller/${entityName}Controller.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template/one/java/${bussiPackage}/${entityPackage}/entity/${entityName}.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template/one2/java/${bussiPackage}/controller/${entityPackage}/${entityName}Controller.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template/onetomany/java/${bussiPackage}/${entityPackage}/controller/${entityName}Controller.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template/onetomany/java/${bussiPackage}/${entityPackage}/entity/${entityName}.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template/onetomany/java/${bussiPackage}/${entityPackage}/entity/[1-n]Entity.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template/onetomany/java/${bussiPackage}/${entityPackage}/vo/${entityName}Page.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template/onetomany2/java/${bussiPackage}/${entityPackage}/controller/${entityName}Controller.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template/onetomany2/java/${bussiPackage}/${entityPackage}/entity/${entityName}.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template/onetomany2/java/${bussiPackage}/${entityPackage}/entity/[1-n]Entity.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/resources/jeecg/code-template/onetomany2/java/${bussiPackage}/${entityPackage}/vo/${entityName}Page.javai
#	jeecg-boot/jeecg-module-system/jeecg-system-start/pom.xml
#	jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/java/org/jeecg/config/flyway/FlywayConfig.java
#	jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml
#	jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/application-prod.yml
#	jeecg-boot/jeecg-module-system/jeecg-system-start/src/main/resources/application-test.yml
#	jeecg-boot/jeecg-module-system/jeecg-system-start/src/test/java/org/jeecg/modules/system/test/SampleTest.java
#	jeecg-boot/jeecg-server-cloud/jeecg-cloud-gateway/src/main/java/org/jeecg/handler/swagger/SwaggerResourceController.java
#	jeecg-boot/jeecg-server-cloud/jeecg-cloud-gateway/src/main/java/org/jeecg/loader/DynamicRouteLoader.java
#	jeecg-boot/jeecg-server-cloud/jeecg-cloud-gateway/src/main/resources/application.yml
#	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-more/src/main/java/org/jeecg/modules/test/feign/controller/JeecgTestFeignController.java
#	jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-test/jeecg-cloud-test-rocketmq/src/main/java/org/jeecg/modules/test/rocketmq/controller/JeecgMqTestController.java
#	jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-test/jeecg-cloud-test-seata/jeecg-cloud-test-seata-order/src/main/java/org/jeecg/modules/test/seata/order/controller/SeataOrderController.java
#	jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-xxljob/src/main/java/com/xxl/job/admin/core/old/RemoteHttpJobBean.java
#	jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-xxljob/src/main/java/com/xxl/job/admin/core/old/XxlJobDynamicScheduler.java
#	jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-xxljob/src/main/java/com/xxl/job/admin/core/old/XxlJobThreadPool.java
#	jeecg-boot/pom.xml
2025-05-15 20:01:54 +08:00
286d10a50f Merge branch 'springboot3_upgrade344' of https://github.com/jeecgboot/jeecg-boot into springboot3_upgrade344 2025-05-14 15:19:10 +08:00
68f36cb1e5 升级online 2025-05-14 13:59:56 +08:00
78454d3434 Merge pull request #8273 from MuShan-bit/springboot3_upgrade344-upgrade-shiro2.0.4
feat: 升级 shiro 到 2.0.4 版本,解决 ShiroRequestMappingConfig 获取 RequestMappingHandlerMapping Bean 冲突
2025-05-14 09:21:36 +08:00
56fbc2ed8f feat: 升级 shiro 到 2.0.4 版本,解决Shiro获取 requestMappingHandlerMapping 时 spring-boot-autoconfigure:3.4.5 和 spring-boot-actuator-autoconfigure:3.4.5 Bean 依赖冲突, 2025-05-13 22:48:48 +08:00
197d7adaaf Merge pull request #8256 from MuShan-bit/springboot3-chore-upgrade-shiro
upgrade shiro to 2.0.4
2025-05-11 10:34:23 +08:00
e952518d71 feat: 升级 shiro 到 2.0.4 版本 2025-05-10 22:14:02 +08:00
1e259c805e fastjson升级到2.0.57;jimureport升级到1.9.5;minidao升级到1.10.8 2025-05-08 22:39:57 +08:00
8a82141c95 升级jsqlparser到4.9 2025-05-08 16:47:46 +08:00
888a032266 优化bean无法被所有beanpostprocessor处理 2025-04-30 10:00:02 +08:00
309c76d268 修复swagger接口文档正常显示 2025-04-25 18:08:45 +08:00
f78eabfc66 使用minidao适配jsqlparser 2025-04-25 16:54:55 +08:00
748331d649 处理jsqlparser兼容问题 2025-04-22 16:00:17 +08:00
b70e709e53 升级spring boot 3.4.4 2025-04-16 16:18:32 +08:00
2ba17648c4 Merge pull request #8116 from EightMonth/springboot3
优化swagger文档改造
2025-04-15 11:41:44 +08:00
36caab37e2 Update application-mysql.yml 2025-04-15 11:07:54 +08:00
6e721e4120 归集spring-doc默认配置
(cherry picked from commit d4d0c884f0)
2025-04-15 10:39:05 +08:00
a17b403675 优化swagger文档架构改造 2025-04-03 17:46:11 +08:00
632fd72d79 Merge pull request #8053 from EightMonth/springboot3
排除部分接口文档,为免登录接口排除token校验请求头
2025-04-01 21:28:44 +08:00
15fc262675 排除部分接口文档,为免登录接口排除token校验请求头 2025-04-01 18:05:04 +08:00
6768d65e1e Merge pull request #8008 from EightMonth/springboot3
修复 CVE-2023-6378
2025-03-25 16:02:57 +08:00
410ab7bcc3 修复 CVE-2023-6378 2025-03-25 15:58:19 +08:00
174f1ae432 Merge pull request #8004 from EightMonth/springboot3
jeewx-api修改成weixin4j
2025-03-25 14:19:42 +08:00
eef2f7e269 jeewx-api修改成weixin4j 2025-03-25 14:13:45 +08:00
6a0ec66d3d Merge branch 'springboot3' of https://github.com/jeecgboot/jeecg-boot into springboot3 2025-03-25 14:10:17 +08:00
163b0b531f 视频介绍 2025-03-18 10:08:05 +08:00
d1af49a33f Merge pull request #7949 from EightMonth/springboot3
解决严重bug,War包方式部署,服务启动报错
2025-03-12 15:28:07 +08:00
03265691e6 解决严重bug,War包方式部署,服务启动报错 2025-03-12 14:12:00 +08:00
de9cc2f30d Merge pull request #7874 from EightMonth/springboot3
修复 #7613
2025-03-03 17:05:01 +08:00
26887959cd 修复 #7613 2025-03-03 14:27:16 +08:00
7e15e81218 版本合并,升级springboot3分支到3.7.3 2025-02-20 17:56:16 +08:00
8b0e0367c7 Merge pull request #7797 from EightMonth/springboot3
固定vue-router版本号
2025-02-11 14:49:06 +08:00
334f7dbb62 Update package.json 2025-02-11 14:24:18 +08:00
e9ddd21286 固定vue-router版本号 2025-02-11 09:53:29 +08:00
458526075e Merge remote-tracking branch 'origin/springboot3' into springboot3 2024-12-24 15:44:51 +08:00
a1b55f0d40 解决vue-router升级版本报错问题 2024-12-24 15:44:27 +08:00
2f0a3bcd87 Merge pull request #7379 from EightMonth/springboot3
分布式事务demo修复
2024-11-20 10:31:10 +08:00
30d3a9f17b 分布式事务demo修复 2024-10-24 09:15:06 +08:00
03739f2837 【springboot3分支 issues/7353】The bean 'dataSource', defined in class path resource #7353 2024-10-19 20:18:35 +08:00
d9e8bd2bc8 Merge pull request #7317 from EightMonth/springboot3
修改docker镜像base为JDK17
2024-10-09 16:08:38 +08:00
81eef5a838 修改docker镜像base为JDK17 2024-10-09 16:05:22 +08:00
f528f72903 升级仪表盘到最新版 2024-10-08 22:33:29 +08:00
918286c144 升级online模块和autopoi 2024-10-08 21:18:18 +08:00
512234a804 【版本合并】 branch 'origin/master' into springboot3 2024-10-08 19:30:14 +08:00
cacc59b8fd 升级jimureport到最新版1.7.8 2024-07-08 12:18:09 +08:00
c744633139 升级jimureport到最新版1.7.7 2024-07-06 22:20:36 +08:00
0e4d304878 升级仪表盘 2024-07-03 11:56:53 +08:00
17a8964487 更新online模块为3.7.0最新依赖 2024-07-03 11:09:15 +08:00
8ac6989d2c Merge remote-tracking branch 'origin/master' into springboot3 2024-06-23 11:22:06 +08:00
402ab0ffc4 补充合并丢失的代码 2024-06-23 10:27:33 +08:00
7778ede90e Merge remote-tracking branch 'origin/master' into springboot3
# Conflicts:
#	jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/Swagger3Config.java
#	jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/UndertowCustomizer.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/SysFilesController.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/entity/SysFiles.java
2024-06-23 10:23:56 +08:00
06144206df 修改nacos命名空间springboot3的id也是springboot3,方便理解 2024-06-22 23:23:05 +08:00
3d3b5850ad online的依赖升级个小版本号 2024-06-22 17:18:59 +08:00
816eeb9225 3.7.0版本合并springboot3分支
本次提交未升级【minidao、仪表盘、online的依赖】
2024-06-22 17:03:43 +08:00
0b42efbbbf Merge remote-tracking branch 'origin/springboot3' into springboot3 2024-06-22 14:58:29 +08:00
b8e0d4391d Merge pull request #6200 from EightMonth/springboot3
修复 #6169
2024-04-30 13:52:00 +08:00
72b34d082b 修复 #6169 2024-04-30 11:53:59 +08:00
7112649a21 Merge branch 'springboot3' of https://github.com/jeecgboot/jeecg-boot into springboot3 2024-04-26 11:06:35 +08:00
fbc312c35d Merge pull request #6173 from EightMonth/springboot3
修复#6127 #6130
2024-04-25 20:04:26 +08:00
b8162a4a6d 修复#6127 #6130 2024-04-25 16:01:58 +08:00
28404d2fd3 Merge pull request #6091 from EightMonth/springboot3
升级druid v1.2.22版本兼容处理
2024-04-08 15:26:49 +08:00
c92c9be49a 升级druid v1.2.22版本兼容处理 2024-04-08 14:04:07 +08:00
58e85e0569 Merge pull request #6081 from EightMonth/springboot3
升级druid1.2.22版本兼容处理
2024-04-03 16:35:30 +08:00
6fc34d8a39 升级druid1.2.22版本兼容处理 2024-04-03 16:18:31 +08:00
790df934b5 Merge branch 'springboot3' of https://github.com/jeecgboot/jeecg-boot into springboot3
 Conflicts:
	pom.xml
2024-03-30 23:39:54 +08:00
8aee4011a2 Merge pull request #6036 from EightMonth/springboot3
合并master变更,升级 3.6.3
2024-03-25 15:32:01 +08:00
6e0277c60a 升级druid版本,修复 #5936 2024-03-25 14:37:00 +08:00
e923654161 升级jimu版本至1.7.3,屏蔽flyway 2024-03-25 10:52:37 +08:00
06b41ae479 Merge branch 'master' into springboot3
# Conflicts:
#	db/tables_nacos.sql
#	jeecg-boot-base-core/pom.xml
#	jeecg-boot-base-core/src/main/java/org/jeecg/config/Swagger2Config.java
#	jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/ShiroConfig.java
#	jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/SysFilesController.java
#	jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/SysRoleIndexController.java
#	jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/entity/SysFiles.java
#	jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/impl/SysPermissionServiceImpl.java
#	jeecg-module-system/jeecg-system-start/pom.xml
#	jeecg-module-system/jeecg-system-start/src/main/resources/application-dev.yml
#	jeecg-server-cloud/jeecg-cloud-gateway/pom.xml
#	jeecg-server-cloud/jeecg-visual/jeecg-cloud-sentinel/pom.xml
#	pom.xml
2024-03-25 09:39:13 +08:00
11af85d87a springboot3, 积木报表 聚合分组查询失败 #2398 2024-03-04 21:12:35 +08:00
4caff75cce Merge pull request #5935 from EightMonth/springboot3_config
修正spring boot3默认配置
2024-03-01 16:24:33 +08:00
811861a957 添加nacos sql自动创建nacos库 2024-03-01 16:15:06 +08:00
24623ba4b0 梳理服务配置信息 2024-03-01 16:06:12 +08:00
7c68b46943 添加springboot3的配置变更 2024-03-01 16:04:57 +08:00
7c34161369 删除无用文件 2024-02-29 17:41:13 +08:00
bc52aa918d gateway的配置改坏了,导致命名空间等不好使 2024-02-29 17:30:04 +08:00
9dfdd47b36 springboot3版本的仪表盘依赖有问题,升级一个版本 2024-01-12 11:32:26 +08:00
272a7540eb 仪表盘升级为springboot3版本 2024-01-12 11:00:51 +08:00
ad796f079f flywaydb兼容springboot3报错,先注释掉 2024-01-12 11:00:37 +08:00
e7e7716d05 Merge pull request #5782 from EightMonth/springboot3
同步主干分支版本代码,并升级jedis至3.8.0
2024-01-12 10:39:24 +08:00
c5d620d2b2 升级jedis版本至3.8.0 2024-01-08 13:54:04 +08:00
cdea05ebb0 Merge branch 'master' into springboot3
# Conflicts:
#	jeecg-boot-base-core/src/main/java/org/jeecg/config/shiro/ShiroConfig.java
#	jeecg-module-demo/src/main/java/org/jeecg/modules/demo/test/entity/JeecgDemo.java
#	jeecg-module-system/jeecg-system-biz/pom.xml
#	jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/SysTableWhiteListController.java
#	jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/WechatVerifyController.java
#	jeecg-module-system/jeecg-system-start/pom.xml
#	jeecg-server-cloud/jeecg-visual/jeecg-cloud-sentinel/pom.xml
#	pom.xml
2024-01-08 13:52:33 +08:00
ca9a433f3c Merge pull request #5765 from hoperunChen/springboot3-fix-#5723
fix issue for springboot3 #5723: 指定jaxb-runtime版本,添加修改记录
2024-01-04 21:41:11 +08:00
2be6052cd4 Merge pull request #5766 from hoperunChen/springboot3-fix-#5742
fix issue for springboot3 #5742: 修改代码生成时的schema注解参数
2024-01-04 21:40:53 +08:00
68ed67ee49 shiro 无法使用 spring boot 3.X 自带的jedis,降版本处理 Merge pull request #5767 from hoperunChen/springboot3-fix-#5741
fix issue for springboot3 #5741: shiro 无法使用 spring boot 3.X 自带的jedis,降版本处理
2024-01-04 21:40:31 +08:00
d5903ba52a Merge remote-tracking branch 'upstream/springboot3' into springboot3-fix-#5741 2024-01-04 20:37:37 +08:00
3ee635eddf fix issue for springboot3 #5723: 指定jaxb-runtime版本,添加修改记录 2024-01-04 20:32:26 +08:00
21bc68fb53 fix issue for springboot3 #5741: shiro 无法使用 spring boot 3.X 自带的jedis,降版本处理 2024-01-04 20:19:46 +08:00
f532e57862 解决升级到springboot3, 表单excel导出失败,找不到 javax/servlet/ServletOutputStream #5738 2024-01-03 17:26:41 +08:00
da08adbea1 fix issue for springboot3 #5742: 修改代码生成时的schema注解参数 2024-01-02 13:41:54 +08:00
46e3e62b59 fix issue for springboot3 #5741: shiro 无法使用 spring boot 3.X 自带的jedis,降版本处理 2024-01-02 13:31:57 +08:00
3656264f8a 提供积木报表fastjson2版本 2023-12-28 22:34:23 +08:00
3361d48cd4 Merge branch 'springboot3' of https://github.com/zhangdaiscott/jeecg-boot into springboot3 2023-12-28 11:03:26 +08:00
ed86ea3da1 默认不需要nosql支持包 2023-12-28 11:03:10 +08:00
3deb0e5487 Merge pull request #5730 from EightMonth/springboot3
修改自动生成接口文档范围
2023-12-28 10:49:10 +08:00
9e4792941e 修改自动秣接口文档范围 2023-12-28 10:43:58 +08:00
b5fd5fe782 Merge pull request #5716 from EightMonth/springboot3
升级fastjson至2.0.43,替换tomcat为undertow
2023-12-26 17:21:30 +08:00
33c0104a02 增加undertow配置到test\prod 环境 2023-12-26 17:11:54 +08:00
81ed5100af 补充注释 2023-12-26 16:42:24 +08:00
87f9dc0064 去除无意义内容 2023-12-26 15:17:50 +08:00
b311fedc6b 升级fastjson至2.0.43,替换tomcat为undertow 2023-12-26 15:03:35 +08:00
e321a0405f 升级aliyun.oss和minio的依赖 2023-12-26 10:01:57 +08:00
d8bc74794d 仪表盘也支持springboot3 2023-12-21 15:31:12 +08:00
732f05dc74 提供springboot3版本的online依赖支持 2023-12-21 14:57:14 +08:00
6ce92798c6 Merge pull request #5704 from EightMonth/springboot3
升级jeecg 3.6.1版本
2023-12-21 11:46:59 +08:00
f4454e9348 Merge branch 'springboot3' into springboot3 2023-12-21 09:52:14 +08:00
d9134ae0c8 Update WechatVerifyController.java 2023-12-21 09:46:52 +08:00
25180e41c8 更新minidao支持springboot3版本 2023-12-21 09:29:16 +08:00
a99e3f2268 更新积木报表支持springboot3版本 2023-12-21 09:28:00 +08:00
d27c354bf1 修改错误的配置 2023-12-21 09:26:40 +08:00
d818b1dd9d 更新jeecg-boot-starter3依赖 2023-12-21 09:26:39 +08:00
bcdbec0091 更新jeecg-boot-starter3依赖 2023-12-21 09:26:39 +08:00
098bb12b9e 更改jeecg-boot-starter3依赖 2023-12-21 09:26:39 +08:00
4a6c750b19 为注释内容添加注释原因 2023-12-21 09:26:39 +08:00
d396e5304a Update pom.xml 2023-12-21 09:26:38 +08:00
9bed25be8c spring3 2023-12-21 09:26:30 +08:00
7109b42092 Merge pull request #5698 from EightMonth/springboot3
更新积木报表、Minidao支持Springboot3版本
2023-12-20 10:10:51 +08:00
1667b14194 更新minidao支持springboot3版本 2023-12-20 10:00:14 +08:00
e9514873d2 更新积木报表支持springboot3版本 2023-12-19 14:31:17 +08:00
0ee090664e 修改错误的配置 2023-11-13 20:03:53 +08:00
4a9eda4ab0 Merge pull request #5567 from EightMonth/spring3
更新jeecg-boot-starter3依赖
2023-11-13 18:45:02 +08:00
2416c8b251 更新jeecg-boot-starter3依赖 2023-11-13 16:19:22 +08:00
5b056f9dd6 更新jeecg-boot-starter3依赖 2023-11-13 16:12:46 +08:00
a93998dc56 Merge pull request #5566 from EightMonth/spring3
更改jeecg-boot-starter3依赖
2023-11-13 15:43:21 +08:00
268c27a782 更改jeecg-boot-starter3依赖 2023-11-13 15:34:25 +08:00
23ace2712a Merge pull request #5563 from EightMonth/spring3
Spring Boot3 & JDK 17
2023-11-13 09:49:04 +08:00
157feeb925 为注释内容添加注释原因 2023-11-06 14:16:02 +08:00
4e25d4162f Update pom.xml 2023-11-06 14:11:23 +08:00
47a68f31e1 spring3 2023-11-06 12:41:57 +08:00
519 changed files with 6390 additions and 13334 deletions

View File

@ -16,19 +16,6 @@ JeecgBoot平台的AIGC功能模块是一套类似`Dify`的`AIGC应用开发
[![](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/jeecg_aivideo.png)](https://www.bilibili.com/video/BV1zmd7YFE4w)
##### 功能大模块
- AI应用开发平台
- AI知识库系统
- AI大模型管理
- AI流程编排
- AI对话支持图片
- AI对话助手(智能问答)
- AI建表Online表单
- AI写文章CMS
- AI表单字段建议表单设计器
#### Dify `VS` JEECG AI
@ -57,10 +44,10 @@ JeecgBoot平台的AIGC功能模块是一套类似`Dify`的`AIGC应用开发
### 技术文档
### 安装向量库 pgvector
- https://help.jeecg.com/aigc/config
- [AIGC开发文档](https://help.jeecg.com/aigc)
- [安装向量库 pgvector](https://help.jeecg.com/aigc/config)
@ -86,7 +73,7 @@ JeecgBoot平台的AIGC功能模块是一套类似`Dify`的`AIGC应用开发
## 技术交流
- 开发文档https://help.jeecg.com/aigc
- QQ群964611995、716488839(满)
- QQ群716488839
## 功能列表

View File

@ -7,12 +7,12 @@
JEECG BOOT AI Low Code Platform
===============
Current version: 3.8.1 (Release date: 2025-06-30)
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.1-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot)
[![](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)
@ -81,7 +81,7 @@ Technical documentation
- 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 964611995、⑩716488839(满)、⑨808791225(满)
- QQ group ⑩716488839、⑨808791225

View File

@ -1,124 +0,0 @@
JeecgBoot低代码平台(商业版介绍)
===============
项目介绍
-----------------------------------
<h3 align="center">企业级AI低代码平台</h3>
JeecgBoot是一款集成AI应用的基于BPM流程的低代码平台旨在帮助企业快速实现低代码开发和构建个性化AI应用前后端分离架构Ant Design&Vue3SpringBootSpringCloud AlibabaMybatis-plusShiro。强大的代码生成器让前后端代码一键生成无需写任何代码 引领AI低代码开发模式: AI生成->OnlineCoding-> 代码生成-> 手工MERGE 帮助Java项目解决80%的重复工作让开发更多关注业务提高效率、节省成本同时又不失灵活性低代码能力Online表单、表单设计、流程设计、Online报表、大屏/仪表盘设计、报表设计; AI应用平台功能AI知识库问答、AI模型管理、AI流程编排、AI聊天等支持含ChatGPT、DeepSeek、Ollama等多种AI大模型
JeecgBoot 提供了一系列 `低代码能力`,实现`真正的零代码`在线开发Online表单开发、Online报表、复杂报表设计、打印设计、在线图表设计、仪表盘设计、大屏设计、移动图表能力、表单设计器、在线设计流程、流程自动化配置、插件能力可插拔
`AI赋能低代码:` 目前提供了AI应用、AI模型管理、AI流程编排、AI对话助手AI建表、AI写文章、AI知识库问答、AI字段建议等功能;支持各种AI大模型ChatGPT、DeepSeek、Ollama、智普、千问等.
`JEECG宗旨是:` 简单功能由OnlineCoding配置实现做到`零代码开发`复杂功能由代码生成器生成进行手工Merge 实现`低代码开发`,既保证了`智能`又兼顾`灵活`;实现了低代码开发的同时又支持灵活编码,解决了当前低代码产品普遍不灵活的弊端!
`JEECG业务流程:` 采用工作流来实现、扩展出任务接口,供开发编写业务逻辑,表单提供多种解决方案: 表单设计器、online配置表单、编码表单。同时实现了流程与表单的分离设计松耦合、并支持任务节点灵活配置既保证了公司流程的保密性又减少了开发人员的工作量。
#### JeecgBoot商业版与同类产品区别
-----------------------------------
- 灵活性jeecgboot基于开源技术栈设计初考虑到可插拔性和集成灵活性确保平台的智能性与灵活性避免因平台过于庞大而导致的扩展困难。
- 流程管理:支持一个表单挂接多个流程,同时一个流程可以连接多个表单,增强了流程的灵活性和复杂性管理。
- 符合中国国情的流程针对中国市场的特定需求jeecgboot能够实现各种符合中国国情的业务流程。
- 强大的表单设计器jeecgboot的表单设计器与敲敲云共享具备高质量和智能化的特点能够满足零代码应用的需求业内同类产品中不多见。
- 报表功能:自主研发的报表工具,拥有独立知识产权,功能上比业内老牌产品如帆软更智能,操作简便。
- BI产品整合提供大屏、仪表盘、门户等功能完美解决这些需求并支持移动面板的设计与渲染。
- 自主研发的模块jeecgboot的所有模块均为自主研发具有独立的知识产权。
- 颗粒度和功能细致在功能细致度和颗粒度上jeecgboot远超同类产品尤其在零代码能力方面表现突出。
- 零代码应用管理最新版支持与敲敲云的零代码应用管理能力的集成使得jeecgboot既具备低代码又具备零代码的应用能力业内独一无二。
- 强大的代码生成器作为开源代码生成器的先锋jeecgboot在代码生成的智能化和在线低代码与代码生成的结合方面优势明显。
- 精细化权限管理提供行级和列级的数据权限控制满足企业在ERP和OA领域对权限管理的严格需求。
- 多平台支持的APP目前采用uniapp3实现支持小程序、H5、App及鸿蒙、鸿蒙Next、Electron桌面应用等多种终端。
> 综上所述jeecgboot不仅在功能上具备丰富性和灵活性还在技术架构、权限管理和用户体验等方面展现出明显的优势是一个综合性能强大的低代码平台。
商业版演示
-----------------------------------
JeecgBoot vs 敲敲云
> - JeecgBoot是低代码产品拥有系列低代码能力比如流程设计、表单设计、大屏设计代码生成器适合半开发模式开发+低代码结合),也可以集成零代码应用管理模块.
> - 敲敲云是零代码产品完全不写代码通过配置搭建业务系统其在jeecgboot基础上研发而成删除了online、代码生成、OA等需要编码功能只保留应用管理功能和聊天、日程、文件三个OA组件.
- JeecgBoot低代码 https://boot3.jeecg.com
- 敲敲云零代码https://app.qiaoqiaoyun.com
- APP演示(多端): http://jeecg.com/appIndex
### 流程视频介绍
[![](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/flow_video.png)](https://www.bilibili.com/video/BV1Nk4y1o7Qc)
### 商业版功能简述
> 详细的功能介绍,[请联系官方](https://jeecg.com/vip)
```
│─更多商业功能
│ ├─流程设计器
│ ├─简流设计器(类钉钉版)
│ ├─门户设计NEW
│ ├─表单设计器
│ ├─大屏设计器
│ └─我的任务
│ └─历史流程
│ └─历史流程
│ └─流程实例管理
│ └─流程监听管理
│ └─流程表达式
│ └─我发起的流程
│ └─我的抄送
│ └─流程委派、抄送、跳转
│ └─OA办公组件
│ └─零代码应用管理(无需编码,在线搭建应用系统)
│ ├─积木报表企业版含jimureport、jimubi
│ ├─AI流程设计器源码
│ ├─Online全模块功能和源码
│ ├─AI写文章CMS
│ ├─AI表单字段建议表单设计器
│ ├─OA办公协同组件
│ ├─在线聊天功能
│ ├─设计表单移动适配
│ ├─设计表单支持外部填报
│ ├─设计表单AI字段建议
│ ├─设计表单视图功能(支持多种类型含日历、表格、看板、甘特图)
│ └─。。。
```
##### 流程设计
![](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)

395
README.md
View File

@ -2,12 +2,12 @@
JeecgBoot AI低代码平台
===============
当前最新版本: 3.8.1发布日期2025-06-30
当前最新版本: 3.8.0发布日期2025-04-18
[![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.1-brightgreen.svg)](https://github.com/jeecgboot/JeecgBoot)
[![](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)
@ -16,49 +16,66 @@ JeecgBoot AI低代码平台
项目介绍
-----------------------------------
<h3 align="center">企业级AI低代码平台</h3>
<h3 align="center">Java AI Low Code Platform</h3>
JeecgBoot是一款企业级低代码平台集成了AI应用平台功能,旨在帮助开发者快速实现低代码开发和构建、部署个性化的 AI 应用。
前后端分离架构Ant Design4、Vue3SpringBootSpringCloud AlibabaMybatis-plusShiro/SpringAuthorizationServer,强大的代码生成器让前后端代码一键生成,无需写任何代码;提供强大的报表和大屏工具,满足企业级数据产品需求
引领AI低代码开发模式: AI生成->OnlineCoding-> 代码生成-> 手工MERGE 帮助Java项目解决80%的重复工作让开发更多关注业务提高效率、节省成本同时又不失灵活性低代码能力Online表单、表单设计、流程设计、Online报表、大屏/仪表盘设计、报表设计; AI应用平台功能AI知识库问答、AI模型管理、AI流程编排、AI聊天等支持含ChatGPT、DeepSeek、Ollama等多种AI大模型
JeecgBoot是一款基于AIGC和低代码引擎的AI低代码平台,旨在帮助开发者快速实现低代码开发和构建、部署个性化的 AI 应用。
前后端分离架构Ant Design&Vue3SpringBootSpringCloud AlibabaMybatis-plusShiro强大的代码生成器让前后端代码一键生成无需写任何代码
成套AI大模型功能: AI模型管理、AI应用、知识库、AI流程编排、AI对话助手等
引领AI低代码开发模式: AIGC生成->OnlineCoding-> 代码生成-> 手工MERGE 帮助Java项目解决80%的重复工作,让开发更多关注业务,快速提高效率 节省成本,同时又不失灵活性!
`AI赋能低代码:` 提供一套成熟AI应用平台功能包含AI应用管理、AI模型管理、AI对话助手、AI知识库问答、AI流程编排、AI流程设计器AI建表等功能; 支持各种AI大模型ChatGPT、DeepSeek、Ollama、智普、千问等.
JeecgBoot 提供了一系列 `低代码能力`,实现`真正的零代码`在线开发Online表单开发、Online报表、复杂报表设计、打印设计、在线图表设计、仪表盘设计、大屏设计、移动图表能力、表单设计器、在线设计流程、流程自动化配置、插件能力可插拔
`JEECG宗旨是:` 简单功能由OnlineCoding零代码搭建做到`零代码开发`复杂功能由代码生成器生成进行手工Merge 实现`低代码开发`,既保证了`智能`又兼顾`灵活`,解决了当前低代码产品普遍不灵活的弊端!
`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低代码平台可以应用在任何J2EE项目的开发中支持信创国产化。尤其适合SAAS项目、企业信息管理系统MIS、内部办公系统OA、企业资源计划系统ERP、客户关系管理系统CRM、AI知识库其半智能手工Merge的开发方式可以显著提高开发效率70%以上,极大降低开发成本。
JeecgBoot AI低代码平台可以应用在任何J2EE项目的开发中支持信创国产化。尤其适合SAAS项目、企业信息管理系统MIS、内部办公系统OA、企业资源计划系统ERP、客户关系管理系统CRM其半智能手工Merge的开发方式可以显著提高开发效率70%以上,极大降低开发成本。
又是一个全栈式 AI 开发平台,快速帮助企业构建和部署个性化的 AI 应用。
**信创兼容说明**
信创国产化
-----------------------------------
JeecgBoot 是一个开源低代码开发平台,支持全信创环境。它兼容多种国产操作系统和数据库,包括:
- 操作系统:国产麒麟、银河麒麟等国产系统几乎都是基于 Linux 内核,因此它们具有良好的兼容性。
- 数据库达梦、人大金仓、TiDB
- 数据库达梦、人大金仓、TiDB , [转库文档](https://my.oschina.net/jeecg/blog/4905722)
- 中间件:东方通 TongWeb、TongRDS宝兰德 AppServer、CacheDB, [信创配置文档](https://help.jeecg.com/java/tongweb-deploy/)
通过这些适配JeecgBoot 为使用国产软件和硬件的用户提供了高效的开发解决方案。
版本说明
项目说明
-----------------------------------
|下载 | JDK17 + SpringBoot2.7 | JDK17 + SpringBoot3.3 + Shiro |JDK17 + SpringBoot3.3+ SpringAuthorizationServer |
|------|----------------------------------------------------|-----------------------------------------------------------------------------|--------------------------------------------|
| Github | [`master`](https://github.com/jeecgboot/JeecgBoot) | [`springboot3`](https://github.com/jeecgboot/JeecgBoot/tree/springboot3) 分支 | [`springboot3_sas`](https://github.com/jeecgboot/JeecgBoot/tree/springboot3_sas) 分支 |
| Gitee | [`master`](https://gitee.com/jeecg/JeecgBoot) | [`springboot3`](https://gitee.com/jeecg/JeecgBoot/tree/springboot3/) 分支 | [`springboot3_sas`](https://gitee.com/jeecg/JeecgBoot/tree/springboot3_sas) 分支 |
| 项目名 | 说明 |
|--------------------|------------------------|
| `jeecg-boot` | 后端源码JAVASpringBoot微服务架构 |
| `jeecgboot-vue3` | 前端源码VUE3vue3+vite6+ts最新技术栈 |
| `JeecgUniapp` | [配套APP框架](https://github.com/jeecgboot/JeecgUniapp) 适配多个终端支持APP、小程序、H5 |
- `jeecg-boot` 是后端JAVA源码项目支持单体和微服务切换.
- `jeecgboot-vue3` 是前端VUE3源码项目vue3+vite6+ts最新技术栈.
- `JeecgUniapp` 是[配套APP框架](https://github.com/jeecgboot/JeecgUniapp) 适配多个终端支持APP、小程序、H5、鸿蒙、鸿蒙Next.
- 参考 [文档](https://help.jeecg.com/ui/2dev/mini) 可以删除不需要的demo制作一个精简版本
技术文档
-----------------------------------
- 官方网站: [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(满)、其他(满)
@ -70,83 +87,68 @@ JeecgBoot低代码平台可以应用在任何J2EE项目的开发中支持
技术文档
AIGC应用平台介绍
-----------------------------------
- 官方网站: [http://www.jeecg.com](http://www.jeecg.com)
- 入门指南: [快速入门](http://www.jeecg.com/doc/quickstart) | [开发文档](https://help.jeecg.com) | [AI应用使用手册](https://help.jeecg.com/aigc)
- 技术支持: [反馈问题](https://github.com/jeecgboot/JeecgBoot/issues/new?template=bug_report.md) | [视频教程](http://jeecg.com/doc/video) | [低代码体验一分钟](https://jeecg.blog.csdn.net/article/details/106079007)
- QQ交流群 964611995、⑩716488839(满)、⑨808791225(满)、其他(满)
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视频介绍
JeecgBoot平台提供了一套完善的AI应用管理系统模块是一套类似`Dify``AIGC应用开发平台`+`知识库问答`是一款基于LLM大语言模型AI应用平台和 RAG 的知识库问答系统。
其直观的界面结合了 AI 流程编排、RAG 管道、知识库管理、模型管理、对接向量库、实时运行可观察等让您可以快速从原型到生产拥有AI服务能力。 [详细专题介绍,请点击查看](README-AI.md)
[![](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/jeecg_aivideo.png)](https://www.bilibili.com/video/BV1zmd7YFE4w)
为什么选择JeecgBoot?
-----------------------------------
- 1.采用最新主流前后分离框架Spring Boot + MyBatis + Ant Design4 + Vue3容易上手代码生成器依赖性低灵活的扩展能力可快速实现二次开发。
- 2.前端大版本换代,最新版采用 Vue3.0 + TypeScript + Vite6 + Ant Design Vue4 等新技术方案。
- 3.支持微服务Spring Cloud AlibabaNacos、Gateway、Sentinel、Skywalking提供简易机制支持单体和微服务自由切换这样可以满足各类项目需求
- 4.开发效率高支持在线建表和AI建表提供强大代码生成器单表、树列表、一对多、一对一等数据模型增删改查功能一键生成菜单配置直接使用。
- 5.代码生成器提供强大模板机制,支持自定义模板,目前提供四套风格模板(单表两套、树模型一套、一对多三套)。
- 6.提供强大的报表和大屏可视化工具,支持丰富的数据源连接,能够通过拖拉拽方式快速制作报表、大屏和门户设计;支持多种图表类型:柱形图、折线图、散点图、饼图、环形图、面积图、漏斗图、进度图、仪表盘、雷达图、地图等。
- 7.低代码能力在线表单无需编码通过在线配置表单实现表单的增删改查支持单表、树、一对多、一对一等模型实现人人皆可编码在线配置零代码开发、所见即所得支持23种类控件。
- 8.低代码能力:在线报表、在线图表(无需编码,通过在线配置方式,实现数据报表和图形报表,可以快速抽取数据,减轻开发压力,实现人人皆可编码)
- 9.Online支持在线增强开发提供在线代码编辑器支持代码高亮、代码提示等功能支持多种语言Java、SQL、JavaScript等
- 10.封装完善的用户、角色、菜单、组织机构、数据字典、在线定时任务等基础功能,支持访问授权、按钮权限、数据权限等功能
- 11.前端UI提供丰富的组件库支持各种常用组件如表格、树形控件、下拉框、日期选择器等满足各种复杂的业务需求 [UI组件库文档](https://help.jeecg.com/category/ui%E7%BB%84%E4%BB%B6%E5%BA%93)。
- 12.提供APP配套框架一份多代码多终端适配一份代码多终端适配小程序、H5、安卓、iOS、鸿蒙Next。
- 13.新版APP框架采用Uniapp、Vue3.0、Vite、Wot-design-uni、TypeScript等最新技术栈包括二次封装组件、路由拦截、请求拦截等功能。实现了与JeecgBoot完美对接目前已经实现登录、用户信息、通讯录、公告、移动首页、九宫格、聊天、Online表单、仪表盘等功能提供了丰富的组件。
- 14.提供了一套成熟的AI应用平台功能从AI模型、知识库到AI应用搭建助力企业快速落地AI服务加速智能化升级。
- 15.AI能力目前JeecgBoot支持AI大模型chatgpt和deepseek现在最新版默认使用deepseek速度更快质量更高。目前提供了AI对话助手、AI知识库、AI应用、AI建表、AI报表等功能。
- 16.提供新行编辑表格JVXETable轻松满足各种复杂ERP布局拥有更高的性能、更灵活的扩展、更强大的功能。
- 17.平台首页风格,提供多种组合模式,支持自定义风格;支持门户设计,支持自定义首页。
- 18.常用共通封装各种工具类定时任务、短信接口、邮件发送、Excel导入导出等基本满足80%项目需求。
- 19.简易Excel导入导出支持单表导出和一对多表模式导出生成的代码自带导入导出功能。
- 20.集成智能报表工具报表打印、图像报表和数据导出非常方便可极其方便地生成PDF、Excel、Word等报表。
- 21.采用前后分离技术页面UI风格精美针对常用组件做了封装时间、行表格控件、截取显示控件、报表组件、编辑器等。
- 22.查询过滤器查询功能自动生成后台动态拼SQL追加查询条件支持多种匹配方式全匹配/模糊查询/包含查询/不匹配查询)。
- 23.数据权限(精细化数据权限控制,控制到行级、列表级、表单字段级,实现不同人看不同数据,不同人对同一个页面操作不同字段)。
- 24.接口安全机制可细化控制接口授权非常简便实现不同客户端只看自己数据等控制也提供了基于AK和SK认证鉴权的OpenAPI功能。
- 25.活跃的社区支持;近年来,随着网络威胁的日益增加,团队在安全和漏洞管理方面积累了丰富的经验,能够为企业提供全面的安全解决方案。
- 26.权限控制采用RBACRole-Based Access Control基于角色的访问控制
- 27.页面校验自动生成(必须输入、数字校验、金额校验、时间空间等)。
- 28.支持SaaS服务模式提供SaaS多租户架构方案。
- 29.分布式文件服务集成MinIO、阿里OSS等优秀的第三方提供便捷的文件上传与管理同时也支持本地存储。
- 30.主流数据库兼容一套代码完全兼容MySQL、PostgreSQL、Oracle、SQL Server、MariaDB、达梦、人大金仓等主流数据库。
- 31.集成工作流Flowable并实现了只需在页面配置流程转向可极大简化BPM工作流的开发用BPM的流程设计器画出了流程走向一个工作流基本就完成了只需写很少量的Java代码。
- 32.低代码能力在线流程设计采用开源Flowable流程引擎实现在线画流程、自定义表单、表单挂靠、业务流转。
- 33.多数据源:极其简易的使用方式,在线配置数据源配置,便捷地从其他数据抓取数据。
- 34.提供单点登录CAS集成方案项目中已经提供完善的对接代码。
- 35.低代码能力表单设计器支持用户自定义表单布局支持单表、一对多表单支持select、radio、checkbox、textarea、date、popup、列表、宏等控件。
- 36.专业接口对接机制统一采用RESTful接口方式集成Swagger-UI在线接口文档JWT token安全验证方便客户端对接。
- 37.高级组合查询功能,在线配置支持主子表关联查询,可保存查询历史。
- 38.提供各种系统监控实时跟踪系统运行情况监控Redis、Tomcat、JVM、服务器信息、请求追踪、SQL监控
- 39.消息中心支持短信、邮件、微信推送等集成WebSocket消息通知机制。
- 40.支持多语言,提供国际化方案。
- 41.数据变更记录日志,可记录数据每次变更内容,通过版本对比功能查看历史变化。
- 42.提供简单易用的打印插件支持谷歌、火狐、IE11+等各种浏览器。
- 43.后端采用Maven分模块开发方式前端支持菜单动态路由。
- 44.提供丰富的示例代码,涵盖了常用的业务场景,便于学习和参考。
##### 在线体验
- 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应用开发平台
- AI知识库系统
- AI大模型管理
- AI流程编排
- AI对话支持图片
- AI对话助手(智能问答)
- AI建表Online表单
- AI写文章CMS
- AI表单字段建议表单设计器
##### AI大模型支持
| AI大模型 | 支持 |
| --- | --- |
| DeepSeek | √ |
| ChatGTP | √ |
| Qwq | √ |
| 智库 | √ |
| Ollama本地模型 | √ |
| 等等。。 | √ |
技术架构:
-----------------------------------
#### 前端
- 前端环境要求Node.js要求`Node 20+` 版本以上、pnpm 要求`9+` 版本以上
- 依赖管理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插件 )
@ -163,9 +165,28 @@ JeecgBoot平台提供了一套完善的AI应用管理系统模块是一套类
- 日志打印logback
- 缓存Redis
- 其他autopoi, fastjsonpoiSwagger-uiquartz, lombok简化代码等。
- 默认提供MySQL5.7+数据库脚本
- 默认数据库脚本:MySQL5.7+
- [其他数据库,需要自己转](https://my.oschina.net/jeecg/blog/4905722)
#### 数据库支持
#### 前端
- 前端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
#### 前端环境要求
* 本地环境安装 `Node.js 、npm 、pnpm`
* pnpm 要求`9+` 版本以上
* Node.js 版本建议`v20.15.0`,要求`Node 20+` 版本以上
` ( 因为Vite6 需要 Node.js 18 / 20+ )`
#### 平台支持数据库
> jeecgboot平台支持以下数据库默认我们只提供mysql脚本其他数据库可以参考[转库文档](https://my.oschina.net/jeecg/blog/4905722)自己转。
@ -180,17 +201,11 @@ JeecgBoot平台提供了一套完善的AI应用管理系统模块是一套类
| 人大金仓 | √ |
| TiDB | √ |
| kingbase8 | √ |
## 微服务解决方案
> 微服务方式快速启动
> - [单体快速切换微服务](https://help.jeecg.com/java/springcloud/switchcloud/monomer)
> - [Docker一键启动微服务前后端](https://help.jeecg.com/java/docker/quickcloud)
- 1、服务注册和发现 Nacos √
- 2、统一配置中心 Nacos √
@ -209,23 +224,62 @@ JeecgBoot平台提供了一套完善的AI应用管理系统模块是一套类
- 15、CAS 单点登录 √
- 16、路由限流 √
#### 微服务方式启动
- [单体快速切换微服务](https://help.jeecg.com/java/springcloud/switchcloud/monomer)
- [Docker一键启动微服务前后端](https://help.jeecg.com/java/docker/quickcloud)
#### 微服务架构图
![微服务架构图](https://jeecgos.oss-cn-beijing.aliyuncs.com/files/jeecgboot_springcloud2022.png "在这里输入图片标题")
开源版与企业版区别?
为什么选择JeecgBoot?
-----------------------------------
- JeecgBoot开源版采用 [Apache-2.0 license](LICENSE) 开源协议,允许商用使用,不会造成侵权行为,允许基于本平台软件开展业务系统开发(在任何情况下,您不得使用本软件开发可能被认为与本软件竞争的软件).
- 商业版与开源版主要区别在于商业版提供了技术支持 和 更多的企业级功能(例如Online图表、流程监控、流程设计、流程审批、表单设计器、表单视图、积木报表企业版、OA办公、商业APP、零代码应用、Online模块源码等功能). [更多商业功能介绍,点击查看](README-Enterprise.md)
- JeecgBoot未来发展方向是零代码平台的建设也就是团队的另外一款产品 [敲敲云零代码](https://www.qiaoqiaoyun.com) 无需编码即可通过拖拽快速搭建企业级应用与JeecgBoot低代码平台形成互补满足从简单业务到复杂系统的全场景开发需求目前已经上线[欢迎注册体验](https://app.qiaoqiaoyun.com)
* 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.提供仪表盘设计器,类大屏设计支持移动端,免费的数据可视化设计工具,支持丰富的数据源连接,能够通过拖拉拽方式快速制作图表和门户设计;目前支持多种图表类型:柱形图、折线图、散点图、饼图、环形图、面积图、漏斗图、进度图、仪表盘、雷达图、地图等等;
### Jeecg Boot 产品功能蓝图
@ -234,9 +288,42 @@ JeecgBoot平台提供了一套完善的AI应用管理系统模块是一套类
### 分支说明
### 开源版功能清单
> 主干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
├─系统管理
│ ├─用户管理
│ ├─角色管理
@ -252,29 +339,6 @@ JeecgBoot平台提供了一套完善的AI应用管理系统模块是一套类
│ └─通讯录
│ ├─多数据源管理
│ └─多租户管理(租户管理、租户角色、我的租户)
├─Online在线开发(低代码)
│ ├─Online在线表单
│ ├─Online代码生成器
│ ├─Online在线报表
│ ├─仪表盘设计器
│ ├─系统编码规则
│ ├─系统校验规则
├─AI应用平台
│ ├─AI知识库问答系统
│ ├─AI大模型管理
│ ├─AI流程编排
│ ├─AI流程设计器
│ ├─AI对话支持图片
│ ├─AI对话助手(智能问答)
│ ├─AI建表Online表单
│ ├─AI聊天窗口支持嵌入第三方
│ ├─AI聊天窗口支持移动端
│ ├─支持常见大模型ChatGPT和DeepSeek、ollama等等
│ ├─AI OCR示例
├─积木报表设计器
│ ├─打印设计器
│ ├─数据报表设计
│ ├─图形报表设计支持echart
├─消息中心
│ ├─消息管理
│ ├─模板管理
@ -286,9 +350,7 @@ JeecgBoot平台提供了一套完善的AI应用管理系统模块是一套类
│ ├─高级查询器(弹窗自动组合查询条件)
│ ├─Excel导入导出工具集成支持单表一对多 导入导出)
│ ├─平台移动自适应支持
│ ├─提供新版uniapp3的代码生成器模板
├─系统监控
│ ├─基于AK和SK认证鉴权OpenAPI功能
│ ├─Gateway路由网关
│ ├─性能扫描监控
│ │ ├─监控 Redis
@ -368,10 +430,22 @@ JeecgBoot平台提供了一套完善的AI应用管理系统模块是一套类
│ ├─提供单点登录CAS集成方案
│ ├─提供APP发布方案
│ ├─集成Websocket消息通知机制
├─支持electron桌面应用打包(支持windows、linux、macOS三大平台)
│ ├─docker容器支持
│ ├─提供移动APP框架及源码Uniapp3版本支持H5、小程序、APP、鸿蒙Next
│ ├─提供移动APP低代码设计(Online表单、仪表盘)
─更多商业功能
│ ├─流程设计器
│ ├─表单设计器
│ ├─大屏设计器
│ └─我的任务
│ └─历史流程
│ └─历史流程
│ └─流程实例管理
│ └─流程监听管理
│ └─流程表达式
│ └─我发起的流程
│ └─我的抄送
│ └─流程委派、抄送、跳转
│ └─OA办公组件
│ └─。。。
```
@ -379,6 +453,21 @@ JeecgBoot平台提供了一套完善的AI应用管理系统模块是一套类
### 系统效果
##### 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端
@ -400,22 +489,6 @@ JeecgBoot平台提供了一套完善的AI应用管理系统模块是一套类
![](https://oscimg.oschina.net/oscnet/up-16c07e000278329b69b228ae3189814b8e9.png)
##### 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)
##### 仪表盘设计器
@ -482,6 +555,28 @@ AI写文章
![](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)

View File

@ -2,12 +2,12 @@
JeecgBoot 低代码开发平台
===============
当前最新版本: 3.8.1发布日期2025-06-30
当前最新版本: 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.1-brightgreen.svg)](https://github.com/zhangdaiscott/jeecg-boot)
[![](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)
@ -35,7 +35,7 @@ JeecgBoot 是一款基于代码生成器的`低代码开发平台`!前后端
- 官方网站: [http://www.jeecg.com](http://www.jeecg.com)
- 新手指南: [快速入门](http://www.jeecg.com/doc/quickstart)
- QQ交流群 964611995、⑩716488839(满)、⑨808791225(满)、其他(满)
- QQ交流群 ⑩716488839、⑨808791225、其他(满)
- 在线演示 [在线演示](http://boot3.jeecg.com) | [APP演示](http://jeecg.com/appIndex)
> 演示系统的登录账号密码,请点击 [获取账号密码](http://jeecg.com/doc/demo) 获取

File diff suppressed because one or more lines are too long

View File

@ -14,7 +14,7 @@ use `nacos`;
Target Server Version : 50738 (5.7.38)
File Encoding : 65001
Date: 28/05/2025 15:48:34
Date: 22/05/2025 10:58:10
*/
SET NAMES utf8mb4;
@ -49,7 +49,7 @@ CREATE TABLE `config_info` (
-- ----------------------------
-- Records of config_info
-- ----------------------------
INSERT INTO `config_info` VALUES (1, 'jeecg-dev.yaml', 'DEFAULT_GROUP', 'spring:\n datasource:\n druid:\n stat-view-servlet:\n enabled: true\n loginUsername: admin\n loginPassword: 123456\n allow:\n web-stat-filter:\n enabled: true\n dynamic:\n druid:\n initial-size: 5\n min-idle: 5\n maxActive: 20\n maxWait: 60000\n timeBetweenEvictionRunsMillis: 60000\n minEvictableIdleTimeMillis: 300000\n validationQuery: SELECT 1 FROM DUAL\n testWhileIdle: true\n testOnBorrow: false\n testOnReturn: false\n poolPreparedStatements: true\n maxPoolPreparedStatementPerConnectionSize: 20\n filters: stat,wall,slf4j\n wall:\n selectWhereAlwayTrueCheck: false\n stat:\n merge-sql: true\n slow-sql-millis: 5000\n datasource:\n master:\n url: jdbc:mysql://jeecg-boot-mysql:3306/jeecg-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai\n username: root\n password: root\n driver-class-name: com.mysql.cj.jdbc.Driver\n redis:\n database: 0\n host: jeecg-boot-redis\n password:\n port: 6379\n rabbitmq:\n host: jeecg-boot-rabbitmq\n username: guest\n password: guest\n port: 5672\n publisher-confirms: true\n publisher-returns: true\n virtual-host: /\n listener:\n simple:\n acknowledge-mode: manual\n concurrency: 1\n max-concurrency: 1\n retry:\n enabled: true\n flyway:\n enabled: false\n locations: classpath:flyway/sql/mysql\nminidao:\n base-package: org.jeecg.modules.jmreport.*,org.jeecg.modules.drag.*\njeecg:\n firewall:\n dataSourceSafe: false\n lowCodeMode: dev\n signatureSecret: dd05f1c54d63749eda95f9fa6d49v442a\n signUrls: /sys/dict/getDictItems/*,/sys/dict/loadDict/*,/sys/dict/loadDictOrderByValue/*,/sys/dict/loadDictItem/*,/sys/dict/loadTreeData,/sys/api/queryTableDictItemsByCode,/sys/api/queryFilterTableDictInfo,/sys/api/queryTableDictByKeys,/sys/api/translateDictFromTable,/sys/api/translateDictFromTableByKeys,/sys/sendChangePwdSms,/sys/user/sendChangePhoneSms,/sys/sms,/desform/api/sendVerifyCode\n uploadType: local\n domainUrl:\n pc: http://localhost:3100\n app: http://localhost:8051\n path:\n upload: /opt/upFiles\n webapp: /opt/webapp\n shiro:\n excludeUrls: /test/jeecgDemo/demo3,/test/jeecgDemo/redisDemo/**,/category/**,/visual/**,/map/**,/jmreport/bigscreen2/**\n oss:\n endpoint: oss-cn-beijing.aliyuncs.com\n accessKey: ??\n secretKey: ??\n bucketName: jeecgdev\n staticDomain: ??\n file-view-domain: 127.0.0.1:8012\n minio:\n minio_url: http://minio.jeecg.com\n minio_name: ??\n minio_pass: ??\n bucketName: otatest\n jmreport:\n saasMode:\n firewall:\n dataSourceSafe: false\n lowCodeMode: dev\n wps:\n domain: https://wwo.wps.cn/office/\n appid: ??\n appsecret: ??\n xxljob:\n enabled: true\n adminAddresses: http://jeecg-boot-xxljob:9080/xxl-job-admin\n appname: ${spring.application.name}\n accessToken: \'\'\n logPath: logs/jeecg/job/jobhandler/\n logRetentionDays: 30\n redisson:\n address: jeecg-boot-redis:6379\n password:\n type: STANDALONE\n enabled: true\n ai-chat:\n enabled: false\n apiKey: \"\"\n apiHost: \"https://api.openai.com\"\n timeout: 60\n ai-rag:\n embed-store:\n host: 127.0.0.1\n port: 5432\n database: postgres\n user: postgres\n password: postgres\n table: embeddings\nlogging:\n level:\n org.jeecg.modules.system.mapper : info\ncas:\n prefixUrl: http://localhost:8888/cas\nknife4j:\n production: false\n basic:\n enable: false\n username: jeecg\n password: jeecg1314\njustauth:\n enabled: true\n type:\n GITHUB:\n client-id: ??\n client-secret: ??\n redirect-uri: http://sso.test.com:8080/jeecg-boot/thirdLogin/github/callback\n WECHAT_ENTERPRISE:\n client-id: ??\n client-secret: ??\n redirect-uri: http://sso.test.com:8080/jeecg-boot/thirdLogin/wechat_enterprise/callback\n agent-id: ??\n DINGTALK:\n client-id: ??\n client-secret: ??\n redirect-uri: http://sso.test.com:8080/jeecg-boot/thirdLogin/dingtalk/callback\n cache:\n type: default\n prefix: \'demo::\'\n timeout: 1h\nthird-app:\n enabled: false\n type:\n WECHAT_ENTERPRISE:\n enabled: false\n client-id: ??\n client-secret: ??\n agent-id: ??\n DINGTALK:\n enabled: false\n client-id: ??\n client-secret: ??\n agent-id: ??', '68112d529219e88a44245402ccf54676', '2021-03-03 13:01:11', '2025-05-28 07:47:53', NULL, '0:0:0:0:0:0:0:1', '', '', '', '', '', 'yaml', '', '');
INSERT INTO `config_info` VALUES (1, 'jeecg-dev.yaml', 'DEFAULT_GROUP', 'spring:\n datasource:\n druid:\n stat-view-servlet:\n enabled: true\n loginUsername: admin\n loginPassword: 123456\n allow:\n web-stat-filter:\n enabled: true\n dynamic:\n druid:\n initial-size: 5\n min-idle: 5\n maxActive: 20\n maxWait: 60000\n timeBetweenEvictionRunsMillis: 60000\n minEvictableIdleTimeMillis: 300000\n validationQuery: SELECT 1 FROM DUAL\n testWhileIdle: true\n testOnBorrow: false\n testOnReturn: false\n poolPreparedStatements: true\n maxPoolPreparedStatementPerConnectionSize: 20\n filters: stat,wall,slf4j\n wall:\n selectWhereAlwayTrueCheck: false\n stat:\n merge-sql: true\n slow-sql-millis: 5000\n datasource:\n master:\n url: jdbc:mysql://jeecg-boot-mysql:3306/jeecg-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai\n username: root\n password: root\n driver-class-name: com.mysql.cj.jdbc.Driver\n redis:\n database: 0\n host: jeecg-boot-redis\n password:\n port: 6379\n rabbitmq:\n host: jeecg-boot-rabbitmq\n username: guest\n password: guest\n port: 5672\n publisher-confirms: true\n publisher-returns: true\n virtual-host: /\n listener:\n simple:\n acknowledge-mode: manual\n concurrency: 1\n max-concurrency: 1\n retry:\n enabled: true\n flyway:\n enabled: false\n locations: classpath:flyway/sql/mysql\nminidao:\n base-package: org.jeecg.modules.jmreport.*,org.jeecg.modules.drag.*\njeecg:\n firewall:\n dataSourceSafe: false\n lowCodeMode: dev\n signatureSecret: dd05f1c54d63749eda95f9fa6d49v442a\n signUrls: /sys/dict/getDictItems/*,/sys/dict/loadDict/*,/sys/dict/loadDictOrderByValue/*,/sys/dict/loadDictItem/*,/sys/dict/loadTreeData,/sys/api/queryTableDictItemsByCode,/sys/api/queryFilterTableDictInfo,/sys/api/queryTableDictByKeys,/sys/api/translateDictFromTable,/sys/api/translateDictFromTableByKeys,/sys/sendChangePwdSms,/sys/user/sendChangePhoneSms,/sys/sms,/desform/api/sendVerifyCode\n uploadType: local\n domainUrl:\n pc: http://localhost:3100\n app: http://localhost:8051\n path:\n upload: /opt/upFiles\n webapp: /opt/webapp\n shiro:\n excludeUrls: /test/jeecgDemo/demo3,/test/jeecgDemo/redisDemo/**,/category/**,/visual/**,/map/**,/jmreport/bigscreen2/**\n oss:\n endpoint: oss-cn-beijing.aliyuncs.com\n accessKey: ??\n secretKey: ??\n bucketName: jeecgdev\n staticDomain: ??\n minio:\n minio_url: http://minio.jeecg.com\n minio_name: ??\n minio_pass: ??\n bucketName: otatest\n jmreport:\n saasMode:\n firewall:\n dataSourceSafe: false\n lowCodeMode: dev\n wps:\n domain: https://wwo.wps.cn/office/\n appid: ??\n appsecret: ??\n xxljob:\n enabled: true\n adminAddresses: http://jeecg-boot-xxljob:9080/xxl-job-admin\n appname: ${spring.application.name}\n accessToken: \'\'\n logPath: logs/jeecg/job/jobhandler/\n logRetentionDays: 30\n redisson:\n address: jeecg-boot-redis:6379\n password:\n type: STANDALONE\n enabled: true\n ai-chat:\n enabled: false\n apiKey: \"\"\n apiHost: \"https://api.openai.com\"\n timeout: 60\n ai-rag:\n embed-store:\n host: 127.0.0.1\n port: 5432\n database: postgres\n user: postgres\n password: postgres\n table: embeddings\nlogging:\n level:\n org.jeecg.modules.system.mapper : info\ncas:\n prefixUrl: http://localhost:8888/cas\nknife4j:\n production: false\n basic:\n enable: false\n username: jeecg\n password: jeecg1314\njustauth:\n enabled: true\n type:\n GITHUB:\n client-id: ??\n client-secret: ??\n redirect-uri: http://sso.test.com:8080/jeecg-boot/thirdLogin/github/callback\n WECHAT_ENTERPRISE:\n client-id: ??\n client-secret: ??\n redirect-uri: http://sso.test.com:8080/jeecg-boot/thirdLogin/wechat_enterprise/callback\n agent-id: ??\n DINGTALK:\n client-id: ??\n client-secret: ??\n redirect-uri: http://sso.test.com:8080/jeecg-boot/thirdLogin/dingtalk/callback\n cache:\n type: default\n prefix: \'demo::\'\n timeout: 1h\nthird-app:\n enabled: false\n type:\n WECHAT_ENTERPRISE:\n enabled: false\n client-id: ??\n client-secret: ??\n agent-id: ??\n DINGTALK:\n enabled: false\n client-id: ??\n client-secret: ??\n agent-id: ??', 'f1d8102a50c7b1f59458e8f9a0112012', '2021-03-03 13:01:11', '2025-05-19 12:08:56', NULL, '0:0:0:0:0:0:0:1', '', '', '', '', '', 'yaml', '', '');
INSERT INTO `config_info` VALUES (2, 'jeecg.yaml', 'DEFAULT_GROUP', 'server:\n undertow:\n worker-threads: 16\n buffers:\n websocket: 8192\n io: 16384\n error:\n include-exception: true\n include-stacktrace: ALWAYS\n include-message: ALWAYS\n compression:\n enabled: true\n min-response-size: 1024\n mime-types: application/javascript,application/json,application/xml,text/html,text/xml,text/plain,text/css,image/*\nmanagement:\n health:\n mail:\n enabled: false\n endpoints:\n web:\n exposure:\n include: \"*\"\n health:\n sensitive: true\n endpoint:\n health:\n show-details: ALWAYS\nspring:\n servlet:\n multipart:\n max-file-size: 10MB\n max-request-size: 10MB\n mail:\n host: smtp.163.com\n username: jeecgos@163.com\n password: ??\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n quartz:\n job-store-type: jdbc\n initialize-schema: embedded\n auto-startup: false\n startup-delay: 1s\n overwrite-existing-jobs: true\n properties:\n org:\n quartz:\n scheduler:\n instanceName: MyScheduler\n instanceId: AUTO\n jobStore:\n class: org.springframework.scheduling.quartz.LocalDataSourceJobStore\n driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate\n tablePrefix: QRTZ_\n isClustered: true\n misfireThreshold: 12000\n clusterCheckinInterval: 15000\n threadPool:\n class: org.quartz.simpl.SimpleThreadPool\n threadCount: 10\n threadPriority: 5\n threadsInheritContextClassLoaderOfInitializingThread: true\n jackson:\n date-format: yyyy-MM-dd HH:mm:ss\n time-zone: GMT+8\n aop:\n proxy-target-class: true\n activiti:\n check-process-definitions: false\n async-executor-activate: false\n job-executor-activate: false\n jpa:\n open-in-view: false\n freemarker:\n suffix: .ftl\n content-type: text/html\n charset: UTF-8\n cache: false\n prefer-file-system-access: false\n template-loader-path:\n - classpath:/templates\n mvc:\n static-path-pattern: /**\n pathmatch:\n matching-strategy: ant_path_matcher\n resource:\n static-locations: classpath:/static/,classpath:/public/\n autoconfigure:\n exclude:\n - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure\n - org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration\nmybatis-plus:\n mapper-locations: classpath*:org/jeecg/**/xml/*Mapper.xml\n global-config:\n banner: false\n db-config:\n id-type: ASSIGN_ID\n table-underline: true\n configuration:\n call-setters-on-nulls: true', '20596e678c211d4322ead0000c0ffdbc', '2021-03-03 13:01:42', '2025-05-19 09:51:57', NULL, '0:0:0:0:0:0:0:1', '', '', '', '', '', 'yaml', '', '');
INSERT INTO `config_info` VALUES (3, 'jeecg-gateway-router.json', 'DEFAULT_GROUP', '[{\n \"id\": \"jeecg-system\",\n \"order\": 0,\n \"predicates\": [{\n \"name\": \"Path\",\n \"args\": {\n \"_genkey_0\": \"/sys/**\",\n \"_genkey_1\": \"/jmreport/**\",\n \"_genkey_3\": \"/online/**\",\n \"_genkey_4\": \"/generic/**\",\n \"_genkey_5\": \"/drag/**\",\n \"_genkey_6\": \"/actuator/**\",\n \"_genkey_7\": \"/airag/**\",\n \"_genkey_8\": \"/jimubi/**\",\n \"_genkey_9\": \"/openapi/**\"\n }\n }],\n \"filters\": [],\n \"uri\": \"lb://jeecg-system\"\n}, {\n \"id\": \"jeecg-demo\",\n \"order\": 1,\n \"predicates\": [{\n \"name\": \"Path\",\n \"args\": {\n \"_genkey_0\": \"/mock/**\",\n \"_genkey_1\": \"/test/**\",\n \"_genkey_2\": \"/bigscreen/template1/**\",\n \"_genkey_3\": \"/bigscreen/template2/**\"\n }\n }],\n \"filters\": [],\n \"uri\": \"lb://jeecg-demo\"\n}, {\n \"id\": \"jeecg-system-websocket\",\n \"order\": 2,\n \"predicates\": [{\n \"name\": \"Path\",\n \"args\": {\n \"_genkey_0\": \"/websocket/**\",\n \"_genkey_1\": \"/newsWebsocket/**\"\n }\n }],\n \"filters\": [],\n \"uri\": \"lb:ws://jeecg-system\"\n}, {\n \"id\": \"jeecg-demo-websocket\",\n \"order\": 3,\n \"predicates\": [{\n \"name\": \"Path\",\n \"args\": {\n \"_genkey_0\": \"/vxeSocket/**\"\n }\n }],\n \"filters\": [],\n \"uri\": \"lb:ws://jeecg-demo\"\n}]', '856da7f7ff7931c6b1932e89d87b83ba', '2021-03-03 13:02:14', '2025-05-21 05:41:09', NULL, '0:0:0:0:0:0:0:1', '', '', '', '', '', 'json', '', '');
INSERT INTO `config_info` VALUES (11, 'jeecg-sharding.yaml', 'DEFAULT_GROUP', 'spring:\n shardingsphere:\n datasource:\n names: ds0\n ds0:\n driverClassName: com.mysql.cj.jdbc.Driver\n url: jdbc:mysql://jeecg-boot-mysql:3306/jeecg-boot?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai\n username: root\n password: root\n type: com.alibaba.druid.pool.DruidDataSource\n props:\n sql-show: true\n rules:\n sharding:\n binding-tables: sys_log\n key-generators:\n snowflake:\n type: SNOWFLAKE\n props:\n worker-id: 123\n sharding-algorithms:\n table-classbased:\n props:\n strategy: standard\n algorithmClassName: org.jeecg.modules.test.sharding.algorithm.StandardModTableShardAlgorithm\n type: CLASS_BASED\n tables:\n sys_log:\n actual-data-nodes: ds0.sys_log$->{0..1}\n table-strategy:\n standard:\n sharding-algorithm-name: table-classbased\n sharding-column: log_type', 'a93fa455c32cd37ca84631d2bbe13005', '2022-04-13 03:12:28', '2022-08-07 13:13:57', 'nacos', '0:0:0:0:0:0:0:1', '', '', '', '', '', 'yaml', '', NULL);
@ -214,7 +214,7 @@ CREATE TABLE `his_config_info` (
INDEX `idx_gmt_create`(`gmt_create`) USING BTREE,
INDEX `idx_gmt_modified`(`gmt_modified`) USING BTREE,
INDEX `idx_did`(`data_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8 COLLATE = utf8_bin COMMENT = '多租户改造' ROW_FORMAT = DYNAMIC;
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8 COLLATE = utf8_bin COMMENT = '多租户改造' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of his_config_info
@ -224,7 +224,6 @@ INSERT INTO `his_config_info` VALUES (43, 4, 'jeecg-dev.yaml', 'DEFAULT_GROUP',
INSERT INTO `his_config_info` VALUES (2, 5, 'jeecg.yaml', 'DEFAULT_GROUP', '', 'server:\n undertow:\n worker-threads: 16\n buffers:\n websocket: 8192\n io: 16384\n error:\n include-exception: true\n include-stacktrace: ALWAYS\n include-message: ALWAYS\n compression:\n enabled: true\n min-response-size: 1024\n mime-types: application/javascript,application/json,application/xml,text/html,text/xml,text/plain,text/css,image/*\nmanagement:\n health:\n mail:\n enabled: false\n endpoints:\n web:\n exposure:\n include: \"*\"\n health:\n sensitive: true\n endpoint:\n health:\n show-details: ALWAYS\nspringdoc:\n autoTagClasses: false\n packagesToScan: org.jeecg\nspring:\n servlet:\n multipart:\n max-file-size: 10MB\n max-request-size: 10MB\n mail:\n host: smtp.163.com\n username: jeecgos@163.com\n password: ??\n properties:\n mail:\n smtp:\n auth: true\n starttls:\n enable: true\n required: true\n quartz:\n job-store-type: jdbc\n initialize-schema: embedded\n auto-startup: false\n startup-delay: 1s\n overwrite-existing-jobs: true\n properties:\n org:\n quartz:\n scheduler:\n instanceName: MyScheduler\n instanceId: AUTO\n jobStore:\n class: org.springframework.scheduling.quartz.LocalDataSourceJobStore\n driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate\n tablePrefix: QRTZ_\n isClustered: true\n misfireThreshold: 12000\n clusterCheckinInterval: 15000\n threadPool:\n class: org.quartz.simpl.SimpleThreadPool\n threadCount: 10\n threadPriority: 5\n threadsInheritContextClassLoaderOfInitializingThread: true\n jackson:\n date-format: yyyy-MM-dd HH:mm:ss\n time-zone: GMT+8\n aop:\n proxy-target-class: true\n activiti:\n check-process-definitions: false\n async-executor-activate: false\n job-executor-activate: false\n jpa:\n open-in-view: false\n freemarker:\n suffix: .ftl\n content-type: text/html\n charset: UTF-8\n cache: false\n prefer-file-system-access: false\n template-loader-path:\n - classpath:/templates\n mvc:\n static-path-pattern: /**\n pathmatch:\n matching-strategy: ant_path_matcher\n resource:\n static-locations: classpath:/static/,classpath:/public/\n autoconfigure:\n exclude:\n - com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure\n - org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration\nmybatis-plus:\n mapper-locations: classpath*:org/jeecg/**/xml/*Mapper.xml\n global-config:\n banner: false\n db-config:\n id-type: ASSIGN_ID\n table-underline: true\n configuration:\n call-setters-on-nulls: true', '7f00366a5d35b2bb444f95499c0c40a9', '2010-05-05 00:00:00', '2025-05-19 09:51:57', NULL, '0:0:0:0:0:0:0:1', 'U', '', '');
INSERT INTO `his_config_info` VALUES (1, 6, 'jeecg-dev.yaml', 'DEFAULT_GROUP', '', 'spring:\n datasource:\n druid:\n stat-view-servlet:\n enabled: true\n loginUsername: admin\n loginPassword: 123456\n allow:\n web-stat-filter:\n enabled: true\n dynamic:\n druid:\n initial-size: 5\n min-idle: 5\n maxActive: 20\n maxWait: 60000\n timeBetweenEvictionRunsMillis: 60000\n minEvictableIdleTimeMillis: 300000\n validationQuery: SELECT 1 FROM DUAL\n testWhileIdle: true\n testOnBorrow: false\n testOnReturn: false\n poolPreparedStatements: true\n maxPoolPreparedStatementPerConnectionSize: 20\n filters: stat,wall,slf4j\n wall:\n selectWhereAlwayTrueCheck: false\n stat:\n merge-sql: true\n slow-sql-millis: 5000\n datasource:\n master:\n url: jdbc:mysql://jeecg-boot-mysql:3306/jeecg-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai\n username: root\n password: root\n driver-class-name: com.mysql.cj.jdbc.Driver\n redis:\n database: 0\n host: jeecg-boot-redis\n password:\n port: 6379\n rabbitmq:\n host: jeecg-boot-rabbitmq\n username: guest\n password: guest\n port: 5672\n publisher-confirms: true\n publisher-returns: true\n virtual-host: /\n listener:\n simple:\n acknowledge-mode: manual\n concurrency: 1\n max-concurrency: 1\n retry:\n enabled: true\n flyway:\n enabled: false\n locations: classpath:flyway/sql/mysql\nminidao:\n base-package: org.jeecg.modules.jmreport.*,org.jeecg.modules.drag.*\njeecg:\n firewall:\n dataSourceSafe: false\n lowCodeMode: dev\n signatureSecret: dd05f1c54d63749eda95f9fa6d49v442a\n signUrls: /sys/dict/getDictItems/*,/sys/dict/loadDict/*,/sys/dict/loadDictOrderByValue/*,/sys/dict/loadDictItem/*,/sys/dict/loadTreeData,/sys/api/queryTableDictItemsByCode,/sys/api/queryFilterTableDictInfo,/sys/api/queryTableDictByKeys,/sys/api/translateDictFromTable,/sys/api/translateDictFromTableByKeys,/sys/sendChangePwdSms,/sys/user/sendChangePhoneSms,/sys/sms,/desform/api/sendVerifyCode\n uploadType: local\n domainUrl:\n pc: http://localhost:3100\n app: http://localhost:8051\n path:\n upload: /opt/upFiles\n webapp: /opt/webapp\n shiro:\n excludeUrls: /test/jeecgDemo/demo3,/test/jeecgDemo/redisDemo/**,/category/**,/visual/**,/map/**,/jmreport/bigscreen2/**\n oss:\n endpoint: oss-cn-beijing.aliyuncs.com\n accessKey: ??\n secretKey: ??\n bucketName: jeecgdev\n staticDomain: ??\n elasticsearch:\n cluster-name: jeecg-ES\n cluster-nodes: jeecg-boot-es:9200\n check-enabled: false\n file-view-domain: 127.0.0.1:8012\n minio:\n minio_url: http://minio.jeecg.com\n minio_name: ??\n minio_pass: ??\n bucketName: otatest\n jmreport:\n saasMode:\n firewall:\n dataSourceSafe: false\n lowCodeMode: dev\n wps:\n domain: https://wwo.wps.cn/office/\n appid: ??\n appsecret: ??\n xxljob:\n enabled: true\n adminAddresses: http://jeecg-boot-xxljob:9080/xxl-job-admin\n appname: ${spring.application.name}\n accessToken: \'\'\n logPath: logs/jeecg/job/jobhandler/\n logRetentionDays: 30\n redisson:\n address: jeecg-boot-redis:6379\n password:\n type: STANDALONE\n enabled: true\n ai-chat:\n enabled: false\n apiKey: \"\"\n apiHost: \"https://api.openai.com\"\n timeout: 60\nlogging:\n level:\n org.jeecg.modules.system.mapper : info\ncas:\n prefixUrl: http://localhost:8888/cas\nknife4j:\n production: false\n basic:\n enable: false\n username: jeecg\n password: jeecg1314\njustauth:\n enabled: true\n type:\n GITHUB:\n client-id: ??\n client-secret: ??\n redirect-uri: http://sso.test.com:8080/jeecg-boot/thirdLogin/github/callback\n WECHAT_ENTERPRISE:\n client-id: ??\n client-secret: ??\n redirect-uri: http://sso.test.com:8080/jeecg-boot/thirdLogin/wechat_enterprise/callback\n agent-id: ??\n DINGTALK:\n client-id: ??\n client-secret: ??\n redirect-uri: http://sso.test.com:8080/jeecg-boot/thirdLogin/dingtalk/callback\n cache:\n type: default\n prefix: \'demo::\'\n timeout: 1h\nthird-app:\n enabled: false\n type:\n WECHAT_ENTERPRISE:\n enabled: false\n client-id: ??\n client-secret: ??\n agent-id: ??\n DINGTALK:\n enabled: false\n client-id: ??\n client-secret: ??\n agent-id: ??', '057a1634c9b1ebda338515520bd3924a', '2010-05-05 00:00:00', '2025-05-19 12:08:56', NULL, '0:0:0:0:0:0:0:1', 'U', '', '');
INSERT INTO `his_config_info` VALUES (3, 7, 'jeecg-gateway-router.json', 'DEFAULT_GROUP', '', '[{\n \"id\": \"jeecg-system\",\n \"order\": 0,\n \"predicates\": [{\n \"name\": \"Path\",\n \"args\": {\n \"_genkey_0\": \"/sys/**\",\n \"_genkey_1\": \"/jmreport/**\",\n \"_genkey_3\": \"/online/**\",\n \"_genkey_4\": \"/generic/**\",\n \"_genkey_5\": \"/drag/**\",\n \"_genkey_6\": \"/actuator/**\"\n }\n }],\n \"filters\": [],\n \"uri\": \"lb://jeecg-system\"\n}, {\n \"id\": \"jeecg-demo\",\n \"order\": 1,\n \"predicates\": [{\n \"name\": \"Path\",\n \"args\": {\n \"_genkey_0\": \"/mock/**\",\n \"_genkey_1\": \"/test/**\",\n \"_genkey_2\": \"/bigscreen/template1/**\",\n \"_genkey_3\": \"/bigscreen/template2/**\"\n }\n }],\n \"filters\": [],\n \"uri\": \"lb://jeecg-demo\"\n}, {\n \"id\": \"jeecg-system-websocket\",\n \"order\": 2,\n \"predicates\": [{\n \"name\": \"Path\",\n \"args\": {\n \"_genkey_0\": \"/websocket/**\",\n \"_genkey_1\": \"/newsWebsocket/**\"\n }\n }],\n \"filters\": [],\n \"uri\": \"lb:ws://jeecg-system\"\n}, {\n \"id\": \"jeecg-demo-websocket\",\n \"order\": 3,\n \"predicates\": [{\n \"name\": \"Path\",\n \"args\": {\n \"_genkey_0\": \"/vxeSocket/**\"\n }\n }],\n \"filters\": [],\n \"uri\": \"lb:ws://jeecg-demo\"\n}]', '708c0948118bdb96bdfaa87200a14432', '2010-05-05 00:00:00', '2025-05-21 05:41:09', NULL, '0:0:0:0:0:0:0:1', 'U', '', '');
INSERT INTO `his_config_info` VALUES (1, 8, 'jeecg-dev.yaml', 'DEFAULT_GROUP', '', 'spring:\n datasource:\n druid:\n stat-view-servlet:\n enabled: true\n loginUsername: admin\n loginPassword: 123456\n allow:\n web-stat-filter:\n enabled: true\n dynamic:\n druid:\n initial-size: 5\n min-idle: 5\n maxActive: 20\n maxWait: 60000\n timeBetweenEvictionRunsMillis: 60000\n minEvictableIdleTimeMillis: 300000\n validationQuery: SELECT 1 FROM DUAL\n testWhileIdle: true\n testOnBorrow: false\n testOnReturn: false\n poolPreparedStatements: true\n maxPoolPreparedStatementPerConnectionSize: 20\n filters: stat,wall,slf4j\n wall:\n selectWhereAlwayTrueCheck: false\n stat:\n merge-sql: true\n slow-sql-millis: 5000\n datasource:\n master:\n url: jdbc:mysql://jeecg-boot-mysql:3306/jeecg-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai\n username: root\n password: root\n driver-class-name: com.mysql.cj.jdbc.Driver\n redis:\n database: 0\n host: jeecg-boot-redis\n password:\n port: 6379\n rabbitmq:\n host: jeecg-boot-rabbitmq\n username: guest\n password: guest\n port: 5672\n publisher-confirms: true\n publisher-returns: true\n virtual-host: /\n listener:\n simple:\n acknowledge-mode: manual\n concurrency: 1\n max-concurrency: 1\n retry:\n enabled: true\n flyway:\n enabled: false\n locations: classpath:flyway/sql/mysql\nminidao:\n base-package: org.jeecg.modules.jmreport.*,org.jeecg.modules.drag.*\njeecg:\n firewall:\n dataSourceSafe: false\n lowCodeMode: dev\n signatureSecret: dd05f1c54d63749eda95f9fa6d49v442a\n signUrls: /sys/dict/getDictItems/*,/sys/dict/loadDict/*,/sys/dict/loadDictOrderByValue/*,/sys/dict/loadDictItem/*,/sys/dict/loadTreeData,/sys/api/queryTableDictItemsByCode,/sys/api/queryFilterTableDictInfo,/sys/api/queryTableDictByKeys,/sys/api/translateDictFromTable,/sys/api/translateDictFromTableByKeys,/sys/sendChangePwdSms,/sys/user/sendChangePhoneSms,/sys/sms,/desform/api/sendVerifyCode\n uploadType: local\n domainUrl:\n pc: http://localhost:3100\n app: http://localhost:8051\n path:\n upload: /opt/upFiles\n webapp: /opt/webapp\n shiro:\n excludeUrls: /test/jeecgDemo/demo3,/test/jeecgDemo/redisDemo/**,/category/**,/visual/**,/map/**,/jmreport/bigscreen2/**\n oss:\n endpoint: oss-cn-beijing.aliyuncs.com\n accessKey: ??\n secretKey: ??\n bucketName: jeecgdev\n staticDomain: ??\n minio:\n minio_url: http://minio.jeecg.com\n minio_name: ??\n minio_pass: ??\n bucketName: otatest\n jmreport:\n saasMode:\n firewall:\n dataSourceSafe: false\n lowCodeMode: dev\n wps:\n domain: https://wwo.wps.cn/office/\n appid: ??\n appsecret: ??\n xxljob:\n enabled: true\n adminAddresses: http://jeecg-boot-xxljob:9080/xxl-job-admin\n appname: ${spring.application.name}\n accessToken: \'\'\n logPath: logs/jeecg/job/jobhandler/\n logRetentionDays: 30\n redisson:\n address: jeecg-boot-redis:6379\n password:\n type: STANDALONE\n enabled: true\n ai-chat:\n enabled: false\n apiKey: \"\"\n apiHost: \"https://api.openai.com\"\n timeout: 60\n ai-rag:\n embed-store:\n host: 127.0.0.1\n port: 5432\n database: postgres\n user: postgres\n password: postgres\n table: embeddings\nlogging:\n level:\n org.jeecg.modules.system.mapper : info\ncas:\n prefixUrl: http://localhost:8888/cas\nknife4j:\n production: false\n basic:\n enable: false\n username: jeecg\n password: jeecg1314\njustauth:\n enabled: true\n type:\n GITHUB:\n client-id: ??\n client-secret: ??\n redirect-uri: http://sso.test.com:8080/jeecg-boot/thirdLogin/github/callback\n WECHAT_ENTERPRISE:\n client-id: ??\n client-secret: ??\n redirect-uri: http://sso.test.com:8080/jeecg-boot/thirdLogin/wechat_enterprise/callback\n agent-id: ??\n DINGTALK:\n client-id: ??\n client-secret: ??\n redirect-uri: http://sso.test.com:8080/jeecg-boot/thirdLogin/dingtalk/callback\n cache:\n type: default\n prefix: \'demo::\'\n timeout: 1h\nthird-app:\n enabled: false\n type:\n WECHAT_ENTERPRISE:\n enabled: false\n client-id: ??\n client-secret: ??\n agent-id: ??\n DINGTALK:\n enabled: false\n client-id: ??\n client-secret: ??\n agent-id: ??', 'f1d8102a50c7b1f59458e8f9a0112012', '2010-05-05 00:00:00', '2025-05-28 07:47:53', NULL, '0:0:0:0:0:0:0:1', 'U', '', '');
-- ----------------------------
-- Table structure for permissions

View File

@ -22,22 +22,22 @@ SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
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
`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;
-- ----------------------------
@ -50,16 +50,16 @@ INSERT INTO `open_api` VALUES ('1922132683346649090', '根据部门查询用户'
-- ----------------------------
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
`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;
-- ----------------------------
@ -72,13 +72,13 @@ INSERT INTO `open_api_auth` VALUES ('1922164194775056386', 'scott', 'ak-pFjyNHWR
-- ----------------------------
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
`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;
-- ----------------------------
@ -117,14 +117,14 @@ INSERT INTO `open_api_log` VALUES ('1922836856287428610', '1922132683346649090',
-- ----------------------------
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
`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;
-- ----------------------------
@ -165,4 +165,6 @@ INSERT INTO sys_role_permission (id, role_id, permission_id, data_rule_ids, oper
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 ('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

@ -3,6 +3,7 @@
> JeecgBoot属于平台级产品每次升级改动较大目前做不到平滑升级。
### 增量升级方案
#### 1.代码合并
本地通过svn或git做好主干在分支上做业务开发jeecg每次版本发布可以手工覆盖主干的代码对比合并代码
@ -11,5 +12,12 @@
- 其他库请手工执行SQL, 目录: `jeecg-module-system\jeecg-system-start\src\main\resources\flyway\sql\mysql`
> 注意: 升级sql只提供mysql版本如果有权限升级, 还需要手工角色授权,退出重新登录才好使。
#### 3.兼容问题
#### 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

@ -43,7 +43,7 @@
<!--jeecg-tools-->
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-common</artifactId>
<artifactId>jeecg-boot-common3</artifactId>
</dependency>
<!--集成springmvc框架并实现自动配置 -->
<dependency>
@ -108,26 +108,32 @@
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-jsqlparser-4.9</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- minidao -->
<dependency>
<groupId>org.jeecgframework</groupId>
<artifactId>minidao-spring-boot-starter</artifactId>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-jsqlparser-4.9</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<artifactId>druid-spring-boot-3-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- 动态数据源 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
<version>${dynamic-datasource-spring-boot-starter.version}</version>
</dependency>
@ -196,7 +202,50 @@
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<classifier>jakarta</classifier>
<version>${shiro.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<classifier>jakarta</classifier>
<version>${shiro.version}</version>
<!-- 排除仍使用了javax.servlet的依赖 -->
<exclusions>
<exclusion>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 引入适配jakarta的依赖包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<classifier>jakarta</classifier>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<classifier>jakarta</classifier>
<version>${shiro.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- shiro-redis -->
<dependency>
@ -215,12 +264,23 @@
</exclusions>
</dependency>
<!-- knife4j -->
<!-- <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-spring-boot-starter</artifactId>
<artifactId>knife4j-openapi3-ui</artifactId>
<version>${knife4j-spring-boot-starter.version}</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.7.0</version>
</dependency>
<!-- 代码生成器 -->
<!-- 如下载失败,请参考此文档 https://help.jeecg.com/java/setup/maven.html -->
@ -242,7 +302,7 @@
<!-- AutoPoi Excel工具类-->
<dependency>
<groupId>org.jeecgframework</groupId>
<groupId>org.jeecgframework.boot3</groupId>
<artifactId>autopoi-web</artifactId>
<version>${autopoi-web.version}</version>
<exclusions>
@ -291,6 +351,16 @@
<dependency>
<groupId>com.xkcoding.justauth</groupId>
<artifactId>justauth-spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
@ -317,7 +387,12 @@
<!-- chatgpt -->
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-starter-chatgpt</artifactId>
<artifactId>jeecg-boot-starter3-chatgpt</artifactId>
</dependency>
<!-- minidao -->
<dependency>
<groupId>org.jeecgframework.boot3</groupId>
<artifactId>minidao-spring-boot-starter-jsqlparser-4.9</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -2,7 +2,7 @@ package org.jeecg.common.api.dto;
import lombok.Data;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import java.io.Serializable;
/**

View File

@ -1,7 +1,6 @@
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

@ -20,14 +20,14 @@ import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.util.IpUtils;
import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.StandardReflectionParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import jakarta.annotation.Resource;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
@ -172,7 +172,7 @@ public class AutoLogAspect {
// 请求的方法参数值
Object[] args = joinPoint.getArgs();
// 请求的方法参数名称
LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
StandardReflectionParameterNameDiscoverer u=new StandardReflectionParameterNameDiscoverer();
String[] paramNames = u.getParameterNames(method);
if (args != null && paramNames != null) {
for (int i = 0; i < args.length; i++) {

View File

@ -21,7 +21,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.List;

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

@ -1,7 +1,8 @@
package org.jeecg.common.exception;
import cn.hutool.core.util.ObjectUtil;
import io.undertow.server.RequestTooBigException;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.shiro.SecurityUtils;
@ -31,11 +32,8 @@ 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 javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.stream.Collectors;
@ -58,7 +56,7 @@ public class JeecgBootExceptionHandler {
addSysLog(e);
return Result.error("校验失败!" + e.getBindingResult().getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.joining(",")));
}
/**
* 处理自定义异常
*/
@ -168,27 +166,6 @@ 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 && cause.getCause() instanceof RequestTooBigException) {
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);
@ -244,16 +221,11 @@ public class JeecgBootExceptionHandler {
} catch (NullPointerException | BeansException ignored) {
}
if (null != request) {
//update-begin---author:chenrui ---date:20250408 for[QQYUN-11716]上传大图片失败没有精确提示------------
//请求的参数
if (!isTooBigException(e)) {
// 文件上传过大异常时不能获取参数,否则会报错
Map<String, String[]> parameterMap = request.getParameterMap();
if(!CollectionUtils.isEmpty(parameterMap)) {
log.setMethod(oConvertUtils.mapToString(request.getParameterMap()));
}
Map<String, String[]> parameterMap = request.getParameterMap();
if(!CollectionUtils.isEmpty(parameterMap)){
log.setMethod(oConvertUtils.mapToString(request.getParameterMap()));
}
//update-end---author:chenrui ---date:20250408 for[QQYUN-11716]上传大图片失败没有精确提示------------
// 请求地址
log.setRequestUrl(request.getRequestURI());
//设置IP地址
@ -279,26 +251,4 @@ public class JeecgBootExceptionHandler {
}
//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 && cause.getCause() instanceof RequestTooBigException){
isTooBigException = true;
}
}
if(e instanceof MaxUploadSizeExceededException){
isTooBigException = true;
}
return isTooBigException;
}
}

View File

@ -23,9 +23,9 @@ import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

View File

@ -2,6 +2,7 @@ package org.jeecg.common.system.base.entity;
import java.io.Serializable;
import io.swagger.v3.oas.annotations.media.Schema;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.springframework.format.annotation.DateTimeFormat;
@ -9,10 +10,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

@ -5,7 +5,7 @@ import org.jeecg.common.system.vo.SysUserCacheInfo;
import org.jeecg.common.util.SpringContextUtils;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;

View File

@ -6,16 +6,18 @@ import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Joiner;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Date;
import java.util.Objects;
import java.util.stream.Collectors;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;

View File

@ -5,7 +5,7 @@ import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
/**
*

View File

@ -19,7 +19,7 @@ import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import javax.sql.DataSource;
import java.io.ByteArrayInputStream;
import java.io.File;

View File

@ -1,10 +1,10 @@
package org.jeecg.common.util;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.jeecg.common.exception.JeecgBootException;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;

View File

@ -4,13 +4,13 @@ import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.constant.SymbolConstant;
import org.jeecg.common.handler.IFillRuleHandler;
import org.jeecg.common.system.query.QueryGenerator;
import javax.servlet.http.HttpServletRequest;
/**

View File

@ -1,6 +1,6 @@
package org.jeecg.common.util;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.constant.CommonConstant;

View File

@ -1,7 +1,7 @@
package org.jeecg.common.util;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.ServiceNameConstants;

View File

@ -16,13 +16,7 @@ import java.util.regex.Pattern;
* @author zhoujf
*/
@Slf4j
public class SqlInjectionUtil {
/**
* sql注入黑名单数据库名
*/
public final static String XSS_STR_TABLE = "peformance_schema|information_schema";
public class SqlInjectionUtil {
/**
* 默认—sql注入关键词
*/
@ -173,28 +167,7 @@ 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注入过滤处理遇到注入关键字抛异常
*
@ -235,14 +208,6 @@ 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) {
@ -279,14 +244,6 @@ 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) {

View File

@ -11,7 +11,7 @@ import org.jeecg.common.exception.JeecgBoot401Exception;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
/**
* @Author scott

View File

@ -68,13 +68,6 @@ 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

@ -1,7 +1,6 @@
package org.jeecg.common.util.encryption;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.lang.codec.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

View File

@ -7,10 +7,9 @@ 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 javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
@ -1134,14 +1133,5 @@ public class oConvertUtils {
public static <T> boolean isIn(T obj, T... objs) {
return isIn(obj, objs);
}
/**
* 判断租户ID是否有效
* @param tenantId
* @return
*/
public static boolean isEffectiveTenant(String tenantId) {
return MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL && isNotEmpty(tenantId) && !("0").equals(tenantId);
}
}

View File

@ -3,8 +3,6 @@ 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;
@ -68,8 +66,6 @@ public abstract class AbstractQueryBlackListHandler {
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);
@ -85,16 +81,6 @@ 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;
}
}
}
// 返回黑名单校验结果(不合法直接抛出异常)
@ -149,8 +135,6 @@ public abstract class AbstractQueryBlackListHandler {
* 查询的表的信息
*/
protected class QueryTable {
//数据库名
private String dbName;
//表名
private String name;
//表的别名
@ -174,14 +158,6 @@ 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,7 +3,7 @@ package org.jeecg.config;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Resource;
import jakarta.annotation.Resource;
import org.jeecg.common.api.CommonAPI;
import org.jeecg.common.system.vo.DictModel;

View File

@ -2,7 +2,9 @@ package org.jeecg.config;
import java.io.IOException;
import javax.servlet.*;
import com.alibaba.druid.spring.boot3.autoconfigure.DruidDataSourceAutoConfigure;
import com.alibaba.druid.spring.boot3.autoconfigure.properties.DruidStatProperties;
import jakarta.servlet.*;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ -11,8 +13,6 @@ import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure;
import com.alibaba.druid.spring.boot.autoconfigure.properties.DruidStatProperties;
import com.alibaba.druid.util.Utils;
/**

View File

@ -12,6 +12,7 @@ import java.util.HashMap;
import java.util.Map;
/**
* @author eightmonth@qq.com
* 启动程序修改DruidWallConfig配置
* 允许SELECT语句的WHERE子句是一个永真条件
* @author eightmonth

View File

@ -1,7 +1,10 @@
package org.jeecg.config;
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;
@ -11,6 +14,7 @@ import org.springframework.stereotype.Component;
*/
@Component("jeecgBaseConfig")
@ConfigurationProperties(prefix = "jeecg")
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class JeecgBaseConfig {
/**
* 签名密钥串(字典等敏感接口)

View File

@ -1,9 +1,8 @@
//package org.jeecg.config;
//
//
//import io.swagger.v3.oas.annotations.Operation;
//import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
//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;
@ -19,13 +18,15 @@
//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.EnableSwagger2WebMvc;
//import springfox.documentation.swagger2.annotations.EnableSwagger2;
//
//import java.lang.reflect.Field;
//import java.util.ArrayList;
@ -37,7 +38,8 @@
// * @Author scott
// */
//@Configuration
//@EnableSwagger2WebMvc
//@EnableSwagger2 //开启 Swagger2
//@EnableKnife4j //开启 knife4j可以不写
//@Import(BeanValidatorPluginsConfiguration.class)
//public class Swagger2Config implements WebMvcConfigurer {
//
@ -95,14 +97,6 @@
// List<Parameter> pars = new ArrayList<>();
// tokenPar.name(CommonConstant.X_ACCESS_TOKEN).description("token").modelRef(new ModelRef("string")).parameterType("header").required(false).build();
// pars.add(tokenPar.build());
// //update-begin-author:liusq---date:2024-08-15--for: 开启多租户时全局参数增加租户id
// if(MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL){
// ParameterBuilder tenantPar = new ParameterBuilder();
// tenantPar.name(CommonConstant.TENANT_ID).description("租户ID").modelRef(new ModelRef("string")).parameterType("header").required(false).build();
// pars.add(tenantPar.build());
// }
// //update-end-author:liusq---date:2024-08-15--for: 开启多租户时全局参数增加租户id
//
// return pars;
// }
//
@ -157,7 +151,7 @@
//
// @Override
// public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// if (bean instanceof WebMvcRequestHandlerProvider) {
// if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
// customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
// }
// return bean;

View File

@ -90,7 +90,7 @@ public class Swagger3Config implements WebMvcConfigurer {
return new OpenAPI()
.info(new Info()
.title("JeecgBoot 后台服务API接口文档")
.version("3.8.1")
.version("3.8.0")
.contact(new Contact().name("北京国炬信息技术有限公司").url("www.jeccg.com").email("jeecgos@163.com"))
.description( "后台API接口")
.termsOfService("NO terms of service")

View File

@ -0,0 +1,19 @@
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

@ -10,19 +10,23 @@ import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
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.prometheus.PrometheusMeterRegistry;
import io.micrometer.prometheusmetrics.PrometheusMeterRegistry;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectProvider;
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.context.event.ApplicationReadyEvent;
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.context.event.EventListener;
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;
@ -32,7 +36,6 @@ import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
@ -58,14 +61,6 @@ 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、图片、视频、音频等
*/
@ -154,17 +149,12 @@ public class WebMvcConfiguration implements WebMvcConfigurer {
/**
* 监听应用启动完成事件,确保 PrometheusMeterRegistry 已经初始化
* for [QQYUN-12558]【监控】系统监控的头两个tab不好使接口404
* @param event
* @author chenrui
* @date 2025/5/26 16:46
* 解决metrics端点不显示jvm信息的问题(zyf)
*/
@EventListener
public void onApplicationReady(ApplicationReadyEvent event) {
if(null != meterRegistryPostProcessor){
meterRegistryPostProcessor.postProcessAfterInitialization(prometheusMeterRegistry, "");
}
@Bean
@ConditionalOnBean(name = "meterRegistryPostProcessor")
InitializingBean forcePrometheusPostProcessor(BeanPostProcessor meterRegistryPostProcessor) {
return () -> meterRegistryPostProcessor.postProcessAfterInitialization(prometheusMeterRegistry, "");
}
// /**

View File

@ -3,8 +3,8 @@ package org.jeecg.config.filter;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.config.sign.util.BodyReaderHttpServletRequestWrapper;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
/**

View File

@ -7,9 +7,9 @@ import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.TokenUtils;
import org.jeecg.common.util.oConvertUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
/**

View File

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

View File

@ -12,12 +12,11 @@ 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 javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Set;
@ -39,7 +38,6 @@ import java.util.Set;
* @date 20230904
*/
@Slf4j
@Component
public class LowCodeModeInterceptor implements HandlerInterceptor {
/**
* 低代码开发模式
@ -49,8 +47,7 @@ public class LowCodeModeInterceptor implements HandlerInterceptor {
@Resource
private JeecgBaseConfig jeecgBaseConfig;
@Autowired(required = false)
@Autowired
private CommonAPI commonAPI;
/**
@ -58,15 +55,10 @@ public class LowCodeModeInterceptor implements HandlerInterceptor {
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
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

@ -6,6 +6,8 @@ import java.util.List;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
import org.jeecg.common.config.TenantContext;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.TenantConstant;
@ -21,8 +23,6 @@ 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 net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.LongValue;
/**
* 单数据源配置jeecg.datasource.open = false时生效

View File

@ -11,7 +11,7 @@ import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.config.mybatis.ThreadLocalDataHelper;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
/**

View File

@ -6,8 +6,8 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* 动态数据源切换拦截器

View File

@ -1,5 +1,6 @@
package org.jeecg.config.oss;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.constant.SymbolConstant;
@ -26,7 +27,7 @@ public class MinioConfig {
@Value(value = "${jeecg.minio.bucketName}")
private String bucketName;
@Bean
@PostConstruct
public void initMinio(){
if(!minioUrl.startsWith(CommonConstant.STR_HTTP)){
minioUrl = "http://" + minioUrl;

View File

@ -1,5 +1,6 @@
package org.jeecg.config.oss;
import jakarta.annotation.PostConstruct;
import org.jeecg.common.util.oss.OssBootUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ -26,7 +27,7 @@ public class OssConfiguration {
private String staticDomain;
@Bean
@PostConstruct
public void initOssBootConfiguration() {
OssBootUtil.setEndPoint(endpoint);
OssBootUtil.setAccessKeyId(accessKeyId);

View File

@ -1,5 +1,8 @@
package org.jeecg.config.shiro;
import jakarta.annotation.Resource;
import jakarta.servlet.DispatcherType;
import jakarta.servlet.Filter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
@ -8,6 +11,7 @@ import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.ShiroUrlPathHelper;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.crazycake.shiro.*;
import org.jeecg.common.constant.CommonConstant;
@ -17,25 +21,20 @@ 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 javax.annotation.Resource;
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import java.lang.reflect.Method;
import java.util.*;
/**
@ -46,6 +45,7 @@ import java.util.*;
@Slf4j
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ShiroConfig {
@Resource
@ -221,7 +221,6 @@ public class ShiroConfig {
registration.addUrlPatterns("/airag/flow/debug");
registration.addUrlPatterns("/airag/chat/send");
registration.addUrlPatterns("/airag/app/debug");
registration.addUrlPatterns("/airag/app/prompt/generate");
//支持异步
registration.setAsyncSupported(true);
registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC);
@ -321,7 +320,7 @@ public class ShiroConfig {
return sentinelManager;
}
// redis 单机支持,在集群为空,或者集群无机器时候使用 add by jzyadmin@163.com
if (lettuceConnectionFactory.getClusterConfiguration() == null || lettuceConnectionFactory.getClusterConfiguration().getClusterNodes().isEmpty()) {
RedisManager redisManager = new RedisManager();
@ -354,6 +353,18 @@ public class ShiroConfig {
return manager;
}
/**
* 解决 ShiroRequestMappingConfig 获取 requestMappingHandlerMapping Bean 冲突
* spring-boot-autoconfigure:3.4.5 和 spring-boot-actuator-autoconfigure:3.4.5
*/
@Primary
@Bean
public RequestMappingHandlerMapping overridedRequestMappingHandlerMapping() {
RequestMappingHandlerMapping mapping = new RequestMappingHandlerMapping();
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

@ -20,11 +20,13 @@ import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.TokenUtils;
import org.jeecg.common.util.oConvertUtils;
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Role;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Set;
/**
@ -35,6 +37,7 @@ import java.util.Set;
*/
@Component
@Slf4j
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ShiroRealm extends AuthorizingRealm {
@Lazy
@Resource

View File

@ -12,7 +12,7 @@ import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.apache.shiro.mgt.SecurityManager;
import org.springframework.beans.factory.BeanInitializationException;
import javax.servlet.Filter;
import jakarta.servlet.Filter;
import java.util.Map;
/**

View File

@ -13,10 +13,10 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* @Description: 鉴权登录拦截器

View File

@ -1,9 +1,9 @@
package org.jeecg.config.shiro.filters;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import lombok.extern.slf4j.Slf4j;

View File

@ -10,7 +10,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.annotation.Resource;
import jakarta.annotation.Resource;
/**
* 签名 拦截器配置

View File

@ -4,8 +4,8 @@ package org.jeecg.config.sign.interceptor;
import java.io.PrintWriter;
import java.util.SortedMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;

View File

@ -1,10 +1,10 @@
package org.jeecg.config.sign.util;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import jakarta.servlet.ReadListener;
import jakarta.servlet.ServletInputStream;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import java.io.*;
import java.nio.charset.Charset;

View File

@ -10,7 +10,7 @@ import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.constant.SymbolConstant;

View File

@ -14,8 +14,8 @@ import org.jeecg.common.util.SpringContextUtils;
import org.jeecg.common.util.oConvertUtils;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import java.util.*;
/**

View File

@ -1,3 +1,2 @@
springdoc.auto-tag-classes: false
springdoc.packages-to-scan: org.jeecg
springdoc.default-flat-param-object: true
springdoc.packages-to-scan: org.jeecg

View File

@ -53,29 +53,19 @@
<artifactId>jeecg-system-cloud-api</artifactId>
</dependency>-->
<!-- aiflow依赖 -->
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<groupId>org.jeecgframework.boot3</groupId>
<artifactId>jeecg-aiflow</artifactId>
<version>1.1.1</version>
</dependency>
<!-- beigin 这两个依赖太多每个包50M左右如果你发布需要使用请把<scope>provided</scope>删掉 -->
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-scripting-jsr223</artifactId>
<version>${kotlin.version}</version>
<scope>provided</scope>
<version>1.0.5</version>
</dependency>
<!-- aiflow 脚本依赖 -->
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-script-graaljs</artifactId>
<version>${liteflow.version}</version>
<scope>provided</scope>
<scope>runtime</scope>
</dependency>
<!-- end 这两个依赖太多每个包50M左右如果你发布需要使用请把<scope>provided</scope>删掉 -->
<!-- aiflow 脚本依赖 -->
<dependency>
<groupId>com.yomahub</groupId>
<artifactId>liteflow-script-groovy</artifactId>

View File

@ -16,10 +16,6 @@ public class AiAppConsts {
* 状态:禁用
*/
public static final String STATUS_DISABLE = "disable";
/**
* 状态:发布
*/
public static final String STATUS_RELEASE = "release";
/**

View File

@ -4,13 +4,10 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.util.AssertUtils;
import org.jeecg.common.util.TokenUtils;
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
import org.jeecg.config.shiro.IgnoreAuth;
import org.jeecg.modules.airag.app.consts.AiAppConsts;
import org.jeecg.modules.airag.app.entity.AiragApp;
@ -21,7 +18,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Arrays;
/**
* @Description: AI应用
@ -66,7 +64,6 @@ public class AiragAppController extends JeecgController<AiragApp, IAiragAppServi
* @return
*/
@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
@RequiresPermissions("airag:app:edit")
public Result<String> edit(@RequestBody AiragApp airagApp) {
AssertUtils.assertNotEmpty("参数异常", airagApp);
AssertUtils.assertNotEmpty("请输入应用名称", airagApp.getName());
@ -76,28 +73,6 @@ public class AiragAppController extends JeecgController<AiragApp, IAiragAppServi
return Result.OK("保存完成!", airagApp.getId());
}
/**
* 发布应用
*
* @return
*/
@RequestMapping(value = "/release", method = RequestMethod.POST)
public Result<String> release(@RequestParam(name = "id") String id, @RequestParam(name = "release") Boolean release) {
AssertUtils.assertNotEmpty("id必须填写", id);
if (release == null) {
release = true;
}
AiragApp airagApp = new AiragApp();
airagApp.setId(id);
if (release) {
airagApp.setStatus(AiAppConsts.STATUS_RELEASE);
} else {
airagApp.setStatus(AiAppConsts.STATUS_ENABLE);
}
airagAppService.updateById(airagApp);
return Result.OK(release ? "发布成功" : "取消发布成功");
}
/**
* 通过id删除
*
@ -105,23 +80,23 @@ public class AiragAppController extends JeecgController<AiragApp, IAiragAppServi
* @return
*/
@DeleteMapping(value = "/delete")
@RequiresPermissions("airag:app:delete")
public Result<String> delete(HttpServletRequest request,@RequestParam(name = "id", required = true) String id) {
//update-begin---author:chenrui ---date:20250606 for[issues/8337]关于ai工作列表的数据权限问题 #8337------------
//如果是saas隔离的情况下判断当前租户id是否是当前租户下的
if (MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL) {
AiragApp app = airagAppService.getById(id);
//获取当前租户
String currentTenantId = TokenUtils.getTenantIdByRequest(request);
if (null == app || !app.getTenantId().equals(currentTenantId)) {
return Result.error("删除AI应用失败不能删除其他租户的AI应用");
}
}
//update-end---author:chenrui ---date:20250606 for[issues/8337]关于ai工作列表的数据权限问题 #8337------------
public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
airagAppService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
this.airagAppService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*

View File

@ -2,22 +2,14 @@ package org.jeecg.modules.airag.app.controller;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.util.CommonUtils;
import org.jeecg.config.shiro.IgnoreAuth;
import org.jeecg.modules.airag.app.service.IAiragChatService;
import org.jeecg.modules.airag.app.vo.ChatConversation;
import org.jeecg.modules.airag.app.vo.ChatSendParams;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* airag应用-chat
@ -33,15 +25,6 @@ public class AiragChatController {
@Autowired
IAiragChatService chatService;
@Value(value = "${jeecg.path.upload}")
private String uploadpath;
/**
* 本地local miniominio 阿里alioss
*/
@Value(value="${jeecg.uploadType}")
private String uploadType;
/**
* 发送消息
@ -76,19 +59,6 @@ public class AiragChatController {
return chatService.send(chatSendParams);
}
/**
* 获取所有对话
*
* @return 返回一个Result对象包含所有对话的信息
* @author chenrui
* @date 2025/2/25 11:42
*/
@IgnoreAuth
@GetMapping(value = "/init")
public Result<?> initChat(@RequestParam(name = "id", required = true) String id) {
return chatService.initChat(id);
}
/**
* 获取所有对话
*
@ -171,36 +141,4 @@ public class AiragChatController {
return chatService.stop(requestId);
}
/**
* 上传文件
* for [QQYUN-12135]AI聊天上传图片提示非法token
*
* @param request
* @param response
* @return
* @throws Exception
* @author chenrui
* @date 2025/4/25 11:04
*/
@IgnoreAuth
@PostMapping(value = "/upload")
public Result<?> upload(HttpServletRequest request, HttpServletResponse response) throws Exception {
String bizPath = "airag";
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
// 获取上传文件对象
MultipartFile file = multipartRequest.getFile("file");
String savePath;
if (CommonConstant.UPLOAD_TYPE_LOCAL.equals(uploadType)) {
savePath = CommonUtils.uploadLocal(file, bizPath, uploadpath);
} else {
savePath = CommonUtils.upload(file, bizPath, uploadType);
}
Result<?> result = new Result<>();
result.setMessage(savePath);
result.setSuccess(true);
return result;
}
}

View File

@ -39,13 +39,12 @@ public class AiragApp implements Serializable {
*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
private String id;
/**
* 创建人
*/
@Schema(description = "创建人")
@Dict(dictTable = "sys_user",dicCode = "username",dicText = "realname")
private java.lang.String createBy;
private String createBy;
/**
* 创建日期
*/
@ -57,7 +56,7 @@ public class AiragApp implements Serializable {
* 更新人
*/
@Schema(description = "更新人")
private java.lang.String updateBy;
private String updateBy;
/**
* 更新日期
*/
@ -69,95 +68,95 @@ public class AiragApp implements Serializable {
* 所属部门
*/
@Schema(description = "所属部门")
private java.lang.String sysOrgCode;
private String sysOrgCode;
/**
* 租户id
*/
@Excel(name = "租户id", width = 15)
@Schema(description = "租户id")
private java.lang.String tenantId;
private String tenantId;
/**
* 应用名称
*/
@Excel(name = "应用名称", width = 15)
@Schema(description = "应用名称")
private java.lang.String name;
private String name;
/**
* 应用描述
*/
@Excel(name = "应用描述", width = 15)
@Schema(description = "应用描述")
private java.lang.String descr;
private String descr;
/**
* 应用图标
*/
@Excel(name = "应用图标", width = 15)
@Schema(description = "应用图标")
private java.lang.String icon;
private String icon;
/**
* 应用类型
*/
@Excel(name = "应用类型", width = 15, dicCode = "ai_app_type")
@Dict(dicCode = "ai_app_type")
@Schema(description = "应用类型")
private java.lang.String type;
private String type;
/**
* 开场白
*/
@Excel(name = "开场白", width = 15)
@Schema(description = "开场白")
private java.lang.String prologue;
private String prologue;
/**
* 预设问题
*/
@Excel(name = "预设问题", width = 15)
@Schema(description = "预设问题")
private java.lang.String presetQuestion;
private String presetQuestion;
/**
* 提示词
*/
@Excel(name = "提示词", width = 15)
@Schema(description = "提示词")
private java.lang.String prompt;
private String prompt;
/**
* 模型配置
*/
@Excel(name = "模型配置", width = 15, dictTable = "airag_model where model_type = 'LLM' ", dicText = "name", dicCode = "id")
@Dict(dictTable = "airag_model where model_type = 'LLM' ", dicText = "name", dicCode = "id")
@Schema(description = "模型配置")
private java.lang.String modelId;
private String modelId;
/**
* 历史消息数
*/
@Excel(name = "历史消息数", width = 15)
@Schema(description = "历史消息数")
private java.lang.Integer msgNum;
private Integer msgNum;
/**
* 知识库
*/
@Excel(name = "知识库", width = 15, dictTable = "airag_knowledge where status = 'enable'", dicText = "name", dicCode = "id")
@Dict(dictTable = "airag_knowledge where status = 'enable'", dicText = "name", dicCode = "id")
@Schema(description = "知识库")
private java.lang.String knowledgeIds;
private String knowledgeIds;
/**
* 流程
*/
@Excel(name = "流程", width = 15, dictTable = "airag_flow where status = 'enable' ", dicText = "name", dicCode = "id")
@Dict(dictTable = "airag_flow where status = 'enable' ", dicText = "name", dicCode = "id")
@Schema(description = "流程")
private java.lang.String flowId;
private String flowId;
/**
* 快捷指令
*/
@Excel(name = "快捷指令", width = 15)
@Schema(description = "快捷指令")
private java.lang.String quickCommand;
private String quickCommand;
/**
* 状态enable=启用、disable=禁用、release=发布)
* 状态
*/
@Excel(name = "状态", width = 15)
@Schema(description = "状态")
private java.lang.String status;
private String status;
/**
@ -165,7 +164,7 @@ public class AiragApp implements Serializable {
*/
@Excel(name = "元数据", width = 15)
@Schema(description = "元数据")
private java.lang.String metadata;
private String metadata;
/**
* 知识库ids

View File

@ -1,6 +1,5 @@
package org.jeecg.modules.airag.app.mapper;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.jeecg.modules.airag.app.entity.AiragApp;
@ -12,14 +11,4 @@ import org.jeecg.modules.airag.app.entity.AiragApp;
*/
public interface AiragAppMapper extends BaseMapper<AiragApp> {
/**
* 根据ID查询app信息(忽略租户)
* @param id
* @return
* @author chenrui
* @date 2025/4/21 16:03
*/
@InterceptorIgnore(tenantLine = "true")
AiragApp getByIdIgnoreTenant(String id);
}

View File

@ -2,8 +2,4 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.jeecg.modules.airag.app.mapper.AiragAppMapper">
<select id="getByIdIgnoreTenant" resultType="org.jeecg.modules.airag.app.entity.AiragApp">
SELECT * FROM airag_app WHERE id = #{id}
</select>
</mapper>

View File

@ -92,14 +92,4 @@ public interface IAiragChatService {
* @date 2025/3/3 19:49
*/
Result<?> clearMessage(String conversationId);
/**
* 初始化聊天(忽略租户)
* [QQYUN-12113]分享之后的聊天,应用、模型、知识库不根据租户查询
* @param appId
* @return
* @author chenrui
* @date 2025/4/21 14:17
*/
Result<?> initChat(String appId);
}

View File

@ -13,7 +13,6 @@ import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.util.*;
import org.jeecg.modules.airag.app.consts.AiAppConsts;
import org.jeecg.modules.airag.app.entity.AiragApp;
import org.jeecg.modules.airag.app.mapper.AiragAppMapper;
import org.jeecg.modules.airag.app.service.IAiragAppService;
import org.jeecg.modules.airag.app.service.IAiragChatService;
import org.jeecg.modules.airag.app.vo.AppDebugParams;
@ -38,7 +37,7 @@ import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
@ -64,7 +63,7 @@ public class AiragChatServiceImpl implements IAiragChatService {
RedisTemplate redisTemplate;
@Autowired
AiragAppMapper airagAppMapper;
IAiragAppService airagAppService;
@Autowired
IAiragFlowService airagFlowService;
@ -86,7 +85,7 @@ public class AiragChatServiceImpl implements IAiragChatService {
// 获取app信息
AiragApp app = null;
if (oConvertUtils.isNotEmpty(chatSendParams.getAppId())) {
app = airagAppMapper.getByIdIgnoreTenant(chatSendParams.getAppId());
app = airagAppService.getById(chatSendParams.getAppId());
}
ChatConversation chatConversation = getOrCreateChatConversation(app, conversationId);
// 更新标题
@ -147,19 +146,13 @@ public class AiragChatServiceImpl implements IAiragChatService {
try {
// 发送完成事件
emitter.send(SseEmitter.event().data(eventData));
} catch (Exception e) {
} catch (IOException e) {
log.error("终止会话时发生错误", e);
try {
// 防止异常冒泡
emitter.completeWithError(e);
} catch (Exception ignore) {}
} finally {
// 从缓存中移除emitter
AiragLocalCache.remove(AiragConsts.CACHE_TYPE_SSE, eventData.getRequestId());
// 关闭emitter
try {
emitter.complete();
} catch (Exception ignore) {}
emitter.complete();
}
}
@ -244,12 +237,6 @@ public class AiragChatServiceImpl implements IAiragChatService {
return Result.ok();
}
@Override
public Result<?> initChat(String appId) {
AiragApp app = airagAppMapper.getByIdIgnoreTenant(appId);
return Result.ok(app);
}
@Override
public Result<?> deleteConversation(String conversationId) {
AssertUtils.assertNotEmpty("请选择要删除的会话", conversationId);
@ -291,7 +278,7 @@ public class AiragChatServiceImpl implements IAiragChatService {
* @author chenrui
* @date 2025/2/25 19:27
*/
private String getConversationCacheKey(String conversationId, HttpServletRequest httpRequest) {
private String getConversationCacheKey(String conversationId,HttpServletRequest httpRequest) {
if (oConvertUtils.isEmpty(conversationId)) {
return null;
}
@ -389,7 +376,7 @@ public class AiragChatServiceImpl implements IAiragChatService {
* @author chenrui
* @date 2025/2/25 19:27
*/
private void saveChatConversation(ChatConversation chatConversation, boolean temp, HttpServletRequest httpRequest) {
private void saveChatConversation(ChatConversation chatConversation, boolean temp,HttpServletRequest httpRequest) {
if (null == chatConversation) {
return;
}
@ -427,7 +414,8 @@ public class AiragChatServiceImpl implements IAiragChatService {
case AiragConsts.MESSAGE_ROLE_USER:
List<Content> contents = new ArrayList<>();
List<MessageHistory.ImageHistory> images = history.getImages();
if (oConvertUtils.isObjectNotEmpty(images) && !images.isEmpty()) {
if (oConvertUtils.isObjectNotEmpty(images)
&& !images.isEmpty()) {
contents.addAll(images.stream().map(imageHistory -> {
if (oConvertUtils.isNotEmpty(imageHistory.getUrl())) {
return ImageContent.from(imageHistory.getUrl());
@ -464,7 +452,8 @@ public class AiragChatServiceImpl implements IAiragChatService {
* @author chenrui
* @date 2025/2/25 19:05
*/
private void appendMessage(List<ChatMessage> messages, ChatMessage message, ChatConversation chatConversation, String topicId) {
private void appendMessage(List<ChatMessage> messages, ChatMessage message, ChatConversation
chatConversation, String topicId) {
if (message.type().equals(ChatMessageType.SYSTEM)) {
// 系统消息,放到消息列表最前面,并且不记录历史
@ -478,7 +467,11 @@ public class AiragChatServiceImpl implements IAiragChatService {
histories = new ArrayList<>();
}
// 消息记录
MessageHistory historyMessage = MessageHistory.builder().conversationId(chatConversation.getId()).topicId(topicId).datetime(DateUtils.now()).build();
MessageHistory historyMessage = MessageHistory.builder()
.conversationId(chatConversation.getId())
.topicId(topicId)
.datetime(DateUtils.now())
.build();
if (message.type().equals(ChatMessageType.USER)) {
historyMessage.setRole(AiragConsts.MESSAGE_ROLE_USER);
StringBuilder textContent = new StringBuilder();
@ -523,21 +516,8 @@ public class AiragChatServiceImpl implements IAiragChatService {
// 每次会话都生成一个新的,用来缓存emitter
String requestId = UUIDGenerator.generate();
SseEmitter emitter = new SseEmitter(-0L);
emitter.onError(throwable -> {
log.warn("SEE向客户端发送消息失败: {}", throwable.getMessage());
AiragLocalCache.remove(AiragConsts.CACHE_TYPE_SSE, requestId);
try {
emitter.complete();
} catch (Exception ignore) {}
});
EventData eventRequestId = new EventData(requestId, null, EventData.EVENT_INIT_REQUEST_ID, chatConversation.getId(), topicId);
eventRequestId.setData(EventMessageData.builder().message("").build());
sendMessage2Client(emitter, eventRequestId);
// 缓存emitter
AiragLocalCache.put(AiragConsts.CACHE_TYPE_SSE, requestId, emitter);
// 缓存开始发送时间
log.info("[AI-CHAT]开始发送消息,requestId:{}", requestId);
AiragLocalCache.put(AiragConsts.CACHE_TYPE_SSE_SEND_TIME, requestId, System.currentTimeMillis());
try {
// 组装用户消息
UserMessage userMessage = aiChatHandler.buildUserMessage(sendParams.getContent(), sendParams.getImages());
@ -609,51 +589,36 @@ public class AiragChatServiceImpl implements IAiragChatService {
SseEmitter emitter = AiragLocalCache.get(AiragConsts.CACHE_TYPE_SSE, requestId);
flowRunParams.setEventCallback(eventData -> {
if (EventData.EVENT_FLOW_FINISHED.equals(eventData.getEvent())) {
// 打印耗时日志
printChatDuration(requestId, "流程执行完毕");
// 已经执行完了,删除时间缓存
AiragLocalCache.remove(AiragConsts.CACHE_TYPE_SSE_SEND_TIME, requestId);
EventFlowData data = (EventFlowData) eventData.getData();
if(data.isSuccess()) {
Object outputs = data.getOutputs();
if (oConvertUtils.isObjectNotEmpty(outputs)) {
AiMessage aiMessage;
if (outputs instanceof String) {
// 兼容推理模型
String messageText = String.valueOf(outputs);
messageText = messageText.replaceAll("<think>([\\s\\S]*?)</think>", "> $1");
aiMessage = new AiMessage(messageText);
} else {
aiMessage = new AiMessage(JSONObject.toJSONString(outputs));
}
EventData msgEventData = new EventData(requestId, null, EventData.EVENT_MESSAGE, chatConversation.getId(), topicId);
EventMessageData messageEventData = EventMessageData.builder().message(aiMessage.text()).build();
msgEventData.setData(messageEventData);
msgEventData.setRequestId(requestId);
sendMessage2Client(emitter, msgEventData);
appendMessage(messages, aiMessage, chatConversation, topicId);
// 保存会话
saveChatConversation(chatConversation, false, httpRequest);
Object outputs = data.getOutputs();
if (oConvertUtils.isObjectNotEmpty(outputs)) {
AiMessage aiMessage;
if (outputs instanceof String) {
// 兼容推理模型
String messageText = String.valueOf(outputs);
messageText = messageText.replaceAll("<think>([\\s\\S]*?)</think>", "> $1");
aiMessage = new AiMessage(messageText);
} else {
aiMessage = new AiMessage(JSONObject.toJSONString(outputs));
}
}else{
//update-begin---author:chenrui ---date:20250425 for[QQYUN-12203]AI 聊天,超时或者服务器报错,给个友好提示------------
// 失败
String message = data.getMessage();
if (message != null && message.contains(FlowConsts.FLOW_ERROR_MSG_LLM_TIMEOUT)) {
message = "当前用户较多,排队中,请稍后再试!";
EventData errEventData = new EventData(requestId, null, EventData.EVENT_MESSAGE, chatConversation.getId(), topicId);
errEventData.setData(EventMessageData.builder().message("\n" + message).build());
sendMessage2Client(emitter, errEventData);
errEventData = new EventData(requestId, null, EventData.EVENT_MESSAGE_END, chatConversation.getId(), topicId);
// 如果是超时,主动关闭SSE,防止流程切面中返回异常消息导致前端不能正常展示上面的{普通消息}.
closeSSE(emitter, errEventData);
EventData msgEventData = new EventData(requestId, null, EventData.EVENT_MESSAGE, chatConversation.getId(), topicId);
EventMessageData messageEventData = EventMessageData.builder()
.message(aiMessage.text())
.build();
msgEventData.setData(messageEventData);
try {
String eventStr = JSONObject.toJSONString(msgEventData);
log.debug("[AI应用]接收FLOW返回消息:{}", eventStr);
emitter.send(SseEmitter.event().data(eventStr));
} catch (IOException e) {
throw new RuntimeException(e);
}
//update-end---author:chenrui ---date:20250425 for[QQYUN-12203]AI 聊天,超时或者服务器报错,给个友好提示------------
appendMessage(messages, aiMessage, chatConversation, topicId);
// 保存会话
saveChatConversation(chatConversation, false, httpRequest);
}
}
});
// 打印流程耗时日志
printChatDuration(requestId, "开始执行流程");
airagFlowService.runFlow(flowRunParams);
}
@ -684,26 +649,24 @@ public class AiragChatServiceImpl implements IAiragChatService {
String metadataStr = aiApp.getMetadata();
if (oConvertUtils.isNotEmpty(metadataStr)) {
JSONObject metadata = JSONObject.parseObject(metadataStr);
if (oConvertUtils.isNotEmpty(metadata)) {
if(oConvertUtils.isNotEmpty(metadata)){
if (metadata.containsKey("temperature")) {
aiChatParams.setTemperature(metadata.getDouble("temperature"));
}
if (metadata.containsKey("topP")) {
aiChatParams.setTopP(metadata.getDouble("topP"));
aiChatParams.setTopP(metadata.getDouble("temperature"));
}
if (metadata.containsKey("presencePenalty")) {
aiChatParams.setPresencePenalty(metadata.getDouble("presencePenalty"));
aiChatParams.setPresencePenalty(metadata.getDouble("temperature"));
}
if (metadata.containsKey("frequencyPenalty")) {
aiChatParams.setFrequencyPenalty(metadata.getDouble("frequencyPenalty"));
aiChatParams.setFrequencyPenalty(metadata.getDouble("temperature"));
}
if (metadata.containsKey("maxTokens")) {
aiChatParams.setMaxTokens(metadata.getInteger("maxTokens"));
aiChatParams.setMaxTokens(metadata.getInteger("temperature"));
}
}
}
// 打印流程耗时日志
printChatDuration(requestId, "构造应用自定义参数完成");
// 发消息
sendWithDefault(requestId, chatConversation, topicId, modelId, messages, aiChatParams);
}
@ -720,9 +683,10 @@ public class AiragChatServiceImpl implements IAiragChatService {
* @author chenrui
* @date 2025/2/25 19:24
*/
private void sendWithDefault(String requestId, ChatConversation chatConversation, String topicId, String modelId, List<ChatMessage> messages, AIChatParams aiChatParams) {
private void sendWithDefault(String requestId, ChatConversation chatConversation, String topicId, String modelId,
List<ChatMessage> messages,AIChatParams aiChatParams) {
// 调用ai聊天
if (null == aiChatParams) {
if(null == aiChatParams){
aiChatParams = new AIChatParams();
}
aiChatParams.setKnowIds(chatConversation.getApp().getKnowIds());
@ -730,15 +694,13 @@ public class AiragChatServiceImpl implements IAiragChatService {
HttpServletRequest httpRequest = SpringContextUtils.getHttpServletRequest();
TokenStream chatStream;
try {
// 打印流程耗时日志
printChatDuration(requestId, "开始向LLM发送消息");
if (oConvertUtils.isNotEmpty(modelId)) {
chatStream = aiChatHandler.chat(modelId, messages, aiChatParams);
} else {
chatStream = aiChatHandler.chatByDefaultModel(messages, aiChatParams);
}
} catch (Exception e) {
log.error(e.getMessage(), e);
log.error(e.getMessage(),e);
throw new JeecgBootBizTipException("调用大模型接口失败:" + e.getMessage());
}
/**
@ -747,120 +709,91 @@ public class AiragChatServiceImpl implements IAiragChatService {
AtomicBoolean isThinking = new AtomicBoolean(false);
// ai聊天响应逻辑
chatStream.onNext((String resMessage) -> {
// 兼容推理模型
if ("<think>".equals(resMessage)) {
isThinking.set(true);
resMessage = "> ";
}
if ("</think>".equals(resMessage)) {
isThinking.set(false);
resMessage = "\n\n";
}
if (isThinking.get()) {
if (null != resMessage && resMessage.contains("\n")) {
resMessage = "\n> ";
}
}
EventData eventData = new EventData(requestId, null, EventData.EVENT_MESSAGE, chatConversation.getId(), topicId);
EventMessageData messageEventData = EventMessageData.builder().message(resMessage).build();
eventData.setData(messageEventData);
eventData.setRequestId(requestId);
// sse
SseEmitter emitter = AiragLocalCache.get(AiragConsts.CACHE_TYPE_SSE, requestId);
if (null == emitter) {
log.warn("[AI应用]接收LLM返回会话已关闭");
return;
}
sendMessage2Client(emitter, eventData);
}).onComplete((responseMessage) -> {
// 打印流程耗时日志
printChatDuration(requestId, "LLM输出消息完成");
AiragLocalCache.remove(AiragConsts.CACHE_TYPE_SSE_SEND_TIME, requestId);
// 记录ai的回复
AiMessage aiMessage = responseMessage.content();
FinishReason finishReason = responseMessage.finishReason();
String respText = aiMessage.text();
// sse
SseEmitter emitter = AiragLocalCache.get(AiragConsts.CACHE_TYPE_SSE, requestId);
if (null == emitter) {
log.warn("[AI应用]接收LLM返回会话已关闭");
return;
}
if (FinishReason.STOP.equals(finishReason) || null == finishReason) {
// 正常结束
EventData eventData = new EventData(requestId, null, EventData.EVENT_MESSAGE_END, chatConversation.getId(), topicId);
appendMessage(messages, aiMessage, chatConversation, topicId);
// 保存会话
saveChatConversation(chatConversation, false, httpRequest);
closeSSE(emitter, eventData);
} else if (FinishReason.TOOL_EXECUTION.equals(finishReason)) {
// 需要执行工具
// TODO author: chenrui for: date:2025/3/7
} else if (FinishReason.LENGTH.equals(finishReason)) {
// 上下文长度超过限制
log.error("调用模型异常:上下文长度超过限制:{}", responseMessage.tokenUsage());
EventData eventData = new EventData(requestId, null, EventData.EVENT_MESSAGE, chatConversation.getId(), topicId);
eventData.setData(EventMessageData.builder().message("\n上下文长度超过限制请调整模型最大Tokens").build());
sendMessage2Client(emitter, eventData);
eventData = new EventData(requestId, null, EventData.EVENT_MESSAGE_END, chatConversation.getId(), topicId);
closeSSE(emitter, eventData);
} else {
// 异常结束
log.error("调用模型异常:" + respText);
if (respText.contains("insufficient Balance")) {
respText = "大预言模型账号余额不足!";
}
EventData eventData = new EventData(requestId, null, EventData.EVENT_FLOW_ERROR, chatConversation.getId(), topicId);
eventData.setData(EventFlowData.builder().success(false).message(respText).build());
closeSSE(emitter, eventData);
}
}).onError((Throwable error) -> {
// 打印流程耗时日志
printChatDuration(requestId, "LLM输出消息异常");
AiragLocalCache.remove(AiragConsts.CACHE_TYPE_SSE_SEND_TIME, requestId);
// sse
SseEmitter emitter = AiragLocalCache.get(AiragConsts.CACHE_TYPE_SSE, requestId);
if (null == emitter) {
log.warn("[AI应用]接收LLM返回会话已关闭{}", requestId);
return;
}
log.error(error.getMessage(), error);
String errMsg = error.getMessage();
if (errMsg != null && errMsg.contains("timeout")) {
//update-begin---author:chenrui ---date:20250425 for[QQYUN-12203]AI 聊天,超时或者服务器报错,给个友好提示------------
errMsg = "当前用户较多,排队中,请稍后再试!";
EventData eventData = new EventData(requestId, null, EventData.EVENT_MESSAGE, chatConversation.getId(), topicId);
eventData.setData(EventMessageData.builder().message("\n" + errMsg).build());
sendMessage2Client(emitter, eventData);
eventData = new EventData(requestId, null, EventData.EVENT_MESSAGE_END, chatConversation.getId(), topicId);
closeSSE(emitter, eventData);
//update-end---author:chenrui ---date:20250425 for[QQYUN-12203]AI 聊天,超时或者服务器报错,给个友好提示------------
} else {
errMsg = "调用大模型接口失败:" + errMsg;
EventData eventData = new EventData(requestId, null, EventData.EVENT_FLOW_ERROR, chatConversation.getId(), topicId);
eventData.setData(EventFlowData.builder().success(false).message(errMsg).build());
closeSSE(emitter, eventData);
}
}).start();
}
/**
* 发送消息到客户端
*
* @param emitter
* @param eventData
* @author chenrui
* @date 2025/4/22 19:58
*/
private static void sendMessage2Client(SseEmitter emitter, EventData eventData) {
try {
log.info("发送消息:{}", eventData.getRequestId());
String eventStr = JSONObject.toJSONString(eventData);
log.debug("[AI应用]接收LLM返回消息:{}", eventStr);
emitter.send(SseEmitter.event().data(eventStr));
} catch (IOException e) {
log.error("发送消息失败", e);
}
// 兼容推理模型
if ("<think>".equals(resMessage)) {
isThinking.set(true);
resMessage = "> ";
}
if ("</think>".equals(resMessage)) {
isThinking.set(false);
resMessage = "\n\n";
}
if (isThinking.get()) {
if (null != resMessage && resMessage.contains("\n")) {
resMessage = "\n> ";
}
}
EventData eventData = new EventData(requestId, null, EventData.EVENT_MESSAGE, chatConversation.getId(), topicId);
EventMessageData messageEventData = EventMessageData.builder()
.message(resMessage)
.build();
eventData.setData(messageEventData);
// sse
SseEmitter emitter = AiragLocalCache.get(AiragConsts.CACHE_TYPE_SSE, requestId);
if (null == emitter) {
log.warn("[AI应用]接收LLM返回会话已关闭");
return;
}
try {
String eventStr = JSONObject.toJSONString(eventData);
log.debug("[AI应用]接收LLM返回消息:{}", eventStr);
emitter.send(SseEmitter.event().data(eventStr));
} catch (IOException e) {
throw new RuntimeException(e);
}
})
.onComplete((responseMessage) -> {
// 记录ai的回复
AiMessage aiMessage = responseMessage.content();
FinishReason finishReason = responseMessage.finishReason();
String respText = aiMessage.text();
// sse
SseEmitter emitter = AiragLocalCache.get(AiragConsts.CACHE_TYPE_SSE, requestId);
if (null == emitter) {
log.warn("[AI应用]接收LLM返回会话已关闭");
return;
}
if (FinishReason.STOP.equals(finishReason) || null == finishReason) {
// 正常结束
EventData eventData = new EventData(requestId, null, EventData.EVENT_MESSAGE_END, chatConversation.getId(), topicId);
try {
log.debug("[AI应用]接收LLM返回消息完成:{}", respText);
emitter.send(SseEmitter.event().data(eventData));
} catch (IOException e) {
throw new RuntimeException(e);
}
appendMessage(messages, aiMessage, chatConversation, topicId);
// 保存会话
saveChatConversation(chatConversation,false,httpRequest);
closeSSE(emitter, eventData);
} else if (FinishReason.TOOL_EXECUTION.equals(finishReason)) {
// 需要执行工具
// TODO author: chenrui for: date:2025/3/7
} else {
// 异常结束
log.error("调用模型异常:" + respText);
if (respText.contains("insufficient Balance")) {
respText = "大预言模型账号余额不足!";
}
EventData eventData = new EventData(requestId, null, EventData.EVENT_FLOW_ERROR, chatConversation.getId(), topicId);
eventData.setData(EventFlowData.builder().success(false).message(respText).build());
closeSSE(emitter, eventData);
}
})
.onError((Throwable error) -> {
// sse
SseEmitter emitter = AiragLocalCache.get(AiragConsts.CACHE_TYPE_SSE, requestId);
if (null == emitter) {
log.warn("[AI应用]接收LLM返回会话已关闭");
return;
}
String errMsg = "调用大模型接口失败:" + error.getMessage();
log.error(errMsg, error);
EventData eventData = new EventData(requestId, null, EventData.EVENT_FLOW_ERROR, chatConversation.getId(), topicId);
eventData.setData(EventFlowData.builder().success(false).message(errMsg).build());
closeSSE(emitter, eventData);
})
.start();
}
/**
@ -904,7 +837,12 @@ public class AiragChatServiceImpl implements IAiragChatService {
}
CompletableFuture.runAsync(() -> {
List<ChatMessage> messages = new LinkedList<>();
String systemMsgStr = "根据用户的问题,总结会话标题.\n" + "要求如下:\n" + "1. 使用中文回答.\n" + "2. 标题长度控制在5个汉字10个英文字符以内\n" + "3. 直接回复会话标题,不要有其他任何无关描述\n" + "4. 如果无法总结,回复不知道\n";
String systemMsgStr = "根据用户的问题,总结会话标题.\n" +
"要求如下:\n" +
"1. 使用中文回答.\n" +
"2. 标题长度控制在5个汉字10个英文字符以内\n" +
"3. 直接回复会话标题,不要有其他任何无关描述\n" +
"4. 如果无法总结,回复不知道\n";
messages.add(new SystemMessage(systemMsgStr));
messages.add(new UserMessage(question));
String summaryTitle;
@ -938,7 +876,6 @@ public class AiragChatServiceImpl implements IAiragChatService {
/**
* 获取用户名
*
* @param httpRequest
* @return
* @author chenrui
@ -948,9 +885,9 @@ public class AiragChatServiceImpl implements IAiragChatService {
try {
TokenUtils.getTokenByRequest();
String token;
if (null != httpRequest) {
if(null != httpRequest){
token = TokenUtils.getTokenByRequest(httpRequest);
} else {
}else{
token = TokenUtils.getTokenByRequest();
}
if (TokenUtils.verifyToken(token, sysBaseApi, redisUtil)) {
@ -961,19 +898,4 @@ public class AiragChatServiceImpl implements IAiragChatService {
}
return null;
}
/**
* 打印耗时
* @param requestId
* @param message
* @author chenrui
* @date 2025/4/28 15:15
*/
private static void printChatDuration(String requestId,String message) {
Long beginTime = AiragLocalCache.get(AiragConsts.CACHE_TYPE_SSE_SEND_TIME, requestId);
if (null != beginTime) {
log.info("[AI-CHAT]{},requestId:{},耗时:{}s", message, requestId, (System.currentTimeMillis() - beginTime) / 1000);
}
}
}

View File

@ -1,83 +0,0 @@
package org.jeecg.modules.airag.demo;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.exception.JeecgBootBizTipException;
import org.jeecg.modules.airag.flow.component.enhance.IAiRagEnhanceJava;
import org.jeecgframework.poi.excel.ExcelImportUtil;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.springframework.stereotype.Component;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Java增强Demo: Excel数据读取器
* for [QQYUN-11718]【AI】积木报表对接AI流程编排接口展示报表
* @Author: chenrui
* @Date: 2025/4/29 16:51
*/
@Component("jimuDataReader")
@Slf4j
public class JimuDataReader implements IAiRagEnhanceJava {
@Override
public Map<String, Object> process(Map<String, Object> inputParams) {
// inputParams: {"bizData":"/xxxx/xxxx/xxxx/xxxx.xls"}
try {
String filePath = (String) inputParams.get("bizData");
if (filePath == null || filePath.isEmpty()) {
throw new IllegalArgumentException("File path is empty");
}
File excelFile = new File(filePath);
if (!excelFile.exists() || !excelFile.isFile()) {
throw new IllegalArgumentException("File not found: " + filePath);
}
// Since we don't know the target entity class, we'll read the Excel generically
return readExcelData(excelFile);
} catch (Exception e) {
log.error("Error processing Excel file", e);
throw new JeecgBootBizTipException("调用java增强失败", e);
}
}
/**
* Excel导入工具方法基于ExcelImportUtil
*
* @param file Excel文件
* @return Excel读取结果包含字段和数据
* @throws Exception 导入过程中的异常
*/
public static Map<String, Object> readExcelData(File file) throws Exception {
Map<String, Object> result = new HashMap<>();
// 设置导入参数
ImportParams params = new ImportParams();
params.setTitleRows(0); // 没有标题
params.setHeadRows(1); // 第一行是表头
// 读取Excel数据
List<Map<String, Object>> dataList = ExcelImportUtil.importExcel(file, Map.class, params);
// 如果没有数据,返回空结果
if (dataList == null || dataList.isEmpty()) {
result.put("fields", new ArrayList<>());
result.put("datas", new ArrayList<>());
return result;
}
// 从第一行数据中获取字段名
List<String> fieldNames = new ArrayList<>(dataList.get(0).keySet());
result.put("fields", fieldNames);
result.put("datas", dataList);
return result;
}
}

View File

@ -1,22 +0,0 @@
package org.jeecg.modules.airag.demo;
import org.jeecg.modules.airag.flow.component.enhance.IAiRagEnhanceJava;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.Map;
/**
* @Description: Java增强节点示例类
* @Author: chenrui
* @Date: 2025/3/6 11:42
*/
@Component("testAiragEnhance")
public class TestAiragEnhance implements IAiRagEnhanceJava {
@Override
public Map<String, Object> process(Map<String, Object> inputParams) {
Object arg1 = inputParams.get("arg1");
Object arg2 = inputParams.get("arg2");
return Collections.singletonMap("result",arg1.toString()+"java拼接"+arg2.toString());
}
}

View File

@ -35,11 +35,6 @@ public class LLMConsts {
*/
public static final String MODEL_TYPE_LLM = "LLM";
/**
* 向量模型:默认维度
*/
public static final Integer EMBED_MODEL_DEFAULT_DIMENSION = 1536;
/**
* 知识库:文档状态:草稿
*/
@ -52,10 +47,7 @@ public class LLMConsts {
* 知识库:文档状态:构建完成
*/
public static final String KNOWLEDGE_DOC_STATUS_COMPLETE = "complete";
/**
* 知识库:文档状态:构建失败
*/
public static final String KNOWLEDGE_DOC_STATUS_FAILED = "failed";
/**
* 知识库:文档类型:文本

View File

@ -4,12 +4,9 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.util.AssertUtils;
import org.jeecg.common.util.TokenUtils;
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
import org.jeecg.modules.airag.common.vo.knowledge.KnowledgeSearchResult;
import org.jeecg.modules.airag.llm.consts.LLMConsts;
import org.jeecg.modules.airag.llm.entity.AiragKnowledge;
@ -22,7 +19,7 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -77,7 +74,6 @@ public class AiragKnowledgeController {
* @date 2025/2/18 17:09
*/
@PostMapping(value = "/add")
@RequiresPermissions("airag:knowledge:add")
public Result<String> add(@RequestBody AiragKnowledge airagKnowledge) {
airagKnowledge.setStatus(LLMConsts.STATUS_ENABLE);
airagKnowledgeService.save(airagKnowledge);
@ -94,7 +90,6 @@ public class AiragKnowledgeController {
*/
@Transactional(rollbackFor = Exception.class)
@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
@RequiresPermissions("airag:knowledge:edit")
public Result<String> edit(@RequestBody AiragKnowledge airagKnowledge) {
AiragKnowledge airagKnowledgeEntity = airagKnowledgeService.getById(airagKnowledge.getId());
if (airagKnowledgeEntity == null) {
@ -118,7 +113,6 @@ public class AiragKnowledgeController {
* @date 2025/3/12 17:05
*/
@PutMapping(value = "/rebuild")
@RequiresPermissions("airag:knowledge:rebuild")
public Result<?> rebuild(@RequestParam("knowIds") String knowIds) {
String[] knowIdArr = knowIds.split(",");
for (String knowId : knowIdArr) {
@ -137,24 +131,29 @@ public class AiragKnowledgeController {
*/
@Transactional(rollbackFor = Exception.class)
@DeleteMapping(value = "/delete")
@RequiresPermissions("airag:knowledge:delete")
public Result<String> delete(HttpServletRequest request, @RequestParam(name = "id", required = true) String id) {
//update-begin---author:chenrui ---date:20250606 for[issues/8337]关于ai工作列表的数据权限问题 #8337------------
//如果是saas隔离的情况下判断当前租户id是否是当前租户下的
if (MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL) {
AiragKnowledge know = airagKnowledgeService.getById(id);
//获取当前租户
String currentTenantId = TokenUtils.getTenantIdByRequest(request);
if (null == know || !know.getTenantId().equals(currentTenantId)) {
return Result.error("删除AI知识库失败不能删除其他租户的AI知识库");
}
}
//update-end---author:chenrui ---date:20250606 for[issues/8337]关于ai工作列表的数据权限问题 #8337------------
public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
airagKnowledgeDocService.removeByKnowIds(Collections.singletonList(id));
airagKnowledgeService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除知识库
*
* @param ids
* @return
* @author chenrui
* @date 2025/2/18 17:09
*/
@Transactional(rollbackFor = Exception.class)
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
List<String> idsList = Arrays.asList(ids.split(","));
airagKnowledgeDocService.removeByKnowIds(idsList);
airagKnowledgeService.removeByIds(idsList);
return Result.OK("批量删除成功!");
}
/**
* 通过id查询知识库
*
@ -204,7 +203,6 @@ public class AiragKnowledgeController {
* @date 2025/2/18 15:47
*/
@PostMapping(value = "/doc/edit")
@RequiresPermissions("airag:knowledge:doc:edit")
public Result<?> addDocument(@RequestBody AiragKnowledgeDoc airagKnowledgeDoc) {
return airagKnowledgeDocService.editDocument(airagKnowledgeDoc);
}
@ -217,7 +215,6 @@ public class AiragKnowledgeController {
* @date 2025/3/20 11:29
*/
@PostMapping(value = "/doc/import/zip")
@RequiresPermissions("airag:knowledge:doc:zip")
public Result<?> importDocumentFromZip(@RequestParam(name = "knowId", required = true) String knowId,
@RequestParam(name = "file", required = true) MultipartFile file) {
return airagKnowledgeDocService.importDocumentFromZip(knowId,file);
@ -244,7 +241,6 @@ public class AiragKnowledgeController {
* @date 2025/2/18 15:47
*/
@PutMapping(value = "/doc/rebuild")
@RequiresPermissions("airag:knowledge:doc:rebuild")
public Result<?> rebuildDocument(@RequestParam("docIds") String docIds) {
return airagKnowledgeDocService.rebuildDocument(docIds);
}
@ -259,49 +255,12 @@ public class AiragKnowledgeController {
*/
@Transactional(rollbackFor = Exception.class)
@DeleteMapping(value = "/doc/deleteBatch")
@RequiresPermissions("airag:knowledge:doc:deleteBatch")
public Result<String> deleteDocumentBatch(HttpServletRequest request, @RequestParam(name = "ids", required = true) String ids) {
public Result<String> deleteDocumentBatch(@RequestParam(name = "ids", required = true) String ids) {
List<String> idsList = Arrays.asList(ids.split(","));
//update-begin---author:chenrui ---date:20250606 for[issues/8337]关于ai工作列表的数据权限问题 #8337------------
//如果是saas隔离的情况下判断当前租户id是否是当前租户下的
if (MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL) {
List<AiragKnowledgeDoc> docList = airagKnowledgeDocService.listByIds(idsList);
//获取当前租户
String currentTenantId = TokenUtils.getTenantIdByRequest(request);
docList.forEach(airagKnowledgeDoc -> {
if (null == airagKnowledgeDoc || !airagKnowledgeDoc.getTenantId().equals(currentTenantId)) {
throw new IllegalArgumentException("删除AI知识库文档失败不能删除其他租户的AI知识库文档");
}
});
}
//update-end---author:chenrui ---date:20250606 for[issues/8337]关于ai工作列表的数据权限问题 #8337------------
airagKnowledgeDocService.removeDocByIds(idsList);
return Result.OK("批量删除成功!");
}
/**
* 清空知识库文档
*
* @param
* @return
*/
@Transactional(rollbackFor = Exception.class)
@DeleteMapping(value = "/doc/deleteAll")
@RequiresPermissions("airag:knowledge:doc:deleteAll")
public Result<?> deleteDocumentAll(HttpServletRequest request, @RequestParam(name = "knowId") String knowId) {
//update-begin---author:chenrui ---date:20250606 for[issues/8337]关于ai工作列表的数据权限问题 #8337------------
//如果是saas隔离的情况下判断当前租户id是否是当前租户下的
if (MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL) {
AiragKnowledge know = airagKnowledgeService.getById(knowId);
//获取当前租户
String currentTenantId = TokenUtils.getTenantIdByRequest(request);
if (null == know || !know.getTenantId().equals(currentTenantId)) {
return Result.error("删除AI知识库失败不能删除其他租户的AI知识库");
}
}
//update-end---author:chenrui ---date:20250606 for[issues/8337]关于ai工作列表的数据权限问题 #8337------------
return airagKnowledgeDocService.deleteAllByKnowId(knowId);
}
/**
* 命中测试

View File

@ -3,32 +3,20 @@ package org.jeecg.modules.airag.llm.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.model.embedding.EmbeddingModel;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.jeecg.ai.factory.AiModelFactory;
import org.jeecg.ai.factory.AiModelOptions;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.util.AssertUtils;
import org.jeecg.common.util.TokenUtils;
import org.jeecg.config.mybatis.MybatisPlusSaasConfig;
import org.jeecg.modules.airag.llm.consts.LLMConsts;
import org.jeecg.modules.airag.llm.entity.AiragModel;
import org.jeecg.modules.airag.llm.handler.AIChatHandler;
import org.jeecg.modules.airag.llm.handler.EmbeddingHandler;
import org.jeecg.modules.airag.llm.service.IAiragModelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.Collections;
/**
* @Description: AiRag模型配置
@ -44,8 +32,6 @@ public class AiragModelController extends JeecgController<AiragModel, IAiragMode
@Autowired
private IAiragModelService airagModelService;
@Autowired
AIChatHandler aiChatHandler;
/**
* 分页列表查询
@ -71,12 +57,7 @@ public class AiragModelController extends JeecgController<AiragModel, IAiragMode
* @return
*/
@PostMapping(value = "/add")
@RequiresPermissions("airag:model:add")
public Result<String> add(@RequestBody AiragModel airagModel) {
// 验证 模型名称/模型类型/基础模型
AssertUtils.assertNotEmpty("模型名称不能为空", airagModel.getName());
AssertUtils.assertNotEmpty("模型类型不能为空", airagModel.getModelType());
AssertUtils.assertNotEmpty("基础模型不能为空", airagModel.getModelName());
airagModelService.save(airagModel);
return Result.OK("添加成功!");
}
@ -88,7 +69,6 @@ public class AiragModelController extends JeecgController<AiragModel, IAiragMode
* @return
*/
@RequestMapping(value = "/edit", method = {RequestMethod.PUT, RequestMethod.POST})
@RequiresPermissions("airag:model:edit")
public Result<String> edit(@RequestBody AiragModel airagModel) {
airagModelService.updateById(airagModel);
return Result.OK("编辑成功!");
@ -101,23 +81,23 @@ public class AiragModelController extends JeecgController<AiragModel, IAiragMode
* @return
*/
@DeleteMapping(value = "/delete")
@RequiresPermissions("airag:model:delete")
public Result<String> delete(HttpServletRequest request, @RequestParam(name = "id", required = true) String id) {
//update-begin---author:chenrui ---date:20250606 for[issues/8337]关于ai工作列表的数据权限问题 #8337------------
//如果是saas隔离的情况下判断当前租户id是否是当前租户下的
if (MybatisPlusSaasConfig.OPEN_SYSTEM_TENANT_CONTROL) {
AiragModel model = airagModelService.getById(id);
//获取当前租户
String currentTenantId = TokenUtils.getTenantIdByRequest(request);
if (null == model || !model.getTenantId().equals(currentTenantId)) {
return Result.error("删除AI模型失败不能删除其他租户的AI模型");
}
}
//update-end---author:chenrui ---date:20250606 for[issues/8337]关于ai工作列表的数据权限问题 #8337------------
public Result<String> delete(@RequestParam(name = "id", required = true) String id) {
airagModelService.removeById(id);
return Result.OK("删除成功!");
}
/**
* 批量删除
*
* @param ids
* @return
*/
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name = "ids", required = true) String ids) {
this.airagModelService.removeByIds(Arrays.asList(ids.split(",")));
return Result.OK("批量删除成功!");
}
/**
* 通过id查询
*
@ -156,25 +136,4 @@ public class AiragModelController extends JeecgController<AiragModel, IAiragMode
return super.importExcel(request, response, AiragModel.class);
}
@PostMapping(value = "/test")
public Result<?> test(@RequestBody AiragModel airagModel) {
// 验证 模型名称/模型类型/基础模型
AssertUtils.assertNotEmpty("模型名称不能为空", airagModel.getName());
AssertUtils.assertNotEmpty("模型类型不能为空", airagModel.getModelType());
AssertUtils.assertNotEmpty("基础模型不能为空", airagModel.getModelName());
try {
if(LLMConsts.MODEL_TYPE_LLM.equals(airagModel.getModelType())){
aiChatHandler.completions(airagModel, Collections.singletonList(UserMessage.from("test connection")), null);
}else{
AiModelOptions aiModelOptions = EmbeddingHandler.buildModelOptions(airagModel);
EmbeddingModel embeddingModel = AiModelFactory.createEmbeddingModel(aiModelOptions);
embeddingModel.embed("test text");
}
}catch (Exception e){
log.error("测试模型连接失败", e);
return Result.error("测试模型连接失败" + e.getMessage());
}
return Result.OK("测试模型连接成功");
}
}

View File

@ -30,14 +30,13 @@ public class AiragKnowledge implements Serializable {
*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description = "主键")
private java.lang.String id;
private String id;
/**
* 创建人
*/
@Schema(description = "创建人")
@Dict(dictTable = "sys_user",dicCode = "username",dicText = "realname")
private java.lang.String createBy;
private String createBy;
/**
* 创建日期
@ -51,7 +50,7 @@ public class AiragKnowledge implements Serializable {
* 更新人
*/
@Schema(description = "更新人")
private java.lang.String updateBy;
private String updateBy;
/**
* 更新日期
@ -65,21 +64,21 @@ public class AiragKnowledge implements Serializable {
* 所属部门
*/
@Schema(description = "所属部门")
private java.lang.String sysOrgCode;
private String sysOrgCode;
/**
* 租户id
*/
@Excel(name = "租户id", width = 15)
@Schema(description = "租户id")
private java.lang.String tenantId;
private String tenantId;
/**
* 知识库名称
*/
@Excel(name = "知识库名称", width = 15)
@Schema(description = "知识库名称")
private java.lang.String name;
private String name;
/**
* 向量模型id
@ -87,19 +86,19 @@ public class AiragKnowledge implements Serializable {
@Excel(name = "向量模型id", width = 15, dictTable = "airag_model where model_type = 'EMBED'", dicText = "name", dicCode = "id")
@Dict(dictTable = "airag_model where model_type = 'EMBED'", dicText = "name", dicCode = "id")
@Schema(description = "向量模型id")
private java.lang.String embedId;
private String embedId;
/**
* 描述
*/
@Excel(name = "描述", width = 15)
@Schema(description = "描述")
private java.lang.String descr;
private String descr;
/**
* 状态
*/
@Excel(name = "状态", width = 15)
@Schema(description = "状态")
private java.lang.String status;
private String status;
}

View File

@ -3,7 +3,6 @@ package org.jeecg.modules.airag.llm.entity;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.*;
import org.jeecg.common.aspect.annotation.Dict;
import org.jeecg.common.constant.ProvinceCityArea;
import org.jeecg.common.util.SpringContextUtils;
import lombok.Data;
@ -41,7 +40,6 @@ public class AiragKnowledgeDoc implements Serializable {
* 创建人
*/
@Schema(description = "创建人")
@Dict(dictTable = "sys_user",dicCode = "username",dicText = "realname")
private String createBy;
/**
@ -121,4 +119,9 @@ public class AiragKnowledgeDoc implements Serializable {
@Schema(description = "状态")
private String status;
/**
* 服务器基础路径
*/
@TableField(exist = false)
private String baseUrl;
}

View File

@ -45,7 +45,6 @@ public class AiragModel implements Serializable {
* 创建人
*/
@Schema(description = "创建人")
@Dict(dictTable = "sys_user",dicCode = "username",dicText = "realname")
private String createBy;
/**
* 创建日期

View File

@ -12,7 +12,7 @@ import org.jeecg.modules.airag.common.handler.AIChatParams;
import org.jeecg.modules.airag.common.handler.IAIChatHandler;
import org.jeecg.modules.airag.llm.consts.LLMConsts;
import org.jeecg.modules.airag.llm.entity.AiragModel;
import org.jeecg.modules.airag.llm.mapper.AiragModelMapper;
import org.jeecg.modules.airag.llm.service.IAiragModelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@ -38,7 +38,7 @@ import java.util.regex.Matcher;
public class AIChatHandler implements IAIChatHandler {
@Autowired
AiragModelMapper airagModelMapper;
IAiragModelService airagModelService;
@Autowired
EmbeddingHandler embeddingHandler;
@ -82,7 +82,7 @@ public class AIChatHandler implements IAIChatHandler {
AssertUtils.assertNotEmpty("至少发送一条消息", messages);
AssertUtils.assertNotEmpty("请选择模型", modelId);
AiragModel airagModel = airagModelMapper.getByIdIgnoreTenant(modelId);
AiragModel airagModel = airagModelService.getById(modelId);
return completions(airagModel, messages, params);
}
@ -96,7 +96,7 @@ public class AIChatHandler implements IAIChatHandler {
* @author chenrui
* @date 2025/2/24 17:30
*/
public String completions(AiragModel airagModel, List<ChatMessage> messages, AIChatParams params) {
private String completions(AiragModel airagModel, List<ChatMessage> messages, AIChatParams params) {
params = mergeParams(airagModel, params);
String resp = llmHandler.completions(messages, params);
if (resp.contains("</think>")
@ -150,7 +150,7 @@ public class AIChatHandler implements IAIChatHandler {
AssertUtils.assertNotEmpty("至少发送一条消息", messages);
AssertUtils.assertNotEmpty("请选择模型", modelId);
AiragModel airagModel = airagModelMapper.getByIdIgnoreTenant(modelId);
AiragModel airagModel = airagModelService.getById(modelId);
return chat(airagModel, messages, params);
}
@ -226,7 +226,7 @@ public class AIChatHandler implements IAIChatHandler {
params.setMaxTokens(modelParams.getInteger("maxTokens"));
}
if (oConvertUtils.isObjectEmpty(params.getTimeout())) {
params.setTimeout(modelParams.getInteger("timeout"));
params.setMaxTokens(modelParams.getInteger("timeout"));
}
}
@ -237,16 +237,6 @@ public class AIChatHandler implements IAIChatHandler {
params.setQueryRouter(queryRouter);
}
// 设置确保maxTokens值正确
if (oConvertUtils.isObjectNotEmpty(params.getMaxTokens()) && params.getMaxTokens() <= 0) {
params.setMaxTokens(null);
}
// 默认超时时间
if(oConvertUtils.isObjectEmpty(params.getTimeout())){
params.setTimeout(60);
}
return params;
}

View File

@ -35,9 +35,8 @@ import org.jeecg.modules.airag.llm.document.TikaDocumentParser;
import org.jeecg.modules.airag.llm.entity.AiragKnowledge;
import org.jeecg.modules.airag.llm.entity.AiragKnowledgeDoc;
import org.jeecg.modules.airag.llm.entity.AiragModel;
import org.jeecg.modules.airag.llm.mapper.AiragKnowledgeMapper;
import org.jeecg.modules.airag.llm.mapper.AiragModelMapper;
import org.jeecg.modules.airag.llm.service.IAiragKnowledgeService;
import org.jeecg.modules.airag.llm.service.IAiragModelService;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@ -70,15 +69,13 @@ public class EmbeddingHandler implements IEmbeddingHandler {
EmbedStoreConfigBean embedStoreConfigBean;
@Autowired
private AiragModelMapper airagModelMapper;
@Lazy
private IAiragModelService airagModelService;
@Autowired
@Lazy
private IAiragKnowledgeService airagKnowledgeService;
@Autowired
private AiragKnowledgeMapper airagKnowledgeMapper;
@Value(value = "${jeecg.path.upload:}")
private String uploadpath;
@ -115,13 +112,6 @@ public class EmbeddingHandler implements IEmbeddingHandler {
*/
private static final ConcurrentHashMap<String, EmbeddingStore<TextSegment>> EMBED_STORE_CACHE = new ConcurrentHashMap<>();
/**
* 正则匹配: md图片
* "!\\[(.*?)]\\((.*?)(\\s*=\\d+)?\\)"
*/
private static final Pattern PATTERN_MD_IMAGE = Pattern.compile("!\\[(.*?)]\\((.*?)\\)");
/**
* 向量化文档
*
@ -193,7 +183,6 @@ public class EmbeddingHandler implements IEmbeddingHandler {
* @author chenrui
* @date 2025/2/18 16:52
*/
@Override
public KnowledgeSearchResult embeddingSearch(List<String> knowIds, String queryText, Integer topNumber, Double similarity) {
AssertUtils.assertNotEmpty("请选择知识库", knowIds);
AssertUtils.assertNotEmpty("请填写查询内容", queryText);
@ -234,7 +223,7 @@ public class EmbeddingHandler implements IEmbeddingHandler {
*/
public List<Map<String, Object>> searchEmbedding(String knowId, String queryText, Integer topNumber, Double similarity) {
AssertUtils.assertNotEmpty("请选择知识库", knowId);
AiragKnowledge knowledge = airagKnowledgeMapper.getByIdIgnoreTenant(knowId);
AiragKnowledge knowledge = airagKnowledgeService.getById(knowId);
AssertUtils.assertNotEmpty("知识库不存在", knowledge);
AssertUtils.assertNotEmpty("请填写查询内容", queryText);
AiragModel model = getEmbedModelData(knowledge.getEmbedId());
@ -279,7 +268,6 @@ public class EmbeddingHandler implements IEmbeddingHandler {
* @author chenrui
* @date 2025/2/20 21:03
*/
@Override
public QueryRouter getQueryRouter(List<String> knowIds, Integer topNumber, Double similarity) {
AssertUtils.assertNotEmpty("请选择知识库", knowIds);
List<ContentRetriever> retrievers = Lists.newArrayList();
@ -287,7 +275,7 @@ public class EmbeddingHandler implements IEmbeddingHandler {
if (oConvertUtils.isEmpty(knowId)) {
continue;
}
AiragKnowledge knowledge = airagKnowledgeMapper.getByIdIgnoreTenant(knowId);
AiragKnowledge knowledge = airagKnowledgeService.getById(knowId);
AssertUtils.assertNotEmpty("知识库不存在", knowledge);
AiragModel model = getEmbedModelData(knowledge.getEmbedId());
AiModelOptions modelOptions = buildModelOptions(model);
@ -357,7 +345,7 @@ public class EmbeddingHandler implements IEmbeddingHandler {
*/
private AiragModel getEmbedModelData(String modelId) {
AssertUtils.assertNotEmpty("向量模型不能为空", modelId);
AiragModel model = airagModelMapper.getByIdIgnoreTenant(modelId);
AiragModel model = airagModelService.getById(modelId);
AssertUtils.assertNotEmpty("向量模型不存在", model);
AssertUtils.assertEquals("仅支持向量模型", LLMConsts.MODEL_TYPE_EMBED, model.getModelType());
return model;
@ -383,18 +371,6 @@ public class EmbeddingHandler implements IEmbeddingHandler {
AiModelOptions modelOp = buildModelOptions(model);
EmbeddingModel embeddingModel = AiModelFactory.createEmbeddingModel(modelOp);
String tableName = embedStoreConfigBean.getTable();
// update-begin---author:sunjianlei ---date:20250509 for【QQYUN-12345】向量模型维度不一致问题
// 如果该模型不是默认的向量维度
int dimension = embeddingModel.dimension();
if (!LLMConsts.EMBED_MODEL_DEFAULT_DIMENSION.equals(dimension)) {
// 就加上维度后缀,防止因维度不一致导致保存失败
tableName += ("_" + dimension);
}
// update-end-----author:sunjianlei ---date:20250509 for【QQYUN-12345】向量模型维度不一致问题
EmbeddingStore<TextSegment> embeddingStore = PgVectorEmbeddingStore.builder()
// Connection and table parameters
.host(embedStoreConfigBean.getHost())
@ -402,7 +378,7 @@ public class EmbeddingHandler implements IEmbeddingHandler {
.database(embedStoreConfigBean.getDatabase())
.user(embedStoreConfigBean.getUser())
.password(embedStoreConfigBean.getPassword())
.table(tableName)
.table(embedStoreConfigBean.getTable())
// Embedding dimension
// Required: Must match the embedding models output dimension
.dimension(embeddingModel.dimension())
@ -473,20 +449,16 @@ public class EmbeddingHandler implements IEmbeddingHandler {
String fileType = FilenameUtils.getExtension(docFile.getName());
if ("md".contains(fileType)) {
// 如果是md文件查找所有图片语法如果是本地图片替换成网络图片
String baseUrl = "#{domainURL}/sys/common/static/";
String baseUrl = doc.getBaseUrl() + "/sys/common/static/";
String sourcePath = metadataJson.getString(LLMConsts.KNOWLEDGE_DOC_METADATA_SOURCES_PATH);
if(oConvertUtils.isNotEmpty(sourcePath)) {
String escapedPath = uploadpath;
//update-begin---author:wangshuai---date:2025-06-03---for:【QQYUN-12636】【AI知识库】文档库上传 本地local 文档中的图片不展示---
/*if (File.separator.equals("\\")){
if (File.separator.equals("\\")){
escapedPath = uploadpath.replace("//", "\\\\");
}*/
//update-end---author:wangshuai---date:2025-06-03---for:【QQYUN-12636】【AI知识库】文档库上传 本地local 文档中的图片不展示---
}
sourcePath = sourcePath.replaceFirst("^" + escapedPath, "").replace("\\", "/");
String docFilePath = metadataJson.getString(LLMConsts.KNOWLEDGE_DOC_METADATA_FILEPATH);
docFilePath = FilenameUtils.getPath(docFilePath);
docFilePath = docFilePath.replace("\\", "/");
StringBuffer sb = replaceImageUrl(content, baseUrl + sourcePath + "/", baseUrl + docFilePath);
baseUrl = baseUrl + sourcePath + "/";
StringBuffer sb = replaceImageUrl(content, baseUrl);
content = sb.toString();
}
}
@ -497,9 +469,11 @@ public class EmbeddingHandler implements IEmbeddingHandler {
}
@NotNull
private static StringBuffer replaceImageUrl(String content, String abstractBaseUrl, String relativeBaseUrl) {
private static StringBuffer replaceImageUrl(String content, String baseUrl) {
// 正则表达式匹配md文件中的图片语法 ![alt text](image url)
Matcher matcher = PATTERN_MD_IMAGE.matcher(content);
String mdImagePattern = "!\\[(.*?)]\\((.*?)(\\s*=\\d+)?\\)";
Pattern pattern = Pattern.compile(mdImagePattern);
Matcher matcher = pattern.matcher(content);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
@ -507,16 +481,7 @@ public class EmbeddingHandler implements IEmbeddingHandler {
// 检查是否是本地图片路径
if (!imageUrl.startsWith("http")) {
// 替换成网络图片路径
String networkImageUrl = abstractBaseUrl + imageUrl;
if(imageUrl.startsWith("/")) {
// 绝对路径
networkImageUrl = abstractBaseUrl + imageUrl;
}else{
// 相对路径
networkImageUrl = relativeBaseUrl + imageUrl;
}
// 修改图片路径中//->/但保留http://和https://
networkImageUrl = networkImageUrl.replaceAll("(?<!http:)(?<!https:)//", "/");
String networkImageUrl = baseUrl + imageUrl;
matcher.appendReplacement(sb, "![" + matcher.group(1) + "](" + networkImageUrl + ")");
} else {
matcher.appendReplacement(sb, "![" + matcher.group(1) + "](" + imageUrl + ")");

View File

@ -1,6 +1,5 @@
package org.jeecg.modules.airag.llm.mapper;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.jeecg.modules.airag.llm.entity.AiragKnowledge;
@ -12,14 +11,4 @@ import org.jeecg.modules.airag.llm.entity.AiragKnowledge;
*/
public interface AiragKnowledgeMapper extends BaseMapper<AiragKnowledge> {
/**
* 根据ID查询知识库信息(忽略租户)
* for [QQYUN-12113]分享之后的聊天,应用、模型、知识库不根据租户查询
* @param id
* @return
* @author chenrui
* @date 2025/4/21 15:24
*/
@InterceptorIgnore(tenantLine = "true")
AiragKnowledge getByIdIgnoreTenant(String id);
}

View File

@ -1,6 +1,5 @@
package org.jeecg.modules.airag.llm.mapper;
import com.baomidou.mybatisplus.annotation.InterceptorIgnore;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.jeecg.modules.airag.llm.entity.AiragModel;
@ -12,14 +11,4 @@ import org.jeecg.modules.airag.llm.entity.AiragModel;
*/
public interface AiragModelMapper extends BaseMapper<AiragModel> {
/**
* 根据ID查询模型信息(忽略租户)
* for [QQYUN-12113]分享之后的聊天,应用、模型、知识库不根据租户查询
* @param id
* @return
* @author chenrui
* @date 2025/4/21 15:24
*/
@InterceptorIgnore(tenantLine = "true")
AiragModel getByIdIgnoreTenant(String id);
}

View File

@ -2,8 +2,4 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.jeecg.modules.airag.llm.mapper.AiragKnowledgeMapper">
<select id="getByIdIgnoreTenant" resultType="org.jeecg.modules.airag.llm.entity.AiragKnowledge">
SELECT * FROM airag_knowledge WHERE id = #{id}
</select>
</mapper>

View File

@ -2,8 +2,4 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.jeecg.modules.airag.llm.mapper.AiragModelMapper">
<select id="getByIdIgnoreTenant" resultType="org.jeecg.modules.airag.llm.entity.AiragModel">
SELECT * FROM airag_model WHERE id = #{id}
</select>
</mapper>

View File

@ -67,14 +67,6 @@ public interface IAiragKnowledgeDocService extends IService<AiragKnowledgeDoc> {
*/
Result<?> removeDocByIds(List<String> docIds);
/**
* 通过知识库id删除所以文档
*
* @param knowId
* @return
*/
Result<?> deleteAllByKnowId(String knowId);
/**
* 从zip包导入文档
* @param knowId

View File

@ -1,12 +1,9 @@
package org.jeecg.modules.airag.llm.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipFile;
import org.apache.commons.io.FilenameUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.config.TenantContext;
@ -27,14 +24,11 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
@ -42,6 +36,8 @@ import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import static org.jeecg.modules.airag.llm.consts.LLMConsts.*;
@ -83,12 +79,6 @@ public class AiragKnowledgeDocServiceImpl extends ServiceImpl<AiragKnowledgeDocM
*/
private static final ExecutorService buildDocExecutorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
// 解压文件:单个文件最大150MB
private static final long MAX_FILE_SIZE = 150 * 1024 * 1024;
// 解压文件:总解压大小1024MB
private static final long MAX_TOTAL_SIZE = 1024 * 1024 * 1024;
// 解压文件:最多解压10000个Entry
private static final int MAX_ENTRY_COUNT = 10000;
@Transactional(rollbackFor = {Exception.class})
@Override
@ -132,6 +122,7 @@ public class AiragKnowledgeDocServiceImpl extends ServiceImpl<AiragKnowledgeDocM
AssertUtils.assertNotEmpty("文档不存在", docList);
HttpServletRequest request = SpringContextUtils.getHttpServletRequest();
String baseUrl = CommonUtils.getBaseUrl(request);
// 检查状态
List<AiragKnowledgeDoc> knowledgeDocs = docList.stream()
.filter(doc -> {
@ -152,6 +143,7 @@ public class AiragKnowledgeDocServiceImpl extends ServiceImpl<AiragKnowledgeDocM
})
.peek(doc -> {
doc.setStatus(KNOWLEDGE_DOC_STATUS_BUILDING);
doc.setBaseUrl(baseUrl);
})
.collect(Collectors.toList());
if (oConvertUtils.isObjectEmpty(knowledgeDocs)) {
@ -182,11 +174,13 @@ public class AiragKnowledgeDocServiceImpl extends ServiceImpl<AiragKnowledgeDocM
this.updateById(doc);
log.info("重建文档成功, 知识库id: {}, 文档id: {}", knowId, doc.getId());
} else {
this.handleDocBuildFailed(doc, "向量化失败");
doc.setStatus(KNOWLEDGE_DOC_STATUS_DRAFT);
this.updateById(doc);
log.info("重建文档失败, 知识库id: {}, 文档id: {}", knowId, doc.getId());
}
}catch (Throwable t){
this.handleDocBuildFailed(doc, t.getMessage());
doc.setStatus(KNOWLEDGE_DOC_STATUS_DRAFT);
this.updateById(doc);
log.error("重建文档失败:" + t.getMessage() + ", 知识库id: " + knowId + ", 文档id: " + doc.getId(), t);
}
//update-end---author:chenrui ---date:20250410 for[QQYUN-11943]【ai】ai知识库 上传完文档 一直显示构建中?------------
@ -196,24 +190,6 @@ public class AiragKnowledgeDocServiceImpl extends ServiceImpl<AiragKnowledgeDocM
return Result.ok("操作成功");
}
/**
* 处理文档构建失败
*/
private void handleDocBuildFailed(AiragKnowledgeDoc doc, String failedReason) {
doc.setStatus(KNOWLEDGE_DOC_STATUS_FAILED);
String metadataStr = doc.getMetadata();
JSONObject metadata;
if (oConvertUtils.isEmpty(metadataStr)) {
metadata = new JSONObject();
} else {
metadata = JSONObject.parseObject(metadataStr);
}
metadata.put("failedReason", failedReason);
doc.setMetadata(metadata.toJSONString());
this.updateById(doc);
}
@Override
public Result<?> removeByKnowIds(List<String> knowIds) {
@ -222,16 +198,8 @@ public class AiragKnowledgeDocServiceImpl extends ServiceImpl<AiragKnowledgeDocM
AiragKnowledge airagKnowledge = airagKnowledgeMapper.selectById(knowId);
AssertUtils.assertNotEmpty("知识库不存在", airagKnowledge);
AssertUtils.assertNotEmpty("请先为知识库配置向量模型库", airagKnowledge.getEmbedId());
// 异步删除向量数据
final String embedId = airagKnowledge.getEmbedId();
final String finalKnowId = knowId;
CompletableFuture.runAsync(() -> {
try {
embeddingHandler.deleteEmbedDocsByKnowId(finalKnowId, embedId);
} catch (Throwable ignore) {
}
});
// 删除数据
embeddingHandler.deleteEmbedDocsByKnowId(knowId, airagKnowledge.getEmbedId());
airagKnowledgeDocMapper.deleteByMainId(knowId);
}
return Result.OK();
@ -255,39 +223,13 @@ public class AiragKnowledgeDocServiceImpl extends ServiceImpl<AiragKnowledgeDocM
AiragKnowledge airagKnowledge = airagKnowledgeMapper.selectById(knowId);
AssertUtils.assertNotEmpty("知识库不存在", airagKnowledge);
AssertUtils.assertNotEmpty("请先为知识库配置向量模型库", airagKnowledge.getEmbedId());
// 异步删除向量数据
final String embedId = airagKnowledge.getEmbedId();
final List<String> docIdsToDelete = new ArrayList<>(groupedDocIds);
CompletableFuture.runAsync(() -> {
try {
embeddingHandler.deleteEmbedDocsByDocIds(docIdsToDelete, embedId);
} catch (Throwable ignore) {
}
});
// 删除数据
embeddingHandler.deleteEmbedDocsByDocIds(groupedDocIds, airagKnowledge.getEmbedId());
airagKnowledgeDocMapper.deleteBatchIds(groupedDocIds);
});
return Result.ok("success");
}
@Override
public Result<?> deleteAllByKnowId(String knowId) {
if (oConvertUtils.isEmpty(knowId)) {
return Result.error("知识库id不能为空");
}
LambdaQueryWrapper<AiragKnowledgeDoc> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(AiragKnowledgeDoc::getKnowledgeId, knowId);
//noinspection unchecked
wrapper.select(AiragKnowledgeDoc::getId);
List<AiragKnowledgeDoc> docList = airagKnowledgeDocMapper.selectList(wrapper);
if (docList.isEmpty()) {
return Result.ok("暂无文档");
}
List<String> docIds = docList.stream().map(AiragKnowledgeDoc::getId).collect(Collectors.toList());
this.removeDocByIds(docIds);
return Result.ok("清空完成");
}
@Transactional(rollbackFor = {java.lang.Exception.class})
@Override
public Result<?> importDocumentFromZip(String knowId, MultipartFile zipFile) {
@ -341,7 +283,6 @@ public class AiragKnowledgeDocServiceImpl extends ServiceImpl<AiragKnowledgeDocM
doc.setMetadata(metadata.toJSONString());
docList.add(doc);
});
AssertUtils.assertNotEmpty("压缩包中没有符合要求的文档", docList);
// 保存数据
this.saveBatch(docList);
// 重建文档
@ -358,113 +299,57 @@ public class AiragKnowledgeDocServiceImpl extends ServiceImpl<AiragKnowledgeDocM
/**
* 解压缩文件
*
* @param zipFilePath 压缩文件路径
* @param destDir 目标文件夹
* @param afterExtract 解压完成后回调
* @param zipFilePath
* @param destDir
* @param afterExtract
* @throws IOException
* @author chenrui
* @date 2025/3/20 14:37
*/
public static void unzipFile(String zipFilePath, String destDir, Consumer<File> afterExtract) throws IOException {
unzipFile(Paths.get(zipFilePath), Paths.get(destDir), afterExtract);
}
/**
* 解压缩文件
*
* @param zipFilePath 压缩文件路径
* @param targetDir 目标文件夹
* @param afterExtract 解压完成后回调
* @throws IOException
* @author chenrui
* @date 2025/4/28 17:02
*/
private static void unzipFile(Path zipFilePath, Path targetDir, Consumer<File> afterExtract) throws IOException {
long totalUnzippedSize = 0;
int entryCount = 0;
if (!Files.exists(targetDir)) {
Files.createDirectories(targetDir);
public static void unzipFile(String zipFilePath, String destDir, Consumer<File> afterExtract) throws
IOException {
// 创建目标目录
File dir = new File(destDir);
if (!dir.exists()) {
dir.mkdirs();
}
try (ZipFile zipFile = new ZipFile(zipFilePath.toFile())) {
Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();
try (ZipFile zipFile = new ZipFile(zipFilePath)) {
Enumeration<? extends ZipEntry> entries = zipFile.entries();
byte[] buffer = new byte[1024];
while (entries.hasMoreElements()) {
ZipArchiveEntry entry = entries.nextElement();
entryCount++;
if (entryCount > MAX_ENTRY_COUNT) {
throw new IOException("解压文件数量超限可能是zip bomb攻击");
ZipEntry ze = entries.nextElement();
File newFile = new File(destDir, ze.getName());
// 预防 ZIP 路径穿越攻击
String canonicalDestDirPath = dir.getCanonicalPath();
String canonicalFilePath = newFile.getCanonicalPath();
if (!canonicalFilePath.startsWith(canonicalDestDirPath + File.separator)) {
throw new IOException("ZIP 路径穿越攻击被阻止: " + ze.getName());
}
Path newPath = safeResolve(targetDir, entry.getName());
if (entry.isDirectory()) {
Files.createDirectories(newPath);
if (ze.isDirectory()) {
newFile.mkdirs();
} else {
Files.createDirectories(newPath.getParent());
try (InputStream is = zipFile.getInputStream(entry);
OutputStream os = Files.newOutputStream(newPath)) {
// 创建父目录
new File(newFile.getParent()).mkdirs();
long bytesCopied = copyLimited(is, os, MAX_FILE_SIZE);
totalUnzippedSize += bytesCopied;
if (totalUnzippedSize > MAX_TOTAL_SIZE) {
throw new IOException("解压总大小超限可能是zip bomb攻击");
// 读取 ZIP 文件并写入新文件
try (InputStream zis = zipFile.getInputStream(ze);
FileOutputStream fos = new FileOutputStream(newFile)) {
int len;
while ((len = zis.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
}
// 解压完成后回调
if (afterExtract != null) {
afterExtract.accept(newPath.toFile());
afterExtract.accept(newFile);
}
}
}
}
}
/**
* 安全解析路径防止Zip Slip攻击
*
* @param targetDir
* @param entryName
* @return
* @throws IOException
* @author chenrui
* @date 2025/4/28 16:46
*/
private static Path safeResolve(Path targetDir, String entryName) throws IOException {
Path resolvedPath = targetDir.resolve(entryName).normalize();
if (!resolvedPath.startsWith(targetDir)) {
throw new IOException("ZIP 路径穿越攻击被阻止:" + entryName);
}
return resolvedPath;
}
/**
* 复制输入流到输出流,并限制最大字节数
*
* @param in
* @param out
* @param maxBytes
* @return
* @throws IOException
* @author chenrui
* @date 2025/4/28 17:03
*/
private static long copyLimited(InputStream in, OutputStream out, long maxBytes) throws IOException {
byte[] buffer = new byte[8192];
long totalCopied = 0;
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
totalCopied += bytesRead;
if (totalCopied > maxBytes) {
throw new IOException("单个文件解压超限可能是zip bomb攻击");
}
out.write(buffer, 0, bytesRead);
}
return totalCopied;
}
}

View File

@ -0,0 +1,33 @@
package org.jeecg.modules.airag.llm.vo;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
import java.util.Map;
/**
* 知识库查询返回结果
*
* @Author: chenrui
* @Date: 2025/2/18 17:53
*/
@Data
@NoArgsConstructor
public class KnowledgeSearchResult {
/**
* 命中的文档内容
*/
String data;
/**
* 命中的文档列表
*/
List<Map<String, Object>> documents;
public KnowledgeSearchResult(String data, List<Map<String, Object>> documents) {
this.data = data;
this.documents = documents;
}
}

View File

@ -1,94 +0,0 @@
package org.jeecg.modules.airag.ocr.controller;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.commons.collections.CollectionUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.modules.airag.ocr.entity.AiOcr;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@RestController
@RequestMapping("/airag/ocr")
public class AiOcrController {
@Autowired
private RedisUtil redisUtil;
private static final String AI_OCR_REDIS_KEY = "airag:ocr";
@GetMapping("/list")
public Result<?> list(){
Object aiOcr = redisUtil.get(AI_OCR_REDIS_KEY);
IPage<AiOcr> page = new Page<>(1,10);
if(null != aiOcr){
List<AiOcr> aiOcrList = JSONObject.parseArray(aiOcr.toString(), AiOcr.class);
page.setRecords(aiOcrList);
page.setTotal(aiOcrList.size());
page.setPages(aiOcrList.size());
}
return Result.OK(page);
}
@PostMapping("/add")
public Result<String> add(@RequestBody AiOcr aiOcr){
Object aiOcrList = redisUtil.get(AI_OCR_REDIS_KEY);
aiOcr.setId(UUID.randomUUID().toString().replace("-",""));
if(null == aiOcrList){
List<AiOcr> list = new ArrayList<>();
list.add(aiOcr);
redisUtil.set(AI_OCR_REDIS_KEY, JSONObject.toJSONString(list));
}else{
List<AiOcr> aiOcrs = JSONObject.parseArray(aiOcrList.toString(), AiOcr.class);
aiOcrs.add(aiOcr);
redisUtil.set(AI_OCR_REDIS_KEY,JSONObject.toJSONString(aiOcrs));
}
return Result.OK("添加成功");
}
@PutMapping("/edit")
public Result<String> updateById(@RequestBody AiOcr aiOcr){
Object aiOcrList = redisUtil.get(AI_OCR_REDIS_KEY);
if(null != aiOcrList){
List<AiOcr> aiOcrs = JSONObject.parseArray(aiOcrList.toString(), AiOcr.class);
aiOcrs.forEach(item->{
if(item.getId().equals(aiOcr.getId())){
BeanUtils.copyProperties(aiOcr,item);
}
});
redisUtil.set(AI_OCR_REDIS_KEY,JSONObject.toJSONString(aiOcrs));
}else{
return Result.OK("编辑失败,未找到该数据");
}
return Result.OK("编辑成功");
}
@DeleteMapping("/deleteById")
public Result<String> deleteById(@RequestBody AiOcr aiOcr){
Object aiOcrObj = redisUtil.get(AI_OCR_REDIS_KEY);
if(null != aiOcrObj){
List<AiOcr> aiOcrs = JSONObject.parseArray(aiOcrObj.toString(), AiOcr.class);
List<AiOcr> aiOcrList = new ArrayList<>();
for(AiOcr ocr: aiOcrs){
if(!ocr.getId().equals(aiOcr.getId())){
aiOcrList.add(ocr);
}
}
if(CollectionUtils.isNotEmpty(aiOcrList)){
redisUtil.set(AI_OCR_REDIS_KEY,JSONObject.toJSONString(aiOcrList));
}else{
redisUtil.removeAll(AI_OCR_REDIS_KEY);
}
}else{
return Result.OK("删除失败,未找到该数据");
}
return Result.OK("删除成功");
}
}

View File

@ -1,29 +0,0 @@
package org.jeecg.modules.airag.ocr.entity;
import lombok.Data;
/**
* @Description: OCR识别实体类
*
* @author: wangshuai
* @date: 2025/4/16 17:01
*/
@Data
public class AiOcr {
/**
* 编号
*/
private String id;
/**
* 标题
*/
private String title;
/**
* 提示词
*/
private String prompt;
}

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!--定义日志文件的存储地址 -->
<property name="LOG_HOME" value="../logs" />
<!--<property name="COLOR_PATTERN" value="%black(%contextName-) %red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta( %replace(%caller{1}){'\t|Caller.{1}0|\r\n', ''})- %gray(%msg%xEx%n)" />-->
<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期%thread表示线程名%-5level级别从左显示5个字符宽度%msg日志消息%n是换行符
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n</pattern>-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{50}:%L) - %msg%n</pattern>
</encoder>
</appender>
<!-- 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--日志文件输出的文件名 -->
<FileNamePattern>${LOG_HOME}/jeecgboot-%d{yyyy-MM-dd}.%i.log</FileNamePattern>
<!--日志文件保留天数 -->
<MaxHistory>30</MaxHistory>
<maxFileSize>10MB</maxFileSize>
</rollingPolicy>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化输出:%d表示日期%thread表示线程名%-5level级别从左显示5个字符宽度%msg日志消息%n是换行符 -->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n</pattern>
</encoder>
</appender>
<!-- 生成 error html格式日志开始 -->
<appender name="HTML" class="ch.qos.logback.core.FileAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<!--设置日志级别,过滤掉info日志,只输入error日志-->
<level>ERROR</level>
</filter>
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.classic.html.HTMLLayout">
<pattern>%p%d%msg%M%F{32}%L</pattern>
</layout>
</encoder>
<file>${LOG_HOME}/error-log.html</file>
</appender>
<!-- 生成 error html格式日志结束 -->
<!-- 每天生成一个html格式的日志开始 -->
<appender name="FILE_HTML" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--日志文件输出的文件名 -->
<FileNamePattern>${LOG_HOME}/jeecgboot-%d{yyyy-MM-dd}.%i.html</FileNamePattern>
<!--日志文件保留天数 -->
<MaxHistory>30</MaxHistory>
<MaxFileSize>10MB</MaxFileSize>
</rollingPolicy>
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.classic.html.HTMLLayout">
<pattern>%p%d%msg%M%F{32}%L</pattern>
</layout>
</encoder>
</appender>
<!-- 每天生成一个html格式的日志结束 -->
<!--myibatis log configure -->
<logger name="com.apache.ibatis" level="TRACE" />
<logger name="java.sql.Connection" level="DEBUG" />
<logger name="java.sql.Statement" level="DEBUG" />
<logger name="java.sql.PreparedStatement" level="DEBUG" />
<logger name="logging.level.dev.langchain4j" level="DEBUG" />
<logger name="logging.level.dev.ai4j.openai4j" level="DEBUG" />
<!-- 日志输出级别 -->
<root level="info">
<appender-ref ref="STDOUT" />
<!-- <appender-ref ref="FILE" />-->
<!-- <appender-ref ref="HTML" />-->
<!-- <appender-ref ref="FILE_HTML" />-->
</root>
</configuration>

View File

@ -1,10 +1,7 @@
//package org.jeecg.modules.airag.test;
//
//import dev.langchain4j.agent.tool.ToolParameters;
//import dev.langchain4j.agent.tool.ToolSpecification;
//import dev.langchain4j.data.message.*;
//import dev.langchain4j.model.chat.ChatLanguageModel;
//import dev.langchain4j.model.chat.request.json.JsonObjectSchema;
//import dev.langchain4j.model.openai.OpenAiChatModel;
//import dev.langchain4j.model.output.Response;
//import dev.langchain4j.service.AiServices;
@ -15,13 +12,15 @@
//import org.jeecg.ai.factory.AiModelOptions;
//import org.jeecg.ai.handler.AIParams;
//import org.jeecg.ai.handler.LLMHandler;
//import org.junit.jupiter.api.Test;
//import org.jeecg.modules.airag.llm.handler.AIChatParams;
//import org.junit.Test;
//
//import java.io.IOException;
//import java.io.Serial;
//import java.nio.file.Files;
//import java.nio.file.Paths;
//import java.util.*;
//import java.util.ArrayList;
//import java.util.Base64;
//import java.util.Collections;
//import java.util.concurrent.ConcurrentHashMap;
//import java.util.concurrent.CountDownLatch;
//
@ -175,51 +174,4 @@
// }
//
//
// @Test
// public void testFuncCalling() {
// String apiKey = "sk-";
// String baseUrl = "https://api.v3.cm/v1";
// String modelName = "gpt-4o-mini";
// double temperature = 0.7;
// ChatLanguageModel llmModel = OpenAiChatModel.builder()
// .apiKey(apiKey)
// .baseUrl(baseUrl)
// .modelName(modelName)
// .temperature(temperature)
// .build();
//
// List<ToolSpecification> toolSpecifications = new ArrayList<>();
// Map<String, Map<String, Object>> properties = new ConcurrentHashMap<>();
//
// properties.put("location", new HashMap<>() {
// @Serial
// private static final long serialVersionUID = -8440944665582258534L;
// {
// put("type", "string");
// put("description", "The city and state, e.g. San Francisco, CA");
// }
// });
// ToolSpecification toolSpecification = ToolSpecification.builder()
// .name("get_current_weather")
// .description("Get the current weather in a given location")
// .parameters(ToolParameters.builder()
// .properties(properties)
// .required(List.of("location"))
// .type("string")
// .build())
// .build();
// toolSpecifications.add(toolSpecification);
//
// List<ChatMessage> messages = new ArrayList<>();
// messages.add(UserMessage.from("How is the weather in Beijing today?"));
//
// Response<AiMessage> resp = llmModel.generate(messages, toolSpecifications);
// System.out.println(resp);
//
// /*Response { content = AiMessage { text = null
// toolExecutionRequests = [ToolExecutionRequest { id = "call_hjPaRh9WAHv6Ib7KpgHpp8Tl", name = "get_current_weather", arguments = "{"location":"Beijing, China"}" }] },
// tokenUsage = TokenUsage { inputTokenCount = 69, outputTokenCount = 19, totalTokenCount = 88 }, finishReason = TOOL_EXECUTION, metadata = {} }*/
// }
//
//
//}

View File

@ -0,0 +1,97 @@
常见问题
1.pnpm安装依赖报错
错误: node\_modules\\vite\\node\_modules\\esbuild\\esbuild.exe ENOENT
解决方案https://blog.csdn.net/weixin_41760500/article/details/119885574命令node ./node_modules/esbuild/install.js
2.pnpm安装后访问提示缺少依赖

解决方案: https://stackoverflow.com/questions/70597494/pnpm-does-not-resolve-dependencies
3.pnpm install出现错误
错误ERR\_PNPM\_PEER\_DEP\_ISSUES Unmet peer dependencies
http://ms521.cn/index.php/Home/Index/article/aid/271
4.项目安装依赖无问题,访问页面报错
前端部分报错:
[plugin:vite:vue-jsx] Cannot find package 'C:\Users\123\Desktop\JeecgBoot-master\pincone_system\jeecgboot-vue3\node_modules\.pnpm\@vitejs+plugin-vue-jsx@3.1.0_vite@5.4.9_@types+node@20.16.13_less@4.2.0_terser@5.36.0__vue@3.5.12_typescript@4.9.5_\node_modules\@babel\plugin-transform-typescript\lib\index.js' imported from C:\Users\123\Desktop\JeecgBoot-master\pincone_system\jeecgboot-vue3\node_modules\.pnpm\@vitejs+plugin-vue-jsx@3.1.0_vite@5.4.9_@types+node@20.16.13_less@4.2.0_terser@5.36.0__vue@3.5.12_typescript@4.9.5_\node_modules\@vitejs\plugin-vue-jsx\dist\index.cjs Did you mean to import "@babel/plugin-transform-typescript/lib/index.js"? #7396
回答: 是因为项目的路径太长导致 相关问题Issues查看相关博客
• https://blog.csdn.net/weixin_43235500/article/details/142144989
• https://blog.csdn.net/qq_25996219/article/details/140328092
5. 通过npm install启动报错
建议请使用pnpm i 可以避免更多问题
错误情况:

解决:进入提示的路径 \node_modules\vite-plugin-mock\node_modules\esbuild\
执行命令: node install.js再启动就好了
6. 前端刷新进不了登录页面
报错props.ts:15 Uncaught (in promise) SyntaxError: Unexpected token '='
错误截图:
原因:谷歌浏览器版本过低,升级浏览器
比如这边版本就过低了
7.表单如何全部禁用
加上这个属性就可以了

效果

8.table列表如何自定义排序
defSort: {
column: 'id',
order: 'desc',
},
参考示例:
9.idea编写js时爆红提示statement expected
https://blog.csdn.net/mlsama/article/details/80633009
10.抽屉的setDrawerProps不好使值会还原
11. 如何删除不需要的demo制作一个精简版本
精简项目删除demo等非必须功能
12.vue3 暗黑模式下显示不完整
错误示例:
解决方案:
在样式中的字体颜色和背景颜色使用@变量名称来代替
color: @text-color;
background-color: @component-background;
[info] 通用样式变量名称可以在在目录bulid->vite->plugin->themes.ts中找到,darkModifyVars是重写antd中的样式
[info]更多样式变量名称请参考目录node_modules/es/style/themes/default.less

改造完成之后的效果
13.操作列“删除按钮”界面布局异常
相关issue
https://github.com/jeecgboot/jeecgboot-vue3/issues/458
问题截图:
解决方案找到popConfirm填写属性placement: 'left'
placement: 'left',
效果截图
14.在centos7中下载依赖pnpm i时 mozjpeg依赖下载不下来
https://github.com/jeecgboot/jeecgboot-vue3/issues/433#issuecomment-1510470534
15.日期遮挡问题
问题截图:
下拉显示组件,页面滚动时,页面出现错位遮挡问题
解决方案:将组件挂载到父节点上
getPopupContainer: (node) => node.parentNode,
效果截图
16.nextTick作用
在 Vue 3 中nextTick 方法用于在 DOM 更新之后执行回调函数。它的作用是在下次 DOM 更新循环结束后执行一些操作,以确保你在操作更新的 DOM 元素时能够获取到最新的结果。nextTick 方法可以用于以下情况:
1 在更新数据后立即操作 DOM 元素。
2 在更新组件后执行某些逻辑或触发一些副作用。
3 在更新后获取更新后的 DOM 元素的尺寸或位置等信息。
使用nextTick 方法有两种方式1.使用回调函数:
nextTick(()=>
//在DOM更新后执行的操作
}):
2.使用Promise:
nextTick().then(()=>
//在DOM更新后执行的操作
})
无论使用哪种方式传入的回调函数或Promisel回调都会在下一次DOM更新周期之后被调用。这样可以确保在数据变化后Vue已经完成了相应的DOM更新。
需要注意的是nextTick 方法是异步执行的因此不能保证回调函数会立即执行。如果需要等待nextTick执行完成可以使用await关键字或者. then()方法来等待Promise的完成。
17. 一个页面多个表格,列的展示会互相影响
[info] 相关issue
https://github.com/jeecgboot/jeecgboot-vue3/issues/1064
[info]问题截图
[info] 解决方案在不同的tableProps下设置不同的checkKey即可解决
tableSetting: { cacheKey: 'depart_user_departInfo' },
18. 通过npm install启动报错

可以使用这个命令:
npm install --ignore-scripts

View File

@ -1,8 +1,8 @@
//package org.jeecg.modules.demo.cloud.controller;
//
//import com.alibaba.csp.sentinel.annotation.SentinelResource;
//import io.swagger.v3.oas.annotations.tags.Tag;
//import io.swagger.v3.oas.annotations.Operation;
//import io.swagger.annotations.Api;
//import io.swagger.annotations.ApiOperation;
//import lombok.extern.slf4j.Slf4j;
//import org.jeecg.common.api.vo.Result;
//import org.jeecg.common.system.api.ISysBaseAPI;
@ -18,7 +18,7 @@
// *
// */
//@Slf4j
//@Tag(name = "【微服务】单元测试")
//@Api(tags = "【微服务】单元测试")
//@RestController
//@RequestMapping("/test")
//public class JcloudDemoFeignController {
@ -34,7 +34,7 @@
// */
// @GetMapping("/callSystem")
// //@SentinelResource(value = "remoteDict",fallback = "getDefaultHandler")
// @Operation(summary = "通过feign调用system服务")
// @ApiOperation(value = "通过feign调用system服务", notes = "测试jeecg-demo服务是否通过fegin调用system服务接口")
// public Result getRemoteDict() {
// List<DictModel> list = sysBaseApi.queryAllDict();
// return Result.OK(list);
@ -48,7 +48,7 @@
//// * @return
//// */
//// @GetMapping("/callErp")
//// @Operation(summary = "测试feign erp")
//// @ApiOperation(value = "测试feign erp", notes = "测试feign erp")
//// public Result callErp() {
//// log.info("call erp 服务");
//// String res = erpHelloApi.callHello();

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