分站前台原本对顾客暴露分销身份,且登录等页面写死了主站品牌, 与「一手独立商城」的定位不符;同时首页文案全部硬编码,站长无法按自己口径调整。 去分销身份(顾客可见处): - 头部品牌区、首页主视觉、移动端认证卡去掉「官方分销/官方分销认证/官方分销站」标识 - 会员徽标「分销专享」→「会员专享」,「所属商城」兜底「专属分销站/商城」→「官方商城」 - 订单页说明改为「当前为【X】商城,仅展示在本站产生的订单记录」,保留订单按站隔离的必要说明 - 无公告时的兜底跑马灯、轮播角标去掉分销措辞 - 清理随之失效的 pc-brand-badge / mobile-brand-badge 样式 品牌跟随站点配置(即可自定义): - PC/移动登录页、移动注册页、加桌面引导页的品牌改为 站点名称 + Logo(无 Logo 取站名首字), 不再写死主站「大锤商行」;主站行为不变(新增 store getter brandMark:分站取站名首字,主站仍为「锤」) 首页文案可配置: - 新增迁移 000076 与 distribution_sites.storefront_text_json (tagline 头部副标题 / hero_badge 主视觉徽标 / hero_desc 主视觉描述 / card_sub 移动端卡片副标题) - 分销后台「品牌装修 → 首页文案」可编辑,占位提示即默认值,留空或整段清空回落内置默认 - 贯通 UpdateSiteBranding → 公开站点信息接口(含 Redis 缓存失效)→ 前台 store 逐字段合并默认值 - 装修预览同步展示站长填写的副标题与徽标 测试: - 后端补装修字段写入/公开接口透出/清空回落断言 - 前端新增 storefrontText 单测(默认合并、覆盖、空白与非法类型回落)
794 lines
21 KiB
TypeScript
794 lines
21 KiB
TypeScript
import axios from 'axios'
|
|
|
|
const TOKEN_KEY = 'hfb_dist_access_token'
|
|
|
|
export function getStoredToken(): string | null {
|
|
try {
|
|
return localStorage.getItem(TOKEN_KEY)
|
|
} catch {
|
|
return null
|
|
}
|
|
}
|
|
|
|
export function setStoredToken(token: string): void {
|
|
try {
|
|
localStorage.setItem(TOKEN_KEY, token)
|
|
} catch {
|
|
// ignore local storage errors
|
|
}
|
|
}
|
|
|
|
export function clearStoredToken(): void {
|
|
try {
|
|
localStorage.removeItem(TOKEN_KEY)
|
|
} catch {
|
|
// ignore local storage errors
|
|
}
|
|
}
|
|
|
|
const api = axios.create({ baseURL: '/api', withCredentials: true })
|
|
|
|
api.interceptors.request.use((config) => {
|
|
const token = getStoredToken()
|
|
if (token) {
|
|
config.headers.Authorization = `Bearer ${token}`
|
|
}
|
|
return config
|
|
})
|
|
|
|
api.interceptors.response.use(
|
|
(resp) => resp,
|
|
(error) => {
|
|
if (error?.response?.status === 401) {
|
|
clearStoredToken()
|
|
if (typeof window !== 'undefined' && !window.location.pathname.startsWith('/login')) {
|
|
window.location.href = '/login'
|
|
}
|
|
}
|
|
return Promise.reject(error)
|
|
},
|
|
)
|
|
|
|
export interface ApiResponse<T> {
|
|
data: T
|
|
code?: string | number
|
|
message?: string
|
|
}
|
|
|
|
export function extract<T>(resp: { data: ApiResponse<T> }): T {
|
|
return resp.data.data
|
|
}
|
|
|
|
export function formatMoney(cent: number | undefined | null): string {
|
|
return `¥${((cent ?? 0) / 100).toFixed(2)}`
|
|
}
|
|
|
|
export function formatRatio(ratio: number | undefined | null): string {
|
|
const value = Number(ratio || 0)
|
|
if (!Number.isFinite(value) || value <= 0) return '--'
|
|
return `1:${value.toFixed(1)}`
|
|
}
|
|
|
|
export interface CurrentMember {
|
|
admin_id: number
|
|
username: string
|
|
nickname: string
|
|
is_superadmin: boolean
|
|
site_id?: number
|
|
site_code?: string
|
|
site_name?: string
|
|
domain?: string
|
|
member_type?: string
|
|
}
|
|
|
|
export interface LoginResult {
|
|
token: string
|
|
refresh_token?: string
|
|
member: CurrentMember
|
|
}
|
|
|
|
export async function login(username: string, password: string): Promise<LoginResult> {
|
|
const resp = await api.post<ApiResponse<LoginResult>>('/distribution/auth/login', {
|
|
username,
|
|
password,
|
|
})
|
|
const res = extract(resp)
|
|
if (res.token) {
|
|
setStoredToken(res.token)
|
|
}
|
|
return res
|
|
}
|
|
|
|
export async function getMe(): Promise<CurrentMember> {
|
|
const resp = await api.get<ApiResponse<CurrentMember>>('/distribution/auth/me')
|
|
return extract(resp)
|
|
}
|
|
|
|
export async function logout(): Promise<void> {
|
|
try {
|
|
await api.post('/distribution/auth/logout')
|
|
} finally {
|
|
clearStoredToken()
|
|
}
|
|
}
|
|
|
|
export interface Site {
|
|
id: number
|
|
site_code: string
|
|
site_name: string
|
|
domain: string
|
|
status: string
|
|
display_account_enabled: boolean
|
|
tax_enabled: boolean
|
|
tax_rate_bps: number
|
|
settlement_hold_days?: number
|
|
logo_url?: string
|
|
favicon_url?: string
|
|
banners_json?: string
|
|
announcements_json?: string
|
|
footer_info_json?: string
|
|
aw_recycle_json?: string
|
|
cooperation_feedback_json?: string
|
|
storefront_text_json?: string
|
|
listing_qrcode_url?: string
|
|
receive_ratio_delta?: number
|
|
receive_cost_delta?: number
|
|
admin_user_id?: number
|
|
admin_username?: string
|
|
admin_nickname?: string
|
|
member_type?: string
|
|
created_at: string
|
|
}
|
|
|
|
export interface Rule {
|
|
id: number
|
|
site_id?: number
|
|
mode: string
|
|
version: number
|
|
status: string
|
|
platform_receive_ratio: number
|
|
platform_sell_ratio: number
|
|
distributor_receive_ratio: number
|
|
distributor_sell_ratio: number
|
|
distributor_a_ratio: number
|
|
distributor_b_ratio: number
|
|
service_fee_value: number
|
|
service_fee_unit: string
|
|
payment_fee_bps: number
|
|
tax_enabled: boolean
|
|
tax_rate_bps: number
|
|
effective_from: string
|
|
effective_to?: string
|
|
}
|
|
|
|
export interface DistributionOrder {
|
|
order_id: number
|
|
order_no: string
|
|
site_id: number
|
|
status: string
|
|
handoff_status?: string
|
|
refund_status: string
|
|
settlement_status: string
|
|
distributor_settlement_status?: string
|
|
release_at?: string
|
|
distribution_mode: string
|
|
gross_paid_amount_cent?: number
|
|
refund_amount_cent?: number
|
|
net_paid_amount_cent: number
|
|
payment_fee_cent: number
|
|
service_fee_cent: number
|
|
markup_amount_cent: number
|
|
tax_amount_cent: number
|
|
split_base_amount_cent: number
|
|
rounding_adjustment_cent: number
|
|
created_at: string
|
|
|
|
// 商品与租客字段
|
|
listing_no?: string
|
|
title?: string
|
|
game_name?: string
|
|
server_region?: string
|
|
login_platform?: string
|
|
rent_duration_hours?: number
|
|
rented_at?: string
|
|
renter_phone?: string
|
|
renter_nickname?: string
|
|
owner_rent_amount_cent?: number
|
|
base_price_cent?: number
|
|
shelf_markup_cent?: number
|
|
deposit_amount_cent?: number
|
|
|
|
// 商户本站收益拆解字段
|
|
distributor_earning_cent?: number
|
|
distributor_role?: string
|
|
distributor_markup_cent?: number
|
|
distributor_split_cent?: number
|
|
}
|
|
|
|
export interface OrderPage {
|
|
items: DistributionOrder[]
|
|
total: number
|
|
page: number
|
|
page_size: number
|
|
}
|
|
|
|
export interface AccountDisplay {
|
|
id: number
|
|
site_id: number
|
|
account_id: number
|
|
listing_id: number
|
|
display_status: string
|
|
markup_amount_cent?: number
|
|
shelf_ratio_delta?: number
|
|
base_ratio?: number
|
|
site_ratio?: number
|
|
listing_no?: string
|
|
title?: string
|
|
game_name?: string
|
|
server_region?: string
|
|
login_platform?: string
|
|
rank_level?: string
|
|
haf_coin_amount?: number
|
|
price_cent?: number
|
|
site_price_cent?: number
|
|
deposit_amount_cent?: number
|
|
listing_status?: string
|
|
in_transaction?: boolean
|
|
listing_site_id?: number | null
|
|
source_channel?: string
|
|
}
|
|
|
|
export interface SyncSiteAccountsRequest {
|
|
listing_ids?: number[]
|
|
clean_stale_listings?: boolean
|
|
default_shelf_ratio_delta?: number
|
|
}
|
|
|
|
export interface SyncSiteAccountsResult {
|
|
synced_count: number
|
|
skipped_count: number
|
|
updated_count?: number
|
|
reenabled_count?: number
|
|
disabled_stale_count?: number
|
|
total_active: number
|
|
}
|
|
|
|
export interface PublicDistributionListing {
|
|
listing_id: number
|
|
listing_no: string
|
|
account_id: number
|
|
title: string
|
|
game_name: string
|
|
server_region: string
|
|
login_platform: string
|
|
rank_level: string
|
|
haf_coin_amount: number
|
|
price_cent: number
|
|
deposit_amount_cent: number
|
|
in_transaction: boolean
|
|
site_code: string
|
|
site_name: string
|
|
}
|
|
|
|
export interface PublicDistributionListingsResult {
|
|
items: PublicDistributionListing[]
|
|
total: number
|
|
page: number
|
|
page_size: number
|
|
}
|
|
|
|
export async function fetchPublicSiteListings(
|
|
siteCode: string,
|
|
page = 1,
|
|
pageSize = 24,
|
|
): Promise<PublicDistributionListingsResult> {
|
|
const resp = await api.get<ApiResponse<PublicDistributionListingsResult>>(
|
|
`/distribution/sites/${siteCode}/listings`,
|
|
{
|
|
params: { page, page_size: pageSize },
|
|
},
|
|
)
|
|
return extract(resp)
|
|
}
|
|
|
|
export interface PaymentChannel {
|
|
id: number
|
|
site_id: number
|
|
config_id: number
|
|
priority: number
|
|
status: string
|
|
}
|
|
|
|
export interface SettlementBatch {
|
|
id: number
|
|
site_id: number
|
|
batch_no: string
|
|
period_start: string
|
|
period_end: string
|
|
total_amount_cent: number
|
|
status: string
|
|
}
|
|
|
|
export interface SettlementItem {
|
|
id: number
|
|
order_id: number
|
|
party_role: string
|
|
amount_cent: number
|
|
status: string
|
|
}
|
|
|
|
export interface OnboardSitePayload {
|
|
site_code: string
|
|
site_name: string
|
|
domain: string
|
|
admin_username: string
|
|
admin_password: string
|
|
admin_nickname?: string
|
|
member_type?: string
|
|
display_account_enabled?: boolean
|
|
tax_enabled?: boolean
|
|
tax_rate_bps?: number
|
|
settlement_hold_days?: number
|
|
}
|
|
|
|
export interface OnboardSiteResult {
|
|
site: Site
|
|
rule: Rule
|
|
member: CurrentMember
|
|
}
|
|
|
|
export async function onboardSite(payload: OnboardSitePayload): Promise<OnboardSiteResult> {
|
|
const resp = await api.post<ApiResponse<OnboardSiteResult>>('/admin/distribution/sites/onboard', payload)
|
|
return extract(resp)
|
|
}
|
|
|
|
export async function listSites(): Promise<Site[]> {
|
|
const resp = await api.get<ApiResponse<{ items: Site[] }>>('/admin/distribution/sites')
|
|
return extract(resp).items ?? []
|
|
}
|
|
|
|
export async function updateSite(
|
|
siteID: number,
|
|
payload: Partial<
|
|
Pick<
|
|
Site,
|
|
| 'site_name'
|
|
| 'domain'
|
|
| 'status'
|
|
| 'display_account_enabled'
|
|
| 'tax_enabled'
|
|
| 'tax_rate_bps'
|
|
| 'settlement_hold_days'
|
|
| 'listing_qrcode_url'
|
|
| 'receive_ratio_delta'
|
|
| 'receive_cost_delta'
|
|
>
|
|
>
|
|
): Promise<Site> {
|
|
const resp = await api.put<ApiResponse<Site>>(`/admin/distribution/sites/${siteID}`, payload)
|
|
return extract(resp)
|
|
}
|
|
|
|
export async function deleteSite(siteID: number): Promise<{ deleted: boolean }> {
|
|
const resp = await api.delete<ApiResponse<{ deleted: boolean }>>(`/admin/distribution/sites/${siteID}`)
|
|
return extract(resp)
|
|
}
|
|
|
|
export async function resetSiteAdminPassword(siteID: number, newPassword: string): Promise<{ message: string }> {
|
|
const resp = await api.put<ApiResponse<{ message: string }>>(`/admin/distribution/sites/${siteID}/admin-password`, {
|
|
new_password: newPassword,
|
|
})
|
|
return extract(resp)
|
|
}
|
|
|
|
export interface DistributionSiteDefaults {
|
|
default_settlement_hold_days: number
|
|
}
|
|
|
|
// fetchDistributionSiteDefaults 读取新建站点的默认结算冷却天数,供开通表单预填。
|
|
export async function fetchDistributionSiteDefaults(): Promise<DistributionSiteDefaults> {
|
|
const resp = await api.get<ApiResponse<DistributionSiteDefaults>>(
|
|
'/admin/distribution/site-defaults'
|
|
)
|
|
return extract(resp)
|
|
}
|
|
|
|
export async function listRules(siteID?: number, mode?: string): Promise<Rule[]> {
|
|
const resp = await api.get<ApiResponse<{ items: Rule[] }>>('/admin/distribution/rules', {
|
|
params: { site_id: siteID || undefined, mode: mode || undefined },
|
|
})
|
|
return extract(resp).items ?? []
|
|
}
|
|
|
|
export interface CreateRulePayload {
|
|
site_id?: number
|
|
mode: string
|
|
distributor_receive_ratio?: number
|
|
platform_sell_ratio?: number
|
|
distributor_sell_ratio?: number
|
|
platform_receive_ratio?: number
|
|
distributor_a_ratio?: number
|
|
distributor_b_ratio?: number
|
|
service_fee_value: number
|
|
service_fee_unit: string
|
|
payment_fee_bps: number
|
|
tax_enabled: boolean
|
|
tax_rate_bps: number
|
|
effective_from?: string
|
|
}
|
|
|
|
export async function createRule(payload: CreateRulePayload): Promise<Rule> {
|
|
const resp = await api.post<ApiResponse<Rule>>('/admin/distribution/rules', payload)
|
|
return extract(resp)
|
|
}
|
|
|
|
export async function updateRuleStatus(id: number, status: string): Promise<Rule> {
|
|
const resp = await api.put<ApiResponse<Rule>>(`/admin/distribution/rules/${id}/status`, { status })
|
|
return extract(resp)
|
|
}
|
|
|
|
export async function listOrders(params: {
|
|
kind?: string
|
|
role?: string
|
|
status?: string
|
|
siteID?: number
|
|
page?: number
|
|
pageSize?: number
|
|
keyword?: string
|
|
}): Promise<OrderPage> {
|
|
const resp = await api.get<ApiResponse<OrderPage>>('/admin/distribution/orders', {
|
|
params: {
|
|
kind: params.kind || undefined,
|
|
role: params.role || undefined,
|
|
status: params.status || undefined,
|
|
keyword: params.keyword || undefined,
|
|
site_id: params.siteID || undefined,
|
|
page: params.page || 1,
|
|
page_size: params.pageSize || 20,
|
|
},
|
|
})
|
|
return extract(resp)
|
|
}
|
|
|
|
export async function listAccountDisplays(siteID: number, status?: string): Promise<AccountDisplay[]> {
|
|
const resp = await api.get<ApiResponse<{ items: AccountDisplay[] }>>('/admin/distribution/accounts', {
|
|
params: { site_id: siteID, status: status || undefined },
|
|
})
|
|
return extract(resp).items ?? []
|
|
}
|
|
|
|
export async function updateAccountDisplay(id: number, displayStatus: string): Promise<AccountDisplay> {
|
|
const resp = await api.put<ApiResponse<AccountDisplay>>(`/admin/distribution/accounts/${id}/display`, {
|
|
display_status: displayStatus,
|
|
})
|
|
return extract(resp)
|
|
}
|
|
|
|
export async function syncSiteAccounts(siteID: number, req?: SyncSiteAccountsRequest): Promise<SyncSiteAccountsResult> {
|
|
const resp = await api.post<ApiResponse<SyncSiteAccountsResult>>(`/admin/distribution/sites/${siteID}/sync-accounts`, req ?? {})
|
|
return extract(resp)
|
|
}
|
|
|
|
export async function listPaymentChannels(siteID: number): Promise<PaymentChannel[]> {
|
|
const resp = await api.get<ApiResponse<{ items: PaymentChannel[] }>>('/admin/distribution/payment-channels', {
|
|
params: { site_id: siteID },
|
|
})
|
|
return extract(resp).items ?? []
|
|
}
|
|
|
|
export async function bindPaymentChannel(siteID: number, configID: number, priority: number): Promise<PaymentChannel> {
|
|
const resp = await api.post<ApiResponse<PaymentChannel>>(
|
|
'/admin/distribution/payment-channels',
|
|
{ config_id: configID, priority },
|
|
{ params: { site_id: siteID } },
|
|
)
|
|
return extract(resp)
|
|
}
|
|
|
|
export async function unbindPaymentChannel(id: number): Promise<void> {
|
|
await api.delete(`/admin/distribution/payment-channels/${id}`)
|
|
}
|
|
|
|
export async function listSettlementBatches(siteID: number): Promise<SettlementBatch[]> {
|
|
const resp = await api.get<ApiResponse<{ items: SettlementBatch[] }>>('/admin/distribution/settlement-batches', {
|
|
params: { site_id: siteID },
|
|
})
|
|
return extract(resp).items ?? []
|
|
}
|
|
|
|
export async function createSettlementBatch(siteID: number, periodStart: string, periodEnd: string): Promise<SettlementBatch> {
|
|
const resp = await api.post<ApiResponse<SettlementBatch>>('/admin/distribution/settlement-batches', {
|
|
site_id: siteID,
|
|
period_start: periodStart,
|
|
period_end: periodEnd,
|
|
})
|
|
return extract(resp)
|
|
}
|
|
|
|
export async function settleBatch(id: number): Promise<SettlementBatch> {
|
|
const resp = await api.post<ApiResponse<SettlementBatch>>(`/admin/distribution/settlement-batches/${id}/settle`)
|
|
return extract(resp)
|
|
}
|
|
|
|
export async function cancelSettlementBatch(id: number): Promise<SettlementBatch> {
|
|
const resp = await api.post<ApiResponse<SettlementBatch>>(`/admin/distribution/settlement-batches/${id}/cancel`)
|
|
return extract(resp)
|
|
}
|
|
|
|
export async function listSettlementItems(batchID: number): Promise<SettlementItem[]> {
|
|
const resp = await api.get<ApiResponse<{ items: SettlementItem[] }>>(`/admin/distribution/settlement-batches/${batchID}/items`)
|
|
return extract(resp).items ?? []
|
|
}
|
|
|
|
export interface ReceivedListing {
|
|
listing_id: number
|
|
listing_no: string
|
|
site_id: number
|
|
account_id: number
|
|
owner_id: number
|
|
owner_phone?: string
|
|
owner_nickname?: string
|
|
title: string
|
|
game_name: string
|
|
server_region: string
|
|
login_platform: string
|
|
rank_level: string
|
|
haf_coin_amount: number
|
|
seller_ratio?: number
|
|
seller_total_price_cent?: number
|
|
buyer_ratio?: number
|
|
price_cent: number
|
|
deposit_amount_cent: number
|
|
status: string
|
|
review_status: string
|
|
review_reason: string
|
|
published_at?: string
|
|
created_at: string
|
|
}
|
|
|
|
export interface ReceivedListingPage {
|
|
items: ReceivedListing[]
|
|
total: number
|
|
page: number
|
|
page_size: number
|
|
}
|
|
|
|
export async function fetchReceivedListings(params: {
|
|
siteID?: number
|
|
page?: number
|
|
pageSize?: number
|
|
status?: string
|
|
reviewStatus?: string
|
|
gameName?: string
|
|
keyword?: string
|
|
}): Promise<ReceivedListingPage> {
|
|
const resp = await api.get<ApiResponse<ReceivedListingPage>>('/admin/distribution/received-listings', {
|
|
params: {
|
|
site_id: params.siteID,
|
|
page: params.page || 1,
|
|
page_size: params.pageSize || 20,
|
|
status: params.status || undefined,
|
|
review_status: params.reviewStatus || undefined,
|
|
game_name: params.gameName || undefined,
|
|
keyword: params.keyword || undefined,
|
|
},
|
|
})
|
|
return extract(resp)
|
|
}
|
|
|
|
export async function updateReceivedListingStatus(id: number, status: string): Promise<void> {
|
|
await api.put(`/admin/distribution/received-listings/${id}/status`, { status })
|
|
}
|
|
|
|
export interface SiteWallet {
|
|
site_id: number
|
|
available_balance_cent: number
|
|
frozen_balance_cent: number
|
|
pending_release_cent: number
|
|
total_settled_cent: number
|
|
total_withdrawn_cent: number
|
|
settlement_hold_days?: number
|
|
}
|
|
|
|
export async function fetchSiteWallet(siteID: number): Promise<SiteWallet> {
|
|
const resp = await api.get<ApiResponse<SiteWallet>>(`/admin/distribution/sites/${siteID}/wallet`)
|
|
return extract(resp)
|
|
}
|
|
|
|
export interface SiteWithdrawal {
|
|
id: number
|
|
withdraw_no: string
|
|
site_id: number
|
|
amount_cent: number
|
|
fee_cent: number
|
|
actual_amount_cent: number
|
|
account_type: string
|
|
account_name: string
|
|
account_no: string
|
|
bank_name?: string
|
|
bank_branch?: string
|
|
status: string
|
|
review_remark?: string
|
|
payment_proof_url?: string
|
|
payment_remark?: string
|
|
paid_at?: string
|
|
created_at: string
|
|
}
|
|
|
|
export interface SiteWithdrawalPage {
|
|
items: SiteWithdrawal[]
|
|
total: number
|
|
page: number
|
|
page_size: number
|
|
}
|
|
|
|
export async function fetchSiteWithdrawals(siteID: number, page = 1, pageSize = 20): Promise<SiteWithdrawalPage> {
|
|
const resp = await api.get<ApiResponse<SiteWithdrawalPage>>(`/admin/distribution/sites/${siteID}/withdrawals`, {
|
|
params: { page, page_size: pageSize },
|
|
})
|
|
return extract(resp)
|
|
}
|
|
|
|
export interface ApplySiteWithdrawalPayload {
|
|
site_id?: number
|
|
amount_cent: number
|
|
account_type: 'alipay' | 'bank'
|
|
account_name: string
|
|
account_no: string
|
|
bank_name?: string
|
|
bank_branch?: string
|
|
}
|
|
|
|
export async function applySiteWithdrawal(payload: ApplySiteWithdrawalPayload): Promise<SiteWithdrawal> {
|
|
const resp = await api.post<ApiResponse<SiteWithdrawal>>(`/admin/distribution/sites/${payload.site_id}/withdrawals`, payload)
|
|
return extract(resp)
|
|
}
|
|
|
|
export interface SiteBrandingPayload {
|
|
logo_url?: string
|
|
favicon_url?: string
|
|
banners_json?: string
|
|
announcements_json?: string
|
|
footer_info_json?: string
|
|
aw_recycle_json?: string
|
|
cooperation_feedback_json?: string
|
|
storefront_text_json?: string
|
|
listing_qrcode_url?: string
|
|
}
|
|
|
|
export async function uploadSiteImage(file: File, scene: string): Promise<string> {
|
|
const form = new FormData()
|
|
form.append('file', file)
|
|
form.append('scene', scene)
|
|
const resp = await api.post<ApiResponse<{ url: string }>>('/admin/files/upload', form, {
|
|
headers: { 'Content-Type': 'multipart/form-data' },
|
|
})
|
|
return extract(resp).url
|
|
}
|
|
|
|
export async function updateSiteBranding(siteID: number, payload: SiteBrandingPayload): Promise<Site> {
|
|
const resp = await api.put<ApiResponse<Site>>(`/admin/distribution/sites/${siteID}/branding`, payload)
|
|
return extract(resp)
|
|
}
|
|
|
|
export async function updateAccountMarkup(id: number, shelfRatioDelta: number): Promise<AccountDisplay> {
|
|
const resp = await api.put<ApiResponse<AccountDisplay>>(`/admin/distribution/accounts/${id}/markup`, {
|
|
shelf_ratio_delta: shelfRatioDelta,
|
|
})
|
|
return extract(resp)
|
|
}
|
|
|
|
export interface BatchUpdateAccountMarkupItem {
|
|
id?: number
|
|
listing_id?: number
|
|
shelf_ratio_delta: number
|
|
}
|
|
|
|
export interface BatchUpdateAccountMarkupRequest {
|
|
listing_ids?: number[]
|
|
display_ids?: number[]
|
|
shelf_ratio_delta?: number
|
|
items?: BatchUpdateAccountMarkupItem[]
|
|
}
|
|
|
|
export async function batchUpdateAccountMarkup(
|
|
siteID: number,
|
|
reqOrListingIDs: BatchUpdateAccountMarkupRequest | number[],
|
|
shelfRatioDelta?: number,
|
|
): Promise<void> {
|
|
const payload: BatchUpdateAccountMarkupRequest = Array.isArray(reqOrListingIDs)
|
|
? { listing_ids: reqOrListingIDs, shelf_ratio_delta: shelfRatioDelta ?? 0 }
|
|
: reqOrListingIDs
|
|
|
|
await api.post(`/admin/distribution/accounts/batch-markup`, payload, {
|
|
params: { site_id: siteID },
|
|
})
|
|
}
|
|
|
|
export async function batchUpdateAccountDisplay(
|
|
siteID: number,
|
|
displayIDs: number[],
|
|
targetStatus: 'enabled' | 'disabled',
|
|
): Promise<void> {
|
|
await api.post(
|
|
`/admin/distribution/accounts/batch-display`,
|
|
{
|
|
display_ids: displayIDs,
|
|
target_status: targetStatus,
|
|
},
|
|
{
|
|
params: { site_id: siteID },
|
|
},
|
|
)
|
|
}
|
|
|
|
export interface DistributionAnalytics {
|
|
today_received_count: number
|
|
total_received_count: number
|
|
active_listing_count: number
|
|
today_order_count: number
|
|
today_gmv_cent: number
|
|
today_profit_cent: number
|
|
month_profit_cent: number
|
|
available_balance_cent: number
|
|
trend_days: {
|
|
date: string
|
|
received_count: number
|
|
order_count: number
|
|
gmv_cent: number
|
|
profit_cent: number
|
|
}[]
|
|
}
|
|
|
|
export async function fetchDashboardAnalytics(siteID: number): Promise<DistributionAnalytics> {
|
|
const resp = await api.get<ApiResponse<DistributionAnalytics>>(`/admin/distribution/sites/${siteID}/analytics`)
|
|
return extract(resp)
|
|
}
|
|
|
|
export interface SiteSMSConfig {
|
|
enabled: boolean
|
|
provider: string
|
|
access_key_id: string
|
|
masked_secret: string
|
|
has_secret: boolean
|
|
endpoint: string
|
|
sign_name: string
|
|
login_template_code: string
|
|
}
|
|
|
|
export interface UpdateSiteSMSConfigPayload {
|
|
enabled?: boolean
|
|
provider?: string
|
|
access_key_id?: string
|
|
access_key_secret?: string
|
|
endpoint?: string
|
|
sign_name?: string
|
|
login_template_code?: string
|
|
}
|
|
|
|
export interface TestSiteSMSConfigPayload {
|
|
phone: string
|
|
enabled?: boolean
|
|
provider?: string
|
|
access_key_id?: string
|
|
access_key_secret?: string
|
|
endpoint?: string
|
|
sign_name?: string
|
|
login_template_code?: string
|
|
}
|
|
|
|
export async function fetchSiteSMSConfig(siteID: number): Promise<SiteSMSConfig> {
|
|
const resp = await api.get<ApiResponse<SiteSMSConfig>>(`/admin/distribution/sites/${siteID}/sms-config`)
|
|
return extract(resp)
|
|
}
|
|
|
|
export async function updateSiteSMSConfig(siteID: number, payload: UpdateSiteSMSConfigPayload): Promise<SiteSMSConfig> {
|
|
const resp = await api.put<ApiResponse<SiteSMSConfig>>(`/admin/distribution/sites/${siteID}/sms-config`, payload)
|
|
return extract(resp)
|
|
}
|
|
|
|
export async function testSiteSMSConfig(siteID: number, payload: TestSiteSMSConfigPayload): Promise<{ message: string }> {
|
|
const resp = await api.post<ApiResponse<{ message: string }>>(`/admin/distribution/sites/${siteID}/sms-config/test`, payload)
|
|
return extract(resp)
|
|
}
|