Add product/channel billing condition editors, place-order additional services, billing simulator, forecast-to-inbound fee delta and margin warnings, insurance-aware claim tips, OMS preset dict seeds, and related AI skills/system pages. Co-authored-by: Cursor <cursoragent@cursor.com>
213 lines
7.5 KiB
TypeScript
213 lines
7.5 KiB
TypeScript
import { axiosInstance } from '@/api/rbac'
|
||
import { assertBusinessOk, omsUrl, parseListPayload, unwrapData } from '@/api/oms/common'
|
||
|
||
/** 从 HTTP 响应体取出详情对象:标准 { code, data } 或根级直接返回实体 */
|
||
function extractSalesProductDetailFromBody(body: any): any {
|
||
if (body == null || typeof body !== 'object') return undefined
|
||
const nested = body.data ?? body.Data
|
||
if (nested != null && typeof nested === 'object') return nested
|
||
const looksLikeProduct =
|
||
body.productCode != null ||
|
||
body.ProductCode != null ||
|
||
(body.id != null && (body.productName != null || body.ProductName != null))
|
||
if (looksLikeProduct) return body
|
||
return undefined
|
||
}
|
||
|
||
/** 兼容 detail 返回 data 为空、或多层 { data: { ... } } 包裹 */
|
||
function normalizeSalesProductDetailPayload(raw: unknown): Record<string, any> | undefined {
|
||
if (raw == null || typeof raw !== 'object') return undefined
|
||
let cur: any = raw
|
||
for (let i = 0; i < 5; i++) {
|
||
if (
|
||
cur.productCode != null ||
|
||
cur.ProductCode != null ||
|
||
(cur.id != null && (cur.productName != null || cur.ProductName != null))
|
||
) {
|
||
return cur as Record<string, any>
|
||
}
|
||
const next = cur.data ?? cur.Data ?? cur.result ?? cur.Result ?? cur.item ?? cur.Item
|
||
if (next == null || typeof next !== 'object') break
|
||
cur = next
|
||
}
|
||
return typeof cur === 'object' ? (cur as Record<string, any>) : undefined
|
||
}
|
||
|
||
export async function getSalesProductList(params: {
|
||
page?: number
|
||
pageSize?: number
|
||
productGroup?: string
|
||
keyword?: string
|
||
isActive?: number
|
||
}) {
|
||
const res = await axiosInstance.get(omsUrl('/channel/sales-product/list'), { params })
|
||
return parseListPayload(res.data)
|
||
}
|
||
|
||
export async function getSalesProductDetail(id: number | string) {
|
||
const res = await axiosInstance.get(omsUrl('/channel/sales-product/detail'), { params: { id } })
|
||
const body = res.data
|
||
assertBusinessOk(body)
|
||
const raw = extractSalesProductDetailFromBody(body)
|
||
return normalizeSalesProductDetailPayload(raw) ?? (raw && typeof raw === 'object' ? raw : undefined)
|
||
}
|
||
|
||
export async function createSalesProduct(body: Record<string, any>) {
|
||
const res = await axiosInstance.post(omsUrl('/channel/sales-product/create'), body)
|
||
assertBusinessOk(res.data)
|
||
}
|
||
|
||
export async function updateSalesProduct(body: Record<string, any> & { id: number | string }) {
|
||
const res = await axiosInstance.post(omsUrl('/channel/sales-product/update'), body)
|
||
assertBusinessOk(res.data)
|
||
}
|
||
|
||
export async function deleteSalesProduct(id: number | string) {
|
||
const res = await axiosInstance.post(omsUrl('/channel/sales-product/delete'), { id })
|
||
assertBusinessOk(res.data)
|
||
}
|
||
|
||
/** 对客说明 */
|
||
export async function saveSalesProductCustomerGuide(body: {
|
||
salesProductId: number | string
|
||
customerGuideMarkdown?: string
|
||
}) {
|
||
const res = await axiosInstance.post(omsUrl('/channel/sales-product/customer-guide/save'), body)
|
||
assertBusinessOk(res.data)
|
||
}
|
||
|
||
/** 卖价 */
|
||
export async function getSalesProductPriceList(salesProductId: number | string) {
|
||
const res = await axiosInstance.get(omsUrl('/channel/sales-product/price/list'), {
|
||
params: { salesProductId },
|
||
})
|
||
const { items } = parseListPayload(res.data)
|
||
return items ?? []
|
||
}
|
||
|
||
export async function createSalesProductPrice(body: Record<string, any>) {
|
||
const res = await axiosInstance.post(omsUrl('/channel/sales-product/price/create'), body)
|
||
assertBusinessOk(res.data)
|
||
return unwrapData<any>(res.data)
|
||
}
|
||
|
||
export async function updateSalesProductPrice(body: Record<string, any> & { id: number | string }) {
|
||
const res = await axiosInstance.post(omsUrl('/channel/sales-product/price/update'), body)
|
||
assertBusinessOk(res.data)
|
||
}
|
||
|
||
export async function deleteSalesProductPrice(id: number | string) {
|
||
const res = await axiosInstance.post(omsUrl('/channel/sales-product/price/delete'), { id })
|
||
assertBusinessOk(res.data)
|
||
}
|
||
|
||
export async function toggleSalesProductPriceStatus(id: number | string, status: number) {
|
||
const res = await axiosInstance.post(omsUrl('/channel/sales-product/price/toggleStatus'), { id, status })
|
||
assertBusinessOk(res.data)
|
||
}
|
||
|
||
/** allocation/get:兼容 { code, data: { scheme, providers } }、或与 code 同级的根字段、或无 code 仅实体 */
|
||
function extractSalesProductAllocationFromBody(body: any): Record<string, any> | undefined {
|
||
if (body == null || typeof body !== 'object') return undefined
|
||
const nested = body.data ?? body.Data
|
||
const looksLikeAllocation = (o: any) =>
|
||
o != null &&
|
||
typeof o === 'object' &&
|
||
!Array.isArray(o) &&
|
||
(Array.isArray(o.providers) ||
|
||
Array.isArray(o.Providers) ||
|
||
o.scheme != null ||
|
||
o.Scheme != null ||
|
||
o.salesProductId != null ||
|
||
o.SalesProductId != null)
|
||
if (looksLikeAllocation(nested)) return nested as Record<string, any>
|
||
if (looksLikeAllocation(body)) return body as Record<string, any>
|
||
return undefined
|
||
}
|
||
|
||
/** 排货 */
|
||
export async function getSalesProductAllocation(salesProductId: number | string) {
|
||
const res = await axiosInstance.get(omsUrl('/channel/sales-product/allocation/get'), {
|
||
params: { salesProductId },
|
||
})
|
||
const body = res.data
|
||
assertBusinessOk(body)
|
||
return extractSalesProductAllocationFromBody(body)
|
||
}
|
||
|
||
export async function saveSalesProductAllocation(body: Record<string, any>) {
|
||
const res = await axiosInstance.post(omsUrl('/channel/sales-product/allocation/save'), body)
|
||
assertBusinessOk(res.data)
|
||
}
|
||
|
||
/** 发布 */
|
||
export async function getSalesProductPublishPriceList(params: {
|
||
salesProductId: number | string
|
||
page?: number
|
||
pageSize?: number
|
||
}) {
|
||
const res = await axiosInstance.get(omsUrl('/channel/sales-product/publish/price/list'), { params })
|
||
return parseListPayload(res.data)
|
||
}
|
||
|
||
export async function getSalesProductPublishRulesList(params: {
|
||
salesProductId: number | string
|
||
page?: number
|
||
pageSize?: number
|
||
}) {
|
||
const res = await axiosInstance.get(omsUrl('/channel/sales-product/publish/rules/list'), { params })
|
||
return parseListPayload(res.data)
|
||
}
|
||
|
||
/** 预览即将发布的快照(不落库),用于发布弹窗与发布记录核对 */
|
||
export async function getSalesProductPublishPreview(params: {
|
||
salesProductId: number | string
|
||
operatorType: 'rules' | 'price' | string
|
||
}) {
|
||
const res = await axiosInstance.get(omsUrl('/channel/sales-product/publish/preview'), { params })
|
||
return unwrapData<Record<string, unknown>>(res.data)
|
||
}
|
||
|
||
export async function publishSalesProduct(body: {
|
||
salesProductId: number | string
|
||
operatorType: string
|
||
description?: string
|
||
/** 价格发布必填:AI 风险分析报告原文 */
|
||
aiAnalysisReport?: string
|
||
}) {
|
||
const res = await axiosInstance.post(omsUrl('/channel/sales-product/publish'), body)
|
||
assertBusinessOk(res.data)
|
||
}
|
||
|
||
export async function rollbackSalesProductPublish(body: {
|
||
salesProductId: number | string
|
||
recordId: number | string
|
||
operatorType: string
|
||
}) {
|
||
const res = await axiosInstance.post(omsUrl('/channel/sales-product/publish/rollback'), body)
|
||
assertBusinessOk(res.data)
|
||
}
|
||
|
||
/** 销售产品计费模拟(基于当前库内卖价) */
|
||
export async function postSalesProductBillingSimulate(body: {
|
||
salesProductId: number | string
|
||
countryCode: string
|
||
province?: string
|
||
city?: string
|
||
zipCode?: string
|
||
length: number
|
||
width: number
|
||
height: number
|
||
actualWeight: number
|
||
cargoAttribute?: string
|
||
warehouseId?: number | null
|
||
additionalServices?: string[]
|
||
declaredSum?: number | null
|
||
declaredCurrency?: string
|
||
isRemote?: boolean | null
|
||
priceTierKey?: string
|
||
}) {
|
||
const res = await axiosInstance.post(omsUrl('/channel/sales-product/billing/simulate'), body)
|
||
return unwrapData<Record<string, any>>(res.data)
|
||
}
|