完成 Game Core P0 验收收口

This commit is contained in:
JSD\13999
2026-05-20 11:57:00 +08:00
parent e866e75e86
commit e1197b5c0f
3 changed files with 471 additions and 0 deletions

View File

@@ -0,0 +1,102 @@
# AudioService 取消语义修复说明
日期2026-05-20
范围:`My project/Assets/FlowScope/Runtime/Audio/AudioService.cs`
优先级P0 小修复
结论:建议立即顺手修复
## 问题概述
`AudioService.PlayBgmAsync``AudioService.PlaySfxAsync` 支持传入 `CancellationToken`,但当前内部 `LoadClipAsync` 会捕获所有异常,并统一包装为 `InvalidOperationException`
这会导致调用方主动取消音频加载时,拿到的不是 `OperationCanceledException`,而是普通失败异常。
## 影响
取消语义被破坏后,调用方无法可靠区分以下两类情况:
- 生命周期或用户操作导致的正常取消。
- 资源缺失、Addressables 失败、类型不匹配等真实加载错误。
可能造成的问题:
- 正常取消被记录成错误日志。
- UI 或业务层误判为资源加载失败。
- 后续重试、错误兜底、统计埋点出现噪音。
- 框架内不同服务的取消语义不一致,增加异步生命周期排查成本。
## 推荐修改
`AudioService.LoadClipAsync` 中单独保留 `OperationCanceledException`,不要包装。
目标代码形态:
```csharp
private async Task<IResourceHandle<AudioClip>> LoadClipAsync(
string key,
CancellationToken cancellationToken)
{
try
{
var handle = await _resources.LoadAsync<AudioClip>(key, cancellationToken);
return ValidateLoadedClip(key, handle);
}
catch (OperationCanceledException)
{
throw;
}
catch (Exception exception)
{
throw new InvalidOperationException($"Failed to load audio clip '{key}'.", exception);
}
}
```
## 测试建议
`AudioServiceTests` 中补充异步取消测试。
建议覆盖:
1. `PlayBgmAsync_WhenCanceled_ThrowsOperationCanceledException`
2. `PlaySfxAsync_WhenCanceled_ThrowsOperationCanceledException`
测试要点:
- 创建 `CancellationTokenSource`
- 调用 `Cancel()`
- 调用 `PlayBgmAsync``PlaySfxAsync`
- 断言抛出 `OperationCanceledException`
- 断言没有创建新的 `AudioSource`
示例结构:
```csharp
[UnityTest]
public IEnumerator PlayBgmAsync_WhenCanceled_ThrowsOperationCanceledException()
{
var service = CreateService();
using var cts = new CancellationTokenSource();
cts.Cancel();
var sourceCountBeforePlay = Object.FindObjectsOfType<AudioSource>().Length;
yield return ThrowsAsync<OperationCanceledException>(
service.PlayBgmAsync("bgm-a", cancellationToken: cts.Token));
Assert.That(Object.FindObjectsOfType<AudioSource>().Length, Is.EqualTo(sourceCountBeforePlay));
}
```
如果现有测试工具方法只支持 `Func<Task>`,也可以按当前测试文件风格新增一个接收 `Task``ThrowsAsync<TException>` helper。
## 验收标准
- `PlayBgmAsync` 在取消时抛出 `OperationCanceledException`
- `PlaySfxAsync` 在取消时抛出 `OperationCanceledException`
- 取消时不创建 `AudioSource`
- 取消时不把异常包装成 `InvalidOperationException`
- 现有 PlayMode 音频测试继续通过。
## 后续备注
该问题不是架构阻塞项,但修复成本很低。建议本轮直接修掉,避免后续异步服务取消语义不一致。