对账文案不再断言素材库已刷新
43a4e7ef7 的对账尾句写「素材库已刷新,如已生成派生图请勿重复执行」,但
refreshAssetLibrary 在 canAccessProtectedData 为 false 时直接 return,读取失败也
只在鉴权错误时弹登录框、其余一律吞掉,返回 Promise<void> 不带成败信号;它本身还是
可选 prop。三种情况下这句都是假话,而它旁边那句 Promise.resolve(...).catch() 也抓
不到任何东西——不是防御到位,是根本没有可抓的。
后果是用户读到「已刷新」却对着旧列表,判定「没有派生图,可以安全重试」,重新走回
这条修复本来要避免的重复创建,只是需要多一次失败才能到达。
改文案不改结构:刷新照旧调用(成功时用户白赚一份新列表),尾句从事实断言换成指令
「请确认素材库是否已生成派生图,再决定是否重试」,刷新失效时也成立。让
refreshAssetLibrary 返回成功标志是另一个选项,但那个 hook 多处共用,为一句文案改它
的契约不划算。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -5980,5 +5980,6 @@
|
||||
- 决策:由服务端显式告知。`AppError` 新增 `with_detail_field`,在已有 details 上补字段而不是像 `with_details` 那样整体替换,保留下游写入的 provider / message——客户端要靠 message 定位、靠新字段决策。`snap_editor_image_to_pixel_art` 在第一次 OSS PUT 之后的四条失败路径(账号素材持久化失败、项目资源缺失、账号素材缺失、画布回填失败)置 `resultPersistenceStarted: true`。客户端只对「完全无响应」和「带该标记」的失败做对账,常见的纯校验 `400`、排队 `503`、预算 `504` 既不多打读取也不附加提示。
|
||||
- 配套修复:对账成功分支补上 `hasCanvasGenerationDialogById` 检查——权威快照里占位消失有两种原因,服务端消费掉或用户在请求期间删除,后者契约要求不应用完成快照、不写历史,成功路径同一处早有这道检查而对账路径漏了。对账同时刷新素材库(新增可选 `refreshAssetLibrary` 贯穿 `ImageCanvasEditorView` → surface → workflow),否则只 GET 项目却让用户核对素材库,他看到的仍是旧列表,不满足契约的「项目 / 素材快照」。占位存活分支刻意不调 `applyProjectSnapshot`:传入的是 `applyGeneratedProjectSnapshot`,其 action 默认值为 `generate-image`,不传 action 会写一条类型错误且受撤销保护的历史,而权威快照此刻与本地一致,套用只会覆盖未保存编辑。
|
||||
- 验证:服务端 `assert_function_occurrence_count` 把标记钉为 4 处,并用顺序断言要求它只出现在 `persist_editor_generated_image_owned` 之后——漏标一处或误标在校验阶段都会失败。客户端新增用例覆盖「带标记的 409 触发对账并刷新素材库」「未带标记的 400 不对账、文案保持服务端原文」「用户删除占位则不应用快照不写历史」。
|
||||
- 文案边界:对账尾句只下指令、不断言素材库已刷新。`refreshAssetLibrary` 在 `canAccessProtectedData` 为 false 时直接 return,读取失败也只在鉴权错误时弹登录框、其余一律吞掉,返回 `Promise<void>` 不带成败信号,且它本身是可选 prop——三种情况下「已刷新」都是假话,会让用户对着旧列表判定「没有派生图,可以重试」,重新走回这条修复要避免的重复创建。刷新照旧调用(成功时用户白赚一份新列表),但文案在刷新失效时也必须成立。
|
||||
- 未覆盖:刷新页面后停在 `generating` 的占位仍无自动收口,需先给 dialog 增加「无 durable job 的 inline 链路」标记,单独立项。客户端 120 秒超时相对服务端 30 秒预算是四倍冗余,未调整。
|
||||
- 关联文档:`docs/technical/【前端架构】图片画布编辑器MVP接入方案-2026-06-11.md`。
|
||||
|
||||
@@ -2376,7 +2376,8 @@ describe('useImageCanvasGenerationWorkflow', () => {
|
||||
const unresolvedMessage =
|
||||
screen.getByTestId('dialog-error').textContent ?? '';
|
||||
expect(unresolvedMessage).toContain('画布未收到完美像素结果。');
|
||||
expect(unresolvedMessage).toContain('素材库已刷新');
|
||||
expect(unresolvedMessage).toContain('请确认素材库是否已生成派生图');
|
||||
expect(unresolvedMessage).not.toContain('素材库已刷新');
|
||||
});
|
||||
|
||||
it('does not reconcile when the perfect-pixel request never left the client', async () => {
|
||||
@@ -2476,7 +2477,10 @@ describe('useImageCanvasGenerationWorkflow', () => {
|
||||
// 中文注释:服务端原文保留便于定位,对账结论与核对提示附加在后。
|
||||
expect(message).toContain('画布版本冲突。');
|
||||
expect(message).toContain('画布未收到完美像素结果。');
|
||||
expect(message).toContain('素材库已刷新');
|
||||
// 中文注释:只下指令、不断言素材库已刷新——refreshAssetLibrary 可能是 no-op 或
|
||||
// 静默失败,断言会让用户对着旧列表判定「没有派生图,可以重试」。
|
||||
expect(message).toContain('请确认素材库是否已生成派生图');
|
||||
expect(message).not.toContain('素材库已刷新');
|
||||
});
|
||||
|
||||
it('skips reconciliation for a responded failure the server did not mark', async () => {
|
||||
|
||||
@@ -1876,6 +1876,12 @@ export function useImageCanvasGenerationWorkflow({
|
||||
if (outcomeMayBePersisted && perfectPixelDialogId) {
|
||||
// 中文注释:契约要求核对「项目 / 素材」两份快照。素材库是独立记录,只 GET
|
||||
// 项目却让用户去核对素材库,他看到的仍是旧列表。
|
||||
//
|
||||
// 但不能因为调了它就在文案里断言「素材库已刷新」:refreshAssetLibrary 在
|
||||
// `canAccessProtectedData` 为 false 时直接 return,读取失败也只在鉴权错误时
|
||||
// 弹登录框、其余一律吞掉,返回 `Promise<void>` 不带成败信号;它本身还是可选
|
||||
// prop。刷新成功时用户白赚一份新列表,失败时文案必须仍然成立,所以下面只给
|
||||
// 「请确认」的指令,不给「已刷新」的事实断言。
|
||||
const [reconciled] = await Promise.all([
|
||||
loadEditorProject(normalizedProjectId).catch(() => null),
|
||||
Promise.resolve(refreshAssetLibrary?.()).catch(() => undefined),
|
||||
@@ -1919,9 +1925,10 @@ export function useImageCanvasGenerationWorkflow({
|
||||
: undefined;
|
||||
// 中文注释:保留服务端原文(例如 assetKind 校验失败)便于定位,同时附上对账结论。
|
||||
// 持久化非事务,对象与账号素材可能已落库而只有画布回填没完成,所以必须让用户先核对
|
||||
// 素材库再决定,而不是直接重试。
|
||||
// 素材库再决定,而不是直接重试。尾句只下指令、不断言素材库已经刷新——上面那次刷新
|
||||
// 可能是 no-op 或被静默吞掉,断言会让用户对着旧列表判定「没有派生图,可以重试」。
|
||||
const errorMessage = reconciledMessage
|
||||
? `${serverMessage ? `${serverMessage} ` : ''}${reconciledMessage}素材库已刷新,如已生成派生图请勿重复执行。`
|
||||
? `${serverMessage ? `${serverMessage} ` : ''}${reconciledMessage}请确认素材库是否已生成派生图,再决定是否重试。`
|
||||
: (serverMessage ?? '完美像素处理失败');
|
||||
if (
|
||||
perfectPixelDialogId &&
|
||||
|
||||
Reference in New Issue
Block a user