{
  "openapi": "3.0.3",
  "info": {
    "title": "智兰 Open API",
    "description": "智兰智能兰花养护系统 — 第三方开放接口。\n\n供 AI Agent、智能家居平台等第三方应用接入，读取用户设备的传感器数据和状态信息。\n\n## 快速开始\n\n1. 用户在小程序「我的 → Open API」中生成 **6位授权码**（5分钟有效，一次性）\n2. 第三方应用调用 `POST /openapi/exchange-token` 用授权码换取长期 Token（`zk_` 前缀）\n3. 后续请求携带 `Authorization: Bearer zk_xxx` 访问设备数据\n\n## 权限说明\n\n- 所有设备接口为 **只读**，不支持远程控制\n- Token 仅能访问该用户名下的设备\n- 频率限制：60 次/分钟/Token\n",
    "version": "1.0.0",
    "contact": {
      "name": "智兰技术支持",
      "email": "zenlan_cn@qq.com",
      "url": "https://zenlan.cn"
    }
  },
  "servers": [
    {
      "url": "https://api.zenlan.cn/api/v1",
      "description": "生产环境"
    },
    {
      "url": "http://localhost:8080/api/v1",
      "description": "本地开发"
    }
  ],
  "tags": [
    {
      "name": "授权",
      "description": "授权码生成与 Token 交换"
    },
    {
      "name": "Token 管理",
      "description": "API Token 的创建、查询、吊销"
    },
    {
      "name": "设备",
      "description": "设备列表与详情查询"
    },
    {
      "name": "数据",
      "description": "传感器历史数据与趋势"
    }
  ],
  "security": [
    {
      "TokenAuth": []
    }
  ],
  "paths": {
    "/openapi/auth-code": {
      "post": {
        "tags": [
          "授权"
        ],
        "summary": "生成授权码",
        "description": "小程序端调用，生成 6 位数字授权码（5 分钟有效，一次性使用）。需要用户登录态（JWT）。",
        "security": [
          {
            "CendAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "生成成功",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiResponse"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "code": {
                              "type": "string",
                              "description": "6位数字授权码",
                              "example": "837261"
                            },
                            "expires_in": {
                              "type": "integer",
                              "description": "有效期（秒）",
                              "example": 300
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          }
        }
      }
    },
    "/openapi/exchange-token": {
      "post": {
        "tags": [
          "授权"
        ],
        "summary": "用授权码换取 Token",
        "description": "用小程序生成的授权码换取长期 API Token。授权码只能使用一次，5 分钟后过期。",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "auth_code"
                ],
                "properties": {
                  "auth_code": {
                    "type": "string",
                    "description": "6位数字授权码",
                    "example": "837261"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "交换成功",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiResponse"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "token": {
                              "type": "string",
                              "description": "API Token（zk_ 前缀，请妥善保存，仅显示一次）",
                              "example": "zk_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6"
                            },
                            "id": {
                              "type": "integer",
                              "example": 1
                            },
                            "name": {
                              "type": "string",
                              "example": "默认 Token"
                            },
                            "scope": {
                              "type": "string",
                              "example": "read_only"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "description": "授权码无效或已过期",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponse"
                }
              }
            }
          }
        }
      }
    },
    "/openapi/tokens": {
      "get": {
        "tags": [
          "Token 管理"
        ],
        "summary": "列出所有 Token",
        "description": "获取当前用户的所有 API Token 列表（Token 值不再返回，仅显示前缀）",
        "responses": {
          "200": {
            "description": "查询成功",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiResponse"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "tokens": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/ApiToken"
                              }
                            },
                            "total": {
                              "type": "integer",
                              "example": 2
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Token 管理"
        ],
        "summary": "创建新 Token",
        "description": "创建一个新的 API Token（zk_ 前缀）。Token 值仅创建时返回一次，请妥善保存。",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 64,
                    "description": "Token 名称（便于识别用途）",
                    "example": "Home Assistant"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "创建成功",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiResponse"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "token": {
                              "type": "string",
                              "description": "API Token（仅此一次返回完整值）",
                              "example": "zk_f6e5d4c3b2a1f0e9d8c7b6a5f4e3d2c1"
                            },
                            "id": {
                              "type": "integer",
                              "example": 3
                            },
                            "name": {
                              "type": "string",
                              "example": "Home Assistant"
                            },
                            "scope": {
                              "type": "string",
                              "example": "read_only"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          }
        }
      }
    },
    "/openapi/tokens/{id}": {
      "delete": {
        "tags": [
          "Token 管理"
        ],
        "summary": "吊销 Token",
        "description": "吊销指定的 API Token，吊销后立即失效。",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            },
            "description": "Token ID"
          }
        ],
        "responses": {
          "200": {
            "description": "吊销成功",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiResponse"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "message": {
                              "type": "string",
                              "example": "Token 已吊销"
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/openapi/devices": {
      "get": {
        "tags": [
          "设备"
        ],
        "summary": "获取设备列表",
        "description": "获取当前用户名下所有设备的摘要信息，包含在线状态、执行器开关、最新物模型快照。",
        "responses": {
          "200": {
            "description": "查询成功",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiResponse"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "devices": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/DeviceSummary"
                              }
                            },
                            "total": {
                              "type": "integer",
                              "example": 1
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/openapi/devices/{device_name}": {
      "get": {
        "tags": [
          "设备"
        ],
        "summary": "获取设备详情",
        "description": "获取指定设备的完整信息，包含设备属性、MQTT 配置等。",
        "parameters": [
          {
            "name": "device_name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "设备序列号（device_name）",
            "example": "00000001"
          }
        ],
        "responses": {
          "200": {
            "description": "查询成功",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiResponse"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/DeviceDetail"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "无权访问此设备"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/openapi/devices/{device_name}/data": {
      "get": {
        "tags": [
          "数据"
        ],
        "summary": "获取历史遥测数据",
        "description": "获取设备的历史传感器遥测数据。列定义从产品物模板动态解析（仅包含数值型遥测属性），\n无模板时从首条记录的 payload 扫描 key 作为兜底。\n\n返回 `columns`（列定义）+ `records`（数据行），每行包含时间戳和各属性值。\n",
        "parameters": [
          {
            "name": "device_name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "设备序列号",
            "example": "00000001"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20,
              "minimum": 1,
              "maximum": 100
            },
            "description": "返回条数"
          }
        ],
        "responses": {
          "200": {
            "description": "查询成功",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiResponse"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "$ref": "#/components/schemas/TelemetryResponse"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "无权访问此设备"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/openapi/devices/{device_name}/data/trend": {
      "get": {
        "tags": [
          "数据"
        ],
        "summary": "获取趋势数据",
        "description": "获取设备按天聚合的趋势数据，包含温度（均值/最高/最低）、湿度均值、光照均值、土壤湿度均值。",
        "parameters": [
          {
            "name": "device_name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "设备序列号",
            "example": "00000001"
          },
          {
            "name": "days",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 7,
              "minimum": 1,
              "maximum": 365
            },
            "description": "查询天数"
          }
        ],
        "responses": {
          "200": {
            "description": "查询成功",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    {
                      "$ref": "#/components/schemas/ApiResponse"
                    },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "object",
                          "properties": {
                            "device_name": {
                              "type": "string",
                              "example": "00000001"
                            },
                            "data": {
                              "type": "array",
                              "items": {
                                "$ref": "#/components/schemas/DayStats"
                              }
                            },
                            "total": {
                              "type": "integer",
                              "example": 7
                            }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "403": {
            "description": "无权访问此设备"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "TokenAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "API Token 认证（zk_ 前缀）"
      },
      "CendAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "C端用户 JWT（微信小程序登录态）"
      }
    },
    "responses": {
      "BadRequest": {
        "description": "请求参数错误",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ApiResponse"
            },
            "example": {
              "code": 400,
              "message": "参数错误",
              "data": null
            }
          }
        }
      },
      "Unauthorized": {
        "description": "未授权（Token 无效或过期）",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ApiResponse"
            },
            "example": {
              "code": 401,
              "message": "未授权",
              "data": null
            }
          }
        }
      },
      "NotFound": {
        "description": "资源不存在",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ApiResponse"
            },
            "example": {
              "code": 404,
              "message": "资源不存在",
              "data": null
            }
          }
        }
      }
    },
    "schemas": {
      "ApiResponse": {
        "type": "object",
        "properties": {
          "code": {
            "type": "integer",
            "description": "业务状态码（0=成功）",
            "example": 0
          },
          "message": {
            "type": "string",
            "example": "success"
          },
          "data": {
            "description": "响应数据",
            "nullable": true
          }
        },
        "required": [
          "code",
          "message"
        ]
      },
      "ApiToken": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "example": 1
          },
          "user_id": {
            "type": "integer",
            "example": 2
          },
          "name": {
            "type": "string",
            "description": "Token 名称",
            "example": "默认 Token"
          },
          "token_prefix": {
            "type": "string",
            "description": "Token 前8位（用于识别）",
            "example": "zk_a1b2"
          },
          "scope": {
            "type": "string",
            "description": "权限范围",
            "enum": [
              "read_only"
            ],
            "example": "read_only"
          },
          "status": {
            "type": "integer",
            "description": "状态（1=有效, 0=已吊销）",
            "enum": [
              0,
              1
            ],
            "example": 1
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "DeviceSummary": {
        "type": "object",
        "properties": {
          "device_name": {
            "type": "string",
            "description": "设备序列号",
            "example": "00000001"
          },
          "name": {
            "type": "string",
            "description": "设备名称",
            "example": "我的春兰"
          },
          "status": {
            "type": "string",
            "description": "在线状态",
            "enum": [
              "online",
              "offline"
            ],
            "example": "online"
          },
          "work_mode": {
            "type": "string",
            "description": "工作模式",
            "example": "smart"
          },
          "connection_type": {
            "type": "string",
            "description": "连接方式",
            "enum": [
              "wifi_ble",
              "ble",
              "ble_gateway"
            ],
            "example": "wifi_ble"
          },
          "led_on": {
            "type": "boolean",
            "description": "补光灯状态",
            "example": true
          },
          "fan_on": {
            "type": "boolean",
            "description": "通风状态",
            "example": false
          },
          "mist_on": {
            "type": "boolean",
            "description": "加湿状态",
            "example": false
          },
          "last_online_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "temperature": {
            "type": "number",
            "description": "当前温度（°C）",
            "example": 24.5
          },
          "humidity": {
            "type": "number",
            "description": "当前湿度（%）",
            "example": 68
          },
          "light": {
            "type": "integer",
            "description": "当前光照（lux）",
            "example": 3500
          },
          "soil_moisture": {
            "type": "integer",
            "description": "当前土壤湿度（%）",
            "example": 45
          }
        }
      },
      "DeviceDetail": {
        "type": "object",
        "description": "设备完整信息（对应 devices 表全部字段）",
        "properties": {
          "id": {
            "type": "integer",
            "example": 2
          },
          "device_name": {
            "type": "string",
            "example": "00000001"
          },
          "name": {
            "type": "string",
            "example": "我的春兰"
          },
          "product_key": {
            "type": "string",
            "example": "ab4b10fc"
          },
          "status": {
            "type": "string",
            "enum": [
              "online",
              "offline"
            ],
            "example": "online"
          },
          "connection_type": {
            "type": "string",
            "example": "wifi_ble"
          },
          "wifi_mac": {
            "type": "string",
            "example": "E8:F6:0A:D7:0B:74"
          },
          "location": {
            "type": "string",
            "example": "客厅"
          },
          "firmware_ver": {
            "type": "string",
            "example": "1.0.0"
          },
          "led_on": {
            "type": "boolean"
          },
          "fan_on": {
            "type": "boolean"
          },
          "mist_on": {
            "type": "boolean"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "TelemetryColumn": {
        "type": "object",
        "description": "遥测数据列定义（从物模板动态解析）",
        "properties": {
          "property_key": {
            "type": "string",
            "example": "temperature"
          },
          "display_name": {
            "type": "string",
            "example": "环境温度"
          },
          "type": {
            "type": "string",
            "enum": [
              "float",
              "int"
            ],
            "example": "float"
          },
          "unit": {
            "type": "string",
            "example": "°C"
          }
        }
      },
      "TelemetryResponse": {
        "type": "object",
        "properties": {
          "device_name": {
            "type": "string",
            "example": "00000001"
          },
          "columns": {
            "type": "array",
            "description": "动态列定义（从产品物模板解析）",
            "items": {
              "$ref": "#/components/schemas/TelemetryColumn"
            }
          },
          "records": {
            "type": "array",
            "description": "数据行（每行含时间戳 + 各属性值）",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "integer"
                },
                "created_at": {
                  "type": "string",
                  "example": "2026-05-27 10:30:00"
                },
                "temperature": {
                  "type": "number"
                },
                "humidity": {
                  "type": "number"
                },
                "light": {
                  "type": "integer"
                },
                "soil_moisture": {
                  "type": "number"
                }
              }
            }
          },
          "total": {
            "type": "integer",
            "example": 20
          }
        }
      },
      "DayStats": {
        "type": "object",
        "description": "按天聚合的传感器统计数据",
        "properties": {
          "date": {
            "type": "string",
            "format": "date",
            "example": "2026-05-27"
          },
          "temp_avg": {
            "type": "number",
            "description": "日均温度（°C）",
            "example": 24.3
          },
          "temp_high": {
            "type": "number",
            "description": "日最高温度（°C）",
            "example": 28.1
          },
          "temp_low": {
            "type": "number",
            "description": "日最低温度（°C）",
            "example": 21.5
          },
          "humidity_avg": {
            "type": "number",
            "description": "日均湿度（%）",
            "example": 65.2
          },
          "light_avg": {
            "type": "number",
            "description": "日均光照（lux）",
            "example": 3200
          },
          "soil_avg": {
            "type": "number",
            "description": "日均土壤湿度（%）",
            "example": 43.7
          }
        }
      }
    }
  }
}