Compare commits

..

646 Commits

Author SHA1 Message Date
thinkgem
73c52bbd1f update README.md 2025-08-19 00:27:09 +08:00
thinkgem
63773c97a5 完善xss过滤表达式,避免出现data: 2025-08-18 23:06:15 +08:00
thinkgem
6b75fe67af 5.13.0 2025-08-18 17:10:05 +08:00
thinkgem
65aa023c92 优化 toArray 预设大小的数组替换为零长度数组 2025-08-14 21:52:00 +08:00
thinkgem
7f77715cc0 update logger 2025-08-14 21:13:23 +08:00
thinkgem
c146e0e101 取消 sys:stste 权限 2025-08-14 12:53:01 +08:00
thinkgem
11478921a6 拼写更正 Templete to Template 2025-08-14 12:47:22 +08:00
thinkgem
c5bd0bc597 增加子表类型生成;表格组件和表单组件,完善泛型支持; 2025-08-13 21:48:55 +08:00
thinkgem
b797bb99d0 代码优化 2025-08-08 17:44:31 +08:00
thinkgem
fc37b649b1 form:fileupload 组件 preview 参数优化,默认使用 file.preview 配置文件参数,方便全局设置。 2025-08-07 18:14:57 +08:00
thinkgem
fd3a334e32 更新jdbc驱动版本 2025-08-06 16:04:03 +08:00
thinkgem
f48a2b2aa8 spring ai 1.0.0 release 2025-08-04 18:20:41 +08:00
thinkgem
dd7a1bd539 spring 3.5.4 2025-08-04 17:52:28 +08:00
thinkgem
0f2c22dbd9 update productVersion 2025-08-04 17:50:14 +08:00
thinkgem
b6fff119e0 优化提示信息 2025-08-01 10:51:25 +08:00
thinkgem
1d3b3a5f9d spring boot 3.4.7 shiro 2.0.5 druid 1.2.27 2025-07-31 12:07:46 +08:00
thinkgem
7e6426adea next version 2025-07-29 09:52:45 +08:00
thinkgem
daae9f05a6 vue生成模版增加list查询条件初始化 2025-07-28 12:02:59 +08:00
thinkgem
615448c2ce 优化 spring boot 3.4 jta 事务 2025-07-28 12:01:43 +08:00
thinkgem
5bf6c026f0 模版生成增加application.assistant.yml文件 2025-07-21 18:53:40 +08:00
thinkgem
c887260f40 update README.md 2025-07-10 10:05:33 +08:00
thinkgem
bc05da3bd2 完善beetl的xss格式化,默认使用非html文本处理 2025-07-09 13:38:25 +08:00
thinkgem
3585737d21 完善xss正则表达式,处理on前面是/的问题;完善beetl的xss格式化,默认使用非html文本处理 2025-07-09 13:03:17 +08:00
thinkgem
da999aa4e6 Recover 修改个人信息后跳转目标页面不对问题 2025-07-09 11:03:26 +08:00
thinkgem
8d62eb25b2 5.12.1 2025-07-08 00:30:49 +08:00
thinkgem
3d06b8d009 调整默认的允许重定向地址,只允许项目内部跳转 2025-07-08 00:22:45 +08:00
thinkgem
1c5e49b081 禁用UEditor图片抓取器,对CMS模版查看的地址进行限定 2025-07-08 00:22:44 +08:00
thinkgem
b522b3ff1e 5.12.1 2025-07-07 18:29:36 +08:00
thinkgem
743dcc6f4c 新增 SHA-256 工具,使用 ShaUtils 替代 Sha1Utils 2025-07-07 08:55:36 +08:00
thinkgem
7de53c00ef 代码生成模版,优化 useI18n 参数 2025-07-05 16:31:07 +08:00
thinkgem
c0f02b4599 左树右表,切换树节点的时候定向到列表第一页 2025-07-04 15:01:33 +08:00
thinkgem
278f67fe72 代码生成使用简化请求路径时,api文件名中不含模块名。 2025-07-03 16:49:44 +08:00
thinkgem
d41545f2b0 update yml 2025-07-03 14:01:17 +08:00
thinkgem
bbbace28ea 默认使用oracle ojdbc8驱动 2025-07-03 13:47:34 +08:00
thinkgem
27447c2479 update 2025-07-01 12:34:13 +08:00
thinkgem
3107ab8f3a 代码生成模版支持多级模块设置,如模块管理:ai-core;功能生成模块名:ai.core;新增几个模版参数:moduleMinus、modulePath、subModuleNameDot、subModulePath 2025-06-30 11:19:48 +08:00
thinkgem
63dd5c161d 修正beetl修改密码后再登录系统,没有跳转到首页问题 2025-06-30 11:19:38 +08:00
thinkgem
511d76e03f 生成模版优化 2025-06-27 17:18:04 +08:00
thinkgem
a683a9ed5c vue主键为输入框的时候,不验证主键重复问题 2025-06-27 17:13:56 +08:00
thinkgem
e64b1d3b49 update 2025-06-27 14:53:51 +08:00
thinkgem
3b7ef10492 ztree 当没有title数据的时候,使用name作为title显示 2025-06-27 09:22:38 +08:00
thinkgem
b17c58e792 优化 默认系统提示词和模板 2025-06-23 14:21:25 +08:00
thinkgem
6faa39ebea 代码优化 2025-06-23 13:59:02 +08:00
thinkgem
32f4d7e0ae 增加 spring.cache.globalKeyNames 参数,指定哪些为全局缓存,节省资源 2025-06-23 09:37:24 +08:00
thinkgem
fbf25775b3 update README.md 2025-06-23 08:28:13 +08:00
thinkgem
0732be618e 修正请求对象被回收提示 2025-06-21 19:29:34 +08:00
thinkgem
3e16c27442 update ai README.md 2025-06-20 19:16:21 +08:00
thinkgem
2dea278a45 Merge branch 'v5.springboot3' into v5.springboot3.temp 2025-06-19 15:39:05 +08:00
thinkgem
02adbd3af3 update 2025-06-19 15:04:00 +08:00
thinkgem
f1281b73cc 新增vue前端模块代码生成模板 2025-06-18 11:03:01 +08:00
thinkgem
5fc096ab0a 通过yml开关控制使用哪些模型和向量库 2025-06-18 10:59:55 +08:00
thinkgem
28293383dd 代码优化 2025-06-10 11:47:00 +08:00
thinkgem
7459bc220c lint type 2025-06-08 10:42:18 +08:00
thinkgem
a81297c2be 代码生成模板默认替换为 Monorepo 2025-06-08 10:38:42 +08:00
thinkgem
cb5957ee61 add AiChatServiceTest 2025-06-07 16:11:53 +08:00
thinkgem
b55467312f 增加ureport接口权限配置 2025-06-06 15:03:21 +08:00
thinkgem
a567c82ef2 增加JsonFormat条件防止出现类型转换问题 2025-06-05 13:04:51 +08:00
thinkgem
2f6530e725 update jdbc driver 2025-05-27 23:36:54 +08:00
thinkgem
e1d8548e82 5.12.0 2025-05-26 10:18:57 +08:00
thinkgem
3c295c86b8 新增用户界面的时区切换 2025-05-26 09:42:59 +08:00
thinkgem
9118907838 JsonMapper日期类型转换代码优化 2025-05-24 18:42:12 +08:00
thinkgem
1b465578b6 新增 LocaleUtils 本地化语言和时区对象管理工具类 2025-05-24 18:41:46 +08:00
thinkgem
d78b7f3c17 DateUtils.parseDate 多格式字符串解析性能优化,并支持时区 2025-05-24 18:41:08 +08:00
thinkgem
4726020b54 更新语言包 2025-05-24 18:36:33 +08:00
thinkgem
eaa25b7975 beetl 日期格式化,使用性能更好的 DataUtils 管理 2025-05-24 18:34:29 +08:00
thinkgem
01690c76cd 代码优化 2025-05-24 16:59:23 +08:00
thinkgem
6f05485e8f 向量库可选,在pom里去掉向量库实现即可 2025-05-23 19:01:15 +08:00
thinkgem
4e5d776104 添加 spring-boot-devtools 配置 2025-05-21 11:00:28 +08:00
thinkgem
b0e6664909 添加达梦和金仓数据库的参数 2025-05-21 10:59:03 +08:00
thinkgem
25574c1a54 新增 AI 结构化输出例子、文本格式、JSON格式、Java对象格式、结合Tool Calling方式输出 2025-05-19 17:15:51 +08:00
thinkgem
b18dcb3001 新增 AI 结构化输出例子、文本格式、JSON格式、Java对象格式、结合Tool Calling方式输出 2025-05-19 17:15:33 +08:00
thinkgem
228334faf2 代码格式化 2025-05-19 10:46:49 +08:00
thinkgem
fc3efc6819 JsonMapper 使用 DateUtils 解析日期,以支持更多的格式;优化 JsonFormat 注解非日期类型的自定义 2025-05-19 10:44:07 +08:00
thinkgem
cfca3406a1 移除 web.json.defaultDateFormat 参数,使用 DateUtils 解析,以支持更多的格式 2025-05-19 10:41:47 +08:00
thinkgem
a867020070 DateUtils 增加 yyyy-MM-dd'T'HH:mm:ss'Z' 格式解析 2025-05-19 10:38:33 +08:00
thinkgem
f063347492 添加 @Serial 序列化注解 2025-05-18 23:20:00 +08:00
thinkgem
8c5cefb626 修正分离版下新增空白页问题 2025-05-18 23:10:59 +08:00
thinkgem
ca54e4cd3b 添加 @Serial 序列化注解 2025-05-17 22:14:14 +08:00
thinkgem
687a02b199 update jeesite-cms-ai.yml 2025-05-15 16:03:02 +08:00
thinkgem
d4f2c1ddb7 优化文章附件读取,如果html附件,则保留格式 2025-05-15 15:22:26 +08:00
thinkgem
375fbbe375 代码优化 2025-05-15 11:06:50 +08:00
thinkgem
1b21375e1c 升级 Spring AI 1.0.0-RC1、Chroma 1.0.0 2025-05-14 21:48:29 +08:00
thinkgem
723a088eed 避免URL带#号导致的错误 2025-05-14 21:47:45 +08:00
thinkgem
7bd5c9eed0 spring boot 3.4.5、mybatis 3.5.19、jsqlparser 4.9、quartz 2.5.0、liquibase 4.31.1、等等 2025-05-12 11:25:40 +08:00
thinkgem
bc15521b3a 增加 defaultPromptTemplate 默认问题回答模板参数 2025-05-12 11:24:04 +08:00
thinkgem
c3e2f6fabb 代码优化支持reasoning_content为空或空字符串的时候结束思考 2025-05-12 11:22:19 +08:00
thinkgem
a1b5a868ec 默认包含达梦和人大金仓驱动,减少咨询,由于个别jdbc驱动体积比较大,可按需移除依赖 2025-05-12 11:09:56 +08:00
thinkgem
1794d086b4 upgrade seata 2.3.0 2025-05-12 11:04:50 +08:00
thinkgem
8789c83942 FastJSON 2.x 只有个别第三方在使用,1.x 已经没有再用了,代码移除 2025-05-12 11:02:30 +08:00
thinkgem
681f4e7510 当已知异常信息包含更多异常的时候,将异常信息打印到日志中 2025-05-09 17:49:14 +08:00
thinkgem
d30b5f60d7 fileAllowSuffixes add .ofd 2025-05-09 14:29:33 +08:00
thinkgem
179ec878ea 升级 spring ai 1.0.0-M8 2025-05-08 11:52:18 +08:00
thinkgem
95ab40a14f 增加 ai 异步任务池定义 2025-05-08 11:51:52 +08:00
thinkgem
31eab113a2 顶部消息框优化 2025-05-08 09:52:04 +08:00
thinkgem
6b4a69453d next 2025-05-08 09:46:25 +08:00
thinkgem
349757216e 表单界面优化 2025-05-07 14:55:12 +08:00
thinkgem
cc31d01f57 增加参数 mybatis.orderBySqlFilter 仅对内部参数设置生效,UI传输的数据仍然必须经过过滤。 2025-05-07 12:19:34 +08:00
thinkgem
9c78772e1f 一级菜单美化,圆角展示选中背景色 2025-05-06 15:07:49 +08:00
thinkgem
9eb21e0d80 切换主题时,刷新当前页,不跳转到首页 2025-05-06 15:06:11 +08:00
thinkgem
1f9af0706d update DaoMapperTest.java 2025-04-29 15:43:09 +08:00
thinkgem
885a69f9c8 移动端代码生成模板增强 2025-04-27 22:15:49 +08:00
thinkgem
91ded3eac6 移动端模板补充日期时间组件生成 2025-04-27 15:39:04 +08:00
thinkgem
864e996f64 修正移动端模板的一些问题,textarea v-model 重复问题;maxlength 为 0 问题;列表 query 参数不对问题 2025-04-27 14:24:04 +08:00
thinkgem
7358034209 增加 按多个角色查询用户接口 user.roleCodes 2025-04-25 18:47:37 +08:00
thinkgem
ad93ac861d 当选择控件未设置查询列的时候没有生成office接口问题 2025-04-25 18:47:21 +08:00
thinkgem
70dec92cf8 update README.md 2025-04-24 08:15:29 +08:00
thinkgem
e4d695ef93 代码生成模板类型错误 2025-04-23 17:24:58 +08:00
thinkgem
0b7d490a8a 5.11.1 2025-04-23 09:42:10 +08:00
thinkgem
75da09424a update README.md 2025-04-23 09:00:48 +08:00
thinkgem
d877b8d93b update README.md 2025-04-23 09:00:40 +08:00
thinkgem
d03ed31483 默认系统提示词,放到 yml 中配置,并完善注释 2025-04-21 19:41:52 +08:00
thinkgem
4c5c0e63d5 补充系统提示词 2025-04-21 16:32:59 +08:00
thinkgem
7c65d67b3e update 2025-04-21 16:32:35 +08:00
thinkgem
ea1744afda 健壮性增强 2025-04-21 13:40:20 +08:00
thinkgem
5ad7114d89 删除用不到的文件 2025-04-21 13:38:42 +08:00
thinkgem
1b8b6162f4 1. 主动停止的响应的消息仍然存储对话数据;2. 对话消息框当手动向上滚动的时候,停止滚动到底部,方便阅读已生成的消息;3. 消息内容,对 JSON 格式的数据,进行格式化显示;4. 支持“深度思考”按钮,可展开和折叠深度思考的消息;对 AI 的用户消息进行转义,避免不会支持的消息报错;5. 当 AI 接口调用异常的时候,给于用户提示实际的接口返回内容;6. 新增自动更新对话标题;6. 加载消息过程中,避免再次发送新消息和切换对话。 2025-04-21 13:24:23 +08:00
thinkgem
695762b34c 升级 spring ai 1.0.0-M7 2025-04-21 13:23:08 +08:00
thinkgem
8f2444fdbb 新增 web-ai 项目,方便研发 2025-04-21 13:22:44 +08:00
thinkgem
47b3d918eb 界面优化 2025-04-19 09:58:42 +08:00
thinkgem
79ab80062d add ai menu 2025-04-18 19:30:16 +08:00
thinkgem
f4c5910758 整理初始化数据库脚本菜单,扩展模块移到扩展菜单里。 2025-04-18 15:38:35 +08:00
thinkgem
661fea7d29 update Dockerfile 2025-04-17 16:15:53 +08:00
thinkgem
b8a6f215e6 update docker-build.sh 2025-04-17 14:00:37 +08:00
thinkgem
77b38b31d9 调整属性文件加载顺序,classpath:application.yml 优先于 classpath:config/application.yml(升级注意) 2025-04-17 09:07:50 +08:00
thinkgem
5580deb796 update h2 2025-04-17 09:06:23 +08:00
thinkgem
70bb0c1d57 代码优化 2025-04-17 09:05:55 +08:00
thinkgem
731599ac70 优化界面 2025-04-15 12:43:27 +08:00
thinkgem
adae0af8d6 表格操作列图标更新、默认按钮美化、表格设置按钮更新 2025-04-14 12:59:09 +08:00
thinkgem
00a260028f 弹窗消息透明度修改为0.9 2025-04-14 12:55:31 +08:00
thinkgem
3e0a5abcda 新增 Markdown 编辑器,文件上传接口 2025-04-09 15:07:55 +08:00
thinkgem
228b507f12 增加文章附件解析,存入向量数据库。 2025-04-08 15:38:25 +08:00
thinkgem
e69cc355b3 update ds_pgvector 2025-04-08 15:37:45 +08:00
thinkgem
9684e75697 HttpClientUtils增加文件流下载方法 2025-04-08 15:35:59 +08:00
thinkgem
48e639f5d4 update PropertiesUtils 2025-04-07 18:39:24 +08:00
thinkgem
c0e40dfab0 remove SchemeHttpsConfig 2025-04-07 18:38:42 +08:00
thinkgem
1f7ddcf1cc 支持 dataSourceNames 免配置,自动读取 jdbc 的属性来加载多数据源 2025-04-07 18:37:04 +08:00
thinkgem
cac28a3d57 代码生成模板中增加重启流程操作 2025-04-07 12:05:03 +08:00
thinkgem
282050262c webp格式上传,默认参数少个点 2025-04-07 11:54:13 +08:00
thinkgem
b2545d4ae2 启用岗位角色权限权限后,给予操作提示 2025-04-04 17:19:00 +08:00
thinkgem
7a2850c3ff 增加参数:登录后激活当前用户所在的主部门,默认为不激活即混合权限 2025-04-04 10:48:36 +08:00
thinkgem
6715af11da 优化 DataScope 数据权限,移除 apiMode 参数,增加 apiMode 方法;
优化 DataScope 数据权限,增加 debug、joinMode、ruleSql 方法,方便动态调用
优化 扩展查询 SQL 实现,使用 sqlMap().getColumn().addExtSql 替代 extColumnKeys;
优化 扩展查询 SQL 实现,使用 sqlMap().getTable().addExtSql 替代 extFormKeys;
优化 扩展查询 SQL 实现,使用 sqlMap().getWhere().addExtSql 替代 extWhereKeys;
新增 扩展更新 SQL 语句,sqlMap().getUpdate().addExtColumnSql 和 addExtWhereSql
2025-04-02 16:39:01 +08:00
thinkgem
c0637ebf0d 优化 SqlMap 性能,增加 update 和 insert 生成参数接口,方便在 Mapper 中直接引用。 2025-04-01 11:57:35 +08:00
thinkgem
9bc8d5dd55 update title 2025-03-31 11:27:34 +08:00
thinkgem
38a56ab404 优化树表性能,左树右表联动时,减少一次查询 2025-03-31 09:42:28 +08:00
thinkgem
029f6906e4 查询条件如区域、公司、配置、机构、岗位、栏目、站点、分类等显示编码使用 like 查询,等其它功能查询细节优化 2025-03-30 15:04:05 +08:00
thinkgem
d2b790702b 优化树表管理代码生成模板,左树右表代码生成模板 2025-03-29 22:22:03 +08:00
thinkgem
f1a3dab555 /test/testTree/list to /test/testTree/index 2025-03-29 22:08:16 +08:00
thinkgem
cf7f27d3b0 EncodeUtils增加手机号和邮箱的掩码处理方法 2025-03-29 22:05:34 +08:00
thinkgem
2905763862 testTree增加index,左树快捷选择 2025-03-29 22:04:49 +08:00
thinkgem
0e0ee1d7d3 Tool Calling、Function Calling 2025-03-29 11:31:44 +08:00
thinkgem
5b6d7de04a 支持机构子节点数据的机构负责人和电话的模糊查询 2025-03-29 11:15:47 +08:00
thinkgem
cd5af0ab78 更正机构导出模板的显示标题 2025-03-29 11:14:32 +08:00
thinkgem
2931cc52bb 新增 Tool calling,让 AI 调用你的 java 实现你的业务联动 2025-03-29 11:08:35 +08:00
thinkgem
52c62be658 公司和部门管理列表,根据代码模糊查询时,可以查出子节点 2025-03-28 10:22:01 +08:00
thinkgem
6324fa15ae HttpClientUtils 使用 jdk 自带的 http5 替代 apache httpclient 工具 2025-03-28 10:21:37 +08:00
thinkgem
c7cc1343c8 优化外部调用接口调用测试类 2025-03-27 14:15:30 +08:00
thinkgem
11524982e5 EncodeUtils.encodeUrl 忽略字符编码传递空值,默认 UTF-8 2025-03-27 14:14:31 +08:00
thinkgem
54656cb441 点击右上角【Star】收藏本软件 ^_^ 2025-03-27 09:06:52 +08:00
thinkgem
05fc17bd5e next version 2025-03-27 08:59:25 +08:00
thinkgem
97eb30fc11 update README.md 2025-03-25 14:15:51 +08:00
thinkgem
3fda79ec8d update README.md 2025-03-25 13:23:43 +08:00
thinkgem
a08a4cf06c 5.11.0 2025-03-24 14:50:19 +08:00
thinkgem
0264dec97b 新增 sys.office.notAllowDeleteIfUserExists 和 sys.company.notAllowDeleteIfUserExists 参数,增加删除机构和公司数据的时候校验是否有用户 2025-03-24 14:03:58 +08:00
thinkgem
500cbf8268 清理组织和公司下的用户缓存,包含子机构 2025-03-24 14:02:46 +08:00
thinkgem
82732c2993 update README.md 2025-03-24 13:59:55 +08:00
thinkgem
69a1941be6 update productVersion 2025-03-24 13:59:00 +08:00
thinkgem
80ffeac471 update 2025-03-22 18:51:47 +08:00
thinkgem
8826dc3259 update README.md 2025-03-22 18:51:19 +08:00
thinkgem
b87fb6a07f 网站优化一些基础样式 2025-03-22 18:48:46 +08:00
thinkgem
e9ba5b96f2 文章按权重排序修复 2025-03-22 18:48:23 +08:00
thinkgem
0ee7337789 AI 增加 Chroma 向量库 2025-03-20 21:30:49 +08:00
thinkgem
1356893d4e update 2025-03-20 12:07:15 +08:00
thinkgem
080b48559f 新增CMS+RAG+AI知识库模块/向量数据库检索增强生成及人工智能对话 2025-03-19 12:39:51 +08:00
thinkgem
af61f14a9a add job init remarks 2025-03-17 15:46:01 +08:00
thinkgem
b53e257085 增加首列title属性 2025-03-13 12:58:08 +08:00
thinkgem
988eac224d update README.md 2025-03-11 22:21:50 +08:00
thinkgem
186d18c160 新增内容管理数据到向量数据库的集成接口 2025-03-11 15:27:11 +08:00
thinkgem
132f814c2d 属性获取工具,支持从系统环境变量中获取参数 2025-03-11 15:22:55 +08:00
thinkgem
f8736420de update README.md 2025-03-11 15:22:34 +08:00
thinkgem
6dca1ded69 jeesite-*.yml模块配置文件,增加IDEA提示助手 2025-03-08 10:11:30 +08:00
thinkgem
0b3b0731ff 修正代码生成微服务模板缺少配置文件问题 2025-03-07 16:23:08 +08:00
thinkgem
5bcc66d3f7 update 2025-03-04 13:03:00 +08:00
thinkgem
c14f464c3f 修正安全管理器限制的本地调用 2025-03-04 09:50:57 +08:00
thinkgem
75e6ccf888 账号找回密码增加租户参数 2025-03-02 21:03:07 +08:00
thinkgem
3bf68070fe 登录界面租户框调整到上面 2025-02-28 16:55:07 +08:00
thinkgem
0374499e8a update config 2025-02-28 13:35:17 +08:00
thinkgem
0b6a1157cc 个人中心代码优化 2025-02-26 14:19:19 +08:00
thinkgem
d76f991a9f next 2025-02-24 09:25:02 +08:00
thinkgem
00ec4d17c9 新增字段权限接口,过滤 select 返回值,update 和 insert 字段值 2025-02-21 15:52:06 +08:00
thinkgem
de33f20797 增加更严格的权限控制,对单条数据进行数据权限过滤 2025-02-21 15:47:01 +08:00
thinkgem
5f56575df4 AppComment合并更新状态方法 2025-02-21 15:45:14 +08:00
thinkgem
4cade5fa31 优化 编辑公司时,清理公司下的用户缓存 2025-02-20 17:50:02 +08:00
thinkgem
de2c13279b 优化 编辑机构时,清理机构下的用户缓存 2025-02-20 17:49:51 +08:00
thinkgem
87116f0fc7 新增 findUserListByCompanyCodes 方法,根据公司编码获取用户编码和名称 2025-02-20 17:48:48 +08:00
thinkgem
3e68536eea fileupload bizType参数为空时,readonly不生效问题 2025-02-20 17:47:50 +08:00
thinkgem
6f55139127 update README.md 2025-02-17 10:49:27 +08:00
thinkgem
4407d979e6 点击右上角【Star】收藏本软件 ^_^ 2025-02-17 10:48:51 +08:00
thinkgem
ddac21b170 5.10.1 2025-02-15 23:18:09 +08:00
thinkgem
94c5ad6e06 5.10.1 2025-02-15 21:17:12 +08:00
thinkgem
d429dde8c5 自定义数据权限分配树,默认展开等级设为1 2025-02-15 16:41:29 +08:00
thinkgem
9c5db77a65 登录后不指定默认当前部门编号 2025-02-15 16:40:28 +08:00
thinkgem
dddad4d113 更新 visual 类名 2025-02-14 22:04:00 +08:00
thinkgem
0ff7e3bf8f 添加 文档接口响应参数 2025-02-13 18:36:35 +08:00
thinkgem
bfc9953b32 新增 条件规则数据权限、自定义SQL数据权限,暂存 2025-02-12 18:40:12 +08:00
thinkgem
4a3604169c 优化 二级管理员参数提示信息 2025-02-12 18:17:06 +08:00
thinkgem
8630ec6e71 remove aria-hidden="true" 2025-02-12 18:16:01 +08:00
thinkgem
f063d6327b 新增 按菜单进行数据权限授权,后端通过addFilterByPermission方法调用权限。 2025-02-11 14:29:07 +08:00
thinkgem
8b97b011f8 新增 user.dataScopeJoinMode 参数,默认关闭 JOIN 模式的数据权限,如有需要可打开此参数 2025-02-11 12:50:46 +08:00
thinkgem
04cbc2a4fe 新增 列名 columnName 正则表达式的安全过滤 2025-02-11 12:46:53 +08:00
thinkgem
46fd24c819 update 2025-02-11 12:44:10 +08:00
thinkgem
46bdef5058 增加默认值 2025-02-07 20:44:22 +08:00
thinkgem
1fd1b3310f 更新配置文件 2025-02-07 17:22:55 +08:00
thinkgem
cc9add884d 代码优化 EmpUtils 增加 getCompanyCodes、getCompanyParentCodess 方法 2025-02-07 17:11:11 +08:00
thinkgem
b59d91ea4e 将 router.push 替换为 useGo 增加代码健壮性 2025-02-07 16:03:33 +08:00
thinkgem
9e728c5608 add ObjectUtilsTest.java 2025-02-05 21:10:23 +08:00
thinkgem
05185bb29a 修正String转Long丢失精度问题 2025-02-05 21:10:00 +08:00
thinkgem
35c6dabab3 update config 2025-02-05 20:54:12 +08:00
thinkgem
4de0934e7f 新增 切换部门功能、多部门岗位权限 2025-02-01 16:22:15 +08:00
thinkgem
b12c010cd3 优化 查询当前员工关联的岗位信息和附属机构信息接口 2025-01-28 11:47:05 +08:00
thinkgem
16ba930729 新增 获取当前用户岗位列表 EmpUtils.getEmployeePostList 缓存接口 2025-01-28 11:45:26 +08:00
thinkgem
f717a5230a 新增 岗位查询角色接口新增 postCode_in 查询条件 2025-01-28 11:44:18 +08:00
thinkgem
e47cc42da8 丰富 EmployeeOffice 返回结果 2025-01-28 11:43:00 +08:00
thinkgem
ca0fb1d2cb 新增 form:treeselect 组件 fieldNames 属性,自定义数据源的字段名 2025-01-28 11:41:09 +08:00
thinkgem
f027edab9a 从LoginController分离出SwitchController类 2025-01-27 23:42:56 +08:00
thinkgem
a578a70a9b 修正 sqlserver 分页时,缺少 order by 的问题 2025-01-21 15:37:29 +08:00
thinkgem
9d2d00bc89 优化 切换当前身份时,自动对应身份的桌面地址 2025-01-20 17:31:16 +08:00
thinkgem
8a9739c362 新增 RSA 非对称加密工具 2025-01-16 17:36:21 +08:00
thinkgem
ad25e79f31 update README.md 2025-01-16 17:27:00 +08:00
thinkgem
b45cabbd25 format code 2025-01-14 10:37:34 +08:00
thinkgem
e9bc20dd9b update MsgPushTest 2025-01-13 15:09:52 +08:00
thinkgem
d18028a247 update README.md 2025-01-10 14:00:47 +08:00
thinkgem
2ace98d3df upgrade h2 to 2.3.232 2025-01-08 17:03:39 +08:00
thinkgem
99f908b338 update useless code 2025-01-08 17:02:56 +08:00
thinkgem
05873ff46d update README.md 2025-01-06 11:22:07 +08:00
thinkgem
a2e7f3f640 mybatis 3.5.16、mybatis spring 3.0.4 2025-01-06 10:26:40 +08:00
thinkgem
24c7d20531 next version 2025-01-06 09:42:21 +08:00
thinkgem
812e3f2a28 自定义 InitBinder 不进行绑定 Header 中的数据 2025-01-04 09:41:19 +08:00
thinkgem
2c59192482 update area data 2025-01-01 19:44:18 +08:00
thinkgem
f31810325a 5.10.0 2024-12-31 11:27:32 +08:00
thinkgem
ac47ac128d update import icon 2024-12-31 10:30:06 +08:00
thinkgem
71ebba0b8f 2025 2024-12-30 14:57:04 +08:00
thinkgem
58e3c0f6e1 update h2db 2024-12-29 16:45:27 +08:00
thinkgem
6350b7d87c update README.md 2024-12-29 16:45:02 +08:00
thinkgem
647c17c350 update bin 2024-12-26 11:43:10 +08:00
thinkgem
79c1213391 上传文件必填时,防止表单验证提示输入历史 2024-12-25 16:20:51 +08:00
thinkgem
49967508f4 update README.md 2024-12-25 15:50:16 +08:00
thinkgem
65b3cb19be update jdbc url 2024-12-25 10:30:54 +08:00
thinkgem
fa1107737a update yml config 2024-12-24 00:15:54 +08:00
thinkgem
f4d3b2016e spring boot 3.4.0 2024-12-23 17:35:32 +08:00
thinkgem
719ad508d0 remove beetl includeJSP 2024-12-23 17:14:12 +08:00
thinkgem
a43f65f4b8 add fury serialize 2024-12-23 17:12:34 +08:00
thinkgem
542c3ff07b update README.md 2024-12-13 12:59:09 +08:00
thinkgem
1dbe3ae0ee update README.md 2024-12-13 10:19:25 +08:00
thinkgem
235966b4a8 增加参考配置,可查看哪些用户的数据,ctrlPermi 更改为 0 后显示 2024-12-11 10:32:41 +08:00
thinkgem
b6e69ea0c3 细节优化,控制业务范围重命名为业务范围 2024-12-11 10:27:57 +08:00
thinkgem
4a233121c9 移除重复参数 2024-12-11 10:23:18 +08:00
thinkgem
23b14b4517 新增BigDecimalType 2024-12-05 11:12:00 +08:00
thinkgem
27254d09b3 update 2024-12-05 11:11:51 +08:00
thinkgem
f2f75aceee update version 2024-12-04 14:30:09 +08:00
thinkgem
bd8bd10ed5 使用对象存储时,文件管理的在线预览文件中的下载地址修正 2024-12-04 14:17:35 +08:00
thinkgem
6984e97cd2 update 2024-11-26 20:10:09 +08:00
thinkgem
ea2a3193ea update README.md 2024-11-24 20:36:58 +08:00
thinkgem
290e8ec31e 5.9.2 2024-11-23 09:55:58 +08:00
thinkgem
30039a35bb update 2024-11-21 09:49:06 +08:00
thinkgem
14f57be279 更新注册提示信息 2024-11-13 17:16:45 +08:00
thinkgem
207897e7be update 2024-11-13 13:47:51 +08:00
thinkgem
df728bc3d6 切换系统时,清除当前岗位和角色状态 2024-11-12 10:55:08 +08:00
thinkgem
3333ad2e10 设备信息字段 device_info 长度调整为 4000 2024-11-11 17:11:33 +08:00
thinkgem
83752a77cb spring boot 3.3.5 2024-11-08 09:52:44 +08:00
thinkgem
14437dfb7a 登录后重定向地址验证,如果是非法地址,则指定默认的登录成功地址 2024-11-07 17:53:43 +08:00
thinkgem
ce1c06eef2 update README.md 2024-11-07 16:13:30 +08:00
thinkgem
a98f8a816a update description 2024-11-07 16:13:26 +08:00
thinkgem
5d16bb8103 update version 2024-11-07 15:28:16 +08:00
thinkgem
48dbec081d Vue登录界面useCorpModel更改为loginCodeCorpUnique 2024-11-07 15:15:48 +08:00
thinkgem
f9648bcaf8 新增 窗口右上角的头像下拉菜单,切换当前用户所属岗位,并联动切换当前菜单权限 2024-11-07 10:26:27 +08:00
thinkgem
68a7c395e0 提升登录性能;支持普通用户升级为超管用户。 2024-11-07 10:22:32 +08:00
thinkgem
0b4bb63dbe 优化 MenuDao.xml 接口 findByUserCode 替换为 findByRoleCodes 升级时注意 2024-11-07 10:19:25 +08:00
thinkgem
03c5d4a051 新增 当前用户 user.getRoleList() 支持返回 岗位关联角色数据 2024-11-07 10:19:13 +08:00
thinkgem
380ee54ae5 新增 岗位关联角色(用户->岗位->角色)权限控制,可通过 user.postRolePermi=true 参数开启 2024-11-07 10:17:24 +08:00
thinkgem
6e3b93f645 增加岗位切换接口;优化登录菜单权限查询,直接通过当前用户角色查询,减少联表,提升查询效率。 2024-11-07 10:15:59 +08:00
thinkgem
3eaa799455 优化 增加岗位保存时清理关联的用户缓存 2024-11-07 10:14:32 +08:00
thinkgem
283d27d0df 优化 超级管理员作为普通员工时的角色数据查询,允许修改超级管理员员工信息,不允许停用禁用删除超管 2024-11-07 10:12:54 +08:00
thinkgem
8c86d1534c 增加手机端单表代码生成模板 2024-11-05 11:26:05 +08:00
thinkgem
40054e4c6e quartz 2.4.0.rc3 2024-11-04 18:14:14 +08:00
thinkgem
2981699bd1 update 2024-11-04 17:52:11 +08:00
thinkgem
3484d93a42 jackson 2.18.1 2024-11-04 17:36:31 +08:00
thinkgem
9a70ddb758 优化 mvc 接受字段类型不一致信息显示,方便调试 2024-11-01 19:59:51 +08:00
thinkgem
805759db01 调整代码生成模板目录 2024-11-01 10:44:03 +08:00
thinkgem
481246f52e 优化表单内表格的行高和边距 2024-11-01 10:43:32 +08:00
thinkgem
ba077ecd41 update 2024-10-25 19:07:18 +08:00
thinkgem
00d3995bd4 5.9.1 2024-10-21 22:34:58 +08:00
thinkgem
ab801bd9cc update app name 2024-10-21 14:12:14 +08:00
thinkgem
d71ad5e140 spring boot 3.2.10 2024-10-10 21:10:45 +08:00
thinkgem
e7177d7f65 编辑表格中被隐藏的旧数据行控件忽略前端校验 2024-10-10 09:03:26 +08:00
thinkgem
8fba82935b update 2024-10-08 16:34:12 +08:00
thinkgem
7d9b643edf 代码生成模板新增vue子表独立组件的生成 2024-10-08 14:13:32 +08:00
thinkgem
2f74e04645 代码生成支持带减号的工程模块名生成工程和微服务工程 2024-10-08 14:08:23 +08:00
thinkgem
1e723aae04 驼峰命名法工具增加减号替换 2024-10-08 14:04:57 +08:00
thinkgem
330a2977eb 增加启动完成提示信息日志 2024-10-08 14:03:05 +08:00
thinkgem
8167fff4bc 生成前端路径更改为 Vue 前端路径,仅用于分离端的代码生成路径。 2024-09-27 15:09:29 +08:00
thinkgem
6c3ecdac63 代码生成模板新增classNameSimple参数,不带模块名的类名。子表List字段名不带模块名。 2024-09-27 15:08:44 +08:00
thinkgem
874a4465ea fix gen template version 2024-09-24 17:09:57 +08:00
thinkgem
c3e07cd96c 启动日志中显示 tomcat 版本号 2024-09-24 14:02:40 +08:00
thinkgem
509ab88fee 新增监控开关参数 服务器监控 state.server 缓存监控 state.cache 数据监控 state.druid 2024-09-23 13:32:45 +08:00
thinkgem
edce962f2c 文件预览列表增加文件名返回参数 2024-09-23 12:02:14 +08:00
thinkgem
01f599db82 update upbw html 2024-09-22 19:28:23 +08:00
thinkgem
518b386637 update README.md 2024-09-22 17:38:33 +08:00
thinkgem
586a6adf97 5.9.0 2024-09-18 09:45:29 +08:00
thinkgem
495da42f2b 增加 gen.simpleRequestMapping 默认参数 true 可简洁 url 地址 2024-09-18 09:08:33 +08:00
thinkgem
b353477793 fix 代码生成业务列表选择框i18n路径 2024-09-18 09:08:08 +08:00
thinkgem
4d3dd85a1c 数据源管理:新增连接等待时间、连接失败中断默认参数;新增集群支持,单机修改后自动同步到集群其它节点 2024-09-16 22:22:08 +08:00
thinkgem
d7b4c2494d update README.md 2024-09-14 13:05:54 +08:00
thinkgem
0abd8cbd5a update 2024-09-14 13:04:27 +08:00
thinkgem
9355576fad update 2024-09-14 12:54:52 +08:00
thinkgem
f979005e2f 小屏幕下的菜单按钮布局优化 2024-09-13 10:47:28 +08:00
thinkgem
f65c6e0ab5 默认显示列表搜索框 2024-09-13 10:46:36 +08:00
thinkgem
dbafe28918 默认显示列表搜索框 2024-09-13 10:46:19 +08:00
thinkgem
ec47a71e16 默认显示列表搜索框 2024-09-13 10:46:06 +08:00
thinkgem
4b566084e3 默认显示列表搜索框 2024-09-13 10:45:49 +08:00
thinkgem
a4ecd765d4 new version 2024-09-12 10:12:16 +08:00
thinkgem
f3ea863e76 子表增加表单验证提示、代码格式化 2024-09-11 10:16:42 +08:00
thinkgem
7787d7ce0a update README.md 2024-08-29 16:22:51 +08:00
thinkgem
3cfef3c57e 优化导入 2024-08-29 13:12:41 +08:00
thinkgem
8a10f1679a update 2024-08-29 10:31:14 +08:00
thinkgem
f80e1b6c3a 新增支持 Vue 地址路由参数,使用 {param1} 自动替换为 :param1,用法详见 params.vue 2024-08-28 15:09:31 +08:00
thinkgem
49393124ac 新增导出Excel数据按钮loading状态 2024-08-27 14:40:45 +08:00
thinkgem
8e9b9043aa update 2024-08-26 21:42:45 +08:00
thinkgem
a210e7f621 Merge remote-tracking branch 'origin/v5.springboot3' into v5.springboot3 2024-08-22 17:44:11 +08:00
thinkgem
9cf4cd30a2 update README.md 2024-08-22 17:40:04 +08:00
thinkgem
60d6b9c1cf 优化流程表单模板,支持从表单进入显示审批意见框 2024-08-16 18:44:26 +08:00
thinkgem
b499bb951b update 2024-08-16 17:49:20 +08:00
thinkgem
da91070c30 5.8.1 2024-08-16 16:00:36 +08:00
thinkgem
8fd7fcb489 update 2024-08-15 19:59:57 +08:00
thinkgem
cad2ff85ef 5.8.1 2024-08-12 11:17:22 +08:00
thinkgem
8bbd343af4 优化查询的更多按钮,显示一个双箭头,增加动画效果 2024-08-12 10:21:49 +08:00
thinkgem
7b4499135b 添加版本提示 2024-08-09 14:18:09 +08:00
thinkgem
da954bf68d 将账号+手机验证码登录,替换为手机号+验证码登录;当一个手机号绑定多个账号的时候,可以让用户选择登录的账号进行登录;新增userService.findListByMobile接口 2024-08-02 20:01:24 +08:00
thinkgem
6b428bcb50 Excel导入导出支持ExcelFields注解添加到类上 2024-07-28 13:24:25 +08:00
thinkgem
1d94e4511d spring boot 3.2 2024-07-28 13:22:34 +08:00
thinkgem
91b6344479 代码优化 2024-07-26 11:10:04 +08:00
thinkgem
35a32ce799 添加 webp 格式 2024-07-25 11:53:05 +08:00
thinkgem
768c162150 代码优化 2024-07-25 11:36:11 +08:00
thinkgem
f6e598d225 移除fst依赖 2024-07-25 11:35:54 +08:00
thinkgem
5084ac39cf 新增 web.isDefaultResult 参数,默认全局进行接口结果包装为 { code: 200, msg: "", data: {} | [] };新增 web.resultParamName 和 headerParamName 参数,对个别结果进行包装 2024-07-24 14:59:45 +08:00
thinkgem
ea5e164a4e update README.md 2024-07-24 14:54:52 +08:00
thinkgem
fcc53684cd 新增 SM2Utils、SM3Utils、SM4Utils 国密算法工具类及测试类 2024-07-23 19:44:28 +08:00
thinkgem
c53b52f932 新增 encrypt.defaultKey 参数,适用于 Aes、M3 加密的默认秘钥;新增 encrypt.storeBase64 参数,是否将 Hex 编码改为 Base64 编码存储;新增 encrypt.smAlgorithm 参数,是否使用国密 SM 算法(一键替换 SHA-1 替换为 SM3、AES 替换为 SM4 算法) 2024-07-23 19:43:27 +08:00
thinkgem
5f104fce87 新增 encrypt.smAlgorithm 参数,是否使用国密 SM 算法(一键替换 SHA-1 替换为 SM3、AES 替换为 SM4 算法) 2024-07-23 19:41:18 +08:00
thinkgem
a15fb508f9 新增 EncryptTypeHandler、SM4TypeHandler 加密 MyBatis Handler 2024-07-23 19:39:56 +08:00
thinkgem
89d785f04c 新增 encrypt.defaultKey 参数,适用于 Aes、M3 加密的默认秘钥;新增 encrypt.storeBase64 参数,是否将 Hex 编码改为 Base64 编码存储 2024-07-23 19:39:19 +08:00
thinkgem
09b1505120 优化 加密算法工具类,增加测试类。 2024-07-23 19:37:56 +08:00
thinkgem
6561862aae 添加 bcprov 算法类库 2024-07-23 19:37:08 +08:00
thinkgem
e4335eb840 新增 PwdService 服务类方便自定义重写密码相关实现 2024-07-21 17:40:03 +08:00
thinkgem
5e5111523d update 消除干扰 2024-07-21 17:30:17 +08:00
thinkgem
ad893798a5 new version 2024-07-19 08:55:07 +08:00
thinkgem
414f44c338 增加Excel导入导出测试类,增加模板导出。 2024-07-18 15:52:08 +08:00
thinkgem
237ee91644 update README.md. 2024-07-17 08:59:15 +08:00
thinkgem
c0b42921ec add ckplayer 2024-07-17 08:58:33 +08:00
thinkgem
3394ddcc03 修复UEditor上传视频的首图不显示的问题 2024-07-11 10:22:38 +08:00
thinkgem
bfd128f6bd update 2024-07-11 09:47:34 +08:00
thinkgem
1263b18724 update 2024-07-08 17:27:53 +08:00
thinkgem
e942c70553 format pom.xml 2024-07-07 19:10:36 +08:00
thinkgem
3d63ccba2f update sysLogin 2024-07-07 12:26:50 +08:00
thinkgem
7b20b3cc0e poi version 2024-07-07 12:21:21 +08:00
thinkgem
0caf88a0bc 将 test 独立出模块,方便代码管理 2024-07-06 22:21:58 +08:00
thinkgem
5e933119f3 使用 __url 参数统一管理成功跳转地址 2024-07-06 10:23:18 +08:00
thinkgem
b4f3e6b6a2 update 2024-07-04 19:23:19 +08:00
thinkgem
e4fc9057b3 新增附属部门切换接口,方便获取当前默认部门 2024-07-04 16:45:00 +08:00
thinkgem
dd3e52a8ca 5.8.0 2024-07-03 12:54:20 +08:00
thinkgem
e81e548f92 5.8.0 2024-07-01 18:51:42 +08:00
thinkgem
616e2f45cb 匿名类添加过滤器名称,防止被重名覆盖 2024-07-01 18:39:01 +08:00
thinkgem
de312d3ec6 update 2024-07-01 18:38:01 +08:00
thinkgem
41456b8965 RoleService增加findManageMenuList 2024-06-27 15:43:56 +08:00
thinkgem
afe32c5abe 5.8.0 2024-06-26 11:22:13 +08:00
thinkgem
31349430b6 update 2024-06-25 17:13:55 +08:00
thinkgem
11747540ad spring boot 3.2.7、mybatis 3.5.15、jsqlparser 4.7、shiro 2.0.1、tools... 2024-06-25 17:10:07 +08:00
thinkgem
a8ad781197 uploadPath参数增加变量corpCode、userCode、userType、userCache中的key 2024-06-24 11:32:43 +08:00
thinkgem
a8353b654b update sso api success url 2024-06-22 09:05:53 +08:00
thinkgem
66b4932d9d 当没有传递参数类型并有方法重载时,需优先获取无参数的方法名 2024-06-20 17:56:25 +08:00
thinkgem
b50e6005da abc验证的修改会影响租户管理的保存,还原并增加abc123验证 2024-06-13 11:29:12 +08:00
thinkgem
5567e96d65 更新CMS全文搜索模块安装提示 2024-06-03 10:58:21 +08:00
thinkgem
865986da28 消息查看增加空值校验 2024-06-03 10:56:46 +08:00
thinkgem
13d8b8390a next 2024-05-28 15:03:13 +08:00
thinkgem
cc0794c3fc 增加ctxPath参数,当映射根路径不同的时候使用 2024-05-28 14:38:49 +08:00
thinkgem
0a29bed4ad update 2024-05-27 15:30:14 +08:00
thinkgem
5a4c03332a add dbm module 2024-05-27 15:28:32 +08:00
thinkgem
fa6b887ed2 file.uploadPath 增加 {bizType} 变量 2024-05-27 08:34:20 +08:00
thinkgem
d3da625f72 update 2024-05-23 08:51:43 +08:00
thinkgem
c70889eedd get cookie add xss 2024-05-20 10:29:49 +08:00
thinkgem
6c62d191c5 大文件下载 2024-05-17 12:31:05 +08:00
thinkgem
2900fb0ce2 gen template type fix 2024-05-14 22:26:30 +08:00
thinkgem
7597dcd572 update README.md 2024-05-14 19:22:05 +08:00
thinkgem
3430d160bf update README.md 2024-05-14 18:47:33 +08:00
thinkgem
ae63df2d55 update README.md 2024-05-14 15:15:06 +08:00
thinkgem
668042ca1a 5.7.1 2024-05-13 14:51:43 +08:00
thinkgem
3eef280ade 代码优化 2024-05-12 16:00:10 +08:00
thinkgem
f83e3f5e7f skinName add xssFilter 2024-05-11 15:28:12 +08:00
thinkgem
92870be96e biz_category 2024-05-11 14:23:59 +08:00
thinkgem
a3a7bd0230 MsgInnerService抽离接口 2024-05-11 14:23:14 +08:00
thinkgem
d0eba614bd 调整配置文件目录 2024-05-11 13:44:05 +08:00
thinkgem
7f42e99070 update README.md 2024-05-10 22:52:33 +08:00
thinkgem
867ba3933c update 2024-05-10 17:41:32 +08:00
thinkgem
149ca55246 update 2024-05-08 16:47:49 +08:00
thinkgem
15c180440f h2数据表增加备注信息 2024-05-08 15:15:58 +08:00
thinkgem
8053457e17 添加达梦和金仓数据库的驱动注释 2024-05-07 13:58:33 +08:00
thinkgem
9f952f1b5b 字典值dict_value字段存储加长 2024-05-07 13:42:35 +08:00
thinkgem
66740854f6 update 2024-05-07 13:41:20 +08:00
thinkgem
ecb31bf21a bootstrap css 3.4.1 2024-05-07 13:41:01 +08:00
thinkgem
2e8cc49dfa update 2024-05-02 21:17:36 +08:00
thinkgem
97ef1fdec1 update yml help 2024-04-30 17:58:21 +08:00
thinkgem
9decf2ed58 重命名一些CMS模块字段名,避免有些数据库关键字问题 2024-04-29 14:58:38 +08:00
thinkgem
54b9d85b0d 新增业务分类功能维护,作为通用业务分类,替换流程分类 2024-04-27 18:08:31 +08:00
thinkgem
1d1eda1f0a add dbm 2024-04-27 11:27:36 +08:00
thinkgem
65ed0fa550 update 2024-04-27 10:27:05 +08:00
thinkgem
15c440be0a update 2024-04-26 16:14:39 +08:00
thinkgem
dbc4eb9bed 美化表单的子标题缩进一些及其它细节 2024-04-26 08:28:50 +08:00
thinkgem
beb3285a51 添加 IdGen.randomShortString() 方法 2024-04-25 23:31:26 +08:00
thinkgem
1b74cbe4ba update 2024-04-25 23:30:20 +08:00
thinkgem
3fde0742aa 修改前后差异数据表格展示表格美化 2024-04-23 15:39:06 +08:00
thinkgem
71ea34ec0e update 2024-04-23 15:37:32 +08:00
thinkgem
41f1675c2b bpm:comment 组件增加 showCommWords 参数 2024-04-22 20:53:50 +08:00
thinkgem
57deb501ba update 2024-04-21 19:54:19 +08:00
thinkgem
e806f67b9f 后端验证提示信息优化 2024-04-21 19:53:22 +08:00
thinkgem
dc80b74c79 前后差异日志分析,排除@id的属性展示,展示界面美化 2024-04-21 19:52:50 +08:00
thinkgem
a3b4a1fc7d 提高日志标题生成的准确率 2024-04-21 19:49:28 +08:00
thinkgem
660c045965 样式细节优化 2024-04-19 19:04:13 +08:00
thinkgem
5c54c1aae4 后面显示编号的,去掉括号前的空格 2024-04-19 19:03:55 +08:00
thinkgem
132ff7a58d update 排除 char 2024-04-19 19:03:00 +08:00
thinkgem
d26d86805a 新增一些便捷方法,如果有设定属性值再使用 2024-04-18 21:56:23 +08:00
thinkgem
de601181df 新增能接受异常的 Function 2024-04-18 21:54:18 +08:00
thinkgem
cdd95e1ad5 增加 连接池 查询超时时间、事务超时时间 参数 2024-04-18 21:44:59 +08:00
thinkgem
da33d8781b 标题子标题优化 2024-04-18 21:22:44 +08:00
thinkgem
6529177a6b validation abc 验证增加字母验证要求 2024-04-18 21:20:47 +08:00
thinkgem
391e20d6f8 liquibase 4.20.0 2024-04-17 12:18:41 +08:00
thinkgem
4042b8cace 代码优化 2024-04-17 10:27:07 +08:00
thinkgem
cde338b7c2 update format 2024-04-17 09:06:04 +08:00
thinkgem
03f5b373f6 fix crud_cloud_select_vue 2024-04-15 11:16:06 +08:00
thinkgem
586ee75d4a 更新菜单链接地址帮助提示信息 2024-04-13 17:04:33 +08:00
thinkgem
9ef6140be0 如果填写组件路径,则组件名称根据组件路径自动生成,而不是链接地址 2024-04-12 21:25:53 +08:00
thinkgem
56fbc4d52a 样式美化 2024-04-12 18:02:19 +08:00
thinkgem
2f315cd39a dataGrid编辑表格下拉框增加itemTitle属性 2024-04-12 18:00:21 +08:00
thinkgem
66e5bff947 公司部门岗位增加代码模糊查询 2024-04-11 15:55:34 +08:00
thinkgem
21c8fe15b7 json 提交表单,增加 exclude 参数,排除不被序列化的参数 2024-04-11 15:55:06 +08:00
thinkgem
49506fc10e update 2024-04-10 14:17:07 +08:00
thinkgem
57d767226a 更新apiMode提示 2024-04-09 14:47:40 +08:00
thinkgem
0a9007793a data-params to data-url-params 2024-04-09 08:52:38 +08:00
thinkgem
c82b83492d 更新提示 2024-04-08 21:58:31 +08:00
thinkgem
6e3a1476ca update 2024-04-08 14:12:20 +08:00
thinkgem
7abd08f04e 单选框和下拉框控件只显示字典类型的根节点数据 2024-04-08 10:35:37 +08:00
thinkgem
11168f4356 自动排序号优化 2024-04-08 10:24:27 +08:00
thinkgem
6d0d134ea2 update 2024-04-08 10:08:50 +08:00
thinkgem
ac17706fc7 字典数据管理去掉停用启用校验下级的限制,并支持级联更新 2024-04-08 10:00:30 +08:00
thinkgem
0e82478fad new version 2024-04-07 09:24:25 +08:00
thinkgem
f7344d4736 add testResource config 2024-04-05 16:54:07 +08:00
thinkgem
137080b3c0 仅侧边栏时加 first-open 2024-04-05 16:53:17 +08:00
thinkgem
2bf621c0cf update README.md 2024-04-01 12:03:11 +08:00
thinkgem
59e0a80b37 web-fast 默认使用 vue 前端展示 2024-04-01 12:02:24 +08:00
thinkgem
77d4247449 update PageNotFound log level 2024-04-01 12:01:59 +08:00
thinkgem
04028745ad 404 page javax to jakarta 2024-04-01 11:53:14 +08:00
thinkgem
9b9e7a85a6 增加msgview初始菜单地址 2024-04-01 11:52:15 +08:00
thinkgem
a0db2ed70a 代码优化 2024-04-01 11:51:33 +08:00
thinkgem
5dd59473a3 5.7.0 2024-03-28 14:15:58 +08:00
thinkgem
6bd9f8179a update README.md 2024-03-28 14:15:52 +08:00
thinkgem
59e3745580 更新建表语句 2024-03-28 14:14:43 +08:00
thinkgem
9164fe2dc6 vue icon add prefix i- 2024-03-27 11:12:08 +08:00
thinkgem
70f26006da update import 2024-03-27 11:11:42 +08:00
thinkgem
9332517f39 新增IP地址黑白名单过滤器及参数配置 2024-03-25 13:57:26 +08:00
thinkgem
b53789818d 更新注释提示 2024-03-25 13:53:10 +08:00
thinkgem
cc0f5fedb8 update fonts 2024-03-22 21:45:22 +08:00
thinkgem
7696692ad9 访问登录页不生成会话,但要在生成验证码前获取会话,方便验证。 2024-03-22 17:29:53 +08:00
thinkgem
ddaaca6be8 增加根据用户编码查询岗位列表接口 2024-03-22 14:42:57 +08:00
thinkgem
96bd62a375 侧边栏增加是否展开第一个菜单开关 2024-03-22 14:42:16 +08:00
thinkgem
b9f6b93bbe move file 2024-03-22 14:41:32 +08:00
thinkgem
4df4f822bc format code 2024-03-22 14:40:49 +08:00
thinkgem
45b8595122 EmployeePostDao 增加根据用户编码查询岗位 sqlMap().loadJoinTableAlias("p,u") 的时候有效 2024-03-22 14:40:28 +08:00
thinkgem
03a031360c 模块管理增加生成模块的基础路径和模板存储字段,生成模块代码时给于确认提示;微服务下isLoader为true; 2024-03-22 09:16:43 +08:00
thinkgem
c222ab29ff 修正代码生成模板logback-spring-elk和prod.xml文件名 2024-03-22 09:15:42 +08:00
thinkgem
ff58e4f05c update tip 2024-03-21 17:26:34 +08:00
thinkgem
0e95a41edc add getPageList(List<T> list, int pageNo, int pageSize) 2024-03-21 15:46:04 +08:00
thinkgem
680a3f2a8c 分离端调用login和index接口时,同时返回cookie。特殊情况下的iframe的页面会话同步 2024-03-20 14:05:32 +08:00
thinkgem
aa904c5763 update productVersion 2024-03-20 09:39:23 +08:00
thinkgem
293821d0ab fix import 2024-03-15 20:53:12 +08:00
thinkgem
bcedc006e1 返回响应头增加x-token,通过记住我登录系统时使用。 2024-03-13 13:34:24 +08:00
thinkgem
be9326d671 演示模式不允许清理会话和缓存 2024-03-13 13:29:10 +08:00
thinkgem
debc349810 返回响应头增加x-token,通过记住我登录系统时使用。 2024-03-13 13:18:44 +08:00
thinkgem
40cab1f49e 代码生成器config.xml的attrType字典增加attrName,用于选择控件编码和名称获取 2024-03-13 11:35:31 +08:00
thinkgem
14365a6e76 登录超时后不再统计在线人数 2024-03-12 13:20:33 +08:00
thinkgem
6cf4bebebf 缩短Logback日志TRACE_ID的长度 2024-03-12 12:24:19 +08:00
thinkgem
7914b18e20 完善内部消息保存后的提示消息 2024-03-12 12:23:29 +08:00
thinkgem
d0fd74fa51 代码生成模板添加公司选择组件v2 2024-03-11 21:24:26 +08:00
thinkgem
000fadeb95 新增 AesTypeHandler 处理字段数据加密解密或脱敏;新增 JsonTypeHandler 处理字段数据进行 JSON 字符串与 Java 对象 自动互转 2024-03-11 17:02:58 +08:00
thinkgem
d7c9138a31 代码优化 2024-03-11 17:00:36 +08:00
thinkgem
5e4608d486 代码生成模板添加公司选择组件 2024-03-10 10:44:16 +08:00
thinkgem
9415356b20 根据当前用户有的子系统,切换到默认系统下 2024-03-08 13:30:09 +08:00
thinkgem
73a93b2721 update nacos config 2024-03-04 11:50:22 +08:00
thinkgem
d93b0c4814 shardingsphere 5.4.1、shiro 2.0.0 RELEASE 2024-03-04 09:56:03 +08:00
thinkgem
81154d7cfb spring boot 3.1.9 2024-03-01 12:34:44 +08:00
thinkgem
21215850e4 更新Docker多CPU平台架构 2024-02-28 10:34:46 +08:00
thinkgem
1b4a7523f8 fix h2db job sql 2024-02-28 10:07:25 +08:00
thinkgem
68291c61da TimeUtils millisecond to ms 2024-02-27 11:30:48 +08:00
thinkgem
be19b92e82 分页栏居右、布局调整、样式美化 2024-02-23 14:16:34 +08:00
thinkgem
0b6f5e285a next version 2024-02-23 14:01:52 +08:00
thinkgem
2f83793638 5.6.1 2024-02-18 16:01:00 +08:00
thinkgem
11cc857de7 修改默认值,oracle 下默认不启用 maxPoolPreparedStatementPerConnectionSize 可根据需要开启 2024-02-02 16:59:24 +08:00
thinkgem
fe076d7caf update 2024-02-01 09:20:47 +08:00
thinkgem
5db33072ac ueditor黑暗模式下的文字 2024-01-31 12:04:40 +08:00
thinkgem
5813875d6e spring boot 3.2.2 2024-01-31 12:02:22 +08:00
thinkgem
1c4ce3842a 更新路由表单组件 2024-01-30 16:53:32 +08:00
thinkgem
4f722ea1d7 form:fileupload组件默认加dataMap属性 2024-01-30 11:40:39 +08:00
thinkgem
05bdaad7aa 新增 js.ajaxSubmitForm支持application/json提交表单;新增 js.ajaxSubmitJson 方法 2024-01-30 11:38:24 +08:00
thinkgem
af06033521 代码优化 2024-01-26 14:42:47 +08:00
thinkgem
a54344950b ts type check 2024-01-23 17:52:55 +08:00
thinkgem
392dc80617 code format 2024-01-17 16:17:05 +08:00
thinkgem
a32f6b67f5 代码优化,方便自定义内嵌的Web服务 2024-01-15 13:45:51 +08:00
thinkgem
e343bd7ba7 报表增加请求参数的例子 2024-01-10 13:08:10 +08:00
thinkgem
0deacd54bb update 2024-01-10 13:04:28 +08:00
thinkgem
33df1fa37c 增加vue无框架页面的组件类型 2024-01-09 16:43:22 +08:00
thinkgem
faba69ca6d 存储Key增加了默认,个性化时再指定 2024-01-08 15:25:22 +08:00
thinkgem
0b7eb4138e 菜单管理增加无图标的默认图标显示 2024-01-08 15:24:18 +08:00
thinkgem
bdfba55a82 update version 2024-01-08 10:40:08 +08:00
thinkgem
aea8eb2241 5.6.0 2024-01-04 12:06:07 +08:00
thinkgem
f8cf61e02f 5.6.0 2024-01-03 16:58:43 +08:00
thinkgem
8a765082cf 配置类 ShiroConfig 改为 ShiroAutoConfiguration 方便覆写 2024-01-03 08:54:09 +08:00
thinkgem
abb86ed422 分号修正 2024-01-03 08:24:58 +08:00
thinkgem
de4dc8542b update 2024 2024-01-02 10:24:31 +08:00
thinkgem
e3c1f4f860 add ConditionalOnMissingBean 2024-01-02 10:23:31 +08:00
thinkgem
f91b714349 update 2024-01-02 10:22:45 +08:00
thinkgem
31da0c637e select2 i18n fix 2023-12-29 10:55:33 +08:00
thinkgem
93181c921d 代码优化提示 2023-12-28 09:33:26 +08:00
thinkgem
231b8308c6 优化在线用户列表数值类型的排序 2023-12-27 10:47:52 +08:00
thinkgem
1e0c073bb4 组件名称非必填 2023-12-27 10:46:44 +08:00
thinkgem
e250b23084 代码生成模板:新增Vue路由表单、新增Vue弹窗表单、新增仅后端模板 2023-12-24 21:36:17 +08:00
thinkgem
2d78175001 不设置adminPath的时候需要增加oauth2的地址认证排除 2023-12-23 23:01:31 +08:00
thinkgem
4f4bbac861 用户管理增加解冻用户功能提示 2023-12-23 23:00:43 +08:00
thinkgem
758c66e5a5 细节优化 2023-12-23 22:59:24 +08:00
thinkgem
025007203c spring boot 3.2.0、jackson 2.16.0、mybatis 3.5.14、shiro 2.0.0-a4、swagger3 2.2.19 2023-12-13 21:09:23 +08:00
thinkgem
ac32d2f061 升级 elasticsearch 8.11.1 2023-12-13 21:07:34 +08:00
thinkgem
d1973adfba 增加一些vue下用的路由 2023-12-12 09:42:50 +08:00
thinkgem
ae0745954d update 2023-12-12 09:41:55 +08:00
thinkgem
fbf952247d 全栈版本菜单增加组件名称和参数 2023-12-11 10:23:37 +08:00
thinkgem
a07cb38dab BpmButton.vue将提交审批信息移动到提交对话框中 2023-12-07 17:20:48 +08:00
thinkgem
d00f4b6d25 update 2023-12-05 16:30:44 +08:00
thinkgem
cecec315d4 update 2023-12-05 16:13:35 +08:00
thinkgem
95f7987d22 datagrid拖拽行调色 2023-12-03 16:08:20 +08:00
thinkgem
8d87b4142c 代码优化 2023-12-03 15:55:41 +08:00
thinkgem
e32c87d009 vue兼容优化 2023-12-01 10:41:35 +08:00
thinkgem
d931e0f03a 优化vue生成模板 2023-11-28 16:15:21 +08:00
thinkgem
2f80eadd26 优化vue生成模板 2023-11-28 15:46:10 +08:00
thinkgem
601205144f BasicTable子表加TextArea组件 2023-11-27 12:23:12 +08:00
thinkgem
fe5d61e4a6 树表的局部刷新改进,删除、停用、启用、修改父节点的情况下完美体验 2023-11-27 10:12:38 +08:00
thinkgem
934821e8d4 next 2023-11-27 09:11:19 +08:00
thinkgem
20d2cd7408 Layer弹窗后首先关闭获取焦点,防止回车弹出多个 2023-11-22 11:13:54 +08:00
thinkgem
dc6f361891 5.5.2 2023-11-21 21:47:23 +08:00
thinkgem
de03b43367 更新h2的库名 2023-11-20 16:01:15 +08:00
thinkgem
5f208ab60f 添加自动切换到脚本所在目录 2023-11-20 15:45:00 +08:00
thinkgem
9888d2f901 更新docker运行脚本帮助 2023-11-20 15:16:32 +08:00
thinkgem
137c523267 add db docs xlsx png 2023-11-17 11:18:11 +08:00
thinkgem
d4936ac085 增加quartz表ER图和注释 2023-11-16 11:57:14 +08:00
thinkgem
bcbdb18e15 微小Web 支持只连接业务库,不使用自带sys表 2023-11-16 10:22:45 +08:00
thinkgem
d828879568 update 2023-11-15 17:46:58 +08:00
thinkgem
bfd767f65b 增加vue的ListSelect选择框代码生成模板 2023-11-09 11:31:47 +08:00
thinkgem
25a41bd105 Byte格式化默认显示两位小数 2023-11-08 11:47:00 +08:00
thinkgem
1e6779a473 update 2023-11-06 15:38:59 +08:00
thinkgem
fcb42bfda9 修复包名取不到jakarta异常类问题 2023-11-06 15:38:47 +08:00
thinkgem
128690bd2a 小分辨率下菜单排序列不显示省略号,操作列显示水平滚动条 2023-11-01 15:40:00 +08:00
thinkgem
51542c11ed 文件上传支持自定义存储表;将文件管理独立file_upload表,与sys分离。 2023-10-30 08:47:01 +08:00
thinkgem
98876b588e 添加jdbc.connectTimeou和jdbc.socketTimeout连接池参数 2023-10-30 08:46:00 +08:00
thinkgem
00487c463d update version 2023-10-26 11:34:51 +08:00
thinkgem
66b4f61f18 模块名称增加国际化显示参数 2023-10-26 11:32:28 +08:00
thinkgem
8dbbf13520 新增一个日志清理接口,可根据需要创建job调用 2023-10-26 11:28:31 +08:00
thinkgem
80dcbc7bce 新增富文本和头像的对象存储 2023-10-23 10:57:48 +08:00
thinkgem
cdebf586d5 查询和重置增加图标、搜索框布局优化 2023-10-23 10:36:30 +08:00
thinkgem
396464feda 5.5.1 2023-10-16 21:42:47 +08:00
thinkgem
3e3176fc63 update README.md 2023-10-16 20:06:15 +08:00
thinkgem
810ad853e3 update README.md 2023-10-16 09:06:50 +08:00
thinkgem
270afadccc docker update 2023-10-12 21:26:27 +08:00
thinkgem
4beb86c2ea spring boot 3.1.4、shiro 2.0.0-a-3 2023-10-08 17:26:11 +08:00
thinkgem
7c22be7a54 update README.md 2023-10-08 17:22:02 +08:00
thinkgem
37dd13061c 修改erm默认字体大小,适应多操作系统 2023-10-01 16:55:39 +08:00
thinkgem
0b4cadf163 格式化代码 2023-09-28 16:49:01 +08:00
thinkgem
7934e40586 移动到static项目 2023-09-28 16:47:39 +08:00
thinkgem
4e1b7bf17a 开源全部基础功能的前端视图和控制器文件 2023-09-28 16:42:03 +08:00
thinkgem
739a07f0da 开源Beetl全部的表单组件源码 2023-09-28 16:38:38 +08:00
thinkgem
5b619ce6dc 新增Static静态工程,减轻Core,并开源前端组件 2023-09-28 16:36:56 +08:00
thinkgem
6f49a9b5eb opensource layout, strength... 2023-09-28 16:32:53 +08:00
thinkgem
4208223617 优化JsonMapper,增加XSS过滤通用方法 2023-09-27 13:19:43 +08:00
thinkgem
dff27dc659 新增一个极小的内核web服务,1秒启动项目。 2023-09-21 15:30:19 +08:00
thinkgem
81cb9dcbba update README.md 2023-09-21 13:58:34 +08:00
thinkgem
7fa294c450 update version 2023-09-20 11:48:03 +08:00
thinkgem
5db4986d91 update README.md 2023-09-19 14:27:22 +08:00
thinkgem
d91feb0659 update 2023-09-18 10:59:38 +08:00
thinkgem
440bb920b7 linux下如果有前缀就不增加了 2023-09-18 10:59:04 +08:00
thinkgem
95fb0a5764 update script 2023-09-12 10:24:58 +08:00
thinkgem
cc5a1f3a0e add startup info 2023-08-30 15:33:03 +08:00
thinkgem
e51e1c1cd7 snakeyaml 1.33 2023-08-22 17:10:22 +08:00
thinkgem
9c8cfa3f53 5.5.0 2023-08-21 17:09:48 +08:00
thinkgem
7750fb14b9 maven ssl 2023-08-21 13:10:47 +08:00
thinkgem
2ffd3bb7bb update area data 2023-08-21 11:07:18 +08:00
thinkgem
3a1b6314a1 update 2023-08-21 10:37:46 +08:00
thinkgem
924e035074 update 5.5 2023-08-17 14:25:40 +08:00
thinkgem
b2654d3044 角色增加包含系统参数,切换系统时只需展示当前用户包含的子系统 2023-08-16 16:30:42 +08:00
thinkgem
2cfe47fcd0 new version 2023-08-16 10:42:02 +08:00
thinkgem
520dd4694b 隐藏表单右上角按钮 2023-08-14 12:40:35 +08:00
thinkgem
48eb91d65b spring boot 3.1.2 2023-08-14 12:37:26 +08:00
thinkgem
23941a4fb9 升级jquery migrate版本,解决ie9下的一些问题修复 2023-08-09 19:38:10 +08:00
thinkgem
42065195ec downloadByUrl支持post带参数下载文件 2023-08-09 16:45:41 +08:00
thinkgem
9ca999a90a 下载文件增加clearParams参数,清理掉不需要添加的请求参数,如:pageNo 2023-08-09 16:42:15 +08:00
thinkgem
a445e43fbb html和js语法编写不同的占位符,减少IDE标红提示 2023-08-03 17:23:24 +08:00
thinkgem
c47398295b remove web.xml 2023-08-03 11:17:17 +08:00
thinkgem
539ec80a3b 增加缩略图生成设置,前端组件可使用 imageThumbName 使用缩略图 2023-08-02 10:41:50 +08:00
thinkgem
f0814f45d9 ImageUtils 生成缩略图,增加目标文件参数,可以不是原文件 2023-08-02 10:39:31 +08:00
thinkgem
9032d2f98d 修改弹窗表头高度小一点 2023-08-01 11:28:04 +08:00
thinkgem
e6ce5206a4 用户列表选择样式优化 2023-08-01 11:26:55 +08:00
thinkgem
f63a2757b6 online 代码优化 2023-08-01 11:24:38 +08:00
thinkgem
90ccacbb38 update build time 2023-07-30 20:10:42 +08:00
thinkgem
47a4bd31a7 update 2023-07-27 21:44:13 +08:00
thinkgem
84df35df1b update i18n 2023-07-26 16:21:03 +08:00
thinkgem
4ee9342f2d 国际化完善 2023-07-18 11:55:40 +08:00
thinkgem
f6d1d18781 基础圆角调整 antdv4.0 2023-07-17 12:54:58 +08:00
thinkgem
df3af9d059 多语言译文优化 2023-07-17 11:33:43 +08:00
thinkgem
4b2bbff887 将语言名称传递给vue,登录时检测 2023-07-17 10:54:35 +08:00
thinkgem
59bd70328a update readme 2023-07-11 10:49:37 +08:00
thinkgem
2070c33c34 update 2023-07-11 10:15:57 +08:00
thinkgem
99625753ec api update 2023-07-10 17:08:15 +08:00
thinkgem
5ce8523702 update script 2023-07-07 20:52:06 +08:00
thinkgem
a76371e508 update 2023-07-07 19:37:20 +08:00
thinkgem
7659fa0e7d update script 2023-07-07 17:32:31 +08:00
thinkgem
d9ea710e9b update redis config 2023-07-07 17:32:01 +08:00
thinkgem
3271bb3afc springdoc、openapi、swagger v3 2023-07-05 22:38:36 +08:00
thinkgem
b371d685d4 update 2023-07-01 10:30:29 +08:00
thinkgem
f18682a3a9 jta通过 2023-06-29 12:17:05 +08:00
thinkgem
cce67cf0ad update version 2023-06-28 19:50:45 +08:00
thinkgem
b919e68dad spring boot 3.1.1、fst 3.0.4、atomikos 6.0、shiro 2.0.0、jdk17 2023-06-28 19:11:14 +08:00
thinkgem
ca0adaeeed druid jakarta 2023-06-28 19:08:52 +08:00
thinkgem
9b228d7808 update 2023-06-28 19:07:22 +08:00
thinkgem
171194cb6b 增加参数 web.xssFilterExcludeUri 2023-06-28 19:07:01 +08:00
thinkgem
07e2fe4955 update 2023-06-28 17:23:50 +08:00
thinkgem
64d694f025 shiro 2.0 2023-06-28 08:56:06 +08:00
thinkgem
f558bf356d shiro 2.0 2023-06-28 08:55:32 +08:00
thinkgem
aff67ed8fb httpclient add version 2023-06-28 08:54:29 +08:00
thinkgem
b3b5d53763 open module cas 2023-06-28 08:53:48 +08:00
thinkgem
74f72fb2b8 import javax.validation. 替换为 import jakarta.validation. 2023-06-24 14:39:14 +08:00
thinkgem
8e81648bdd import javax.validation. 替换为 import jakarta.validation. 2023-06-24 14:38:38 +08:00
thinkgem
bc0a9f7f80 javax.servlet-api to jakarta.servlet 2023-06-23 18:31:16 +08:00
thinkgem
023743bbe9 import javax.servlet. 替换为 import jakarta.servlet. 2023-06-23 18:30:23 +08:00
thinkgem
4638656f5d prepare springboot3 2023-06-23 18:15:39 +08:00
270 changed files with 7862 additions and 915 deletions

