All checks were successful
Build and Deploy Front / deploy (push) Successful in 6m4s
Co-authored-by: Cursor <cursoragent@cursor.com>
139 lines
4.5 KiB
TypeScript
139 lines
4.5 KiB
TypeScript
/**
|
||
* OMS admin 接口通用:与 /order 同源(开发环境走 Vite 代理路径前缀)
|
||
*/
|
||
import type { AxiosError } from 'axios'
|
||
import { axiosInstance } from '@/api/rbac'
|
||
import { resolveAdminApiPath, resolveAdminAssetOrigin } from '@/utils/adminApi'
|
||
|
||
/** 从 axios / 业务响应中提取可读错误信息 */
|
||
export function extractOmsRequestError(err: unknown): string {
|
||
const axiosErr = err as AxiosError<{ msg?: string; Msg?: string; message?: string; Message?: string }>
|
||
const data = axiosErr?.response?.data
|
||
if (data && typeof data === 'object') {
|
||
const msg = data.msg ?? data.Msg ?? data.message ?? data.Message
|
||
if (msg != null && String(msg).trim()) {
|
||
return String(msg).trim()
|
||
}
|
||
}
|
||
return err instanceof Error ? err.message : String(err)
|
||
}
|
||
|
||
export function omsUrl(path: string): string {
|
||
return resolveAdminApiPath(path)
|
||
}
|
||
|
||
export function assertBusinessOk(body: any) {
|
||
if (body == null) throw new Error('空响应')
|
||
const code = body.code ?? body.Code
|
||
const ok =
|
||
code === undefined ||
|
||
code === null ||
|
||
code === 0 ||
|
||
code === 200 ||
|
||
code === '0' ||
|
||
code === '200' ||
|
||
body.success === true ||
|
||
body.Success === true
|
||
if (!ok) {
|
||
throw new Error(body.msg ?? body.Msg ?? body.message ?? body.Message ?? '请求失败')
|
||
}
|
||
}
|
||
|
||
/** 分页列表载荷(兼容 camel/Pascal、items/list/records) */
|
||
export function parseListPayload(resBody: any): { items: any[]; total: number } {
|
||
assertBusinessOk(resBody)
|
||
const inner = resBody.data ?? resBody.Data ?? resBody
|
||
if (Array.isArray(inner)) {
|
||
return { items: inner, total: inner.length }
|
||
}
|
||
const payload =
|
||
inner && typeof inner === 'object' && !Array.isArray(inner) ? inner : resBody
|
||
|
||
const items =
|
||
payload.items ??
|
||
payload.Items ??
|
||
payload.list ??
|
||
payload.List ??
|
||
payload.records ??
|
||
payload.Records ??
|
||
payload.rows ??
|
||
payload.Rows ??
|
||
payload.result ??
|
||
payload.Result ??
|
||
(Array.isArray(payload.data) ? payload.data : undefined) ??
|
||
(Array.isArray(payload.Data) ? payload.Data : undefined) ??
|
||
[]
|
||
|
||
const totalRaw =
|
||
payload.total ??
|
||
payload.Total ??
|
||
payload.totalCount ??
|
||
payload.TotalCount ??
|
||
payload.count ??
|
||
payload.Count
|
||
|
||
const list = Array.isArray(items) ? items : []
|
||
const total =
|
||
typeof totalRaw === 'number'
|
||
? totalRaw
|
||
: totalRaw != null && totalRaw !== ''
|
||
? Number(totalRaw)
|
||
: list.length
|
||
|
||
return { items: list, total: Number.isFinite(total) ? total : list.length }
|
||
}
|
||
|
||
export function unwrapData<T = any>(resBody: any): T {
|
||
assertBusinessOk(resBody)
|
||
const nested = resBody.data ?? resBody.Data
|
||
if (nested !== undefined && nested !== null) {
|
||
return nested as T
|
||
}
|
||
// 部分 OMS 接口直接返回业务 DTO,无 data 包裹
|
||
return resBody as T
|
||
}
|
||
|
||
export async function omsGet<T = any>(path: string, params?: Record<string, any>): Promise<T> {
|
||
const res = await axiosInstance.get(omsUrl(path), { params })
|
||
return unwrapData<T>(res.data)
|
||
}
|
||
|
||
export async function omsPost<T = any>(path: string, body?: Record<string, any>): Promise<T> {
|
||
const res = await axiosInstance.post(omsUrl(path), body ?? {})
|
||
return unwrapData<T>(res.data)
|
||
}
|
||
|
||
/**
|
||
* COS 绝对地址 → 同源 `/__cos__/<host>/...`(开发 Vite / 生产 nginx 反代)。
|
||
* cos.ideship.com 等自定义域名 https 不可用,直链 http 又会在 https 页面触发混合内容。
|
||
*/
|
||
function toCosSameOriginProxyUrl(absoluteUrl: string): string | null {
|
||
try {
|
||
const u = new URL(absoluteUrl)
|
||
const host = u.hostname.toLowerCase()
|
||
if (!host.startsWith('cos.') && !host.includes('myqcloud.com')) return null
|
||
return `/__cos__/${u.host}${u.pathname}${u.search}`
|
||
} catch {
|
||
return null
|
||
}
|
||
}
|
||
|
||
/** 将 /uploads/... 相对路径转为可访问 URL(同源相对,或 VITE_API_ASSET_ORIGIN 绝对前缀) */
|
||
export function resolveOmsAssetUrl(path?: string | null): string {
|
||
if (!path) return ''
|
||
const value = path.trim()
|
||
if (!value) return ''
|
||
if (/^https?:\/\//i.test(value) || value.startsWith('data:') || value.startsWith('blob:')) {
|
||
const cosProxied = toCosSameOriginProxyUrl(value)
|
||
if (cosProxied) return cosProxied
|
||
// 纠正常见错链:api.nanafa.com/uploads → api.nanafa.com/admin/uploads
|
||
return value.replace(
|
||
/^(https?:\/\/api\.(?:nanafa|ideship)\.com)\/uploads\//i,
|
||
'$1/admin/uploads/',
|
||
)
|
||
}
|
||
const origin = resolveAdminAssetOrigin()
|
||
const rel = value.startsWith('/') ? value : `/${value}`
|
||
return origin ? `${origin}${rel}` : rel
|
||
}
|