# Multi-Module Architecture Refactor Implementation Plan > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** 将 novalon-manage-api 从单体模块重构为网关 + 单体多模块架构,实现模块化、统一认证授权、独立部署和性能优化。 **Architecture:** 采用网关(manage-gateway)+ 应用层(manage-app)+ 业务模块(manage-sys、manage-audit、manage-notify、manage-file)+ 公共模块(manage-common、manage-db)的分层架构。网关统一处理JWT认证、RBAC权限和限流熔断,应用层聚合所有业务模块,通过Caffeine缓存优化性能,使用Docker容器化部署。 **Tech Stack:** Java 21, Spring Boot 3.4.1, Spring WebFlux, Spring Security, R2DBC, PostgreSQL, JWT, Caffeine, Docker, Maven --- ## Phase 1: Preparation (1-2 days) ### Task 1: Create manage-gateway module structure **Files:** - Create: `novalon-manage-api/manage-gateway/pom.xml` - Create: `novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/GatewayApplication.java` - Create: `novalon-manage-api/manage-gateway/src/main/resources/application.yml` - Create: `novalon-manage-api/manage-gateway/src/main/resources/application-dev.yml` - Create: `novalon-manage-api/manage-gateway/src/main/resources/application-prod.yml` - Create: `novalon-manage-api/manage-gateway/Dockerfile` **Step 1: Create manage-gateway pom.xml** ```xml 4.0.0 cn.novalon.manage novalon-manage-api 1.0.0 manage-gateway jar Manage Gateway Gateway module for Novalon Manage API cn.novalon.manage manage-common ${project.version} org.springframework.boot spring-boot-starter-webflux org.springframework.boot spring-boot-starter-actuator org.springframework.cloud spring-cloud-starter-gateway 4.1.0 io.micrometer micrometer-registry-prometheus org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin cn.novalon.manage.gateway.GatewayApplication ``` **Step 2: Create GatewayApplication.java** ```java package cn.novalon.manage.gateway; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.context.annotation.Bean; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route("manage-app", r -> r .path("/api/**") .uri("http://manage-app:8081")) .build(); } } ``` **Step 3: Create application.yml** ```yaml server: port: 8080 spring: application: name: manage-gateway cloud: gateway: routes: - id: manage-app uri: http://manage-app:8081 predicates: - Path=/api/** default-filters: - name: Retry args: retries: 3 statuses: BAD_GATEWAY,SERVICE_UNAVAILABLE methods: GET,POST backoff: firstBackoff: 10ms maxBackoff: 50ms factor: 2 basedOnPreviousValue: false management: endpoints: web: exposure: include: health,info,metrics base-path: /actuator endpoint: health: show-details: always metrics: tags: application: ${spring.application.name} logging: level: cn.novalon.manage: DEBUG org.springframework.cloud.gateway: DEBUG ``` **Step 4: Create Dockerfile** ```dockerfile FROM openjdk:21-jdk-slim WORKDIR /app COPY manage-gateway/target/manage-gateway-1.0.0.jar app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"] ``` **Step 5: Compile and verify** Run: `cd novalon-manage-api && mvn clean compile -pl manage-gateway -am` Expected: BUILD SUCCESS **Step 6: Commit** ```bash git add novalon-manage-api/manage-gateway/ git commit -m "feat: create manage-gateway module structure" ``` --- ### Task 2: Create manage-app module structure **Files:** - Create: `novalon-manage-api/manage-app/pom.xml` - Create: `novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/ManageApplication.java` - Create: `novalon-manage-api/manage-app/src/main/resources/application.yml` - Create: `novalon-manage-api/manage-app/src/main/resources/application-dev.yml` - Create: `novalon-manage-api/manage-app/src/main/resources/application-prod.yml` - Create: `novalon-manage-api/manage-app/Dockerfile` **Step 1: Create manage-app pom.xml** ```xml 4.0.0 cn.novalon.manage novalon-manage-api 1.0.0 manage-app jar Manage App Application module for Novalon Manage API cn.novalon.manage manage-sys ${project.version} cn.novalon.manage manage-audit ${project.version} cn.novalon.manage manage-notify ${project.version} cn.novalon.manage manage-file ${project.version} cn.novalon.manage manage-db ${project.version} cn.novalon.manage manage-common ${project.version} org.springframework.boot spring-boot-starter-webflux org.springframework.boot spring-boot-starter-actuator io.micrometer micrometer-registry-prometheus org.postgresql r2dbc-postgresql org.postgresql postgresql org.flywaydb flyway-core org.flywaydb flyway-database-postgresql org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin cn.novalon.manage.app.ManageApplication ``` **Step 2: Create ManageApplication.java** ```java package cn.novalon.manage.app; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.ConfigurationPropertiesScan; import org.springframework.context.annotation.ComponentScan; @SpringBootApplication @ConfigurationPropertiesScan(basePackages = "cn.novalon.manage") @ComponentScan(basePackages = "cn.novalon.manage") public class ManageApplication { public static void main(String[] args) { SpringApplication.run(ManageApplication.class, args); } } ``` **Step 3: Create application.yml** ```yaml server: port: 8081 spring: application: name: manage-app r2dbc: url: r2dbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5432}/${DB_NAME:novalon_manage} username: ${DB_USERNAME:postgres} password: ${DB_PASSWORD:postgres} pool: initial-size: 10 max-size: 50 max-idle-time: 30m max-life-time: 1h acquire-timeout: 5s flyway: enabled: true locations: classpath:db/migration management: endpoints: web: exposure: include: health,info,metrics,env,loggers base-path: /actuator endpoint: health: show-details: always metrics: tags: application: ${spring.application.name} environment: ${spring.profiles.active} logging: level: cn.novalon.manage: DEBUG org.springframework.r2dbc: DEBUG ``` **Step 4: Create Dockerfile** ```dockerfile FROM openjdk:21-jdk-slim WORKDIR /app COPY manage-app/target/manage-app-1.0.0.jar app.jar EXPOSE 8081 ENTRYPOINT ["java", "-jar", "app.jar"] ``` **Step 5: Compile and verify** Run: `cd novalon-manage-api && mvn clean compile -pl manage-app -am` Expected: BUILD SUCCESS **Step 6: Commit** ```bash git add novalon-manage-api/manage-app/ git commit -m "feat: create manage-app module structure" ``` --- ### Task 3: Create manage-common module structure **Files:** - Create: `novalon-manage-api/manage-common/pom.xml` - Create: `novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/` - Create: `novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/util/` - Create: `novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/config/` - Create: `novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/filter/` - Create: `novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/exception/` **Step 1: Create manage-common pom.xml** ```xml 4.0.0 cn.novalon.manage novalon-manage-api 1.0.0 manage-common jar Manage Common Common module for Novalon Manage API org.springframework.boot spring-boot-starter-webflux org.springframework.boot spring-boot-starter-validation org.springframework.boot spring-boot-starter-cache com.github.ben-manes.caffeine caffeine org.apache.commons commons-lang3 io.jsonwebtoken jjwt-api io.jsonwebtoken jjwt-impl runtime io.jsonwebtoken jjwt-jackson runtime org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin true ``` **Step 2: Create package structure** Run: `mkdir -p novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/{util,config,filter,exception,dto}` Expected: Directories created successfully **Step 3: Create common exception classes** Create: `novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/exception/BusinessException.java` ```java package cn.novalon.manage.common.exception; public class BusinessException extends RuntimeException { private final String code; private final String message; public BusinessException(String code, String message) { super(message); this.code = code; this.message = message; } public String getCode() { return code; } @Override public String getMessage() { return message; } } ``` **Step 4: Compile and verify** Run: `cd novalon-manage-api && mvn clean compile -pl manage-common -am` Expected: BUILD SUCCESS **Step 5: Commit** ```bash git add novalon-manage-api/manage-common/ git commit -m "feat: create manage-common module structure" ``` --- ### Task 4: Create manage-db module structure **Files:** - Create: `novalon-manage-api/manage-db/pom.xml` - Create: `novalon-manage-api/manage-db/src/main/java/cn/novalon/manage/db/` - Create: `novalon-manage-api/manage-db/src/main/java/cn/novalon/manage/db/entity/` - Create: `novalon-manage-api/manage-db/src/main/java/cn/novalon/manage/db/repository/` - Create: `novalon-manage-api/manage-db/src/main/resources/db/migration/` **Step 1: Create manage-db pom.xml** ```xml 4.0.0 cn.novalon.manage novalon-manage-api 1.0.0 manage-db jar Manage DB Database module for Novalon Manage API org.springframework.boot spring-boot-starter-data-r2dbc org.postgresql r2dbc-postgresql org.postgresql postgresql org.flywaydb flyway-core org.flywaydb flyway-database-postgresql org.mapstruct mapstruct 1.5.5.Final org.mapstruct mapstruct-processor 1.5.5.Final provided org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin true ``` **Step 2: Copy migration scripts** Run: `cp -r novalon-manage-api/manage-sys/src/main/resources/db/migration/* novalon-manage-api/manage-db/src/main/resources/db/migration/` Expected: Migration scripts copied successfully **Step 3: Compile and verify** Run: `cd novalon-manage-api && mvn clean compile -pl manage-db -am` Expected: BUILD SUCCESS **Step 4: Commit** ```bash git add novalon-manage-api/manage-db/ git commit -m "feat: create manage-db module structure" ``` --- ### Task 5: Create manage-audit module structure **Files:** - Create: `novalon-manage-api/manage-audit/pom.xml` - Create: `novalon-manage-api/manage-audit/src/main/java/cn/novalon/manage/audit/` - Create: `novalon-manage-api/manage-audit/src/main/java/cn/novalon/manage/audit/handler/` - Create: `novalon-manage-api/manage-audit/src/main/java/cn/novalon/manage/audit/service/` - Create: `novalon-manage-api/manage-audit/src/main/java/cn/novalon/manage/audit/dto/` **Step 1: Create manage-audit pom.xml** ```xml 4.0.0 cn.novalon.manage novalon-manage-api 1.0.0 manage-audit jar Manage Audit Audit module for Novalon Manage API cn.novalon.manage manage-db ${project.version} cn.novalon.manage manage-common ${project.version} org.springframework.boot spring-boot-starter-webflux org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin true ``` **Step 2: Compile and verify** Run: `cd novalon-manage-api && mvn clean compile -pl manage-audit -am` Expected: BUILD SUCCESS **Step 3: Commit** ```bash git add novalon-manage-api/manage-audit/ git commit -m "feat: create manage-audit module structure" ``` --- ### Task 6: Create manage-notify module structure **Files:** - Create: `novalon-manage-api/manage-notify/pom.xml` - Create: `novalon-manage-api/manage-notify/src/main/java/cn/novalon/manage/notify/` - Create: `novalon-manage-api/manage-notify/src/main/java/cn/novalon/manage/notify/handler/` - Create: `novalon-manage-api/manage-notify/src/main/java/cn/novalon/manage/notify/service/` - Create: `novalon-manage-api/manage-notify/src/main/java/cn/novalon/manage/notify/config/` **Step 1: Create manage-notify pom.xml** ```xml 4.0.0 cn.novalon.manage novalon-manage-api 1.0.0 manage-notify jar Manage Notify Notify module for Novalon Manage API cn.novalon.manage manage-db ${project.version} cn.novalon.manage manage-common ${project.version} org.springframework.boot spring-boot-starter-webflux org.springframework.boot spring-boot-starter-websocket org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin true ``` **Step 2: Compile and verify** Run: `cd novalon-manage-api && mvn clean compile -pl manage-notify -am` Expected: BUILD SUCCESS **Step 3: Commit** ```bash git add novalon-manage-api/manage-notify/ git commit -m "feat: create manage-notify module structure" ``` --- ### Task 7: Create manage-file module structure **Files:** - Create: `novalon-manage-api/manage-file/pom.xml` - Create: `novalon-manage-api/manage-file/src/main/java/cn/novalon/manage/file/` - Create: `novalon-manage-api/manage-file/src/main/java/cn/novalon/manage/file/handler/` - Create: `novalon-manage-api/manage-file/src/main/java/cn/novalon/manage/file/service/` - Create: `novalon-manage-api/manage-file/src/main/java/cn/novalon/manage/file/config/` **Step 1: Create manage-file pom.xml** ```xml 4.0.0 cn.novalon.manage novalon-manage-api 1.0.0 manage-file jar Manage File File module for Novalon Manage API cn.novalon.manage manage-db ${project.version} cn.novalon.manage manage-common ${project.version} org.springframework.boot spring-boot-starter-webflux org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-maven-plugin true ``` **Step 2: Compile and verify** Run: `cd novalon-manage-api && mvn clean compile -pl manage-file -am` Expected: BUILD SUCCESS **Step 3: Commit** ```bash git add novalon-manage-api/manage-file/ git commit -m "feat: create manage-file module structure" ``` --- ### Task 8: Update parent pom.xml with new modules **Files:** - Modify: `novalon-manage-api/pom.xml` **Step 1: Update modules section** Read: `novalon-manage-api/pom.xml` Find: `` section and replace with: ```xml manage-gateway manage-app manage-sys manage-audit manage-notify manage-file manage-common manage-db ``` **Step 2: Verify build** Run: `cd novalon-manage-api && mvn clean compile` Expected: BUILD SUCCESS **Step 3: Commit** ```bash git add novalon-manage-api/pom.xml git commit -m "feat: update parent pom.xml with new modules" ``` --- ## Phase 2: Module Extraction (3-5 days) ### Task 9: Extract common utilities to manage-common **Files:** - Modify: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/utils/SnowflakeId.java` - Create: `novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/util/SnowflakeId.java` - Modify: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/config/JwtProperties.java` - Create: `novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/config/JwtProperties.java` **Step 1: Move SnowflakeId to manage-common** Read: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/utils/SnowflakeId.java` Copy content to: `novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/util/SnowflakeId.java` Update package declaration: `package cn.novalon.manage.common.util;` **Step 2: Move JwtProperties to manage-common** Read: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/config/JwtProperties.java` Copy content to: `novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/config/JwtProperties.java` Update package declaration: `package cn.novalon.manage.common.config;` **Step 3: Update manage-sys imports** Find all files in manage-sys that import SnowflakeId or JwtProperties and update imports: ```bash cd novalon-manage-api/manage-sys find src -name "*.java" -exec grep -l "SnowflakeId\|JwtProperties" {} \; ``` Update imports from: - `import cn.novalon.manage.sys.utils.SnowflakeId;` to `import cn.novalon.manage.common.util.SnowflakeId;` - `import cn.novalon.manage.sys.config.JwtProperties;` to `import cn.novalon.manage.common.config.JwtProperties;` **Step 4: Remove old files from manage-sys** Run: `rm novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/utils/SnowflakeId.java` Run: `rm novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/config/JwtProperties.java` **Step 5: Compile and verify** Run: `cd novalon-manage-api && mvn clean compile` Expected: BUILD SUCCESS **Step 6: Commit** ```bash git add novalon-manage-api/manage-common/ novalon-manage-api/manage-sys/ git commit -m "refactor: extract common utilities to manage-common" ``` --- ### Task 10: Extract database entities to manage-db **Files:** - Move: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/infrastructure/db/entity/*` to `novalon-manage-api/manage-db/src/main/java/cn/novalon/manage/db/entity/` - Move: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/infrastructure/db/repository/*` to `novalon-manage-api/manage-db/src/main/java/cn/novalon/manage/db/repository/` **Step 1: Move entity classes** Run: `mkdir -p novalon-manage-api/manage-db/src/main/java/cn/novalon/manage/db/entity` Run: `mv novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/infrastructure/db/entity/* novalon-manage-api/manage-db/src/main/java/cn/novalon/manage/db/entity/` **Step 2: Update entity package declarations** For each entity file in manage-db/entity, update package declaration: From: `package cn.novalon.manage.sys.infrastructure.db.entity;` To: `package cn.novalon.manage.db.entity;` **Step 3: Move repository interfaces** Run: `mkdir -p novalon-manage-api/manage-db/src/main/java/cn/novalon/manage/db/repository` Run: `mv novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/infrastructure/db/repository/* novalon-manage-api/manage-db/src/main/java/cn/novalon/manage/db/repository/` **Step 4: Update repository package declarations** For each repository file in manage-db/repository, update package declaration: From: `package cn.novalon.manage.sys.infrastructure.db.repository;` To: `package cn.novalon.manage.db.repository;` **Step 5: Update imports in manage-sys** Find all files in manage-sys that import entities or repositories and update imports: ```bash cd novalon-manage-api/manage-sys find src -name "*.java" -exec grep -l "cn.novalon.manage.sys.infrastructure.db" {} \; ``` Update imports from: - `import cn.novalon.manage.sys.infrastructure.db.entity.*;` to `import cn.novalon.manage.db.entity.*;` - `import cn.novalon.manage.sys.infrastructure.db.repository.*;` to `import cn.novalon.manage.db.repository.*;` **Step 6: Compile and verify** Run: `cd novalon-manage-api && mvn clean compile` Expected: BUILD SUCCESS **Step 7: Commit** ```bash git add novalon-manage-api/manage-db/ novalon-manage-api/manage-sys/ git commit -m "refactor: extract database entities to manage-db" ``` --- ### Task 11: Refactor manage-sys module **Files:** - Modify: `novalon-manage-api/manage-sys/pom.xml` - Modify: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/ManageSysApplication.java` **Step 1: Update manage-sys pom.xml dependencies** Read: `novalon-manage-api/manage-sys/pom.xml` Replace dependencies section with: ```xml cn.novalon.manage manage-db ${project.version} cn.novalon.manage manage-common ${project.version} org.springframework.boot spring-boot-starter-webflux org.springframework.boot spring-boot-starter-validation org.springdoc springdoc-openapi-starter-webflux-ui org.springframework.boot spring-boot-starter-test test ``` **Step 2: Update ManageSysApplication.java** Read: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/ManageSysApplication.java` Update package scan to include new packages: ```java package cn.novalon.manage.sys; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.ConfigurationPropertiesScan; import org.springframework.context.annotation.ComponentScan; @SpringBootApplication @ConfigurationPropertiesScan(basePackages = "cn.novalon.manage") @ComponentScan(basePackages = "cn.novalon.manage") public class ManageSysApplication { public static void main(String[] args) { SpringApplication.run(ManageSysApplication.class, args); } } ``` **Step 3: Compile and verify** Run: `cd novalon-manage-api && mvn clean compile -pl manage-sys -am` Expected: BUILD SUCCESS **Step 4: Commit** ```bash git add novalon-manage-api/manage-sys/ git commit -m "refactor: update manage-sys module dependencies" ``` --- ### Task 12: Migrate audit functionality to manage-audit **Files:** - Move: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/handler/log/SysLogHandler.java` to `novalon-manage-api/manage-audit/src/main/java/cn/novalon/manage/audit/handler/` - Move: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/core/service/impl/OperationLogService.java` to `novalon-manage-api/manage-audit/src/main/java/cn/novalon/manage/audit/service/impl/` - Move: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/core/service/impl/SysLoginLogService.java` to `novalon-manage-api/manage-audit/src/main/java/cn/novalon/manage/audit/service/impl/` - Move: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/core/service/impl/SysExceptionLogService.java` to `novalon-manage-api/manage-audit/src/main/java/cn/novalon/manage/audit/service/impl/` **Step 1: Move audit handlers** Run: `mkdir -p novalon-manage-api/manage-audit/src/main/java/cn/novalon/manage/audit/handler` Run: `mv novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/handler/log/SysLogHandler.java novalon-manage-api/manage-audit/src/main/java/cn/novalon/manage/audit/handler/` **Step 2: Move audit services** Run: `mkdir -p novalon-manage-api/manage-audit/src/main/java/cn/novalon/manage/audit/service/impl` Run: `mv novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/core/service/impl/OperationLogService.java novalon-manage-api/manage-audit/src/main/java/cn/novalon/manage/audit/service/impl/` Run: `mv novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/core/service/impl/SysLoginLogService.java novalon-manage-api/manage-audit/src/main/java/cn/novalon/manage/audit/service/impl/` Run: `mv novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/core/service/impl/SysExceptionLogService.java novalon-manage-api/manage-audit/src/main/java/cn/novalon/manage/audit/service/impl/` **Step 3: Update package declarations** For each moved file, update package declaration: From: `package cn.novalon.manage.sys.handler.log;` to `package cn.novalon.manage.audit.handler;` From: `package cn.novalon.manage.sys.core.service.impl;` to `package cn.novalon.manage.audit.service.impl;` **Step 4: Compile and verify** Run: `cd novalon-manage-api && mvn clean compile -pl manage-audit -am` Expected: BUILD SUCCESS **Step 5: Commit** ```bash git add novalon-manage-api/manage-audit/ novalon-manage-api/manage-sys/ git commit -m "refactor: migrate audit functionality to manage-audit" ``` --- ### Task 13: Migrate notify functionality to manage-notify **Files:** - Move: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/handler/notice/SysNoticeHandler.java` to `novalon-manage-api/manage-notify/src/main/java/cn/novalon/manage/notify/handler/` - Move: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/handler/message/SysUserMessageHandler.java` to `novalon-manage-api/manage-notify/src/main/java/cn/novalon/manage/notify/handler/` - Move: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/core/service/impl/SysNoticeService.java` to `novalon-manage-api/manage-notify/src/main/java/cn/novalon/manage/notify/service/impl/` - Move: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/core/service/impl/SysUserMessageService.java` to `novalon-manage-api/manage-notify/src/main/java/cn/novalon/manage/notify/service/impl/` - Move: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/core/service/impl/WebSocketServiceImpl.java` to `novalon-manage-api/manage-notify/src/main/java/cn/novalon/manage/notify/service/impl/` - Move: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/config/WebSocketConfig.java` to `novalon-manage-api/manage-notify/src/main/java/cn/novalon/manage/notify/config/` **Step 1: Move notify handlers** Run: `mkdir -p novalon-manage-api/manage-notify/src/main/java/cn/novalon/manage/notify/handler` Run: `mv novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/handler/notice/SysNoticeHandler.java novalon-manage-api/manage-notify/src/main/java/cn/novalon/manage/notify/handler/` Run: `mv novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/handler/message/SysUserMessageHandler.java novalon-manage-api/manage-notify/src/main/java/cn/novalon/manage/notify/handler/` **Step 2: Move notify services** Run: `mkdir -p novalon-manage-api/manage-notify/src/main/java/cn/novalon/manage/notify/service/impl` Run: `mv novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/core/service/impl/SysNoticeService.java novalon-manage-api/manage-notify/src/main/java/cn/novalon/manage/notify/service/impl/` Run: `mv novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/core/service/impl/SysUserMessageService.java novalon-manage-api/manage-notify/src/main/java/cn/novalon/manage/notify/service/impl/` Run: `mv novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/core/service/impl/WebSocketServiceImpl.java novalon-manage-api/manage-notify/src/main/java/cn/novalon/manage/notify/service/impl/` **Step 3: Move notify config** Run: `mkdir -p novalon-manage-api/manage-notify/src/main/java/cn/novalon/manage/notify/config` Run: `mv novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/config/WebSocketConfig.java novalon-manage-api/manage-notify/src/main/java/cn/novalon/manage/notify/config/` **Step 4: Update package declarations** For each moved file, update package declaration: From: `package cn.novalon.manage.sys.handler.notice;` to `package cn.novalon.manage.notify.handler;` From: `package cn.novalon.manage.sys.handler.message;` to `package cn.novalon.manage.notify.handler;` From: `package cn.novalon.manage.sys.core.service.impl;` to `package cn.novalon.manage.notify.service.impl;` From: `package cn.novalon.manage.sys.config;` to `package cn.novalon.manage.notify.config;` **Step 5: Compile and verify** Run: `cd novalon-manage-api && mvn clean compile -pl manage-notify -am` Expected: BUILD SUCCESS **Step 6: Commit** ```bash git add novalon-manage-api/manage-notify/ novalon-manage-api/manage-sys/ git commit -m "refactor: migrate notify functionality to manage-notify" ``` --- ### Task 14: Migrate file functionality to manage-file **Files:** - Move: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/handler/file/SysFileHandler.java` to `novalon-manage-api/manage-file/src/main/java/cn/novalon/manage/file/handler/` - Move: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/core/service/impl/SysFileService.java` to `novalon-manage-api/manage-file/src/main/java/cn/novalon/manage/file/service/impl/` - Move: `novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/config/MultipartConfig.java` to `novalon-manage-api/manage-file/src/main/java/cn/novalon/manage/file/config/` **Step 1: Move file handler** Run: `mkdir -p novalon-manage-api/manage-file/src/main/java/cn/novalon/manage/file/handler` Run: `mv novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/handler/file/SysFileHandler.java novalon-manage-api/manage-file/src/main/java/cn/novalon/manage/file/handler/` **Step 2: Move file service** Run: `mkdir -p novalon-manage-api/manage-file/src/main/java/cn/novalon/manage/file/service/impl` Run: `mv novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/core/service/impl/SysFileService.java novalon-manage-api/manage-file/src/main/java/cn/novalon/manage/file/service/impl/` **Step 3: Move file config** Run: `mkdir -p novalon-manage-api/manage-file/src/main/java/cn/novalon/manage/file/config` Run: `mv novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/config/MultipartConfig.java novalon-manage-api/manage-file/src/main/java/cn/novalon/manage/file/config/` **Step 4: Update package declarations** For each moved file, update package declaration: From: `package cn.novalon.manage.sys.handler.file;` to `package cn.novalon.manage.file.handler;` From: `package cn.novalon.manage.sys.core.service.impl;` to `package cn.novalon.manage.file.service.impl;` From: `package cn.novalon.manage.sys.config;` to `package cn.novalon.manage.file.config;` **Step 5: Compile and verify** Run: `cd novalon-manage-api && mvn clean compile -pl manage-file -am` Expected: BUILD SUCCESS **Step 6: Commit** ```bash git add novalon-manage-api/manage-file/ novalon-manage-api/manage-sys/ git commit -m "refactor: migrate file functionality to manage-file" ``` --- ## Phase 3: Gateway and Application Layer (2-3 days) ### Task 15: Implement JWT authentication filter in manage-gateway **Files:** - Create: `novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/filter/JwtAuthenticationFilter.java` - Create: `novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/util/JwtTokenProvider.java` - Create: `novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/dto/JwtUser.java` **Step 1: Create JwtUser DTO** Create: `novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/dto/JwtUser.java` ```java package cn.novalon.manage.common.dto; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; @Data @NoArgsConstructor @AllArgsConstructor public class JwtUser { private String userId; private String username; private List roles; private List permissions; } ``` **Step 2: Create JwtTokenProvider** Create: `novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/util/JwtTokenProvider.java` ```java package cn.novalon.manage.common.util; import cn.novalon.manage.common.config.JwtProperties; import cn.novalon.manage.common.dto.JwtUser; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; import org.springframework.stereotype.Component; import javax.crypto.SecretKey; import java.util.Date; import java.util.List; @Component public class JwtTokenProvider { private final JwtProperties jwtProperties; private final SecretKey key; public JwtTokenProvider(JwtProperties jwtProperties) { this.jwtProperties = jwtProperties; this.key = Keys.hmacShaKeyFor(jwtProperties.getSecret().getBytes()); } public String generateToken(JwtUser user) { Date now = new Date(); Date expiryDate = new Date(now.getTime() + jwtProperties.getExpiration() * 1000); return Jwts.builder() .subject(user.getUserId()) .claim("username", user.getUsername()) .claim("roles", user.getRoles()) .claim("permissions", user.getPermissions()) .issuedAt(now) .expiration(expiryDate) .signWith(key) .compact(); } public JwtUser parseToken(String token) { Claims claims = Jwts.parserBuilder() .setSigningKey(key) .build() .parseClaimsJws(token) .getBody(); JwtUser user = new JwtUser(); user.setUserId(claims.getSubject()); user.setUsername(claims.get("username", String.class)); user.setRoles(claims.get("roles", List.class)); user.setPermissions(claims.get("permissions", List.class)); return user; } public boolean validateToken(String token) { try { Jwts.parserBuilder() .setSigningKey(key) .build() .parseClaimsJws(token); return true; } catch (Exception e) { return false; } } } ``` **Step 3: Create JwtAuthenticationFilter** Create: `novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/filter/JwtAuthenticationFilter.java` ```java package cn.novalon.manage.gateway.filter; import cn.novalon.manage.common.dto.JwtUser; import cn.novalon.manage.common.util.JwtTokenProvider; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @Component public class JwtAuthenticationFilter extends AbstractGatewayFilterFactory { private final JwtTokenProvider jwtTokenProvider; public JwtAuthenticationFilter(JwtTokenProvider jwtTokenProvider) { super(Config.class); this.jwtTokenProvider = jwtTokenProvider; } @Override public GatewayFilter apply(Config config) { return (exchange, chain) -> { ServerHttpRequest request = exchange.getRequest(); String path = request.getPath().value(); if (isPublicPath(path)) { return chain.filter(exchange); } String token = extractToken(request); if (token == null || !jwtTokenProvider.validateToken(token)) { return unauthorized(exchange.getResponse()); } JwtUser user = jwtTokenProvider.parseToken(token); addHeaders(exchange, user); return chain.filter(exchange); }; } private boolean isPublicPath(String path) { return path.startsWith("/api/auth/login") || path.startsWith("/api/auth/register"); } private String extractToken(ServerHttpRequest request) { String bearerToken = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION); if (bearerToken != null && bearerToken.startsWith("Bearer ")) { return bearerToken.substring(7); } return null; } private void addHeaders(ServerWebExchange exchange, JwtUser user) { ServerHttpRequest mutatedRequest = exchange.getRequest().mutate() .header("X-User-Id", user.getUserId()) .header("X-User-Username", user.getUsername()) .header("X-User-Roles", String.join(",", user.getRoles())) .header("X-User-Permissions", String.join(",", user.getPermissions())) .build(); exchange.mutate().request(mutatedRequest).build(); } private Mono unauthorized(ServerHttpResponse response) { response.setStatusCode(HttpStatus.UNAUTHORIZED); return response.setComplete(); } public static class Config { } } ``` **Step 4: Compile and verify** Run: `cd novalon-manage-api && mvn clean compile -pl manage-gateway,manage-common -am` Expected: BUILD SUCCESS **Step 5: Commit** ```bash git add novalon-manage-api/manage-gateway/ novalon-manage-api/manage-common/ git commit -m "feat: implement JWT authentication filter in manage-gateway" ``` --- ### Task 16: Implement RBAC authorization filter in manage-gateway **Files:** - Create: `novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/filter/RbacAuthorizationFilter.java` - Create: `novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/config/PermissionConfig.java` **Step 1: Create PermissionConfig** Create: `novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/config/PermissionConfig.java` ```java package cn.novalon.manage.gateway.config; import org.springframework.context.annotation.Configuration; import java.util.HashMap; import java.util.Map; @Configuration public class PermissionConfig { public static final Map PATH_PERMISSIONS = new HashMap<>(); static { PATH_PERMISSIONS.put("/api/sys/users", "user:read"); PATH_PERMISSIONS.put("/api/sys/users/**", "user:write"); PATH_PERMISSIONS.put("/api/sys/roles", "role:read"); PATH_PERMISSIONS.put("/api/sys/roles/**", "role:write"); PATH_PERMISSIONS.put("/api/sys/menus", "menu:read"); PATH_PERMISSIONS.put("/api/sys/menus/**", "menu:write"); PATH_PERMISSIONS.put("/api/sys/config", "config:read"); PATH_PERMISSIONS.put("/api/sys/config/**", "config:write"); PATH_PERMISSIONS.put("/api/audit/logs", "audit:read"); PATH_PERMISSIONS.put("/api/notify/notices", "notice:read"); PATH_PERMISSIONS.put("/api/notify/notices/**", "notice:write"); PATH_PERMISSIONS.put("/api/file/files", "file:read"); PATH_PERMISSIONS.put("/api/file/files/**", "file:write"); } public static String getRequiredPermission(String path, String method) { String permissionKey = path; if (!PATH_PERMISSIONS.containsKey(path)) { permissionKey = path.substring(0, path.lastIndexOf('/')); } return PATH_PERMISSIONS.get(permissionKey); } } ``` **Step 2: Create RbacAuthorizationFilter** Create: `novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/filter/RbacAuthorizationFilter.java` ```java package cn.novalon.manage.gateway.filter; import cn.novalon.manage.gateway.config.PermissionConfig; import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; import org.springframework.http.HttpStatus; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; import java.util.Arrays; import java.util.List; @Component public class RbacAuthorizationFilter extends AbstractGatewayFilterFactory { @Override public GatewayFilter apply(Config config) { return (exchange, chain) -> { ServerHttpRequest request = exchange.getRequest(); String path = request.getPath().value(); String method = request.getMethod().name(); if (isPublicPath(path)) { return chain.filter(exchange); } List permissions = request.getHeaders().get("X-User-Permissions"); List roles = request.getHeaders().get("X-User-Roles"); if (permissions == null || roles == null) { return forbidden(exchange.getResponse()); } String requiredPermission = PermissionConfig.getRequiredPermission(path, method); if (requiredPermission != null && !hasPermission(permissions, requiredPermission)) { return forbidden(exchange.getResponse()); } return chain.filter(exchange); }; } private boolean isPublicPath(String path) { return path.startsWith("/api/auth/login") || path.startsWith("/api/auth/register"); } private boolean hasPermission(List permissions, String requiredPermission) { return permissions.contains(requiredPermission) || permissions.contains("*:*"); } private Mono forbidden(ServerHttpResponse response) { response.setStatusCode(HttpStatus.FORBIDDEN); return response.setComplete(); } public static class Config { } } ``` **Step 3: Update GatewayApplication to register filters** Read: `novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/GatewayApplication.java` Update to include filters: ```java package cn.novalon.manage.gateway; import cn.novalon.manage.gateway.filter.JwtAuthenticationFilter; import cn.novalon.manage.gateway.filter.RbacAuthorizationFilter; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.context.annotation.Bean; @SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route("manage-app", r -> r .path("/api/**") .filters(f -> f .filter(new JwtAuthenticationFilter.Config()) .filter(new RbacAuthorizationFilter.Config())) .uri("http://manage-app:8081")) .build(); } } ``` **Step 4: Compile and verify** Run: `cd novalon-manage-api && mvn clean compile -pl manage-gateway -am` Expected: BUILD SUCCESS **Step 5: Commit** ```bash git add novalon-manage-api/manage-gateway/ git commit -m "feat: implement RBAC authorization filter in manage-gateway" ``` --- ### Task 17: Configure Caffeine cache in manage-common **Files:** - Create: `novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/config/CacheConfig.java` - Create: `novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/service/TokenBlacklistService.java` **Step 1: Create CacheConfig** Create: `novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/config/CacheConfig.java` ```java package cn.novalon.manage.common.config; import com.github.benmanes.caffeine.cache.Caffeine; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.caffeine.CaffeineCacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.concurrent.TimeUnit; @Configuration @EnableCaching public class CacheConfig { @Bean public CacheManager cacheManager() { CaffeineCacheManager cacheManager = new CaffeineCacheManager(); cacheManager.setCaffeine(caffeineCacheBuilder()); return cacheManager; } @Bean public com.github.benmanes.caffeine.cache.Cache caffeineCache() { return Caffeine.newBuilder() .maximumSize(10000) .expireAfterWrite(10, TimeUnit.MINUTES) .build(); } private Caffeine caffeineCacheBuilder() { return Caffeine.newBuilder() .maximumSize(10000) .expireAfterWrite(10, TimeUnit.MINUTES); } } ``` **Step 2: Create TokenBlacklistService** Create: `novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/service/TokenBlacklistService.java` ```java package cn.novalon.manage.common.service; import com.github.benmanes.caffeine.cache.Cache; import org.springframework.stereotype.Service; import java.util.concurrent.TimeUnit; @Service public class TokenBlacklistService { private final Cache tokenBlacklistCache; public TokenBlacklistService() { this.tokenBlacklistCache = com.github.benmanes.caffeine.cache.Caffeine.newBuilder() .maximumSize(10000) .expireAfterWrite(2, TimeUnit.HOURS) .build(); } public void addToBlacklist(String tokenId) { tokenBlacklistCache.put(tokenId, true); } public boolean isBlacklisted(String tokenId) { return tokenBlacklistCache.getIfPresent(tokenId) != null; } } ``` **Step 3: Compile and verify** Run: `cd novalon-manage-api && mvn clean compile -pl manage-common -am` Expected: BUILD SUCCESS **Step 4: Commit** ```bash git add novalon-manage-api/manage-common/ git commit -m "feat: configure Caffeine cache in manage-common" ``` --- ### Task 18: Create Docker Compose configuration **Files:** - Create: `novalon-manage-system/docker-compose.yml` - Create: `novalon-manage-system/.env.example` **Step 1: Create docker-compose.yml** Create: `novalon-manage-system/docker-compose.yml` ```yaml version: '3.8' services: postgres: image: postgres:15-alpine container_name: novalon-postgres environment: POSTGRES_DB: novalon_manage POSTGRES_USER: ${DB_USERNAME:-postgres} POSTGRES_PASSWORD: ${DB_PASSWORD:-postgres} ports: - "5432:5432" volumes: - postgres_data:/var/lib/postgresql/data - ./docs/sql:/docker-entrypoint-initdb.d networks: - novalon-network healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 10s timeout: 5s retries: 5 manage-gateway: build: context: ./novalon-manage-api dockerfile: manage-gateway/Dockerfile container_name: novalon-gateway ports: - "8080:8080" environment: SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-prod} JWT_SECRET: ${JWT_SECRET} JWT_EXPIRATION: ${JWT_EXPIRATION:-7200} APP_SERVICE_URL: http://manage-app:8081 depends_on: manage-app: condition: service_healthy networks: - novalon-network healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"] interval: 30s timeout: 10s retries: 3 manage-app: build: context: ./novalon-manage-api dockerfile: manage-app/Dockerfile container_name: novalon-app ports: - "8081:8081" environment: SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-prod} DB_HOST: postgres DB_PORT: 5432 DB_NAME: novalon_manage DB_USERNAME: ${DB_USERNAME:-postgres} DB_PASSWORD: ${DB_PASSWORD:-postgres} JWT_SECRET: ${JWT_SECRET} depends_on: postgres: condition: service_healthy networks: - novalon-network healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8081/actuator/health"] interval: 30s timeout: 10s retries: 3 volumes: postgres_data: networks: novalon-network: driver: bridge ``` **Step 2: Create .env.example** Create: `novalon-manage-system/.env.example` ```bash # Database Configuration DB_USERNAME=postgres DB_PASSWORD=your_secure_password # JWT Configuration JWT_SECRET=your_jwt_secret_key_minimum_256_bits JWT_EXPIRATION=7200 # Spring Profile SPRING_PROFILES_ACTIVE=prod ``` **Step 3: Commit** ```bash git add novalon-manage-system/docker-compose.yml novalon-manage-system/.env.example git commit -m "feat: add Docker Compose configuration" ``` --- ## Phase 4: Testing and Optimization (2-3 days) ### Task 19: Write unit tests for manage-common **Files:** - Create: `novalon-manage-api/manage-common/src/test/java/cn/novalon/manage/common/util/JwtTokenProviderTest.java` - Create: `novalon-manage-api/manage-common/src/test/java/cn/novalon/manage/common/service/TokenBlacklistServiceTest.java` **Step 1: Write JwtTokenProviderTest** Create: `novalon-manage-api/manage-common/src/test/java/cn/novalon/manage/common/util/JwtTokenProviderTest.java` ```java package cn.novalon.manage.common.util; import cn.novalon.manage.common.config.JwtProperties; import cn.novalon.manage.common.dto.JwtUser; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import static org.junit.jupiter.api.Assertions.*; class JwtTokenProviderTest { private JwtTokenProvider jwtTokenProvider; private JwtProperties jwtProperties; @BeforeEach void setUp() { jwtProperties = new JwtProperties(); jwtProperties.setSecret("test-secret-key-for-testing-purposes-only"); jwtProperties.setExpiration(7200); jwtTokenProvider = new JwtTokenProvider(jwtProperties); } @Test void testGenerateToken() { JwtUser user = new JwtUser(); user.setUserId("user123"); user.setUsername("testuser"); user.setRoles(Arrays.asList("ADMIN")); user.setPermissions(Arrays.asList("user:read", "user:write")); String token = jwtTokenProvider.generateToken(user); assertNotNull(token); assertFalse(token.isEmpty()); } @Test void testParseToken() { JwtUser originalUser = new JwtUser(); originalUser.setUserId("user123"); originalUser.setUsername("testuser"); originalUser.setRoles(Arrays.asList("ADMIN")); originalUser.setPermissions(Arrays.asList("user:read", "user:write")); String token = jwtTokenProvider.generateToken(originalUser); JwtUser parsedUser = jwtTokenProvider.parseToken(token); assertEquals(originalUser.getUserId(), parsedUser.getUserId()); assertEquals(originalUser.getUsername(), parsedUser.getUsername()); assertEquals(originalUser.getRoles(), parsedUser.getRoles()); assertEquals(originalUser.getPermissions(), parsedUser.getPermissions()); } @Test void testValidateToken() { JwtUser user = new JwtUser(); user.setUserId("user123"); user.setUsername("testuser"); user.setRoles(Arrays.asList("ADMIN")); user.setPermissions(Arrays.asList("user:read")); String validToken = jwtTokenProvider.generateToken(user); assertTrue(jwtTokenProvider.validateToken(validToken)); String invalidToken = "invalid.token.here"; assertFalse(jwtTokenProvider.validateToken(invalidToken)); } } ``` **Step 2: Write TokenBlacklistServiceTest** Create: `novalon-manage-api/manage-common/src/test/java/cn/novalon/manage/common/service/TokenBlacklistServiceTest.java` ```java package cn.novalon.manage.common.service; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; class TokenBlacklistServiceTest { private TokenBlacklistService tokenBlacklistService; @BeforeEach void setUp() { tokenBlacklistService = new TokenBlacklistService(); } @Test void testAddToBlacklist() { String tokenId = "token123"; tokenBlacklistService.addToBlacklist(tokenId); assertTrue(tokenBlacklistService.isBlacklisted(tokenId)); } @Test void testIsBlacklisted() { String tokenId = "token456"; assertFalse(tokenBlacklistService.isBlacklisted(tokenId)); tokenBlacklistService.addToBlacklist(tokenId); assertTrue(tokenBlacklistService.isBlacklisted(tokenId)); } @Test void testMultipleTokens() { String token1 = "token1"; String token2 = "token2"; String token3 = "token3"; tokenBlacklistService.addToBlacklist(token1); tokenBlacklistService.addToBlacklist(token2); tokenBlacklistService.addToBlacklist(token3); assertTrue(tokenBlacklistService.isBlacklisted(token1)); assertTrue(tokenBlacklistService.isBlacklisted(token2)); assertTrue(tokenBlacklistService.isBlacklisted(token3)); } } ``` **Step 3: Run tests** Run: `cd novalon-manage-api && mvn test -pl manage-common` Expected: All tests pass **Step 4: Commit** ```bash