feat: 添加用户数据初始化功能并优化配置管理

refactor: 重构CORS配置使其从配置文件读取
refactor: 移除无用分类和消息初始化代码

build: 更新pom.xml依赖项

docs: 拆分应用配置文件为环境特定配置
This commit is contained in:
qingfeng1121
2025-12-11 12:42:53 +08:00
parent eb1f70d431
commit 51392bf807
22 changed files with 5470 additions and 5893 deletions

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,31 +13,50 @@ 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);
// 明确列出允许的HTTP方法比使用通配符更安全
config.addAllowedMethod("GET");
config.addAllowedMethod("POST");
config.addAllowedMethod("PUT");
config.addAllowedMethod("DELETE");
config.addAllowedMethod("OPTIONS");
config.addAllowedMethod("PATCH");
// 允许的请求头
config.addAllowedHeader("*");
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");
@@ -44,16 +64,16 @@ public class CorsConfig {
config.addExposedHeader("Accept");
config.addExposedHeader("Access-Control-Allow-Origin");
config.addExposedHeader("Access-Control-Allow-Credentials");
// 设置预检请求的有效期(秒)
config.setMaxAge(86400L); // 增加到24小时减少预检请求次数
// 设置预检请求的有效期(秒),从配置文件读取
config.setMaxAge(maxAge);
// 创建基于URL的CORS配置源
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
// 为所有路径应用CORS配置
source.registerCorsConfiguration("/**", config);
// 返回配置好的CORS过滤器
return new CorsFilter(source);
}

View File

@@ -20,6 +20,7 @@ public class PasswordEncoderConfig {
@Bean
public PasswordEncoder passwordEncoder() {
// 强度设置为10这是一个平衡安全性和性能的值
// 数值越高,计算成本越大,安全性越好
return new BCryptPasswordEncoder(10);
}
}

View File

@@ -93,7 +93,7 @@ public class SecurityConfig {
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", "86400");
response.setHeader("Access-Control-Max-Age", "3600");
// 如果是预检请求直接返回200
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {

View File

@@ -21,6 +21,7 @@ import java.util.List;
*/
@RestController
@RequestMapping("/api/articles")
@Validated
public class ArticleController {

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

@@ -10,7 +10,7 @@ public class NonsenseDto {
private Integer status;//状态 0未发表 1已发表 2已删除
private Date time;
public Integer getId() {
return id;

View File

@@ -1,13 +1,8 @@
package com.qf.myafterprojecy.pojo.dto;
import javax.ws.rs.DefaultValue;
public class PageDto {
private Integer status;
@DefaultValue("0")
private Integer page;
@DefaultValue("10")
private Integer size;
// 数据验证
public void validate() {