### **轻量化账号管理平台 - 工程设计蓝图** #### 1. 核心设计哲学 * **单表驱动**: 所有核心数据聚合在 `accounts` 表,简化数据库模型。 * **OwnerID 即身份**: `ownerId` 同时作为拥有者标识和API认证凭证。 * **配置硬编码**: 关键状态(如 `available`, `locked`)硬编码在业务逻辑中,降低系统复杂性。 * **职责分离的API**: 为自动化脚本和管理后台提供两套独立的、高度优化的API。 * **规范化通信**: 所有API遵循统一的响应结构,业务结果通过 `code` 字段传递。 * **高性能与健壮性**: 依赖PostgreSQL原生特性(事务、行锁、索引)和专用的后台任务确保系统稳定。 #### 2. 技术栈 * **运行时**: Node.js (v18+) * **语言**: TypeScript * **Web 框架**: **Fastify** * **数据库**: PostgreSQL (v14+) * **ORM/查询构建器**: **Drizzle ORM** * **数据校验**: **Zod** #### 3. 项目结构 采用模块化、可扩展的目录结构,清晰分离各项职责。 ``` . ├── drizzle/ # Drizzle ORM 迁移文件 ├── src/ │ ├── api/ # API 路由定义 │ │ └── v1/ │ │ ├── web/ # 前端管理后台 API │ │ │ ├── accounts.ts # 账号增删改查 │ │ │ └── stats.ts # 统计数据 │ │ └── script/ # 自动化脚本 API │ │ └── actions.ts # 获取、更新、上传 │ ├── core/ # 核心业务逻辑 (Services) │ │ └── AccountService.ts # 封装所有与账号相关的业务操作 │ ├── db/ # 数据库配置与 Schema │ │ ├── index.ts # Drizzle 客户端实例 │ │ └── schema.ts # 数据库表定义 │ ├── jobs/ # 后台定时任务 │ │ └── staleLockCleanup.ts # 清理超时锁定的任务 │ ├── lib/ # 通用库、工具函数 │ │ └── apiResponse.ts # 统一响应格式的辅助函数 │ ├── types/ # 全局类型定义 │ │ ├── api.ts # API 请求/响应体类型 │ │ └── index.ts # 核心业务模型类型 │ ├── index.ts # 应用入口,启动 Fastify 服务器 │ └── config.ts # 应用配置 (数据库连接、端口等) ├── .env # 环境变量 ├── .env.example ├── package.json └── tsconfig.json ``` #### 4. 数据库设计 (单表模型) ```typescript // file: src/db/schema.ts import { pgTable, serial, varchar, timestamp, uniqueIndex, text, index } from 'drizzle-orm/pg-core'; export const accounts = pgTable('accounts', { id: serial('id').primaryKey(), ownerId: varchar('owner_id', { length: 128 }).notNull(), platform: varchar('platform', { length: 100 }).notNull(), customId: varchar('custom_id', { length: 255 }).notNull(), data: text('data').notNull(), status: varchar('status', { length: 50 }).notNull(), notes: text('notes'), lockedAt: timestamp('locked_at'), createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), }, (table) => ({ platformCustomIdIdx: uniqueIndex('platform_custom_id_idx').on(table.platform, table.customId), ownerIdStatusIdx: index('owner_id_status_idx').on(table.ownerId, table.status), platformOwnerIdx: index('platform_owner_idx').on(table.platform, table.ownerId), })); ``` #### 5. 核心类型与接口定义 ##### 5.1. 业务模型 ```typescript // file: src/types/index.ts import { accounts } from '../db/schema'; import { InferSelectModel } from 'drizzle-orm'; // 从数据库 Schema 自动推断出的 Account 类型,确保与数据库一致 export type Account = InferSelectModel; ``` ##### 5.2. API 通信规范 ```typescript // file: src/types/api.ts /** * 业务状态码枚举,避免使用魔法数字 */ export enum BusinessCode { Success = 0, NoResource = 1001, InvalidParams = 2001, ResourceConflict = 3001, PermissionDenied = 4001, // 业务层权限拒绝 BusinessError = 5001, } /** * 统一的 API 响应体结构 */ export interface ApiResponse { code: BusinessCode; message: string; data: T | null; } /** * 分页查询结果 */ export interface PaginationResult { page: number; pageSize: number; total: number; totalPages: number; } ``` ##### 5.3. API 请求/响应体类型 ```typescript // file: src/types/api.ts // --- 脚本 API --- export type ScriptAcquireResponse = Pick[]; export interface ScriptUploadItem { platform: string; customId: string; data: string; status?: string; } // --- 前端管理 API --- export interface ListAccountsFilters { platform?: string; status?: string[]; ownerId?: string; search?: string; // 模糊搜索 customId 或 notes } export interface ListAccountsBody { filters: ListAccountsFilters; pagination: { page: number; pageSize: number; }; sort: { field: keyof Account; order: 'asc' | 'desc'; }; } export interface ListAccountsResponse { list: Account[]; pagination: PaginationResult; } export interface BatchDeleteBody { ids: number[]; } export interface BatchUpdateBody { ids: number[]; payload: Partial>; } export interface StatsOverview { totalAccounts: number; platformSummary: Record; ownerSummary: Record; statusSummary: Record; detailedBreakdown: { platform: string; ownerId: string; status: string; count: number; }[]; } ``` --- ### **6. API 设计** #### A. 脚本专用 API (Base URL: `/s/v1/{ownerId}`) **认证**: 所有请求通过 URL 中的 `{ownerId}` 进行身份验证和授权。 1. **获取账号** * **Endpoint**: `GET /acquire` * **描述**: 获取一个或多个可用的账号,并将其状态锁定。 * **Query Params**: * `platform` (string, **必须**): 平台标识。 * `count` (number, 可选, 默认 1): 获取数量。 * **成功响应 (200 OK)**: ```json // Body: ApiResponse { "code": 0, // BusinessCode.Success "message": "Successfully acquired 2 accounts.", "data": [ { "id": 101, "customId": "user1@gmail.com", "data": "cookie_string_1" }, { "id": 102, "customId": "user2@gmail.com", "data": "cookie_string_2" } ] } ``` * **业务失败响应 (200 OK)**: ```json // Body: ApiResponse { "code": 1001, // BusinessCode.NoResource "message": "No available accounts found for platform 'google'.", "data": null } ``` 2. **更新账号状态** * **Endpoint**: `GET /update/{accountId}/{newStatus}` * **描述**: 快速更新指定账号的状态。 * **Query Params**: * `notes` (string, 可选): 添加备注信息。 * **成功响应 (200 OK)**: ```json // Body: ApiResponse<{ updatedId: number }> { "code": 0, "message": "Account status updated to 'banned'.", "data": { "updatedId": 12345 } } ``` * **传输错误响应 (403 Forbidden)**: 当 URL 中的 `ownerId` 与 `accountId` 对应的账号所有者不匹配时返回。 3. **上传/更新账号** * **Endpoint**: `POST /upload` * **描述**: 批量创建或更新账号。 * **Request Body**: `ScriptUploadItem[]` * **成功响应 (200 OK)**: ```json // Body: ApiResponse<{ processedCount: number, createdCount: number, updatedCount: number }> { "code": 0, "message": "Successfully processed 10 accounts (5 created, 5 updated).", "data": { "processedCount": 10, "createdCount": 5, "updatedCount": 5 } } ``` #### B. 前端管理 API (Base URL: `/web/v1`) 1. **获取账号列表 (复杂查询)** * **Endpoint**: `POST /accounts/list` * **描述**: 支持多维度筛选、分页和排序的账号查询。 * **Request Body**: `ListAccountsBody` * **成功响应 (200 OK)**: ```json // Body: ApiResponse { "code": 0, "message": "Success", "data": { "list": [ /* ... Account 对象数组 ... */ ], "pagination": { "page": 1, "pageSize": 50, "total": 123, "totalPages": 3 } } } ``` 2. **批量删除账号** * **Endpoint**: `POST /accounts/delete-batch` * **描述**: 根据 ID 列表批量删除账号。 * **Request Body**: `BatchDeleteBody` * **成功响应 (200 OK)**: ```json // Body: ApiResponse<{ deletedCount: number }> { "code": 0, "message": "Successfully deleted 5 accounts.", "data": { "deletedCount": 5 } } ``` 3. **批量更新账号** * **Endpoint**: `POST /accounts/update-batch` * **描述**: 批量修改账号的状态、所有者或备注。 * **Request Body**: `BatchUpdateBody` * **成功响应 (200 OK)**: ```json // Body: ApiResponse<{ updatedCount: number }> { "code": 0, "message": "Successfully updated 3 accounts.", "data": { "updatedCount": 3 } } ``` 4. **核心统计接口** * **Endpoint**: `GET /stats/overview` * **描述**: 一次性获取仪表盘所需的全部聚合统计数据。 * **成功响应 (200 OK)**: ```json // Body: ApiResponse { "code": 0, "message": "Success", "data": { /* ... StatsOverview 对象 ... */ } } ``` ### **7. 关键后台任务** * **任务名称**: Stale Lock Cleanup (超时锁定清理) * **执行频率**: 建议每 1 分钟执行一次。 * **核心逻辑**: * 查找所有 `status` 为 `locked` 且 `lockedAt` 时间早于预设阈值(例如 5 分钟前)的账号。 * 执行 SQL: `UPDATE accounts SET status = 'available', lockedAt = NULL WHERE status = 'locked' AND lockedAt < NOW() - INTERVAL '5 minutes';` * **目的**: 自动释放因脚本异常中断而未能解锁的账号,保证账号资源的流转性。