style: prettier 格式化 useStep4Preview + useStep6Cover
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 46s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m35s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m46s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 1m13s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 1m44s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 2m49s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 2m24s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 3m13s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 4m31s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 1m0s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 19s
AI Code Review / AI Code Review (pull_request) Successful in 6m57s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 6m11s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 7m28s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 10m13s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 9m48s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 5m49s
CI/CD Pipeline / CI Gate (pull_request) Failing after 6s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 832h49m50s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 832h49m51s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 832h49m51s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 832h49m52s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 833h0m25s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 833h0m28s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 833h0m29s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 833h0m29s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 833h0m26s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 833h23m20s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 833h23m21s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 833h33m57s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 833h33m59s

This commit is contained in:
xiaoxia
2026-08-12 22:26:19 +08:00
parent cada58e9ea
commit efab5b45cd
2 changed files with 87 additions and 80 deletions
@@ -212,99 +212,102 @@ export function useStep4Preview({
}, [clearPollTimer])
/** 轮询单个预览任务状态 */
const pollPreviewStatus = useCallback((index: number, taskId: string) => {
const poll = async () => {
// 竞态检查
if (taskIdsRef.current.get(index) !== taskId) return
// 超时检查
if (Date.now() - startTimeRef.current > POLL_TIMEOUT_MS) {
setItems((prev) =>
prev.map((it) =>
it.index === index ? { ...it, status: "error", error: "预览生成超时,请重试" } : it,
),
)
return
}
try {
const data: PreviewTaskResponse = await getPreviewStatus(taskId)
const pollPreviewStatus = useCallback(
(index: number, taskId: string) => {
const poll = async () => {
// 竞态检查
if (taskIdsRef.current.get(index) !== taskId) return
const status = data.status as ApiPreviewStatus
// 超时检查
if (Date.now() - startTimeRef.current > POLL_TIMEOUT_MS) {
setItems((prev) =>
prev.map((it) =>
it.index === index ? { ...it, status: "error", error: "预览生成超时,请重试" } : it,
),
)
return
}
if (status === "completed") {
const result: PreviewResult = {
taskId: safeString(data.task_id, ""),
videoUrl: safeString(data.video_url, ""),
clipCount: safeNumber(data.clip_count),
transitionCount: safeNumber(data.transition_count),
materialUsage: safeNumber(data.material_usage),
duration: safeNumber(data.duration),
fileSize: safeNumber(data.file_size),
generateDuration: safeNumber(data.generate_duration),
progress: 100,
try {
const data: PreviewTaskResponse = await getPreviewStatus(taskId)
if (taskIdsRef.current.get(index) !== taskId) return
const status = data.status as ApiPreviewStatus
if (status === "completed") {
const result: PreviewResult = {
taskId: safeString(data.task_id, ""),
videoUrl: safeString(data.video_url, ""),
clipCount: safeNumber(data.clip_count),
transitionCount: safeNumber(data.transition_count),
materialUsage: safeNumber(data.material_usage),
duration: safeNumber(data.duration),
fileSize: safeNumber(data.file_size),
generateDuration: safeNumber(data.generate_duration),
progress: 100,
}
setItems((prev) =>
prev.map((it) =>
it.index === index ? { ...it, status: "ready", result, progress: 100 } : it,
),
)
// 保存预览视频 URL 到 plan config,供封面生成使用
if (result.videoUrl && selectedTemplate) {
updateEditPlan(selectedTemplate, {
config: { rendered_storage_key: result.videoUrl },
}).catch((err) => {
console.warn("[Step4] 保存预览视频URL到plan config失败:", err)
})
}
return
}
setItems((prev) =>
prev.map((it) =>
it.index === index ? { ...it, status: "ready", result, progress: 100 } : it,
),
)
// 保存预览视频 URL 到 plan config,供封面生成使用
if (result.videoUrl && selectedTemplate) {
updateEditPlan(selectedTemplate, {
config: { rendered_storage_key: result.videoUrl },
}).catch((err) => {
console.warn("[Step4] 保存预览视频URL到plan config失败:", err)
})
if (status === "failed") {
setItems((prev) =>
prev.map((it) =>
it.index === index
? {
...it,
status: "error",
error: safeString(data.error_message, "预览生成失败,请重试"),
}
: it,
),
)
return
}
return
}
if (status === "failed") {
if (status === "cancelled") {
setItems((prev) =>
prev.map((it) =>
it.index === index ? { ...it, status: "error", error: "预览任务已取消" } : it,
),
)
return
}
// pending / generating 状态继续轮询
const prog = safeNumber(data.progress)
const nextStatus: PreviewStatus = status === "pending" ? "pending" : "generating"
setItems((prev) =>
prev.map((it) =>
it.index === index
? {
...it,
status: "error",
error: safeString(data.error_message, "预览生成失败,请重试"),
}
: it,
it.index === index ? { ...it, status: nextStatus, progress: prog } : it,
),
)
return
const delay = status === "pending" ? 5000 : 2000
pollTimersRef.current.set(index, setTimeout(poll, delay))
} catch {
if (taskIdsRef.current.get(index) !== taskId) return
pollTimersRef.current.set(index, setTimeout(poll, 3000))
}
if (status === "cancelled") {
setItems((prev) =>
prev.map((it) =>
it.index === index ? { ...it, status: "error", error: "预览任务已取消" } : it,
),
)
return
}
// pending / generating 状态继续轮询
const prog = safeNumber(data.progress)
const nextStatus: PreviewStatus = status === "pending" ? "pending" : "generating"
setItems((prev) =>
prev.map((it) =>
it.index === index ? { ...it, status: nextStatus, progress: prog } : it,
),
)
const delay = status === "pending" ? 5000 : 2000
pollTimersRef.current.set(index, setTimeout(poll, delay))
} catch {
if (taskIdsRef.current.get(index) !== taskId) return
pollTimersRef.current.set(index, setTimeout(poll, 3000))
}
}
pollTimersRef.current.set(index, setTimeout(poll, 1000))
}, [selectedTemplate])
pollTimersRef.current.set(index, setTimeout(poll, 1000))
},
[selectedTemplate],
)
/** 生成所有预览 */
const generatePreview = useCallback(async () => {
@@ -110,7 +110,11 @@ export function useStep6Cover({
// 提取详细错误信息
let errorMsg = "封面生成失败"
const e = err as { response?: { data?: { detail?: string; message?: string }; status?: number }; request?: unknown; message?: string }
const e = err as {
response?: { data?: { detail?: string; message?: string }; status?: number }
request?: unknown
message?: string
}
if (e.response) {
// 后端返回错误
const detail = e.response.data?.detail || e.response.data?.message || ""