Files
novalon-manage-system/docs/plans/2026-03-13-multi-module-refactor-implementation-plan.md
张翔 dc53a233b9 refactor(domain): 将领域模型移动到common模块
重构项目结构,将分散在各模块的领域模型统一移动到manage-common模块
更新相关依赖和引用路径
调整docker-compose配置和测试标记
添加新的Playwright测试配置
优化Dockerfile构建过程
2026-03-13 19:58:57 +08:00

64 KiB
Raw Permalink Blame History

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 version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>cn.novalon.manage</groupId>
        <artifactId>novalon-manage-api</artifactId>
        <version>1.0.0</version>
    </parent>

    <artifactId>manage-gateway</artifactId>
    <packaging>jar</packaging>

    <name>Manage Gateway</name>
    <description>Gateway module for Novalon Manage API</description>

    <dependencies>
        <dependency>
            <groupId>cn.novalon.manage</groupId>
            <artifactId>manage-common</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
            <version>4.1.0</version>
        </dependency>
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-prometheus</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>cn.novalon.manage.gateway.GatewayApplication</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Step 2: Create GatewayApplication.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

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

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

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 version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>cn.novalon.manage</groupId>
        <artifactId>novalon-manage-api</artifactId>
        <version>1.0.0</version>
    </parent>

    <artifactId>manage-app</artifactId>
    <packaging>jar</packaging>

    <name>Manage App</name>
    <description>Application module for Novalon Manage API</description>

    <dependencies>
        <dependency>
            <groupId>cn.novalon.manage</groupId>
            <artifactId>manage-sys</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>cn.novalon.manage</groupId>
            <artifactId>manage-audit</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>cn.novalon.manage</groupId>
            <artifactId>manage-notify</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>cn.novalon.manage</groupId>
            <artifactId>manage-file</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>cn.novalon.manage</groupId>
            <artifactId>manage-db</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>cn.novalon.manage</groupId>
            <artifactId>manage-common</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-prometheus</artifactId>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>r2dbc-postgresql</artifactId>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
        </dependency>
        <dependency>
            <groupId>org.flywaydb</groupId>
            <artifactId>flyway-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.flywaydb</groupId>
            <artifactId>flyway-database-postgresql</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>cn.novalon.manage.app.ManageApplication</mainClass>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Step 2: Create ManageApplication.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

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

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

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 version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>cn.novalon.manage</groupId>
        <artifactId>novalon-manage-api</artifactId>
        <version>1.0.0</version>
    </parent>

    <artifactId>manage-common</artifactId>
    <packaging>jar</packaging>

    <name>Manage Common</name>
    <description>Common module for Novalon Manage API</description>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.ben-manes.caffeine</groupId>
            <artifactId>caffeine</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

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

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

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 version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>cn.novalon.manage</groupId>
        <artifactId>novalon-manage-api</artifactId>
        <version>1.0.0</version>
    </parent>

    <artifactId>manage-db</artifactId>
    <packaging>jar</packaging>

    <name>Manage DB</name>
    <description>Database module for Novalon Manage API</description>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-r2dbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>r2dbc-postgresql</artifactId>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
        </dependency>
        <dependency>
            <groupId>org.flywaydb</groupId>
            <artifactId>flyway-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.flywaydb</groupId>
            <artifactId>flyway-database-postgresql</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>1.5.5.Final</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>1.5.5.Final</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

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

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 version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>cn.novalon.manage</groupId>
        <artifactId>novalon-manage-api</artifactId>
        <version>1.0.0</version>
    </parent>

    <artifactId>manage-audit</artifactId>
    <packaging>jar</packaging>

    <name>Manage Audit</name>
    <description>Audit module for Novalon Manage API</description>

    <dependencies>
        <dependency>
            <groupId>cn.novalon.manage</groupId>
            <artifactId>manage-db</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>cn.novalon.manage</groupId>
            <artifactId>manage-common</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Step 2: Compile and verify

Run: cd novalon-manage-api && mvn clean compile -pl manage-audit -am

Expected: BUILD SUCCESS

Step 3: Commit

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 version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>cn.novalon.manage</groupId>
        <artifactId>novalon-manage-api</artifactId>
        <version>1.0.0</version>
    </parent>

    <artifactId>manage-notify</artifactId>
    <packaging>jar</packaging>

    <name>Manage Notify</name>
    <description>Notify module for Novalon Manage API</description>

    <dependencies>
        <dependency>
            <groupId>cn.novalon.manage</groupId>
            <artifactId>manage-db</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>cn.novalon.manage</groupId>
            <artifactId>manage-common</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Step 2: Compile and verify

Run: cd novalon-manage-api && mvn clean compile -pl manage-notify -am

Expected: BUILD SUCCESS

Step 3: Commit

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 version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>cn.novalon.manage</groupId>
        <artifactId>novalon-manage-api</artifactId>
        <version>1.0.0</version>
    </parent>

    <artifactId>manage-file</artifactId>
    <packaging>jar</packaging>

    <name>Manage File</name>
    <description>File module for Novalon Manage API</description>

    <dependencies>
        <dependency>
            <groupId>cn.novalon.manage</groupId>
            <artifactId>manage-db</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>cn.novalon.manage</groupId>
            <artifactId>manage-common</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Step 2: Compile and verify

Run: cd novalon-manage-api && mvn clean compile -pl manage-file -am

Expected: BUILD SUCCESS

Step 3: Commit

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: <modules> section and replace with:

<modules>
    <module>manage-gateway</module>
    <module>manage-app</module>
    <module>manage-sys</module>
    <module>manage-audit</module>
    <module>manage-notify</module>
    <module>manage-file</module>
    <module>manage-common</module>
    <module>manage-db</module>
</modules>

Step 2: Verify build

Run: cd novalon-manage-api && mvn clean compile

Expected: BUILD SUCCESS

Step 3: Commit

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:

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

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:

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

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:

<dependencies>
    <dependency>
        <groupId>cn.novalon.manage</groupId>
        <artifactId>manage-db</artifactId>
        <version>${project.version}</version>
    </dependency>
    <dependency>
        <groupId>cn.novalon.manage</groupId>
        <artifactId>manage-common</artifactId>
        <version>${project.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springdoc</groupId>
        <artifactId>springdoc-openapi-starter-webflux-ui</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

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:

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

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

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

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

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

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<String> roles;
    private List<String> permissions;
}

Step 2: Create JwtTokenProvider

Create: novalon-manage-api/manage-common/src/main/java/cn/novalon/manage/common/util/JwtTokenProvider.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

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<JwtAuthenticationFilter.Config> {

    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<Void> 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

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

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<String, String> 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

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<RbacAuthorizationFilter.Config> {

    @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<String> permissions = request.getHeaders().get("X-User-Permissions");
            List<String> 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<String> permissions, String requiredPermission) {
        return permissions.contains(requiredPermission) || permissions.contains("*:*");
    }

    private Mono<Void> 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:

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

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

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<Object, Object> caffeineCache() {
        return Caffeine.newBuilder()
                .maximumSize(10000)
                .expireAfterWrite(10, TimeUnit.MINUTES)
                .build();
    }

    private Caffeine<Object, Object> 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

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<String, Boolean> 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

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

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

# 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

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

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

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