# 开发 mm API 接口

## 规则（Rules）

- API 接口位于 `app/{模块名}/plugin/{插件名}/` 下的 API 目录中，**不能独立存在**
- 支持多种 API 目录模式（同一插件可并存）：
  - `api_client/` — 简洁模式，不带模块前缀
  - `api_{模块名}_client/` — 显式模式，带模块前缀
  - `api_{模块名}_web/` — Web 页面
  - `api_upload/` 等 — 特殊功能目录
- 每个接口包含：`api.json`（路由）+ `index.js`（业务）+ `param.json`（校验，可选）
- **Web 类型 API 必须额外指定 `type:"web"`、`app`、`plugin` 字段**
- **所有开发操作前必须先调用 `get_run_path` 获取运行路径**

## 方法（Methods）

### 步骤0：获取运行路径

使用 `get_run_path` 工具获取当前程序运行根目录，确认 mm_os 项目路径。

### 步骤1：确定接口类型

- **Client 类型**（`api_client` / `api_{模块名}_client` 下）→ 返回 JSON 数据，使用 `$.ret.obj()`
- **Web 类型**（`api_{模块名}_web` 下）→ 返回 HTML 页面，使用 `db.tpl.view()`

### 步骤2：创建目录结构

```
{API目录}/{接口名}/
├── api.json
├── index.js
├── param.json        # 可选
└── sql.json          # 可选
```

### 步骤3：编写 api.json

**Client 类型示例：**

```json
{
    "path": "/api/my_api",
    "name": "api_my_api",
    "title": "我的接口",
    "description": "接口描述",
    "method": "ALL",
    "scope": true,
    "cache": 0,
    "state": 1,
    "oauth": {
        "sign_in": false
    }
}
```

**Web 类型示例（需要额外字段 `type`、`app`、`plugin`）：**

```json
{
    "path": "/sys/my_page",
    "name": "sys_my_page",
    "title": "我的页面",
    "method": "ALL",
    "type": "web",
    "app": "server",
    "plugin": "sys",
    "scope": true,
    "oauth": {
        "sign_in": false
    }
}
```

> **`name` 和 `path` 无固定格式**：`name` 可以是 `api_ls` 或 `sys_pendant`，`path` 可以是 `/api/sys/ls` 或 `/sys/pendant`。建议参照 `app/sys/plugin/main/` 下已有 API 的写法保持一致。

### 步骤4：编写 index.js

Client API（返回 JSON）：
```javascript
async function main(ctx, db) {
    var { query, body } = ctx.request;
    var result = await doSomething(query);
    return $.ret.obj({ success: true, data: result });
}
exports.main = main;
```

Web 页面（返回 HTML）：
```javascript
async function main(ctx, db) {
    var model = {};
    return db.tpl.view("./index.html".fullname(__dirname), model);
}
exports.main = main;
```

### 步骤5：编写 param.json 参数校验

```json
{
    "filter": true,
    "get": {
        "query": ["name"],
        "query_required": ["name"]
    },
    "post": {
        "body": ["data"],
        "body_required": []
    },
    "list": [{
        "name": "name",
        "title": "名称",
        "type": "string",
        "string": { "range": [1, 50] }
    }]
}
```

### 步骤6：注册 API

在所属插件的 `_init` 中注册：

```javascript
var api = $.admin.api('分组名', '分组标题');
await api.call('update', 'app/');
```

## 技巧（Tips）

- **返回格式**：成功用 `$.ret.obj(data)`，失败用 `$.ret.error(code, msg)`，布尔用 `$.ret.bl(success, msg)`
- **请求参数**：GET 在 `ctx.request.query`，POST 在 `ctx.request.body`
- **数据库操作**：通过 `db.new(table, primaryKey)` 创建数据管理器
- **Web vs Client**：Web 类型必须在 api.json 中加 `"type":"web"`、`"app"`、`"plugin"`，否则框架无法正确路由
- **命名参照**：`app/sys/plugin/main/` 和 `app/sys/plugin/tencent/` 是完整参考
- **运行路径**：始终通过 `get_run_path` 获取，不硬编码绝对路径
