feat: 完成前端业务闭环与后端联调

This commit is contained in:
2026-08-24 23:37:56 +08:00
parent c59e36f933
commit 9ad572907b
175 changed files with 10711 additions and 1740 deletions
+42 -13
View File
@@ -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