From 60fb84e3062c871ff7b62c647cc6d5e4da73fc6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E7=BF=94?= Date: Wed, 15 Apr 2026 23:38:03 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E6=9B=B4=E6=96=B0Docker=E5=92=8CCI?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 更新Woodpecker CI配置 - 更新Docker Compose配置 - 更新应用主类配置 - 更新网关路由服务 - 更新审计日志相关代码 --- .woodpecker.yml | 196 +++++++++++------- docker-compose.test.yml | 172 ++++++++------- docker-compose.yml | 60 +++++- novalon-manage-api/Dockerfile | 39 +++- .../novalon/manage/app/ManageApplication.java | 6 +- .../src/main/resources/application.yml | 6 +- .../service/impl/DynamicRouteService.java | 24 +-- .../route/DynamicRouteServiceTest.java | 19 +- .../audit/controller/AuditLogController.java | 20 +- .../manage/sys/audit/domain/AuditLog.java | 26 +++ .../sys/audit/dto/AuditLogQueryRequest.java | 34 +++ 11 files changed, 398 insertions(+), 204 deletions(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index 57dd148..61bfe45 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -1,99 +1,153 @@ -# Woodpecker CI/CD 流水线配置 -# TDD工作流规范 - 质量门禁配置 +# Woodpecker CI/CD 流水线配置 - 企业级质量门禁 +# 基于Docker化部署的完整CI/CD流水线 pipeline: - # 后端单元测试和集成测试 + # 代码质量检查阶段 + code-quality: + group: 质量检查 + image: maven:3.9-openjdk-21 + commands: + - echo "🔍 开始代码质量检查..." + - cd novalon-manage-api + - echo "📊 运行静态代码分析..." + - mvn spotbugs:check + - echo "📏 检查代码规范..." + - mvn checkstyle:check + - echo "📈 生成代码质量报告..." + - mvn pmd:check + - echo "✅ 代码质量检查完成" + when: + event: [push, pull_request] + + # 后端测试阶段 test-backend: + group: 后端测试 image: maven:3.9-openjdk-21 commands: - echo "🚀 开始后端测试..." - cd novalon-manage-api + - echo "🧪 运行单元测试..." - mvn clean test jacoco:report - - echo "✅ 后端测试完成,生成覆盖率报告" + - echo "📊 生成测试覆盖率报告..." + - mvn jacoco:check + - echo "✅ 后端测试完成,覆盖率: $(cat target/site/jacoco/jacoco.xml | grep -oP 'lineCoverage=\"\K[0-9.]+')%" when: event: [push, pull_request] - - # 构建后端JAR文件(用于E2E测试) - build-backend-jar: - image: maven:3.9-openjdk-21 + + # 前端测试阶段 + test-frontend: + group: 前端测试 + image: node:20 commands: - - echo "📦 构建后端JAR文件..." - - cd novalon-manage-api/manage-app - - mvn clean package -DskipTests - - echo "✅ JAR文件构建完成: target/manage-app-1.0.0.jar" - when: - event: [push, pull_request] - - # 前端单元测试 - test-frontend-unit: - image: node:18 - commands: - - echo "🚀 开始前端单元测试..." + - echo "🚀 开始前端测试..." - cd novalon-manage-web + - echo "📦 安装依赖..." - npm ci + - echo "🧪 运行单元测试..." - npm run test:unit - - echo "✅ 前端单元测试完成" + - echo "📏 检查代码规范..." + - npm run lint + - echo "✅ 前端测试完成" when: event: [push, pull_request] - - # 前端E2E测试 - test-frontend-e2e: - image: mcr.microsoft.com/playwright:v1.40.0-jammy - environment: - - DISPLAY=:99 + + # Docker化构建阶段 + docker-build: + group: 容器化构建 + image: docker:24 + volumes: + - /var/run/docker.sock:/var/run/docker.sock commands: - - echo "🚀 开始前端E2E测试..." + - echo "🐳 开始Docker化构建..." + - echo "📦 构建后端镜像..." + - docker build -t novalon/backend:${CI_COMMIT_SHA:0:8} -f novalon-manage-api/Dockerfile ./novalon-manage-api + - echo "🌐 构建前端镜像..." + - docker build -t novalon/frontend:${CI_COMMIT_SHA:0:8} -f novalon-manage-web/Dockerfile ./novalon-manage-web + - echo "✅ Docker镜像构建完成" + when: + event: [push] + branch: [main, develop] + + # 集成测试阶段(使用Docker Compose) + integration-test: + group: 集成测试 + image: docker:24 + volumes: + - /var/run/docker.sock:/var/run/docker.sock + commands: + - echo "🧪 开始集成测试..." + - echo "🐳 启动测试环境..." + - docker-compose -f docker-compose.test.yml up -d + - echo "⏳ 等待服务就绪..." + - sleep 60 + - echo "🔍 检查服务健康状态..." + - curl -f http://localhost:8085/actuator/health || (docker-compose -f docker-compose.test.yml logs && exit 1) + - curl -f http://localhost:3002 || (docker-compose -f docker-compose.test.yml logs && exit 1) + - echo "✅ 集成测试环境就绪" + when: + event: [push] + branch: [main, develop] + + # E2E测试阶段 + e2e-test: + group: E2E测试 + image: mcr.microsoft.com/playwright:v1.58.2-jammy + commands: + - echo "🎭 开始E2E测试..." - cd novalon-manage-web + - echo "📦 安装依赖..." - npm ci + - echo "🔧 安装浏览器..." - npx playwright install --with-deps chromium - - - echo "📦 启动后端服务..." - - cd ../novalon-manage-api/manage-app - - java -jar target/manage-app-1.0.0.jar --spring.profiles.active=test & - - BACKEND_PID=$! - - cd ../../novalon-manage-web - - - echo "⏳ 等待后端服务就绪..." - - | - for i in {1..60}; do - if curl -f http://localhost:8084/actuator/health > /dev/null 2>&1; then - echo "✅ 后端服务就绪" - break - fi - sleep 1 - done - - - echo "🎭 运行Playwright测试..." - - npx playwright test --project=chromium - - - echo "🛑 停止后端服务..." - - kill $BACKEND_PID || true - + - echo "🧪 运行E2E测试..." + - npx playwright test --project=journeys --reporter=html,json,junit - echo "✅ E2E测试完成" when: - event: [push, pull_request] - - # 质量门禁检查 - quality-gates: - image: maven:3.9-openjdk-21 + event: [push] + branch: [main, develop] + + # 安全扫描阶段 + security-scan: + group: 安全扫描 + image: aquasec/trivy:latest commands: - - echo "🔍 开始质量门禁检查..." - - cd novalon-manage-api - - mvn jacoco:check - - echo "✅ 测试覆盖率检查通过" - - echo "✅ 所有测试用例通过" - - echo "✅ 代码规范检查通过" + - echo "🔒 开始安全扫描..." + - echo "📊 扫描后端镜像..." + - trivy image novalon/backend:${CI_COMMIT_SHA:0:8} + - echo "📊 扫描前端镜像..." + - trivy image novalon/frontend:${CI_COMMIT_SHA:0:8} + - echo "✅ 安全扫描完成" when: - event: [pull_request] - - # 构建阶段 - build: - image: maven:3.9-openjdk-21 + event: [push] + branch: [main, develop] + + # 部署阶段 + deploy: + group: 部署 + image: alpine:latest commands: - - echo "📦 开始构建..." - - cd novalon-manage-api - - mvn clean package -DskipTests - - echo "✅ 构建成功" + - echo "🚀 开始部署..." + - echo "📦 推送镜像到仓库..." + - docker tag novalon/backend:${CI_COMMIT_SHA:0:8} ${DOCKER_REGISTRY}/novalon/backend:${CI_COMMIT_SHA:0:8} + - docker tag novalon/frontend:${CI_COMMIT_SHA:0:8} ${DOCKER_REGISTRY}/novalon/frontend:${CI_COMMIT_SHA:0:8} + - docker push ${DOCKER_REGISTRY}/novalon/backend:${CI_COMMIT_SHA:0:8} + - docker push ${DOCKER_REGISTRY}/novalon/frontend:${CI_COMMIT_SHA:0:8} + - echo "✅ 部署完成" + when: + event: [push] + branch: [main] + + # 清理阶段 + cleanup: + group: 清理 + image: docker:24 + volumes: + - /var/run/docker.sock:/var/run/docker.sock + commands: + - echo "🧹 开始清理..." + - docker-compose -f docker-compose.test.yml down -v + - docker system prune -f + - echo "✅ 清理完成" when: event: [push] branch: [main, develop] diff --git a/docker-compose.test.yml b/docker-compose.test.yml index e0481d2..fe53ecd 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -1,118 +1,109 @@ version: '3.8' +x-common-env: &common-env + TZ: Asia/Shanghai + LANG: zh_CN.UTF-8 + services: - # PostgreSQL 数据库(用于测试) + # PostgreSQL数据库服务(测试环境) postgres: image: postgres:15-alpine - container_name: novalon-test-db + container_name: novalon-postgres-test environment: - POSTGRES_DB: novalon_test - POSTGRES_USER: novalon - POSTGRES_PASSWORD: novalon123 + <<: *common-env + POSTGRES_DB: manage_system_test + POSTGRES_USER: novalon_test + POSTGRES_PASSWORD: novalon_test123 + POSTGRES_INITDB_ARGS: "--encoding=UTF8 --locale=zh_CN.UTF-8" ports: - - "5432:5432" + - "55433:5432" volumes: - postgres_test_data:/var/lib/postgresql/data + - ./novalon-manage-api/manage-db/src/main/resources/db/migration:/docker-entrypoint-initdb.d healthcheck: - test: ["CMD-SHELL", "pg_isready -U novalon -d novalon_test"] + test: ["CMD-SHELL", "pg_isready -U novalon_test -d manage_system_test"] interval: 10s timeout: 5s retries: 5 - networks: - - novalon-test-network - - # Redis 缓存(可选) - redis: - image: redis:7-alpine - container_name: novalon-test-redis - ports: - - "6379:6379" - healthcheck: - test: ["CMD", "redis-cli", "ping"] - interval: 10s - timeout: 5s - retries: 5 - networks: - - novalon-test-network - - # 后端服务 - backend: - build: - context: ./novalon-manage-api - dockerfile: Dockerfile - container_name: novalon-test-backend - environment: - SPRING_PROFILES_ACTIVE: test - SPRING_R2DBC_URL: r2dbc:postgresql://postgres:5432/novalon_test - SPRING_R2DBC_USERNAME: novalon - SPRING_R2DBC_PASSWORD: novalon123 - SPRING_FLYWAY_URL: jdbc:postgresql://postgres:5432/novalon_test - SPRING_FLYWAY_USER: novalon - SPRING_FLYWAY_PASSWORD: novalon123 - SPRING_DATA_REDIS_HOST: redis - SPRING_DATA_REDIS_PORT: 6379 - ports: - - "8084:8084" - depends_on: - postgres: - condition: service_healthy - redis: - condition: service_healthy - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8084/actuator/health"] - interval: 30s - timeout: 10s - retries: 5 - start_period: 60s - networks: - - novalon-test-network - - # 网关服务 - gateway: - build: - context: ./novalon-manage-api/manage-gateway - dockerfile: Dockerfile - container_name: novalon-test-gateway - environment: - SPRING_PROFILES_ACTIVE: test - BACKEND_SERVICE_URL: http://backend:8084 - SPRING_DATA_REDIS_HOST: redis - SPRING_DATA_REDIS_PORT: 6379 - ports: - - "8080:8080" - depends_on: - backend: - condition: service_healthy - redis: - condition: service_healthy - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"] - interval: 30s - timeout: 10s - retries: 5 start_period: 30s networks: - novalon-test-network - # 前端服务(开发模式) + # 后端API服务(测试环境) + backend: + build: + context: ./novalon-manage-api + dockerfile: Dockerfile + args: + - BUILD_VERSION=test + container_name: novalon-backend-test + environment: + <<: *common-env + SPRING_PROFILES_ACTIVE: test + SPRING_R2DBC_URL: r2dbc:postgresql://postgres:5432/manage_system_test + SPRING_R2DBC_USERNAME: novalon_test + SPRING_R2DBC_PASSWORD: novalon_test123 + SPRING_JACKSON_TIME_ZONE: Asia/Shanghai + MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE: health,info,metrics + ports: + - "8085:8084" + depends_on: + postgres: + condition: service_healthy + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8084/actuator/health"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + networks: + - novalon-test-network + + # 前端Web服务(测试环境) frontend: build: context: ./novalon-manage-web - dockerfile: Dockerfile.dev - container_name: novalon-test-frontend - environment: - VITE_API_BASE_URL: http://gateway:8080 + dockerfile: Dockerfile + args: + - BUILD_VERSION=test + container_name: novalon-frontend-test ports: - - "3002:3002" - volumes: - - ./novalon-manage-web:/app - - /app/node_modules + - "3002:80" depends_on: - gateway: + backend: condition: service_healthy + environment: + <<: *common-env + - VITE_API_BASE_URL=http://backend:8084 + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:80"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 40s networks: - novalon-test-network + # E2E测试服务 + e2e-tests: + build: + context: ./novalon-manage-web + dockerfile: Dockerfile.playwright + container_name: novalon-e2e-tests + depends_on: + frontend: + condition: service_healthy + backend: + condition: service_healthy + environment: + <<: *common-env + TEST_BASE_URL: http://frontend:80 + PLAYWRIGHT_HEADLESS: "true" + CI: "true" + networks: + - novalon-test-network + command: npm run test:e2e:journeys + volumes: postgres_test_data: driver: local @@ -120,3 +111,6 @@ volumes: networks: novalon-test-network: driver: bridge + ipam: + config: + - subnet: 172.21.0.0/16 \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index dd0b616..5560563 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,37 +1,51 @@ version: '3.8' +x-common-env: &common-env + TZ: Asia/Shanghai + LANG: zh_CN.UTF-8 + services: # PostgreSQL数据库服务 postgres: image: postgres:15-alpine container_name: novalon-postgres environment: + <<: *common-env POSTGRES_DB: manage_system POSTGRES_USER: novalon POSTGRES_PASSWORD: novalon123 + POSTGRES_INITDB_ARGS: "--encoding=UTF8 --locale=zh_CN.UTF-8" ports: - "55432:5432" volumes: - postgres_data:/var/lib/postgresql/data + - ./novalon-manage-api/manage-db/src/main/resources/db/migration:/docker-entrypoint-initdb.d healthcheck: test: ["CMD-SHELL", "pg_isready -U novalon -d manage_system"] interval: 10s timeout: 5s retries: 5 + start_period: 30s networks: - novalon-network + restart: unless-stopped # 后端API服务 backend: build: context: ./novalon-manage-api dockerfile: Dockerfile + args: + - BUILD_VERSION=${BUILD_VERSION:-latest} container_name: novalon-backend environment: + <<: *common-env SPRING_PROFILES_ACTIVE: docker SPRING_R2DBC_URL: r2dbc:postgresql://postgres:5432/manage_system SPRING_R2DBC_USERNAME: novalon SPRING_R2DBC_PASSWORD: novalon123 + SPRING_JACKSON_TIME_ZONE: Asia/Shanghai + MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE: health,info,metrics ports: - "8084:8084" depends_on: @@ -42,15 +56,23 @@ services: interval: 30s timeout: 10s retries: 3 - start_period: 40s + start_period: 60s networks: - novalon-network + restart: unless-stopped + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" # 前端Web服务 frontend: build: context: ./novalon-manage-web dockerfile: Dockerfile + args: + - BUILD_VERSION=${BUILD_VERSION:-latest} container_name: novalon-frontend ports: - "3001:80" @@ -58,7 +80,8 @@ services: backend: condition: service_healthy environment: - - VITE_API_BASE_URL=http://backend:8084 + <<: *common-env + VITE_API_BASE_URL: http://backend:8084 healthcheck: test: ["CMD", "curl", "-f", "http://localhost:80"] interval: 30s @@ -67,11 +90,42 @@ services: start_period: 40s networks: - novalon-network + restart: unless-stopped + logging: + driver: "json-file" + options: + max-size: "10m" + max-file: "3" + + # Redis缓存服务(可选) + redis: + image: redis:7-alpine + container_name: novalon-redis + environment: + <<: *common-env + ports: + - "6379:6379" + command: redis-server --appendonly yes --requirepass novalon123 + volumes: + - redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 10s + timeout: 3s + retries: 5 + networks: + - novalon-network + restart: unless-stopped volumes: postgres_data: driver: local + redis_data: + driver: local networks: novalon-network: - driver: bridge \ No newline at end of file + driver: bridge + ipam: + config: + - subnet: 172.20.0.0/16 \ No newline at end of file diff --git a/novalon-manage-api/Dockerfile b/novalon-manage-api/Dockerfile index fee4eff..02e8eef 100644 --- a/novalon-manage-api/Dockerfile +++ b/novalon-manage-api/Dockerfile @@ -1,22 +1,49 @@ -FROM maven:3.9-eclipse-temurin-17 AS builder +# 多阶段构建优化Dockerfile +FROM maven:3.9-eclipse-temurin-21 AS builder WORKDIR /app +# 复制Maven配置文件和源码 COPY pom.xml . COPY mvnw . COPY mvnw.cmd . COPY .mvn .mvn -COPY src ./src -RUN chmod +x mvnw +# 下载依赖(利用Docker缓存层) +RUN ./mvnw dependency:go-offline -B + +# 复制源码并构建 +COPY src ./src RUN ./mvnw clean package -DskipTests -FROM openjdk:17-slim +# 运行时镜像 +FROM eclipse-temurin:21-jre-jammy + +# 设置时区和语言环境 +RUN apt-get update && apt-get install -y \ + curl \ + && rm -rf /var/lib/apt/lists/* + +# 创建非root用户运行应用 +RUN groupadd -r novalon && useradd -r -g novalon novalon WORKDIR /app -COPY --from=builder /app/target/*.jar app.jar +# 复制构建产物 +COPY --from=builder --chown=novalon:novalon /app/target/*.jar app.jar +# 设置JVM参数优化 +ENV JAVA_OPTS="-Xmx512m -Xms256m -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:+UseContainerSupport -Djava.security.egd=file:/dev/./urandom" + +# 暴露端口 EXPOSE 8084 -ENTRYPOINT ["java", "-jar", "app.jar"] \ No newline at end of file +# 切换用户 +USER novalon + +# 健康检查 +HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ + CMD curl -f http://localhost:8084/actuator/health || exit 1 + +# 启动命令 +ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"] \ No newline at end of file diff --git a/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/ManageApplication.java b/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/ManageApplication.java index d260fef..bc5566c 100644 --- a/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/ManageApplication.java +++ b/novalon-manage-api/manage-app/src/main/java/cn/novalon/manage/app/ManageApplication.java @@ -9,9 +9,7 @@ import org.springframework.boot.context.properties.ConfigurationPropertiesScan; import org.springframework.context.annotation.ComponentScan; import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories; -@SpringBootApplication(exclude = {ReactiveUserDetailsServiceAutoConfiguration.class}) -@ConfigurationPropertiesScan(basePackages = "cn.novalon.manage") -@ComponentScan(basePackages = "cn.novalon.manage") +@SpringBootApplication(scanBasePackages = "cn.novalon.manage", exclude = {ReactiveUserDetailsServiceAutoConfiguration.class}) @EnableR2dbcRepositories(basePackages = {"cn.novalon.manage.db.dao", "cn.novalon.manage.sys.audit.repository"}) public class ManageApplication { @@ -20,6 +18,8 @@ public class ManageApplication { public static void main(String[] args) { logger.info("应用程序启动中..."); logger.info("包扫描路径: cn.novalon.manage"); + + // 使用简单的启动方式,避免自动配置问题 SpringApplication.run(ManageApplication.class, args); logger.info("应用程序启动完成"); } diff --git a/novalon-manage-api/manage-app/src/main/resources/application.yml b/novalon-manage-api/manage-app/src/main/resources/application.yml index 04bb41d..08dbee4 100644 --- a/novalon-manage-api/manage-app/src/main/resources/application.yml +++ b/novalon-manage-api/manage-app/src/main/resources/application.yml @@ -20,7 +20,11 @@ spring: password: ${DB_PASSWORD:postgres} driver-class-name: org.postgresql.Driver flyway: - enabled: false + enabled: true + locations: classpath:db/migration + baseline-on-migrate: true + baseline-version: 0 + validate-on-migrate: true security: user: name: disabled diff --git a/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/impl/DynamicRouteService.java b/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/impl/DynamicRouteService.java index e04c67c..2084627 100644 --- a/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/impl/DynamicRouteService.java +++ b/novalon-manage-api/manage-gateway/src/main/java/cn/novalon/manage/gateway/service/impl/DynamicRouteService.java @@ -74,11 +74,9 @@ public class DynamicRouteService implements IDynamicRouteService { logger.info("Adding route: {}", routeId); return routeDefinitionWriter.save(Mono.just(routeDefinition)) - .then(Mono.fromRunnable(() -> { - routeCache.put(routeId, routeDefinition); - refreshRoutes(); - logger.info("Route added successfully: {}", routeId); - })) + .then(Mono.fromRunnable(() -> routeCache.put(routeId, routeDefinition))) + .then(refreshRoutes()) + .then(Mono.fromRunnable(() -> logger.info("Route added successfully: {}", routeId))) .thenReturn(true) .onErrorResume(error -> { logger.error("Failed to add route: {}", routeId, error); @@ -104,11 +102,9 @@ public class DynamicRouteService implements IDynamicRouteService { return routeDefinitionWriter.delete(Mono.just(routeId)) .then(routeDefinitionWriter.save(Mono.just(routeDefinition))) - .then(Mono.fromRunnable(() -> { - routeCache.put(routeId, routeDefinition); - refreshRoutes(); - logger.info("Route updated successfully: {}", routeId); - })) + .then(Mono.fromRunnable(() -> routeCache.put(routeId, routeDefinition))) + .then(refreshRoutes()) + .then(Mono.fromRunnable(() -> logger.info("Route updated successfully: {}", routeId))) .thenReturn(true) .onErrorResume(error -> { logger.error("Failed to update route: {}", routeId, error); @@ -131,11 +127,9 @@ public class DynamicRouteService implements IDynamicRouteService { logger.info("Deleting route: {}", routeId); return routeDefinitionWriter.delete(Mono.just(routeId)) - .then(Mono.fromRunnable(() -> { - routeCache.remove(routeId); - refreshRoutes(); - logger.info("Route deleted successfully: {}", routeId); - })) + .then(Mono.fromRunnable(() -> routeCache.remove(routeId))) + .then(refreshRoutes()) + .then(Mono.fromRunnable(() -> logger.info("Route deleted successfully: {}", routeId))) .thenReturn(true) .onErrorResume(error -> { logger.error("Failed to delete route: {}", routeId, error); diff --git a/novalon-manage-api/manage-gateway/src/test/java/cn/novalon/manage/gateway/route/DynamicRouteServiceTest.java b/novalon-manage-api/manage-gateway/src/test/java/cn/novalon/manage/gateway/route/DynamicRouteServiceTest.java index 2f02962..1a5f81c 100644 --- a/novalon-manage-api/manage-gateway/src/test/java/cn/novalon/manage/gateway/route/DynamicRouteServiceTest.java +++ b/novalon-manage-api/manage-gateway/src/test/java/cn/novalon/manage/gateway/route/DynamicRouteServiceTest.java @@ -1,5 +1,6 @@ package cn.novalon.manage.gateway.route; +import cn.novalon.manage.gateway.service.impl.DynamicRouteService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -25,7 +26,7 @@ import static org.mockito.Mockito.*; * 涉及业务:路由增删改查、路由刷新 * * @author 张翔 - * @date 2026-03-26 + * @date 2026-04-14 */ @ExtendWith(MockitoExtension.class) class DynamicRouteServiceTest { @@ -78,7 +79,15 @@ class DynamicRouteServiceTest { @Test void testDeleteRoute_Success() { String routeId = "test-route"; + RouteDefinition routeDefinition = createRouteDefinition(routeId); + // 先添加路由到缓存中 + when(routeDefinitionWriter.save(any())).thenReturn(Mono.empty()); + StepVerifier.create(dynamicRouteService.addRoute(routeDefinition)) + .expectNext(true) + .verifyComplete(); + + // 然后删除路由 when(routeDefinitionWriter.delete(any())).thenReturn(Mono.empty()); StepVerifier.create(dynamicRouteService.deleteRoute(routeId)) @@ -86,7 +95,7 @@ class DynamicRouteServiceTest { .verifyComplete(); verify(routeDefinitionWriter).delete(any()); - verify(publisher).publishEvent(any(RefreshRoutesEvent.class)); + verify(publisher, times(2)).publishEvent(any(RefreshRoutesEvent.class)); } @Test @@ -113,7 +122,7 @@ class DynamicRouteServiceTest { .expectNext(true) .verifyComplete(); - StepVerifier.create(dynamicRouteService.getAllRoutes().collectList()) + StepVerifier.create(dynamicRouteService.getRoutes().collectList()) .assertNext(routes -> { assertNotNull(routes); assertTrue(routes.size() >= 2); @@ -131,7 +140,9 @@ class DynamicRouteServiceTest { .expectNext(true) .verifyComplete(); - assertTrue(dynamicRouteService.getRouteCount() >= 1); + StepVerifier.create(dynamicRouteService.getRouteCount()) + .assertNext(count -> assertTrue(count >= 1)) + .verifyComplete(); } private RouteDefinition createRouteDefinition(String id) { diff --git a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/controller/AuditLogController.java b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/controller/AuditLogController.java index 52c9964..28a5a78 100644 --- a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/controller/AuditLogController.java +++ b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/controller/AuditLogController.java @@ -47,9 +47,8 @@ public class AuditLogController { public Flux query(AuditLogQueryRequest request) { if (request.getEntityType() != null && request.getEntityId() != null) { return auditLogService.findByEntityTypeAndEntityId( - request.getEntityType(), - request.getEntityId() - ); + request.getEntityType(), + request.getEntityId()); } else if (request.getEntityType() != null) { return auditLogService.findByEntityType(request.getEntityType()); } else if (request.getOperator() != null) { @@ -58,11 +57,10 @@ public class AuditLogController { return auditLogService.findByOperationType(request.getOperationType()); } else if (request.getStartTime() != null && request.getEndTime() != null) { return auditLogService.findByOperationTimeBetween( - request.getStartTime(), - request.getEndTime() - ); + request.getStartTime(), + request.getEndTime()); } - + return Flux.empty(); } @@ -97,10 +95,8 @@ public class AuditLogController { @GetMapping("/time-range") @Operation(summary = "按时间范围查询", description = "根据时间范围查询审计日志") public Flux findByTimeRange( - @Parameter(description = "开始时间") - @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startTime, - @Parameter(description = "结束时间") - @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime endTime) { + @Parameter(description = "开始时间") @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime startTime, + @Parameter(description = "结束时间") @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime endTime) { return auditLogService.findByOperationTimeBetween(startTime, endTime); } @@ -108,7 +104,7 @@ public class AuditLogController { @Operation(summary = "审计日志统计", description = "获取审计日志的统计信息") public Mono getStatistics() { AuditLogStatistics statistics = new AuditLogStatistics(); - + return Mono.just(statistics); } diff --git a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/domain/AuditLog.java b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/domain/AuditLog.java index a380f00..1c25995 100644 --- a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/domain/AuditLog.java +++ b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/domain/AuditLog.java @@ -138,4 +138,30 @@ public class AuditLog extends BaseDomain { public void setDescription(String description) { this.description = description; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + AuditLog auditLog = (AuditLog) o; + return java.util.Objects.equals(entityType, auditLog.entityType) && + java.util.Objects.equals(entityId, auditLog.entityId) && + java.util.Objects.equals(operationType, auditLog.operationType) && + java.util.Objects.equals(operator, auditLog.operator) && + java.util.Objects.equals(operationTime, auditLog.operationTime) && + java.util.Objects.equals(beforeData, auditLog.beforeData) && + java.util.Objects.equals(afterData, auditLog.afterData) && + java.util.Arrays.equals(changedFields, auditLog.changedFields) && + java.util.Objects.equals(ipAddress, auditLog.ipAddress) && + java.util.Objects.equals(userAgent, auditLog.userAgent) && + java.util.Objects.equals(description, auditLog.description); + } + + @Override + public int hashCode() { + return java.util.Objects.hash(super.hashCode(), entityType, entityId, operationType, operator, + operationTime, beforeData, afterData, java.util.Arrays.hashCode(changedFields), + ipAddress, userAgent, description); + } } diff --git a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/dto/AuditLogQueryRequest.java b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/dto/AuditLogQueryRequest.java index 96371b9..c0dff51 100644 --- a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/dto/AuditLogQueryRequest.java +++ b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/dto/AuditLogQueryRequest.java @@ -100,4 +100,38 @@ public class AuditLogQueryRequest { public void setSize(Integer size) { this.size = size; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AuditLogQueryRequest that = (AuditLogQueryRequest) o; + return java.util.Objects.equals(entityType, that.entityType) && + java.util.Objects.equals(entityId, that.entityId) && + java.util.Objects.equals(operationType, that.operationType) && + java.util.Objects.equals(operator, that.operator) && + java.util.Objects.equals(startTime, that.startTime) && + java.util.Objects.equals(endTime, that.endTime) && + java.util.Objects.equals(page, that.page) && + java.util.Objects.equals(size, that.size); + } + + @Override + public int hashCode() { + return java.util.Objects.hash(entityType, entityId, operationType, operator, startTime, endTime, page, size); + } + + @Override + public String toString() { + return "AuditLogQueryRequest{" + + "entityType='" + entityType + '\'' + + ", entityId=" + entityId + + ", operationType='" + operationType + '\'' + + ", operator='" + operator + '\'' + + ", startTime=" + startTime + + ", endTime=" + endTime + + ", page=" + page + + ", size=" + size + + '}'; + } }