Compare commits

...

18 Commits

Author SHA1 Message Date
qingfeng1121
15eca0d0b5 refactor(controller/service/repository): 重构分类属性、文章和消息相关功能
重构分类属性相关类名从Category_attribute改为Categoryattribute
优化文章和消息的分页查询功能,新增分页DTO类
移除旧的分页DTO类PageDto,新增ArriclePageDto和MessagePageDto
调整消息统计逻辑,区分文章评论和独立消息
更新安全配置,开放消息新增接口权限
2025-12-18 15:19:59 +08:00
qingfeng1121
f27be2130c refactor: 优化代码结构和配置
- 在PageDto中添加toString方法以便调试
- 关闭开发环境的SQL日志输出以减少日志噪音
- 允许匿名用户发表评论
- 调整安全配置,仅允许POST方法访问认证端点
- 修改文章状态分页接口,使用查询参数替代路径参数
2025-12-15 17:54:14 +08:00
qingfeng1121
9b01ee8889 refactor(项目结构): 重构项目包结构和异常处理
- 将ResponseMessage和GlobalExceptionHandler移动到exceptopn包
- 重构服务接口包结构,将接口从service.imp移动到service包
- 更新所有控制器中ResponseMessage的引用路径
- 统一服务接口命名规范,去除I前缀
- 调整application.properties配置,统一服务端口
- 优化SecurityConfig权限配置,简化API访问控制
- 清理旧的日志文件
- 更新开发环境配置,添加前端开发端口支持
2025-12-12 17:35:53 +08:00
qingfeng1121
505a7a0944 chore: 更新配置文件及pom.xml编码设置
修改开发环境jwt注释格式
调整生产环境jpa ddl-auto为create
添加maven资源插件UTF-8编码配置
2025-12-11 14:48:15 +08:00
qingfeng1121
51392bf807 feat: 添加用户数据初始化功能并优化配置管理
refactor: 重构CORS配置使其从配置文件读取
refactor: 移除无用分类和消息初始化代码

build: 更新pom.xml依赖项

docs: 拆分应用配置文件为环境特定配置
2025-12-11 12:42:53 +08:00
qingfeng1121
eb1f70d431 feat(文章): 添加根据状态分页查询文章功能
实现文章列表的分页查询功能,包括:
- 在ArticleRepository添加分页查询方法
- 在IArticleService和ArticleService添加分页接口和实现
- 新增PageDto用于分页参数校验
- 在ArticleController添加分页接口
2025-11-14 15:30:39 +08:00
qingfeng1121
47c357695b chore: 添加日志文件 web_project.log.2025-11-08.0.gz 2025-11-09 16:27:49 +08:00
qingfeng1121
d8c6c74de4 feat(文章/疯言疯语): 添加状态管理功能
实现文章和疯言疯语内容的状态管理,支持按状态查询和更新
允许公开访问文章查看接口
完善相关文档和日志记录
2025-11-08 11:16:14 +08:00
qingfeng1121
5136a3a78b feat: 添加随机内容模块并优化安全配置
新增Nonsense相关实体、DTO、Repository、Service和Controller,实现随机内容的CRUD功能
优化CORS和安全配置,增加更精细的权限控制和错误处理
移除Article和Message中不必要的验证注解,调整部分API的权限要求
2025-11-05 16:11:38 +08:00
qingfeng1121
25eeab4940 feat(security): 实现JWT认证并增强API安全控制
添加JWT依赖并实现token生成与验证功能
在控制器方法上添加权限注解保护API端点
更新安全配置以集成JWT过滤器
移除无用的编码测试工具类
修改JWT相关配置为更安全的设置
2025-11-03 16:14:53 +08:00
qingfeng1121
f6d1d719a9 feat: 添加UTF-8编码支持并优化DTO验证
refactor: 重构用户服务密码更新逻辑
fix: 删除不再使用的MarkdownDto类
style: 清理日志文件并优化日志配置
build: 更新pom.xml配置以支持UTF-8编码
docs: 更新application.properties配置文档
2025-10-30 19:00:47 +08:00
qingfeng1121
5803080352 feat(security): 重构安全配置并添加用户认证功能
refactor: 将ResponseMessage移动到config包并增强功能
feat: 添加用户管理相关功能及密码加密配置
fix: 修复HelpController中README文件路径问题
docs: 更新application.properties配置注释
style: 清理无用导入和日志文件
2025-10-28 12:47:02 +08:00
qingfeng1121
9132feb870 refactor(service): 重构服务接口和实现类结构
将服务接口从service包移动到service.imp包
修复ArticleRepository中viewCount的COALESCE处理
添加getPublishedArticles方法获取已发布文章
优化incrementViewCount方法使用仓库直接更新
修正HelpController中README_API.md路径
2025-10-26 20:18:35 +08:00
qingfeng1121
46be613f28 feat(消息): 添加消息点赞功能
- 在MessageController中添加点赞接口
- 在MessageRepository中添加点赞数更新方法
- 在IMessageService和MessageService中实现点赞逻辑
- 初始化测试数据时设置点赞数默认值为0
- 完善相关文档注释
2025-10-23 18:18:27 +08:00
qingfeng1121
f53e251d46 feat(消息): 添加回复ID字段支持消息回复功能
在Message和MessageDto中添加replyid字段,支持消息回复功能
添加删除所有评论的API端点
重构消息控制器方法顺序
```

```msg
feat(文章): 实现文章浏览量增加功能

添加incrementViewCount方法用于增加文章浏览量
在文章实体中添加likes字段记录点赞数
更新API文档说明新增字段
```

```msg
chore: 移除数据初始化类

注释掉CategoryDataInit和MessageDataInit类
这些初始化功能将由其他方式实现
2025-10-22 13:28:30 +08:00
qingfeng1121
848b13506c fix: 移除MessageDto中未使用的JPA注解并更新数据库配置
移除MessageDto中未使用的JPA注解以简化代码结构
在数据库连接URL中添加allowPublicKeyRetrieval参数以解决连接问题
清理过期的日志文件
2025-10-20 12:00:07 +08:00
qingfeng1121
effcc3838d refactor(pojo): 修正Article类中attributeid字段的列名拼写
feat(controller): 在ArticleController中添加根据属性ID获取文章的方法

style(repository): 在CategoryRepository方法上添加空行提高可读性

chore: 移除MyAfterProjecyApplication中多余的MapperScan注解
2025-10-19 11:11:56 +08:00
qingfeng1121
bd6b240f52 refactor: 删除未使用的Category_attribute接口
该接口未被项目使用且功能可由其他现有接口替代,移除以减少代码冗余
2025-10-18 10:29:23 +08:00
68 changed files with 8006 additions and 2305 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

53
pom.xml
View File

@@ -18,11 +18,7 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!-- Spring Security 核心依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -81,29 +77,13 @@
<version>0.18.2</version>
</dependency>
<!-- JCache API -->
<!-- JWT依赖 -->
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!-- EHCache 3作为JCache实现 -->
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>8.0.1</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<dependencyManagement>
@@ -138,6 +118,7 @@
<configuration>
<mainClass>com.qf.myafterprojecy.MyAfterProjecyApplication</mainClass>
<skip>false</skip>
<jvmArguments>-Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8</jvmArguments>
</configuration>
<executions>
<execution>
@@ -148,7 +129,27 @@
</execution>
</executions>
</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>
<!-- 确保项目编译和资源处理使用UTF-8编码 -->
<sourceDirectory>src/main/java</sourceDirectory>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*</include>
</includes>
</resource>
</resources>
</build>
</project>

View File

@@ -1,14 +1,42 @@
package com.qf.myafterprojecy;
import org.mybatis.spring.annotation.MapperScan;
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());
}
}
}

View File

@@ -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;
}
}

View File

@@ -1,5 +1,6 @@
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;
@@ -12,38 +13,67 @@ import org.springframework.web.filter.CorsFilter;
@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();
// 允许的来源,这里允许所有来源,实际生产环境应该限制特定域名
config.addAllowedOriginPattern("*");
// 允许的来源,从配置文件读取并分割
String[] originsArray = allowedOrigins.split(",");
for (String origin : originsArray) {
config.addAllowedOrigin(origin.trim());
}
// 允许携带凭证如Cookie
config.setAllowCredentials(true);
// 允许的请求方法
config.addAllowedMethod("*");
// 允许的请求头
config.addAllowedHeader("*");
// 暴露的响应头这些头信息可以被前端JavaScript访问
config.addExposedHeader("*");
// 设置预检请求的有效期(秒)
config.setMaxAge(3600L);
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);
}

View File

@@ -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
);
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}

