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 |
2 |
3 |
9 | {{tag.nick}}
10 |
11 |
20 |
25 |
32 |
33 | {{ props.item.name }}
34 | {{ props.item.nick }}
35 |
36 |
37 |
38 | + New Tag
39 |
40 |
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 | 
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 | 
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 |
7 |
8 |
12 |
13 |
14 |
21 |
27 |
28 |
33 |
34 |
39 |
40 |
43 |
44 | {{item.name}}
47 |
48 |
49 |
55 |
56 |
62 |
63 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
87 |
88 |
89 |
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 | 
130 |
131 | 可以选择中文界面,赞,填写好url,链接进去:
132 |
133 |
134 | 
135 |
136 | 使用文件类型后,会在用户的目录下创建数据库文件,trace文件里面是文本,为数据库错误的一些日志堆栈。
137 |
138 |
139 | 
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 |
7 |
8 |
9 |
10 |
16 |
17 | 将文件拖到此处,或点击上传,只能上传log文件
18 |
19 |
23 |
24 |
25 |
34 |
38 |
39 |
45 |
46 |
49 |
50 | {{scope.row.dataCount}}
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | 当前文件:{{currentFileName}}
61 |
62 |
63 |
64 |
65 |
66 |
67 |
178 |
179 |
202 |
--------------------------------------------------------------------------------