feat(media-supply): add media resource supply marketplace
Desktop Client Build / Resolve Build Metadata (push) Successful in 43s
Frontend CI / Frontend (push) Successful in 3m49s
Backend CI / Backend (push) Failing after 7m10s
Desktop Client Build / Build Desktop Client (push) Successful in 23m4s
Desktop Client Build / Publish Client Artifacts to NAS (push) Successful in 28s

Introduce an end-to-end media-supply feature: tenant-side resource sync
service/worker backed by a Meijiequan supplier client, ops-side management
APIs, and admin/ops web views for resources, orders, favorites and
submission. Adds a shared digitocr helper, MediaSupply config blocks for
tenant and ops, shared types, and migrations for supplier media resources,
price overrides, customer visibility and order refunds.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-29 23:17:01 +08:00
parent 607a3fffe7
commit 9d6181260a
121 changed files with 14909 additions and 36 deletions
+239
View File
@@ -411,6 +411,7 @@ export interface CreatePublishJobResponse {
task_ids: string[]
created_task_ids: string[]
existing_task_ids: string[]
requeued_task_ids?: string[]
}
export interface WorkspaceOverview {
@@ -470,6 +471,244 @@ export interface AIPointUsageListResponse {
page_size: number
}
export interface SupplierMediaResource {
id: number
supplier: string
supplier_resource_id: string
model_id: number
name: string
status: string
cost_price_cents: number
cost_prices: Record<string, number>
sell_price_cents: number
sale_price_label?: string | null
resource_url?: string | null
baidu_weight?: number | null
resource_remark?: string | null
channel_type?: string | null
region?: string | null
inclusion_effect?: string | null
link_type?: string | null
publish_rate?: string | null
delivery_speed?: string | null
supplier_updated_at?: string | null
last_synced_at: string
raw?: JsonValue
}
export interface CustomerSupplierMediaResource {
id: number
supplier_resource_id: string
model_id: number
name: string
status: string
sell_price_cents: number
resource_url?: string | null
baidu_weight?: number | null
resource_remark?: string | null
channel_type?: string | null
region?: string | null
inclusion_effect?: string | null
link_type?: string | null
publish_rate?: string | null
delivery_speed?: string | null
last_synced_at: string
}
export interface ListSupplierMediaResourcesParams {
model_id?: number
keyword?: string
remark_keyword?: string
channel_type?: string
portal?: string
region?: string
inclusion_effect?: string
special_industry?: string
link_type?: string
delivery_speed?: string
ai_include?: string
other_option?: string
min_price_cents?: number
max_price_cents?: number
sort?: 'default' | 'price' | 'weight'
page?: number
page_size?: number
}
export interface ListSupplierMediaResourcesResponse {
items: SupplierMediaResource[]
total: number
page: number
page_size: number
}
export interface ListCustomerSupplierMediaResourcesResponse {
items: CustomerSupplierMediaResource[]
total: number
page: number
page_size: number
}
export interface MediaSupplySearchOptionGroup {
key: string
name: string
list: string[]
}
export interface MediaSupplySearchOptionsResponse {
model_id: number
groups: MediaSupplySearchOptionGroup[]
updated_at: string
}
export interface CreateMediaSupplyOrderRequest {
article_id?: number | null
model_id: number
title: string
content: string
remark?: string
order_brand?: string
items: Array<{
resource_id: number
price_type?: string
}>
extra?: Record<string, JsonValue>
}
export interface CreateMediaSupplyOrderResponse {
order_id: number
status: string
cost_total_cents: number
sell_total_cents: number
balance_after_cents: number
}
export interface SetSupplierMediaPriceRequest {
price_type?: string
sell_price_cents: number
enabled?: boolean
}
export interface MediaSupplyOrderItem {
id: number
order_id: number
resource_id: number
supplier_resource_id: string
price_type: string
resource_name_snapshot: string
locked_cost_price_cents: number
locked_sell_price_cents: number
status: string
external_article_url?: string | null
}
export interface MediaSupplyOrderDetail {
id: number
tenant_id: number
workspace_id: number
brand_id: number
user_id: number
article_id?: number | null
supplier: string
model_id: number
status: string
title: string
remark?: string | null
order_brand?: string | null
external_order_id?: string | null
external_order_code?: string | null
supplier_status?: string | null
cost_total_cents: number
sell_total_cents: number
wallet_debit_cents: number
wallet_debited_at?: string | null
wallet_refunded_at?: string | null
error_message?: string | null
attempt_count: number
queued_at: string
submitted_at?: string | null
completed_at?: string | null
created_at: string
updated_at: string
items?: MediaSupplyOrderItem[]
}
export interface ListMediaSupplyOrdersParams {
status?: string
article_id?: number
page?: number
page_size?: number
}
export interface ListMediaSupplyOrdersResponse {
items: MediaSupplyOrderDetail[]
total: number
page: number
page_size: number
}
export interface SyncMediaSupplyBacklinksResponse {
published_fetched: number
pending_fetched: number
problem_fetched: number
orders_checked: number
orders_updated: number
order_codes_updated: number
refunded_orders: number
links_updated: number
}
export interface MediaSupplyWalletStatus {
tenant_id: number
user_id: number
balance_cents: number
updated_at: string
ledger_total?: number | null
last_ledger_at?: string | null
}
export interface MediaSupplyWalletLedger {
id: number
tenant_id: number
user_id: number
order_id?: number | null
order_title?: string | null
delta_cents: number
balance_after_cents: number
reason: string
note?: string | null
created_by?: number | null
created_at: string
}
export interface ListMediaSupplyWalletLedgersParams {
page?: number
page_size?: number
user_id?: number
created_from?: string
created_to?: string
}
export interface ListMediaSupplyWalletLedgersResponse {
items: MediaSupplyWalletLedger[]
total: number
page: number
page_size: number
}
export interface AdjustMediaSupplyWalletRequest {
user_id: number
delta_cents: number
note?: string
}
export interface MediaSupplySessionStatus {
available: boolean
expires_at?: string | null
updated_at?: string | null
account_configured: boolean
}
export interface TemplateCard {
id: number
scope: string