feat(impedance): 新增用户耳机阻抗功能模块
- 设计并添加 user_headphone_impedance 表,包含唯一复合索引保证数据完整性 - 实现阻抗上报API接口 /audio/reportImpedance,支持设备上传阻抗数据 - 使用Redis Hash结构缓存阻抗数据,键为 "headphone_impedances" - 新增定时持久化任务,每5分钟从Redis读取数据批量写入数据库 - 对品牌和型号数据进行trim+lower归一化处理,支持数据去重更新 - 优化查询性能,确保mac_addr与品牌型号归一化组合唯一索引生效 - 更新故障排查指南,新增Redis缓存及唯一索引相关问题检查 - 新增阻抗持久化配置开关,允许按需启用该功能
This commit is contained in:
@@ -33,6 +33,12 @@
|
||||
- [README.md](file://README.md)
|
||||
</cite>
|
||||
|
||||
## 更新摘要
|
||||
**变更内容**
|
||||
- 更新了等化曲线API端点的查询参数处理说明,现在使用标准的Gin查询参数处理
|
||||
- 添加了关于URL编码中加号字符处理的详细说明
|
||||
- 更新了相关接口的请求示例和注意事项
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [项目结构](#项目结构)
|
||||
@@ -214,6 +220,10 @@ G-->>C : "HTTP 响应(JSON/可选base64)"
|
||||
- 失败:code 非 0,message 为错误描述
|
||||
- base64Resp
|
||||
- 可选查询参数 base64Resp=true|1|空 时,将响应体 JSON 再进行自定义 Base64 编码返回;默认开启。
|
||||
- **更新** URL参数编码规范
|
||||
- 所有查询参数现在使用标准的Gin查询参数处理,遵循标准HTTP URL编码规范
|
||||
- 加号字符(+)在查询参数中被解释为空格,这是标准HTTP URL编码行为
|
||||
- 如果需要在参数中使用字面量加号,应将其编码为 %2B
|
||||
|
||||
**章节来源**
|
||||
- [internal/router/router.go:27-38](file://internal/router/router.go#L27-L38)
|
||||
@@ -483,6 +493,13 @@ G-->>C : "HTTP 响应(JSON/可选base64)"
|
||||
- 示例
|
||||
- curl: `curl "http://localhost:8080/audio/getCurve?brand=Sony&name=WH-1000XM4&target=Harman%20over-ear%202018&base64Resp=true"`
|
||||
|
||||
**更新** URL参数编码注意事项
|
||||
- 等化曲线API端点现在使用标准的Gin查询参数处理,遵循标准HTTP URL编码规范
|
||||
- 当target参数包含特殊字符(如空格、加号等)时,需要进行正确的URL编码
|
||||
- 例如:`Harman over-ear 2018` 应该编码为 `Harman%20over-ear%202018`
|
||||
- 如果需要传递字面量的加号字符(+),应将其编码为 `%2B`,因为标准HTTP会将+解释为空格
|
||||
- 客户端库通常会自动处理URL编码,但手动构造URL时需要特别注意
|
||||
|
||||
**章节来源**
|
||||
- [internal/router/router.go:70-71](file://internal/router/router.go#L70-L71)
|
||||
- [internal/handler/curve.go:56-198](file://internal/handler/curve.go#L56-L198)
|
||||
@@ -656,6 +673,7 @@ Router --> Middleware_CacheControl
|
||||
- **新增**:检查 EQ API 调用与响应解析;确认 S3 CSV 读取权限;验证缓存结构与分布式锁。
|
||||
- **新增**:关注 fr 数据独立存储与合并逻辑。
|
||||
- **新增**:检查缓存控制中间件的缓存策略是否正确应用。
|
||||
- **更新**:检查URL参数编码问题,特别是target参数中的特殊字符处理。
|
||||
- 统一错误响应
|
||||
- 所有错误均通过统一响应体返回,code 非 0 表示失败,message 描述错误原因。
|
||||
|
||||
@@ -677,6 +695,7 @@ Router --> Middleware_CacheControl
|
||||
- 等化曲线管理支持多种目标曲线的参数化 EQ 计算,具备智能缓存优化和 CSV 数据读取能力。
|
||||
- S3 存储集成支持 CSV 数据读取,为 Eafonyoung 源的等化曲线处理提供测量数据。
|
||||
- 缓存控制中间件提供针对不同接口的差异化缓存策略,提升系统性能与用户体验。
|
||||
- **更新**:等化曲线API端点现在使用标准的Gin查询参数处理,提供更好的URL编码兼容性。
|
||||
|
||||
## 附录
|
||||
|
||||
@@ -818,4 +837,40 @@ end
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [internal/cache/curve_cache.go:18-148](file://internal/cache/curve_cache.go#L18-L148)
|
||||
- [internal/cache/curve_cache.go:18-148](file://internal/cache/curve_cache.go#L18-L148)
|
||||
|
||||
### URL参数编码最佳实践
|
||||
|
||||
**更新** 客户端实现建议
|
||||
|
||||
对于等化曲线API端点,建议使用标准的URL编码库来处理查询参数:
|
||||
|
||||
```javascript
|
||||
// JavaScript/Node.js 示例
|
||||
const target = "Harman over-ear 2018";
|
||||
const url = `/audio/getCurve?brand=Sony&name=WH-1000XM4&target=${encodeURIComponent(target)}`;
|
||||
|
||||
// Python 示例
|
||||
from urllib.parse import quote_plus
|
||||
target = "Harman over-ear 2018"
|
||||
url = f"/audio/getCurve?brand=Sony&name=WH-1000XM4&target={quote_plus(target)}"
|
||||
|
||||
// Go 示例
|
||||
import "net/url"
|
||||
target := "Harman over-ear 2018"
|
||||
params := url.Values{}
|
||||
params.Set("brand", "Sony")
|
||||
params.Set("name", "WH-1000XM4")
|
||||
params.Set("target", target)
|
||||
url := fmt.Sprintf("/audio/getCurve?%s", params.Encode())
|
||||
```
|
||||
|
||||
**重要提示**:
|
||||
- 现代编程语言的标准URL编码库会自动处理特殊字符
|
||||
- 如果需要传递字面量的加号字符(+),应使用 `%2B` 而不是 `+`
|
||||
- 空格字符应编码为 `%20` 或使用 `+`(两者在HTTP中都被解释为空格)
|
||||
- 避免手动拼接URL字符串,使用标准的URL编码函数
|
||||
|
||||
**章节来源**
|
||||
- [internal/handler/curve.go:144-153](file://internal/handler/curve.go#L144-L153)
|
||||
- [internal/handler/curve.go:55-58](file://internal/handler/curve.go#L55-L58)
|
||||
@@ -3,31 +3,37 @@
|
||||
<cite>
|
||||
**本文引用的文件**
|
||||
- [internal/handler/device.go](file://internal/handler/device.go)
|
||||
- [internal/handler/impedance.go](file://internal/handler/impedance.go)
|
||||
- [internal/router/router.go](file://internal/router/router.go)
|
||||
- [internal/cache/redis.go](file://internal/cache/redis.go)
|
||||
- [internal/config/redis.go](file://internal/config/redis.go)
|
||||
- [internal/config/database.go](file://internal/config/database.go)
|
||||
- [internal/config/config.go](file://internal/config/config.go)
|
||||
- [internal/database/mysql.go](file://internal/database/mysql.go)
|
||||
- [internal/model/user_device.go](file://internal/model/user_device.go)
|
||||
- [internal/model/user_active.go](file://internal/model/user_active.go)
|
||||
- [internal/model/user_headphone_impedance.go](file://internal/model/user_headphone_impedance.go)
|
||||
- [internal/repository/device.go](file://internal/repository/device.go)
|
||||
- [internal/repository/headphone_impedance.go](file://internal/repository/headphone_impedance.go)
|
||||
- [internal/task/device_persist.go](file://internal/task/device_persist.go)
|
||||
- [internal/task/impedance_persist.go](file://internal/task/impedance_persist.go)
|
||||
- [internal/response/response.go](file://internal/response/response.go)
|
||||
- [internal/middleware/logger.go](file://internal/middleware/logger.go)
|
||||
- [internal/middleware/cors.go](file://internal/middleware/cors.go)
|
||||
- [cmd/server/main.go](file://cmd/server/main.go)
|
||||
- [sql/user_device.sql](file://sql/user_device.sql)
|
||||
- [sql/user_active.sql](file://sql/user_active.sql)
|
||||
- [sql/user_headphone_impedance.sql](file://sql/user_headphone_impedance.sql)
|
||||
- [README.md](file://README.md)
|
||||
</cite>
|
||||
|
||||
## 更新摘要
|
||||
**变更内容**
|
||||
- 新增设备信息持久化系统,实现Redis到数据库的定时同步
|
||||
- 添加DevicePersistTask组件,支持设备注册和活动跟踪的数据库持久化
|
||||
- 新增数据库表user_device和user_active的完整数据结构
|
||||
- 实现每5分钟的批量数据同步机制
|
||||
- 增强数据一致性保证和错误处理机制
|
||||
- 新增耳机阻抗上报功能,提供完整的音频设备阻抗数据采集能力
|
||||
- 实现独立的阻抗处理器、数据模型、仓库层和持久化任务
|
||||
- 添加专用的数据库表结构和索引优化
|
||||
- 支持品牌型号归一化和去重更新机制
|
||||
- 保持与现有设备管理系统的一致性架构设计
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
@@ -35,29 +41,31 @@
|
||||
3. [核心组件](#核心组件)
|
||||
4. [架构概览](#架构概览)
|
||||
5. [详细组件分析](#详细组件分析)
|
||||
6. [数据库持久化系统](#数据库持久化系统)
|
||||
7. [依赖关系分析](#依赖关系分析)
|
||||
8. [性能考虑](#性能考虑)
|
||||
9. [故障排除指南](#故障排除指南)
|
||||
10. [结论](#结论)
|
||||
6. [设备信息上报接口](#设备信息上报接口)
|
||||
7. [耳机阻抗上报接口](#耳机阻抗上报接口)
|
||||
8. [数据库持久化系统](#数据库持久化系统)
|
||||
9. [依赖关系分析](#依赖关系分析)
|
||||
10. [性能考虑](#性能考虑)
|
||||
11. [故障排除指南](#故障排除指南)
|
||||
12. [结论](#结论)
|
||||
|
||||
## 简介
|
||||
|
||||
本文档详细描述了设备管理接口的完整规范,特别是设备信息上报接口。该接口允许设备向服务器上报其基本信息,包括 MAC 地址、型号、版本号等,并将数据存储在 Redis 缓存中。系统现已集成设备信息持久化系统,通过DevicePersistTask组件实现Redis到数据库的定时同步,支持设备注册和活动跟踪的数据库持久化。
|
||||
本文档详细描述了设备管理接口的完整规范,包括设备信息上报接口和新增的耳机阻抗上报接口。该系统为音频设备提供了全面的采集和分析能力,支持设备基本信息上报和耳机阻抗数据采集,并通过Redis缓存和MySQL数据库实现了高性能的数据处理和长期持久化存储。
|
||||
|
||||
文档涵盖了 HTTP 方法、URL 模式、请求参数结构、响应格式、错误处理、数据验证规则、重复上报处理和数据一致性保证等方面。
|
||||
系统现已集成双通道持久化系统,通过DevicePersistTask和ImpedancePersistTask两个独立的任务组件,分别实现设备注册信息和耳机阻抗数据的定时同步,确保数据的可靠性和一致性。
|
||||
|
||||
## 项目结构
|
||||
|
||||
该项目采用分层架构设计,主要分为以下层次:
|
||||
- **入口层**: `cmd/server/main.go` - 应用程序入口点,负责初始化配置、数据库连接、搜索引擎和缓存客户端
|
||||
- **路由层**: `internal/router/router.go` - 路由定义和中间件配置
|
||||
- **处理器层**: `internal/handler/device.go` - 设备信息上报业务逻辑
|
||||
- **任务层**: `internal/task/device_persist.go` - 设备信息持久化任务
|
||||
- **处理器层**: `internal/handler/device.go` 和 `internal/handler/impedance.go` - 设备信息和阻抗信息上报业务逻辑
|
||||
- **任务层**: `internal/task/device_persist.go` 和 `internal/task/impedance_persist.go` - 数据持久化任务
|
||||
- **缓存层**: `internal/cache/redis.go` 和 `internal/config/redis.go` - Redis 客户端配置和连接管理
|
||||
- **数据库层**: `internal/config/database.go`、`internal/database/mysql.go` - 数据库配置和连接管理
|
||||
- **模型层**: `internal/model/user_device.go`、`internal/model/user_active.go` - 数据库模型定义
|
||||
- **仓库层**: `internal/repository/device.go` - 数据访问层
|
||||
- **模型层**: `internal/model/user_device.go`、`internal/model/user_active.go`、`internal/model/user_headphone_impedance.go` - 数据库模型定义
|
||||
- **仓库层**: `internal/repository/device.go` 和 `internal/repository/headphone_impedance.go` - 数据访问层
|
||||
- **响应层**: `internal/response/response.go` - 统一响应格式
|
||||
- **中间件层**: `internal/middleware/logger.go` 和 `internal/middleware/cors.go` - 日志记录和跨域支持
|
||||
|
||||
@@ -73,7 +81,9 @@ Middleware[中间件层]
|
||||
end
|
||||
subgraph "业务处理"
|
||||
DeviceHandler[设备处理器]
|
||||
ImpedanceHandler[阻抗处理器]
|
||||
DevicePersistTask[设备持久化任务]
|
||||
ImpedancePersistTask[阻抗持久化任务]
|
||||
Response[响应层]
|
||||
end
|
||||
subgraph "数据存储"
|
||||
@@ -84,99 +94,77 @@ end
|
||||
subgraph "数据模型"
|
||||
UserDevice[用户设备模型]
|
||||
UserActive[用户活跃模型]
|
||||
Repository[数据访问层]
|
||||
UserHeadphoneImpedance[用户耳机阻抗模型]
|
||||
DeviceRepository[设备数据访问层]
|
||||
ImpedanceRepository[阻抗数据访问层]
|
||||
end
|
||||
Main --> Gin
|
||||
Gin --> Router
|
||||
Router --> Middleware
|
||||
Router --> DeviceHandler
|
||||
Router --> ImpedanceHandler
|
||||
DeviceHandler --> Response
|
||||
ImpedanceHandler --> Response
|
||||
DeviceHandler --> Redis
|
||||
ImpedanceHandler --> Redis
|
||||
DevicePersistTask --> MySQL
|
||||
ImpedancePersistTask --> MySQL
|
||||
DevicePersistTask --> Redis
|
||||
DevicePersistTask --> Repository
|
||||
Repository --> UserDevice
|
||||
Repository --> UserActive
|
||||
ImpedancePersistTask --> Redis
|
||||
DevicePersistTask --> DeviceRepository
|
||||
ImpedancePersistTask --> ImpedanceRepository
|
||||
DeviceRepository --> UserDevice
|
||||
DeviceRepository --> UserActive
|
||||
ImpedanceRepository --> UserHeadphoneImpedance
|
||||
Main --> Config
|
||||
Main --> Redis
|
||||
Main --> MySQL
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [cmd/server/main.go:27-76](file://cmd/server/main.go#L27-L76)
|
||||
- [cmd/server/main.go:27-106](file://cmd/server/main.go#L27-L106)
|
||||
- [internal/handler/device.go:15-25](file://internal/handler/device.go#L15-L25)
|
||||
- [internal/handler/impedance.go:18-28](file://internal/handler/impedance.go#L18-L28)
|
||||
- [internal/task/device_persist.go:14-22](file://internal/task/device_persist.go#L14-L22)
|
||||
- [internal/task/impedance_persist.go:16-28](file://internal/task/impedance_persist.go#L16-L28)
|
||||
|
||||
**章节来源**
|
||||
- [cmd/server/main.go:1-156](file://cmd/server/main.go#L1-L156)
|
||||
- [internal/router/router.go:1-42](file://internal/router/router.go#L1-L42)
|
||||
- [cmd/server/main.go:1-209](file://cmd/server/main.go#L1-L209)
|
||||
- [internal/router/router.go:1-84](file://internal/router/router.go#L1-L84)
|
||||
|
||||
## 核心组件
|
||||
|
||||
### 设备信息上报接口
|
||||
|
||||
设备信息上报接口是本项目的核心功能,负责接收设备上报的信息并进行处理。该接口实现了以下关键功能:
|
||||
设备信息上报接口是本项目的核心功能之一,负责接收设备的基本信息并进行处理。该接口实现了以下关键功能:
|
||||
|
||||
- **参数验证**: 对必需参数进行验证,确保数据完整性
|
||||
- **数据格式化**: 将设备信息转换为统一的 JSON 格式
|
||||
- **缓存存储**: 使用 Redis Hash 结构存储设备信息
|
||||
- **错误处理**: 提供统一的错误响应格式
|
||||
|
||||
### 耳机阻抗上报接口
|
||||
|
||||
新增的耳机阻抗上报接口专门用于采集用户的耳机阻抗数据,支持多品牌多型号的耳机管理。该接口具有以下特点:
|
||||
|
||||
- **完整参数验证**: 验证MAC地址、设备型号、耳机品牌、型号和阻抗值
|
||||
- **智能去重**: 基于MAC地址+耳机品牌+耳机型号的唯一性判断
|
||||
- **文本归一化**: 自动去除空格并转换为小写,确保数据一致性
|
||||
- **实时更新**: 同一条记录始终反映最新阻抗值,不保留历史版本
|
||||
|
||||
### 设备持久化任务
|
||||
|
||||
新增的设备持久化任务组件负责将Redis中的设备信息定期同步到数据库中,实现数据的长期持久化存储。该组件具有以下特点:
|
||||
设备持久化任务组件负责将Redis中的设备信息定期同步到数据库中,实现数据的长期持久化存储。
|
||||
|
||||
- **定时同步**: 每5分钟自动执行一次数据同步
|
||||
- **批量处理**: 支持批量读取和处理Redis中的设备数据
|
||||
- **事务保证**: 确保数据库操作的一致性
|
||||
- **错误恢复**: 具备完善的错误处理和日志记录机制
|
||||
### 阻抗持久化任务
|
||||
|
||||
### 接口规范
|
||||
|
||||
#### HTTP 请求规范
|
||||
|
||||
- **方法**: GET
|
||||
- **路径**: `/audio/reportDevInfo`
|
||||
- **协议**: HTTP/1.1
|
||||
- **内容类型**: application/x-www-form-urlencoded
|
||||
|
||||
#### 请求参数
|
||||
|
||||
| 参数名 | 类型 | 必填 | 描述 | 示例 |
|
||||
|--------|------|------|------|------|
|
||||
| mac | string | 是 | 设备 MAC 地址 | `00:1A:2B:3C:4D:5E` |
|
||||
| model | string | 是 | 设备型号 | `ES900` |
|
||||
| ver | string | 否 | 设备版本号 | `v2.1.0` |
|
||||
|
||||
#### 请求头
|
||||
|
||||
| 头部名称 | 描述 | 示例 |
|
||||
|----------|------|------|
|
||||
| X-Forwarded-For | 客户端真实 IP 地址 | `192.168.1.100` |
|
||||
|
||||
#### 响应格式
|
||||
|
||||
接口返回统一的 JSON 格式响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功"
|
||||
}
|
||||
```
|
||||
|
||||
#### 错误响应
|
||||
|
||||
当请求参数无效或系统发生错误时,接口返回相应的错误码:
|
||||
|
||||
| 状态码 | 错误码 | 描述 | 响应示例 |
|
||||
|--------|--------|------|----------|
|
||||
| 200 | 400 | 参数校验失败 | `{"code": 400, "msg": "参数校验失败"}` |
|
||||
| 200 | 500 | 系统错误 | `{"code": 500, "msg": "系统错误"}` |
|
||||
新增的阻抗持久化任务组件专门处理耳机阻抗数据的持久化,具备完善的去重更新机制和错误恢复能力。
|
||||
|
||||
**章节来源**
|
||||
- [internal/handler/device.go:27-83](file://internal/handler/device.go#L27-L83)
|
||||
- [internal/router/router.go:32-38](file://internal/router/router.go#L32-L38)
|
||||
- [internal/handler/impedance.go:30-107](file://internal/handler/impedance.go#L30-L107)
|
||||
- [internal/task/device_persist.go:37-91](file://internal/task/device_persist.go#L37-L91)
|
||||
- [internal/task/impedance_persist.go:43-108](file://internal/task/impedance_persist.go#L43-L108)
|
||||
|
||||
## 架构概览
|
||||
|
||||
@@ -186,42 +174,62 @@ Main --> MySQL
|
||||
sequenceDiagram
|
||||
participant Client as 设备客户端
|
||||
participant API as API网关
|
||||
participant Handler as 设备处理器
|
||||
participant DeviceHandler as 设备处理器
|
||||
participant ImpedanceHandler as 阻抗处理器
|
||||
participant Redis as Redis缓存
|
||||
participant Task as 持久化任务
|
||||
participant DeviceTask as 设备持久化任务
|
||||
participant ImpedanceTask as 阻抗持久化任务
|
||||
participant DB as MySQL数据库
|
||||
participant Logger as 日志系统
|
||||
Note over Client,Logger : 设备信息上报流程
|
||||
Client->>API : GET /audio/reportDevInfo?mac=&model=&ver=
|
||||
API->>Handler : 调用 ReportDevInfo()
|
||||
Handler->>Handler : 参数验证
|
||||
API->>DeviceHandler : 调用 ReportDevInfo()
|
||||
DeviceHandler->>DeviceHandler : 参数验证
|
||||
alt 参数验证失败
|
||||
Handler->>Client : 返回错误响应
|
||||
DeviceHandler->>Client : 返回错误响应
|
||||
else 参数验证成功
|
||||
Handler->>Handler : 格式化设备信息
|
||||
Handler->>Redis : HSET devices {mac} : {json_data}
|
||||
DeviceHandler->>DeviceHandler : 格式化设备信息
|
||||
DeviceHandler->>Redis : HSET devices {mac} : {json_data}
|
||||
alt Redis操作失败
|
||||
Handler->>Logger : 记录错误日志
|
||||
Handler->>Client : 返回系统错误
|
||||
DeviceHandler->>Logger : 记录错误日志
|
||||
DeviceHandler->>Client : 返回系统错误
|
||||
else Redis操作成功
|
||||
Handler->>Client : 返回成功响应
|
||||
DeviceHandler->>Client : 返回成功响应
|
||||
end
|
||||
end
|
||||
Note over Task,DB : 每5分钟定时执行
|
||||
Task->>Redis : HGetAll devices
|
||||
Task->>Task : 解析JSON数据
|
||||
Task->>DB : 插入/更新 user_device
|
||||
Task->>DB : 插入/更新 user_active
|
||||
Task->>Redis : HDel 成功记录
|
||||
Task->>Logger : 记录同步结果
|
||||
Note over Client,Logger : 阻抗信息上报流程
|
||||
Client->>API : GET /audio/reportImpedance?mac=&name=&brand=&model=&value=
|
||||
API->>ImpedanceHandler : 调用 ReportImpedance()
|
||||
ImpedanceHandler->>ImpedanceHandler : 参数验证和文本归一化
|
||||
alt 参数验证失败
|
||||
ImpedanceHandler->>Client : 返回错误响应
|
||||
else 参数验证成功
|
||||
ImpedanceHandler->>ImpedanceHandler : 格式化阻抗信息
|
||||
ImpedanceHandler->>Redis : HSET headphone_impedances {field} : {json_data}
|
||||
alt Redis操作失败
|
||||
ImpedanceHandler->>Logger : 记录错误日志
|
||||
ImpedanceHandler->>Client : 返回系统错误
|
||||
else Redis操作成功
|
||||
ImpedanceHandler->>Client : 返回成功响应
|
||||
end
|
||||
end
|
||||
Note over DeviceTask,DB : 每5分钟定时执行
|
||||
DeviceTask->>Redis : HGetAll devices
|
||||
DeviceTask->>DB : 插入/更新 user_device 和 user_active
|
||||
Note over ImpedanceTask,DB : 每5分钟定时执行
|
||||
ImpedanceTask->>Redis : HGetAll headphone_impedances
|
||||
ImpedanceTask->>DB : 插入/更新 user_headphone_impedance (带去重逻辑)
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [internal/handler/device.go:27-83](file://internal/handler/device.go#L27-L83)
|
||||
- [internal/handler/impedance.go:43-107](file://internal/handler/impedance.go#L43-L107)
|
||||
- [internal/task/device_persist.go:37-91](file://internal/task/device_persist.go#L37-L91)
|
||||
- [internal/task/impedance_persist.go:43-108](file://internal/task/impedance_persist.go#L43-L108)
|
||||
|
||||
## 详细组件分析
|
||||
|
||||
### 设备处理器类图
|
||||
### 处理器类图
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
@@ -231,6 +239,14 @@ class DeviceHandler {
|
||||
+NewDeviceHandler(redis, log) DeviceHandler
|
||||
+ReportDevInfo(c) void
|
||||
}
|
||||
class ImpedanceHandler {
|
||||
-redis : redis.Client
|
||||
-log : zap.Logger
|
||||
+NewImpedanceHandler(redis, log) ImpedanceHandler
|
||||
+ReportImpedance(c) void
|
||||
+normalizeHeadphoneText(s) string
|
||||
+impedanceRedisField(mac, brandNorm, modelNorm) string
|
||||
}
|
||||
class DevicePersistTask {
|
||||
-rdb : redis.Client
|
||||
-repo : DeviceRepository
|
||||
@@ -238,8 +254,15 @@ class DevicePersistTask {
|
||||
+NewDevicePersistTask(rdb, repo, log) DevicePersistTask
|
||||
+Start(interval) void
|
||||
+Persist() void
|
||||
+persistDevice(ctx, info) bool
|
||||
+persistActive(ctx, info) bool
|
||||
}
|
||||
class ImpedancePersistTask {
|
||||
-rdb : redis.Client
|
||||
-repo : HeadphoneImpedanceRepository
|
||||
-log : zap.Logger
|
||||
+NewImpedancePersistTask(rdb, repo, log) ImpedancePersistTask
|
||||
+Start(interval) void
|
||||
+Persist() void
|
||||
+persistOne(ctx, info) bool
|
||||
}
|
||||
class RedisClient {
|
||||
+HSet(ctx, key, field, value) error
|
||||
@@ -252,18 +275,24 @@ class Logger {
|
||||
+Error(message, error) void
|
||||
}
|
||||
DeviceHandler --> RedisClient : 使用
|
||||
ImpedanceHandler --> RedisClient : 使用
|
||||
DeviceHandler --> Logger : 记录日志
|
||||
ImpedanceHandler --> Logger : 记录日志
|
||||
DevicePersistTask --> RedisClient : 读取数据
|
||||
ImpedancePersistTask --> RedisClient : 读取数据
|
||||
DevicePersistTask --> Logger : 记录日志
|
||||
ImpedancePersistTask --> Logger : 记录日志
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [internal/handler/device.go:15-25](file://internal/handler/device.go#L15-L25)
|
||||
- [internal/handler/impedance.go:18-28](file://internal/handler/impedance.go#L18-L28)
|
||||
- [internal/task/device_persist.go:14-22](file://internal/task/device_persist.go#L14-L22)
|
||||
- [internal/task/impedance_persist.go:16-28](file://internal/task/impedance_persist.go#L16-L28)
|
||||
|
||||
### 参数验证流程
|
||||
### 参数验证流程对比
|
||||
|
||||
设备信息上报接口的参数验证流程如下:
|
||||
#### 设备信息上报参数验证流程
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
@@ -291,64 +320,202 @@ ReturnSystemError --> End
|
||||
ReturnSuccess --> End
|
||||
```
|
||||
|
||||
#### 阻抗信息上报参数验证流程
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([开始处理请求]) --> GetParams["获取查询参数<br/>mac, name, brand, model, value"]
|
||||
GetParams --> TrimParams["去除所有参数空白字符"]
|
||||
TrimParams --> ValidateRequired{"必需参数是否为空?"}
|
||||
ValidateRequired --> |是| ReturnError["返回参数校验失败"]
|
||||
ValidateRequired --> |否| ParseValue["解析阻抗值为整数"]
|
||||
ParseValue --> ParseSuccess{"解析成功?"}
|
||||
ParseSuccess --> |否| ReturnError
|
||||
ParseSuccess --> |是| NormalizeText["品牌型号文本归一化<br/>(trim + lower)"]
|
||||
NormalizeText --> CreateField["创建Redis字段<br/>(mac|brand_norm|model_norm)"]
|
||||
CreateField --> FormatData["格式化阻抗信息"]
|
||||
FormatData --> MarshalJSON["序列化为JSON"]
|
||||
MarshalJSON --> JSONSuccess{"JSON序列化成功?"}
|
||||
JSONSuccess --> |否| ReturnSystemError["返回系统错误"]
|
||||
JSONSuccess --> |是| SaveToRedis["保存到Redis"]
|
||||
SaveToRedis --> RedisSuccess{"Redis操作成功?"}
|
||||
RedisSuccess --> |否| ReturnSystemError
|
||||
RedisSuccess --> |是| ReturnSuccess["返回成功响应"]
|
||||
ReturnError --> End([结束])
|
||||
ReturnSystemError --> End
|
||||
ReturnSuccess --> End
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [internal/handler/device.go:27-83](file://internal/handler/device.go#L27-L83)
|
||||
- [internal/handler/impedance.go:43-107](file://internal/handler/impedance.go#L43-L107)
|
||||
|
||||
### Redis 缓存交互
|
||||
|
||||
设备信息上报接口使用 Redis Hash 结构存储设备数据:
|
||||
系统使用两个独立的Redis Hash结构来存储不同类型的数据:
|
||||
|
||||
#### 设备信息存储
|
||||
- **键名**: `devices`
|
||||
- **字段**: 设备 MAC 地址
|
||||
- **值**: JSON 格式的设备信息对象
|
||||
|
||||
#### 阻抗信息存储
|
||||
- **键名**: `headphone_impedances`
|
||||
- **字段**: `mac_addr|headphone_brand_norm|headphone_model_norm`
|
||||
- **值**: JSON 格式的阻抗信息对象
|
||||
|
||||
存储的数据结构示例:
|
||||
```json
|
||||
{
|
||||
"mac_addr": "00:1A:2B:3C:4D:5E",
|
||||
"model": "ES900",
|
||||
"active_date": "2024-01-15",
|
||||
"ip_addr": "192.168.1.100",
|
||||
"ver": "v2.1.0"
|
||||
"device_model": "Luxsin-X8",
|
||||
"impedance_ohm": 32,
|
||||
"headphone_brand": "Sony",
|
||||
"headphone_model": "WH-1000XM4",
|
||||
"headphone_brand_norm": "sony",
|
||||
"headphone_model_norm": "wh-1000xm4",
|
||||
"ip_addr": "192.168.1.100"
|
||||
}
|
||||
```
|
||||
|
||||
**章节来源**
|
||||
- [internal/handler/device.go:50-77](file://internal/handler/device.go#L50-L77)
|
||||
- [internal/handler/impedance.go:71-80](file://internal/handler/impedance.go#L71-L80)
|
||||
- [internal/handler/impedance.go:113-115](file://internal/handler/impedance.go#L113-L115)
|
||||
|
||||
### 中间件集成
|
||||
## 设备信息上报接口
|
||||
|
||||
系统集成了多个中间件来增强功能:
|
||||
### HTTP 请求规范
|
||||
|
||||
#### 日志中间件
|
||||
- 记录每个请求的状态、方法、路径、延迟时间
|
||||
- 区分不同级别的日志输出
|
||||
- 包含请求 ID 和错误信息
|
||||
- **方法**: GET
|
||||
- **路径**: `/audio/reportDevInfo`
|
||||
- **协议**: HTTP/1.1
|
||||
- **内容类型**: application/x-www-form-urlencoded
|
||||
|
||||
#### CORS 中间件
|
||||
- 支持跨域请求
|
||||
- 允许的方法:GET, POST, PUT, PATCH, DELETE, OPTIONS
|
||||
- 允许的头部:Origin, Content-Type, Accept, Authorization, X-Request-ID
|
||||
### 请求参数
|
||||
|
||||
| 参数名 | 类型 | 必填 | 描述 | 示例 |
|
||||
|--------|------|------|------|------|
|
||||
| mac | string | 是 | 设备 MAC 地址 | `00:1A:2B:3C:4D:5E` |
|
||||
| model | string | 是 | 设备型号 | `ES900` |
|
||||
| ver | string | 否 | 设备版本号 | `v2.1.0` |
|
||||
|
||||
### 请求头
|
||||
|
||||
| 头部名称 | 描述 | 示例 |
|
||||
|----------|------|------|
|
||||
| X-Forwarded-For | 客户端真实 IP 地址 | `192.168.1.100` |
|
||||
|
||||
### 响应格式
|
||||
|
||||
接口返回统一的 JSON 格式响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功"
|
||||
}
|
||||
```
|
||||
|
||||
### 错误响应
|
||||
|
||||
当请求参数无效或系统发生错误时,接口返回相应的错误码:
|
||||
|
||||
| 状态码 | 错误码 | 描述 | 响应示例 |
|
||||
|--------|--------|------|----------|
|
||||
| 200 | 400 | 参数校验失败 | `{"code": 400, "msg": "参数校验失败"}` |
|
||||
| 200 | 500 | 系统错误 | `{"code": 500, "msg": "系统错误"}` |
|
||||
|
||||
**章节来源**
|
||||
- [internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45)
|
||||
- [internal/middleware/cors.go:7-20](file://internal/middleware/cors.go#L7-L20)
|
||||
- [internal/handler/device.go:27-83](file://internal/handler/device.go#L27-L83)
|
||||
- [internal/router/router.go:69](file://internal/router/router.go#L69)
|
||||
|
||||
## 耳机阻抗上报接口
|
||||
|
||||
### HTTP 请求规范
|
||||
|
||||
- **方法**: GET
|
||||
- **路径**: `/audio/reportImpedance`
|
||||
- **协议**: HTTP/1.1
|
||||
- **内容类型**: application/x-www-form-urlencoded
|
||||
|
||||
### 请求参数
|
||||
|
||||
| 参数名 | 类型 | 必填 | 描述 | 示例 |
|
||||
|--------|------|------|------|------|
|
||||
| mac | string | 是 | 设备 MAC 地址(用户标识) | `00:1A:2B:3C:4D:5E` |
|
||||
| name | string | 是 | 设备型号(Luxsin-X8/Luxsin-X9) | `Luxsin-X8` |
|
||||
| brand | string | 是 | 耳机品牌 | `Sony` |
|
||||
| model | string | 是 | 耳机型号 | `WH-1000XM4` |
|
||||
| value | int | 是 | 阻抗值(整数,单位Ω) | `32` |
|
||||
|
||||
### 请求头
|
||||
|
||||
| 头部名称 | 描述 | 示例 |
|
||||
|----------|------|------|
|
||||
| X-Forwarded-For | 客户端真实 IP 地址 | `192.168.1.100` |
|
||||
|
||||
### 响应格式
|
||||
|
||||
接口返回统一的 JSON 格式响应:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "操作成功"
|
||||
}
|
||||
```
|
||||
|
||||
### 错误响应
|
||||
|
||||
当请求参数无效或系统发生错误时,接口返回相应的错误码:
|
||||
|
||||
| 状态码 | 错误码 | 描述 | 响应示例 |
|
||||
|--------|--------|------|----------|
|
||||
| 200 | 400 | 参数校验失败 | `{"code": 400, "msg": "参数校验失败"}` |
|
||||
| 200 | 500 | 系统错误 | `{"code": 500, "msg": "系统错误"}` |
|
||||
|
||||
### 数据验证规则
|
||||
|
||||
1. **必填参数验证**: 所有参数都不能为空
|
||||
2. **数据类型验证**: impedance值必须是有效的整数
|
||||
3. **文本归一化**: 品牌和型号会自动去除首尾空格并转换为小写
|
||||
4. **唯一性约束**: 同一用户同一耳机的多条记录会被合并为最新值
|
||||
|
||||
### 去重更新机制
|
||||
|
||||
系统采用智能去重策略,基于以下规则判断是否为同一耳机:
|
||||
- **唯一判定条件**: `mac_addr + headphone_brand_norm + headphone_model_norm`
|
||||
- **文本比较规则**: 先进行 `trim`(去首尾空格)+ `lower`(忽略大小写)
|
||||
- **更新策略**: 命中同一条记录时,更新最新的阻抗值、设备型号、IP地址和更新时间
|
||||
|
||||
**章节来源**
|
||||
- [internal/handler/impedance.go:30-107](file://internal/handler/impedance.go#L30-L107)
|
||||
- [internal/router/router.go:70](file://internal/router/router.go#L70)
|
||||
|
||||
## 数据库持久化系统
|
||||
|
||||
### 设备持久化任务架构
|
||||
### 双通道持久化架构
|
||||
|
||||
新增的设备持久化任务系统实现了Redis到数据库的定时同步,确保设备信息的长期持久化存储。
|
||||
系统实现了两个独立的持久化任务,分别处理设备信息和阻抗信息:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start([启动持久化任务]) --> Timer["5分钟定时器"]
|
||||
Timer --> GetData["从Redis读取所有设备数据"]
|
||||
Timer --> GetData["从Redis读取数据"]
|
||||
GetData --> ParseData["解析JSON数据"]
|
||||
ParseData --> FixDate["修复日期格式"]
|
||||
FixDate --> ProcessDevice["处理设备信息"]
|
||||
ProcessDevice --> ProcessActive["处理活跃信息"]
|
||||
ProcessActive --> Success{"处理成功?"}
|
||||
ParseData --> ProcessType{"数据类型判断"}
|
||||
ProcessType --> |设备信息| ProcessDevice["处理设备信息"]
|
||||
ProcessType --> |阻抗信息| ProcessImpedance["处理阻抗信息"]
|
||||
ProcessDevice --> FixDate["修复日期格式"]
|
||||
FixDate --> UpdateDevice["更新user_device表"]
|
||||
UpdateDevice --> UpdateActive["更新user_active表"]
|
||||
UpdateActive --> Success{"处理成功?"}
|
||||
ProcessImpedance --> CheckExisting["检查是否存在记录"]
|
||||
CheckExisting --> |不存在| InsertImpedance["插入新阻抗记录"]
|
||||
CheckExisting --> |存在| UpdateImpedance["更新阻抗记录"]
|
||||
InsertImpedance --> Success
|
||||
UpdateImpedance --> Success
|
||||
Success --> |是| BatchDelete["批量删除已处理记录"]
|
||||
Success --> |否| LogError["记录错误日志"]
|
||||
BatchDelete --> Complete["完成同步"]
|
||||
@@ -357,8 +524,10 @@ Complete --> Timer
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [internal/task/device_persist.go:24-35](file://internal/task/device_persist.go#L24-L35)
|
||||
- [internal/task/device_persist.go:24-35](file://internal/task/device_persist.go#L24-35)
|
||||
- [internal/task/impedance_persist.go:30-41](file://internal/task/impedance_persist.go#L30-41)
|
||||
- [internal/task/device_persist.go:37-91](file://internal/task/device_persist.go#L37-L91)
|
||||
- [internal/task/impedance_persist.go:43-108](file://internal/task/impedance_persist.go#L43-L108)
|
||||
|
||||
### 数据库表结构
|
||||
|
||||
@@ -383,68 +552,118 @@ Complete --> Timer
|
||||
| ip_addr | varchar(100) | NOT NULL | IP地址 |
|
||||
| create_at | datetime | DEFAULT CURRENT_TIMESTAMP | 创建时间 |
|
||||
|
||||
#### 用户耳机阻抗表 (user_headphone_impedance)
|
||||
|
||||
| 字段名 | 类型 | 约束 | 描述 |
|
||||
|--------|------|------|------|
|
||||
| id | int | PRIMARY KEY, AUTO_INCREMENT | 主键ID |
|
||||
| mac_addr | varchar(255) | NOT NULL | 设备MAC地址(用户标识) |
|
||||
| device_model | varchar(50) | NOT NULL | 设备型号(Luxsin-X8/Luxsin-X9) |
|
||||
| impedance_ohm | int | NOT NULL | 阻抗值(Ω)整数 |
|
||||
| headphone_brand | varchar(255) | NOT NULL | 耳机品牌(原始输入) |
|
||||
| headphone_model | varchar(255) | NOT NULL | 耳机型号(原始输入) |
|
||||
| headphone_brand_norm | varchar(255) | NOT NULL | 耳机品牌(trim+lower,用于去重) |
|
||||
| headphone_model_norm | varchar(255) | NOT NULL | 耳机型号(trim+lower,用于去重) |
|
||||
| ip_addr | varchar(100) | NOT NULL | 服务端获取的设备IP(最新值) |
|
||||
| create_at | datetime | NOT NULL DEFAULT CURRENT_TIMESTAMP | 创建时间 |
|
||||
| update_at | datetime | NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | 更新时间 |
|
||||
|
||||
**索引设计**:
|
||||
- **唯一索引**: `uniq_mac_brand_model(mac_addr, headphone_brand_norm, headphone_model_norm)`
|
||||
- **辅助索引**: `idx_mac(mac_addr)`、`idx_device_model(device_model)`
|
||||
|
||||
**章节来源**
|
||||
- [sql/user_device.sql:24-32](file://sql/user_device.sql#L24-L32)
|
||||
- [sql/user_active.sql:24-33](file://sql/user_active.sql#L24-L33)
|
||||
- [sql/user_headphone_impedance.sql:20-36](file://sql/user_headphone_impedance.sql#L20-L36)
|
||||
|
||||
### 数据同步流程
|
||||
|
||||
设备持久化任务的数据同步流程如下:
|
||||
#### 设备信息同步流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Redis as Redis缓存
|
||||
participant Task as 持久化任务
|
||||
participant Repo as 数据访问层
|
||||
participant DeviceTask as 设备持久化任务
|
||||
participant Repo as 设备数据访问层
|
||||
participant DB as MySQL数据库
|
||||
Task->>Redis : HGetAll devices
|
||||
Redis->>Task : 返回设备数据
|
||||
Task->>Task : 解析JSON数据
|
||||
Task->>Task : 修复日期格式
|
||||
Task->>Repo : 查找设备信息
|
||||
DeviceTask->>Redis : HGetAll devices
|
||||
Redis->>DeviceTask : 返回设备数据
|
||||
DeviceTask->>DeviceTask : 解析JSON数据
|
||||
DeviceTask->>DeviceTask : 修复日期格式
|
||||
DeviceTask->>Repo : 查找设备信息
|
||||
Repo->>DB : 查询user_device
|
||||
DB->>Repo : 返回查询结果
|
||||
alt 设备不存在
|
||||
Task->>Repo : 插入新设备
|
||||
DeviceTask->>Repo : 插入新设备
|
||||
Repo->>DB : INSERT user_device
|
||||
DB->>Repo : 返回插入结果
|
||||
else 设备存在
|
||||
Task->>Repo : 更新设备版本
|
||||
DeviceTask->>Repo : 更新设备版本
|
||||
Repo->>DB : UPDATE user_device
|
||||
DB->>Repo : 返回更新结果
|
||||
end
|
||||
Task->>Repo : 查找活跃信息
|
||||
DeviceTask->>Repo : 查找活跃信息
|
||||
Repo->>DB : 查询user_active
|
||||
DB->>Repo : 返回查询结果
|
||||
alt 活跃信息不存在
|
||||
Task->>Repo : 插入新活跃记录
|
||||
DeviceTask->>Repo : 插入新活跃记录
|
||||
Repo->>DB : INSERT user_active
|
||||
DB->>Repo : 返回插入结果
|
||||
else 活跃信息存在
|
||||
Task->>Repo : 更新IP地址
|
||||
DeviceTask->>Repo : 更新IP地址
|
||||
Repo->>DB : UPDATE user_active
|
||||
DB->>Repo : 返回更新结果
|
||||
end
|
||||
Task->>Redis : HDel 成功记录
|
||||
DeviceTask->>Redis : HDel 成功记录
|
||||
```
|
||||
|
||||
#### 阻抗信息同步流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Redis as Redis缓存
|
||||
participant ImpedanceTask as 阻抗持久化任务
|
||||
participant Repo as 阻抗数据访问层
|
||||
participant DB as MySQL数据库
|
||||
ImpedanceTask->>Redis : HGetAll headphone_impedances
|
||||
Redis->>ImpedanceTask : 返回阻抗数据
|
||||
ImpedanceTask->>ImpedanceTask : 解析JSON数据
|
||||
ImpedanceTask->>Repo : 查找阻抗记录
|
||||
Repo->>DB : 查询user_headphone_impedance
|
||||
DB->>Repo : 返回查询结果
|
||||
alt 记录不存在
|
||||
ImpedanceTask->>Repo : 插入新阻抗记录
|
||||
Repo->>DB : INSERT user_headphone_impedance
|
||||
DB->>Repo : 返回插入结果
|
||||
else 记录存在
|
||||
ImpedanceTask->>Repo : 更新阻抗记录
|
||||
Repo->>DB : UPDATE user_headphone_impedance
|
||||
DB->>Repo : 返回更新结果
|
||||
end
|
||||
ImpedanceTask->>Redis : HDel 成功记录
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [internal/task/device_persist.go:93-131](file://internal/task/device_persist.go#L93-L131)
|
||||
- [internal/task/device_persist.go:133-166](file://internal/task/device_persist.go#L133-L166)
|
||||
- [internal/task/impedance_persist.go:110-162](file://internal/task/impedance_persist.go#L110-L162)
|
||||
|
||||
### 数据一致性保证
|
||||
|
||||
系统通过以下机制确保数据一致性:
|
||||
|
||||
1. **原子性**: 使用数据库事务确保设备和活跃信息的更新原子性
|
||||
1. **原子性**: 使用数据库事务确保相关操作的原子性
|
||||
2. **幂等性**: 支持重复上报的处理,避免数据重复
|
||||
3. **回滚机制**: 当部分操作失败时,系统会记录错误并继续处理其他记录
|
||||
4. **数据修复**: 自动修复历史数据中的无效日期格式
|
||||
5. **智能去重**: 阻抗数据基于规范化后的品牌型号进行唯一性判断
|
||||
|
||||
**章节来源**
|
||||
- [internal/task/device_persist.go:70-73](file://internal/task/device_persist.go#L70-L73)
|
||||
- [internal/task/device_persist.go:101-128](file://internal/task/device_persist.go#L101-L128)
|
||||
- [internal/task/device_persist.go:141-163](file://internal/task/device_persist.go#L141-L163)
|
||||
- [internal/task/impedance_persist.go:120-162](file://internal/task/impedance_persist.go#L120-L162)
|
||||
|
||||
## 依赖关系分析
|
||||
|
||||
@@ -493,7 +712,9 @@ Middleware --> Zap
|
||||
|
||||
**图表来源**
|
||||
- [internal/handler/device.go:3-13](file://internal/handler/device.go#L3-L13)
|
||||
- [internal/handler/impedance.go:3-14](file://internal/handler/impedance.go#L3-L14)
|
||||
- [internal/task/device_persist.go:3-12](file://internal/task/device_persist.go#L3-L12)
|
||||
- [internal/task/impedance_persist.go:3-12](file://internal/task/impedance_persist.go#L3-L12)
|
||||
|
||||
### 内部模块依赖
|
||||
|
||||
@@ -509,17 +730,20 @@ DBConfig[internal/config/database.go]
|
||||
end
|
||||
subgraph "服务模块"
|
||||
Router[internal/router/router.go]
|
||||
Handler[internal/handler/device.go]
|
||||
Task[internal/task/device_persist.go]
|
||||
DeviceHandler[internal/handler/device.go]
|
||||
ImpedanceHandler[internal/handler/impedance.go]
|
||||
DeviceTask[internal/task/device_persist.go]
|
||||
ImpedanceTask[internal/task/impedance_persist.go]
|
||||
Cache[internal/cache/redis.go]
|
||||
Database[internal/database/mysql.go]
|
||||
end
|
||||
subgraph "数据模块"
|
||||
Model[internal/model/user_device.go]
|
||||
Model2[internal/model/user_active.go]
|
||||
Repository[internal/repository/device.go]
|
||||
SQL[sql/user_device.sql]
|
||||
SQL2[sql/user_active.sql]
|
||||
UserDevice[internal/model/user_device.go]
|
||||
UserActive[internal/model/user_active.go]
|
||||
UserHeadphoneImpedance[internal/model/user_headphone_impedance.go]
|
||||
DeviceRepo[internal/repository/device.go]
|
||||
ImpedanceRepo[internal/repository/headphone_impedance.go]
|
||||
SQL[sql/*.sql]
|
||||
end
|
||||
subgraph "工具模块"
|
||||
Response[internal/response/response.go]
|
||||
@@ -530,16 +754,24 @@ Main --> Config
|
||||
Main --> Router
|
||||
Main --> Cache
|
||||
Main --> Database
|
||||
Router --> Handler
|
||||
Router --> Task
|
||||
Handler --> Response
|
||||
Handler --> Cache
|
||||
Task --> Repository
|
||||
Task --> Model
|
||||
Task --> Model2
|
||||
Repository --> Database
|
||||
Repository --> Model
|
||||
Repository --> Model2
|
||||
Router --> DeviceHandler
|
||||
Router --> ImpedanceHandler
|
||||
Router --> DeviceTask
|
||||
Router --> ImpedanceTask
|
||||
DeviceHandler --> Response
|
||||
ImpedanceHandler --> Response
|
||||
DeviceHandler --> Cache
|
||||
ImpedanceHandler --> Cache
|
||||
DeviceTask --> DeviceRepo
|
||||
ImpedanceTask --> ImpedanceRepo
|
||||
DeviceTask --> UserDevice
|
||||
DeviceTask --> UserActive
|
||||
ImpedanceTask --> UserHeadphoneImpedance
|
||||
DeviceRepo --> Database
|
||||
ImpedanceRepo --> Database
|
||||
DeviceRepo --> UserDevice
|
||||
DeviceRepo --> UserActive
|
||||
ImpedanceRepo --> UserHeadphoneImpedance
|
||||
Router --> Logger
|
||||
Router --> CORS
|
||||
Cache --> RedisConfig
|
||||
@@ -547,27 +779,30 @@ Database --> DBConfig
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [cmd/server/main.go:27-76](file://cmd/server/main.go#L27-L76)
|
||||
- [cmd/server/main.go:27-106](file://cmd/server/main.go#L27-L106)
|
||||
- [internal/router/router.go:6-12](file://internal/router/router.go#L6-L12)
|
||||
|
||||
**章节来源**
|
||||
- [cmd/server/main.go:1-156](file://cmd/server/main.go#L1-L156)
|
||||
- [internal/config/config.go:1-64](file://internal/config/config.go#L1-L64)
|
||||
- [cmd/server/main.go:1-209](file://cmd/server/main.go#L1-L209)
|
||||
- [internal/config/config.go:1-97](file://internal/config/config.go#L1-L97)
|
||||
|
||||
## 性能考虑
|
||||
|
||||
### Redis 性能优化
|
||||
|
||||
1. **连接池管理**: 使用单个 Redis 客户端实例,避免频繁创建连接
|
||||
2. **内存使用**: 设备信息以 JSON 字符串形式存储,占用内存较小
|
||||
3. **键设计**: 使用简单的哈希结构,查询效率高
|
||||
2. **内存使用**: 设备信息和阻抗信息以 JSON 字符串形式存储,占用内存较小
|
||||
3. **键设计**: 使用简单的哈希结构,查询效率高;阻抗数据使用复合字段键提高去重效率
|
||||
4. **批量操作**: 持久化任务使用批量删除操作,提高Redis清理效率
|
||||
|
||||
### 数据库性能优化
|
||||
|
||||
1. **连接池配置**: MySQL连接池最大25个连接,空闲5个连接,连接生命周期5分钟
|
||||
2. **分区策略**: user_active表按日期分区,提高查询性能
|
||||
3. **索引优化**: 设备表使用唯一索引,活跃表使用复合索引
|
||||
3. **索引优化**:
|
||||
- 设备表使用唯一索引
|
||||
- 活跃表使用复合索引
|
||||
- 阻抗表使用唯一索引和辅助索引组合
|
||||
4. **批量写入**: 持久化任务支持批量数据写入
|
||||
|
||||
### 请求处理优化
|
||||
@@ -576,13 +811,14 @@ Database --> DBConfig
|
||||
2. **早期返回**: 在参数验证失败时立即返回错误
|
||||
3. **上下文传递**: 使用请求上下文进行异步操作
|
||||
4. **定时任务**: 使用ticker实现精确的5分钟定时执行
|
||||
5. **文本归一化**: 阻抗数据在入库前进行标准化处理,减少后续查询复杂度
|
||||
|
||||
### 监控建议
|
||||
|
||||
1. **Redis 监控**: 监控键数量、内存使用率、命令执行时间
|
||||
2. **数据库监控**: 监控连接数、查询性能、分区使用情况
|
||||
3. **应用监控**: 监控请求延迟、错误率、并发请求数
|
||||
4. **持久化监控**: 监控数据同步成功率、处理速度
|
||||
4. **持久化监控**: 监控数据同步成功率、处理速度、失败重试次数
|
||||
|
||||
## 故障排除指南
|
||||
|
||||
@@ -590,8 +826,8 @@ Database --> DBConfig
|
||||
|
||||
#### 参数验证失败
|
||||
**症状**: 返回 `{"code": 400, "msg": "参数校验失败"}`
|
||||
**原因**: 缺少必需参数 `mac` 或 `model`
|
||||
**解决方法**: 确保在请求中包含完整的参数
|
||||
**原因**: 缺少必需参数或参数格式不正确
|
||||
**解决方法**: 确保在请求中包含完整的参数,阻抗值必须是有效整数
|
||||
|
||||
#### Redis 连接错误
|
||||
**症状**: 返回 `{"code": 500, "msg": "系统错误"}`
|
||||
@@ -600,8 +836,8 @@ Database --> DBConfig
|
||||
|
||||
#### JSON 序列化错误
|
||||
**症状**: 返回 `{"code": 500, "msg": "系统错误"}`
|
||||
**原因**: 设备信息格式化过程中出现异常
|
||||
**解决方法**: 检查设备信息的数据类型和格式
|
||||
**原因**: 设备信息或阻抗信息格式化过程中出现异常
|
||||
**解决方法**: 检查数据的数据类型和格式
|
||||
|
||||
#### 数据库连接错误
|
||||
**症状**: 持久化任务日志显示数据库连接失败
|
||||
@@ -609,9 +845,14 @@ Database --> DBConfig
|
||||
**解决方法**: 检查数据库配置和网络连接
|
||||
|
||||
#### 数据同步失败
|
||||
**症状**: 持久化任务日志显示某些设备同步失败
|
||||
**症状**: 持久化任务日志显示某些记录同步失败
|
||||
**原因**: 数据库约束冲突或数据格式问题
|
||||
**解决方法**: 检查设备数据的完整性和格式
|
||||
**解决方法**: 检查数据的完整性和格式,特别是阻抗数据的唯一性约束
|
||||
|
||||
#### 阻抗数据重复问题
|
||||
**症状**: 数据库中同一耳机的多条记录
|
||||
**原因**: 品牌或型号文本不一致导致去重失败
|
||||
**解决方法**: 确认前端发送的品牌型号数据,系统会自动进行trim和lower处理
|
||||
|
||||
### 调试步骤
|
||||
|
||||
@@ -621,15 +862,19 @@ Database --> DBConfig
|
||||
4. **查看响应**: 分析接口返回的具体错误信息
|
||||
5. **检查数据库**: 验证数据库连接和表结构
|
||||
6. **监控持久化**: 查看持久化任务的执行状态和日志
|
||||
7. **检查配置**: 确认ENABLE_IMPEDANCE_PERSIST_TASK环境变量设置
|
||||
|
||||
**章节来源**
|
||||
- [internal/handler/device.go:33-39](file://internal/handler/device.go#L33-L39)
|
||||
- [internal/handler/impedance.go:51-66](file://internal/handler/impedance.go#L51-L66)
|
||||
- [internal/handler/device.go:69-76](file://internal/handler/device.go#L69-L76)
|
||||
- [internal/handler/impedance.go:83-101](file://internal/handler/impedance.go#L83-L101)
|
||||
- [internal/task/device_persist.go:43-46](file://internal/task/device_persist.go#L43-L46)
|
||||
- [internal/task/impedance_persist.go:48-51](file://internal/task/impedance_persist.go#L48-L51)
|
||||
|
||||
## 结论
|
||||
|
||||
设备管理接口提供了简单而高效的设备信息上报功能,并通过新增的设备持久化系统实现了数据的长期可靠存储。该系统结合了Redis的高性能缓存和MySQL的持久化存储,形成了完整的设备管理解决方案。
|
||||
设备管理接口提供了简单而高效的设备信息采集功能,并通过新增的耳机阻抗上报功能进一步完善了音频设备数据分析能力。系统结合了Redis的高性能缓存和MySQL的持久化存储,形成了完整的设备管理和阻抗数据采集解决方案。
|
||||
|
||||
### 主要优势
|
||||
|
||||
@@ -637,8 +882,10 @@ Database --> DBConfig
|
||||
2. **可靠性强**: 完善的错误处理和日志记录机制
|
||||
3. **性能优秀**: 使用 Redis 缓存,响应速度快;支持定时批量同步
|
||||
4. **扩展性强**: 基于 Gin 框架,便于功能扩展
|
||||
5. **数据持久化**: 通过定时任务确保数据的长期存储
|
||||
5. **数据持久化**: 通过双通道定时任务确保数据的长期存储
|
||||
6. **数据一致性**: 通过事务和幂等性设计保证数据一致性
|
||||
7. **智能去重**: 阻抗数据支持基于规范化文本的智能去重更新
|
||||
8. **文本标准化**: 自动处理品牌型号文本的标准化,确保数据质量
|
||||
|
||||
### 改进建议
|
||||
|
||||
@@ -647,5 +894,6 @@ Database --> DBConfig
|
||||
3. **API 版本控制**: 可以添加 API 版本号,便于后续升级
|
||||
4. **监控告警**: 可以添加更完善的监控和告警机制
|
||||
5. **数据备份**: 可以添加数据库备份和恢复机制
|
||||
6. **统计分析**: 可以添加阻抗数据的统计分析和报表功能
|
||||
|
||||
该接口为设备管理系统提供了坚实的基础,新增的持久化系统进一步增强了系统的可靠性和数据安全性,可以根据具体需求进一步扩展和完善。
|
||||
该接口为设备管理系统提供了坚实的基础,新增的阻抗上报功能和双通道持久化系统进一步增强了系统的可靠性和数据安全性,可以根据具体需求进一步扩展和完善。
|
||||
Reference in New Issue
Block a user