View File

@@ -1,21 +1,52 @@
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 配置过程中可能出现的异常
@@ -23,18 +54,81 @@ public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
// 启用CORS支持确保与CorsConfig中配置的过滤器配合工作
.cors().and()
// 禁用CSRF保护对于API服务通常不需要
.csrf().disable()
// 允许所有请求通过,不需要认证
// 配置URL访问权限
.authorizeRequests()
.anyRequest().permitAll()
// 允许公开访问的路径
// 登录和认证相关端点应该全部公开
.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()
// 禁用表单登录
.formLogin().disable()
// 禁用HTTP基本认证
.httpBasic().disable()
// 禁用会话管理对于无状态API服务
.sessionManagement().disable();
// 配置会话管理,使用无状态会话策略
// 这意味着每个请求都需要包含认证信息如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();
}

View File

@@ -1,9 +1,13 @@
package com.qf.myafterprojecy.controller;
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
import com.qf.myafterprojecy.pojo.Article;
import com.qf.myafterprojecy.pojo.ResponseMessage;
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;
@@ -18,6 +22,7 @@ import java.util.List;
*/
@RestController
@RequestMapping("/api/articles")
@Validated
public class ArticleController {
@@ -33,7 +38,38 @@ public class ArticleController {
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对象
@@ -42,6 +78,15 @@ public class ArticleController {
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 文章标题
@@ -51,57 +96,7 @@ public class ArticleController {
public ResponseMessage<List<Article>> getArticlesByTitle(@PathVariable String title) {
return articleService.getArticlesByTitle(title);
}
/**
* 创建新文章
* 仅限AUTHOR角色用户访问
* @param articleDto 包含文章数据的DTO对象
* @return 返回包含新创建文章信息的ResponseMessage对象
*/
@PostMapping
@PreAuthorize("hasRole('AUTHOR')")
public ResponseMessage<Article> createArticle(@Valid @RequestBody ArticleDto articleDto) {
return articleService.saveArticle(articleDto);
}
/**
* 更新现有文章
* 仅限AUTHOR角色用户访问
* @param id 要更新的文章ID
* @param articleDto 包含更新后文章数据的DTO对象
* @return 返回包含更新后文章信息的ResponseMessage对象
*/
@PutMapping("/{id}")
@PreAuthorize("hasRole('AUTHOR')")
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('AUTHOR') or hasRole('ADMIN')")
public ResponseMessage<Article> deleteArticle(@PathVariable Integer id) {
return articleService.deleteArticle(id);
}
/**
* 根据分类ID获取该分类下的所有文章兼容旧接口
* @param categoryId 分类ID
* @return 返回包含文章列表的ResponseMessage对象
*/
@GetMapping("/category/{categoryId}")
public ResponseMessage<List<Article>> getArticlesByCategory(@PathVariable Integer categoryId) {
return articleService.getArticlesByCategory(categoryId);
}
/**
* 根据属性ID获取该属性下的所有文章
* @param attributeId 属性ID
@@ -121,13 +116,52 @@ public class ArticleController {
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);
}
/**
* 获取浏览量最高的文章列表
* @return 返回包含热门文章列表的ResponseMessage对象
* 删除文章
* 仅限AUTHOR或ADMIN角色用户访问
* @param id 要删除的文章ID
* @return 返回包含被删除文章信息的ResponseMessage对象
*/
@GetMapping("/popular")
public ResponseMessage<List<Article>> getMostViewedArticles() {
return articleService.getMostViewedArticles();
@DeleteMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')")
public ResponseMessage<Article> deleteArticle(@PathVariable Integer id) {
return articleService.deleteArticle(id);
}
}

View File

@@ -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("登出成功");
}
}

View File

@@ -1,12 +1,14 @@
package com.qf.myafterprojecy.controller;
import com.qf.myafterprojecy.pojo.Category_attribute;
import com.qf.myafterprojecy.pojo.ResponseMessage;
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.*;
@@ -29,10 +31,15 @@ public class CategoryAttributeController {
* @return 属性信息
*/
@GetMapping("/{id}")
public ResponseMessage<Category_attribute> getAttributeById(@PathVariable Integer 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获取属性列表
@@ -40,7 +47,7 @@ public class CategoryAttributeController {
* @return 属性列表
*/
@GetMapping("/category/{categoryId}")
public ResponseMessage<List<Category_attribute>> getAttributesByCategory(@PathVariable Integer categoryId) {
public ResponseMessage<List<Categoryattribute>> getAttributesByCategory(@PathVariable Integer categoryId) {
log.info("接收根据分类ID获取属性列表的请求: 分类ID={}", categoryId);
return categoryAttributeService.getAttributesByCategoryId(categoryId);
}
@@ -51,7 +58,8 @@ public class CategoryAttributeController {
* @return 创建结果
*/
@PostMapping
public ResponseMessage<Category_attribute> createAttribute(@Valid @RequestBody CategoryAttributeDto dto) {
@PreAuthorize("hasRole('ADMIN')")
public ResponseMessage<Categoryattribute> createAttribute(@Valid @RequestBody CategoryAttributeDto dto) {
log.info("接收创建分类属性的请求: 分类ID={}, 属性名称={}",
dto.getCategoryid(), dto.getAttributename());
return categoryAttributeService.saveCategoryAttribute(dto);
@@ -64,7 +72,8 @@ public class CategoryAttributeController {
* @return 更新结果
*/
@PutMapping("/{id}")
public ResponseMessage<Category_attribute> updateAttribute(
@PreAuthorize("hasRole('ADMIN')")
public ResponseMessage<Categoryattribute> updateAttribute(
@PathVariable Integer id,
@Valid @RequestBody CategoryAttributeDto dto) {
log.info("接收更新分类属性的请求: ID={}, 分类ID={}, 属性名称={}",
@@ -78,6 +87,7 @@ public class CategoryAttributeController {
* @return 删除结果
*/
@DeleteMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')")
public ResponseMessage<Boolean> deleteAttribute(@PathVariable Integer id) {
log.info("接收删除分类属性的请求: ID={}", id);
return categoryAttributeService.deleteCategoryAttribute(id);

View File

@@ -1,12 +1,14 @@
package com.qf.myafterprojecy.controller;
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
import com.qf.myafterprojecy.pojo.Category;
import com.qf.myafterprojecy.pojo.ResponseMessage;
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.*;
@@ -54,6 +56,7 @@ public class CategoryController {
* @return 返回创建结果
*/
@PostMapping
@PreAuthorize("hasRole('ADMIN')")
public ResponseMessage<Category> createCategory(@Valid @RequestBody CategoryDto categoryDto) {
log.info("接收创建分类的请求: {}", categoryDto.getTypename());
return categoryService.saveCategory(categoryDto);
@@ -66,6 +69,7 @@ public class CategoryController {
* @return 返回更新结果
*/
@PutMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')")
public ResponseMessage<Category> updateCategory(
@PathVariable Integer id,
@Valid @RequestBody CategoryDto categoryDto) {
@@ -79,6 +83,7 @@ public class CategoryController {
* @return 返回删除结果
*/
@DeleteMapping("/{id}")
@PreAuthorize("hasRole('ADMIN')")
public ResponseMessage<Boolean> deleteCategory(@PathVariable Integer id) {
log.info("接收删除分类的请求: {}", id);
return categoryService.deleteCategory(id);

View File

@@ -1,6 +1,5 @@
package com.qf.myafterprojecy.controller;
import com.qf.myafterprojecy.pojo.ResponseMessage;
import org.commonmark.node.Node;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
@@ -10,6 +9,8 @@ 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;
@@ -31,7 +32,7 @@ public class HelpController {
public ResponseMessage<String> getReadmeApi() {
try {
// 获取项目根目录
String rootPath = System.getProperty("user.dir");
String rootPath = System.getProperty("user.dir") ;
// 构建README_API.md文件路径
File readmeFile = new File(rootPath, "README_API.md");

View File

@@ -1,12 +1,15 @@
package com.qf.myafterprojecy.controller;
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
import com.qf.myafterprojecy.pojo.Message;
import com.qf.myafterprojecy.pojo.ResponseMessage;
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;
@@ -28,7 +31,25 @@ public class MessageController {
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获取消息
*/
@@ -37,26 +58,6 @@ public class MessageController {
logger.info("接收根据ID获取消息的请求: {}", id);
return messageService.getMessageById(id);
}
/**
* 创建新消息
*/
@PostMapping
public ResponseMessage<Message> createMessage(@RequestBody MessageDto message) {
logger.info("接收创建消息的请求: {}", message != null ? message.getNickname() : "null");
return messageService.saveMessage(message);
}
/**
* 根据ID删除消息
*/
@DeleteMapping("/{id}")
public ResponseMessage<Message> deleteMessage(@PathVariable Integer id) {
logger.info("接收删除消息的请求: {}", id);
return messageService.deleteMessage(id);
}
// 新增API端点
/**
* 根据文章ID获取消息列表
@@ -95,11 +96,34 @@ public class MessageController {
}
/**
* 获取指定文章的评论数量
* 创建新消息
*/
@GetMapping("/count/article/{articleId}")
public ResponseMessage<Long> getMessageCountByArticleId(@PathVariable Integer articleId) {
logger.info("接收获取文章评论数量的请求: {}", articleId);
return messageService.getMessageCountByArticleId(articleId);
@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();
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -1,7 +1,4 @@
package com.qf.myafterprojecy;
import com.qf.myafterprojecy.pojo.ResponseMessage;
package com.qf.myafterprojecy.exceptopn;
import javax.servlet.http.HttpServletRequest;

View File

@@ -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;
}
}

View File

@@ -1,85 +0,0 @@
package com.qf.myafterprojecy.init;
import com.qf.myafterprojecy.pojo.Category;
import com.qf.myafterprojecy.repository.CategoryRepository;
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.stereotype.Component;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
/**
* 分类数据初始化类,用于在应用启动时初始化分类数据
*/
@Component
public class CategoryDataInit implements ApplicationRunner {
private static final Logger logger = LoggerFactory.getLogger(CategoryDataInit.class);
@Autowired
private CategoryRepository categoryRepository;
@Override
public void run(ApplicationArguments args) throws Exception {
logger.info("===== 分类数据初始化开始 =====");
// 检查数据库中是否已有分类数据
long count = categoryRepository.count();
logger.info("当前数据库中分类数量: {}", count);
// 如果没有分类数据,添加一些测试数据
if (count == 0) {
logger.info("数据库中没有分类数据,开始添加初始化数据...");
addInitialCategories();
} else {
logger.info("数据库中已存在分类数据,无需初始化");
}
logger.info("===== 分类数据初始化结束 =====");
}
/**
* 添加初始分类数据
*/
private void addInitialCategories() {
List<Category> categories = new ArrayList<>();
// 创建几个常见的文章分类
Category category1 = new Category();
category1.setTypename("技术分享");
category1.setDescription("技术文章、教程、经验分享等");
category1.setCreatedAt(LocalDateTime.now());
category1.setUpdatedAt(LocalDateTime.now());
categories.add(category1);
Category category2 = new Category();
category2.setTypename("生活随笔");
category2.setDescription("日常生活、心情记录、随笔等");
category2.setCreatedAt(LocalDateTime.now());
category2.setUpdatedAt(LocalDateTime.now());
categories.add(category2);
Category category3 = new Category();
category3.setTypename("学习笔记");
category3.setDescription("学习过程中的笔记、总结等");
category3.setCreatedAt(LocalDateTime.now());
category3.setUpdatedAt(LocalDateTime.now());
categories.add(category3);
Category category4 = new Category();
category4.setTypename("行业动态");
category4.setDescription("行业新闻、趋势分析等");
category4.setCreatedAt(LocalDateTime.now());
category4.setUpdatedAt(LocalDateTime.now());
categories.add(category4);
// 保存分类数据到数据库
categoryRepository.saveAll(categories);
logger.info("成功添加 {} 条分类数据", categories.size());
}
}

View File

@@ -1,96 +0,0 @@
package com.qf.myafterprojecy.init;
import com.qf.myafterprojecy.pojo.Message;
import com.qf.myafterprojecy.repository.MessageRepository;
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.stereotype.Component;
import java.util.Date;
/**
* 消息数据初始化类用于在应用启动时为Message表添加默认的测试数据
*/
@Component
public class MessageDataInit implements ApplicationRunner {
private static final Logger logger = LoggerFactory.getLogger(MessageDataInit.class);
@Autowired
private MessageRepository messageRepository;
@Override
public void run(ApplicationArguments args) throws Exception {
logger.info("===== 消息数据初始化开始 =====");
// 检查数据库中是否已有消息数据
long count = messageRepository.count();
logger.info("当前数据库中消息数量: {}", count);
// 如果没有消息数据,添加一些测试数据
if (count == 0) {
logger.info("数据库中没有消息数据,开始添加初始化数据...");
addInitialMessages();
} else {
logger.info("数据库中已存在消息数据,无需初始化");
}
logger.info("===== 消息数据初始化结束 =====");
}
private void addInitialMessages() {
// 添加第一篇文章的评论
Message message1 = new Message();
message1.setNickname("系统用户");
message1.setEmail("system@example.com");
message1.setContent("这是系统自动添加的第一条评论,欢迎使用本系统!");
message1.setCreatedAt(new Date());
message1.setArticleid(1);
message1.setParentid(null); // 根评论
messageRepository.save(message1);
// 添加回复
Message reply1 = new Message();
reply1.setNickname("管理员");
reply1.setEmail("admin@example.com");
reply1.setContent("感谢您的支持,如有任何问题请随时联系我们!");
reply1.setCreatedAt(new Date());
reply1.setArticleid(1);
reply1.setParentid(message1.getMessageid()); // 回复第一条评论
messageRepository.save(reply1);
// 添加第二篇文章的评论
Message message2 = new Message();
message2.setNickname("访客");
message2.setEmail("visitor@example.com");
message2.setContent("这篇文章写得非常好,学到了很多知识。");
message2.setCreatedAt(new Date());
message2.setArticleid(2);
message2.setParentid(null);
messageRepository.save(message2);
// 再添加一些测试数据
Message message3 = new Message();
message3.setNickname("测试用户1");
message3.setEmail("test1@example.com");
message3.setContent("这是测试内容1");
message3.setCreatedAt(new Date());
message3.setArticleid(1);
message3.setParentid(null);
messageRepository.save(message3);
Message reply2 = new Message();
reply2.setNickname("测试用户2");
reply2.setEmail("test2@example.com");
reply2.setContent("回复测试内容1");
reply2.setCreatedAt(new Date());
reply2.setArticleid(1);
reply2.setParentid(message3.getMessageid());
messageRepository.save(reply2);
logger.info("成功添加了{}条初始化消息数据", messageRepository.count());
}
}

View 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());
}
}
}
}

View File

@@ -17,12 +17,11 @@ public class Article {
@Column(name = "title")
private String title;
@NotBlank(message = "内容不能为空")
@Column(name = "content", columnDefinition = "TEXT")
private String content;
@NotNull(message = "类别id不能为空")
@Column(name = "attributeid")
@Column(name = "attribute_id")
private Integer attributeid;
@Column(name = "img")
@@ -33,14 +32,30 @@ public class Article {
@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;
@@ -113,4 +128,12 @@ public class Article {
public void setViewCount(Integer viewCount) {
this.viewCount = viewCount;
}
public String getMarkdownscontent() {
return markdownscontent;
}
public void setMarkdownscontent(String markdownscontent) {
this.markdownscontent = markdownscontent;
}
}

View File

@@ -9,8 +9,8 @@ import java.time.LocalDateTime;
public class Category {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "typeid")
private Integer typeid;
@Column(name = "categoryid")
private Integer categoryid;
@NotBlank(message = "分类名称不能为空")
@Column(name = "typename")
@@ -26,12 +26,12 @@ public class Category {
private LocalDateTime updatedAt;
// Getters and Setters
public Integer getTypeid() {
return typeid;
public Integer getCategoryid() {
return categoryid;
}
public void setTypeid(Integer typeid) {
this.typeid = typeid;
public void setCategoryid(Integer categoryid) {
this.categoryid = categoryid;
}
public String getTypename() {

View File

@@ -4,7 +4,7 @@ import javax.persistence.*;
@Entity
@Table(name = "category_attribute")
public class Category_attribute {
public class Categoryattribute {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "attributeid")

View File

@@ -8,22 +8,53 @@ import java.util.Date;
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(columnDefinition = "text")
@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;
private Integer articleid;
@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;
@@ -80,4 +111,12 @@ public class Message {
public void setArticleid(Integer articleid) {
this.articleid = articleid;
}
public String getMessageimg() {
return messageimg;
}
public void setMessageimg(String messageimg) {
this.messageimg = messageimg;
}
}

View 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;
}
}

View File

@@ -1,128 +0,0 @@
package com.qf.myafterprojecy.pojo;
import lombok.Data;
import org.springframework.http.HttpStatus;
/**
* 通用响应消息类,用于封装接口返回的数据结构
* 使用泛型T来支持不同类型的数据返回
* @param <T> 数据类型可以是任意Java对象
*/
@Data
public class ResponseMessage<T> {
// 状态码,通常用于表示请求的处理结果
private Integer code;
// 响应消息,用于描述请求的处理结果信息
private String message;
// 请求是否成功的标志
private boolean success;
// 响应数据,泛型类型,支持不同类型的数据
private T data;
/**
* 构造方法,用于创建响应消息对象
* @param code 状态码
* @param message 响应消息
* @param data 响应数据
*/
public ResponseMessage(Integer code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
// 获取成功状态的getter方法
public boolean isSuccess() {
return success;
}
// 设置成功状态的setter方法
public void setSuccess(boolean success) {
this.success = success;
}
// 获取状态码的getter方法
public Integer getCode() {
return code;
}
// 设置状态码的setter方法
public void setCode(Integer code) {
this.code = code;
}
// 获取响应消息的getter方法
public String getMessage() {
return message;
}
// 设置响应消息的setter方法
public void setMessage(String message) {
this.message = message;
}
// 获取响应数据的getter方法
public T getData() {
return data;
}
// 设置响应数据的setter方法
public void setData(T data) {
this.data = data;
}
/**
* 完整参数的构造方法
* @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;
}
// 接口请求成功
public static <T> ResponseMessage<T> success(T data ,String message ,boolean success) {
return new ResponseMessage(HttpStatus.OK.value(), message, data ,success);
}
/**
* 创建一个表示操作失败的响应消息
* @param message 失败原因的描述信息
* @return 返回一个包含错误状态码和错误信息的ResponseMessage对象
*/
public static <T> ResponseMessage<T> failure(String message) {
return new ResponseMessage<>(HttpStatus.INTERNAL_SERVER_ERROR.value(), message, null, false);
}
public static <T> ResponseMessage<T> success(T data) {
return new ResponseMessage<>(HttpStatus.OK.value(), "操作成功", data, true);
}
public static <T> ResponseMessage<T> success(T data, String message) {
return new ResponseMessage<>(HttpStatus.OK.value(), message, data, true);
}
public static <T> ResponseMessage<T> error(String message) {
return new ResponseMessage<>(HttpStatus.INTERNAL_SERVER_ERROR.value(), message, null, false);
}
public static <T> ResponseMessage<T> error(Integer code, String message) {
return new ResponseMessage<>(code, message, null, false);
}
public static <T> ResponseMessage<T> Save(boolean success) {
return success ?
new ResponseMessage<>(HttpStatus.OK.value(), "保存成功", null, true) :
new ResponseMessage<>(HttpStatus.INTERNAL_SERVER_ERROR.value(), "保存失败", null, false);
}
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);
}
}

View 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;
}
}

View File

@@ -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 +
'}';
}
}

View File

@@ -1,32 +1,36 @@
package com.qf.myafterprojecy.pojo.dto;
import lombok.Getter;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@Getter
public class ArticleDto {
private Integer id;
private Integer articleid;
@NotBlank(message = "标题不能为空")
private String title;
@NotBlank(message = "内容不能为空")
private String content;
private String content;// 如果为空说明是长篇文章 不为空则是短篇说说
@NotNull(message = "属性ID不能为空")
private Integer attributeid;
private String img;
private Integer viewCount;
private Integer likes;
private Integer status;
// Getters and Setters
private String markdownscontent; // 文章内容的Markdown格式
public Integer getId() {
return id;
// Getters and Setters
public Integer getArticleid() {
return articleid;
}
public void setId(Integer id) {
this.id = id;
public void setArticleid(Integer articleid) {
this.articleid = articleid;
}
public String getTitle() {
@@ -45,12 +49,12 @@ public class ArticleDto {
this.content = content;
}
public Integer getStatus() {
return status;
public Integer getAttributeid() {
return attributeid;
}
public void setStatus(Integer status) {
this.status = status;
public void setAttributeid(Integer attributeid) {
this.attributeid = attributeid;
}
public String getImg() {
@@ -60,4 +64,36 @@ public class ArticleDto {
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;
}
}

View File

@@ -4,6 +4,7 @@ import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
public class CategoryAttributeDto {
private Integer attributeid;
@NotNull(message = "分类ID不能为空")
private Integer categoryid;
@@ -12,6 +13,14 @@ public class CategoryAttributeDto {
private String attributename;
// Getters and Setters
public Integer getAttributeid() {
return attributeid;
}
public void setAttributeid(Integer attributeid) {
this.attributeid = attributeid;
}
public Integer getCategoryid() {
return categoryid;
}

View File

@@ -1,21 +1,27 @@
package com.qf.myafterprojecy.pojo.dto;
import javax.validation.constraints.NotBlank;
import java.time.LocalDateTime;
public class CategoryDto {
private Integer typeid;
private Integer Categoryid;
@NotBlank(message = "分类名称不能为空")
private String typename;
private String description;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
// Getters and Setters
public Integer getTypeid() {
return typeid;
public Integer getCategoryid() {
return Categoryid;
}
public void setTypeid(Integer typeid) {
this.typeid = typeid;
public void setCategoryid(Integer Categoryid) {
this.Categoryid = Categoryid;
}
public String getTypename() {

View File

@@ -1,6 +1,5 @@
package com.qf.myafterprojecy.pojo.dto;
import javax.persistence.*;
import java.util.Date;
public class MessageDto {
@@ -15,10 +14,23 @@ public class MessageDto {
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;
}
@@ -74,4 +86,20 @@ public class MessageDto {
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;
}
}

View File

@@ -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 +
'}';
}
}

View File

@@ -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;
}
}

View 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;
}
}

View File

@@ -1,6 +1,8 @@
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;
@@ -10,69 +12,124 @@ import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
@Repository // 表明这是一个数据访问层组件,用于持久层操作
@Repository // 表明这是一个数据访问层组件,用于持久层操作
public interface ArticleRepository extends JpaRepository<Article, Integer> {
/**
* 根据文章ID查询文章信息的方法
* 使用JPQLJava Persistence Query Language进行查询
*
* @param id 文章的唯一标识符,作为查询条件
* @return 返回一个Optional<Article>对象可能包含文章信息也可能为空如果未找到对应ID的文章
*/
/**
* 根据文章ID查询文章信息的方法
* 使用JPQLJava 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 返回符合查询条件的文章列表
*/
/**
* 根据文章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对象的列表
*/
/**
* 根据分类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对象的列表按创建时间降序排列
*/
/**
* 根据属性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注解标记这是一个修改操作通常用于UPDATE或DELETE语句
* 使用@Query注解定义自定义的JPQL查询语句
* 该查询用于将指定文章的浏览量(viewCount)增加1
*
* @param articleid 文章的唯一标识符,通过@Param注解将方法参数与查询参数绑定
*/
@Modifying
@Query("UPDATE Article a SET a.viewCount = a.viewCount + 1 WHERE a.articleid = :articleid")
@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)降序排列
*/
/**
* 根据浏览量降序查询状态为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);
}

View File

@@ -1,6 +1,6 @@
package com.qf.myafterprojecy.repository;
import com.qf.myafterprojecy.pojo.Category_attribute;
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;
@@ -10,23 +10,23 @@ import java.util.List;
import java.util.Optional;
@Repository
public interface CategoryAttributeRepository extends JpaRepository<Category_attribute, Integer> {
public interface CategoryAttributeRepository extends JpaRepository<Categoryattribute, Integer> {
/**
* 根据分类ID查询分类属性列表
* @param categoryid 分类ID
* @return 返回该分类下的所有属性列表
*/
@Query("SELECT ca FROM Category_attribute ca WHERE ca.categoryid = :categoryid")
List<Category_attribute> findByCategoryId(@Param("categoryid") Integer categoryid);
@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 Category_attribute ca WHERE ca.attributeid = :attributeid")
Optional<Category_attribute> findByAttributeId(@Param("attributeid") Integer attributeid);
@Query("SELECT ca FROM Categoryattribute ca WHERE ca.attributeid = :attributeid")
Optional<Categoryattribute> findByAttributeId(@Param("attributeid") Integer attributeid);
/**
* 检查分类下是否存在指定名称的属性
@@ -42,5 +42,5 @@ public interface CategoryAttributeRepository extends JpaRepository<Category_attr
* @param attributename 属性名称
* @return 属性对象
*/
Optional<Category_attribute> findByCategoryidAndAttributename(Integer categoryid, String attributename);
Optional<Categoryattribute> findByCategoryidAndAttributename(Integer categoryid, String attributename);
}

View File

@@ -14,6 +14,7 @@ public interface CategoryRepository extends JpaRepository<Category, Integer> {
* @param typename 分类名称
* @return 返回符合条件的分类列表
*/
List<Category> findByTypenameContaining(String typename);
/**

View File

@@ -1,6 +0,0 @@
package com.qf.myafterprojecy.repository;
import org.hibernate.metamodel.model.convert.spi.JpaAttributeConverter;
public interface Category_attribute extends JpaAttributeConverter<Category_attribute, Integer> {
}

View File

@@ -1,9 +1,15 @@
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;
@@ -11,22 +17,80 @@ import java.util.List;
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);
// 查询指定文章下的所有父消息(根回复)
@Query("SELECT m FROM Message m WHERE m.articleid = ?1 AND m.parentid IS NULL ORDER BY m.createdAt DESC")
List<Message> findRootMessagesByArticleId(Integer articleId);
/**
* 查询指定文章下的所有父消息(根回复)
* @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);
// 统计指定文章的评论数量
@Query("SELECT COUNT(m) FROM Message m WHERE m.articleid = ?1")
Long countByArticleId(Integer articleId);
/**
* 根据文章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();
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -1,165 +0,0 @@
package com.qf.myafterprojecy.runner;
import com.qf.myafterprojecy.pojo.Message;
import com.qf.myafterprojecy.pojo.ResponseMessage;
import com.qf.myafterprojecy.pojo.dto.MessageDto;
import com.qf.myafterprojecy.repository.MessageRepository;
import com.qf.myafterprojecy.service.IMessageService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.Optional;
/**
* 消息数据检查器,用于验证消息相关的业务代码是否正常工作
*/
@Component
public class MessageDataChecker implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(MessageDataChecker.class);
@Autowired
private MessageRepository messageRepository;
@Autowired
private IMessageService messageService;
@Override
public void run(String... args) throws Exception {
logger.info("===== 消息数据检查器开始运行 =====");
// 检查数据库中是否已有消息数据
long count = messageRepository.count();
logger.info("当前数据库中消息数量: {}", count);
// 如果没有消息数据,添加一些测试数据
if (count == 0) {
logger.info("数据库中没有消息数据,开始添加测试数据...");
addTestMessages();
}
// 测试查询方法
testQueryMethods();
// 测试服务层方法
testServiceMethods();
logger.info("===== 消息数据检查器运行结束 =====");
}
private void addTestMessages() {
// 添加第一篇文章的评论
Message message1 = new Message();
message1.setNickname("张三");
message1.setEmail("zhangsan@example.com");
message1.setContent("这是一篇很棒的文章!");
message1.setCreatedAt(new Date());
message1.setArticleid(1);
message1.setParentid(null); // 根评论
messageRepository.save(message1);
// 添加回复
Message reply1 = new Message();
reply1.setNickname("李四");
reply1.setEmail("lisi@example.com");
reply1.setContent("同意你的观点!");
reply1.setCreatedAt(new Date());
reply1.setArticleid(1);
reply1.setParentid(message1.getMessageid()); // 回复第一篇评论
messageRepository.save(reply1);
// 添加第二篇文章的评论
Message message2 = new Message();
message2.setNickname("王五");
message2.setEmail("wangwu@example.com");
message2.setContent("学到了很多东西,谢谢分享!");
message2.setCreatedAt(new Date());
message2.setArticleid(2);
message2.setParentid(null);
messageRepository.save(message2);
logger.info("成功添加了{}条测试消息数据", messageRepository.count());
}
private void testQueryMethods() {
logger.info("===== 测试Repository查询方法 =====");
// 测试根据文章ID查询
List<Message> article1Messages = messageRepository.findByArticleid(1);
logger.info("文章ID为1的消息数量: {}", article1Messages.size());
// 测试查询所有根消息
List<Message> rootMessages = messageRepository.findByParentidIsNull();
logger.info("根消息数量: {}", rootMessages.size());
// 测试根据昵称模糊查询
List<Message> zhangMessages = messageRepository.findByNicknameContaining("");
logger.info("昵称包含'张'的消息数量: {}", zhangMessages.size());
// 测试统计文章评论数量
Long article1Count = messageRepository.countByArticleId(1);
logger.info("文章ID为1的评论数量: {}", article1Count);
// 如果有根消息,测试查询回复
if (!rootMessages.isEmpty()) {
Integer firstRootId = rootMessages.get(0).getMessageid();
List<Message> replies = messageRepository.findByParentid(firstRootId);
logger.info("消息ID为{}的回复数量: {}", firstRootId, replies.size());
}
}
private void testServiceMethods() {
logger.info("===== 测试Service层方法 =====");
// 测试获取所有消息
ResponseMessage<Iterable<Message>> allMessagesResponse = messageService.getAllMessages();
logger.info("获取所有消息: 成功={}, 消息数量={}", allMessagesResponse.isSuccess(),
((List<Message>)allMessagesResponse.getData()).size());
// 测试根据ID获取消息
if (messageRepository.count() > 0) {
Message firstMessage = messageRepository.findAll().iterator().next();
Integer messageId = firstMessage.getMessageid();
ResponseMessage<Message> messageResponse = messageService.getMessageById(messageId);
logger.info("根据ID{}获取消息: 成功={}, 昵称={}", messageId,
messageResponse.isSuccess(),
messageResponse.getData() != null ? messageResponse.getData().getNickname() : "null");
// 测试获取指定文章的评论数量
ResponseMessage<Long> countResponse = messageService.getMessageCountByArticleId(1);
logger.info("获取文章ID为1的评论数量: 成功={}, 数量={}",
countResponse.isSuccess(), countResponse.getData());
// 测试获取根消息
ResponseMessage<List<Message>> rootResponse = messageService.getRootMessages();
logger.info("获取根消息: 成功={}, 数量={}",
rootResponse.isSuccess(),
rootResponse.getData() != null ? rootResponse.getData().size() : 0);
}
// 测试保存新消息
MessageDto newMessage = new MessageDto();
newMessage.setNickname("测试用户");
newMessage.setEmail("test@example.com");
newMessage.setContent("这是一条测试消息");
newMessage.setArticleid(1);
newMessage.setParentid(null);
ResponseMessage<Message> saveResponse = messageService.saveMessage(newMessage);
logger.info("保存新消息: 成功={}, 消息ID={}",
saveResponse.isSuccess(),
saveResponse.getData() != null ? saveResponse.getData().getMessageid() : "null");
// 如果保存成功,测试删除
if (saveResponse.isSuccess() && saveResponse.getData() != null) {
Integer savedId = saveResponse.getData().getMessageid();
ResponseMessage<Message> deleteResponse = messageService.deleteMessage(savedId);
logger.info("删除消息ID{}: 成功={}", savedId, deleteResponse.isSuccess());
}
}
}

View File

@@ -1,199 +0,0 @@
package com.qf.myafterprojecy.service;
import com.qf.myafterprojecy.pojo.Article;
import com.qf.myafterprojecy.pojo.ResponseMessage;
import com.qf.myafterprojecy.pojo.dto.ArticleDto;
import com.qf.myafterprojecy.repository.ArticleRepository;
import com.qf.myafterprojecy.repository.CategoryAttributeRepository;
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 ArticleService implements IArticleService {
private static final Logger log = LoggerFactory.getLogger(ArticleService.class);
@Autowired
private ArticleRepository articleRepository;
@Autowired
private CategoryAttributeRepository categoryAttributeRepository;
@Override
@Transactional(readOnly = true)
public ResponseMessage<Article> getArticleById(String id) {
try {
if (id == null || id.isEmpty()) {
return ResponseMessage.failure("文章ID不能为空");
}
Article article = articleRepository.findById(Integer.parseInt(id))
.orElseThrow(() -> new RuntimeException("文章不存在"));
// 暂时不增加浏览次数,以避免事务问题
// articleRepository.incrementViewCount(id);
return ResponseMessage.success(article);
} catch (Exception e) {
log.error("获取文章失败: {}", e.getMessage());
return ResponseMessage.failure("获取文章失败");
}
}
@Override
@Transactional(readOnly = true)
public ResponseMessage<List<Article>> getArticlesByTitle(String title) {
try {
if (title == null || title.isEmpty()) {
return ResponseMessage.failure("文章标题不能为空");
}
List<Article> articles = articleRepository.findByTitle(title);
return ResponseMessage.success(articles);
} catch (Exception e) {
log.error("根据标题查询文章列表失败: {}", e.getMessage());
return ResponseMessage.failure("根据标题查询文章列表失败");
}
}
@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.failure("获取文章列表失败");
}
}
@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.success(savedArticle);
} catch (DataAccessException e) {
log.error("保存文章失败: {}", e.getMessage());
return ResponseMessage.failure("保存文章失败");
}
}
@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.success(updatedArticle);
} catch (Exception e) {
log.error("更新文章失败: {}", e.getMessage());
return ResponseMessage.failure("更新文章失败");
}
}
@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.success(null);
} catch (Exception e) {
log.error("删除文章失败: {}", e.getMessage());
return ResponseMessage.failure("删除文章失败");
}
}
@Override
@Transactional(readOnly = true)
public ResponseMessage<List<Article>> getArticlesByCategory(Integer categoryId) {
try {
// 为了兼容旧接口,这里需要调整逻辑
// 旧接口使用typeid但我们现在用attributeid
// 可以考虑查询该分类下的所有属性,然后获取相关文章
log.warn("使用了旧接口getArticlesByCategory请考虑迁移到getArticlesByAttribute");
return ResponseMessage.success(articleRepository.findPublishedByAttribute(categoryId));
} catch (DataAccessException e) {
log.error("获取分类文章失败: {}", e.getMessage());
return ResponseMessage.failure("获取分类文章失败");
}
}
@Override
@Transactional(readOnly = true)
public ResponseMessage<List<Article>> getArticlesByAttribute(Integer attributeid) {
try {
if (attributeid == null || attributeid <= 0) {
return ResponseMessage.failure("属性ID无效");
}
// 验证属性是否存在
if (!categoryAttributeRepository.existsById(attributeid)) {
return ResponseMessage.failure("属性不存在");
}
List<Article> articles = articleRepository.findPublishedByAttribute(attributeid);
return ResponseMessage.success(articles);
} catch (DataAccessException e) {
log.error("获取属性文章失败: {}", e.getMessage());
return ResponseMessage.failure("获取属性文章失败");
}
}
@Override
@Transactional(readOnly = true)
public ResponseMessage<List<Article>> getLatestArticlesByAttribute(Integer attributeid) {
try {
if (attributeid == null || attributeid <= 0) {
return ResponseMessage.failure("属性ID无效");
}
// 验证属性是否存在
if (!categoryAttributeRepository.existsById(attributeid)) {
return ResponseMessage.failure("属性不存在");
}
List<Article> articles = articleRepository.findLatestByAttribute(attributeid);
return ResponseMessage.success(articles);
} catch (DataAccessException e) {
log.error("获取最新属性文章失败: {}", e.getMessage());
return ResponseMessage.failure("获取最新属性文章失败");
}
}
@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.failure("获取热门文章失败");
}
}
}

View File

@@ -1,154 +0,0 @@
package com.qf.myafterprojecy.service;
import com.qf.myafterprojecy.pojo.Category_attribute;
import com.qf.myafterprojecy.pojo.ResponseMessage;
import com.qf.myafterprojecy.pojo.dto.CategoryAttributeDto;
import com.qf.myafterprojecy.repository.CategoryAttributeRepository;
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;
@Override
@Transactional(readOnly = true)
public ResponseMessage<Category_attribute> getCategoryAttributeById(Integer id) {
try {
if (id == null || id <= 0) {
return ResponseMessage.failure("属性ID无效");
}
Category_attribute attribute = categoryAttributeRepository.findById(id)
.orElseThrow(() -> new RuntimeException("分类属性不存在"));
return ResponseMessage.success(attribute);
} catch (Exception e) {
log.error("获取分类属性失败: {}", e.getMessage());
return ResponseMessage.failure("获取分类属性失败");
}
}
@Override
@Transactional(readOnly = true)
public ResponseMessage<List<Category_attribute>> getAttributesByCategoryId(Integer categoryId) {
try {
if (categoryId == null || categoryId <= 0) {
return ResponseMessage.failure("分类ID无效");
}
List<Category_attribute> attributes = categoryAttributeRepository.findByCategoryId(categoryId);
return ResponseMessage.success(attributes);
} catch (DataAccessException e) {
log.error("获取分类属性列表失败: {}", e.getMessage());
return ResponseMessage.failure("获取分类属性列表失败");
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public ResponseMessage<Category_attribute> saveCategoryAttribute(CategoryAttributeDto dto) {
try {
// 检查属性名称是否已存在于该分类下
if (categoryAttributeRepository.existsByCategoryidAndAttributename(
dto.getCategoryid(), dto.getAttributename())) {
return ResponseMessage.failure("该分类下已存在同名属性");
}
Category_attribute attribute = new Category_attribute();
BeanUtils.copyProperties(dto, attribute);
Category_attribute savedAttribute = categoryAttributeRepository.save(attribute);
log.info("成功创建分类属性: {}, 分类ID: {}",
savedAttribute.getAttributename(), savedAttribute.getCategoryid());
return ResponseMessage.success(savedAttribute);
} catch (DataAccessException e) {
log.error("保存分类属性失败: {}", e.getMessage());
return ResponseMessage.failure("保存分类属性失败");
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public ResponseMessage<Category_attribute> updateCategoryAttribute(Integer id, CategoryAttributeDto dto) {
try {
if (id == null || id <= 0) {
return ResponseMessage.failure("属性ID无效");
}
Category_attribute attribute = categoryAttributeRepository.findById(id)
.orElseThrow(() -> new RuntimeException("分类属性不存在"));
// 如果修改了属性名称,检查新名称是否已存在
if (!attribute.getAttributename().equals(dto.getAttributename()) &&
categoryAttributeRepository.existsByCategoryidAndAttributename(
dto.getCategoryid(), dto.getAttributename())) {
return ResponseMessage.failure("该分类下已存在同名属性");
}
BeanUtils.copyProperties(dto, attribute);
attribute.setAttributeid(id); // 确保ID不变
Category_attribute updatedAttribute = categoryAttributeRepository.save(attribute);
log.info("成功更新分类属性: ID={}, 名称={}",
updatedAttribute.getAttributeid(), updatedAttribute.getAttributename());
return ResponseMessage.success(updatedAttribute);
} catch (Exception e) {
log.error("更新分类属性失败: {}", e.getMessage());
return ResponseMessage.failure("更新分类属性失败");
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public ResponseMessage<Boolean> deleteCategoryAttribute(Integer id) {
try {
if (id == null || id <= 0) {
return ResponseMessage.failure("属性ID无效");
}
if (!categoryAttributeRepository.existsById(id)) {
return ResponseMessage.failure("分类属性不存在");
}
categoryAttributeRepository.deleteById(id);
log.info("成功删除分类属性: ID={}", id);
return ResponseMessage.success(true);
} catch (Exception e) {
log.error("删除分类属性失败: {}", e.getMessage());
return ResponseMessage.failure("删除分类属性失败");
}
}
@Override
@Transactional(readOnly = true)
public ResponseMessage<Boolean> existsByCategoryAndName(Integer categoryId, String attributeName) {
try {
if (categoryId == null || categoryId <= 0 || attributeName == null || attributeName.isEmpty()) {
return ResponseMessage.failure("参数无效");
}
boolean exists = categoryAttributeRepository.existsByCategoryidAndAttributename(
categoryId, attributeName);
return ResponseMessage.success(exists);
} catch (DataAccessException e) {
log.error("检查分类属性是否存在失败: {}", e.getMessage());
return ResponseMessage.failure("检查分类属性是否存在失败");
}
}
}

View File

@@ -1,42 +1,108 @@
package com.qf.myafterprojecy.service;
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
import com.qf.myafterprojecy.pojo.Article;
import com.qf.myafterprojecy.pojo.ResponseMessage;
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);
}

View File

@@ -1,33 +1,38 @@
package com.qf.myafterprojecy.service;
import com.qf.myafterprojecy.pojo.Category_attribute;
import com.qf.myafterprojecy.pojo.ResponseMessage;
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<Category_attribute> getCategoryAttributeById(Integer id);
ResponseMessage<Categoryattribute> getCategoryAttributeById(Integer id);
/**
* 根据分类ID获取属性列表
* @param categoryId 分类ID
* @return 属性列表
*/
ResponseMessage<List<Category_attribute>> getAttributesByCategoryId(Integer categoryId);
ResponseMessage<List<Categoryattribute>> getAttributesByCategoryId(Integer categoryId);
/**
* 创建新的分类属性
* @param dto 分类属性数据
* @return 创建结果
*/
ResponseMessage<Category_attribute> saveCategoryAttribute(CategoryAttributeDto dto);
ResponseMessage<Categoryattribute> saveCategoryAttribute(CategoryAttributeDto dto);
/**
* 更新分类属性
@@ -35,7 +40,7 @@ public interface ICategoryAttributeService {
* @param dto 分类属性数据
* @return 更新结果
*/
ResponseMessage<Category_attribute> updateCategoryAttribute(Integer id, CategoryAttributeDto dto);
ResponseMessage<Categoryattribute> updateCategoryAttribute(Integer id, CategoryAttributeDto dto);
/**
* 删除分类属性

View File

@@ -1,7 +1,7 @@
package com.qf.myafterprojecy.service;
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
import com.qf.myafterprojecy.pojo.Category;
import com.qf.myafterprojecy.pojo.ResponseMessage;
import com.qf.myafterprojecy.pojo.dto.CategoryDto;
import java.util.List;

View File

@@ -1,69 +1,99 @@
package com.qf.myafterprojecy.service;
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
import com.qf.myafterprojecy.pojo.Message;
import com.qf.myafterprojecy.pojo.ResponseMessage;
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>表示可迭代的消息集合
*/
/**
* 获取所有消息的方法
*
* @return 返回一个ResponseMessage对象其中包含一个可迭代的Message集合
* ResponseMessage是响应消息的包装类Iterable<Message>表示可迭代的消息集合
*/
ResponseMessage<Iterable<Message>> getAllMessages();
/**
* 根据消息ID获取消息的方法
* @param id 消息的唯一标识符
* @return ResponseMessage<Message> 包含消息的响应对象其中Message为消息的具体内容
*/
/**
* 分页
* @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> 返回一个响应消息对象,包含操作结果和保存后的消息信息
*/
/**
* 保存消息的方法
*
* @param message 消息数据传输对象,包含需要保存的消息信息
* @return ResponseMessage<Message> 返回一个响应消息对象,包含操作结果和保存后的消息信息
*/
ResponseMessage<Message> saveMessage(MessageDto message);
/**
* 根据消息ID删除消息的方法
* @param id 要删除的消息ID
* @return ResponseMessage<Message> 包含操作结果的响应消息其中泛型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);
/**
* 获取指定文章的评论数量
* @param articleId 文章ID
* @return 评论数量
*/
ResponseMessage<Long> getMessageCountByArticleId(Integer articleId);
// 删除所有评论
ResponseMessage<Void> deleteAllMessages();
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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("获取热门文章失败");
}
}
}

View File

@@ -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("检查分类属性是否存在失败");
}
}
}

View File

@@ -1,9 +1,11 @@
package com.qf.myafterprojecy.service;
package com.qf.myafterprojecy.service.impl;
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
import com.qf.myafterprojecy.pojo.Category;
import com.qf.myafterprojecy.pojo.ResponseMessage;
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;
@@ -28,14 +30,20 @@ public class CategoryService implements ICategoryService {
public ResponseMessage<Category> getCategoryById(Integer id) {
try {
if (id == null || id <= 0) {
return ResponseMessage.failure("分类ID无效");
return ResponseMessage.badRequest("分类ID无效");
}
Category category = categoryRepository.findById(id)
.orElseThrow(() -> new RuntimeException("分类不存在"));
return ResponseMessage.success(category);
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.failure("获取分类失败");
return ResponseMessage.error("获取分类失败");
}
}
@@ -44,10 +52,10 @@ public class CategoryService implements ICategoryService {
public ResponseMessage<List<Category>> getAllCategories() {
try {
List<Category> categories = categoryRepository.findAll();
return ResponseMessage.success(categories);
return ResponseMessage.success(categories, "获取分类列表成功");
} catch (DataAccessException e) {
log.error("获取分类列表失败: {}", e.getMessage());
return ResponseMessage.failure("获取分类列表失败");
return ResponseMessage.error("获取分类列表失败");
}
}
@@ -57,19 +65,19 @@ public class CategoryService implements ICategoryService {
try {
// 检查分类名称是否已存在
if (categoryRepository.existsByTypename(categoryDto.getTypename())) {
return ResponseMessage.failure("分类名称已存在");
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.success(savedCategory);
return ResponseMessage.save(true, savedCategory);
} catch (DataAccessException e) {
log.error("保存分类失败: {}", e.getMessage());
return ResponseMessage.failure("保存分类失败");
return ResponseMessage.error("保存分类失败");
}
}
@@ -78,26 +86,32 @@ public class CategoryService implements ICategoryService {
public ResponseMessage<Category> updateCategory(Integer id, CategoryDto categoryDto) {
try {
if (id == null || id <= 0) {
return ResponseMessage.failure("分类ID无效");
return ResponseMessage.badRequest("分类ID无效");
}
Category category = categoryRepository.findById(id)
.orElseThrow(() -> new RuntimeException("分类不存在"));
// 如果修改了分类名称检查新名称是否已存在
if (!category.getTypename().equals(categoryDto.getTypename()) &&
if (!category.getTypename().equals(categoryDto.getTypename()) &&
categoryRepository.existsByTypename(categoryDto.getTypename())) {
return ResponseMessage.failure("分类名称已存在");
return ResponseMessage.badRequest("分类名称已存在");
}
BeanUtils.copyProperties(categoryDto, category);
category.setUpdatedAt(LocalDateTime.now());
Category updatedCategory = categoryRepository.save(category);
return ResponseMessage.success(updatedCategory);
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.failure("更新分类失败");
return ResponseMessage.error("更新分类失败");
}
}
@@ -106,20 +120,20 @@ public class CategoryService implements ICategoryService {
public ResponseMessage<Boolean> deleteCategory(Integer id) {
try {
if (id == null || id <= 0) {
return ResponseMessage.failure("分类ID无效");
return ResponseMessage.badRequest("分类ID无效");
}
if (!categoryRepository.existsById(id)) {
return ResponseMessage.failure("分类不存在");
return ResponseMessage.notFound("分类不存在");
}
// 注意实际项目中可能需要先检查是否有文章引用该分类
// 如果有可能需要先处理文章或者禁止删除
categoryRepository.deleteById(id);
return ResponseMessage.success(true);
return ResponseMessage.delete(true);
} catch (Exception e) {
log.error("删除分类失败: {}", e.getMessage());
return ResponseMessage.failure("删除分类失败");
return ResponseMessage.error("删除分类失败");
}
}
@@ -128,14 +142,14 @@ public class CategoryService implements ICategoryService {
public ResponseMessage<List<Category>> searchCategoriesByTypename(String typename) {
try {
if (typename == null || typename.trim().isEmpty()) {
return ResponseMessage.failure("分类名称不能为空");
return ResponseMessage.badRequest("分类名称不能为空");
}
List<Category> categories = categoryRepository.findByTypenameContaining(typename);
return ResponseMessage.success(categories);
return ResponseMessage.success(categories, "搜索分类成功");
} catch (DataAccessException e) {
log.error("搜索分类失败: {}", e.getMessage());
return ResponseMessage.failure("搜索分类失败");
return ResponseMessage.error("搜索分类失败");
}
}
}

View File

@@ -1,14 +1,20 @@
package com.qf.myafterprojecy.service;
package com.qf.myafterprojecy.service.impl;
import com.qf.myafterprojecy.exceptopn.ResponseMessage;
import com.qf.myafterprojecy.pojo.Message;
import com.qf.myafterprojecy.pojo.ResponseMessage;
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;
@@ -16,11 +22,13 @@ 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;
@@ -29,10 +37,10 @@ public class MessageService implements IMessageService {
try {
logger.info("查询所有消息");
Iterable<Message> messages = messageRepository.findAll();
return ResponseMessage.success(messages, "查询成功", true);
return ResponseMessage.success(messages, "查询成功");
} catch (DataAccessException e) {
logger.error("查询所有消息失败", e);
return ResponseMessage.failure("查询消息失败:" + e.getMessage());
return ResponseMessage.error("查询消息失败:" + e.getMessage());
}
}
@@ -40,74 +48,76 @@ public class MessageService implements IMessageService {
public ResponseMessage<Message> getMessageById(Integer id) {
if (id == null || id <= 0) {
logger.warn("获取消息时ID无效: {}", id);
return ResponseMessage.failure("消息ID无效");
return ResponseMessage.badRequest("消息ID无效");
}
try {
logger.info("根据ID查询消息: {}", id);
Optional<Message> messageOptional = messageRepository.findById(id);
if (messageOptional.isPresent()) {
return ResponseMessage.success(messageOptional.get(), "查询成功", true);
return ResponseMessage.success(messageOptional.get(), "查询成功");
} else {
logger.warn("未找到ID为{}的消息", id);
return ResponseMessage.failure("未找到指定消息");
return ResponseMessage.notFound("未找到指定消息");
}
} catch (DataAccessException e) {
logger.error("查询消息失败: {}", id, e);
return ResponseMessage.failure("查询消息失败:" + e.getMessage());
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.failure("回复的父消息不存在");
return ResponseMessage.notFound("回复的父消息不存在");
}
}
Message savedMessage = messageRepository.save(message);
logger.info("消息保存成功: {}", savedMessage.getMessageid());
return ResponseMessage.success(savedMessage, "保存成功", true);
return ResponseMessage.save(true, savedMessage);
} catch (DataAccessException e) {
logger.error("保存消息失败", e);
return ResponseMessage.failure("保存消息失败:" + e.getMessage());
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.failure("消息ID无效");
return ResponseMessage.badRequest("消息ID无效");
}
try {
logger.info("删除消息: {}", id);
if (messageRepository.existsById(id)) {
@@ -119,14 +129,14 @@ public class MessageService implements IMessageService {
logger.info("同时删除了{}条回复消息", replies.size());
}
logger.info("消息删除成功: {}", id);
return ResponseMessage.success(null, "删除成功", true);
return ResponseMessage.delete(true);
} else {
logger.warn("未找到要删除的消息: {}", id);
return ResponseMessage.failure("未找到要删除的消息");
return ResponseMessage.notFound("未找到要删除的消息");
}
} catch (DataAccessException e) {
logger.error("删除消息失败: {}", id, e);
return ResponseMessage.failure("删除消息失败:" + e.getMessage());
return ResponseMessage.error("删除消息失败:" + e.getMessage());
}
}
@@ -134,16 +144,16 @@ public class MessageService implements IMessageService {
public ResponseMessage<List<Message>> getMessagesByArticleId(Integer articleId) {
if (articleId == null || articleId <= 0) {
logger.warn("根据文章ID查询消息时ID无效: {}", articleId);
return ResponseMessage.failure("文章ID无效");
return ResponseMessage.badRequest("文章ID无效");
}
try {
logger.info("根据文章ID查询消息: {}", articleId);
List<Message> messages = messageRepository.findByArticleid(articleId);
return ResponseMessage.success(messages, "查询成功", true);
return ResponseMessage.success(messages, "查询成功");
} catch (DataAccessException e) {
logger.error("根据文章ID查询消息失败: {}", articleId, e);
return ResponseMessage.failure("查询消息失败:" + e.getMessage());
return ResponseMessage.error("查询消息失败:" + e.getMessage());
}
}
@@ -152,10 +162,10 @@ public class MessageService implements IMessageService {
try {
logger.info("查询所有根消息");
List<Message> messages = messageRepository.findByParentidIsNull();
return ResponseMessage.success(messages, "查询成功", true);
return ResponseMessage.success(messages, "查询成功");
} catch (DataAccessException e) {
logger.error("查询根消息失败", e);
return ResponseMessage.failure("查询消息失败:" + e.getMessage());
return ResponseMessage.error("查询消息失败:" + e.getMessage());
}
}
@@ -163,15 +173,34 @@ public class MessageService implements IMessageService {
public ResponseMessage<List<Message>> getRepliesByParentId(Integer parentId) {
if (parentId == null || parentId <= 0) {
logger.warn("根据父消息ID查询回复时ID无效: {}", parentId);
return ResponseMessage.failure("父消息ID无效");
return ResponseMessage.badRequest("父消息ID无效");
}
try {
logger.info("根据父消息ID查询回复: {}", parentId);
List<Message> replies = messageRepository.findByParentid(parentId);
return ResponseMessage.success(replies, "查询成功", true);
return ResponseMessage.success(replies, "查询成功");
} catch (DataAccessException e) {
logger.error("查询回复消息失败: {}", parentId, e);
return ResponseMessage.failure("查询消息失败:" + e.getMessage());
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());
}
}
@@ -179,33 +208,79 @@ public class MessageService implements IMessageService {
public ResponseMessage<List<Message>> searchMessagesByNickname(String nickname) {
if (StringUtils.isEmpty(nickname)) {
logger.warn("根据昵称查询消息时昵称为空");
return ResponseMessage.failure("昵称不能为空");
return ResponseMessage.badRequest("昵称不能为空");
}
try {
logger.info("根据昵称查询消息: {}", nickname);
List<Message> messages = messageRepository.findByNicknameContaining(nickname);
return ResponseMessage.success(messages, "查询成功", true);
return ResponseMessage.success(messages, "查询成功");
} catch (DataAccessException e) {
logger.error("根据昵称查询消息失败: {}", nickname, e);
return ResponseMessage.failure("查询消息失败:" + e.getMessage());
return ResponseMessage.error("查询消息失败:" + e.getMessage());
}
}
// 删除所有评论
@Override
public ResponseMessage<Long> getMessageCountByArticleId(Integer articleId) {
if (articleId == null || articleId <= 0) {
logger.warn("获取文章评论数量时ID无效: {}", articleId);
return ResponseMessage.failure("文章ID无效");
@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);
Long count = messageRepository.countByArticleId(articleId);
return ResponseMessage.success(count, "查询成功", true);
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.failure("查询评论数量失败:" + e.getMessage());
logger.error("获取文章回复数量失败: {}", articleId, e);
return ResponseMessage.error("查询回复数量失败:" + e.getMessage());
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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("服务器异常");
}
}
}

View 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;
}
}

View File

@@ -0,0 +1,5 @@
{"properties": [{
"name": "Content-Type,",
"type": "java.lang.String",
"description": "A description for 'Content-Type,'"
}]}

View 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

View 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

View File

@@ -1,110 +1,43 @@
# 应用服务 WEB 访问端口
server.port=8080
# ====================================================================
# 应用基本配置 - 通用配置
# ====================================================================
# 环境激活配置
# 说明:默认激活开发环境,生产环境部署时应通过命令行参数或环境变量覆盖
spring.profiles.active=dev
server.port=7070
# 应用名称(通用配置)
spring.application.name=web_project
# 数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/webproject?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&createDatabaseIfNotExist=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=10
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-auto改为none
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=false
spring.jpa.properties.hibernate.format_sql=true
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
# 暂时禁用Hibernate二级缓存和查询缓存
# spring.jpa.properties.hibernate.cache.use_second_level_cache=true
# spring.jpa.properties.hibernate.cache.use_query_cache=true
缓存配置
spring.cache.type=redis
spring.cache.redis.time-to-live=1800000
spring.cache.redis.key-prefix=CACHE_
spring.cache.redis.use-key-prefix=true
spring.cache.redis.cache-null-values=false
Redis配置
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=123456
spring.redis.database=0
spring.redis.timeout=10000ms
Redis连接池优化配置
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=10000ms
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.min-idle=2
spring.redis.lettuce.shutdown-timeout=100ms
# 日志配置
logging.level.root=INFO
logging.level.com.qf.myafterprojecy=DEBUG
logging.level.org.springframework.security=INFO
logging.level.org.hibernate.SQL=WARN
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
# 日志文件配置
logging.file.name=logs/web_project.log
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
# Actuator配置 - 生产环境建议限制暴露的端点
management.endpoints.web.exposure.include=health,info,metrics,prometheus
management.endpoint.health.show-details=when_authorized
management.metrics.export.prometheus.enabled=true
# JWT配置 - 生产环境应使用更安全的密钥和环境变量
jwt.secret=mySecretKey
jwt.expiration=86400000
jwt.header=Authorization
jwt.token-prefix=Bearer
# CORS配置 - 生产环境应限制允许的源
cors.allowed-origins=http://localhost:3000
cors.allowed-methods=GET,POST,PUT,DELETE,OPTIONS
cors.allowed-headers=*,
Content-Type,
X-Requested-With,
accept,
Origin,
Access-Control-Request-Method,
Access-Control-Request-Headers,
Authorization
cors.allow-credentials=true
cors.max-age=3600
# 安全配置增强
security.basic.enabled=false
security.ignored=/css/**,/js/**,/images/**,/favicon.ico
# 生产环境建议配置
# server.ssl.key-store=classpath:keystore.p12
# server.ssl.key-store-password=password
# server.ssl.key-store-type=PKCS12
# server.ssl.key-alias=tomcat
# ====================================================================
# 会话与编码配置 - 通用配置
# ====================================================================
# 会话配置
server.servlet.session.timeout=30m
server.session.tracking-modes=cookie
# 国际化配置
#spring.mvc.locale-resolver=fixed
#spring.mvc.locale=zh_CN
#
## 响应编码配置
#spring.http.encoding.charset=UTF-8
#spring.http.encoding.enabled=true
#spring.http.encoding.force=true
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
# ====================================================================