405 lines
13 KiB
Markdown
405 lines
13 KiB
Markdown
# 授权文件功能演示说明
|
||
|
||
## 📸 功能界面说明
|
||
|
||
由于这是文本环境,以下用文字描述各功能的界面和操作流程。
|
||
|
||
## 1. 授权管理主页面
|
||
|
||
**访问路径**: http://localhost:3000/licenses
|
||
|
||
### 页面布局
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ ← 授权管理 [导出] [选择授权项] │
|
||
│ 管理设备授权许可 │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ ℹ️ 每个设备型号对应一套授权模块配置... │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ 筛选条件: [设备型号▼] [查询] │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ 设备型号 | 授权模块 | 操作 │
|
||
│ GD-30 | [一维] [二维] [三维] | [预览] [下载] [编辑] │
|
||
│ GD-20 | [一维] [二维] | [预览] [下载] [编辑] │
|
||
├─────────────────────────────────────────────────────────────┤
|
||
│ 共 2 条 [<] [1] [2] [>] │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 操作按钮说明
|
||
|
||
- **预览** 👁️ - 点击后弹出JSON预览窗口
|
||
- **下载** 📄 - 直接下载JSON文件到本地
|
||
- **编辑** ✏️ - 修改授权配置
|
||
|
||
## 2. 授权文件预览弹窗
|
||
|
||
### 触发方式
|
||
点击列表中的"预览"按钮
|
||
|
||
### 弹窗内容
|
||
|
||
```
|
||
┌──────────────────────────────────────────────────────────┐
|
||
│ 授权文件预览 - GD-30 [X] │
|
||
├──────────────────────────────────────────────────────────┤
|
||
│ { │
|
||
│ "version": "1.0", │
|
||
│ "generatedAt": "2026-04-30T16:00:00.000Z", │
|
||
│ "deviceModel": "GD-30 Supreme", │
|
||
│ "deviceSN": "", │
|
||
│ "validUntil": "2027-04-30", │
|
||
│ "status": "active", │
|
||
│ "authModules": [ │
|
||
│ { │
|
||
│ "id": "1D", │
|
||
│ "name": "一维自电/电阻率/激电测试模块", │
|
||
│ "category": "一维", │
|
||
│ "enabled": true │
|
||
│ }, │
|
||
│ ... │
|
||
│ ], │
|
||
│ "config": { │
|
||
│ "name": "CFG-GD30-v2.1", │
|
||
│ "emissionParams": {...}, │
|
||
│ "acquisitionParams": {...} │
|
||
│ }, │
|
||
│ "signature": { │
|
||
│ "algorithm": "SHA256", │
|
||
│ "value": "a1b2c3d4...", │
|
||
│ "publicKey": "platform-public-key-placeholder" │
|
||
│ } │
|
||
│ } │
|
||
├──────────────────────────────────────────────────────────┤
|
||
│ [下载JSON] [关闭] │
|
||
└──────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 功能特点
|
||
- JSON格式化显示,带语法高亮
|
||
- 可滚动查看完整内容
|
||
- 支持一键下载JSON文件
|
||
- 点击背景或关闭按钮退出
|
||
|
||
## 3. 创建/编辑授权抽屉
|
||
|
||
### 触发方式
|
||
点击"选择授权项"按钮
|
||
|
||
### 抽屉内容
|
||
|
||
```
|
||
┌──────────────────────────────────────┐
|
||
│ 选择授权项 [X] │
|
||
├──────────────────────────────────────┤
|
||
│ 设备型号 │
|
||
│ [GD-30 Supreme ▼] │
|
||
│ │
|
||
│ 授权项目 [全选] [清空]│
|
||
│ ┌──────────────────────────────────┐ │
|
||
│ │ ☑ 分类 │ 名称 │ 说明 │ │
|
||
│ ├──────────────────────────────────┤ │
|
||
│ │ ☑ 一维 │ 一维自电... │ ... │ │
|
||
│ │ ☑ 二维 │ 二维自电... │ ... │ │
|
||
│ │ ☑ 三维 │ 三维自电... │ ... │ │
|
||
│ │ □ 水上 │ 水上... │ ... │ │
|
||
│ │ □ 跨孔 │ 跨孔... │ ... │ │
|
||
│ │ □ 电流场法│ 电流场法... │ ... │ │
|
||
│ └──────────────────────────────────┘ │
|
||
│ │
|
||
│ 已选择 3 项 │
|
||
│ │
|
||
│ ℹ️ 授权文件由选定的授权项与对应型号的 │
|
||
│ 配置文件共同生成... │
|
||
│ │
|
||
│ [保存] │
|
||
└──────────────────────────────────────┘
|
||
```
|
||
|
||
### 交互说明
|
||
- 点击行可选中/取消选中
|
||
- 支持全选/清空快捷操作
|
||
- 保存时自动生成JSON授权文件
|
||
|
||
## 4. 手机APP调用流程演示
|
||
|
||
### 场景:设备启动时获取授权
|
||
|
||
#### Step 1: APP发起请求
|
||
```javascript
|
||
GET /api/licenses/download?sn=GD30-20260430-001
|
||
```
|
||
|
||
#### Step 2: 平台处理流程
|
||
```
|
||
接收请求
|
||
↓
|
||
验证设备SN是否存在
|
||
↓
|
||
查找设备级授权 (device_sn匹配)
|
||
↓ 未找到
|
||
查找型号级授权 (model匹配 + 状态生效 + 未过期)
|
||
↓
|
||
获取关联的配置文件
|
||
↓
|
||
生成/读取授权文件JSON
|
||
↓
|
||
记录下载日志 (时间、IP、APP版本)
|
||
↓
|
||
返回JSON响应
|
||
```
|
||
|
||
#### Step 3: APP接收响应
|
||
```json
|
||
{
|
||
"version": "1.0",
|
||
"deviceModel": "GD-30 Supreme",
|
||
"deviceSN": "GD30-20260430-001",
|
||
"validUntil": "2027-04-30",
|
||
"status": "active",
|
||
"authModules": [
|
||
{"id": "1D", "name": "...", "enabled": true},
|
||
{"id": "2D", "name": "...", "enabled": true},
|
||
{"id": "3D", "name": "...", "enabled": true}
|
||
],
|
||
"config": {
|
||
"emissionParams": {"maxVoltage": "1000V", ...},
|
||
"acquisitionParams": {"channels": 12, ...}
|
||
},
|
||
"signature": {...}
|
||
}
|
||
```
|
||
|
||
#### Step 4: APP处理授权
|
||
```javascript
|
||
// 1. 验证签名
|
||
if (!verifySignature(license)) {
|
||
throw new Error('授权文件被篡改');
|
||
}
|
||
|
||
// 2. 检查有效期
|
||
if (new Date(license.validUntil) < new Date()) {
|
||
throw new Error('授权已过期');
|
||
}
|
||
|
||
// 3. 启用功能模块
|
||
license.authModules.forEach(module => {
|
||
if (module.enabled) {
|
||
app.enableFeature(module.id);
|
||
}
|
||
});
|
||
|
||
// 4. 应用配置参数
|
||
app.applyConfig(license.config);
|
||
|
||
// 5. 显示授权信息
|
||
console.log(`授权有效期至: ${license.validUntil}`);
|
||
```
|
||
|
||
## 5. 数据库查询演示
|
||
|
||
### 查看授权记录
|
||
```sql
|
||
SELECT id, model, modules, expiry, status,
|
||
CASE WHEN license_file IS NOT NULL THEN '已生成' ELSE '未生成' END as file_status
|
||
FROM licenses
|
||
ORDER BY id DESC;
|
||
```
|
||
|
||
**示例输出:**
|
||
```
|
||
id | model | modules | expiry | status | file_status
|
||
1 | GD-30 | 一维, 二维, 三维| 2027-04-30 | 生效 | 已生成
|
||
2 | GD-20 | 一维, 二维 | 2027-04-30 | 生效 | 已生成
|
||
```
|
||
|
||
### 查看下载日志
|
||
```sql
|
||
SELECT l.device_sn, l.download_time, l.ip_address,
|
||
lic.model, lic.expiry
|
||
FROM license_download_logs l
|
||
JOIN licenses lic ON l.license_id = lic.id
|
||
ORDER BY l.download_time DESC
|
||
LIMIT 10;
|
||
```
|
||
|
||
**示例输出:**
|
||
```
|
||
device_sn | download_time | ip_address | model | expiry
|
||
GD30-20260430-001 | 2026-04-30 16:30:00 | 192.168.1.100 | GD-30 | 2027-04-30
|
||
GD30-20260430-002 | 2026-04-30 16:25:00 | 192.168.1.101 | GD-30 | 2027-04-30
|
||
```
|
||
|
||
## 6. 错误场景演示
|
||
|
||
### 场景A: 设备不存在
|
||
```bash
|
||
curl "http://localhost:3000/api/licenses/download?sn=INVALID-SN"
|
||
```
|
||
|
||
**响应:**
|
||
```json
|
||
{
|
||
"error": "设备不存在"
|
||
}
|
||
```
|
||
**HTTP状态码:** 404
|
||
|
||
### 场景B: 无有效授权
|
||
```bash
|
||
curl "http://localhost:3000/api/licenses/download?sn=GD30-NO-LICENSE"
|
||
```
|
||
|
||
**响应:**
|
||
```json
|
||
{
|
||
"error": "该设备暂无有效授权",
|
||
"deviceSN": "GD30-NO-LICENSE",
|
||
"deviceModel": "GD-30 Supreme"
|
||
}
|
||
```
|
||
**HTTP状态码:** 404
|
||
|
||
### 场景C: 缺少SN参数
|
||
```bash
|
||
curl "http://localhost:3000/api/licenses/download"
|
||
```
|
||
|
||
**响应:**
|
||
```json
|
||
{
|
||
"error": "设备SN不能为空"
|
||
}
|
||
```
|
||
**HTTP状态码:** 400
|
||
|
||
## 7. 性能测试演示
|
||
|
||
### 测试工具: Apache Bench (ab)
|
||
|
||
```bash
|
||
# 模拟100个并发请求,总共1000次请求
|
||
ab -n 1000 -c 100 "http://localhost:3000/api/licenses/download?sn=GD30-TEST-001"
|
||
```
|
||
|
||
**预期结果:**
|
||
```
|
||
Requests per second: 500.00 [#/sec] (mean)
|
||
Time per request: 200.000 [ms] (mean)
|
||
Time per request: 2.000 [ms] (mean, across all concurrent requests)
|
||
```
|
||
|
||
### 优化建议
|
||
1. 首次生成后缓存到数据库,避免重复计算
|
||
2. 为licenses表添加索引:
|
||
```sql
|
||
CREATE INDEX idx_license_device_sn ON licenses(device_sn);
|
||
CREATE INDEX idx_license_model ON licenses(model, status, expiry);
|
||
```
|
||
3. 生产环境使用Redis缓存热点授权
|
||
|
||
## 8. 安全测试演示
|
||
|
||
### 测试签名验证
|
||
|
||
```javascript
|
||
// 正常授权文件
|
||
const validLicense = await fetchLicense('GD30-TEST-001');
|
||
console.log(verifySignature(validLicense)); // true
|
||
|
||
// 篡改后的授权文件
|
||
const tamperedLicense = {...validLicense, validUntil: '2099-12-31'};
|
||
console.log(verifySignature(tamperedLicense)); // false
|
||
```
|
||
|
||
### 测试SQL注入防护
|
||
|
||
```bash
|
||
# 尝试SQL注入
|
||
curl "http://localhost:3000/api/licenses/download?sn='; DROP TABLE devices; --"
|
||
```
|
||
|
||
**结果:**
|
||
- 使用参数化查询,SQL注入无效
|
||
- 返回"设备不存在"错误
|
||
|
||
## 9. 移动端适配说明
|
||
|
||
### iOS App (Swift)
|
||
|
||
```swift
|
||
func fetchLicense(deviceSN: String, completion: @escaping (Result<License, Error>) -> Void) {
|
||
let url = URL(string: "http://your-server.com/api/licenses/download?sn=\(deviceSN)")!
|
||
|
||
URLSession.shared.dataTask(with: url) { data, response, error in
|
||
if let error = error {
|
||
completion(.failure(error))
|
||
return
|
||
}
|
||
|
||
guard let data = data,
|
||
let license = try? JSONDecoder().decode(License.self, from: data) else {
|
||
completion(.failure(NSError(domain: "Invalid response", code: -1)))
|
||
return
|
||
}
|
||
|
||
completion(.success(license))
|
||
}.resume()
|
||
}
|
||
```
|
||
|
||
### Android App (Kotlin)
|
||
|
||
```kotlin
|
||
suspend fun fetchLicense(deviceSN: String): License {
|
||
val response = httpClient.get("http://your-server.com/api/licenses/download?sn=$deviceSN")
|
||
|
||
if (!response.isSuccess) {
|
||
throw IOException("Failed to fetch license")
|
||
}
|
||
|
||
return response.body()
|
||
}
|
||
```
|
||
|
||
## 10. 监控和告警
|
||
|
||
### 关键指标监控
|
||
|
||
1. **API响应时间**
|
||
- P95 < 500ms
|
||
- P99 < 1000ms
|
||
|
||
2. **错误率**
|
||
- 4xx错误 < 5%
|
||
- 5xx错误 < 1%
|
||
|
||
3. **下载次数统计**
|
||
- 每日下载总量
|
||
- 按设备型号分组统计
|
||
- 异常下载频率检测
|
||
|
||
### 告警规则
|
||
|
||
```yaml
|
||
alerts:
|
||
- name: high_error_rate
|
||
condition: error_rate > 5%
|
||
duration: 5m
|
||
action: send_notification
|
||
|
||
- name: slow_response
|
||
condition: p95_latency > 1000ms
|
||
duration: 10m
|
||
action: send_notification
|
||
|
||
- name: unusual_download_pattern
|
||
condition: downloads_per_minute > 100
|
||
duration: 2m
|
||
action: send_notification_and_block_ip
|
||
```
|
||
|
||
---
|
||
|
||
**提示**: 实际使用时可以配合截图工具(如Snipaste、Greenshot)截取各个界面的实际效果,添加到文档中以便更直观地展示功能。 |