├── src └── main │ ├── resources │ ├── static │ │ ├── css │ │ │ ├── buttons.min.css │ │ │ ├── dashicons.min.css │ │ │ ├── alt │ │ │ │ ├── AdminLTE-select2.min.css │ │ │ │ ├── AdminLTE-bootstrap-social.min.css │ │ │ │ └── AdminLTE-without-plugins.min.css │ │ │ ├── skins │ │ │ │ └── _all-skins.min.css │ │ │ └── AdminLTE.min.css │ │ ├── front │ │ │ ├── js │ │ │ │ └── bundle.min.js │ │ │ ├── images │ │ │ │ ├── logo.jpeg │ │ │ │ ├── single-topic-img01.jpg │ │ │ │ ├── single-topic-img02.jpg │ │ │ │ ├── single-topic-img03.jpg │ │ │ │ ├── single-topic-img04.jpg │ │ │ │ ├── single-topic-img05.jpg │ │ │ │ ├── single-topic-img06.jpg │ │ │ │ ├── single-topic-img07.jpg │ │ │ │ └── single-topic-img08.jpg │ │ │ ├── favicon │ │ │ │ └── favicon.ico │ │ │ ├── css │ │ │ │ └── main.css │ │ │ └── scss │ │ │ │ ├── _custom_magnific_popup.scss │ │ │ │ ├── _pageContent.scss │ │ │ │ ├── _layout404.scss │ │ │ │ ├── _variables.scss │ │ │ │ ├── _btn-create-topic.scss │ │ │ │ ├── _categories_single.scss │ │ │ │ ├── _user_header.scss │ │ │ │ ├── _login.scss │ │ │ │ ├── _gallery.scss │ │ │ │ ├── _default_tabs.scss │ │ │ │ ├── _editor.scss │ │ │ │ ├── _categories_list.scss │ │ │ │ └── _popup_settings.scss │ │ ├── plugins │ │ │ ├── pace │ │ │ │ ├── pace.min.js │ │ │ │ ├── pace.min.css │ │ │ │ └── pace.css │ │ │ ├── layer │ │ │ │ ├── mobile │ │ │ │ │ ├── need │ │ │ │ │ │ └── layer.css │ │ │ │ │ └── layer.js │ │ │ │ ├── theme │ │ │ │ │ └── default │ │ │ │ │ │ ├── layer.css │ │ │ │ │ │ ├── icon.png │ │ │ │ │ │ ├── icon-ext.png │ │ │ │ │ │ ├── loading-0.gif │ │ │ │ │ │ ├── loading-1.gif │ │ │ │ │ │ └── loading-2.gif │ │ │ │ └── layer.js │ │ │ ├── toast │ │ │ │ ├── js │ │ │ │ │ └── jquery.toast.min.js │ │ │ │ └── css │ │ │ │ │ └── jquery.toast.min.css │ │ │ ├── bootstrap │ │ │ │ ├── css │ │ │ │ │ ├── bootstrap.css.map │ │ │ │ │ ├── bootstrap-theme.css.map │ │ │ │ │ ├── bootstrap.min.css.map │ │ │ │ │ └── bootstrap-theme.min.css.map │ │ │ │ ├── fonts │ │ │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ │ │ └── glyphicons-halflings-regular.woff2 │ │ │ │ └── js │ │ │ │ │ └── npm.js │ │ │ ├── bootstrapStyle │ │ │ │ ├── bootstrapStyle.css │ │ │ │ └── img │ │ │ │ │ └── bootstrap.png │ │ │ ├── pretty-checkbox │ │ │ │ └── maps │ │ │ │ │ └── pretty-checkbox.css.map │ │ │ ├── lazyload │ │ │ │ └── jquery.lazyload.min.js │ │ │ ├── font-awesome │ │ │ │ └── fonts │ │ │ │ │ ├── FontAwesome.otf │ │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ │ ├── fontawesome-webfont.woff │ │ │ │ │ └── fontawesome-webfont.woff2 │ │ │ ├── froala-editor │ │ │ │ ├── js │ │ │ │ │ ├── plugins │ │ │ │ │ │ ├── url.min.js │ │ │ │ │ │ ├── align.min.js │ │ │ │ │ │ ├── colors.min.js │ │ │ │ │ │ ├── entities.min.js │ │ │ │ │ │ ├── file.min.js │ │ │ │ │ │ ├── forms.min.js │ │ │ │ │ │ ├── help.min.js │ │ │ │ │ │ ├── image.min.js │ │ │ │ │ │ ├── link.min.js │ │ │ │ │ │ ├── lists.min.js │ │ │ │ │ │ ├── print.min.js │ │ │ │ │ │ ├── quote.min.js │ │ │ │ │ │ ├── save.min.js │ │ │ │ │ │ ├── table.min.js │ │ │ │ │ │ ├── video.min.js │ │ │ │ │ │ ├── char_counter.min.js │ │ │ │ │ │ ├── code_view.min.js │ │ │ │ │ │ ├── draggable.min.js │ │ │ │ │ │ ├── emoticons.min.js │ │ │ │ │ │ ├── font_family.min.js │ │ │ │ │ │ ├── font_size.min.js │ │ │ │ │ │ ├── fullscreen.min.js │ │ │ │ │ │ ├── image_manager.min.js │ │ │ │ │ │ ├── inline_class.min.js │ │ │ │ │ │ ├── inline_style.min.js │ │ │ │ │ │ ├── line_breaker.min.js │ │ │ │ │ │ ├── line_height.min.js │ │ │ │ │ │ ├── quick_insert.min.js │ │ │ │ │ │ ├── word_paste.min.js │ │ │ │ │ │ ├── code_beautifier.min.js │ │ │ │ │ │ ├── paragraph_format.min.js │ │ │ │ │ │ ├── paragraph_style.min.js │ │ │ │ │ │ └── special_characters.min.js │ │ │ │ │ ├── froala_editor.min.js │ │ │ │ │ ├── froala_editor.pkgd.min.js │ │ │ │ │ └── third_party │ │ │ │ │ │ ├── embedly.min.js │ │ │ │ │ │ ├── image_tui.min.js │ │ │ │ │ │ ├── font_awesome.min.js │ │ │ │ │ │ ├── image_aviary.min.js │ │ │ │ │ │ └── spell_checker.min.js │ │ │ │ └── css │ │ │ │ │ ├── froala_editor.min.css │ │ │ │ │ ├── froala_style.min.css │ │ │ │ │ ├── plugins │ │ │ │ │ ├── colors.min.css │ │ │ │ │ ├── file.min.css │ │ │ │ │ ├── help.min.css │ │ │ │ │ ├── image.min.css │ │ │ │ │ ├── table.min.css │ │ │ │ │ ├── video.min.css │ │ │ │ │ ├── code_view.min.css │ │ │ │ │ ├── draggable.min.css │ │ │ │ │ ├── emoticons.min.css │ │ │ │ │ ├── fullscreen.min.css │ │ │ │ │ ├── char_counter.min.css │ │ │ │ │ ├── image_manager.min.css │ │ │ │ │ ├── line_breaker.min.css │ │ │ │ │ ├── quick_insert.min.css │ │ │ │ │ ├── special_characters.min.css │ │ │ │ │ ├── fullscreen.css │ │ │ │ │ ├── line_breaker.css │ │ │ │ │ ├── draggable.css │ │ │ │ │ ├── emoticons.css │ │ │ │ │ ├── special_characters.css │ │ │ │ │ ├── char_counter.css │ │ │ │ │ ├── help.css │ │ │ │ │ ├── quick_insert.css │ │ │ │ │ └── code_view.css │ │ │ │ │ ├── themes │ │ │ │ │ ├── dark.min.css │ │ │ │ │ ├── gray.min.css │ │ │ │ │ ├── red.min.css │ │ │ │ │ └── royal.min.css │ │ │ │ │ ├── froala_editor.pkgd.min.css │ │ │ │ │ └── third_party │ │ │ │ │ ├── embedly.min.css │ │ │ │ │ ├── font_awesome.min.css │ │ │ │ │ ├── image_tui.min.css │ │ │ │ │ ├── spell_checker.min.css │ │ │ │ │ ├── image_tui.css │ │ │ │ │ ├── font_awesome.css │ │ │ │ │ ├── embedly.css │ │ │ │ │ └── spell_checker.css │ │ │ ├── animate │ │ │ │ └── animate.min.css │ │ │ └── bootstrapvalidator │ │ │ │ └── css │ │ │ │ ├── bootstrapValidator.min.css │ │ │ │ └── bootstrapValidator.css │ │ └── images │ │ │ ├── icon-spring-framework.svg │ │ │ ├── ghs.png │ │ │ ├── icons.png │ │ │ ├── bg_icons.png │ │ │ ├── boxed-bg.jpg │ │ │ ├── boxed-bg.png │ │ │ ├── default.png │ │ │ ├── favicon.ico │ │ │ ├── avatar │ │ │ ├── 1.jpeg │ │ │ ├── 10.jpeg │ │ │ ├── 11.jpeg │ │ │ ├── 12.jpeg │ │ │ ├── 13.jpeg │ │ │ ├── 14.jpeg │ │ │ ├── 15.jpeg │ │ │ ├── 16.jpeg │ │ │ ├── 17.jpeg │ │ │ ├── 18.jpeg │ │ │ ├── 19.jpeg │ │ │ ├── 2.jpeg │ │ │ ├── 20.jpeg │ │ │ ├── 21.jpeg │ │ │ ├── 22.jpeg │ │ │ ├── 23.jpeg │ │ │ ├── 24.jpeg │ │ │ ├── 25.jpeg │ │ │ ├── 26.jpeg │ │ │ ├── 27.jpeg │ │ │ ├── 28.jpeg │ │ │ ├── 29.jpeg │ │ │ ├── 3.jpeg │ │ │ ├── 30.jpeg │ │ │ ├── 31.jpeg │ │ │ ├── 32.jpeg │ │ │ ├── 33.jpeg │ │ │ ├── 34.jpeg │ │ │ ├── 35.jpeg │ │ │ ├── 36.jpeg │ │ │ ├── 37.jpeg │ │ │ ├── 38.jpeg │ │ │ ├── 39.jpeg │ │ │ ├── 4.jpeg │ │ │ ├── 40.jpeg │ │ │ ├── 5.jpeg │ │ │ ├── 6.jpeg │ │ │ ├── 7.jpeg │ │ │ ├── 8.jpeg │ │ │ └── 9.jpeg │ │ │ ├── notlogin.jpeg │ │ │ ├── default-50x50.gif │ │ │ ├── login-third-party-new.png │ │ │ └── tail-spin.svg │ ├── mapper │ │ ├── LinkMapper.xml │ │ ├── TaskMapper.xml │ │ ├── UserRoleRefMapper.xml │ │ ├── RolePermissionRefMapper.xml │ │ ├── CategoryMapper.xml │ │ ├── PostTagRefMapper.xml │ │ ├── PostCategoryRefMapper.xml │ │ ├── TagMapper.xml │ │ └── RoleMapper.xml │ └── application.yaml │ └── java │ └── com │ └── example │ └── forum │ ├── mapper │ ├── LinkMapper.java │ ├── UserRoleRefMapper.java │ ├── RolePermissionRefMapper.java │ ├── CategoryMapper.java │ ├── PostTagRefMapper.java │ ├── PostCategoryRefMapper.java │ ├── TagMapper.java │ ├── RoleMapper.java │ ├── PostMapper.java │ ├── PermissionMapper.java │ ├── CommentMapper.java │ └── UserMapper.java │ ├── service │ ├── LinkService.java │ ├── UserRoleRefService.java │ ├── MailService.java │ ├── RolePermissionRefService.java │ ├── impl │ │ ├── RolePermissionRefServiceImpl.java │ │ ├── MailServiceImpl.java │ │ ├── UserRoleRefServiceImpl.java │ │ └── LinkServiceImpl.java │ ├── PostService.java │ ├── CategoryService.java │ ├── CommentService.java │ ├── TagService.java │ ├── PermissionService.java │ ├── RoleService.java │ └── UserService.java │ ├── vo │ ├── SearchVo.java │ └── PageVo.java │ ├── common │ ├── constant │ │ └── CommonConstant.java │ └── base │ │ └── BaseEntity.java │ ├── enums │ ├── ResultCodeEnum.java │ ├── UserStatusEnum.java │ ├── TrueFalseEnum.java │ ├── CommentIsReadEnum.java │ ├── RoleEnum.java │ ├── CommonParamsEnum.java │ ├── LogTypeEnum.java │ ├── PostTypeEnum.java │ ├── PostStatusEnum.java │ └── ResourceTypeEnum.java │ ├── dto │ ├── PostQueryCondition.java │ ├── QueryCondition.java │ └── JsonResult.java │ ├── entity │ ├── Category.java │ ├── Tag.java │ ├── Link.java │ ├── RolePermissionRef.java │ ├── UserRoleRef.java │ ├── PostTagRef.java │ ├── PostCategoryRef.java │ ├── Role.java │ ├── Permission.java │ ├── Comment.java │ ├── User.java │ └── Post.java │ ├── util │ ├── SensUtils.java │ ├── RegexUtil.java │ ├── ThreadPoolUtil.java │ ├── SpringUtils.java │ ├── SpringUtil.java │ ├── IpInfoUtil.java │ ├── ObjectUtil.java │ ├── Md5Util.java │ ├── CommentUtil.java │ ├── RelativeDateFormat.java │ ├── Response.java │ └── FileUtil.java │ ├── Application.java │ └── exception │ └── MyBusinessException.java ├── img ├── db.png ├── home.png ├── smtp.png ├── user.png ├── comment.png ├── forget.png ├── login.png ├── project.png ├── category.png ├── post-edit.png ├── register.png ├── role-edit.png ├── admin-admin.png ├── admin-user.png ├── post-details.png └── post-details2.png ├── .gitattributes ├── .gitignore └── README.md /src/main/resources/static/css/buttons.min.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/static/css/dashicons.min.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/static/front/js/bundle.min.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/pace/pace.min.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/static/css/alt/AdminLTE-select2.min.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/static/css/skins/_all-skins.min.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/static/images/icon-spring-framework.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/layer/mobile/need/layer.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/layer/theme/default/layer.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/toast/js/jquery.toast.min.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/db.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/img/db.png -------------------------------------------------------------------------------- /src/main/resources/static/css/alt/AdminLTE-bootstrap-social.min.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/bootstrap/css/bootstrap.css.map: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/bootstrapStyle/bootstrapStyle.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/toast/css/jquery.toast.min.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/img/home.png -------------------------------------------------------------------------------- /img/smtp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/img/smtp.png -------------------------------------------------------------------------------- /img/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/img/user.png -------------------------------------------------------------------------------- /src/main/resources/static/plugins/bootstrap/css/bootstrap-theme.css.map: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/bootstrap/css/bootstrap.min.css.map: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/img/comment.png -------------------------------------------------------------------------------- /img/forget.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/img/forget.png -------------------------------------------------------------------------------- /img/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/img/login.png -------------------------------------------------------------------------------- /img/project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/img/project.png -------------------------------------------------------------------------------- /src/main/resources/static/plugins/bootstrap/css/bootstrap-theme.min.css.map: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/pretty-checkbox/maps/pretty-checkbox.css.map: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/category.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/img/category.png -------------------------------------------------------------------------------- /img/post-edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/img/post-edit.png -------------------------------------------------------------------------------- /img/register.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/img/register.png -------------------------------------------------------------------------------- /img/role-edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/img/role-edit.png -------------------------------------------------------------------------------- /img/admin-admin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/img/admin-admin.png -------------------------------------------------------------------------------- /img/admin-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/img/admin-user.png -------------------------------------------------------------------------------- /img/post-details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/img/post-details.png -------------------------------------------------------------------------------- /img/post-details2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/img/post-details2.png -------------------------------------------------------------------------------- /src/main/resources/static/images/ghs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/ghs.png -------------------------------------------------------------------------------- /src/main/resources/static/plugins/layer/layer.js: -------------------------------------------------------------------------------- 1 | /*! layer-v3.1.1 Web弹层组件 MIT License http://layer.layui.com/ By 贤心 */ 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=Java 2 | *.css linguist-language=Java 3 | *.html linguist-language=Thymeleaf 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/images/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/icons.png -------------------------------------------------------------------------------- /src/main/resources/static/images/bg_icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/bg_icons.png -------------------------------------------------------------------------------- /src/main/resources/static/images/boxed-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/boxed-bg.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/boxed-bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/boxed-bg.png -------------------------------------------------------------------------------- /src/main/resources/static/images/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/default.png -------------------------------------------------------------------------------- /src/main/resources/static/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/favicon.ico -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/1.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/10.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/10.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/11.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/11.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/12.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/12.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/13.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/13.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/14.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/14.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/15.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/15.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/16.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/16.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/17.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/17.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/18.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/18.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/19.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/19.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/2.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/20.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/20.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/21.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/21.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/22.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/22.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/23.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/23.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/24.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/24.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/25.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/25.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/26.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/26.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/27.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/27.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/28.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/28.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/29.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/29.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/3.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/30.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/30.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/31.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/31.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/32.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/32.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/33.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/33.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/34.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/34.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/35.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/35.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/36.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/36.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/37.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/37.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/38.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/38.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/39.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/39.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/4.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/40.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/40.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/5.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/5.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/6.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/6.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/7.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/7.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/8.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/8.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/avatar/9.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/avatar/9.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/notlogin.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/notlogin.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/plugins/lazyload/jquery.lazyload.min.js: -------------------------------------------------------------------------------- 1 | /*! Lazy Load 1.9.7 - MIT license - Copyright 2010-2015 Mika Tuupola */ 2 | -------------------------------------------------------------------------------- /src/main/resources/static/front/images/logo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/front/images/logo.jpeg -------------------------------------------------------------------------------- /src/main/resources/static/images/default-50x50.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/default-50x50.gif -------------------------------------------------------------------------------- /src/main/resources/static/plugins/layer/mobile/layer.js: -------------------------------------------------------------------------------- 1 | /*! layer mobile-v2.0.0 Web弹层组件 MIT License http://layer.layui.com/mobile By 贤心 */ 2 | -------------------------------------------------------------------------------- /src/main/resources/static/front/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/front/favicon/favicon.ico -------------------------------------------------------------------------------- /src/main/resources/static/front/css/main.css: -------------------------------------------------------------------------------- 1 | .avatar { 2 | width: 40px; 3 | height: 40px; 4 | border-radius: 50%; 5 | background-color: #fff; 6 | -------------------------------------------------------------------------------- /src/main/resources/static/images/login-third-party-new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/images/login-third-party-new.png -------------------------------------------------------------------------------- /src/main/resources/static/front/images/single-topic-img01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/front/images/single-topic-img01.jpg -------------------------------------------------------------------------------- /src/main/resources/static/front/images/single-topic-img02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/front/images/single-topic-img02.jpg -------------------------------------------------------------------------------- /src/main/resources/static/front/images/single-topic-img03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/front/images/single-topic-img03.jpg -------------------------------------------------------------------------------- /src/main/resources/static/front/images/single-topic-img04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/front/images/single-topic-img04.jpg -------------------------------------------------------------------------------- /src/main/resources/static/front/images/single-topic-img05.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/front/images/single-topic-img05.jpg -------------------------------------------------------------------------------- /src/main/resources/static/front/images/single-topic-img06.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/front/images/single-topic-img06.jpg -------------------------------------------------------------------------------- /src/main/resources/static/front/images/single-topic-img07.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/front/images/single-topic-img07.jpg -------------------------------------------------------------------------------- /src/main/resources/static/front/images/single-topic-img08.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/front/images/single-topic-img08.jpg -------------------------------------------------------------------------------- /src/main/resources/static/plugins/layer/theme/default/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/plugins/layer/theme/default/icon.png -------------------------------------------------------------------------------- /src/main/resources/static/plugins/bootstrapStyle/img/bootstrap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/plugins/bootstrapStyle/img/bootstrap.png -------------------------------------------------------------------------------- /src/main/resources/static/plugins/layer/theme/default/icon-ext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/plugins/layer/theme/default/icon-ext.png -------------------------------------------------------------------------------- /src/main/resources/static/plugins/layer/theme/default/loading-0.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/plugins/layer/theme/default/loading-0.gif -------------------------------------------------------------------------------- /src/main/resources/static/plugins/layer/theme/default/loading-1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/plugins/layer/theme/default/loading-1.gif -------------------------------------------------------------------------------- /src/main/resources/static/plugins/layer/theme/default/loading-2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/plugins/layer/theme/default/loading-2.gif -------------------------------------------------------------------------------- /src/main/resources/static/plugins/font-awesome/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/plugins/font-awesome/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /src/main/resources/static/plugins/font-awesome/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/plugins/font-awesome/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /src/main/resources/static/plugins/font-awesome/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/plugins/font-awesome/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /src/main/resources/static/plugins/font-awesome/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/plugins/font-awesome/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /src/main/resources/static/plugins/font-awesome/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/plugins/font-awesome/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /src/main/resources/static/plugins/bootstrap/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/plugins/bootstrap/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /src/main/resources/static/plugins/bootstrap/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/plugins/bootstrap/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /src/main/resources/static/plugins/bootstrap/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/plugins/bootstrap/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /src/main/resources/static/plugins/bootstrap/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saysky/forum/HEAD/src/main/resources/static/plugins/bootstrap/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/url.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/froala_editor.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/froala_style.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/plugins/colors.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/plugins/file.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/plugins/help.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/plugins/image.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/plugins/table.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/plugins/video.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/themes/dark.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/themes/gray.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/themes/red.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/themes/royal.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/froala_editor.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/froala_editor.pkgd.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/align.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/colors.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/entities.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/file.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/forms.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/help.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/image.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/link.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/lists.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/print.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/quote.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/save.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/table.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/video.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/mapper/LinkMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/main/resources/mapper/TaskMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/froala_editor.pkgd.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/plugins/code_view.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/plugins/draggable.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/plugins/emoticons.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/plugins/fullscreen.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/third_party/embedly.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/char_counter.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/code_view.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/draggable.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/emoticons.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/font_family.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/font_size.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/fullscreen.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/image_manager.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/inline_class.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/inline_style.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/line_breaker.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/line_height.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/quick_insert.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/word_paste.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/third_party/embedly.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/third_party/image_tui.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/plugins/char_counter.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/plugins/image_manager.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/plugins/line_breaker.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/plugins/quick_insert.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/third_party/font_awesome.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/third_party/image_tui.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/code_beautifier.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/paragraph_format.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/paragraph_style.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/plugins/special_characters.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/third_party/font_awesome.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/third_party/image_aviary.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/js/third_party/spell_checker.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/plugins/special_characters.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/third_party/spell_checker.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/static/css/AdminLTE.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * AdminLTE v2.4.5 3 | * Author: Almsaeed Studio 4 | * Website: Almsaeed Studio 5 | * License: Open source - MIT 6 | * Please visit http://opensource.org/licenses/MIT for more information 7 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/animate/animate.min.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | /*! 4 | * animate.css -http://daneden.me/animate 5 | * Version - 3.5.1 6 | * Licensed under the MIT license - http://opensource.org/licenses/MIT 7 | * 8 | * Copyright (c) 2016 Daniel Eden 9 | */ 10 | 11 | -------------------------------------------------------------------------------- /src/main/resources/static/css/alt/AdminLTE-without-plugins.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * AdminLTE v2.4.0 Without Third-Party Plugins 3 | * Author: Almsaeed Studio 4 | * Website: Almsaeed Studio 5 | * License: Open source - MIT 6 | * Please visit http://opensource.org/licenses/MIT for more information 7 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/mapper/LinkMapper.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.example.forum.entity.Link; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | /** 8 | * @author liuyanzhao 9 | */ 10 | @Mapper 11 | public interface LinkMapper extends BaseMapper { 12 | 13 | 14 | } 15 | 16 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/service/LinkService.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.service; 2 | 3 | 4 | import com.example.forum.common.base.BaseService; 5 | import com.example.forum.entity.Link; 6 | 7 | /** 8 | *
 9 |  *     友情链接业务逻辑接口
10 |  * 
11 | * 12 | * @author : saysky 13 | * @date : 2017/11/14 14 | */ 15 | public interface LinkService extends BaseService { 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/vo/SearchVo.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.vo; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * @author example 9 | */ 10 | @Data 11 | public class SearchVo implements Serializable { 12 | 13 | /** 14 | * 起始日期 15 | */ 16 | private String startDate; 17 | 18 | /** 19 | * 结束日期 20 | */ 21 | private String endDate; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/service/UserRoleRefService.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.service; 2 | 3 | import com.example.forum.entity.UserRoleRef; 4 | import com.example.forum.common.base.BaseService; 5 | 6 | 7 | public interface UserRoleRefService extends BaseService { 8 | 9 | /** 10 | * 根据用户Id删除 11 | * 12 | * @param userId 用户Id 13 | */ 14 | void deleteByUserId(Long userId); 15 | 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/common/constant/CommonConstant.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.common.constant; 2 | 3 | /** 4 | * 常量 5 | * @author 言曌 6 | */ 7 | public interface CommonConstant { 8 | 9 | /** 10 | * 正常状态 11 | */ 12 | Integer STATUS_NORMAL = 0; 13 | 14 | /** 15 | * 用户密码加盐的盐 16 | */ 17 | String PASSWORD_SALT = "sens"; 18 | 19 | /** 20 | * none 21 | */ 22 | String NONE = "none"; 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/enums/ResultCodeEnum.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.enums; 2 | 3 | /** 4 | * 返回结果enum 5 | */ 6 | public enum ResultCodeEnum { 7 | 8 | /** 9 | * 成功 10 | */ 11 | SUCCESS(1), 12 | 13 | /** 14 | * 失败 15 | */ 16 | FAIL(0); 17 | 18 | Integer code; 19 | 20 | ResultCodeEnum(Integer code) { 21 | this.code = code; 22 | } 23 | 24 | public Integer getCode() { 25 | return code; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/enums/UserStatusEnum.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.enums; 2 | 3 | /** 4 | * 用户状态enum 5 | */ 6 | public enum UserStatusEnum { 7 | 8 | /** 9 | * 正常 10 | */ 11 | NORMAL(0), 12 | 13 | /** 14 | * 禁止登录 15 | */ 16 | BAN(1); 17 | 18 | 19 | private Integer code; 20 | 21 | UserStatusEnum(Integer code) { 22 | this.code = code; 23 | } 24 | 25 | public Integer getCode() { 26 | return code; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/service/MailService.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.service; 2 | 3 | 4 | import javax.mail.MessagingException; 5 | 6 | /** 7 | *
 8 |  *     邮件发送业务逻辑接口
 9 |  * 
10 | */ 11 | public interface MailService { 12 | 13 | /** 14 | * 发送邮件 15 | * 16 | * @param to 接收者 17 | * @param title 标题 18 | * @param content 内容 19 | */ 20 | void sendMail(String to, String title, String content) throws MessagingException; 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/enums/TrueFalseEnum.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.enums; 2 | 3 | /** 4 | * true or false enum 5 | */ 6 | public enum TrueFalseEnum { 7 | 8 | /** 9 | * 真 10 | */ 11 | TRUE("true"), 12 | 13 | /** 14 | * 假 15 | */ 16 | FALSE("false"); 17 | 18 | private String value; 19 | 20 | TrueFalseEnum(String value) { 21 | this.value = value; 22 | } 23 | 24 | public String getValue() { 25 | return value; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/resources/static/front/scss/_custom_magnific_popup.scss: -------------------------------------------------------------------------------- 1 | .mfp-gallery{ 2 | button{ 3 | outline: none; 4 | } 5 | } 6 | .mfp-with-zoom .mfp-container, 7 | .mfp-with-zoom.mfp-bg { 8 | opacity: 0; 9 | -webkit-backface-visibility: hidden; 10 | transition: all 0.3s ease-out; 11 | } 12 | .mfp-with-zoom.mfp-ready .mfp-container { 13 | opacity: 1; 14 | } 15 | .mfp-with-zoom.mfp-ready.mfp-bg { 16 | opacity: 0.8; 17 | } 18 | .mfp-with-zoom.mfp-removing .mfp-container, 19 | .mfp-with-zoom.mfp-removing.mfp-bg { 20 | opacity: 0; 21 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/bootstrap/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/dto/PostQueryCondition.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author 言曌 7 | * @date 2020/3/12 4:53 下午 8 | */ 9 | @Data 10 | public class PostQueryCondition { 11 | 12 | /** 13 | * 用户ID 14 | */ 15 | private Long userId; 16 | 17 | /** 18 | * 标签ID 19 | */ 20 | private Long tagId; 21 | 22 | /** 23 | * 分类ID 24 | */ 25 | private Long cateId; 26 | 27 | /** 28 | * 关键字 29 | */ 30 | private String keywords; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/mapper/UserRoleRefMapper.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.example.forum.entity.UserRoleRef; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | /** 8 | * @author example 9 | */ 10 | @Mapper 11 | public interface UserRoleRefMapper extends BaseMapper { 12 | 13 | /** 14 | * 根据用户Id删除 15 | * 16 | * @param userId 用户Id 17 | * @return 影响行数 18 | */ 19 | Integer deleteByUserId(Long userId); 20 | } 21 | 22 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/bootstrapvalidator/css/bootstrapValidator.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * BootstrapValidator (http://bootstrapvalidator.com) 3 | * The best jQuery plugin to validate form fields. Designed to use with Bootstrap 3 4 | * 5 | * @version v0.5.3, built on 2014-11-05 9:14:18 PM 6 | * @author https://twitter.com/nghuuphuoc 7 | * @copyright (c) 2013 - 2014 Nguyen Huu Phuoc 8 | * @license Commercial: http://bootstrapvalidator.com/license/ 9 | * Non-commercial: http://creativecommons.org/licenses/by-nc-nd/3.0/ 10 | */ 11 | 12 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/enums/CommentIsReadEnum.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.enums; 2 | 3 | /** 4 | *
 5 |  *     评论已读状态enum
 6 |  * 
7 | * 8 | * @author : saysky 9 | * @date : 2018/7/1 10 | */ 11 | public enum CommentIsReadEnum { 12 | 13 | /** 14 | * 已读 15 | */ 16 | READ(1), 17 | 18 | /** 19 | * 未读 20 | */ 21 | NOT_READ(0); 22 | 23 | private Integer code; 24 | 25 | CommentIsReadEnum(Integer code) { 26 | this.code = code; 27 | } 28 | 29 | public Integer getCode() { 30 | return code; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | logs/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | 5 | ### STS ### 6 | .apt_generated 7 | .classpath 8 | .factorypath 9 | .project 10 | .settings 11 | .springBeans 12 | .sts4-cache 13 | 14 | ### IntelliJ IDEA ### 15 | .idea 16 | *.iws 17 | *.iml 18 | *.ipr 19 | log/ 20 | 21 | ### NetBeans ### 22 | nbproject/private/ 23 | build/ 24 | nbbuild/ 25 | dist/ 26 | nbdist/ 27 | .nb-gradle/ 28 | 29 | ### Mac 30 | .DS_Store 31 | */.DS_Store 32 | 33 | ### VS Code ### 34 | *.project 35 | *.factorypath 36 | 37 | ### 屏蔽,需要完整代码连续博主 38 | *.html 39 | /templates 40 | /static 41 | *.sql 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/main/resources/static/front/scss/_pageContent.scss: -------------------------------------------------------------------------------- 1 | #tt-pageContent{ 2 | &:not([class^="tt-offset"]){ 3 | padding-top:100px; 4 | } 5 | &.tt-offset-small{ 6 | padding-top: 70px; 7 | } 8 | @media (max-width: 575px){ 9 | .container[class^="tt-custom-"]{ 10 | padding-left: 0; 11 | padding-right: 0; 12 | } 13 | } 14 | p{ 15 | margin-bottom: 26px; 16 | } 17 | p:last-child{ 18 | margin-bottom: 0; 19 | } 20 | .container.tt-custom-mobile-indent{ 21 | padding-left: 22px; 22 | padding-right: 22px; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/enums/RoleEnum.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.enums; 2 | 3 | /** 4 | *
 5 |  *     角色类型enum
 6 |  * 
7 | * 8 | */ 9 | public enum RoleEnum { 10 | 11 | /** 12 | * 管理员 13 | */ 14 | ADMIN("admin"), 15 | 16 | /** 17 | * 普通用户 18 | */ 19 | USER("user"), 20 | 21 | /** 22 | * 驾校负责人 23 | */ 24 | DRIVING_SCHOOL_USER("driving_school_user"); 25 | 26 | private String value; 27 | 28 | RoleEnum(String value) { 29 | this.value = value; 30 | } 31 | 32 | public String getValue() { 33 | return value; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/enums/CommonParamsEnum.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.enums; 2 | 3 | /** 4 | * 常用数字 5 | */ 6 | public enum CommonParamsEnum { 7 | 8 | /** 9 | * 数字10 10 | */ 11 | TEN(10), 12 | 13 | /** 14 | * 数字5 15 | */ 16 | FIVE(5), 17 | 18 | /** 19 | * 数字404 20 | */ 21 | NOT_FOUND(404), 22 | 23 | /** 24 | * 数字1024 25 | */ 26 | BYTE(1024); 27 | 28 | private Integer value; 29 | 30 | CommonParamsEnum(Integer value) { 31 | this.value = value; 32 | } 33 | 34 | public Integer getValue() { 35 | return value; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/enums/LogTypeEnum.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.enums; 2 | 3 | /** 4 | * @author example 5 | */ 6 | public enum LogTypeEnum { 7 | 8 | /** 9 | * 操作 10 | */ 11 | OPERATION("operation"), 12 | 13 | /** 14 | * 登录 15 | */ 16 | LOGIN("login"), 17 | 18 | /** 19 | * 违规记录 20 | */ 21 | BAN("ban"); 22 | 23 | private String value; 24 | 25 | LogTypeEnum(String value) { 26 | this.value = value; 27 | } 28 | 29 | public String getValue() { 30 | return value; 31 | } 32 | 33 | public void setValue(String value) { 34 | this.value = value; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/entity/Category.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableName; 4 | import com.example.forum.common.base.BaseEntity; 5 | import lombok.Data; 6 | 7 | /** 8 | *
 9 |  *     帖子分类
10 |  * 
11 | * 12 | * @author : saysky 13 | * @date : 2017/11/30 14 | */ 15 | @Data 16 | @TableName("category") 17 | public class Category extends BaseEntity { 18 | 19 | /** 20 | * 分类名称 21 | */ 22 | private String cateName; 23 | 24 | /** 25 | * 分类排序号 26 | */ 27 | private Integer cateSort; 28 | 29 | /** 30 | * 分类描述 31 | */ 32 | private String cateDesc; 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/enums/PostTypeEnum.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.enums; 2 | 3 | /** 4 | *
 5 |  *     文章类型enum
 6 |  * 
7 | * 8 | * @author : saysky 9 | * @date : 2018/7/1 10 | */ 11 | public enum PostTypeEnum { 12 | 13 | /** 14 | * 文章 15 | */ 16 | POST_TYPE_POST("post"), 17 | 18 | /** 19 | * 页面 20 | */ 21 | POST_TYPE_PAGE("page"), 22 | 23 | /** 24 | * 公告 25 | */ 26 | POST_TYPE_NOTICE("notice"); 27 | 28 | private String value; 29 | 30 | PostTypeEnum(String value) { 31 | this.value = value; 32 | } 33 | 34 | public String getValue() { 35 | return value; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/entity/Tag.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableField; 4 | import com.baomidou.mybatisplus.annotation.TableName; 5 | import com.example.forum.common.base.BaseEntity; 6 | import lombok.Data; 7 | 8 | 9 | /** 10 | *
11 |  *     帖子标签
12 |  * 
13 | * 14 | * @author : saysky 15 | * @date : 2018/1/12 16 | */ 17 | @Data 18 | @TableName("tag") 19 | public class Tag extends BaseEntity { 20 | 21 | /** 22 | * 标签名称 23 | */ 24 | private String tagName; 25 | 26 | 27 | /** 28 | * 数量 29 | */ 30 | @TableField(exist = false) 31 | private Integer count; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/entity/Link.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableName; 4 | import com.example.forum.common.base.BaseEntity; 5 | import lombok.Data; 6 | 7 | /** 8 | *
 9 |  *     友情链接
10 |  * 
11 | */ 12 | @Data 13 | @TableName("link") 14 | public class Link extends BaseEntity { 15 | 16 | /** 17 | * 友情链接名称 18 | */ 19 | private String linkName; 20 | 21 | /** 22 | * 友情链接地址 23 | */ 24 | private String linkUrl; 25 | 26 | /** 27 | * 友情链接头像 28 | */ 29 | private String linkPic; 30 | 31 | /** 32 | * 友情链接描述 33 | */ 34 | private String linkDesc; 35 | } 36 | -------------------------------------------------------------------------------- /src/main/resources/mapper/UserRoleRefMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | DELETE 11 | FROM `user_role_ref` 12 | WHERE user_id = #{value} 13 | AND del_flag = 0 14 | 15 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/entity/RolePermissionRef.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableName; 4 | import com.example.forum.common.base.BaseEntity; 5 | import lombok.Data; 6 | 7 | @Data 8 | @TableName("role_permission_ref") 9 | public class RolePermissionRef extends BaseEntity { 10 | 11 | /** 12 | * 角色Id 13 | */ 14 | private Long roleId; 15 | 16 | /** 17 | * 权限Id 18 | */ 19 | private Long permissionId; 20 | 21 | public RolePermissionRef() { 22 | } 23 | 24 | public RolePermissionRef(Long roleId, Long permissionId) { 25 | this.roleId = roleId; 26 | this.permissionId = permissionId; 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/enums/PostStatusEnum.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.enums; 2 | 3 | /** 4 | *
 5 |  *     文章状态enum
 6 |  * 
7 | * 8 | * @author : saysky 9 | * @date : 2018/7/1 10 | */ 11 | public enum PostStatusEnum { 12 | 13 | /** 14 | * 已发布 15 | */ 16 | PUBLISHED(0), 17 | 18 | /** 19 | * 草稿 20 | */ 21 | DRAFT(1), 22 | 23 | /** 24 | * 回收站 25 | */ 26 | RECYCLE(2), 27 | 28 | /** 29 | * 待审核 30 | */ 31 | CHECKING(3); 32 | 33 | private Integer code; 34 | 35 | PostStatusEnum(Integer code) { 36 | this.code = code; 37 | } 38 | 39 | public Integer getCode() { 40 | return code; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/entity/UserRoleRef.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableName; 4 | import com.example.forum.common.base.BaseEntity; 5 | import lombok.Data; 6 | 7 | 8 | /** 9 | * 用户和角色关联 10 | * @author example 11 | */ 12 | @Data 13 | @TableName("user_role_ref") 14 | public class UserRoleRef extends BaseEntity { 15 | 16 | 17 | /** 18 | * 用户Id 19 | */ 20 | private Long userId; 21 | 22 | /** 23 | * 角色Id 24 | */ 25 | private Long roleId; 26 | 27 | public UserRoleRef(Long userId, Long roleId) { 28 | this.userId = userId; 29 | this.roleId = roleId; 30 | } 31 | 32 | public UserRoleRef() { 33 | } 34 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/plugins/fullscreen.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | body.fr-fullscreen { 8 | overflow: hidden; 9 | height: 100%; 10 | width: 100%; 11 | position: fixed; 12 | } 13 | .fr-box.fr-fullscreen { 14 | margin: 0 !important; 15 | position: fixed; 16 | top: 0; 17 | left: 0; 18 | bottom: 0; 19 | right: 0; 20 | z-index: 2147483630 !important; 21 | width: auto !important; 22 | } 23 | .fr-box.fr-fullscreen .fr-toolbar.fr-top { 24 | top: 0 !important; 25 | } 26 | .fr-box.fr-fullscreen .fr-toolbar.fr-bottom { 27 | bottom: 0 !important; 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/util/SensUtils.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.util; 2 | 3 | import io.github.biezhi.ome.OhMyEmail; 4 | import lombok.extern.slf4j.Slf4j; 5 | import java.util.*; 6 | 7 | /** 8 | *
 9 |  *     常用工具
10 |  * 
11 | */ 12 | @Slf4j 13 | public class SensUtils { 14 | 15 | /** 16 | * 配置邮件 17 | * 18 | * @param smtpHost smtpHost 19 | * @param userName 邮件地址 20 | * @param password password 21 | */ 22 | public static void configMail(String smtpHost, String userName, String password) { 23 | Properties properties = OhMyEmail.defaultConfig(false); 24 | properties.setProperty("mail.smtp.host", smtpHost); 25 | OhMyEmail.config(properties, userName, password); 26 | } 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/dto/QueryCondition.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.dto; 2 | 3 | import com.example.forum.vo.SearchVo; 4 | import lombok.Data; 5 | 6 | import java.io.Serializable; 7 | 8 | /** 9 | * 查询封装类 10 | * @author 言曌 11 | * @date 2019-08-16 13:45 12 | */ 13 | @Data 14 | public class QueryCondition implements Serializable { 15 | 16 | /** 17 | * 根据字段筛选 18 | */ 19 | private T data; 20 | 21 | /** 22 | * 一般筛选 23 | */ 24 | private SearchVo searchVo; 25 | 26 | 27 | public QueryCondition() { 28 | } 29 | 30 | public QueryCondition(T data) { 31 | this.data = data; 32 | } 33 | 34 | public QueryCondition(T data, SearchVo searchVo) { 35 | this.data = data; 36 | this.searchVo = searchVo; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/util/RegexUtil.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.util; 2 | 3 | import java.util.regex.Matcher; 4 | import java.util.regex.Pattern; 5 | 6 | /** 7 | * @author 言曌 8 | * @date 2020/3/8 1:55 下午 9 | */ 10 | 11 | public class RegexUtil { 12 | 13 | /** 14 | * 判断Email合法性 15 | * 16 | * @param email 17 | * @return 18 | */ 19 | public static boolean isEmail(String email) { 20 | if (email == null) { 21 | return false; 22 | } 23 | String rule = "[\\w!#$%&'*+/=?^_`{|}~-]+(?:\\.[\\w!#$%&'*+/=?^_`{|}~-]+)*@(?:[\\w](?:[\\w-]*[\\w])?\\.)+[\\w](?:[\\w-]*[\\w])?"; 24 | Pattern pattern = Pattern.compile(rule); 25 | Matcher matcher = pattern.matcher(email); 26 | return matcher.matches(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/service/RolePermissionRefService.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.service; 2 | 3 | import com.example.forum.entity.RolePermissionRef; 4 | 5 | import java.util.List; 6 | 7 | 8 | public interface RolePermissionRefService { 9 | 10 | /** 11 | * 删除某个角色的所有关联 12 | * 13 | * @param roleId 角色Id 14 | */ 15 | void deleteRefByRoleId(Long roleId); 16 | 17 | /** 18 | * 添加角色和权限关联 19 | * 20 | * @param rolePermissionRef RolePermissionRef 21 | * @return UserRoleRef 22 | */ 23 | void saveByRolePermissionRef(RolePermissionRef rolePermissionRef); 24 | 25 | /** 26 | * 批量添加 27 | * 28 | * @param rolePermissionRefs 列表 29 | */ 30 | void batchSaveByRolePermissionRef(List rolePermissionRefs); 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/entity/PostTagRef.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableName; 4 | import com.example.forum.common.base.BaseEntity; 5 | import lombok.Data; 6 | 7 | /** 8 | * 帖子标签关联表 9 | * 10 | * @author 言曌 11 | * @date 2018/12/24 下午4:16 12 | */ 13 | 14 | @Data 15 | @TableName("post_tag_ref") 16 | public class PostTagRef extends BaseEntity { 17 | 18 | private static final long serialVersionUID = 1989776329130364722L; 19 | /** 20 | * 帖子Id 21 | */ 22 | private Long postId; 23 | 24 | /** 25 | * 标签Id 26 | */ 27 | private Long tagId; 28 | 29 | public PostTagRef(Long postId, Long tagId) { 30 | this.postId = postId; 31 | this.tagId = tagId; 32 | } 33 | 34 | public PostTagRef() { 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/entity/PostCategoryRef.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableName; 4 | import com.example.forum.common.base.BaseEntity; 5 | import lombok.Data; 6 | 7 | /** 8 | * 帖子分类关联表 9 | * 10 | * @author 言曌 11 | * @date 2018/12/24 下午4:16 12 | */ 13 | 14 | @Data 15 | @TableName("post_category_ref") 16 | public class PostCategoryRef extends BaseEntity { 17 | 18 | private static final long serialVersionUID = 1989776329130364722L; 19 | /** 20 | * 帖子Id 21 | */ 22 | private Long postId; 23 | 24 | /** 25 | * 分类Id 26 | */ 27 | private Long cateId; 28 | 29 | public PostCategoryRef(Long postId, Long cateId) { 30 | this.postId = postId; 31 | this.cateId = cateId; 32 | } 33 | 34 | public PostCategoryRef() { 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/resources/static/front/scss/_layout404.scss: -------------------------------------------------------------------------------- 1 | /* 2 | Page 404 3 | */ 4 | .tt-layout-404{ 5 | display: flex; 6 | flex-direction: column; 7 | flex-wrap: nowrap; 8 | justify-content: center; 9 | align-content: stretch; 10 | align-items: center; 11 | text-align: center; 12 | letter-spacing: 0.01em; 13 | .tt-title{ 14 | color: $default_color_title; 15 | font-size: 18px; 16 | line-height: 1.3; 17 | font-weight: 500; 18 | margin-top: 64px; 19 | padding-bottom: 8px; 20 | } 21 | @media (min-width: 576px){ 22 | padding: 112px 0 17px 0; 23 | } 24 | @media (max-width: 575px){ 25 | padding: 17px 0 17px 0; 26 | .tt-icon{ 27 | svg{ 28 | width: 200px; 29 | height: 100px; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/bootstrapvalidator/css/bootstrapValidator.css: -------------------------------------------------------------------------------- 1 | /** 2 | * BootstrapValidator (http://bootstrapvalidator.com) 3 | * The best jQuery plugin to validate form fields. Designed to use with Bootstrap 3 4 | * 5 | * @author http://twitter.com/nghuuphuoc 6 | * @copyright (c) 2013 - 2014 Nguyen Huu Phuoc 7 | * @license Commercial: http://bootstrapvalidator.com/license/ 8 | * Non-commercial: http://creativecommons.org/licenses/by-nc-nd/3.0/ 9 | */ 10 | 11 | .bv-form .help-block { 12 | margin-bottom: 0; 13 | } 14 | .bv-form .tooltip-inner { 15 | text-align: left; 16 | } 17 | .nav-tabs li.bv-tab-success > a { 18 | color: #3c763d; 19 | } 20 | .nav-tabs li.bv-tab-error > a { 21 | color: #a94442; 22 | } 23 | 24 | .bv-form .bv-icon-no-label { 25 | top: 0; 26 | } 27 | 28 | .bv-form .bv-icon-input-group { 29 | top: 0; 30 | z-index: 100; 31 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/plugins/line_breaker.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | .clearfix::after { 8 | clear: both; 9 | display: block; 10 | content: ""; 11 | height: 0; 12 | } 13 | .hide-by-clipping { 14 | position: absolute; 15 | width: 1px; 16 | height: 1px; 17 | padding: 0; 18 | margin: -1px; 19 | overflow: hidden; 20 | clip: rect(0, 0, 0, 0); 21 | border: 0; 22 | } 23 | .fr-line-breaker { 24 | cursor: text; 25 | border-top: 1px solid #1e88e5; 26 | position: fixed; 27 | z-index: 2; 28 | display: none; 29 | } 30 | .fr-line-breaker.fr-visible { 31 | display: block; 32 | } 33 | .fr-line-breaker a.fr-floating-btn { 34 | position: absolute; 35 | left: calc(50% - (32px / 2)); 36 | top: -16px; 37 | } 38 | -------------------------------------------------------------------------------- /src/main/resources/static/front/scss/_variables.scss: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Krub:400,500,600'); 2 | 3 | /* Bootstrap variables */ 4 | $font-family-sans-serif: 'Krub', sans-serif; 5 | $default_font: $font-family-sans-serif; 6 | $body-bg: #f8f9fb !default; 7 | $body-color: #666f74 !default; 8 | 9 | /* Colors */ 10 | $default_color: #2172cd; 11 | $default_color2: #182730; 12 | $border: #e2e7ea; 13 | /* Grid */ 14 | $grid-breakpoints: ( 15 | xs: 0, 16 | sm: 576px, 17 | md: 768px, 18 | lg: 992px, 19 | xl: 1230px 20 | ) !default; 21 | $container-max-widths: ( 22 | sm: 540px, 23 | md: 720px, 24 | lg: 960px, 25 | xl: 1200px 26 | ) !default; 27 | $grid-columns: 12 !default; 28 | $grid-gutter-width: 30px !default; 29 | 30 | 31 | /* Custom variables */ 32 | $base-radius: 3px; 33 | $speed: 0.2s; 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/Application.java: -------------------------------------------------------------------------------- 1 | package com.example.forum; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.mybatis.spring.annotation.MapperScan; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.cache.annotation.EnableCaching; 8 | import org.springframework.context.ApplicationContext; 9 | 10 | /** 11 | *
12 |  *     SENS run!
13 |  * 
14 | * 15 | * @author : saysky 16 | * @date : 2017/11/14 17 | */ 18 | @Slf4j 19 | @SpringBootApplication 20 | @EnableCaching 21 | @MapperScan("com.example.forum.mapper*") 22 | public class Application { 23 | public static void main(String[] args) { 24 | ApplicationContext context = SpringApplication.run(Application.class, args); 25 | String serverPort = context.getEnvironment().getProperty("server.port"); 26 | log.info("SENS started at http://localhost:" + serverPort); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/enums/ResourceTypeEnum.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.enums; 2 | 3 | /** 4 | * 资源类型 5 | */ 6 | public enum ResourceTypeEnum { 7 | 8 | /** 9 | * 菜单 10 | */ 11 | MENU("menu", "菜单"), 12 | 13 | /** 14 | * 接口 15 | */ 16 | BUTTON("button", "接口"), 17 | 18 | /** 19 | * 菜单 20 | */ 21 | PAGE("page", "页面"); 22 | 23 | 24 | private String code; 25 | 26 | private String description; 27 | 28 | ResourceTypeEnum(String code, String description) { 29 | this.code = code; 30 | this.description = description; 31 | } 32 | 33 | public String getCode() { 34 | return code; 35 | } 36 | 37 | public void setCode(String code) { 38 | this.code = code; 39 | } 40 | 41 | public String getDescription() { 42 | return description; 43 | } 44 | 45 | public void setDescription(String description) { 46 | this.description = description; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/third_party/image_tui.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | .clearfix::after { 8 | clear: both; 9 | display: block; 10 | content: ""; 11 | height: 0; 12 | } 13 | .hide-by-clipping { 14 | position: absolute; 15 | width: 1px; 16 | height: 1px; 17 | padding: 0; 18 | margin: -1px; 19 | overflow: hidden; 20 | clip: rect(0, 0, 0, 0); 21 | border: 0; 22 | } 23 | .tui-image-editor-container { 24 | position: fixed; 25 | top: 0; 26 | left: 0; 27 | bottom: 0; 28 | right: 0; 29 | height: 100%; 30 | width: 100%; 31 | z-index: 10; 32 | } 33 | .tui-editor-cancel-btn { 34 | background-color: #ffffff; 35 | border: 1px solid #cccccc; 36 | color: #222; 37 | } 38 | .tui-editor-save-btn { 39 | background-color: #fdba3b; 40 | border: 1px solid #fdba3b; 41 | color: #ffffff; 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/mapper/RolePermissionRefMapper.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.example.forum.entity.RolePermissionRef; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | import java.util.List; 8 | 9 | 10 | /** 11 | * @author example 12 | */ 13 | @Mapper 14 | public interface RolePermissionRefMapper extends BaseMapper { 15 | 16 | 17 | /** 18 | * 根据角色Id删除 19 | * 20 | * @param roleId 角色Id 21 | * @return 影响行数 22 | */ 23 | Integer deleteByRoleId(Long roleId); 24 | 25 | /** 26 | * 根据权限Id删除 27 | * 28 | * @param permissionId 权限Id 29 | * @return 影响行数 30 | */ 31 | Integer deleteByPermissionId(Long permissionId); 32 | /** 33 | * 批量添加 34 | * 35 | * @param rolePermissionRefList 列表 36 | * @return 影响喊你高数 37 | */ 38 | Integer batchInsert(List rolePermissionRefList); 39 | } 40 | 41 | -------------------------------------------------------------------------------- /src/main/resources/static/front/scss/_btn-create-topic.scss: -------------------------------------------------------------------------------- 1 | .tt-btn-create-topic{ 2 | display: inline-block; 3 | position: fixed; 4 | z-index: 23; 5 | right: 15px; 6 | @media (max-width: 1282px){ 7 | &.column-open{ 8 | z-index: 13; 9 | } 10 | } 11 | .tt-icon{ 12 | transition: fill $speed linear; 13 | fill:#ffffff; 14 | svg{ 15 | width: 40px; 16 | height: 40px; 17 | 18 | } 19 | &:hover{ 20 | fill:$default_color2; 21 | } 22 | } 23 | @media (min-width: 1283px){ 24 | top: 14px; 25 | } 26 | @media (max-width: 1282px){ 27 | bottom: 20px; 28 | } 29 | &.column-open{ 30 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0.5); 31 | -webkit-transform: rotate(45deg); 32 | -moz-transform: rotate(45deg); 33 | -ms-transform: rotate(45deg); 34 | -o-transform: rotate(45deg); 35 | transform: rotate(45deg); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/resources/mapper/RolePermissionRefMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | DELETE 7 | FROM 8 | role_permission_ref 9 | WHERE 10 | role_id = #{value} 11 | AND del_flag = 0 12 | 13 | 14 | 15 | DELETE 16 | FROM 17 | role_permission_ref 18 | WHERE 19 | permission_id = #{value} 20 | AND del_flag = 0 21 | 22 | 23 | 24 | 25 | 26 | INSERT INTO 27 | role_permission_ref 28 | ( role_id, permission_id ) 29 | VALUES 30 | ( 31 | #{item.roleId}, #{item.permissionId} 32 | ); 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/exception/MyBusinessException.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.exception; 2 | 3 | /** 4 | * @author 言曌 5 | * @date 2019-08-09 16:47 6 | */ 7 | 8 | public class MyBusinessException extends RuntimeException { 9 | 10 | private Integer code; 11 | 12 | private String message; 13 | 14 | 15 | public MyBusinessException() { 16 | super(); 17 | } 18 | 19 | public MyBusinessException(String message) { 20 | this.code = 500; 21 | this.message = message; 22 | } 23 | 24 | public MyBusinessException(Integer code, String message) { 25 | this.code = code; 26 | this.message = message; 27 | } 28 | 29 | public Integer getCode() { 30 | return code; 31 | } 32 | 33 | public void setCode(Integer code) { 34 | this.code = code; 35 | } 36 | 37 | @Override 38 | public String getMessage() { 39 | return message; 40 | } 41 | 42 | public void setMessage(String message) { 43 | this.message = message; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/mapper/CategoryMapper.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 5 | import com.example.forum.entity.Category; 6 | import org.apache.ibatis.annotations.Mapper; 7 | import org.apache.ibatis.annotations.Param; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @author liuyanzhao 13 | */ 14 | @Mapper 15 | public interface CategoryMapper extends BaseMapper { 16 | 17 | /** 18 | * 获得某篇帖子的分类列表 19 | * 20 | * @param postId 帖子Id 21 | * @return List 22 | */ 23 | Category findByPostId(Long postId); 24 | 25 | 26 | /** 27 | * 获得子分类Id列表 28 | * 29 | * @param pathTrace /138/ 这种格式 30 | * @return 子分类Id列表 31 | */ 32 | List selectChildCateIds(@Param("pathTrace") String pathTrace); 33 | 34 | /** 35 | * 根据用户ID删除 36 | * @param userId 37 | * @return 38 | */ 39 | Integer deleteByUserId(Long userId); 40 | } 41 | 42 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/entity/Role.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableField; 4 | import com.baomidou.mybatisplus.annotation.TableName; 5 | import com.example.forum.common.base.BaseEntity; 6 | import lombok.Data; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * @author example 12 | */ 13 | @Data 14 | @TableName("role") 15 | public class Role extends BaseEntity { 16 | 17 | /** 18 | * 角色名称:admin,author,subscriber 19 | */ 20 | private String role; 21 | 22 | /** 23 | * 描述:管理员,作者,订阅者 24 | */ 25 | private String description; 26 | 27 | /** 28 | * 级别 29 | */ 30 | private Integer level; 31 | 32 | /** 33 | * 用户注册默认角色 34 | */ 35 | private Integer isRegisterDefault; 36 | 37 | /** 38 | * 该角色对应的用户数量,非数据库字段 39 | */ 40 | @TableField(exist = false) 41 | private Integer count; 42 | 43 | /** 44 | * 当前角色的权限列表 45 | */ 46 | @TableField(exist = false) 47 | private List permissions; 48 | 49 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/mapper/PostTagRefMapper.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.example.forum.entity.PostTagRef; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @author liuyanzhao 11 | */ 12 | @Mapper 13 | public interface PostTagRefMapper extends BaseMapper { 14 | 15 | /** 16 | * 根据帖子Id删除记录 17 | * 18 | * @param postId 帖子Id 19 | * @return 影响行数 20 | */ 21 | Integer deleteByPostId(Long postId); 22 | 23 | /** 24 | * 根据标签Id删除记录 25 | * 26 | * @param tagId 标签Id 27 | * @return 影响行数 28 | */ 29 | Integer deleteByTagId(Long tagId); 30 | 31 | /** 32 | * 根据标签Id查询帖子Id 33 | * 34 | * @param tagId 标签Id 35 | * @return 帖子Id列表 36 | */ 37 | List selectPostIdByTagId(Long tagId); 38 | 39 | /** 40 | * 根据帖子Id查询标签Id 41 | * @param postId 帖子Id 42 | * @return 标签Id列表 43 | */ 44 | List selectTagIdByPostId(Long postId); 45 | } 46 | 47 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/plugins/draggable.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | .clearfix::after { 8 | clear: both; 9 | display: block; 10 | content: ""; 11 | height: 0; 12 | } 13 | .hide-by-clipping { 14 | position: absolute; 15 | width: 1px; 16 | height: 1px; 17 | padding: 0; 18 | margin: -1px; 19 | overflow: hidden; 20 | clip: rect(0, 0, 0, 0); 21 | border: 0; 22 | } 23 | .fr-drag-helper { 24 | background: #1e88e5; 25 | height: 2px; 26 | margin-top: -1px; 27 | -webkit-opacity: 0.2; 28 | -moz-opacity: 0.2; 29 | opacity: 0.2; 30 | -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; 31 | position: absolute; 32 | z-index: 2147483640; 33 | display: none; 34 | } 35 | .fr-drag-helper.fr-visible { 36 | display: block; 37 | } 38 | .fr-dragging { 39 | -webkit-opacity: 0.4; 40 | -moz-opacity: 0.4; 41 | opacity: 0.4; 42 | -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; 43 | } 44 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/plugins/emoticons.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | .clearfix::after { 8 | clear: both; 9 | display: block; 10 | content: ""; 11 | height: 0; 12 | } 13 | .hide-by-clipping { 14 | position: absolute; 15 | width: 1px; 16 | height: 1px; 17 | padding: 0; 18 | margin: -1px; 19 | overflow: hidden; 20 | clip: rect(0, 0, 0, 0); 21 | border: 0; 22 | } 23 | .fr-popup .fr-emoticon { 24 | display: inline-block; 25 | font-size: 20px; 26 | width: 20px; 27 | padding: 5px; 28 | line-height: 1; 29 | cursor: default; 30 | font-weight: normal; 31 | font-family: "Apple Color Emoji", "Segoe UI Emoji", "NotoColorEmoji", "Segoe UI Symbol", "Android Emoji", "EmojiSymbols"; 32 | -webkit-box-sizing: content-box; 33 | -moz-box-sizing: content-box; 34 | box-sizing: content-box; 35 | } 36 | .fr-popup .fr-emoticon img { 37 | height: 20px; 38 | } 39 | .fr-popup .fr-link:focus { 40 | outline: 0; 41 | background: #ebebeb; 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/service/impl/RolePermissionRefServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.service.impl; 2 | 3 | import com.example.forum.entity.RolePermissionRef; 4 | import com.example.forum.mapper.RolePermissionRefMapper; 5 | import com.example.forum.service.RolePermissionRefService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Service; 8 | 9 | import java.util.List; 10 | 11 | @Service 12 | public class RolePermissionRefServiceImpl implements RolePermissionRefService { 13 | 14 | @Autowired 15 | private RolePermissionRefMapper rolePermissionRefMapper; 16 | 17 | @Override 18 | public void deleteRefByRoleId(Long roleId) { 19 | rolePermissionRefMapper.deleteByRoleId(roleId); 20 | } 21 | 22 | @Override 23 | public void saveByRolePermissionRef(RolePermissionRef rolePermissionRef) { 24 | rolePermissionRefMapper.insert(rolePermissionRef); 25 | } 26 | 27 | @Override 28 | public void batchSaveByRolePermissionRef(List rolePermissionRefs) { 29 | rolePermissionRefMapper.batchInsert(rolePermissionRefs); 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/entity/Permission.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableField; 4 | import com.baomidou.mybatisplus.annotation.TableName; 5 | import com.example.forum.common.base.BaseEntity; 6 | import lombok.Data; 7 | 8 | import java.util.List; 9 | 10 | 11 | /** 12 | * 13 | * 权限,后台的菜单 14 | * @author example 15 | */ 16 | @Data 17 | @TableName("permission") 18 | public class Permission extends BaseEntity { 19 | 20 | /** 21 | * 权限名称 22 | */ 23 | private String name; 24 | 25 | /** 26 | * pid 27 | */ 28 | private Long pid; 29 | 30 | /** 31 | * 资源类型 32 | */ 33 | private String resourceType; 34 | 35 | /** 36 | * 请求URL 37 | */ 38 | private String url; 39 | 40 | /** 41 | * 图标 42 | */ 43 | private String icon; 44 | 45 | /** 46 | * 序号(越小越靠前) 47 | */ 48 | private Double sort; 49 | 50 | /** 51 | * 级别 52 | */ 53 | @TableField(exist = false) 54 | private Integer level; 55 | 56 | /** 57 | * 子权限列表 58 | */ 59 | @TableField(exist = false) 60 | private List childPermissions; 61 | 62 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/service/PostService.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.service; 2 | 3 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 4 | import com.example.forum.common.base.BaseService; 5 | import com.example.forum.dto.PostQueryCondition; 6 | import com.example.forum.entity.Post; 7 | 8 | /** 9 | *
10 |  *     记录/页面业务逻辑接口
11 |  * 
12 | */ 13 | public interface PostService extends BaseService { 14 | 15 | /** 16 | * 修改记录阅读量 17 | * 18 | * @param postId 记录Id 19 | * @return 记录访问量 20 | */ 21 | void updatePostView(Long postId); 22 | 23 | /** 24 | * 获取所有记录的阅读量 25 | * 26 | * @return Long 27 | */ 28 | Long getTotalPostViews(); 29 | 30 | /** 31 | * 更新记录回帖数 32 | * 33 | * @param postId 记录Id 34 | */ 35 | void resetCommentSize(Long postId); 36 | 37 | /** 38 | * 删除用户的记录 39 | * 40 | * @param userId 用户Id 41 | */ 42 | void deleteByUserId(Long userId); 43 | 44 | /** 45 | * 根据条件获得列表 46 | * @param condition 47 | * @return 48 | */ 49 | Page findPostByCondition(PostQueryCondition condition, Page page); 50 | 51 | 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/common/base/BaseEntity.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.common.base; 2 | 3 | import com.baomidou.mybatisplus.annotation.IdType; 4 | import com.baomidou.mybatisplus.annotation.TableField; 5 | import com.baomidou.mybatisplus.annotation.TableId; 6 | import com.baomidou.mybatisplus.annotation.TableLogic; 7 | import com.example.forum.common.constant.CommonConstant; 8 | import lombok.Data; 9 | 10 | import java.io.Serializable; 11 | import java.util.Date; 12 | 13 | /** 14 | * @author 言曌 15 | * @date 2019-08-07 00:28 16 | */ 17 | @Data 18 | public class BaseEntity implements Serializable { 19 | 20 | /** 21 | * ID,自动生成 22 | */ 23 | @TableId(type = IdType.AUTO) 24 | private Long id; 25 | 26 | /** 27 | * 删除状态:1删除,0未删除 28 | */ 29 | @TableField(value = "del_flag") 30 | @TableLogic 31 | private Integer delFlag = CommonConstant.STATUS_NORMAL; 32 | 33 | /** 34 | * 创建人用户名 35 | */ 36 | private String createBy; 37 | 38 | /** 39 | * 创建时间 40 | */ 41 | private Date createTime; 42 | 43 | /** 44 | * 更新人 45 | */ 46 | private String updateBy; 47 | 48 | /** 49 | * 更新时间 50 | */ 51 | private Date updateTime; 52 | } 53 | -------------------------------------------------------------------------------- /src/main/resources/mapper/CategoryMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | DELETE 7 | FROM 8 | category 9 | WHERE 10 | user_id = #{value} 11 | 12 | 13 | 30 | 31 | 40 | 41 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/vo/PageVo.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.vo; 2 | 3 | import lombok.Data; 4 | 5 | import java.io.Serializable; 6 | 7 | 8 | /** 9 | * @author example 10 | */ 11 | @Data 12 | public class PageVo implements Serializable { 13 | 14 | private static final long serialVersionUID = 1L; 15 | 16 | /** 17 | * 页号 18 | */ 19 | private long page = 1; 20 | 21 | /** 22 | * 页大小 23 | */ 24 | private long size = 10; 25 | 26 | /** 27 | * 排序字段 28 | */ 29 | private String sort = "create_time"; 30 | 31 | /** 32 | * 排序方式 asc/desc 33 | */ 34 | private String order = "desc"; 35 | 36 | /** 37 | * 当前页码 38 | */ 39 | private long current; 40 | 41 | /** 42 | * 总数 43 | */ 44 | private long total; 45 | 46 | /** 47 | * 页数 48 | */ 49 | private long pages; 50 | 51 | 52 | public PageVo() { 53 | } 54 | 55 | public PageVo(int page, int size) { 56 | this.page = page; 57 | this.size = size; 58 | } 59 | 60 | public PageVo(int page, int size, String sort, String order) { 61 | this.page = page; 62 | this.size = size; 63 | this.sort = sort; 64 | this.order = order; 65 | } 66 | 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/mapper/PostCategoryRefMapper.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.example.forum.entity.PostCategoryRef; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | import java.util.List; 8 | 9 | 10 | /** 11 | * @author liuyanzhao 12 | */ 13 | @Mapper 14 | public interface PostCategoryRefMapper extends BaseMapper { 15 | 16 | /** 17 | * 根据帖子Id删除记录 18 | * 19 | * @param postId 帖子Id 20 | * @return 影响行数 21 | */ 22 | Integer deleteByPostId(Long postId); 23 | 24 | /** 25 | * 根据分类Id删除记录 26 | * 27 | * @param cateId 分类Id 28 | * @return 影响行数 29 | */ 30 | Integer deleteByCateId(Long cateId); 31 | 32 | /** 33 | * 根据分类Id查询帖子Id 34 | * 35 | * @param cateId 分类Id 36 | * @return 帖子Id列表 37 | */ 38 | List selectPostIdByCateId(Long cateId); 39 | 40 | /** 41 | * 根据帖子Id查询分类Id 42 | * 43 | * @param postId 帖子Id 44 | * @return 分类Id列表 45 | */ 46 | List selectCateIdByPostId(Long postId); 47 | 48 | /** 49 | * 统计某篇分类的帖子数 50 | * 51 | * @param cateId 分类Id 52 | * @return 帖子Id列表 53 | */ 54 | Integer countPostByCateId(Long cateId); 55 | } 56 | 57 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/util/ThreadPoolUtil.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.util; 2 | 3 | import java.util.concurrent.ArrayBlockingQueue; 4 | import java.util.concurrent.BlockingQueue; 5 | import java.util.concurrent.ThreadPoolExecutor; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | /** 9 | * @author example 10 | */ 11 | public class ThreadPoolUtil { 12 | 13 | /** 14 | * 线程缓冲队列 15 | */ 16 | private static BlockingQueue bqueue = new ArrayBlockingQueue(100); 17 | /** 18 | * 核心线程数,会一直存活,即使没有任务,线程池也会维护线程的最少数量 19 | */ 20 | private static final int SIZE_CORE_POOL = 5; 21 | /** 22 | * 线程池维护线程的最大数量 23 | */ 24 | private static final int SIZE_MAX_POOL = 10; 25 | /** 26 | * 线程池维护线程所允许的空闲时间 27 | */ 28 | private static final long ALIVE_TIME = 2000; 29 | 30 | private static ThreadPoolExecutor pool = new ThreadPoolExecutor(SIZE_CORE_POOL, SIZE_MAX_POOL, ALIVE_TIME, TimeUnit.MILLISECONDS, bqueue, new ThreadPoolExecutor.CallerRunsPolicy()); 31 | 32 | static { 33 | 34 | pool.prestartAllCoreThreads(); 35 | } 36 | 37 | public static ThreadPoolExecutor getPool() { 38 | return pool; 39 | } 40 | 41 | public static void main(String[] args) { 42 | System.out.println(pool.getPoolSize()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/util/SpringUtils.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.util; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.ApplicationContextAware; 6 | import org.springframework.stereotype.Component; 7 | 8 | 9 | @Component 10 | public class SpringUtils implements ApplicationContextAware { 11 | private static ApplicationContext applicationContext; 12 | 13 | @Override 14 | public void setApplicationContext(ApplicationContext applicationContext) 15 | throws BeansException { 16 | if (SpringUtils.applicationContext == null) { 17 | SpringUtils.applicationContext = applicationContext; 18 | } 19 | 20 | } 21 | 22 | public static ApplicationContext getApplicationContext() { 23 | return applicationContext; 24 | } 25 | 26 | //根据name 27 | public static Object getBean(String name) { 28 | return getApplicationContext().getBean(name); 29 | } 30 | 31 | //根据类型 32 | public static T getBean(Class clazz) { 33 | return getApplicationContext().getBean(clazz); 34 | } 35 | 36 | public static T getBean(String name, Class clazz) { 37 | return getApplicationContext().getBean(name, clazz); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/mapper/TagMapper.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 5 | import com.example.forum.entity.Tag; 6 | import org.apache.ibatis.annotations.Mapper; 7 | import org.apache.ibatis.annotations.Param; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @author liuyanzhao 13 | */ 14 | @Mapper 15 | public interface TagMapper extends BaseMapper { 16 | 17 | /** 18 | * 获得某篇帖子的标签列表 19 | * 20 | * @param postId 帖子Id 21 | * @return List 22 | */ 23 | List findByPostId(Long postId); 24 | 25 | /** 26 | * 获得所有包括统计帖子数 27 | * 28 | * @return 标签列表 29 | */ 30 | List findAllWithCount(Integer limit); 31 | 32 | /** 33 | * 查询没有用过的标签 34 | * 35 | * @return 标签列表 36 | */ 37 | List findTagNotUse(); 38 | 39 | /** 40 | * 根据用户ID删除 41 | * @param userId 42 | * @return 43 | */ 44 | Integer deleteByUserId(Long userId); 45 | 46 | /** 47 | * 热门标签 48 | * @param keywords 49 | * @param limit 50 | * @return 51 | */ 52 | List getHotTags(@Param("keywords") String keywords, 53 | @Param("limit") Integer limit); 54 | 55 | } 56 | 57 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/service/CategoryService.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.service; 2 | 3 | 4 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 5 | import com.example.forum.common.base.BaseService; 6 | import com.example.forum.entity.Category; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | *
12 |  *     分类业务逻辑接口
13 |  * 
14 | * 15 | * @author : saysky 16 | * @date : 2017/11/30 17 | */ 18 | public interface CategoryService extends BaseService { 19 | 20 | /** 21 | * 查询所有分类目录,带count和根据level封装name 22 | * 23 | * @return 返回List集合 24 | */ 25 | List findByUserId(Long userId); 26 | 27 | /** 28 | * 根据文章Id获得分类列表 29 | * 30 | * @param postId 文章id 31 | * @return 分类列表 32 | */ 33 | Category findByPostId(Long postId); 34 | 35 | /** 36 | * 获得某个分类的所有文章数 37 | * 38 | * @param cateId 分类Id 39 | * @return 文章数 40 | */ 41 | Integer countPostByCateId(Long cateId); 42 | 43 | /** 44 | * 根据用户ID删除 45 | * 46 | * @param userId 47 | * @return 48 | */ 49 | Integer deleteByUserId(Long userId); 50 | 51 | /** 52 | * 将分类ID列表转成分类 53 | * 54 | * @param cateIds 55 | * @param userId 56 | * @return 57 | */ 58 | List cateIdsToCateList(List cateIds, Long userId); 59 | } 60 | -------------------------------------------------------------------------------- /src/main/resources/mapper/PostTagRefMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | DELETE 11 | FROM 12 | post_tag_ref 13 | WHERE 14 | post_id = #{value} 15 | AND del_flag = 0 16 | 17 | 18 | 19 | DELETE 20 | FROM 21 | post_tag_ref 22 | WHERE 23 | tag_id = #{value} 24 | AND del_flag = 0 25 | 26 | 27 | 36 | 37 | 46 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/service/CommentService.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.service; 2 | 3 | 4 | import com.example.forum.common.base.BaseService; 5 | import com.example.forum.entity.Comment; 6 | import org.apache.ibatis.annotations.Param; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | *
12 |  *     回帖业务逻辑接口
13 |  * 
14 | * 15 | * @author : saysky 16 | * @date : 2018/1/22 17 | */ 18 | public interface CommentService extends BaseService { 19 | 20 | /** 21 | * 根据用户Id删除回帖 22 | * 23 | * @param userId 用户Id 24 | */ 25 | Integer deleteByUserId(Long userId); 26 | 27 | /** 28 | * 根据回帖接受人Id删除回帖 29 | * 30 | * @param acceptId 用户Id 31 | */ 32 | Integer deleteByAcceptUserId(Long acceptId); 33 | 34 | /** 35 | * 根据文章ID获得评论列表 36 | * @param postId 37 | * @return 38 | */ 39 | List findByPostId(Long postId); 40 | 41 | /** 42 | * 根据用户ID,标记所有收到的回帖为已读 43 | * @param userId 44 | */ 45 | void readAllByUserId(Long userId); 46 | 47 | /** 48 | * 统计某个用户未读数量 49 | * @return 50 | */ 51 | Integer countNotReadByUserId(Long userId); 52 | 53 | /** 54 | * 更新某个用户在某篇文章下的收到的评论为已读 55 | * @param postId 56 | * @param userId 57 | */ 58 | void updateIsReadByPostIdAndUserId(@Param("postId") Long postId, @Param("userId") Long userId); 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/mapper/RoleMapper.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.example.forum.entity.Role; 5 | import org.apache.ibatis.annotations.Mapper; 6 | 7 | import java.util.List; 8 | 9 | 10 | /** 11 | * @author example 12 | */ 13 | @Mapper 14 | public interface RoleMapper extends BaseMapper { 15 | 16 | 17 | /** 18 | * 根据用户Id获得角色 19 | * 20 | * @param userId 用户Id 21 | * @return 角色列表 22 | */ 23 | Role findByUserId(Long userId); 24 | 25 | 26 | /** 27 | * 删除用户和角色管理 28 | * 29 | * @param userId 用户ID 30 | * @return 影响行数 31 | */ 32 | Integer deleteByUserId(Long userId); 33 | 34 | /** 35 | * 统计某个角色的用户数 36 | * 37 | * @param roleId 角色Id 38 | * @return 用户数 39 | */ 40 | Integer countUserByRoleId(Long roleId); 41 | 42 | 43 | /** 44 | * 获得所有角色和对应用户数量 45 | * @return 46 | */ 47 | List findAllWithCount(); 48 | 49 | /** 50 | * 查询小于等于该等级的角色 51 | * @param level 52 | * @return 53 | */ 54 | List findByLessThanLevel(Integer level); 55 | 56 | /** 57 | * 查询某个用户最大的角色等级 58 | * @param userId 59 | * @return 60 | */ 61 | Integer findMaxLevelByUserId(Long userId); 62 | 63 | /** 64 | * 获得用户注册默认角色 65 | * @return 66 | */ 67 | Role findDefaultRole(); 68 | } 69 | 70 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/service/TagService.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.service; 2 | 3 | import com.example.forum.common.base.BaseService; 4 | import com.example.forum.entity.Tag; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | *
10 |  *     标签业务逻辑接口
11 |  * 
12 | * 13 | */ 14 | public interface TagService extends BaseService { 15 | 16 | 17 | /** 18 | * 热门标签 19 | * 20 | * @return 标签列表 21 | */ 22 | List findHotTags(Integer limit); 23 | 24 | /** 25 | * 根据标签名称查询 26 | * 27 | * @param tagName tagName 28 | * @return Tag 29 | */ 30 | Tag findTagByTagName(String tagName); 31 | 32 | /** 33 | * 转换标签字符串为实体集合 34 | * 35 | * @param tagList tagList 36 | * @return List 37 | */ 38 | List strListToTagList(String tagList); 39 | 40 | /** 41 | * 标签列表转成字符串 42 | * @param tagList 43 | * @return 44 | */ 45 | String tagListToStr(List tagList); 46 | /** 47 | * 根据文章Id获得标签列表 48 | * 49 | * @param postId 文章id 50 | * @return 分类列表 51 | */ 52 | List findByPostId(Long postId); 53 | 54 | /** 55 | * 根据用户ID删除 56 | * @param userId 57 | * @return 58 | */ 59 | Integer deleteByUserId(Long userId); 60 | 61 | /** 62 | * 热门标签 63 | * @param keywords 64 | * @return 65 | */ 66 | List getHotTags(String keywords, Integer limit); 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/service/impl/MailServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.service.impl; 2 | 3 | import com.example.forum.service.MailService; 4 | import com.example.forum.util.SensUtils; 5 | import io.github.biezhi.ome.OhMyEmail; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.stereotype.Service; 8 | 9 | import javax.mail.MessagingException; 10 | 11 | /** 12 | *
13 |  *     邮件发送业务逻辑实现类
14 |  * 
15 | * 16 | * @author : saysky 17 | * @date : 2018/1/23 18 | */ 19 | @Service 20 | public class MailServiceImpl implements MailService { 21 | 22 | @Value("${mail.smtp.host}") 23 | private String host; 24 | 25 | @Value("${mail.smtp.username}") 26 | private String username; 27 | 28 | @Value("${mail.smtp.password}") 29 | private String password; 30 | 31 | @Value("${mail.from.name}") 32 | private String fromName; 33 | 34 | /** 35 | * 发送邮件 36 | * 37 | * @param to to 接收者 38 | * @param title subject 标题 39 | * @param content content 内容 40 | */ 41 | @Override 42 | public void sendMail(String to, String title, String content) throws MessagingException { 43 | //配置邮件服务器 44 | SensUtils.configMail(host, username, password); 45 | OhMyEmail.subject(title) 46 | .from(fromName) 47 | .to(to) 48 | .text(content) 49 | .send(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/resources/static/images/tail-spin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 20 | 21 | 22 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/main/resources/mapper/PostCategoryRefMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | DELETE 8 | FROM 9 | post_category_ref 10 | WHERE 11 | post_id = #{value} 12 | AND del_flag = 0 13 | 14 | 15 | 16 | DELETE 17 | FROM 18 | post_category_ref 19 | WHERE 20 | cate_id = #{value} 21 | AND del_flag = 0 22 | 23 | 24 | 33 | 34 | 43 | 44 | 53 | 54 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/util/SpringUtil.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.util; 2 | 3 | import org.springframework.beans.BeansException; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.ApplicationContextAware; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public class SpringUtil implements ApplicationContextAware { 10 | 11 | private static ApplicationContext applicationContext; 12 | 13 | /** 14 | * 获取applicationContext 15 | * 16 | * @return ApplicationContext 17 | */ 18 | public static ApplicationContext getApplicationContext() { 19 | return applicationContext; 20 | } 21 | 22 | @Override 23 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 24 | if (SpringUtil.applicationContext == null) { 25 | SpringUtil.applicationContext = applicationContext; 26 | } 27 | } 28 | 29 | /** 30 | * 通过name获取 Bean. 31 | * 32 | * @param name name 33 | * @return Object 34 | */ 35 | public static Object getBean(String name) { 36 | return getApplicationContext().getBean(name); 37 | } 38 | 39 | /** 40 | * 通过class获取Bean 41 | * 42 | * @param clazz clazz 43 | * @param 44 | * @return T 45 | */ 46 | public static T getBean(Class clazz) { 47 | return getApplicationContext().getBean(clazz); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/third_party/font_awesome.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | .clearfix::after { 8 | clear: both; 9 | display: block; 10 | content: ""; 11 | height: 0; 12 | } 13 | .hide-by-clipping { 14 | position: absolute; 15 | width: 1px; 16 | height: 1px; 17 | padding: 0; 18 | margin: -1px; 19 | overflow: hidden; 20 | clip: rect(0, 0, 0, 0); 21 | border: 0; 22 | } 23 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-font-awesome-modal { 24 | text-align: left; 25 | padding: 20px 20px 10px; 26 | } 27 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-font-awesome-modal .fr-font-awesome-list { 28 | margin-bottom: 20px; 29 | } 30 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-font-awesome-modal .fr-font-awesome-title { 31 | font-size: 20px; 32 | padding: 6px 0 4px; 33 | margin: 15px 0 5px; 34 | border-bottom: solid 1px #f0f0f0; 35 | } 36 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-font-awesome-modal .fr-font-awesome { 37 | display: inline-block; 38 | font-size: 16px; 39 | width: 20px; 40 | height: 20px; 41 | padding: 16px; 42 | line-height: 20px; 43 | cursor: default; 44 | font-weight: normal; 45 | -webkit-box-sizing: content-box; 46 | -moz-box-sizing: content-box; 47 | box-sizing: content-box; 48 | text-align: center; 49 | margin: -1px 0 0 -1px; 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/service/PermissionService.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.service; 2 | 3 | import com.example.forum.entity.Permission; 4 | import com.example.forum.common.base.BaseService; 5 | 6 | import java.util.List; 7 | import java.util.Set; 8 | 9 | /** 10 | * 权限逻辑接口 11 | */ 12 | public interface PermissionService extends BaseService { 13 | 14 | /** 15 | * 根据角色Id获得权限列表 16 | * 17 | * @param roleId 角色Id 18 | * @return 权限列表 19 | */ 20 | List listPermissionsByRoleId(Long roleId); 21 | 22 | /** 23 | * 获得某个用户的权限URL列表 24 | * 25 | * @param userId 26 | * @return 27 | */ 28 | Set findPermissionUrlsByUserId(Long userId); 29 | 30 | /** 31 | * 获得某个用户的用户ID和资源类型 32 | * 33 | * @param userId 34 | * @param resourceType 35 | * @return 36 | */ 37 | List findPermissionTreeByUserIdAndResourceType(Long userId, String resourceType); 38 | 39 | /** 40 | * 根据角色ID获得权限列表 41 | * @param roleId 42 | * @return 43 | */ 44 | List findPermissionByRoleId(Long roleId); 45 | 46 | /** 47 | * 获得所有权限,带有等级 48 | * @return 49 | */ 50 | List findPermissionListWithLevel(); 51 | 52 | /** 53 | * 统计子节点数量 54 | * @param id 55 | * @return 56 | */ 57 | Integer countChildPermission(Long id); 58 | 59 | /** 60 | * 根据URL获得权限 61 | * @param url 62 | * @return 63 | */ 64 | Permission findByUrl(String url); 65 | } 66 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/plugins/special_characters.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | .clearfix::after { 8 | clear: both; 9 | display: block; 10 | content: ""; 11 | height: 0; 12 | } 13 | .hide-by-clipping { 14 | position: absolute; 15 | width: 1px; 16 | height: 1px; 17 | padding: 0; 18 | margin: -1px; 19 | overflow: hidden; 20 | clip: rect(0, 0, 0, 0); 21 | border: 0; 22 | } 23 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-special-characters-modal { 24 | text-align: left; 25 | padding: 20px 20px 10px; 26 | } 27 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-special-characters-modal .fr-special-characters-list { 28 | margin-bottom: 20px; 29 | } 30 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-special-characters-modal .fr-special-characters-title { 31 | font-weight: bold; 32 | font-size: 14px; 33 | padding: 6px 0 4px; 34 | margin: 0 0 5px; 35 | } 36 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-special-characters-modal .fr-special-character { 37 | display: inline-block; 38 | font-size: 16px; 39 | width: 20px; 40 | height: 20px; 41 | padding: 5px; 42 | line-height: 20px; 43 | cursor: default; 44 | font-weight: normal; 45 | -webkit-box-sizing: content-box; 46 | -moz-box-sizing: content-box; 47 | box-sizing: content-box; 48 | text-align: center; 49 | border: 1px solid #cccccc; 50 | margin: -1px 0 0 -1px; 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/service/impl/UserRoleRefServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.service.impl; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 5 | import com.example.forum.entity.UserRoleRef; 6 | import com.example.forum.mapper.UserRoleRefMapper; 7 | import com.example.forum.service.UserRoleRefService; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Service; 10 | 11 | 12 | @Service 13 | public class UserRoleRefServiceImpl implements UserRoleRefService { 14 | 15 | @Autowired 16 | private UserRoleRefMapper roleRefMapper; 17 | 18 | 19 | @Override 20 | public void deleteByUserId(Long userId) { 21 | roleRefMapper.deleteByUserId(userId); 22 | } 23 | 24 | @Override 25 | public BaseMapper getRepository() { 26 | return roleRefMapper; 27 | } 28 | 29 | @Override 30 | public QueryWrapper getQueryWrapper(UserRoleRef userRoleRef) { 31 | //对指定字段查询 32 | QueryWrapper queryWrapper = new QueryWrapper<>(); 33 | if (userRoleRef != null) { 34 | if (userRoleRef.getUserId() != null) { 35 | queryWrapper.eq("user_id", userRoleRef.getUserId()); 36 | } 37 | if (userRoleRef.getRoleId() != null) { 38 | queryWrapper.eq("role_id", userRoleRef.getRoleId()); 39 | } 40 | } 41 | return queryWrapper; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/plugins/char_counter.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | .clearfix::after { 8 | clear: both; 9 | display: block; 10 | content: ""; 11 | height: 0; 12 | } 13 | .hide-by-clipping { 14 | position: absolute; 15 | width: 1px; 16 | height: 1px; 17 | padding: 0; 18 | margin: -1px; 19 | overflow: hidden; 20 | clip: rect(0, 0, 0, 0); 21 | border: 0; 22 | } 23 | .fr-box .fr-counter { 24 | position: absolute; 25 | bottom: 0px; 26 | padding: 5px; 27 | right: 0px; 28 | color: #cccccc; 29 | content: attr(data-chars); 30 | font-size: 15px; 31 | font-family: "Times New Roman", Georgia, Serif; 32 | z-index: 1; 33 | background: #ffffff; 34 | border-top: solid 1px #ebebeb; 35 | border-left: solid 1px #ebebeb; 36 | border-radius: 2px 0 0 0; 37 | -moz-border-radius: 2px 0 0 0; 38 | -webkit-border-radius: 2px 0 0 0; 39 | -moz-background-clip: padding; 40 | -webkit-background-clip: padding-box; 41 | background-clip: padding-box; 42 | } 43 | .fr-box.fr-rtl .fr-counter { 44 | left: 0px; 45 | right: auto; 46 | border-left: none; 47 | border-right: solid 1px #ebebeb; 48 | border-radius: 0 2px 0 0; 49 | -moz-border-radius: 0 2px 0 0; 50 | -webkit-border-radius: 0 2px 0 0; 51 | -moz-background-clip: padding; 52 | -webkit-background-clip: padding-box; 53 | background-clip: padding-box; 54 | } 55 | .fr-box.fr-code-view .fr-counter { 56 | display: none; 57 | } 58 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/third_party/embedly.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | .clearfix::after { 8 | clear: both; 9 | display: block; 10 | content: ""; 11 | height: 0; 12 | } 13 | .hide-by-clipping { 14 | position: absolute; 15 | width: 1px; 16 | height: 1px; 17 | padding: 0; 18 | margin: -1px; 19 | overflow: hidden; 20 | clip: rect(0, 0, 0, 0); 21 | border: 0; 22 | } 23 | .fr-element .fr-embedly { 24 | user-select: none; 25 | -o-user-select: none; 26 | -moz-user-select: none; 27 | -khtml-user-select: none; 28 | -webkit-user-select: none; 29 | -ms-user-select: none; 30 | position: relative; 31 | } 32 | .fr-element .fr-embedly::after { 33 | position: absolute; 34 | content: ''; 35 | z-index: 1; 36 | top: 0; 37 | left: 0; 38 | right: 0; 39 | bottom: 0; 40 | cursor: pointer; 41 | display: block; 42 | background: rgba(0, 0, 0, 0); 43 | } 44 | .fr-element .fr-embedly > * { 45 | -webkit-box-sizing: content-box; 46 | -moz-box-sizing: content-box; 47 | box-sizing: content-box; 48 | max-width: 100%; 49 | border: none; 50 | } 51 | .fr-box .fr-embedly-resizer { 52 | position: absolute; 53 | border: solid 1px #1e88e5; 54 | display: none; 55 | user-select: none; 56 | -o-user-select: none; 57 | -moz-user-select: none; 58 | -khtml-user-select: none; 59 | -webkit-user-select: none; 60 | -ms-user-select: none; 61 | } 62 | .fr-box .fr-embedly-resizer.fr-active { 63 | display: block; 64 | } 65 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/plugins/help.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | .clearfix::after { 8 | clear: both; 9 | display: block; 10 | content: ""; 11 | height: 0; 12 | } 13 | .hide-by-clipping { 14 | position: absolute; 15 | width: 1px; 16 | height: 1px; 17 | padding: 0; 18 | margin: -1px; 19 | overflow: hidden; 20 | clip: rect(0, 0, 0, 0); 21 | border: 0; 22 | } 23 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal { 24 | text-align: left; 25 | padding: 20px 20px 10px; 26 | } 27 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal table { 28 | border-collapse: collapse; 29 | font-size: 14px; 30 | line-height: 1.5; 31 | width: 100%; 32 | } 33 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal table + table { 34 | margin-top: 20px; 35 | } 36 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal table tr { 37 | border: 0; 38 | } 39 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal table th, 40 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal table td { 41 | padding: 6px 0 4px; 42 | } 43 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal table tbody tr { 44 | border-bottom: solid 1px #ebebeb; 45 | } 46 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal table tbody td:first-child { 47 | width: 60%; 48 | color: #646464; 49 | } 50 | .fr-modal .fr-modal-wrapper .fr-modal-body .fr-help-modal table tbody td:nth-child(n+2) { 51 | letter-spacing: 0.5px; 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/service/RoleService.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.service; 2 | 3 | import com.example.forum.entity.Role; 4 | import com.example.forum.common.base.BaseService; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * 角色逻辑接口 10 | */ 11 | public interface RoleService extends BaseService { 12 | 13 | /** 14 | * 删除某个用户的所有关联 15 | * 16 | * @param userId 用户Id 17 | */ 18 | void deleteByUserId(Long userId); 19 | 20 | /** 21 | * 根据编号查询单个权限 22 | * 23 | * @param roleId roleId 24 | * @return Role 25 | */ 26 | Role findByRoleId(Long roleId); 27 | 28 | /** 29 | * 根据编号查询单个权限 30 | * 31 | * @param roleName roleName 32 | * @return Role 33 | */ 34 | Role findByRoleName(String roleName); 35 | 36 | /** 37 | * 根据用户Id获得角色 38 | * 39 | * @param userId 用户Id 40 | * @return 角色列表 41 | */ 42 | Role findByUserId(Long userId); 43 | 44 | /** 45 | * 统计这个角色的用户数 46 | * 47 | * @param roleId 角色Id 48 | */ 49 | Integer countUserByRoleId(Long roleId); 50 | 51 | /** 52 | * 查询某个用户最大的角色等级 53 | * @param userId 54 | * @return 55 | */ 56 | Integer findMaxLevelByUserId(Long userId); 57 | 58 | /** 59 | * 查询小于等于该等级的角色 60 | * @param level 61 | * @return 62 | */ 63 | List findByLessThanLevel(Integer level); 64 | 65 | /** 66 | * 获得用户注册默认角色 67 | * @return 68 | */ 69 | Role findDefaultRole(); 70 | 71 | /** 72 | * 获得用户注册默认角色 73 | * @return 74 | */ 75 | Role getMaxRoleByUserId(Long userId); 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/mapper/PostMapper.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 5 | import com.example.forum.dto.PostQueryCondition; 6 | import com.example.forum.entity.Post; 7 | import org.apache.ibatis.annotations.Mapper; 8 | import org.apache.ibatis.annotations.Param; 9 | 10 | import java.util.Date; 11 | import java.util.List; 12 | 13 | /** 14 | * @author liuyanzhao 15 | */ 16 | @Mapper 17 | public interface PostMapper extends BaseMapper { 18 | 19 | /** 20 | * 获取所有文章阅读量总和 21 | * 22 | * @return Long 23 | */ 24 | Long getPostViewsSum(); 25 | 26 | /** 27 | * 重置回帖数量 28 | * 29 | * @return 数量 30 | */ 31 | Integer resetCommentSize(Long postId); 32 | 33 | /** 34 | * 根据用户Id删除 35 | * 36 | * @return 影响行数 37 | */ 38 | Integer deleteByUserId(Long userId); 39 | 40 | /** 41 | * 文章点赞量+1 42 | * 43 | * @param postId 44 | * @return 45 | */ 46 | Integer incrPostLikes(Long postId); 47 | 48 | /** 49 | * 文章访问量+1 50 | * 51 | * @param postId 52 | * @return 53 | */ 54 | Integer incrPostViews(Long postId); 55 | 56 | 57 | /** 58 | * 获得今日新增数量 59 | * 60 | * @return 61 | */ 62 | Integer getTodayCount(); 63 | 64 | /** 65 | * 根据标签ID查询文章 66 | * 67 | * @param condition 68 | * @param page 69 | * @return 70 | */ 71 | List findPostByCondition(@Param("condition") PostQueryCondition condition, Page page); 72 | 73 | 74 | } 75 | 76 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.service; 2 | 3 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 4 | import com.example.forum.common.base.BaseService; 5 | import com.example.forum.entity.User; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * 用户业务逻辑接口 11 | */ 12 | public interface UserService extends BaseService { 13 | 14 | /** 15 | * 根据用户名获得用户 16 | * 17 | * @param userName 用户名 18 | * @return 用户 19 | */ 20 | User findByUserName(String userName); 21 | 22 | 23 | /** 24 | * 根据邮箱查找用户 25 | * 26 | * @param userEmail 邮箱 27 | * @return User 28 | */ 29 | User findByEmail(String userEmail); 30 | 31 | /** 32 | * 更新密码 33 | * 34 | * @param userId 用户Id 35 | * @param password 密码 36 | */ 37 | void updatePassword(Long userId, String password); 38 | 39 | /** 40 | * 分页获取所有用户 41 | * 42 | * @param roleName 角色名称 43 | * @param condition 查询条件 44 | * @param page 分页信息 45 | * @return 用户列表 46 | */ 47 | Page findByRoleAndCondition(String roleName, User condition, Page page); 48 | 49 | 50 | /** 51 | * 修改禁用状态 52 | * 53 | * @param enable enable 54 | */ 55 | void updateUserLoginEnable(User user, String enable); 56 | 57 | /** 58 | * 增加登录错误次数 59 | * 60 | * @return 登录错误次数 61 | */ 62 | Integer updateUserLoginError(User user); 63 | 64 | /** 65 | * 修改用户的登录状态为正常 66 | * 67 | * @return User 68 | */ 69 | User updateUserLoginNormal(User user); 70 | 71 | 72 | /** 73 | * 获得热门用户 74 | * @param limit 用户数量 75 | * @return 76 | */ 77 | List getHotUsers(Integer limit); 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/mapper/PermissionMapper.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.example.forum.entity.Permission; 5 | import org.apache.ibatis.annotations.Mapper; 6 | import org.apache.ibatis.annotations.Param; 7 | 8 | import java.util.List; 9 | 10 | 11 | /** 12 | * @author example 13 | */ 14 | @Mapper 15 | public interface PermissionMapper extends BaseMapper { 16 | 17 | /** 18 | * 根据角色Id获得权限列表 19 | * 20 | * @param roleId 角色Id 21 | * @return 权限列表 22 | */ 23 | List findByRoleId(Long roleId); 24 | 25 | /** 26 | * 获得某个用户的权限列表 27 | * 28 | * @param userId 29 | * @return 30 | */ 31 | List findPermissionByUserId(Long userId); 32 | 33 | /** 34 | * 获得某个用户的权限列表 35 | * 36 | * @param userId 37 | * @param resourceType 38 | * @return 39 | */ 40 | List findPermissionByUserIdAndResourceType(@Param("userId") Long userId, 41 | @Param("resourceType") String resourceType); 42 | 43 | 44 | /** 45 | * 获得权限列表 46 | * 47 | * @param resourceType 48 | * @return 49 | */ 50 | List findPermissionByResourceType(Integer resourceType); 51 | 52 | /** 53 | * 根据角色ID获得权限列表 54 | * @param roleId 55 | * @return 56 | */ 57 | List findPermissionByRoleId(Long roleId); 58 | 59 | /** 60 | * 统计子节点数量 61 | * @param id 62 | * @return 63 | */ 64 | Integer countChildPermission(Long id); 65 | 66 | /** 67 | * 根据URL获得权限 68 | * @param url 69 | * @return 70 | */ 71 | Permission findByUrl(String url); 72 | } 73 | 74 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/mapper/CommentMapper.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.example.forum.entity.Comment; 5 | import org.apache.ibatis.annotations.Mapper; 6 | import org.apache.ibatis.annotations.Param; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * @author liuyanzhao 12 | */ 13 | @Mapper 14 | public interface CommentMapper extends BaseMapper { 15 | 16 | /** 17 | * 查询前limit条回帖 18 | * 19 | * @param limit 查询数量 20 | * @return 回帖列表 21 | */ 22 | List findLatestCommentByLimit(Integer limit); 23 | 24 | /** 25 | * 根据用户Id删除 26 | * 27 | * @param userId 用户Id 28 | * @return 影响行数 29 | */ 30 | Integer deleteByUserId(Long userId); 31 | 32 | /** 33 | * 根据用户Id删除 34 | * 35 | * @param userId 用户Id 36 | * @return 影响行数 37 | */ 38 | Integer deleteByAcceptUserId(Long userId); 39 | 40 | 41 | /** 42 | * 获得子回帖Id列表 43 | * 44 | * @param pathTrace 回帖pathTrace封装 45 | * @return 回帖Id列表 46 | */ 47 | List selectChildCommentIds(@Param("pathTrace") String pathTrace); 48 | 49 | /** 50 | * 根据用户ID,标记所有收到的回帖为已读 51 | * @param userId 52 | */ 53 | void readAllByUserId(Long userId); 54 | 55 | /** 56 | * 根据文章ID获得评论列表 57 | * @param postId 58 | * @return 59 | */ 60 | List findByPostId(Long postId); 61 | 62 | /** 63 | * 统计某个用户未读数量 64 | * @return 65 | */ 66 | Integer countNotReadByUserId(Long userId); 67 | 68 | /** 69 | * 更新某个用户在某篇文章下的收到的评论为已读 70 | * @param postId 71 | * @param userId 72 | */ 73 | void updateIsReadByPostIdAndUserId(@Param("postId") Long postId, @Param("userId") Long userId); 74 | } 75 | 76 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/entity/Comment.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableField; 4 | import com.baomidou.mybatisplus.annotation.TableName; 5 | import com.example.forum.common.base.BaseEntity; 6 | import com.example.forum.util.RelativeDateFormat; 7 | import lombok.Data; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * 回复 13 | */ 14 | @Data 15 | @TableName("comment") 16 | public class Comment extends BaseEntity { 17 | 18 | /** 19 | * 帖子ID 20 | */ 21 | private Long postId; 22 | 23 | /** 24 | * 用户ID 25 | */ 26 | private Long userId; 27 | 28 | /** 29 | * 回帖内容 30 | */ 31 | private String commentContent; 32 | 33 | /** 34 | * 上一级 35 | */ 36 | private Long commentParent = 0L; 37 | 38 | /** 39 | * 关系路径 40 | */ 41 | private String pathTrace; 42 | 43 | /** 44 | * 接受者用户Id 45 | */ 46 | private Long acceptUserId; 47 | 48 | /** 49 | * 点赞数 50 | */ 51 | private Long likeCount; 52 | 53 | /** 54 | * 点踩数 55 | */ 56 | private Long dislikeCount; 57 | 58 | /** 59 | * 回帖帖子 60 | */ 61 | @TableField(exist = false) 62 | private Post post; 63 | 64 | 65 | /** 66 | * 是否阅读:1是,0否 67 | */ 68 | private Integer isRead; 69 | 70 | /** 71 | * 回帖人 72 | */ 73 | @TableField(exist = false) 74 | private User user; 75 | 76 | /** 77 | * 当前回帖下的所有子回帖 78 | */ 79 | @TableField(exist = false) 80 | private List childComments; 81 | 82 | /** 83 | * 创建时间 84 | */ 85 | @TableField(exist = false) 86 | private String createTimeStr; 87 | 88 | 89 | public String getCreateTimeStr() { 90 | return RelativeDateFormat.format(getCreateTime()); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; 5 | import com.example.forum.entity.User; 6 | import org.apache.ibatis.annotations.Mapper; 7 | import org.apache.ibatis.annotations.Param; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @author example 13 | */ 14 | @Mapper 15 | public interface UserMapper extends BaseMapper { 16 | 17 | /** 18 | * 根据角色Id获得用户 19 | * 20 | * @param roleId 角色Id 21 | * @param page 分页信息 22 | * @return 用户列表 23 | */ 24 | List findByRoleId(@Param("roleId") Long roleId, Page page); 25 | 26 | /** 27 | * 根据角色Id和条件获得用户 28 | * 29 | * @param roleId 角色Id 30 | * @param user 条件 31 | * @param page 分页信息 32 | * @return 用户列表 33 | */ 34 | List findByRoleIdAndCondition(@Param("roleId") Long roleId, 35 | @Param("user") User user, Page page); 36 | 37 | /** 38 | * 根据条件查询 39 | * 40 | * @param user 用户 41 | * @param page 分页 42 | * @return 用户列表 43 | */ 44 | List findByCondition( @Param("user") User user, Page page); 45 | 46 | /** 47 | * 获得今日新增数量 48 | * @return 49 | */ 50 | Integer getTodayCount(); 51 | 52 | /** 53 | * 获得用户帖子数排名 54 | * @param limit 查询数量 55 | * @return 56 | */ 57 | List getUserPostRanking(Integer limit); 58 | 59 | /** 60 | * 获得最新注册用户 61 | * @param limit 62 | * @return 63 | */ 64 | List getLatestUser(Integer limit); 65 | 66 | /** 67 | * 获得热门用户 68 | * @param limit 用户数量 69 | * @return 70 | */ 71 | List getHotUsers(Integer limit); 72 | 73 | } 74 | 75 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/util/IpInfoUtil.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.util; 2 | 3 | 4 | import lombok.extern.slf4j.Slf4j; 5 | 6 | import javax.servlet.http.HttpServletRequest; 7 | import java.net.InetAddress; 8 | import java.net.UnknownHostException; 9 | 10 | 11 | /** 12 | * @author example 13 | */ 14 | @Slf4j 15 | public class IpInfoUtil { 16 | 17 | /** 18 | * 获取客户端IP地址 19 | * @param request 请求 20 | * @return 21 | */ 22 | public static String getIpAddr(HttpServletRequest request) { 23 | 24 | String ip = request.getHeader("x-forwarded-for"); 25 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 26 | ip = request.getHeader("Proxy-Client-IP"); 27 | } 28 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 29 | ip = request.getHeader("WL-Proxy-Client-IP"); 30 | } 31 | if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 32 | ip = request.getRemoteAddr(); 33 | if ("127.0.0.1".equals(ip)) { 34 | //根据网卡取本机配置的IP 35 | InetAddress inet = null; 36 | try { 37 | inet = InetAddress.getLocalHost(); 38 | } catch (UnknownHostException e) { 39 | e.printStackTrace(); 40 | } 41 | ip = inet.getHostAddress(); 42 | } 43 | } 44 | // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割 45 | if (ip != null && ip.length() > 15) { 46 | if (ip.indexOf(",") > 0) { 47 | ip = ip.substring(0, ip.indexOf(",")); 48 | } 49 | } 50 | if("0:0:0:0:0:0:0:1".equals(ip)){ 51 | ip = "127.0.0.1"; 52 | } 53 | return ip; 54 | } 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/pace/pace.min.css: -------------------------------------------------------------------------------- 1 | .pace{-webkit-pointer-events:none;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.pace-inactive{display:none}.pace .pace-progress{background:#fff;position:fixed;z-index:2000;top:0;right:100%;width:100%;height:2px}.pace .pace-progress-inner{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #fff,0 0 5px #fff;opacity:1;-webkit-transform:rotate(3deg) translate(0px,-4px);-moz-transform:rotate(3deg) translate(0px,-4px);-ms-transform:rotate(3deg) translate(0px,-4px);-o-transform:rotate(3deg) translate(0px,-4px);transform:rotate(3deg) translate(0px,-4px)}.pace .pace-activity{display:block;position:fixed;z-index:2000;top:15px;right:50%;width:14px;height:14px;border:solid 2px transparent;border-top-color:#fff;border-left-color:#fff;border-radius:10px;-webkit-animation:pace-spinner 400ms linear infinite;-moz-animation:pace-spinner 400ms linear infinite;-ms-animation:pace-spinner 400ms linear infinite;-o-animation:pace-spinner 400ms linear infinite;animation:pace-spinner 400ms linear infinite}@media (max-width: 767px){.pace .pace-activity{top:15px;right:15px;width:14px;height:14px}}@-webkit-keyframes pace-spinner{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@-moz-keyframes pace-spinner{0%{-moz-transform:rotate(0deg);transform:rotate(0deg)}100%{-moz-transform:rotate(360deg);transform:rotate(360deg)}}@-o-keyframes pace-spinner{0%{-o-transform:rotate(0deg);transform:rotate(0deg)}100%{-o-transform:rotate(360deg);transform:rotate(360deg)}}@-ms-keyframes pace-spinner{0%{-ms-transform:rotate(0deg);transform:rotate(0deg)}100%{-ms-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes pace-spinner{0%{transform:rotate(0deg);transform:rotate(0deg)}100%{transform:rotate(360deg);transform:rotate(360deg)}} 2 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/entity/User.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableField; 4 | import com.baomidou.mybatisplus.annotation.TableName; 5 | import com.example.forum.common.base.BaseEntity; 6 | import com.fasterxml.jackson.annotation.JsonIgnore; 7 | import lombok.Data; 8 | 9 | import javax.validation.constraints.Email; 10 | import javax.validation.constraints.NotBlank; 11 | import java.util.Date; 12 | 13 | /** 14 | * 用户信息 15 | */ 16 | @Data 17 | @TableName("user") 18 | public class User extends BaseEntity { 19 | 20 | /** 21 | * 用户名 22 | */ 23 | @NotBlank(message = "用户名不能为空") 24 | private String userName; 25 | 26 | /** 27 | * 显示名称 28 | */ 29 | private String userDisplayName; 30 | 31 | /** 32 | * 密码 33 | */ 34 | @JsonIgnore 35 | private String userPass; 36 | 37 | /** 38 | * 邮箱 39 | */ 40 | @Email(message = "邮箱格式不正确") 41 | private String userEmail; 42 | 43 | /** 44 | * 头像 45 | */ 46 | private String userAvatar; 47 | 48 | /** 49 | * 说明 50 | */ 51 | private String userDesc; 52 | 53 | /** 54 | * 是否禁用登录 55 | */ 56 | // @JsonIgnore 57 | private String loginEnable = "true"; 58 | 59 | 60 | /** 61 | * 登录错误次数记录 62 | */ 63 | private Integer loginError = 0; 64 | 65 | /** 66 | * 最后一次登录时间 67 | */ 68 | private Date loginLast; 69 | 70 | /** 71 | * 0 正常 72 | * 1 禁用 73 | * 2 已删除 74 | */ 75 | private Integer status = 0; 76 | 77 | /** 78 | * 创建时间 79 | */ 80 | private Date createTime; 81 | 82 | /** 83 | * 角色名称 84 | */ 85 | @TableField(exist = false) 86 | private String role; 87 | 88 | /** 89 | * 帖子数 90 | */ 91 | @TableField(exist = false) 92 | private Integer postCount; 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/dto/JsonResult.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | *
 7 |  *     Json格式
 8 |  * 
9 | * 10 | * @author : saysky 11 | * @date : 2018/5/24 12 | */ 13 | @Data 14 | public class JsonResult { 15 | 16 | /** 17 | * 返回的状态码,0:失败,1:成功 18 | */ 19 | private Integer code; 20 | 21 | /** 22 | * 返回信息 23 | */ 24 | private String msg; 25 | 26 | /** 27 | * 返回的数据 28 | */ 29 | private Object result; 30 | 31 | /** 32 | * 不返回数据的构造方法 33 | * 34 | * @param code 状态码 35 | * @param msg 信息 36 | */ 37 | public JsonResult(Integer code, String msg) { 38 | this.code = code; 39 | this.msg = msg; 40 | } 41 | 42 | /** 43 | * 返回数据的构造方法 44 | * 45 | * @param code 状态码 46 | * @param msg 信息 47 | * @param result 数据 48 | */ 49 | public JsonResult(Integer code, String msg, Object result) { 50 | this.code = code; 51 | this.msg = msg; 52 | this.result = result; 53 | } 54 | 55 | /** 56 | * 返回状态码和数据 57 | * 58 | * @param code 状态码 59 | * @param result 数据 60 | */ 61 | public JsonResult(Integer code, Object result) { 62 | this.code = code; 63 | this.result = result; 64 | } 65 | 66 | public static JsonResult error(String msg) { 67 | return new JsonResult(0, msg); 68 | } 69 | public static JsonResult error(String msg, Object data) { 70 | return new JsonResult(0, msg, data); 71 | } 72 | public static JsonResult success() { 73 | return new JsonResult(1, "操作成功"); 74 | } 75 | 76 | public static JsonResult success(String msg) { 77 | return new JsonResult(1, msg); 78 | } 79 | 80 | public static JsonResult success(String msg, Object result) { 81 | return new JsonResult(1, msg, result); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/plugins/quick_insert.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | .clearfix::after { 8 | clear: both; 9 | display: block; 10 | content: ""; 11 | height: 0; 12 | } 13 | .hide-by-clipping { 14 | position: absolute; 15 | width: 1px; 16 | height: 1px; 17 | padding: 0; 18 | margin: -1px; 19 | overflow: hidden; 20 | clip: rect(0, 0, 0, 0); 21 | border: 0; 22 | } 23 | .fr-quick-insert { 24 | position: absolute; 25 | z-index: 2147483639; 26 | white-space: nowrap; 27 | padding-right: 5px; 28 | margin-left: -5px; 29 | -webkit-box-sizing: content-box; 30 | -moz-box-sizing: content-box; 31 | box-sizing: content-box; 32 | } 33 | .fr-quick-insert.fr-on a.fr-floating-btn svg { 34 | -webkit-transform: rotate(135deg); 35 | -moz-transform: rotate(135deg); 36 | -ms-transform: rotate(135deg); 37 | -o-transform: rotate(135deg); 38 | } 39 | .fr-quick-insert.fr-hidden { 40 | display: none; 41 | } 42 | .fr-qi-helper { 43 | position: absolute; 44 | z-index: 3; 45 | padding-left: 16px; 46 | white-space: nowrap; 47 | } 48 | .fr-qi-helper a.fr-btn.fr-floating-btn { 49 | text-align: center; 50 | display: inline-block; 51 | color: #222222; 52 | -webkit-opacity: 0; 53 | -moz-opacity: 0; 54 | opacity: 0; 55 | -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; 56 | -webkit-transform: scale(0); 57 | -moz-transform: scale(0); 58 | -ms-transform: scale(0); 59 | -o-transform: scale(0); 60 | } 61 | .fr-qi-helper a.fr-btn.fr-floating-btn.fr-size-1 { 62 | -webkit-opacity: 1; 63 | -moz-opacity: 1; 64 | opacity: 1; 65 | -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; 66 | -webkit-transform: scale(1); 67 | -moz-transform: scale(1); 68 | -ms-transform: scale(1); 69 | -o-transform: scale(1); 70 | } 71 | -------------------------------------------------------------------------------- /src/main/resources/static/front/scss/_categories_single.scss: -------------------------------------------------------------------------------- 1 | .tt-catSingle-title{ 2 | padding: 0 0 30px; 3 | letter-spacing: 0.01em; 4 | border-bottom: 1px solid $border; 5 | .tt-row{ 6 | display: flex; 7 | flex-direction: row; 8 | flex-wrap: nowrap; 9 | justify-content: flex-start; 10 | align-content: center; 11 | align-items: center; 12 | .tt-col-left{ 13 | flex: 2 1 auto; 14 | } 15 | .tt-col-item{ 16 | display: inline-block; 17 | } 18 | .tt-search{ 19 | width: 255px; 20 | } 21 | } 22 | .tt-value{ 23 | color: $default_color2; 24 | font-size: 14px; 25 | line-height: 26px; 26 | font-weight: 400; 27 | } 28 | .tt-title{ 29 | color: $default_color2; 30 | font-size: 14px; 31 | font-weight: 700; 32 | padding-bottom: 6px; 33 | margin-bottom: 0; 34 | } 35 | .tt-btn-icon .tt-icon svg{ 36 | width: 20px; 37 | height: 18px; 38 | } 39 | .tt-innerwrapper{ 40 | &:not(:first-child){ 41 | margin-top: 13px; 42 | } 43 | } 44 | @media (min-width: 768px){ 45 | margin-left: 30px; 46 | margin-right: 30px; 47 | .tt-row{ 48 | .tt-col-right{ 49 | .tt-col-item{ 50 | margin-left: 11px; 51 | } 52 | } 53 | } 54 | } 55 | @media (max-width: 767px){ 56 | margin-left: 10px; 57 | margin-right: 10px; 58 | .tt-search{ 59 | width: auto !important; 60 | .search-wrapper{ 61 | display: none; 62 | } 63 | .tt-search-toggle{ 64 | display: inline-block; 65 | } 66 | } 67 | .tt-btn-icon{ 68 | padding-left: 10px; 69 | padding-right: 10px; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/service/impl/LinkServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.service.impl; 2 | 3 | import cn.hutool.core.util.StrUtil; 4 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 5 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 6 | import com.example.forum.entity.Link; 7 | import com.example.forum.mapper.LinkMapper; 8 | import com.example.forum.service.LinkService; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Service; 11 | 12 | import java.util.List; 13 | 14 | /** 15 | *
16 |  *     友情链接业务逻辑实现类
17 |  * 
18 | * 19 | */ 20 | @Service 21 | public class LinkServiceImpl implements LinkService { 22 | 23 | 24 | @Autowired 25 | private LinkMapper linkMapper; 26 | 27 | @Override 28 | public BaseMapper getRepository() { 29 | return linkMapper; 30 | } 31 | 32 | @Override 33 | public QueryWrapper getQueryWrapper(Link link) { 34 | //对指定字段查询 35 | QueryWrapper queryWrapper = new QueryWrapper<>(); 36 | if (link != null) { 37 | if (StrUtil.isNotBlank(link.getLinkName())) { 38 | queryWrapper.like("link_name", link.getLinkName()); 39 | } 40 | if (StrUtil.isNotBlank(link.getLinkUrl())) { 41 | queryWrapper.like("link_url", link.getLinkUrl()); 42 | } 43 | } 44 | return queryWrapper; 45 | } 46 | 47 | @Override 48 | public Link insertOrUpdate(Link entity) { 49 | if (entity.getId() == null) { 50 | insert(entity); 51 | } else { 52 | update(entity); 53 | } 54 | return entity; 55 | } 56 | 57 | @Override 58 | public void delete(Long id) { 59 | linkMapper.deleteById(id); 60 | } 61 | 62 | @Override 63 | public List findAll() { 64 | List linkList = linkMapper.selectList(null); 65 | return linkList; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/util/ObjectUtil.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.util; 2 | 3 | import cn.hutool.core.util.StrUtil; 4 | import com.google.common.collect.Maps; 5 | import com.google.gson.Gson; 6 | import org.springframework.cglib.beans.BeanMap; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | /** 12 | * @author example 13 | */ 14 | public class ObjectUtil { 15 | 16 | public static String mapToString(Map paramMap){ 17 | 18 | if (paramMap == null) { 19 | return ""; 20 | } 21 | Map params = new HashMap<>(16); 22 | for (Map.Entry param : paramMap.entrySet()) { 23 | 24 | String key = param.getKey(); 25 | String paramValue = (param.getValue() != null && param.getValue().length > 0 ? param.getValue()[0] : ""); 26 | String obj = StrUtil.endWithIgnoreCase(param.getKey(), "password") ? "密码隐藏" : paramValue; 27 | params.put(key,obj); 28 | } 29 | return new Gson().toJson(params); 30 | } 31 | 32 | public static String mapToStringAll(Map paramMap){ 33 | 34 | if (paramMap == null) { 35 | return ""; 36 | } 37 | Map params = new HashMap<>(16); 38 | for (Map.Entry param : paramMap.entrySet()) { 39 | 40 | String key = param.getKey(); 41 | String paramValue = (param.getValue() != null && param.getValue().length > 0 ? param.getValue()[0] : ""); 42 | params.put(key, paramValue); 43 | } 44 | return new Gson().toJson(params); 45 | } 46 | 47 | public static Map beanToMap(T bean) { 48 | Map map = Maps.newHashMap(); 49 | if (bean != null) { 50 | BeanMap beanMap = BeanMap.create(bean); 51 | for (Object key : beanMap.keySet()) { 52 | map.put(key+"", beanMap.get(key)); 53 | } 54 | } 55 | return map; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/util/Md5Util.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.util; 2 | 3 | import cn.hutool.core.text.StrBuilder; 4 | import org.apache.shiro.crypto.hash.Md5Hash; 5 | import org.springframework.web.multipart.MultipartFile; 6 | 7 | import java.io.InputStream; 8 | import java.security.MessageDigest; 9 | 10 | /** 11 | * 获取文件hash 12 | */ 13 | public class Md5Util { 14 | 15 | 16 | /** 17 | * shiro的md5加密 18 | * 19 | * @param pwd 密码 20 | * @param salt 盐 21 | * @param i 加密次数 22 | * @return 加密后字符串 23 | */ 24 | public static String toMd5(String pwd, String salt, int i) { 25 | Md5Hash toMd5 = new Md5Hash(pwd, salt, i); 26 | return toMd5.toString(); 27 | } 28 | 29 | /** 30 | * 计算文件MD5编码 31 | * 32 | * @param file file 33 | * @return byte 34 | * @throws Exception Exception 35 | */ 36 | private static byte[] createChecksum(MultipartFile file) throws Exception { 37 | final InputStream fis = file.getInputStream(); 38 | 39 | final byte[] buffer = new byte[1024]; 40 | final MessageDigest complete = MessageDigest.getInstance("MD5"); 41 | int numRead; 42 | 43 | do { 44 | numRead = fis.read(buffer); 45 | if (numRead > 0) { 46 | complete.update(buffer, 0, numRead); 47 | } 48 | } while (numRead != -1); 49 | 50 | fis.close(); 51 | return complete.digest(); 52 | } 53 | 54 | /** 55 | * 生成文件hash值 56 | * 57 | * @param file file 58 | * @return String 59 | * @throws Exception Exception 60 | */ 61 | public static String getMD5Checksum(MultipartFile file) throws Exception { 62 | final byte[] b = createChecksum(file); 63 | StrBuilder result = new StrBuilder(); 64 | 65 | for (int i = 0; i < b.length; i++) { 66 | result.append(Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1)); 67 | } 68 | return result.toString(); 69 | } 70 | 71 | public static void main(String args[]) { 72 | 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/util/CommentUtil.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.util; 2 | 3 | import com.example.forum.entity.Comment; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Collections; 7 | import java.util.List; 8 | 9 | /** 10 | *
11 |  *     拼装回帖
12 |  * 
13 | * 14 | * @author : saysky 15 | * @date : 2018/7/12 16 | */ 17 | public class CommentUtil { 18 | 19 | /** 20 | * 获取回帖树 21 | * 22 | * @param commentsRoot commentsRoot 23 | * @return List 24 | */ 25 | public static List getComments(List commentsRoot) { 26 | if(commentsRoot == null || commentsRoot.size() == 0) { 27 | return Collections.emptyList(); 28 | } 29 | List commentsResult = new ArrayList<>(); 30 | for (Comment comment : commentsRoot) { 31 | if (comment.getCommentParent() == 0) { 32 | commentsResult.add(comment); 33 | } 34 | } 35 | for (Comment comment : commentsResult) { 36 | comment.setChildComments(getChild(comment.getId(), commentsRoot)); 37 | } 38 | return commentsResult; 39 | } 40 | 41 | /** 42 | * 获取回帖的子回帖 43 | * 44 | * @param id 回帖编号 45 | * @param commentsRoot commentsRoot 46 | * @return List 47 | */ 48 | private static List getChild(Long id, List commentsRoot) { 49 | List commentsChild = new ArrayList<>(); 50 | for (Comment comment : commentsRoot) { 51 | if (comment.getCommentParent() != 0) { 52 | if (comment.getCommentParent().equals(id)) { 53 | commentsChild.add(comment); 54 | } 55 | } 56 | } 57 | for (Comment comment : commentsChild) { 58 | if (comment.getCommentParent() != 0) { 59 | comment.setChildComments(getChild(comment.getId(), commentsRoot)); 60 | } 61 | } 62 | if (commentsChild.size() == 0) { 63 | return null; 64 | } 65 | return commentsChild; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/third_party/spell_checker.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | .clearfix::after { 8 | clear: both; 9 | display: block; 10 | content: ""; 11 | height: 0; 12 | } 13 | .hide-by-clipping { 14 | position: absolute; 15 | width: 1px; 16 | height: 1px; 17 | padding: 0; 18 | margin: -1px; 19 | overflow: hidden; 20 | clip: rect(0, 0, 0, 0); 21 | border: 0; 22 | } 23 | .examples-variante > a { 24 | font-size: 14px; 25 | font-family: Arial, Helvetica, sans-serif; 26 | } 27 | .sc-cm-holder > .sc-cm { 28 | border-top: 5px solid #222222 !important; 29 | padding: 0px !important; 30 | line-height: 200% !important; 31 | -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16); 32 | -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16); 33 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16); 34 | } 35 | .sc-cm .sc-cm__item.examples-variante.sc-cm__item_active > a > i { 36 | display: none !important; 37 | } 38 | .sc-cm .sc-cm__item.examples-variante > a > i { 39 | display: none !important; 40 | } 41 | .sc-cm__item_dropdown .i-icon { 42 | display: none !important; 43 | } 44 | .sc-cm__item_dropdown .i-icon::before { 45 | display: none !important; 46 | } 47 | .sc-cm::before { 48 | display: none !important; 49 | } 50 | div.sc-cm-holder.sc-cm_show > ul > li.sc-cm__item.sc-cm__item_dropdown.sc-cm__item_arrow > div > ul { 51 | border-style: none !important; 52 | padding: 0px !important; 53 | } 54 | .sc-cm__item_dropdown:hover > a, 55 | .sc-cm a:hover { 56 | background-color: #ebebeb !important; 57 | } 58 | .sc-cm__item_active > a, 59 | .sc-cm__item_active > a:hover, 60 | .sc-cm a:active, 61 | .sc-cm a:focus { 62 | background-color: #d6d6d6 !important; 63 | } 64 | .sc-cm__item > a { 65 | line-height: 200% !important; 66 | } 67 | .sc-cm-holder > .sc-cm:before { 68 | background-color: #ebebeb !important; 69 | } 70 | .sc-cm-holder { 71 | display: none; 72 | } 73 | -------------------------------------------------------------------------------- /src/main/resources/static/front/scss/_user_header.scss: -------------------------------------------------------------------------------- 1 | /* 2 | user-header(*page-single-user.html) 3 | */ 4 | .tt-user-header{ 5 | display: flex; 6 | flex-direction: row; 7 | flex-wrap: nowrap; 8 | justify-content: flex-start; 9 | align-content: center; 10 | align-items: center; 11 | padding: 29px 0 29px 15px; 12 | .tt-col-avatar{ 13 | padding-left: 15px; 14 | padding-right: 15px; 15 | width: 8.33333%; 16 | .tt-icon{ 17 | width: 40px; 18 | height: 40px; 19 | @media (max-width: 575px){ 20 | width: 30px; 21 | height: 30px; 22 | } 23 | } 24 | @media (max-width: 991px){ 25 | width: 10.33333%; 26 | } 27 | @media (max-width: 767px){ 28 | width: 12.33333%; 29 | min-width: 50px; 30 | } 31 | 32 | } 33 | .tt-col-title{ 34 | display: flex; 35 | flex-direction: row; 36 | flex-wrap: nowrap; 37 | justify-content: flex-start; 38 | align-content: center; 39 | align-items: center; 40 | padding-left: 15px; 41 | padding-right: 15px; 42 | flex: 2 1 auto; 43 | .tt-title{ 44 | color: #182730; 45 | font-weight: 600; 46 | a{ 47 | color: #182730; 48 | transition: color 0.2s linear; 49 | &:hover{ 50 | color: #2172cd; 51 | } 52 | } 53 | } 54 | > *:not(:first-child){ 55 | margin-left: 15px; 56 | } 57 | } 58 | .tt-btn-icon{ 59 | svg{ 60 | width: 18px; 61 | height: 18px; 62 | } 63 | } 64 | @media (min-width: 768px){ 65 | .btn{ 66 | min-width: 110px; 67 | } 68 | } 69 | @media (max-width: 767px){ 70 | flex-wrap: wrap; 71 | .tt-col-btn{ 72 | width: 100%; 73 | display: block; 74 | margin-top: 20px; 75 | .tt-btn-icon{ 76 | padding-left: 0; 77 | } 78 | } 79 | } 80 | @media (max-width: 1024px){ 81 | >[class^=tt-col] { 82 | padding-left: 10px; 83 | padding-right: 10px; 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/entity/Post.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.entity; 2 | 3 | import com.baomidou.mybatisplus.annotation.TableField; 4 | import com.baomidou.mybatisplus.annotation.TableName; 5 | import com.example.forum.common.base.BaseEntity; 6 | import com.example.forum.util.RelativeDateFormat; 7 | import lombok.Data; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | /** 13 | * @author example 14 | */ 15 | @Data 16 | @TableName("post") 17 | public class Post extends BaseEntity { 18 | 19 | /** 20 | * 用户ID 21 | */ 22 | private Long userId; 23 | /** 24 | * 帖子标题 25 | */ 26 | private String postType; 27 | 28 | /** 29 | * 帖子标题 30 | */ 31 | private String postTitle; 32 | 33 | /** 34 | * 帖子内容 html格式 35 | */ 36 | private String postContent; 37 | 38 | /** 39 | * 帖子摘要 40 | */ 41 | private String postSummary; 42 | 43 | /** 44 | * 缩略图 45 | */ 46 | private String postThumbnail; 47 | 48 | /** 49 | * 0 已发布 50 | * 1 草稿 51 | * 2 回收站 52 | */ 53 | private Integer postStatus; 54 | 55 | /** 56 | * 帖子访问量 57 | */ 58 | private Long postViews; 59 | 60 | /** 61 | * 点赞访问量 62 | */ 63 | private Long postLikes; 64 | 65 | /** 66 | * 回帖数量(冗余字段,加快查询速度) 67 | */ 68 | private Long commentSize; 69 | 70 | 71 | /** 72 | * 发表用户 多对一 73 | */ 74 | @TableField(exist = false) 75 | private User user; 76 | 77 | /** 78 | * 帖子所属分类 79 | */ 80 | @TableField(exist = false) 81 | private Category category; 82 | 83 | /** 84 | * 帖子所属标签 85 | */ 86 | @TableField(exist = false) 87 | private List tagList = new ArrayList<>(); 88 | 89 | /** 90 | * 帖子的回帖 91 | */ 92 | @TableField(exist = false) 93 | private List comments = new ArrayList<>(); 94 | 95 | /** 96 | * 更新时间 97 | */ 98 | @TableField(exist = false) 99 | private String createTimeStr; 100 | 101 | public String getCreateTimeStr() { 102 | return RelativeDateFormat.format(getCreateTime()); 103 | } 104 | 105 | -------------------------------------------------------------------------------- /src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8080 3 | forward-headers-strategy: true 4 | undertow: 5 | io-threads: 2 6 | worker-threads: 36 7 | buffer-size: 1024 8 | directBuffers: true 9 | servlet: 10 | session: 11 | timeout: 86400 12 | 13 | spring: 14 | transaction: 15 | rollback-on-commit-failure: true 16 | datasource: 17 | type: com.alibaba.druid.pool.DruidDataSource 18 | #MySql配置 19 | driver-class-name: com.mysql.cj.jdbc.Driver 20 | url: jdbc:mysql://127.0.0.1:3306/car_forum?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false&allowMultiQueries=true 21 | username: root 22 | password: 123456 23 | test-while-idle: true 24 | time-between-eviction-runs-millis: 300000 25 | min-evictable-idle-time-millis: 1800000 26 | thymeleaf: 27 | mode: HTML5 28 | cache: false 29 | prefix: classpath:/templates/ 30 | encoding: UTF-8 31 | suffix: .html 32 | check-template-location: false 33 | servlet: 34 | multipart: 35 | max-file-size: 2MB 36 | max-request-size: 20MB 37 | devtools: 38 | restart: 39 | enabled: true 40 | additional-paths: src/main/java 41 | 42 | mybatis-plus: 43 | mapper-locations: classpath*:/mapper/**Mapper.xml 44 | #实体扫描,多个package用逗号或者分号分隔 45 | typeAliasesPackage: com.example.sens.entity 46 | global-config: 47 | #主键类型 0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID"; 48 | id-type: 0 49 | #字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断" 50 | field-strategy: 2 51 | #驼峰下划线转换 52 | db-column-underline: true 53 | #刷新mapper 调试神器 54 | refresh-mapper: true 55 | #逻辑删除配置(下面3个配置) 56 | logic-delete-value: 1 57 | logic-not-delete-value: 0 58 | configuration: 59 | map-underscore-to-camel-case: true 60 | cache-enabled: true 61 | 62 | logging: 63 | file: ./logs/log.log 64 | level: 65 | org: 66 | springframework: 67 | boot: 68 | autoconfigure: error 69 | # web: 70 | # trace 71 | 72 | application: 73 | formatted-version: 1.0.0 74 | 75 | shiro: 76 | userNativeSessionManager: true 77 | 78 | mail: 79 | smtp: 80 | host: smtp.qq.com 81 | username: 847064370@qq.com 82 | password: vtvhcjsacnuubdaj 83 | from: 84 | name: 学车社区 85 | -------------------------------------------------------------------------------- /src/main/resources/mapper/TagMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | DELETE 7 | FROM 8 | tag 9 | WHERE 10 | user_id = #{value} 11 | 12 | 13 | 26 | 27 | 46 | 47 | 48 | 60 | 61 | 76 | 77 | -------------------------------------------------------------------------------- /src/main/resources/static/front/scss/_login.scss: -------------------------------------------------------------------------------- 1 | /* Login page*/ 2 | .tt-loginpages{ 3 | letter-spacing: 0.01em; 4 | @media (min-width: 576px){ 5 | background-color:#ffffff; 6 | box-shadow: 0px 2px 3px rgba(0, 0, 0, 0.09); 7 | padding: 30px 30px 37px; 8 | margin-top: 50px; 9 | margin-bottom: 50px; 10 | } 11 | @media (max-width: 575px){ 12 | padding: 30px 20px 37px; 13 | margin-top: 30px; 14 | margin-bottom: 30px; 15 | } 16 | .tt-block-title{ 17 | position: relative; 18 | padding-bottom: 23px; 19 | margin-bottom: 21px; 20 | display: block; 21 | &:before{ 22 | content: ''; 23 | position: absolute; 24 | bottom: 0; 25 | left: 0; 26 | width: 160px; 27 | height: 1px; 28 | background-color:$border; 29 | } 30 | .tt-title{ 31 | color: #303344; 32 | font-size: 18px; 33 | line-height: 26px; 34 | font-weight: 600; 35 | margin-top: 21px; 36 | padding: 0; 37 | letter-spacing: 0.01em; 38 | } 39 | > *:nth-child(1){ 40 | margin-top: 0; 41 | } 42 | & .tt-title{ 43 | position: relative; 44 | transition: color .2s linear; 45 | } 46 | &:hover{ 47 | .tt-title{ 48 | color: $default_color; 49 | } 50 | } 51 | } 52 | .tt-description{ 53 | color: $default_text; 54 | } 55 | .form-default{ 56 | .form-group{ 57 | margin-bottom: 21px; 58 | label:not(.error){ 59 | padding-bottom: 7px; 60 | } 61 | .btn{ 62 | margin-top: 9px; 63 | } 64 | .checkbox-group{ 65 | label:not(.error){ 66 | padding-bottom: 0; 67 | } 68 | } 69 | } 70 | p{ 71 | margin-bottom: 13px !important; 72 | } 73 | } 74 | .tt-notes{ 75 | position: relative; 76 | font-size: 14px; 77 | line-height: 26px; 78 | padding-top: 21px; 79 | margin-top: 22px; 80 | &:before{ 81 | content: ''; 82 | display: block; 83 | position: absolute; 84 | top: 0; 85 | left: 0; 86 | width: 65px; 87 | height: 1px; 88 | background-color: $border; 89 | } 90 | } 91 | } 92 | .tt-loginpages-wrapper{ 93 | display: flex; 94 | flex-direction: row; 95 | flex-wrap: wrap; 96 | justify-content: center; 97 | align-content: stretch; 98 | align-items: flex-start; 99 | .tt-loginpages{ 100 | @media (min-width: 471px){ 101 | width: 410px; 102 | } 103 | @media (max-width: 470px){ 104 | width: 100%; 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/resources/static/front/scss/_gallery.scss: -------------------------------------------------------------------------------- 1 | /* 2 | Gallery 3 | */ 4 | .tt-gallery-layout{ 5 | display: flex; 6 | flex-direction: row; 7 | flex-wrap: wrap; 8 | justify-content: flex-start; 9 | align-content: stretch; 10 | align-items: flex-start; 11 | @media (min-width: 1025px){ 12 | margin-top: -30px; 13 | margin-left: -30px; 14 | .tt-item{ 15 | margin-top: 30px; 16 | margin-left: 30px; 17 | width: calc(33.333% - 30px); 18 | } 19 | } 20 | @media (max-width: 1024px){ 21 | margin-top: -20px; 22 | margin-left: -20px; 23 | .tt-item{ 24 | margin-top: 20px; 25 | margin-left: 20px; 26 | width: calc(33.333% - 20px); 27 | } 28 | } 29 | @media (max-width: 470px){ 30 | .tt-item{ 31 | width: calc(50% - 20px); 32 | } 33 | } 34 | } 35 | .tt-gallery-obj{ 36 | display: block; 37 | position: relative; 38 | transition: all 0.3s linear; 39 | img{ 40 | width: 100%; 41 | height: auto; 42 | } 43 | &:before{ 44 | content: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyMy4wLjEsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iTGF5ZXJfNDgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSIwIDAgMTQ0IDkwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCAxNDQgOTA7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4NCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoJLnN0MHtmaWxsOiNmZmZmZmY7fQ0KPC9zdHlsZT4NCjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik03MiwwQzMyLjI0LDAsMCwzNiwwLDQ1czMyLjI0LDQ1LDcyLDQ1czcyLTM2LDcyLTQ1UzExMS43NiwwLDcyLDB6IE03Miw3MmMtMTQuOTEsMC0yNy0xMi4wOS0yNy0yNw0KCWMwLTE0LjkxLDEyLjA5LTI3LDI3LTI3YzE0LjkxLDAsMjcsMTIuMDksMjcsMjdDOTksNTkuOTEsODYuOTEsNzIsNzIsNzJ6Ii8+DQo8L3N2Zz4NCg=='); 45 | display: block; 46 | top: 50%; 47 | left: 50%; 48 | margin-top: -13px; 49 | margin-left: -20px; 50 | width: 40px; 51 | height: 25px; 52 | position: absolute; 53 | display: block; 54 | transition: all 0.1s linear; 55 | pointer-events: none; 56 | } 57 | @media (min-width: 1025px){ 58 | &:before{ 59 | transform: scale(0); 60 | } 61 | &:hover{ 62 | &:before{ 63 | transform: scale(1); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/util/RelativeDateFormat.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.util; 2 | 3 | 4 | import java.util.Date; 5 | 6 | /** 7 | * @author 言曌 8 | * @date 2020/3/10 9:48 下午 9 | */ 10 | 11 | public class RelativeDateFormat { 12 | 13 | private static final long ONE_MINUTE = 60000L; 14 | private static final long ONE_HOUR = 3600000L; 15 | private static final long ONE_DAY = 86400000L; 16 | private static final long ONE_WEEK = 604800000L; 17 | 18 | private static final String ONE_SECOND_AGO = "秒前"; 19 | private static final String ONE_MINUTE_AGO = "分钟前"; 20 | private static final String ONE_HOUR_AGO = "小时前"; 21 | private static final String ONE_DAY_AGO = "天前"; 22 | private static final String ONE_MONTH_AGO = "月前"; 23 | private static final String ONE_YEAR_AGO = "年前"; 24 | 25 | public static String format(Date date) { 26 | long delta = System.currentTimeMillis() - date.getTime(); 27 | if (delta < 1L * ONE_MINUTE) { 28 | long seconds = toSeconds(delta); 29 | return (seconds <= 0 ? 1 : seconds) + ONE_SECOND_AGO; 30 | } 31 | if (delta < 45L * ONE_MINUTE) { 32 | long minutes = toMinutes(delta); 33 | return (minutes <= 0 ? 1 : minutes) + ONE_MINUTE_AGO; 34 | } 35 | if (delta < 24L * ONE_HOUR) { 36 | long hours = toHours(delta); 37 | return (hours <= 0 ? 1 : hours) + ONE_HOUR_AGO; 38 | } 39 | if (delta < 48L * ONE_HOUR) { 40 | return "昨天"; 41 | } 42 | if (delta < 30L * ONE_DAY) { 43 | long days = toDays(delta); 44 | return (days <= 0 ? 1 : days) + ONE_DAY_AGO; 45 | } 46 | if (delta < 12L * 4L * ONE_WEEK) { 47 | long months = toMonths(delta); 48 | return (months <= 0 ? 1 : months) + ONE_MONTH_AGO; 49 | } else { 50 | long years = toYears(delta); 51 | return (years <= 0 ? 1 : years) + ONE_YEAR_AGO; 52 | } 53 | } 54 | 55 | private static long toSeconds(long date) { 56 | return date / 1000L; 57 | } 58 | 59 | private static long toMinutes(long date) { 60 | return toSeconds(date) / 60L; 61 | } 62 | 63 | private static long toHours(long date) { 64 | return toMinutes(date) / 60L; 65 | } 66 | 67 | private static long toDays(long date) { 68 | return toHours(date) / 24L; 69 | } 70 | 71 | private static long toMonths(long date) { 72 | return toDays(date) / 30L; 73 | } 74 | 75 | private static long toYears(long date) { 76 | return toMonths(date) / 365L; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/util/Response.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.util; 2 | 3 | public class Response { 4 | 5 | private Boolean success; 6 | 7 | private String message; 8 | 9 | private T data; 10 | 11 | /** 12 | * 状态码 13 | */ 14 | private Integer status = 200; 15 | 16 | public Response() { 17 | } 18 | 19 | public Response(Boolean success) { 20 | this.success = success; 21 | } 22 | 23 | public Response(Boolean success, String message) { 24 | this.success = success; 25 | this.message = message; 26 | } 27 | 28 | public Response(Boolean success, String message, T data) { 29 | this.success = success; 30 | this.message = message; 31 | this.data = data; 32 | } 33 | 34 | public Response(Boolean success, Integer status, String message, T data) { 35 | this.success = success; 36 | this.message = message; 37 | this.data = data; 38 | this.status = status; 39 | } 40 | 41 | 42 | public static Response yes() { 43 | return new Response(true, 200, "成功", null); 44 | } 45 | 46 | public static Response yes(T data) { 47 | return new Response(true, 200, "成功", data); 48 | } 49 | 50 | public static Response yes(String message, T data) { 51 | return new Response(true, 200, message, data); 52 | } 53 | 54 | public static Response no() { 55 | return new Response(false, 500, "失败", null); 56 | } 57 | 58 | public static Response no(String message) { 59 | return new Response(false, 500, message, null); 60 | } 61 | 62 | public static Response no(Integer status, String message) { 63 | return new Response(false, status, message, null); 64 | } 65 | 66 | public Boolean isSuccess() { 67 | return success; 68 | } 69 | 70 | public void setSuccess(Boolean success) { 71 | this.success = success; 72 | } 73 | 74 | public String getMessage() { 75 | return message; 76 | } 77 | 78 | public void setMessage(String message) { 79 | this.message = message; 80 | } 81 | 82 | public T getData() { 83 | return data; 84 | } 85 | 86 | public void setData(T data) { 87 | this.data = data; 88 | } 89 | 90 | public Boolean getSuccess() { 91 | return success; 92 | } 93 | 94 | public Integer getStatus() { 95 | return status; 96 | } 97 | 98 | public void setStatus(Integer status) { 99 | this.status = status; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/pace/pace.css: -------------------------------------------------------------------------------- 1 | .pace { 2 | -webkit-pointer-events: none; 3 | pointer-events: none; 4 | -webkit-user-select: none; 5 | -moz-user-select: none; 6 | user-select: none; 7 | } 8 | 9 | .pace-inactive { 10 | display: none; 11 | } 12 | 13 | .pace .pace-progress { 14 | background: #fff; 15 | position: fixed; 16 | z-index: 2000; 17 | top: 0; 18 | right: 100%; 19 | width: 100%; 20 | height: 2px; 21 | } 22 | 23 | .pace .pace-progress-inner { 24 | display: block; 25 | position: absolute; 26 | right: 0px; 27 | width: 100px; 28 | height: 100%; 29 | box-shadow: 0 0 10px #fff, 0 0 5px #fff; 30 | opacity: 1.0; 31 | -webkit-transform: rotate(3deg) translate(0px, -4px); 32 | -moz-transform: rotate(3deg) translate(0px, -4px); 33 | -ms-transform: rotate(3deg) translate(0px, -4px); 34 | -o-transform: rotate(3deg) translate(0px, -4px); 35 | transform: rotate(3deg) translate(0px, -4px); 36 | } 37 | 38 | .pace .pace-activity { 39 | display: block; 40 | position: fixed; 41 | z-index: 2000; 42 | top: 15px; 43 | right: 50%; 44 | width: 14px; 45 | height: 14px; 46 | border: solid 2px transparent; 47 | border-top-color: #fff; 48 | border-left-color: #fff; 49 | border-radius: 10px; 50 | -webkit-animation: pace-spinner 400ms linear infinite; 51 | -moz-animation: pace-spinner 400ms linear infinite; 52 | -ms-animation: pace-spinner 400ms linear infinite; 53 | -o-animation: pace-spinner 400ms linear infinite; 54 | animation: pace-spinner 400ms linear infinite; 55 | } 56 | 57 | @media (max-width: 767px) { 58 | .pace .pace-activity { 59 | top: 15px; 60 | right: 15px; 61 | width: 14px; 62 | height: 14px; 63 | } 64 | } 65 | 66 | @-webkit-keyframes pace-spinner { 67 | 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 68 | 100% { -webkit-transform: rotate(360deg); transform: rotate(360deg); } 69 | } 70 | @-moz-keyframes pace-spinner { 71 | 0% { -moz-transform: rotate(0deg); transform: rotate(0deg); } 72 | 100% { -moz-transform: rotate(360deg); transform: rotate(360deg); } 73 | } 74 | @-o-keyframes pace-spinner { 75 | 0% { -o-transform: rotate(0deg); transform: rotate(0deg); } 76 | 100% { -o-transform: rotate(360deg); transform: rotate(360deg); } 77 | } 78 | @-ms-keyframes pace-spinner { 79 | 0% { -ms-transform: rotate(0deg); transform: rotate(0deg); } 80 | 100% { -ms-transform: rotate(360deg); transform: rotate(360deg); } 81 | } 82 | @keyframes pace-spinner { 83 | 0% { transform: rotate(0deg); transform: rotate(0deg); } 84 | 100% { transform: rotate(360deg); transform: rotate(360deg); } 85 | -------------------------------------------------------------------------------- /src/main/resources/static/front/scss/_default_tabs.scss: -------------------------------------------------------------------------------- 1 | /* 2 | default tabs 3 | */ 4 | .tt-tab-wrapper{ 5 | .nav-tabs{ 6 | display: flex; 7 | flex-direction: row; 8 | flex-wrap: wrap; 9 | justify-content: flex-start; 10 | align-content: flex-start; 11 | align-items: center; 12 | list-style: none; 13 | margin: 0; 14 | padding: 0; 15 | border-bottom:1px solid $border; 16 | .nav-item{ 17 | a{ 18 | color: $default_color2; 19 | display: inline-block; 20 | transition: color 0.2s linear; 21 | font-weight: 500; 22 | span{ 23 | padding-top: 22px; 24 | padding-bottom: 23px; 25 | display: inline-block; 26 | position: relative; 27 | letter-spacing: 0.01em; 28 | &:after{ 29 | content: ''; 30 | display: block; 31 | position: absolute; 32 | background-color: transparent; 33 | bottom: 0; 34 | left: 0; 35 | width: 100%; 36 | height: 2px; 37 | transition: background-color 0.2s linear; 38 | } 39 | } 40 | &:hover{ 41 | color: $default_color; 42 | } 43 | &.active{ 44 | color: $default_color; 45 | span{ 46 | &:after{ 47 | background-color: $default_color; 48 | } 49 | } 50 | } 51 | } 52 | &:first-child{ 53 | a{ 54 | padding-left: 0; 55 | } 56 | } 57 | &:last-child{ 58 | a{ 59 | padding-right: 0; 60 | } 61 | } 62 | @media (min-width: 992px){ 63 | padding-left: 14px; 64 | padding-right: 14px; 65 | &:first-child{ 66 | padding-left: 0; 67 | } 68 | } 69 | @media (max-width: 991px){ 70 | padding-left: 12px; 71 | padding-right: 12px; 72 | &:first-child{ 73 | padding-left: 0; 74 | } 75 | } 76 | @media (max-width: 767px){ 77 | &.tt-hide-md{ 78 | display: none; 79 | } 80 | } 81 | @media (max-width: 575px){ 82 | &.tt-hide-xs{ 83 | display: none; 84 | } 85 | } 86 | } 87 | } 88 | .tab-content{ 89 | .tab-pane{ 90 | &:not(.active){ 91 | display: none; 92 | } 93 | &:not(.tt-indent-none){ 94 | padding: 40px 0 0 0; 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/resources/static/front/scss/_editor.scss: -------------------------------------------------------------------------------- 1 | /* 2 | Editor 3 | */ 4 | .pt-editor{ 5 | padding-top: 36px; 6 | .pt-title{ 7 | color: $default_color2; 8 | font-weight: 600; 9 | font-size: 16px; 10 | line-height: 26px; 11 | margin: 0; 12 | padding: 0 0 12px 0; 13 | letter-spacing: 0.01em; 14 | } 15 | .pt-row{ 16 | padding-bottom: 12px; 17 | [class^=col]{ 18 | &:first-child{ 19 | padding-left: 0; 20 | } 21 | &:last-child{ 22 | padding-right: 0; 23 | } 24 | } 25 | @media (min-width: 576px){ 26 | display: flex; 27 | flex-direction: row; 28 | flex-wrap: nowrap; 29 | justify-content: space-between; 30 | align-content: stretch; 31 | align-items: flex-start; 32 | } 33 | @media (max-width: 575px){ 34 | display: flex; 35 | flex-direction: column; 36 | flex-wrap: wrap; 37 | } 38 | > *:not(:last-child){ 39 | margin-bottom: 10px; 40 | } 41 | } 42 | textarea{ 43 | height:169px; 44 | } 45 | .btn-custom{ 46 | min-width: 97px; 47 | } 48 | .pt-edit-btn{ 49 | padding: 0 0 8px 0; 50 | margin: 0 0 0 -14px; 51 | list-style: none; 52 | .btn-icon{ 53 | background: none; 54 | outline: none; 55 | border: none; 56 | padding: 5px 14px; 57 | svg{ 58 | width: 18px; 59 | height: 18px; 60 | transition:fill $speed linear 61 | } 62 | & + .btn-icon{ 63 | margin-right: 22px; 64 | } 65 | &:hover{ 66 | fill: $default_color; 67 | } 68 | } 69 | hr{ 70 | display: none; 71 | } 72 | } 73 | .btn-width-lg{ 74 | min-width: 160px; 75 | } 76 | @media (min-width: 576px){ 77 | .pt-edit-btn{ 78 | display: flex; 79 | flex-direction: row; 80 | flex-wrap: wrap; 81 | justify-content: flex-start; 82 | align-content: stretch; 83 | align-items: flex-start; 84 | } 85 | } 86 | @media (max-width: 575px){ 87 | .pt-edit-btn{ 88 | display: flex; 89 | flex-direction: row; 90 | flex-wrap: wrap; 91 | justify-content: space-around; 92 | align-content: stretch; 93 | align-items: center; 94 | padding-bottom: 3px; 95 | li.hr{ 96 | display: block; 97 | width: 100%; 98 | height: 1px; 99 | margin:12px 0 10px; 100 | background-color: $border; 101 | } 102 | .btn-icon{ 103 | padding-left: 8px; 104 | padding-right: 8px; 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/com/example/forum/util/FileUtil.java: -------------------------------------------------------------------------------- 1 | package com.example.forum.util; 2 | 3 | import cn.hutool.core.date.DateUtil; 4 | import cn.hutool.core.text.StrBuilder; 5 | import com.example.forum.exception.MyBusinessException; 6 | import org.springframework.web.multipart.MultipartFile; 7 | 8 | import java.io.File; 9 | import java.io.IOException; 10 | 11 | /** 12 | * @author 言曌 13 | * @date 2020/3/8 5:45 下午 14 | */ 15 | public class FileUtil { 16 | 17 | /** 18 | * 上传文件返回URL 19 | * 20 | * @param file 21 | * @return 22 | */ 23 | public static String upload(MultipartFile file) { 24 | String path = ""; 25 | try { 26 | //用户目录 27 | final StrBuilder uploadPath = new StrBuilder(System.getProperties().getProperty("user.home")); 28 | uploadPath.append("/sens/upload/" + DateUtil.thisYear()).append("/").append(DateUtil.thisMonth() + 1).append("/"); 29 | final File mediaPath = new File(uploadPath.toString()); 30 | if (!mediaPath.exists()) { 31 | if (!mediaPath.mkdirs()) { 32 | throw new MyBusinessException("上传失败"); 33 | } 34 | } 35 | 36 | //不带后缀 37 | String nameWithOutSuffix = file.getOriginalFilename().substring(0, file.getOriginalFilename().lastIndexOf('.')).replaceAll(" ", "_").replaceAll(",", ""); 38 | 39 | //文件后缀 40 | final String fileSuffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf('.') + 1); 41 | 42 | //带后缀 43 | String fileName = nameWithOutSuffix + "." + fileSuffix; 44 | 45 | //判断文件名是否已存在 46 | File descFile = new File(mediaPath.getAbsoluteFile(), fileName); 47 | int i = 1; 48 | while (descFile.exists()) { 49 | nameWithOutSuffix = nameWithOutSuffix + "(" + i + ")"; 50 | descFile = new File(mediaPath.getAbsoluteFile(), nameWithOutSuffix + "." + fileSuffix); 51 | i++; 52 | } 53 | file.transferTo(descFile); 54 | 55 | //文件原路径 56 | final StrBuilder fullPath = new StrBuilder(mediaPath.getAbsolutePath()); 57 | fullPath.append("/"); 58 | fullPath.append(nameWithOutSuffix + "." + fileSuffix); 59 | 60 | //压缩文件路径 61 | final StrBuilder fullSmallPath = new StrBuilder(mediaPath.getAbsolutePath()); 62 | fullSmallPath.append("/"); 63 | fullSmallPath.append(nameWithOutSuffix); 64 | fullSmallPath.append("_small."); 65 | fullSmallPath.append(fileSuffix); 66 | 67 | //映射路径 68 | final StrBuilder filePath = new StrBuilder("/upload/"); 69 | filePath.append(DateUtil.thisYear()); 70 | filePath.append("/"); 71 | filePath.append(DateUtil.thisMonth() + 1); 72 | filePath.append("/"); 73 | filePath.append(nameWithOutSuffix + "." + fileSuffix); 74 | path = filePath.toString(); 75 | 76 | } catch (IOException e) { 77 | e.printStackTrace(); 78 | } 79 | return path; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/resources/static/front/scss/_categories_list.scss: -------------------------------------------------------------------------------- 1 | .tt-categories-title{ 2 | display: flex; 3 | flex-direction: row; 4 | flex-wrap: wrap; 5 | justify-content: space-between; 6 | align-content: stretch; 7 | align-items: center; 8 | padding-bottom: 16px; 9 | border-bottom: 1px solid #e2e7ea; 10 | margin-bottom: 30px; 11 | .tt-title{ 12 | color: $default_color_title; 13 | font-size: 18px; 14 | font-weight: 500; 15 | letter-spacing: 0.01em; 16 | } 17 | .tt-search{ 18 | width: 255px; 19 | } 20 | } 21 | .tt-categories-list{ 22 | .tt-item{ 23 | background-color: #ffffff; 24 | border-radius:3px; 25 | .tt-item-header{ 26 | display: flex; 27 | flex-direction: row; 28 | flex-wrap: nowrap; 29 | justify-content: space-between; 30 | align-content: center; 31 | align-items: center; 32 | padding-bottom: 21px; 33 | .tt-title{ 34 | color: $default_color2; 35 | font-size: 14px; 36 | line-height: 26px; 37 | margin-bottom: 0; 38 | font-weight: 400; 39 | a{ 40 | color: $default_color2; 41 | transition: color .2s linear; 42 | &:hover{ 43 | color: $default_color; 44 | } 45 | } 46 | } 47 | } 48 | .tt-item-layout{ 49 | .tt-title{ 50 | font-size: 14px; 51 | line-height: 26px; 52 | color: $default_color2; 53 | font-weight: 700; 54 | margin-bottom: 6px; 55 | } 56 | .innerwrapper{ 57 | letter-spacing: 0.01em; 58 | &:not(:first-child){ 59 | margin-top: 12px; 60 | } 61 | } 62 | .tt-btn-icon{ 63 | margin-top: 19px; 64 | padding-left: 0; 65 | padding-bottom: 0; 66 | height: auto; 67 | svg{ 68 | width: 20px; 69 | height: 18px; 70 | } 71 | } 72 | } 73 | } 74 | @media (min-width: 576px){ 75 | margin-top: -30px; 76 | .tt-item{ 77 | padding: 30px; 78 | margin-top: 30px; 79 | } 80 | } 81 | @media (max-width: 575px){ 82 | margin-top: -20px; 83 | .tt-item{ 84 | padding: 20px; 85 | margin-top: 20px; 86 | .tt-item-header{ 87 | padding-bottom: 11px; 88 | } 89 | .tt-item-layout{ 90 | .innerwrapper{ 91 | &:not(:first-child){ 92 | margin-top: 2px; 93 | } 94 | } 95 | .tt-btn-icon{ 96 | margin-top: 16px; 97 | } 98 | } 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 博主开发的其他论坛或博客项目全部在这里 2 | **[https://liuyanzhao.com/shop.html?k=论坛](https://liuyanzhao.com/shop.html?k=论坛)**
3 | **[https://liuyanzhao.com/shop.html?k=博客](https://liuyanzhao.com/shop.html?k=博客)**
4 | - ------------------------------------------------------------------------------- 5 | 基于 SpringBoot + MyBatis + Thymeleaf 实现的博客/论坛系统。 6 |
7 | 详细地址:[https://liuyanzhao.com/10206.html](https://liuyanzhao.com/10206.html) 8 | 同版论坛地址[https://github.com/saysky/forum](https://github.com/saysky/forum) 9 | 10 | 11 | ## 主要技术 12 | 1. SpringBoot2.x 13 | 2. MyBatis-Plus3.x 14 | 3. Thymeleaf 15 | 4. MySQL5.x (建议5.6+)或 MySQL8.x 16 | 5. Maven 3.5 17 | 18 | ## 本地启动教程 19 | 1. 使用 IDEA/Eclipse/MyEclipse 导入项目,推荐使用IDEA 20 | 2. 安装 Lombok 插件,因为项目中大量使用 `@Data` 和 `@Sl4j` 如果不安装将无法自动生成 `getter/setter` 方法 和 `log` 对象。 21 | - IDEA 安装Lombok方法是在 Plugins 里安装 22 | - Eclipse/MyEclipse 可以参考 `https://blog.csdn.net/yiyijianxian/article/details/80156910` 23 | 3. 创建数据库 `forum`, 导入 `forum.sql` 文件; 修改 application.yml 的数据库用户名和密码。 24 | 4. 以 Maven 方法导入项目,等依赖下载完毕,点击 Application.java 运行即可 25 | 5. 配置SMTP发送邮件 26 | 只有配置了SMTP,才能发送邮件,主要用于找回密码功能 27 | 1. 获取授权码
28 | - 推荐使用QQ邮箱,打开QQ邮箱,点击【设置】-> 【账户】,往下翻,找到【POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV服务】。 29 | 开启【POP3/SMTP服务】,点击旁边的【启用】按钮。 点击【生成授权码】获取授权码,如 授权码如vtvhcjsacnuutest。 30 | ![image](img/smtp.png) 31 | 32 | - 修改配置文件
33 | 修改 `application.yml` 34 | ```$xslt 35 | mail: 36 | smtp: 37 | host: smtp.qq.com 38 | username: 你的QQ邮箱如847064370@qq.com 39 | password: 授权码如vtvhcjsacnuutest 40 | from: 41 | name: 网站名称如言曌博客 42 | ``` 43 | 44 | 45 | ## 项目结构和表结构 46 | 1. 项目结构 47 | - 后端 springboot + mybatis 48 | - 前端 thymeleaf + bootstrap + jQuery 49 | ![image](img/project.png) 50 | 51 | 2. 表结构 52 | - 表名称按顺序分别如下: 53 | - 分类表、评论表、友情链接表、权限表、文章表、文章和分类关联表、文章和标签关联表、角色表、角色和权限关联表、标签表、用户表、用户和角色关联表
54 | ![image](img/db.png) 55 | 56 | ## 运行预览 57 | 备注:目前这个示例是学车论坛系统,只需要全局替换"贴子"->"文章"、"回帖"->"文章"。然后在后台修改文章标题和内容即可。 58 | 59 | 1. 首页 60 | ![image](img/home.png) 61 | 62 | 2. 详情页 63 | ![image](img/post-details.png) 64 | 65 | 3. 详情页2 66 | - 可以发布文字、图片和视频 67 | - 有访问量、点赞和评论功能 68 | ![image](img/post-details2.png) 69 | 70 | 4. 评论 71 | - 可以评论和对评论回复 72 | - 可以对评论进行点赞和点踩 73 | ![image](img/comment.png) 74 | 75 | 5. 分类列表 76 | - 所有的分类 77 | ![image](img/category.png) 78 | 79 | 6. 活跃用户列表 80 | - 活跃的用户 81 | ![image](img/user.png) 82 | 83 | 7. 登录 84 | - 支持记住密码 85 | ![image](img/login.png) 86 | 87 | 8. 注册 88 | - 会校验用户名和邮箱是否注册 89 | ![image](img/register.png) 90 | 91 | 9. 忘记密码 92 | - 如果用户名和邮箱匹配,会给该邮箱发送新的密码 93 | ![image](img/forget.png) 94 | 95 | 10. 管理员后台 96 | - 管理员后台功能较多 97 | - 可以控制所有的文章和所有的评论 98 | - 可以管理分类、标签、用户、权限和角色等 99 | ![image](img/admin-admin.png) 100 | 101 | 11. 普通用户后台 102 | - 普通用户可以管理自己的文章 103 | - 可以看到自己发布的评论和收到的评论 104 | ![image](img/admin-user.png) 105 | 106 | 12. 文章编辑页面 107 | - 富文本编辑器 108 | - 可以上传图片和视频 109 | ![image](img/post-edit.png) 110 | 111 | 13. 角色授权页面 112 | - 修改角色信息 113 | - 树状绑定权限 114 | ![image](img/role-edit.png) 115 | 116 | 其他的就不一一截图了 117 | 118 | ## 完整版 119 | 目前前端HTML文件没有开源,但是后端代码依然值得学习。需要完整版本的,联系本人: 120 | 微信 847064370 121 | 122 | 123 | -------------------------------------------------------------------------------- /src/main/resources/mapper/RoleMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 24 | 25 | 26 | DELETE 27 | FROM 28 | user_role_ref 29 | WHERE 30 | user_id = #{value} 31 | AND del_flag = 0 32 | 33 | 34 | 46 | 47 | 66 | 67 | 80 | 81 | 91 | 92 | 107 | 108 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /src/main/resources/static/front/scss/_popup_settings.scss: -------------------------------------------------------------------------------- 1 | .tt-popup-settings{ 2 | position: fixed; 3 | display: block; 4 | margin: 0; 5 | padding: 70px 30px; 6 | background: #ffffff; 7 | z-index: 14; 8 | top: 0; 9 | right: -150%; 10 | height: 100%; 11 | max-width: inherit; 12 | text-align: left; 13 | transition: right 0.3s linear; 14 | &.column-open{ 15 | right: 0; 16 | } 17 | .tt-btn-col-close{ 18 | margin-bottom: 25px; 19 | position: relative; 20 | .tt-icon-title{ 21 | pointer-events: none; 22 | position: relative; 23 | top: -1px; 24 | svg{ 25 | width: 17px; 26 | height: 18px; 27 | fill:#666f74; 28 | } 29 | margin-right: 30px; 30 | } 31 | .tt-icon-close{ 32 | pointer-events: none; 33 | position: absolute; 34 | right: 0px; 35 | top: 35px; 36 | svg{ 37 | width: 13px; 38 | height: 12px; 39 | fill:#70797d; 40 | transition: fill $speed linear; 41 | } 42 | } 43 | a{ 44 | display: block; 45 | color: #303344; 46 | font-weight: 600; 47 | font-size: 16px; 48 | line-height: 26px; 49 | padding: 36px 0 22px; 50 | border-bottom: 1px solid $border02; 51 | transition: color $speed linear; 52 | &:hover{ 53 | color: $default_color; 54 | .tt-icon-close{ 55 | svg{ 56 | fill:$default_color2; 57 | } 58 | } 59 | } 60 | } 61 | } 62 | .tt-form-upload{ 63 | padding-bottom: 27px; 64 | .tt-avatar{ 65 | svg{ 66 | width: 40px; 67 | height: 40px; 68 | } 69 | } 70 | .btn{ 71 | min-width: 170px; 72 | } 73 | } 74 | .form-default{ 75 | .form-group{ 76 | margin-bottom: 21px; 77 | label:not(.error){ 78 | padding-bottom: 7px; 79 | + .checkbox-group{ 80 | margin-top: 7px; 81 | } 82 | } 83 | } 84 | .btn{ 85 | min-width: 160px; 86 | } 87 | } 88 | .checkbox-group{ 89 | + .checkbox-group{ 90 | margin-top: 8px; 91 | } 92 | } 93 | textarea.form-control{ 94 | height: 129px; 95 | padding: 19px 12px 12px 28px; 96 | } 97 | 98 | @media (min-width: 451px){ 99 | width: 410px; 100 | } 101 | @media (max-width: 450px){ 102 | width: 320px; 103 | padding-left: 20px; 104 | padding-right: 20px; 105 | .form-group{ 106 | .btn{ 107 | width: 100%; 108 | } 109 | } 110 | } 111 | } 112 | .modal-filter{ 113 | background: rgba(0, 0, 0, 0.5); 114 | position: fixed; 115 | top: 0; 116 | right: 0; 117 | overflow: hidden; 118 | width: 100%; 119 | height: 100%; 120 | z-index: 11; 121 | display: block; 122 | opacity: 0; 123 | transition: background 0.2s linear; 124 | } 125 | -------------------------------------------------------------------------------- /src/main/resources/static/plugins/froala-editor/css/plugins/code_view.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * froala_editor v2.9.1 (https://www.froala.com/wysiwyg-editor) 3 | * License https://froala.com/wysiwyg-editor/terms/ 4 | * Copyright 2014-2018 Froala Labs 5 | */ 6 | 7 | .clearfix::after { 8 | clear: both; 9 | display: block; 10 | content: ""; 11 | height: 0; 12 | } 13 | .hide-by-clipping { 14 | position: absolute; 15 | width: 1px; 16 | height: 1px; 17 | padding: 0; 18 | margin: -1px; 19 | overflow: hidden; 20 | clip: rect(0, 0, 0, 0); 21 | border: 0; 22 | } 23 | textarea.fr-code { 24 | display: none; 25 | width: 100%; 26 | resize: none; 27 | -moz-resize: none; 28 | -webkit-resize: none; 29 | -webkit-box-sizing: border-box; 30 | -moz-box-sizing: border-box; 31 | box-sizing: border-box; 32 | border: none; 33 | padding: 10px; 34 | margin: 0px; 35 | font-family: "Courier New", monospace; 36 | font-size: 14px; 37 | background: #ffffff; 38 | color: #000000; 39 | outline: none; 40 | } 41 | .fr-box.fr-rtl textarea.fr-code { 42 | direction: rtl; 43 | } 44 | .fr-box .CodeMirror { 45 | display: none; 46 | } 47 | .fr-box.fr-code-view textarea.fr-code { 48 | display: block; 49 | } 50 | .fr-box.fr-code-view.fr-inline { 51 | -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16); 52 | -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16); 53 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16); 54 | } 55 | .fr-box.fr-code-view .fr-element, 56 | .fr-box.fr-code-view .fr-placeholder, 57 | .fr-box.fr-code-view .fr-iframe { 58 | display: none; 59 | } 60 | .fr-box.fr-code-view .CodeMirror { 61 | display: block; 62 | } 63 | .fr-box.fr-inline.fr-code-view .fr-command.fr-btn.html-switch { 64 | display: block; 65 | } 66 | .fr-box.fr-inline .fr-command.fr-btn.html-switch { 67 | position: absolute; 68 | top: 0; 69 | right: 0; 70 | -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16); 71 | -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16); 72 | box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 1px 1px rgba(0, 0, 0, 0.16); 73 | display: none; 74 | background: #ffffff; 75 | color: #222222; 76 | -moz-outline: 0; 77 | outline: 0; 78 | border: 0; 79 | line-height: 1; 80 | cursor: pointer; 81 | text-align: left; 82 | padding: 12px 12px; 83 | -webkit-transition: background 0.2s ease 0s; 84 | -moz-transition: background 0.2s ease 0s; 85 | -ms-transition: background 0.2s ease 0s; 86 | -o-transition: background 0.2s ease 0s; 87 | border-radius: 0; 88 | -moz-border-radius: 0; 89 | -webkit-border-radius: 0; 90 | -moz-background-clip: padding; 91 | -webkit-background-clip: padding-box; 92 | background-clip: padding-box; 93 | z-index: 2; 94 | -webkit-box-sizing: border-box; 95 | -moz-box-sizing: border-box; 96 | box-sizing: border-box; 97 | text-decoration: none; 98 | user-select: none; 99 | -o-user-select: none; 100 | -moz-user-select: none; 101 | -khtml-user-select: none; 102 | -webkit-user-select: none; 103 | -ms-user-select: none; 104 | } 105 | .fr-box.fr-inline .fr-command.fr-btn.html-switch i { 106 | font-size: 14px; 107 | width: 14px; 108 | text-align: center; 109 | } 110 | .fr-box.fr-inline .fr-command.fr-btn.html-switch.fr-desktop:hover { 111 | background: #ebebeb; 112 | } 113 | --------------------------------------------------------------------------------