12451 b553c4e519 feat: API 统一 + 设备详情补全 + H5 导航优化
- API 路由统一: 移除 /api/h5/,统一 /api/pms/ 入口
- 二进制改名: api-server -> pms-api(与服务名一致)
- nginx: 合并 /api/ + /pms/api/ 为 /api/pms/
- 新增 docs/API.md 接口文档
- 后端: CabinetDetail 增加 rssi/pow_fail/address/soc 等字段
- Redis 管道批量读取仓体实时数据(SOC/电压/电流/功率)
- DB: 所有 TIMESTAMP 列改为 DATETIME(修复类型不匹配)
- 用户管理: 新增创建时间/修改时间列,状态改用 Switch
- 角色管理/用户管理: 操作按钮改为图标
- PC 设备详情: 基本信息补全 RSSI/ICCID/电源/地址
- PC 卡片: 通道统计改用 Lucide 图标,颜色统一
- PC 二维码: canvas 渲染,含编号文字,支持右键下载
- H5: 按 uni-app 规范修正导航(navigateTo 替代 reLaunch)
- H5: 详情页加返回导航条
- H5: 扫码支持摄像头(jsQR)+ ?id= 参数
- H5: 底部 tabBar 在所有页面常显
- H5: 首页统计改为 6 项(设备/通道/空闲/充电/充满/故障)
- H5: 下拉刷新 / 上拉加载
- 产品首页: 添加业务联系
- 文档: 清理过时文件,补充缺失规范章节
2026-07-04 04:03:02 +08:00

