forked from natuka/web.puabadge.com
修改:处理偶发会话不存在的问题
This commit is contained in:
1 parent
3b10685876
commit
789d4f106c
4 files changed
+97
-14
No files matched your search
+8
-1
@@ -712,7 +712,14 @@ export const getUploadUrl = async (input: {
|
||||
)
|
||||
|
||||
// 注意:若 Content-Type 不是 application/json,axios 会把 body 留成 string
|
||||
const ticket = parseUploadUrlResult(response?.data)
|
||||
const body = coerceResponseBody(response?.data)
|
||||
const code = Number(body.code)
|
||||
// 业务失败(如会话不存在)需抛出原文,避免被吞成「缺少 upload_id」
|
||||
if (Number.isFinite(code) && code !== 1000 && code !== 0) {
|
||||
throw new Error(textOf(body.message || body.msg) || '签发上传地址失败')
|
||||
}
|
||||
|
||||
const ticket = parseUploadUrlResult(body)
|
||||
|
||||
if (!ticket.upload_id) {
|
||||
throw new Error('签发上传地址失败:缺少 upload_id')
|
||||
|
||||
@@ -64,6 +64,8 @@ export const jingdongConfig = {
|
||||
needProductTitle: (label: string) => `请填写${label}后再发送`,
|
||||
uploadingTip: '正在上传照片,请稍候…',
|
||||
inspectingTip: '正在识别照片,请稍候…',
|
||||
sessionLoadingTip: '页面加载中,请稍候再上传…',
|
||||
sessionMissing: '会话未就绪,请稍候再试或刷新页面',
|
||||
/** 仅提示,不拦截发送 */
|
||||
subjectMismatchTip: '上传图片的主体数量与下单主体数量不一致,生图时候会按下单的主体数量自动调整主体',
|
||||
subjectMismatchToast: (got: string, need: string) =>
|
||||
|
||||
@@ -32,6 +32,7 @@ const KNOWN: Record<string, string> = {
|
||||
效果图加载失败: '效果图加载失败,请重试',
|
||||
'效果图加载失败,请重试': '效果图加载失败,请重试',
|
||||
'product_id不在Agent开放分类': '当前产品暂不支持在线生图,请联系客服',
|
||||
会话不存在: jingdongConfig.messages.sessionMissing,
|
||||
}
|
||||
|
||||
/** 生图 / 编辑:内容策略单独翻译;Processing/ApiError 走场景兜底文案 */
|
||||
|
||||
@@ -205,6 +205,10 @@ const remainCount = ref(0)
|
||||
/** 打底时已统计到的成功次数;新增成功 = 当前成功数 - baselineUsed */
|
||||
const remainBaselineUsed = ref(0)
|
||||
const sessionId = ref('')
|
||||
/** orders/stat 的 session_id 已通过 session/detail 确认可用 */
|
||||
const sessionReady = ref(false)
|
||||
/** 会话初始化结束但仍未就绪(需刷新) */
|
||||
const sessionBootFailed = ref(false)
|
||||
const subjectName = ref('')
|
||||
/** stat 返回的主体文案(订单约定主体,标签始终展示此值) */
|
||||
const subjectNameFromStat = ref('')
|
||||
@@ -368,6 +372,11 @@ const canSend = computed(() => {
|
||||
|
||||
const composerStatusTip = computed(() => {
|
||||
if (isBusy.value) return config.messages.generatingTip
|
||||
if (!sessionReady.value) {
|
||||
return sessionBootFailed.value
|
||||
? config.messages.sessionMissing
|
||||
: config.messages.sessionLoadingTip
|
||||
}
|
||||
if (uploading.value) return config.messages.uploadingTip
|
||||
if (inspecting.value) return config.messages.inspectingTip
|
||||
if (remainCount.value <= 0) return config.messages.noRemain
|
||||
@@ -445,11 +454,26 @@ function scrollToBottom() {
|
||||
})
|
||||
}
|
||||
|
||||
function isSessionMissingError(error: unknown) {
|
||||
const msg =
|
||||
error instanceof Error
|
||||
? String(error.message || '')
|
||||
: typeof error === 'string'
|
||||
? error
|
||||
: String((error as any)?.message || (error as any)?.msg || '')
|
||||
return /会话不存在|session\s*(not\s*found|missing|invalid|expired)/i.test(msg)
|
||||
}
|
||||
|
||||
function triggerUpload() {
|
||||
if (isBusy.value) {
|
||||
showToast(config.messages.generatingTip)
|
||||
return
|
||||
}
|
||||
if (!sessionReady.value) {
|
||||
showToast(config.messages.sessionLoadingTip)
|
||||
void fetchOrderStat()
|
||||
return
|
||||
}
|
||||
if (isEditing.value) {
|
||||
showToast('编辑效果图时请直接描述想调整的内容')
|
||||
return
|
||||
@@ -509,20 +533,40 @@ async function startPendingUploadAndClassify(file: File) {
|
||||
photoInspect.value = null
|
||||
subjectMatched.value = true
|
||||
|
||||
const sid = String(sessionId.value || '').trim()
|
||||
if (!sid) {
|
||||
showToast(friendlyError('缺少会话信息,请刷新后重试', 'common'))
|
||||
let sid = String(sessionId.value || '').trim()
|
||||
if (!sid || !sessionReady.value) {
|
||||
await fetchOrderStat()
|
||||
if (serial !== uploadSerial) return
|
||||
sid = String(sessionId.value || '').trim()
|
||||
}
|
||||
if (!sid || !sessionReady.value) {
|
||||
showToast(config.messages.sessionMissing)
|
||||
return
|
||||
}
|
||||
|
||||
uploading.value = true
|
||||
inspecting.value = false
|
||||
try {
|
||||
const runPrepare = () =>
|
||||
jingdongApi.prepareUpload(file, {
|
||||
session_id: sid,
|
||||
purpose: 'origin',
|
||||
})
|
||||
|
||||
// 仅签发 url 并 PUT,不 confirm
|
||||
const prepared = await jingdongApi.prepareUpload(file, {
|
||||
session_id: sid,
|
||||
purpose: 'origin',
|
||||
})
|
||||
let prepared
|
||||
try {
|
||||
prepared = await runPrepare()
|
||||
} catch (error) {
|
||||
// 偶发「会话不存在」:重拉会话后自动再试一次
|
||||
if (!isSessionMissingError(error)) throw error
|
||||
sessionReady.value = false
|
||||
await fetchOrderStat()
|
||||
if (serial !== uploadSerial) return
|
||||
sid = String(sessionId.value || '').trim()
|
||||
if (!sid || !sessionReady.value) throw error
|
||||
prepared = await runPrepare()
|
||||
}
|
||||
if (serial !== uploadSerial) return
|
||||
pendingUploadId.value = prepared.uploadId
|
||||
lastUploadId.value = prepared.uploadId
|
||||
@@ -530,7 +574,9 @@ async function startPendingUploadAndClassify(file: File) {
|
||||
if (serial !== uploadSerial) return
|
||||
pendingUploadId.value = ''
|
||||
showToast({
|
||||
message: friendlyError(error, 'upload'),
|
||||
message: isSessionMissingError(error)
|
||||
? config.messages.sessionMissing
|
||||
: friendlyError(error, 'upload'),
|
||||
duration: 2800,
|
||||
})
|
||||
return
|
||||
@@ -1012,12 +1058,13 @@ function applySessionHistory(detail: ReturnType<typeof jingdongApi.parseSessionD
|
||||
scrollToBottom()
|
||||
}
|
||||
|
||||
function fetchSessionDetail(sid: string) {
|
||||
/** 拉取 session/detail;成功返回 true(用于确认会话已在 JD 侧可用) */
|
||||
function fetchSessionDetail(sid: string): Promise<boolean> {
|
||||
const id = String(sid || '').trim()
|
||||
if (!id) {
|
||||
sessionDetail.value = null
|
||||
seedRemainFromApi(orderStat.value?.remain_count)
|
||||
return Promise.resolve()
|
||||
return Promise.resolve(false)
|
||||
}
|
||||
return jingdongApi
|
||||
.getSessionDetail({ session_id: id })
|
||||
@@ -1030,17 +1077,31 @@ function fetchSessionDetail(sid: string) {
|
||||
}
|
||||
sessionDetail.value = parsed
|
||||
applySessionHistory(parsed)
|
||||
return true
|
||||
})
|
||||
.catch(() => {
|
||||
sessionDetail.value = null
|
||||
seedRemainFromApi(orderStat.value?.remain_count)
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
/** detail 偶发未就绪时短退避重试,避免过早放开上传 */
|
||||
async function waitForSessionDetail(sid: string, attempts = 3): Promise<boolean> {
|
||||
for (let i = 0; i < attempts; i += 1) {
|
||||
const ok = await fetchSessionDetail(sid)
|
||||
if (ok) return true
|
||||
if (i < attempts - 1) await sleep(350 * (i + 1))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function fetchOrderStat() {
|
||||
sessionReady.value = false
|
||||
sessionBootFailed.value = false
|
||||
return badgeApi
|
||||
.getOrderStat({})
|
||||
.then((res: any) => {
|
||||
.then(async (res: any) => {
|
||||
// interceptor 已解包 data:remain_count / subject_name / type_name / session_id / prod_id ...
|
||||
orderStat.value = res || {}
|
||||
subjectNameFromStat.value = String(res?.subject_name || '').trim()
|
||||
@@ -1048,13 +1109,22 @@ function fetchOrderStat() {
|
||||
styleName.value = String(res?.style_name || '').trim()
|
||||
// 产品名称展示取 cate_name
|
||||
typeName.value = String(res?.cate_name || '').trim()
|
||||
sessionId.value = String(res?.session_id || '').trim()
|
||||
const sid = String(res?.session_id || '').trim()
|
||||
sessionId.value = sid
|
||||
subjectMatched.value = true
|
||||
// 先记下接口值;等会话历史回来后再按已生成次数扣除
|
||||
const apiRemain = Number(res?.remain_count)
|
||||
remainCount.value = Number.isFinite(apiRemain) ? Math.max(0, apiRemain) : 0
|
||||
remainBaselineUsed.value = 0
|
||||
return fetchSessionDetail(sessionId.value)
|
||||
if (!sid) {
|
||||
sessionReady.value = false
|
||||
sessionBootFailed.value = true
|
||||
return false
|
||||
}
|
||||
const ok = await waitForSessionDetail(sid)
|
||||
sessionReady.value = ok
|
||||
sessionBootFailed.value = !ok
|
||||
return ok
|
||||
})
|
||||
.catch(() => {
|
||||
orderStat.value = null
|
||||
@@ -1065,7 +1135,10 @@ function fetchOrderStat() {
|
||||
typeName.value = ''
|
||||
sessionId.value = ''
|
||||
sessionDetail.value = null
|
||||
sessionReady.value = false
|
||||
sessionBootFailed.value = true
|
||||
subjectMatched.value = true
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user