# API实现

## 规则（Rules）

# API接口开发规范

## 适用对象和范围

本规范适用于所有实现后端API接口的场景，包括路由处理器、控制器、业务逻辑、参数校验和数据模型编写。

---

## 1. 分层职责规范

**规则**：必须严格遵循分层架构，每层只做自己的事。

| 层次 | 职责 | 禁止做的事 |
|------|------|-----------|
| 路由（routes） | URL映射和方法分发 | 写业务逻辑 |
| 控制器（controllers） | 处理请求、调用服务、返回响应 | 直接操作数据库 |
| 模型（models） | 数据访问和数据库操作 | 处理请求/响应 |
| 校验器（validators） | 请求参数校验 | 修改数据 |

- ✅ 正确：路由只做 `router.get('/users', userController.list)`
- ❌ 错误：在路由中写 `router.get('/users', async (req, res) => { await db.query(...) })`

**违反后果**：分层混乱导致代码耦合度高，难以维护和测试。

---

## 2. 命名规范

**规则**：函数使用小驼峰（camelCase），变量和参数使用小写蛇形（snake_case），常量使用全大写蛇形（SCREAMING_SNAKE_CASE）。

| 标识符类型 | 命名法 | 示例 |
|-----------|--------|------|
| 函数 | camelCase | `function getUserData(user_id) {}` |
| 变量/参数 | snake_case | `let user_name = 'xxx';` |
| 常量 | SCREAMING_SNAKE_CASE | `const MAX_RETRY_COUNT = 3;` |

**违反后果**：命名不一致导致代码可读性差。

---

## 3. 缩进规范

**规则**：统一使用2个空格缩进。

**违反后果**：缩进不一致导致代码结构混乱。

---

## 4. 注释规范

**规则**：函数必须使用JSDoc格式注释。

```javascript
/**
 * 获取用户数据
 * @param {number} user_id - 用户ID
 * @returns {Promise<Object>} 用户数据对象
 */
async function getUserData(user_id) { ... }
```

**违反后果**：缺少JSDoc注释导致IDE无法提供类型提示。

---

## 5. 参数校验规范

**规则**：所有用户输入必须经过参数校验，禁止信任任何外部输入。

- ✅ 正确：校验器检查字段类型、格式、必填性
- ❌ 错误：直接将用户输入传给数据库查询

**违反后果**：未校验用户输入存在SQL注入等安全风险。

---

## 6. 错误处理规范

**规则**：控制器中的异步操作必须使用 try/catch 捕获异常，返回统一错误格式。

- ✅ 正确：
```javascript
async function list(req, res) {
  try {
    const users = await userModel.findAll();
    res.json({ code: 0, data: users });
  } catch (error) {
    res.status(500).json({ code: 500, message: error.message });
  }
}
```

**违反后果**：未捕获异常导致服务器崩溃或返回500但不含有用信息。

## 方法（Methods）

# API接口开发方法

## 前置条件

- [ ] 已获取API设计文档（路由、参数、响应格式）
- [ ] 已了解数据模型定义
- [ ] 已搭建好服务端框架

## 流程概览

分析API设计文档 → 编写路由定义 → 编写参数校验器 → 编写数据模型 → 编写控制器 → 注册路由到应用入口 → 验证接口完整性

## 详细步骤

### 步骤1：分析API设计文档
分析API设计文档，提取每个接口的以下信息：
- 请求方法（GET/POST/PUT/DELETE）
- 路由路径
- 请求参数（路径参数、查询参数、请求体）
- 响应格式
- 业务逻辑描述

## 技巧（Tips）

# API接口开发技巧

## 1. 使用路由前缀统一管理版本

**适用场景**：需要支持多个API版本时。

**具体做法**：使用 `express.Router()` 的prefix参数统一添加版本前缀。

**示例**：
```javascript
// routes/index.js
const router = require('express').Router();
const userRoutes = require('./user_routes');

router.use('/api/v1/users', userRoutes);

module.exports = router;
```

**注意事项**：版本号建议放在路由层，而不是在每个路由路径中手动添加。

---

## 2. 封装统一响应工具函数

**适用场景**：需要确保所有接口返回格式一致时。

**具体做法**：在 `utils/response.js` 中封装成功和失败的响应函数。

**对比说明**：

| 维度 | 每个接口手动构造响应 | 统一响应函数 |
|------|-------------------|-------------|
| 代码量 | 每个接口都要写格式 | 一行调用 |
| 一致性 | 容易遗漏字段 | 自动保证格式统一 |
| 修改 | 需改所有接口 | 只改响应函数 |

**示例**：
```javascript
// utils/response.js
exports.success = (res, data, message = 'success') => {
  res.json({ code: 0, message, data });
};

exports.fail = (res, message, code = 400) => {
  res.status(code).json({ code, message, data: null });
};

// controllers/user_controller.js
const { success, fail } = require('../utils/response');

exports.list = async (req, res) => {
  try {
    const users = await userModel.findAll();
    success(res, users);
  } catch (error) {
    fail(res, error.message, 500);
  }
};
```

**注意事项**：错误码应与前端约定一致，建议维护一个错误码表。

---

## 3. 使用事务处理关联操作

**适用场景**：需要同时操作多个表，保证数据一致性时。

**具体做法**：使用数据库事务包裹多个操作。

**示例**：
```javascript
// 使用数据库事务
async function createOrder(userId, items) {
  const conn = await db.getConnection();
  try {
    await conn.beginTransaction();
    
    const order = await conn.query('INSERT INTO orders ...');
    for (const item of items) {
      await conn.query('INSERT INTO order_items ...', [order.id, ...]);
    }
    
    await conn.commit();
    return order;
  } catch (error) {
    await conn.rollback();
    throw error;
  } finally {
    conn.release();
  }
}
```

**注意事项**：事务会锁表，尽量保持事务简短，避免在事务中执行耗时操作。

---

## 4. 常见问题速查

| 问题 | 原因 | 解决方案 |
|------|------|---------|
| 路由404 | 路由未注册或路径错误 | 检查 `app.js` 中是否 `use` 了路由 |
| 请求体为空 | 未配置body解析中间件 | 添加 `express.json()` |
| 参数校验不通过 | 校验规则过于严格 | 检查校验器的字段名和类型是否匹配 |
| 数据库查询超时 | 缺少索引或查询量过大 | 添加索引或优化查询条件 |
| 跨域请求被拦截 | 未配置CORS | 添加 `cors` 中间件 |
