Files
order_site/apps/frontend/src/services/admin/fulfillment-channel.ts
T

289 lines
6.4 KiB
TypeScript

import { apiDelete, apiGet, apiPost } from '@/lib/http'
/** 待分类队列(按内部 SKU 聚合)。 */
export type PendingChannelGroup = {
internal_sku: string
sku_name: string
providers: string[]
shop_ids: string[]
shops: Array<{ id: string; name: string }>
sku_codes: string[]
pending_reasons: string[]
item_count: number
revenue_fen: number
first_seen_at: string
last_seen_at: string
}
export type PendingChannelGroupListResult = {
items: PendingChannelGroup[]
total: number
page: number
pageSize: number
}
/** 渠道规则(L1)。 */
export type ChannelRule = {
id: number
match_key_type: 'internal_sku'
match_key: string
channel: 'work_order' | 'affiliate_dash' | 'third_party_voucher' | 'manual'
priority: number
enabled: boolean
is_fallback: boolean
source: string
config_json: Record<string, unknown>
note: string
}
export type ChannelRuleListResult = {
items: ChannelRule[]
total: number
page: number
pageSize: number
}
export type InternalSkuMappingMatchType =
| 'sku_code'
| 'sku_nick'
| 'out_sku_sn'
| 'spec'
| 'sku_name'
export type InternalSkuMapping = {
id: number
provider: string
shop_id: string
match_type: InternalSkuMappingMatchType
match_value: string
internal_sku: string
priority: number
enabled: boolean
source: string
note: string
updated_at: string
}
export type InternalSkuMappingListResult = {
items: InternalSkuMapping[]
total: number
page: number
pageSize: number
}
export type ChannelException = {
id: number
reason: string
status: string
scope_key: string
item_count: number
first_seen_at: string
last_seen_at: string
detail_json: unknown
}
export type ChannelExceptionListResult = {
items: ChannelException[]
total: number
page: number
pageSize: number
}
export type PipelineRunResult = Record<string, unknown>
export type PendingWorkOrderRetryResult = {
scanned: number
createdWorkOrders: number
assigned: number
remaining: number
carrierFailed: number
skipReasons: Record<string, number>
}
export type DeletePendingChannelGroupResult = {
archivedItems: number
preservedOrders: number
deletedCostSnapshots: number
resolvedExceptions: number
refreshedDays: number
skippedClosedDays: number
}
export type FinancePipelineStatus = {
pipeline: string
label: string
lastItemId: number
lastStatDate: string | null
lastSuccessAt: string | null
lastError: string
jobId: string
scheduler: {
running: boolean
enabled: boolean
lastRunAt: string
lastFinishedAt: string
nextRunAt: string
lastStatus: string
lastMessage: string
} | null
}
export function fetchPendingChannelGroups(
params: {
page?: number
pageSize?: number
keyword?: string
} = {},
) {
return apiGet<PendingChannelGroupListResult>('/api/v1/admin/fulfillment/pending', params)
}
export function fetchChannelExceptions(
params: {
reason?: string
status?: string
page?: number
pageSize?: number
} = {},
) {
return apiGet<ChannelExceptionListResult>('/api/v1/admin/fulfillment/exceptions', params)
}
export function retryPendingWorkOrderRouting(
payload: { internalSku?: string; limit?: number } = {},
) {
return apiPost<PendingWorkOrderRetryResult>(
'/api/v1/admin/fulfillment/pending/retry-work-orders',
payload,
)
}
export function deletePendingChannelGroup(payload: { internalSku: string }) {
return apiPost<DeletePendingChannelGroupResult>(
'/api/v1/admin/fulfillment/pending/delete',
payload,
)
}
export function assignChannelByInternalSku(payload: {
internalSku: string
channel: string
note?: string
}) {
return apiPost<{ updated: number; carrierFailed: number; ruleCreated: boolean }>(
'/api/v1/admin/fulfillment/assign',
payload,
)
}
export function assignChannelToItems(payload: {
orderItemIds: number[]
channel: string
note?: string
}) {
return apiPost<{ updated: number; carrierFailed: number }>(
'/api/v1/admin/fulfillment/assign',
payload,
)
}
export function switchChannelForItems(payload: {
orderItemIds: number[]
channel: string
note?: string
}) {
return apiPost<{
switched: number
failed: number
errors: Array<{ orderItemId: number; reason: string }>
}>('/api/v1/admin/fulfillment/switch', payload)
}
export function fetchChannelRules(
params: {
channel?: string
source?: string
keyword?: string
enabled?: boolean
page?: number
pageSize?: number
} = {},
) {
return apiGet<ChannelRuleListResult>('/api/v1/admin/fulfillment/rules', params)
}
export function fetchInternalSkuMappings(
params: {
keyword?: string
provider?: string
shopId?: string
matchType?: string
page?: number
pageSize?: number
} = {},
) {
return apiGet<InternalSkuMappingListResult>(
'/api/v1/admin/fulfillment/internal-sku-mappings',
params,
)
}
export function saveInternalSkuMapping(payload: {
provider?: string
shopId?: string
matchType: InternalSkuMappingMatchType
matchValue: string
internalSku: string
priority?: number
note?: string
enabled?: boolean
}) {
return apiPost<{ id: number }>('/api/v1/admin/fulfillment/internal-sku-mappings', payload)
}
export function deleteInternalSkuMapping(id: number) {
return apiDelete<boolean>(`/api/v1/admin/fulfillment/internal-sku-mappings/${id}`)
}
export function saveChannelRule(payload: {
id?: number
matchKeyType: string
matchKey: string
channel: string
priority?: number
enabled?: boolean
note?: string
}) {
return apiPost<{ id: number }>('/api/v1/admin/fulfillment/rules', payload)
}
export function deleteChannelRule(ruleId: number) {
return apiDelete<{ deleted: boolean }>(`/api/v1/admin/fulfillment/rules/${ruleId}`)
}
export function deleteChannelRules(ids: number[]) {
return apiPost<{
deletedCount: number
deletedIds: number[]
missingIds: number[]
}>('/api/v1/admin/fulfillment/rules/batch-delete', { ids })
}
export function forceDeleteChannelRules(ids: number[]) {
return apiPost<{
deletedCount: number
deletedIds: number[]
missingIds: number[]
}>('/api/v1/admin/fulfillment/rules/force-delete', { ids })
}
export function fetchPipelineStatus() {
return apiGet<FinancePipelineStatus[]>('/api/v1/admin/fulfillment/pipeline/status')
}
export function runFinancePipeline(payload: { days?: number; pipeline?: string } = {}) {
return apiPost<PipelineRunResult>('/api/v1/admin/fulfillment/pipeline/run', payload, {
timeout: 300_000,
})
}