1101 lines
23 KiB
Markdown
1101 lines
23 KiB
Markdown
# 个人记账预算系统 API 接口文档
|
||
|
||
> **版本**: v1.0.0
|
||
> **基础路径**: `http://localhost:3001/api`
|
||
> **技术栈**: Node.js + Express + Prisma (SQLite)
|
||
> **更新**: 2026-04-27
|
||
|
||
---
|
||
|
||
## 一、文档说明
|
||
|
||
本文档遵循 **OpenAPI 3.0** 规范,定义个人记账预算系统的所有 RESTful API 接口。文档与代码实现保持同步,可作为前后端开发、测试验证、接口调用的权威参考。
|
||
|
||
---
|
||
|
||
## 二、认证与鉴权
|
||
|
||
**MVP 阶段**: 当前所有接口通过 `userId` 查询参数实现数据隔离,生产环境需替换为 JWT Token 鉴权。
|
||
|
||
```
|
||
Authorization: Bearer <JWT_TOKEN>
|
||
```
|
||
|
||
**数据隔离规则**: 所有接口均需要传入 `userId` 参数,确保用户只能访问自己的数据。
|
||
|
||
---
|
||
|
||
## 三、全局约定
|
||
|
||
### 3.1 统一响应格式
|
||
|
||
所有接口均返回 JSON 格式,遵循统一结构:
|
||
|
||
#### 成功响应
|
||
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": { ... },
|
||
"message": "操作成功"
|
||
}
|
||
```
|
||
|
||
#### 错误响应
|
||
|
||
```json
|
||
{
|
||
"success": false,
|
||
"data": null,
|
||
"message": "错误信息描述"
|
||
}
|
||
```
|
||
|
||
### 3.2 HTTP 状态码
|
||
|
||
| 状态码 | 说明 | 触发场景 |
|
||
|--------|------|---------|
|
||
| 200 | 成功 | 请求成功处理 |
|
||
| 400 | 客户端错误 | 参数校验失败、必填字段缺失 |
|
||
| 404 | 资源不存在 | 请求的记录/账户/预算不存在 |
|
||
| 500 | 服务端错误 | 数据库异常、内部逻辑错误 |
|
||
|
||
### 3.3 日期格式
|
||
|
||
| 字段类型 | 格式 | 示例 |
|
||
|---------|------|------|
|
||
| 日期 | `YYYY-MM-DD` | `2026-04-27` |
|
||
| 日期时间 | ISO 8601 | `2026-04-27T10:00:00.000Z` |
|
||
| 月份 | `YYYY-MM` | `2026-04` |
|
||
|
||
### 3.4 金额处理
|
||
|
||
- 所有金额字段为 **Number** 类型,单位为 **元**
|
||
- 精度:保留两位小数
|
||
- 示例:`168.50` 表示 168.5 元
|
||
|
||
### 3.5 分页约定
|
||
|
||
当前接口采用 **全量返回** 模式,前端负责分页逻辑(每页 10 条)。后续将接入后端分页。
|
||
|
||
---
|
||
|
||
## 四、核心业务模型
|
||
|
||
### 4.1 用户 (User)
|
||
|
||
```typescript
|
||
interface User {
|
||
id: number; // 用户唯一标识
|
||
name: string; // 用户名
|
||
email: string; // 邮箱(唯一)
|
||
createdAt: string; // 创建时间
|
||
}
|
||
```
|
||
|
||
### 4.2 账户 (Account)
|
||
|
||
```typescript
|
||
interface Account {
|
||
id: number; // 账户唯一标识
|
||
userId: number; // 所属用户 ID
|
||
name: string; // 账户名称(如"支付宝")
|
||
type: string; // 账户类型:payment/bank/cash
|
||
color: string; // UI 显示颜色(如 "#1890FF")
|
||
balance: number; // 当前余额(元)
|
||
createdAt: string; // 创建时间
|
||
updatedAt: string; // 更新时间
|
||
}
|
||
```
|
||
|
||
### 4.3 交易记录 (Record)
|
||
|
||
```typescript
|
||
interface Record {
|
||
id: number; // 记录唯一标识
|
||
userId: number; // 所属用户 ID
|
||
accountId: number; // 关联账户 ID
|
||
type: string; // 类型:income/expense
|
||
amount: number; // 金额(元)
|
||
category: string; // 分类(如"餐饮")
|
||
description?: string; // 备注
|
||
date: string; // 交易日期
|
||
account?: Account; // 关联账户详情(JOIN 查询填充)
|
||
createdAt: string; // 创建时间
|
||
updatedAt: string; // 更新时间
|
||
}
|
||
```
|
||
|
||
### 4.4 预算 (Budget)
|
||
|
||
```typescript
|
||
interface Budget {
|
||
id: number; // 预算唯一标识
|
||
userId: number; // 所属用户 ID
|
||
category: string; // 预算分类
|
||
amount: number; // 预算金额上限(元)
|
||
month: string; // 预算月份(YYYY-MM)
|
||
createdAt: string; // 创建时间
|
||
updatedAt: string; // 更新时间
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 五、接口详情
|
||
|
||
### 5.1 健康检查与系统接口
|
||
|
||
#### 5.1.1 服务健康检查
|
||
|
||
**接口**: `GET /health`
|
||
|
||
**说明**: 用于负载均衡/容器探针,不依赖数据库
|
||
|
||
**请求**: 无
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"timestamp": "2026-04-27T10:00:00.000Z"
|
||
},
|
||
"message": "Personal Finance Backend is running"
|
||
}
|
||
```
|
||
|
||
#### 5.1.2 API 根路径
|
||
|
||
**接口**: `GET /api`
|
||
|
||
**说明**: 返回版本信息,用于前端检测后端可达性
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"version": "1.0.0"
|
||
},
|
||
"message": "Personal Finance API"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 5.2 用户模块 `/api/users`
|
||
|
||
> **说明**: MVP 阶段临时接口,生产环境应替换为注册/登录流程
|
||
|
||
#### 5.2.1 创建用户
|
||
|
||
**接口**: `POST /api/users`
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"name": "测试用户",
|
||
"email": "test@example.com"
|
||
}
|
||
```
|
||
|
||
**参数说明**:
|
||
| 参数 | 类型 | 必填 | 说明 | 校验规则 |
|
||
|------|------|------|------|---------|
|
||
| name | string | 是 | 用户名 | 非空字符串 |
|
||
| email | string | 是 | 邮箱 | 唯一约束 |
|
||
|
||
**成功响应** (200):
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"id": 1,
|
||
"name": "测试用户",
|
||
"email": "test@example.com",
|
||
"createdAt": "2026-04-27T10:00:00.000Z",
|
||
"updatedAt": "2026-04-27T10:00:00.000Z"
|
||
},
|
||
"message": "用户创建成功"
|
||
}
|
||
```
|
||
|
||
**错误响应**:
|
||
```json
|
||
{
|
||
"success": false,
|
||
"data": null,
|
||
"message": "该邮箱已被注册"
|
||
}
|
||
```
|
||
|
||
**错误码**:
|
||
| 状态码 | 错误码 | 说明 |
|
||
|--------|--------|------|
|
||
| 400 | P2002 | 邮箱重复 |
|
||
| 500 | INTERNAL_ERROR | 服务器内部错误 |
|
||
|
||
#### 5.2.2 获取用户列表
|
||
|
||
**接口**: `GET /api/users`
|
||
|
||
**请求**: 无
|
||
|
||
**响应** (200):
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": [
|
||
{
|
||
"id": 1,
|
||
"name": "测试用户",
|
||
"email": "test@example.com",
|
||
"createdAt": "2026-04-27T10:00:00.000Z",
|
||
"updatedAt": "2026-04-27T10:00:00.000Z"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 5.3 账户模块 `/api/accounts`
|
||
|
||
#### 5.3.1 获取账户列表
|
||
|
||
**接口**: `GET /api/accounts`
|
||
|
||
**查询参数**:
|
||
| 参数 | 类型 | 必填 | 位置 | 说明 |
|
||
|------|------|------|------|------|
|
||
| userId | number | 是 | query | 用户 ID |
|
||
|
||
**请求示例**:
|
||
```
|
||
GET /api/accounts?userId=1
|
||
```
|
||
|
||
**响应** (200):
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": [
|
||
{
|
||
"id": 1,
|
||
"userId": 1,
|
||
"name": "支付宝",
|
||
"type": "payment",
|
||
"color": "#1890FF",
|
||
"balance": 5000,
|
||
"createdAt": "2026-04-27T10:00:00.000Z",
|
||
"updatedAt": "2026-04-27T10:00:00.000Z"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
#### 5.3.2 获取账户详情
|
||
|
||
**接口**: `GET /api/accounts/:id`
|
||
|
||
**路径参数**:
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| id | number | 是 | 账户 ID |
|
||
|
||
**请求示例**:
|
||
```
|
||
GET /api/accounts/1
|
||
```
|
||
|
||
**响应** (200):
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"id": 1,
|
||
"userId": 1,
|
||
"name": "支付宝",
|
||
"type": "payment",
|
||
"color": "#1890FF",
|
||
"balance": 5000,
|
||
"createdAt": "2026-04-27T10:00:00.000Z",
|
||
"updatedAt": "2026-04-27T10:00:00.000Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
**错误响应** (404):
|
||
```json
|
||
{
|
||
"success": false,
|
||
"data": null,
|
||
"message": "账户不存在"
|
||
}
|
||
```
|
||
|
||
#### 5.3.3 创建账户
|
||
|
||
**接口**: `POST /api/accounts`
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"userId": 1,
|
||
"name": "支付宝",
|
||
"type": "payment",
|
||
"color": "#1890FF",
|
||
"balance": 5000
|
||
}
|
||
```
|
||
|
||
**参数说明**:
|
||
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
||
|------|------|------|--------|------|
|
||
| userId | number | 是 | - | 用户 ID |
|
||
| name | string | 是 | - | 账户名称 |
|
||
| type | string | 是 | - | 账户类型:payment/bank/cash |
|
||
| color | string | 否 | "#1890FF" | UI 颜色 |
|
||
| balance | number | 否 | 0 | 初始余额 |
|
||
|
||
**成功响应** (200):
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"id": 2,
|
||
"userId": 1,
|
||
"name": "支付宝",
|
||
"type": "payment",
|
||
"color": "#1890FF",
|
||
"balance": 5000,
|
||
"createdAt": "2026-04-27T10:00:00.000Z",
|
||
"updatedAt": "2026-04-27T10:00:00.000Z"
|
||
},
|
||
"message": "账户创建成功"
|
||
}
|
||
```
|
||
|
||
#### 5.3.4 更新账户
|
||
|
||
**接口**: `PUT /api/accounts/:id`
|
||
|
||
**路径参数**:
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| id | number | 是 | 账户 ID |
|
||
|
||
**请求体** (部分更新,仅更新传入字段):
|
||
```json
|
||
{
|
||
"name": "新名称",
|
||
"balance": 6000
|
||
}
|
||
```
|
||
|
||
**响应** (200):
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": { ... },
|
||
"message": "账户更新成功"
|
||
}
|
||
```
|
||
|
||
#### 5.3.5 删除账户
|
||
|
||
**接口**: `DELETE /api/accounts/:id`
|
||
|
||
**路径参数**:
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| id | number | 是 | 账户 ID |
|
||
|
||
**响应** (200):
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": null,
|
||
"message": "账户删除成功"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 5.4 交易记录模块 `/api/records`
|
||
|
||
> **核心业务**: 创建/更新/删除记录时,使用 Prisma 事务联动更新账户余额,确保数据一致性。
|
||
|
||
#### 5.4.1 获取交易记录列表
|
||
|
||
**接口**: `GET /api/records`
|
||
|
||
**查询参数**:
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| userId | number | 是 | 用户 ID |
|
||
| accountId | number | 否 | 账户 ID 筛选 |
|
||
| type | string | 否 | 类型筛选:income/expense |
|
||
| category | string | 否 | 分类筛选 |
|
||
| startDate | string | 否 | 开始日期(YYYY-MM-DD) |
|
||
| endDate | string | 否 | 结束日期(YYYY-MM-DD) |
|
||
|
||
**请求示例**:
|
||
```
|
||
GET /api/records?userId=1&type=expense&startDate=2026-04-01&endDate=2026-04-30
|
||
```
|
||
|
||
**排序规则**: 按 `createdAt` 倒序(最新记录在前)
|
||
|
||
**关联查询**: 自动包含 `account` 对象,便于前端展示账户名
|
||
|
||
**响应** (200):
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": [
|
||
{
|
||
"id": 5,
|
||
"userId": 1,
|
||
"accountId": 1,
|
||
"type": "expense",
|
||
"amount": 68,
|
||
"category": "餐饮",
|
||
"description": "午饭",
|
||
"date": "2026-04-27T00:00:00.000Z",
|
||
"account": {
|
||
"id": 1,
|
||
"name": "支付宝",
|
||
"type": "payment",
|
||
"color": "#1890FF",
|
||
"balance": 4932
|
||
},
|
||
"createdAt": "2026-04-27T10:30:00.000Z",
|
||
"updatedAt": "2026-04-27T10:30:00.000Z"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
#### 5.4.2 获取交易记录详情
|
||
|
||
**接口**: `GET /api/records/:id`
|
||
|
||
**路径参数**:
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| id | number | 是 | 记录 ID |
|
||
|
||
**响应** (200):
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"id": 5,
|
||
"userId": 1,
|
||
"accountId": 1,
|
||
"type": "expense",
|
||
"amount": 68,
|
||
"category": "餐饮",
|
||
"description": "午饭",
|
||
"date": "2026-04-27T00:00:00.000Z",
|
||
"account": { ... },
|
||
"createdAt": "2026-04-27T10:30:00.000Z",
|
||
"updatedAt": "2026-04-27T10:30:00.000Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 5.4.3 创建交易记录
|
||
|
||
**接口**: `POST /api/records`
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"userId": 1,
|
||
"accountId": 1,
|
||
"type": "expense",
|
||
"amount": 68,
|
||
"category": "餐饮",
|
||
"description": "午饭",
|
||
"date": "2026-04-27"
|
||
}
|
||
```
|
||
|
||
**参数说明**:
|
||
| 参数 | 类型 | 必填 | 说明 | 校验规则 |
|
||
|------|------|------|------|---------|
|
||
| userId | number | 是 | 用户 ID | 正整数 |
|
||
| accountId | number | 是 | 账户 ID | 正整数 |
|
||
| type | string | 是 | 交易类型 | 枚举:income/expense |
|
||
| amount | number | 是 | 金额(元) | 必须大于 0 |
|
||
| category | string | 是 | 分类名称 | 非空字符串 |
|
||
| description | string | 否 | 备注说明 | 最大长度 200 |
|
||
| date | string | 是 | 交易日期 | 格式:YYYY-MM-DD |
|
||
|
||
**业务逻辑**:
|
||
```
|
||
1. 校验必填字段和金额合法性
|
||
2. 安全解析日期(本地时区,避免 UTC 偏移)
|
||
3. 开启 Prisma 事务
|
||
├─ 3.1 创建交易记录
|
||
├─ 3.2 查询当前账户余额
|
||
├─ 3.3 根据类型计算新余额(income 加,expense 减)
|
||
└─ 3.4 更新账户余额
|
||
4. 提交事务,返回创建记录
|
||
5. 任何步骤失败则整体回滚
|
||
```
|
||
|
||
**成功响应** (200):
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"id": 6,
|
||
"userId": 1,
|
||
"accountId": 1,
|
||
"type": "expense",
|
||
"amount": 68,
|
||
"category": "餐饮",
|
||
"description": "午饭",
|
||
"date": "2026-04-27T00:00:00.000Z",
|
||
"createdAt": "2026-04-27T10:30:00.000Z",
|
||
"updatedAt": "2026-04-27T10:30:00.000Z"
|
||
},
|
||
"message": "交易记录创建成功"
|
||
}
|
||
```
|
||
|
||
**错误响应**:
|
||
```json
|
||
{
|
||
"success": false,
|
||
"data": null,
|
||
"message": "必填字段缺失"
|
||
}
|
||
```
|
||
|
||
#### 5.4.4 更新交易记录
|
||
|
||
**接口**: `PUT /api/records/:id`
|
||
|
||
**路径参数**:
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| id | number | 是 | 记录 ID |
|
||
|
||
**请求体** (部分更新):
|
||
```json
|
||
{
|
||
"type": "expense",
|
||
"amount": 100,
|
||
"category": "交通",
|
||
"description": "打车",
|
||
"date": "2026-04-27"
|
||
}
|
||
```
|
||
|
||
**业务逻辑**:
|
||
```
|
||
1. 查询原记录
|
||
2. 反向冲销原金额对余额的影响
|
||
├─ 原收入:减去原金额
|
||
└─ 原支出:加回原金额
|
||
3. 更新记录字段(仅更新传入字段)
|
||
4. 按新 type/amount 重新计算余额
|
||
5. 更新账户余额
|
||
6. 提交事务
|
||
```
|
||
|
||
**响应** (200):
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": { ... },
|
||
"message": "交易记录更新成功"
|
||
}
|
||
```
|
||
|
||
#### 5.4.5 删除交易记录
|
||
|
||
**接口**: `DELETE /api/records/:id`
|
||
|
||
**路径参数**:
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| id | number | 是 | 记录 ID |
|
||
|
||
**业务逻辑**:
|
||
```
|
||
1. 查询原记录
|
||
2. 反向冲销余额(撤销记录对余额的影响)
|
||
├─ 原收入:减去金额
|
||
└─ 原支出:加回金额
|
||
3. 更新账户余额
|
||
4. 删除记录
|
||
5. 提交事务
|
||
```
|
||
|
||
**响应** (200):
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": null,
|
||
"message": "交易记录删除成功"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 5.5 预算模块 `/api/budgets`
|
||
|
||
#### 5.5.1 获取预算列表
|
||
|
||
**接口**: `GET /api/budgets`
|
||
|
||
**查询参数**:
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| userId | number | 是 | 用户 ID |
|
||
| month | string | 否 | 月份筛选(YYYY-MM) |
|
||
|
||
**请求示例**:
|
||
```
|
||
GET /api/budgets?userId=1&month=2026-04
|
||
```
|
||
|
||
**排序规则**: 按 `createdAt` 倒序
|
||
|
||
**响应** (200):
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": [
|
||
{
|
||
"id": 1,
|
||
"userId": 1,
|
||
"category": "餐饮",
|
||
"amount": 1500,
|
||
"month": "2026-04",
|
||
"createdAt": "2026-04-27T10:00:00.000Z",
|
||
"updatedAt": "2026-04-27T10:00:00.000Z"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
#### 5.5.2 获取预算详情
|
||
|
||
**接口**: `GET /api/budgets/:id`
|
||
|
||
**路径参数**:
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| id | number | 是 | 预算 ID |
|
||
|
||
#### 5.5.3 创建预算
|
||
|
||
**接口**: `POST /api/budgets`
|
||
|
||
**请求体**:
|
||
```json
|
||
{
|
||
"userId": 1,
|
||
"category": "餐饮",
|
||
"amount": 1500,
|
||
"month": "2026-04"
|
||
}
|
||
```
|
||
|
||
**参数说明**:
|
||
| 参数 | 类型 | 必填 | 说明 | 校验规则 |
|
||
|------|------|------|------|---------|
|
||
| userId | number | 是 | 用户 ID | 正整数 |
|
||
| category | string | 是 | 预算分类 | 非空字符串 |
|
||
| amount | number | 是 | 预算金额(元) | 必须大于 0 |
|
||
| month | string | 是 | 预算月份 | 格式:YYYY-MM |
|
||
|
||
#### 5.5.4 更新预算
|
||
|
||
**接口**: `PUT /api/budgets/:id`
|
||
|
||
**请求体** (部分更新):
|
||
```json
|
||
{
|
||
"amount": 2000
|
||
}
|
||
```
|
||
|
||
#### 5.5.5 删除预算
|
||
|
||
**接口**: `DELETE /api/budgets/:id`
|
||
|
||
---
|
||
|
||
### 5.6 统计分析模块 `/api/statistics`
|
||
|
||
#### 5.6.1 月度统计
|
||
|
||
**接口**: `GET /api/statistics/monthly`
|
||
|
||
**说明**: 获取指定月份的总收入、总支出、结余,以及各支出分类的汇总金额(用于饼图)
|
||
|
||
**查询参数**:
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| userId | number | 是 | 用户 ID |
|
||
| month | string | 是 | 月份(YYYY-MM) |
|
||
|
||
**请求示例**:
|
||
```
|
||
GET /api/statistics/monthly?userId=1&month=2026-04
|
||
```
|
||
|
||
**响应** (200):
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"totalIncome": 9000,
|
||
"totalExpense": 520,
|
||
"balance": 8480,
|
||
"categoryStats": [
|
||
{"category": "餐饮", "amount": 68},
|
||
{"category": "交通", "amount": 25},
|
||
{"category": "购物", "amount": 299},
|
||
{"category": "娱乐", "amount": 128}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 5.6.2 趋势统计
|
||
|
||
**接口**: `GET /api/statistics/trend`
|
||
|
||
**说明**: 获取按日期聚合的日级收支数据(用于折线图/柱状图)
|
||
|
||
**查询参数**:
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| userId | number | 是 | 用户 ID |
|
||
| startDate | string | 否 | 开始日期(不传则返回所有数据) |
|
||
| endDate | string | 否 | 结束日期 |
|
||
|
||
**请求示例**:
|
||
```
|
||
GET /api/statistics/trend?userId=1&startDate=2026-04-01&endDate=2026-04-30
|
||
```
|
||
|
||
**排序规则**: 按 `date` 正序
|
||
|
||
**响应** (200):
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": [
|
||
{"date": "2026-04-25", "income": 0, "expense": 128},
|
||
{"date": "2026-04-26", "income": 0, "expense": 299},
|
||
{"date": "2026-04-27", "income": 0, "expense": 93}
|
||
]
|
||
}
|
||
```
|
||
|
||
#### 5.6.3 月度对比
|
||
|
||
**接口**: `GET /api/statistics/compare`
|
||
|
||
**说明**: 获取当前月与上月的收支对比数据(用于环比分析)
|
||
|
||
**查询参数**:
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| userId | number | 是 | 用户 ID |
|
||
| month | string | 是 | 月份(YYYY-MM) |
|
||
|
||
**请求示例**:
|
||
```
|
||
GET /api/statistics/compare?userId=1&month=2026-04
|
||
```
|
||
|
||
**响应** (200):
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"currentMonth": {
|
||
"label": "4月",
|
||
"income": 9000,
|
||
"expense": 520
|
||
},
|
||
"lastMonth": {
|
||
"label": "3月",
|
||
"income": 8500,
|
||
"expense": 1200
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 5.7 仪表盘模块 `/api/dashboard`
|
||
|
||
#### 5.7.1 仪表盘汇总数据
|
||
|
||
**接口**: `GET /api/dashboard/summary`
|
||
|
||
**说明**: 聚合多源数据(账户余额/本月收支/预算使用率),为首页提供一次性数据,减少前端请求次数
|
||
|
||
**查询参数**:
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| userId | number | 是 | 用户 ID |
|
||
|
||
**请求示例**:
|
||
```
|
||
GET /api/dashboard/summary?userId=1
|
||
```
|
||
|
||
**性能优化**: 使用 `Promise.all` 并行查询三类数据(账户、交易、预算)
|
||
|
||
**响应** (200):
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"totalBalance": 18000,
|
||
"monthIncome": 9000,
|
||
"monthExpense": 520,
|
||
"accounts": [
|
||
{
|
||
"id": 1,
|
||
"userId": 1,
|
||
"name": "支付宝",
|
||
"type": "payment",
|
||
"color": "#1890FF",
|
||
"balance": 4932
|
||
},
|
||
{
|
||
"id": 2,
|
||
"userId": 1,
|
||
"name": "微信钱包",
|
||
"type": "payment",
|
||
"color": "#52C41A",
|
||
"balance": 2975
|
||
}
|
||
],
|
||
"budgetUsage": [
|
||
{
|
||
"id": 1,
|
||
"userId": 1,
|
||
"category": "餐饮",
|
||
"amount": 1500,
|
||
"month": "2026-04",
|
||
"spent": 68,
|
||
"percentage": 4.53
|
||
},
|
||
{
|
||
"id": 2,
|
||
"userId": 1,
|
||
"category": "交通",
|
||
"amount": 500,
|
||
"month": "2026-04",
|
||
"spent": 25,
|
||
"percentage": 5
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 5.8 工具接口
|
||
|
||
#### 5.8.1 初始化测试数据
|
||
|
||
**接口**: `GET /api/init-test-data`
|
||
|
||
**说明**: 仅开发环境使用,用于快速创建测试数据。具有幂等性(已有数据则跳过)
|
||
|
||
**安全警告**: 生产环境必须移除此接口
|
||
|
||
**请求示例**:
|
||
```
|
||
GET /api/init-test-data
|
||
```
|
||
|
||
**响应** (200):
|
||
```json
|
||
{
|
||
"success": true,
|
||
"data": {
|
||
"userId": 1
|
||
},
|
||
"message": "数据初始化成功"
|
||
}
|
||
```
|
||
|
||
**初始化数据**:
|
||
- 1 个测试用户(测试用户)
|
||
- 3 个账户(支付宝/微信钱包/招商银行)
|
||
- 6 笔交易记录(2 收入 + 4 支出)
|
||
- 4 笔预算(餐饮/交通/购物/娱乐)
|
||
|
||
---
|
||
|
||
## 六、业务逻辑说明
|
||
|
||
### 6.1 余额联动机制
|
||
|
||
交易记录的创建/更新/删除均会联动更新账户余额,使用 Prisma 事务确保一致性:
|
||
|
||
| 操作 | 账户余额变化 |
|
||
|------|------------|
|
||
| 创建收入记录 | `balance += amount` |
|
||
| 创建支出记录 | `balance -= amount` |
|
||
| 更新记录 | 先冲销原金额,再应用新金额 |
|
||
| 删除记录 | 反向冲销原金额影响 |
|
||
|
||
### 6.2 日期处理机制
|
||
|
||
**问题**: `new Date("YYYY-MM-DD")` 在 JavaScript 中会被当作 UTC 时间解析,导致 UTC+8 时区下出现 8 小时偏移。
|
||
|
||
**解决方案**: 使用安全日期解析函数
|
||
|
||
```javascript
|
||
function parseDate(dateStr) {
|
||
if (dateStr.includes('T')) {
|
||
return new Date(dateStr); // 完整时间戳直接解析
|
||
}
|
||
|
||
const parts = dateStr.split('-');
|
||
if (parts.length === 3) {
|
||
// 按本地时区构造日期,月份从 0 开始
|
||
return new Date(
|
||
parseInt(parts[0]),
|
||
parseInt(parts[1]) - 1,
|
||
parseInt(parts[2])
|
||
);
|
||
}
|
||
|
||
return new Date(dateStr); // 兜底
|
||
}
|
||
```
|
||
|
||
### 6.3 排序字段选择
|
||
|
||
**规则**: 使用 `createdAt` 而非 `date` 进行记录排序
|
||
|
||
**原因**: `date` 字段存储业务日期,可能存在 UTC 转换问题;`createdAt` 是系统自动生成的创建时间戳,更准确反映记录顺序。
|
||
|
||
---
|
||
|
||
## 七、错误码汇总
|
||
|
||
### 7.1 HTTP 状态码
|
||
|
||
| 状态码 | 说明 | 常见场景 |
|
||
|--------|------|---------|
|
||
| 200 | 成功 | 请求成功处理 |
|
||
| 400 | 客户端错误 | 参数缺失、校验失败 |
|
||
| 404 | 资源不存在 | 记录/账户/预算不存在 |
|
||
| 500 | 服务端错误 | 数据库异常 |
|
||
|
||
### 7.2 Prisma 错误码
|
||
|
||
| 错误码 | 说明 | 处理策略 |
|
||
|--------|------|---------|
|
||
| P2002 | 唯一约束冲突 | 返回友好提示(如"邮箱已被注册") |
|
||
| P2025 | 记录不存在 | 返回 404 状态 |
|
||
|
||
---
|
||
|
||
## 八、前端调用示例
|
||
|
||
### 8.1 获取账户列表
|
||
|
||
```typescript
|
||
import { apiClient } from '@/services/apiClient';
|
||
import type { Account, ApiResponse } from '@/types';
|
||
|
||
const response: ApiResponse<Account[]> = await apiClient.get('/accounts', {
|
||
userId: 1
|
||
});
|
||
|
||
if (response.success) {
|
||
console.log('账户列表:', response.data);
|
||
}
|
||
```
|
||
|
||
### 8.2 创建交易记录
|
||
|
||
```typescript
|
||
const response: ApiResponse<Record> = await apiClient.post('/records', {
|
||
userId: 1,
|
||
accountId: 3,
|
||
type: 'expense',
|
||
amount: 68,
|
||
category: '餐饮',
|
||
description: '午饭',
|
||
date: '2026-04-27'
|
||
});
|
||
|
||
if (response.success) {
|
||
console.log('记录创建成功:', response.data);
|
||
}
|
||
```
|
||
|
||
### 8.3 获取仪表盘数据
|
||
|
||
```typescript
|
||
const response: ApiResponse<DashboardSummary> = await apiClient.get(
|
||
'/dashboard/summary',
|
||
{ userId: 1 }
|
||
);
|
||
|
||
if (response.success) {
|
||
const { totalBalance, monthIncome, monthExpense, budgetUsage } = response.data;
|
||
console.log(`总余额: ¥${totalBalance}`);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 九、API 变更记录
|
||
|
||
| 日期 | 版本 | 变更人 | 变更内容 | 影响范围 |
|
||
|------|------|--------|---------|---------|
|
||
| 2026-04-27 | v1.0.0 | 架构师 | 初始版本,定义所有核心接口 | 全模块 |
|
||
| 2026-04-27 | v1.0.1 | 后端 | 修复排序字段(date → createdAt) | /api/records |
|
||
| 2026-04-27 | v1.0.2 | 后端 | 增加日期安全解析逻辑 | POST /api/records |
|
||
|
||
---
|
||
|
||
## 十、安全规范
|
||
|
||
### 10.1 当前状态 (MVP)
|
||
|
||
- 所有接口通过 `userId` 参数做数据隔离
|
||
- CORS 允许所有来源(仅开发环境)
|
||
- 无请求频率限制
|
||
|
||
### 10.2 生产环境要求
|
||
|
||
- [ ] 接入 JWT 鉴权,从 Token 解析 userId,禁止客户端传入
|
||
- [ ] CORS 限制为前端域名
|
||
- [ ] 增加请求频率限制(Rate Limit)
|
||
- [ ] 移除 `/api/init-test-data` 等开发接口
|
||
- [ ] 增加 SQL 注入防护(Prisma 已提供基础防护)
|
||
- [ ] 增加请求体大小限制(防止 DoS 攻击)
|
||
- [ ] 增加 HTTPS 强制
|
||
|
||
---
|
||
|
||
**文档版本**: v1.0.2
|
||
**最后更新**: 2026-04-27
|
||
**维护人**: 架构师
|
||
**反馈渠道**: 提交 Issue 至项目仓库 |