feat: 初始化设计文档草稿

- SPEC.md: 项目概览与文档索引
- docs/architecture.md: 系统架构设计
- docs/api.md: 后台 API 接口设计
- docs/database.md: 数据库表结构设计
- docs/h5.md: H5 页面结构与交互设计
- docs/offline.md: 离线数据方案

状态: 设计中,待设备API协议确认后细化
This commit is contained in:
2026-04-14 11:25:17 +08:00
commit 86777f585e
6 changed files with 1165 additions and 0 deletions

294
docs/api.md Normal file
View File

@@ -0,0 +1,294 @@
# 后台 API 接口设计
> 状态: 设计中(待设备 API 协议确认后细化)
---
## 1. API 概览
- **Base URL**: `https://api.example.com/v1`
- **认证**: `Authorization: Bearer <jwt_token>`
- **数据格式**: JSON
- **字符编码**: UTF-8
---
## 2. 认证接口
### 2.1 登录
```
POST /auth/login
```
**请求体**:
```json
{
"username": "string",
"password": "string"
}
```
**响应** (200):
```json
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"expires_at": "2026-04-15T11:00:00Z",
"user": {
"id": 1,
"username": "admin",
"role": "admin"
}
}
```
---
## 3. 设备接口
### 3.1 获取设备台账列表
```
GET /devices
```
**Query 参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| type | string | 设备类型: `tower_crane` / `elevator` / 空(全部) |
| status | string | 在线状态: `online` / `offline` / 空(全部) |
| page | int | 页码,默认 1 |
| page_size | int | 每页数量,默认 20 |
**响应** (200):
```json
{
"total": 100,
"page": 1,
"page_size": 20,
"items": [
{
"id": "device_001",
"name": "1号塔吊",
"type": "tower_crane",
"model": "QTZ500",
"location": "A区施工现场",
"status": "online",
"last_seen": "2026-04-14T10:30:00Z",
"install_date": "2026-01-15"
}
]
}
```
### 3.2 获取设备实时数据
```
GET /devices/{device_id}/realtime
```
**响应** (200):
```json
{
"device_id": "device_001",
"timestamp": "2026-04-14T10:30:00Z",
"data": {
// 塔吊
"load": 45.2, // 当前载重 (kN)
"range": 30.5, // 回转幅度 (m)
"height": 85.0, // 起升高度 (m)
"wind_speed": 5.2, // 风速 (m/s)
"torque": 320.5, // 力矩 (kN·m)
"angle": 120.5, // 回转角度 (°)
// 升降机
"speed": 1.2, // 升降速度 (m/s)
"floor": 12, // 当前楼层
"door_status": "closed"
}
}
```
### 3.3 获取设备历史数据
```
GET /devices/{device_id}/history
```
**Query 参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| start | datetime | 开始时间 ISO8601 |
| end | datetime | 结束时间 ISO8601 |
| metric | string | 指标字段名(可选,默认全量) |
| page | int | 页码 |
| page_size | int | 每页数量,默认 100 |
**响应** (200):
```json
{
"device_id": "device_001",
"total": 1000,
"items": [
{
"timestamp": "2026-04-14T10:00:00Z",
"load": 44.8,
"range": 30.2,
"height": 84.8
}
]
}
```
---
## 4. 预警接口
### 4.1 获取预警列表
```
GET /alerts
```
**Query 参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| device_id | string | 设备ID可选 |
| level | string | 预警级别: `warning` / `danger` / 空(全部) |
| status | string | 处理状态: `unread` / `handled` / `ignored` / 空(全部) |
| page | int | 页码 |
| page_size | int | 每页数量,默认 20 |
**响应** (200):
```json
{
"total": 50,
"unread_count": 3,
"items": [
{
"id": "alert_001",
"device_id": "device_001",
"device_name": "1号塔吊",
"level": "danger",
"message": "载重超限: 当前45.2kN超过额定值40kN",
"metric": "load",
"value": 45.2,
"threshold": 40.0,
"created_at": "2026-04-14T10:25:00Z",
"status": "unread"
}
]
}
```
### 4.2 获取预警详情
```
GET /alerts/{alert_id}
```
### 4.3 处理预警
```
POST /alerts/{alert_id}/handle
```
**请求体**:
```json
{
"action": "handled",
"note": "已现场确认,正常作业"
}
```
### 4.4 忽略预警
```
POST /alerts/{alert_id}/ignore
```
---
## 5. OSS 文件接口
### 5.1 获取上传凭证(预签名 URL
```
POST /oss/upload-token
```
**请求体**:
```json
{
"filename": "report_2026_04.pdf",
"content_type": "application/pdf"
}
```
**响应** (200):
```json
{
"upload_url": "https://bucket.oss-cn-beijing.aliyuncs.com/...",
"object_key": "reports/2026/04/report_xxx.pdf",
"expires_at": "2026-04-14T11:00:00Z"
}
```
### 5.2 获取文件下载链接
```
GET /oss/download-url
```
**Query 参数**:
| 参数 | 类型 | 说明 |
|------|------|------|
| object_key | string | OSS 对象路径 |
**响应** (200):
```json
{
"download_url": "https://bucket.oss-cn-beijing.aliyuncs.com/...?Signature=...",
"expires_at": "2026-04-14T12:00:00Z"
}
```
---
## 6. 通用响应格式
**成功**:
```json
{
"code": 0,
"message": "success",
"data": {}
}
```
**错误**:
```json
{
"code": 1001,
"message": "设备不存在",
"detail": "device_id: device_999 不在系统中"
}
```
**HTTP 状态码**:
| 状态码 | 说明 |
|--------|------|
| 200 | 成功 |
| 400 | 参数错误 |
| 401 | 未认证 |
| 403 | 无权限 |
| 404 | 资源不存在 |
| 500 | 服务器内部错误 |
---
## 7. 待确认
- [ ] 设备 API 协议格式(塔吊/升降机数据字段)
- [ ] JWT Secret 如何管理
- [ ] 预警推送机制(轮询 vs WebSocket
- [ ] OSS bucket 名称和目录结构

139
docs/architecture.md Normal file
View File

@@ -0,0 +1,139 @@
# 系统架构设计
> 状态: 设计中
---
## 1. 系统架构图
```
┌─────────────────────────────────────────┐
│ 阿里云 OSS │
│ (文件存储: 证书/报告/日志) │
└───────────────┬─────────────────────────┘
│ upload
┌──────────┐ ┌─────────────────────────┐ ┌──────────────┐
│ 已有API │───▶│ 后台 API 服务 │◀───│ H5 客户端 │
│ (塔吊/ │ │ Python / FastAPI │ │ (移动端浏览器)│
│ 升降机) │ │ │ │ │
└──────────┘ │ ┌───────────────────┐ │ └──────────────┘
│ │ 设备数据接入层 │ │ ▲
│ │ 预警引擎 │ │ │ polling / websocket
│ │ OSS 文件管理 │ │
│ │ REST API │ │
│ └───────────────────┘ │ │
└────────────┬────────────┘ │
│ │
▼ │
┌─────────────────────────┐ │
│ PostgreSQL │ │
│ (设备台账/预警记录/ │◀────────────┘
│ 用户/OSS文件索引) │
└─────────────────────────┘
```
---
## 2. 模块划分
### 2.1 后台 API 服务
**职责**:数据聚合层,统一对接已有设备 API屏蔽异构数据源差异对 H5 提供标准化 REST API。
| 模块 | 说明 |
|------|------|
| `device_adapter` | 设备 API 适配器:塔吊协议解析、升降机协议解析 |
| `monitor` | 设备状态监控:定时轮询已有 API写入本地 DB |
| `alert_engine` | 预警引擎:阈值判断、预警生成、通知分发 |
| `file_mgr` | OSS 文件管理:上传、下载、索引 |
| `api_routes` | REST API 路由设备台账、实时数据、历史查询、预警、OSS |
### 2.2 H5 客户端
**职责**:移动端展示与操作界面,基于响应式 H5 开发,适配手机浏览器。
| 模块 | 说明 |
|------|------|
| `dashboard` | 首页仪表盘:设备总数、在线/离线、告警统计 |
| `device_list` | 设备列表:塔吊/升降机分类展示 |
| `device_detail` | 设备详情:实时数据面板、历史曲线 |
| `alert_list` | 预警列表:未读/已处理筛选 |
| `alert_detail` | 预警详情:处理/忽略操作 |
| `profile` | 个人中心:账号信息、通知设置 |
---
## 3. 数据流向
### 3.1 设备数据采集
```
已有设备 API
│ (定时轮询: 每 30s 或设备主动推送)
device_adapter (协议解析)
monitor (写入 DB + 触发预警判断)
├──────────────────┐
▼ ▼
PostgreSQL alert_engine
(设备状态表) (阈值判断)
alert_records (预警记录表)
通知分发 (站内/OSS记录)
```
### 3.2 H5 数据请求
```
H5 浏览器
│ GET /api/devices
│ GET /api/devices/{id}/realtime
│ GET /api/alerts
api_routes (FastAPI)
├──────────────┐
▼ ▼
PostgreSQL OSS
(结构化数据) (文件URL)
```
---
## 4. 部署架构
MVP 阶段采用轻量部署:
| 组件 | 部署方式 |
|------|----------|
| 后台 API | 阿里云函数计算FC或轻量服务器 |
| PostgreSQL | 阿里云 RDS 或轻量服务器自建 |
| OSS | 阿里云 OSS 标准存储 |
---
## 5. 安全设计
| 措施 | 说明 |
|------|------|
| HTTPS | 全链路 HTTPS证书由阿里云管理 |
| Token 认证 | H5 API 请求携带 JWT Token |
| CORS | 限制允许的 H5 域名 |
| OSS 鉴权 | H5 通过后台签发临时 URL 访问 OSS 文件 |
| 输入校验 | FastAPI Pydantic 模型全量参数校验 |
---
## 6. 待细化
- [ ] 设备 API 协议细节(目前尚未获取,需跟进沟通)
- [ ] 预警阈值配置方案(管理员界面 or 配置文件)
- [ ] 实时通知方案WebSocket / SSE / 轮询)
- [ ] 多租户隔离方案(目前 MVP 单租户设计)

