# API接口快速入门

## 🚀 5分钟快速上手

### 第一步：创建API控制器

在 `app/api/controller/` 目录下创建 `Demo.php`：

```php
<?php
namespace app\api\controller;

use iyibai\bases\BaseApi;
use think\facade\Db;

class Demo extends BaseApi
{
    /**
     * @name Hello World
     * @desc 第一个API接口
     */
    public function hello()
    {
        $data = [
            'message' => 'Hello World!',
            'time' => date('Y-m-d H:i:s'),
            'user_id' => $this->user_id,
            'mch_id' => $this->mch_id
        ];
        
        return $this->success($data, '调用成功');
    }
}
```

### 第二步：测试接口

#### 方式1：浏览器访问（跳过签名验证）

```
http://yourdomain.com/api.php?do=app.api.Demo.hello&i=1&q=0
```

参数说明：
- `do`: 接口路径（必需）
- `i`: 商户ID（必需）
- `q`: 验证模式，0=跳过验证（开发阶段使用）

#### 方式2：命令行测试

```bash
curl "http://localhost/api.php?do=app.api.Demo.hello&i=1&q=0"
```

预期响应：

```json
{
    "code": 0,
    "msg": "调用成功",
    "data": {
        "message": "Hello World!",
        "time": "2025-01-20 10:00:00",
        "user_id": 0,
        "mch_id": 1
    }
}
```

---

## 📝 常用功能示例

### 1. 获取请求参数

```php
public function getUserInfo()
{
    // 获取单个参数
    $userId = $this->request->param('user_id', 0);
    
    // 获取所有参数（自动过滤路由参数）
    $params = $this->param();
    
    // 只获取POST参数
    $postData = $this->post();
    
    return $this->success($params);
}
```

### 2. 数据库查询

```php
public function getList()
{
    $list = Db::name('article')
        ->where('mch_id', $this->mch_id)
        ->where('status', 1)
        ->limit(10)
        ->select();
    
    return $this->success($list, '获取成功');
}
```

### 3. 参数验证

```php
public function create()
{
    $params = $this->param();
    
    // 简单验证
    if (empty($params['title'])) {
        return $this->error('标题不能为空', 1001);
    }
    
    if (empty($params['content'])) {
        return $this->error('内容不能为空', 1002);
    }
    
    // 保存数据
    $id = Db::name('article')->insertGetId([
        'title' => $params['title'],
        'content' => $params['content'],
        'mch_id' => $this->mch_id,
        'create_time' => time()
    ]);
    
    return $this->success(['id' => $id], '创建成功');
}
```

### 4. 异常处理

```php
public function update()
{
    try {
        $id = $this->request->param('id', 0);
        
        $result = Db::name('article')
            ->where('id', $id)
            ->where('mch_id', $this->mch_id)
            ->update([
                'title' => $this->request->param('title'),
                'update_time' => time()
            ]);
        
        if ($result) {
            return $this->success([], '更新成功');
        } else {
            return $this->error('更新失败', 1003);
        }
        
    } catch (\Exception $e) {
        return $this->error('系统异常: ' . $e->getMessage(), 500);
    }
}
```

---

## 🔐 启用签名验证

### 第一步：生成签名

```php
<?php
use app\common\service\ApiSignService;

// 准备参数
$params = [
    'do' => 'app.api.Demo.hello',
    'i' => 1,
    'user_id' => 123,
    'nonce_str' => uniqid(),  // 随机字符串
    'timestamp' => time()
];

// 生成签名
$key = ApiSignService::getSignKey(1);
$sign = ApiSignService::makeSign($params, $key);

$params['sign'] = $sign;
$params['q'] = 1;  // 启用签名验证

// 构建URL
$url = 'http://yourdomain.com/api.php?' . http_build_query($params);
```

### 第二步：小程序调用示例

```javascript
// 小程序端签名生成函数
function makeSign(params, key) {
    // 1. 参数排序
    let keys = Object.keys(params).sort();
    
    // 2. 拼接字符串
    let str = '';
    keys.forEach(k => {
        if (k !== 'sign' && params[k] !== '') {
            str += k + '=' + params[k] + '&';
        }
    });
    
    str = str.slice(0, -1) + '&key=' + key;
    
    // 3. MD5加密（需要引入MD5库）
    return md5(str);
}

// 调用接口
let params = {
    do: 'app.api.Demo.hello',
    i: 1,
    user_id: 123,
    nonce_str: Date.now().toString(),
    timestamp: Math.floor(Date.now() / 1000)
};

// 生成签名
params.sign = makeSign(params, 'your_key_here');
params.q = 1;

wx.request({
    url: 'https://yourdomain.com/api.php',
    data: params,
    success: (res) => {
        if (res.data.code === 0) {
            console.log('成功:', res.data.data);
        } else {
            console.log('失败:', res.data.msg);
        }
    }
});
```

