feat(health): 线下活动管理端 CRUD + 积分统计 API + 前端页面 (Chunk 4)
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled

后端:
- 线下活动管理: create/update/delete/list/checkin 5 个管理端接口
- 活动签到自动发放积分 (事务内原子操作)
- 积分统计 API: 总发放/总消耗/总过期/活跃账户/Top10排行

前端:
- OfflineEventList: 活动管理页面 (创建/编辑/删除/状态筛选)
- points.ts 扩展: 线下活动 + 统计 API 方法
- 侧边栏新增线下活动入口
This commit is contained in:
iven
2026-04-25 17:34:54 +08:00
parent eb937d3d02
commit 7b18a7398d
8 changed files with 987 additions and 0 deletions

View File

@@ -75,6 +75,49 @@ export interface VerifyOrderReq {
qr_code: string;
}
export interface OfflineEvent {
id: string;
title: string;
description: string | null;
event_date: string;
start_time: string | null;
end_time: string | null;
location: string | null;
points_reward: number;
max_participants: number;
current_participants: number;
status: string; // draft / published / ongoing / completed / cancelled
image_url: string | null;
created_at: string;
updated_at: string;
version: number;
}
export interface CreateOfflineEventReq {
title: string;
description?: string;
event_date: string;
start_time?: string;
end_time?: string;
location?: string;
points_reward?: number;
max_participants?: number;
status?: string;
image_url?: string;
}
export interface PointsStatistics {
total_issued: number;
total_spent: number;
total_expired: number;
active_accounts: number;
top_earners: Array<{
account_id: string;
patient_id: string;
total_earned: number;
}>;
}
// --- API ---
export const pointsApi = {
@@ -128,4 +171,44 @@ export const pointsApi = {
}>('/health/points/verify', req);
return data.data;
},
// Offline Events
listOfflineEvents: async (params?: Record<string, unknown>) => {
const { data } = await client.get<{
success: boolean;
data: PaginatedResponse<OfflineEvent>;
}>('/health/admin/offline-events', { params });
return data.data;
},
createOfflineEvent: async (req: CreateOfflineEventReq) => {
const { data } = await client.post<{
success: boolean;
data: OfflineEvent;
}>('/health/admin/offline-events', req);
return data.data;
},
updateOfflineEvent: async (id: string, req: Partial<CreateOfflineEventReq> & { version: number }) => {
const { data } = await client.put<{
success: boolean;
data: OfflineEvent;
}>(`/health/admin/offline-events/${id}`, req);
return data.data;
},
deleteOfflineEvent: async (id: string, version: number) => {
await client.delete(`/health/admin/offline-events/${id}`, {
data: { version },
});
},
// Points Statistics
getStatistics: async () => {
const { data } = await client.get<{
success: boolean;
data: PointsStatistics;
}>('/health/admin/points/statistics');
return data.data;
},
};