194
docs/database.md Normal file
View File

@@ -0,0 +1,194 @@
# 数据库表结构设计
> 状态: 设计中MVP 版本PostgreSQL
---
## 1. ER 图概述
```
users ─────────┐
│ 1:N
user_device_access (N:M)
devices ───────┤
├──── 1:N ──── device_realtime (最新状态)
├──── 1:N ──── device_history (历史数据)
└──── 1:N ──── alert_records (预警记录)
alert_records ────── N:1 ──── users (处理人)
oss_files ──────────────────────────────────────────────────┐
(其他模块按需关联) │
```
---
## 2. 表结构
### 2.1 users用户表
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| id | SERIAL | PRIMARY KEY | 自增ID |
| username | VARCHAR(64) | UNIQUE NOT NULL | 用户名 |
| password_hash | VARCHAR(256) | NOT NULL | bcrypt 密码哈希 |
| role | VARCHAR(32) | NOT NULL DEFAULT 'viewer' | 角色: admin / operator / viewer |
| real_name | VARCHAR(128) | | 真实姓名 |
| phone | VARCHAR(32) | | 手机号 |
| is_active | BOOLEAN | DEFAULT true | 是否启用 |
| created_at | TIMESTAMP | DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMP | DEFAULT NOW() | 更新时间 |
**索引**:
- `idx_users_username` ON (`username`)
---
### 2.2 devices设备台账表
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| id | VARCHAR(64) | PRIMARY KEY | 设备ID对接已有API的设备编号 |
| name | VARCHAR(128) | NOT NULL | 设备名称,如 "1号塔吊" |
| type | VARCHAR(32) | NOT NULL | 类型: `tower_crane` / `elevator` |
| model | VARCHAR(128) | | 设备型号,如 "QTZ500" |
| manufacturer | VARCHAR(128) | | 制造商 |
| location | VARCHAR(256) | | 安装位置,如 "A区施工现场" |
| install_date | DATE | | 安装日期 |
| status | VARCHAR(32) | DEFAULT 'offline' | 在线状态: online / offline |
| last_seen | TIMESTAMP | | 最后在线时间 |
| config | JSONB | DEFAULT '{}' | 设备配置(预警阈值等) |
| created_at | TIMESTAMP | DEFAULT NOW() | 创建时间 |
| updated_at | TIMESTAMP | DEFAULT NOW() | 更新时间 |
**索引**:
- `idx_devices_type` ON (`type`)
- `idx_devices_status` ON (`status`)
---
### 2.3 device_realtime设备实时状态
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| id | SERIAL | PRIMARY KEY | 自增ID |
| device_id | VARCHAR(64) | FOREIGN KEY → devices(id) | 设备ID |
| timestamp | TIMESTAMP | NOT NULL | 数据时间戳 |
| raw_data | JSONB | NOT NULL | 原始数据快照 |
| parsed_data | JSONB | NOT NULL | 解析后的结构化数据 |
| created_at | TIMESTAMP | DEFAULT NOW() | 写入时间 |
**索引**:
- `idx_realtime_device_id` ON (`device_id`)
- `idx_realtime_timestamp` ON (`timestamp` DESC)
> 每次设备数据更新时UPSERT 写入此表,仅保留最新一条记录。
---
### 2.4 device_history设备历史数据
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| id | BIGSERIAL | PRIMARY KEY | 自增ID |
| device_id | VARCHAR(64) | FOREIGN KEY → devices(id) | 设备ID |
| timestamp | TIMESTAMP | NOT NULL | 数据时间戳 |
| metric_name | VARCHAR(64) | NOT NULL | 指标名: load / range / height 等 |
| metric_value | DECIMAL(18,4) | NOT NULL | 指标值 |
| unit | VARCHAR(32) | | 单位: kN / m / m/s 等 |
| created_at | TIMESTAMP | DEFAULT NOW() | 写入时间 |
**索引**:
- `idx_history_device_time` ON (`device_id`, `timestamp` DESC)
- `idx_history_metric` ON (`metric_name`)
- **分区**: 按月分区(`timestamp`按设备ID + 时间范围查询
> 为节省存储空间,历史数据可定期归档到 OSS冷存储DB 仅保留近3个月数据。
---
### 2.5 alert_records预警记录表
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| id | VARCHAR(64) | PRIMARY KEY | 预警IDUUID |
| device_id | VARCHAR(64) | FOREIGN KEY → devices(id) | 设备ID |
| device_name | VARCHAR(128) | | 冗余存储,查询方便 |
| level | VARCHAR(32) | NOT NULL | 级别: warning / danger |
| metric | VARCHAR(64) | NOT NULL | 触发指标: load / wind_speed 等 |
| message | VARCHAR(512) | NOT NULL | 预警消息 |
| value | DECIMAL(18,4) | NOT NULL | 触发值 |
| threshold | DECIMAL(18,4) | NOT NULL | 阈值 |
| status | VARCHAR(32) | DEFAULT 'unread' | 状态: unread / handled / ignored |
| handled_by | INTEGER | FOREIGN KEY → users(id) | 处理人 |
| handle_note | TEXT | | 处理备注 |
| created_at | TIMESTAMP | DEFAULT NOW() | 创建时间 |
| handled_at | TIMESTAMP | | 处理时间 |
**索引**:
- `idx_alerts_device` ON (`device_id`)
- `idx_alerts_status` ON (`status`)
- `idx_alerts_level` ON (`level`)
- `idx_alerts_created` ON (`created_at` DESC)
---
### 2.6 oss_filesOSS 文件索引表)
| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| id | SERIAL | PRIMARY KEY | 自增ID |
| object_key | VARCHAR(512) | UNIQUE NOT NULL | OSS 对象路径 |
| filename | VARCHAR(256) | NOT NULL | 原始文件名 |
| content_type | VARCHAR(128) | | MIME 类型 |
| size | BIGINT | | 文件大小(字节) |
| category | VARCHAR(64) | | 分类: report / certificate / log |
| uploaded_by | INTEGER | FOREIGN KEY → users(id) | 上传人 |
| uploaded_at | TIMESTAMP | DEFAULT NOW() | 上传时间 |
| expires_at | TIMESTAMP | | 过期时间(临时文件) |
**索引**:
- `idx_oss_category` ON (`category`)
- `idx_oss_uploaded_by` ON (`uploaded_by`)
---
## 3. 初始数据
### 3.1 超级管理员
| username | password | role |
|----------|----------|------|
| admin | (首次部署时设置) | admin |
---
## 4. 迁移工具
使用 **Alembic** 管理数据库迁移:
```bash
# 初始化
alembic init alembic
# 生成迁移脚本
alembic revision --autogenerate -m "init schema"
# 执行迁移
alembic upgrade head
```
---
## 5. 待确认
- [ ] 设备历史数据保留策略(多久归档到 OSS
- [ ] 多租户隔离方案MVP 单租户,后续扩展)
- [ ] 预警阈值配置(存 DB 还是配置文件)

299
docs/h5.md Normal file
View File

@@ -0,0 +1,299 @@
# H5 页面结构与交互设计
> 状态: 设计中
---
## 1. 技术选型
| 项目 | 选型 |
|------|------|
| 页面渲染 | 原生 HTML5 + CSS3 + Vanilla JS |
| UI 框架 | 无,响应式布局手工实现 |
| 图表 | EChartsCDN 引入) |
| 字体图标 | 阿里巴巴矢量图标库iconfont |
| 构建 | 无构建步骤,单 HTML 文件按模块拆分 |
**设计原则**
- 移动端优先(适配 375px ~ 414px 宽度)
- 低依赖,不依赖任何 JS 框架
- 离线可缓存核心资产Service Worker
---
## 2. 页面清单
| 页面 | 路由 | 说明 |
|------|------|------|
| 登录 | `/login.html` | 账号密码登录 |
| 首页仪表盘 | `/index.html` | 设备统计、预警统计、快捷入口 |
| 设备列表 | `/devices.html` | 塔吊/升降机分类列表 |
| 设备详情 | `/device.html?id=xxx` | 实时数据 + 历史曲线 |
| 预警列表 | `/alerts.html` | 预警推送列表 |
| 预警详情 | `/alert.html?id=xxx` | 预警详情 + 处理操作 |
| 个人中心 | `/profile.html` | 个人信息、设置 |
---
## 3. 页面结构
### 3.1 首页仪表盘 `/index.html`
```
┌──────────────────────────────────────┐
│ ☰ 郑州智慧工地 [👤 个人中心] │ ← 顶栏
├──────────────────────────────────────┤
│ │
│ ┌────────────┐ ┌────────────┐ │
│ │ 塔吊 │ │ 升降机 │ │ ← 设备数量卡片
│ │ 12 台 │ │ 8 台 │ │
│ │ 12 在线 │ │ 8 在线 │ │
│ └────────────┘ └────────────┘ │
│ │
│ ┌──────────────────────────────┐ │
│ │ 今日预警 │ │ ← 预警统计卡片
│ │ 危险 2 警告 5 │ │
│ └──────────────────────────────┘ │
│ │
│ 最新预警 │
│ ┌──────────────────────────────┐ │
│ │ 🔴 1号塔吊 载重超限 │ │ ← 预警列表最新5条
│ │ 10:25 │ │
│ ├──────────────────────────────┤ │
│ │ 🟡 3号升降机 风速过大 │ │
│ │ 10:20 │ │
│ └──────────────────────────────┘ │
│ │
│ [查看全部预警] │
│ │
└──────────────────────────────────────┘
```
### 3.2 设备列表 `/devices.html`
```
┌──────────────────────────────────────┐
│ ☰ 设备列表 [筛选 ▼] │
├──────────────────────────────────────┤
│ [全部] [塔吊] [升降机] │ ← Tab 切换
├──────────────────────────────────────┤
│ │
│ ┌──────────────────────────────┐ │
│ │ 🏗️ 1号塔吊 🟢 在线 │ │ ← 设备卡片
│ │ QTZ500 | A区施工现场 │ │
│ │ 最后在线: 10:30 │ │
│ └──────────────────────────────┘ │
│ │
│ ┌──────────────────────────────┐ │
│ │ 🏗️ 2号塔吊 ⚫ 离线 │ │
│ │ QTZ500 | B区施工现场 │ │
│ │ 最后离线: 04-13 18:00 │ │
│ └──────────────────────────────┘ │
│ │
└──────────────────────────────────────┘
```
### 3.3 设备详情 `/device.html`
```
┌──────────────────────────────────────┐
│ ← 返回 1号塔吊 │
├──────────────────────────────────────┤
│ │
│ ┌──────────────────────────────┐ │
│ │ 实时数据面板 │ │ ← 关键指标卡片
│ │ 载重 幅度 起升高度 │ │
│ │ 45.2kN 30.5m 85.0m │ │
│ │ │ │
│ │ 风速 力矩 回转角度 │ │
│ │ 5.2m/s 320.5 120.5° │ │
│ │ │ │
│ │ ⚠️ 载重超限 │ │ ← 告警状态
│ └──────────────────────────────┘ │
│ │
│ 历史趋势最近24小时
│ ┌──────────────────────────────┐ │
│ │ 📈 ECharts 折线图 │ │ ← ECharts 图表
│ │ 载重 / 幅度 / 风速 曲线 │ │
│ └──────────────────────────────┘ │
│ │
│ [ 刷新 ] │
│ │
└──────────────────────────────────────┘
```
### 3.4 预警列表 `/alerts.html`
```
┌──────────────────────────────────────┐
│ 预警中心 [筛选 ▼] │
├──────────────────────────────────────┤
│ [全部] [危险] [警告] [已处理] │ ← Tab 筛选
├──────────────────────────────────────┤
│ │
│ ┌──────────────────────────────┐ │
│ │ 🔴 载重超限 │ │ ← 危险预警(红色)
│ │ 1号塔吊 | 2026-04-14 10:25 │ │
│ │ 当前45.2kN超过额定40kN │ │
│ └──────────────────────────────┘ │
│ │
│ ┌──────────────────────────────┐ │
│ │ 🟡 风速过大 │ │ ← 警告预警(黄色)
│ │ 3号升降机 | 2026-04-14 10:20 │ │
│ │ 当前8.5m/s超过阈值6m/s │ │
│ └──────────────────────────────┘ │
│ │
└──────────────────────────────────────┘
```
### 3.5 预警详情 `/alert.html`
```
┌──────────────────────────────────────┐
│ ← 返回 预警详情 │
├──────────────────────────────────────┤
│ │
│ 🔴 危险 │
│ │
│ 载重超限 │
│ │
│ 设备: 1号塔吊QTZ500
│ 位置: A区施工现场 │
│ 时间: 2026-04-14 10:25:00 │
│ │
│ 当前载重: 45.2 kN │
│ 额定载重: 40.0 kN阈值
│ 超限幅度: 13% │
│ │
│ ┌──────────────────────────────┐ │
│ │ 处理方式: │ │
│ │ │ │
│ │ [确认处理] [忽略] │ │ ← 操作按钮
│ │ │ │
│ │ 备注: ____________________ │ │
│ │ │ │
│ │ [提交] │ │
│ └──────────────────────────────┘ │
│ │
└──────────────────────────────────────┘
```
### 3.6 个人中心 `/profile.html`
```
┌──────────────────────────────────────┐
│ ← 返回 个人中心 │
├──────────────────────────────────────┤
│ │
│ ┌──────────┐ │
│ │ 👤 │ │
│ └──────────┘ │
│ 张三(管理员) │
│ 138****1234 │
│ │
│ ┌──────────────────────────────┐ │
│ │ 📊 预警接收设置 │ │
│ │ [ ] 危险预警推送 │ │
│ │ [ ] 警告预警推送 │ │
│ │ [x] 短信通知 │ │
│ └──────────────────────────────┘ │
│ │
│ ┌──────────────────────────────┐ │
│ │ 🔔 消息推送设置 │ │
│ └──────────────────────────────┘ │
│ │
│ ┌──────────────────────────────┐ │
│ │ 🔐 修改密码 │ │
│ └──────────────────────────────┘ │
│ │
│ [退出登录] │
│ │
└──────────────────────────────────────┘
```
---
## 4. 页面交互
### 4.1 API 请求
所有页面通过 `fetch` 调用后台 API基础封装
```javascript
// api.js
const API_BASE = '/v1';
async function api(endpoint, options = {}) {
const token = localStorage.getItem('token');
const resp = await fetch(API_BASE + endpoint, {
...options,
headers: {
'Content-Type': 'application/json',
'Authorization': token ? `Bearer ${token}` : '',
...options.headers,
}
});
if (resp.status === 401) {
location.href = '/login.html';
return;
}
const json = await resp.json();
if (json.code !== 0) {
alert(json.message);
return;
}
return json.data;
}
```
### 4.2 数据加载流程
```
页面加载
检查登录状态localStorage.token
├─ 无 token → 跳转 login.html
└─ 有 token → API 请求
├─ 成功 → 渲染页面
└─ 失败 → 显示错误提示
```
### 4.3 实时数据刷新
- **策略**: 首次加载后,每 **30 秒**自动刷新实时数据
- **实现**: `setInterval` 定时调用 `GET /devices/{id}/realtime`
- **优化**: 仅在设备详情页刷新,其他页面用户主动下拉刷新
### 4.4 离线缓存
使用 **Service Worker** 缓存静态资源:
```javascript
// 缓存策略Network First, Fallback to Cache
// 缓存内容HTML / CSS / JS / iconfont 字体
```
---
## 5. 响应式断点
| 断点 | 宽度 | 适配 |
|------|------|------|
| mobile-s | 320px ~ 374px | 小屏手机 |
| mobile-l | 375px ~ 413px | 主流手机 |
| tablet | 768px ~ 1023px | 平板 |
| desktop | 1024px+ | 桌面端 |
---
## 6. 待确认
- [ ] 预警推送机制(轮询 vs WebSocket
- [ ] 登录 Session 管理方案JWT 有效期)
- [ ] 是否有扫码登录需求
- [ ] 报表导出H5 直接拉 OSS 预签名 URL

