mi-assessment/.kiro/specs/redis-report-queue/tasks.md
2026-02-25 11:00:04 +08:00

9.5 KiB
Raw Blame History

Implementation Plan: Redis 报告生成队列

Overview

将测评报告生成从同步调用改为 Redis List 异步队列模式。按照自底向上的顺序实现:先扩展 Redis 基础设施,再构建队列生产者/消费者,然后改造现有服务,最后添加后台管理接口。每一步都在前一步基础上构建,确保无孤立代码。

Tasks

  • 1. 扩展 IRedisService 接口与 RedisService 实现List 操作)

    • 1.1 在 IRedisService 中新增 ListLeftPushAsyncListRightPopAsyncListLengthAsync 三个方法签名

      • 文件:MiAssessment.Core/Interfaces/IRedisService.cs
      • ListLeftPushAsync(string key, string value) 对应 LPUSH
      • ListRightPopAsync(string key, TimeSpan timeout) 对应 BRPOP超时返回 null
      • ListLengthAsync(string key) 对应 LLEN
      • Requirements: 4.1, 4.2, 4.3
    • 1.2 在 RedisService 中实现三个 List 方法

      • 文件:MiAssessment.Infrastructure/Cache/RedisService.cs
      • 使用 StackExchange.Redis 的 ListLeftPushAsyncListRightPopAsync(通过 ExecuteAsync("BRPOP", ...))、ListLengthAsync
      • 连接不可用时:ListLeftPushAsync 静默返回、ListRightPopAsync 返回 null、ListLengthAsync 返回 0
      • Requirements: 4.4
    • * 1.3 编写 RedisService List 方法的单元测试

      • 测试连接不可用时的静默降级行为
      • Requirements: 4.4
    • * 1.4 编写属性测试Redis List LPUSH/BRPOP round trip

      • Property 2: Redis List LPUSH/BRPOP round trip
      • Validates: Requirements 4.1, 4.2, 4.3
  • 2. 创建队列消息模型与生产者

    • 2.1 创建 ReportQueueMessage 模型类

      • 文件:MiAssessment.Core/Models/ReportQueueMessage.cs
      • 包含 RecordIdlongRetryCountintEnqueueTimeDateTime属性
      • 添加 XML 注释
      • Requirements: 1.1
    • * 2.2 编写属性测试:队列消息序列化/反序列化 round trip

      • Property 1: 队列消息序列化/反序列化 round trip
      • Validates: Requirements 1.1, 2.2
    • 2.3 创建 IReportQueueProducer 接口和 ReportQueueProducer 实现

      • 接口文件:MiAssessment.Core/Interfaces/IReportQueueProducer.cs
      • 实现文件:MiAssessment.Core/Services/ReportQueueProducer.cs
      • EnqueueAsync(long recordId) 方法:构造 ReportQueueMessageRetryCount=0, EnqueueTime=DateTime.Now序列化为 JSON调用 IRedisService.ListLeftPushAsync("report:queue", json)
      • 定义常量 ReportQueueKey = "report:queue"
      • Requirements: 1.1
    • 2.4 在 Autofac ServiceModule 中注册 IReportQueueProducer

      • 文件:MiAssessment.Infrastructure/Modules/ServiceModule.cs
      • 使用 InstancePerLifetimeScope 生命周期
      • Requirements: 1.1
    • * 2.5 编写 ReportQueueProducer 的单元测试

      • 验证调用 IRedisService.ListLeftPushAsync 的参数正确性key 为 report:queuevalue 为包含正确 RecordId 和 RetryCount=0 的 JSON
      • Requirements: 1.1
  • 3. Checkpoint - 确保基础设施层编译通过

    • 确保所有测试通过ask the user if questions arise.
  • 4. 创建 ReportQueueConsumerBackgroundService 消费者)

    • 4.1 创建 ReportQueueConsumer

      • 文件:MiAssessment.Api/BackgroundServices/ReportQueueConsumer.cs
      • 继承 BackgroundService,注入 IRedisServiceIServiceScopeFactoryILogger<ReportQueueConsumer>
      • ExecuteAsync 中循环调用 ListRightPopAsync("report:queue", 30s)
      • 反序列化消息,通过 scope 解析 ReportGenerationService 调用 GenerateReportAsync
      • 定义常量:MaxRetryCount=3DeadLetterQueueKey="report:queue:dead"RetryDelays=[10s, 30s, 60s]ErrorRecoveryDelay=5s
      • Requirements: 2.1, 2.2, 2.3, 2.4, 2.5
    • 4.2 实现失败重试与死信队列逻辑

      • 失败且 RetryCount < MaxRetryCount按退避时间等待后 RetryCount+1 重新 LPUSH 到 report:queue
      • 失败且 RetryCount = MaxRetryCountLPUSH 到 report:queue:dead,更新记录 Status=5
      • 消息反序列化失败:记录错误日志,丢弃消息
      • BRPOP 异常:记录错误日志,等待 5 秒后重新监听
      • Requirements: 3.1, 3.2, 3.3, 3.4
    • 4.3 在 Program.cs 中注册 ReportQueueConsumer

      • 文件:MiAssessment.Api/Program.cs
      • 使用 builder.Services.AddHostedService<ReportQueueConsumer>()
      • Requirements: 2.1
    • * 4.4 编写 ReportQueueConsumer 的单元测试

      • 验证成功消费后记录日志
      • 验证失败后重试入队RetryCount 递增)
      • 验证超过重试次数进入死信队列并更新状态为 5
      • Requirements: 2.2, 2.3, 3.1, 3.2, 3.3, 3.4
    • * 4.5 编写属性测试:失败重试递增 RetryCount

      • Property 3: 失败重试递增 RetryCount
      • Validates: Requirements 3.1
  • 5. 改造 AssessmentService同步改异步入队

    • 5.1 修改 AssessmentService.SubmitAnswersAsync

      • 文件:MiAssessment.Core/Services/AssessmentService.cs
      • 注入 IReportQueueProducer
      • 移除对 ReportGenerationService.GenerateReportAsync 的直接 await 调用
      • 改为调用 IReportQueueProducer.EnqueueAsync(recordId)
      • 入队失败时try-catch记录错误日志仍返回成功响应状态保持 3
      • Requirements: 1.1, 1.2, 1.3
    • * 5.2 编写 AssessmentService 改造后的单元测试

      • 验证不再直接调用 GenerateReportAsync
      • 验证调用 EnqueueAsync
      • 验证 Redis 失败时仍返回成功
      • Requirements: 1.1, 1.2, 1.3
  • 6. 添加生成失败状态支持Status=5

    • 6.1 扩展状态描述映射

      • AssessmentRecordService.StatusNames 字典中新增 { 5, "生成失败" }
      • AssessmentService.GetStatusText 中新增 5 => "生成失败"
      • AssessmentService.GetResultStatusAsync 中对 Status=5 返回描述"报告生成失败,请联系客服"
      • Requirements: 7.1, 7.2, 7.3
    • * 6.2 编写状态描述的单元测试

      • 验证 Status=5 返回"生成失败"
      • 验证 GetResultStatusAsync 对 Status=5 返回正确描述
      • Requirements: 7.1, 7.2
  • 7. Checkpoint - 确保核心队列功能完整

    • 确保所有测试通过ask the user if questions arise.
  • 8. 实现后台管理端重新生成接口

    • 8.1 创建请求/响应 DTO 模型

      • 文件目录:MiAssessment.Admin.Business/Models/AssessmentRecord/
      • 创建 RegenerateReportRequest(包含 Id 字段)
      • 创建 BatchRegenerateReportRequest(包含 Ids 列表字段)
      • 创建 BatchRegenerateResult(包含 SuccessCountSkippedCount 字段)
      • Requirements: 5.1, 6.1
    • 8.2 在 IAssessmentRecordService 中新增接口方法

      • 文件:MiAssessment.Admin.Business/Services/Interfaces/IAssessmentRecordService.cs
      • 新增 RegenerateReportAsync(long recordId) 方法
      • 新增 BatchRegenerateReportAsync(List<long> recordIds) 方法,返回 BatchRegenerateResult
      • Requirements: 5.1, 6.1
    • 8.3 在 AssessmentRecordService 中实现重新生成逻辑

      • 文件:MiAssessment.Admin.Business/Services/AssessmentRecordService.cs
      • 注入 IRedisService
      • RegenerateReportAsync:校验记录存在且状态为 3 或 5重置状态为 3清除已有测评结果数据构造 ReportQueueMessageRetryCount=0LPUSH 入队
      • BatchRegenerateReportAsync:遍历 ID 列表,对每条符合条件的记录执行重新生成逻辑,统计 SuccessCount 和 SkippedCount
      • 错误处理:记录不存在返回错误码 3241状态不符返回错误码 2005空列表返回错误码 1001
      • Requirements: 5.2, 5.3, 5.4, 6.2, 6.3, 6.4
    • 8.4 在 AssessmentRecordController 中新增两个接口

      • 文件:MiAssessment.Admin.Business/Controllers/AssessmentRecordController.cs
      • POST /api/admin/assessmentRecord/regenerateReport:接收 RegenerateReportRequest,调用 RegenerateReportAsync
      • POST /api/admin/assessmentRecord/batchRegenerateReport:接收 BatchRegenerateReportRequest,调用 BatchRegenerateReportAsync
      • Requirements: 5.1, 6.1
    • * 8.5 编写 AssessmentRecordService 重新生成逻辑的单元测试

      • 验证状态重置为 3、清除已有结果、入队新消息
      • 验证不存在记录返回错误
      • 验证非法状态返回错误
      • Requirements: 5.2, 5.3, 5.4
    • * 8.6 编写属性测试:重新生成重置状态并入队

      • Property 4: 重新生成重置状态并入队
      • Validates: Requirements 5.2
    • * 8.7 编写属性测试:非法状态拒绝重新生成

      • Property 5: 非法状态拒绝重新生成
      • Validates: Requirements 5.4
    • * 8.8 编写属性测试:批量重新生成按状态过滤

      • Property 6: 批量重新生成按状态过滤
      • Validates: Requirements 6.2
    • * 8.9 编写属性测试:批量操作计数不变量

      • Property 7: 批量操作计数不变量
      • Validates: Requirements 6.3
  • 9. Final checkpoint - 确保所有功能完整集成

    • 确保所有测试通过ask the user if questions arise.

Notes

  • Tasks marked with * are optional and can be skipped for faster MVP
  • Each task references specific requirements for traceability
  • Checkpoints ensure incremental validation
  • Property tests validate universal correctness properties from the design document
  • 所有代码使用 C#.NET 10遵循项目现有的 Autofac DI 和 RPC 风格 API 规范