Compare commits

...

116 Commits

Author SHA1 Message Date
thinkgem
ef4d06ff2e update README.md 2024-04-01 12:03:00 +08:00
thinkgem
24d51e416e web-fast 默认使用 vue 前端展示 2024-04-01 12:02:36 +08:00
thinkgem
ed836c2f6a update PageNotFound log level 2024-04-01 11:57:44 +08:00
thinkgem
529d2b8192 代码优化 2024-04-01 11:52:46 +08:00
thinkgem
9c0885b4c9 增加msgview初始菜单地址 2024-04-01 11:52:20 +08:00
thinkgem
7ccbb05b7b 代码优化 2024-04-01 11:51:19 +08:00
thinkgem
53af15f85d 5.7.0 2024-03-28 14:15:33 +08:00
thinkgem
21b15f16e3 更新建表语句 2024-03-28 14:14:34 +08:00
thinkgem
8176853791 vue icon add prefix i- 2024-03-27 11:12:14 +08:00
thinkgem
a56e29d8f7 update import 2024-03-27 11:11:36 +08:00
thinkgem
081505f79c update import 2024-03-26 19:27:48 +08:00
thinkgem
a6adfbc726 新增IP地址黑白名单过滤器及参数配置 2024-03-25 13:57:03 +08:00
thinkgem
e883de354f 更新注释提示 2024-03-25 13:52:46 +08:00
thinkgem
f4909921ac update fonts 2024-03-22 21:45:17 +08:00
thinkgem
0eb09a0dbb 增加根据用户编码查询岗位列表接口 2024-03-22 14:43:02 +08:00
thinkgem
a58a2dcda2 侧边栏增加是否展开第一个菜单开关 2024-03-22 14:42:26 +08:00
thinkgem
1318ce840c format code 2024-03-22 14:40:54 +08:00
thinkgem
0a771a7008 EmployeePostDao 增加根据用户编码查询岗位 sqlMap().loadJoinTableAlias("p,u") 的时候有效 2024-03-22 14:40:15 +08:00
thinkgem
2ea481a417 模块管理增加生成模块的基础路径和模板存储字段,生成模块代码时给于确认提示;微服务下isLoader为true; 2024-03-22 09:16:33 +08:00
thinkgem
07d3eef8ab 修正代码生成模板logback-spring-elk和prod.xml文件名 2024-03-22 09:15:51 +08:00
thinkgem
f3c4a0dbe9 add getPageList(List<T> list, int pageNo, int pageSize) 2024-03-21 15:46:14 +08:00
thinkgem
8f14549fee 分离端调用login和index接口时,同时返回cookie。特殊情况下的iframe的页面会话同步 2024-03-20 14:05:38 +08:00
thinkgem
bccecc8b39 update productVersion 2024-03-20 09:39:05 +08:00
thinkgem
4a8bad7d35 返回响应头增加x-token,通过记住我登录系统时使用。 2024-03-13 13:34:17 +08:00
thinkgem
8ef29563f5 演示模式不允许清理会话和缓存 2024-03-13 13:29:02 +08:00
thinkgem
87b8dc6ec4 返回响应头增加x-token,通过记住我登录系统时使用。 2024-03-13 13:18:36 +08:00
thinkgem
b1f29a9d57 代码生成器config.xml的attrType字典增加attrName,用于选择控件编码和名称获取 2024-03-13 11:35:16 +08:00
thinkgem
33eb95f120 登录超时后不再统计在线人数 2024-03-12 13:20:44 +08:00
thinkgem
4bfe7b2316 缩短Logback日志TRACE_ID的长度 2024-03-12 12:24:24 +08:00
thinkgem
b0350b36d7 完善内部消息保存后的提示消息 2024-03-12 12:23:39 +08:00
thinkgem
9f16332c09 代码生成模板添加公司选择组件v2 2024-03-11 21:24:20 +08:00
thinkgem
419ed4f01b 新增 AesTypeHandler 处理字段数据加密解密或脱敏;新增 JsonTypeHandler 处理字段数据进行 JSON 字符串与 Java 对象 自动互转 2024-03-11 17:02:40 +08:00
thinkgem
69cd032b7e 代码优化 2024-03-11 17:01:20 +08:00
thinkgem
c0aeef4881 代码生成模板添加公司选择组件 2024-03-10 10:44:00 +08:00
thinkgem
e7c6e7e85b add vueFormRoute.xml 2024-03-10 10:42:17 +08:00
thinkgem
7f06df2bed 根据当前用户有的子系统,切换到默认系统下 2024-03-08 13:29:42 +08:00
thinkgem
250788a398 update nacos config 2024-03-04 11:50:43 +08:00
thinkgem
196cf4f10c 更新Docker多CPU平台架构 2024-02-28 10:35:36 +08:00
thinkgem
3b587c661b fix h2db job sql 2024-02-28 10:07:14 +08:00
thinkgem
7eb9484d7e TimeUtils millisecond to ms 2024-02-27 11:30:41 +08:00
thinkgem
c610af7c6b 分页栏居右、布局调整、样式美化 2024-02-23 14:16:28 +08:00
thinkgem
0c7cefb184 next version 2024-02-23 14:05:06 +08:00
thinkgem
19d2399eb2 5.6.1 2024-02-18 16:01:14 +08:00
thinkgem
37b0a120c9 1、修改默认值,oracle 下默认不启用 maxPoolPreparedStatementPerConnectionSize 可根据需要开启;2、将 com.oracle.ojdbc 驱动替换为 com.oracle.database.jdbc 驱动;3、默认将 jtds 驱动替换为 sqlserver 驱动; 2024-02-02 14:03:11 +08:00
thinkgem
5fb6ab0ca6 ueditor黑暗模式下的文字 2024-01-31 12:04:34 +08:00
thinkgem
2ba8b953d3 更新路由表单组件 2024-01-30 16:53:39 +08:00
thinkgem
738fd5c552 form:fileupload组件默认加dataMap属性 2024-01-30 11:40:29 +08:00
thinkgem
745f6e88c0 新增 js.ajaxSubmitForm支持application/json提交表单;新增 js.ajaxSubmitJson 方法 2024-01-30 11:38:39 +08:00
thinkgem
3db837d6d6 代码优化 2024-01-26 14:43:03 +08:00
thinkgem
14054a964f ts type check 2024-01-23 17:53:07 +08:00
thinkgem
14581655d2 code format 2024-01-17 16:17:09 +08:00
thinkgem
f162584d1e 代码优化,方便自定义内嵌的Web服务 2024-01-15 13:44:39 +08:00
thinkgem
e57e8127b5 报表增加请求参数的例子 2024-01-10 13:08:17 +08:00
thinkgem
b7d4efa6a6 update 2024-01-10 13:04:34 +08:00
thinkgem
91a4db4eb8 增加vue无框架页面的组件类型 2024-01-09 16:42:53 +08:00
thinkgem
7ed84e48cf 存储Key增加了默认,个性化时再指定 2024-01-08 15:25:41 +08:00
thinkgem
f068bd9c90 update version 2024-01-08 10:39:18 +08:00
thinkgem
4ae907a023 update 2024-01-05 20:49:46 +08:00
thinkgem
652a242c2b 5.6.0 2024-01-04 12:06:16 +08:00
thinkgem
d242408de2 5.6.0 2024-01-03 16:58:35 +08:00
thinkgem
b8f51011ed 配置类 ShiroConfig 改为 ShiroAutoConfiguration 方便覆写 2024-01-03 08:53:55 +08:00
thinkgem
84913ab783 分号修正 2024-01-03 08:24:38 +08:00
thinkgem
b7bb765193 update 2024 2024-01-02 10:24:19 +08:00
thinkgem
d1b06c45c8 add ConditionalOnMissingBean 2024-01-02 10:23:37 +08:00
thinkgem
19fe2023a6 update 2024-01-02 10:22:57 +08:00
thinkgem
590046d90c select2 i18n fix 2023-12-29 10:56:03 +08:00
thinkgem
00c162ece1 代码优化提示 2023-12-28 09:33:20 +08:00
thinkgem
d5deb38769 优化在线用户列表数值类型的排序 2023-12-27 10:47:44 +08:00
thinkgem
b71165f73c 代码生成模板:新增Vue路由表单、新增Vue弹窗表单、新增仅后端模板 2023-12-24 21:36:09 +08:00
thinkgem
4260accf88 不设置adminPath的时候需要增加oauth2的地址认证排除 2023-12-23 23:01:37 +08:00
thinkgem
353131ab2c 用户管理增加解冻用户功能提示 2023-12-23 23:00:30 +08:00
thinkgem
acb6baa226 细节优化 2023-12-23 22:59:38 +08:00
thinkgem
5b7f7b0860 spring boot 2.7.18、jackson 2.16.0、fastjson 2.0.43、mybatis 3.5.14、shiro 1.13.0 2023-12-13 21:37:09 +08:00
thinkgem
c57238e279 spring.data.elasticsearch.enabled to spring.elasticsearch.enabled 2023-12-13 21:06:42 +08:00
thinkgem
24ea4aaed0 增加一些vue下用的路由 2023-12-12 09:42:56 +08:00
thinkgem
0af1ae18c8 update 2023-12-12 09:41:38 +08:00
thinkgem
e054df919f BpmButton.vue将提交审批信息移动到提交对话框中 2023-12-07 17:20:30 +08:00
thinkgem
a9ca557672 update 2023-12-05 16:30:31 +08:00
thinkgem
5bc76dd23d update 2023-12-05 16:12:42 +08:00
thinkgem
f337faeecc 代码优化 2023-12-03 15:55:22 +08:00
thinkgem
a6d84c59c9 vue兼容优化 2023-12-01 10:40:54 +08:00
thinkgem
b8279ffe64 优化修改密码后的操作 2023-11-28 16:15:16 +08:00
thinkgem
af44331820 优化vue生成模板 2023-11-28 15:46:05 +08:00
thinkgem
00ead34076 BasicTable子表加TextArea组件 2023-11-27 12:23:54 +08:00
thinkgem
9b07e167f7 树表的局部刷新改进,删除、停用、启用、修改父节点的情况下完美体验 2023-11-27 10:11:56 +08:00
thinkgem
0fb15e31d6 next 2023-11-27 09:06:15 +08:00
thinkgem
3e769325f8 Layer弹窗后首先关闭获取焦点,防止回车弹出多个 2023-11-22 11:14:08 +08:00
thinkgem
d555d508c3 5.5.2 2023-11-21 21:47:55 +08:00
thinkgem
11511c14bd 更新h2的库名 2023-11-20 16:01:08 +08:00
thinkgem
2238ca062f 添加自动切换到脚本所在目录 2023-11-20 15:43:41 +08:00
thinkgem
3563f18335 更新docker运行脚本帮助 2023-11-20 15:16:26 +08:00
thinkgem
983ad2faff add db docs xlsx png 2023-11-17 11:17:35 +08:00
thinkgem
f569da908d 增加quartz表ER图和注释 2023-11-16 11:57:02 +08:00
thinkgem
2e0718ba41 增加vue的ListSelect选择框代码生成模板 2023-11-09 11:31:31 +08:00
thinkgem
846bf82375 Byte格式化默认显示两位小数 2023-11-08 11:46:51 +08:00
thinkgem
8cc6f25dd1 添加jdbc.connectTimeou和jdbc.socketTimeout连接池参数 2023-10-30 08:44:44 +08:00
thinkgem
5ff9ca9815 update version 2023-10-26 11:38:15 +08:00
thinkgem
31e6b01b4e 新增一个日志清理接口,可根据需要创建job调用 2023-10-26 11:27:50 +08:00
thinkgem
bc8a6d2db7 新增富文本和头像的对象存储调用 2023-10-23 10:57:27 +08:00
thinkgem
9a13b33dd8 查询和重置增加图标、搜索框布局优化 2023-10-23 10:55:34 +08:00
thinkgem
4bdba535bb 5.5.1 2023-10-16 21:43:00 +08:00
thinkgem
09bf40feee update README.md 2023-10-16 20:06:08 +08:00
thinkgem
5d74d2783f update README.md 2023-10-16 07:00:46 +08:00
thinkgem
807b47dbc4 docker update 2023-10-12 14:50:32 +08:00
thinkgem
456cdaf883 Merge remote-tracking branch 'origin/v5.temp' into v5.temp 2023-10-07 17:14:35 +08:00
thinkgem
d1b588b7d5 spring boot 2.7.16 2023-10-07 15:05:45 +08:00
thinkgem
22a4b0fcf7 update README.md 2023-10-07 10:57:23 +08:00
thinkgem
84407b5785 修改erm默认字体大小,适应多操作系统 2023-10-01 16:52:45 +08:00
thinkgem
f5771c56a4 修改erm默认字体大小,适应多操作系统 2023-09-28 16:54:29 +08:00
thinkgem
a3dee0f70a 优化JsonMapper,增加XSS过滤通用方法 2023-09-27 13:19:22 +08:00
thinkgem
797abcdf87 update README.md 2023-09-19 14:27:28 +08:00
thinkgem
0559c79687 update 2023-09-18 10:59:26 +08:00
thinkgem
f514b86bbc linux下如果有前缀就不增加了 2023-09-18 10:59:10 +08:00
thinkgem
49992195df update script 2023-09-12 10:26:09 +08:00
thinkgem
631225a108 new version 2023-09-11 13:49:25 +08:00
thinkgem
5922951c16 add startup info 2023-08-30 15:32:57 +08:00
194 changed files with 13685 additions and 7391 deletions

View File

