{
  "openapi": "3.0.3",
  "info": {
    "title": "个人 API 服务",
    "version": "1.0.0",
    "description": "上传月度销售 Excel（xlsx）→ 返回 JSON 统计结果的 HTTP API。全程 HTTPS、无状态。除 /api/health 外均需 HMAC-SHA256 签名认证。\n\n认证请求头：x-api-key（客户端标识）、x-timestamp（秒级时间戳，服务端校验 ±5 分钟窗口）、x-signature（签名）。\n签名算法：bodyHash = sha256Hex(body)；signature = HMAC-SHA256(secret, `${timestamp}:${bodyHash}`)，十六进制小写。\n可选加密：请求头 x-encrypted=1，body 为 AES-256-GCM 密文 base64(iv).base64(tag).base64(data)，密钥 = sha256(secret + \":\" + apiKey)。\n\n统一响应信封：{ code, message, data }，成功 code=0；失败 code 与 HTTP 状态码一致、data=null。"
  },
  "servers": [
    {
      "url": "https://apis.gankun.cn.lu",
      "description": "生产环境"
    }
  ],
  "paths": {
    "/api/analyze": {
      "post": {
        "summary": "月度销售统计",
        "description": "上传 xlsx 文件（base64），返回按月份拆分的销售统计（MonthlySalesStats）。需要签名认证。",
        "operationId": "analyze",
        "security": [{ "apiKeyAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/AnalyzeRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ApiResponse_MonthlySalesStats" }
              }
            }
          },
          "400": {
            "description": "请求体不是合法 JSON / 缺少 file 字段 / xlsx 解析失败",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          },
          "401": {
            "description": "缺少认证头 / 时间戳超出 5 分钟窗口 / 签名验证失败 / 请求体解密失败",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          },
          "405": {
            "description": "仅支持 POST 方法",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          },
          "413": {
            "description": "请求体超过平台 4.5MB 上限（FUNCTION_PAYLOAD_TOO_LARGE）",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ErrorResponse" }
              }
            }
          }
        }
      }
    },
    "/api/health": {
      "get": {
        "summary": "健康检查",
        "description": "无需认证，返回服务状态与已注册接口列表。",
        "operationId": "health",
        "responses": {
          "200": {
            "description": "成功",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/ApiResponse_Health" }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key",
        "description": "客户端标识（可公开）。除签名头 x-signature 与时间戳头 x-timestamp 外，仍需携带：x-timestamp（秒级，±5 分钟窗口）、x-signature = HMAC-SHA256(secret, `${timestamp}:${sha256Hex(body)}`)。返回 401 时附带失败原因。"
      }
    },
    "schemas": {
      "ApiResponse_MonthlySalesStats": {
        "type": "object",
        "required": ["code", "message", "data"],
        "properties": {
          "code": { "type": "integer", "description": "0 表示成功" },
          "message": { "type": "string" },
          "data": { "$ref": "#/components/schemas/MonthlySalesStats" }
        }
      },
      "ApiResponse_Health": {
        "type": "object",
        "required": ["code", "message", "data"],
        "properties": {
          "code": { "type": "integer" },
          "message": { "type": "string" },
          "data": {
            "type": "object",
            "required": ["status", "service", "time", "endpoints"],
            "properties": {
              "status": { "type": "string", "enum": ["up"] },
              "service": { "type": "string" },
              "time": { "type": "string", "format": "date-time" },
              "endpoints": {
                "type": "array",
                "items": {
                  "type": "object",
                  "required": ["method", "path", "desc"],
                  "properties": {
                    "method": { "type": "string" },
                    "path": { "type": "string" },
                    "desc": { "type": "string" }
                  }
                }
              }
            }
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": ["code", "message", "data"],
        "properties": {
          "code": { "type": "integer", "description": "与 HTTP 状态码一致" },
          "message": { "type": "string" },
          "data": { "type": "null" }
        }
      },
      "AnalyzeRequest": {
        "type": "object",
        "required": ["file"],
        "properties": {
          "file": {
            "type": "string",
            "description": "xlsx 文件的 base64 内容（原始二进制，非 Data URL）"
          },
          "filename": {
            "type": "string",
            "description": "原始文件名，仅用于日志/溯源"
          }
        }
      },
      "MonthlySalesStats": {
        "type": "object",
        "required": ["months", "byMonth", "total"],
        "properties": {
          "months": {
            "type": "array",
            "items": { "type": "string", "pattern": "^\\d{4}-\\d{2}$" },
            "description": "月份列表，如 [\"2026-07\"]，升序"
          },
          "byMonth": {
            "type": "object",
            "additionalProperties": { "$ref": "#/components/schemas/SalesStats" },
            "description": "各月统计，key 为 YYYY-MM"
          },
          "total": {
            "$ref": "#/components/schemas/SalesStats",
            "description": "全部数据跨月合并统计（meta.month 为 0）"
          }
        }
      },
      "SalesStats": {
        "type": "object",
        "required": ["meta", "byHospital", "byLevel", "byDistributor", "byAgent", "byManager", "byProvince", "newHospitals"],
        "properties": {
          "meta": { "$ref": "#/components/schemas/Meta" },
          "byHospital": { "type": "array", "items": { "$ref": "#/components/schemas/GroupStat" }, "description": "各医院月销量，按折算万盒降序" },
          "byLevel": { "type": "array", "items": { "$ref": "#/components/schemas/GroupStat" }, "description": "各级别医院月销量，按折算万盒降序" },
          "byDistributor": { "type": "array", "items": { "$ref": "#/components/schemas/GroupStat" }, "description": "配送商配送量，按折算万盒降序" },
          "byAgent": { "type": "array", "items": { "$ref": "#/components/schemas/GroupStat" }, "description": "代理商月销量，按折算万盒降序" },
          "byManager": { "type": "array", "items": { "$ref": "#/components/schemas/GroupStat" }, "description": "招商经理月销量，按折算万盒降序" },
          "byProvince": { "type": "array", "items": { "$ref": "#/components/schemas/GroupStat" }, "description": "各省份月销量，按折算万盒降序" },
          "newHospitals": { "type": "array", "items": { "$ref": "#/components/schemas/NewHospital" }, "description": "新增医院明细，按折算万盒降序" }
        }
      },
      "Meta": {
        "type": "object",
        "required": ["year", "month", "rows", "hospitals", "totalQty", "totalConv", "newHospitals"],
        "properties": {
          "year": { "type": "integer", "description": "年" },
          "month": { "type": "integer", "description": "月（跨月汇总时为 0）" },
          "rows": { "type": "integer", "description": "数据行数" },
          "hospitals": { "type": "integer", "description": "涉及医院数（去重）" },
          "totalQty": { "type": "number", "description": "总出库数量（盒）" },
          "totalConv": { "type": "number", "description": "总折算2贴数量（万盒）" },
          "newHospitals": { "type": "integer", "description": "新增医院数（医院去重=1）" }
        }
      },
      "GroupStat": {
        "type": "object",
        "required": ["name", "qty", "conv", "records"],
        "properties": {
          "name": { "type": "string", "description": "分组名称（空值显示为 \"(空)\"）" },
          "qty": { "type": "number", "description": "出库数量（盒）" },
          "conv": { "type": "number", "description": "折算2贴数量（万盒）" },
          "records": { "type": "integer", "description": "记录数" }
        }
      },
      "NewHospital": {
        "type": "object",
        "required": ["hospital", "level", "province", "qty", "conv"],
        "properties": {
          "hospital": { "type": "string", "description": "标准医院名称" },
          "level": { "type": "string", "description": "标准级别" },
          "province": { "type": "string", "description": "省份" },
          "qty": { "type": "number", "description": "出库数量（盒）" },
          "conv": { "type": "number", "description": "折算2贴数量（万盒）" }
        }
      }
    }
  }
}