Files
photo-live/apps/admin/src/utils/albums/assetUpload.ts
T
2026-09-18 12:33:46 +08:00

79 lines
2.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 资产预签名上传状态机(纯函数 reducer)
*
* 三段链路 init(POST /assets 预签名)→ PUT(staging 直传)→ complete
* (核验生效并返回签名 URL)。任何一步失败进入 error(携带阶段与原因,
* 可重新开始);complete 幂等,成功态保留签名 URL 与过期时间用于预览。
* 抽成纯 reducer 以便单测与 UI 解耦。
*/
export type AssetUploadStage =
'idle' | 'initializing' | 'uploading' | 'completing' | 'done' | 'error'
export interface AssetUploadState {
stage: AssetUploadStage
/** 失败发生的阶段(error 态定位重试入口) */
failedStage?: 'init' | 'put' | 'complete'
/** 错误消息(已本地化/服务端文案) */
error?: string
/** complete 返回的签名预览 URL */
previewUrl?: string
/** 签名 URL 过期时间(ISO) */
previewExpiresAt?: string
}
export type AssetUploadEvent =
| { type: 'START' }
| { type: 'INIT_OK' }
| { type: 'PUT_OK' }
| { type: 'COMPLETE_OK'; url: string; expiresAt: string }
| { type: 'FAIL'; stage: 'init' | 'put' | 'complete'; message?: string }
| { type: 'RESET' }
export const INITIAL_ASSET_UPLOAD_STATE: AssetUploadState = { stage: 'idle' }
/**
* 状态转移:非法转移(如 idle 收到 PUT_OK)保持原状——UI 侧的迟到响应
* 不得污染已重置的新会话。
*/
export function nextAssetUploadState(
state: AssetUploadState,
event: AssetUploadEvent
): AssetUploadState {
switch (event.type) {
case 'START':
if (state.stage === 'idle' || state.stage === 'done' || state.stage === 'error') {
return { stage: 'initializing' }
}
return state
case 'INIT_OK':
if (state.stage === 'initializing') return { stage: 'uploading' }
return state
case 'PUT_OK':
if (state.stage === 'uploading') return { stage: 'completing' }
return state
case 'COMPLETE_OK':
if (state.stage === 'completing') {
return {
stage: 'done',
previewUrl: event.url,
previewExpiresAt: event.expiresAt
}
}
return state
case 'FAIL':
if (
state.stage === 'initializing' ||
state.stage === 'uploading' ||
state.stage === 'completing'
) {
return { stage: 'error', failedStage: event.stage, error: event.message }
}
return state
case 'RESET':
return INITIAL_ASSET_UPLOAD_STATE
default:
return state
}
}