View File

@@ -1,23 +1,25 @@
### 是什么问题、该问题是怎么引起的?
1.
1. 这里替换为你的问题
### 重现步骤、期望结果、截图、代码
1.
1. 这里替换为你的操作步骤,期望结果
```
这里贴你的代码块
```
### 实际结果、报错信息、截图
1.
1. 这里替换为你的操作步骤的实际结果
```
这里贴错误信息
这里贴你的错误信息
```
### 环境版本:
- JDK版本1.8、11、17
- JDK版本1.8、11、17、21
- 浏览器版本Chrome xx、Firefox xx、IE xx
- 平台版本JeeSite 4.x.x、5.x.xpom.xml里查看

View File

@@ -2,7 +2,7 @@
<p align="center">
<img alt="JeeSite" src="https://jeesite.com/assets/images/logo.png" width="120" height="120" style="margin-bottom: 10px;">
</p>
<h3 align="center" style="margin:30px 0 30px;font-weight:bold;font-size:30px;">快速开发平台 - Spring Boot</h3>
<h3 align="center" style="margin:30px 0 30px;font-weight:bold;font-size:30px;">快速开发平台 - Spring Boot 3</h3>
<p align="center">
<a href="https://spring.io/projects/spring-boot" target="__blank"><img alt="SpringBoot-V3.5 or 2.7" src="https://img.shields.io/badge/SpringBoot-V3.5 or 2.7-blue.svg"></a>
<a href="https://v3.cn.vuejs.org/" target="__blank"><img alt="TypeScript-Vue3" src="https://img.shields.io/badge/TypeScript-Vue3-green.svg"></a>
@@ -70,6 +70,8 @@
* 2021 年终发布 Vue3 的前后分离版本,使得 JeeSite 拥有同一个后台服务 Web 来支撑分离版和全栈版两套前端技术栈。
* 对接常见 AI 大模型OpenAPI、Ollama、DeepSeek等支持检索增强生成 RAG 技术,实现企业知识库智能对话。
* 支持国产化软硬件环境,如国产芯片、操作系统、数据库、中间件、国密算法等。
## 核心优势
@@ -95,29 +97,30 @@
## 技术选型
* 主框架Spring Boot 2.7、Spring Framework 5.3、Apache Shiro 1.12、J2Cache
* 持久层Apache MyBatis 3.5、Hibernate Validator 6.2、Alibaba Druid 1.2
* 视图层Spring MVC 5.3、Beetl 3.10替换JSP、Bootstrap 3.3、AdminLTE 2.4
* 前端组件jQuery 3.7、jqGrid 4.7、layer 3.5、zTree 3.5、jQuery Validation
* 分离前端版Node.js、TypeScript、Vue3、Vite、Ant Design Vue、Vue Vben Admin
* 工作流引擎Flowable 6.6、符合 BPMN 规范、在线流程设计器、中国式流程、退回、撤回、自由流
* Bootstrap 版 支持 IE10 及以上版本及其他所有现代浏览器,如:谷歌、火狐、国产浏览器 等
* Vue3 版 支持现代浏览器,如:谷歌 Chrome 86+、火狐、国产浏览器 等
* 技术选型(详细)<http://jeesite.com/docs/technology/>
* JeeSite Vue 版本:<https://gitee.com/thinkgem/jeesite-vue>
* Spring Boot 3.x 版本:<https://gitee.com/thinkgem/jeesite5/tree/v5.springboot3>
* 主框架Spring Boot 3.5、Spring Framework 6、Apache Shiro 2、J2Cache
* 持久层Apache MyBatis 3.5、Hibernate Validator 8、Alibaba Druid 1.2
* 分离版Node.js、TypeScript、Vue3、Vite、Ant Design Vue、Vue Vben Admin
* 经典版Beetl 3.10HTML、jQuery 3.7、Bootstrap 3.3、AdminLTE 2.4
* 分离版:支持所有现代浏览器,如:谷歌 Chrome 86+、微软 Edge、火狐、国产浏览器 等
* 经典版:支持 IE10 和以上版本,以及其他所有现代浏览器,如:谷歌、火狐、国产浏览器 等
* 工作流引擎Flowable 7.1、符合 BPMN 规范、在线流程设计器、中国式流程、退回、撤回、自由流
* 技术选型(详细)已支持数据库:<http://jeesite.com/docs/technology/>
* JeeSite Vue 前后分离版<https://gitee.com/thinkgem/jeesite-vue>
* Spring Boot 3.x 版本:<https://gitee.com/thinkgem/jeesite5/tree/v5.springboot3/>
* Spring Boot 2.x 版本:<https://gitee.com/thinkgem/jeesite5/tree/v5.springboot2/>
## 更多介绍
* 目录结构:<https://jeesite.com/docs/catalog/>
* 内置功能:<https://jeesite.com/docs/function/>
* 架构特点:<https://jeesite.com/docs/feature/>
* 内置功能:<https://jeesite.com/docs/function/>
* 目录结构:<https://jeesite.com/docs/catalog/>
* 参数配置:<https://jeesite.com/docs/config/>
* 开发规范:<https://jeesite.com/docs/standard/>
* 代码生成:<https://jeesite.com/docs/code-gen/>
## 生态系统
* AI + RAG + CMS 人工智能助手:<https://jeesite.com/docs/cms-ai>
* 分布式微服务Spring Cloud<https://gitee.com/thinkgem/jeesite-cloud>
* Flowable业务流程引擎BPM<http://jeesite.com/docs/bpm/>
* 多站点内容管理模块CMS<https://jeesite.com/docs/cms/>
@@ -139,11 +142,11 @@
### 本地运行
1. 环境准备:`JDK 1.8 or 11、17``Maven 3.6+`、使用 `MySQL 5.7 or 8.0` 数据库、[其它数据库](https://jeesite.com/docs/technology/#_8、已支持数据库)
2. 下载源码:<https://gitee.com/thinkgem/jeesite5/repository/archive/v5.9.zip> 并解压
3. 打开文件:`/web/src/main/resources/config/application.yml` 配置JDBC连接
4. 执行脚本:`/web/bin/init-data.bat` 初始化数据库
5. 执行脚本:`/web/bin/run-tomcat.bat` 启动服务即可
1. 环境准备:`JDK 17+``Maven 3.8+`、使用 `MySQL 8.0+` 数据库、[其它数据库](https://jeesite.com/docs/technology/#_8、已支持数据库)
2. 下载源码:<https://gitee.com/thinkgem/jeesite5/repository/archive/v5.springboot3.zip> 并解压
3. 打开文件:`/web/src/main/resources/config/application.yml` 配置JDBC连接(建立一个新库)
4. 执行脚本:`/web/bin/init-data.bat(sh)` 初始化数据库(自动往新库里创建表和初始数据)
5. 执行脚本:`/web/bin/run-tomcat.bat(sh)` 启动服务即可
6. 浏览器访问:<http://127.0.0.1:8980/js> 账号 system 密码 admin
7. 部署常见问题:<https://jeesite.com/docs/faq/>
8. 分离端安装:<https://jeesite.com/docs/vue-install-deploy/>
@@ -151,9 +154,9 @@
### 快速运行
1. 环境准备:`JDK 1.8 or 11、17``Maven 3.6+`、无需准备数据库(使用内嵌 H2 DB、包含 Vue 和 全栈双版本)
2. 下载源码:<https://gitee.com/thinkgem/jeesite5/repository/archive/v5.9.zip> 并解压
3. 执行脚本:`/web-fast/bin/run-tomcat.bat` 启动服务即可(自动初始化库)
1. 环境准备:`JDK 17+``Maven 3.8+`、无需准备数据库(使用内嵌 H2 DB、包含 Vue 和 全栈双版本)
2. 下载源码:<https://gitee.com/thinkgem/jeesite5/repository/archive/v5.springboot3.zip> 并解压
3. 执行脚本:`/web-fast/bin/run-tomcat.bat(sh)` 启动服务即可(无需手动建库,自动初始化数据库)
4. Vue分离版本地址<http://127.0.0.1:8980/vue/login>
5. 全栈版本地址:<http://127.0.0.1:8980/a/login>
6. 初始登录账号超级管理员system 密码admin

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>com.jeesite</groupId>
<artifactId>jeesite-parent</artifactId>
<version>5.13.0-SNAPSHOT</version>
<version>5.13.0.springboot3-SNAPSHOT</version>
<relativePath>../parent/pom.xml</relativePath>
</parent>
@@ -21,8 +21,8 @@
<!-- Servlet Api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
@@ -45,12 +45,12 @@
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<!-- Java serialization -->
<!-- Fury serialize -->
<dependency>
<groupId>de.ruedigermoeller</groupId>
<artifactId>fst</artifactId>
<version>${fst.version}</version>
<groupId>org.apache.fury</groupId>
<artifactId>fury-core</artifactId>
<version>${fury.version}</version>
</dependency>
<!-- Jackson json -->
@@ -87,13 +87,6 @@
<version>${snakeyaml.version}</version>
</dependency>
<!-- Apache HTTP -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>
<!-- Email -->
<dependency>
<groupId>org.apache.commons</groupId>

View File

@@ -15,7 +15,7 @@ import org.apache.commons.text.StringEscapeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;

View File

@@ -18,8 +18,8 @@ import java.util.UUID;
*/
public class IdGen {
private static SecureRandom random = new SecureRandom();
private static IdWorker idWorker = new IdWorker(-1, -1);
private static final SecureRandom random = new SecureRandom();
private static final IdWorker idWorker = new IdWorker(-1, -1);
/**
* 生成UUID, 中间无-分割.

View File

@@ -7,6 +7,9 @@ package com.jeesite.common.io;
import com.jeesite.common.codec.EncodeUtils;
import com.jeesite.common.collect.ListUtils;
import com.jeesite.common.lang.StringUtils;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.sf.jmimemagic.Magic;
import net.sf.jmimemagic.MagicMatch;
import org.apache.commons.io.IOUtils;
@@ -15,9 +18,6 @@ import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import javax.activation.MimetypesFileTypeMap;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;

View File

@@ -4,7 +4,6 @@
*/
package com.jeesite.common.io;
import com.alibaba.fastjson.parser.ParserConfig;
import com.jeesite.common.lang.StringUtils;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
import org.springframework.boot.env.PropertiesPropertySourceLoader;
@@ -39,12 +38,12 @@ public class PropertyLoader implements org.springframework.boot.env.PropertySour
List<PropertySource<?>> propertySources = new ArrayList<>();
if (!isLoadJeeSitePropertySource) {
isLoadJeeSitePropertySource = true;
try {
// 默认开启 FastJSON 1.x 的,安全模式
ParserConfig.getGlobalInstance().setSafeMode(true);
} catch (Throwable ignored) {
// 兼容 FastJSON 2.x 的调用,忽略异常
}
// try {
// // 默认开启 FastJSON 1.x 的,安全模式
// ParserConfig.getGlobalInstance().setSafeMode(true);
// } catch (Throwable ignored) {
// // 兼容 FastJSON 2.x 的调用,忽略异常
// }
Properties properties = PropertiesUtils.getInstance().getProperties();
propertySources.add(new OriginTrackedMapPropertySource("jeesite", properties));
} else {

View File

@@ -4,8 +4,8 @@
*/
package com.jeesite.common.lang;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
import java.io.StringWriter;

View File

@@ -4,12 +4,14 @@
*/
package com.jeesite.common.lang;
import com.jeesite.common.io.PropertiesUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.nustaq.serialization.FSTConfiguration;
import org.apache.fury.Fury;
import org.apache.fury.ThreadSafeFury;
import org.apache.fury.config.Language;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.core.NamedThreadLocal;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -25,13 +27,24 @@ import java.lang.reflect.InvocationTargetException;
public class ObjectUtils extends org.apache.commons.lang3.ObjectUtils {
private static final Logger logger = LoggerFactory.getLogger(ObjectUtils.class);
private static final boolean isJavaSerialize;
static {
String[] ver = StringUtils.split(System.getProperty("java.version"), StringUtils.DOT);
isJavaSerialize = ver.length > 0 && Integer.parseInt(ver[0]) > 1;
/**
* 当前类的实例持有者(静态内部类,延迟加载,懒汉式,线程安全的单例模式)
*/
private static final class Static {
private static final Boolean isJavaSerialize;
private static final ThreadSafeFury fury;
static {
isJavaSerialize = PropertiesUtils.getInstance()
.getPropertyToBoolean("isJavaSerialize", "false");
org.apache.fury.logging.LoggerFactory.useSlf4jLogging(true);
fury = Fury.builder().withLanguage(Language.JAVA)
.withRefTracking(true)
.requireClassRegistration(false)
.buildThreadSafeFury();
}
}
/**
* 转换为 Double 类型
*/
@@ -186,10 +199,10 @@ public class ObjectUtils extends org.apache.commons.lang3.ObjectUtils {
*/
public static byte[] serialize(Object object) {
try {
if (isJavaSerialize) {
if (Static.isJavaSerialize) {
return ObjectUtils.serializeJava(object);
}else {
return ObjectUtils.serializeFst(object);
} else {
return ObjectUtils.serializeFury(object);
}
} catch (Exception e) {
logger.error("serialize: {}", e.getMessage());
@@ -204,10 +217,10 @@ public class ObjectUtils extends org.apache.commons.lang3.ObjectUtils {
*/
public static Object unserialize(byte[] bytes) {
try {
if (isJavaSerialize) {
if (Static.isJavaSerialize) {
return ObjectUtils.unserializeJava(bytes);
}else {
return ObjectUtils.unserializeFst(bytes);
} else {
return ObjectUtils.unserializeFury(bytes);
}
} catch (Exception e) {
logger.error("unserialize: {}", e.getMessage());
@@ -265,51 +278,88 @@ public class ObjectUtils extends org.apache.commons.lang3.ObjectUtils {
}
return object;
}
private static ThreadLocal<FSTConfiguration> fstConfiguration =
new NamedThreadLocal<FSTConfiguration>("FSTConfiguration") {
@Override
public FSTConfiguration initialValue() {
return FSTConfiguration.createDefaultConfiguration();
}
};
/**
* FST 序列化对象
* 序列化对象
* @param object
* @return
*/
public static byte[] serializeFst(Object object) {
public static byte[] serializeFury(Object object) {
if (object == null){
return null;
}
long beginTime = System.currentTimeMillis();
byte[] bytes = fstConfiguration.get().asByteArray(object);
byte[] bytes = Static.fury.serialize(object);
long totalTime = System.currentTimeMillis() - beginTime;
if (totalTime > 30000){
logger.warn(object.getClass() + " fst serialize time: " + TimeUtils.formatTime(totalTime));
logger.warn(object.getClass() + " serialize time: " + TimeUtils.formatTime(totalTime));
}
return bytes;
}
/**
* FST 反序列化对象
* 反序列化对象
* @param bytes
* @return
*/
public static Object unserializeFst(byte[] bytes) {
public static Object unserializeFury(byte[] bytes) {
if (bytes == null){
return null;
}
long beginTime = System.currentTimeMillis();
Object object = fstConfiguration.get().asObject(bytes);
Object object = Static.fury.deserialize(bytes);
long totalTime = System.currentTimeMillis() - beginTime;
if (totalTime > 30000 && object != null){
logger.warn(object.getClass() + " fst unserialize time: " + TimeUtils.formatTime(totalTime));
logger.warn(object.getClass() + " unserialize time: " + TimeUtils.formatTime(totalTime));
}
return object;
}
// private static ThreadLocal<FSTConfiguration> fstConfiguration =
// new NamedThreadLocal<FSTConfiguration>("FSTConfiguration") {
// @Override
// public FSTConfiguration initialValue() {
// return FSTConfiguration.createDefaultConfiguration();
// }
// };
//
// /**
// * FST 序列化对象
// * @param object
// * @return
// */
// public static byte[] serializeFst(Object object) {
// if (object == null){
// return null;
// }
// long beginTime = System.currentTimeMillis();
// byte[] bytes = fstConfiguration.get().asByteArray(object);
// long totalTime = System.currentTimeMillis() - beginTime;
// if (totalTime > 30000){
// logger.warn(object.getClass() + " fst serialize time: " + TimeUtils.formatTime(totalTime));
// }
// return bytes;
// }
//
// /**
// * FST 反序列化对象
// * @param bytes
// * @return
// */
// public static Object unserializeFst(byte[] bytes) {
// if (bytes == null){
// return null;
// }
// long beginTime = System.currentTimeMillis();
// Object object = fstConfiguration.get().asObject(bytes);
// long totalTime = System.currentTimeMillis() - beginTime;
// if (totalTime > 30000 && object != null){
// logger.warn(object.getClass() + " fst unserialize time: " + TimeUtils.formatTime(totalTime));
// }
// return object;
// }
// private static Pool<Kryo> kryoPool = new Pool<Kryo>(true, false, 8) {
// protected Kryo create() {
// Kryo kryo = new Kryo();

View File

@@ -27,6 +27,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import java.io.IOException;
import java.io.Serial;
import java.lang.reflect.AnnotatedElement;
import java.util.Date;
import java.util.List;
@@ -40,6 +41,7 @@ import java.util.TimeZone;
*/
public class JsonMapper extends ObjectMapper {
@Serial
private static final long serialVersionUID = 1L;
private static final Logger logger = LoggerFactory.getLogger(JsonMapper.class);
@@ -78,7 +80,9 @@ public class JsonMapper extends ObjectMapper {
this.setTimeZone(TimeZone.getTimeZone(PropertiesUtils.getInstance()
.getProperty("lang.defaultTimeZone", "GMT+08:00")));
this.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {
@Serial
private static final long serialVersionUID = 1L;
private static final String[] pattern = new String[] {"yyyy", "MM", "dd", "HH", "mm", "ss", "SSS"};
@Override
public Object findSerializer(Annotated a) {
if (a instanceof AnnotatedMethod) {
@@ -120,10 +124,9 @@ public class JsonMapper extends ObjectMapper {
return this;
}
private static final String[] pattern = new String[] {"yyyy", "MM", "dd", "HH", "mm", "ss", "SSS"};
private static final class JeeSiteJsonSerializer extends JsonSerializer<Date> {
public static final class JeeSiteJsonSerializer extends JsonSerializer<Date> {
private final String pattern;
private JeeSiteJsonSerializer(String pattern) {
public JeeSiteJsonSerializer(String pattern) {
this.pattern = pattern;
}
@Override
@@ -137,9 +140,10 @@ public class JsonMapper extends ObjectMapper {
}
}
}
private static final class JeeSiteJsonDeserializer extends JsonDeserializer<Date> {
public static final class JeeSiteJsonDeserializer extends JsonDeserializer<Date> {
private final String pattern;
private JeeSiteJsonDeserializer(String pattern) {
public JeeSiteJsonDeserializer(String pattern) {
this.pattern = pattern;
}
@Override

View File

@@ -12,6 +12,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import java.io.IOException;
import java.io.Serial;
import java.util.TimeZone;
/**
@@ -21,6 +22,7 @@ import java.util.TimeZone;
*/
public class XmlMapper extends com.fasterxml.jackson.dataformat.xml.XmlMapper{
@Serial
private static final long serialVersionUID = 1L;
private static final Logger logger = LoggerFactory.getLogger(XmlMapper.class);

View File

@@ -8,7 +8,7 @@ import com.jeesite.common.codec.EncodeUtils;
import com.jeesite.common.io.PropertiesUtils;
import com.jeesite.common.lang.StringUtils;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
/**
* IP 地址工具

View File

@@ -6,11 +6,11 @@ package com.jeesite.common.utils;
import com.jeesite.common.io.PropertiesUtils;
import com.jeesite.common.web.http.ServletUtils;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.context.i18n.TimeZoneAwareLocaleContext;
import org.springframework.core.NamedThreadLocal;
import org.springframework.web.servlet.LocaleContextResolver;
import javax.servlet.http.HttpServletRequest;
import java.util.Locale;
import java.util.TimeZone;

View File

@@ -4,12 +4,15 @@
*/
package com.jeesite.common.utils.excel;
import java.io.Serial;
/**
* Excel Exception
* @author ThinkGem
*/
public class ExcelException extends RuntimeException {
@Serial
private static final long serialVersionUID = 1L;
public ExcelException() {

View File

@@ -23,7 +23,7 @@ import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

View File

@@ -7,9 +7,9 @@ package com.jeesite.common.web;
import com.jeesite.common.codec.EncodeUtils;
import com.jeesite.common.io.PropertiesUtils;
import com.jeesite.common.lang.StringUtils;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.Cookie;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* Cookie工具类

View File

@@ -4,218 +4,262 @@
*/
package com.jeesite.common.web.http;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import com.jeesite.common.codec.EncodeUtils;
import com.jeesite.common.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509ExtendedTrustManager;
import java.io.InputStream;
import java.net.Socket;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.time.Duration;
import java.util.Map;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import com.jeesite.common.codec.EncodeUtils;
/**
* HTTP客户端工具类支持HTTPS
* HTTP 客户端工具类支持HTTPS
* @author ThinkGem
* @version 2017-3-27
* @version 2025-03-26
*/
public class HttpClientUtils {
private final static Logger logger = LoggerFactory.getLogger(HttpClientUtils.class);
private static final HttpClient client = createHttpClient(60);
/**
* http的get请求
* @param url
* HTTP 的 GET 请求
*/
public static String get(String url) {
return get(url, EncodeUtils.UTF_8);
}
/**
* http的get请求
* @param url
*/
public static String get(String url, String charset) {
HttpGet httpGet = new HttpGet(url);
return executeRequest(httpGet, charset);
}
/**
* http的get请求增加异步请求头参数
* @param url
*/
public static String ajaxGet(String url) {
return ajaxGet(url, EncodeUtils.UTF_8);
}
/**
* http的get请求增加异步请求头参数
* @param url
*/
public static String ajaxGet(String url, String charset) {
HttpGet httpGet = new HttpGet(url);
httpGet.setHeader("X-Requested-With", "XMLHttpRequest");
return executeRequest(httpGet, charset);
return get(url, null, null);
}
/**
* http的post请求,传递map格式参数
* HTTP 的 GET 请求,传递 Map 格式参数
*/
public static String get(String url, Map<String, String> dataMap) {
return get(url, dataMap, EncodeUtils.UTF_8);
}
/**
* HTTP 的 GET 请求,传递 Map 格式参数,支持指定编码
*/
public static String get(String url, Map<String, String> dataMap, String charset) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(buildUrl(url, dataMap, charset)))
.GET()
.build();
return executeRequest(request);
}
/**
* HTTP 的 GET 请求,增加 ajax 请求头
*/
public static String ajaxGet(String url) {
return ajaxGet(url, null, null);
}
/**
* HTTP 的 GET 请求,增加 ajax 请求头,传递 Map 格式参数
*/
public static String ajaxGet(String url, Map<String, String> dataMap) {
return ajaxGet(url, dataMap, EncodeUtils.UTF_8);
}
/**
* HTTP 的 GET 请求,增加 ajax 请求头,传递 Map 格式参数,支持指定编码
*/
public static String ajaxGet(String url, Map<String, String> dataMap, String charset) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(buildUrl(url, dataMap, charset)))
.header("X-Requested-With", "XMLHttpRequest")
.GET()
.build();
return executeRequest(request);
}
/**
* 构建表单数据Map 转换 params支持指定编码
*/
private static String buildUrl(String url, Map<String, String> dataMap, String charset) {
url = StringUtils.substringBefore(url, "#");
if (dataMap == null) {
return url;
}
StringBuilder sb = new StringBuilder(url);
if (!url.contains("?")) {
sb.append("?");
} else if (!url.endsWith("&")) {
sb.append("&");
}
return sb + buildFormData(dataMap, charset);
}
/**
* HTTP 的 POST 请求,传递 Map 格式参数
*/
public static String post(String url, Map<String, String> dataMap) {
return post(url, dataMap, EncodeUtils.UTF_8);
}
/**
* http的post请求,传递map格式参数
* HTTP 的 POST 请求,传递 Map 格式参数,支持指定编码
*/
public static String post(String url, Map<String, String> dataMap, String charset) {
HttpPost httpPost = new HttpPost(url);
try {
if (dataMap != null){
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
for (Map.Entry<String, String> entry : dataMap.entrySet()) {
nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nvps, charset);
formEntity.setContentEncoding(charset);
httpPost.setEntity(formEntity);
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return executeRequest(httpPost, charset);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(buildFormData(dataMap, charset)))
.build();
return executeRequest(request);
}
/**
* http的post请求增加异步请求头参数,传递map格式参数
* HTTP 的 POST 请求,增加 ajax 请求头,传递 Map 格式参数
*/
public static String ajaxPost(String url, Map<String, String> dataMap) {
return ajaxPost(url, dataMap, EncodeUtils.UTF_8);
}
/**
* http的post请求增加异步请求头参数,传递map格式参数
* HTTP 的 POST 请求,增加 ajax 请求头,传递 Map 格式参数,支持指定编码
*/
public static String ajaxPost(String url, Map<String, String> dataMap, String charset) {
HttpPost httpPost = new HttpPost(url);
httpPost.setHeader("X-Requested-With", "XMLHttpRequest");
try {
if (dataMap != null){
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
for (Map.Entry<String, String> entry : dataMap.entrySet()) {
nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(nvps, charset);
formEntity.setContentEncoding(charset);
httpPost.setEntity(formEntity);
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return executeRequest(httpPost, charset);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("X-Requested-With", "XMLHttpRequest")
.header("Content-Type", "application/x-www-form-urlencoded")
.POST(HttpRequest.BodyPublishers.ofString(buildFormData(dataMap, charset)))
.build();
return executeRequest(request);
}
/**
* http的post请求增加异步请求头参数传递json格式参数
* 构建表单数据Map 转换 params支持指定编码
*/
private static String buildFormData(Map<String, String> dataMap, String charset) {
return dataMap.entrySet().stream()
.map(entry -> entry.getKey() + "="
+ EncodeUtils.encodeUrl(entry.getValue(), charset))
.reduce((a, b) -> a + "&" + b)
.orElse(StringUtils.EMPTY);
}
/**
* HTTP 的 POST 请求,使用 json 请求头,传递 json 格式参数
*/
public static String ajaxPostJson(String url, String jsonString) {
return ajaxPostJson(url, jsonString, EncodeUtils.UTF_8);
}
/**
* http的post请求增加异步请求头参数传递json格式参数
* HTTP 的 POST 请求,使用 json 请求头,传递 json 格式参数,支持指定编码
*/
public static String ajaxPostJson(String url, String jsonString, String charset) {
HttpPost httpPost = new HttpPost(url);
httpPost.setHeader("X-Requested-With", "XMLHttpRequest");
// try {
StringEntity stringEntity = new StringEntity(jsonString, charset);// 解决中文乱码问题
stringEntity.setContentEncoding(charset);
stringEntity.setContentType("application/json");
httpPost.setEntity(stringEntity);
// } catch (UnsupportedEncodingException e) {
// e.printStackTrace();
// }
return executeRequest(httpPost, charset);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.header("X-Requested-With", "XMLHttpRequest")
.header("Content-Type", "application/json; charset="
+ (StringUtils.isNotBlank(charset) ? charset : EncodeUtils.UTF_8))
.POST(HttpRequest.BodyPublishers.ofString(jsonString))
.build();
return executeRequest(request);
}
/**
* 执行一个http请求传递HttpGet或HttpPost参数
* 执行一个 http 请求,传递 HttpRequest 参数
*/
public static String executeRequest(HttpUriRequest httpRequest) {
return executeRequest(httpRequest, EncodeUtils.UTF_8);
public static String executeRequest(HttpRequest request) {
try {
return client.send(request, HttpResponse.BodyHandlers.ofString()).body();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 执行一个http请求传递HttpGet或HttpPost参数
* HTTP 的 GET 请求,返回文件流
*/
public static String executeRequest(HttpUriRequest httpRequest, String charset) {
CloseableHttpClient httpclient;
if ("https".equals(httpRequest.getURI().getScheme())){
httpclient = createSSLInsecureClient();
}else{
httpclient = HttpClients.createDefault();
}
String result = "";
try {
try {
CloseableHttpResponse response = httpclient.execute(httpRequest);
HttpEntity entity = null;
try {
entity = response.getEntity();
result = EntityUtils.toString(entity, charset);
} finally {
EntityUtils.consume(entity);
response.close();
}
} finally {
httpclient.close();
}
}catch(IOException ex){
ex.printStackTrace();
}
return result;
public static InputStream getInputStream(String url) {
return getInputStream(url, null);
}
/**
* 创建 SSL连接
* HTTP 的 GET 请求,传递 Map 格式参数,返回文件流
*/
public static CloseableHttpClient createSSLInsecureClient() {
public static InputStream getInputStream(String url, Map<String, String> dataMap) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(buildUrl(url, dataMap, EncodeUtils.UTF_8)))
.GET()
.build();
return executeRequestInputStream(request);
}
/**
* 执行一个 http 请求,传递 HttpRequest 参数,返回文件流
*/
public static InputStream executeRequestInputStream(HttpRequest request) {
try {
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
}).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
return HttpClients.custom().setSSLSocketFactory(sslsf).build();
} catch (GeneralSecurityException ex) {
throw new RuntimeException(ex);
HttpResponse<InputStream> response = client.send(request, HttpResponse.BodyHandlers.ofInputStream());
if (response.statusCode() == 200) {
return response.body();
} else {
logger.info("URL: {} statusCode: {}", request.uri().toString(), response.statusCode());
return null;
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static HttpClient createHttpClient(long seconds) {
HttpClient client;
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{new UnsafeX509ExtendedTrustManager()}, new SecureRandom());
client = HttpClient.newBuilder()
.sslContext(sslContext)
.connectTimeout(Duration.ofSeconds(seconds))
.build();
} catch (Exception e) {
logger.info(e.getMessage(), e);
client = HttpClient.newHttpClient();
}
return client;
}
private static final class UnsafeX509ExtendedTrustManager extends X509ExtendedTrustManager {
private static final X509Certificate[] EMPTY_CERTIFICATES = new X509Certificate[0];
@Override
public void checkClientTrusted(X509Certificate[] certificates, String authType) {
}
@Override
public void checkClientTrusted(X509Certificate[] certificates, String authType, Socket socket) {
}
@Override
public void checkClientTrusted(X509Certificate[] certificates, String authType, SSLEngine sslEngine) {
}
@Override
public void checkServerTrusted(X509Certificate[] certificates, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] certificates, String authType, Socket socket) {
}
@Override
public void checkServerTrusted(X509Certificate[] certificates, String authType, SSLEngine sslEngine) {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return EMPTY_CERTIFICATES;
}
}

View File

@@ -5,8 +5,8 @@
package com.jeesite.common.web.http;
import com.jeesite.common.io.PropertiesUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
/**
* 统一包装结果输出类:{ code: 200, msg: "", data: {} | [] }

View File

@@ -12,15 +12,15 @@ import com.jeesite.common.lang.ExceptionUtils;
import com.jeesite.common.lang.StringUtils;
import com.jeesite.common.mapper.JsonMapper;
import com.jeesite.common.mapper.XmlMapper;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.Validate;
import org.springframework.http.MediaType;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;

View File

@@ -4,7 +4,7 @@
*/
package com.jeesite.common.web.http;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import eu.bitwalker.useragentutils.Browser;
import eu.bitwalker.useragentutils.DeviceType;

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
*/
package com.jeesite.test.web.http;
import com.jeesite.common.mapper.JsonMapper;
import com.jeesite.common.web.http.HttpClientUtils;
import java.util.HashMap;
import java.util.Map;
/**
* HTTP客户端测试工具类支持HTTPS
* @author ThinkGem
* @version 2017-3-27
*/
public class HttpClientUtilsTest {
public static void main(String[] args) {
String url = "https://vue.jeesite.com/js/a/sys/corpAdmin/treeData";
Map<String, String> dataMap = new HashMap<>();
dataMap.put("isShowCode", "true");
dataMap.put("param1", "你好");
System.out.println(HttpClientUtils.get(url, dataMap));
System.out.println(HttpClientUtils.ajaxGet(url, dataMap));
System.out.println(HttpClientUtils.post(url, dataMap));
System.out.println(HttpClientUtils.ajaxPost(url, dataMap));
System.out.println(HttpClientUtils.ajaxPostJson(url, JsonMapper.toJson(dataMap)));
}
}

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>com.jeesite</groupId>
<artifactId>jeesite-parent</artifactId>
<version>5.13.0-SNAPSHOT</version>
<version>5.13.0.springboot3-SNAPSHOT</version>
<relativePath>../../parent/pom.xml</relativePath>
</parent>

View File

@@ -9,9 +9,10 @@ import com.jeesite.common.entity.DataEntity;
import com.jeesite.common.mybatis.annotation.Column;
import com.jeesite.common.mybatis.annotation.Table;
import com.jeesite.common.mybatis.mapper.query.QueryType;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.io.Serial;
import java.util.Date;
/**
@@ -36,6 +37,7 @@ import java.util.Date;
)
public class AppComment extends DataEntity<AppComment> {
@Serial
private static final long serialVersionUID = 1L;
private String category; // 问题分类
private String content; // 问题和意见

View File

@@ -4,9 +4,10 @@
*/
package com.jeesite.modules.app.entity;
import java.io.Serial;
import java.util.Date;
import javax.validation.constraints.Size;
import jakarta.validation.constraints.Size;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.jeesite.common.entity.DataEntity;
@@ -34,6 +35,7 @@ import com.jeesite.common.mybatis.mapper.query.QueryType;
)
public class AppUpgrade extends DataEntity<AppUpgrade> {
@Serial
private static final long serialVersionUID = 1L;
private String appCode; // 应用编号
private String upTitle; // 升级标题

View File

@@ -9,6 +9,8 @@ import com.jeesite.common.entity.Page;
import com.jeesite.common.web.BaseController;
import com.jeesite.modules.app.entity.AppComment;
import com.jeesite.modules.app.service.AppCommentService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
@@ -20,8 +22,6 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
/**

View File

@@ -4,8 +4,8 @@
*/
package com.jeesite.modules.app.web;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;

View File

@@ -160,7 +160,7 @@
</div>
<% } %>
<script>
$("#inputForm").validate({
$('#inputForm').validate({
submitHandler: function(form){
js.ajaxSubmitForm($(form), function(data){
js.showMessage(data.message);

View File

@@ -132,7 +132,7 @@
</div>
<% } %>
<script>
$("#inputForm").validate({
$('#inputForm').validate({
submitHandler: function(form){
js.ajaxSubmitForm($(form), function(data){
js.showMessage(data.message);

183
modules/cms-ai/README.md Normal file
View File

@@ -0,0 +1,183 @@
## 模块简介
本模块基于 Spring AI 和 JeeSite 内容管理系统CMS并结合了检索增强生成Retrieval-Augmented Generation, RAG技术
和先进的人工智能算法AI打造了一个强大的企业级知识管理和智能对话平台。该模块专为企业设计旨在通过高效的知识获取和精准的对话能力
提升企业的信息管理效率和员工的工作效能。
检索增强生成 RAG 技术使系统能够自动从海量的企业文档中检索最相关的信息,并将其融入到生成的回答中,确保每一次查询都
能获得最新且准确的结果。这种检索与生成相结合的方式,不仅提高了信息检索的准确性,还增强了回答的上下文关联性,
特别适合处理复杂的企业知识库。
此外该模块支持在线大模型和本地部署的大模型Ollama、DeepSeek、通义千问理论上支持所有 OpenAPI 标准接口的 AI 提供商。
并能无缝集成多种嵌入式 AI 模型的向量数据库,如 Chroma、PGVector、Elasticsearch、Milvus 等,实现高效的数据存储、检索及分析。
无论是大规模数据集还是高度专业化的领域知识JeeSite CMS + RAG + AI 都能提供定制化解决方案,满足企业多样化的业务需求和技术要求。
企业可以轻松管理和访问复杂的信息资源,促进内部知识共享和创新,从而在竞争激烈的市场环境中保持领先地位。
优势:本模块结构清晰,代码简洁易懂,不管是正式项目、或是学习 AI 技术、都能轻松应对读懂源代码。
## 在线演示
* 地址:<https://vue.jeesite.com/cms/chat/index>
## 源码下载
* 后端:<https://gitee.com/thinkgem/jeesite5/tree/v5.springboot3/modules/cms-ai>
* 前端:<https://gitee.com/thinkgem/jeesite-vue/tree/main/packages/cms>
## AI 模型配置
支持的 AI 模型列表:<https://docs.spring.io/spring-ai/reference/1.0/api/index.html>
* 线上模型:理论上支持所有 [OpenAPI](https://help.aliyun.com/zh/model-studio/developer-reference/use-qwen-by-calling-api) 标准接口的 AI 提供商。
* 本地模型:使用 [Ollama](https://ollama.com) 安装方法,本文不多赘述,网上有很多安装资料。
* 模型类型包括:聊天对话模型和嵌入式向量库模型,需注意 dimensions 维度参数,要和模型要求的匹配。
```yml
# 向量库类型openai、ollama
spring.ai.model.chat: openai
```
具体配置项详见 [jeesite-cms-ai.yml](https://gitee.com/thinkgem/jeesite5/blob/v5.springboot3/modules/cms-ai/src/main/resources/config/jeesite-cms-ai.yml) 文件,有注释。
## 向量数据库配置
支持的向量库列表:<https://docs.spring.io/spring-ai/reference/1.0/api/vectordbs.html>
* Chroma
* PGVector
* Elasticsearch
* Milvus
* ...
```yml
# 向量库类型chroma、pgvector、elasticsearch、milvus、指定 none 表示不使用向量库
spring.ai.vectorstore.type: none
```
具体配置项详见 [jeesite-cms-ai.yml](https://gitee.com/thinkgem/jeesite5/blob/v5.springboot3/modules/cms-ai/src/main/resources/config/jeesite-cms-ai.yml) 文件,有注释。
### 安装 Chroma
```sh
docker run -d --name chroma -p 8000:8000 ghcr.io/chroma-core/chroma:1.0.0
```
### 安装 PGVector
```sh
docker run -d --name pgvector -p 5433:5432 -e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=postgres pgvector/pgvector:pg17
```
* 进入容器
```sql
docker exec -it pgvector psql -U postgres
```
* 建库语句
```sql
CREATE DATABASE "jeesite-ai";
-- 激活数据库
\connect "jeesite-ai";
-- 建立数据表和索引
CREATE EXTENSION IF NOT EXISTS vector;
CREATE EXTENSION IF NOT EXISTS hstore;
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- 使用 all-minilm 模型时创建
DROP TABLE IF EXISTS vector_store_384;
CREATE TABLE IF NOT EXISTS vector_store_384 (
id varchar(64) DEFAULT uuid_generate_v4() PRIMARY KEY,
content text,
metadata json,
embedding vector(384)
);
CREATE INDEX ON vector_store_384 USING HNSW (embedding vector_cosine_ops);
-- 使用 nomic-embed-text 模型时创建
DROP TABLE IF EXISTS vector_store_786;
CREATE TABLE IF NOT EXISTS vector_store_786 (
id varchar(64) DEFAULT uuid_generate_v4() PRIMARY KEY,
content text,
metadata json,
embedding vector(768)
);
CREATE INDEX ON vector_store_786 USING HNSW (embedding vector_cosine_ops);
-- 使用 bge-m3 模型时创建
DROP TABLE IF EXISTS vector_store_1024;
CREATE TABLE IF NOT EXISTS vector_store_1024 (
id varchar(64) DEFAULT uuid_generate_v4() PRIMARY KEY,
content text,
metadata json,
embedding vector(1024)
);
CREATE INDEX ON vector_store_1024 USING HNSW (embedding vector_cosine_ops);
```
## 创建 AI 菜单
系统管理 -> 系统设置 -> 菜单管理 -> 新增
* 菜单名称AI 助手
* 菜单地址:/cms/chat/index
## 工具调用 Tool Calling
工具调用 Tool Calling也称 Function Calling是人工智能应用程序中的常见模式允许模型与一组 API 或工具交互,从而增强其功能。
实例代码,详见 [CmsAiTools.java](https://gitee.com/thinkgem/jeesite5/blob/v5.springboot3/modules/cms-ai/src/main/java/com/jeesite/modules/cms/ai/tools/CmsAiTools.java) 让 AI 调用你的 java 实现你的业务联动。
## 支持结构化输出
对于依赖可靠解析输出值的下游应用程序来说LLM 产生结构化输出的能力很重要。开发人员希望将 AI 模型的结果快速转换为数据类型,如 JSON、XML 或 Java 类,这些类可以传递给其他应用程序函数和方法。
pom.xml 中注释掉 `<artifactId>spring-ai-starter-model-openai</artifactId>`
打开注释 `<artifactId>spring-ai-starter-model-ollama</artifactId>`
启用 `Ollama` 本地模型,测试类:`AiChatServiceTest.java`,或测试地址:
* 文本格式输出
- 源码位置CmsAiChatService.chatText(message)
- 访问地址:<http://127.0.0.1:8980/js/a/cms/chat/text?message=你好>
- 输出结果:`你好!有什么问题或需要帮助的吗?`
* JSON 类型输出
- 源码位置CmsAiChatService.chatJson(message)
- 访问地址:<http://127.0.0.1:8980/js/a/cms/chat/json?message=张三>
- 输出结果:`{"sex":"男","name":"张三","age":"17"}`
* 结合 Tool Calling 结构化输出
- 开启参数:`spring.ai.tool-calls: true`
- 源码位置CmsAiChatService.chatJson(message)
- 访问地址:<http://127.0.0.1:8980/js/a/cms/chat/json?message=打开客厅的灯>
- 输出结果:`{"message":"客厅房间里的灯被打开","roomName":"客厅","on":true}`
* Java 对象类型输出:
- 源码位置CmsAiChatService.chatArea(message)
- 访问地址:<http://127.0.0.1:8980/js/a/cms/chat/entity?message=北京>
- 输出结果:`[{"id":"110000","pageNo":1,"pageSize":10,"orderBy":"","isNewRecord":false,"dataMap":{},"status":"0","createBy":"system","createDate":"2025-01-01 19:25","updateBy":"system","updateDate":"2025-01-01 19:25","remarks":"","lastUpdateDateTime":1677843300000,"parentCodes":"0,","treeSort":110000,"treeSorts":"0000110000,","treeLeaf":"0","treeLevel":0,"treeNames":"北京市","childList":[{"id":"110100","pageNo":1,"pageSize":10,"orderBy":"","isNewRecord":false,"dataMap":{},"status":"0","createBy":"system","createDate":"2025-01-01 19:25","updateBy":"system","updateDate":"2025-01-01 19:25","remarks":"","lastUpdateDateTime":1677843300000,"parentCodes":"0,110000,","treeSort":110100,"treeSorts":"0000110000,0000110100,","treeLeaf":"0","treeLevel":1,"treeNames":"北京城区","childList":[{"id":"110101","isNewRecord":false,"areaCode":"110101","areaName":"东城区","areaType":"3","isRoot":true,"isTreeLeaf":false},{"id":"110102","isNewRecord":false,"areaCode":"110102","areaName":"西城区","areaType":"3","isRoot":true,"isTreeLeaf":false},{"id":"110105","isNewRecord":false,"areaCode":"110105","areaName":"朝阳区","areaType":"3","isRoot":true,"isTreeLeaf":false},{"id":"110106","isNewRecord":false,"areaCode":"110106","areaName":"丰台区","areaType":"3","isRoot":true,"isTreeLeaf":false},{"id":"110107","isNewRecord":false,"areaCode":"110107","areaName":"石景山区","areaType":"3","isRoot":true,"isTreeLeaf":false},{"id":"110108","isNewRecord":false,"areaCode":"110108","areaName":"海淀区","areaType":"3","isRoot":true,"isTreeLeaf":false},{"id":"110109","isNewRecord":false,"areaCode":"110109","areaName":"门头沟区","areaType":"3","isRoot":true,"isTreeLeaf":false},{"id":"110111","isNewRecord":false,"areaCode":"110111","areaName":"房山区","areaType":"3","isRoot":true,"isTreeLeaf":false}],"isQueryChildren":true,"areaCode":"110100","areaName":"北京城区","areaType":"2","isRoot":false,"parentCode":"110000","isTreeLeaf":false,"parentName":"北京市"}],"isQueryChildren":true,"areaCode":"110000","areaName":"北京市","areaType":"1","isRoot":true,"isTreeLeaf":false}]`
## 授权协议声明
1. 基于 Apache License Version 2.0 协议发布,可用于商业项目,但必须遵守以下补充条款。
2. 不得将本软件应用于危害国家安全、荣誉和利益的行为,不能以任何形式用于非法为目的的行为。
3. 在使用本软件时,由于它集成了众多第三方开源软件,请共同遵守这些开源软件的使用许可条款规定。
4. 在延伸的代码中(修改和有源代码衍生的代码中)需要带有原来代码中的协议、版权声明和其他原作者
规定需要包含的说明(请尊重原作者的著作权,不要删除或修改文件中的`Copyright``@author`信息)
更不要,全局替换源代码中的 jeesite 或 ThinkGem 等字样,否则你将违反本协议条款承担责任。
5. 您若套用本软件的一些代码或功能参考,请保留源文件中的版权和作者,需要在您的软件介绍明显位置
说明出处,举例:本软件基于 JeeSite 快速开发平台并附带链接http://jeesite.com
6. 任何基于本软件而产生的一切法律纠纷和责任,均于我司无关。
7. 如果你对本软件有改进,希望可以贡献给我们,共同进步。
8. 本项目已申请软件著作权,请尊重开源,感谢阅读。
9. 无用户数限制,无在线人数限制,放心使用。
## 技术支持与服务
* 本软件免费,我们也提供了相应的收费服务,因为:
* 没有资金的支撑就很难得到发展,特别是一个好的产品,如果 JeeSite 帮助了您,请为我们点赞。支持我们,您可以获得更多回馈,我们会把公益事业做的更好,开放更多资源,回报社区和社会。请给我们一些动力吧,在此非常感谢已支持我们的朋友!
* **联系我们**:请访问技术支持与服务页面:<http://s.jeesite.com>

View File

@@ -0,0 +1,22 @@
@echo off
rem /**
rem * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
rem * No deletion without permission, or be held responsible to law.
rem *
rem * Author: ThinkGem@163.com
rem */
echo.
echo [<5B><>Ϣ] <20><><EFBFBD>𹤳̵<F0B9A4B3>Maven<65><6E><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
echo.
%~d0
cd %~dp0
call mvn -v
echo.
cd ..
call mvn clean deploy -Dmaven.test.skip=true -Pdeploy
cd bin
pause

View File

@@ -0,0 +1,18 @@
#!/bin/sh
# /**
# * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
# * No deletion without permission, or be held responsible to law.
# *
# * Author: ThinkGem@163.com
# */
echo ""
echo "[信息] 部署工程到Maven服务器。"
echo ""
mvn -v
echo ""
cd ..
mvn clean deploy -Dmaven.test.skip=true -Pdeploy
cd bin

View File

@@ -0,0 +1,22 @@
@echo off
rem /**
rem * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
rem * No deletion without permission, or be held responsible to law.
rem *
rem * Author: ThinkGem@163.com
rem */
echo.
echo [<5B><>Ϣ] <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>װ<EFBFBD><D7B0><EFBFBD>̣<EFBFBD><CCA3><EFBFBD><EFBFBD><EFBFBD>jar<61><72><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD>
echo.
%~d0
cd %~dp0
call mvn -v
echo.
cd ..
call mvn clean install -Dmaven.test.skip=true -Ppackage
cd bin
pause

View File

@@ -0,0 +1,18 @@
#!/bin/sh
# /**
# * Copyright (c) 2013-Now http://jeesite.com All rights reserved.
# * No deletion without permission, or be held responsible to law.
# *
# * Author: ThinkGem@163.com
# */
echo ""
echo "[信息] 打包安装工程生成jar包文件。"
echo ""
mvn -v
echo ""
cd ..
mvn clean install -Dmaven.test.skip=true -Ppackage
cd bin

3590
modules/cms-ai/db/cms-ai.erm Normal file

File diff suppressed because it is too large Load Diff

150
modules/cms-ai/pom.xml Normal file
View File

@@ -0,0 +1,150 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.jeesite</groupId>
<artifactId>jeesite-parent</artifactId>
<version>5.13.0.springboot3-SNAPSHOT</version>
<relativePath>../../parent/pom.xml</relativePath>
</parent>
<artifactId>jeesite-module-cms-ai</artifactId>
<packaging>jar</packaging>
<name>JeeSite Module CMS+RAG+AI</name>
<url>http://jeesite.com</url>
<inceptionYear>2013-Now</inceptionYear>
<properties>
<spring-ai.version>1.0.0</spring-ai.version>
</properties>
<dependencies>
<dependency>
<groupId>com.jeesite</groupId>
<artifactId>jeesite-module-core</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>com.jeesite</groupId>
<artifactId>jeesite-module-cms</artifactId>
<version>${project.parent.version}</version>
</dependency>
<!-- 在线大模型 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
<!-- 本地大模型 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>
<!-- 向量数据库 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-advisors-vector-store</artifactId>
</dependency>
<!-- Chroma 向量数据库 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-chroma</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
</dependency>
<!-- PG 向量数据库 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-pgvector</artifactId>
</dependency>
<!-- ES 向量数据库 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-elasticsearch</artifactId>
</dependency>
<!-- Milvus 向量数据库 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-milvus</artifactId>
<exclusions>
<exclusion>
<artifactId>slf4j-reload4j</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-resolver-dns-native-macos</artifactId>
<classifier>osx-aarch_64</classifier>
</dependency>
<!-- HTML 转 Markdown -->
<dependency>
<groupId>com.vladsch.flexmark</groupId>
<artifactId>flexmark-html2md-converter</artifactId>
<version>0.64.8</version>
</dependency>
<!-- Office、zip 等文件内容解析 -->
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-parsers-standard-package</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.27.1</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<developers>
<developer>
<id>thinkgem</id>
<name>WangZhen</name>
<email>thinkgem at 163.com</email>
<roles><role>Project lead</role></roles>
<timezone>+8</timezone>
</developer>
</developers>
<organization>
<name>JeeSite</name>
<url>http://jeesite.com</url>
</organization>
</project>

View File

@@ -0,0 +1,74 @@
/**
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
*/
package com.jeesite.modules.cms.ai.config;
import com.jeesite.common.datasource.DataSourceHolder;
import com.jeesite.common.lang.StringUtils;
import com.jeesite.modules.cms.ai.properties.CmsAiProperties;
import com.jeesite.modules.cms.ai.service.CacheChatMemoryRepository;
import com.jeesite.modules.cms.ai.tools.CmsAiTools;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;
/**
* AI 聊天配置类
* @author ThinkGem
*/
@Configuration
@EnableConfigurationProperties(CmsAiProperties.class)
public class CmsAiChatConfig {
/**
* 聊天对话客户端
* @author ThinkGem
*/
@Bean
public ChatClient chatClient(ChatClient.Builder builder, CmsAiProperties properties) {
if (StringUtils.isNotBlank(properties.getDefaultSystem())) {
builder.defaultSystem(properties.getDefaultSystem());
}
if (properties.getToolCalls()) {
builder.defaultTools(new CmsAiTools());
}
return builder.build();
}
/**
* 聊天对话数据存储
* @author ThinkGem
*/
@Bean
public ChatMemory chatMemory(CacheChatMemoryRepository cacheChatMemoryRepository) {
return MessageWindowChatMemory.builder()
.chatMemoryRepository(cacheChatMemoryRepository)
.maxMessages(1024)
.build();
}
// @Bean
// public BatchingStrategy batchingStrategy() {
// return new TokenCountBatchingStrategy(EncodingType.CL100K_BASE, Integer.MAX_VALUE, 0.1);
// }
/**
* PG向量库数据源
* @author ThinkGem
*/
@Bean
@Primary
@ConditionalOnProperty(name = "jdbc.ds_pgvector.type")
public JdbcTemplate pgVectorStoreJdbcTemplate() {
return DataSourceHolder.getRoutingDataSource()
.getJdbcTemplate("ds_pgvector");
}
}

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
*/
package com.jeesite.modules.cms.ai.config;
import com.jeesite.common.config.Global;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* MVC 异步任务池定义
* @author ThinkGem
*/
@Configuration
public class CmsAiWebMvcConfig implements WebMvcConfigurer {
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setTaskExecutor(webMvcAsyncTaskExecutor());
}
@Bean
public ThreadPoolTaskExecutor webMvcAsyncTaskExecutor() {
ThreadPoolTaskExecutor bean = new ThreadPoolTaskExecutor();
bean.setCorePoolSize(Global.getPropertyToInteger("web.taskPool.corePoolSize", "8"));
bean.setMaxPoolSize(Global.getPropertyToInteger("web.taskPool.maxPoolSize", "20"));
bean.setKeepAliveSeconds(Global.getPropertyToInteger("web.taskPool.keepAliveSeconds", "60"));
bean.setQueueCapacity(Global.getPropertyToInteger("web.taskPool.queueCapacity", String.valueOf(Integer.MAX_VALUE)));
bean.setThreadNamePrefix("web-async-");
return bean;
}
}

View File

@@ -0,0 +1,129 @@
/**
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
*/
package com.jeesite.modules.cms.ai.config;
import com.jeesite.common.mapper.JsonMapper;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.web.reactive.function.client.WebClientCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* 推理模型OpenAI兼容处理
* @author ThinkGem
*/
@Configuration
public class WebClientThinkConfig {
private final Logger logger = LoggerFactory.getLogger(WebClientThinkConfig.class);
@Bean
@ConditionalOnMissingBean
public WebClientCustomizer webClientCustomizerThink() {
return webClientBuilder -> {
ExchangeFilterFunction requestFilter = ExchangeFilterFunction.ofRequestProcessor(clientRequest -> {
logger.trace("Request url: {}: {}", clientRequest.method(), clientRequest.url());
return Mono.just(clientRequest);
});
ExchangeFilterFunction responseFilter = ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
logger.trace("Response status: {}", clientResponse.statusCode());
AtomicBoolean thinkingFlag = new AtomicBoolean(false);
Flux<DataBuffer> modifiedBody = clientResponse.bodyToFlux(DataBuffer.class)
.map(buf -> {
byte[] bytes = new byte[buf.readableByteCount()];
buf.read(bytes);
DataBufferUtils.release(buf);
return new String(bytes, StandardCharsets.UTF_8);
})
.flatMap(eventString -> {
logger.trace("Original response: ==> {}", eventString);
List<String> lines = new ArrayList<>();
String[] list = eventString.split("\\n", -1);
for (String line : list) {
if (!line.startsWith("data: ")) {
lines.add(line);
continue;
}
String jsonPart = line.substring("data: ".length()).trim();
if (!(StringUtils.startsWith(jsonPart, "{")
&& StringUtils.endsWith(jsonPart, "}")
&& !"data: [DONE]".equals(line))) {
lines.add(line);
continue;
}
Map<String, Object> map = JsonMapper.fromJson(jsonPart, Map.class);
if (map == null) {
lines.add(line);
continue;
}
// 修改内容字段
List<Object> choices = (List<Object>)map.get("choices");
if (choices == null) {
lines.add(line);
continue;
}
for (Object o : choices) {
Map<String, Object> choice = (Map<String, Object>) o;
if (choice == null) {
continue;
}
Map<String, Object> delta = (Map<String, Object>) choice.get("delta");
if (delta == null) {
continue;
}
String reasoningContent = (String) delta.get("reasoning_content");
String content = (String) delta.get("content");
if (StringUtils.isNotEmpty(reasoningContent) && StringUtils.isEmpty(content)) {
if (!thinkingFlag.get()) {
thinkingFlag.set(true);
delta.put("content", "<think>\n" + reasoningContent);
} else {
delta.put("content", reasoningContent);
}
} else {
if (thinkingFlag.get()) {
thinkingFlag.set(false);
delta.put("content", "</think>\n" + (content == null ? "" : content));
}
}
}
// 重新生成事件字符串
lines.add("data: " + JsonMapper.toJson(map));
}
String finalLine = StringUtils.join(lines, "\n");
logger.trace("Modified response: ==> {}", finalLine);
return Mono.just(finalLine);
})
.map(str -> {
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
return new DefaultDataBufferFactory().wrap(bytes);
});
ClientResponse modifiedResponse = ClientResponse.from(clientResponse)
.headers(headers -> headers.remove(HttpHeaders.CONTENT_LENGTH))
.body(modifiedBody)
.build();
return Mono.just(modifiedResponse);
});
webClientBuilder.filter(requestFilter).filter(responseFilter);
};
}
}

View File

@@ -0,0 +1,73 @@
package com.jeesite.modules.cms.ai.properties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
@ConfigurationProperties("spring.ai")
public class CmsAiProperties {
/**
* 是否启用 Tool calling 工具调用
*/
private Boolean toolCalls = false;
/**
* 默认系统提示词
*/
private String defaultSystem = "";
/**
* 默认问题模板格式
*/
private String defaultPromptTemplate = "";
/**
* 向量数据库设置
*/
@NestedConfigurationProperty
private final Vectorstore vectorstore = new Vectorstore();
public Boolean getToolCalls() {
return toolCalls;
}
public void setToolCalls(Boolean toolCalls) {
this.toolCalls = toolCalls;
}
public String getDefaultSystem() {
return defaultSystem;
}
public void setDefaultSystem(String defaultSystem) {
this.defaultSystem = defaultSystem;
}
public String getDefaultPromptTemplate() {
return defaultPromptTemplate;
}
public void setDefaultPromptTemplate(String defaultPromptTemplate) {
this.defaultPromptTemplate = defaultPromptTemplate;
}
public Vectorstore getVectorstore() {
return vectorstore;
}
public static class Vectorstore {
/**
* 向量库类型选择chroma、pgvector、elasticsearch、milvus
*/
private String type;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
}

View File

@@ -0,0 +1,221 @@
/**
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
*/
package com.jeesite.modules.cms.ai.service;
import com.jeesite.common.collect.ListUtils;
import com.jeesite.common.collect.MapUtils;
import com.jeesite.common.config.Global;
import com.jeesite.common.io.IOUtils;
import com.jeesite.common.lang.StringUtils;
import com.jeesite.common.lang.TimeUtils;
import com.jeesite.common.utils.PageUtils;
import com.jeesite.common.web.http.HttpClientUtils;
import com.jeesite.common.web.http.ServletUtils;
import com.jeesite.modules.cms.entity.Article;
import com.jeesite.modules.cms.service.ArticleVectorStore;
import com.jeesite.modules.cms.utils.CmsUtils;
import com.vladsch.flexmark.html.renderer.LinkType;
import com.vladsch.flexmark.html.renderer.ResolvedLink;
import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter;
import com.vladsch.flexmark.html2md.converter.HtmlLinkResolver;
import com.vladsch.flexmark.html2md.converter.HtmlLinkResolverFactory;
import com.vladsch.flexmark.html2md.converter.HtmlNodeConverterContext;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.tika.Tika;
import org.apache.tika.config.TikaConfig;
import org.apache.tika.exception.TikaException;
import org.apache.tika.io.TikaInputStream;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.mime.MediaType;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.document.Document;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.ai.vectorstore.filter.FilterExpressionBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* CMS 文章向量库存储
* @author ThinkGem
*/
@Service
public class ArticleVectorStoreImpl implements ArticleVectorStore {
protected Logger logger = LoggerFactory.getLogger(getClass());
@Autowired(required = false)
private VectorStore vectorStore;
/**
* 保存文章到向量库
* @author ThinkGem
*/
@Override
public void save(Article article) {
if (vectorStore == null) return;
Map<String, Object> metadata = MapUtils.newHashMap();
metadata.put("id", article.getId());
metadata.put("siteCode", article.getCategory().getSite().getSiteCode());
metadata.put("categoryCode", article.getCategory().getCategoryCode());
metadata.put("categoryName", article.getCategory().getCategoryName());
metadata.put("title", article.getTitle());
metadata.put("href", article.getHref());
metadata.put("keywords", article.getKeywords());
metadata.put("description", article.getDescription());
metadata.put("url", article.getUrl());
metadata.put("status", article.getStatus());
metadata.put("createBy", article.getCreateBy());
metadata.put("createDate", article.getCreateDate());
metadata.put("updateBy", article.getUpdateBy());
metadata.put("updateDate", article.getUpdateDate());
List<String> attachmentList = ListUtils.newArrayList();
String content = article.getTitle() + ", " + article.getKeywords() + ", "
+ article.getDescription() + ", " + FlexmarkHtmlConverter.builder()
.linkResolverFactory(getHtmlLinkResolverFactory(attachmentList)).build()
.convert(article.getArticleData().getContent())
+ ", attachment: " + attachmentList;
List<Document> documents = List.of(new Document(article.getId(), content, metadata));
List<Document> splitDocuments = new TokenTextSplitter().apply(documents);
this.delete(article); // 删除原数据
ListUtils.pageList(splitDocuments, 10, params -> {
vectorStore.add((List<Document>)params[0]); // 增加新数据
return null;
});
}
/**
* 解析文章中的连接并提取内容
* @author ThinkGem
*/
private @NotNull HtmlLinkResolverFactory getHtmlLinkResolverFactory(List<String> attachmentList) {
HttpServletRequest request = ServletUtils.getRequest();
return new HtmlLinkResolverFactory() {
@Override
public @NotNull Set<Class<?>> getAfterDependents() {
return Set.of();
}
@Override
public @NotNull Set<Class<?>> getBeforeDependents() {
return Set.of();
}
@Override
public boolean affectsGlobalScope() {
return false;
}
@Override
public HtmlLinkResolver apply(HtmlNodeConverterContext htmlNodeConverterContext) {
return (node, context, resolvedLink) -> {
if ("a".equalsIgnoreCase(node.nodeName())) {
String href = node.attributes().get("href"); String url = href;
if (StringUtils.contains(url, "://")) {
// 只提取系统允许跳转的附件内容外部网站内容不进行提取shiro.allowRedirects 参数设置范围
if (ServletUtils.isAllowRedirects(request, url)) {
try (InputStream is = HttpClientUtils.getInputStream(url, null)) {
if (is != null) {
String text = getDocumentText(is);
attachmentList.add(url + text);
}
} catch (IOException | TikaException e) {
logger.error(e.getMessage(), e);
}
}
} else {
String ctxPath = Global.getCtxPath();
if (StringUtils.isNotBlank(ctxPath) && StringUtils.startsWith(url, ctxPath)){
url = url.substring(ctxPath.length());
}
try (InputStream is = IOUtils.getFileInputStream(Global.getUserfilesBaseDir(url))){
if (is != null) {
String text = getDocumentText(is);
attachmentList.add(url + text);
}
} catch (IOException | TikaException e) {
logger.error(e.getMessage(), e);
}
}
return new ResolvedLink(LinkType.LINK, href);
}
return resolvedLink;
};
}
/**
* 获取文章附件中的内容
* @author ThinkGem
*/
private static @NotNull String getDocumentText(InputStream is) throws IOException, TikaException {
TikaConfig config = TikaConfig.getDefaultConfig();
Tika tika = new Tika(config);
Metadata metadata = new Metadata();
TikaInputStream stream = TikaInputStream.get(is);
MediaType mimetype = tika.getDetector().detect(stream, metadata);
if (mimetype != null && StringUtils.equals(mimetype.getType(), "text")) {
String text = IOUtils.toString(stream, StandardCharsets.UTF_8);
if (StringUtils.isNotBlank(text)) {
return FlexmarkHtmlConverter.builder().build().convert(text);
} else {
return text;
}
}
String content = tika.parseToString(stream, metadata);
return content.lines()
.map(String::strip).filter(line -> !line.isEmpty())
.reduce((a, b) -> a + System.lineSeparator() + b)
.orElse(StringUtils.EMPTY);
}
};
}
/**
* 删除向量库文章
* @author ThinkGem
*/
@Override
public void delete(Article article) {
if (vectorStore == null) return;
if (StringUtils.isNotBlank(article.getId())) {
vectorStore.delete(new FilterExpressionBuilder().eq("id", article.getId()).build());
}
}
/**
* 重建向量库文章
* @author ThinkGem
*/
public String rebuild(Article article) {
if (vectorStore == null) return null;
logger.debug("开始重建向量库。 siteCode: {}, categoryCode: {}",
article.getCategory().getSite().getSiteCode(),
article.getCategory().getCategoryCode());
long start = System.currentTimeMillis();
try{
article.setIsQueryArticleData(true); // 查询文章内容
PageUtils.findList(article, null, e -> {
List<Article> list = CmsUtils.getArticleService().findList((Article) e);
if (!list.isEmpty()) {
list.forEach(this::save);
return true;
}
return false;
});
}catch(Exception ex){
logger.error("重建向量库失败", ex);
return "重建向量库失败:" + ex.getMessage();
}
String message = "重建向量库完成! 用时" + TimeUtils.formatTime(System.currentTimeMillis() - start) + "";
logger.debug(message);
return message;
}
}

View File

@@ -0,0 +1,44 @@
/**
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
*/
package com.jeesite.modules.cms.ai.service;
import com.jeesite.common.cache.CacheUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.ai.chat.memory.ChatMemoryRepository;
import org.springframework.ai.chat.messages.Message;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* AI 对话消息存储
* @author ThinkGem
*/
@Service
public class CacheChatMemoryRepository implements ChatMemoryRepository {
private static final String CMS_CHAT_MSG_CACHE = "cmsChatMsgCache";
@Override
public @NotNull List<String> findConversationIds() {
return CacheUtils.getCache(CMS_CHAT_MSG_CACHE).keys().stream().map(Object::toString).toList();
}
@Override
public @NotNull List<Message> findByConversationId(@NotNull String conversationId) {
List<Message> all = CacheUtils.get(CMS_CHAT_MSG_CACHE, conversationId);
return all != null ? all : List.of();
}
@Override
public void saveAll(@NotNull String conversationId, @NotNull List<Message> messages) {
CacheUtils.put(CMS_CHAT_MSG_CACHE, conversationId, messages);
}
@Override
public void deleteByConversationId(@NotNull String conversationId) {
CacheUtils.remove(CMS_CHAT_MSG_CACHE, conversationId);
}
}

View File

@@ -0,0 +1,268 @@
/**
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
*/
package com.jeesite.modules.cms.ai.service;
import com.jeesite.common.cache.CacheUtils;
import com.jeesite.common.collect.ListUtils;
import com.jeesite.common.collect.MapUtils;
import com.jeesite.common.idgen.IdGen;
import com.jeesite.common.lang.DateUtils;
import com.jeesite.common.lang.StringUtils;
import com.jeesite.common.mapper.JsonMapper;
import com.jeesite.common.service.BaseService;
import com.jeesite.modules.cms.ai.properties.CmsAiProperties;
import com.jeesite.modules.sys.entity.Area;
import com.jeesite.modules.sys.utils.AreaUtils;
import com.jeesite.modules.sys.utils.UserUtils;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.client.advisor.vectorstore.QuestionAnswerAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.model.Generation;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.content.Media;
import org.springframework.ai.converter.AbstractMessageOutputConverter;
import org.springframework.ai.converter.BeanOutputConverter;
import org.springframework.ai.converter.MapOutputConverter;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Flux;
import reactor.core.publisher.SignalType;
import java.util.List;
import java.util.Map;
/**
* AI 聊天服务类
* @author ThinkGem
*/
@Service
public class CmsAiChatService extends BaseService {
private static final String CMS_CHAT_CACHE = "cmsChatCache";
private static final String[] USER_MESSAGE_SEARCH = new String[]{"{", "}"};
private static final String[] USER_MESSAGE_REPLACE = new String[]{"\\{", "\\}"};
@Autowired
private ChatClient chatClient;
@Autowired
private ChatMemory chatMemory;
@Autowired(required = false)
private VectorStore vectorStore;
@Autowired
private CmsAiProperties properties;
/**
* 获取聊天对话消息
* @author ThinkGem
*/
public List<Message> getChatMessage(String conversationId) {
if (StringUtils.isBlank(conversationId)) {
return List.of();
}
return chatMemory.get(conversationId);
}
private static String getChatCacheKey() {
String key = UserUtils.getUser().getId();
if (StringUtils.isBlank(key)) {
key = UserUtils.getSession().getId().toString();
}
return key;
}
public Map<String, Map<String, Object>> getChatCacheMap() {
Map<String, Map<String, Object>> cache = CacheUtils.get(CMS_CHAT_CACHE, getChatCacheKey());
if (cache == null) {
cache = MapUtils.newHashMap();
}
return cache;
}
/**
* 新建或更新聊天对话
* @author ThinkGem
*/
public Map<String, Object> saveChatConversation(String conversationId, String title) {
if (StringUtils.isBlank(conversationId)) {
conversationId = IdGen.nextId();
}
if (StringUtils.isBlank(title)) {
title = "新对话 " + DateUtils.getTime();
}
Map<String, Object> map = MapUtils.newHashMap();
map.put("id", conversationId);
map.put("title", title);
Map<String, Map<String, Object>> cache = getChatCacheMap();
cache.put(conversationId, map);
CacheUtils.put(CMS_CHAT_CACHE, getChatCacheKey(), cache);
return map;
}
/**
* 删除聊天对话
* @author ThinkGem
*/
public void deleteChatConversation(String conversationId) {
Map<String, Map<String, Object>> cache = getChatCacheMap();
cache.remove(conversationId);
CacheUtils.put(CMS_CHAT_CACHE, getChatCacheKey(), cache);
chatMemory.clear(conversationId);
}
/**
* 聊天对话,流输出
* @author ThinkGem
*/
public Flux<ChatResponse> chatStream(String conversationId, String message, HttpServletRequest request) {
String text = StringUtils.replaceEach(message, USER_MESSAGE_SEARCH, USER_MESSAGE_REPLACE);
List<Media> media = ListUtils.newArrayList();
// List<FileUpload> fileUploadList = FileUploadUtils.findFileUpload(conversationId, "cms-chat");
// for (FileUpload fileUpload : fileUploadList) {
// File file = new File(fileUpload.getFileEntity().getFileRealPath());
// MediaType mediaType = MediaType.parseMediaType(FileUtils.getContentType(file.getName()));
// media.add(Media.builder().mimeType(mediaType).data(file).build());
// }
UserMessage userMessage = UserMessage.builder().text(text).media(media).build();
ChatClient.ChatClientRequestSpec spec = chatClient.prompt().messages(userMessage)
.advisors(MessageChatMemoryAdvisor.builder(chatMemory)
.conversationId(conversationId)
.build());
if (vectorStore != null) {
spec.advisors(QuestionAnswerAdvisor.builder(vectorStore)
.searchRequest(SearchRequest.builder().similarityThreshold(0.6F).topK(6).build())
.promptTemplate(new PromptTemplate(properties.getDefaultPromptTemplate()))
.build());
}
return spec.stream()
.chatResponse()
.doOnNext(response -> {
if (response.getResult() != null && StringUtils.isNotBlank(response.getResult().getOutput().getText())) {
AssistantMessage assistantMessage = (AssistantMessage)request.getAttribute("assistantMessage");
AssistantMessage currAssistantMessage = response.getResult().getOutput();
if (assistantMessage == null) {
request.setAttribute("assistantMessage", currAssistantMessage);
} else {
request.setAttribute("assistantMessage", new AssistantMessage(
assistantMessage.getText() + currAssistantMessage.getText(),
currAssistantMessage.getMetadata()));
}
}
})
.doFinally((signalType) -> {
if (signalType != SignalType.ON_COMPLETE) {
AssistantMessage assistantMessage = (AssistantMessage)request.getAttribute("assistantMessage");
if (assistantMessage != null) {
chatMemory.add(conversationId, assistantMessage);
} else if (signalType == SignalType.CANCEL) {
chatMemory.add(conversationId, new AssistantMessage(text("暂无消息,你已主动停止响应。")));
}
}
})
.onErrorResume(error -> {
String errorMessage = error.getMessage();
if (error instanceof WebClientResponseException webClientError) {
errorMessage = webClientError.getResponseBodyAsString();
}
AssistantMessage assistantMessage = new AssistantMessage(errorMessage);
chatMemory.add(conversationId, assistantMessage);
logger.error("Error message: {}", errorMessage);
return Flux.just(ChatResponse.builder()
.generations(List.of(new Generation(assistantMessage)))
.build());
});
}
/**
* 聊天对话,文本输出
* @author ThinkGem
*/
public String chatText(String message) {
return chatClient.prompt()
.messages(
new UserMessage(StringUtils.replaceEach(message, USER_MESSAGE_SEARCH, USER_MESSAGE_REPLACE))
)
.call()
.content();
}
/**
* 聊天对话结构化输出Map
* @author ThinkGem
*/
public Map<String, Object> chatJson(String message) {
return chatClient.prompt()
.messages(
new SystemMessage("""
[{name:'张三', sex:'男', age:'17'}, {name:'李四', sex:'女', age:'18'}],返回 json。
"""),
new UserMessage(StringUtils.replaceEach(message, USER_MESSAGE_SEARCH, USER_MESSAGE_REPLACE))
)
.call()
.responseEntity(
new AbstractMessageOutputConverter<Map<String, Object>>(
new MappingJackson2MessageConverter(JsonMapper.getInstance())
) {
final MapOutputConverter mapOutputConverter = new MapOutputConverter();
@Override
public Map<String, Object> convert(String source) {
return mapOutputConverter.convert(source);
}
@Override
public String getFormat() {
return mapOutputConverter.getFormat();
}
}
)
.getEntity();
}
/**
* 聊天对话结构化输出Area
* @author ThinkGem
*/
public List<Area> chatArea(String message) {
List<Area> list = AreaUtils.getAreaAllList();
if (list.size() > 10) list = list.subList(0, 10);
ChatClient.ChatClientRequestSpec spec = chatClient.prompt()
.messages(
new SystemMessage(JsonMapper.toJson(list)),
new UserMessage(StringUtils.replaceEach(message, USER_MESSAGE_SEARCH, USER_MESSAGE_REPLACE))
);
if (vectorStore != null) {
spec.advisors(QuestionAnswerAdvisor.builder(vectorStore)
.searchRequest(SearchRequest.builder().similarityThreshold(0.6F).topK(6).build())
.promptTemplate(new PromptTemplate(properties.getDefaultPromptTemplate()))
.build());
}
return spec.call()
.responseEntity(new BeanOutputConverter<>(new ParameterizedTypeReference<List<Area>>() {},
JsonMapper.getInstance()))
.getEntity();
}
// public static void main(String[] args) {
// String s = """
// [{"id":"110000","isNewRecord":false,"createBy":"system","createDate":"2025-01-01T19:25:11Z","updateBy":"system","updateDate":"2025-01-01 19:25","childList":[{"id":"110100","isNewRecord":false,"createBy":"system","createDate":"2025-01-01 19:25","updateBy":"system","updateDate":"2025-01-01 19:25","childList":[{"id":"110101","isNewRecord":false,"areaCode":"110101","areaName":"东城区","areaType":"3","isRoot":true,"isTreeLeaf":false},{"id":"110102","isNewRecord":false,"areaCode":"110102","areaName":"西城区","areaType":"3","isRoot":true,"isTreeLeaf":false},{"id":"110105","isNewRecord":false,"areaCode":"110105","areaName":"朝阳区","areaType":"3","isRoot":true,"isTreeLeaf":false},{"id":"110106","isNewRecord":false,"areaCode":"110106","areaName":"丰台区","areaType":"3","isRoot":true,"isTreeLeaf":false},{"id":"110107","isNewRecord":false,"areaCode":"110107","areaName":"石景山区","areaType":"3","isRoot":true,"isTreeLeaf":false},{"id":"110108","isNewRecord":false,"areaCode":"110108","areaName":"海淀区","areaType":"3","isRoot":true,"isTreeLeaf":false},{"id":"110109","isNewRecord":false,"areaCode":"110109","areaName":"门头沟区","areaType":"3","isRoot":true,"isTreeLeaf":false},{"id":"110111","isNewRecord":false,"areaCode":"110111","areaName":"房山区","areaType":"3","isRoot":true,"isTreeLeaf":false}],"areaCode":"110100","areaName":"北京城区","areaType":"2","isRoot":true,"isTreeLeaf":false}],"areaCode":"110000","areaName":"北京市","areaType":"1","isRoot":true,"isTreeLeaf":false}]
// """;
// JsonMapper jsonMapper = JsonMapper.getInstance();
// ParameterizedTypeReference<List<Area>> p = new ParameterizedTypeReference<List<Area>>() {};
// List<Area> entity = jsonMapper.fromJsonString(s, jsonMapper.constructType(p.getType()));
// System.out.println(entity);
// String json = jsonMapper.toJsonString(entity);
// System.out.println(json);
// }
}

View File

@@ -0,0 +1,46 @@
/**
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
*/
package com.jeesite.modules.cms.ai.tools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import java.time.LocalDateTime;
/**
* AI 工具调用、Tool calling需选择支持 Tools 的模型)
* @author ThinkGem
*/
public class CmsAiTools {
private final Logger logger = LoggerFactory.getLogger(CmsAiTools.class);
/**
* 未联网搜索的时候,可获取到服务器时间
*/
@Tool(description = "当前时间,当前日期,几点了")
public String getCurrentDateTime() {
String dateTime = "当前日期时间:" + LocalDateTime.now();
logger.info(dateTime + " ============== ");
return dateTime;
}
/**
* 你可以询问:打开客厅的灯,关闭卧室的灯(需创建新对话)
*/
@Tool(description = "房间里的灯打开或关闭")
public String turnLight(@ToolParam(description = "房间") String roomName, @ToolParam(description = "开关") boolean on) {
String message = roomName + " 房间里的灯被 " + (on ? "打开" : "关闭");
logger.info(message + " ============== ");
return String.format("""
message: %s
roomName: %s
on: %s
""",
roomName, on, message);
}
}

View File

@@ -0,0 +1,119 @@
/**
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
*/
package com.jeesite.modules.cms.ai.web;
import com.jeesite.common.config.Global;
import com.jeesite.common.web.BaseController;
import com.jeesite.modules.cms.ai.service.CmsAiChatService;
import com.jeesite.modules.sys.entity.Area;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* AI 聊天控制器类
* @author ThinkGem
*/
@RestController
@RequestMapping("${adminPath}/cms/chat")
public class CmsAiChatController extends BaseController {
@Autowired
private CmsAiChatService cmsAiChatService;
/**
* 获取聊天对话消息
* @author ThinkGem
*/
@RequestMapping("/message")
public List<Message> message(String id) {
return cmsAiChatService.getChatMessage(id);
}
/**
* 聊天对话列表
* @author ThinkGem
*/
@RequestMapping("/list")
public Collection<Map<String, Object>> list() {
return cmsAiChatService.getChatCacheMap().values().stream()
.sorted(Comparator.comparing(map -> (String) map.get("id"),
Comparator.reverseOrder())).collect(Collectors.toList());
}
/**
* 新建或更新聊天对话
* @author ThinkGem
*/
@RequestMapping("/save")
public String save(String id, String title) {
Map<String, Object> map = cmsAiChatService.saveChatConversation(id, title);
return renderResult(Global.TRUE, "保存成功", map);
}
/**
* 删除聊天对话
* @author ThinkGem
*/
@RequestMapping("/delete")
public String delete(@RequestParam String id) {
cmsAiChatService.deleteChatConversation(id);
return renderResult(Global.TRUE, "删除成功", id);
}
/**
* 聊天对话,流输出
* @author ThinkGem
* http://127.0.0.1:8980/js/a/cms/chat/stream?id=1&message=你好
*/
@RequestMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ChatResponse> stream(@RequestParam String id, @RequestParam String message, HttpServletRequest request) {
return cmsAiChatService.chatStream(id, message, request);
}
/**
* 聊天对话,文本输出
* @author ThinkGem
* http://127.0.0.1:8980/js/a/cms/chat/text?message=你好
*/
@RequestMapping(value = "/text")
public String text(@RequestParam String message) {
return cmsAiChatService.chatText(message);
}
/**
* 聊天对话,结构化输出 JSON
* @author ThinkGem
* http://127.0.0.1:8980/js/a/cms/chat/json?message=张三
* http://127.0.0.1:8980/js/a/cms/chat/json?message=打开客厅的灯
*/
@RequestMapping(value = "/json")
public Map<String, Object> json(@RequestParam String message) {
return cmsAiChatService.chatJson(message);
}
/**
* 聊天对话,结构化输出 Entity
* @author ThinkGem
* http://127.0.0.1:8980/js/a/cms/chat/entity?message=北京
*/
@RequestMapping(value = "/entity")
public List<Area> entity(@RequestParam String message) {
return cmsAiChatService.chatArea(message);
}
}

View File

@@ -0,0 +1,12 @@
## 重要提示Tip
## 请勿在该配置文件中添加其它任何配置(添加也不会生效)。
## 该文件,仅仅是为了让 jeesite-cms-ai.yml 文件,
## 在 IDEA 中有一个自动完成及帮助提示,并无其它用意。
## 参数配置请在 jeesite-cms-ai.yml 文件中添加。
spring:
config:
import:
- classpath:config/jeesite-cms-ai.yml

View File

@@ -0,0 +1,189 @@
# 温馨提示不建议直接修改此文件为了平台升级方便建议将需要修改的参数值复制到application.yml里进行覆盖该参数值。
spring:
ai:
# 模型选择openai、ollama
model:
chat: openai
embedding: ${spring.ai.model.chat}
image: ${spring.ai.model.chat}
audio: ${spring.ai.model.chat}
# 在线大模型【请在 pom.xml 中打开 openai 的注释,并注释上其它模型】
openai:
# 硅基流动
base-url: https://api.siliconflow.cn
api-key: ${SFLOW_APP_KEY}
# 聊天对话模型
chat:
options:
model: deepseek-ai/DeepSeek-R1-Distill-Qwen-7B
max-tokens: 1024
temperature: 0.6
top-p: 0.9
frequency-penalty: 0
# 向量库知识库模型(注意:不同的模型维度不同)
embedding:
options:
model: BAAI/bge-m3
dimensions: 512
# # 模力方舟
# base-url: https://ai.gitee.com
# api-key: ${GITEE_APP_KEY}
# # 聊天对话模型
# chat:
# options:
# model: DeepSeek-R1-Distill-Qwen-14B
# max-tokens: 1024
# temperature: 0.6
# top-p: 0.9
# frequency-penalty: 0
# #logprobs: true
# # 向量库知识库模型(注意:不同的模型维度不同)
# embedding:
# options:
# model: bge-large-zh-v1.5
# dimensions: 512
# # 阿里百炼
# base-url: https://dashscope.aliyuncs.com/compatible-mode
# api-key: ${BAILIAN_APP_KEY}
# # 聊天对话模型
# chat:
# options:
# model: deepseek-r1-distill-llama-8b
# max-tokens: 1024
# temperature: 0.6
# top-p: 0.9
# frequency-penalty: 0
# #logprobs: true
# # 向量库知识库模型(注意:不同的模型维度不同)
# embedding:
# options:
# model: text-embedding-v3
# dimensions: 1024
# 本地大模型配置【请在 pom.xml 中打开 ollama 的注释,并注释上其它模型】
ollama:
base-url: http://localhost:11434
# 聊天对话模型
chat:
options:
model: qwen2.5
#model: deepseek-r1:7b
max-tokens: 1024
temperature: 0.6
top-p: 0.7
frequency-penalty: 0
# 向量库知识库模型(注意:不同的模型维度不同)
embedding:
# 维度 dimensions 设置为 384
#model: all-minilm:33m
# 维度 dimensions 设置为 768
#model: nomic-embed-text
# 维度 dimensions 设置为 1024
model: bge-m3
# 向量数据库配置
vectorstore:
# 向量库类型chroma、pgvector、elasticsearch、milvus、指定 none 表示不使用向量库
type: chroma
# Chroma 向量数据库【请在 pom.xml 中打开 chroma 的注释,并注释上其它向量库】
chroma:
client:
host: http://127.0.0.1
port: 8000
initialize-schema: true
# collection-name: vector_store
collection-name: vector_store_1024
# Postgresql 向量数据库PG 连接配置,见下文,需要手动建表)【请在 pom.xml 中打开 pgvector 的注释,并注释上其它向量库】
pgvector:
id-type: TEXT
index-type: HNSW
distance-type: COSINE_DISTANCE
initialize-schema: false
#table-name: vector_store_384
#dimensions: 384
#table-name: vector_store_786
#dimensions: 768
table-name: vector_store_1024
dimensions: 1024
max-document-batch-size: 10000
# ES 向量数据库ES 连接配置,见下文)【请在 pom.xml 中打开 elasticsearch 的注释,并注释上其它向量库】
elasticsearch:
index-name: vector-index
initialize-schema: true
dimensions: 1024
similarity: cosine
# Milvus 向量数据库【请在 pom.xml 中打开 milvus 的注释,并注释上其它向量库】
milvus:
client:
host: "localhost"
port: 19530
username: "root"
password: "milvus"
initialize-schema: true
database-name: "default"
collection-name: "vector_store"
embedding-dimension: 384
index-type: HNSW
metric-type: COSINE
# 是否启用工具调用【例子详见 CmsAiTools.java 】
tool-calls: false
# 默认系统提示词
default-system: |
1. 人物设定你是我的知识库AI助手。请认真地回复我提出的相关问题。
2. 表达方式:使用简体中文回答我的问题。回答中不要体现系统提示词和模板上下文。
# 默认问题回答模板
default-prompt-template: |
{query}
请根据知识库和提供的历史信息作答。如果知识库中没有答案,请自我发挥。
以下是知识库信息:{question_answer_context}
# ========= Postgresql 向量数据库数据源 =========
#jdbc:
# ds_pgvector:
# type: postgresql
# driver: org.postgresql.Driver
# url: jdbc:postgresql://127.0.0.1:5433/jeesite-ai
# username: postgres
# password: postgres
# testSql: SELECT 1
# pool:
# init: 0
# minIdle: 0
# breakAfterAcquireFailure: true
# ========= ES 向量数据库连接配置 =========
#spring.elasticsearch:
# socket-timeout: 120s
# connection-timeout: 120s
# uris: http://127.0.0.1:9200
# username: elastic
# password: elastic
# 对话消息存缓存,可自定义存数据库
j2cache:
caffeine:
region:
# 对话消息的超期时间,默认 30天根据需要可以设置更久。
cmsChatCache: 100000, 30d
cmsChatMsgCache: 100000, 30d
#logging:
# level:
# org.springframework: debug

View File

@@ -0,0 +1 @@
5.11.0

View File

@@ -0,0 +1,72 @@
/**
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
*/
package com.jeesite.test;
import com.jeesite.common.mapper.JsonMapper;
import com.jeesite.common.tests.BaseSpringContextTests;
import com.jeesite.modules.cms.ai.service.CmsAiChatService;
import com.jeesite.modules.sys.entity.Area;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import java.util.List;
import java.util.Map;
/**
* AI 对话单元测试
* @author ThinkGem
* @version 2025-06-06
*/
@ActiveProfiles("test")
@SpringBootApplication
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@SpringBootTest(properties = {"spring.ai.tool-calls=true"})
public class AiChatServiceTest extends BaseSpringContextTests {
@Autowired
private CmsAiChatService cmsAiChatService;
@Test
public void test01Text() {
logger.info("===== 聊天对话,文本输出");
String message = "你好";
String text = cmsAiChatService.chatText(message);
System.out.println(text);
}
@Test
public void test02Json() {
logger.info("===== 聊天对话,结构化输出 JSON");
String message = "张三";
Map<String, Object> map = cmsAiChatService.chatJson(message);
System.out.println(JsonMapper.toJson(map));
}
@Test
public void test03Tool() {
logger.info("===== 聊天对话,结构化输出 Tool Calling");
String message = "打开客厅的灯";
Map<String, Object> map = cmsAiChatService.chatJson(message);
System.out.println(JsonMapper.toJson(map));
message = "关闭客厅的灯";
map = cmsAiChatService.chatJson(message);
System.out.println(JsonMapper.toJson(map));
}
@Test
public void test04Entity() {
logger.info("===== 聊天对话,结构化输出 Entity");
String message = "北京";
List<Area> list = cmsAiChatService.chatArea(message);
System.out.println(JsonMapper.toJson(list));
}
}

View File

@@ -0,0 +1,28 @@
# 产品或项目名称、软件开发公司名称
productName: JeeSite Demo
companyName: ThinkGem
# 产品版本、版权年份
productVersion: V5.13
copyrightYear: 2025
# 数据库连接
jdbc:
# Mysql 数据库配置
type: mysql
driver: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/jeesite_v5?useSSL=false&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=CONVERT_TO_NULL&serverTimezone=Asia/Shanghai
username: root
password: 123456
testSql: SELECT 1
# 日志配置
logging:
config: classpath:logback-test.xml
# 消息推送
msg:
enabled: true

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false" scan="false">
<!-- Logger level setting -->
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<include resource="config/logger-core.xml"/>
<!-- Console log output -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} %clr(%-5p) %clr([%-39logger{39}]){cyan} - %m%n%wEx</pattern>
</encoder>
</appender>
<!-- Level: FATAL 0 ERROR 3 WARN 4 INFO 6 DEBUG 7 -->
<root level="WARN">
<appender-ref ref="console" />
</root>
</configuration>

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>com.jeesite</groupId>
<artifactId>jeesite-parent</artifactId>
<version>5.13.0-SNAPSHOT</version>
<version>5.13.0.springboot3-SNAPSHOT</version>
<relativePath>../../parent/pom.xml</relativePath>
</parent>

View File

@@ -13,9 +13,11 @@ import com.jeesite.common.mybatis.annotation.Table;
import com.jeesite.common.mybatis.mapper.query.QueryType;
import com.jeesite.modules.cms.utils.CmsUtils;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import java.io.Serial;
import java.util.Date;
/**
@@ -66,6 +68,7 @@ import java.util.Date;
public class Article extends DataEntity<Article> {
public static final String DEFAULT_TEMPLATE = "viewArticle"; // 默认文章内容模板
@Serial
private static final long serialVersionUID = 1L;
private Category category; // 栏目编码

View File

@@ -4,13 +4,15 @@
*/
package com.jeesite.modules.cms.entity;
import javax.validation.constraints.Size;
import jakarta.validation.constraints.Size;
import com.jeesite.common.entity.DataEntity;
import com.jeesite.common.entity.Extend;
import com.jeesite.common.mybatis.annotation.Column;
import com.jeesite.common.mybatis.annotation.Table;
import java.io.Serial;
/**
* 文章详情表Entity
* @author 长春叭哥、ThinkGem
@@ -26,6 +28,7 @@ import com.jeesite.common.mybatis.annotation.Table;
)
public class ArticleData extends DataEntity<ArticleData> {
@Serial
private static final long serialVersionUID = 1L;
private String content; // 文章内容
private String relation; // 相关文章

View File

@@ -8,6 +8,8 @@ import com.jeesite.common.entity.DataEntity;
import com.jeesite.common.mybatis.annotation.Column;
import com.jeesite.common.mybatis.annotation.Table;
import java.io.Serial;
/**
* 文章推荐位Entity
* @author 长春叭哥、ThinkGem
@@ -20,6 +22,7 @@ import com.jeesite.common.mybatis.annotation.Table;
)
public class ArticlePosid extends DataEntity<ArticlePosid> {
@Serial
private static final long serialVersionUID = 1L;
private String articleId; // 内容编号
private String postid; // 推荐位置1轮播图 2首页推荐 3栏目页面

View File

@@ -8,6 +8,8 @@ import com.jeesite.common.entity.DataEntity;
import com.jeesite.common.mybatis.annotation.Column;
import com.jeesite.common.mybatis.annotation.Table;
import java.io.Serial;
/**
* 文章与标签关系Entity
* @author 长春叭哥、ThinkGem
@@ -20,6 +22,7 @@ import com.jeesite.common.mybatis.annotation.Table;
)
public class ArticleTag extends DataEntity<ArticleTag> {
@Serial
private static final long serialVersionUID = 1L;
private String articleId; // 内容编号
private String tagName; // 标签名称

View File

@@ -14,8 +14,10 @@ import com.jeesite.common.mybatis.annotation.Table;
import com.jeesite.common.mybatis.mapper.query.QueryType;
import com.jeesite.modules.cms.utils.CmsUtils;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import java.io.Serial;
import java.util.List;
/**
@@ -59,6 +61,7 @@ public class Category extends TreeEntity<Category> {
public static final String SHOW_MODES_CENTENT_LIST = "2"; // 首栏目内容列表
public static final String SHOW_MODES_FIRST_CONTENT = "3"; // 简介类栏目,栏目第一条内容
@Serial
private static final long serialVersionUID = 1L;
private String categoryCode; // 栏目编码
private String categoryName; // 栏目名称

View File

@@ -4,12 +4,13 @@
*/
package com.jeesite.modules.cms.entity;
import java.io.Serial;
import java.util.Date;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import jakarta.validation.constraints.Size;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.jeesite.common.entity.BaseEntity;
@@ -43,6 +44,7 @@ import com.jeesite.common.mybatis.mapper.query.QueryType;
)
public class Comment extends DataEntity<Comment> {
@Serial
private static final long serialVersionUID = 1L;
private Category category;// 分类编号

View File

@@ -6,6 +6,7 @@ package com.jeesite.modules.cms.entity;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serial;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
@@ -24,6 +25,7 @@ import com.jeesite.common.lang.ExceptionUtils;
*/
public class FileTemplate implements Comparable<FileTemplate>, Serializable {
@Serial
private static final long serialVersionUID = 1L;
private final Resource resource;
private String fileName;

View File

@@ -4,12 +4,14 @@
*/
package com.jeesite.modules.cms.entity;
import javax.validation.constraints.Size;
import jakarta.validation.constraints.Size;
import com.jeesite.common.entity.DataEntity;
import com.jeesite.common.mybatis.annotation.Column;
import com.jeesite.common.mybatis.annotation.Table;
import java.io.Serial;
/**
* 内容举报表Entity
* @author 长春叭哥、ThinkGem
@@ -26,6 +28,7 @@ import com.jeesite.common.mybatis.annotation.Table;
)
public class Report extends DataEntity<Report> {
@Serial
private static final long serialVersionUID = 1L;
private String reportSource; // 举报来源1文章、2评论
private String reportContent; // 举报内容(文章标题 评论内容)

View File

@@ -14,9 +14,10 @@ import com.jeesite.modules.cms.utils.CmsUtils;
import com.jeesite.modules.sys.utils.CorpUtils;
import com.jeesite.modules.sys.utils.UserUtils;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import java.io.Serial;
import java.util.List;
/**
@@ -54,6 +55,7 @@ public class Site extends DataEntity<Site> {
*/
public static final String DEFAULT_TEMPLATE = "index";
@Serial
private static final long serialVersionUID = 1L;
private String siteCode; // 站点编码
private String siteName; // 站点名称

View File

@@ -4,12 +4,14 @@
*/
package com.jeesite.modules.cms.entity;
import javax.validation.constraints.NotNull;
import jakarta.validation.constraints.NotNull;
import com.jeesite.common.entity.DataEntity;
import com.jeesite.common.mybatis.annotation.Column;
import com.jeesite.common.mybatis.annotation.Table;
import java.io.Serial;
/**
* 内容标签Entity
* @author 长春叭哥、ThinkGem
@@ -22,6 +24,7 @@ import com.jeesite.common.mybatis.annotation.Table;
)
public class Tag extends DataEntity<Tag> {
@Serial
private static final long serialVersionUID = 1L;
private String tagName; // 标签名称
private Integer clicknum; // 点击次数

View File

@@ -4,9 +4,10 @@
*/
package com.jeesite.modules.cms.entity;
import java.io.Serial;
import java.util.Date;
import javax.validation.constraints.Size;
import jakarta.validation.constraints.Size;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.jeesite.common.entity.BaseEntity;
@@ -57,6 +58,7 @@ import com.jeesite.common.mybatis.mapper.query.QueryType;
)
public class VisitLog extends DataEntity<VisitLog> {
@Serial
private static final long serialVersionUID = 1L;
private String requestUrl; // 请求的URL地址
private String requestUrlHost; // 受访域名

View File

@@ -44,6 +44,8 @@ public class ArticleService extends CrudService<ArticleDao, Article> {
@Autowired(required = false)
private ArticleIndexService articleIndexService;
@Autowired(required = false)
private ArticleVectorStore articleVectorStore;
@Autowired(required = false)
private PageCacheService pageCacheService;
private static final ExecutorService updateExpiredWeightThreadPool = new ThreadPoolExecutor(5, 20,
@@ -166,6 +168,10 @@ public class ArticleService extends CrudService<ArticleDao, Article> {
if (articleIndexService != null && Article.STATUS_NORMAL.equals(article.getStatus())) {
articleIndexService.save(article);
}
// 保存文章到向量数据库
if (articleVectorStore != null && Article.STATUS_NORMAL.equals(article.getStatus())) {
articleVectorStore.save(article);
}
// 清理首页、栏目和文章页面缓存
if (pageCacheService != null) {
pageCacheService.clearCache(article);
@@ -188,6 +194,14 @@ public class ArticleService extends CrudService<ArticleDao, Article> {
articleIndexService.delete(article);
}
}
// 保存文章到向量数据库
if (articleVectorStore != null) {
if (Article.STATUS_NORMAL.equals(article.getStatus())) {
articleVectorStore.save(article);
} else {
articleVectorStore.delete(article);
}
}
// 清理首页、栏目和文章页面缓存
if (pageCacheService != null) {
pageCacheService.clearCache(article);
@@ -221,6 +235,10 @@ public class ArticleService extends CrudService<ArticleDao, Article> {
if (articleIndexService != null) {
articleIndexService.delete(article);
}
// 保存文章到向量数据库
if (articleVectorStore != null) {
articleVectorStore.delete(article);
}
// 清理首页、栏目和文章页面缓存
if (pageCacheService != null) {
pageCacheService.clearCache(article);

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
*/
package com.jeesite.modules.cms.service;
import com.jeesite.common.entity.Page;
import com.jeesite.modules.cms.entity.Article;
import java.util.Map;
/**
* 文章向量存储服务类
* @author ThinkGem
*/
public interface ArticleVectorStore {
/**
* 保存索引
* @author ThinkGem
*/
void save(Article article);
/**
* 删除索引
* @author ThinkGem
*/
void delete(Article article);
/**
* 重建向量库
* @author ThinkGem
*/
String rebuild(Article article);
}

View File

@@ -27,6 +27,8 @@ public class CategoryService extends TreeService<CategoryDao, Category> {
@Autowired(required = false)
private ArticleIndexService articleIndexService;
@Autowired(required = false)
private ArticleVectorStore articleVectorStore;
@Autowired(required = false)
private PageCacheService pageCacheService;
/**
@@ -125,4 +127,15 @@ public class CategoryService extends TreeService<CategoryDao, Category> {
return articleIndexService.rebuild(new Article(category));
}
/**
* 重建向量数据库
* @author ThinkGem
*/
public String rebuildVectorStore(Category category) {
if (articleVectorStore == null) {
return text("您好,系统未配置向量数据库");
}
return articleVectorStore.rebuild(new Article(category));
}
}

View File

@@ -27,6 +27,8 @@ public class SiteService extends CrudService<SiteDao, Site> {
@Autowired(required = false)
private ArticleIndexService articleIndexService;
@Autowired(required = false)
private ArticleVectorStore articleVectorStore;
@Autowired(required = false)
private PageCacheService pageCacheService;
/**
@@ -120,5 +122,16 @@ public class SiteService extends CrudService<SiteDao, Site> {
}
return articleIndexService.rebuild(new Article(new Category(site)));
}
/**
* 重建向量数据库
* @author ThinkGem
*/
public String rebuildVectorStore(Site site) {
if (articleVectorStore == null) {
return text("您好,系统未配置向量数据库");
}
return articleVectorStore.rebuild(new Article(new Category(site)));
}
}

View File

@@ -20,7 +20,7 @@ import com.jeesite.modules.cms.service.CategoryService;
import com.jeesite.modules.cms.service.SiteService;
import org.springframework.ui.Model;
import javax.servlet.ServletContext;
import jakarta.servlet.ServletContext;
import java.util.Collections;
import java.util.List;
import java.util.Map;

View File

@@ -27,8 +27,8 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

View File

@@ -17,7 +17,7 @@ import com.jeesite.modules.cms.service.CategoryService;
import com.jeesite.modules.cms.service.FileTemplateService;
import com.jeesite.modules.cms.utils.CmsUtils;
import com.jeesite.modules.sys.utils.DictUtils;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
@@ -270,6 +270,17 @@ public class CategoryController extends BaseController {
return renderResult(Global.TRUE, categoryService.rebuildIndex(category));
}
/**
* 重建向量数据库
* @author ThinkGem
*/
@RequiresPermissions("cms:category:rebuildVectorStore")
@ResponseBody
@RequestMapping(value = "rebuildVectorStore")
public String rebuildVectorStore(Category category) {
return renderResult(Global.TRUE, categoryService.rebuildVectorStore(category));
}
/**
* 获取树结构数据
* @param excludeCode 排除的Code

View File

@@ -4,8 +4,8 @@
*/
package com.jeesite.modules.cms.web;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;

View File

@@ -4,8 +4,8 @@
*/
package com.jeesite.modules.cms.web;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;

View File

@@ -12,6 +12,8 @@ import com.jeesite.modules.cms.entity.Site;
import com.jeesite.modules.cms.service.FileTemplateService;
import com.jeesite.modules.cms.service.SiteService;
import com.jeesite.modules.sys.utils.UserUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@@ -22,8 +24,6 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
@@ -142,6 +142,17 @@ public class SiteController extends BaseController {
public String rebuildIndex(Site site) {
return renderResult(Global.TRUE, siteService.rebuildIndex(site));
}
/**
* 重建向量数据库
* @author ThinkGem
*/
@RequiresPermissions("cms:site:rebuildVectorStore")
@ResponseBody
@RequestMapping(value = "rebuildVectorStore")
public String rebuildVectorStore(Site site) {
return renderResult(Global.TRUE, siteService.rebuildVectorStore(site));
}
/**
* 选择站点

View File

@@ -21,8 +21,8 @@ import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.List;

View File

@@ -6,8 +6,8 @@ package com.jeesite.modules.cmsfront.web;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

View File

@@ -20,5 +20,7 @@ j2cache:
#spring:
# elasticsearch:
# enabled: true
# uris: http://Win11:9200
# uris: http://127.0.0.1:9200
# connection-timeout: 120s
# username: elastic
# password: elastic

View File

@@ -9,7 +9,7 @@
<a href="#" class="btn btn-default" id="btnSearch" title="${text('查询')}"><i class="fa fa-filter"></i> ${text('查询')}</a>
<a href="${ctxFront}/index" target="_blank" class="btn btn-default" title="${text('访问站点')}"><i class="fa fa-globe"></i> ${text('访问网站')}</a>
<% if(hasPermi('cms:article:edit')){ %>
<a href="${ctx}/cms/article/form" onclick="$(this).data('href', this.href+'?category.categoryCode='+$('#categoryCode').val())" class="btn btn-primary btnTool" title="${text('新增文章')}"><i class="fa fa-plus"></i> ${text('新增')}</a>
<a href="javascript:" onclick="$(this).data('href', '${ctx}/cms/article/form?category.categoryCode='+$('#categoryCode').val())" class="btn btn-primary btnTool" title="${text('新增文章')}"><i class="fa fa-plus"></i> ${text('新增')}</a>
<% } %>
</div>
</div>

View File

@@ -173,7 +173,7 @@
</div>
<% } %>
<script>
$("#inputForm").validate({
$('#inputForm').validate({
submitHandler: function(form){
js.ajaxSubmitForm($(form), function(data){
js.showMessage(data.message);

View File

@@ -107,7 +107,7 @@
</div>
<% } %>
<script>
$("#inputForm").validate({
$('#inputForm').validate({
submitHandler: function(form){
js.ajaxSubmitForm($(form), function(data){
js.showMessage(data.message);

View File

@@ -351,7 +351,7 @@
</div>
<% } %>
<script>
$("#inputForm").validate({
$('#inputForm').validate({
submitHandler: function(form){
js.ajaxSubmitForm($(form), function(data){
js.showMessage(data.message);

View File

@@ -25,7 +25,7 @@ if (message != ''){
function page(n,s){
$("#pageNo").val(n);
$("#pageSize").val(s);
$("#searchForm").submit();
$('#searchForm').submit();
return false;
}
function sel(th, val){

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>com.jeesite</groupId>
<artifactId>jeesite-parent</artifactId>
<version>5.13.0-SNAPSHOT</version>
<version>5.13.0.springboot3-SNAPSHOT</version>
<relativePath>../../parent/pom.xml</relativePath>
</parent>
@@ -75,7 +75,7 @@
<dependency>
<groupId>com.dameng</groupId>
<artifactId>DmJdbcDriver18</artifactId>
<version>8.1.3.62</version>
<version>${dameng.version}</version>
<scope>runtime</scope>
</dependency>
@@ -83,7 +83,7 @@
<dependency>
<groupId>cn.com.kingbase</groupId>
<artifactId>kingbase8</artifactId>
<version>8.6.1</version>
<version>${kingbase.version}</version>
<scope>runtime</scope>
</dependency>
@@ -135,6 +135,13 @@
<artifactId>logstash-logback-encoder</artifactId>
<version>${logstash-logback.version}</version>
</dependency>
<!-- 热部署工具 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

View File

@@ -17,6 +17,7 @@ import com.jeesite.common.shiro.session.SessionDAO;
import com.jeesite.common.shiro.session.SessionManager;
import com.jeesite.common.shiro.web.ShiroFilterFactoryBean;
import com.jeesite.common.shiro.web.WebSecurityManager;
import jakarta.servlet.Filter;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.ldap.JndiLdapContextFactory;
@@ -34,7 +35,6 @@ import org.springframework.context.annotation.DependsOn;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import javax.servlet.Filter;
import java.util.Collection;
import java.util.Map;

View File

@@ -4,6 +4,7 @@
*/
package com.jeesite.common.shiro.authc;
import java.io.Serial;
import java.util.Map;
/**
@@ -13,6 +14,7 @@ import java.util.Map;
*/
public class LdapToken extends FormToken {
@Serial
private static final long serialVersionUID = 1L;
public LdapToken() {

View File

@@ -16,10 +16,10 @@ import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.util.WebUtils;
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;
/**
* CAS过滤器

View File

@@ -20,6 +20,10 @@ import com.jeesite.common.web.CookieUtils;
import com.jeesite.common.web.http.ServletUtils;
import com.jeesite.modules.sys.entity.*;
import com.jeesite.modules.sys.utils.*;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
@@ -33,10 +37,6 @@ import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Map;

View File

@@ -8,9 +8,9 @@ import com.jeesite.common.config.Global;
import com.jeesite.common.lang.StringUtils;
import org.apache.shiro.web.filter.AccessControlFilter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
/**
* 内部系统访问过滤器

View File

@@ -6,8 +6,8 @@ package com.jeesite.common.shiro.filter;
import java.util.Map;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import org.apache.shiro.authc.AuthenticationToken;

View File

@@ -4,10 +4,10 @@
*/
package com.jeesite.common.shiro.filter;
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.commons.lang3.StringUtils;
import org.apache.shiro.session.SessionException;

View File

@@ -9,15 +9,15 @@ import com.jeesite.common.config.Global;
import com.jeesite.common.lang.StringUtils;
import com.jeesite.common.web.http.ServletUtils;
import com.jeesite.common.web.http.wrapper.GetHttpServletRequestWrapper;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.util.WebUtils;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**

View File

@@ -4,8 +4,9 @@
*/
package com.jeesite.common.shiro.filter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import java.io.IOException;
/**

View File

@@ -6,10 +6,10 @@ package com.jeesite.common.shiro.filter;
import com.jeesite.common.config.Global;
import com.jeesite.common.lang.StringUtils;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**

View File

@@ -15,6 +15,7 @@ import com.jeesite.modules.sys.entity.User;
import com.jeesite.modules.sys.service.UserService;
import com.jeesite.modules.sys.utils.LogUtils;
import com.jeesite.modules.sys.utils.UserUtils;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
@@ -22,8 +23,6 @@ import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import javax.servlet.http.HttpServletRequest;
/**
* 系统认证授权实现类
* @author ThinkGem

View File

@@ -30,8 +30,8 @@ import org.jasig.cas.client.validation.TicketValidationException;
import org.jasig.cas.client.validation.TicketValidator;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import javax.servlet.http.HttpServletRequest;
import javax.validation.ValidationException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ValidationException;
import java.util.Map;
/**

View File

@@ -4,11 +4,17 @@
*/
package com.jeesite.common.shiro.realm;
import javax.naming.AuthenticationNotSupportedException;
import javax.naming.NamingException;
import javax.naming.ldap.LdapContext;
import javax.servlet.http.HttpServletRequest;
import com.jeesite.common.shiro.authc.FormToken;
import com.jeesite.common.shiro.authc.LdapToken;
import com.jeesite.common.utils.SpringUtils;
import com.jeesite.common.web.http.ServletUtils;
import com.jeesite.modules.sys.entity.Log;
import com.jeesite.modules.sys.entity.User;
import com.jeesite.modules.sys.service.EmpUserService;
import com.jeesite.modules.sys.service.UserService;
import com.jeesite.modules.sys.utils.LogUtils;
import com.jeesite.modules.sys.utils.UserUtils;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
@@ -21,20 +27,13 @@ import org.apache.shiro.realm.ldap.LdapContextFactory;
import org.apache.shiro.realm.ldap.LdapUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import com.jeesite.common.shiro.authc.FormToken;
import com.jeesite.common.shiro.authc.LdapToken;
import com.jeesite.common.utils.SpringUtils;
import com.jeesite.common.web.http.ServletUtils;
import com.jeesite.modules.sys.entity.Log;
import com.jeesite.modules.sys.entity.User;
import com.jeesite.modules.sys.service.EmpUserService;
import com.jeesite.modules.sys.service.UserService;
import com.jeesite.modules.sys.utils.LogUtils;
import com.jeesite.modules.sys.utils.UserUtils;
import javax.naming.AuthenticationNotSupportedException;
import javax.naming.NamingException;
import javax.naming.ldap.LdapContext;
/**
* 系统认证授权实现类

View File

@@ -6,9 +6,8 @@ import com.jeesite.common.ueditor.define.AppInfo;
import com.jeesite.common.ueditor.define.BaseState;
import com.jeesite.common.ueditor.define.State;
import com.jeesite.common.ueditor.hunter.FileManager;
import com.jeesite.common.ueditor.hunter.ImageHunter;
import com.jeesite.common.ueditor.upload.Uploader;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import java.util.Map;

View File

@@ -5,7 +5,7 @@ import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;

View File

@@ -6,9 +6,9 @@ import com.jeesite.common.ueditor.define.AppInfo;
import com.jeesite.common.ueditor.define.BaseState;
import com.jeesite.common.ueditor.define.MultiState;
import com.jeesite.common.ueditor.define.State;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.io.FileUtils;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.util.Arrays;
import java.util.Collection;

View File

@@ -1,12 +1,5 @@
package com.jeesite.common.ueditor.hunter;
import com.jeesite.common.config.Global;
import com.jeesite.common.ueditor.PathFormat;
import com.jeesite.common.ueditor.define.*;
import com.jeesite.common.ueditor.upload.StorageManager;
import org.apache.commons.io.IOUtils;
import javax.servlet.http.HttpServletRequest;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.URL;
@@ -15,6 +8,19 @@ import java.util.Arrays;
import java.util.List;
import java.util.Map;
import com.jeesite.common.config.Global;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.io.IOUtils;
import com.jeesite.common.ueditor.PathFormat;
import com.jeesite.common.ueditor.define.AppInfo;
import com.jeesite.common.ueditor.define.BaseState;
import com.jeesite.common.ueditor.define.MIMEType;
import com.jeesite.common.ueditor.define.MultiState;
import com.jeesite.common.ueditor.define.State;
import com.jeesite.common.ueditor.upload.StorageManager;
/**
* 图片抓取器
*

View File

@@ -6,9 +6,9 @@ import com.jeesite.common.ueditor.define.AppInfo;
import com.jeesite.common.ueditor.define.BaseState;
import com.jeesite.common.ueditor.define.FileType;
import com.jeesite.common.ueditor.define.State;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.codec.binary.Base64;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
public final class Base64Uploader {

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