classDiagram direction TB class TokenConfig { +TOKEN_SECRET: string +ACCESS_TOKEN_EXPIRY: number +REFRESH_TOKEN_EXPIRY: number } class AuthMiddleware { +authMiddleware(req, res, next): void -verifyHmacToken(token): userId -isTokenExpired(timestamp): boolean } class AuthRoute { +POST /login: LoginResult +POST /demo-login: LoginResult +POST /refresh: RefreshResult -signToken(userId): string -generateRefreshToken(userId): string -hashToken(token): string -autoSetAdmin(userId): void } class LoginResult { +accessToken: string +refreshToken: string +expiresIn: number +userId: number +nickname: string +avatar_url: string +role: string } class RefreshResult { +accessToken: string +refreshToken: string +expiresIn: number } class RefreshTokenRow { +id: number +user_id: number +token_hash: string +expires_at: Date +revoked_at: Date|null +created_at: Date } class DownloadTokenRow { +id: number +token: string +user_id: number +resource_type: string +resource_id: string +expires_at: Date +used_at: Date|null +created_at: Date } class DownloadTokenRoute { +POST /download-tokens: GenerateDownloadToken +GET /download/:token: FileStream -validateToken(token): DownloadTokenRow -markUsed(tokenId): void -cleanupExpired(): void } class AdminRoute { +GET /dashboard: DashboardData +GET /users: UserList +PUT /users/:id/status: void +DELETE /users/:id: void~~软删除 +POST /users/:id/restore: void~~恢复 } class UserRow { +id: number +openid: string +nickname: string +avatar_url: string +role: string +deleted_at: Date|null +created_at: Date } class ManageListComponent { +items: T[] +displayField: string +colorField: string +showDrag: boolean +showEdit: boolean +showDelete: boolean +emptyText: string +emit_add(): void +emit_edit(item): void +emit_delete(item): void +emit_sort(ids): void } class ColorPickerComponent { +modelValue: string +colors: string[] +emit_update:modelValue(color): void } class EditModalComponent { +visible: boolean +title: string +fields: EditField[] +emit_confirm(data): void } class SnackbarComponent { +message: string +actionText: string +duration: number +visible: boolean +emit_action(): void +show(msg, action): void +hide(): void } class OfflineQueue { -pendingOps: PendingOp[] -isProcessing: boolean +enqueue(op): void +dequeue(id): void +processPendingOps(): Promise~void~ +isOnline(): boolean +startNetworkListener(): void } class PendingOp { +id: string +type: string +data: object +createdAt: number +status: string } class SchedulerService { +start(): void +stop(): void -weeklyReportCron(): void -monthlyReportCron(): void -dailyBudgetScanCron(): void } class ReportService { +generateWeeklyReport(userId): WeeklyReport +generateMonthlyReport(userId): MonthlyReport -queryPeriodData(userId, start, end): PeriodData } class BudgetAlertService { +checkBudgetAlert(userId, month): void -calculateUsageRatio(userId, month): number -sendNotification(userId, level): void -sendWechatMessage(userId, level): void -hasAlerted(userId, month, level): boolean } class WechatSubscribeService { +sendMessage(userId, templateId, data): void -getAccessToken(): string } class BudgetAlertRow { +id: number +user_id: number +month: string +level: string +group_id: number|null +created_at: Date } class UserSettings { +user_id: number +report_push_enabled: boolean +updated_at: Date } class StatsRoute { +GET /overview: Overview +GET /category: CategoryStat[] +GET /trend: TrendPoint[] +GET /dashboard: DashboardData -buildWhereClause(userId, groupId): WhereResult } class DashboardData { +overview: Overview +category: CategoryStat[] +trend: TrendPoint[] } TokenConfig <-- AuthMiddleware : uses TokenConfig <-- AuthRoute : uses AuthRoute --> LoginResult : returns AuthRoute --> RefreshResult : returns AuthRoute --> RefreshTokenRow : creates/revokes DownloadTokenRoute --> DownloadTokenRow : creates/validates AdminRoute --> UserRow : soft deletes SchedulerService --> ReportService : triggers SchedulerService --> BudgetAlertService : triggers ReportService --> WechatSubscribeService : uses BudgetAlertService --> WechatSubscribeService : uses BudgetAlertService --> BudgetAlertRow : creates BudgetAlertService --> UserSettings : checks StatsRoute --> DashboardData : returns