fix(withdrawal): 修复提现详情收款二维码使用公网URL导致鉴权失败加载错误

- backend: withdrawal presenter 增加 privateCertificateURLs 兼容转换历史公网 payment-cert 路径
- frontend: AuthImage 与 fetchFileBlobByURL 兼容私有凭证 key,后台上下文通过管理员私有对象接口安全加载
- frontend: 按对象接口路径与 key 前缀精确判定,避免外链带 ?key= 被误代理到后台私有对象接口
- tests: 补充 TestPrivateCertificateURLs 单元测试
This commit is contained in:
yml2213
2026-09-16 14:39:32 +08:00
parent 3465475756
commit 5f5a57b81f
4 changed files with 57 additions and 6 deletions
+8
View File
@@ -57,6 +57,14 @@ export async function fetchFileBlobByURL(fileURL: string) {
if (apiPath.startsWith('/api/')) {
apiPath = apiPath.slice(4)
}
// 历史收款凭证存的是公开 URL,但 payment-cert 已转为私有,需精确识别 key 前缀后改走私有对象接口。
if (apiPath.startsWith('/public/files/object')) {
const queryIndex = apiPath.indexOf('?')
const search = new URLSearchParams(queryIndex >= 0 ? apiPath.slice(queryIndex) : '')
if ((search.get('key') || '').startsWith('payment-cert/')) {
apiPath = `/files/object${queryIndex >= 0 ? apiPath.slice(queryIndex) : ''}`
}
}
const { data } = await apiClient.get<Blob>(apiPath, {
responseType: 'blob',
})
@@ -67,17 +67,26 @@ function withAuthImageLoadSlot<T>(task: () => Promise<T>): Promise<T> {
})
}
function extractObjectKey(value: string) {
function tryParseURL(value: string): URL | null {
try {
const parsed = new URL(value, window.location.origin)
return parsed.searchParams.get('key') || ''
return new URL(value, window.location.origin)
} catch {
return ''
return null
}
}
function extractObjectKey(value: string) {
return tryParseURL(value)?.searchParams.get('key') || ''
}
function shouldFetchWithAuth(value: string) {
return value.includes('/api/files/object') || value.includes('/api/admin/files/object')
const parsed = tryParseURL(value)
if (!parsed) return false
// 公开对象接口仅对已转私有的收款凭证做鉴权兜底,其余公开资源维持直连,避免外链被误代理。
if (parsed.pathname.endsWith('/public/files/object')) {
return (parsed.searchParams.get('key') || '').startsWith('payment-cert/')
}
return parsed.pathname.endsWith('/files/object')
}
function shouldFetchAsAdmin(value: string) {