2026-05-27 18:07:55 +08:00
|
|
|
|
# 设备管理接口
|
|
|
|
|
|
|
|
|
|
|
|
<cite>
|
|
|
|
|
|
**本文引用的文件**
|
|
|
|
|
|
- [internal/handler/device.go](file://internal/handler/device.go)
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [internal/handler/impedance.go](file://internal/handler/impedance.go)
|
2026-05-27 18:07:55 +08:00
|
|
|
|
- [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)
|
2026-05-31 09:52:11 +08:00
|
|
|
|
- [internal/config/database.go](file://internal/config/database.go)
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [internal/config/config.go](file://internal/config/config.go)
|
2026-05-31 09:52:11 +08:00
|
|
|
|
- [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)
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [internal/model/user_headphone_impedance.go](file://internal/model/user_headphone_impedance.go)
|
2026-05-31 09:52:11 +08:00
|
|
|
|
- [internal/repository/device.go](file://internal/repository/device.go)
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [internal/repository/headphone_impedance.go](file://internal/repository/headphone_impedance.go)
|
2026-05-31 09:52:11 +08:00
|
|
|
|
- [internal/task/device_persist.go](file://internal/task/device_persist.go)
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [internal/task/impedance_persist.go](file://internal/task/impedance_persist.go)
|
2026-05-27 18:07:55 +08:00
|
|
|
|
- [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)
|
2026-05-31 09:52:11 +08:00
|
|
|
|
- [sql/user_device.sql](file://sql/user_device.sql)
|
|
|
|
|
|
- [sql/user_active.sql](file://sql/user_active.sql)
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [sql/user_headphone_impedance.sql](file://sql/user_headphone_impedance.sql)
|
2026-05-27 18:07:55 +08:00
|
|
|
|
- [README.md](file://README.md)
|
|
|
|
|
|
</cite>
|
|
|
|
|
|
|
2026-05-31 09:52:11 +08:00
|
|
|
|
## 更新摘要
|
|
|
|
|
|
**变更内容**
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- 新增耳机阻抗上报功能,提供完整的音频设备阻抗数据采集能力
|
|
|
|
|
|
- 实现独立的阻抗处理器、数据模型、仓库层和持久化任务
|
|
|
|
|
|
- 添加专用的数据库表结构和索引优化
|
|
|
|
|
|
- 支持品牌型号归一化和去重更新机制
|
|
|
|
|
|
- 保持与现有设备管理系统的一致性架构设计
|
2026-05-31 09:52:11 +08:00
|
|
|
|
|
2026-05-27 18:07:55 +08:00
|
|
|
|
## 目录
|
|
|
|
|
|
1. [简介](#简介)
|
|
|
|
|
|
2. [项目结构](#项目结构)
|
|
|
|
|
|
3. [核心组件](#核心组件)
|
|
|
|
|
|
4. [架构概览](#架构概览)
|
|
|
|
|
|
5. [详细组件分析](#详细组件分析)
|
2026-07-07 19:16:02 +08:00
|
|
|
|
6. [设备信息上报接口](#设备信息上报接口)
|
|
|
|
|
|
7. [耳机阻抗上报接口](#耳机阻抗上报接口)
|
|
|
|
|
|
8. [数据库持久化系统](#数据库持久化系统)
|
|
|
|
|
|
9. [依赖关系分析](#依赖关系分析)
|
|
|
|
|
|
10. [性能考虑](#性能考虑)
|
|
|
|
|
|
11. [故障排除指南](#故障排除指南)
|
|
|
|
|
|
12. [结论](#结论)
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
|
|
|
|
|
## 简介
|
|
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
本文档详细描述了设备管理接口的完整规范,包括设备信息上报接口和新增的耳机阻抗上报接口。该系统为音频设备提供了全面的采集和分析能力,支持设备基本信息上报和耳机阻抗数据采集,并通过Redis缓存和MySQL数据库实现了高性能的数据处理和长期持久化存储。
|
2026-05-31 09:52:11 +08:00
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
系统现已集成双通道持久化系统,通过DevicePersistTask和ImpedancePersistTask两个独立的任务组件,分别实现设备注册信息和耳机阻抗数据的定时同步,确保数据的可靠性和一致性。
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
|
|
|
|
|
## 项目结构
|
|
|
|
|
|
|
|
|
|
|
|
该项目采用分层架构设计,主要分为以下层次:
|
|
|
|
|
|
- **入口层**: `cmd/server/main.go` - 应用程序入口点,负责初始化配置、数据库连接、搜索引擎和缓存客户端
|
|
|
|
|
|
- **路由层**: `internal/router/router.go` - 路由定义和中间件配置
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- **处理器层**: `internal/handler/device.go` 和 `internal/handler/impedance.go` - 设备信息和阻抗信息上报业务逻辑
|
|
|
|
|
|
- **任务层**: `internal/task/device_persist.go` 和 `internal/task/impedance_persist.go` - 数据持久化任务
|
2026-05-27 18:07:55 +08:00
|
|
|
|
- **缓存层**: `internal/cache/redis.go` 和 `internal/config/redis.go` - Redis 客户端配置和连接管理
|
2026-05-31 09:52:11 +08:00
|
|
|
|
- **数据库层**: `internal/config/database.go`、`internal/database/mysql.go` - 数据库配置和连接管理
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- **模型层**: `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` - 数据访问层
|
2026-05-27 18:07:55 +08:00
|
|
|
|
- **响应层**: `internal/response/response.go` - 统一响应格式
|
|
|
|
|
|
- **中间件层**: `internal/middleware/logger.go` 和 `internal/middleware/cors.go` - 日志记录和跨域支持
|
|
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
|
graph TB
|
|
|
|
|
|
subgraph "应用入口"
|
|
|
|
|
|
Main[cmd/server/main.go]
|
|
|
|
|
|
end
|
|
|
|
|
|
subgraph "HTTP框架"
|
|
|
|
|
|
Gin[Gin Web框架]
|
|
|
|
|
|
Router[路由层]
|
|
|
|
|
|
Middleware[中间件层]
|
|
|
|
|
|
end
|
|
|
|
|
|
subgraph "业务处理"
|
|
|
|
|
|
DeviceHandler[设备处理器]
|
2026-07-07 19:16:02 +08:00
|
|
|
|
ImpedanceHandler[阻抗处理器]
|
2026-05-31 09:52:11 +08:00
|
|
|
|
DevicePersistTask[设备持久化任务]
|
2026-07-07 19:16:02 +08:00
|
|
|
|
ImpedancePersistTask[阻抗持久化任务]
|
2026-05-27 18:07:55 +08:00
|
|
|
|
Response[响应层]
|
|
|
|
|
|
end
|
|
|
|
|
|
subgraph "数据存储"
|
|
|
|
|
|
Redis[Redis缓存]
|
2026-05-31 09:52:11 +08:00
|
|
|
|
MySQL[MySQL数据库]
|
2026-05-27 18:07:55 +08:00
|
|
|
|
Config[配置管理]
|
|
|
|
|
|
end
|
2026-05-31 09:52:11 +08:00
|
|
|
|
subgraph "数据模型"
|
|
|
|
|
|
UserDevice[用户设备模型]
|
|
|
|
|
|
UserActive[用户活跃模型]
|
2026-07-07 19:16:02 +08:00
|
|
|
|
UserHeadphoneImpedance[用户耳机阻抗模型]
|
|
|
|
|
|
DeviceRepository[设备数据访问层]
|
|
|
|
|
|
ImpedanceRepository[阻抗数据访问层]
|
2026-05-31 09:52:11 +08:00
|
|
|
|
end
|
2026-05-27 18:07:55 +08:00
|
|
|
|
Main --> Gin
|
|
|
|
|
|
Gin --> Router
|
|
|
|
|
|
Router --> Middleware
|
|
|
|
|
|
Router --> DeviceHandler
|
2026-07-07 19:16:02 +08:00
|
|
|
|
Router --> ImpedanceHandler
|
2026-05-27 18:07:55 +08:00
|
|
|
|
DeviceHandler --> Response
|
2026-07-07 19:16:02 +08:00
|
|
|
|
ImpedanceHandler --> Response
|
2026-05-27 18:07:55 +08:00
|
|
|
|
DeviceHandler --> Redis
|
2026-07-07 19:16:02 +08:00
|
|
|
|
ImpedanceHandler --> Redis
|
2026-05-31 09:52:11 +08:00
|
|
|
|
DevicePersistTask --> MySQL
|
2026-07-07 19:16:02 +08:00
|
|
|
|
ImpedancePersistTask --> MySQL
|
2026-05-31 09:52:11 +08:00
|
|
|
|
DevicePersistTask --> Redis
|
2026-07-07 19:16:02 +08:00
|
|
|
|
ImpedancePersistTask --> Redis
|
|
|
|
|
|
DevicePersistTask --> DeviceRepository
|
|
|
|
|
|
ImpedancePersistTask --> ImpedanceRepository
|
|
|
|
|
|
DeviceRepository --> UserDevice
|
|
|
|
|
|
DeviceRepository --> UserActive
|
|
|
|
|
|
ImpedanceRepository --> UserHeadphoneImpedance
|
2026-05-27 18:07:55 +08:00
|
|
|
|
Main --> Config
|
|
|
|
|
|
Main --> Redis
|
2026-05-31 09:52:11 +08:00
|
|
|
|
Main --> MySQL
|
2026-05-27 18:07:55 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**图表来源**
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [cmd/server/main.go:27-106](file://cmd/server/main.go#L27-L106)
|
2026-05-31 09:52:11 +08:00
|
|
|
|
- [internal/handler/device.go:15-25](file://internal/handler/device.go#L15-L25)
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [internal/handler/impedance.go:18-28](file://internal/handler/impedance.go#L18-L28)
|
2026-05-31 09:52:11 +08:00
|
|
|
|
- [internal/task/device_persist.go:14-22](file://internal/task/device_persist.go#L14-L22)
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [internal/task/impedance_persist.go:16-28](file://internal/task/impedance_persist.go#L16-L28)
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
|
|
|
|
|
**章节来源**
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [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)
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
|
|
|
|
|
## 核心组件
|
|
|
|
|
|
|
|
|
|
|
|
### 设备信息上报接口
|
|
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
设备信息上报接口是本项目的核心功能之一,负责接收设备的基本信息并进行处理。该接口实现了以下关键功能:
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
|
|
|
|
|
- **参数验证**: 对必需参数进行验证,确保数据完整性
|
|
|
|
|
|
- **数据格式化**: 将设备信息转换为统一的 JSON 格式
|
|
|
|
|
|
- **缓存存储**: 使用 Redis Hash 结构存储设备信息
|
|
|
|
|
|
- **错误处理**: 提供统一的错误响应格式
|
|
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
### 耳机阻抗上报接口
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
新增的耳机阻抗上报接口专门用于采集用户的耳机阻抗数据,支持多品牌多型号的耳机管理。该接口具有以下特点:
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- **完整参数验证**: 验证MAC地址、设备型号、耳机品牌、型号和阻抗值
|
|
|
|
|
|
- **智能去重**: 基于MAC地址+耳机品牌+耳机型号的唯一性判断
|
|
|
|
|
|
- **文本归一化**: 自动去除空格并转换为小写,确保数据一致性
|
|
|
|
|
|
- **实时更新**: 同一条记录始终反映最新阻抗值,不保留历史版本
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
### 设备持久化任务
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
设备持久化任务组件负责将Redis中的设备信息定期同步到数据库中,实现数据的长期持久化存储。
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
### 阻抗持久化任务
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
新增的阻抗持久化任务组件专门处理耳机阻抗数据的持久化,具备完善的去重更新机制和错误恢复能力。
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
|
|
|
|
|
**章节来源**
|
2026-05-31 09:52:11 +08:00
|
|
|
|
- [internal/handler/device.go:27-83](file://internal/handler/device.go#L27-L83)
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [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)
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
|
|
|
|
|
## 架构概览
|
|
|
|
|
|
|
|
|
|
|
|
设备管理接口在整个系统架构中的位置如下:
|
|
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
|
sequenceDiagram
|
|
|
|
|
|
participant Client as 设备客户端
|
|
|
|
|
|
participant API as API网关
|
2026-07-07 19:16:02 +08:00
|
|
|
|
participant DeviceHandler as 设备处理器
|
|
|
|
|
|
participant ImpedanceHandler as 阻抗处理器
|
2026-05-27 18:07:55 +08:00
|
|
|
|
participant Redis as Redis缓存
|
2026-07-07 19:16:02 +08:00
|
|
|
|
participant DeviceTask as 设备持久化任务
|
|
|
|
|
|
participant ImpedanceTask as 阻抗持久化任务
|
2026-05-31 09:52:11 +08:00
|
|
|
|
participant DB as MySQL数据库
|
2026-05-27 18:07:55 +08:00
|
|
|
|
participant Logger as 日志系统
|
2026-07-07 19:16:02 +08:00
|
|
|
|
Note over Client,Logger : 设备信息上报流程
|
2026-05-27 18:07:55 +08:00
|
|
|
|
Client->>API : GET /audio/reportDevInfo?mac=&model=&ver=
|
2026-07-07 19:16:02 +08:00
|
|
|
|
API->>DeviceHandler : 调用 ReportDevInfo()
|
|
|
|
|
|
DeviceHandler->>DeviceHandler : 参数验证
|
2026-05-27 18:07:55 +08:00
|
|
|
|
alt 参数验证失败
|
2026-07-07 19:16:02 +08:00
|
|
|
|
DeviceHandler->>Client : 返回错误响应
|
2026-05-27 18:07:55 +08:00
|
|
|
|
else 参数验证成功
|
2026-07-07 19:16:02 +08:00
|
|
|
|
DeviceHandler->>DeviceHandler : 格式化设备信息
|
|
|
|
|
|
DeviceHandler->>Redis : HSET devices {mac} : {json_data}
|
2026-05-27 18:07:55 +08:00
|
|
|
|
alt Redis操作失败
|
2026-07-07 19:16:02 +08:00
|
|
|
|
DeviceHandler->>Logger : 记录错误日志
|
|
|
|
|
|
DeviceHandler->>Client : 返回系统错误
|
2026-05-27 18:07:55 +08:00
|
|
|
|
else Redis操作成功
|
2026-07-07 19:16:02 +08:00
|
|
|
|
DeviceHandler->>Client : 返回成功响应
|
2026-05-27 18:07:55 +08:00
|
|
|
|
end
|
|
|
|
|
|
end
|
2026-07-07 19:16:02 +08:00
|
|
|
|
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 (带去重逻辑)
|
2026-05-27 18:07:55 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**图表来源**
|
2026-05-31 09:52:11 +08:00
|
|
|
|
- [internal/handler/device.go:27-83](file://internal/handler/device.go#L27-L83)
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [internal/handler/impedance.go:43-107](file://internal/handler/impedance.go#L43-L107)
|
2026-05-31 09:52:11 +08:00
|
|
|
|
- [internal/task/device_persist.go:37-91](file://internal/task/device_persist.go#L37-L91)
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [internal/task/impedance_persist.go:43-108](file://internal/task/impedance_persist.go#L43-L108)
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
|
|
|
|
|
## 详细组件分析
|
|
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
### 处理器类图
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
|
classDiagram
|
|
|
|
|
|
class DeviceHandler {
|
|
|
|
|
|
-redis : redis.Client
|
|
|
|
|
|
-log : zap.Logger
|
|
|
|
|
|
+NewDeviceHandler(redis, log) DeviceHandler
|
|
|
|
|
|
+ReportDevInfo(c) void
|
|
|
|
|
|
}
|
2026-07-07 19:16:02 +08:00
|
|
|
|
class ImpedanceHandler {
|
|
|
|
|
|
-redis : redis.Client
|
|
|
|
|
|
-log : zap.Logger
|
|
|
|
|
|
+NewImpedanceHandler(redis, log) ImpedanceHandler
|
|
|
|
|
|
+ReportImpedance(c) void
|
|
|
|
|
|
+normalizeHeadphoneText(s) string
|
|
|
|
|
|
+impedanceRedisField(mac, brandNorm, modelNorm) string
|
|
|
|
|
|
}
|
2026-05-31 09:52:11 +08:00
|
|
|
|
class DevicePersistTask {
|
|
|
|
|
|
-rdb : redis.Client
|
|
|
|
|
|
-repo : DeviceRepository
|
|
|
|
|
|
-log : zap.Logger
|
|
|
|
|
|
+NewDevicePersistTask(rdb, repo, log) DevicePersistTask
|
|
|
|
|
|
+Start(interval) void
|
|
|
|
|
|
+Persist() void
|
2026-07-07 19:16:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
class ImpedancePersistTask {
|
|
|
|
|
|
-rdb : redis.Client
|
|
|
|
|
|
-repo : HeadphoneImpedanceRepository
|
|
|
|
|
|
-log : zap.Logger
|
|
|
|
|
|
+NewImpedancePersistTask(rdb, repo, log) ImpedancePersistTask
|
|
|
|
|
|
+Start(interval) void
|
|
|
|
|
|
+Persist() void
|
|
|
|
|
|
+persistOne(ctx, info) bool
|
2026-05-31 09:52:11 +08:00
|
|
|
|
}
|
2026-05-27 18:07:55 +08:00
|
|
|
|
class RedisClient {
|
|
|
|
|
|
+HSet(ctx, key, field, value) error
|
2026-05-31 09:52:11 +08:00
|
|
|
|
+HGetAll(ctx, key) map[string]string
|
|
|
|
|
|
+HDel(ctx, key, fields) error
|
2026-05-27 18:07:55 +08:00
|
|
|
|
+Close() error
|
|
|
|
|
|
}
|
|
|
|
|
|
class Logger {
|
|
|
|
|
|
+Info(message, fields) void
|
|
|
|
|
|
+Error(message, error) void
|
|
|
|
|
|
}
|
|
|
|
|
|
DeviceHandler --> RedisClient : 使用
|
2026-07-07 19:16:02 +08:00
|
|
|
|
ImpedanceHandler --> RedisClient : 使用
|
2026-05-27 18:07:55 +08:00
|
|
|
|
DeviceHandler --> Logger : 记录日志
|
2026-07-07 19:16:02 +08:00
|
|
|
|
ImpedanceHandler --> Logger : 记录日志
|
2026-05-31 09:52:11 +08:00
|
|
|
|
DevicePersistTask --> RedisClient : 读取数据
|
2026-07-07 19:16:02 +08:00
|
|
|
|
ImpedancePersistTask --> RedisClient : 读取数据
|
2026-05-31 09:52:11 +08:00
|
|
|
|
DevicePersistTask --> Logger : 记录日志
|
2026-07-07 19:16:02 +08:00
|
|
|
|
ImpedancePersistTask --> Logger : 记录日志
|
2026-05-27 18:07:55 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**图表来源**
|
2026-05-31 09:52:11 +08:00
|
|
|
|
- [internal/handler/device.go:15-25](file://internal/handler/device.go#L15-L25)
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [internal/handler/impedance.go:18-28](file://internal/handler/impedance.go#L18-L28)
|
2026-05-31 09:52:11 +08:00
|
|
|
|
- [internal/task/device_persist.go:14-22](file://internal/task/device_persist.go#L14-L22)
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [internal/task/impedance_persist.go:16-28](file://internal/task/impedance_persist.go#L16-L28)
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
### 参数验证流程对比
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
#### 设备信息上报参数验证流程
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
|
flowchart TD
|
|
|
|
|
|
Start([开始处理请求]) --> GetParams["获取查询参数<br/>mac, model, ver"]
|
|
|
|
|
|
GetParams --> TrimParams["去除空白字符"]
|
|
|
|
|
|
TrimParams --> ValidateMac{"mac是否为空?"}
|
|
|
|
|
|
ValidateMac --> |是| ReturnError["返回参数校验失败"]
|
|
|
|
|
|
ValidateMac --> |否| ValidateModel{"model是否为空?"}
|
|
|
|
|
|
ValidateModel --> |是| ReturnError
|
|
|
|
|
|
ValidateModel --> |否| CheckVer{"ver是否为空?"}
|
|
|
|
|
|
CheckVer --> |是| SetEmptyVer["设置ver为空字符串"]
|
|
|
|
|
|
CheckVer --> |否| GetIP["获取X-Forwarded-For头部"]
|
|
|
|
|
|
SetEmptyVer --> GetIP
|
|
|
|
|
|
GetIP --> LogInfo["记录日志信息"]
|
|
|
|
|
|
LogInfo --> 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
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
#### 阻抗信息上报参数验证流程
|
|
|
|
|
|
|
|
|
|
|
|
```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
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-05-27 18:07:55 +08:00
|
|
|
|
**图表来源**
|
2026-05-31 09:52:11 +08:00
|
|
|
|
- [internal/handler/device.go:27-83](file://internal/handler/device.go#L27-L83)
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [internal/handler/impedance.go:43-107](file://internal/handler/impedance.go#L43-L107)
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
|
|
|
|
|
### Redis 缓存交互
|
|
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
系统使用两个独立的Redis Hash结构来存储不同类型的数据:
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
#### 设备信息存储
|
2026-05-27 18:07:55 +08:00
|
|
|
|
- **键名**: `devices`
|
|
|
|
|
|
- **字段**: 设备 MAC 地址
|
|
|
|
|
|
- **值**: JSON 格式的设备信息对象
|
|
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
#### 阻抗信息存储
|
|
|
|
|
|
- **键名**: `headphone_impedances`
|
|
|
|
|
|
- **字段**: `mac_addr|headphone_brand_norm|headphone_model_norm`
|
|
|
|
|
|
- **值**: JSON 格式的阻抗信息对象
|
|
|
|
|
|
|
2026-05-27 18:07:55 +08:00
|
|
|
|
存储的数据结构示例:
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"mac_addr": "00:1A:2B:3C:4D:5E",
|
2026-07-07 19:16:02 +08:00
|
|
|
|
"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"
|
2026-05-27 18:07:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**章节来源**
|
2026-05-31 09:52:11 +08:00
|
|
|
|
- [internal/handler/device.go:50-77](file://internal/handler/device.go#L50-L77)
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [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 请求规范
|
|
|
|
|
|
|
|
|
|
|
|
- **方法**: GET
|
|
|
|
|
|
- **路径**: `/audio/reportDevInfo`
|
|
|
|
|
|
- **协议**: HTTP/1.1
|
|
|
|
|
|
- **内容类型**: application/x-www-form-urlencoded
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
### 请求参数
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
| 参数名 | 类型 | 必填 | 描述 | 示例 |
|
|
|
|
|
|
|--------|------|------|------|------|
|
|
|
|
|
|
| mac | string | 是 | 设备 MAC 地址 | `00:1A:2B:3C:4D:5E` |
|
|
|
|
|
|
| model | string | 是 | 设备型号 | `ES900` |
|
|
|
|
|
|
| ver | string | 否 | 设备版本号 | `v2.1.0` |
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
### 请求头
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
| 头部名称 | 描述 | 示例 |
|
|
|
|
|
|
|----------|------|------|
|
|
|
|
|
|
| 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: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地址和更新时间
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
|
|
|
|
|
**章节来源**
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [internal/handler/impedance.go:30-107](file://internal/handler/impedance.go#L30-L107)
|
|
|
|
|
|
- [internal/router/router.go:70](file://internal/router/router.go#L70)
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
2026-05-31 09:52:11 +08:00
|
|
|
|
## 数据库持久化系统
|
|
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
### 双通道持久化架构
|
2026-05-31 09:52:11 +08:00
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
系统实现了两个独立的持久化任务,分别处理设备信息和阻抗信息:
|
2026-05-31 09:52:11 +08:00
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
|
flowchart TD
|
|
|
|
|
|
Start([启动持久化任务]) --> Timer["5分钟定时器"]
|
2026-07-07 19:16:02 +08:00
|
|
|
|
Timer --> GetData["从Redis读取数据"]
|
2026-05-31 09:52:11 +08:00
|
|
|
|
GetData --> ParseData["解析JSON数据"]
|
2026-07-07 19:16:02 +08:00
|
|
|
|
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
|
2026-05-31 09:52:11 +08:00
|
|
|
|
Success --> |是| BatchDelete["批量删除已处理记录"]
|
|
|
|
|
|
Success --> |否| LogError["记录错误日志"]
|
|
|
|
|
|
BatchDelete --> Complete["完成同步"]
|
|
|
|
|
|
LogError --> Complete
|
|
|
|
|
|
Complete --> Timer
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**图表来源**
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [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)
|
2026-05-31 09:52:11 +08:00
|
|
|
|
- [internal/task/device_persist.go:37-91](file://internal/task/device_persist.go#L37-L91)
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [internal/task/impedance_persist.go:43-108](file://internal/task/impedance_persist.go#L43-L108)
|
2026-05-31 09:52:11 +08:00
|
|
|
|
|
|
|
|
|
|
### 数据库表结构
|
|
|
|
|
|
|
|
|
|
|
|
#### 用户设备表 (user_device)
|
|
|
|
|
|
|
|
|
|
|
|
| 字段名 | 类型 | 约束 | 描述 |
|
|
|
|
|
|
|--------|------|------|------|
|
|
|
|
|
|
| id | int | PRIMARY KEY, AUTO_INCREMENT | 主键ID |
|
|
|
|
|
|
| mac_addr | varchar(255) | UNIQUE, NOT NULL | 设备MAC地址 |
|
|
|
|
|
|
| model | varchar(255) | NOT NULL | 设备型号 |
|
|
|
|
|
|
| add_time | datetime | DEFAULT CURRENT_TIMESTAMP | 添加时间 |
|
|
|
|
|
|
| ver | varchar(10) | NULL | 设备版本号 |
|
|
|
|
|
|
|
|
|
|
|
|
#### 用户活跃表 (user_active)
|
|
|
|
|
|
|
|
|
|
|
|
| 字段名 | 类型 | 约束 | 描述 |
|
|
|
|
|
|
|--------|------|------|------|
|
|
|
|
|
|
| id | int | PRIMARY KEY, AUTO_INCREMENT | 主键ID |
|
|
|
|
|
|
| mac_addr | varchar(100) | NOT NULL | 设备MAC地址 |
|
|
|
|
|
|
| model | varchar(50) | NOT NULL | 设备型号 |
|
|
|
|
|
|
| active_date | date | NOT NULL | 活跃日期 |
|
|
|
|
|
|
| ip_addr | varchar(100) | NOT NULL | IP地址 |
|
|
|
|
|
|
| create_at | datetime | DEFAULT CURRENT_TIMESTAMP | 创建时间 |
|
|
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
#### 用户耳机阻抗表 (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)`
|
|
|
|
|
|
|
2026-05-31 09:52:11 +08:00
|
|
|
|
**章节来源**
|
|
|
|
|
|
- [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)
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [sql/user_headphone_impedance.sql:20-36](file://sql/user_headphone_impedance.sql#L20-L36)
|
2026-05-31 09:52:11 +08:00
|
|
|
|
|
|
|
|
|
|
### 数据同步流程
|
|
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
#### 设备信息同步流程
|
2026-05-31 09:52:11 +08:00
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
|
sequenceDiagram
|
|
|
|
|
|
participant Redis as Redis缓存
|
2026-07-07 19:16:02 +08:00
|
|
|
|
participant DeviceTask as 设备持久化任务
|
|
|
|
|
|
participant Repo as 设备数据访问层
|
2026-05-31 09:52:11 +08:00
|
|
|
|
participant DB as MySQL数据库
|
2026-07-07 19:16:02 +08:00
|
|
|
|
DeviceTask->>Redis : HGetAll devices
|
|
|
|
|
|
Redis->>DeviceTask : 返回设备数据
|
|
|
|
|
|
DeviceTask->>DeviceTask : 解析JSON数据
|
|
|
|
|
|
DeviceTask->>DeviceTask : 修复日期格式
|
|
|
|
|
|
DeviceTask->>Repo : 查找设备信息
|
2026-05-31 09:52:11 +08:00
|
|
|
|
Repo->>DB : 查询user_device
|
|
|
|
|
|
DB->>Repo : 返回查询结果
|
|
|
|
|
|
alt 设备不存在
|
2026-07-07 19:16:02 +08:00
|
|
|
|
DeviceTask->>Repo : 插入新设备
|
2026-05-31 09:52:11 +08:00
|
|
|
|
Repo->>DB : INSERT user_device
|
|
|
|
|
|
DB->>Repo : 返回插入结果
|
|
|
|
|
|
else 设备存在
|
2026-07-07 19:16:02 +08:00
|
|
|
|
DeviceTask->>Repo : 更新设备版本
|
2026-05-31 09:52:11 +08:00
|
|
|
|
Repo->>DB : UPDATE user_device
|
|
|
|
|
|
DB->>Repo : 返回更新结果
|
|
|
|
|
|
end
|
2026-07-07 19:16:02 +08:00
|
|
|
|
DeviceTask->>Repo : 查找活跃信息
|
2026-05-31 09:52:11 +08:00
|
|
|
|
Repo->>DB : 查询user_active
|
|
|
|
|
|
DB->>Repo : 返回查询结果
|
|
|
|
|
|
alt 活跃信息不存在
|
2026-07-07 19:16:02 +08:00
|
|
|
|
DeviceTask->>Repo : 插入新活跃记录
|
2026-05-31 09:52:11 +08:00
|
|
|
|
Repo->>DB : INSERT user_active
|
|
|
|
|
|
DB->>Repo : 返回插入结果
|
|
|
|
|
|
else 活跃信息存在
|
2026-07-07 19:16:02 +08:00
|
|
|
|
DeviceTask->>Repo : 更新IP地址
|
2026-05-31 09:52:11 +08:00
|
|
|
|
Repo->>DB : UPDATE user_active
|
|
|
|
|
|
DB->>Repo : 返回更新结果
|
|
|
|
|
|
end
|
2026-07-07 19:16:02 +08:00
|
|
|
|
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 成功记录
|
2026-05-31 09:52:11 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**图表来源**
|
|
|
|
|
|
- [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)
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [internal/task/impedance_persist.go:110-162](file://internal/task/impedance_persist.go#L110-L162)
|
2026-05-31 09:52:11 +08:00
|
|
|
|
|
|
|
|
|
|
### 数据一致性保证
|
|
|
|
|
|
|
|
|
|
|
|
系统通过以下机制确保数据一致性:
|
|
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
1. **原子性**: 使用数据库事务确保相关操作的原子性
|
2026-05-31 09:52:11 +08:00
|
|
|
|
2. **幂等性**: 支持重复上报的处理,避免数据重复
|
|
|
|
|
|
3. **回滚机制**: 当部分操作失败时,系统会记录错误并继续处理其他记录
|
|
|
|
|
|
4. **数据修复**: 自动修复历史数据中的无效日期格式
|
2026-07-07 19:16:02 +08:00
|
|
|
|
5. **智能去重**: 阻抗数据基于规范化后的品牌型号进行唯一性判断
|
2026-05-31 09:52:11 +08:00
|
|
|
|
|
|
|
|
|
|
**章节来源**
|
|
|
|
|
|
- [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)
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [internal/task/impedance_persist.go:120-162](file://internal/task/impedance_persist.go#L120-L162)
|
2026-05-31 09:52:11 +08:00
|
|
|
|
|
2026-05-27 18:07:55 +08:00
|
|
|
|
## 依赖关系分析
|
|
|
|
|
|
|
|
|
|
|
|
### 外部依赖
|
|
|
|
|
|
|
|
|
|
|
|
项目的主要外部依赖包括:
|
|
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
|
graph LR
|
|
|
|
|
|
subgraph "Go标准库"
|
|
|
|
|
|
StdLib[标准库]
|
|
|
|
|
|
end
|
|
|
|
|
|
subgraph "第三方库"
|
|
|
|
|
|
Gin[Gin Web框架]
|
|
|
|
|
|
Redis[Redis客户端]
|
2026-05-31 09:52:11 +08:00
|
|
|
|
MySQL[MySQL驱动]
|
2026-05-27 18:07:55 +08:00
|
|
|
|
Zap[Zap日志库]
|
2026-05-31 09:52:11 +08:00
|
|
|
|
Time[时间处理]
|
|
|
|
|
|
Context[上下文]
|
|
|
|
|
|
End
|
2026-05-27 18:07:55 +08:00
|
|
|
|
subgraph "内部模块"
|
|
|
|
|
|
Handler[处理器层]
|
2026-05-31 09:52:11 +08:00
|
|
|
|
Task[任务层]
|
2026-05-27 18:07:55 +08:00
|
|
|
|
Cache[缓存层]
|
2026-05-31 09:52:11 +08:00
|
|
|
|
Database[数据库层]
|
|
|
|
|
|
Model[模型层]
|
|
|
|
|
|
Repository[仓库层]
|
2026-05-27 18:07:55 +08:00
|
|
|
|
Response[响应层]
|
|
|
|
|
|
Middleware[中间件层]
|
|
|
|
|
|
end
|
|
|
|
|
|
Handler --> Gin
|
|
|
|
|
|
Handler --> Redis
|
2026-05-31 09:52:11 +08:00
|
|
|
|
Handler --> Context
|
|
|
|
|
|
Task --> Redis
|
|
|
|
|
|
Task --> MySQL
|
|
|
|
|
|
Task --> Time
|
|
|
|
|
|
Task --> Model
|
|
|
|
|
|
Task --> Repository
|
2026-05-27 18:07:55 +08:00
|
|
|
|
Cache --> Redis
|
2026-05-31 09:52:11 +08:00
|
|
|
|
Database --> MySQL
|
|
|
|
|
|
Repository --> Database
|
2026-05-27 18:07:55 +08:00
|
|
|
|
Response --> Gin
|
|
|
|
|
|
Middleware --> Gin
|
|
|
|
|
|
Middleware --> Zap
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**图表来源**
|
2026-05-31 09:52:11 +08:00
|
|
|
|
- [internal/handler/device.go:3-13](file://internal/handler/device.go#L3-L13)
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [internal/handler/impedance.go:3-14](file://internal/handler/impedance.go#L3-L14)
|
2026-05-31 09:52:11 +08:00
|
|
|
|
- [internal/task/device_persist.go:3-12](file://internal/task/device_persist.go#L3-L12)
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [internal/task/impedance_persist.go:3-12](file://internal/task/impedance_persist.go#L3-L12)
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
|
|
|
|
|
### 内部模块依赖
|
|
|
|
|
|
|
|
|
|
|
|
```mermaid
|
|
|
|
|
|
graph TB
|
|
|
|
|
|
subgraph "入口模块"
|
|
|
|
|
|
Main[cmd/server/main.go]
|
|
|
|
|
|
end
|
|
|
|
|
|
subgraph "配置模块"
|
|
|
|
|
|
Config[internal/config/config.go]
|
|
|
|
|
|
RedisConfig[internal/config/redis.go]
|
2026-05-31 09:52:11 +08:00
|
|
|
|
DBConfig[internal/config/database.go]
|
2026-05-27 18:07:55 +08:00
|
|
|
|
end
|
|
|
|
|
|
subgraph "服务模块"
|
|
|
|
|
|
Router[internal/router/router.go]
|
2026-07-07 19:16:02 +08:00
|
|
|
|
DeviceHandler[internal/handler/device.go]
|
|
|
|
|
|
ImpedanceHandler[internal/handler/impedance.go]
|
|
|
|
|
|
DeviceTask[internal/task/device_persist.go]
|
|
|
|
|
|
ImpedanceTask[internal/task/impedance_persist.go]
|
2026-05-27 18:07:55 +08:00
|
|
|
|
Cache[internal/cache/redis.go]
|
2026-05-31 09:52:11 +08:00
|
|
|
|
Database[internal/database/mysql.go]
|
|
|
|
|
|
end
|
|
|
|
|
|
subgraph "数据模块"
|
2026-07-07 19:16:02 +08:00
|
|
|
|
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]
|
2026-05-27 18:07:55 +08:00
|
|
|
|
end
|
|
|
|
|
|
subgraph "工具模块"
|
|
|
|
|
|
Response[internal/response/response.go]
|
|
|
|
|
|
Logger[internal/middleware/logger.go]
|
|
|
|
|
|
CORS[internal/middleware/cors.go]
|
|
|
|
|
|
end
|
|
|
|
|
|
Main --> Config
|
|
|
|
|
|
Main --> Router
|
|
|
|
|
|
Main --> Cache
|
2026-05-31 09:52:11 +08:00
|
|
|
|
Main --> Database
|
2026-07-07 19:16:02 +08:00
|
|
|
|
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
|
2026-05-27 18:07:55 +08:00
|
|
|
|
Router --> Logger
|
|
|
|
|
|
Router --> CORS
|
|
|
|
|
|
Cache --> RedisConfig
|
2026-05-31 09:52:11 +08:00
|
|
|
|
Database --> DBConfig
|
2026-05-27 18:07:55 +08:00
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**图表来源**
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [cmd/server/main.go:27-106](file://cmd/server/main.go#L27-L106)
|
2026-05-27 18:07:55 +08:00
|
|
|
|
- [internal/router/router.go:6-12](file://internal/router/router.go#L6-L12)
|
|
|
|
|
|
|
|
|
|
|
|
**章节来源**
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [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)
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
|
|
|
|
|
## 性能考虑
|
|
|
|
|
|
|
|
|
|
|
|
### Redis 性能优化
|
|
|
|
|
|
|
|
|
|
|
|
1. **连接池管理**: 使用单个 Redis 客户端实例,避免频繁创建连接
|
2026-07-07 19:16:02 +08:00
|
|
|
|
2. **内存使用**: 设备信息和阻抗信息以 JSON 字符串形式存储,占用内存较小
|
|
|
|
|
|
3. **键设计**: 使用简单的哈希结构,查询效率高;阻抗数据使用复合字段键提高去重效率
|
2026-05-31 09:52:11 +08:00
|
|
|
|
4. **批量操作**: 持久化任务使用批量删除操作,提高Redis清理效率
|
|
|
|
|
|
|
|
|
|
|
|
### 数据库性能优化
|
|
|
|
|
|
|
|
|
|
|
|
1. **连接池配置**: MySQL连接池最大25个连接,空闲5个连接,连接生命周期5分钟
|
|
|
|
|
|
2. **分区策略**: user_active表按日期分区,提高查询性能
|
2026-07-07 19:16:02 +08:00
|
|
|
|
3. **索引优化**:
|
|
|
|
|
|
- 设备表使用唯一索引
|
|
|
|
|
|
- 活跃表使用复合索引
|
|
|
|
|
|
- 阻抗表使用唯一索引和辅助索引组合
|
2026-05-31 09:52:11 +08:00
|
|
|
|
4. **批量写入**: 持久化任务支持批量数据写入
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
|
|
|
|
|
### 请求处理优化
|
|
|
|
|
|
|
|
|
|
|
|
1. **参数预处理**: 使用 `strings.TrimSpace()` 去除多余空白字符
|
|
|
|
|
|
2. **早期返回**: 在参数验证失败时立即返回错误
|
|
|
|
|
|
3. **上下文传递**: 使用请求上下文进行异步操作
|
2026-05-31 09:52:11 +08:00
|
|
|
|
4. **定时任务**: 使用ticker实现精确的5分钟定时执行
|
2026-07-07 19:16:02 +08:00
|
|
|
|
5. **文本归一化**: 阻抗数据在入库前进行标准化处理,减少后续查询复杂度
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
|
|
|
|
|
### 监控建议
|
|
|
|
|
|
|
|
|
|
|
|
1. **Redis 监控**: 监控键数量、内存使用率、命令执行时间
|
2026-05-31 09:52:11 +08:00
|
|
|
|
2. **数据库监控**: 监控连接数、查询性能、分区使用情况
|
|
|
|
|
|
3. **应用监控**: 监控请求延迟、错误率、并发请求数
|
2026-07-07 19:16:02 +08:00
|
|
|
|
4. **持久化监控**: 监控数据同步成功率、处理速度、失败重试次数
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
|
|
|
|
|
## 故障排除指南
|
|
|
|
|
|
|
|
|
|
|
|
### 常见问题及解决方案
|
|
|
|
|
|
|
|
|
|
|
|
#### 参数验证失败
|
|
|
|
|
|
**症状**: 返回 `{"code": 400, "msg": "参数校验失败"}`
|
2026-07-07 19:16:02 +08:00
|
|
|
|
**原因**: 缺少必需参数或参数格式不正确
|
|
|
|
|
|
**解决方法**: 确保在请求中包含完整的参数,阻抗值必须是有效整数
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
|
|
|
|
|
#### Redis 连接错误
|
|
|
|
|
|
**症状**: 返回 `{"code": 500, "msg": "系统错误"}`
|
|
|
|
|
|
**原因**: Redis 服务器不可达或认证失败
|
|
|
|
|
|
**解决方法**: 检查 Redis 配置和网络连接
|
|
|
|
|
|
|
|
|
|
|
|
#### JSON 序列化错误
|
|
|
|
|
|
**症状**: 返回 `{"code": 500, "msg": "系统错误"}`
|
2026-07-07 19:16:02 +08:00
|
|
|
|
**原因**: 设备信息或阻抗信息格式化过程中出现异常
|
|
|
|
|
|
**解决方法**: 检查数据的数据类型和格式
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
2026-05-31 09:52:11 +08:00
|
|
|
|
#### 数据库连接错误
|
|
|
|
|
|
**症状**: 持久化任务日志显示数据库连接失败
|
|
|
|
|
|
**原因**: 数据库服务器不可达或配置错误
|
|
|
|
|
|
**解决方法**: 检查数据库配置和网络连接
|
|
|
|
|
|
|
|
|
|
|
|
#### 数据同步失败
|
2026-07-07 19:16:02 +08:00
|
|
|
|
**症状**: 持久化任务日志显示某些记录同步失败
|
2026-05-31 09:52:11 +08:00
|
|
|
|
**原因**: 数据库约束冲突或数据格式问题
|
2026-07-07 19:16:02 +08:00
|
|
|
|
**解决方法**: 检查数据的完整性和格式,特别是阻抗数据的唯一性约束
|
|
|
|
|
|
|
|
|
|
|
|
#### 阻抗数据重复问题
|
|
|
|
|
|
**症状**: 数据库中同一耳机的多条记录
|
|
|
|
|
|
**原因**: 品牌或型号文本不一致导致去重失败
|
|
|
|
|
|
**解决方法**: 确认前端发送的品牌型号数据,系统会自动进行trim和lower处理
|
2026-05-31 09:52:11 +08:00
|
|
|
|
|
2026-05-27 18:07:55 +08:00
|
|
|
|
### 调试步骤
|
|
|
|
|
|
|
|
|
|
|
|
1. **启用详细日志**: 检查日志中间件输出的请求信息
|
|
|
|
|
|
2. **验证参数**: 确认请求参数的完整性和正确性
|
|
|
|
|
|
3. **测试 Redis**: 验证 Redis 服务的可用性和权限设置
|
|
|
|
|
|
4. **查看响应**: 分析接口返回的具体错误信息
|
2026-05-31 09:52:11 +08:00
|
|
|
|
5. **检查数据库**: 验证数据库连接和表结构
|
|
|
|
|
|
6. **监控持久化**: 查看持久化任务的执行状态和日志
|
2026-07-07 19:16:02 +08:00
|
|
|
|
7. **检查配置**: 确认ENABLE_IMPEDANCE_PERSIST_TASK环境变量设置
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
|
|
|
|
|
**章节来源**
|
2026-05-31 09:52:11 +08:00
|
|
|
|
- [internal/handler/device.go:33-39](file://internal/handler/device.go#L33-L39)
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [internal/handler/impedance.go:51-66](file://internal/handler/impedance.go#L51-L66)
|
2026-05-31 09:52:11 +08:00
|
|
|
|
- [internal/handler/device.go:69-76](file://internal/handler/device.go#L69-L76)
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [internal/handler/impedance.go:83-101](file://internal/handler/impedance.go#L83-L101)
|
2026-05-31 09:52:11 +08:00
|
|
|
|
- [internal/task/device_persist.go:43-46](file://internal/task/device_persist.go#L43-L46)
|
2026-07-07 19:16:02 +08:00
|
|
|
|
- [internal/task/impedance_persist.go:48-51](file://internal/task/impedance_persist.go#L48-L51)
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
|
|
|
|
|
## 结论
|
|
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
设备管理接口提供了简单而高效的设备信息采集功能,并通过新增的耳机阻抗上报功能进一步完善了音频设备数据分析能力。系统结合了Redis的高性能缓存和MySQL的持久化存储,形成了完整的设备管理和阻抗数据采集解决方案。
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
|
|
|
|
|
### 主要优势
|
|
|
|
|
|
|
|
|
|
|
|
1. **简洁明了**: 接口设计简单,易于理解和使用
|
|
|
|
|
|
2. **可靠性强**: 完善的错误处理和日志记录机制
|
2026-05-31 09:52:11 +08:00
|
|
|
|
3. **性能优秀**: 使用 Redis 缓存,响应速度快;支持定时批量同步
|
2026-05-27 18:07:55 +08:00
|
|
|
|
4. **扩展性强**: 基于 Gin 框架,便于功能扩展
|
2026-07-07 19:16:02 +08:00
|
|
|
|
5. **数据持久化**: 通过双通道定时任务确保数据的长期存储
|
2026-05-31 09:52:11 +08:00
|
|
|
|
6. **数据一致性**: 通过事务和幂等性设计保证数据一致性
|
2026-07-07 19:16:02 +08:00
|
|
|
|
7. **智能去重**: 阻抗数据支持基于规范化文本的智能去重更新
|
|
|
|
|
|
8. **文本标准化**: 自动处理品牌型号文本的标准化,确保数据质量
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
|
|
|
|
|
### 改进建议
|
|
|
|
|
|
|
|
|
|
|
|
1. **数据验证增强**: 可以添加更严格的数据格式验证
|
|
|
|
|
|
2. **限流机制**: 可以添加请求频率限制,防止恶意刷取
|
2026-05-31 09:52:11 +08:00
|
|
|
|
3. **API 版本控制**: 可以添加 API 版本号,便于后续升级
|
|
|
|
|
|
4. **监控告警**: 可以添加更完善的监控和告警机制
|
|
|
|
|
|
5. **数据备份**: 可以添加数据库备份和恢复机制
|
2026-07-07 19:16:02 +08:00
|
|
|
|
6. **统计分析**: 可以添加阻抗数据的统计分析和报表功能
|
2026-05-27 18:07:55 +08:00
|
|
|
|
|
2026-07-07 19:16:02 +08:00
|
|
|
|
该接口为设备管理系统提供了坚实的基础,新增的阻抗上报功能和双通道持久化系统进一步增强了系统的可靠性和数据安全性,可以根据具体需求进一步扩展和完善。
|