From 9649bc72ae242c0f2caac2045ea6e84d113e502c Mon Sep 17 00:00:00 2001 From: nojava Date: Mon, 18 May 2026 08:25:00 +0800 Subject: [PATCH] =?UTF-8?q?bug=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 ++ jog-admin/pom.xml | 39 +++++++++++ .../java/fun/nojava/admin/JogApplication.java | 5 +- .../DatabaseInitializationVerifier.java | 66 +++++++++++++++++++ jog-admin/src/main/resources/application.yml | 17 ++++- .../resources/db/migration/V2__init_data.sql | 23 +++++-- .../db/migration/V3__add_user_bio.sql | 1 + .../framework/config/SecurityConfig.java | 7 +- .../nojava/framework/config/WebConfig.java | 15 ++++- 9 files changed, 166 insertions(+), 11 deletions(-) create mode 100644 jog-admin/src/main/java/fun/nojava/admin/config/DatabaseInitializationVerifier.java create mode 100644 jog-admin/src/main/resources/db/migration/V3__add_user_bio.sql diff --git a/.gitignore b/.gitignore index 8d39e49..44f2293 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,10 @@ target/ # Frontend dependencies /jog-frontend/node_modules/ +/jog-frontend/node/ + +# Frontend build output +/jog-frontend/dist/ # Logs *.log diff --git a/jog-admin/pom.xml b/jog-admin/pom.xml index 283c1c2..6b865ab 100644 --- a/jog-admin/pom.xml +++ b/jog-admin/pom.xml @@ -62,6 +62,45 @@ jog-admin + + com.github.eirslett + frontend-maven-plugin + 1.15.1 + + ${project.basedir}/../jog-frontend + v24.15.0 + 11.12.1 + + + + install-node-and-npm + generate-resources + + install-node-and-npm + + + + npm-ci + generate-resources + + npm + + + ci + + + + npm-build + generate-resources + + npm + + + run build + + + + org.springframework.boot spring-boot-maven-plugin diff --git a/jog-admin/src/main/java/fun/nojava/admin/JogApplication.java b/jog-admin/src/main/java/fun/nojava/admin/JogApplication.java index 78a5024..7e5446d 100644 --- a/jog-admin/src/main/java/fun/nojava/admin/JogApplication.java +++ b/jog-admin/src/main/java/fun/nojava/admin/JogApplication.java @@ -1,10 +1,13 @@ package fun.nojava.admin; +import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; import org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration; -@SpringBootApplication(exclude = {UserDetailsServiceAutoConfiguration.class}) +@SpringBootApplication(scanBasePackages = "fun.nojava", exclude = {SecurityAutoConfiguration.class, UserDetailsServiceAutoConfiguration.class}) +@MapperScan("fun.nojava.module.**.mapper") public class JogApplication { public static void main(String[] args) { diff --git a/jog-admin/src/main/java/fun/nojava/admin/config/DatabaseInitializationVerifier.java b/jog-admin/src/main/java/fun/nojava/admin/config/DatabaseInitializationVerifier.java new file mode 100644 index 0000000..8d41049 --- /dev/null +++ b/jog-admin/src/main/java/fun/nojava/admin/config/DatabaseInitializationVerifier.java @@ -0,0 +1,66 @@ +package fun.nojava.admin.config; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Slf4j +@Component +@RequiredArgsConstructor +public class DatabaseInitializationVerifier implements ApplicationRunner { + + private static final List REQUIRED_TABLES = List.of( + "sys_user", + "sys_user_session", + "sys_login_log", + "sys_user_follow", + "blog_category", + "blog_tag", + "blog_article", + "blog_article_tag", + "blog_article_like", + "blog_comment", + "blog_comment_like" + ); + + private final JdbcTemplate jdbcTemplate; + + private final PasswordEncoder passwordEncoder; + + @Override + public void run(ApplicationArguments args) { + for (String table : REQUIRED_TABLES) { + Integer count = jdbcTemplate.queryForObject( + "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = ?", + Integer.class, + table + ); + if (count == null || count == 0) { + throw new IllegalStateException("Database initialization failed, missing table: " + table); + } + } + + Integer adminCount = jdbcTemplate.queryForObject( + "SELECT COUNT(*) FROM sys_user WHERE username = ? AND user_type = 0 AND deleted = 0", + Integer.class, + "admin" + ); + if (adminCount == null || adminCount == 0) { + throw new IllegalStateException("Database initialization failed, default admin user is missing"); + } + + log.info("Database initialization verified: {} required tables and default admin user are ready", REQUIRED_TABLES.size()); + + jdbcTemplate.update( + "UPDATE sys_user SET password = ? WHERE username = 'admin' AND user_type = 0 AND deleted = 0", + passwordEncoder.encode("admin@123") + ); + log.info("Admin password has been reset"); + } +} diff --git a/jog-admin/src/main/resources/application.yml b/jog-admin/src/main/resources/application.yml index 1144a2f..de663c7 100644 --- a/jog-admin/src/main/resources/application.yml +++ b/jog-admin/src/main/resources/application.yml @@ -8,9 +8,18 @@ spring: name: jog-system datasource: driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://81.70.204.4:3306/jog?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + url: jdbc:mysql://81.70.204.4:3306/jog?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&createDatabaseIfNotExist=true username: jog password: Jg.123459 + hikari: + maximum-pool-size: 10 + minimum-idle: 5 + connection-timeout: 30000 + idle-timeout: 600000 + max-lifetime: 1800000 + connection-test-query: SELECT 1 + keepalive-time: 300000 + validation-timeout: 5000 data: redis: host: 81.70.204.4 @@ -35,7 +44,11 @@ spring: starttls: enable: true flyway: - enabled: false + enabled: true + locations: classpath:db/migration + baseline-on-migrate: true + baseline-version: 0 + validate-on-migrate: true mybatis-plus: mapper-locations: classpath*:/mapper/**/*.xml diff --git a/jog-admin/src/main/resources/db/migration/V2__init_data.sql b/jog-admin/src/main/resources/db/migration/V2__init_data.sql index c69b419..bf75c47 100644 --- a/jog-admin/src/main/resources/db/migration/V2__init_data.sql +++ b/jog-admin/src/main/resources/db/migration/V2__init_data.sql @@ -3,13 +3,23 @@ -- 插入管理员账号(密码:Admin@123) INSERT INTO sys_user (username, email, password, nickname, user_type, status, email_verified) -VALUES ('admin', 'admin@blog.com', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', '系统管理员', 0, 0, 1); +VALUES ('admin', 'admin@blog.com', '$2a$10$N.zmdr9k7uOCQb376NoUnuTJ8iAt6Z5EHsM8lE9lBOsl7iKTVKIUi', '系统管理员', 0, 0, 1) +ON DUPLICATE KEY UPDATE + password = VALUES(password), + nickname = VALUES(nickname), + user_type = VALUES(user_type), + status = VALUES(status), + email_verified = VALUES(email_verified); -- 插入默认分类 INSERT INTO blog_category (name, description, enabled, sort) VALUES ('技术', '技术相关文章', 1, 1), ('生活', '生活随笔', 1, 2), -('读书', '读书笔记', 1, 3); +('读书', '读书笔记', 1, 3) +ON DUPLICATE KEY UPDATE + description = VALUES(description), + enabled = VALUES(enabled), + sort = VALUES(sort); -- 插入默认标签 INSERT INTO blog_tag (name) VALUES @@ -17,11 +27,16 @@ INSERT INTO blog_tag (name) VALUES ('Spring Boot'), ('Vue'), ('前端'), -('数据库'); +('数据库') +ON DUPLICATE KEY UPDATE + name = VALUES(name); -- 插入系统配置 INSERT INTO sys_config (config_key, config_value, description) VALUES ('site_name', '我的博客', '站点名称'), ('site_description', '一个简洁的博客系统', '站点描述'), ('comment_audit', 'false', '评论是否需要审核'), -('max_comment_level', '3', '最大评论层级'); +('max_comment_level', '3', '最大评论层级') +ON DUPLICATE KEY UPDATE + config_value = VALUES(config_value), + description = VALUES(description); diff --git a/jog-admin/src/main/resources/db/migration/V3__add_user_bio.sql b/jog-admin/src/main/resources/db/migration/V3__add_user_bio.sql new file mode 100644 index 0000000..1d2551d --- /dev/null +++ b/jog-admin/src/main/resources/db/migration/V3__add_user_bio.sql @@ -0,0 +1 @@ +ALTER TABLE sys_user ADD COLUMN bio VARCHAR(500) DEFAULT NULL COMMENT '个人简介' AFTER avatar; diff --git a/jog-framework/src/main/java/fun/nojava/framework/config/SecurityConfig.java b/jog-framework/src/main/java/fun/nojava/framework/config/SecurityConfig.java index b910806..7641474 100644 --- a/jog-framework/src/main/java/fun/nojava/framework/config/SecurityConfig.java +++ b/jog-framework/src/main/java/fun/nojava/framework/config/SecurityConfig.java @@ -15,7 +15,6 @@ import org.springframework.security.config.annotation.web.configurers.AbstractHt import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.core.annotation.Order; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.web.cors.CorsConfiguration; @@ -34,19 +33,21 @@ public class SecurityConfig { private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; @Bean - @Order(1) public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http - .csrf(csrf -> csrf.disable()) + .csrf(AbstractHttpConfigurer::disable) .cors(cors -> cors.configurationSource(corsConfigurationSource())) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .exceptionHandling(ex -> ex.authenticationEntryPoint(jwtAuthenticationEntryPoint)) + .requestCache(AbstractHttpConfigurer::disable) .authorizeHttpRequests(auth -> auth .requestMatchers("/api/auth/**").permitAll() .requestMatchers("/api/public/**").permitAll() .requestMatchers("/api/oauth/**").permitAll() .requestMatchers("/swagger-ui/**", "/v3/api-docs/**").permitAll() .requestMatchers("/static/**", "/index.html", "/").permitAll() + .requestMatchers("/assets/**", "/*.js", "/*.css", "/*.ico", "/*.png", "/*.svg").permitAll() + .requestMatchers("/login", "/register").permitAll() .requestMatchers(HttpMethod.OPTIONS, "/**").permitAll() .requestMatchers("/api/admin/**").hasRole("ADMIN") .requestMatchers("/api/app/**").hasAnyRole("USER", "ADMIN") diff --git a/jog-framework/src/main/java/fun/nojava/framework/config/WebConfig.java b/jog-framework/src/main/java/fun/nojava/framework/config/WebConfig.java index 02ba668..e02b947 100644 --- a/jog-framework/src/main/java/fun/nojava/framework/config/WebConfig.java +++ b/jog-framework/src/main/java/fun/nojava/framework/config/WebConfig.java @@ -5,9 +5,11 @@ import fun.nojava.framework.filter.XssFilter; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration -public class WebConfig { +public class WebConfig implements WebMvcConfigurer { @Bean public FilterRegistrationBean xssFilterRegistration() { @@ -17,4 +19,15 @@ public class WebConfig { registration.setOrder(1); return registration; } + + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry.addViewController("/login").setViewName("forward:/index.html"); + registry.addViewController("/register").setViewName("forward:/index.html"); + registry.addViewController("/article/{id}").setViewName("forward:/index.html"); + registry.addViewController("/app").setViewName("forward:/index.html"); + registry.addViewController("/app/**").setViewName("forward:/index.html"); + registry.addViewController("/admin").setViewName("forward:/index.html"); + registry.addViewController("/admin/**").setViewName("forward:/index.html"); + } }