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
+103
View File
@@ -0,0 +1,103 @@
import { http } from '@/lib/http'
export interface OpsMediaSupplyResource {
id: number
supplier_resource_id: string
model_id: number
name: string
status: string
cost_price_cents: number
cost_prices: Record<string, number>
sell_price_cents: number
resource_url?: string | null
baidu_weight?: number | null
resource_remark?: string | null
customer_visible: boolean
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 OpsMediaSupplyResourcesParams {
model_id?: number
keyword?: string
remark_keyword?: string
channel_type?: string
region?: string
inclusion_effect?: string
link_type?: string
min_price_cents?: number
max_price_cents?: number
visibility?: string
page?: number
page_size?: number
}
export interface OpsMediaSupplyResourcesResponse {
items: OpsMediaSupplyResource[]
total: number
page: number
size: number
}
export interface OpsMediaSupplyWallet {
tenant_id: number
tenant_name?: string | null
user_id: number
user_name?: string | null
user_phone?: string | null
balance_cents: number
updated_at: string
last_ledger_at?: string | null
ledger_entries: number
}
export interface OpsMediaSupplyWalletsResponse {
items: OpsMediaSupplyWallet[]
total: number
page: number
size: number
}
export interface OpsMediaSupplyWalletsParams {
keyword?: string
tenant_id?: number
page?: number
page_size?: number
}
export const opsMediaSupplyApi = {
listResources(params: OpsMediaSupplyResourcesParams) {
return http.get<OpsMediaSupplyResourcesResponse>(
'/media-supply/resources',
params as Record<string, unknown>,
)
},
setPrice(id: number, payload: { price_type?: string; sell_price_cents: number; enabled?: boolean }) {
return http.put<{ updated: boolean }, typeof payload>(`/media-supply/resources/${id}/price`, payload)
},
setVisibility(id: number, customerVisible: boolean) {
return http.put<{ updated: boolean }, { customer_visible: boolean }>(
`/media-supply/resources/${id}/visibility`,
{ customer_visible: customerVisible },
)
},
queueSync(modelId = 1) {
return http.post<{ job_id: number }, { model_id: number }>('/media-supply/sync-jobs', {
model_id: modelId,
})
},
listWallets(params: OpsMediaSupplyWalletsParams) {
return http.get<OpsMediaSupplyWalletsResponse>(
'/media-supply/wallets',
params as Record<string, unknown>,
)
},
adjustWallet(payload: { tenant_id: number; user_id: number; delta_cents: number; note?: string }) {
return http.post<OpsMediaSupplyWallet, typeof payload>('/media-supply/wallets/adjustments', payload)
},
}