统一订单单状态模型
This commit is contained in:
@@ -67,7 +67,7 @@ export const merchantApi = {
|
||||
sku: string
|
||||
buyer_reference?: string
|
||||
note?: string
|
||||
fulfillment_status?: 'pending' | 'failed'
|
||||
order_status?: 'paid' | 'ship_failed'
|
||||
}) => request.post('/merchant/orders/test', data).then((r) => r.data.data as CreateTestOrderResult),
|
||||
getDeliveryLink: (orderNo: string) =>
|
||||
request.get(`/merchant/orders/${encodeURIComponent(orderNo)}/delivery-link`).then((r) => r.data.data as DeliveryLinkResult),
|
||||
|
||||
@@ -16,28 +16,21 @@ export const errorCodes: ParamSpec[] = [
|
||||
{ name: '500', type: 'int', desc: '服务端内部错误' },
|
||||
]
|
||||
|
||||
// 订单状态与可履约说明(payment_status / fulfillment_status)。
|
||||
// 订单状态与可履约说明。
|
||||
export const orderStatusTable: ParamSpec[] = [
|
||||
{ name: 'pending', type: 'fulfillment_status', desc: '待履约,可被履约器接单', example: 'can_fulfill=true' },
|
||||
{ name: 'processing', type: 'fulfillment_status', desc: '履约中', example: 'can_fulfill=false' },
|
||||
{ name: 'succeeded', type: 'fulfillment_status', desc: '履约成功', example: 'can_fulfill=false' },
|
||||
{ name: 'failed', type: 'fulfillment_status', desc: '履约失败,可重试', example: 'can_fulfill=true' },
|
||||
{ name: 'cancelled', type: 'fulfillment_status', desc: '已取消', example: 'can_fulfill=false' },
|
||||
]
|
||||
|
||||
export const paymentStatusTable: ParamSpec[] = [
|
||||
{ name: 'pending', type: 'payment_status', desc: '待支付' },
|
||||
{ name: 'paid', type: 'payment_status', desc: '已支付(下单成功即为 paid)' },
|
||||
{ name: 'refunded', type: 'payment_status', desc: '已退款(取消订单后)' },
|
||||
{ name: 'cancelled', type: 'payment_status', desc: '已取消' },
|
||||
{ name: 'pending', type: 'order_status', desc: '待支付(当前无真实支付流程,仅预留)', example: 'can_fulfill=false' },
|
||||
{ name: 'paid', type: 'order_status', desc: '已支付/已扣款,可发货', example: 'can_fulfill=true' },
|
||||
{ name: 'delivering', type: 'order_status', desc: '发货中', example: 'can_fulfill=false' },
|
||||
{ name: 'delivered', type: 'order_status', desc: '已交付', example: 'can_fulfill=false' },
|
||||
{ name: 'ship_failed', type: 'order_status', desc: '发货失败,可重试', example: 'can_fulfill=true' },
|
||||
{ name: 'cancelled', type: 'order_status', desc: '已取消', example: 'can_fulfill=false' },
|
||||
]
|
||||
|
||||
// 订单对象公共字段说明,下单/查询/取消响应共用。
|
||||
const orderFields: ParamSpec[] = [
|
||||
{ name: 'order_no', type: 'string', required: true, desc: '平台订单号' },
|
||||
{ name: 'client_order_no', type: 'string', required: true, desc: '调用方传入的幂等单号' },
|
||||
{ name: 'payment_status', type: 'enum', desc: '支付状态,见「状态说明」' },
|
||||
{ name: 'fulfillment_status', type: 'enum', desc: '履约状态,见「状态说明」' },
|
||||
{ name: 'order_status', type: 'enum', desc: '订单状态,见「状态说明」' },
|
||||
{ name: 'can_fulfill', type: 'bool', desc: '是否允许履约器接单/发货' },
|
||||
{ name: 'cannot_fulfill_reason', type: 'string', desc: 'can_fulfill=false 时的原因' },
|
||||
{ name: 'product.sku', type: 'string', desc: '商品标识,履约以此为准' },
|
||||
@@ -64,8 +57,7 @@ const orderResponseExample = `{
|
||||
"data": {
|
||||
"order_no": "FO20260730000123",
|
||||
"client_order_no": "shop-10001",
|
||||
"payment_status": "paid",
|
||||
"fulfillment_status": "pending",
|
||||
"order_status": "paid",
|
||||
"can_fulfill": true,
|
||||
"cannot_fulfill_reason": "",
|
||||
"product": { "sku": "suit_pink_sheep", "name": "套装-糯粉咩咩" },
|
||||
@@ -134,7 +126,7 @@ const deliverySubmitResponseExample = `{
|
||||
"message": "ok",
|
||||
"data": {
|
||||
"order_no": "FO20260730000123",
|
||||
"status": "processing",
|
||||
"status": "delivering",
|
||||
"message": "已提交上游发货,等待发货结果回传",
|
||||
"provider_order_no": "provider-1",
|
||||
"game_account": {
|
||||
@@ -222,8 +214,7 @@ export const endpoints: EndpointSpec[] = [
|
||||
"order": {
|
||||
"order_no": "FO20260730000123",
|
||||
"client_order_no": "shop-10001",
|
||||
"payment_status": "paid",
|
||||
"fulfillment_status": "pending",
|
||||
"order_status": "paid",
|
||||
"can_fulfill": true
|
||||
}
|
||||
}
|
||||
@@ -234,7 +225,7 @@ export const endpoints: EndpointSpec[] = [
|
||||
],
|
||||
notes: [
|
||||
'client_order_no 是唯一幂等单号,请保证同一商户下不重复。',
|
||||
'下单即扣款(payment_status=paid),余额不足返回 400。',
|
||||
'当前无真实支付流程,下单成功即扣款并进入 order_status=paid,余额不足返回 400。',
|
||||
'同一 client_order_no 重复请求不会重复扣款,返回 idempotent=true。',
|
||||
],
|
||||
},
|
||||
@@ -254,7 +245,7 @@ export const endpoints: EndpointSpec[] = [
|
||||
method: 'POST',
|
||||
path: '/api/client/v1/orders/{order_no}/cancel',
|
||||
title: '取消订单',
|
||||
summary: '取消未履约订单并退款(仅 pending/failed 可取消)',
|
||||
summary: '取消未发货订单并退款(仅 paid/ship_failed 可取消)',
|
||||
scope: 'orders:write',
|
||||
pathParams: [{ name: 'order_no', type: 'string', required: true, desc: '平台订单号', example: 'FO20260730000123' }],
|
||||
bodyParams: [{ name: 'reason', type: 'string', desc: '取消原因(可选)', example: '买家主动取消' }],
|
||||
@@ -262,8 +253,8 @@ export const endpoints: EndpointSpec[] = [
|
||||
responseExample: orderResponseExample,
|
||||
responseFields: orderFields,
|
||||
notes: [
|
||||
'取消成功后 payment_status 变为 refunded,金额退回商户钱包。',
|
||||
'已进入 processing/succeeded 的订单不可取消,返回 400。',
|
||||
'取消成功后 order_status 变为 cancelled,金额退回商户钱包。',
|
||||
'已进入 delivering/delivered 的订单不可取消,返回 400。',
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -131,10 +131,10 @@ export default function Dashboard() {
|
||||
</Col>
|
||||
<Col xs={24} sm={12} lg={8}>
|
||||
<MetricCard
|
||||
title="待履约订单"
|
||||
value={stats?.pending_order_count ?? 0}
|
||||
title="待发货订单"
|
||||
value={stats?.paid_order_count ?? 0}
|
||||
icon={<ClockCircleOutlined />}
|
||||
color={(stats?.pending_order_count ?? 0) > 0 ? '#cf1322' : undefined}
|
||||
color={(stats?.paid_order_count ?? 0) > 0 ? '#cf1322' : undefined}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
@@ -162,26 +162,32 @@ export default function Dashboard() {
|
||||
<Card title="订单状态">
|
||||
<Space direction="vertical" size="middle" style={{ width: '100%' }}>
|
||||
<StatusLine
|
||||
label="已成功"
|
||||
value={stats?.succeeded_order_count ?? 0}
|
||||
total={stats?.order_count ?? 0}
|
||||
color="#52c41a"
|
||||
/>
|
||||
<StatusLine
|
||||
label="待履约"
|
||||
label="待支付"
|
||||
value={stats?.pending_order_count ?? 0}
|
||||
total={stats?.order_count ?? 0}
|
||||
color="#faad14"
|
||||
/>
|
||||
<StatusLine
|
||||
label="履约中"
|
||||
value={stats?.processing_order_count ?? 0}
|
||||
label="待发货"
|
||||
value={stats?.paid_order_count ?? 0}
|
||||
total={stats?.order_count ?? 0}
|
||||
color="#1677ff"
|
||||
/>
|
||||
<StatusLine
|
||||
label="已失败"
|
||||
value={stats?.failed_order_count ?? 0}
|
||||
label="发货中"
|
||||
value={stats?.delivering_order_count ?? 0}
|
||||
total={stats?.order_count ?? 0}
|
||||
color="#13c2c2"
|
||||
/>
|
||||
<StatusLine
|
||||
label="已交付"
|
||||
value={stats?.delivered_order_count ?? 0}
|
||||
total={stats?.order_count ?? 0}
|
||||
color="#52c41a"
|
||||
/>
|
||||
<StatusLine
|
||||
label="发货失败"
|
||||
value={stats?.ship_failed_order_count ?? 0}
|
||||
total={stats?.order_count ?? 0}
|
||||
color="#ff4d4f"
|
||||
/>
|
||||
|
||||
@@ -41,18 +41,12 @@ import type {
|
||||
WalletLedgerEntry,
|
||||
} from '../types'
|
||||
|
||||
const fulfillmentStatusMap: Record<string, { color: string; text: string }> = {
|
||||
pending: { color: 'orange', text: '待履约' },
|
||||
processing: { color: 'cyan', text: '履约中' },
|
||||
succeeded: { color: 'green', text: '已成功' },
|
||||
failed: { color: 'red', text: '已失败' },
|
||||
cancelled: { color: 'default', text: '已取消' },
|
||||
}
|
||||
|
||||
const paymentStatusMap: Record<string, { color: string; text: string }> = {
|
||||
const orderStatusMap: Record<string, { color: string; text: string }> = {
|
||||
pending: { color: 'orange', text: '待支付' },
|
||||
paid: { color: 'blue', text: '已支付' },
|
||||
refunded: { color: 'purple', text: '已退款' },
|
||||
paid: { color: 'blue', text: '待发货' },
|
||||
delivering: { color: 'cyan', text: '发货中' },
|
||||
delivered: { color: 'green', text: '已交付' },
|
||||
ship_failed: { color: 'red', text: '发货失败' },
|
||||
cancelled: { color: 'default', text: '已取消' },
|
||||
}
|
||||
|
||||
@@ -78,8 +72,8 @@ const eventOptions = [
|
||||
]
|
||||
|
||||
const testOrderStatusOptions = [
|
||||
{ value: 'pending', label: '已支付,可发货' },
|
||||
{ value: 'failed', label: '发货失败,可重试' },
|
||||
{ value: 'paid', label: '已支付,可发货' },
|
||||
{ value: 'ship_failed', label: '发货失败,可重试' },
|
||||
]
|
||||
|
||||
type MerchantCenterTab = 'products' | 'orders' | 'wallet' | 'api' | 'callbacks' | 'members'
|
||||
@@ -347,7 +341,7 @@ export default function MerchantCenter({ fixedTab, title = '商户中心' }: Mer
|
||||
testOrderForm.setFieldsValue({
|
||||
buyer_reference: '测试买家',
|
||||
note: '联调测试订单',
|
||||
fulfillment_status: 'pending',
|
||||
order_status: 'paid',
|
||||
})
|
||||
setTestOrderOpen(true)
|
||||
setTestOrderProductsLoading(true)
|
||||
@@ -374,7 +368,7 @@ export default function MerchantCenter({ fixedTab, title = '商户中心' }: Mer
|
||||
sku: values.sku,
|
||||
buyer_reference: values.buyer_reference,
|
||||
note: values.note,
|
||||
fulfillment_status: values.fulfillment_status,
|
||||
order_status: values.order_status,
|
||||
})
|
||||
setTestOrderOpen(false)
|
||||
setTestOrderResult(result)
|
||||
@@ -494,8 +488,7 @@ export default function MerchantCenter({ fixedTab, title = '商户中心' }: Mer
|
||||
{ title: '基础金额', dataIndex: 'base_amount', width: 88, render: money },
|
||||
{ title: '手续费', dataIndex: 'service_fee_amount', width: 82, render: money },
|
||||
{ title: '扣款合计', dataIndex: 'amount', width: 88, render: money },
|
||||
{ title: '支付', dataIndex: 'payment_status', width: 76, render: paymentStatusTag },
|
||||
{ title: '履约', dataIndex: 'fulfillment_status', width: 86, render: fulfillmentStatusTag },
|
||||
{ title: '状态', dataIndex: 'order_status', width: 92, render: orderStatusTag },
|
||||
{ title: '上游单号', dataIndex: 'provider_order_no', width: 120, ellipsis: true, render: (v) => v || '-' },
|
||||
{
|
||||
title: '链接有效期',
|
||||
@@ -791,7 +784,7 @@ export default function MerchantCenter({ fixedTab, title = '商户中心' }: Mer
|
||||
<Form.Item name="buyer_reference" label="买家名称" rules={[{ max: 128 }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="fulfillment_status" label="可发货状态" rules={[{ required: true }]}>
|
||||
<Form.Item name="order_status" label="订单状态" rules={[{ required: true }]}>
|
||||
<Select options={testOrderStatusOptions} />
|
||||
</Form.Item>
|
||||
<Form.Item name="note" label="备注" rules={[{ max: 512 }]}>
|
||||
@@ -1004,14 +997,9 @@ function productOptionLabel(item: MerchantProduct) {
|
||||
return name === item.sku ? item.sku : `${name} / ${item.sku}`
|
||||
}
|
||||
|
||||
function paymentStatusTag(value: string) {
|
||||
const item = paymentStatusMap[value] || { color: 'default', text: value }
|
||||
return <Tag color={item.color}>{item.text}</Tag>
|
||||
}
|
||||
|
||||
function fulfillmentStatusTag(value: string) {
|
||||
const item = fulfillmentStatusMap[value] || { color: 'default', text: value }
|
||||
return <Tag color={item.color}>{item.text}</Tag>
|
||||
function orderStatusTag(value: string) {
|
||||
const item = orderStatusMap[value] || { color: 'default', text: value }
|
||||
return <Tag color={item.color}>{item.text}</Tag>
|
||||
}
|
||||
|
||||
function ledgerTypeTag(value: string) {
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
endpoints,
|
||||
errorCodes,
|
||||
orderStatusTable,
|
||||
paymentStatusTable,
|
||||
} from '../openapi/endpoints'
|
||||
import { endpointSections, type EndpointSpec } from '../openapi/types'
|
||||
|
||||
@@ -453,7 +452,7 @@ function EndpointSection({ spec }: { spec: EndpointSpec }) {
|
||||
function StatusSection() {
|
||||
return (
|
||||
<SectionWrap id="status" title="状态说明">
|
||||
<Card className="api-docs__card" size="small" title="履约状态 fulfillment_status 与 can_fulfill">
|
||||
<Card className="api-docs__card" size="small" title="订单状态 order_status 与 can_fulfill">
|
||||
<Table
|
||||
className="api-docs__table"
|
||||
size="small"
|
||||
@@ -473,19 +472,6 @@ function StatusSection() {
|
||||
]}
|
||||
/>
|
||||
</Card>
|
||||
<Card className="api-docs__card" size="small" title="支付状态 payment_status">
|
||||
<Table
|
||||
className="api-docs__table"
|
||||
size="small"
|
||||
pagination={false}
|
||||
rowKey="name"
|
||||
dataSource={paymentStatusTable}
|
||||
columns={[
|
||||
{ title: '状态', dataIndex: 'name', width: 140, render: (v) => <Text code>{v}</Text> },
|
||||
{ title: '说明', dataIndex: 'desc' },
|
||||
]}
|
||||
/>
|
||||
</Card>
|
||||
<Alert
|
||||
className="api-docs__alert"
|
||||
type="info"
|
||||
@@ -501,7 +487,7 @@ POST /orders 下单(client_order_no 幂等)
|
||||
↓
|
||||
发货平台查询订单并执行发货
|
||||
↓
|
||||
发货平台通过上游回调更新 fulfillment_status
|
||||
发货平台通过上游回调更新 order_status
|
||||
↓
|
||||
GET /orders/{order_no} 轮询,或接收 order.fulfillment.updated 回调`}</pre>
|
||||
}
|
||||
|
||||
@@ -108,8 +108,7 @@ export interface FulfillmentOrder {
|
||||
service_fee_amount: number
|
||||
amount: number
|
||||
currency: string
|
||||
payment_status: string
|
||||
fulfillment_status: string
|
||||
order_status: string
|
||||
buyer_reference: string
|
||||
provider_order_no: string
|
||||
failure_reason: string
|
||||
@@ -222,9 +221,10 @@ export interface DashboardStats {
|
||||
total_fees: number
|
||||
today_fees: number
|
||||
pending_order_count: number
|
||||
processing_order_count: number
|
||||
succeeded_order_count: number
|
||||
failed_order_count: number
|
||||
paid_order_count: number
|
||||
delivering_order_count: number
|
||||
delivered_order_count: number
|
||||
ship_failed_order_count: number
|
||||
cancelled_order_count: number
|
||||
wallet_available_balance: number
|
||||
wallet_frozen_balance: number
|
||||
|
||||
Reference in New Issue
Block a user