{
  "openapi": "3.1.0",
  "info": {
    "title": "800BUS Open API",
    "version": "1.0.0",
    "summary": "800BUS 公交图片档案站只读开放接口",
    "description": "800BUS 对外提供的只读数据接口，覆盖档案、资讯、地区、公司、用户公开资料、评论、排行榜与地图标记。\n\n使用约定：\n- 全部接口为 GET，匿名调用，无需 API Key；默认 60 次/分钟/IP。\n- 仅返回公开数据，不包含邮箱、拍摄地点、审核人、IP 与后台字段。\n- 图片使用须遵守 CC BY-NC-ND 4.0：非商业用途、署名 800BUS、禁止演绎；禁止用于模型训练或批量镜像。\n- 请优先使用 thumbnail 字段，避免直接拉取原图造成带宽浪费。\n\n适合 AI 调用的原因：参数简单、响应稳定、无副作用（不会写入数据、不发通知）。",
    "contact": {
      "name": "800BUS",
      "url": "https://800bus.cn/yszc/openapi.html"
    },
    "license": {
      "name": "CC BY-NC-ND 4.0",
      "url": "https://creativecommons.org/licenses/by-nc-nd/4.0/deed.zh"
    }
  },
  "servers": [
    {
      "url": "https://800bus.cn",
      "description": "生产环境"
    }
  ],
  "tags": [
    { "name": "概览", "description": "站点统计与参数字典" },
    { "name": "档案", "description": "图片档案检索、详情与评论" },
    { "name": "资讯", "description": "资讯列表与详情" },
    { "name": "地区与公司", "description": "省 / 市 / 公司层级" },
    { "name": "用户", "description": "用户公开资料" },
    { "name": "榜单与地图", "description": "排行榜与地图标记" }
  ],
  "paths": {
    "/api/v1/stats": {
      "get": {
        "tags": ["概览"],
        "operationId": "getSiteStats",
        "summary": "获取站点统计",
        "description": "返回档案、用户、公司、资讯、评论总数，以及今日新增和分区分布。",
        "responses": {
          "200": {
            "description": "站点统计",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/StatsResponse" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/meta": {
      "get": {
        "tags": ["概览"],
        "operationId": "getMeta",
        "summary": "获取参数字典",
        "description": "返回分区、站点、排序方式与分页上限，供 AI 构造合法参数。",
        "responses": {
          "200": {
            "description": "参数字典",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/MetaResponse" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/photos": {
      "get": {
        "tags": ["档案"],
        "operationId": "listPhotos",
        "summary": "搜索 / 列出档案",
        "description": "按关键词、车牌、分区、地区、公司、作者筛选，支持分页与多种排序。",
        "parameters": [
          { "name": "q", "in": "query", "description": "关键词，匹配品牌、车牌、地区、描述、公司名、作者名", "schema": { "type": "string", "maxLength": 60 } },
          { "name": "plate", "in": "query", "description": "车牌片段，例如 B12345", "schema": { "type": "string", "maxLength": 20 } },
          { "name": "section", "in": "query", "description": "分区 key，取值见 /api/v1/meta", "schema": { "type": "string" } },
          { "name": "region", "in": "query", "description": "地区文本片段", "schema": { "type": "string", "maxLength": 40 } },
          { "name": "company_id", "in": "query", "schema": { "type": "integer", "minimum": 1 } },
          { "name": "author_id", "in": "query", "schema": { "type": "integer", "minimum": 1 } },
          { "name": "site", "in": "query", "description": "站点筛选", "schema": { "type": "string", "enum": ["all", "main", "draw"], "default": "all" } },
          { "name": "sort", "in": "query", "schema": { "type": "string", "enum": ["latest", "oldest", "hot", "rating", "featured"], "default": "latest" } },
          { "name": "featured", "in": "query", "description": "仅返回精选", "schema": { "type": "boolean" } },
          { "name": "page", "in": "query", "schema": { "type": "integer", "minimum": 1, "default": 1 } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 50, "default": 20 } }
        ],
        "responses": {
          "200": {
            "description": "档案列表",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "type": "array", "items": { "$ref": "#/components/schemas/PhotoSummary" } },
                        "meta": { "$ref": "#/components/schemas/Pagination" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/photos/{id}": {
      "get": {
        "tags": ["档案"],
        "operationId": "getPhoto",
        "summary": "获取档案详情",
        "description": "返回单个已审核档案的完整公开信息，含作者头像与简介，不含拍摄地点。",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }
        ],
        "responses": {
          "200": {
            "description": "档案详情",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/PhotoDetail" } } }
                  ]
                }
              }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/photos/{id}/comments": {
      "get": {
        "tags": ["档案"],
        "operationId": "listPhotoComments",
        "summary": "获取档案评论",
        "description": "返回已通过审核的评论及嵌套回复。匿名评论统一显示为“匿名用户”，不暴露真实账号。",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } },
          { "name": "page", "in": "query", "schema": { "type": "integer", "minimum": 1, "default": 1 } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 50, "default": 20 } }
        ],
        "responses": {
          "200": {
            "description": "评论列表",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "type": "array", "items": { "$ref": "#/components/schemas/Comment" } },
                        "meta": { "$ref": "#/components/schemas/Pagination" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/news": {
      "get": {
        "tags": ["资讯"],
        "operationId": "listNews",
        "summary": "列出资讯",
        "description": "返回已发布资讯，按发布时间倒序。列表仅含摘要，正文请调用详情接口。",
        "parameters": [
          { "name": "category", "in": "query", "schema": { "type": "string" } },
          { "name": "province_id", "in": "query", "schema": { "type": "integer" } },
          { "name": "city_id", "in": "query", "schema": { "type": "integer" } },
          { "name": "page", "in": "query", "schema": { "type": "integer", "minimum": 1, "default": 1 } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 50, "default": 12 } }
        ],
        "responses": {
          "200": {
            "description": "资讯列表",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "type": "array", "items": { "$ref": "#/components/schemas/NewsSummary" } },
                        "meta": { "$ref": "#/components/schemas/Pagination" }
                      }
                    }
                  ]
                }
              }
            }
          },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/news/{id}": {
      "get": {
        "tags": ["资讯"],
        "operationId": "getNews",
        "summary": "获取资讯详情",
        "description": "返回资讯正文（Markdown 原文）与图片列表。",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }
        ],
        "responses": {
          "200": {
            "description": "资讯详情",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/NewsDetail" } } }
                  ]
                }
              }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/regions": {
      "get": {
        "tags": ["地区与公司"],
        "operationId": "listRegions",
        "summary": "获取地区 / 公司层级",
        "description": "level=province 返回省份；level=city 需 parent_id=省份 ID；level=company 需 parent_id 或 city_id=城市 ID。",
        "parameters": [
          { "name": "level", "in": "query", "schema": { "type": "string", "enum": ["province", "city", "company"], "default": "province" } },
          { "name": "parent_id", "in": "query", "description": "父级 ID：省份 ID 或城市 ID", "schema": { "type": "integer" } },
          { "name": "city_id", "in": "query", "description": "level=company 时可用", "schema": { "type": "integer" } }
        ],
        "responses": {
          "200": {
            "description": "层级列表",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    { "type": "object", "properties": { "data": { "type": "array", "items": { "$ref": "#/components/schemas/Region" } } } }
                  ]
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/companies/{id}": {
      "get": {
        "tags": ["地区与公司"],
        "operationId": "getCompany",
        "summary": "获取公司资料与作品",
        "description": "返回公司名称、所在城市与省份，以及该公司最新作品的分页列表。",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } },
          { "name": "page", "in": "query", "schema": { "type": "integer", "minimum": 1, "default": 1 } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 50, "default": 12 } }
        ],
        "responses": {
          "200": {
            "description": "公司详情",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/CompanyDetail" } } }
                  ]
                }
              }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/users/{id}": {
      "get": {
        "tags": ["用户"],
        "operationId": "getUserPublicProfile",
        "summary": "获取用户公开资料",
        "description": "返回昵称、头像、简介、作品数与平均分、关注与粉丝数。不含邮箱等隐私信息。",
        "parameters": [
          { "name": "id", "in": "path", "required": true, "schema": { "type": "integer" } }
        ],
        "responses": {
          "200": {
            "description": "用户公开资料",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    { "type": "object", "properties": { "data": { "$ref": "#/components/schemas/UserPublic" } } }
                  ]
                }
              }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/rankings": {
      "get": {
        "tags": ["榜单与地图"],
        "operationId": "getRankings",
        "summary": "获取排行榜",
        "description": "type=weekly 返回近 7 天最热档案；type=monthly 返回近 30 天贡献者。",
        "parameters": [
          { "name": "type", "in": "query", "schema": { "type": "string", "enum": ["weekly", "monthly"], "default": "weekly" } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 20, "default": 10 } }
        ],
        "responses": {
          "200": {
            "description": "排行榜",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": {
                            "oneOf": [
                              { "$ref": "#/components/schemas/RankingPhoto" },
                              { "$ref": "#/components/schemas/RankingUser" }
                            ]
                          }
                        },
                        "meta": {
                          "type": "object",
                          "properties": {
                            "type": { "type": "string" },
                            "window_days": { "type": "integer" }
                          }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/api/v1/map/markers": {
      "get": {
        "tags": ["榜单与地图"],
        "operationId": "listMapMarkers",
        "summary": "获取地图标记",
        "description": "返回矩形范围内允许公开位置信息的档案标记。坐标保留 4 位小数，不返回详细地址。",
        "parameters": [
          { "name": "sw_lat", "in": "query", "description": "西南角纬度", "schema": { "type": "number" } },
          { "name": "sw_lng", "in": "query", "description": "西南角经度", "schema": { "type": "number" } },
          { "name": "ne_lat", "in": "query", "description": "东北角纬度", "schema": { "type": "number" } },
          { "name": "ne_lng", "in": "query", "description": "东北角经度", "schema": { "type": "number" } },
          { "name": "bbox", "in": "query", "description": "minLng,minLat,maxLng,maxLat，可替代上面四个参数", "schema": { "type": "string" } },
          { "name": "section", "in": "query", "schema": { "type": "string" } },
          { "name": "limit", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 500, "default": 500 } }
        ],
        "responses": {
          "200": {
            "description": "地图标记",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/Envelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": { "type": "array", "items": { "$ref": "#/components/schemas/MapMarker" } }
                      }
                    }
                  ]
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    }
  },
  "components": {
    "responses": {
      "BadRequest": {
        "description": "参数错误",
        "content": {
          "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }
        }
      },
      "NotFound": {
        "description": "资源不存在",
        "content": {
          "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }
        }
      },
      "RateLimited": {
        "description": "请求超过 60 次/分钟",
        "headers": {
          "Retry-After": { "schema": { "type": "integer" }, "description": "建议等待秒数" },
          "X-RateLimit-Limit": { "schema": { "type": "integer" } },
          "X-RateLimit-Remaining": { "schema": { "type": "integer" } },
          "X-RateLimit-Reset": { "schema": { "type": "integer" }, "description": "窗口重置时间（Unix 秒）" }
        },
        "content": {
          "application/json": { "schema": { "$ref": "#/components/schemas/Error" } }
        }
      }
    },
    "schemas": {
      "Envelope": {
        "type": "object",
        "required": ["success"],
        "properties": {
          "success": { "type": "boolean" },
          "data": {},
          "meta": { "type": "object" }
        }
      },
      "Error": {
        "type": "object",
        "required": ["success", "error"],
        "properties": {
          "success": { "type": "boolean", "const": false },
          "error": {
            "type": "object",
            "required": ["code", "message"],
            "properties": {
              "code": { "type": "string", "enum": ["bad_request", "not_found", "rate_limited", "method_not_allowed", "server_error"] },
              "message": { "type": "string" }
            }
          }
        }
      },
      "Pagination": {
        "type": "object",
        "properties": {
          "page": { "type": "integer" },
          "limit": { "type": "integer" },
          "total": { "type": "integer" },
          "total_pages": { "type": "integer" }
        }
      },
      "PhotoSummary": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "url": { "type": "string", "example": "/photo/1234" },
          "brand": { "type": "string" },
          "license_plate": { "type": "string" },
          "section": {
            "type": "object",
            "properties": { "key": { "type": "string" }, "name": { "type": "string" } }
          },
          "site": { "type": "string", "enum": ["main", "draw"] },
          "region": { "type": "string" },
          "company": {
            "type": ["object", "null"],
            "properties": { "id": { "type": "integer" }, "name": { "type": "string" } }
          },
          "city": { "type": "string" },
          "province": { "type": "string" },
          "description": { "type": "string" },
          "featured": { "type": "boolean" },
          "rating": {
            "type": "object",
            "properties": {
              "average": { "type": "number" },
              "count": { "type": "integer" }
            }
          },
          "author": {
            "type": ["object", "null"],
            "properties": {
              "id": { "type": "integer" },
              "username": { "type": "string" },
              "url": { "type": "string" }
            }
          },
          "created_at": { "type": ["string", "null"], "format": "date-time" },
          "photo": {
            "type": "object",
            "properties": {
              "original": { "type": "string" },
              "thumbnail": { "type": "string" },
              "blur": { "type": "string" }
            }
          }
        }
      },
      "PhotoDetail": {
        "allOf": [
          { "$ref": "#/components/schemas/PhotoSummary" },
          {
            "type": "object",
            "properties": {
              "comments_count": { "type": "integer" },
              "author": {
                "type": ["object", "null"],
                "properties": {
                  "id": { "type": "integer" },
                  "username": { "type": "string" },
                  "url": { "type": "string" },
                  "avatar": { "type": "string" },
                  "bio": { "type": "string" },
                  "joined_at": { "type": ["string", "null"], "format": "date-time" }
                }
              }
            }
          }
        ]
      },
      "Comment": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "parent_id": { "type": "integer" },
          "username": { "type": "string", "description": "匿名评论固定为“匿名用户”" },
          "is_anonymous": { "type": "boolean" },
          "content": { "type": "string" },
          "reply_to": { "type": "string" },
          "created_at": { "type": ["string", "null"], "format": "date-time" },
          "replies": { "type": "array", "items": { "$ref": "#/components/schemas/Comment" } }
        }
      },
      "NewsSummary": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "url": { "type": "string" },
          "title": { "type": "string" },
          "category": { "type": "string" },
          "author": {
            "type": ["object", "null"],
            "properties": {
              "id": { "type": "integer" },
              "username": { "type": "string" },
              "url": { "type": "string" }
            }
          },
          "is_anonymous": { "type": "boolean" },
          "province": { "type": "string" },
          "city": { "type": "string" },
          "images": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": { "original": { "type": "string" }, "thumbnail": { "type": "string" } }
            }
          },
          "cover": { "type": "string" },
          "view_count": { "type": "integer" },
          "excerpt": { "type": "string" },
          "created_at": { "type": ["string", "null"], "format": "date-time" }
        }
      },
      "NewsDetail": {
        "allOf": [
          { "$ref": "#/components/schemas/NewsSummary" },
          { "type": "object", "properties": { "content": { "type": "string", "description": "Markdown 原文" } } }
        ]
      },
      "Region": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "name": { "type": "string" },
          "parent_id": { "type": "integer" },
          "level": { "type": "string", "enum": ["province", "city", "company"] },
          "cities_count": { "type": "integer" },
          "companies_count": { "type": "integer" },
          "photos_count": { "type": "integer" }
        }
      },
      "CompanyDetail": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "name": { "type": "string" },
          "city": { "type": "string" },
          "province": { "type": "string" },
          "photos_count": { "type": "integer" },
          "photos": { "type": "array", "items": { "$ref": "#/components/schemas/PhotoSummary" } }
        }
      },
      "UserPublic": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "username": { "type": "string" },
          "url": { "type": "string" },
          "avatar": { "type": "string" },
          "bio": { "type": "string" },
          "joined_at": { "type": ["string", "null"], "format": "date-time" },
          "stats": {
            "type": "object",
            "properties": {
              "photos": { "type": "integer" },
              "average_rating": { "type": "number" }
            }
          },
          "follows": {
            "type": "object",
            "properties": {
              "following": { "type": "integer" },
              "followers": { "type": "integer" }
            }
          }
        }
      },
      "RankingPhoto": {
        "allOf": [
          { "$ref": "#/components/schemas/PhotoSummary" },
          {
            "type": "object",
            "properties": {
              "rank": { "type": "integer" },
              "score": { "type": "number" }
            }
          }
        ]
      },
      "RankingUser": {
        "type": "object",
        "properties": {
          "rank": { "type": "integer" },
          "user": { "$ref": "#/components/schemas/UserPublic" },
          "photos": { "type": "integer" }
        }
      },
      "MapMarker": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "url": { "type": "string" },
          "brand": { "type": "string" },
          "license_plate": { "type": "string" },
          "region": { "type": "string" },
          "section": {
            "type": "object",
            "properties": { "key": { "type": "string" }, "name": { "type": "string" } }
          },
          "lat": { "type": "number" },
          "lng": { "type": "number" },
          "thumbnail": { "type": "string" }
        }
      },
      "StatsResponse": {
        "allOf": [
          { "$ref": "#/components/schemas/Envelope" },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "object",
                "properties": {
                  "photos": { "type": "integer" },
                  "users": { "type": "integer" },
                  "companies": { "type": "integer" },
                  "news": { "type": "integer" },
                  "comments": { "type": "integer" },
                  "today_uploads": { "type": "integer" },
                  "sections": {
                    "type": "array",
                    "items": {
                      "type": "object",
                      "properties": {
                        "key": { "type": "string" },
                        "name": { "type": "string" },
                        "count": { "type": "integer" }
                      }
                    }
                  },
                  "updated_at": { "type": "string", "format": "date-time" }
                }
              }
            }
          }
        ]
      },
      "MetaResponse": {
        "allOf": [
          { "$ref": "#/components/schemas/Envelope" },
          {
            "type": "object",
            "properties": {
              "data": {
                "type": "object",
                "properties": {
                  "sections": { "type": "array", "items": { "$ref": "#/components/schemas/KeyName" } },
                  "draw_sections": { "type": "array", "items": { "$ref": "#/components/schemas/KeyName" } },
                  "sites": { "type": "array", "items": { "$ref": "#/components/schemas/KeyName" } },
                  "sorts": { "type": "array", "items": { "$ref": "#/components/schemas/KeyName" } },
                  "limits": { "type": "object" }
                }
              }
            }
          }
        ]
      },
      "KeyName": {
        "type": "object",
        "properties": {
          "key": { "type": "string" },
          "name": { "type": "string" }
        }
      }
    }
  }
}
