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
|
// 注意:若 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) {
|
if (!ticket.upload_id) {
|
||||||
throw new Error('签发上传地址失败:缺少 upload_id')
|
throw new Error('签发上传地址失败:缺少 upload_id')
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ export const jingdongConfig = {
|
|||||||
needProductTitle: (label: string) => `请填写${label}后再发送`,
|
needProductTitle: (label: string) => `请填写${label}后再发送`,
|
||||||
uploadingTip: '正在上传照片,请稍候…',
|
uploadingTip: '正在上传照片,请稍候…',
|
||||||
inspectingTip: '正在识别照片,请稍候…',
|
inspectingTip: '正在识别照片,请稍候…',
|
||||||
|
sessionLoadingTip: '页面加载中,请稍候再上传…',
|
||||||
|
sessionMissing: '会话未就绪,请稍候再试或刷新页面',
|
||||||
/** 仅提示,不拦截发送 */
|
/** 仅提示,不拦截发送 */
|
||||||
subjectMismatchTip: '上传图片的主体数量与下单主体数量不一致,生图时候会按下单的主体数量自动调整主体',
|
subjectMismatchTip: '上传图片的主体数量与下单主体数量不一致,生图时候会按下单的主体数量自动调整主体',
|
||||||
subjectMismatchToast: (got: string, need: string) =>
|
subjectMismatchToast: (got: string, need: string) =>
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ const KNOWN: Record<string, string> = {
|
|||||||
效果图加载失败: '效果图加载失败,请重试',
|
效果图加载失败: '效果图加载失败,请重试',
|
||||||
'效果图加载失败,请重试': '效果图加载失败,请重试',
|
'效果图加载失败,请重试': '效果图加载失败,请重试',
|
||||||
'product_id不在Agent开放分类': '当前产品暂不支持在线生图,请联系客服',
|
'product_id不在Agent开放分类': '当前产品暂不支持在线生图,请联系客服',
|
||||||
|
会话不存在: jingdongConfig.messages.sessionMissing,
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 生图 / 编辑:内容策略单独翻译;Processing/ApiError 走场景兜底文案 */
|
/** 生图 / 编辑:内容策略单独翻译;Processing/ApiError 走场景兜底文案 */
|
||||||
|
|||||||
@@ -205,6 +205,10 @@ const remainCount = ref(0)
|
|||||||
/** 打底时已统计到的成功次数;新增成功 = 当前成功数 - baselineUsed */
|
/** 打底时已统计到的成功次数;新增成功 = 当前成功数 - baselineUsed */
|
||||||
const remainBaselineUsed = ref(0)
|
const remainBaselineUsed = ref(0)
|
||||||
const sessionId = ref('')
|
const sessionId = ref('')
|
||||||
|
/** orders/stat 的 session_id 已通过 session/detail 确认可用 */
|
||||||
|
const sessionReady = ref(false)
|
||||||
|
/** 会话初始化结束但仍未就绪(需刷新) */
|
||||||
|
const sessionBootFailed = ref(false)
|
||||||
const subjectName = ref('')
|
const subjectName = ref('')
|
||||||
/** stat 返回的主体文案(订单约定主体,标签始终展示此值) */
|
/** stat 返回的主体文案(订单约定主体,标签始终展示此值) */
|
||||||
const subjectNameFromStat = ref('')
|
const subjectNameFromStat = ref('')
|
||||||
@@ -368,6 +372,11 @@ const canSend = computed(() => {
|
|||||||
|
|
||||||
const composerStatusTip = computed(() => {
|
const composerStatusTip = computed(() => {
|
||||||
if (isBusy.value) return config.messages.generatingTip
|
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 (uploading.value) return config.messages.uploadingTip
|
||||||
if (inspecting.value) return config.messages.inspectingTip
|
if (inspecting.value) return config.messages.inspectingTip
|
||||||
if (remainCount.value <= 0) return config.messages.noRemain
|
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() {
|
function triggerUpload() {
|
||||||
if (isBusy.value) {
|
if (isBusy.value) {
|
||||||
showToast(config.messages.generatingTip)
|
showToast(config.messages.generatingTip)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (!sessionReady.value) {
|
||||||
|
showToast(config.messages.sessionLoadingTip)
|
||||||
|
void fetchOrderStat()
|
||||||
|
return
|
||||||
|
}
|
||||||
if (isEditing.value) {
|
if (isEditing.value) {
|
||||||
showToast('编辑效果图时请直接描述想调整的内容')
|
showToast('编辑效果图时请直接描述想调整的内容')
|
||||||
return
|
return
|
||||||
@@ -509,20 +533,40 @@ async function startPendingUploadAndClassify(file: File) {
|
|||||||
photoInspect.value = null
|
photoInspect.value = null
|
||||||
subjectMatched.value = true
|
subjectMatched.value = true
|
||||||
|
|
||||||
const sid = String(sessionId.value || '').trim()
|
let sid = String(sessionId.value || '').trim()
|
||||||
if (!sid) {
|
if (!sid || !sessionReady.value) {
|
||||||
showToast(friendlyError('缺少会话信息,请刷新后重试', 'common'))
|
await fetchOrderStat()
|
||||||
|
if (serial !== uploadSerial) return
|
||||||
|
sid = String(sessionId.value || '').trim()
|
||||||
|
}
|
||||||
|
if (!sid || !sessionReady.value) {
|
||||||
|
showToast(config.messages.sessionMissing)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
uploading.value = true
|
uploading.value = true
|
||||||
inspecting.value = false
|
inspecting.value = false
|
||||||
try {
|
try {
|
||||||
|
const runPrepare = () =>
|
||||||
|
jingdongApi.prepareUpload(file, {
|
||||||
|
session_id: sid,
|
||||||
|
purpose: 'origin',
|
||||||
|
})
|
||||||
|
|
||||||
// 仅签发 url 并 PUT,不 confirm
|
// 仅签发 url 并 PUT,不 confirm
|
||||||
const prepared = await jingdongApi.prepareUpload(file, {
|
let prepared
|
||||||
session_id: sid,
|
try {
|
||||||
purpose: 'origin',
|
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
|
if (serial !== uploadSerial) return
|
||||||
pendingUploadId.value = prepared.uploadId
|
pendingUploadId.value = prepared.uploadId
|
||||||
lastUploadId.value = prepared.uploadId
|
lastUploadId.value = prepared.uploadId
|
||||||
@@ -530,7 +574,9 @@ async function startPendingUploadAndClassify(file: File) {
|
|||||||
if (serial !== uploadSerial) return
|
if (serial !== uploadSerial) return
|
||||||
pendingUploadId.value = ''
|
pendingUploadId.value = ''
|
||||||
showToast({
|
showToast({
|
||||||
message: friendlyError(error, 'upload'),
|
message: isSessionMissingError(error)
|
||||||
|
? config.messages.sessionMissing
|
||||||
|
: friendlyError(error, 'upload'),
|
||||||
duration: 2800,
|
duration: 2800,
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
@@ -1012,12 +1058,13 @@ function applySessionHistory(detail: ReturnType<typeof jingdongApi.parseSessionD
|
|||||||
scrollToBottom()
|
scrollToBottom()
|
||||||
}
|
}
|
||||||
|
|
||||||
function fetchSessionDetail(sid: string) {
|
/** 拉取 session/detail;成功返回 true(用于确认会话已在 JD 侧可用) */
|
||||||
|
function fetchSessionDetail(sid: string): Promise<boolean> {
|
||||||
const id = String(sid || '').trim()
|
const id = String(sid || '').trim()
|
||||||
if (!id) {
|
if (!id) {
|
||||||
sessionDetail.value = null
|
sessionDetail.value = null
|
||||||
seedRemainFromApi(orderStat.value?.remain_count)
|
seedRemainFromApi(orderStat.value?.remain_count)
|
||||||
return Promise.resolve()
|
return Promise.resolve(false)
|
||||||
}
|
}
|
||||||
return jingdongApi
|
return jingdongApi
|
||||||
.getSessionDetail({ session_id: id })
|
.getSessionDetail({ session_id: id })
|
||||||
@@ -1030,17 +1077,31 @@ function fetchSessionDetail(sid: string) {
|
|||||||
}
|
}
|
||||||
sessionDetail.value = parsed
|
sessionDetail.value = parsed
|
||||||
applySessionHistory(parsed)
|
applySessionHistory(parsed)
|
||||||
|
return true
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
sessionDetail.value = null
|
sessionDetail.value = null
|
||||||
seedRemainFromApi(orderStat.value?.remain_count)
|
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() {
|
function fetchOrderStat() {
|
||||||
|
sessionReady.value = false
|
||||||
|
sessionBootFailed.value = false
|
||||||
return badgeApi
|
return badgeApi
|
||||||
.getOrderStat({})
|
.getOrderStat({})
|
||||||
.then((res: any) => {
|
.then(async (res: any) => {
|
||||||
// interceptor 已解包 data:remain_count / subject_name / type_name / session_id / prod_id ...
|
// interceptor 已解包 data:remain_count / subject_name / type_name / session_id / prod_id ...
|
||||||
orderStat.value = res || {}
|
orderStat.value = res || {}
|
||||||
subjectNameFromStat.value = String(res?.subject_name || '').trim()
|
subjectNameFromStat.value = String(res?.subject_name || '').trim()
|
||||||
@@ -1048,13 +1109,22 @@ function fetchOrderStat() {
|
|||||||
styleName.value = String(res?.style_name || '').trim()
|
styleName.value = String(res?.style_name || '').trim()
|
||||||
// 产品名称展示取 cate_name
|
// 产品名称展示取 cate_name
|
||||||
typeName.value = String(res?.cate_name || '').trim()
|
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
|
subjectMatched.value = true
|
||||||
// 先记下接口值;等会话历史回来后再按已生成次数扣除
|
// 先记下接口值;等会话历史回来后再按已生成次数扣除
|
||||||
const apiRemain = Number(res?.remain_count)
|
const apiRemain = Number(res?.remain_count)
|
||||||
remainCount.value = Number.isFinite(apiRemain) ? Math.max(0, apiRemain) : 0
|
remainCount.value = Number.isFinite(apiRemain) ? Math.max(0, apiRemain) : 0
|
||||||
remainBaselineUsed.value = 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(() => {
|
.catch(() => {
|
||||||
orderStat.value = null
|
orderStat.value = null
|
||||||
@@ -1065,7 +1135,10 @@ function fetchOrderStat() {
|
|||||||
typeName.value = ''
|
typeName.value = ''
|
||||||
sessionId.value = ''
|
sessionId.value = ''
|
||||||
sessionDetail.value = null
|
sessionDetail.value = null
|
||||||
|
sessionReady.value = false
|
||||||
|
sessionBootFailed.value = true
|
||||||
subjectMatched.value = true
|
subjectMatched.value = true
|
||||||
|
return false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user