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 返回有权限的门店列表,每家包含 id 和 scope_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. 返回值怎么理解
- 空调用电,kWh;北京时间自然日。金额/电量用十进制字符串,如
"80.0000",客户端不要用二进制浮点改变原始精度。 null/NO_DATA是缺失;"0.0000"是真实零。不能把二者混为一谈。CLEANED为清洗来源;LEGACY_DIRECT为已封存老库来源。两者可用不同颜色展示;来自老库不自动表示已完成质量复核。清洗非空优先,清洗空白回查封存老库,双方无值留空。- 当前数据是冻结的真实快照,没有持续同步调度器。查询读取新库当前生效记录,不会在每次请求时连接老库。
- TES-180 与 TES-210 是两个独立指标,分别为设备单价×计价数量÷180÷电价、÷210÷电价,电价固定 1 元/kWh,单位 kWh/日。
tes_180_kwh、tes_210_kwh不由旧兼容字段tes_kwh推导或二选一。 - 达美乐已封存双 TES 候选;正式生效日期尚未定位,故 targets 仍为空。候选不等于正式发布。遇见小面 TEV 取老库最新有效正 target_value 并保留来源日期,不倒填已知首日以前。
- EBA 目前只有小寨 2026-08-18~31 的 14 天夏季结果;其他门店、非夏季或无结果日留空。AES/AER 和正式达标结论还需日期对应历史阶段,目前阶段未补齐。
- 每个 TES 目标独立使用 min(TES,EBA) 形成比较阈值。检查各自 pass 与缺失状态,不能用一个 TES 结果代表另一个。
- 周期 requested_days 与 actual_days 要一起看;部分天数合计不等于完整周期合计。设备和策略是封存状态,不能解读为实时控制或实时在线监测。
4. 导入与档案修改
普通数据消费者到读取步骤即可。写入方需要 write 权限及数据库成员角色允许,原始追溯另需 raw。当前客户端只提供读取;操作人员使用平台“手动导入清洗数据”或依据以下完整契约开发专用调用。
- CSV 使用 UTF-8,字段以平台导入模板为准;仅支持已实现的固定格式,不接受任意 Excel 文件。单文件上限 4 MiB、最多 10,000 行。
POST /api/v1/imports/preview,Content-Type: application/json,Authorization: Bearer 凭证,Idempotency-Key 为本次预览的唯一字符串(最多 64 字符)。JSON 请求为 filename、content_base64、reason。content_base64 是完整 CSV 文件字节的 Base64。预览会保存来源和导入草稿,但不改变生效能耗。- 检查预览响应中批次、每行结果、错误与变化;有错误先修正文件,不自动提交。
POST /api/v1/imports/{batch_id}/commit,JSON 为{},使用该批次 ID 和提交操作自己的 Idempotency-Key。提交会修改真实数据;同一操作重试复用原键及原请求,不换键制造重复业务。- 修改档案先 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 凭证调用业务接口即可,无需模拟浏览器登录。