133
This commit is contained in:
parent
7654692449
commit
d830323687
244
weblog-springboot-133/pom.xml
Normal file
244
weblog-springboot-133/pom.xml
Normal file
@ -0,0 +1,244 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<!-- 将 Spring Boot 的版本号切换成 2.6 版本 -->
|
||||
<version>2.6.3</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
<groupId>com.quanxiaoha</groupId>
|
||||
<artifactId>weblog-springboot</artifactId>
|
||||
<version>${revision}</version>
|
||||
<name>weblog-springboot</name>
|
||||
<!-- 项目描述 -->
|
||||
<description>前后端分离博客 Weblog By 犬小哈</description>
|
||||
|
||||
<!-- 多模块项目父工程打包模式必须指定为 pom -->
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<!-- 子模块管理 -->
|
||||
<modules>
|
||||
<!-- 入口模块 -->
|
||||
<module>weblog-web</module>
|
||||
<!-- 管理后台 -->
|
||||
<module>weblog-module-admin</module>
|
||||
<!-- 通用模块 -->
|
||||
<module>weblog-module-common</module>
|
||||
<!-- JWT 模块 -->
|
||||
<module>weblog-module-jwt</module>
|
||||
</modules>
|
||||
|
||||
|
||||
<!-- 版本号统一管理 -->
|
||||
<properties>
|
||||
<!-- 项目版本号 -->
|
||||
<revision>0.0.1-SNAPSHOT</revision>
|
||||
<java.version>1.8</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<!-- Maven 相关 -->
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
<maven.compiler.target>${java.version}</maven.compiler.target>
|
||||
|
||||
<!-- 依赖包版本 -->
|
||||
<lombok.version>1.18.28</lombok.version>
|
||||
<guava.version>31.1-jre</guava.version>
|
||||
<commons-lang3.version>3.12.0</commons-lang3.version>
|
||||
<jackson.version>2.15.2</jackson.version>
|
||||
<knife4j.version>4.3.0</knife4j.version>
|
||||
<mybatis-plus.version>3.5.2</mybatis-plus.version>
|
||||
<p6spy.version>3.9.1</p6spy.version>
|
||||
<jjwt.version>0.11.2</jjwt.version>
|
||||
<minio.version>8.2.1</minio.version>
|
||||
<mapstruct.version>1.5.5.Final</mapstruct.version>
|
||||
<commonmark.version>0.20.0</commonmark.version>
|
||||
</properties>
|
||||
|
||||
<!-- 统一依赖管理 -->
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.quanxiaoha</groupId>
|
||||
<artifactId>weblog-module-admin</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.quanxiaoha</groupId>
|
||||
<artifactId>weblog-module-common</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.quanxiaoha</groupId>
|
||||
<artifactId>weblog-module-jwt</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 常用工具库 -->
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>${guava.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${commons-lang3.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Jackson -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>${jackson.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- knife4j(API 文档工具) -->
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
|
||||
<version>${knife4j.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Mybatis Plus -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>${mybatis-plus.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>p6spy</groupId>
|
||||
<artifactId>p6spy</artifactId>
|
||||
<version>${p6spy.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- JWT -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
<version>${jjwt.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<version>${jjwt.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<version>${jjwt.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 对象存储 Minio -->
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio</artifactId>
|
||||
<version>${minio.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Mapstruct 属性映射 -->
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct</artifactId>
|
||||
<version>${mapstruct.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Markdown 解析 -->
|
||||
<dependency>
|
||||
<groupId>org.commonmark</groupId>
|
||||
<artifactId>commonmark</artifactId>
|
||||
<version>${commonmark.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.commonmark</groupId>
|
||||
<artifactId>commonmark-ext-gfm-tables</artifactId>
|
||||
<version>${commonmark.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.commonmark</groupId>
|
||||
<artifactId>commonmark-ext-heading-anchor</artifactId>
|
||||
<version>${commonmark.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.commonmark</groupId>
|
||||
<artifactId>commonmark-ext-image-attributes</artifactId>
|
||||
<version>${commonmark.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.commonmark</groupId>
|
||||
<artifactId>commonmark-ext-task-list-items</artifactId>
|
||||
<version>${commonmark.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<!-- 统一插件管理 -->
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>${java.version}</source> <!-- 根据你的 JDK 版本进行调整 -->
|
||||
<target>${java.version}</target> <!-- 根据你的 JDK 版本进行调整 -->
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
</path>
|
||||
<path>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct-processor</artifactId>
|
||||
<version>${mapstruct.version}</version> <!-- 使用时请检查最新版本 -->
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
|
||||
<!-- 使用阿里云的 Maven 仓库源,提升包下载速度 -->
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>aliyunmaven</id>
|
||||
<name>aliyun</name>
|
||||
<url>https://maven.aliyun.com/repository/public</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
</project>
|
||||
33
weblog-springboot-133/weblog-module-admin/.gitignore
vendored
Normal file
33
weblog-springboot-133/weblog-module-admin/.gitignore
vendored
Normal file
@ -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/
|
||||
78
weblog-springboot-133/weblog-module-admin/pom.xml
Normal file
78
weblog-springboot-133/weblog-module-admin/pom.xml
Normal file
@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<!-- 指定父项目为 weblog-springboot -->
|
||||
<parent>
|
||||
<groupId>com.quanxiaoha</groupId>
|
||||
<artifactId>weblog-springboot</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.quanxiaoha</groupId>
|
||||
<artifactId>weblog-module-admin</artifactId>
|
||||
<name>weblog-module-admin</name>
|
||||
<description>weblog-admin (负责管理后台相关功能)</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.quanxiaoha</groupId>
|
||||
<artifactId>weblog-module-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.quanxiaoha</groupId>
|
||||
<artifactId>weblog-module-jwt</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 免写冗余的 Java 样板式代码 -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- 单元测试 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- knife4j -->
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId>
|
||||
<artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Security -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 入参校验 -->
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate.validator</groupId>
|
||||
<artifactId>hibernate-validator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 对象存储 Minio -->
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
}
|
||||
@ -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);
|
||||
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @author: 犬小哈
|
||||
* @url: www.quanxiaoha.com
|
||||
* @date: 2023-09-15 14:07
|
||||
* @description: TODO
|
||||
**/
|
||||
package com.quanxiaoha.weblog.admin.model;
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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<Long> tagIds;
|
||||
|
||||
/**
|
||||
* 摘要
|
||||
*/
|
||||
private String summary;
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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<String> tags;
|
||||
}
|
||||
@ -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<String> tags;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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<String> tags;
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @author: 犬小哈
|
||||
* @url: www.quanxiaoha.com
|
||||
* @date: 2023-08-16 9:28
|
||||
* @description: TODO
|
||||
**/
|
||||
package com.quanxiaoha.weblog.admin;
|
||||
@ -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);
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
@ -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();
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
@ -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<String> 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<ArticleDO> articleDOPage = articleMapper.selectPageList(current, size, title, startDate, endDate);
|
||||
|
||||
List<ArticleDO> articleDOS = articleDOPage.getRecords();
|
||||
|
||||
// DO 转 VO
|
||||
List<FindArticlePageListRspVO> 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<FindArticleDetailRspVO> 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<ArticleTagRelDO> articleTagRelDOS = articleTagRelMapper.selectByArticleId(articleId);
|
||||
// 获取对应标签 ID 集合
|
||||
List<Long> 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<String> publishTags = updateArticleReqVO.getTags();
|
||||
insertTags(articleId, publishTags);
|
||||
|
||||
return Response.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存标签
|
||||
* @param articleId
|
||||
* @param publishTags
|
||||
*/
|
||||
private void insertTags(Long articleId, List<String> publishTags) {
|
||||
// 筛选提交的标签(表中不存在的标签)
|
||||
List<String> notExistTags = null;
|
||||
// 筛选提交的标签(表中已存在的标签)
|
||||
List<String> existedTags = null;
|
||||
|
||||
// 查询出所有标签
|
||||
List<TagDO> tagDOS = tagMapper.selectList(Wrappers.emptyWrapper());
|
||||
|
||||
// 如果表中还没有添加任何标签
|
||||
if (CollectionUtils.isEmpty(tagDOS)) {
|
||||
notExistTags = publishTags;
|
||||
} else {
|
||||
List<String> 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<String, Long> tagNameIdMap = tagDOS.stream().collect(Collectors.toMap(tagDO -> tagDO.getName().toLowerCase(), TagDO::getId));
|
||||
|
||||
// 使用迭代器进行安全的删除操作
|
||||
Iterator<String> 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<ArticleTagRelDO> 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<ArticleTagRelDO> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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<BlogSettingsMapper, BlogSettingsDO> 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);
|
||||
}
|
||||
}
|
||||
@ -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<CategoryDO> categoryDOPage = categoryMapper.selectPageList(current, size, name, startDate, endDate);
|
||||
|
||||
List<CategoryDO> categoryDOS = categoryDOPage.getRecords();
|
||||
|
||||
// DO 转 VO
|
||||
List<FindCategoryPageListRspVO> 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<CategoryDO> categoryDOS = categoryMapper.selectList(null);
|
||||
|
||||
// DO 转 VO
|
||||
List<SelectRspVO> 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);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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<TagMapper, TagDO> implements AdminTagService {
|
||||
|
||||
@Autowired
|
||||
private TagMapper tagMapper;
|
||||
@Autowired
|
||||
private ArticleTagRelMapper articleTagRelMapper;
|
||||
|
||||
/**
|
||||
* 添加标签集合
|
||||
*
|
||||
* @param addTagReqVO
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public Response addTags(AddTagReqVO addTagReqVO) {
|
||||
// vo 转 do
|
||||
List<TagDO> 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<TagDO> page = tagMapper.selectPageList(current, size, name, startDate, endDate);
|
||||
|
||||
List<TagDO> records = page.getRecords();
|
||||
|
||||
// do 转 vo
|
||||
List<FindTagPageListRspVO> 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<TagDO> tagDOS = tagMapper.selectByKey(key);
|
||||
|
||||
// do 转 vo
|
||||
List<SelectRspVO> 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<TagDO> tagDOS = tagMapper.selectList(Wrappers.emptyWrapper());
|
||||
|
||||
// DO 转 VO
|
||||
List<SelectRspVO> 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);
|
||||
}
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @author: 犬小哈
|
||||
* @url: www.quanxiaoha.com
|
||||
* @date: 2023-09-15 14:02
|
||||
* @description: TODO
|
||||
**/
|
||||
package com.quanxiaoha.weblog.admin.service;
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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 {
|
||||
}
|
||||
|
||||
}
|
||||
33
weblog-springboot-133/weblog-module-common/.gitignore
vendored
Normal file
33
weblog-springboot-133/weblog-module-common/.gitignore
vendored
Normal file
@ -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/
|
||||
69
weblog-springboot-133/weblog-module-common/pom.xml
Normal file
69
weblog-springboot-133/weblog-module-common/pom.xml
Normal file
@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.quanxiaoha</groupId>
|
||||
<artifactId>weblog-springboot</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.quanxiaoha</groupId>
|
||||
<artifactId>weblog-module-common</artifactId>
|
||||
<name>weblog-module-common</name>
|
||||
<description>weblog-module-common (此模块用于存放一些通用的功能)</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<!-- 免写冗余的 Java 样板式代码 -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- 单元测试 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- AOP 切面 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Jackson -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Mybatis Plus -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- mysql 依赖 -->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>p6spy</groupId>
|
||||
<artifactId>p6spy</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -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 "";
|
||||
|
||||
}
|
||||
|
||||
@ -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<Object, String> toJsonStr() {
|
||||
return arg -> JsonUtil.toJsonString(arg);
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<T> extends BaseMapper<T> {
|
||||
|
||||
// 批量插入
|
||||
int insertBatchSomeColumn(@Param("list") List<T> batchList);
|
||||
|
||||
}
|
||||
@ -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<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
|
||||
// super.getMethodList() 保留 Mybatis Plus 自带的方法
|
||||
List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
|
||||
// 添加自定义方法:批量插入,方法名为 insertBatchSomeColumn
|
||||
methodList.add(new InsertBatchSomeColumn());
|
||||
return methodList;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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<ArticleCategoryRelDO> {
|
||||
|
||||
/**
|
||||
* 根据文章 ID 删除关联记录
|
||||
* @param articleId
|
||||
* @return
|
||||
*/
|
||||
default int deleteByArticleId(Long articleId) {
|
||||
return delete(Wrappers.<ArticleCategoryRelDO>lambdaQuery()
|
||||
.eq(ArticleCategoryRelDO::getArticleId, articleId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文章 ID 查询
|
||||
* @param articleId
|
||||
* @return
|
||||
*/
|
||||
default ArticleCategoryRelDO selectByArticleId(Long articleId) {
|
||||
return selectOne(Wrappers.<ArticleCategoryRelDO>lambdaQuery()
|
||||
.eq(ArticleCategoryRelDO::getArticleId, articleId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据分类 ID 查询
|
||||
* @param categoryId
|
||||
* @return
|
||||
*/
|
||||
default ArticleCategoryRelDO selectOneByCategoryId(Long categoryId) {
|
||||
return selectOne(Wrappers.<ArticleCategoryRelDO>lambdaQuery()
|
||||
.eq(ArticleCategoryRelDO::getCategoryId, categoryId)
|
||||
.last("LIMIT 1"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文章 ID 集合批量查询
|
||||
* @param articleIds
|
||||
* @return
|
||||
*/
|
||||
default List<ArticleCategoryRelDO> selectByArticleIds(List<Long> articleIds) {
|
||||
return selectList(Wrappers.<ArticleCategoryRelDO>lambdaQuery()
|
||||
.in(ArticleCategoryRelDO::getArticleId, articleIds));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据分类 ID 查询所有的关联记录
|
||||
* @param categoryId
|
||||
* @return
|
||||
*/
|
||||
default List<ArticleCategoryRelDO> selectListByCategoryId(Long categoryId) {
|
||||
return selectList(Wrappers.<ArticleCategoryRelDO>lambdaQuery()
|
||||
.eq(ArticleCategoryRelDO::getCategoryId, categoryId));
|
||||
}
|
||||
}
|
||||
@ -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<ArticleContentDO> {
|
||||
|
||||
/**
|
||||
* 根据文章 ID 删除记录
|
||||
* @param articleId
|
||||
* @return
|
||||
*/
|
||||
default int deleteByArticleId(Long articleId) {
|
||||
return delete(Wrappers.<ArticleContentDO>lambdaQuery()
|
||||
.eq(ArticleContentDO::getArticleId, articleId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文章 ID 查询
|
||||
* @param articleId
|
||||
* @return
|
||||
*/
|
||||
default ArticleContentDO selectByArticleId(Long articleId) {
|
||||
return selectOne(Wrappers.<ArticleContentDO>lambdaQuery()
|
||||
.eq(ArticleContentDO::getArticleId, articleId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过文章 ID 更新
|
||||
* @param articleContentDO
|
||||
*/
|
||||
default int updateByArticleId(ArticleContentDO articleContentDO) {
|
||||
return update(articleContentDO,
|
||||
Wrappers.<ArticleContentDO>lambdaQuery()
|
||||
.eq(ArticleContentDO::getArticleId, articleContentDO.getArticleId()));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,87 @@
|
||||
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<ArticleDO> {
|
||||
|
||||
/**
|
||||
* 分页查询
|
||||
* @param current
|
||||
* @param size
|
||||
* @param title
|
||||
* @param startDate
|
||||
* @param endDate
|
||||
* @return
|
||||
*/
|
||||
default Page<ArticleDO> selectPageList(Long current, Long size, String title, LocalDate startDate, LocalDate endDate) {
|
||||
// 分页对象(查询第几页、每页多少数据)
|
||||
Page<ArticleDO> page = new Page<>(current, size);
|
||||
|
||||
// 构建查询条件
|
||||
LambdaQueryWrapper<ArticleDO> wrapper = Wrappers.<ArticleDO>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<ArticleDO> selectPageListByArticleIds(Long current, Long size, List<Long> articleIds) {
|
||||
// 分页对象(查询第几页、每页多少数据)
|
||||
Page<ArticleDO> page = new Page<>(current, size);
|
||||
|
||||
// 构建查询条件
|
||||
LambdaQueryWrapper<ArticleDO> wrapper = Wrappers.<ArticleDO>lambdaQuery()
|
||||
.in(ArticleDO::getId, articleIds)
|
||||
.orderByDesc(ArticleDO::getCreateTime); // 按创建时间倒叙
|
||||
|
||||
return selectPage(page, wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询上一篇文章
|
||||
* @param articleId
|
||||
* @return
|
||||
*/
|
||||
default ArticleDO selectPreArticle(Long articleId) {
|
||||
return selectOne(Wrappers.<ArticleDO>lambdaQuery()
|
||||
.orderByAsc(ArticleDO::getId) // 按文章 ID 倒序排列
|
||||
.gt(ArticleDO::getId, articleId) // 查询比当前文章 ID 大的
|
||||
.last("limit 1")); // 第一条记录即为上一篇文章
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询下一篇文章
|
||||
* @param articleId
|
||||
* @return
|
||||
*/
|
||||
default ArticleDO selectNextArticle(Long articleId) {
|
||||
return selectOne(Wrappers.<ArticleDO>lambdaQuery()
|
||||
.orderByDesc(ArticleDO::getId) // 按文章 ID 倒序排列
|
||||
.lt(ArticleDO::getId, articleId) // 查询比当前文章 ID 小的
|
||||
.last("limit 1")); // 第一条记录即为下一篇文章
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
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<ArticleTagRelDO> {
|
||||
|
||||
/**
|
||||
* 根据文章 ID 删除关联记录
|
||||
* @param articleId
|
||||
* @return
|
||||
*/
|
||||
default int deleteByArticleId(Long articleId) {
|
||||
return delete(Wrappers.<ArticleTagRelDO>lambdaQuery()
|
||||
.eq(ArticleTagRelDO::getArticleId, articleId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文章 ID 来查询
|
||||
* @param articleId
|
||||
* @return
|
||||
*/
|
||||
default List<ArticleTagRelDO> selectByArticleId(Long articleId) {
|
||||
return selectList(Wrappers.<ArticleTagRelDO>lambdaQuery()
|
||||
.eq(ArticleTagRelDO::getArticleId, articleId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据标签 ID 查询
|
||||
* @param tagId
|
||||
* @return
|
||||
*/
|
||||
default ArticleTagRelDO selectOneByTagId(Long tagId) {
|
||||
return selectOne(Wrappers.<ArticleTagRelDO>lambdaQuery()
|
||||
.eq(ArticleTagRelDO::getTagId, tagId)
|
||||
.last("LIMIT 1"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文章 ID 集合批量查询
|
||||
* @param articleIds
|
||||
* @return
|
||||
*/
|
||||
default List<ArticleTagRelDO> selectByArticleIds(List<Long> articleIds) {
|
||||
return selectList(Wrappers.<ArticleTagRelDO>lambdaQuery()
|
||||
.in(ArticleTagRelDO::getArticleId, articleIds));
|
||||
}
|
||||
}
|
||||
@ -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<BlogSettingsDO> {
|
||||
}
|
||||
@ -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<CategoryDO> {
|
||||
|
||||
/**
|
||||
* 查询分类分页数据
|
||||
* @return
|
||||
*/
|
||||
default Page<CategoryDO> selectPageList(long current, long size, String name, LocalDate startDate, LocalDate endDate) {
|
||||
// 分页对象(查询第几页、每页多少数据)
|
||||
Page<CategoryDO> page = new Page<>(current, size);
|
||||
|
||||
// 构建查询条件
|
||||
LambdaQueryWrapper<CategoryDO> 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<CategoryDO> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(CategoryDO::getName, categoryName);
|
||||
|
||||
// 执行查询
|
||||
return selectOne(wrapper);
|
||||
}
|
||||
}
|
||||
@ -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<TagDO> {
|
||||
|
||||
/**
|
||||
* 分页查询
|
||||
* @param current
|
||||
* @param size
|
||||
* @param name
|
||||
* @param startDate
|
||||
* @param endDate
|
||||
* @return
|
||||
*/
|
||||
default Page<TagDO> selectPageList(long current, long size, String name, LocalDate startDate, LocalDate endDate) {
|
||||
// 分页对象
|
||||
Page<TagDO> page = new Page<>(current, size);
|
||||
|
||||
// 构建查询条件
|
||||
LambdaQueryWrapper<TagDO> 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<TagDO> selectByKey(String key) {
|
||||
LambdaQueryWrapper<TagDO> wrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
// 构造模糊查询的条件
|
||||
wrapper.like(TagDO::getName, key).orderByDesc(TagDO::getCreateTime);
|
||||
|
||||
return selectList(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据标签 ID 批量查询
|
||||
* @param tagIds
|
||||
* @return
|
||||
*/
|
||||
default List<TagDO> selectByIds(List<Long> tagIds) {
|
||||
return selectList(Wrappers.<TagDO>lambdaQuery()
|
||||
.in(TagDO::getId, tagIds));
|
||||
}
|
||||
}
|
||||
@ -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<UserDO> {
|
||||
default UserDO findByUsername(String username) {
|
||||
LambdaQueryWrapper<UserDO> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(UserDO::getUsername, username);
|
||||
return selectOne(wrapper);
|
||||
}
|
||||
|
||||
default int updatePasswordByUsername(String username, String password) {
|
||||
LambdaUpdateWrapper<UserDO> wrapper = new LambdaUpdateWrapper<>();
|
||||
// 设置要更新的字段
|
||||
wrapper.set(UserDO::getPassword, password);
|
||||
wrapper.set(UserDO::getUpdateTime, LocalDateTime.now());
|
||||
// 更新条件
|
||||
wrapper.eq(UserDO::getUsername, username);
|
||||
|
||||
return update(null, wrapper);
|
||||
}
|
||||
}
|
||||
@ -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<UserRoleDO> {
|
||||
/**
|
||||
* 根据用户名查询
|
||||
* @param username
|
||||
* @return
|
||||
*/
|
||||
default List<UserRoleDO> selectByUsername(String username) {
|
||||
LambdaQueryWrapper<UserRoleDO> wrapper = new LambdaQueryWrapper<>();
|
||||
wrapper.eq(UserRoleDO::getUsername, username);
|
||||
|
||||
return selectList(wrapper);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @author: 犬小哈
|
||||
* @url: www.quanxiaoha.com
|
||||
* @date: 2023-08-22 16:57
|
||||
* @description: TODO
|
||||
**/
|
||||
package com.quanxiaoha.weblog.common.domain;
|
||||
@ -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;
|
||||
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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<Object> 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<Object> 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<Object> handleOtherException(HttpServletRequest request, Exception e) {
|
||||
log.error("{} request error, ", request.getRequestURI(), e);
|
||||
return Response.fail(ResponseCodeEnum.SYSTEM_ERROR);
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @author: 犬小哈
|
||||
* @url: www.quanxiaoha.com
|
||||
* @date: 2023-09-19 8:53
|
||||
* @description: TODO
|
||||
**/
|
||||
package com.quanxiaoha.weblog.common.model;
|
||||
@ -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;
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* @author: 犬小哈
|
||||
* @url: www.quanxiaoha.com
|
||||
* @date: 2023-08-10 9:20
|
||||
* @description: TODO
|
||||
**/
|
||||
package com.quanxiaoha.weblog.common;
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<T> extends Response<List<T>> {
|
||||
|
||||
/**
|
||||
* 总记录数
|
||||
*/
|
||||
private long total = 0L;
|
||||
|
||||
/**
|
||||
* 每页显示的记录数,默认每页显示 10 条
|
||||
*/
|
||||
private long size = 10L;
|
||||
|
||||
/**
|
||||
* 当前页码
|
||||
*/
|
||||
private long current;
|
||||
|
||||
/**
|
||||
* 总页数
|
||||
*/
|
||||
private long pages;
|
||||
|
||||
/**
|
||||
* 成功响应
|
||||
* @param page Mybatis Plus 提供的分页接口
|
||||
* @param data
|
||||
* @return
|
||||
* @param <T>
|
||||
*/
|
||||
public static <T> PageResponse<T> success(IPage page, List<T> data) {
|
||||
PageResponse<T> 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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<T> implements Serializable {
|
||||
|
||||
// 是否成功,默认为 true
|
||||
private boolean success = true;
|
||||
// 响应消息
|
||||
private String message;
|
||||
// 异常码
|
||||
private String errorCode;
|
||||
// 响应数据
|
||||
private T data;
|
||||
|
||||
// =================================== 成功响应 ===================================
|
||||
public static <T> Response<T> success() {
|
||||
Response<T> response = new Response<>();
|
||||
return response;
|
||||
}
|
||||
|
||||
public static <T> Response<T> success(T data) {
|
||||
Response<T> response = new Response<>();
|
||||
response.setData(data);
|
||||
return response;
|
||||
}
|
||||
|
||||
// =================================== 失败响应 ===================================
|
||||
public static <T> Response<T> fail() {
|
||||
Response<T> response = new Response<>();
|
||||
response.setSuccess(false);
|
||||
return response;
|
||||
}
|
||||
|
||||
public static <T> Response<T> fail(String errorMessage) {
|
||||
Response<T> response = new Response<>();
|
||||
response.setSuccess(false);
|
||||
response.setMessage(errorMessage);
|
||||
return response;
|
||||
}
|
||||
|
||||
public static <T> Response<T> fail(String errorCode, String errorMessage) {
|
||||
Response<T> response = new Response<>();
|
||||
response.setSuccess(false);
|
||||
response.setErrorCode(errorCode);
|
||||
response.setMessage(errorMessage);
|
||||
return response;
|
||||
}
|
||||
|
||||
public static <T> Response<T> fail(BizException bizException) {
|
||||
Response<T> response = new Response<>();
|
||||
response.setSuccess(false);
|
||||
response.setErrorCode(bizException.getErrorCode());
|
||||
response.setMessage(bizException.getErrorMessage());
|
||||
return response;
|
||||
}
|
||||
|
||||
public static <T> Response<T> fail(BaseExceptionInterface baseExceptionInterface) {
|
||||
Response<T> response = new Response<>();
|
||||
response.setSuccess(false);
|
||||
response.setErrorCode(baseExceptionInterface.getErrorCode());
|
||||
response.setMessage(baseExceptionInterface.getErrorMessage());
|
||||
return response;
|
||||
}
|
||||
|
||||
}
|
||||
@ -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() {
|
||||
}
|
||||
|
||||
}
|
||||
33
weblog-springboot-133/weblog-module-jwt/.gitignore
vendored
Normal file
33
weblog-springboot-133/weblog-module-jwt/.gitignore
vendored
Normal file
@ -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/
|
||||
74
weblog-springboot-133/weblog-module-jwt/pom.xml
Normal file
74
weblog-springboot-133/weblog-module-jwt/pom.xml
Normal file
@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.quanxiaoha</groupId>
|
||||
<artifactId>weblog-springboot</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.quanxiaoha</groupId>
|
||||
<artifactId>weblog-module-jwt</artifactId>
|
||||
<name>weblog-module-jwt</name>
|
||||
<description>weblog-module-jwt (JWT 模块,管理用户认证、鉴权)</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- 免写冗余的 Java 样板式代码 -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- 单元测试 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Security -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- JWT -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 工具包 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 通用模块 -->
|
||||
<dependency>
|
||||
<groupId>com.quanxiaoha</groupId>
|
||||
<artifactId>weblog-module-common</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@ -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<DefaultSecurityFilterChain, HttpSecurity> {
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
@ -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"));
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user