front/src/api/order/index.ts
2026-07-02 15:52:09 +08:00

506 lines
15 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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
}