442 lines
10 KiB
TypeScript
442 lines
10 KiB
TypeScript
import { apiDelete, apiGet, apiPost } from '@/lib/http'
|
|
|
|
/** 待分类队列(按内部 SKU 聚合)。 */
|
|
export type PendingChannelGroup = {
|
|
group_key: string
|
|
scope_type: 'internal_sku' | 'source_sku'
|
|
internal_sku: string | null
|
|
sku_name: string
|
|
providers: string[]
|
|
shop_ids: string[]
|
|
shops: Array<{ id: string; name: string }>
|
|
sku_codes: string[]
|
|
pending_reasons: string[]
|
|
pending_subreasons: string[]
|
|
configured_channels: string[]
|
|
locked_item_count: number
|
|
item_count: number
|
|
revenue_fen: number
|
|
first_seen_at: string
|
|
last_seen_at: string
|
|
issue_breakdown: Array<{
|
|
issue_code: string
|
|
issue_category: string
|
|
item_count: number
|
|
locked_item_count: number
|
|
retryable: boolean
|
|
auto_retryable: boolean
|
|
owner_role: string
|
|
}>
|
|
retryable_item_count: number
|
|
retryable_unlocked_item_count: number
|
|
auto_retryable_item_count: number
|
|
auto_retryable_unlocked_item_count: number
|
|
config_issue_item_count: number
|
|
manual_review_item_count: number
|
|
after_sales_item_count: number
|
|
system_issue_item_count: number
|
|
oldest_age_minutes: number
|
|
}
|
|
|
|
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' | 'on_account'
|
|
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 PendingCarrierRetryResult = {
|
|
scanned: number
|
|
workOrdersCreated: number
|
|
affiliateTasksCreated: number
|
|
assigned: number
|
|
failed: number
|
|
remaining: 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 type VoucherConsumeProvider = 'kuaishou_industry' | 'douyin_mashida'
|
|
export type VoucherConsumeChannel =
|
|
| 'affiliate_dash'
|
|
| 'work_order'
|
|
| 'manual'
|
|
| 'on_account'
|
|
| 'third_party_voucher'
|
|
export type VoucherConsumeMode =
|
|
| 'manual'
|
|
| 'after_order_received'
|
|
| 'after_link_generated'
|
|
| 'after_task_created'
|
|
| 'after_work_order_published'
|
|
| 'after_work_order_assigned'
|
|
| 'after_work_order_published_or_assigned'
|
|
|
|
export type VoucherChannelConsumePolicy = {
|
|
enabled: boolean
|
|
mode: VoucherConsumeMode
|
|
delaySeconds: number
|
|
}
|
|
|
|
export type VoucherConsumePolicy = {
|
|
version: number
|
|
enabled: boolean
|
|
platforms: Record<
|
|
VoucherConsumeProvider,
|
|
{
|
|
enabled: boolean
|
|
channels: Record<VoucherConsumeChannel, VoucherChannelConsumePolicy>
|
|
}
|
|
>
|
|
}
|
|
|
|
export type VoucherConsumeJob = {
|
|
id: number
|
|
provider: VoucherConsumeProvider
|
|
channel: VoucherConsumeChannel
|
|
trigger_event: string
|
|
platform_order_id: string
|
|
order_id: number | null
|
|
order_item_id: number | null
|
|
task_id: number | null
|
|
work_order_id: number | null
|
|
due_at: string
|
|
status: 'pending' | 'processing' | 'succeeded' | 'failed' | 'cancelled'
|
|
attempt_count: number
|
|
max_attempts: number
|
|
last_error: string
|
|
voucher_codes: string[]
|
|
created_at: string
|
|
updated_at: string
|
|
finished_at: string | null
|
|
}
|
|
|
|
export type VoucherConsumeJobListResult = {
|
|
items: VoucherConsumeJob[]
|
|
total: number
|
|
page: number
|
|
pageSize: number
|
|
}
|
|
|
|
export function fetchPendingChannelGroups(
|
|
params: {
|
|
page?: number
|
|
pageSize?: number
|
|
keyword?: string
|
|
issueCategory?: string
|
|
} = {},
|
|
) {
|
|
return apiGet<PendingChannelGroupListResult>('/api/v1/admin/fulfillment/pending', params)
|
|
}
|
|
|
|
export function fetchVoucherConsumePolicy() {
|
|
return apiGet<VoucherConsumePolicy>('/api/v1/admin/fulfillment/voucher-consume-policy')
|
|
}
|
|
|
|
export function saveVoucherConsumePolicy(payload: VoucherConsumePolicy) {
|
|
return apiPost<VoucherConsumePolicy>('/api/v1/admin/fulfillment/voucher-consume-policy', payload)
|
|
}
|
|
|
|
export function fetchVoucherConsumeJobs(
|
|
params: {
|
|
keyword?: string
|
|
provider?: VoucherConsumeProvider
|
|
channel?: VoucherConsumeChannel
|
|
status?: VoucherConsumeJob['status']
|
|
page?: number
|
|
pageSize?: number
|
|
} = {},
|
|
) {
|
|
return apiGet<VoucherConsumeJobListResult>(
|
|
'/api/v1/admin/fulfillment/voucher-consume-jobs',
|
|
params,
|
|
)
|
|
}
|
|
|
|
export function retryVoucherConsumeJob(id: number) {
|
|
return apiPost<VoucherConsumeJob | null>(
|
|
`/api/v1/admin/fulfillment/voucher-consume-jobs/${id}/retry`,
|
|
{},
|
|
)
|
|
}
|
|
|
|
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 retryPendingCarriers(payload: {
|
|
internalSku: string
|
|
retryInactiveCarrier?: boolean
|
|
includeLocked?: boolean
|
|
issueCodes?: string[]
|
|
}) {
|
|
return apiPost<PendingCarrierRetryResult>(
|
|
'/api/v1/admin/fulfillment/pending/retry-carriers',
|
|
payload,
|
|
)
|
|
}
|
|
|
|
export function unlockPendingChannelGroup(payload: {
|
|
groupKey?: string
|
|
internalSku?: string
|
|
issueCategory?: string
|
|
}) {
|
|
return apiPost<{ updated: number }>('/api/v1/admin/fulfillment/pending/unlock', payload)
|
|
}
|
|
|
|
export function deletePendingChannelGroup(payload: { groupKey: string; issueCategory?: string }) {
|
|
return apiPost<DeletePendingChannelGroupResult>(
|
|
'/api/v1/admin/fulfillment/pending/delete',
|
|
payload,
|
|
)
|
|
}
|
|
|
|
export function assignChannelByInternalSku(payload: {
|
|
groupKey?: string
|
|
internalSku: string
|
|
mappedInternalSku?: string
|
|
channel: string
|
|
issueCategory?: string
|
|
note?: string
|
|
}) {
|
|
return apiPost<{
|
|
updated: number
|
|
carrierFailed: number
|
|
ruleCreated: boolean
|
|
channelSaved: boolean
|
|
draftProductId: number | null
|
|
carrierFailures: Array<{ orderItemId: number; reason: string; message: string }>
|
|
}>('/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,
|
|
})
|
|
}
|