From 310e2efa89f21ba16221d4102b9f6c1c3253ad44 Mon Sep 17 00:00:00 2001 From: wol <1293433164@qq.com> Date: Sun, 22 Dec 2024 21:59:37 +0800 Subject: [PATCH] 1311 --- weblog-springboot-134/pom.xml | 244 ++++++++++++ .../weblog-module-admin/.gitignore | 33 ++ .../weblog-module-admin/pom.xml | 78 ++++ .../admin/config/Knife4jAdminConfig.java | 53 +++ .../weblog/admin/config/MinioConfig.java | 27 ++ .../weblog/admin/config/MinioProperties.java | 21 ++ .../weblog/admin/config/TheadPoolConfig.java | 31 ++ .../admin/config/WebSecurityConfig.java | 65 ++++ .../controller/AdminArticleController.java | 69 ++++ .../AdminBlogSettingsController.java | 48 +++ .../controller/AdminCategoryController.java | 67 ++++ .../admin/controller/AdminFileController.java | 38 ++ .../admin/controller/AdminTagController.java | 72 ++++ .../admin/controller/AdminUserController.java | 46 +++ .../admin/convert/ArticleDetailConvert.java | 28 ++ .../admin/convert/BlogSettingsConvert.java | 36 ++ .../weblog/admin/event/ReadArticleEvent.java | 25 ++ .../subscriber/ReadArticleSubscriber.java | 39 ++ .../weblog/admin/model/package-info.java | 7 + .../model/vo/article/DeleteArticleReqVO.java | 26 ++ .../vo/article/FindArticleDetailReqVO.java | 30 ++ .../vo/article/FindArticleDetailRspVO.java | 57 +++ .../vo/article/FindArticlePageListReqVO.java | 40 ++ .../vo/article/FindArticlePageListRspVO.java | 42 +++ .../model/vo/article/PublishArticleReqVO.java | 45 +++ .../model/vo/article/UpdateArticleReqVO.java | 48 +++ .../blogsettings/FindBlogSettingsRspVO.java | 40 ++ .../blogsettings/UpdateBlogSettingsReqVO.java | 47 +++ .../model/vo/category/AddCategoryReqVO.java | 30 ++ .../vo/category/DeleteCategoryReqVO.java | 29 ++ .../category/FindCategoryPageListReqVO.java | 43 +++ .../category/FindCategoryPageListRspVO.java | 40 ++ .../admin/model/vo/file/UploadFileRspVO.java | 27 ++ .../admin/model/vo/tag/AddTagReqVO.java | 30 ++ .../admin/model/vo/tag/DeleteTagReqVO.java | 27 ++ .../model/vo/tag/FindTagPageListReqVO.java | 40 ++ .../model/vo/tag/FindTagPageListRspVO.java | 37 ++ .../admin/model/vo/tag/SearchTagsReqVO.java | 27 ++ .../model/vo/user/FindUserInfoRspVO.java | 28 ++ .../vo/user/UpdateAdminUserPasswordReqVO.java | 31 ++ .../quanxiaoha/weblog/admin/package-info.java | 7 + .../admin/service/AdminArticleService.java | 47 +++ .../service/AdminBlogSettingsService.java | 26 ++ .../admin/service/AdminCategoryService.java | 45 +++ .../admin/service/AdminFileService.java | 20 + .../weblog/admin/service/AdminTagService.java | 51 +++ .../admin/service/AdminUserService.java | 25 ++ .../service/impl/AdminArticleServiceImpl.java | 347 ++++++++++++++++++ .../impl/AdminBlogSettingsServiceImpl.java | 61 +++ .../impl/AdminCategoryServiceImpl.java | 158 ++++++++ .../service/impl/AdminFileServiceImpl.java | 55 +++ .../service/impl/AdminTagServiceImpl.java | 178 +++++++++ .../service/impl/AdminUserServiceImpl.java | 62 ++++ .../weblog/admin/service/package-info.java | 7 + .../weblog/admin/utils/MinioUtil.java | 75 ++++ .../WeblogModuleAdminApplicationTests.java | 25 ++ .../weblog-module-common/.gitignore | 33 ++ .../weblog-module-common/pom.xml | 69 ++++ .../weblog/common/aspect/ApiOperationLog.java | 17 + .../common/aspect/ApiOperationLogAspect.java | 102 +++++ .../common/config/InsertBatchMapper.java | 19 + .../common/config/InsertBatchSqlInjector.java | 26 ++ .../weblog/common/config/JacksonConfig.java | 65 ++++ .../common/config/MybatisPlusConfig.java | 37 ++ .../domain/dos/ArticleCategoryRelDO.java | 30 ++ .../common/domain/dos/ArticleContentDO.java | 31 ++ .../weblog/common/domain/dos/ArticleDO.java | 42 +++ .../common/domain/dos/ArticleTagRelDO.java | 30 ++ .../common/domain/dos/BlogSettingsDO.java | 46 +++ .../weblog/common/domain/dos/CategoryDO.java | 36 ++ .../weblog/common/domain/dos/TagDO.java | 36 ++ .../weblog/common/domain/dos/UserDO.java | 38 ++ .../weblog/common/domain/dos/UserRoleDO.java | 20 + .../mapper/ArticleCategoryRelMapper.java | 67 ++++ .../domain/mapper/ArticleContentMapper.java | 44 +++ .../common/domain/mapper/ArticleMapper.java | 98 +++++ .../domain/mapper/ArticleTagRelMapper.java | 67 ++++ .../domain/mapper/BlogSettingsMapper.java | 18 + .../common/domain/mapper/CategoryMapper.java | 54 +++ .../common/domain/mapper/TagMapper.java | 68 ++++ .../common/domain/mapper/UserMapper.java | 33 ++ .../common/domain/mapper/UserRoleMapper.java | 27 ++ .../weblog/common/domain/package-info.java | 7 + .../weblog/common/enums/ResponseCodeEnum.java | 43 +++ .../exception/BaseExceptionInterface.java | 13 + .../weblog/common/exception/BizException.java | 24 ++ .../exception/GlobalExceptionHandler.java | 92 +++++ .../weblog/common/model/BasePageQuery.java | 21 ++ .../weblog/common/model/package-info.java | 7 + .../weblog/common/model/vo/SelectRspVO.java | 28 ++ .../weblog/common/package-info.java | 7 + .../weblog/common/utils/JsonUtil.java | 31 ++ .../weblog/common/utils/PageResponse.java | 60 +++ .../weblog/common/utils/Response.java | 77 ++++ .../WeblogModuleCommonApplicationTests.java | 23 ++ .../weblog-module-jwt/.gitignore | 33 ++ .../weblog-module-jwt/pom.xml | 74 ++++ .../JwtAuthenticationSecurityConfig.java | 58 +++ .../jwt/config/PasswordEncoderConfig.java | 27 ++ .../UsernameOrPasswordNullException.java | 19 + .../jwt/filter/JwtAuthenticationFilter.java | 59 +++ .../jwt/filter/TokenAuthenticationFilter.java | 102 +++++ .../jwt/handler/RestAccessDeniedHandler.java | 32 ++ .../handler/RestAuthenticationEntryPoint.java | 38 ++ .../RestAuthenticationFailureHandler.java | 43 +++ .../RestAuthenticationSuccessHandler.java | 47 +++ .../weblog/jwt/model/LoginRspVO.java | 25 ++ .../jwt/service/UserDetailServiceImpl.java | 63 ++++ .../weblog/jwt/utils/JwtTokenHelper.java | 146 ++++++++ .../weblog/jwt/utils/ResultUtil.java | 73 ++++ .../jwt/WeblogModuleJwtApplicationTests.java | 16 + weblog-springboot-134/weblog-web/.gitignore | 33 ++ weblog-springboot-134/weblog-web/pom.xml | 104 ++++++ .../weblog/web/WeblogWebApplication.java | 15 + .../weblog/web/config/Knife4jConfig.java | 53 +++ .../web/controller/ArchiveController.java | 35 ++ .../web/controller/ArticleController.java | 45 +++ .../controller/BlogSettingsController.java | 34 ++ .../web/controller/CategoryController.java | 44 +++ .../weblog/web/controller/TagController.java | 44 +++ .../weblog/web/convert/ArticleConvert.java | 57 +++ .../web/convert/BlogSettingsConvert.java | 28 ++ .../weblog/web/markdown/MarkdownHelper.java | 70 ++++ .../NofollowLinkAttributeProvider.java | 34 ++ .../markdown/renderer/ImageNodeRenderer.java | 107 ++++++ .../FindArchiveArticlePageListReqVO.java | 18 + .../FindArchiveArticlePageListRspVO.java | 29 ++ .../vo/archive/FindArchiveArticleRspVO.java | 34 ++ .../vo/article/FindArticleDetailReqVO.java | 25 ++ .../vo/article/FindArticleDetailRspVO.java | 59 +++ .../FindIndexArticlePageListReqVO.java | 18 + .../FindIndexArticlePageListRspVO.java | 38 ++ .../vo/article/FindPreNextArticleRspVO.java | 28 ++ .../FindBlogSettingsDetailRspVO.java | 28 ++ .../FindCategoryArticlePageListReqVO.java | 29 ++ .../FindCategoryArticlePageListRspVO.java | 29 ++ .../vo/category/FindCategoryListRspVO.java | 21 ++ .../vo/tag/FindTagArticlePageListReqVO.java | 29 ++ .../vo/tag/FindTagArticlePageListRspVO.java | 29 ++ .../web/model/vo/tag/FindTagListRspVO.java | 21 ++ .../weblog/web/service/ArchiveService.java | 19 + .../weblog/web/service/ArticleService.java | 28 ++ .../web/service/BlogSettingsService.java | 17 + .../weblog/web/service/CategoryService.java | 25 ++ .../weblog/web/service/TagService.java | 25 ++ .../web/service/impl/ArchiveServiceImpl.java | 70 ++++ .../web/service/impl/ArticleServiceImpl.java | 217 +++++++++++ .../service/impl/BlogSettingsServiceImpl.java | 40 ++ .../web/service/impl/CategoryServiceImpl.java | 115 ++++++ .../web/service/impl/TagServiceImpl.java | 116 ++++++ .../src/main/resources/application-dev.yml | 27 ++ .../src/main/resources/application-prod.yml | 35 ++ .../src/main/resources/application.yml | 18 + .../src/main/resources/logback-weblog.xml | 46 +++ .../src/main/resources/spy.properties | 24 ++ .../weblog/web/WeblogWebApplicationTests.java | 86 +++++ 156 files changed, 7467 insertions(+) create mode 100644 weblog-springboot-134/pom.xml create mode 100644 weblog-springboot-134/weblog-module-admin/.gitignore create mode 100644 weblog-springboot-134/weblog-module-admin/pom.xml create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/config/Knife4jAdminConfig.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/config/MinioConfig.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/config/MinioProperties.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/config/TheadPoolConfig.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/config/WebSecurityConfig.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminArticleController.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminBlogSettingsController.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminCategoryController.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminFileController.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminTagController.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminUserController.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/convert/ArticleDetailConvert.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/convert/BlogSettingsConvert.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/event/ReadArticleEvent.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/event/subscriber/ReadArticleSubscriber.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/package-info.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/DeleteArticleReqVO.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/FindArticleDetailReqVO.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/FindArticleDetailRspVO.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/FindArticlePageListReqVO.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/FindArticlePageListRspVO.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/PublishArticleReqVO.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/UpdateArticleReqVO.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/blogsettings/FindBlogSettingsRspVO.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/blogsettings/UpdateBlogSettingsReqVO.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/category/AddCategoryReqVO.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/category/DeleteCategoryReqVO.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/category/FindCategoryPageListReqVO.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/category/FindCategoryPageListRspVO.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/file/UploadFileRspVO.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/tag/AddTagReqVO.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/tag/DeleteTagReqVO.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/tag/FindTagPageListReqVO.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/tag/FindTagPageListRspVO.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/tag/SearchTagsReqVO.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/user/FindUserInfoRspVO.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/user/UpdateAdminUserPasswordReqVO.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/package-info.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminArticleService.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminBlogSettingsService.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminCategoryService.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminFileService.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminTagService.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminUserService.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminArticleServiceImpl.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminBlogSettingsServiceImpl.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminCategoryServiceImpl.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminFileServiceImpl.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminTagServiceImpl.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminUserServiceImpl.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/package-info.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/utils/MinioUtil.java create mode 100644 weblog-springboot-134/weblog-module-admin/src/test/java/com/quanxiaoha/weblog/admin/WeblogModuleAdminApplicationTests.java create mode 100644 weblog-springboot-134/weblog-module-common/.gitignore create mode 100644 weblog-springboot-134/weblog-module-common/pom.xml create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/aspect/ApiOperationLog.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/aspect/ApiOperationLogAspect.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/config/InsertBatchMapper.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/config/InsertBatchSqlInjector.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/config/JacksonConfig.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/config/MybatisPlusConfig.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/ArticleCategoryRelDO.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/ArticleContentDO.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/ArticleDO.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/ArticleTagRelDO.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/BlogSettingsDO.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/CategoryDO.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/TagDO.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/UserDO.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/UserRoleDO.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/ArticleCategoryRelMapper.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/ArticleContentMapper.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/ArticleMapper.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/ArticleTagRelMapper.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/BlogSettingsMapper.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/CategoryMapper.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/TagMapper.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/UserMapper.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/UserRoleMapper.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/package-info.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/enums/ResponseCodeEnum.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/exception/BaseExceptionInterface.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/exception/BizException.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/exception/GlobalExceptionHandler.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/model/BasePageQuery.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/model/package-info.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/model/vo/SelectRspVO.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/package-info.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/utils/JsonUtil.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/utils/PageResponse.java create mode 100644 weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/utils/Response.java create mode 100644 weblog-springboot-134/weblog-module-common/src/test/java/com/quanxiaoha/weblog/common/WeblogModuleCommonApplicationTests.java create mode 100644 weblog-springboot-134/weblog-module-jwt/.gitignore create mode 100644 weblog-springboot-134/weblog-module-jwt/pom.xml create mode 100644 weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/config/JwtAuthenticationSecurityConfig.java create mode 100644 weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/config/PasswordEncoderConfig.java create mode 100644 weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/exception/UsernameOrPasswordNullException.java create mode 100644 weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/filter/JwtAuthenticationFilter.java create mode 100644 weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/filter/TokenAuthenticationFilter.java create mode 100644 weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/handler/RestAccessDeniedHandler.java create mode 100644 weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/handler/RestAuthenticationEntryPoint.java create mode 100644 weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/handler/RestAuthenticationFailureHandler.java create mode 100644 weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/handler/RestAuthenticationSuccessHandler.java create mode 100644 weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/model/LoginRspVO.java create mode 100644 weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/service/UserDetailServiceImpl.java create mode 100644 weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/utils/JwtTokenHelper.java create mode 100644 weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/utils/ResultUtil.java create mode 100644 weblog-springboot-134/weblog-module-jwt/src/test/java/com/quanxiaoha/weblog/jwt/WeblogModuleJwtApplicationTests.java create mode 100644 weblog-springboot-134/weblog-web/.gitignore create mode 100644 weblog-springboot-134/weblog-web/pom.xml create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/WeblogWebApplication.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/config/Knife4jConfig.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/controller/ArchiveController.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/controller/ArticleController.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/controller/BlogSettingsController.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/controller/CategoryController.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/controller/TagController.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/convert/ArticleConvert.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/convert/BlogSettingsConvert.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/markdown/MarkdownHelper.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/markdown/provider/NofollowLinkAttributeProvider.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/markdown/renderer/ImageNodeRenderer.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/archive/FindArchiveArticlePageListReqVO.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/archive/FindArchiveArticlePageListRspVO.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/archive/FindArchiveArticleRspVO.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/article/FindArticleDetailReqVO.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/article/FindArticleDetailRspVO.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/article/FindIndexArticlePageListReqVO.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/article/FindIndexArticlePageListRspVO.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/article/FindPreNextArticleRspVO.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/blogsettings/FindBlogSettingsDetailRspVO.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/category/FindCategoryArticlePageListReqVO.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/category/FindCategoryArticlePageListRspVO.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/category/FindCategoryListRspVO.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/tag/FindTagArticlePageListReqVO.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/tag/FindTagArticlePageListRspVO.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/tag/FindTagListRspVO.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/ArchiveService.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/ArticleService.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/BlogSettingsService.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/CategoryService.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/TagService.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/impl/ArchiveServiceImpl.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/impl/ArticleServiceImpl.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/impl/BlogSettingsServiceImpl.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/impl/CategoryServiceImpl.java create mode 100644 weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/impl/TagServiceImpl.java create mode 100644 weblog-springboot-134/weblog-web/src/main/resources/application-dev.yml create mode 100644 weblog-springboot-134/weblog-web/src/main/resources/application-prod.yml create mode 100644 weblog-springboot-134/weblog-web/src/main/resources/application.yml create mode 100644 weblog-springboot-134/weblog-web/src/main/resources/logback-weblog.xml create mode 100644 weblog-springboot-134/weblog-web/src/main/resources/spy.properties create mode 100644 weblog-springboot-134/weblog-web/src/test/java/com/quanxiaoha/weblog/web/WeblogWebApplicationTests.java diff --git a/weblog-springboot-134/pom.xml b/weblog-springboot-134/pom.xml new file mode 100644 index 0000000..96aa6a1 --- /dev/null +++ b/weblog-springboot-134/pom.xml @@ -0,0 +1,244 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + + 2.6.3 + + + + com.quanxiaoha + weblog-springboot + ${revision} + weblog-springboot + + 前后端分离博客 Weblog By 犬小哈 + + + pom + + + + + weblog-web + + weblog-module-admin + + weblog-module-common + + weblog-module-jwt + + + + + + + 0.0.1-SNAPSHOT + 1.8 + UTF-8 + + ${java.version} + ${java.version} + + + 1.18.28 + 31.1-jre + 3.12.0 + 2.15.2 + 4.3.0 + 3.5.2 + 3.9.1 + 0.11.2 + 8.2.1 + 1.5.5.Final + 0.20.0 + + + + + + + com.quanxiaoha + weblog-module-admin + ${revision} + + + + com.quanxiaoha + weblog-module-common + ${revision} + + + + com.quanxiaoha + weblog-module-jwt + ${revision} + + + + + com.google.guava + guava + ${guava.version} + + + + org.apache.commons + commons-lang3 + ${commons-lang3.version} + + + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + + com.fasterxml.jackson.core + jackson-core + ${jackson.version} + + + + + + com.github.xiaoymin + knife4j-openapi2-spring-boot-starter + ${knife4j.version} + + + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus.version} + + + + p6spy + p6spy + ${p6spy.version} + + + + + io.jsonwebtoken + jjwt-api + ${jjwt.version} + + + io.jsonwebtoken + jjwt-impl + ${jjwt.version} + + + io.jsonwebtoken + jjwt-jackson + ${jjwt.version} + + + + + io.minio + minio + ${minio.version} + + + + + org.mapstruct + mapstruct + ${mapstruct.version} + + + + + org.commonmark + commonmark + ${commonmark.version} + + + + org.commonmark + commonmark-ext-gfm-tables + ${commonmark.version} + + + + org.commonmark + commonmark-ext-heading-anchor + ${commonmark.version} + + + + org.commonmark + commonmark-ext-image-attributes + ${commonmark.version} + + + + org.commonmark + commonmark-ext-task-list-items + ${commonmark.version} + + + + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + + + org.projectlombok + lombok + ${lombok.version} + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + + + + + + + + + + aliyunmaven + aliyun + https://maven.aliyun.com/repository/public + + + diff --git a/weblog-springboot-134/weblog-module-admin/.gitignore b/weblog-springboot-134/weblog-module-admin/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/weblog-springboot-134/weblog-module-admin/pom.xml b/weblog-springboot-134/weblog-module-admin/pom.xml new file mode 100644 index 0000000..536651a --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/pom.xml @@ -0,0 +1,78 @@ + + + 4.0.0 + + + com.quanxiaoha + weblog-springboot + ${revision} + + + com.quanxiaoha + weblog-module-admin + weblog-module-admin + weblog-admin (负责管理后台相关功能) + + + + com.quanxiaoha + weblog-module-common + + + + com.quanxiaoha + weblog-module-jwt + + + + + org.projectlombok + lombok + true + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + com.github.xiaoymin + knife4j-openapi2-spring-boot-starter + + + + + org.springframework.boot + spring-boot-starter-security + + + + + jakarta.validation + jakarta.validation-api + + + org.hibernate.validator + hibernate-validator + + + + + io.minio + minio + + + + org.mapstruct + mapstruct + + + + + + \ No newline at end of file diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/config/Knife4jAdminConfig.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/config/Knife4jAdminConfig.java new file mode 100644 index 0000000..3c2b2da --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/config/Knife4jAdminConfig.java @@ -0,0 +1,53 @@ +package com.quanxiaoha.weblog.admin.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-16 7:53 + * @description: Knife4j 配置 + **/ +@Configuration +@EnableSwagger2WebMvc +@Profile("dev") // 只在 dev 环境中开启 +public class Knife4jAdminConfig { + + @Bean("adminApi") + public Docket createApiDoc() { + Docket docket = new Docket(DocumentationType.SWAGGER_2) + .apiInfo(buildApiInfo()) + // 分组名称 + .groupName("Admin 后台接口") + .select() + // 这里指定 Controller 扫描包路径 + .apis(RequestHandlerSelectors.basePackage("com.quanxiaoha.weblog.admin.controller")) + .paths(PathSelectors.any()) + .build(); + return docket; + } + + /** + * 构建 API 信息 + * @return + */ + private ApiInfo buildApiInfo() { + return new ApiInfoBuilder() + .title("Weblog 博客 Admin 后台接口文档") // 标题 + .description("Weblog 是一款由 Spring Boot + Vue 3.2 + Vite 4.3 开发的前后端分离博客。") // 描述 + .termsOfServiceUrl("https://www.quanxiaoha.com/") // API 服务条款 + .contact(new Contact("犬小哈", "https://www.quanxiaoha.com", "871361652@qq.com")) // 联系人 + .version("1.0") // 版本号 + .build(); + } +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/config/MinioConfig.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/config/MinioConfig.java new file mode 100644 index 0000000..1083984 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/config/MinioConfig.java @@ -0,0 +1,27 @@ +package com.quanxiaoha.weblog.admin.config; + +import io.minio.MinioClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-05-11 8:49 + * @description: TODO + **/ +@Configuration +public class MinioConfig { + @Autowired + private MinioProperties minioProperties; + + @Bean + public MinioClient minioClient() { + // 构建 Minio 客户端 + return MinioClient.builder() + .endpoint(minioProperties.getEndpoint()) + .credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey()) + .build(); + } +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/config/MinioProperties.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/config/MinioProperties.java new file mode 100644 index 0000000..c72af04 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/config/MinioProperties.java @@ -0,0 +1,21 @@ +package com.quanxiaoha.weblog.admin.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-05-11 8:49 + * @description: TODO + **/ +@ConfigurationProperties(prefix = "minio") +@Component +@Data +public class MinioProperties { + private String endpoint; + private String accessKey; + private String secretKey; + private String bucketName; +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/config/TheadPoolConfig.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/config/TheadPoolConfig.java new file mode 100644 index 0000000..d72683a --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/config/TheadPoolConfig.java @@ -0,0 +1,31 @@ +package com.quanxiaoha.weblog.admin.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.Executor; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023/11/9 10:47 + * @description: 线程池配置 + **/ +@Configuration +@EnableAsync +public class TheadPoolConfig { + + @Bean("threadPoolTaskExecutor") + public Executor threadPoolTaskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(10); // 核心线程数 + executor.setMaxPoolSize(20); // 最大线程数 + executor.setQueueCapacity(100); // 队列容量 + executor.setThreadNamePrefix("WeblogThreadPool-"); // 线程名前缀 + executor.initialize(); + return executor; + } + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/config/WebSecurityConfig.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/config/WebSecurityConfig.java new file mode 100644 index 0000000..f5fbac2 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/config/WebSecurityConfig.java @@ -0,0 +1,65 @@ +package com.quanxiaoha.weblog.admin.config; + +import com.quanxiaoha.weblog.jwt.config.JwtAuthenticationSecurityConfig; +import com.quanxiaoha.weblog.jwt.filter.TokenAuthenticationFilter; +import com.quanxiaoha.weblog.jwt.handler.RestAccessDeniedHandler; +import com.quanxiaoha.weblog.jwt.handler.RestAuthenticationEntryPoint; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-23 15:48 + * @description: Spring Security 配置类 + **/ +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + + @Autowired + private JwtAuthenticationSecurityConfig jwtAuthenticationSecurityConfig; + @Autowired + private RestAuthenticationEntryPoint authEntryPoint; + @Autowired + private RestAccessDeniedHandler deniedHandler; + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.csrf().disable(). // 禁用 csrf + formLogin().disable() // 禁用表单登录 + .apply(jwtAuthenticationSecurityConfig) // 设置用户登录认证相关配置 + .and() + .authorizeHttpRequests() + .mvcMatchers("/admin/**").authenticated() // 认证所有以 /admin 为前缀的 URL 资源 + .anyRequest().permitAll() // 其他都需要放行,无需认证 + .and() + .httpBasic().authenticationEntryPoint(authEntryPoint) // 处理用户未登录访问受保护的资源的情况 + .and() + .exceptionHandling().accessDeniedHandler(deniedHandler) // 处理登录成功后访问受保护的资源,但是权限不够的情况 + .and() + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 前后端分离,无需创建会话 + .and() + .addFilterBefore(tokenAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class) // 将 Token 校验过滤器添加到用户认证过滤器之前 + ; + } + + /** + * Token 校验过滤器 + * @return + */ + @Bean + public TokenAuthenticationFilter tokenAuthenticationFilter() { + return new TokenAuthenticationFilter(); + } + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminArticleController.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminArticleController.java new file mode 100644 index 0000000..f31c462 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminArticleController.java @@ -0,0 +1,69 @@ +package com.quanxiaoha.weblog.admin.controller; + +import com.quanxiaoha.weblog.admin.model.vo.article.*; +import com.quanxiaoha.weblog.admin.service.AdminArticleService; +import com.quanxiaoha.weblog.common.aspect.ApiOperationLog; +import com.quanxiaoha.weblog.common.utils.Response; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:01 + * @description: 文章模块 + **/ +@RestController +@RequestMapping("/admin/article") +@Api(tags = "Admin 文章模块") +public class AdminArticleController { + + @Autowired + private AdminArticleService articleService; + + @PostMapping("/publish") + @ApiOperation(value = "文章发布") + @ApiOperationLog(description = "文章发布") + @PreAuthorize("hasRole('ROLE_ADMIN')") + public Response publishArticle(@RequestBody @Validated PublishArticleReqVO publishArticleReqVO) { + return articleService.publishArticle(publishArticleReqVO); + } + + @PostMapping("/delete") + @ApiOperation(value = "文章删除") + @ApiOperationLog(description = "文章删除") + @PreAuthorize("hasRole('ROLE_ADMIN')") + public Response deleteArticle(@RequestBody @Validated DeleteArticleReqVO deleteArticleReqVO) { + return articleService.deleteArticle(deleteArticleReqVO); + } + + @PostMapping("/list") + @ApiOperation(value = "查询文章分页数据") + @ApiOperationLog(description = "查询文章分页数据") + public Response findArticlePageList(@RequestBody @Validated FindArticlePageListReqVO findArticlePageListReqVO) { + return articleService.findArticlePageList(findArticlePageListReqVO); + } + + @PostMapping("/detail") + @ApiOperation(value = "查询文章详情") + @ApiOperationLog(description = "查询文章详情") + public Response findArticleDetail(@RequestBody @Validated FindArticleDetailReqVO findArticlePageListReqVO) { + return articleService.findArticleDetail(findArticlePageListReqVO); + } + + @PostMapping("/update") + @ApiOperation(value = "更新文章") + @ApiOperationLog(description = "更新文章") + @PreAuthorize("hasRole('ROLE_ADMIN')") + public Response updateArticle(@RequestBody @Validated UpdateArticleReqVO updateArticleReqVO) { + return articleService.updateArticle(updateArticleReqVO); + } + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminBlogSettingsController.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminBlogSettingsController.java new file mode 100644 index 0000000..17fd3be --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminBlogSettingsController.java @@ -0,0 +1,48 @@ +package com.quanxiaoha.weblog.admin.controller; + +import com.quanxiaoha.weblog.admin.model.vo.blogsettings.UpdateBlogSettingsReqVO; +import com.quanxiaoha.weblog.admin.model.vo.user.UpdateAdminUserPasswordReqVO; +import com.quanxiaoha.weblog.admin.service.AdminBlogSettingsService; +import com.quanxiaoha.weblog.admin.service.AdminUserService; +import com.quanxiaoha.weblog.common.aspect.ApiOperationLog; +import com.quanxiaoha.weblog.common.utils.Response; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:01 + * @description: 博客设置 + **/ +@RestController +@RequestMapping("/admin/blog/settings") +@Api(tags = "Admin 博客设置模块") +public class AdminBlogSettingsController { + + @Autowired + private AdminBlogSettingsService blogSettingsService; + + @PostMapping("/update") + @ApiOperation(value = "博客基础信息修改") + @ApiOperationLog(description = "博客基础信息修改") + @PreAuthorize("hasRole('ROLE_ADMIN')") + public Response updateBlogSettings(@RequestBody @Validated UpdateBlogSettingsReqVO updateBlogSettingsReqVO) { + return blogSettingsService.updateBlogSettings(updateBlogSettingsReqVO); + } + + @PostMapping("/detail") + @ApiOperation(value = "获取博客设置详情") + @ApiOperationLog(description = "获取博客设置详情") + public Response findDetail() { + return blogSettingsService.findDetail(); + } + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminCategoryController.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminCategoryController.java new file mode 100644 index 0000000..74225d8 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminCategoryController.java @@ -0,0 +1,67 @@ +package com.quanxiaoha.weblog.admin.controller; + +import com.quanxiaoha.weblog.admin.model.vo.category.AddCategoryReqVO; +import com.quanxiaoha.weblog.admin.model.vo.category.DeleteCategoryReqVO; +import com.quanxiaoha.weblog.admin.model.vo.category.FindCategoryPageListReqVO; +import com.quanxiaoha.weblog.admin.model.vo.user.UpdateAdminUserPasswordReqVO; +import com.quanxiaoha.weblog.admin.service.AdminCategoryService; +import com.quanxiaoha.weblog.admin.service.AdminUserService; +import com.quanxiaoha.weblog.common.aspect.ApiOperationLog; +import com.quanxiaoha.weblog.common.utils.PageResponse; +import com.quanxiaoha.weblog.common.utils.Response; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:01 + * @description: 分类 + **/ +@RestController +@RequestMapping("/admin/category") +@Api(tags = "Admin 分类模块") +public class AdminCategoryController { + + @Autowired + private AdminCategoryService categoryService; + + @PostMapping("/add") + @ApiOperation(value = "添加分类") + @ApiOperationLog(description = "添加分类") + @PreAuthorize("hasRole('ROLE_ADMIN')") + public Response addCategory(@RequestBody @Validated AddCategoryReqVO addCategoryReqVO) { + return categoryService.addCategory(addCategoryReqVO); + } + + @PostMapping("/list") + @ApiOperation(value = "分类分页数据获取") + @ApiOperationLog(description = "分类分页数据获取") + public PageResponse findCategoryPageList(@RequestBody @Validated FindCategoryPageListReqVO findCategoryPageListReqVO) { + return categoryService.findCategoryPageList(findCategoryPageListReqVO); + } + + @PostMapping("/delete") + @ApiOperation(value = "删除分类") + @ApiOperationLog(description = "删除分类") + @PreAuthorize("hasRole('ROLE_ADMIN')") + public Response deleteCategory(@RequestBody @Validated DeleteCategoryReqVO deleteCategoryReqVO) { + return categoryService.deleteCategory(deleteCategoryReqVO); + } + + @PostMapping("/select/list") + @ApiOperation(value = "分类 Select 下拉列表数据获取") + @ApiOperationLog(description = "分类 Select 下拉列表数据获取") + public Response findCategorySelectList() { + return categoryService.findCategorySelectList(); + } + + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminFileController.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminFileController.java new file mode 100644 index 0000000..9caccb5 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminFileController.java @@ -0,0 +1,38 @@ +package com.quanxiaoha.weblog.admin.controller; + +import com.quanxiaoha.weblog.admin.service.AdminFileService; +import com.quanxiaoha.weblog.common.aspect.ApiOperationLog; +import com.quanxiaoha.weblog.common.utils.Response; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:01 + * @description: 文件模块 + **/ +@RestController +@RequestMapping("/admin") +@Api(tags = "Admin 文件模块") +public class AdminFileController { + + @Autowired + private AdminFileService fileService; + + @PostMapping("/file/upload") + @ApiOperation(value = "文件上传") + @ApiOperationLog(description = "文件上传") + @PreAuthorize("hasRole('ROLE_ADMIN')") + public Response uploadFile(@RequestParam MultipartFile file) { + return fileService.uploadFile(file); + } + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminTagController.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminTagController.java new file mode 100644 index 0000000..51cd37d --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminTagController.java @@ -0,0 +1,72 @@ +package com.quanxiaoha.weblog.admin.controller; + +import com.quanxiaoha.weblog.admin.model.vo.tag.AddTagReqVO; +import com.quanxiaoha.weblog.admin.model.vo.tag.DeleteTagReqVO; +import com.quanxiaoha.weblog.admin.model.vo.tag.FindTagPageListReqVO; +import com.quanxiaoha.weblog.admin.model.vo.tag.SearchTagsReqVO; +import com.quanxiaoha.weblog.admin.service.AdminTagService; +import com.quanxiaoha.weblog.common.aspect.ApiOperationLog; +import com.quanxiaoha.weblog.common.utils.PageResponse; +import com.quanxiaoha.weblog.common.utils.Response; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:01 + * @description: 标签模块 + **/ +@RestController +@RequestMapping("/admin/tag") +@Api(tags = "Admin 标签模块") +public class AdminTagController { + + @Autowired + private AdminTagService tagService; + + @PostMapping("/add") + @ApiOperation(value = "添加标签") + @ApiOperationLog(description = "添加标签") + @PreAuthorize("hasRole('ROLE_ADMIN')") + public Response addTags(@RequestBody @Validated AddTagReqVO addTagReqVO) { + return tagService.addTags(addTagReqVO); + } + + @PostMapping("/list") + @ApiOperation(value = "标签分页数据获取") + @ApiOperationLog(description = "标签分页数据获取") + public PageResponse findTagPageList(@RequestBody @Validated FindTagPageListReqVO findTagPageListReqVO) { + return tagService.findTagPageList(findTagPageListReqVO); + } + + @PostMapping("/delete") + @ApiOperation(value = "删除标签") + @ApiOperationLog(description = "删除标签") + @PreAuthorize("hasRole('ROLE_ADMIN')") + public Response deleteTag(@RequestBody @Validated DeleteTagReqVO deleteTagReqVO) { + return tagService.deleteTag(deleteTagReqVO); + } + + @PostMapping("/search") + @ApiOperation(value = "标签模糊查询") + @ApiOperationLog(description = "标签模糊查询") + public Response searchTags(@RequestBody @Validated SearchTagsReqVO searchTagsReqVO) { + return tagService.searchTags(searchTagsReqVO); + } + + @PostMapping("/select/list") + @ApiOperation(value = "查询标签 Select 列表数据") + @ApiOperationLog(description = "查询标签 Select 列表数据") + public Response findTagSelectList() { + return tagService.findTagSelectList(); + } + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminUserController.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminUserController.java new file mode 100644 index 0000000..65d49ac --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminUserController.java @@ -0,0 +1,46 @@ +package com.quanxiaoha.weblog.admin.controller; + +import com.quanxiaoha.weblog.admin.model.vo.user.UpdateAdminUserPasswordReqVO; +import com.quanxiaoha.weblog.admin.service.AdminUserService; +import com.quanxiaoha.weblog.common.aspect.ApiOperationLog; +import com.quanxiaoha.weblog.common.utils.Response; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:01 + * @description: 用户 + **/ +@RestController +@RequestMapping("/admin") +@Api(tags = "Admin 用户模块") +public class AdminUserController { + + @Autowired + private AdminUserService userService; + + @PostMapping("/password/update") + @ApiOperation(value = "修改用户密码") + @ApiOperationLog(description = "修改用户密码") + @PreAuthorize("hasRole('ROLE_ADMIN')") + public Response updatePassword(@RequestBody @Validated UpdateAdminUserPasswordReqVO updateAdminUserPasswordReqVO) { + return userService.updatePassword(updateAdminUserPasswordReqVO); + } + + @PostMapping("/user/info") + @ApiOperation(value = "获取用户信息") + @ApiOperationLog(description = "获取用户信息") + public Response findUserInfo() { + return userService.findUserInfo(); + } + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/convert/ArticleDetailConvert.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/convert/ArticleDetailConvert.java new file mode 100644 index 0000000..1b86aa2 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/convert/ArticleDetailConvert.java @@ -0,0 +1,28 @@ +package com.quanxiaoha.weblog.admin.convert; + +import com.quanxiaoha.weblog.admin.model.vo.article.FindArticleDetailRspVO; +import com.quanxiaoha.weblog.common.domain.dos.ArticleDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023/10/8 14:57 + * @description: 文章详情转换 + **/ +@Mapper +public interface ArticleDetailConvert { + /** + * 初始化 convert 实例 + */ + ArticleDetailConvert INSTANCE = Mappers.getMapper(ArticleDetailConvert.class); + + /** + * 将 DO 转化为 VO + * @param bean + * @return + */ + FindArticleDetailRspVO convertDO2VO(ArticleDO bean); + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/convert/BlogSettingsConvert.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/convert/BlogSettingsConvert.java new file mode 100644 index 0000000..2dcec5f --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/convert/BlogSettingsConvert.java @@ -0,0 +1,36 @@ +package com.quanxiaoha.weblog.admin.convert; + +import com.quanxiaoha.weblog.admin.model.vo.blogsettings.FindBlogSettingsRspVO; +import com.quanxiaoha.weblog.admin.model.vo.blogsettings.UpdateBlogSettingsReqVO; +import com.quanxiaoha.weblog.common.domain.dos.BlogSettingsDO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023/10/8 14:57 + * @description: 博客设置转换 + **/ +@Mapper +public interface BlogSettingsConvert { + /** + * 初始化 convert 实例 + */ + BlogSettingsConvert INSTANCE = Mappers.getMapper(BlogSettingsConvert.class); + + /** + * 将 VO 转化为 DO + * @param bean + * @return + */ + BlogSettingsDO convertVO2DO(UpdateBlogSettingsReqVO bean); + + /** + * 将 DO 转化为 VO + * @param bean + * @return + */ + FindBlogSettingsRspVO convertDO2VO(BlogSettingsDO bean); + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/event/ReadArticleEvent.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/event/ReadArticleEvent.java new file mode 100644 index 0000000..b3d70c0 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/event/ReadArticleEvent.java @@ -0,0 +1,25 @@ +package com.quanxiaoha.weblog.admin.event; + +import lombok.Getter; +import org.springframework.context.ApplicationEvent; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023/11/9 10:05 + * @description: 文章被阅读事件 + **/ + +@Getter +public class ReadArticleEvent extends ApplicationEvent { + + /** + * 文章 ID + */ + private Long articleId; + + public ReadArticleEvent(Object source, Long articleId) { + super(source); + this.articleId = articleId; + } +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/event/subscriber/ReadArticleSubscriber.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/event/subscriber/ReadArticleSubscriber.java new file mode 100644 index 0000000..e68eeba --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/event/subscriber/ReadArticleSubscriber.java @@ -0,0 +1,39 @@ +package com.quanxiaoha.weblog.admin.event.subscriber; + +import com.quanxiaoha.weblog.admin.event.ReadArticleEvent; +import com.quanxiaoha.weblog.common.domain.mapper.ArticleMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023/11/9 10:08 + * @description: TODO + **/ +@Component +@Slf4j +public class ReadArticleSubscriber implements ApplicationListener { + + + @Autowired + private ArticleMapper articleMapper; + + @Override + @Async("threadPoolTaskExecutor") + public void onApplicationEvent(ReadArticleEvent event) { + // 在这里处理收到的事件,可以是任何逻辑操作 + Long articleId = event.getArticleId(); + + // 获取当前线程名称 + String threadName = Thread.currentThread().getName(); + + log.info("==> threadName: {}", threadName); + log.info("==> 文章阅读事件消费成功,articleId: {}", articleId); + + articleMapper.increaseReadNum(articleId); + } +} \ No newline at end of file diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/package-info.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/package-info.java new file mode 100644 index 0000000..e84aac5 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/package-info.java @@ -0,0 +1,7 @@ +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: TODO + **/ +package com.quanxiaoha.weblog.admin.model; \ No newline at end of file diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/DeleteArticleReqVO.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/DeleteArticleReqVO.java new file mode 100644 index 0000000..fdcc840 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/DeleteArticleReqVO.java @@ -0,0 +1,26 @@ +package com.quanxiaoha.weblog.admin.model.vo.article; + +import io.swagger.annotations.ApiModel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 删除文章 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@ApiModel(value = "删除文章 VO") +public class DeleteArticleReqVO { + + @NotNull(message = "文章 ID 不能为空") + private Long id; +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/FindArticleDetailReqVO.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/FindArticleDetailReqVO.java new file mode 100644 index 0000000..e82fdba --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/FindArticleDetailReqVO.java @@ -0,0 +1,30 @@ +package com.quanxiaoha.weblog.admin.model.vo.article; + +import io.swagger.annotations.ApiModel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 文章详情 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@ApiModel(value = "查询文章详情入参 VO") +public class FindArticleDetailReqVO { + + /** + * 文章 ID + */ + @NotNull(message = "文章 ID 不能为空") + private Long id; + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/FindArticleDetailRspVO.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/FindArticleDetailRspVO.java new file mode 100644 index 0000000..76709d1 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/FindArticleDetailRspVO.java @@ -0,0 +1,57 @@ +package com.quanxiaoha.weblog.admin.model.vo.article; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 文章详情 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class FindArticleDetailRspVO { + + /** + * 文章 ID + */ + private Long id; + + /** + * 文章标题 + */ + private String title; + + /** + * 文章封面 + */ + private String cover; + + /** + * 文章内容 + */ + private String content; + + /** + * 分类 ID + */ + private Long categoryId; + + /** + * 标签 ID 集合 + */ + private List tagIds; + + /** + * 摘要 + */ + private String summary; + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/FindArticlePageListReqVO.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/FindArticlePageListReqVO.java new file mode 100644 index 0000000..189ead5 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/FindArticlePageListReqVO.java @@ -0,0 +1,40 @@ +package com.quanxiaoha.weblog.admin.model.vo.article; + +import com.quanxiaoha.weblog.common.model.BasePageQuery; +import io.swagger.annotations.ApiModel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 文章分页 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@ApiModel(value = "查询文章分页数据入参 VO") +public class FindArticlePageListReqVO extends BasePageQuery { + + /** + * 文章标题 + */ + private String title; + + /** + * 发布的起始日期 + */ + private LocalDate startDate; + + /** + * 发布的结束日期 + */ + private LocalDate endDate; + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/FindArticlePageListRspVO.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/FindArticlePageListRspVO.java new file mode 100644 index 0000000..a791eac --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/FindArticlePageListRspVO.java @@ -0,0 +1,42 @@ +package com.quanxiaoha.weblog.admin.model.vo.article; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 文章分页 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class FindArticlePageListRspVO { + + /** + * 文章 ID + */ + private Long id; + + /** + * 文章标题 + */ + private String title; + + /** + * 文章封面 + */ + private String cover; + + /** + * 发布时间 + */ + private LocalDateTime createTime; + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/PublishArticleReqVO.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/PublishArticleReqVO.java new file mode 100644 index 0000000..357ab07 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/PublishArticleReqVO.java @@ -0,0 +1,45 @@ +package com.quanxiaoha.weblog.admin.model.vo.article; + +import io.swagger.annotations.ApiModel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; +import org.hibernate.validator.constraints.NotEmpty; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 文章发布 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@ApiModel(value = "发布文章 VO") +public class PublishArticleReqVO { + + @NotBlank(message = "文章标题不能为空") + @Length(min = 1, max = 40, message = "文章标题字数需大于 1 小于 40") + private String title; + + @NotBlank(message = "文章内容不能为空") + private String content; + + @NotBlank(message = "文章封面不能为空") + private String cover; + + private String summary; + + @NotNull(message = "文章分类不能为空") + private Long categoryId; + + @NotEmpty(message = "文章标签不能为空") + private List tags; +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/UpdateArticleReqVO.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/UpdateArticleReqVO.java new file mode 100644 index 0000000..951ef98 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/article/UpdateArticleReqVO.java @@ -0,0 +1,48 @@ +package com.quanxiaoha.weblog.admin.model.vo.article; + +import io.swagger.annotations.ApiModel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; +import org.hibernate.validator.constraints.NotEmpty; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 更新文章 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@ApiModel(value = "更新文章 VO") +public class UpdateArticleReqVO { + + @NotNull(message = "文章 ID 不能为空") + private Long id; + + @NotBlank(message = "文章标题不能为空") + @Length(min = 1, max = 40, message = "文章标题字数需大于 1 小于 40") + private String title; + + @NotBlank(message = "文章内容不能为空") + private String content; + + @NotBlank(message = "文章封面不能为空") + private String cover; + + private String summary; + + @NotNull(message = "文章分类不能为空") + private Long categoryId; + + @NotEmpty(message = "文章标签不能为空") + private List tags; +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/blogsettings/FindBlogSettingsRspVO.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/blogsettings/FindBlogSettingsRspVO.java new file mode 100644 index 0000000..0d15bd2 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/blogsettings/FindBlogSettingsRspVO.java @@ -0,0 +1,40 @@ +package com.quanxiaoha.weblog.admin.model.vo.blogsettings; + +import io.swagger.annotations.ApiModel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 博客基础信息 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class FindBlogSettingsRspVO { + + private String logo; + + private String name; + + private String author; + + private String introduction; + + private String avatar; + + private String githubHomepage; + + private String csdnHomepage; + + private String giteeHomepage; + + private String zhihuHomepage; +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/blogsettings/UpdateBlogSettingsReqVO.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/blogsettings/UpdateBlogSettingsReqVO.java new file mode 100644 index 0000000..eb0c9de --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/blogsettings/UpdateBlogSettingsReqVO.java @@ -0,0 +1,47 @@ +package com.quanxiaoha.weblog.admin.model.vo.blogsettings; + +import io.swagger.annotations.ApiModel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotBlank; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 博客基础信息修改 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@ApiModel(value = " 博客基础信息修改 VO") +public class UpdateBlogSettingsReqVO { + + @NotBlank(message = "博客 LOGO 不能为空") + private String logo; + + @NotBlank(message = "博客名称不能为空") + private String name; + + @NotBlank(message = "博客作者不能为空") + private String author; + + @NotBlank(message = "博客介绍语不能为空") + private String introduction; + + @NotBlank(message = "博客头像不能为空") + private String avatar; + + private String githubHomepage; + + private String csdnHomepage; + + private String giteeHomepage; + + private String zhihuHomepage; +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/category/AddCategoryReqVO.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/category/AddCategoryReqVO.java new file mode 100644 index 0000000..ff250e1 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/category/AddCategoryReqVO.java @@ -0,0 +1,30 @@ +package com.quanxiaoha.weblog.admin.model.vo.category; + +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiModel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotBlank; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 分类新增 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@ApiModel(value = "添加分类 VO") +public class AddCategoryReqVO { + + @NotBlank(message = "分类名称不能为空") + @Length(min = 1, max = 20, message = "分类名称字数限制 1 ~ 20 之间") + private String name; + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/category/DeleteCategoryReqVO.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/category/DeleteCategoryReqVO.java new file mode 100644 index 0000000..26e55a1 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/category/DeleteCategoryReqVO.java @@ -0,0 +1,29 @@ +package com.quanxiaoha.weblog.admin.model.vo.category; + +import io.swagger.annotations.ApiModel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 删除分类 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@ApiModel(value = "删除分类 VO") +public class DeleteCategoryReqVO { + + @NotNull(message = "分类 ID 不能为空") + private Long id; + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/category/FindCategoryPageListReqVO.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/category/FindCategoryPageListReqVO.java new file mode 100644 index 0000000..3a26532 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/category/FindCategoryPageListReqVO.java @@ -0,0 +1,43 @@ +package com.quanxiaoha.weblog.admin.model.vo.category; + +import com.quanxiaoha.weblog.common.model.BasePageQuery; +import io.swagger.annotations.ApiModel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotBlank; +import java.time.LocalDate; +import java.time.LocalDateTime; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 分类分页 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@ApiModel(value = "查询分类分页数据入参 VO") +public class FindCategoryPageListReqVO extends BasePageQuery { + + /** + * 分类名称 + */ + private String name; + + /** + * 创建的起始日期 + */ + private LocalDate startDate; + + /** + * 创建的结束日期 + */ + private LocalDate endDate; + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/category/FindCategoryPageListRspVO.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/category/FindCategoryPageListRspVO.java new file mode 100644 index 0000000..b49b5a0 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/category/FindCategoryPageListRspVO.java @@ -0,0 +1,40 @@ +package com.quanxiaoha.weblog.admin.model.vo.category; + +import com.quanxiaoha.weblog.common.model.BasePageQuery; +import io.swagger.annotations.ApiModel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 分类分页 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class FindCategoryPageListRspVO { + + /** + * 分类 ID + */ + private Long id; + + /** + * 分类名称 + */ + private String name; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/file/UploadFileRspVO.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/file/UploadFileRspVO.java new file mode 100644 index 0000000..683baaf --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/file/UploadFileRspVO.java @@ -0,0 +1,27 @@ +package com.quanxiaoha.weblog.admin.model.vo.file; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 上传文件 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class UploadFileRspVO { + + /** + * 文件的访问链接 + */ + private String url; + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/tag/AddTagReqVO.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/tag/AddTagReqVO.java new file mode 100644 index 0000000..36d0a48 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/tag/AddTagReqVO.java @@ -0,0 +1,30 @@ +package com.quanxiaoha.weblog.admin.model.vo.tag; + +import io.swagger.annotations.ApiModel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; +import java.util.List; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 标签新增 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@ApiModel(value = "添加标签 VO") +public class AddTagReqVO { + + @NotEmpty(message = "标签集合不能为空") + private List tags; + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/tag/DeleteTagReqVO.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/tag/DeleteTagReqVO.java new file mode 100644 index 0000000..acd775d --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/tag/DeleteTagReqVO.java @@ -0,0 +1,27 @@ +package com.quanxiaoha.weblog.admin.model.vo.tag; + +import io.swagger.annotations.ApiModel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 删除标签 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@ApiModel(value = "删除标签 VO") +public class DeleteTagReqVO { + + @NotNull(message = "标签 ID 不能为空") + private Long id; + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/tag/FindTagPageListReqVO.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/tag/FindTagPageListReqVO.java new file mode 100644 index 0000000..12951fc --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/tag/FindTagPageListReqVO.java @@ -0,0 +1,40 @@ +package com.quanxiaoha.weblog.admin.model.vo.tag; + +import com.quanxiaoha.weblog.common.model.BasePageQuery; +import io.swagger.annotations.ApiModel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 标签分页 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@ApiModel(value = "查询标签分页数据入参 VO") +public class FindTagPageListReqVO extends BasePageQuery { + + /** + * 标签名称 + */ + private String name; + + /** + * 创建的起始日期 + */ + private LocalDate startDate; + + /** + * 创建的结束日期 + */ + private LocalDate endDate; + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/tag/FindTagPageListRspVO.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/tag/FindTagPageListRspVO.java new file mode 100644 index 0000000..6835637 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/tag/FindTagPageListRspVO.java @@ -0,0 +1,37 @@ +package com.quanxiaoha.weblog.admin.model.vo.tag; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 标签分页 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class FindTagPageListRspVO { + + /** + * 标签 ID + */ + private Long id; + + /** + * 标签名称 + */ + private String name; + + /** + * 创建时间 + */ + private LocalDateTime createTime; + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/tag/SearchTagsReqVO.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/tag/SearchTagsReqVO.java new file mode 100644 index 0000000..0045454 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/tag/SearchTagsReqVO.java @@ -0,0 +1,27 @@ +package com.quanxiaoha.weblog.admin.model.vo.tag; + +import io.swagger.annotations.ApiModel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 标签模糊查询 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@ApiModel(value = "标签模糊查询 VO") +public class SearchTagsReqVO { + + @NotBlank(message = "标签查询关键词不能为空") + private String key; + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/user/FindUserInfoRspVO.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/user/FindUserInfoRspVO.java new file mode 100644 index 0000000..13fcb86 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/user/FindUserInfoRspVO.java @@ -0,0 +1,28 @@ +package com.quanxiaoha.weblog.admin.model.vo.user; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotBlank; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 查询用户信息 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class FindUserInfoRspVO { + /** + * 用户名 + */ + private String username; + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/user/UpdateAdminUserPasswordReqVO.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/user/UpdateAdminUserPasswordReqVO.java new file mode 100644 index 0000000..1107969 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/user/UpdateAdminUserPasswordReqVO.java @@ -0,0 +1,31 @@ +package com.quanxiaoha.weblog.admin.model.vo.user; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import javax.validation.constraints.*; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: TODO + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@ApiModel(value = "修改用户密码 VO") +public class UpdateAdminUserPasswordReqVO { + + @NotBlank(message = "用户名不能为空") + @ApiModelProperty(value = "用户名") + private String username; + + @NotBlank(message = "密码不能为空") + @ApiModelProperty(value = "密码") + private String password; +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/package-info.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/package-info.java new file mode 100644 index 0000000..8a825e4 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/package-info.java @@ -0,0 +1,7 @@ +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-16 9:28 + * @description: TODO + **/ +package com.quanxiaoha.weblog.admin; \ No newline at end of file diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminArticleService.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminArticleService.java new file mode 100644 index 0000000..0075441 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminArticleService.java @@ -0,0 +1,47 @@ +package com.quanxiaoha.weblog.admin.service; + +import com.quanxiaoha.weblog.admin.model.vo.article.*; +import com.quanxiaoha.weblog.common.utils.Response; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:03 + * @description: 文章 + **/ +public interface AdminArticleService { + /** + * 发布文章 + * @param publishArticleReqVO + * @return + */ + Response publishArticle(PublishArticleReqVO publishArticleReqVO); + + /** + * 删除文章 + * @param deleteArticleReqVO + * @return + */ + Response deleteArticle(DeleteArticleReqVO deleteArticleReqVO); + + /** + * 查询文章分页数据 + * @param findArticlePageListReqVO + * @return + */ + Response findArticlePageList(FindArticlePageListReqVO findArticlePageListReqVO); + + /** + * 查询文章详情 + * @param findArticlePageListReqVO + * @return + */ + Response findArticleDetail(FindArticleDetailReqVO findArticlePageListReqVO); + + /** + * 更新文章 + * @param updateArticleReqVO + * @return + */ + Response updateArticle(UpdateArticleReqVO updateArticleReqVO); +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminBlogSettingsService.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminBlogSettingsService.java new file mode 100644 index 0000000..80350a9 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminBlogSettingsService.java @@ -0,0 +1,26 @@ +package com.quanxiaoha.weblog.admin.service; + +import com.quanxiaoha.weblog.admin.model.vo.blogsettings.UpdateBlogSettingsReqVO; +import com.quanxiaoha.weblog.admin.model.vo.user.UpdateAdminUserPasswordReqVO; +import com.quanxiaoha.weblog.common.utils.Response; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:03 + * @description: TODO + **/ +public interface AdminBlogSettingsService { + /** + * 更新博客设置信息 + * @param updateBlogSettingsReqVO + * @return + */ + Response updateBlogSettings(UpdateBlogSettingsReqVO updateBlogSettingsReqVO); + + /** + * 获取博客设置详情 + * @return + */ + Response findDetail(); +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminCategoryService.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminCategoryService.java new file mode 100644 index 0000000..a16de2b --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminCategoryService.java @@ -0,0 +1,45 @@ +package com.quanxiaoha.weblog.admin.service; + +import com.quanxiaoha.weblog.admin.model.vo.category.AddCategoryReqVO; +import com.quanxiaoha.weblog.admin.model.vo.category.DeleteCategoryReqVO; +import com.quanxiaoha.weblog.admin.model.vo.category.FindCategoryPageListReqVO; +import com.quanxiaoha.weblog.admin.model.vo.tag.AddTagReqVO; +import com.quanxiaoha.weblog.admin.model.vo.user.UpdateAdminUserPasswordReqVO; +import com.quanxiaoha.weblog.common.utils.PageResponse; +import com.quanxiaoha.weblog.common.utils.Response; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:03 + * @description: TODO + **/ +public interface AdminCategoryService { + /** + * 添加分类 + * @param addCategoryReqVO + * @return + */ + Response addCategory(AddCategoryReqVO addCategoryReqVO); + + /** + * 分类分页数据查询 + * @param findCategoryPageListReqVO + * @return + */ + PageResponse findCategoryPageList(FindCategoryPageListReqVO findCategoryPageListReqVO); + + /** + * 删除分类 + * @param deleteCategoryReqVO + * @return + */ + Response deleteCategory(DeleteCategoryReqVO deleteCategoryReqVO); + + /** + * 获取文章分类的 Select 列表数据 + * @return + */ + Response findCategorySelectList(); + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminFileService.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminFileService.java new file mode 100644 index 0000000..3219e63 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminFileService.java @@ -0,0 +1,20 @@ +package com.quanxiaoha.weblog.admin.service; + +import com.quanxiaoha.weblog.admin.model.vo.user.UpdateAdminUserPasswordReqVO; +import com.quanxiaoha.weblog.common.utils.Response; +import org.springframework.web.multipart.MultipartFile; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:03 + * @description: TODO + **/ +public interface AdminFileService { + /** + * 上传文件 + * @param file + * @return + */ + Response uploadFile(MultipartFile file); +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminTagService.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminTagService.java new file mode 100644 index 0000000..aaa88e9 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminTagService.java @@ -0,0 +1,51 @@ +package com.quanxiaoha.weblog.admin.service; + +import com.quanxiaoha.weblog.admin.model.vo.tag.AddTagReqVO; +import com.quanxiaoha.weblog.admin.model.vo.tag.DeleteTagReqVO; +import com.quanxiaoha.weblog.admin.model.vo.tag.FindTagPageListReqVO; +import com.quanxiaoha.weblog.admin.model.vo.tag.SearchTagsReqVO; +import com.quanxiaoha.weblog.common.utils.PageResponse; +import com.quanxiaoha.weblog.common.utils.Response; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:03 + * @description: TODO + **/ +public interface AdminTagService { + + /** + * 添加标签集合 + * @param addTagReqVO + * @return + */ + Response addTags(AddTagReqVO addTagReqVO); + + /** + * 查询标签分页 + * @param findTagPageListReqVO + * @return + */ + PageResponse findTagPageList(FindTagPageListReqVO findTagPageListReqVO); + + /** + * 删除标签 + * @param deleteTagReqVO + * @return + */ + Response deleteTag(DeleteTagReqVO deleteTagReqVO); + + /** + * 根据标签关键词模糊查询 + * @param searchTagsReqVO + * @return + */ + Response searchTags(SearchTagsReqVO searchTagsReqVO); + + /** + * 查询标签 Select 列表数据 + * @return + */ + Response findTagSelectList(); +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminUserService.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminUserService.java new file mode 100644 index 0000000..53a26fe --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminUserService.java @@ -0,0 +1,25 @@ +package com.quanxiaoha.weblog.admin.service; + +import com.quanxiaoha.weblog.admin.model.vo.user.UpdateAdminUserPasswordReqVO; +import com.quanxiaoha.weblog.common.utils.Response; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:03 + * @description: TODO + **/ +public interface AdminUserService { + /** + * 修改密码 + * @param updateAdminUserPasswordReqVO + * @return + */ + Response updatePassword(UpdateAdminUserPasswordReqVO updateAdminUserPasswordReqVO); + + /** + * 获取当前登录用户信息 + * @return + */ + Response findUserInfo(); +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminArticleServiceImpl.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminArticleServiceImpl.java new file mode 100644 index 0000000..f8401a7 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminArticleServiceImpl.java @@ -0,0 +1,347 @@ +package com.quanxiaoha.weblog.admin.service.impl; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.google.common.collect.Lists; +import com.quanxiaoha.weblog.admin.convert.ArticleDetailConvert; +import com.quanxiaoha.weblog.admin.model.vo.article.*; +import com.quanxiaoha.weblog.admin.service.AdminArticleService; +import com.quanxiaoha.weblog.common.domain.dos.*; +import com.quanxiaoha.weblog.common.domain.mapper.*; +import com.quanxiaoha.weblog.common.enums.ResponseCodeEnum; +import com.quanxiaoha.weblog.common.exception.BizException; +import com.quanxiaoha.weblog.common.utils.PageResponse; +import com.quanxiaoha.weblog.common.utils.Response; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:03 + * @description: 文章 + **/ +@Service +@Slf4j +public class AdminArticleServiceImpl implements AdminArticleService { + + @Autowired + private ArticleMapper articleMapper; + @Autowired + private ArticleContentMapper articleContentMapper; + @Autowired + private ArticleCategoryRelMapper articleCategoryRelMapper; + @Autowired + private CategoryMapper categoryMapper; + @Autowired + private TagMapper tagMapper; + @Autowired + private ArticleTagRelMapper articleTagRelMapper; + + /** + * 发布文章 + * + * @param publishArticleReqVO + * @return + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Response publishArticle(PublishArticleReqVO publishArticleReqVO) { + // 1. VO 转 ArticleDO, 并保存 + ArticleDO articleDO = ArticleDO.builder() + .title(publishArticleReqVO.getTitle()) + .cover(publishArticleReqVO.getCover()) + .summary(publishArticleReqVO.getSummary()) + .createTime(LocalDateTime.now()) + .updateTime(LocalDateTime.now()) + .build(); + articleMapper.insert(articleDO); + + // 拿到插入记录的主键 ID + Long articleId = articleDO.getId(); + + // 2. VO 转 ArticleContentDO,并保存 + ArticleContentDO articleContentDO = ArticleContentDO.builder() + .articleId(articleId) + .content(publishArticleReqVO.getContent()) + .build(); + articleContentMapper.insert(articleContentDO); + + // 3. 处理文章关联的分类 + Long categoryId = publishArticleReqVO.getCategoryId(); + + // 3.1 校验提交的分类是否真实存在 + CategoryDO categoryDO = categoryMapper.selectById(categoryId); + if (Objects.isNull(categoryDO)) { + log.warn("==> 分类不存在, categoryId: {}", categoryId); + throw new BizException(ResponseCodeEnum.CATEGORY_NOT_EXISTED); + } + + ArticleCategoryRelDO articleCategoryRelDO = ArticleCategoryRelDO.builder() + .articleId(articleId) + .categoryId(categoryId) + .build(); + articleCategoryRelMapper.insert(articleCategoryRelDO); + + // 4. 保存文章关联的标签集合 + List publishTags = publishArticleReqVO.getTags(); + insertTags(articleId, publishTags); + + return Response.success(); + } + + /** + * 删除文章 + * + * @param deleteArticleReqVO + * @return + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Response deleteArticle(DeleteArticleReqVO deleteArticleReqVO) { + Long articleId = deleteArticleReqVO.getId(); + + // 1. 删除文章 + articleMapper.deleteById(articleId); + + // 2. 删除文章内容 + articleContentMapper.deleteByArticleId(articleId); + + // 3. 删除文章-分类关联记录 + articleCategoryRelMapper.deleteByArticleId(articleId); + + // 4. 删除文章-标签关联记录 + articleTagRelMapper.deleteByArticleId(articleId); + + return Response.success(); + } + + /** + * 查询文章分页数据 + * + * @param findArticlePageListReqVO + * @return + */ + @Override + public Response findArticlePageList(FindArticlePageListReqVO findArticlePageListReqVO) { + // 获取当前页、以及每页需要展示的数据数量 + Long current = findArticlePageListReqVO.getCurrent(); + Long size = findArticlePageListReqVO.getSize(); + String title = findArticlePageListReqVO.getTitle(); + LocalDate startDate = findArticlePageListReqVO.getStartDate(); + LocalDate endDate = findArticlePageListReqVO.getEndDate(); + + // 执行分页查询 + Page articleDOPage = articleMapper.selectPageList(current, size, title, startDate, endDate); + + List articleDOS = articleDOPage.getRecords(); + + // DO 转 VO + List vos = null; + if (!CollectionUtils.isEmpty(articleDOS)) { + vos = articleDOS.stream() + .map(articleDO -> FindArticlePageListRspVO.builder() + .id(articleDO.getId()) + .title(articleDO.getTitle()) + .cover(articleDO.getCover()) + .createTime(articleDO.getCreateTime()) + .build()) + .collect(Collectors.toList()); + } + + return PageResponse.success(articleDOPage, vos); + } + + /** + * 查询文章详情 + * + * @param findArticlePageListReqVO + * @return + */ + @Override + public Response findArticleDetail(FindArticleDetailReqVO findArticlePageListReqVO) { + Long articleId = findArticlePageListReqVO.getId(); + + ArticleDO articleDO = articleMapper.selectById(articleId); + + if (Objects.isNull(articleDO)) { + log.warn("==> 查询的文章不存在,articleId: {}", articleId); + throw new BizException(ResponseCodeEnum.ARTICLE_NOT_FOUND); + } + + ArticleContentDO articleContentDO = articleContentMapper.selectByArticleId(articleId); + + // 所属分类 + ArticleCategoryRelDO articleCategoryRelDO = articleCategoryRelMapper.selectByArticleId(articleId); + + // 对应标签 + List articleTagRelDOS = articleTagRelMapper.selectByArticleId(articleId); + // 获取对应标签 ID 集合 + List tagIds = articleTagRelDOS.stream().map(ArticleTagRelDO::getTagId).collect(Collectors.toList()); + + // DO 转 VO + FindArticleDetailRspVO vo = ArticleDetailConvert.INSTANCE.convertDO2VO(articleDO); + vo.setContent(articleContentDO.getContent()); + vo.setCategoryId(articleCategoryRelDO.getCategoryId()); + vo.setTagIds(tagIds); + + return Response.success(vo); + } + + /** + * 更新文章 + * + * @param updateArticleReqVO + * @return + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Response updateArticle(UpdateArticleReqVO updateArticleReqVO) { + Long articleId = updateArticleReqVO.getId(); + + // 1. VO 转 ArticleDO, 并更新 + ArticleDO articleDO = ArticleDO.builder() + .id(articleId) + .title(updateArticleReqVO.getTitle()) + .cover(updateArticleReqVO.getCover()) + .summary(updateArticleReqVO.getSummary()) + .updateTime(LocalDateTime.now()) + .build(); + int count = articleMapper.updateById(articleDO); + + // 根据更新是否成功,来判断该文章是否存在 + if (count == 0) { + log.warn("==> 该文章不存在, articleId: {}", articleId); + throw new BizException(ResponseCodeEnum.ARTICLE_NOT_FOUND); + } + + // 2. VO 转 ArticleContentDO,并更新 + ArticleContentDO articleContentDO = ArticleContentDO.builder() + .articleId(articleId) + .content(updateArticleReqVO.getContent()) + .build(); + articleContentMapper.updateByArticleId(articleContentDO); + + + // 3. 更新文章分类 + Long categoryId = updateArticleReqVO.getCategoryId(); + + // 3.1 校验提交的分类是否真实存在 + CategoryDO categoryDO = categoryMapper.selectById(categoryId); + if (Objects.isNull(categoryDO)) { + log.warn("==> 分类不存在, categoryId: {}", categoryId); + throw new BizException(ResponseCodeEnum.CATEGORY_NOT_EXISTED); + } + + // 先删除该文章关联的分类记录,再插入新的关联关系 + articleCategoryRelMapper.deleteByArticleId(articleId); + ArticleCategoryRelDO articleCategoryRelDO = ArticleCategoryRelDO.builder() + .articleId(articleId) + .categoryId(categoryId) + .build(); + articleCategoryRelMapper.insert(articleCategoryRelDO); + + // 4. 保存文章关联的标签集合 + // 先删除该文章对应的标签 + articleTagRelMapper.deleteByArticleId(articleId); + List publishTags = updateArticleReqVO.getTags(); + insertTags(articleId, publishTags); + + return Response.success(); + } + + /** + * 保存标签 + * @param articleId + * @param publishTags + */ + private void insertTags(Long articleId, List publishTags) { + // 筛选提交的标签(表中不存在的标签) + List notExistTags = null; + // 筛选提交的标签(表中已存在的标签) + List existedTags = null; + + // 查询出所有标签 + List tagDOS = tagMapper.selectList(Wrappers.emptyWrapper()); + + // 如果表中还没有添加任何标签 + if (CollectionUtils.isEmpty(tagDOS)) { + notExistTags = publishTags; + } else { + List tagIds = tagDOS.stream().map(tagDO -> String.valueOf(tagDO.getId())).collect(Collectors.toList()); + // 表中已添加相关标签,则需要筛选 + // 通过标签 ID 来筛选,包含对应 ID 则表示提交的标签是表中存在的 + existedTags = publishTags.stream().filter(publishTag -> tagIds.contains(publishTag)).collect(Collectors.toList()); + // 否则则是不存在的 + notExistTags = publishTags.stream().filter(publishTag -> !tagIds.contains(publishTag)).collect(Collectors.toList()); + + // 还有一种可能:按字符串名称提交上来的标签,也有可能是表中已存在的,比如表中已经有了 Java 标签,用户提交了个 java 小写的标签,需要内部装换为 Java 标签 + Map tagNameIdMap = tagDOS.stream().collect(Collectors.toMap(tagDO -> tagDO.getName().toLowerCase(), TagDO::getId)); + + // 使用迭代器进行安全的删除操作 + Iterator iterator = notExistTags.iterator(); + while (iterator.hasNext()) { + String notExistTag = iterator.next(); + // 转小写, 若 Map 中相同的 key,则表示该新标签是重复标签 + if (tagNameIdMap.containsKey(notExistTag.toLowerCase())) { + // 从不存在的标签集合中清除 + iterator.remove(); + // 并将对应的 ID 添加到已存在的标签集合 + existedTags.add(String.valueOf(tagNameIdMap.get(notExistTag.toLowerCase()))); + } + } + } + + // 将提交的上来的,已存在于表中的标签,文章-标签关联关系入库 + if (!CollectionUtils.isEmpty(existedTags)) { + List articleTagRelDOS = Lists.newArrayList(); + existedTags.forEach(tagId -> { + ArticleTagRelDO articleTagRelDO = ArticleTagRelDO.builder() + .articleId(articleId) + .tagId(Long.valueOf(tagId)) + .build(); + articleTagRelDOS.add(articleTagRelDO); + }); + // 批量插入 + articleTagRelMapper.insertBatchSomeColumn(articleTagRelDOS); + } + + // 将提交的上来的,不存在于表中的标签,入库保存 + if (!CollectionUtils.isEmpty(notExistTags)) { + // 需要先将标签入库,拿到对应标签 ID 后,再把文章-标签关联关系入库 + List articleTagRelDOS = Lists.newArrayList(); + notExistTags.forEach(tagName -> { + TagDO tagDO = TagDO.builder() + .name(tagName) + .createTime(LocalDateTime.now()) + .updateTime(LocalDateTime.now()) + .build(); + + tagMapper.insert(tagDO); + + // 拿到保存的标签 ID + Long tagId = tagDO.getId(); + + // 文章-标签关联关系 + ArticleTagRelDO articleTagRelDO = ArticleTagRelDO.builder() + .articleId(articleId) + .tagId(tagId) + .build(); + articleTagRelDOS.add(articleTagRelDO); + }); + // 批量插入 + articleTagRelMapper.insertBatchSomeColumn(articleTagRelDOS); + } + } +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminBlogSettingsServiceImpl.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminBlogSettingsServiceImpl.java new file mode 100644 index 0000000..d244b99 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminBlogSettingsServiceImpl.java @@ -0,0 +1,61 @@ +package com.quanxiaoha.weblog.admin.service.impl; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.quanxiaoha.weblog.admin.convert.BlogSettingsConvert; +import com.quanxiaoha.weblog.admin.model.vo.blogsettings.FindBlogSettingsRspVO; +import com.quanxiaoha.weblog.admin.model.vo.blogsettings.UpdateBlogSettingsReqVO; +import com.quanxiaoha.weblog.admin.model.vo.user.FindUserInfoRspVO; +import com.quanxiaoha.weblog.admin.model.vo.user.UpdateAdminUserPasswordReqVO; +import com.quanxiaoha.weblog.admin.service.AdminBlogSettingsService; +import com.quanxiaoha.weblog.admin.service.AdminUserService; +import com.quanxiaoha.weblog.common.domain.dos.BlogSettingsDO; +import com.quanxiaoha.weblog.common.domain.mapper.BlogSettingsMapper; +import com.quanxiaoha.weblog.common.domain.mapper.UserMapper; +import com.quanxiaoha.weblog.common.enums.ResponseCodeEnum; +import com.quanxiaoha.weblog.common.utils.Response; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:03 + * @description: TODO + **/ +@Service +public class AdminBlogSettingsServiceImpl extends ServiceImpl implements AdminBlogSettingsService { + + @Autowired + private BlogSettingsMapper blogSettingsMapper; + + @Override + public Response updateBlogSettings(UpdateBlogSettingsReqVO updateBlogSettingsReqVO) { + // VO 转 DO + BlogSettingsDO blogSettingsDO = BlogSettingsConvert.INSTANCE.convertVO2DO(updateBlogSettingsReqVO); + blogSettingsDO.setId(1L); + + // 保存或更新(当数据库中存在 ID 为 1 的记录时,则执行更新操作,否则执行插入操作) + saveOrUpdate(blogSettingsDO); + return Response.success(); + } + + /** + * 获取博客设置详情 + * + * @return + */ + @Override + public Response findDetail() { + // 查询 ID 为 1 的记录 + BlogSettingsDO blogSettingsDO = blogSettingsMapper.selectById(1L); + + // DO 转 VO + FindBlogSettingsRspVO vo = BlogSettingsConvert.INSTANCE.convertDO2VO(blogSettingsDO); + + return Response.success(vo); + } +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminCategoryServiceImpl.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminCategoryServiceImpl.java new file mode 100644 index 0000000..e10327a --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminCategoryServiceImpl.java @@ -0,0 +1,158 @@ +package com.quanxiaoha.weblog.admin.service.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.quanxiaoha.weblog.admin.model.vo.category.AddCategoryReqVO; +import com.quanxiaoha.weblog.admin.model.vo.category.DeleteCategoryReqVO; +import com.quanxiaoha.weblog.admin.model.vo.category.FindCategoryPageListReqVO; +import com.quanxiaoha.weblog.admin.model.vo.category.FindCategoryPageListRspVO; +import com.quanxiaoha.weblog.admin.service.AdminCategoryService; +import com.quanxiaoha.weblog.common.domain.dos.ArticleCategoryRelDO; +import com.quanxiaoha.weblog.common.domain.dos.CategoryDO; +import com.quanxiaoha.weblog.common.domain.mapper.ArticleCategoryRelMapper; +import com.quanxiaoha.weblog.common.domain.mapper.CategoryMapper; +import com.quanxiaoha.weblog.common.enums.ResponseCodeEnum; +import com.quanxiaoha.weblog.common.exception.BizException; +import com.quanxiaoha.weblog.common.model.vo.SelectRspVO; +import com.quanxiaoha.weblog.common.utils.PageResponse; +import com.quanxiaoha.weblog.common.utils.Response; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.time.LocalDate; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:03 + * @description: TODO + **/ +@Service +@Slf4j +public class AdminCategoryServiceImpl implements AdminCategoryService { + + @Autowired + private CategoryMapper categoryMapper; + @Autowired + private ArticleCategoryRelMapper articleCategoryRelMapper; + + /** + * 添加分类 + * + * @param addCategoryReqVO + * @return + */ + @Override + public Response addCategory(AddCategoryReqVO addCategoryReqVO) { + String categoryName = addCategoryReqVO.getName(); + + // 先判断该分类是否已经存在 + CategoryDO categoryDO = categoryMapper.selectByName(categoryName); + + if (Objects.nonNull(categoryDO)) { + log.warn("分类名称: {}, 此已存在", categoryName); + throw new BizException(ResponseCodeEnum.CATEGORY_NAME_IS_EXISTED); + } + + // 构建 DO 类 + CategoryDO insertCategoryDO = CategoryDO.builder() + .name(addCategoryReqVO.getName().trim()) + .build(); + + // 执行 insert + categoryMapper.insert(insertCategoryDO); + + return Response.success(); + } + + /** + * 分类分页数据查询 + * + * @param findCategoryPageListReqVO + * @return + */ + @Override + public PageResponse findCategoryPageList(FindCategoryPageListReqVO findCategoryPageListReqVO) { + // 获取当前页、以及每页需要展示的数据数量 + Long current = findCategoryPageListReqVO.getCurrent(); + Long size = findCategoryPageListReqVO.getSize(); + String name = findCategoryPageListReqVO.getName(); + LocalDate startDate = findCategoryPageListReqVO.getStartDate(); + LocalDate endDate = findCategoryPageListReqVO.getEndDate(); + + // 执行分页查询 + Page categoryDOPage = categoryMapper.selectPageList(current, size, name, startDate, endDate); + + List categoryDOS = categoryDOPage.getRecords(); + + // DO 转 VO + List vos = null; + if (!CollectionUtils.isEmpty(categoryDOS)) { + vos = categoryDOS.stream() + .map(categoryDO -> FindCategoryPageListRspVO.builder() + .id(categoryDO.getId()) + .name(categoryDO.getName()) + .createTime(categoryDO.getCreateTime()) + .build()) + .collect(Collectors.toList()); + } + + return PageResponse.success(categoryDOPage, vos); + } + + /** + * 删除分类 + * + * @param deleteCategoryReqVO + * @return + */ + @Override + public Response deleteCategory(DeleteCategoryReqVO deleteCategoryReqVO) { + // 分类 ID + Long categoryId = deleteCategoryReqVO.getId(); + + // 校验该分类下是否已经有文章,若有,则提示需要先删除分类下所有文章,才能删除 + ArticleCategoryRelDO articleCategoryRelDO = articleCategoryRelMapper.selectOneByCategoryId(categoryId); + + if (Objects.nonNull(articleCategoryRelDO)) { + log.warn("==> 此分类下包含文章,无法删除,categoryId: {}", categoryId); + throw new BizException(ResponseCodeEnum.CATEGORY_CAN_NOT_DELETE); + } + + // 删除分类 + categoryMapper.deleteById(categoryId); + + return Response.success(); + } + + /** + * 获取文章分类的 Select 列表数据 + * + * @return + */ + @Override + public Response findCategorySelectList() { + // 查询所有分类 + List categoryDOS = categoryMapper.selectList(null); + + // DO 转 VO + List selectRspVOS = null; + // 如果分类数据不为空 + if (!CollectionUtils.isEmpty(categoryDOS)) { + // 将分类 ID 作为 Value 值,将分类名称作为 label 展示 + selectRspVOS = categoryDOS.stream() + .map(categoryDO -> SelectRspVO.builder() + .label(categoryDO.getName()) + .value(categoryDO.getId()) + .build()) + .collect(Collectors.toList()); + } + + return Response.success(selectRspVOS); + } + +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminFileServiceImpl.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminFileServiceImpl.java new file mode 100644 index 0000000..85b224b --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminFileServiceImpl.java @@ -0,0 +1,55 @@ +package com.quanxiaoha.weblog.admin.service.impl; + +import com.quanxiaoha.weblog.admin.model.vo.file.UploadFileRspVO; +import com.quanxiaoha.weblog.admin.model.vo.user.FindUserInfoRspVO; +import com.quanxiaoha.weblog.admin.model.vo.user.UpdateAdminUserPasswordReqVO; +import com.quanxiaoha.weblog.admin.service.AdminFileService; +import com.quanxiaoha.weblog.admin.service.AdminUserService; +import com.quanxiaoha.weblog.admin.utils.MinioUtil; +import com.quanxiaoha.weblog.common.domain.mapper.UserMapper; +import com.quanxiaoha.weblog.common.enums.ResponseCodeEnum; +import com.quanxiaoha.weblog.common.exception.BizException; +import com.quanxiaoha.weblog.common.utils.Response; +import io.minio.MinioClient; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:03 + * @description: 文件上传 + **/ +@Service +@Slf4j +public class AdminFileServiceImpl implements AdminFileService { + + @Autowired + private MinioUtil minioUtil; + + /** + * 上传文件 + * + * @param file + * @return + */ + @Override + public Response uploadFile(MultipartFile file) { + try { + // 上传文件 + String url = minioUtil.uploadFile(file); + + // 构建成功返参,将图片的访问链接返回 + return Response.success(UploadFileRspVO.builder().url(url).build()); + } catch (Exception e) { + log.error("==> 上传文件至 Minio 错误: ", e); + // 手动抛出业务异常,提示 “文件上传失败” + throw new BizException(ResponseCodeEnum.FILE_UPLOAD_FAILED); + } + } +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminTagServiceImpl.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminTagServiceImpl.java new file mode 100644 index 0000000..7fc6df7 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminTagServiceImpl.java @@ -0,0 +1,178 @@ +package com.quanxiaoha.weblog.admin.service.impl; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.quanxiaoha.weblog.admin.model.vo.tag.*; +import com.quanxiaoha.weblog.admin.service.AdminTagService; +import com.quanxiaoha.weblog.common.domain.dos.ArticleTagRelDO; +import com.quanxiaoha.weblog.common.domain.dos.TagDO; +import com.quanxiaoha.weblog.common.domain.mapper.ArticleTagRelMapper; +import com.quanxiaoha.weblog.common.domain.mapper.TagMapper; +import com.quanxiaoha.weblog.common.enums.ResponseCodeEnum; +import com.quanxiaoha.weblog.common.exception.BizException; +import com.quanxiaoha.weblog.common.model.vo.SelectRspVO; +import com.quanxiaoha.weblog.common.utils.PageResponse; +import com.quanxiaoha.weblog.common.utils.Response; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:03 + * @description: TODO + **/ +@Service +@Slf4j +public class AdminTagServiceImpl extends ServiceImpl implements AdminTagService { + + @Autowired + private TagMapper tagMapper; + @Autowired + private ArticleTagRelMapper articleTagRelMapper; + + /** + * 添加标签集合 + * + * @param addTagReqVO + * @return + */ + @Override + public Response addTags(AddTagReqVO addTagReqVO) { + // vo 转 do + List tagDOS = addTagReqVO.getTags().stream() + .map(tagName -> TagDO.builder() + .name(tagName.trim()) // 去掉前后空格 + .createTime(LocalDateTime.now()) + .updateTime(LocalDateTime.now()) + .build()) + .collect(Collectors.toList()); + + // 批量插入 + try { + saveBatch(tagDOS); + } catch (Exception e) { + log.warn("该标签已存在", e); + } + + return Response.success(); + } + + /** + * 查询标签分页 + * + * @param findTagPageListReqVO + * @return + */ + @Override + public PageResponse findTagPageList(FindTagPageListReqVO findTagPageListReqVO) { + // 分页参数、条件参数 + Long current = findTagPageListReqVO.getCurrent(); + Long size = findTagPageListReqVO.getSize(); + String name = findTagPageListReqVO.getName(); + LocalDate startDate = findTagPageListReqVO.getStartDate(); + LocalDate endDate = findTagPageListReqVO.getEndDate(); + + // 分页查询 + Page page = tagMapper.selectPageList(current, size, name, startDate, endDate); + + List records = page.getRecords(); + + // do 转 vo + List vos = null; + if (!CollectionUtils.isEmpty(records)) { + vos = records.stream().map(tagDO -> FindTagPageListRspVO.builder() + .id(tagDO.getId()) + .name(tagDO.getName()) + .createTime(tagDO.getCreateTime()) + .build()).collect(Collectors.toList()); + } + + return PageResponse.success(page, vos); + } + + /** + * 删除标签 + * + * @param deleteTagReqVO + * @return + */ + @Override + public Response deleteTag(DeleteTagReqVO deleteTagReqVO) { + // 标签 ID + Long tagId = deleteTagReqVO.getId(); + + // 校验该标签下是否有关联的文章,若有,则不允许删除,提示用户需要先删除标签下的文章 + ArticleTagRelDO articleTagRelDO = articleTagRelMapper.selectOneByTagId(tagId); + + if (Objects.nonNull(articleTagRelDO)) { + log.warn("==> 此标签下包含文章,无法删除,tagId: {}", tagId); + throw new BizException(ResponseCodeEnum.TAG_CAN_NOT_DELETE); + } + + // 根据标签 ID 删除 + int count = tagMapper.deleteById(tagId); + + return count == 1 ? Response.success() : Response.fail(ResponseCodeEnum.TAG_NOT_EXISTED); + } + + /** + * 根据标签关键词模糊查询 + * + * @param searchTagsReqVO + * @return + */ + @Override + public Response searchTags(SearchTagsReqVO searchTagsReqVO) { + String key = searchTagsReqVO.getKey(); + + // 执行模糊查询 + List tagDOS = tagMapper.selectByKey(key); + + // do 转 vo + List vos = null; + if (!CollectionUtils.isEmpty(tagDOS)) { + vos = tagDOS.stream() + .map(tagDO -> SelectRspVO.builder() + .label(tagDO.getName()) + .value(tagDO.getId()) + .build()) + .collect(Collectors.toList()); + } + + return Response.success(vos); + } + + /** + * 查询标签 Select 列表数据 + * + * @return + */ + @Override + public Response findTagSelectList() { + // 查询所有标签 + List tagDOS = tagMapper.selectList(Wrappers.emptyWrapper()); + + // DO 转 VO + List vos = null; + if (!CollectionUtils.isEmpty(tagDOS)) { + vos = tagDOS.stream() + .map(tagDO -> SelectRspVO.builder() + .label(tagDO.getName()) + .value(tagDO.getId()) + .build()) + .collect(Collectors.toList()); + } + + return Response.success(vos); + } +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminUserServiceImpl.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminUserServiceImpl.java new file mode 100644 index 0000000..5bf44ff --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminUserServiceImpl.java @@ -0,0 +1,62 @@ +package com.quanxiaoha.weblog.admin.service.impl; + +import com.quanxiaoha.weblog.admin.model.vo.user.FindUserInfoRspVO; +import com.quanxiaoha.weblog.admin.model.vo.user.UpdateAdminUserPasswordReqVO; +import com.quanxiaoha.weblog.admin.service.AdminUserService; +import com.quanxiaoha.weblog.common.domain.mapper.UserMapper; +import com.quanxiaoha.weblog.common.enums.ResponseCodeEnum; +import com.quanxiaoha.weblog.common.utils.Response; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:03 + * @description: TODO + **/ +@Service +public class AdminUserServiceImpl implements AdminUserService { + + @Autowired + private UserMapper userMapper; + @Autowired + private PasswordEncoder passwordEncoder; + + /** + * 修改密码 + * @param updateAdminUserPasswordReqVO + * @return + */ + @Override + public Response updatePassword(UpdateAdminUserPasswordReqVO updateAdminUserPasswordReqVO) { + // 拿到用户名、密码 + String username = updateAdminUserPasswordReqVO.getUsername(); + String password = updateAdminUserPasswordReqVO.getPassword(); + + // 加密密码 + String encodePassword = passwordEncoder.encode(password); + + // 更新到数据库 + int count = userMapper.updatePasswordByUsername(username, encodePassword); + + return count == 1 ? Response.success() : Response.fail(ResponseCodeEnum.USERNAME_NOT_FOUND); + } + + /** + * 获取当前登录用户信息 + * @return + */ + @Override + public Response findUserInfo() { + // 获取存储在 ThreadLocal 中的用户信息 + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + // 拿到用户名 + String username = authentication.getName(); + + return Response.success(FindUserInfoRspVO.builder().username(username).build()); + } +} diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/package-info.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/package-info.java new file mode 100644 index 0000000..25284f5 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/package-info.java @@ -0,0 +1,7 @@ +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:02 + * @description: TODO + **/ +package com.quanxiaoha.weblog.admin.service; \ No newline at end of file diff --git a/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/utils/MinioUtil.java b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/utils/MinioUtil.java new file mode 100644 index 0000000..4bec268 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/utils/MinioUtil.java @@ -0,0 +1,75 @@ +package com.quanxiaoha.weblog.admin.utils; + +import com.quanxiaoha.weblog.admin.config.MinioProperties; +import com.quanxiaoha.weblog.common.exception.BizException; +import io.minio.MinioClient; +import io.minio.PutObjectArgs; +import io.minio.errors.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.UUID; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-05-11 9:02 + * @description: TODO + **/ +@Component +@Slf4j +public class MinioUtil { + + @Autowired + private MinioProperties minioProperties; + + @Autowired + private MinioClient minioClient; + + /** + * 上传文件 + * @param file + * @return + * @throws Exception + */ + public String uploadFile(MultipartFile file) throws Exception { + // 判断文件是否为空 + if (file == null || file.getSize() == 0) { + log.error("==> 上传文件异常:文件大小为空 ..."); + throw new RuntimeException("文件大小不能为空"); + } + + // 文件的原始名称 + String originalFileName = file.getOriginalFilename(); + // 文件的 Content-Type + String contentType = file.getContentType(); + + // 生成存储对象的名称(将 UUID 字符串中的 - 替换成空字符串) + String key = UUID.randomUUID().toString().replace("-", ""); + // 获取文件的后缀,如 .jpg + String suffix = originalFileName.substring(originalFileName.lastIndexOf(".")); + + // 拼接上文件后缀,即为要存储的文件名 + String objectName = String.format("%s%s", key, suffix); + + log.info("==> 开始上传文件至 Minio, ObjectName: {}", objectName); + + // 上传文件至 Minio + minioClient.putObject(PutObjectArgs.builder() + .bucket(minioProperties.getBucketName()) + .object(objectName) + .stream(file.getInputStream(), file.getSize(), -1) + .contentType(contentType) + .build()); + + // 返回文件的访问链接 + String url = String.format("%s/%s/%s", minioProperties.getEndpoint(), minioProperties.getBucketName(), objectName); + log.info("==> 上传文件至 Minio 成功,访问路径: {}", url); + return url; + } +} diff --git a/weblog-springboot-134/weblog-module-admin/src/test/java/com/quanxiaoha/weblog/admin/WeblogModuleAdminApplicationTests.java b/weblog-springboot-134/weblog-module-admin/src/test/java/com/quanxiaoha/weblog/admin/WeblogModuleAdminApplicationTests.java new file mode 100644 index 0000000..1b6daf7 --- /dev/null +++ b/weblog-springboot-134/weblog-module-admin/src/test/java/com/quanxiaoha/weblog/admin/WeblogModuleAdminApplicationTests.java @@ -0,0 +1,25 @@ +package com.quanxiaoha.weblog.admin; + +import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; +import com.quanxiaoha.weblog.common.domain.dos.UserDO; +import com.quanxiaoha.weblog.common.domain.mapper.UserMapper; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; + +import java.time.LocalDateTime; + +@SpringBootTest(classes = WeblogModuleAdminApplicationTests.Application.class) +class WeblogModuleAdminApplicationTests { + // @Import({ + // DataSourceAutoConfiguration.class, // Spring DB 自动配置类 + // DataSourceTransactionManagerAutoConfiguration.class, // Spring 事务自动配置类 + // MybatisPlusAutoConfiguration.class, // MyBatis 的自动配置类 + // }) + public static class Application { + } + +} diff --git a/weblog-springboot-134/weblog-module-common/.gitignore b/weblog-springboot-134/weblog-module-common/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/weblog-springboot-134/weblog-module-common/pom.xml b/weblog-springboot-134/weblog-module-common/pom.xml new file mode 100644 index 0000000..9ef9da3 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/pom.xml @@ -0,0 +1,69 @@ + + + 4.0.0 + + com.quanxiaoha + weblog-springboot + ${revision} + + + com.quanxiaoha + weblog-module-common + weblog-module-common + weblog-module-common (此模块用于存放一些通用的功能) + + + + org.springframework.boot + spring-boot-starter-web + + + + org.projectlombok + lombok + true + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + org.springframework.boot + spring-boot-starter-aop + + + + + com.fasterxml.jackson.core + jackson-databind + + + + + com.baomidou + mybatis-plus-boot-starter + + + + + mysql + mysql-connector-java + + + + p6spy + p6spy + + + + org.springframework.boot + spring-boot-starter-security + + + diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/aspect/ApiOperationLog.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/aspect/ApiOperationLog.java new file mode 100644 index 0000000..38eeb0f --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/aspect/ApiOperationLog.java @@ -0,0 +1,17 @@ +package com.quanxiaoha.weblog.common.aspect; + +import java.lang.annotation.*; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD}) +@Documented +public @interface ApiOperationLog { + /** + * API 功能描述 + * + * @return + */ + String description() default ""; + +} + diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/aspect/ApiOperationLogAspect.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/aspect/ApiOperationLogAspect.java new file mode 100644 index 0000000..ef199b4 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/aspect/ApiOperationLogAspect.java @@ -0,0 +1,102 @@ + +package com.quanxiaoha.weblog.common.aspect; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.quanxiaoha.weblog.common.utils.JsonUtil; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.*; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.MDC; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.UUID; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Aspect +@Component +@Slf4j +public class ApiOperationLogAspect { + + /** 以自定义 @ApiOperationLog 注解为切点,凡是添加 @ApiOperationLog 的方法,都会执行环绕中的代码 */ + @Pointcut("@annotation(com.quanxiaoha.weblog.common.aspect.ApiOperationLog)") + public void apiOperationLog() {} + + /** + * 环绕 + * @param joinPoint + * @return + * @throws Throwable + */ + @Around("apiOperationLog()") + public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { + try { + // 请求开始时间 + long startTime = System.currentTimeMillis(); + + // MDC + MDC.put("traceId", UUID.randomUUID().toString()); + + // 获取被请求的类和方法 + String className = joinPoint.getTarget().getClass().getSimpleName(); + String methodName = joinPoint.getSignature().getName(); + + // 请求入参 + Object[] args = joinPoint.getArgs(); + // 入参转 JSON 字符串 + String argsJsonStr = Arrays.stream(args).map(toJsonStr()).collect(Collectors.joining(", ")); + + // 功能描述信息 + String description = getApiOperationLogDescription(joinPoint); + + // 打印请求相关参数 + log.info("====== 请求开始: [{}], 入参: {}, 请求类: {}, 请求方法: {} =================================== ", + description, argsJsonStr, className, methodName); + + // 执行切点方法 + Object result = joinPoint.proceed(); + + // 执行耗时 + long executionTime = System.currentTimeMillis() - startTime; + + // 打印出参等相关信息 + log.info("====== 请求结束: [{}], 耗时: {}ms, 出参: {} =================================== ", + description, executionTime, JsonUtil.toJsonString(result)); + + return result; + } finally { + MDC.clear(); + } + } + + /** + * 获取注解的描述信息 + * @param joinPoint + * @return + */ + private String getApiOperationLogDescription(ProceedingJoinPoint joinPoint) { + // 1. 从 ProceedingJoinPoint 获取 MethodSignature + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + + // 2. 使用 MethodSignature 获取当前被注解的 Method + Method method = signature.getMethod(); + + // 3. 从 Method 中提取 LogExecution 注解 + ApiOperationLog apiOperationLog = method.getAnnotation(ApiOperationLog.class); + + // 4. 从 LogExecution 注解中获取 description 属性 + return apiOperationLog.description(); + } + + /** + * 转 JSON 字符串 + * @return + */ + private Function toJsonStr() { + return arg -> JsonUtil.toJsonString(arg); + } + +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/config/InsertBatchMapper.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/config/InsertBatchMapper.java new file mode 100644 index 0000000..7399554 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/config/InsertBatchMapper.java @@ -0,0 +1,19 @@ +package com.quanxiaoha.weblog.common.config; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-06-13 22:50 + * @description: 批量插入 + **/ +public interface InsertBatchMapper extends BaseMapper { + + // 批量插入 + int insertBatchSomeColumn(@Param("list") List batchList); + +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/config/InsertBatchSqlInjector.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/config/InsertBatchSqlInjector.java new file mode 100644 index 0000000..69426f9 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/config/InsertBatchSqlInjector.java @@ -0,0 +1,26 @@ +package com.quanxiaoha.weblog.common.config; + +import com.baomidou.mybatisplus.core.injector.AbstractMethod; +import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector; +import com.baomidou.mybatisplus.core.metadata.TableInfo; +import com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn; + +import java.util.List; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-06-13 22:46 + * @description: 批量插入 SQL 注入器 + **/ +public class InsertBatchSqlInjector extends DefaultSqlInjector { + + @Override + public List getMethodList(Class mapperClass, TableInfo tableInfo) { + // super.getMethodList() 保留 Mybatis Plus 自带的方法 + List methodList = super.getMethodList(mapperClass, tableInfo); + // 添加自定义方法:批量插入,方法名为 insertBatchSomeColumn + methodList.add(new InsertBatchSomeColumn()); + return methodList; + } +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/config/JacksonConfig.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/config/JacksonConfig.java new file mode 100644 index 0000000..cf3b0bc --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/config/JacksonConfig.java @@ -0,0 +1,65 @@ +package com.quanxiaoha.weblog.common.config; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.deser.YearMonthDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; +import com.fasterxml.jackson.datatype.jsr310.ser.YearMonthSerializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.YearMonth; +import java.time.format.DateTimeFormatter; +import java.util.TimeZone; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-17 16:08 + * @description: 自定义 Jackson + **/ +@Configuration +public class JacksonConfig { + + @Bean + public ObjectMapper objectMapper() { + // 初始化一个 ObjectMapper 对象,用于自定义 Jackson 的行为 + ObjectMapper objectMapper = new ObjectMapper(); + + // 忽略未知属性 + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + // JavaTimeModule 用于指定序列化和反序列化规则 + JavaTimeModule javaTimeModule = new JavaTimeModule(); + + // 支持 LocalDateTime、LocalDate、LocalTime + javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); + javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); + javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss"))); + javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss"))); + // 支持 YearMonth + javaTimeModule.addSerializer(YearMonth.class, new YearMonthSerializer(DateTimeFormatter.ofPattern("yyyy-MM"))); + javaTimeModule.addDeserializer(YearMonth.class, new YearMonthDeserializer(DateTimeFormatter.ofPattern("yyyy-MM"))); + + objectMapper.registerModule(javaTimeModule); + + // 设置时区 + objectMapper.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); + + // 设置凡是为 null 的字段,返参中均不返回,请根据项目组约定是否开启 + // objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + + return objectMapper; + } +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/config/MybatisPlusConfig.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/config/MybatisPlusConfig.java new file mode 100644 index 0000000..c3d434d --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/config/MybatisPlusConfig.java @@ -0,0 +1,37 @@ +package com.quanxiaoha.weblog.common.config; + +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-22 16:52 + * @description: Mybatis Plus 配置文件 + **/ +@Configuration +@MapperScan("com.quanxiaoha.weblog.common.domain.mapper") +public class MybatisPlusConfig { + + /** + * 分页插件 + * @return + */ + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor(){ + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); + return interceptor; + } + + /** + * 自定义批量插入 SQL 注入器 + */ + @Bean + public InsertBatchSqlInjector insertBatchSqlInjector() { + return new InsertBatchSqlInjector(); + } +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/ArticleCategoryRelDO.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/ArticleCategoryRelDO.java new file mode 100644 index 0000000..3305af8 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/ArticleCategoryRelDO.java @@ -0,0 +1,30 @@ +package com.quanxiaoha.weblog.common.domain.dos; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-22 17:01 + * @description: 文章 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@TableName("t_article_category_rel") +public class ArticleCategoryRelDO { + + @TableId(type = IdType.AUTO) + private Long id; + + private Long articleId; + + private Long categoryId; +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/ArticleContentDO.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/ArticleContentDO.java new file mode 100644 index 0000000..106169d --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/ArticleContentDO.java @@ -0,0 +1,31 @@ +package com.quanxiaoha.weblog.common.domain.dos; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-22 17:01 + * @description: 文章内容 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@TableName("t_article_content") +public class ArticleContentDO { + + @TableId(type = IdType.AUTO) + private Long id; + + private Long articleId; + + private String content; + +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/ArticleDO.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/ArticleDO.java new file mode 100644 index 0000000..ebe3a7c --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/ArticleDO.java @@ -0,0 +1,42 @@ +package com.quanxiaoha.weblog.common.domain.dos; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-22 17:01 + * @description: 文章 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@TableName("t_article") +public class ArticleDO { + + @TableId(type = IdType.AUTO) + private Long id; + + private String title; + + private String cover; + + private String summary; + + private LocalDateTime createTime; + + private LocalDateTime updateTime; + + private Boolean isDeleted; + + private Long readNum; +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/ArticleTagRelDO.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/ArticleTagRelDO.java new file mode 100644 index 0000000..a10ff5f --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/ArticleTagRelDO.java @@ -0,0 +1,30 @@ +package com.quanxiaoha.weblog.common.domain.dos; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-22 17:01 + * @description: 文章 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@TableName("t_article_tag_rel") +public class ArticleTagRelDO { + + @TableId(type = IdType.AUTO) + private Long id; + + private Long articleId; + + private Long tagId; +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/BlogSettingsDO.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/BlogSettingsDO.java new file mode 100644 index 0000000..362d5dc --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/BlogSettingsDO.java @@ -0,0 +1,46 @@ +package com.quanxiaoha.weblog.common.domain.dos; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-22 17:01 + * @description: 博客设置 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@TableName("t_blog_settings") +public class BlogSettingsDO { + + @TableId(type = IdType.AUTO) + private Long id; + + private String logo; + + private String name; + + private String author; + + private String introduction; + + private String avatar; + + private String githubHomepage; + + private String csdnHomepage; + + private String giteeHomepage; + + private String zhihuHomepage; +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/CategoryDO.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/CategoryDO.java new file mode 100644 index 0000000..cd11f96 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/CategoryDO.java @@ -0,0 +1,36 @@ +package com.quanxiaoha.weblog.common.domain.dos; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-22 17:01 + * @description: 分类 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@TableName("t_category") +public class CategoryDO { + + @TableId(type = IdType.AUTO) + private Long id; + + private String name; + + private LocalDateTime createTime; + + private LocalDateTime updateTime; + + private Boolean isDeleted; +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/TagDO.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/TagDO.java new file mode 100644 index 0000000..2e8e1d5 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/TagDO.java @@ -0,0 +1,36 @@ +package com.quanxiaoha.weblog.common.domain.dos; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-22 17:01 + * @description: 标签 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@TableName("t_tag") +public class TagDO { + + @TableId(type = IdType.AUTO) + private Long id; + + private String name; + + private LocalDateTime createTime; + + private LocalDateTime updateTime; + + private Boolean isDeleted; +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/UserDO.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/UserDO.java new file mode 100644 index 0000000..bfa6ac2 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/UserDO.java @@ -0,0 +1,38 @@ +package com.quanxiaoha.weblog.common.domain.dos; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-22 17:01 + * @description: 用户 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@TableName("t_user") +public class UserDO { + + @TableId(type = IdType.AUTO) + private Long id; + + private String username; + + private String password; + + private LocalDateTime createTime; + + private LocalDateTime updateTime; + + private Boolean isDeleted; +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/UserRoleDO.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/UserRoleDO.java new file mode 100644 index 0000000..fbd6ed8 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/dos/UserRoleDO.java @@ -0,0 +1,20 @@ +package com.quanxiaoha.weblog.common.domain.dos; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Builder; +import lombok.Data; + +import java.util.Date; + +@Data +@Builder +@TableName("t_user_role") +public class UserRoleDO { + @TableId(type = IdType.AUTO) + private Long id; + private String username; + private String role; + private Date createTime; +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/ArticleCategoryRelMapper.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/ArticleCategoryRelMapper.java new file mode 100644 index 0000000..2d0ffb2 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/ArticleCategoryRelMapper.java @@ -0,0 +1,67 @@ +package com.quanxiaoha.weblog.common.domain.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.quanxiaoha.weblog.common.domain.dos.ArticleCategoryRelDO; + +import java.util.List; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-22 17:06 + * @description: 文章分类关联 + **/ +public interface ArticleCategoryRelMapper extends BaseMapper { + + /** + * 根据文章 ID 删除关联记录 + * @param articleId + * @return + */ + default int deleteByArticleId(Long articleId) { + return delete(Wrappers.lambdaQuery() + .eq(ArticleCategoryRelDO::getArticleId, articleId)); + } + + /** + * 根据文章 ID 查询 + * @param articleId + * @return + */ + default ArticleCategoryRelDO selectByArticleId(Long articleId) { + return selectOne(Wrappers.lambdaQuery() + .eq(ArticleCategoryRelDO::getArticleId, articleId)); + } + + /** + * 根据分类 ID 查询 + * @param categoryId + * @return + */ + default ArticleCategoryRelDO selectOneByCategoryId(Long categoryId) { + return selectOne(Wrappers.lambdaQuery() + .eq(ArticleCategoryRelDO::getCategoryId, categoryId) + .last("LIMIT 1")); + } + + /** + * 根据文章 ID 集合批量查询 + * @param articleIds + * @return + */ + default List selectByArticleIds(List articleIds) { + return selectList(Wrappers.lambdaQuery() + .in(ArticleCategoryRelDO::getArticleId, articleIds)); + } + + /** + * 根据分类 ID 查询所有的关联记录 + * @param categoryId + * @return + */ + default List selectListByCategoryId(Long categoryId) { + return selectList(Wrappers.lambdaQuery() + .eq(ArticleCategoryRelDO::getCategoryId, categoryId)); + } +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/ArticleContentMapper.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/ArticleContentMapper.java new file mode 100644 index 0000000..9fa2739 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/ArticleContentMapper.java @@ -0,0 +1,44 @@ +package com.quanxiaoha.weblog.common.domain.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.quanxiaoha.weblog.common.domain.dos.ArticleContentDO; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-22 17:06 + * @description: 文章 + **/ +public interface ArticleContentMapper extends BaseMapper { + + /** + * 根据文章 ID 删除记录 + * @param articleId + * @return + */ + default int deleteByArticleId(Long articleId) { + return delete(Wrappers.lambdaQuery() + .eq(ArticleContentDO::getArticleId, articleId)); + } + + /** + * 根据文章 ID 查询 + * @param articleId + * @return + */ + default ArticleContentDO selectByArticleId(Long articleId) { + return selectOne(Wrappers.lambdaQuery() + .eq(ArticleContentDO::getArticleId, articleId)); + } + + /** + * 通过文章 ID 更新 + * @param articleContentDO + */ + default int updateByArticleId(ArticleContentDO articleContentDO) { + return update(articleContentDO, + Wrappers.lambdaQuery() + .eq(ArticleContentDO::getArticleId, articleContentDO.getArticleId())); + } +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/ArticleMapper.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/ArticleMapper.java new file mode 100644 index 0000000..01e9a0b --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/ArticleMapper.java @@ -0,0 +1,98 @@ +package com.quanxiaoha.weblog.common.domain.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.quanxiaoha.weblog.common.domain.dos.ArticleDO; + +import java.time.LocalDate; +import java.util.List; +import java.util.Objects; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-22 17:06 + * @description: 文章 + **/ +public interface ArticleMapper extends BaseMapper { + + /** + * 分页查询 + * @param current + * @param size + * @param title + * @param startDate + * @param endDate + * @return + */ + default Page selectPageList(Long current, Long size, String title, LocalDate startDate, LocalDate endDate) { + // 分页对象(查询第几页、每页多少数据) + Page page = new Page<>(current, size); + + // 构建查询条件 + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery() + .like(StringUtils.isNotBlank(title), ArticleDO::getTitle, title) // like 模块查询 + .ge(Objects.nonNull(startDate), ArticleDO::getCreateTime, startDate) // 大于等于 startDate + .le(Objects.nonNull(endDate), ArticleDO::getCreateTime, endDate) // 小于等于 endDate + .orderByDesc(ArticleDO::getCreateTime); // 按创建时间倒叙 + + return selectPage(page, wrapper); + } + + /** + * 根据文章 ID 批量分页查询 + * @param current + * @param size + * @param articleIds + * @return + */ + default Page selectPageListByArticleIds(Long current, Long size, List articleIds) { + // 分页对象(查询第几页、每页多少数据) + Page page = new Page<>(current, size); + + // 构建查询条件 + LambdaQueryWrapper wrapper = Wrappers.lambdaQuery() + .in(ArticleDO::getId, articleIds) + .orderByDesc(ArticleDO::getCreateTime); // 按创建时间倒叙 + + return selectPage(page, wrapper); + } + + /** + * 查询上一篇文章 + * @param articleId + * @return + */ + default ArticleDO selectPreArticle(Long articleId) { + return selectOne(Wrappers.lambdaQuery() + .orderByAsc(ArticleDO::getId) // 按文章 ID 倒序排列 + .gt(ArticleDO::getId, articleId) // 查询比当前文章 ID 大的 + .last("limit 1")); // 第一条记录即为上一篇文章 + } + + /** + * 查询下一篇文章 + * @param articleId + * @return + */ + default ArticleDO selectNextArticle(Long articleId) { + return selectOne(Wrappers.lambdaQuery() + .orderByDesc(ArticleDO::getId) // 按文章 ID 倒序排列 + .lt(ArticleDO::getId, articleId) // 查询比当前文章 ID 小的 + .last("limit 1")); // 第一条记录即为下一篇文章 + } + + /** + * 阅读量+1 + * @param articleId + * @return + */ + default int increaseReadNum(Long articleId) { + return update(null, Wrappers.lambdaUpdate() + .setSql("read_num = read_num + 1") + .eq(ArticleDO::getId, articleId)); + } +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/ArticleTagRelMapper.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/ArticleTagRelMapper.java new file mode 100644 index 0000000..3039548 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/ArticleTagRelMapper.java @@ -0,0 +1,67 @@ +package com.quanxiaoha.weblog.common.domain.mapper; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.quanxiaoha.weblog.common.config.InsertBatchMapper; +import com.quanxiaoha.weblog.common.domain.dos.ArticleTagRelDO; + +import java.util.List; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-22 17:06 + * @description: 文章标签关联 + **/ +public interface ArticleTagRelMapper extends InsertBatchMapper { + + /** + * 根据文章 ID 删除关联记录 + * @param articleId + * @return + */ + default int deleteByArticleId(Long articleId) { + return delete(Wrappers.lambdaQuery() + .eq(ArticleTagRelDO::getArticleId, articleId)); + } + + /** + * 根据文章 ID 来查询 + * @param articleId + * @return + */ + default List selectByArticleId(Long articleId) { + return selectList(Wrappers.lambdaQuery() + .eq(ArticleTagRelDO::getArticleId, articleId)); + } + + /** + * 根据标签 ID 查询 + * @param tagId + * @return + */ + default ArticleTagRelDO selectOneByTagId(Long tagId) { + return selectOne(Wrappers.lambdaQuery() + .eq(ArticleTagRelDO::getTagId, tagId) + .last("LIMIT 1")); + } + + /** + * 根据文章 ID 集合批量查询 + * @param articleIds + * @return + */ + default List selectByArticleIds(List articleIds) { + return selectList(Wrappers.lambdaQuery() + .in(ArticleTagRelDO::getArticleId, articleIds)); + } + + /** + * 查询该标签 ID 下所有关联记录 + * @param tagId + * @return + */ + default List selectByTagId(Long tagId) { + return selectList(Wrappers.lambdaQuery() + .eq(ArticleTagRelDO::getTagId, tagId)); + } +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/BlogSettingsMapper.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/BlogSettingsMapper.java new file mode 100644 index 0000000..d7a70c9 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/BlogSettingsMapper.java @@ -0,0 +1,18 @@ +package com.quanxiaoha.weblog.common.domain.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.quanxiaoha.weblog.common.domain.dos.BlogSettingsDO; +import com.quanxiaoha.weblog.common.domain.dos.UserDO; + +import java.time.LocalDateTime; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-22 17:06 + * @description: TODO + **/ +public interface BlogSettingsMapper extends BaseMapper { +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/CategoryMapper.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/CategoryMapper.java new file mode 100644 index 0000000..9a7f701 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/CategoryMapper.java @@ -0,0 +1,54 @@ +package com.quanxiaoha.weblog.common.domain.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.quanxiaoha.weblog.common.domain.dos.CategoryDO; + +import java.time.LocalDate; +import java.util.List; +import java.util.Objects; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-22 17:06 + * @description: TODO + **/ +public interface CategoryMapper extends BaseMapper { + + /** + * 查询分类分页数据 + * @return + */ + default Page selectPageList(long current, long size, String name, LocalDate startDate, LocalDate endDate) { + // 分页对象(查询第几页、每页多少数据) + Page page = new Page<>(current, size); + + // 构建查询条件 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + wrapper + .like(StringUtils.isNotBlank(name), CategoryDO::getName, name.trim()) // like 模块查询 + .ge(Objects.nonNull(startDate), CategoryDO::getCreateTime, startDate) // 大于等于 startDate + .le(Objects.nonNull(endDate), CategoryDO::getCreateTime, endDate) // 小于等于 endDate + .orderByDesc(CategoryDO::getCreateTime); // 按创建时间倒叙 + + return selectPage(page, wrapper); + } + + /** + * 根据用户名查询 + * @param categoryName + * @return + */ + default CategoryDO selectByName(String categoryName) { + // 构建查询条件 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(CategoryDO::getName, categoryName); + + // 执行查询 + return selectOne(wrapper); + } +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/TagMapper.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/TagMapper.java new file mode 100644 index 0000000..c1a2c7a --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/TagMapper.java @@ -0,0 +1,68 @@ +package com.quanxiaoha.weblog.common.domain.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.quanxiaoha.weblog.common.domain.dos.TagDO; + +import java.time.LocalDate; +import java.util.List; +import java.util.Objects; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-22 17:06 + * @description: TODO + **/ +public interface TagMapper extends BaseMapper { + + /** + * 分页查询 + * @param current + * @param size + * @param name + * @param startDate + * @param endDate + * @return + */ + default Page selectPageList(long current, long size, String name, LocalDate startDate, LocalDate endDate) { + // 分页对象 + Page page = new Page<>(current, size); + + // 构建查询条件 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper + .like(Objects.nonNull(name), TagDO::getName, name) // 模糊查询 + .ge(Objects.nonNull(startDate), TagDO::getCreateTime, startDate) // 大于等于开始时间 + .le(Objects.nonNull(endDate), TagDO::getCreateTime, endDate) // 小于等于结束时间 + .orderByDesc(TagDO::getCreateTime); // order by create_time desc + + return selectPage(page, wrapper); + } + + /** + * 根据标签模糊查询 + * @param key + * @return + */ + default List selectByKey(String key) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + // 构造模糊查询的条件 + wrapper.like(TagDO::getName, key).orderByDesc(TagDO::getCreateTime); + + return selectList(wrapper); + } + + /** + * 根据标签 ID 批量查询 + * @param tagIds + * @return + */ + default List selectByIds(List tagIds) { + return selectList(Wrappers.lambdaQuery() + .in(TagDO::getId, tagIds)); + } +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/UserMapper.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/UserMapper.java new file mode 100644 index 0000000..e0f5cd8 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/UserMapper.java @@ -0,0 +1,33 @@ +package com.quanxiaoha.weblog.common.domain.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.quanxiaoha.weblog.common.domain.dos.UserDO; + +import java.time.LocalDateTime; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-22 17:06 + * @description: TODO + **/ +public interface UserMapper extends BaseMapper { + default UserDO findByUsername(String username) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(UserDO::getUsername, username); + return selectOne(wrapper); + } + + default int updatePasswordByUsername(String username, String password) { + LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); + // 设置要更新的字段 + wrapper.set(UserDO::getPassword, password); + wrapper.set(UserDO::getUpdateTime, LocalDateTime.now()); + // 更新条件 + wrapper.eq(UserDO::getUsername, username); + + return update(null, wrapper); + } +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/UserRoleMapper.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/UserRoleMapper.java new file mode 100644 index 0000000..553bd70 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/mapper/UserRoleMapper.java @@ -0,0 +1,27 @@ +package com.quanxiaoha.weblog.common.domain.mapper; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.quanxiaoha.weblog.common.domain.dos.UserRoleDO; + +import java.util.List; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-22 17:06 + * @description: TODO + **/ +public interface UserRoleMapper extends BaseMapper { + /** + * 根据用户名查询 + * @param username + * @return + */ + default List selectByUsername(String username) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(UserRoleDO::getUsername, username); + + return selectList(wrapper); + } +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/package-info.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/package-info.java new file mode 100644 index 0000000..948fa16 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/domain/package-info.java @@ -0,0 +1,7 @@ +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-22 16:57 + * @description: TODO + **/ +package com.quanxiaoha.weblog.common.domain; \ No newline at end of file diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/enums/ResponseCodeEnum.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/enums/ResponseCodeEnum.java new file mode 100644 index 0000000..6b168b8 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/enums/ResponseCodeEnum.java @@ -0,0 +1,43 @@ +package com.quanxiaoha.weblog.common.enums; + +import com.quanxiaoha.weblog.common.exception.BaseExceptionInterface; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-15 10:33 + * @description: 响应异常码 + **/ +@Getter +@AllArgsConstructor +public enum ResponseCodeEnum implements BaseExceptionInterface { + + // ----------- 通用异常状态码 ----------- + SYSTEM_ERROR("10000", "出错啦,后台小哥正在努力修复中..."), + PARAM_NOT_VALID("10001", "参数错误"), + + + // ----------- 业务异常状态码 ----------- + LOGIN_FAIL("20000", "登录失败"), + USERNAME_OR_PWD_ERROR("20001", "用户名或密码错误"), + UNAUTHORIZED("20002", "无访问权限,请先登录!"), + USERNAME_NOT_FOUND("20003", "该用户不存在"), + FORBIDDEN("20004", "演示账号仅支持查询操作!"), + CATEGORY_NAME_IS_EXISTED("20005", "该分类已存在,请勿重复添加!"), + TAG_CANT_DUPLICATE("20006", "请勿添加表中已存在的标签!"), + TAG_NOT_EXISTED("20007", "该标签不存在!"), + FILE_UPLOAD_FAILED("20008", "文件上传失败!"), + CATEGORY_NOT_EXISTED("20009", "提交的分类不存在!"), + ARTICLE_NOT_FOUND("20010", "该文章不存在!"), + CATEGORY_CAN_NOT_DELETE("20011", "该分类下包含文章,请先删除对应文章,才能删除!"), + TAG_CAN_NOT_DELETE("20012", "该标签下包含文章,请先删除对应文章,才能删除!"), + ; + + // 异常码 + private String errorCode; + // 错误信息 + private String errorMessage; + +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/exception/BaseExceptionInterface.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/exception/BaseExceptionInterface.java new file mode 100644 index 0000000..ad51d50 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/exception/BaseExceptionInterface.java @@ -0,0 +1,13 @@ +package com.quanxiaoha.weblog.common.exception; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-15 9:54 + * @description: 通用异常接口 + **/ +public interface BaseExceptionInterface { + String getErrorCode(); + + String getErrorMessage(); +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/exception/BizException.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/exception/BizException.java new file mode 100644 index 0000000..cf618c2 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/exception/BizException.java @@ -0,0 +1,24 @@ +package com.quanxiaoha.weblog.common.exception; + +import lombok.Getter; +import lombok.Setter; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-15 9:52 + * @description: 业务异常 + **/ +@Getter +@Setter +public class BizException extends RuntimeException { + // 异常码 + private String errorCode; + // 错误信息 + private String errorMessage; + + public BizException(BaseExceptionInterface baseExceptionInterface) { + this.errorCode = baseExceptionInterface.getErrorCode(); + this.errorMessage = baseExceptionInterface.getErrorMessage(); + } +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/exception/GlobalExceptionHandler.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..6cb4e03 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/exception/GlobalExceptionHandler.java @@ -0,0 +1,92 @@ +package com.quanxiaoha.weblog.common.exception; + +import com.quanxiaoha.weblog.common.enums.ResponseCodeEnum; +import com.quanxiaoha.weblog.common.utils.Response; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.context.properties.bind.BindResult; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; + +import javax.servlet.http.HttpServletRequest; +import java.util.Optional; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-15 10:14 + * @description: 全局异常处理 + **/ +@ControllerAdvice +@Slf4j +public class GlobalExceptionHandler { + + /** + * 捕获自定义业务异常 + * @return + */ + @ExceptionHandler({ BizException.class }) + @ResponseBody + public Response handleBizException(HttpServletRequest request, BizException e) { + log.warn("{} request fail, errorCode: {}, errorMessage: {}", request.getRequestURI(), e.getErrorCode(), e.getErrorMessage()); + return Response.fail(e); + } + + /** + * 捕获参数校验异常 + * @return + */ + @ExceptionHandler({ MethodArgumentNotValidException.class }) + @ResponseBody + public Response handleMethodArgumentNotValidException(HttpServletRequest request, MethodArgumentNotValidException e) { + // 参数错误异常码 + String errorCode = ResponseCodeEnum.PARAM_NOT_VALID.getErrorCode(); + + // 获取 BindingResult + BindingResult bindingResult = e.getBindingResult(); + + StringBuilder sb = new StringBuilder(); + + // 获取校验不通过的字段,并组合错误信息,格式为: email 邮箱格式不正确, 当前值: '123124qq.com'; + Optional.ofNullable(bindingResult.getFieldErrors()).ifPresent(errors -> { + errors.forEach(error -> + sb.append(error.getField()) + .append(" ") + .append(error.getDefaultMessage()) + .append(", 当前值: '") + .append(error.getRejectedValue()) + .append("'; ") + + ); + }); + + // 错误信息 + String errorMessage = sb.toString(); + + log.warn("{} request error, errorCode: {}, errorMessage: {}", request.getRequestURI(), errorCode, errorMessage); + + return Response.fail(errorCode, errorMessage); + } + + @ExceptionHandler({ AccessDeniedException.class }) + public void throwAccessDeniedException(AccessDeniedException e) throws AccessDeniedException { + log.info("============= 捕获到 AccessDeniedException"); + throw e; + } + + /** + * 其他类型异常 + * @param request + * @param e + * @return + */ + @ExceptionHandler({ Exception.class }) + @ResponseBody + public Response handleOtherException(HttpServletRequest request, Exception e) { + log.error("{} request error, ", request.getRequestURI(), e); + return Response.fail(ResponseCodeEnum.SYSTEM_ERROR); + } +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/model/BasePageQuery.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/model/BasePageQuery.java new file mode 100644 index 0000000..fd47028 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/model/BasePageQuery.java @@ -0,0 +1,21 @@ +package com.quanxiaoha.weblog.common.model; + +import lombok.Data; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-19 8:54 + * @description: TODO + **/ +@Data +public class BasePageQuery { + /** + * 当前页码, 默认第一页 + */ + private Long current = 1L; + /** + * 每页展示的数据数量,默认每页展示 10 条数据 + */ + private Long size = 10L; +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/model/package-info.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/model/package-info.java new file mode 100644 index 0000000..ab657b0 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/model/package-info.java @@ -0,0 +1,7 @@ +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-19 8:53 + * @description: TODO + **/ +package com.quanxiaoha.weblog.common.model; \ No newline at end of file diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/model/vo/SelectRspVO.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/model/vo/SelectRspVO.java new file mode 100644 index 0000000..56fa4cb --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/model/vo/SelectRspVO.java @@ -0,0 +1,28 @@ +package com.quanxiaoha.weblog.common.model.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023/9/20 16:02 + * @description: TODO + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class SelectRspVO { + /** + * Select 下拉列表的展示文字 + */ + private String label; + + /** + * Select 下拉列表的 value 值,如 ID 等 + */ + private Object value; +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/package-info.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/package-info.java new file mode 100644 index 0000000..f36a31e --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/package-info.java @@ -0,0 +1,7 @@ +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-10 9:20 + * @description: TODO + **/ +package com.quanxiaoha.weblog.common; \ No newline at end of file diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/utils/JsonUtil.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/utils/JsonUtil.java new file mode 100644 index 0000000..bc2f4c7 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/utils/JsonUtil.java @@ -0,0 +1,31 @@ +package com.quanxiaoha.weblog.common.utils; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-14 16:27 + * @description: JSON 工具类 + **/ +@Slf4j +public class JsonUtil { + + private static final ObjectMapper INSTANCE = new ObjectMapper(); + + public static String toJsonString(Object obj) { + try { + return INSTANCE.writeValueAsString(obj); + } catch (JsonProcessingException e) { + // todo Jackson 出参打印包含 Java 8 新日期出错问题 + return obj.toString(); + } + } + +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/utils/PageResponse.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/utils/PageResponse.java new file mode 100644 index 0000000..f9cb8b6 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/utils/PageResponse.java @@ -0,0 +1,60 @@ +package com.quanxiaoha.weblog.common.utils; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.quanxiaoha.weblog.common.exception.BaseExceptionInterface; +import com.quanxiaoha.weblog.common.exception.BizException; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; +import java.util.Objects; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-11 19:50 + * @description: 分页响应参数工具类 + **/ +@Data +public class PageResponse extends Response> { + + /** + * 总记录数 + */ + private long total = 0L; + + /** + * 每页显示的记录数,默认每页显示 10 条 + */ + private long size = 10L; + + /** + * 当前页码 + */ + private long current; + + /** + * 总页数 + */ + private long pages; + + /** + * 成功响应 + * @param page Mybatis Plus 提供的分页接口 + * @param data + * @return + * @param + */ + public static PageResponse success(IPage page, List data) { + PageResponse response = new PageResponse<>(); + response.setSuccess(true); + response.setCurrent(Objects.isNull(page) ? 1L : page.getCurrent()); + response.setSize(Objects.isNull(page) ? 10L : page.getSize()); + response.setPages(Objects.isNull(page) ? 0L : page.getPages()); + response.setTotal(Objects.isNull(page) ? 0L : page.getTotal()); + response.setData(data); + return response; + } + + +} diff --git a/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/utils/Response.java b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/utils/Response.java new file mode 100644 index 0000000..dfa1743 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/utils/Response.java @@ -0,0 +1,77 @@ +package com.quanxiaoha.weblog.common.utils; + +import com.quanxiaoha.weblog.common.exception.BaseExceptionInterface; +import com.quanxiaoha.weblog.common.exception.BizException; +import lombok.Data; + +import java.io.Serializable; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-11 19:50 + * @description: 响应参数工具类 + **/ +@Data +public class Response implements Serializable { + + // 是否成功,默认为 true + private boolean success = true; + // 响应消息 + private String message; + // 异常码 + private String errorCode; + // 响应数据 + private T data; + + // =================================== 成功响应 =================================== + public static Response success() { + Response response = new Response<>(); + return response; + } + + public static Response success(T data) { + Response response = new Response<>(); + response.setData(data); + return response; + } + + // =================================== 失败响应 =================================== + public static Response fail() { + Response response = new Response<>(); + response.setSuccess(false); + return response; + } + + public static Response fail(String errorMessage) { + Response response = new Response<>(); + response.setSuccess(false); + response.setMessage(errorMessage); + return response; + } + + public static Response fail(String errorCode, String errorMessage) { + Response response = new Response<>(); + response.setSuccess(false); + response.setErrorCode(errorCode); + response.setMessage(errorMessage); + return response; + } + + public static Response fail(BizException bizException) { + Response response = new Response<>(); + response.setSuccess(false); + response.setErrorCode(bizException.getErrorCode()); + response.setMessage(bizException.getErrorMessage()); + return response; + } + + public static Response fail(BaseExceptionInterface baseExceptionInterface) { + Response response = new Response<>(); + response.setSuccess(false); + response.setErrorCode(baseExceptionInterface.getErrorCode()); + response.setMessage(baseExceptionInterface.getErrorMessage()); + return response; + } + +} diff --git a/weblog-springboot-134/weblog-module-common/src/test/java/com/quanxiaoha/weblog/common/WeblogModuleCommonApplicationTests.java b/weblog-springboot-134/weblog-module-common/src/test/java/com/quanxiaoha/weblog/common/WeblogModuleCommonApplicationTests.java new file mode 100644 index 0000000..3f5ed89 --- /dev/null +++ b/weblog-springboot-134/weblog-module-common/src/test/java/com/quanxiaoha/weblog/common/WeblogModuleCommonApplicationTests.java @@ -0,0 +1,23 @@ +package com.quanxiaoha.weblog.common; + +import com.quanxiaoha.weblog.common.domain.dos.UserDO; +import com.quanxiaoha.weblog.common.domain.mapper.UserMapper; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.util.Date; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = WeblogModuleCommonApplicationTests.Application.class) +@Slf4j +class WeblogModuleCommonApplicationTests { + + public static class Application { + } + + @Test + public void test() { + } + +} diff --git a/weblog-springboot-134/weblog-module-jwt/.gitignore b/weblog-springboot-134/weblog-module-jwt/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/weblog-springboot-134/weblog-module-jwt/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/weblog-springboot-134/weblog-module-jwt/pom.xml b/weblog-springboot-134/weblog-module-jwt/pom.xml new file mode 100644 index 0000000..f538850 --- /dev/null +++ b/weblog-springboot-134/weblog-module-jwt/pom.xml @@ -0,0 +1,74 @@ + + + 4.0.0 + + com.quanxiaoha + weblog-springboot + ${revision} + + + com.quanxiaoha + weblog-module-jwt + weblog-module-jwt + weblog-module-jwt (JWT 模块,管理用户认证、鉴权) + + + + + org.projectlombok + lombok + true + + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + org.springframework.boot + spring-boot-starter-security + + + + + io.jsonwebtoken + jjwt-api + + + io.jsonwebtoken + jjwt-impl + + + io.jsonwebtoken + jjwt-jackson + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.apache.commons + commons-lang3 + + + + + com.quanxiaoha + weblog-module-common + + + + com.fasterxml.jackson.core + jackson-core + + + + diff --git a/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/config/JwtAuthenticationSecurityConfig.java b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/config/JwtAuthenticationSecurityConfig.java new file mode 100644 index 0000000..52d7c16 --- /dev/null +++ b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/config/JwtAuthenticationSecurityConfig.java @@ -0,0 +1,58 @@ +package com.quanxiaoha.weblog.jwt.config; + +import com.quanxiaoha.weblog.jwt.filter.JwtAuthenticationFilter; +import com.quanxiaoha.weblog.jwt.handler.RestAuthenticationFailureHandler; +import com.quanxiaoha.weblog.jwt.handler.RestAuthenticationSuccessHandler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.SecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.DefaultSecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-24 16:45 + * @description: 认证功能相关配置 + **/ +@Configuration +public class JwtAuthenticationSecurityConfig extends SecurityConfigurerAdapter { + + @Autowired + private RestAuthenticationSuccessHandler restAuthenticationSuccessHandler; + + @Autowired + private RestAuthenticationFailureHandler restAuthenticationFailureHandler; + + @Autowired + private PasswordEncoder passwordEncoder; + + @Autowired + private UserDetailsService userDetailsService; + + @Override + public void configure(HttpSecurity httpSecurity) throws Exception { + // 自定义的用于 JWT 身份验证的过滤器 + JwtAuthenticationFilter filter = new JwtAuthenticationFilter(); + filter.setAuthenticationManager(httpSecurity.getSharedObject(AuthenticationManager.class)); + + // 设置登录认证对应的处理类(成功处理、失败处理) + filter.setAuthenticationSuccessHandler(restAuthenticationSuccessHandler); + filter.setAuthenticationFailureHandler(restAuthenticationFailureHandler); + + // 直接使用 DaoAuthenticationProvider, 它是 Spring Security 提供的默认的身份验证提供者之一 + DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); + // 设置 userDetailService,用于获取用户的详细信息 + provider.setUserDetailsService(userDetailsService); + // 设置加密算法 + provider.setPasswordEncoder(passwordEncoder); + httpSecurity.authenticationProvider(provider); + // 将这个过滤器添加到 UsernamePasswordAuthenticationFilter 之前执行 + httpSecurity.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class); + } +} diff --git a/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/config/PasswordEncoderConfig.java b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/config/PasswordEncoderConfig.java new file mode 100644 index 0000000..f2614d9 --- /dev/null +++ b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/config/PasswordEncoderConfig.java @@ -0,0 +1,27 @@ +package com.quanxiaoha.weblog.jwt.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Component; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-24 9:17 + * @description: 密码加密 + **/ +@Component +public class PasswordEncoderConfig { + + @Bean + public PasswordEncoder passwordEncoder() { + // BCrypt 是一种安全且适合密码存储的哈希算法,它在进行哈希时会自动加入“盐”,增加密码的安全性。 + return new BCryptPasswordEncoder(); + } + + public static void main(String[] args) { + BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); + System.out.println(encoder.encode("111")); + } +} diff --git a/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/exception/UsernameOrPasswordNullException.java b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/exception/UsernameOrPasswordNullException.java new file mode 100644 index 0000000..e50ba96 --- /dev/null +++ b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/exception/UsernameOrPasswordNullException.java @@ -0,0 +1,19 @@ +package com.quanxiaoha.weblog.jwt.exception; + +import org.springframework.security.core.AuthenticationException; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-24 17:11 + * @description: 用户名或者密码为空异常 + **/ +public class UsernameOrPasswordNullException extends AuthenticationException { + public UsernameOrPasswordNullException(String msg, Throwable cause) { + super(msg, cause); + } + + public UsernameOrPasswordNullException(String msg) { + super(msg); + } +} diff --git a/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/filter/JwtAuthenticationFilter.java b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/filter/JwtAuthenticationFilter.java new file mode 100644 index 0000000..736c914 --- /dev/null +++ b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/filter/JwtAuthenticationFilter.java @@ -0,0 +1,59 @@ +package com.quanxiaoha.weblog.jwt.filter; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.quanxiaoha.weblog.jwt.exception.UsernameOrPasswordNullException; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Objects; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-24 9:36 + * @description: 用户认证过滤器 + **/ +@Slf4j +public class JwtAuthenticationFilter extends AbstractAuthenticationProcessingFilter { + + + /** + * 指定用户登录的访问地址 + */ + public JwtAuthenticationFilter() { + super(new AntPathRequestMatcher("/login", "POST")); + } + + @Override + public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { + ObjectMapper mapper = new ObjectMapper(); + // 解析提交的 JSON 数据 + JsonNode jsonNode = mapper.readTree(request.getInputStream()); + JsonNode usernameNode = jsonNode.get("username"); + JsonNode passwordNode = jsonNode.get("password"); + + // 判断用户名、密码是否为空 + if (Objects.isNull(usernameNode) || Objects.isNull(passwordNode) + || StringUtils.isBlank(usernameNode.textValue()) || StringUtils.isBlank(passwordNode.textValue())) { + throw new UsernameOrPasswordNullException("用户名或密码不能为空"); + } + + String username = usernameNode.textValue(); + String password = passwordNode.textValue(); + + // 将用户名、密码封装到 Token 中 + UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken + = new UsernamePasswordAuthenticationToken(username, password); + return getAuthenticationManager().authenticate(usernamePasswordAuthenticationToken); + } +} diff --git a/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/filter/TokenAuthenticationFilter.java b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/filter/TokenAuthenticationFilter.java new file mode 100644 index 0000000..c7bf8ed --- /dev/null +++ b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/filter/TokenAuthenticationFilter.java @@ -0,0 +1,102 @@ +package com.quanxiaoha.weblog.jwt.filter; + +import com.quanxiaoha.weblog.jwt.utils.JwtTokenHelper; +import io.jsonwebtoken.ExpiredJwtException; +import io.jsonwebtoken.MalformedJwtException; +import io.jsonwebtoken.UnsupportedJwtException; +import io.jsonwebtoken.security.SignatureException; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Objects; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-27 16:58 + * @description: Token 校验过滤器 + **/ +@Slf4j +public class TokenAuthenticationFilter extends OncePerRequestFilter { + + @Value("${jwt.tokenPrefix}") + private String tokenPrefix; + + @Value("${jwt.tokenHeaderKey}") + private String tokenHeaderKey; + + @Autowired + private JwtTokenHelper jwtTokenHelper; + + @Autowired + private UserDetailsService userDetailsService; + + @Autowired + private AuthenticationEntryPoint authenticationEntryPoint; + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + String requestURI = request.getRequestURI(); + + if (requestURI.startsWith("/admin")) { + // 从请求头中获取 key 为 Authorization 的值 + String header = request.getHeader(tokenHeaderKey); + + // 判断 value 值是否以 Bearer 开头 + if (StringUtils.startsWith(header, tokenPrefix)) { + // 截取 Token 令牌 + String token = StringUtils.substring(header, 7); + log.info("Token: {}", token); + + // 判空 Token + if (StringUtils.isNotBlank(token)) { + try { + // 校验 Token 是否可用, 若解析异常,针对不同异常做出不同的响应参数 + jwtTokenHelper.validateToken(token); + } catch (SignatureException | MalformedJwtException | UnsupportedJwtException | IllegalArgumentException e) { + // 抛出异常,统一让 AuthenticationEntryPoint 处理响应参数 + authenticationEntryPoint.commence(request, response, new AuthenticationServiceException("Token 不可用")); + return; + } catch (ExpiredJwtException e) { + authenticationEntryPoint.commence(request, response, new AuthenticationServiceException("Token 已失效")); + return; + } + + // 从 Token 中解析出用户名 + String username = jwtTokenHelper.getUsernameByToken(token); + + if (StringUtils.isNotBlank(username) + && Objects.isNull(SecurityContextHolder.getContext().getAuthentication())) { + // 根据用户名获取用户详情信息 + UserDetails userDetails = userDetailsService.loadUserByUsername(username); + + // 将用户信息存入 authentication,方便后续校验 + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, + userDetails.getAuthorities()); + authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); + // 将 authentication 存入 ThreadLocal,方便后续获取用户信息 + SecurityContextHolder.getContext().setAuthentication(authentication); + } + } + } + } + + // 继续执行写一个过滤器 + filterChain.doFilter(request, response); + } +} diff --git a/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/handler/RestAccessDeniedHandler.java b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/handler/RestAccessDeniedHandler.java new file mode 100644 index 0000000..7575512 --- /dev/null +++ b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/handler/RestAccessDeniedHandler.java @@ -0,0 +1,32 @@ +package com.quanxiaoha.weblog.jwt.handler; + +import com.quanxiaoha.weblog.common.enums.ResponseCodeEnum; +import com.quanxiaoha.weblog.common.utils.Response; +import com.quanxiaoha.weblog.jwt.utils.ResultUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-27 17:32 + * @description: 登录成功访问收保护的资源,但是权限不够 + **/ +@Slf4j +@Component +public class RestAccessDeniedHandler implements AccessDeniedHandler { + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { + log.warn("登录成功访问收保护的资源,但是权限不够: ", accessDeniedException); + // 预留,后面引入多角色时会用到 + ResultUtil.fail(response, Response.fail(ResponseCodeEnum.FORBIDDEN)); + } +} diff --git a/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/handler/RestAuthenticationEntryPoint.java b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/handler/RestAuthenticationEntryPoint.java new file mode 100644 index 0000000..850ca63 --- /dev/null +++ b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/handler/RestAuthenticationEntryPoint.java @@ -0,0 +1,38 @@ +package com.quanxiaoha.weblog.jwt.handler; + +import com.quanxiaoha.weblog.common.enums.ResponseCodeEnum; +import com.quanxiaoha.weblog.common.utils.Response; +import com.quanxiaoha.weblog.jwt.utils.ResultUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.InsufficientAuthenticationException; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-27 17:27 + * @description: 用户未登录访问受保护的资源 + **/ +@Slf4j +@Component +public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint { + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { + log.warn("用户未登录访问受保护的资源: ", authException); + if (authException instanceof InsufficientAuthenticationException) { + ResultUtil.fail(response, HttpStatus.UNAUTHORIZED.value(), Response.fail(ResponseCodeEnum.UNAUTHORIZED)); + } + + ResultUtil.fail(response, HttpStatus.UNAUTHORIZED.value(), Response.fail(authException.getMessage())); + } +} diff --git a/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/handler/RestAuthenticationFailureHandler.java b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/handler/RestAuthenticationFailureHandler.java new file mode 100644 index 0000000..82ff738 --- /dev/null +++ b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/handler/RestAuthenticationFailureHandler.java @@ -0,0 +1,43 @@ +package com.quanxiaoha.weblog.jwt.handler; + +import com.quanxiaoha.weblog.common.enums.ResponseCodeEnum; +import com.quanxiaoha.weblog.common.utils.Response; +import com.quanxiaoha.weblog.jwt.exception.UsernameOrPasswordNullException; +import com.quanxiaoha.weblog.jwt.utils.ResultUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.InternalAuthenticationServiceException; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-24 15:19 + * @description: 认证失败处理器 + **/ +@Component +@Slf4j +public class RestAuthenticationFailureHandler implements AuthenticationFailureHandler { + @Override + public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { + log.warn("AuthenticationException: ", exception); + if (exception instanceof UsernameOrPasswordNullException) { + // 用户名或密码为空 + ResultUtil.fail(response, Response.fail(exception.getMessage())); + } else if (exception instanceof BadCredentialsException) { + // 用户名或密码错误 + ResultUtil.fail(response, Response.fail(ResponseCodeEnum.USERNAME_OR_PWD_ERROR)); + } + + // 登录失败 + ResultUtil.fail(response, Response.fail(ResponseCodeEnum.LOGIN_FAIL)); + } +} diff --git a/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/handler/RestAuthenticationSuccessHandler.java b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/handler/RestAuthenticationSuccessHandler.java new file mode 100644 index 0000000..d57015a --- /dev/null +++ b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/handler/RestAuthenticationSuccessHandler.java @@ -0,0 +1,47 @@ +package com.quanxiaoha.weblog.jwt.handler; + +import com.quanxiaoha.weblog.common.utils.Response; +import com.quanxiaoha.weblog.jwt.model.LoginRspVO; +import com.quanxiaoha.weblog.jwt.utils.JwtTokenHelper; +import com.quanxiaoha.weblog.jwt.utils.ResultUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.web.authentication.AuthenticationFailureHandler; +import org.springframework.security.web.authentication.AuthenticationSuccessHandler; +import org.springframework.stereotype.Component; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-24 15:19 + * @description: 认证成功处理器 + **/ +@Component +@Slf4j +public class RestAuthenticationSuccessHandler implements AuthenticationSuccessHandler { + @Autowired + private JwtTokenHelper jwtTokenHelper; + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { + // 从 authentication 对象中获取用户的 UserDetails 实例,这里是获取用户的用户名 + UserDetails userDetails = (UserDetails) authentication.getPrincipal(); + + // 通过用户名生成 Token + String username = userDetails.getUsername(); + String token = jwtTokenHelper.generateToken(username); + + // 返回 Token + LoginRspVO loginRspVO = LoginRspVO.builder().token(token).build(); + + ResultUtil.ok(response, Response.success(loginRspVO)); + } +} diff --git a/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/model/LoginRspVO.java b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/model/LoginRspVO.java new file mode 100644 index 0000000..b8c8e3e --- /dev/null +++ b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/model/LoginRspVO.java @@ -0,0 +1,25 @@ +package com.quanxiaoha.weblog.jwt.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-24 9:43 + * @description: 用户登录 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class LoginRspVO { + + /** + * Token 值 + */ + private String token; + +} diff --git a/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/service/UserDetailServiceImpl.java b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/service/UserDetailServiceImpl.java new file mode 100644 index 0000000..f9f72af --- /dev/null +++ b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/service/UserDetailServiceImpl.java @@ -0,0 +1,63 @@ +package com.quanxiaoha.weblog.jwt.service; + +import com.quanxiaoha.weblog.common.domain.dos.UserDO; +import com.quanxiaoha.weblog.common.domain.dos.UserRoleDO; +import com.quanxiaoha.weblog.common.domain.mapper.UserMapper; +import com.quanxiaoha.weblog.common.domain.mapper.UserRoleMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-24 9:14 + * @description: TODO + **/ +@Service +@Slf4j +public class UserDetailServiceImpl implements UserDetailsService { + + @Autowired + private UserMapper userMapper; + @Autowired + private UserRoleMapper userRoleMapper; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + // 从数据库中查询 + UserDO userDO = userMapper.findByUsername(username); + + // 判断用户是否存在 + if (Objects.isNull(userDO)) { + throw new UsernameNotFoundException("该用户不存在"); + } + + // 用户角色 + List roleDOS = userRoleMapper.selectByUsername(username); + + String[] roleArr = null; + + if (!CollectionUtils.isEmpty(roleDOS)) { + List roles = roleDOS.stream().map(p -> p.getRole()).collect(Collectors.toList()); + roleArr = roles.toArray(new String[roles.size()]); + } + + // authorities 用于指定角色 + return User.withUsername(userDO.getUsername()) + .password(userDO.getPassword()) + .authorities(roleArr) + .build(); + } +} diff --git a/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/utils/JwtTokenHelper.java b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/utils/JwtTokenHelper.java new file mode 100644 index 0000000..3e67020 --- /dev/null +++ b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/utils/JwtTokenHelper.java @@ -0,0 +1,146 @@ +package com.quanxiaoha.weblog.jwt.utils; + +import io.jsonwebtoken.*; +import io.jsonwebtoken.security.Keys; +import io.jsonwebtoken.security.SignatureException; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.authentication.CredentialsExpiredException; +import org.springframework.stereotype.Component; + +import java.security.Key; +import java.sql.Date; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Base64; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-24 8:16 + * @description: JWT Token 工具类 + **/ +@Component +public class JwtTokenHelper implements InitializingBean { + + /** + * 签发人 + */ + @Value("${jwt.issuer}") + private String issuer; + + /** + * Token 失效时间(分钟) + */ + @Value("${jwt.tokenExpireTime}") + private Long tokenExpireTime; + /** + * 秘钥 + */ + private Key key; + + /** + * JWT 解析 + */ + private JwtParser jwtParser; + + /** + * 解码配置文件中配置的 Base 64 编码 key 为秘钥 + * @param base64Key + */ + @Value("${jwt.secret}") + public void setBase64Key(String base64Key) { + key = Keys.hmacShaKeyFor(Base64.getDecoder().decode(base64Key)); + } + + + /** + * 初始化 JwtParser + * @throws Exception + */ + @Override + public void afterPropertiesSet() throws Exception { + // 考虑到不同服务器之间可能存在时钟偏移,setAllowedClockSkewSeconds 用于设置能够容忍的最大的时钟误差 + jwtParser = Jwts.parserBuilder().requireIssuer(issuer) + .setSigningKey(key).setAllowedClockSkewSeconds(10) + .build(); + } + + /** + * 生成 Token + * @param username + * @return + */ + public String generateToken(String username) { + LocalDateTime now = LocalDateTime.now(); + // 设置 Token 失效时间 + LocalDateTime expireTime = now.plusMinutes(tokenExpireTime); + + return Jwts.builder().setSubject(username) + .setIssuer(issuer) + .setIssuedAt(Date.from(now.atZone(ZoneId.systemDefault()).toInstant())) + .setExpiration(Date.from(expireTime.atZone(ZoneId.systemDefault()).toInstant())) + .signWith(key) + .compact(); + } + + /** + * 解析 Token + * @param token + * @return + */ + public Jws parseToken(String token) { + try { + return jwtParser.parseClaimsJws(token); + } catch (SignatureException | MalformedJwtException | UnsupportedJwtException | IllegalArgumentException e) { + throw new BadCredentialsException("Token 不可用", e); + } catch (ExpiredJwtException e) { + throw new CredentialsExpiredException("Token 失效", e); + } + } + + /** + * 校验 Token 是否可用 + * @param token + * @return + */ + public void validateToken(String token) { + jwtParser.parseClaimsJws(token); + } + + /** + * 解析 Token 获取用户名 + * @param token + * @return + */ + public String getUsernameByToken(String token) { + try { + Claims claims = jwtParser.parseClaimsJws(token).getBody(); + String username = claims.getSubject(); + return username; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * 生成一个 Base64 的安全秘钥 + * @return + */ + private static String generateBase64Key() { + // 生成安全秘钥 + Key secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS512); + + // 将密钥进行 Base64 编码 + String base64Key = Base64.getEncoder().encodeToString(secretKey.getEncoded()); + + return base64Key; + } + + public static void main(String[] args) { + String key = generateBase64Key(); + System.out.println("key: " + key); + } +} diff --git a/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/utils/ResultUtil.java b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/utils/ResultUtil.java new file mode 100644 index 0000000..2102e41 --- /dev/null +++ b/weblog-springboot-134/weblog-module-jwt/src/main/java/com/quanxiaoha/weblog/jwt/utils/ResultUtil.java @@ -0,0 +1,73 @@ +package com.quanxiaoha.weblog.jwt.utils; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.quanxiaoha.weblog.common.utils.Response; +import org.springframework.http.HttpStatus; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-04-18 15:05 + * @description: 响参工具 + **/ +public class ResultUtil { + + /** + * 成功响参 + * @param response + * @param result + * @throws IOException + */ + public static void ok(HttpServletResponse response, Response result) throws IOException { + response.setCharacterEncoding("UTF-8"); + response.setStatus(HttpStatus.OK.value()); + response.setContentType("application/json"); + PrintWriter writer = response.getWriter(); + + ObjectMapper mapper = new ObjectMapper(); + writer.write(mapper.writeValueAsString(result)); + writer.flush(); + writer.close(); + } + + /** + * 失败响参 + * @param response + * @param result + * @throws IOException + */ + public static void fail(HttpServletResponse response, Response result) throws IOException { + response.setCharacterEncoding("UTF-8"); + response.setStatus(HttpStatus.OK.value()); + response.setContentType("application/json"); + PrintWriter writer = response.getWriter(); + + ObjectMapper mapper = new ObjectMapper(); + writer.write(mapper.writeValueAsString(result)); + writer.flush(); + writer.close(); + } + + /** + * 失败响参 + * @param response + * @param status 可指定响应码,如 401 等 + * @param result + * @throws IOException + */ + public static void fail(HttpServletResponse response, int status, Response result) throws IOException { + response.setCharacterEncoding("UTF-8"); + response.setStatus(status); + response.setContentType("application/json"); + PrintWriter writer = response.getWriter(); + + ObjectMapper mapper = new ObjectMapper(); + writer.write(mapper.writeValueAsString(result)); + writer.flush(); + writer.close(); + } +} diff --git a/weblog-springboot-134/weblog-module-jwt/src/test/java/com/quanxiaoha/weblog/jwt/WeblogModuleJwtApplicationTests.java b/weblog-springboot-134/weblog-module-jwt/src/test/java/com/quanxiaoha/weblog/jwt/WeblogModuleJwtApplicationTests.java new file mode 100644 index 0000000..1e5a164 --- /dev/null +++ b/weblog-springboot-134/weblog-module-jwt/src/test/java/com/quanxiaoha/weblog/jwt/WeblogModuleJwtApplicationTests.java @@ -0,0 +1,16 @@ +package com.quanxiaoha.weblog.jwt; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = WeblogModuleJwtApplicationTests.Application.class) +class WeblogModuleJwtApplicationTests { + + public static class Application { + } + + @Test + void contextLoads() { + } + +} diff --git a/weblog-springboot-134/weblog-web/.gitignore b/weblog-springboot-134/weblog-web/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/weblog-springboot-134/weblog-web/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/weblog-springboot-134/weblog-web/pom.xml b/weblog-springboot-134/weblog-web/pom.xml new file mode 100644 index 0000000..47720e2 --- /dev/null +++ b/weblog-springboot-134/weblog-web/pom.xml @@ -0,0 +1,104 @@ + + + 4.0.0 + + + com.quanxiaoha + weblog-springboot + ${revision} + + + com.quanxiaoha + weblog-web + weblog-web + weblog-web (入口项目,负责博客前台展示相关功能,打包也放在这个模块负责) + + + + com.quanxiaoha + weblog-module-common + + + + com.quanxiaoha + weblog-module-admin + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.projectlombok + lombok + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + org.springframework.boot + spring-boot-starter-validation + + + + + com.github.xiaoymin + knife4j-openapi2-spring-boot-starter + + + + + org.commonmark + commonmark + + + org.commonmark + commonmark-ext-gfm-tables + + + org.commonmark + commonmark-ext-heading-anchor + + + org.commonmark + commonmark-ext-image-attributes + + + org.commonmark + commonmark-ext-task-list-items + + + + + + + + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + + + + diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/WeblogWebApplication.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/WeblogWebApplication.java new file mode 100644 index 0000000..ff25423 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/WeblogWebApplication.java @@ -0,0 +1,15 @@ +package com.quanxiaoha.weblog.web; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@ComponentScan({"com.quanxiaoha.weblog.*"}) // 多模块项目中,必需手动指定扫描 com.quanxiaoha.weblog 包下面的所有类 +public class WeblogWebApplication { + + public static void main(String[] args) { + SpringApplication.run(WeblogWebApplication.class, args); + } + +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/config/Knife4jConfig.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/config/Knife4jConfig.java new file mode 100644 index 0000000..e0edb26 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/config/Knife4jConfig.java @@ -0,0 +1,53 @@ +package com.quanxiaoha.weblog.web.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.service.Contact; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-08-16 7:53 + * @description: Knife4j 配置 + **/ +@Configuration +@EnableSwagger2WebMvc +@Profile("dev") // 只在 dev 环境中开启 +public class Knife4jConfig { + + @Bean("webApi") + public Docket createApiDoc() { + Docket docket = new Docket(DocumentationType.SWAGGER_2) + .apiInfo(buildApiInfo()) + // 分组名称 + .groupName("Web 前台接口") + .select() + // 这里指定 Controller 扫描包路径 + .apis(RequestHandlerSelectors.basePackage("com.quanxiaoha.weblog.web.controller")) + .paths(PathSelectors.any()) + .build(); + return docket; + } + + /** + * 构建 API 信息 + * @return + */ + private ApiInfo buildApiInfo() { + return new ApiInfoBuilder() + .title("Weblog 博客前台接口文档") // 标题 + .description("Weblog 是一款由 Spring Boot + Vue 3.2 + Vite 4.3 开发的前后端分离博客。") // 描述 + .termsOfServiceUrl("https://www.quanxiaoha.com/") // API 服务条款 + .contact(new Contact("犬小哈", "https://www.quanxiaoha.com", "871361652@qq.com")) // 联系人 + .version("1.0") // 版本号 + .build(); + } +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/controller/ArchiveController.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/controller/ArchiveController.java new file mode 100644 index 0000000..10159e8 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/controller/ArchiveController.java @@ -0,0 +1,35 @@ +package com.quanxiaoha.weblog.web.controller; + +import com.quanxiaoha.weblog.common.aspect.ApiOperationLog; +import com.quanxiaoha.weblog.common.utils.Response; +import com.quanxiaoha.weblog.web.model.vo.archive.FindArchiveArticlePageListReqVO; +import com.quanxiaoha.weblog.web.service.ArchiveService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:01 + * @description: 文章归档 + **/ +@RestController +@Api(tags = "文章归档") +public class ArchiveController { + + @Autowired + private ArchiveService archiveService; + + @PostMapping("/archive/list") + @ApiOperation(value = "获取文章归档分页数据") + @ApiOperationLog(description = "获取文章归档分页数据") + public Response findArchivePageList(@RequestBody @Validated FindArchiveArticlePageListReqVO findArchiveArticlePageListReqVO) { + return archiveService.findArchivePageList(findArchiveArticlePageListReqVO); + } + +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/controller/ArticleController.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/controller/ArticleController.java new file mode 100644 index 0000000..a89a4db --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/controller/ArticleController.java @@ -0,0 +1,45 @@ +package com.quanxiaoha.weblog.web.controller; + +import com.quanxiaoha.weblog.common.aspect.ApiOperationLog; +import com.quanxiaoha.weblog.common.utils.Response; +import com.quanxiaoha.weblog.web.model.vo.article.FindArticleDetailReqVO; +import com.quanxiaoha.weblog.web.model.vo.article.FindIndexArticlePageListReqVO; +import com.quanxiaoha.weblog.web.service.ArticleService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:01 + * @description: 文章 + **/ +@RestController +@RequestMapping("/article") +@Api(tags = "文章") +public class ArticleController { + + @Autowired + private ArticleService articleService; + + @PostMapping("/list") + @ApiOperation(value = "获取首页文章分页数据") + @ApiOperationLog(description = "获取首页文章分页数据") + public Response findArticlePageList(@RequestBody FindIndexArticlePageListReqVO findIndexArticlePageListReqVO) { + return articleService.findArticlePageList(findIndexArticlePageListReqVO); + } + + + @PostMapping("/detail") + @ApiOperation(value = "获取文章详情") + @ApiOperationLog(description = "获取文章详情") + public Response findArticleDetail(@RequestBody FindArticleDetailReqVO findArticleDetailReqVO) { + return articleService.findArticleDetail(findArticleDetailReqVO); + } + +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/controller/BlogSettingsController.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/controller/BlogSettingsController.java new file mode 100644 index 0000000..f2d1a89 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/controller/BlogSettingsController.java @@ -0,0 +1,34 @@ +package com.quanxiaoha.weblog.web.controller; + +import com.quanxiaoha.weblog.common.aspect.ApiOperationLog; +import com.quanxiaoha.weblog.common.utils.Response; +import com.quanxiaoha.weblog.web.service.BlogSettingsService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:01 + * @description: 博客设置 + **/ +@RestController +@RequestMapping("/blog/settings") +@Api(tags = "博客设置") +public class BlogSettingsController { + + @Autowired + private BlogSettingsService blogSettingsService; + + @PostMapping("/detail") + @ApiOperation(value = "前台获取博客详情") + @ApiOperationLog(description = "前台获取博客详情") + public Response findDetail() { + return blogSettingsService.findDetail(); + } + +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/controller/CategoryController.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/controller/CategoryController.java new file mode 100644 index 0000000..c6f6e2e --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/controller/CategoryController.java @@ -0,0 +1,44 @@ +package com.quanxiaoha.weblog.web.controller; + +import com.quanxiaoha.weblog.common.aspect.ApiOperationLog; +import com.quanxiaoha.weblog.common.utils.Response; +import com.quanxiaoha.weblog.web.model.vo.category.FindCategoryArticlePageListReqVO; +import com.quanxiaoha.weblog.web.service.CategoryService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:01 + * @description: 分类 + **/ +@RestController +@RequestMapping("/category") +@Api(tags = "分类") +public class CategoryController { + + @Autowired + private CategoryService categoryService; + + @PostMapping("/list") + @ApiOperation(value = "前台获取分类列表") + @ApiOperationLog(description = "前台获取分类列表") + public Response findCategoryList() { + return categoryService.findCategoryList(); + } + + @PostMapping("/article/list") + @ApiOperation(value = "前台获取分类下文章分页数据") + @ApiOperationLog(description = "前台获取分类下文章分页数据") + public Response findCategoryArticlePageList(@RequestBody @Validated FindCategoryArticlePageListReqVO findCategoryArticlePageListReqVO) { + return categoryService.findCategoryArticlePageList(findCategoryArticlePageListReqVO); + } + +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/controller/TagController.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/controller/TagController.java new file mode 100644 index 0000000..2c2a543 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/controller/TagController.java @@ -0,0 +1,44 @@ +package com.quanxiaoha.weblog.web.controller; + +import com.quanxiaoha.weblog.common.aspect.ApiOperationLog; +import com.quanxiaoha.weblog.common.utils.Response; +import com.quanxiaoha.weblog.web.model.vo.tag.FindTagArticlePageListReqVO; +import com.quanxiaoha.weblog.web.service.TagService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:01 + * @description: 标签 + **/ +@RestController +@RequestMapping("/tag") +@Api(tags = "标签") +public class TagController { + + @Autowired + private TagService tagService; + + @PostMapping("/list") + @ApiOperation(value = "前台获取标签列表") + @ApiOperationLog(description = "前台获取标签列表") + public Response findTagList() { + return tagService.findTagList(); + } + + @PostMapping("/article/list") + @ApiOperation(value = "前台获取标签下文章列表") + @ApiOperationLog(description = "前台获取标签下文章列表") + public Response findTagPageList(@RequestBody @Validated FindTagArticlePageListReqVO findTagArticlePageListReqVO) { + return tagService.findTagPageList(findTagArticlePageListReqVO); + } + +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/convert/ArticleConvert.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/convert/ArticleConvert.java new file mode 100644 index 0000000..ba94f33 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/convert/ArticleConvert.java @@ -0,0 +1,57 @@ +package com.quanxiaoha.weblog.web.convert; + +import com.quanxiaoha.weblog.common.domain.dos.ArticleDO; +import com.quanxiaoha.weblog.web.model.vo.archive.FindArchiveArticleRspVO; +import com.quanxiaoha.weblog.web.model.vo.article.FindIndexArticlePageListRspVO; +import com.quanxiaoha.weblog.web.model.vo.category.FindCategoryArticlePageListRspVO; +import com.quanxiaoha.weblog.web.model.vo.tag.FindTagArticlePageListRspVO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023/10/8 14:57 + * @description: 文章转换 + **/ +@Mapper +public interface ArticleConvert { + /** + * 初始化 convert 实例 + */ + ArticleConvert INSTANCE = Mappers.getMapper(ArticleConvert.class); + + /** + * ArticleDO -> FindIndexArticlePageListRspVO + * @param bean + * @return + */ + @Mapping(target = "createDate", expression = "java(java.time.LocalDate.from(bean.getCreateTime()))") + FindIndexArticlePageListRspVO convertDO2VO(ArticleDO bean); + + /** + * ArticleDO -> FindArchiveArticleRspVO + * @param bean + * @return + */ + @Mapping(target = "createDate", expression = "java(java.time.LocalDate.from(bean.getCreateTime()))") + @Mapping(target = "createMonth", expression = "java(java.time.YearMonth.from(bean.getCreateTime()))") + FindArchiveArticleRspVO convertDO2ArchiveArticleVO(ArticleDO bean); + + /** + * ArticleDO -> FindCategoryArticlePageListRspVO + * @param bean + * @return + */ + @Mapping(target = "createDate", expression = "java(java.time.LocalDate.from(bean.getCreateTime()))") + FindCategoryArticlePageListRspVO convertDO2CategoryArticleVO(ArticleDO bean); + + /** + * ArticleDO -> FindTagArticlePageListRspVO + * @param bean + * @return + */ + @Mapping(target = "createDate", expression = "java(java.time.LocalDate.from(bean.getCreateTime()))") + FindTagArticlePageListRspVO convertDO2TagArticleVO(ArticleDO bean); +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/convert/BlogSettingsConvert.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/convert/BlogSettingsConvert.java new file mode 100644 index 0000000..4bece43 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/convert/BlogSettingsConvert.java @@ -0,0 +1,28 @@ +package com.quanxiaoha.weblog.web.convert; + +import com.quanxiaoha.weblog.common.domain.dos.BlogSettingsDO; +import com.quanxiaoha.weblog.web.model.vo.blogsettings.FindBlogSettingsDetailRspVO; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023/10/8 14:57 + * @description: 博客设置转换 + **/ +@Mapper +public interface BlogSettingsConvert { + /** + * 初始化 convert 实例 + */ + BlogSettingsConvert INSTANCE = Mappers.getMapper(BlogSettingsConvert.class); + + /** + * 将 DO 转化为 VO + * @param bean + * @return + */ + FindBlogSettingsDetailRspVO convertDO2VO(BlogSettingsDO bean); + +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/markdown/MarkdownHelper.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/markdown/MarkdownHelper.java new file mode 100644 index 0000000..f94b882 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/markdown/MarkdownHelper.java @@ -0,0 +1,70 @@ +package com.quanxiaoha.weblog.web.markdown; + +import com.quanxiaoha.weblog.web.markdown.provider.NofollowLinkAttributeProvider; +import com.quanxiaoha.weblog.web.markdown.renderer.ImageNodeRenderer; +import org.commonmark.Extension; +import org.commonmark.ext.gfm.tables.TablesExtension; +import org.commonmark.ext.heading.anchor.HeadingAnchorExtension; +import org.commonmark.ext.image.attributes.ImageAttributesExtension; +import org.commonmark.ext.task.list.items.TaskListItemsExtension; +import org.commonmark.node.Node; +import org.commonmark.parser.Parser; +import org.commonmark.renderer.html.HtmlRenderer; + +import java.util.Arrays; +import java.util.List; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023/10/31 21:16 + * @description: Markdown 转换器 + **/ +public class MarkdownHelper { + + /** + * Markdown 解析器 + */ + private final static Parser PARSER; + /** + * HTML 渲染器 + */ + private final static HtmlRenderer HTML_RENDERER; + + /** + * 初始化 + */ + static { + // Markdown 拓展 + List extensions = Arrays.asList( + TablesExtension.create(), // 表格拓展 + HeadingAnchorExtension.create(), // 标题锚定项 + ImageAttributesExtension.create(), // 图片宽高 + TaskListItemsExtension.create() // 任务列表 + ); + + PARSER = Parser.builder().extensions(extensions).build(); + HTML_RENDERER = HtmlRenderer.builder() + .extensions(extensions) + .attributeProviderFactory(context -> new NofollowLinkAttributeProvider()) + .nodeRendererFactory(context -> new ImageNodeRenderer(context)) + .build(); + } + + + /** + * 将 Markdown 转换成 HTML + * @param markdown + * @return + */ + public static String convertMarkdown2Html(String markdown) { + Node document = PARSER.parse(markdown); + return HTML_RENDERER.render(document); + } + + public static void main(String[] args) { + String markdown = "[http://www.quanxiaoha.com1](http://www.quanxiaoha.com \"http://www.quanxiaoha.com2\")"; + System.out.println(MarkdownHelper.convertMarkdown2Html(markdown)); + } + +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/markdown/provider/NofollowLinkAttributeProvider.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/markdown/provider/NofollowLinkAttributeProvider.java new file mode 100644 index 0000000..317472d --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/markdown/provider/NofollowLinkAttributeProvider.java @@ -0,0 +1,34 @@ +package com.quanxiaoha.weblog.web.markdown.provider; + +import org.commonmark.node.Link; +import org.commonmark.node.Node; +import org.commonmark.renderer.html.AttributeProvider; + +import java.util.Map; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023/11/1 14:40 + * @description: 超链接添加 nofollow + **/ +public class NofollowLinkAttributeProvider implements AttributeProvider { + + /** + * 网站域名(上线后需要改成自己的域名) + */ + private final static String DOMAIN = "www.quanxiaoha.com"; + + @Override + public void setAttributes(Node node, String tagName, Map attributes) { + if (node instanceof Link) { + Link linkNode = (Link) node; + // 获取链接地址 + String href = linkNode.getDestination(); + // 如果链接不是自己域名,则添加 rel="nofollow" 属性 + if (!href.contains(DOMAIN)) { + attributes.put("rel", "nofollow"); + } + } + } +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/markdown/renderer/ImageNodeRenderer.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/markdown/renderer/ImageNodeRenderer.java new file mode 100644 index 0000000..ddc36f8 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/markdown/renderer/ImageNodeRenderer.java @@ -0,0 +1,107 @@ +package com.quanxiaoha.weblog.web.markdown.renderer; + +import com.google.common.collect.Sets; +import org.apache.commons.lang3.StringUtils; +import org.commonmark.ext.image.attributes.ImageAttributes; +import org.commonmark.node.Image; +import org.commonmark.node.Node; +import org.commonmark.renderer.NodeRenderer; +import org.commonmark.renderer.html.HtmlNodeRendererContext; +import org.commonmark.renderer.html.HtmlWriter; +import org.springframework.util.CollectionUtils; + +import java.util.Objects; +import java.util.Set; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023/11/1 14:41 + * @description: 自定义图片节点渲染器 + **/ +public class ImageNodeRenderer implements NodeRenderer { + + private final HtmlWriter html; + + /** + * 图片宽 + */ + private final static String KEY_WIDTH = "width"; + /** + * 图片高 + */ + private final static String KEY_HEIGHT = "height"; + + + public ImageNodeRenderer(HtmlNodeRendererContext context) { + this.html = context.getWriter(); + } + + @Override + public Set> getNodeTypes() { + // 指定想要自定义渲染的节点,这里指定为图片 Image + // return Sets.newHashSet(Image.class, Link.class); + return Sets.newHashSet(Image.class); + } + + @Override + public void render(Node node) { + if (node instanceof Image) { + // We only handle one type as per getNodeTypes, so we can just cast it here. + Image img = (Image) node; + html.line(); + // 图片链接 + String imgUrl = img.getDestination(); + // 图片描述 + String imgTitle = img.getTitle(); + + // 拼接 HTML 结构 + StringBuilder sb = new StringBuilder("图片标题"); + + if (StringUtils.isNotBlank(imgTitle)) { + // 图文下方文字 + sb.append(String.format("%s", imgTitle)); + } + + // 设置 HTML 内容 + html.raw(sb.toString()); + html.line(); + } + // else if (node instanceof Link) { + // Link link = (Link) node; + // System.out.println(link); + // html.line(); + // html.tag("a"); + // String title = link.getTitle(); + // + // if (StringUtils.isNotBlank(title)) { + // + // } + // + // String url = link.getDestination(); + // html.text(url); + // html.tag("/a"); + // html.line(); + // } + } +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/archive/FindArchiveArticlePageListReqVO.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/archive/FindArchiveArticlePageListReqVO.java new file mode 100644 index 0000000..93c001c --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/archive/FindArchiveArticlePageListReqVO.java @@ -0,0 +1,18 @@ +package com.quanxiaoha.weblog.web.model.vo.archive; + +import com.quanxiaoha.weblog.common.model.BasePageQuery; +import io.swagger.annotations.ApiModel; +import lombok.Builder; +import lombok.Data; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 文章归档 + **/ +@Data +@Builder +@ApiModel(value = "文章归档分页 VO") +public class FindArchiveArticlePageListReqVO extends BasePageQuery { +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/archive/FindArchiveArticlePageListRspVO.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/archive/FindArchiveArticlePageListRspVO.java new file mode 100644 index 0000000..d54251d --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/archive/FindArchiveArticlePageListRspVO.java @@ -0,0 +1,29 @@ +package com.quanxiaoha.weblog.web.model.vo.archive; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.YearMonth; +import java.util.List; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 文章归档 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class FindArchiveArticlePageListRspVO { + /** + * 归档的月份 + */ + private YearMonth month; + + private List articles; + +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/archive/FindArchiveArticleRspVO.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/archive/FindArchiveArticleRspVO.java new file mode 100644 index 0000000..5a03f53 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/archive/FindArchiveArticleRspVO.java @@ -0,0 +1,34 @@ +package com.quanxiaoha.weblog.web.model.vo.archive; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; +import java.time.YearMonth; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 归档列表中的文章 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class FindArchiveArticleRspVO { + private Long id; + private String cover; + private String title; + /** + * 发布日期 + */ + private LocalDate createDate; + + /** + * 发布的月份(此字段不需要展示在前端,主要用于按月份分组使用) + */ + private YearMonth createMonth; +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/article/FindArticleDetailReqVO.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/article/FindArticleDetailReqVO.java new file mode 100644 index 0000000..0fdf3c3 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/article/FindArticleDetailReqVO.java @@ -0,0 +1,25 @@ +package com.quanxiaoha.weblog.web.model.vo.article; + +import io.swagger.annotations.ApiModel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 文章详情 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +@ApiModel(value = "查询文章详情 VO") +public class FindArticleDetailReqVO { + /** + * 文章 ID + */ + private Long articleId; +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/article/FindArticleDetailRspVO.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/article/FindArticleDetailRspVO.java new file mode 100644 index 0000000..96c4d7e --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/article/FindArticleDetailRspVO.java @@ -0,0 +1,59 @@ +package com.quanxiaoha.weblog.web.model.vo.article; + +import com.quanxiaoha.weblog.web.model.vo.tag.FindTagListRspVO; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 文章详情 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class FindArticleDetailRspVO { + /** + * 文章标题 + */ + private String title; + /** + * 文章正文(HTML) + */ + private String content; + /** + * 发布时间 + */ + private LocalDateTime createTime; + /** + * 分类 ID + */ + private Long categoryId; + /** + * 分类名称 + */ + private String categoryName; + /** + * 阅读量 + */ + private Long readNum; + /** + * 标签集合 + */ + private List tags; + /** + * 上一篇文章 + */ + private FindPreNextArticleRspVO preArticle; + /** + * 下一篇文章 + */ + private FindPreNextArticleRspVO nextArticle; +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/article/FindIndexArticlePageListReqVO.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/article/FindIndexArticlePageListReqVO.java new file mode 100644 index 0000000..6bf83f4 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/article/FindIndexArticlePageListReqVO.java @@ -0,0 +1,18 @@ +package com.quanxiaoha.weblog.web.model.vo.article; + +import com.quanxiaoha.weblog.common.model.BasePageQuery; +import io.swagger.annotations.ApiModel; +import lombok.Builder; +import lombok.Data; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 首页-文章分页 + **/ +@Data +@Builder +@ApiModel(value = "首页查询文章分页 VO") +public class FindIndexArticlePageListReqVO extends BasePageQuery { +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/article/FindIndexArticlePageListRspVO.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/article/FindIndexArticlePageListRspVO.java new file mode 100644 index 0000000..2364626 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/article/FindIndexArticlePageListRspVO.java @@ -0,0 +1,38 @@ +package com.quanxiaoha.weblog.web.model.vo.article; + +import com.quanxiaoha.weblog.web.model.vo.category.FindCategoryListRspVO; +import com.quanxiaoha.weblog.web.model.vo.tag.FindTagListRspVO; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; +import java.util.List; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 首页-文章分页 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class FindIndexArticlePageListRspVO { + private Long id; + private String cover; + private String title; + private LocalDate createDate; + private String summary; + /** + * 文章分类 + */ + private FindCategoryListRspVO category; + + /** + * 文章标签 + */ + private List tags; +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/article/FindPreNextArticleRspVO.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/article/FindPreNextArticleRspVO.java new file mode 100644 index 0000000..f3743de --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/article/FindPreNextArticleRspVO.java @@ -0,0 +1,28 @@ +package com.quanxiaoha.weblog.web.model.vo.article; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 文章上下页 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class FindPreNextArticleRspVO { + /** + * 文章 ID + */ + private Long articleId; + + /** + * 文章标题 + */ + private String articleTitle; +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/blogsettings/FindBlogSettingsDetailRspVO.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/blogsettings/FindBlogSettingsDetailRspVO.java new file mode 100644 index 0000000..e2181d8 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/blogsettings/FindBlogSettingsDetailRspVO.java @@ -0,0 +1,28 @@ +package com.quanxiaoha.weblog.web.model.vo.blogsettings; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 博客设置详情 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class FindBlogSettingsDetailRspVO { + private String logo; + private String name; + private String author; + private String introduction; + private String avatar; + private String githubHomepage; + private String csdnHomepage; + private String giteeHomepage; + private String zhihuHomepage; +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/category/FindCategoryArticlePageListReqVO.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/category/FindCategoryArticlePageListReqVO.java new file mode 100644 index 0000000..54bea83 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/category/FindCategoryArticlePageListReqVO.java @@ -0,0 +1,29 @@ +package com.quanxiaoha.weblog.web.model.vo.category; + +import com.quanxiaoha.weblog.common.model.BasePageQuery; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 分类文章 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class FindCategoryArticlePageListReqVO extends BasePageQuery { + + /** + * 分类 ID + */ + @NotNull(message = "分类 ID 不能为空") + private Long id; + +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/category/FindCategoryArticlePageListRspVO.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/category/FindCategoryArticlePageListRspVO.java new file mode 100644 index 0000000..d25c51c --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/category/FindCategoryArticlePageListRspVO.java @@ -0,0 +1,29 @@ +package com.quanxiaoha.weblog.web.model.vo.category; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 分类文章 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class FindCategoryArticlePageListRspVO { + private Long id; + private String cover; + private String title; + /** + * 发布日期 + */ + private LocalDate createDate; + +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/category/FindCategoryListRspVO.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/category/FindCategoryListRspVO.java new file mode 100644 index 0000000..25078cd --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/category/FindCategoryListRspVO.java @@ -0,0 +1,21 @@ +package com.quanxiaoha.weblog.web.model.vo.category; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-04-19 16:06 + * @description: 分类 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class FindCategoryListRspVO { + private Long id; + private String name; +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/tag/FindTagArticlePageListReqVO.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/tag/FindTagArticlePageListReqVO.java new file mode 100644 index 0000000..acef09b --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/tag/FindTagArticlePageListReqVO.java @@ -0,0 +1,29 @@ +package com.quanxiaoha.weblog.web.model.vo.tag; + +import com.quanxiaoha.weblog.common.model.BasePageQuery; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 标签下文章列表 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class FindTagArticlePageListReqVO extends BasePageQuery { + + /** + * 标签 ID + */ + @NotNull(message = "标签 ID 不能为空") + private Long id; + +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/tag/FindTagArticlePageListRspVO.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/tag/FindTagArticlePageListRspVO.java new file mode 100644 index 0000000..fe37636 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/tag/FindTagArticlePageListRspVO.java @@ -0,0 +1,29 @@ +package com.quanxiaoha.weblog.web.model.vo.tag; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:07 + * @description: 标签下文章列表 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class FindTagArticlePageListRspVO { + private Long id; + private String cover; + private String title; + /** + * 发布日期 + */ + private LocalDate createDate; + +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/tag/FindTagListRspVO.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/tag/FindTagListRspVO.java new file mode 100644 index 0000000..1b0b142 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/model/vo/tag/FindTagListRspVO.java @@ -0,0 +1,21 @@ +package com.quanxiaoha.weblog.web.model.vo.tag; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-04-19 16:06 + * @description: 标签 + **/ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class FindTagListRspVO { + private Long id; + private String name; +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/ArchiveService.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/ArchiveService.java new file mode 100644 index 0000000..d1aea58 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/ArchiveService.java @@ -0,0 +1,19 @@ +package com.quanxiaoha.weblog.web.service; + +import com.quanxiaoha.weblog.common.utils.Response; +import com.quanxiaoha.weblog.web.model.vo.archive.FindArchiveArticlePageListReqVO; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:03 + * @description: 归档文章 + **/ +public interface ArchiveService { + /** + * 获取文章归档分页数据 + * @param findArchiveArticlePageListReqVO + * @return + */ + Response findArchivePageList(FindArchiveArticlePageListReqVO findArchiveArticlePageListReqVO); +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/ArticleService.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/ArticleService.java new file mode 100644 index 0000000..c6b6b48 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/ArticleService.java @@ -0,0 +1,28 @@ +package com.quanxiaoha.weblog.web.service; + +import com.quanxiaoha.weblog.common.utils.Response; +import com.quanxiaoha.weblog.web.model.vo.article.FindArticleDetailReqVO; +import com.quanxiaoha.weblog.web.model.vo.article.FindIndexArticlePageListReqVO; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:03 + * @description: 文章 + **/ +public interface ArticleService { + /** + * 获取首页文章分页数据 + * @param findIndexArticlePageListReqVO + * @return + */ + Response findArticlePageList(FindIndexArticlePageListReqVO findIndexArticlePageListReqVO); + + /** + * 获取文章详情 + * @param findArticleDetailReqVO + * @return + */ + Response findArticleDetail(FindArticleDetailReqVO findArticleDetailReqVO); + +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/BlogSettingsService.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/BlogSettingsService.java new file mode 100644 index 0000000..552864d --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/BlogSettingsService.java @@ -0,0 +1,17 @@ +package com.quanxiaoha.weblog.web.service; + +import com.quanxiaoha.weblog.common.utils.Response; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:03 + * @description: 博客设置 + **/ +public interface BlogSettingsService { + /** + * 获取博客设置信息 + * @return + */ + Response findDetail(); +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/CategoryService.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/CategoryService.java new file mode 100644 index 0000000..650be56 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/CategoryService.java @@ -0,0 +1,25 @@ +package com.quanxiaoha.weblog.web.service; + +import com.quanxiaoha.weblog.common.utils.Response; +import com.quanxiaoha.weblog.web.model.vo.category.FindCategoryArticlePageListReqVO; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:03 + * @description: 分类 + **/ +public interface CategoryService { + /** + * 获取分类列表 + * @return + */ + Response findCategoryList(); + + /** + * 获取分类下文章分页数据 + * @param findCategoryArticlePageListReqVO + * @return + */ + Response findCategoryArticlePageList(FindCategoryArticlePageListReqVO findCategoryArticlePageListReqVO); +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/TagService.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/TagService.java new file mode 100644 index 0000000..7783ed1 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/TagService.java @@ -0,0 +1,25 @@ +package com.quanxiaoha.weblog.web.service; + +import com.quanxiaoha.weblog.common.utils.Response; +import com.quanxiaoha.weblog.web.model.vo.tag.FindTagArticlePageListReqVO; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:03 + * @description: 分类 + **/ +public interface TagService { + /** + * 获取标签列表 + * @return + */ + Response findTagList(); + + /** + * 获取标签下文章分页列表 + * @param findTagArticlePageListReqVO + * @return + */ + Response findTagPageList(FindTagArticlePageListReqVO findTagArticlePageListReqVO); +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/impl/ArchiveServiceImpl.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/impl/ArchiveServiceImpl.java new file mode 100644 index 0000000..00a9949 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/impl/ArchiveServiceImpl.java @@ -0,0 +1,70 @@ +package com.quanxiaoha.weblog.web.service.impl; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.google.common.collect.Lists; +import com.quanxiaoha.weblog.common.domain.dos.ArticleDO; +import com.quanxiaoha.weblog.common.domain.mapper.ArticleMapper; +import com.quanxiaoha.weblog.common.utils.PageResponse; +import com.quanxiaoha.weblog.common.utils.Response; +import com.quanxiaoha.weblog.web.convert.ArticleConvert; +import com.quanxiaoha.weblog.web.model.vo.archive.FindArchiveArticlePageListReqVO; +import com.quanxiaoha.weblog.web.model.vo.archive.FindArchiveArticlePageListRspVO; +import com.quanxiaoha.weblog.web.model.vo.archive.FindArchiveArticleRspVO; +import com.quanxiaoha.weblog.web.service.ArchiveService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.time.YearMonth; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:03 + * @description: 文章归档 + **/ +@Service +@Slf4j +public class ArchiveServiceImpl implements ArchiveService { + + @Autowired + private ArticleMapper articleMapper; + + /** + * 获取文章归档分页数据 + * + * @param findArchiveArticlePageListReqVO + * @return + */ + @Override + public Response findArchivePageList(FindArchiveArticlePageListReqVO findArchiveArticlePageListReqVO) { + Long current = findArchiveArticlePageListReqVO.getCurrent(); + Long size = findArchiveArticlePageListReqVO.getSize(); + + // 分页查询 + IPage page = articleMapper.selectPageList(current, size, null, null, null); + List articleDOS = page.getRecords(); + + List vos = Lists.newArrayList(); + if (!CollectionUtils.isEmpty(articleDOS)) { + // DO 转 VO + List archiveArticleRspVOS = articleDOS.stream() + .map(articleDO -> ArticleConvert.INSTANCE.convertDO2ArchiveArticleVO(articleDO)) + .collect(Collectors.toList()); + + // 按创建的月份进行分组 + Map> map = archiveArticleRspVOS.stream().collect(Collectors.groupingBy(FindArchiveArticleRspVO::getCreateMonth)); + // 使用 TreeMap 按月份倒序排列 + Map> sortedMap = new TreeMap<>(Collections.reverseOrder()); + sortedMap.putAll(map); + + // 遍历排序后的 Map,将其转换为归档 VO + sortedMap.forEach((k, v) -> vos.add(FindArchiveArticlePageListRspVO.builder().month(k).articles(v).build())); + } + + return PageResponse.success(page, vos); + } +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/impl/ArticleServiceImpl.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/impl/ArticleServiceImpl.java new file mode 100644 index 0000000..24d76cf --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/impl/ArticleServiceImpl.java @@ -0,0 +1,217 @@ +package com.quanxiaoha.weblog.web.service.impl; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.google.common.collect.Lists; +import com.quanxiaoha.weblog.admin.event.ReadArticleEvent; +import com.quanxiaoha.weblog.common.domain.dos.*; +import com.quanxiaoha.weblog.common.domain.mapper.*; +import com.quanxiaoha.weblog.common.enums.ResponseCodeEnum; +import com.quanxiaoha.weblog.common.exception.BizException; +import com.quanxiaoha.weblog.common.utils.PageResponse; +import com.quanxiaoha.weblog.common.utils.Response; +import com.quanxiaoha.weblog.web.convert.ArticleConvert; +import com.quanxiaoha.weblog.web.markdown.MarkdownHelper; +import com.quanxiaoha.weblog.web.model.vo.article.*; +import com.quanxiaoha.weblog.web.model.vo.category.FindCategoryListRspVO; +import com.quanxiaoha.weblog.web.model.vo.tag.FindTagListRspVO; +import com.quanxiaoha.weblog.web.service.ArticleService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:03 + * @description: 文章 + **/ +@Service +@Slf4j +public class ArticleServiceImpl implements ArticleService { + + @Autowired + private ArticleMapper articleMapper; + @Autowired + private ArticleContentMapper articleContentMapper; + @Autowired + private CategoryMapper categoryMapper; + @Autowired + private ArticleCategoryRelMapper articleCategoryRelMapper; + @Autowired + private TagMapper tagMapper; + @Autowired + private ArticleTagRelMapper articleTagRelMapper; + @Autowired + private ApplicationEventPublisher eventPublisher; + + /** + * 获取首页文章分页数据 + * + * @param findIndexArticlePageListReqVO + * @return + */ + @Override + public Response findArticlePageList(FindIndexArticlePageListReqVO findIndexArticlePageListReqVO) { + Long current = findIndexArticlePageListReqVO.getCurrent(); + Long size = findIndexArticlePageListReqVO.getSize(); + + // 第一步:分页查询文章主体记录 + Page articleDOPage = articleMapper.selectPageList(current, size, null, null, null); + + // 返回的分页数据 + List articleDOS = articleDOPage.getRecords(); + + List vos = null; + if (!CollectionUtils.isEmpty(articleDOS)) { + // 文章 DO 转 VO + vos = articleDOS.stream() + .map(articleDO -> ArticleConvert.INSTANCE.convertDO2VO(articleDO)) + .collect(Collectors.toList()); + + // 拿到所有文章的 ID 集合 + List articleIds = articleDOS.stream().map(ArticleDO::getId).collect(Collectors.toList()); + + // 第二步:设置文章所属分类 + // 查询所有分类 + List categoryDOS = categoryMapper.selectList(Wrappers.emptyWrapper()); + // 转 Map, 方便后续根据分类 ID 拿到对应的分类名称 + Map categoryIdNameMap = categoryDOS.stream().collect(Collectors.toMap(CategoryDO::getId, CategoryDO::getName)); + + // 根据文章 ID 批量查询所有关联记录 + List articleCategoryRelDOS = articleCategoryRelMapper.selectByArticleIds(articleIds); + + vos.forEach(vo -> { + Long currArticleId = vo.getId(); + // 过滤出当前文章对应的关联数据 + Optional optional = articleCategoryRelDOS.stream().filter(rel -> Objects.equals(rel.getArticleId(), currArticleId)).findAny(); + + // 若不为空 + if (optional.isPresent()) { + ArticleCategoryRelDO articleCategoryRelDO = optional.get(); + Long categoryId = articleCategoryRelDO.getCategoryId(); + // 通过分类 ID 从 map 中拿到对应的分类名称 + String categoryName = categoryIdNameMap.get(categoryId); + + FindCategoryListRspVO findCategoryListRspVO = FindCategoryListRspVO.builder() + .id(categoryId) + .name(categoryName) + .build(); + // 设置到当前 vo 类中 + vo.setCategory(findCategoryListRspVO); + } + }); + + // 第三步:设置文章标签 + // 查询所有标签 + List tagDOS = tagMapper.selectList(Wrappers.emptyWrapper()); + // 转 Map, 方便后续根据标签 ID 拿到对应的标签名称 + Map mapIdNameMap = tagDOS.stream().collect(Collectors.toMap(TagDO::getId, TagDO::getName)); + + // 拿到所有文章的标签关联记录 + List articleTagRelDOS = articleTagRelMapper.selectByArticleIds(articleIds); + vos.forEach(vo -> { + Long currArticleId = vo.getId(); + // 过滤出当前文章的标签关联记录 + List articleTagRelDOList = articleTagRelDOS.stream().filter(rel -> Objects.equals(rel.getArticleId(), currArticleId)).collect(Collectors.toList()); + + List findTagListRspVOS = Lists.newArrayList(); + // 将关联记录 DO 转 VO, 并设置对应的标签名称 + articleTagRelDOList.forEach(articleTagRelDO -> { + Long tagId = articleTagRelDO.getTagId(); + String tagName = mapIdNameMap.get(tagId); + + FindTagListRspVO findTagListRspVO = FindTagListRspVO.builder() + .id(tagId) + .name(tagName) + .build(); + findTagListRspVOS.add(findTagListRspVO); + }); + // 设置转换后的标签数据 + vo.setTags(findTagListRspVOS); + }); + } + + return PageResponse.success(articleDOPage, vos); + } + + /** + * 获取文章详情 + * + * @param findArticleDetailReqVO + * @return + */ + @Override + public Response findArticleDetail(FindArticleDetailReqVO findArticleDetailReqVO) { + Long articleId = findArticleDetailReqVO.getArticleId(); + + ArticleDO articleDO = articleMapper.selectById(articleId); + + // 判断文章是否存在 + if (Objects.isNull(articleDO)) { + log.warn("==> 该文章不存在, articleId: {}", articleId); + throw new BizException(ResponseCodeEnum.ARTICLE_NOT_FOUND); + } + + // 查询正文 + ArticleContentDO articleContentDO = articleContentMapper.selectByArticleId(articleId); + + // DO 转 VO + FindArticleDetailRspVO vo = FindArticleDetailRspVO.builder() + .title(articleDO.getTitle()) + .createTime(articleDO.getCreateTime()) + .content(MarkdownHelper.convertMarkdown2Html(articleContentDO.getContent())) + .readNum(articleDO.getReadNum()) + .build(); + + // 查询所属分类 + ArticleCategoryRelDO articleCategoryRelDO = articleCategoryRelMapper.selectByArticleId(articleId); + CategoryDO categoryDO = categoryMapper.selectById(articleCategoryRelDO.getCategoryId()); + vo.setCategoryId(categoryDO.getId()); + vo.setCategoryName(categoryDO.getName()); + + // 查询标签 + List articleTagRelDOS = articleTagRelMapper.selectByArticleId(articleId); + List tagIds = articleTagRelDOS.stream().map(ArticleTagRelDO::getTagId).collect(Collectors.toList()); + List tagDOS = tagMapper.selectByIds(tagIds); + + // 标签 DO 转 VO + List tagVOS = tagDOS.stream() + .map(tagDO -> FindTagListRspVO.builder().id(tagDO.getId()).name(tagDO.getName()).build()) + .collect(Collectors.toList()); + vo.setTags(tagVOS); + + // 上一篇 + ArticleDO preArticleDO = articleMapper.selectPreArticle(articleId); + if (Objects.nonNull(preArticleDO)) { + FindPreNextArticleRspVO preArticleVO = FindPreNextArticleRspVO.builder() + .articleId(preArticleDO.getId()) + .articleTitle(preArticleDO.getTitle()) + .build(); + vo.setPreArticle(preArticleVO); + } + + // 下一篇 + ArticleDO nextArticleDO = articleMapper.selectNextArticle(articleId); + if (Objects.nonNull(nextArticleDO)) { + FindPreNextArticleRspVO nextArticleVO = FindPreNextArticleRspVO.builder() + .articleId(nextArticleDO.getId()) + .articleTitle(nextArticleDO.getTitle()) + .build(); + vo.setNextArticle(nextArticleVO); + } + + // 发布文章阅读事件 + eventPublisher.publishEvent(new ReadArticleEvent(this, articleId)); + + return Response.success(vo); + } +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/impl/BlogSettingsServiceImpl.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/impl/BlogSettingsServiceImpl.java new file mode 100644 index 0000000..5487fa7 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/impl/BlogSettingsServiceImpl.java @@ -0,0 +1,40 @@ +package com.quanxiaoha.weblog.web.service.impl; + +import com.quanxiaoha.weblog.common.domain.dos.BlogSettingsDO; +import com.quanxiaoha.weblog.common.domain.mapper.BlogSettingsMapper; +import com.quanxiaoha.weblog.common.utils.Response; +import com.quanxiaoha.weblog.web.convert.BlogSettingsConvert; +import com.quanxiaoha.weblog.web.model.vo.blogsettings.FindBlogSettingsDetailRspVO; +import com.quanxiaoha.weblog.web.service.BlogSettingsService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:03 + * @description: 博客设置 + **/ +@Service +@Slf4j +public class BlogSettingsServiceImpl implements BlogSettingsService { + + @Autowired + private BlogSettingsMapper blogSettingsMapper; + + /** + * 获取博客设置信息 + * + * @return + */ + @Override + public Response findDetail() { + // 查询博客设置信息(约定的 ID 为 1) + BlogSettingsDO blogSettingsDO = blogSettingsMapper.selectById(1L); + // DO 转 VO + FindBlogSettingsDetailRspVO vo = BlogSettingsConvert.INSTANCE.convertDO2VO(blogSettingsDO); + + return Response.success(vo); + } +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/impl/CategoryServiceImpl.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/impl/CategoryServiceImpl.java new file mode 100644 index 0000000..5aaaeb3 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/impl/CategoryServiceImpl.java @@ -0,0 +1,115 @@ +package com.quanxiaoha.weblog.web.service.impl; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.quanxiaoha.weblog.common.domain.dos.ArticleCategoryRelDO; +import com.quanxiaoha.weblog.common.domain.dos.ArticleDO; +import com.quanxiaoha.weblog.common.domain.dos.CategoryDO; +import com.quanxiaoha.weblog.common.domain.mapper.ArticleCategoryRelMapper; +import com.quanxiaoha.weblog.common.domain.mapper.ArticleMapper; +import com.quanxiaoha.weblog.common.domain.mapper.CategoryMapper; +import com.quanxiaoha.weblog.common.enums.ResponseCodeEnum; +import com.quanxiaoha.weblog.common.exception.BizException; +import com.quanxiaoha.weblog.common.utils.PageResponse; +import com.quanxiaoha.weblog.common.utils.Response; +import com.quanxiaoha.weblog.web.convert.ArticleConvert; +import com.quanxiaoha.weblog.web.model.vo.category.FindCategoryArticlePageListReqVO; +import com.quanxiaoha.weblog.web.model.vo.category.FindCategoryArticlePageListRspVO; +import com.quanxiaoha.weblog.web.model.vo.category.FindCategoryListRspVO; +import com.quanxiaoha.weblog.web.service.CategoryService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:03 + * @description: 分类 + **/ +@Service +@Slf4j +public class CategoryServiceImpl implements CategoryService { + + @Autowired + private CategoryMapper categoryMapper; + @Autowired + private ArticleCategoryRelMapper articleCategoryRelMapper; + @Autowired + private ArticleMapper articleMapper; + + /** + * 获取分类列表 + * + * @return + */ + @Override + public Response findCategoryList() { + // 查询所有分类 + List categoryDOS = categoryMapper.selectList(Wrappers.emptyWrapper()); + + // DO 转 VO + List vos = null; + if (!CollectionUtils.isEmpty(categoryDOS)) { + vos = categoryDOS.stream() + .map(categoryDO -> FindCategoryListRspVO.builder() + .id(categoryDO.getId()) + .name(categoryDO.getName()) + .build()) + .collect(Collectors.toList()); + } + + return Response.success(vos); + } + + /** + * 获取分类下文章分页数据 + * + * @param findCategoryArticlePageListReqVO + * @return + */ + @Override + public Response findCategoryArticlePageList(FindCategoryArticlePageListReqVO findCategoryArticlePageListReqVO) { + Long current = findCategoryArticlePageListReqVO.getCurrent(); + Long size = findCategoryArticlePageListReqVO.getSize(); + Long categoryId = findCategoryArticlePageListReqVO.getId(); + + CategoryDO categoryDO = categoryMapper.selectById(categoryId); + + // 判断该分类是否存在 + if (Objects.isNull(categoryDO)) { + log.warn("==> 该分类不存在, categoryId: {}", categoryId); + throw new BizException(ResponseCodeEnum.CATEGORY_NOT_EXISTED); + } + + // 先查询该分类下所有关联的文章 ID + List articleCategoryRelDOS = articleCategoryRelMapper.selectListByCategoryId(categoryId); + + // 若该分类下未发布任何文章 + if (CollectionUtils.isEmpty(articleCategoryRelDOS)) { + log.info("==> 该分类下还未发布任何文章, categoryId: {}", categoryId); + return PageResponse.success(null, null); + } + + List articleIds = articleCategoryRelDOS.stream().map(ArticleCategoryRelDO::getArticleId).collect(Collectors.toList()); + + // 根据文章 ID 集合查询文章分页数据 + Page page = articleMapper.selectPageListByArticleIds(current, size, articleIds); + List articleDOS = page.getRecords(); + + // DO 转 VO + List vos = null; + if (!CollectionUtils.isEmpty(articleDOS)) { + vos = articleDOS.stream() + .map(articleDO -> ArticleConvert.INSTANCE.convertDO2CategoryArticleVO(articleDO)) + .collect(Collectors.toList()); + } + + return PageResponse.success(page, vos); + } +} diff --git a/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/impl/TagServiceImpl.java b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/impl/TagServiceImpl.java new file mode 100644 index 0000000..9627252 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/java/com/quanxiaoha/weblog/web/service/impl/TagServiceImpl.java @@ -0,0 +1,116 @@ +package com.quanxiaoha.weblog.web.service.impl; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.quanxiaoha.weblog.common.domain.dos.ArticleDO; +import com.quanxiaoha.weblog.common.domain.dos.ArticleTagRelDO; +import com.quanxiaoha.weblog.common.domain.dos.TagDO; +import com.quanxiaoha.weblog.common.domain.mapper.ArticleMapper; +import com.quanxiaoha.weblog.common.domain.mapper.ArticleTagRelMapper; +import com.quanxiaoha.weblog.common.domain.mapper.TagMapper; +import com.quanxiaoha.weblog.common.enums.ResponseCodeEnum; +import com.quanxiaoha.weblog.common.exception.BizException; +import com.quanxiaoha.weblog.common.utils.PageResponse; +import com.quanxiaoha.weblog.common.utils.Response; +import com.quanxiaoha.weblog.web.convert.ArticleConvert; +import com.quanxiaoha.weblog.web.model.vo.tag.FindTagArticlePageListReqVO; +import com.quanxiaoha.weblog.web.model.vo.tag.FindTagArticlePageListRspVO; +import com.quanxiaoha.weblog.web.model.vo.tag.FindTagListRspVO; +import com.quanxiaoha.weblog.web.service.TagService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @author: 犬小哈 + * @url: www.quanxiaoha.com + * @date: 2023-09-15 14:03 + * @description: 标签 + **/ +@Service +@Slf4j +public class TagServiceImpl implements TagService { + + @Autowired + private TagMapper tagMapper; + @Autowired + private ArticleTagRelMapper articleTagRelMapper; + @Autowired + private ArticleMapper articleMapper; + + /** + * 获取标签列表 + * + * @return + */ + @Override + public Response findTagList() { + // 查询所有标签 + List tagDOS = tagMapper.selectList(Wrappers.emptyWrapper()); + + // DO 转 VO + List vos = null; + if (!CollectionUtils.isEmpty(tagDOS)) { + vos = tagDOS.stream() + .map(tagDO -> FindTagListRspVO.builder() + .id(tagDO.getId()) + .name(tagDO.getName()) + .build()) + .collect(Collectors.toList()); + } + + return Response.success(vos); + } + + /** + * 获取标签下文章分页列表 + * + * @param findTagArticlePageListReqVO + * @return + */ + @Override + public Response findTagPageList(FindTagArticlePageListReqVO findTagArticlePageListReqVO) { + Long current = findTagArticlePageListReqVO.getCurrent(); + Long size = findTagArticlePageListReqVO.getSize(); + // 标签 ID + Long tagId = findTagArticlePageListReqVO.getId(); + + // 判断该标签是否存在 + TagDO tagDO = tagMapper.selectById(tagId); + if (Objects.isNull(tagDO)) { + log.warn("==> 该标签不存在, tagId: {}", tagId); + throw new BizException(ResponseCodeEnum.TAG_NOT_EXISTED); + } + + // 先查询该标签下所有关联的文章 ID + List articleTagRelDOS = articleTagRelMapper.selectByTagId(tagId); + + // 若该标签下未发布任何文章 + if (CollectionUtils.isEmpty(articleTagRelDOS)) { + log.info("==> 该标签下还未发布任何文章, tagId: {}", tagId); + return PageResponse.success(null, null); + } + + // 提取所有文章 ID + List articleIds = articleTagRelDOS.stream().map(ArticleTagRelDO::getArticleId).collect(Collectors.toList()); + + // 根据文章 ID 集合查询文章分页数据 + Page page = articleMapper.selectPageListByArticleIds(current, size, articleIds); + List articleDOS = page.getRecords(); + + // DO 转 VO + List vos = null; + if (!CollectionUtils.isEmpty(articleDOS)) { + vos = articleDOS.stream() + .map(articleDO -> ArticleConvert.INSTANCE.convertDO2TagArticleVO(articleDO)) + .collect(Collectors.toList()); + } + + return PageResponse.success(page, vos); + } +} diff --git a/weblog-springboot-134/weblog-web/src/main/resources/application-dev.yml b/weblog-springboot-134/weblog-web/src/main/resources/application-dev.yml new file mode 100644 index 0000000..bc01d83 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/resources/application-dev.yml @@ -0,0 +1,27 @@ +spring: + datasource: + # 指定数据库驱动类 + driver-class-name: com.p6spy.engine.spy.P6SpyDriver + # 数据库连接信息 + url: jdbc:p6spy:mysql://127.0.0.1:3306/weblog?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull + username: root + password: 123456 + hikari: # 数据库连接池使用 Hikari + minimum-idle: 5 # 连接池中最小空闲连接数 + maximum-pool-size: 20 # 连接池中允许的最大连接数 + auto-commit: true # 是否自动提交事务 + idle-timeout: 30000 # 连接在连接池中闲置的最长时间,超过这个时间会被释放。 + pool-name: Weblog-HikariCP # 自定义连接池的名字 + max-lifetime: 1800000 # 连接在连接池中的最大存活时间,超过这个时间会被强制关闭。 + connection-timeout: 30000 # 连接的超时时间 + connection-test-query: SELECT 1 # 用于测试连接是否可用的SQL查询 + + +#================================================================= +# minio (上传图片需要,需配置成自己的地址) +#================================================================= +minio: + endpoint: http://127.0.0.1:9000 + accessKey: quanxiaoha + secretKey: quanxiaoha + bucketName: weblog diff --git a/weblog-springboot-134/weblog-web/src/main/resources/application-prod.yml b/weblog-springboot-134/weblog-web/src/main/resources/application-prod.yml new file mode 100644 index 0000000..76a9837 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/resources/application-prod.yml @@ -0,0 +1,35 @@ +server: + port: 8082 +spring: + datasource: + # 指定数据库驱动类 + driver-class-name: com.mysql.cj.jdbc.Driver + # 数据库连接信息 + url: jdbc:mysql://172.17.0.1:3306/weblog?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull + username: root + password: 123456 + hikari: # 数据库连接池使用 Hikari + minimum-idle: 5 # 连接池中最小空闲连接数 + maximum-pool-size: 20 # 连接池中允许的最大连接数 + auto-commit: true # 是否自动提交事务 + idle-timeout: 30000 # 连接在连接池中闲置的最长时间,超过这个时间会被释放。 + pool-name: Weblog-HikariCP # 自定义连接池的名字 + max-lifetime: 1800000 # 连接在连接池中的最大存活时间,超过这个时间会被强制关闭。 + connection-timeout: 30000 # 连接的超时时间 + connection-test-query: SELECT 1 # 用于测试连接是否可用的SQL查询 + + +#================================================================= +# log 日志 +#================================================================= +logging: + config: classpath:logback-weblog.xml + +#================================================================= +# minio (上传图片需要,需配置成自己的地址) +#================================================================= +minio: + endpoint: http://127.0.0.1:9000 + accessKey: quanxiaoha + secretKey: quanxiaoha + bucketName: weblog \ No newline at end of file diff --git a/weblog-springboot-134/weblog-web/src/main/resources/application.yml b/weblog-springboot-134/weblog-web/src/main/resources/application.yml new file mode 100644 index 0000000..dd9f9c9 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/resources/application.yml @@ -0,0 +1,18 @@ +spring: + profiles: + # 默认激活 dev 环境 + active: dev + +jwt: + # 签发人 + issuer: quanxiaoha + # 秘钥 + secret: jElxcSUj38+Bnh73T68lNs0DfBSit6U3whQlcGO2XwnI+Bo3g4xsiCIPg8PV/L0fQMis08iupNwhe2PzYLB9Xg== + # token 过期时间(单位:分钟) 24*60 + tokenExpireTime: 1440 + # token 请求头中的 key 值 + tokenHeaderKey: Authorization + # token 请求头中的 value 值前缀 + tokenPrefix: Bearer + + diff --git a/weblog-springboot-134/weblog-web/src/main/resources/logback-weblog.xml b/weblog-springboot-134/weblog-web/src/main/resources/logback-weblog.xml new file mode 100644 index 0000000..85ffc3f --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/resources/logback-weblog.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + ${LOG_FILE}-%i.log + + 30 + + + 10MB + + + + + ${FILE_LOG_PATTERN} + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/weblog-springboot-134/weblog-web/src/main/resources/spy.properties b/weblog-springboot-134/weblog-web/src/main/resources/spy.properties new file mode 100644 index 0000000..f035706 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/main/resources/spy.properties @@ -0,0 +1,24 @@ +#3.2.1???? +modulelist=com.baomidou.mybatisplus.extension.p6spy.MybatisPlusLogFactory,com.p6spy.engine.outage.P6OutageFactory +#3.2.1????????? +#modulelist=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory +# ??????? +logMessageFormat=com.baomidou.mybatisplus.extension.p6spy.P6SpyLogger +#???????? +appender=com.baomidou.mybatisplus.extension.p6spy.StdoutLogger +# ???????? sql +#appender=com.p6spy.engine.spy.appender.Slf4JLogger +# ?? p6spy driver ?? +deregisterdrivers=true +# ??JDBC URL?? +useprefix=true +# ???? Log ??,????????error,info,batch,debug,statement,commit,rollback,result,resultset. +excludecategories=info,debug,result,commit,resultset +# ???? +dateformat=yyyy-MM-dd HH:mm:ss +# ??????? +#driverlist=org.h2.Driver +# ?????SQL?? +outagedetection=true +# ?SQL???? 2 ? +outagedetectioninterval=2 \ No newline at end of file diff --git a/weblog-springboot-134/weblog-web/src/test/java/com/quanxiaoha/weblog/web/WeblogWebApplicationTests.java b/weblog-springboot-134/weblog-web/src/test/java/com/quanxiaoha/weblog/web/WeblogWebApplicationTests.java new file mode 100644 index 0000000..5c1def7 --- /dev/null +++ b/weblog-springboot-134/weblog-web/src/test/java/com/quanxiaoha/weblog/web/WeblogWebApplicationTests.java @@ -0,0 +1,86 @@ +package com.quanxiaoha.weblog.web; + +import com.quanxiaoha.weblog.admin.config.MinioProperties; +import com.quanxiaoha.weblog.common.domain.dos.UserDO; +import com.quanxiaoha.weblog.common.domain.mapper.UserMapper; +import io.minio.MinioClient; +import io.minio.PutObjectArgs; +import io.minio.errors.*; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.time.LocalDateTime; +import java.util.UUID; + +@SpringBootTest +@Slf4j +class WeblogWebApplicationTests { + + @Autowired + private UserMapper userMapper; + + @Autowired + private MinioProperties minioProperties; + + @Autowired + private MinioClient minioClient; + + + @Test + void testLog() { + log.info("这是一行 Info 级别日志"); + log.warn("这是一行 Warn 级别日志"); + log.error("这是一行 Error 级别日志"); + + // 占位符 + String author = "犬小哈"; + log.info("这是一行带有占位符日志,作者:{}", author); + } + + @Test + void insertTest() { + // 构建数据库实体类 + UserDO userDO = UserDO.builder() + .username("犬小哈") + .password("123456") + .createTime(LocalDateTime.now()) + .updateTime(LocalDateTime.now()) + .isDeleted(false) + .build(); + + userMapper.insert(userDO); + } + + @Test + void uploadFile2MinioTest() throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException { + File file = new File("D:\\Backup\\Documents\\My Pictures\\111.jpg"); + String originalFileName = file.getName(); + String contentType = "image/jpeg"; + + String key = UUID.randomUUID().toString().replace("-", ""); + String suffix = originalFileName.substring(originalFileName.lastIndexOf(".")); + + String objectName = String.format("%s%s", key, suffix); + + log.info("==> 开始上传文件至 Minio, ObjectName: {}", objectName); + minioClient.putObject(PutObjectArgs.builder() + .bucket(minioProperties.getBucketName()) + .object(objectName) + .stream(new FileInputStream(file), file.length(), -1) + .contentType(contentType) + .build()); + + String url = String.format("%s/%s/%s", minioProperties.getEndpoint(), minioProperties.getBucketName(), objectName); + log.info("==> 上传文件至 Minio 成功,访问路径: {}", url); + } + +}