506 lines
15 KiB
TypeScript
506 lines
15 KiB
TypeScript
/**
|
||
* OMS 订单接口(Swagger:OMS-Order,与 admin 同源)
|
||
*/
|
||
import { axiosInstance } from '@/api/rbac'
|
||
import { resolveAdminApiPath } from '@/utils/adminApi'
|
||
|
||
function orderUrl(path: string): string {
|
||
return resolveAdminApiPath(path)
|
||
}
|
||
|
||
export interface OrderListRequest {
|
||
page: number
|
||
pageSize: number
|
||
/** all 或各状态英文 key,与文档一致 */
|
||
status?: string
|
||
keyword?: string
|
||
createTimeStart?: string
|
||
createTimeEnd?: string
|
||
}
|
||
|
||
export interface OrderListResult {
|
||
items: any[]
|
||
total: number
|
||
}
|
||
|
||
export interface OrderStatusCountsResult {
|
||
total: number
|
||
counts: Record<string, number>
|
||
}
|
||
|
||
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 ?? '请求失败')
|
||
}
|
||
}
|
||
|
||
/** 取出分页载荷(兼容 data 包裹或顶层直出、items/list/records、camel/Pascal) */
|
||
function parseOrderListPayload(resBody: any): OrderListResult {
|
||
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 ??
|
||
(Array.isArray(payload) ? payload : [])
|
||
|
||
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 : 0,
|
||
}
|
||
}
|
||
|
||
/** 分页查询订单列表 POST /order/list */
|
||
export async function postOrderList(params: OrderListRequest): Promise<OrderListResult> {
|
||
const body: OrderListRequest = {
|
||
page: params.page,
|
||
pageSize: params.pageSize,
|
||
status: params.status ?? 'all',
|
||
keyword: params.keyword ?? '',
|
||
}
|
||
if (params.createTimeStart?.trim()) {
|
||
body.createTimeStart = params.createTimeStart.trim()
|
||
}
|
||
if (params.createTimeEnd?.trim()) {
|
||
body.createTimeEnd = params.createTimeEnd.trim()
|
||
}
|
||
const res = await axiosInstance.post(orderUrl('/order/list'), body)
|
||
return parseOrderListPayload(res.data)
|
||
}
|
||
|
||
/** 各状态订单数量聚合 GET /order/status-counts(单次查询,非逐状态分页) */
|
||
export async function getOrderStatusCounts(): Promise<OrderStatusCountsResult> {
|
||
const res = await axiosInstance.get(orderUrl('/order/status-counts'))
|
||
assertBusinessOk(res.data)
|
||
const inner = res.data?.data ?? res.data?.Data ?? res.data ?? {}
|
||
const totalRaw = inner.total ?? inner.Total ?? 0
|
||
const counts = inner.counts ?? inner.Counts ?? {}
|
||
return {
|
||
total: Number(totalRaw) || 0,
|
||
counts: typeof counts === 'object' && counts !== null ? counts : {},
|
||
}
|
||
}
|
||
|
||
/** 订单详情 GET /order/detail?id= */
|
||
export async function getOrderDetail(id: number | string): Promise<any> {
|
||
const res = await axiosInstance.get(orderUrl('/order/detail'), {
|
||
params: { id },
|
||
})
|
||
assertBusinessOk(res.data)
|
||
return res.data?.data ?? res.data?.Data ?? res.data
|
||
}
|
||
|
||
export interface OrderIdRequest {
|
||
id: number | string
|
||
}
|
||
|
||
export interface OrderExportRequest {
|
||
status?: string
|
||
keyword?: string
|
||
createTimeStart?: string
|
||
createTimeEnd?: string
|
||
}
|
||
|
||
export interface OrderPrintResult {
|
||
printUrl?: string
|
||
}
|
||
|
||
export interface OrderExportResult {
|
||
downloadUrl?: string
|
||
expireAt?: string
|
||
}
|
||
|
||
async function postOrderById(path: string, body: OrderIdRequest): Promise<void> {
|
||
const res = await axiosInstance.post(orderUrl(path), { id: body.id })
|
||
assertBusinessOk(res.data)
|
||
}
|
||
|
||
/** POST /order/pay */
|
||
export function postOrderPay(body: OrderIdRequest) {
|
||
return postOrderById('/order/pay', body)
|
||
}
|
||
|
||
/** POST /order/pre-declare */
|
||
export function postOrderPreDeclare(body: OrderIdRequest) {
|
||
return postOrderById('/order/pre-declare', body)
|
||
}
|
||
|
||
/** POST /order/intercept */
|
||
export function postOrderIntercept(body: OrderIdRequest) {
|
||
return postOrderById('/order/intercept', body)
|
||
}
|
||
|
||
/** POST /order/cancel-intercept */
|
||
export function postOrderCancelIntercept(body: OrderIdRequest) {
|
||
return postOrderById('/order/cancel-intercept', body)
|
||
}
|
||
|
||
/** POST /order/delete */
|
||
export function postOrderDelete(body: OrderIdRequest) {
|
||
return postOrderById('/order/delete', body)
|
||
}
|
||
|
||
export interface OrderTrackUpsertResult {
|
||
skipped?: boolean
|
||
skipReason?: string
|
||
success?: boolean
|
||
url?: string
|
||
requestJson?: string
|
||
curlCommand?: string
|
||
statusCode?: number
|
||
responseBody?: string
|
||
}
|
||
|
||
/** POST /order/track-upsert-small-bag → 同步调用轨迹 UpsertSmallBag(联调/手动触发) */
|
||
export async function postOrderTrackUpsertSmallBag(body: OrderIdRequest): Promise<OrderTrackUpsertResult> {
|
||
const res = await axiosInstance.post(orderUrl('/order/track-upsert-small-bag'), { id: body.id })
|
||
assertBusinessOk(res.data)
|
||
const d = res.data?.data ?? res.data?.Data ?? res.data ?? {}
|
||
return {
|
||
skipped: d.skipped ?? d.Skipped,
|
||
skipReason: d.skipReason ?? d.SkipReason,
|
||
success: d.success ?? d.Success,
|
||
url: d.url ?? d.Url,
|
||
requestJson: d.requestJson ?? d.RequestJson,
|
||
curlCommand: d.curlCommand ?? d.CurlCommand,
|
||
statusCode: d.statusCode ?? d.StatusCode,
|
||
responseBody: d.responseBody ?? d.ResponseBody,
|
||
}
|
||
}
|
||
|
||
export interface OrderDownstreamForwardResult {
|
||
success?: boolean
|
||
supplierCode?: string
|
||
url?: string
|
||
requestJson?: string
|
||
statusCode?: number
|
||
responseBody?: string
|
||
}
|
||
|
||
/** POST /order/downstream-forward-create-one → 后端查订单表组装并转发 createOrderOne */
|
||
export async function postOrderDownstreamForwardCreateOne(
|
||
body: OrderIdRequest
|
||
): Promise<OrderDownstreamForwardResult> {
|
||
const res = await axiosInstance.post(orderUrl('/order/downstream-forward-create-one'), { id: body.id })
|
||
assertBusinessOk(res.data)
|
||
const d = res.data?.data ?? res.data?.Data ?? res.data ?? {}
|
||
return {
|
||
success: d.success ?? d.Success,
|
||
supplierCode: d.supplierCode ?? d.SupplierCode,
|
||
url: d.url ?? d.Url,
|
||
requestJson: d.requestJson ?? d.RequestJson,
|
||
statusCode: d.statusCode ?? d.StatusCode,
|
||
responseBody: d.responseBody ?? d.ResponseBody,
|
||
}
|
||
}
|
||
|
||
/** POST /order/print → data.printUrl */
|
||
export async function postOrderPrint(body: OrderIdRequest): Promise<OrderPrintResult> {
|
||
const res = await axiosInstance.post(orderUrl('/order/print'), { id: body.id })
|
||
assertBusinessOk(res.data)
|
||
const d = res.data?.data ?? res.data?.Data ?? {}
|
||
return {
|
||
printUrl: d.printUrl ?? d.PrintUrl ?? '',
|
||
}
|
||
}
|
||
|
||
/** POST /order/export → data.downloadUrl、expireAt */
|
||
export async function postOrderExport(params: OrderExportRequest): Promise<OrderExportResult> {
|
||
const res = await axiosInstance.post(orderUrl('/order/export'), {
|
||
status: params.status ?? 'all',
|
||
keyword: params.keyword ?? '',
|
||
createTimeStart: params.createTimeStart ?? '',
|
||
createTimeEnd: params.createTimeEnd ?? '',
|
||
})
|
||
assertBusinessOk(res.data)
|
||
const d = res.data?.data ?? res.data?.Data ?? {}
|
||
return {
|
||
downloadUrl: d.downloadUrl ?? d.DownloadUrl ?? '',
|
||
expireAt: d.expireAt ?? d.ExpireAt ?? '',
|
||
}
|
||
}
|
||
|
||
/** POST /order/price-check → data.items + data.failures */
|
||
export interface PriceCheckRequest {
|
||
destinationCountry: string
|
||
shippingMethod: string
|
||
cargoWeight: string
|
||
length: string
|
||
width: string
|
||
height: string
|
||
/** @deprecated 请使用 cargoTypes */
|
||
cargoType?: string
|
||
/** 货物类型(多选,字典 cargo_attribute 编码) */
|
||
cargoTypes?: string[]
|
||
/** 仓库主键(多选;为空则遍历全部仓库) */
|
||
warehouseIds?: number[]
|
||
destinationProvince: string
|
||
postalCode: string
|
||
}
|
||
|
||
export interface PriceCheckFailure {
|
||
productId?: number
|
||
productCode?: string
|
||
productName?: string
|
||
step?: string
|
||
reason?: string
|
||
cargoTypeName?: string
|
||
warehouseName?: string
|
||
}
|
||
|
||
function normalizePriceCheckFailure(raw: unknown): PriceCheckFailure | null {
|
||
if (raw == null) return null
|
||
if (typeof raw === 'string') {
|
||
const text = raw.trim()
|
||
return text ? { reason: text } : null
|
||
}
|
||
if (typeof raw !== 'object') return null
|
||
const row = raw as Record<string, unknown>
|
||
const reason = String(
|
||
row.reason ?? row.Reason ?? row.message ?? row.Message ?? row.msg ?? row.Msg ?? '',
|
||
).trim()
|
||
if (!reason && row.step == null && row.Step == null) return null
|
||
return {
|
||
productId: (row.productId ?? row.ProductId) as number | undefined,
|
||
productCode: (row.productCode ?? row.ProductCode) as string | undefined,
|
||
productName: (row.productName ?? row.ProductName) as string | undefined,
|
||
step: (row.step ?? row.Step) as string | undefined,
|
||
reason: reason || undefined,
|
||
cargoTypeName: (row.cargoTypeName ?? row.CargoTypeName) as string | undefined,
|
||
warehouseName: (row.warehouseName ?? row.WarehouseName) as string | undefined,
|
||
}
|
||
}
|
||
|
||
/** 解析查价响应(兼容 data 包裹 / 直出、camel/Pascal、failures 在根或 data 内) */
|
||
export function parsePriceCheckResponse(resBody: any): {
|
||
items: any[]
|
||
failures: PriceCheckFailure[]
|
||
} {
|
||
assertBusinessOk(resBody)
|
||
const root = resBody ?? {}
|
||
const inner = root.data ?? root.Data ?? root
|
||
|
||
if (Array.isArray(inner)) {
|
||
return { items: inner, failures: [] }
|
||
}
|
||
|
||
const payload =
|
||
inner && typeof inner === 'object' && !Array.isArray(inner) ? inner : root
|
||
|
||
const items =
|
||
(payload as any).items ??
|
||
(payload as any).Items ??
|
||
(payload as any).list ??
|
||
(payload as any).List ??
|
||
[]
|
||
|
||
const rawFailures =
|
||
(payload as any).failures ??
|
||
(payload as any).Failures ??
|
||
root.failures ??
|
||
root.Failures ??
|
||
[]
|
||
|
||
const failureRows = Array.isArray(rawFailures)
|
||
? rawFailures
|
||
: rawFailures && typeof rawFailures === 'object'
|
||
? [rawFailures]
|
||
: []
|
||
|
||
const failures = failureRows
|
||
.map(normalizePriceCheckFailure)
|
||
.filter((f): f is PriceCheckFailure => f != null && Boolean(f.reason?.trim()))
|
||
|
||
const itemList = Array.isArray(items) ? items : []
|
||
|
||
if (!itemList.length && !failures.length) {
|
||
const msg = String(
|
||
root.msg ??
|
||
root.Msg ??
|
||
root.message ??
|
||
root.Message ??
|
||
(payload as any).msg ??
|
||
(payload as any).Msg ??
|
||
(payload as any).message ??
|
||
(payload as any).Message ??
|
||
'',
|
||
).trim()
|
||
if (msg) {
|
||
failures.push({ reason: msg })
|
||
}
|
||
}
|
||
|
||
return { items: itemList, failures }
|
||
}
|
||
|
||
export async function postOrderPriceCheck(params: PriceCheckRequest): Promise<{
|
||
items: any[]
|
||
failures: PriceCheckFailure[]
|
||
}> {
|
||
const res = await axiosInstance.post(orderUrl('/order/price-check'), params)
|
||
return parsePriceCheckResponse(res.data)
|
||
}
|
||
|
||
/** GET /order/template/list */
|
||
export async function getOrderTemplateList(params: {
|
||
page?: number
|
||
pageSize?: number
|
||
type?: 'standard' | 'custom' | string
|
||
keyword?: string
|
||
}): Promise<OrderListResult> {
|
||
const res = await axiosInstance.get(orderUrl('/order/template/list'), {
|
||
params: {
|
||
page: params.page ?? 1,
|
||
pageSize: params.pageSize ?? 50,
|
||
type: params.type ?? '',
|
||
keyword: params.keyword ?? '',
|
||
},
|
||
})
|
||
return parseOrderListPayload(res.data)
|
||
}
|
||
|
||
/** GET /order/template/download?templateId= → Excel 文件流 */
|
||
export async function downloadOrderTemplate(templateId: number, fileName: string) {
|
||
const res = await axiosInstance.get(orderUrl('/order/template/download'), {
|
||
params: { templateId },
|
||
responseType: 'blob',
|
||
})
|
||
const blob = res.data as Blob
|
||
const ct = (res.headers['content-type'] || '').toString()
|
||
if (ct.includes('application/json')) {
|
||
const text = await blob.text()
|
||
const json = JSON.parse(text)
|
||
throw new Error(json.msg ?? json.message ?? json.Message ?? '下载失败')
|
||
}
|
||
const url = URL.createObjectURL(blob)
|
||
const a = document.createElement('a')
|
||
a.href = url
|
||
a.download = fileName.endsWith('.xlsx') ? fileName : `${fileName}.xlsx`
|
||
a.style.display = 'none'
|
||
document.body.appendChild(a)
|
||
a.click()
|
||
document.body.removeChild(a)
|
||
URL.revokeObjectURL(url)
|
||
}
|
||
|
||
/** POST /order/upload multipart: file + templateId */
|
||
export async function postOrderUpload(file: File, templateId: number): Promise<{ taskId?: number }> {
|
||
const form = new FormData()
|
||
form.append('file', file)
|
||
form.append('templateId', String(templateId))
|
||
const res = await axiosInstance.post(orderUrl('/order/upload'), form)
|
||
assertBusinessOk(res.data)
|
||
const d = res.data?.data ?? res.data?.Data ?? {}
|
||
const taskId = d.taskId ?? d.TaskId
|
||
return { taskId: taskId != null ? Number(taskId) : undefined }
|
||
}
|
||
|
||
/** GET /order/task/list */
|
||
export async function getOrderTaskList(params: {
|
||
page?: number
|
||
pageSize?: number
|
||
status?: string
|
||
keyword?: string
|
||
}): Promise<OrderListResult> {
|
||
const res = await axiosInstance.get(orderUrl('/order/task/list'), {
|
||
params: {
|
||
page: params.page ?? 1,
|
||
pageSize: params.pageSize ?? 20,
|
||
status: params.status ?? '',
|
||
keyword: params.keyword ?? '',
|
||
},
|
||
})
|
||
return parseOrderListPayload(res.data)
|
||
}
|
||
|
||
/** GET /order/task/detail?taskId= */
|
||
export async function getOrderTaskDetail(taskId: number | string): Promise<any> {
|
||
const res = await axiosInstance.get(orderUrl('/order/task/detail'), {
|
||
params: { taskId },
|
||
})
|
||
assertBusinessOk(res.data)
|
||
return res.data?.data ?? res.data?.Data ?? {}
|
||
}
|
||
|
||
/** POST /order/parcel/create */
|
||
export async function postOrderParcelCreate(body: Record<string, any>): Promise<any> {
|
||
const res = await axiosInstance.post(orderUrl('/order/parcel/create'), body)
|
||
assertBusinessOk(res.data)
|
||
return res.data?.data ?? res.data?.Data ?? {}
|
||
}
|
||
|
||
/** POST /order/update */
|
||
export async function postOrderUpdate(body: Record<string, any>): Promise<any> {
|
||
const res = await axiosInstance.post(orderUrl('/order/update'), body)
|
||
assertBusinessOk(res.data)
|
||
return res.data?.data ?? res.data?.Data ?? res.data
|
||
}
|
||
|
||
/** POST /order/weight/update */
|
||
export async function postOrderWeightUpdate(body: {
|
||
id: number | string
|
||
weights: Array<{
|
||
type: string
|
||
length?: number
|
||
width?: number
|
||
height?: number
|
||
actualWeight?: number
|
||
}>
|
||
}): Promise<any> {
|
||
const res = await axiosInstance.post(orderUrl('/order/weight/update'), body)
|
||
assertBusinessOk(res.data)
|
||
return res.data?.data ?? res.data?.Data ?? res.data
|
||
}
|
||
|
||
/** POST /order/fee/recalculate-receivable */
|
||
export async function postOrderRecalculateReceivable(body: OrderIdRequest): Promise<any> {
|
||
const res = await axiosInstance.post(orderUrl('/order/fee/recalculate-receivable'), { id: body.id })
|
||
assertBusinessOk(res.data)
|
||
return res.data?.data ?? res.data?.Data ?? res.data
|
||
}
|
||
|
||
/** POST /order/fee/recalculate-payable */
|
||
export async function postOrderRecalculatePayable(body: OrderIdRequest): Promise<any> {
|
||
const res = await axiosInstance.post(orderUrl('/order/fee/recalculate-payable'), { id: body.id })
|
||
assertBusinessOk(res.data)
|
||
return res.data?.data ?? res.data?.Data ?? res.data
|
||
}
|