6a3ce5d340
新版数据大盘页(dashboard/page.tsx, #24)引用的后端字段在 types.ts 未同步, next build 类型检查失败致 admin-web main 无法构建/部署(0.2.6 发车 deploy 卡在此步): - DashboardOverview 补 period 嵌套结构(date_from/to + users/comparison/coins/cash/trend), 对齐已部署后端 app/admin/schemas/dashboard.py 的 DashboardPeriod - AdRevenueReport 补可选 by_ad_type(按广告类型富小计含金币明细);后端当前不下发, 页面用 ?. 探测、缺失时按 items 现算兜底, 此处仅补类型声明让其编译通过 均为类型声明同步, 不改任何运行逻辑;本地 next build 已通过。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
663 lines
22 KiB
TypeScript
663 lines
22 KiB
TypeScript
// 与后端 app/admin/schemas/* 对齐的类型定义。
|
||
|
||
export interface AdminInfo {
|
||
id: number;
|
||
username: string;
|
||
role: string; // super_admin / finance / operator
|
||
status: string;
|
||
created_at: string;
|
||
last_login_at: string | null;
|
||
}
|
||
|
||
export interface LoginResponse {
|
||
access_token: string;
|
||
token_type: string;
|
||
expires_in: number;
|
||
admin: AdminInfo;
|
||
}
|
||
|
||
export interface CursorPage<T> {
|
||
items: T[];
|
||
next_cursor: number | null;
|
||
total?: number | null; // offset 分页时为符合筛选条件的总条数(页码分页用);加载更多接口可能没有
|
||
}
|
||
|
||
export interface UserListItem {
|
||
id: number;
|
||
phone: string;
|
||
nickname: string | null;
|
||
register_channel: string;
|
||
status: string;
|
||
// 调试链接权限:开了的用户在 App 比价结果页/记录页可复制 price.shaguabijia.com 的 trace 调试链接
|
||
debug_trace_enabled: boolean;
|
||
// 运营「一键开启新手引导」:true = 该用户下次打开 App 会被强制重走新手引导,走完自动取消
|
||
force_onboarding: boolean;
|
||
wechat_openid: string | null;
|
||
created_at: string;
|
||
last_login_at: string;
|
||
}
|
||
|
||
// 设备维度新手引导:一台设备(ANDROID_ID)聚合,走过引导的账号数 + 最近完成时间。
|
||
export interface DeviceOnboardingItem {
|
||
device_id: string;
|
||
account_count: number;
|
||
last_completed_at: string;
|
||
}
|
||
|
||
// 设备存活监控:device_liveness 表一行 + 后端按心跳阈值派生的在线/掉线/掉线时长。
|
||
export interface DeviceLivenessItem {
|
||
id: number;
|
||
user_id: number;
|
||
phone: string | null;
|
||
nickname: string | null;
|
||
device_id: string;
|
||
device_model: string | null; // 由 device_id 解析的机型
|
||
platform: string;
|
||
app_version: string | null;
|
||
registration_id: string | null; // 非空 = 可极光推送
|
||
ever_protected: boolean;
|
||
first_protected_at: string | null; // 首次开无障碍时刻(老设备为 null)
|
||
last_heartbeat_at: string | null;
|
||
last_report_protection_on: boolean;
|
||
liveness_state: string; // unknown / alive / silent(当前未用) / notified
|
||
notified_at: string | null;
|
||
kill_alert_pending: boolean; // 掉线召回待客户端 ack
|
||
created_at: string;
|
||
updated_at: string;
|
||
// 后端派生
|
||
online: boolean;
|
||
display_state: string; // online 在线 / offline 已掉线 / never 未启用
|
||
offline_seconds: number | null; // 掉线时长(秒);仅 offline 有值
|
||
}
|
||
|
||
export interface DeviceLivenessStats {
|
||
total: number;
|
||
online: number;
|
||
offline: number;
|
||
never: number;
|
||
}
|
||
|
||
export interface UserOverview {
|
||
user: UserListItem;
|
||
coin_balance: number;
|
||
cash_balance_cents: number;
|
||
total_coin_earned: number;
|
||
comparison_total: number;
|
||
comparison_success: number;
|
||
withdraw_total: number;
|
||
withdraw_success_cents: number;
|
||
feedback_total: number;
|
||
}
|
||
|
||
export interface CoinTxn {
|
||
id: number;
|
||
user_id: number;
|
||
amount: number;
|
||
balance_after: number;
|
||
biz_type: string;
|
||
ref_id: string | null;
|
||
remark: string | null;
|
||
created_at: string;
|
||
}
|
||
|
||
export interface CashTxn {
|
||
id: number;
|
||
user_id: number;
|
||
amount_cents: number;
|
||
balance_after_cents: number;
|
||
biz_type: string;
|
||
ref_id: string | null;
|
||
remark: string | null;
|
||
created_at: string;
|
||
}
|
||
|
||
export interface WithdrawOrder {
|
||
id: number;
|
||
user_id: number;
|
||
out_bill_no: string;
|
||
amount_cents: number;
|
||
user_name: string | null;
|
||
status: string; // reviewing / pending / success / failed / rejected
|
||
wechat_state: string | null;
|
||
transfer_bill_no: string | null;
|
||
fail_reason: string | null;
|
||
created_at: string;
|
||
updated_at: string;
|
||
}
|
||
|
||
// 提现列表项:WithdrawOrder + 列表接口联表带出的用户字段。
|
||
// 详情抽屉的 recent_withdraws 仍是裸 WithdrawOrder(无这些字段)。
|
||
export interface WithdrawListItem extends WithdrawOrder {
|
||
phone: string | null;
|
||
nickname: string | null;
|
||
cumulative_success_cents: number; // 累计成功提现(分)
|
||
}
|
||
|
||
export interface WithdrawSummary {
|
||
reviewing_count: number;
|
||
reviewing_amount_cents: number;
|
||
pending_count: number;
|
||
failed_count: number;
|
||
today_success_count: number;
|
||
today_success_amount_cents: number;
|
||
today_rejected_count: number;
|
||
}
|
||
|
||
export interface WithdrawUserSnapshot {
|
||
id: number;
|
||
phone: string;
|
||
nickname: string | null;
|
||
status: string;
|
||
wechat_nickname: string | null;
|
||
wechat_avatar_url: string | null;
|
||
created_at: string;
|
||
last_login_at: string;
|
||
cash_balance_cents: number;
|
||
withdraw_total: number;
|
||
withdraw_success_cents: number;
|
||
}
|
||
|
||
export interface WithdrawDetail {
|
||
order: WithdrawOrder;
|
||
user: WithdrawUserSnapshot | null;
|
||
risk_flags: string[];
|
||
risk_score: number;
|
||
recent_withdraws: WithdrawOrder[];
|
||
recent_cash_transactions: CashTxn[];
|
||
audit_logs: AuditLog[];
|
||
}
|
||
|
||
// 提现详情抽屉「用户统计区」:按时间窗口算的提现 + 看广告行为统计。
|
||
// *_cash_cents = 该来源累计金币折算成可提现现金(分);现金余额是当前快照不随窗口。
|
||
export interface UserRewardStats {
|
||
withdraw_success_cents: number; // 累计提现
|
||
cash_balance_cents: number; // 现金余额(快照)
|
||
withdraw_total: number; // 提现总次数(全部状态)
|
||
traditional_task_cash_cents: number; // 传统任务提现
|
||
reward_video_count: number; // 累计激励视频数
|
||
reward_video_avg_ecpm: number; // 平均激励视频 eCPM(分/千次)
|
||
reward_video_cash_cents: number; // 激励视频提现
|
||
feed_count: number; // 累计信息流广告数(份)
|
||
feed_avg_ecpm: number; // 平均信息流广告 eCPM(分/千次)
|
||
feed_cash_cents: number; // 信息流广告提现
|
||
}
|
||
|
||
// 金币发放记录(提现详情底部表)。ecpm 为原始分/千次,非广告来源为 null。
|
||
export interface UserCoinRecord {
|
||
source: string; // reward_video / signin_boost / feed / signin
|
||
source_label: string; // 激励视频 / 签到膨胀 / 信息流广告 / 签到
|
||
created_at: string;
|
||
ecpm: string | null;
|
||
coin: number;
|
||
}
|
||
|
||
export interface WithdrawBulkItemResult {
|
||
out_bill_no: string;
|
||
ok: boolean;
|
||
status: string | null;
|
||
error: string | null;
|
||
}
|
||
|
||
export interface WithdrawBulkResult {
|
||
total: number;
|
||
success: number;
|
||
failed: number;
|
||
items: WithdrawBulkItemResult[];
|
||
}
|
||
|
||
export interface WithdrawLedgerCheck {
|
||
ok: boolean;
|
||
cash_balance_total_cents: number;
|
||
cash_transaction_total_cents: number;
|
||
balance_diff_cents: number;
|
||
missing_withdraw_txn_count: number;
|
||
missing_refund_txn_count: number;
|
||
duplicate_refund_txn_count: number;
|
||
refund_txn_on_non_terminal_count: number;
|
||
}
|
||
|
||
export interface WxpayHealthCheck {
|
||
ok: boolean;
|
||
wxpay_configured: boolean;
|
||
wxpay_auth_configured: boolean;
|
||
private_key_path: string;
|
||
private_key_exists: boolean;
|
||
private_key_loadable: boolean;
|
||
public_key_path: string;
|
||
public_key_exists: boolean;
|
||
public_key_loadable: boolean;
|
||
auth_notify_url_configured: boolean;
|
||
auto_reconcile_enabled: boolean;
|
||
auto_reconcile_interval_sec: number;
|
||
auto_reconcile_older_than_minutes: number;
|
||
issues: string[];
|
||
}
|
||
|
||
export interface Feedback {
|
||
id: number;
|
||
user_id: number;
|
||
content: string;
|
||
contact: string;
|
||
images: string[] | null;
|
||
// pending(审核中) / adopted(已采纳) / rejected(未采纳);历史数据可能为 new
|
||
status: string;
|
||
reject_reason: string | null; // 未采纳原因(用户端可见)
|
||
reward_coins: number | null; // 采纳后发放金币
|
||
review_note: string | null; // 审核批注(采纳要点 / 内部备注)
|
||
reviewed_by_admin_id: number | null;
|
||
reviewed_at: string | null;
|
||
created_at: string;
|
||
// 提交端环境快照:提交版本号 / 机型OS版本;改版前的历史反馈为 null
|
||
app_version: string | null;
|
||
device_model: string | null;
|
||
rom_name: string | null;
|
||
android_version: string | null;
|
||
// 联表瞬态:展示完整手机号(点手机号查该用户全部反馈)
|
||
phone: string | null;
|
||
nickname: string | null;
|
||
}
|
||
|
||
// 反馈审核台顶部各状态计数(pending 含历史 new 态)。后端 GET /admin/api/feedbacks/summary。
|
||
export interface FeedbackSummary {
|
||
pending: number;
|
||
adopted: number;
|
||
rejected: number;
|
||
total: number;
|
||
}
|
||
|
||
// 反馈页「加群二维码」卡配置(运营后台改 → App 意见反馈页同步)。
|
||
// image_url 为后端 /media 相对路径(留空=App 走本地兜底图);其余为卡片三行文案与显隐开关。
|
||
export interface FeedbackQrConfig {
|
||
enabled: boolean;
|
||
image_url: string | null;
|
||
title: string; // 第一行(操作提示),如「长按图片保存二维码」
|
||
group_name: string; // 第二行加粗群名,App 渲染为 直通「{group_name}」
|
||
subtitle: string; // 第三行(标语)
|
||
updated_at: string | null;
|
||
}
|
||
|
||
export interface AuditLog {
|
||
id: number;
|
||
admin_id: number;
|
||
admin_username: string;
|
||
action: string;
|
||
target_type: string;
|
||
target_id: string | null;
|
||
detail: Record<string, unknown> | null;
|
||
ip: string | null;
|
||
created_at: string;
|
||
}
|
||
|
||
// 广告收益报表聚合行下钻的单条展示明细
|
||
// ===== 比价记录(admin debug 页)=====
|
||
export interface ComparisonRecordListItem {
|
||
id: number;
|
||
user_id: number;
|
||
phone: string | null;
|
||
nickname: string | null;
|
||
business_type: string;
|
||
trace_id: string;
|
||
trace_url: string | null;
|
||
status: string;
|
||
information: string | null;
|
||
store_name: string | null;
|
||
source_platform_name: string | null;
|
||
best_platform_name: string | null;
|
||
source_price_cents: number | null;
|
||
best_price_cents: number | null;
|
||
saved_amount_cents: number | null;
|
||
total_ms: number | null;
|
||
step_count: number | null;
|
||
llm_call_count: number | null;
|
||
retry_count: number | null;
|
||
input_tokens: number | null;
|
||
output_tokens: number | null;
|
||
device_model: string | null;
|
||
rom_vendor: string | null;
|
||
rom_name: string | null;
|
||
android_version: string | null;
|
||
app_version: string | null;
|
||
created_at: string;
|
||
}
|
||
|
||
// 单次 LLM 调用明细(pricebot chat() 收口落盘,server 按 trace 拉来)
|
||
export interface LlmCall {
|
||
ts?: number;
|
||
scene: string;
|
||
model: string;
|
||
input_messages: { role: string; content: string }[];
|
||
output: string | null;
|
||
usage: { prompt_tokens?: number; completion_tokens?: number; total_tokens?: number } | null;
|
||
latency_ms: number | null;
|
||
error: string | null;
|
||
}
|
||
|
||
export interface ComparisonRecordDetail extends ComparisonRecordListItem {
|
||
source_platform_id: string | null;
|
||
source_package: string | null;
|
||
best_platform_id: string | null;
|
||
best_deeplink: string | null;
|
||
is_source_best: boolean | null;
|
||
total_dish_count: number | null;
|
||
skipped_dish_count: number | null;
|
||
device_id: string | null;
|
||
items: { name: string; qty?: number; specs?: string[] }[];
|
||
comparison_results: Record<string, unknown>[];
|
||
skipped_dish_names: string[];
|
||
device_manufacturer: string | null;
|
||
rom_version: number | null;
|
||
android_sdk: number | null;
|
||
app_version_code: number | null;
|
||
source_app_version: string | null;
|
||
longitude: number | null;
|
||
latitude: number | null;
|
||
llm_calls: LlmCall[] | null;
|
||
raw_payload: Record<string, unknown> | null;
|
||
}
|
||
|
||
export interface AdRevenueImpression {
|
||
id: number;
|
||
created_at: string;
|
||
ecpm: string; // 分/千次展示
|
||
revenue_yuan: number; // 本次展示预估收益(元)
|
||
adn: string | null;
|
||
slot_id: string | null; // 底层 mediation rit
|
||
}
|
||
|
||
// 广告收益报表聚合行下钻的单条发奖复算明细
|
||
export interface AdRevenueRecord {
|
||
record_id: number;
|
||
created_at: string;
|
||
status: string; // granted / capped / ecpm_missing / too_short / closed_early
|
||
ecpm: string | null;
|
||
ecpm_factor: number | null; // 因子1
|
||
units: number; // 份数
|
||
lt_index_start: number | null;
|
||
lt_index_end: number | null;
|
||
lt_factor_start: number | null; // 因子2
|
||
lt_factor_end: number | null;
|
||
expected_coin: number;
|
||
actual_coin: number;
|
||
matched: boolean;
|
||
}
|
||
|
||
// 广告收益报表:按日期汇总的一天(按天趋势图用;全量,不受分页影响)
|
||
export interface AdRevenueDaily {
|
||
date: string; // 北京时间 YYYY-MM-DD
|
||
impressions: number;
|
||
revenue_yuan: number; // 客户端预估收益(eCPM 折算)
|
||
pangle_revenue_yuan: number | null; // 穿山甲后台预估收益(GroMore revenue);非全量视图/无数据为 null
|
||
pangle_api_revenue_yuan: number | null; // 穿山甲收益API(更接近结算);未配/当天/无数据为 null
|
||
expected_coin: number;
|
||
actual_coin: number;
|
||
}
|
||
|
||
// 广告收益报表:按北京小时(0–23)汇总的一小时(按小时趋势图用;全量,不受分页影响;按天查询时为空)
|
||
export interface AdRevenueHourly {
|
||
hour: number; // 北京时间小时 0–23
|
||
impressions: number;
|
||
revenue_yuan: number;
|
||
expected_coin: number;
|
||
actual_coin: number;
|
||
}
|
||
|
||
// 广告收益报表:按广告类型(ad_type)的小计(展示条数 + 预估收益;eCPM 前端用 收益÷展示×1000 算)
|
||
export interface AdRevenueTypeStat {
|
||
impressions: number; // 该类型展示条数合计
|
||
revenue_yuan: number; // 该类型预估收益合计(元)
|
||
}
|
||
|
||
// 广告收益报表:一次广告事件(逐条一行)。激励视频展示+发奖按 ad_session_id 合并;信息流展示/发奖各自成行。
|
||
export interface AdRevenueRow {
|
||
event_key: string; // 事件稳定唯一键(前端 rowKey)
|
||
report_date: string; // 该事件所属日期 北京时间 YYYY-MM-DD
|
||
user_id: number;
|
||
user_phone: string | null; // 用户手机号(admin 展示;查不到为空)
|
||
ad_type: string; // reward_video / feed / draw
|
||
feed_scene?: string | null; // Draw 信息流投放场景 comparison(比价) / coupon(领券) / welfare(福利);非信息流或旧数据为 null
|
||
app_env: string | null; // prod(傻瓜比价) / test(测试);旧数据为空
|
||
our_code_id: string | null; // 我们配置的代码位 104xxx;旧数据为空
|
||
hour: number | null; // 北京时间小时 0–23(按小时粒度时有值;按天为 null)
|
||
created_at: string; // 事件时间(有展示=展示时间,纯发奖=发奖时间)
|
||
// ── 展示侧 ──
|
||
has_impression: boolean; // 是否有广告展示(信息流逐条展示=true,纯发奖行=false)
|
||
impressions: number; // 本行展示条数 1/0(供日汇总、趋势图复用)
|
||
ecpm: string | null; // 分/千次;展示行取展示值,纯发奖行取发奖采用值
|
||
revenue_yuan: number; // 本次展示预估收益(元);纯发奖行=0
|
||
adn: string | null; // 实际填充 ADN;纯发奖行为空
|
||
slot_id: string | null; // 底层 mediation rit;纯发奖行为空
|
||
// ── 发奖侧 ──
|
||
has_reward: boolean; // 是否有发奖(激励视频合并行/信息流整场发奖行=true;纯展示=false)
|
||
status: string | null; // 发奖状态 granted/closed_early/…;纯展示为空
|
||
expected_coin: number; // 应发金币;纯展示=0
|
||
actual_coin: number; // 实发金币;纯展示=0
|
||
matched: boolean; // 本条应发==实发;纯展示恒 true(不计对账)
|
||
reward_detail: AdRevenueRecord | null; // 发奖复算明细(点行展开下钻);纯展示为空
|
||
}
|
||
|
||
export interface AdRevenueReport {
|
||
date_from: string; // 起始日 北京时间 YYYY-MM-DD
|
||
date_to: string; // 结束日 北京时间 YYYY-MM-DD(闭区间;单日时与 date_from 相同)
|
||
daily: AdRevenueDaily[]; // 按日期汇总序列(全量,供按天趋势图)
|
||
hourly: AdRevenueHourly[]; // 按小时汇总序列(全量,供按小时趋势图;按天查询时为空)
|
||
type_stats: Record<string, AdRevenueTypeStat>; // 按广告类型小计;前端取 draw / reward_video
|
||
// 可选:后端预聚合的「按广告类型」富小计(含发放金币明细)。当前后端未下发 → 数据大盘页
|
||
// (dashboard adTypeSummary)用 `?.` 探测、缺失时按 items 现算兜底;后端补上即自动启用。
|
||
by_ad_type?: Record<
|
||
string,
|
||
{ ad_type: string; impressions: number; revenue_yuan: number; expected_coin: number; actual_coin: number }
|
||
>;
|
||
dau: number | null; // 今日活跃(复用大盘 last_login_at);仅查询=今日时有值,否则 null
|
||
total: number; // 当前筛选下的分页总条数(全量,不受分页影响)
|
||
truncated: boolean; // 当前页之后是否还有更多事件(分页后前端不再据此报警)
|
||
total_impressions: number;
|
||
total_revenue_yuan: number; // 客户端预估收益合计(eCPM 折算)
|
||
total_pangle_revenue_yuan: number | null; // 穿山甲后台预估收益合计(GroMore revenue);非全量视图/无数据为 null
|
||
total_pangle_api_revenue_yuan: number | null; // 穿山甲收益API合计(更接近结算);未配/当天/非全量视图为 null
|
||
pangle_revenue_available: boolean; // 本次结果是否带穿山甲后台收益(全量视图且已同步到数据)
|
||
total_expected_coin: number;
|
||
total_actual_coin: number;
|
||
mismatch_count: number; // 应发≠实发的发奖条数
|
||
items: AdRevenueRow[];
|
||
}
|
||
|
||
export interface DashboardOverview {
|
||
users: {
|
||
total: number;
|
||
active: number;
|
||
disabled: number;
|
||
deleted: number;
|
||
new_today: number;
|
||
dau: number;
|
||
};
|
||
coins: {
|
||
reward_video_coin_total: number;
|
||
reward_video_watch_count: number;
|
||
feed_ad_coin_total: number;
|
||
feed_ad_watch_count: number;
|
||
signin_coin_total: number;
|
||
signin_count: number;
|
||
signin_boost_coin_total: number;
|
||
signin_boost_watch_count: number;
|
||
granted_total: number;
|
||
};
|
||
cash: {
|
||
withdraw_success_cents: number;
|
||
withdraw_pending_count: number;
|
||
withdraw_success_count: number;
|
||
withdraw_failed_count: number;
|
||
};
|
||
comparison: { total: number; success: number; success_rate: number };
|
||
// 时段大盘(对应后端 app/admin/schemas/dashboard.py DashboardPeriod);新版数据大盘页用
|
||
period: {
|
||
date_from: string;
|
||
date_to: string;
|
||
users: {
|
||
new: number;
|
||
active: number;
|
||
retained_new_users: number;
|
||
retention_rate: number | null;
|
||
retention_note: string;
|
||
};
|
||
comparison: {
|
||
total: number;
|
||
success: number;
|
||
success_rate: number;
|
||
ordered: number;
|
||
average_duration_ms: number | null;
|
||
average_saved_cents: number | null;
|
||
};
|
||
coins: {
|
||
granted_total: number;
|
||
reward_video_coin_total: number;
|
||
feed_ad_coin_total: number;
|
||
signin_coin_total: number;
|
||
signin_boost_coin_total: number;
|
||
task_coin_total: number;
|
||
coupon_reward_coin_total: number;
|
||
comparison_reward_coin_total: number;
|
||
regular_task_coin_total: number;
|
||
};
|
||
cash: { withdraw_success_cents: number };
|
||
trend: {
|
||
date: string;
|
||
active_users: number;
|
||
new_users: number;
|
||
comparisons: number;
|
||
}[];
|
||
};
|
||
feedback: { new: number };
|
||
cps: {
|
||
available: boolean;
|
||
note: string;
|
||
meituan_order_count: number;
|
||
meituan_commission_cents: number;
|
||
meituan_hit_count: number;
|
||
meituan_miss_count: number;
|
||
meituan_unknown_rate_count: number;
|
||
meituan_hit_rate: number | null;
|
||
};
|
||
}
|
||
|
||
export interface PriceReport {
|
||
id: number;
|
||
user_id: number;
|
||
comparison_record_id: number | null;
|
||
store_name: string | null;
|
||
dish_summary: string | null;
|
||
original_platform_id: string | null;
|
||
original_platform_name: string | null;
|
||
original_price_cents: number | null;
|
||
reported_platform_id: string;
|
||
reported_platform_name: string;
|
||
reported_price_cents: number;
|
||
images: string[];
|
||
status: string; // pending / approved / rejected
|
||
reject_reason: string | null;
|
||
reward_coins: number | null;
|
||
reviewed_at: string | null;
|
||
created_at: string;
|
||
// 联表瞬态:phone/nickname 展示完整手机号(点手机号查该用户全部上报);
|
||
// 其余取自关联比价记录(无关联记录时为 null):trace 调试链接、机型OS版本、提交版本号
|
||
phone: string | null;
|
||
nickname: string | null;
|
||
trace_id: string | null;
|
||
trace_url: string | null;
|
||
device_model: string | null; // 机型,如 PEEM00
|
||
rom_name: string | null; // ROM/OS 名,如 ColorOS / MIUI / HarmonyOS
|
||
android_version: string | null; // Android 版本号,如 13
|
||
app_version: string | null; // 提交时我们 app 的 versionName,如 1.2.3
|
||
}
|
||
|
||
export interface PriceReportSummary {
|
||
pending: number;
|
||
approved: number;
|
||
rejected: number;
|
||
total: number;
|
||
}
|
||
|
||
// ===== CPS 分发与对账 =====
|
||
export interface CpsGroup {
|
||
id: number;
|
||
sid: string | null; // 仅美团群有(渠道追踪位)
|
||
name: string;
|
||
platforms: string[]; // meituan/taobao/jd 多选
|
||
member_count: number | null;
|
||
status: string; // active / archived
|
||
remark: string | null;
|
||
created_at: string;
|
||
}
|
||
|
||
export interface CpsActivity {
|
||
id: number;
|
||
platform: string; // meituan / taobao / jd
|
||
name: string;
|
||
act_id: string | null; // 美团活动物料 ID
|
||
product_view_sign: string | null;
|
||
payload: string | null; // 淘宝整段淘口令 / 京东链接
|
||
image_url: string | null; // 淘宝落地页主视觉图(绝对 URL)
|
||
status: string;
|
||
remark: string | null;
|
||
created_at: string;
|
||
}
|
||
|
||
export interface CpsOrder {
|
||
id: number;
|
||
order_id: string;
|
||
sid: string | null;
|
||
act_id: string | null;
|
||
pay_price_cents: number | null;
|
||
commission_cents: number | null;
|
||
commission_rate: string | null; // "300"=3% "10"=0.1%
|
||
mt_status: string | null; // 2付款 3完成 4取消 5风控 6结算
|
||
invalid_reason: string | null;
|
||
product_name: string | null;
|
||
pay_time: string | null;
|
||
}
|
||
|
||
export interface CpsReferralLinkItem {
|
||
activity_id: number;
|
||
activity_name: string;
|
||
platform: string;
|
||
redirect_url: string; // 我们的落地页 /c/{code}(发群用)
|
||
code: string;
|
||
}
|
||
|
||
export interface CpsReferralLinks {
|
||
group_name: string;
|
||
results: CpsReferralLinkItem[];
|
||
}
|
||
|
||
export interface CpsReconcileResult {
|
||
fetched: number;
|
||
inserted: number;
|
||
updated: number;
|
||
pages: number;
|
||
}
|
||
|
||
export interface CpsGroupStat {
|
||
group_id: number | null;
|
||
sid: string | null;
|
||
name: string;
|
||
platforms: string[];
|
||
member_count: number | null;
|
||
click_pv: number;
|
||
click_uv: number;
|
||
copy_pv: number; // 淘宝"复制口令"次数
|
||
copy_uv: number;
|
||
// 对账类:淘宝/京东无法对账 → null(前端显示 "-")
|
||
order_count: number | null;
|
||
settled_count: number | null;
|
||
canceled_count: number | null;
|
||
gmv_cents: number | null;
|
||
est_commission_cents: number | null;
|
||
settled_commission_cents: number | null;
|
||
}
|
||
|
||
export interface CpsStats {
|
||
groups: CpsGroupStat[];
|
||
total_order_count: number;
|
||
total_est_commission_cents: number;
|
||
total_settled_commission_cents: number;
|
||
}
|