feat(任务判死): 心跳正常但 180 分钟无结果上报的二次判死线(13 模块)+ 同期待发改动

判死线(治 28131 型「主线程卡死、心跳线程照发」):
- 判据改看 biz_task_scope_state.last_chunk_at(HTTP 心跳不刷新它);从未上报跳过不判
- 中央线覆盖 DELETE_BRAND/PRODUCT_RISK_RESOLVE/PRICE_TRACK/SHOP_MATCH/PATROL_DELETE/QUERY_ASIN/WITHDRAW
- 自带线接入 COLLECT_DATA/SIMILAR_ASIN/APPEARANCE_PATENT/SHOP_DATA_CRAWL/PUBLISH/BRAND
- 客户端心跳带处理位置 progressText,判死文案含最后位置;no-result-upload-timeout-minutes 默认 180(0 关闭)

同期带上另一工作流的待发改动:跟价换 IP 重试、品牌检测重试上限与 LLM 并发下调、
教程包后台管理页与 V126 迁移、admin-vue 教程记录页。
This commit is contained in:
2026-09-14 17:56:20 +08:00
parent b54f72d3f6
commit 52b55df7b2
36 changed files with 1538 additions and 57 deletions
@@ -22,11 +22,12 @@ export interface TutorialUploadTarget {
objectKey: string
uploadUrl: string
fileUrl: string
version: string
}
/** 第一步:向后端申请直传 PUT 预签名地址(登录会话签发,3 分钟有效)。 */
export async function requestTutorialPresign(fileName: string): Promise<TutorialUploadTarget> {
const { data } = await http.post<unknown>(TUTORIAL_PRESIGN_ENDPOINT, null, { params: { file_name: fileName } })
export async function requestTutorialPresign(fileName: string, version = ''): Promise<TutorialUploadTarget> {
const { data } = await http.post<unknown>(TUTORIAL_PRESIGN_ENDPOINT, null, { params: { file_name: fileName, version } })
const core = (unwrap(data) ?? {}) as Record<string, unknown>
const uploadUrl = typeof core.upload_url === 'string' ? core.upload_url : ''
const objectKey = typeof core.object_key === 'string' ? core.object_key : ''
@@ -37,13 +38,14 @@ export async function requestTutorialPresign(fileName: string): Promise<Tutorial
objectKey,
uploadUrl,
fileUrl: typeof core.file_url === 'string' ? core.file_url : '',
version: typeof core.version === 'string' ? core.version : version.trim(),
}
}
/** 第三步:直传完成后通知后端校验对象并写入记录(data.item 为新记录行)。 */
export async function confirmTutorialPackage(objectKey: string, fileName: string): Promise<TutorialPackageItem | null> {
export async function confirmTutorialPackage(objectKey: string, fileName: string, version = ''): Promise<TutorialPackageItem | null> {
const { data } = await http.post<unknown>(TUTORIAL_CONFIRM_ENDPOINT, null, {
params: { object_key: objectKey, file_name: fileName },
params: { object_key: objectKey, file_name: fileName, version },
})
return parseTutorialPackageUpload(data)
}
@@ -63,9 +65,10 @@ export async function deleteTutorialPackages(ids: number[]): Promise<number> {
export async function uploadTutorialPackage(
file: Blob,
fileName: string,
version = '',
onProgress?: (percent: number) => void,
): Promise<TutorialPackageItem | null> {
const target = await requestTutorialPresign(fileName)
const target = await requestTutorialPresign(fileName, version)
await directPut.put(target.uploadUrl, file, {
headers: { 'Content-Type': 'application/octet-stream' },
onUploadProgress: (event) => {
@@ -74,5 +77,5 @@ export async function uploadTutorialPackage(
onProgress(total > 0 ? Math.min(Math.round((event.loaded / total) * 100), 100) : 0)
},
})
return confirmTutorialPackage(target.objectKey, fileName)
return confirmTutorialPackage(target.objectKey, fileName, target.version)
}