feat(impedance): 新增用户耳机阻抗功能模块

- 设计并添加 user_headphone_impedance 表,包含唯一复合索引保证数据完整性
- 实现阻抗上报API接口 /audio/reportImpedance,支持设备上传阻抗数据
- 使用Redis Hash结构缓存阻抗数据,键为 "headphone_impedances"
- 新增定时持久化任务,每5分钟从Redis读取数据批量写入数据库
- 对品牌和型号数据进行trim+lower归一化处理,支持数据去重更新
- 优化查询性能,确保mac_addr与品牌型号归一化组合唯一索引生效
- 更新故障排查指南,新增Redis缓存及唯一索引相关问题检查
- 新增阻抗持久化配置开关,允许按需启用该功能
This commit is contained in:
eafonyang
2026-07-07 19:16:02 +08:00
parent 5d2910e89e
commit c1547f9a8f
8 changed files with 1567 additions and 398 deletions
@@ -9,12 +9,14 @@
- [internal/model/target.go](file://internal/model/target.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/brand.go](file://internal/repository/brand.go)
- [internal/repository/model.go](file://internal/repository/model.go)
- [internal/repository/ota.go](file://internal/repository/ota.go)
- [internal/repository/share_code.go](file://internal/repository/share_code.go)
- [internal/repository/curve.go](file://internal/repository/curve.go)
- [internal/repository/device.go](file://internal/repository/device.go)
- [internal/repository/headphone_impedance.go](file://internal/repository/headphone_impedance.go)
- [internal/handler/brand.go](file://internal/handler/brand.go)
- [internal/handler/model.go](file://internal/handler/model.go)
- [internal/handler/model_list.go](file://internal/handler/model_list.go)
@@ -22,12 +24,15 @@
- [internal/handler/share_code.go](file://internal/handler/share_code.go)
- [internal/handler/device.go](file://internal/handler/device.go)
- [internal/handler/curve.go](file://internal/handler/curve.go)
- [internal/handler/impedance.go](file://internal/handler/impedance.go)
- [internal/router/router.go](file://internal/router/router.go)
- [internal/config/database.go](file://internal/config/database.go)
- [internal/config/share_code_ttl.go](file://internal/config/share_code_ttl.go)
- [internal/config/config.go](file://internal/config/config.go)
- [internal/response/response.go](file://internal/response/response.go)
- [internal/task/device_persist.go](file://internal/task/device_persist.go)
- [internal/task/share_code_persist.go](file://internal/task/share_code_persist.go)
- [internal/task/impedance_persist.go](file://internal/task/impedance_persist.go)
- [pkg/encode/base64.go](file://pkg/encode/base64.go)
- [sql/model.sql](file://sql/model.sql)
- [sql/ota.sql](file://sql/ota.sql)
@@ -36,6 +41,7 @@
- [sql/share_code_log.sql](file://sql/share_code_log.sql)
- [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)
- [cmd/server/main.go](file://cmd/server/main.go)
</cite>
@@ -45,6 +51,9 @@
- OTA灰度升级设备模型重构:从包含Type字段的定向设备改为不包含类型字段的灰度升级设备,简化了设备管理逻辑
- 数据库表结构同步更新:ota_target_device表移除了type字段,OTATargetDevice结构体不再包含Type字段
- 业务逻辑调整:OTA升级策略从"定向设备"概念转变为"灰度升级设备"概念,简化了升级控制逻辑
- **新增**UserHeadphoneImpedance数据模型,用于存储用户上报的耳机阻抗信息,包含MAC地址、设备型号、阻抗值、耳机信息(原始和标准化版本)、IP地址和时间戳等字段
- **新增**:耳机阻抗持久化任务,定时从Redis缓存批量持久化阻抗数据到数据库
- **新增**:耳机阻抗上报API接口,支持设备上报耳机阻抗信息
## 目录
1. [简介](#简介)
@@ -59,10 +68,10 @@
10. [附录](#附录)
## 简介
本文件面向 Luxsin 应用 API 的数据模型,系统性梳理 Brand、Model、OTA、ShareCode、Target、UserDeviceUserActive 类数据模型的设计理念、字段定义、约束条件、业务含义以及与数据库表结构的映射关系。文档还覆盖 ORM 映射配置(基于原生 sql.DB 的扫描映射)、模型验证与序列化机制、模型在各层之间的传递方式与性能考量,并给出扩展、版本管理与向后兼容的最佳实践建议。内容兼顾初学者与高级开发者,既提供高层概览,也包含代码级的可视化图示与来源标注。
本文件面向 Luxsin 应用 API 的数据模型,系统性梳理 Brand、Model、OTA、ShareCode、Target、UserDeviceUserActive 和 UserHeadphoneImpedance 八类数据模型的设计理念、字段定义、约束条件、业务含义以及与数据库表结构的映射关系。文档还覆盖 ORM 映射配置(基于原生 sql.DB 的扫描映射)、模型验证与序列化机制、模型在各层之间的传递方式与性能考量,并给出扩展、版本管理与向后兼容的最佳实践建议。内容兼顾初学者与高级开发者,既提供高层概览,也包含代码级的可视化图示与来源标注。
## 项目结构
该项目采用分层架构:路由层负责请求入口与中间件;处理器层处理业务逻辑与参数解析;仓库层封装数据库访问;模型层承载数据结构;响应层统一返回格式;编码层提供可选的响应体压缩编码;配置层加载数据库、Redis等外部服务配置。新增的分享码功能、目标曲线模型增强的OTA模型进一步完善了系统的数据模型体系。
该项目采用分层架构:路由层负责请求入口与中间件;处理器层处理业务逻辑与参数解析;仓库层封装数据库访问;模型层承载数据结构;响应层统一返回格式;编码层提供可选的响应体压缩编码;配置层加载数据库、Redis等外部服务配置。新增的分享码功能、目标曲线模型增强的OTA模型和用户耳机阻抗跟踪功能进一步完善了系统的数据模型体系。
```mermaid
graph TB
@@ -80,6 +89,7 @@ OTA_H["internal/handler/ota.go"]
DEVICE_H["internal/handler/device.go"]
SHARE_CODE_H["internal/handler/share_code.go"]
CURVE_H["internal/handler/curve.go"]
IMPEDANCE_H["internal/handler/impedance.go"]
end
subgraph "仓库层"
BRAND_R["internal/repository/brand.go"]
@@ -88,6 +98,7 @@ OTA_R["internal/repository/ota.go"]
DEVICE_R["internal/repository/device.go"]
SHARE_CODE_R["internal/repository/share_code.go"]
CURVE_R["internal/repository/curve.go"]
IMPEDANCE_R["internal/repository/headphone_impedance.go"]
end
subgraph "模型层"
MODEL_M["internal/model/brand.go"]
@@ -97,6 +108,7 @@ MODEL_M4["internal/model/user_device.go"]
MODEL_M5["internal/model/user_active.go"]
MODEL_M6["internal/model/share_code.go"]
MODEL_M7["internal/model/target.go"]
MODEL_M8["internal/model/user_headphone_impedance.go"]
end
subgraph "缓存层"
CACHE["internal/cache/*"]
@@ -108,11 +120,13 @@ end
subgraph "配置与数据库"
DB_CFG["internal/config/database.go"]
SHARE_TTL["internal/config/share_code_ttl.go"]
CONFIG["internal/config/config.go"]
DB_SQL["sql/*.sql"]
end
subgraph "任务处理"
DEVICE_TASK["internal/task/device_persist.go"]
SHARE_TASK["internal/task/share_code_persist.go"]
IMPEDANCE_TASK["internal/task/impedance_persist.go"]
end
MAIN --> ROUTER
ROUTER --> BRAND_H
@@ -122,6 +136,7 @@ ROUTER --> OTA_H
ROUTER --> DEVICE_H
ROUTER --> SHARE_CODE_H
ROUTER --> CURVE_H
ROUTER --> IMPEDANCE_H
BRAND_H --> BRAND_R
MODEL_H --> MODEL_R
MODEL_LIST_H --> MODEL_R
@@ -130,6 +145,7 @@ DEVICE_H --> DEVICE_R
SHARE_CODE_H --> CACHE
SHARE_CODE_H --> SHARE_CODE_R
CURVE_H --> CURVE_R
IMPEDANCE_H --> IMPEDANCE_R
BRAND_R --> MODEL_M
MODEL_R --> MODEL_M2
OTA_R --> MODEL_M3
@@ -137,6 +153,7 @@ DEVICE_R --> MODEL_M4
DEVICE_R --> MODEL_M5
SHARE_CODE_R --> MODEL_M6
CURVE_R --> MODEL_M7
IMPEDANCE_R --> MODEL_M8
BRAND_H --> RESP
MODEL_H --> RESP
MODEL_LIST_H --> RESP
@@ -144,11 +161,15 @@ OTA_H --> RESP
DEVICE_H --> RESP
SHARE_CODE_H --> RESP
CURVE_H --> RESP
IMPEDANCE_H --> RESP
DEVICE_H --> ENCODE
SHARE_CODE_H --> ENCODE
SHARE_TASK --> SHARE_CODE_R
IMPEDANCE_TASK --> IMPEDANCE_R
IMPEDANCE_TASK --> CACHE
DEVICE_TASK --> DEVICE_R
SHARE_TASK --> SHARE_CODE_R
DB_CFG --> DB_SQL
CONFIG --> DB_SQL
CACHE --> SHARE_TTL
```
@@ -162,12 +183,14 @@ CACHE --> SHARE_TTL
- [internal/handler/device.go:15-83](file://internal/handler/device.go#L15-L83)
- [internal/handler/share_code.go:1-383](file://internal/handler/share_code.go#L1-L383)
- [internal/handler/curve.go:1-582](file://internal/handler/curve.go#L1-L582)
- [internal/handler/impedance.go:1-115](file://internal/handler/impedance.go#L1-L115)
- [internal/repository/brand.go:16-51](file://internal/repository/brand.go#L16-L51)
- [internal/repository/model.go:16-95](file://internal/repository/model.go#L16-L95)
- [internal/repository/ota.go:11-159](file://internal/repository/ota.go#L11-L159)
- [internal/repository/device.go:11-88](file://internal/repository/device.go#L11-L88)
- [internal/repository/share_code.go:1-59](file://internal/repository/share_code.go#L1-L59)
- [internal/repository/curve.go:1-66](file://internal/repository/curve.go#L1-L66)
- [internal/repository/headphone_impedance.go:1-99](file://internal/repository/headphone_impedance.go#L1-L99)
- [internal/model/brand.go:3-7](file://internal/model/brand.go#L3-L7)
- [internal/model/model.go:5-15](file://internal/model/model.go#L5-L15)
- [internal/model/ota.go:5-63](file://internal/model/ota.go#L5-L63)
@@ -175,10 +198,12 @@ CACHE --> SHARE_TTL
- [internal/model/user_active.go:5-13](file://internal/model/user_active.go#L5-L13)
- [internal/model/share_code.go:14-25](file://internal/model/share_code.go#L14-L25)
- [internal/model/target.go:5-14](file://internal/model/target.go#L5-L14)
- [internal/model/user_headphone_impedance.go:5-17](file://internal/model/user_headphone_impedance.go#L5-L17)
- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37)
- [pkg/encode/base64.go:21-52](file://pkg/encode/base64.go#L21-L52)
- [pkg/encode/base64.go:21-52](file://pkg/encode/base64.go#L21-52)
- [internal/config/database.go:17-72](file://internal/config/database.go#L17-L72)
- [internal/config/share_code_ttl.go:12-60](file://internal/config/share_code_ttl.go#L12-L60)
- [internal/config/config.go:10-23](file://internal/config/config.go#L10-L23)
- [sql/model.sql:20-38](file://sql/model.sql#L20-L38)
- [sql/ota.sql:23-40](file://sql/ota.sql#L23-L40)
- [sql/ota_target_device.sql:23-31](file://sql/ota_target_device.sql#L23-L31)
@@ -186,8 +211,10 @@ CACHE --> SHARE_TTL
- [sql/share_code_log.sql:8-23](file://sql/share_code_log.sql#L8-L23)
- [sql/user_device.sql:23-32](file://sql/user_device.sql#L23-L32)
- [sql/user_active.sql:23-32](file://sql/user_active.sql#L23-L32)
- [sql/user_headphone_impedance.sql:20-36](file://sql/user_headphone_impedance.sql#L20-L36)
- [internal/task/device_persist.go:14-167](file://internal/task/device_persist.go#L14-L167)
- [internal/task/share_code_persist.go:1-232](file://internal/task/share_code_persist.go#L1-L232)
- [internal/task/impedance_persist.go:1-163](file://internal/task/impedance_persist.go#L1-L163)
**章节来源**
- [cmd/server/main.go:22-64](file://cmd/server/main.go#L22-L64)
@@ -209,6 +236,8 @@ CACHE --> SHARE_TTL
- **用户设备跟踪模型层**
- UserDevice:用户设备信息,包含设备MAC地址、型号、添加时间、当前版本等。
- UserActive:用户活动记录,包含设备活跃信息、IP地址、活跃日期等。
- **用户耳机阻抗模型层**
- UserHeadphoneImpedance:用户耳机阻抗信息,包含MAC地址、设备型号、阻抗值、耳机信息(原始和标准化版本)、IP地址和时间戳等。
- **仓库层(Repository**
- BrandRepository:提供按品牌名模糊查询的品牌列表。
- ModelRepository:提供按品牌名或型号名模糊查询的型号列表,并处理可空字段的扫描。
@@ -216,6 +245,7 @@ CACHE --> SHARE_TTL
- DeviceRepository:提供设备信息查询、插入、更新以及活动记录管理。
- ShareCodeRepository:提供分享码日志插入、导出记录检查等功能。
- CurveRepository:提供型号和目标曲线查询功能。
- HeadphoneImpedanceRepository:提供用户耳机阻抗信息的查询、插入和更新功能。
- **处理器层(Handler**
- BrandHandler:接收查询参数,调用仓库层,支持可选的响应体 Base64 编码。
- ModelHandler:接收品牌与型号查询参数,调用仓库层,支持可选的响应体 Base64 编码。
@@ -224,9 +254,11 @@ CACHE --> SHARE_TTL
- DeviceHandler:处理设备信息上报,将设备活动信息写入Redis缓存。
- ShareCodeHandler:处理分享码创建、查询、导入、删除等操作,支持Redis缓存和数据库持久化。
- CurveHandler:处理曲线数据获取,支持目标曲线配置和EQ参数化。
- ImpedanceHandler:处理耳机阻抗上报,将阻抗信息写入Redis缓存。
- **任务处理层**
- DevicePersistTask:定时从Redis缓存批量持久化设备信息到数据库。
- ShareCodePersistTask:定时从Redis缓存批量持久化分享码日志到数据库。
- ImpedancePersistTask:定时从Redis缓存批量持久化耳机阻抗信息到数据库。
**章节来源**
- [internal/model/brand.go:3-7](file://internal/model/brand.go#L3-L7)
@@ -236,12 +268,14 @@ CACHE --> SHARE_TTL
- [internal/model/target.go:5-14](file://internal/model/target.go#L5-L14)
- [internal/model/user_device.go:5-12](file://internal/model/user_device.go#L5-L12)
- [internal/model/user_active.go:5-13](file://internal/model/user_active.go#L5-L13)
- [internal/model/user_headphone_impedance.go:5-17](file://internal/model/user_headphone_impedance.go#L5-L17)
- [internal/repository/brand.go:20-51](file://internal/repository/brand.go#L20-L51)
- [internal/repository/model.go:20-95](file://internal/repository/model.go#L20-L95)
- [internal/repository/ota.go:19-159](file://internal/repository/ota.go#L19-L159)
- [internal/repository/device.go:19-88](file://internal/repository/device.go#L19-L88)
- [internal/repository/share_code.go:12-59](file://internal/repository/share_code.go#L12-L59)
- [internal/repository/curve.go:11-66](file://internal/repository/curve.go#L11-L66)
- [internal/repository/headphone_impedance.go:19-99](file://internal/repository/headphone_impedance.go#L19-L99)
- [internal/handler/brand.go:26-50](file://internal/handler/brand.go#L26-L50)
- [internal/handler/model.go:26-51](file://internal/handler/model.go#L26-L51)
- [internal/handler/model_list.go:26-57](file://internal/handler/model_list.go#L26-L57)
@@ -249,11 +283,13 @@ CACHE --> SHARE_TTL
- [internal/handler/device.go:27-83](file://internal/handler/device.go#L27-L83)
- [internal/handler/share_code.go:15-383](file://internal/handler/share_code.go#L15-L383)
- [internal/handler/curve.go:27-582](file://internal/handler/curve.go#L27-L582)
- [internal/handler/impedance.go:30-107](file://internal/handler/impedance.go#L30-L107)
- [internal/task/device_persist.go:24-167](file://internal/task/device_persist.go#L24-L167)
- [internal/task/share_code_persist.go:14-232](file://internal/task/share_code_persist.go#L14-L232)
- [internal/task/impedance_persist.go:30-163](file://internal/task/impedance_persist.go#L30-L163)
## 架构总览
下图展示了从请求到响应的关键路径,包括处理器、仓库、数据库与可选编码流程,以及新增的分享码功能、目标曲线配置增强的OTA固件升级功能。
下图展示了从请求到响应的关键路径,包括处理器、仓库、数据库与可选编码流程,以及新增的分享码功能、目标曲线配置增强的OTA固件升级功能和用户耳机阻抗跟踪功能
```mermaid
sequenceDiagram
@@ -264,6 +300,12 @@ participant Repo as "仓库层"
participant Cache as "缓存层"
participant DB as "数据库"
participant Enc as "编码层"
C->>R : "GET /audio/reportImpedance?mac=xx&name=Luxsin-X9&brand=Sony&model=WH-1000XM4&value=32"
R->>H : "ImpedanceHandler.ReportImpedance"
H->>H : "参数校验 + 文本归一化"
H->>Cache : "HSet(headphone_impedances, mac|brand_norm|model_norm, JSON)"
Cache-->>H : "确认写入"
H-->>C : "200 OK + 操作成功"
C->>R : "POST /audio/shareCreate"
R->>H : "ShareCodeHandler.ExportShareCode"
H->>Cache : "Create(分享码, EQ数据)"
@@ -273,16 +315,6 @@ Repo->>DB : "插入分享码日志"
DB-->>Repo : "确认插入"
Repo-->>H : "确认"
H-->>C : "200 OK + 分享码信息"
C->>R : "GET /audio/shareQuery?shareCode=ABCDE"
R->>H : "ShareCodeHandler.QueryShareCode"
H->>Cache : "Get(分享码)"
Cache-->>H : "ShareCodeData"
H-->>C : "200 OK + EQ数据"
C->>R : "GET /audio/shareAccept?mac=xx&model=Luxsin-X9&shareCode=ABCDE"
R->>H : "ShareCodeHandler.ImportShareCode"
H->>Cache : "EnqueueImportLog(导入日志)"
Cache-->>H : "排队成功"
H-->>C : "200 OK + EQ数据"
C->>R : "GET /audio/getCurve?brand=xxx&name=xxx&target=xxx"
R->>H : "CurveHandler.GetCurve"
H->>Repo : "GetTargetByLabel(目标曲线)"
@@ -295,10 +327,9 @@ H-->>C : "200 OK + parametric_eq"
```
**图表来源**
- [internal/router/router.go:73-77](file://internal/router/router.go#L73-L77)
- [internal/router/router.go:64-80](file://internal/router/router.go#L64-L80)
- [internal/handler/impedance.go:43-107](file://internal/handler/impedance.go#L43-L107)
- [internal/handler/share_code.go:40-125](file://internal/handler/share_code.go#L40-L125)
- [internal/handler/share_code.go:192-211](file://internal/handler/share_code.go#L192-L211)
- [internal/handler/share_code.go:228-279](file://internal/handler/share_code.go#L228-L279)
- [internal/handler/curve.go:145-198](file://internal/handler/curve.go#L145-L198)
- [internal/repository/curve.go:44-66](file://internal/repository/curve.go#L44-L66)
- [internal/cache/curve_cache.go:27-102](file://internal/cache/curve_cache.go#L27-L102)
@@ -702,6 +733,76 @@ DeviceRepository --> UserActive : "返回"
- [sql/user_device.sql:23-32](file://sql/user_device.sql#L23-L32)
- [sql/user_active.sql:23-32](file://sql/user_active.sql#L23-L32)
### 用户耳机阻抗数据模型
- **设计理念**
- UserHeadphoneImpedance模型用于存储用户上报的耳机阻抗信息,支持去重更新机制。
- 每个用户(MAC地址)可以拥有多个耳机,以"耳机品牌 + 耳机型号"进行区分。
- 采用去重更新策略,同一耳机的阻抗信息始终反映最新值。
- **字段定义与约束**
- ID:整数,主键,自增。
- MacAddr:字符串,设备MAC地址,用户标识。
- DeviceModel:字符串,设备型号(Luxsin-X8/Luxsin-X9)。
- ImpedanceOhm:整数,阻抗值(单位:欧姆Ω)。
- HeadphoneBrand:字符串,耳机品牌(原始输入)。
- HeadphoneModel:字符串,耳机型号(原始输入)。
- HeadphoneBrandNorm:字符串,耳机品牌(trim+lower,用于去重)。
- HeadphoneModelNorm:字符串,耳机型号(trim+lower,用于去重)。
- IpAddr:字符串,服务端获取的设备IP地址。
- CreateAt:时间戳,记录创建时间。
- UpdateAt:时间戳,记录更新时间。
- **业务含义**
- 支持用户上报耳机阻抗信息,为后续音频调优提供数据支持。
- 通过规范化处理确保数据一致性,避免重复记录。
- 保持最新阻抗值,不保留历史版本。
- **数据库映射**
- 表名:user_headphone_impedance。
- 字段:完整映射关系详见数据库表结构。
- 索引:唯一索引 (mac_addr, headphone_brand_norm, headphone_model_norm),辅助索引 (mac_addr)、(device_model)。
- **ORM 映射配置**
- 使用原生 sql.DB 扫描,直接映射到结构体字段。
- 时间字段使用 time.Time 类型。
- **查询流程**
- 支持按MAC地址和规范化耳机信息进行查询。
- 支持插入新记录和更新现有记录。
- **去重规则**
- 唯一判定条件:mac_addr + headphone_brand_norm + headphone_model_norm。
- 命中相同记录时,更新阻抗值、设备型号、IP地址和更新时间。
- 不命中相同记录时,插入新记录。
```mermaid
classDiagram
class UserHeadphoneImpedance {
+int ID
+string MacAddr
+string DeviceModel
+int ImpedanceOhm
+string HeadphoneBrand
+string HeadphoneModel
+string HeadphoneBrandNorm
+string HeadphoneModelNorm
+string IpAddr
+time CreateAt
+time UpdateAt
}
class HeadphoneImpedanceRepository {
-db *sql.DB
+FindByMacAndNorm(ctx, macAddr, brandNorm, modelNorm) *UserHeadphoneImpedance
+Insert(ctx, rec) error
+Update(ctx, rec) error
}
HeadphoneImpedanceRepository --> UserHeadphoneImpedance : "返回"
```
**图表来源**
- [internal/model/user_headphone_impedance.go:5-17](file://internal/model/user_headphone_impedance.go#L5-L17)
- [internal/repository/headphone_impedance.go:19-99](file://internal/repository/headphone_impedance.go#L19-L99)
- [sql/user_headphone_impedance.sql:20-36](file://sql/user_headphone_impedance.sql#L20-L36)
**章节来源**
- [internal/model/user_headphone_impedance.go:5-17](file://internal/model/user_headphone_impedance.go#L5-L17)
- [internal/repository/headphone_impedance.go:19-99](file://internal/repository/headphone_impedance.go#L19-L99)
- [sql/user_headphone_impedance.sql:20-36](file://sql/user_headphone_impedance.sql#L20-L36)
### 处理器与序列化机制
- **统一响应体**
- 统一响应体包含 code、message、data 字段,便于前端统一处理。
@@ -723,35 +824,46 @@ DeviceRepository --> UserActive : "返回"
- **曲线处理器特殊逻辑**
- 支持目标曲线配置查询和EQ参数化计算。
- 使用Redis缓存优化曲线数据访问性能。
- **耳机阻抗处理器特殊逻辑**
- 支持耳机阻抗信息上报,进行参数校验和文本归一化处理。
- 将阻抗信息写入Redis缓存,支持异步持久化。
- 提供标准化的耳机品牌和型号处理,确保数据一致性。
- **错误处理**
- 处理器捕获仓库层错误,记录日志并通过统一响应体返回内部错误。
```mermaid
sequenceDiagram
participant C as "客户端"
participant H as "ShareCodeHandler"
participant Cache as "ShareCodeCache"
participant Repo as "ShareCodeRepository"
participant H as "ImpedanceHandler"
participant Cache as "Redis缓存"
participant Task as "持久化任务"
participant Repo as "HeadphoneImpedanceRepository"
participant DB as "数据库"
participant Enc as "编码层"
C->>H : "POST /audio/shareCreate"
H->>Cache : "Create(mac, model, eqData)"
Cache-->>H : "ShareCodeData"
H->>Repo : "InsertLog(export)"
Repo->>DB : "插入日志"
DB-->>Repo : "确认"
Repo-->>H : "确认"
H-->>C : "200 OK + 分享码信息"
C->>H : "GET /audio/shareAccept?shareCode=ABCDE"
H->>Cache : "EnqueueImportLog(mac, model, eqData)"
Cache-->>H : "排队成功"
H-->>C : "200 OK + EQ数据"
C->>H : "GET /audio/reportImpedance?mac=xx&name=Luxsin-X9&brand=Sony&model=WH-1000XM4&value=32"
H->>H : "参数校验 + 文本归一化"
H->>Cache : "HSet(headphone_impedances, mac|brand_norm|model_norm, JSON)"
Cache-->>H : "确认写入"
H-->>C : "200 OK + 操作成功"
Note over Task,DB : "定时任务每5分钟执行一次"
Task->>Cache : "HGetAll(headphone_impedances)"
Cache-->>Task : "阻抗数据列表"
Task->>Repo : "FindByMacAndNorm(查询是否存在)"
Repo->>DB : "查询数据库"
DB-->>Repo : "返回结果"
alt 记录不存在
Repo->>DB : "INSERT 新记录"
else 记录存在
Repo->>DB : "UPDATE 更新记录"
end
Task->>Cache : "HDel 删除已持久化数据"
```
**图表来源**
- [internal/handler/impedance.go:43-107](file://internal/handler/impedance.go#L43-L107)
- [internal/task/impedance_persist.go:44-163](file://internal/task/impedance_persist.go#L44-L163)
- [internal/repository/headphone_impedance.go:19-99](file://internal/repository/headphone_impedance.go#L19-L99)
- [internal/handler/share_code.go:40-125](file://internal/handler/share_code.go#L40-L125)
- [internal/handler/share_code.go:228-279](file://internal/handler/share_code.go#L228-L279)
- [internal/repository/share_code.go:20-59](file://internal/repository/share_code.go#L20-L59)
- [internal/handler/curve.go:145-198](file://internal/handler/curve.go#L145-L198)
- [pkg/encode/base64.go:21-52](file://pkg/encode/base64.go#L21-L52)
- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37)
@@ -763,6 +875,7 @@ H-->>C : "200 OK + EQ数据"
- [internal/handler/device.go:27-83](file://internal/handler/device.go#L27-L83)
- [internal/handler/share_code.go:15-383](file://internal/handler/share_code.go#L15-L383)
- [internal/handler/curve.go:27-582](file://internal/handler/curve.go#L27-L582)
- [internal/handler/impedance.go:30-107](file://internal/handler/impedance.go#L30-L107)
- [pkg/encode/base64.go:21-52](file://pkg/encode/base64.go#L21-L52)
- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37)
@@ -789,25 +902,28 @@ H-->>C : "200 OK + EQ数据"
- **目标曲线查询**
- 支持按标签查询目标曲线配置。
- 支持CSV文件路径和贝斯增强配置的动态加载。
- **耳机阻抗查询**
- 支持按MAC地址和规范化耳机信息进行查询。
- 支持插入新记录和更新现有记录的去重逻辑。
- 使用复合唯一索引确保数据一致性。
```mermaid
flowchart TD
Start(["进入分享码查询"]) --> Params["参数校验"]
Params --> CheckMac{"mac参数是否空?"}
CheckMac --> |是| ListByMac["ListByMac 查询"]
CheckMac --> |否| CheckCode{"shareCode参数是否5位?"}
CheckCode --> |是| GetByCode["Get 查询"]
CheckCode --> |否| Error["返回错误"]
ListByMac --> CleanExpired["清理过期条目"]
CleanExpired --> GetDetails["逐个获取详情"]
GetDetails --> ReturnList["返回分享码列表"]
GetByCode --> ReturnDetail["返回分享码详情"]
ReturnList --> End(["返回结果"])
ReturnDetail --> End
Error --> End
Start(["进入耳机阻抗上报"]) --> Params["参数校验"]
Params --> CheckEmpty{"所有参数是否空?"}
CheckEmpty --> |是| Error["返回参数校验失败"]
CheckEmpty --> |否| ParseValue["解析阻抗值"]
ParseValue --> CheckValue{"阻抗值是否为有效整数?"}
CheckValue --> |否| Error
CheckValue --> |是| Normalize["文本归一化处理"]
Normalize --> RedisWrite["写入Redis缓存"]
RedisWrite --> Success["返回操作成功"]
Error --> End(["结束"])
Success --> End
```
**图表来源**
- [internal/handler/impedance.go:43-107](file://internal/handler/impedance.go#L43-L107)
- [internal/handler/share_code.go:138-177](file://internal/handler/share_code.go#L138-L177)
- [internal/handler/share_code.go:192-211](file://internal/handler/share_code.go#L192-L211)
- [internal/cache/share_code_cache.go:281-319](file://internal/cache/share_code_cache.go#L281-L319)
@@ -819,29 +935,33 @@ Error --> End
- [internal/repository/device.go:19-88](file://internal/repository/device.go#L19-L88)
- [internal/repository/share_code.go:20-59](file://internal/repository/share_code.go#L20-L59)
- [internal/repository/curve.go:44-66](file://internal/repository/curve.go#L44-L66)
- [internal/repository/headphone_impedance.go:19-99](file://internal/repository/headphone_impedance.go#L19-L99)
- [internal/handler/share_code.go:138-177](file://internal/handler/share_code.go#L138-L177)
- [internal/handler/impedance.go:43-107](file://internal/handler/impedance.go#L43-L107)
## 依赖分析
- **层间耦合**
- Handler 依赖 RepositoryRepository 依赖 sql.DBModel 为纯数据结构。
- 统一响应体与编码层被 Handler 调用,降低重复逻辑。
- 新增的Redis依赖用于分享码缓存设备信息缓存。
- 新增的Redis依赖用于分享码缓存设备信息缓存和耳机阻抗缓存
- 分享码功能依赖Redis缓存和数据库的双重存储。
- 目标曲线功能依赖CurveRepository和CurveCache。
- 耳机阻抗功能依赖Redis缓存和数据库的双重存储。
- **外部依赖**
- 数据库:MySQL,通过 sql.DB 访问。
- 日志:zap。
- Web 框架:Gin。
- 搜索:MeilisearchModelListHandler)。
- 缓存:RedisDevice、ShareCode、Curve相关处理器)。
- 缓存:RedisDevice、ShareCode、Curve、Impedance相关处理器)。
- 对象存储:S3(曲线数据CSV文件)。
- 任务调度:定时任务用于设备信息分享码持久化。
- 任务调度:定时任务用于设备信息分享码和耳机阻抗持久化。
- **潜在循环依赖**
- 当前结构清晰,无循环导入迹象。
- **新增依赖关系**
- 分享码功能依赖Redis缓存和数据库的双重存储。
- 目标曲线功能依赖CurveRepository和CurveCache。
- OTA功能依赖多个表的关联查询。
- 耳机阻抗功能依赖Redis缓存和数据库的双重存储,以及定时持久化任务。
```mermaid
graph LR
@@ -853,15 +973,18 @@ H_Device["DeviceHandler"] --> Repo_Device["DeviceRepository"]
H_ShareCode["ShareCodeHandler"] --> Cache_Share["ShareCodeCache"]
H_ShareCode --> Repo_Share["ShareCodeRepository"]
H_Curve["CurveHandler"] --> Repo_Curve["CurveRepository"]
H_Impedance["ImpedanceHandler"] --> Repo_Impedance["HeadphoneImpedanceRepository"]
Repo_Brand --> DB["sql.DB"]
Repo_Model --> DB
Repo_OTA --> DB
Repo_Device --> DB
Repo_Share --> DB
Repo_Curve --> DB
Repo_Impedance --> DB
H_Device --> Redis["Redis Client"]
H_ShareCode --> Redis
H_Curve --> Redis
H_Impedance --> Redis
H_Curve --> S3["S3 Storage"]
H_Brand --> Resp["统一响应体"]
H_Model --> Resp
@@ -870,6 +993,7 @@ H_OTA --> Resp
H_Device --> Resp
H_ShareCode --> Resp
H_Curve --> Resp
H_Impedance --> Resp
H_Brand --> Encode["Base64 编码"]
H_Model --> Encode
H_ModelList --> Encode
@@ -877,6 +1001,8 @@ Task_Device["DevicePersistTask"] --> Repo_Device
Task_Device --> Redis
Task_Share["ShareCodePersistTask"] --> Repo_Share
Task_Share --> Redis
Task_Impedance["ImpedancePersistTask"] --> Repo_Impedance
Task_Impedance --> Redis
```
**图表来源**
@@ -887,16 +1013,19 @@ Task_Share --> Redis
- [internal/handler/device.go:15-83](file://internal/handler/device.go#L15-L83)
- [internal/handler/share_code.go:15-383](file://internal/handler/share_code.go#L15-L383)
- [internal/handler/curve.go:27-582](file://internal/handler/curve.go#L27-L582)
- [internal/handler/impedance.go:18-107](file://internal/handler/impedance.go#L18-L107)
- [internal/repository/brand.go:12-51](file://internal/repository/brand.go#L12-L51)
- [internal/repository/model.go:12-95](file://internal/repository/model.go#L12-L95)
- [internal/repository/ota.go:11-159](file://internal/repository/ota.go#L11-L159)
- [internal/repository/device.go:11-88](file://internal/repository/device.go#L11-L88)
- [internal/repository/share_code.go:12-59](file://internal/repository/share_code.go#L12-L59)
- [internal/repository/curve.go:11-66](file://internal/repository/curve.go#L11-L66)
- [internal/repository/headphone_impedance.go:11-99](file://internal/repository/headphone_impedance.go#L11-L99)
- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37)
- [pkg/encode/base64.go:21-52](file://pkg/encode/base64.go#L21-L52)
- [internal/task/device_persist.go:14-167](file://internal/task/device_persist.go#L14-L167)
- [internal/task/share_code_persist.go:14-232](file://internal/task/share_code_persist.go#L14-L232)
- [internal/task/impedance_persist.go:16-163](file://internal/task/impedance_persist.go#L16-L163)
**章节来源**
- [internal/router/router.go:14-82](file://internal/router/router.go#L14-L82)
@@ -909,6 +1038,7 @@ Task_Share --> Redis
- UserDevice表对(mac_addr)建立唯一索引,支持快速设备查询。
- UserActive表对(mac_addr, active_date)建立联合索引,支持按日期分区查询。
- share_code_log表对(mac_addr, share_code, create_at)建立索引,支持分享码查询。
- user_headphone_impedance表对(mac_addr, headphone_brand_norm, headphone_model_norm)建立唯一索引,支持快速阻抗查询和去重。
- 查询时优先按品牌名匹配,减少 LIKE 的范围。
- **更新**:OTA查询现在使用简化的字段列表,减少了不必要的字段扫描。
- **更新**:OTA查询现在使用灰度升级设备概念,简化了设备匹配逻辑,提高了查询性能。
@@ -916,17 +1046,20 @@ Task_Share --> Redis
- 使用 sql.NullString 扫描可空列,避免零值歧义;转换为指针字符串减少冗余存储。
- OTA模型使用BoolInt类型处理布尔值,支持数据库整型与JSON布尔值的双向转换。
- ShareCodeLog模型使用[]byte存储JSON数据,减少字符串处理开销。
- 耳机阻抗模型使用原生类型,避免额外的类型转换开销。
- **缓存策略**
- 设备信息先写入Redis缓存,通过定时任务批量持久化到数据库,减少数据库压力。
- 品牌和型号信息使用Redis缓存提升查询性能。
- 分享码数据使用Redis缓存,支持TTL自动过期和分布式锁保证一致性。
- 目标曲线数据使用Redis缓存,支持fr数据独立存储优化缓存空间。
- 耳机阻抗数据使用Redis缓存,支持批量持久化和去重更新。
- **编码策略**
- 可选的自定义 Base64 编码可降低响应体积,适合大列表传输场景。
- **并发与超时**
- 服务器设置读写超时与优雅关闭,保障稳定性。
- 设备持久化任务分享码持久化任务使用定时器定期执行,避免阻塞主线程。
- 设备持久化任务分享码持久化任务和耳机阻抗持久化任务使用定时器定期执行,避免阻塞主线程。
- 分布式锁机制保证Redis操作的原子性和数据一致性。
- 耳机阻抗持久化任务支持部分失败重试,提高系统健壮性。
**章节来源**
- [sql/model.sql:34-35](file://sql/model.sql#L34-L35)
@@ -934,14 +1067,17 @@ Task_Share --> Redis
- [sql/share_code_log.sql:20-23](file://sql/share_code_log.sql#L20-L23)
- [sql/user_device.sql:24-32](file://sql/user_device.sql#L24-L32)
- [sql/user_active.sql:24-32](file://sql/user_active.sql#L24-L32)
- [sql/user_headphone_impedance.sql:32-35](file://sql/user_headphone_impedance.sql#L32-L35)
- [internal/repository/model.go:63-95](file://internal/repository/model.go#L63-L95)
- [internal/repository/ota.go:20-159](file://internal/repository/ota.go#L20-L159)
- [internal/repository/device.go:19-88](file://internal/repository/device.go#L19-L88)
- [internal/repository/share_code.go:20-59](file://internal/repository/share_code.go#L20-L59)
- [internal/repository/headphone_impedance.go:19-99](file://internal/repository/headphone_impedance.go#L19-L99)
- [pkg/encode/base64.go:21-52](file://pkg/encode/base64.go#L21-L52)
- [cmd/server/main.go:66-95](file://cmd/server/main.go#L66-L95)
- [internal/task/device_persist.go:24-91](file://internal/task/device_persist.go#L24-L91)
- [internal/task/share_code_persist.go:24-113](file://internal/task/share_code_persist.go#L24-L113)
- [internal/task/impedance_persist.go:30-163](file://internal/task/impedance_persist.go#L30-L163)
## 故障排查指南
- **常见问题**
@@ -954,15 +1090,18 @@ Task_Share --> Redis
- 分享码导入失败:检查导入日志队列、数据库连接、分布式锁状态。
- 目标曲线查询失败:检查目标曲线配置、CSV文件路径、S3访问权限。
- **更新**:OTA灰度升级设备查询失败:检查ota_target_device表结构,确认type字段已被移除。
- 耳机阻抗上报失败:检查参数完整性、Redis连接状态、文本归一化处理。
- 耳机阻抗持久化失败:检查Redis队列状态、数据库连接、唯一索引冲突。
- **排查步骤**
- 查看日志:处理器记录错误日志,统一响应体返回错误码。
- 核对数据库:确认表结构与索引是否存在。
- 验证参数:确认查询参数是否符合预期。
- 检查Redis:确认设备信息、分享码数据是否正确写入Redis缓存。
- 监控任务:确认设备持久化任务分享码持久化任务是否按预期执行。
- 检查Redis:确认设备信息、分享码数据、耳机阻抗数据是否正确写入Redis缓存。
- 监控任务:确认设备持久化任务分享码持久化任务和耳机阻抗持久化任务是否按预期执行。
- 检查分布式锁:确认分享码操作的分布式锁状态和超时处理。
- **更新**:检查OTA表结构,确认paw*字段不存在,验证简化的字段结构。
- **更新**:检查ota_target_device表结构,确认type字段已被移除,验证灰度升级设备查询逻辑。
- 检查耳机阻抗数据:确认规范化字段是否正确生成,唯一索引是否正常工作。
**章节来源**
- [internal/config/database.go:57-72](file://internal/config/database.go#L57-L72)
@@ -973,13 +1112,16 @@ Task_Share --> Redis
- [internal/handler/device.go:33-39](file://internal/handler/device.go#L33-L39)
- [internal/handler/share_code.go:46-81](file://internal/handler/share_code.go#L46-L81)
- [internal/handler/curve.go:302-368](file://internal/handler/curve.go#L302-L368)
- [internal/handler/impedance.go:51-66](file://internal/handler/impedance.go#L51-L66)
- [internal/response/response.go:30-37](file://internal/response/response.go#L30-L37)
## 结论
本项目的数据模型设计简洁明确:Brand 与 Model 分别承担品牌与型号的维度,通过仓库层的原生 SQL 访问实现高效查询;处理器层统一响应与可选编码,提升传输效率与前端体验。新增的分享码功能、目标曲线模型增强的OTA模型进一步完善了系统的数据模型体系,支持复杂的固件升级策略、设备信息跟踪、用户行为分析EQ数据分享功能。数据库层面通过唯一索引与合理字段设计保障一致性与性能。Redis缓存层提供了高性能的临时数据存储和分布式锁机制。
本项目的数据模型设计简洁明确:Brand 与 Model 分别承担品牌与型号的维度,通过仓库层的原生 SQL 访问实现高效查询;处理器层统一响应与可选编码,提升传输效率与前端体验。新增的分享码功能、目标曲线模型增强的OTA模型和用户耳机阻抗跟踪功能进一步完善了系统的数据模型体系,支持复杂的固件升级策略、设备信息跟踪、用户行为分析EQ数据分享和耳机阻抗采集功能。数据库层面通过唯一索引与合理字段设计保障一致性与性能。Redis缓存层提供了高性能的临时数据存储和分布式锁机制。
**更新**:OTA模型经过简化重构,移除了pawVerCode、pawVerName、pawUrl、pawMd5四个冗余字段,现在只包含核心升级信息,提高了数据模型的清晰度和维护性。同时,OTATargetDevice模型从"定向设备"概念重构为"灰度升级设备"概念,移除了Type字段,简化了设备管理逻辑,提高了系统的易用性和维护性。新的字段结构更加简洁,减少了不必要的数据冗余,同时保持了完整的升级策略支持能力。
**新增**UserHeadphoneImpedance数据模型为用户耳机阻抗信息采集提供了完善的数据支撑,通过规范化处理和去重更新机制确保了数据的一致性和准确性。Redis缓存与定时持久化任务的结合,在保证性能的同时实现了数据的可靠存储。
建议在后续迭代中引入显式的校验与转换层,增强健壮性与可维护性,同时考虑添加更多的监控指标和告警机制。
## 附录
@@ -1011,6 +1153,9 @@ Task_Share --> Redis
- **表:target**
- 字段:id(主键)、label、read_csv、file、bassBoost、addtime。
- 约束:无显式约束。
- **表:user_headphone_impedance**
- 字段:id(主键)、mac_addr、device_model、impedance_ohm、headphone_brand、headphone_model、headphone_brand_norm、headphone_model_norm、ip_addr、create_at、update_at。
- 约束:唯一索引 uniq_mac_brand_model(mac_addr, headphone_brand_norm, headphone_model_norm),辅助索引 idx_mac(mac_addr)、idx_device_model(device_model)。
- **映射关系**
- Model.id ↔ model.id
- Model.brandName ↔ model.brand_name
@@ -1063,6 +1208,17 @@ Task_Share --> Redis
- Target.File ↔ target.file
- Target.BassBoost ↔ target.bassBoost
- Target.AddTime ↔ target.addtime
- UserHeadphoneImpedance.ID ↔ user_headphone_impedance.id
- UserHeadphoneImpedance.MacAddr ↔ user_headphone_impedance.mac_addr
- UserHeadphoneImpedance.DeviceModel ↔ user_headphone_impedance.device_model
- UserHeadphoneImpedance.ImpedanceOhm ↔ user_headphone_impedance.impedance_ohm
- UserHeadphoneImpedance.HeadphoneBrand ↔ user_headphone_impedance.headphone_brand
- UserHeadphoneImpedance.HeadphoneModel ↔ user_headphone_impedance.headphone_model
- UserHeadphoneImpedance.HeadphoneBrandNorm ↔ user_headphone_impedance.headphone_brand_norm
- UserHeadphoneImpedance.HeadphoneModelNorm ↔ user_headphone_impedance.headphone_model_norm
- UserHeadphoneImpedance.IpAddr ↔ user_headphone_impedance.ip_addr
- UserHeadphoneImpedance.CreateAt ↔ user_headphone_impedance.create_at
- UserHeadphoneImpedance.UpdateAt ↔ user_headphone_impedance.update_at
**章节来源**
- [sql/model.sql:24-35](file://sql/model.sql#L24-L35)
@@ -1073,12 +1229,14 @@ Task_Share --> Redis
- [sql/user_active.sql:24-32](file://sql/user_active.sql#L24-L32)
- [sql/share_code_log.sql:9-23](file://sql/share_code_log.sql#L9-L23)
- [sql/target.sql:24-31](file://sql/target.sql#L24-L31)
- [sql/user_headphone_impedance.sql:20-36](file://sql/user_headphone_impedance.sql#L20-L36)
- [internal/model/model.go:5-15](file://internal/model/model.go#L5-L15)
- [internal/model/ota.go:29-63](file://internal/model/ota.go#L29-L63)
- [internal/model/user_device.go:5-12](file://internal/model/user_device.go#L5-L12)
- [internal/model/user_active.go:5-13](file://internal/model/user_active.go#L5-L13)
- [internal/model/share_code.go:14-25](file://internal/model/share_code.go#L14-L25)
- [internal/model/target.go:5-14](file://internal/model/target.go#L5-L14)
- [internal/model/user_headphone_impedance.go:5-17](file://internal/model/user_headphone_impedance.go#L5-L17)
### 请求与响应示例(路径参考)
- **获取品牌列表**
@@ -1113,6 +1271,13 @@ Task_Share --> Redis
- 参数:mac(必需)、model(必需)、ver(可选)
- 返回:操作结果
- 参考路径:[internal/handler/device.go:27-83](file://internal/handler/device.go#L27-L83)
- **耳机阻抗上报**
- 路由:/audio/reportImpedance
- 方法:GET
- 参数:mac(必需)、name(必需,设备型号)、brand(必需,耳机品牌)、model(必需,耳机型号)、value(必需,阻抗值整数)
- 返回:操作结果
- **新增**:支持用户上报耳机阻抗信息,数据进行规范化处理后写入Redis缓存
- 参考路径:[internal/handler/impedance.go:30-107](file://internal/handler/impedance.go#L30-L107)
- **创建分享码**
- 路由:/audio/shareCreate
- 方法:POST
@@ -1145,6 +1310,7 @@ Task_Share --> Redis
- [internal/handler/model_list.go:26-57](file://internal/handler/model_list.go#L26-L57)
- [internal/handler/ota.go:23-145](file://internal/handler/ota.go#L23-L145)
- [internal/handler/device.go:27-83](file://internal/handler/device.go#L27-L83)
- [internal/handler/impedance.go:30-107](file://internal/handler/impedance.go#L30-L107)
- [internal/handler/share_code.go:40-125](file://internal/handler/share_code.go#L40-L125)
- [internal/handler/share_code.go:192-211](file://internal/handler/share_code.go#L192-L211)
- [internal/handler/share_code.go:228-279](file://internal/handler/share_code.go#L228-L279)
@@ -1158,6 +1324,7 @@ Task_Share --> Redis
- 分享码模型支持多种设备型号,建议添加设备型号验证和限制。
- **更新**:OTA模型简化后,减少了字段验证的复杂度,但仍需确保核心字段的完整性。
- **更新**OTATargetDevice模型简化后,灰度升级设备管理更加直观,减少了类型判断的复杂性。
- **新增**:耳机阻抗模型支持用户上报阻抗信息,建议添加阻抗值范围验证和设备型号白名单验证。
- **版本管理与向后兼容**
- 通过 API 版本号(如 /api/v1)隔离变更;新增字段采用可选策略,保持旧字段必填。
- 对于破坏性变更,提供迁移脚本与双写策略。
@@ -1165,6 +1332,7 @@ Task_Share --> Redis
- 分享码功能的新增不影响现有API,保持向前兼容。
- **更新**:OTA模型的简化属于向后兼容的重构,不会影响现有API调用。
- **更新**OTATargetDevice模型的重构属于概念性重构,从"定向设备"到"灰度升级设备",保持了API的向后兼容性。
- **新增**:耳机阻抗上报API为新功能,不影响现有API,保持向前兼容。
- **性能优化**
- 为高频查询字段建立索引;避免 SELECT *,仅选择必要字段。
- 对大列表启用可选 Base64 编码;结合分页与缓存策略。
@@ -1174,6 +1342,7 @@ Task_Share --> Redis
- 目标曲线使用Redis缓存,优化fr数据的独立存储。
- **更新**:OTA查询现在使用简化的字段列表,减少了查询开销。
- **更新**:OTA查询现在使用灰度升级设备概念,简化了设备匹配逻辑,提高了查询性能。
- **新增**:耳机阻抗使用Redis缓存,支持批量持久化和去重更新,提高写入性能。
- **错误处理与可观测性**
- 统一错误码与消息格式;记录关键链路日志;对数据库与外部服务增加超时与重试。
- OTA查询失败需要详细的日志记录,包括参数、查询结果等。
@@ -1181,6 +1350,7 @@ Task_Share --> Redis
- 分享码持久化任务需要监控Redis队列状态和数据库插入结果。
- 目标曲线查询需要监控S3访问权限和CSV文件读取。
- **更新**:OTA灰度升级设备查询失败需要详细的日志记录,包括设备MAC地址和OTA ID。
- **新增**:耳机阻抗持久化任务需要监控Redis队列状态、数据库插入结果和唯一索引冲突。
- **安全考虑**
- OTA下载链接需要安全性验证,防止恶意下载。
- 设备信息上报需要参数验证,防止注入攻击。
@@ -1189,6 +1359,7 @@ Task_Share --> Redis
- 分享码删除操作需要MAC地址验证,防止越权操作。
- EQ数据存储需要JSON格式验证,防止恶意数据注入。
- **更新**:灰度升级设备管理需要MAC地址验证,防止越权访问。
- **新增**:耳机阻抗上报需要参数验证,防止恶意数据注入和数值溢出。
- **监控与运维**
- 添加Redis连接池监控,确保缓存层稳定运行。
- 添加数据库连接池监控,避免连接泄漏。
@@ -1196,4 +1367,13 @@ Task_Share --> Redis
- 添加分布式锁监控,防止死锁和资源竞争。
- 添加S3访问监控,确保CSV文件读取正常。
- **更新**:监控OTA表结构变化,确保简化的字段结构得到正确应用。
- **更新**:监控ota_target_device表结构变化,确保type字段已被移除,验证灰度升级设备查询逻辑。
- **更新**:监控ota_target_device表结构变化,确保type字段已被移除,验证灰度升级设备查询逻辑。
- **新增**:监控耳机阻抗表结构变化,确保唯一索引正常工作,验证去重更新逻辑。
- **新增**:监控耳机阻抗持久化任务的执行状态和错误率。
**章节来源**
- [internal/config/config.go:10-23](file://internal/config/config.go#L10-L23)
- [internal/config/config.go:68-69](file://internal/config/config.go#L68-L69)
- [internal/handler/impedance.go:51-66](file://internal/handler/impedance.go#L51-L66)
- [internal/task/impedance_persist.go:102-107](file://internal/task/impedance_persist.go#L102-L107)
- [sql/user_headphone_impedance.sql:32-35](file://sql/user_headphone_impedance.sql#L32-L35)