Compare commits

...

296 Commits
1.0 ... master

Author SHA1 Message Date
13786834a0 Merge pull request 'fix:安全问题修复。新增书签耗时长问题修复' (#22) from dev into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #22
2024-07-01 20:30:39 +08:00
fanxb
e0dccb6fd2 fix:安全问题修复。新增书签耗时长问题修复 2024-06-22 19:30:25 +08:00
845b1b077e Merge pull request 'fix:修复bing获取报错问题' (#21) from dev into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #21
2024-05-23 22:08:22 +08:00
fanxb
bb62066b82 fix:修复bing获取报错问题 2024-05-23 22:07:45 +08:00
63f1c9a54e Merge pull request 'fix:修复name过长无法导入问题' (#20) from dev into master
All checks were successful
continuous-integration/drone Build is passing
Reviewed-on: #20
2023-12-02 18:41:11 +08:00
fanxb
c9156f12d1 fix:修复name过长无法导入问题 2023-12-02 18:40:43 +08:00
325401e198 Merge pull request 'dcos:文档更新' (#19) from dev into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #19
2023-12-01 02:36:05 -05:00
fanxb
5126e31867 dcos:文档更新 2023-12-01 15:34:54 +08:00
f7994d232d Merge pull request 'fix:修复展示问题' (#18) from dev into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #18
2023-12-01 02:28:52 -05:00
fanxb
f78349a1d2 fix:修复展示问题 2023-12-01 15:27:51 +08:00
44c1ca91fc Merge pull request 'dev' (#17) from dev into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: #17
2023-12-01 02:17:00 -05:00
fanxb
b0b608de5a docs:文档更新
Some checks failed
continuous-integration/drone/pr Build is failing
2023-12-01 14:58:42 +08:00
fanxb
523698a967 feat:首页图加载优化,增加OneNav导入支持 2023-12-01 13:18:44 +08:00
fanxb
405a2e05ed feat:首图下载下来 2023-11-29 23:42:33 +08:00
fanxb
6b59dfbf26 Merge branch 'master' into dev 2023-11-29 19:29:42 +08:00
5a8f805a15 更新 README.md
Some checks failed
continuous-integration/drone/push Build is failing
2023-11-25 00:50:04 -05:00
42a2829847 Merge pull request 'master' (#16) from master into dev
Reviewed-on: #16
2023-11-25 00:49:22 -05:00
dfb39e5ee8 fix:修复执行报错
Some checks failed
continuous-integration/drone/push Build is failing
2023-11-25 00:48:57 -05:00
fanxb
946c032ba5 Merge pull request 'dev' (#15) from dev into master
Reviewed-on: #15
2023-08-13 15:37:04 +08:00
fleyx
3a77e5af4d doc:文档更新 2023-08-13 15:36:02 +08:00
fleyx
1d282b1095 Merge remote-tracking branch 'origin/master' into dev 2023-08-13 15:19:33 +08:00
fanxb
bb7deda121 Merge pull request 'feat:支持搜索引擎自定义' (#14) from dev into master
Reviewed-on: #14
2023-08-13 15:19:18 +08:00
fleyx
0d000223a2 feat:支持搜索引擎自定义 2023-08-13 15:18:48 +08:00
fanxb
51eb22adaa Merge pull request 'dev' (#13) from dev into master
Reviewed-on: #13
2023-08-13 15:07:16 +08:00
fleyx
c56ca2809c feat:支持搜索引擎自定义 2023-08-13 15:05:40 +08:00
fleyx
383450ebe4 temp 2023-08-08 23:10:59 +08:00
fanxb
ebc1da0972 更新 '.drone.yml' 2023-04-01 05:41:48 -04:00
fanxb
9dc1fe47d9 Merge pull request 'dev' (#12) from dev into master
Reviewed-on: #12
2023-04-01 05:30:11 -04:00
fanxb
35d3420470 feat:放开github登录 2023-04-01 17:29:45 +08:00
fanxb
529b42a185 更新 '.drone.yml' 2023-03-12 01:32:33 -05:00
fanxb
58a8cefaf5 Merge branch 'dev' of ssh://gitea.fleyx.com:222/fanxb/bookmark into dev 2023-03-12 10:55:06 +08:00
fanxb
b9cef34a06 refactor:vue-cli upgrade to 5.0.8 2023-03-12 10:54:54 +08:00
fanxb
928e3fac48 fix:修复bookmark缺陷 2023-02-16 19:07:40 +08:00
fanxb
c9429557b3 Merge pull request 'fix:修复bookmark缺陷' (#11) from dev into master
Reviewed-on: #11
2023-02-16 19:01:59 +08:00
fanxb
4bed4fd34d docs:修改文档 2022-12-05 19:09:28 +08:00
fanxb
5d52e389d6 Merge pull request 'docs:修改文档' (#10) from dev into master
Reviewed-on: #10
2022-12-05 19:05:42 +08:00
fanxb
767c26d89b docs:文档修改 2022-12-04 20:52:58 +08:00
fanxb
83125ae55a Merge pull request 'dev' (#9) from dev into master
Reviewed-on: #9
2022-12-04 20:49:07 +08:00
fanxb
b5f715eda6 Merge pull request 'master' (#8) from master into dev
Reviewed-on: #8
2022-12-04 19:04:45 +08:00
fanxb
cc3298f3b4 fix:修改arm上编译报错 2022-12-04 18:34:44 +08:00
fanxb
2b69814296 更新 'README.md' 2022-11-25 23:19:01 +08:00
fanxb
2880b6282c Merge pull request 'fix:修复定时任务线程池不起作用' (#7) from dev into master
Reviewed-on: #7
2022-09-24 18:50:34 +08:00
fanxb
ff560ae790 fix:修复定时任务线程池不起作用 2022-09-24 18:50:18 +08:00
fanxb
c5a35ea54e Merge pull request 'fix:修复定时任务线程池不起作用' (#6) from dev into master
Reviewed-on: #6
2022-09-24 18:44:57 +08:00
fanxb
22c2c48eea Merge branch 'master' into dev 2022-09-24 18:44:50 +08:00
fanxb
580d18a500 fix:修复定时任务线程池不起作用 2022-09-24 18:45:20 +08:00
fanxb
5868aa4a98 Merge pull request 'dev' (#5) from dev into master
Reviewed-on: #5
2022-09-24 17:19:27 +08:00
fanxb
42cbbe5999 deploy:增加drone部署 2022-09-24 17:15:51 +08:00
fanxb
9dc21ed87a Merge pull request 'master' (#3) from master into dev
Reviewed-on: #3
2022-09-24 17:00:35 +08:00
fanxb
32e63ed1fb Merge pull request 'deploy:增加drone部署' (#2) from dev into master
Reviewed-on: #2
2022-09-24 17:00:20 +08:00
fanxb
35ee930661 Merge remote-tracking branch 'origin/master' 2022-09-24 16:59:07 +08:00
fanxb
19aeab2856 deploy:增加drone部署 2022-09-24 16:56:21 +08:00
FleyX
866ce22bb5
Merge pull request #38 from FleyX/dev
Dev
2022-09-22 19:50:45 +08:00
FleyX
84c2c213a9
Merge pull request #37 from FleyX/dependabot/maven/bookMarkService/business/bookmark/org.jsoup-jsoup-1.15.3
build(deps): bump jsoup from 1.14.3 to 1.15.3 in /bookMarkService/business/bookmark
2022-09-21 16:35:44 +08:00
dependabot[bot]
9cc1a7b871
build(deps): bump jsoup in /bookMarkService/business/bookmark
Bumps [jsoup](https://github.com/jhy/jsoup) from 1.14.3 to 1.15.3.
- [Release notes](https://github.com/jhy/jsoup/releases)
- [Changelog](https://github.com/jhy/jsoup/blob/master/CHANGES)
- [Commits](https://github.com/jhy/jsoup/compare/jsoup-1.14.3...jsoup-1.15.3)

---
updated-dependencies:
- dependency-name: org.jsoup:jsoup
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-01 23:51:05 +00:00
fanxb
5e12b5a8b1 fix:修复定时任务线程池不起作用 2022-07-18 20:25:24 +08:00
fanxb
8ab7d0c341 Merge branch 'dev' of fanxb/bookmark into master 2022-07-10 13:18:36 +08:00
fanxb
4b9adc3acd Merge branch 'dev' of ssh://git.fleyx.com:2200/fanxb/bookmark into dev 2022-07-10 13:16:47 +08:00
fanxb
a67ef95b25 feat:修改域名 2022-07-10 13:16:44 +08:00
fanxb
39e9f8221a Merge branch 'dev' of fanxb/bookmark into master 2022-07-08 10:27:54 +08:00
fanxb
40cdb1f19e fix:样式细节修复 2022-07-08 10:27:20 +08:00
fanxb
106dccb68f Merge branch 'dev' of fanxb/bookmark into master 2022-07-08 10:22:49 +08:00
fanxb
5aeae15228 feat:增加背景下载按钮 2022-07-08 10:15:52 +08:00
FleyX
837a4a7650
Merge pull request #35 from FleyX/dev
fix:修复fastjsonbug
2022-05-28 20:34:05 +08:00
fanxb
8c81842571 Merge branch 'dev' of fanxb/bookmark into master 2022-05-28 20:33:26 +08:00
fanxb
64d4504178 fix:修复fastjsonbug 2022-05-28 20:33:07 +08:00
FleyX
3072d757f0
Merge pull request #34 from FleyX/dev
Dev
2022-05-12 16:39:36 +08:00
FleyX
62c88d78fc Update README.md 2022-05-12 16:38:32 +08:00
FleyX
ff323e6eea
Update README.md 2022-05-12 16:34:44 +08:00
fanxb
e240905d58 Merge branch 'dev' of fanxb/bookmark into master 2022-05-12 16:32:43 +08:00
fanxb
f23e720fb9 fix:修改注释 2022-05-12 16:30:28 +08:00
fanxb
faffadeba9 Merge branch 'dev' of fanxb/bookmark into master 2022-05-11 17:15:11 +08:00
fanxb
b2353a0ea1 feat:细节优化 2022-05-11 17:13:50 +08:00
fanxb
d5e2b55c28 refactor:日志打印级别改为debug 2022-05-11 14:30:38 +08:00
FleyX
35910c34e1
Merge pull request #33 from FleyX/dev
Dev
2022-04-28 10:46:28 +08:00
fanxb
d19325aaad Merge branch 'dev' of fanxb/bookmark into master 2022-04-28 10:45:39 +08:00
fanxb
df5578f267 refactor:优化请求,避免白屏时间过长 2022-04-28 10:43:05 +08:00
fanxb
9a5a4cae52 Merge branch 'dev' of fanxb/bookmark into master 2022-04-23 14:27:18 +08:00
fanxb
9a689cac65 fix:修复host_icon表数据重复问题 2022-04-23 14:26:29 +08:00
fanxb
238cc21ffa Merge branch 'dev' of fanxb/bookmark into master 2022-04-19 18:10:21 +08:00
fanxb
b950c666cc feat:新增书签可选择文件夹保持 2022-04-19 18:09:51 +08:00
fanxb
828207f672 temp 2022-04-19 17:17:34 +08:00
fanxb
ce0028cc49 refactor:首页点击不打开新页面 2022-04-17 20:20:42 +08:00
fanxb
9e2c75c3ec Merge branch 'dev' of fanxb/bookmark into master 2022-04-17 18:41:43 +08:00
fanxb
d36967d852 fix:修复带端口号地址不显示问题 2022-04-17 18:40:33 +08:00
fanxb
9e3308d6ca Merge branch 'dev' of fanxb/bookmark into master 2022-04-17 18:02:54 +08:00
fanxb
85688595f8 feat:浏览器插件版本显示 2022-04-17 16:36:02 +08:00
FleyX
9a97129c79
Merge pull request #32 from FleyX/dev
Dev
2022-04-17 15:41:59 +08:00
fanxb
6444cd6ebb docs:修改文档 2022-04-17 15:41:16 +08:00
fanxb
310ae76e8c Merge branch 'dev' of fanxb/bookmark into master 2022-04-17 15:39:41 +08:00
fanxb
473da94244 fix:修复跳转问题 2022-04-17 15:39:03 +08:00
fanxb
5ed2aa1690 Merge branch 'dev' of fanxb/bookmark into master 2022-04-17 15:24:44 +08:00
fanxb
c43cf5d5ae deploy:修改部署脚本 2022-04-17 15:24:05 +08:00
fanxb
1087f5e48b feat:支持浏览器新标签页 2022-04-17 14:53:44 +08:00
fanxb
b74be9f961 feat:浏览器插件替换主页 2022-04-17 12:10:51 +08:00
fanxb
6029f9d9c0 fix:修复回车搜索报错 2022-04-17 12:02:15 +08:00
FleyX
2c10ec4831
Merge pull request #31 from FleyX/dev
Dev
2022-04-15 16:33:59 +08:00
fanxb
89ad88e359 docs:修改帮助文档 2022-04-15 16:30:49 +08:00
fanxb
a7decc0c76 Merge branch 'dev' of fanxb/bookmark into master 2022-04-15 15:58:52 +08:00
fanxb
cb5e30a7bb fix:修改插件压缩包 2022-04-15 15:57:25 +08:00
fanxb
e8a646dbe8 Merge branch 'dev' of fanxb/bookmark into master 2022-04-15 15:49:08 +08:00
fanxb
d44700a971 feat:浏览器插件新增书签功能实现 2022-04-15 15:45:19 +08:00
fanxb
50e1e0e951 temp 2022-04-14 17:10:39 +08:00
fanxb
90b1dbcf9f temp 2022-04-12 17:04:48 +08:00
fanxb
1ba7617165 temp 2022-04-11 17:42:00 +08:00
fanxb
57a6944ec5 temp 2022-04-10 21:43:25 +08:00
fanxb
d251734267 feat:新的浏览器拓展 2022-04-08 17:04:13 +08:00
FleyX
c128cba5f6
Merge pull request #30 from FleyX/dev
fix:修复背景图片填充不完全
2022-03-31 13:49:28 +08:00
fanxb
a6f1380e9c Merge branch 'dev' of fanxb/bookmark into master 2022-03-31 13:45:52 +08:00
fanxb
3d7243e276 fix:修复背景图片填充不完全 2022-03-31 13:44:14 +08:00
FleyX
bd17be5dd5
Merge pull request #29 from FleyX/dev
feat:首页背景更换为每日一图
2022-03-31 13:35:11 +08:00
fanxb
77086daeb9 Merge branch 'dev' of fanxb/bookmark into master 2022-03-31 13:33:03 +08:00
fanxb
553cec1338 feat:首页背景更换为每日一图 2022-03-31 13:31:20 +08:00
FleyX
f424187334
Merge pull request #28 from FleyX/dev
Dev
2022-03-30 17:09:21 +08:00
fanxb
8d639b6a8c Merge branch 'dev' of fanxb/bookmark into master 2022-03-30 16:57:23 +08:00
fanxb
91a78a8459 fix:修复弹窗问题 2022-03-30 16:44:43 +08:00
fanxb
14b3c6da2f Merge branch 'dev' of fanxb/bookmark into master 2022-03-30 14:21:06 +08:00
fanxb
f048496d73 fix:修复注册进入主页后显示未登录 2022-03-30 14:19:38 +08:00
fanxb
c467970e93 fix:修复首页跳转问题 2022-03-30 10:46:40 +08:00
fanxb
15ed9cab08 feat:增加使用教程地址 2022-03-29 23:27:04 +08:00
fanxb
7bb4c42cb8 fix:修复首页icon过大问题 2022-03-29 23:16:07 +08:00
fanxb
f936a6b71b Merge branch 'master' of https://github.com/FleyX/bookmark 2022-03-29 22:03:03 +08:00
fanxb
aefe882faa docs:修改文档 2022-03-29 21:55:24 +08:00
fanxb
bc4364ea53 refactor:icon使用同步获取 2022-03-29 21:55:01 +08:00
fanxb
5a2f3da51a fix:修复书签导入失败bug 2022-03-29 21:54:31 +08:00
fanxb
f536f4e0b2 feat:修改首页默认图片 2022-03-29 20:16:31 +08:00
fanxb
0a54e784db feat:首页完成 2022-03-29 17:16:29 +08:00
fanxb
ee22c4b77a temp 2022-03-28 17:01:45 +08:00
fanxb
62d3c43436 temp 2022-03-25 17:12:41 +08:00
fanxb
1affbf16ac temp 2022-03-24 17:03:13 +08:00
fanxb
974f8c18b4 refactor:实体类移动到po包下 2022-03-24 10:24:33 +08:00
fanxb
6e2948ab9c temp 2022-03-23 17:02:52 +08:00
fanxb
92614c7344 feat:主页搜索完成 2022-03-23 17:02:39 +08:00
fanxb
2e63b2706a temp 2022-03-22 17:03:22 +08:00
fanxb
530af3a5e3 feat:书签数据变更刷新方式修改 2022-03-22 10:40:05 +08:00
fanxb
b83612681f temp:首页布局 2022-03-21 22:08:26 +08:00
fanxb
40290e6ac8 temp 2022-03-21 17:06:52 +08:00
fanxb
ff87689671 feat:前端增加全局路由钩子 2022-03-20 22:19:37 +08:00
fanxb
022d24ae54 temp 2022-03-17 17:10:49 +08:00
fanxb
d707be00de temp 2022-03-16 16:58:25 +08:00
fanxb
c7b82047ae refactor:常量类重命名 2022-03-16 16:31:56 +08:00
fanxb
4603b11326 feat:使用java进行拼音转换,去掉node模块 2022-03-16 16:28:33 +08:00
fanxb
04a3aa987b fix:修复后端编译报错问题 2022-03-10 14:41:08 +08:00
fanxb
47d4698ac3 deploy:build.sh增加执行权限 2022-03-10 14:35:23 +08:00
fanxb
0ce47b9a81 deploy:修改部署脚本 2022-03-10 14:30:01 +08:00
fanxb
1c7294e855 feat:增加页面统计 2022-03-10 14:18:06 +08:00
fanxb
05b6cb29d8 deploy:修改springboot版本 2022-02-08 17:02:07 +08:00
fanxb
fb9cc32322 refactor:修改版本检查时间为5分钟一次 2022-02-08 15:42:44 +08:00
fanxb
8ef2d9923b Merge remote-tracking branch 'origin/master' 2022-01-18 16:34:35 +08:00
fanxb
60970f14c4 deploy:修改部署脚本 2021-12-11 08:53:47 -05:00
fanxb
7fc169c4f5 refactor:文件夹也建立搜索索引,图标改用异步生成 2021-11-17 16:48:42 +08:00
fanxb
10580d231f docs:修改文档 2021-11-04 17:05:53 +08:00
fanxb
2ba00eb861 refactor:增加忽略文件 2021-11-04 17:00:07 +08:00
fanxb
9b4a5187cc deploy:保存redis数据文件 2021-11-04 16:54:27 +08:00
FleyX
84b266d8bd
Update README.md
doc:修改readme.md
2021-10-22 21:24:40 +08:00
fanxb
4ef6582b2a refactor:增加日志打印 2021-10-19 15:20:19 +08:00
fanxb
e067de6c79 refactor:增加日志打印 2021-10-19 15:04:08 +08:00
fanxb
7c59a50ec4 refactor:修改http错误日志打印 2021-10-19 14:57:26 +08:00
fanxb
e735411bc3 refactor:增加日志打印 2021-10-19 14:49:37 +08:00
fanxb
02790f5ad3 fix:修复编译报错 2021-10-17 15:38:29 +08:00
fanxb
57c3f4dfc6 feat:后台新增通知查询功能 2021-10-17 15:37:15 +08:00
fanxb
13bd71f0a7 Merge branch 'dev' of github.com:FleyX/bookmark into dev 2021-10-17 11:59:36 +08:00
fanxb
89484b2587 refactor:删除无用文件 2021-10-17 11:59:18 +08:00
fanxb
a6b502d901 docs:新增公告表sql 2021-10-17 11:59:17 +08:00
fanxb
53a85db09c refactor:删除无用文件 2021-10-17 11:55:39 +08:00
fanxb
e075eeaeff Merge remote-tracking branch 'origin/master' into dev 2021-10-17 11:54:56 +08:00
FleyX
8baaf7a609
Merge pull request #25 from FleyX/dependabot/maven/bookMarkService/org.elasticsearch-elasticsearch-7.14.0
build(deps): bump elasticsearch from 7.13.3 to 7.14.0 in /bookMarkService
2021-10-17 11:51:00 +08:00
fanxb
e6701e557e docs:新增公告表sql 2021-09-23 17:06:33 +08:00
dependabot[bot]
36f92c54e9
build(deps): bump elasticsearch in /bookMarkService
Bumps [elasticsearch](https://github.com/elastic/elasticsearch) from 7.13.3 to 7.14.0.
- [Release notes](https://github.com/elastic/elasticsearch/releases)
- [Commits](https://github.com/elastic/elasticsearch/compare/v7.13.3...v7.14.0)

---
updated-dependencies:
- dependency-name: org.elasticsearch:elasticsearch
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-20 20:36:30 +00:00
fanxb
86997b234f Merge branch 'dev' 2021-09-15 22:43:08 +08:00
fanxb
5f57415b53 refactor:删除yarn.lock文件 2021-09-15 22:42:13 +08:00
fanxb
2e8a8455a2 feat:增加全局配置。前端根据是否配置代理决定是否展示github登陆 2021-09-15 22:41:44 +08:00
FleyX
dcf39ffea7
Merge pull request #24 from FleyX/dependabot/maven/bookMarkService/business/bookmark/org.jsoup-jsoup-1.14.2
build(deps): bump jsoup from 1.12.1 to 1.14.2 in /bookMarkService/business/bookmark
2021-08-31 21:36:57 +08:00
dependabot[bot]
a7f408b054
build(deps): bump jsoup in /bookMarkService/business/bookmark
Bumps [jsoup](https://github.com/jhy/jsoup) from 1.12.1 to 1.14.2.
- [Release notes](https://github.com/jhy/jsoup/releases)
- [Changelog](https://github.com/jhy/jsoup/blob/master/CHANGES)
- [Commits](https://github.com/jhy/jsoup/compare/jsoup-1.12.1...jsoup-1.14.2)

---
updated-dependencies:
- dependency-name: org.jsoup:jsoup
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-23 21:05:11 +00:00
fanxb
b8f3d4f3b7 refactor:代码规范修改 2021-08-20 14:25:34 +08:00
FleyX
b8baf22387
Merge pull request #22 from FleyX/dependabot/npm_and_yarn/bookmark_front/url-parse-1.5.3
build(deps): bump url-parse from 1.5.1 to 1.5.3 in /bookmark_front
2021-08-20 10:42:01 +08:00
FleyX
f28840f822
Merge pull request #23 from FleyX/dependabot/npm_and_yarn/bookmark_front/path-parse-1.0.7
build(deps): bump path-parse from 1.0.6 to 1.0.7 in /bookmark_front
2021-08-20 10:41:49 +08:00
dependabot[bot]
59a6a2c0d4
build(deps): bump path-parse from 1.0.6 to 1.0.7 in /bookmark_front
Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7.
- [Release notes](https://github.com/jbgutierrez/path-parse/releases)
- [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7)

---
updated-dependencies:
- dependency-name: path-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-11 02:42:07 +00:00
dependabot[bot]
a99033e572
build(deps): bump url-parse from 1.5.1 to 1.5.3 in /bookmark_front
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.5.1 to 1.5.3.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.5.1...1.5.3)

---
updated-dependencies:
- dependency-name: url-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-11 02:42:04 +00:00
FleyX
913bed79ad
Merge pull request #17 from FleyX/dependabot/npm_and_yarn/bookmark_front/dns-packet-1.3.4
build(deps): bump dns-packet from 1.3.1 to 1.3.4 in /bookmark_front
2021-08-11 10:41:50 +08:00
FleyX
1adb661bcf
Merge pull request #18 from FleyX/dependabot/npm_and_yarn/bookmark_front/ws-6.2.2
build(deps): bump ws from 6.2.1 to 6.2.2 in /bookmark_front
2021-08-11 10:41:36 +08:00
FleyX
acfc93d837
Merge pull request #19 from FleyX/dependabot/npm_and_yarn/bookmark_front/postcss-7.0.36
build(deps): bump postcss from 7.0.35 to 7.0.36 in /bookmark_front
2021-08-11 10:41:21 +08:00
FleyX
4bb1347545
Merge pull request #21 from FleyX/dependabot/maven/bookMarkService/org.elasticsearch-elasticsearch-7.13.3
build(deps): bump elasticsearch from 7.12.0 to 7.13.3 in /bookMarkService
2021-08-11 10:40:34 +08:00
dependabot[bot]
7dad169b3d
build(deps): bump elasticsearch in /bookMarkService
Bumps [elasticsearch](https://github.com/elastic/elasticsearch) from 7.12.0 to 7.13.3.
- [Release notes](https://github.com/elastic/elasticsearch/releases)
- [Commits](https://github.com/elastic/elasticsearch/compare/v7.12.0...v7.13.3)

---
updated-dependencies:
- dependency-name: org.elasticsearch:elasticsearch
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-09 20:43:36 +00:00
fanxb
4f0b8915e0 deploy:修改部署脚本,支持重复执行 2021-06-18 17:29:29 +08:00
fanxb
52a0908477 fix:修复java11编译的lombok依赖问题 2021-06-18 16:51:22 +08:00
dependabot[bot]
7c4bfb6e26
build(deps): bump postcss from 7.0.35 to 7.0.36 in /bookmark_front
Bumps [postcss](https://github.com/postcss/postcss) from 7.0.35 to 7.0.36.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/7.0.35...7.0.36)

---
updated-dependencies:
- dependency-name: postcss
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-17 15:44:24 +00:00
dependabot[bot]
9f85a65029
build(deps): bump ws from 6.2.1 to 6.2.2 in /bookmark_front
Bumps [ws](https://github.com/websockets/ws) from 6.2.1 to 6.2.2.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/commits)

---
updated-dependencies:
- dependency-name: ws
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-06 17:19:04 +00:00
dependabot[bot]
e370181f70
build(deps): bump dns-packet from 1.3.1 to 1.3.4 in /bookmark_front
Bumps [dns-packet](https://github.com/mafintosh/dns-packet) from 1.3.1 to 1.3.4.
- [Release notes](https://github.com/mafintosh/dns-packet/releases)
- [Changelog](https://github.com/mafintosh/dns-packet/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mafintosh/dns-packet/compare/v1.3.1...v1.3.4)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-27 02:38:23 +00:00
FleyX
a133bf1276
Merge pull request #16 from FleyX/dependabot/npm_and_yarn/bookmark_front/browserslist-4.16.6
build(deps): bump browserslist from 4.16.3 to 4.16.6 in /bookmark_front
2021-05-27 10:37:46 +08:00
dependabot[bot]
533786d9e1
build(deps): bump browserslist from 4.16.3 to 4.16.6 in /bookmark_front
Bumps [browserslist](https://github.com/browserslist/browserslist) from 4.16.3 to 4.16.6.
- [Release notes](https://github.com/browserslist/browserslist/releases)
- [Changelog](https://github.com/browserslist/browserslist/blob/main/CHANGELOG.md)
- [Commits](https://github.com/browserslist/browserslist/compare/4.16.3...4.16.6)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-27 01:37:35 +00:00
FleyX
0f6f3de253
Merge pull request #13 from FleyX/dependabot/npm_and_yarn/bookmark_front/hosted-git-info-2.8.9
build(deps): bump hosted-git-info from 2.8.8 to 2.8.9 in /bookmark_front
2021-05-13 21:51:42 +08:00
FleyX
74ff2df6a2
Merge pull request #12 from FleyX/dependabot/npm_and_yarn/bookmark_front/ssri-6.0.2
build(deps): bump ssri from 6.0.1 to 6.0.2 in /bookmark_front
2021-05-13 21:51:19 +08:00
dependabot[bot]
fff744290e
build(deps): bump hosted-git-info from 2.8.8 to 2.8.9 in /bookmark_front
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-11 23:41:32 +00:00
dependabot[bot]
31a4967420
build(deps): bump ssri from 6.0.1 to 6.0.2 in /bookmark_front
Bumps [ssri](https://github.com/npm/ssri) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/npm/ssri/releases)
- [Changelog](https://github.com/npm/ssri/blob/v6.0.2/CHANGELOG.md)
- [Commits](https://github.com/npm/ssri/compare/v6.0.1...v6.0.2)

Signed-off-by: dependabot[bot] <support@github.com>
2021-04-20 02:50:17 +00:00
fanxb
c8a9e2dd60 fix:修复搜索框无法输入中文 2021-04-19 15:08:56 +08:00
fanxb
1a9239b093 deploy:修改es版本 2021-03-31 16:22:15 +08:00
fanxb
607ff151a0 Merge remote-tracking branch 'origin/dependabot/maven/bookMarkService/org.elasticsearch-elasticsearch-7.11.0' 2021-03-31 16:06:14 +08:00
fanxb
3cd8b22b89 fix:修复筛选的问题 2021-03-31 16:00:28 +08:00
dependabot[bot]
c955b8357f
build(deps): bump elasticsearch from 7.2.0 to 7.11.0 in /bookMarkService
Bumps [elasticsearch](https://github.com/elastic/elasticsearch) from 7.2.0 to 7.11.0.
- [Release notes](https://github.com/elastic/elasticsearch/releases)
- [Commits](https://github.com/elastic/elasticsearch/compare/v7.2.0...v7.11.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-31 03:49:55 +00:00
fanxb
fbb4f8e005 deploy:修复编译报错问题 2021-03-31 11:48:45 +08:00
fanxb
bc63a91dc1 Merge remote-tracking branch 'origin/dependabot/maven/bookMarkService/org.elasticsearch-elasticsearch-7.11.0' 2021-03-31 11:33:42 +08:00
fanxb
fbf3922635 Merge remote-tracking branch 'origin/dependabot/npm_and_yarn/bookmark_front/axios-0.21.1' 2021-03-31 11:32:47 +08:00
fanxb
7459332382 Merge branch 'dev' 2021-03-31 11:32:26 +08:00
fanxb
decea5cf86 docs:修改文档 2021-03-31 11:31:55 +08:00
fanxb
c8c004e75b fix:修复搜索相关问题 2021-03-31 11:21:21 +08:00
fanxb
2db39c0b77 refactor:注册接口加校验 2021-03-25 17:22:10 +08:00
dependabot[bot]
230fedaae8
build(deps): bump elasticsearch from 7.2.0 to 7.11.0 in /bookMarkService
Bumps [elasticsearch](https://github.com/elastic/elasticsearch) from 7.2.0 to 7.11.0.
- [Release notes](https://github.com/elastic/elasticsearch/releases)
- [Commits](https://github.com/elastic/elasticsearch/compare/v7.2.0...v7.11.0)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-18 19:29:07 +00:00
fanxb
8f06c3a0a6 Merge branch 'dev' 2021-03-17 19:37:25 +08:00
fanxb
6dfe6863c5 fix:修复404 2021-03-17 19:36:27 +08:00
fanxb
81a5878f71 Merge branch 'dev' 2021-03-17 16:35:28 +08:00
fanxb
02e2b7f325 fix:增加方法处理异常书签数据 2021-03-17 16:28:01 +08:00
fanxb
faba445664 Merge branch 'dev' 2021-03-17 10:34:48 +08:00
fanxb
c30cde0176 feat:修改icon 2021-03-17 10:34:02 +08:00
fanxb
a91bbb07b2 fix:修复移动端登陆页面显示问题 2021-03-17 10:28:29 +08:00
dependabot[bot]
b991a07182
build(deps): bump axios from 0.19.2 to 0.21.1 in /bookmark_front
Bumps [axios](https://github.com/axios/axios) from 0.19.2 to 0.21.1.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/master/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.19.2...v0.21.1)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-16 15:53:41 +00:00
fanxb
a16cc728bb Merge branch 'dev'
# Conflicts:
#	README.md
#	front/package-lock.json
#	front/package.json
2021-03-16 23:52:19 +08:00
fanxb
6eccd3d2c4 deploy:删除无用文件 2021-03-16 23:50:18 +08:00
fanxb
0f51df5fec fix:修复icon无法获取问题 2021-03-16 10:37:15 +08:00
fanxb
ee499905b3 feat:修改书签时改变icon 2021-03-15 17:56:14 +08:00
fanxb
8b2f36fb72 fix:修复书签文件上传解析问题 2021-03-15 17:43:54 +08:00
fanxb
54c0843561 fix:修复github新用户登陆报错 2021-03-15 15:51:54 +08:00
fanxb
237f699f70 deploy:修改部署参数 2021-03-15 14:55:40 +08:00
fanxb
e55c86c407 fix:修复首次使用书签树不展示问题 2021-03-15 14:40:49 +08:00
fanxb
0aac5fdc20 fix:修复重复创建索引bug 2021-03-15 00:06:57 +08:00
fanxb
924bf8c2d3 feat:增加搜索引擎配置 2021-03-14 23:36:30 +08:00
fanxb
4c407c5b60 refactor:屏幕宽度兼容 2021-03-13 17:16:12 +08:00
fanxb
1be1b94c71 fix:修复icon过大问题 2021-03-13 16:58:19 +08:00
fanxb
fe22eab018 refactor: search生成逻辑重构 2021-03-13 16:58:01 +08:00
fanxb
f79faab1c3 feat: 管理员id设置默认值 2021-03-13 16:39:04 +08:00
fanxb
302d03a77c feat:icon更新功能完成 2021-03-13 16:37:48 +08:00
fanxb
1196926ac9 feat:github登陆接入 2021-03-11 16:53:36 +08:00
fanxb
cc71076b45 refactor:重构 2021-03-10 14:50:08 +08:00
fanxb
3368da0b2f refactor:删除无用代码 2021-03-09 14:39:47 +08:00
fanxb
f72141527d deploy:部署脚本修改 2021-03-09 11:44:12 +08:00
fanxb
6ac58f9900 deploy:部署修改 2021-03-09 11:40:21 +08:00
fanxb
d8c6aefd13 feat:个人信息功能修改 2021-03-05 17:03:51 +08:00
fanxb
a960ca062f feat:刷新页面时判断缓存 2021-03-05 14:17:49 +08:00
fanxb
270587c990 fix:修复移动端宽度超出问题 2021-03-04 17:31:00 +08:00
fanxb
bfc7385fae feat:导出功能完成 2021-03-04 16:15:35 +08:00
fanxb
91d69aab6a feat:书签,搜索基本完成 2021-03-04 11:03:51 +08:00
fanxb
f968a41303 feat:搜索完成 2021-03-03 14:13:59 +08:00
FleyX
fe56e51d96
Merge pull request #8 from FleyX/dependabot/npm_and_yarn/front/axios-0.21.1
build(deps): bump axios from 0.19.0 to 0.21.1 in /front
2021-01-18 15:17:30 +08:00
dependabot[bot]
aaceb3bbbd
build(deps): bump axios from 0.19.0 to 0.21.1 in /front
Bumps [axios](https://github.com/axios/axios) from 0.19.0 to 0.21.1.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v0.21.1/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.19.0...v0.21.1)

Signed-off-by: dependabot[bot] <support@github.com>
2021-01-05 09:32:42 +00:00
FleyX
6513a0861b
Update README.md 2020-12-24 14:26:01 +08:00
fanxb
a705cc76fb feat:搜索完成50% 2020-10-05 23:36:50 +08:00
fanxb
97e015ac49 feat:拖拽完成 2020-09-21 18:25:03 +08:00
fanxb
6a547ac856 feat: 完成删除功能 2020-08-26 22:03:20 +08:00
fanxb
92cb24801b feat:增加获取用户访问次数前十接口 2020-08-26 17:24:32 +08:00
fanxb
98883e63f5 temp 2020-08-25 21:44:19 +08:00
fanxb
cfe5f358a8 feat: 书签主页面显示 2020-07-31 00:07:32 +08:00
fanxb
2a78c3ce05 feat:重置密码完成 2020-07-15 22:28:46 +08:00
fanxb
3754a4db90 feat:vue版登陆、注册、重置密码功能完成 2020-07-14 14:38:15 +08:00
fanxb
8c493c0868 refactor:注册时返回用户信息 2020-07-14 14:37:48 +08:00
fanxb
0b9f38436a feat:登录页完成 2020-07-12 20:46:13 +08:00
fanxb
8676c8d1a9 temp 2020-07-07 08:26:17 +08:00
fanxb
8091cfc3d2 feat:vue前端构建 2020-07-05 09:40:54 +08:00
fanxb
e89993073c feat:vue前端构建 2020-07-05 09:39:43 +08:00
fanxb
aae34e0691 Merge branch 'dev' of github.com:FleyX/bookmark into dev 2020-06-29 15:04:22 +08:00
fanxb
f0956589e0 Merge branch 'master' into dev 2020-06-29 15:00:59 +08:00
fanxb
cf42ea4c3e deploy:修改pingyin部署 2020-06-29 15:00:51 +08:00
fanxb
d69df99a10 init:初始化 2020-06-24 13:54:15 +08:00
fanxb
f3ae2ab5f4 fix:修复设置spring.profiles.active不生效 2020-06-16 00:33:37 +08:00
fanxb
fd3538d11f fix:修复特殊字符报错问题 2020-06-16 00:06:46 +08:00
fanxb
36e13aaa36 fix:fastjson更新版本,避免被攻击 2020-06-15 23:30:50 +08:00
FleyX
fda9cb64de
Update README.md 2020-06-14 20:31:07 +08:00
fanxb
b0b3fbfa17 feat:浏览器插件右键菜单添加书签完成 2020-06-03 23:40:11 +08:00
fanxb
fafffe5aec feat:使用background.js做后台 2020-05-31 19:16:16 +08:00
fanxb
395e35f3fa feat:增加右键菜单 2020-05-31 14:51:37 +08:00
FleyX
d1d49f7973
Create LICENSE 2020-05-24 10:39:11 +08:00
fanxb
c3aab407a7 fix:修复编辑节点后提示更新缓存数据 2020-05-17 23:03:10 +08:00
fanxb
3090fd35f3 Merge remote-tracking branch 'origin/master' 2020-05-17 16:01:08 +08:00
fanxb
8884d2ca43 fix:修复redis消息消费错误bug 2020-05-17 15:59:34 +08:00
fanxb
41311e9385 Merge branch 'dev' 2020-05-12 13:48:56 +08:00
fanxb
03add0d05a refactor:代码格式修改 2020-05-12 13:48:33 +08:00
fanxb
cf960d4210 feat:用户只能增加自己的书签访问次数 2020-05-12 11:57:01 +08:00
fanxb
4e7861b5b1 feat:增加bookmark访问次数统计 2020-05-12 11:44:28 +08:00
fanxb
6bd68544c6 deploy:修复init执行报错问题 2020-05-10 21:43:06 +08:00
fanxb
ee84f8d68e refactor:漏提交 2020-05-10 21:06:13 +08:00
fanxb
5c26835a3c refactor:增加前端格式化配置文件 2020-05-10 14:07:39 +08:00
fanxb
6e91e49404 fix:修复页面书签数据缓存问题 2020-05-10 14:07:11 +08:00
fanxb
2be78755f4 deploy:加入es是否启用开关 2020-05-10 11:30:55 +08:00
fanxb
36f168fa2d deploy:去除docker部署es,暂时不需要es 2020-05-09 14:46:24 +08:00
fanxb
c2b865e996 temp 2019-12-08 09:05:37 +00:00
fanxb
3b5f178ca7 fix:修复更新所有人bookmarkUpdateTime不生效问题 2020-03-29 18:20:05 +08:00
fanxb
f7b195a21e docs:更新readme 2020-03-29 18:09:07 +08:00
fanxb
c995483ec6 deploy:修改pinyin部署程序 2020-03-29 17:52:08 +08:00
fanxb
b247c78994 deploy:docker部署增加pinyin服务 2020-03-29 17:20:41 +08:00
fanxb
09795f2d59 feat:增加拼音检索功能 2020-03-29 17:08:18 +08:00
fanxb
afb9886756 fix:issues-2 2020-03-29 16:35:09 +08:00
fanxb
160fa38c52 feat:使用redis模拟消息队列,支持单/多实例的生产消费模式和单实例的广播模式 2020-03-29 00:24:46 +08:00
fanxb
0b062c0e1a feat:后台增加拼音转换功能 2020-03-22 23:40:24 +08:00
fanxb
060178d4fc temp:零时提交 2020-03-22 20:43:39 +08:00
fanxb
a214f4074b feat:增加拼音转换功能 2020-03-15 18:32:04 +08:00
fanxb
cf269c5eea feat:增加反馈功能 2020-03-10 23:31:29 +08:00
fanxb
c85203b8e6 fix:修复搜索后没有默认选中 2020-03-10 22:17:34 +08:00
320 changed files with 18759 additions and 19880 deletions

24
.drone.yml Normal file
View File

@ -0,0 +1,24 @@
kind: pipeline
type: docker
name: bookmarkPublish
trigger:
branch:
- master
clone:
disable: true
steps:
- name: deploy
image: appleboy/drone-ssh
settings:
host:
from_secret: devHost
port: 22
user: root
key:
from_secret: privateSsh
command_timeout: 30m
script:
- cd /root/bookmark && git pull && bash build.sh && bash syncFile.sh

31
.env Normal file
View File

@ -0,0 +1,31 @@
#Mysql地址
MYSQL_ADDRESS=mysql:3306
#Mysql密码
MYSQL_PASSWORD=123456
#redis地址
REDIS_HOST=redis
#redis端口
REDIS_PORT=6379
# smtp地址
SMTP_HOST=
# smtp用户名
SMTP_USERNAME=
# smtp密码
SMTP_PASSWORD=
# 外网访问域名
BOOKMARK_HOST=localhost
# 文件存储地址比如用户上传的icon文件
BOOKMARK_FILE_SAVE_PATH=./data/files
# jwt密钥
JWT_SECRET=123456
# http网络代理ip(github api调用可能需要)
PROXY_IP=
# http网络代理端口
PROXY_PORT=
# 如果要支持github登陆需要配置以下两个参数
# github clientId
GITHUB_CLIENT_ID=
# github secret
GITHUB_SECRET=
# 管理员用户id
MANAGE_USER_ID=-1

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.idea

20
DEPLOY.md Normal file
View File

@ -0,0 +1,20 @@
本程序基于 docker 来进行部署,使用 docker-compose 管理服务。
**注意,仅在 x86 环境下测试,arm 下不保证可用性(目前测试可用)**
## 首次部署
0. 克隆代码`git clone https://github.com/FleyX/bookmark.git`
1. 进入文件夹`cd bookmark`
2. 安装新版的 docker,docker-compose,zip `apt install docker docker-compose zip`
3. 修改.env 文件中的参数,改为你的实际配置
4. 修改`浏览器插件/bookmarkBrowserPlugin/static/js/config.js`中的 bookmarkHost改为你的实际部署路径
5. 修改`浏览器插件/bookmarkBrowserPlugin/tab/index.html`中的`<meta http-equiv="Refresh" content="0;url=https://bm.fleyx.com" />`,将 url 改为你的实际部署地址
6. 执行`build.sh`编译前后端代码 `bash build.sh`
7. root 权限运行 `docker-compose up -d` 启动服务。
## 更新系统
0. 代码库更新`cd bookmark;git pull`
1. 执行`build.sh`编译前后端代码 `bash build.sh`
2. root 权限运行 `docker-compose restart` 启动服务

115
HELP.md Normal file
View File

@ -0,0 +1,115 @@
## 使用教程
### 首页
![首页](https://qiniupic.fleyx.com/blog/20220329202726.png?imageView2/2/w/1920)
#### 多功能搜索框
1. 搜索
搜索框支持书签搜索和网页搜索,输入关键词会在下方展示搜索结果:
![](https://qiniupic.fleyx.com/blog/20220329203152.png?imageView2/2/w/1920)
书签搜索支持**拼音/拼音首字母/名称关键字/链接关键词**搜索.比如对于`博客园。https://cnblogs.com`,可通过以下方式搜索到:
- ky (拼音首字母)
- boke (拼音)
- 客园 (名称关键词)
- blogs (链接关键词)
**注意不支持组合搜索,也就是不能同时用两种内容搜索**
2. 快捷键
tab/方向键上/下:用于选择书签(默认不选中书签)
enter/回车: 未选中书签情况下,用于发起网页搜索。选中书签的情况下跳转到对应书签
3. 网页搜索引擎
默认网页搜索引擎为**百度**,可点击图标进行切换,会自动保存选择的搜索引擎
![](https://qiniupic.fleyx.com/blog/20220329204103.png?imageView2/2/w/1920)
4. 搜索结果快捷操作
搜索书签后,通过鼠标悬浮在搜索结果上展示快捷操作,可进行如下操作:
- 定位到书签树中(仅在书签管理页面展示)
- 复制书签链接
- 固定到首页下方
![](https://qiniupic.fleyx.com/blog/20220329212023.png?imageView2/2/w/1920)
#### 书签固定区
可将一些常用书签固定在下方,方便快速跳转,最多可添加 20 个书签,未手动固定的情况下默认会按访问量顺序展示书签
![](https://qiniupic.fleyx.com/blog/20220329212211.png?imageView2/2/w/1920)
### 书签管理
通过点击右上方的头像,选择书签管理进入。
![](https://qiniupic.fleyx.com/blog/20220329212513.png?imageView2/2/w/1920)
本页面以书签树的形式展示所有的书签。
![](https://qiniupic.fleyx.com/blog/20220329212745.png?imageView2/2/w/1920)
上方搜索框介绍见[上一节:多功能搜索框](#多功能搜索框)
#### 书签树
- 新增
新增书签/文件夹可通过点击第一行"+"(添加到根路径)或者右键文件夹选择新增(添加到当前文件夹下)
![](https://qiniupic.fleyx.com/blog/20220329213056.png?imageView2/2/w/1920)
- 修改
右键要修改的书签/文件夹,选择修改按钮
- 删除
- 右键要删除的书签/文件夹选择删除
- 点击第一行的![](https://qiniupic.fleyx.com/blog/20220329213336.png?imageView2/2/w/1920),进入多选模式,然后再点击删除图标进行批量删除
![](https://qiniupic.fleyx.com/blog/20220329213551.png?imageView2/2/w/1920)
- 排序
支持拖拽排序或移动文件夹
![](https://qiniupic.fleyx.com/blog/20220329213716.png?imageView2/2/w/1920)
- 导入/导出
通过文件导入功能,快速导入现有浏览器书签
![](https://qiniupic.fleyx.com/blog/20220329214827.png?imageView2/2/w/1920)
点击书签右侧导出按钮,可导出现有书签数据到 html 文件中,此文件可直接导入到浏览器中
![](https://qiniupic.fleyx.com/blog/20220329214936.png?imageView2/2/w/1920)
### 个人中心
通过右上角悬浮菜单进入个人中心页面,可进行头像更换,密码修改等操作
### 浏览器插件
浏览器插件功能终于有 0.1 版本,支持鼠标右键菜单添加书签。
#### 安装插件
1. 首先下载插件压缩包,下载地址:[点击下载](https://fleyx.com/static/bookmarkBrowserPlugin.7z)
2. 安装插件(以 chrome 浏览器为例,其他支持插件的浏览器差不多)进入插件管理页面->开启开发者模式->加载已解压的拓展程序
![](https://qiniupic.fleyx.com/blog/202204151605709.png)
3. 之后页面点击右键->添加到书签,即可
![](https://qiniupic.fleyx.com/blog/202204151607593.png)

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 FleyX
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,8 +1,12 @@
本项目是一个云书签的项目,取名为:签签世界。 ![图片](https://s3.fleyx.com/picbed/2023/08/Snipaste_2023-08-13_15-33-16.png)
访问地址:[bm.tapme.top](http://bm.tapme.top) 本项目是一个在线书签管理的项目,名为:签签世界.
web端已经完成。 在线使用地址(长期提供服务):[bm.fleyx.com](https://bm.fleyx.com)
**为获得更好的体验,建议将主页设置为 bm.fleyx.com,并安装浏览器拓展,[点击查看如何安装](https://blog.fleyx.com/blog/detail/20220329/#%e6%b5%8f%e8%a7%88%e5%99%a8%e6%8f%92%e4%bb%b6)**
也可自己部署搭建,教程见:[docker-compose 部署](https://github.com/FleyX/bookmark/blob/master/DEPLOY.md)
# 缘由 # 缘由
@ -14,71 +18,39 @@ web端已经完成。
所以有了这样这样一个项目,建立一个和平台无关的书签管理器,可在任意平台使用。 所以有了这样这样一个项目,建立一个和平台无关的书签管理器,可在任意平台使用。
计划开发顺序如下web 端->chrome 插件->firfox 插件。
最终目的就是所有浏览器(不包含 ie10 及以下等远古浏览器)中都能便捷的使用书签。
# 主要功能 # 主要功能
## 查 帮助文档:[点击跳转](https://blog.fleyx.com/blog/detail/20220329/)
1. 节点树展示书签 - 支持从 chrome,edge,firefox 等浏览器导入书签数据。
![](https://raw.githubusercontent.com/FleyX/files/master/blogImg/20190801185846.png) - 支持从 OneEnv 导入书签数据
采用懒加载方式加载每一层数据,即使大量数据也不会卡顿。 - 树型多级目录支持
- 支持导出标准 html 书签文件
- 强大的检索功能,支持拼音检索
- 支持浏览器插件,安装插件以后可右键添加书签
2. 全文检索<br> # 更新日志
![](https://raw.githubusercontent.com/FleyX/files/master/blogImg/20190801190427.png)
- 可对`书签名``链接`进行全文检索 ## 1.4.1
- 支持方向键-上/下切换,回车确认
- 可直接搜索 google/baidu,tab 键切换。
![](https://raw.githubusercontent.com/FleyX/files/master/blogImg/20190801190720.png)
- 支持右键复制 url(移动端不支持右键,需点击编辑-->菜单键)
![](https://raw.githubusercontent.com/FleyX/files/master/blogImg/20190801191010.png)
## 增 - 修复书签名过长无法导入问题
![](https://raw.githubusercontent.com/FleyX/files/master/blogImg/20190801191452.png) ## 1.4
1. 手动编辑导入 - 优化首图加载逻辑
![](https://raw.githubusercontent.com/FleyX/files/master/blogImg/20190801191601.png) - 支持 OneEnv 备份文件导入
2. 谷歌、火狐浏览器书签备份文件直接导入.如果同一级别下名称相同导致冲突,将跳过,保留原有的。
![](https://raw.githubusercontent.com/FleyX/files/master/blogImg/20190801191721.png)
## ## 1.3
1. 修改节点内容,右键->编辑。(移动端长按相当于右键) ![pic](https://s3.fleyx.com/picbed/2023/08/Snipaste_2023-08-13_15-01-20.png)
2. 修改书签顺序,所属文件夹,直接拖拽书签到目标位置
搜索引擎支持自定义[#43](https://github.com/FleyX/bookmark/issues/43)
# 开发进度 位置:右上角个人中心-管理搜索引擎
## 2019-06-27 # TODO
**tag: 第一篇:环境搭建** - [x] 主页功能
- [x] 拼音检索
前端 react 框架搭建完成。 - [x] 书签导出
- [x] 浏览器插件
## 2019-07-10
**tag: 第二篇:注册登录重置密码完成**
后台框架搭建,并完成以下功能:
- 登录,注册,重置密码,发送验证码接口完成
- 书签 html 上传解析并存到数据库,仅测试了 chrome 导出的书签文件
- 查询某个用户的书签树
前台完成以下功能:
- 注册,登录,重置密码界面完成
## 2019-07-22
增删改查功能完成。支持节点拖拽
## 2019-07-30
- docker 部署重新整理,部署更方便了。
- 加入 elasticsearch 全文检索,可以方便的搜索书签啦。
- 树节点增加右键菜单,更加便捷的增删改

View File

@ -1,4 +0,0 @@
mavenRep
es/data
mysql/data
nginx/log

View File

@ -1,29 +0,0 @@
本程序基于 docker 来进行部署。
docker 镜像 构建文件为本目录下的`Dockerfile`,已经生产推送到阿里云的容器镜像库registry.cn-hangzhou.aliyuncs.com/fleyx/node-java-env:v2.本镜像包含如下:
- node 运行环境,已安装 cnpm
- java 运行编译环境openjdk11
- maven 运行环境,已设置为阿里源
部署过程如下:
1. 首先运行 init.sh 进行前后端打包。
2. 将密码smtp 等相关敏感信息设置 到环境变量中,内容如下:<br/>
```bash
export MYSQL_PASSWORD=123456
export JWT_SECRET=123456
export SMTP_HOST=localhost
export SMTP_USERNAME=test
export SMTP_PASSWORD=test
export SMTP_PORT=465
```
两种设置办法:
- 在终端执行上述命令.这种办法在关闭终端后这些变量会失效,如果重新部署 docker-compose 会报警告--环境变量未定义
- 写到配置文件中,比如/etc/profile 等文件中,然后`source /etc/profile` 使其生效。
3. 执行`docker-compose up -d` 后台启动系统。

View File

@ -1,98 +0,0 @@
version: "2"
services:
bookmark-mysql:
image: mysql:8.0.16
container_name: bookmark-mysql
ports:
- 3307:3306
networks:
- bookmark
volumes:
- ./mysql/data:/var/lib/mysql
- ./mysql/temp:/var/lib/mysql-files
- ./mysql/my.cnf:/etc/mysql/my.cnf
- /etc/localtime:/etc/localtime
- ./timezone:/etc/timezone
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_PASSWORD}
- MYSQL_DATABASE=bookmark
bookmark-redis:
image: redis:3.2.10
container_name: bookmark-redis
volumes:
- /etc/localtime:/etc/localtime
- ./timezone:/etc/timezone
networks:
- bookmark
# redis未设置密码如端口暴露可能会被攻击
ports:
- 6380:6379
bookmark-es:
image: elasticsearch:7.2.0
container_name: bookmark-es
volumes:
- /etc/localtime:/etc/localtime
- ./timezone:/etc/timezone
- ./es/data:/usr/share/elasticsearch/data
- ./es/ik:/usr/share/elasticsearch/plugins/ik
- ./es/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
environment:
- "ES_JAVA_OPTS=-Xms1500m -Xmx1500m"
ports:
- 9200:9200
- 9300:9300
networks:
- bookmark
bookmark-front:
image: nginx
container_name: bookmark-front
depends_on:
- bookmark-service
networks:
- bookmark
volumes:
- /etc/localtime:/etc/localtime
- ./timezone:/etc/timezone
- ../front/build:/opt/dist
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/log:/var/log/nginx
- ${BOOKMARK_FILE_SAVE_PATH}/files/public:/opt/files/public
ports:
- 8083:8080
bookmark-service:
image: registry.cn-hangzhou.aliyuncs.com/fleyx/node-java-env:v2
container_name: bookmark-service
depends_on:
- bookmark-mysql
- bookmark-redis
- bookmark-es
networks:
- bookmark
volumes:
- /etc/localtime:/etc/localtime
- ./timezone:/etc/timezone
- ../bookMarkService/web/target/bookmark-web-1.0-SNAPSHOT.jar:/opt/app/service.jar
- ${BOOKMARK_FILE_SAVE_PATH}:/opt/files
working_dir: /opt/app
command:
- /bin/bash
- -c
- |
sleep 20 && \
ls -l && \
java -jar -DisDev=false \
-DjwtSecret=${JWT_SECRET} \
-Dmybatis-plus.configuration.log-impl=org.apache.ibatis.logging.nologging.NoLoggingImpl \
-Dspring.mail.host=${SMTP_HOST} \
-Dspring.mail.username=${SMTP_USERNAME} \
-Dspring.mail.password=${SMTP_PASSWORD} \
-Dspring.datasource.druid.password=${MYSQL_PASSWORD} \
-Dspring.datasource.druid.url=jdbc:mysql://bookmark-mysql:3306/bookmark?useUnicode=true\&characterEncoding=utf-8\&useSSL=false\&useJDBCCompliantTimezoneShift=true\&useLegacyDatetimeCode=false\&serverTimezone=UTC \
-Dspring.redis.host=bookmark-redis \
-Des.host=bookmark-es \
-DserviceAddress=${BOOKMARK_HOST} \
-DfileSavePath=/opt/files \
-DserviceAddress=https://bm.tapme.top \
service.jar
networks:
bookmark:

View File

@ -1,15 +0,0 @@
#/bin/bash
base=$(cd "$(dirname "$0")";pwd)
# 创建es存放数据文件夹并设置读写权限
mkdir $base/es/data>/dev/null 2>&1
chmod 777 $base/es/data
sysctl -w vm.max_map_count=262144>/dev/null 2>&1
# 用于前后端打包
docker run -it --rm --name buildBookmark -v $base/../front:/opt/front registry.cn-hangzhou.aliyuncs.com/fleyx/node-java-env:v2 sh -c "cd /opt/front && npm --registry https://registry.npm.taobao.org install && npm run build"
docker run -it --rm --name buildBookmark -v $base/mavenRep:/opt/mavenRep -v $base/../bookMarkService:/opt/backend registry.cn-hangzhou.aliyuncs.com/fleyx/node-java-env:v2 sh -c "cd /opt/backend && mvn clean install && echo over"

View File

@ -17,6 +17,7 @@ static
*.iws *.iws
*.iml *.iml
*.ipr *.ipr
node_modules
### NetBeans ### ### NetBeans ###
/nbproject/private/ /nbproject/private/

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>bookmark-business</artifactId>
<groupId>com.fanxb</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>bookmark-business-api</artifactId>
</project>

View File

@ -0,0 +1,28 @@
package com.fanxb.bookmark.business.api;
import java.util.Set;
/**
* @author fanxb
* @date 2021/3/13
**/
public interface BookmarkApi {
/**
* 更新某个用户的icon数据
*
* @param userId 用户id
* @author fanxb
* @date 2021/3/11
**/
void updateUserBookmarkIcon(int userId);
/***
* 删除一次数据
* @author fanxb
* @param delete 是否删除问题数据
* @param userId userId
* @return 返回删除的数据
* @date 2021/3/17
**/
Set<String> dealBadBookmark(boolean delete, int userId);
}

View File

@ -0,0 +1,22 @@
package com.fanxb.bookmark.business.api;
/**
* @author fanxb
* @date 2021/8/20 下午2:12
*/
public interface UserApi {
/**
* 版本自增
*
* @param userId 用户id
*/
void versionPlus(int userId);
/**
* 更新所有用户的version
*
* @author fanxb
* @date 2021/3/13
**/
void allUserVersionPlus();
}

View File

@ -0,0 +1,6 @@
/**
* 用于模块间的方法相互调用,具体使用如下
* 1. 首先在business模块下建立interface接口然后在个具体模块中实现
* 2. 最后诸如interface,就能实现同级模块间的直接调用
*/
package com.fanxb.bookmark.business.api;

View File

@ -12,12 +12,27 @@
<artifactId>bookmark-business-bookmark</artifactId> <artifactId>bookmark-business-bookmark</artifactId>
<dependencies> <dependencies>
<dependency>
<groupId>com.fanxb</groupId>
<artifactId>bookmark-business-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--html文件解析--> <!--html文件解析-->
<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup --> <!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency> <dependency>
<groupId>org.jsoup</groupId> <groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId> <artifactId>jsoup</artifactId>
<version>1.12.1</version> <version>1.15.3</version>
</dependency>
<dependency>
<groupId>com.github.houbb</groupId>
<artifactId>pinyin</artifactId>
<version>0.3.1</version>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.44.1.0</version>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -0,0 +1,16 @@
package com.fanxb.bookmark.business.bookmark.constant;
import java.nio.file.Paths;
/**
* TODO
*
* @author fanxb
*/
public class FileConstant {
/**
* 网站icon存储路径
*/
public static final String FAVICON_PATH = Paths.get("files", "public", "favicon").toString();
}

View File

@ -0,0 +1,45 @@
package com.fanxb.bookmark.business.bookmark.consumer;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.fanxb.bookmark.business.bookmark.dao.PinBookmarkDao;
import com.fanxb.bookmark.business.bookmark.entity.redis.BookmarkDeleteMessage;
import com.fanxb.bookmark.common.annotation.MqConsumer;
import com.fanxb.bookmark.common.constant.EsConstant;
import com.fanxb.bookmark.common.constant.RedisConstant;
import com.fanxb.bookmark.common.entity.redis.RedisConsumer;
import com.fanxb.bookmark.common.util.EsUtil;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
/**
* Created with IntelliJ IDEA
*
* @author fanxb
* Date: 2020/3/29
* Time: 13:08
*/
@MqConsumer(RedisConstant.BOOKMARK_DELETE_ES)
public class BookmarkDeleteMessageConsumer implements RedisConsumer {
private final PinBookmarkDao pinBookmarkDao;
private final EsUtil esUtil;
@Autowired
public BookmarkDeleteMessageConsumer(PinBookmarkDao pinBookmarkDao, EsUtil esUtil) {
this.pinBookmarkDao = pinBookmarkDao;
this.esUtil = esUtil;
}
@Override
public void deal(String message) {
BookmarkDeleteMessage obj = JSON.parseObject(message, BookmarkDeleteMessage.class);
//删除首页固定的数据
pinBookmarkDao.deleteUnExistBookmark(obj.getUserId());
//删除es数据
if (CollectionUtil.isNotEmpty(obj.getBookmarkIds())) {
esUtil.deleteBatch(EsConstant.BOOKMARK_INDEX, obj.getBookmarkIds());
}
}
}

View File

@ -0,0 +1,43 @@
package com.fanxb.bookmark.business.bookmark.consumer;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSONArray;
import com.fanxb.bookmark.business.bookmark.entity.BookmarkEs;
import com.fanxb.bookmark.common.annotation.MqConsumer;
import com.fanxb.bookmark.common.constant.EsConstant;
import com.fanxb.bookmark.common.constant.RedisConstant;
import com.fanxb.bookmark.common.entity.po.Bookmark;
import com.fanxb.bookmark.common.entity.EsEntity;
import com.fanxb.bookmark.common.entity.redis.RedisConsumer;
import com.fanxb.bookmark.common.util.EsUtil;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import java.util.stream.Collectors;
/**
* 插入/更新数据到es中
* Created with IntelliJ IDEA
* Created By Fxb
* Date: 2020/3/29
* Time: 11:34
* @author fanxb
*/
@MqConsumer(RedisConstant.BOOKMARK_INSERT_ES)
public class BookmarkInsertEsConsumer implements RedisConsumer {
@Autowired
private EsUtil esUtil;
@Override
public void deal(String message) {
List<Bookmark> bookmarks = JSONArray.parseArray(message, Bookmark.class);
if (CollectionUtil.isEmpty(bookmarks)) {
return;
}
List<EsEntity<BookmarkEs>> esList = bookmarks.stream()
.map(item -> new EsEntity<>(item.getBookmarkId().toString(), new BookmarkEs(item))).collect(Collectors.toList());
esUtil.insertBatch(EsConstant.BOOKMARK_INDEX, esList);
}
}

View File

@ -0,0 +1,41 @@
package com.fanxb.bookmark.business.bookmark.consumer;
import com.alibaba.fastjson.JSON;
import com.fanxb.bookmark.business.bookmark.dao.BookmarkDao;
import com.fanxb.bookmark.business.bookmark.entity.redis.VisitNumPlus;
import com.fanxb.bookmark.common.annotation.MqConsumer;
import com.fanxb.bookmark.common.constant.RedisConstant;
import com.fanxb.bookmark.common.entity.redis.RedisConsumer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
/**
* 更新书签时间
* Created with IntelliJ IDEA
* Created By Fxb
* Date: 2020/5/12
* Time: 10:33
*
* @author fanxb
*/
@MqConsumer(RedisConstant.BOOKMARK_VISIT_NUM_PLUS)
@Slf4j
public class BookmarkVisitNumPlusConsumer implements RedisConsumer {
private final BookmarkDao bookmarkDao;
@Autowired
public BookmarkVisitNumPlusConsumer(BookmarkDao bookmarkDao) {
this.bookmarkDao = bookmarkDao;
}
@Override
public void deal(String message) {
VisitNumPlus item = JSON.parseObject(message, VisitNumPlus.class);
try {
bookmarkDao.updateVisitNum(item);
} catch (Exception e) {
log.error("书签访问次数增加失败:{}", e.getMessage());
}
}
}

View File

@ -1,6 +1,7 @@
package com.fanxb.bookmark.business.bookmark.controller; package com.fanxb.bookmark.business.bookmark.controller;
import com.fanxb.bookmark.business.bookmark.service.BookmarkBackupService; import com.fanxb.bookmark.business.bookmark.service.BookmarkBackupService;
import com.fanxb.bookmark.business.bookmark.service.impl.BookmarkBackupServiceImpl;
import com.fanxb.bookmark.common.entity.Result; import com.fanxb.bookmark.common.entity.Result;
import com.fanxb.bookmark.common.util.ThreadPoolUtil; import com.fanxb.bookmark.common.util.ThreadPoolUtil;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -20,17 +21,17 @@ import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/bookmarkBackup") @RequestMapping("/bookmarkBackup")
public class BookmarkBackupController { public class BookmarkBackupController {
private BookmarkBackupService backupService; private final BookmarkBackupService backupService;
@Autowired @Autowired
public BookmarkBackupController(BookmarkBackupService backupService) { public BookmarkBackupController(BookmarkBackupServiceImpl backupService) {
this.backupService = backupService; this.backupService = backupService;
} }
@PostMapping("/mysqlToEs") @PostMapping("/mysqlToEs")
public Result backupToEs() { public Result backupToEs() {
//异步执行同步任务 //异步执行同步任务
ThreadPoolUtil.execute(() -> backupService.backupToEs()); ThreadPoolUtil.execute(backupService::backupToEs);
return Result.success(null); return Result.success(null);
} }
} }

View File

@ -1,12 +1,14 @@
package com.fanxb.bookmark.business.bookmark.controller; package com.fanxb.bookmark.business.bookmark.controller;
import com.alibaba.fastjson.JSONObject;
import com.fanxb.bookmark.business.bookmark.entity.BatchDeleteBody; import com.fanxb.bookmark.business.bookmark.entity.BatchDeleteBody;
import com.fanxb.bookmark.business.bookmark.entity.BookmarkEs; import com.fanxb.bookmark.business.bookmark.entity.BookmarkEs;
import com.fanxb.bookmark.business.bookmark.entity.MoveNodeBody; import com.fanxb.bookmark.business.bookmark.entity.MoveNodeBody;
import com.fanxb.bookmark.business.bookmark.service.BookmarkBackupService;
import com.fanxb.bookmark.business.bookmark.service.BookmarkService; import com.fanxb.bookmark.business.bookmark.service.BookmarkService;
import com.fanxb.bookmark.common.entity.Bookmark; import com.fanxb.bookmark.business.bookmark.service.PinYinService;
import com.fanxb.bookmark.common.entity.po.Bookmark;
import com.fanxb.bookmark.common.entity.Result; import com.fanxb.bookmark.common.entity.Result;
import com.fanxb.bookmark.common.entity.UserContext;
import com.fanxb.bookmark.common.util.UserContextHolder; import com.fanxb.bookmark.common.util.UserContextHolder;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -24,9 +26,12 @@ import java.util.List;
@RestController @RestController
@RequestMapping("/bookmark") @RequestMapping("/bookmark")
public class BookmarkController { public class BookmarkController {
@Autowired
private BookmarkBackupService bookmarkBackupService;
@Autowired @Autowired
private BookmarkService bookmarkService; private BookmarkService bookmarkService;
@Autowired
private PinYinService pinYinService;
/** /**
* Description: 获取路径为path的书签数据 * Description: 获取路径为path的书签数据
@ -49,7 +54,7 @@ public class BookmarkController {
* @date 2019/12/14 0:09 * @date 2019/12/14 0:09
*/ */
@GetMapping("/currentUser") @GetMapping("/currentUser")
public Result getBookmarkMap(){ public Result getBookmarkMap() {
return Result.success(bookmarkService.getOneBookmarkTree(UserContextHolder.get().getUserId())); return Result.success(bookmarkService.getOneBookmarkTree(UserContextHolder.get().getUserId()));
} }
@ -62,9 +67,9 @@ public class BookmarkController {
* @author fanxb * @author fanxb
* @date 2019/7/8 15:17 * @date 2019/7/8 15:17
*/ */
@PutMapping("/uploadBookmarkFile") @RequestMapping("/uploadBookmarkFile")
public Result uploadFile(@RequestParam("file") MultipartFile file, @RequestParam("path") String path) throws Exception { public Result uploadFile(@RequestParam("file") MultipartFile file, @RequestParam("path") String path) throws Exception {
bookmarkService.parseBookmarkFile(UserContextHolder.get().getUserId(), file.getInputStream(), path); bookmarkService.parseBookmarkFile(UserContextHolder.get().getUserId(), file, path);
return Result.success(null); return Result.success(null);
} }
@ -93,8 +98,7 @@ public class BookmarkController {
*/ */
@PostMapping("/updateOne") @PostMapping("/updateOne")
public Result editCurrentUserBookmark(@RequestBody Bookmark bookmark) { public Result editCurrentUserBookmark(@RequestBody Bookmark bookmark) {
bookmarkService.updateOne(UserContextHolder.get().getUserId(), bookmark); return Result.success(bookmarkService.updateOne(UserContextHolder.get().getUserId(), bookmark));
return Result.success(null);
} }
/** /**
@ -107,7 +111,7 @@ public class BookmarkController {
*/ */
@PostMapping("/batchDelete") @PostMapping("/batchDelete")
public Result batchDelete(@RequestBody BatchDeleteBody body) { public Result batchDelete(@RequestBody BatchDeleteBody body) {
bookmarkService.batchDelete(UserContextHolder.get().getUserId(), body.getFolderIdList(), body.getBookmarkIdList()); bookmarkService.batchDelete(UserContextHolder.get().getUserId(), body.getPathList(), body.getBookmarkIdList());
return Result.success(null); return Result.success(null);
} }
@ -140,8 +144,70 @@ public class BookmarkController {
*/ */
@PostMapping("/syncBookmark") @PostMapping("/syncBookmark")
public Result syncBookmark() { public Result syncBookmark() {
bookmarkService.syncUserBookmark(UserContextHolder.get().getUserId()); bookmarkBackupService.syncUserBookmark(UserContextHolder.get().getUserId());
return Result.success(null); return Result.success(null);
} }
/**
* 功能描述: 全量更新拼音数据
*
* @return com.fanxb.bookmark.common.entity.Result
* @author fanxb
* @date 2020/3/22 22:22
*/
@PostMapping("/allPinyinCreate")
public Result changeAllPinyin() {
pinYinService.changeAll();
return Result.success(null);
}
/**
* 功能描述: 书签增加1
*
* @param id id
* @return com.fanxb.bookmark.common.entity.Result
* @author fanxb
* @date 2020/5/12 10:44
*/
@PostMapping("/visitNum")
public Result visitNum(int id) {
bookmarkService.visitNumPlus(id);
return Result.success(null);
}
/**
* 功能描述: 获取用户访问次数前10的书签
*
* @author fanxb
*/
@GetMapping("/user/popular")
public Result currentUserPopular() {
return Result.success(bookmarkService.userPopular(10));
}
/**
* 更新所有的icon
*
* @author fanxb
* @date 2021/3/11
**/
@PostMapping("/updateCurrentUserIcon")
public Result updateCurrentUserIcon() {
bookmarkService.updateUserBookmarkIcon(UserContextHolder.get().getUserId());
return Result.success(null);
}
/**
* 检查/删除无父节点的数据
*
* @return com.fanxb.bookmark.common.entity.Result
* @author fanxb
* @date 2021/3/17
**/
@PostMapping("/dealBadBookmark")
public Result dealBadBookmark(@RequestBody JSONObject obj) {
return Result.success(bookmarkService.dealBadBookmark(obj.getBoolean("delete"), UserContextHolder.get().getUserId()));
}
} }

View File

@ -0,0 +1,56 @@
package com.fanxb.bookmark.business.bookmark.controller;
import com.fanxb.bookmark.business.bookmark.entity.po.PInBookmarkPo;
import com.fanxb.bookmark.business.bookmark.service.HomePinService;
import com.fanxb.bookmark.common.entity.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* 首页相关接口
*
* @author fanxb
*/
@RestController
@RequestMapping("/home")
public class HomeController {
private final HomePinService homePinService;
@Autowired
public HomeController(HomePinService homePinService) {
this.homePinService = homePinService;
}
/**
* 获取首页固定标签
*
* @author fanxb
*/
@GetMapping("/pin")
public Result getPin() {
return Result.success(homePinService.getHomePinList());
}
/**
* 新增固定书签
*
* @author fanxb
*/
@PutMapping("/pin")
public Result addPin(@RequestBody PInBookmarkPo po) {
return Result.success(homePinService.addOne(po));
}
/**
* 删除书签
*
* @param id url参数id
* @author fanxb
*/
@DeleteMapping("/pin")
public Result deleteOne(int id) {
homePinService.delete(id);
return Result.success();
}
}

View File

@ -1,27 +0,0 @@
package com.fanxb.bookmark.business.bookmark.dao;
import com.fanxb.bookmark.common.entity.Bookmark;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* Created with IntelliJ IDEA
* Created By Fxb
* Date: 2019/11/12
* Time: 0:24
*/
@Mapper
public interface BookmarkBackupDao {
/**
* 分页获取所有的书签
* @param size 大小
* @param startIndex 开始下标
* @return
*/
@Select("select * from bookmark order by bookmarkId limit ${startIndex},${size}")
List<Bookmark> getBookmarkListPage(@Param("size") int size,@Param("startIndex") int startIndex);
}

View File

@ -1,8 +1,12 @@
package com.fanxb.bookmark.business.bookmark.dao; package com.fanxb.bookmark.business.bookmark.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.fanxb.bookmark.business.bookmark.entity.BookmarkEs; import com.fanxb.bookmark.business.bookmark.entity.BookmarkEs;
import com.fanxb.bookmark.common.entity.Bookmark; import com.fanxb.bookmark.business.bookmark.entity.redis.VisitNumPlus;
import com.fanxb.bookmark.common.entity.po.Bookmark;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.List; import java.util.List;
@ -15,13 +19,12 @@ import java.util.List;
* @date 2019/7/8 16:39 * @date 2019/7/8 16:39
*/ */
@Component @Component
public interface BookmarkDao { public interface BookmarkDao extends BaseMapper<Bookmark> {
/** /**
* Description: 插入一条书签记录 * Description: 插入一条书签记录
* *
* @param node node * @param node node
* @return void
* @author fanxb * @author fanxb
* @date 2019/7/8 16:49 * @date 2019/7/8 16:49
*/ */
@ -54,7 +57,7 @@ public interface BookmarkDao {
* Description: 根据用户id获取其所有数据 * Description: 根据用户id获取其所有数据
* *
* @param userId userid * @param userId userid
* @return java.util.List<com.fanxb.bookmark.common.entity.Bookmark> * @return java.util.List<com.fanxb.bookmark.common.entity.po.Bookmark>
* @author fanxb * @author fanxb
* @date 2019/7/9 18:55 * @date 2019/7/9 18:55
*/ */
@ -65,21 +68,21 @@ public interface BookmarkDao {
* *
* @param userId userId * @param userId userId
* @param path path * @param path path
* @return java.util.List<com.fanxb.bookmark.common.entity.Bookmark> * @return java.util.List<com.fanxb.bookmark.common.entity.po.Bookmark>
* @author fanxb * @author fanxb
* @date 2019/7/17 14:48 * @date 2019/7/17 14:48
*/ */
List<Bookmark> getListByUserIdAndPath(@Param("userId") int userId, @Param("path") String path); List<Bookmark> getListByUserIdAndPath(@Param("userId") int userId, @Param("path") String path);
/** /**
* Description: 删除某用户某个书签文件下所有数据 * Description: 删除某用户某个书签路径下所有数据,不包含文件夹自身
* *
* @param userId 用户id * @param userId 用户id
* @param folderId 文件夹id * @param path 文件夹id
* @author fanxb * @author fanxb
* @date 2019/7/12 14:13 * @date 2019/7/12 14:13
*/ */
void deleteUserFolder(@Param("userId") int userId, @Param("folderId") int folderId); void deleteUserFolder(@Param("userId") int userId, @Param("path") String path);
/** /**
* Description: 删除用户书签 * Description: 删除用户书签
@ -135,27 +138,127 @@ public interface BookmarkDao {
void updatePathAndSort(@Param("userId") int userId, @Param("bookmarkId") int bookmarkId, @Param("path") String path, @Param("sort") int sort); void updatePathAndSort(@Param("userId") int userId, @Param("bookmarkId") int bookmarkId, @Param("path") String path, @Param("sort") int sort);
/** /**
* Description: 获取某个文件夹下所有的节点id * Description: 获取某个路径下所有的节点id
* *
* @param userId userId * @param userId userId
* @param folderId folderId * @param path path
* @return java.util.List<java.lang.Integer> * @return java.util.List<java.lang.Integer>
* @author fanxb * @author fanxb
* @date 2019/7/25 14:14 * @date 2019/7/25 14:14
*/ */
List<Integer> getChildrenBookmarkId(@Param("userId") int userId, @Param("folderId") int folderId); List<Integer> getChildrenBookmarkId(@Param("userId") int userId, @Param("path") String path);
/** /**
* Description: 根据用户id类别分页查找书签 * Description: 根据用户id类别分页查找书签
* *
* @param userId userId
* @param type type
* @param start start
* @param size size
* @return java.util.List<com.fanxb.bookmark.business.bookmark.entity.BookmarkEs>
* @author fanxb * @author fanxb
* @date 2019/7/26 15:23 * @date 2019/7/26 15:23
* @param userId userId
* @param type type
* @param start start
* @param size size
* @return java.util.List<com.fanxb.bookmark.business.bookmark.entity.BookmarkEs>
*/ */
List<BookmarkEs> selectBookmarkEsByUserIdAndType(@Param("userId") int userId, @Param("type") int type, @Param("start") int start, @Param("size") int size); List<BookmarkEs> selectBookmarkEsByUserIdAndType(@Param("userId") int userId, @Param("type") int type, @Param("start") int start, @Param("size") int size);
/**
* 功能描述: 查询所有的bookmark,用于全量更新拼音key
*
* @param bookmarkId bookmarkId
* @param size size
* @return List<Bookmark>
* @author fanxb
* @date 2020/3/22 22:06
*/
@Select("select bookmarkId,name,url from bookmark where bookmarkId>${bookmarkId} order by bookmarkId asc limit 0,${size}")
List<Bookmark> selectPinyinEmpty(@Param("bookmarkId") int bookmarkId, @Param("size") int size);
/**
* 功能描述: 更新一个bookmark的key
*
* @param bookmarkId id
* @param searchKey searchKey
* @author fanxb
* @date 2020/3/22 22:08
*/
@Update("update bookmark set searchKey=#{searchKey} where bookmarkId=#{bookmarkId}")
void updateSearchKey(@Param("bookmarkId") int bookmarkId, @Param("searchKey") String searchKey);
/**
* 批量更新searchKey
*
* @param list list
* @author fanxb
* @date 2020/3/22 22:08
*/
void updateSearchKeyBatch(List<Bookmark> list);
/**
* 分页获取所有的书签
*
* @param size 大小
* @param startIndex 开始下标
* @return bookmark List
* @author fanxb
*/
@Select("select * from bookmark order by bookmarkId limit ${startIndex},${size}")
List<Bookmark> getBookmarkListPage(@Param("size") int size, @Param("startIndex") int startIndex);
/**
* 功能描述: 书签访问次数+1
*
* @param item 信息
* @author fanxb
* @date 2020/5/12 10:40
*/
@Update("update bookmark set visitNum=visitNum+1 where userId=#{userId} and bookmarkId=#{bookmarkId}")
void updateVisitNum(VisitNumPlus item);
/**
* 查询用户最常访问的几个书签
*
* @param userId 用户id
* @param num num
* @return java.util.List<com.fanxb.bookmark.common.entity.po.Bookmark>
* @author fanxb
* @date 2021/8/20 上午11:52
*/
@Select("select bookmarkId,name,url,icon from bookmark where userId=#{userId} and type=0 order by visitNum desc limit 0,#{num}")
List<Bookmark> selectPopular(@Param("userId") int userId, @Param("num") int num);
/**
* 获取某用户无icon的条目
*
* @param userId userId
* @param start 开始
* @param size 数量
* @return java.util.List<com.fanxb.bookmark.common.entity.po.Bookmark>
* @author fanxb
* @date 2021/8/20 下午12:02
*/
@Select("select userId,bookmarkId,url,icon from bookmark where userId=#{userId} and icon='' order by bookmarkId asc limit #{start},#{size}")
List<Bookmark> selectUserNoIcon(@Param("userId") int userId, @Param("start") int start, @Param("size") int size);
/**
* 更新icon
*
* @param bookmarkId bookmarkId
* @param icon icon
* @author fanxb
* @date 2021/3/13
**/
@Update("update bookmark set icon=#{icon} where bookmarkId=#{bookmarkId}")
void updateIcon(@Param("bookmarkId") int bookmarkId, @Param("icon") String icon);
/**
* 查询一个用户全部的书签路径
*
* @param userId userId
* @return java.util.List<com.fanxb.bookmark.common.entity.po.Bookmark>
* @author fanxb
* @date 2021/8/20 上午11:58
*/
@Select("select bookmarkId,path from bookmark where userId=#{userId}")
List<Bookmark> selectBookmarkIdPathByUserId(int userId);
} }

View File

@ -0,0 +1,39 @@
package com.fanxb.bookmark.business.bookmark.dao;
import org.apache.ibatis.annotations.*;
/**
* @author fanxb
*/
@Mapper
public interface HostIconDao {
/**
* 插入一条数据
*
* @param host host
* @param iconPath path
* @author fanxb
*/
@Insert("insert into host_icon(host,iconPath) value(#{host},#{iconPath})")
void insert(@Param("host") String host, @Param("iconPath") String iconPath);
/**
* 根据host获取iconPath
*
* @param host host
* @return {@link String}
* @author fanxb
*/
@Select("select iconPath from host_icon where host=#{host} limit 1")
String selectByHost(String host);
/**
* 删除一条
*
* @param host host
* @author FleyX
*/
@Delete("delete from host_icon where host=#{host}")
void deleteByHost(String host);
}

View File

@ -0,0 +1,44 @@
package com.fanxb.bookmark.business.bookmark.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.fanxb.bookmark.business.bookmark.entity.po.PInBookmarkPo;
import com.fanxb.bookmark.business.bookmark.entity.vo.HomePinItemVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* @author fanxb
*/
@Mapper
public interface PinBookmarkDao extends BaseMapper<PInBookmarkPo> {
/***
* 获取用户固定的书签
*
* @param userId userId
* @return {@link List<HomePinItemVo>}
* @author fanxb
*/
@Select("select a.id,b.bookmarkId,b.name,b.url,b.icon from pin_bookmark a inner join bookmark b on a.bookmarkId=b.bookmarkId where a.userId=#{userId}")
List<HomePinItemVo> selectUserPin(int userId);
/**
* 获取当前最大的序列号
*
* @param userId userId
* @return {@link int}
* @author fanxb
*/
@Select("select ifnull(max(sort),0) from pin_bookmark where userId=#{userId}")
int getUserMaxSort(int userId);
/**
* 删除书签后需要清理此表,将不存在的书签删除
*
* @param userId userId
* @author fanxb
*/
void deleteUnExistBookmark(int userId);
}

View File

@ -13,6 +13,12 @@ import java.util.List;
*/ */
@Data @Data
public class BatchDeleteBody{ public class BatchDeleteBody{
private List<Integer> folderIdList; /**
* 要删除的书签路径
*/
private List<String> pathList;
/**
* 要删除的书签id
*/
private List<Integer> bookmarkIdList; private List<Integer> bookmarkIdList;
} }

View File

@ -1,6 +1,6 @@
package com.fanxb.bookmark.business.bookmark.entity; package com.fanxb.bookmark.business.bookmark.entity;
import com.fanxb.bookmark.common.entity.Bookmark; import com.fanxb.bookmark.common.entity.po.Bookmark;
import lombok.Data; import lombok.Data;
/** /**

View File

@ -0,0 +1,41 @@
package com.fanxb.bookmark.business.bookmark.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import java.util.List;
/**
* Created with IntelliJ IDEA
*
* @author fanxb
* Date: 2020/3/19
* Time: 0:05
*/
@Data
@Builder
public class PinYinBody {
/**
* 待转换拼音的文本列表
*/
private List<String> strs;
private Config config;
@Data
@AllArgsConstructor
public static class Config {
/**
* 是否启用分词模式
*/
private boolean segment;
/**
* 是否启用多音字
*/
private boolean heteronym;
/**
* 风格
*/
private int style;
}
}

View File

@ -0,0 +1,29 @@
package com.fanxb.bookmark.business.bookmark.entity.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* 首页固定标签
*
* @author fanxb
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@TableName("pin_bookmark")
public class PInBookmarkPo {
@TableId(value = "id", type = IdType.AUTO)
private int id;
private int userId;
private int bookmarkId;
private int sort;
private long createDate;
}

View File

@ -0,0 +1,26 @@
package com.fanxb.bookmark.business.bookmark.entity.redis;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.util.Collection;
import java.util.List;
/**
* @author fanxb
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BookmarkDeleteMessage {
/**
* 用户id
*/
private int userId;
/**
* 批量删除的书签id
*/
private Collection<String> bookmarkIds;
}

View File

@ -0,0 +1,23 @@
package com.fanxb.bookmark.business.bookmark.entity.redis;
import lombok.AllArgsConstructor;
import lombok.Data;
/**
* Created with IntelliJ IDEA
*
* @author fanxb
* Date: 2020/5/12 11:47
*/
@Data
@AllArgsConstructor
public class VisitNumPlus {
/**
* 用户id
*/
private int userId;
/**
* 书签id
*/
private int bookmarkId;
}

View File

@ -0,0 +1,37 @@
package com.fanxb.bookmark.business.bookmark.entity.vo;
import com.fanxb.bookmark.business.bookmark.entity.po.PInBookmarkPo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
/**
* @author fanxb
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class HomePinItemVo {
/**
* pinBookmark Id
*/
private Integer id;
/**
* 书签id
*/
private int bookmarkId;
/**
* 书签名
*/
private String name;
/**
* url
*/
private String url;
/**
* icon
*/
private String icon;
}

View File

@ -1,38 +1,12 @@
package com.fanxb.bookmark.business.bookmark.service; package com.fanxb.bookmark.business.bookmark.service;
import com.fanxb.bookmark.business.bookmark.dao.BookmarkBackupDao;
import com.fanxb.bookmark.business.bookmark.dao.BookmarkDao;
import com.fanxb.bookmark.common.constant.EsConstant;
import com.fanxb.bookmark.common.entity.Bookmark;
import com.fanxb.bookmark.common.entity.EsEntity;
import com.fanxb.bookmark.common.util.EsUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/** /**
* Created with IntelliJ IDEA
* Created By Fxb
* Date: 2019/11/12
* Time: 0:22
*
* @author fanxb * @author fanxb
* Date: 2020/3/29
* Time: 12:43
*/ */
@Service public interface BookmarkBackupService {
public class BookmarkBackupService {
@Autowired
private BookmarkBackupDao bookmarkBackupDao;
@Autowired
private EsUtil esUtil;
/**
* 一次同步BACKUP_SIZE条到es中
*/
private static final int BACKUP_SIZE = 500;
/** /**
* 功能描述: 将mysql数据同步到es中 * 功能描述: 将mysql数据同步到es中
@ -40,14 +14,14 @@ public class BookmarkBackupService {
* @author fanxb * @author fanxb
* @date 2019/11/12 0:22 * @date 2019/11/12 0:22
*/ */
public void backupToEs() { void backupToEs();
int start = 0;
List<Bookmark> list; /**
while ((list = bookmarkBackupDao.getBookmarkListPage(BACKUP_SIZE, start)).size() != 0) { * Description: 将某个用户的书签数据mysql同步到es中
List<EsEntity> batchList = new ArrayList<>(list.size()); *
list.forEach(item -> batchList.add(new EsEntity<>(item.getBookmarkId().toString(), item))); * @param userId 用户id
esUtil.insertBatch(EsConstant.BOOKMARK_INDEX, batchList); * @author fanxb
start += BACKUP_SIZE; * @date 2019/7/26 11:27
} */
} void syncUserBookmark(int userId);
} }

View File

@ -1,323 +1,150 @@
package com.fanxb.bookmark.business.bookmark.service; package com.fanxb.bookmark.business.bookmark.service;
import com.fanxb.bookmark.business.bookmark.dao.BookmarkDao;
import com.fanxb.bookmark.business.bookmark.entity.BookmarkEs; import com.fanxb.bookmark.business.bookmark.entity.BookmarkEs;
import com.fanxb.bookmark.business.bookmark.entity.MoveNodeBody; import com.fanxb.bookmark.business.bookmark.entity.MoveNodeBody;
import com.fanxb.bookmark.common.constant.EsConstant; import com.fanxb.bookmark.common.entity.po.Bookmark;
import com.fanxb.bookmark.common.constant.RedisConstant; import org.springframework.web.multipart.MultipartFile;
import com.fanxb.bookmark.common.entity.Bookmark;
import com.fanxb.bookmark.common.entity.EsEntity;
import com.fanxb.bookmark.common.entity.redis.UserBookmarkUpdate;
import com.fanxb.bookmark.common.exception.FormDataException;
import com.fanxb.bookmark.common.util.EsUtil;
import com.fanxb.bookmark.common.util.RedisUtil;
import com.fanxb.bookmark.common.util.UserContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.InputStream; import java.io.InputStream;
import java.util.*; import java.util.List;
import java.util.Map;
import java.util.Set;
/** /**
* 类功能简述 * Created with IntelliJ IDEA
* 类功能详述
* *
* @author fanxb * @author fanxb
* @date 2019/7/8 15:00 * Date: 2020/3/29
* Time: 12:25
*/ */
@Service public interface BookmarkService {
@Slf4j
public class BookmarkService {
/** /**
* chrome导出书签tag * chrome导出书签tag
*/ */
private static final String DT = "dt"; String DT = "dt";
private static final String A = "a"; String A = "a";
@Autowired
private BookmarkDao bookmarkDao;
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private EsUtil esUtil;
/**
* Description: 解析书签文件
*
* @param stream 输入流
* @param path 存放路径
* @author fanxb
* @date 2019/7/9 18:44
*/
@Transactional(rollbackFor = Exception.class)
public void parseBookmarkFile(int userId, InputStream stream, String path) throws Exception {
Document doc = Jsoup.parse(stream, "utf-8", "");
Elements elements = doc.select("html>body>dl>dt");
//获取当前层sort最大值
Integer sortBase = bookmarkDao.selectMaxSort(userId, path);
if (sortBase == null) {
sortBase = 0;
}
int count = 0;
// 將要插入es的书签数据放到list中,最后一次插入尽量避免mysql回滚了但是es插入了
List<EsEntity> insertEsList = new ArrayList<>();
for (int i = 0, length = elements.size(); i < length; i++) {
if (i == 0) {
Elements firstChildren = elements.get(0).child(1).children();
count = firstChildren.size();
for (int j = 0; j < count; j++) {
dealBookmark(userId, firstChildren.get(j), path, sortBase + j, insertEsList);
}
} else {
dealBookmark(userId, elements.get(i), path, sortBase + count + i - 1, insertEsList);
}
}
redisTemplate.opsForList().leftPush(RedisConstant.BOOKMARK_UPDATE_TIME, new UserBookmarkUpdate(userId, System.currentTimeMillis()).toString());
esUtil.insertBatch(EsConstant.BOOKMARK_INDEX, insertEsList);
}
/**
* Description: 处理html节点解析出文件夹和书签
*
* @param ele 待处理节点
* @param path 节点路径不包含自身
* @param sort 当前层级中的排序序号
* @author fanxb
* @date 2019/7/8 14:49
*/
private void dealBookmark(int userId, Element ele, String path, int sort, List<EsEntity> insertList) {
if (!DT.equalsIgnoreCase(ele.tagName())) {
return;
}
Element first = ele.child(0);
if (A.equalsIgnoreCase(first.tagName())) {
//说明为链接
Bookmark node = new Bookmark(userId, path, first.ownText(), first.attr("href"), first.attr("icon")
, Long.parseLong(first.attr("add_date")) * 1000, sort);
//存入数据库
insertOne(node);
insertList.add(new EsEntity<>(node.getBookmarkId().toString(), new BookmarkEs(node)));
} else {
//说明为文件夹
Bookmark node = new Bookmark(userId, path, first.ownText(), Long.parseLong(first.attr("add_date")) * 1000, sort);
Integer sortBase = 0;
if (insertOne(node)) {
sortBase = bookmarkDao.selectMaxSort(node.getUserId(), path);
if (sortBase == null) {
sortBase = 0;
}
}
String childPath = path + "." + node.getBookmarkId();
Elements children = ele.child(1).children();
for (int i = 0, size = children.size(); i < size; i++) {
dealBookmark(userId, children.get(i), childPath, sortBase + i + 1, insertList);
}
}
}
/**
* Description: 插入一条书签如果已经存在同名书签将跳过
*
* @param node node
* @return boolean 如果已经存在返回true否则false
* @author fanxb
* @date 2019/7/8 17:25
*/
private boolean insertOne(Bookmark node) {
//先根据name,userId,parentId获取此节点id
Integer id = bookmarkDao.selectIdByUserIdAndNameAndPath(node.getUserId(), node.getName(), node.getPath());
if (id == null) {
bookmarkDao.insertOne(node);
return false;
} else {
node.setBookmarkId(id);
return true;
}
}
/**
* 功能描述: 获取某个用户的书签map
*
* @param userId userId
* @return java.util.Map<java.lang.String, java.util.List < com.fanxb.bookmark.common.entity.Bookmark>>
* @author fanxb
* @date 2019/12/14 0:02
*/
public Map<String, List<Bookmark>> getOneBookmarkTree(int userId) {
List<Bookmark> list = bookmarkDao.getListByUserId(userId);
Map<String, List<Bookmark>> map = new HashMap<>(50);
list.forEach(item -> {
map.computeIfAbsent(item.getPath(), k -> new ArrayList<>());
map.get(item.getPath()).add(item);
});
return map;
}
/** /**
* Description: 根据userId和path获取书签列表 * Description: 根据userId和path获取书签列表
* *
* @param userId userId * @param userId userId
* @param path path * @param path path
* @return java.util.List<com.fanxb.bookmark.common.entity.Bookmark> * @return java.util.List<com.fanxb.bookmark.common.entity.po.Bookmark>
* @author fanxb * @author fanxb
* @date 2019/7/15 13:40 * @date 2019/7/15 13:40
*/ */
public List<Bookmark> getBookmarkListByPath(int userId, String path) { List<Bookmark> getBookmarkListByPath(int userId, String path);
return bookmarkDao.getListByUserIdAndPath(userId, path);
}
/** /**
* Description: 批量删除书签 * 功能描述: 获取某个用户的书签map
* *
* @param userId 用户id * @param userId userId
* @param folderIdList 书签文件夹id list * @return java.util.Map<java.lang.String, java.util.List < com.fanxb.bookmark.common.entity.po.Bookmark>>
* @param bookmarkIdList 书签id list
* @author fanxb * @author fanxb
* @date 2019/7/12 14:09 * @date 2019/12/14 0:02
*/ */
@Transactional(rollbackFor = Exception.class) Map<String, List<Bookmark>> getOneBookmarkTree(int userId);
public void batchDelete(int userId, List<Integer> folderIdList, List<Integer> bookmarkIdList) {
Set<Integer> set = new HashSet<>(); /**
for (Integer item : folderIdList) { * Description: 解析书签文件
set.addAll(bookmarkDao.getChildrenBookmarkId(userId, item)); *
bookmarkDao.deleteUserFolder(userId, item); * @param stream 输入流
bookmarkIdList.add(item); * @param path 存放路径
} * @param userId userId
if (bookmarkIdList.size() > 0) { * @throws Exception 各种异常
bookmarkDao.deleteUserBookmark(userId, bookmarkIdList); * @author fanxb
} * @date 2019/7/9 18:44
set.addAll(bookmarkIdList); */
redisTemplate.opsForList().leftPush(RedisConstant.BOOKMARK_UPDATE_TIME, new UserBookmarkUpdate(userId, System.currentTimeMillis()).toString()); void parseBookmarkFile(int userId, MultipartFile file, String path) throws Exception;
//es 中批量删除
esUtil.deleteBatch(EsConstant.BOOKMARK_INDEX, set);
}
/** /**
* Description: 详情 * Description: 详情
* *
* @param bookmark 插入一条记录 * @param bookmark 插入一条记录
* @return com.fanxb.bookmark.common.entity.Bookmark * @return com.fanxb.bookmark.common.entity.po.Bookmark
* @author fanxb * @author fanxb
* @date 2019/7/12 17:18 * @date 2019/7/12 17:18
*/ */
@Transactional(rollbackFor = Exception.class) Bookmark addOne(Bookmark bookmark);
public Bookmark addOne(Bookmark bookmark) {
int userId = UserContextHolder.get().getUserId();
Integer sort = bookmarkDao.selectMaxSort(userId, bookmark.getPath());
bookmark.setSort(sort == null ? 1 : sort + 1);
bookmark.setUserId(userId);
bookmark.setCreateTime(System.currentTimeMillis());
bookmark.setAddTime(bookmark.getCreateTime());
try {
bookmarkDao.insertOne(bookmark);
} catch (DuplicateKeyException e) {
throw new FormDataException("同级目录下不能存在相同名称的数据");
}
//如果是书签插入到es中
if (bookmark.getType() == 0) {
esUtil.insertOrUpdateOne(EsConstant.BOOKMARK_INDEX,
new EsEntity<>(bookmark.getBookmarkId().toString(), new BookmarkEs(bookmark)));
}
redisTemplate.opsForList().leftPush(RedisConstant.BOOKMARK_UPDATE_TIME, new UserBookmarkUpdate(userId, System.currentTimeMillis()).toString());
return bookmark;
}
/** /**
* Description: 编辑某个用户的某个书签 * Description: 编辑某个用户的某个书签
* *
* @param userId userId * @param userId userId
* @param bookmark bookmark * @param bookmark bookmark
* @return 更新后的icon
* @author fanxb * @author fanxb
* @date 2019/7/17 14:42 * @date 2019/7/17 14:42
*/ */
@Transactional(rollbackFor = Exception.class) String updateOne(int userId, Bookmark bookmark);
public void updateOne(int userId, Bookmark bookmark) {
bookmark.setUserId(userId);
bookmarkDao.editBookmark(bookmark);
if (bookmark.getType() == 0) {
esUtil.insertOrUpdateOne(EsConstant.BOOKMARK_INDEX,
new EsEntity<>(bookmark.getBookmarkId().toString(), new BookmarkEs(bookmark)));
}
redisTemplate.opsForList().leftPush(RedisConstant.BOOKMARK_UPDATE_TIME, new UserBookmarkUpdate(userId, System.currentTimeMillis()).toString());
}
/**
* Description: 批量删除书签
*
* @param userId 用户id
* @param pathList 要删除的路径list
* @param bookmarkIdList 书签id list
* @author fanxb
* @date 2019/7/12 14:09
*/
void batchDelete(int userId, List<String> pathList, List<Integer> bookmarkIdList);
@Transactional(rollbackFor = Exception.class) /**
public void moveNode(int userId, MoveNodeBody body) { * 功能描述: 移动一个节点
if (body.getSort() == -1) { *
Integer max = bookmarkDao.selectMaxSort(userId, body.getTargetPath()); * @param userId userId
body.setSort(max == null ? 1 : max); * @param body body
} else { * @author 123
//更新目标节点的sort * @date 2020/3/29 12:30
bookmarkDao.sortPlus(userId, body.getTargetPath(), body.getSort()); */
} void moveNode(int userId, MoveNodeBody body);
//如果目标位置和当前位置不在一个层级中需要更新子节点的path
if (!body.getTargetPath().equals(body.getSourcePath())) {
bookmarkDao.updateChildrenPath(userId, body.getSourcePath() + "." + body.getBookmarkId()
, body.getTargetPath() + "." + body.getBookmarkId());
}
//更新被移动节点的path和sort
bookmarkDao.updatePathAndSort(userId, body.getBookmarkId(), body.getTargetPath(), body.getSort());
redisTemplate.opsForList().leftPush(RedisConstant.BOOKMARK_UPDATE_TIME, new UserBookmarkUpdate(userId, System.currentTimeMillis()).toString());
log.info("{},从{}移动到{},sort:{}", userId, body.getSourcePath(), body.getTargetPath(), body.getSort());
}
/** /**
* Description: 根据context搜索 * Description: 根据context搜索
* *
* @param userId userId * @param userId userId
* @param context context * @param context context
* @return es搜索结果
* @author fanxb * @author fanxb
* @date 2019/7/25 10:45 * @date 2019/7/25 10:45
*/ */
public List<BookmarkEs> searchUserBookmark(int userId, String context) { List<BookmarkEs> searchUserBookmark(int userId, String context);
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
boolQueryBuilder.must(QueryBuilders.termQuery("userId", userId));
boolQueryBuilder.must(QueryBuilders.multiMatchQuery(context, "name", "url"));
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.size(5);
builder.query(boolQueryBuilder);
return esUtil.search(EsConstant.BOOKMARK_INDEX, builder, BookmarkEs.class);
}
/** /**
* Description: 将某个用户的书签数据mysql同步到es中 * 功能描述: 当前用户书签访问次数+1
* *
* @param id 书签id
* @author fanxb * @author fanxb
* @date 2019/7/26 11:27 * @date 2020/5/12 10:21
*/ */
public void syncUserBookmark(int userId) { void visitNumPlus(int id);
//删除旧的数据
esUtil.deleteByQuery(EsConstant.BOOKMARK_INDEX, new TermQueryBuilder("userId", userId));
int index = 0;
int size = 500;
List<EsEntity> res = new ArrayList<>();
do {
res.clear();
bookmarkDao.selectBookmarkEsByUserIdAndType(userId, 0, index, size)
.forEach(item -> res.add(new EsEntity<>(item.getBookmarkId().toString(), item)));
if (res.size() > 0) {
esUtil.insertBatch(EsConstant.BOOKMARK_INDEX, res);
}
index += size;
} while (res.size() == 500);
} /**
* 功能描述: 获取用户访问次数前num的书签数据
*
* @param num 获取条数
* @return java.util.List<com.fanxb.bookmark.common.entity.po.Bookmark>
* @author fanxb
* @date 2020/8/26 15:54
*/
List<Bookmark> userPopular(int num);
/**
* 更新某个用户的icon数据
*
* @param userId 用户id
* @author fanxb
* @date 2021/3/11
**/
void updateUserBookmarkIcon(int userId);
/***
* 检查无父节点的数据
* @author fanxb
* @param delete 是否删除数据
* @param userId 用户id
* @return java.util.List<java.lang.String>
* @date 2021/3/17
**/
Set<String> dealBadBookmark(boolean delete, int userId);
} }

View File

@ -0,0 +1,39 @@
package com.fanxb.bookmark.business.bookmark.service;
import com.fanxb.bookmark.business.bookmark.entity.po.PInBookmarkPo;
import com.fanxb.bookmark.business.bookmark.entity.vo.HomePinItemVo;
import java.util.List;
/**
* 首页相关的书签接口
*
* @author fanxb
*/
public interface HomePinService {
/**
* 获取固定的标签
*
* @return {@link List<HomePinItemVo>}
* @author fanxb
*/
List<HomePinItemVo> getHomePinList();
/**
* 新增一个
*
* @return {@link HomePinItemVo}
* @author fanxb
*/
PInBookmarkPo addOne(PInBookmarkPo po);
/**
* 删除一个
*
* @param id id
* @author fanxb
*/
void delete(int id);
}

View File

@ -0,0 +1,66 @@
package com.fanxb.bookmark.business.bookmark.service;
import com.fanxb.bookmark.common.entity.po.Bookmark;
import java.util.List;
/**
* Created with IntelliJ IDEA
*
* @author fanxb
* Date: 2020/3/18
* Time: 23:47
*/
public interface PinYinService {
/**
* 分隔符
*/
String PARTITION = "||";
/**
* 拼音接口路径
*/
String PATH = "/pinyinChange";
/**
* 分页查询页大小
*/
int SIZE = 500;
/**
* 功能描述: 首次上线用于全量初始化
*
* @author fanxb
* @date 2020/3/22 21:40
*/
void changeAll();
/**
* 处理bookmark searchKey
*
* @param bookmark 处理单个
* @return java.util.List<com.fanxb.bookmark.common.entity.po.Bookmark>
* @author fanxb
* @date 2021/3/13
**/
Bookmark changeBookmark(Bookmark bookmark);
/**
* 处理bookmarks searchKey
*
* @param bookmarks 待处理舒淇啊你列表
* @return java.util.List<com.fanxb.bookmark.common.entity.po.Bookmark>
* @author fanxb
* @date 2021/3/13
**/
List<Bookmark> changeBookmarks(List<Bookmark> bookmarks);
/**
* 功能描述:返回用于前端搜索的key
*
* @param stringList 待拼音化的字符串
* @return java.util.List<java.lang.String>
* @author fanxb
* @date 2020/3/22 21:38
*/
List<String> changeStrings(List<String> stringList);
}

View File

@ -0,0 +1,34 @@
package com.fanxb.bookmark.business.bookmark.service.impl;
import com.fanxb.bookmark.business.api.BookmarkApi;
import com.fanxb.bookmark.business.bookmark.service.BookmarkService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Set;
/**
* bookmark模块api暴露
*
* @author fanxb
* @date 2021/3/13
**/
@Service
public class BookmarkApiImpl implements BookmarkApi {
private final BookmarkService bookmarkService;
@Autowired
public BookmarkApiImpl(BookmarkService bookmarkService) {
this.bookmarkService = bookmarkService;
}
@Override
public void updateUserBookmarkIcon(int userId) {
bookmarkService.updateUserBookmarkIcon(userId);
}
@Override
public Set<String> dealBadBookmark(boolean delete, int userId) {
return bookmarkService.dealBadBookmark(delete, userId);
}
}

View File

@ -0,0 +1,69 @@
package com.fanxb.bookmark.business.bookmark.service.impl;
import com.fanxb.bookmark.business.bookmark.dao.BookmarkDao;
import com.fanxb.bookmark.business.bookmark.entity.BookmarkEs;
import com.fanxb.bookmark.business.bookmark.service.BookmarkBackupService;
import com.fanxb.bookmark.common.constant.EsConstant;
import com.fanxb.bookmark.common.entity.po.Bookmark;
import com.fanxb.bookmark.common.entity.EsEntity;
import com.fanxb.bookmark.common.util.EsUtil;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* Created with IntelliJ IDEA
* Created By Fxb
* Date: 2019/11/12
* Time: 0:22
*
* @author fanxb
*/
@Service
public class BookmarkBackupServiceImpl implements BookmarkBackupService {
@Autowired
private BookmarkDao bookmarkDao;
@Autowired
private EsUtil esUtil;
/**
* 一次同步BACKUP_SIZE条到es中
*/
private static final int BACKUP_SIZE = 500;
@Override
public void backupToEs() {
int start = 0;
List<Bookmark> list;
while ((list = bookmarkDao.getBookmarkListPage(BACKUP_SIZE, start)).size() != 0) {
List<EsEntity<BookmarkEs>> batchList = new ArrayList<>(list.size());
list.forEach(item -> batchList.add(new EsEntity<>(item.getBookmarkId().toString(), new BookmarkEs(item))));
esUtil.insertBatch(EsConstant.BOOKMARK_INDEX, batchList);
start += BACKUP_SIZE;
}
}
@Override
public void syncUserBookmark(int userId) {
//删除旧的数据
esUtil.deleteByQuery(EsConstant.BOOKMARK_INDEX, new TermQueryBuilder("userId", userId));
int index = 0;
int size = 500;
List<EsEntity<BookmarkEs>> res = new ArrayList<>();
do {
res.clear();
bookmarkDao.selectBookmarkEsByUserIdAndType(userId, 0, index, size)
.forEach(item -> res.add(new EsEntity<>(item.getBookmarkId().toString(), item)));
if (res.size() > 0) {
esUtil.insertBatch(EsConstant.BOOKMARK_INDEX, res);
}
index += size;
} while (res.size() == 500);
}
}

View File

@ -0,0 +1,503 @@
package com.fanxb.bookmark.business.bookmark.service.impl;
import cn.hutool.core.codec.Base64Decoder;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.*;
import cn.hutool.core.util.HashUtil;
import com.alibaba.fastjson.JSON;
import com.fanxb.bookmark.business.api.UserApi;
import com.fanxb.bookmark.business.bookmark.constant.FileConstant;
import com.fanxb.bookmark.business.bookmark.dao.BookmarkDao;
import com.fanxb.bookmark.business.bookmark.dao.HostIconDao;
import com.fanxb.bookmark.business.bookmark.entity.BookmarkEs;
import com.fanxb.bookmark.business.bookmark.entity.MoveNodeBody;
import com.fanxb.bookmark.business.bookmark.entity.redis.BookmarkDeleteMessage;
import com.fanxb.bookmark.business.bookmark.entity.redis.VisitNumPlus;
import com.fanxb.bookmark.business.bookmark.service.BookmarkService;
import com.fanxb.bookmark.business.bookmark.service.PinYinService;
import com.fanxb.bookmark.common.constant.CommonConstant;
import com.fanxb.bookmark.common.constant.EsConstant;
import com.fanxb.bookmark.common.constant.RedisConstant;
import com.fanxb.bookmark.common.entity.po.Bookmark;
import com.fanxb.bookmark.common.exception.CustomException;
import com.fanxb.bookmark.common.util.*;
import com.mysql.cj.conf.url.SingleConnectionUrl;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Request;
import okhttp3.Response;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.awt.print.Book;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.*;
import java.util.stream.Collectors;
/**
* 类功能简述
* 类功能详述
*
* @author fanxb
*/
@Service
@Slf4j
public class BookmarkServiceImpl implements BookmarkService {
@Value("${urlIconAddress}")
private String urlIconAddress;
private final BookmarkDao bookmarkDao;
private final PinYinService pinYinService;
private final UserApi userApi;
private final EsUtil esUtil;
private final HostIconDao hostIconDao;
@Autowired
public BookmarkServiceImpl(BookmarkDao bookmarkDao, PinYinService pinYinService, UserApi userApi, EsUtil esUtil, HostIconDao hostIconDao) {
this.bookmarkDao = bookmarkDao;
this.pinYinService = pinYinService;
this.userApi = userApi;
this.esUtil = esUtil;
this.hostIconDao = hostIconDao;
}
@Override
@Transactional(rollbackFor = Exception.class)
public void parseBookmarkFile(int userId, MultipartFile file, String path) throws Exception {
List<Bookmark> bookmarks = new ArrayList<>();
//获取当前层sort最大值
Integer sortBase = bookmarkDao.selectMaxSort(userId, path);
if (sortBase == null) {
sortBase = 0;
}
if (file.getOriginalFilename().endsWith(".db3")) {
//处理db文件
readFromOneEnv(bookmarks, userId, file, path, sortBase);
} else {
InputStream stream = file.getInputStream();
Document doc = Jsoup.parse(stream, "utf-8", "");
Elements elements = doc.select("html>body>dl>dt");
for (int i = 0, length = elements.size(); i < length; i++) {
dealBookmark(userId, elements.get(i), path, sortBase + i, bookmarks);
}
}
//每一千条处理插入一次,批量更新搜索字段
List<Bookmark> tempList = new ArrayList<>(1000);
for (int i = 0; i < bookmarks.size(); i++) {
tempList.add(bookmarks.get(i));
if (tempList.size() == 1000 || i == bookmarks.size() - 1) {
tempList = pinYinService.changeBookmarks(tempList);
bookmarkDao.updateSearchKeyBatch(tempList);
tempList.clear();
}
}
userApi.versionPlus(userId);
//异步更新icon
ThreadPoolUtil.execute(() -> {
updateUserBookmarkIcon(userId);
userApi.versionPlus(userId);
});
}
/**
* Description: 处理html节点解析出文件夹和书签
*
* @param ele 待处理节点
* @param path 节点路径不包含自身
* @param sort 当前层级中的排序序号
* @author fanxb
*/
private void dealBookmark(int userId, Element ele, String path, int sort, List<Bookmark> bookmarks) {
if (!DT.equalsIgnoreCase(ele.tagName())) {
return;
}
Element first = ele.child(0);
if (A.equalsIgnoreCase(first.tagName())) {
//说明为链接
Bookmark node = new Bookmark(userId, path, first.ownText(), first.attr("href"), ""
, Long.parseLong(first.attr("add_date")) * 1000, sort);
//存入数据库
insertOne(node);
bookmarks.add(node);
} else {
//说明为文件夹
Bookmark node = new Bookmark(userId, path, first.ownText(), Long.parseLong(first.attr("add_date")) * 1000, sort);
Integer sortBase = 0;
//同名文件夹将会合并
if (insertOne(node)) {
sortBase = bookmarkDao.selectMaxSort(node.getUserId(), path);
if (sortBase == null) {
sortBase = 0;
}
}
String childPath = path + "." + node.getBookmarkId();
Elements children = ele.child(1).children();
for (int i = 0, size = children.size(); i < size; i++) {
dealBookmark(userId, children.get(i), childPath, sortBase + i + 1, bookmarks);
}
}
}
/**
* 处理oneenv的导出
*
* @param bookmarks 书签列表
* @param userId 用户id
* @param file file
* @param path path
* @param sort sort
*/
private void readFromOneEnv(List<Bookmark> bookmarks, int userId, MultipartFile file, String path, int sort) {
String filePath = CommonConstant.fileSavePath + "/files/" + IdUtil.simpleUUID() + ".db3";
try {
file.transferTo(FileUtil.newFile(filePath));
try (Connection conn = DriverManager.getConnection("jdbc:sqlite:" + filePath)) {
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery("select * from on_categorys");
Map<Long, Bookmark> folderMap = new HashMap<>();
Map<Long, Integer> childSortBaseMap = new HashMap<>();
while (rs.next()) {
long addTime = rs.getLong("add_time");
Bookmark folder = new Bookmark(userId, path, StrUtil.nullToEmpty(rs.getString("name")), addTime == 0 ? System.currentTimeMillis() : addTime * 1000, sort++);
int childSortBase = 0;
if (insertOne(folder)) {
childSortBase = ObjectUtil.defaultIfNull(bookmarkDao.selectMaxSort(userId, path), 0);
}
long id = rs.getLong("id");
folderMap.put(id, folder);
childSortBaseMap.put(id, childSortBase);
}
rs.close();
rs = stat.executeQuery("select * from on_links");
while (rs.next()) {
long fId = rs.getLong("fid");
long addTime = rs.getLong("add_time");
int tempSort = childSortBaseMap.get(fId);
childSortBaseMap.put(fId, tempSort + 1);
Bookmark folder = folderMap.get(fId);
String curPath = folder == null ? "" : folder.getPath() + "." + folder.getBookmarkId();
Bookmark bookmark = new Bookmark(userId, curPath, StrUtil.nullToEmpty(rs.getString("title"))
, StrUtil.nullToEmpty(rs.getString("url")), "", addTime == 0 ? System.currentTimeMillis() : addTime * 1000, tempSort);
bookmarks.add(bookmark);
insertOne(bookmark);
}
rs.close();
stat.close();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Description: 插入一条书签如果已经存在同名书签将跳过
*
* @param node node
* @return boolean 如果已经存在返回true否则false
* @author fanxb
*/
private boolean insertOne(Bookmark node) {
//先根据name,userId,parentId获取此节点id
Integer id = bookmarkDao.selectIdByUserIdAndNameAndPath(node.getUserId(), node.getName(), node.getPath());
if (id == null) {
bookmarkDao.insertOne(node);
return false;
} else {
node.setBookmarkId(id);
return true;
}
}
@Override
public Map<String, List<Bookmark>> getOneBookmarkTree(int userId) {
List<Bookmark> list = bookmarkDao.getListByUserId(userId);
Map<String, List<Bookmark>> map = new HashMap<>(50);
list.forEach(item -> {
map.computeIfAbsent(item.getPath(), k -> new ArrayList<>());
map.get(item.getPath()).add(item);
});
return map;
}
@Override
public List<Bookmark> getBookmarkListByPath(int userId, String path) {
return bookmarkDao.getListByUserIdAndPath(userId, path);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void batchDelete(int userId, List<String> pathList, List<Integer> bookmarkIdList) {
//所有要删除的书签id
Set<String> set = new HashSet<>();
for (String path : pathList) {
Integer id = Integer.parseInt(ArrayUtil.reverse(path.split("\\."))[0]);
set.addAll(bookmarkDao.getChildrenBookmarkId(userId, path).stream().map(String::valueOf).collect(Collectors.toSet()));
//删除此文件夹所有的子节点
bookmarkDao.deleteUserFolder(userId, path);
bookmarkIdList.add(id);
}
if (bookmarkIdList.size() > 0) {
bookmarkDao.deleteUserBookmark(userId, bookmarkIdList);
set.addAll(bookmarkIdList.stream().map(String::valueOf).collect(Collectors.toSet()));
}
RedisUtil.addToMq(RedisConstant.BOOKMARK_DELETE_ES, new BookmarkDeleteMessage(userId, set));
userApi.versionPlus(userId);
}
@Override
@Transactional(rollbackFor = Exception.class)
public Bookmark addOne(Bookmark bookmark) {
int userId = UserContextHolder.get().getUserId();
Integer sort = bookmarkDao.selectMaxSort(userId, bookmark.getPath());
bookmark.setSort(sort == null ? 1 : sort + 1);
bookmark.setUserId(userId);
bookmark.setCreateTime(System.currentTimeMillis());
bookmark.setAddTime(bookmark.getCreateTime());
bookmark.setIcon(bookmark.getType() == 1 ? "" : getIconPath(bookmark.getUrl(), bookmark.getIcon(), bookmark.getIconUrl(), true));
//文件夹和书签都建立搜索key
pinYinService.changeBookmark(bookmark);
bookmarkDao.insertOne(bookmark);
userApi.versionPlus(userId);
if (StrUtil.isEmpty(bookmark.getIcon()) && bookmark.getType() == 0) {
updateIconAsync(bookmark.getBookmarkId(), bookmark.getUrl(), userId);
}
return bookmark;
}
@Override
@Transactional(rollbackFor = Exception.class)
public String updateOne(int userId, Bookmark bookmark) {
bookmark.setUserId(userId);
if (bookmark.getType() == 0) {
pinYinService.changeBookmark(bookmark);
bookmark.setIcon(getIconPath(bookmark.getUrl(), null, null, true));
if (StrUtil.isEmpty(bookmark.getIcon())) {
updateIconAsync(bookmark.getBookmarkId(), bookmark.getUrl(), userId);
}
}
bookmarkDao.editBookmark(bookmark);
userApi.versionPlus(userId);
return bookmark.getIcon();
}
/**
* 异步更新书签icon
*
* @param id 书签id
* @param url 书签url
* @param userId userId
*/
private void updateIconAsync(int id, String url, int userId) {
ThreadPoolUtil.execute(() -> {
String icon = getIconPath(url, null, null, false);
if (StrUtil.isEmpty(icon)) {
return;
}
bookmarkDao.updateIcon(id, icon);
});
}
@Override
@Transactional(rollbackFor = Exception.class)
public void moveNode(int userId, MoveNodeBody body) {
if (body.getSort() == -1) {
Integer max = bookmarkDao.selectMaxSort(userId, body.getTargetPath());
body.setSort(max == null ? 1 : max);
} else {
//更新目标节点的sort
bookmarkDao.sortPlus(userId, body.getTargetPath(), body.getSort());
}
//如果目标位置和当前位置不在一个层级中需要更新子节点的path
if (!body.getTargetPath().equals(body.getSourcePath())) {
bookmarkDao.updateChildrenPath(userId, body.getSourcePath() + "." + body.getBookmarkId()
, body.getTargetPath() + "." + body.getBookmarkId());
}
//更新被移动节点的path和sort
bookmarkDao.updatePathAndSort(userId, body.getBookmarkId(), body.getTargetPath(), body.getSort());
userApi.versionPlus(userId);
}
@Override
public List<BookmarkEs> searchUserBookmark(int userId, String context) {
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
boolQueryBuilder.must(QueryBuilders.termQuery("userId", userId));
boolQueryBuilder.must(QueryBuilders.multiMatchQuery(context, "name", "url"));
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.size(5);
builder.query(boolQueryBuilder);
return esUtil.search(EsConstant.BOOKMARK_INDEX, builder, BookmarkEs.class);
}
@Override
public void visitNumPlus(int id) {
VisitNumPlus item = new VisitNumPlus(UserContextHolder.get().getUserId(), id);
RedisUtil.addToMq(RedisConstant.BOOKMARK_VISIT_NUM_PLUS, JSON.toJSONString(item));
}
@Override
public List<Bookmark> userPopular(int num) {
return bookmarkDao.selectPopular(UserContextHolder.get().getUserId(), num);
}
@Override
public void updateUserBookmarkIcon(int userId) {
log.info("开始更新:{}", userId);
int size = 100;
int start = 0;
List<Bookmark> deal;
while (!(deal = bookmarkDao.selectUserNoIcon(userId, start, size)).isEmpty()) {
start += size;
deal.forEach(item -> {
String icon = getIconPath(item.getUrl(), null, null, false);
if (StrUtil.isNotEmpty(icon)) {
bookmarkDao.updateIcon(item.getBookmarkId(), icon);
}
});
}
userApi.versionPlus(userId);
}
@Override
public Set<String> dealBadBookmark(boolean delete, int userId) {
List<Bookmark> bookmarks = bookmarkDao.selectBookmarkIdPathByUserId(userId);
Set<Integer> idSet = new HashSet<>(bookmarks.size());
bookmarks.forEach(item -> idSet.add(item.getBookmarkId()));
Set<String> resPath = new HashSet<>();
bookmarks.forEach(item -> {
if (StrUtil.isEmpty(item.getPath())) {
return;
}
String parentId = item.getPath().substring(item.getPath().lastIndexOf(".") + 1);
if (!idSet.contains(Integer.valueOf(parentId))) {
resPath.add(item.getPath());
}
});
if (delete && resPath.size() > 0) {
resPath.forEach(item -> bookmarkDao.deleteUserFolder(userId, item));
userApi.versionPlus(userId);
}
return resPath;
}
/**
* 获取icon,通过网络获取或者从base64还原
*
* @param url 书签url路径
* @param icon base64编码的icon
* @param iconUrl base64编码的文件文件名,用于获取文件名后缀
* @param quick 是否快速获取
* @return {@link String}
* @author fanxb
*/
private String getIconPath(String url, String icon, String iconUrl, boolean quick) {
String host;
try {
URL urlObj = new URL(url);
host = urlObj.getAuthority();
} catch (Exception e) {
log.warn("url无法解析出domain:{}", url);
return "";
}
if (StrUtil.isNotBlank(icon)) {
//优先从base64还原出图片
try {
byte[] b = Base64Decoder.decode(icon.substring(icon.indexOf(",") + 1));
String iconPath = saveToFile(iconUrl, host, b);
hostIconDao.deleteByHost(host);
hostIconDao.insert(host, iconPath);
return iconPath;
} catch (Exception e) {
log.error("解析base64获取icon故障:{}", iconUrl, e);
}
}
String iconPath = hostIconDao.selectByHost(host);
if (iconPath != null) {
return iconPath;
}
//再根据url解析
iconPath = saveFile(host, urlIconAddress + "/icon?url=" + host + "&size=16..128..256", quick);
if (StrUtil.isNotEmpty(iconPath)) {
hostIconDao.insert(host, iconPath);
}
return iconPath;
}
/**
* 保存文件到icon路径
*
* @param host host
* @param url url
* @param quick 是否快速获取,快速获取超时时间1s
* @return {@link String}
* @author FleyX
*/
private String saveFile(String host, String url, boolean quick) {
try (Response res = (quick ? HttpUtil.getSHORT_CLIENT() : HttpUtil.getClient(false)).newCall(new Request.Builder().url(url)
.header("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36 Edg/100.0.1185.36")
.get().build()).execute()) {
assert res.body() != null;
if (!HttpUtil.checkIsOk(res.code())) {
throw new CustomException("请求错误:" + res.code());
}
byte[] data = res.body().byteStream().readAllBytes();
if (data.length > 0) {
String iconUrl = new URL(res.request().url().toString()).getPath();
return saveToFile(iconUrl, host, data);
} else {
log.info("未获取到icon:{}", url);
}
} catch (SocketTimeoutException timeoutException) {
log.info("获取icon超时{}", host);
} catch (Exception e) {
log.error("url获取icon故障:{}", url, e);
}
return "";
}
/**
* 保存到文件中
*
* @param iconUrl icon文件名
* @param host host
* @param b 数据
* @return {@link String}
* @author FleyX
*/
private String saveToFile(String iconUrl, String host, byte[] b) {
String fileName = host.replace(":", ".") + iconUrl.substring(iconUrl.lastIndexOf("."));
String filePath = Paths.get(FileConstant.FAVICON_PATH, host.replace("www", "").replaceAll("\\.", "").substring(0, 2), fileName).toString();
FileUtil.writeBytes(b, Paths.get(CommonConstant.fileSavePath, filePath).toString());
return File.separator + filePath;
}
}

View File

@ -0,0 +1,81 @@
package com.fanxb.bookmark.business.bookmark.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.fanxb.bookmark.business.bookmark.dao.BookmarkDao;
import com.fanxb.bookmark.business.bookmark.dao.PinBookmarkDao;
import com.fanxb.bookmark.business.bookmark.entity.po.PInBookmarkPo;
import com.fanxb.bookmark.business.bookmark.entity.vo.HomePinItemVo;
import com.fanxb.bookmark.business.bookmark.service.BookmarkService;
import com.fanxb.bookmark.business.bookmark.service.HomePinService;
import com.fanxb.bookmark.common.entity.UserContext;
import com.fanxb.bookmark.common.entity.po.Bookmark;
import com.fanxb.bookmark.common.exception.CustomException;
import com.fanxb.bookmark.common.util.UserContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author fanxb
*/
@Service
@Slf4j
public class HomePinServiceImpl implements HomePinService {
private final PinBookmarkDao pinBookmarkDao;
private final BookmarkDao bookmarkDao;
@Autowired
public HomePinServiceImpl(PinBookmarkDao pinBookmarkDao, BookmarkDao bookmarkDao) {
this.pinBookmarkDao = pinBookmarkDao;
this.bookmarkDao = bookmarkDao;
}
/**
* 首页固定书签最大数量
*/
private static final int MAX_PIN = 20;
@Override
public List<HomePinItemVo> getHomePinList() {
List<HomePinItemVo> res = new ArrayList<>(MAX_PIN);
if (UserContextHolder.get() != null) {
res.addAll(pinBookmarkDao.selectUserPin(UserContextHolder.get().getUserId()));
if (res.size() < MAX_PIN - 1) {
//需要从取到的书签数据中排除已经固定到首页的数据
Set<Integer> existBookmark = res.stream().map(HomePinItemVo::getBookmarkId).collect(Collectors.toSet());
List<Bookmark> bookmarks = bookmarkDao.selectPopular(UserContextHolder.get().getUserId(), MAX_PIN - 1);
res.addAll(bookmarks.stream().filter(item -> !existBookmark.contains(item.getBookmarkId())).limit(MAX_PIN - res.size() - 1)
.map(item -> new HomePinItemVo(null, item.getBookmarkId(), item.getName(), item.getUrl(), item.getIcon())).collect(Collectors.toList()));
}
}
return res;
}
@Override
public PInBookmarkPo addOne(PInBookmarkPo po) {
int userId = UserContextHolder.get().getUserId();
long count = pinBookmarkDao.selectCount(new QueryWrapper<PInBookmarkPo>().eq("userId", userId));
if (count > MAX_PIN) {
throw new CustomException("固定数量已超过最大限制:" + MAX_PIN);
}
po.setUserId(userId);
po.setCreateDate(System.currentTimeMillis());
po.setSort(pinBookmarkDao.getUserMaxSort(userId) + 1);
pinBookmarkDao.insert(po);
return po;
}
@Override
public void delete(int id) {
pinBookmarkDao.deleteById(id);
}
}

View File

@ -0,0 +1,83 @@
package com.fanxb.bookmark.business.bookmark.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import com.fanxb.bookmark.business.api.UserApi;
import com.fanxb.bookmark.business.bookmark.dao.BookmarkDao;
import com.fanxb.bookmark.business.bookmark.service.PinYinService;
import com.fanxb.bookmark.common.entity.po.Bookmark;
import com.fanxb.bookmark.common.exception.CustomException;
import com.fanxb.bookmark.common.util.UserContextHolder;
import com.github.houbb.pinyin.constant.enums.PinyinStyleEnum;
import com.github.houbb.pinyin.util.PinyinHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
/**
* Created with IntelliJ IDEA
*
* @author fanxb
* Date: 2020/3/18
* Time: 23:48
*/
@Service
public class PinYinServiceImpl implements PinYinService {
private final BookmarkDao bookmarkDao;
private final UserApi userApi;
@Autowired
public PinYinServiceImpl(BookmarkDao bookmarkDao, UserApi userApi) {
this.bookmarkDao = bookmarkDao;
this.userApi = userApi;
}
@Override
public void changeAll() {
if (!UserContextHolder.get().getManageUser()) {
throw new CustomException("非管理员用户,无法执行本操作");
}
int i = 0;
while (true) {
List<Bookmark> bookmarks = changeBookmarks(bookmarkDao.selectPinyinEmpty(i, SIZE));
if (bookmarks.size() > 0) {
bookmarkDao.updateSearchKeyBatch(bookmarks);
}
if (bookmarks.size() < SIZE) {
break;
}
i = bookmarks.get(SIZE - 1).getBookmarkId();
}
//更新所有用户版本数据
userApi.allUserVersionPlus();
}
@Override
public Bookmark changeBookmark(Bookmark bookmark) {
return changeBookmarks(Collections.singletonList(bookmark)).get(0);
}
@Override
public List<Bookmark> changeBookmarks(List<Bookmark> bookmarks) {
List<String> resList = changeStrings(bookmarks.stream().map(Bookmark::getName).collect(Collectors.toList()));
for (int j = 0, size = bookmarks.size(); j < size; j++) {
Bookmark bookmark = bookmarks.get(j);
int length = bookmark.getUrl().length();
bookmark.setSearchKey(resList.get(j) + (length == 0 ? "" : (PARTITION + bookmark.getUrl().substring(0, Math.min(length, 50)))));
}
return bookmarks;
}
@Override
public List<String> changeStrings(List<String> stringList) {
return stringList.stream().map(item -> {
List<String> temp = Arrays.stream(PinyinHelper.toPinyin(item.replaceAll(" ", ""), PinyinStyleEnum.NORMAL).split(" "))
.filter(one -> one.length() > 0).collect(Collectors.toList());
return item.toLowerCase(Locale.getDefault()) + PARTITION + CollectionUtil.join(temp, "") + PARTITION
+ temp.stream().map(one -> one.substring(0, 1)).collect(Collectors.joining());
}).collect(Collectors.toList());
}
}

View File

@ -4,22 +4,16 @@
<insert id="insertOne" useGeneratedKeys="true" keyColumn="bookmarkId" keyProperty="bookmarkId"> <insert id="insertOne" useGeneratedKeys="true" keyColumn="bookmarkId" keyProperty="bookmarkId">
insert into bookmark(userId,path,type,name,url,icon,sort,addTime,createTime) insert into bookmark(userId,path,type,name,searchKey,url,icon,sort,addTime,createTime)
value value
( #{userId},#{path},#{type},#{name}, ( #{userId},#{path},#{type},#{name},#{searchKey},#{url},#{icon},
<if test="url == null">
"","",
</if>
<if test="url != null">
#{url},#{icon},
</if>
#{sort},#{addTime},#{createTime}) #{sort},#{addTime},#{createTime})
</insert> </insert>
<select id="selectIdByUserIdAndNameAndPath" resultType="java.lang.Integer"> <select id="selectIdByUserIdAndNameAndPath" resultType="java.lang.Integer">
select bookmarkId select bookmarkId
from bookmark from bookmark
where userId = #{userId} and path = #{path} and name = #{name}; where userId = #{userId} and path = #{path} and name = #{name} limit 1;
</select> </select>
<select id="selectMaxSort" resultType="java.lang.Integer"> <select id="selectMaxSort" resultType="java.lang.Integer">
@ -28,29 +22,30 @@
where userId = #{userId} and path = #{path} where userId = #{userId} and path = #{path}
</select> </select>
<select id="getListByUserId" resultType="com.fanxb.bookmark.common.entity.Bookmark"> <select id="getListByUserId" resultType="com.fanxb.bookmark.common.entity.po.Bookmark">
select select
bookmarkId, bookmarkId,
path, path,
type, type,
name, name,
url, searchKey,
icon, url,
sort icon,
sort
from bookmark from bookmark
where userId = #{userId} where userId = #{userId}
order by path, sort order by path, sort
</select> </select>
<select id="getListByUserIdAndPath" resultType="com.fanxb.bookmark.common.entity.Bookmark"> <select id="getListByUserIdAndPath" resultType="com.fanxb.bookmark.common.entity.po.Bookmark">
select select
bookmarkId, bookmarkId,
path, path,
type, type,
name, name,
url, url,
icon, icon,
sort sort
from bookmark from bookmark
where userId = #{userId} and path = #{path} where userId = #{userId} and path = #{path}
order by sort order by sort
@ -59,24 +54,18 @@
<delete id="deleteUserFolder"> <delete id="deleteUserFolder">
DELETE DELETE
FROM FROM
bookmark bookmark
WHERE WHERE
userId = #{userId} userId = #{userId}
and path LIKE (SELECT a.path and (path = #{path} or path like concat(#{path},".%"))
FROM (SELECT CONCAT(path, '.', '${folderId}', '%') AS path
FROM bookmark
WHERE bookmarkId = #{folderId}) a);
</delete> </delete>
<select id="getChildrenBookmarkId" resultType="integer"> <select id="getChildrenBookmarkId" resultType="integer">
select bookmarkId select bookmarkId
from bookmark from bookmark
where where
userId = #{userId} userId = #{userId}
and path LIKE (SELECT a.path and (path =#{path} or path like concat(#{path},".%") );
FROM (SELECT CONCAT(path, '.', '${folderId}', '%') AS path
FROM bookmark
WHERE bookmarkId = #{folderId}) a);
</select> </select>
<delete id="deleteUserBookmark"> <delete id="deleteUserBookmark">
@ -86,9 +75,9 @@
</foreach> </foreach>
</delete> </delete>
<update id="editBookmark" parameterType="com.fanxb.bookmark.common.entity.Bookmark"> <update id="editBookmark" parameterType="com.fanxb.bookmark.common.entity.po.Bookmark">
update bookmark update bookmark
set name = #{name}, url = #{url} set name = #{name}, url = #{url},searchKey = #{searchKey},icon=#{icon}
where bookmarkId = #{bookmarkId} and userId = #{userId} where bookmarkId = #{bookmarkId} and userId = #{userId}
</update> </update>
@ -112,15 +101,25 @@
<select id="selectBookmarkEsByUserIdAndType" resultType="com.fanxb.bookmark.business.bookmark.entity.BookmarkEs"> <select id="selectBookmarkEsByUserIdAndType" resultType="com.fanxb.bookmark.business.bookmark.entity.BookmarkEs">
select select
userId, userId,
bookmarkId, bookmarkId,
name, name,
url url
from bookmark from bookmark
where userId = #{userId} and type = #{type} where userId = #{userId} and type = #{type}
order by bookmarkId order by bookmarkId
limit ${start}, ${size} limit ${start}, ${size}
</select> </select>
<update id="updateSearchKeyBatch">
UPDATE `bookmark` a JOIN
(
<foreach collection="list" item="item" separator="union">
select #{item.bookmarkId} as bookmarkId,#{item.searchKey} as searchKey
</foreach>
) b USING(bookmarkId)
SET a.searchKey=b.searchKey;
</update>
</mapper> </mapper>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fanxb.bookmark.business.bookmark.dao.PinBookmarkDao">
<delete id="deleteUnExistBookmark">
delete
a
from pin_bookmark a
left join bookmark b on
a.bookmarkId = b.bookmarkId
where a.userId = #{userId}
and b.bookmarkId is null
</delete>
</mapper>

View File

@ -14,6 +14,7 @@
<modules> <modules>
<module>user</module> <module>user</module>
<module>bookmark</module> <module>bookmark</module>
<module>api</module>
</modules> </modules>
<dependencies> <dependencies>
@ -22,12 +23,6 @@
<artifactId>bookmark-common</artifactId> <artifactId>bookmark-common</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies> </dependencies>

View File

@ -11,5 +11,12 @@
<artifactId>bookmark-business-user</artifactId> <artifactId>bookmark-business-user</artifactId>
<dependencies>
<dependency>
<groupId>com.fanxb</groupId>
<artifactId>bookmark-business-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project> </project>

View File

@ -1,10 +1,8 @@
package com.fanxb.bookmark.business.user.constant; package com.fanxb.bookmark.business.user.constant;
import com.fanxb.bookmark.common.constant.Constant; import com.fanxb.bookmark.common.constant.CommonConstant;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.io.File;
import java.nio.file.Paths; import java.nio.file.Paths;
/** /**
@ -20,6 +18,6 @@ public class FileConstant {
/** /**
* 用户头像目录 * 用户头像目录
*/ */
public static String iconPath = Paths.get("files", "public", "icon").toString(); public static String iconPath = Paths.get(CommonConstant.fileSavePath, "files", "public", "icon").toString();
} }

View File

@ -0,0 +1,30 @@
package com.fanxb.bookmark.business.user.consumer;
import com.alibaba.fastjson.JSON;
import com.fanxb.bookmark.business.user.dao.UserDao;
import com.fanxb.bookmark.common.annotation.MqConsumer;
import com.fanxb.bookmark.common.constant.RedisConstant;
import com.fanxb.bookmark.common.entity.redis.RedisConsumer;
import com.fanxb.bookmark.common.entity.redis.UserBookmarkUpdate;
import org.springframework.beans.factory.annotation.Autowired;
/**
* @author fanxb
* @date 2020/1/26 上午11:54
*/
@MqConsumer(RedisConstant.BOOKMARK_UPDATE_VERSION)
public class UserInfoUpdateConsumer implements RedisConsumer {
@Autowired
private UserDao userDao;
@Override
public void deal(String message) {
// int userId = Integer.parseInt(message);
// if (userId == -1) {
// userDao.updateAllBookmarkUpdateVersion();
// } else {
// userDao.updateLastBookmarkUpdateTime(userId);
// }
}
}

View File

@ -1,16 +1,16 @@
package com.fanxb.bookmark.business.user.controller; package com.fanxb.bookmark.business.user.controller;
import com.fanxb.bookmark.business.user.entity.EmailUpdateBody;
import com.fanxb.bookmark.business.user.entity.UpdatePasswordBody;
import com.fanxb.bookmark.business.user.entity.UsernameBody;
import com.fanxb.bookmark.business.user.service.BaseInfoService; import com.fanxb.bookmark.business.user.service.BaseInfoService;
import com.fanxb.bookmark.business.user.vo.EmailUpdateBody;
import com.fanxb.bookmark.business.user.vo.UpdatePasswordBody;
import com.fanxb.bookmark.business.user.vo.UsernameBody;
import com.fanxb.bookmark.common.entity.Result; import com.fanxb.bookmark.common.entity.Result;
import com.fanxb.bookmark.common.entity.po.User;
import com.fanxb.bookmark.common.util.UserContextHolder;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
/** /**
* 类功能简述用户基本信息相关功能 * 类功能简述用户基本信息相关功能
@ -23,8 +23,12 @@ import javax.validation.Valid;
@Validated @Validated
public class BaseInfoController { public class BaseInfoController {
private final BaseInfoService baseInfoService;
@Autowired @Autowired
private BaseInfoService baseInfoService; public BaseInfoController(BaseInfoService baseInfoService) {
this.baseInfoService = baseInfoService;
}
/** /**
* Description: 修改密码 * Description: 修改密码
@ -82,4 +86,17 @@ public class BaseInfoController {
baseInfoService.verifyEmail(secret); baseInfoService.verifyEmail(secret);
return Result.success(null); return Result.success(null);
} }
/**
* 修改用户默认搜索引擎
*
* @author fanxb
* @date 2021/3/14
**/
@PostMapping("/updateSearchEngine")
public Result updateSearchEngine(@RequestBody User user) {
user.setUserId(UserContextHolder.get().getUserId());
baseInfoService.changeDefaultSearchEngine(user);
return Result.success(null);
}
} }

View File

@ -0,0 +1,32 @@
package com.fanxb.bookmark.business.user.controller;
import com.fanxb.bookmark.business.user.entity.Feedback;
import com.fanxb.bookmark.business.user.service.FeedbackService;
import com.fanxb.bookmark.common.entity.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Created with IntelliJ IDEA
*
* @author fanxb
* Date: 2020/3/10
* Time: 23:16
*/
@RestController
@RequestMapping("/feedback")
public class FeedbackController {
@Autowired
private FeedbackService feedbackService;
@PutMapping("")
public Result addone(@Validated @RequestBody Feedback feedback) {
feedbackService.addOne(feedback);
return Result.success(null);
}
}

View File

@ -0,0 +1,37 @@
package com.fanxb.bookmark.business.user.controller;
import com.fanxb.bookmark.business.user.service.NotifyAnnounceService;
import com.fanxb.bookmark.common.entity.Result;
import com.fanxb.bookmark.common.util.UserContextHolder;
import org.apache.ibatis.annotations.Update;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @author fanxb
* @date 2021-10-17 14:03
*/
@RestController
@RequestMapping("/announce/user")
public class NotifyAnnounceController {
@Autowired
private NotifyAnnounceService notifyAnnounceService;
/**
* 获取站内信
*/
@GetMapping
public Result getUserAnnounce(@RequestParam int status) {
return Result.success(notifyAnnounceService.getUserAnnounce(UserContextHolder.get().getUserId(), status));
}
/**
* 标记为已读
*/
@PostMapping("/read")
public Result readNotifyAnnounce(@RequestParam int notifyAnnounceId) {
notifyAnnounceService.markAsRead(UserContextHolder.get().getUserId(), notifyAnnounceId);
return Result.success();
}
}

View File

@ -0,0 +1,47 @@
package com.fanxb.bookmark.business.user.controller;
import com.fanxb.bookmark.business.user.dao.SearchEngineDao;
import com.fanxb.bookmark.business.user.entity.SearchEngine;
import com.fanxb.bookmark.business.user.service.SearchEngineService;
import com.fanxb.bookmark.common.entity.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/searchEngine")
public class SearchEngineController {
@Autowired
private SearchEngineService searchEngineService;
/**
* 列表查询
*/
@GetMapping("/list")
public Result list() {
return Result.success(searchEngineService.list());
}
@PostMapping("/insert")
public Result insert(@RequestBody SearchEngine body){
searchEngineService.insertOne(body);
return Result.success();
}
@PostMapping("/edit")
public Result edit(@RequestBody SearchEngine body){
searchEngineService.editOne(body);
return Result.success();
}
@PostMapping("/delete")
public Result delete(@RequestBody SearchEngine body){
searchEngineService.deleteOne(body.getId());
return Result.success();
}
@PostMapping("/setChecked")
public Result setChecked(@RequestBody SearchEngine body){
searchEngineService.setChecked(body.getId());
return Result.success();
}
}

View File

@ -1,9 +1,12 @@
package com.fanxb.bookmark.business.user.controller; package com.fanxb.bookmark.business.user.controller;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.fanxb.bookmark.business.user.entity.LoginBody; import com.fanxb.bookmark.business.user.service.OauthService;
import com.fanxb.bookmark.business.user.entity.RegisterBody;
import com.fanxb.bookmark.business.user.service.UserService; import com.fanxb.bookmark.business.user.service.UserService;
import com.fanxb.bookmark.business.user.vo.LoginBody;
import com.fanxb.bookmark.business.user.vo.OauthBody;
import com.fanxb.bookmark.business.user.vo.RegisterBody;
import com.fanxb.bookmark.business.user.service.impl.UserServiceImpl;
import com.fanxb.bookmark.common.entity.Result; import com.fanxb.bookmark.common.entity.Result;
import com.fanxb.bookmark.common.util.UserContextHolder; import com.fanxb.bookmark.common.util.UserContextHolder;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
@ -11,6 +14,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import javax.validation.Valid;
/** /**
* 类功能简述 * 类功能简述
* 类功能详述 * 类功能详述
@ -22,8 +27,16 @@ import org.springframework.web.multipart.MultipartFile;
@RequestMapping("/user") @RequestMapping("/user")
public class UserController { public class UserController {
private final UserServiceImpl userServiceImpl;
private final OauthService oAuthService;
private final UserService userService;
@Autowired @Autowired
private UserService userService; public UserController(UserServiceImpl userServiceImpl, OauthService oAuthService, UserService userService) {
this.userServiceImpl = userServiceImpl;
this.oAuthService = oAuthService;
this.userService = userService;
}
/** /**
* Description: 获取验证码 * Description: 获取验证码
@ -35,7 +48,7 @@ public class UserController {
*/ */
@GetMapping("/authCode") @GetMapping("/authCode")
public Result getAuthCode(@Param("email") String email) { public Result getAuthCode(@Param("email") String email) {
userService.sendAuthCode(email); userServiceImpl.sendAuthCode(email);
return Result.success(null); return Result.success(null);
} }
@ -48,9 +61,8 @@ public class UserController {
* @date 2019/7/6 16:34 * @date 2019/7/6 16:34
*/ */
@PutMapping("") @PutMapping("")
public Result register(@RequestBody RegisterBody body) { public Result register(@Valid @RequestBody RegisterBody body) {
userService.register(body); return Result.success(userServiceImpl.register(body));
return Result.success(null);
} }
/** /**
@ -62,7 +74,7 @@ public class UserController {
*/ */
@GetMapping("/currentUserInfo") @GetMapping("/currentUserInfo")
public Result currentUserInfo() { public Result currentUserInfo() {
return Result.success(userService.getUserInfo(UserContextHolder.get().getUserId())); return Result.success(userServiceImpl.getUserInfo(UserContextHolder.get().getUserId()));
} }
@ -73,7 +85,7 @@ public class UserController {
*/ */
@PostMapping("/icon") @PostMapping("/icon")
public Result pushIcon(@RequestParam("file") MultipartFile file) throws Exception { public Result pushIcon(@RequestParam("file") MultipartFile file) throws Exception {
return Result.success(userService.updateIcon(file)); return Result.success(userServiceImpl.updateIcon(file));
} }
/** /**
@ -86,7 +98,7 @@ public class UserController {
*/ */
@PostMapping("/login") @PostMapping("/login")
public Result login(@RequestBody LoginBody body) { public Result login(@RequestBody LoginBody body) {
return Result.success(userService.login(body)); return Result.success(userServiceImpl.login(body));
} }
/** /**
@ -99,7 +111,7 @@ public class UserController {
*/ */
@PostMapping("/resetPassword") @PostMapping("/resetPassword")
public Result resetPassword(@RequestBody RegisterBody body) { public Result resetPassword(@RequestBody RegisterBody body) {
userService.resetPassword(body); userServiceImpl.resetPassword(body);
return Result.success(null); return Result.success(null);
} }
@ -113,7 +125,7 @@ public class UserController {
*/ */
@PostMapping("/checkPassword") @PostMapping("/checkPassword")
public Result checkPassword(@RequestBody JSONObject obj) { public Result checkPassword(@RequestBody JSONObject obj) {
return Result.success(userService.checkPassword(obj.getString("password"))); return Result.success(userServiceImpl.checkPassword(obj.getString("password")));
} }
@GetMapping("/loginStatus") @GetMapping("/loginStatus")
@ -121,5 +133,54 @@ public class UserController {
return Result.success(null); return Result.success(null);
} }
/**
* 第三方登陆
*
* @param body 入参
* @return com.fanxb.bookmark.common.entity.Result
* @author fanxb
* @date 2021/3/10
*/
@PostMapping("oAuthLogin")
public Result oAuthLogin(@RequestBody OauthBody body) {
return Result.success(oAuthService.oAuthCheck(body));
}
/**
* 获取用户version
*
* @date 2021/3/11
**/
@GetMapping("/version")
public Result getUserVersion() {
return Result.success(userService.getCurrentUserVersion(UserContextHolder.get().getUserId()));
}
/**
* 更新所有人的icon数据
*
* @return com.fanxb.bookmark.common.entity.Result
* @author fanxb
* @date 2021/3/13
**/
@PostMapping("/updateAllUserIcon")
public Result updateAllUserIcon() {
userService.updateAllUserIcon();
return Result.success(null);
}
/**
* 处理所有的问题书签数据
*
* @param obj obj
* @return com.fanxb.bookmark.common.entity.Result
* @author fanxb
* @date 2021/3/17
**/
@PostMapping("/dealAllUserBookmark")
public Result dealAllUserBookmark(@RequestBody JSONObject obj) {
return Result.success(userService.dealAllUserBookmark(obj.getBoolean("delete")));
}
} }

View File

@ -0,0 +1,27 @@
package com.fanxb.bookmark.business.user.dao;
import com.fanxb.bookmark.business.user.entity.Feedback;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Component;
/**
* Created with IntelliJ IDEA
*
* @author fanxb
* Date: 2020/3/10
* Time: 23:14
*/
@Component
public interface FeedbackDao {
/**
* 功能描述: 插入一条记录
*
* @param feedback feedback
* @author fanxb
* @date 2020/3/10 23:16
*/
@Insert("insert into feedback(userId,type,content) value(#{userId},#{type},#{content})")
void insertOne(Feedback feedback);
}

View File

@ -0,0 +1,48 @@
package com.fanxb.bookmark.business.user.dao;
import com.fanxb.bookmark.business.user.vo.UserNotifyAnnounceRes;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
/**
* @author fanxb
* @date 2021-10-17 14:19
*/
public interface NotifyAnnounceDao {
/**
* 获取用户站内信
*
* @param userId userId
* @param status status
* @return java.util.List<com.fanxb.bookmark.business.user.vo.UserNotifyAnnounceRes>
* @author fanxb
* @date 2021/10/17 14:28
*/
List<UserNotifyAnnounceRes> queryUserAnnounce(@Param("userId") int userId, @Param("status") int status, @Param("date") long date);
/**
* 处理某人的邮件
*
* @param userId userId
* @author fanxb
* @date 2021/10/17 15:05
*/
@Insert("insert into user_notify_announce(userId,notifyAnnounceId,status) select #{userId},notifyAnnounceId,0 from notify_announce a where a.createdDate > (select lastSyncAnnounceDate from user where userId=#{userId})")
void dealNotifyAnnounceById(int userId);
/**
* 标记为已读
*
* @param userId userId
* @param notifyAnnounceId notifyAnnounceId
* @author fanxb
* @date 2021/10/17 15:26
*/
@Update("update user_notify_announce set status=1,readDate=#{readDate} where userId=#{userId} and notifyAnnounceId=#{notifyAnnounceId}")
void markAsRead(@Param("userId") int userId, @Param("notifyAnnounceId") int notifyAnnounceId, @Param("readDate") long readDate);
}

View File

@ -0,0 +1,8 @@
package com.fanxb.bookmark.business.user.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.fanxb.bookmark.business.user.entity.SearchEngine;
public interface SearchEngineDao extends BaseMapper<SearchEngine> {
}

View File

@ -1,11 +1,14 @@
package com.fanxb.bookmark.business.user.dao; package com.fanxb.bookmark.business.user.dao;
import com.fanxb.bookmark.common.entity.User; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.fanxb.bookmark.common.entity.redis.UserBookmarkUpdate; import com.fanxb.bookmark.common.entity.po.User;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update; import org.apache.ibatis.annotations.Update;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.List;
/** /**
* 类功能简述 * 类功能简述
* 类功能详述 * 类功能详述
@ -14,7 +17,7 @@ import org.springframework.stereotype.Component;
* @date 2019/7/6 11:36 * @date 2019/7/6 11:36
*/ */
@Component @Component
public interface UserDao { public interface UserDao extends BaseMapper<User> {
/** /**
* Description: 新增一个用户 * Description: 新增一个用户
@ -30,7 +33,7 @@ public interface UserDao {
* *
* @param name username * @param name username
* @param email email * @param email email
* @return com.fanxb.bookmark.common.entity.User * @return com.fanxb.bookmark.common.entity.po.User
* @author fanxb * @author fanxb
* @date 2019/7/6 16:45 * @date 2019/7/6 16:45
*/ */
@ -59,12 +62,13 @@ public interface UserDao {
/** /**
* Description: 根据用户id查询用户信息 * Description: 根据用户id查询用户信息
* *
* @param userId userId * @param userId userId
* @return com.fanxb.bookmark.common.entity.User * @param githubId githubId
* @return com.fanxb.bookmark.common.entity.po.User
* @author fanxb * @author fanxb
* @date 2019/7/30 16:08 * @date 2019/7/30 16:08
*/ */
User selectByUserId(int userId); User selectByUserIdOrGithubId(@Param("userId") Integer userId, @Param("githubId") Long githubId);
/** /**
* Description: 更新用户icon * Description: 更新用户icon
@ -102,11 +106,11 @@ public interface UserDao {
/** /**
* 更新用户新邮箱 * 更新用户新邮箱
* *
* @param userId userId * @param userId userId
* @param newPassword userId * @param newEmail email
*/ */
@Update("update user set newEmail=#{newPassword} where userId= #{userId}") @Update("update user set newEmail=#{newEmail} where userId= #{userId}")
void updateNewEmailByUserId(@Param("userId") int userId, @Param("newPassword") String newPassword); void updateNewEmailByUserId(@Param("userId") int userId, @Param("newEmail") String newEmail);
/** /**
* 新邮箱校验成功更新邮箱 * 新邮箱校验成功更新邮箱
@ -119,10 +123,78 @@ public interface UserDao {
/** /**
* 功能描述: 更新用户上次更新书签时间 * 功能描述: 更新用户上次更新书签时间
* *
* @param item item * @param userId userId
* @author fanxb * @author fanxb
* @date 2020/1/26 下午3:47 * @date 2020/1/26 下午3:47
*/ */
@Update("update user set bookmarkChangeTime=#{updateTime} where userId=#{userId}") @Update("update user set version=version+1 where userId=#{userId}")
void updateLastBookmarkUpdateTime(UserBookmarkUpdate item); void updateUserVersion(int userId);
/**
* 功能描述: 更新所有用户的更新时间
*
* @author fanxb
* @date 2020/3/29 18:18
*/
@Update("update user set version=version+1")
void updateAllBookmarkUpdateVersion();
/**
* 判断用户名是否存在
*
* @param name name
* @return boolean
* @author fanxb
* @date 2021/3/11
**/
@Select("select count(1) from user where username=#{name}")
boolean usernameExist(String name);
/**
* 更新githubId
*
* @param user user
* @author fanxb
* @date 2021/3/11
**/
@Update("update user set githubId=#{githubId},email=#{email} where userId=#{userId}")
void updateEmailAndGithubId(User user);
/**
* 获取用户版本
*
* @param userId userId
* @return int
* @author fanxb
* @date 2021/3/11
**/
@Select("select version from user where userId=#{userId}")
int getUserVersion(int userId);
/**
* 分页查询用户id列表
*
* @param start 开始
* @param size 页大小
* @return java.util.List<java.lang.Integer>
* @author fanxb
* @date 2021/3/13
**/
@Select("select userId from user order by userId limit #{start},#{size}")
List<Integer> selectUserIdPage(@Param("start") int start, @Param("size") int size);
/**
* 更新一个字段-一个条件
*
* @param column 字段名
* @param val 字段值
* @param termColumn 条件字段名
* @param termVal 条件字段值
* @author fanxb
* @date 2021/10/17 15:03
*/
@Update("update user set ${column} = #{val} where ${termColumn} = #{termVal}")
void updateOneColumnByOneTerm(String column, Object val, String termColumn, Object termVal);
} }

View File

@ -0,0 +1,21 @@
package com.fanxb.bookmark.business.user.entity;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
/**
* Created with IntelliJ IDEA
*
* @author fanxb
* Date: 2020/3/10
* Time: 23:13
*/
@Data
public class Feedback {
private int feedbackId;
private int userId;
private String type;
@NotEmpty(message = "内容不能为空")
private String content;
}

View File

@ -1,20 +0,0 @@
package com.fanxb.bookmark.business.user.entity;
import lombok.Data;
/**
* 类功能简述登录返回数据
* 类功能详述
*
* @author fanxb
* @date 2019/7/6 16:52
*/
@Data
public class LoginRes {
private String token;
private int userId;
private String username;
private String email;
private String lastLoginTime;
private String icon;
}

View File

@ -0,0 +1,36 @@
package com.fanxb.bookmark.business.user.entity;
/**
* @author fanxb
* @date 2021-10-17 14:04
*/
public class NotifyAnnounce {
/**
* 通知id
*/
private int notifyAnnounceId;
/**
* 发送人id
*/
private int senderId;
/**
* 标题
*/
private String title;
/**
* 正文
*/
private String content;
/**
* 创建时间
*/
private Long createdDate;
/**
* 通知开始时间
*/
private Long startDate;
/**
* 通知结束时间
*/
private Long endDate;
}

View File

@ -1,18 +0,0 @@
package com.fanxb.bookmark.business.user.entity;
import lombok.Data;
/**
* 类功能简述 注册表单
* 类功能详述
*
* @author fanxb
* @date 2019/7/6 11:23
*/
@Data
public class RegisterBody {
private String username;
private String password;
private String email;
private String authCode;
}

View File

@ -0,0 +1,29 @@
package com.fanxb.bookmark.business.user.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@TableName("search_engine")
public class SearchEngine {
@TableId(type = IdType.AUTO)
private Integer id;
private Integer userId;
private Integer checked;
/**
* 名称
*/
private String name;
/**
* url
*/
private String url;
/**
* 图标
*/
private String icon;
}

View File

@ -1,38 +0,0 @@
package com.fanxb.bookmark.business.user.schedule;
import com.alibaba.fastjson.JSON;
import com.fanxb.bookmark.business.user.dao.UserDao;
import com.fanxb.bookmark.common.constant.RedisConstant;
import com.fanxb.bookmark.common.entity.redis.UserBookmarkUpdate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
/**
* @author fanxb
* @date 2020/1/26 上午11:54
*/
@Component
public class UserInfoUpdate {
/**
* 阻塞时间
*/
private static final int BLOCK_TIME = 15;
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private UserDao userDao;
@Scheduled(fixedDelay = 100000)
public void userBookmarkUpdateTime() {
String value;
while ((value = redisTemplate.opsForList().rightPop(RedisConstant.BOOKMARK_UPDATE_TIME, BLOCK_TIME, TimeUnit.SECONDS)) != null) {
UserBookmarkUpdate item = JSON.parseObject(value, UserBookmarkUpdate.class);
userDao.updateLastBookmarkUpdateTime(item);
}
}
}

View File

@ -0,0 +1,7 @@
/**
* 定时调度类
*
* @author fanxb
* @date 2021-10-17 14:37
*/
package com.fanxb.bookmark.business.user.schedule;

View File

@ -1,52 +1,25 @@
package com.fanxb.bookmark.business.user.service; package com.fanxb.bookmark.business.user.service;
import com.fanxb.bookmark.business.user.constant.RedisConstant; import com.fanxb.bookmark.business.user.vo.EmailUpdateBody;
import com.fanxb.bookmark.business.user.dao.UserDao; import com.fanxb.bookmark.business.user.vo.UpdatePasswordBody;
import com.fanxb.bookmark.business.user.entity.EmailUpdateBody; import com.fanxb.bookmark.common.entity.po.User;
import com.fanxb.bookmark.business.user.entity.UpdatePasswordBody;
import com.fanxb.bookmark.common.constant.Constant;
import com.fanxb.bookmark.common.entity.MailInfo;
import com.fanxb.bookmark.common.exception.CustomException;
import com.fanxb.bookmark.common.exception.FormDataException;
import com.fanxb.bookmark.common.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.UUID;
/** /**
* 类功能简述 * 个人信息修改
* 类功能详述
* *
* @author fanxb * @author fanxb
* @date 2019/9/18 15:54 * @date 2021/3/14
*/ **/
@Service public interface BaseInfoService {
@Slf4j
public class BaseInfoService {
private static final String VERIFY_EMAIL = FileUtil.streamToString(BaseInfoService.class
.getClassLoader().getResourceAsStream("verifyEmail.html"));
private static final String VERIFY_EMAIL_PATH = "/public/verifyEmail?key=";
@Autowired
private UserDao userDao;
public void changePassword(UpdatePasswordBody body) {
int userId = UserContextHolder.get().getUserId();
String checkAuthKey = com.fanxb.bookmark.common.constant.RedisConstant.getPasswordCheckKey(userId, body.getActionId());
String str = RedisUtil.get(checkAuthKey, String.class);
if (str == null) {
throw new CustomException("密码校验失败,无法更新密码");
}
userDao.updatePasswordByUserId(userId, HashUtil.getPassword(body.getPassword()));
}
/**
* 修改密码
*
* @param body body
* @author fanxb
* @date 2021/3/14
**/
void changePassword(UpdatePasswordBody body);
/** /**
* Description: 修改用户名 * Description: 修改用户名
@ -55,9 +28,7 @@ public class BaseInfoService {
* @author fanxb * @author fanxb
* @date 2019/9/20 16:18 * @date 2019/9/20 16:18
*/ */
public void updateUsername(String username) { void updateUsername(String username);
userDao.updateUsernameByUserId(UserContextHolder.get().getUserId(), username);
}
/** /**
* 功能描述: 预备更新email需要校验密码 * 功能描述: 预备更新email需要校验密码
@ -66,23 +37,7 @@ public class BaseInfoService {
* @author fanxb * @author fanxb
* @date 2019/9/26 17:27 * @date 2019/9/26 17:27
*/ */
@Transactional(rollbackFor = Exception.class) void updateEmail(EmailUpdateBody body);
public void updateEmail(EmailUpdateBody body) {
int userId = UserContextHolder.get().getUserId();
String checkAuthKey = com.fanxb.bookmark.common.constant.RedisConstant.getPasswordCheckKey(userId, body.getActionId());
String str = RedisUtil.get(checkAuthKey, String.class);
if (str == null) {
throw new CustomException("密码校验失败无法更新email");
}
RedisUtil.delete(checkAuthKey);
String secret = UUID.randomUUID().toString().replaceAll("-", "");
String url = VERIFY_EMAIL.replaceAll("XXXX", Constant.serviceAddress + VERIFY_EMAIL_PATH + secret);
log.debug(url);
MailInfo info = new MailInfo(body.getEmail(), "验证邮箱", url);
MailUtil.sendMail(info, true);
RedisUtil.set(RedisConstant.getUpdateEmailKey(secret), String.valueOf(userId), TimeUtil.DAY_MS);
userDao.updateNewEmailByUserId(userId, body.getEmail());
}
/** /**
* 功能描述: 校验新邮箱校验成功就更新 * 功能描述: 校验新邮箱校验成功就更新
@ -91,13 +46,14 @@ public class BaseInfoService {
* @author fanxb * @author fanxb
* @date 2019/11/11 23:24 * @date 2019/11/11 23:24
*/ */
public void verifyEmail(String secret) { void verifyEmail(String secret);
String key = RedisConstant.getUpdateEmailKey(secret);
Integer userId = RedisUtil.get(key, Integer.class); /**
RedisUtil.delete(key); * 修改用户默认搜索引擎
if (userId == null) { *
throw new CustomException("校验失败,请重试"); * @param user user
} * @author fanxb
userDao.updateEmailByUserId(userId); * @date 2021/3/14
} **/
void changeDefaultSearchEngine(User user);
} }

View File

@ -0,0 +1,21 @@
package com.fanxb.bookmark.business.user.service;
import com.fanxb.bookmark.business.user.entity.Feedback;
/**
* Created with IntelliJ IDEA
*
* @author fanxb
* Date: 2020/3/10
* Time: 23:17
*/
public interface FeedbackService {
/**
* 功能描述: 插入一条记录
*
* @param feedback feedback
* @author fanxb
* @date 2020/3/10 23:18
*/
void addOne(Feedback feedback);
}

View File

@ -0,0 +1,33 @@
package com.fanxb.bookmark.business.user.service;
import com.fanxb.bookmark.business.user.entity.NotifyAnnounce;
import com.fanxb.bookmark.business.user.vo.UserNotifyAnnounceRes;
import java.util.List;
/**
* @author fanxb
* @date 2021-10-17 14:07
*/
public interface NotifyAnnounceService {
/**
* 获取用户通知
*
* @param userId 用户id
* @param status 状态 0:未读1:已读
* @author fanxb
* @date 2021/10/17 14:14
*/
List<UserNotifyAnnounceRes> getUserAnnounce(int userId, int status);
/**
* 标记为已读
*
* @param userId 用户id
* @param notifyAnnounceId 通知id
* @author fanxb
* @date 2021/10/17 14:14
*/
void markAsRead(int userId, int notifyAnnounceId);
}

View File

@ -0,0 +1,19 @@
package com.fanxb.bookmark.business.user.service;
import com.fanxb.bookmark.business.user.vo.OauthBody;
/**
* @author fanxb
* @date 2021/8/20 下午2:13
*/
public interface OauthService {
/**
* oauth登陆校验
*
* @param body body
* @return java.lang.String
* @author fanxb
* @date 2021/8/20 下午2:13
*/
String oAuthCheck(OauthBody body);
}

View File

@ -0,0 +1,49 @@
package com.fanxb.bookmark.business.user.service;
import com.fanxb.bookmark.business.user.entity.SearchEngine;
import java.util.List;
public interface SearchEngineService {
/**
* 列表查询
*/
List<SearchEngine> list();
/**
* delete one by id
*
* @param id id
*/
void deleteOne(int id);
/**
* insert one
*
* @param body body
*/
void insertOne(SearchEngine body);
/**
* edit one
*
* @param body body
*/
void editOne(SearchEngine body);
/**
* 设为默认搜索项
*
* @param id
*/
void setChecked(Integer id);
/**
* 新用户初始化
*
* @param userId userId
*/
void newUserInit(int userId);
}

View File

@ -1,207 +1,65 @@
package com.fanxb.bookmark.business.user.service; package com.fanxb.bookmark.business.user.service;
import com.fanxb.bookmark.business.user.constant.FileConstant; import com.fanxb.bookmark.common.util.TimeUtil;
import com.fanxb.bookmark.business.user.dao.UserDao;
import com.fanxb.bookmark.business.user.entity.LoginBody;
import com.fanxb.bookmark.business.user.entity.LoginRes;
import com.fanxb.bookmark.business.user.entity.RegisterBody;
import com.fanxb.bookmark.common.constant.Constant;
import com.fanxb.bookmark.common.constant.NumberConstant;
import com.fanxb.bookmark.common.constant.RedisConstant;
import com.fanxb.bookmark.common.entity.MailInfo;
import com.fanxb.bookmark.common.entity.User;
import com.fanxb.bookmark.common.exception.FormDataException;
import com.fanxb.bookmark.common.util.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.Set;
/** /**
* 类功能简述 * 用户接口
* 类功能详述
* *
* @author fanxb * @author fanxb
* @date 2019/7/5 17:39 * @date 2021/3/11
*/ **/
@Service public interface UserService {
public class UserService { String DEFAULT_ICON = "/favicon.ico";
private static final String DEFAULT_ICON = "/favicon.ico";
/** /**
* 短期jwt失效时间 * 短期jwt失效时间
*/ */
private static final long SHORT_EXPIRE_TIME = 2 * 60 * 60 * 1000; long SHORT_EXPIRE_TIME = 2 * 60 * 60 * 1000;
/** /**
* 长期jwt失效时间 * 长期jwt失效时间
*/ */
private static final long LONG_EXPIRE_TIME = 30L * TimeUtil.DAY_MS; long LONG_EXPIRE_TIME = 300L * TimeUtil.DAY_MS;
/** /**
* 头像文件大小限制 单位KB * 头像文件大小限制 单位KB
*/ */
private static final int ICON_SIZE = 200; int ICON_SIZE = 200;
@Autowired /***
private UserDao userDao; * 获取一个可用的用户名
/**
* Description: 向目标发送验证码
*
* @param email 目标
* @author fanxb * @author fanxb
* @date 2019/7/5 17:48 * @return java.lang.String
*/ * @date 2021/3/11
public void sendAuthCode(String email) { **/
MailInfo info = new MailInfo(); String createNewUsername();
info.setSubject("签签世界注册验证码");
String code = StringUtil.getRandomString(6, 2);
info.setContent("欢迎注册 签签世界 ,本次验证码");
info.setContent(code + " 是您的验证码注意验证码有效期为15分钟哦");
info.setReceiver(email);
if (Constant.isDev) {
code = "123456";
} else {
MailUtil.sendTextMail(info);
}
RedisUtil.set(Constant.authCodeKey(email), code, Constant.AUTH_CODE_EXPIRE);
}
/** /**
* Description: 用户注册 * 获取当前用户的version
*
* @param body 注册表单
* @author fanxb
* @date 2019/7/6 11:30
*/
public void register(RegisterBody body) {
User user = userDao.selectByUsernameOrEmail(body.getUsername(), body.getEmail());
if (user != null) {
if (user.getUsername().equals(body.getUsername())) {
throw new FormDataException("用户名已经被注册");
}
if (user.getEmail().equals(body.getEmail())) {
throw new FormDataException("邮箱已经被注册");
}
}
user = new User();
user.setUsername(body.getUsername());
user.setEmail(body.getEmail());
user.setIcon(DEFAULT_ICON);
user.setPassword(HashUtil.sha1(HashUtil.md5(body.getPassword())));
user.setCreateTime(System.currentTimeMillis());
user.setLastLoginTime(0);
userDao.addOne(user);
}
/**
* Description: 登录
*
* @param body 登录表单
* @return LoginRes
* @author fanxb
* @date 2019/7/6 16:37
*/
public LoginRes login(LoginBody body) {
User userInfo = userDao.selectByUsernameOrEmail(body.getStr(), body.getStr());
if (userInfo == null) {
throw new FormDataException("账号/密码错误");
}
if (!HashUtil.sha1(HashUtil.md5(body.getPassword())).equals(userInfo.getPassword())) {
throw new FormDataException("账号/密码错误");
}
Map<String, String> data = new HashMap<>(1);
data.put("userId", String.valueOf(userInfo.getUserId()));
String token = JwtUtil.encode(data, Constant.jwtSecret, body.isRememberMe() ? LONG_EXPIRE_TIME : SHORT_EXPIRE_TIME);
LoginRes res = new LoginRes();
res.setToken(token);
res.setUserId(userInfo.getUserId());
res.setUsername(userInfo.getUsername());
res.setEmail(userInfo.getEmail());
res.setIcon(userInfo.getIcon());
userDao.updateLastLoginTime(System.currentTimeMillis(), userInfo.getUserId());
return res;
}
/**
* Description: 重置密码
*
* @param body 重置密码 由于参数和注册差不多所以用同一个表单
* @author fanxb
* @date 2019/7/9 19:59
*/
public void resetPassword(RegisterBody body) {
User user = userDao.selectByUsernameOrEmail(body.getEmail(), body.getEmail());
if (user == null) {
throw new FormDataException("用户不存在");
}
String codeKey = Constant.authCodeKey(body.getEmail());
String realCode = RedisUtil.get(codeKey, String.class);
if (StringUtil.isEmpty(realCode) || (!realCode.equals(body.getAuthCode()))) {
throw new FormDataException("验证码错误");
}
RedisUtil.delete(codeKey);
String newPassword = HashUtil.getPassword(body.getPassword());
userDao.resetPassword(newPassword, body.getEmail());
}
/**
* Description: 根据userId获取用户信息
* *
* @param userId userId * @param userId userId
* @return com.fanxb.bookmark.common.entity.User * @return int
* @author fanxb * @author fanxb
* @date 2019/7/30 15:57 * @date 2021/3/11
*/ **/
public User getUserInfo(int userId) { int getCurrentUserVersion(int userId);
return userDao.selectByUserId(userId);
}
/** /**
* 修改用户头像 * 更新所有用户的空icon
* *
* @param file file * @author fanxb
* @return 访问路径 * @date 2021/3/13
*/ **/
public String updateIcon(MultipartFile file) throws Exception { void updateAllUserIcon();
if (file.getSize() / NumberConstant.K_SIZE > ICON_SIZE) {
throw new FormDataException("文件大小超过限制");
}
int userId = UserContextHolder.get().getUserId();
String fileName = file.getOriginalFilename();
String path = Paths.get(FileConstant.iconPath, userId + "." + System.currentTimeMillis() + fileName.substring(fileName.lastIndexOf("."))).toString();
Path realPath = Paths.get(Constant.fileSavePath, path);
FileUtil.ensurePathExist(realPath.getParent().toString());
file.transferTo(realPath);
path = File.separator + path;
userDao.updateUserIcon(userId, path);
return path;
}
/** /**
* 功能描述: 密码校验校验成功返回一个actionId以执行敏感操作 * 检查所有用户的问题书签数据
* *
* @param password password * @param delete 是否删除问题数据
* @return java.lang.String * @return 返回用户删除的数据
* @author fanxb * @author fanxb
* @date 2019/11/11 23:41 * @date 2021/3/17
*/ **/
public String checkPassword(String password) { Map<Integer, Set<String>> dealAllUserBookmark(boolean delete);
int userId = UserContextHolder.get().getUserId();
String pass = HashUtil.getPassword(password);
User user = userDao.selectByUserId(userId);
if (!user.getPassword().equals(pass)) {
throw new FormDataException("密码错误,请重试");
}
String actionId = UUID.randomUUID().toString().replaceAll("-", "");
String key = RedisConstant.getPasswordCheckKey(userId, actionId);
RedisUtil.set(key, "1", 5 * 60 * 1000);
return actionId;
}
} }

View File

@ -0,0 +1,92 @@
package com.fanxb.bookmark.business.user.service.impl;
import cn.hutool.core.util.StrUtil;
import com.fanxb.bookmark.business.user.constant.RedisConstant;
import com.fanxb.bookmark.business.user.dao.UserDao;
import com.fanxb.bookmark.business.user.service.BaseInfoService;
import com.fanxb.bookmark.business.user.vo.EmailUpdateBody;
import com.fanxb.bookmark.business.user.vo.UpdatePasswordBody;
import com.fanxb.bookmark.common.constant.CommonConstant;
import com.fanxb.bookmark.common.entity.MailInfo;
import com.fanxb.bookmark.common.entity.po.User;
import com.fanxb.bookmark.common.exception.CustomException;
import com.fanxb.bookmark.common.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.UUID;
/**
* 类功能简述
* 类功能详述
*
* @author fanxb
* @date 2019/9/18 15:54
*/
@Service
@Slf4j
public class BaseInfoServiceImpl implements BaseInfoService {
private static final String VERIFY_EMAIL = FileUtil.streamToString(BaseInfoServiceImpl.class
.getClassLoader().getResourceAsStream("verifyEmail.html"));
private static final String VERIFY_EMAIL_PATH = "/public/verifyEmail?key=";
private final UserDao userDao;
@Autowired
public BaseInfoServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void changePassword(UpdatePasswordBody body) {
int userId = UserContextHolder.get().getUserId();
String password = userDao.selectByUserIdOrGithubId(userId, null).getPassword();
if (StrUtil.isNotEmpty(password) && !StrUtil.equals(password, HashUtil.getPassword(body.getOldPassword()))) {
throw new CustomException("旧密码错误");
}
userDao.updatePasswordByUserId(userId, HashUtil.getPassword(body.getPassword()));
}
@Override
public void updateUsername(String username) {
userDao.updateUsernameByUserId(UserContextHolder.get().getUserId(), username);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateEmail(EmailUpdateBody body) {
int userId = UserContextHolder.get().getUserId();
String oldPassword = userDao.selectByUserIdOrGithubId(userId, null).getPassword();
if (!StrUtil.equals(oldPassword, HashUtil.getPassword(body.getOldPassword()))) {
throw new CustomException("密码校验失败无法更新email");
}
String secret = UUID.randomUUID().toString().replaceAll("-", "");
String url = VERIFY_EMAIL.replaceAll("XXXX", CommonConstant.serviceAddress + VERIFY_EMAIL_PATH + secret);
log.debug(url);
MailInfo info = new MailInfo(body.getEmail(), "验证邮箱", url);
MailUtil.sendMail(info, true);
RedisUtil.set(RedisConstant.getUpdateEmailKey(secret), String.valueOf(userId), TimeUtil.DAY_MS);
userDao.updateNewEmailByUserId(userId, body.getEmail());
}
@Override
public void verifyEmail(String secret) {
String key = RedisConstant.getUpdateEmailKey(secret);
Integer userId = RedisUtil.get(key, Integer.class);
RedisUtil.delete(key);
if (userId == null) {
throw new CustomException("校验失败,请重试");
}
userDao.updateEmailByUserId(userId);
}
@Override
public void changeDefaultSearchEngine(User user) {
userDao.updateById(user);
}
}

View File

@ -0,0 +1,31 @@
package com.fanxb.bookmark.business.user.service.impl;
import com.fanxb.bookmark.business.user.dao.FeedbackDao;
import com.fanxb.bookmark.business.user.entity.Feedback;
import com.fanxb.bookmark.business.user.service.FeedbackService;
import com.fanxb.bookmark.common.util.UserContextHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* Created with IntelliJ IDEA
*
* @author fanxb
* Date: 2020/3/10
* Time: 23:17
*/
@Service
public class FeedbackServiceImpl implements FeedbackService {
private final FeedbackDao feedbackDao;
@Autowired
public FeedbackServiceImpl(FeedbackDao feedbackDao) {
this.feedbackDao = feedbackDao;
}
@Override
public void addOne(Feedback feedback) {
feedback.setUserId(UserContextHolder.get().getUserId());
feedbackDao.insertOne(feedback);
}
}

View File

@ -0,0 +1,37 @@
package com.fanxb.bookmark.business.user.service.impl;
import com.fanxb.bookmark.business.user.dao.NotifyAnnounceDao;
import com.fanxb.bookmark.business.user.dao.UserDao;
import com.fanxb.bookmark.business.user.entity.NotifyAnnounce;
import com.fanxb.bookmark.business.user.service.NotifyAnnounceService;
import com.fanxb.bookmark.business.user.vo.UserNotifyAnnounceRes;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* @author fanxb
* @date 2021-10-17 14:16
*/
@Service
public class NotifyAnnounceServiceImpl implements NotifyAnnounceService {
@Autowired
private NotifyAnnounceDao notifyAnnounceDao;
@Autowired
private UserDao userDao;
@Override
@Transactional(rollbackFor = Exception.class)
public List<UserNotifyAnnounceRes> getUserAnnounce(int userId, int status) {
notifyAnnounceDao.dealNotifyAnnounceById(userId);
userDao.updateOneColumnByOneTerm("lastSyncAnnounceDate", System.currentTimeMillis(), "userId", userId);
return notifyAnnounceDao.queryUserAnnounce(userId, status, System.currentTimeMillis());
}
@Override
public void markAsRead(int userId, int notifyAnnounceId) {
notifyAnnounceDao.markAsRead(userId, notifyAnnounceId, System.currentTimeMillis());
}
}

View File

@ -0,0 +1,122 @@
package com.fanxb.bookmark.business.user.service.impl;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.fanxb.bookmark.business.user.dao.UserDao;
import com.fanxb.bookmark.business.user.service.OauthService;
import com.fanxb.bookmark.business.user.service.SearchEngineService;
import com.fanxb.bookmark.business.user.service.UserService;
import com.fanxb.bookmark.business.user.vo.OauthBody;
import com.fanxb.bookmark.common.constant.CommonConstant;
import com.fanxb.bookmark.common.entity.po.User;
import com.fanxb.bookmark.common.exception.CustomException;
import com.fanxb.bookmark.common.util.HttpUtil;
import com.fanxb.bookmark.common.util.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import static com.fanxb.bookmark.business.user.service.UserService.LONG_EXPIRE_TIME;
import static com.fanxb.bookmark.business.user.service.UserService.SHORT_EXPIRE_TIME;
/**
* OAuth交互类
*
* @author fanxb
* @date 2021/3/10
**/
@Service
@Slf4j
public class OauthServiceImpl implements OauthService {
@Value("${OAuth.github.clientId}")
private String githubClientId;
@Value("${OAuth.github.secret}")
private String githubSecret;
@Autowired
private SearchEngineService searchEngineService;
private final UserDao userDao;
private final UserService userService;
@Autowired
public OauthServiceImpl(UserDao userDao, UserService userService) {
this.userDao = userDao;
this.userService = userService;
}
@Override
@Transactional(rollbackFor = Exception.class)
public String oAuthCheck(OauthBody body) {
User current, other = new User();
if (StrUtil.equals(body.getType(), OauthBody.GITHUB)) {
Map<String, String> header = new HashMap<>(2);
header.put("accept", "application/json");
String url = "https://github.com/login/oauth/access_token?client_id=" + githubClientId + "&client_secret=" + githubSecret + "&code=" + body.getCode();
JSONObject obj = HttpUtil.getObj(url, header, true);
String accessToken = obj.getString("access_token");
if (StrUtil.isEmpty(accessToken)) {
throw new CustomException("github登陆失败请稍后重试");
}
header.put("Authorization", "token " + accessToken);
JSONObject userInfo = HttpUtil.getObj("https://api.github.com/user", header, true);
other.setGithubId(userInfo.getLong("id"));
if (other.getGithubId() == null) {
log.error("github返回异常:{}", userInfo);
throw new CustomException("登陆异常,请稍后重试");
}
other.setEmail(userInfo.getString("email"));
other.setIcon(userInfo.getString("avatar_url"));
other.setUsername(userInfo.getString("login"));
current = userDao.selectByUserIdOrGithubId(null, other.getGithubId());
if (current == null) {
current = userDao.selectByUsernameOrEmail(null, other.getEmail());
}
} else {
throw new CustomException("不支持的登陆方式" + body.getType());
}
User newest = dealOauth(current, other);
return JwtUtil.encode(Collections.singletonMap("userId", String.valueOf(newest.getUserId())), CommonConstant.jwtSecret
, body.isRememberMe() ? LONG_EXPIRE_TIME : SHORT_EXPIRE_TIME);
}
/**
* oauth登陆
*
* @param current 当前是否存在该用户
* @param other 第三方获取的数据
* @return User 最新的用户信息
* @author fanxb
* @date 2021/3/11
**/
private User dealOauth(User current, User other) {
if (current == null) {
//判断用户名是否可用
if (userDao.usernameExist(other.getUsername())) {
other.setUsername(userService.createNewUsername());
}
other.setPassword("");
other.setCreateTime(System.currentTimeMillis());
other.setLastLoginTime(System.currentTimeMillis());
other.setIcon(UserService.DEFAULT_ICON);
other.setCreateTime(System.currentTimeMillis());
other.setLastLoginTime(System.currentTimeMillis());
other.setVersion(0);
userDao.addOne(other);
searchEngineService.newUserInit(other.getUserId());
return other;
} else {
if (!current.getEmail().equals(other.getEmail()) || !current.getGithubId().equals(other.getGithubId())) {
current.setEmail(other.getEmail());
current.setGithubId(other.getGithubId());
userDao.updateEmailAndGithubId(current);
}
return current;
}
}
}

View File

@ -0,0 +1,88 @@
package com.fanxb.bookmark.business.user.service.impl;
import cn.hutool.core.util.StrUtil;
import com.alibaba.druid.support.ibatis.SpringIbatisBeanNameAutoProxyCreator;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.fanxb.bookmark.business.user.dao.SearchEngineDao;
import com.fanxb.bookmark.business.user.dao.UserDao;
import com.fanxb.bookmark.business.user.entity.SearchEngine;
import com.fanxb.bookmark.business.user.service.SearchEngineService;
import com.fanxb.bookmark.common.entity.UserContext;
import com.fanxb.bookmark.common.entity.po.User;
import com.fanxb.bookmark.common.exception.CustomException;
import com.fanxb.bookmark.common.util.UserContextHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class SearchEngineServiceImpl implements SearchEngineService {
@Autowired
private SearchEngineDao searchEngineDao;
@Autowired
private UserDao userDao;
@Override
public List<SearchEngine> list() {
return searchEngineDao.selectList(new LambdaQueryWrapper<SearchEngine>().eq(SearchEngine::getUserId, UserContextHolder.get().getUserId()));
}
@Override
public void deleteOne(int id) {
SearchEngine engine = searchEngineDao.selectById(id);
if (engine.getUserId() != UserContextHolder.get().getUserId()) {
throw new CustomException("无法操作其他人数据");
}
if (engine.getChecked() == 1) {
throw new CustomException("默认搜索引擎无法删除");
}
searchEngineDao.deleteById(id);
}
@Override
public void insertOne(SearchEngine body) {
checkOne(body);
body.setId(null).setChecked(0).setUserId(UserContextHolder.get().getUserId());
searchEngineDao.insert(body);
}
private void checkOne(SearchEngine body) {
if (StrUtil.hasBlank(body.getIcon(), body.getUrl(), body.getName())) {
throw new CustomException("请填写完整");
}
if (!body.getUrl().contains("%s")) {
throw new CustomException("路径中必须包含%s");
}
}
@Override
public void editOne(SearchEngine body) {
SearchEngine engine = searchEngineDao.selectById(body.getId());
if (engine.getUserId() != UserContextHolder.get().getUserId()) {
throw new CustomException("无法操作其他人数据");
}
checkOne(body);
searchEngineDao.updateById(body);
}
@Override
@Transactional(rollbackFor = Exception.class)
public void setChecked(Integer id) {
int userId = UserContextHolder.get().getUserId();
LambdaUpdateWrapper<SearchEngine> update = new LambdaUpdateWrapper<SearchEngine>().set(SearchEngine::getChecked, 0).eq(SearchEngine::getUserId, userId).eq(SearchEngine::getChecked, 1);
searchEngineDao.update(null, update);
update = new LambdaUpdateWrapper<SearchEngine>().set(SearchEngine::getChecked, 1).eq(SearchEngine::getId, id).eq(SearchEngine::getUserId, userId);
searchEngineDao.update(null, update);
}
@Override
public void newUserInit(int userId) {
searchEngineDao.insert(new SearchEngine().setUserId(userId).setIcon("icon-baidu").setName("百度").setUrl("https://www.baidu.com/s?ie=UTF-8&wd=%s").setChecked(1));
searchEngineDao.insert(new SearchEngine().setUserId(userId).setIcon("icon-bing").setName("必应").setUrl("https://www.bing.com/search?q=%s").setChecked(0));
searchEngineDao.insert(new SearchEngine().setUserId(userId).setIcon("icon-google").setName("谷歌").setUrl("https://www.google.com/search?q=%s").setChecked(0));
}
}

View File

@ -0,0 +1,30 @@
package com.fanxb.bookmark.business.user.service.impl;
import com.fanxb.bookmark.business.api.UserApi;
import com.fanxb.bookmark.business.user.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author fanxb
* @date 2021/8/20 下午2:15
*/
@Service
public class UserApiImpl implements UserApi {
private final UserDao userDao;
@Autowired
public UserApiImpl(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void versionPlus(int userId) {
userDao.updateUserVersion(userId);
}
@Override
public void allUserVersionPlus() {
userDao.updateAllBookmarkUpdateVersion();
}
}

View File

@ -0,0 +1,274 @@
package com.fanxb.bookmark.business.user.service.impl;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.fanxb.bookmark.business.api.BookmarkApi;
import com.fanxb.bookmark.business.user.constant.FileConstant;
import com.fanxb.bookmark.business.user.dao.UserDao;
import com.fanxb.bookmark.business.user.service.SearchEngineService;
import com.fanxb.bookmark.business.user.service.UserService;
import com.fanxb.bookmark.business.user.vo.LoginBody;
import com.fanxb.bookmark.business.user.vo.RegisterBody;
import com.fanxb.bookmark.common.constant.CommonConstant;
import com.fanxb.bookmark.common.constant.NumberConstant;
import com.fanxb.bookmark.common.constant.RedisConstant;
import com.fanxb.bookmark.common.entity.MailInfo;
import com.fanxb.bookmark.common.entity.po.User;
import com.fanxb.bookmark.common.exception.CustomException;
import com.fanxb.bookmark.common.exception.FormDataException;
import com.fanxb.bookmark.common.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* 类功能简述
* 类功能详述
*
* @author fanxb
* @date 2019/7/5 17:39
*/
@Service
@Slf4j
public class UserServiceImpl implements UserService {
/**
* 登陆最大重试次数
*/
private static final int LOGIN_COUNT = 5;
@Autowired
private SearchEngineService searchEngineService;
private final UserDao userDao;
private final StringRedisTemplate redisTemplate;
private final BookmarkApi bookmarkApi;
@Autowired
public UserServiceImpl(UserDao userDao, StringRedisTemplate redisTemplate, BookmarkApi bookmarkApi) {
this.userDao = userDao;
this.redisTemplate = redisTemplate;
this.bookmarkApi = bookmarkApi;
}
/**
* Description: 向目标发送验证码
*
* @param email 目标
* @author fanxb
* @date 2019/7/5 17:48
*/
public void sendAuthCode(String email) {
MailInfo info = new MailInfo();
info.setSubject("签签世界注册验证码");
String code = StringUtil.getRandomString(6, 2);
info.setContent("欢迎注册 签签世界 ,本次验证码");
info.setContent(code + " 是您的验证码注意验证码有效期为15分钟哦");
info.setReceiver(email);
if (CommonConstant.isDev) {
code = "123456";
} else {
MailUtil.sendTextMail(info);
}
RedisUtil.set(CommonConstant.authCodeKey(email), code, CommonConstant.AUTH_CODE_EXPIRE);
}
/**
* Description: 用户注册
*
* @param body 注册表单
* @author fanxb
* @date 2019/7/6 11:30
*/
@Transactional(rollbackFor = Exception.class)
public String register(RegisterBody body) {
User user = userDao.selectByUsernameOrEmail(body.getUsername(), body.getEmail());
if (user != null) {
if (user.getUsername().equals(body.getUsername())) {
throw new FormDataException("用户名已经被注册");
}
if (user.getEmail().equals(body.getEmail())) {
throw new FormDataException("邮箱已经被注册");
}
}
user = new User();
user.setUsername(body.getUsername());
user.setEmail(body.getEmail());
user.setIcon(DEFAULT_ICON);
user.setPassword(HashUtil.sha1(HashUtil.md5(body.getPassword())));
user.setCreateTime(System.currentTimeMillis());
user.setLastLoginTime(System.currentTimeMillis());
user.setVersion(0);
userDao.addOne(user);
searchEngineService.newUserInit(user.getUserId());
Map<String, String> data = new HashMap<>(1);
data.put("userId", String.valueOf(user.getUserId()));
return JwtUtil.encode(data, CommonConstant.jwtSecret, LONG_EXPIRE_TIME);
}
/**
* Description: 登录
*
* @param body 登录表单
* @return string
* @author fanxb
* @date 2019/7/6 16:37
*/
public String login(LoginBody body) {
String key = RedisConstant.getUserFailCountKey(body.getStr());
String count = redisTemplate.opsForValue().get(key);
if (count != null && Integer.parseInt(count) >= LOGIN_COUNT) {
redisTemplate.expire(key, 30, TimeUnit.MINUTES);
throw new FormDataException("您已连续输错密码5次请30分钟后再试或联系管理员处理");
}
User userInfo = userDao.selectByUsernameOrEmail(body.getStr(), body.getStr());
if (userInfo == null || StrUtil.isEmpty(userInfo.getPassword()) || !HashUtil.sha1(HashUtil.md5(body.getPassword())).equals(userInfo.getPassword())) {
redisTemplate.opsForValue().set(key, count == null ? "1" : String.valueOf(Integer.parseInt(count) + 1), 30, TimeUnit.MINUTES);
throw new FormDataException("账号密码错误");
}
redisTemplate.delete(key);
userDao.updateLastLoginTime(System.currentTimeMillis(), userInfo.getUserId());
return JwtUtil.encode(Collections.singletonMap("userId", String.valueOf(userInfo.getUserId())), CommonConstant.jwtSecret
, body.isRememberMe() ? LONG_EXPIRE_TIME : SHORT_EXPIRE_TIME);
}
/**
* Description: 重置密码
*
* @param body 重置密码 由于参数和注册差不多所以用同一个表单
* @author fanxb
* @date 2019/7/9 19:59
*/
public void resetPassword(RegisterBody body) {
User user = userDao.selectByUsernameOrEmail(body.getEmail(), body.getEmail());
if (user == null) {
throw new FormDataException("用户不存在");
}
String codeKey = CommonConstant.authCodeKey(body.getEmail());
String realCode = RedisUtil.get(codeKey, String.class);
if (StringUtil.isEmpty(realCode) || (!realCode.equals(body.getAuthCode()))) {
throw new FormDataException("验证码错误");
}
RedisUtil.delete(codeKey);
String newPassword = HashUtil.getPassword(body.getPassword());
userDao.resetPassword(newPassword, body.getEmail());
}
/**
* Description: 根据userId获取用户信息
*
* @param userId userId
* @return com.fanxb.bookmark.common.entity.po.User
* @author fanxb
* @date 2019/7/30 15:57
*/
public User getUserInfo(int userId) {
User user = userDao.selectByUserIdOrGithubId(userId, null);
user.setNoPassword(StrUtil.isEmpty(user.getPassword()));
return user;
}
/**
* 修改用户头像
*
* @param file file
* @return 访问路径
*/
public String updateIcon(MultipartFile file) throws Exception {
if (file.getSize() / NumberConstant.K_SIZE > ICON_SIZE) {
throw new FormDataException("文件大小超过限制");
}
int userId = UserContextHolder.get().getUserId();
String fileName = file.getOriginalFilename();
assert fileName != null;
String path = Paths.get(FileConstant.iconPath, userId + "." + System.currentTimeMillis() + fileName.substring(fileName.lastIndexOf("."))).toString();
Path realPath = Paths.get(CommonConstant.fileSavePath, path);
FileUtil.ensurePathExist(realPath.getParent().toString());
file.transferTo(realPath);
path = File.separator + path;
userDao.updateUserIcon(userId, path);
return path;
}
/**
* 功能描述: 密码校验校验成功返回一个actionId以执行敏感操作
*
* @param password password
* @return java.lang.String
* @author fanxb
* @date 2019/11/11 23:41
*/
public String checkPassword(String password) {
int userId = UserContextHolder.get().getUserId();
String pass = HashUtil.getPassword(password);
User user = userDao.selectByUserIdOrGithubId(userId, null);
if (!user.getPassword().equals(pass)) {
throw new FormDataException("密码错误,请重试");
}
String actionId = UUID.randomUUID().toString().replaceAll("-", "");
String key = RedisConstant.getPasswordCheckKey(userId, actionId);
RedisUtil.set(key, "1", 5 * 60 * 1000);
return actionId;
}
@Override
public String createNewUsername() {
while (true) {
String name = RandomUtil.randomString(8);
if (!userDao.usernameExist(name)) {
return name;
}
}
}
@Override
public int getCurrentUserVersion(int userId) {
return userDao.getUserVersion(userId);
}
@Override
public void updateAllUserIcon() {
if (!UserContextHolder.get().getManageUser()) {
throw new CustomException("非管理员用户,无法执行操作");
}
ThreadPoolUtil.execute(() -> {
log.info("开始更新所有人icon");
int start = 0, size = 1000;
List<Integer> ids;
while ((ids = userDao.selectUserIdPage(start, size)).size() > 0) {
start += size;
ids.forEach(bookmarkApi::updateUserBookmarkIcon);
}
log.info("结束更新所有人icon");
});
}
@Override
public Map<Integer, Set<String>> dealAllUserBookmark(boolean delete) {
if (!UserContextHolder.get().getManageUser()) {
throw new CustomException("非管理员用户,无法执行操作");
}
log.info("开始处理所有问题书签数据");
int start = 0, size = 1000;
List<Integer> ids;
Map<Integer, Set<String>> res = new HashMap<>(1000);
while ((ids = userDao.selectUserIdPage(start, size)).size() > 0) {
start += size;
ids.forEach(id -> {
Set<String> oneUser = bookmarkApi.dealBadBookmark(delete, id);
if (oneUser.size() > 0) {
res.put(id, oneUser);
}
});
}
log.info("处理完毕");
return res;
}
}

View File

@ -1,4 +1,4 @@
package com.fanxb.bookmark.business.user.entity; package com.fanxb.bookmark.business.user.vo;
import com.fanxb.bookmark.business.user.constant.ValidatedConstant; import com.fanxb.bookmark.business.user.constant.ValidatedConstant;
import lombok.Data; import lombok.Data;
@ -17,7 +17,7 @@ import javax.validation.constraints.Pattern;
@Data @Data
public class EmailUpdateBody { public class EmailUpdateBody {
@NotNull(message = "参数不为空") @NotNull(message = "参数不为空")
private String actionId; private String oldPassword;
@Email(message = "请输入有效邮箱地址") @Email(message = "请输入有效邮箱地址")
private String email; private String email;
} }

View File

@ -1,4 +1,4 @@
package com.fanxb.bookmark.business.user.entity; package com.fanxb.bookmark.business.user.vo;
import lombok.Data; import lombok.Data;

View File

@ -0,0 +1,26 @@
package com.fanxb.bookmark.business.user.vo;
import com.fanxb.bookmark.common.entity.po.User;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 类功能简述登录返回数据
*
* @author fanxb
* @date 2019/7/6 16:52
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class LoginRes {
/**
* 用户信息
*/
private User user;
/**
* token
*/
private String token;
}

View File

@ -0,0 +1,26 @@
package com.fanxb.bookmark.business.user.vo;
import lombok.Data;
/**
* 第三方登陆入参
*
* @author fanxb
* @date 2021/3/10
**/
@Data
public class OauthBody {
public static final String GITHUB = "github";
/**
* 类别
*/
private String type;
/**
* 识别码
*/
private String code;
/**
* 是否保持登陆
*/
private boolean rememberMe;
}

View File

@ -0,0 +1,26 @@
package com.fanxb.bookmark.business.user.vo;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
/**
* 类功能简述 注册表单
* 类功能详述
*
* @author fanxb
* @date 2019/7/6 11:23
*/
@Data
public class RegisterBody {
@NotBlank(message = "用户名不能为空")
@Pattern(regexp = "^\\w{1,50}$", message = "用户名长度为1-50")
private String username;
@NotBlank(message = "密码不能为空")
@Pattern(regexp = "^\\w{6,18}$", message = "密码为6-18位组合")
private String password;
@NotBlank(message = "邮箱不能为空")
private String email;
private String authCode;
}

View File

@ -1,4 +1,4 @@
package com.fanxb.bookmark.business.user.entity; package com.fanxb.bookmark.business.user.vo;
import com.fanxb.bookmark.business.user.constant.ValidatedConstant; import com.fanxb.bookmark.business.user.constant.ValidatedConstant;
import lombok.Data; import lombok.Data;
@ -15,7 +15,7 @@ import javax.validation.constraints.Pattern;
@Data @Data
public class UpdatePasswordBody { public class UpdatePasswordBody {
private String actionId; private String oldPassword;
@Pattern(regexp = ValidatedConstant.PASSWORD_REG, message = ValidatedConstant.PASSWORD_MESSAGE) @Pattern(regexp = ValidatedConstant.PASSWORD_REG, message = ValidatedConstant.PASSWORD_MESSAGE)
private String password; private String password;
} }

View File

@ -0,0 +1,15 @@
package com.fanxb.bookmark.business.user.vo;
import lombok.Data;
/**
* @author fanxb
* @date 2021-10-17 14:22
*/
@Data
public class UserNotifyAnnounceRes {
private int notifyAnnounceId;
private String title;
private String content;
private long readDate;
}

View File

@ -1,4 +1,4 @@
package com.fanxb.bookmark.business.user.entity; package com.fanxb.bookmark.business.user.vo;
import com.fanxb.bookmark.business.user.constant.ValidatedConstant; import com.fanxb.bookmark.business.user.constant.ValidatedConstant;
import lombok.Data; import lombok.Data;

View File

@ -0,0 +1,8 @@
/*
* @description: 前后端交互类
* @author: fanxb
* @param: null
* @return:
* @date: 2021/3/10
*/
package com.fanxb.bookmark.business.user.vo;

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.fanxb.bookmark.business.user.dao.NotifyAnnounceDao">
<select id="queryUserAnnounce" resultType="com.fanxb.bookmark.business.user.vo.UserNotifyAnnounceRes">
select b.notifyAnnounceId,b.title,b.content,a.readDate from user_notify_announce a inner join notify_announce b
on a.notifyAnnounceId=b.notifyAnnounceId where a.userId=#{userId} and a.status=#{status}
<if test="status == 0">
and b.startDate&lt;#{date} and b.endDate>#{date}
</if>
</select>
</mapper>

View File

@ -3,22 +3,17 @@
<mapper namespace="com.fanxb.bookmark.business.user.dao.UserDao"> <mapper namespace="com.fanxb.bookmark.business.user.dao.UserDao">
<insert id="addOne"> <insert id="addOne" useGeneratedKeys="true" keyColumn="userId" keyProperty="userId">
insert into user (username, email, icon, password, createTime, lastLoginTime) insert into user (username, email, icon, password, createTime, lastLoginTime, version)
value value
(#{username}, #{email}, #{icon}, #{password}, #{createTime}, #{lastLoginTime}) (#{username}, #{email}, #{icon}, #{password}, #{createTime}, #{lastLoginTime}, #{version})
</insert> </insert>
<select id="selectByUsernameOrEmail" resultType="com.fanxb.bookmark.common.entity.User"> <select id="selectByUsernameOrEmail" resultType="com.fanxb.bookmark.common.entity.po.User">
select select *
userId,
username,
email,
icon,
password,
createTime
from user from user
where username = #{name} or email = #{email} where username = #{name}
or email = #{email}
limit 1 limit 1
</select> </select>
@ -34,10 +29,11 @@
where email = #{email} where email = #{email}
</update> </update>
<select id="selectByUserId" resultType="com.fanxb.bookmark.common.entity.User"> <select id="selectByUserIdOrGithubId" resultType="com.fanxb.bookmark.common.entity.po.User">
select * select *
from user from user
where userId = #{userId} where userId = #{userId}
or githubId = #{githubId}
</select> </select>

View File

@ -37,53 +37,87 @@
<artifactId>commons-pool2</artifactId> <artifactId>commons-pool2</artifactId>
</dependency> </dependency>
<!--mybatis依赖-->
<!-- <dependency>-->
<!-- <groupId>org.mybatis.spring.boot</groupId>-->
<!-- <artifactId>mybatis-spring-boot-starter</artifactId>-->
<!-- <version>2.0.1</version>-->
<!-- </dependency>-->
<dependency> <dependency>
<groupId>com.baomidou</groupId> <groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId> <artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version> <version>3.5.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<version>2.5.5</version>
</dependency> </dependency>
<!--druid连接池依赖--> <!--druid连接池依赖-->
<dependency> <dependency>
<groupId>com.alibaba</groupId> <groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId> <artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.18</version> <version>1.2.18</version>
</dependency> </dependency>
<!--数据库版本管理--> <!--数据库版本管理-->
<dependency> <dependency>
<groupId>org.flywaydb</groupId> <groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId> <artifactId>flyway-core</artifactId>
<version>5.2.4</version> <version>9.21.1</version>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-mysql</artifactId>
<version>9.21.1</version>
</dependency> </dependency>
<!--mysql jdbc依赖--> <!--mysql jdbc依赖-->
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency> </dependency>
<!-- &lt;!&ndash;邮件依赖&ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-mail</artifactId>-->
<!-- </dependency>-->
<!-- &lt;!&ndash;减负依赖&ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>org.projectlombok</groupId>-->
<!-- <artifactId>lombok</artifactId>-->
<!-- </dependency>-->
<!-- &lt;!&ndash;json工具依赖&ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>com.alibaba</groupId>-->
<!-- <artifactId>fastjson</artifactId>-->
<!-- <version>1.2.83</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.elasticsearch.client</groupId>-->
<!-- <artifactId>elasticsearch-rest-high-level-client</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>cn.hutool</groupId>-->
<!-- <artifactId>hutool-all</artifactId>-->
<!-- <version>5.8.21</version>-->
<!-- </dependency>-->
<!--单元测试-->
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
<!--mysql jdbc依赖-->
<!-- <dependency>-->
<!-- <groupId>mysql</groupId>-->
<!-- <artifactId>mysql-connector-java</artifactId>-->
<!-- <version>8.0.33</version>-->
<!-- </dependency>-->
<!--邮件依赖--> <!--邮件依赖-->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId> <artifactId>spring-boot-starter-mail</artifactId>
</dependency> </dependency>
<!--减负依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--json工具依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.56</version>
</dependency>
<!--减负依赖--> <!--减负依赖-->
<dependency> <dependency>
@ -94,7 +128,7 @@
<dependency> <dependency>
<groupId>com.alibaba</groupId> <groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId> <artifactId>fastjson</artifactId>
<version>1.2.56</version> <version>1.2.83</version>
</dependency> </dependency>
@ -103,6 +137,11 @@
<artifactId>elasticsearch-rest-high-level-client</artifactId> <artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency> </dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.25</version>
</dependency>
<!--单元测试--> <!--单元测试-->
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test --> <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->

View File

@ -0,0 +1,29 @@
package com.fanxb.bookmark.common.annotation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.validation.constraints.Null;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义消费者注解
* Created with IntelliJ IDEA
* Created By Fxb
* Date: 2020/3/26
* Time: 15:26
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface MqConsumer {
/**
* 队列主题
*/
String value() default "default_es_topic";
}

View File

@ -0,0 +1,126 @@
package com.fanxb.bookmark.common.configuration;
import com.fanxb.bookmark.common.annotation.MqConsumer;
import com.fanxb.bookmark.common.entity.redis.RedisConsumer;
import com.fanxb.bookmark.common.factory.ThreadPoolFactory;
import com.fanxb.bookmark.common.util.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created with IntelliJ IDEA
* Created By Fxb
* Date: 2020/3/24
* Time: 15:37
*/
@Component
@Slf4j
public class MqConfiguration implements ApplicationRunner, DisposableBean {
/**
* 是否运行
*/
private static volatile boolean isRun = true;
/**
* 订阅对象与执行方法关系支持广播模式
*/
private static final Map<String, List<RedisConsumer>> topicMap = new HashMap<>();
/**
* 执行线程池
*/
private static final ThreadPoolExecutor threadPoolExecutor = ThreadPoolFactory.createPool(2, 8, 5000, 1000, "mqConsumer");
@Autowired
ApplicationContext context;
@Override
public void run(ApplicationArguments args) {
Map<String, Object> map = context.getBeansWithAnnotation(MqConsumer.class);
map.values().forEach(item -> {
if (!(item instanceof RedisConsumer)) {
log.warn("注意检测到被@EsConsumer注解的类{}未实现RedisConsumer接口", item.getClass().getCanonicalName());
return;
}
MqConsumer[] annotations = item.getClass().getAnnotationsByType(MqConsumer.class);
MqConsumer annotation = annotations[0];
topicMap.computeIfAbsent(annotation.value(), k -> new ArrayList<>()).add((RedisConsumer) item);
});
log.info("redis订阅信息汇总完毕");
//由一个线程始终循环获取es队列数据
threadPoolExecutor.execute(loop());
}
@Override
public void destroy() {
log.info("进程结束关闭redis");
isRun = false;
ThreadPoolFactory.shutdown(threadPoolExecutor);
}
private Runnable loop() {
return () -> {
while (isRun) {
AtomicInteger count = new AtomicInteger(0);
topicMap.forEach((k, v) -> {
try {
String message = RedisUtil.redisTemplate.opsForList().rightPop(k);
if (message == null) {
count.getAndIncrement();
} else {
pushTask(v, message, k);
}
} catch (Exception e) {
log.error("redis消息队列异常", e);
}
});
if (count.get() == topicMap.keySet().size()) {
//当所有的队列都为空时休眠3s
try {
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
log.error("休眠出错", e);
}
}
}
};
}
/**
* 功能描述: 推送任务到线程池中执行
*
* @param list list
* @param value value
* @param key key
* @author 123
* @date 2020/3/28 23:52
*/
private void pushTask(List<RedisConsumer> list, String value, String key) {
for (RedisConsumer consumer : list) {
threadPoolExecutor.execute(() -> {
try {
consumer.deal(value);
} catch (Exception e) {
log.error("执行消费任务出错", e);
if (list.size() == 1) {
//非广播消息进行数据回补
RedisUtil.redisTemplate.opsForList().rightPush(key, value);
}
}
});
}
}
}

View File

@ -1,6 +1,9 @@
package com.fanxb.bookmark.common.configuration; package com.fanxb.bookmark.common.configuration;
import com.fanxb.bookmark.common.factory.CustomThreadFactory; import com.fanxb.bookmark.common.factory.CustomThreadFactory;
import com.fanxb.bookmark.common.factory.ThreadPoolFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer; import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar; import org.springframework.scheduling.config.ScheduledTaskRegistrar;
@ -11,15 +14,16 @@ import java.util.concurrent.ScheduledThreadPoolExecutor;
* Created with IntelliJ IDEA * Created with IntelliJ IDEA
* *
* @author fanxb * @author fanxb
* @date 2020/1/26
*/ */
@Configuration
@Slf4j
public class ScheduleConfig implements SchedulingConfigurer { public class ScheduleConfig implements SchedulingConfigurer {
@Override @Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) { public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
ScheduledExecutorService service = new ScheduledThreadPoolExecutor(5, new CustomThreadFactory("schedule")); ScheduledExecutorService service = new ScheduledThreadPoolExecutor(5, new CustomThreadFactory("schedule"));
scheduledTaskRegistrar.setScheduler(service); scheduledTaskRegistrar.setScheduler(service);
log.info("自定义schedule线程池成功");
} }
} }

View File

@ -8,10 +8,9 @@ import org.springframework.stereotype.Component;
* 类功能详述 * 类功能详述
* *
* @author fanxb * @author fanxb
* @date 2019/4/4 16:10
*/ */
@Component @Component
public class Constant { public class CommonConstant {
/** /**
@ -37,18 +36,21 @@ public class Constant {
return email + "_authCode"; return email + "_authCode";
} }
/**
* 是否为开发环境
*/
public static boolean isDev = false; public static boolean isDev = false;
@Value("${isDev}") @Value("${spring.profiles.active}")
public void setIsDev(boolean isDev) { public void setIsDev(String active) {
Constant.isDev = isDev; CommonConstant.isDev = active.contains("dev");
} }
public static String jwtSecret = ""; public static String jwtSecret = "";
@Value("${jwtSecret}") @Value("${jwtSecret}")
public void setJwtSecret(String jwtSecret) { public void setJwtSecret(String jwtSecret) {
Constant.jwtSecret = jwtSecret; CommonConstant.jwtSecret = jwtSecret;
} }
/** /**
@ -71,5 +73,4 @@ public class Constant {
public void setServiceAddress(String address) { public void setServiceAddress(String address) {
serviceAddress = address; serviceAddress = address;
} }
} }

View File

@ -13,4 +13,8 @@ public class NumberConstant {
* 2^10 * 2^10
*/ */
public static final int K_SIZE = 1024; public static final int K_SIZE = 1024;
/**
* 一天的秒数
*/
public static final int S_DAY = 24 * 60 * 60;
} }

View File

@ -14,5 +14,27 @@ public class RedisConstant {
/** /**
* 某用户书签数据更新时间,该队列左进右出 * 某用户书签数据更新时间,该队列左进右出
*/ */
public static final String BOOKMARK_UPDATE_TIME = "bookmark_update_time"; public static final String BOOKMARK_UPDATE_VERSION = "bookmark_update_version";
/**
* 某个用户上传了文件夹需要进行书签转化
*/
public static final String BOOKMARK_PINYIN_CHANGE = "bookmark_pinyin_change";
/**
* 插入书签数据到es中
*/
public static final String BOOKMARK_INSERT_ES = "bookmark_insert_es";
/**
* 从es中删除数据
*/
public static final String BOOKMARK_DELETE_ES = "bookmark_DELETE_es";
/**
* 书签访问次数+1
*/
public static final String BOOKMARK_VISIT_NUM_PLUS = "bookmark_visit_num_plus";
public static String getUserFailCountKey(String username) {
return "bookmark_user_fail_count_" + username;
}
public static final String BING_IMG = "bing_img";
} }

View File

@ -0,0 +1,36 @@
package com.fanxb.bookmark.common.controller;
import com.fanxb.bookmark.common.entity.Result;
import com.fanxb.bookmark.common.service.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author fanxb
* @date 2021-09-15-下午9:55
*/
@RestController
@RequestMapping("/common/config")
public class ConfigController {
private final ConfigService configService;
@Autowired
public ConfigController(ConfigService configService) {
this.configService = configService;
}
/**
* 获取全局配置
*
* @return com.fanxb.bookmark.common.entity.Result
* @author fanxb
* @date 2021/9/15 下午9:56
*/
@GetMapping("/global")
public Result getGlobalConfig() {
return Result.success(configService.getGlobalConfig());
}
}

View File

@ -0,0 +1,14 @@
package com.fanxb.bookmark.common.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.fanxb.bookmark.common.entity.po.GlobalConfigPo;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* @author fanxb
*/
@Mapper
public interface GlobalConfigDao extends BaseMapper<GlobalConfigPo> {
}

View File

@ -1,6 +1,6 @@
package com.fanxb.bookmark.common.dao; package com.fanxb.bookmark.common.dao;
import com.fanxb.bookmark.common.entity.Url; import com.fanxb.bookmark.common.entity.po.Url;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.util.List; import java.util.List;
@ -20,7 +20,7 @@ public interface UrlDao {
* *
* @author fanxb * @author fanxb
* @date 2019/7/9 14:52 * @date 2019/7/9 14:52
* @return java.util.List<com.fanxb.bookmark.common.entity.Url> * @return java.util.List<com.fanxb.bookmark.common.entity.po.Url>
*/ */
List<Url> getPublicUrl(); List<Url> getPublicUrl();
} }

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