Compare commits
29 Commits
cd6a13ba9d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15eca0d0b5 | ||
|
|
f27be2130c | ||
|
|
9b01ee8889 | ||
|
|
505a7a0944 | ||
|
|
51392bf807 | ||
|
|
eb1f70d431 | ||
|
|
47c357695b | ||
|
|
d8c6c74de4 | ||
|
|
5136a3a78b | ||
|
|
25eeab4940 | ||
|
|
f6d1d719a9 | ||
|
|
5803080352 | ||
|
|
9132feb870 | ||
|
|
46be613f28 | ||
|
|
f53e251d46 | ||
|
|
848b13506c | ||
|
|
effcc3838d | ||
|
|
bd6b240f52 | ||
|
|
ffea3e85ae | ||
|
|
8cc4c1da1d | ||
|
|
2809837422 | ||
|
|
299c9a57ec | ||
|
|
470cf71713 | ||
|
|
60f4752124 | ||
|
|
fdb0608751 | ||
|
|
92c604e1f5 | ||
|
|
6841ba67f1 | ||
|
|
1d4dee573e | ||
|
|
f84c66b6db |
110
251010使用TRAEai.md
Normal file
110
251010使用TRAEai.md
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
# 技术问题总结文档
|
||||||
|
|
||||||
|
本文档总结了在项目开发和调试过程中遇到的主要技术问题、原因分析及解决方案。
|
||||||
|
|
||||||
|
## 一、事务回滚问题(核心问题)
|
||||||
|
|
||||||
|
### 问题描述
|
||||||
|
API接口调用时出现500错误,响应信息为:"Transaction silently rolled back because it has been marked as rollback-only"(事务被静默回滚,因为它已被标记为只能回滚)。
|
||||||
|
|
||||||
|
### 根本原因
|
||||||
|
通过详细分析,发现问题的根本原因是**@Param注解导入错误**:
|
||||||
|
|
||||||
|
在`ArticleRepository`接口中,项目使用的是Spring Data JPA框架,但错误地导入了MyBatis的`@Param`注解:
|
||||||
|
```java
|
||||||
|
import org.apache.ibatis.annotations.Param; // 错误的导入
|
||||||
|
```
|
||||||
|
|
||||||
|
这导致Spring Data JPA无法正确识别和绑定查询参数,从而在执行数据库操作时抛出异常,最终导致事务被标记为只能回滚。
|
||||||
|
|
||||||
|
### 解决方案
|
||||||
|
将MyBatis的`@Param`注解替换为Spring Data JPA正确的`@Param`注解:
|
||||||
|
```java
|
||||||
|
import org.springframework.data.repository.query.Param; // 正确的导入
|
||||||
|
```
|
||||||
|
|
||||||
|
## 二、方法参数与@Param注解不匹配问题
|
||||||
|
|
||||||
|
### 问题描述
|
||||||
|
在`ArticleRepository`中存在多个查询方法的参数名称与`@Param`注解值不匹配的情况:
|
||||||
|
|
||||||
|
1. `findPublishedByAuthor`方法:
|
||||||
|
- 方法参数为`id`
|
||||||
|
- `@Param`注解值为`"authorId"`
|
||||||
|
- 但`Article`实体中不存在`authorId`字段
|
||||||
|
|
||||||
|
2. `findPublishedByCategory`方法:
|
||||||
|
- 方法参数为`typeid`
|
||||||
|
- `@Param`注解值为`"categoryId"`
|
||||||
|
- JPQL查询中使用`:categoryId`参数
|
||||||
|
|
||||||
|
### 解决方案
|
||||||
|
1. 对于`findPublishedByAuthor`方法:
|
||||||
|
- 由于`Article`实体中不存在`authorId`字段,移除了相关的查询条件和参数
|
||||||
|
- 仅保留状态筛选条件:`SELECT a FROM Article a WHERE a.status = 1`
|
||||||
|
- 修改方法签名为无参方法:`List<Article> findPublishedByAuthor()`
|
||||||
|
|
||||||
|
2. 对于`findPublishedByCategory`方法:
|
||||||
|
- 统一参数名称,修改方法参数名为`categoryId`
|
||||||
|
- 确保`@Param`注解值和JPQL查询参数名保持一致
|
||||||
|
|
||||||
|
## 三、事务配置问题
|
||||||
|
|
||||||
|
### 问题描述
|
||||||
|
在`ArticleService`的`getArticleById`方法中,最初使用了`@Transactional(readOnly = true)`注解,但方法内部却调用了`incrementViewCount`写操作方法,导致事务冲突。
|
||||||
|
|
||||||
|
### 解决方案
|
||||||
|
尝试了两种方案:
|
||||||
|
1. 第一种方案:将`@Transactional(readOnly = true)`修改为普通的`@Transactional`注解,允许在事务内执行写操作
|
||||||
|
2. 第二种方案(最终采用):为了排查问题,暂时注释掉了`incrementViewCount`方法调用,将事务配置改回`@Transactional(readOnly = true)`
|
||||||
|
|
||||||
|
但根本问题解决后(修复了@Param注解导入错误),两种配置都可以正常工作。
|
||||||
|
|
||||||
|
## 四、配置文件优化问题
|
||||||
|
|
||||||
|
### 问题描述
|
||||||
|
项目的`application.properties`配置文件存在一些可以优化的地方,包括:
|
||||||
|
1. 数据库URL中的拼写错误
|
||||||
|
2. 未使用的MyBatis配置残留
|
||||||
|
3. 数据库连接池配置不完整
|
||||||
|
4. JPA性能配置缺失
|
||||||
|
5. Redis连接池配置不合理
|
||||||
|
6. 安全性配置不完善
|
||||||
|
|
||||||
|
### 解决方案
|
||||||
|
对配置文件进行了全面优化,主要包括:
|
||||||
|
|
||||||
|
1. **数据库配置优化**:
|
||||||
|
- 修复了数据库URL拼写错误
|
||||||
|
- 添加了字符集、SSL、时区等连接参数
|
||||||
|
|
||||||
|
2. **移除不必要的配置**:
|
||||||
|
- 删除了未使用的MyBatis配置
|
||||||
|
|
||||||
|
3. **性能优化配置**:
|
||||||
|
- 完善了Hikari连接池配置
|
||||||
|
- 添加了JPA批量操作和缓存配置
|
||||||
|
- 优化了Redis连接池参数
|
||||||
|
|
||||||
|
4. **安全性增强**:
|
||||||
|
- 限制了Actuator暴露的端点
|
||||||
|
- 完善了JWT和CORS配置
|
||||||
|
|
||||||
|
5. **添加必要的配置**:
|
||||||
|
- 会话管理配置
|
||||||
|
- 国际化配置
|
||||||
|
- 响应编码配置
|
||||||
|
|
||||||
|
## 五、其他相关问题
|
||||||
|
|
||||||
|
### Article实体与查询方法不匹配
|
||||||
|
在排查过程中发现,`ArticleRepository`中的某些查询方法引用了`Article`实体中不存在的字段(如`authorId`),这表明在开发过程中可能存在实体设计与数据访问层不一致的情况。
|
||||||
|
|
||||||
|
### 日志级别配置
|
||||||
|
为了更好地排查问题,调整了日志配置,将核心包的日志级别设置为DEBUG,同时限制了其他框架的日志输出,避免日志信息过于冗长。
|
||||||
|
|
||||||
|
## 六、总结
|
||||||
|
|
||||||
|
本次问题排查和修复过程中,我们发现了多个相互关联的技术问题,其中最核心的问题是**@Param注解导入错误**。这一问题导致了一系列连锁反应,最终表现为事务回滚错误。
|
||||||
|
|
||||||
|
通过系统性地分析和解决这些问题,我们不仅修复了API功能,还优化了项目的整体配置和性能。这个过程也提醒我们在开发过程中要特别注意框架注解的正确使用,以及保持代码各部分之间的一致性。
|
||||||
1191
README_API.md
Normal file
1191
README_API.md
Normal file
File diff suppressed because it is too large
Load Diff
3542
logs/web_project.log
Normal file
3542
logs/web_project.log
Normal file
File diff suppressed because it is too large
Load Diff
69
pom.xml
69
pom.xml
@@ -18,12 +18,24 @@
|
|||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring Security 核心依赖 -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mybatis.spring.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>mybatis-spring-boot-starter</artifactId>
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
<version>2.2.2</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Spring Security 配置 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.security</groupId>
|
||||||
|
<artifactId>spring-security-config</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- AOP支持(用于方法级安全控制) -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-aop</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-devtools</artifactId>
|
<artifactId>spring-boot-devtools</artifactId>
|
||||||
@@ -43,13 +55,36 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.projectlombok</groupId>
|
<groupId>org.projectlombok</groupId>
|
||||||
<artifactId>lombok</artifactId>
|
<artifactId>lombok</artifactId>
|
||||||
<optional>true</optional>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!--参数校验-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<!-- Markdown解析库 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.commonmark</groupId>
|
||||||
|
<artifactId>commonmark</artifactId>
|
||||||
|
<version>0.18.2</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- JWT依赖 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.jsonwebtoken</groupId>
|
||||||
|
<artifactId>jjwt</artifactId>
|
||||||
|
<version>0.9.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@@ -61,6 +96,7 @@
|
|||||||
<scope>import</scope>
|
<scope>import</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
@@ -68,7 +104,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>3.8.1</version>
|
<version>3.7.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<source>1.8</source>
|
<source>1.8</source>
|
||||||
<target>1.8</target>
|
<target>1.8</target>
|
||||||
@@ -81,7 +117,8 @@
|
|||||||
<version>${spring-boot.version}</version>
|
<version>${spring-boot.version}</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<mainClass>com.qf.myafterprojecy.MyAfterProjecyApplication</mainClass>
|
<mainClass>com.qf.myafterprojecy.MyAfterProjecyApplication</mainClass>
|
||||||
<skip>true</skip>
|
<skip>false</skip>
|
||||||
|
<jvmArguments>-Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8</jvmArguments>
|
||||||
</configuration>
|
</configuration>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
@@ -92,7 +129,27 @@
|
|||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<!-- 添加资源编码配置 -->
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
|
<version>3.3.1</version>
|
||||||
|
<configuration>
|
||||||
|
<encoding>UTF-8</encoding>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
|
<!-- 确保项目编译和资源处理使用UTF-8编码 -->
|
||||||
|
<sourceDirectory>src/main/java</sourceDirectory>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
<includes>
|
||||||
|
<include>**/*</include>
|
||||||
|
</includes>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package com.qf.myafterprojecy;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用主类
|
||||||
|
* 设置系统编码并启动Spring Boot应用
|
||||||
|
*/
|
||||||
|
@SpringBootApplication
|
||||||
|
public class MyAfterProjecyApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// 在应用启动前设置系统编码,确保所有输出都使用UTF-8
|
||||||
|
setSystemEncoding();
|
||||||
|
|
||||||
|
// 启动Spring Boot应用
|
||||||
|
SpringApplication.run(MyAfterProjecyApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置系统编码为UTF-8
|
||||||
|
* 解决控制台输出和日志中的中文乱码问题
|
||||||
|
*/
|
||||||
|
private static void setSystemEncoding() {
|
||||||
|
// 设置系统属性,确保所有输出流都使用UTF-8编码
|
||||||
|
System.setProperty("file.encoding", StandardCharsets.UTF_8.name());
|
||||||
|
System.setProperty("sun.stdout.encoding", StandardCharsets.UTF_8.name());
|
||||||
|
System.setProperty("sun.stderr.encoding", StandardCharsets.UTF_8.name());
|
||||||
|
|
||||||
|
// 设置默认字符编码
|
||||||
|
try {
|
||||||
|
java.nio.charset.Charset.defaultCharset();
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 记录编码设置异常
|
||||||
|
System.err.println("设置默认字符编码时发生异常: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
package com.qf.myafterprojecy.config;
|
||||||
|
|
||||||
|
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.web.filter.CharacterEncodingFilter;
|
||||||
|
|
||||||
|
import javax.servlet.Filter;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字符编码配置类
|
||||||
|
* 确保所有HTTP请求和响应都使用UTF-8编码,解决中文乱码问题
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class CharacterEncodingConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建字符编码过滤器
|
||||||
|
* 优先级设置为最高,确保在所有其他过滤器之前执行
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public FilterRegistrationBean<Filter> characterEncodingFilter() {
|
||||||
|
// 创建字符编码过滤器
|
||||||
|
CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
|
||||||
|
|
||||||
|
// 设置请求编码为UTF-8
|
||||||
|
encodingFilter.setEncoding(StandardCharsets.UTF_8.name());
|
||||||
|
|
||||||
|
// 强制请求使用UTF-8编码
|
||||||
|
encodingFilter.setForceRequestEncoding(true);
|
||||||
|
|
||||||
|
// 强制响应使用UTF-8编码
|
||||||
|
encodingFilter.setForceResponseEncoding(true);
|
||||||
|
|
||||||
|
// 创建过滤器注册Bean
|
||||||
|
FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>(encodingFilter);
|
||||||
|
|
||||||
|
// 设置过滤器顺序为最高优先级
|
||||||
|
registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
|
||||||
|
|
||||||
|
// 为所有请求路径注册过滤器
|
||||||
|
registrationBean.addUrlPatterns("/*");
|
||||||
|
|
||||||
|
return registrationBean;
|
||||||
|
}
|
||||||
|
}
|
||||||
80
src/main/java/com/qf/myafterprojecy/config/CorsConfig.java
Normal file
80
src/main/java/com/qf/myafterprojecy/config/CorsConfig.java
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
package com.qf.myafterprojecy.config;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
|
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||||
|
import org.springframework.web.filter.CorsFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CORS配置类,用于解决跨域问题
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class CorsConfig {
|
||||||
|
|
||||||
|
// 从配置文件中读取CORS配置
|
||||||
|
@Value("${cors.allowed-origins}")
|
||||||
|
private String allowedOrigins;
|
||||||
|
|
||||||
|
@Value("${cors.allowed-methods}")
|
||||||
|
private String allowedMethods;
|
||||||
|
|
||||||
|
@Value("${cors.allowed-headers}")
|
||||||
|
private String allowedHeaders;
|
||||||
|
|
||||||
|
@Value("${cors.allow-credentials}")
|
||||||
|
private Boolean allowCredentials;
|
||||||
|
|
||||||
|
@Value("${cors.max-age:3600}")
|
||||||
|
private Long maxAge;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建CORS过滤器,配置跨域请求的规则
|
||||||
|
* 从配置文件中读取CORS配置,实现配置的统一管理
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public CorsFilter corsFilter() {
|
||||||
|
// 创建CORS配置对象
|
||||||
|
CorsConfiguration config = new CorsConfiguration();
|
||||||
|
|
||||||
|
// 允许的来源,从配置文件读取并分割
|
||||||
|
String[] originsArray = allowedOrigins.split(",");
|
||||||
|
for (String origin : originsArray) {
|
||||||
|
config.addAllowedOrigin(origin.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 允许携带凭证(如Cookie)
|
||||||
|
config.setAllowCredentials(allowCredentials);
|
||||||
|
|
||||||
|
// 允许的HTTP方法,从配置文件读取并分割
|
||||||
|
String[] methodsArray = allowedMethods.split(",");
|
||||||
|
for (String method : methodsArray) {
|
||||||
|
config.addAllowedMethod(method.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 允许的请求头,从配置文件读取
|
||||||
|
config.addAllowedHeader(allowedHeaders);
|
||||||
|
|
||||||
|
// 明确暴露的响应头,对于JWT认证很重要
|
||||||
|
config.addExposedHeader("Authorization");
|
||||||
|
config.addExposedHeader("Content-Type");
|
||||||
|
config.addExposedHeader("X-Requested-With");
|
||||||
|
config.addExposedHeader("Accept");
|
||||||
|
config.addExposedHeader("Access-Control-Allow-Origin");
|
||||||
|
config.addExposedHeader("Access-Control-Allow-Credentials");
|
||||||
|
|
||||||
|
// 设置预检请求的有效期(秒),从配置文件读取
|
||||||
|
config.setMaxAge(maxAge);
|
||||||
|
|
||||||
|
// 创建基于URL的CORS配置源
|
||||||
|
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||||
|
|
||||||
|
// 为所有路径应用CORS配置
|
||||||
|
source.registerCorsConfiguration("/**", config);
|
||||||
|
|
||||||
|
// 返回配置好的CORS过滤器
|
||||||
|
return new CorsFilter(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package com.qf.myafterprojecy.config;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.pojo.Users;
|
||||||
|
import com.qf.myafterprojecy.repository.UsersRepository;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义的UserDetailsService实现
|
||||||
|
* 用于从数据库加载用户信息进行认证
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class CustomUserDetailsService implements UserDetailsService {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(CustomUserDetailsService.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UsersRepository usersRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
|
||||||
|
logger.info("用户登录认证: {}", username);
|
||||||
|
|
||||||
|
// 从数据库中查询用户
|
||||||
|
Users user = usersRepository.findByUsername(username)
|
||||||
|
.orElseThrow(() -> new UsernameNotFoundException("用户不存在: " + username));
|
||||||
|
|
||||||
|
// 转换用户角色为Spring Security的权限
|
||||||
|
// 根据role字段的值设置不同的角色权限
|
||||||
|
String role = "ROLE_USER"; // 默认角色
|
||||||
|
if (user.getRole() == 1) {
|
||||||
|
role = "ROLE_ADMIN"; // 管理员角色
|
||||||
|
}
|
||||||
|
|
||||||
|
List<SimpleGrantedAuthority> authorities = Collections.singletonList(new SimpleGrantedAuthority(role));
|
||||||
|
|
||||||
|
// 返回Spring Security的User对象
|
||||||
|
return new org.springframework.security.core.userdetails.User(
|
||||||
|
user.getUsername(),
|
||||||
|
user.getPassword(),
|
||||||
|
authorities
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package com.qf.myafterprojecy.config;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.utils.JwtUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
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.authentication.WebAuthenticationDetailsSource;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JWT认证过滤器,用于验证token并授权用户
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class JwtAuthenticationFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private JwtUtils jwtUtils;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserDetailsService userDetailsService;
|
||||||
|
|
||||||
|
@Value("${jwt.header:Authorization}")
|
||||||
|
private String tokenHeader;
|
||||||
|
|
||||||
|
@Value("${jwt.token-prefix:Bearer}")
|
||||||
|
private String tokenPrefix;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
|
||||||
|
try {
|
||||||
|
// 获取token
|
||||||
|
String token = getTokenFromRequest(request);
|
||||||
|
System.out.println(token);
|
||||||
|
if (token != null && validateToken(token)) {
|
||||||
|
// 从token中获取用户名
|
||||||
|
String username = jwtUtils.getUsernameFromToken(token);
|
||||||
|
System.out.println("username: " + username);
|
||||||
|
// 加载用户信息
|
||||||
|
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
|
||||||
|
|
||||||
|
// 创建认证对象
|
||||||
|
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
|
||||||
|
userDetails, null, userDetails.getAuthorities());
|
||||||
|
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
|
||||||
|
|
||||||
|
// 设置认证信息到上下文
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("无法设置用户认证: {}", e);
|
||||||
|
SecurityContextHolder.clearContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从请求头中获取token
|
||||||
|
*/
|
||||||
|
private String getTokenFromRequest(HttpServletRequest request) {
|
||||||
|
String bearerToken = request.getHeader(tokenHeader);
|
||||||
|
if (bearerToken != null && bearerToken.startsWith(tokenPrefix + " ")) {
|
||||||
|
return bearerToken.substring(tokenPrefix.length() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证token
|
||||||
|
*/
|
||||||
|
private boolean validateToken(String token) {
|
||||||
|
try {
|
||||||
|
String username = jwtUtils.getUsernameFromToken(token);
|
||||||
|
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
|
||||||
|
return jwtUtils.validateToken(token, userDetails);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("无效的token: {}", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.qf.myafterprojecy.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密码编码器配置类
|
||||||
|
* 用于配置Spring Security使用的密码加密方式
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class PasswordEncoderConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建BCrypt密码编码器
|
||||||
|
* BCrypt是一种强哈希函数,适合密码存储
|
||||||
|
* @return PasswordEncoder实例
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public PasswordEncoder passwordEncoder() {
|
||||||
|
// 强度设置为10,这是一个平衡安全性和性能的值
|
||||||
|
// 数值越高,计算成本越大,安全性越好
|
||||||
|
return new BCryptPasswordEncoder(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
135
src/main/java/com/qf/myafterprojecy/config/SecurityConfig.java
Normal file
135
src/main/java/com/qf/myafterprojecy/config/SecurityConfig.java
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
package com.qf.myafterprojecy.config;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
|
||||||
|
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.authentication.configuration.AuthenticationConfiguration;
|
||||||
|
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.http.SessionCreationPolicy;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring Security配置类
|
||||||
|
* 配置权限管理功能
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@EnableWebSecurity
|
||||||
|
// 启用方法级别的安全控制
|
||||||
|
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
|
||||||
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserDetailsService userDetailsService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private JwtAuthenticationFilter jwtAuthenticationFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置AuthenticationManager Bean
|
||||||
|
* 使用AuthenticationConfiguration来获取认证管理器,这是更现代的方式
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
|
||||||
|
return authenticationConfiguration.getAuthenticationManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置安全过滤器链
|
||||||
|
* @param http HttpSecurity对象,用于配置HTTP安全策略
|
||||||
|
* @return 配置好的SecurityFilterChain对象
|
||||||
|
* @throws Exception 配置过程中可能出现的异常
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
http
|
||||||
|
// 启用CORS支持,确保与CorsConfig中配置的过滤器配合工作
|
||||||
|
.cors().and()
|
||||||
|
// 禁用CSRF保护(对于API服务通常不需要)
|
||||||
|
.csrf().disable()
|
||||||
|
// 配置URL访问权限
|
||||||
|
.authorizeRequests()
|
||||||
|
// 允许公开访问的路径
|
||||||
|
// 登录和认证相关端点应该全部公开
|
||||||
|
.antMatchers(HttpMethod.POST,"/api/auth/**").permitAll()
|
||||||
|
// 文章浏览量增加接口公开
|
||||||
|
.antMatchers(HttpMethod.POST,"/api/articles/view/**").permitAll()
|
||||||
|
// 所有GET请求公开
|
||||||
|
.antMatchers(HttpMethod.GET,"/api/**").permitAll()
|
||||||
|
// 公开评论新增接口
|
||||||
|
.antMatchers(HttpMethod.POST,"/api/messages").permitAll()
|
||||||
|
// 新增、删除、修改操作需要管理员权限
|
||||||
|
.antMatchers(HttpMethod.POST,"/api/**").hasRole("ADMIN")
|
||||||
|
.antMatchers(HttpMethod.PUT,"/api/**").hasRole("ADMIN")
|
||||||
|
.antMatchers(HttpMethod.DELETE,"/api/**").hasRole("ADMIN")
|
||||||
|
// 管理员才能访问的路径
|
||||||
|
.antMatchers("/api/admin/**").hasRole("ADMIN")
|
||||||
|
// 其他所有请求都需要认证
|
||||||
|
.anyRequest().authenticated()
|
||||||
|
.and()
|
||||||
|
// 配置会话管理,使用无状态会话策略
|
||||||
|
// 这意味着每个请求都需要包含认证信息(如JWT)
|
||||||
|
.sessionManagement()
|
||||||
|
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
||||||
|
.and()
|
||||||
|
// 确保OPTIONS请求能够通过,处理预检请求
|
||||||
|
.exceptionHandling()
|
||||||
|
.authenticationEntryPoint((request, response, authException) -> {
|
||||||
|
// 设置CORS头信息
|
||||||
|
response.setHeader("Access-Control-Allow-Origin", "*");
|
||||||
|
response.setHeader("Access-Control-Allow-Credentials", "true");
|
||||||
|
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, PATCH");
|
||||||
|
response.setHeader("Access-Control-Allow-Headers", "*");
|
||||||
|
response.setHeader("Access-Control-Max-Age", "3600");
|
||||||
|
|
||||||
|
// 如果是预检请求,直接返回200
|
||||||
|
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
|
||||||
|
response.setStatus(HttpServletResponse.SC_OK);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 未认证处理
|
||||||
|
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||||
|
response.setContentType("application/json;charset=UTF-8");
|
||||||
|
response.getWriter().write("{\"message\": \"未授权访问,请先登录\"}");
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加JWT认证过滤器
|
||||||
|
http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||||
|
|
||||||
|
// 确保Spring Security不会添加额外的CharacterEncodingFilter
|
||||||
|
// 因为我们在CharacterEncodingConfig中已经配置了自定义的过滤器
|
||||||
|
http.addFilterBefore((request, response, chain) -> {
|
||||||
|
// 确保响应使用UTF-8编码
|
||||||
|
response.setCharacterEncoding("UTF-8");
|
||||||
|
response.setContentType("application/json;charset=UTF-8");
|
||||||
|
chain.doFilter(request, response);
|
||||||
|
}, JwtAuthenticationFilter.class);
|
||||||
|
|
||||||
|
// 配置访问拒绝处理器
|
||||||
|
http.exceptionHandling()
|
||||||
|
.accessDeniedHandler((request, response, accessDeniedException) -> {
|
||||||
|
// 设置CORS头信息
|
||||||
|
response.setHeader("Access-Control-Allow-Origin", "*");
|
||||||
|
response.setHeader("Access-Control-Allow-Credentials", "true");
|
||||||
|
|
||||||
|
// 无权限处理
|
||||||
|
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
|
||||||
|
response.setContentType("application/json;charset=UTF-8");
|
||||||
|
response.getWriter().write("{\"message\": \"权限不足,无法访问\"}");
|
||||||
|
});
|
||||||
|
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
package com.qf.myafterprojecy.controller;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
|
||||||
|
import com.qf.myafterprojecy.pojo.Article;
|
||||||
|
import com.qf.myafterprojecy.pojo.dto.ArticleDto;
|
||||||
|
import com.qf.myafterprojecy.pojo.dto.ArriclePageDto;
|
||||||
|
import com.qf.myafterprojecy.service.IArticleService;
|
||||||
|
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文章控制器类,处理文章相关的HTTP请求
|
||||||
|
* 提供文章的增删改查功能,以及按作者、分类和浏览量获取文章的接口
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/articles")
|
||||||
|
|
||||||
|
@Validated
|
||||||
|
public class ArticleController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IArticleService articleService; // 注入文章服务接口
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID获取单个文章
|
||||||
|
* @param id 文章ID
|
||||||
|
* @return 返回包含文章信息的ResponseMessage对象
|
||||||
|
*/
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public ResponseMessage<Article> getArticle(@PathVariable String id) {
|
||||||
|
return articleService.getArticleById(id);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取已发布或未发布的文章列表
|
||||||
|
* @return 返回包含已发布文章列表的ResponseMessage对象
|
||||||
|
*/
|
||||||
|
@GetMapping("/published")
|
||||||
|
public ResponseMessage<List<Article>> getPublishedArticles() {
|
||||||
|
return articleService.getPublishedArticles();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 根据状态获取文章列表
|
||||||
|
* @param status 文章状态(0:未发表 1:已发表 2:已删除)
|
||||||
|
* @return 返回包含文章列表的ResponseMessage对象
|
||||||
|
*/
|
||||||
|
@GetMapping("/status/{status}")
|
||||||
|
public ResponseMessage<List<Article>> getArticlesByStatus(@PathVariable Integer status) {
|
||||||
|
return articleService.getArticlesByStatus(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据状态分页获取文章列表
|
||||||
|
* @param status 文章状态(0:未发表 1:已发表 2:已删除)
|
||||||
|
* @param page 页码,从0开始(可选,默认为0)
|
||||||
|
* @param size 每页大小(可选,默认为10,最大为100)
|
||||||
|
* @return 返回包含分页文章列表的ResponseMessage对象
|
||||||
|
*/
|
||||||
|
// api/articles/status/page?status=1&page=1&size=2
|
||||||
|
// get 只能这样不能传递json
|
||||||
|
@GetMapping("/status/page")
|
||||||
|
public ResponseMessage<Page<Article>> getArticlesByStatusWithPagination(ArriclePageDto pageDto) {
|
||||||
|
return articleService.getArticlesByStatusWithPagination(pageDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有文章列表
|
||||||
|
* @return 返回包含文章列表的ResponseMessage对象
|
||||||
|
*/
|
||||||
|
@GetMapping
|
||||||
|
public ResponseMessage<List<Article>> getAllArticles() {
|
||||||
|
return articleService.getAllArticles();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取文章数量
|
||||||
|
* @param status 文章状态(0:未发表 1:已发表 2:已删除)
|
||||||
|
* @return 返回文章数量
|
||||||
|
*/
|
||||||
|
@GetMapping("/count/{status}")
|
||||||
|
public ResponseMessage<Integer> getArticleCount(@PathVariable Integer status) {
|
||||||
|
return articleService.getArticleCount(status);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 根据标题查询文章列表
|
||||||
|
* @param title 文章标题
|
||||||
|
* @return 返回包含文章列表的ResponseMessage对象
|
||||||
|
*/
|
||||||
|
@GetMapping("/title/{title}")
|
||||||
|
public ResponseMessage<List<Article>> getArticlesByTitle(@PathVariable String title) {
|
||||||
|
return articleService.getArticlesByTitle(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据属性ID获取该属性下的所有文章
|
||||||
|
* @param attributeId 属性ID
|
||||||
|
* @return 返回包含文章列表的ResponseMessage对象
|
||||||
|
*/
|
||||||
|
@GetMapping("/attribute/{attributeId}")
|
||||||
|
public ResponseMessage<List<Article>> getArticlesByAttribute(@PathVariable Integer attributeId) {
|
||||||
|
return articleService.getArticlesByAttribute(attributeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据属性ID获取最新文章(按创建时间降序)
|
||||||
|
* @param attributeId 属性ID
|
||||||
|
* @return 返回包含最新文章列表的ResponseMessage对象
|
||||||
|
*/
|
||||||
|
@GetMapping("/attribute/{attributeId}/latest")
|
||||||
|
public ResponseMessage<List<Article>> getLatestArticlesByAttribute(@PathVariable Integer attributeId) {
|
||||||
|
return articleService.getLatestArticlesByAttribute(attributeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建新文章
|
||||||
|
* 仅限AUTHOR角色用户访问
|
||||||
|
* @param articleDto 包含文章数据的DTO对象
|
||||||
|
* @return 返回包含新创建文章信息的ResponseMessage对象
|
||||||
|
*/
|
||||||
|
@PostMapping
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
|
public ResponseMessage<Article> createArticle(@Valid @RequestBody ArticleDto articleDto) {
|
||||||
|
return articleService.saveArticle(articleDto);
|
||||||
|
}
|
||||||
|
/**文章浏览量
|
||||||
|
* 增加文章浏览量
|
||||||
|
* @param id 文章ID
|
||||||
|
* @return 返回包含更新后文章信息的ResponseMessage对象
|
||||||
|
*/
|
||||||
|
@PostMapping("/view/{id}")
|
||||||
|
public ResponseMessage<Article> incrementViewCount(@PathVariable Integer id) {
|
||||||
|
return articleService.incrementViewCount(id);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 更新现有文章
|
||||||
|
* 仅限AUTHOR角色用户访问
|
||||||
|
* @param id 要更新的文章ID
|
||||||
|
* @param articleDto 包含更新后文章数据的DTO对象
|
||||||
|
* @return 返回包含更新后文章信息的ResponseMessage对象
|
||||||
|
*/
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
|
public ResponseMessage<Article> updateArticle(
|
||||||
|
@PathVariable Integer id,
|
||||||
|
@Valid @RequestBody ArticleDto articleDto) {
|
||||||
|
return articleService.updateArticle(id, articleDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除文章
|
||||||
|
* 仅限AUTHOR或ADMIN角色用户访问
|
||||||
|
* @param id 要删除的文章ID
|
||||||
|
* @return 返回包含被删除文章信息的ResponseMessage对象
|
||||||
|
*/
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
|
public ResponseMessage<Article> deleteArticle(@PathVariable Integer id) {
|
||||||
|
return articleService.deleteArticle(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
package com.qf.myafterprojecy.controller;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
|
||||||
|
import com.qf.myafterprojecy.utils.JwtUtils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.authentication.AuthenticationManager;
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.AuthenticationException;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
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;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 认证控制器
|
||||||
|
* 处理用户登录相关请求
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/auth")
|
||||||
|
public class AuthController {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(AuthController.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private AuthenticationManager authenticationManager;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private JwtUtils jwtUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户登录请求体
|
||||||
|
*/
|
||||||
|
static class LoginRequest {
|
||||||
|
private String username;
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
// getters and setters
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户登录接口
|
||||||
|
* @param loginRequest 登录请求参数
|
||||||
|
* @return 登录结果
|
||||||
|
*/
|
||||||
|
@PostMapping("/login")
|
||||||
|
public ResponseMessage<Map<String, Object>> login(@RequestBody LoginRequest loginRequest) {
|
||||||
|
logger.info("用户登录请求: {}", loginRequest.getUsername());
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 创建认证令牌
|
||||||
|
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword());
|
||||||
|
|
||||||
|
// 执行认证
|
||||||
|
Authentication authentication = authenticationManager.authenticate(authenticationToken);
|
||||||
|
|
||||||
|
// 将认证信息存入上下文
|
||||||
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||||
|
|
||||||
|
// 获取认证后的用户信息
|
||||||
|
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
|
||||||
|
// 生成JWT token
|
||||||
|
String token = jwtUtils.generateToken(userDetails);
|
||||||
|
// 构建返回数据
|
||||||
|
Map<String, Object> data = new HashMap<>();
|
||||||
|
data.put("username", userDetails.getUsername());
|
||||||
|
data.put("authorities", userDetails.getAuthorities());
|
||||||
|
data.put("token", token);
|
||||||
|
data.put("tokenPrefix", jwtUtils.getTokenPrefix());
|
||||||
|
|
||||||
|
return ResponseMessage.success(data, "登录成功");
|
||||||
|
|
||||||
|
} catch (AuthenticationException e) {
|
||||||
|
logger.error("登录失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("用户名或密码错误");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前登录用户信息
|
||||||
|
* @return 当前用户信息
|
||||||
|
*/
|
||||||
|
@PostMapping("/info")
|
||||||
|
public ResponseMessage<Map<String, Object>> getCurrentUserInfo() {
|
||||||
|
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
|
||||||
|
if (authentication == null || !authentication.isAuthenticated()) {
|
||||||
|
return ResponseMessage.error("未登录");
|
||||||
|
}
|
||||||
|
|
||||||
|
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
|
||||||
|
Map<String, Object> data = new HashMap<>();
|
||||||
|
data.put("username", userDetails.getUsername());
|
||||||
|
data.put("authorities", userDetails.getAuthorities());
|
||||||
|
|
||||||
|
return ResponseMessage.success(data, "获取用户信息成功");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户登出接口
|
||||||
|
* @return 登出结果
|
||||||
|
*/
|
||||||
|
@PostMapping("/logout")
|
||||||
|
public ResponseMessage<Void> logout() {
|
||||||
|
SecurityContextHolder.clearContext();
|
||||||
|
return ResponseMessage.successEmpty("登出成功");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
package com.qf.myafterprojecy.controller;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
|
||||||
|
import com.qf.myafterprojecy.pojo.Categoryattribute;
|
||||||
|
import com.qf.myafterprojecy.pojo.dto.CategoryAttributeDto;
|
||||||
|
import com.qf.myafterprojecy.service.ICategoryAttributeService;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import javax.validation.Valid;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/category-attributes")
|
||||||
|
@Validated
|
||||||
|
public class CategoryAttributeController {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(CategoryAttributeController.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ICategoryAttributeService categoryAttributeService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID获取分类属性
|
||||||
|
* @param id 属性ID
|
||||||
|
* @return 属性信息
|
||||||
|
*/
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public ResponseMessage<Categoryattribute> getAttributeById(@PathVariable Integer id) {
|
||||||
|
log.info("接收根据ID获取分类属性的请求: ID={}", id);
|
||||||
|
return categoryAttributeService.getCategoryAttributeById(id);
|
||||||
|
}
|
||||||
|
@GetMapping
|
||||||
|
public ResponseMessage<List<Categoryattribute>> getAttributeCount() {
|
||||||
|
log.info("接收获取分类属性数量的请求");
|
||||||
|
return categoryAttributeService.getAllCategoryAttributes();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据分类ID获取属性列表
|
||||||
|
* @param categoryId 分类ID
|
||||||
|
* @return 属性列表
|
||||||
|
*/
|
||||||
|
@GetMapping("/category/{categoryId}")
|
||||||
|
public ResponseMessage<List<Categoryattribute>> getAttributesByCategory(@PathVariable Integer categoryId) {
|
||||||
|
log.info("接收根据分类ID获取属性列表的请求: 分类ID={}", categoryId);
|
||||||
|
return categoryAttributeService.getAttributesByCategoryId(categoryId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建新的分类属性
|
||||||
|
* @param dto 分类属性数据
|
||||||
|
* @return 创建结果
|
||||||
|
*/
|
||||||
|
@PostMapping
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
|
public ResponseMessage<Categoryattribute> createAttribute(@Valid @RequestBody CategoryAttributeDto dto) {
|
||||||
|
log.info("接收创建分类属性的请求: 分类ID={}, 属性名称={}",
|
||||||
|
dto.getCategoryid(), dto.getAttributename());
|
||||||
|
return categoryAttributeService.saveCategoryAttribute(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新分类属性
|
||||||
|
* @param id 属性ID
|
||||||
|
* @param dto 分类属性数据
|
||||||
|
* @return 更新结果
|
||||||
|
*/
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
|
public ResponseMessage<Categoryattribute> updateAttribute(
|
||||||
|
@PathVariable Integer id,
|
||||||
|
@Valid @RequestBody CategoryAttributeDto dto) {
|
||||||
|
log.info("接收更新分类属性的请求: ID={}, 分类ID={}, 属性名称={}",
|
||||||
|
id, dto.getCategoryid(), dto.getAttributename());
|
||||||
|
return categoryAttributeService.updateCategoryAttribute(id, dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除分类属性
|
||||||
|
* @param id 属性ID
|
||||||
|
* @return 删除结果
|
||||||
|
*/
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
|
public ResponseMessage<Boolean> deleteAttribute(@PathVariable Integer id) {
|
||||||
|
log.info("接收删除分类属性的请求: ID={}", id);
|
||||||
|
return categoryAttributeService.deleteCategoryAttribute(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查分类下是否存在指定名称的属性
|
||||||
|
* @param categoryId 分类ID
|
||||||
|
* @param attributeName 属性名称
|
||||||
|
* @return 是否存在
|
||||||
|
*/
|
||||||
|
@GetMapping("/check-exists")
|
||||||
|
public ResponseMessage<Boolean> checkAttributeExists(
|
||||||
|
@RequestParam Integer categoryId,
|
||||||
|
@RequestParam String attributeName) {
|
||||||
|
log.info("接收检查分类属性是否存在的请求: 分类ID={}, 属性名称={}", categoryId, attributeName);
|
||||||
|
return categoryAttributeService.existsByCategoryAndName(categoryId, attributeName);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package com.qf.myafterprojecy.controller;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
|
||||||
|
import com.qf.myafterprojecy.pojo.Category;
|
||||||
|
import com.qf.myafterprojecy.pojo.dto.CategoryDto;
|
||||||
|
import com.qf.myafterprojecy.service.ICategoryService;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分类控制器类,处理分类相关的HTTP请求
|
||||||
|
* 提供分类的增删改查功能
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/categories")
|
||||||
|
@Validated
|
||||||
|
public class CategoryController {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(CategoryController.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ICategoryService categoryService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID获取分类信息
|
||||||
|
* @param id 分类ID
|
||||||
|
* @return 返回分类信息
|
||||||
|
*/
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public ResponseMessage<Category> getCategoryById(@PathVariable Integer id) {
|
||||||
|
log.info("接收根据ID获取分类的请求: {}", id);
|
||||||
|
return categoryService.getCategoryById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有分类列表
|
||||||
|
* @return 返回分类列表
|
||||||
|
*/
|
||||||
|
@GetMapping
|
||||||
|
public ResponseMessage<List<Category>> getAllCategories() {
|
||||||
|
log.info("接收获取所有分类列表的请求");
|
||||||
|
return categoryService.getAllCategories();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建新分类
|
||||||
|
* @param categoryDto 分类数据传输对象
|
||||||
|
* @return 返回创建结果
|
||||||
|
*/
|
||||||
|
@PostMapping
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
|
public ResponseMessage<Category> createCategory(@Valid @RequestBody CategoryDto categoryDto) {
|
||||||
|
log.info("接收创建分类的请求: {}", categoryDto.getTypename());
|
||||||
|
return categoryService.saveCategory(categoryDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新分类信息
|
||||||
|
* @param id 分类ID
|
||||||
|
* @param categoryDto 分类数据传输对象
|
||||||
|
* @return 返回更新结果
|
||||||
|
*/
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
|
public ResponseMessage<Category> updateCategory(
|
||||||
|
@PathVariable Integer id,
|
||||||
|
@Valid @RequestBody CategoryDto categoryDto) {
|
||||||
|
log.info("接收更新分类的请求: ID={}, 分类名称={}", id, categoryDto.getTypename());
|
||||||
|
return categoryService.updateCategory(id, categoryDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除分类
|
||||||
|
* @param id 分类ID
|
||||||
|
* @return 返回删除结果
|
||||||
|
*/
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
|
public ResponseMessage<Boolean> deleteCategory(@PathVariable Integer id) {
|
||||||
|
log.info("接收删除分类的请求: {}", id);
|
||||||
|
return categoryService.deleteCategory(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据分类名称搜索分类
|
||||||
|
* @param typename 分类名称
|
||||||
|
* @return 返回符合条件的分类列表
|
||||||
|
*/
|
||||||
|
@GetMapping("/search")
|
||||||
|
public ResponseMessage<List<Category>> searchCategoriesByTypename(@RequestParam String typename) {
|
||||||
|
log.info("接收根据名称搜索分类的请求: {}", typename);
|
||||||
|
return categoryService.searchCategoriesByTypename(typename);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package com.qf.myafterprojecy.controller;
|
||||||
|
|
||||||
|
import org.commonmark.node.Node;
|
||||||
|
import org.commonmark.parser.Parser;
|
||||||
|
import org.commonmark.renderer.html.HtmlRenderer;
|
||||||
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
import org.springframework.util.FileCopyUtils;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 帮助控制器类,处理前端调用api/help请求
|
||||||
|
* 提供README_API.md文件的读取和返回功能
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/help")
|
||||||
|
public class HelpController {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取README_API.md文件内容
|
||||||
|
* @return 返回包含README_API.md文件内容的ResponseMessage对象
|
||||||
|
*/
|
||||||
|
@GetMapping
|
||||||
|
public ResponseMessage<String> getReadmeApi() {
|
||||||
|
try {
|
||||||
|
// 获取项目根目录
|
||||||
|
String rootPath = System.getProperty("user.dir") ;
|
||||||
|
// 构建README_API.md文件路径
|
||||||
|
File readmeFile = new File(rootPath, "README_API.md");
|
||||||
|
|
||||||
|
// 检查文件是否存在
|
||||||
|
if (!readmeFile.exists() || !readmeFile.isFile()) {
|
||||||
|
// 如果不存在,尝试使用类路径资源加载
|
||||||
|
try {
|
||||||
|
ClassPathResource resource = new ClassPathResource("README_API.md");
|
||||||
|
String markdownContent = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()), StandardCharsets.UTF_8);
|
||||||
|
// 将Markdown转换为HTML
|
||||||
|
String htmlContent = convertMarkdownToHtml(markdownContent);
|
||||||
|
return ResponseMessage.success(htmlContent, "获取API文档成功");
|
||||||
|
} catch (IOException e) {
|
||||||
|
return ResponseMessage.error("未找到README_API.md文件");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取文件内容
|
||||||
|
String markdownContent = new String(FileCopyUtils.copyToByteArray(new FileInputStream(readmeFile)), StandardCharsets.UTF_8);
|
||||||
|
// 将Markdown转换为HTML
|
||||||
|
String htmlContent = convertMarkdownToHtml(markdownContent);
|
||||||
|
return ResponseMessage.success(htmlContent, "获取API文档成功");
|
||||||
|
} catch (IOException e) {
|
||||||
|
return ResponseMessage.error("读取README_API.md文件失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将Markdown文本转换为HTML
|
||||||
|
* @param markdown 原始Markdown文本
|
||||||
|
* @return 转换后的HTML字符串
|
||||||
|
*/
|
||||||
|
private String convertMarkdownToHtml(String markdown) {
|
||||||
|
Parser parser = Parser.builder().build();
|
||||||
|
Node document = parser.parse(markdown);
|
||||||
|
HtmlRenderer renderer = HtmlRenderer.builder().build();
|
||||||
|
return renderer.render(document);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
package com.qf.myafterprojecy.controller;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
|
||||||
|
import com.qf.myafterprojecy.pojo.Message;
|
||||||
|
import com.qf.myafterprojecy.pojo.dto.MessageDto;
|
||||||
|
import com.qf.myafterprojecy.pojo.dto.MessagePageDto;
|
||||||
|
import com.qf.myafterprojecy.service.IMessageService;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/messages")
|
||||||
|
public class MessageController {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(MessageController.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IMessageService messageService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有消息
|
||||||
|
*/
|
||||||
|
@GetMapping
|
||||||
|
public ResponseMessage<Iterable<Message>> getAllMessages() {
|
||||||
|
logger.info("接收获取所有消息的请求");
|
||||||
|
return messageService.getAllMessages();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 分页查询消息
|
||||||
|
*/
|
||||||
|
@GetMapping("/page")
|
||||||
|
public ResponseMessage<List<Message>> getMessagesByPage(MessagePageDto messagePageDto) {
|
||||||
|
logger.info("接收分页查询消息的请求: {}", messagePageDto);
|
||||||
|
return messageService.getMessagesByPage(messagePageDto);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取消息数量
|
||||||
|
* @param articleId 文章ID (可选)
|
||||||
|
* @return 消息数量
|
||||||
|
* 文章ID为null时返回所有消息数量
|
||||||
|
*/
|
||||||
|
@GetMapping("/count")
|
||||||
|
public ResponseMessage<Integer> getMessageCount( Integer articleId) {
|
||||||
|
logger.info("接收获取消息数量的请求: {}", articleId);
|
||||||
|
return messageService.getMessageCountByArticleId(articleId);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 根据ID获取消息
|
||||||
|
*/
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public ResponseMessage<Message> getMessage(@PathVariable Integer id) {
|
||||||
|
logger.info("接收根据ID获取消息的请求: {}", id);
|
||||||
|
return messageService.getMessageById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据文章ID获取消息列表
|
||||||
|
*/
|
||||||
|
@GetMapping("/article/{articleId}")
|
||||||
|
public ResponseMessage<List<Message>> getMessagesByArticleId(@PathVariable Integer articleId) {
|
||||||
|
logger.info("接收根据文章ID获取消息的请求: {}", articleId);
|
||||||
|
return messageService.getMessagesByArticleId(articleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有根消息(非回复的消息)
|
||||||
|
*/
|
||||||
|
@GetMapping("/root")
|
||||||
|
public ResponseMessage<List<Message>> getRootMessages() {
|
||||||
|
logger.info("接收获取所有根消息的请求");
|
||||||
|
return messageService.getRootMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据父消息ID获取回复列表
|
||||||
|
*/
|
||||||
|
@GetMapping("/{parentId}/replies")
|
||||||
|
public ResponseMessage<List<Message>> getRepliesByParentId(@PathVariable Integer parentId) {
|
||||||
|
logger.info("接收根据父消息ID获取回复的请求: {}", parentId);
|
||||||
|
return messageService.getRepliesByParentId(parentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据昵称搜索消息
|
||||||
|
*/
|
||||||
|
@GetMapping("/search")
|
||||||
|
public ResponseMessage<List<Message>> searchMessagesByNickname(@RequestParam String nickname) {
|
||||||
|
logger.info("接收根据昵称搜索消息的请求: {}", nickname);
|
||||||
|
return messageService.searchMessagesByNickname(nickname);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建新消息
|
||||||
|
*/
|
||||||
|
@PostMapping
|
||||||
|
// 允许匿名用户发表评论
|
||||||
|
public ResponseMessage<Message> createMessage(@RequestBody MessageDto message) {
|
||||||
|
logger.info("接收创建消息的请求: {}", message != null ? message.getNickname() : "null");
|
||||||
|
return messageService.saveMessage(message);
|
||||||
|
}
|
||||||
|
// 点赞数增加
|
||||||
|
@PostMapping("/{id}/like")
|
||||||
|
public ResponseMessage<Message> likeMessage(@PathVariable Integer id) {
|
||||||
|
logger.info("接收点赞消息的请求: {}", id);
|
||||||
|
return messageService.likeMessage(id);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 根据ID删除消息
|
||||||
|
*/
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
@PreAuthorize("hasRole('ADMIN') or #id == authentication.principal.id")
|
||||||
|
public ResponseMessage<Message> deleteMessage(@PathVariable Integer id) {
|
||||||
|
logger.info("接收删除消息的请求: {}", id);
|
||||||
|
return messageService.deleteMessage(id);
|
||||||
|
}
|
||||||
|
//删除所有评论 - 仅管理员可操作
|
||||||
|
@DeleteMapping("/all")
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
|
public ResponseMessage<Void> deleteAllMessages() {
|
||||||
|
logger.info("接收删除所有消息的请求");
|
||||||
|
return messageService.deleteAllMessages();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
package com.qf.myafterprojecy.controller;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
|
||||||
|
import com.qf.myafterprojecy.pojo.Nonsense;
|
||||||
|
import com.qf.myafterprojecy.pojo.dto.NonsenseDto;
|
||||||
|
import com.qf.myafterprojecy.service.INonsenseService;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import javax.validation.constraints.Max;
|
||||||
|
import javax.validation.constraints.Min;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/nonsense")
|
||||||
|
@Validated
|
||||||
|
public class NonsenseController {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(NonsenseController.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private INonsenseService nonsenseService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有疯言疯语内容
|
||||||
|
* @return 疯言疯语内容列表
|
||||||
|
*/
|
||||||
|
@GetMapping
|
||||||
|
public ResponseMessage<List<Nonsense>> getAllNonsense() {
|
||||||
|
logger.info("请求获取所有疯言疯语内容");
|
||||||
|
return nonsenseService.getAllNonsense();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据状态获取疯言疯语内容
|
||||||
|
* @param status 状态:0未发表 1已发表 2已删除
|
||||||
|
* @return 疯言疯语内容列表
|
||||||
|
*/
|
||||||
|
@GetMapping("/status/{status}")
|
||||||
|
public ResponseMessage<List<Nonsense>> getNonsenseByStatus(
|
||||||
|
@PathVariable("status") Integer status) {
|
||||||
|
logger.info("请求获取状态为{}的疯言疯语内容", status);
|
||||||
|
return nonsenseService.getNonsenseByStatus(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID获取疯言疯语内容
|
||||||
|
* @param id 疯言疯语内容ID
|
||||||
|
* @return 疯言疯语内容
|
||||||
|
*/
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public ResponseMessage<Nonsense> getNonsenseById(@PathVariable("id") Integer id) {
|
||||||
|
logger.info("请求获取ID为{}的疯言疯语内容", id);
|
||||||
|
return nonsenseService.getNonsenseById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建疯言疯语内容
|
||||||
|
* 需要管理员权限
|
||||||
|
* @param nonsenseDto 疯言疯语内容数据
|
||||||
|
* @return 创建结果
|
||||||
|
*/
|
||||||
|
@PostMapping
|
||||||
|
public ResponseMessage<Nonsense> saveNonsense(@Valid @RequestBody NonsenseDto nonsenseDto) {
|
||||||
|
logger.info("请求保存疯言疯语内容");
|
||||||
|
return nonsenseService.saveNonsense(nonsenseDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新疯言疯语内容
|
||||||
|
* 需要管理员权限
|
||||||
|
* @param id 疯言疯语内容ID
|
||||||
|
* @param nonsenseDto 疯言疯语内容数据
|
||||||
|
* @return 更新结果
|
||||||
|
*/
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
|
public ResponseMessage<Nonsense> updateNonsense(@PathVariable("id") Integer id, @Valid @RequestBody NonsenseDto nonsenseDto) {
|
||||||
|
logger.info("请求更新ID为{}的疯言疯语内容", id);
|
||||||
|
return nonsenseService.updateNonsense(id, nonsenseDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除疯言疯语内容
|
||||||
|
* 需要管理员权限
|
||||||
|
* @param id 疯言疯语内容ID
|
||||||
|
* @return 删除结果
|
||||||
|
*/
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
|
public ResponseMessage<Boolean> deleteNonsense(@PathVariable("id") Integer id) {
|
||||||
|
logger.info("请求删除ID为{}的疯言疯语内容", id);
|
||||||
|
return nonsenseService.deleteNonsense(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新疯言疯语内容状态
|
||||||
|
* 需要管理员权限
|
||||||
|
* @param id 疯言疯语内容ID
|
||||||
|
* @param status 新状态:0未发表 1已发表 2已删除
|
||||||
|
* @return 更新结果
|
||||||
|
*/
|
||||||
|
@PutMapping("/{id}/status/{status}")
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
|
public ResponseMessage<Nonsense> updateNonsenseStatus(
|
||||||
|
@PathVariable("id") Integer id,
|
||||||
|
@PathVariable("status") @Min(0) @Max(2) Integer status) {
|
||||||
|
logger.info("请求更新ID为{}的疯言疯语内容状态为{}", id, status);
|
||||||
|
return nonsenseService.updateNonsenseStatus(id, status);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
package com.qf.myafterprojecy.controller;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
|
||||||
|
import com.qf.myafterprojecy.pojo.Users;
|
||||||
|
import com.qf.myafterprojecy.pojo.dto.UserDto;
|
||||||
|
import com.qf.myafterprojecy.service.IUserService;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import javax.validation.Valid;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/users")
|
||||||
|
@Validated
|
||||||
|
public class UserController {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private IUserService userService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID获取用户信息
|
||||||
|
* @param id 用户ID
|
||||||
|
* @return 用户信息
|
||||||
|
*/
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public ResponseMessage<Users> getUserById(@PathVariable Long id) {
|
||||||
|
logger.info("获取用户信息,用户ID: {}", id);
|
||||||
|
return userService.getUserById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有用户列表
|
||||||
|
* @return 用户列表
|
||||||
|
*/
|
||||||
|
@GetMapping
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
|
public ResponseMessage<List<Users>> getAllUsers() {
|
||||||
|
logger.info("获取所有用户列表");
|
||||||
|
return userService.getAllUsers();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户名获取用户信息
|
||||||
|
* @return 用户信息
|
||||||
|
*/
|
||||||
|
@GetMapping("/username/{username}")
|
||||||
|
@PreAuthorize("hasRole('ADMIN') or #username == authentication.name")
|
||||||
|
public ResponseMessage<Users> getUserByUsername(@PathVariable String username) {
|
||||||
|
logger.info("根据用户名获取用户信息,用户名: {}", username);
|
||||||
|
return userService.getUserByUsername(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建新用户
|
||||||
|
* @param userDto 用户数据
|
||||||
|
* @return 创建结果
|
||||||
|
*/
|
||||||
|
@PostMapping
|
||||||
|
public ResponseMessage<Users> saveUser(@Valid @RequestBody UserDto userDto) {
|
||||||
|
logger.info("创建新用户,用户名: {}", userDto.getUsername());
|
||||||
|
return userService.saveUser(userDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新用户信息
|
||||||
|
* @param id 用户ID
|
||||||
|
* @param userDto 用户数据
|
||||||
|
* @return 更新结果
|
||||||
|
*/
|
||||||
|
@PutMapping("/{id}")
|
||||||
|
@PreAuthorize("hasRole('ADMIN') or #id == authentication.principal.id")
|
||||||
|
public ResponseMessage<Users> updateUser(@PathVariable Long id, @Valid @RequestBody UserDto userDto) {
|
||||||
|
logger.info("更新用户信息,用户ID: {}", id);
|
||||||
|
return userService.updateUser(id, userDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除用户
|
||||||
|
* @param id 用户ID
|
||||||
|
* @return 删除结果
|
||||||
|
*/
|
||||||
|
@DeleteMapping("/{id}")
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
|
public ResponseMessage<Boolean> deleteUser(@PathVariable Long id) {
|
||||||
|
logger.info("删除用户,用户ID: {}", id);
|
||||||
|
return userService.deleteUser(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据角色查询用户列表
|
||||||
|
* @param role 角色
|
||||||
|
* @return 用户列表
|
||||||
|
*/
|
||||||
|
@GetMapping("/role/{role}")
|
||||||
|
@PreAuthorize("hasRole('ADMIN')")
|
||||||
|
public ResponseMessage<List<Users>> getUsersByRole(@PathVariable int role) {
|
||||||
|
logger.info("根据角色查询用户列表,角色: {}", role);
|
||||||
|
return userService.getUsersByRole(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查用户名是否存在
|
||||||
|
* @param username 用户名
|
||||||
|
* @return 是否存在
|
||||||
|
*/
|
||||||
|
@GetMapping("/check/username/{username}")
|
||||||
|
public ResponseMessage<Boolean> existsByUsername(@PathVariable String username) {
|
||||||
|
logger.info("检查用户名是否存在,用户名: {}", username);
|
||||||
|
return userService.existsByUsername(username);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查邮箱是否存在
|
||||||
|
* @param email 邮箱
|
||||||
|
* @return 是否存在
|
||||||
|
*/
|
||||||
|
@GetMapping("/check/email/{email}")
|
||||||
|
public ResponseMessage<Boolean> existsByEmail(@PathVariable String email) {
|
||||||
|
logger.info("检查邮箱是否存在,邮箱: {}", email);
|
||||||
|
return userService.existsByEmail(email);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查手机号是否存在
|
||||||
|
* @param phone 手机号
|
||||||
|
* @return 是否存在
|
||||||
|
*/
|
||||||
|
@GetMapping("/check/phone/{phone}")
|
||||||
|
public ResponseMessage<Boolean> existsByPhone(@PathVariable String phone) {
|
||||||
|
logger.info("检查手机号是否存在,手机号: {}", phone);
|
||||||
|
return userService.existsByPhone(phone);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package com.qf.myafterprojecy.exceptopn;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||||
|
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||||
|
|
||||||
|
@RestControllerAdvice
|
||||||
|
public class GlobalExceptionHandler {
|
||||||
|
Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
|
||||||
|
@ExceptionHandler(value=Exception.class)
|
||||||
|
public ResponseMessage<String> handleException(Exception e, HttpServletRequest request) {
|
||||||
|
logger.error("请求路径:{},异常消息:{}",request.getRequestURI(),e.getMessage());
|
||||||
|
return new ResponseMessage<>(500,"服务器异常",e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,257 @@
|
|||||||
|
package com.qf.myafterprojecy.exceptopn;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用响应消息类,用于封装接口返回的数据结构
|
||||||
|
* 遵循RESTful API设计规范,提供统一的响应格式
|
||||||
|
* @param <T> 数据类型,可以是任意Java对象
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ResponseMessage<T> {
|
||||||
|
// 状态码,通常用于表示请求的处理结果
|
||||||
|
private Integer code;
|
||||||
|
// 响应消息,用于描述请求的处理结果信息
|
||||||
|
private String message;
|
||||||
|
// 请求是否成功的标志,根据状态码自动设置
|
||||||
|
private boolean success;
|
||||||
|
// 响应数据,泛型类型,支持不同类型的数据
|
||||||
|
private T data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造方法,用于创建响应消息对象
|
||||||
|
* 自动根据状态码设置success字段
|
||||||
|
* @param code 状态码
|
||||||
|
* @param message 响应消息
|
||||||
|
* @param data 响应数据
|
||||||
|
*/
|
||||||
|
public ResponseMessage(Integer code, String message, T data) {
|
||||||
|
this.code = code;
|
||||||
|
this.message = message;
|
||||||
|
this.data = data;
|
||||||
|
// 自动根据状态码判断是否成功
|
||||||
|
this.success = code >= 200 && code < 300;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完整参数的构造方法
|
||||||
|
* @param code 状态码
|
||||||
|
* @param message 响应消息
|
||||||
|
* @param data 响应数据
|
||||||
|
* @param success 是否成功
|
||||||
|
*/
|
||||||
|
public ResponseMessage(Integer code, String message, T data, boolean success) {
|
||||||
|
this.code = code;
|
||||||
|
this.message = message;
|
||||||
|
this.data = data;
|
||||||
|
this.success = success;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------- 成功响应方法 -----------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建成功响应,默认消息为"操作成功"
|
||||||
|
* @param data 响应数据
|
||||||
|
* @param <T> 数据类型
|
||||||
|
* @return 成功响应对象
|
||||||
|
*/
|
||||||
|
public static <T> ResponseMessage<T> success(T data) {
|
||||||
|
return new ResponseMessage<>(HttpStatus.OK.value(), "操作成功", data, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建成功响应,自定义消息
|
||||||
|
* @param data 响应数据
|
||||||
|
* @param message 响应消息
|
||||||
|
* @param <T> 数据类型
|
||||||
|
* @return 成功响应对象
|
||||||
|
*/
|
||||||
|
public static <T> ResponseMessage<T> success(T data, String message) {
|
||||||
|
return new ResponseMessage<>(HttpStatus.OK.value(), message, data, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建成功响应,自定义状态码
|
||||||
|
* @param code 状态码
|
||||||
|
* @param message 响应消息
|
||||||
|
* @param data 响应数据
|
||||||
|
* @param <T> 数据类型
|
||||||
|
* @return 成功响应对象
|
||||||
|
*/
|
||||||
|
public static <T> ResponseMessage<T> successWithCode(Integer code, String message, T data) {
|
||||||
|
return new ResponseMessage<>(code, message, data, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建空数据的成功响应
|
||||||
|
* @param message 响应消息
|
||||||
|
* @param <T> 数据类型
|
||||||
|
* @return 成功响应对象
|
||||||
|
*/
|
||||||
|
public static <T> ResponseMessage<T> successEmpty(String message) {
|
||||||
|
return new ResponseMessage<>(HttpStatus.OK.value(), message, null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------- 错误响应方法 -----------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建错误响应,默认状态码500
|
||||||
|
* @param message 错误消息
|
||||||
|
* @param <T> 数据类型
|
||||||
|
* @return 错误响应对象
|
||||||
|
*/
|
||||||
|
public static <T> ResponseMessage<T> error(String message) {
|
||||||
|
return new ResponseMessage<>(HttpStatus.INTERNAL_SERVER_ERROR.value(), message, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建错误响应,自定义状态码
|
||||||
|
* @param code 状态码
|
||||||
|
* @param message 错误消息
|
||||||
|
* @param <T> 数据类型
|
||||||
|
* @return 错误响应对象
|
||||||
|
*/
|
||||||
|
public static <T> ResponseMessage<T> error(Integer code, String message) {
|
||||||
|
return new ResponseMessage<>(code, message, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建错误响应,包含错误数据
|
||||||
|
* @param code 状态码
|
||||||
|
* @param message 错误消息
|
||||||
|
* @param data 错误数据
|
||||||
|
* @param <T> 数据类型
|
||||||
|
* @return 错误响应对象
|
||||||
|
*/
|
||||||
|
public static <T> ResponseMessage<T> errorWithData(Integer code, String message, T data) {
|
||||||
|
return new ResponseMessage<>(code, message, data, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建参数错误响应,状态码400
|
||||||
|
* @param message 错误消息
|
||||||
|
* @param <T> 数据类型
|
||||||
|
* @return 错误响应对象
|
||||||
|
*/
|
||||||
|
public static <T> ResponseMessage<T> badRequest(String message) {
|
||||||
|
return new ResponseMessage<>(HttpStatus.BAD_REQUEST.value(), message, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建未找到资源响应,状态码404
|
||||||
|
* @param message 错误消息
|
||||||
|
* @param <T> 数据类型
|
||||||
|
* @return 错误响应对象
|
||||||
|
*/
|
||||||
|
public static <T> ResponseMessage<T> notFound(String message) {
|
||||||
|
return new ResponseMessage<>(HttpStatus.NOT_FOUND.value(), message, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建权限错误响应,状态码403
|
||||||
|
* @param message 错误消息
|
||||||
|
* @param <T> 数据类型
|
||||||
|
* @return 错误响应对象
|
||||||
|
*/
|
||||||
|
public static <T> ResponseMessage<T> forbidden(String message) {
|
||||||
|
return new ResponseMessage<>(HttpStatus.FORBIDDEN.value(), message, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------- 业务操作响应方法 -----------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建保存操作响应
|
||||||
|
* @param success 是否成功
|
||||||
|
* @param data 响应数据
|
||||||
|
* @param <T> 数据类型
|
||||||
|
* @return 操作响应对象
|
||||||
|
*/
|
||||||
|
public static <T> ResponseMessage<T> save(boolean success, T data) {
|
||||||
|
return success ?
|
||||||
|
new ResponseMessage<>(HttpStatus.OK.value(), "保存成功", data, true) :
|
||||||
|
new ResponseMessage<>(HttpStatus.INTERNAL_SERVER_ERROR.value(), "保存失败", null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建更新操作响应
|
||||||
|
* @param success 是否成功
|
||||||
|
* @param data 响应数据
|
||||||
|
* @param <T> 数据类型
|
||||||
|
* @return 操作响应对象
|
||||||
|
*/
|
||||||
|
public static <T> ResponseMessage<T> update(boolean success, T data) {
|
||||||
|
return success ?
|
||||||
|
new ResponseMessage<>(HttpStatus.OK.value(), "更新成功", data, true) :
|
||||||
|
new ResponseMessage<>(HttpStatus.INTERNAL_SERVER_ERROR.value(), "更新失败", null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建删除操作响应
|
||||||
|
* @param success 是否成功
|
||||||
|
* @param <T> 数据类型
|
||||||
|
* @return 操作响应对象
|
||||||
|
*/
|
||||||
|
public static <T> ResponseMessage<T> delete(boolean success) {
|
||||||
|
return success ?
|
||||||
|
new ResponseMessage<>(HttpStatus.OK.value(), "删除成功", null, true) :
|
||||||
|
new ResponseMessage<>(HttpStatus.INTERNAL_SERVER_ERROR.value(), "删除失败", null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建批量删除操作响应
|
||||||
|
* @param success 是否成功
|
||||||
|
* @param deletedCount 删除的数量
|
||||||
|
* @param <T> 数据类型
|
||||||
|
* @return 操作响应对象
|
||||||
|
*/
|
||||||
|
public static <T> ResponseMessage<T> batchDelete(boolean success, int deletedCount) {
|
||||||
|
return success ?
|
||||||
|
new ResponseMessage<>(HttpStatus.OK.value(), "成功删除" + deletedCount + "条数据", null, true) :
|
||||||
|
new ResponseMessage<>(HttpStatus.INTERNAL_SERVER_ERROR.value(), "批量删除失败", null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建分页查询响应
|
||||||
|
* @param data 分页数据
|
||||||
|
* @param message 响应消息
|
||||||
|
* @param <T> 数据类型
|
||||||
|
* @return 分页响应对象
|
||||||
|
*/
|
||||||
|
public static <T> ResponseMessage<T> page(T data, String message) {
|
||||||
|
return new ResponseMessage<>(HttpStatus.OK.value(), message, data, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCode(Integer code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSuccess() {
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSuccess(boolean success) {
|
||||||
|
this.success = success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setData(T data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
65
src/main/java/com/qf/myafterprojecy/init/UserDataInit.java
Normal file
65
src/main/java/com/qf/myafterprojecy/init/UserDataInit.java
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package com.qf.myafterprojecy.init;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.pojo.Users;
|
||||||
|
import com.qf.myafterprojecy.repository.UsersRepository;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.ApplicationArguments;
|
||||||
|
import org.springframework.boot.ApplicationRunner;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户数据初始化类
|
||||||
|
* 在应用启动时检查并创建管理员账号
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class UserDataInit implements ApplicationRunner {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(UserDataInit.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UsersRepository usersRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
// 管理员账号信息
|
||||||
|
private static final String ADMIN_USERNAME = "qf1121";
|
||||||
|
private static final String ADMIN_PASSWORD = "qf1121";
|
||||||
|
private static final String ADMIN_EMAIL = "admin@qf1121.com";
|
||||||
|
private static final String ADMIN_PHONE = "13800138000";
|
||||||
|
private static final Integer ADMIN_ROLE = 1; // 1表示管理员角色
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(ApplicationArguments args) {
|
||||||
|
logger.info("开始检查管理员账号...");
|
||||||
|
|
||||||
|
// 检查管理员账号是否已存在
|
||||||
|
Optional<Users> adminUser = usersRepository.findByUsername(ADMIN_USERNAME);
|
||||||
|
|
||||||
|
if (adminUser.isPresent()) {
|
||||||
|
logger.info("管理员账号 {} 已存在,无需创建", ADMIN_USERNAME);
|
||||||
|
} else {
|
||||||
|
// 创建管理员账号
|
||||||
|
Users newAdmin = new Users();
|
||||||
|
newAdmin.setUsername(ADMIN_USERNAME);
|
||||||
|
// 加密密码
|
||||||
|
newAdmin.setPassword(passwordEncoder.encode(ADMIN_PASSWORD));
|
||||||
|
newAdmin.setEmail(ADMIN_EMAIL);
|
||||||
|
newAdmin.setPhone(ADMIN_PHONE);
|
||||||
|
newAdmin.setRole(ADMIN_ROLE);
|
||||||
|
newAdmin.setCreateTime(LocalDateTime.now());
|
||||||
|
try {
|
||||||
|
usersRepository.save(newAdmin);
|
||||||
|
logger.info("管理员账号 {} 创建成功", ADMIN_USERNAME);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("创建管理员账号失败: {}", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
139
src/main/java/com/qf/myafterprojecy/pojo/Article.java
Normal file
139
src/main/java/com/qf/myafterprojecy/pojo/Article.java
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
package com.qf.myafterprojecy.pojo;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "article")
|
||||||
|
public class Article {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(name = "articleid")
|
||||||
|
private Integer articleid;
|
||||||
|
|
||||||
|
@NotBlank(message = "标题不能为空")
|
||||||
|
@Column(name = "title")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
@Column(name = "content", columnDefinition = "TEXT")
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@NotNull(message = "类别id不能为空")
|
||||||
|
@Column(name = "attribute_id")
|
||||||
|
private Integer attributeid;
|
||||||
|
|
||||||
|
@Column(name = "img")
|
||||||
|
private String img;
|
||||||
|
|
||||||
|
@Column(name = "created_at")
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
@Column(name = "updated_at")
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
|
@Column(name = "view_count")
|
||||||
|
private Integer viewCount;
|
||||||
|
|
||||||
|
@Column(name = "likes")
|
||||||
|
private Integer likes; // 点赞数
|
||||||
|
|
||||||
|
@Column(name = "status")
|
||||||
|
private Integer status; // 0-草稿,1-已发布,2-已删除
|
||||||
|
|
||||||
|
@Column(name = "markdownscontent")
|
||||||
|
private String markdownscontent;
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
|
||||||
|
public Integer getLikes() {
|
||||||
|
return likes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLikes(Integer likes) {
|
||||||
|
this.likes = likes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public Integer getAttributeid() {
|
||||||
|
return attributeid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttributeid(Integer attributeid) {
|
||||||
|
this.attributeid = attributeid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getArticleid() {
|
||||||
|
return articleid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setArticleid(Integer articleid) {
|
||||||
|
this.articleid = articleid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContent(String content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getCreatedAt() {
|
||||||
|
return createdAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreatedAt(LocalDateTime createdAt) {
|
||||||
|
this.createdAt = createdAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getUpdatedAt() {
|
||||||
|
return updatedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||||
|
this.updatedAt = updatedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(Integer status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImg() {
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImg(String img) {
|
||||||
|
this.img = img;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getViewCount() {
|
||||||
|
return viewCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setViewCount(Integer viewCount) {
|
||||||
|
this.viewCount = viewCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMarkdownscontent() {
|
||||||
|
return markdownscontent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMarkdownscontent(String markdownscontent) {
|
||||||
|
this.markdownscontent = markdownscontent;
|
||||||
|
}
|
||||||
|
}
|
||||||
68
src/main/java/com/qf/myafterprojecy/pojo/Category.java
Normal file
68
src/main/java/com/qf/myafterprojecy/pojo/Category.java
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package com.qf.myafterprojecy.pojo;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "category")
|
||||||
|
public class Category {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(name = "categoryid")
|
||||||
|
private Integer categoryid;
|
||||||
|
|
||||||
|
@NotBlank(message = "分类名称不能为空")
|
||||||
|
@Column(name = "typename")
|
||||||
|
private String typename;
|
||||||
|
|
||||||
|
@Column(name = "description")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Column(name = "created_at")
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
@Column(name = "updated_at")
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public Integer getCategoryid() {
|
||||||
|
return categoryid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCategoryid(Integer categoryid) {
|
||||||
|
this.categoryid = categoryid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTypename() {
|
||||||
|
return typename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTypename(String typename) {
|
||||||
|
this.typename = typename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getCreatedAt() {
|
||||||
|
return createdAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreatedAt(LocalDateTime createdAt) {
|
||||||
|
this.createdAt = createdAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getUpdatedAt() {
|
||||||
|
return updatedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||||
|
this.updatedAt = updatedAt;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package com.qf.myafterprojecy.pojo;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "category_attribute")
|
||||||
|
public class Categoryattribute {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(name = "attributeid")
|
||||||
|
private Integer attributeid;
|
||||||
|
|
||||||
|
@Column(name = "categoryid")
|
||||||
|
private Integer categoryid;
|
||||||
|
|
||||||
|
@Column(name = "attributename")
|
||||||
|
private String attributename;
|
||||||
|
|
||||||
|
public Integer getAttributeid() {
|
||||||
|
return attributeid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttributeid(Integer attributeid) {
|
||||||
|
this.attributeid = attributeid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getCategoryid() {
|
||||||
|
return categoryid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCategoryid(Integer categoryid) {
|
||||||
|
this.categoryid = categoryid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAttributename() {
|
||||||
|
return attributename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttributename(String attributename) {
|
||||||
|
this.attributename = attributename;
|
||||||
|
}
|
||||||
|
}
|
||||||
122
src/main/java/com/qf/myafterprojecy/pojo/Message.java
Normal file
122
src/main/java/com/qf/myafterprojecy/pojo/Message.java
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
package com.qf.myafterprojecy.pojo;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "message")
|
||||||
|
public class Message {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(name = "messageid")
|
||||||
|
private Integer messageid;
|
||||||
|
|
||||||
|
@Column(name = "nickname")
|
||||||
|
private String nickname;
|
||||||
|
|
||||||
|
@Column(name = "email")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@Column(name = "content", columnDefinition = "text")
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@Column(name = "messageimg")
|
||||||
|
private String messageimg;
|
||||||
|
|
||||||
|
@Temporal(TemporalType.TIMESTAMP)
|
||||||
|
@Column(name = "created_at")
|
||||||
|
private Date createdAt;
|
||||||
|
|
||||||
|
@Column(name = "parentid")
|
||||||
|
private Integer parentid;
|
||||||
|
|
||||||
|
@Column(name = "replyid")
|
||||||
|
private Integer replyid;
|
||||||
|
|
||||||
|
@Column(name = "articleid")
|
||||||
|
private Integer articleid;
|
||||||
|
|
||||||
|
@Column(name = "likes")
|
||||||
|
private Integer likes; // 点赞数
|
||||||
|
|
||||||
|
|
||||||
|
public Integer getLikes() {
|
||||||
|
return likes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLikes(Integer likes) {
|
||||||
|
this.likes = likes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getReplyid() {
|
||||||
|
return replyid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReplyid(Integer replyid) {
|
||||||
|
this.replyid = replyid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getMessageid() {
|
||||||
|
return messageid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessageid(Integer messageid) {
|
||||||
|
this.messageid = messageid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNickname() {
|
||||||
|
return nickname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNickname(String nickname) {
|
||||||
|
this.nickname = nickname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContent(String content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getCreatedAt() {
|
||||||
|
return createdAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreatedAt(Date createdAt) {
|
||||||
|
this.createdAt = createdAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getParentid() {
|
||||||
|
return parentid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParentid(Integer parentid) {
|
||||||
|
this.parentid = parentid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getArticleid() {
|
||||||
|
return articleid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setArticleid(Integer articleid) {
|
||||||
|
this.articleid = articleid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessageimg() {
|
||||||
|
return messageimg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessageimg(String messageimg) {
|
||||||
|
this.messageimg = messageimg;
|
||||||
|
}
|
||||||
|
}
|
||||||
46
src/main/java/com/qf/myafterprojecy/pojo/Nonsense.java
Normal file
46
src/main/java/com/qf/myafterprojecy/pojo/Nonsense.java
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package com.qf.myafterprojecy.pojo;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "nonsense")
|
||||||
|
public class Nonsense {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(name = "id", nullable = false, unique = true)
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
@Column(name = "content",nullable = false)
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@Column(name = "status",nullable = false)
|
||||||
|
private Integer status;//状态 0:未发表 1:已发表 2:已删除
|
||||||
|
|
||||||
|
@Column(name = "time")
|
||||||
|
private Date time;
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
public String getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
public void setContent(String content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
public Date getTime() {
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
public void setTime(Date time) {
|
||||||
|
this.time = time;
|
||||||
|
}
|
||||||
|
public Integer getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
public void setStatus(Integer status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
89
src/main/java/com/qf/myafterprojecy/pojo/Users.java
Normal file
89
src/main/java/com/qf/myafterprojecy/pojo/Users.java
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package com.qf.myafterprojecy.pojo;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
@Table(name = "users")
|
||||||
|
public class Users {
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
@Column(name = "id", nullable = false, unique = true)
|
||||||
|
private Long id;
|
||||||
|
@NotBlank(message = "用户名不能为空")
|
||||||
|
@Column(name = "username", nullable = false, unique = true)
|
||||||
|
private String username;
|
||||||
|
@NotBlank(message = "密码不能为空")
|
||||||
|
@Column(name = "password", nullable = false)
|
||||||
|
private String password;
|
||||||
|
@NotBlank(message = "邮箱不能为空")
|
||||||
|
@Column(name = "email", nullable = false, unique = true)
|
||||||
|
private String email;
|
||||||
|
@NotBlank(message = "手机号不能为空")
|
||||||
|
@Column(name = "phone", nullable = false, unique = true)
|
||||||
|
private String phone;
|
||||||
|
@Column(name = "role", nullable = false)
|
||||||
|
private int role;
|
||||||
|
@Column(name = "create_time", nullable = false)
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPhone() {
|
||||||
|
return phone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPhone(String phone) {
|
||||||
|
this.phone = phone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRole() {
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRole(int role) {
|
||||||
|
this.role = role;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getCreateTime() {
|
||||||
|
return createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreateTime(LocalDateTime createTime) {
|
||||||
|
this.createTime = createTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package com.qf.myafterprojecy.pojo.dto;
|
||||||
|
public class ArriclePageDto {
|
||||||
|
|
||||||
|
private Integer status;
|
||||||
|
private String title;
|
||||||
|
private Integer attributeid;
|
||||||
|
private Integer categoryid;
|
||||||
|
private Integer pagenum;
|
||||||
|
private Integer pagesize;
|
||||||
|
public Integer getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
public void setStatus(Integer status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
public Integer getPagenum() {
|
||||||
|
return pagenum;
|
||||||
|
}
|
||||||
|
public void setPagenum(Integer pagenum) {
|
||||||
|
this.pagenum = pagenum;
|
||||||
|
}
|
||||||
|
public Integer getPagesize() {
|
||||||
|
return pagesize;
|
||||||
|
}
|
||||||
|
public void setPagesize(Integer pagesize) {
|
||||||
|
this.pagesize = pagesize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
public Integer getAttributeid() {
|
||||||
|
return attributeid;
|
||||||
|
}
|
||||||
|
public void setAttributeid(Integer attributeid) {
|
||||||
|
this.attributeid = attributeid;
|
||||||
|
}
|
||||||
|
public Integer getCategoryid() {
|
||||||
|
return categoryid;
|
||||||
|
}
|
||||||
|
public void setCategoryid(Integer categoryid) {
|
||||||
|
this.categoryid = categoryid;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ArPageDto{" +
|
||||||
|
"status=" + status +
|
||||||
|
", pagenum=" + pagenum +
|
||||||
|
", pagesize=" + pagesize +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
99
src/main/java/com/qf/myafterprojecy/pojo/dto/ArticleDto.java
Normal file
99
src/main/java/com/qf/myafterprojecy/pojo/dto/ArticleDto.java
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
package com.qf.myafterprojecy.pojo.dto;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
public class ArticleDto {
|
||||||
|
private Integer articleid;
|
||||||
|
|
||||||
|
@NotBlank(message = "标题不能为空")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
private String content;// 如果为空说明是长篇文章 不为空则是短篇说说
|
||||||
|
|
||||||
|
@NotNull(message = "属性ID不能为空")
|
||||||
|
private Integer attributeid;
|
||||||
|
|
||||||
|
private String img;
|
||||||
|
|
||||||
|
private Integer viewCount;
|
||||||
|
|
||||||
|
private Integer likes;
|
||||||
|
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
private String markdownscontent; // 文章内容的Markdown格式
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public Integer getArticleid() {
|
||||||
|
return articleid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setArticleid(Integer articleid) {
|
||||||
|
this.articleid = articleid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTitle(String title) {
|
||||||
|
this.title = title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContent(String content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getAttributeid() {
|
||||||
|
return attributeid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttributeid(Integer attributeid) {
|
||||||
|
this.attributeid = attributeid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getImg() {
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImg(String img) {
|
||||||
|
this.img = img;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getViewCount() {
|
||||||
|
return viewCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setViewCount(Integer viewCount) {
|
||||||
|
this.viewCount = viewCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getLikes() {
|
||||||
|
return likes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLikes(Integer likes) {
|
||||||
|
this.likes = likes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(Integer status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMarkdownscontent() {
|
||||||
|
return markdownscontent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMarkdownscontent(String markdownscontent) {
|
||||||
|
this.markdownscontent = markdownscontent;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package com.qf.myafterprojecy.pojo.dto;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
public class CategoryAttributeDto {
|
||||||
|
private Integer attributeid;
|
||||||
|
|
||||||
|
@NotNull(message = "分类ID不能为空")
|
||||||
|
private Integer categoryid;
|
||||||
|
|
||||||
|
@NotBlank(message = "属性名称不能为空")
|
||||||
|
private String attributename;
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public Integer getAttributeid() {
|
||||||
|
return attributeid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttributeid(Integer attributeid) {
|
||||||
|
this.attributeid = attributeid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getCategoryid() {
|
||||||
|
return categoryid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCategoryid(Integer categoryid) {
|
||||||
|
this.categoryid = categoryid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAttributename() {
|
||||||
|
return attributename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAttributename(String attributename) {
|
||||||
|
this.attributename = attributename;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
package com.qf.myafterprojecy.pojo.dto;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
public class CategoryDto {
|
||||||
|
private Integer Categoryid;
|
||||||
|
|
||||||
|
@NotBlank(message = "分类名称不能为空")
|
||||||
|
private String typename;
|
||||||
|
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
private LocalDateTime createdAt;
|
||||||
|
|
||||||
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public Integer getCategoryid() {
|
||||||
|
return Categoryid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCategoryid(Integer Categoryid) {
|
||||||
|
this.Categoryid = Categoryid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTypename() {
|
||||||
|
return typename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTypename(String typename) {
|
||||||
|
this.typename = typename;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getCreatedAt() {
|
||||||
|
return createdAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreatedAt(LocalDateTime createdAt) {
|
||||||
|
this.createdAt = createdAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalDateTime getUpdatedAt() {
|
||||||
|
return updatedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUpdatedAt(LocalDateTime updatedAt) {
|
||||||
|
this.updatedAt = updatedAt;
|
||||||
|
}
|
||||||
|
}
|
||||||
105
src/main/java/com/qf/myafterprojecy/pojo/dto/MessageDto.java
Normal file
105
src/main/java/com/qf/myafterprojecy/pojo/dto/MessageDto.java
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
package com.qf.myafterprojecy.pojo.dto;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class MessageDto {
|
||||||
|
private Integer messageid;
|
||||||
|
|
||||||
|
private String nickname;
|
||||||
|
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
private Date createdAt;
|
||||||
|
|
||||||
|
private Integer parentid;
|
||||||
|
|
||||||
|
private Integer replyid;
|
||||||
|
|
||||||
|
private Integer articleid;
|
||||||
|
|
||||||
|
private Integer likes;
|
||||||
|
|
||||||
|
private String messageimg;
|
||||||
|
|
||||||
|
public Integer getReplyid() {
|
||||||
|
return replyid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReplyid(Integer replyid) {
|
||||||
|
this.replyid = replyid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getMessageid() {
|
||||||
|
return messageid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessageid(Integer messageid) {
|
||||||
|
this.messageid = messageid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getNickname() {
|
||||||
|
return nickname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNickname(String nickname) {
|
||||||
|
this.nickname = nickname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContent(String content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getCreatedAt() {
|
||||||
|
return createdAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreatedAt(Date createdAt) {
|
||||||
|
this.createdAt = createdAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getParentid() {
|
||||||
|
return parentid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParentid(Integer parentid) {
|
||||||
|
this.parentid = parentid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getArticleid() {
|
||||||
|
return articleid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setArticleid(Integer articleid) {
|
||||||
|
this.articleid = articleid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getLikes() {
|
||||||
|
return likes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLikes(Integer likes) {
|
||||||
|
this.likes = likes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessageimg() {
|
||||||
|
return messageimg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessageimg(String messageimg) {
|
||||||
|
this.messageimg = messageimg;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.qf.myafterprojecy.pojo.dto;
|
||||||
|
|
||||||
|
public class MessagePageDto {
|
||||||
|
private Integer pageNum;
|
||||||
|
private Integer pageSize;
|
||||||
|
private Integer articleid;
|
||||||
|
|
||||||
|
public Integer getPageNum() {
|
||||||
|
return pageNum;
|
||||||
|
}
|
||||||
|
public void setPageNum(Integer pageNum) {
|
||||||
|
this.pageNum = pageNum;
|
||||||
|
}
|
||||||
|
public Integer getPageSize() {
|
||||||
|
return pageSize;
|
||||||
|
}
|
||||||
|
public void setPageSize(Integer pageSize) {
|
||||||
|
this.pageSize = pageSize;
|
||||||
|
}
|
||||||
|
public Integer getArticleid() {
|
||||||
|
return articleid;
|
||||||
|
}
|
||||||
|
public void setArticleid(Integer articleid) {
|
||||||
|
this.articleid = articleid;
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "MessagePageDto{" +
|
||||||
|
"pageNum=" + pageNum +
|
||||||
|
", pageSize=" + pageSize +
|
||||||
|
", articleid=" + articleid +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.qf.myafterprojecy.pojo.dto;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public class NonsenseDto {
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
private Integer status;//状态 0:未发表 1:已发表 2:已删除
|
||||||
|
|
||||||
|
private Date time;
|
||||||
|
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContent(String content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getStatus() {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(Integer status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getTime() {
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTime(Date time) {
|
||||||
|
this.time = time;
|
||||||
|
}
|
||||||
|
}
|
||||||
69
src/main/java/com/qf/myafterprojecy/pojo/dto/UserDto.java
Normal file
69
src/main/java/com/qf/myafterprojecy/pojo/dto/UserDto.java
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package com.qf.myafterprojecy.pojo.dto;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
|
||||||
|
public class UserDto {
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@NotBlank(message = "用户名不能为空")
|
||||||
|
private String username;
|
||||||
|
|
||||||
|
@NotBlank(message = "密码不能为空")
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
@NotBlank(message = "邮箱不能为空")
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
@NotBlank(message = "手机号不能为空")
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
private int role;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsername(String username) {
|
||||||
|
this.username = username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPassword(String password) {
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEmail(String email) {
|
||||||
|
this.email = email;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPhone() {
|
||||||
|
return phone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPhone(String phone) {
|
||||||
|
this.phone = phone;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRole() {
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRole(int role) {
|
||||||
|
this.role = role;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
package com.qf.myafterprojecy.repository;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.pojo.Article;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Modifying;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Repository // 表明这是一个数据访问层组件,用于持久层操作
|
||||||
|
public interface ArticleRepository extends JpaRepository<Article, Integer> {
|
||||||
|
/**
|
||||||
|
* 根据文章ID查询文章信息的方法
|
||||||
|
* 使用JPQL(Java Persistence Query Language)进行查询
|
||||||
|
*
|
||||||
|
* @param id 文章的唯一标识符,作为查询条件
|
||||||
|
* @return 返回一个Optional<Article>对象,可能包含文章信息,也可能为空(如果未找到对应ID的文章)
|
||||||
|
*/
|
||||||
|
@Query("SELECT a FROM Article a WHERE a.articleid = :id")
|
||||||
|
Optional<Article> findById(@Param("id") Integer id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据标题查询文章列表
|
||||||
|
*
|
||||||
|
* @param title 文章标题的一部分,用于模糊查询
|
||||||
|
* @return 返回符合查询条件的文章列表
|
||||||
|
*/
|
||||||
|
@Query("SELECT a FROM Article a WHERE a.title LIKE %:title%")
|
||||||
|
List<Article> findByTitle(@Param("title") String title);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据文章ID查询已发布的文章
|
||||||
|
* 使用JPQL查询语句,只查询状态为1(已发布)且指定ID的文章
|
||||||
|
*
|
||||||
|
* @return 返回符合查询条件的文章列表
|
||||||
|
*/
|
||||||
|
@Query("SELECT a FROM Article a WHERE a.status = 1")
|
||||||
|
List<Article> findPublishedByAuthor();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据分类ID查询已发布的文章列表
|
||||||
|
* 使用JPQL查询语句,筛选状态为已发布(status=1)且指定分类(typeid)的文章
|
||||||
|
*
|
||||||
|
* @param attributeid 分类ID,通过@Param注解映射到查询语句中的:attributeid参数
|
||||||
|
* @return 返回符合条件Article对象的列表
|
||||||
|
*/
|
||||||
|
@Query("SELECT a FROM Article a WHERE a.status = 1 AND a.attributeid = :attributeid")
|
||||||
|
List<Article> findPublishedByAttribute(@Param("attributeid") Integer attributeid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据属性ID查询最新的文章列表
|
||||||
|
* 使用JPQL查询语句,筛选状态为已发布(status=1)且指定属性(attributeid)的文章,按创建时间降序排序
|
||||||
|
*
|
||||||
|
* @param attributeid 属性ID,通过@Param注解映射到查询语句中的:attributeid参数
|
||||||
|
* @return 返回符合条件Article对象的列表,按创建时间降序排列
|
||||||
|
*/
|
||||||
|
@Query("SELECT a FROM Article a WHERE a.status = 1 AND a.attributeid = :attributeid ORDER BY a.createdAt DESC")
|
||||||
|
List<Article> findLatestByAttribute(@Param("attributeid") Integer attributeid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用@Modifying注解标记这是一个修改操作,通常用于UPDATE或DELETE语句
|
||||||
|
* 使用@Query注解定义自定义的JPQL查询语句
|
||||||
|
* 该查询用于将指定文章的浏览量(viewCount)增加1
|
||||||
|
*
|
||||||
|
* @param articleid 文章的唯一标识符,通过@Param注解将方法参数与查询参数绑定
|
||||||
|
*/
|
||||||
|
@Modifying
|
||||||
|
@Query("UPDATE Article a SET a.viewCount = COALESCE(a.viewCount, 0) + 1 WHERE a.articleid = :articleid")
|
||||||
|
void incrementViewCount(@Param("articleid") Integer articleid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据浏览量降序查询状态为1的所有文章
|
||||||
|
* 该查询使用JPQL语句,从Article实体中选取数据
|
||||||
|
*
|
||||||
|
* @return 返回一个Article对象的列表,按浏览量(viewCount)降序排列
|
||||||
|
*/
|
||||||
|
@Query("SELECT a FROM Article a WHERE a.status = 1 ORDER BY a.viewCount DESC")
|
||||||
|
List<Article> findMostViewed();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据状态查询文章列表
|
||||||
|
* @param status 文章状态,0-草稿,1-已发布,2-已删除
|
||||||
|
* @return 返回符合状态条件的文章列表
|
||||||
|
*/
|
||||||
|
@Query("SELECT a FROM Article a WHERE a.status = :status")
|
||||||
|
List<Article> findByStatus(@Param("status") Integer status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据状态分页查询文章列表
|
||||||
|
* @param status 文章状态,0-草稿,1-已发布,2-已删除
|
||||||
|
* @param pageable 分页参数,包含页码、每页大小和排序信息
|
||||||
|
* @return 返回符合状态条件的文章分页结果
|
||||||
|
*/
|
||||||
|
@Query("SELECT a FROM Article a WHERE a.status = :status ORDER BY a.createdAt DESC")
|
||||||
|
Page<Article> findByStatusWithPagination(@Param("status") Integer status, Pageable pageable);
|
||||||
|
/**
|
||||||
|
* 根据属性ID数组分页查询文章列表
|
||||||
|
* @param attributeids 文章属性ID数组
|
||||||
|
* @param status 文章状态,0-草稿,1-已发布,2-已删除
|
||||||
|
* @param pageable 分页参数,包含页码、每页大小和排序信息
|
||||||
|
* @return 返回符合状态条件的文章分页结果
|
||||||
|
*/
|
||||||
|
@Query("SELECT a FROM Article a WHERE a.status = :status AND a.attributeid IN :attributeids ORDER BY a.createdAt DESC")
|
||||||
|
Page<Article> findByStatusWithPagination(@Param("status") Integer status, @Param("attributeids") List<Integer> attributeids, Pageable pageable);
|
||||||
|
/**
|
||||||
|
* 根据属性ID分页查询文章列表
|
||||||
|
* @param attributeid 文章属性ID
|
||||||
|
* @param status 文章状态,0-草稿,1-已发布,2-已删除
|
||||||
|
* @param pageable 分页参数,包含页码、每页大小和排序信息
|
||||||
|
* @return 返回符合状态条件的文章分页结果
|
||||||
|
*/
|
||||||
|
@Query("SELECT a FROM Article a WHERE a.status = :status AND a.attributeid = :attributeid ORDER BY a.createdAt DESC")
|
||||||
|
Page<Article> findByStatusWithPagination(@Param("status") Integer status, @Param("attributeid") Integer attributeid, Pageable pageable);
|
||||||
|
/**
|
||||||
|
* 根据文章标题分页模糊查询文章列表
|
||||||
|
* @param title 文章标题
|
||||||
|
* @param status 文章状态,0-草稿,1-已发布,2-已删除
|
||||||
|
* @param pageable 分页参数,包含页码、每页大小和排序信息
|
||||||
|
* @return 返回符合状态条件的文章分页结果
|
||||||
|
*/
|
||||||
|
@Query("SELECT a FROM Article a WHERE a.status = :status AND a.title LIKE %:title% ORDER BY a.createdAt DESC")
|
||||||
|
Page<Article> findByStatusWithPagination(@Param("status") Integer status, @Param("title") String title, Pageable pageable);
|
||||||
|
/**
|
||||||
|
* 统计指定状态的文章数量
|
||||||
|
* @param status 文章状态,0-草稿,1-已发布,2-已删除
|
||||||
|
* @return 返回符合状态条件的文章数量
|
||||||
|
*/
|
||||||
|
@Query("SELECT COUNT(a) FROM Article a WHERE a.status = :status")
|
||||||
|
Integer countByStatus(@Param("status") Integer status);
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.qf.myafterprojecy.repository;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.pojo.Categoryattribute;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface CategoryAttributeRepository extends JpaRepository<Categoryattribute, Integer> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据分类ID查询分类属性列表
|
||||||
|
* @param categoryid 分类ID
|
||||||
|
* @return 返回该分类下的所有属性列表
|
||||||
|
*/
|
||||||
|
@Query("SELECT ca FROM Categoryattribute ca WHERE ca.categoryid = :categoryid")
|
||||||
|
List<Categoryattribute> findByCategoryId(@Param("categoryid") Integer categoryid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据属性ID查询属性信息
|
||||||
|
* @param attributeid 属性ID
|
||||||
|
* @return 返回属性对象
|
||||||
|
*/
|
||||||
|
@Query("SELECT ca FROM Categoryattribute ca WHERE ca.attributeid = :attributeid")
|
||||||
|
Optional<Categoryattribute> findByAttributeId(@Param("attributeid") Integer attributeid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查分类下是否存在指定名称的属性
|
||||||
|
* @param categoryid 分类ID
|
||||||
|
* @param attributename 属性名称
|
||||||
|
* @return 是否存在
|
||||||
|
*/
|
||||||
|
boolean existsByCategoryidAndAttributename(Integer categoryid, String attributename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据分类ID和属性名称查询属性
|
||||||
|
* @param categoryid 分类ID
|
||||||
|
* @param attributename 属性名称
|
||||||
|
* @return 属性对象
|
||||||
|
*/
|
||||||
|
Optional<Categoryattribute> findByCategoryidAndAttributename(Integer categoryid, String attributename);
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package com.qf.myafterprojecy.repository;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.pojo.Category;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface CategoryRepository extends JpaRepository<Category, Integer> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据分类名称查询分类信息
|
||||||
|
* @param typename 分类名称
|
||||||
|
* @return 返回符合条件的分类列表
|
||||||
|
*/
|
||||||
|
|
||||||
|
List<Category> findByTypenameContaining(String typename);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查分类名称是否存在
|
||||||
|
* @param typename 分类名称
|
||||||
|
* @return 返回是否存在
|
||||||
|
*/
|
||||||
|
boolean existsByTypename(String typename);
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
package com.qf.myafterprojecy.repository;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.pojo.Message;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.data.web.SpringDataWebProperties.Pageable;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
import org.springframework.data.jpa.repository.Modifying;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface MessageRepository extends JpaRepository<Message, Integer> {
|
||||||
|
|
||||||
|
// 根据文章ID查询消息
|
||||||
|
/**
|
||||||
|
* 根据文章ID查询消息
|
||||||
|
* @param articleid 文章ID
|
||||||
|
* @return 文章下的消息列表
|
||||||
|
*/
|
||||||
|
List<Message> findByArticleid(Integer articleid);
|
||||||
|
|
||||||
|
// 查询所有父消息(回复的根消息)
|
||||||
|
/**
|
||||||
|
* 查询所有父消息(回复的根消息)
|
||||||
|
* @return 根回复消息列表
|
||||||
|
*/
|
||||||
|
List<Message> findByParentidIsNull();
|
||||||
|
|
||||||
|
// 根据父消息ID查询回复
|
||||||
|
/**
|
||||||
|
* 根据父消息ID查询回复
|
||||||
|
* @param parentid 父消息ID
|
||||||
|
* @return 回复消息列表
|
||||||
|
*/
|
||||||
|
List<Message> findByParentid(Integer parentid);
|
||||||
|
|
||||||
|
// 根据昵称模糊查询消息
|
||||||
|
/**
|
||||||
|
* 根据昵称模糊查询消息
|
||||||
|
* @param nickname 昵称关键词
|
||||||
|
* @return 包含关键词的消息列表
|
||||||
|
*/
|
||||||
|
List<Message> findByNicknameContaining(String nickname);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询指定文章下的所有父消息(根回复)
|
||||||
|
* @param articleId 文章ID
|
||||||
|
* @return 根回复消息列表
|
||||||
|
*/
|
||||||
|
@Query("SELECT m FROM Message m WHERE m.articleid = :articleId AND m.parentid IS NULL ORDER BY m.createdAt DESC")
|
||||||
|
List<Message> findRootMessagesByArticleId(@Param("articleId") Integer articleId);
|
||||||
|
/**
|
||||||
|
* 点赞数增加
|
||||||
|
* @param messageId 消息ID
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Modifying
|
||||||
|
@Query("UPDATE Message m SET m.likes = COALESCE(m.likes, 0) + 1 WHERE m.messageid = :messageId")
|
||||||
|
void incrementLikes(@Param("messageId") Integer messageId);
|
||||||
|
// 统计指定文章的评论数量
|
||||||
|
/**
|
||||||
|
* 根据文章ID分页查询消息
|
||||||
|
* @param articleid 文章ID
|
||||||
|
* @param pageable 分页信息
|
||||||
|
* @return 分页消息列表
|
||||||
|
*/
|
||||||
|
@Query("SELECT m FROM Message m WHERE m.articleid = :articleId ORDER BY m.createdAt DESC")
|
||||||
|
Page<Message> findByArticleId(@Param("articleId") Integer articleId, PageRequest pageable);
|
||||||
|
/**
|
||||||
|
* 根据页查询消息
|
||||||
|
* @param pageable 分页信息
|
||||||
|
* @return 分页消息列表
|
||||||
|
*/
|
||||||
|
@Query("SELECT m FROM Message m WHERE m.articleid IS NULL ORDER BY m.createdAt DESC")
|
||||||
|
Page<Message> findAllMessages(PageRequest pageable);
|
||||||
|
/**
|
||||||
|
* 统计指定文章下的回复消息数量
|
||||||
|
* @param articleId 文章ID
|
||||||
|
* @return 回复消息数量
|
||||||
|
*/
|
||||||
|
@Query("SELECT COUNT(m) FROM Message m WHERE m.articleid = :articleId AND m.parentid IS NULL")
|
||||||
|
Integer countReplyByArticleId(@Param("articleId") Integer articleId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 统计指定文章id parentid为空的回复消息数量
|
||||||
|
* @param articleId 文章ID
|
||||||
|
* @return 回复消息数量
|
||||||
|
*/
|
||||||
|
@Query("SELECT COUNT(m) FROM Message m WHERE m.articleid IS NULL AND m.parentid IS NULL")
|
||||||
|
Integer countReplyByArticleIdIsNull();
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package com.qf.myafterprojecy.repository;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.pojo.Nonsense;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface NonsenseRepository extends JpaRepository<Nonsense, Integer> {
|
||||||
|
/**
|
||||||
|
* 根据状态获取文章列表
|
||||||
|
* @param status 文章状态(0:未发表 1:已发表 2:已删除)
|
||||||
|
* @return 返回包含文章列表的ResponseMessage对象
|
||||||
|
*/
|
||||||
|
@Query("SELECT n FROM Nonsense n WHERE n.status = :status")
|
||||||
|
List<Nonsense> findByStatus(@Param("status") Integer status);
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package com.qf.myafterprojecy.repository;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.pojo.Users;
|
||||||
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.stereotype.Repository;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Repository
|
||||||
|
public interface UsersRepository extends JpaRepository<Users, Long> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户名查询用户信息
|
||||||
|
* @param username 用户名
|
||||||
|
* @return 返回符合条件的用户对象
|
||||||
|
*/
|
||||||
|
Optional<Users> findByUsername(String username);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据邮箱查询用户信息
|
||||||
|
* @param email 邮箱
|
||||||
|
* @return 返回符合条件的用户对象
|
||||||
|
*/
|
||||||
|
Optional<Users> findByEmail(String email);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据手机号查询用户信息
|
||||||
|
* @param phone 手机号
|
||||||
|
* @return 返回符合条件的用户对象
|
||||||
|
*/
|
||||||
|
Optional<Users> findByPhone(String phone);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查用户名是否存在
|
||||||
|
* @param username 用户名
|
||||||
|
* @return 返回是否存在
|
||||||
|
*/
|
||||||
|
boolean existsByUsername(String username);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查邮箱是否存在
|
||||||
|
* @param email 邮箱
|
||||||
|
* @return 返回是否存在
|
||||||
|
*/
|
||||||
|
boolean existsByEmail(String email);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查手机号是否存在
|
||||||
|
* @param phone 手机号
|
||||||
|
* @return 返回是否存在
|
||||||
|
*/
|
||||||
|
boolean existsByPhone(String phone);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据角色查询用户列表
|
||||||
|
* @param role 角色
|
||||||
|
* @return 用户列表
|
||||||
|
*/
|
||||||
|
@Query("SELECT u FROM Users u WHERE u.role = :role")
|
||||||
|
java.util.List<Users> findByRole(@Param("role") int role);
|
||||||
|
}
|
||||||
108
src/main/java/com/qf/myafterprojecy/service/IArticleService.java
Normal file
108
src/main/java/com/qf/myafterprojecy/service/IArticleService.java
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
package com.qf.myafterprojecy.service;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
|
||||||
|
import com.qf.myafterprojecy.pojo.Article;
|
||||||
|
import com.qf.myafterprojecy.pojo.dto.ArriclePageDto;
|
||||||
|
import com.qf.myafterprojecy.pojo.dto.ArticleDto;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface IArticleService {
|
||||||
|
ResponseMessage<Article> getArticleById(String id);
|
||||||
|
|
||||||
|
ResponseMessage<List<Article>> getAllArticles();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据标题查询文章列表
|
||||||
|
*
|
||||||
|
* @param title 文章标题的一部分,用于模糊查询
|
||||||
|
* @return 返回符合查询条件的文章列表
|
||||||
|
*/
|
||||||
|
ResponseMessage<List<Article>> getArticlesByTitle(String title);
|
||||||
|
/**
|
||||||
|
* 根据状态获取文章列表
|
||||||
|
* @param status 文章状态(0:未发表 1:已发表 2:已删除)
|
||||||
|
* @return 返回包含文章列表的ResponseMessage对象
|
||||||
|
*/
|
||||||
|
ResponseMessage<List<Article>> getArticlesByStatus(Integer status);
|
||||||
|
/**
|
||||||
|
* 获取文章数量
|
||||||
|
* @param status 文章状态(0:未发表 1:已发表 2:已删除)
|
||||||
|
* @return 返回文章数量
|
||||||
|
*/
|
||||||
|
ResponseMessage<Integer> getArticleCount(Integer status);
|
||||||
|
/**
|
||||||
|
* 创建新文章
|
||||||
|
* 仅限AUTHOR角色用户访问
|
||||||
|
*
|
||||||
|
* @param articleDto 包含文章数据的DTO对象
|
||||||
|
* @return 返回包含新创建文章信息的ResponseMessage对象
|
||||||
|
*/
|
||||||
|
ResponseMessage<Article> saveArticle(ArticleDto articleDto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新指定ID的文章
|
||||||
|
*
|
||||||
|
* @param id 文章ID
|
||||||
|
* @param articleDto 包含更新信息的ArticleDto对象
|
||||||
|
* @return 返回包含操作结果的ResponseMessage对象
|
||||||
|
*/
|
||||||
|
ResponseMessage<Article> updateArticle(Integer id, ArticleDto articleDto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除指定ID的文章
|
||||||
|
*
|
||||||
|
* @param id 文章ID
|
||||||
|
* @return 返回包含操作结果的ResponseMessage对象
|
||||||
|
*/
|
||||||
|
ResponseMessage<Article> deleteArticle(Integer id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据分类ID查询文章列表(兼容旧接口)
|
||||||
|
*
|
||||||
|
* @param typeid 分类ID
|
||||||
|
* @return 返回符合查询条件的文章列表
|
||||||
|
*/
|
||||||
|
ResponseMessage<List<Article>> getArticlesByCategory(Integer typeid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据属性ID查询文章列表
|
||||||
|
*
|
||||||
|
* @param attributeid 属性ID
|
||||||
|
* @return 返回符合查询条件的文章列表
|
||||||
|
*/
|
||||||
|
ResponseMessage<List<Article>> getArticlesByAttribute(Integer attributeid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据属性ID查询最新文章列表
|
||||||
|
*
|
||||||
|
* @param attributeid 属性ID
|
||||||
|
* @return 返回符合查询条件的最新文章列表
|
||||||
|
*/
|
||||||
|
ResponseMessage<List<Article>> getLatestArticlesByAttribute(Integer attributeid);
|
||||||
|
|
||||||
|
ResponseMessage<List<Article>> getMostViewedArticles();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增加文章浏览量
|
||||||
|
*
|
||||||
|
* @param id 文章ID
|
||||||
|
* @return 返回包含更新后文章信息的ResponseMessage对象
|
||||||
|
*/
|
||||||
|
ResponseMessage<Article> incrementViewCount(Integer id);
|
||||||
|
/**
|
||||||
|
* 获取已发布的文章列表
|
||||||
|
* @return 返回包含已发布文章列表的ResponseMessage对象
|
||||||
|
*/
|
||||||
|
ResponseMessage<List<Article>> getPublishedArticles();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据状态分页查询文章列表
|
||||||
|
* @param status 文章状态(0:未发表 1:已发表 2:已删除)
|
||||||
|
* @param page 页码,从0开始
|
||||||
|
* @param size 每页大小
|
||||||
|
* @return 返回包含分页文章列表的ResponseMessage对象
|
||||||
|
*/
|
||||||
|
ResponseMessage<Page<Article>> getArticlesByStatusWithPagination(ArriclePageDto arriclePageDto);
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package com.qf.myafterprojecy.service;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
|
||||||
|
import com.qf.myafterprojecy.pojo.Categoryattribute;
|
||||||
|
import com.qf.myafterprojecy.pojo.dto.CategoryAttributeDto;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface ICategoryAttributeService {
|
||||||
|
/**
|
||||||
|
* 获取全部分类属性
|
||||||
|
* @return 所有分类属性列表
|
||||||
|
*/
|
||||||
|
ResponseMessage<List<Categoryattribute>> getAllCategoryAttributes();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID获取分类属性
|
||||||
|
* @param id 属性ID
|
||||||
|
* @return 分类属性信息
|
||||||
|
*/
|
||||||
|
ResponseMessage<Categoryattribute> getCategoryAttributeById(Integer id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据分类ID获取属性列表
|
||||||
|
* @param categoryId 分类ID
|
||||||
|
* @return 属性列表
|
||||||
|
*/
|
||||||
|
ResponseMessage<List<Categoryattribute>> getAttributesByCategoryId(Integer categoryId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建新的分类属性
|
||||||
|
* @param dto 分类属性数据
|
||||||
|
* @return 创建结果
|
||||||
|
*/
|
||||||
|
ResponseMessage<Categoryattribute> saveCategoryAttribute(CategoryAttributeDto dto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新分类属性
|
||||||
|
* @param id 属性ID
|
||||||
|
* @param dto 分类属性数据
|
||||||
|
* @return 更新结果
|
||||||
|
*/
|
||||||
|
ResponseMessage<Categoryattribute> updateCategoryAttribute(Integer id, CategoryAttributeDto dto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除分类属性
|
||||||
|
* @param id 属性ID
|
||||||
|
* @return 删除结果
|
||||||
|
*/
|
||||||
|
ResponseMessage<Boolean> deleteCategoryAttribute(Integer id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查分类下是否存在指定名称的属性
|
||||||
|
* @param categoryId 分类ID
|
||||||
|
* @param attributeName 属性名称
|
||||||
|
* @return 是否存在
|
||||||
|
*/
|
||||||
|
ResponseMessage<Boolean> existsByCategoryAndName(Integer categoryId, String attributeName);
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package com.qf.myafterprojecy.service;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
|
||||||
|
import com.qf.myafterprojecy.pojo.Category;
|
||||||
|
import com.qf.myafterprojecy.pojo.dto.CategoryDto;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface ICategoryService {
|
||||||
|
/**
|
||||||
|
* 根据ID获取分类信息
|
||||||
|
* @param id 分类ID
|
||||||
|
* @return 返回分类信息
|
||||||
|
*/
|
||||||
|
ResponseMessage<Category> getCategoryById(Integer id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有分类列表
|
||||||
|
* @return 返回分类列表
|
||||||
|
*/
|
||||||
|
ResponseMessage<List<Category>> getAllCategories();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存新分类
|
||||||
|
* @param categoryDto 分类数据传输对象
|
||||||
|
* @return 返回保存结果
|
||||||
|
*/
|
||||||
|
ResponseMessage<Category> saveCategory(CategoryDto categoryDto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新分类信息
|
||||||
|
* @param id 分类ID
|
||||||
|
* @param categoryDto 分类数据传输对象
|
||||||
|
* @return 返回更新结果
|
||||||
|
*/
|
||||||
|
ResponseMessage<Category> updateCategory(Integer id, CategoryDto categoryDto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除分类
|
||||||
|
* @param id 分类ID
|
||||||
|
* @return 返回删除结果
|
||||||
|
*/
|
||||||
|
ResponseMessage<Boolean> deleteCategory(Integer id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据分类名称搜索分类
|
||||||
|
* @param typename 分类名称
|
||||||
|
* @return 返回符合条件的分类列表
|
||||||
|
*/
|
||||||
|
ResponseMessage<List<Category>> searchCategoriesByTypename(String typename);
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package com.qf.myafterprojecy.service;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
|
||||||
|
import com.qf.myafterprojecy.pojo.Message;
|
||||||
|
import com.qf.myafterprojecy.pojo.dto.MessageDto;
|
||||||
|
import com.qf.myafterprojecy.pojo.dto.MessagePageDto;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface IMessageService {
|
||||||
|
/**
|
||||||
|
* 获取所有消息的方法
|
||||||
|
*
|
||||||
|
* @return 返回一个ResponseMessage对象,其中包含一个可迭代的Message集合
|
||||||
|
* ResponseMessage是响应消息的包装类,Iterable<Message>表示可迭代的消息集合
|
||||||
|
*/
|
||||||
|
ResponseMessage<Iterable<Message>> getAllMessages();
|
||||||
|
/**
|
||||||
|
* 分页
|
||||||
|
* @param id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
ResponseMessage<List<Message>> getMessagesByPage(MessagePageDto messagePageDto);
|
||||||
|
/**
|
||||||
|
* 获取回复消息条数 如果id为空获取文章id为空的消息条数
|
||||||
|
* @param articleId 文章id
|
||||||
|
* @return 回复消息条数
|
||||||
|
*/
|
||||||
|
ResponseMessage<Integer> getMessageCountByArticleId(Integer articleId);
|
||||||
|
/**
|
||||||
|
* 根据消息ID获取消息的方法
|
||||||
|
*
|
||||||
|
* @param id 消息的唯一标识符
|
||||||
|
* @return ResponseMessage<Message> 包含消息的响应对象,其中Message为消息的具体内容
|
||||||
|
*/
|
||||||
|
ResponseMessage<Message> getMessageById(Integer id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存消息的方法
|
||||||
|
*
|
||||||
|
* @param message 消息数据传输对象,包含需要保存的消息信息
|
||||||
|
* @return ResponseMessage<Message> 返回一个响应消息对象,包含操作结果和保存后的消息信息
|
||||||
|
*/
|
||||||
|
ResponseMessage<Message> saveMessage(MessageDto message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据消息ID删除消息的方法
|
||||||
|
*
|
||||||
|
* @param id 要删除的消息ID
|
||||||
|
* @return ResponseMessage<Message> 包含操作结果的响应消息,其中泛型Message表示被删除的消息内容
|
||||||
|
*/
|
||||||
|
ResponseMessage<Message> deleteMessage(Integer id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增加消息的点赞数
|
||||||
|
*
|
||||||
|
* @param id 消息ID
|
||||||
|
* @return 包含操作结果的响应消息,其中泛型Message表示操作后的消息内容
|
||||||
|
*/
|
||||||
|
ResponseMessage<Message> likeMessage(Integer id);
|
||||||
|
|
||||||
|
// 新增方法
|
||||||
|
/**
|
||||||
|
* 根据文章ID查询消息
|
||||||
|
*
|
||||||
|
* @param articleId 文章ID
|
||||||
|
* @return 消息列表
|
||||||
|
*/
|
||||||
|
ResponseMessage<List<Message>> getMessagesByArticleId(Integer articleId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询所有父消息(根回复)
|
||||||
|
*
|
||||||
|
* @return 父消息列表
|
||||||
|
*/
|
||||||
|
ResponseMessage<List<Message>> getRootMessages();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据父消息ID查询回复
|
||||||
|
*
|
||||||
|
* @param parentId 父消息ID
|
||||||
|
* @return 回复消息列表
|
||||||
|
*/
|
||||||
|
ResponseMessage<List<Message>> getRepliesByParentId(Integer parentId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据昵称模糊查询消息
|
||||||
|
*
|
||||||
|
* @param nickname 昵称
|
||||||
|
* @return 匹配的消息列表
|
||||||
|
*/
|
||||||
|
ResponseMessage<List<Message>> searchMessagesByNickname(String nickname);
|
||||||
|
|
||||||
|
// 删除所有评论
|
||||||
|
ResponseMessage<Void> deleteAllMessages();
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package com.qf.myafterprojecy.service;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
|
||||||
|
import com.qf.myafterprojecy.pojo.Nonsense;
|
||||||
|
import com.qf.myafterprojecy.pojo.dto.NonsenseDto;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface INonsenseService {
|
||||||
|
/**
|
||||||
|
* 获取所有疯言疯语内容
|
||||||
|
* @return 疯言疯语内容列表
|
||||||
|
*/
|
||||||
|
ResponseMessage<List<Nonsense>> getAllNonsense();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID获取疯言疯语内容
|
||||||
|
* @param id 疯言疯语内容ID
|
||||||
|
* @return 疯言疯语内容
|
||||||
|
*/
|
||||||
|
ResponseMessage<Nonsense> getNonsenseById(Integer id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据状态获取疯言疯语内容
|
||||||
|
* @param status 状态:0未发表 1已发表 2已删除
|
||||||
|
* @return 疯言疯语内容列表
|
||||||
|
*/
|
||||||
|
ResponseMessage<List<Nonsense>> getNonsenseByStatus(Integer status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新疯言疯语内容状态
|
||||||
|
* @param id 疯言疯语内容ID
|
||||||
|
* @param status 新状态:0未发表 1已发表 2已删除
|
||||||
|
* @return 更新结果
|
||||||
|
*/
|
||||||
|
ResponseMessage<Nonsense> updateNonsenseStatus(Integer id, Integer status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存疯言疯语内容
|
||||||
|
* @param nonsenseDto 疯言疯语内容数据传输对象
|
||||||
|
* @return 保存结果
|
||||||
|
*/
|
||||||
|
ResponseMessage<Nonsense> saveNonsense(NonsenseDto nonsenseDto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新疯言疯语内容
|
||||||
|
* @param id 疯言疯语内容ID
|
||||||
|
* @param nonsenseDto 疯言疯语内容数据传输对象
|
||||||
|
* @return 更新结果
|
||||||
|
*/
|
||||||
|
ResponseMessage<Nonsense> updateNonsense(Integer id, NonsenseDto nonsenseDto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除疯言疯语内容
|
||||||
|
* @param id 疯言疯语内容ID
|
||||||
|
* @return 删除结果
|
||||||
|
*/
|
||||||
|
ResponseMessage<Boolean> deleteNonsense(Integer id);
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package com.qf.myafterprojecy.service;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
|
||||||
|
import com.qf.myafterprojecy.pojo.Users;
|
||||||
|
import com.qf.myafterprojecy.pojo.dto.UserDto;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface IUserService {
|
||||||
|
/**
|
||||||
|
* 根据ID获取用户信息
|
||||||
|
* @param id 用户ID
|
||||||
|
* @return 返回用户信息
|
||||||
|
*/
|
||||||
|
ResponseMessage<Users> getUserById(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有用户列表
|
||||||
|
* @return 返回用户列表
|
||||||
|
*/
|
||||||
|
ResponseMessage<List<Users>> getAllUsers();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户名获取用户信息
|
||||||
|
* @param username 用户名
|
||||||
|
* @return 返回用户信息
|
||||||
|
*/
|
||||||
|
ResponseMessage<Users> getUserByUsername(String username);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存新用户
|
||||||
|
* @param userDto 用户数据传输对象
|
||||||
|
* @return 返回保存结果
|
||||||
|
*/
|
||||||
|
ResponseMessage<Users> saveUser(UserDto userDto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新用户信息
|
||||||
|
* @param id 用户ID
|
||||||
|
* @param userDto 用户数据传输对象
|
||||||
|
* @return 返回更新结果
|
||||||
|
*/
|
||||||
|
ResponseMessage<Users> updateUser(Long id, UserDto userDto);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除用户
|
||||||
|
* @param id 用户ID
|
||||||
|
* @return 返回删除结果
|
||||||
|
*/
|
||||||
|
ResponseMessage<Boolean> deleteUser(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据角色查询用户列表
|
||||||
|
* @param role 角色
|
||||||
|
* @return 用户列表
|
||||||
|
*/
|
||||||
|
ResponseMessage<List<Users>> getUsersByRole(int role);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查用户名是否存在
|
||||||
|
* @param username 用户名
|
||||||
|
* @return 是否存在
|
||||||
|
*/
|
||||||
|
ResponseMessage<Boolean> existsByUsername(String username);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查邮箱是否存在
|
||||||
|
* @param email 邮箱
|
||||||
|
* @return 是否存在
|
||||||
|
*/
|
||||||
|
ResponseMessage<Boolean> existsByEmail(String email);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查手机号是否存在
|
||||||
|
* @param phone 手机号
|
||||||
|
* @return 是否存在
|
||||||
|
*/
|
||||||
|
ResponseMessage<Boolean> existsByPhone(String phone);
|
||||||
|
}
|
||||||
@@ -0,0 +1,362 @@
|
|||||||
|
package com.qf.myafterprojecy.service.impl;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
|
||||||
|
import com.qf.myafterprojecy.pojo.Article;
|
||||||
|
import com.qf.myafterprojecy.pojo.Categoryattribute;
|
||||||
|
import com.qf.myafterprojecy.pojo.dto.ArriclePageDto;
|
||||||
|
import com.qf.myafterprojecy.pojo.dto.ArticleDto;
|
||||||
|
import com.qf.myafterprojecy.repository.ArticleRepository;
|
||||||
|
import com.qf.myafterprojecy.repository.CategoryAttributeRepository;
|
||||||
|
import com.qf.myafterprojecy.service.IArticleService;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.dao.DataAccessException;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class ArticleService implements IArticleService {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ArticleService.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ArticleRepository articleRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CategoryAttributeRepository categoryAttributeRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据文章ID获取文章详情
|
||||||
|
* @param id 文章ID
|
||||||
|
* @return 返回包含文章详情的ResponseMessage对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public ResponseMessage<Article> getArticleById(String id) {
|
||||||
|
try {
|
||||||
|
if (id == null || id.isEmpty()) {
|
||||||
|
return ResponseMessage.badRequest("文章ID不能为空");
|
||||||
|
}
|
||||||
|
Article article = articleRepository.findById(Integer.parseInt(id))
|
||||||
|
.orElseThrow(() -> new RuntimeException("文章不存在"));
|
||||||
|
// 文章浏览次数增加
|
||||||
|
articleRepository.incrementViewCount(Integer.parseInt(id));
|
||||||
|
|
||||||
|
|
||||||
|
return ResponseMessage.success(article, "获取文章成功");
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return ResponseMessage.badRequest("文章ID格式不正确");
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
if (e.getMessage().contains("文章不存在")) {
|
||||||
|
return ResponseMessage.notFound("文章不存在");
|
||||||
|
}
|
||||||
|
log.error("获取文章失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("获取文章失败");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取文章失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("获取文章失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取文章数量
|
||||||
|
* @param status 文章状态(0:未发表 1:已发表 2:已删除)
|
||||||
|
* @return 返回文章数量
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public ResponseMessage<Integer> getArticleCount(Integer status) {
|
||||||
|
try {
|
||||||
|
if (status == null) {
|
||||||
|
return ResponseMessage.badRequest("文章状态不能为空");
|
||||||
|
}
|
||||||
|
if (status < 0 || status > 2) {
|
||||||
|
return ResponseMessage.badRequest("文章状态值必须在0到2之间");
|
||||||
|
}
|
||||||
|
Integer count = articleRepository.countByStatus(status);
|
||||||
|
return ResponseMessage.success(count, "获取文章数量成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取文章数量失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("获取文章数量失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 根据状态获取文章列表
|
||||||
|
* @param status 文章状态(0:未发表 1:已发表 2:已删除)
|
||||||
|
* @return 返回包含文章列表的ResponseMessage对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public ResponseMessage<List<Article>> getArticlesByStatus(Integer status) {
|
||||||
|
try {
|
||||||
|
if (status == null) {
|
||||||
|
return ResponseMessage.badRequest("文章状态不能为空");
|
||||||
|
}
|
||||||
|
if (status < 0 || status > 2) {
|
||||||
|
return ResponseMessage.badRequest("文章状态值必须在0到2之间");
|
||||||
|
}
|
||||||
|
List<Article> articles = articleRepository.findByStatus(status);
|
||||||
|
return ResponseMessage.success(articles, "根据状态查询文章成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("根据状态查询文章列表失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("根据状态查询文章列表失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 获取已发布的文章列表
|
||||||
|
* @return 返回包含已发布文章列表的ResponseMessage对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public ResponseMessage<List<Article>> getPublishedArticles() {
|
||||||
|
try {
|
||||||
|
List<Article> articles = articleRepository.findByStatus(1);
|
||||||
|
return ResponseMessage.success(articles, "获取已发布文章列表成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取已发布文章列表失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("获取已发布文章列表失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public ResponseMessage<Page<Article>> getArticlesByStatusWithPagination(ArriclePageDto arriclePageDto) {
|
||||||
|
if (arriclePageDto.getPagenum() == null || arriclePageDto.getPagenum() < 0) {
|
||||||
|
arriclePageDto.setPagenum(0); // 默认第一页
|
||||||
|
}
|
||||||
|
if (arriclePageDto.getPagesize() == null || arriclePageDto.getPagesize() <= 0 || arriclePageDto.getPagesize() > 100) {
|
||||||
|
arriclePageDto.setPagesize(10); // 默认每页10条,最大100条
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// 如果文章状态值是否在0到2之间则根据文章状态查询文章列表
|
||||||
|
if (arriclePageDto.getStatus() < 0 || arriclePageDto.getStatus() > 2) {
|
||||||
|
return ResponseMessage.badRequest("文章状态值必须在0到2之间");
|
||||||
|
}
|
||||||
|
PageRequest pageRequest = PageRequest.of(arriclePageDto.getPagenum(), arriclePageDto.getPagesize());
|
||||||
|
// 如果文章分类ID不为空则根据文章分类ID查询文章列表
|
||||||
|
if (arriclePageDto.getCategoryid() != null && arriclePageDto.getCategoryid() > 0) {
|
||||||
|
// 如果文章分类ID不为空则根据文章分类ID查询文章列表
|
||||||
|
List<Categoryattribute> categoryAttribute = categoryAttributeRepository.findByCategoryId(arriclePageDto.getCategoryid());
|
||||||
|
if (categoryAttribute.isEmpty()) {
|
||||||
|
return ResponseMessage.badRequest("分类下没有属性");
|
||||||
|
}
|
||||||
|
// 如果文章属性ID数组不为空则根据文章属性ID数组查询文章列表
|
||||||
|
List<Integer> attributeids = categoryAttribute.stream().map(Categoryattribute::getAttributeid).collect(Collectors.toList());
|
||||||
|
// 根据分类ID对应的属性ID数组分页查询文章列表
|
||||||
|
Page<Article> articlePage = articleRepository.findByStatusWithPagination(arriclePageDto.getStatus(), attributeids, pageRequest);
|
||||||
|
return ResponseMessage.success(articlePage, "根据分类ID分页查询文章成功");
|
||||||
|
}
|
||||||
|
// 如果文章属性ID不为空则根据文章属性ID查询文章列表
|
||||||
|
if (arriclePageDto.getAttributeid() != null && arriclePageDto.getAttributeid() > 0) {
|
||||||
|
Page<Article> articlePage = articleRepository.findByStatusWithPagination(arriclePageDto.getStatus(), arriclePageDto.getAttributeid(), pageRequest);
|
||||||
|
return ResponseMessage.success(articlePage, "根据属性ID分页查询文章成功");
|
||||||
|
}
|
||||||
|
// 如果文章标题不为空则根据文章标题查询文章列表
|
||||||
|
if (arriclePageDto.getTitle() != null && !arriclePageDto.getTitle().isEmpty()) {
|
||||||
|
Page<Article> articlePage = articleRepository.findByStatusWithPagination(arriclePageDto.getStatus(), arriclePageDto.getTitle(), pageRequest);
|
||||||
|
return ResponseMessage.success(articlePage, "根据标题分页查询文章成功");
|
||||||
|
}
|
||||||
|
Page<Article> articlePage = articleRepository.findByStatusWithPagination(arriclePageDto.getStatus(), pageRequest);
|
||||||
|
return ResponseMessage.success(articlePage, "根据状态分页查询文章成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("根据状态分页查询文章列表失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("根据状态分页查询文章列表失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public ResponseMessage<List<Article>> getArticlesByTitle(String title) {
|
||||||
|
try {
|
||||||
|
if (title == null || title.isEmpty()) {
|
||||||
|
return ResponseMessage.badRequest("文章标题不能为空");
|
||||||
|
}
|
||||||
|
List<Article> articles = articleRepository.findByTitle(title);
|
||||||
|
return ResponseMessage.success(articles, "根据标题查询文章成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("根据标题查询文章列表失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("根据标题查询文章列表失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public ResponseMessage<List<Article>> getAllArticles() {
|
||||||
|
try {
|
||||||
|
List<Article> articles = articleRepository.findAll();
|
||||||
|
return ResponseMessage.success(articles, "获取文章列表成功");
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
log.error("获取文章列表失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("获取文章列表失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public ResponseMessage<Article> saveArticle(ArticleDto articleDto) {
|
||||||
|
try {
|
||||||
|
Article article = new Article();
|
||||||
|
BeanUtils.copyProperties(articleDto, article);
|
||||||
|
article.setCreatedAt(LocalDateTime.now());
|
||||||
|
article.setUpdatedAt(LocalDateTime.now());
|
||||||
|
article.setImg(articleDto.getImg() != null ? articleDto.getImg() : "");
|
||||||
|
article.setStatus(articleDto.getStatus() != null ? articleDto.getStatus() : 0);
|
||||||
|
|
||||||
|
Article savedArticle = articleRepository.save(article);
|
||||||
|
return ResponseMessage.save(true, savedArticle);
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
log.error("保存文章失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("保存文章失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public ResponseMessage<Article> updateArticle(Integer id, ArticleDto articleDto) {
|
||||||
|
try {
|
||||||
|
Article article = articleRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new RuntimeException("文章不存在"));
|
||||||
|
|
||||||
|
BeanUtils.copyProperties(articleDto, article);
|
||||||
|
article.setUpdatedAt(LocalDateTime.now());
|
||||||
|
|
||||||
|
Article updatedArticle = articleRepository.save(article);
|
||||||
|
return ResponseMessage.update(true, updatedArticle);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
if (e.getMessage().contains("文章不存在")) {
|
||||||
|
return ResponseMessage.notFound("文章不存在");
|
||||||
|
}
|
||||||
|
log.error("更新文章失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("更新文章失败");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("更新文章失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("更新文章失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public ResponseMessage<Article> deleteArticle(Integer id) {
|
||||||
|
try {
|
||||||
|
Article article = articleRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new RuntimeException("文章不存在"));
|
||||||
|
|
||||||
|
article.setStatus(2); // 标记为已删除
|
||||||
|
article.setUpdatedAt(LocalDateTime.now());
|
||||||
|
articleRepository.save(article);
|
||||||
|
|
||||||
|
return ResponseMessage.delete(true);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
if (e.getMessage().contains("文章不存在")) {
|
||||||
|
return ResponseMessage.notFound("文章不存在");
|
||||||
|
}
|
||||||
|
log.error("删除文章失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("删除文章失败");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("删除文章失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("删除文章失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public ResponseMessage<List<Article>> getArticlesByCategory(Integer categoryId) {
|
||||||
|
try {
|
||||||
|
// 可以考虑查询该分类下的所有属性,然后获取相关文章
|
||||||
|
log.warn("使用了旧接口getArticlesByCategory,请考虑迁移到getArticlesByAttribute");
|
||||||
|
return ResponseMessage.success(articleRepository.findPublishedByAttribute(categoryId), "获取分类文章成功");
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
log.error("获取分类文章失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("获取分类文章失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public ResponseMessage<List<Article>> getArticlesByAttribute(Integer attributeid) {
|
||||||
|
try {
|
||||||
|
if (attributeid == null || attributeid <= 0) {
|
||||||
|
return ResponseMessage.badRequest("属性ID无效");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证属性是否存在
|
||||||
|
if (!categoryAttributeRepository.existsById(attributeid)) {
|
||||||
|
return ResponseMessage.notFound("属性不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Article> articles = articleRepository.findPublishedByAttribute(attributeid);
|
||||||
|
return ResponseMessage.success(articles, "获取属性文章成功");
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
log.error("获取属性文章失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("获取属性文章失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增加文章浏览量
|
||||||
|
*
|
||||||
|
* @param id 文章ID
|
||||||
|
* @return 返回包含更新后文章信息的ResponseMessage对象
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public ResponseMessage<Article> incrementViewCount(Integer id) {
|
||||||
|
try {
|
||||||
|
Article article = articleRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new RuntimeException("文章不存在"));
|
||||||
|
|
||||||
|
articleRepository.incrementViewCount(id);
|
||||||
|
|
||||||
|
return ResponseMessage.success(article, "增加文章浏览量成功");
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
if (e.getMessage().contains("文章不存在")) {
|
||||||
|
return ResponseMessage.notFound("文章不存在");
|
||||||
|
}
|
||||||
|
log.error("增加文章浏览量失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("增加文章浏览量失败");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("增加文章浏览量失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("增加文章浏览量失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public ResponseMessage<List<Article>> getLatestArticlesByAttribute(Integer attributeid) {
|
||||||
|
try {
|
||||||
|
if (attributeid == null || attributeid <= 0) {
|
||||||
|
return ResponseMessage.badRequest("属性ID无效");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证属性是否存在
|
||||||
|
if (!categoryAttributeRepository.existsById(attributeid)) {
|
||||||
|
return ResponseMessage.notFound("属性不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Article> articles = articleRepository.findLatestByAttribute(attributeid);
|
||||||
|
return ResponseMessage.success(articles, "获取最新属性文章成功");
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
log.error("获取最新属性文章失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("获取最新属性文章失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public ResponseMessage<List<Article>> getMostViewedArticles() {
|
||||||
|
try {
|
||||||
|
List<Article> articles = articleRepository.findMostViewed();
|
||||||
|
return ResponseMessage.success(articles, "获取热门文章成功");
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
log.error("获取热门文章失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("获取热门文章失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,215 @@
|
|||||||
|
package com.qf.myafterprojecy.service.impl;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
|
||||||
|
import com.qf.myafterprojecy.pojo.Categoryattribute;
|
||||||
|
import com.qf.myafterprojecy.pojo.dto.CategoryAttributeDto;
|
||||||
|
import com.qf.myafterprojecy.repository.CategoryAttributeRepository;
|
||||||
|
import com.qf.myafterprojecy.service.ICategoryAttributeService;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.dao.DataAccessException;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class CategoryAttributeService implements ICategoryAttributeService {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(CategoryAttributeService.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CategoryAttributeRepository categoryAttributeRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取全部分类属性
|
||||||
|
* @return 所有分类属性列表
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public ResponseMessage<List<Categoryattribute>> getAllCategoryAttributes() {
|
||||||
|
try {
|
||||||
|
List<Categoryattribute> attributes = categoryAttributeRepository.findAll();
|
||||||
|
return ResponseMessage.success(attributes, "获取所有分类属性成功");
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
log.error("获取所有分类属性失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("获取所有分类属性失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据ID获取分类属性
|
||||||
|
* @param id 属性ID
|
||||||
|
* @return 分类属性信息
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public ResponseMessage<Categoryattribute> getCategoryAttributeById(Integer id) {
|
||||||
|
try {
|
||||||
|
if (id == null || id <= 0) {
|
||||||
|
return ResponseMessage.badRequest("属性ID无效");
|
||||||
|
}
|
||||||
|
|
||||||
|
Categoryattribute attribute = categoryAttributeRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new RuntimeException("分类属性不存在"));
|
||||||
|
|
||||||
|
return ResponseMessage.success(attribute, "获取分类属性成功");
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
if (e.getMessage().contains("分类属性不存在")) {
|
||||||
|
return ResponseMessage.notFound("分类属性不存在");
|
||||||
|
}
|
||||||
|
log.error("获取分类属性失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("获取分类属性失败");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取分类属性失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("获取分类属性失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据分类ID获取属性列表
|
||||||
|
* @param categoryId 分类ID
|
||||||
|
* @return 属性列表
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public ResponseMessage<List<Categoryattribute>> getAttributesByCategoryId(Integer categoryId) {
|
||||||
|
try {
|
||||||
|
if (categoryId == null || categoryId <= 0) {
|
||||||
|
return ResponseMessage.badRequest("分类ID无效");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Categoryattribute> attributes = categoryAttributeRepository.findByCategoryId(categoryId);
|
||||||
|
return ResponseMessage.success(attributes, "获取分类属性列表成功");
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
log.error("获取分类属性列表失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("获取分类属性列表失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存分类属性
|
||||||
|
* @param dto 分类属性DTO
|
||||||
|
* @return 保存结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public ResponseMessage<Categoryattribute> saveCategoryAttribute(CategoryAttributeDto dto) {
|
||||||
|
try {
|
||||||
|
// 检查属性名称是否已存在于该分类下
|
||||||
|
if (categoryAttributeRepository.existsByCategoryidAndAttributename(
|
||||||
|
dto.getCategoryid(), dto.getAttributename())) {
|
||||||
|
return ResponseMessage.badRequest("该分类下已存在同名属性");
|
||||||
|
}
|
||||||
|
|
||||||
|
Categoryattribute attribute = new Categoryattribute();
|
||||||
|
BeanUtils.copyProperties(dto, attribute);
|
||||||
|
|
||||||
|
Categoryattribute savedAttribute = categoryAttributeRepository.save(attribute);
|
||||||
|
log.info("成功创建分类属性: {}, 分类ID: {}",
|
||||||
|
savedAttribute.getAttributename(), savedAttribute.getCategoryid());
|
||||||
|
|
||||||
|
return ResponseMessage.save(true, savedAttribute);
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
log.error("保存分类属性失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("保存分类属性失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新分类属性
|
||||||
|
* @param id 属性ID
|
||||||
|
* @param dto 分类属性DTO
|
||||||
|
* @return 更新结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public ResponseMessage<Categoryattribute> updateCategoryAttribute(Integer id, CategoryAttributeDto dto) {
|
||||||
|
try {
|
||||||
|
if (id == null || id <= 0) {
|
||||||
|
return ResponseMessage.badRequest("属性ID无效");
|
||||||
|
}
|
||||||
|
|
||||||
|
Categoryattribute attribute = categoryAttributeRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new RuntimeException("分类属性不存在"));
|
||||||
|
|
||||||
|
// 如果修改了属性名称,检查新名称是否已存在
|
||||||
|
if (!attribute.getAttributename().equals(dto.getAttributename()) &&
|
||||||
|
categoryAttributeRepository.existsByCategoryidAndAttributename(
|
||||||
|
dto.getCategoryid(), dto.getAttributename())) {
|
||||||
|
return ResponseMessage.badRequest("该分类下已存在同名属性");
|
||||||
|
}
|
||||||
|
|
||||||
|
BeanUtils.copyProperties(dto, attribute);
|
||||||
|
attribute.setAttributeid(id); // 确保ID不变
|
||||||
|
|
||||||
|
Categoryattribute updatedAttribute = categoryAttributeRepository.save(attribute);
|
||||||
|
log.info("成功更新分类属性: ID={}, 名称={}",
|
||||||
|
updatedAttribute.getAttributeid(), updatedAttribute.getAttributename());
|
||||||
|
|
||||||
|
return ResponseMessage.update(true, updatedAttribute);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
if (e.getMessage().contains("分类属性不存在")) {
|
||||||
|
return ResponseMessage.notFound("分类属性不存在");
|
||||||
|
}
|
||||||
|
log.error("更新分类属性失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("更新分类属性失败");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("更新分类属性失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("更新分类属性失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除分类属性
|
||||||
|
* @param id 属性ID
|
||||||
|
* @return 删除结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public ResponseMessage<Boolean> deleteCategoryAttribute(Integer id) {
|
||||||
|
try {
|
||||||
|
if (id == null || id <= 0) {
|
||||||
|
return ResponseMessage.badRequest("属性ID无效");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!categoryAttributeRepository.existsById(id)) {
|
||||||
|
return ResponseMessage.notFound("分类属性不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
categoryAttributeRepository.deleteById(id);
|
||||||
|
log.info("成功删除分类属性: ID={}", id);
|
||||||
|
|
||||||
|
return ResponseMessage.delete(true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("删除分类属性失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("删除分类属性失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 检查分类属性是否存在
|
||||||
|
* @param categoryId 分类ID
|
||||||
|
* @param attributeName 属性名称
|
||||||
|
* @return 是否存在
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public ResponseMessage<Boolean> existsByCategoryAndName(Integer categoryId, String attributeName) {
|
||||||
|
try {
|
||||||
|
if (categoryId == null || categoryId <= 0 || attributeName == null || attributeName.isEmpty()) {
|
||||||
|
return ResponseMessage.badRequest("参数无效");
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean exists = categoryAttributeRepository.existsByCategoryidAndAttributename(
|
||||||
|
categoryId, attributeName);
|
||||||
|
|
||||||
|
return ResponseMessage.success(exists, "检查分类属性成功");
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
log.error("检查分类属性是否存在失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("检查分类属性是否存在失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
package com.qf.myafterprojecy.service.impl;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
|
||||||
|
import com.qf.myafterprojecy.pojo.Category;
|
||||||
|
import com.qf.myafterprojecy.pojo.dto.CategoryDto;
|
||||||
|
import com.qf.myafterprojecy.repository.CategoryRepository;
|
||||||
|
import com.qf.myafterprojecy.service.ICategoryService;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.dao.DataAccessException;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class CategoryService implements ICategoryService {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(CategoryService.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CategoryRepository categoryRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public ResponseMessage<Category> getCategoryById(Integer id) {
|
||||||
|
try {
|
||||||
|
if (id == null || id <= 0) {
|
||||||
|
return ResponseMessage.badRequest("分类ID无效");
|
||||||
|
}
|
||||||
|
Category category = categoryRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new RuntimeException("分类不存在"));
|
||||||
|
return ResponseMessage.success(category, "获取分类成功");
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
if (e.getMessage().contains("分类不存在")) {
|
||||||
|
return ResponseMessage.notFound("分类不存在");
|
||||||
|
}
|
||||||
|
log.error("获取分类失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("获取分类失败");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("获取分类失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("获取分类失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public ResponseMessage<List<Category>> getAllCategories() {
|
||||||
|
try {
|
||||||
|
List<Category> categories = categoryRepository.findAll();
|
||||||
|
return ResponseMessage.success(categories, "获取分类列表成功");
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
log.error("获取分类列表失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("获取分类列表失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public ResponseMessage<Category> saveCategory(CategoryDto categoryDto) {
|
||||||
|
try {
|
||||||
|
// 检查分类名称是否已存在
|
||||||
|
if (categoryRepository.existsByTypename(categoryDto.getTypename())) {
|
||||||
|
return ResponseMessage.badRequest( "分类名称已存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
Category category = new Category();
|
||||||
|
BeanUtils.copyProperties(categoryDto, category);
|
||||||
|
category.setCreatedAt(LocalDateTime.now());
|
||||||
|
category.setUpdatedAt(LocalDateTime.now());
|
||||||
|
|
||||||
|
Category savedCategory = categoryRepository.save(category);
|
||||||
|
return ResponseMessage.save(true, savedCategory);
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
log.error("保存分类失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("保存分类失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public ResponseMessage<Category> updateCategory(Integer id, CategoryDto categoryDto) {
|
||||||
|
try {
|
||||||
|
if (id == null || id <= 0) {
|
||||||
|
return ResponseMessage.badRequest("分类ID无效");
|
||||||
|
}
|
||||||
|
|
||||||
|
Category category = categoryRepository.findById(id)
|
||||||
|
.orElseThrow(() -> new RuntimeException("分类不存在"));
|
||||||
|
|
||||||
|
// 如果修改了分类名称,检查新名称是否已存在
|
||||||
|
if (!category.getTypename().equals(categoryDto.getTypename()) &&
|
||||||
|
categoryRepository.existsByTypename(categoryDto.getTypename())) {
|
||||||
|
return ResponseMessage.badRequest("分类名称已存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
BeanUtils.copyProperties(categoryDto, category);
|
||||||
|
category.setUpdatedAt(LocalDateTime.now());
|
||||||
|
|
||||||
|
Category updatedCategory = categoryRepository.save(category);
|
||||||
|
return ResponseMessage.update(true, updatedCategory);
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
if (e.getMessage().contains("分类不存在")) {
|
||||||
|
return ResponseMessage.notFound("分类不存在");
|
||||||
|
}
|
||||||
|
log.error("更新分类失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("更新分类失败");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("更新分类失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("更新分类失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public ResponseMessage<Boolean> deleteCategory(Integer id) {
|
||||||
|
try {
|
||||||
|
if (id == null || id <= 0) {
|
||||||
|
return ResponseMessage.badRequest("分类ID无效");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!categoryRepository.existsById(id)) {
|
||||||
|
return ResponseMessage.notFound("分类不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注意:实际项目中可能需要先检查是否有文章引用该分类
|
||||||
|
// 如果有,可能需要先处理文章或者禁止删除
|
||||||
|
categoryRepository.deleteById(id);
|
||||||
|
return ResponseMessage.delete(true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("删除分类失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("删除分类失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public ResponseMessage<List<Category>> searchCategoriesByTypename(String typename) {
|
||||||
|
try {
|
||||||
|
if (typename == null || typename.trim().isEmpty()) {
|
||||||
|
return ResponseMessage.badRequest("分类名称不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Category> categories = categoryRepository.findByTypenameContaining(typename);
|
||||||
|
return ResponseMessage.success(categories, "搜索分类成功");
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
log.error("搜索分类失败: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("搜索分类失败");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,286 @@
|
|||||||
|
package com.qf.myafterprojecy.service.impl;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
|
||||||
|
import com.qf.myafterprojecy.pojo.Message;
|
||||||
|
import com.qf.myafterprojecy.pojo.dto.MessageDto;
|
||||||
|
import com.qf.myafterprojecy.pojo.dto.MessagePageDto;
|
||||||
|
import com.qf.myafterprojecy.repository.MessageRepository;
|
||||||
|
import com.qf.myafterprojecy.service.IMessageService;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.data.web.SpringDataWebProperties.Pageable;
|
||||||
|
import org.springframework.dao.DataAccessException;
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class MessageService implements IMessageService {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(MessageService.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private MessageRepository messageRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseMessage<Iterable<Message>> getAllMessages() {
|
||||||
|
try {
|
||||||
|
logger.info("查询所有消息");
|
||||||
|
Iterable<Message> messages = messageRepository.findAll();
|
||||||
|
return ResponseMessage.success(messages, "查询成功");
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("查询所有消息失败", e);
|
||||||
|
return ResponseMessage.error("查询消息失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseMessage<Message> getMessageById(Integer id) {
|
||||||
|
if (id == null || id <= 0) {
|
||||||
|
logger.warn("获取消息时ID无效: {}", id);
|
||||||
|
return ResponseMessage.badRequest("消息ID无效");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
logger.info("根据ID查询消息: {}", id);
|
||||||
|
Optional<Message> messageOptional = messageRepository.findById(id);
|
||||||
|
if (messageOptional.isPresent()) {
|
||||||
|
return ResponseMessage.success(messageOptional.get(), "查询成功");
|
||||||
|
} else {
|
||||||
|
logger.warn("未找到ID为{}的消息", id);
|
||||||
|
return ResponseMessage.notFound("未找到指定消息");
|
||||||
|
}
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("查询消息失败: {}", id, e);
|
||||||
|
return ResponseMessage.error("查询消息失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public ResponseMessage<Message> saveMessage(MessageDto messageDto) {
|
||||||
|
// 参数校验
|
||||||
|
if (messageDto == null) {
|
||||||
|
logger.warn("保存消息时参数为空");
|
||||||
|
throw new IllegalArgumentException("MessageDto cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 业务逻辑校验
|
||||||
|
if (StringUtils.isEmpty(messageDto.getContent())) {
|
||||||
|
logger.warn("保存消息时内容为空");
|
||||||
|
throw new IllegalArgumentException("Message content cannot be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isEmpty(messageDto.getNickname())) {
|
||||||
|
logger.warn("保存消息时昵称为空");
|
||||||
|
throw new IllegalArgumentException("Message nickname cannot be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用Repository保存数据
|
||||||
|
try {
|
||||||
|
logger.info("保存消息: {}", messageDto.getNickname());
|
||||||
|
Message message = new Message();
|
||||||
|
BeanUtils.copyProperties(messageDto, message);
|
||||||
|
message.setCreatedAt(new Date()); // 设置创建时间
|
||||||
|
message.setLikes(0); // 设置点赞数初始值为0
|
||||||
|
|
||||||
|
// 如果是回复,确保parentid有效
|
||||||
|
if (messageDto.getParentid() != null && messageDto.getParentid() > 0) {
|
||||||
|
if (!messageRepository.existsById(messageDto.getParentid())) {
|
||||||
|
logger.warn("回复的父消息不存在: {}", messageDto.getParentid());
|
||||||
|
return ResponseMessage.notFound("回复的父消息不存在");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Message savedMessage = messageRepository.save(message);
|
||||||
|
logger.info("消息保存成功: {}", savedMessage.getMessageid());
|
||||||
|
return ResponseMessage.save(true, savedMessage);
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("保存消息失败", e);
|
||||||
|
return ResponseMessage.error("保存消息失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public ResponseMessage<Message> deleteMessage(Integer id) {
|
||||||
|
if (id == null || id <= 0) {
|
||||||
|
logger.warn("删除消息时ID无效: {}", id);
|
||||||
|
return ResponseMessage.badRequest("消息ID无效");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
logger.info("删除消息: {}", id);
|
||||||
|
if (messageRepository.existsById(id)) {
|
||||||
|
messageRepository.deleteById(id);
|
||||||
|
// 同时删除该消息的所有回复
|
||||||
|
List<Message> replies = messageRepository.findByParentid(id);
|
||||||
|
if (replies != null && !replies.isEmpty()) {
|
||||||
|
messageRepository.deleteAll(replies);
|
||||||
|
logger.info("同时删除了{}条回复消息", replies.size());
|
||||||
|
}
|
||||||
|
logger.info("消息删除成功: {}", id);
|
||||||
|
return ResponseMessage.delete(true);
|
||||||
|
} else {
|
||||||
|
logger.warn("未找到要删除的消息: {}", id);
|
||||||
|
return ResponseMessage.notFound("未找到要删除的消息");
|
||||||
|
}
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("删除消息失败: {}", id, e);
|
||||||
|
return ResponseMessage.error("删除消息失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseMessage<List<Message>> getMessagesByArticleId(Integer articleId) {
|
||||||
|
if (articleId == null || articleId <= 0) {
|
||||||
|
logger.warn("根据文章ID查询消息时ID无效: {}", articleId);
|
||||||
|
return ResponseMessage.badRequest("文章ID无效");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
logger.info("根据文章ID查询消息: {}", articleId);
|
||||||
|
List<Message> messages = messageRepository.findByArticleid(articleId);
|
||||||
|
return ResponseMessage.success(messages, "查询成功");
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("根据文章ID查询消息失败: {}", articleId, e);
|
||||||
|
return ResponseMessage.error("查询消息失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseMessage<List<Message>> getRootMessages() {
|
||||||
|
try {
|
||||||
|
logger.info("查询所有根消息");
|
||||||
|
List<Message> messages = messageRepository.findByParentidIsNull();
|
||||||
|
return ResponseMessage.success(messages, "查询成功");
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("查询根消息失败", e);
|
||||||
|
return ResponseMessage.error("查询消息失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseMessage<List<Message>> getRepliesByParentId(Integer parentId) {
|
||||||
|
if (parentId == null || parentId <= 0) {
|
||||||
|
logger.warn("根据父消息ID查询回复时ID无效: {}", parentId);
|
||||||
|
return ResponseMessage.badRequest("父消息ID无效");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
logger.info("根据父消息ID查询回复: {}", parentId);
|
||||||
|
List<Message> replies = messageRepository.findByParentid(parentId);
|
||||||
|
return ResponseMessage.success(replies, "查询成功");
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("查询回复消息失败: {}", parentId, e);
|
||||||
|
return ResponseMessage.error("查询消息失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点赞数增加
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public ResponseMessage<Message> likeMessage(Integer id) {
|
||||||
|
if (id == null || id <= 0) {
|
||||||
|
logger.warn("点赞消息时ID无效: {}", id);
|
||||||
|
return ResponseMessage.badRequest("消息ID无效");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
logger.info("点赞消息: {}", id);
|
||||||
|
messageRepository.incrementLikes(id);
|
||||||
|
Message likedMessage = messageRepository.findById(id).orElse(null);
|
||||||
|
return ResponseMessage.success(likedMessage, "点赞成功");
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("点赞消息失败: {}", id, e);
|
||||||
|
return ResponseMessage.error("点赞消息失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseMessage<List<Message>> searchMessagesByNickname(String nickname) {
|
||||||
|
if (StringUtils.isEmpty(nickname)) {
|
||||||
|
logger.warn("根据昵称查询消息时昵称为空");
|
||||||
|
return ResponseMessage.badRequest("昵称不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
logger.info("根据昵称查询消息: {}", nickname);
|
||||||
|
List<Message> messages = messageRepository.findByNicknameContaining(nickname);
|
||||||
|
return ResponseMessage.success(messages, "查询成功");
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("根据昵称查询消息失败: {}", nickname, e);
|
||||||
|
return ResponseMessage.error("查询消息失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除所有评论
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public ResponseMessage<Void> deleteAllMessages() {
|
||||||
|
try {
|
||||||
|
logger.info("删除所有消息");
|
||||||
|
messageRepository.deleteAll();
|
||||||
|
return ResponseMessage.delete(true);
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("删除所有消息失败", e);
|
||||||
|
return ResponseMessage.error("删除消息失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseMessage<List<Message>> getMessagesByPage(MessagePageDto messagePageDto) {
|
||||||
|
if (messagePageDto == null) {
|
||||||
|
logger.warn("分页查询消息时参数为空");
|
||||||
|
return ResponseMessage.badRequest("分页参数不能为空");
|
||||||
|
}
|
||||||
|
if (messagePageDto.getPageNum() == null) {
|
||||||
|
logger.warn("分页查询消息时页码无效: {}", messagePageDto.getPageNum());
|
||||||
|
return ResponseMessage.badRequest("页码无效");
|
||||||
|
}
|
||||||
|
if (messagePageDto.getPageSize() == null || messagePageDto.getPageSize() <= 0) {
|
||||||
|
logger.warn("分页查询消息时每页数量无效: {}", messagePageDto.getPageSize());
|
||||||
|
return ResponseMessage.badRequest("每页数量无效");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// 如何文章id为空,默认根据分页基础信息查询消息
|
||||||
|
PageRequest pageable = PageRequest.of(messagePageDto.getPageNum(), messagePageDto.getPageSize());
|
||||||
|
if (messagePageDto.getArticleid() != null && messagePageDto.getArticleid() > 0) {
|
||||||
|
// 如果文章ID存在,根据文章ID查询消息
|
||||||
|
Page<Message> messagePage = messageRepository.findByArticleId(messagePageDto.getArticleid(), pageable);
|
||||||
|
return ResponseMessage.success(messagePage.getContent(), "查询成功");
|
||||||
|
}
|
||||||
|
// 如果文章ID不存在,根据分页基础信息查询所有消息
|
||||||
|
Page<Message> messagePage = messageRepository.findAllMessages(pageable);
|
||||||
|
return ResponseMessage.success(messagePage.getContent(), "查询成功");
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("分页查询消息失败: {}", messagePageDto, e);
|
||||||
|
return ResponseMessage.error("查询消息失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 获取回复消息条数 如果id为空获取文章id为空的消息条数
|
||||||
|
@Override
|
||||||
|
public ResponseMessage<Integer> getMessageCountByArticleId(Integer articleId) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
logger.info("获取文章回复数量: {}", articleId);
|
||||||
|
if (articleId == null || articleId <= 0) {
|
||||||
|
Integer count = messageRepository.countReplyByArticleIdIsNull();
|
||||||
|
return ResponseMessage.success(count, "查询成功");
|
||||||
|
}
|
||||||
|
Integer count = messageRepository.countReplyByArticleId(articleId);
|
||||||
|
return ResponseMessage.success(count, "查询成功");
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("获取文章回复数量失败: {}", articleId, e);
|
||||||
|
return ResponseMessage.error("查询回复数量失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,202 @@
|
|||||||
|
package com.qf.myafterprojecy.service.impl;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
|
||||||
|
import com.qf.myafterprojecy.pojo.Nonsense;
|
||||||
|
import com.qf.myafterprojecy.pojo.dto.NonsenseDto;
|
||||||
|
import com.qf.myafterprojecy.repository.NonsenseRepository;
|
||||||
|
import com.qf.myafterprojecy.service.INonsenseService;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.dao.DataAccessException;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class NonsenseService implements INonsenseService {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(NonsenseService.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private NonsenseRepository nonsenseRepository;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseMessage<List<Nonsense>> getAllNonsense() {
|
||||||
|
try {
|
||||||
|
// 获取所有疯言疯语内容,但在API层面只返回已发表(1)的内容
|
||||||
|
List<Nonsense> allNonsense = nonsenseRepository.findAll();
|
||||||
|
|
||||||
|
return new ResponseMessage<>(200, "获取成功", allNonsense, true);
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("获取所有疯言疯语内容失败", e);
|
||||||
|
return new ResponseMessage<>(500, "数据库查询异常", null, false);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("获取所有疯言疯语内容失败", e);
|
||||||
|
return new ResponseMessage<>(500, "服务器内部错误", null, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseMessage<Nonsense> getNonsenseById(Integer id) {
|
||||||
|
try {
|
||||||
|
Optional<Nonsense> nonsenseOptional = nonsenseRepository.findById(id);
|
||||||
|
if (nonsenseOptional.isPresent()) {
|
||||||
|
Nonsense nonsense = nonsenseOptional.get();
|
||||||
|
logger.info("获取ID为{}的疯言疯语内容成功,状态: {}", id, nonsense.getStatus());
|
||||||
|
return new ResponseMessage<>(200, "获取成功", nonsense, true);
|
||||||
|
} else {
|
||||||
|
logger.warn("未找到ID为{}的疯言疯语内容", id);
|
||||||
|
return new ResponseMessage<>(404, "未找到指定疯言疯语内容", null, false);
|
||||||
|
}
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("根据ID查询疯言疯语内容失败,ID: {}", id, e);
|
||||||
|
return new ResponseMessage<>(500, "数据库查询异常", null, false);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("根据ID查询疯言疯语内容失败,ID: {}", id, e);
|
||||||
|
return new ResponseMessage<>(500, "服务器内部错误", null, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseMessage<List<Nonsense>> getNonsenseByStatus(Integer status) {
|
||||||
|
try {
|
||||||
|
// 验证状态值是否有效
|
||||||
|
if (status < 0 || status > 2) {
|
||||||
|
logger.warn("无效的状态值: {}", status);
|
||||||
|
return new ResponseMessage<>(400, "无效的状态值,必须是0(未发表)、1(已发表)或2(已删除)", null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Nonsense> nonsenseList = nonsenseRepository.findByStatus(status);
|
||||||
|
// 根据状态过滤
|
||||||
|
return new ResponseMessage<>(200, "获取成功", nonsenseList, true);
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("根据状态获取疯言疯语内容失败,状态: {}", status, e);
|
||||||
|
return new ResponseMessage<>(500, "数据库查询异常", null, false);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("根据状态获取疯言疯语内容失败,状态: {}", status, e);
|
||||||
|
return new ResponseMessage<>(500, "服务器内部错误", null, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public ResponseMessage<Nonsense> updateNonsenseStatus(Integer id, Integer status) {
|
||||||
|
try {
|
||||||
|
// 验证状态值是否有效
|
||||||
|
if (status < 0 || status > 2) {
|
||||||
|
logger.warn("无效的状态值: {} 用于ID为{}的疯言疯语内容", status, id);
|
||||||
|
return new ResponseMessage<>(400, "无效的状态值,必须是0(未发表)、1(已发表)或2(已删除)", null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
Optional<Nonsense> nonsenseOptional = nonsenseRepository.findById(id);
|
||||||
|
if (nonsenseOptional.isPresent()) {
|
||||||
|
Nonsense nonsense = nonsenseOptional.get();
|
||||||
|
Integer oldStatus = nonsense.getStatus();
|
||||||
|
nonsense.setStatus(status);
|
||||||
|
|
||||||
|
Nonsense updatedNonsense = nonsenseRepository.save(nonsense);
|
||||||
|
logger.info("更新疯言疯语内容状态成功,ID: {}, 旧状态: {}, 新状态: {}", id, oldStatus, status);
|
||||||
|
return new ResponseMessage<>(200, "状态更新成功", updatedNonsense, true);
|
||||||
|
} else {
|
||||||
|
logger.warn("更新状态失败,未找到ID为{}的疯言疯语内容", id);
|
||||||
|
return new ResponseMessage<>(404, "未找到指定疯言疯语内容", null, false);
|
||||||
|
}
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("更新疯言疯语内容状态失败,ID: {}, 状态: {}", id, status, e);
|
||||||
|
return new ResponseMessage<>(500, "数据库操作异常", null, false);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("更新疯言疯语内容状态失败,ID: {}, 状态: {}", id, status, e);
|
||||||
|
return new ResponseMessage<>(500, "服务器内部错误", null, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public ResponseMessage<Nonsense> saveNonsense(NonsenseDto nonsenseDto) {
|
||||||
|
try {
|
||||||
|
Nonsense nonsense = new Nonsense();
|
||||||
|
BeanUtils.copyProperties(nonsenseDto, nonsense);
|
||||||
|
|
||||||
|
// 设置创建时间
|
||||||
|
if (nonsense.getTime() == null) {
|
||||||
|
nonsense.setTime(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置默认状态为未发表(0),如果DTO中未提供状态值
|
||||||
|
if (nonsense.getStatus() == null) {
|
||||||
|
nonsense.setStatus(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Nonsense savedNonsense = nonsenseRepository.save(nonsense);
|
||||||
|
logger.info("保存疯言疯语内容成功,ID: {}, 状态: {}", savedNonsense.getId(), savedNonsense.getStatus());
|
||||||
|
return new ResponseMessage<>(200, "保存成功", savedNonsense, true);
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("保存疯言疯语内容失败", e);
|
||||||
|
return new ResponseMessage<>(500, "数据库操作异常", null, false);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("保存疯言疯语内容失败", e);
|
||||||
|
return new ResponseMessage<>(500, "服务器内部错误", null, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public ResponseMessage<Nonsense> updateNonsense(Integer id, NonsenseDto nonsenseDto) {
|
||||||
|
try {
|
||||||
|
Optional<Nonsense> nonsenseOptional = nonsenseRepository.findById(id);
|
||||||
|
if (nonsenseOptional.isPresent()) {
|
||||||
|
Nonsense nonsense = nonsenseOptional.get();
|
||||||
|
// 只有当DTO中提供了status值时才更新
|
||||||
|
if (nonsenseDto.getStatus() != null) {
|
||||||
|
logger.info("更新疯言疯语内容状态,ID: {}, 新状态: {}", id, nonsenseDto.getStatus());
|
||||||
|
}
|
||||||
|
BeanUtils.copyProperties(nonsenseDto, nonsense, "id");
|
||||||
|
|
||||||
|
Nonsense updatedNonsense = nonsenseRepository.save(nonsense);
|
||||||
|
logger.info("更新疯言疯语内容成功,ID: {}, 当前状态: {}", id, updatedNonsense.getStatus());
|
||||||
|
return new ResponseMessage<>(200, "更新成功", updatedNonsense, true);
|
||||||
|
} else {
|
||||||
|
logger.warn("更新失败,未找到ID为{}的疯言疯语内容", id);
|
||||||
|
return new ResponseMessage<>(404, "未找到指定疯言疯语内容", null, false);
|
||||||
|
}
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("更新疯言疯语内容失败,ID: {}", id, e);
|
||||||
|
return new ResponseMessage<>(500, "数据库操作异常", null, false);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("更新疯言疯语内容失败,ID: {}", id, e);
|
||||||
|
return new ResponseMessage<>(500, "服务器内部错误", null, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public ResponseMessage<Boolean> deleteNonsense(Integer id) {
|
||||||
|
try {
|
||||||
|
if (nonsenseRepository.existsById(id)) {
|
||||||
|
// 先将状态设置为已删除(2)
|
||||||
|
Nonsense nonsense = nonsenseRepository.findById(id).get();
|
||||||
|
nonsense.setStatus(2);
|
||||||
|
nonsenseRepository.save(nonsense);
|
||||||
|
|
||||||
|
// 物理删除
|
||||||
|
// nonsenseRepository.deleteById(id);
|
||||||
|
logger.info("删除疯言疯语内容成功,ID: {}", id);
|
||||||
|
return new ResponseMessage<>(200, "删除成功", true, true);
|
||||||
|
} else {
|
||||||
|
logger.warn("删除失败,未找到ID为{}的疯言疯语内容", id);
|
||||||
|
return new ResponseMessage<>(404, "未找到指定疯言疯语内容", false, false);
|
||||||
|
}
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("删除疯言疯语内容失败,ID: {}", id, e);
|
||||||
|
return new ResponseMessage<>(500, "数据库操作异常", false, false);
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("删除疯言疯语内容失败,ID: {}", id, e);
|
||||||
|
return new ResponseMessage<>(500, "服务器内部错误", false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,239 @@
|
|||||||
|
package com.qf.myafterprojecy.service.impl;
|
||||||
|
|
||||||
|
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
|
||||||
|
import com.qf.myafterprojecy.pojo.Users;
|
||||||
|
import com.qf.myafterprojecy.pojo.dto.UserDto;
|
||||||
|
import com.qf.myafterprojecy.repository.UsersRepository;
|
||||||
|
import com.qf.myafterprojecy.service.IUserService;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.dao.DataAccessException;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class UserService implements IUserService {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UsersRepository usersRepository;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private PasswordEncoder passwordEncoder;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseMessage<Users> getUserById(Long id) {
|
||||||
|
try {
|
||||||
|
Optional<Users> userOptional = usersRepository.findById(id);
|
||||||
|
if (userOptional.isPresent()) {
|
||||||
|
return ResponseMessage.success(userOptional.get(), "获取用户成功");
|
||||||
|
} else {
|
||||||
|
return ResponseMessage.notFound("用户不存在");
|
||||||
|
}
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("获取用户异常: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("数据库访问异常");
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("获取用户未知异常: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("服务器异常");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseMessage<List<Users>> getAllUsers() {
|
||||||
|
try {
|
||||||
|
List<Users> users = usersRepository.findAll();
|
||||||
|
return ResponseMessage.success(users, "获取所有用户成功");
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("获取所有用户异常: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("数据库访问异常");
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("获取所有用户未知异常: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("服务器异常");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseMessage<Users> getUserByUsername(String username) {
|
||||||
|
try {
|
||||||
|
Optional<Users> userOptional = usersRepository.findByUsername(username);
|
||||||
|
if (userOptional.isPresent()) {
|
||||||
|
return ResponseMessage.success(userOptional.get(), "获取用户成功");
|
||||||
|
} else {
|
||||||
|
return ResponseMessage.notFound("用户不存在");
|
||||||
|
}
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("根据用户名获取用户异常: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("数据库访问异常");
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("根据用户名获取用户未知异常: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("服务器异常");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public ResponseMessage<Users> saveUser(UserDto userDto) {
|
||||||
|
try {
|
||||||
|
// 检查用户名是否已存在
|
||||||
|
if (usersRepository.existsByUsername(userDto.getUsername())) {
|
||||||
|
return ResponseMessage.badRequest("用户名已存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查邮箱是否已存在
|
||||||
|
if (usersRepository.existsByEmail(userDto.getEmail())) {
|
||||||
|
return ResponseMessage.badRequest("邮箱已存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查手机号是否已存在
|
||||||
|
if (usersRepository.existsByPhone(userDto.getPhone())) {
|
||||||
|
return ResponseMessage.badRequest("手机号已存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建新用户
|
||||||
|
Users user = new Users();
|
||||||
|
BeanUtils.copyProperties(userDto, user);
|
||||||
|
// 使用密码编码器加密密码
|
||||||
|
user.setPassword(passwordEncoder.encode(user.getPassword()));
|
||||||
|
user.setCreateTime(LocalDateTime.now());
|
||||||
|
|
||||||
|
// 保存用户
|
||||||
|
Users savedUser = usersRepository.save(user);
|
||||||
|
return ResponseMessage.save(true, savedUser);
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("保存用户异常: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("数据库访问异常");
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("保存用户未知异常: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("服务器异常");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public ResponseMessage<Users> updateUser(Long id, UserDto userDto) {
|
||||||
|
try {
|
||||||
|
Optional<Users> userOptional = usersRepository.findById(id);
|
||||||
|
if (!userOptional.isPresent()) {
|
||||||
|
return ResponseMessage.notFound("用户不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
Users user = userOptional.get();
|
||||||
|
|
||||||
|
// 检查用户名是否被其他用户使用
|
||||||
|
if (!user.getUsername().equals(userDto.getUsername()) && usersRepository.existsByUsername(userDto.getUsername())) {
|
||||||
|
return ResponseMessage.badRequest("用户名已存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查邮箱是否被其他用户使用
|
||||||
|
if (!user.getEmail().equals(userDto.getEmail()) && usersRepository.existsByEmail(userDto.getEmail())) {
|
||||||
|
return ResponseMessage.badRequest("邮箱已存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查手机号是否被其他用户使用
|
||||||
|
if (!user.getPhone().equals(userDto.getPhone()) && usersRepository.existsByPhone(userDto.getPhone())) {
|
||||||
|
return ResponseMessage.badRequest("手机号已存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新用户信息
|
||||||
|
BeanUtils.copyProperties(userDto, user);
|
||||||
|
|
||||||
|
// 如果提供了新密码,则进行加密
|
||||||
|
if (userDto.getPassword() != null && !userDto.getPassword().isEmpty()) {
|
||||||
|
user.setPassword(passwordEncoder.encode(user.getPassword()));
|
||||||
|
}
|
||||||
|
Users updatedUser = usersRepository.save(user);
|
||||||
|
return ResponseMessage.update(true, updatedUser);
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("更新用户异常: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("数据库访问异常");
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("更新用户未知异常: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("服务器异常");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public ResponseMessage<Boolean> deleteUser(Long id) {
|
||||||
|
try {
|
||||||
|
if (!usersRepository.existsById(id)) {
|
||||||
|
return ResponseMessage.notFound("用户不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
usersRepository.deleteById(id);
|
||||||
|
return ResponseMessage.delete(true);
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("删除用户异常: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("数据库访问异常");
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("删除用户未知异常: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("服务器异常");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseMessage<List<Users>> getUsersByRole(int role) {
|
||||||
|
try {
|
||||||
|
List<Users> users = usersRepository.findByRole(role);
|
||||||
|
return ResponseMessage.success(users, "根据角色获取用户成功");
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("根据角色获取用户异常: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("数据库访问异常");
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("根据角色获取用户未知异常: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("服务器异常");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseMessage<Boolean> existsByUsername(String username) {
|
||||||
|
try {
|
||||||
|
boolean exists = usersRepository.existsByUsername(username);
|
||||||
|
return ResponseMessage.success(exists, "查询成功");
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("检查用户名存在性异常: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("数据库访问异常");
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("检查用户名存在性未知异常: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("服务器异常");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseMessage<Boolean> existsByEmail(String email) {
|
||||||
|
try {
|
||||||
|
boolean exists = usersRepository.existsByEmail(email);
|
||||||
|
return ResponseMessage.success(exists, "查询成功");
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("检查邮箱存在性异常: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("数据库访问异常");
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("检查邮箱存在性未知异常: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("服务器异常");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseMessage<Boolean> existsByPhone(String phone) {
|
||||||
|
try {
|
||||||
|
boolean exists = usersRepository.existsByPhone(phone);
|
||||||
|
return ResponseMessage.success(exists, "查询成功");
|
||||||
|
} catch (DataAccessException e) {
|
||||||
|
logger.error("检查手机号存在性异常: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("数据库访问异常");
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("检查手机号存在性未知异常: {}", e.getMessage());
|
||||||
|
return ResponseMessage.error("服务器异常");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
103
src/main/java/com/qf/myafterprojecy/utils/JwtUtils.java
Normal file
103
src/main/java/com/qf/myafterprojecy/utils/JwtUtils.java
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package com.qf.myafterprojecy.utils;
|
||||||
|
|
||||||
|
import io.jsonwebtoken.Claims;
|
||||||
|
import io.jsonwebtoken.Jwts;
|
||||||
|
import io.jsonwebtoken.SignatureAlgorithm;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JWT工具类,用于生成和验证token
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class JwtUtils {
|
||||||
|
|
||||||
|
@Value("${jwt.secret:default_secret_key_for_development}")
|
||||||
|
private String secret;
|
||||||
|
|
||||||
|
@Value("${jwt.expiration:86400000}")
|
||||||
|
private long expiration;
|
||||||
|
|
||||||
|
@Value("${jwt.token-prefix:Bearer}")
|
||||||
|
private String tokenPrefix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从token中获取用户名
|
||||||
|
*/
|
||||||
|
public String getUsernameFromToken(String token) {
|
||||||
|
return getClaimFromToken(token, Claims::getSubject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从token中获取过期时间
|
||||||
|
*/
|
||||||
|
public Date getExpirationDateFromToken(String token) {
|
||||||
|
return getClaimFromToken(token, Claims::getExpiration);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从token中获取指定的claim
|
||||||
|
*/
|
||||||
|
public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
|
||||||
|
final Claims claims = getAllClaimsFromToken(token);
|
||||||
|
return claimsResolver.apply(claims);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取token中的所有claims
|
||||||
|
*/
|
||||||
|
private Claims getAllClaimsFromToken(String token) {
|
||||||
|
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查token是否过期
|
||||||
|
*/
|
||||||
|
private Boolean isTokenExpired(String token) {
|
||||||
|
final Date expiration = getExpirationDateFromToken(token);
|
||||||
|
return expiration.before(new Date());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成token
|
||||||
|
*/
|
||||||
|
public String generateToken(UserDetails userDetails) {
|
||||||
|
Map<String, Object> claims = new HashMap<>();
|
||||||
|
claims.put("authorities", userDetails.getAuthorities());
|
||||||
|
return doGenerateToken(claims, userDetails.getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成token的核心方法
|
||||||
|
*/
|
||||||
|
private String doGenerateToken(Map<String, Object> claims, String subject) {
|
||||||
|
return Jwts.builder()
|
||||||
|
.setClaims(claims)
|
||||||
|
.setSubject(subject)
|
||||||
|
.setIssuedAt(new Date(System.currentTimeMillis()))
|
||||||
|
.setExpiration(new Date(System.currentTimeMillis() + expiration))
|
||||||
|
.signWith(SignatureAlgorithm.HS512, secret)
|
||||||
|
.compact();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证token
|
||||||
|
*/
|
||||||
|
public Boolean validateToken(String token, UserDetails userDetails) {
|
||||||
|
final String username = getUsernameFromToken(token);
|
||||||
|
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取token前缀
|
||||||
|
*/
|
||||||
|
public String getTokenPrefix() {
|
||||||
|
return tokenPrefix;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{"properties": [{
|
||||||
|
"name": "Content-Type,",
|
||||||
|
"type": "java.lang.String",
|
||||||
|
"description": "A description for 'Content-Type,'"
|
||||||
|
}]}
|
||||||
68
src/main/resources/application-dev.properties
Normal file
68
src/main/resources/application-dev.properties
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
# ====================================================================
|
||||||
|
# 开发环境配置文件
|
||||||
|
# 说明:此配置用于本地开发和调试,只包含开发环境特定配置
|
||||||
|
# 通用配置请参考主配置文件 application.properties
|
||||||
|
# ====================================================================
|
||||||
|
|
||||||
|
# 应用基本配置 - 开发特定
|
||||||
|
server.port=7071
|
||||||
|
|
||||||
|
# ====================================================================
|
||||||
|
# 数据库与JPA配置 - 开发用
|
||||||
|
# ====================================================================
|
||||||
|
spring.datasource.url=jdbc:mysql://localhost:3306/webproject?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&createDatabaseIfNotExist=true&allowPublicKeyRetrieval=true
|
||||||
|
spring.datasource.username=root
|
||||||
|
spring.datasource.password=123456
|
||||||
|
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||||
|
|
||||||
|
# 数据库连接池配置(开发环境简化版)
|
||||||
|
spring.datasource.hikari.maximum-pool-size=5
|
||||||
|
spring.datasource.hikari.minimum-idle=2
|
||||||
|
|
||||||
|
# JPA配置(开发环境使用update以便自动创建/更新表结构)
|
||||||
|
spring.jpa.hibernate.ddl-auto=update
|
||||||
|
spring.jpa.show-sql=false
|
||||||
|
spring.jpa.properties.hibernate.format_sql=false
|
||||||
|
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
|
||||||
|
|
||||||
|
# ====================================================================
|
||||||
|
# JWT 配置 - 开发用(方便调试,密钥较短,过期时间短)
|
||||||
|
# ====================================================================
|
||||||
|
jwt.secret=devSecretKey2024ForLocalDevelopment
|
||||||
|
jwt.expiration=3600000
|
||||||
|
# 1小时过期,方便调试
|
||||||
|
jwt.header=Authorization
|
||||||
|
jwt.token-prefix=Bearer
|
||||||
|
|
||||||
|
# ====================================================================
|
||||||
|
# 安全与CORS配置 - 开发用
|
||||||
|
# ====================================================================
|
||||||
|
# CORS配置(开发环境允许所有本地前端访问)
|
||||||
|
cors.allowed-origins=http://localhost:3000,http://localhost:8080,http://localhost:5173
|
||||||
|
cors.allowed-methods=GET,POST,PUT,DELETE,OPTIONS
|
||||||
|
cors.allowed-headers=*
|
||||||
|
cors.allow-credentials=true
|
||||||
|
|
||||||
|
# ====================================================================
|
||||||
|
# 日志配置 - 开发用(详细日志便于调试)
|
||||||
|
# ====================================================================
|
||||||
|
logging.level.root=INFO
|
||||||
|
logging.level.com.qf.myafterprojecy=DEBUG
|
||||||
|
logging.level.org.springframework=INFO
|
||||||
|
logging.level.org.hibernate.SQL=DEBUG
|
||||||
|
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
|
||||||
|
|
||||||
|
# ====================================================================
|
||||||
|
# 开发环境特有配置
|
||||||
|
# ====================================================================
|
||||||
|
# 热部署配置
|
||||||
|
spring.devtools.restart.enabled=true
|
||||||
|
spring.devtools.restart.additional-paths=src/main/java
|
||||||
|
# 解决DevTools重启时丢失profile配置的问题
|
||||||
|
spring.devtools.restart.poll-interval=2s
|
||||||
|
spring.devtools.restart.quiet-period=1s
|
||||||
|
spring.devtools.restart.exclude=static/**,public/**
|
||||||
|
|
||||||
|
# Actuator配置(开发环境开放更多端点便于调试)
|
||||||
|
management.endpoints.web.exposure.include=*
|
||||||
|
management.endpoint.health.show-details=always
|
||||||
70
src/main/resources/application-prod.properties
Normal file
70
src/main/resources/application-prod.properties
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
# ====================================================================
|
||||||
|
# 生产环境配置文件
|
||||||
|
# 说明:此配置用于生产环境部署,敏感信息从环境变量读取
|
||||||
|
# ====================================================================
|
||||||
|
|
||||||
|
# 应用基本配置 - 生产特定
|
||||||
|
server.port=7070
|
||||||
|
|
||||||
|
# ====================================================================
|
||||||
|
# 数据库与JPA配置 - 生产用
|
||||||
|
# ====================================================================
|
||||||
|
spring.datasource.url=${DB_URL:jdbc:mysql://mysql:3306/webproject?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai}
|
||||||
|
spring.datasource.username=${DB_USERNAME:root}
|
||||||
|
spring.datasource.password=${DB_PASSWORD:root}
|
||||||
|
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
|
||||||
|
|
||||||
|
# 数据库连接池配置(生产环境优化版)
|
||||||
|
spring.datasource.hikari.maximum-pool-size=20
|
||||||
|
spring.datasource.hikari.minimum-idle=5
|
||||||
|
spring.datasource.hikari.idle-timeout=300000
|
||||||
|
spring.datasource.hikari.connection-timeout=20000
|
||||||
|
spring.datasource.hikari.max-lifetime=1200000
|
||||||
|
spring.datasource.hikari.connection-test-query=SELECT 1
|
||||||
|
spring.datasource.hikari.pool-name=WebProjectHikariCP
|
||||||
|
|
||||||
|
# JPA配置(生产环境禁用自动DDL,避免意外修改表结构)
|
||||||
|
spring.jpa.hibernate.ddl-auto=create
|
||||||
|
spring.jpa.show-sql=false
|
||||||
|
spring.jpa.properties.hibernate.format_sql=false
|
||||||
|
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
|
||||||
|
spring.jpa.open-in-view=false
|
||||||
|
|
||||||
|
# JPA性能优化配置
|
||||||
|
spring.jpa.properties.hibernate.jdbc.batch_size=30
|
||||||
|
spring.jpa.properties.hibernate.order_inserts=true
|
||||||
|
spring.jpa.properties.hibernate.order_updates=true
|
||||||
|
|
||||||
|
# ====================================================================
|
||||||
|
# JWT 配置 - 生产用(敏感信息从环境变量读取)
|
||||||
|
# ====================================================================
|
||||||
|
jwt.secret=${JWT_SECRET:}
|
||||||
|
jwt.expiration=${JWT_EXPIRATION:86400000}
|
||||||
|
jwt.header=Authorization
|
||||||
|
jwt.token-prefix=Bearer
|
||||||
|
|
||||||
|
# ====================================================================
|
||||||
|
# 安全与CORS配置 - 生产用
|
||||||
|
# ====================================================================
|
||||||
|
# CORS配置(生产环境限制为具体域名)
|
||||||
|
cors.allowed-origins=http://qf1121.top,https://qf1121.top,http://www.qf1121.top,https://www.qf1121.top
|
||||||
|
cors.allowed-methods=GET,POST,PUT,DELETE,OPTIONS
|
||||||
|
cors.allowed-headers=*
|
||||||
|
cors.allow-credentials=true
|
||||||
|
cors.max-age=3600
|
||||||
|
|
||||||
|
# ====================================================================
|
||||||
|
# 日志配置 - 生产用(精简日志,提高性能)
|
||||||
|
# ====================================================================
|
||||||
|
logging.level.root=WARN
|
||||||
|
logging.level.com.qf.myafterprojecy=INFO
|
||||||
|
logging.level.org.springframework.security=WARN
|
||||||
|
logging.level.org.hibernate.SQL=ERROR
|
||||||
|
logging.file.name=logs/web_project.log
|
||||||
|
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
|
||||||
|
# ====================================================================
|
||||||
|
# API与应用配置 - 生产用
|
||||||
|
# ====================================================================
|
||||||
|
# Actuator配置(生产环境限制访问)
|
||||||
|
management.endpoints.web.exposure.include=health,info
|
||||||
|
management.endpoint.health.show-details=when_authorized
|
||||||
43
src/main/resources/application.properties
Normal file
43
src/main/resources/application.properties
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# ====================================================================
|
||||||
|
# 应用基本配置 - 通用配置
|
||||||
|
# ====================================================================
|
||||||
|
# 环境激活配置
|
||||||
|
# 说明:默认激活开发环境,生产环境部署时应通过命令行参数或环境变量覆盖
|
||||||
|
spring.profiles.active=dev
|
||||||
|
server.port=7070
|
||||||
|
# 应用名称(通用配置)
|
||||||
|
spring.application.name=web_project
|
||||||
|
|
||||||
|
# ====================================================================
|
||||||
|
# 会话与编码配置 - 通用配置
|
||||||
|
# ====================================================================
|
||||||
|
# 会话配置
|
||||||
|
server.servlet.session.timeout=30m
|
||||||
|
server.session.tracking-modes=cookie
|
||||||
|
|
||||||
|
# 国际化配置
|
||||||
|
spring.web.locale=zh_CN
|
||||||
|
spring.messages.encoding=UTF-8
|
||||||
|
|
||||||
|
# 响应编码配置(通用,所有环境保持一致)
|
||||||
|
server.servlet.encoding.charset=UTF-8
|
||||||
|
server.servlet.encoding.force=true
|
||||||
|
server.servlet.encoding.force-request=true
|
||||||
|
server.servlet.encoding.force-response=true
|
||||||
|
server.servlet.encoding.enabled=true
|
||||||
|
|
||||||
|
# ====================================================================
|
||||||
|
# API与应用配置 - 通用配置
|
||||||
|
# ====================================================================
|
||||||
|
# API 文档配置(通用)
|
||||||
|
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
|
||||||
|
|
||||||
|
# 应用性能优化配置(通用)
|
||||||
|
spring.main.allow-bean-definition-overriding=true
|
||||||
|
spring.main.lazy-initialization=false
|
||||||
|
|
||||||
|
# ====================================================================
|
||||||
|
# 说明:环境特定的配置(如数据库、JWT、CORS等)已移至对应的环境配置文件中
|
||||||
|
# - 开发环境:application-dev.properties
|
||||||
|
# - 生产环境:application-prod.properties
|
||||||
|
# ====================================================================
|
||||||
6
src/main/resources/static/index.html
Normal file
6
src/main/resources/static/index.html
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<h1>hello word!!!</h1>
|
||||||
|
<p>this is a html page</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.qf.myafterprojecy;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
|
||||||
|
@SpringBootTest
|
||||||
|
class MyAfterProjecyApplicationTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void contextLoads() {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user