├── WebProject ├── .vscode │ └── settings.json ├── .env.development ├── .env.production ├── src │ ├── config │ │ ├── env.js │ │ └── config.js │ ├── locale.js │ ├── assets │ │ └── logo.png │ ├── plugins │ │ └── element.js │ ├── router.js │ ├── stores │ │ ├── index.js │ │ └── modules │ │ │ └── config.js │ ├── vendors.js │ ├── commons │ │ ├── index.js │ │ ├── Pagination.vue │ │ └── SelectUser.vue │ ├── app.vue │ ├── template │ │ └── index.ejs │ ├── components │ │ ├── tree │ │ │ ├── SimpleTree.vue │ │ │ └── SimpleTreeWithIcon.vue │ │ ├── table │ │ │ ├── ConfigTableSimple.vue │ │ │ ├── ConfigTableSimpleFilter.vue │ │ │ ├── ConfigTable.vue │ │ │ ├── ConfigTable2.vue │ │ │ └── UserTable.vue │ │ ├── ConfigAdd.vue │ │ ├── index.js │ │ ├── LoginDialog.vue │ │ ├── HelloWorld.vue │ │ └── uploadfile │ │ │ ├── UploadFile.vue │ │ │ └── UploadHistory.vue │ ├── libs │ │ ├── param.js │ │ └── util.js │ └── main.js ├── babel.config.js ├── public │ ├── favicon.ico │ └── index.html ├── vue.config.js ├── .gitignore ├── README.md └── package.json ├── docs ├── note │ ├── README.md │ ├── jms.md │ ├── pictures │ │ ├── h2-1.png │ │ ├── h2-2.png │ │ ├── h2-3.png │ │ ├── swagger1.png │ │ ├── swagger2.png │ │ ├── redisclient.png │ │ ├── vue-filter.gif │ │ └── redisconsole.png │ ├── print-all-filters.md │ ├── vue-comps.md │ ├── keng.md │ ├── springboot.md │ ├── enable-swagger2.md │ ├── update-elementui.md │ ├── hibernate.md │ ├── vue-filter.md │ ├── spring-security.md │ ├── use-redis-cache.md │ └── spring-jpa-data-use-h2-database.md ├── main.png ├── vuenote │ └── README.md ├── .vuepress │ └── config.js └── README.md ├── WebProject-webpack-version-backup.zip ├── backup └── 0(20170923)完成vuex,ajax的脚手架和config范例功能.rar ├── JavaSource ├── core │ ├── src │ │ └── main │ │ │ ├── java │ │ │ └── cn │ │ │ │ └── xiaowenjie │ │ │ │ ├── consts │ │ │ │ ├── ObjType.java │ │ │ │ └── LogConst.java │ │ │ │ ├── common │ │ │ │ ├── consts │ │ │ │ │ └── Roles.java │ │ │ │ ├── daos │ │ │ │ │ ├── RoleDao.java │ │ │ │ │ └── UserDao.java │ │ │ │ ├── rbac │ │ │ │ │ ├── Role.java │ │ │ │ │ └── User.java │ │ │ │ ├── annotations │ │ │ │ │ └── Log.java │ │ │ │ ├── beans │ │ │ │ │ ├── PageResp.java │ │ │ │ │ ├── ResultBean.java │ │ │ │ │ └── PageReq.java │ │ │ │ ├── exceptions │ │ │ │ │ ├── CheckException.java │ │ │ │ │ └── UnloginException.java │ │ │ │ ├── utils │ │ │ │ │ ├── StaticFieldInjectionConfiguration.java │ │ │ │ │ ├── CheckUtil.java │ │ │ │ │ ├── SPELUtil.java │ │ │ │ │ └── UserUtil.java │ │ │ │ ├── aop │ │ │ │ │ ├── ValidExceptionHandler.java │ │ │ │ │ ├── LogAOP.java │ │ │ │ │ └── ControllerAOP.java │ │ │ │ └── filters │ │ │ │ │ └── UserFilter.java │ │ │ │ ├── jms │ │ │ │ ├── JMSType.java │ │ │ │ ├── JMSTool.java │ │ │ │ └── JMSMailComsumer.java │ │ │ │ ├── features │ │ │ │ └── Favoritable.java │ │ │ │ ├── caches │ │ │ │ ├── CacheNames.java │ │ │ │ └── ClearCacheTask.java │ │ │ │ ├── beans │ │ │ │ ├── TreeNode.java │ │ │ │ ├── Config.java │ │ │ │ └── BaseEntity.java │ │ │ │ ├── tool │ │ │ │ ├── PasswordUtil.java │ │ │ │ ├── ConfigUtil.java │ │ │ │ ├── MailTool.java │ │ │ │ └── XWJDate.java │ │ │ │ ├── daos │ │ │ │ └── ConfigDao.java │ │ │ │ ├── jpa │ │ │ │ └── JPAListener.java │ │ │ │ ├── controllers │ │ │ │ ├── MailControlller.java │ │ │ │ ├── TreeController.java │ │ │ │ ├── UserController.java │ │ │ │ ├── AppController.java │ │ │ │ ├── ConfigController.java │ │ │ │ └── CacheController.java │ │ │ │ ├── services │ │ │ │ ├── UserService.java │ │ │ │ └── ConfigService.java │ │ │ │ └── CreateTestData.java │ │ │ └── resources │ │ │ ├── i18n │ │ │ ├── messages_en.properties │ │ │ └── messages_zh_CN.properties │ │ │ ├── static │ │ │ ├── index.html │ │ │ └── cache.html │ │ │ ├── application.yml │ │ │ └── logback.xml │ └── pom.xml ├── .idea │ ├── vcs.xml │ ├── JavaSource.iml │ ├── encodings.xml │ ├── compiler.xml │ └── misc.xml ├── chart │ ├── src │ │ └── main │ │ │ └── java │ │ │ └── cn │ │ │ └── xiaowenjie │ │ │ ├── chartbeans │ │ │ ├── LineBean.java │ │ │ ├── Entry.java │ │ │ └── EndData.java │ │ │ ├── daos │ │ │ ├── EndDataDao.java │ │ │ └── UploadRecordDao.java │ │ │ ├── beans │ │ │ └── UploadRecord.java │ │ │ ├── ChartModule.java │ │ │ ├── controllers │ │ │ ├── ChartController.java │ │ │ └── UploadController.java │ │ │ ├── tool │ │ │ ├── Data.java │ │ │ └── ReadLog2CSV.java │ │ │ └── services │ │ │ ├── ChartService.java │ │ │ └── UploadFileService.java │ └── pom.xml ├── main │ ├── src │ │ ├── main │ │ │ └── java │ │ │ │ └── cn │ │ │ │ └── xiaowenjie │ │ │ │ ├── jpa │ │ │ │ ├── JPAThreadLocal.java │ │ │ │ ├── JPAConfig.java │ │ │ │ └── JMSObjectComsumer.java │ │ │ │ ├── springconfigs │ │ │ │ ├── DruidStatFilter.java │ │ │ │ ├── DruidStatViewServlet.java │ │ │ │ ├── JMSConfig.java │ │ │ │ ├── RedisConfig.java │ │ │ │ └── MyAuthorizingRealm.java │ │ │ │ └── SpringbootCodeTemplateApplication.java │ │ └── test │ │ │ └── java │ │ │ └── cn │ │ │ └── xiaowenjie │ │ │ └── SpringbootCodeTemplate │ │ │ ├── MailTest.java │ │ │ └── ConfigTest.java │ └── pom.xml ├── favorite │ ├── src │ │ └── main │ │ │ └── java │ │ │ └── cn │ │ │ └── xiaowenjie │ │ │ ├── FavoriteModule.java │ │ │ ├── beans │ │ │ └── Favorite.java │ │ │ ├── daos │ │ │ └── FavoriteDao.java │ │ │ ├── controllers │ │ │ └── FavoriteController.java │ │ │ └── services │ │ │ └── FavoriteService.java │ └── pom.xml └── blog │ ├── src │ └── main │ │ └── java │ │ └── cn │ │ └── xiaowenjie │ │ ├── daos │ │ └── BlogDao.java │ │ ├── BlogModule.java │ │ ├── beans │ │ └── Blog.java │ │ ├── controllers │ │ └── BlogController.java │ │ └── services │ │ └── BlogService.java │ └── pom.xml ├── README.md ├── package.json └── .gitignore /WebProject/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /WebProject/.env.development: -------------------------------------------------------------------------------- 1 | NODE_ENV=development -------------------------------------------------------------------------------- /WebProject/.env.production: -------------------------------------------------------------------------------- 1 | NODE_ENV=production -------------------------------------------------------------------------------- /docs/note/README.md: -------------------------------------------------------------------------------- 1 | # 开发笔记说明 2 | 3 | 记录开发过程的配置和坑等。 -------------------------------------------------------------------------------- /WebProject/src/config/env.js: -------------------------------------------------------------------------------- 1 | export default process.env.NODE_ENV; -------------------------------------------------------------------------------- /WebProject/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/app' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /docs/main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/ElementVueSpringbootCodeTemplate/HEAD/docs/main.png -------------------------------------------------------------------------------- /WebProject/src/locale.js: -------------------------------------------------------------------------------- 1 | export default { 2 | 'zh-CN': { 3 | 4 | }, 5 | 'en-US': { 6 | 7 | } 8 | }; -------------------------------------------------------------------------------- /docs/vuenote/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/ElementVueSpringbootCodeTemplate/HEAD/docs/vuenote/README.md -------------------------------------------------------------------------------- /docs/note/jms.md: -------------------------------------------------------------------------------- 1 | # JMS使用 2 | 3 | ## 集成JMS发送MAIL组件 4 | 5 | 使用 `jms` 执行 异步任务 ,当然你也可以用 `线程池` 来做异步操作,使用 `jms` 的好处是将来好扩展 6 | -------------------------------------------------------------------------------- /docs/note/pictures/h2-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/ElementVueSpringbootCodeTemplate/HEAD/docs/note/pictures/h2-1.png -------------------------------------------------------------------------------- /docs/note/pictures/h2-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/ElementVueSpringbootCodeTemplate/HEAD/docs/note/pictures/h2-2.png -------------------------------------------------------------------------------- /docs/note/pictures/h2-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/ElementVueSpringbootCodeTemplate/HEAD/docs/note/pictures/h2-3.png -------------------------------------------------------------------------------- /WebProject/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/ElementVueSpringbootCodeTemplate/HEAD/WebProject/public/favicon.ico -------------------------------------------------------------------------------- /WebProject/src/config/config.js: -------------------------------------------------------------------------------- 1 | import Env from './env'; 2 | 3 | let config = { 4 | env: Env 5 | }; 6 | export default config; -------------------------------------------------------------------------------- /WebProject/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/ElementVueSpringbootCodeTemplate/HEAD/WebProject/src/assets/logo.png -------------------------------------------------------------------------------- /docs/note/pictures/swagger1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/ElementVueSpringbootCodeTemplate/HEAD/docs/note/pictures/swagger1.png -------------------------------------------------------------------------------- /docs/note/pictures/swagger2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/ElementVueSpringbootCodeTemplate/HEAD/docs/note/pictures/swagger2.png -------------------------------------------------------------------------------- /docs/note/print-all-filters.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/ElementVueSpringbootCodeTemplate/HEAD/docs/note/print-all-filters.md -------------------------------------------------------------------------------- /docs/note/pictures/redisclient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/ElementVueSpringbootCodeTemplate/HEAD/docs/note/pictures/redisclient.png -------------------------------------------------------------------------------- /docs/note/pictures/vue-filter.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/ElementVueSpringbootCodeTemplate/HEAD/docs/note/pictures/vue-filter.gif -------------------------------------------------------------------------------- /docs/note/vue-comps.md: -------------------------------------------------------------------------------- 1 | # Vue组件 2 | 3 | ## 分页组件 4 | 5 | [知乎:VUE分页组件封装](https://zhuanlan.zhihu.com/p/31638307) , 使用简单,前后台代码都封装好了,相当不错。 6 | 7 | -------------------------------------------------------------------------------- /WebProject-webpack-version-backup.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/ElementVueSpringbootCodeTemplate/HEAD/WebProject-webpack-version-backup.zip -------------------------------------------------------------------------------- /docs/note/pictures/redisconsole.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/ElementVueSpringbootCodeTemplate/HEAD/docs/note/pictures/redisconsole.png -------------------------------------------------------------------------------- /docs/note/keng.md: -------------------------------------------------------------------------------- 1 | # 踩坑记 2 | 3 | ## logback的 property 不能设置为空 4 | 5 | ```xml 6 | 7 | ``` 8 | 9 | 为空无法启动 10 | 11 | -------------------------------------------------------------------------------- /WebProject/src/plugins/element.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Element from 'element-ui' 3 | import 'element-ui/lib/theme-chalk/index.css' 4 | 5 | Vue.use(Element) 6 | -------------------------------------------------------------------------------- /backup/0(20170923)完成vuex,ajax的脚手架和config范例功能.rar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwjie/ElementVueSpringbootCodeTemplate/HEAD/backup/0(20170923)完成vuex,ajax的脚手架和config范例功能.rar -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/consts/ObjType.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.consts; 2 | 3 | /** 4 | * 对象类型 5 | */ 6 | public interface ObjType { 7 | int BLOG = 1; 8 | 9 | } 10 | -------------------------------------------------------------------------------- /WebProject/src/router.js: -------------------------------------------------------------------------------- 1 | const routers = [{ 2 | path: '/', 3 | meta: { 4 | title: '' 5 | }, 6 | component: (resolve) => require(['./views/index.vue'], resolve) 7 | }]; 8 | export default routers; -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/common/consts/Roles.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.common.consts; 2 | 3 | public interface Roles { 4 | 5 | String ADMIN = "admin"; 6 | 7 | String NORMAL_USER = "user"; 8 | } 9 | -------------------------------------------------------------------------------- /JavaSource/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /docs/note/springboot.md: -------------------------------------------------------------------------------- 1 | # springboot笔记 2 | 3 | ## 更新代码自动重启 4 | 增加依赖 5 | ```xml 6 | 7 | org.springframework.boot 8 | spring-boot-devtools 9 | 10 | ``` 11 | 12 | 13 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/jms/JMSType.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.jms; 2 | 3 | public interface JMSType { 4 | 5 | String SEND_MAIL = "send-mail"; 6 | 7 | String CREATE = "create"; 8 | String DELETE = "delete" ; 9 | } 10 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/features/Favoritable.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.features; 2 | 3 | /** 4 | * 可以收藏 5 | */ 6 | public interface Favoritable { 7 | 8 | int getFavoriteCount(); 9 | 10 | void setFavoriteCount(int value); 11 | } 12 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/caches/CacheNames.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.caches; 2 | 3 | /** 4 | * cache 名称常量 5 | * 6 | * @author 肖文杰 https://github.com/xwjie 7 | */ 8 | public interface CacheNames { 9 | 10 | String CONFIG = "config"; 11 | } 12 | -------------------------------------------------------------------------------- /WebProject/src/stores/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | import config from './modules/config'; 4 | 5 | 6 | Vue.use(Vuex); 7 | 8 | const store = new Vuex.Store({ 9 | modules: { 10 | config, 11 | }, 12 | 13 | }); 14 | 15 | export default store; 16 | -------------------------------------------------------------------------------- /WebProject/vue.config.js: -------------------------------------------------------------------------------- 1 | // vue.config.js 2 | module.exports = { 3 | // ... 4 | devServer: { 5 | open: true, //process.platform === 'darwin', 6 | host: '0.0.0.0', 7 | port: 9090, // CHANGE YOUR PORT HERE! 8 | https: false, 9 | hotOnly: false, 10 | }, 11 | // ... 12 | } -------------------------------------------------------------------------------- /WebProject/src/vendors.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import VueRouter from 'vue-router'; 3 | import axios from 'axios'; 4 | import Vuex from 'vuex'; 5 | 6 | //import echarts from 'echarts'; 7 | //import Cookies from 'js-cookie'; 8 | //import html2canvas from 'html2canvas'; 9 | //import rasterizehtml from 'rasterizehtml'; -------------------------------------------------------------------------------- /JavaSource/chart/src/main/java/cn/xiaowenjie/chartbeans/LineBean.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.chartbeans; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * 柱状图数据类 9 | */ 10 | @Data 11 | public class LineBean { 12 | private List columns; 13 | private List rows; 14 | } 15 | -------------------------------------------------------------------------------- /WebProject/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw* 22 | -------------------------------------------------------------------------------- /JavaSource/.idea/JavaSource.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /WebProject/README.md: -------------------------------------------------------------------------------- 1 | # hello-world 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run dev 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | npm run lint 21 | ``` 22 | -------------------------------------------------------------------------------- /WebProject/src/commons/index.js: -------------------------------------------------------------------------------- 1 | import Pagination from './Pagination'; 2 | import SelectUser from './SelectUser'; 3 | 4 | import Vue from 'vue'; 5 | 6 | function registerCommonComponents(){ 7 | Vue.component('Pagination', Pagination); 8 | Vue.component('SelectUser', SelectUser); 9 | } 10 | 11 | export { 12 | registerCommonComponents 13 | } -------------------------------------------------------------------------------- /docs/note/enable-swagger2.md: -------------------------------------------------------------------------------- 1 | # 增加swagger2 2 | 3 | ## 增加依赖 4 | 5 | 在 `pom.xml` 里面增加以下2个依赖。 6 | 7 | ![](./pictures/swagger1.png) 8 | 9 | ## 开启swagger 10 | 11 | 增加 `@EnableSwagger2` 。 12 | 13 | ```java 14 | @EnableSwagger2 15 | public class SpringbootCodeTemplateApplication{ 16 | ... 17 | } 18 | ``` 19 | 20 | 访问 http://localhost:8080/swagger-ui.html ,默认把所有接口都列出来了,效果如图: 21 | 22 | ![](./pictures/swagger2.png) 23 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/resources/i18n/messages_en.properties: -------------------------------------------------------------------------------- 1 | param.is.null=Param is null 2 | field.invalid= Filed Value Invalid 3 | name.is.null=Name is null 4 | value.is.null=Value is null 5 | id.error=Invalid id:{0} 6 | 7 | name.repeat=Name already exist. 8 | 9 | no.permission=No Permission. 10 | 11 | config.not.exist=Config {0} not exist 12 | 13 | unsupport.file.format=Unsupported File Format 14 | password.invalid=Password Invalid -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 晓风轻SpringBoot+Vue代码模板 2 | 3 | 使用Vue+VueX+ElementUI+SpringBoot的代码框架。 4 | [**详细文档请点击这里**](https://xwjie.github.io/ElementVueSpringbootCodeTemplate) 5 | 6 | 相关文章:[程序员你为什么这么累?](https://zhuanlan.zhihu.com/p/28705206) 7 | 8 | ![](docs/main.png) 9 | 10 | # 使用vuepress编译文档 11 | 12 | ``` 13 | // 全局安装vuepress 14 | npm i -g vuepress 15 | 16 | npm i 17 | 18 | // 查看文档,打开7070端口 19 | npm run docs:dev 20 | 21 | // 发布 22 | npm run deploy:build 23 | ``` 24 | 25 | -------------------------------------------------------------------------------- /WebProject/src/app.vue: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /JavaSource/chart/src/main/java/cn/xiaowenjie/daos/EndDataDao.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.daos; 2 | 3 | import cn.xiaowenjie.chartbeans.EndData; 4 | import org.springframework.data.repository.PagingAndSortingRepository; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 用户DAO 10 | * 11 | * @author 肖文杰 https://github.com/xwjie/ 12 | * 13 | */ 14 | public interface EndDataDao extends PagingAndSortingRepository { 15 | 16 | List findAllByRecordId(long uploadRecordId); 17 | } -------------------------------------------------------------------------------- /docs/note/update-elementui.md: -------------------------------------------------------------------------------- 1 | # ElementUI升级2.0.2 2 | 3 | :::tip 4 | 时间:2017.11.02 5 | ::: 6 | 7 | 先升级vue到2.5.2,在升级elementui 8 | 9 | 命令: 10 | 11 | ``` 12 | npm update fsevents 13 | npm update vue 14 | npm update vue-template-compiler 15 | 16 | npm uninstall element-ui 17 | npm install element-ui@2.0.2 -S 18 | ``` 19 | 20 | 修改elementui的css路径: 21 | 22 | ``` 23 | import 'element-ui/lib/theme-default/index.css' 24 | ``` 25 | 26 | 改为 27 | 28 | ``` 29 | import 'element-ui/lib/theme-chalk/index.css' 30 | ``` 31 | 32 | done. 33 | 34 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/beans/TreeNode.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.beans; 2 | 3 | import java.util.List; 4 | 5 | import lombok.Data; 6 | 7 | /** 8 | * 数节点,测试前台控件使用 9 | * 10 | * @author 肖文杰 https://github.com/xwjie/ 11 | */ 12 | @Data 13 | public class TreeNode { 14 | 15 | private long id; 16 | 17 | private String name; 18 | 19 | private String icon = "edit"; 20 | 21 | private List subnodes; 22 | 23 | public TreeNode(long id, String name) { 24 | super(); 25 | this.id = id; 26 | this.name = name; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/common/daos/RoleDao.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.common.daos; 2 | 3 | import cn.xiaowenjie.common.rbac.Role; 4 | import org.springframework.data.domain.Page; 5 | import org.springframework.data.domain.Pageable; 6 | import org.springframework.data.jpa.repository.Query; 7 | import org.springframework.data.repository.PagingAndSortingRepository; 8 | 9 | /** 10 | * 角色 DAO 11 | * 12 | * @author 肖文杰 https://github.com/xwjie/ 13 | * 14 | */ 15 | public interface RoleDao extends PagingAndSortingRepository { 16 | 17 | } -------------------------------------------------------------------------------- /JavaSource/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /JavaSource/main/src/main/java/cn/xiaowenjie/jpa/JPAThreadLocal.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.jpa; 2 | 3 | public class JPAThreadLocal { 4 | static ThreadLocal backgroundThreadLocal =new ThreadLocal(){ 5 | @Override 6 | protected Boolean initialValue() { 7 | return false; 8 | } 9 | }; 10 | 11 | public static void setBackground(boolean value) { 12 | backgroundThreadLocal.set(value); 13 | } 14 | 15 | public static boolean background(){ 16 | return backgroundThreadLocal.get(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/resources/i18n/messages_zh_CN.properties: -------------------------------------------------------------------------------- 1 | param.is.null=\u53C2\u6570\u4E3A\u7A7A 2 | field.invalid=\u5B57\u6BB5\u53D6\u503C\u9519\u8BEF 3 | name.is.null=\u540D\u79F0\u4E3A\u7A7A 4 | value.is.null=\u53D6\u503C\u4E3A\u7A7A 5 | id.error=\u975E\u6CD5\u7684id\uFF1A{0} 6 | 7 | name.repeat=\u540D\u79F0\u5DF2\u7ECF\u5B58\u5728 8 | 9 | no.permission=\u6CA1\u6709\u6743\u9650 10 | 11 | config.not.exist=\u914D\u7F6E\u9879 {0} \u4E0D\u5B58\u5728 12 | 13 | unsupport.file.format=\u4E0D\u652F\u6301\u7684\u6587\u4EF6\u683C\u5F0F 14 | 15 | password.invalid=\u975E\u6CD5\u7684\u5BC6\u7801 -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/jms/JMSTool.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.jms; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.jms.core.JmsTemplate; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | * 发送jms消息 9 | * 10 | * @author 晓风轻 https://github.com/xwjie/ 11 | */ 12 | @Component 13 | public class JMSTool { 14 | 15 | @Autowired 16 | JmsTemplate jmsTemplate; 17 | 18 | public void sendMessage(String destination, Object message) { 19 | jmsTemplate.convertAndSend(destination, message); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /JavaSource/main/src/main/java/cn/xiaowenjie/springconfigs/DruidStatFilter.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.springconfigs; 2 | 3 | import com.alibaba.druid.support.http.WebStatFilter; 4 | 5 | import javax.servlet.annotation.WebFilter; 6 | import javax.servlet.annotation.WebInitParam; 7 | 8 | // druid过滤器. 9 | @WebFilter(filterName = "druidWebStatFilter", urlPatterns = "/*", 10 | initParams = { 11 | // 忽略资源 12 | @WebInitParam(name = "exclusions", value = "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*") 13 | } 14 | ) 15 | public class DruidStatFilter extends WebStatFilter { 16 | } -------------------------------------------------------------------------------- /JavaSource/core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | springbootcodetemplate 7 | cn.xiaowenjie 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | core 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/common/rbac/Role.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.common.rbac; 2 | 3 | import cn.xiaowenjie.beans.BaseEntity; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.NoArgsConstructor; 7 | 8 | import javax.persistence.*; 9 | import java.util.List; 10 | 11 | /** 12 | * 角色 13 | */ 14 | @Entity 15 | @Data 16 | @EqualsAndHashCode(callSuper = true) 17 | @NoArgsConstructor 18 | public class Role extends BaseEntity { 19 | 20 | /** 21 | * 角色名称 22 | */ 23 | private String name; 24 | 25 | /** 26 | * 角色描述 27 | */ 28 | private String comment; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/common/annotations/Log.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.common.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 日志信息注解 10 | */ 11 | @Target(ElementType.METHOD) 12 | @Retention(RetentionPolicy.RUNTIME) 13 | public @interface Log { 14 | // 操作 15 | String action(); 16 | 17 | // 对象类型 18 | String itemType() default ""; 19 | 20 | // 对象标识 21 | String itemId() default ""; 22 | 23 | // 其他参数 24 | String param() default ""; 25 | } 26 | -------------------------------------------------------------------------------- /WebProject/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 晓风轻VUE/ElementUI代码模板 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/tool/PasswordUtil.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.tool; 2 | 3 | import org.apache.shiro.crypto.hash.SimpleHash; 4 | 5 | public class PasswordUtil { 6 | 7 | public static final String ALGORITHM_NAME = "MD5"; 8 | 9 | public static final int HASH_ITERATIONS = 1024; 10 | /** 11 | * 重新计算md5值 12 | * @param password 13 | * @param salt 14 | * @return 15 | */ 16 | public static String renewPassword(String password, String salt){ 17 | SimpleHash md5hash = new SimpleHash( 18 | ALGORITHM_NAME, password, salt, HASH_ITERATIONS); 19 | return md5hash.toHex(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /JavaSource/chart/src/main/java/cn/xiaowenjie/chartbeans/Entry.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.chartbeans; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.LinkedHashMap; 6 | 7 | /** 8 | * 图表数据的一条数据 9 | */ 10 | @Data 11 | public class Entry extends LinkedHashMap { 12 | 13 | /** 14 | * X轴数据 15 | * @param name 16 | * @param value 17 | */ 18 | public void addX(String name, String value){ 19 | this.put(name, value); 20 | } 21 | 22 | /** 23 | * Y轴数据 24 | * @param name 25 | * @param value 26 | */ 27 | public void addY(String name, Number value){ 28 | this.put(name, value); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/consts/LogConst.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.consts; 2 | 3 | /** 4 | * 日志注解里面的常量,自己增加即可 5 | * 6 | * @author 晓风轻 https://github.com/xwjie/PLMCodeTemplate 7 | */ 8 | public interface LogConst { 9 | /** 10 | * 操作 11 | * 为了节省日志文件大小,这些常量可以使用单字母代替 12 | */ 13 | String ACTION_ADD = "add"; 14 | 15 | String ACTION_DELETE = "del"; 16 | 17 | String ACTION_UPDATE = "update"; 18 | 19 | String ACTION_QUERY= "query"; 20 | 21 | /** 22 | * 对象类型 23 | */ 24 | String ITEM_TYPE_BLOG = "blog"; 25 | 26 | String ITEM_TYPE_FAVORITE = "favorite"; 27 | 28 | String ITEM_TYPE_CACHE = "cache"; 29 | 30 | } -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/common/beans/PageResp.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.common.beans; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.data.domain.Page; 6 | 7 | import lombok.Data; 8 | 9 | /** 10 | * 分页响应对象 11 | * 12 | * @author 肖文杰 https://github.com/xwjie/ 13 | */ 14 | @Data 15 | public class PageResp { 16 | private List rows; 17 | 18 | private int page; 19 | 20 | private int pagesize; 21 | 22 | private long total; 23 | 24 | public PageResp(Page page) { 25 | this.rows = page.getContent(); 26 | this.page = page.getNumber() + 1; 27 | this.pagesize = page.getSize(); 28 | this.total = page.getTotalElements(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /JavaSource/chart/src/main/java/cn/xiaowenjie/chartbeans/EndData.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.chartbeans; 2 | 3 | import cn.xiaowenjie.beans.BaseEntity; 4 | import lombok.EqualsAndHashCode; 5 | import org.springframework.data.jpa.domain.support.AuditingEntityListener; 6 | 7 | import javax.persistence.Entity; 8 | import javax.persistence.EntityListeners; 9 | 10 | /** 11 | * 最终记录 12 | */ 13 | @Entity 14 | @lombok.Data 15 | @EqualsAndHashCode(callSuper = true) 16 | @EntityListeners(AuditingEntityListener.class) 17 | public class EndData extends BaseEntity { 18 | /** 19 | * 关联的日志文件id 20 | */ 21 | private long recordId; 22 | 23 | 24 | private String date ; 25 | 26 | private int volume; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/common/exceptions/CheckException.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.common.exceptions; 2 | 3 | public class CheckException extends RuntimeException { 4 | 5 | private static final long serialVersionUID = 1L; 6 | 7 | public CheckException() { 8 | } 9 | 10 | public CheckException(String message) { 11 | super(message); 12 | } 13 | 14 | public CheckException(Throwable cause) { 15 | super(cause); 16 | } 17 | 18 | public CheckException(String message, Throwable cause) { 19 | super(message, cause); 20 | } 21 | 22 | public CheckException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 23 | super(message, cause, enableSuppression, writableStackTrace); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /JavaSource/chart/src/main/java/cn/xiaowenjie/beans/UploadRecord.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.beans; 2 | 3 | import lombok.Data; 4 | import lombok.EqualsAndHashCode; 5 | import org.springframework.data.jpa.domain.support.AuditingEntityListener; 6 | 7 | import javax.persistence.Entity; 8 | import javax.persistence.EntityListeners; 9 | 10 | /** 11 | * 上传记录 12 | */ 13 | @Entity 14 | @Data 15 | @EqualsAndHashCode(callSuper=true) 16 | @EntityListeners(AuditingEntityListener.class) 17 | public class UploadRecord extends BaseEntity { 18 | 19 | private String name; 20 | 21 | private String realPath; 22 | 23 | /** 24 | * 文件大小 25 | */ 26 | private long size; 27 | 28 | /** 29 | * 文件包含的数据量 30 | */ 31 | private int dataCount; 32 | } 33 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/common/daos/UserDao.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.common.daos; 2 | 3 | import org.springframework.data.domain.Page; 4 | import org.springframework.data.domain.Pageable; 5 | import org.springframework.data.jpa.repository.Query; 6 | import org.springframework.data.repository.PagingAndSortingRepository; 7 | 8 | import cn.xiaowenjie.common.rbac.User; 9 | 10 | /** 11 | * 用户DAO 12 | * 13 | * @author 肖文杰 https://github.com/xwjie/ 14 | * 15 | */ 16 | public interface UserDao extends PagingAndSortingRepository { 17 | 18 | User findByName(String username); 19 | 20 | @Query(value = "select t from User t where t.name like %?1%", nativeQuery = false) 21 | Page findAllByKeyword(String keyword, Pageable pageable); 22 | } -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/common/exceptions/UnloginException.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.common.exceptions; 2 | 3 | public class UnloginException extends RuntimeException { 4 | 5 | private static final long serialVersionUID = 1L; 6 | 7 | public UnloginException() { 8 | } 9 | 10 | public UnloginException(String message) { 11 | super(message); 12 | } 13 | 14 | public UnloginException(Throwable cause) { 15 | super(cause); 16 | } 17 | 18 | public UnloginException(String message, Throwable cause) { 19 | super(message, cause); 20 | } 21 | 22 | public UnloginException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 23 | super(message, cause, enableSuppression, writableStackTrace); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /WebProject/src/template/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 晓风轻 Element Vue Springboot 代码模板 6 | 7 | 8 | 9 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/caches/ClearCacheTask.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.caches; 2 | 3 | import org.springframework.cache.annotation.CacheEvict; 4 | import org.springframework.scheduling.annotation.EnableScheduling; 5 | import org.springframework.scheduling.annotation.Scheduled; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * 定时清空cache 10 | * 11 | * @author 肖文杰 https://github.com/xwjie 12 | */ 13 | @Component 14 | @EnableScheduling 15 | public class ClearCacheTask { 16 | 17 | /** 18 | * 定时清空缓存 19 | */ 20 | @Scheduled(fixedRate = 60 * 60 * 1000L) 21 | @CacheEvict(value = { CacheNames.CONFIG }, allEntries = true) 22 | public void clearCaches() { 23 | System.out.println("\n------------ clear caches ------------\n"); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /JavaSource/favorite/src/main/java/cn/xiaowenjie/FavoriteModule.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import javax.annotation.PostConstruct; 6 | 7 | @Component 8 | public class FavoriteModule { 9 | 10 | @PostConstruct 11 | public void init(){ 12 | System.out.println("-----------------------------------------------"); 13 | System.out.println("-- --"); 14 | System.out.println("-- Favorite Module Loaded --"); 15 | System.out.println("-- --"); 16 | System.out.println("-----------------------------------------------"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /JavaSource/blog/src/main/java/cn/xiaowenjie/daos/BlogDao.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.daos; 2 | 3 | import cn.xiaowenjie.beans.Blog; 4 | import cn.xiaowenjie.beans.Config; 5 | import org.springframework.data.domain.Page; 6 | import org.springframework.data.domain.Pageable; 7 | import org.springframework.data.jpa.repository.Query; 8 | import org.springframework.data.repository.PagingAndSortingRepository; 9 | 10 | /** 11 | * Blog类DAO 12 | * 13 | * @author 肖文杰 https://github.com/xwjie/ 14 | * 15 | */ 16 | public interface BlogDao extends PagingAndSortingRepository { 17 | Blog findByTitle(String title); 18 | 19 | @Query(value = "select t from Blog t where t.title like %?1% or t.body like %?1%", nativeQuery = false) 20 | Page findAllByKeyword(String keyword, Pageable pageable); 21 | } -------------------------------------------------------------------------------- /WebProject/src/components/tree/SimpleTree.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/daos/ConfigDao.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.daos; 2 | 3 | import org.springframework.data.domain.Page; 4 | import org.springframework.data.domain.Pageable; 5 | import org.springframework.data.jpa.repository.Query; 6 | import org.springframework.data.repository.PagingAndSortingRepository; 7 | 8 | import cn.xiaowenjie.beans.Config; 9 | 10 | /** 11 | * 配置类DAO 12 | * 13 | * @author 肖文杰 https://github.com/xwjie/ 14 | * 15 | */ 16 | public interface ConfigDao extends PagingAndSortingRepository { 17 | Config findByName(String name); 18 | 19 | @Query(value = "select t from Config t where t.name like %?1% or t.value like %?1% or t.description like %?1%", nativeQuery = false) 20 | Page findAllByKeyword(String keyword, Pageable pageable); 21 | } -------------------------------------------------------------------------------- /JavaSource/blog/src/main/java/cn/xiaowenjie/BlogModule.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import javax.annotation.PostConstruct; 6 | 7 | /** 8 | * 图标和上传模块 9 | */ 10 | @Component 11 | public class BlogModule { 12 | 13 | @PostConstruct 14 | public void init(){ 15 | System.out.println("-----------------------------------------------"); 16 | System.out.println("-- --"); 17 | System.out.println("-- Blog Module Loaded --"); 18 | System.out.println("-- --"); 19 | System.out.println("-----------------------------------------------"); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /JavaSource/chart/src/main/java/cn/xiaowenjie/ChartModule.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import javax.annotation.PostConstruct; 6 | 7 | /** 8 | * 图标和上传模块 9 | */ 10 | @Component 11 | public class ChartModule { 12 | 13 | @PostConstruct 14 | public void init(){ 15 | System.out.println("-----------------------------------------------"); 16 | System.out.println("-- --"); 17 | System.out.println("-- Chart Module Loaded --"); 18 | System.out.println("-- --"); 19 | System.out.println("-----------------------------------------------"); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /JavaSource/chart/src/main/java/cn/xiaowenjie/controllers/ChartController.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.controllers; 2 | 3 | import cn.xiaowenjie.chartbeans.LineBean; 4 | import cn.xiaowenjie.common.beans.ResultBean; 5 | import cn.xiaowenjie.services.ChartService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.*; 8 | 9 | /** 10 | * 上传日志并分析成图表 11 | * 12 | * @author 肖文杰 https://github.com/xwjie/ 13 | * 14 | */ 15 | @RequestMapping("/chart") 16 | @RestController 17 | @CrossOrigin 18 | public class ChartController { 19 | 20 | @Autowired 21 | ChartService chartService; 22 | 23 | @GetMapping("/line") 24 | public ResultBean line(long uploadRecordId){ 25 | return new ResultBean(chartService.line(uploadRecordId)); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /JavaSource/chart/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | springbootcodetemplate 7 | cn.xiaowenjie 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | chart 13 | 14 | 15 | 16 | cn.xiaowenjie 17 | core 18 | 1.0-SNAPSHOT 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /JavaSource/chart/src/main/java/cn/xiaowenjie/tool/Data.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.tool; 2 | 3 | /** 4 | * 日志里面的文件 5 | */ 6 | public class Data { 7 | // 0, 2018-03-29 , 600721 , Buy!, price: 9.54856 , volume: 853 8 | String date; 9 | String code; 10 | boolean buy; 11 | int price; 12 | int volume; 13 | 14 | public static Data fromArray(String[] datas) { 15 | Data d = new Data(); 16 | 17 | d.date = datas[1].trim(); 18 | d.code = datas[2].trim(); 19 | d.buy = datas[3].trim().startsWith("Buy"); 20 | d.price = (int) (Float 21 | .parseFloat(datas[4].trim().substring("price: ".length())) * 100); 22 | d.volume = Integer.parseInt(datas[5].trim().substring("volume: ".length())) 23 | * 100; 24 | 25 | return d; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /JavaSource/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /JavaSource/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 13 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/beans/Config.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.beans; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.EntityListeners; 5 | import javax.persistence.ManyToOne; 6 | 7 | import cn.xiaowenjie.common.rbac.User; 8 | import org.springframework.data.annotation.CreatedBy; 9 | import org.springframework.data.jpa.domain.support.AuditingEntityListener; 10 | 11 | import lombok.Data; 12 | import lombok.EqualsAndHashCode; 13 | 14 | @Entity 15 | @Data 16 | @EqualsAndHashCode(callSuper=true) 17 | @EntityListeners(AuditingEntityListener.class) 18 | public class Config extends BaseEntity { 19 | 20 | private static final long serialVersionUID = 1L; 21 | 22 | private String name, description, value; 23 | 24 | /** 25 | * 创建者 26 | */ 27 | @CreatedBy 28 | @ManyToOne 29 | private User creator; 30 | } 31 | -------------------------------------------------------------------------------- /WebProject/src/stores/modules/config.js: -------------------------------------------------------------------------------- 1 | import util from '../../libs/util'; 2 | 3 | const config = { 4 | namespaced: true, 5 | state: { 6 | configs: [ 7 | { id: 1, 'name': '初始化配置项', value: '晓风轻' } 8 | ] 9 | }, 10 | mutations: { 11 | reload(state, data) { 12 | state.configs = data; 13 | } 14 | }, 15 | actions: { 16 | reload({ state, commit, rootState }) { 17 | util.ajax.get('/config/all').then(result => { 18 | if (result.code == 0) { 19 | //提交数据修改 20 | commit('reload', result.data); 21 | } 22 | else { 23 | //FIXME module中出错如何提示合理? this.error(result.msg); 24 | } 25 | }); 26 | } 27 | } 28 | } 29 | 30 | export default config 31 | -------------------------------------------------------------------------------- /JavaSource/blog/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | springbootcodetemplate 7 | cn.xiaowenjie 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | blog 13 | 14 | 15 | cn.xiaowenjie 16 | core 17 | 1.0-SNAPSHOT 18 | compile 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/common/beans/ResultBean.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.common.beans; 2 | 3 | import java.io.Serializable; 4 | 5 | import lombok.Data; 6 | 7 | @Data 8 | public class ResultBean implements Serializable { 9 | 10 | private static final long serialVersionUID = 1L; 11 | 12 | public static final int NO_LOGIN = -1; 13 | 14 | public static final int SUCCESS = 0; 15 | 16 | public static final int FAIL = 1; 17 | 18 | public static final int NO_PERMISSION = 2; 19 | 20 | private String msg = "success"; 21 | 22 | private int code = SUCCESS; 23 | 24 | private T data; 25 | 26 | public ResultBean() { 27 | super(); 28 | } 29 | 30 | public ResultBean(T data) { 31 | super(); 32 | this.data = data; 33 | } 34 | 35 | public ResultBean(Throwable e) { 36 | super(); 37 | this.msg = e.toString(); 38 | this.code = FAIL; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /JavaSource/favorite/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | springbootcodetemplate 7 | cn.xiaowenjie 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | favorite 13 | 14 | 15 | 16 | 17 | 18 | cn.xiaowenjie 19 | core 20 | 1.0-SNAPSHOT 21 | compile 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/jpa/JPAListener.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.jpa; 2 | 3 | import cn.xiaowenjie.beans.BaseEntity; 4 | import cn.xiaowenjie.jms.JMSTool; 5 | import cn.xiaowenjie.jms.JMSType; 6 | import lombok.Setter; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | 9 | import javax.persistence.PostPersist; 10 | import javax.persistence.PostRemove; 11 | 12 | 13 | public class JPAListener { 14 | 15 | @Setter 16 | static JMSTool jmsTool; 17 | 18 | 19 | @PostPersist 20 | public void createObject(BaseEntity o) { 21 | //System.out.println("create object :"+ o); 22 | jmsTool.sendMessage(JMSType.CREATE, o); 23 | } 24 | 25 | 26 | @PostRemove 27 | public void removeObject(BaseEntity o) { 28 | //System.out.println("create object :"+ o); 29 | jmsTool.sendMessage(JMSType.DELETE, o); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/controllers/MailControlller.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.controllers; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RestController; 7 | 8 | import cn.xiaowenjie.common.beans.ResultBean; 9 | import cn.xiaowenjie.tool.MailTool; 10 | 11 | @RestController 12 | @RequestMapping("/mail") 13 | public class MailControlller { 14 | 15 | @Autowired 16 | MailTool mailTool; 17 | 18 | /** 19 | * JUNIT里面测试由于jms的关系会失败,所以启动应用来测试 20 | */ 21 | @GetMapping("/test") 22 | public ResultBean test() { 23 | String to = "1304471323@qq.com"; 24 | mailTool.send("测试发送标题", "这是正文\n没有html", to); 25 | return new ResultBean("send mail to: " + to); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /JavaSource/favorite/src/main/java/cn/xiaowenjie/beans/Favorite.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.beans; 2 | 3 | import cn.xiaowenjie.common.rbac.User; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | import org.springframework.data.annotation.CreatedBy; 7 | import org.springframework.data.jpa.domain.support.AuditingEntityListener; 8 | 9 | import javax.persistence.*; 10 | import javax.validation.constraints.Min; 11 | 12 | /** 13 | * 收藏类 14 | * @author 晓风轻 15 | * 16 | * (用户id+业务对象类型+业务对象id)唯一索引为了防止并发操作数据错误 17 | */ 18 | @Entity 19 | @Data 20 | @Table( 21 | uniqueConstraints= 22 | @UniqueConstraint(columnNames={"userId", "objType", "objId"}) 23 | ) 24 | @EqualsAndHashCode(callSuper=true) 25 | @EntityListeners(AuditingEntityListener.class) 26 | public class Favorite extends BaseEntity { 27 | 28 | private static final long serialVersionUID = 1L; 29 | 30 | private long userId; 31 | 32 | @Min(1) 33 | private int objType; 34 | 35 | @Min(1) 36 | private long objId; 37 | 38 | } 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "elementvuespringbootcodetemplate", 3 | "version": "1.0.0", 4 | "description": "晓风轻的Vue+ElementUI+Springbooth框架代码", 5 | "main": "index.js", 6 | "directories": { 7 | "doc": "docs" 8 | }, 9 | "scripts": { 10 | "docs:dev": "vuepress dev docs", 11 | "docs:build": "vuepress build docs", 12 | "deploy": "gh-pages -d dist", 13 | "deploy:build": "npm run docs:build && gh-pages -d dist" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/xwjie/ElementVueSpringbootCodeTemplate.git" 18 | }, 19 | "keywords": [ 20 | "晓风轻", 21 | "ElementUI", 22 | "Vue", 23 | "Springboot" 24 | ], 25 | "author": "xwjie", 26 | "license": "ISC", 27 | "bugs": { 28 | "url": "https://github.com/xwjie/ElementVueSpringbootCodeTemplate/issues" 29 | }, 30 | "homepage": "https://github.com/xwjie/ElementVueSpringbootCodeTemplate#readme", 31 | "devDependencies": { 32 | "gh-pages": "^1.1.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /JavaSource/favorite/src/main/java/cn/xiaowenjie/daos/FavoriteDao.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.daos; 2 | 3 | import cn.xiaowenjie.beans.Favorite; 4 | import org.springframework.data.domain.Page; 5 | import org.springframework.data.domain.Pageable; 6 | import org.springframework.data.jpa.repository.Query; 7 | import org.springframework.data.repository.PagingAndSortingRepository; 8 | 9 | import java.util.List; 10 | 11 | 12 | /** 13 | * 配置类DAO 14 | * 15 | * @author 肖文杰 https://github.com/xwjie/ 16 | * 17 | */ 18 | public interface FavoriteDao extends PagingAndSortingRepository { 19 | List findAllByObjType(int type); 20 | 21 | Favorite findByUserIdAndObjTypeAndObjId(long userId, int objType, long objId); 22 | 23 | int countByObjTypeAndObjId(int objType, long objId); 24 | 25 | // @Query(value = "select t from Config t where t.name like %?1% or t.value like %?1% or t.description like %?1%", nativeQuery = false) 26 | // Page findAllByKeyword(String keyword, Pageable pageable); 27 | } -------------------------------------------------------------------------------- /JavaSource/blog/src/main/java/cn/xiaowenjie/beans/Blog.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.beans; 2 | 3 | import cn.xiaowenjie.common.rbac.User; 4 | import cn.xiaowenjie.features.Favoritable; 5 | import lombok.EqualsAndHashCode; 6 | import org.springframework.data.annotation.CreatedBy; 7 | import org.springframework.data.jpa.domain.support.AuditingEntityListener; 8 | 9 | import javax.persistence.*; 10 | import javax.validation.constraints.Size; 11 | 12 | @Entity 13 | @lombok.Data 14 | @EqualsAndHashCode(callSuper = true) 15 | @EntityListeners(AuditingEntityListener.class) 16 | public class Blog extends BaseEntity implements Favoritable { 17 | 18 | @Size(min = 3, max = 30) // , message = "{javax.validation.constraints.Size.message}" 19 | private String title; 20 | 21 | @Size(min = 10, max = 30000) 22 | @Lob 23 | private String body; 24 | 25 | /** 26 | * 收藏数 27 | */ 28 | int favoriteCount; 29 | 30 | /** 31 | * 创建者 32 | */ 33 | @CreatedBy 34 | @ManyToOne 35 | private User creator; 36 | 37 | } 38 | -------------------------------------------------------------------------------- /JavaSource/main/src/main/java/cn/xiaowenjie/springconfigs/DruidStatViewServlet.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.springconfigs; 2 | 3 | import com.alibaba.druid.support.http.StatViewServlet; 4 | 5 | import javax.servlet.annotation.WebInitParam; 6 | import javax.servlet.annotation.WebServlet; 7 | 8 | //druid数据源状态监控. 9 | @WebServlet(urlPatterns = "/druid/*", 10 | initParams = { 11 | // IP白名单 (没有配置或者为空,则允许所有访问) 12 | @WebInitParam(name = "allow", value = "192.168.1.72,127.0.0.1"), 13 | // IP黑名单 (存在共同时,deny优先于allow) 14 | @WebInitParam(name = "deny", value = "192.168.1.73"), 15 | // 用户名 16 | @WebInitParam(name = "loginUsername", value = "admin"), 17 | // 密码 18 | @WebInitParam(name = "loginPassword", value = "admin"), 19 | // 禁用HTML页面上的“Reset All”功能 20 | @WebInitParam(name = "resetEnable", value = "false") 21 | } 22 | ) 23 | public class DruidStatViewServlet extends StatViewServlet { 24 | 25 | } -------------------------------------------------------------------------------- /JavaSource/chart/src/main/java/cn/xiaowenjie/daos/UploadRecordDao.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.daos; 2 | 3 | import cn.xiaowenjie.beans.Config; 4 | import cn.xiaowenjie.beans.UploadRecord; 5 | import org.springframework.data.domain.Page; 6 | import org.springframework.data.domain.Pageable; 7 | import org.springframework.data.jpa.repository.Query; 8 | import org.springframework.data.repository.PagingAndSortingRepository; 9 | 10 | /** 11 | * 上传记录DAO 12 | * 13 | * @author 肖文杰 https://github.com/xwjie/ 14 | * 15 | */ 16 | public interface UploadRecordDao extends PagingAndSortingRepository { 17 | 18 | @Query(value = "select t from UploadRecord t where t.name like %?1% order by t.createTime desc", nativeQuery = false) 19 | Page findAllByKeyword(String keyword, Pageable pageable); 20 | 21 | /** 22 | * 分页查找所有记录,按创建时间倒序 23 | * @param pageable 24 | * @return 25 | */ 26 | Page findAllByOrderByCreateTimeDesc(Pageable pageable); 27 | 28 | UploadRecord findByName(String name); 29 | } -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/beans/BaseEntity.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.beans; 2 | 3 | import java.io.Serializable; 4 | import java.util.Date; 5 | 6 | import javax.persistence.EntityListeners; 7 | import javax.persistence.GeneratedValue; 8 | import javax.persistence.Id; 9 | import javax.persistence.MappedSuperclass; 10 | 11 | import cn.xiaowenjie.jpa.JPAListener; 12 | import org.hibernate.annotations.CreationTimestamp; 13 | import org.hibernate.annotations.UpdateTimestamp; 14 | 15 | import lombok.Data; 16 | import lombok.EqualsAndHashCode; 17 | 18 | /** 19 | * 基类 20 | * 21 | * @author 肖文杰 https://github.com/xwjie/ 22 | */ 23 | @Data 24 | @MappedSuperclass 25 | @EqualsAndHashCode(of = "id") 26 | @EntityListeners(value = JPAListener.class) 27 | public abstract class BaseEntity implements Serializable { 28 | 29 | private static final long serialVersionUID = 1L; 30 | 31 | @Id 32 | @GeneratedValue 33 | private long id; 34 | 35 | @CreationTimestamp 36 | private Date createTime; 37 | 38 | @UpdateTimestamp 39 | private Date updateTime; 40 | 41 | } 42 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/tool/ConfigUtil.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.tool; 2 | 3 | import cn.xiaowenjie.beans.Config; 4 | import cn.xiaowenjie.daos.ConfigDao; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | 7 | import static cn.xiaowenjie.common.utils.CheckUtil.check; 8 | 9 | public class ConfigUtil { 10 | 11 | private static ConfigDao configDao; 12 | 13 | public static void setConfigDao(ConfigDao configDao) { 14 | ConfigUtil.configDao = configDao; 15 | } 16 | 17 | public static String get(String name){ 18 | Config config = configDao.findByName(name); 19 | 20 | check(config != null, "config.not.exist", name); 21 | 22 | return config.getValue(); 23 | } 24 | 25 | public static String get(String name, String defaultValue){ 26 | Config config = configDao.findByName(name); 27 | return config != null ? config.getValue() : defaultValue; 28 | } 29 | 30 | public static String getInt(String name){ 31 | throw new UnsupportedOperationException("等你实现"); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /docs/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | title: '晓风轻SpringBoot+Vue代码模板', 3 | description: '晓风轻SpringBoot+Vue代码模板', 4 | dest: './dist', // 设置输出目录 5 | base: '/ElementVueSpringbootCodeTemplate/', // 设置站点根路径 6 | port: 7070, 7 | themeConfig: { 8 | repo: 'https://github.com/xwjie/ElementVueSpringbootCodeTemplate' , 9 | nav: [ 10 | { text: '首页', link: '/' }, 11 | { text: '开发笔记', link: '/note/' }, 12 | ], 13 | sidebar: { 14 | '/note/': [{ 15 | title: '开发笔记', 16 | collapsable: false, 17 | children: [ 18 | '', 19 | 'use-redis-cache', 20 | 'update-elementui', 21 | 'spring-jpa-data-use-h2-database', 22 | 'vue-filter', 23 | 'enable-swagger2', 24 | 'vue-comps', 25 | 'spring-security', 26 | 'hibernate', 27 | 'jms', 28 | 'springboot', 29 | 'keng', 30 | ] 31 | }], 32 | 33 | } 34 | }, //themeConfig 35 | configureWebpack: { 36 | resolve: { 37 | alias: { 38 | '@img': 'images' 39 | } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /JavaSource/main/src/main/java/cn/xiaowenjie/jpa/JPAConfig.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.jpa; 2 | 3 | import cn.xiaowenjie.common.rbac.User; 4 | import cn.xiaowenjie.common.utils.UserUtil; 5 | import cn.xiaowenjie.jpa.JPAThreadLocal; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.data.domain.AuditorAware; 9 | import org.springframework.data.jpa.repository.config.EnableJpaAuditing; 10 | 11 | @Configuration 12 | @EnableJpaAuditing 13 | // 不指定bean也可以 @EnableJpaAuditing(auditorAwareRef = "auditorAware") 14 | public class JPAConfig { 15 | @Bean 16 | public AuditorAware auditorAware() { 17 | return new AuditorAware() { 18 | 19 | @Override 20 | public User getCurrentAuditor() { 21 | //System.out.println("\n\nJPAConfig.auditorAware().new AuditorAware() {...}.getCurrentAuditor()"); 22 | 23 | // 后台任务,不需要登录 24 | // TODO 后台创建的生活,可能就会为空 25 | if (JPAThreadLocal.background()){ 26 | return null; 27 | } 28 | else { 29 | return UserUtil.getUser(); 30 | } 31 | } 32 | }; 33 | } 34 | } -------------------------------------------------------------------------------- /WebProject/src/components/table/ConfigTableSimple.vue: -------------------------------------------------------------------------------- 1 | /** 2 | * 表格组件范例 3 | * 4 | * @author xiaowenjie https://github.com/xwjie 5 | */ 6 | 43 | 44 | -------------------------------------------------------------------------------- /docs/note/hibernate.md: -------------------------------------------------------------------------------- 1 | # hibernate 笔记 2 | 3 | ## 自动生成时间 4 | 5 | 增加基类,把id和创建修改时间放到基类里面,并把类修改为 `abstract` 。 6 | 7 | 需要使用 `@MappedSuperclass` 注解。 8 | 9 | 使用 `@CreationTimestamp` 和 `@UpdateTimestamp` 自动生成对应的时间。 10 | 11 | ```java 12 | package cn.xiaowenjie.beans; 13 | 14 | import java.io.Serializable; 15 | import java.util.Date; 16 | 17 | import javax.persistence.GeneratedValue; 18 | import javax.persistence.Id; 19 | import javax.persistence.MappedSuperclass; 20 | 21 | import org.hibernate.annotations.CreationTimestamp; 22 | import org.hibernate.annotations.UpdateTimestamp; 23 | 24 | import lombok.Data; 25 | 26 | /** 27 | * 基类 28 | * 29 | * @author 晓风轻 https://github.com/xwjie/ 30 | */ 31 | @Data 32 | @MappedSuperclass 33 | public abstract class BaseEntity implements Serializable { 34 | 35 | @Id 36 | @GeneratedValue 37 | private long id; 38 | 39 | @CreationTimestamp 40 | private Date createTime; 41 | 42 | @UpdateTimestamp 43 | private Date updateTime; 44 | 45 | } 46 | 47 | ``` 48 | 49 | ## 增加唯一主键 50 | 51 | User表: 52 | 53 | ```java 54 | @Table(indexes = { @Index(name = "user_name_unique", columnList = "name", unique = true) }) 55 | ``` -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /dist 2 | target 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | *.pid.lock 16 | 17 | # Directory for instrumented libs generated by jscoverage/JSCover 18 | lib-cov 19 | 20 | # Coverage directory used by tools like istanbul 21 | coverage 22 | 23 | # nyc test coverage 24 | .nyc_output 25 | 26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # Bower dependency directory (https://bower.io/) 30 | bower_components 31 | 32 | # node-waf configuration 33 | .lock-wscript 34 | 35 | # Compiled binary addons (http://nodejs.org/api/addons.html) 36 | build/Release 37 | 38 | # Dependency directories 39 | node_modules/ 40 | jspm_packages/ 41 | 42 | # Typescript v1 declaration files 43 | typings/ 44 | 45 | # Optional npm cache directory 46 | .npm 47 | 48 | # Optional eslint cache 49 | .eslintcache 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | # Output of 'npm pack' 55 | *.tgz 56 | 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | 63 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 晓风轻的代码模板 6 | 8 | 9 | 10 |

