feat: 完成前端业务闭环与后端联调
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
import { runtimeConfig } from '@/utils/runtime-config.js'
|
||||
import { goRoot } from '@/utils/navigation/gateway.js'
|
||||
import { recoverToAuthRoot } from '@/utils/navigation/gateway.js'
|
||||
import { session } from '@/utils/session.js'
|
||||
import { createRequestCancelledError } from './request-controller.js'
|
||||
|
||||
const SUCCESS_CODES = [0, 200]
|
||||
const REQUEST_TIMEOUT_MS = 15000
|
||||
const FILE_UPLOAD_TIMEOUT_MS = 120000
|
||||
|
||||
export const createRequestError = (message, code, details = {}) => {
|
||||
const error = new Error(message)
|
||||
@@ -14,7 +15,8 @@ export const createRequestError = (message, code, details = {}) => {
|
||||
}
|
||||
|
||||
const isAuthenticatedSessionRejected = (error) =>
|
||||
error?.code === 'BUSINESS_ERROR' && Number(error.businessCode) === 401
|
||||
(error?.code === 'BUSINESS_ERROR' && Number(error.businessCode) === 401) ||
|
||||
(error?.code === 'HTTP_ERROR' && Number(error.httpStatus) === 401)
|
||||
|
||||
let authenticationRecoveryInFlight = false
|
||||
|
||||
@@ -25,7 +27,7 @@ const recoverExpiredAuthenticatedSession = () => {
|
||||
const finishRecovery = () => {
|
||||
authenticationRecoveryInFlight = false
|
||||
}
|
||||
goRoot('A01').then(finishRecovery, finishRecovery)
|
||||
recoverToAuthRoot().then(finishRecovery, finishRecovery)
|
||||
}
|
||||
|
||||
export const unwrapResponse = (response) => {
|
||||
@@ -87,19 +89,27 @@ export const request = (options, {
|
||||
},
|
||||
success: ({ data, statusCode }) => {
|
||||
if (expectedStatus !== null && statusCode !== expectedStatus) {
|
||||
rejectOnce(createRequestError(
|
||||
const error = createRequestError(
|
||||
`服务只接受 HTTP ${expectedStatus} 响应,实际为 ${statusCode}`,
|
||||
'HTTP_ERROR',
|
||||
{ httpStatus: statusCode }
|
||||
))
|
||||
)
|
||||
if (authenticated && isAuthenticatedSessionRejected(error)) {
|
||||
recoverExpiredAuthenticatedSession()
|
||||
}
|
||||
rejectOnce(error)
|
||||
return
|
||||
}
|
||||
if (statusCode < 200 || statusCode >= 300) {
|
||||
rejectOnce(createRequestError(
|
||||
const error = createRequestError(
|
||||
data?.msg || `请求失败(${statusCode})`,
|
||||
'HTTP_ERROR',
|
||||
{ httpStatus: statusCode }
|
||||
))
|
||||
)
|
||||
if (authenticated && isAuthenticatedSessionRejected(error)) {
|
||||
recoverExpiredAuthenticatedSession()
|
||||
}
|
||||
rejectOnce(error)
|
||||
return
|
||||
}
|
||||
if (
|
||||
@@ -189,6 +199,25 @@ const parseStrictUploadResponse = (responseText, requireData) => {
|
||||
return unwrapResponse(uploadEnvelope)
|
||||
}
|
||||
|
||||
const rejectAuthenticatedUploadStatus = (statusCode) => {
|
||||
const error = createRequestError(
|
||||
`文件分片上传失败(HTTP ${statusCode})`,
|
||||
'HTTP_ERROR',
|
||||
{ httpStatus: statusCode }
|
||||
)
|
||||
if (isAuthenticatedSessionRejected(error)) recoverExpiredAuthenticatedSession()
|
||||
return error
|
||||
}
|
||||
|
||||
const unwrapAuthenticatedUploadResponse = (responseText) => {
|
||||
try {
|
||||
return parseStrictUploadResponse(responseText, false)
|
||||
} catch (error) {
|
||||
if (isAuthenticatedSessionRejected(error)) recoverExpiredAuthenticatedSession()
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export const requestNativeFileChunk = ({ uploadId, chunkIndex, chunkMd5, filePath }, { requestController = null } = {}) => new Promise((resolve, reject) => {
|
||||
if (typeof globalThis.uni?.uploadFile !== 'function') {
|
||||
reject(createRequestError('当前运行环境不支持文件上传', 'FILE_UPLOAD_UNAVAILABLE'))
|
||||
@@ -239,14 +268,14 @@ export const requestNativeFileChunk = ({ uploadId, chunkIndex, chunkMd5, filePat
|
||||
tenantId: runtimeConfig.tenantId,
|
||||
...(token ? { Authorization: `Bearer ${token}` } : {})
|
||||
},
|
||||
timeout: REQUEST_TIMEOUT_MS,
|
||||
timeout: FILE_UPLOAD_TIMEOUT_MS,
|
||||
success: (upload) => {
|
||||
if (upload.statusCode !== 200) {
|
||||
rejectOnce(createRequestError(`文件分片上传失败(HTTP ${upload.statusCode})`, 'HTTP_ERROR', { httpStatus: upload.statusCode }))
|
||||
rejectOnce(rejectAuthenticatedUploadStatus(upload.statusCode))
|
||||
return
|
||||
}
|
||||
try {
|
||||
parseStrictUploadResponse(upload.data, false)
|
||||
unwrapAuthenticatedUploadResponse(upload.data)
|
||||
resolveOnce(null)
|
||||
} catch (error) {
|
||||
rejectOnce(error)
|
||||
@@ -324,7 +353,7 @@ export const requestBrowserFileChunk = ({ uploadId, chunkIndex, chunkMd5, file }
|
||||
timeoutId = setTimeout(() => {
|
||||
abortController.abort()
|
||||
rejectOnce(createRequestError('文件分片上传超时', 'REQUEST_TIMEOUT'))
|
||||
}, REQUEST_TIMEOUT_MS)
|
||||
}, FILE_UPLOAD_TIMEOUT_MS)
|
||||
globalThis.fetch(`${runtimeConfig.baseUrl}/genealogy/app/files/resumable/chunk`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -336,9 +365,9 @@ export const requestBrowserFileChunk = ({ uploadId, chunkIndex, chunkMd5, file }
|
||||
signal: abortController.signal
|
||||
}).then(async (response) => {
|
||||
if (response.status !== 200) {
|
||||
throw createRequestError(`文件分片上传失败(HTTP ${response.status})`, 'HTTP_ERROR', { httpStatus: response.status })
|
||||
throw rejectAuthenticatedUploadStatus(response.status)
|
||||
}
|
||||
parseStrictUploadResponse(await response.text(), false)
|
||||
unwrapAuthenticatedUploadResponse(await response.text())
|
||||
resolveOnce(null)
|
||||
}).catch((error) => {
|
||||
if (settled) return
|
||||
|
||||
Reference in New Issue
Block a user