25 KiB
label_backend 标准目录扁平化 Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 把 com.label.module.* 与 com.label.common.aop/common.config 迁移到规范要求的扁平目录,保证行为不变。
Architecture: 先写迁移守卫测试,再按 基础设施 -> 数据层 -> 服务层 -> 控制层 -> 清理回归 的顺序做 git mv 和导包修正,每阶段都用最小测试集验证。
Tech Stack: Java 21、Spring Boot 3.1.5、MyBatis-Plus、Shiro、JUnit 5、AssertJ、Maven
目标目录
src/main/java/com/label/annotationsrc/main/java/com/label/aspectsrc/main/java/com/label/configsrc/main/java/com/label/controllersrc/main/java/com/label/dtosrc/main/java/com/label/entitysrc/main/java/com/label/eventsrc/main/java/com/label/listenersrc/main/java/com/label/mappersrc/main/java/com/label/servicesrc/test/java/com/label/unit/PackageStructureMigrationTest.java
Task 1: 锁定基础设施迁移目标
Files:
-
Create:
src/test/java/com/label/unit/PackageStructureMigrationTest.java -
Modify:
src/main/java/com/label/common/aop/OperationLog.java -
Modify:
src/main/java/com/label/common/aop/AuditAspect.java -
Modify:
src/main/java/com/label/common/config/MybatisPlusConfig.java -
Modify:
src/main/java/com/label/common/config/OpenApiConfig.java -
Modify:
src/main/java/com/label/common/config/RedisConfig.java -
Modify:
src/main/java/com/label/module/annotation/event/ExtractionApprovedEvent.java -
Modify:
src/main/java/com/label/module/annotation/service/ExtractionApprovedEventListener.java -
Modify:
src/main/java/com/label/module/annotation/service/ExtractionService.java -
Test:
src/test/java/com/label/unit/PackageStructureMigrationTest.java -
Step 1: 写失败测试
package com.label.unit;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@DisplayName("标准目录扁平化迁移守卫测试")
class PackageStructureMigrationTest {
@Test
@DisplayName("基础设施类已迁移到目标目录")
void infrastructureTypesMoved() {
assertClassExists("com.label.annotation.OperationLog");
assertClassExists("com.label.aspect.AuditAspect");
assertClassExists("com.label.config.MybatisPlusConfig");
assertClassExists("com.label.config.OpenApiConfig");
assertClassExists("com.label.config.RedisConfig");
assertClassExists("com.label.event.ExtractionApprovedEvent");
assertClassExists("com.label.listener.ExtractionApprovedEventListener");
assertClassMissing("com.label.common.aop.OperationLog");
assertClassMissing("com.label.common.aop.AuditAspect");
assertClassMissing("com.label.common.config.MybatisPlusConfig");
assertClassMissing("com.label.common.config.OpenApiConfig");
assertClassMissing("com.label.common.config.RedisConfig");
assertClassMissing("com.label.module.annotation.event.ExtractionApprovedEvent");
assertClassMissing("com.label.module.annotation.service.ExtractionApprovedEventListener");
}
private static void assertClassExists(String fqcn) {
assertThatCode(() -> Class.forName(fqcn)).doesNotThrowAnyException();
}
private static void assertClassMissing(String fqcn) {
assertThatThrownBy(() -> Class.forName(fqcn)).isInstanceOf(ClassNotFoundException.class);
}
}
- Step 2: 跑红
Run: mvn -q "-Dtest=PackageStructureMigrationTest#infrastructureTypesMoved" test
Expected: FAIL,提示新包类不存在。
- Step 3: 最小实现
先执行迁移:
git mv src/main/java/com/label/common/aop/OperationLog.java src/main/java/com/label/annotation/OperationLog.java
git mv src/main/java/com/label/common/aop/AuditAspect.java src/main/java/com/label/aspect/AuditAspect.java
git mv src/main/java/com/label/common/config/MybatisPlusConfig.java src/main/java/com/label/config/MybatisPlusConfig.java
git mv src/main/java/com/label/common/config/OpenApiConfig.java src/main/java/com/label/config/OpenApiConfig.java
git mv src/main/java/com/label/common/config/RedisConfig.java src/main/java/com/label/config/RedisConfig.java
git mv src/main/java/com/label/module/annotation/event/ExtractionApprovedEvent.java src/main/java/com/label/event/ExtractionApprovedEvent.java
git mv src/main/java/com/label/module/annotation/service/ExtractionApprovedEventListener.java src/main/java/com/label/listener/ExtractionApprovedEventListener.java
再做精确替换:
// OperationLog.java
package com.label.annotation;
// AuditAspect.java
package com.label.aspect;
import com.label.annotation.OperationLog;
// MybatisPlusConfig.java / OpenApiConfig.java / RedisConfig.java
package com.label.config;
// ExtractionApprovedEvent.java
package com.label.event;
// ExtractionApprovedEventListener.java
package com.label.listener;
import com.label.event.ExtractionApprovedEvent;
import com.label.module.annotation.entity.TrainingDataset;
import com.label.module.annotation.mapper.AnnotationResultMapper;
import com.label.module.annotation.mapper.TrainingDatasetMapper;
import com.label.module.source.entity.SourceData;
import com.label.module.source.mapper.SourceDataMapper;
import com.label.module.task.service.TaskService;
并把 src/main/java/com/label/module/annotation/service/ExtractionService.java 中事件 import 改成 com.label.event.ExtractionApprovedEvent。
- Step 4: 跑绿
Run:
mvn -q "-Dtest=PackageStructureMigrationTest#infrastructureTypesMoved" testmvn -q -DskipTests compile
Expected: 两条命令都 BUILD SUCCESS。
- Step 5: Commit
git add src/test/java/com/label/unit/PackageStructureMigrationTest.java src/main/java/com/label/annotation src/main/java/com/label/aspect src/main/java/com/label/config src/main/java/com/label/event src/main/java/com/label/listener src/main/java/com/label/module/annotation/service/ExtractionService.java
git commit -m "refactor: flatten infrastructure packages"
Task 2: 迁移 DTO、Entity、Mapper
Files:
-
Modify:
src/test/java/com/label/unit/PackageStructureMigrationTest.java -
Modify:
src/test/java/com/label/unit/OpenApiAnnotationTest.java -
Modify:
src/test/java/com/label/integration/AuthIntegrationTest.java -
Modify:
src/test/java/com/label/integration/ExtractionApprovalIntegrationTest.java -
Modify:
src/test/java/com/label/integration/QaApprovalIntegrationTest.java -
Modify:
src/test/java/com/label/integration/UserManagementIntegrationTest.java -
Modify:
src/main/java/com/label/module/annotation/service/ExtractionService.java -
Modify:
src/main/java/com/label/module/annotation/service/QaService.java -
Modify:
src/main/java/com/label/module/config/service/SysConfigService.java -
Modify:
src/main/java/com/label/module/config/controller/SysConfigController.java -
Modify:
src/main/java/com/label/module/export/service/ExportService.java -
Modify:
src/main/java/com/label/module/export/service/FinetuneService.java -
Modify:
src/main/java/com/label/module/export/controller/ExportController.java -
Modify:
src/main/java/com/label/module/source/service/SourceService.java -
Modify:
src/main/java/com/label/module/source/controller/SourceController.java -
Modify:
src/main/java/com/label/module/task/service/TaskClaimService.java -
Modify:
src/main/java/com/label/module/task/service/TaskService.java -
Modify:
src/main/java/com/label/module/task/controller/TaskController.java -
Modify:
src/main/java/com/label/module/user/service/AuthService.java -
Modify:
src/main/java/com/label/module/user/service/UserService.java -
Modify:
src/main/java/com/label/module/user/controller/AuthController.java -
Modify:
src/main/java/com/label/module/user/controller/UserController.java -
Modify:
src/main/java/com/label/module/video/service/VideoProcessService.java -
Modify:
src/main/java/com/label/module/video/controller/VideoController.java -
Test:
src/test/java/com/label/unit/PackageStructureMigrationTest.java -
Step 1: 写失败测试
在 PackageStructureMigrationTest.java 里追加:
@Test
@DisplayName("DTO、实体、Mapper 已迁移到扁平数据层")
void dataTypesMoved() {
for (String fqcn : java.util.List.of(
"com.label.dto.LoginRequest", "com.label.dto.LoginResponse", "com.label.dto.UserInfoResponse",
"com.label.dto.TaskResponse", "com.label.dto.SourceResponse",
"com.label.entity.AnnotationResult", "com.label.entity.TrainingDataset", "com.label.entity.SysConfig",
"com.label.entity.ExportBatch", "com.label.entity.SourceData", "com.label.entity.AnnotationTask",
"com.label.entity.AnnotationTaskHistory", "com.label.entity.SysCompany", "com.label.entity.SysUser",
"com.label.entity.VideoProcessJob",
"com.label.mapper.AnnotationResultMapper", "com.label.mapper.TrainingDatasetMapper",
"com.label.mapper.SysConfigMapper", "com.label.mapper.ExportBatchMapper", "com.label.mapper.SourceDataMapper",
"com.label.mapper.AnnotationTaskMapper", "com.label.mapper.TaskHistoryMapper",
"com.label.mapper.SysCompanyMapper", "com.label.mapper.SysUserMapper", "com.label.mapper.VideoProcessJobMapper")) {
assertClassExists(fqcn);
}
}
- Step 2: 跑红
Run: mvn -q "-Dtest=PackageStructureMigrationTest#dataTypesMoved" test
Expected: FAIL,提示 com.label.dto.LoginRequest 等不存在。
- Step 3: 最小实现
执行迁移:
git mv src/main/java/com/label/module/user/dto/LoginRequest.java src/main/java/com/label/dto/LoginRequest.java
git mv src/main/java/com/label/module/user/dto/LoginResponse.java src/main/java/com/label/dto/LoginResponse.java
git mv src/main/java/com/label/module/user/dto/UserInfoResponse.java src/main/java/com/label/dto/UserInfoResponse.java
git mv src/main/java/com/label/module/task/dto/TaskResponse.java src/main/java/com/label/dto/TaskResponse.java
git mv src/main/java/com/label/module/source/dto/SourceResponse.java src/main/java/com/label/dto/SourceResponse.java
git mv src/main/java/com/label/module/annotation/entity/AnnotationResult.java src/main/java/com/label/entity/AnnotationResult.java
git mv src/main/java/com/label/module/annotation/entity/TrainingDataset.java src/main/java/com/label/entity/TrainingDataset.java
git mv src/main/java/com/label/module/config/entity/SysConfig.java src/main/java/com/label/entity/SysConfig.java
git mv src/main/java/com/label/module/export/entity/ExportBatch.java src/main/java/com/label/entity/ExportBatch.java
git mv src/main/java/com/label/module/source/entity/SourceData.java src/main/java/com/label/entity/SourceData.java
git mv src/main/java/com/label/module/task/entity/AnnotationTask.java src/main/java/com/label/entity/AnnotationTask.java
git mv src/main/java/com/label/module/task/entity/AnnotationTaskHistory.java src/main/java/com/label/entity/AnnotationTaskHistory.java
git mv src/main/java/com/label/module/user/entity/SysCompany.java src/main/java/com/label/entity/SysCompany.java
git mv src/main/java/com/label/module/user/entity/SysUser.java src/main/java/com/label/entity/SysUser.java
git mv src/main/java/com/label/module/video/entity/VideoProcessJob.java src/main/java/com/label/entity/VideoProcessJob.java
git mv src/main/java/com/label/module/annotation/mapper/AnnotationResultMapper.java src/main/java/com/label/mapper/AnnotationResultMapper.java
git mv src/main/java/com/label/module/annotation/mapper/TrainingDatasetMapper.java src/main/java/com/label/mapper/TrainingDatasetMapper.java
git mv src/main/java/com/label/module/config/mapper/SysConfigMapper.java src/main/java/com/label/mapper/SysConfigMapper.java
git mv src/main/java/com/label/module/export/mapper/ExportBatchMapper.java src/main/java/com/label/mapper/ExportBatchMapper.java
git mv src/main/java/com/label/module/source/mapper/SourceDataMapper.java src/main/java/com/label/mapper/SourceDataMapper.java
git mv src/main/java/com/label/module/task/mapper/AnnotationTaskMapper.java src/main/java/com/label/mapper/AnnotationTaskMapper.java
git mv src/main/java/com/label/module/task/mapper/TaskHistoryMapper.java src/main/java/com/label/mapper/TaskHistoryMapper.java
git mv src/main/java/com/label/module/user/mapper/SysCompanyMapper.java src/main/java/com/label/mapper/SysCompanyMapper.java
git mv src/main/java/com/label/module/user/mapper/SysUserMapper.java src/main/java/com/label/mapper/SysUserMapper.java
git mv src/main/java/com/label/module/video/mapper/VideoProcessJobMapper.java src/main/java/com/label/mapper/VideoProcessJobMapper.java
统一替换包声明:
package com.label.dto;
package com.label.entity;
package com.label.mapper;
然后把上面 Files 列表中的旧 com.label.module.*.(dto|entity|mapper) import 全部改到新包。
- Step 4: 跑绿
Run:
mvn -q "-Dtest=PackageStructureMigrationTest#dataTypesMoved,OpenApiAnnotationTest,AuthIntegrationTest,ExtractionApprovalIntegrationTest,QaApprovalIntegrationTest,UserManagementIntegrationTest" test
Expected: PASS。
- Step 5: Commit
git add src/main/java/com/label/dto src/main/java/com/label/entity src/main/java/com/label/mapper src/test/java/com/label/unit/PackageStructureMigrationTest.java src/test/java/com/label/unit/OpenApiAnnotationTest.java src/test/java/com/label/integration/AuthIntegrationTest.java src/test/java/com/label/integration/ExtractionApprovalIntegrationTest.java src/test/java/com/label/integration/QaApprovalIntegrationTest.java src/test/java/com/label/integration/UserManagementIntegrationTest.java src/main/java/com/label/module
git commit -m "refactor: flatten dto entity and mapper packages"
Task 3: 迁移业务服务层
Files:
-
Modify:
src/test/java/com/label/unit/PackageStructureMigrationTest.java -
Modify:
src/main/java/com/label/module/annotation/controller/ExtractionController.java -
Modify:
src/main/java/com/label/module/annotation/controller/QaController.java -
Modify:
src/main/java/com/label/module/config/controller/SysConfigController.java -
Modify:
src/main/java/com/label/module/export/controller/ExportController.java -
Modify:
src/main/java/com/label/module/source/controller/SourceController.java -
Modify:
src/main/java/com/label/module/task/controller/TaskController.java -
Modify:
src/main/java/com/label/module/user/controller/AuthController.java -
Modify:
src/main/java/com/label/module/user/controller/UserController.java -
Modify:
src/main/java/com/label/module/video/controller/VideoController.java -
Test:
src/test/java/com/label/unit/PackageStructureMigrationTest.java -
Step 1: 写失败测试
@Test
@DisplayName("服务类已迁移到扁平 service 目录")
void serviceTypesMoved() {
for (String fqcn : java.util.List.of(
"com.label.service.ExtractionService", "com.label.service.QaService",
"com.label.service.SysConfigService", "com.label.service.ExportService",
"com.label.service.FinetuneService", "com.label.service.SourceService",
"com.label.service.TaskClaimService", "com.label.service.TaskService",
"com.label.service.AuthService", "com.label.service.UserService",
"com.label.service.VideoProcessService")) {
assertClassExists(fqcn);
}
}
- Step 2: 跑红
Run: mvn -q "-Dtest=PackageStructureMigrationTest#serviceTypesMoved" test
Expected: FAIL,提示 com.label.service.* 不存在。
- Step 3: 最小实现
执行迁移:
git mv src/main/java/com/label/module/annotation/service/ExtractionService.java src/main/java/com/label/service/ExtractionService.java
git mv src/main/java/com/label/module/annotation/service/QaService.java src/main/java/com/label/service/QaService.java
git mv src/main/java/com/label/module/config/service/SysConfigService.java src/main/java/com/label/service/SysConfigService.java
git mv src/main/java/com/label/module/export/service/ExportService.java src/main/java/com/label/service/ExportService.java
git mv src/main/java/com/label/module/export/service/FinetuneService.java src/main/java/com/label/service/FinetuneService.java
git mv src/main/java/com/label/module/source/service/SourceService.java src/main/java/com/label/service/SourceService.java
git mv src/main/java/com/label/module/task/service/TaskClaimService.java src/main/java/com/label/service/TaskClaimService.java
git mv src/main/java/com/label/module/task/service/TaskService.java src/main/java/com/label/service/TaskService.java
git mv src/main/java/com/label/module/user/service/AuthService.java src/main/java/com/label/service/AuthService.java
git mv src/main/java/com/label/module/user/service/UserService.java src/main/java/com/label/service/UserService.java
git mv src/main/java/com/label/module/video/service/VideoProcessService.java src/main/java/com/label/service/VideoProcessService.java
统一替换:
package com.label.service;
并把 ExtractionController、QaController、SysConfigController、ExportController、SourceController、TaskController、AuthController、UserController、VideoController 的服务导包改到 com.label.service.*。
- Step 4: 跑绿
Run:
mvn -q "-Dtest=PackageStructureMigrationTest#serviceTypesMoved" testmvn -q -DskipTests compile
Expected: PASS。
- Step 5: Commit
git add src/main/java/com/label/service src/main/java/com/label/module/annotation/controller/ExtractionController.java src/main/java/com/label/module/annotation/controller/QaController.java src/main/java/com/label/module/config/controller/SysConfigController.java src/main/java/com/label/module/export/controller/ExportController.java src/main/java/com/label/module/source/controller/SourceController.java src/main/java/com/label/module/task/controller/TaskController.java src/main/java/com/label/module/user/controller/AuthController.java src/main/java/com/label/module/user/controller/UserController.java src/main/java/com/label/module/video/controller/VideoController.java src/test/java/com/label/unit/PackageStructureMigrationTest.java
git commit -m "refactor: flatten service packages"
Task 4: 迁移控制器并收敛 OpenAPI 测试
Files:
-
Modify:
src/test/java/com/label/unit/PackageStructureMigrationTest.java -
Modify:
src/test/java/com/label/unit/OpenApiAnnotationTest.java -
Test:
src/test/java/com/label/unit/PackageStructureMigrationTest.java -
Test:
src/test/java/com/label/unit/OpenApiAnnotationTest.java -
Step 1: 写失败测试
@Test
@DisplayName("控制器已迁移到扁平 controller 目录")
void controllerTypesMoved() {
for (String fqcn : java.util.List.of(
"com.label.controller.AuthController", "com.label.controller.UserController",
"com.label.controller.SourceController", "com.label.controller.TaskController",
"com.label.controller.ExtractionController", "com.label.controller.QaController",
"com.label.controller.ExportController", "com.label.controller.SysConfigController",
"com.label.controller.VideoController")) {
assertClassExists(fqcn);
}
}
- Step 2: 跑红
Run: mvn -q "-Dtest=PackageStructureMigrationTest#controllerTypesMoved" test
Expected: FAIL,提示 com.label.controller.* 不存在。
- Step 3: 最小实现
执行迁移:
git mv src/main/java/com/label/module/annotation/controller/ExtractionController.java src/main/java/com/label/controller/ExtractionController.java
git mv src/main/java/com/label/module/annotation/controller/QaController.java src/main/java/com/label/controller/QaController.java
git mv src/main/java/com/label/module/config/controller/SysConfigController.java src/main/java/com/label/controller/SysConfigController.java
git mv src/main/java/com/label/module/export/controller/ExportController.java src/main/java/com/label/controller/ExportController.java
git mv src/main/java/com/label/module/source/controller/SourceController.java src/main/java/com/label/controller/SourceController.java
git mv src/main/java/com/label/module/task/controller/TaskController.java src/main/java/com/label/controller/TaskController.java
git mv src/main/java/com/label/module/user/controller/AuthController.java src/main/java/com/label/controller/AuthController.java
git mv src/main/java/com/label/module/user/controller/UserController.java src/main/java/com/label/controller/UserController.java
git mv src/main/java/com/label/module/video/controller/VideoController.java src/main/java/com/label/controller/VideoController.java
统一替换:
package com.label.controller;
并把 OpenApiAnnotationTest.java 的 import 替换成:
import com.label.controller.AuthController;
import com.label.controller.UserController;
import com.label.controller.SourceController;
import com.label.controller.TaskController;
import com.label.controller.ExtractionController;
import com.label.controller.QaController;
import com.label.controller.ExportController;
import com.label.controller.SysConfigController;
import com.label.controller.VideoController;
import com.label.dto.LoginRequest;
import com.label.dto.LoginResponse;
import com.label.dto.UserInfoResponse;
import com.label.dto.TaskResponse;
import com.label.dto.SourceResponse;
- Step 4: 跑绿
Run: mvn -q "-Dtest=PackageStructureMigrationTest#controllerTypesMoved,OpenApiAnnotationTest" test
Expected: PASS。
- Step 5: Commit
git add src/main/java/com/label/controller src/test/java/com/label/unit/PackageStructureMigrationTest.java src/test/java/com/label/unit/OpenApiAnnotationTest.java
git commit -m "refactor: flatten controller packages"
Task 5: 清理旧包残留并做全量回归
Files:
-
Modify:
src/test/java/com/label/unit/PackageStructureMigrationTest.java -
Test:
src/test/java/com/label/LabelBackendApplicationTests.java -
Test:
src/test/java/com/label/unit/ApplicationConfigTest.java -
Test:
src/test/java/com/label/unit/ShiroConfigTest.java -
Test:
src/test/java/com/label/unit/StateMachineTest.java -
Test:
src/test/java/com/label/unit/TokenFilterTest.java -
Test:
src/test/java/com/label/integration/AuthIntegrationTest.java -
Test:
src/test/java/com/label/integration/ExportIntegrationTest.java -
Test:
src/test/java/com/label/integration/ExtractionApprovalIntegrationTest.java -
Test:
src/test/java/com/label/integration/MultiTenantIsolationTest.java -
Test:
src/test/java/com/label/integration/QaApprovalIntegrationTest.java -
Test:
src/test/java/com/label/integration/ShiroFilterIntegrationTest.java -
Test:
src/test/java/com/label/integration/SourceIntegrationTest.java -
Test:
src/test/java/com/label/integration/SysConfigIntegrationTest.java -
Test:
src/test/java/com/label/integration/TaskClaimConcurrencyTest.java -
Test:
src/test/java/com/label/integration/UserManagementIntegrationTest.java -
Test:
src/test/java/com/label/integration/VideoCallbackIdempotencyTest.java -
Step 1: 写最终守卫测试
在 PackageStructureMigrationTest.java 中补充:
@Test
@DisplayName("源码中不再引用旧的 module、common.aop、common.config 包")
void sourceTreeHasNoLegacyPackageReferences() throws Exception {
try (java.util.stream.Stream<java.nio.file.Path> paths = java.nio.file.Files.walk(java.nio.file.Path.of("src"))) {
java.util.List<String> violations = paths
.filter(path -> path.toString().endsWith(".java"))
.map(path -> {
try {
String text = java.nio.file.Files.readString(path);
boolean legacy = text.contains("com.label.module.")
|| text.contains("com.label.common.aop")
|| text.contains("com.label.common.config");
return legacy ? path.toString() : null;
} catch (Exception e) {
throw new RuntimeException(e);
}
})
.filter(java.util.Objects::nonNull)
.toList();
org.assertj.core.api.Assertions.assertThat(violations).isEmpty();
}
}
- Step 2: 跑红并定位残留
Run:
mvn -q "-Dtest=PackageStructureMigrationTest#sourceTreeHasNoLegacyPackageReferences" testrg -n "com\.label\.module\.|com\.label\.common\.aop|com\.label\.common\.config" src/main/java src/test/java
Expected: 初次 FAIL,并列出残留文件。
- Step 3: 清理残留并确认旧目录为空
Run:
Get-ChildItem -Path src/main/java/com/label/module -Recurse
Expected: 没有 Java 文件残留;确认后删除空目录。
- Step 4: 全量回归
Run: mvn clean test
Expected: BUILD SUCCESS。
- Step 5: Commit
git add src/main/java src/test/java
git commit -m "refactor: complete backend directory flattening"
自检
- 覆盖了
annotation / aspect / config / event / listener / dto / entity / mapper / service / controller - 没有引入
service.impl - 没有拆分
dto/request、dto/response - 最终门槛是
mvn clean test