feat: init iot-util project with OneNET API integration

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-03 11:09:41 +08:00
co-authored by Claude Opus 4.7
commit 69e13f504f
36 changed files with 2610 additions and 0 deletions
+134
View File
@@ -0,0 +1,134 @@
# iot-util 项目开发规范
## 本地测试
- **规则**: 不进行本地启动测试,由用户自行在 IDEA 中运行验证。
- **约束**: 严禁使用任何方式启动或停止本地 Java 进程。
---
## 项目概述
IoT 设备管理工具,对接中国移动 OneNET 平台(iot-api.heclouds.com),提供产品详情、设备列表、设备属性、物模型、数据点等查询功能,并附带 Web 管理界面。
- **框架**: Solon 3.10.5
- **认证**: Sa-Token 1.44.0(登录鉴权)+ HMAC-SHA1IoT 平台鉴权)
- **HTTP 客户端**: OkHttp 4.12.0
- **模板引擎**: FreeMarker
- **JSON**: Jacksonsolon-serialization-jackson 3.10.5
- **端口**: 8081app.yml 配置)
---
## 目录结构
```
src/main/java/com/yixiong/iot/
├── App.java # 启动类
├── config/
│ └── IotProperties.java # IoT 配置属性(对应 app.yml iot.* 节点)
├── controller/
│ ├── AuthController.java # 登录/登出页面路由
│ ├── PageController.java # 主页路由(注入 defaultProductId 到模板)
│ ├── ProductController.java # GET /product/detail
│ ├── DeviceController.java # GET /device/list|detail|event-log|datapoints
│ ├── ThingModelController.java # GET|POST /thingmodel/*
│ └── GlobalExceptionFilter.java # 全局异常过滤,统一返回 JSON
├── service/
│ ├── ProductService.java
│ ├── DeviceService.java
│ └── ThingModelService.java
├── model/
│ ├── IotResponse.java # 通用响应包装(code/msg/requestId/data
│ ├── OldIotResponse.java # 旧版响应(errno/error/data,已备用)
│ ├── ProductDetailData.java
│ ├── DeviceItem.java # 设备列表项 & 设备详情复用
│ ├── DeviceListData.java
│ ├── DeviceEventItem.java
│ ├── DeviceEventListData.java
│ ├── DevicePropertyItem.java # 设备属性最新数据
│ ├── PropertyHistoryData.java # 属性历史包装(data.list
│ ├── PropertyHistoryItem.java
│ ├── DatapointData.java # 数据点(含内部类 DatapointStream/DatapointItem
│ └── ThingModelData.java # 物模型(含内部类 Property/Event/Service
└── util/
├── IotHttpClient.java # OkHttp 封装,HMAC 鉴权,正常响应 DEBUG 日志
└── IotTokenUtil.java # HMAC-SHA1 Token 生成
src/main/resources/
├── app.yml
└── templates/
├── login.ftl # 登录页
└── index.ftl # 主页(产品详情 + 设备列表 Tab,多个弹窗)
```
---
## API 路由一览
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/` | 主页(需登录) |
| GET | `/auth/login` | 登录页 |
| POST | `/auth/doLogin` | 登录处理 |
| GET | `/auth/logout` | 登出 |
| GET | `/product/detail?product_id=` | 产品详情 |
| GET | `/device/list` | 设备列表(product_id 取配置默认值) |
| GET | `/device/detail?device_name=` | 设备详情(product_id 取配置默认值) |
| GET | `/device/event-log` | 设备事件记录 |
| GET | `/device/datapoints` | 设备历史数据点 |
| GET | `/thingmodel/query-thing-model` | 产品物模型(product_id 取配置默认值) |
| GET | `/thingmodel/query-device-property?device_name=` | 设备属性最新数据 |
| POST | `/thingmodel/query-device-property-detail` | 设备属性详情(下发命令) |
| GET | `/thingmodel/query-property-history` | 设备属性历史记录(data 为 Object,结构不固定) |
---
## 配置说明(app.yml
```yaml
iot:
auth:
version: "2022-05-01"
resource-name: "userid/459810" # IoT 平台用户 ID
access-key: "..." # HMAC 密钥(Base64
signature-method: "sha1"
expiration-seconds: 3600
http:
base-url: "https://iot-api.heclouds.com" # 新版 APIHMAC 鉴权)
default-product-id: "VGi8wC99jO" # 默认产品 ID,多数接口使用此值
old-base-url: "https://api.heclouds.com" # 旧版 APIapi-key 鉴权,备用)
api-key: "..." # 旧版 Master Key
auth:
username: admin
password: ... # 登录密码
```
---
## 关键设计决策
### IoT 平台鉴权
- 新版接口(`iot-api.heclouds.com`)使用 HMAC-SHA1 Token,放在 `Authorization` 请求头
- Token 格式:`version=...&res=...&et=...&method=sha1&sign=...`(查询字符串格式)
- 每次请求实时生成 Token,避免过期
### HTTP 客户端
- `IotHttpClient.execute()` 不对非 2xx 状态码抛异常,始终返回响应体
- 正常响应打 DEBUG 日志,非 2xx 打 WARN,网络异常打 ERROR
- 全局日志级别 WARN,框架 INFO 日志屏蔽
### 响应反序列化
- 各 Service 使用独立 `ObjectMapper`,配置 `SNAKE_CASE` 命名策略 + 忽略未知字段
- 属性历史接口(`/thingmodel/query-property-history`)的 `data` 结构不固定,反序列化为 `Object` 透传
### 前端页面
- 纯原生 HTML/CSS/JS + FreeMarker,无第三方 UI 框架
- 主页两个 Tab:产品详情、设备列表;页面加载后自动触发两个查询
- 设备列表每行操作按钮:详情、数据点、事件记录、属性(均以弹窗展示)
- 属性弹窗支持二级跳转:属性列表 → 历史记录弹窗
- 产品详情页有"物模型"按钮,展示属性/事件/服务功能点
### 已知注意事项
- `DeviceDetailData.java``OldIotResponse.java` 为旧版 API 遗留,当前设备详情已改用新版接口(`/device/detail`),这两个类暂未删除
- `ThingModelData.ThingModelService` 内部类与 Spring 的 `@Service` 无关,是物模型的服务功能点 DTO