diff --git a/weblog-springboot-095/pom.xml b/weblog-springboot-095/pom.xml
new file mode 100644
index 0000000..5173976
--- /dev/null
+++ b/weblog-springboot-095/pom.xml
@@ -0,0 +1,211 @@
+
+
+ 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
+
+
+
+
+
+
+ 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.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-095/weblog-module-admin/.gitignore b/weblog-springboot-095/weblog-module-admin/.gitignore
new file mode 100644
index 0000000..549e00a
--- /dev/null
+++ b/weblog-springboot-095/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-095/weblog-module-admin/pom.xml b/weblog-springboot-095/weblog-module-admin/pom.xml
new file mode 100644
index 0000000..536651a
--- /dev/null
+++ b/weblog-springboot-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/config/Knife4jAdminConfig.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/config/MinioConfig.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/config/MinioProperties.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/config/WebSecurityConfig.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminBlogSettingsController.java b/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminBlogSettingsController.java
new file mode 100644
index 0000000..e7af50e
--- /dev/null
+++ b/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminBlogSettingsController.java
@@ -0,0 +1,41 @@
+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);
+ }
+
+}
diff --git a/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminCategoryController.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminFileController.java b/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminFileController.java
new file mode 100644
index 0000000..01af399
--- /dev/null
+++ b/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminFileController.java
@@ -0,0 +1,37 @@
+package com.quanxiaoha.weblog.admin.controller;
+
+import com.quanxiaoha.weblog.admin.model.vo.blogsettings.UpdateBlogSettingsReqVO;
+import com.quanxiaoha.weblog.admin.service.AdminBlogSettingsService;
+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.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+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 = "文件上传")
+ public Response uploadFile(@RequestParam MultipartFile file) {
+ return fileService.uploadFile(file);
+ }
+
+}
diff --git a/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminTagController.java b/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminTagController.java
new file mode 100644
index 0000000..08dea58
--- /dev/null
+++ b/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminTagController.java
@@ -0,0 +1,65 @@
+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);
+ }
+
+}
diff --git a/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/controller/AdminUserController.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/convert/BlogSettingsConvert.java b/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/convert/BlogSettingsConvert.java
new file mode 100644
index 0000000..d6b0613
--- /dev/null
+++ b/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/convert/BlogSettingsConvert.java
@@ -0,0 +1,28 @@
+package com.quanxiaoha.weblog.admin.convert;
+
+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);
+
+}
diff --git a/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/package-info.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/blogsettings/UpdateBlogSettingsReqVO.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/category/AddCategoryReqVO.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/category/DeleteCategoryReqVO.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/category/FindCategoryPageListReqVO.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/category/FindCategoryPageListRspVO.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/file/UploadFileRspVO.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/tag/AddTagReqVO.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/tag/DeleteTagReqVO.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/tag/FindTagPageListReqVO.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/tag/FindTagPageListRspVO.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/tag/SearchTagsReqVO.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/user/FindUserInfoRspVO.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/model/vo/user/UpdateAdminUserPasswordReqVO.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/package-info.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminBlogSettingsService.java b/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminBlogSettingsService.java
new file mode 100644
index 0000000..a81c477
--- /dev/null
+++ b/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminBlogSettingsService.java
@@ -0,0 +1,20 @@
+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);
+}
diff --git a/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminCategoryService.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminFileService.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminTagService.java b/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminTagService.java
new file mode 100644
index 0000000..5fe2f49
--- /dev/null
+++ b/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminTagService.java
@@ -0,0 +1,45 @@
+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);
+}
diff --git a/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/AdminUserService.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminBlogSettingsServiceImpl.java b/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminBlogSettingsServiceImpl.java
new file mode 100644
index 0000000..1d58c8e
--- /dev/null
+++ b/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminBlogSettingsServiceImpl.java
@@ -0,0 +1,41 @@
+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.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 {
+
+ @Override
+ public Response updateBlogSettings(UpdateBlogSettingsReqVO updateBlogSettingsReqVO) {
+ // VO 转 DO
+ BlogSettingsDO blogSettingsDO = BlogSettingsConvert.INSTANCE.convertVO2DO(updateBlogSettingsReqVO);
+ blogSettingsDO.setId(1L);
+
+ // 保存或更新(当数据库中存在 ID 为 1 的记录时,则执行更新操作,否则执行插入操作)
+ saveOrUpdate(blogSettingsDO);
+ return Response.success();
+ }
+}
diff --git a/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminCategoryServiceImpl.java b/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminCategoryServiceImpl.java
new file mode 100644
index 0000000..849f95b
--- /dev/null
+++ b/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminCategoryServiceImpl.java
@@ -0,0 +1,157 @@
+package com.quanxiaoha.weblog.admin.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.injector.methods.SelectPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+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.model.vo.user.FindUserInfoRspVO;
+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.domain.dos.CategoryDO;
+import com.quanxiaoha.weblog.common.domain.mapper.CategoryMapper;
+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.model.vo.SelectRspVO;
+import com.quanxiaoha.weblog.common.utils.PageResponse;
+import com.quanxiaoha.weblog.common.utils.Response;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+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.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;
+
+ /**
+ * 添加分类
+ *
+ * @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();
+
+ // 删除分类
+ 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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminFileServiceImpl.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminTagServiceImpl.java b/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminTagServiceImpl.java
new file mode 100644
index 0000000..12d9322
--- /dev/null
+++ b/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminTagServiceImpl.java
@@ -0,0 +1,139 @@
+package com.quanxiaoha.weblog.admin.service.impl;
+
+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.TagDO;
+import com.quanxiaoha.weblog.common.domain.mapper.TagMapper;
+import com.quanxiaoha.weblog.common.enums.ResponseCodeEnum;
+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.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;
+
+ /**
+ * 添加标签集合
+ *
+ * @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();
+
+ // 根据标签 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);
+ }
+}
diff --git a/weblog-springboot-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/impl/AdminUserServiceImpl.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/service/package-info.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/main/java/com/quanxiaoha/weblog/admin/utils/MinioUtil.java b/weblog-springboot-095/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-095/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-095/weblog-module-admin/src/test/java/com/quanxiaoha/weblog/admin/WeblogModuleAdminApplicationTests.java b/weblog-springboot-095/weblog-module-admin/src/test/java/com/quanxiaoha/weblog/admin/WeblogModuleAdminApplicationTests.java
new file mode 100644
index 0000000..1b6daf7
--- /dev/null
+++ b/weblog-springboot-095/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-095/weblog-module-common/.gitignore b/weblog-springboot-095/weblog-module-common/.gitignore
new file mode 100644
index 0000000..549e00a
--- /dev/null
+++ b/weblog-springboot-095/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-095/weblog-module-common/pom.xml b/weblog-springboot-095/weblog-module-common/pom.xml
new file mode 100644
index 0000000..9ef9da3
--- /dev/null
+++ b/weblog-springboot-095/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-095/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/aspect/ApiOperationLog.java b/weblog-springboot-095/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-095/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-095/weblog-module-common/src/main/java/com/quanxiaoha/weblog/common/aspect/ApiOperationLogAspect.java b/weblog-springboot-095/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-095/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