✨ feat(*): 添加 PCB 检测记录的 CSV 持久化存储功能
* 新增 CsvBoardRecordRepository 实现按日期分卷的 CSV 记录存储 * 新增 RecordPersistenceModels 定义板件检测记录数据结构 * 在 WorkflowHostedService 中集成检测完成后的记录持久化 * 更新 MainWindowViewModel 支持记录查询与异常标记 * 更新 AppStateStore 添加记录存储相关状态管理 * 新增 DashboardPage UI 元素展示记录存储状态 * 更新 SystemSettingViewModel 与 appConfig 添加存储路径配置 * 注册 TimeProvider 与 IBoardRecordRepository 到 DI 容器 * 新增 CsvBoardRecordRepositoryTests 与 MainWindowViewModelTests * 配置 Release 模式 portable PDB 并在发布时自动移除
This commit is contained in:
@@ -148,6 +148,46 @@ public sealed class SystemSettingViewModelTests
|
||||
Assert.Null(backgroundException);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当 UI 调度已被取消时,系统设置页的快照事件不应将取消异常继续抛到同步上下文。
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void SnapshotChanged_ShouldIgnoreCanceledDispatcherTask()
|
||||
{
|
||||
var configService = new FakeAppConfigService();
|
||||
var stateStore = new AppStateStore();
|
||||
var viewModel = new SystemSettingViewModel(
|
||||
configService,
|
||||
new FakeSftpLookupService(),
|
||||
stateStore,
|
||||
new CanceledDispatcherService());
|
||||
var synchronizationContext = new ExceptionCapturingSynchronizationContext();
|
||||
SynchronizationContext? originalContext = SynchronizationContext.Current;
|
||||
|
||||
try
|
||||
{
|
||||
SynchronizationContext.SetSynchronizationContext(synchronizationContext);
|
||||
|
||||
stateStore.UpdateSnapshot(snapshot =>
|
||||
{
|
||||
snapshot.PlcMonitorItems.Add(new PlcMonitorItem
|
||||
{
|
||||
GroupName = "Inputs",
|
||||
Name = "PcbArrived",
|
||||
CurrentValue = "True",
|
||||
LastUpdatedAt = DateTimeOffset.Now
|
||||
});
|
||||
});
|
||||
|
||||
Assert.Null(synchronizationContext.CapturedException);
|
||||
Assert.Empty(viewModel.PlcMonitorItems);
|
||||
}
|
||||
finally
|
||||
{
|
||||
SynchronizationContext.SetSynchronizationContext(originalContext);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在 STA 线程中执行指定委托,并将内部异常重新抛回当前测试线程。
|
||||
/// </summary>
|
||||
@@ -269,6 +309,48 @@ public sealed class SystemSettingViewModelTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 捕获 async void 事件处理器回抛到同步上下文中的异常。
|
||||
/// </summary>
|
||||
private sealed class ExceptionCapturingSynchronizationContext : SynchronizationContext
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取或设置捕获到的异常。
|
||||
/// </summary>
|
||||
public Exception? CapturedException { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 立即执行回调并记录内部异常,避免测试进程直接崩溃。
|
||||
/// </summary>
|
||||
/// <param name="d">待执行回调。</param>
|
||||
/// <param name="state">回调状态。</param>
|
||||
public override void Post(SendOrPostCallback d, object? state)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(d);
|
||||
|
||||
try
|
||||
{
|
||||
d(state);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
CapturedException = ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 提供始终返回已取消任务的测试调度服务,用于模拟应用关闭阶段的 Dispatcher 中止。
|
||||
/// </summary>
|
||||
private sealed class CanceledDispatcherService : IDispatcherService
|
||||
{
|
||||
public Task InvokeAsync(Action action)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(action);
|
||||
return Task.FromCanceled(new CancellationToken(canceled: true));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 提供绑定到指定 Dispatcher 的测试调度服务,用于验证跨线程回切行为。
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user