Week 11 - 阶段检查
这一页用来确认 Week 11 是否真正掌握:权限能限制动作,审计能追踪操作,导入能解释错误,后台任务能显示进度,设置能保存恢复,RC 能按清单交付。
本周学习地图
| 阶段 | 先掌握什么 | 最终能写什么 |
|---|---|---|
| 权限 | 角色、权限点、服务层检查 | 能限制关键动作 |
| 审计 | 操作人、动作、结果、时间 | 能追踪成功和失败 |
| 导入导出 | 行级校验、错误报告 | 能解释导入失败原因 |
| 后台任务 | 进度、取消、恢复状态 | 能处理耗时操作 |
| 设置 | 默认值、保存、加载 | 能保存用户偏好 |
| RC | 发布候选、验收清单 | 能进入发布前检查 |
本周自测项目:商品管理增强版
在 Week 10 MVP 上继续加功能,至少包含:
- Viewer 不能删除商品,Admin 可以删除。
- 新增、编辑、删除都写审计日志。
- CSV 导入能输出成功行和失败行。
- 导入过程显示进度并支持取消。
- 设置页能保存默认分类或数据目录。
- 生成 RC 检查清单。
过关标准:能证明项目不是只能演示主流程,错误流程也能处理。
上位机阶段验收:现场交付功能完善
本周目标: 增加权限、审计、点位批量导入、稳定重连、设置和发布候选版。
必须亲手完成:
- 三种角色权限在界面和服务层都生效
- 关键修改留下前后值审计
- 断网后显示离线,恢复后自动重连
过关标准: 使用测试账号和故障演练逐项验收,并留下日志、截图和检查表。
不要只看答案。新建一个空项目重做一次,运行成功后再故意改坏一处并自己排错。
本周流程图
用户执行操作
-> PermissionService 检查权限
-> Service 执行业务规则
-> AuditLog 记录成功或失败
-> 导入导出按行返回结果
-> BackgroundTask 显示进度并支持取消
-> Settings 保存和恢复偏好
-> RC 清单判断是否能发布前半部分:本周原理地图
Week 11 的主线是:
给商品管理项目补上可靠性能力,让它不只是能用,还能控制、追踪、恢复、验收和交付。能力地图
| 能力 | 解决的问题 | 最小判断 |
|---|---|---|
| 权限 | 谁能执行关键动作 | Viewer 不能删,Admin 能删 |
| 审计 | 谁做过什么 | 成功和失败都能查到记录 |
| 导入导出 | 数据怎么进出系统 | 成功行导入,错误行有原因 |
| 后台任务 | 耗时任务不阻塞界面 | 有进度、能取消、能恢复状态 |
| 设置偏好 | 用户配置能保存 | 默认值、保存、加载、恢复默认 |
| 发布候选 | 交付前能验收 | README、CHANGELOG、清单齐全 |
本周通过线
达到下面这些,Week 11 才算过关:
- 能写出角色和权限表。
- 能在服务层检查权限。
- 能写审计日志字段:用户、动作、对象、时间、结果。
- 能导入 CSV,并记录错误行。
- 能导出成功数据。
- 能写后台任务的进度和取消。
- 能把设置保存为 JSON 并恢复默认。
- 能写发布候选清单。
Week 11 必须会用的操作
| 主题 | 必须会的写法 |
|---|---|
| 角色 | enum Role、Viewer、Editor、Admin |
| 权限 | CanDelete、HashSet<string>、权限名常量 |
| 审计 | AuditEntry、用户、动作、目标、时间、结果 |
| CSV 导入 | ReadAllLines、Split、Trim、TryParse、错误行 |
| CSV 导出 | string.Join、WriteAllLines |
| 后台任务 | async Task、CancellationTokenSource、Progress、IsBusy |
| 设置 | AppSettings、JSON、默认值、Normalize |
| 发布候选 | restore、build、test、publish、README、CHANGELOG |
下半部分:阶段检查完整答案
下面这份程序把 Week 11 的关键能力压缩成一个可运行检查。
它会检查:
- 权限拒绝和权限成功。
- 审计记录。
- CSV 导入成功和失败。
- 设置非法值回退。
- 后台任务进度。
- 发布候选检查项。
检查题:Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
var auditLogs = new List<string>();
TryDelete(Role.Viewer, "Alice", auditLogs);
TryDelete(Role.Admin, "Cindy", auditLogs);
Console.WriteLine("审计日志:");
foreach (string log in auditLogs)
{
Console.WriteLine(log);
}
string[] csvLines =
{
"Id,Name,Price,Stock",
"1,Keyboard,199,10",
"2,,59,30",
"3,Monitor,abc,5",
"4,Mouse,59,20"
};
ImportSummary importSummary = ImportProducts(csvLines);
Console.WriteLine($"导入成功:{importSummary.SuccessCount}");
Console.WriteLine($"导入失败:{importSummary.Errors.Count}");
foreach (string error in importSummary.Errors)
{
Console.WriteLine(error);
}
AppSettings settings = NormalizeSettings(new AppSettings("Blue", 999, ""));
Console.WriteLine($"设置:Theme={settings.Theme}, PageSize={settings.PageSize}, DataPath={settings.DataPath}");
await RunBackgroundJobAsync(progress =>
{
Console.WriteLine($"后台任务进度:{progress}%");
});
string[] releaseChecklist =
{
"版本号已更新",
"主流程已回归",
"README 已写运行方式",
"CHANGELOG 已写变更",
"已知问题已记录"
};
Console.WriteLine("发布候选检查:");
foreach (string item in releaseChecklist)
{
Console.WriteLine($"[x] {item}");
}
void TryDelete(Role role, string user, List<string> audit)
{
if (role != Role.Admin)
{
audit.Add($"{DateTimeOffset.UtcNow:HH:mm:ss} {user} Product.Delete denied");
Console.WriteLine($"{user} 没有删除权限。");
return;
}
audit.Add($"{DateTimeOffset.UtcNow:HH:mm:ss} {user} Product.Delete success");
Console.WriteLine($"{user} 删除成功。");
}
ImportSummary ImportProducts(IReadOnlyList<string> lines)
{
int successCount = 0;
var errors = new List<string>();
var usedIds = new HashSet<int>();
for (int index = 1; index < lines.Count; index++)
{
int lineNumber = index + 1;
string line = lines[index];
string[] parts = line.Split(',');
if (parts.Length != 4)
{
errors.Add($"第 {lineNumber} 行:列数必须是 4。");
continue;
}
if (!int.TryParse(parts[0], out int id))
{
errors.Add($"第 {lineNumber} 行:Id 必须是整数。");
continue;
}
if (!usedIds.Add(id))
{
errors.Add($"第 {lineNumber} 行:Id 重复。");
continue;
}
if (string.IsNullOrWhiteSpace(parts[1]))
{
errors.Add($"第 {lineNumber} 行:Name 不能为空。");
continue;
}
if (!decimal.TryParse(parts[2], out decimal price) || price <= 0)
{
errors.Add($"第 {lineNumber} 行:Price 必须是大于 0 的数字。");
continue;
}
if (!int.TryParse(parts[3], out int stock) || stock < 0)
{
errors.Add($"第 {lineNumber} 行:Stock 必须是大于或等于 0 的整数。");
continue;
}
successCount++;
}
return new ImportSummary(successCount, errors);
}
AppSettings NormalizeSettings(AppSettings settings)
{
string theme = settings.Theme is "Light" or "Dark"
? settings.Theme
: "Light";
int pageSize = settings.PageSize is >= 10 and <= 500
? settings.PageSize
: 50;
string dataPath = string.IsNullOrWhiteSpace(settings.DataPath)
? "products.json"
: settings.DataPath;
return new AppSettings(theme, pageSize, dataPath);
}
async Task RunBackgroundJobAsync(Action<int> reportProgress)
{
for (int index = 1; index <= 5; index++)
{
await Task.Delay(80);
reportProgress(index * 20);
}
}
public enum Role
{
Viewer,
Admin
}
public sealed record ImportSummary(int SuccessCount, IReadOnlyList<string> Errors);
public sealed record AppSettings(string Theme, int PageSize, string DataPath);运行命令
dotnet new console -n Week11Checkpoint
cd Week11Checkpoint把上面的代码放进 Program.cs,然后执行:
dotnet run正确运行结果
Alice 没有删除权限。
Cindy 删除成功。
审计日志:
...
导入成功:2
导入失败:2
第 3 行:Name 不能为空。
第 4 行:Price 必须是大于 0 的数字。
设置:Theme=Light, PageSize=50, DataPath=products.json
后台任务进度:20%
后台任务进度:40%
后台任务进度:60%
后台任务进度:80%
后台任务进度:100%
发布候选检查:
[x] 版本号已更新
[x] 主流程已回归
[x] README 已写运行方式
[x] CHANGELOG 已写变更
[x] 已知问题已记录这份程序检查了什么
| 检查点 | 合格标准 |
|---|---|
| 权限 | Viewer 被拒绝,Admin 成功 |
| 审计 | denied 和 success 都有记录 |
| 导入 | 正确行成功,错误行有原因 |
| 设置 | 非法主题、页大小、路径能回退 |
| 后台任务 | 进度能到 100 |
| 发布候选 | 交付检查项能列出 |
Avalonia 页面验收
后台任务页面
1. 点击开始后进度条变化。
2. 任务运行时开始按钮禁用。
3. 任务运行时取消按钮启用。
4. 点击取消后任务停止。
5. 完成或取消后界面状态恢复。设置页面
1. 设置文件不存在时显示默认值。
2. 保存后生成 settings.json。
3. 重新加载能恢复保存值。
4. 非法页大小能提示或回退。
5. 恢复默认能回到 Light、50、products.json。RC 交付验收
发布候选必须包含:
release-candidate/
app/
README.md
CHANGELOG.md
KNOWN-ISSUES.md
RELEASE-CHECKLIST.md
sample-data/并且:
dotnet build通过。dotnet publish -c Release通过。- 从发布目录能启动。
- README 写清运行方式。
- CHANGELOG 写清本版变化。
- KNOWN-ISSUES 写清已知问题。
- RELEASE-CHECKLIST 可以逐项执行。
最终通过标准
这一页通过时,应该能做到:
- 运行阶段检查程序,输出和上面一致。
- 能说清权限和审计的关系。
- 能说清导入错误行为什么不能直接丢掉。
- 能说清后台任务的
IsBusy、Progress、取消分别解决什么。 - 能说清设置默认值和非法值回退。
- 能准备一份 RC 交付清单。
Week 11 过关的标准是:项目不只会增删改查,还能限制动作、记录动作、处理外部数据、运行长任务、保存设置,并在交付前按清单验收。