Skip to content

Latest commit

 

History

History
272 lines (208 loc) · 7.59 KB

File metadata and controls

272 lines (208 loc) · 7.59 KB

数据库结构

users
 └── ninebot_accounts  (一对多:一个用户可绑定多个九号账号)
      └── devices       (一对多:一个账号对应多台设备)
           └── device_snapshots  (一对多:每次采集追加一行快照)

ninebot_accounts

字段 类型 说明
user_id FK 关联本系统用户
username string 九号账号用户名
password text (encrypted) 九号账号密码(AES 加密存储)
ninebot_uuid string 九号平台用户 UUID
phone string 手机号
email string 邮箱
avatar string 头像 URL
access_token text 九号 access token
refresh_token text 九号 refresh token
access_token_validity bigint access token 过期时间(毫秒时间戳)
region string 区域(如 bj
enabled boolean 账号是否启用

devices

字段 类型 说明
ninebot_account_id FK 关联九号账号
sn string 设备序列号(unique per account)
device_name string 设备名称
img string 设备图片 URL

device_snapshots

字段 类型 说明
device_id FK 关联设备
gsm smallint 信号强度(0–31)
gsm_time int 信号采集时间(Unix 时间戳)
pwr tinyint 电源开关状态(0/1)
dump_energy tinyint 剩余电量(%)
estimate_mileage decimal(8,2) 预计续航里程(km)
location_desc string 位置文字描述
charging_state tinyint 充电状态(0=未充电)
power_status tinyint 车辆状态(0=锁车)
remain_charge_time string 剩余充电时间

API 文档

所有接口(除注册/登录外)均需在请求头携带:

Authorization: Bearer <access_token>

认证

注册

POST /api/register
Content-Type: application/json

{
    "name": "张三",
    "email": "zhang@example.com",
    "password": "password123",
    "password_confirmation": "password123"
}

响应 201

{
    "session": {
        "token_type": "bearer",
        "access_token": "eyJ...",
        "expires_in": 3600,
        "user": {
            "id": 1,
            "name": "张三",
            "email": "zhang@example.com"
        }
    }
}

登录

POST /api/login
Content-Type: application/json

{
    "email": "zhang@example.com",
    "password": "password123"
}

获取当前用户

GET /api/me
Authorization: Bearer <token>

刷新 Token

POST /api/refresh
Authorization: Bearer <token>

登出

POST /api/logout
Authorization: Bearer <token>

九号账号绑定

绑定账号

调用九号登录接口验证后,将凭证与账号信息存入数据库(密码加密存储)。

POST /api/ninebot-accounts
Authorization: Bearer <token>
Content-Type: application/json

{
    "username": "shine09",
    "password": "your_ninebot_password"
}

响应 201

{
    "ninebot_account": {
        "id": 1,
        "username": "shine09",
        "ninebot_uuid": "1148401935171325952",
        "phone": "139****7804",
        "avatar": "https://avatar-cn.ninebot.com/...",
        "region": "bj",
        "enabled": true,
        "created_at": "2026-03-13T06:28:53.000000Z",
        "updated_at": "2026-03-13T06:28:53.000000Z"
    }
}

同一账号名重复绑定时,会更新已有记录(upsert)。

列出绑定账号

GET /api/ninebot-accounts
Authorization: Bearer <token>

解绑账号

DELETE /api/ninebot-accounts/{id}
Authorization: Bearer <token>

响应 204(No Content)。


设备管理

同步并获取设备列表

调用九号设备列表接口,将返回的设备写入/更新 devices 表,然后返回。

GET /api/ninebot-accounts/{ninebotAccountId}/devices
Authorization: Bearer <token>

响应 200

{
    "devices": [
        {
            "id": 1,
            "ninebot_account_id": 1,
            "sn": "N9DFDxxxxxxxxx",
            "device_name": "新国飙",
            "img": "https://oms-oss-public.ninebot.com/..."
        }
    ]
}

错误响应

错误体风格:

{
    "errors": [
        {
            "type": "invalid_request_error",
            "code": "ninebot_login_failed",
            "message": "Invalid username or password."
        }
    ]
}
HTTP 状态码 场景
401 未携带 Token 或 Token 无效
403 操作不属于自己的资源
404 设备不存在或不属于当前用户
422 参数校验失败,或九号接口返回非成功状态

前端图表说明

电量趋势图数值标签显示逻辑

前端设备页中的电量趋势图(frontend/src/components/devices/battery-trend-chart.tsx)在显示折线数值标签时,遵循以下规则:

  • 仅在开启“显示数值标签”时渲染标签。
  • 按折线数据顺序逐点判断。
  • 当前点数值如果与“上一个已显示标签的数值”相同,则不重复显示。
  • 当前点数值如果与“上一个已显示标签的数值”不同,则显示一次。
  • 非法值或小于等于 0 的值不显示标签。

也就是说,标签逻辑不是“只显示最高点和最低点”,而是“相邻重复值去重后,每个变化过的数值显示一次”。

Tooltip 与交互说明

  • Tooltip 跟随横轴高亮线显示当前时间点的数据。
  • Tooltip 会展示当前时间点、状态说明,以及当前视图对应的百分比值。
  • 为避免频繁触发埋点,Tooltip 对外追踪回调做了节流处理。
  • Tooltip 使用 portal 渲染,避免在图表最左侧或最右侧因为容器裁切而不可见。

虚拟实时点说明

  • 当存在实时电量且当前时间不在整点时,图表尾部会补一个“虚拟实时点”。
  • 该点用于表达“当前时刻”的实时电量,不代表接口返回的一条整点历史记录。
  • 虚拟点会在图上显示 实时 标记,并在 Tooltip 中标识为“实时(虚拟)”。

坐标轴说明

  • 横轴表示时间。
  • 纵轴表示百分比(%)。
  • soc 视图中,纵轴表示当前剩余电量百分比。
  • discharge 视图中,纵轴表示每段时间内的耗电百分比。
  • charge 视图中,纵轴表示每段时间内的充入百分比。
  • 纵轴刻度会根据当前区间动态收敛,避免在小范围波动时显示过密的刻度标签。