# 能源管理系统 API 接口文档 ## 1. 文档概述 本文档用于规范能源管理系统后台接口调用标准,为前端开发、第三方对接、接口测试提供统一依据。 适用接口范围包括: - 账号密码登录并获取授权 Token - 按自定义条件查询小时电量数据 ## 2. 通用约定 | 项目 | 说明 | | --- | --- | | 基础服务地址 | `http://192.168.10.180:8089/api` | | 请求编码 | `UTF-8` | | 默认请求方式 | `POST` | | 默认请求头 | `Content-Type: application/json` | | 鉴权方式 | 登录接口无需鉴权;业务接口需在请求头携带 `token` | ## 3. 接口分析汇总 | 序号 | 接口 | 路径 | 是否鉴权 | 主要用途 | | --- | --- | --- | --- | --- | | 1 | 获取授权 Token | `/thingshome-admin/sys/service/getToken` | 否 | 使用账号密码换取全局业务接口 Token | | 2 | 查询小时电量数据 | `/thingshome-ems/realTime/selectByCustom` | 是 | 按时间范围、采集点位、能源类型查询小时用电量 | 字段与文档一致性说明: - `tagNameList` 在参数表中写为 `String`,但请求示例为数组;结合字段名和接口语义,建议按 `Array[String]` 传参。 - `energyTypeList` 原文写为 `Arrary[String]`,应为 `Array[String]`。 - 查询接口示例中包含 `type: "1"`,但参数表未说明;建议测试时保留该字段,避免后端依赖。 - 查询接口返回说明中 `data.dataList.fullTime`、`data.energyValue.thisValue` 结构可能表示 `data` 内含列表对象,实际返回结构需以联调响应为准。 ## 4. 获取授权 Token ### 4.1 基础信息 | 项目 | 内容 | | --- | --- | | 接口路径 | `/thingshome-admin/sys/service/getToken` | | 完整地址 | `http://192.168.10.180:8089/api/thingshome-admin/sys/service/getToken` | | 请求方式 | `POST` | | 是否鉴权 | 否 | | 接口用途 | 通过账号密码登录,获取全局接口授权 Token | ### 4.2 请求头 | 参数名 | 是否必填 | 说明 | | --- | --- | --- | | `Content-Type` | 是 | 固定为 `application/json` | ### 4.3 请求参数 | 参数名 | 类型 | 是否必填 | 说明 | 示例值 | | --- | --- | --- | --- | --- | | `username` | String | 是 | 系统后台登录账号 | `dt` | | `password` | String | 是 | 系统后台登录密码 | `dt2026` | ### 4.4 请求示例 ```json { "username": "dt", "password": "dt2026" } ``` ### 4.5 返回参数 | 参数名 | 类型 | 说明 | | --- | --- | --- | | `msg` | String | 操作结果提示 | | `code` | Integer | `0` 表示成功,非 `0` 表示失败 | | `data` | Object | 固定返回 `null`,无业务数据 | | `expire` | Long | Token 有效时长,单位秒,默认 `43200` 秒 | | `token` | String | 接口全局授权凭证,业务接口请求头携带 | ### 4.6 成功返回示例 ```json { "msg": "操作成功", "code": 0, "data": null, "expire": 43200, "token": "3b622f1e7620d97589ba56d4b2ada5f4" } ``` ### 4.7 失败返回示例 ```json { "msg": "账号或密码不正确", "code": 500, "data": null } ``` ### 4.8 可执行 curl 测试 ```bash curl -X POST "http://192.168.10.180:8089/api/thingshome-admin/sys/service/getToken" \ -H "Content-Type: application/json" \ -d '{"username":"dt","password":"dt2026"}' ``` ### 4.9 可执行 Python 测试 依赖安装:`pip install requests` ```python import requests base_url = "http://192.168.10.180:8089/api" url = f"{base_url}/thingshome-admin/sys/service/getToken" payload = { "username": "dt", "password": "dt2026", } response = requests.post(url, json=payload, timeout=15) print("HTTP Status:", response.status_code) print(response.text) response.raise_for_status() result = response.json() if result.get("code") == 0: print("Token:", result.get("token")) else: raise RuntimeError(f"登录失败: {result}") ``` ## 5. 自定义条件查询小时电量数据 ### 5.1 基础信息 | 项目 | 内容 | | --- | --- | | 接口路径 | `/thingshome-ems/realTime/selectByCustom` | | 完整地址 | `http://192.168.10.180:8089/api/thingshome-ems/realTime/selectByCustom` | | 请求方式 | `POST` | | 是否鉴权 | 是,请求头必须携带 `token` | | 接口用途 | 根据时间范围、采集点位、能源类型查询小时维度用电量,支持同比、环比数据计算 | ### 5.2 请求头 | 参数名 | 类型 | 是否必填 | 说明 | | --- | --- | --- | --- | | `Content-Type` | String | 是 | 固定为 `application/json` | | `token` | String | 是 | 登录接口获取的授权凭证 | ### 5.3 请求参数 | 参数名 | 类型 | 是否必填 | 说明 | 示例值 | | --- | --- | --- | --- | --- | | `type` | String | 建议传 | 原始示例包含该字段,参数表未说明 | `1` | | `tagNameList` | Array[String] | 是 | 设备采集点位 ID 列表 | `["1024a2f19bbe464ab9073b5f5d92eea5"]` | | `energyTypeList` | Array[String] | 是 | 能源类型,固定为 `["electric power"]` | `["electric power"]` | | `tb` | String | 是 | 固定为 `0` | `0` | | `startTime` | String | 是 | 查询开始时间,格式 `yyyy-MM-dd HH:mm:ss` | `2026-07-01 00:00:00` | | `endTime` | String | 是 | 查询结束时间,格式 `yyyy-MM-dd HH:mm:ss` | `2026-07-01 23:59:59` | | `dateCode` | String | 是 | 小时维度固定为 `h` | `h` | ### 5.4 请求示例 ```json { "type": "1", "tagNameList": ["1024a2f19bbe464ab9073b5f5d92eea5"], "energyTypeList": ["electric power"], "tb": "0", "startTime": "2026-07-01 00:00:00", "endTime": "2026-07-01 23:59:59", "dateCode": "h" } ``` ### 5.5 返回参数 | 参数名 | 类型 | 说明 | | --- | --- | --- | | `msg` | String | 请求结果提示 | | `code` | Integer | 状态码,`0` 表示成功 | | `data` | Array/Object | 电量数据结果,实际结构以接口返回为准 | | `data.dataList.fullTime` | String | 数据统计小时时间 | | `data.energyValue.thisValue` | Double | 当前小时用电量,单位 `kWh` | ### 5.6 可执行 curl 测试 先将登录接口返回的 Token 替换到 `$TOKEN`。 ```bash export TOKEN="替换为登录接口返回的token" date && curl -X POST "http://192.168.10.180:8089/api/thingshome-ems/realTime/selectByCustom" \ -H "Content-Type: application/json" \ -H "token: ${TOKEN}" \ -d '{"type":"1","tagNameList":["451a9243-7817-4b75-87df-f1cae52dfa30", "44ca7632-b9a5-47e8-af4b-2fe3971021fc","0a55b5eb-9a7a-401c-954a-9ce4488f5a8b"],"energyTypeList":["electric power"],"tb":"0","startTime":"2026-07-01 15:00:00","endTime":"2026-07-01 18:59:59","dateCode":"h"}' ``` ### 5.7 可执行 Python 测试 依赖安装:`pip install requests` ```python import requests base_url = "http://192.168.10.180:8089/api" login_url = f"{base_url}/thingshome-admin/sys/service/getToken" login_payload = { "username": "dt", "password": "dt2026", } login_response = requests.post(login_url, json=login_payload, timeout=15) print("Login HTTP Status:", login_response.status_code) print(login_response.text) login_response.raise_for_status() login_result = login_response.json() if login_result.get("code") != 0: raise RuntimeError(f"登录失败: {login_result}") token = login_result["token"] query_url = f"{base_url}/thingshome-ems/realTime/selectByCustom" headers = { "Content-Type": "application/json", "token": token, } query_payload = { "type": "1", "tagNameList": ["1024a2f19bbe464ab9073b5f5d92eea5"], "energyTypeList": ["electric power"], "tb": "0", "startTime": "2026-07-01 23:30:00", "endTime": "2026-07-01 23:59:59", "dateCode": "h", } query_response = requests.post(query_url, headers=headers, json=query_payload, timeout=30) print("Query HTTP Status:", query_response.status_code) print(query_response.text) query_response.raise_for_status() query_result = query_response.json() if query_result.get("code") != 0: raise RuntimeError(f"查询失败: {query_result}") ``` ### 5.8 响应数据 ``` { "msg": "操作成功", "code": 0, "data": { "unit": "kW·h", "data": [ { "name": "全厂", "id": "1024a2f19bbe464ab9073b5f5d92eea5", "parentId": "0", "type": null, "value": 0.0, "tbValue": 0.0, "hbValue": 0.0, "tbCost": 0.0, "hbCost": 0.0, "tbCoal": 0.0, "hbCoal": 0.0, "tbCarbon": 0.0, "hbCarbon": 0.0, "tbCompare": 0.0, "hbCompare": 0.0, "dataList": [ { "id": "1024a2f19bbe464ab9073b5f5d92eea5", "costValue": 0.0, "coalValue": 0.0, "carbonValue": 0.0, "hbValue": 0.0, "tbValue": 0.0, "hbCarbon": 0.0, "tbCarbon": 0.0, "hbCoal": 0.0, "tbCoal": 0.0, "hbCost": 0.0, "tbCost": 0.0, "sort": null, "hbCompare": 0.0, "tbCompare": 0.0, "name": "全厂", "unit": "kW·h", "aggregationType": null, "fullTime": "2026-07-01 23:00:00", "forecastValue": null, "savingValue": null, "productName": null, "energyType": "electric power", "weekTime": null, "thisValue": 0.0, "time": "2026-07-01 23", "unitName": "kW·h", "acquisitionTime": "2026-07-01 23", "mean": null, "up": null, "low": null, "std": null, "cpk": null, "status": 1, "paramsList": null, "ybList": null, "e": null, "t": null, "coalUnit": null, "carbonUnit": null, "costUnit": null, "forestValue": null, "deviationValue": null, "forestDeviationValue": null } ], "unit": "kW·h", "unitName": "kW·h", "costValue": 0.0, "coalValue": 0.0, "carbonValue": 0.0, "sort": 0, "energyType": "electric power", "fullName": "全厂", "accuracy": "0.0", "coalUnit": null, "carbonUnit": null, "costUnit": null } ], "trend": [ { "id": "1024a2f19bbe464ab9073b5f5d92eea5", "costValue": 0.0, "coalValue": 0.0, "carbonValue": 0.0, "hbValue": 0.0, "tbValue": 0.0, "hbCarbon": 0.0, "tbCarbon": 0.0, "hbCoal": 0.0, "tbCoal": 0.0, "hbCost": 0.0, "tbCost": 0.0, "sort": null, "hbCompare": 0.0, "tbCompare": 0.0, "name": "全厂", "unit": "kW·h", "aggregationType": null, "fullTime": "2026-07-01 23:00:00", "forecastValue": null, "savingValue": null, "productName": null, "energyType": "electric power", "weekTime": null, "thisValue": 0.0, "time": "2026-07-01 23", "unitName": "kW·h", "acquisitionTime": "2026-07-01 23", "mean": null, "up": null, "low": null, "std": null, "cpk": null, "status": 1, "paramsList": null, "ybList": null, "e": null, "t": null, "coalUnit": null, "carbonUnit": null, "costUnit": null, "forestValue": null, "deviationValue": null, "forestDeviationValue": null } ], "sum": 0.0 } } ``` ## 6. 联调建议 - 登录成功后,Token 默认有效期为 `43200` 秒,业务接口测试时优先复用同一个 Token。 - 若查询接口返回鉴权失败,先重新调用登录接口获取 Token。 - 若查询结果为空,优先检查 `tagNameList` 点位 ID 是否存在、时间范围内是否有小时电量数据。 - 若后端对请求头大小写敏感,应保持请求头名称为文档中的 `token`。 ## 7. 小时数据采集入库程序 ### 7.1 程序目标 使用 Python 编写小时数据采集程序,定时从能源管理系统接口查询上一小时点位数据,通过 `addpointdatum` 接口写入点位数据,并触发计算聚合接口。 已实现脚本:`main.py`。 ### 7.2 执行时间规则 程序默认每小时第 `30` 分钟执行一次。 示例: - `18:30` 执行一次 - 查询开始时间:`17:00:00` - 查询结束时间:`17:59:59` - 聚合接口 `begin` 时间戳:`17:00:00` - 聚合接口 `end` 时间戳:`18:00:00` 时间戳按当前服务器本地时区转换。 ### 7.3 配置文件 程序使用 YAML 配置文件,默认读取 `config.yaml`。 配置示例: ```yaml energy_chaowang: base_url: "http://192.168.10.180:8089/api" username: "dt" password: "dt2026" login_timeout_seconds: 15 query_timeout_seconds: 30 token_refresh_interval_hours: 6 points: file: "points.xlsx" services: basedataportal: "http://basedataportal-svc:8080" calcagg: "http://127.0.0.1:8080" schedule: minute: 30 logging: file: "logs/jdf-energy-collector.log" retention_days: 3 ``` 字段说明: | 配置项 | 说明 | | --- | --- | | `energy_chaowang.base_url` | 能源管理系统接口基础地址 | | `energy_chaowang.username` | 登录用户名 | | `energy_chaowang.password` | 登录密码 | | `energy_chaowang.login_timeout_seconds` | 登录接口超时时间 | | `energy_chaowang.query_timeout_seconds` | 小时点位数据查询接口超时时间 | | `energy_chaowang.token_refresh_interval_hours` | Token 重新登录间隔,默认 `6` 小时 | | `points.file` | 点位 Excel 文件路径 | | `services.basedataportal` | 基础数据服务基础地址,例如 `http://basedataportal-svc:8080` | | `services.calcagg` | 计算聚合服务基础地址,例如 `http://127.0.0.1:8080` | | `schedule.minute` | 每小时执行分钟,默认 `30` | | `logging.file` | 日志文件路径 | | `logging.retention_days` | 日志保留天数,默认 `3` 天 | ### 7.4 点位来源 点位文件为 `points.xlsx`。 点位文件只在程序启动时读取一次,后续每次 `run_once` 直接使用内存中的点位映射。 启动时会校验: - `id` 列不能有重复值 - `point_id` 列不能有重复值 - 若存在重复值,程序直接退出,不进入常驻调度 使用字段: | Excel 列名 | 用途 | | --- | --- | | `id` | 作为能源管理系统查询接口 `tagNameList` 的内容 | | `point_id` | 作为 `addpointdatum` 和聚合接口 `point_ids` 的内容 | Excel 示例: | id | point_id | | --- | --- | | `b541250d-23a8-432c-863a-e6807c70da0f` | `P_TEST_1` | | `0c1867f8-cc95-46a3-a3f4-ff56b0ffc3bf` | `P_TEST_2` | ### 7.5 登录接口调用规则 程序启动后首次查询小时点位数据前,需要先调用登录接口获取 Token。Token 保存在内存中,后续查询复用该 Token。 登录刷新规则: - 默认每 `6` 小时重新调用一次登录接口 - 可通过 `energy_chaowang.token_refresh_interval_hours` 配置 - 示例:上次登录接口请求时间为 `10:00`,下次登录接口请求时间为 `16:00` 登录重试规则: - 若登录失败,等待 `60` 秒后重试 - 最多尝试 `3` 次 - `3` 次全部失败后,本轮任务终止 ### 7.6 小时点位数据查询规则 请求体中的 `tagNameList` 使用 `points.xlsx` 的 `id` 列。 请求体示例: ```json { "type": "1", "tagNameList": [ "points.xlsx 中的 id" ], "energyTypeList": [ "electric power" ], "tb": "0", "startTime": "2026-07-03 17:00:00", "endTime": "2026-07-03 17:59:59", "dateCode": "h" } ``` 查询接口重试规则: - 仅当请求出现 `timeout` 时重试 - 每次 `timeout` 后等待 `10` 秒 - 最多尝试 `3` 次 - 只要任意一次成功,即继续后续 `addpointdatum` 写入和聚合调用 每次调用 HTTP 接口前后都会写入日志,日志包含请求接口、请求参数、响应内容、HTTP 状态码和耗时 `elapsed_ms`。敏感字段如 `password`、`token` 会脱敏。 ### 7.7 响应数据解析规则 优先从响应数据中解析: ```text data.data[*].dataList[*] ``` 若该结构为空,则尝试读取: ```text data.trend[*] ``` 需要获取字段: | 字段 | 用途 | | --- | --- | | `id` | 匹配 Excel 中的 `id` | | `name` | 打印输出 | | `fullTime` | 打印输出,并转换为服务器本地时区时间戳 | | `thisValue` | 打印输出;非 `0` 时作为 `addpointdatum` 的 `value` 写入 | 打印内容包含: ```text name, fullTime, thisValue ``` ### 7.8 addpointdatum 写入规则 接口地址拼接规则: ```text {services.basedataportal}/ai/addpointdatum ``` 请求体为数组,每个元素对应一个 `point_id`。 请求体示例: ```json [ { "point_id": "Excel中的point_id", "data": [ { "ts": 1781770962, "value": "49.97" } ] } ] ``` 字段说明: | 字段 | 说明 | | --- | --- | | `point_id` | 来自 `points.xlsx` 的 `point_id` 列 | | `data[].ts` | 接口响应中的 `fullTime` 按服务器本地时区转换后的秒级时间戳 | | `data[].value` | 接口响应中的 `thisValue`,按字符串写入 | 写入方式:按点位聚合为一个数组后,一次性调用 `addpointdatum` 接口。 若 `thisValue` 为 `0` 或 `0.0`,该点位跳过写入,并记录当前遍历对象内容。 若 `addpointdatum` 响应中的 `state != 0`,本轮任务失败,不继续调用计算聚合接口。 ### 7.9 计算聚合接口调用规则 `addpointdatum` 写入成功后,调用计算聚合接口。 接口地址拼接规则: ```text {services.calcagg}/api/calcagg/calc_agg_points_range ``` 请求体: ```json { "end": 1893528000, "begin": 1893524400, "sync_run": true, "point_ids": [ "Excel中的point_id" ], "operator_id": 1, "operator_name": "jdf-energy-collector" } ``` 字段说明: | 字段 | 说明 | | --- | --- | | `begin` | 上一个整点时间戳,例如 `17:00:00` | | `end` | 当前整点时间戳,例如 `18:00:00` | | `sync_run` | 固定为 `true` | | `point_ids` | 本轮成功写入 `addpointdatum` 的 Excel `point_id` 列 | | `operator_id` | 固定为 `1` | | `operator_name` | 固定为 `jdf-energy-collector` | 响应处理规则: - 等待接口响应 - 若响应中的 `state != 0`,打印完整响应内容 - 调用前后写入日志,包含请求体、响应体、HTTP 状态码和耗时 `elapsed_ms` ### 7.10 运行方式 安装依赖: ```bash poetry install ``` 常驻运行: ```bash poetry run python main.py --config config.yaml ``` ### 7.11 Python 依赖 程序依赖: ```text requests openpyxl PyYAML ```