Files
hfb_sys/frontend/src/features/admin/api/adminDashboard.ts
T
yml2213 cca40940d5 feat(admin): 新增客服手动录单功能与仪表盘今日录单统计
- 做了什么:
  - 新增客服手动录单数据库迁移 000084 及对应模型,支持编号唯一校验、客服快照绑定与 CRUD 操作;
  - 后端提供 csmanualentry 业务模块及相关 API,并在仪表盘摘要中聚合今日各客服录单数量;
  - 前端增加客服手动录单视图页面、路由与侧边栏导航,支持录单二次确认与细粒度权限控制;
  - 管理后台仪表盘增加今日客服手动录单卡片展示。
- 为什么:
  - 满足客服在后台手动登记外部业务编号需求,并便于运营按客服统计今日录单业绩和追溯对账。
- 验证方式:
  - 执行后端全量测试 go test ./... 与静态检查 go vet ./... 全部通过;
  - 执行前端代码门禁 npm run check 与 vite build 全部通过;
  - 执行 goose down/up 迁移回滚与应用测试通过,角色权限分配正确;
  - 本地 dev 联调 POST /api/admin/manual-entries 录单成功返回 201。
2026-10-12 14:52:24 +08:00

119 lines
3.0 KiB
TypeScript

import { apiClient } from '@/shared/api/client'
import type { ApiResponse } from '@/shared/types/types'
import type { DisputeStatus, OrderStatus } from '@/shared/types/status'
export interface DashboardMetrics {
total_users: number
verified_users: number
total_listings: number
published_listings: number
total_orders: number
renting_orders: number
today_orders: number
today_ledger_amount_cent: number
}
export interface DashboardPending {
listing_reviews: number
disputes: number
pending_handoffs: number
pending_return_confirms: number
}
export interface ListingDayStats {
date: string
published_count: number
active_offline_count: number
trade_leave_count: number
}
export interface ListingChannelDayStats extends ListingDayStats {
source_channel: string
}
export interface ListingDailyOverview {
today: ListingDayStats
trend: ListingDayStats[]
today_channels: ListingChannelDayStats[]
channel_trend: ListingChannelDayStats[]
today_uploaders: ListingUploaderDayStats[]
uploader_trend: ListingUploaderDayStats[]
days: number
timezone: string
}
export interface ListingUploaderDayStats {
date: string
uploader_id: number
uploader_name: string
upload_count: number
}
export interface DashboardRecentOrder {
id: number
order_no: string
title: string
renter_id: number
owner_id: number
status: OrderStatus
rent_amount_cent: number
deposit_amount_cent: number
created_at: string
}
export interface DashboardRecentDispute {
id: number
order_id: number
order_no: string
title: string
type: string
status: DisputeStatus
initiator_id: number
created_at: string
}
export interface ManualEntryTodayStats {
admin_id: number
admin_name: string
count: number
}
export interface AdminDashboard {
metrics: DashboardMetrics
pending: DashboardPending
listing_daily: ListingDailyOverview
manual_entry_today: ManualEntryTodayStats[]
recent_orders: DashboardRecentOrder[]
recent_disputes: DashboardRecentDispute[]
generated_at: string
}
const emptyDayStats = (): ListingDayStats => ({
date: '',
published_count: 0,
active_offline_count: 0,
trade_leave_count: 0,
})
export async function fetchAdminDashboard() {
const { data } = await apiClient.get<ApiResponse<AdminDashboard>>('/admin/dashboard')
const payload = data.data
const listingDaily = payload.listing_daily
return {
...payload,
listing_daily: {
today: listingDaily?.today ?? emptyDayStats(),
trend: listingDaily?.trend ?? [],
today_channels: listingDaily?.today_channels ?? [],
channel_trend: listingDaily?.channel_trend ?? [],
today_uploaders: listingDaily?.today_uploaders ?? [],
uploader_trend: listingDaily?.uploader_trend ?? [],
days: listingDaily?.days ?? 7,
timezone: listingDaily?.timezone ?? 'Asia/Shanghai',
},
manual_entry_today: payload.manual_entry_today ?? [],
recent_orders: payload.recent_orders ?? [],
recent_disputes: payload.recent_disputes ?? [],
}
}