晓风轻Vue+Element+SpringBoot代码模板

11 | 20 |

用户

21 | 管理员:xwjie,普通用户:user1~user10,密码默认都是 123456 22 |

工程使用方法

23 |
    24 |
  • 使用了redis做cache,需启动redis,默认端口。
  • 25 |
  • 在VSCode运行“npm run dev”(或者直接运行)打开web工程WebProject测试。
  • 26 |
  • 后台默认端口:8080, 前台默认端口:9090
  • 27 |
28 |

辅助界面

29 | 43 | 44 | -------------------------------------------------------------------------------- /JavaSource/main/src/main/java/cn/xiaowenjie/SpringbootCodeTemplateApplication.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.web.servlet.ServletComponentScan; 6 | import org.springframework.context.MessageSource; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.support.ResourceBundleMessageSource; 9 | import springfox.documentation.swagger2.annotations.EnableSwagger2; 10 | 11 | @SpringBootApplication 12 | @ServletComponentScan 13 | @EnableSwagger2 14 | public class SpringbootCodeTemplateApplication { 15 | 16 | public static void main(String[] args) { 17 | SpringApplication.run(SpringbootCodeTemplateApplication.class, args); 18 | } 19 | 20 | /** 21 | * 国际化配置 22 | * @return 23 | */ 24 | @Bean 25 | public MessageSource messageSource() { 26 | ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); 27 | messageSource.setBasenames("i18n/messages"); // , "i18n/errormsg" 28 | messageSource.setDefaultEncoding("UTF-8"); 29 | return messageSource; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /JavaSource/main/src/test/java/cn/xiaowenjie/SpringbootCodeTemplate/MailTest.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.SpringbootCodeTemplate; 2 | 3 | import cn.xiaowenjie.SpringbootCodeTemplateApplication; 4 | import org.junit.FixMethodOrder; 5 | import org.junit.Test; 6 | import org.junit.runner.RunWith; 7 | import org.junit.runners.MethodSorters; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.boot.test.context.SpringBootTest; 10 | import org.springframework.test.context.junit4.SpringRunner; 11 | 12 | import cn.xiaowenjie.tool.MailTool; 13 | 14 | /** 15 | * 发送邮件测试(异步) 16 | * 17 | * @author 晓风轻 18 | * 19 | */ 20 | @RunWith(SpringRunner.class) 21 | @SpringBootTest(classes = SpringbootCodeTemplateApplication.class) 22 | @FixMethodOrder(MethodSorters.NAME_ASCENDING) 23 | public class MailTest { 24 | 25 | @Autowired 26 | MailTool mailTool; 27 | 28 | @Test 29 | public void test1_sendmai() { 30 | mailTool.send("测试发送标题", "这是正文\n没有html", "1304471323@qq.com"); 31 | } 32 | 33 | //FIXME @Test 34 | public void test2_sendhtmlmai() { 35 | mailTool.sendHtml("测试发送标题html", 36 | "

这是正文


html", 37 | "1304471323@qq.com"); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /JavaSource/chart/src/main/java/cn/xiaowenjie/controllers/UploadController.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.controllers; 2 | 3 | import cn.xiaowenjie.beans.UploadRecord; 4 | import cn.xiaowenjie.common.beans.PageReq; 5 | import cn.xiaowenjie.common.beans.PageResp; 6 | import cn.xiaowenjie.common.beans.ResultBean; 7 | import cn.xiaowenjie.services.UploadFileService; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.web.bind.annotation.*; 10 | import org.springframework.web.multipart.MultipartFile; 11 | 12 | /** 13 | * 上传日志并分析成图表 14 | * 15 | * 16 | * 17 | * @author 肖文杰 https://github.com/xwjie/ 18 | * 19 | */ 20 | @RequestMapping("/upload") 21 | @RestController 22 | @CrossOrigin 23 | public class UploadController { 24 | 25 | @Autowired 26 | UploadFileService uploadFileService; 27 | 28 | @PostMapping("/") 29 | public ResultBean upload(@RequestBody() MultipartFile file){ 30 | return new ResultBean(uploadFileService.upload(file)); 31 | } 32 | 33 | @GetMapping(value = "/list") 34 | public ResultBean> list(PageReq param) { 35 | return new ResultBean<>(uploadFileService.listPage(param.toPageable(), param.getKeyword())); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # 介绍 2 | 3 | 使用Vue+VueX+ElementUI+SpringBoot的代码框架。相关文章讲解:[程序员你为什么这么累?](https://zhuanlan.zhihu.com/p/28705206) 4 | 5 | 6 | ![](./main.png) 7 | 8 | ## 特色 9 | 10 | * 前后台一站式代码模板 11 | 12 | ## 技术点 13 | 14 | ### 后台 15 | * SpringBoot 16 | * Spring Data JPA 17 | * Spring Cache 18 | * JMS 19 | 20 | ### 前台 21 | * VUE + VUEX + ElementUI 22 | 23 | ## 使用方法 24 | 25 | ### 数据库 26 | 使用 `h2` 内置数据库演示 `spring data jpa` 功能。无需任何配置。 27 | 28 | 使用了redis做cache,需启动redis,默认端口。 29 | 30 | ### 测试数据 31 | 32 | `CreateTestData` 类会自动执行,创建测试数据。 33 | 34 | ### 后台 35 | 36 | 导入Maven工程目录`JavaSource`到IDE,使用了[lombok](https://projectlombok.org/),需要在IDE里面先安装插件。 37 | 38 | 直接启动 `SpringbootCodeTemplateApplication` 即可。默认8080端口 39 | 40 | ### 前台 41 | 42 | 使用 `VSCode` 打开 `WebProject` , 运行 `npm run dev` ,会自动打开 [http://localhost:9090/](http://localhost:9090/) 。 43 | 44 | 45 | 如需要登陆,默认的内置账号密码为 `xwjie/xwjie` 。 46 | 47 | ## 辅助操作界面 48 | 49 | * swagger [http://localhost:8080/swagger-ui.html](http://localhost:8080/swagger-ui.html) 50 | 51 | * H2维护界面 [http://localhost:8080/h2-console/](http://localhost:8080/h2-console/) 52 | 53 | 1. JDBC URL: jdbc:h2:~/mydb.h2 54 | 2. 用户名:sa 55 | 3. 密码:sa 56 | 57 | * 自己编写的Cache界面 [http://localhost:8080/cache.html](http://localhost:8080/cache.html) 58 | -------------------------------------------------------------------------------- /WebProject/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xiaofengqing-vue-template", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "build": "vue-cli-service build", 7 | "lint": "vue-cli-service lint", 8 | "dev": "vue-cli-service serve" 9 | }, 10 | "dependencies": { 11 | "axios": "^0.18.0", 12 | "echarts": "^4.1.0", 13 | "element-ui": "^2.4.6", 14 | "v-charts": "^1.17.10", 15 | "vue": "^2.5.17", 16 | "vue-bus": "^1.1.0", 17 | "vue-i18n": "^8.0.0", 18 | "vue-router": "^3.0.1", 19 | "vuex": "^3.0.1" 20 | }, 21 | "devDependencies": { 22 | "@vue/cli-plugin-babel": "^3.0.1", 23 | "@vue/cli-plugin-eslint": "^3.0.1", 24 | "@vue/cli-service": "^3.0.1", 25 | "vue-cli-plugin-element": "^1.0.0", 26 | "vue-template-compiler": "^2.5.17" 27 | }, 28 | "eslintConfig": { 29 | "root": true, 30 | "env": { 31 | "node": true 32 | }, 33 | "extends": [ 34 | "plugin:vue/essential", 35 | "eslint:recommended" 36 | ], 37 | "rules": {}, 38 | "parserOptions": { 39 | "parser": "babel-eslint" 40 | } 41 | }, 42 | "postcss": { 43 | "plugins": { 44 | "autoprefixer": {} 45 | } 46 | }, 47 | "browserslist": [ 48 | "> 1%", 49 | "last 2 versions", 50 | "not ie <= 8" 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /WebProject/src/components/ConfigAdd.vue: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/common/utils/StaticFieldInjectionConfiguration.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.common.utils; 2 | 3 | import javax.annotation.PostConstruct; 4 | 5 | import cn.xiaowenjie.daos.ConfigDao; 6 | import cn.xiaowenjie.jms.JMSTool; 7 | import cn.xiaowenjie.jpa.JPAListener; 8 | import cn.xiaowenjie.tool.ConfigUtil; 9 | import lombok.Setter; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.beans.factory.annotation.Qualifier; 12 | import org.springframework.context.MessageSource; 13 | import org.springframework.context.annotation.Configuration; 14 | 15 | import cn.xiaowenjie.common.filters.UserFilter; 16 | import cn.xiaowenjie.services.UserService; 17 | 18 | /** 19 | * 工具类的注入 20 | * 21 | * @author 肖文杰 22 | */ 23 | @Configuration 24 | public class StaticFieldInjectionConfiguration { 25 | 26 | @Autowired 27 | MessageSource resources; 28 | 29 | @Autowired 30 | ConfigDao configDao; 31 | 32 | @Autowired 33 | JMSTool jmsTool; 34 | 35 | @PostConstruct 36 | private void init() { 37 | System.out.println("\n\n-----StaticFieldInjectionConfiguration----\n\n"); 38 | 39 | CheckUtil.setResources(resources); 40 | ConfigUtil.setConfigDao(configDao); 41 | 42 | JPAListener.setJmsTool(jmsTool); 43 | } 44 | } -------------------------------------------------------------------------------- /WebProject/src/components/table/ConfigTableSimpleFilter.vue: -------------------------------------------------------------------------------- 1 | /** 2 | * 表格组件范例 3 | * 4 | * @author xiaowenjie https://github.com/xwjie 5 | */ 6 | 49 | 50 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/common/rbac/User.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.common.rbac; 2 | 3 | import javax.persistence.*; 4 | 5 | import cn.xiaowenjie.beans.BaseEntity; 6 | import cn.xiaowenjie.common.consts.Roles; 7 | import com.fasterxml.jackson.annotation.JsonIgnore; 8 | import lombok.Data; 9 | import lombok.EqualsAndHashCode; 10 | import lombok.NoArgsConstructor; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | * 用户信息 16 | * 17 | * @author 肖文杰 https://github.com/xwjie/ 18 | */ 19 | @Entity 20 | @Data 21 | @EqualsAndHashCode(callSuper = true) 22 | @NoArgsConstructor 23 | @Table(indexes = { @Index(name = "user_name_unique", columnList = "name", unique = true) }) 24 | public class User extends BaseEntity { 25 | 26 | private static final long serialVersionUID = 1L; 27 | 28 | private String name; 29 | 30 | private String nick; 31 | 32 | /** 33 | * 加密的盐 34 | */ 35 | @JsonIgnore 36 | private String salt; 37 | 38 | /** 39 | * 密码 40 | * FIXME 注意任何时候不能返回到前台,必要时候安全相关字段放另外一个表 41 | */ 42 | @JsonIgnore 43 | private String password; 44 | 45 | /** 46 | * 角色 47 | */ 48 | @ManyToMany(fetch = FetchType.EAGER ) 49 | @JoinTable(name="link_user_role", 50 | joinColumns={ @JoinColumn(name="user_id",referencedColumnName="id")}, 51 | inverseJoinColumns={@JoinColumn(name="role_id",referencedColumnName="id")}) 52 | private List roles; 53 | 54 | } 55 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/common/utils/CheckUtil.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.common.utils; 2 | 3 | import org.springframework.context.MessageSource; 4 | 5 | import cn.xiaowenjie.common.exceptions.CheckException; 6 | 7 | /** 8 | * 校验工具类 9 | * 10 | * @author 肖文杰 11 | * 12 | */ 13 | public class CheckUtil { 14 | private static MessageSource resources; 15 | 16 | public static void setResources(MessageSource resources) { 17 | CheckUtil.resources = resources; 18 | } 19 | 20 | public static void check(boolean condition, String msgKey, Object... args) { 21 | if (!condition) { 22 | fail(msgKey, args); 23 | } 24 | } 25 | 26 | public static void check(boolean condition) { 27 | if (!condition) { 28 | fail("field.invalid"); 29 | } 30 | } 31 | 32 | public static void notEmpty(String str, String msgKey, Object... args) { 33 | if (str == null || str.isEmpty()) { 34 | fail(msgKey, args); 35 | } 36 | } 37 | 38 | public static void notNull(Object obj, String msgKey, Object... args) { 39 | if (obj == null) { 40 | fail(msgKey, args); 41 | } 42 | } 43 | 44 | public static void notNull(Object obj) { 45 | if (obj == null) { 46 | notNull(obj, "param.is.null"); 47 | } 48 | } 49 | 50 | private static void fail(String msgKey, Object... args) { 51 | throw new CheckException(resources.getMessage(msgKey, args, UserUtil.getLocale())); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /docs/note/vue-filter.md: -------------------------------------------------------------------------------- 1 | # 增加vue过滤器 2 | 3 | ## 定义filter 4 | 5 | [官方文档](https://cn.vuejs.org/v2/guide/filters.html) 。 这里定义的是全局的,也可以定义局部的。 6 | 7 | ```js 8 | // 定义全局filter 9 | Vue.filter('filterKeyword', function (value, key) { 10 | if (!key) return value; 11 | return value.filter(e => Util.isMatch(e, key)); 12 | }); 13 | ``` 14 | 15 | 判断对象是否包含关键字 16 | 17 | ```js 18 | /** 19 | * 对象是否包含指定关键字 key 20 | */ 21 | util.isMatch = function(e, key){ 22 | var props = ['name', 'value', 'description']; 23 | 24 | for(var i in props){ 25 | var field = e[props[i]]; 26 | 27 | if(field && field.indexOf(key) > -1){ 28 | return true; 29 | } 30 | } 31 | 32 | return false; 33 | } 34 | ``` 35 | 36 | ## 使用 37 | 38 | 给要过滤的组件传入 `keyword` 39 | 40 | ```js 41 | 45 | 46 | 47 | 48 | 49 | ``` 50 | 51 | 组件 `ConfigTable` 中增加 `keyword` props。 52 | 53 | ```js 54 | export default { 55 | props: ["keyword"], 56 | ... 57 | } 58 | ``` 59 | 60 | `ConfigTable` 中需要过滤的控件`el-table`上增加 `filterKeyword(keyword)` 。 61 | 62 | ```js 63 | 68 | ... 69 | ``` 70 | 71 | ## 效果图 72 | 73 | ![](./pictures/vue-filter.gif) -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/controllers/TreeController.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.controllers; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | import org.springframework.web.bind.annotation.CrossOrigin; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | import cn.xiaowenjie.beans.TreeNode; 12 | import cn.xiaowenjie.common.beans.ResultBean; 13 | 14 | @RestController 15 | @CrossOrigin 16 | @RequestMapping("/tree") 17 | public class TreeController { 18 | 19 | @GetMapping("/simple") 20 | public ResultBean> simple() { 21 | 22 | List nodes = createSimpleNodes(); 23 | 24 | return new ResultBean<>(nodes); 25 | } 26 | 27 | private List createSimpleNodes() { 28 | List nodes = createNodes("第1层节点"); 29 | 30 | for (int i = 0; i < nodes.size(); i++) { 31 | TreeNode n = nodes.get(i); 32 | 33 | if (i > 2) { 34 | n.setSubnodes(createNodes("第2层节点")); 35 | } 36 | } 37 | return nodes; 38 | } 39 | 40 | private List createNodes(String name) { 41 | List nodes2 = new ArrayList<>(); 42 | for (int j = 0; j < 5; j++) { 43 | TreeNode n2 = new TreeNode(j, name + j); 44 | nodes2.add(n2); 45 | 46 | } 47 | return nodes2; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /WebProject/src/components/index.js: -------------------------------------------------------------------------------- 1 | import ConfigAdd from './ConfigAdd.vue'; 2 | import ConfigTable from './table/ConfigTable'; 3 | import ConfigTable2 from './table/ConfigTable2'; 4 | 5 | import LoginDialog from './LoginDialog'; 6 | import ConfigTableSimple from './table/ConfigTableSimple'; 7 | import ConfigTableSimpleFilter from './table/ConfigTableSimpleFilter'; 8 | 9 | //tree 10 | import SimpleTree from './tree/SimpleTree'; 11 | import SimpleTreeWithIcon from './tree/SimpleTreeWithIcon'; 12 | 13 | // uploadfile 14 | import UploadFile from './uploadfile/UploadFile'; 15 | import UploadHistory from './uploadfile/UploadHistory'; 16 | 17 | // 18 | import UserTable from './table/UserTable'; 19 | 20 | import Vue from 'vue'; 21 | 22 | function registerComponents(){ 23 | 24 | Vue.component('ConfigAdd', ConfigAdd); 25 | 26 | Vue.component('ConfigTable', ConfigTable); 27 | Vue.component('ConfigTable2', ConfigTable2); 28 | Vue.component('LoginDialog', LoginDialog); 29 | Vue.component('ConfigTableSimple', ConfigTableSimple); 30 | Vue.component('ConfigTableSimpleFilter', ConfigTableSimpleFilter); 31 | 32 | //tree 33 | Vue.component('SimpleTree', SimpleTree); 34 | Vue.component('SimpleTreeWithIcon', SimpleTreeWithIcon); 35 | 36 | // upload file 37 | Vue.component('UploadFile', UploadFile); 38 | Vue.component('UploadHistory', UploadHistory); 39 | 40 | // User 41 | Vue.component('UserTable', UserTable); 42 | 43 | } 44 | 45 | export { 46 | registerComponents 47 | } -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/jms/JMSMailComsumer.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.jms; 2 | 3 | import javax.jms.Message; 4 | import javax.jms.Session; 5 | import javax.mail.internet.MimeMessage; 6 | 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.jms.annotation.JmsListener; 9 | import org.springframework.mail.SimpleMailMessage; 10 | import org.springframework.mail.javamail.JavaMailSender; 11 | import org.springframework.messaging.MessageHeaders; 12 | import org.springframework.messaging.handler.annotation.Headers; 13 | import org.springframework.messaging.handler.annotation.Payload; 14 | import org.springframework.stereotype.Component; 15 | 16 | import lombok.extern.slf4j.Slf4j; 17 | 18 | @Component 19 | @Slf4j 20 | public class JMSMailComsumer { 21 | 22 | @Autowired 23 | private JavaMailSender mailSender; 24 | 25 | @JmsListener(destination = JMSType.SEND_MAIL) 26 | public void sendMail(@Payload Object obj, @Headers MessageHeaders headers, Message message, Session session) { 27 | log.info("recived mail: {}", obj); 28 | 29 | System.out.println("-------------------------"); 30 | System.out.println("obj:" + obj); 31 | System.out.println("headers:" + headers); 32 | System.out.println("message:" + message); 33 | System.out.println("session:" + session); 34 | System.out.println("-------------------------"); 35 | 36 | if (obj instanceof MimeMessage) { 37 | mailSender.send((MimeMessage) obj); 38 | } else { 39 | mailSender.send((SimpleMailMessage) obj); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /JavaSource/chart/src/main/java/cn/xiaowenjie/services/ChartService.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.services; 2 | 3 | import cn.xiaowenjie.chartbeans.EndData; 4 | import cn.xiaowenjie.chartbeans.Entry; 5 | import cn.xiaowenjie.chartbeans.LineBean; 6 | import cn.xiaowenjie.daos.EndDataDao; 7 | import com.google.common.collect.Lists; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Service; 11 | 12 | import java.util.List; 13 | import java.util.stream.Collectors; 14 | 15 | /** 16 | * 生成图表 17 | */ 18 | @Service 19 | @Slf4j 20 | public class ChartService { 21 | 22 | 23 | @Autowired 24 | EndDataDao endDataDao; 25 | 26 | 27 | public LineBean line(long uploadRecordId) { 28 | LineBean bean = new LineBean(); 29 | 30 | // 31 | bean.setColumns(Lists.newArrayList("日期", "盈亏")); 32 | 33 | // 得到记录 34 | List endDatas = endDataDao.findAllByRecordId(uploadRecordId); 35 | 36 | log.info("uploadRecordId: " + uploadRecordId +", data size: " + endDatas.size() ); 37 | 38 | // 转换为图表对象 39 | List rows = endDatas.stream().map(ChartService::toChartEntry).collect(Collectors.toList()); 40 | 41 | bean.setRows(rows); 42 | 43 | return bean; 44 | } 45 | 46 | private static Entry toChartEntry(EndData data) { 47 | Entry e = new Entry(); 48 | 49 | e.addX("日期", data.getDate()); 50 | e.addY("盈亏", data.getVolume()); 51 | 52 | return e; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /JavaSource/favorite/src/main/java/cn/xiaowenjie/controllers/FavoriteController.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.controllers; 2 | 3 | import cn.xiaowenjie.beans.Config; 4 | import cn.xiaowenjie.beans.Favorite; 5 | import cn.xiaowenjie.common.beans.PageReq; 6 | import cn.xiaowenjie.common.beans.PageResp; 7 | import cn.xiaowenjie.common.beans.ResultBean; 8 | import cn.xiaowenjie.services.ConfigService; 9 | import cn.xiaowenjie.services.FavoriteService; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.web.bind.annotation.*; 12 | 13 | import javax.validation.Valid; 14 | import java.util.Collection; 15 | 16 | /** 17 | * 收藏相关的controller,支持跨域 18 | * 19 | * @author 肖文杰 https://github.com/xwjie/ 20 | * 21 | */ 22 | @RequestMapping("/favorite") 23 | @RestController 24 | @CrossOrigin 25 | public class FavoriteController { 26 | 27 | @Autowired 28 | FavoriteService favoriteService; 29 | 30 | @GetMapping("/all") 31 | public ResultBean> getAll(@RequestParam int type) { 32 | return new ResultBean>(favoriteService.getAll(type)); 33 | } 34 | 35 | /** 36 | * 新增配置 37 | * 38 | * FIXME 同时支持json格式和表单格式 39 | * 40 | * @param favorite 41 | * @return 42 | */ 43 | @PostMapping("/add") 44 | public ResultBean add(@RequestBody @Valid Favorite favorite) { 45 | return new ResultBean(favoriteService.add(favorite)); 46 | } 47 | 48 | @PostMapping("/delete") 49 | public ResultBean delete(@RequestParam long id) { 50 | return new ResultBean(favoriteService.delete(id)); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /JavaSource/chart/src/main/java/cn/xiaowenjie/tool/ReadLog2CSV.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.tool; 2 | 3 | import cn.xiaowenjie.chartbeans.EndData; 4 | 5 | import java.io.*; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.stream.Collectors; 9 | 10 | public class ReadLog2CSV { 11 | 12 | public static List readFile2List(InputStream fileInputStream, 13 | String charset) throws IOException { 14 | List sb = new ArrayList(); 15 | 16 | BufferedReader br = null; 17 | 18 | try { 19 | br = new BufferedReader(new InputStreamReader(fileInputStream, charset)); 20 | 21 | String s = null; 22 | 23 | while ((s = br.readLine()) != null) { 24 | sb.add(s); 25 | } 26 | } finally { 27 | if (br != null) { 28 | 29 | br.close(); 30 | } 31 | } 32 | 33 | return sb; 34 | } 35 | 36 | public static List deal(File file) throws IOException { 37 | List list = readFile2List(new FileInputStream(file), "Cp1252"); 38 | 39 | return list.stream().map(s -> s.split(",")).filter(arr -> arr.length == 2) 40 | .map(ReadLog2CSV::toData).collect(Collectors.toList()); 41 | } 42 | 43 | public static EndData toData(String[] arr) { 44 | EndData data = new EndData(); 45 | 46 | data.setDate(arr[0].trim()); 47 | data.setVolume(Integer.parseInt(arr[1].trim())); 48 | 49 | return data; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /WebProject/src/components/tree/SimpleTreeWithIcon.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/controllers/UserController.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.controllers; 2 | 3 | import java.util.Arrays; 4 | import java.util.List; 5 | 6 | import cn.xiaowenjie.common.beans.PageReq; 7 | import cn.xiaowenjie.common.beans.PageResp; 8 | import cn.xiaowenjie.services.UserService; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.web.bind.annotation.*; 11 | 12 | import cn.xiaowenjie.common.rbac.User; 13 | import cn.xiaowenjie.common.beans.ResultBean; 14 | import cn.xiaowenjie.common.consts.Roles; 15 | 16 | @RestController 17 | @CrossOrigin 18 | @RequestMapping("/user") 19 | public class UserController { 20 | 21 | @Autowired 22 | UserService userService; 23 | 24 | /** 25 | * 测试数据 26 | * 27 | * @param keyword 28 | * @return 29 | */ 30 | @GetMapping("/search") 31 | public ResultBean> search(@RequestParam String keyword) { 32 | System.out.println("UserController.search()" + keyword); 33 | 34 | List nodes = Arrays.asList(); 35 | 36 | return new ResultBean<>(nodes); 37 | } 38 | 39 | @GetMapping("/list") 40 | public ResultBean> list(PageReq page){ 41 | return new ResultBean<>(userService.list(page.toPageable(), page.getKeyword())); 42 | } 43 | 44 | /** 45 | * 修改密码 46 | * @param id 47 | * @param password 48 | * @return 49 | */ 50 | @PostMapping("updatepwd") 51 | public ResultBean updatePwd(long id, String password){ 52 | System.out.println(userService.getClass());//FIXME DELETE 53 | userService.updatePwd(id, password.trim()); 54 | return new ResultBean<>(true); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/common/beans/PageReq.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.common.beans; 2 | 3 | import com.alibaba.fastjson.annotation.JSONType; 4 | import org.springframework.data.domain.PageRequest; 5 | import org.springframework.data.domain.Pageable; 6 | import org.springframework.data.domain.Sort.Direction; 7 | import org.springframework.util.StringUtils; 8 | 9 | import lombok.Data; 10 | 11 | /** 12 | * 分页请求参数 13 | * 14 | * @author 肖文杰 https://github.com/xwjie/ 15 | * 16 | */ 17 | @Data 18 | @JSONType(ignores = "pageable") // 不加fastjson toJson的时候 报 StackOverflowError 19 | public class PageReq { 20 | 21 | private int page = 1; 22 | 23 | private int pagesize = 10; 24 | 25 | private String sortfield = ""; 26 | 27 | private String sort = ""; 28 | 29 | private String keyword = ""; 30 | 31 | public PageReq() { 32 | super(); 33 | } 34 | 35 | public PageReq(int page, int pagesize, String sortfield, String sort, 36 | String keyword) { 37 | super(); 38 | this.page = page; 39 | this.pagesize = pagesize; 40 | this.sortfield = sortfield; 41 | this.sort = sort; 42 | this.keyword = keyword; 43 | } 44 | 45 | public PageReq getPageable() { 46 | return new PageReq(page, pagesize, sortfield, sort, keyword); 47 | } 48 | 49 | public Pageable toPageable() { 50 | // pageable里面是从第0页开始的。 51 | Pageable pageable = null; 52 | 53 | if (StringUtils.isEmpty(sortfield)) { 54 | pageable = new PageRequest(page - 1, pagesize); 55 | } 56 | else { 57 | pageable = new PageRequest(page - 1, pagesize, 58 | sort.toLowerCase().startsWith("desc") ? Direction.DESC 59 | : Direction.ASC, 60 | sortfield); 61 | } 62 | 63 | return pageable; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/tool/MailTool.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.tool; 2 | 3 | import cn.xiaowenjie.jms.JMSTool; 4 | import cn.xiaowenjie.jms.JMSType; 5 | import lombok.SneakyThrows; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.mail.SimpleMailMessage; 9 | import org.springframework.mail.javamail.JavaMailSender; 10 | import org.springframework.mail.javamail.MimeMessageHelper; 11 | import org.springframework.stereotype.Component; 12 | 13 | import javax.mail.internet.MimeMessage; 14 | 15 | @Component 16 | public class MailTool { 17 | 18 | @Autowired 19 | JMSTool jmsTool; 20 | 21 | @Value("${spring.mail.from}") 22 | private String from; 23 | 24 | @Autowired 25 | private JavaMailSender mailSender; 26 | 27 | public void send(String subjct, String text, String... to) { 28 | SimpleMailMessage message = new SimpleMailMessage(); 29 | 30 | message.setFrom(from); 31 | message.setTo(to); 32 | message.setSubject(subjct); 33 | message.setText(text); 34 | 35 | jmsTool.sendMessage(JMSType.SEND_MAIL, message); 36 | } 37 | 38 | @SneakyThrows 39 | public void sendHtml(String subjct, String text, String... to) { 40 | MimeMessage mimeMessage = mailSender.createMimeMessage(); 41 | 42 | MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true); 43 | 44 | messageHelper.setFrom(from); 45 | messageHelper.setTo(to); 46 | messageHelper.setSubject(subjct); 47 | 48 | // html格式,第二个参数为true 49 | messageHelper.setText(text, true); 50 | 51 | // 附件 52 | // messageHelper.addAttachment 53 | 54 | jmsTool.sendMessage(JMSType.SEND_MAIL, mimeMessage); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /WebProject/src/components/LoginDialog.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | profiles: 3 | active: dev 4 | redis: 5 | host: localhost 6 | port: 6379 7 | datasource: 8 | url: jdbc:h2:file:~/mydb.h2 9 | username: sa 10 | password: sa 11 | driverClassName: org.h2.Driver 12 | type: com.alibaba.druid.pool.DruidDataSource 13 | # 下面为连接池的补充设置,应用到上面所有数据源中 14 | # 初始化大小,最小,最大 15 | initialSize: 1 16 | minIdle: 3 17 | maxActive: 20 18 | # 配置获取连接等待超时的时间 19 | maxWait: 60000 20 | # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 21 | timeBetweenEvictionRunsMillis: 60000 22 | # 配置一个连接在池中最小生存的时间,单位是毫秒 23 | minEvictableIdleTimeMillis: 30000 24 | validationQuery: select 'x' 25 | testWhileIdle: true 26 | testOnBorrow: false 27 | testOnReturn: false 28 | # 打开PSCache,并且指定每个连接上PSCache的大小 29 | poolPreparedStatements: true 30 | maxPoolPreparedStatementPerConnectionSize: 20 31 | # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 32 | filters: stat,wall,slf4j 33 | # 通过connectProperties属性来打开mergeSql功能;慢SQL记录 34 | connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 35 | # 合并多个DruidDataSource的监控数据 36 | #useGlobalDataSourceStat: true 37 | jpa: 38 | database: h2 39 | show-sql: true 40 | hibernate: 41 | ddl-auto: update 42 | h2: 43 | console: 44 | enabled: true 45 | path: /h2-console 46 | settings: 47 | web-allow-others: true 48 | trace: true 49 | mail: 50 | password: xxxxxx 51 | host: smtp.163.com 52 | username: xxxxxx 53 | from: xxxxxx@163.com 54 | properties: 55 | mail: 56 | smtp: 57 | auth: true 58 | starttls: 59 | enable: true 60 | required: true 61 | server: 62 | port: 8080 -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/common/utils/SPELUtil.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.common.utils; 2 | 3 | import org.aspectj.lang.ProceedingJoinPoint; 4 | import org.aspectj.lang.reflect.MethodSignature; 5 | import org.springframework.expression.spel.standard.SpelExpressionParser; 6 | import org.springframework.expression.spel.support.StandardEvaluationContext; 7 | 8 | /** 9 | * spring 表达式 AOP 处理工具类 10 | * 11 | * @author 晓风轻 https://github.com/xwjie/PLMCodeTemplate 12 | */ 13 | public class SPELUtil { 14 | 15 | 16 | private final SpelExpressionParser parser; 17 | private final StandardEvaluationContext context; 18 | 19 | public SPELUtil(ProceedingJoinPoint pjp) { 20 | this.parser = new SpelExpressionParser(); 21 | this.context = new StandardEvaluationContext(); 22 | 23 | extractArgments(pjp); 24 | } 25 | 26 | /** 27 | * 得到参数名称和值 放到 spel 上下文 28 | * @param pjp 29 | */ 30 | private void extractArgments(ProceedingJoinPoint pjp) { 31 | MethodSignature methodSignature = (MethodSignature) pjp.getSignature(); 32 | 33 | String[] names = methodSignature.getParameterNames(); 34 | Object[] args = pjp.getArgs(); 35 | 36 | for (int i = 0; i < names.length; i++) { 37 | this.context.setVariable(names[i], args[i]); 38 | } 39 | } 40 | 41 | /** 42 | * 计算表达式 43 | * 44 | * @param expr 45 | * @return 46 | */ 47 | public Object cacl(String expr) { 48 | if (expr == null || expr.trim().isEmpty() ) { 49 | return null; 50 | } 51 | 52 | // Expression expression = this.parser.parseExpression(greetingExp, 53 | // new TemplateParserContext()); 54 | // System.out.println(expression.getValue(context, String.class)); 55 | 56 | return this.parser.parseRaw(expr).getValue(this.context); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/controllers/AppController.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.controllers; 2 | 3 | import javax.servlet.http.HttpSession; 4 | 5 | import org.apache.shiro.SecurityUtils; 6 | import org.apache.shiro.subject.Subject; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.web.bind.annotation.*; 11 | 12 | import cn.xiaowenjie.common.rbac.User; 13 | import cn.xiaowenjie.common.beans.ResultBean; 14 | import cn.xiaowenjie.common.utils.UserUtil; 15 | import cn.xiaowenjie.services.UserService; 16 | 17 | /** 18 | * app相关的controller,支持跨域 19 | * 20 | * @author 肖文杰 https://github.com/xwjie/ 21 | */ 22 | @RequestMapping("/app") 23 | @RestController 24 | @CrossOrigin 25 | public class AppController { 26 | private static final Logger logger = LoggerFactory.getLogger(AppController.class); 27 | 28 | @Autowired 29 | UserService userService; 30 | 31 | @PostMapping(value = "/login") 32 | public ResultBean login(HttpSession session, @RequestParam String username, @RequestParam String 33 | password) { 34 | logger.info("login user:" + username); 35 | 36 | User user = userService.login(username, password); 37 | 38 | //FIXME 39 | session.setAttribute(UserUtil.KEY_USER, user); 40 | 41 | return new ResultBean(user); 42 | } 43 | 44 | @PostMapping(value = "/user") 45 | public ResultBean login(HttpSession session) { 46 | logger.info("get current user"); 47 | return new ResultBean(UserUtil.getUserIfLogin()); 48 | } 49 | 50 | @PostMapping(value = "/logout") 51 | public ResultBean logout(HttpSession session) { 52 | //session.invalidate(); 53 | userService.logout(); 54 | 55 | return new ResultBean(true); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /WebProject/src/libs/param.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 把json对象序列化成表单格式(如:name=xiao&password=xwjie) 3 | * 4 | * https://github.com/xwjie/ElementVueSpringbootCodeTemplate 5 | * 6 | * @param {*} obj 7 | */ 8 | export default function (obj) { 9 | var s = [], rbracket = /\[\]$/, 10 | isArray = function (obj) { 11 | return Object.prototype.toString.call(obj) === '[object Array]'; 12 | }, add = function (k, v) { 13 | v = typeof v === 'function' ? v() : v === null ? '' : v === undefined ? '' : v; 14 | s[s.length] = encodeURIComponent(k) + '=' + encodeURIComponent(v); 15 | }, buildParams = function (prefix, obj) { 16 | var i, len, key; 17 | 18 | if (prefix) { 19 | if (isArray(obj)) { 20 | for (i = 0, len = obj.length; i < len; i++) { 21 | if (rbracket.test(prefix)) { 22 | add(prefix, obj[i]); 23 | } else { 24 | buildParams(prefix + '[' + (typeof obj[i] === 'object' ? i : '') + ']', obj[i]); 25 | } 26 | } 27 | } else if (obj && String(obj) === '[object Object]') { 28 | for (key in obj) { 29 | buildParams(prefix + '[' + key + ']', obj[key]); 30 | } 31 | } else { 32 | add(prefix, obj); 33 | } 34 | } else if (isArray(obj)) { 35 | for (i = 0, len = obj.length; i < len; i++) { 36 | add(obj[i].name, obj[i].value); 37 | } 38 | } else { 39 | for (key in obj) { 40 | buildParams(key, obj[key]); 41 | } 42 | } 43 | return s; 44 | }; 45 | 46 | return buildParams('', obj).join('&').replace(/%20/g, '+'); 47 | } 48 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/common/aop/ValidExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.common.aop; 2 | 3 | import cn.xiaowenjie.common.beans.ResultBean; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.stereotype.Controller; 6 | import org.springframework.util.StringUtils; 7 | import org.springframework.validation.BindingResult; 8 | import org.springframework.validation.FieldError; 9 | import org.springframework.validation.ObjectError; 10 | import org.springframework.web.bind.MethodArgumentNotValidException; 11 | import org.springframework.web.bind.annotation.ControllerAdvice; 12 | import org.springframework.web.bind.annotation.ExceptionHandler; 13 | import org.springframework.web.bind.annotation.ResponseBody; 14 | import org.springframework.web.bind.annotation.RestController; 15 | 16 | import java.util.List; 17 | 18 | /** 19 | * 处理hibernate valid异常 20 | * 21 | * @author 肖文杰 22 | */ 23 | @ControllerAdvice(annotations = {RestController.class, Controller.class}) 24 | @ResponseBody 25 | @Slf4j 26 | public class ValidExceptionHandler { 27 | 28 | @ExceptionHandler(MethodArgumentNotValidException.class) 29 | public ResultBean handleMethodArgumentNotValidException(MethodArgumentNotValidException e) { 30 | 31 | // 异常信息 32 | String msg = extractErrorMsg(e.getBindingResult()); 33 | 34 | // 返回对象 35 | ResultBean resultBean = new ResultBean<>(); 36 | 37 | resultBean.setCode(ResultBean.FAIL); 38 | resultBean.setMsg(msg); 39 | 40 | return resultBean; 41 | } 42 | 43 | /** 44 | * 异常信息 45 | * 46 | * @param result 47 | * @return 48 | */ 49 | private String extractErrorMsg(BindingResult result) { 50 | List errors = result.getFieldErrors(); 51 | 52 | return errors.stream().map(e -> e.getField()+ ":" + e.getDefaultMessage()) 53 | .reduce((s1, s2) -> s1 + " ; " +s2).orElseGet( ()->"参数非法"); 54 | } 55 | } -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/tool/XWJDate.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.tool; 2 | 3 | import java.text.ParseException; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Calendar; 6 | import java.util.Date; 7 | 8 | public class XWJDate { 9 | 10 | /** 11 | * 日期相隔多少天(除掉周末) 12 | * 13 | * @param d1 14 | * @param d2 15 | * @return 16 | */ 17 | public static int betweenWorkday(Date d1, Date d2) { 18 | return doBetween(d1, d2, true); 19 | } 20 | 21 | /** 22 | * 日期相隔多少天 23 | * 24 | * @param d1 25 | * @param d2 26 | * @return 27 | */ 28 | public static int between(Date d1, Date d2) { 29 | return doBetween(d1, d2, false); 30 | } 31 | 32 | private static int doBetween(Date d1, Date d2, boolean onlywork) { 33 | if (d2.before(d1)) { 34 | throw new IllegalArgumentException("d1 > d2"); 35 | } 36 | 37 | Calendar calendar = Calendar.getInstance(); 38 | calendar.setTime(d1); 39 | 40 | int count = 1; 41 | while (true) { 42 | calendar.add(Calendar.DAY_OF_MONTH, 1); 43 | if (calendar.getTime().equals(d2)) { 44 | return count; 45 | } 46 | 47 | if (onlywork) { 48 | int day = calendar.get(Calendar.DAY_OF_WEEK); 49 | if (day == 0 || day == 6) { 50 | continue; 51 | } 52 | } 53 | 54 | count++; 55 | } 56 | } 57 | 58 | /** 59 | * 字符串转日期 yyyy-MM-dd 60 | * 61 | * @param s 62 | * @return 63 | */ 64 | public static Date toDate(String s) { 65 | SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd"); 66 | try { 67 | return sf.parse(s); 68 | } catch (ParseException e) { 69 | e.printStackTrace(); 70 | } 71 | throw new IllegalArgumentException("错误的日志格式yyyy-MM-dd: " + s); 72 | } 73 | 74 | public static void main(String[] args) throws ParseException { 75 | Date d1 = toDate("2018-04-01"); 76 | Date d2 = toDate("2018-04-07"); 77 | int between = between(d1, d2); 78 | System.out.println(between); 79 | 80 | int between2 = betweenWorkday(d1, d2); 81 | System.out.println(between2); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /JavaSource/blog/src/main/java/cn/xiaowenjie/controllers/BlogController.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.controllers; 2 | 3 | import cn.xiaowenjie.beans.Blog; 4 | import cn.xiaowenjie.beans.Config; 5 | import cn.xiaowenjie.common.annotations.Log; 6 | import cn.xiaowenjie.common.beans.PageReq; 7 | import cn.xiaowenjie.common.beans.PageResp; 8 | import cn.xiaowenjie.common.beans.ResultBean; 9 | import cn.xiaowenjie.consts.LogConst; 10 | import cn.xiaowenjie.services.BlogService; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | import javax.validation.Valid; 15 | import java.util.Collection; 16 | 17 | /** 18 | * blog 19 | * 20 | * @author 肖文杰 https://github.com/xwjie/ 21 | * 22 | */ 23 | @RequestMapping("/blog") 24 | @RestController 25 | @CrossOrigin 26 | public class BlogController { 27 | 28 | @Autowired 29 | BlogService blogService; 30 | 31 | @GetMapping("/all") 32 | @Log(action = LogConst.ACTION_QUERY, itemType = LogConst.ITEM_TYPE_BLOG) 33 | public ResultBean> getAll() { 34 | return new ResultBean>(blogService.getAll()); 35 | } 36 | 37 | @GetMapping(value = "/list") 38 | @Log(action = LogConst.ACTION_QUERY, itemType = LogConst.ITEM_TYPE_BLOG, param = "#param") 39 | public ResultBean> list(PageReq param) { 40 | return new ResultBean<>(blogService.listPage(param.toPageable(), param.getKeyword())); 41 | } 42 | 43 | /** 44 | * 新增配置 45 | * 46 | * @param blog 47 | * @return 48 | */ 49 | @PostMapping("/add") 50 | @Log(action = LogConst.ACTION_ADD, itemType = LogConst.ITEM_TYPE_BLOG, itemId = "#blog.title") 51 | public ResultBean add(@RequestBody @Valid Blog blog) { 52 | return new ResultBean(blogService.add(blog)); 53 | } 54 | 55 | @PostMapping("/delete") 56 | @Log(action = LogConst.ACTION_DELETE, itemType = LogConst.ITEM_TYPE_BLOG, itemId = "#id") 57 | public ResultBean delete(@RequestParam long id) { 58 | return new ResultBean(blogService.delete(id)); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /JavaSource/main/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | springbootcodetemplate 7 | cn.xiaowenjie 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | main 13 | 14 | 15 | 16 | 17 | 18 | cn.xiaowenjie 19 | core 20 | 1.0-SNAPSHOT 21 | 22 | 23 | 24 | cn.xiaowenjie 25 | favorite 26 | 1.0-SNAPSHOT 27 | 28 | 29 | 30 | cn.xiaowenjie 31 | blog 32 | 1.0-SNAPSHOT 33 | 34 | 35 | 36 | 37 | 38 | org.springframework 39 | spring-test 40 | 5.0.6.RELEASE 41 | compile 42 | 43 | 44 | 45 | cn.xiaowenjie 46 | chart 47 | 1.0-SNAPSHOT 48 | 49 | 50 | 51 | junit 52 | junit 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-test 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/resources/static/cache.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Cache Index 6 | 7 | 8 |

9 | Cache Names 10 | 11 |

12 |
13 | 27 | 28 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/controllers/ConfigController.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.controllers; 2 | 3 | import java.util.Collection; 4 | 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.CrossOrigin; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.ModelAttribute; 9 | import org.springframework.web.bind.annotation.PostMapping; 10 | import org.springframework.web.bind.annotation.RequestAttribute; 11 | import org.springframework.web.bind.annotation.RequestBody; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.RestController; 14 | 15 | import cn.xiaowenjie.beans.Config; 16 | import cn.xiaowenjie.common.beans.PageReq; 17 | import cn.xiaowenjie.common.beans.PageResp; 18 | import cn.xiaowenjie.common.beans.ResultBean; 19 | import cn.xiaowenjie.services.ConfigService; 20 | 21 | /** 22 | * 配置相关的controller,支持跨域 23 | * 24 | * @author 肖文杰 https://github.com/xwjie/ 25 | * 26 | */ 27 | @RequestMapping("/config") 28 | @RestController 29 | @CrossOrigin 30 | public class ConfigController { 31 | 32 | @Autowired 33 | ConfigService configService; 34 | 35 | @GetMapping("/all") 36 | public ResultBean> getAll() { 37 | return new ResultBean>(configService.getAll()); 38 | } 39 | 40 | @GetMapping(value = "/list") 41 | public ResultBean> list(PageReq param) { 42 | return new ResultBean<>(configService.listPage(param.toPageable(), param.getKeyword())); 43 | } 44 | 45 | /** 46 | * 新增配置 47 | * 48 | * FIXME 同时支持json格式和表单格式 49 | * 50 | * @param config 51 | * @return 52 | */ 53 | @PostMapping("/add") 54 | public ResultBean add(@RequestBody Config config) { 55 | System.out.println(configService.getClass()); 56 | return new ResultBean(configService.add(config)); 57 | } 58 | 59 | @PostMapping("/delete") 60 | public ResultBean delete(long id) { 61 | return new ResultBean(configService.delete(id)); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/controllers/CacheController.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.controllers; 2 | 3 | import java.util.Collection; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.cache.CacheManager; 9 | import org.springframework.web.bind.annotation.CrossOrigin; 10 | import org.springframework.web.bind.annotation.GetMapping; 11 | import org.springframework.web.bind.annotation.PostMapping; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.RequestParam; 14 | import org.springframework.web.bind.annotation.RestController; 15 | 16 | import cn.xiaowenjie.common.beans.ResultBean; 17 | 18 | /** 19 | * cache 操作 20 | * 21 | * @author 肖文杰 https://github.com/xwjie/ 22 | * 23 | */ 24 | @RequestMapping("/cache") 25 | @RestController 26 | @CrossOrigin 27 | public class CacheController { 28 | private static final Logger logger = LoggerFactory.getLogger(CacheController.class); 29 | 30 | @Autowired 31 | CacheManager cacheManager; 32 | 33 | /** 34 | * 得到所有的cache 35 | * 36 | * @return 37 | */ 38 | @GetMapping("/names") 39 | public ResultBean> index() { 40 | return new ResultBean<>(cacheManager.getCacheNames()); 41 | } 42 | 43 | /** 44 | * 清空某个cache 45 | * 46 | * @param name 47 | * @return 48 | */ 49 | @PostMapping("/clear") 50 | public ResultBean clear(@RequestParam String name) { 51 | logger.info("clear cache : {}", name); 52 | cacheManager.getCache(name).clear(); 53 | return new ResultBean<>(true); 54 | } 55 | 56 | /** 57 | * 清空所有cache 58 | * 59 | * @return 60 | */ 61 | @PostMapping("/clearAll") 62 | public ResultBean clearAll() { 63 | logger.info("clear all cache ..."); 64 | 65 | Collection cacheNames = cacheManager.getCacheNames(); 66 | 67 | for (String name : cacheNames) { 68 | logger.info("clear cache : {}", name); 69 | cacheManager.getCache(name).clear(); 70 | } 71 | 72 | return new ResultBean<>(true); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /docs/note/spring-security.md: -------------------------------------------------------------------------------- 1 | # Spring Security 使用 2 | 3 | ## 增加依赖 4 | 5 | ```xml 6 | 7 | org.springframework.boot 8 | spring-boot-starter-security 9 | 10 | ``` 11 | 12 | ## response 多了三个头 13 | 14 | ``` 15 | X-Content-Type-Options:nosniff 16 | X-Frame-Options:DENY 17 | X-XSS-Protection:1; mode=block 18 | ``` 19 | 20 | ## 指定认证方式 21 | 22 | 使用basic认证和普通的页面输入认证,只能选一种,否则返回401的时候还会重定向到登录页面。 23 | 24 | * basis 认证 25 | 26 | ```java 27 | .httpBasic().and() 28 | ``` 29 | 30 | * 页面认证 31 | 32 | ```java 33 | .formLogin().loginPage("/login").permitAll().and() 34 | ``` 35 | 36 | 完整: 37 | 38 | ```java 39 | @Configuration 40 | @EnableWebSecurity 41 | public class WebSecurityConfig extends WebSecurityConfigurerAdapter { 42 | 43 | @Override 44 | protected void configure(HttpSecurity http) throws Exception { 45 | // 先放开 46 | http.authorizeRequests().anyRequest().permitAll(); 47 | 48 | // http 49 | // .authorizeRequests() 50 | // .antMatchers("/", "/home","/resources/**", "/favicon.ico").permitAll() 51 | // .anyRequest() 52 | // .authenticated() 53 | // .and() 54 | // // basic返回401 ,【不能】配置loginpage 55 | // .httpBasic() 56 | // .and() 57 | // // 不是http认证的时候,返回302,需要配置loginPage链接 58 | //// .formLogin() 59 | //// .loginPage("/login") 60 | //// .permitAll() 61 | //// .and() 62 | // .logout() 63 | // .permitAll(); 64 | 65 | //http.csrf().disable(); 66 | } 67 | 68 | @Autowired 69 | public void configure(AuthenticationManagerBuilder auth) throws Exception { 70 | auth 71 | .inMemoryAuthentication() 72 | .withUser("xwjie").password("xwjie").roles("USER"); 73 | } 74 | } 75 | ``` 76 | 77 | ## 修改 X-Frame-Options 78 | 79 | spring security 默认的 `X-Frame-Options` 是 `deny` ,修改为 `SAMEORIGIN` ,否则连h2都无法嵌入显示了。如下: 80 | 81 | ```java 82 | http.headers().frameOptions().sameOrigin(); 83 | ``` 84 | -------------------------------------------------------------------------------- /JavaSource/main/src/main/java/cn/xiaowenjie/springconfigs/JMSConfig.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.springconfigs; 2 | 3 | import javax.jms.ConnectionFactory; 4 | 5 | import org.apache.activemq.ActiveMQConnectionFactory; 6 | import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.jms.annotation.EnableJms; 10 | import org.springframework.jms.config.DefaultJmsListenerContainerFactory; 11 | import org.springframework.jms.config.JmsListenerContainerFactory; 12 | import org.springframework.jms.support.converter.MappingJackson2MessageConverter; 13 | import org.springframework.jms.support.converter.MessageConverter; 14 | import org.springframework.jms.support.converter.MessageType; 15 | 16 | @Configuration 17 | @EnableJms 18 | public class JMSConfig { 19 | 20 | @Bean 21 | public JmsListenerContainerFactory myFactory(ConnectionFactory connectionFactory, 22 | DefaultJmsListenerContainerFactoryConfigurer configurer) { 23 | DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); 24 | // This provides all boot's default to this factory, including the message 25 | // converter 26 | configurer.configure(factory, connectionFactory); 27 | 28 | // You could still override some of Boot's default if necessary. 29 | 30 | // 设置连接数 31 | factory.setConcurrency("3-10"); 32 | // 重连间隔时间 33 | factory.setRecoveryInterval(1000L); 34 | 35 | return factory; 36 | } 37 | 38 | /** 39 | * 使用内置的activeMQ 40 | */ 41 | private static final String JMS_BROKER_URL = "vm://embedded?broker.persistent=false,useShutdownHook=false"; 42 | 43 | @Bean 44 | public ConnectionFactory connectionFactory() { 45 | return new ActiveMQConnectionFactory(JMS_BROKER_URL); 46 | } 47 | 48 | @Bean 49 | public MessageConverter jacksonJmsMessageConverter() { 50 | MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter(); 51 | 52 | converter.setTargetType(MessageType.TEXT); 53 | converter.setTypeIdPropertyName("_type"); 54 | 55 | return converter; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /WebProject/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 41 | 42 | 43 | 59 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/common/filters/UserFilter.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.common.filters; 2 | 3 | import cn.xiaowenjie.common.rbac.User; 4 | import cn.xiaowenjie.common.utils.UserUtil; 5 | import cn.xiaowenjie.services.UserService; 6 | 7 | import javax.servlet.*; 8 | import javax.servlet.annotation.WebFilter; 9 | import javax.servlet.http.Cookie; 10 | import javax.servlet.http.HttpServletRequest; 11 | import javax.servlet.http.HttpSession; 12 | import java.io.IOException; 13 | 14 | /** 15 | * 用户filter,设置当前用户和语言到threadlocal中。 16 | * 17 | * @author 肖文杰 18 | * 19 | */ 20 | @WebFilter(filterName = "userFilter", urlPatterns = "/*") 21 | public class UserFilter implements Filter { 22 | 23 | @Override 24 | public void init(FilterConfig filterConfig) throws ServletException { 25 | 26 | } 27 | 28 | @Override 29 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 30 | throws IOException, ServletException { 31 | 32 | fillUserInfo((HttpServletRequest) request); 33 | 34 | try { 35 | chain.doFilter(request, response); 36 | } finally { 37 | clearAllUserInfo(); 38 | } 39 | } 40 | 41 | 42 | private void clearAllUserInfo() { 43 | UserUtil.clearAllUserInfo(); 44 | } 45 | 46 | private void fillUserInfo(HttpServletRequest request) { 47 | // 用户信息 48 | User user = getUserFromSession(request); 49 | //FIXME User user = getUserFromSpringSecurity(); 50 | 51 | if (user != null) { 52 | UserUtil.setUser(user); 53 | } 54 | 55 | // 语言信息 56 | String locale = getLocaleFromCookies(request); 57 | 58 | if (locale != null) { 59 | UserUtil.setLocale(locale); 60 | } 61 | } 62 | 63 | private String getLocaleFromCookies(HttpServletRequest request) { 64 | Cookie[] cookies = request.getCookies(); 65 | 66 | if (cookies == null) { 67 | return null; 68 | } 69 | 70 | for (int i = 0; i < cookies.length; i++) { 71 | if (UserUtil.KEY_LANG.equals(cookies[i].getName())) { 72 | return cookies[i].getValue(); 73 | } 74 | } 75 | 76 | return null; 77 | } 78 | 79 | private User getUserFromSession(HttpServletRequest request) { 80 | HttpSession session = request.getSession(); 81 | 82 | //if (session == null) { 83 | // return null; 84 | //} 85 | 86 | // 从session中获取用户信息放到工具类中 87 | return (User) session.getAttribute(UserUtil.KEY_USER); 88 | } 89 | 90 | @Override 91 | public void destroy() { 92 | 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /WebProject/src/components/table/ConfigTable.vue: -------------------------------------------------------------------------------- 1 | /** 2 | * 表格组件范例 3 | * 4 | * @author xiaowenjie https://github.com/xwjie 5 | */ 6 | 61 | 62 | -------------------------------------------------------------------------------- /WebProject/src/components/table/ConfigTable2.vue: -------------------------------------------------------------------------------- 1 | /** 2 | * 表格组件范例 3 | * 4 | * @author xiaowenjie https://github.com/xwjie 5 | */ 6 | 59 | 60 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/common/utils/UserUtil.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.common.utils; 2 | 3 | import java.util.List; 4 | import java.util.Locale; 5 | 6 | import cn.xiaowenjie.common.consts.Roles; 7 | import cn.xiaowenjie.common.rbac.Role; 8 | import org.apache.log4j.MDC; 9 | 10 | import cn.xiaowenjie.common.rbac.User; 11 | import cn.xiaowenjie.common.exceptions.UnloginException; 12 | 13 | /** 14 | * 用户工具类 15 | * 16 | * @author 肖文杰 17 | */ 18 | public class UserUtil { 19 | 20 | private final static ThreadLocal tlUser = new ThreadLocal(); 21 | 22 | private final static ThreadLocal tlLocale = new ThreadLocal() { 23 | protected Locale initialValue() { 24 | // 语言的默认值 25 | return Locale.CHINESE; 26 | } 27 | 28 | ; 29 | }; 30 | 31 | public static final String KEY_LANG = "lang"; 32 | 33 | public static final String KEY_USER = "user"; 34 | 35 | public static void setUser(User user) { 36 | tlUser.set(user); 37 | 38 | // 把用户信息放到log4j 39 | MDC.put(KEY_USER, user.getName()); 40 | } 41 | 42 | /** 43 | * 如果没有登录,返回null 44 | * 45 | * @return 46 | */ 47 | public static User getUserIfLogin() { 48 | return tlUser.get(); 49 | } 50 | 51 | /** 52 | * 如果没有登录会抛出异常 53 | * 54 | * @return 55 | */ 56 | public static User getUser() { 57 | User user = tlUser.get(); 58 | 59 | if (user == null) { 60 | throw new UnloginException(); 61 | } 62 | 63 | return user; 64 | } 65 | 66 | public static long getUserId() { 67 | return getUser().getId(); 68 | } 69 | 70 | public static void setLocale(String locale) { 71 | setLocale(new Locale(locale)); 72 | } 73 | 74 | public static void setLocale(Locale locale) { 75 | tlLocale.set(locale); 76 | } 77 | 78 | public static Locale getLocale() { 79 | return tlLocale.get(); 80 | } 81 | 82 | public static void clearAllUserInfo() { 83 | tlUser.remove(); 84 | tlLocale.remove(); 85 | 86 | MDC.remove(KEY_USER); 87 | } 88 | 89 | /** 90 | * 是否管理员 91 | * 92 | * @return 93 | */ 94 | public static boolean isAdmin() { 95 | List roles = getUser().getRoles(); 96 | 97 | for (Role role : roles) { 98 | if (Roles.ADMIN .equals(role.getName())) { 99 | return true; 100 | } 101 | } 102 | 103 | return false; 104 | 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/services/UserService.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.services; 2 | 3 | import cn.xiaowenjie.common.beans.PageResp; 4 | import cn.xiaowenjie.common.consts.Roles; 5 | import cn.xiaowenjie.common.daos.UserDao; 6 | import cn.xiaowenjie.common.rbac.User; 7 | import cn.xiaowenjie.tool.PasswordUtil; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.apache.shiro.SecurityUtils; 10 | import org.apache.shiro.authc.UsernamePasswordToken; 11 | import org.apache.shiro.authz.annotation.RequiresRoles; 12 | import org.apache.shiro.subject.Subject; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.data.domain.Pageable; 15 | import org.springframework.stereotype.Service; 16 | import org.springframework.util.StringUtils; 17 | 18 | import static cn.xiaowenjie.common.utils.CheckUtil.check; 19 | 20 | /** 21 | * 用户相关处理类 22 | * 23 | * @author 肖文杰 https://github.com/xwjie/ 24 | * 25 | */ 26 | @Service 27 | @Slf4j 28 | public class UserService { 29 | 30 | @Autowired 31 | UserDao userDao; 32 | 33 | public User findUser(String username) { 34 | return userDao.findByName(username); 35 | } 36 | 37 | /** 38 | * FIXME 39 | * @param username 40 | * @param password 41 | * @return 42 | */ 43 | public User login(String username, String password) { 44 | Subject subject = SecurityUtils.getSubject(); 45 | 46 | UsernamePasswordToken token = new UsernamePasswordToken(username, password); 47 | subject.login(token); 48 | 49 | // 登陆成功,取出用户 50 | User user = (User) subject.getPrincipal(); 51 | 52 | return user; 53 | //return findUser(username); 54 | } 55 | 56 | 57 | public void logout() { 58 | Subject subject = SecurityUtils.getSubject(); 59 | subject.logout(); 60 | } 61 | 62 | public PageResp list(Pageable pageable, String keyword) { 63 | if (StringUtils.isEmpty(keyword)) { 64 | return new PageResp(userDao.findAll(pageable)); 65 | } else { 66 | // 也可以用springjpa 的 Specification 来实现查找 67 | return new PageResp<>(userDao.findAllByKeyword(keyword, pageable)); 68 | } 69 | } 70 | 71 | /** 72 | * 修改密码 73 | * @param id 74 | * @param password 75 | */ 76 | //FIXME why not work??!! 77 | @RequiresRoles(Roles.ADMIN) 78 | public void updatePwd(long id, String password) { 79 | User user = userDao.findOne(id); 80 | 81 | check(user != null , "id.error", id); 82 | check(checkPwd(password), "password.invalid"); 83 | 84 | // FIXME 85 | log.info("modify password, user id: " + id + ", password:" + password); 86 | 87 | // 生成新密码 88 | String hash = PasswordUtil.renewPassword(password, user.getSalt()); 89 | 90 | user.setPassword(hash); 91 | 92 | userDao.save(user); 93 | } 94 | 95 | //TODO 96 | private boolean checkPwd(String password) { 97 | return password.length() >= 3; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /WebProject/src/commons/Pagination.vue: -------------------------------------------------------------------------------- 1 | /** 2 | * 分页组件 3 | * 父组件更新需要调用reload方法。 4 | * @author xiaowenjie https://github.com/xwjie 5 | */ 6 | 17 | 18 | 120 | 121 | 122 | 125 | -------------------------------------------------------------------------------- /JavaSource/main/src/main/java/cn/xiaowenjie/springconfigs/RedisConfig.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.springconfigs; 2 | 3 | import java.lang.reflect.Method; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | import org.springframework.cache.CacheManager; 8 | import org.springframework.cache.annotation.CachingConfigurerSupport; 9 | import org.springframework.cache.annotation.EnableCaching; 10 | import org.springframework.cache.interceptor.KeyGenerator; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.Configuration; 13 | import org.springframework.data.redis.cache.RedisCacheManager; 14 | import org.springframework.data.redis.connection.RedisConnectionFactory; 15 | import org.springframework.data.redis.core.RedisTemplate; 16 | import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; 17 | import org.springframework.data.redis.serializer.RedisSerializer; 18 | import org.springframework.data.redis.serializer.StringRedisSerializer; 19 | 20 | import cn.xiaowenjie.caches.CacheNames; 21 | 22 | /** 23 | * Redis 配置 24 | * 25 | * @author 肖文杰 https://github.com/xwjie 26 | */ 27 | @Configuration 28 | @EnableCaching 29 | public class RedisConfig extends CachingConfigurerSupport { 30 | 31 | @Override 32 | public KeyGenerator keyGenerator() { 33 | return new KeyGenerator() { 34 | @Override 35 | public Object generate(Object target, Method method, Object... params) { 36 | StringBuilder sb = new StringBuilder(); 37 | 38 | sb.append(target.getClass().getSimpleName()); 39 | sb.append('.').append(method.getName()); 40 | 41 | // FIXME 参数太长的时候请指定key属性,否则key太长 42 | for (Object obj : params) { 43 | if (obj != null) { 44 | sb.append(obj.toString()); 45 | } 46 | } 47 | 48 | return sb.toString(); 49 | } 50 | }; 51 | } 52 | 53 | @Bean 54 | public CacheManager cacheManager(RedisTemplate redisTemplate) { 55 | RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate); 56 | 57 | Map expires = new HashMap<>(); 58 | 59 | expires.put(CacheNames.CONFIG, 600L); 60 | 61 | // 设置超时 62 | cacheManager.setExpires(expires); 63 | 64 | // 没有设置的缓存默认过期时间 65 | cacheManager.setDefaultExpiration(60 * 60); 66 | 67 | return cacheManager; 68 | } 69 | 70 | @Bean 71 | public RedisTemplate redisTemplate(RedisConnectionFactory factory) { 72 | RedisTemplate template = new RedisTemplate(); 73 | 74 | template.setConnectionFactory(factory); 75 | 76 | RedisSerializer keySerializer = new StringRedisSerializer(); 77 | 78 | // 设置key序列化类,否则key前面会多了一些乱码 79 | template.setKeySerializer(keySerializer); 80 | template.setHashKeySerializer(keySerializer); 81 | 82 | // FIXME 有些版本有bug,没有序列化的只能序列化,但无法反序列化 83 | GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer(); 84 | 85 | // 设置内容序列化类 86 | template.setValueSerializer(jsonSerializer); 87 | 88 | template.afterPropertiesSet(); 89 | 90 | return template; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /JavaSource/main/src/main/java/cn/xiaowenjie/jpa/JMSObjectComsumer.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.jpa; 2 | 3 | import cn.xiaowenjie.beans.BaseEntity; 4 | import cn.xiaowenjie.beans.Favorite; 5 | import cn.xiaowenjie.consts.ObjType; 6 | import cn.xiaowenjie.daos.BlogDao; 7 | import cn.xiaowenjie.daos.FavoriteDao; 8 | import cn.xiaowenjie.features.Favoritable; 9 | import cn.xiaowenjie.jms.JMSType; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.data.repository.PagingAndSortingRepository; 13 | import org.springframework.jms.annotation.JmsListener; 14 | import org.springframework.messaging.handler.annotation.Payload; 15 | import org.springframework.stereotype.Component; 16 | 17 | /** 18 | * 对象创建事件 19 | */ 20 | @Component 21 | @Slf4j 22 | public class JMSObjectComsumer { 23 | 24 | @JmsListener(destination = JMSType.CREATE) 25 | public void objectCreatePost(@Payload BaseEntity obj) { 26 | log.info("create: {}", obj); 27 | 28 | updateCounts(obj); 29 | } 30 | 31 | @JmsListener(destination = JMSType.DELETE) 32 | public void deleteObjectPost(@Payload BaseEntity obj) { 33 | log.info("delete : {}", obj); 34 | 35 | updateCounts(obj); 36 | } 37 | 38 | /** 39 | * 现在删除对象的时候,更新各种数据(如收藏数) 40 | * @param obj 41 | */ 42 | private void updateCounts(@Payload BaseEntity obj) { 43 | try { 44 | // 不用担心冲掉 45 | JPAThreadLocal.setBackground(true); 46 | 47 | // 如果是新建了收藏对象,把对应的业务对象的数字增加 48 | if (obj instanceof Favorite) { 49 | log.info("create favorite, update favorite count."); 50 | Favorite favorite = (Favorite) obj; 51 | 52 | // 53 | updateFavoriteCount(favorite.getObjType(), favorite.getObjId()); 54 | } 55 | } finally { 56 | JPAThreadLocal.setBackground(false); 57 | } 58 | } 59 | 60 | /** 61 | * 更新对象的收藏数量 62 | * @param objType 63 | * @param objId 64 | */ 65 | public void updateFavoriteCount(int objType, long objId) { 66 | 67 | PagingAndSortingRepository dao = findDao(objType); 68 | 69 | // 找到对应的对象 70 | // 对象必须实现了可收藏,否则报错,可以把报错完善一下 71 | Favoritable bizObj = (Favoritable) dao.findOne(objId); 72 | 73 | // 更新收藏数量字段 74 | // FIXME 也可以在DAO上做,会少一些SQL 75 | int count = favoriteDao.countByObjTypeAndObjId(objType, objId); 76 | bizObj.setFavoriteCount(count); 77 | 78 | dao.save(bizObj); 79 | 80 | log.info("update obj [type:{}, id:{}] favorite count[{}] success.", objType, objId, count); 81 | } 82 | 83 | @Autowired 84 | BlogDao blogDao; 85 | 86 | @Autowired 87 | FavoriteDao favoriteDao; 88 | 89 | PagingAndSortingRepository findDao(int objType) { 90 | if (objType == ObjType.BLOG) { 91 | return blogDao; 92 | } 93 | 94 | throw new IllegalArgumentException("object type error: " + objType); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | ${LOG_HOME}/${appname}.log.%d{yyyy-MM-dd}.log 19 | 20 | 30 21 | 22 | 23 | 24 | 25 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%X{logjson}] [%thread] %-5level %logger{50} -%msg%n 26 | 27 | 28 | 29 | 31 | 10MB 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | %d{yyyy-MM-dd HH:mm:ss.SSS} [%X{logjson}] [%thread] %-5level %logger{36} -%msg%n 41 | 42 | 43 | 44 | 45 | 46 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | ${smtpHost} 62 | ${smtpPort} 63 | ${username} 64 | ${password} 65 | ${SSL} 66 | true 67 | ${email_to} 68 | ${email_from} 69 | ${email_subject} 70 | 71 | %date%level%thread%logger{0}%line%message 72 | 73 | 74 | 75 | 76 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/common/aop/LogAOP.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.common.aop; 2 | 3 | import cn.xiaowenjie.common.annotations.Log; 4 | import cn.xiaowenjie.common.rbac.User; 5 | import cn.xiaowenjie.common.utils.SPELUtil; 6 | import cn.xiaowenjie.common.utils.UserUtil; 7 | import com.alibaba.fastjson.JSONObject; 8 | import lombok.SneakyThrows; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.apache.log4j.MDC; 11 | import org.aspectj.lang.ProceedingJoinPoint; 12 | import org.aspectj.lang.annotation.Around; 13 | import org.aspectj.lang.annotation.Aspect; 14 | import org.aspectj.lang.annotation.Pointcut; 15 | import org.aspectj.lang.reflect.MethodSignature; 16 | import org.springframework.core.annotation.Order; 17 | import org.springframework.stereotype.Component; 18 | 19 | /** 20 | * 日志信息处理AOP 21 | * 把需要的信息从参数中提取出来,转成json字符串放到MDC中使用。 22 | * 23 | * 注意:注解不支持重入(就是嵌套的方法里面还有LOG注解),因为我觉得不需要嵌套 24 | * 如果你项目中有这种使用场景,自己修改一下也非常简单,就是修改前保存起来即可。 25 | * 26 | * @author 晓风轻 https://github.com/xwjie/ElementVueSpringbootCodeTemplate 27 | */ 28 | @Order(-100) 29 | @Component 30 | @Aspect 31 | @Slf4j 32 | public class LogAOP { 33 | 34 | private static final String JSON_KEY = "logjson"; 35 | 36 | @Pointcut("@annotation(cn.xiaowenjie.common.annotations.Log)") 37 | public void logMethod() { 38 | 39 | } 40 | 41 | @Around("logMethod()") 42 | @SneakyThrows 43 | public Object handleLogMethod(ProceedingJoinPoint pjp) { 44 | long startTime = System.currentTimeMillis(); 45 | 46 | Object result; 47 | 48 | try { 49 | putLogInfo2MDC(pjp); 50 | 51 | result = pjp.proceed(); 52 | 53 | // 本次操作用时(毫秒) 54 | long elapsedTime = System.currentTimeMillis() - startTime; 55 | log.info("[{}]use time: {}", pjp.getSignature(), elapsedTime); 56 | } finally { 57 | clearMDC(); 58 | } 59 | 60 | return result; 61 | } 62 | 63 | private void clearMDC() { 64 | MDC.remove(JSON_KEY); 65 | } 66 | 67 | private void putLogInfo2MDC(ProceedingJoinPoint pjp) { 68 | // 得到方法上的注解 69 | MethodSignature signature = (MethodSignature) pjp.getSignature(); 70 | 71 | Log logAnnotation = signature.getMethod().getAnnotation(Log.class); 72 | 73 | 74 | SPELUtil spel = new SPELUtil(pjp); 75 | 76 | JSONObject json = new JSONObject(); 77 | 78 | // 使用单字母而不是全名,是为了节省日志文件大小。 79 | 80 | // 用户 81 | User user = UserUtil.getUserIfLogin(); 82 | 83 | if (user != null) { 84 | json.put("U", user.getName()); 85 | } 86 | 87 | // 操作 88 | json.put("A", logAnnotation.action()); 89 | 90 | // 对象类型 91 | json.put("T", logAnnotation.itemType()); 92 | 93 | // 对象id,spel表达式 94 | json.put("I", spel.cacl(logAnnotation.itemId())); 95 | 96 | // 其他参数,spel表达式 97 | json.put("P", spel.cacl(logAnnotation.param())); 98 | 99 | MDC.put(JSON_KEY, json.toJSONString()); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/common/aop/ControllerAOP.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.common.aop; 2 | 3 | import org.apache.shiro.authc.IncorrectCredentialsException; 4 | import org.apache.shiro.authz.AuthorizationException; 5 | import org.apache.shiro.authz.UnauthorizedException; 6 | import org.aspectj.lang.ProceedingJoinPoint; 7 | import org.aspectj.lang.annotation.Around; 8 | import org.aspectj.lang.annotation.Aspect; 9 | import org.aspectj.lang.annotation.Pointcut; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.core.annotation.Order; 13 | import org.springframework.stereotype.Component; 14 | 15 | import cn.xiaowenjie.common.beans.ResultBean; 16 | import cn.xiaowenjie.common.exceptions.CheckException; 17 | import cn.xiaowenjie.common.exceptions.UnloginException; 18 | 19 | /** 20 | * 处理和包装异常 21 | * 22 | * @author 肖文杰 23 | */ 24 | @Aspect 25 | @Component 26 | @Order(-99) 27 | public class ControllerAOP { 28 | private static final Logger logger = LoggerFactory.getLogger(ControllerAOP.class); 29 | 30 | @Pointcut("execution(public cn.xiaowenjie.common.beans.ResultBean *(..))") 31 | public void controllerMethod() { 32 | } 33 | 34 | @Around("controllerMethod()") 35 | public Object handlerControllerMethod(ProceedingJoinPoint pjp) { 36 | long startTime = System.currentTimeMillis(); 37 | 38 | ResultBean result; 39 | 40 | try { 41 | result = (ResultBean) pjp.proceed(); 42 | logger.info(pjp.getSignature() + "use time:" + (System.currentTimeMillis() - startTime)); 43 | } catch (Throwable e) { 44 | result = handlerException(pjp, e); 45 | } 46 | 47 | return result; 48 | } 49 | 50 | private ResultBean handlerException(ProceedingJoinPoint pjp, Throwable e) { 51 | ResultBean result = new ResultBean(); 52 | 53 | // 已知异常 54 | if (e instanceof CheckException) { 55 | result.setMsg(e.getLocalizedMessage()); 56 | result.setCode(ResultBean.FAIL); 57 | } 58 | // 自己抛出的 59 | else if (e instanceof UnloginException ) { 60 | result.setMsg("Unlogin"); 61 | result.setCode(ResultBean.NO_LOGIN); 62 | } 63 | //shiro异常: 登陆失败,如密码错误 64 | else if (e instanceof IncorrectCredentialsException) { 65 | result.setMsg("Login failed. Try xwjie/123456"); 66 | result.setCode(ResultBean.FAIL); 67 | } 68 | // shiro异常:没有权限 69 | else if (e instanceof UnauthorizedException) { 70 | result.setMsg("NO PERMISSION: " + e.getMessage()); 71 | result.setCode(ResultBean.NO_PERMISSION); 72 | } 73 | // shiro抛出 74 | else if (e instanceof AuthorizationException) { 75 | result.setMsg("Unlogin"); 76 | result.setCode(ResultBean.NO_LOGIN); 77 | } 78 | else { 79 | logger.error(pjp.getSignature() + " error ", e); 80 | 81 | //TODO 未知的异常,应该格外注意,可以发送邮件通知等 82 | result.setMsg(e.toString()); 83 | result.setCode(ResultBean.FAIL); 84 | } 85 | 86 | return result; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /WebProject/src/libs/util.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import env from '../config/env'; 3 | import Vue from 'vue'; 4 | import param from './param' 5 | 6 | let util = { 7 | 8 | }; 9 | 10 | util.title = function (title) { 11 | title = title ? title + ' - Home' : '晓风轻 Element Vue Springboot 代码模板'; 12 | window.document.title = title; 13 | }; 14 | 15 | const ajaxUrl = env === 'development' ? 16 | 'http://localhost:8080' : 17 | env === 'production' ? 18 | 'springboot' : 19 | 'springboot'; 20 | 21 | let axiosInstance = axios.create({ 22 | baseURL: ajaxUrl, 23 | timeout: 30000, 24 | withCredentials: true 25 | }); 26 | 27 | /* 28 | * 返回Response的data(即json数据) 29 | */ 30 | function handlerData(response) { 31 | // 全局的没有登录异常单独处理 32 | //if (response.data.code === -1) { 33 | // console.log('no login'); 34 | // 还没有找到中断promise好的办法 35 | //} 36 | 37 | console.log(response.data); 38 | 39 | return response.data; 40 | } 41 | 42 | //添加一个返回拦截器 43 | axiosInstance.interceptors.response.use(function (response) { 44 | let data = response.data 45 | 46 | console.log('axiosInstance.interceptors', data); 47 | console.log('axiosInstance.interceptors', data.code); 48 | 49 | //对返回的数据进行一些处理 50 | // 全局的没有登录异常单独处理 51 | if (response.status == 401 || data.code === -1) { 52 | console.log('no login'); 53 | 54 | // 通知打开登录窗口 55 | Vue.bus.emit('login-open'); 56 | 57 | // 还没有找到中断promise好的办法 58 | return Promise.reject('ERROR_NOLOGIN'); 59 | } 60 | 61 | return response; 62 | }, function (error) { 63 | let response = error.response; 64 | 65 | console.log("response", error); 66 | 67 | for(var i in error){ 68 | console.log(i, error[i]); 69 | } 70 | 71 | if(response){ 72 | // 全局的没有登录异常单独处理 73 | if (response.status == 401 || response.data.code === -1) { 74 | console.log('no login'); 75 | 76 | // 通知打开登录窗口 77 | Vue.bus.emit('login-open'); 78 | 79 | // 还没有找到中断promise好的办法 80 | return Promise.reject('ERROR_NOLOGIN'); 81 | } 82 | } 83 | 84 | //对返回的错误进行一些处理 85 | return Promise.reject(error); 86 | }); 87 | 88 | util.ajax = {}; 89 | 90 | util.ajax.post = function () { 91 | return axiosInstance.post.apply(this, arguments).then(handlerData) 92 | } 93 | 94 | util.ajax.postForm = function (url, data) { 95 | return axiosInstance.post(url, data, { 96 | transformRequest: [param], 97 | headers: { 98 | 'Content-Type': 'application/x-www-form-urlencoded' 99 | } 100 | }).then(handlerData) 101 | } 102 | 103 | util.ajax.get = function () { 104 | return axiosInstance.get.apply(this, arguments).then(handlerData); 105 | } 106 | 107 | /** 108 | * 对象是否包含指定关键字 key 109 | */ 110 | util.isMatch = function(e, key){ 111 | var props = ['name', 'value', 'description']; 112 | 113 | for(var i in props){ 114 | var field = e[props[i]]; 115 | 116 | if(field && field.indexOf(key) > -1){ 117 | return true; 118 | } 119 | } 120 | 121 | return false; 122 | } 123 | 124 | export default util; -------------------------------------------------------------------------------- /WebProject/src/components/uploadfile/UploadFile.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | 101 | 109 | -------------------------------------------------------------------------------- /JavaSource/main/src/main/java/cn/xiaowenjie/springconfigs/MyAuthorizingRealm.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.springconfigs; 2 | 3 | import cn.xiaowenjie.common.rbac.Role; 4 | import cn.xiaowenjie.common.rbac.User; 5 | import cn.xiaowenjie.services.UserService; 6 | import org.apache.shiro.authc.AuthenticationException; 7 | import org.apache.shiro.authc.AuthenticationInfo; 8 | import org.apache.shiro.authc.AuthenticationToken; 9 | import org.apache.shiro.authc.SimpleAuthenticationInfo; 10 | import org.apache.shiro.authc.UsernamePasswordToken; 11 | import org.apache.shiro.authz.AuthorizationInfo; 12 | import org.apache.shiro.authz.SimpleAuthorizationInfo; 13 | import org.apache.shiro.realm.AuthorizingRealm; 14 | import org.apache.shiro.subject.PrincipalCollection; 15 | import org.apache.shiro.util.ByteSource; 16 | import org.slf4j.Logger; 17 | import org.slf4j.LoggerFactory; 18 | import org.springframework.beans.factory.annotation.Autowired; 19 | 20 | public class MyAuthorizingRealm extends AuthorizingRealm { 21 | 22 | private final static Logger logger = LoggerFactory.getLogger(MyAuthorizingRealm.class); 23 | 24 | @Autowired 25 | private UserService userService; 26 | 27 | //shiro的权限配置方法 28 | @Override 29 | protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { 30 | 31 | logger.info("权限配置-->doGetAuthorizationInfo"); 32 | 33 | SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); 34 | logger.info("----------------------------->" + principals.getPrimaryPrincipal()); 35 | 36 | User user = (User) principals.getPrimaryPrincipal(); 37 | 38 | for (Role role : user.getRoles()) { 39 | 40 | authorizationInfo.addRole(role.getName()); 41 | 42 | // 如果有权限,应该增加所有角色对应的权限 43 | // authorizationInfo.addStringPermission() 44 | } 45 | 46 | logger.info("用户" + user.getName() + "具有的角色:" + authorizationInfo.getRoles()); 47 | logger.info("用户" + user.getName() + "具有的权限:" + authorizationInfo.getStringPermissions()); 48 | 49 | return authorizationInfo; 50 | } 51 | 52 | //shiro的身份验证方法 53 | @Override 54 | protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { 55 | 56 | logger.info("正在验证身份..."); 57 | SimpleAuthenticationInfo info = null; 58 | 59 | //将token转换成UsernamePasswordToken 60 | UsernamePasswordToken upToken = (UsernamePasswordToken) token; 61 | //从转换后的token中获取用户名 62 | String username = upToken.getUsername(); 63 | logger.info("----->" + username); 64 | 65 | //查询数据库,得到用户 66 | User user = userService.findUser(username); 67 | 68 | if (user == null) { 69 | return null; 70 | } 71 | 72 | //得到加密密码的盐值 73 | ByteSource salt = ByteSource.Util.bytes(user.getSalt()); 74 | 75 | // logger.info("加密密码的盐:"+salt); 76 | // //得到盐值加密后的密码:只用于方便数据库测试,后期不会用到。 77 | // Object md = new SimpleHash("MD5",upToken.getPassword(),salt,1024); 78 | // logger.info("盐值加密后的密码:"+md); 79 | 80 | info = new SimpleAuthenticationInfo( 81 | user, //用户名 82 | user.getPassword(), //密码 83 | salt, //加密的盐值 84 | getName() //realm name 85 | ); 86 | 87 | return info; 88 | } 89 | 90 | } -------------------------------------------------------------------------------- /JavaSource/favorite/src/main/java/cn/xiaowenjie/services/FavoriteService.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.services; 2 | 3 | import cn.xiaowenjie.beans.Favorite; 4 | import cn.xiaowenjie.beans.Favorite; 5 | import cn.xiaowenjie.common.beans.PageResp; 6 | import cn.xiaowenjie.common.consts.Roles; 7 | import cn.xiaowenjie.common.utils.UserUtil; 8 | import cn.xiaowenjie.daos.FavoriteDao; 9 | import cn.xiaowenjie.daos.FavoriteDao; 10 | import com.google.common.collect.Lists; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.apache.shiro.authz.annotation.RequiresRoles; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.data.domain.Pageable; 15 | import org.springframework.stereotype.Service; 16 | import org.springframework.util.StringUtils; 17 | 18 | import java.util.Collection; 19 | import java.util.List; 20 | 21 | import static cn.xiaowenjie.common.utils.CheckUtil.*; 22 | 23 | /** 24 | * 配置业务处理类 25 | * 26 | * @author 肖文杰 https://github.com/xwjie/ 27 | */ 28 | @Service 29 | @Slf4j 30 | public class FavoriteService { 31 | 32 | @Autowired 33 | FavoriteDao dao; 34 | 35 | public Collection getAll(int type) { 36 | // 校验通过后打印重要的日志 37 | log.info("getAll start ..."); 38 | 39 | List data = dao.findAllByObjType(type); 40 | 41 | log.info("getAll end, data size:" + data.size()); 42 | 43 | return data; 44 | } 45 | 46 | /** 47 | * 增加配置,需要管理员权限 48 | * 49 | * @param favorite 50 | * @return 51 | */ 52 | public long add(Favorite favorite) { 53 | // 参数校验 54 | notNull(favorite); 55 | 56 | check(favorite.getObjType() > 0); 57 | check(favorite.getObjId() > 0L); 58 | 59 | // 校验通过后打印重要的日志 60 | log.info("add favorite:" + favorite); 61 | 62 | long userId = UserUtil.getUserId(); 63 | 64 | // 校验重复 65 | Favorite favoriteNew = dao.findByUserIdAndObjTypeAndObjId(userId, favorite.getObjType(), favorite.getObjId()); 66 | 67 | // 如果没有记录,就新增 68 | if (favoriteNew == null) { 69 | // 设置用户id 70 | favorite.setUserId(userId); 71 | 72 | favoriteNew = dao.save(favorite); 73 | 74 | // 修改操作需要打印操作结果 75 | log.info("add favorite success, id:" + favoriteNew.getId()); 76 | } 77 | 78 | return favoriteNew.getId(); 79 | } 80 | 81 | /** 82 | * 根据id删除配置项 83 | *

84 | * 管理员或者自己创建的才可以删除掉 85 | * 86 | * @param id 87 | * @return 88 | */ 89 | public boolean delete(long id) { 90 | Favorite favorite = dao.findOne(id); 91 | 92 | // 参数校验 93 | check(favorite != null, "id.error", id); 94 | 95 | // 判断是否可以删除 96 | check(canDelete(favorite), "no.permission"); 97 | 98 | dao.delete(id); 99 | 100 | // 修改操作需要打印操作结果 101 | log.info("delete favorite success, id:" + id); 102 | 103 | return true; 104 | } 105 | 106 | /** 107 | * 自己创建的或者管理员可以删除数据 108 | * 判断逻辑变化可能性大,抽取一个函数 109 | * 110 | * @param favorite 111 | * @return 112 | */ 113 | private boolean canDelete(Favorite favorite) { 114 | return UserUtil.getUserId() == favorite.getUserId() || UserUtil.isAdmin(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /JavaSource/blog/src/main/java/cn/xiaowenjie/services/BlogService.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.services; 2 | 3 | import cn.xiaowenjie.beans.Blog; 4 | import cn.xiaowenjie.common.beans.PageResp; 5 | import cn.xiaowenjie.common.consts.Roles; 6 | import cn.xiaowenjie.common.utils.UserUtil; 7 | import cn.xiaowenjie.daos.BlogDao; 8 | import com.google.common.collect.Lists; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.apache.shiro.authz.annotation.RequiresRoles; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.data.domain.Pageable; 13 | import org.springframework.stereotype.Service; 14 | import org.springframework.util.StringUtils; 15 | 16 | import java.util.Collection; 17 | import java.util.List; 18 | 19 | import static cn.xiaowenjie.common.utils.CheckUtil.*; 20 | 21 | /** 22 | * 配置业务处理类 23 | * 24 | * @author 肖文杰 https://github.com/xwjie/ 25 | */ 26 | @Service 27 | @Slf4j 28 | public class BlogService { 29 | 30 | @Autowired 31 | BlogDao dao; 32 | 33 | public Collection getAll() { 34 | // 校验通过后打印重要的日志 35 | log.info("getAll start ..."); 36 | 37 | List data = Lists.newArrayList(dao.findAll()); 38 | 39 | log.info("getAll end, data size:" + data.size()); 40 | 41 | return data; 42 | } 43 | 44 | /** 45 | * 增加配置,需要管理员权限 46 | * @param blog 47 | * @return 48 | */ 49 | @RequiresRoles(Roles.ADMIN) 50 | public long add(Blog blog) { 51 | // 参数校验 52 | notNull(blog, "param.is.null"); 53 | notEmpty(blog.getTitle(), "name.is.null"); 54 | notEmpty(blog.getBody(), "value.is.null"); 55 | 56 | // 校验通过后打印重要的日志 57 | log.info("add blog:" + blog); 58 | 59 | // 校验重复 60 | check(null == dao.findByTitle(blog.getTitle()), "name.repeat"); 61 | 62 | blog = dao.save(blog); 63 | 64 | // 修改操作需要打印操作结果 65 | log.info("add blog success, id:" + blog.getId()); 66 | 67 | return blog.getId(); 68 | } 69 | 70 | /** 71 | * 根据id删除配置项 72 | * 73 | * 管理员或者自己创建的才可以删除掉 74 | * @param id 75 | * @return 76 | */ 77 | @RequiresRoles(Roles.ADMIN) 78 | public boolean delete(long id) { 79 | 80 | Blog blog = dao.findOne(id); 81 | 82 | // 参数校验 83 | check(blog != null, "id.error", id); 84 | 85 | // 判断是否可以删除 86 | check(canDelete(blog), "no.permission"); 87 | 88 | dao.delete(id); 89 | 90 | // 修改操作需要打印操作结果 91 | log.info("delete blog success, id:" + id); 92 | 93 | return true; 94 | } 95 | 96 | /** 97 | * 自己创建的或者管理员可以删除数据 98 | * 判断逻辑变化可能性大,抽取一个函数 99 | * 100 | * @param blog 101 | * @return 102 | */ 103 | private boolean canDelete(Blog blog) { 104 | return UserUtil.getUser().equals(blog.getCreator()) || UserUtil.isAdmin(); 105 | } 106 | 107 | /** 108 | * 分页查找 109 | * 110 | * @param pageable 111 | * @param keyword 112 | * @return 113 | */ 114 | public PageResp listPage(Pageable pageable, String keyword) { 115 | if (StringUtils.isEmpty(keyword)) { 116 | return new PageResp(dao.findAll(pageable)); 117 | } else { 118 | // 也可以用springjpa 的 Specification 来实现查找 119 | return new PageResp<>(dao.findAllByKeyword(keyword, pageable)); 120 | } 121 | } 122 | 123 | public Blog getByTitle(String title) { 124 | return dao.findByTitle(title); 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /WebProject/src/commons/SelectUser.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 81 | 82 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/services/ConfigService.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.services; 2 | 3 | import static cn.xiaowenjie.common.utils.CheckUtil.check; 4 | import static cn.xiaowenjie.common.utils.CheckUtil.notEmpty; 5 | import static cn.xiaowenjie.common.utils.CheckUtil.notNull; 6 | 7 | import java.util.Collection; 8 | import java.util.List; 9 | 10 | import cn.xiaowenjie.common.consts.Roles; 11 | import cn.xiaowenjie.common.utils.UserUtil; 12 | import lombok.extern.slf4j.Slf4j; 13 | import org.apache.shiro.authz.annotation.RequiresRoles; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | import org.springframework.beans.factory.annotation.Autowired; 17 | import org.springframework.data.domain.Pageable; 18 | import org.springframework.stereotype.Service; 19 | import org.springframework.util.StringUtils; 20 | 21 | import com.google.common.collect.Lists; 22 | 23 | import cn.xiaowenjie.beans.Config; 24 | import cn.xiaowenjie.common.beans.PageResp; 25 | import cn.xiaowenjie.daos.ConfigDao; 26 | 27 | /** 28 | * 配置业务处理类 29 | * 30 | * @author 肖文杰 https://github.com/xwjie/ 31 | */ 32 | @Service 33 | @Slf4j 34 | public class ConfigService { 35 | 36 | @Autowired 37 | ConfigDao dao; 38 | 39 | public Collection getAll() { 40 | // 校验通过后打印重要的日志 41 | log.info("getAll start ..."); 42 | 43 | List data = Lists.newArrayList(dao.findAll()); 44 | 45 | log.info("getAll end, data size:" + data.size()); 46 | 47 | return data; 48 | } 49 | 50 | /** 51 | * 增加配置,需要管理员权限 52 | * @param config 53 | * @return 54 | */ 55 | @RequiresRoles(Roles.ADMIN) 56 | public long add(Config config) { 57 | // 参数校验 58 | notNull(config, "param.is.null"); 59 | notEmpty(config.getName(), "name.is.null"); 60 | notEmpty(config.getValue(), "value.is.null"); 61 | 62 | // 校验通过后打印重要的日志 63 | log.info("add config:" + config); 64 | 65 | // 校验重复 66 | check(null == dao.findByName(config.getName()), "name.repeat"); 67 | 68 | config = dao.save(config); 69 | 70 | // 修改操作需要打印操作结果 71 | log.info("add config success, id:" + config.getId()); 72 | 73 | return config.getId(); 74 | } 75 | 76 | /** 77 | * 根据id删除配置项 78 | * 79 | * 管理员或者自己创建的才可以删除掉 80 | * @param id 81 | * @return 82 | */ 83 | @RequiresRoles(Roles.ADMIN) 84 | public boolean delete(long id) { 85 | 86 | Config config = dao.findOne(id); 87 | 88 | // 参数校验 89 | check(config != null, "id.error", id); 90 | 91 | // 判断是否可以删除 92 | check(canDelete(config), "no.permission"); 93 | 94 | dao.delete(id); 95 | 96 | // 修改操作需要打印操作结果 97 | log.info("delete config success, id:" + id); 98 | 99 | return true; 100 | } 101 | 102 | /** 103 | * 自己创建的或者管理员可以删除数据 104 | * 判断逻辑变化可能性大,抽取一个函数 105 | * 106 | * @param config 107 | * @return 108 | */ 109 | private boolean canDelete(Config config) { 110 | return UserUtil.getUser().equals(config.getCreator()) || UserUtil.isAdmin(); 111 | } 112 | 113 | /** 114 | * 分页查找 115 | * 116 | * @param pageable 117 | * @param keyword 118 | * @return 119 | */ 120 | public PageResp listPage(Pageable pageable, String keyword) { 121 | if (StringUtils.isEmpty(keyword)) { 122 | return new PageResp(dao.findAll(pageable)); 123 | } else { 124 | // 也可以用springjpa 的 Specification 来实现查找 125 | return new PageResp<>(dao.findAllByKeyword(keyword, pageable)); 126 | } 127 | } 128 | 129 | public Config getByName(String name) { 130 | return dao.findByName(name); 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /JavaSource/core/src/main/java/cn/xiaowenjie/CreateTestData.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie; 2 | 3 | import cn.xiaowenjie.beans.Config; 4 | import cn.xiaowenjie.common.consts.Roles; 5 | import cn.xiaowenjie.common.daos.RoleDao; 6 | import cn.xiaowenjie.common.daos.UserDao; 7 | import cn.xiaowenjie.common.rbac.Role; 8 | import cn.xiaowenjie.common.rbac.User; 9 | import cn.xiaowenjie.common.utils.UserUtil; 10 | import cn.xiaowenjie.services.ConfigService; 11 | import cn.xiaowenjie.services.UserService; 12 | import cn.xiaowenjie.tool.PasswordUtil; 13 | import com.google.common.collect.Lists; 14 | import lombok.extern.slf4j.Slf4j; 15 | import org.apache.shiro.mgt.SecurityManager; 16 | import org.apache.shiro.util.ThreadContext; 17 | import org.springframework.beans.factory.annotation.Autowired; 18 | import org.springframework.boot.CommandLineRunner; 19 | import org.springframework.stereotype.Component; 20 | 21 | /** 22 | * 增加测试数据 (上线时候需要删除掉) 23 | * 24 | * @author 晓风轻 25 | */ 26 | @Component 27 | @Slf4j 28 | public class CreateTestData implements CommandLineRunner { 29 | 30 | @Autowired 31 | ConfigService configService; 32 | 33 | @Autowired 34 | UserDao userDao; 35 | 36 | @Autowired 37 | UserService userSevice; 38 | 39 | @Autowired 40 | RoleDao roleDao; 41 | 42 | @Override 43 | public void run(String... args) throws Exception { 44 | // 用户不存在则创建测试数据 45 | if (userDao.findByName("xwjie") == null) { 46 | log.error("创建测试数据....."); 47 | 48 | createUsers(); 49 | 50 | // FIXME 51 | // createConfigs(); 52 | 53 | log.error("创建测试数据完毕"); 54 | } 55 | } 56 | 57 | public void createUsers() { 58 | log.error("---addUser---"); 59 | 60 | // role 61 | Role normaleRole = new Role(); 62 | 63 | normaleRole.setName(Roles.NORMAL_USER); 64 | normaleRole.setComment("普通用户"); 65 | 66 | normaleRole = roleDao.save(normaleRole); 67 | 68 | 69 | Role adminRole = new Role(); 70 | 71 | adminRole.setName(Roles.ADMIN); 72 | adminRole.setComment("管理员"); 73 | 74 | adminRole = roleDao.save(adminRole); 75 | 76 | // amdin 77 | User admin = new User(); 78 | 79 | admin.setName("xwjie"); 80 | admin.setNick("晓风轻"); 81 | 82 | // 盐和密码 83 | admin.setSalt("admin"); 84 | String password = PasswordUtil.renewPassword("123456", admin.getSalt()); 85 | 86 | // 计算后密码 87 | admin.setPassword(password); 88 | 89 | // 角色 90 | admin.setRoles(Lists.newArrayList(adminRole, normaleRole)); 91 | 92 | userDao.save(admin); 93 | 94 | for (int i = 1; i <= 10; i++) { 95 | User user = new User(); 96 | 97 | user.setName("user" + i); 98 | user.setNick("测试用户" + i); 99 | 100 | // 盐和密码 101 | user.setSalt(user.getName()); 102 | String password2 = PasswordUtil.renewPassword("123456", user.getSalt()); 103 | 104 | // 计算后密码 105 | user.setPassword(password2); 106 | 107 | user.setRoles(Lists.newArrayList(normaleRole)); 108 | 109 | userDao.save(user); 110 | } 111 | } 112 | 113 | @Autowired 114 | SecurityManager securityManager; 115 | 116 | public void createConfigs() { 117 | log.error("---addTestData---"); 118 | 119 | // 登陆 120 | userSevice.login("xwjie", "123456"); 121 | 122 | // 123 | ThreadContext.bind(securityManager); 124 | 125 | for (int i = 1; i <= 20; i++) { 126 | 127 | Config config = new Config(); 128 | 129 | config.setName("测试数据:" + i); 130 | System.out.println("测试数据:" + i); 131 | config.setValue("https://github.com/xwjie"); 132 | config.setDescription("晓风轻:" + i); 133 | 134 | // 创建记录的用户 135 | config.setCreator(UserUtil.getUser()); 136 | 137 | configService.add(config); 138 | } 139 | } 140 | } 141 | 142 | -------------------------------------------------------------------------------- /JavaSource/chart/src/main/java/cn/xiaowenjie/services/UploadFileService.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.services; 2 | 3 | import cn.xiaowenjie.beans.UploadRecord; 4 | import cn.xiaowenjie.chartbeans.EndData; 5 | import cn.xiaowenjie.common.beans.PageResp; 6 | import cn.xiaowenjie.daos.EndDataDao; 7 | import cn.xiaowenjie.daos.UploadRecordDao; 8 | import cn.xiaowenjie.tool.ConfigUtil; 9 | import cn.xiaowenjie.tool.ReadLog2CSV; 10 | import lombok.SneakyThrows; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.data.domain.Pageable; 14 | import org.springframework.stereotype.Service; 15 | import org.springframework.util.StringUtils; 16 | import org.springframework.web.multipart.MultipartFile; 17 | 18 | import java.io.File; 19 | import java.time.LocalDate; 20 | import java.time.format.DateTimeFormatter; 21 | import java.util.List; 22 | import java.util.concurrent.atomic.AtomicInteger; 23 | 24 | import static cn.xiaowenjie.common.utils.CheckUtil.check; 25 | 26 | /** 27 | * 上传日志 28 | */ 29 | @Service 30 | @Slf4j 31 | public class UploadFileService { 32 | 33 | @Autowired 34 | UploadRecordDao uploadRecordDao; 35 | 36 | @Autowired 37 | EndDataDao endDataDao; 38 | 39 | /** 40 | * 上传文件 41 | * @param f 42 | * @return 43 | */ 44 | @SneakyThrows 45 | public UploadRecord upload(MultipartFile f) { 46 | String filename = f.getOriginalFilename(); 47 | String name = filename.substring(0, filename.length() - 4); 48 | 49 | log.info("start upload..." + filename + "," + f.getSize()); 50 | 51 | // 后缀校验 52 | check(filename.toLowerCase().endsWith(".log"), "unsupport.file.format"); 53 | 54 | // 不能重名,防止重复提交 55 | check(uploadRecordDao.findByName(name) == null, "name.repeat"); 56 | 57 | // 保存的目录 58 | String saveDir = ConfigUtil.get("PATH.LOGS"); 59 | 60 | // 得到要保存的文件名 61 | String realName = generateFileName(saveDir, f); 62 | 63 | log.info("save to :" + saveDir + " , " + realName); 64 | 65 | // save file 66 | File newFile = new File(saveDir, realName); 67 | f.transferTo(newFile); 68 | 69 | // save record to db 70 | UploadRecord record = new UploadRecord(); 71 | 72 | record.setName(name); 73 | record.setRealPath(realName); 74 | record.setSize(f.getSize()); 75 | 76 | // 保存数据 77 | record = uploadRecordDao.save(record); 78 | 79 | // 分析文件 80 | int dataCount = parseLog(record.getId(), newFile); 81 | 82 | // 保存文件的记录数 83 | record.setDataCount(dataCount); 84 | record = uploadRecordDao.save(record); 85 | 86 | return record; 87 | } 88 | 89 | @SneakyThrows 90 | private int parseLog(long recordId, File newFile) { 91 | // 把csv文件读取为对象 92 | List data = ReadLog2CSV.deal(newFile); 93 | 94 | // 设置关联id 95 | data.forEach(d -> d.setRecordId(recordId)); 96 | 97 | // 保存到数据库 98 | endDataDao.save(data); 99 | 100 | return data.size(); 101 | } 102 | 103 | /** 104 | * 简单防止冲突(单机版) 105 | */ 106 | private final static AtomicInteger fileIndex = new AtomicInteger(100); 107 | 108 | private String generateFileName(String saveDir, MultipartFile f) { 109 | // FIXME 并发可能覆盖 110 | String newDir = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); 111 | 112 | // 自动创建目录 113 | new File(saveDir, newDir).mkdirs(); 114 | 115 | return String.format("%s//%d-%d%s", newDir, System.currentTimeMillis(), fileIndex.getAndIncrement(), getFileExt(f.getOriginalFilename())); 116 | } 117 | 118 | /** 119 | * 得到文件名后缀 120 | * 121 | * @param name 122 | * @return 123 | */ 124 | private String getFileExt(String name) { 125 | int index = name.lastIndexOf('.'); 126 | 127 | if (index != -1) { 128 | return name.substring(index); 129 | } 130 | 131 | return ""; 132 | } 133 | 134 | /** 135 | * 分页查找 136 | * 137 | * @param pageable 138 | * @param keyword 139 | * @return 140 | */ 141 | public PageResp listPage(Pageable pageable, String keyword) { 142 | if (StringUtils.isEmpty(keyword)) { 143 | return new PageResp(uploadRecordDao.findAllByOrderByCreateTimeDesc(pageable)); 144 | } else { 145 | // 也可以用springjpa 的 Specification 来实现查找 146 | return new PageResp(uploadRecordDao.findAllByKeyword(keyword, pageable)); 147 | } 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /WebProject/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | 3 | import VueRouter from 'vue-router'; 4 | import Routers from './router'; 5 | import Vuex from 'vuex'; 6 | import Util from './libs/util'; 7 | import App from './App.vue'; 8 | import store from './stores'; 9 | 10 | Vue.use(VueRouter); 11 | Vue.use(Vuex); 12 | 13 | // ---------------------------------------- element ui 14 | import ElementUI from 'element-ui' 15 | import 'element-ui/lib/theme-chalk/index.css' 16 | Vue.use(ElementUI) 17 | 18 | Vue.config.lang = 'zh-cn' 19 | 20 | // ---------------------------------------- charts 21 | import VCharts from 'v-charts'; 22 | Vue.use(VCharts) 23 | 24 | // ---------------------------------------- 提示信息工具类 25 | Vue.prototype.info = function (msg) { 26 | //this.$message(msg); 27 | this.$message({ 28 | type: "success", 29 | message: msg 30 | }); 31 | } 32 | 33 | Vue.prototype.error = function (msg) { 34 | this.$message({ type: 'error', message: msg }); 35 | } 36 | 37 | Vue.prototype.confirm = function () { 38 | return this.$confirm(...arguments); 39 | } 40 | 41 | // ---------------------------------------- 请求 42 | Vue.prototype.ajax = Util.ajax; 43 | 44 | // ---------------------------------------- 组件 45 | import { registerCommonComponents } from './commons'; 46 | registerCommonComponents(); 47 | 48 | import { registerComponents } from './components'; 49 | registerComponents(); 50 | 51 | // 定义全局filter 52 | Vue.filter('filterKeyword', function (value, key) { 53 | if (!key) return value; 54 | return value.filter(e => Util.isMatch(e, key)); 55 | }); 56 | 57 | // ---------------------------------------- event bus 58 | import VueBus from 'vue-bus'; 59 | 60 | import './plugins/element.js' 61 | Vue.use(VueBus); 62 | 63 | 64 | // ---------------------------------------- 自动设置语言 65 | function switchLanguage(value){ 66 | var lang = value; 67 | 68 | if(lang){ 69 | setCookie('lang', value); 70 | } 71 | else{ 72 | const navLang = navigator.language; 73 | const localLang = (navLang === 'zh-CN' || navLang === 'en-US') ? navLang : false; 74 | //const lang = window.localStorage.getItem('language') || localLang || 'zh-CN'; 75 | lang = getCookie('lang') || localLang || 'zh-CN'; 76 | } 77 | 78 | Vue.config.lang = lang; 79 | 80 | console.log("language", lang); 81 | } 82 | 83 | switchLanguage(); 84 | 85 | // ---------------------------------------- 路由配置 86 | const RouterConfig = { 87 | mode: 'history', 88 | routes: Routers 89 | }; 90 | const router = new VueRouter(RouterConfig); 91 | 92 | let loading; 93 | 94 | router.beforeEach((to, from, next) => { 95 | loading = ElementUI.Loading.service({ fullscreen: true }); 96 | Util.title(to.meta.title); 97 | next(); 98 | }); 99 | 100 | router.afterEach(() => { 101 | loading.close(); 102 | window.scrollTo(0, 0); 103 | }); 104 | 105 | /* 106 | const store = new Vuex.Store({ 107 | state: { 108 | 109 | }, 110 | getters: { 111 | 112 | }, 113 | mutations: { 114 | 115 | }, 116 | actions: { 117 | 118 | } 119 | }); 120 | */ 121 | 122 | 123 | var instance = new Vue({ 124 | el: '#app', 125 | router: router, 126 | store: store, 127 | render: h => h(App) 128 | }); 129 | 130 | 131 | instance.$bus.on("lang-change", switchLanguage); 132 | 133 | // ---------------------------------------- 工具类 134 | 135 | // 对Date的扩展,将 Date 转化为指定格式的String 136 | // 月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符, 137 | // 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字) 138 | // 例子: 139 | // (new Date()).Format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423 140 | // (new Date()).Format("yyyy-M-d h:m:s.S") ==> 2006-7-2 8:9:4.18 141 | Date.prototype.format = function (fmt) { //author: meizz 142 | var o = { 143 | "M+": this.getMonth() + 1, //月份 144 | "d+": this.getDate(), //日 145 | "h+": this.getHours(), //小时 146 | "m+": this.getMinutes(), //分 147 | "s+": this.getSeconds(), //秒 148 | "q+": Math.floor((this.getMonth() + 3) / 3), //季度 149 | "S": this.getMilliseconds() //毫秒 150 | }; 151 | if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); 152 | for (var k in o) 153 | if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); 154 | return fmt; 155 | } 156 | 157 | function getCookie(name) { 158 | var arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)"); 159 | if (arr = document.cookie.match(reg)) 160 | return (arr[2]); 161 | else 162 | return null; 163 | } 164 | 165 | function setCookie(name, value) { 166 | document.cookie = name + '=' + value; 167 | } -------------------------------------------------------------------------------- /docs/note/use-redis-cache.md: -------------------------------------------------------------------------------- 1 | # 使用redis 2 | 3 | ## 为什么使用redis 4 | 性能有保证,redis支持集群等,数据也有保证。而且后面使用 `spring-session` 做分布式Session的时候,也是使用redis做持久化。所以一次到位直接使用redis。 5 | 6 | 7 | ## 增加redis依赖 8 | 9 | ```xml 10 | 11 | org.springframework.boot 12 | spring-boot-starter-data-redis 13 | 14 | ``` 15 | 16 | ## 增加redis配置 17 | 18 | `application.yml` 上增加: 19 | 20 | ``` 21 | spring: 22 | redis: 23 | host: localhost 24 | port: 6379 25 | ``` 26 | 27 | ## 配置redis 28 | 29 | 继承CachingConfigurerSupport,增加 `@EnableCaching` 注解,需要重写 `keyGenerator` 方法。 30 | 31 | ```java 32 | @Configuration 33 | @EnableCaching 34 | public class RedisConfig extends CachingConfigurerSupport 35 | ``` 36 | 37 | 在类里面配置 `RestTemplate` ,需要配置key和value的序列化类。 38 | 39 | key序列化使用`StringRedisSerializer`, 不配置的话key前面会出现乱码。 40 | 41 | value序列化使用 `GenericJackson2JsonRedisSerializer` ,保存为Json格式。该类目前反序列化还有一些bug,只能反序列化实现了Serialize的类。 42 | 43 | 44 | ```java 45 | @Bean 46 | public RedisTemplate redisTemplate(RedisConnectionFactory factory) { 47 | RedisTemplate template = new RedisTemplate(); 48 | 49 | template.setConnectionFactory(factory); 50 | 51 | RedisSerializer keySerializer = new StringRedisSerializer(); 52 | 53 | // 设置key序列化类,否则key前面会多了一些乱码 54 | template.setKeySerializer(keySerializer); 55 | template.setHashKeySerializer(keySerializer); 56 | 57 | // FIXME 有些版本有bug,没有序列化的只能序列化,但无法反序列化 58 | GenericJackson2JsonRedisSerializer jsonSerializer = new GenericJackson2JsonRedisSerializer(); 59 | 60 | // 设置内容序列化类 61 | template.setValueSerializer(jsonSerializer); 62 | 63 | template.afterPropertiesSet(); 64 | 65 | return template; 66 | } 67 | ``` 68 | 69 | 70 | 配置 CacheManager,包括指定缓存和默认缓存的超时时间的配置。 71 | 72 | ```java 73 | @Bean 74 | public CacheManager cacheManager(RedisTemplate redisTemplate) { 75 | RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate); 76 | 77 | Map expires = new HashMap<>(); 78 | 79 | expires.put(CacheNames.CONFIG, 60L); 80 | 81 | // 设置超时 82 | cacheManager.setExpires(expires); 83 | 84 | // 没有设置的缓存默认过期时间 85 | cacheManager.setDefaultExpiration(60 * 60); 86 | 87 | return cacheManager; 88 | } 89 | ``` 90 | 91 | 92 | 重写 `keyGenerator`,可以支持使用@Cacheable不指定Key。 93 | 94 | ```java 95 | @Override 96 | public KeyGenerator keyGenerator() { 97 | return new KeyGenerator() { 98 | @Override 99 | public Object generate(Object target, Method method, Object... params) { 100 | StringBuilder sb = new StringBuilder(); 101 | 102 | sb.append(target.getClass().getSimpleName()); 103 | sb.append('.').append(method.getName()); 104 | 105 | // FIXME 参数太长的时候请指定key属性,否则key太长 106 | for (Object obj : params) { 107 | if (obj != null) { 108 | sb.append(obj.toString()); 109 | } 110 | } 111 | 112 | return sb.toString(); 113 | } 114 | }; 115 | } 116 | ``` 117 | 118 | ## 使用缓存 119 | 120 | 保存的时候使用 `@Cacheable`,清空使用 `@CacheEvict` ,更新的时候使用 `@CachePut` 。 121 | 122 | 反序列化有bug,没有实现 `Serializable` 的只能序列化,无法反序列化。可能后续版本会解决该问题。 123 | 124 | 所以把下面的查询代码修改一下,用 实现了 `Serializable` 的 `ArrayList` 包装返回。 125 | 126 | ```java 127 | @Cacheable("config") 128 | @Override 129 | public Collection getAll() { 130 | System.out.println("\n----------GetAll----------\n"); 131 | return new ArrayList<>(configs.values()); 132 | } 133 | 134 | @CacheEvict(value = CacheNames.CONFIG, allEntries = true) 135 | @Override 136 | public long add(Config config) { 137 | 138 | } 139 | 140 | /** 141 | * 删除配置项 142 | */ 143 | @CacheEvict(value = CacheNames.CONFIG, allEntries = true) 144 | @Override 145 | public boolean delete(long id) { 146 | 147 | } 148 | ``` 149 | 150 | ## 定时清空缓存 151 | 152 | 也可以定时清空cache,使用 `@EnableScheduling` 和 `@Scheduled` 注解。 153 | 154 | ```java 155 | @Component 156 | @EnableScheduling 157 | public class ClearCacheTask { 158 | 159 | /** 160 | * 定时清空缓存 161 | */ 162 | @Scheduled(fixedRate = 60 * 1000L) 163 | @CacheEvict(value = { CacheNames.CONFIG }, allEntries = true) 164 | public void clearCaches() { 165 | System.out.println("\n------------ clear caches ------------\n"); 166 | } 167 | } 168 | ``` 169 | 170 | ## redis 怎么样保存cache 171 | 172 | 增加2条数据,一个是类型为 `zset` 的 `缓存名~keys` , 里面存放了该缓存所有的key, 一个是对应的key,值为序列化后的json。 173 | 174 | `zset` 是带权重的有序集合,可以使用 `zrange config~keys -1 1 withscores` 查看元素,新加入的都是 0.0 。使用 ` zcount config~keys -1 1` 查看个数。 175 | 176 | 可以使用 `ttl` 命令查看超时时间,单位为秒。 177 | 178 | ![redis console](/pictures/redisconsole.png) 179 | 180 | 181 | ## 安装redis 182 | 183 | https://github.com/MicrosoftArchive/redis/releases 下载最新版本,3.2 184 | 185 | 启动 186 | ``` 187 | redis-server.exe redis.windows.conf 188 | ``` 189 | 190 | 也可以使用 [Redis Client](https://github.com/caoxinyu/RedisClient) 查看。 191 | 192 | ![redis client](/pictures/redisclient.png) 193 | 194 | ## redis 比较重要命令 195 | * keys * / keys cn* 查看数据 196 | * type keyname 查看数据类型 197 | * dbsize 查看记录数 198 | * flushdb 删除【当前数据库】所有记录 199 | * flushall 删除所有数据库里面的所有数据,注意不是当前数据库,而是所有数据库。 200 | * expire,ttl 设置和查看超时时间 201 | * select 选择0-15号数据库 -------------------------------------------------------------------------------- /WebProject/src/components/table/UserTable.vue: -------------------------------------------------------------------------------- 1 | /** 2 | * 表格组件范例 3 | * 4 | * @author xiaowenjie https://github.com/xwjie 5 | */ 6 | 90 | 91 | 168 | 169 | 172 | -------------------------------------------------------------------------------- /docs/note/spring-jpa-data-use-h2-database.md: -------------------------------------------------------------------------------- 1 | # 使用H2数据库 2 | 3 | ## 加入依赖 4 | 5 | ```xml 6 | 7 | com.h2database 8 | h2 9 | 10 | 11 | org.springframework.boot 12 | spring-boot-starter-data-jpa 13 | 14 | 15 | ``` 16 | 17 | ## 定义实体类 18 | 19 | ```java 20 | import java.io.Serializable; 21 | 22 | import javax.persistence.Entity; 23 | import javax.persistence.GeneratedValue; 24 | import javax.persistence.Id; 25 | 26 | import lombok.Data; 27 | 28 | @Entity 29 | @Data 30 | public class Config implements Serializable { 31 | 32 | private static final long serialVersionUID = 1L; 33 | 34 | private String name, description, value; 35 | 36 | @Id 37 | @GeneratedValue 38 | private long id; 39 | 40 | /** 41 | * 创建者 42 | */ 43 | private long creator; 44 | } 45 | ``` 46 | 47 | ## 定义Dao( Repository ) 48 | 49 | 可以使用 `PagingAndSortingRepository` 或者 `CrudRepository`。 50 | 51 | 增加了一个根据名称查找的方法。 52 | 53 | ```java 54 | import org.springframework.data.repository.PagingAndSortingRepository; 55 | 56 | import cn.xiaowenjie.beans.Config; 57 | 58 | public interface ConfigDao extends PagingAndSortingRepository { 59 | Config findByName(String name); 60 | } 61 | ``` 62 | 63 | ## 配置数据库 64 | 65 | 配置使用h2数据库,内存型的时候url为 `jdbc:h2:mem:mydb` 。 文件类型为:`jdbc:h2:file:~/mydb.h2` 66 | 67 | ``` 68 | spring: 69 | profiles: 70 | active: dev 71 | redis: 72 | host: localhost 73 | port: 6379 74 | datasource: 75 | url: jdbc:h2:file:~/mydb.h2 76 | #url: jdbc:h2:mem:mydb 77 | username: sa 78 | password: sa 79 | driverClassName: org.h2.Driver 80 | jpa: 81 | database: h2 82 | show-sql: true 83 | hibernate: 84 | ddl-auto: update 85 | h2: 86 | console: 87 | enabled: true 88 | path: /h2-console 89 | settings: 90 | web-allow-others: true 91 | trace: true 92 | ``` 93 | 94 | ## 引入guava, 把查询结果转为list 95 | 96 | 使用jpa查询结果中,返回的为 iterable,需要转换为list返回给前台。 97 | 98 | ```xml 99 | 100 | com.google.guava 101 | guava 102 | 16.0.1 103 | 104 | ``` 105 | 106 | ```java 107 | import com.google.common.collect.Lists; 108 | 109 | public Collection getAll() { 110 | // 校验通过后打印重要的日志 111 | logger.info("getAll start ..."); 112 | 113 | List data = Lists.newArrayList(dao.findAll()); 114 | 115 | logger.info("getAll end, data size:" + data.size()); 116 | 117 | return data; 118 | } 119 | ``` 120 | 121 | ## 使用h2的web console 122 | 123 | 访问 http://127.0.0.1:8080/h2-console/ 124 | 125 | 1. JDBC URL: jdbc:h2:~/mydb.h2 126 | 2. 用户名:sa 127 | 3. 密码:sa 128 | 129 | ![](./pictures/h2-1.png) 130 | 131 | 可以选择中文界面,赞,填写好url,链接进去: 132 | 133 | 134 | ![](./pictures/h2-2.png) 135 | 136 | 使用文件类型后,会在用户的目录下创建数据库文件,trace文件里面是文本,为数据库错误的一些日志堆栈。 137 | 138 | 139 | ![](./pictures/h2-3.png) 140 | 141 | >注意:上线稳定的时候注意把控制台关掉。 142 | 143 | ## 使用jpa 144 | 145 | 直接注入 Repository `ConfigDao` ,具体参考工程代码 `ConfigService`。 146 | 147 | ```java 148 | package cn.xiaowenjie.services; 149 | 150 | import static cn.xiaowenjie.common.utils.CheckUtil.check; 151 | import static cn.xiaowenjie.common.utils.CheckUtil.notEmpty; 152 | import static cn.xiaowenjie.common.utils.CheckUtil.notNull; 153 | 154 | import java.util.Collection; 155 | import java.util.List; 156 | 157 | import org.slf4j.Logger; 158 | import org.slf4j.LoggerFactory; 159 | import org.springframework.beans.factory.annotation.Autowired; 160 | import org.springframework.stereotype.Service; 161 | 162 | import com.google.common.collect.Lists; 163 | 164 | import cn.xiaowenjie.beans.Config; 165 | import cn.xiaowenjie.daos.ConfigDao; 166 | 167 | /** 168 | * 配置业务处理类 169 | * 170 | * @author 晓风轻 https://github.com/xwjie/ 171 | * 172 | */ 173 | @Service 174 | public class ConfigService { 175 | 176 | private static final Logger logger = LoggerFactory.getLogger(ConfigService.class); 177 | 178 | @Autowired 179 | ConfigDao dao; 180 | 181 | public Collection getAll() { 182 | // 校验通过后打印重要的日志 183 | logger.info("getAll start ..."); 184 | 185 | List data = Lists.newArrayList(dao.findAll()); 186 | 187 | logger.info("getAll end, data size:" + data.size()); 188 | 189 | return data; 190 | } 191 | 192 | public long add(Config config) { 193 | // 参数校验 194 | notNull(config, "param.is.null"); 195 | notEmpty(config.getName(), "name.is.null"); 196 | notEmpty(config.getValue(), "value.is.null"); 197 | 198 | // 校验通过后打印重要的日志 199 | logger.info("add config:" + config); 200 | 201 | // 校验重复 202 | check(null == dao.findByName(config.getName()), "name.repeat"); 203 | 204 | config = dao.save(config); 205 | 206 | // 修改操作需要打印操作结果 207 | logger.info("add config success, id:" + config.getId()); 208 | 209 | return config.getId(); 210 | } 211 | 212 | public boolean delete(long id) { 213 | // 参数校验 214 | check(id > 0L, "id.error", id); 215 | 216 | dao.delete(id); 217 | 218 | // 修改操作需要打印操作结果 219 | logger.info("delete config success, id:" + id); 220 | 221 | return true; 222 | } 223 | } 224 | 225 | ``` 226 | -------------------------------------------------------------------------------- /JavaSource/main/src/test/java/cn/xiaowenjie/SpringbootCodeTemplate/ConfigTest.java: -------------------------------------------------------------------------------- 1 | package cn.xiaowenjie.SpringbootCodeTemplate; 2 | 3 | import static org.junit.Assert.assertNotNull; 4 | import static org.junit.Assert.assertTrue; 5 | 6 | import java.util.Collection; 7 | 8 | import org.junit.Before; 9 | import org.junit.FixMethodOrder; 10 | import org.junit.Rule; 11 | import org.junit.Test; 12 | import org.junit.rules.ExpectedException; 13 | import org.junit.runner.RunWith; 14 | import org.junit.runners.MethodSorters; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.boot.test.context.SpringBootTest; 17 | import org.springframework.test.context.junit4.SpringRunner; 18 | 19 | import cn.xiaowenjie.SpringbootCodeTemplateApplication; 20 | import cn.xiaowenjie.beans.Config; 21 | import cn.xiaowenjie.common.rbac.User; 22 | import cn.xiaowenjie.common.consts.Roles; 23 | import cn.xiaowenjie.common.exceptions.CheckException; 24 | import cn.xiaowenjie.common.utils.UserUtil; 25 | import cn.xiaowenjie.common.daos.UserDao; 26 | import cn.xiaowenjie.services.ConfigService; 27 | 28 | @RunWith(SpringRunner.class) 29 | @SpringBootTest(classes = SpringbootCodeTemplateApplication.class) 30 | @FixMethodOrder(MethodSorters.NAME_ASCENDING) 31 | public class ConfigTest { 32 | 33 | @Autowired 34 | ConfigService configService; 35 | 36 | @Autowired 37 | UserDao userDao; 38 | 39 | @Rule 40 | public ExpectedException thrown = ExpectedException.none(); 41 | 42 | private static long randomNum = 0; 43 | private static String userName = null; 44 | 45 | @Before 46 | public void random() { 47 | // 整个方法会执行多次 48 | if (randomNum == 0) { 49 | randomNum = System.currentTimeMillis(); 50 | userName = "junituser" + randomNum; 51 | } 52 | } 53 | 54 | @Test 55 | public void test1_addUser() { 56 | System.out.println("\n\n---addUser: " + userName); 57 | 58 | // 新增用户 59 | { 60 | User user = new User(); 61 | 62 | user.setName(userName); 63 | user.setNick("测试用户"); 64 | 65 | // user.setRole(Roles.USER); 66 | 67 | user = userDao.save(user); 68 | 69 | assertTrue(user.getId() > 0L); 70 | } 71 | 72 | // 测试新增数据是否成功 73 | { 74 | User user = userDao.findByName(userName); 75 | assertNotNull(user); 76 | } 77 | 78 | } 79 | 80 | @Test 81 | public void test2_addConfig() { 82 | System.out.println("\n\n---addConfig---\n\n"); 83 | 84 | // 设置当前登陆用户 85 | User user = userDao.findByName(userName); 86 | UserUtil.setUser(user); 87 | 88 | // 创建config数据 89 | { 90 | Config config = new Config(); 91 | 92 | config.setName("测试数据:" + randomNum); 93 | System.out.println("测试数据:" + randomNum); 94 | config.setValue("https://github.com/xwjie"); 95 | config.setDescription("晓风轻:" + randomNum); 96 | 97 | long newId = configService.add(config); 98 | assertTrue(newId > 0L); 99 | } 100 | } 101 | 102 | @Test 103 | public void test3_getAllConfig() { 104 | System.out.println("\n\n---testGetConfig---\n\n"); 105 | 106 | Collection all = configService.getAll(); 107 | 108 | // 有数据 109 | assertTrue(all.size() > 0); 110 | } 111 | 112 | @Test 113 | public void test4_addConfigException() { 114 | System.out.println("\n\n---addTestData---\n\n"); 115 | 116 | User user = userDao.findByName(userName); 117 | 118 | // 设置当前登陆用户 119 | UserUtil.setUser(user); 120 | 121 | // 创建config数据 122 | { 123 | System.out.println("\n\n--测试[参数为空]---\n\n"); 124 | 125 | thrown.expect(CheckException.class); 126 | thrown.expectMessage("参数为空"); 127 | 128 | configService.add(null); 129 | } 130 | 131 | } 132 | 133 | @Test 134 | public void test5_addConfigException() { 135 | System.out.println("\n\n---addTestData---\n\n"); 136 | 137 | User user = userDao.findByName(userName); 138 | 139 | // 设置当前登陆用户 140 | UserUtil.setUser(user); 141 | 142 | // 创建config数据 143 | { 144 | System.out.println("\n\n--测试[取值为空]---\n\n"); 145 | 146 | thrown.expect(CheckException.class); 147 | thrown.expectMessage("取值为空"); 148 | 149 | Config config = new Config(); 150 | 151 | config.setName("测试数据:" + randomNum); 152 | config.setValue(null); 153 | 154 | configService.add(config); 155 | } 156 | } 157 | 158 | @Test 159 | public void test6_addConfigException() { 160 | System.out.println("\n\n---addTestData---\n\n"); 161 | 162 | User user = userDao.findByName(userName); 163 | 164 | // 设置当前登陆用户 165 | UserUtil.setUser(user); 166 | 167 | // 创建config数据 168 | { 169 | System.out.println("\n\n--测试[名称已经存在]---\n\n"); 170 | 171 | thrown.expect(CheckException.class); 172 | thrown.expectMessage("名称已经存在"); 173 | 174 | Config config = new Config(); 175 | 176 | config.setName("测试数据:" + randomNum); 177 | config.setValue("https://github.com/xwjie"); 178 | config.setDescription("晓风轻:" + randomNum); 179 | 180 | configService.add(config); 181 | } 182 | } 183 | 184 | @Test 185 | public void test7_deleteConfig() { 186 | System.out.println("\n\n---deleteConfig---\n\n"); 187 | 188 | User user = userDao.findByName(userName); 189 | 190 | // 设置当前登陆用户 191 | UserUtil.setUser(user); 192 | 193 | Config config = configService.getByName("测试数据:" + randomNum); 194 | assertTrue(config != null); 195 | assertTrue(configService.delete(config.getId())); 196 | } 197 | 198 | } 199 | -------------------------------------------------------------------------------- /WebProject/src/components/uploadfile/UploadHistory.vue: -------------------------------------------------------------------------------- 1 | /** 2 | * 表格组件范例 3 | * 4 | * @author xiaowenjie https://github.com/xwjie 5 | */ 6 | 66 | 67 | 178 | 179 | 202 | --------------------------------------------------------------------------------