Initial commit from Create Next App
This commit is contained in:
867
docs/API文档.md
Normal file
867
docs/API文档.md
Normal file
@@ -0,0 +1,867 @@
|
||||
# 账户管理系统 API 文档
|
||||
|
||||
## 概述
|
||||
|
||||
本文档描述了账户管理系统的API接口,用于Web前端对接。系统提供了账户的获取、上传、更新、删除和统计等功能。
|
||||
|
||||
### 基础信息
|
||||
|
||||
- **基础URL**: `{API_BASE_URL}`
|
||||
- **数据格式**: JSON
|
||||
- **字符编码**: UTF-8
|
||||
- **认证方式**: 需要在请求头中包含认证信息
|
||||
|
||||
### 通用响应格式
|
||||
|
||||
所有API接口都使用统一的响应格式:
|
||||
|
||||
```typescript
|
||||
interface ApiResponse<T> {
|
||||
code: BusinessCode; // 业务状态码
|
||||
message: string; // 响应消息
|
||||
data: T | null; // 响应数据
|
||||
}
|
||||
```
|
||||
|
||||
#### 业务状态码 (BusinessCode)
|
||||
|
||||
| 状态码 | 名称 | 说明 |
|
||||
|--------|------|------|
|
||||
| 0 | Success | 请求成功 |
|
||||
| 1001 | NoResource | 资源不存在 |
|
||||
| 2001 | InvalidParams | 参数无效 |
|
||||
| 3001 | ResourceConflict | 资源冲突 |
|
||||
| 4001 | PermissionDenied | 权限不足 |
|
||||
| 5001 | BusinessError | 业务错误 |
|
||||
|
||||
## 数据模型
|
||||
|
||||
### 账户 (Account)
|
||||
|
||||
账户是系统的核心数据模型,包含以下字段:
|
||||
|
||||
```typescript
|
||||
interface Account {
|
||||
id: number; // 账户ID,自增主键
|
||||
ownerId: string; // 所有者ID
|
||||
platform: string; // 平台名称
|
||||
customId: string; // 自定义ID
|
||||
data: string; // 账户数据,JSON格式字符串
|
||||
status: string; // 账户状态
|
||||
notes?: string; // 备注,可选
|
||||
lockedAt?: Date; // 锁定时间,可选
|
||||
createdAt: Date; // 创建时间
|
||||
updatedAt: Date; // 更新时间
|
||||
}
|
||||
```
|
||||
|
||||
#### 账户状态
|
||||
|
||||
- `available`: 可用
|
||||
- `locked`: 已锁定
|
||||
- 其他自定义状态
|
||||
|
||||
### 分页结果 (PaginationResult)
|
||||
|
||||
```typescript
|
||||
interface PaginationResult {
|
||||
page: number; // 当前页码
|
||||
pageSize: number; // 每页条数
|
||||
total: number; // 总记录数
|
||||
totalPages: number; // 总页数
|
||||
}
|
||||
```
|
||||
|
||||
### 统计概览 (StatsOverview)
|
||||
|
||||
```typescript
|
||||
interface StatsOverview {
|
||||
totalAccounts: number; // 总账户数
|
||||
platformSummary: Record<string, number>; // 平台统计
|
||||
ownerSummary: Record<string, number>; // 所有者统计
|
||||
statusSummary: Record<string, number>; // 状态统计
|
||||
detailedBreakdown: { // 详细统计
|
||||
platform: string;
|
||||
ownerId: string;
|
||||
status: string;
|
||||
count: number;
|
||||
}[];
|
||||
}
|
||||
```
|
||||
|
||||
## API 接口
|
||||
|
||||
### 1. 账户获取接口
|
||||
|
||||
用于脚本端获取可用账户。
|
||||
|
||||
#### 请求
|
||||
|
||||
```http
|
||||
GET /s/v1/{ownerId}/acquire?platform={platform}&count={count}
|
||||
```
|
||||
|
||||
#### 参数
|
||||
|
||||
| 参数名 | 位置 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|------|
|
||||
| ownerId | 路径 | string | 是 | 所有者ID |
|
||||
| platform | 查询 | string | 是 | 平台名称 |
|
||||
| count | 查询 | number | 否 | 获取数量,默认1,最大100 |
|
||||
|
||||
#### 响应
|
||||
|
||||
成功响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "Successfully acquired 1 account.",
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"customId": "custom123",
|
||||
"data": "{\"username\":\"test\",\"password\":\"123456\"}"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
无可用账户响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 1001,
|
||||
"message": "No available accounts found for platform 'example'.",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
#### 示例
|
||||
|
||||
```javascript
|
||||
// 使用fetch获取账户
|
||||
async function acquireAccounts(ownerId, platform, count = 1) {
|
||||
const response = await fetch(`/s/v1/${ownerId}/acquire?platform=${platform}&count=${count}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer YOUR_TOKEN'
|
||||
}
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
return result;
|
||||
}
|
||||
|
||||
// 调用示例
|
||||
const result = await acquireAccounts('owner123', 'example', 2);
|
||||
if (result.code === 0) {
|
||||
console.log('获取成功:', result.data);
|
||||
} else {
|
||||
console.error('获取失败:', result.message);
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 账户状态更新接口
|
||||
|
||||
用于脚本端更新账户状态。
|
||||
|
||||
#### 请求
|
||||
|
||||
```http
|
||||
GET /s/v1/{ownerId}/update/{accountId}/{newStatus}?notes={notes}
|
||||
```
|
||||
|
||||
#### 参数
|
||||
|
||||
| 参数名 | 位置 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|------|
|
||||
| ownerId | 路径 | string | 是 | 所有者ID |
|
||||
| accountId | 路径 | number | 是 | 账户ID |
|
||||
| newStatus | 路径 | string | 是 | 新状态 |
|
||||
| notes | 查询 | string | 否 | 备注 |
|
||||
|
||||
#### 响应
|
||||
|
||||
成功响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "Account status updated to 'used'.",
|
||||
"data": {
|
||||
"updatedId": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
权限不足响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 4001,
|
||||
"message": "Account not found or permission denied.",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
#### 示例
|
||||
|
||||
```javascript
|
||||
// 使用fetch更新账户状态
|
||||
async function updateAccountStatus(ownerId, accountId, newStatus, notes) {
|
||||
const url = `/s/v1/${ownerId}/update/${accountId}/${newStatus}`;
|
||||
if (notes) {
|
||||
url += `?notes=${encodeURIComponent(notes)}`;
|
||||
}
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer YOUR_TOKEN'
|
||||
}
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
return result;
|
||||
}
|
||||
|
||||
// 调用示例
|
||||
const result = await updateAccountStatus('owner123', 1, 'used', '测试使用');
|
||||
if (result.code === 0) {
|
||||
console.log('更新成功:', result.data);
|
||||
} else {
|
||||
console.error('更新失败:', result.message);
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 账户上传接口
|
||||
|
||||
用于脚本端批量上传账户。
|
||||
|
||||
#### 请求
|
||||
|
||||
```http
|
||||
POST /s/v1/{ownerId}/upload
|
||||
```
|
||||
|
||||
#### 请求体
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"platform": "string",
|
||||
"customId": "string",
|
||||
"data": "string",
|
||||
"status": "string"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### 参数
|
||||
|
||||
| 参数名 | 位置 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|------|
|
||||
| ownerId | 路径 | string | 是 | 所有者ID |
|
||||
| body | 请求体 | ScriptUploadItem[] | 是 | 账户数据数组 |
|
||||
|
||||
#### ScriptUploadItem 类型
|
||||
|
||||
```typescript
|
||||
interface ScriptUploadItem {
|
||||
platform: string; // 平台名称
|
||||
customId: string; // 自定义ID
|
||||
data: string; // 账户数据,JSON格式字符串
|
||||
status?: string; // 账户状态,可选,默认为"available"
|
||||
}
|
||||
```
|
||||
|
||||
#### 响应
|
||||
|
||||
成功响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "Successfully processed 2 accounts (1 created, 1 updated).",
|
||||
"data": {
|
||||
"processedCount": 2,
|
||||
"createdCount": 1,
|
||||
"updatedCount": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 示例
|
||||
|
||||
```javascript
|
||||
// 使用fetch上传账户
|
||||
async function uploadAccounts(ownerId, accounts) {
|
||||
const response = await fetch(`/s/v1/${ownerId}/upload`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer YOUR_TOKEN'
|
||||
},
|
||||
body: JSON.stringify(accounts)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
return result;
|
||||
}
|
||||
|
||||
// 调用示例
|
||||
const accounts = [
|
||||
{
|
||||
platform: 'example',
|
||||
customId: 'user123',
|
||||
data: JSON.stringify({ username: 'user123', password: 'pass123' }),
|
||||
status: 'available'
|
||||
},
|
||||
{
|
||||
platform: 'example',
|
||||
customId: 'user456',
|
||||
data: JSON.stringify({ username: 'user456', password: 'pass456' })
|
||||
}
|
||||
];
|
||||
|
||||
const result = await uploadAccounts('owner123', accounts);
|
||||
if (result.code === 0) {
|
||||
console.log('上传成功:', result.data);
|
||||
} else {
|
||||
console.error('上传失败:', result.message);
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 账户列表接口
|
||||
|
||||
用于Web端获取账户列表,支持筛选、分页和排序。
|
||||
|
||||
#### 请求
|
||||
|
||||
```http
|
||||
POST /web/v1/accounts/list
|
||||
```
|
||||
|
||||
#### 请求体
|
||||
|
||||
```json
|
||||
{
|
||||
"filters": {
|
||||
"platform": "string",
|
||||
"status": ["string"],
|
||||
"ownerId": "string",
|
||||
"search": "string"
|
||||
},
|
||||
"pagination": {
|
||||
"page": 1,
|
||||
"pageSize": 10
|
||||
},
|
||||
"sort": {
|
||||
"field": "id",
|
||||
"order": "desc"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 参数
|
||||
|
||||
| 参数名 | 位置 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|------|
|
||||
| filters | 请求体 | ListAccountsFilters | 否 | 筛选条件 |
|
||||
| pagination | 请求体 | object | 是 | 分页参数 |
|
||||
| sort | 请求体 | object | 是 | 排序参数 |
|
||||
|
||||
#### ListAccountsFilters 类型
|
||||
|
||||
```typescript
|
||||
interface ListAccountsFilters {
|
||||
platform?: string; // 平台名称筛选
|
||||
status?: string[]; // 状态筛选,多选
|
||||
ownerId?: string; // 所有者ID筛选
|
||||
search?: string; // 搜索关键词,搜索customId和notes
|
||||
}
|
||||
```
|
||||
|
||||
#### 排序字段
|
||||
|
||||
支持以下字段排序:
|
||||
- `id`: 账户ID
|
||||
- `ownerId`: 所有者ID
|
||||
- `platform`: 平台名称
|
||||
- `customId`: 自定义ID
|
||||
- `data`: 账户数据
|
||||
- `status`: 账户状态
|
||||
- `notes`: 备注
|
||||
- `lockedAt`: 锁定时间
|
||||
- `createdAt`: 创建时间
|
||||
- `updatedAt`: 更新时间
|
||||
|
||||
#### 响应
|
||||
|
||||
成功响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"list": [
|
||||
{
|
||||
"id": 1,
|
||||
"ownerId": "owner123",
|
||||
"platform": "example",
|
||||
"customId": "user123",
|
||||
"data": "{\"username\":\"user123\",\"password\":\"pass123\"}",
|
||||
"status": "available",
|
||||
"notes": "测试账户",
|
||||
"lockedAt": null,
|
||||
"createdAt": "2023-01-01T00:00:00.000Z",
|
||||
"updatedAt": "2023-01-01T00:00:00.000Z"
|
||||
}
|
||||
],
|
||||
"pagination": {
|
||||
"page": 1,
|
||||
"pageSize": 10,
|
||||
"total": 1,
|
||||
"totalPages": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 示例
|
||||
|
||||
```javascript
|
||||
// 使用fetch获取账户列表
|
||||
async function getAccountsList(filters, pagination, sort) {
|
||||
const response = await fetch('/web/v1/accounts/list', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer YOUR_TOKEN'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
filters,
|
||||
pagination,
|
||||
sort
|
||||
})
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
return result;
|
||||
}
|
||||
|
||||
// 调用示例
|
||||
const filters = {
|
||||
platform: 'example',
|
||||
status: ['available'],
|
||||
search: 'test'
|
||||
};
|
||||
|
||||
const pagination = {
|
||||
page: 1,
|
||||
pageSize: 10
|
||||
};
|
||||
|
||||
const sort = {
|
||||
field: 'createdAt',
|
||||
order: 'desc'
|
||||
};
|
||||
|
||||
const result = await getAccountsList(filters, pagination, sort);
|
||||
if (result.code === 0) {
|
||||
console.log('账户列表:', result.data.list);
|
||||
console.log('分页信息:', result.data.pagination);
|
||||
} else {
|
||||
console.error('获取失败:', result.message);
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 批量删除账户接口
|
||||
|
||||
用于Web端批量删除账户。
|
||||
|
||||
#### 请求
|
||||
|
||||
```http
|
||||
POST /web/v1/accounts/delete-batch
|
||||
```
|
||||
|
||||
#### 请求体
|
||||
|
||||
```json
|
||||
{
|
||||
"ids": [1, 2, 3]
|
||||
}
|
||||
```
|
||||
|
||||
#### 参数
|
||||
|
||||
| 参数名 | 位置 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|------|
|
||||
| ids | 请求体 | number[] | 是 | 要删除的账户ID数组 |
|
||||
|
||||
#### 响应
|
||||
|
||||
成功响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "Successfully deleted 3 accounts.",
|
||||
"data": {
|
||||
"deletedCount": 3
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 示例
|
||||
|
||||
```javascript
|
||||
// 使用fetch批量删除账户
|
||||
async function batchDeleteAccounts(ids) {
|
||||
const response = await fetch('/web/v1/accounts/delete-batch', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer YOUR_TOKEN'
|
||||
},
|
||||
body: JSON.stringify({ ids })
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
return result;
|
||||
}
|
||||
|
||||
// 调用示例
|
||||
const result = await batchDeleteAccounts([1, 2, 3]);
|
||||
if (result.code === 0) {
|
||||
console.log('删除成功:', result.data);
|
||||
} else {
|
||||
console.error('删除失败:', result.message);
|
||||
}
|
||||
```
|
||||
|
||||
### 6. 批量更新账户接口
|
||||
|
||||
用于Web端批量更新账户。
|
||||
|
||||
#### 请求
|
||||
|
||||
```http
|
||||
POST /web/v1/accounts/update-batch
|
||||
```
|
||||
|
||||
#### 请求体
|
||||
|
||||
```json
|
||||
{
|
||||
"ids": [1, 2, 3],
|
||||
"payload": {
|
||||
"status": "available",
|
||||
"ownerId": "newOwner",
|
||||
"notes": "批量更新备注"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 参数
|
||||
|
||||
| 参数名 | 位置 | 类型 | 必填 | 说明 |
|
||||
|--------|------|------|------|------|
|
||||
| ids | 请求体 | number[] | 是 | 要更新的账户ID数组 |
|
||||
| payload | 请求体 | object | 是 | 更新数据 |
|
||||
|
||||
#### payload 类型
|
||||
|
||||
```typescript
|
||||
interface UpdatePayload {
|
||||
status?: string; // 新状态
|
||||
ownerId?: string; // 新所有者ID
|
||||
notes?: string; // 新备注
|
||||
}
|
||||
```
|
||||
|
||||
#### 响应
|
||||
|
||||
成功响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "Successfully updated 3 accounts.",
|
||||
"data": {
|
||||
"updatedCount": 3
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 示例
|
||||
|
||||
```javascript
|
||||
// 使用fetch批量更新账户
|
||||
async function batchUpdateAccounts(ids, payload) {
|
||||
const response = await fetch('/web/v1/accounts/update-batch', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer YOUR_TOKEN'
|
||||
},
|
||||
body: JSON.stringify({ ids, payload })
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
return result;
|
||||
}
|
||||
|
||||
// 调用示例
|
||||
const ids = [1, 2, 3];
|
||||
const payload = {
|
||||
status: 'available',
|
||||
notes: '批量更新为可用状态'
|
||||
};
|
||||
|
||||
const result = await batchUpdateAccounts(ids, payload);
|
||||
if (result.code === 0) {
|
||||
console.log('更新成功:', result.data);
|
||||
} else {
|
||||
console.error('更新失败:', result.message);
|
||||
}
|
||||
```
|
||||
|
||||
### 7. 统计概览接口
|
||||
|
||||
用于Web端获取账户统计信息。
|
||||
|
||||
#### 请求
|
||||
|
||||
```http
|
||||
GET /web/v1/stats/overview
|
||||
```
|
||||
|
||||
#### 响应
|
||||
|
||||
成功响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "Success",
|
||||
"data": {
|
||||
"totalAccounts": 100,
|
||||
"platformSummary": {
|
||||
"example": 50,
|
||||
"test": 30,
|
||||
"demo": 20
|
||||
},
|
||||
"ownerSummary": {
|
||||
"owner1": 40,
|
||||
"owner2": 35,
|
||||
"owner3": 25
|
||||
},
|
||||
"statusSummary": {
|
||||
"available": 70,
|
||||
"locked": 20,
|
||||
"used": 10
|
||||
},
|
||||
"detailedBreakdown": [
|
||||
{
|
||||
"platform": "example",
|
||||
"ownerId": "owner1",
|
||||
"status": "available",
|
||||
"count": 20
|
||||
},
|
||||
{
|
||||
"platform": "example",
|
||||
"ownerId": "owner1",
|
||||
"status": "locked",
|
||||
"count": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 示例
|
||||
|
||||
```javascript
|
||||
// 使用fetch获取统计概览
|
||||
async function getStatsOverview() {
|
||||
const response = await fetch('/web/v1/stats/overview', {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer YOUR_TOKEN'
|
||||
}
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
return result;
|
||||
}
|
||||
|
||||
// 调用示例
|
||||
const result = await getStatsOverview();
|
||||
if (result.code === 0) {
|
||||
console.log('总账户数:', result.data.totalAccounts);
|
||||
console.log('平台统计:', result.data.platformSummary);
|
||||
console.log('所有者统计:', result.data.ownerSummary);
|
||||
console.log('状态统计:', result.data.statusSummary);
|
||||
console.log('详细统计:', result.data.detailedBreakdown);
|
||||
} else {
|
||||
console.error('获取失败:', result.message);
|
||||
}
|
||||
```
|
||||
|
||||
## 错误处理
|
||||
|
||||
所有API接口在发生错误时都会返回统一的错误响应格式:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "错误码",
|
||||
"message": "错误信息",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
### 常见错误码
|
||||
|
||||
| 错误码 | 说明 | 处理建议 |
|
||||
|--------|------|----------|
|
||||
| 1001 | 资源不存在 | 检查请求的资源是否存在 |
|
||||
| 2001 | 参数无效 | 检查请求参数是否符合要求 |
|
||||
| 3001 | 资源冲突 | 检查是否有重复数据 |
|
||||
| 4001 | 权限不足 | 检查认证信息是否正确 |
|
||||
| 5001 | 业务错误 | 检查业务逻辑是否正确 |
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. **认证**:所有API请求都需要在请求头中包含认证信息,例如:
|
||||
```http
|
||||
Authorization: Bearer YOUR_TOKEN
|
||||
```
|
||||
|
||||
2. **错误处理**:前端应该根据返回的`code`值进行相应的错误处理,而不是依赖`message`字段。
|
||||
|
||||
3. **分页**:在获取列表数据时,合理设置分页大小,建议不超过100条。
|
||||
|
||||
4. **批量操作**:批量操作时,建议限制单次操作的数量,避免服务器压力过大。
|
||||
|
||||
5. **数据格式**:账户数据字段`data`存储的是JSON格式字符串,前端需要进行相应的序列化和反序列化操作。
|
||||
|
||||
## 类型定义
|
||||
|
||||
以下是完整的TypeScript类型定义,可以直接在前端项目中使用:
|
||||
|
||||
```typescript
|
||||
// 业务状态码
|
||||
enum BusinessCode {
|
||||
Success = 0,
|
||||
NoResource = 1001,
|
||||
InvalidParams = 2001,
|
||||
ResourceConflict = 3001,
|
||||
PermissionDenied = 4001,
|
||||
BusinessError = 5001,
|
||||
}
|
||||
|
||||
// 通用响应格式
|
||||
interface ApiResponse<T> {
|
||||
code: BusinessCode;
|
||||
message: string;
|
||||
data: T | null;
|
||||
}
|
||||
|
||||
// 账户类型
|
||||
interface Account {
|
||||
id: number;
|
||||
ownerId: string;
|
||||
platform: string;
|
||||
customId: string;
|
||||
data: string;
|
||||
status: string;
|
||||
notes?: string;
|
||||
lockedAt?: Date | null;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
// 分页结果
|
||||
interface PaginationResult {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
total: number;
|
||||
totalPages: number;
|
||||
}
|
||||
|
||||
// 脚本获取响应
|
||||
type ScriptAcquireResponse = Pick<Account, 'id' | 'customId' | 'data'>[];
|
||||
|
||||
// 脚本上传项
|
||||
interface ScriptUploadItem {
|
||||
platform: string;
|
||||
customId: string;
|
||||
data: string;
|
||||
status?: string;
|
||||
}
|
||||
|
||||
// 账户列表筛选条件
|
||||
interface ListAccountsFilters {
|
||||
platform?: string;
|
||||
status?: string[];
|
||||
ownerId?: string;
|
||||
search?: string;
|
||||
}
|
||||
|
||||
// 账户列表请求体
|
||||
interface ListAccountsBody {
|
||||
filters: ListAccountsFilters;
|
||||
pagination: {
|
||||
page: number;
|
||||
pageSize: number;
|
||||
};
|
||||
sort: {
|
||||
field: keyof Account;
|
||||
order: 'asc' | 'desc';
|
||||
};
|
||||
}
|
||||
|
||||
// 账户列表响应
|
||||
interface ListAccountsResponse {
|
||||
list: Account[];
|
||||
pagination: PaginationResult;
|
||||
}
|
||||
|
||||
// 批量删除请求体
|
||||
interface BatchDeleteBody {
|
||||
ids: number[];
|
||||
}
|
||||
|
||||
// 批量更新请求体
|
||||
interface BatchUpdateBody {
|
||||
ids: number[];
|
||||
payload: Partial<Pick<Account, 'status' | 'ownerId' | 'notes'>>;
|
||||
}
|
||||
|
||||
// 统计概览
|
||||
interface StatsOverview {
|
||||
totalAccounts: number;
|
||||
platformSummary: Record<string, number>;
|
||||
ownerSummary: Record<string, number>;
|
||||
statusSummary: Record<string, number>;
|
||||
detailedBreakdown: {
|
||||
platform: string;
|
||||
ownerId: string;
|
||||
status: string;
|
||||
count: number;
|
||||
}[];
|
||||
}
|
||||
```
|
||||
|
||||
## 版本历史
|
||||
|
||||
| 版本 | 日期 | 说明 |
|
||||
|------|------|------|
|
||||
| 1.0.0 | 2023-01-01 | 初始版本 |
|
||||
328
docs/后端设计.md
Normal file
328
docs/后端设计.md
Normal file
@@ -0,0 +1,328 @@
|
||||
### **轻量化账号管理平台 - 工程设计蓝图**
|
||||
|
||||
#### 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<typeof accounts>;
|
||||
```
|
||||
|
||||
##### 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<T> {
|
||||
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<Account, 'id' | 'customId' | 'data'>[];
|
||||
|
||||
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<Pick<Account, 'status' | 'ownerId' | 'notes'>>;
|
||||
}
|
||||
|
||||
export interface StatsOverview {
|
||||
totalAccounts: number;
|
||||
platformSummary: Record<string, number>;
|
||||
ownerSummary: Record<string, number>;
|
||||
statusSummary: Record<string, number>;
|
||||
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<ScriptAcquireResponse>
|
||||
{
|
||||
"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<null>
|
||||
{
|
||||
"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<ListAccountsResponse>
|
||||
{
|
||||
"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<StatsOverview>
|
||||
{
|
||||
"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';`
|
||||
* **目的**: 自动释放因脚本异常中断而未能解锁的账号,保证账号资源的流转性。
|
||||
Reference in New Issue
Block a user