# 模块架构重构执行计划 > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** 重构项目模块架构,实现清晰的职责划分和依赖倒置 **Architecture:** - app模块:只包含启动类、应用级配置和flyway脚本 - sys模块:包含所有业务代码(domain、service、handler等)和业务级配置 - gateway模块:包含路由和限流配置 - db模块:依赖sys模块,实现repository接口 - common模块:提供通用工具类和基础配置 **Tech Stack:** Maven, Spring Boot, Spring WebFlux, Spring Security, R2DBC --- ## 重构目标 ### 模块职责划分 | 模块 | 职责 | 内容 | |-------|--------|------| | manage-app | 应用启动和配置 | ManageApplication.java、application.yml、flyway脚本、应用级配置(WebFluxConfig、MultipartConfig、OpenApiConfig) | | manage-sys | 业务逻辑 | domain、repository接口、service接口和实现、handler、业务级配置(SecurityConfig、WebSocketConfig) | | manage-gateway | 网关路由和限流 | GatewayApplication.java、路由配置(SystemRouter)、限流配置(RateLimitConfig) | | manage-db | 数据访问实现 | entity、dao、repository实现、converter | | manage-common | 通用工具和配置 | 工具类、通用DTO、基础配置、全局异常处理(GlobalExceptionHandler) | ### 依赖关系 ``` manage-gateway → 无依赖(独立模块) manage-app → manage-sys + manage-db manage-sys → manage-common manage-db → manage-sys manage-common → 无依赖 ``` --- ## Task 1: 将RateLimitConfig从app模块移到gateway模块 **Files:** - Create: `manage-gateway/src/main/java/cn/novalon/manage/gateway/config/RateLimitConfig.java` - Delete: `manage-app/src/main/java/cn/novalon/manage/app/config/RateLimitConfig.java` **Step 1: 创建gateway模块的config目录** ```bash mkdir -p manage-gateway/src/main/java/cn/novalon/manage/gateway/config ``` **Step 2: 移动RateLimitConfig.java** ```bash mv manage-app/src/main/java/cn/novalon/manage/app/config/RateLimitConfig.java \ manage-gateway/src/main/java/cn/novalon/manage/gateway/config/ ``` **Step 3: 更新RateLimitConfig.java的包声明** ```java // 将 package cn.novalon.manage.sys.config; // 改为 package cn.novalon.manage.gateway.config; ``` **Step 4: 更新gateway模块的pom.xml,添加Resilience4j依赖** ```xml io.github.resilience4j resilience4j-spring-boot3 2.2.0 io.github.resilience4j resilience4j-reactor 2.2.0 ``` **Step 5: 更新gateway模块的application.yml,添加限流配置** ```yaml rate: limit: limit-for-period: 100 limit-refresh-period: 1s timeout-duration: 0 ``` **Step 6: 提交更改** ```bash git add manage-gateway/src/main/java/cn/novalon/manage/gateway/config/RateLimitConfig.java git add manage-gateway/pom.xml git add manage-gateway/src/main/resources/application.yml git rm manage-app/src/main/java/cn/novalon/manage/app/config/RateLimitConfig.java git commit -m "refactor: move RateLimitConfig to gateway module" ``` --- ## Task 2: 将SystemRouter从app模块移到gateway模块 **Files:** - Create: `manage-gateway/src/main/java/cn/novalon/manage/gateway/config/SystemRouter.java` - Delete: `manage-app/src/main/java/cn/novalon/manage/app/config/SystemRouter.java` **Step 1: 移动SystemRouter.java** ```bash mv manage-app/src/main/java/cn/novalon/manage/app/config/SystemRouter.java \ manage-gateway/src/main/java/cn/novalon/manage/gateway/config/ ``` **Step 2: 更新SystemRouter.java的包声明** ```java // 将 package cn.novalon.manage.sys.config; // 改为 package cn.novalon.manage.gateway.config; ``` **Step 3: 更新GatewayApplication.java,集成SystemRouter** ```java package cn.novalon.manage.gateway; import cn.novalon.manage.gateway.config.SystemRouter; 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, SystemRouter systemRouter) { return systemRouter.buildRoutes(builder); } } ``` **Step 4: 更新SystemRouter.java,使用RouteLocatorBuilder** ```java package cn.novalon.manage.gateway.config; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.stereotype.Component; /** * 系统路由配置 * * 文件定义:配置Spring Cloud Gateway的路由规则 * 涉及业务:API路由、负载均衡、服务发现 * 算法:使用Spring Cloud Gateway的路由匹配和转发 * * @author 张翔 * @date 2026-03-13 */ @Component public class SystemRouter { public RouteLocator buildRoutes(RouteLocatorBuilder builder) { return builder.routes() .route("manage-app", r -> r .path("/api/**") .uri("http://manage-app:8081")) .build(); } } ``` **Step 5: 提交更改** ```bash git add manage-gateway/src/main/java/cn/novalon/manage/gateway/config/SystemRouter.java git add manage-gateway/src/main/java/cn/novalon/manage/gateway/GatewayApplication.java git rm manage-app/src/main/java/cn/novalon/manage/app/config/SystemRouter.java git commit -m "refactor: move SystemRouter to gateway module" ``` --- ## Task 3: 将SecurityConfig从app模块移到sys模块 **Files:** - Create: `manage-sys/src/main/java/cn/novalon/manage/sys/config/SecurityConfig.java` - Delete: `manage-app/src/main/java/cn/novalon/manage/app/config/SecurityConfig.java` **Step 1: 创建sys模块的config目录** ```bash mkdir -p manage-sys/src/main/java/cn/novalon/manage/sys/config ``` **Step 2: 移动SecurityConfig.java** ```bash mv manage-app/src/main/java/cn/novalon/manage/app/config/SecurityConfig.java \ manage-sys/src/main/java/cn/novalon/manage/sys/config/ ``` **Step 3: 更新SecurityConfig.java的包声明** ```java // 将 package cn.novalon.manage.sys.config; // 改为 package cn.novalon.manage.sys.config; ``` **Step 4: 提交更改** ```bash git add manage-sys/src/main/java/cn/novalon/manage/sys/config/SecurityConfig.java git rm manage-app/src/main/java/cn/novalon/manage/app/config/SecurityConfig.java git commit -m "refactor: move SecurityConfig to sys module" ``` --- ## Task 4: 将WebSocketConfig从app模块移到sys模块 **Files:** - Create: `manage-sys/src/main/java/cn/novalon/manage/sys/config/WebSocketConfig.java` - Delete: `manage-app/src/main/java/cn/novalon/manage/app/config/WebSocketConfig.java` **Step 1: 移动WebSocketConfig.java** ```bash mv manage-app/src/main/java/cn/novalon/manage/app/config/WebSocketConfig.java \ manage-sys/src/main/java/cn/novalon/manage/sys/config/ ``` **Step 2: 更新WebSocketConfig.java的包声明** ```java // 将 package cn.novalon.manage.sys.config; // 改为 package cn.novalon.manage.sys.config; ``` **Step 3: 提交更改** ```bash git add manage-sys/src/main/java/cn/novalon/manage/sys/config/WebSocketConfig.java git rm manage-app/src/main/java/cn/novalon/manage/app/config/WebSocketConfig.java git commit -m "refactor: move WebSocketConfig to sys module" ``` --- ## Task 5: 将GlobalExceptionHandler移到common模块并重构 **Files:** - Create: `manage-common/src/main/java/cn/novalon/manage/common/handler/GlobalExceptionHandler.java` - Create: `manage-common/src/main/java/cn/novalon/manage/common/handler/ExceptionLogService.java` - Delete: `manage-app/src/main/java/cn/novalon/manage/app/handler/GlobalExceptionHandler.java` **Step 1: 创建common模块的handler目录** ```bash mkdir -p manage-common/src/main/java/cn/novalon/manage/common/handler ``` **Step 2: 创建异常日志服务接口** ```java package cn.novalon.manage.common.handler; import reactor.core.publisher.Mono; /** * 异常日志服务接口 * * 文件定义:定义异常日志记录的抽象接口 * 涉及业务:异常日志记录、错误追踪 * 算法:使用响应式编程实现异步日志记录 * * @author 张翔 * @date 2026-03-13 */ public interface ExceptionLogService { Mono logException(String title, String exceptionName, String exceptionMsg, String methodName, String ip, String stackTrace); } ``` **Step 3: 重构GlobalExceptionHandler,移除对sys模块的依赖** ```java package cn.novalon.manage.common.handler; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.DuplicateKeyException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.server.ResponseStatusException; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.ServerWebInputException; import java.time.LocalDateTime; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; /** * 全局异常处理器 * * 文件定义:统一处理系统中抛出的各种异常,返回标准化的错误响应 * 涉及业务:异常捕获、错误日志记录、错误响应格式化 * 算法:使用@RestControllerAdvice注解实现全局异常拦截 * * @author 张翔 * @date 2026-03-13 */ @RestControllerAdvice public class GlobalExceptionHandler { private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); private final ExceptionLogService exceptionLogService; public GlobalExceptionHandler(ExceptionLogService exceptionLogService) { this.exceptionLogService = exceptionLogService; } @ExceptionHandler(RuntimeException.class) public ResponseEntity> handleRuntimeException(RuntimeException ex, ServerWebExchange exchange) { logger.warn("Runtime exception: ", ex); Map response = new HashMap<>(); if (ex.getMessage() != null && ex.getMessage().contains("not found")) { response.put("code", HttpStatus.NOT_FOUND.value()); response.put("message", ex.getMessage()); response.put("timestamp", LocalDateTime.now()); return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response); } response.put("code", HttpStatus.BAD_REQUEST.value()); response.put("message", ex.getMessage()); response.put("timestamp", LocalDateTime.now()); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); } @ExceptionHandler(Exception.class) public ResponseEntity> handleException(Exception ex, ServerWebExchange exchange) { logger.error("Exception occurred: ", ex); exceptionLogService.logException( "System Exception", ex.getClass().getSimpleName(), ex.getMessage(), exchange.getRequest().getPath().value(), getClientIp(exchange), getStackTrace(ex) ).subscribe(); Map response = new HashMap<>(); response.put("code", HttpStatus.INTERNAL_SERVER_ERROR.value()); response.put("message", "Internal server error"); response.put("timestamp", LocalDateTime.now()); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response); } @ExceptionHandler(IllegalArgumentException.class) public ResponseEntity> handleIllegalArgumentException(IllegalArgumentException ex, ServerWebExchange exchange) { logger.warn("Illegal argument: ", ex); Map response = new HashMap<>(); response.put("code", HttpStatus.BAD_REQUEST.value()); response.put("message", ex.getMessage()); response.put("timestamp", LocalDateTime.now()); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); } @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex, ServerWebExchange exchange) { logger.warn("Validation failed: ", ex); Map response = new HashMap<>(); response.put("code", HttpStatus.BAD_REQUEST.value()); response.put("message", "Validation failed"); response.put("timestamp", LocalDateTime.now()); Map fieldErrors = ex.getBindingResult() .getFieldErrors() .stream() .collect(Collectors.toMap(FieldError::getField, FieldError::getDefaultMessage, (e1, e2) -> e1)); response.put("errors", fieldErrors); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); } @ExceptionHandler(ServerWebInputException.class) public ResponseEntity> handleServerWebInputException(ServerWebInputException ex, ServerWebExchange exchange) { logger.warn("Invalid input: ", ex); Map response = new HashMap<>(); response.put("code", HttpStatus.BAD_REQUEST.value()); response.put("message", "Invalid input"); response.put("timestamp", LocalDateTime.now()); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response); } @ExceptionHandler(ResponseStatusException.class) public ResponseEntity> handleResponseStatusException(ResponseStatusException ex, ServerWebExchange exchange) { logger.warn("Response status exception: ", ex); Map response = new HashMap<>(); response.put("code", ex.getStatusCode().value()); response.put("message", ex.getReason()); response.put("timestamp", LocalDateTime.now()); return ResponseEntity.status(ex.getStatusCode()).body(response); } @ExceptionHandler(DuplicateKeyException.class) public ResponseEntity> handleDuplicateKeyException(DuplicateKeyException ex, ServerWebExchange exchange) { logger.warn("Duplicate key: ", ex); Map response = new HashMap<>(); response.put("code", HttpStatus.CONFLICT.value()); response.put("message", "Duplicate key violation"); response.put("timestamp", LocalDateTime.now()); return ResponseEntity.status(HttpStatus.CONFLICT).body(response); } @ExceptionHandler(DataIntegrityViolationException.class) public ResponseEntity> handleDataIntegrityViolationException(DataIntegrityViolationException ex, ServerWebExchange exchange) { logger.warn("Data integrity violation: ", ex); Map response = new HashMap<>(); response.put("code", HttpStatus.CONFLICT.value()); response.put("message", "Data integrity violation"); response.put("timestamp", LocalDateTime.now()); return ResponseEntity.status(HttpStatus.CONFLICT).body(response); } private String getClientIp(ServerWebExchange exchange) { return exchange.getRequest().getHeaders().getFirst("X-Forwarded-For", exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()); } private String getStackTrace(Exception ex) { StringBuilder stackTrace = new StringBuilder(); for (StackTraceElement element : ex.getStackTrace()) { stackTrace.append(element.toString()).append("\n"); } return stackTrace.toString(); } } ``` **Step 4: 移动GlobalExceptionHandler.java** ```bash mv manage-app/src/main/java/cn/novalon/manage/app/handler/GlobalExceptionHandler.java \ manage-common/src/main/java/cn/novalon/manage/common/handler/ ``` **Step 5: 更新GlobalExceptionHandler.java的包声明** ```java // 将 package cn.novalon.manage.sys.handler; // 改为 package cn.novalon.manage.common.handler; ``` **Step 6: 在sys模块实现ExceptionLogService接口** ```java package cn.novalon.manage.sys.handler; import cn.novalon.manage.common.handler.ExceptionLogService; import cn.novalon.manage.sys.core.domain.SysExceptionLog; import cn.novalon.manage.sys.core.service.ISysExceptionLogService; import org.springframework.stereotype.Service; import reactor.core.publisher.Mono; /** * 异常日志服务实现 * * 文件定义:实现异常日志记录接口,使用sys模块的异常日志服务 * 涉及业务:异常日志记录、错误追踪 * 算法:使用响应式编程实现异步日志记录 * * @author 张翔 * @date 2026-03-13 */ @Service public class ExceptionLogServiceImpl implements ExceptionLogService { private final ISysExceptionLogService exceptionLogService; public ExceptionLogServiceImpl(ISysExceptionLogService exceptionLogService) { this.exceptionLogService = exceptionLogService; } @Override public Mono logException(String title, String exceptionName, String exceptionMsg, String methodName, String ip, String stackTrace) { SysExceptionLog exceptionLog = new SysExceptionLog(); exceptionLog.setTitle(title); exceptionLog.setExceptionName(exceptionName); exceptionLog.setExceptionMsg(exceptionMsg); exceptionLog.setMethodName(methodName); exceptionLog.setIp(ip); exceptionLog.setCreateTime(LocalDateTime.now()); exceptionLog.setStackTrace(stackTrace); return exceptionLogService.save(exceptionLog).then(); } } ``` **Step 7: 在sys模块的配置中注册ExceptionLogServiceImpl** ```java package cn.novalon.manage.sys.config; import cn.novalon.manage.common.handler.ExceptionLogService; import cn.novalon.manage.sys.handler.ExceptionLogServiceImpl; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * 异常日志配置类 * * 文件定义:配置异常日志服务的实现 * 涉及业务:异常日志记录、错误追踪 * 算法:使用Spring的依赖注入实现接口和实现的绑定 * * @author 张翔 * @date 2026-03-13 */ @Configuration public class ExceptionLogConfig { @Bean public ExceptionLogService exceptionLogService(ExceptionLogServiceImpl exceptionLogServiceImpl) { return exceptionLogServiceImpl; } } ``` **Step 8: 提交更改** ```bash git add manage-common/src/main/java/cn/novalon/manage/common/handler/GlobalExceptionHandler.java git add manage-common/src/main/java/cn/novalon/manage/common/handler/ExceptionLogService.java git add manage-sys/src/main/java/cn/novalon/manage/sys/handler/ExceptionLogServiceImpl.java git add manage-sys/src/main/java/cn/novalon/manage/sys/config/ExceptionLogConfig.java git rm manage-app/src/main/java/cn/novalon/manage/app/handler/GlobalExceptionHandler.java git commit -m "refactor: move GlobalExceptionHandler to common module with dependency inversion" ``` --- ## Task 6: 更新app模块的ManageApplication.java **Files:** - Modify: `manage-app/src/main/java/cn/novalon/manage/app/ManageApplication.java` **Step 1: 更新ManageApplication.java的组件扫描配置** ```java package cn.novalon.manage.app; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.ConfigurationPropertiesScan; import org.springframework.context.annotation.ComponentScan; import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories; /** * 管理应用主类 * * 文件定义:Spring Boot应用启动类,配置组件扫描和功能启用 * 涉及业务:应用启动、组件扫描、功能配置 * 算法:使用Spring Boot自动配置和注解驱动 * * @author 张翔 * @date 2026-03-13 */ @SpringBootApplication @ConfigurationPropertiesScan(basePackages = "cn.novalon.manage") @ComponentScan(basePackages = {"cn.novalon.manage.sys", "cn.novalon.manage.db"}) @EnableR2dbcRepositories(basePackages = "cn.novalon.manage.db.repository") public class ManageApplication { public static void main(String[] args) { SpringApplication.run(ManageApplication.class, args); } } ``` **Step 2: 提交更改** ```bash git add manage-app/src/main/java/cn/novalon/manage/app/ManageApplication.java git commit -m "refactor: update ManageApplication component scan configuration" ``` --- ## Task 7: 更新app模块的pom.xml **Files:** - Modify: `manage-app/pom.xml` **Step 1: 确保app模块依赖sys和db模块** ```xml cn.novalon.manage manage-sys ${project.version} cn.novalon.manage manage-db ${project.version} org.springframework.boot spring-boot-starter-webflux org.springframework.boot spring-boot-starter-actuator io.micrometer micrometer-registry-prometheus org.postgresql r2dbc-postgresql org.postgresql postgresql org.flywaydb flyway-core org.flywaydb flyway-database-postgresql org.springframework.boot spring-boot-starter-test test ``` **Step 2: 移除不需要的依赖** ```xml io.github.resilience4j resilience4j-spring-boot3 2.2.0 io.github.resilience4j resilience4j-reactor 2.2.0 ``` **Step 3: 提交更改** ```bash git add manage-app/pom.xml git commit -m "refactor: update app module dependencies" ``` --- ## Task 8: 编译和测试验证 **Files:** - Test: `manage-app`, `manage-sys`, `manage-db`, `manage-gateway` **Step 1: 清理并编译所有模块** ```bash mvn clean compile -DskipTests ``` **Expected:** 所有模块编译成功,无错误 **Step 2: 运行单元测试** ```bash mvn test ``` **Expected:** 所有测试通过 **Step 3: 启动应用验证** ```bash cd manage-app mvn spring-boot:run ``` **Expected:** 应用成功启动,无错误日志 **Step 4: 提交更改** ```bash git add . git commit -m "refactor: complete module architecture refactoring" ``` --- ## Task 9: 更新文档 **Files:** - Create: `docs/architecture/module-architecture.md` **Step 1: 创建模块架构文档** ```markdown # 模块架构设计 ## 模块职责划分 ### manage-app 应用启动和配置模块,包含: - ManageApplication.java:应用启动类 - application.yml:应用配置文件 - flyway脚本:数据库迁移脚本 - 应用级配置:WebFluxConfig、MultipartConfig、OpenApiConfig、GlobalExceptionHandler ### manage-sys 业务逻辑模块,包含: - domain:领域对象(SysUser、SysRole、SysMenu等) - repository:数据访问接口 - service:业务逻辑接口和实现 - handler:业务处理器(用户、角色、菜单等) - 业务级配置:SecurityConfig、WebSocketConfig - 其他:filter、security、websocket、primitive、command、dto ### manage-gateway 网关模块,包含: - GatewayApplication.java:网关启动类 - 路由配置:SystemRouter - 限流配置:RateLimitConfig ### manage-db 数据访问实现模块,包含: - entity:数据库实体 - dao:数据访问对象 - repository:repository实现 - converter:实体和领域对象转换器 ### manage-common 通用工具和配置模块,包含: - 工具类:SnowflakeId等 - 通用DTO:PageRequest、PageResponse - 基础配置:JwtProperties、CacheConfig ## 依赖关系 ``` manage-gateway → 无依赖(独立模块) manage-app → manage-sys + manage-db manage-sys → manage-common manage-db → manage-sys manage-common → 无依赖 ``` ## 依赖倒置实现 通过manage-app模块的依赖注入,实现依赖倒置: - sys模块定义repository接口 - db模块实现repository接口 - app模块通过@ComponentScan扫描db模块的repository实现 - app模块通过@EnableR2dbcRepositories启用R2DBC repository - common模块定义ExceptionLogService接口 - sys模块实现ExceptionLogService接口 - app模块通过配置注册ExceptionLogService实现 ``` **Step 2: 提交文档** ```bash git add docs/architecture/module-architecture.md git commit -m "docs: add module architecture documentation" ``` --- ## 验证清单 ### 编译验证 - [ ] manage-common编译成功 - [ ] manage-sys编译成功 - [ ] manage-db编译成功 - [ ] manage-app编译成功 - [ ] manage-gateway编译成功 ### 功能验证 - [ ] 应用启动成功 - [ ] 数据库连接正常 - [ ] API访问正常 - [ ] WebSocket连接正常 - [ ] 安全认证正常 - [ ] 限流功能正常 ### 依赖验证 - [ ] manage-sys不依赖manage-db - [ ] manage-db依赖manage-sys - [ ] manage-app依赖manage-sys和manage-db - [ ] manage-gateway无依赖 ### 测试验证 - [ ] 单元测试全部通过 - [ ] 集成测试全部通过 - [ ] E2E测试全部通过 --- ## 回滚计划 如果重构过程中出现问题,可以使用以下命令回滚: ```bash # 回滚到重构前的状态 git reset --hard # 或者使用git reflog查找之前的提交 git reflog git reset --hard HEAD@{n} ``` --- ## 注意事项 1. **循环依赖**:确保manage-sys不依赖manage-db 2. **包声明**:移动文件后记得更新包声明 3. **import语句**:更新所有import语句以匹配新的包结构 4. **配置文件**:确保application.yml中的配置正确 5. **组件扫描**:确保ManageApplication.java中的@ComponentScan配置正确 6. **测试覆盖**:重构后确保所有测试仍然通过