27 KiB
任务清单:label_backend 知识图谱智能标注平台
输入: /specs/001-label-backend-spec/ 全部设计文档
前置条件: plan.md ✅ | spec.md ✅ | research.md ✅ | data-model.md ✅ | contracts/ ✅ | quickstart.md ✅
格式说明
- [P]: 可并行执行(不同文件,无未完成任务的依赖)
- [USn]: 对应 spec.md 中的用户故事编号
- 每条任务包含精确的文件路径
Phase 1: 项目初始化
目标: 创建 Maven 项目骨架、基础配置和 Docker 环境
- T001 创建 Maven 项目骨架(
com.labelGroupId,label-backendArtifactId,Java 17 编译目标) - T002 配置
pom.xml(Spring Boot 3、Apache Shiro 1.13.x、MyBatis Plus 3.5.x、Spring Data Redis、AWS S3 SDK v2、Testcontainers、Lombok) - T003 [P] 创建
sql/init.sql(按依赖顺序建全部 11 张表:sys_company → sys_user → source_data → annotation_task → annotation_result → training_dataset → export_batch → sys_config → sys_operation_log → annotation_task_history → video_process_job;含所有索引和初始配置数据) - T004 [P] 创建
docker-compose.yml(postgres、redis、rustfs、backend、ai-service、frontend 六个服务,含健康检查)和后端Dockerfile(eclipse-temurin:17-jre-alpine) - T005 创建
src/main/resources/application.yml(数据源、Redis、RustFS、AI 服务 base-url、Shiro 相关配置项)
检查点: Maven 编译通过(mvn compile),Docker Compose up -d 全部服务健康
Phase 2: 公共基础设施(阻塞性前置条件)
目标: 所有业务模块依赖的公共组件。必须全部完成后用户故事阶段才能开始
⚠️ 重要: 此阶段未完成前任何用户故事均不可开始实现
- T006 创建
Result<T>、ResultCode、PageResult<T>—src/main/java/com/label/common/result/(统一响应格式:{"code":"SUCCESS","data":{...}}) - T007 [P] 创建
BusinessException(含code、message、httpStatus)和GlobalExceptionHandler(@RestControllerAdvice)—src/main/java/com/label/common/exception/ - T008 [P] 创建
CompanyContext(ThreadLocal,含set/get/clear三个方法,clear 必须在 finally 块调用)—src/main/java/com/label/common/context/CompanyContext.java - T009 创建
RedisKeyManager(三个静态方法:tokenKey、userPermKey、taskClaimKey)和RedisService—src/main/java/com/label/common/redis/ - T010 创建 MyBatis Plus 配置类
MybatisPlusConfig,注册TenantLineInnerInterceptor(从CompanyContext获取companyId自动注入 WHERE 子句;sys_company、sys_config加入忽略表列表)—src/main/java/com/label/common/config/MybatisPlusConfig.java - T011 创建
StateValidator(assertTransition泛型方法,违规时抛出BusinessException("INVALID_STATE_TRANSITION",...))—src/main/java/com/label/common/statemachine/StateValidator.java - T012 [P] 创建
SourceStatus枚举(PENDING/PREPROCESSING/EXTRACTING/QA_REVIEW/APPROVED,含 TRANSITIONS Map)—src/main/java/com/label/common/statemachine/SourceStatus.java - T013 [P] 创建
TaskStatus枚举(UNCLAIMED/IN_PROGRESS/SUBMITTED/APPROVED/REJECTED,含 TRANSITIONS Map,含 IN_PROGRESS→IN_PROGRESS 用于 ADMIN 强制转移)—src/main/java/com/label/common/statemachine/TaskStatus.java - T014 [P] 创建
DatasetStatus枚举(PENDING_REVIEW/APPROVED/REJECTED,含 TRANSITIONS Map)—src/main/java/com/label/common/statemachine/DatasetStatus.java - T015 [P] 创建
VideoJobStatus枚举(PENDING/RUNNING/SUCCESS/FAILED/RETRYING,含 TRANSITIONS Map,注释说明 FAILED→PENDING 由 ADMIN 手动触发)—src/main/java/com/label/common/statemachine/VideoJobStatus.java - T016 创建
@OperationLog注解(type和targetType两个属性,@Around级别)—src/main/java/com/label/common/aop/OperationLog.java - T017 创建
AuditAspect(@Around("@annotation(operationLog)"),在 finally 块以独立操作写入sys_operation_log;审计写入失败只记录 error 日志,禁止抛出异常回滚业务)—src/main/java/com/label/common/aop/AuditAspect.java - T018 [P] 创建
RustFsClient(AWS S3 SDK v2 封装,endpoint 指向 RustFS;实现upload、download、delete、getPresignedUrl)—src/main/java/com/label/common/storage/RustFsClient.java - T019 [P] 创建
AiServiceClient(RestClient封装,8 个端点:extractText、extractImage、extractFrames、videoToText、genTextQa、genImageQa、startFinetune、getFinetuneStatus)—src/main/java/com/label/common/ai/AiServiceClient.java - T020 创建 Shiro 三件套:
TokenFilter(解析Authorization: Bearer {uuid},查 Redistoken:{uuid},注入CompanyContext,请求结束 finally 清理 ThreadLocal)、UserRealm(先查 Redisuser:perm:{userId}TTL 5min,未命中查 PG;含addInheritedRoles)、ShiroConfig(过滤器链:/api/auth/login→anon,/api/**→tokenFilter)—src/main/java/com/label/common/shiro/ - T021 创建
AbstractIntegrationTest(Testcontainers,启动真实 PostgreSQL + Redis 容器,执行 sql/init.sql,注入测试用的公司和用户数据)—src/test/java/com/label/AbstractIntegrationTest.java - T022 集成测试:
ShiroFilterIntegrationTest(无 Token → 401;有效 Token 但角色不足 → 403;有效 Token 且角色满足 → 200)—src/test/java/com/label/integration/ShiroFilterIntegrationTest.java - T023 单元测试:
StateMachineTest(验证所有枚举的合法转换通过;非法转换抛出BusinessException("INVALID_STATE_TRANSITION"))—src/test/java/com/label/unit/StateMachineTest.java
检查点: 基础设施就绪,所有 Phase 3+ 的用户故事可并行开始
Phase 3: 用户故事 1 — 用户登录与身份认证(优先级: P1)🎯 MVP
目标: 用户可以用用户名和密码登录,获得会话凭证,使用凭证访问受保护接口,退出后凭证立即失效
独立测试: 登录 → 获取 Token → 访问 /api/auth/me 返回用户信息 → 退出 → 再次访问返回 401
- T024 [P] [US1] 创建
SysCompany实体(MyBatis Plus@TableName)和SysCompanyMapper—src/main/java/com/label/module/user/entity/SysCompany.java+mapper/SysCompanyMapper.java - T025 [P] [US1] 创建
SysUser实体(passwordHash字段加@JsonIgnore)和SysUserMapper(含selectByCompanyAndUsername方法)—src/main/java/com/label/module/user/entity/SysUser.java+mapper/SysUserMapper.java - T026 [US1] 实现
AuthService:login()(BCrypt 校验密码 → UUID v4 Token → Redis Hash 存储 userId/role/companyId/username → 设置 TTL =token_ttl_seconds配置值);logout()(删除 Redis Token Key)—src/main/java/com/label/module/user/service/AuthService.java - T027 [US1] 实现
AuthController:POST /api/auth/login(anon,调用AuthService.login())、POST /api/auth/logout(已登录)、GET /api/auth/me(返回当前用户信息);所有响应用Result<T>包装 —src/main/java/com/label/module/user/controller/AuthController.java - T028 [US1] 集成测试:正确密码登录返回 Token;Token 有效时
/api/auth/me返回 200;主动退出后再访问返回 401;错误密码登录返回 401 —src/test/java/com/label/integration/AuthIntegrationTest.java
检查点: US1 独立可测试 — 登录/退出流程完整可用
Phase 4: 用户故事 2 — 原始资料上传(优先级: P1)
目标: 上传员可以上传文本/图片/视频,查询自己的资料列表;管理员可查看全公司资料
独立测试: 上传文本文件 → 列表查到 → 详情含预签名 URL → 管理员可删除
- T029 [P] [US2] 创建
SourceData实体(含parentSourceId自引用字段)和SourceDataMapper(含updateStatus方法)—src/main/java/com/label/module/source/entity/SourceData.java+mapper/SourceDataMapper.java - T030 [US2] 实现
SourceService:upload()(先 insert 获取 ID → 构造路径 → 上传 RustFS → 更新 filePath);list()(UPLOADER 按uploaderId过滤,ADMIN 不过滤,强制分页);findById()(含 15 分钟预签名 URL);delete()(仅 PENDING 状态可删,同步删 RustFS 文件)—src/main/java/com/label/module/source/service/SourceService.java - T031 [US2] 实现
SourceController(POST /api/source/upload、GET /api/source/list、GET /api/source/{id}、DELETE /api/source/{id};@RequiresRoles注解声明权限;所有响应Result<T>包装)—src/main/java/com/label/module/source/controller/SourceController.java - T032 [US2] 集成测试:UPLOADER 上传文本/图片 → 列表仅返回自己的资料;ADMIN 查看列表返回全部;上传视频 → source_data 状态为 PENDING(视频预处理 Phase 9 覆盖);已进入流水线的资料删除返回 409 —
src/test/java/com/label/integration/SourceIntegrationTest.java
检查点: US2 独立可测试 — 上传/查询/删除流程完整可用
Phase 5: 用户故事 3+4 — 提取阶段标注与审批(优先级: P1)
目标: 标注员可以领取任务(并发安全)、AI 辅助预标注、编辑并提交;审批员可以通过(自动触发 QA 任务)或驳回(标注员可重领)
独立测试: 创建任务 → 标注员领取 → AI 预标注 → 提交 → 审批通过 → QA 任务自动出现在任务池
实体与数据层
- T033 [P] [US3] 创建
AnnotationTask实体 +AnnotationTaskMapper(含claimTask(taskId, userId, companyId)方法,SQL:UPDATE ... SET status='IN_PROGRESS', claimed_by=?, claimed_at=NOW() WHERE id=? AND status='UNCLAIMED' AND company_id=?,返回影响行数)—src/main/java/com/label/module/task/entity/AnnotationTask.java+mapper/AnnotationTaskMapper.java - T034 [P] [US3] 创建
AnnotationTaskHistory实体 +TaskHistoryMapper—src/main/java/com/label/module/task/entity/AnnotationTaskHistory.java+mapper/TaskHistoryMapper.java - T035 [P] [US3] 创建
AnnotationResult实体 +AnnotationResultMapper(含updateResultJson整体覆盖方法和selectByTaskId方法)—src/main/java/com/label/module/annotation/entity/AnnotationResult.java+mapper/AnnotationResultMapper.java
任务管理服务与控制器
- T036 [US3] 实现
TaskClaimService.claim()(① RedisSET NX task:claim:{taskId}TTL 30s,失败抛TASK_CLAIMED;② DBclaimTask()影响行数为 0 时抛TASK_CLAIMED;③insertHistory(UNCLAIMED→IN_PROGRESS))和unclaim()(StateValidator + 清 Redis 锁 + 历史)和reclaim()(校验 REJECTED + claimedBy = 当前用户 + REJECTED→IN_PROGRESS + 历史)—src/main/java/com/label/module/task/service/TaskClaimService.java - T037 [US3] 实现
TaskService(createTask、getPool(按角色过滤:ANNOTATOR→UNCLAIMED/EXTRACTION;REVIEWER→SUBMITTED)、getMine(含 IN_PROGRESS/SUBMITTED/REJECTED)、getPendingReview(SUBMITTED,分页)、getById、reassign(ADMIN,仅更新 claimedBy + 历史))—src/main/java/com/label/module/task/service/TaskService.java - T038 [US3] 实现
TaskController(10 个端点:POST /api/tasks、GET /api/tasks/pool、POST /api/tasks/{id}/claim、POST /api/tasks/{id}/unclaim、GET /api/tasks/mine、POST /api/tasks/{id}/reclaim、GET /api/tasks/pending-review、GET /api/tasks/{id}、GET /api/tasks、PUT /api/tasks/{id}/reassign)—src/main/java/com/label/module/task/controller/TaskController.java
提取标注服务与控制器
- T039 [US3] 实现
ExtractionService.aiPreAnnotate()(调用AiServiceClient.extractText/extractImage,写入annotation_result)和updateResult()(整体覆盖result_json,校验 JSON 格式)—src/main/java/com/label/module/annotation/service/ExtractionService.java - T040 [US3] 实现
ExtractionService.submit()(@Transactional:IN_PROGRESS→SUBMITTED +submitted_at+ insertHistory)—src/main/java/com/label/module/annotation/service/ExtractionService.java - T041 [US4] 创建
ExtractionApprovedEvent(携带taskId、sourceId、sourceType、companyId)—src/main/java/com/label/module/annotation/event/ExtractionApprovedEvent.java - T042 [US4] 实现
ExtractionService.approve()(@Transactional:① 自审校验;②is_final=true;③ SUBMITTED→APPROVED +completedAt+ 历史;④publishEvent(ExtractionApprovedEvent);AI 调用禁止在此事务内执行)—src/main/java/com/label/module/annotation/service/ExtractionService.java - T043 [US4] 实现
ExtractionApprovedEventListener(@TransactionalEventListener(AFTER_COMMIT)+@Transactional(REQUIRES_NEW):调用 AI 生成候选问答对 → 写training_dataset(PENDING_REVIEW)→ 创建 QA_GENERATION 任务(UNCLAIMED)→source_data状态→ QA_REVIEW)—src/main/java/com/label/module/annotation/service/ExtractionApprovedEventListener.java - T044 [US4] 实现
ExtractionService.reject()(@Transactional:① 自审校验;② StateValidator;③ SUBMITTED→REJECTED + 历史)—src/main/java/com/label/module/annotation/service/ExtractionService.java - T045 [US4] 实现
ExtractionController(5 个端点:GET /api/extraction/{taskId}、PUT /api/extraction/{taskId}、POST /api/extraction/{taskId}/submit、POST /api/extraction/{taskId}/approve、POST /api/extraction/{taskId}/reject)—src/main/java/com/label/module/annotation/controller/ExtractionController.java
集成测试
- T046 [US3] 并发集成测试:10 个线程同时争抢同一 UNCLAIMED 任务,验证恰好 1 人成功、其余均收到
TASK_CLAIMED错误、DB 中claimed_by唯一 —src/test/java/com/label/integration/TaskClaimConcurrencyTest.java - T047 [US4] 集成测试:审批通过 → QA 任务自动出现在任务池;自审返回
SELF_REVIEW_FORBIDDEN403;驳回后标注员可重领并再次提交 —src/test/java/com/label/integration/ExtractionApprovalIntegrationTest.java
检查点: US3+US4 独立可测试 — 完整提取流水线(领取→标注→提交→审批→QA任务自动创建)可用
Phase 6: 用户故事 5 — 问答生成阶段标注与审批(优先级: P2)
目标: 标注员领取 QA 任务、修改候选问答对并提交;审批员通过后训练样本入库,整条流水线完成
独立测试: 领取 QA 任务 → 修改问答对 → 提交 → 审批通过 → training_dataset 状态 APPROVED,source_data 状态 APPROVED
- T048 [P] [US5] 创建
TrainingDataset实体 +TrainingDatasetMapper(含approveByTaskId、deleteByTaskId方法)—src/main/java/com/label/module/annotation/entity/TrainingDataset.java+mapper/TrainingDatasetMapper.java - T049 [US5] 实现
QaService.updateResult()(整体覆盖问答对 JSONB)和submit()(@Transactional:IN_PROGRESS→SUBMITTED + 历史)—src/main/java/com/label/module/annotation/service/QaService.java - T050 [US5] 实现
QaService.approve()(@Transactional:①validateAndGetTask先于一切 DB 写入;② 自审校验;③training_dataset→ APPROVED;④annotation_task→ APPROVED + 历史;⑤source_data→ APPROVED)—src/main/java/com/label/module/annotation/service/QaService.java - T051 [US5] 实现
QaService.reject()(@Transactional:① 自审校验;②deleteByTaskId清除候选问答对;③ SUBMITTED→REJECTED + 历史;④source_data保持 QA_REVIEW 不变)—src/main/java/com/label/module/annotation/service/QaService.java - T052 [US5] 实现
QaController(5 个端点:GET /api/qa/{taskId}、PUT /api/qa/{taskId}、POST /api/qa/{taskId}/submit、POST /api/qa/{taskId}/approve、POST /api/qa/{taskId}/reject)—src/main/java/com/label/module/annotation/controller/QaController.java - T053 [US5] 集成测试:QA 审批通过 →
training_dataset.status = APPROVED,source_data.status = APPROVED;QA 驳回 → 候选记录被删除,标注员可重领 —src/test/java/com/label/integration/QaApprovalIntegrationTest.java
检查点: US5 独立可测试 — 完整 QA 流水线可用,training_dataset 产出验证通过
Phase 7: 用户故事 6 — 训练数据导出与微调提交(优先级: P2)
目标: 管理员将已审批样本批量导出为 JSONL,并可提交 GLM 微调任务
独立测试: 选取已审批样本 → 创建批次 → RustFS 中存在 JSONL 文件 → 提交微调 → 可查询状态
- T054 [P] [US6] 创建
ExportBatch实体 +ExportBatchMapper—src/main/java/com/label/module/export/entity/ExportBatch.java+mapper/ExportBatchMapper.java - T055 [US6] 实现
ExportService.createBatch()(@Transactional:① 校验全部样本为 APPROVED;② 生成 JSONL(每行一个glm_format_json);③ 上传 RustFSfinetune-export/export/{batchUuid}.jsonl;④ 批量更新export_batch_id/exported_at;⑤ 插入export_batch记录)—src/main/java/com/label/module/export/service/ExportService.java - T056 [US6] 实现
FinetuneService:trigger()(调用AiServiceClient.startFinetune(),更新glm_job_id和finetune_status = RUNNING)和getStatus()(调用AiServiceClient.getFinetuneStatus())—src/main/java/com/label/module/export/service/FinetuneService.java - T057 [US6] 实现
ExportController(GET /api/training/samples、POST /api/export/batch、POST /api/export/{batchId}/finetune、GET /api/export/{batchId}/status、GET /api/export/list;全部@RequiresRoles("ADMIN"))—src/main/java/com/label/module/export/controller/ExportController.java - T058 [US6] 集成测试:成功创建批次后 JSONL 文件存在于 RustFS;包含非 APPROVED 样本时返回
INVALID_SAMPLES400 —src/test/java/com/label/integration/ExportIntegrationTest.java
检查点: US6 独立可测试 — 导出批次创建和微调提交流程可用
Phase 8: 用户故事 7 — 用户与权限管理(优先级: P2)
目标: 管理员可以创建用户、变更角色(立即生效)、禁用账号(立即失效)
独立测试: 创建标注员用户 → 验证其能领取任务 → 升为审批员 → 验证立即可以审批 → 禁用账号 → 已有 Token 立即失效
- T059 [US7] 实现
UserService:createUser()(BCrypt 哈希密码,强度因子 ≥ 10);updateUser();updateRole()(DB 写入后立即redisTemplate.delete(userPermKey(userId)));updateStatus()(禁用时删 Redis Token + 权限缓存)—src/main/java/com/label/module/user/service/UserService.java - T060 [US7] 实现
UserController(GET /api/users、POST /api/users、PUT /api/users/{id}、PUT /api/users/{id}/status、PUT /api/users/{id}/role;全部@RequiresRoles("ADMIN"))—src/main/java/com/label/module/user/controller/UserController.java - T061 [US7] 集成测试:变更角色后权限下一次请求立即生效(无需重新登录);禁用账号后现有 Token 下一次请求立即返回 401 —
src/test/java/com/label/integration/UserManagementIntegrationTest.java
检查点: US7 独立可测试 — 用户管理和即时权限变更可用
Phase 9: 用户故事 8 — 视频处理与系统配置(优先级: P3)
目标: 上传视频后触发异步预处理(帧提取/转文字);AI 回调幂等处理;管理员可配置 Prompt 模板等系统参数
独立测试(视频): 上传视频 → 创建处理任务 → 模拟成功回调 → annotation_task 出现在任务池;重复成功回调 → 任务数量不增加
独立测试(配置): 为公司设置专属 Prompt → 验证该公司使用新值;其他公司使用全局默认
- T062 [P] [US8] 创建
VideoProcessJob实体 +VideoProcessJobMapper—src/main/java/com/label/module/video/entity/VideoProcessJob.java+mapper/VideoProcessJobMapper.java - T063 [P] [US8] 创建
SysConfig实体 +SysConfigMapper(含selectByCompanyAndKey(companyId, configKey)方法,支持companyId IS NULL查询)—src/main/java/com/label/module/config/entity/SysConfig.java+mapper/SysConfigMapper.java - T064 [US8] 实现
VideoProcessService:createJob()(@Transactional:source_data.status → PREPROCESSING+ 插入 job + 触发 AI 异步调用);handleCallback()(@Transactional:幂等检查 status==SUCCESS 则 return;成功 → SUCCESS +source_data.status → PENDING;失败 → 按 retry_count 决定 RETRYING 或 FAILED);reset()(FAILED → PENDING)—src/main/java/com/label/module/video/service/VideoProcessService.java - T065 [US8] 实现
VideoController(POST /api/video/process、GET /api/video/jobs/{jobId}、POST /api/video/jobs/{jobId}/reset、POST /api/video/callback(内部接口,IP 白名单或服务密钥保护))—src/main/java/com/label/module/video/controller/VideoController.java - T066 [US8] 实现
SysConfigService.get(configKey)(先按(companyId, key)查;未命中按(NULL, key)查全局默认)和update(key, value)(UPSERT:公司级配置不存在则创建,存在则覆盖)—src/main/java/com/label/module/config/service/SysConfigService.java - T067 [US8] 实现
SysConfigController(GET /api/config(合并公司级 + 全局,标注 scope)、PUT /api/config/{key};均@RequiresRoles("ADMIN"))—src/main/java/com/label/module/config/controller/SysConfigController.java - T068 [US8] 集成测试:同一 jobId 两次成功回调,
annotation_task记录数为 1(幂等);达最大重试次数后 status = FAILED —src/test/java/com/label/integration/VideoCallbackIdempotencyTest.java - T069 [US8] 集成测试:公司级配置覆盖同 Key 的全局默认;其他公司读取全局默认 —
src/test/java/com/label/integration/SysConfigIntegrationTest.java
检查点: US8 独立可测试 — 视频处理幂等和配置管理可用
Phase 10: 收尾与横切关注点
目标: 多租户隔离验证、整体合规检查、快速启动验证
- T070 集成测试:
MultiTenantIsolationTest(公司 A 身份查询公司 B 的资料/任务 → 返回空列表或 404,不泄露数据)—src/test/java/com/label/integration/MultiTenantIsolationTest.java - T071 [P] 代码审查:检查所有 Controller 方法返回值均为
Result<T>或Result<PageResult<T>>,无裸 POJO 或裸 List 返回 - T072 [P] 代码审查:检查所有列表查询方法均含分页参数(
page/pageSize),无selectAll()或不分页的查询 - T073 [P] 代码审查:检查
sys_operation_log相关代码,确认应用层零处 UPDATE 或 DELETE - T074 [P] 代码审查:检查所有
@Transactional方法内无AiServiceClient的同步 HTTP 调用(审批触发 AI 必须通过@TransactionalEventListener) - T075 运行
quickstart.md端到端验证:docker compose up -d→ 登录 → 上传文件 → 创建任务 → 领取 → 提交 → 审批通过 → 确认 QA 任务出现
依赖关系与执行顺序
阶段依赖
Phase 1(初始化)
↓
Phase 2(基础设施)[全部完成后解锁所有用户故事]
↓
Phase 3(US1 认证) ← 可与 Phase 4/5/6/7/8/9 并行
Phase 4(US2 上传) ← 依赖 Phase 2,独立于其他用户故事
Phase 5(US3+4 提取) ← 依赖 Phase 2(上传已有资料的集成测试依赖 US2)
Phase 6(US5 QA) ← 依赖 Phase 5 完成(QA 任务由提取审批自动创建)
Phase 7(US6 导出) ← 依赖 Phase 6 完成(需要 APPROVED 的 training_dataset)
Phase 8(US7 用户管理) ← 依赖 Phase 3(UserService 在 AuthService 基础上扩展)
Phase 9(US8 视频+配置) ← 依赖 Phase 2,其余独立
↓
Phase 10(收尾)
用户故事间依赖
- US1(认证): 仅依赖 Phase 2,完全独立
- US2(上传): 仅依赖 Phase 2,完全独立
- US3+4(提取): 依赖 Phase 2;集成测试中使用已上传资料需 US2
- US5(QA): 依赖 US3+4(QA 任务来源于提取阶段审批通过的级联触发)
- US6(导出): 依赖 US5(需要 APPROVED 状态的 training_dataset)
- US7(用户管理): 依赖 US1(UserService 扩展 AuthService 的用户实体)
- US8(视频+配置): 仅依赖 Phase 2
阶段内并行机会
- Phase 2:T007-T010、T012-T015、T018-T019 均可并行(独立文件)
- Phase 3:T024、T025 可并行(独立文件)
- Phase 5:T033、T034、T035 可并行(独立文件)
- Phase 9:T062、T063 可并行(独立文件)
- Phase 10:T071-T074 全部可并行(仅代码审查,无文件修改)
并行执行示例
Phase 2 基础设施并行
同时启动:
任务: "创建 BusinessException、GlobalExceptionHandler — common/exception/" [T007]
任务: "创建 CompanyContext(ThreadLocal)— common/context/" [T008]
任务: "创建 RustFsClient — common/storage/" [T018]
任务: "创建 AiServiceClient — common/ai/" [T019]
任务: "创建 SourceStatus 枚举" [T012]
任务: "创建 TaskStatus 枚举" [T013]
Phase 5 提取阶段并行
同时启动(实体/Mapper):
任务: "创建 AnnotationTask 实体 + Mapper" [T033]
任务: "创建 AnnotationTaskHistory 实体 + Mapper" [T034]
任务: "创建 AnnotationResult 实体 + Mapper" [T035]
实施策略
MVP 优先(仅用户故事 1)
- 完成 Phase 1(初始化)
- 完成 Phase 2(基础设施)— 关键,阻塞所有故事
- 完成 Phase 3(US1 认证)
- 停止并验证: 登录/退出/权限校验全流程可用
- 可以独立部署演示认证功能
增量交付
- Phase 1 + Phase 2 → 基础就绪
- Phase 3(US1)→ 验证 → 演示(MVP)
- Phase 4(US2)→ 验证 → 演示(上传功能)
- Phase 5(US3+4)→ 验证 → 演示(标注流程)
- Phase 6(US5)→ 验证 → 演示(完整双阶段流水线)
- Phase 7(US6)→ 验证 → 演示(训练数据产出)
- Phase 8+9 → 验证 → 演示(完整平台)
- Phase 10 → 收尾
多人协作策略
Phase 2 完成后:
- 开发者 A:Phase 3(US1 认证)+ Phase 8(US7 用户管理)
- 开发者 B:Phase 4(US2 上传)+ Phase 5(US3+4 提取)
- 开发者 C:Phase 9(US8 视频+配置)
Phase 5 完成后:
- 开发者 A/B 合力:Phase 6(US5 QA)→ Phase 7(US6 导出)
说明
[P]任务 = 不同文件,无依赖,可并行[USn]标签将任务映射到具体用户故事,便于追踪- 每个用户故事应独立可完成和可测试
- 每完成一个阶段后提交 git commit
- 在每个检查点停下来独立验证该用户故事
- 避免:模糊任务、同文件并发冲突、破坏独立性的跨故事依赖