21
This commit is contained in:
parent
ab9d7dd262
commit
8858e0eef3
362
.kiro/specs/assessment-report-generation/design.md
Normal file
362
.kiro/specs/assessment-report-generation/design.md
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
# 设计文档:测评结论生成服务
|
||||
|
||||
## 概述
|
||||
|
||||
测评结论生成服务(ReportGenerationService)是学业邑规划测评系统中衔接"答案提交"与"报告查看"的核心后端模块。用户完成80道测评题目提交答案后,`AssessmentService.SubmitAnswersAsync` 将测评记录状态设为3(生成中)并提交事务,随后调用本服务执行以下流水线:
|
||||
|
||||
1. **得分计算**:根据 ScoreRule(累加/二值)计算各叶子分类的得分、满分和百分比
|
||||
2. **排名计算**:在同一 CategoryType 维度内按百分比降序排名(支持并列)
|
||||
3. **星级评定**:根据排名位置分配1-5星
|
||||
4. **结论匹配**:将星级映射为结论类型,查询对应结论文本
|
||||
5. **结果持久化**:在单次事务中写入所有 AssessmentResult 记录并更新状态为4(已完成)
|
||||
|
||||
该服务作为独立类注入 DI 容器,使用 `MiAssessmentDbContext` 访问 Business 库。
|
||||
|
||||
### 设计决策
|
||||
|
||||
- **同步调用而非异步队列**:当前测评数据量(80题 × 8维度)计算量小,同步调用即可满足性能要求,避免引入消息队列的复杂度
|
||||
- **在 MiAssessment.Core 项目中实现**:与 AssessmentService 同层,便于直接依赖注入和调用
|
||||
- **复用 MiAssessmentDbContext**:遵循双数据库架构规范,小程序 API 通过此上下文读写 Business 库
|
||||
- **需在 MiAssessment.Model 中新增实体**:QuestionCategoryMapping 和 ReportConclusion 实体目前仅存在于 Admin.Business 项目中,需在 Model 项目中创建对应实体以供 Core 层使用
|
||||
|
||||
## 架构
|
||||
|
||||
### 调用流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as 小程序客户端
|
||||
participant API as AssessmentController
|
||||
participant AS as AssessmentService
|
||||
participant RGS as ReportGenerationService
|
||||
participant DB as MiAssessmentDbContext
|
||||
|
||||
Client->>API: POST /api/assessment/submitAnswers
|
||||
API->>AS: SubmitAnswersAsync(userId, request)
|
||||
AS->>DB: 保存答案 + 状态设为3(生成中)
|
||||
AS->>DB: CommitAsync()
|
||||
AS->>RGS: GenerateReportAsync(recordId)
|
||||
RGS->>DB: 查询答案、分类映射、结论
|
||||
RGS->>RGS: 计算得分 → 排名 → 星级 → 匹配结论
|
||||
RGS->>DB: BeginTransaction
|
||||
RGS->>DB: 删除旧结果 + 写入新结果 + 状态设为4
|
||||
RGS->>DB: CommitAsync()
|
||||
RGS-->>AS: 完成
|
||||
AS-->>API: SubmitAnswersResponse
|
||||
API-->>Client: { success: true }
|
||||
```
|
||||
|
||||
### 项目层级
|
||||
|
||||
```
|
||||
MiAssessment.Model/
|
||||
├── Entities/
|
||||
│ ├── QuestionCategoryMapping.cs ← 新增
|
||||
│ └── ReportConclusion.cs ← 新增
|
||||
└── Data/
|
||||
└── MiAssessmentDbContext.cs ← 新增 DbSet
|
||||
|
||||
MiAssessment.Core/
|
||||
└── Services/
|
||||
├── AssessmentService.cs ← 修改:调用 ReportGenerationService
|
||||
└── ReportGenerationService.cs ← 新增
|
||||
```
|
||||
|
||||
## 组件与接口
|
||||
|
||||
### ReportGenerationService
|
||||
|
||||
```csharp
|
||||
namespace MiAssessment.Core.Services;
|
||||
|
||||
/// <summary>
|
||||
/// 测评报告生成服务
|
||||
/// </summary>
|
||||
public class ReportGenerationService
|
||||
{
|
||||
private readonly MiAssessmentDbContext _dbContext;
|
||||
private readonly ILogger<ReportGenerationService> _logger;
|
||||
|
||||
public ReportGenerationService(
|
||||
MiAssessmentDbContext dbContext,
|
||||
ILogger<ReportGenerationService> logger);
|
||||
|
||||
/// <summary>
|
||||
/// 根据测评记录ID生成报告
|
||||
/// </summary>
|
||||
/// <param name="recordId">测评记录ID</param>
|
||||
public async Task GenerateReportAsync(long recordId);
|
||||
}
|
||||
```
|
||||
|
||||
### 内部处理步骤
|
||||
|
||||
`GenerateReportAsync` 方法内部按以下顺序执行:
|
||||
|
||||
1. **ValidateRecord(recordId)**:验证记录存在、未软删除、Status=3、有答案数据
|
||||
2. **LoadData(record)**:一次性加载答案、分类映射、叶子分类、结论数据
|
||||
3. **CalculateScores(answers, mappings, categories)**:按 ScoreRule 计算各分类得分
|
||||
4. **CalculateRanks(scores)**:按 CategoryType 分组排名
|
||||
5. **AssignStarLevels(rankedScores)**:根据排名分配星级
|
||||
6. **MatchConclusions(starResults, conclusions)**:星级→结论类型映射
|
||||
7. **PersistResults(recordId, results)**:事务内写入结果并更新状态
|
||||
|
||||
### AssessmentService 集成修改
|
||||
|
||||
在 `SubmitAnswersAsync` 方法的事务提交成功后,添加对 `ReportGenerationService.GenerateReportAsync` 的调用:
|
||||
|
||||
```csharp
|
||||
// 现有代码:事务提交后
|
||||
await transaction.CommitAsync();
|
||||
|
||||
// 新增:触发报告生成
|
||||
try
|
||||
{
|
||||
await _reportGenerationService.GenerateReportAsync(request.RecordId);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "报告生成失败,recordId: {RecordId}", request.RecordId);
|
||||
// 报告生成失败不影响答案提交的返回,状态保持为3(生成中)
|
||||
}
|
||||
```
|
||||
|
||||
## 数据模型
|
||||
|
||||
### 新增实体:QuestionCategoryMapping(MiAssessment.Model)
|
||||
|
||||
参照 `MiAssessment.Admin.Business.Entities.QuestionCategoryMapping` 的结构,在 Model 项目中创建对应实体:
|
||||
|
||||
```csharp
|
||||
namespace MiAssessment.Model.Entities;
|
||||
|
||||
[Table("question_category_mappings")]
|
||||
public class QuestionCategoryMapping
|
||||
{
|
||||
[Key]
|
||||
public long Id { get; set; }
|
||||
public long QuestionId { get; set; }
|
||||
public long CategoryId { get; set; }
|
||||
public DateTime CreateTime { get; set; }
|
||||
|
||||
[ForeignKey(nameof(QuestionId))]
|
||||
public virtual Question? Question { get; set; }
|
||||
|
||||
[ForeignKey(nameof(CategoryId))]
|
||||
public virtual ReportCategory? Category { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
### 新增实体:ReportConclusion(MiAssessment.Model)
|
||||
|
||||
参照 `MiAssessment.Admin.Business.Entities.ReportConclusion` 的结构:
|
||||
|
||||
```csharp
|
||||
namespace MiAssessment.Model.Entities;
|
||||
|
||||
[Table("report_conclusions")]
|
||||
public class ReportConclusion
|
||||
{
|
||||
[Key]
|
||||
public long Id { get; set; }
|
||||
public long CategoryId { get; set; }
|
||||
public int ConclusionType { get; set; }
|
||||
[MaxLength(100)]
|
||||
public string? Title { get; set; }
|
||||
[Required]
|
||||
[Column(TypeName = "nvarchar(max)")]
|
||||
public string Content { get; set; } = null!;
|
||||
public DateTime CreateTime { get; set; }
|
||||
public DateTime UpdateTime { get; set; }
|
||||
public bool IsDeleted { get; set; }
|
||||
|
||||
[ForeignKey(nameof(CategoryId))]
|
||||
public virtual ReportCategory? Category { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
### MiAssessmentDbContext 扩展
|
||||
|
||||
新增两个 DbSet:
|
||||
|
||||
```csharp
|
||||
public virtual DbSet<QuestionCategoryMapping> QuestionCategoryMappings { get; set; }
|
||||
public virtual DbSet<ReportConclusion> ReportConclusions { get; set; }
|
||||
```
|
||||
|
||||
### 核心计算逻辑数据流
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[AssessmentAnswer 列表] --> B[按 QuestionCategoryMapping 分组到各 Category]
|
||||
B --> C{ScoreRule?}
|
||||
C -->|1 累加| D[Score = Σ AnswerValue]
|
||||
C -->|2 二值| E[Score = Σ (AnswerValue >= 6 ? 1 : 0)]
|
||||
D --> F[MaxScore = 题目数 × 10]
|
||||
E --> G[MaxScore = 题目数 × 1]
|
||||
F --> H[Percentage = Score / MaxScore × 100]
|
||||
G --> H
|
||||
H --> I[按 CategoryType 分组]
|
||||
I --> J[组内按 Percentage 降序排名]
|
||||
J --> K[根据排名位置分配 1-5 星]
|
||||
K --> L{StarLevel 映射}
|
||||
L -->|5星| M[ConclusionType = 1 最强]
|
||||
L -->|4星| N[ConclusionType = 2 较强]
|
||||
L -->|3星| N
|
||||
L -->|2星| O[ConclusionType = 3 较弱]
|
||||
L -->|1星| P[ConclusionType = 4 最弱]
|
||||
M --> Q[查询 ReportConclusion]
|
||||
N --> Q
|
||||
O --> Q
|
||||
P --> Q
|
||||
Q --> R[写入 AssessmentResult]
|
||||
```
|
||||
|
||||
### 星级分配算法
|
||||
|
||||
对于同一 CategoryType 维度内的 N 个叶子分类(已按百分比降序排名):
|
||||
|
||||
- **N ≥ 5**:排名第1 → 5星,排名最后 → 1星,中间按排名位置均匀映射到 2-4 星
|
||||
- 公式:`starLevel = 5 - round((rank - 1) / (N - 1) * 4)`
|
||||
- **N < 5**:排名第1 → 5星,排名最后 → 1星,中间按比例分配
|
||||
- 公式同上,适用于任意 N ≥ 2
|
||||
- **N = 1**:唯一分类 → 5星
|
||||
- **并列排名**:相同百分比的分类获得相同排名和相同星级
|
||||
|
||||
|
||||
|
||||
## 正确性属性(Correctness Properties)
|
||||
|
||||
*属性(Property)是指在系统所有合法执行中都应成立的特征或行为——本质上是对系统应做之事的形式化陈述。属性是人类可读规格说明与机器可验证正确性保证之间的桥梁。*
|
||||
|
||||
### Property 1: 累加计分正确性
|
||||
|
||||
*For any* 一组答案(AnswerValue 范围 1-10)和一个 ScoreRule=1 的叶子分类,该分类的计算结果应满足:Score 等于所有映射题目 AnswerValue 之和,MaxScore 等于映射题目数量乘以 10,Percentage 等于 round(Score / MaxScore × 100, 2)。
|
||||
|
||||
**Validates: Requirements 1.1, 1.4, 1.5**
|
||||
|
||||
### Property 2: 二值计分正确性
|
||||
|
||||
*For any* 一组答案(AnswerValue 范围 1-10)和一个 ScoreRule=2 的叶子分类,该分类的计算结果应满足:Score 等于映射题目中 AnswerValue ≥ 6 的数量,MaxScore 等于映射题目数量乘以 1,Percentage 等于 round(Score / MaxScore × 100, 2)。
|
||||
|
||||
**Validates: Requirements 1.2, 1.4, 1.5**
|
||||
|
||||
### Property 3: 多分类映射得分独立性
|
||||
|
||||
*For any* 一道题目映射到多个分类的情况,每个分类的得分计算应独立包含该题目的贡献,且一道题目对不同分类的贡献互不影响。
|
||||
|
||||
**Validates: Requirements 1.3**
|
||||
|
||||
### Property 4: 排名顺序正确性
|
||||
|
||||
*For any* 同一 CategoryType 维度内的一组叶子分类及其百分比,排名结果应满足:百分比更高的分类排名值更小(更靠前),且百分比相同的分类排名值相同。
|
||||
|
||||
**Validates: Requirements 2.1, 2.2**
|
||||
|
||||
### Property 5: 仅叶子节点参与排名
|
||||
|
||||
*For any* 包含父子层级关系的分类树,排名结果中应仅包含叶子节点分类(ParentId 不被其他分类引用的分类),父分类不应出现在排名结果中。
|
||||
|
||||
**Validates: Requirements 2.3**
|
||||
|
||||
### Property 6: 星级分配正确性
|
||||
|
||||
*For any* 同一 CategoryType 维度内 N 个已排名的叶子分类(N ≥ 2),星级分配应满足:排名第 1 的分类获得 5 星,排名最后的分类获得 1 星,所有星级值在 [1, 5] 范围内,且排名更靠前的分类星级不低于排名更靠后的分类。当 N = 1 时,唯一分类获得 5 星。
|
||||
|
||||
**Validates: Requirements 3.1, 3.2, 3.3**
|
||||
|
||||
### Property 7: 星级到结论类型映射正确性
|
||||
|
||||
*For any* 星级值(1-5),映射到的结论类型应满足:5→1(最强),4→2(较强),3→2(较强),2→3(较弱),1→4(最弱)。
|
||||
|
||||
**Validates: Requirements 4.1**
|
||||
|
||||
### Property 8: 结果持久化完整性
|
||||
|
||||
*For any* 成功完成的报告生成,写入 AssessmentResult 表的记录应与计算结果一一对应:每条记录的 RecordId、CategoryId、Score、MaxScore、Percentage、Rank、StarLevel 均与内存计算值一致。
|
||||
|
||||
**Validates: Requirements 5.1, 4.2**
|
||||
|
||||
### Property 9: 重新生成幂等性
|
||||
|
||||
*For any* 已有 AssessmentResult 记录的测评记录,重新执行报告生成后,数据库中该 RecordId 的结果记录应完全等于最新一次计算的结果,不存在旧数据残留。
|
||||
|
||||
**Validates: Requirements 5.3**
|
||||
|
||||
### Property 10: 生成完成后状态与时间一致性
|
||||
|
||||
*For any* 成功完成报告生成的测评记录,其 Status 应为 4(已完成),CompleteTime 应非空且不早于生成开始时间。
|
||||
|
||||
**Validates: Requirements 6.1, 6.2**
|
||||
|
||||
## 错误处理
|
||||
|
||||
| 场景 | 处理方式 | 状态影响 |
|
||||
|------|----------|----------|
|
||||
| RecordId 对应的记录不存在或已软删除 | 抛出 `InvalidOperationException`,记录错误日志 | 无状态变更 |
|
||||
| Record.Status ≠ 3 | 抛出 `InvalidOperationException`,记录错误日志 | 无状态变更 |
|
||||
| 答案记录数量为 0 | 抛出 `InvalidOperationException`,记录错误日志 | 无状态变更 |
|
||||
| 数据库写入异常 | 回滚事务,记录错误日志 | Status 保持为 3(生成中) |
|
||||
| 某分类无匹配结论 | 结论文本记录为空,继续处理其余分类 | 不影响整体流程 |
|
||||
|
||||
错误处理遵循以下原则:
|
||||
- 验证阶段的错误(记录不存在、状态不对、无答案)直接抛出异常,由调用方捕获
|
||||
- 持久化阶段的错误通过事务回滚保证数据一致性
|
||||
- 结论缺失属于数据不完整而非系统错误,采用容错处理
|
||||
|
||||
## 测试策略
|
||||
|
||||
### 测试框架
|
||||
|
||||
- 单元测试:xUnit + Moq
|
||||
- 属性测试:FsCheck(.NET 属性测试库)
|
||||
- 数据库测试:使用 EF Core InMemory Provider 或 SQLite InMemory
|
||||
|
||||
### 单元测试
|
||||
|
||||
单元测试覆盖以下场景:
|
||||
|
||||
1. **得分计算**:验证累加计分和二值计分的具体示例
|
||||
2. **排名计算**:验证正常排名、并列排名的具体示例
|
||||
3. **星级分配**:验证 N=1, N=3, N=5, N=8 等不同分类数量的星级分配
|
||||
4. **结论匹配**:验证有结论和无结论的情况
|
||||
5. **错误处理**:验证各种异常输入(记录不存在、状态不对、无答案等)
|
||||
6. **集成测试**:验证完整的报告生成流程(从答案到最终结果)
|
||||
|
||||
### 属性测试
|
||||
|
||||
每个正确性属性对应一个属性测试,使用 FsCheck 生成随机输入,最少运行 100 次迭代。
|
||||
|
||||
属性测试标注格式:
|
||||
```
|
||||
Feature: assessment-report-generation, Property {number}: {property_text}
|
||||
```
|
||||
|
||||
示例:
|
||||
```csharp
|
||||
/// <summary>
|
||||
/// Feature: assessment-report-generation, Property 1: 累加计分正确性
|
||||
/// *For any* 一组答案和 ScoreRule=1 的分类,Score 应等于 AnswerValue 之和
|
||||
/// **Validates: Requirements 1.1, 1.4, 1.5**
|
||||
/// </summary>
|
||||
[Property(MaxTest = 100)]
|
||||
public Property CumulativeScoring_ShouldSumAnswerValues()
|
||||
{
|
||||
// FsCheck 生成随机答案值列表,验证计算结果
|
||||
}
|
||||
```
|
||||
|
||||
### 测试覆盖矩阵
|
||||
|
||||
| 属性 | 属性测试 | 单元测试 | 边界情况 |
|
||||
|------|----------|----------|----------|
|
||||
| Property 1: 累加计分 | ✓ 随机答案值 | ✓ 具体示例 | 全1分、全10分 |
|
||||
| Property 2: 二值计分 | ✓ 随机答案值 | ✓ 具体示例 | 全5分(边界)、全6分 |
|
||||
| Property 3: 多分类映射 | ✓ 随机映射关系 | ✓ 一题多分类示例 | - |
|
||||
| Property 4: 排名顺序 | ✓ 随机百分比集合 | ✓ 并列排名示例 | 全部相同百分比 |
|
||||
| Property 5: 叶子节点 | ✓ 随机分类树 | ✓ 含父子层级示例 | - |
|
||||
| Property 6: 星级分配 | ✓ 随机排名 | ✓ N=1,3,5,8 示例 | N=1, N=2 |
|
||||
| Property 7: 星级映射 | ✓ 所有星级值 | - | - |
|
||||
| Property 8: 持久化完整性 | ✓ 随机计算结果 | ✓ 完整流程示例 | - |
|
||||
| Property 9: 重新生成 | ✓ 随机旧数据+新数据 | ✓ 覆盖示例 | - |
|
||||
| Property 10: 状态一致性 | ✓ 随机成功场景 | ✓ 状态验证示例 | - |
|
||||
149
.kiro/specs/assessment-report-generation/tasks.md
Normal file
149
.kiro/specs/assessment-report-generation/tasks.md
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
# Implementation Plan: 测评结论生成服务
|
||||
|
||||
## Overview
|
||||
|
||||
将设计文档中的报告生成流水线转化为可执行的编码任务。按照数据模型 → 核心计算逻辑 → 服务集成 → DI注册的顺序递增实现,确保每一步都可编译运行。所有代码在 MiAssessment.Model 和 MiAssessment.Core 项目中实现,使用 MiAssessmentDbContext 访问 Business 库。
|
||||
|
||||
## Tasks
|
||||
|
||||
- [ ] 1. 新增实体类并扩展 DbContext
|
||||
- [ ] 1.1 创建 QuestionCategoryMapping 实体类
|
||||
- 在 `MiAssessment.Model/Entities/QuestionCategoryMapping.cs` 中创建实体
|
||||
- 映射到 `question_category_mappings` 表,包含 Id、QuestionId、CategoryId、CreateTime 字段
|
||||
- 添加 Question 和 ReportCategory 的导航属性及 ForeignKey 特性
|
||||
- 遵循现有实体类的 XML 注释和命名规范
|
||||
- _Requirements: 8.3_
|
||||
|
||||
- [ ] 1.2 创建 ReportConclusion 实体类
|
||||
- 在 `MiAssessment.Model/Entities/ReportConclusion.cs` 中创建实体
|
||||
- 映射到 `report_conclusions` 表,包含 Id、CategoryId、ConclusionType、Title、Content、CreateTime、UpdateTime、IsDeleted 字段
|
||||
- Content 字段使用 `[Column(TypeName = "nvarchar(max)")]`,Title 使用 `[MaxLength(100)]`
|
||||
- 添加 ReportCategory 的导航属性及 ForeignKey 特性
|
||||
- _Requirements: 8.3_
|
||||
|
||||
- [ ] 1.3 在 MiAssessmentDbContext 中添加 DbSet
|
||||
- 在 `MiAssessment.Model/Data/MiAssessmentDbContext.cs` 的测评业务表区域添加:
|
||||
- `DbSet<QuestionCategoryMapping> QuestionCategoryMappings`
|
||||
- `DbSet<ReportConclusion> ReportConclusions`
|
||||
- _Requirements: 8.1, 8.2_
|
||||
|
||||
- [ ] 2. 实现 ReportGenerationService 核心计算逻辑
|
||||
- [ ] 2.1 创建 ReportGenerationService 类骨架
|
||||
- 在 `MiAssessment.Core/Services/ReportGenerationService.cs` 中创建服务类
|
||||
- 注入 MiAssessmentDbContext 和 ILogger<ReportGenerationService>
|
||||
- 定义 `GenerateReportAsync(long recordId)` 公开方法
|
||||
- 实现 ValidateRecord 验证逻辑:检查记录存在且未软删除、Status=3、有答案数据
|
||||
- _Requirements: 9.1, 9.2, 9.3, 9.4_
|
||||
|
||||
- [ ] 2.2 实现得分计算逻辑
|
||||
- 在 GenerateReportAsync 中实现 LoadData:一次性加载答案、分类映射、叶子分类、结论数据
|
||||
- 实现 CalculateScores:按 ScoreRule 区分累加计分(AnswerValue 直接累加)和二值计分(AnswerValue>=6 计1分,否则0分)
|
||||
- 计算每个叶子分类的 Score、MaxScore(累加: 题目数×10,二值: 题目数×1)、Percentage(Score/MaxScore×100,保留两位小数)
|
||||
- 通过 QuestionCategoryMapping 确定题目所属分类,支持一题多分类
|
||||
- _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5_
|
||||
|
||||
- [ ]* 2.3 编写属性测试:累加计分正确性
|
||||
- **Property 1: 累加计分正确性**
|
||||
- 使用 FsCheck 生成随机 AnswerValue(1-10)列表,验证 ScoreRule=1 时 Score = Σ AnswerValue,MaxScore = N×10,Percentage = round(Score/MaxScore×100, 2)
|
||||
- **Validates: Requirements 1.1, 1.4, 1.5**
|
||||
|
||||
- [ ]* 2.4 编写属性测试:二值计分正确性
|
||||
- **Property 2: 二值计分正确性**
|
||||
- 使用 FsCheck 生成随机 AnswerValue(1-10)列表,验证 ScoreRule=2 时 Score = count(AnswerValue>=6),MaxScore = N×1
|
||||
- **Validates: Requirements 1.2, 1.4, 1.5**
|
||||
|
||||
- [ ]* 2.5 编写属性测试:多分类映射得分独立性
|
||||
- **Property 3: 多分类映射得分独立性**
|
||||
- 验证一道题目映射到多个分类时,各分类得分计算独立,互不影响
|
||||
- **Validates: Requirements 1.3**
|
||||
|
||||
- [ ] 2.6 实现排名计算逻辑
|
||||
- 实现 CalculateRanks:按 CategoryType 分组,组内按 Percentage 降序排名
|
||||
- 支持并列排名:相同 Percentage 的分类分配相同排名值
|
||||
- 仅对叶子节点分类(无子分类)进行排名
|
||||
- _Requirements: 2.1, 2.2, 2.3_
|
||||
|
||||
- [ ]* 2.7 编写属性测试:排名顺序正确性
|
||||
- **Property 4: 排名顺序正确性**
|
||||
- 使用 FsCheck 生成随机百分比集合,验证百分比更高的分类排名更靠前,相同百分比排名相同
|
||||
- **Validates: Requirements 2.1, 2.2**
|
||||
|
||||
- [ ]* 2.8 编写属性测试:仅叶子节点参与排名
|
||||
- **Property 5: 仅叶子节点参与排名**
|
||||
- 验证排名结果中仅包含叶子节点分类,父分类不出现在排名结果中
|
||||
- **Validates: Requirements 2.3**
|
||||
|
||||
- [ ] 2.9 实现星级评定逻辑
|
||||
- 实现 AssignStarLevels:根据排名位置分配 1-5 星
|
||||
- N≥2 时公式:`starLevel = 5 - round((rank - 1) / (N - 1) * 4)`
|
||||
- N=1 时:唯一分类获得 5 星
|
||||
- 并列排名的分类获得相同星级
|
||||
- _Requirements: 3.1, 3.2, 3.3_
|
||||
|
||||
- [ ]* 2.10 编写属性测试:星级分配正确性
|
||||
- **Property 6: 星级分配正确性**
|
||||
- 使用 FsCheck 生成随机排名,验证排名第1→5星、排名最后→1星、星级在[1,5]范围内、排名靠前星级不低于靠后
|
||||
- **Validates: Requirements 3.1, 3.2, 3.3**
|
||||
|
||||
- [ ] 2.11 实现结论匹配逻辑
|
||||
- 实现 MatchConclusions:星级→结论类型映射(5→1, 4→2, 3→2, 2→3, 1→4)
|
||||
- 查询 ReportConclusion 表匹配 CategoryId 和 ConclusionType
|
||||
- 无匹配结论时记录为空,继续处理其余分类
|
||||
- _Requirements: 4.1, 4.2, 4.3_
|
||||
|
||||
- [ ]* 2.12 编写属性测试:星级到结论类型映射正确性
|
||||
- **Property 7: 星级到结论类型映射正确性**
|
||||
- 验证所有星级值(1-5)映射到正确的结论类型:5→1, 4→2, 3→2, 2→3, 1→4
|
||||
- **Validates: Requirements 4.1**
|
||||
|
||||
- [ ] 3. Checkpoint - 确保核心计算逻辑正确
|
||||
- Ensure all tests pass, ask the user if questions arise.
|
||||
|
||||
- [ ] 4. 实现结果持久化与状态流转
|
||||
- [ ] 4.1 实现 PersistResults 事务逻辑
|
||||
- 在 GenerateReportAsync 中实现事务内操作:
|
||||
- 删除该 RecordId 已有的 AssessmentResult 记录(支持重新生成)
|
||||
- 批量写入新的 AssessmentResult 记录(RecordId、CategoryId、Score、MaxScore、Percentage、Rank、StarLevel、CreateTime)
|
||||
- 更新 AssessmentRecord 的 Status 为 4(已完成)、设置 CompleteTime 为当前时间
|
||||
- 异常时回滚事务,Status 保持为 3
|
||||
- _Requirements: 5.1, 5.2, 5.3, 6.1, 6.2, 6.3, 9.1_
|
||||
|
||||
- [ ]* 4.2 编写属性测试:结果持久化完整性
|
||||
- **Property 8: 结果持久化完整性**
|
||||
- 验证写入 AssessmentResult 的记录与内存计算值一一对应
|
||||
- **Validates: Requirements 5.1, 4.2**
|
||||
|
||||
- [ ]* 4.3 编写属性测试:重新生成幂等性
|
||||
- **Property 9: 重新生成幂等性**
|
||||
- 验证重新执行报告生成后,数据库中该 RecordId 的结果完全等于最新计算结果,无旧数据残留
|
||||
- **Validates: Requirements 5.3**
|
||||
|
||||
- [ ]* 4.4 编写属性测试:生成完成后状态与时间一致性
|
||||
- **Property 10: 生成完成后状态与时间一致性**
|
||||
- 验证成功完成后 Status=4,CompleteTime 非空且不早于生成开始时间
|
||||
- **Validates: Requirements 6.1, 6.2**
|
||||
|
||||
- [ ] 5. 服务集成与 DI 注册
|
||||
- [ ] 5.1 在 ServiceModule 中注册 ReportGenerationService
|
||||
- 在 `MiAssessment.Infrastructure/Modules/ServiceModule.cs` 中添加 ReportGenerationService 的 Autofac 注册
|
||||
- 注入 MiAssessmentDbContext 和 ILogger<ReportGenerationService>,使用 InstancePerLifetimeScope
|
||||
- _Requirements: 7.2_
|
||||
|
||||
- [ ] 5.2 修改 AssessmentService 集成 ReportGenerationService
|
||||
- 在 AssessmentService 构造函数中添加 ReportGenerationService 依赖
|
||||
- 在 SubmitAnswersAsync 方法的 `transaction.CommitAsync()` 之后添加 `_reportGenerationService.GenerateReportAsync(request.RecordId)` 调用
|
||||
- 用 try-catch 包裹报告生成调用,失败时记录错误日志但不影响答案提交返回(状态保持为3)
|
||||
- 更新 ServiceModule 中 AssessmentService 的注册,添加 ReportGenerationService 依赖解析
|
||||
- _Requirements: 7.1, 7.3_
|
||||
|
||||
- [ ] 6. Final checkpoint - 确保所有测试通过
|
||||
- Ensure all tests pass, 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
|
||||
- All code uses C# (.NET 10),遵循项目开发规范中的命名和注释标准
|
||||
- 使用 Autofac 进行依赖注入注册,与现有项目架构一致
|
||||
Loading…
Reference in New Issue
Block a user