---

## 🎯 进阶技巧

### 1. 使用服务层

创建 `app/common/service/DemoService.php`：

```php
<?php
namespace app\common\service;

use think\facade\Db;

class DemoService
{
    /**
     * 获取数据列表
     */
    public static function getList(int $mchId, int $page = 1, int $limit = 20)
    {
        return Db::name('demo')
            ->where('mch_id', $mchId)
            ->page($page, $limit)
            ->select();
    }
}
```

在控制器中调用：

```php
use app\common\service\DemoService;

public function list()
{
    $page = $this->request->param('page', 1);
    $limit = $this->request->param('limit', 20);
    
    $list = DemoService::getList($this->mch_id, $page, $limit);
    
    return $this->success($list);
}
```

### 2. 返回分页数据

```php
public function getArticles()
{
    $page = $this->request->param('page', 1);
    $limit = $this->request->param('limit', 20);
    
    $query = Db::name('article')
        ->where('mch_id', $this->mch_id)
        ->where('status', 1);
    
    $total = $query->count();
    $list = $query->page($page, $limit)->select();
    
    return $this->success([
        'list' => $list,
        'total' => $total,
        'page' => $page,
        'limit' => $limit,
        'pages' => ceil($total / $limit)
    ]);
}
```

### 3. 文件上传

```php
public function upload()
{
    $file = $this->request->file('image');
    
    if (!$file) {
        return $this->error('请选择文件', 1001);
    }
    
    try {
        $savePath = app()->getRootPath() . 'public/uploads/';
        $saveName = \think\facade\Filesystem::disk('public')->putFile('images', $file);
        
        return $this->success([
            'url' => '/uploads/' . $saveName
        ], '上传成功');
        
    } catch (\Exception $e) {
        return $this->error('上传失败: ' . $e->getMessage(), 1002);
    }
}
```

---

## 🐛 调试技巧

### 1. 查看日志

```bash
# 实时查看日志
tail -f runtime/log/202X-XX-XX.log

# 过滤API相关日志
tail -f runtime/log/202X-XX-XX.log | grep "API"

# 查看签名验证日志
tail -f runtime/log/202X-XX-XX.log | grep "签名"
```

### 2. 跳过验证（开发阶段）

在URL中添加 `q=0` 参数即可跳过所有验证：

```
http://localhost/api.php?do=app.api.Demo.hello&i=1&q=0
```

### 3. 使用Postman测试

1. 创建新请求
2. 方法选择 `GET` 或 `POST`
3. URL填写：`http://yourdomain.com/api.php`
4. 添加参数：
   - `do`: app.api.Demo.hello
   - `i`: 1
   - `q`: 0
5. 发送请求查看响应

---

## ⚠️ 常见错误

### 错误1：接口不存在

```json
{
    "code": 0,
    "msg": "接口不存在"
}
```

**解决方法**：检查 `do` 参数是否正确，确保控制器和方法存在。

### 错误2：签名验证失败

```json
{
    "code": 0,
    "msg": "签名验证失败"
}
```

**解决方法**：
1. 检查签名算法是否正确
2. 确认密钥是否正确
3. 参数是否完整
4. 开发阶段可使用 `q=0` 跳过验证

### 错误3：参数缺失

```json
{
    "code": 0,
    "msg": "签名参数缺失"
}
```

**解决方法**：确保传递了必需参数：`do`、`i`、`nonce_str`、`sign`

---

## 📚 相关文档

- [API架构说明](./API_ARCHITECTURE.md)
- [迁移指南](./MIGRATION_GUIDE.md)
- [更新日志](../../CHANGELOG_API.md)

---

## 💡 最佳实践

1. ✅ 控制器只处理请求和响应，业务逻辑放在服务层
2. ✅ 所有成功返回使用 `code=0`，错误返回使用 `code>0`
3. ✅ 必须使用 `try-catch` 捕获异常
4. ✅ 敏感操作记录日志
5. ✅ 开发阶段使用 `q=0`，生产环境启用签名验证
6. ✅ 为每个接口编写清晰的注释

---

更新日期: 2025-01-XX
