# PingCAP Account API 文档

## 说明

> 所有的 API 调用都有频率限制，未认证用户 30 次/分钟, 认证用户 100 次/分钟，超出调用频率会返回 HTTP-Status-Code 429  
> i18n支持(zh-hans/en-us), 在请求 Header 中声明： Accept-Language: zh-hans  
> 当 cookie 中有 csrftoken 时, put|post|delete|patch 等请求需在 header 中声明: X-CSRFTOKEN  
> 以下 API 文档中均未提及 5xx 错误, 但你在使用 API 的时候应考虑到相关错误(包括网络延时)

### 关于跨域

以下接口同时部署在了 `https://accounts.pingcap.cn/api/`、`https://tidb.io/api/` 和 `https://asktug.com/_/sso/api/` 目录下，在这三个域名下你只需要设置不同的 URL Prefix 即可调用对应接口, 所以不会涉及到进行跨域的问题。

- [获取用户等级和经验](#获取用户等级和经验)
- [每日签到](#每日签到)
- [获取商品列表](#获取商品列表)
- [兑换商品 发送验证码](#兑换商品-发送验证码)
- [兑换商品](#兑换商品)
- [获取兑换记录](#获取兑换记录)
- [获取积分记录](#获取积分记录)
- [授予用户积分和经验](#授予用户积分和经验)

### 获取用户等级和经验

#### Request

- Method: **GET**
- URL: `/api/points/me`
- Content-Type: application/json
- Permission: Authenticated User

#### Response Example

- Success

  - HTTP 200

    ```json
    {
      "detail": "success",
      "data": {
          "current_level": 5,     // 等级
          "current_points": 348,  // 积分
          "current_exps": 828,    // 经验
          "current_rank": 123,    // 经验排名
          "is_today_checked": true,  // 今天是否签到
          "level_desc": {         // 当前等级描述
              "min_exps": 500,    // 当前等级的最低经验要求
              "max_exps": 999,    // 下一等级的最低经验要求
              "progress": 0.6573  // 当前等级进度
          }
      }
    }
    ```

- Failed

  - HTTP 401

    ```json
    {
      "detail": "Authentication credentials were not provided"
    }
    ```

### 每日签到

#### Request

- Method: **POST**
- URL: `/api/points/daily-checkin`
- Content-Type: application/json
- Permission: Authenticated User

#### Response Example

- Success

  - HTTP 200 签到成功

    ```json
    {
      "detail": "success",
      "data": {
          "continues_checkin_count": 1,  // 已连续签到多少天
          "points": 2,                   // 获得积分奖励
          "tomorrow_points": 3            // 明日继续签到将获得的积分
      }
    }
    ```

- Failed

  - HTTP 401 未登录

    ```json
    {
      "detail": "Authentication credentials were not provided"
    }
    ```

  - HTTP 409 今日已签到

    ```json
    {
      "detail": "already checked in"
    }
    ```

### 获取商品列表

#### Request

- Method: **GET**
- URL: `/api/points/shop/products`
- Content-Type: application/json
- Permission: Authenticated User

#### Response Example

- Success

  - HTTP 200

    ```json
    {
      "detail": "success",
      "data": [
        {
            "id": 330010,    // 商品 ID
            "name": "TiDB & Flink 联名限量款双肩包",  // 商品名称
            "cover_url": "https://img2.pingcap.com/forms/b/9/b93839175459283bfd72a96601c6c7cf73ffcb3c.png",  // 商品展示图
            "points": 1500, // 兑换需要的积分
            "remains": 58  // 商品剩余数量
        },
        {
            "id": 390010,    // 商品 ID
            "name": "限量纸质版】数据库架构选型指南",  // 商品名称
            "cover_url": "https://img2.pingcap.com/forms/2/0/2037945e7f15ca1886c15ea0756f979ab46ad3ad_TO7hDor.png",  // 商品展示图
            "points": 111, // 兑换需要的积分
            "remains": 107  // 商品剩余数量
        }
      ]
    }
    ```

- Failed

  - HTTP 401 未登录

    ```json
    {
      "detail": "Authentication credentials were not provided"
    }
    ```

### 兑换商品 发送验证码

#### Request

- Method: **POST**
- URL: `/api/sms/code`
- Content-Type: application/json
- Permission: Authenticated User
- Body

  ```json
  { 
    "phone" : "136xxxxxxxx",  // 手机号, 必填
  }
  ```

#### Response Example

- Success

  - HTTP 200 发送成功

    ```json
    {
      "detail": "success"
    }
    ```

- Failed

  - HTTP 400 参数错误

    ```json
    {
      "detail": "params error",
      "errors": {
          "phone": ["this field is required"],  // 键是传入的名参数; 值是一个数组, 其中包含了具体的错误信息
      }
    }
    ```

  - HTTP 401 未登录

    ```json
    {
      "detail": "Authentication credentials were not provided"
    }
    ```

  - HTTP 429 频率限制, 同一手机号, 1 分钟内只能发送一次验证码

    ```json
    {
      "detail": "rate limit"
    }
    ```

### 兑换商品

#### Request

- Method: **POST**
- URL: `/api/points/shop/redeem`
- Content-Type: application/json
- Permission: Authenticated User
- Body

  ```json
  { 
    "product_id" : 330010,  // 商品 ID, 必填
    "consignee_name": "张三", // 收件人姓名, 必填
    "consignee_phone": "136xxxxxxxx", // 收件人手机号, 必填
    "consignee_phone_code": "123456", // 收件人手机验证码, 必填
    "consignee_addrss": "北京", // 收货地址, 必填
    "comment": "无", // 订单备注, 必填
  }
  ```

#### Response Example

- Success

  - HTTP 200

    ```json
    {
      "detail": "submit success",
      "data": {
          "redeem_success": true, // 是否兑换成功,
          "redeem_record_id": 1, // 兑换记录 ID, 如果兑换失败则为 null
          "comment": "兑换失败，商品不存在或已下架，请刷新重试", // 兑换失败的原因
      }
    }
    ```

- Failed

  - HTTP 400 参数错误

    ```json
    {
      "detail": "params error",
      "errors": {
          "product_id": ["this field is required"],  // 键是传入的名参数; 值是一个数组, 其中包含了具体的错误信息
          "consignee_phone_code": ["this field is required"]
      }
    }
    ```

  - HTTP 401 未登录

    ```json
    {
      "detail": "Authentication credentials were not provided"
    }
    ```

### 获取兑换记录

#### Request

- Method: **GET**
- URL: `/api/points/shop/redeem-records`
- Content-Type: application/json
- Permission: Authenticated User

#### Response Example

- Success

  - HTTP 200

    ```json
    {
      "detail": "success",
      "data": [
        {
            "product_id": 30010,  // 商品 ID
            "product_name": "TiDB U型枕",  // 商品名称
            "points": 500,   // 兑换消耗的积分
            "consignee_name": "张三",  // 收件人名称
            "consignee_phone": "136xxxxxxxx",  // 收件人手机号
            "consignee_addrss": "北京市",  // 收货地址
            "comment": "无", // 订单备注
            "courier_company": "顺丰",  // 发货的物流公司, 未发货时为空
            "tracking_number": "SF1421361031071",  // 物流号码, 未发货时为空
            "created_at": "2021-06-22/14:29"  // 兑换时间
        }
      ]
    }
    ```

- Failed

  - HTTP 401 未登录

    ```json
    {
      "detail": "Authentication credentials were not provided"
    }
    ```

### 获取积分记录

#### Request

- Method: **GET**
- URL: `/api/points/awarded`
- Params:
  | 参数名 | 类型 | 默认值| 是否必填 | 说明 |
  | ---- | ---- | ---- | ---- | ----- |
  | currentPage | int | 1 | 否 | 当前页数 |
  | perPage | int | 10 | 否 | 每页条目数 |
- Content-Type: application/json
- Permission: Authenticated User

#### Response Example

- Success

  - HTTP 200

    ```json
    {
      "detail": "success",
      "data": [
        {
            "digest": "连续签到 1 天",   // 摘要
            "points": 2,               // 获得积分数量, 可能为负
            "exps": 2,                 // 获得经验数量, 可能为负
            "comment": "",             // 备注
            "created_at": "2022-01-04" // 创建时间
        },
        {
            "digest": "绑定 GitHub 账号",
            "points": 20,
            "exps": 10,
            "comment": "",
            "created_at": "2021-06-08"
        }
      ],
      "total_num": 150, // 总条目数
      "per_page": 10,   // 每页条目数
      "cur_page": 1     // 当前页数
    }
    ```

- Failed

  - HTTP 401 未登录

    ```json
    {
      "detail": "Authentication credentials were not provided"
    }
    ```

### 授予用户积分和经验

#### Request

- Method: **POST**
- URL: `/api/admin/points/users/{username or openid}/manual-award`
- Content-Type: application/json
- Permission: Staff
- Body

  ```json
  {
    "digest": "摘要",       // 授予概要, 必填, 用户可见
    "reason": "管理员备注",  // 授予原因, 必填, 用户不可见
    "points": 1,           // 积分值, 整数, 必填
    "exps": 1              // 经验值, 整数, 必填
  }
  ```

#### Response Example

- Success

  - HTTP 200

    ```json
    {
      "detail": "success"
    }
    ```

- Failed

  - HTTP 400 参数错误

    ```json
    {
      "detail": "params error",
      "errors": {
          "digest": ["this field is required"],  // 键是传入的名参数; 值是一个数组, 其中包含了具体的错误信息
          "reason": ["this field is required"]
      }
    }
    ```

  - HTTP 401 未登录

    ```json
    {
      "detail": "Authentication credentials were not provided"
    }
    ```

  - HTTP 404 用户未找到