@@ -4,7 +4,7 @@
</p>
<h3 align="center" style="margin:30px 0 30px;font-weight:bold;font-size:30px;">快速开发平台 - Spring Boot</h3>
<p align="center">
<a href="https://jeesite.com/docs/upgrade/" target="__blank"><img alt="JeeSite-V5.5" src="https://img.shields.io/badge/JeeSite-V5.5-success.svg"></a>
<a href="https://jeesite.com/docs/upgrade/" target="__blank"><img alt="JeeSite-V5.7" src="https://img.shields.io/badge/JeeSite-V5.7-success.svg"></a>
<a href="https://spring.io/projects/spring-boot#learn" target="__blank"><img alt="SpringBoot-2.7" src="https://img.shields.io/badge/SpringBoot-2.7-blue.svg"></a>
<a href="https://gitee.com/thinkgem/jeesite4/stargazers" target="__blank"><img alt="star" src="https://gitee.com/thinkgem/jeesite4/badge/star.svg?theme=dark"></a>
<a href="https://gitee.com/thinkgem/jeesite4/members" target="__blank"><img alt="fork" src="https://gitee.com/thinkgem/jeesite4/badge/fork.svg?theme=dark"></a>
@@ -28,21 +28,27 @@
## 平台介绍
JeeSite 快速开发平台,不仅仅是一个后台开发框架,它是一个企业级快速开发解决方案,后端基于经典组合 Spring Boot、Shiro、MyBatis前端采用 Beetl、Bootstrap、AdminLTE 经典开发模式,或者分离版 Vue3、Vite、Ant Design Vue、TypeScript、Vben Admin 最先进技术栈。提供在线代码生成功能可自动创建业务模块工程和微服务模块工程自动生成前端代码和后端代码包括功能模块如组织机构、角色用户、菜单及按钮授权、数据权限、系统参数、内容管理、工作流等。采用松耦合设计微内核和插件架构模块增减便捷界面无刷新一键换肤众多账号安全设置密码策略文件在线预览消息推送多元化第三方登录在线定时任务配置支持集群支持SAAS支持多数据源支持读写分离、分库分表支持微服务应用。
JeeSite 快速开发平台,不仅仅是一个后台开发框架,它是一个企业级快速开发解决方案,后端基于经典组合 Spring Boot、Shiro、MyBatis前端采用 Beetl、Bootstrap、AdminLTE 经典开发模式,或者分离版 Vue3、Vite、Ant Design Vue、TypeScript、Vben Admin 最先进技术栈。
JeeSite 快速开发平台的主要目的是能够让初级的研发人员快速的开发出复杂的业务功能,中高级人员有时间做一些更有用的事情。让开发者注重专注业务,其余有平台来封装技术细节,降低技术难度,从而节省人力成本,缩短项目周期,提高软件安全质量
提供在线代码生成功能,可自动创建业务模块工程和微服务模块工程,自动生成前端代码和后端代码;包括功能模块如:组织机构、角色用户、菜单及按钮授权、数据权限、系统参数、内容管理、工作流等
JeeSite 自 2013 年发布以来已被广大爱好者用到了企业、政府、医疗、金融、互联网等各个领域中JeeSite 架构精良、易于扩展、大众思维的设计模式、工匠精神打磨每一个细节,深入开发者的内心,并荣获开源中国《最受欢迎中国开源软件》奖杯,期间也帮助了不少刚毕业的大学生,教师作为入门教材,快速的去实践
采用松耦合设计微内核和插件架构模块增减便捷界面细节到位一键换肤众多账号安全设置密码策略文件在线预览消息推送多元化第三方登录在线定时任务配置支持集群支持SAAS支持多数据源支持读写分离、分库分表支持微服务应用
JeeSite4 的升级,作者结合了多年总结和经验,以及各方面的应用案例,对架构完成了一次全部重构,也纳入很多新的思想。不管是从开发者模式、底层架构、逻辑处理还是到用户界面,用户交互体验上都有很大的进步,在不忘学习成本、提高开发效率的情况下,安全方面也做和很多工作,包括:身份认证、密码策略、安全审计、日志收集等众多安全选项供你选择。努力为大中小微企业打造全方位企业级快速开发解决方案
主要目的是能够让初级的研发人员快速的开发出复杂的业务功能,中高级人员有时间做一些更有用的事情。让开发者注重专注业务,其余有平台来封装技术细节,降低技术难度,从而节省人力成本,缩短项目周期,提高软件安全质量
JeeSite 自 2013 年发布以来已被广大爱好者用到了企业、政府、医疗、金融、互联网等各个领域中,拥有:精良架构、易于扩展、大众思维的设计模式,工匠精神,用心打磨每一个细节,深入开发者的内心,并荣获开源中国《最受欢迎中国开源软件》多次奖项,期间也帮助了不少刚毕业的大学生,教师作为入门教材,快速的去实践。
2019 年换代升级,作者结合了多年总结和经验,以及各方面的应用案例,对架构完成了一次全部重构,也纳入很多新的思想。不管是从开发者模式、底层架构、逻辑处理还是到用户界面,用户交互体验上都有很大的进步,在不忘学习成本、提高开发效率的情况下,安全方面也做和很多工作,包括:身份认证、密码策略、安全审计、日志收集等众多安全选项供您选择。努力为大中小微企业打造全方位企业级快速开发解决方案。
2021 年终发布 Vue3 的前后分离版本,使得 JeeSite 拥有同一个后台服务 Web 来支撑分离版和全栈版两套前端技术栈。
## 平台优势
JeeSite 整体架构清晰、稳定技术先进、源代码书写规范、经典技术会的人多、易于维护、易于扩展、安全稳定。
JeeSite 非常易于二次开发,可控性高,整体架构清晰、技术稳定而先进、源代码书写规范、经典技术会的人多、易于维护、易于扩展、安全稳定。
JeeSite 功能全,知识点非常多,也非常少。因为她使用的都是一些通用的技术,通俗的设计风格,大多数基础知识点多数人都能掌握,所以每一个 JeeSite 的功能点都非常容易掌握。只要学会使用这些功能和组件的应用,就可以顺利的完成系统开发了。
JeeSite 功能全,知识点非常多,也非常少。因为她使用的都是一些通用的技术,通俗的设计风格,大多数基础知识点多数人都能掌握,所以每一个 JeeSite 的功能点都非常容易掌握。只要学会使用这些功能和组件的应用,就可以顺利的完成系统开发了。
JeeSite 是一个低代码开发平台,具有较高的封装度、扩展性,封装不是限制去做一些事情而是在便捷的同时也具有较好的扩展性在不具备一些功能的情况下JeeSite 提供了扩展接口,提供了原生调用方法。
JeeSite 是一个低代码开发平台,具有较高的封装度、扩展性,封装不是限制去做一些事情而是在便捷的同时也具有较好的扩展性在不具备一些功能的情况下JeeSite 提供了扩展接口,提供了原生调用方法。
大家都在用 Spring也在学习 Spring 的优点Spring 提供了较好的扩展性,可又有多少人去修改它的源代码呢,退一步说,大家去修改了 Spring 的源码反而会对未来升级造成很大困扰您说不是呢这样的例子很多所以不要纠结我们非常注重这一点JeeSite 也一样具备强大的扩展性。
@@ -51,12 +57,13 @@ JeeSite 是一个低代码开发平台,具有较高的封装度、扩展性,
* 至今 JeeSite 平台架构已经非常稳定。
* JeeSite 精益求精,用心打磨每一个细节。
* JeeSite 是一个专业的平台,是一个让你使用放心的平台。
* 社区版基于 Apache License 2.0 开源协议,永久免费使用。
### 架构特点及安全方面的优势:<https://jeesite.com/docs/feature/>
## 技术选型
* 主框架Spring Boot 2.7、Spring Framework 5.3、Apache Shiro 1.11、J2Cache
* 主框架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
@@ -66,7 +73,7 @@ JeeSite 是一个低代码开发平台,具有较高的封装度、扩展性,
* Vue3 版 支持现代浏览器,如:谷歌 Chrome 86+、火狐、国产浏览器 等
* 技术选型(详细):<http://jeesite.com/docs/technology/>
* JeeSite Vue 版本:<https://gitee.com/thinkgem/jeesite-vue>
* Spring Boot 3.x 版本https://gitee.com/thinkgem/jeesite4/tree/v5.springboot3/
* Spring Boot 3.x 版本:<https://gitee.com/thinkgem/jeesite4/tree/v5.springboot3>
## 更多介绍
@@ -78,50 +85,56 @@ JeeSite 是一个低代码开发平台,具有较高的封装度、扩展性,
## 生态系统
* 分布式微服务Spring Cloud<https://gitee.com/thinkgem/jeesite4-cloud>
* 分布式微服务Spring Cloud<https://gitee.com/thinkgem/jeesite-cloud>
* Flowable业务流程引擎BPM<http://jeesite.com/docs/bpm/>
* JFlow工作流引擎<https://gitee.com/thinkgem/jeesite4-jflow> <http://ccflow.org>
* 内容管理模块CMS<https://gitee.com/thinkgem/jeesite4/tree/v5.3/modules/cms>
* 手机端移动端:<https://gitee.com/thinkgem/jeesite4-uniapp>
* JFlow工作流引擎<https://gitee.com/thinkgem/jeesite-jflow>
* 多站点内容管理模块CMS<https://jeesite.com/docs/cms/>
* 手机端移动端:<https://gitee.com/thinkgem/jeesite-uniapp>
* PC客户端程序<https://gitee.com/thinkgem/jeesite-client>
* Vue3分离版本<https://gitee.com/thinkgem/jeesite-vue>
* JeeSite统一认证<https://jeesite.com/docs/oauth2-server>
* MybatisPlus: <https://gitee.com/thinkgem/jeesite-mybatisplus>
* Magic接口快速开发<https://gitee.com/thinkgem/jeesite-magic-api>
* 内外网中间件:<https://my.oschina.net/thinkgem/blog/4624519>
## 快速体验
### 在线演示
1. 地址:<http://demo.jeesite.com/>
1. 全栈版地址:<http://vue.jeesite.com/js>
2. Vue3分离版地址<http://vue.jeesite.com>
### 本地运行
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/jeesite4/repository/archive/v5.3.zip> 并解压
2. 下载源码:<https://gitee.com/thinkgem/jeesite4/repository/archive/v5.7.zip> 并解压
3. 打开文件:`/web/src/main/resources/config/application.yml` 配置JDBC连接
4. 执行脚本:`/web/bin/init-data.bat` 初始化数据库
5. 执行脚本:`/web/bin/run-tomcat.bat` 启动服务即可
6. 浏览器访问:<http://127.0.0.1:8980/js/> 账号 system 密码 admin
6. 浏览器访问:<http://127.0.0.1:8980/js> 账号 system 密码 admin
7. 部署常见问题:<https://jeesite.com/docs/faq/>
8. 分离端安装:<https://jeesite.com/docs/vue-install-deploy/>
### 快速运行
1. 环境准备:`JDK 1.8 or 11、17``Maven 3.6+`、无需准备数据库(使用内嵌 H2 DB
2. 下载源码:<https://gitee.com/thinkgem/jeesite4/repository/archive/v5.3.zip> 并解压
1. 环境准备:`JDK 1.8 or 11、17``Maven 3.6+`、无需准备数据库(使用内嵌 H2 DB、Vue资源包
2. 下载源码:<https://gitee.com/thinkgem/jeesite4/repository/archive/v5.7.zip> 并解压
3. 执行脚本:`/web-fast/bin/run-tomcat.bat` 启动服务即可(自动初始化库)
4. 浏览器访问<http://127.0.0.1:8980/js/> 账号 system 密码 admin
5. 部署常见问题<https://jeesite.com/docs/faq/>
8. 分离端安装:<https://jeesite.com/docs/vue-install-deploy/>
4. 全栈版本地址<http://127.0.0.1:8980/a/login>
5. Vue分离版本地址<http://127.0.0.1:8980/vue/login>
6. 默认超级管理员账号system 密码 admin
7. 部署常见问题:<https://jeesite.com/docs/faq/>
### 容器运行
- 拉取 Docker 镜像演示使用JeeSite版本较久
```
```sh
docker pull thinkgem/jeesite-web
```
- 启动脚本:
```
docker run -d -p 8980:8980 --name jeesite-web -v /data:/data \
thinkgem/jeesite-web && docker logs -f jeesite-web
```sh
docker run --name jeesite-web -p 8980:8980 -d --restart unless-stopped \
-v ~/:/data thinkgem/jeesite-web && docker logs -f jeesite-web
```
- 浏览器访问:<http://127.0.0.1:8980/js/> 账号 system 密码 admin
- 分离端安装:<https://jeesite.com/docs/vue-install-deploy/>

View File

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

View File

@@ -391,6 +391,18 @@ public class ListUtils {
pageNo++;
}
}
/**
* 列表分页方法
* @param list 源数据
* @param pageNo 当前页码
* @param pageSize 每页显示条数
* @author ThinkGem
*/
public static <T> List<T> getPageList(List<T> list, int pageNo, int pageSize) {
int totalPage = (list.size() + pageSize - 1) / pageSize;
return getPageList(list, pageNo, pageSize, totalPage);
}
/**
* 列表分页方法

View File

@@ -779,12 +779,12 @@ public class FileUtils extends org.apache.commons.io.FileUtils {
String p = StringUtils.replace(path, WIN_SEPARATOR, SEPARATOR);
p = StringUtils.join(StringUtils.split(p, SEPARATOR), SEPARATOR);
if (!StringUtils.startsWithAny(p, SEPARATOR) && StringUtils.startsWithAny(path, WIN_SEPARATOR, SEPARATOR)){
p += SEPARATOR;
p = SEPARATOR + p;
}
if (!StringUtils.endsWithAny(p, SEPARATOR) && StringUtils.endsWithAny(path, WIN_SEPARATOR, SEPARATOR)){
p = p + SEPARATOR;
}
if (path != null && path.startsWith(SEPARATOR)){
if (path != null && path.startsWith(SEPARATOR) && !p.startsWith(SEPARATOR)){
p = SEPARATOR + p; // linux下路径
}
return p;

View File

@@ -42,7 +42,7 @@ public class PropertyLoader implements org.springframework.boot.env.PropertySour
try {
// 默认开启 FastJSON 1.x 的,安全模式
ParserConfig.getGlobalInstance().setSafeMode(true);
} catch (Exception ignored) {
} catch (Throwable ignored) {
// 兼容 FastJSON 2.x 的调用,忽略异常
}
Properties properties = PropertiesUtils.getInstance().getProperties();

View File

@@ -66,9 +66,9 @@ public class ByteUtils {
private static String format(double size, String type) {
int precision = 0;
if(size * 1000 % 10 > 0) {
/*if(size * 1000 % 10 > 0) {
precision = 3;
} else if(size * 100 % 10 > 0) {
} else */if(size * 100 % 10 > 0) {
precision = 2;
} else if(size * 10 % 10 > 0) {
precision = 1;

View File

@@ -5,11 +5,11 @@
*/
package com.jeesite.common.lang;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
import java.io.StringWriter;
import javax.servlet.http.HttpServletRequest;
/**
* 关于异常的工具类.
* @author calvin
@@ -26,8 +26,8 @@ public class ExceptionUtils {
Throwable ex = null;
if (request.getAttribute("exception") != null) {
ex = (Throwable) request.getAttribute("exception");
} else if (request.getAttribute("javax.servlet.error.exception") != null) {
ex = (Throwable) request.getAttribute("javax.servlet.error.exception");
} else if (request.getAttribute(RequestDispatcher.ERROR_EXCEPTION) != null) {
ex = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION);
}
return ex;
}

View File

@@ -14,7 +14,7 @@ import java.util.Date;
public class TimeUtils {
public static final String[] CN = new String[] {"毫秒", "", "", "", ""};
public static final String[] EN = new String[] {" millisecond ", " second ", " minute ", " hour ", " day "};
public static final String[] EN = new String[] {"ms", " second ", " minute ", " hour ", " day "};
public static final String[] AGO_CN = new String[] {"刚刚", "秒前", "分钟前", "小时前", "天前"};
public static final String[] AGO_EN = new String[] {"just now", " seconds ago", " minutes ago", " hours ago", " days ago"};

View File

@@ -7,13 +7,16 @@ package com.jeesite.common.mapper;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonParser.Feature;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.util.JSONPObject;
import com.jeesite.common.codec.EncodeUtils;
import com.jeesite.common.collect.ListUtils;
import com.jeesite.common.io.PropertiesUtils;
import com.jeesite.common.lang.DateUtils;
@@ -31,16 +34,15 @@ import java.util.Map;
import java.util.TimeZone;
/**
* 简单封装Jackson实现JSON String<->Java Object的Mapper.
* 封装不同的输出风格, 使用不同的builder函数创建实例.
* 封装 Jackson实现 JSON StringJava Object 互转
* @author ThinkGem
* @version 2016-3-2
* @version 2023-09-26
*/
public class JsonMapper extends ObjectMapper {
private static final long serialVersionUID = 1L;
private static Logger logger = LoggerFactory.getLogger(JsonMapper.class);
private static final Logger logger = LoggerFactory.getLogger(JsonMapper.class);
/**
* 当前类的实例持有者(静态内部类,延迟加载,懒汉式,线程安全的单例模式)
@@ -48,7 +50,7 @@ public class JsonMapper extends ObjectMapper {
private static final class JsonMapperHolder {
private static final JsonMapper INSTANCE = new JsonMapper();
}
public JsonMapper() {
// Spring ObjectMapper 初始化配置,支持 @JsonView
new Jackson2ObjectMapperBuilder().configure(this);
@@ -59,9 +61,30 @@ public class JsonMapper extends ObjectMapper {
// 允许不带引号的字段名称
this.configure(Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
// 设置默认时区
this.setDefaultTimeZone();
// 设置默认日期格式
this.setDefaultDateFormat();
// 遇到空值处理为空串
this.enabledNullValueToEmpty();
// 设置输入时忽略在JSON字符串中存在但Java对象实际没有的属性
this.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
}
/**
* 开启日期类型默认格式化
* @author ThinkGem
*/
public JsonMapper setDefaultTimeZone(){
this.setTimeZone(TimeZone.getTimeZone(PropertiesUtils.getInstance()
.getProperty("lang.defaultTimeZone", "GMT+08:00")));
// 设置默认日期格式
return this;
}
/**
* 开启日期类型默认格式化
* @author ThinkGem
*/
public JsonMapper setDefaultDateFormat(){
this.setDateFormat(new SimpleDateFormat(PropertiesUtils.getInstance()
.getProperty("web.json.defaultDateFormat", "yyyy-MM-dd HH:mm:ss")));
this.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {
@@ -74,8 +97,7 @@ public class JsonMapper extends ObjectMapper {
if (jf != null) {
return new JsonSerializer<Date>(){
@Override
public void serialize(Date value, JsonGenerator jgen,
SerializerProvider provider) throws IOException, JsonProcessingException {
public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
if (value != null){
jgen.writeString(DateUtils.formatDate(value, jf.pattern()));
}
@@ -86,27 +108,39 @@ public class JsonMapper extends ObjectMapper {
return super.findSerializer(a);
}
});
// 设置输入时忽略在JSON字符串中存在但Java对象实际没有的属性
this.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
// 遇到空值处理为空串
return this;
}
/**
* 开启将空值转换为空字符串
* @author ThinkGem
*/
public JsonMapper enabledNullValueToEmpty(){
this.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>(){
@Override
public void serialize(Object value, JsonGenerator jgen,
SerializerProvider provider) throws IOException, JsonProcessingException {
public void serialize(Object value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
jgen.writeString(StringUtils.EMPTY);
}
});
// // 进行HTML解码先注释掉否则会造成XSS攻击比如菜单名称里输入<script>alert(123)</script>转josn后就会还原这个编码 ,并在浏览器中运行)。
// this.registerModule(new SimpleModule().addSerializer(String.class, new JsonSerializer<String>(){
// @Override
// public void serialize(String value, JsonGenerator jgen,
// SerializerProvider provider) throws IOException,
// JsonProcessingException {
// if (value != null){
// jgen.writeString(StringEscapeUtils.unescapeHtml4(value));
// }
// }
// }));
return this;
}
/**
* 开启 XSS 过滤器
* @author ThinkGem
*/
public JsonMapper enabledXssFilter(){
this.registerModule(new SimpleModule().addDeserializer(String.class, new JsonDeserializer<String>() {
@Override
public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
String text = p.getText();
if (text != null) {
return EncodeUtils.xssFilter(text);
}
return null;
}
}));
return this;
}
/**

View File

@@ -93,7 +93,7 @@ public class IpUtils {
}
public static byte[] textToNumericFormatV4(String paramString) {
if (paramString.length() == 0) {
if (paramString.isEmpty()) {
return null;
}
byte[] arrayOfByte = new byte[4];

View File

@@ -1,5 +1,7 @@
package com.jeesite.common.utils;
import org.apache.commons.lang3.StringUtils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
@@ -7,8 +9,6 @@ import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
/**
* 身份证工具类
*
@@ -152,7 +152,7 @@ public class IdcardUtils extends StringUtils {
int iSum17 = getPowerSum(iCard);
// 获取校验位
String sVal = getCheckCode18(iSum17);
if (sVal.length() > 0) {
if (!sVal.isEmpty()) {
idCard18 += sVal;
} else {
return null;
@@ -205,7 +205,7 @@ public class IdcardUtils extends StringUtils {
int iSum17 = getPowerSum(iCard);
// 获取校验位
String val = getCheckCode18(iSum17);
if (val.length() > 0) {
if (!val.isEmpty()) {
if (val.equalsIgnoreCase(code18)) {
bTrue = true;
}

View File

@@ -187,6 +187,8 @@ a, a:hover, a:active, a:focus, .form-unit, th[aria-selected=true] .ui-jqgrid-sor
.open>.dropdown-toggle.btn-primary:hover, .layui-layer-btn .layui-layer-btn0,
.select2-container--default .select2-selection--multiple .select2-selection__choice,
.select2-container--default .select2-results__option--highlighted[aria-selected],
.pagination>.active>a, .pagination>.active>a:focus, .pagination>.active>a:hover,
.pagination>.active>span, .pagination>.active>span:focus, .pagination>.active>span:hover,
.wup_container .placeholder .webuploader-pick {background-color:#1890ff!important;border-color:#1890ff;}
.form-unit, th[aria-selected=true] .ui-jqgrid-sortable {color:#1890ff;}
.form-unit {border-bottom:1px solid #eee;}

View File

@@ -209,6 +209,8 @@ a, a:hover, a:active, a:focus, .form-unit, th[aria-selected=true] .ui-jqgrid-sor
.open>.dropdown-toggle.btn-primary:hover, .layui-layer-btn .layui-layer-btn0,
.select2-container--default .select2-selection--multiple .select2-selection__choice,
.select2-container--default .select2-results__option--highlighted[aria-selected],
.pagination>.active>a, .pagination>.active>a:focus, .pagination>.active>a:hover,
.pagination>.active>span, .pagination>.active>span:focus, .pagination>.active>span:hover,
.wup_container .placeholder .webuploader-pick {background-color:#1890ff!important;border-color:#1890ff!important;}
.form-unit, th[aria-selected=true] .ui-jqgrid-sortable {color:#1890ff;}
.form-unit {border-bottom:1px solid #eee;}

View File

@@ -187,6 +187,8 @@ a, a:hover, a:active, a:focus, .form-unit, th[aria-selected=true] .ui-jqgrid-sor
.open>.dropdown-toggle.btn-primary:hover, .layui-layer-btn .layui-layer-btn0,
.select2-container--default .select2-selection--multiple .select2-selection__choice,
.select2-container--default .select2-results__option--highlighted[aria-selected],
.pagination>.active>a, .pagination>.active>a:focus, .pagination>.active>a:hover,
.pagination>.active>span, .pagination>.active>span:focus, .pagination>.active>span:hover,
.wup_container .placeholder .webuploader-pick {background-color:#1e5edb!important;border-color:#1e5edb!important;}
.form-unit, th[aria-selected=true] .ui-jqgrid-sortable {color:#1e5edb;}
.form-unit {border-bottom:1px solid #eee;}

View File

@@ -209,6 +209,8 @@ a, a:hover, a:active, a:focus, .form-unit, th[aria-selected=true] .ui-jqgrid-sor
.open>.dropdown-toggle.btn-primary:hover, .layui-layer-btn .layui-layer-btn0,
.select2-container--default .select2-selection--multiple .select2-selection__choice,
.select2-container--default .select2-results__option--highlighted[aria-selected],
.pagination>.active>a, .pagination>.active>a:focus, .pagination>.active>a:hover,
.pagination>.active>span, .pagination>.active>span:focus, .pagination>.active>span:hover,
.wup_container .placeholder .webuploader-pick {background-color:#1e5edb!important;border-color:#1e5edb!important;}
.form-unit, th[aria-selected=true] .ui-jqgrid-sortable {color:#1e5edb;}
.form-unit {border-bottom:1px solid #eee;}

View File

@@ -222,7 +222,9 @@ a, a:hover, a:active, a:focus, .form-unit, th[aria-selected=true] .ui-jqgrid-sor
.open>.dropdown-toggle.btn-primary:hover, .layui-layer-btn .layui-layer-btn0,
.select2-container--default .select2-selection--multiple .select2-selection__choice,
.select2-container--default .select2-results__option--highlighted[aria-selected],
.wup_container .placeholder .webuploader-pick {background-color:#1e5edb!important;border-color:#1e5edb!important;}
.pagination>.active>a, .pagination>.active>a:focus, .pagination>.active>a:hover,
.pagination>.active>span, .pagination>.active>span:focus, .pagination>.active>span:hover,
.wup_container .placeholder .webuploader-pick {background-color:#3aa0ff!important;border-color:#3aa0ff!important;}
.form-unit, th[aria-selected=true] .ui-jqgrid-sortable {color:#2975bc;}
.form-unit {border-bottom:1px solid #4e4e4e;}

View File

@@ -156,4 +156,6 @@ th[aria-selected=true] .ui-jqgrid-sortable {color:#00a65a;}
.open>.dropdown-toggle.btn-primary:hover, .layui-layer-btn .layui-layer-btn0,
.select2-container--default .select2-selection--multiple .select2-selection__choice,
.select2-container--default .select2-results__option--highlighted[aria-selected],
.pagination>.active>a, .pagination>.active>a:focus, .pagination>.active>a:hover,
.pagination>.active>span, .pagination>.active>span:focus, .pagination>.active>span:hover,
.wup_container .placeholder .webuploader-pick {background-color:#00a65a!important;border-color:#00a65a;}

View File

@@ -141,4 +141,6 @@ th[aria-selected=true] .ui-jqgrid-sortable {color:#00a65a;}
.open>.dropdown-toggle.btn-primary:hover, .layui-layer-btn .layui-layer-btn0,
.select2-container--default .select2-selection--multiple .select2-selection__choice,
.select2-container--default .select2-results__option--highlighted[aria-selected],
.pagination>.active>a, .pagination>.active>a:focus, .pagination>.active>a:hover,
.pagination>.active>span, .pagination>.active>span:focus, .pagination>.active>span:hover,
.wup_container .placeholder .webuploader-pick {background-color:#00a65a!important;border-color:#00a65a;}

View File

@@ -156,4 +156,6 @@ th[aria-selected=true] .ui-jqgrid-sortable {color:#605ca8;}
.open>.dropdown-toggle.btn-primary:hover, .layui-layer-btn .layui-layer-btn0,
.select2-container--default .select2-selection--multiple .select2-selection__choice,
.select2-container--default .select2-results__option--highlighted[aria-selected],
.pagination>.active>a, .pagination>.active>a:focus, .pagination>.active>a:hover,
.pagination>.active>span, .pagination>.active>span:focus, .pagination>.active>span:hover,
.wup_container .placeholder .webuploader-pick {background-color:#605ca8!important;border-color:#605ca8;}

View File

@@ -141,4 +141,6 @@ th[aria-selected=true] .ui-jqgrid-sortable {color:#605ca8;}
.open>.dropdown-toggle.btn-primary:hover, .layui-layer-btn .layui-layer-btn0,
.select2-container--default .select2-selection--multiple .select2-selection__choice,
.select2-container--default .select2-results__option--highlighted[aria-selected],
.pagination>.active>a, .pagination>.active>a:focus, .pagination>.active>a:hover,
.pagination>.active>span, .pagination>.active>span:focus, .pagination>.active>span:hover,
.wup_container .placeholder .webuploader-pick {background-color:#605ca8!important;border-color:#605ca8;}

View File

@@ -156,4 +156,6 @@ th[aria-selected=true] .ui-jqgrid-sortable {color:#dd4b39;}
.open>.dropdown-toggle.btn-primary:hover, .layui-layer-btn .layui-layer-btn0,
.select2-container--default .select2-selection--multiple .select2-selection__choice,
.select2-container--default .select2-results__option--highlighted[aria-selected],
.pagination>.active>a, .pagination>.active>a:focus, .pagination>.active>a:hover,
.pagination>.active>span, .pagination>.active>span:focus, .pagination>.active>span:hover,
.wup_container .placeholder .webuploader-pick {background-color:#dd4b39!important;border-color:#dd4b39;}

View File

@@ -141,4 +141,6 @@ th[aria-selected=true] .ui-jqgrid-sortable {color:#dd4b39;}
.open>.dropdown-toggle.btn-primary:hover, .layui-layer-btn .layui-layer-btn0,
.select2-container--default .select2-selection--multiple .select2-selection__choice,
.select2-container--default .select2-results__option--highlighted[aria-selected],
.pagination>.active>a, .pagination>.active>a:focus, .pagination>.active>a:hover,
.pagination>.active>span, .pagination>.active>span:focus, .pagination>.active>span:hover,
.wup_container .placeholder .webuploader-pick {background-color:#dd4b39!important;border-color:#dd4b39;}

View File

@@ -156,4 +156,6 @@ th[aria-selected=true] .ui-jqgrid-sortable {color:#f39c12;}
.open>.dropdown-toggle.btn-primary:hover, .layui-layer-btn .layui-layer-btn0,
.select2-container--default .select2-selection--multiple .select2-selection__choice,
.select2-container--default .select2-results__option--highlighted[aria-selected],
.pagination>.active>a, .pagination>.active>a:focus, .pagination>.active>a:hover,
.pagination>.active>span, .pagination>.active>span:focus, .pagination>.active>span:hover,
.wup_container .placeholder .webuploader-pick {background-color:#f39c12!important;border-color:#f39c12;}

View File

@@ -141,4 +141,6 @@ th[aria-selected=true] .ui-jqgrid-sortable {color:#f39c12;}
.open>.dropdown-toggle.btn-primary:hover, .layui-layer-btn .layui-layer-btn0,
.select2-container--default .select2-selection--multiple .select2-selection__choice,
.select2-container--default .select2-results__option--highlighted[aria-selected],
.pagination>.active>a, .pagination>.active>a:focus, .pagination>.active>a:hover,
.pagination>.active>span, .pagination>.active>span:focus, .pagination>.active>span:hover,
.wup_container .placeholder .webuploader-pick {background-color:#f39c12!important;border-color:#f39c12;}

View File

@@ -710,6 +710,7 @@ Class.pt.callback = function(){
var that = this, layero = that.layero, config = that.config;
that.openLayer();
if(config.success){
layero.find('.layui-layer-close').focus(); // 弹窗后首先关闭获取焦点,防止回车弹出多个
if(config.type == 2){
layero.find('iframe').on('load', function(){
config.success(layero, that.index);

View File

@@ -4469,7 +4469,7 @@ S2.define('select2/dropdown/closeOnSelect',[
});
// ThinkGem 默认为中文
S2.define('select2/i18n/en',[],function () {
S2.define('select2/i18n/zh_CN',[],function () {
// English
return {
errorLoading: function () {
@@ -4549,7 +4549,7 @@ S2.define('select2/defaults',[
'./dropdown/selectOnClose',
'./dropdown/closeOnSelect',
'./i18n/en'
'./i18n/zh_CN'
], function ($, require,
ResultsList,
@@ -4763,7 +4763,7 @@ S2.define('select2/defaults',[
if ($.isArray(options.language)) {
var languages = new Translation();
options.language.push('en');
options.language.push('zh_CN');
var languageNames = options.language;
@@ -4800,7 +4800,7 @@ S2.define('select2/defaults',[
options.translations = languages;
} else {
var baseTranslation = Translation.loadPath(
this.defaults.amdLanguageBase + 'en'
this.defaults.amdLanguageBase + 'zh_CN'
);
var customTranslation = new Translation(options.language);
@@ -4952,6 +4952,8 @@ S2.define('select2/options',[
this.options.language = $e.prop('lang').toLowerCase();
} else if ($e.closest('[lang]').prop('lang')) {
this.options.language = $e.closest('[lang]').prop('lang');
} else if (window.lang) {
this.options.language = window.lang;
}
}

File diff suppressed because it is too large Load Diff

BIN
modules/app/db/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

View File

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

View File

@@ -10,7 +10,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import com.jeesite.common.callback.MethodCallback;
import com.jeesite.common.config.Global;
import com.jeesite.common.tests.BaseInitDataTests;
import com.jeesite.modules.app.entity.AppComment;

View File

@@ -11,4 +11,9 @@
5.3.2
5.4.0
5.4.1
5.5.0
5.5.0
5.5.1
5.5.2
5.6.0
5.6.1
5.7.0

View File

@@ -18,20 +18,20 @@
data-page-no="${parameter.pageNo}" data-page-size="${parameter.pageSize}" data-order-by="${parameter.orderBy}">
<div class="form-group">
<label class="control-label">${text('问题分类')}</label>
<div class="control-inline width-120">
<div class="control-inline width-90">
<#form:select path="category" dictType="app_comment_category" blankOption="true" class="form-control"/>
</div>
</div>
<div class="form-group">
<label class="control-label">${text('问题和意见')}</label>
<div class="control-inline">
<#form:input path="content" maxlength="500" class="form-control width-120"/>
<#form:input path="content" maxlength="500" class="form-control width-90"/>
</div>
</div>
<div class="form-group">
<label class="control-label">${text('联系方式')}</label>
<div class="control-inline">
<#form:input path="contact" maxlength="200" class="form-control width-120"/>
<#form:input path="contact" maxlength="200" class="form-control width-90"/>
</div>
</div>
<div class="form-group">
@@ -49,8 +49,8 @@
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-sm">${text('查询')}</button>
<button type="reset" class="btn btn-default btn-sm isQuick">${text('重置')}</button>
<button type="submit" class="btn btn-primary btn-sm"><i class="glyphicon glyphicon-search"></i> ${text('查询')}</button>
<button type="reset" class="btn btn-default btn-sm isQuick"><i class="glyphicon glyphicon-repeat"></i> ${text('重置')}</button>
</div>
</#form:form>
<table id="dataGrid"></table>
@@ -62,7 +62,7 @@
<script>
//# // 初始化DataGrid对象
$('#dataGrid').dataGrid({
searchForm: $("#searchForm"),
searchForm: $('#searchForm'),
columnModel: [
{header:'${text("问题和意见")}', name:'content', index:'a.content', width:250, align:"left", frozen:true, formatter: function(val, obj, row, act){
return '<a href="${ctx}/app/appComment/form?id='+row.id+'" class="btnList" data-title="${text("编辑意见")}">'+(val||row.id)+'</a>';

View File

@@ -18,19 +18,19 @@
data-page-no="${parameter.pageNo}" data-page-size="${parameter.pageSize}" data-order-by="${parameter.orderBy}">
<div class="form-group">
<label class="control-label">${text('应用代号')}</label>
<div class="control-inline width-120">
<div class="control-inline width-90">
<#form:select path="appCode" dictType="app_code" blankOption="true" class="form-control"/>
</div>
</div>
<div class="form-group">
<label class="control-label">${text('升级标题')}</label>
<div class="control-inline">
<#form:input path="upTitle" maxlength="200" class="form-control width-120"/>
<#form:input path="upTitle" maxlength="200" class="form-control width-90"/>
</div>
</div>
<div class="form-group">
<label class="control-label">${text('升级类型')}</label>
<div class="control-inline width-120">
<div class="control-inline width-90">
<#form:select path="upType" dictType="app_upgrade_type" blankOption="true" class="form-control"/>
</div>
</div>
@@ -48,8 +48,8 @@
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-sm">${text('查询')}</button>
<button type="reset" class="btn btn-default btn-sm isQuick">${text('重置')}</button>
<button type="submit" class="btn btn-primary btn-sm"><i class="glyphicon glyphicon-search"></i> ${text('查询')}</button>
<button type="reset" class="btn btn-default btn-sm isQuick"><i class="glyphicon glyphicon-repeat"></i> ${text('重置')}</button>
</div>
</#form:form>
<table id="dataGrid"></table>
@@ -61,7 +61,7 @@
<script>
//# // 初始化DataGrid对象
$('#dataGrid').dataGrid({
searchForm: $("#searchForm"),
searchForm: $('#searchForm'),
columnModel: [
{header:'${text("升级标题")}', name:'upTitle', index:'a.up_title', width:350, align:"left", frozen:true, formatter: function(val, obj, row, act){
return '<a href="${ctx}/app/appUpgrade/form?id='+row.id+'" class="btnList" data-title="${text("编辑版本")}">'+(val||row.id)+'</a>';

File diff suppressed because it is too large Load Diff

BIN
modules/cms/db/cms.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 MiB

View File

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

View File

@@ -18,18 +18,7 @@ j2cache:
cmsPageCache: 100000, 7d
#spring:
# data:
# elasticsearch:
# # 开启 ES 功能
# enabled: true
#
# # 设置 ES 服务地址
# client:
# reactive:
# endpoints: 127.0.0.1:9200
#
# # 连接超时的时间
# properties:
# transport:
# tcp:
# connect_timeout: 120s
# elasticsearch:
# enabled: true
# uris: http://Win11:9200
# connection-timeout: 120s

View File

@@ -19,4 +19,9 @@
5.3.2
5.4.0
5.4.1
5.5.0
5.5.0
5.5.1
5.5.2
5.6.0
5.6.1
5.7.0

View File

@@ -2,8 +2,8 @@
// Bootswatch
// -----------------------------------------------------
$web-font-path: "https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,700" !default;
@import url($web-font-path);
//$web-font-path: "https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,700" !default;
//@import url($web-font-path);
// Navbar =====================================================================

View File

@@ -1,4 +1,4 @@
@import url("https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,700");
/*@import url("https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,700");*/
/*!
* bootswatch v3.4.1
* Homepage: http://bootswatch.com

View File

@@ -1,4 +1,5 @@
@import url("https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,700");/*!
/*@import url("https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,700");*/
/*!
* bootswatch v3.4.1
* Homepage: http://bootswatch.com
* Copyright 2012-2019 Thomas Park

View File

@@ -2,12 +2,12 @@
// Bootswatch
// -----------------------------------------------------
@web-font-path: "https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,700";
.web-font(@path) {
@import (css) url("@{path}");
}
.web-font(@web-font-path);
//@web-font-path: "https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,700";
//
//.web-font(@path) {
// @import (css) url("@{path}");
//}
//.web-font(@web-font-path);
// Navbar =====================================================================

View File

@@ -21,7 +21,7 @@
<label class="control-label col-sm-4" title="">
<span class="required ">*</span> ${text('所属栏目')}<i class="fa icon-question hide"></i></label>
<div class="col-sm-8">
<#form:treeselect id="category" title="${text('所属栏目')}"
<#form:treeselect id="category" title="${text('所属栏目')}"
path="category.categoryCode" labelPath="category.categoryName"
url="${ctx}/cms/category/treeData?excludeCode=${article.category.categoryCode}"
class="required" allowClear="false" canSelectRoot="true" canSelectParent="false" />
@@ -70,145 +70,145 @@
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title="${text('数值越大排序越靠前,可设置权重过期时间')}。">
${text('权重/排序')} <i class="fa icon-question"></i></label>
<div class="col-sm-8">
<div class="form-inline m0">
<#form:input path="weight" class="form-control width-90 digits" maxlength="10"/> &nbsp;
<#form:checkbox id="weightTop" label="${text('置顶')}" value="${article.weight==9999 ?'1' : ''}"
class="form-control" style="vertical-align:middle;"/>
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title="${text('数值越大排序越靠前,可设置权重过期时间')}。">
${text('权重/排序')} <i class="fa icon-question"></i></label>
<div class="col-sm-8">
<div class="form-inline m0">
<#form:input path="weight" class="form-control width-90 digits" maxlength="10"/> &nbsp;
<#form:checkbox id="weightTop" label="${text('置顶')}" value="${article.weight==9999 ?'1' : ''}"
class="form-control" style="vertical-align:middle;"/>
</div>
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title="${text('时间到期后权重自动恢复为0如果为空则权重永不过期')}。">
${text('权重过期时间')} <i class="fa icon-question"></i></label>
<div class="col-sm-8">
<#form:input path="weightDate" readonly="true" maxlength="20" class="form-control laydate"
dataFormat="datetime" data-type="datetime" data-format="yyyy-MM-dd HH:mm"/>
</div>
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title="${text('时间到期后权重自动恢复为0如果为空则权重永不过期')}。">
${text('权重过期时间')} <i class="fa icon-question"></i></label>
<div class="col-sm-8">
<#form:input path="weightDate" readonly="true" maxlength="20" class="form-control laydate"
dataFormat="datetime" data-type="datetime" data-format="yyyy-MM-dd HH:mm"/>
</div>
</div>
</div>
</div>
<div class="form-unit">${text('详细信息')}</div>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label class="control-label col-sm-1">${text('摘要')}</label>
<div class="col-sm-11">
<#form:textarea path="description" maxlength="500" class="form-control"/>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label class="control-label col-sm-1">${text('正文')}</label>
<div class="col-sm-11">
<#form:ueditor id="content" path="articleData.content" maxlength="10000" height="500" class="required" outline="${parameter.outline}"/>
</div>
</div>
</div>
</div>
<div class="form-unit">${text('其他信息')}</div>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label class="control-label col-sm-2" title="">
<span class="required hide">*</span> ${text('内容图片')}<i class="fa icon-question hide"></i></label>
<div class="col-sm-10">
<#form:fileupload id="uploadImage" bizKey="${article.id}"
bizType="article_image" returnPath="true"
filePathInputId="image" uploadType="image" readonly="false"
maxUploadNum="4" isMini="false" />
<#form:input path="image" maxlength="1000" readonly="true" class="form-control" />
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title="">
<span class="required hide">*</span> ${text('关键字')}<i class="fa icon-question hide"></i></label>
<div class="col-sm-8">
<#form:input path="keywords" maxlength="500" class="form-control"/>
</div>
</div>
</div>
<!--<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4">${text('推荐位')}</label>
<div class="col-sm-8">
<div class="checkbox-list">
<#form:checkbox path="posidList" dictType="cms_post" class="form-control" />
<div class="form-unit">${text('详细信息')}</div>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label class="control-label col-sm-1">${text('摘要')}</label>
<div class="col-sm-11">
<#form:textarea path="description" maxlength="500" class="form-control"/>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title="${text('文章的发布状态')}">${text('状态')}<i class="fa icon-question"></i></label>
<div class="col-sm-8">
<#form:select path="state" dictType="sys_status" blankOption="true" class="form-control" />
</div>
</div>
</div>-->
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title="${text('可修改发布时间,不填则使用当前时间')}">
${text('发布时间')} <i class="fa icon-question"></i></label>
<div class="col-sm-8">
<#form:input path="createDate" readonly="true" maxlength="20" class="form-control laydate"
dataFormat="datetime" data-type="datetime" data-format="yyyy-MM-dd HH:mm"/>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label class="control-label col-sm-1">${text('正文')}</label>
<div class="col-sm-11">
<#form:ueditor id="content" path="articleData.content" maxlength="10000" height="500" class="required" outline="${parameter.outline}"/>
</div>
</div>
</div>
</div>
</div>
<div class="form-unit">${text('高级信息')}</div>
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title="">
<span class="required hide">*</span> ${text('自定义内容视图')}<i class="fa icon-question" title="自定义内容视图名称必须以'${article_DEFAULT_TEMPLATE}'开始"></i></label>
<div class="col-sm-8">
<#form:select path="customContentView" items="${contentViewList}" itemLabel="id" itemValue="id" blankOption="true" class="form-control " />
<div class="form-unit">${text('其他信息')}</div>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label class="control-label col-sm-2" title="">
<span class="required hide">*</span> ${text('内容图片')}<i class="fa icon-question hide"></i></label>
<div class="col-sm-10">
<#form:fileupload id="uploadImage" bizKey="${article.id}"
bizType="article_image" returnPath="true"
filePathInputId="image" uploadType="image" readonly="false"
maxUploadNum="4" isMini="false" />
<#form:input path="image" maxlength="1000" readonly="true" class="form-control" />
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label class="control-label col-sm-2" title="">
<span class="required hide">*</span> ${text('视图参数配置')}<i class="fa icon-question" title="视图参数例如: {count:2, title_show:'yes'} 则在视图文件中的获取方法是:\${viewConfig_count}、\${viewConfig_titleShow}"></i></label>
<div class="col-sm-10">
<#form:input path="viewConfig" maxlength="1000" placeholder="视图参数例如: {count:2, title_show:'yes'} 则在视图文件中的获取方法是:${'${'}viewConfig_count}、${'${'}viewConfig_titleShow}" class="form-control"/>
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title="">
<span class="required hide">*</span> ${text('关键字')}<i class="fa icon-question hide"></i></label>
<div class="col-sm-8">
<#form:input path="keywords" maxlength="500" class="form-control"/>
</div>
</div>
</div>
<!--<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4">${text('推荐位')}</label>
<div class="col-sm-8">
<div class="checkbox-list">
<#form:checkbox path="posidList" dictType="cms_post" class="form-control" />
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label class="control-label col-sm-2" title="">
<span class="required hide">*</span> ${text('备注信息')}<i class="fa icon-question hide"></i></label>
<div class="col-sm-10">
<#form:textarea path="remarks" rows="4" maxlength="500" class="form-control"/>
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title="${text('文章的发布状态')}">${text('状态')}<i class="fa icon-question"></i></label>
<div class="col-sm-8">
<#form:select path="state" dictType="sys_status" blankOption="true" class="form-control" />
</div>
</div>
</div>-->
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title="${text('可修改发布时间,不填则使用当前时间')}">
${text('发布时间')} <i class="fa icon-question"></i></label>
<div class="col-sm-8">
<#form:input path="createDate" readonly="true" maxlength="20" class="form-control laydate"
dataFormat="datetime" data-type="datetime" data-format="yyyy-MM-dd HH:mm"/>
</div>
</div>
</div>
</div>
<div class="form-unit">${text('页面配置')}</div>
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title="">
<span class="required hide">*</span> ${text('自定义内容视图')}<i class="fa icon-question" title="自定义内容视图名称必须以'${article_DEFAULT_TEMPLATE}'开始"></i></label>
<div class="col-sm-8">
<#form:select path="customContentView" items="${contentViewList}" itemLabel="id" itemValue="id" blankOption="true" class="form-control " />
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label class="control-label col-sm-2" title="">
<span class="required hide">*</span> ${text('视图参数配置')}<i class="fa icon-question" title="视图参数例如: {count:2, title_show:'yes'} 则在视图文件中的获取方法是:\${viewConfig_count}、\${viewConfig_titleShow}"></i></label>
<div class="col-sm-10">
<#form:input path="viewConfig" maxlength="1000" placeholder="视图参数例如: {count:2, title_show:'yes'} 则在视图文件中的获取方法是:${'${'}viewConfig_count}、${'${'}viewConfig_titleShow}" class="form-control"/>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label class="control-label col-sm-2" title="">
<span class="required hide">*</span> ${text('备注信息')}<i class="fa icon-question hide"></i></label>
<div class="col-sm-10">
<#form:textarea path="remarks" rows="4" maxlength="500" class="form-control"/>
</div>
</div>
</div>
</div>
<#form:extend collapsed="true" pathPrefix="articleData"/>
</div>
<#form:extend collapsed="true" pathPrefix="articleData"/>
<div class="box-footer">
<div class="row">
<div class="col-sm-offset-2 col-sm-10">
@@ -226,7 +226,7 @@
<script src="${ctxStatic}/colorpicker/bootstrap-colorpicker.js"></script>
<script type="text/javascript">
// 颜色控件初始化
$("#inputForm .input-color").colorpicker();
$('#inputForm .input-color').colorpicker();
// 权重、排序
$('#weightTop input').on('ifChecked ifUnchecked', function(){
if ($(this).is(':checked')){
@@ -235,7 +235,7 @@ $('#weightTop input').on('ifChecked ifUnchecked', function(){
$('#weight').val('0');
}
});
$("#inputForm").validate({
$('#inputForm').validate({
submitHandler: function(form){
$('#wordCount').val(contentUE.getContentTxt().length);
js.ajaxSubmitForm($(form), function(data){

View File

@@ -28,12 +28,12 @@
<#form:input path="title" maxlength="255" class="form-control width-120"/>
</div>
</div>
<div class="form-group">
<!--<div class="form-group">
<label class="control-label">${text('关键字')}</label>
<div class="control-inline">
<#form:input path="keywords" maxlength="500" class="form-control width-120"/>
</div>
</div>
</div>-->
<div class="form-group">
<label class="control-label">${text('状态')}</label>
<div class="control-inline width-90">
@@ -41,8 +41,8 @@
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-sm">${text('查询')}</button>
<button type="reset" class="btn btn-default btn-sm isQuick">${text('重置')}</button>
<button type="submit" class="btn btn-primary btn-sm"><i class="glyphicon glyphicon-search"></i> ${text('查询')}</button>
<button type="reset" class="btn btn-default btn-sm isQuick"><i class="glyphicon glyphicon-repeat"></i> ${text('重置')}</button>
</div>
</#form:form>
<table id="dataGrid"></table>

View File

@@ -11,6 +11,7 @@
</div>
<#form:form id="inputForm" model="${category}" action="${ctx}/cms/category/save" method="post" class="form-horizontal">
<div class="box-body">
<div class="form-unit">${text('基本信息')}</div>
<div class="row">
<div class="col-xs-6">
<div class="form-group">
@@ -131,112 +132,112 @@
</div>
</div>
</div>
<div class="form-unit">${text('栏目配置')}</div>
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title="是否在导航中显示该栏目">
<span class="required hide">*</span> ${text('是否在导航中显示')}<i class="fa icon-question"></i></label>
<div class="col-sm-8">
<#form:radio path="inMenu" dictType="sys_show_hide" class="form-control" />
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title="">
<span class="required hide">*</span> ${text('是否允许评论')}<i class="fa icon-question hide"></i></label>
<div class="col-sm-8">
<#form:radio path="isCanComment" dictType="sys_yes_no" class="form-control" />
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title="是否在分类页中显示该栏目的文章列表">
<span class="required hide">*</span> ${text('是否在分类页中显示')}<i class="fa icon-question" ></i></label>
<div class="col-sm-8">
<#form:radio path="inList" dictType="sys_show_hide" class="form-control" />
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title=""> <span class="required hide">*</span>
${text('是否需要审核')}<i class="fa icon-question hide"></i></label>
<div class="col-sm-8">
<#form:radio path="isNeedAudit" dictType="sys_yes_no" class="form-control" />
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title="默认展现方式,首栏目内容列表,栏目第一条内容">
<span class="required hide">*</span> ${text('内容展现模式')}<i class="fa icon-question" ></i></label>
<div class="col-sm-8">
<#form:radio path="showModes" dictType="cms_show_modes" class="form-control" />
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title="自定义内容视图名称必须以'${category_DEFAULT_TEMPLATE}'开始">
${text('自定义列表视图')}<i class="fa icon-question "></i></label>
<div class="col-sm-8">
<#form:select path="customListView" items="${listViewList}" itemLabel="id" itemValue="id" blankOption="true" class="form-control " />
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title="自定义内容视图名称必须以'${article_DEFAULT_TEMPLATE}'开始"> <span class="required hide">*</span>
${text('自定义内容视图')}<i class="fa icon-question "></i></label>
<div class="col-sm-8">
<#form:select path="customContentView" items="${contentViewList}" itemLabel="id" itemValue="id" blankOption="true" class="form-control " />
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label class="control-label col-sm-2" title="视图参数例如: {count:2, title_show:'yes'} 则在视图文件中的获取方法是:\${viewConfig_count}、\${viewConfig_titleShow}">
${text('视图参数配置')}<i class="fa icon-question"></i></label>
<div class="col-sm-10">
<#form:input path="viewConfig" maxlength="1000" class="form-control"
placeholder="视图参数例如: {count:2, title_show:'yes'} 则在视图文件中的获取方法是:${'${'}viewConfig_count}、${'${'}viewConfig_titleShow}" />
<br />
<ul class="text-muted well well-lg no-shadow m0 pt10 pb10">
<li>例如视图参数设置为:{count:2,titleShow:'yes'} 则在视图文件中的获取方法是:\${viewConfig_count}、\${viewConfig_titleShow}。</li>
<li>设置栏目的管理地址若设置【adminUrl:false】表示无管理地址在内容发布栏目列表中不显示该栏目</li>
<li>设置【adminUrl:'/cms/guestbook'】表示有管理地址,在内容发布栏目列表中点击该栏目链接到该地址。</li>
<!-- <li>管理地址参数若设置【adminUrlParam:'fileDownload=true'】则代表链接模型为文件下载的栏目,新增链接的时候出现文件上传对话框。</li>
<li>管理地址参数若设置【adminUrlParam:'outlineView=true'】则代表文章模型开启大纲视图编辑,在线编辑器左侧显示大纲视图。</li> -->
</ul>
</div>
</div>
</div>
</div>
<div class="form-unit">${text('其他信息')}</div>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label class="control-label col-sm-2" title="">
<span class="required hide">*</span> ${text('备注信息')}<i class="fa icon-question hide"></i></label>
<div class="col-sm-10">
<#form:textarea path="remarks" rows="4" maxlength="500" class="form-control" />
</div>
</div>
</div>
</div>
<#form:extend collapsed="true" />
</div>
<div class="form-unit">${text('栏目配置')}</div>
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title="是否在导航中显示该栏目">
<span class="required hide">*</span> ${text('是否在导航中显示')}<i class="fa icon-question"></i></label>
<div class="col-sm-8">
<#form:radio path="inMenu" dictType="sys_show_hide" class="form-control" />
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title="">
<span class="required hide">*</span> ${text('是否允许评论')}<i class="fa icon-question hide"></i></label>
<div class="col-sm-8">
<#form:radio path="isCanComment" dictType="sys_yes_no" class="form-control" />
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title="是否在分类页中显示该栏目的文章列表">
<span class="required hide">*</span> ${text('是否在分类页中显示')}<i class="fa icon-question" ></i></label>
<div class="col-sm-8">
<#form:radio path="inList" dictType="sys_show_hide" class="form-control" />
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title=""> <span class="required hide">*</span>
${text('是否需要审核')}<i class="fa icon-question hide"></i></label>
<div class="col-sm-8">
<#form:radio path="isNeedAudit" dictType="sys_yes_no" class="form-control" />
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title="默认展现方式,首栏目内容列表,栏目第一条内容">
<span class="required hide">*</span> ${text('内容展现模式')}<i class="fa icon-question" ></i></label>
<div class="col-sm-8">
<#form:radio path="showModes" dictType="cms_show_modes" class="form-control" />
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title="自定义内容视图名称必须以'${category_DEFAULT_TEMPLATE}'开始">
${text('自定义列表视图')}<i class="fa icon-question "></i></label>
<div class="col-sm-8">
<#form:select path="customListView" items="${listViewList}" itemLabel="id" itemValue="id" blankOption="true" class="form-control " />
</div>
</div>
</div>
<div class="col-xs-6">
<div class="form-group">
<label class="control-label col-sm-4" title="自定义内容视图名称必须以'${article_DEFAULT_TEMPLATE}'开始"> <span class="required hide">*</span>
${text('自定义内容视图')}<i class="fa icon-question "></i></label>
<div class="col-sm-8">
<#form:select path="customContentView" items="${contentViewList}" itemLabel="id" itemValue="id" blankOption="true" class="form-control " />
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label class="control-label col-sm-2" title="视图参数例如: {count:2, title_show:'yes'} 则在视图文件中的获取方法是:\${viewConfig_count}、\${viewConfig_titleShow}">
${text('视图参数配置')}<i class="fa icon-question"></i></label>
<div class="col-sm-10">
<#form:input path="viewConfig" maxlength="1000" class="form-control"
placeholder="视图参数例如: {count:2, title_show:'yes'} 则在视图文件中的获取方法是:${'${'}viewConfig_count}、${'${'}viewConfig_titleShow}" />
<br />
<ul class="text-muted well well-lg no-shadow m0 pt10 pb10">
<li>例如视图参数设置为:{count:2,titleShow:'yes'} 则在视图文件中的获取方法是:\${viewConfig_count}、\${viewConfig_titleShow}。</li>
<li>设置栏目的管理地址若设置【adminUrl:false】表示无管理地址在内容发布栏目列表中不显示该栏目</li>
<li>设置【adminUrl:'/cms/guestbook'】表示有管理地址,在内容发布栏目列表中点击该栏目链接到该地址。</li>
<!-- <li>管理地址参数若设置【adminUrlParam:'fileDownload=true'】则代表链接模型为文件下载的栏目,新增链接的时候出现文件上传对话框。</li>
<li>管理地址参数若设置【adminUrlParam:'outlineView=true'】则代表文章模型开启大纲视图编辑,在线编辑器左侧显示大纲视图。</li> -->
</ul>
</div>
</div>
</div>
</div>
<div class="form-unit">${text('其他信息')}</div>
<div class="row">
<div class="col-xs-12">
<div class="form-group">
<label class="control-label col-sm-2" title="">
<span class="required hide">*</span> ${text('备注信息')}<i class="fa icon-question hide"></i></label>
<div class="col-sm-10">
<#form:textarea path="remarks" rows="4" maxlength="500" class="form-control" />
</div>
</div>
</div>
</div>
<#form:extend collapsed="true" />
<div class="box-footer">
<div class="row">
<div class="col-sm-offset-2 col-sm-10">
@@ -252,7 +253,7 @@
</div>
<% } %>
<script>
$("#inputForm").validate({
$('#inputForm').validate({
submitHandler : function(form) {
js.ajaxSubmitForm($(form), function(data) {
js.showMessage(data.message);

View File

@@ -57,8 +57,8 @@
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-sm">${text('查询')}</button>
<button type="reset" class="btn btn-default btn-sm isQuick">${text('重置')}</button>
<button type="submit" class="btn btn-primary btn-sm"><i class="glyphicon glyphicon-search"></i> ${text('查询')}</button>
<button type="reset" class="btn btn-default btn-sm isQuick"><i class="glyphicon glyphicon-repeat"></i> ${text('重置')}</button>
</div>
</#form:form>
<table id="dataGrid"></table>

View File

@@ -94,8 +94,8 @@
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-sm">${text('查询')}</button>
<button type="reset" class="btn btn-default btn-sm isQuick">${text('重置')}</button>
<button type="submit" class="btn btn-primary btn-sm"><i class="glyphicon glyphicon-search"></i> ${text('查询')}</button>
<button type="reset" class="btn btn-default btn-sm isQuick"><i class="glyphicon glyphicon-repeat"></i> ${text('重置')}</button>
</div>
</#form:form>
<table id="dataGrid"></table>

View File

@@ -46,8 +46,8 @@
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-sm">${text('查询')}</button>
<button type="reset" class="btn btn-default btn-sm isQuick">${text('重置')}</button>
<button type="submit" class="btn btn-primary btn-sm"><i class="glyphicon glyphicon-search"></i> ${text('查询')}</button>
<button type="reset" class="btn btn-default btn-sm isQuick"><i class="glyphicon glyphicon-repeat"></i> ${text('重置')}</button>
</div>
</#form:form>
<table id="dataGrid"></table>

View File

@@ -1,5 +1,4 @@
<% layout('/layouts/default.html', {title: '站点管理', libs: ['validate','fileupload','ueditor','dataGrid']}){ %>
<div class="main-content">
<div class="box box-main">
<div class="box-header with-border">
@@ -71,12 +70,10 @@
<div class="form-group">
<label class="control-label col-sm-2">${text('站点logo')}</label>
<div class="col-sm-10">
<#form:fileupload id="uploadLogo" bizKey="${site.id}" bizType="site_logo" returnPath="true"
filePathInputId="logo"
uploadType="image" readonly="false" maxUploadNum="1" isMini="false"/>
<#form:input path="logo" class="form-control"/>
<#form:fileupload id="uploadLogo" bizKey="${site.id}" bizType="site_logo" returnPath="true"
filePathInputId="logo" uploadType="image" readonly="false" maxUploadNum="1" isMini="false"/>
<#form:input path="logo" class="form-control"/>
</div>
</div>
</div>
</div>
@@ -163,7 +160,7 @@
</div>
<% } %>
<script>
$("#inputForm").validate({
$('#inputForm').validate({
submitHandler: function(form){
js.ajaxSubmitForm($(form), function(data){
js.showMessage(data.message);

View File

@@ -40,8 +40,8 @@
</div>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-sm">${text('查询')}</button>
<button type="reset" class="btn btn-default btn-sm isQuick">${text('重置')}</button>
<button type="submit" class="btn btn-primary btn-sm"><i class="glyphicon glyphicon-search"></i> ${text('查询')}</button>
<button type="reset" class="btn btn-default btn-sm isQuick"><i class="glyphicon glyphicon-repeat"></i> ${text('重置')}</button>
</div>
</#form:form>
<table id="dataGrid"></table>

View File

@@ -53,8 +53,8 @@
<div class="form-group">
<button type="submit" class="btn btn-primary btn-sm">${text('查询')}</button>
<button type="reset" class="btn btn-default btn-sm isQuick">${text('重置')}</button>
<button type="submit" class="btn btn-primary btn-sm"><i class="glyphicon glyphicon-search"></i> ${text('查询')}</button>
<button type="reset" class="btn btn-default btn-sm isQuick"><i class="glyphicon glyphicon-repeat"></i> ${text('重置')}</button>
</div>
</#form:form>
<table id="dataGrid"></table>

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1007 KiB

After

Width:  |  Height:  |  Size: 3.3 MiB

BIN
modules/core/db/core.xlsx Normal file

Binary file not shown.

View File

@@ -476,6 +476,8 @@ CREATE TABLE js_sys_module
main_class_name varchar(500),
current_version varchar(50),
upgrade_info varchar(300),
gen_base_dir vargraphic(1000),
tpl_category varchar(200),
status char(1) DEFAULT '0' NOT NULL,
create_by varchar(64) NOT NULL,
create_date timestamp NOT NULL,

View File

@@ -476,6 +476,8 @@ CREATE TABLE js_sys_module
main_class_name varchar(500),
current_version varchar(50),
upgrade_info varchar(300),
gen_base_dir varchar(1000),
tpl_category varchar(200),
status char(1) DEFAULT '0' NOT NULL,
create_by varchar(64) NOT NULL,
create_date datetime NOT NULL,

4534
modules/core/db/job.erm Normal file

File diff suppressed because it is too large Load Diff

BIN
modules/core/db/job.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 531 KiB

BIN
modules/core/db/job.xlsx Normal file

Binary file not shown.

View File

@@ -476,6 +476,8 @@ CREATE TABLE [js_sys_module]
[main_class_name] varchar(500),
[current_version] varchar(50),
[upgrade_info] varchar(300),
[gen_base_dir] nvarchar(1000),
[tpl_category] varchar(200),
[status] char(1) DEFAULT '0' NOT NULL,
[create_by] varchar(64) NOT NULL,
[create_date] datetime NOT NULL,

View File

@@ -477,6 +477,8 @@ CREATE TABLE js_sys_module
main_class_name varchar(500) COMMENT '主类全名',
current_version varchar(50) COMMENT '当前版本',
upgrade_info varchar(300) COMMENT '升级信息',
gen_base_dir varchar(1000) COMMENT '生成基础路径',
tpl_category varchar(200) COMMENT '使用的模板',
status char(1) DEFAULT '0' NOT NULL COMMENT '状态0正常 1删除 2停用',
create_by varchar(64) NOT NULL COMMENT '创建者',
create_date datetime NOT NULL COMMENT '创建时间',

View File

@@ -476,6 +476,8 @@ CREATE TABLE js_sys_module
main_class_name varchar2(500),
current_version varchar2(50),
upgrade_info varchar2(300),
gen_base_dir nvarchar2(1000),
tpl_category varchar2(200),
status char(1) DEFAULT '0' NOT NULL,
create_by varchar2(64) NOT NULL,
create_date timestamp NOT NULL,
@@ -1336,6 +1338,8 @@ COMMENT ON COLUMN js_sys_module.description IS '模块描述';
COMMENT ON COLUMN js_sys_module.main_class_name IS '主类全名';
COMMENT ON COLUMN js_sys_module.current_version IS '当前版本';
COMMENT ON COLUMN js_sys_module.upgrade_info IS '升级信息';
COMMENT ON COLUMN js_sys_module.gen_base_dir IS '生成基础路径';
COMMENT ON COLUMN js_sys_module.tpl_category IS '使用的模板';
COMMENT ON COLUMN js_sys_module.status IS '状态0正常 1删除 2停用';
COMMENT ON COLUMN js_sys_module.create_by IS '创建者';
COMMENT ON COLUMN js_sys_module.create_date IS '创建时间';

View File

@@ -476,6 +476,8 @@ CREATE TABLE js_sys_module
main_class_name varchar(500),
current_version varchar(50),
upgrade_info varchar(300),
gen_base_dir varchar(1000),
tpl_category varchar(200),
status char(1) DEFAULT '0' NOT NULL,
create_by varchar(64) NOT NULL,
create_date timestamp NOT NULL,
@@ -1336,6 +1338,8 @@ COMMENT ON COLUMN js_sys_module.description IS '模块描述';
COMMENT ON COLUMN js_sys_module.main_class_name IS '主类全名';
COMMENT ON COLUMN js_sys_module.current_version IS '当前版本';
COMMENT ON COLUMN js_sys_module.upgrade_info IS '升级信息';
COMMENT ON COLUMN js_sys_module.gen_base_dir IS '生成基础路径';
COMMENT ON COLUMN js_sys_module.tpl_category IS '使用的模板';
COMMENT ON COLUMN js_sys_module.status IS '状态0正常 1删除 2停用';
COMMENT ON COLUMN js_sys_module.create_by IS '创建者';
COMMENT ON COLUMN js_sys_module.create_date IS '创建时间';

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>com.jeesite</groupId>
<artifactId>jeesite-parent</artifactId>
<version>5.5.0-SNAPSHOT</version>
<version>5.7.0-SNAPSHOT</version>
<relativePath>../../parent/pom.xml</relativePath>
</parent>
@@ -29,6 +29,7 @@
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Oracle 11g -->
<dependency>
<groupId>com.oracle</groupId>
@@ -36,37 +37,33 @@
<version>11.2.0.3</version>
<scope>runtime</scope>
</dependency>
<!-- Oracle 12c
<!-- Oracle 12c 及以上版本
<dependency>
<groupId>com.oracle.ojdbc</groupId>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc8</artifactId>
<version>19.3.0.0</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.oracle.ojdbc</groupId>
<artifactId>orai18n</artifactId>
<version>19.3.0.0</version>
<scope>runtime</scope>
</dependency> -->
<!-- SqlServer 2008 -->
<!-- SqlServer 2008
<dependency>
<groupId>net.sourceforge.jtds</groupId>
<artifactId>jtds</artifactId>
<scope>runtime</scope>
</dependency>
<!-- SqlServer 2012
</dependency> -->
<!-- SqlServer 2012 及以上版本 -->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<scope>runtime</scope>
</dependency> -->
</dependency>
<!-- PostgreSQL -->
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<!-- H2 DB
<dependency>
<groupId>com.h2database</groupId>
@@ -87,6 +84,11 @@
<artifactId>jeesite-framework</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<!--<artifactId>spring-boot-starter-undertow</artifactId>-->
</dependency>
<!-- ELK 日志收集 -->
<dependency>

View File

@@ -2,13 +2,20 @@
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
*/
package com.jeesite.modules.config;
import java.util.Collection;
import java.util.Map;
import javax.servlet.Filter;
package com.jeesite.autoconfigure.core;
import com.jeesite.common.collect.ListUtils;
import com.jeesite.common.config.Global;
import com.jeesite.common.shiro.cas.CasOutHandler;
import com.jeesite.common.shiro.config.FilterChainDefinitionMap;
import com.jeesite.common.shiro.filter.*;
import com.jeesite.common.shiro.realm.AuthorizingRealm;
import com.jeesite.common.shiro.realm.CasAuthorizingRealm;
import com.jeesite.common.shiro.realm.LdapAuthorizingRealm;
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 org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cas.CasSubjectFactory;
import org.apache.shiro.realm.Realm;
@@ -17,49 +24,33 @@ import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.web.filter.InvalidRequestFilter;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import com.jeesite.common.collect.ListUtils;
import com.jeesite.common.config.Global;
import com.jeesite.common.shiro.cas.CasOutHandler;
import com.jeesite.common.shiro.config.FilterChainDefinitionMap;
import com.jeesite.common.shiro.filter.CasFilter;
import com.jeesite.common.shiro.filter.FormFilter;
import com.jeesite.common.shiro.filter.InnerFilter;
import com.jeesite.common.shiro.filter.LdapFilter;
import com.jeesite.common.shiro.filter.LogoutFilter;
import com.jeesite.common.shiro.filter.PermissionsFilter;
import com.jeesite.common.shiro.filter.RolesFilter;
import com.jeesite.common.shiro.filter.UserFilter;
import com.jeesite.common.shiro.realm.AuthorizingRealm;
import com.jeesite.common.shiro.realm.CasAuthorizingRealm;
import com.jeesite.common.shiro.realm.LdapAuthorizingRealm;
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 javax.servlet.Filter;
import java.util.Collection;
import java.util.Map;
/**
* Shiro配置
* @author ThinkGem
* @version 2021-7-6
* @version 2023-12-20
*/
@SuppressWarnings("deprecation")
@Configuration(proxyBeanMethods = false)
@AutoConfiguration(before = SessionAutoConfiguration.class)
@ConditionalOnProperty(name="user.enabled", havingValue="true", matchIfMissing=true)
public class ShiroConfig {
public class ShiroAutoConfiguration {
/**
* Apache Shiro Filter
*/
@Bean
@Bean("shiroFilterProxy")
@Order(Ordered.HIGHEST_PRECEDENCE + 5000)
@ConditionalOnMissingBean(name="shiroFilterProxy")
public FilterRegistrationBean<Filter> shiroFilterProxy(ShiroFilterFactoryBean shiroFilter) throws Exception {
@@ -69,14 +60,14 @@ public class ShiroConfig {
bean.setOrder(Ordered.HIGHEST_PRECEDENCE + 5000);
return bean;
}
/**
* 内部系统访问过滤器
*/
private InnerFilter shiroInnerFilter() {
return new InnerFilter();
}
/**
* CAS登录过滤器
*/
@@ -85,7 +76,7 @@ public class ShiroConfig {
bean.setAuthorizingRealm(casAuthorizingRealm);
return bean;
}
/**
* LDAP登录过滤器
*/
@@ -133,7 +124,7 @@ public class ShiroConfig {
private UserFilter shiroUserFilter() {
return new UserFilter();
}
/**
* 非法请求过滤器
*/
@@ -142,12 +133,13 @@ public class ShiroConfig {
bean.setBlockNonAscii(false);
return bean;
}
/**
* Shiro认证过滤器
*/
@Bean
public ShiroFilterFactoryBean shiroFilter(WebSecurityManager webSecurityManager, AuthorizingRealm authorizingRealm,
@Bean("shiroFilter")
@ConditionalOnMissingBean(name="shiroFilter")
public ShiroFilterFactoryBean shiroFilter(WebSecurityManager webSecurityManager, AuthorizingRealm authorizingRealm,
CasAuthorizingRealm casAuthorizingRealm, LdapAuthorizingRealm ldapAuthorizingRealm) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(webSecurityManager);
@@ -169,30 +161,33 @@ public class ShiroConfig {
bean.setFilterChainDefinitionMap(chains.getObject());
return bean;
}
/**
* 系统安全认证实现类
*/
@Bean
public AuthorizingRealm authorizingRealm(SessionDAO sessionDAO) {
@Bean("authorizingRealm")
@ConditionalOnMissingBean(name="authorizingRealm")
public AuthorizingRealm authorizingRealm(@Qualifier("sessionDAO") SessionDAO sessionDAO) {
AuthorizingRealm bean = new AuthorizingRealm();
bean.setSessionDAO(sessionDAO);
return bean;
}
/**
* 单点登录信息句柄单点退出用
*/
@Bean
@Bean("casOutHandler")
@ConditionalOnMissingBean(name="casOutHandler")
public CasOutHandler casOutHandler() {
return new CasOutHandler();
}
/**
* CAS安全认证实现类
*/
@Bean
public CasAuthorizingRealm casAuthorizingRealm(SessionDAO sessionDAO, CasOutHandler casOutHandler) {
@Bean("casAuthorizingRealm")
@ConditionalOnMissingBean(name="casAuthorizingRealm")
public CasAuthorizingRealm casAuthorizingRealm(@Qualifier("sessionDAO") SessionDAO sessionDAO, CasOutHandler casOutHandler) {
CasAuthorizingRealm bean = new CasAuthorizingRealm();
bean.setSessionDAO(sessionDAO);
bean.setCasOutHandler(casOutHandler);
@@ -200,12 +195,13 @@ public class ShiroConfig {
bean.setCasServerCallbackUrl(Global.getProperty("shiro.casClientUrl") + Global.getAdminPath() + "/login-cas");
return bean;
}
/**
* LDAP安全认证实现类
*/
@Bean
public LdapAuthorizingRealm ldapAuthorizingRealm(SessionDAO sessionDAO, CasOutHandler casOutHandler) {
@Bean("ldapAuthorizingRealm")
@ConditionalOnMissingBean(name="ldapAuthorizingRealm")
public LdapAuthorizingRealm ldapAuthorizingRealm(@Qualifier("sessionDAO") SessionDAO sessionDAO, CasOutHandler casOutHandler) {
LdapAuthorizingRealm bean = new LdapAuthorizingRealm();
JndiLdapContextFactory contextFactory = (JndiLdapContextFactory) bean.getContextFactory();
contextFactory.setUrl(Global.getProperty("shiro.ldapUrl"/*, "ldap://127.0.0.1:389"*/));
@@ -217,9 +213,10 @@ public class ShiroConfig {
/**
* 定义Shiro安全管理配置
*/
@Bean
@Bean("webSecurityManager")
@ConditionalOnMissingBean(name="webSecurityManager")
public WebSecurityManager webSecurityManager(AuthorizingRealm authorizingRealm, CasAuthorizingRealm casAuthorizingRealm,
LdapAuthorizingRealm ldapAuthorizingRealm, SessionManager sessionManager, CacheManager shiroCacheManager) {
LdapAuthorizingRealm ldapAuthorizingRealm, SessionManager sessionManager, @Qualifier("shiroCacheManager") CacheManager shiroCacheManager) {
WebSecurityManager bean = new WebSecurityManager();
Collection<Realm> realms = ListUtils.newArrayList();
realms.add(authorizingRealm); // 第一个为权限授权控制类
@@ -232,21 +229,23 @@ public class ShiroConfig {
//bean.setRememberMeManager(null); // 关闭 RememberMe
return bean;
}
/**
* Shiro 生命周期处理器实现初始化和销毁回调
*/
@Bean(name="lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
@Bean("lifecycleBeanPostProcessor")
@ConditionalOnMissingBean(name="lifecycleBeanPostProcessor")
public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* Shiro 过滤器代理配置
*/
@Bean
@Bean("defaultAdvisorAutoProxyCreator")
@DependsOn({ "lifecycleBeanPostProcessor" })
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
@ConditionalOnMissingBean(name="defaultAdvisorAutoProxyCreator")
public static DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator bean = new DefaultAdvisorAutoProxyCreator();
bean.setProxyTargetClass(true);
return bean;
@@ -255,22 +254,11 @@ public class ShiroConfig {
/**
* 启用Shrio授权注解拦截方式AOP式方法级权限检查
*/
@Bean
@Bean("authorizationAttributeSourceAdvisor")
@ConditionalOnMissingBean(name="authorizationAttributeSourceAdvisor")
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(WebSecurityManager webSecurityManager) {
AuthorizationAttributeSourceAdvisor bean = new AuthorizationAttributeSourceAdvisor();
bean.setSecurityManager(webSecurityManager);
return bean;
}
// /**
// * 在方法中 注入 webSecurityManager 进行代理控制
// */
// @Bean
// public MethodInvokingFactoryBean methodInvokingFactoryBean(DefaultWebSecurityManager webSecurityManager) {
// MethodInvokingFactoryBean bean = new MethodInvokingFactoryBean();
// bean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
// bean.setArguments(new Object[] { webSecurityManager });
// return bean;
// }
}

View File

@@ -14,6 +14,7 @@ import com.jeesite.common.lang.StringUtils;
import com.jeesite.common.network.IpUtils;
import com.jeesite.common.shiro.authc.FormToken;
import com.jeesite.common.shiro.realm.BaseAuthorizingRealm;
import com.jeesite.common.utils.SpringUtils;
import com.jeesite.common.web.CookieUtils;
import com.jeesite.common.web.http.ServletUtils;
import com.jeesite.modules.sys.entity.Log;
@@ -31,7 +32,6 @@ import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.servlet.Cookie;
import org.apache.shiro.web.servlet.Cookie.SameSiteOptions;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
@@ -60,23 +60,21 @@ public class FormFilter extends org.apache.shiro.web.filter.authc.FormAuthentica
public static final String LOGIN_PARAM = "__login"; // 支持GET方式登录的参数
private static final Logger logger = LoggerFactory.getLogger(FormFilter.class);
private static FormFilter instance;
private static Cookie sessionIdCookie;
private static Cookie rememberUserCodeCookie;
private BaseAuthorizingRealm authorizingRealm;
private Cookie rememberUserCodeCookie; // 记住用户名Cookie
/**
* 构造方法
*/
public FormFilter() {
super();
rememberUserCodeCookie = new SimpleCookie();
sessionIdCookie = SpringUtils.getBean("sessionIdCookie");
rememberUserCodeCookie = new SimpleCookie(sessionIdCookie);
rememberUserCodeCookie.setName(REMEMBER_USERCODE_PARAM);
rememberUserCodeCookie.setPath(Global.getProperty("session.sessionIdCookiePath"));
rememberUserCodeCookie.setSecure(Global.getPropertyToBoolean("session.sessionIdCookieSecure", "false"));
rememberUserCodeCookie.setHttpOnly(Global.getPropertyToBoolean("session.sessionIdCookieHttpOnly", "true"));
String sameSite = Global.getProperty("session.sessionIdCookieSameSite", "Lax"); // Null、None、Lax、Strict
rememberUserCodeCookie.setSameSite(!"Null".equalsIgnoreCase(sameSite) ? SameSiteOptions.valueOf(StringUtils.upperCase(sameSite)) : null);
rememberUserCodeCookie.setMaxAge(Cookie.ONE_YEAR);
instance = this;
}
@@ -200,14 +198,6 @@ public class FormFilter extends org.apache.shiro.web.filter.authc.FormAuthentica
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
return (!isLoginRequest(request, response) && isPermissive(mappedValue)); // 不验证登录状态,只验证登录请求
}
/**
* 跳转登录页时,跳转到默认首页
*/
@Override
protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
PermissionsFilter.redirectToDefaultPath(request, response);
}
/**
* 地址访问接入验证
@@ -255,6 +245,14 @@ public class FormFilter extends org.apache.shiro.web.filter.authc.FormAuthentica
boolean isLogin = WebUtils.isTrue(request, LOGIN_PARAM);
return super.isLoginSubmission(request, response) || isLogin;
}
/**
* 跳转登录页时,跳转到默认首页
*/
@Override
protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
PermissionsFilter.redirectToDefaultPath(request, response);
}
/**
* 执行登录方法
@@ -361,20 +359,9 @@ public class FormFilter extends org.apache.shiro.web.filter.authc.FormAuthentica
// 是否显示验证码
data.put("isValidCodeLogin", Global.getConfigToInteger("sys.login.failedNumAfterValidCode", "200") == 0);
//获取当前会话对象
Session session = UserUtils.getSession();
data.put("sessionid", (String)session.getId());
// 如果登录设置了语言,则切换语言
if (paramMap.get("lang") != null){
Global.setLang((String)paramMap.get("lang"), request, response);
}
// 设置公共结果数据
setCommonData(request, response, data, paramMap);
data.put("result", "login");
data.put("demoMode", Global.isDemoMode());
data.put("useCorpModel", Global.isUseCorpModel()
&& Global.getConfigToBoolean("user.loginCodeCorpUnique", "false"));
data.put("title", Global.getProperty("productName"));
return data;
}
@@ -415,30 +402,47 @@ public class FormFilter extends org.apache.shiro.web.filter.authc.FormAuthentica
String corpCode = (String)paramMap.get("corpCode");
User user = UserUtils.getByLoginCode(username, corpCode);
LogUtils.saveLog(user, request, "登录失败", Log.TYPE_LOGIN_LOGOUT);
//获取当前会话对象
Session session = UserUtils.getSession();
data.put("sessionid", (String)session.getId());
// 如果登录设置了语言,则切换语言
// 设置公共结果数据
setCommonData(request, response, data, paramMap);
data.put("result", Global.FALSE);
return data;
}
/**
* 设置公共数据
* @author ThinkGem
*/
private static void setCommonData(HttpServletRequest request, HttpServletResponse response,
Map<String, Object> data, Map<String, Object> paramMap) {
if (ServletUtils.isAjaxRequest(request)) {
Session session = UserUtils.getSession();
data.put("sessionid", session.getId());
Cookie cookie = new SimpleCookie(sessionIdCookie);
cookie.setValue((String)session.getId());
cookie.saveTo(request, response);
}
if (paramMap.get("lang") != null){
Global.setLang((String)paramMap.get("lang"), request, response);
}
data.put("result", Global.FALSE);
data.put("demoMode", Global.isDemoMode());
data.put("useCorpModel", Global.isUseCorpModel()
&& Global.getConfigToBoolean("user.loginCodeCorpUnique", "false"));
data.put("title", Global.getProperty("productName"));
return data;
}
/**
* 获取登录页面数据
* @author ThinkGem
*/
public static Map<String, Object> getLoginSuccessData(User user, Session session) {
public static Map<String, Object> getLoginSuccessData(HttpServletRequest request, HttpServletResponse response,
User user, Session session) {
Map<String, Object> data = MapUtils.newHashMap();
if (ServletUtils.isAjaxRequest(request)) {
Cookie cookie = new SimpleCookie(sessionIdCookie);
cookie.setValue((String)session.getId());
cookie.saveTo(request, response);
}
data.put("user", user); // 设置当前用户信息
data.put("demoMode", Global.isDemoMode());
data.put("useCorpModel", Global.isUseCorpModel());

View File

@@ -19,15 +19,17 @@ import javax.servlet.http.HttpServletRequest;
*/
public class InnerFilter extends AccessControlFilter {
private static final String[] prefixes = Global.getPropertyToArray("shiro.innerFilterAllowRemoteAddrs", "127.0.0.1");
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
boolean result = false;
String[] prefixes = (String[])mappedValue;
if (prefixes == null){
prefixes = Global.getPropertyToArray("shiro.innerFilterAllowRemoteAddrs", "127.0.0.1");
prefixes = InnerFilter.prefixes;
}
if (prefixes != null && request instanceof HttpServletRequest){
String ip = request.getRemoteAddr();
String ip = request.getRemoteAddr() + "]";
for (String prefix : prefixes){
result = StringUtils.startsWithIgnoreCase(ip, StringUtils.trim(prefix));
if (result){

View File

@@ -29,11 +29,6 @@ import com.jeesite.common.web.http.wrapper.GetHttpServletRequestWrapper;
*/
public class PermissionsFilter extends org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter {
@Override
protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
PermissionsFilter.redirectToDefaultPath(request, response);
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
return PermissionsFilter.redirectTo403Page(request, response);
@@ -99,5 +94,10 @@ public class PermissionsFilter extends org.apache.shiro.web.filter.authz.Permiss
WebUtils.issueRedirect(request, response, loginUrl);
}
}
@Override
protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
PermissionsFilter.redirectToDefaultPath(request, response);
}
}

View File

@@ -4,10 +4,9 @@
*/
package com.jeesite.common.shiro.filter;
import java.io.IOException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
/**
* 角色权限过滤器
@@ -16,14 +15,14 @@ import javax.servlet.ServletResponse;
*/
public class RolesFilter extends org.apache.shiro.web.filter.authz.RolesAuthorizationFilter {
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
return PermissionsFilter.redirectTo403Page(request, response);
}
@Override
protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
PermissionsFilter.redirectToDefaultPath(request, response);
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
return PermissionsFilter.redirectTo403Page(request, response);
}
}

View File

@@ -4,10 +4,13 @@
*/
package com.jeesite.common.shiro.filter;
import java.io.IOException;
import com.jeesite.common.config.Global;
import com.jeesite.common.lang.StringUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 用户权限过滤器
@@ -16,14 +19,36 @@ import javax.servlet.ServletResponse;
*/
public class UserFilter extends org.apache.shiro.web.filter.authc.UserFilter {
@Override
protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
PermissionsFilter.redirectToDefaultPath(request, response);
private String sessionIdHeaderName;
public UserFilter () {
this.setSessionIdHeaderName(Global.getProperty("session.sessionIdHeaderName", "x-token"));
}
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
String sessionId = (String)request.getAttribute(getSessionIdHeaderName());
if (StringUtils.isNotBlank(sessionId)) {
((HttpServletResponse)response).setHeader(getSessionIdHeaderName(), sessionId);
}
return super.isAccessAllowed(request, response, mappedValue);
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
return PermissionsFilter.redirectTo403Page(request, response);
}
@Override
protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
PermissionsFilter.redirectToDefaultPath(request, response);
}
public String getSessionIdHeaderName() {
return sessionIdHeaderName;
}
public void setSessionIdHeaderName(String sessionIdHeaderName) {
this.sessionIdHeaderName = sessionIdHeaderName;
}
}

View File

@@ -16,6 +16,9 @@ import com.jeesite.modules.sys.utils.UserUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import javax.servlet.http.HttpServletRequest;
@@ -25,13 +28,13 @@ import javax.servlet.http.HttpServletRequest;
* @version 2018-7-11
*/
public class AuthorizingRealm extends BaseAuthorizingRealm {
public static final String HASH_ALGORITHM = "SHA-1";
public static final int HASH_INTERATIONS = 1024;
public static final int SALT_SIZE = 8;
private UserService userService;
public AuthorizingRealm() {
super();
// // 设定密码校验的Hash算法与迭代次数V4.1.4及以上版本不需要了统一使用validatePassword验证密码
@@ -39,7 +42,7 @@ public class AuthorizingRealm extends BaseAuthorizingRealm {
// matcher.setHashIterations(HASH_INTERATIONS);
// this.setCredentialsMatcher(matcher);
}
/**
* 获取登录凭证,将 authcToken 转换为 FormToken参考 CAS 实现
*/
@@ -47,7 +50,7 @@ public class AuthorizingRealm extends BaseAuthorizingRealm {
protected FormToken getFormToken(AuthenticationToken authcToken) {
return super.getFormToken(authcToken);
}
/**
* 用于用户根据登录信息获取用户信息<br>
* 1、默认根据登录账号登录信息UserUtils.getByLoginCode(formToken.getUsername(), formToken.getParam("corpCode"));<br>
@@ -58,7 +61,7 @@ public class AuthorizingRealm extends BaseAuthorizingRealm {
protected User getUserInfo(FormToken formToken) {
return super.getUserInfo(formToken);
}
/**
* 校验登录凭证如密码验证token验证验证失败抛出 AuthenticationException 异常
*/
@@ -66,6 +69,14 @@ public class AuthorizingRealm extends BaseAuthorizingRealm {
protected void assertCredentialsMatch(AuthenticationToken authcToken, AuthenticationInfo authcInfo) throws AuthenticationException {
super.assertCredentialsMatch(authcToken, authcInfo);
}
/**
* 获取用户授权信息,默认返回类型 SimpleAuthorizationInfo
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(LoginInfo loginInfo, Subject subject, Session session, User user) {
return super.doGetAuthorizationInfo(loginInfo, subject, session, user);
}
/**
* 生成密文密码生成随机的16位salt并经过1024次 sha-1 hash

View File

@@ -1,5 +1,14 @@
package com.jeesite.common.ueditor.upload;
import com.jeesite.common.image.ImageUtils;
import com.jeesite.common.io.FileUtils;
import com.jeesite.common.media.VideoUtils;
import com.jeesite.common.ueditor.PathFormat;
import com.jeesite.common.ueditor.define.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -8,21 +17,6 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import com.jeesite.common.image.ImageUtils;
import com.jeesite.common.io.FileUtils;
import com.jeesite.common.media.VideoUtils;
import com.jeesite.common.ueditor.PathFormat;
import com.jeesite.common.ueditor.define.ActionMap;
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;
public class BinaryUploader {
public static final State save(HttpServletRequest request, Map<String, Object> conf) {
@@ -64,7 +58,7 @@ public class BinaryUploader {
savePath = PathFormat.parse(savePath, originFileName);
String physicalPath = FileUtils.path((String) conf.get("rootPath") + savePath);
String physicalPath = FileUtils.path(conf.get("rootPath") + savePath);
InputStream is = null;
State storageState = null;

View File

@@ -1,12 +1,6 @@
package com.jeesite.common.ueditor.upload;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import com.jeesite.common.config.Global;
import com.jeesite.common.idgen.IdGen;
import com.jeesite.common.io.FileUtils;
import com.jeesite.common.io.PropertiesUtils;
@@ -14,6 +8,11 @@ import com.jeesite.common.lang.StringUtils;
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.web.http.ServletUtils;
import com.jeesite.modules.file.utils.FileUploadUtils;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
public class StorageManager {
@@ -194,12 +193,21 @@ public class StorageManager {
/**
* UEditor上传文件成功后调用事件
*
* @param physicalPath 上传文件实际路径
* @param storageState url 返回到客户端的文件访问地址
*/
public static void uploadFileSuccess(String physicalPath, State storageState) {
if (!Global.getPropertyToBoolean("file.enabled", "true")) {
return;
}
File file = new File(physicalPath);
String url = FileUploadUtils.ossFileUpload(file, StringUtils.substringAfter(
FileUtils.path(file.getAbsolutePath()), Global.USERFILES_BASE_URL));
if (!StringUtils.contains(url, "://")) {
HttpServletRequest request = ServletUtils.getRequest();
url = FileUtils.path((request != null ? request.getContextPath() : StringUtils.EMPTY) + url);
}
storageState.putInfo("url", url);
}
}

View File

@@ -0,0 +1,81 @@
/**
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
* No deletion without permission, or be held responsible to law.
*/
package com.jeesite.modules.config.web;
import com.jeesite.common.config.Global;
import com.jeesite.common.lang.StringUtils;
import com.jeesite.common.web.http.ServletUtils;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import javax.servlet.Filter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
/**
* IP地址黑白名单过滤器配置
*/
@Configuration(proxyBeanMethods = false)
public class IpAddrFilterConfig {
private static long clearCacheTime;
private static String[] allowPrefixes;
private static String[] denyPrefixes;
@Bean
public FilterRegistrationBean<Filter> ipAddrFilter() {
FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>();
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
bean.setFilter((setvletRequest, setvletResponse, chain) -> {
if (isAccessAllowed(setvletRequest, setvletResponse)) {
chain.doFilter(setvletRequest, setvletResponse);
} else {
HttpServletResponse response = (HttpServletResponse) setvletResponse;
response.setStatus(403);
ServletUtils.renderString(response, Global.getText("访问拒绝"));
}
});
bean.addUrlPatterns("/*");
return bean;
}
private boolean isAccessAllowed(ServletRequest request, ServletResponse response) {
if (clearCacheTime == 0 || clearCacheTime != Global.getClearCacheTime()) {
allowPrefixes = Global.getConfigToArray("sys.filter.allowIpAddrs", StringUtils.EMPTY);
denyPrefixes = Global.getConfigToArray("sys.filter.denyIpAddrs", StringUtils.EMPTY);
clearCacheTime = Global.getClearCacheTime();
}
// 如果未初始化,直接拒绝
if (allowPrefixes == null || denyPrefixes == null) {
return false;
}
// 如果未设置黑白名单,直接通过
if (allowPrefixes.length == 0 && denyPrefixes.length == 0) {
return true;
}
// 如果未设置白名单,则直接通过白名单,再从黑名单中检查
boolean result = allowPrefixes.length == 0;
String ip = request.getRemoteAddr() + "]";
for (String prefix : allowPrefixes) {
if (StringUtils.startsWithIgnoreCase(ip, StringUtils.trim(prefix))){
result = true;
break;
}
}
for (String prefix : denyPrefixes) {
if (StringUtils.startsWithIgnoreCase(ip, StringUtils.trim(prefix))){
result = false;
break;
}
}
if (result) {
return true;
}
return false;
}
}

View File

@@ -4,23 +4,16 @@
*/
package com.jeesite.modules.config.web;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import javax.servlet.Filter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
/**
* 将请求协议转换为 https
* @author ThinkGem
@@ -34,35 +27,23 @@ public class SchemeHttpsConfig {
public FilterRegistrationBean<Filter> schemeFilterRegistrationBean() {
FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<>();
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
bean.setFilter(new Filter() {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
bean.setFilter((request, response, chain) -> {
chain.doFilter(new HttpServletRequestWrapper((HttpServletRequest) request) {
@Override
public String getScheme() {
return "https";
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(new HttpServletRequestWrapper((HttpServletRequest) request) {
@Override
public String getScheme() {
return "https";
@Override
public StringBuffer getRequestURL() {
StringBuffer sb = super.getRequestURL();
if ("http:".equals(sb.substring(0, 5))){
return sb.replace(0, 5, "https:");
}else{
return sb;
}
@Override
public StringBuffer getRequestURL() {
StringBuffer sb = super.getRequestURL();
if ("http:".equals(sb.substring(0, 5))){
return sb.replace(0, 5, "https:");
}else{
return sb;
}
}
}, response);
}
@Override
public void destroy() {}
}
}, response);
});
bean.addUrlPatterns("/*");
return bean;

View File

@@ -4,9 +4,14 @@
*/
package com.jeesite.modules.msg.web;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.jeesite.common.config.Global;
import com.jeesite.common.entity.Page;
import com.jeesite.common.lang.StringUtils;
import com.jeesite.common.web.BaseController;
import com.jeesite.modules.msg.entity.MsgInner;
import com.jeesite.modules.msg.entity.MsgInnerRecord;
import com.jeesite.modules.msg.entity.MsgPush;
import com.jeesite.modules.msg.service.MsgInnerService;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -18,14 +23,8 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.jeesite.common.config.Global;
import com.jeesite.common.entity.Page;
import com.jeesite.common.lang.StringUtils;
import com.jeesite.common.web.BaseController;
import com.jeesite.modules.msg.entity.MsgInner;
import com.jeesite.modules.msg.entity.MsgInnerRecord;
import com.jeesite.modules.msg.entity.MsgPush;
import com.jeesite.modules.msg.service.MsgInnerService;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 内部消息Controller
@@ -122,7 +121,8 @@ public class MsgInnerController extends BaseController {
return renderResult(Global.FALSE, "数据已发布,不允许修改!");
}
msgInnerService.save(msgInner);
return renderResult(Global.TRUE, text("保存消息成功!"));
return renderResult(Global.TRUE, text((MsgInner.STATUS_NORMAL
.equals(msgInner.getStatus()) ? "发布" : "保存") + "消息成功!"));
}
/**

View File

@@ -4,13 +4,13 @@
*/
package com.jeesite.modules.sys.dao;
import java.util.List;
import com.jeesite.common.dao.CrudDao;
import com.jeesite.common.mybatis.annotation.MyBatisDao;
import com.jeesite.modules.sys.entity.EmpUser;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import java.util.List;
/**
* 员工管理DAO接口
* @author ThinkGem
@@ -20,6 +20,14 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ConditionalOnProperty(name="user.enabled", havingValue="true", matchIfMissing=true)
public interface EmpUserDao extends CrudDao<EmpUser> {
// @Override
// @Results({
// @Result(column = "mobile", property = "mobile",
// javaType = String.class, typeHandler = AesTypeHandler.class)
// })
// @SelectProvider(type = SelectSqlProvider.class, method = "get")
// EmpUser get(EmpUser entity);
/**
* 查询全部用户,仅返回基本信息
*/

View File

@@ -18,4 +18,10 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@ConditionalOnProperty(name="user.enabled", havingValue="true", matchIfMissing=true)
public interface LogDao extends CrudDao<Log> {
/**
* 删除某个日期之前创建的日志
* @param log .createDate
*/
int deleteLogBefore(Log log);
}

View File

@@ -27,6 +27,10 @@ import javax.validation.Valid;
* @version 2017-03-25
*/
@Table(name="${_prefix}sys_user", alias="a", label="员工信息", columns={
// 手机号加密脱敏例子,共同打开 EmpUserDao.java 和 EmpUserDao.xml 中的 result 注释
// 详细 typeHandler 用法请看文档https://jeesite.com/docs/dao-mybatis/#手机号加密脱敏
// @Column(name="mobile", attrName="mobile", label="手机号码", queryType=QueryType.EQ,
// javaType = String.class, typeHandler = AesTypeHandler.class),
@Column(includeEntity=User.class),
}, joinTable={
@JoinTable(type=Type.JOIN, entity=Employee.class, alias="e",
@@ -42,35 +46,35 @@ import javax.validation.Valid;
@JoinTable(type=Type.LEFT_JOIN, entity=Office.class, alias="o",
on="o.office_code=e.office_code", attrName="employee.office",
columns={
@Column(includeEntity=DataEntity.class),
@Column(includeEntity=TreeEntity.class),
@Column(name="office_code", label="机构编码", isPK=true),
@Column(name="view_code", label="机构代码"),
@Column(name="office_name", label="机构名称", isQuery=false),
@Column(name="full_name", label="机构全称"),
@Column(name="office_type", label="机构类型"),
@Column(name="leader", label="负责人"),
@Column(name="phone", label="电话"),
@Column(name="address", label="联系地址"),
@Column(name="zip_code", label="邮政编码"),
@Column(name="email", label="邮箱"),
@Column(includeEntity=DataEntity.class),
@Column(includeEntity=TreeEntity.class),
@Column(name="office_code", label="机构编码", isPK=true),
@Column(name="view_code", label="机构代码"),
@Column(name="office_name", label="机构名称", isQuery=false),
@Column(name="full_name", label="机构全称"),
@Column(name="office_type", label="机构类型"),
@Column(name="leader", label="负责人"),
@Column(name="phone", label="电话"),
@Column(name="address", label="联系地址"),
@Column(name="zip_code", label="邮政编码"),
@Column(name="email", label="邮箱"),
}),
@JoinTable(type=Type.LEFT_JOIN, entity=Company.class, alias="c",
on="c.company_code=e.company_code", attrName="employee.company",
columns={
@Column(includeEntity=DataEntity.class),
@Column(includeEntity=TreeEntity.class),
@Column(name="company_code", label="公司编码", isPK=true),
@Column(name="view_code", label="公司代码"),
@Column(name="company_name", label="公司名称", isQuery=false),
@Column(name="full_name", label="公司全称"),
@Column(includeEntity=DataEntity.class),
@Column(includeEntity=TreeEntity.class),
@Column(name="company_code", label="公司编码", isPK=true),
@Column(name="view_code", label="公司代码"),
@Column(name="company_name", label="公司名称", isQuery=false),
@Column(name="full_name", label="公司全称"),
}),
@JoinTable(type=Type.LEFT_JOIN, entity=Area.class, alias="ar",
on="ar.area_code = c.area_code", attrName="employee.company.area",
columns={
@Column(name="area_code", label="区域代码", isPK=true),
@Column(name="area_name", label="区域名称", isQuery=false),
@Column(name="area_type", label="区域类型"),
@Column(name="area_code", label="区域代码", isPK=true),
@Column(name="area_name", label="区域名称", isQuery=false),
@Column(name="area_type", label="区域类型"),
}),
// @JoinTable(type=Type.LEFT_JOIN, entity=User.class, attrName="this", alias="u",
// on="u.user_code = a.create_by", columns={

View File

@@ -6,6 +6,7 @@ package com.jeesite.modules.sys.entity;
import com.jeesite.common.entity.DataEntity;
import com.jeesite.common.mybatis.annotation.Column;
import com.jeesite.common.mybatis.annotation.JoinTable;
import com.jeesite.common.mybatis.annotation.Table;
/**
@@ -16,6 +17,19 @@ import com.jeesite.common.mybatis.annotation.Table;
@Table(name="${_prefix}sys_employee_post", alias="a", columns={
@Column(name="emp_code", attrName="empCode", label="员工编码", isPK=true),
@Column(name="post_code", attrName="postCode", label="岗位编码", isPK=true),
},
joinTable={
@JoinTable(type=JoinTable.Type.LEFT_JOIN, entity=Post.class, alias="p", lazy = true,
on="a.post_code = p.post_code", attrName="post",
columns={
@Column(name="post_code", attrName="postCode", label="岗位编码", isPK=true),
@Column(name="post_name", attrName="postName", label="岗位名称"),
}),
@JoinTable(type=JoinTable.Type.JOIN, entity=User.class, alias="u", lazy = true,
on="a.emp_code = u.ref_code AND u.user_type='employee'", attrName="this",
columns={
@Column(name="user_code", attrName="userCode", label="用户编码", isPK=true),
})
}, orderBy=""
)
public class EmployeePost extends DataEntity<EmployeePost> {
@@ -23,7 +37,11 @@ public class EmployeePost extends DataEntity<EmployeePost> {
private static final long serialVersionUID = 1L;
private String empCode; // 员工编码
private String postCode; // 岗位编码
private Post post; // sqlMap().loadJoinTableAlias("p") 的时候返回数据
private String userCode; // 根据用户编码查询 sqlMap().loadJoinTableAlias("p,u") 的时候有效
public EmployeePost() {
this(null, null);
}
@@ -48,5 +66,20 @@ public class EmployeePost extends DataEntity<EmployeePost> {
public void setPostCode(String postCode) {
this.postCode = postCode;
}
public Post getPost() {
return post;
}
public void setPost(Post post) {
this.post = post;
}
public String getUserCode() {
return userCode;
}
public void setUserCode(String userCode) {
this.userCode = userCode;
}
}

View File

@@ -240,7 +240,7 @@ public class Log extends DataEntity<Log> {
/**
* 设置请求参数
* @param paramMap
* @param paramsMap
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public void setRequestParams(Map paramsMap){

View File

@@ -35,6 +35,7 @@ public class Post extends DataEntity<Post> {
private Integer postSort; // 岗位排序(升序)
private String empCode; // 根据用户查询岗位
private String userCode; // 根据用户编码查询
private String roleCodes; // 关联的角色编号
private String roleNames; // 关联的角色名称
@@ -110,6 +111,16 @@ public class Post extends DataEntity<Post> {
this.empCode = empCode;
}
@ApiModelProperty("根据用户编码查询")
public String getUserCode() {
return userCode;
}
public void setUserCode(String userCode) {
this.userCode = userCode;
}
@ApiModelProperty("岗位绑定角色编码")
public String getRoleCodes() {
return roleCodes;
}

View File

@@ -18,12 +18,12 @@ import com.jeesite.common.mybatis.annotation.Table;
@Column(name="role_code", attrName="roleCode", label="角色编码", isPK=true),
@Column(name="post_code", attrName="postCode", label="岗位编码", isPK=true),
},
joinTable = {
joinTable={
@JoinTable(type=JoinTable.Type.LEFT_JOIN, entity=Role.class, alias="r", lazy = true,
on="a.role_code = r.role_code", attrName="role",
columns={
@Column(name="role_code", attrName="roleCode", label="角色编码", isPK=true),
@Column(name="role_name", attrName="roleName", label="角色名称"),
@Column(name="role_code", attrName="roleCode", label="角色编码", isPK=true),
@Column(name="role_name", attrName="roleName", label="角色名称"),
})
}, orderBy=""
)
@@ -33,7 +33,7 @@ public class PostRole extends DataEntity<PostRole> {
private String postCode; // 岗位编码
private String roleCode; // 角色编码
private Role role; // sqlMap().loadJoinTableAlias("r")的时候返回数据
private Role role; // sqlMap().loadJoinTableAlias("r") 的时候返回数据
public PostRole() {
this(null, null);

View File

@@ -4,14 +4,6 @@
*/
package com.jeesite.modules.sys.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.MDC;
import org.springframework.core.NamedThreadLocal;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import com.jeesite.common.idgen.IdGen;
import com.jeesite.common.lang.ByteUtils;
import com.jeesite.common.lang.StringUtils;
@@ -20,6 +12,13 @@ import com.jeesite.common.network.IpUtils;
import com.jeesite.common.service.BaseService;
import com.jeesite.modules.sys.utils.LogUtils;
import com.jeesite.modules.sys.utils.UserUtils;
import org.slf4j.MDC;
import org.springframework.core.NamedThreadLocal;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 日志拦截器
@@ -36,7 +35,7 @@ public class LogInterceptor extends BaseService implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
if (StringUtils.isBlank(MDC.get(TRACE_ID))) {
MDC.put(TRACE_ID, IdGen.nextId());
MDC.put(TRACE_ID, IdGen.randomBase62(4));
}
long beginTime = System.currentTimeMillis();// 1、开始时间
startTimeThreadLocal.set(beginTime); // 线程绑定变量(该数据只有当前请求的线程可见)

View File

@@ -25,5 +25,13 @@ public interface LogService extends CrudServiceApi<Log> {
* 不使用数据库事务,执行插入日志
*/
void insertLog(Log entity);
/**
* 清理指定日期之前的日志可新建job定时调用
* 1、清理1年前的所有日志logService.deleteLogBefore(1, 0, 0);
* 2、清理6个月前的所有日志logService.deleteLogBefore(0, 6, 0);
* 3、清理7天前的所有日志logService.deleteLogBefore(0, 0, 7);
* 4、清理1年6个月前的所有日志logService.deleteLogBefore(1, 6, 0);
*/
void deleteLogBefore(Integer year, Integer months, Integer days);
}

View File

@@ -80,6 +80,14 @@ public class EmpUserServiceSupport extends CrudService<EmpUserDao, EmpUser>
}
}
/**
* 查询数据
*/
@Override
public List<EmpUser> findList(EmpUser entity) {
return super.findList(entity);
}
/**
* 分页查询数据
*/

View File

@@ -49,7 +49,15 @@ public class EmployeeServiceSupport extends CrudService<EmployeeDao, Employee>
where.setEmpNo(employee.getEmpNo());
return dao.getByEntity(where);
}
/**
* 查询数据
*/
@Override
public List<Employee> findList(Employee entity) {
return super.findList(entity);
}
/**
* 查询分页数据
*/

View File

@@ -4,10 +4,13 @@
*/
package com.jeesite.modules.sys.service.support;
import java.util.Date;
import org.springframework.transaction.annotation.Transactional;
import com.jeesite.common.datasource.DataSourceHolder;
import com.jeesite.common.entity.Page;
import com.jeesite.common.lang.DateUtils;
import com.jeesite.common.service.CrudService;
import com.jeesite.modules.sys.dao.LogDao;
import com.jeesite.modules.sys.entity.Log;
@@ -50,4 +53,30 @@ public class LogServiceSupport extends CrudService<LogDao, Log>
dao.insert(entity);
}
/**
* 清理指定日期之前的日志可新建job定时调用
* 1、清理1年前的所有日志logService.deleteLogBefore(1, 0, 0);
* 2、清理6个月前的所有日志logService.deleteLogBefore(0, 6, 0);
* 3、清理7天前的所有日志logService.deleteLogBefore(0, 0, 7);
* 4、清理1年6个月前的所有日志logService.deleteLogBefore(1, 6, 0);
*/
@Override
@Transactional
public void deleteLogBefore(Integer year, Integer months, Integer days) {
Date date = DateUtils.getOfDayLast(new Date());
if (year != null && year != 0) {
date = DateUtils.addYears(date, -year);
}
if (months != null && months != 0) {
date = DateUtils.addMonths(date, -months);
}
if (days != null && days != 0) {
date = DateUtils.addDays(date, -days);
}
Log log = new Log();
log.setCreateDate(date);
dao.deleteLogBefore(log);
}
}

View File

@@ -50,6 +50,14 @@ public class PostServiceSupport extends CrudService<PostDao, Post>
return dao.getByEntity(where);
}
/**
* 查询数据
*/
@Override
public List<Post> findList(Post entity) {
return super.findList(entity);
}
/**
* 查询岗位
*/

View File

@@ -14,6 +14,7 @@ import com.jeesite.common.web.BaseController;
import com.jeesite.common.web.CookieUtils;
import com.jeesite.common.web.http.ServletUtils;
import com.jeesite.modules.sys.entity.Menu;
import com.jeesite.modules.sys.entity.Role;
import com.jeesite.modules.sys.entity.User;
import com.jeesite.modules.sys.utils.PwdUtils;
import com.jeesite.modules.sys.utils.UserUtils;
@@ -107,9 +108,8 @@ public class LoginController extends BaseController{
*/
@RequestMapping(value = "loginFailure")
public String loginFailure(HttpServletRequest request, HttpServletResponse response, Model model) {
LoginInfo loginInfo = UserUtils.getLoginInfo();
// 如果已经登录,则跳转到管理首页
// // 如果已经登录,则跳转到管理首页
// LoginInfo loginInfo = UserUtils.getLoginInfo();
// if(loginInfo != null){ // 注释掉,已经登录的账号,正常返回登录失败信息,方便前端判断。
// String queryString = request.getQueryString();
// queryString = queryString == null ? "" : "?" + queryString;
@@ -188,7 +188,7 @@ public class LoginController extends BaseController{
// 获取当前会话对象,并返回一些数据
Session session = UserUtils.getSession();
model.addAllAttributes(FormFilter.getLoginSuccessData(user, session));
model.addAllAttributes(FormFilter.getLoginSuccessData(request, response, user, session));
// 是否是登录操作
boolean isLogin = Global.TRUE.equals(session.getAttribute(BaseAuthorizingRealm.IS_LOGIN_OPER));
@@ -204,6 +204,23 @@ public class LoginController extends BaseController{
if (loginInfo.getParam("lang") != null){
Global.setLang(loginInfo.getParam("lang"), request, response);
}
// 根据当前用户子系统,切换到默认系统下
for(Role role : user.getRoleList()) {
if (role.getSysCodes() != null) {
String sysCode = null;
for (String code : StringUtils.splitComma(role.getSysCodes())) {
if (StringUtils.isNotBlank(code)) {
sysCode = code;
break;
}
}
if (sysCode != null) {
session.setAttribute("sysCode", sysCode);
UserUtils.removeCache(UserUtils.CACHE_AUTH_INFO+"_"+session.getId());
break;
}
}
}
}
// 获取登录成功后跳转的页面

View File

@@ -67,7 +67,7 @@ public class OnlineController extends BaseController{
public String list(Model model) {
return "modules/sys/onlineList";
}
/**
* 在线用户列表数据
* @author ThinkGem
@@ -75,12 +75,12 @@ public class OnlineController extends BaseController{
@RequiresPermissions("sys:online:view")
@RequestMapping(value = "listData")
@ResponseBody
public List<Map<String, Object>> listData(String isAllOnline, String isVisitor, String sessionId,
public List<Map<String, Object>> listData(String isAllOnline, String isVisitor, String sessionId,
String userCode, String userName, String userType, String orderBy) {
List<Map<String, Object>> list = ListUtils.newArrayList();
boolean excludeLeave = !Global.YES.equals(isAllOnline);
boolean excludeVisitor = !Global.YES.equals(isVisitor);
Collection<Session> sessions = sessionDAO.getActiveSessions(excludeLeave,
Collection<Session> sessions = sessionDAO.getActiveSessions(excludeLeave,
excludeVisitor, null, sessionId, userCode);
long currentTime = System.currentTimeMillis();
for (Session session : sessions){
@@ -93,10 +93,11 @@ public class OnlineController extends BaseController{
Map<String, Object> map = MapUtils.newLinkedHashMap();
// 为了安全性,需要有权限的人才能看
if (UserUtils.getSubject().isPermitted("sys:online:edit")){
map.put("id", session.getId().toString());
map.put("id", session.getId().toString());
}
map.put("startTimestamp", DateUtils.formatDateTime(session.getStartTimestamp()));
map.put("lastAccessTime", DateUtils.formatDateTime(session.getLastAccessTime()));
map.put("timeoutLong", session.getTimeout()-(currentTime-session.getLastAccessTime().getTime()));
map.put("timeout", TimeUtils.formatTime(session.getTimeout()-(currentTime-session.getLastAccessTime().getTime())));
map.put("userCode", session.getAttribute("userCode"));
map.put("userName", session.getAttribute("userName"));
@@ -109,18 +110,27 @@ public class OnlineController extends BaseController{
if (StringUtils.isBlank(orderBy)){
orderBy = "lastAccessTime desc";
}
orderBy = StringUtils.replace(orderBy, "timeout", "timeoutLong");
final String[] ss = orderBy.trim().split(" ");
if (ss.length == 2){
list.sort((o1, o2) -> {
String s1 = (String) o1.get(ss[0]);
String s2 = (String) o2.get(ss[0]);
Object s1 = o1.get(ss[0]);
Object s2 = o2.get(ss[0]);
if (s1 == null || s2 == null) {
return -1;
}
if ("asc".equals(ss[1])) {
return s1.compareTo(s2);
} else {
return s2.compareTo(s1);
if (StringUtils.endsWith(ss[0], "Long")) {
if ("asc".equals(ss[1])) {
return ((Long)s1).compareTo((Long)s2);
} else {
return ((Long)s2).compareTo((Long)s1);
}
}else{
if ("asc".equals(ss[1])) {
return ((String)s1).compareTo((String)s2);
} else {
return ((String)s2).compareTo((String)s1);
}
}
});
}
@@ -135,6 +145,9 @@ public class OnlineController extends BaseController{
@RequestMapping(value = "tickOut")
@ResponseBody
public String kickOut(String sessionId) {
if (Global.isDemoMode()){
return renderResult(Global.FALSE, "演示模式,不允许操作!");
}
Session session = sessionDAO.readSession(sessionId);
if (session != null){
Map<String, String> onlineTickOutMap = SysCacheUtils.get("onlineTickOutMap");

View File

@@ -4,29 +4,6 @@
*/
package com.jeesite.modules.sys.web.user;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import io.swagger.annotations.Api;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import com.alibaba.fastjson.JSONValidator;
import com.jeesite.common.codec.EncodeUtils;
import com.jeesite.common.collect.ListUtils;
@@ -41,20 +18,28 @@ import com.jeesite.common.shiro.realm.AuthorizingRealm;
import com.jeesite.common.utils.excel.ExcelExport;
import com.jeesite.common.utils.excel.annotation.ExcelField.Type;
import com.jeesite.common.web.BaseController;
import com.jeesite.modules.sys.entity.EmpUser;
import com.jeesite.modules.sys.entity.Employee;
import com.jeesite.modules.sys.entity.Post;
import com.jeesite.modules.sys.entity.Role;
import com.jeesite.modules.sys.entity.User;
import com.jeesite.modules.sys.entity.UserDataScope;
import com.jeesite.modules.sys.service.EmpUserService;
import com.jeesite.modules.sys.service.EmployeeService;
import com.jeesite.modules.sys.service.PostService;
import com.jeesite.modules.sys.service.RoleService;
import com.jeesite.modules.sys.service.UserService;
import com.jeesite.modules.sys.entity.*;
import com.jeesite.modules.sys.service.*;
import com.jeesite.modules.sys.utils.EmpUtils;
import com.jeesite.modules.sys.utils.ModuleUtils;
import com.jeesite.modules.sys.utils.UserUtils;
import io.swagger.annotations.Api;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
/**
* 员工用户Controller
@@ -279,19 +264,20 @@ public class EmpUserController extends BaseController {
@RequiresPermissions("sys:empUser:updateStatus")
@ResponseBody
@RequestMapping(value = "disable")
public String disable(EmpUser empUser) {
public String disable(EmpUser empUser, boolean freeze) {
if (User.isSuperAdmin(empUser.getUserCode())) {
return renderResult(Global.FALSE, "非法操作,不能够操作此用户!");
}
if (!EmpUser.USER_TYPE_EMPLOYEE.equals(empUser.getUserType())){
return renderResult(Global.FALSE, "非法操作,不能够操作此用户!");
}
String text = freeze ? "冻结" : "停用";
if (empUser.currentUser().getUserCode().equals(empUser.getUserCode())) {
return renderResult(Global.FALSE, text("停用用户失败,不允许停用当前用户"));
return renderResult(Global.FALSE, text(text + "用户失败,不允许" + text + "当前用户"));
}
empUser.setStatus(User.STATUS_DISABLE);
empUser.setStatus(freeze ? User.STATUS_FREEZE : User.STATUS_DISABLE);
empUserService.updateStatus(empUser);
return renderResult(Global.TRUE, text("停用用户''{0}''成功", empUser.getUserName()));
return renderResult(Global.TRUE, text(text + "用户''{0}''成功", empUser.getUserName()));
}
/**
@@ -302,17 +288,18 @@ public class EmpUserController extends BaseController {
@RequiresPermissions("sys:empUser:updateStatus")
@ResponseBody
@RequestMapping(value = "enable")
public String enable(EmpUser empUser) {
public String enable(EmpUser empUser, boolean freeze) {
if (User.isSuperAdmin(empUser.getUserCode())) {
return renderResult(Global.FALSE, "非法操作,不能够操作此用户!");
}
if (!EmpUser.USER_TYPE_EMPLOYEE.equals(empUser.getUserType())){
return renderResult(Global.FALSE, "非法操作,不能够操作此用户!");
}
String text = freeze ? "解冻" : "启用";
empUser.setStatus(User.STATUS_NORMAL);
empUserService.updateStatus(empUser);
AuthorizingRealm.isValidCodeLogin(empUser.getLoginCode(), empUser.getCorpCode_(), null, "success");
return renderResult(Global.TRUE, text("启用用户''{0}''成功", empUser.getUserName()));
return renderResult(Global.TRUE, text(text + "用户''{0}''成功", empUser.getUserName()));
}
/**

View File

@@ -1 +1,2 @@
com.jeesite.autoconfigure.core.ShiroAutoConfiguration
com.jeesite.autoconfigure.sys.SysAutoConfiguration

View File

@@ -27,6 +27,10 @@ jdbc:
# 获取连接等待超时时间单位毫秒1分钟4.0.6+
maxWait: 60000
# 连接超时参数,单位毫秒 v5.5.2+
connectTimeout: ~
socketTimeout: ~
# 从池中取出连接前进行检验如果检验失败则从池中去除连接并尝试取出另一个4.0.6+
testOnBorrow: false
@@ -47,7 +51,7 @@ jdbc:
removeAbandoned: false
removeAbandonedTimeout: 2100
# Oracle 下会自动开启 PSCache并指定每个连接上 PSCache 大小。若不指定,则与 maxActive 相同4.1.5+
# 是否缓存 PreparedStatement 对象的最大数量4.1.5+
maxPoolPreparedStatementPerConnectionSize: ~
# 设置连接属性,可获取到表的 remark (备注)
@@ -402,7 +406,7 @@ shiro:
# 允许跨域访问时 CORS可以获取和返回的方法和请求头
#accessControlAllowMethods: GET, POST, OPTIONS
#accessControlAllowHeaders: content-type, x-requested-with, x-ajax, x-token, x-remember
#accessControlExposeHeaders: x-remember
#accessControlExposeHeaders: x-token, x-remember
# 是否允许接收跨域的Cookie凭证数据 CORS
#accessControlAllowCredentials: false
@@ -416,8 +420,8 @@ shiro:
# 是否在登录后生成新的Session默认false
isGenerateNewSessionAfterLogin: false
# 内部系统访问过滤器可设置多个允许的内部系统IP地址串多个用逗号隔开
innerFilterAllowRemoteAddrs: 127.0.0.1
# 内部系统访问过滤器可设置多个允许的内部系统IP地址串多个用逗号隔开完整的IP使用“]”符号结尾
innerFilterAllowRemoteAddrs: 127.0.0.1]
# URI 权限过滤器定义(自定义添加参数时,请不要移除 ${adminPath}/** = user否则会导致权限异常
# 提示:填写过滤规则,请注意先后顺序,从上到下,先匹配先受用规则,匹配成功后不再继续匹配。
@@ -447,6 +451,9 @@ shiro:
/userfiles/** = anon
/validCode = anon
/static/** = anon
/oauth2/login/** = anon
/oauth2/binder/** = anon
/oauth2/callback/** = anon
/oauth2/authorize = user
/druid/** = perms[sys:state:druid]
/bpm/modeler/** = perms[bpm:modeler]

View File

@@ -17,7 +17,8 @@
<logger name="com.atomikos.recovery.xa.XaResourceRecoveryManager" level="ERROR" />
<logger name="org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator" level="DEBUG" />
<!-- <logger name="org.springframework.transaction.support.TransactionSynchronizationManager" level="TRACE" /> -->
<logger name="org.springframework.web.servlet.PageNotFound" level="ERROR" />
<logger name="springfox.documentation.spring.web.readers.parameter.ParameterDataTypeReader" level="ERROR" />
<logger name="springfox.documentation.schema.property.CachingModelPropertiesProvider" level="ERROR" />
<!-- <logger name="io.swagger" level="DEBUG" /> -->

View File

@@ -476,6 +476,8 @@ CREATE TABLE ${_prefix}sys_module
main_class_name varchar(500),
current_version varchar(50),
upgrade_info varchar(300),
gen_base_dir vargraphic(1000),
tpl_category varchar(200),
status char(1) DEFAULT '0' NOT NULL,
create_by varchar(64) NOT NULL,
create_date timestamp NOT NULL,

View File

@@ -1,63 +1,62 @@
-- tables_db2_v95.sql
create table ${_prefix}job_job_details(
sched_name varchar(120) not null,
job_name varchar(80) not null,
job_group varchar(80) not null,
description varchar(120),
job_class_name varchar(128) not null,
is_durable integer not null,
is_nonconcurrent integer not null,
is_update_data integer not null,
requests_recovery integer not null,
job_data blob(2000),
primary key (sched_name,job_name,job_group)
sched_name varchar(120) not null,
job_name varchar(80) not null,
job_group varchar(80) not null,
description varchar(120),
job_class_name varchar(128) not null,
is_durable integer not null,
is_nonconcurrent integer not null,
is_update_data integer not null,
requests_recovery integer not null,
job_data blob(2000),
primary key (sched_name,job_name,job_group)
);
create table ${_prefix}job_triggers(
sched_name varchar(120) not null,
trigger_name varchar(80) not null,
trigger_group varchar(80) not null,
job_name varchar(80) not null,
job_group varchar(80) not null,
description varchar(120),
next_fire_time bigint,
prev_fire_time bigint,
priority integer,
trigger_state varchar(16) not null,
trigger_type varchar(8) not null,
start_time bigint not null,
end_time bigint,
calendar_name varchar(80),
misfire_instr smallint,
job_data blob(2000),
primary key (sched_name,trigger_name,trigger_group),
foreign key (sched_name,job_name,job_group) references ${_prefix}job_job_details(sched_name,job_name,job_group)
sched_name varchar(120) not null,
trigger_name varchar(80) not null,
trigger_group varchar(80) not null,
job_name varchar(80) not null,
job_group varchar(80) not null,
description varchar(120),
next_fire_time bigint,
prev_fire_time bigint,
priority integer,
trigger_state varchar(16) not null,
trigger_type varchar(8) not null,
start_time bigint not null,
end_time bigint,
calendar_name varchar(80),
misfire_instr smallint,
job_data blob(2000),
primary key (sched_name,trigger_name,trigger_group),
foreign key (sched_name,job_name,job_group) references ${_prefix}job_job_details(sched_name,job_name,job_group)
);
create table ${_prefix}job_simple_triggers(
sched_name varchar(120) not null,
trigger_name varchar(80) not null,
trigger_group varchar(80) not null,
repeat_count bigint not null,
repeat_interval bigint not null,
times_triggered bigint not null,
primary key (sched_name,trigger_name,trigger_group),
foreign key (sched_name,trigger_name,trigger_group) references ${_prefix}job_triggers(sched_name,trigger_name,trigger_group)
sched_name varchar(120) not null,
trigger_name varchar(80) not null,
trigger_group varchar(80) not null,
repeat_count bigint not null,
repeat_interval bigint not null,
times_triggered bigint not null,
primary key (sched_name,trigger_name,trigger_group),
foreign key (sched_name,trigger_name,trigger_group) references ${_prefix}job_triggers(sched_name,trigger_name,trigger_group)
);
create table ${_prefix}job_cron_triggers(
sched_name varchar(120) not null,
trigger_name varchar(80) not null,
trigger_group varchar(80) not null,
cron_expression varchar(120) not null,
time_zone_id varchar(80),
primary key (sched_name,trigger_name,trigger_group),
foreign key (sched_name,trigger_name,trigger_group) references ${_prefix}job_triggers(sched_name,trigger_name,trigger_group)
sched_name varchar(120) not null,
trigger_name varchar(80) not null,
trigger_group varchar(80) not null,
cron_expression varchar(120) not null,
time_zone_id varchar(80),
primary key (sched_name,trigger_name,trigger_group),
foreign key (sched_name,trigger_name,trigger_group) references ${_prefix}job_triggers(sched_name,trigger_name,trigger_group)
);
CREATE TABLE ${_prefix}job_simprop_triggers
(
CREATE TABLE ${_prefix}job_simprop_triggers(
sched_name varchar(120) not null,
TRIGGER_NAME VARCHAR(200) NOT NULL,
TRIGGER_GROUP VARCHAR(200) NOT NULL,
@@ -78,54 +77,54 @@ CREATE TABLE ${_prefix}job_simprop_triggers
);
create table ${_prefix}job_blob_triggers(
sched_name varchar(120) not null,
trigger_name varchar(80) not null,
trigger_group varchar(80) not null,
blob_data blob(2000),
primary key (sched_name,trigger_name,trigger_group),
foreign key (sched_name,trigger_name,trigger_group) references ${_prefix}job_triggers(sched_name,trigger_name,trigger_group)
sched_name varchar(120) not null,
trigger_name varchar(80) not null,
trigger_group varchar(80) not null,
blob_data blob(2000),
primary key (sched_name,trigger_name,trigger_group),
foreign key (sched_name,trigger_name,trigger_group) references ${_prefix}job_triggers(sched_name,trigger_name,trigger_group)
);
create table ${_prefix}job_calendars(
sched_name varchar(120) not null,
calendar_name varchar(80) not null,
calendar blob(2000) not null,
primary key (calendar_name)
sched_name varchar(120) not null,
calendar_name varchar(80) not null,
calendar blob(2000) not null,
primary key (calendar_name)
);
create table ${_prefix}job_fired_triggers(
sched_name varchar(120) not null,
entry_id varchar(95) not null,
trigger_name varchar(80) not null,
trigger_group varchar(80) not null,
instance_name varchar(80) not null,
fired_time bigint not null,
sched_time bigint not null,
priority integer not null,
state varchar(16) not null,
job_name varchar(80),
job_group varchar(80),
is_nonconcurrent integer,
requests_recovery integer,
primary key (sched_name,entry_id)
sched_name varchar(120) not null,
entry_id varchar(95) not null,
trigger_name varchar(80) not null,
trigger_group varchar(80) not null,
instance_name varchar(80) not null,
fired_time bigint not null,
sched_time bigint not null,
priority integer not null,
state varchar(16) not null,
job_name varchar(80),
job_group varchar(80),
is_nonconcurrent integer,
requests_recovery integer,
primary key (sched_name,entry_id)
);
create table ${_prefix}job_paused_trigger_grps(
sched_name varchar(120) not null,
trigger_group varchar(80) not null,
primary key (sched_name,trigger_group)
sched_name varchar(120) not null,
trigger_group varchar(80) not null,
primary key (sched_name,trigger_group)
);
create table ${_prefix}job_scheduler_state(
sched_name varchar(120) not null,
instance_name varchar(80) not null,
last_checkin_time bigint not null,
checkin_interval bigint not null,
primary key (sched_name,instance_name)
sched_name varchar(120) not null,
instance_name varchar(80) not null,
last_checkin_time bigint not null,
checkin_interval bigint not null,
primary key (sched_name,instance_name)
);
create table ${_prefix}job_locks(
sched_name varchar(120) not null,
lock_name varchar(40) not null,
primary key (sched_name,lock_name)
sched_name varchar(120) not null,
lock_name varchar(40) not null,
primary key (sched_name,lock_name)
);

View File

@@ -476,6 +476,8 @@ CREATE TABLE ${_prefix}sys_module
main_class_name varchar(500),
current_version varchar(50),
upgrade_info varchar(300),
gen_base_dir varchar(1000),
tpl_category varchar(200),
status char(1) DEFAULT '0' NOT NULL,
create_by varchar(64) NOT NULL,
create_date datetime NOT NULL,

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