191
docs/offline.md Normal file
View File

@@ -0,0 +1,191 @@
# 离线数据方案
> 状态: 设计中MVP 阶段可简化)
---
## 1. 背景
工地现场网络环境不稳定H5 移动端可能出现网络中断。需要保证:
1. **已加载数据可继续浏览**(设备列表、预警等)
2. **网络恢复后数据自动同步**
3. **离线期间的操作不丢失**
---
## 2. 离线存储策略
### 2.1 存储层级
| 层级 | 技术 | 容量 | 适用场景 |
|------|------|------|----------|
| Memory | JavaScript 变量 | 无限 | 页面运行期间 |
| LocalStorage | 浏览器本地存储 | ~5MB | 永久存储(小数据) |
| IndexedDB | 浏览器索引数据库 | ~50MB+ | 结构化数据存储 |
**选型**: 以 **IndexedDB** 为主LocalStorage 辅助。
---
## 3. 离线缓存设计
### 3.1 IndexedDB 表设计
```javascript
// 数据库: SmartProjectDB
// 版本: 1
// 对象存储 1: devices
{
store: 'devices',
keyPath: 'id',
indexes: ['type', 'status'],
data: { id, name, type, model, location, status, last_seen }
}
// 对象存储 2: alerts
{
store: 'alerts',
keyPath: 'id',
indexes: ['status', 'level', 'created_at'],
data: { id, device_id, device_name, level, message, status, created_at }
}
// 对象存储 3: sync_queue离线操作队列
{
store: 'sync_queue',
keyPath: 'id',
autoIncrement: true,
data: { id, action, payload, created_at, status }
}
```
### 3.2 缓存更新策略
| 数据类型 | 更新频率 | 缓存策略 |
|----------|----------|----------|
| 设备列表 | 每 5 分钟 | 后台静默更新 |
| 设备详情 | 每 30 秒 | 页面打开时拉取 + 定时刷新 |
| 预警列表 | 每 1 分钟 | 后台静默更新 + 新预警推送 |
| 预警详情 | 实时 | 打开时拉取 |
**更新流程**:
```
用户打开页面
├─ 有网络 → API 请求 → 更新 IndexedDB → 渲染
└─ 无网络 → 读取 IndexedDB → 渲染(显示"离线模式"
```
---
## 4. 离线操作队列
### 4.1 场景
用户在离线状态下执行操作(如处理预警、提交备注),操作记录存入 `sync_queue`,网络恢复后自动同步。
### 4.2 同步流程
```
检测到网络恢复navigator.onLine 事件)
读取 sync_queuestatus: pending
逐条执行 API 请求
├─ 成功 → 删除队列记录,更新本地数据
└─ 失败 → 重试3次仍失败标记为 failed用户手动处理
```
### 4.3 队列记录格式
```javascript
{
id: 1,
action: 'handle_alert',
payload: {
alert_id: 'alert_001',
action: 'handled',
note: '已现场确认'
},
created_at: '2026-04-14T10:00:00Z',
status: 'pending', // pending / syncing / failed
retry_count: 0
}
```
---
## 5. Service Worker
### 5.1 职责
- 拦截 H5 页面的 HTTP 请求
- 实现缓存策略(静态资源优先缓存,网络优先等)
- 离线时返回缓存的页面
### 5.2 缓存策略
| 资源类型 | 缓存策略 | 说明 |
|----------|----------|------|
| HTML | Network First | 始终尝试获取最新 |
| CSS/JS | Cache First | 优先使用缓存,性能优先 |
| API 数据 | Network Only | 实时性要求高,不缓存 |
| 图标/字体 | Cache First | 长期不变 |
### 5.3 Service Worker 注册
```javascript
// main.js
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(reg => console.log('SW registered'))
.catch(err => console.error('SW registration failed:', err));
}
```
---
## 6. 网络状态提示
页面监听网络状态,给用户明确反馈:
```javascript
// 监听网络状态
window.addEventListener('online', () => {
showToast('网络已恢复,正在同步...');
syncPendingOperations();
});
window.addEventListener('offline', () => {
showToast('网络已断开,显示离线数据');
});
```
---
## 7. 简化方案MVP
MVP 阶段可降低离线复杂度:
| 方案 | 说明 |
|------|------|
| 简单缓存 | 仅在 LocalStorage 缓存用户信息 + 最后设备列表H5 页面可离线展示 |
| 无离线操作 | 离线期间禁用操作按钮,提示"网络异常" |
| 手动刷新 | 网络恢复后用户下拉刷新 |
**后续迭代再实现完整离线能力**
---
## 8. 待确认
- [ ] 是否需要 PWA可安装到桌面/主屏幕)
- [ ] IndexedDB 库选型idb / Dexie.js 还是原生)
- [ ] 离线数据保留时长策略