commit
cdfb3f0132
2540 changed files with 1398158 additions and 0 deletions
@ -0,0 +1,2 @@ |
|||||
|
/log/ |
||||
|
/target/ |
||||
@ -0,0 +1,25 @@ |
|||||
|
1:命令行进入项目根目录,然后运行 mvn clean package 即可打包 |
||||
|
|
||||
|
2:打包完后,进入 jfinal-club/target/jfinal-club-release/jfinal-club |
||||
|
目录,windows 下双击 start.bat 启动项目, linux 下运行 start.sh 脚本启动项目, |
||||
|
运行 stop.sh 关闭项目 |
||||
|
|
||||
|
注意 jfinal-club/target 目录下面还会有一个 jfinal-club-release.zip 文件 |
||||
|
该文件等价于对 /target/jfinal-club-release/jfinal-club 目录进行的压缩 |
||||
|
方便上传到服务器上解压即部署,可通过删除 package.xml 中的 <format>zip</format> |
||||
|
项避免打出该 zip 包,具体用法在 package.xml 中有说明 |
||||
|
|
||||
|
3:start.sh 脚本中提供了详细的说明,根据说明可选择不同的运行模式 |
||||
|
|
||||
|
|
||||
|
说明:打包部署只需在 pom.xml 中配置 maven-assembly-plugin 这一个插件,该插件用到了 |
||||
|
package.xml 中的配置,整个过程已被简化到极致 |
||||
|
|
||||
|
start.sh、stop.sh、restart.sh、start.bat 这四个运行脚本在用于别的项目时,只需要修改一下 |
||||
|
MAIN_CLASS 变量指向实际的启动入口类即可,例如在 jfinal-club 中的配置如下: |
||||
|
MAIN_CLASS=com.jfinal.club.common.JFinalClubConfig |
||||
|
|
||||
|
|
||||
|
俱乐部 2018-12-16 的直播视频中详细演示了 jfinal club 的打包与部署过程,还演示了 fatjar 的打包, |
||||
|
以及 HTTPS/SSL 的使用方法,有需要的同学可以在俱乐部 QQ 群通知里面找到下载链接 |
||||
|
|
||||
@ -0,0 +1,439 @@ |
|||||
|
jfinal club 4.9.08 changes: |
||||
|
1:jfinal 升级到 4.9.08 |
||||
|
|
||||
|
2:jfinal undertow 升级到 2.5 |
||||
|
|
||||
|
------------------------------------------ |
||||
|
|
||||
|
jfinal club 4.9.06 changes: |
||||
|
1:jfinal 升级到 4.9.06 |
||||
|
|
||||
|
2:jfinal undertow 升级到 2.4 |
||||
|
|
||||
|
3:commons-email 升级到 1.5 |
||||
|
|
||||
|
4:JFinalClubConfig 中开启路由扫描功能 |
||||
|
|
||||
|
5:删除 FrontRoutes、AdminRoutes,路由注册已被路由扫描功能所取代 |
||||
|
|
||||
|
6:EmailKit 改为走 ssl 通道 465 端口,阿里云、腾迅云、华为云都屏蔽了 25 端口 |
||||
|
|
||||
|
------------------------------------------ |
||||
|
|
||||
|
jfinal club 4.9.02 changes: |
||||
|
1:jfinal 升级到 4.9.02 |
||||
|
|
||||
|
2:jfinal undertow 升级到 2.2 |
||||
|
|
||||
|
3:fastjson 升级到 1.2.73 |
||||
|
|
||||
|
4:JsoupFilter 加强对于 CSRF 攻击的防备 |
||||
|
|
||||
|
5:删掉 TimeKit,该类已被添加到 jfinal 4.9.02 中 |
||||
|
|
||||
|
6:LoginService 删掉 loginAccount.put("sessionId", sessionId) |
||||
|
|
||||
|
7:AccountAdminService 限定对超级管理员的操作 |
||||
|
|
||||
|
------------------------------------------ |
||||
|
|
||||
|
jfinal club 4.9.01 changes: |
||||
|
1:jfinal 升级到最新版本 4.9.01 |
||||
|
|
||||
|
2:fastjson 升到 1.2.72 |
||||
|
|
||||
|
3:sql 模板文件与 java 源代码放在相同目录,需在 pom.xml 中添加 <resources> 配置 |
||||
|
|
||||
|
4:由于 sql 模板将被打入 jar 包,所以 _all_sqls.sql 中的 #include 改为绝对路径 |
||||
|
|
||||
|
5:TimeKit.parseDate(...) 方法改为 parse(...),因为这个更常使用且对 dataPattern 兼容更好, |
||||
|
原方法名改为 parseLocalDateTime(...) |
||||
|
|
||||
|
6:删除 MyRedirectRender、MyRedirect301Render、MyRenderFactory,jfinal 4.9.01 已不再需要这个扩展 |
||||
|
|
||||
|
7:删除 JFinalClubConfig 中的 me.setRenderFactory(new MyRenderFactory()) 配置,原因同上 |
||||
|
|
||||
|
------------------------------------------ |
||||
|
|
||||
|
jfinal club 4.9 相对 4.8 的变化: |
||||
|
1:jfinal 升级到最新版本 4.9 |
||||
|
|
||||
|
2:jfinal undertow 升级到最新版本 2.1 |
||||
|
|
||||
|
3:文件上传组件 cos 升级到最新版本 2020.4 |
||||
|
|
||||
|
4:优化 css 样式 |
||||
|
|
||||
|
5:新增 TimeKit 工具类 |
||||
|
|
||||
|
6:新增 MyRenderFactory、MyRedirectRender、MyRedirect301Render 用于解决 |
||||
|
nginx 代理 https 时重定向到了 http 的问题,需要在 nginx 中添加如下配置: |
||||
|
proxy_set_header X-Forwarded-Proto $scheme; |
||||
|
proxy_set_header X-Forwarded-Port $server_port; |
||||
|
|
||||
|
|
||||
|
7:fastjson 升到 1.2.70 |
||||
|
|
||||
|
------------------------------------------ |
||||
|
|
||||
|
|
||||
|
jfinal club 4.8 相对 4.2 的变化: |
||||
|
1:jfinal 升级到最新版本 4.8 |
||||
|
|
||||
|
2:jfinal undertow 升级到最新版本 2.0 |
||||
|
|
||||
|
3:文件上传组件 cos 升级到最新版本 2019.8 |
||||
|
|
||||
|
4:log4j 升级到 1.2.17 |
||||
|
|
||||
|
5:首页添加轮播广告组件 |
||||
|
|
||||
|
6:package.xml 中 fileSet 标签添加 lineEnding 子标签,解决不同平台下脚本复制换行的问题 |
||||
|
|
||||
|
7:分页向前、向后按钮改为大于小于字符 |
||||
|
|
||||
|
8:MyFeedbackService、MyProjectSevice、MyShareService 的 update() 方法添加 |
||||
|
remove("createAt"),避免 mass assignment 修改 createAt 值 |
||||
|
|
||||
|
9:fatjar 打包的 pom.xml 中的 shade 插件添加 filter,过滤掉 *.SF、*.DSA、*.RAS |
||||
|
|
||||
|
------------------------------------------ |
||||
|
|
||||
|
jfinal club 4.2 相对 4.1 的变化: |
||||
|
1:jfinal 升级到 4.2 |
||||
|
|
||||
|
2:根据用户对 jfinal.com UI 的反馈,改进 html、css、js 细节,提升用户体验 |
||||
|
|
||||
|
3:首页、分享、反馈、动态列表中的 h1 改为 div, 否则在浏览器渲染的时候,如果电脑当时很慢 |
||||
|
会首先显示很大的 h1 字体,然后才变成小字体 |
||||
|
|
||||
|
|
||||
|
------------------------------------------ |
||||
|
|
||||
|
|
||||
|
jfinal club 4.1 相对 4.0 的变化: |
||||
|
1:jfinal 升级到 4.1 |
||||
|
|
||||
|
2:大量调整 html、css、js 提升用户体验,例如:文档页面左侧菜单及脚本被大幅调整 |
||||
|
|
||||
|
3:所有 findByCache、findFirst、paginateByCache 由 getSqlPara 模式 |
||||
|
改为 template(...) 模板模式 |
||||
|
|
||||
|
4:JsoupFilter 过滤 a 标签 href 中的 javascript 脚本,加强 XSS 防范 |
||||
|
|
||||
|
5:JsoupFilter 开启 html 5 的 video 标签,方便在文章中插入视频 |
||||
|
|
||||
|
6:Project、Share、Feedback 在 configEngine 中添加 me.addSharedObject(...), |
||||
|
便于在页面使用 field 表达式代替 static field 表达式,例如: |
||||
|
Project.REPORT_BLOCK_NUM |
||||
|
可改成下面的形式,进一步节省代码量 |
||||
|
com.jfinal.club.common.model.Project::REPORT_BLOCK_NUM |
||||
|
|
||||
|
---> 注意:Model 的 field 表达式读取字段的功能,最低 jfinal 4.0 版本才开始支持 |
||||
|
|
||||
|
7:后台 html 页面中 Project、Share、Feedback 的 static field 表达式改为 field 表达式 |
||||
|
|
||||
|
|
||||
|
------------------------------------------ |
||||
|
|
||||
|
|
||||
|
jfinal club 4.0 相对 2.0 的变化: |
||||
|
1:升级到 jfinal 4.0,去除 cglib/asm 依赖 |
||||
|
|
||||
|
2:前台 UI 全部替换,改进文档频道用户体验 |
||||
|
|
||||
|
3:控制层与业务层的依赖全部改成 @Inject |
||||
|
|
||||
|
4:Validator 中改用 setRet(..) 与 render(getRet()),使得 js 中的代码统一,减少大量代码 |
||||
|
|
||||
|
5:优化部分 sql |
||||
|
|
||||
|
6:AdminAuthService、PorjectService、ShareService、FeedbackService 中使用 jfinal 4.0 |
||||
|
新提供的 template(...) 方法,节省大量代码,例如: |
||||
|
Db.template("admin.auth.isSuperAdmin", accountId).queryInt(); |
||||
|
|
||||
|
|
||||
|
------------------------------------------ |
||||
|
|
||||
|
|
||||
|
jfinal club 2.0 相对 1.9 的变化: |
||||
|
1:jfinal 升级到 3.8 |
||||
|
|
||||
|
2:jfinal undertow 升级到 1.6 |
||||
|
|
||||
|
3:升级前端 UI (TODO) |
||||
|
|
||||
|
4:configConstant(...) 添加配置 me.setInjectSuperClass(true),支持超类依赖注入 |
||||
|
|
||||
|
5:生成器添加配置 gen.setGenerateRemarks(true) 为 base model 生成备注 |
||||
|
|
||||
|
6:all_sqls.sql 改名为 _all_sqls.sql,修改 JFinalClubConfig 相应配置 |
||||
|
|
||||
|
7:start.sh、stop.sh、restart.sh 合并为 jfinal.sh,添加 stop.bat |
||||
|
|
||||
|
8:JFinalClubConfig 中的 |
||||
|
PropKit.use(dev.txt).appendIfExists(pro.txt) |
||||
|
改为 |
||||
|
PropKit.useFirstFound(pro.txt, dev.txt); |
||||
|
也即由生产环境配置追加模式改为生产环境配置整体代替模式 |
||||
|
|
||||
|
|
||||
|
------------------------------------------ |
||||
|
|
||||
|
jfinal club 1.9 相对 1.8 的变化: |
||||
|
|
||||
|
0:jfinal 升级到 3.6,避免客户端中途断开连接时抛出异常,日志文件会很干净。还包括很多其它改进与优化 |
||||
|
|
||||
|
1:jfinal undertow 升级到 1.5,解决 session 热加载问题 |
||||
|
|
||||
|
2:DocumentAdminService 、DocumentAdminController 的 findById 改成 findByIds |
||||
|
|
||||
|
3:MessageController.friend() 添加 null 值判断并 renderError(404) |
||||
|
|
||||
|
4:log4j.properties 日志文件存放添加 log 子目录,保持项目根路径干净清爽 |
||||
|
|
||||
|
5:JFinalClubConfig 中的 afterJFinalStart() 改名为 onStart() |
||||
|
|
||||
|
6:stop.sh 三处 beforeJFinalStop() 改为 onStop()。添加 kill -9 有关注释 |
||||
|
|
||||
|
7:AccountService.getUsefulById(...) 添加 null 值判断 |
||||
|
|
||||
|
8:fastjson 升级到 1.2.55,支持 Page 反序列化(需要 jfinal-3.6 支持) |
||||
|
|
||||
|
9:_Generator 中添加 setGenerateRemarks(false) 配置 |
||||
|
|
||||
|
10:添加配置:undertow.resourcePath=src/main/webapp, classpath:webapp |
||||
|
|
||||
|
11:fatjar 打包配置文档修改主两处错误 |
||||
|
|
||||
|
|
||||
|
------------------------------------------ |
||||
|
|
||||
|
jfinal club 1.8 相对 1.7 的变化: |
||||
|
|
||||
|
0:jfinal-undertow 升级到 1.3 |
||||
|
|
||||
|
1:pom.xml 中添加 maven-jar-plugin 插件,避免 src/main/resources 下的配置文件打入 jar 包 |
||||
|
好让 config 目录下配置文件生效,否则 config 中与 jar 包内的同名配置文件将不会生效 |
||||
|
|
||||
|
2:package.xml 中过滤掉对 web.xml、WEB-INF 的复制,jfinal-undertow 不需要 web.xml |
||||
|
|
||||
|
3:更新 undertow-config-demo.txt,添加 jfinal-undertow-1.3 新增功能配置 |
||||
|
|
||||
|
4:添加 restart.sh 重启脚本 |
||||
|
|
||||
|
5:改进 ImageKit,解决 png 转 jpg 背景颜色改变的问题,感谢 @步步(讯) 的贡献 |
||||
|
|
||||
|
6:jfinal-club-1.8.sql 中的 role、permission、account_role、role_permission |
||||
|
表的 charset 由 utf8mb4 改成 utf8,解决老版本 mysql 无法导入问题 |
||||
|
|
||||
|
------------------------------------------ |
||||
|
|
||||
|
jfinal club 1.7 相对 1.6 的变化: |
||||
|
|
||||
|
0:添加 "doc/___极速打包部署.txt" 说明文件 |
||||
|
|
||||
|
1:pom.xml 中删除 jetty-server 依赖 |
||||
|
|
||||
|
2:pom.xml 中添加 jfinal-undertow 依赖,从 jetty 切换到 undertow |
||||
|
|
||||
|
3:pom.xml 中添加 maven-assembly-plugin 打包插件,根目录下面添加 package.xml 打包配置 |
||||
|
|
||||
|
4:根目录下面添加 start.sh、stop.sh、start.bat 启动脚本 |
||||
|
|
||||
|
5:JFinalClubConfig 中 JFinal.start(...) 改为 UndertowServer.start(...) |
||||
|
|
||||
|
6:配置 文件 jfinal_club_config_dev.txt 的下划线改为减号 |
||||
|
|
||||
|
7:添加 undertow.txt 配置文件 |
||||
|
|
||||
|
8:修改 jfinal-club启动必读.txt,去除与 jetty 有关的一切内容 |
||||
|
|
||||
|
总述:jfinal-club 1.7 项目自身未做任何改动,所有变化围绕从 jetty-server 切换到 |
||||
|
jfinal-undertow |
||||
|
|
||||
|
jfinal-undertow 不仅启动极为迅速,热加载响应十分快捷,更重要是开发、打包、部署、运行 |
||||
|
完全不用对项目或者配置有任何改动,从而实现从开发到部署的极致快捷 |
||||
|
|
||||
|
------------------------------------------ |
||||
|
|
||||
|
jfinal club 1.6 相对 1.5 版本的主要变化: |
||||
|
|
||||
|
1:jfinal 升级到 3.5,pom.xml 中的 JDK 编译级别升为 1.8 |
||||
|
|
||||
|
2:jetty-server 升级到 jetty-server-2018.11,该版本的 JDK 最低要求为 1.8 |
||||
|
|
||||
|
3:JFinalClubConfig 中配置 me.setInjectDependency(true) 启用最新的 Aop |
||||
|
模块功能,免去业务层 AOP 必须手动 enhance 的步骤,免去业务层维护单例的样板式 |
||||
|
代码,例如: |
||||
|
public static AccountService me = new AccountService(); |
||||
|
|
||||
|
注意:为了节省改动时间,现只先修改了后台管理模块的 AOP 使用方式。 |
||||
|
此外,对于工具类的业务使用原有的 public static Xxx me 模式 |
||||
|
比 @Inject 更加方便,同理,对于全局多处依赖的业务也如此, |
||||
|
例如:AccountService |
||||
|
|
||||
|
4:添加文档管理模块,方便同学们使用 jfinal club 二次开发属于自己的带文档的社区 |
||||
|
|
||||
|
5:添加 @Remark 注解用于一键同步时向 permission 表的 remark 字段添加内容(该字段有内容时跳过) |
||||
|
详细用法请见 DocumentAdminController.java |
||||
|
|
||||
|
6:账户管理添加 "查看后台账户/管理员" 功能,便于查看后台都有哪些账户被分配了角色,在对账户误操作分配了角色时, |
||||
|
也便于取消角色分配 |
||||
|
|
||||
|
7:模板文件 "/_view/admin/common/_menu.html" 之中添加了 #permission 指令 "细粒度" |
||||
|
控制菜单的示例,以及 #role 指令 "细粒度" 控制菜单的示例。缘于很多同学在问如何 "细粒度" |
||||
|
控制菜单权限的问题,jfinal club 细粒度控制菜单不需要添加后台代码,只需要事先在需要控制 |
||||
|
的菜单那里用 #permission 与 #role 指令事先埋好点即可,权限控制的行为与后端完全一致, |
||||
|
前后端权限使用同一套逻辑,避免发生前后权限逻辑的死角、遗漏、不一致性 |
||||
|
|
||||
|
8:pom.xml 中添加 jetty-maven-plugin 插件,方便习惯于使用该插件的人启动并开发项目,启动项目只需 |
||||
|
在控制台输入: mvn jetty:run |
||||
|
这种启动开发项目的方式也支持热加载,使用该启动方式可以去掉对 jetty-server 项目的依赖。 |
||||
|
相对于 jfinal 官方提供的启动方式控制台输出信息较多较杂,启动速度慢,绝大多数情况建议 |
||||
|
使用 jfinal 官方的启动方式 |
||||
|
|
||||
|
9:pom.xml 中添加 slf4j-nop 依赖避免在开发阶段启动项目时输出的 INFO 干扰信息,控制台从此特别干净清爽 |
||||
|
|
||||
|
|
||||
|
10:reply.html 页面的 title、content 的输出使用 #escape() 指令,避免出现页面格式混乱 |
||||
|
|
||||
|
11:左侧首页菜单图标改为 fa-home,美观且符合度更高 |
||||
|
|
||||
|
12:BaseController 中添加 _clear_() |
||||
|
|
||||
|
13:BaseController 中的 @Before(NotAction.class) 改用 @NotAction |
||||
|
|
||||
|
14:启动方法统一使用 JFinal.start("src/main/webapp", 80, "/", 5),jfinal 3.5 已支持 IDEA |
||||
|
|
||||
|
15:删掉 MyClassPathSourceFactory、MyClassPathSource,jfinal 3.5 已改进过 ClassPathSource |
||||
|
|
||||
|
16:删除 com.jfinal.club.common.aop 包,改为使用最新版本 jfinal 3.5 的 aop |
||||
|
|
||||
|
17:jdbcUrl 配置中添加 useSSL=false,避免使用高版本 mysql 时,控制台输出干扰开发的无用 INFO 信息 |
||||
|
|
||||
|
18:删除 JFinalClubConfig 中用于 IDEA 的启动代码以及相关所有注释说明,jfinal 3.5 已支持 IDEA 以及 |
||||
|
所有 eclipse 版本 |
||||
|
|
||||
|
19:pom.xml 中的 maven-compiler-plugin 插件参数添加 -parameters 配置,便于二次开发中使用 action 带参 |
||||
|
eclipse 相关编译配置见文档:https://jfinal.com/doc/3-3 |
||||
|
|
||||
|
--------------------------------------------------- |
||||
|
|
||||
|
jfinal club 1.5 相对 1.4 版本的主要变化: |
||||
|
|
||||
|
1:项目管理、分享管理、反馈管理添加创建功能 |
||||
|
|
||||
|
2:分享管理、反馈管理模块添加回复管理功能 |
||||
|
|
||||
|
3:账户管理添加更换头像功能 |
||||
|
|
||||
|
4:权限管理的权限列表识别不存在的 action,并给出操作提示 |
||||
|
|
||||
|
5:授权管理添加 AdminAuthKit,支持 #if(hasPermission(...) ... #else ... #end 以及 |
||||
|
#if(hasRole(...) ... #else ... #end |
||||
|
|
||||
|
6:改进 ImageKit,移除对于 com.sun 包下的图片处理 API 依赖,更好支持 jdk 7/8 |
||||
|
|
||||
|
7:将 Switchery 组件换成 magic input 组件 |
||||
|
a:在 __admin_layout.html 中引入 magic input 的 css 文件,删除 Switchery 的 css 与 js 引用 |
||||
|
b:升级 jfinal-admin.js 中相关的 init 方法 |
||||
|
c:将 checkbox 组件的 class="js-switch" 样式改为 class="mgc-switch mgc-tiny" |
||||
|
并将 initSwitchery() 方法名改为 initMagicInput(),并去掉第二个参数 |
||||
|
|
||||
|
8:修改页面提交以后返回到当前记录所在的页,老版本总是返回到第一页 |
||||
|
|
||||
|
9:RoleDirective.hasRole(...) PermissionDirective.hasPermission(...) 重构至 AdminAuthService |
||||
|
|
||||
|
10:RoleDirective、PermissionDirective 中获取 loginAccount 的方式改为从 scope 中获取 |
||||
|
|
||||
|
11:重构 PermissionDirective、RoleDirective,代码更简洁 |
||||
|
|
||||
|
12:删掉 admin_role.sql 与 admin_permission.sql ,其中的 sql 转至 admin_auth.sql |
||||
|
|
||||
|
13:删掉 all_sqls.sql 中对 admin_role.sql 与 admin_permission.sql 的 #include 包含 |
||||
|
|
||||
|
14:角色管理的 add、edit、_form 三个页面合并为 add_edit.html |
||||
|
|
||||
|
15:UploadController 中的 renderJson(ret) 改为 render(new JsonRender(ret).forIE()) |
||||
|
防止 ueditor 图片上传功能在 IE 下出现文件下载现象 |
||||
|
|
||||
|
16:$.pjax.defaults.timeout = 1500; 改为 5000 |
||||
|
|
||||
|
17:后台管理菜单颜色的 #ffffffb3 改为 hsla(0,0%,100%,.7),支持老版本 IE |
||||
|
|
||||
|
18:其它打磨 |
||||
|
|
||||
|
升级建议:由于 jfinal club 1.5 打磨的地方比较多、比较细、也很重要,升级建议以 jfinal club 1.5 为基础, |
||||
|
将已经基于 jfinal club 1.4 做的项目中的自己写的代码重新整合到 jfinal club 1.5 中去。 |
||||
|
二次开发产生的 js、css 代码要写到新建的文件中去,然后在 layout 中引用,这样有利于将来升级 |
||||
|
|
||||
|
此外,permission 表中的内容有所增加,升级完以后需要进入权限管理进行"一键同步",最后按个人需求 |
||||
|
为角色分配相应的权限即可 |
||||
|
|
||||
|
--------------------------------------------------- |
||||
|
|
||||
|
|
||||
|
jfinal club 1.4 相对 1.3 版本的变化: |
||||
|
|
||||
|
1:升级到 jfinal 3.4 |
||||
|
|
||||
|
2:后台管理功能全部重写,添加内容管理、账户管理、角色管理、权限管理等功能 |
||||
|
|
||||
|
3:后台管理界面全部重写,手写骨架,右下角内容区域使用 bootstrap 3,方便进行二次开发 |
||||
|
二次开发基本只需要照猫画虎,添加右下角内容区域代码 |
||||
|
|
||||
|
4:添加 #role #permission 指令,用于在界面控制权限 |
||||
|
|
||||
|
5:其它一些细节变化将在直播中讲解,会有更深刻的了解 |
||||
|
|
||||
|
|
||||
|
注意:后台管理功能看似简单,其实里头很多细节,采用极简设计,尽可能消除学习成本和二次开发成本 |
||||
|
后台管理源代码与功能将会在直播中详细讲解 |
||||
|
|
||||
|
|
||||
|
--------------------------------------------------- |
||||
|
|
||||
|
|
||||
|
jfinal club 1.3 相对 1.2 版本的变化: |
||||
|
|
||||
|
1:升级到 jfinal 3.3 |
||||
|
|
||||
|
2:JFinalClubConfig 中加载配置文件由 try catch 改为使用 PropKit.appendIfExists(...) |
||||
|
|
||||
|
3:ShareController、FeedbackController、MessageController、NewsFeedController |
||||
|
中的 renderToString(...) 的模板去掉路径 |
||||
|
|
||||
|
4:sql 管理功能使用 ClassPathSourceFactory 来做,从类路径里头加载 |
||||
|
|
||||
|
5:mysql 驱动升级到 5.1.44 版本 |
||||
|
|
||||
|
注意:jf club 1.2 已经处理过 Ret 类的升级,从 1.2 升级到 1.3 只需要关注上述五条 |
||||
|
doc 目录下面的三个 png 图片文件,仅用于指导更老的版本升级到 1.2 或 1.3 |
||||
|
|
||||
|
--------------------------------------------------- |
||||
|
|
||||
|
|
||||
|
jfinal club 1.2 相对 1.1 版本的变化: |
||||
|
1:jfinal 升级到 3.2 版本 |
||||
|
2:文件上传组件 cos 升级到 2017.5 版本 |
||||
|
3:UploadController 捕获 ExceededSizeException 异常,更好处理上传文件长度超出范围 |
||||
|
4:"/upload" 路由改为 "/common/upload",修改 ueditor 相关配置指向新路由。此改进主要为了 nginx 配置方便 |
||||
|
避免与 "webapp/upload" 的配置产生影响,减少 nginx 配置量 |
||||
|
5:查找替换 html 文件中的 22 处 ret.isOk 为 ret.state == "ok" |
||||
|
6:查找替换 html 文件中的 4 处 ret.isFail 为 ret.state == "fail" |
||||
|
7:查找替换 js 文件中的 11 处 ret.isOk 为 ret.state == "ok" |
||||
|
|
||||
|
|
||||
|
建议大家升级自己项目 Ret 的方式为: |
||||
|
1:利用查找替换功能将 html 与 js 中的 ret.isOk 替换为 ret.state == "ok" |
||||
|
2:利用查找替换功能将 html 与 js 中的 ret.isFail 替换为 ret.state == "fail" |
||||
|
|
||||
|
|
||||
|
如果项目未涉及到 Ret 生成的 json 数据则不需要处理,java 代码中的 |
||||
|
ret.isOk() ret.isFail() 行为并无变化,不需要处理 |
||||
|
|
||||
|
|
||||
|
如果觉得上述升级方式比较麻烦,可以在项目启动时调用一次 Ret.setToOldWorkMode() |
||||
|
可以继续沿用老版本的工作模式,这种升级方式比较适合不需要怎么改动的老项目 |
||||
|
|
||||
@ -0,0 +1,57 @@ |
|||||
|
|
||||
|
|
||||
|
1、 public void configEngine(Engine me) 方法中添加如下两行代码 |
||||
|
me.setBaseTemplatePath("webapp"); |
||||
|
me.setToClassPathSourceFactory(); |
||||
|
注意:上面两行配置代码要放在最前面,因为 addSharedFunction(...) |
||||
|
这类配置对上面配置有依赖关系 |
||||
|
|
||||
|
|
||||
|
当你的 webapp 资源目录本来就放在 src/main/resources 之下时,需要 "去掉" pom.xml 中的如下配置: |
||||
|
<resource> |
||||
|
<directory>src/main/webapp</directory> |
||||
|
<targetPath>webapp</targetPath> |
||||
|
</resource> |
||||
|
上述配置将 src/main/webapp 目录整体复制到 target/classes 目录之下,好让其打到 jar 包之中去 |
||||
|
|
||||
|
|
||||
|
所以,如果项目确定当成 fatjar 来开发的话,建议将 src/main/webapp 整体挪到 |
||||
|
src/main/resources 之下。这样就从开发到打包都不用做上述这些配置与额外动作,避免了出错的可能 |
||||
|
|
||||
|
有部分同学习惯于使用 static 代替 webapp,将上述涉及 webapp 的地方改成 static 即可 |
||||
|
|
||||
|
2、在 undertow.txt 配置文件中添加如下配置: |
||||
|
undertow.resourcePath=src/main/webapp, classpath:webapp |
||||
|
|
||||
|
3、将本文件夹下的 pom.xml 替换掉项目中原有的 pom.xml 文件 |
||||
|
|
||||
|
这两个文件的差别在于前者打 fatjar 包用到的是 maven-shade-plugin 这个插件 |
||||
|
而后者用到的是 maven-assembly-plugin 插件 |
||||
|
|
||||
|
4、 打包运行 |
||||
|
mvn clean package |
||||
|
java -jar jfinal-club.jar |
||||
|
|
||||
|
5、 隐藏功能 |
||||
|
在打好的 jar 包的目录中添加 config 目录并添加配置文件可以被项目加载,注意,这里的 |
||||
|
配置文件名与 jar 包中的配置文件名不能相同,可以参考 JFinalClubConfig 中的用法: |
||||
|
PropKit.useFirstFound("jfinal-club-config-pro.txt", "jfinal-club-config-dev.txt"); |
||||
|
也就是说在开发的时候使用 dev 配置,在生产环境手动创建一个 pro 配置,由于该配置文件 |
||||
|
在 jar 包中不存在,所以会被加载 |
||||
|
|
||||
|
在打好的 jar 包目录中添加将项目中的 webapp 复制过来,便于对外部 css、js、html 等资源 |
||||
|
进行修改,更重要的是支持有文件上传功能的 web 项目 |
||||
|
|
||||
|
以上两个隐藏功能,需要在项目启动之前添加好目录与文件。简单来说这两个目录不存在时 |
||||
|
使用 jar 包中的资源,否则就使用它。注意 config 目录中与 jar 包中的同名配置不会被加载 |
||||
|
|
||||
|
6、退出程序 |
||||
|
如果启动时没带与字符 ‘&’ 结尾,则 Ctrl + C 即可退出否则使用下面的方法: |
||||
|
找到程序 pid: ps aux | grep java |
||||
|
杀掉进程: kill pid |
||||
|
|
||||
|
注意使用 kill -9 pid 杀进程时,JFinalConfig.onStop() 不会被回调 |
||||
|
可以参考 "非 fatjar" 项目的运行、停止脚本来写一个用于 fatjar 的脚本 |
||||
|
|
||||
|
小结:以上配置并不是仅仅适用于打包部署,开发阶段也使用这些配置,不用改来改去 |
||||
|
|
||||
@ -0,0 +1,278 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> |
||||
|
<modelVersion>4.0.0</modelVersion> |
||||
|
|
||||
|
|
||||
|
<groupId>com.jfinal</groupId> |
||||
|
<artifactId>jfinal-club</artifactId> |
||||
|
<version>4.9.08</version> |
||||
|
<packaging>jar</packaging> |
||||
|
|
||||
|
|
||||
|
<name>jfinal-club</name> |
||||
|
<url>https://jfinal.com/club</url> |
||||
|
|
||||
|
<properties> |
||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
||||
|
<maven.compiler.encoding>UTF-8</maven.compiler.encoding> |
||||
|
</properties> |
||||
|
|
||||
|
<!-- 使用阿里 maven 库 --> |
||||
|
<repositories> |
||||
|
<repository> |
||||
|
<id>ali-maven</id> |
||||
|
<url>http://maven.aliyun.com/nexus/content/groups/public</url> |
||||
|
<releases> |
||||
|
<enabled>true</enabled> |
||||
|
</releases> |
||||
|
<snapshots> |
||||
|
<enabled>true</enabled> |
||||
|
<updatePolicy>always</updatePolicy> |
||||
|
<checksumPolicy>fail</checksumPolicy> |
||||
|
</snapshots> |
||||
|
</repository> |
||||
|
</repositories> |
||||
|
|
||||
|
<dependencies> |
||||
|
|
||||
|
<!-- jfinal --> |
||||
|
<dependency> |
||||
|
<groupId>com.jfinal</groupId> |
||||
|
<artifactId>jfinal</artifactId> |
||||
|
<version>4.9.08</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- jfinal-undertow 开发、部署一体化 web 服务器 --> |
||||
|
<dependency> |
||||
|
<groupId>com.jfinal</groupId> |
||||
|
<artifactId>jfinal-undertow</artifactId> |
||||
|
<version>2.5</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- cos 文件上传 --> |
||||
|
<dependency> |
||||
|
<groupId>com.jfinal</groupId> |
||||
|
<artifactId>cos</artifactId> |
||||
|
<version>2020.4</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- fastjson json 转换 --> |
||||
|
<dependency> |
||||
|
<groupId>com.alibaba</groupId> |
||||
|
<artifactId>fastjson</artifactId> |
||||
|
<version>1.2.73</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- 开发 WebSockets 时开启下面的依赖 --> |
||||
|
<!-- |
||||
|
<dependency> |
||||
|
<groupId>io.undertow</groupId> |
||||
|
<artifactId>undertow-websockets-jsr</artifactId> |
||||
|
<version>2.0.32.Final</version> |
||||
|
</dependency> |
||||
|
--> |
||||
|
|
||||
|
<!-- junit 单元测试 --> |
||||
|
<dependency> |
||||
|
<groupId>junit</groupId> |
||||
|
<artifactId>junit</artifactId> |
||||
|
<version>3.8.1</version> |
||||
|
<scope>test</scope> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- 避免控制台输出如下提示信息: |
||||
|
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". |
||||
|
项目中实际上用不到这个 jar 包 |
||||
|
注意:eclipse 下可以将 scope 设置为 provided |
||||
|
--> |
||||
|
<dependency> |
||||
|
<groupId>org.slf4j</groupId> |
||||
|
<artifactId>slf4j-nop</artifactId> |
||||
|
<version>1.7.29</version> |
||||
|
<!-- 打包前改成 provided,此处使用 compile 仅为支持 IDEA --> |
||||
|
<scope>compile</scope> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- druid 数据源连接池 --> |
||||
|
<dependency> |
||||
|
<groupId>com.alibaba</groupId> |
||||
|
<artifactId>druid</artifactId> |
||||
|
<version>1.0.29</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- log4j 日志 --> |
||||
|
<dependency> |
||||
|
<groupId>log4j</groupId> |
||||
|
<artifactId>log4j</artifactId> |
||||
|
<version>1.2.17</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- mysql 驱动 --> |
||||
|
<dependency> |
||||
|
<groupId>mysql</groupId> |
||||
|
<artifactId>mysql-connector-java</artifactId> |
||||
|
<version>5.1.44</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- ehcache 缓存 --> |
||||
|
<dependency> |
||||
|
<groupId>net.sf.ehcache</groupId> |
||||
|
<artifactId>ehcache-core</artifactId> |
||||
|
<version>2.6.11</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- 邮件发送 --> |
||||
|
<dependency> |
||||
|
<groupId>org.apache.commons</groupId> |
||||
|
<artifactId>commons-email</artifactId> |
||||
|
<version>1.5</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- jsoup 过滤恶意数据提交 --> |
||||
|
<dependency> |
||||
|
<groupId>org.jsoup</groupId> |
||||
|
<artifactId>jsoup</artifactId> |
||||
|
<version>1.9.2</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- joda-time 日期、时间变换 --> |
||||
|
<dependency> |
||||
|
<groupId>joda-time</groupId> |
||||
|
<artifactId>joda-time</artifactId> |
||||
|
<version>2.9.3</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- cron4j 任务调度 --> |
||||
|
<dependency> |
||||
|
<groupId>it.sauronsoftware.cron4j</groupId> |
||||
|
<artifactId>cron4j</artifactId> |
||||
|
<version>2.2.5</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- zxing 二维码生成 --> |
||||
|
<dependency> |
||||
|
<groupId>com.google.zxing</groupId> |
||||
|
<artifactId>javase</artifactId> |
||||
|
<version>3.2.1</version> |
||||
|
</dependency> |
||||
|
|
||||
|
</dependencies> |
||||
|
|
||||
|
|
||||
|
<build> |
||||
|
<finalName>jfinal-club</finalName> |
||||
|
|
||||
|
|
||||
|
<!-- |
||||
|
添加 includes 配置后,excludes 默认为所有文件 **/*.*,反之亦然 |
||||
|
该规则在 maven-jar-plugin 等插件中同样适用 |
||||
|
--> |
||||
|
<resources> |
||||
|
<!-- 添加该配置是为了将 .sql 文件打入 jar 包 --> |
||||
|
<resource> |
||||
|
<directory>src/main/java</directory> |
||||
|
<includes> |
||||
|
<!-- **/* 前缀用法,可以匹配所有路径 --> |
||||
|
<include>**/*.sql</include> |
||||
|
<include>**/*.class</include> |
||||
|
</includes> |
||||
|
</resource> |
||||
|
|
||||
|
<!-- |
||||
|
没有添加 resources 配置时,src/main/resources 目录是默认配置 |
||||
|
一旦添加 resources 配置指向 src/main/java 目录时,原先的默认配置被取代, |
||||
|
所以需要添加如下配置将默认配置再添加进来,否则无法使用 src/main/resources |
||||
|
下的资源文件 |
||||
|
--> |
||||
|
<resource> |
||||
|
<directory>src/main/resources</directory> |
||||
|
</resource> |
||||
|
|
||||
|
<!-- |
||||
|
将 webapp 也指定为资源文件,并且将之输出到 webapp 目录下 |
||||
|
该配置可以取代 maven-resources-plugin 的功能 |
||||
|
|
||||
|
targetPath: http://maven.apache.org/pom.html#Resources |
||||
|
|
||||
|
注意:如果 web 资源本身就放在 src/main/resources/webapp 之下, |
||||
|
需要删除下面的配置 |
||||
|
--> |
||||
|
<resource> |
||||
|
<directory>src/main/webapp</directory> |
||||
|
<targetPath>webapp</targetPath> |
||||
|
</resource> |
||||
|
</resources> |
||||
|
|
||||
|
|
||||
|
<plugins> |
||||
|
|
||||
|
<plugin> |
||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||
|
<artifactId>maven-compiler-plugin</artifactId> |
||||
|
<version>3.6.1</version> |
||||
|
<configuration> |
||||
|
<source>1.8</source> |
||||
|
<target>1.8</target> |
||||
|
<encoding>UTF-8</encoding> |
||||
|
<!-- java8 保留参数名编译参数 --> |
||||
|
<compilerArgument>-parameters</compilerArgument> |
||||
|
<compilerArguments><verbose /></compilerArguments> |
||||
|
</configuration> |
||||
|
</plugin> |
||||
|
|
||||
|
<plugin> |
||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||
|
<artifactId>maven-shade-plugin</artifactId> |
||||
|
<version>3.2.1</version> |
||||
|
<executions> |
||||
|
<execution> |
||||
|
<phase>package</phase> |
||||
|
<goals> |
||||
|
<goal>shade</goal> |
||||
|
</goals> |
||||
|
<configuration> |
||||
|
<finalName>jfinal-club</finalName> |
||||
|
<transformers> |
||||
|
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> |
||||
|
<mainClass>com.bt.common.JFinalClubConfig</mainClass> |
||||
|
</transformer> |
||||
|
|
||||
|
<!-- |
||||
|
下面的配置支持排除指定文件打包到 jar 之中,可以用于排除需要修改的配置文件以便于在外部的 config 目录下的 |
||||
|
同名配置文件生效,建议使用 Prop.appendIfExists(xxx_pro.txt) 在外部放一个非同名配置来覆盖开发环境的配置 |
||||
|
则可以不用使用下面的配置,文档参考: |
||||
|
http://maven.apache.org/plugins/maven-shade-plugin/examples/resource-transformers.html#DontIncludeResourceTransformer |
||||
|
--> |
||||
|
<transformer implementation="org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer"> |
||||
|
<resources> |
||||
|
<!-- <resource>jfinal-club-config-dev.txt</resource> --> |
||||
|
<!-- <resource>.PDF</resource> --> |
||||
|
<!-- <resource>READ.md</resource> --> |
||||
|
</resources> |
||||
|
</transformer> |
||||
|
</transformers> |
||||
|
|
||||
|
<!-- |
||||
|
解决 fatjar 的 "java.lang.SecurityException: Invalid signature file digest |
||||
|
for Manifest main attributes" 问题 |
||||
|
--> |
||||
|
<filters> |
||||
|
<filter> |
||||
|
<artifact>*:*</artifact> |
||||
|
<excludes> |
||||
|
<exclude>META-INF/*.SF</exclude> |
||||
|
<exclude>META-INF/*.DSA</exclude> |
||||
|
<exclude>META-INF/*.RSA</exclude> |
||||
|
</excludes> |
||||
|
</filter> |
||||
|
</filters> |
||||
|
|
||||
|
</configuration> |
||||
|
</execution> |
||||
|
</executions> |
||||
|
</plugin> |
||||
|
|
||||
|
</plugins> |
||||
|
</build> |
||||
|
</project> |
||||
File diff suppressed because it is too large
@ -0,0 +1,50 @@ |
|||||
|
|
||||
|
超级管理员登录账号:test@test.com 密码:111111 |
||||
|
|
||||
|
另外的几个用于演示账户、角色、权限管理的账号如下: |
||||
|
test2@test.com 密码:111111 |
||||
|
test3@test.com 密码:111111 |
||||
|
test4@test.com 密码:111111 |
||||
|
test5@test.com 密码:111111 |
||||
|
test6@test.com 密码:111111 |
||||
|
test7@test.com 密码:111111 |
||||
|
test8@test.com 密码:111111 |
||||
|
|
||||
|
注意:超级管理员角色在 role 表中 id 值固定为 1,请勿在数据库中修改该值 |
||||
|
所有账号都可以在后台管理界面中修改登录名 |
||||
|
修改密码功能忘了添加,暂时可以在前台的个人中心去操作: |
||||
|
http://localhost/my/setting/password |
||||
|
|
||||
|
|
||||
|
------------------------------------------------------------------------ |
||||
|
|
||||
|
0:启动之前需要先创建名为 jfinal_club 的数据库,字符集使用 utf-8 |
||||
|
导入 jfinal-club-init.sql 中的建表与初始化数据 |
||||
|
|
||||
|
1:修改 src/main/resources 目录下的 jfinal-club-config-dev.txt 配置文件 |
||||
|
|
||||
|
2:该项目为标准的 maven web app 项目,以往相关经验可直接使用 |
||||
|
|
||||
|
3:JFinalClubConfig.java 文件中已经创建了一个 main 方法, |
||||
|
直接右键该文件,点击 debug 或 run 即可运行 |
||||
|
|
||||
|
4:按照以上方式运行过一次以后,eclipse 或 IDEA 会自动生成一条debug、run configuration |
||||
|
配置项,对该配置项可进行进一步设置,例如可设置 VM argument: -XX:PermSize=64M -XX:MaxPermSize=256M |
||||
|
|
||||
|
5:由于 jfinal 是通过监听被编译出来的 class 文件是否被修改来支持的热载,而 IDEA 默认是不会 |
||||
|
自动编译被修改的 java 源文件的,所以无法自动化支持热加载 |
||||
|
|
||||
|
所以在 IDEA 下开发,需要手动按一下 Ctrl + F9 快捷键来编译被修改过的 Java 源代码, |
||||
|
从而触发热加载(mac 操作系统按 Cmd + F9),当然你还可以在网上找一下配置 IDEA 自动 |
||||
|
编译的方法来自动化实现热加载 |
||||
|
|
||||
|
此外,在 IDEA 下还可以按 Alt + 5 激活调试窗口,再按一下 Ctrl + F5 的方式重启项目, |
||||
|
此方法用来代替热加载功能也比较方便(mac 操作系统下是: Cmd + 5 再 Cmd + R) |
||||
|
|
||||
|
|
||||
|
x:源码中只给出了很粗浅的注释说明,加入俱乐部可全面掌握源码内涵:https://jfinal.com/club |
||||
|
加入俱乐部还可以获取往期大量直播视频,可以深度理解并掌握 jfinal 以及 jfinal club 的设计与用法 |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
@ -0,0 +1,75 @@ |
|||||
|
# 配置样例文件 undertow-config-demo.txt 使用说明: |
||||
|
# |
||||
|
# 1:系统默认在 class path 根目录下先加载 undertow.txt 再加载 undertow-pro.txt |
||||
|
# 进行配置,当上述两个配置文件不存在时不抛异常并使用默认值配置 |
||||
|
# |
||||
|
# 2:所有配置可以省略,省略时使用默认配置 |
||||
|
# |
||||
|
# 3:开发阶段 undertow.devMode 配置为 true 才支持热加载 |
||||
|
# |
||||
|
# 4:该文件列出了绝大多数可配置项,更多不常用配置可以查看 UndertowConfig 源码中的配置常量定义 |
||||
|
# |
||||
|
# 5:当配置项不足以满足需求时,可以通过继承 UndertowServer 并覆盖 configMore() |
||||
|
# 方法来添加额外的配置项 |
||||
|
|
||||
|
|
||||
|
# true 值支持热加载,生产环境建议配置成 false |
||||
|
undertow.devMode=false |
||||
|
|
||||
|
# 避免项目中的 .class 打成 jar 包以后,同时在使用 devMode 时报的异常 |
||||
|
# 只要 underto.devMode 设置为 false,或者不打包就不会有异常 |
||||
|
# 添加此配置以后则无需关心上面这类事情,多个前缀用逗号分隔开 |
||||
|
undertow.hotSwapClassPrefix=com.jfinal.club. |
||||
|
|
||||
|
|
||||
|
undertow.port=80 |
||||
|
undertow.host=localhost |
||||
|
undertow.contextPath=/ |
||||
|
|
||||
|
# js、css 等等 web 资源存放的目录 |
||||
|
undertow.resourcePath=webapp, src/main/webapp, classpath:static |
||||
|
|
||||
|
# io 线程数与 worker 线程数 |
||||
|
# undertow.ioThreads= |
||||
|
# undertow.workerThreads= |
||||
|
|
||||
|
# gzip 压缩开关 |
||||
|
undertow.gzip.enable=false |
||||
|
# 配置压缩级别,默认值 -1。 可配置 1 到 9。 1 拥有最快压缩速度,9 拥有最高压缩率 |
||||
|
undertow.gzip.level=-1 |
||||
|
# 触发压缩的最小内容长度 |
||||
|
undertow.gzip.minLength=1024 |
||||
|
|
||||
|
|
||||
|
# session 过期时间,注意单位是秒 |
||||
|
# undertow.session.timeout=1800 |
||||
|
# 热加载保持 session 值,避免依赖于 session 的登录型项目反复登录,默认值为 true。仅用于 devMode,生产环境无影响 |
||||
|
# undertow.session.hotSwap=true |
||||
|
|
||||
|
# 下面两行命令生成密钥库 |
||||
|
# keytool -genkeypair -validity 3650 -alias club -keyalg RSA -keystore club.jks |
||||
|
# keytool -importkeystore -srckeystore club.jks -destkeystore club.pfx -deststoretype PKCS12 |
||||
|
# 生成过程中提示输入 "名字与姓氏" 时输入 localhost。生产环境从阿里云下载 tomcat 类型的密钥库 |
||||
|
# 是否开启 ssl |
||||
|
undertow.ssl.enable=true |
||||
|
# ssl 监听端口号,部署环境设置为 443 |
||||
|
undertow.ssl.port=443 |
||||
|
# 密钥库类型,建议使用 PKCS12 |
||||
|
undertow.ssl.keyStoreType=PKCS12 |
||||
|
# 密钥库文件 |
||||
|
undertow.ssl.keyStore=club.pfx |
||||
|
# 密钥库密码 |
||||
|
undertow.ssl.keyStorePassword=111111 |
||||
|
|
||||
|
|
||||
|
# ssl 开启时,是否开启 http2。检测该配置是否生效在 chrome 地址栏中输入: chrome://net-internals/#http2 |
||||
|
# undertow.http2.enable=true |
||||
|
|
||||
|
|
||||
|
# ssl 开启时,http 请求是否重定向到 https |
||||
|
# undertow.http.toHttps=false |
||||
|
# ssl 开启时,http 请求跳转到 https 使用的状态码,默认值 302 |
||||
|
# undertow.http.toHttpsStatusCode=302 |
||||
|
# ssl 开启时,是否关闭 http |
||||
|
# undertow.http.disable=false |
||||
|
|
||||
@ -0,0 +1,58 @@ |
|||||
|
0:jfinal club 1.4 新添加了如下四张表: |
||||
|
role(id, name) |
||||
|
permission(id, actionKey, controller, remark) |
||||
|
account_role(accountId, roleId) |
||||
|
role_permission(roleId, permissionId) |
||||
|
|
||||
|
其中 role、permission 与老版本的 account 构成权限管理功能的三张主表, |
||||
|
而 account_role、role_permission 建立这三张主表间的关联 |
||||
|
|
||||
|
|
||||
|
1:权限管理模块对左侧菜单进行管理,可以参考 "/_view/_admin/common/_menu.html" |
||||
|
中的 #role("权限管理员", "CEO", "CTO") 用法,只需要先用这种式方式预先 |
||||
|
安排好哪些角色可以访问哪些菜单,然后就可以通过为 account 配置 role 的方式 |
||||
|
来配置菜单权限了,这种模式比通过再创建 menu 表要简单方便 |
||||
|
|
||||
|
此外,也可以通过 #permission(...) 更细粒度的控制每一个菜单,一般用 #role 指令即可 |
||||
|
|
||||
|
|
||||
|
2:权限管理模块对界面操作按钮之类的组件细粒度的控制可以参考 "/_view/_admin/project/index.html" |
||||
|
中的 #permission("/admin/project/delete") 用法,只需要先用这种方式预先 |
||||
|
安排好哪些权限用于控制访问哪些按钮或组件,然后就可以通过为 account 配置权限的 |
||||
|
方式来细粒度控制按纽、组件了 |
||||
|
|
||||
|
|
||||
|
3:share、feedback 的回复管理下一版本添加:贴子 title + table 结构 |
||||
|
|
||||
|
|
||||
|
4:头部导航已经做过搜索的界面,一直对美观不满意,留到下版本做进去, |
||||
|
搜索栏样式参考:https://fontawesome.com/v4.7.0/icons/ |
||||
|
|
||||
|
|
||||
|
5:项目整体结构采用先划分模块,然后在模块内部再分层的方式,便于向大型系统进化, |
||||
|
市面上很多先划分层次,然后再划分模块的方式远没有此方式适应大型系统的开发, |
||||
|
此划分方式,还有利于在未来将模块独立拆分成小型服务,再以微服务的方式做分布式 |
||||
|
而先分层再分模块的方式则无法方便支持 |
||||
|
|
||||
|
|
||||
|
6:后台管理源码的包名以及视图文件的目录名以下划线打头 "_",是为了让其始终排列在 |
||||
|
固定的位置,便于开发过程中快速定位,提升开发效率。否则后台管理模块的位置 |
||||
|
会不断变化,例如,如果存在 about 包名,则 admin 会排在其之后 |
||||
|
|
||||
|
|
||||
|
7:视图文件基础路径 "_view" 以下划线打头 "_",是为了将其排在 webapp 的最前面,便于快速 |
||||
|
定位,提升开发效率,否则该目录会被夹杂在 assets、WEB-INF、upload 等目录之间 |
||||
|
由于 jfinal 有 baseViewPath 配置,所以此安排不会给开发带来麻烦 |
||||
|
|
||||
|
|
||||
|
8:后台管理并没有专用的登录界面,一切针对于后台管理登录界面的黑客攻击根本找不到这个界面 |
||||
|
一切对于该界面的寻找必将返回 404 页面,没有专用的管理账户表,管理员账号存在于普通 |
||||
|
账号之中。无招才是更好的招。 |
||||
|
|
||||
|
|
||||
|
总之:jfinal-club 值得讨论和学习的细节极多,上述仅为冰山一角,大家一定要加入俱乐部 |
||||
|
收获所有福利 https://jfinal.com/club |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
@ -0,0 +1,24 @@ |
|||||
|
|
||||
|
|
||||
|
请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业, |
||||
|
进而保护我们自己的利益,即便是公司的同事,也请尊重 JFinal 作者的 |
||||
|
努力与付出,不要复制给任何人 |
||||
|
|
||||
|
|
||||
|
JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
首次的尝试扼杀在了摇篮之中 |
||||
|
|
||||
|
|
||||
|
如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部: |
||||
|
https://jfinal.com/club |
||||
|
|
||||
|
|
||||
|
俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者 |
||||
|
在俱乐部定期的分享与答疑,价值远比仅仅拥有 jfinal club |
||||
|
项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
新福利计划: |
||||
|
https://jfinal.com/club/1-2 |
||||
|
|
||||
|
JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
|
||||
@ -0,0 +1,69 @@ |
|||||
|
@echo off |
||||
|
|
||||
|
rem ------------------------------------------------------------------------- |
||||
|
rem |
||||
|
rem 使用说明: |
||||
|
rem |
||||
|
rem 1: 该脚本用于别的项目时只需要修改 MAIN_CLASS 即可运行 |
||||
|
rem |
||||
|
rem 2: JAVA_OPTS 可通过 -D 传入 undertow.port 与 undertow.host 这类参数覆盖 |
||||
|
rem 配置文件中的相同值此外还有 undertow.resourcePath, undertow.ioThreads |
||||
|
rem undertow.workerThreads 共五个参数可通过 -D 进行传入 |
||||
|
rem |
||||
|
rem 3: JAVA_OPTS 可传入标准的 java 命令行参数,例如 -Xms256m -Xmx1024m 这类常用参数 |
||||
|
rem |
||||
|
rem |
||||
|
rem ------------------------------------------------------------------------- |
||||
|
|
||||
|
setlocal & pushd |
||||
|
|
||||
|
|
||||
|
rem 启动入口类,该脚本文件用于别的项目时要改这里 |
||||
|
set MAIN_CLASS=com.jfinal.club.common.JFinalClubConfig |
||||
|
|
||||
|
rem Java 命令行参数,根据需要开启下面的配置,改成自己需要的,注意等号前后不能有空格 |
||||
|
rem set "JAVA_OPTS=-Xms256m -Xmx1024m -Dundertow.port=80 -Dundertow.host=0.0.0.0" |
||||
|
rem set "JAVA_OPTS=-Dundertow.port=80 -Dundertow.host=0.0.0.0" |
||||
|
|
||||
|
|
||||
|
if "%1"=="start" goto normal |
||||
|
if "%1"=="stop" goto normal |
||||
|
if "%1"=="restart" goto normal |
||||
|
|
||||
|
goto error |
||||
|
|
||||
|
|
||||
|
:error |
||||
|
echo Usage: jfinal.bat start | stop | restart |
||||
|
goto :eof |
||||
|
|
||||
|
|
||||
|
:normal |
||||
|
if "%1"=="start" goto start |
||||
|
if "%1"=="stop" goto stop |
||||
|
if "%1"=="restart" goto restart |
||||
|
goto :eof |
||||
|
|
||||
|
|
||||
|
:start |
||||
|
set APP_BASE_PATH=%~dp0 |
||||
|
set CP=%APP_BASE_PATH%config;%APP_BASE_PATH%lib\* |
||||
|
echo starting jfinal undertow |
||||
|
java -Xverify:none %JAVA_OPTS% -cp %CP% %MAIN_CLASS% |
||||
|
goto :eof |
||||
|
|
||||
|
|
||||
|
:stop |
||||
|
set "PATH=%JAVA_HOME%\bin;%PATH%" |
||||
|
echo stopping jfinal undertow |
||||
|
for /f "tokens=1" %%i in ('jps -l ^| find "%MAIN_CLASS%"') do ( taskkill /F /PID %%i ) |
||||
|
goto :eof |
||||
|
|
||||
|
|
||||
|
:restart |
||||
|
call :stop |
||||
|
call :start |
||||
|
goto :eof |
||||
|
|
||||
|
endlocal & popd |
||||
|
pause |
||||
@ -0,0 +1,82 @@ |
|||||
|
#!/bin/bash |
||||
|
# ---------------------------------------------------------------------- |
||||
|
# name: jfinal.sh |
||||
|
# version: 1.0 |
||||
|
# author: yangfuhai |
||||
|
# email: fuhai999@gmail.com |
||||
|
# |
||||
|
# 使用说明: |
||||
|
# 1: 该脚本使用前需要首先修改 MAIN_CLASS 值,使其指向实际的启动类 |
||||
|
# |
||||
|
# 2:使用命令行 ./jfinal.sh start | stop | restart 可启动/关闭/重启项目 |
||||
|
# |
||||
|
# 3: JAVA_OPTS 可通过 -D 传入 undertow.port 与 undertow.host 这类参数覆盖 |
||||
|
# 配置文件中的相同值此外还有 undertow.resourcePath、undertow.ioThreads、 |
||||
|
# undertow.workerThreads 共五个参数可通过 -D 进行传入,该功能尽可能减少了 |
||||
|
# 修改 undertow 配置文件的必要性 |
||||
|
# |
||||
|
# 4: JAVA_OPTS 可传入标准的 java 命令行参数,例如 -Xms256m -Xmx1024m 这类常用参数 |
||||
|
# |
||||
|
# 5: 函数 start() 给出了 4 种启动项目的命令行,根据注释中的提示自行选择合适的方式 |
||||
|
# |
||||
|
# ---------------------------------------------------------------------- |
||||
|
|
||||
|
# 启动入口类,该脚本文件用于别的项目时要改这里 |
||||
|
MAIN_CLASS=com.jfinal.club.common.JFinalClubConfig |
||||
|
|
||||
|
if [[ "$MAIN_CLASS" == "com.yourpackage.YourMainClass" ]]; then |
||||
|
echo "请先修改 MAIN_CLASS 的值为你自己项目启动Class,然后再执行此脚本。" |
||||
|
exit 0 |
||||
|
fi |
||||
|
|
||||
|
COMMAND="$1" |
||||
|
|
||||
|
if [[ "$COMMAND" != "start" ]] && [[ "$COMMAND" != "stop" ]] && [[ "$COMMAND" != "restart" ]]; then |
||||
|
echo "Usage: $0 start | stop | restart" |
||||
|
exit 0 |
||||
|
fi |
||||
|
|
||||
|
|
||||
|
# Java 命令行参数,根据需要开启下面的配置,改成自己需要的,注意等号前后不能有空格 |
||||
|
# JAVA_OPTS="-Xms256m -Xmx1024m -Dundertow.port=80 -Dundertow.host=0.0.0.0" |
||||
|
# JAVA_OPTS="-Dundertow.port=80 -Dundertow.host=0.0.0.0" |
||||
|
|
||||
|
# 生成 class path 值 |
||||
|
APP_BASE_PATH=$(cd `dirname $0`; pwd) |
||||
|
CP=${APP_BASE_PATH}/config:${APP_BASE_PATH}/lib/* |
||||
|
|
||||
|
function start() |
||||
|
{ |
||||
|
# 运行为后台进程,并在控制台输出信息 |
||||
|
java -Xverify:none ${JAVA_OPTS} -cp ${CP} ${MAIN_CLASS} & |
||||
|
|
||||
|
# 运行为后台进程,并且不在控制台输出信息 |
||||
|
# nohup java -Xverify:none ${JAVA_OPTS} -cp ${CP} ${MAIN_CLASS} >/dev/null 2>&1 & |
||||
|
|
||||
|
# 运行为后台进程,并且将信息输出到 output.log 文件 |
||||
|
# nohup java -Xverify:none ${JAVA_OPTS} -cp ${CP} ${MAIN_CLASS} > output.log & |
||||
|
|
||||
|
# 运行为非后台进程,多用于开发阶段,快捷键 ctrl + c 可停止服务 |
||||
|
# java -Xverify:none ${JAVA_OPTS} -cp ${CP} ${MAIN_CLASS} |
||||
|
} |
||||
|
|
||||
|
function stop() |
||||
|
{ |
||||
|
# 支持集群部署 |
||||
|
kill `pgrep -f ${APP_BASE_PATH}` 2>/dev/null |
||||
|
|
||||
|
# kill 命令不使用 -9 参数时,会回调 onStop() 方法,确定不需要此回调建议使用 -9 参数 |
||||
|
# kill `pgrep -f ${MAIN_CLASS}` 2>/dev/null |
||||
|
|
||||
|
# 以下代码与上述代码等价 |
||||
|
# kill $(pgrep -f ${MAIN_CLASS}) 2>/dev/null |
||||
|
} |
||||
|
|
||||
|
if [[ "$COMMAND" == "start" ]]; then |
||||
|
start |
||||
|
elif [[ "$COMMAND" == "stop" ]]; then |
||||
|
stop |
||||
|
else |
||||
|
stop |
||||
|
start |
||||
|
fi |
||||
@ -0,0 +1,87 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.0.0" |
||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
|
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.0.0 http://maven.apache.org/xsd/assembly-2.0.0.xsd"> |
||||
|
|
||||
|
<!-- |
||||
|
assembly 打包配置更多配置可参考官方文档: |
||||
|
http://maven.apache.org/plugins/maven-assembly-plugin/assembly.html |
||||
|
--> |
||||
|
|
||||
|
<id>release</id> |
||||
|
|
||||
|
<!-- |
||||
|
设置打包格式,可同时设置多种格式,常用格式有:dir、zip、tar、tar.gz |
||||
|
dir 格式便于在本地测试打包结果 |
||||
|
zip 格式便于 windows 系统下解压运行 |
||||
|
tar、tar.gz 格式便于 linux 系统下解压运行 |
||||
|
--> |
||||
|
<formats> |
||||
|
<format>dir</format> |
||||
|
<format>zip</format> |
||||
|
<!-- <format>tar.gz</format> --> |
||||
|
</formats> |
||||
|
|
||||
|
<!-- 打 zip 设置为 true 时,会在 zip 包中生成一个根目录,打 dir 时设置为 false 少层目录 --> |
||||
|
<includeBaseDirectory>true</includeBaseDirectory> |
||||
|
|
||||
|
<fileSets> |
||||
|
<!-- src/main/resources 全部 copy 到 config 目录下 --> |
||||
|
<fileSet> |
||||
|
<directory>${basedir}/src/main/resources</directory> |
||||
|
<outputDirectory>config</outputDirectory> |
||||
|
</fileSet> |
||||
|
|
||||
|
<!-- src/main/webapp 全部 copy 到 webapp 目录下 --> |
||||
|
<fileSet> |
||||
|
<directory>${basedir}/src/main/webapp</directory> |
||||
|
<outputDirectory>webapp</outputDirectory> |
||||
|
<excludes> |
||||
|
<!-- **/* 前缀用法,可以匹配所有路径,例如:**/*.txt --> |
||||
|
<exclude>WEB-INF</exclude> |
||||
|
<exclude>WEB-INF/web.xml</exclude> |
||||
|
</excludes> |
||||
|
</fileSet> |
||||
|
|
||||
|
<!-- 项目根下面的脚本文件 copy 到根目录下 --> |
||||
|
<fileSet> |
||||
|
<directory>${basedir}</directory> |
||||
|
<outputDirectory></outputDirectory> |
||||
|
<!-- 脚本文件在 linux 下的权限设为 755,无需 chmod 可直接运行 --> |
||||
|
<fileMode>755</fileMode> |
||||
|
<lineEnding>unix</lineEnding> |
||||
|
<includes> |
||||
|
<include>*.sh</include> |
||||
|
</includes> |
||||
|
</fileSet> |
||||
|
<fileSet> |
||||
|
<directory>${basedir}</directory> |
||||
|
<outputDirectory></outputDirectory> |
||||
|
<fileMode>755</fileMode> |
||||
|
<lineEnding>windows</lineEnding> |
||||
|
<includes> |
||||
|
<include>*.bat</include> |
||||
|
</includes> |
||||
|
</fileSet> |
||||
|
|
||||
|
|
||||
|
<!-- 项目 lib 目录下的本地 jar 包全部 copy 到 lib 目录下 --> |
||||
|
<!-- fileSet> |
||||
|
<directory>${basedir}/lib</directory> |
||||
|
<outputDirectory>lib</outputDirectory> |
||||
|
</fileSet --> |
||||
|
|
||||
|
|
||||
|
</fileSets> |
||||
|
|
||||
|
<!-- 依赖的 jar 包 copy 到 lib 目录下 --> |
||||
|
<dependencySets> |
||||
|
<dependencySet> |
||||
|
<outputDirectory>lib</outputDirectory> |
||||
|
</dependencySet> |
||||
|
</dependencySets> |
||||
|
|
||||
|
</assembly> |
||||
|
|
||||
|
|
||||
|
|
||||
@ -0,0 +1,276 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> |
||||
|
<modelVersion>4.0.0</modelVersion> |
||||
|
|
||||
|
|
||||
|
<groupId>com.jfinal</groupId> |
||||
|
<artifactId>bt-panel</artifactId> |
||||
|
<version>0.0.1</version> |
||||
|
<packaging>jar</packaging> |
||||
|
|
||||
|
|
||||
|
<name>bt-panel</name> |
||||
|
<url>https://jfinal.com/club</url> |
||||
|
|
||||
|
<properties> |
||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> |
||||
|
<maven.compiler.encoding>UTF-8</maven.compiler.encoding> |
||||
|
</properties> |
||||
|
|
||||
|
<!-- 使用阿里 maven 库 --> |
||||
|
<repositories> |
||||
|
<repository> |
||||
|
<id>ali-maven</id> |
||||
|
<url>http://maven.aliyun.com/nexus/content/groups/public</url> |
||||
|
<releases> |
||||
|
<enabled>true</enabled> |
||||
|
</releases> |
||||
|
<snapshots> |
||||
|
<enabled>true</enabled> |
||||
|
<updatePolicy>always</updatePolicy> |
||||
|
<checksumPolicy>fail</checksumPolicy> |
||||
|
</snapshots> |
||||
|
</repository> |
||||
|
</repositories> |
||||
|
|
||||
|
<dependencies> |
||||
|
|
||||
|
|
||||
|
<!-- jfinal-undertow 开发、部署一体化 web 服务器 --> |
||||
|
<dependency> |
||||
|
<groupId>com.jfinal</groupId> |
||||
|
<artifactId>jfinal-undertow</artifactId> |
||||
|
<version>3.5</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- jfinal --> |
||||
|
<dependency> |
||||
|
<groupId>com.jfinal</groupId> |
||||
|
<artifactId>jfinal</artifactId> |
||||
|
<version>5.1.2</version> |
||||
|
</dependency> |
||||
|
|
||||
|
|
||||
|
|
||||
|
<!-- cos 文件上传 --> |
||||
|
<dependency> |
||||
|
<groupId>com.jfinal</groupId> |
||||
|
<artifactId>cos</artifactId> |
||||
|
<version>2020.4</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- fastjson json 转换 --> |
||||
|
<dependency> |
||||
|
<groupId>com.alibaba</groupId> |
||||
|
<artifactId>fastjson</artifactId> |
||||
|
<version>2.0.48</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- 开发 WebSockets 时开启下面的依赖 --> |
||||
|
<!-- |
||||
|
<dependency> |
||||
|
<groupId>io.undertow</groupId> |
||||
|
<artifactId>undertow-websockets-jsr</artifactId> |
||||
|
<version>2.0.32.Final</version> |
||||
|
</dependency> |
||||
|
--> |
||||
|
|
||||
|
<!-- junit 单元测试 --> |
||||
|
<dependency> |
||||
|
<groupId>junit</groupId> |
||||
|
<artifactId>junit</artifactId> |
||||
|
<version>4.13.2</version> |
||||
|
<scope>test</scope> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- 避免控制台输出如下提示信息: |
||||
|
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". |
||||
|
项目中实际上用不到这个 jar 包 |
||||
|
注意:eclipse 下可以将 scope 设置为 provided |
||||
|
--> |
||||
|
<dependency> |
||||
|
<groupId>org.slf4j</groupId> |
||||
|
<artifactId>slf4j-nop</artifactId> |
||||
|
<version>1.7.29</version> |
||||
|
<!-- 打包前改成 provided,此处使用 compile 仅为支持 IDEA --> |
||||
|
<scope>compile</scope> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- druid 数据源连接池 --> |
||||
|
<dependency> |
||||
|
<groupId>com.alibaba</groupId> |
||||
|
<artifactId>druid</artifactId> |
||||
|
<version>1.2.22</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- log4j 日志 --> |
||||
|
<dependency> |
||||
|
<groupId>log4j</groupId> |
||||
|
<artifactId>log4j</artifactId> |
||||
|
<version>1.2.17</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- mysql 驱动 --> |
||||
|
<dependency> |
||||
|
<groupId>mysql</groupId> |
||||
|
<artifactId>mysql-connector-java</artifactId> |
||||
|
<version>8.0.33</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- ehcache 缓存 --> |
||||
|
<dependency> |
||||
|
<groupId>net.sf.ehcache</groupId> |
||||
|
<artifactId>ehcache-core</artifactId> |
||||
|
<version>2.6.11</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- 邮件发送 --> |
||||
|
<dependency> |
||||
|
<groupId>org.apache.commons</groupId> |
||||
|
<artifactId>commons-email</artifactId> |
||||
|
<version>1.5</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- jsoup 过滤恶意数据提交 --> |
||||
|
<dependency> |
||||
|
<groupId>org.jsoup</groupId> |
||||
|
<artifactId>jsoup</artifactId> |
||||
|
<version>1.9.2</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- joda-time 日期、时间变换 --> |
||||
|
<dependency> |
||||
|
<groupId>joda-time</groupId> |
||||
|
<artifactId>joda-time</artifactId> |
||||
|
<version>2.12.7</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- cron4j 任务调度 --> |
||||
|
<dependency> |
||||
|
<groupId>it.sauronsoftware.cron4j</groupId> |
||||
|
<artifactId>cron4j</artifactId> |
||||
|
<version>2.2.5</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<!-- zxing 二维码生成 --> |
||||
|
<dependency> |
||||
|
<groupId>com.google.zxing</groupId> |
||||
|
<artifactId>javase</artifactId> |
||||
|
<version>3.5.3</version> |
||||
|
</dependency> |
||||
|
|
||||
|
<dependency> |
||||
|
<groupId>com.alipay.sdk</groupId> |
||||
|
<artifactId>alipay-sdk-java</artifactId> |
||||
|
<version>4.39.37.ALL</version> |
||||
|
</dependency> |
||||
|
|
||||
|
</dependencies> |
||||
|
|
||||
|
|
||||
|
<build> |
||||
|
|
||||
|
|
||||
|
<!-- |
||||
|
添加 includes 配置后,excludes 默认为所有文件 **/*.*,反之亦然 |
||||
|
该规则在 maven-jar-plugin 等插件中同样适用 |
||||
|
--> |
||||
|
<resources> |
||||
|
<!-- 添加该配置是为了将 .sql 文件打入 jar 包 --> |
||||
|
<resource> |
||||
|
<directory>src/main/java</directory> |
||||
|
<includes> |
||||
|
<!-- **/* 前缀用法,可以匹配所有路径 --> |
||||
|
<include>**/*.sql</include> |
||||
|
<include>**/*.jf</include> |
||||
|
</includes> |
||||
|
</resource> |
||||
|
|
||||
|
<!-- |
||||
|
没有添加 resources 配置时,src/main/resources 目录是默认配置 |
||||
|
一旦添加 resources 配置指向 src/main/java 目录时,原先的默认配置被取代, |
||||
|
所以需要添加如下配置将默认配置再添加进来,否则无法使用 src/main/resources |
||||
|
下的资源文件 |
||||
|
--> |
||||
|
<resource> |
||||
|
<directory>src/main/resources</directory> |
||||
|
</resource> |
||||
|
</resources> |
||||
|
|
||||
|
|
||||
|
<plugins> |
||||
|
|
||||
|
<plugin> |
||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||
|
<artifactId>maven-compiler-plugin</artifactId> |
||||
|
<version>3.6.1</version> |
||||
|
<configuration> |
||||
|
<source>1.8</source> |
||||
|
<target>1.8</target> |
||||
|
<encoding>UTF-8</encoding> |
||||
|
<!-- java8 保留参数名编译参数 --> |
||||
|
<compilerArgument>-parameters</compilerArgument> |
||||
|
<compilerArguments><verbose /></compilerArguments> |
||||
|
</configuration> |
||||
|
</plugin> |
||||
|
|
||||
|
<!-- |
||||
|
jar 包中的配置文件优先级高于 config 目录下的 "同名文件" |
||||
|
因此,打包时需要排除掉 jar 包中来自 src/main/resources 目录的 |
||||
|
配置文件,否则部署时 config 目录中的同名配置文件不会生效 |
||||
|
--> |
||||
|
<plugin> |
||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||
|
<artifactId>maven-jar-plugin</artifactId> |
||||
|
<version>2.6</version> |
||||
|
<configuration> |
||||
|
<excludes> |
||||
|
<!-- |
||||
|
*.* 用法,可以匹配 jar 包根目录下所有文件 |
||||
|
*.xxx 用法,可以匹配 jar 包根目录下特定扩展名文件,例如:*.xml |
||||
|
**/* 前缀用法,可以匹配所有路径,例如:**/*.txt |
||||
|
--> |
||||
|
<exclude>*.*</exclude> |
||||
|
</excludes> |
||||
|
</configuration> |
||||
|
</plugin> |
||||
|
|
||||
|
<!-- |
||||
|
使用 mvn clean package 打包 |
||||
|
更多配置可参考官方文档:http://maven.apache.org/plugins/maven-assembly-plugin/single-mojo.html |
||||
|
--> |
||||
|
<plugin> |
||||
|
<groupId>org.apache.maven.plugins</groupId> |
||||
|
<artifactId>maven-assembly-plugin</artifactId> |
||||
|
<version>3.1.0</version> |
||||
|
<executions> |
||||
|
<execution> |
||||
|
<id>make-assembly</id> |
||||
|
<phase>package</phase> |
||||
|
<goals> |
||||
|
<goal>single</goal> |
||||
|
</goals> |
||||
|
|
||||
|
<configuration> |
||||
|
<!-- 打包生成的文件名 --> |
||||
|
<finalName>${project.artifactId}</finalName> |
||||
|
<!-- jar 等压缩文件在被打包进入 zip、tar.gz 时是否压缩,设置为 false 可加快打包速度 --> |
||||
|
<recompressZippedFiles>false</recompressZippedFiles> |
||||
|
<!-- 打包生成的文件是否要追加 package.xml 中定义的 id 值 --> |
||||
|
<appendAssemblyId>true</appendAssemblyId> |
||||
|
<!-- 指向打包描述文件 package.xml --> |
||||
|
<descriptors> |
||||
|
<descriptor>package.xml</descriptor> |
||||
|
</descriptors> |
||||
|
<!-- 打包结果输出的基础目录 --> |
||||
|
<outputDirectory>${project.build.directory}/</outputDirectory> |
||||
|
</configuration> |
||||
|
</execution> |
||||
|
</executions> |
||||
|
</plugin> |
||||
|
|
||||
|
</plugins> |
||||
|
</build> |
||||
|
|
||||
|
</project> |
||||
@ -0,0 +1,189 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
* 新福利计划: |
||||
|
* https://jfinal.com/club/1-2
|
||||
|
* |
||||
|
* JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于创建更加高品质的产品与服务,为你带来 |
||||
|
* 更多、更大的价值 |
||||
|
* |
||||
|
* JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
*/ |
||||
|
|
||||
|
package com.bt._admin.account; |
||||
|
|
||||
|
import com.jfinal.aop.Before; |
||||
|
import com.jfinal.aop.Inject; |
||||
|
import com.bt._admin.role.RoleAdminService; |
||||
|
import com.bt.common.account.AccountService; |
||||
|
import com.bt.common.model.Role; |
||||
|
import com.bt.my.setting.MySettingService; |
||||
|
import com.jfinal.core.Path; |
||||
|
import com.jfinal.plugin.activerecord.Page; |
||||
|
import com.bt.common.controller.BaseController; |
||||
|
import com.jfinal.kit.Ret; |
||||
|
import com.bt.common.model.Account; |
||||
|
import com.jfinal.plugin.activerecord.Record; |
||||
|
import com.jfinal.upload.UploadFile; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* 账户管理控制器 |
||||
|
*/ |
||||
|
@Path(value = "/admin/account", viewPath = "/account") |
||||
|
public class AccountAdminController extends BaseController { |
||||
|
|
||||
|
@Inject |
||||
|
AccountAdminService srv; |
||||
|
|
||||
|
@Inject |
||||
|
MySettingService mySettingSrv; |
||||
|
|
||||
|
@Inject |
||||
|
RoleAdminService roleAdminSrv; |
||||
|
|
||||
|
@Inject |
||||
|
AccountService accountSrv; |
||||
|
|
||||
|
public void index() { |
||||
|
Page<Account> accountPage = srv.paginate(getParaToInt("p", 1)); |
||||
|
setAttr("accountPage", accountPage); |
||||
|
render("index.html"); |
||||
|
} |
||||
|
|
||||
|
public void edit() { |
||||
|
keepPara("p"); // 保持住分页的页号,便于在 ajax 提交后跳转到当前数据所在的页
|
||||
|
Account account = srv.findById(getParaToInt("id")); |
||||
|
setAttr("account", account); |
||||
|
render("edit.html"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 提交修改 |
||||
|
*/ |
||||
|
@Before(AccountUpdateValidator.class) |
||||
|
public void update() { |
||||
|
Account account = getBean(Account.class); |
||||
|
Ret ret = srv.update(account); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 账户锁定 |
||||
|
*/ |
||||
|
public void lock() { |
||||
|
Ret ret = srv.lock(getLoginAccountId(), getParaToInt("id")); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 账户解锁 |
||||
|
*/ |
||||
|
public void unlock() { |
||||
|
Ret ret = srv.unlock(getParaToInt("id")); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 分配角色 |
||||
|
*/ |
||||
|
public void assignRoles() { |
||||
|
Account account = srv.findById(getParaToInt("id")); |
||||
|
List<Role> roleList = roleAdminSrv.getAllRoles(); |
||||
|
srv.markAssignedRoles(account, roleList); |
||||
|
|
||||
|
setAttr("account", account); |
||||
|
setAttr("roleList", roleList); |
||||
|
render("assign_roles.html"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 添加角色 |
||||
|
*/ |
||||
|
public void addRole() { |
||||
|
Ret ret = srv.addRole(getParaToInt("accountId"), getParaToInt("roleId")); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除角色 |
||||
|
*/ |
||||
|
public void deleteRole() { |
||||
|
Ret ret = srv.deleteRole(getParaToInt("accountId"), getParaToInt("roleId")); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 显示 "后台账户/管理员" 列表,在 account_role 表中存在的账户(被分配过角色的账户) |
||||
|
* 被定义为 "后台账户/管理员" |
||||
|
* |
||||
|
* 该功能便于查看后台都有哪些账户被分配了角色,在对账户误操作分配了角色时,也便于取消角色分配 |
||||
|
*/ |
||||
|
public void showAdminList() { |
||||
|
List<Record> adminList = srv.getAdminList(); |
||||
|
setAttr("adminList", adminList); |
||||
|
render("admin_list.html"); |
||||
|
} |
||||
|
|
||||
|
public void avatar() { |
||||
|
keepPara("p"); // 保持住分页的页号,便于在 ajax 提交后跳转到当前数据所在的页
|
||||
|
Account account = srv.findById(getParaToInt("accountId")); |
||||
|
setAttr("account", account); |
||||
|
render("avatar.html"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 上传用户图片,为裁切头像做准备 |
||||
|
*/ |
||||
|
public void uploadAvatar() { |
||||
|
UploadFile uf = null; |
||||
|
try { |
||||
|
uf = getFile("avatar", mySettingSrv.getAvatarTempDir(), mySettingSrv.getAvatarMaxSize()); |
||||
|
if (uf == null) { |
||||
|
renderJson(Ret.fail("msg", "请先选择上传文件")); |
||||
|
return; |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
if (e instanceof com.jfinal.upload.ExceededSizeException) { |
||||
|
renderJson(Ret.fail("msg", "文件大小超出范围")); |
||||
|
} else { |
||||
|
if (uf != null) { |
||||
|
// 只有出现异常时才能删除,不能在 finally 中删,因为后面需要用到上传文件
|
||||
|
uf.getFile().delete(); |
||||
|
} |
||||
|
renderJson(Ret.fail("msg", e.getMessage())); |
||||
|
} |
||||
|
return ; |
||||
|
} |
||||
|
|
||||
|
// 注意这里可以更换任意用户的头像,所以并非 getLoginAccountId()
|
||||
|
int accountId = getParaToInt("accountId"); |
||||
|
Ret ret = mySettingSrv.uploadAvatar(accountId, uf); |
||||
|
if (ret.isOk()) { // 上传成功则将文件 url 径暂存起来,供下个环节进行裁切
|
||||
|
setSessionAttr("avatarUrl", ret.get("avatarUrl")); |
||||
|
} |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 保存 jcrop 裁切区域为用户头像 |
||||
|
*/ |
||||
|
public void saveAvatar() { |
||||
|
// 注意这里可以更换任意用户的头像,所以并非 getLoginAccountId()
|
||||
|
int accountId = getParaToInt("accountId"); |
||||
|
Account account = accountSrv.getById(accountId); |
||||
|
|
||||
|
String avatarUrl = getSessionAttr("avatarUrl"); |
||||
|
int x = getParaToInt("x"); |
||||
|
int y = getParaToInt("y"); |
||||
|
int width = getParaToInt("width"); |
||||
|
int height = getParaToInt("height"); |
||||
|
Ret ret = mySettingSrv.saveAvatar(account, avatarUrl, x, y, width, height); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,165 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
* 新福利计划: |
||||
|
* https://jfinal.com/club/1-2
|
||||
|
* |
||||
|
* JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于创建更加高品质的产品与服务,为你带来 |
||||
|
* 更多、更大的价值 |
||||
|
* |
||||
|
* JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
*/ |
||||
|
|
||||
|
package com.bt._admin.account; |
||||
|
|
||||
|
import com.jfinal.aop.Inject; |
||||
|
import com.bt.common.model.Account; |
||||
|
import com.bt.common.model.Role; |
||||
|
import com.bt.common.model.Session; |
||||
|
import com.bt.login.LoginService; |
||||
|
import com.jfinal.kit.Ret; |
||||
|
import com.jfinal.plugin.activerecord.Db; |
||||
|
import com.jfinal.plugin.activerecord.Page; |
||||
|
import com.jfinal.plugin.activerecord.Record; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* 账户管理 |
||||
|
*/ |
||||
|
public class AccountAdminService { |
||||
|
|
||||
|
@Inject |
||||
|
LoginService loginSrv; |
||||
|
|
||||
|
private Account dao = new Account().dao(); |
||||
|
|
||||
|
public Page<Account> paginate(int pageNum) { |
||||
|
return dao.paginate(pageNum, 10, "select *", "from account order by id desc"); |
||||
|
} |
||||
|
|
||||
|
public Account findById(int accountId) { |
||||
|
return dao.findById(accountId); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 注意要验证 nickName 与 userName 是否存在 |
||||
|
*/ |
||||
|
public Ret update(Account account) { |
||||
|
String nickName = account.getNickName().toLowerCase().trim(); |
||||
|
String sql = "select id from account where lower(nickName) = ? and id != ? limit 1"; |
||||
|
Integer id = Db.queryInt(sql, nickName, account.getId()); |
||||
|
if (id != null) { |
||||
|
return Ret.fail("msg", "昵称已经存在,请输入别的昵称"); |
||||
|
} |
||||
|
|
||||
|
String userName = account.getUserName().toLowerCase().trim(); |
||||
|
sql = "select id from account where lower(userName) = ? and id != ? limit 1"; |
||||
|
id = Db.queryInt(sql, userName, account.getId()); |
||||
|
if (id != null) { |
||||
|
return Ret.fail("msg", "邮箱已经存在,请输入别的昵称"); |
||||
|
} |
||||
|
|
||||
|
// 暂时只允许修改 nickName 与 userName
|
||||
|
account.keep("id", "nickName", "userName"); |
||||
|
account.update(); |
||||
|
return Ret.ok("msg", "账户更新成功"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 锁定账号 |
||||
|
*/ |
||||
|
public Ret lock(int loginAccountId, int lockedAccountId) { |
||||
|
if (lockedAccountId == 1) { |
||||
|
return Ret.fail("msg", "不能锁定超级管理员的账号"); |
||||
|
} |
||||
|
|
||||
|
if (loginAccountId == lockedAccountId) { |
||||
|
return Ret.fail("msg", "不能锁定自己的账号"); |
||||
|
} |
||||
|
|
||||
|
int n = Db.update("update account set status = ? where id=?", Account.STATUS_LOCK_ID, lockedAccountId); |
||||
|
|
||||
|
// 锁定后,强制退出登录,避免继续搞破坏
|
||||
|
List<Session> sessionList = Session.dao.find("select * from session where accountId = ?", lockedAccountId); |
||||
|
if (sessionList != null) { |
||||
|
for (Session session : sessionList) { // 处理多客户端同时登录后的多 session 记录
|
||||
|
loginSrv.logout(session.getId()); // 清除登录 cache,强制退出
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (n > 0) { |
||||
|
return Ret.ok("msg", "锁定成功"); |
||||
|
} else { |
||||
|
return Ret.fail("msg", "锁定失败"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 解锁账号 |
||||
|
*/ |
||||
|
public Ret unlock(int accountId) { |
||||
|
// 如果账户未激活,则不能被解锁
|
||||
|
int n = Db.update("update account set status = ? where status != ? and id = ?", Account.STATUS_OK , Account.STATUS_REG , accountId); |
||||
|
Db.update("delete from session where accountId = ?", accountId); |
||||
|
if (n > 0) { |
||||
|
return Ret.ok("msg", "解锁成功"); |
||||
|
} else { |
||||
|
return Ret.fail("msg", "解锁失败,可能是账户未激活,请查看账户详情"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 添加角色 |
||||
|
*/ |
||||
|
public Ret addRole(int accountId, int roleId) { |
||||
|
Record accountRole = new Record().set("accountId", accountId).set("roleId", roleId); |
||||
|
Db.save("account_role", accountRole); |
||||
|
return Ret.ok("msg", "添加角色成功"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除角色 |
||||
|
*/ |
||||
|
public Ret deleteRole(int accountId, int roleId) { |
||||
|
if (accountId == 1 && roleId == 1) { |
||||
|
return Ret.fail("msg", "超级管理员角色账号不能被删除"); |
||||
|
} |
||||
|
|
||||
|
Db.delete("delete from account_role where accountId=? and roleId=?", accountId, roleId); |
||||
|
return Ret.ok("msg", "删除角色成功"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 标记出 account 拥有的角色 |
||||
|
* 未来用 role left join account_role 来优化 |
||||
|
*/ |
||||
|
public void markAssignedRoles(Account account, List<Role> roleList) { |
||||
|
String sql = "select accountId from account_role where accountId=? and roleId=? limit 1"; |
||||
|
for (Role role : roleList) { |
||||
|
Integer accountId = Db.queryInt(sql, account.getId(), role.getId()); |
||||
|
if (accountId != null) { |
||||
|
// 设置 assigned 用于界面输出 checked
|
||||
|
role.put("assigned", true); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取 "后台账户/管理员" 列表,在 account_role 表中存在的账户(被分配过角色的账户) |
||||
|
* 被定义为 "后台账户/管理员" |
||||
|
* |
||||
|
* 该功能便于查看后台都有哪些账户被分配了角色,在对账户误操作分配了角色时,也便于取消角色分配 |
||||
|
*/ |
||||
|
public List<Record> getAdminList() { |
||||
|
String sql = "select a.nickName, a.userName, ar.*, r.name from account a, account_role ar, role r " + |
||||
|
"where a.id = ar.accountId and ar.roleId = r.id " + |
||||
|
"order by roleId asc"; |
||||
|
|
||||
|
return Db.find(sql); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,67 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
* 新福利计划: |
||||
|
* https://jfinal.com/club/1-2
|
||||
|
* |
||||
|
* JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于创建更加高品质的产品与服务,为你带来 |
||||
|
* 更多、更大的价值 |
||||
|
* |
||||
|
* JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
*/ |
||||
|
|
||||
|
package com.bt._admin.account; |
||||
|
|
||||
|
import com.bt.common.kit.SensitiveWordsKit; |
||||
|
import com.bt.reg.RegValidator; |
||||
|
import com.jfinal.core.Controller; |
||||
|
import com.jfinal.kit.Ret; |
||||
|
import com.jfinal.validate.Validator; |
||||
|
|
||||
|
/** |
||||
|
* AccountUpdateValidator 验证账号修改功能表单 |
||||
|
*/ |
||||
|
public class AccountUpdateValidator extends Validator { |
||||
|
|
||||
|
protected void validate(Controller c) { |
||||
|
setShortCircuit(true); |
||||
|
|
||||
|
/** |
||||
|
* 验证 nickName |
||||
|
*/ |
||||
|
if (SensitiveWordsKit.checkSensitiveWord(c.getPara("account.nickName")) != null) { |
||||
|
addError("msg", "昵称不能包含敏感词"); |
||||
|
} |
||||
|
validateRequired("account.nickName", "msg", "昵称不能为空"); |
||||
|
validateString("account.nickName", 1, 19, "msg", "昵称不能超过19个字"); |
||||
|
|
||||
|
String nickName = c.getPara("account.nickName").trim(); |
||||
|
if (nickName.contains("@") || nickName.contains("@")) { // 全角半角都要判断
|
||||
|
addError("msg", "昵称不能包含 \"@\" 字符"); |
||||
|
} |
||||
|
if (nickName.contains(" ") || nickName.contains(" ")) { |
||||
|
addError("msg", "昵称不能包含空格"); |
||||
|
} |
||||
|
Ret ret = RegValidator.validateNickName(nickName); |
||||
|
if (ret.isFail()) { |
||||
|
addError("msg", ret.getStr("msg")); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 验证 userName |
||||
|
*/ |
||||
|
validateRequired("account.userName", "msg", "邮箱不能为空"); |
||||
|
validateEmail("account.userName", "msg", "邮箱格式不正确"); |
||||
|
} |
||||
|
|
||||
|
protected void handleError(Controller c) { |
||||
|
c.setAttr("state", "fail"); |
||||
|
c.renderJson(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,68 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
* 新福利计划: |
||||
|
* https://jfinal.com/club/1-2
|
||||
|
* |
||||
|
* JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于创建更加高品质的产品与服务,为你带来 |
||||
|
* 更多、更大的价值 |
||||
|
* |
||||
|
* JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
*/ |
||||
|
|
||||
|
package com.bt._admin.auth; |
||||
|
|
||||
|
import com.jfinal.aop.Inject; |
||||
|
import com.jfinal.aop.Interceptor; |
||||
|
import com.jfinal.aop.Invocation; |
||||
|
import com.bt.common.model.Account; |
||||
|
import com.bt.login.LoginService; |
||||
|
import com.jfinal.kit.Ret; |
||||
|
|
||||
|
/** |
||||
|
* 后台管理员授权拦截器 |
||||
|
*/ |
||||
|
public class AdminAuthInterceptor implements Interceptor { |
||||
|
|
||||
|
@Inject |
||||
|
AdminAuthService srv; |
||||
|
|
||||
|
/** |
||||
|
* 用于 sharedObject、sharedMethod 扩展中使用 |
||||
|
*/ |
||||
|
private static final ThreadLocal<Account> threadLocal = new ThreadLocal<Account>(); |
||||
|
|
||||
|
public static Account getThreadLocalAccount() { |
||||
|
return threadLocal.get(); |
||||
|
} |
||||
|
|
||||
|
public void intercept(Invocation inv) { |
||||
|
Account loginAccount = inv.getController().getAttr(LoginService.loginAccountCacheName); |
||||
|
if (loginAccount != null && loginAccount.isStatusOk()) { |
||||
|
// 传递给 sharedObject、sharedMethod 扩展使用
|
||||
|
threadLocal.set(loginAccount); |
||||
|
|
||||
|
// 如果是超级管理员或者拥有对当前 action 的访问权限则放行
|
||||
|
if ( srv.isSuperAdmin(loginAccount.getId()) || |
||||
|
srv.hasPermission(loginAccount.getId(), inv.getActionKey())) { |
||||
|
inv.invoke(); |
||||
|
return ; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// renderError(404) 避免暴露后台管理 url,增加安全性
|
||||
|
if (loginAccount == null || inv.getActionKey().equals("/admin")) { |
||||
|
inv.getController().renderError(404); |
||||
|
} |
||||
|
// renderJson 提示没有操作权限,提升用户体验
|
||||
|
else { |
||||
|
inv.getController().renderJson(Ret.fail("msg", "没有操作权限")); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,87 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
* 新福利计划: |
||||
|
* https://jfinal.com/club/1-2
|
||||
|
* |
||||
|
* JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于创建更加高品质的产品与服务,为你带来 |
||||
|
* 更多、更大的价值 |
||||
|
* |
||||
|
* JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
*/ |
||||
|
|
||||
|
package com.bt._admin.auth; |
||||
|
|
||||
|
import com.jfinal.aop.Aop; |
||||
|
import com.bt.common.model.Account; |
||||
|
|
||||
|
/** |
||||
|
* 权限管理的 shared method 扩展 |
||||
|
* |
||||
|
* 作为 #role、#permission 指令的补充,支持 #else 块 |
||||
|
* |
||||
|
* |
||||
|
* 使用示例: |
||||
|
* #if (hasRole("权限管理员", "CEO", "CTO")) |
||||
|
* ... |
||||
|
* #else |
||||
|
* ... |
||||
|
* #end |
||||
|
* |
||||
|
* #if (hasPermission("/admin/project/edit")) |
||||
|
* ... |
||||
|
* #else |
||||
|
* ... |
||||
|
* #end |
||||
|
*/ |
||||
|
public class AdminAuthKit { |
||||
|
|
||||
|
/** |
||||
|
* 注意这里与控制器和拦截器不同,不能使用 @Inject 注入 |
||||
|
* 但可以使用 Aop.get(...) 实现同样的功能,代码稍多点而已 |
||||
|
* |
||||
|
* 不能使用 @Inject 注入的原因是 AdminAuthKit 工具对象 |
||||
|
* 的创建并不是由 jfinal 接管的,而 controller、interceptor |
||||
|
* 的创建是由 jfinal 接管的,在接管后会自动进行注入动作 |
||||
|
* |
||||
|
* 所以,这里需要手动 Aop.get(...) |
||||
|
*/ |
||||
|
static AdminAuthService adminAuthSrv = Aop.get(AdminAuthService.class); |
||||
|
|
||||
|
/** |
||||
|
* 当前账号是否拥有某些角色 |
||||
|
*/ |
||||
|
public boolean hasRole(String... roleNameArray) { |
||||
|
Account account = AdminAuthInterceptor.getThreadLocalAccount(); |
||||
|
if (account != null && account.isStatusOk()) { |
||||
|
if ( adminAuthSrv.isSuperAdmin(account.getId()) || |
||||
|
adminAuthSrv.hasRole(account.getId(), roleNameArray)) { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 是否拥有具体某个权限 |
||||
|
*/ |
||||
|
public boolean hasPermission(String actionKey) { |
||||
|
Account account = AdminAuthInterceptor.getThreadLocalAccount(); |
||||
|
if (account != null && account.isStatusOk()) { |
||||
|
if ( adminAuthSrv.isSuperAdmin(account.getId()) || |
||||
|
adminAuthSrv.hasPermission(account.getId(), actionKey)) { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
@ -0,0 +1,62 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
* 新福利计划: |
||||
|
* https://jfinal.com/club/1-2
|
||||
|
* |
||||
|
* JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于创建更加高品质的产品与服务,为你带来 |
||||
|
* 更多、更大的价值 |
||||
|
* |
||||
|
* JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
*/ |
||||
|
|
||||
|
package com.bt._admin.auth; |
||||
|
|
||||
|
import com.jfinal.kit.Kv; |
||||
|
import com.jfinal.kit.StrKit; |
||||
|
import com.jfinal.plugin.activerecord.Db; |
||||
|
|
||||
|
/** |
||||
|
* 后台管理员授权业务 |
||||
|
*/ |
||||
|
public class AdminAuthService { |
||||
|
|
||||
|
/** |
||||
|
* 是否为超级管理员,role.id 值为 1 的为超级管理员 |
||||
|
*/ |
||||
|
public boolean isSuperAdmin(int accountId) { |
||||
|
Integer ret = Db.template("admin.auth.isSuperAdmin", accountId).queryInt(); |
||||
|
return ret != null; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 当前账号是否拥有某些角色 |
||||
|
*/ |
||||
|
public boolean hasRole(int accountId, String[] roleNameArray) { |
||||
|
if (roleNameArray == null || roleNameArray.length == 0) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
Kv data = Kv.by("accountId", accountId).set("roleNameArray", roleNameArray); |
||||
|
Integer ret = Db.template("admin.auth.hasRole", data).queryInt(); |
||||
|
return ret != null; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 是否拥有具体某个权限 |
||||
|
*/ |
||||
|
public boolean hasPermission(int accountId, String actionKey) { |
||||
|
if (StrKit.isBlank(actionKey)) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
Integer ret = Db.template("admin.auth.hasPermission", actionKey, accountId).queryInt(); |
||||
|
return ret != null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,34 @@ |
|||||
|
### 验证是否为超级管理员, 超级管理员的 roleId 值为固定为 1 |
||||
|
#sql("isSuperAdmin") |
||||
|
select accountId from account_role |
||||
|
where accountId = #para(0) and roleId = 1 |
||||
|
limit 1 |
||||
|
#end |
||||
|
|
||||
|
|
||||
|
### 验证是否拥有某个 role |
||||
|
#sql("hasRole") |
||||
|
select ar.accountId from account_role ar |
||||
|
inner join role r on ar.roleId = r.id |
||||
|
where ar.accountId = #para(accountId) |
||||
|
and ( |
||||
|
#for (x : roleNameArray) |
||||
|
#(for.first ? "" : "or") r.name = #para(x.trim()) |
||||
|
#end |
||||
|
) |
||||
|
limit 1 |
||||
|
#end |
||||
|
|
||||
|
|
||||
|
### 验证是否拥有某个 permission |
||||
|
#sql("hasPermission") |
||||
|
select ar.accountId from ( |
||||
|
select rp.roleId from role_permission rp |
||||
|
inner join permission p on rp.permissionId = p.id |
||||
|
where p.actionKey = #para(0) |
||||
|
) |
||||
|
as t inner join account_role ar on t.roleId = ar.roleId |
||||
|
where ar.accountId = #para(1) |
||||
|
limit 1 |
||||
|
#end |
||||
|
|
||||
@ -0,0 +1,39 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
* 新福利计划: |
||||
|
* https://jfinal.com/club/1-2
|
||||
|
* |
||||
|
* JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于创建更加高品质的产品与服务,为你带来 |
||||
|
* 更多、更大的价值 |
||||
|
* |
||||
|
* JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
*/ |
||||
|
|
||||
|
package com.bt._admin.common; |
||||
|
|
||||
|
import com.jfinal.aop.Interceptor; |
||||
|
import com.jfinal.aop.Invocation; |
||||
|
import com.jfinal.core.Controller; |
||||
|
|
||||
|
/** |
||||
|
* 设置 pjax 标志 |
||||
|
*/ |
||||
|
public class PjaxInterceptor implements Interceptor { |
||||
|
|
||||
|
@Override |
||||
|
public void intercept(Invocation inv) { |
||||
|
try { |
||||
|
inv.invoke(); |
||||
|
} finally { |
||||
|
Controller c = inv.getController(); |
||||
|
boolean isPjax = "true".equalsIgnoreCase(c.getHeader("X-PJAX")); |
||||
|
c.setAttr("isPjax", isPjax); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,103 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
* 新福利计划: |
||||
|
* https://jfinal.com/club/1-2
|
||||
|
* |
||||
|
* JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于创建更加高品质的产品与服务,为你带来 |
||||
|
* 更多、更大的价值 |
||||
|
* |
||||
|
* JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
*/ |
||||
|
|
||||
|
package com.bt._admin.document; |
||||
|
|
||||
|
import com.bt._admin.permission.Remark; |
||||
|
import com.jfinal.aop.Inject; |
||||
|
import com.bt.common.controller.BaseController; |
||||
|
import com.jfinal.kit.Ret; |
||||
|
import com.bt.common.model.Document; |
||||
|
import com.jfinal.core.Path; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* 文档管理控制器 |
||||
|
* 暂不支持主菜单 doc 的显示,主菜单 doc 现在仅用于我自己来 todolist 和大纲 |
||||
|
*/ |
||||
|
@Path(value = "/admin/doc", viewPath = "/document") |
||||
|
public class DocumentAdminController extends BaseController { |
||||
|
|
||||
|
@Inject |
||||
|
DocumentAdminService srv; |
||||
|
|
||||
|
@Remark("文档管理首页") |
||||
|
public void index() { |
||||
|
List<Document> docList = srv.getDocList(); |
||||
|
setAttr("docList", docList); |
||||
|
render("index.html"); |
||||
|
} |
||||
|
|
||||
|
@Remark("创建文档") |
||||
|
public void add() { |
||||
|
List<Document> docList = srv.getDocList(); |
||||
|
setAttr("docList", docList); |
||||
|
render("add_edit.html"); |
||||
|
} |
||||
|
|
||||
|
@Remark("创建文档提交") |
||||
|
public void save() { |
||||
|
Document doc = getBean(Document.class, "doc"); |
||||
|
Ret ret = srv.save(doc); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
@Remark("修改文档") |
||||
|
public void edit() { |
||||
|
Document doc = srv.getByIds(getParaToInt("mainMenu"), getParaToInt("subMenu")); |
||||
|
if (doc == null) { |
||||
|
renderError(404); |
||||
|
} |
||||
|
setAttr("doc", doc); |
||||
|
render("add_edit.html"); |
||||
|
} |
||||
|
|
||||
|
@Remark("修改文档提交") |
||||
|
public void update() { |
||||
|
Document doc = getBean(Document.class, "doc"); |
||||
|
Ret ret = srv.update(getParaToInt("oldMainMenu"), getParaToInt("oldSubMenu"), doc); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
@Remark("删除文档") |
||||
|
public void delete() { |
||||
|
Ret ret = srv.delete(getParaToInt("mainMenu"), getParaToInt("subMenu")); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 发布 |
||||
|
*/ |
||||
|
@Remark("发布文档") |
||||
|
public void publish() { |
||||
|
Ret ret = srv.publish(getParaToInt("mainMenu"), getParaToInt("subMenu")); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 取消发布,成为草稿 |
||||
|
*/ |
||||
|
@Remark("取消发布文档") |
||||
|
public void unpublish() { |
||||
|
Ret ret = srv.unpublish(getParaToInt("mainMenu"), getParaToInt("subMenu")); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
@ -0,0 +1,121 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
* 新福利计划: |
||||
|
* https://jfinal.com/club/1-2
|
||||
|
* |
||||
|
* JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于创建更加高品质的产品与服务,为你带来 |
||||
|
* 更多、更大的价值 |
||||
|
* |
||||
|
* JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
*/ |
||||
|
|
||||
|
package com.bt._admin.document; |
||||
|
|
||||
|
import com.jfinal.plugin.activerecord.Db; |
||||
|
import com.jfinal.kit.Ret; |
||||
|
import com.jfinal.aop.Inject; |
||||
|
import com.bt.common.model.Document; |
||||
|
import com.bt.document.DocumentService; |
||||
|
|
||||
|
import java.time.LocalDateTime; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* document 管理业务 |
||||
|
*/ |
||||
|
public class DocumentAdminService { |
||||
|
|
||||
|
@Inject |
||||
|
DocumentService documentSrv; |
||||
|
|
||||
|
private Document dao = new Document().dao(); |
||||
|
|
||||
|
// 加载一级文档,即便是 publish 为 0 的也加载
|
||||
|
public List<Document> getDocList() { |
||||
|
List<Document> docList = dao.find("select * from document where subMenu = 0 order by mainMenu asc"); |
||||
|
for (Document pDoc : docList) { |
||||
|
loadSubDocList(pDoc); |
||||
|
} |
||||
|
return docList; |
||||
|
} |
||||
|
|
||||
|
// 加载二级文档,文档最多分两级目录,三级甚至更多级目录直接在 content 中体现
|
||||
|
private void loadSubDocList(Document pDoc) { |
||||
|
int mainMenu = pDoc.getMainMenu(); |
||||
|
String sql = "select * from document where mainMenu = ? and subMenu > 0 order by subMenu asc"; |
||||
|
List<Document> subDocList = dao.find(sql, mainMenu); |
||||
|
pDoc.put("subDocList", subDocList); |
||||
|
} |
||||
|
|
||||
|
public Document getByIds(int mainMenu, int subMenu) { |
||||
|
return dao.findByIds(mainMenu, subMenu); |
||||
|
} |
||||
|
|
||||
|
public Ret save(Document doc) { |
||||
|
if (isExists(doc)) { |
||||
|
return Ret.fail("msg", "mainMenu 与 subMenu 组合已经存在"); |
||||
|
} |
||||
|
doc.setCreateAt(LocalDateTime.now()); |
||||
|
doc.setUpdateAt(LocalDateTime.now()); |
||||
|
doc.save(); |
||||
|
documentSrv.clearCache(); // 清缓存
|
||||
|
return Ret.ok(); |
||||
|
} |
||||
|
|
||||
|
public Ret update(int oldMainMenu, int oldSubMenu, Document doc) { |
||||
|
// 当 mainMenu 或 subMenu 值也被修改的时候,判断一下新值是否已经存在
|
||||
|
if (oldMainMenu != doc.getMainMenu() || oldSubMenu != doc.getSubMenu()) { |
||||
|
if (isExists(doc)) { |
||||
|
return Ret.fail("msg", "mainMenu 或 subMenu 已经存在,不能使用"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (oldMainMenu != doc.getMainMenu() || oldSubMenu != doc.getSubMenu()) { |
||||
|
Db.update("update document set mainMenu=?, subMenu=? where mainMenu=? and subMenu=?", |
||||
|
doc.getMainMenu(), doc.getSubMenu(), oldMainMenu, oldSubMenu); |
||||
|
} |
||||
|
doc.setUpdateAt(LocalDateTime.now()); |
||||
|
doc.update(); |
||||
|
|
||||
|
documentSrv.clearCache(); // 清缓存
|
||||
|
return Ret.ok(); |
||||
|
} |
||||
|
|
||||
|
public Ret delete(int mainMenu, int subMenu) { |
||||
|
Db.update("delete from document where mainMenu=? and subMenu=? limit 1", mainMenu, subMenu); |
||||
|
documentSrv.clearCache(); // 清缓存
|
||||
|
|
||||
|
return Ret.ok("msg", "document 删除成功"); |
||||
|
} |
||||
|
|
||||
|
private boolean isExists(Document doc) { |
||||
|
String sql = "select mainMenu from document where mainMenu=? and subMenu=? limit 1"; |
||||
|
return Db.queryInt(sql , doc.getMainMenu(), doc.getSubMenu()) != null; |
||||
|
} |
||||
|
|
||||
|
final String publishSql = "update document set publish = ? where mainMenu=? and subMenu=?"; |
||||
|
|
||||
|
/** |
||||
|
* 发布 |
||||
|
*/ |
||||
|
public Ret publish(int mainMenu, int subMenu) { |
||||
|
Db.update(publishSql, Document.PUBLISH_YES, mainMenu, subMenu); |
||||
|
documentSrv.clearCache(); // 清缓存
|
||||
|
return Ret.ok("msg", "发布成功"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 取消发布,变草稿 |
||||
|
*/ |
||||
|
public Ret unpublish(int mainMenu, int subMenu) { |
||||
|
Db.update(publishSql, Document.PUBLISH_NO, mainMenu, subMenu); |
||||
|
documentSrv.clearCache(); // 清缓存
|
||||
|
return Ret.ok("msg", "取消发布成功"); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,168 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
* 新福利计划: |
||||
|
* https://jfinal.com/club/1-2
|
||||
|
* |
||||
|
* JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于创建更加高品质的产品与服务,为你带来 |
||||
|
* 更多、更大的价值 |
||||
|
* |
||||
|
* JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
*/ |
||||
|
|
||||
|
package com.bt._admin.feedback; |
||||
|
|
||||
|
import com.jfinal.aop.Before; |
||||
|
import com.jfinal.aop.Inject; |
||||
|
import com.bt.common.account.AccountService; |
||||
|
import com.bt.my.feedback.MyFeedbackValidator; |
||||
|
import com.bt.project.ProjectService; |
||||
|
import com.jfinal.core.Path; |
||||
|
import com.jfinal.plugin.activerecord.Page; |
||||
|
import com.bt.common.controller.BaseController; |
||||
|
import com.jfinal.kit.Ret; |
||||
|
import com.bt.common.model.Feedback; |
||||
|
import com.bt.common.model.FeedbackReply; |
||||
|
import com.bt.feedback.FeedbackService; |
||||
|
import com.bt.index.IndexService; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* 反馈管理控制器 |
||||
|
* |
||||
|
* 注意:sql 语句与业务逻辑要写在业务层,在此仅由于时间仓促偷懒的做法 |
||||
|
* 后续版本会改掉这样的用法,请小伙伴们不要效仿 |
||||
|
*/ |
||||
|
@Path(value = "/admin/feedback", viewPath = "/feedback") |
||||
|
public class FeedbackAdminController extends BaseController { |
||||
|
|
||||
|
@Inject |
||||
|
FeedbackAdminService srv; |
||||
|
|
||||
|
@Inject |
||||
|
FeedbackService feedbackSrv; |
||||
|
|
||||
|
@Inject |
||||
|
ProjectService projectSrv; |
||||
|
|
||||
|
@Inject |
||||
|
AccountService accountSrv; |
||||
|
|
||||
|
@Inject |
||||
|
IndexService indexSrv; |
||||
|
|
||||
|
public void index() { |
||||
|
Page<Feedback> feedbackPage = srv.paginate(getParaToInt("p", 1)); |
||||
|
setAttr("feedbackPage", feedbackPage); |
||||
|
render("index.html"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 创建 |
||||
|
*/ |
||||
|
public void add() { |
||||
|
setAttr("projectList", projectSrv.getAllProject("id, name")); // 关联项目下拉列表
|
||||
|
render("add_edit.html"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 提交创建 |
||||
|
*/ |
||||
|
@Before(MyFeedbackValidator.class) |
||||
|
public void save() { |
||||
|
Feedback feedback = getBean(Feedback.class); |
||||
|
Ret ret = srv.save(getLoginAccountId(), feedback); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 修改 |
||||
|
*/ |
||||
|
public void edit() { |
||||
|
keepPara("p"); // 保持住分页的页号,便于在 ajax 提交后跳转到当前数据所在的页
|
||||
|
setAttr("projectList", projectSrv.getAllProject("id, name")); // 关联项目下拉列表
|
||||
|
setAttr("feedback", srv.edit(getParaToInt("id"))); |
||||
|
render("add_edit.html"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 提交修改 |
||||
|
*/ |
||||
|
@Before(MyFeedbackValidator.class) |
||||
|
public void update() { |
||||
|
Feedback feedback = getBean(Feedback.class); |
||||
|
Ret ret = srv.update(feedback); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 锁定,目前先做成使用举报量 report 值锁定 |
||||
|
*/ |
||||
|
public void lock() { |
||||
|
Ret ret = srv.lock(getParaToInt("id")); |
||||
|
|
||||
|
feedbackSrv.clearHotFeedbackCache(); // 清缓存
|
||||
|
indexSrv.clearCache(); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 解除锁定 |
||||
|
*/ |
||||
|
public void unlock() { |
||||
|
Ret ret = srv.unlock(getParaToInt("id")); |
||||
|
|
||||
|
feedbackSrv.clearHotFeedbackCache(); // 清缓存
|
||||
|
indexSrv.clearCache(); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除 feedback |
||||
|
*/ |
||||
|
public void delete() { |
||||
|
Ret ret = srv.delete(getParaToInt("id")); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取 feedback 的 reply 列表 |
||||
|
*/ |
||||
|
public void getReplyList() { |
||||
|
int feedbackId = getParaToInt("feedbackId"); |
||||
|
Feedback feedback = srv.getById(feedbackId); |
||||
|
List<FeedbackReply> feedbackReplyList = srv.getReplyList(feedbackId); |
||||
|
accountSrv.join("accountId", feedbackReplyList, "nickName"); |
||||
|
|
||||
|
setAttr("feedback", feedback); |
||||
|
setAttr("feedbackReplyList", feedbackReplyList); |
||||
|
setAttr("feedbackId", feedbackId); |
||||
|
render("reply.html"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取 feedback 的 reply |
||||
|
*/ |
||||
|
public void getReply() { |
||||
|
int replyId = getParaToInt("replyId"); |
||||
|
Ret ret = srv.getReply(replyId); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除 feedback reply |
||||
|
*/ |
||||
|
public void deleteReply() { |
||||
|
int replyId = getParaToInt("replyId"); |
||||
|
Ret ret = srv.deleteReply(replyId); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
@ -0,0 +1,121 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
* 新福利计划: |
||||
|
* https://jfinal.com/club/1-2
|
||||
|
* |
||||
|
* JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于创建更加高品质的产品与服务,为你带来 |
||||
|
* 更多、更大的价值 |
||||
|
* |
||||
|
* JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
*/ |
||||
|
|
||||
|
package com.bt._admin.feedback; |
||||
|
|
||||
|
import com.jfinal.aop.Inject; |
||||
|
import com.bt.common.model.Feedback; |
||||
|
import com.jfinal.kit.Ret; |
||||
|
import com.jfinal.plugin.activerecord.Db; |
||||
|
import com.bt.common.model.FeedbackReply; |
||||
|
import com.bt.my.feedback.MyFeedbackService; |
||||
|
import com.jfinal.plugin.activerecord.Page; |
||||
|
|
||||
|
import java.time.LocalDateTime; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* feedback 管理业务 |
||||
|
*/ |
||||
|
public class FeedbackAdminService { |
||||
|
|
||||
|
@Inject |
||||
|
MyFeedbackService myFeedbackSrv; |
||||
|
|
||||
|
private Feedback dao = new Feedback().dao(); |
||||
|
private FeedbackReply feedbackReplyDao = new FeedbackReply().dao(); |
||||
|
|
||||
|
/** |
||||
|
* feedback 分页 |
||||
|
*/ |
||||
|
public Page<Feedback> paginate(int pageNum) { |
||||
|
return dao.paginate(pageNum, 10, "select *", "from feedback order by id desc"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 创建反馈 |
||||
|
*/ |
||||
|
public Ret save(int accountId, Feedback feedback) { |
||||
|
feedback.setAccountId(accountId); |
||||
|
feedback.setTitle(feedback.getTitle().trim()); |
||||
|
feedback.setCreateAt(LocalDateTime.now()); |
||||
|
feedback.save(); |
||||
|
return Ret.ok("msg", "创建成功"); |
||||
|
} |
||||
|
|
||||
|
public Feedback edit(int id) { |
||||
|
return dao.findById(id); |
||||
|
} |
||||
|
|
||||
|
public Ret update(Feedback feedback) { |
||||
|
feedback.update(); |
||||
|
return Ret.ok("msg", "修改成功"); |
||||
|
} |
||||
|
|
||||
|
public Ret lock(int id) { |
||||
|
Db.update("update feedback set report = report + ? where id=?", Feedback.REPORT_BLOCK_NUM, id); |
||||
|
return Ret.ok("msg", "锁定成功"); |
||||
|
} |
||||
|
|
||||
|
public Ret unlock(int id) { |
||||
|
Db.update("update feedback set report = 0 where id=?", id); |
||||
|
return Ret.ok("msg", "解除锁定成功"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除 feedback |
||||
|
*/ |
||||
|
public Ret delete(int feedbackId) { |
||||
|
Integer accountId = Db.queryInt("select accountId from feedback where id=? limit 1", feedbackId); |
||||
|
if (accountId != null) { |
||||
|
myFeedbackSrv.delete(accountId, feedbackId); |
||||
|
return Ret.ok("msg", "feedback 删除成功"); |
||||
|
} else { |
||||
|
return Ret.fail("msg", "feedback 删除失败"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public Feedback getById(int feedbackId) { |
||||
|
return dao.findById(feedbackId); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取 reply list |
||||
|
*/ |
||||
|
public List<FeedbackReply> getReplyList(int feedbackId) { |
||||
|
String sql = "select id, accountId, createAt, substring(content, 1, 30) as content from feedback_reply where feedbackId=? order by id desc"; |
||||
|
return feedbackReplyDao.find(sql, feedbackId); |
||||
|
} |
||||
|
|
||||
|
public Ret getReply(int replyId) { |
||||
|
FeedbackReply reply = feedbackReplyDao.findById(replyId); |
||||
|
return Ret.ok("reply", reply); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除 feedback reply |
||||
|
*/ |
||||
|
public Ret deleteReply(int feedbackReplyId) { |
||||
|
Integer accountId = Db.queryInt("select accountId from feedback_reply where id=? limit 1", feedbackReplyId); |
||||
|
if (accountId != null) { |
||||
|
myFeedbackSrv.deleteFeedbackReplyById(accountId, feedbackReplyId); |
||||
|
return Ret.ok("msg", "feedback reply 删除成功"); |
||||
|
} else { |
||||
|
return Ret.fail("msg", "feedback reply 删除失败"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,42 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
* 新福利计划: |
||||
|
* https://jfinal.com/club/1-2
|
||||
|
* |
||||
|
* JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于创建更加高品质的产品与服务,为你带来 |
||||
|
* 更多、更大的价值 |
||||
|
* |
||||
|
* JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
*/ |
||||
|
|
||||
|
package com.bt._admin.index; |
||||
|
|
||||
|
import com.jfinal.aop.Inject; |
||||
|
import com.bt.common.controller.BaseController; |
||||
|
import com.jfinal.core.Path; |
||||
|
|
||||
|
/** |
||||
|
* 后台管理首页 |
||||
|
*/ |
||||
|
@Path(value = "/admin", viewPath = "/index") |
||||
|
public class IndexAdminController extends BaseController { |
||||
|
|
||||
|
@Inject |
||||
|
IndexAdminService srv; |
||||
|
|
||||
|
public void index() { |
||||
|
setAttr("accountProfile", srv.getAccountProfile()); |
||||
|
setAttr("projectProfile", srv.getProjectProfile()); |
||||
|
setAttr("shareProfile", srv.getShareProfile()); |
||||
|
setAttr("feedbackProfile", srv.getFeedbackProfile()); |
||||
|
setAttr("permissionProfile", srv.getPermissionProfile()); |
||||
|
|
||||
|
render("index.html"); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,62 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
* 新福利计划: |
||||
|
* https://jfinal.com/club/1-2
|
||||
|
* |
||||
|
* JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于创建更加高品质的产品与服务,为你带来 |
||||
|
* 更多、更大的价值 |
||||
|
* |
||||
|
* JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
*/ |
||||
|
|
||||
|
package com.bt._admin.index; |
||||
|
|
||||
|
import com.jfinal.kit.Ret; |
||||
|
import com.jfinal.plugin.activerecord.Db; |
||||
|
|
||||
|
/** |
||||
|
* 首页业务 |
||||
|
*/ |
||||
|
public class IndexAdminService { |
||||
|
|
||||
|
public Ret getAccountProfile() { |
||||
|
Ret ret = Ret.by("title", "账户总数"); |
||||
|
Integer total = Db.queryInt("select count(*) from account"); |
||||
|
ret.set("total", total); |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
public Ret getProjectProfile() { |
||||
|
Ret ret = Ret.by("title", "项目总数"); |
||||
|
Integer total = Db.queryInt("select count(*) from project"); |
||||
|
ret.set("total", total); |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
public Ret getShareProfile() { |
||||
|
Ret ret = Ret.by("title", "分享总数"); |
||||
|
Integer total = Db.queryInt("select count(*) from share"); |
||||
|
ret.set("total", total); |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
public Ret getFeedbackProfile() { |
||||
|
Ret ret = Ret.by("title", "反馈总数"); |
||||
|
Integer total = Db.queryInt("select count(*) from feedback"); |
||||
|
ret.set("total", total); |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
public Ret getPermissionProfile() { |
||||
|
Ret ret = Ret.by("title", "权限总数"); |
||||
|
Integer total = Db.queryInt("select count(*) from permission"); |
||||
|
ret.set("total", total); |
||||
|
return ret; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,66 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
* 新福利计划: |
||||
|
* https://jfinal.com/club/1-2
|
||||
|
* |
||||
|
* JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于创建更加高品质的产品与服务,为你带来 |
||||
|
* 更多、更大的价值 |
||||
|
* |
||||
|
* JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
*/ |
||||
|
|
||||
|
package com.bt._admin.permission; |
||||
|
|
||||
|
import com.jfinal.aop.Inject; |
||||
|
import com.bt.common.controller.BaseController; |
||||
|
import com.bt.common.model.Permission; |
||||
|
import com.jfinal.core.Path; |
||||
|
import com.jfinal.kit.Ret; |
||||
|
import com.jfinal.plugin.activerecord.Page; |
||||
|
|
||||
|
/** |
||||
|
* 权限管理 |
||||
|
*/ |
||||
|
@Path(value = "/admin/permission", viewPath = "/permission") |
||||
|
public class PermissionAdminController extends BaseController { |
||||
|
|
||||
|
@Inject |
||||
|
PermissionAdminService srv; |
||||
|
|
||||
|
public void index() { |
||||
|
Page<Permission> permissionPage = srv.paginate(getParaToInt("p", 1)); |
||||
|
srv.replaceControllerPrefix(permissionPage, "com.jfinal.club._admin.", "..."); |
||||
|
boolean hasRemovedPermission = srv.markRemovedActionKey(permissionPage); |
||||
|
setAttr("permissionPage", permissionPage); |
||||
|
setAttr("hasRemovedPermission", hasRemovedPermission); |
||||
|
render("index.html"); |
||||
|
} |
||||
|
|
||||
|
public void sync() { |
||||
|
Ret ret = srv.sync(); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
public void edit() { |
||||
|
keepPara("p"); // 保持住分页的页号,便于在 ajax 提交后跳转到当前数据所在的页
|
||||
|
Permission permission = srv.findById(getParaToInt("id")); |
||||
|
setAttr("permission", permission); |
||||
|
render("edit.html"); |
||||
|
} |
||||
|
|
||||
|
public void update() { |
||||
|
Ret ret = srv.update(getBean(Permission.class)); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
public void delete() { |
||||
|
Ret ret = srv.delete(getParaToInt("id")); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,177 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
* 新福利计划: |
||||
|
* https://jfinal.com/club/1-2
|
||||
|
* |
||||
|
* JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于创建更加高品质的产品与服务,为你带来 |
||||
|
* 更多、更大的价值 |
||||
|
* |
||||
|
* JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
*/ |
||||
|
|
||||
|
package com.bt._admin.permission; |
||||
|
|
||||
|
import com.bt.common.model.Permission; |
||||
|
import com.jfinal.core.Action; |
||||
|
import com.jfinal.core.JFinal; |
||||
|
import com.jfinal.kit.Ret; |
||||
|
import com.jfinal.kit.StrKit; |
||||
|
import com.jfinal.plugin.activerecord.Db; |
||||
|
import com.jfinal.plugin.activerecord.Page; |
||||
|
import java.util.HashSet; |
||||
|
import java.util.List; |
||||
|
import java.util.Set; |
||||
|
|
||||
|
/** |
||||
|
* 权限管理业务 |
||||
|
*/ |
||||
|
public class PermissionAdminService { |
||||
|
|
||||
|
private Permission dao = new Permission().dao(); |
||||
|
|
||||
|
// 一键同步功能需要排除掉的 actionKey
|
||||
|
private Set<String> excludedActionKey = buildExcludedActionKey(); |
||||
|
|
||||
|
/** |
||||
|
* 一键同步功能需要排除的 actionKey,在本方法中添加 |
||||
|
*/ |
||||
|
private Set<String> buildExcludedActionKey() { |
||||
|
Set<String> ret = new HashSet<String>(); |
||||
|
// ret.add("/admin/captcha");
|
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
public Page<Permission> paginate(int pageNum) { |
||||
|
return dao.paginate(pageNum, 10, "select *", "from permission order by actionKey asc"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 在已被移除的 permission 中 put 进去一个 removed 值为 true 的标记,便于在界面显示不同的样式 |
||||
|
* @return 存在被删除的 actionKey 时返回 true,否则返回 false |
||||
|
*/ |
||||
|
public boolean markRemovedActionKey(Page<Permission> permissionPage) { |
||||
|
boolean ret = false; |
||||
|
|
||||
|
for (Permission p : permissionPage.getList()) { |
||||
|
String actionKey = p.getActionKey(); |
||||
|
|
||||
|
String[] urlPara = new String[1]; |
||||
|
Action action = JFinal.me().getAction(actionKey, urlPara); |
||||
|
if (action == null || ! actionKey.equals(action.getActionKey())) { |
||||
|
p.put("removed", true); |
||||
|
ret = true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 替换控制器前缀,界面显示时更加美观 |
||||
|
* |
||||
|
* 例子: |
||||
|
* replaceControllerPrefix(permissionPage, "com.jfinal.club._admin.", "..."); |
||||
|
* 以上例子将 "com.jfinal.club._admin." 这一长串前缀替换成 "...",显示更美观 |
||||
|
*/ |
||||
|
public void replaceControllerPrefix(Page<Permission> permissionPage, String replaceTarget, String replacement) { |
||||
|
for (Permission p : permissionPage.getList()) { |
||||
|
String c = p.getController().replace(replaceTarget, replacement); |
||||
|
p.setController(c); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 同步 permission |
||||
|
* 获取后台管理所有 actionKey 以及 controller,将数据自动写入 permission 表 |
||||
|
* 随着开发过程的前行,可以动态进行同步添加新的 permission 数据 |
||||
|
*/ |
||||
|
public Ret sync() { |
||||
|
int counter = 0; |
||||
|
List<String> allActionKeys = JFinal.me().getAllActionKeys(); |
||||
|
for (String actionKey : allActionKeys) { |
||||
|
// 只处理后台管理 action,其它跳过
|
||||
|
if ( !actionKey.startsWith("/admin") ) { |
||||
|
continue ; |
||||
|
} |
||||
|
// 跳过需要排除的 actionKey
|
||||
|
if (excludedActionKey.contains(actionKey)) { |
||||
|
continue ; |
||||
|
} |
||||
|
|
||||
|
String[] urlPara = new String[1]; |
||||
|
Action action = JFinal.me().getAction(actionKey, urlPara); |
||||
|
// 没有拦截器的 action 任何人都能访问,需要排除掉,例如:"/admin/login" "/admin/logout"
|
||||
|
if (action == null || action.getInterceptors().length == 0) { |
||||
|
continue ; |
||||
|
} |
||||
|
|
||||
|
String controller = action.getControllerClass().getName(); |
||||
|
|
||||
|
String sql = "select * from permission where actionKey=? and controller = ? limit 1"; |
||||
|
Permission permission = dao.findFirst(sql, actionKey, controller); |
||||
|
|
||||
|
if (permission == null) { |
||||
|
permission = new Permission(); |
||||
|
permission.setActionKey(actionKey); |
||||
|
permission.setController(controller); |
||||
|
setRemarkValue(permission, action); |
||||
|
permission.save(); |
||||
|
counter++; |
||||
|
} else { |
||||
|
// 如果 remark 字段是空值,才去尝试使用 @Remark 注解中的值
|
||||
|
if (StrKit.isBlank(permission.getRemark())) { |
||||
|
setRemarkValue(permission, action); |
||||
|
if (permission.update()) { |
||||
|
counter++; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (counter == 0) { |
||||
|
return Ret.ok("msg", "权限已经是最新状态,无需更新"); |
||||
|
} else { |
||||
|
return Ret.ok("msg", "权限更新成功,共更新权限数 : " + counter); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void setRemarkValue(Permission permission, Action action) { |
||||
|
Remark remark = action.getMethod().getAnnotation(Remark.class); |
||||
|
if (remark != null && StrKit.notBlank(remark.value())) { |
||||
|
permission.setRemark(remark.value()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public Permission findById(int id) { |
||||
|
return dao.findById(id); |
||||
|
} |
||||
|
|
||||
|
public List<Permission> getAllPermissions() { |
||||
|
return dao.find("select * from permission order by controller asc"); |
||||
|
} |
||||
|
|
||||
|
public Ret update(Permission permission) { |
||||
|
permission.keep("id", "remark"); // 暂时只允许更新 remark
|
||||
|
permission.update(); |
||||
|
return Ret.ok("msg", "更新成功"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 如果某个 action 已经删掉,或者改了名称,可以使用该方法删除 |
||||
|
*/ |
||||
|
public Ret delete(final int permissionId) { |
||||
|
Db.tx(() -> { |
||||
|
Db.delete("delete from role_permission where permissionId = ?", permissionId); |
||||
|
dao.deleteById(permissionId); |
||||
|
return true; |
||||
|
}); |
||||
|
|
||||
|
return Ret.ok("msg", "权限删除成功"); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,72 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
* 新福利计划: |
||||
|
* https://jfinal.com/club/1-2
|
||||
|
* |
||||
|
* JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于创建更加高品质的产品与服务,为你带来 |
||||
|
* 更多、更大的价值 |
||||
|
* |
||||
|
* JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
*/ |
||||
|
|
||||
|
package com.bt._admin.permission; |
||||
|
|
||||
|
import com.jfinal.aop.Aop; |
||||
|
import com.bt._admin.auth.AdminAuthService; |
||||
|
import com.bt.common.model.Account; |
||||
|
import com.bt.login.LoginService; |
||||
|
import com.jfinal.template.Directive; |
||||
|
import com.jfinal.template.Env; |
||||
|
import com.jfinal.template.io.Writer; |
||||
|
import com.jfinal.template.stat.Scope; |
||||
|
|
||||
|
/** |
||||
|
* 界面上的权限控制功能 |
||||
|
* 用来控制界面上的菜单、按钮等等元素的显示 |
||||
|
* |
||||
|
* 使用示例见模板文件: /_view/_admin/project/index.html 或者 /_view/_admin/permission/index.html |
||||
|
* #permission("/admin/project/edit") |
||||
|
* <a href="/admin/project/edit?id=#(x.id)"> |
||||
|
* <i class="fa fa-pencil" title="修改"></i> |
||||
|
* </a> |
||||
|
* #end |
||||
|
* |
||||
|
* 别名: #perm() #end |
||||
|
*/ |
||||
|
public class PermissionDirective extends Directive { |
||||
|
|
||||
|
static AdminAuthService adminAuthSrv = Aop.get(AdminAuthService.class); |
||||
|
|
||||
|
public void exec(Env env, Scope scope, Writer writer) { |
||||
|
Account account = (Account)scope.getRootData().get(LoginService.loginAccountCacheName); |
||||
|
if (account != null && account.isStatusOk()) { |
||||
|
// 如果是超级管理员,或者拥有指定的权限则放行
|
||||
|
if ( adminAuthSrv.isSuperAdmin(account.getId()) || |
||||
|
adminAuthSrv.hasPermission(account.getId(), getPermission(scope))) { |
||||
|
stat.exec(env, scope, writer); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 从 #permission 指令参数中获取 permission |
||||
|
*/ |
||||
|
private String getPermission(Scope scope) { |
||||
|
Object value = exprList.eval(scope); |
||||
|
if (value instanceof String) { |
||||
|
return (String)value; |
||||
|
} else { |
||||
|
throw new IllegalArgumentException("权限参数只能为 String 类型"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public boolean hasEnd() { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
* 新福利计划: |
||||
|
* https://jfinal.com/club/1-2
|
||||
|
* |
||||
|
* JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于创建更加高品质的产品与服务,为你带来 |
||||
|
* 更多、更大的价值 |
||||
|
* |
||||
|
* JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
*/ |
||||
|
|
||||
|
package com.bt._admin.permission; |
||||
|
|
||||
|
import java.lang.annotation.*; |
||||
|
|
||||
|
/** |
||||
|
* 权限一键同步功能时,自动向 permission 表的 remark 字段中 |
||||
|
* 添加 @Remark 注解的内容,注意只在 remark 字段为空时才添加 |
||||
|
* 否则会覆盖掉用户自己添加的内容 |
||||
|
*/ |
||||
|
@Inherited |
||||
|
@Retention(RetentionPolicy.RUNTIME) |
||||
|
@Target({ElementType.METHOD}) |
||||
|
public @interface Remark { |
||||
|
String value(); |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
|
||||
|
|
||||
|
|
||||
|
role(id, name) |
||||
|
permission(id, actionKey, controller, remark) |
||||
|
|
||||
|
account_role(accountId, roleId) |
||||
|
role_permission(roleId, permissionId) |
||||
|
|
||||
|
|
||||
@ -0,0 +1,119 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
* 新福利计划: |
||||
|
* https://jfinal.com/club/1-2
|
||||
|
* |
||||
|
* JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于创建更加高品质的产品与服务,为你带来 |
||||
|
* 更多、更大的价值 |
||||
|
* |
||||
|
* JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
*/ |
||||
|
|
||||
|
package com.bt._admin.project; |
||||
|
|
||||
|
import com.jfinal.aop.Before; |
||||
|
import com.jfinal.aop.Inject; |
||||
|
import com.bt.my.project.MyProjectValidator; |
||||
|
import com.jfinal.plugin.activerecord.Page; |
||||
|
import com.bt.common.controller.BaseController; |
||||
|
import com.jfinal.kit.Ret; |
||||
|
import com.bt.common.model.Project; |
||||
|
import com.bt.index.IndexService; |
||||
|
import com.bt.project.ProjectService; |
||||
|
import com.jfinal.core.Path; |
||||
|
|
||||
|
/** |
||||
|
* 项目管理控制器 |
||||
|
*/ |
||||
|
@Path(value = "/admin/project", viewPath = "/project") |
||||
|
public class ProjectAdminController extends BaseController { |
||||
|
|
||||
|
@Inject |
||||
|
ProjectAdminService srv; |
||||
|
|
||||
|
@Inject |
||||
|
ProjectService projectSrv; |
||||
|
|
||||
|
@Inject |
||||
|
IndexService indexSrv; |
||||
|
|
||||
|
public void index() { |
||||
|
Page<Project> projectPage = srv.paginate(getParaToInt("p", 1)); |
||||
|
setAttr("projectPage", projectPage); |
||||
|
render("index.html"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 创建 |
||||
|
*/ |
||||
|
public void add() { |
||||
|
render("add_edit.html"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 提交创建 |
||||
|
*/ |
||||
|
@Before(MyProjectValidator.class) |
||||
|
public void save() { |
||||
|
Project project = getBean(Project.class); |
||||
|
Ret ret = srv.save(getLoginAccountId(), project); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 修改 |
||||
|
*/ |
||||
|
public void edit() { |
||||
|
keepPara("p"); // 保持住分页的页号,便于在 ajax 提交后跳转到当前数据所在的页
|
||||
|
setAttr("project", srv.edit(getParaToInt("id"))); |
||||
|
render("add_edit.html"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 提交修改 |
||||
|
*/ |
||||
|
@Before(MyProjectValidator.class) |
||||
|
public void update() { |
||||
|
Project project = getBean(Project.class); |
||||
|
Ret ret = srv.update(project); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 锁定 |
||||
|
*/ |
||||
|
public void lock() { |
||||
|
Ret ret = srv.lock(getParaToInt("id")); |
||||
|
|
||||
|
projectSrv.clearHotProjectCache(); |
||||
|
indexSrv.clearCache(); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 解除锁定 |
||||
|
*/ |
||||
|
public void unlock() { |
||||
|
Ret ret = srv.unlock(getParaToInt("id")); |
||||
|
|
||||
|
projectSrv.clearHotProjectCache(); |
||||
|
indexSrv.clearCache(); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除 project |
||||
|
*/ |
||||
|
public void delete() { |
||||
|
Ret ret = srv.delete(getParaToInt("id")); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
@ -0,0 +1,115 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
* 新福利计划: |
||||
|
* https://jfinal.com/club/1-2
|
||||
|
* |
||||
|
* JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于创建更加高品质的产品与服务,为你带来 |
||||
|
* 更多、更大的价值 |
||||
|
* |
||||
|
* JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
*/ |
||||
|
|
||||
|
package com.bt._admin.project; |
||||
|
|
||||
|
import com.jfinal.aop.Inject; |
||||
|
import com.bt.common.model.Project; |
||||
|
import com.jfinal.kit.Ret; |
||||
|
import com.jfinal.plugin.activerecord.Db; |
||||
|
import com.bt.my.project.MyProjectService; |
||||
|
import com.jfinal.plugin.activerecord.Page; |
||||
|
|
||||
|
import java.time.LocalDateTime; |
||||
|
|
||||
|
/** |
||||
|
* project 管理业务 |
||||
|
*/ |
||||
|
public class ProjectAdminService { |
||||
|
|
||||
|
@Inject |
||||
|
MyProjectService myProjectSrv; |
||||
|
|
||||
|
private Project dao = new Project().dao(); |
||||
|
|
||||
|
/** |
||||
|
* 项目分页 |
||||
|
*/ |
||||
|
public Page<Project> paginate(int pageNum) { |
||||
|
return dao.paginate(pageNum, 10, "select *", "from project order by id desc"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 判断项目名称是否存在 |
||||
|
* @param projectId 当前 project 对象的 id 号,如果 project 对象还未创建,提供一个小于 0 的值即可 |
||||
|
* @param name 项目名 |
||||
|
*/ |
||||
|
public boolean exists(int projectId, String name) { |
||||
|
name = name.toLowerCase().trim(); |
||||
|
String sql = "select id from project where lower(name) = ? and id != ? limit 1"; |
||||
|
Integer id = Db.queryInt(sql, name, projectId); |
||||
|
return id != null; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 创建项目 |
||||
|
*/ |
||||
|
public Ret save(int accountId, Project project) { |
||||
|
if (exists(-1, project.getName())) { |
||||
|
return Ret.fail("msg", "项目名称已经存在,请输入别的名称"); |
||||
|
} |
||||
|
|
||||
|
project.setAccountId(accountId); |
||||
|
project.setName(project.getName().trim()); |
||||
|
project.setCreateAt(LocalDateTime.now()); |
||||
|
project.save(); |
||||
|
return Ret.ok("msg", "创建成功"); |
||||
|
} |
||||
|
|
||||
|
public Project edit(int id) { |
||||
|
return dao.findById(id); |
||||
|
} |
||||
|
|
||||
|
public Ret update(Project project) { |
||||
|
if (exists(project.getId(), project.getName())) { |
||||
|
return Ret.fail("msg", "项目名称已经存在,请输入别的名称"); |
||||
|
} |
||||
|
|
||||
|
project.setName(project.getName().trim()); |
||||
|
project.update(); |
||||
|
return Ret.ok("msg", "修改成功"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 锁定 |
||||
|
*/ |
||||
|
public Ret lock(int id) { |
||||
|
Db.update("update project set report = report + ? where id=?", Project.REPORT_BLOCK_NUM, id); |
||||
|
return Ret.ok("msg", "锁定成功"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 解除锁定 |
||||
|
*/ |
||||
|
public Ret unlock(int id) { |
||||
|
Db.update("update project set report = 0 where id=?", id); |
||||
|
return Ret.ok("msg", "解除锁定成功"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除 project |
||||
|
*/ |
||||
|
public Ret delete(int projectId) { |
||||
|
Integer accountId = Db.queryInt("select accountId from project where id=? limit 1", projectId); |
||||
|
if (accountId != null) { |
||||
|
myProjectSrv.delete(accountId, projectId); |
||||
|
return Ret.ok("msg", "project 删除成功"); |
||||
|
} else { |
||||
|
return Ret.fail("msg", "project 删除失败"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,112 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
* 新福利计划: |
||||
|
* https://jfinal.com/club/1-2
|
||||
|
* |
||||
|
* JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于创建更加高品质的产品与服务,为你带来 |
||||
|
* 更多、更大的价值 |
||||
|
* |
||||
|
* JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
*/ |
||||
|
|
||||
|
package com.bt._admin.role; |
||||
|
|
||||
|
import com.bt._admin.permission.PermissionAdminService; |
||||
|
import com.jfinal.aop.Before; |
||||
|
import com.jfinal.aop.Inject; |
||||
|
import com.bt.common.model.Permission; |
||||
|
import com.bt.common.model.Role; |
||||
|
import com.jfinal.core.Path; |
||||
|
import com.jfinal.plugin.activerecord.Page; |
||||
|
import com.bt.common.controller.BaseController; |
||||
|
import com.jfinal.kit.Ret; |
||||
|
import java.util.LinkedHashMap; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* 角色管理控制器 |
||||
|
*/ |
||||
|
@Path(value = "/admin/role", viewPath = "/role") |
||||
|
public class RoleAdminController extends BaseController { |
||||
|
|
||||
|
@Inject |
||||
|
RoleAdminService srv; |
||||
|
|
||||
|
@Inject |
||||
|
PermissionAdminService permissionAdminSrv; |
||||
|
|
||||
|
public void index() { |
||||
|
Page<Role> rolePage = srv.paginate(getParaToInt("p", 1)); |
||||
|
setAttr("rolePage", rolePage); |
||||
|
render("index.html"); |
||||
|
} |
||||
|
|
||||
|
public void add() { |
||||
|
render("add_edit.html"); |
||||
|
} |
||||
|
|
||||
|
@Before(RoleAdminValidator.class) |
||||
|
public void save() { |
||||
|
Role role = getBean(Role.class); |
||||
|
Ret ret = srv.save(role); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
public void edit() { |
||||
|
keepPara("p"); // 保持住分页的页号,便于在 ajax 提交后跳转到当前数据所在的页
|
||||
|
Role role = srv.findById(getParaToInt("id")); |
||||
|
setAttr("role", role); |
||||
|
render("add_edit.html"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 提交修改 |
||||
|
*/ |
||||
|
@Before(RoleAdminValidator.class) |
||||
|
public void update() { |
||||
|
Role role = getBean(Role.class); |
||||
|
Ret ret = srv.update(role); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
public void delete() { |
||||
|
Ret ret = srv.delete(getParaToInt("id")); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 分配权限 |
||||
|
*/ |
||||
|
public void assignPermissions() { |
||||
|
Role role = srv.findById(getParaToInt("id")); |
||||
|
List<Permission> permissionList = permissionAdminSrv.getAllPermissions(); |
||||
|
srv.markAssignedPermissions(role, permissionList); |
||||
|
LinkedHashMap<String, List<Permission>> permissionMap = srv.groupByController(permissionList); |
||||
|
|
||||
|
setAttr("role", role); |
||||
|
setAttr("permissionMap", permissionMap); |
||||
|
render("assign_permissions.html"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 添加权限 |
||||
|
*/ |
||||
|
public void addPermission() { |
||||
|
Ret ret = srv.addPermission(getParaToInt("roleId"), getParaToInt("permissionId")); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除权限 |
||||
|
*/ |
||||
|
public void deletePermission() { |
||||
|
Ret ret = srv.deletePermission(getParaToInt("roleId"), getParaToInt("permissionId")); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,172 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
* 新福利计划: |
||||
|
* https://jfinal.com/club/1-2
|
||||
|
* |
||||
|
* JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于创建更加高品质的产品与服务,为你带来 |
||||
|
* 更多、更大的价值 |
||||
|
* |
||||
|
* JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
*/ |
||||
|
|
||||
|
package com.bt._admin.role; |
||||
|
|
||||
|
import com.bt.common.model.Permission; |
||||
|
import com.bt.common.model.Role; |
||||
|
import com.jfinal.kit.Ret; |
||||
|
import com.jfinal.plugin.activerecord.Db; |
||||
|
import com.jfinal.plugin.activerecord.Page; |
||||
|
import com.jfinal.plugin.activerecord.Record; |
||||
|
|
||||
|
import java.time.LocalDateTime; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.LinkedHashMap; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* 角色管理 |
||||
|
*/ |
||||
|
public class RoleAdminService { |
||||
|
|
||||
|
private Role dao = new Role().dao(); |
||||
|
|
||||
|
public Page<Role> paginate(int pageNum) { |
||||
|
return dao.paginate(pageNum, 10, "select *", "from role order by id asc"); |
||||
|
} |
||||
|
|
||||
|
public Role findById(int roleId) { |
||||
|
return dao.findById(roleId); |
||||
|
} |
||||
|
|
||||
|
public List<Role> getAllRoles() { |
||||
|
return dao.find("select * from role order by id asc"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 判断角色名是否存在 |
||||
|
* @param roleId 当前 role 对象的 id 号,如果 role 对象还未创建,提供一个小于 0 的值即可 |
||||
|
* @param name 角色名 |
||||
|
*/ |
||||
|
public boolean exists(int roleId, String name) { |
||||
|
name = name.toLowerCase().trim(); |
||||
|
String sql = "select id from role where lower(name) = ? and id != ? limit 1"; |
||||
|
Integer id = Db.queryInt(sql, name, roleId); |
||||
|
return id != null; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 创建角色 |
||||
|
*/ |
||||
|
public Ret save(Role role) { |
||||
|
if (exists(-1, role.getName())) { |
||||
|
return Ret.fail("msg", "角色名称已经存在,请输入别的名称"); |
||||
|
} |
||||
|
|
||||
|
role.setName(role.getName().trim()); |
||||
|
role.setCreateAt(LocalDateTime.now()); |
||||
|
role.save(); |
||||
|
return Ret.ok("msg", "创建成功"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 更新角色 |
||||
|
*/ |
||||
|
public Ret update(Role role) { |
||||
|
if (exists(role.getId(), role.getName())) { |
||||
|
return Ret.fail("msg", "角色名称已经存在,请输入别的名称"); |
||||
|
} |
||||
|
|
||||
|
role.setName(role.getName().trim()); |
||||
|
role.update(); |
||||
|
return Ret.ok("msg", "角色更新成功"); |
||||
|
} |
||||
|
|
||||
|
public Ret delete(final int roleId) { |
||||
|
if (roleId == 1) { |
||||
|
return Ret.fail("msg", "id 值为 1 的超级管理员不能被删除"); |
||||
|
} |
||||
|
|
||||
|
Db.tx(() -> { |
||||
|
Db.delete("delete from account_role where roleId=?", roleId); |
||||
|
Db.delete("delete from role_permission where roleId=?", roleId); |
||||
|
dao.deleteById(roleId); |
||||
|
return true; |
||||
|
}); |
||||
|
|
||||
|
return Ret.ok("msg", "角色删除成功"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 添加权限 |
||||
|
*/ |
||||
|
public Ret addPermission(int roleId, int permissionId) { |
||||
|
if (roleId == 1) { |
||||
|
return Ret.fail("msg", "超级管理员天然拥有所有权限,无需分配"); |
||||
|
} |
||||
|
|
||||
|
Record rolePermission = new Record().set("roleId", roleId).set("permissionId", permissionId); |
||||
|
Db.save("role_permission", rolePermission); |
||||
|
return Ret.ok("msg", "添加权限成功"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除权限 |
||||
|
*/ |
||||
|
public Ret deletePermission(int roleId, int permissionId) { |
||||
|
if (roleId == 1) { |
||||
|
return Ret.fail("msg", "超级管理员天然拥有所有权限,不能删除权限"); |
||||
|
} |
||||
|
|
||||
|
Db.delete("delete from role_permission where roleId=? and permissionId=?", roleId, permissionId); |
||||
|
return Ret.ok("msg", "删除权限成功"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 标记出 role 拥有的权限,用于在界面输出 checkbox 的 checked 属性 |
||||
|
* 未来用 permission left join role_permission 来优化 |
||||
|
*/ |
||||
|
public void markAssignedPermissions(Role role, List<Permission> permissionList) { |
||||
|
// id 为 1 的超级管理员默认拥有所有权限
|
||||
|
if (role.getId() == 1) { |
||||
|
for (Permission permission : permissionList) { |
||||
|
permission.put("assigned", true); |
||||
|
} |
||||
|
return ; |
||||
|
} |
||||
|
|
||||
|
String sql = "select roleId from role_permission where roleId=? and permissionId=? limit 1"; |
||||
|
for (Permission permission : permissionList) { |
||||
|
Integer roleId = Db.queryInt(sql, role.getId(), permission.getId()); |
||||
|
if (roleId != null) { |
||||
|
// 设置 assigned 用于界面输出 checked
|
||||
|
permission.put("assigned", true); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 根据 controller 将 permission 进行分组 |
||||
|
*/ |
||||
|
public LinkedHashMap<String, List<Permission>> groupByController(List<Permission> permissionList) { |
||||
|
LinkedHashMap<String, List<Permission>> ret = new LinkedHashMap<String, List<Permission>>(); |
||||
|
|
||||
|
for (Permission permission : permissionList) { |
||||
|
String controller = permission.getController(); |
||||
|
List<Permission> list = ret.get(controller); |
||||
|
if (list == null) { |
||||
|
list = new ArrayList<Permission>(); |
||||
|
ret.put(controller, list); |
||||
|
} |
||||
|
|
||||
|
list.add(permission); |
||||
|
} |
||||
|
|
||||
|
return ret; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,39 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
* 新福利计划: |
||||
|
* https://jfinal.com/club/1-2
|
||||
|
* |
||||
|
* JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于创建更加高品质的产品与服务,为你带来 |
||||
|
* 更多、更大的价值 |
||||
|
* |
||||
|
* JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
*/ |
||||
|
|
||||
|
package com.bt._admin.role; |
||||
|
|
||||
|
import com.jfinal.core.Controller; |
||||
|
import com.jfinal.validate.Validator; |
||||
|
|
||||
|
/** |
||||
|
* AccountUpdateValidator 验证角色修改功能表单 |
||||
|
*/ |
||||
|
public class RoleAdminValidator extends Validator { |
||||
|
|
||||
|
protected void validate(Controller c) { |
||||
|
setShortCircuit(true); |
||||
|
|
||||
|
validateRequiredString("role.name", "msg", "角色名称不能为空"); |
||||
|
} |
||||
|
|
||||
|
protected void handleError(Controller c) { |
||||
|
c.setAttr("state", "fail"); |
||||
|
c.renderJson(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,72 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
* 新福利计划: |
||||
|
* https://jfinal.com/club/1-2
|
||||
|
* |
||||
|
* JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于创建更加高品质的产品与服务,为你带来 |
||||
|
* 更多、更大的价值 |
||||
|
* |
||||
|
* JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
*/ |
||||
|
|
||||
|
package com.bt._admin.role; |
||||
|
|
||||
|
import com.jfinal.aop.Aop; |
||||
|
import com.bt._admin.auth.AdminAuthService; |
||||
|
import com.bt.common.model.Account; |
||||
|
import com.bt.login.LoginService; |
||||
|
import com.jfinal.template.Directive; |
||||
|
import com.jfinal.template.Env; |
||||
|
import com.jfinal.template.io.Writer; |
||||
|
import com.jfinal.template.stat.Scope; |
||||
|
|
||||
|
/** |
||||
|
* 界面上的权限控制功能 |
||||
|
* 用来控制界面上的菜单、按钮等等元素的显示 |
||||
|
* |
||||
|
* 使用示例见模板文件: /_view/_admin/common/_menu.html 或者 /_view/_admin/permission/index.html |
||||
|
* #role("权限管理员", "CEO", "CTO") |
||||
|
* ... |
||||
|
* #end |
||||
|
*/ |
||||
|
public class RoleDirective extends Directive { |
||||
|
|
||||
|
static AdminAuthService adminAuthSrv = Aop.get(AdminAuthService.class); |
||||
|
|
||||
|
public void exec(Env env, Scope scope, Writer writer) { |
||||
|
Account account = (Account)scope.getRootData().get(LoginService.loginAccountCacheName); |
||||
|
if (account != null && account.isStatusOk()) { |
||||
|
// 如果是超级管理员,或者拥有指定的角色则放行
|
||||
|
if ( adminAuthSrv.isSuperAdmin(account.getId()) || |
||||
|
adminAuthSrv.hasRole(account.getId(), getRoleNameArray(scope))) { |
||||
|
stat.exec(env, scope, writer); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 从 #role 指令参数中获取角色名称数组 |
||||
|
*/ |
||||
|
private String[] getRoleNameArray(Scope scope) { |
||||
|
Object[] values = exprList.evalExprList(scope); |
||||
|
String[] ret = new String[values.length]; |
||||
|
for (int i=0; i<values.length; i++) { |
||||
|
if (values[i] instanceof String) { |
||||
|
ret[i] = (String)values[i]; |
||||
|
} else { |
||||
|
throw new IllegalArgumentException("角色名只能为 String 类型"); |
||||
|
} |
||||
|
} |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
public boolean hasEnd() { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,165 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
* 新福利计划: |
||||
|
* https://jfinal.com/club/1-2
|
||||
|
* |
||||
|
* JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于创建更加高品质的产品与服务,为你带来 |
||||
|
* 更多、更大的价值 |
||||
|
* |
||||
|
* JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
*/ |
||||
|
|
||||
|
package com.bt._admin.share; |
||||
|
|
||||
|
import com.jfinal.aop.Before; |
||||
|
import com.jfinal.aop.Inject; |
||||
|
import com.bt.common.account.AccountService; |
||||
|
import com.bt.my.share.MyShareValidator; |
||||
|
import com.bt.project.ProjectService; |
||||
|
import com.jfinal.plugin.activerecord.Page; |
||||
|
import com.bt.common.controller.BaseController; |
||||
|
import com.jfinal.kit.Ret; |
||||
|
import com.bt.common.model.Share; |
||||
|
import com.bt.common.model.ShareReply; |
||||
|
import com.bt.index.IndexService; |
||||
|
import com.bt.share.ShareService; |
||||
|
import com.jfinal.core.Path; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* 分享管理控制器 |
||||
|
*/ |
||||
|
@Path(value = "/admin/share", viewPath = "/share") |
||||
|
public class ShareAdminController extends BaseController { |
||||
|
|
||||
|
@Inject |
||||
|
ShareAdminService srv; |
||||
|
|
||||
|
@Inject |
||||
|
ShareService shareSrv; |
||||
|
|
||||
|
@Inject |
||||
|
ProjectService projectSrv; |
||||
|
|
||||
|
@Inject |
||||
|
AccountService accountSrv; |
||||
|
|
||||
|
@Inject |
||||
|
IndexService indexSrv; |
||||
|
|
||||
|
public void index() { |
||||
|
Page<Share> sharePage = srv.paginate(getParaToInt("p", 1)); |
||||
|
setAttr("sharePage", sharePage); |
||||
|
render("index.html"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 创建 |
||||
|
*/ |
||||
|
public void add() { |
||||
|
setAttr("projectList", projectSrv.getAllProject("id, name")); // 关联项目下拉列表
|
||||
|
render("add_edit.html"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 提交创建 |
||||
|
*/ |
||||
|
@Before(MyShareValidator.class) |
||||
|
public void save() { |
||||
|
Share share = getBean(Share.class); |
||||
|
Ret ret = srv.save(getLoginAccountId(), share); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 修改 |
||||
|
*/ |
||||
|
public void edit() { |
||||
|
keepPara("p"); // 保持住分页的页号,便于在 ajax 提交后跳转到当前数据所在的页
|
||||
|
setAttr("projectList", projectSrv.getAllProject("id, name")); // 关联项目下拉列表
|
||||
|
setAttr("share", srv.edit(getParaToInt("id"))); |
||||
|
render("add_edit.html"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 提交修改 |
||||
|
*/ |
||||
|
@Before(MyShareValidator.class) |
||||
|
public void update() { |
||||
|
Share share = getBean(Share.class); |
||||
|
Ret ret = srv.update(share); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 锁定 |
||||
|
*/ |
||||
|
public void lock() { |
||||
|
Ret ret = srv.lock(getParaToInt("id")); |
||||
|
|
||||
|
shareSrv.clearHotShareCache(); |
||||
|
indexSrv.clearCache(); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 解除锁定 |
||||
|
*/ |
||||
|
public void unlock() { |
||||
|
Ret ret = srv.unlock(getParaToInt("id")); |
||||
|
|
||||
|
shareSrv.clearHotShareCache(); |
||||
|
indexSrv.clearCache(); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除 share |
||||
|
*/ |
||||
|
public void delete() { |
||||
|
Ret ret = srv.delete(getParaToInt("id")); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取 share 的 reply 列表 |
||||
|
*/ |
||||
|
public void getReplyList() { |
||||
|
int shareId = getParaToInt("shareId"); |
||||
|
Share share = srv.getById(shareId); |
||||
|
List<ShareReply> shareReplyList = srv.getReplyList(shareId); |
||||
|
accountSrv.join("accountId", shareReplyList, "nickName"); |
||||
|
|
||||
|
setAttr("share", share); |
||||
|
setAttr("shareReplyList", shareReplyList); |
||||
|
setAttr("shareId", shareId); |
||||
|
render("reply.html"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取 share 的 reply |
||||
|
*/ |
||||
|
public void getReply() { |
||||
|
int replyId = getParaToInt("replyId"); |
||||
|
Ret ret = srv.getReply(replyId); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除 share reply |
||||
|
*/ |
||||
|
public void deleteReply() { |
||||
|
int replyId = getParaToInt("replyId"); |
||||
|
Ret ret = srv.deleteReply(replyId); |
||||
|
renderJson(ret); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
@ -0,0 +1,133 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给任何人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目源码、直播视频、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多,俱乐部福利资源是不断增加的,以下是俱乐部 |
||||
|
* 新福利计划: |
||||
|
* https://jfinal.com/club/1-2
|
||||
|
* |
||||
|
* JFinal 俱乐部是七年以来首次寻求外部资源的尝试,以便于创建更加高品质的产品与服务,为你带来 |
||||
|
* 更多、更大的价值 |
||||
|
* |
||||
|
* JFinal 项目的可持续性发展需要你的支持!!! |
||||
|
*/ |
||||
|
|
||||
|
package com.bt._admin.share; |
||||
|
|
||||
|
import com.jfinal.aop.Inject; |
||||
|
import com.bt.common.model.Share; |
||||
|
import com.jfinal.kit.Ret; |
||||
|
import com.jfinal.plugin.activerecord.Db; |
||||
|
import com.bt.common.model.ShareReply; |
||||
|
import com.bt.my.share.MyShareService; |
||||
|
import com.jfinal.plugin.activerecord.Page; |
||||
|
|
||||
|
import java.time.LocalDateTime; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* share 管理业务 |
||||
|
*/ |
||||
|
public class ShareAdminService { |
||||
|
|
||||
|
@Inject |
||||
|
MyShareService myShareSrv; |
||||
|
|
||||
|
private Share dao = new Share().dao(); |
||||
|
private ShareReply shareReplyDao = new ShareReply().dao(); |
||||
|
|
||||
|
/** |
||||
|
* share 分页 |
||||
|
*/ |
||||
|
public Page<Share> paginate(int pageNum) { |
||||
|
return dao.paginate(pageNum, 10, "select *", "from share order by id desc"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 创建分享 |
||||
|
*/ |
||||
|
public Ret save(int accountId, Share project) { |
||||
|
project.setAccountId(accountId); |
||||
|
project.setTitle(project.getTitle().trim()); |
||||
|
project.setCreateAt(LocalDateTime.now()); |
||||
|
project.save(); |
||||
|
return Ret.ok("msg", "创建成功"); |
||||
|
} |
||||
|
|
||||
|
public Share edit(int id) { |
||||
|
return dao.findById(id); |
||||
|
} |
||||
|
|
||||
|
public Ret update(Share share) { |
||||
|
share.update(); |
||||
|
return Ret.ok("msg", "修改成功"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 锁定 |
||||
|
*/ |
||||
|
public Ret lock(int id) { |
||||
|
Db.update("update share set report = report + ? where id=?", Share.REPORT_BLOCK_NUM, id); |
||||
|
return Ret.ok("msg", "锁定成功"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 解除锁定 |
||||
|
*/ |
||||
|
public Ret unlock(int id) { |
||||
|
Db.update("update share set report = 0 where id=?", id); |
||||
|
return Ret.ok("msg", "解除锁定成功"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除 share |
||||
|
*/ |
||||
|
public Ret delete(int shareId) { |
||||
|
Integer accountId = Db.queryInt("select accountId from share where id=? limit 1", shareId); |
||||
|
if (accountId != null) { |
||||
|
myShareSrv.delete(accountId, shareId); |
||||
|
return Ret.ok("msg", "share 删除成功"); |
||||
|
} else { |
||||
|
return Ret.fail("msg", "share 删除失败"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public Share getById(int shareId) { |
||||
|
return dao.findById(shareId); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取 reply list |
||||
|
*/ |
||||
|
public List<ShareReply> getReplyList(int shareId) { |
||||
|
String sql = "select id, accountId, createAt, substring(content, 1, 30) as content from share_reply where shareId=? order by id desc"; |
||||
|
return shareReplyDao.find(sql, shareId); |
||||
|
} |
||||
|
|
||||
|
public Ret getReply(int replyId) { |
||||
|
ShareReply reply = shareReplyDao.findById(replyId); |
||||
|
return Ret.ok("reply", reply); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 删除 share reply |
||||
|
*/ |
||||
|
public Ret deleteReply(int shareReplyId) { |
||||
|
Integer accountId = Db.queryInt("select accountId from share_reply where id=? limit 1", shareReplyId); |
||||
|
if (accountId != null) { |
||||
|
myShareSrv.deleteShareReplyById(accountId, shareReplyId); |
||||
|
return Ret.ok("msg", "share reply 删除成功"); |
||||
|
} else { |
||||
|
return Ret.fail("msg", "share reply 删除失败"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
@ -0,0 +1,58 @@ |
|||||
|
0:jfinal club 1.4 新添加了如下四张表: |
||||
|
role(id, name) |
||||
|
permission(id, actionKey, controller, remark) |
||||
|
account_role(accountId, roleId) |
||||
|
role_permission(roleId, permissionId) |
||||
|
|
||||
|
其中 role、permission 与老版本的 account 构成权限管理功能的三张主表, |
||||
|
而 account_role、role_permission 建立这三张主表间的关联 |
||||
|
|
||||
|
|
||||
|
1:权限管理模块对左侧菜单进行管理,可以参考 "/_view/_admin/common/_menu.html" |
||||
|
中的 #role("权限管理员", "CEO", "CTO") 用法,只需要先用这种式方式预先 |
||||
|
安排好哪些角色可以访问哪些菜单,然后就可以通过为 account 配置 role 的方式 |
||||
|
来配置菜单权限了,这种模式比通过再创建 menu 表要简单方便 |
||||
|
|
||||
|
此外,也可以通过 #permission(...) 更细粒度的控制每一个菜单,一般用 #role 指令即可 |
||||
|
|
||||
|
|
||||
|
2:权限管理模块对界面操作按钮之类的组件细粒度的控制可以参考 "/_view/_admin/project/index.html" |
||||
|
中的 #permission("/admin/project/delete") 用法,只需要先用这种方式预先 |
||||
|
安排好哪些权限用于控制访问哪些按钮或组件,然后就可以通过为 account 配置权限的 |
||||
|
方式来细粒度控制按纽、组件了 |
||||
|
|
||||
|
|
||||
|
3:share、feedback 的回复管理下一版本添加:贴子 title + table 结构 |
||||
|
|
||||
|
|
||||
|
4:头部导航已经做过搜索的界面,一直对美观不满意,留到下版本做进去, |
||||
|
搜索栏样式参考:https://fontawesome.com/v4.7.0/icons/ |
||||
|
|
||||
|
|
||||
|
5:项目整体结构采用先划分模块,然后在模块内部再分层的方式,便于向大型系统进化, |
||||
|
市面上很多先划分层次,然后再划分模块的方式远没有此方式适应大型系统的开发, |
||||
|
此划分方式,还有利于在未来将模块独立拆分成小型服务,再以微服务的方式做分布式 |
||||
|
而先分层再分模块的方式则无法方便支持 |
||||
|
|
||||
|
|
||||
|
6:后台管理源码的包名以及视图文件的目录名以下划线打头 "_",是为了让其始终排列在 |
||||
|
固定的位置,便于开发过程中快速定位,提升开发效率。否则后台管理模块的位置 |
||||
|
会不断变化,例如,如果存在 about 包名,则 admin 会排在其之后 |
||||
|
|
||||
|
|
||||
|
7:视图文件基础路径 "_view" 以下划线打头 "_",是为了将其排在 webapp 的最前面,便于快速 |
||||
|
定位,提升开发效率,否则该目录会被夹杂在 assets、WEB-INF、upload 等目录之间 |
||||
|
由于 jfinal 有 baseViewPath 配置,所以此安排不会给开发带来麻烦 |
||||
|
|
||||
|
|
||||
|
8:后台管理并没有专用的登录界面,一切针对于后台管理登录界面的黑客攻击根本找不到这个界面 |
||||
|
一切对于该界面的寻找必将返回 404 页面,没有专用的管理账户表,管理员账号存在于普通 |
||||
|
账号之中。无招才是更好的招。 |
||||
|
|
||||
|
|
||||
|
总之:jfinal-club 值得讨论和学习的细节极多,上述仅为冰山一角,大家一定要加入俱乐部 |
||||
|
收获所有福利 https://jfinal.com/club |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
@ -0,0 +1,243 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common; |
||||
|
|
||||
|
import java.sql.Connection; |
||||
|
import com.alibaba.druid.filter.stat.StatFilter; |
||||
|
import com.alibaba.druid.wall.WallFilter; |
||||
|
import com.bt._admin.auth.AdminAuthInterceptor; |
||||
|
import com.bt._admin.auth.AdminAuthKit; |
||||
|
import com.bt._admin.common.PjaxInterceptor; |
||||
|
import com.bt._admin.permission.PermissionDirective; |
||||
|
import com.bt._admin.role.RoleDirective; |
||||
|
import com.bt.common.kit.DruidKit; |
||||
|
import com.bt.common.model.Document; |
||||
|
import com.bt.common.model.Feedback; |
||||
|
import com.bt.common.model.Project; |
||||
|
import com.bt.common.model.Share; |
||||
|
import com.bt.common.model._MappingKit; |
||||
|
import com.jfinal.config.*; |
||||
|
import com.jfinal.json.MixedJsonFactory; |
||||
|
import com.jfinal.kit.Prop; |
||||
|
import com.jfinal.kit.PropKit; |
||||
|
import com.jfinal.plugin.activerecord.ActiveRecordPlugin; |
||||
|
import com.jfinal.plugin.cron4j.Cron4jPlugin; |
||||
|
import com.jfinal.plugin.druid.DruidPlugin; |
||||
|
import com.jfinal.plugin.ehcache.EhCachePlugin; |
||||
|
import com.jfinal.render.JsonRender; |
||||
|
import com.jfinal.server.undertow.UndertowServer; |
||||
|
import com.jfinal.template.Engine; |
||||
|
import com.bt.common.handler.UrlSeoHandler; |
||||
|
import com.bt.common.model.*; |
||||
|
import com.bt.common.interceptor.LoginSessionInterceptor; |
||||
|
import com.bt.login.LoginService; |
||||
|
import com.bt.my.friend.FriendInterceptor; |
||||
|
|
||||
|
/** |
||||
|
* JFinalClubConfig |
||||
|
*/ |
||||
|
public class JFinalClubConfig extends JFinalConfig { |
||||
|
|
||||
|
// 使用 jfinal-undertow 时此处仅保留声明,不能有加载代码
|
||||
|
private static Prop p; |
||||
|
|
||||
|
private WallFilter wallFilter; |
||||
|
|
||||
|
/** |
||||
|
* 启动入口,运行此 main 方法可以启动项目,此 main 方法 |
||||
|
* 可以放置在任意的 Class 类定义中,不一定要放于此 |
||||
|
*/ |
||||
|
public static void main(String[] args) { |
||||
|
UndertowServer.start(JFinalClubConfig.class); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* PropKit.useFirstFound(...) 使用参数中从左到右最先被找到的配置文件 |
||||
|
* 从左到右依次去找配置,找到则立即加载并立即返回,后续配置将被忽略 |
||||
|
*/ |
||||
|
static void loadConfig() { |
||||
|
if (p == null) { |
||||
|
p = PropKit.useFirstFound("jfinal-club-config-pro.txt", "jfinal-club-config-dev.txt"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public void configConstant(Constants me) { |
||||
|
loadConfig(); |
||||
|
|
||||
|
me.setDevMode(p.getBoolean("devMode", false)); |
||||
|
me.setJsonFactory(MixedJsonFactory.me()); |
||||
|
|
||||
|
// 支持 Controller、Interceptor、Validator 之中使用 @Inject 注入业务层,并且自动实现 AOP
|
||||
|
me.setInjectDependency(true); |
||||
|
|
||||
|
// 是否对超类中的属性进行注入
|
||||
|
me.setInjectSuperClass(true); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 路由拆分到 FrontRoutes 与 AdminRoutes 之中配置的好处: |
||||
|
* 1:可分别配置不同的 baseViewPath 与 Interceptor |
||||
|
* 2:避免多人协同开发时,频繁修改此文件带来的版本冲突 |
||||
|
* 3:避免本文件中内容过多,拆分后可读性增强 |
||||
|
* 4:便于分模块管理路由 |
||||
|
*/ |
||||
|
public void configRoute(Routes me) { |
||||
|
// 改用扫描路由,手动注册路由不再使用,FrontRoutes、AdminRoutes 已被删除
|
||||
|
|
||||
|
/** |
||||
|
* 扫描后台路由 |
||||
|
*/ |
||||
|
me.add(new Routes() { |
||||
|
public void config() { |
||||
|
// 添加后台管理拦截器,将拦截在此方法中注册的所有 Controller
|
||||
|
this.addInterceptor(new AdminAuthInterceptor()); |
||||
|
this.addInterceptor(new PjaxInterceptor()); |
||||
|
|
||||
|
this.setBaseViewPath("/_view/_admin"); |
||||
|
|
||||
|
// 如果被扫描的包在 jar 文件之中,需要添加如下配置:
|
||||
|
// undertow.hotSwapClassPrefix = com.bt._admin.
|
||||
|
this.scan("com.bt._admin."); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
/** |
||||
|
* 扫描前台路由 |
||||
|
* |
||||
|
* 注意: |
||||
|
* 1:scan(...) 方法要添加过滤,过滤掉后台路由,否则后台路由会被扫描到, |
||||
|
* 造成 baseViewPath 以及 routes 级别的拦截器配置错误 |
||||
|
* |
||||
|
* 2: 由于 scan(...) 内部避免了重复扫描同一个类,所以需要将扫描前台路由代码 |
||||
|
* 放在扫描后台路由之前才能验证没有过滤造成的后果 |
||||
|
*/ |
||||
|
me.add(new Routes() { |
||||
|
public void config() { |
||||
|
this.setBaseViewPath("/_view"); |
||||
|
|
||||
|
// 如果被扫描的包在 jar 文件之中,需要添加如下配置:
|
||||
|
// undertow.hotSwapClassPrefix = com.bt.
|
||||
|
this.scan("com.bt.", className -> { |
||||
|
// className 为当前正扫描的类名,返回 true 时表示过滤掉当前类不扫描
|
||||
|
return className.startsWith("com.bt._admin."); |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 配置模板引擎,通常情况只需配置共享的模板函数 |
||||
|
*/ |
||||
|
public void configEngine(Engine me) { |
||||
|
me.setDevMode(p.getBoolean("engineDevMode", false)); |
||||
|
|
||||
|
// 开启压缩功能,常用配置参数有: ' ' 与 '\n'
|
||||
|
// me.setCompressorOn('\n');
|
||||
|
|
||||
|
// 配置用于主菜单的 URL,导航到 https://jfinal.com
|
||||
|
me.addSharedObject("SSL", p.get("SSL")); |
||||
|
|
||||
|
/** |
||||
|
* 用于在页面中使用 field 表达式代替 static field 表达式,节省代码量 |
||||
|
* |
||||
|
* 例如: |
||||
|
* Feedback.REPORT_BLOCK_NUM |
||||
|
* 可代替 |
||||
|
* com.jfinal.club.common.model.Feedback::REPORT_BLOCK_NUM |
||||
|
*/ |
||||
|
me.addSharedObject("Feedback", new Feedback()); |
||||
|
me.addSharedObject("Project", new Project()); |
||||
|
me.addSharedObject("Share", new Share()); |
||||
|
me.addSharedObject("Document", new Document()); |
||||
|
|
||||
|
// 添加角色、权限指令
|
||||
|
me.addDirective("role", RoleDirective.class); |
||||
|
me.addDirective("permission", PermissionDirective.class); |
||||
|
me.addDirective("perm", PermissionDirective.class); // 配置一个别名指令
|
||||
|
|
||||
|
// 添加角色、权限 shared method
|
||||
|
me.addSharedMethod(AdminAuthKit.class); |
||||
|
|
||||
|
me.addSharedFunction("/_view/common/__layout.html"); |
||||
|
me.addSharedFunction("/_view/common/__front_layout.html"); |
||||
|
me.addSharedFunction("/_view/common/__b5layout.html"); |
||||
|
me.addSharedFunction("/_view/common/_paginate.html"); |
||||
|
|
||||
|
me.addSharedFunction("/_view/_admin/common/__admin_layout.html"); |
||||
|
me.addSharedFunction("/_view/_admin/common/_admin_paginate.html"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 抽取成独立的方法,便于 _Generator 中重用该方法,减少代码冗余 |
||||
|
*/ |
||||
|
public static DruidPlugin getDruidPlugin() { |
||||
|
loadConfig(); |
||||
|
return new DruidPlugin(p.get("jdbcUrl"), p.get("user"), p.get("password")); |
||||
|
} |
||||
|
|
||||
|
public void configPlugin(Plugins me) { |
||||
|
DruidPlugin druidPlugin = getDruidPlugin(); |
||||
|
wallFilter = new WallFilter(); // 加强数据库安全
|
||||
|
wallFilter.setDbType("mysql"); |
||||
|
druidPlugin.addFilter(wallFilter); |
||||
|
druidPlugin.addFilter(new StatFilter()); // 添加 StatFilter 才会有统计数据
|
||||
|
me.add(druidPlugin); |
||||
|
|
||||
|
ActiveRecordPlugin arp = new ActiveRecordPlugin(druidPlugin); |
||||
|
arp.setTransactionLevel(Connection.TRANSACTION_READ_COMMITTED); |
||||
|
_MappingKit.mapping(arp); |
||||
|
// 强制指定复合主键的次序,避免不同的开发环境生成在 _MappingKit 中的复合主键次序不相同
|
||||
|
arp.setPrimaryKey("document", "mainMenu,subMenu"); |
||||
|
me.add(arp); |
||||
|
arp.setShowSql(p.getBoolean("devMode", false)); |
||||
|
|
||||
|
arp.getEngine().setToClassPathSourceFactory(); |
||||
|
arp.addSqlTemplate("/com/bt/common/_all_sqls.sql"); |
||||
|
|
||||
|
me.add(new EhCachePlugin()); |
||||
|
me.add(new Cron4jPlugin(p)); |
||||
|
} |
||||
|
|
||||
|
public void configInterceptor(Interceptors me) { |
||||
|
me.add(new LoginSessionInterceptor()); |
||||
|
} |
||||
|
|
||||
|
public void configHandler(Handlers me) { |
||||
|
me.add(DruidKit.getDruidStatViewHandler()); // druid 统计页面功能
|
||||
|
me.add(new UrlSeoHandler()); // index、detail 两类 action 的 url seo
|
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 本方法会在 jfinal 启动过程完成之后被回调,详见 jfinal 手册 |
||||
|
*/ |
||||
|
public void onStart() { |
||||
|
// 调用不带参的 renderJson() 时,排除对 loginAccount、remind 的 json 转换
|
||||
|
JsonRender.addExcludedAttrs( |
||||
|
LoginService.loginAccountCacheName, |
||||
|
LoginSessionInterceptor.remindKey, |
||||
|
FriendInterceptor.followNum, FriendInterceptor.fansNum, FriendInterceptor.friendRelation |
||||
|
); |
||||
|
|
||||
|
// 让 druid 允许在 sql 中使用 union
|
||||
|
// https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE-wallfilter
|
||||
|
wallFilter.getConfig().setSelectUnionCheck(false); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
@ -0,0 +1,26 @@ |
|||||
|
在此统一管理所有 sql,优点有: |
||||
|
1:避免在 JFinalClubConfig 中一个个添加 sql 模板文件 |
||||
|
2:免除在实际的模板文件中书写 namespace,以免让 sql 定义往后缩进一层 |
||||
|
3:在此文件中还可以通过 define 指令定义一些通用模板函数,供全局共享 |
||||
|
例如定义通用的 CRUD 模板函数 |
||||
|
|
||||
|
#namespace("index") |
||||
|
#include("/com/bt/index/index.sql") |
||||
|
#end |
||||
|
|
||||
|
#namespace("project") |
||||
|
#include("/com/bt/project/project.sql") |
||||
|
#end |
||||
|
|
||||
|
#namespace("share") |
||||
|
#include("/com/bt/share/share.sql") |
||||
|
#end |
||||
|
|
||||
|
#namespace("feedback") |
||||
|
#include("/com/bt/feedback/feedback.sql") |
||||
|
#end |
||||
|
|
||||
|
#namespace("admin.auth") |
||||
|
#include("/com/bt/_admin/auth/admin_auth.sql") |
||||
|
#end |
||||
|
|
||||
@ -0,0 +1,157 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.account; |
||||
|
|
||||
|
import com.jfinal.kit.StrKit; |
||||
|
import com.jfinal.plugin.activerecord.Db; |
||||
|
import com.jfinal.plugin.activerecord.Model; |
||||
|
import com.jfinal.plugin.ehcache.CacheKit; |
||||
|
import com.bt.common.model.Account; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* 账户业务 |
||||
|
*/ |
||||
|
@SuppressWarnings("rawtypes") |
||||
|
public class AccountService { |
||||
|
|
||||
|
private Account dao = new Account().dao(); |
||||
|
private final String allAccountsCacheName = "allAccounts"; |
||||
|
|
||||
|
public void updateAccountAvatar(int accountId, String relativePathFileName) { |
||||
|
Db.update("update account set avatar=? where id=? limit 1", relativePathFileName, accountId); |
||||
|
clearCache(accountId); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 通过nickName获取Account对象,仅返回指定的字段,多字段用逗号分隔 |
||||
|
*/ |
||||
|
public Account getByNickName(String nickName, String columns) { |
||||
|
if (StrKit.isBlank(nickName)) { |
||||
|
return null; |
||||
|
} |
||||
|
return dao.findFirst("select " + columns +" from account where nickName=? and status=? limit 1", nickName, Account.STATUS_OK); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 通过 id 获取Account对象,只能获取到未被 block 的 account |
||||
|
*/ |
||||
|
public Account getUsefulById(int accountId) { |
||||
|
// return dao.findFirst("select " + columns +" from account where id=? and status=? limit 1", accountId, Account.STATUS_OK);
|
||||
|
Account account = getById(accountId); |
||||
|
return account != null && account.isStatusOk() ? account : null; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 优先从缓存中获取 account 对象,可获取到被 block 的 account |
||||
|
*/ |
||||
|
public Account getById(int accountId) { |
||||
|
// 优先从缓存中取,未命中缓存则从数据库取
|
||||
|
Account account = CacheKit.get(allAccountsCacheName, accountId); |
||||
|
if (account == null) { |
||||
|
// 考虑到可能需要 join 状态不合法的用户,先放开 status 的判断
|
||||
|
// account = dao.findFirst("select * from account where id=? and status=? limit 1", accountId, Account.STATUS_OK);
|
||||
|
account = dao.findFirst("select * from account where id=? limit 1", accountId); |
||||
|
if (account != null) { |
||||
|
account.removeSensitiveInfo(); |
||||
|
CacheKit.put(allAccountsCacheName, accountId, account); |
||||
|
} |
||||
|
} |
||||
|
return account; |
||||
|
} |
||||
|
|
||||
|
public void joinNickNameAndAvatar(List<? extends Model> modelList) { |
||||
|
join("accountId", modelList, "nickName", "avatar"); |
||||
|
} |
||||
|
|
||||
|
public void joinNickNameAndAvatar(Model model) { |
||||
|
join("accountId", model, "nickName", "avatar"); |
||||
|
} |
||||
|
|
||||
|
public void join(String joinOnField, List<? extends Model> modelList, String... joinAttrs) { |
||||
|
if (modelList != null) { |
||||
|
for (Model m : modelList) { |
||||
|
join(joinOnField, m, joinAttrs); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 在 Project、Share、Feedback、NewsFeed 等模块,需要关联查询获取 Account 对象的 nickName、avatar 时使用此方法 |
||||
|
* 避免使用关联查询,优化性能,在使用中更关键的地方在于缓存的清除 |
||||
|
* @param joinOnField join 使用的字段名,account 这端使用 id |
||||
|
* @param model 需要 join 的 model |
||||
|
* @param joinAttrs 需要 join 到 model 中的的属性名称 |
||||
|
*/ |
||||
|
public void join(String joinOnField, Model model, String... joinAttrs) { |
||||
|
Integer accountId = model.getInt(joinOnField); |
||||
|
if (accountId == null) { |
||||
|
throw new RuntimeException("Model 中的 \"" + joinOnField + "\" 属性值不能为 null"); |
||||
|
} |
||||
|
|
||||
|
Account account = getById(accountId); |
||||
|
|
||||
|
// join 真正开始的地方,前面是准备工作
|
||||
|
if (account != null) { |
||||
|
for (String attr : joinAttrs) { |
||||
|
model.put(attr, account.get(attr)); |
||||
|
} |
||||
|
} else { |
||||
|
throw new RuntimeException("未找到 account 或者 account 状态不合法,account 的 id 值为:" + accountId + " 可能是数据库数据不一致"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 更新 likeCount 字段 |
||||
|
* TODO 未来做成延迟更新模式 |
||||
|
*/ |
||||
|
private void updateLikeCount(int accountId, boolean isAdd) { |
||||
|
String sql = isAdd ? |
||||
|
"update account set likeCount=likeCount+1 where id=? limit 1" : |
||||
|
"update account set likeCount=likeCount-1 where id=? and likeCount > 0 limit 1"; |
||||
|
int n = Db.update(sql, accountId); |
||||
|
if (n > 0) { |
||||
|
// 直接更新缓存中的 likeCount 值
|
||||
|
Account account = CacheKit.get(allAccountsCacheName, accountId); |
||||
|
if (account != null) { |
||||
|
account.setLikeCount(account.getLikeCount() + (isAdd ? 1 : -1)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* likeCount 增加 1 |
||||
|
*/ |
||||
|
public void addLikeCount(int accountId) { |
||||
|
updateLikeCount(accountId, true); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* likeCount 减去 1 |
||||
|
*/ |
||||
|
public void minusLikeCount(int accountId) { |
||||
|
updateLikeCount(accountId, false); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 根据 accountId 值移除缓存 |
||||
|
*/ |
||||
|
public void clearCache(int accountId) { |
||||
|
CacheKit.remove(allAccountsCacheName, accountId); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
@ -0,0 +1,115 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.authcode; |
||||
|
|
||||
|
import com.bt.common.model.AuthCode; |
||||
|
import com.jfinal.kit.StrKit; |
||||
|
import com.jfinal.plugin.activerecord.Db; |
||||
|
import com.jfinal.kit.Ret; |
||||
|
|
||||
|
/** |
||||
|
* 授权码业务 |
||||
|
* 用于一切需要授权的业务,例如: |
||||
|
* 1:邮件激活 |
||||
|
* 2:密码找回 |
||||
|
* 3:未来一切需要授权码的场景 |
||||
|
*/ |
||||
|
public class AuthCodeService { |
||||
|
|
||||
|
private AuthCode dao = new AuthCode().dao(); |
||||
|
|
||||
|
/** |
||||
|
* 创建注册激活授权码,一个小时后过期,3600 秒 |
||||
|
*/ |
||||
|
public String createRegActivateAuthCode(int accountId) { |
||||
|
return createAuthCode(accountId, AuthCode.TYPE_REG_ACTIVATE, 3600); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 创建密码找回授权码,一个小时后过期,3600 秒 |
||||
|
*/ |
||||
|
public String createRetrievePasswordAuthCode(int accountId) { |
||||
|
return createAuthCode(accountId, AuthCode.TYPE_RETRIEVE_PASSWORD, 3600); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取授权码,授权码只能使用一次,在被获取后会被立即删除 |
||||
|
*/ |
||||
|
public AuthCode getAuthCode(String authCodeId) { |
||||
|
if (StrKit.notBlank(authCodeId)) { |
||||
|
AuthCode authCode = dao.findById(authCodeId.trim()); // authCode 是唯一的
|
||||
|
if (authCode != null) { |
||||
|
authCode.delete(); // 只要找到 authCode,则立即删除
|
||||
|
return authCode; |
||||
|
} |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 创建授权码,并自动保存到数据库 |
||||
|
* @param accountId 用户账号id |
||||
|
* @param authType 授权类型 |
||||
|
* @param expireTime 授权码过期时长,过期时长是指授权码自创建时间起直到过期的时间长度,单位为秒 |
||||
|
*/ |
||||
|
private String createAuthCode(int accountId, int authType, int expireTime) { |
||||
|
long et = expireTime; // 使用 long et 为了避免 int 数值溢出,造成保存到数据库中的数值错误
|
||||
|
long expireAt = System.currentTimeMillis() + (et * 1000); |
||||
|
|
||||
|
AuthCode ac = new AuthCode(); |
||||
|
ac.setId(StrKit.getRandomUUID()); |
||||
|
ac.setAccountId(accountId); |
||||
|
ac.setType(authType); |
||||
|
ac.setExpireAt(expireAt); |
||||
|
|
||||
|
if (ac.save()) { |
||||
|
return ac.getId(); |
||||
|
} else { |
||||
|
throw new RuntimeException("保存 auth_code 记录失败,请联系管理员"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 看一眼授权码,未过期时则不删除 |
||||
|
*/ |
||||
|
public Ret peekAuthCode(String id) { |
||||
|
AuthCode authCode = dao.findById(id); |
||||
|
if (authCode != null) { |
||||
|
if (authCode.notExpired()) { |
||||
|
return Ret.ok("authCode", authCode); |
||||
|
} else { |
||||
|
authCode.delete(); |
||||
|
return Ret.fail("msg", "授权码已过期"); |
||||
|
} |
||||
|
} else { |
||||
|
return Ret.fail("msg", "授权码不存在"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 主动清除未使用过的过期授权码 |
||||
|
* 不用经常调用,因为授权码在第一次使用时会自动删除,过期的未删除的授权码仅是未使用过的 |
||||
|
*/ |
||||
|
public int clearExpiredAuthCode() { |
||||
|
return Db.update("delete from auth_code where expireAt < ?", System.currentTimeMillis()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
@ -0,0 +1,96 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.controller; |
||||
|
|
||||
|
import com.jfinal.core.Controller; |
||||
|
import com.bt.common.model.Account; |
||||
|
import com.bt.login.LoginService; |
||||
|
import com.jfinal.core.NotAction; |
||||
|
|
||||
|
/** |
||||
|
* 基础控制器,方便获取登录信息 |
||||
|
* |
||||
|
* 警告:由于 BaseController 中声明了 Account loginAccount 属性, |
||||
|
* 所以不能使用 FastControllerFactory 这类回收 Controller 的用法 |
||||
|
* 在 jfinal 3.5 发布以后,可以通过继承 _clear_() 方法来清除属性中的值 |
||||
|
* 才能使用 FastControllerFactory |
||||
|
* 用户自己的 Controller 也是同样的道理 |
||||
|
* |
||||
|
* 注意: |
||||
|
* 需要 LoginSessionInterceptor 配合,该拦截器使用 |
||||
|
* setAttr("loginAccount", ...) 事先注入了登录账户 |
||||
|
* 否则即便已经登录,该控制器也会认为没有登录 |
||||
|
* |
||||
|
*/ |
||||
|
public class BaseController extends Controller { |
||||
|
|
||||
|
/** |
||||
|
* 警告:由于这个属性的存在,不能直接使用 FastControllerFactory,除非使用 jfinal 3.5 |
||||
|
* 并覆盖父类中的 _clear_() 方法清除本类中与父类中的属性值,详情见本类中的 |
||||
|
* protected void _clear_() 方法 |
||||
|
* |
||||
|
* 原因是 FastControllerFactory 是回收使用 controller 对象的,所以要在 _clear()_ |
||||
|
* 中清除上次使用时的属性值 |
||||
|
*/ |
||||
|
private Account loginAccount = null; |
||||
|
|
||||
|
protected void _clear_() { |
||||
|
this.loginAccount = null; |
||||
|
super._clear_(); |
||||
|
} |
||||
|
|
||||
|
@NotAction |
||||
|
public Account getLoginAccount() { |
||||
|
if (loginAccount == null) { |
||||
|
loginAccount = getAttr(LoginService.loginAccountCacheName); |
||||
|
if (loginAccount != null && ! loginAccount.isStatusOk()) { |
||||
|
throw new IllegalStateException("当前用户状态不允许登录,status = " + loginAccount.getStatus()); |
||||
|
} |
||||
|
} |
||||
|
return loginAccount; |
||||
|
} |
||||
|
|
||||
|
@NotAction |
||||
|
public boolean isLogin() { |
||||
|
return getLoginAccount() != null; |
||||
|
} |
||||
|
|
||||
|
@NotAction |
||||
|
public boolean notLogin() { |
||||
|
return !isLogin(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取登录账户id |
||||
|
* 确保在 FrontAuthInterceptor 之下使用,或者 isLogin() 为 true 时使用 |
||||
|
* 也即确定已经是在登录后才可调用 |
||||
|
*/ |
||||
|
@NotAction |
||||
|
public int getLoginAccountId() { |
||||
|
return getLoginAccount().getId(); |
||||
|
} |
||||
|
|
||||
|
@NotAction |
||||
|
public boolean isPjaxRequest() { |
||||
|
return "true".equalsIgnoreCase(getHeader("X-PJAX")); |
||||
|
} |
||||
|
|
||||
|
@NotAction |
||||
|
public boolean isAjaxRequest() { |
||||
|
return "XMLHttpRequest".equalsIgnoreCase(getHeader("X-Requested-With")); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
@ -0,0 +1,95 @@ |
|||||
|
package com.bt.common.directive; |
||||
|
|
||||
|
import java.io.IOException; |
||||
|
import java.time.LocalDateTime; |
||||
|
import java.time.format.DateTimeFormatter; |
||||
|
import java.time.temporal.TemporalAccessor; |
||||
|
import java.util.Date; |
||||
|
import com.jfinal.template.Directive; |
||||
|
import com.jfinal.template.Env; |
||||
|
import com.jfinal.template.TemplateException; |
||||
|
import com.jfinal.template.expr.ast.Expr; |
||||
|
import com.jfinal.template.expr.ast.ExprList; |
||||
|
import com.jfinal.template.io.Writer; |
||||
|
import com.jfinal.template.stat.ParseException; |
||||
|
import com.jfinal.template.stat.Scope; |
||||
|
/** |
||||
|
* #date 日期格式化输出指令 |
||||
|
* |
||||
|
* 三种用法:<br> |
||||
|
* 1:#date(createAt) 用默认 datePattern 配置,输出 createAt 变量中的日期值<br> |
||||
|
* 2:#date(createAt, "yyyy-MM-dd HH:mm:ss") 用第二个参数指定的 datePattern,输出 createAt 变量中的日期值<br> |
||||
|
* 3:#date() 用默认 datePattern 配置,输出 “当前” 日期值 |
||||
|
* |
||||
|
* 注意:<br> |
||||
|
* 1:#date 指令中的参数可以是变量,例如:#date(d, p) 中的 d 与 p 可以全都是变量<br> |
||||
|
* 2:默认 datePattern 可通过 Engine.setDatePattern(...) 进行配置 |
||||
|
* |
||||
|
* @author Max_Qiu |
||||
|
*/ |
||||
|
public class MyDateDirective extends Directive { |
||||
|
private Expr valueExpr; |
||||
|
private Expr datePatternExpr; |
||||
|
private int paraNum; |
||||
|
@Override |
||||
|
public void setExprList(ExprList exprList) { |
||||
|
this.paraNum = exprList.length(); |
||||
|
if (paraNum > 2) { |
||||
|
throw new ParseException("#date 指令最多两个参数", location); |
||||
|
} |
||||
|
if (paraNum == 0) { |
||||
|
this.valueExpr = null; |
||||
|
this.datePatternExpr = null; |
||||
|
} else if (paraNum == 1) { |
||||
|
this.valueExpr = exprList.getExpr(0); |
||||
|
this.datePatternExpr = null; |
||||
|
} else if (paraNum == 2) { |
||||
|
this.valueExpr = exprList.getExpr(0); |
||||
|
this.datePatternExpr = exprList.getExpr(1); |
||||
|
} |
||||
|
} |
||||
|
@Override |
||||
|
public void exec(Env env, Scope scope, Writer writer) { |
||||
|
if (paraNum == 1) { |
||||
|
outputWithDatePattern(scope, env.getEngineConfig().getDatePattern(), writer); |
||||
|
} else if (paraNum == 2) { |
||||
|
Object datePattern = this.datePatternExpr.eval(scope); |
||||
|
if (!(datePattern instanceof String)) { |
||||
|
throw new TemplateException("#date 指令的第二个参数类型必须是 String", location); |
||||
|
} |
||||
|
outputWithDatePattern(scope, (String)datePattern, writer); |
||||
|
} else { |
||||
|
write(writer, LocalDateTime.now(), env.getEngineConfig().getDatePattern()); |
||||
|
} |
||||
|
} |
||||
|
private void outputWithDatePattern(Scope scope, String datePattern, Writer writer) { |
||||
|
Object value = valueExpr.eval(scope); |
||||
|
if (value instanceof TemporalAccessor) { |
||||
|
write(writer, (TemporalAccessor)value, datePattern); |
||||
|
} else if (value instanceof Date) { |
||||
|
write(writer, (Date)value, datePattern); |
||||
|
} else if (value != null) { |
||||
|
throw new TemplateException("#date 指令的第一个参数必须是实现TemporalAccessor接口的时间类型或者是Date类型", location); |
||||
|
} |
||||
|
} |
||||
|
private void write(Writer writer, TemporalAccessor temporal, String datePattern) { |
||||
|
String s; |
||||
|
try { |
||||
|
s = DateTimeFormatter.ofPattern(datePattern).format(temporal); |
||||
|
} catch (Exception e) { |
||||
|
throw new TemplateException("格式化出错,请检查第二个参数:" + datePattern, location, e); |
||||
|
} |
||||
|
try { |
||||
|
writer.write(s, 0, s.length()); |
||||
|
} catch (IOException e) { |
||||
|
throw new TemplateException(e.getMessage(), location, e); |
||||
|
} |
||||
|
} |
||||
|
private void write(Writer writer, Date date, String datePattern) { |
||||
|
try { |
||||
|
writer.write(date, datePattern); |
||||
|
} catch (IOException e) { |
||||
|
throw new TemplateException(e.getMessage(), location, e); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,80 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.handler; |
||||
|
|
||||
|
import com.jfinal.core.Action; |
||||
|
import com.jfinal.core.JFinal; |
||||
|
import com.jfinal.handler.Handler; |
||||
|
import com.jfinal.kit.HandlerKit; |
||||
|
import com.jfinal.kit.StrKit; |
||||
|
import javax.servlet.http.HttpServletRequest; |
||||
|
import javax.servlet.http.HttpServletResponse; |
||||
|
|
||||
|
/** |
||||
|
* 只影响 index() 与 detail(),这两个 action 的 url 变得更短,有利于 SEO,规则是: |
||||
|
* 1:actionMethod 为 "index" 时,判断是否有 urlPara,有的话直接将 target 转成 controllerKey + "detail" + urlPara |
||||
|
* 2:actionMethod 为 "detail" 时,直接阻止请求,不允许这样的 url 出现 |
||||
|
* 3:其它情况直接放行 |
||||
|
* |
||||
|
* 注意两点: |
||||
|
* 1:由于对带有 urlPara 的 index() 请求,都被转成了 detail() 请求,所以这类应用场景需要将 index() 方法改名 |
||||
|
* 并利用 @ActionKey(controllerKey) 指定为自己想要的 url,具体可参考 "/my" 模块中的一些 Controller 用法 |
||||
|
* |
||||
|
* 2:由于 detail() 方法已被阻止,系统中所有 url 都不能添加 "detail" 为 actionMethod 部分,直接去掉这部分即可 |
||||
|
* |
||||
|
*/ |
||||
|
public class UrlSeoHandler extends Handler { |
||||
|
|
||||
|
private String detailMethodName; |
||||
|
private String detailMethodWithSlash; |
||||
|
|
||||
|
public UrlSeoHandler() { |
||||
|
detailMethodName = "detail"; |
||||
|
detailMethodWithSlash = "/" + detailMethodName + "/"; |
||||
|
} |
||||
|
|
||||
|
public UrlSeoHandler(String detailMethodName) { |
||||
|
if (StrKit.isBlank(detailMethodName)) { |
||||
|
throw new IllegalArgumentException("detailMethodName can not be blank."); |
||||
|
} |
||||
|
this.detailMethodName = detailMethodName; |
||||
|
this.detailMethodWithSlash = "/" + detailMethodName + "/"; |
||||
|
} |
||||
|
|
||||
|
public void handle(String target, HttpServletRequest request, HttpServletResponse response, boolean[] isHandled) { |
||||
|
// 静态请求直接跳出
|
||||
|
if (target.indexOf('.') != -1) { |
||||
|
return ; |
||||
|
} |
||||
|
|
||||
|
String[] urlPara = {null}; |
||||
|
Action action = JFinal.me().getAction(target, urlPara); |
||||
|
if (action != null) { |
||||
|
String methodName = action.getMethodName(); |
||||
|
if ("index".equals(methodName)) { |
||||
|
if (StrKit.notBlank(urlPara[0])) { |
||||
|
target = action.getControllerKey() + detailMethodWithSlash + urlPara[0]; |
||||
|
} |
||||
|
} |
||||
|
// 不允许 "detail" 出现在 url 中,映射到 detail() 方法的 url 仅使用 "/project/123" 这种格式
|
||||
|
else if (detailMethodName.equals(methodName)) { |
||||
|
HandlerKit.renderError404(request, response, isHandled); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
next.handle(target, request, response, isHandled); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
package com.bt.common.interceptor; |
||||
|
|
||||
|
import com.jfinal.aop.Interceptor; |
||||
|
import com.jfinal.aop.Invocation; |
||||
|
import com.bt.common.model.Account; |
||||
|
import com.bt.login.LoginService; |
||||
|
import com.jfinal.plugin.activerecord.Db; |
||||
|
|
||||
|
/** |
||||
|
* 在 role 表中存在的 accountId 拥有清除前端 cache 的权限 |
||||
|
* 超级管理员符合上述条件 |
||||
|
*/ |
||||
|
public class AuthCacheClearInterceptor implements Interceptor { |
||||
|
|
||||
|
public static boolean isAdmin(Account loginAccount) { |
||||
|
if (loginAccount == null || !loginAccount.isStatusOk()) { |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
String admin = "select accountId from account_role where accountId = ? limit 1"; |
||||
|
Integer accountId = Db.queryInt(admin, loginAccount.getId()); |
||||
|
return accountId != null; |
||||
|
} |
||||
|
|
||||
|
public void intercept(Invocation inv) { |
||||
|
Account loginAccount = inv.getController().getAttr(LoginService.loginAccountCacheName); |
||||
|
if (isAdmin(loginAccount)) { |
||||
|
inv.invoke(); |
||||
|
} else { |
||||
|
inv.getController().renderError(404); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,72 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.interceptor; |
||||
|
|
||||
|
import com.jfinal.aop.Interceptor; |
||||
|
import com.jfinal.aop.Invocation; |
||||
|
import com.jfinal.core.Controller; |
||||
|
|
||||
|
/** |
||||
|
* SEO 搜索引擎优化基础拦截器 |
||||
|
*/ |
||||
|
public abstract class BaseSeoInterceptor implements Interceptor { |
||||
|
|
||||
|
public static final String SEO_TITLE = "seoTitle"; |
||||
|
public static final String SEO_KEYWORDS = "seoKeywords"; |
||||
|
public static final String SEO_DESCR = "seoDescr"; |
||||
|
|
||||
|
protected void setSeoTitle(Controller c, String seoTitle) { |
||||
|
c.setAttr(SEO_TITLE, seoTitle); |
||||
|
} |
||||
|
|
||||
|
protected void setSeoKeywords(Controller c, String seoKeywords) { |
||||
|
c.setAttr(SEO_KEYWORDS, seoKeywords); |
||||
|
} |
||||
|
|
||||
|
protected void setSeoDescr(Controller c, String seoDescr) { |
||||
|
c.setAttr(SEO_DESCR, seoDescr); |
||||
|
} |
||||
|
|
||||
|
public final void intercept(Invocation inv) { |
||||
|
inv.invoke(); |
||||
|
|
||||
|
Controller c = inv.getController(); |
||||
|
String method = inv.getMethodName(); |
||||
|
if (method.equals("index")) { |
||||
|
if (c.getPara() == null) { |
||||
|
indexSeo(c); |
||||
|
} else { |
||||
|
detailSeo(c); |
||||
|
} |
||||
|
} else if (method.equals("detail")) { |
||||
|
detailSeo(c); |
||||
|
} else { |
||||
|
othersSeo(c, method); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 对 index() action 进行 seo
|
||||
|
public abstract void indexSeo(Controller c) ; |
||||
|
|
||||
|
// 对 detail() action 进行 seo
|
||||
|
public abstract void detailSeo(Controller c) ; |
||||
|
|
||||
|
// 对其它方法进行 seo,只需在该方法的实现类中判断一下 method 参数名
|
||||
|
// 并调用前面的 seoTitle、setSeoKeywords、setSeoDescr 三个工具方法即可完成 seo
|
||||
|
public abstract void othersSeo(Controller c, String method); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
@ -0,0 +1,43 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.interceptor; |
||||
|
|
||||
|
import com.jfinal.aop.Interceptor; |
||||
|
import com.jfinal.aop.Invocation; |
||||
|
import com.jfinal.kit.StrKit; |
||||
|
import com.bt.login.LoginService; |
||||
|
|
||||
|
/** |
||||
|
* 需要登录才能授权的操作,例如文件下载 |
||||
|
* |
||||
|
* 未登录将被重定向到登录界面,登录成功后又会再返回到原来想跳去的 url |
||||
|
* 注意在登录表单中有 returnUrl 变量的传递才能跳到原来想跳去的 url |
||||
|
* 详见登录页面表单传参 |
||||
|
*/ |
||||
|
public class FrontAuthInterceptor implements Interceptor { |
||||
|
public void intercept(Invocation inv) { |
||||
|
if (inv.getController().getAttr(LoginService.loginAccountCacheName) != null) { |
||||
|
inv.invoke(); |
||||
|
} else { |
||||
|
String queryString = inv.getController().getRequest().getQueryString(); |
||||
|
if (StrKit.isBlank(queryString)) { |
||||
|
inv.getController().redirect("/login?returnUrl=" + inv.getActionKey()); |
||||
|
} else { |
||||
|
inv.getController().redirect("/login?returnUrl=" + inv.getActionKey() + "?" + queryString); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,76 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.interceptor; |
||||
|
|
||||
|
import com.bt.common.kit.IpKit; |
||||
|
import com.bt.common.model.Remind; |
||||
|
import com.jfinal.aop.Inject; |
||||
|
import com.jfinal.aop.Interceptor; |
||||
|
import com.jfinal.aop.Invocation; |
||||
|
import com.jfinal.core.Controller; |
||||
|
import com.bt.common.model.Account; |
||||
|
import com.bt.login.LoginService; |
||||
|
import com.bt.my.newsfeed.RemindService; |
||||
|
|
||||
|
/** |
||||
|
* 从 cookie 中获取 sessionId,如果获取到则根据该值使用 LoginService |
||||
|
* 得到登录的 Account 对象 ---> loginAccount,供后续的流程使用 |
||||
|
* |
||||
|
* 注意:将此拦截器设置为全局拦截器,所有 action 都需要 |
||||
|
*/ |
||||
|
public class LoginSessionInterceptor implements Interceptor { |
||||
|
|
||||
|
@Inject |
||||
|
LoginService loginSrv; |
||||
|
|
||||
|
@Inject |
||||
|
RemindService remindSrv; |
||||
|
|
||||
|
public static final String remindKey = "_remind"; |
||||
|
|
||||
|
public void intercept(Invocation inv) { |
||||
|
Account loginAccount = null; |
||||
|
Controller c = inv.getController(); |
||||
|
String sessionId = c.getCookie(LoginService.sessionIdName); |
||||
|
if (sessionId != null) { |
||||
|
loginAccount = loginSrv.getLoginAccountWithSessionId(sessionId); |
||||
|
if (loginAccount == null) { |
||||
|
String loginIp = IpKit.getRealIp(c.getRequest()); |
||||
|
loginAccount = loginSrv.loginWithSessionId(sessionId, loginIp); |
||||
|
} |
||||
|
if (loginAccount != null) { |
||||
|
// 用户登录账号
|
||||
|
c.setAttr(LoginService.loginAccountCacheName, loginAccount); |
||||
|
} else { |
||||
|
c.removeCookie(LoginService.sessionIdName); // cookie 登录未成功,证明该 cookie 已经没有用处,删之
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
inv.invoke(); |
||||
|
|
||||
|
if (loginAccount != null) { |
||||
|
// remind 对象用于生成提醒 tips
|
||||
|
Remind remind = remindSrv.getRemind(loginAccount.getId()); |
||||
|
if (remind != null) { |
||||
|
if (remind.getReferMe() > 0 || remind.getMessage() > 0 || remind.getFans() > 0) { |
||||
|
c.setAttr(remindKey, remind); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
@ -0,0 +1,69 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.kit; |
||||
|
|
||||
|
import com.jfinal.aop.Aop; |
||||
|
import com.bt.common.interceptor.AuthCacheClearInterceptor; |
||||
|
import com.jfinal.plugin.druid.DruidStatViewHandler; |
||||
|
import com.jfinal.plugin.druid.IDruidStatViewAuth; |
||||
|
import com.bt.common.model.Account; |
||||
|
import com.bt.login.LoginService; |
||||
|
import javax.servlet.http.Cookie; |
||||
|
import javax.servlet.http.HttpServletRequest; |
||||
|
|
||||
|
/** |
||||
|
* 创建 DruidStatViewHandler 的工具类 |
||||
|
* |
||||
|
* 可通过 "/assets/druid" 访问到 druid 提供的 sql 监控与统计功能 |
||||
|
* 方便找到慢 sql,进而对慢 sql 进行优化 |
||||
|
* 注意:这里的访问路径是下面代码中指定的,可以设置为任意路径 |
||||
|
* |
||||
|
* 注意 druid 监控模块中使用的静态资源文件如 .html .css 被打包在了 druid 的jar 包之中 |
||||
|
* 如果你的项目在前端有 nginx 代理过了这些静态资源,需要将这些资源解压出来并放到 |
||||
|
* 正确的目录下面 |
||||
|
* |
||||
|
* 具体到该配置中的 url 为 "/assets/druid",那么相关静态资源需要解压到该目录之下 |
||||
|
*/ |
||||
|
public class DruidKit { |
||||
|
|
||||
|
static LoginService loginSrv = Aop.get(LoginService.class); |
||||
|
|
||||
|
public static DruidStatViewHandler getDruidStatViewHandler() { |
||||
|
return new DruidStatViewHandler("/assets/druid", new IDruidStatViewAuth() { |
||||
|
public boolean isPermitted(HttpServletRequest request) { |
||||
|
String sessionId = getCookie(request, LoginService.sessionIdName); |
||||
|
if (sessionId != null) { |
||||
|
Account loginAccount = loginSrv.getLoginAccountWithSessionId(sessionId); |
||||
|
return AuthCacheClearInterceptor.isAdmin(loginAccount); |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public static String getCookie(HttpServletRequest request, String name) { |
||||
|
Cookie cookie = getCookieObject(request, name); |
||||
|
return cookie != null ? cookie.getValue() : null; |
||||
|
} |
||||
|
|
||||
|
private static Cookie getCookieObject(HttpServletRequest request, String name) { |
||||
|
Cookie[] cookies = request.getCookies(); |
||||
|
if (cookies != null) |
||||
|
for (Cookie cookie : cookies) |
||||
|
if (cookie.getName().equals(name)) |
||||
|
return cookie; |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,135 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.kit; |
||||
|
|
||||
|
import com.jfinal.log.Log; |
||||
|
import org.apache.commons.mail.EmailException; |
||||
|
import org.apache.commons.mail.SimpleEmail; |
||||
|
import com.jfinal.kit.StrKit; |
||||
|
|
||||
|
/** |
||||
|
* 邮件发送工具类 |
||||
|
* |
||||
|
* 文档: |
||||
|
* 核心代码在 com.sun.mail.smtp.SMTPTransport.java。通过调试可以了解很多细节 |
||||
|
* EmailConstants 中有很多可配置常量 |
||||
|
*/ |
||||
|
public class EmailKit { |
||||
|
|
||||
|
private static final Log log = Log.getLog(EmailKit.class); |
||||
|
|
||||
|
private static boolean debug = false; |
||||
|
|
||||
|
private static boolean initSsl = false; |
||||
|
|
||||
|
public static void setDebug(boolean debug) { |
||||
|
EmailKit.debug = debug; |
||||
|
} |
||||
|
|
||||
|
private static void initSsl(String emailServer) { |
||||
|
if (!initSsl) { |
||||
|
/** |
||||
|
* 配置邮件发送客户端信任的发送服务器,配置为 "*" 时信任所有服务器 |
||||
|
* 必须配置,否则将抛出如下异常: |
||||
|
* javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target |
||||
|
* at com.sun.mail.smtp.SMTPTransport.openServer(SMTPTransport.java:2120) |
||||
|
* at com.sun.mail.smtp.SMTPTransport.protocolConnect(SMTPTransport.java:712) |
||||
|
*/ |
||||
|
// System.setProperty("mail.smtp.ssl.trust", emailServer);
|
||||
|
System.setProperty("mail.smtp.ssl.trust", "*"); |
||||
|
|
||||
|
/** |
||||
|
* 本变量不配置也能支持 SSL,因为本变量与 setSSLOnConnect(true) 都能触发 Email.java 中的如下代码被调用 |
||||
|
* properties.setProperty(EmailConstants.MAIL_SMTP_SOCKET_FACTORY_CLASS, "javax.net.ssl.SSLSocketFactory"); |
||||
|
* 其中第一个参数值为: "mail.smtp.socketFactory.class" |
||||
|
* 从而在 com.sun.mail.util.SocketFetcher.getSocket(String host, int port, Properties props, String prefix, boolean useSSL) |
||||
|
* 方法中在 if (isSSL) 两个分支中都通过 "mail.smtp.socketFactory.class" 获取到了 |
||||
|
* "javax.net.ssl.SSLSocketFactory",从而后面的 ssl 逻辑变成一样的了 |
||||
|
* |
||||
|
* |
||||
|
* 本配置在同时配置 SimpleEmail.setDebug(true) 时,控制台第 9 行输出的 isSSL 值为 true: |
||||
|
* DEBUG SMTP: trying to connect to host "old.jfinal.com", port 465, isSSL true |
||||
|
* |
||||
|
* 建议:使用该配置 |
||||
|
*/ |
||||
|
System.setProperty("mail.smtp.ssl.enable", "true"); |
||||
|
|
||||
|
initSsl = true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static String sendEmail(String fromEmail, String toEmail, String title, String content) { |
||||
|
return sendEmail(null, fromEmail, null, toEmail, title, content); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 阿里云从 2019 年 9 月底开始禁止了 25 号端口(腾迅、华为云亦如此) |
||||
|
* 需要使用 465 端口。当密码不为空时认为是在使用 465 SSL 通道 |
||||
|
*/ |
||||
|
public static String sendEmail(String emailServer, String fromEmail, String password, String toEmail, String title, String content) { |
||||
|
// emailServer 为空时,默认使用本地 postfix 发送
|
||||
|
emailServer = StrKit.notBlank(emailServer) ? emailServer : "127.0.0.1"; |
||||
|
|
||||
|
initSsl(emailServer); |
||||
|
|
||||
|
SimpleEmail email = new SimpleEmail(); |
||||
|
email.setHostName(emailServer); |
||||
|
email.setDebug(debug); // 调试错误原因极其有用的配置
|
||||
|
|
||||
|
|
||||
|
email.setSSLOnConnect(true); // 走 465 SSL 通道必须的配置,最关键配置
|
||||
|
// email.setSslSmtpPort("465"); // 可选配置,默认值已经是 "465"
|
||||
|
// email.setStartTLSRequired(true); // 强制使用 StartTLS,在测试 SSL 是否可用时开启一下
|
||||
|
// email.setSSLCheckServerIdentity(true); // 验证服务端证书
|
||||
|
|
||||
|
|
||||
|
email.setSocketConnectionTimeout(32 * 1000); // 连接超时
|
||||
|
email.setSocketTimeout(32 * 1000); // socket IO 超时
|
||||
|
|
||||
|
// 如果密码为空,则不进行认证
|
||||
|
if (StrKit.notBlank(password)) { |
||||
|
email.setAuthentication(fromEmail, password); |
||||
|
} |
||||
|
|
||||
|
email.setCharset("utf-8"); |
||||
|
try { |
||||
|
email.addTo(toEmail); |
||||
|
email.setFrom(fromEmail); |
||||
|
email.setSubject(title); |
||||
|
email.setMsg(content); |
||||
|
return email.send(); |
||||
|
} catch (EmailException e) { |
||||
|
log.error(e.getMessage(), e); |
||||
|
throw new RuntimeException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static void main(String[] args) { |
||||
|
String ret = sendEmail( |
||||
|
"abc.com", // 邮件发送服务器地址
|
||||
|
"no-reply@abc.com", // 发件邮箱
|
||||
|
null, // 发件邮箱密码
|
||||
|
"test@test.com", // 收件地址
|
||||
|
"邮件标题", // 邮件标题
|
||||
|
"content"); // 邮件内容
|
||||
|
System.out.println("发送返回值: " + ret); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
@ -0,0 +1,265 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.kit; |
||||
|
|
||||
|
import java.awt.Graphics; |
||||
|
import java.awt.Color; |
||||
|
import java.awt.Image; |
||||
|
import java.awt.image.BufferedImage; |
||||
|
import java.io.File; |
||||
|
import java.io.FileOutputStream; |
||||
|
import java.io.IOException; |
||||
|
import javax.imageio.IIOImage; |
||||
|
import javax.imageio.ImageIO; |
||||
|
import javax.imageio.ImageWriteParam; |
||||
|
import javax.imageio.ImageWriter; |
||||
|
import javax.imageio.metadata.IIOMetadata; |
||||
|
import javax.imageio.stream.ImageOutputStream; |
||||
|
import com.jfinal.kit.StrKit; |
||||
|
|
||||
|
/** |
||||
|
* ImageKit 图片高保真缩放与裁剪,不依赖于任何第三方库 |
||||
|
*/ |
||||
|
public class ImageKit { |
||||
|
|
||||
|
private final static String[] imgExts = new String[]{"jpg", "jpeg", "png", "bmp"}; |
||||
|
|
||||
|
public static String getExtName(String fileName) { |
||||
|
int index = fileName.lastIndexOf('.'); |
||||
|
if (index != -1 && (index + 1) < fileName.length()) { |
||||
|
return fileName.substring(index + 1); |
||||
|
} else { |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 通过文件扩展名,判断是否为支持的图像文件,支持则返回 true,否则返回 false |
||||
|
*/ |
||||
|
public static boolean isImageExtName(String fileName) { |
||||
|
if (StrKit.isBlank(fileName)) { |
||||
|
return false; |
||||
|
} |
||||
|
fileName = fileName.trim().toLowerCase(); |
||||
|
String ext = getExtName(fileName); |
||||
|
if (ext != null) { |
||||
|
for (String s : imgExts) { |
||||
|
if (s.equals(ext)) { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
public static final boolean notImageExtName(String fileName) { |
||||
|
return ! isImageExtName(fileName); |
||||
|
} |
||||
|
|
||||
|
public static BufferedImage loadImageFile(String sourceImageFileName) { |
||||
|
if (notImageExtName(sourceImageFileName)) { |
||||
|
throw new IllegalArgumentException("只支持如下几种类型的图像文件:jpg、jpeg、png、bmp"); |
||||
|
} |
||||
|
|
||||
|
File sourceImageFile = new File(sourceImageFileName); |
||||
|
if (!sourceImageFile.exists() || !sourceImageFile.isFile()) { |
||||
|
throw new IllegalArgumentException("文件不存在"); |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
return ImageIO.read(sourceImageFile); |
||||
|
} catch (Exception e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 以 maxWidth 为界对图像进行缩放,高保真保存 |
||||
|
* 1:当图像 width > maxWidth 时,将宽度变为 maxWidth,高度等比例进行缩放,高保真保存 |
||||
|
* 2:当图像 width <= maxWidth 时,宽高保持不变,只进行高保真保存 |
||||
|
*/ |
||||
|
public static void zoom(int maxWidth, File srcFile, String saveFile) { |
||||
|
float quality = 0.8f; |
||||
|
|
||||
|
try { |
||||
|
BufferedImage srcImage = ImageIO.read(srcFile); |
||||
|
int srcWidth = srcImage.getWidth(); |
||||
|
int srcHeight = srcImage.getHeight(); |
||||
|
|
||||
|
// 当宽度在 maxWidth 范围之内,不改变图像宽高,只进行图像高保真保存
|
||||
|
if (srcWidth <= maxWidth) { |
||||
|
/** |
||||
|
* 如果图像不进行缩放的话, resize 就没有必要了, |
||||
|
* 经过测试是否有 resize 这一步,生成的结果图像完全一样,一个字节都不差 |
||||
|
* 所以删掉 resize,可以提升性能,少一步操作 |
||||
|
*/ |
||||
|
// BufferedImage ret = resize(srcImage, srcWidth, srcHeight);
|
||||
|
// saveWithQuality(ret, quality, saveFile);
|
||||
|
saveWithQuality(srcImage, quality, saveFile); |
||||
|
} |
||||
|
// 当宽度超出 maxWidth 范围,将宽度变为 maxWidth,而高度按比例变化
|
||||
|
else { |
||||
|
float scalingRatio = (float)maxWidth / (float)srcWidth; // 计算缩放比率
|
||||
|
float maxHeight = ((float)srcHeight * scalingRatio); // 计算缩放后的高度
|
||||
|
BufferedImage ret = resize(srcImage, maxWidth, (int)maxHeight); |
||||
|
saveWithQuality(ret, quality, saveFile); |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 对图片进行剪裁,只保存选中的区域 |
||||
|
* @param sourceImageFile 原图 |
||||
|
* @param left |
||||
|
* @param top |
||||
|
* @param width |
||||
|
* @param height |
||||
|
*/ |
||||
|
public static BufferedImage crop(String sourceImageFile, int left, int top, int width, int height) { |
||||
|
if (notImageExtName(sourceImageFile)) { |
||||
|
throw new IllegalArgumentException("只支持如下几种类型的图像文件:jpg、jpeg、png、bmp"); |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
BufferedImage bi = ImageIO.read(new File(sourceImageFile)); |
||||
|
width = Math.min(width, bi.getWidth()); |
||||
|
height = Math.min(height, bi.getHeight()); |
||||
|
if(width <= 0) width = bi.getWidth(); |
||||
|
if(height <= 0) height = bi.getHeight(); |
||||
|
|
||||
|
left = Math.min(Math.max(0, left), bi.getWidth() - width); |
||||
|
top = Math.min(Math.max(0, top), bi.getHeight() - height); |
||||
|
|
||||
|
BufferedImage subImage = bi.getSubimage(left, top, width, height); |
||||
|
return subImage; // return ImageIO.write(bi, "jpeg", fileDest);
|
||||
|
} catch (Exception e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public static void save(BufferedImage bi, String outputImageFile) { |
||||
|
FileOutputStream newImage = null; |
||||
|
try { |
||||
|
// ImageIO.write(bi, "jpg", new File(outputImageFile));
|
||||
|
ImageIO.write(bi, getExtName(outputImageFile), new File(outputImageFile)); |
||||
|
} catch (Exception e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} finally { |
||||
|
if (newImage != null) { |
||||
|
try { |
||||
|
newImage.close(); |
||||
|
} catch (IOException e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 高保真缩放 |
||||
|
*/ |
||||
|
public static BufferedImage resize(BufferedImage bi, int toWidth, int toHeight) { |
||||
|
Graphics g = null; |
||||
|
try { |
||||
|
// 从 BufferedImage 对象中获取一个经过缩放的 image
|
||||
|
Image scaledImage = bi.getScaledInstance(toWidth, toHeight, Image.SCALE_SMOOTH); |
||||
|
// 创建 BufferedImage 对象,存放缩放过的 image
|
||||
|
BufferedImage ret = new BufferedImage(toWidth, toHeight, BufferedImage.TYPE_INT_RGB); |
||||
|
g = ret.getGraphics(); |
||||
|
g.drawImage(scaledImage, 0, 0, null); |
||||
|
// g.drawImage(scaledImage, 0, 0, Color.WHITE, null);
|
||||
|
return ret; |
||||
|
} catch (Exception e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} finally { |
||||
|
if (g != null) { |
||||
|
g.dispose(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* jfinal.com 使用参数为宽:200, 高:200, 质量:0.8, |
||||
|
* 生成大小为:6.79 KB (6,957 字节) |
||||
|
* |
||||
|
* 如果使用参数为宽:120, 高:120, 质量:0.8, |
||||
|
* 则生成的图片大小为:3.45 KB (3,536 字节) |
||||
|
* |
||||
|
* 如果使用参数为宽:300, 高:300, 质量:0.5, |
||||
|
* 则生成的图片大小为:7.54 KB (7,725 字节) |
||||
|
* |
||||
|
* |
||||
|
* 建议使用 0.8 的 quality 并且稍大点的宽高 |
||||
|
* 只选用两种质量:0.8 与 0.9,这两个差别不是很大, |
||||
|
* 但是如果尺寸大些的话,选用 0.8 比 0.9 要划算,因为占用的空间差不多的时候,尺寸大些的更清晰 |
||||
|
*/ |
||||
|
public static void saveWithQuality(BufferedImage im, float quality, String outputImageFile) { |
||||
|
ImageWriter writer = null; |
||||
|
FileOutputStream newImage = null; |
||||
|
try { |
||||
|
BufferedImage newBufferedImage = new BufferedImage(im.getWidth(), im.getHeight(), BufferedImage.TYPE_INT_RGB); |
||||
|
newBufferedImage.createGraphics().drawImage(im, 0, 0, Color.WHITE, null); |
||||
|
// 输出到文件流
|
||||
|
newImage = new FileOutputStream(outputImageFile); |
||||
|
|
||||
|
writer = ImageIO.getImageWritersBySuffix("jpg").next(); |
||||
|
ImageWriteParam param = writer.getDefaultWriteParam(); |
||||
|
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); |
||||
|
param.setCompressionQuality(quality); |
||||
|
ImageOutputStream os = ImageIO.createImageOutputStream(newImage); |
||||
|
writer.setOutput(os); |
||||
|
writer.write((IIOMetadata) null, new IIOImage(newBufferedImage, null, null), param); |
||||
|
os.flush(); |
||||
|
os.close(); |
||||
|
} |
||||
|
catch (IOException e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} |
||||
|
finally { |
||||
|
if (writer != null) { |
||||
|
try {writer.dispose();} catch (Throwable e) {} |
||||
|
} |
||||
|
if (newImage != null) { |
||||
|
try {newImage.close();} catch (IOException e) {throw new RuntimeException(e);} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* 老版本 |
||||
|
public static void saveWithQuality(BufferedImage im, float quality, String outputImageFile) { |
||||
|
FileOutputStream newImage = null; |
||||
|
try { |
||||
|
// 输出到文件流
|
||||
|
newImage = new FileOutputStream(outputImageFile); |
||||
|
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(newImage); |
||||
|
JPEGEncodeParam jep = JPEGCodec.getDefaultJPEGEncodeParam(im); |
||||
|
// 压缩质量, 0.75 就算是高质量
|
||||
|
jep.setQuality(quality, true); // jep.setQuality(0.9f, true);
|
||||
|
encoder.encode(im, jep); |
||||
|
// 近JPEG编码
|
||||
|
// newImage.close();
|
||||
|
} catch (Exception e) { |
||||
|
throw new RuntimeException(e); |
||||
|
} finally { |
||||
|
if (newImage != null) { |
||||
|
try {newImage.close();} catch (IOException e) {throw new RuntimeException(e);} |
||||
|
} |
||||
|
} |
||||
|
}*/ |
||||
|
} |
||||
|
|
||||
|
|
||||
@ -0,0 +1,86 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.kit; |
||||
|
|
||||
|
import java.net.InetAddress; |
||||
|
import java.net.NetworkInterface; |
||||
|
import java.net.SocketException; |
||||
|
import java.util.Enumeration; |
||||
|
import javax.servlet.http.HttpServletRequest; |
||||
|
|
||||
|
/** |
||||
|
* IpKit 获取 ip 地址的工具类 |
||||
|
*/ |
||||
|
public class IpKit { |
||||
|
|
||||
|
public static String getRealIp(HttpServletRequest request) { |
||||
|
String ip = request.getHeader("x-forwarded-for"); |
||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
||||
|
ip = request.getHeader("Proxy-Client-IP"); |
||||
|
} |
||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
||||
|
ip = request.getHeader("WL-Proxy-Client-IP"); |
||||
|
} |
||||
|
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
||||
|
ip = request.getRemoteAddr(); |
||||
|
} |
||||
|
return ip; |
||||
|
} |
||||
|
|
||||
|
public static String getRealIpV2(HttpServletRequest request) { |
||||
|
String accessIP = request.getHeader("x-forwarded-for"); |
||||
|
if (null == accessIP) |
||||
|
return request.getRemoteAddr(); |
||||
|
return accessIP; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取本机 ip |
||||
|
* @return 本机IP |
||||
|
*/ |
||||
|
public static String getLocalIp() throws SocketException { |
||||
|
String localip = null; // 本地IP,如果没有配置外网IP则返回
|
||||
|
String netip = null; // 外网IP
|
||||
|
|
||||
|
Enumeration<NetworkInterface> netInterfaces = NetworkInterface.getNetworkInterfaces(); |
||||
|
InetAddress ip = null; |
||||
|
boolean finded = false; // 是否找到外网IP
|
||||
|
while (netInterfaces.hasMoreElements() && !finded) { |
||||
|
NetworkInterface ni = netInterfaces.nextElement(); |
||||
|
Enumeration<InetAddress> address = ni.getInetAddresses(); |
||||
|
while (address.hasMoreElements()) { |
||||
|
ip = address.nextElement(); |
||||
|
if (!ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1) {// 外网IP
|
||||
|
netip = ip.getHostAddress(); |
||||
|
finded = true; |
||||
|
break; |
||||
|
} else if (ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1) {// 内网IP
|
||||
|
localip = ip.getHostAddress(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (netip != null && !"".equals(netip)) { |
||||
|
return netip; |
||||
|
} else { |
||||
|
return localip; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
@ -0,0 +1,56 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.kit; |
||||
|
|
||||
|
import com.jfinal.plugin.activerecord.Db; |
||||
|
import com.jfinal.plugin.activerecord.Record; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* 敏感词检测 |
||||
|
*/ |
||||
|
public class SensitiveWordsKit { |
||||
|
|
||||
|
private static final List<String> sensitiveWords = build(); |
||||
|
|
||||
|
private static List<String> build() { |
||||
|
ArrayList<String> ret = new ArrayList<String>(); |
||||
|
List<Record> list = Db.find("select * from sensitive_words where status = 1"); |
||||
|
for (Record r : list) { |
||||
|
ret.add(r.getStr("word")); |
||||
|
} |
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 在 MyProjectValidator 中checkSensitiveWords(c.getPara("project.content"),....) 这行调用 |
||||
|
* 在第一次调用时传入 null 时 target 为 String[1]对象,而里面的内容为 null,第二次调用 target 则为 null |
||||
|
*/ |
||||
|
public static String checkSensitiveWord(String... target) { |
||||
|
if (target != null) { |
||||
|
for (String s : target) { |
||||
|
if (s != null) { |
||||
|
for (String sensitiveWord : sensitiveWords) { |
||||
|
if (s.indexOf(sensitiveWord) >= 0) { |
||||
|
return sensitiveWord; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,41 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.kit; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
/** |
||||
|
* SqlKit |
||||
|
*/ |
||||
|
public class SqlKit { |
||||
|
|
||||
|
/** |
||||
|
* 将 id 列表 join 起来,用逗号分隔,并且用小括号括起来 |
||||
|
*/ |
||||
|
public static void joinIds(List<Integer> idList, StringBuilder ret) { |
||||
|
ret.append("("); |
||||
|
boolean isFirst = true; |
||||
|
for (Integer id : idList) { |
||||
|
if (isFirst) { |
||||
|
isFirst = false; |
||||
|
} else { |
||||
|
ret.append(", "); |
||||
|
} |
||||
|
ret.append(id.toString()); |
||||
|
} |
||||
|
ret.append(")"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,25 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.loginlog; |
||||
|
|
||||
|
import com.jfinal.plugin.activerecord.Model; |
||||
|
|
||||
|
/** |
||||
|
* 记录用户登录信息的表 login_log |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public class LoginLog extends Model<LoginLog> { |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.loginlog; |
||||
|
|
||||
|
/** |
||||
|
* 用户登录时做日志,便于统计活跃用户 |
||||
|
* 用缓存缓冲一下,不要每次都写库 |
||||
|
* |
||||
|
* 例如缓存设置为 |
||||
|
* map {accountId, date, times} |
||||
|
* |
||||
|
* 用户的 session 过期时间设置为 30 分钟,如果过期就会触发登录操作 |
||||
|
* 另外注意,登录操作可能是利用 cookie 值自动实现的,但是这个自动实现也 |
||||
|
* 算做是一次登录,相当于只要 ehcache 中没有用户 session,建立这个 session就算做是一次登录 |
||||
|
* |
||||
|
* 集群部署时需要考虑 ehcache 同步问题 |
||||
|
*/ |
||||
|
public class LoginLogService { |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,55 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.model; |
||||
|
|
||||
|
import com.bt.common.safe.JsoupFilter; |
||||
|
import com.bt.common.model.base.BaseAccount; |
||||
|
|
||||
|
/** |
||||
|
* Account |
||||
|
*/ |
||||
|
public class Account extends BaseAccount<Account> { |
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
public static final String AVATAR_NO_AVATAR = "x.jpg"; // 刚注册时使用默认头像
|
||||
|
|
||||
|
public static final int STATUS_LOCK_ID = -1; // 锁定账号,无法做任何事情
|
||||
|
public static final int STATUS_REG = 0; // 注册、未激活
|
||||
|
public static final int STATUS_OK = 1; // 正常、已激活
|
||||
|
|
||||
|
public boolean isStatusOk() { |
||||
|
return getStatus() == STATUS_OK; |
||||
|
} |
||||
|
|
||||
|
public boolean isStatusReg() { |
||||
|
return getStatus() == STATUS_REG; |
||||
|
} |
||||
|
|
||||
|
public boolean isStatusLockId() { |
||||
|
return getStatus() == STATUS_LOCK_ID; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 过滤掉 nickName 中的 html 标记,恶意脚本 |
||||
|
*/ |
||||
|
protected void filter(int filterBy) { |
||||
|
JsoupFilter.filterAccountNickName(this); |
||||
|
} |
||||
|
|
||||
|
public Account removeSensitiveInfo() { |
||||
|
remove("password", "salt"); |
||||
|
return this; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,88 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.model; |
||||
|
|
||||
|
import com.bt.common.model.base.BaseAuthCode; |
||||
|
|
||||
|
/** |
||||
|
* 授权码,目前已用于: |
||||
|
* 1:账号激活 |
||||
|
* 2:密码找回 |
||||
|
* 未来随着业务增加可添加新类型,可能需要加 data 字段传递业务所需的额外数据 |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public class AuthCode extends BaseAuthCode<AuthCode> { |
||||
|
|
||||
|
public static final int TYPE_REG_ACTIVATE = 0; // 注册激活
|
||||
|
public static final int TYPE_RETRIEVE_PASSWORD = 1; // 找回密码
|
||||
|
|
||||
|
/** |
||||
|
* 在保存前保障 type 正确,随着 type 的增加,需要修改此处的代码 |
||||
|
*/ |
||||
|
public boolean save() { |
||||
|
int type = getType(); |
||||
|
if (type < TYPE_REG_ACTIVATE || type > TYPE_RETRIEVE_PASSWORD) { |
||||
|
throw new RuntimeException("授权码类型不正确: " + type); |
||||
|
} |
||||
|
return super.save(); |
||||
|
} |
||||
|
|
||||
|
public boolean update() { |
||||
|
throw new RuntimeException("授权码不支持更新操作"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 是否是有效的注册激活授权码 |
||||
|
*/ |
||||
|
public boolean isValidRegActivateAuthCode() { |
||||
|
return notExpired() && isTypeRegActivate(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 是否是有效的密码找回授权码 |
||||
|
*/ |
||||
|
public boolean isValidRetrievePasswordAuthCode() { |
||||
|
return notExpired() && isTypeRetrievePassword(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 是否是账号激活授权码 |
||||
|
*/ |
||||
|
public boolean isTypeRegActivate() { |
||||
|
return getType() == TYPE_REG_ACTIVATE; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 是否是密码找回授权码 |
||||
|
*/ |
||||
|
public boolean isTypeRetrievePassword() { |
||||
|
return getType() == TYPE_RETRIEVE_PASSWORD; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 是否已过期 |
||||
|
*/ |
||||
|
public boolean isExpired() { |
||||
|
return getExpireAt() < System.currentTimeMillis(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 是否未过期 |
||||
|
*/ |
||||
|
public boolean notExpired() { |
||||
|
return ! isExpired(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,36 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.model; |
||||
|
|
||||
|
import com.bt.common.model.base.BaseDocument; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal. |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public class Document extends BaseDocument<Document> { |
||||
|
|
||||
|
public static final int PUBLISH_NO = 0; // 不发布
|
||||
|
public static final int PUBLISH_YES = 1; // 发布
|
||||
|
|
||||
|
public boolean isPublished() { |
||||
|
return getPublish() == PUBLISH_YES; |
||||
|
} |
||||
|
|
||||
|
public boolean notPublish() { |
||||
|
return getPublish() == PUBLISH_NO; |
||||
|
} |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,25 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.model; |
||||
|
|
||||
|
import com.bt.common.model.base.BaseDownload; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal. |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public class Download extends BaseDownload<Download> { |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.model; |
||||
|
|
||||
|
import com.bt.common.model.base.BaseDownloadLog; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal. |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public class DownloadLog extends BaseDownloadLog<DownloadLog> { |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,78 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.model; |
||||
|
|
||||
|
import com.bt.common.model.base.BaseFavorite; |
||||
|
import java.util.HashMap; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal. |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public class Favorite extends BaseFavorite<Favorite> { |
||||
|
|
||||
|
// 收藏引用的资源类型
|
||||
|
public static final int REF_TYPE_PROJECT = 1; |
||||
|
public static final int REF_TYPE_SHARE = 2; |
||||
|
public static final int REF_TYPE_FEEDBACK = 3; |
||||
|
|
||||
|
private static Map<String, Integer> tableToTypeValue = new HashMap<String, Integer>(){{ |
||||
|
put("project", REF_TYPE_PROJECT); |
||||
|
put("share", REF_TYPE_SHARE); |
||||
|
put("feedback", REF_TYPE_FEEDBACK); |
||||
|
}}; |
||||
|
|
||||
|
private static Map<Integer, String> typeValueToTable = new HashMap<Integer, String>(){{ |
||||
|
put(REF_TYPE_PROJECT, "project"); |
||||
|
put(REF_TYPE_SHARE, "share"); |
||||
|
put(REF_TYPE_FEEDBACK, "feedback"); |
||||
|
}}; |
||||
|
|
||||
|
public static String getRefTable(int refType) { |
||||
|
return typeValueToTable.get(refType); |
||||
|
} |
||||
|
|
||||
|
public static int getRefType(String tableName) { |
||||
|
Integer refType = tableToTypeValue.get(tableName); |
||||
|
if (refType == null) { |
||||
|
throw new IllegalArgumentException("tableName 不正确"); |
||||
|
} |
||||
|
return refType; |
||||
|
} |
||||
|
|
||||
|
public static void checkRefTypeTable(String refTypeTable) { |
||||
|
if ( !tableToTypeValue.containsKey(refTypeTable) ) { |
||||
|
throw new IllegalArgumentException("refType 不正确"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 返回收藏资源的 url |
||||
|
*/ |
||||
|
public String getRefUrl() { |
||||
|
int refType = getRefType(); |
||||
|
int refId = getRefId(); |
||||
|
if (refType == REF_TYPE_PROJECT) { |
||||
|
return "/project/" + refId; |
||||
|
} else if (refType == REF_TYPE_SHARE) { |
||||
|
return "/share/" + refId; |
||||
|
} else if (refType == REF_TYPE_FEEDBACK) { |
||||
|
return "/feedback/" + refId; |
||||
|
} else { |
||||
|
throw new RuntimeException("refType 不正确,无法生成 url,reType = " + refType); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.model; |
||||
|
|
||||
|
import com.bt.common.safe.JsoupFilter; |
||||
|
import com.bt.common.model.base.BaseFeedback; |
||||
|
|
||||
|
/** |
||||
|
* 反馈 |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public class Feedback extends BaseFeedback<Feedback> { |
||||
|
|
||||
|
/** |
||||
|
* 举报达到屏蔽的次数,达到这个数直接屏蔽帖子 |
||||
|
*/ |
||||
|
public static final int REPORT_BLOCK_NUM = 3; |
||||
|
|
||||
|
/** |
||||
|
* 过滤 title content 字段的 html 标记,防止 XSS 攻击 |
||||
|
*/ |
||||
|
protected void filter(int filterBy) { |
||||
|
JsoupFilter.filterTitleAndContent(this); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.model; |
||||
|
|
||||
|
import com.bt.common.safe.JsoupFilter; |
||||
|
import com.bt.common.model.base.BaseFeedbackReply; |
||||
|
|
||||
|
/** |
||||
|
* 反馈回复 |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public class FeedbackReply extends BaseFeedbackReply<FeedbackReply> { |
||||
|
|
||||
|
/** |
||||
|
* 举报达到屏蔽的次数,达到这个数直接屏蔽帖子 |
||||
|
*/ |
||||
|
public static final int REPORT_BLOCK_NUM = 3; |
||||
|
|
||||
|
/** |
||||
|
* 过滤 title content 字段的 html 标记,防止 XSS 攻击 |
||||
|
* 将回车换行转换成 <br> 标记便于 html 中显示换行 |
||||
|
*/ |
||||
|
protected void filter(int filterBy) { |
||||
|
String content = getContent().trim().replaceAll("\r\n", "<br>").replaceAll("\n", "<br>").replaceAll("\r", "<br>"); |
||||
|
content = JsoupFilter.filterArticleContent(content); |
||||
|
setContent(content); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,38 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.model; |
||||
|
|
||||
|
import com.bt.common.model.base.BaseMessage; |
||||
|
import com.bt.common.safe.JsoupFilter; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal. |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public class Message extends BaseMessage<Message> { |
||||
|
|
||||
|
public static final int TYPE_NORMAL = 0; // 普通消息
|
||||
|
public static final int TYPE_SYSTEM = 1; // 系统消息
|
||||
|
|
||||
|
/** |
||||
|
* 过滤 title content 字段的 html 标记,防止 XSS 攻击 |
||||
|
* 将回车换行转换成 <br> 标记便于 html 中显示换行 |
||||
|
*/ |
||||
|
protected void filter(int filterBy) { |
||||
|
String content = getContent().trim().replaceAll("\r\n", "<br>").replaceAll("\n", "<br>").replaceAll("\r", "<br>"); |
||||
|
content = JsoupFilter.filterArticleContent(content); |
||||
|
setContent(content); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,65 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.model; |
||||
|
|
||||
|
import com.bt.common.model.base.BaseNewsFeed; |
||||
|
|
||||
|
/** |
||||
|
* NewsFeed |
||||
|
* |
||||
|
* id |
||||
|
* accountId 发布该动态的用户 |
||||
|
* refType 动态引用类型 |
||||
|
* refId 动态引用所关联的 id,与 refType 配合,可唯一确定是某个表中的某条记录 |
||||
|
* refParentType refId 对象的父对象,例如 share_reply 的父对象是 share |
||||
|
* refParentId refId 对象的父对象的 id |
||||
|
* createAt |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public class NewsFeed extends BaseNewsFeed<NewsFeed> { |
||||
|
|
||||
|
public static final int REF_TYPE_PROJECT = 1; // 项目动态
|
||||
|
public static final int REF_TYPE_PROJECT_REPLY = 2; // 项目回复动态,暂时不用
|
||||
|
|
||||
|
public static final int REF_TYPE_SHARE = 3; // 分享动态
|
||||
|
public static final int REF_TYPE_SHARE_REPLY = 4; // 分享回复动态
|
||||
|
|
||||
|
public static final int REF_TYPE_FEEDBACK = 5; // 反馈动态
|
||||
|
public static final int REF_TYPE_FEEDBACK_REPLY = 6; // 反馈回复动态
|
||||
|
|
||||
|
public boolean isRefTypeProject() { |
||||
|
return getRefType() == REF_TYPE_PROJECT; |
||||
|
} |
||||
|
|
||||
|
public boolean isRefTypeProjectReply() { |
||||
|
return getRefType() == REF_TYPE_PROJECT_REPLY; |
||||
|
} |
||||
|
|
||||
|
public boolean isRefTypeShare() { |
||||
|
return getRefType() == REF_TYPE_SHARE; |
||||
|
} |
||||
|
|
||||
|
public boolean isRefTypeShareReply() { |
||||
|
return getRefType() == REF_TYPE_SHARE_REPLY; |
||||
|
} |
||||
|
|
||||
|
public boolean isRefTypeFeedback() { |
||||
|
return getRefType() == REF_TYPE_FEEDBACK; |
||||
|
} |
||||
|
|
||||
|
public boolean isRefTypeFeedbackReply() { |
||||
|
return getRefType() == REF_TYPE_FEEDBACK_REPLY; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
package com.bt.common.model; |
||||
|
|
||||
|
import com.bt.common.model.base.BasePermission; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal. |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public class Permission extends BasePermission<Permission> { |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.model; |
||||
|
|
||||
|
import com.bt.common.safe.JsoupFilter; |
||||
|
import com.bt.common.model.base.BaseProject; |
||||
|
|
||||
|
/** |
||||
|
* 项目 |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public class Project extends BaseProject<Project> { |
||||
|
|
||||
|
/** |
||||
|
* 举报达到屏蔽的次数,达到这个数直接屏蔽帖子 |
||||
|
*/ |
||||
|
public static final int REPORT_BLOCK_NUM = 3; |
||||
|
|
||||
|
/** |
||||
|
* 过滤 title content 字段的 html 标记,防止 XSS 攻击 |
||||
|
*/ |
||||
|
protected void filter(int filterBy) { |
||||
|
JsoupFilter.filterProject(this); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.model; |
||||
|
|
||||
|
import com.bt.common.model.base.BaseReferMe; |
||||
|
|
||||
|
/** |
||||
|
* refer_me 提到我 ReferMe 模型,用于生成与我有关的记录, |
||||
|
* 关联到 news_feed 表 |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public class ReferMe extends BaseReferMe<ReferMe> { |
||||
|
public static final int TYPE_AT_ME = 1; // @我
|
||||
|
public static final int TYPE_COMMENT_ME = 2; // 评论我
|
||||
|
|
||||
|
public static final int TYPE_SHARE_REF_MY_PROJECT = 3; // 分享引用到我的项目
|
||||
|
public static final int TYPE_FEEDBACK_TO_MY_PROJECT = 4; // 反馈到我的项目
|
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.model; |
||||
|
|
||||
|
import com.bt.common.model.base.BaseRemind; |
||||
|
|
||||
|
/** |
||||
|
* 提醒,用于在右上角弹出层显示各类提醒 |
||||
|
* accountId int(11) 主键 |
||||
|
* referMe int(11) @提到我 的消息条数 |
||||
|
* message int(11) 私信 消息条数,暂时不用 |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public class Remind extends BaseRemind<Remind> { |
||||
|
// 提醒类型
|
||||
|
public static final int TYPE_REFER_ME = 0; // 非数据库字段,仅用于在业务层判断所操作的类型
|
||||
|
public static final int TYPE_MESSAGE = 1; // 非数据库字段,仅用于在业务层判断所操作的类型
|
||||
|
public static final int TYPE_FANS = 2; // 非数据库字段,仅用于在业务层判断所操作的类型
|
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
package com.bt.common.model; |
||||
|
|
||||
|
import com.bt.common.model.base.BaseRole; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal. |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public class Role extends BaseRole<Role> { |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,44 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.model; |
||||
|
|
||||
|
import com.bt.common.model.base.BaseSession; |
||||
|
|
||||
|
/** |
||||
|
* session 存放在数据库中,并引入 cache 中间层,优点如下: |
||||
|
* 1:简单且高性能 |
||||
|
* 2:支持分布式与集群 |
||||
|
* 3:支持服务器断电和重启 |
||||
|
* 4:支持 tomcat、jetty 等运行容器重启 |
||||
|
*/ |
||||
|
public class Session extends BaseSession<Session> { |
||||
|
|
||||
|
private static final long serialVersionUID = 1L; |
||||
|
public static final Session dao = new Session().dao(); |
||||
|
|
||||
|
/** |
||||
|
* 登录会话是否已过期 |
||||
|
*/ |
||||
|
public boolean isExpired() { |
||||
|
return getExpireAt() < System.currentTimeMillis(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 登录会话是否未过期 |
||||
|
*/ |
||||
|
public boolean notExpired() { |
||||
|
return ! isExpired(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,37 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.model; |
||||
|
|
||||
|
import com.bt.common.safe.JsoupFilter; |
||||
|
import com.bt.common.model.base.BaseShare; |
||||
|
|
||||
|
/** |
||||
|
* Share |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public class Share extends BaseShare<Share> { |
||||
|
|
||||
|
/** |
||||
|
* 举报达到屏蔽的次数,达到这个数直接屏蔽帖子 |
||||
|
*/ |
||||
|
public static final int REPORT_BLOCK_NUM = 3; |
||||
|
|
||||
|
/** |
||||
|
* 过滤 title content 字段的 html 标记,防止 XSS 攻击 |
||||
|
*/ |
||||
|
protected void filter(int filterBy) { |
||||
|
JsoupFilter.filterTitleAndContent(this); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.model; |
||||
|
|
||||
|
import com.bt.common.safe.JsoupFilter; |
||||
|
import com.bt.common.model.base.BaseShareReply; |
||||
|
|
||||
|
/** |
||||
|
* 分享回复 |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public class ShareReply extends BaseShareReply<ShareReply> { |
||||
|
|
||||
|
/** |
||||
|
* 举报达到屏蔽的次数,达到这个数直接屏蔽帖子 |
||||
|
*/ |
||||
|
public static final int REPORT_BLOCK_NUM = 3; |
||||
|
|
||||
|
/** |
||||
|
* 过滤 title content 字段的 html 标记,防止 XSS 攻击 |
||||
|
* 将回车换行转换成 <br> 标记便于 html 中显示换行 |
||||
|
*/ |
||||
|
protected void filter(int filterBy) { |
||||
|
String content = getContent().trim().replaceAll("\r\n", "<br>").replaceAll("\n", "<br>").replaceAll("\r", "<br>"); |
||||
|
content = JsoupFilter.filterArticleContent(content); |
||||
|
setContent(content); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.model; |
||||
|
|
||||
|
import com.bt.common.model.base.BaseTaskList; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal. |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public class TaskList extends BaseTaskList<TaskList> { |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,98 @@ |
|||||
|
/** |
||||
|
* 请勿将俱乐部专享资源复制给其他人,保护知识产权即是保护我们所在的行业,进而保护我们自己的利益 |
||||
|
* 即便是公司的同事,也请尊重 JFinal 作者的努力与付出,不要复制给同事 |
||||
|
* |
||||
|
* 如果你尚未加入俱乐部,请立即删除该项目,或者现在加入俱乐部:http://jfinal.com/club
|
||||
|
* |
||||
|
* 俱乐部将提供 jfinal-club 项目文档与设计资源、专用 QQ 群,以及作者在俱乐部定期的分享与答疑, |
||||
|
* 价值远比仅仅拥有 jfinal club 项目源代码要大得多 |
||||
|
* |
||||
|
* JFinal 俱乐部是五年以来首次寻求外部资源的尝试,以便于有资源创建更加 |
||||
|
* 高品质的产品与服务,为大家带来更大的价值,所以请大家多多支持,不要将 |
||||
|
* 首次的尝试扼杀在了摇篮之中 |
||||
|
*/ |
||||
|
|
||||
|
package com.bt.common.model; |
||||
|
|
||||
|
import com.jfinal.kit.PathKit; |
||||
|
import com.jfinal.plugin.activerecord.dialect.MysqlDialect; |
||||
|
import com.jfinal.plugin.activerecord.generator.Generator; |
||||
|
import com.jfinal.plugin.druid.DruidPlugin; |
||||
|
import com.bt.common.JFinalClubConfig; |
||||
|
import javax.sql.DataSource; |
||||
|
|
||||
|
/** |
||||
|
* Model、BaseModel、_MappingKit 生成器 |
||||
|
*/ |
||||
|
public class _Generator { |
||||
|
|
||||
|
/** |
||||
|
* 在此统一添加不参与生成的 table。不参与生成的 table 主要有: |
||||
|
* 1:用于建立表之间关系的关联表,如 account_role |
||||
|
* 2:部分功能使用 Db + Record 模式实现,如 project_page_view |
||||
|
*/ |
||||
|
private static String[] excludedTable = { |
||||
|
"news_feed_reply", // 暂不实现该功能
|
||||
|
"project_page_view", "share_page_view", "feedback_page_view", |
||||
|
"login_log", |
||||
|
"sensitive_words", |
||||
|
"upload_counter", |
||||
|
"task_run_log", |
||||
|
"message_tip", |
||||
|
"friend", |
||||
|
"project_like", "share_like", "feedback_like", |
||||
|
"share_reply_like", "feedback_reply_like", |
||||
|
"like_message_log", |
||||
|
"account_role", "role_permission" |
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* 重用 JFinalClubConfig 中的数据源配置,避免冗余配置 |
||||
|
*/ |
||||
|
public static DataSource getDataSource() { |
||||
|
DruidPlugin druidPlugin = JFinalClubConfig.getDruidPlugin(); |
||||
|
druidPlugin.start(); |
||||
|
return druidPlugin.getDataSource(); |
||||
|
} |
||||
|
|
||||
|
public static void main(String[] args) { |
||||
|
// base model 所使用的包名
|
||||
|
String baseModelPackageName = "com.bt.common.model.base"; |
||||
|
// base model 文件保存路径
|
||||
|
String baseModelOutputDir = PathKit.getWebRootPath() |
||||
|
+ "/src/main/java/com/bt/common/model/base"; |
||||
|
|
||||
|
System.out.println("输出路径:"+ baseModelOutputDir); |
||||
|
|
||||
|
// model 所使用的包名 (MappingKit 默认使用的包名)
|
||||
|
String modelPackageName = "com.bt.common.model"; |
||||
|
// model 文件保存路径 (MappingKit 与 DataDictionary 文件默认保存路径)
|
||||
|
String modelOutputDir = baseModelOutputDir + "/.."; |
||||
|
|
||||
|
// 创建生成器
|
||||
|
Generator gen = new Generator(getDataSource(), baseModelPackageName, baseModelOutputDir, modelPackageName, modelOutputDir); |
||||
|
|
||||
|
// 设置数据库方言
|
||||
|
gen.setDialect(new MysqlDialect()); |
||||
|
|
||||
|
// 设置是否生成字段备注
|
||||
|
gen.setGenerateRemarks(true); |
||||
|
|
||||
|
// 添加不需要生成的表名
|
||||
|
for (String table : excludedTable) { |
||||
|
gen.addExcludedTable(table.trim()); |
||||
|
} |
||||
|
|
||||
|
// 设置是否在 Model 中生成 dao 对象
|
||||
|
gen.setGenerateDaoInModel(false); |
||||
|
|
||||
|
// 设置是否生成字典文件
|
||||
|
gen.setGenerateDataDictionary(false); |
||||
|
|
||||
|
// 设置需要被移除的表名前缀用于生成modelName。例如表名 "osc_user",移除前缀 "osc_"后生成的model名为 "User"而非 OscUser
|
||||
|
// gernerator.setRemovedTableNamePrefixes("t_");
|
||||
|
|
||||
|
// 生成
|
||||
|
gen.generate(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,41 @@ |
|||||
|
package com.bt.common.model; |
||||
|
|
||||
|
import com.jfinal.plugin.activerecord.ActiveRecordPlugin; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal, do not modify this file. |
||||
|
* <pre> |
||||
|
* Example: |
||||
|
* public void configPlugin(Plugins me) { |
||||
|
* ActiveRecordPlugin arp = new ActiveRecordPlugin(...); |
||||
|
* _MappingKit.mapping(arp); |
||||
|
* me.add(arp); |
||||
|
* } |
||||
|
* </pre> |
||||
|
*/ |
||||
|
public class _MappingKit { |
||||
|
|
||||
|
public static void mapping(ActiveRecordPlugin arp) { |
||||
|
arp.addMapping("account", "id", Account.class); |
||||
|
arp.addMapping("auth_code", "id", AuthCode.class); |
||||
|
// Composite Primary Key order: mainMenu,subMenu
|
||||
|
arp.addMapping("document", "mainMenu,subMenu", Document.class); |
||||
|
arp.addMapping("download", "id", Download.class); |
||||
|
arp.addMapping("download_log", "id", DownloadLog.class); |
||||
|
arp.addMapping("favorite", "id", Favorite.class); |
||||
|
arp.addMapping("feedback", "id", Feedback.class); |
||||
|
arp.addMapping("feedback_reply", "id", FeedbackReply.class); |
||||
|
arp.addMapping("message", "id", Message.class); |
||||
|
arp.addMapping("news_feed", "id", NewsFeed.class); |
||||
|
arp.addMapping("permission", "id", Permission.class); |
||||
|
arp.addMapping("project", "id", Project.class); |
||||
|
arp.addMapping("refer_me", "id", ReferMe.class); |
||||
|
arp.addMapping("remind", "accountId", Remind.class); |
||||
|
arp.addMapping("role", "id", Role.class); |
||||
|
arp.addMapping("session", "id", Session.class); |
||||
|
arp.addMapping("share", "id", Share.class); |
||||
|
arp.addMapping("share_reply", "id", ShareReply.class); |
||||
|
arp.addMapping("task_list", "id", TaskList.class); |
||||
|
} |
||||
|
} |
||||
|
|
||||
@ -0,0 +1,98 @@ |
|||||
|
package com.bt.common.model.base; |
||||
|
|
||||
|
import com.jfinal.plugin.activerecord.Model; |
||||
|
import com.jfinal.plugin.activerecord.IBean; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal, do not modify this file. |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public abstract class BaseAccount<M extends BaseAccount<M>> extends Model<M> implements IBean { |
||||
|
|
||||
|
public void setId(java.lang.Integer id) { |
||||
|
set("id", id); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getId() { |
||||
|
return getInt("id"); |
||||
|
} |
||||
|
|
||||
|
public void setNickName(java.lang.String nickName) { |
||||
|
set("nickName", nickName); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getNickName() { |
||||
|
return getStr("nickName"); |
||||
|
} |
||||
|
|
||||
|
public void setUserName(java.lang.String userName) { |
||||
|
set("userName", userName); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getUserName() { |
||||
|
return getStr("userName"); |
||||
|
} |
||||
|
|
||||
|
public void setPassword(java.lang.String password) { |
||||
|
set("password", password); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getPassword() { |
||||
|
return getStr("password"); |
||||
|
} |
||||
|
|
||||
|
public void setSalt(java.lang.String salt) { |
||||
|
set("salt", salt); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getSalt() { |
||||
|
return getStr("salt"); |
||||
|
} |
||||
|
|
||||
|
public void setStatus(java.lang.Integer status) { |
||||
|
set("status", status); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getStatus() { |
||||
|
return getInt("status"); |
||||
|
} |
||||
|
|
||||
|
public void setCreateAt(java.time.LocalDateTime createAt) { |
||||
|
set("createAt", createAt); |
||||
|
} |
||||
|
|
||||
|
public java.time.LocalDateTime getCreateAt() { |
||||
|
return getLocalDateTime("createAt"); |
||||
|
} |
||||
|
|
||||
|
public void setIp(java.lang.String ip) { |
||||
|
set("ip", ip); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getIp() { |
||||
|
return getStr("ip"); |
||||
|
} |
||||
|
|
||||
|
public void setAvatar(java.lang.String avatar) { |
||||
|
set("avatar", avatar); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getAvatar() { |
||||
|
return getStr("avatar"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 被赞次数 |
||||
|
*/ |
||||
|
public void setLikeCount(java.lang.Integer likeCount) { |
||||
|
set("likeCount", likeCount); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 被赞次数 |
||||
|
*/ |
||||
|
public java.lang.Integer getLikeCount() { |
||||
|
return getInt("likeCount"); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,44 @@ |
|||||
|
package com.bt.common.model.base; |
||||
|
|
||||
|
import com.jfinal.plugin.activerecord.Model; |
||||
|
import com.jfinal.plugin.activerecord.IBean; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal, do not modify this file. |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public abstract class BaseAuthCode<M extends BaseAuthCode<M>> extends Model<M> implements IBean { |
||||
|
|
||||
|
public void setId(java.lang.String id) { |
||||
|
set("id", id); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getId() { |
||||
|
return getStr("id"); |
||||
|
} |
||||
|
|
||||
|
public void setAccountId(java.lang.Integer accountId) { |
||||
|
set("accountId", accountId); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getAccountId() { |
||||
|
return getInt("accountId"); |
||||
|
} |
||||
|
|
||||
|
public void setExpireAt(java.lang.Long expireAt) { |
||||
|
set("expireAt", expireAt); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Long getExpireAt() { |
||||
|
return getLong("expireAt"); |
||||
|
} |
||||
|
|
||||
|
public void setType(java.lang.Integer type) { |
||||
|
set("type", type); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getType() { |
||||
|
return getInt("type"); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,80 @@ |
|||||
|
package com.bt.common.model.base; |
||||
|
|
||||
|
import com.jfinal.plugin.activerecord.Model; |
||||
|
import com.jfinal.plugin.activerecord.IBean; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal, do not modify this file. |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public abstract class BaseDocument<M extends BaseDocument<M>> extends Model<M> implements IBean { |
||||
|
|
||||
|
/** |
||||
|
* 主菜单 |
||||
|
*/ |
||||
|
public void setMainMenu(java.lang.Integer mainMenu) { |
||||
|
set("mainMenu", mainMenu); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 主菜单 |
||||
|
*/ |
||||
|
public java.lang.Integer getMainMenu() { |
||||
|
return getInt("mainMenu"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 子菜单 |
||||
|
*/ |
||||
|
public void setSubMenu(java.lang.Integer subMenu) { |
||||
|
set("subMenu", subMenu); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 子菜单 |
||||
|
*/ |
||||
|
public java.lang.Integer getSubMenu() { |
||||
|
return getInt("subMenu"); |
||||
|
} |
||||
|
|
||||
|
public void setTitle(java.lang.String title) { |
||||
|
set("title", title); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getTitle() { |
||||
|
return getStr("title"); |
||||
|
} |
||||
|
|
||||
|
public void setContent(java.lang.String content) { |
||||
|
set("content", content); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getContent() { |
||||
|
return getStr("content"); |
||||
|
} |
||||
|
|
||||
|
public void setUpdateAt(java.time.LocalDateTime updateAt) { |
||||
|
set("updateAt", updateAt); |
||||
|
} |
||||
|
|
||||
|
public java.time.LocalDateTime getUpdateAt() { |
||||
|
return getLocalDateTime("updateAt"); |
||||
|
} |
||||
|
|
||||
|
public void setCreateAt(java.time.LocalDateTime createAt) { |
||||
|
set("createAt", createAt); |
||||
|
} |
||||
|
|
||||
|
public java.time.LocalDateTime getCreateAt() { |
||||
|
return getLocalDateTime("createAt"); |
||||
|
} |
||||
|
|
||||
|
public void setPublish(java.lang.Integer publish) { |
||||
|
set("publish", publish); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getPublish() { |
||||
|
return getInt("publish"); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,96 @@ |
|||||
|
package com.bt.common.model.base; |
||||
|
|
||||
|
import com.jfinal.plugin.activerecord.Model; |
||||
|
import com.jfinal.plugin.activerecord.IBean; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal, do not modify this file. |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public abstract class BaseDownload<M extends BaseDownload<M>> extends Model<M> implements IBean { |
||||
|
|
||||
|
public void setId(java.lang.Integer id) { |
||||
|
set("id", id); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getId() { |
||||
|
return getInt("id"); |
||||
|
} |
||||
|
|
||||
|
public void setFileName(java.lang.String fileName) { |
||||
|
set("fileName", fileName); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getFileName() { |
||||
|
return getStr("fileName"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 描述 |
||||
|
*/ |
||||
|
public void setDescr(java.lang.String descr) { |
||||
|
set("descr", descr); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 描述 |
||||
|
*/ |
||||
|
public java.lang.String getDescr() { |
||||
|
return getStr("descr"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 文件类型 |
||||
|
*/ |
||||
|
public void setFileType(java.lang.String fileType) { |
||||
|
set("fileType", fileType); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 文件类型 |
||||
|
*/ |
||||
|
public java.lang.String getFileType() { |
||||
|
return getStr("fileType"); |
||||
|
} |
||||
|
|
||||
|
public void setSize(java.lang.String size) { |
||||
|
set("size", size); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getSize() { |
||||
|
return getStr("size"); |
||||
|
} |
||||
|
|
||||
|
public void setCreateDate(java.util.Date createDate) { |
||||
|
set("createDate", createDate); |
||||
|
} |
||||
|
|
||||
|
public java.util.Date getCreateDate() { |
||||
|
return getDate("createDate"); |
||||
|
} |
||||
|
|
||||
|
public void setPath(java.lang.String path) { |
||||
|
set("path", path); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getPath() { |
||||
|
return getStr("path"); |
||||
|
} |
||||
|
|
||||
|
public void setDownloadCount(java.lang.Integer downloadCount) { |
||||
|
set("downloadCount", downloadCount); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getDownloadCount() { |
||||
|
return getInt("downloadCount"); |
||||
|
} |
||||
|
|
||||
|
public void setIsShow(java.lang.Integer isShow) { |
||||
|
set("isShow", isShow); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getIsShow() { |
||||
|
return getInt("isShow"); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,52 @@ |
|||||
|
package com.bt.common.model.base; |
||||
|
|
||||
|
import com.jfinal.plugin.activerecord.Model; |
||||
|
import com.jfinal.plugin.activerecord.IBean; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal, do not modify this file. |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public abstract class BaseDownloadLog<M extends BaseDownloadLog<M>> extends Model<M> implements IBean { |
||||
|
|
||||
|
public void setId(java.lang.Integer id) { |
||||
|
set("id", id); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getId() { |
||||
|
return getInt("id"); |
||||
|
} |
||||
|
|
||||
|
public void setAccountId(java.lang.Integer accountId) { |
||||
|
set("accountId", accountId); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getAccountId() { |
||||
|
return getInt("accountId"); |
||||
|
} |
||||
|
|
||||
|
public void setIp(java.lang.String ip) { |
||||
|
set("ip", ip); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getIp() { |
||||
|
return getStr("ip"); |
||||
|
} |
||||
|
|
||||
|
public void setFileName(java.lang.String fileName) { |
||||
|
set("fileName", fileName); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getFileName() { |
||||
|
return getStr("fileName"); |
||||
|
} |
||||
|
|
||||
|
public void setDownloadDate(java.util.Date downloadDate) { |
||||
|
set("downloadDate", downloadDate); |
||||
|
} |
||||
|
|
||||
|
public java.util.Date getDownloadDate() { |
||||
|
return getDate("downloadDate"); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,70 @@ |
|||||
|
package com.bt.common.model.base; |
||||
|
|
||||
|
import com.jfinal.plugin.activerecord.Model; |
||||
|
import com.jfinal.plugin.activerecord.IBean; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal, do not modify this file. |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public abstract class BaseFavorite<M extends BaseFavorite<M>> extends Model<M> implements IBean { |
||||
|
|
||||
|
public void setId(java.lang.Integer id) { |
||||
|
set("id", id); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getId() { |
||||
|
return getInt("id"); |
||||
|
} |
||||
|
|
||||
|
public void setAccountId(java.lang.Integer accountId) { |
||||
|
set("accountId", accountId); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getAccountId() { |
||||
|
return getInt("accountId"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 收藏类型:1为项目,2为分享,3为反馈 |
||||
|
*/ |
||||
|
public void setRefType(java.lang.Integer refType) { |
||||
|
set("refType", refType); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 收藏类型:1为项目,2为分享,3为反馈 |
||||
|
*/ |
||||
|
public java.lang.Integer getRefType() { |
||||
|
return getInt("refType"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 被收藏的资源 id |
||||
|
*/ |
||||
|
public void setRefId(java.lang.Integer refId) { |
||||
|
set("refId", refId); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 被收藏的资源 id |
||||
|
*/ |
||||
|
public java.lang.Integer getRefId() { |
||||
|
return getInt("refId"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 收藏时间 |
||||
|
*/ |
||||
|
public void setCreateAt(java.time.LocalDateTime createAt) { |
||||
|
set("createAt", createAt); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 收藏时间 |
||||
|
*/ |
||||
|
public java.time.LocalDateTime getCreateAt() { |
||||
|
return getLocalDateTime("createAt"); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,92 @@ |
|||||
|
package com.bt.common.model.base; |
||||
|
|
||||
|
import com.jfinal.plugin.activerecord.Model; |
||||
|
import com.jfinal.plugin.activerecord.IBean; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal, do not modify this file. |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public abstract class BaseFeedback<M extends BaseFeedback<M>> extends Model<M> implements IBean { |
||||
|
|
||||
|
public void setId(java.lang.Integer id) { |
||||
|
set("id", id); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getId() { |
||||
|
return getInt("id"); |
||||
|
} |
||||
|
|
||||
|
public void setAccountId(java.lang.Integer accountId) { |
||||
|
set("accountId", accountId); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getAccountId() { |
||||
|
return getInt("accountId"); |
||||
|
} |
||||
|
|
||||
|
public void setProjectId(java.lang.Integer projectId) { |
||||
|
set("projectId", projectId); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getProjectId() { |
||||
|
return getInt("projectId"); |
||||
|
} |
||||
|
|
||||
|
public void setTitle(java.lang.String title) { |
||||
|
set("title", title); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getTitle() { |
||||
|
return getStr("title"); |
||||
|
} |
||||
|
|
||||
|
public void setContent(java.lang.String content) { |
||||
|
set("content", content); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getContent() { |
||||
|
return getStr("content"); |
||||
|
} |
||||
|
|
||||
|
public void setCreateAt(java.time.LocalDateTime createAt) { |
||||
|
set("createAt", createAt); |
||||
|
} |
||||
|
|
||||
|
public java.time.LocalDateTime getCreateAt() { |
||||
|
return getLocalDateTime("createAt"); |
||||
|
} |
||||
|
|
||||
|
public void setClickCount(java.lang.Integer clickCount) { |
||||
|
set("clickCount", clickCount); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getClickCount() { |
||||
|
return getInt("clickCount"); |
||||
|
} |
||||
|
|
||||
|
public void setReport(java.lang.Integer report) { |
||||
|
set("report", report); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getReport() { |
||||
|
return getInt("report"); |
||||
|
} |
||||
|
|
||||
|
public void setLikeCount(java.lang.Integer likeCount) { |
||||
|
set("likeCount", likeCount); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getLikeCount() { |
||||
|
return getInt("likeCount"); |
||||
|
} |
||||
|
|
||||
|
public void setFavoriteCount(java.lang.Integer favoriteCount) { |
||||
|
set("favoriteCount", favoriteCount); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getFavoriteCount() { |
||||
|
return getInt("favoriteCount"); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,60 @@ |
|||||
|
package com.bt.common.model.base; |
||||
|
|
||||
|
import com.jfinal.plugin.activerecord.Model; |
||||
|
import com.jfinal.plugin.activerecord.IBean; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal, do not modify this file. |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public abstract class BaseFeedbackReply<M extends BaseFeedbackReply<M>> extends Model<M> implements IBean { |
||||
|
|
||||
|
public void setId(java.lang.Integer id) { |
||||
|
set("id", id); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getId() { |
||||
|
return getInt("id"); |
||||
|
} |
||||
|
|
||||
|
public void setFeedbackId(java.lang.Integer feedbackId) { |
||||
|
set("feedbackId", feedbackId); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getFeedbackId() { |
||||
|
return getInt("feedbackId"); |
||||
|
} |
||||
|
|
||||
|
public void setAccountId(java.lang.Integer accountId) { |
||||
|
set("accountId", accountId); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getAccountId() { |
||||
|
return getInt("accountId"); |
||||
|
} |
||||
|
|
||||
|
public void setContent(java.lang.String content) { |
||||
|
set("content", content); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getContent() { |
||||
|
return getStr("content"); |
||||
|
} |
||||
|
|
||||
|
public void setCreateAt(java.time.LocalDateTime createAt) { |
||||
|
set("createAt", createAt); |
||||
|
} |
||||
|
|
||||
|
public java.time.LocalDateTime getCreateAt() { |
||||
|
return getLocalDateTime("createAt"); |
||||
|
} |
||||
|
|
||||
|
public void setReport(java.lang.Integer report) { |
||||
|
set("report", report); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getReport() { |
||||
|
return getInt("report"); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,112 @@ |
|||||
|
package com.bt.common.model.base; |
||||
|
|
||||
|
import com.jfinal.plugin.activerecord.Model; |
||||
|
import com.jfinal.plugin.activerecord.IBean; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal, do not modify this file. |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public abstract class BaseMessage<M extends BaseMessage<M>> extends Model<M> implements IBean { |
||||
|
|
||||
|
public void setId(java.lang.Integer id) { |
||||
|
set("id", id); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getId() { |
||||
|
return getInt("id"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 消息的主人 |
||||
|
*/ |
||||
|
public void setUser(java.lang.Integer user) { |
||||
|
set("user", user); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 消息的主人 |
||||
|
*/ |
||||
|
public java.lang.Integer getUser() { |
||||
|
return getInt("user"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 对方的ID |
||||
|
*/ |
||||
|
public void setFriend(java.lang.Integer friend) { |
||||
|
set("friend", friend); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 对方的ID |
||||
|
*/ |
||||
|
public java.lang.Integer getFriend() { |
||||
|
return getInt("friend"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 发送者 |
||||
|
*/ |
||||
|
public void setSender(java.lang.Integer sender) { |
||||
|
set("sender", sender); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 发送者 |
||||
|
*/ |
||||
|
public java.lang.Integer getSender() { |
||||
|
return getInt("sender"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 接收者 |
||||
|
*/ |
||||
|
public void setReceiver(java.lang.Integer receiver) { |
||||
|
set("receiver", receiver); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 接收者 |
||||
|
*/ |
||||
|
public java.lang.Integer getReceiver() { |
||||
|
return getInt("receiver"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 0:普通消息,1:系统消息 |
||||
|
*/ |
||||
|
public void setType(java.lang.Integer type) { |
||||
|
set("type", type); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 0:普通消息,1:系统消息 |
||||
|
*/ |
||||
|
public java.lang.Integer getType() { |
||||
|
return getInt("type"); |
||||
|
} |
||||
|
|
||||
|
public void setContent(java.lang.String content) { |
||||
|
set("content", content); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getContent() { |
||||
|
return getStr("content"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 创建时间 |
||||
|
*/ |
||||
|
public void setCreateAt(java.time.LocalDateTime createAt) { |
||||
|
set("createAt", createAt); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 创建时间 |
||||
|
*/ |
||||
|
public java.time.LocalDateTime getCreateAt() { |
||||
|
return getLocalDateTime("createAt"); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,98 @@ |
|||||
|
package com.bt.common.model.base; |
||||
|
|
||||
|
import com.jfinal.plugin.activerecord.Model; |
||||
|
import com.jfinal.plugin.activerecord.IBean; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal, do not modify this file. |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public abstract class BaseNewsFeed<M extends BaseNewsFeed<M>> extends Model<M> implements IBean { |
||||
|
|
||||
|
public void setId(java.lang.Integer id) { |
||||
|
set("id", id); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getId() { |
||||
|
return getInt("id"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 动态创建者 |
||||
|
*/ |
||||
|
public void setAccountId(java.lang.Integer accountId) { |
||||
|
set("accountId", accountId); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 动态创建者 |
||||
|
*/ |
||||
|
public java.lang.Integer getAccountId() { |
||||
|
return getInt("accountId"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 动态引用类型 |
||||
|
*/ |
||||
|
public void setRefType(java.lang.Integer refType) { |
||||
|
set("refType", refType); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 动态引用类型 |
||||
|
*/ |
||||
|
public java.lang.Integer getRefType() { |
||||
|
return getInt("refType"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 动态引用所关联的 id |
||||
|
*/ |
||||
|
public void setRefId(java.lang.Integer refId) { |
||||
|
set("refId", refId); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 动态引用所关联的 id |
||||
|
*/ |
||||
|
public java.lang.Integer getRefId() { |
||||
|
return getInt("refId"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* reply所属的贴子类型, 与type 字段填的值一样 |
||||
|
*/ |
||||
|
public void setRefParentType(java.lang.Integer refParentType) { |
||||
|
set("refParentType", refParentType); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* reply所属的贴子类型, 与type 字段填的值一样 |
||||
|
*/ |
||||
|
public java.lang.Integer getRefParentType() { |
||||
|
return getInt("refParentType"); |
||||
|
} |
||||
|
|
||||
|
public void setRefParentId(java.lang.Integer refParentId) { |
||||
|
set("refParentId", refParentId); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getRefParentId() { |
||||
|
return getInt("refParentId"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 动态创建时间 |
||||
|
*/ |
||||
|
public void setCreateAt(java.time.LocalDateTime createAt) { |
||||
|
set("createAt", createAt); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 动态创建时间 |
||||
|
*/ |
||||
|
public java.time.LocalDateTime getCreateAt() { |
||||
|
return getLocalDateTime("createAt"); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,44 @@ |
|||||
|
package com.bt.common.model.base; |
||||
|
|
||||
|
import com.jfinal.plugin.activerecord.Model; |
||||
|
import com.jfinal.plugin.activerecord.IBean; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal, do not modify this file. |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public abstract class BasePermission<M extends BasePermission<M>> extends Model<M> implements IBean { |
||||
|
|
||||
|
public void setId(java.lang.Integer id) { |
||||
|
set("id", id); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getId() { |
||||
|
return getInt("id"); |
||||
|
} |
||||
|
|
||||
|
public void setActionKey(java.lang.String actionKey) { |
||||
|
set("actionKey", actionKey); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getActionKey() { |
||||
|
return getStr("actionKey"); |
||||
|
} |
||||
|
|
||||
|
public void setController(java.lang.String controller) { |
||||
|
set("controller", controller); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getController() { |
||||
|
return getStr("controller"); |
||||
|
} |
||||
|
|
||||
|
public void setRemark(java.lang.String remark) { |
||||
|
set("remark", remark); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getRemark() { |
||||
|
return getStr("remark"); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,92 @@ |
|||||
|
package com.bt.common.model.base; |
||||
|
|
||||
|
import com.jfinal.plugin.activerecord.Model; |
||||
|
import com.jfinal.plugin.activerecord.IBean; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal, do not modify this file. |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public abstract class BaseProject<M extends BaseProject<M>> extends Model<M> implements IBean { |
||||
|
|
||||
|
public void setId(java.lang.Integer id) { |
||||
|
set("id", id); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getId() { |
||||
|
return getInt("id"); |
||||
|
} |
||||
|
|
||||
|
public void setAccountId(java.lang.Integer accountId) { |
||||
|
set("accountId", accountId); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getAccountId() { |
||||
|
return getInt("accountId"); |
||||
|
} |
||||
|
|
||||
|
public void setName(java.lang.String name) { |
||||
|
set("name", name); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getName() { |
||||
|
return getStr("name"); |
||||
|
} |
||||
|
|
||||
|
public void setTitle(java.lang.String title) { |
||||
|
set("title", title); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getTitle() { |
||||
|
return getStr("title"); |
||||
|
} |
||||
|
|
||||
|
public void setContent(java.lang.String content) { |
||||
|
set("content", content); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getContent() { |
||||
|
return getStr("content"); |
||||
|
} |
||||
|
|
||||
|
public void setCreateAt(java.time.LocalDateTime createAt) { |
||||
|
set("createAt", createAt); |
||||
|
} |
||||
|
|
||||
|
public java.time.LocalDateTime getCreateAt() { |
||||
|
return getLocalDateTime("createAt"); |
||||
|
} |
||||
|
|
||||
|
public void setClickCount(java.lang.Integer clickCount) { |
||||
|
set("clickCount", clickCount); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getClickCount() { |
||||
|
return getInt("clickCount"); |
||||
|
} |
||||
|
|
||||
|
public void setReport(java.lang.Integer report) { |
||||
|
set("report", report); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getReport() { |
||||
|
return getInt("report"); |
||||
|
} |
||||
|
|
||||
|
public void setLikeCount(java.lang.Integer likeCount) { |
||||
|
set("likeCount", likeCount); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getLikeCount() { |
||||
|
return getInt("likeCount"); |
||||
|
} |
||||
|
|
||||
|
public void setFavoriteCount(java.lang.Integer favoriteCount) { |
||||
|
set("favoriteCount", favoriteCount); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getFavoriteCount() { |
||||
|
return getInt("favoriteCount"); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,70 @@ |
|||||
|
package com.bt.common.model.base; |
||||
|
|
||||
|
import com.jfinal.plugin.activerecord.Model; |
||||
|
import com.jfinal.plugin.activerecord.IBean; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal, do not modify this file. |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public abstract class BaseReferMe<M extends BaseReferMe<M>> extends Model<M> implements IBean { |
||||
|
|
||||
|
public void setId(java.lang.Integer id) { |
||||
|
set("id", id); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getId() { |
||||
|
return getInt("id"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 接收者账号id |
||||
|
*/ |
||||
|
public void setReferAccountId(java.lang.Integer referAccountId) { |
||||
|
set("referAccountId", referAccountId); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 接收者账号id |
||||
|
*/ |
||||
|
public java.lang.Integer getReferAccountId() { |
||||
|
return getInt("referAccountId"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* newsFeedId |
||||
|
*/ |
||||
|
public void setNewsFeedId(java.lang.Integer newsFeedId) { |
||||
|
set("newsFeedId", newsFeedId); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* newsFeedId |
||||
|
*/ |
||||
|
public java.lang.Integer getNewsFeedId() { |
||||
|
return getInt("newsFeedId"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @我、评论我等等的refer类型 |
||||
|
*/ |
||||
|
public void setType(java.lang.Integer type) { |
||||
|
set("type", type); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* @我、评论我等等的refer类型 |
||||
|
*/ |
||||
|
public java.lang.Integer getType() { |
||||
|
return getInt("type"); |
||||
|
} |
||||
|
|
||||
|
public void setCreateAt(java.time.LocalDateTime createAt) { |
||||
|
set("createAt", createAt); |
||||
|
} |
||||
|
|
||||
|
public java.time.LocalDateTime getCreateAt() { |
||||
|
return getLocalDateTime("createAt"); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,68 @@ |
|||||
|
package com.bt.common.model.base; |
||||
|
|
||||
|
import com.jfinal.plugin.activerecord.Model; |
||||
|
import com.jfinal.plugin.activerecord.IBean; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal, do not modify this file. |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public abstract class BaseRemind<M extends BaseRemind<M>> extends Model<M> implements IBean { |
||||
|
|
||||
|
/** |
||||
|
* 用户账号id,必须手动指定,不自增 |
||||
|
*/ |
||||
|
public void setAccountId(java.lang.Integer accountId) { |
||||
|
set("accountId", accountId); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 用户账号id,必须手动指定,不自增 |
||||
|
*/ |
||||
|
public java.lang.Integer getAccountId() { |
||||
|
return getInt("accountId"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 提到我的消息条数 |
||||
|
*/ |
||||
|
public void setReferMe(java.lang.Integer referMe) { |
||||
|
set("referMe", referMe); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 提到我的消息条数 |
||||
|
*/ |
||||
|
public java.lang.Integer getReferMe() { |
||||
|
return getInt("referMe"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 私信条数 |
||||
|
*/ |
||||
|
public void setMessage(java.lang.Integer message) { |
||||
|
set("message", message); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 私信条数 |
||||
|
*/ |
||||
|
public java.lang.Integer getMessage() { |
||||
|
return getInt("message"); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 粉丝增加个数 |
||||
|
*/ |
||||
|
public void setFans(java.lang.Integer fans) { |
||||
|
set("fans", fans); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 粉丝增加个数 |
||||
|
*/ |
||||
|
public java.lang.Integer getFans() { |
||||
|
return getInt("fans"); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
package com.bt.common.model.base; |
||||
|
|
||||
|
import com.jfinal.plugin.activerecord.Model; |
||||
|
import com.jfinal.plugin.activerecord.IBean; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal, do not modify this file. |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public abstract class BaseRole<M extends BaseRole<M>> extends Model<M> implements IBean { |
||||
|
|
||||
|
public void setId(java.lang.Integer id) { |
||||
|
set("id", id); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getId() { |
||||
|
return getInt("id"); |
||||
|
} |
||||
|
|
||||
|
public void setName(java.lang.String name) { |
||||
|
set("name", name); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getName() { |
||||
|
return getStr("name"); |
||||
|
} |
||||
|
|
||||
|
public void setCreateAt(java.time.LocalDateTime createAt) { |
||||
|
set("createAt", createAt); |
||||
|
} |
||||
|
|
||||
|
public java.time.LocalDateTime getCreateAt() { |
||||
|
return getLocalDateTime("createAt"); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
package com.bt.common.model.base; |
||||
|
|
||||
|
import com.jfinal.plugin.activerecord.Model; |
||||
|
import com.jfinal.plugin.activerecord.IBean; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal, do not modify this file. |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public abstract class BaseSession<M extends BaseSession<M>> extends Model<M> implements IBean { |
||||
|
|
||||
|
public void setId(java.lang.String id) { |
||||
|
set("id", id); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getId() { |
||||
|
return getStr("id"); |
||||
|
} |
||||
|
|
||||
|
public void setAccountId(java.lang.Integer accountId) { |
||||
|
set("accountId", accountId); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getAccountId() { |
||||
|
return getInt("accountId"); |
||||
|
} |
||||
|
|
||||
|
public void setExpireAt(java.lang.Long expireAt) { |
||||
|
set("expireAt", expireAt); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Long getExpireAt() { |
||||
|
return getLong("expireAt"); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,92 @@ |
|||||
|
package com.bt.common.model.base; |
||||
|
|
||||
|
import com.jfinal.plugin.activerecord.Model; |
||||
|
import com.jfinal.plugin.activerecord.IBean; |
||||
|
|
||||
|
/** |
||||
|
* Generated by JFinal, do not modify this file. |
||||
|
*/ |
||||
|
@SuppressWarnings("serial") |
||||
|
public abstract class BaseShare<M extends BaseShare<M>> extends Model<M> implements IBean { |
||||
|
|
||||
|
public void setId(java.lang.Integer id) { |
||||
|
set("id", id); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getId() { |
||||
|
return getInt("id"); |
||||
|
} |
||||
|
|
||||
|
public void setAccountId(java.lang.Integer accountId) { |
||||
|
set("accountId", accountId); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getAccountId() { |
||||
|
return getInt("accountId"); |
||||
|
} |
||||
|
|
||||
|
public void setProjectId(java.lang.Integer projectId) { |
||||
|
set("projectId", projectId); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getProjectId() { |
||||
|
return getInt("projectId"); |
||||
|
} |
||||
|
|
||||
|
public void setTitle(java.lang.String title) { |
||||
|
set("title", title); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getTitle() { |
||||
|
return getStr("title"); |
||||
|
} |
||||
|
|
||||
|
public void setContent(java.lang.String content) { |
||||
|
set("content", content); |
||||
|
} |
||||
|
|
||||
|
public java.lang.String getContent() { |
||||
|
return getStr("content"); |
||||
|
} |
||||
|
|
||||
|
public void setCreateAt(java.time.LocalDateTime createAt) { |
||||
|
set("createAt", createAt); |
||||
|
} |
||||
|
|
||||
|
public java.time.LocalDateTime getCreateAt() { |
||||
|
return getLocalDateTime("createAt"); |
||||
|
} |
||||
|
|
||||
|
public void setClickCount(java.lang.Integer clickCount) { |
||||
|
set("clickCount", clickCount); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getClickCount() { |
||||
|
return getInt("clickCount"); |
||||
|
} |
||||
|
|
||||
|
public void setReport(java.lang.Integer report) { |
||||
|
set("report", report); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getReport() { |
||||
|
return getInt("report"); |
||||
|
} |
||||
|
|
||||
|
public void setLikeCount(java.lang.Integer likeCount) { |
||||
|
set("likeCount", likeCount); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getLikeCount() { |
||||
|
return getInt("likeCount"); |
||||
|
} |
||||
|
|
||||
|
public void setFavoriteCount(java.lang.Integer favoriteCount) { |
||||
|
set("favoriteCount", favoriteCount); |
||||
|
} |
||||
|
|
||||
|
public java.lang.Integer getFavoriteCount() { |
||||
|
return getInt("favoriteCount"); |
||||
|
} |
||||
|
|
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue