SaaS 3.0 · 0.5 接口使用说明

接口版本 0.5.007-service.1 · 更新于 2026-09-16。当前运行的是本机 155 店真实数据测试平台;云网址尚未部署。接口消费者无需 MySQL 账号,也不需要编写 SQL。

1. 在什么地方执行

在这台 Mac 上,按 Command + 空格,输入“终端”,打开 Terminal;也可以用 Codex 底部的终端。下方命令粘贴到终端执行,每个代码框可整体复制。不是粘贴到浏览器地址栏、MySQL 控制台或飞书。Python 3 是客户端唯一运行依赖,客户端不安装数据库驱动。

浏览器查看平台:http://127.0.0.1:8786/;查看本说明:/api-docs;机器读取契约:/openapi.json

export SAAS_APP_DIR='/Users/ethan/SaaS 3.0/0.5/数据库'
export SAAS_TOKEN_FILE="$SAAS_APP_DIR/.local/credentials/local-reader.json"
python3 --version
curl --fail --silent --show-error http://127.0.0.1:8786/health/ready
python3 "$SAAS_APP_DIR/client_example.py" --token-file "$SAAS_TOKEN_FILE" --resource dataset
python3 "$SAAS_APP_DIR/client_example.py" --token-file "$SAAS_TOKEN_FILE" --resource stores

health/ready 返回 ready 状态;dataset 确认数据日期范围与数据集标签;stores 返回有权限的门店列表,每家包含 idscope_id。下面以当前库的门店 ID 6 为操作例子;调用自己的门店时,先从列表选择 ID。客户端会自动查找该门店的 scope_id,不要把老库 org_id 当新库门店 ID。

此电脑已有上述只读凭证文件,当前只允许门店 ID 1~6,所以看到 6 家是权限范围,并不代表平台只有 6 家;管理员凭证可见当前全部 155 家。其他人需要管理员单独签发凭证,并改成自己电脑的文件路径。凭证不是数据库密码。客户端只读取文件里的 access_key,不把它放在命令、URL 或输出中。若文件不存在、到期或撤销,向管理员获取新文件。

若 readiness 连接失败,在当前开发电脑可运行 python3 "$SAAS_APP_DIR/local_database.py" start,随后 python3 "$SAAS_APP_DIR/start_local_services.py"。这要求电脑上原有 MySQL、依赖和真实数据库仍在。新电脑按 handoff 恢复真实备份,单独复制客户端不能启动数据库服务。

2. 各类数据怎么读取

先执行上一节 export 命令。查询默认采用服务当前数据集起止日期:本次是 2026-06-01 至 2026-09-10;可显式指定 start/end,均包含当日。以下命令只读,不导入或修改业务数据。

python3 "$SAAS_APP_DIR/client_example.py" --token-file "$SAAS_TOKEN_FILE" --resource catalog
python3 "$SAAS_APP_DIR/client_example.py" --token-file "$SAAS_TOKEN_FILE" --resource reference --store 6
python3 "$SAAS_APP_DIR/client_example.py" --token-file "$SAAS_TOKEN_FILE" --resource energy --store 6 --start 2026-06-01 --end 2026-09-10
python3 "$SAAS_APP_DIR/client_example.py" --token-file "$SAAS_TOKEN_FILE" --resource targets --store 6
python3 "$SAAS_APP_DIR/client_example.py" --token-file "$SAAS_TOKEN_FILE" --resource metrics --store 6
python3 "$SAAS_APP_DIR/client_example.py" --token-file "$SAAS_TOKEN_FILE" --resource periods --store 6 --as-of 2026-09-10
resource读取内容用途
dataset / stores / catalog数据范围 / 门店及范围 ID / 门店列表概况先选择正确数据集和门店
reference档案、设备、策略快照、来源目标候选核对设备名称与业务配置
energy每日生效空调能耗、来源、修订状态画趋势、导出日表
targets每日 TEV、TES-180、TES-210 及有效期间核对目标是否正式生效
metrics每日 EBA、AES、AER、两个 TES 阈值与判定节能分析,必须同时读取缺失原因
periods截至 as_of 的日、近 7 日、近 30 日、当年统计平台周期卡片;核对覆盖天数

保存 JSON 文件到桌面(文件名已存在会拒绝覆盖,请更换文件名):

python3 "$SAAS_APP_DIR/client_example.py" --token-file "$SAAS_TOKEN_FILE" --resource energy --store 6 --output "$HOME/Desktop/SaaS-3.0-store-6-energy.json"

原始来源、导入明细和修订历史需 raw 权限;普通 reader 返回 403 是正常权限控制。管理员授权后,将 SAAS_RAW_TOKEN_FILE 设置为其安全交付的凭证文件路径。把三个 ID 替换为上一步真实响应中的 ID;它们分别属于能耗、导入行和批次,不能互换。

python3 "$SAAS_APP_DIR/client_example.py" --token-file "$SAAS_RAW_TOKEN_FILE" --resource history --record-id "$SAAS_ENERGY_ID"
python3 "$SAAS_APP_DIR/client_example.py" --token-file "$SAAS_RAW_TOKEN_FILE" --resource lineage --record-id "$SAAS_IMPORT_ROW_ID"
python3 "$SAAS_APP_DIR/client_example.py" --token-file "$SAAS_RAW_TOKEN_FILE" --resource import --record-id "$SAAS_BATCH_ID"

这些带变量的追溯命令是参数模板,先赋值才能运行。history 中的 revision 和 import_row_id 可接着用于 lineage;导入批次详情返回行明细。raw 返回原始内容,应只提供给确有需要的人员。

3. 返回值怎么理解

4. 导入与档案修改

普通数据消费者到读取步骤即可。写入方需要 write 权限及数据库成员角色允许,原始追溯另需 raw。当前客户端只提供读取;操作人员使用平台“手动导入清洗数据”或依据以下完整契约开发专用调用。

  1. CSV 使用 UTF-8,字段以平台导入模板为准;仅支持已实现的固定格式,不接受任意 Excel 文件。单文件上限 4 MiB、最多 10,000 行。
  2. POST /api/v1/imports/preview,Content-Type: application/json,Authorization: Bearer 凭证,Idempotency-Key 为本次预览的唯一字符串(最多 64 字符)。JSON 请求为 filename、content_base64、reason。content_base64 是完整 CSV 文件字节的 Base64。预览会保存来源和导入草稿,但不改变生效能耗。
  3. 检查预览响应中批次、每行结果、错误与变化;有错误先修正文件,不自动提交。
  4. POST /api/v1/imports/{batch_id}/commit,JSON 为 {},使用该批次 ID 和提交操作自己的 Idempotency-Key。提交会修改真实数据;同一操作重试复用原键及原请求,不换键制造重复业务。
  5. 修改档案先 GET reference 读取当前版本,再 PATCH profile,按 ProfileUpdate 传入版本与允许字段。遇到 409 重新读取、核对,不能盲目覆盖其他人的修改。

下方“完整业务契约”展开每个 POST/PATCH 可见请求体字段、必填项、响应与错误。这里没有自动执行写操作的按钮;在正式业务库提交前,应确认实际文件与变更内容。

5. 给其他人员或 AI 使用

项目负责人从私有仓库 Spectrum136/saas-3.0 提供 application/client_example.py、接口文档和单独签发的只读凭证。使用者在自己电脑的终端执行相同命令,改成自己的脚本/凭证路径。Python 客户端仅使用标准库。API 路径与字段同时由 OpenAPI JSON 提供,AI 工具可以按契约构造请求;访问凭证只放工具的私有配置中。

在 Postman 中 Import 当前 http://127.0.0.1:8786/openapi.json(仅服务所在电脑可用),选择 Bearer Token 认证;将真实凭证保存在私有环境配置,不保存到共享集合。先调用 stores,填写其返回的 store_id 与 scope_id,再指定日期调用 energy。服务实际根地址是 8786,业务路径包含 /api/v1。

127.0.0.1 仅代表使用者自己的电脑。目前不能把这个地址发给同事当作公网地址。云部署完成后,管理员提供实际 HTTPS 根地址,客户端添加 --base-url 'https://实际部署域名'。这是未来参数模板,不是已开放的网站。客户端拒绝远程明文 HTTP 和重定向以保护凭证。

6. 权限与失败处理

HTTP含义与处理
400核对日期、参数、scope_id、文件格式和请求体。
401访问凭证无效、到期或撤销,联系管理员更换。
403门店、操作或 raw 权限不足;不改用 SQL 绕过。
404核对路径和对象 ID;记录可能不存在。
409并发版本或幂等冲突;重新读取并核对。相同操作重试保留键,变更请求需新操作。
500服务异常,保留响应头 X-Request-ID 交给维护者,不连续提交写请求。
503数据库或服务暂不可用,读取可退避重试;写操作先确认结果。

浏览器使用凭证换取 HttpOnly 会话;已实现成员/门店范围、有效期及撤销。账号密码、自助注册尚未实现,按当前计划后续补齐。

7. 完整业务契约

以下 14 项业务接口及字段定义由当前代码 api_contract() 自动生成,展开可查看参数位置、类型、必填项、请求体和每种响应。$ref 指向下方同名 Schema,additionalProperties=false 表示不接受未知字段。原始完整 JSON 可在服务 /openapi.json 读取。

接口用途
GET /api/v1/dataset当前真实数据集与日期范围
GET /api/v1/catalog权限内门店完整列表与设备快照统计
GET /api/v1/stores/{store_id}/periods截至as_of的单日、近7日、近30日及年内汇总,保留数据覆盖度
GET /api/v1/stores可访问门店及空调计量范围
GET /api/v1/stores/{store_id}/reference门店档案、有效设备和策略定义快照;权限内只读,非实时状态
PATCH /api/v1/stores/{store_id}/profile仅编辑名称和地址;trim必填,版本冲突409,同值不写审计
GET /api/v1/stores/{store_id}/targets逐日查询已生效TES-180/TES-210/TEV;首尾日期包含,不补0或计算达标
GET /api/v1/stores/{store_id}/metrics夏季EBA与双TES节能指标;缺失、输入过期或历史阶段未知时保留空值
GET /api/v1/stores/{store_id}/energy查询日值及覆盖天数;日期参数首尾均包含,最多366天
GET /api/v1/imports/{batch_id}导入记录及预览动作
GET /api/v1/import-rows/{import_row_id}/lineage查看来源封存记录;要求来源读取权限
GET /api/v1/energy/{energy_id}/history不可变修订历史
POST /api/v1/imports/preview预览清洗CSV;扩量数据集空白回查封存老库,0保留,不覆盖已有清洗值
POST /api/v1/imports/{batch_id}/commit事务提交已预览批次;同键重试幂等,过期或版本冲突返回409
GET /api/v1/dataset
{
  "summary": "当前真实数据集与日期范围",
  "parameters": [],
  "responses": {
    "200": {
      "description": "Successful local operation",
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "label": {
                "type": "string"
              },
              "start": {
                "type": "string",
                "format": "date"
              },
              "end": {
                "type": "string",
                "format": "date"
              },
              "source_policy": {
                "type": "string",
                "enum": [
                  "CLEANED_SAMPLE",
                  "CLEANED_THEN_LEGACY_THEN_NULL"
                ]
              },
              "timezone": {
                "type": "string"
              },
              "unit": {
                "type": "string"
              }
            },
            "required": [
              "label",
              "start",
              "end",
              "source_policy",
              "timezone",
              "unit"
            ],
            "additionalProperties": false
          }
        }
      }
    },
    "400": {
      "description": "Invalid request",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "401": {
      "description": "Unauthenticated",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "403": {
      "description": "No access",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "409": {
      "description": "Conflict; do not silently overwrite",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "500": {
      "description": "Operation failed",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "503": {
      "description": "Not ready",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    }
  }
}
GET /api/v1/catalog
{
  "summary": "权限内门店完整列表与设备快照统计",
  "parameters": [],
  "responses": {
    "200": {
      "description": "Successful local operation",
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "stores": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/CatalogStore"
                }
              }
            },
            "required": [
              "stores"
            ],
            "additionalProperties": false
          }
        }
      }
    },
    "400": {
      "description": "Invalid request",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "401": {
      "description": "Unauthenticated",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "403": {
      "description": "No access",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "409": {
      "description": "Conflict; do not silently overwrite",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "500": {
      "description": "Operation failed",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "503": {
      "description": "Not ready",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    }
  }
}
GET /api/v1/stores/{store_id}/periods
{
  "summary": "截至as_of的单日、近7日、近30日及年内汇总,保留数据覆盖度",
  "parameters": [
    {
      "name": "store_id",
      "in": "path",
      "required": true,
      "schema": {
        "type": "integer",
        "minimum": 1
      }
    },
    {
      "name": "scope_id",
      "in": "query",
      "required": true,
      "schema": {
        "type": "integer",
        "minimum": 1
      }
    },
    {
      "name": "as_of",
      "in": "query",
      "required": true,
      "schema": {
        "type": "string",
        "format": "date"
      }
    }
  ],
  "responses": {
    "200": {
      "description": "Successful local operation",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/PeriodOverview"
          }
        }
      }
    },
    "400": {
      "description": "Invalid request",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "401": {
      "description": "Unauthenticated",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "403": {
      "description": "No access",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "409": {
      "description": "Conflict; do not silently overwrite",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "500": {
      "description": "Operation failed",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "503": {
      "description": "Not ready",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    }
  }
}
GET /api/v1/stores
{
  "summary": "可访问门店及空调计量范围",
  "parameters": [],
  "responses": {
    "200": {
      "description": "Successful local operation",
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "stores": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Store"
                }
              }
            },
            "required": [
              "stores"
            ],
            "additionalProperties": false
          }
        }
      }
    },
    "400": {
      "description": "Invalid request",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "401": {
      "description": "Unauthenticated",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "403": {
      "description": "No access",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "409": {
      "description": "Conflict; do not silently overwrite",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "500": {
      "description": "Operation failed",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "503": {
      "description": "Not ready",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    }
  }
}
GET /api/v1/stores/{store_id}/reference
{
  "summary": "门店档案、有效设备和策略定义快照;权限内只读,非实时状态",
  "parameters": [
    {
      "name": "store_id",
      "in": "path",
      "required": true,
      "schema": {
        "type": "integer",
        "minimum": 1
      }
    }
  ],
  "responses": {
    "200": {
      "description": "Successful local operation",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/StoreReference"
          }
        }
      }
    },
    "400": {
      "description": "Invalid request",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "401": {
      "description": "Unauthenticated",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "403": {
      "description": "No access",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "409": {
      "description": "Conflict; do not silently overwrite",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "500": {
      "description": "Operation failed",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "503": {
      "description": "Not ready",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    }
  }
}
PATCH /api/v1/stores/{store_id}/profile
{
  "summary": "仅编辑名称和地址;trim必填,版本冲突409,同值不写审计",
  "parameters": [
    {
      "name": "store_id",
      "in": "path",
      "required": true,
      "schema": {
        "type": "integer",
        "minimum": 1
      }
    }
  ],
  "responses": {
    "200": {
      "description": "Successful local operation",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/ProfileResult"
          }
        }
      }
    },
    "400": {
      "description": "Invalid request",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "401": {
      "description": "Unauthenticated",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "403": {
      "description": "No access",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "409": {
      "description": "Conflict; do not silently overwrite",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "500": {
      "description": "Operation failed",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "503": {
      "description": "Not ready",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    }
  },
  "requestBody": {
    "required": true,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/ProfileUpdate"
        }
      }
    }
  }
}
GET /api/v1/stores/{store_id}/targets
{
  "summary": "逐日查询已生效TES-180/TES-210/TEV;首尾日期包含,不补0或计算达标",
  "parameters": [
    {
      "name": "store_id",
      "in": "path",
      "required": true,
      "schema": {
        "type": "integer",
        "minimum": 1
      }
    },
    {
      "name": "scope_id",
      "in": "query",
      "required": true,
      "schema": {
        "type": "integer",
        "minimum": 1
      }
    },
    {
      "name": "start",
      "in": "query",
      "required": true,
      "schema": {
        "type": "string",
        "format": "date"
      }
    },
    {
      "name": "end",
      "in": "query",
      "required": true,
      "schema": {
        "type": "string",
        "format": "date"
      }
    }
  ],
  "responses": {
    "200": {
      "description": "Successful local operation",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/TargetsResponse"
          }
        }
      }
    },
    "400": {
      "description": "Invalid request",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "401": {
      "description": "Unauthenticated",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "403": {
      "description": "No access",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "409": {
      "description": "Conflict; do not silently overwrite",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "500": {
      "description": "Operation failed",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "503": {
      "description": "Not ready",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    }
  }
}
GET /api/v1/stores/{store_id}/metrics
{
  "summary": "夏季EBA与双TES节能指标;缺失、输入过期或历史阶段未知时保留空值",
  "parameters": [
    {
      "name": "store_id",
      "in": "path",
      "required": true,
      "schema": {
        "type": "integer",
        "minimum": 1
      }
    },
    {
      "name": "scope_id",
      "in": "query",
      "required": true,
      "schema": {
        "type": "integer",
        "minimum": 1
      }
    },
    {
      "name": "start",
      "in": "query",
      "required": true,
      "schema": {
        "type": "string",
        "format": "date"
      }
    },
    {
      "name": "end",
      "in": "query",
      "required": true,
      "schema": {
        "type": "string",
        "format": "date"
      }
    }
  ],
  "responses": {
    "200": {
      "description": "Successful local operation",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/MetricsResponse"
          }
        }
      }
    },
    "400": {
      "description": "Invalid request",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "401": {
      "description": "Unauthenticated",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "403": {
      "description": "No access",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "409": {
      "description": "Conflict; do not silently overwrite",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "500": {
      "description": "Operation failed",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "503": {
      "description": "Not ready",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    }
  }
}
GET /api/v1/stores/{store_id}/energy
{
  "summary": "查询日值及覆盖天数;日期参数首尾均包含,最多366天",
  "parameters": [
    {
      "name": "store_id",
      "in": "path",
      "required": true,
      "schema": {
        "type": "integer",
        "minimum": 1
      }
    },
    {
      "name": "scope_id",
      "in": "query",
      "required": true,
      "schema": {
        "type": "integer",
        "minimum": 1
      }
    },
    {
      "name": "start",
      "in": "query",
      "required": true,
      "schema": {
        "type": "string",
        "format": "date"
      }
    },
    {
      "name": "end",
      "in": "query",
      "required": true,
      "schema": {
        "type": "string",
        "format": "date"
      }
    }
  ],
  "responses": {
    "200": {
      "description": "Successful local operation",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/EnergyResponse"
          }
        }
      }
    },
    "400": {
      "description": "Invalid request",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "401": {
      "description": "Unauthenticated",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "403": {
      "description": "No access",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "409": {
      "description": "Conflict; do not silently overwrite",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "500": {
      "description": "Operation failed",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "503": {
      "description": "Not ready",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    }
  }
}
GET /api/v1/imports/{batch_id}
{
  "summary": "导入记录及预览动作",
  "parameters": [
    {
      "name": "batch_id",
      "in": "path",
      "required": true,
      "schema": {
        "type": "integer",
        "minimum": 1
      }
    }
  ],
  "responses": {
    "200": {
      "description": "Successful local operation",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Import"
          }
        }
      }
    },
    "400": {
      "description": "Invalid request",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "401": {
      "description": "Unauthenticated",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "403": {
      "description": "No access",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "409": {
      "description": "Conflict; do not silently overwrite",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "500": {
      "description": "Operation failed",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "503": {
      "description": "Not ready",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    }
  }
}
GET /api/v1/import-rows/{import_row_id}/lineage
{
  "summary": "查看来源封存记录;要求来源读取权限",
  "parameters": [
    {
      "name": "import_row_id",
      "in": "path",
      "required": true,
      "schema": {
        "type": "integer",
        "minimum": 1
      }
    }
  ],
  "responses": {
    "200": {
      "description": "Successful local operation",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Lineage"
          }
        }
      }
    },
    "400": {
      "description": "Invalid request",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "401": {
      "description": "Unauthenticated",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "403": {
      "description": "No access",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "409": {
      "description": "Conflict; do not silently overwrite",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "500": {
      "description": "Operation failed",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "503": {
      "description": "Not ready",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    }
  }
}
GET /api/v1/energy/{energy_id}/history
{
  "summary": "不可变修订历史",
  "parameters": [
    {
      "name": "energy_id",
      "in": "path",
      "required": true,
      "schema": {
        "type": "integer",
        "minimum": 1
      }
    }
  ],
  "responses": {
    "200": {
      "description": "Successful local operation",
      "content": {
        "application/json": {
          "schema": {
            "type": "object",
            "properties": {
              "revisions": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/Revision"
                }
              }
            },
            "required": [
              "revisions"
            ],
            "additionalProperties": false
          }
        }
      }
    },
    "400": {
      "description": "Invalid request",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "401": {
      "description": "Unauthenticated",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "403": {
      "description": "No access",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "409": {
      "description": "Conflict; do not silently overwrite",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "500": {
      "description": "Operation failed",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "503": {
      "description": "Not ready",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    }
  }
}
POST /api/v1/imports/preview
{
  "summary": "预览清洗CSV;扩量数据集空白回查封存老库,0保留,不覆盖已有清洗值",
  "parameters": [
    {
      "name": "Idempotency-Key",
      "in": "header",
      "required": true,
      "schema": {
        "type": "string",
        "minLength": 1,
        "maxLength": 64
      }
    }
  ],
  "responses": {
    "200": {
      "description": "Successful local operation",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Import"
          }
        }
      }
    },
    "400": {
      "description": "Invalid request",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "401": {
      "description": "Unauthenticated",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "403": {
      "description": "No access",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "409": {
      "description": "Conflict; do not silently overwrite",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "500": {
      "description": "Operation failed",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "503": {
      "description": "Not ready",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    }
  },
  "requestBody": {
    "required": true,
    "content": {
      "application/json": {
        "schema": {
          "$ref": "#/components/schemas/PreviewInput"
        }
      }
    }
  }
}
POST /api/v1/imports/{batch_id}/commit
{
  "summary": "事务提交已预览批次;同键重试幂等,过期或版本冲突返回409",
  "parameters": [
    {
      "name": "batch_id",
      "in": "path",
      "required": true,
      "schema": {
        "type": "integer",
        "minimum": 1
      }
    },
    {
      "name": "Idempotency-Key",
      "in": "header",
      "required": true,
      "schema": {
        "type": "string",
        "minLength": 1,
        "maxLength": 64
      }
    }
  ],
  "responses": {
    "200": {
      "description": "Successful local operation",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Import"
          }
        }
      }
    },
    "400": {
      "description": "Invalid request",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "401": {
      "description": "Unauthenticated",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "403": {
      "description": "No access",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "409": {
      "description": "Conflict; do not silently overwrite",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "500": {
      "description": "Operation failed",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    },
    "503": {
      "description": "Not ready",
      "content": {
        "application/json": {
          "schema": {
            "$ref": "#/components/schemas/Error"
          }
        }
      }
    }
  },
  "requestBody": {
    "required": true,
    "content": {
      "application/json": {
        "schema": {
          "type": "object",
          "properties": {},
          "required": [],
          "additionalProperties": false
        }
      }
    }
  }
}

所有请求与响应字段 Schema

DailyRow
{
  "type": "object",
  "properties": {
    "business_date": {
      "type": "string",
      "format": "date"
    },
    "energy_id": {
      "type": "integer",
      "minimum": 1,
      "nullable": true
    },
    "energy_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "status": {
      "type": "string",
      "enum": [
        "AVAILABLE",
        "NO_DATA",
        "VOID"
      ]
    },
    "source_kind": {
      "type": "string",
      "nullable": true,
      "enum": [
        "CLEANED",
        "LEGACY_DIRECT",
        "MANUAL_UPLOAD",
        null
      ]
    },
    "source_label": {
      "type": "string"
    },
    "quality_status": {
      "type": "string",
      "nullable": true
    },
    "quality_note": {
      "type": "string",
      "nullable": true
    },
    "revision_no": {
      "type": "integer",
      "minimum": 1,
      "nullable": true
    },
    "import_row_id": {
      "type": "integer",
      "minimum": 1,
      "nullable": true
    },
    "source_reference": {
      "type": "object",
      "nullable": true,
      "additionalProperties": true
    }
  },
  "required": [
    "business_date",
    "energy_id",
    "energy_kwh",
    "status",
    "source_kind",
    "source_label",
    "quality_status",
    "quality_note",
    "revision_no",
    "import_row_id",
    "source_reference"
  ],
  "additionalProperties": false
}
Store
{
  "type": "object",
  "properties": {
    "id": {
      "type": "integer",
      "minimum": 1
    },
    "name": {
      "type": "string"
    },
    "store_code": {
      "type": "string"
    },
    "source_org_id": {
      "type": "integer",
      "minimum": 1,
      "nullable": true
    },
    "brand_name": {
      "type": "string"
    },
    "scope_id": {
      "type": "integer",
      "minimum": 1
    },
    "scope_kind": {
      "type": "string"
    },
    "unit": {
      "type": "string"
    },
    "timezone": {
      "type": "string"
    }
  },
  "required": [
    "id",
    "name",
    "store_code",
    "source_org_id",
    "brand_name",
    "scope_id",
    "scope_kind",
    "unit",
    "timezone"
  ],
  "additionalProperties": false
}
Summary
{
  "type": "object",
  "properties": {
    "requested_days": {
      "type": "integer",
      "minimum": 1
    },
    "available_days": {
      "type": "integer",
      "minimum": 0
    },
    "missing_days": {
      "type": "integer",
      "minimum": 0
    },
    "void_days": {
      "type": "integer",
      "minimum": 0
    },
    "available_energy_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "complete": {
      "type": "boolean"
    },
    "saving_metrics_status": {
      "type": "string",
      "enum": [
        "SEE_METRICS_ENDPOINT"
      ]
    }
  },
  "required": [
    "requested_days",
    "available_days",
    "missing_days",
    "void_days",
    "available_energy_kwh",
    "complete",
    "saving_metrics_status"
  ],
  "additionalProperties": false
}
EnergyResponse
{
  "type": "object",
  "properties": {
    "store_id": {
      "type": "integer",
      "minimum": 1
    },
    "scope_id": {
      "type": "integer",
      "minimum": 1
    },
    "metric": {
      "type": "string"
    },
    "unit": {
      "type": "string"
    },
    "timezone": {
      "type": "string"
    },
    "start": {
      "type": "string",
      "format": "date"
    },
    "end": {
      "type": "string",
      "format": "date"
    },
    "summary": {
      "$ref": "#/components/schemas/Summary"
    },
    "days": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/DailyRow"
      }
    }
  },
  "required": [
    "store_id",
    "scope_id",
    "metric",
    "unit",
    "timezone",
    "start",
    "end",
    "summary",
    "days"
  ],
  "additionalProperties": false
}
ImportRow
{
  "type": "object",
  "properties": {
    "id": {
      "type": "integer",
      "minimum": 1
    },
    "row_number": {
      "type": "integer",
      "minimum": 1
    },
    "resolved_store_id": {
      "type": "integer",
      "minimum": 1
    },
    "business_date": {
      "type": "string",
      "format": "date"
    },
    "raw_energy": {
      "type": "string",
      "nullable": true
    },
    "proposed_energy_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "before_energy_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "action": {
      "type": "string",
      "enum": [
        "INSERT",
        "UPDATE",
        "UNCHANGED",
        "VOID",
        "RESTORE",
        "RELINK",
        "SKIP_MISSING",
        "KEEP_CLEANED"
      ]
    },
    "source_kind": {
      "type": "string"
    },
    "quality_status": {
      "type": "string"
    },
    "quality_note": {
      "type": "string",
      "nullable": true
    },
    "lineage_status": {
      "type": "string"
    }
  },
  "required": [
    "id",
    "row_number",
    "resolved_store_id",
    "business_date",
    "raw_energy",
    "proposed_energy_kwh",
    "before_energy_kwh",
    "action",
    "source_kind",
    "quality_status",
    "quality_note",
    "lineage_status"
  ],
  "additionalProperties": false
}
Import
{
  "type": "object",
  "properties": {
    "id": {
      "type": "integer",
      "minimum": 1
    },
    "batch_no": {
      "type": "string"
    },
    "filename": {
      "type": "string",
      "nullable": true
    },
    "status": {
      "type": "string"
    },
    "total_rows": {
      "type": "integer",
      "minimum": 0
    },
    "insert_rows": {
      "type": "integer",
      "minimum": 0
    },
    "update_rows": {
      "type": "integer",
      "minimum": 0
    },
    "relink_rows": {
      "type": "integer",
      "minimum": 0
    },
    "unchanged_rows": {
      "type": "integer",
      "minimum": 0
    },
    "missing_rows": {
      "type": "integer",
      "minimum": 0
    },
    "kept_cleaned_rows": {
      "type": "integer",
      "minimum": 0
    },
    "uploaded_at": {
      "type": "string"
    },
    "committed_at": {
      "type": "string",
      "nullable": true
    },
    "change_reason": {
      "type": "string",
      "nullable": true
    },
    "rows": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/ImportRow"
      }
    },
    "action_counts": {
      "type": "object",
      "additionalProperties": {
        "type": "integer",
        "minimum": 0
      }
    }
  },
  "required": [
    "id",
    "batch_no",
    "filename",
    "status",
    "total_rows",
    "insert_rows",
    "update_rows",
    "relink_rows",
    "unchanged_rows",
    "missing_rows",
    "kept_cleaned_rows",
    "uploaded_at",
    "committed_at",
    "change_reason",
    "rows",
    "action_counts"
  ],
  "additionalProperties": false
}
Error
{
  "type": "object",
  "properties": {
    "error": {
      "type": "object",
      "properties": {
        "code": {
          "type": "string"
        },
        "message": {
          "type": "string"
        }
      },
      "required": [
        "code",
        "message"
      ],
      "additionalProperties": false
    }
  },
  "required": [
    "error"
  ],
  "additionalProperties": false
}
PreviewInput
{
  "type": "object",
  "properties": {
    "filename": {
      "type": "string",
      "maxLength": 255
    },
    "content_base64": {
      "type": "string",
      "format": "byte",
      "description": "UTF-8 CSV bytes, maximum 4 MiB before base64. Columns 品牌/门店/日期/日能耗, optional 备注."
    },
    "reason": {
      "type": "string",
      "minLength": 1,
      "maxLength": 500
    }
  },
  "required": [
    "filename",
    "content_base64",
    "reason"
  ],
  "additionalProperties": false
}
SourceRecord
{
  "type": "object",
  "properties": {
    "id": {
      "type": "integer",
      "minimum": 1
    },
    "source_kind": {
      "type": "string"
    },
    "source_table": {
      "type": "string"
    },
    "file_sha256": {
      "type": "string"
    },
    "explanation": {
      "type": "string",
      "nullable": true
    },
    "record": {
      "type": "object",
      "additionalProperties": true
    }
  },
  "required": [
    "id",
    "source_kind",
    "source_table",
    "file_sha256",
    "explanation",
    "record"
  ],
  "additionalProperties": false
}
Lineage
{
  "type": "object",
  "properties": {
    "id": {
      "type": "integer",
      "minimum": 1
    },
    "resolved_store_id": {
      "type": "integer",
      "minimum": 1
    },
    "batch_id": {
      "type": "integer",
      "minimum": 1
    },
    "raw_brand": {
      "type": "string",
      "nullable": true
    },
    "raw_store_name": {
      "type": "string",
      "nullable": true
    },
    "raw_date": {
      "type": "string",
      "nullable": true
    },
    "raw_energy": {
      "type": "string",
      "nullable": true
    },
    "action": {
      "type": "string"
    },
    "source_kind": {
      "type": "string"
    },
    "quality_status": {
      "type": "string"
    },
    "quality_note": {
      "type": "string",
      "nullable": true
    },
    "source_reference": {
      "type": "object",
      "nullable": true,
      "additionalProperties": true
    },
    "sources": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/SourceRecord"
      }
    }
  },
  "required": [
    "id",
    "resolved_store_id",
    "batch_id",
    "raw_brand",
    "raw_store_name",
    "raw_date",
    "raw_energy",
    "action",
    "source_kind",
    "quality_status",
    "quality_note",
    "source_reference",
    "sources"
  ],
  "additionalProperties": false
}
Revision
{
  "type": "object",
  "properties": {
    "revision_no": {
      "type": "integer",
      "minimum": 1
    },
    "operation": {
      "type": "string"
    },
    "before_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "after_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "before_is_void": {
      "type": "integer",
      "nullable": true,
      "enum": [
        0,
        1,
        null
      ]
    },
    "after_is_void": {
      "type": "integer",
      "enum": [
        0,
        1
      ]
    },
    "committed_at": {
      "type": "string"
    },
    "change_reason": {
      "type": "string"
    },
    "import_row_id": {
      "type": "integer",
      "minimum": 1
    },
    "source_kind": {
      "type": "string"
    },
    "quality_status": {
      "type": "string"
    },
    "quality_note": {
      "type": "string",
      "nullable": true
    }
  },
  "required": [
    "revision_no",
    "operation",
    "before_kwh",
    "after_kwh",
    "before_is_void",
    "after_is_void",
    "committed_at",
    "change_reason",
    "import_row_id",
    "source_kind",
    "quality_status",
    "quality_note"
  ],
  "additionalProperties": false
}
StoreReference
{
  "type": "object",
  "properties": {
    "target_candidate": {
      "type": "object",
      "properties": {
        "tes_180_kwh": {
          "type": "string",
          "pattern": "^\\d+(\\.\\d{1,4})?$",
          "description": "Exact decimal kWh, serialized as a string; never infer missing as zero."
        },
        "tes_210_kwh": {
          "type": "string",
          "pattern": "^\\d+(\\.\\d{1,4})?$",
          "description": "Exact decimal kWh, serialized as a string; never infer missing as zero."
        },
        "known_payback_days": {
          "type": "integer",
          "nullable": true,
          "enum": [
            180,
            210,
            null
          ]
        },
        "selected_candidate_kwh": {
          "type": "string",
          "pattern": "^\\d+(\\.\\d{1,4})?$",
          "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
          "nullable": true
        },
        "source_revision": {
          "type": "integer",
          "minimum": 1
        },
        "source_sheet_id": {
          "type": "string"
        },
        "source_row": {
          "type": "integer",
          "minimum": 1
        },
        "source_url": {
          "type": "string"
        },
        "status": {
          "type": "string",
          "enum": [
            "AWAITING_EFFECTIVE_DATE",
            "AWAITING_SETTLEMENT_AND_DATE"
          ]
        },
        "unit": {
          "type": "string"
        },
        "basis": {
          "type": "string"
        },
        "capture_id": {
          "type": "integer",
          "minimum": 1
        },
        "source_record_id": {
          "type": "integer",
          "minimum": 1
        }
      },
      "required": [
        "tes_180_kwh",
        "tes_210_kwh",
        "known_payback_days",
        "selected_candidate_kwh",
        "source_revision",
        "source_sheet_id",
        "source_row",
        "source_url",
        "status",
        "unit",
        "basis",
        "capture_id",
        "source_record_id"
      ],
      "additionalProperties": false,
      "nullable": true
    },
    "profile": {
      "type": "object",
      "properties": {
        "id": {
          "type": "integer",
          "minimum": 1
        },
        "name": {
          "type": "string"
        },
        "brand_name": {
          "type": "string"
        },
        "city": {
          "type": "string",
          "nullable": true
        },
        "address": {
          "type": "string",
          "nullable": true
        },
        "business_hours": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "start": {
                "type": "string"
              },
              "end": {
                "type": "string"
              },
              "next_day": {
                "type": "boolean"
              }
            },
            "required": [
              "start",
              "end",
              "next_day"
            ],
            "additionalProperties": false
          },
          "nullable": true
        },
        "business_mode": {
          "type": "string",
          "nullable": true
        },
        "settlement_mode": {
          "type": "string",
          "nullable": true
        },
        "version": {
          "type": "integer",
          "minimum": 1
        },
        "peak_hours": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "start": {
                "type": "string"
              },
              "end": {
                "type": "string"
              },
              "next_day": {
                "type": "boolean"
              }
            },
            "required": [
              "start",
              "end",
              "next_day"
            ],
            "additionalProperties": false
          },
          "nullable": true
        },
        "tags": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "nullable": true
        },
        "is_key_store": {
          "type": "boolean",
          "description": "Legacy storage flag; false alone is not a confirmed non-key classification."
        },
        "key_store_status": {
          "type": "string",
          "enum": [
            "MARKED",
            "UNCONFIRMED"
          ]
        },
        "equipment_profile": {
          "type": "object",
          "nullable": true,
          "additionalProperties": true
        }
      },
      "required": [
        "id",
        "name",
        "brand_name",
        "city",
        "address",
        "business_hours",
        "business_mode",
        "settlement_mode",
        "version",
        "peak_hours",
        "tags",
        "is_key_store",
        "key_store_status",
        "equipment_profile"
      ],
      "additionalProperties": false
    },
    "devices": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer",
            "minimum": 1
          },
          "source_device_id": {
            "type": "string"
          },
          "device_name": {
            "type": "string",
            "nullable": true,
            "maxLength": 50,
            "description": "Legacy op_device.name; null means no provided name, never inferred from code or type."
          },
          "device_code": {
            "type": "string"
          },
          "device_type": {
            "type": "string"
          },
          "online_status": {
            "type": "string",
            "enum": [
              "ONLINE",
              "OFFLINE",
              "UNKNOWN"
            ]
          },
          "status_observed_at": {
            "type": "string",
            "nullable": true
          },
          "synced_at": {
            "type": "string",
            "nullable": true
          },
          "source_reference": {
            "type": "string",
            "nullable": true
          }
        },
        "required": [
          "id",
          "source_device_id",
          "device_name",
          "device_code",
          "device_type",
          "online_status",
          "status_observed_at",
          "synced_at",
          "source_reference"
        ],
        "additionalProperties": false
      }
    },
    "strategy": {
      "type": "object",
      "properties": {
        "configured_count": {
          "type": "integer",
          "minimum": 0,
          "nullable": true
        },
        "dispatched_count": {
          "type": "integer",
          "minimum": 0,
          "nullable": true
        },
        "distinguish_calendar": {
          "type": "boolean",
          "nullable": true
        },
        "execution_status": {
          "type": "string"
        },
        "source_reference": {
          "type": "string",
          "nullable": true
        },
        "observed_at": {
          "type": "string",
          "nullable": true
        },
        "synced_at": {
          "type": "string"
        }
      },
      "required": [
        "configured_count",
        "dispatched_count",
        "distinguish_calendar",
        "execution_status",
        "source_reference",
        "observed_at",
        "synced_at"
      ],
      "additionalProperties": false,
      "nullable": true
    },
    "mode": {
      "type": "string",
      "enum": [
        "FROZEN_LEGACY_REFERENCE"
      ]
    },
    "timestamp_timezone": {
      "type": "string",
      "enum": [
        "UTC"
      ]
    },
    "strategy_count_basis": {
      "type": "string"
    },
    "availability_basis": {
      "type": "string"
    },
    "unavailable": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "editable_fields": {
      "type": "array",
      "items": {
        "type": "string",
        "enum": [
          "name",
          "address"
        ]
      }
    },
    "can_edit_profile": {
      "type": "boolean"
    },
    "stage_snapshot": {
      "type": "object",
      "properties": {
        "stage": {
          "type": "string",
          "nullable": true
        },
        "legacy_progress": {
          "type": "string",
          "nullable": true
        },
        "captured_at": {
          "type": "string"
        },
        "source_reference": {
          "type": "string"
        },
        "basis": {
          "type": "string"
        }
      },
      "required": [
        "stage",
        "legacy_progress",
        "captured_at",
        "source_reference",
        "basis"
      ],
      "additionalProperties": false,
      "nullable": true
    },
    "stage_history": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "stage": {
            "type": "string"
          },
          "valid_from": {
            "type": "string"
          },
          "valid_to": {
            "type": "string",
            "nullable": true
          },
          "change_reason": {
            "type": "string"
          }
        },
        "required": [
          "stage",
          "valid_from",
          "valid_to",
          "change_reason"
        ],
        "additionalProperties": false
      }
    },
    "device_events": {
      "type": "object",
      "properties": {
        "start": {
          "type": "string"
        },
        "end": {
          "type": "string"
        },
        "coverage": {
          "type": "string",
          "enum": [
            "UNVERIFIED"
          ]
        },
        "offline_count": {
          "type": "integer",
          "minimum": 0,
          "nullable": true
        },
        "events": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer",
                "minimum": 1
              },
              "device_id": {
                "type": "integer",
                "minimum": 1
              },
              "event_type": {
                "type": "string"
              },
              "occurred_at": {
                "type": "string"
              },
              "note": {
                "type": "string",
                "nullable": true
              }
            },
            "required": [
              "id",
              "device_id",
              "event_type",
              "occurred_at",
              "note"
            ],
            "additionalProperties": false
          }
        }
      },
      "required": [
        "start",
        "end",
        "coverage",
        "offline_count",
        "events"
      ],
      "additionalProperties": false
    }
  },
  "required": [
    "target_candidate",
    "profile",
    "devices",
    "strategy",
    "mode",
    "timestamp_timezone",
    "strategy_count_basis",
    "availability_basis",
    "unavailable",
    "editable_fields",
    "can_edit_profile",
    "stage_snapshot",
    "stage_history",
    "device_events"
  ],
  "additionalProperties": false
}
ProfileUpdate
{
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 80
    },
    "address": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    },
    "expected_version": {
      "type": "integer",
      "minimum": 1
    }
  },
  "required": [
    "name",
    "address",
    "expected_version"
  ],
  "additionalProperties": false
}
ProfileResult
{
  "type": "object",
  "properties": {
    "status": {
      "type": "string",
      "enum": [
        "UPDATED",
        "UNCHANGED"
      ]
    },
    "profile": {
      "type": "object",
      "properties": {
        "id": {
          "type": "integer",
          "minimum": 1
        },
        "name": {
          "type": "string"
        },
        "address": {
          "type": "string",
          "nullable": true
        },
        "version": {
          "type": "integer",
          "minimum": 1
        }
      },
      "required": [
        "id",
        "name",
        "address",
        "version"
      ],
      "additionalProperties": false
    }
  },
  "required": [
    "status",
    "profile"
  ],
  "additionalProperties": false
}
TargetDay
{
  "type": "object",
  "properties": {
    "business_date": {
      "type": "string",
      "format": "date"
    },
    "status": {
      "type": "string",
      "enum": [
        "AVAILABLE",
        "NO_EFFECTIVE_TARGET"
      ]
    },
    "target_period_id": {
      "type": "integer",
      "minimum": 1,
      "nullable": true
    },
    "tes_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "tes_180_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "tes_210_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "tev_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "effective_tev_warning_ratio": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "tev_latest_observed_date": {
      "type": "string",
      "nullable": true,
      "format": "date"
    },
    "source_kind": {
      "type": "string",
      "nullable": true
    },
    "source_reference": {
      "type": "string",
      "nullable": true
    },
    "evaluation_status": {
      "type": "string",
      "enum": [
        "NOT_EVALUATED"
      ]
    }
  },
  "required": [
    "business_date",
    "status",
    "target_period_id",
    "tes_kwh",
    "tes_180_kwh",
    "tes_210_kwh",
    "tev_kwh",
    "effective_tev_warning_ratio",
    "tev_latest_observed_date",
    "source_kind",
    "source_reference",
    "evaluation_status"
  ],
  "additionalProperties": false
}
TargetsResponse
{
  "type": "object",
  "properties": {
    "store_id": {
      "type": "integer",
      "minimum": 1
    },
    "scope_id": {
      "type": "integer",
      "minimum": 1
    },
    "unit": {
      "type": "string",
      "enum": [
        "kWh/日"
      ]
    },
    "timezone": {
      "type": "string"
    },
    "start": {
      "type": "string",
      "format": "date"
    },
    "end": {
      "type": "string",
      "format": "date"
    },
    "summary": {
      "type": "object",
      "properties": {
        "requested_days": {
          "type": "integer",
          "minimum": 0
        },
        "available_days": {
          "type": "integer",
          "minimum": 0
        },
        "missing_days": {
          "type": "integer",
          "minimum": 0
        }
      },
      "required": [
        "requested_days",
        "available_days",
        "missing_days"
      ],
      "additionalProperties": false
    },
    "days": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/TargetDay"
      }
    },
    "note": {
      "type": "string"
    }
  },
  "required": [
    "store_id",
    "scope_id",
    "unit",
    "timezone",
    "start",
    "end",
    "summary",
    "days",
    "note"
  ],
  "additionalProperties": false
}
MetricDay
{
  "type": "object",
  "properties": {
    "business_date": {
      "type": "string",
      "format": "date"
    },
    "actual_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "eba_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "aes_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "aer_percent": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "tes_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "tes_180_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "tes_210_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "tev_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "tes_threshold_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "tes_180_threshold_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "tes_210_threshold_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "tev_threshold_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "effective_tev_warning_ratio": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "eba_pass": {
      "type": "boolean",
      "nullable": true
    },
    "tes_pass": {
      "type": "boolean",
      "nullable": true
    },
    "tes_180_pass": {
      "type": "boolean",
      "nullable": true
    },
    "tes_210_pass": {
      "type": "boolean",
      "nullable": true
    },
    "tev_pass": {
      "type": "boolean",
      "nullable": true
    },
    "tev_latest_observed_date": {
      "type": "string",
      "nullable": true,
      "format": "date"
    },
    "baseline_status": {
      "type": "string"
    },
    "baseline_reference": {
      "type": "string",
      "nullable": true
    },
    "stage": {
      "type": "string",
      "nullable": true
    },
    "evaluation_status": {
      "type": "string"
    }
  },
  "required": [
    "business_date",
    "actual_kwh",
    "eba_kwh",
    "aes_kwh",
    "aer_percent",
    "tes_kwh",
    "tes_180_kwh",
    "tes_210_kwh",
    "tev_kwh",
    "tes_threshold_kwh",
    "tes_180_threshold_kwh",
    "tes_210_threshold_kwh",
    "tev_threshold_kwh",
    "effective_tev_warning_ratio",
    "eba_pass",
    "tes_pass",
    "tes_180_pass",
    "tes_210_pass",
    "tev_pass",
    "tev_latest_observed_date",
    "baseline_status",
    "baseline_reference",
    "stage",
    "evaluation_status"
  ],
  "additionalProperties": false
}
MetricSummary
{
  "type": "object",
  "properties": {
    "requested_days": {
      "type": "integer",
      "minimum": 0
    },
    "actual_days": {
      "type": "integer",
      "minimum": 0
    },
    "eba_days": {
      "type": "integer",
      "minimum": 0
    },
    "matched_days": {
      "type": "integer",
      "minimum": 0
    },
    "savings_days": {
      "type": "integer",
      "minimum": 0
    },
    "actual_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "matched_eba_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "aes_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "aer_percent": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "tes_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "tes_180_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "tes_210_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "tes_threshold_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "tes_180_threshold_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "tes_210_threshold_kwh": {
      "type": "string",
      "pattern": "^\\d+(\\.\\d{1,4})?$",
      "description": "Exact decimal kWh, serialized as a string; never infer missing as zero.",
      "nullable": true
    },
    "eba_pass": {
      "type": "boolean",
      "nullable": true
    },
    "tes_pass": {
      "type": "boolean",
      "nullable": true
    },
    "tes_180_pass": {
      "type": "boolean",
      "nullable": true
    },
    "tes_210_pass": {
      "type": "boolean",
      "nullable": true
    },
    "tev_pass": {
      "type": "boolean",
      "nullable": true
    },
    "complete_for_savings": {
      "type": "boolean"
    }
  },
  "required": [
    "requested_days",
    "actual_days",
    "eba_days",
    "matched_days",
    "savings_days",
    "actual_kwh",
    "matched_eba_kwh",
    "aes_kwh",
    "aer_percent",
    "tes_kwh",
    "tes_180_kwh",
    "tes_210_kwh",
    "tes_threshold_kwh",
    "tes_180_threshold_kwh",
    "tes_210_threshold_kwh",
    "eba_pass",
    "tes_pass",
    "tes_180_pass",
    "tes_210_pass",
    "tev_pass",
    "complete_for_savings"
  ],
  "additionalProperties": false
}
MetricsResponse
{
  "type": "object",
  "properties": {
    "store_id": {
      "type": "integer",
      "minimum": 1
    },
    "scope_id": {
      "type": "integer",
      "minimum": 1
    },
    "unit": {
      "type": "string"
    },
    "timezone": {
      "type": "string"
    },
    "start": {
      "type": "string",
      "format": "date"
    },
    "end": {
      "type": "string",
      "format": "date"
    },
    "baseline_policy": {
      "type": "string",
      "enum": [
        "SUMMER_ONLY"
      ]
    },
    "baseline_usage": {
      "type": "string",
      "enum": [
        "LOCAL_TRIAL"
      ]
    },
    "rule_version": {
      "type": "string"
    },
    "tev_warning_ratio": {
      "type": "string",
      "enum": [
        "1.2"
      ],
      "description": "Default only; the effective ratio is returned on each day."
    },
    "summary": {
      "$ref": "#/components/schemas/MetricSummary"
    },
    "availability": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "metric": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "available_days": {
            "type": "integer",
            "minimum": 0
          },
          "requested_days": {
            "type": "integer",
            "minimum": 0
          },
          "status": {
            "type": "string",
            "enum": [
              "AVAILABLE",
              "PARTIAL",
              "UNAVAILABLE"
            ]
          },
          "reasons": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "metric",
          "label",
          "available_days",
          "requested_days",
          "status",
          "reasons"
        ],
        "additionalProperties": false
      }
    },
    "days": {
      "type": "array",
      "items": {
        "$ref": "#/components/schemas/MetricDay"
      }
    },
    "notes": {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  },
  "required": [
    "store_id",
    "scope_id",
    "unit",
    "timezone",
    "start",
    "end",
    "baseline_policy",
    "baseline_usage",
    "rule_version",
    "tev_warning_ratio",
    "summary",
    "availability",
    "days",
    "notes"
  ],
  "additionalProperties": false
}
CatalogStore
{
  "type": "object",
  "properties": {
    "id": {
      "type": "integer",
      "minimum": 1
    },
    "name": {
      "type": "string"
    },
    "store_code": {
      "type": "string"
    },
    "source_org_id": {
      "type": "integer",
      "minimum": 1,
      "nullable": true
    },
    "brand_name": {
      "type": "string"
    },
    "scope_id": {
      "type": "integer",
      "minimum": 1
    },
    "scope_kind": {
      "type": "string"
    },
    "unit": {
      "type": "string"
    },
    "timezone": {
      "type": "string"
    },
    "city": {
      "type": "string",
      "nullable": true
    },
    "business_hours": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "start": {
            "type": "string"
          },
          "end": {
            "type": "string"
          },
          "next_day": {
            "type": "boolean"
          }
        },
        "required": [
          "start",
          "end",
          "next_day"
        ],
        "additionalProperties": false
      },
      "nullable": true
    },
    "peak_hours": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "start": {
            "type": "string"
          },
          "end": {
            "type": "string"
          },
          "next_day": {
            "type": "boolean"
          }
        },
        "required": [
          "start",
          "end",
          "next_day"
        ],
        "additionalProperties": false
      },
      "nullable": true
    },
    "business_mode": {
      "type": "string",
      "nullable": true
    },
    "settlement_mode": {
      "type": "string",
      "nullable": true
    },
    "is_key_store": {
      "type": "boolean",
      "description": "Legacy storage flag; false alone is not a confirmed non-key classification."
    },
    "key_store_status": {
      "type": "string",
      "enum": [
        "MARKED",
        "UNCONFIRMED"
      ]
    },
    "tags": {
      "type": "array",
      "items": {
        "type": "string"
      },
      "nullable": true
    },
    "stage": {
      "type": "string",
      "nullable": true
    },
    "stage_basis": {
      "type": "string",
      "nullable": true
    },
    "device_count": {
      "type": "integer",
      "minimum": 0
    },
    "online_count": {
      "type": "integer",
      "minimum": 0
    },
    "offline_count": {
      "type": "integer",
      "minimum": 0
    },
    "unknown_count": {
      "type": "integer",
      "minimum": 0
    }
  },
  "required": [
    "id",
    "name",
    "store_code",
    "source_org_id",
    "brand_name",
    "scope_id",
    "scope_kind",
    "unit",
    "timezone",
    "city",
    "business_hours",
    "peak_hours",
    "business_mode",
    "settlement_mode",
    "is_key_store",
    "key_store_status",
    "tags",
    "stage",
    "stage_basis",
    "device_count",
    "online_count",
    "offline_count",
    "unknown_count"
  ],
  "additionalProperties": false
}
PeriodOverview
{
  "type": "object",
  "properties": {
    "store_id": {
      "type": "integer",
      "minimum": 1
    },
    "scope_id": {
      "type": "integer",
      "minimum": 1
    },
    "as_of": {
      "type": "string",
      "format": "date"
    },
    "timezone": {
      "type": "string"
    },
    "periods": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "enum": [
              "day",
              "week",
              "month",
              "year"
            ]
          },
          "label": {
            "type": "string"
          },
          "start": {
            "type": "string",
            "format": "date"
          },
          "end": {
            "type": "string",
            "format": "date"
          },
          "summary": {
            "$ref": "#/components/schemas/MetricSummary"
          }
        },
        "required": [
          "key",
          "label",
          "start",
          "end",
          "summary"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "store_id",
    "scope_id",
    "as_of",
    "timezone",
    "periods"
  ],
  "additionalProperties": false
}

8. 服务辅助入口

GET /health/live 检查进程;GET /health/ready 检查数据库及迁移;GET /openapi.json 获取契约;GET /api-docs 获取本说明。这些不是业务数据接口。

浏览器认证 POST /auth/session 接收 {"access_key":"凭证"},须合法同源 Origin;成功设置 8 小时会话 cookie。退出使用 POST /auth/logout,同样须合法 Origin。外部程序使用 Bearer 凭证调用业务接口即可,无需模拟浏览器登录。