352 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# API 接口文档
> 充电柜矩阵系统(PMS)接口规范。PC 管理端与 H5 用户端共用同一套 API。
## 基础信息
- **测试环境**:`https://test.anzhizhichong.com/pms/api/`
- **正式环境**:`https://app.anzhizhichong.com/pms/api/`
- **认证方式**:Bearer Token(JWT)
- **响应格式**:`{ "code": 0, "data": ..., "message": "ok" }`
## 统一端点清单
| 方法 | 路径 | 说明 | 权限 |
|------|------|------|------|
| POST | `/api/auth/login` | 登录 | 免认证 |
| GET | `/api/auth/logout` | 登出 | 需认证 |
| GET | `/api/auth/me` | 当前用户信息 | 需认证 |
| GET | `/api/dashboard` | 仪表盘数据 | 需认证 |
| GET | `/api/cabinets` | 设备列表(含 RSSI) | `device:view` |
| POST | `/api/cabinets` | 批量创建柜子 | `device:create` |
| GET | `/api/cabinets/:id` | 设备详情(含仓板+仓体) | `device:view` |
| PUT | `/api/cabinets/:id` | 更新柜子 | `device:edit` |
| DELETE | `/api/cabinets/:id` | 删除柜子 | `device:delete` |
| GET | `/api/cabinets/abstract/:abstract_id` | 扫码查设备(免认证) | 免认证 |
| GET | `/api/cabinets/scan/:abstract_id` | 扫码验证(需认证) | 需认证 |
| GET | `/api/cabinets/filter` | 设备列表(管理端) | 需认证 |
| GET | `/api/cabinets/realtime-status` | 设备实时状态 | 需认证 |
| GET | `/api/compartments/:id` | 仓体/通道详情 | 需认证 |
| POST | `/api/charge/start` | 开始充电 | `charge:start` |
| POST | `/api/charge/stop` | 停止充电 | `charge:stop` |
| POST | `/api/door/open` | 开门 | `device:door:unlock` |
| GET | `/api/organizations` | 组织列表 | `org:view` |
| POST | `/api/organizations` | 创建组织 | `org:create` |
| GET | `/api/organizations/:org_id/projects` | 项目列表 | 需认证 |
| GET | `/api/charge-records` | 充电记录 | `charge_record:view` |
| GET | `/api/energy-stats/summary` | 能耗统计 | 需认证 |
---
## 认证接口
### POST /api/auth/login
**请求:**
```json
{
"phone": "13800138000",
"password": "admin123"
}
```
**响应:**
```json
{
"code": 0,
"data": {
"token": "eyJ0eXAi...",
"user": {
"id": 1,
"name": "管理员",
"phone": "13800138000",
"role_level": 2,
"organization_id": null,
"permissions": ["device:view", "device:create", "..."]
}
},
"message": "登录成功"
}
```
**角色等级:** 1=普通用户,2=超级管理员
---
## H5 专用接口
### GET /api/dashboard
H5 首页仪表盘数据。支持 `org_id` 参数按组织过滤。
**响应:**
```json
{
"code": 0,
"data": {
"online_cabinets": 68,
"charging_count": 214,
"idle_channels": 722,
"fault_channels": 0,
"full_count": 0,
"today_charge_count": 0,
"today_energy_kwh": 0,
"today_charge_hours": 0,
"alerts": [],
"projects": [
{ "id": 1, "name": "无锡太湖新城换电站", "cabinet_count": 2, "charging": 16, "idle": 56, "fault": 0 }
]
}
}
```
### GET /api/cabinets
设备列表,支持 `project_id` 和 `org_id` 过滤。响应包含实时 RSSI。
**响应字段:**
```json
{
"id": 1,
"abstract_id": "10-00000001",
"name": "太湖新城换电柜A",
"status": 1,
"project_id": 1,
"rssi": 85,
"channel_count": 36,
"idle_count": 30,
"charging_count": 6,
"full_count": 0,
"fault_count": 0
}
```
### GET /api/cabinets/abstract/:abstract_id
扫码入口,免认证。通过二维码中的抽象ID查找设备。
**响应:**
```json
{
"code": 0,
"data": {
"id": 1,
"abstract_id": "10-00000001",
"imei": "864720562557021",
"iccid": "8986012520983901841",
"name": "太湖新城换电柜A",
"status": 1,
"cabin_boards": [
{
"id": 1,
"board_id": 1,
"status": 0,
"compartments": [
{ "id": 1, "channel_id": 1, "status": 0 }
]
}
]
}
}
```
### GET /api/compartments/:id
仓体/通道详情,含实时 BMS 数据和充电记录。
**响应字段:**
- `voltage` — mV(整数)
- `current` — mA(整数)
- `power` — mW(整数)
- `energy` — Wh(整数)
- `soc` — %(0-100)
- `soh` — %(0-100)
- `cell_voltages` — mV 数组
### POST /api/charge/start
**请求:** `{ "compartment_id": 123 }`
### POST /api/charge/stop
**请求:** `{ "compartment_id": 123 }`
### POST /api/door/open
**请求:** `{ "compartment_id": 123 }`
### GET /api/cabinets/scan/:abstract_id
扫码验证,需认证。验证当前用户是否有权访问该设备。
---
## 数据单位规范
**核心原则:所有数值字段使用整数存储,禁止浮点类型 (f32/f64/DECIMAL/FLOAT)**
| 字段 | 类型 | 单位 | 说明 |
|------|------|------|------|
| `energy` | i64 | **Wh** | 充电量,不存小数 |
| `voltage` | i64 | **mV** | 电池组总电压 |
| `current` | i64 | **mA** | 电流,正=充电 负=放电 |
| `power` | i64 | **mW** | 功率 |
| `soc` / `soh` | i64 | **%** | 电量百分比 / 健康度 |
| `max_temperature` | i64 | **0.1°C** | 25.3°C → `253` |
| `rssi` | i64 | **dBm** | 信号强度 |
**温度转换:** 前端显示时除以10加小数点(25.3°C → `253`)
---
## TCP 设备通讯协议
### 通信规则
- TCP 长连接,无需 PING 包
- 空闲 1 分钟上报,充电 15 秒上报
- 超时 5000ms
- JSON 去空格回车省流量,LF 处理粘包
- `dev_id`:IMEI(10进制),`sub_device_id`:485 ID
- `msg_id`:自增会话 ID
- 全局通道号:`dev_id-sub_device_id-通道号`(如 `123456789012345-01-6`)
### 签名机制
- 设备首次连接调 `auth_str` 接口获取8位安全码(仅一次)
- `sign = SHA256(dev_id + auth_str + timestamp).substring(56, 63)`
- 时间戳秒级,平台校验±60秒
### 指令清单
| 动作 | 方向 | id格式 | 说明 |
|------|------|--------|------|
| `login` | 设备→平台 | 无 | 登录(含sign+timestamp) |
| `auth_str` | 设备→平台 | 无 | 获取安全码(仅首次) |
| `status_post` | 设备→平台 | 无 | 定时上报(rssi+status字典) |
| `pow_fail` | 设备→平台 | 无 | 电源掉电上报 |
| `bat_in` | 设备→平台 | `IMEI-仓控板-通道` | 电池插入 |
| `bat_out` | 设备→平台 | `IMEI-仓控板-通道` | 电池拔出 |
| `on` | **双向** | `IMEI-仓控板-通道` | 开始充电 |
| `off` | **双向** | `IMEI-仓控板-通道` | 停止供电 |
| `open` | 平台→设备 | `IMEI` | 开门 |
| `pow_on` | 平台→设备 | `IMEI` | 接触器合闸 |
| `pow_off` | 平台→设备 | `IMEI` | 接触器分闸 |
| `reset` | 平台→设备 | `IMEI` 或 `IMEI-仓控板` | 重启整柜或仓控板 |
### 指令详情
#### login(设备→平台)
```json
{"act":"login","msg_id":1,"dev_id":"123456789012345","sign":"abc12345","timestamp":1719800000,"h_ver":"1.0.0","s_ver":"1.0.0"}
// 响应:{"suc":1,"msg_id":1,"cell":6}
```
#### auth_str(设备→平台)
```json
{"act":"auth_str","msg_id":2,"dev_id":"123456789012345","ccid":"89860000000000000000"}
// 响应:{"suc":1,"msg_id":2,"data":{"auth_str":"abcd1234"}}
```
#### status_post(设备→平台)
```json
{"act":"status_post","msg_id":10,"dev_id":"123456789012345","rssi":85,
"status":{"123456789012345-01":"000100020003000400050006"}}
// 响应:{"suc":1,"msg_id":10}
```
#### bat_in / bat_out(设备→平台)
```json
{"act":"bat_in","msg_id":20,"id":"123456789012345-01-6","bms":{"soc":45,"voltage":52.3}}
{"act":"bat_out","msg_id":21,"id":"123456789012345-01-6"}
```
#### on / off(双向)
```json
// 平台→设备:开始充电
{"act":"on","msg_id":30,"id":"123456789012345-01-6"}
// 设备→平台:充电开始确认
{"act":"on","msg_id":30,"id":"123456789012345-01-6"}
```
#### open(平台→设备)
```json
{"act":"open","msg_id":40,"id":"123456789012345"}
// 错误码63 = 门锁故障
```
#### reset(平台→设备)
```json
// 重启整柜
{"act":"reset","msg_id":60,"id":"123456789012345"}
// 重启仓控板
{"act":"reset","msg_id":61,"id":"123456789012345-01"}
```
### 充电结束原因
| 平台收到 | 判断原因 |
|---------|---------|
| `off` (设备→平台) | `normal` — 正常充满 |
| `off` (平台→设备) | `user_stop` — 用户手动停止 |
| `bat_out` | `normal` — 电池拔出 |
| `bat_in` (通道已有电池) | `forced` — 新电池插入强制结束 |
| 超时未响应 | `timeout` — 通信超时 |
---
## Redis 实时状态存储
### 层级结构
```
device:{imei} # 设备级 Hash
├─ online = "1" # 在线标记(TTL 300秒)
├─ rssi = "85" # 信号强度
├─ pow_fail_dc = "0/1" # 直流停电
└─ device:{imei}:board:{board_idx} # 板级 Hash
├─ status_hex = "00010002..." # 原始hex状态
└─ device:{imei}:board:{board_idx}:ch:{0-5} # 通道级 Hash
├─ on = "0/1" # 充电中
├─ full = "0/1" # 充满
├─ fault = "0/1" # 故障
└─ hex = "00010002" # 原始hex
```
### BMS 字段
| 字段 | 类型 | 单位 | 说明 |
|------|------|------|------|
| `soc` | i64 | % | 电池电量百分比 |
| `soh` | i64 | % | 电池健康度百分比 |
| `voltage` | i64 | mV | 电池组总电压 |
| `current` | i64 | mA | 电流 |
| `power` | i64 | mW | 功率 |
| `energy` | i64 | Wh | 累计充电量 |
| `cell_voltages` | i64[] | mV | 单体电芯电压数组 |
---
## 权限码列表
| 权限码 | 说明 |
|--------|------|
| `device:view` | 查看设备 |
| `device:create` | 创建设备 |
| `device:edit` | 编辑设备 |
| `device:delete` | 删除设备 |
| `device:door:unlock` | 开门 |
| `charge:start` | 开始充电 |
| `charge:stop` | 停止充电 |
| `charge_record:view` | 查看充电记录 |
| `org:view` | 查看组织 |
| `org:create` | 创建组织 |
| `org:edit` | 编辑组织 |