feat(adtracking): 新增推广链接追踪与数据大盘,支持自定义短码、注册与下单归因及二维码物料生成

1. 数据库新增 000064_ad_tracking.sql 迁移,创建 ad_tracking_links 和 ad_tracking_clicks 表,并在 users、rental_orders 增加归因字段
2. 后端新增 adtracking 模块,挂载 GET /r/:code 302 智能重定向、点击流水异步记录、转化率/GMV聚合统计及管理接口
3. 用户认证 (auth) 与订单创建 (order) 链路贯通,实现注册用户与订单成交推广短码全链路归因
4. 前端管理后台左侧新增【推广追踪】目录,支持大盘指标总览、推广链接 CRUD、状态启停、一键复制短链及二维码下载 (PNG)
This commit is contained in:
yml2213
2026-09-10 11:05:47 +08:00
parent 33ee8408e9
commit 2b11d70ebe
25 changed files with 2304 additions and 36 deletions
@@ -0,0 +1,110 @@
import { apiClient } from '@/shared/api/client'
import type { ApiResponse } from '@/shared/types/types'
export interface TrackingLinkItem {
id: number
name: string
code: string
channel_type: string
target_url: string
short_url: string
status: 'active' | 'disabled'
cost_cent: number
notes: string
created_by: number
created_at: string
updated_at: string
click_pv: number
click_uv: number
click_ip: number
register_count: number
order_count: number
paid_order_count: number
paid_gmv_cent: number
conversion_rate: number
}
export interface TrackingOverviewStats {
total_links: number
active_links: number
total_pv: number
total_uv: number
total_ip: number
total_registers: number
total_orders: number
total_paid_orders: number
total_paid_gmv_cent: number
today_pv: number
today_uv: number
today_registers: number
today_orders: number
today_paid_gmv_cent: number
}
export interface ListTrackingLinksParams {
keyword?: string
channel_type?: string
status?: string
page?: number
page_size?: number
}
export interface CreateTrackingLinkPayload {
name: string
code: string
channel_type?: string
target_url?: string
cost_cent?: number
notes?: string
}
export interface UpdateTrackingLinkPayload {
name: string
channel_type?: string
target_url?: string
cost_cent?: number
notes?: string
}
export async function fetchTrackingOverview(): Promise<TrackingOverviewStats> {
const { data } = await apiClient.get<ApiResponse<TrackingOverviewStats>>(
'/admin/tracking-links/overview'
)
return data.data
}
export async function fetchTrackingLinks(
params: ListTrackingLinksParams
): Promise<{ items: TrackingLinkItem[]; total: number }> {
const { data } = await apiClient.get<ApiResponse<{ items: TrackingLinkItem[]; total: number }>>(
'/admin/tracking-links',
{ params }
)
return data.data
}
export async function createTrackingLink(
payload: CreateTrackingLinkPayload
): Promise<TrackingLinkItem> {
const { data } = await apiClient.post<ApiResponse<TrackingLinkItem>>(
'/admin/tracking-links',
payload
)
return data.data
}
export async function updateTrackingLink(
id: number,
payload: UpdateTrackingLinkPayload
): Promise<void> {
await apiClient.put(`/admin/tracking-links/${id}`, payload)
}
export async function updateTrackingLinkStatus(
id: number,
status: 'active' | 'disabled'
): Promise<void> {
await apiClient.put(`/admin/tracking-links/${id}/status`, { status })
}