feat: 支持退出后补告衣物处理结果
This commit is contained in:
@@ -0,0 +1,138 @@
|
||||
// 待核对处理结果的衣物(本地持久化 + 结果核对)。
|
||||
//
|
||||
// 衣物上传成功后写入;即使用户退出小程序,下次进入衣橱时也会逐条查询
|
||||
// /clothes/:uuid/status,把「处理成功 / 处理失败」的最终结果补告给用户,
|
||||
// 避免"退出后不知道衣服到底处理成功没有"。
|
||||
//
|
||||
// 处理本身在后端与连接解耦(独立 ctx + 定时恢复调度器),前端只承担
|
||||
// 「进入时核对一次」的最小职责:pending 为空时零额外请求,不做常驻轮询。
|
||||
import { STORAGE_KEYS, ProcessStatus } from './config'
|
||||
import { request } from './request'
|
||||
import { logger } from './logger'
|
||||
|
||||
// 日志模块名
|
||||
const LOG = 'pending-clothes'
|
||||
|
||||
// 存储上限:防御性上限,在途任务正常只有 1-2 条
|
||||
const MAX_ITEMS = 20
|
||||
// 过期时长(秒):超时直接丢弃;后端恢复调度器最迟约 5 分钟接手,24h 足够冗余
|
||||
const EXPIRE_SEC = 24 * 60 * 60
|
||||
// 衣物不存在(已被用户删除):静默移出,避免残留
|
||||
const CLOTHES_NOT_FOUND_CODE = 50000
|
||||
// 无权操作该衣物(换账号后残留的 uuid):同样静默移出,避免每次进入都反复请求
|
||||
const CLOTHES_NO_PERMISSION_CODE = 50005
|
||||
|
||||
export interface PendingClothes {
|
||||
uuid: string
|
||||
createdAt: number // 秒级时间戳
|
||||
}
|
||||
|
||||
export interface PendingResolveResult {
|
||||
finished: string[] // 本轮核对为处理成功
|
||||
failed: Array<{ uuid: string; reason: string }> // 本轮核对为处理失败(含原因)
|
||||
processing: string[] // 仍在处理中,保留待下次核对
|
||||
}
|
||||
|
||||
function writeAll(list: PendingClothes[]): void {
|
||||
try {
|
||||
wx.setStorageSync(STORAGE_KEYS.PENDING_CLOTHES, list)
|
||||
} catch (e) {
|
||||
logger.warn(LOG, '待核对衣物写入失败(降级为仅本次会话生效)')
|
||||
}
|
||||
}
|
||||
|
||||
// 读取并清洗存储:非法结构 / 过期记录一并剔除;读取即顺手落盘
|
||||
export function getPendingClothes(): PendingClothes[] {
|
||||
let raw: any = null
|
||||
try {
|
||||
raw = wx.getStorageSync(STORAGE_KEYS.PENDING_CLOTHES)
|
||||
} catch (e) {
|
||||
raw = null
|
||||
}
|
||||
if (!Array.isArray(raw)) return []
|
||||
|
||||
const now = Math.floor(Date.now() / 1000)
|
||||
const valid = (raw as any[])
|
||||
.filter((it) => it && typeof it.uuid === 'string' && !!it.uuid)
|
||||
.filter((it) => Number(it.createdAt) > 0 && now - Number(it.createdAt) <= EXPIRE_SEC)
|
||||
.slice(-MAX_ITEMS) as PendingClothes[]
|
||||
|
||||
if (valid.length !== raw.length) writeAll(valid)
|
||||
return valid
|
||||
}
|
||||
|
||||
// 登记待核对衣物(按 uuid 幂等去重)
|
||||
export function addPendingClothes(uuid: string): void {
|
||||
if (!uuid) return
|
||||
const list = getPendingClothes().filter((it) => it.uuid !== uuid)
|
||||
list.push({ uuid, createdAt: Math.floor(Date.now() / 1000) })
|
||||
const next = list.slice(-MAX_ITEMS)
|
||||
writeAll(next)
|
||||
logger.debug(LOG, '待核对衣物已登记, uuid=', uuid, '总数=', next.length)
|
||||
}
|
||||
|
||||
export function removePendingClothes(uuid: string): void {
|
||||
if (!uuid) return
|
||||
const list = getPendingClothes()
|
||||
const next = list.filter((it) => it.uuid !== uuid)
|
||||
if (next.length === list.length) return
|
||||
writeAll(next)
|
||||
logger.debug(LOG, '待核对衣物已移出, uuid=', uuid, '剩余=', next.length)
|
||||
}
|
||||
|
||||
/**
|
||||
* 逐条核对处理状态:
|
||||
* - 成功 / 失败:移出存储并计入结果(失败带原因);
|
||||
* - 衣物已删除:静默移出;
|
||||
* - 仍在处理中 / 网络失败 / 未登录:保留待下次核对。
|
||||
* 后台核对不弹错、不触发登录跳转(showError:false + silentAuth:true)。
|
||||
*/
|
||||
export function resolvePendingClothes(): Promise<PendingResolveResult> {
|
||||
const list = getPendingClothes()
|
||||
const result: PendingResolveResult = { finished: [], failed: [], processing: [] }
|
||||
if (!list.length) return Promise.resolve(result)
|
||||
|
||||
const tasks = list.map((item) =>
|
||||
request<{ image_process_status: number; fail_reason?: string }>({
|
||||
url: `/clothes/${item.uuid}/status`,
|
||||
showError: false,
|
||||
silentAuth: true,
|
||||
})
|
||||
.then((res) => {
|
||||
const status = Number(res && res.image_process_status)
|
||||
if (status === ProcessStatus.SUCCESS) {
|
||||
removePendingClothes(item.uuid)
|
||||
result.finished.push(item.uuid)
|
||||
return
|
||||
}
|
||||
if (status === ProcessStatus.FAILED) {
|
||||
removePendingClothes(item.uuid)
|
||||
result.failed.push({ uuid: item.uuid, reason: (res && res.fail_reason) || '识别失败' })
|
||||
return
|
||||
}
|
||||
result.processing.push(item.uuid)
|
||||
})
|
||||
.catch((e: any) => {
|
||||
// 衣物已被删除 / 已不属于当前账号:静默移出,避免每次进入都反复请求
|
||||
if (e && (e.code === CLOTHES_NOT_FOUND_CODE || e.code === CLOTHES_NO_PERMISSION_CODE)) {
|
||||
removePendingClothes(item.uuid)
|
||||
return
|
||||
}
|
||||
logger.warn(LOG, '核对衣物处理状态失败, uuid=', item.uuid, 'code=', e && e.code)
|
||||
result.processing.push(item.uuid)
|
||||
}),
|
||||
)
|
||||
|
||||
return Promise.all(tasks).then(() => {
|
||||
logger.info(
|
||||
LOG,
|
||||
'待核对衣物解析完成, 成功=',
|
||||
result.finished.length,
|
||||
'失败=',
|
||||
result.failed.length,
|
||||
'处理中=',
|
||||
result.processing.length,
|
||||
)
|
||||
return result
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user