feat(impedance): 新增用户耳机阻抗功能模块
- 设计并添加 user_headphone_impedance 表,包含唯一复合索引保证数据完整性 - 实现阻抗上报API接口 /audio/reportImpedance,支持设备上传阻抗数据 - 使用Redis Hash结构缓存阻抗数据,键为 "headphone_impedances" - 新增定时持久化任务,每5分钟从Redis读取数据批量写入数据库 - 对品牌和型号数据进行trim+lower归一化处理,支持数据去重更新 - 优化查询性能,确保mac_addr与品牌型号归一化组合唯一索引生效 - 更新故障排查指南,新增Redis缓存及唯一索引相关问题检查 - 新增阻抗持久化配置开关,允许按需启用该功能
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
- [internal/repository/ota.go](file://internal/repository/ota.go)
|
||||
- [internal/repository/curve.go](file://internal/repository/curve.go)
|
||||
- [internal/repository/share_code.go](file://internal/repository/share_code.go)
|
||||
- [internal/repository/headphone_impedance.go](file://internal/repository/headphone_impedance.go)
|
||||
- [internal/cache/brand_cache.go](file://internal/cache/brand_cache.go)
|
||||
- [internal/cache/model_cache.go](file://internal/cache/model_cache.go)
|
||||
- [internal/cache/curve_cache.go](file://internal/cache/curve_cache.go)
|
||||
@@ -24,20 +25,24 @@
|
||||
- [internal/model/share_code.go](file://internal/model/share_code.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/model/ota.go](file://internal/model/ota.go)
|
||||
- [internal/handler/brand.go](file://internal/handler/brand.go)
|
||||
- [internal/handler/model.go](file://internal/handler/model.go)
|
||||
- [internal/handler/device.go](file://internal/handler/device.go)
|
||||
- [internal/handler/impedance.go](file://internal/handler/impedance.go)
|
||||
- [internal/handler/ota.go](file://internal/handler/ota.go)
|
||||
- [internal/handler/curve.go](file://internal/handler/curve.go)
|
||||
- [internal/handler/share_code.go](file://internal/handler/share_code.go)
|
||||
- [internal/router/router.go](file://internal/router/router.go)
|
||||
- [internal/task/device_persist.go](file://internal/task/device_persist.go)
|
||||
- [internal/task/impedance_persist.go](file://internal/task/impedance_persist.go)
|
||||
- [internal/task/share_code_persist.go](file://internal/task/share_code_persist.go)
|
||||
- [cmd/server/main.go](file://cmd/server/main.go)
|
||||
- [sql/model.sql](file://sql/model.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)
|
||||
- [sql/ota.sql](file://sql/ota.sql)
|
||||
- [sql/ota_target_device.sql](file://sql/ota_target_device.sql)
|
||||
- [sql/share_code_log.sql](file://sql/share_code_log.sql)
|
||||
@@ -46,10 +51,12 @@
|
||||
|
||||
## 更新摘要
|
||||
**变更内容**
|
||||
- OTA仓库层实现已移除pawVerCode、pawVerName、pawUrl、pawMd5字段,简化了查询逻辑
|
||||
- OTA模型结构已精简,移除了不必要的字段,提高了数据传输效率
|
||||
- OTA查询方法的SQL语句已优化,减少了字段选择和扫描操作
|
||||
- OTA表结构已更新,移除了相关字段定义
|
||||
- 新增耳机阻抗数据数据库访问层(HeadphoneImpedanceRepository),支持按MAC地址和标准化耳机标识符查找、插入和更新记录
|
||||
- 实现完整的耳机阻抗数据持久化流程,包括Redis异步处理和定时任务同步
|
||||
- 新增ImpedanceHandler处理API请求,提供耳机阻抗上报接口
|
||||
- 新增ImpedancePersistTask定时任务,从Redis批量同步阻抗数据到数据库
|
||||
- 在main.go中集成新的阻抗持久化任务,支持环境变量控制开关
|
||||
- OTA仓库层已移除pawVerCode、pawVerName、pawUrl、pawMd5字段,简化查询逻辑
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
@@ -65,10 +72,10 @@
|
||||
11. [附录:扩展新 Repository 指南](#附录扩展新-repository-指南)
|
||||
|
||||
## 简介
|
||||
本文件聚焦于 Luxsin 应用 API 的数据访问层(Repository 层),系统性阐述 BrandRepository、ModelRepository、DeviceRepository、OTARepository、CurveRepository、ShareCodeRepository 的设计模式、实现原理与职责边界;解释其在整体架构中的位置与交互方式;深入分析数据库连接管理、SQL 查询优化与事务处理机制;详细介绍新增的缓存感知查询逻辑、多级缓存策略和智能数据刷新机制;给出使用示例路径、错误处理与异常管理策略;总结并发安全、连接池管理与资源清理的最佳实践,并提供扩展新 Repository 的指导原则与注意事项。文档兼顾初学者与资深开发者的需求,既提供高层架构视图,也给出可落地的实现细节。
|
||||
本文件聚焦于 Luxsin 应用 API 的数据访问层(Repository 层),系统性阐述 BrandRepository、ModelRepository、DeviceRepository、OTARepository、CurveRepository、ShareCodeRepository 以及新增的 HeadphoneImpedanceRepository 的设计模式、实现原理与职责边界;解释其在整体架构中的位置与交互方式;深入分析数据库连接管理、SQL 查询优化与事务处理机制;详细介绍新增的缓存感知查询逻辑、多级缓存策略和智能数据刷新机制;给出使用示例路径、错误处理与异常管理策略;总结并发安全、连接池管理与资源清理的最佳实践,并提供扩展新 Repository 的指导原则与注意事项。文档兼顾初学者与资深开发者的需求,既提供高层架构视图,也给出可落地的实现细节。
|
||||
|
||||
## 项目结构
|
||||
数据访问层位于 internal/repository 目录,配合 internal/cache 提供缓存层,internal/database 提供底层数据库连接,internal/model 定义领域模型,internal/handler 通过注入的 Repository 执行业务逻辑,internal/task 提供设备数据持久化任务,最终由 Gin 路由暴露接口。
|
||||
数据访问层位于 internal/repository 目录,配合 internal/cache 提供缓存层,internal/database 提供底层数据库连接,internal/model 定义领域模型,internal/handler 通过注入的 Repository 执行业务逻辑,internal/task 提供设备数据和阻抗数据持久化任务,最终由 Gin 路由暴露接口。
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
@@ -80,6 +87,7 @@ ROUTER["internal/router/router.go<br/>注册路由与中间件"]
|
||||
BRAND_H["internal/handler/brand.go<br/>品牌处理器"]
|
||||
MODEL_H["internal/handler/model.go<br/>型号处理器"]
|
||||
DEVICE_H["internal/handler/device.go<br/>设备处理器"]
|
||||
IMPEDANCE_H["internal/handler/impedance.go<br/>阻抗处理器"]
|
||||
OTA_H["internal/handler/ota.go<br/>OTA处理器"]
|
||||
CURVE_H["internal/handler/curve.go<br/>曲线处理器"]
|
||||
SHARE_H["internal/handler/share_code.go<br/>分享码处理器"]
|
||||
@@ -88,6 +96,7 @@ subgraph "数据访问层"
|
||||
BR_REPO["internal/repository/brand.go<br/>BrandRepository"]
|
||||
MD_REPO["internal/repository/model.go<br/>ModelRepository"]
|
||||
DEV_REPO["internal/repository/device.go<br/>DeviceRepository"]
|
||||
IMPEDANCE_REPO["internal/repository/headphone_impedance.go<br/>HeadphoneImpedanceRepository"]
|
||||
OTA_REPO["internal/repository/ota.go<br/>OTARepository"]
|
||||
CURVE_REPO["internal/repository/curve.go<br/>CurveRepository"]
|
||||
SHARE_REPO["internal/repository/share_code.go<br/>ShareCodeRepository"]
|
||||
@@ -101,6 +110,7 @@ REDIS["internal/cache/redis.go<br/>Redis 客户端"]
|
||||
end
|
||||
subgraph "任务调度"
|
||||
PERSIST_TASK["internal/task/device_persist.go<br/>设备持久化任务"]
|
||||
IMPEDANCE_PERSIST_TASK["internal/task/impedance_persist.go<br/>阻抗持久化任务"]
|
||||
SHARE_PERSIST_TASK["internal/task/share_code_persist.go<br/>分享码持久化任务"]
|
||||
end
|
||||
subgraph "数据库与配置"
|
||||
@@ -112,6 +122,7 @@ CFG_APP["internal/config/config.go<br/>应用配置聚合"]
|
||||
MODEL_TBL["sql/model.sql<br/>表结构定义"]
|
||||
USER_DEVICE_TBL["sql/user_device.sql<br/>用户设备表"]
|
||||
USER_ACTIVE_TBL["sql/user_active.sql<br/>用户活跃表"]
|
||||
USER_IMPEDANCE_TBL["sql/user_headphone_impedance.sql<br/>用户耳机阻抗表"]
|
||||
OTA_TBL["sql/ota.sql<br/>OTA表"]
|
||||
OTA_TARGET_TBL["sql/ota_target_device.sql<br/>OTA定向设备表"]
|
||||
SHARE_LOG_TBL["sql/share_code_log.sql<br/>分享码日志表"]
|
||||
@@ -121,6 +132,7 @@ M_BRAND["internal/model/brand.go<br/>Brand 模型"]
|
||||
M_MODEL["internal/model/model.go<br/>Model 模型"]
|
||||
M_USER_DEVICE["internal/model/user_device.go<br/>UserDevice 模型"]
|
||||
M_USER_ACTIVE["internal/model/user_active.go<br/>UserActive 模型"]
|
||||
M_USER_IMPEDANCE["internal/model/user_headphone_impedance.go<br/>UserHeadphoneImpedance 模型"]
|
||||
M_OTA["internal/model/ota.go<br/>OTA 模型"]
|
||||
M_SHARE["internal/model/share_code.go<br/>ShareCode 模型"]
|
||||
end
|
||||
@@ -128,12 +140,14 @@ MAIN --> ROUTER
|
||||
ROUTER --> BRAND_H
|
||||
ROUTER --> MODEL_H
|
||||
ROUTER --> DEVICE_H
|
||||
ROUTER --> IMPEDANCE_H
|
||||
ROUTER --> OTA_H
|
||||
ROUTER --> CURVE_H
|
||||
ROUTER --> SHARE_H
|
||||
BRAND_H --> BR_REPO
|
||||
MODEL_H --> MD_REPO
|
||||
DEVICE_H --> DEV_REPO
|
||||
IMPEDANCE_H --> IMPEDANCE_REPO
|
||||
OTA_H --> OTA_REPO
|
||||
CURVE_H --> CURVE_REPO
|
||||
SHARE_H --> SHARE_REPO
|
||||
@@ -146,11 +160,14 @@ MD_CACHE --> REDIS
|
||||
CURVE_CACHE --> REDIS
|
||||
SHARE_CACHE --> REDIS
|
||||
DEV_REPO --> DB_SQL
|
||||
IMPEDANCE_REPO --> DB_SQL
|
||||
OTA_REPO --> DB_SQL
|
||||
CURVE_REPO --> DB_SQL
|
||||
SHARE_REPO --> DB_SQL
|
||||
PERSIST_TASK --> DEV_REPO
|
||||
PERSIST_TASK --> REDIS
|
||||
IMPEDANCE_PERSIST_TASK --> IMPEDANCE_REPO
|
||||
IMPEDANCE_PERSIST_TASK --> REDIS
|
||||
SHARE_PERSIST_TASK --> SHARE_REPO
|
||||
SHARE_PERSIST_TASK --> SHARE_CACHE
|
||||
DB_SQL --> CFG_DB
|
||||
@@ -161,11 +178,13 @@ BR_REPO --> M_BRAND
|
||||
MD_REPO --> M_MODEL
|
||||
DEV_REPO --> M_USER_DEVICE
|
||||
DEV_REPO --> M_USER_ACTIVE
|
||||
IMPEDANCE_REPO --> M_USER_IMPEDANCE
|
||||
OTA_REPO --> M_OTA
|
||||
SHARE_REPO --> M_SHARE
|
||||
MODEL_TBL --> DB_SQL
|
||||
USER_DEVICE_TBL --> DB_SQL
|
||||
USER_ACTIVE_TBL --> DB_SQL
|
||||
USER_IMPEDANCE_TBL --> DB_SQL
|
||||
OTA_TBL --> DB_SQL
|
||||
OTA_TARGET_TBL --> DB_SQL
|
||||
SHARE_LOG_TBL --> DB_SQL
|
||||
@@ -177,15 +196,17 @@ SHARE_LOG_TBL --> DB_SQL
|
||||
- [internal/handler/brand.go:19-24](file://internal/handler/brand.go#L19-L24)
|
||||
- [internal/handler/model.go:19-24](file://internal/handler/model.go#L19-L24)
|
||||
- [internal/handler/device.go:15-25](file://internal/handler/device.go#L15-L25)
|
||||
- [internal/handler/impedance.go:23-28](file://internal/handler/impedance.go#L23-L28)
|
||||
- [internal/handler/ota.go:14-21](file://internal/handler/ota.go#L14-L21)
|
||||
- [internal/handler/curve.go:27-39](file://internal/handler/curve.go#L27-L39)
|
||||
- [internal/handler/share_code.go:15-23](file://internal/handler/share_code.go#L15-L23)
|
||||
- [internal/repository/brand.go:14-21](file://internal/repository/brand.go#L14-L21)
|
||||
- [internal/repository/model.go:14-21](file://internal/repository/model.go#L14-L21)
|
||||
- [internal/repository/device.go:11-17](file://internal/repository/device.go#L11-L17)
|
||||
- [internal/repository/headphone_impedance.go:15-17](file://internal/repository/headphone_impedance.go#L15-L17)
|
||||
- [internal/repository/ota.go:11-17](file://internal/repository/ota.go#L11-L17)
|
||||
- [internal/repository/curve.go:11-17](file://internal/repository/curve.go#L11-L17)
|
||||
- [internal/repository/share_code.go:12-18](file://internal/repository/share_code.go#L12-L18)
|
||||
- [internal/repository/share_code.go:12-18](file://internal/repository/share_code.go#L12-18)
|
||||
- [internal/cache/brand_cache.go:18-24](file://internal/cache/brand_cache.go#L18-L24)
|
||||
- [internal/cache/model_cache.go:19-25](file://internal/cache/model_cache.go#L19-L25)
|
||||
- [internal/cache/curve_cache.go:18-25](file://internal/cache/curve_cache.go#L18-L25)
|
||||
@@ -197,10 +218,12 @@ SHARE_LOG_TBL --> DB_SQL
|
||||
- [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52)
|
||||
- [internal/config/share_code_ttl.go:12-59](file://internal/config/share_code_ttl.go#L12-L59)
|
||||
- [internal/task/device_persist.go:14-22](file://internal/task/device_persist.go#L14-L22)
|
||||
- [internal/task/impedance_persist.go:22-28](file://internal/task/impedance_persist.go#L22-L28)
|
||||
- [internal/task/share_code_persist.go:14-22](file://internal/task/share_code_persist.go#L14-L22)
|
||||
- [sql/model.sql:24-35](file://sql/model.sql#L24-L35)
|
||||
- [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)
|
||||
- [sql/ota.sql:23-44](file://sql/ota.sql#L23-L44)
|
||||
- [sql/ota_target_device.sql:23-31](file://sql/ota_target_device.sql#L23-L31)
|
||||
- [sql/share_code_log.sql:8-23](file://sql/share_code_log.sql#L8-L23)
|
||||
@@ -211,12 +234,14 @@ SHARE_LOG_TBL --> DB_SQL
|
||||
- [internal/handler/brand.go:19-24](file://internal/handler/brand.go#L19-L24)
|
||||
- [internal/handler/model.go:19-24](file://internal/handler/model.go#L19-L24)
|
||||
- [internal/handler/device.go:15-25](file://internal/handler/device.go#L15-L25)
|
||||
- [internal/handler/impedance.go:23-28](file://internal/handler/impedance.go#L23-L28)
|
||||
- [internal/handler/ota.go:14-21](file://internal/handler/ota.go#L14-L21)
|
||||
- [internal/handler/curve.go:27-39](file://internal/handler/curve.go#L27-L39)
|
||||
- [internal/handler/share_code.go:15-23](file://internal/handler/share_code.go#L15-L23)
|
||||
- [internal/repository/brand.go:14-21](file://internal/repository/brand.go#L14-L21)
|
||||
- [internal/repository/model.go:14-21](file://internal/repository/model.go#L14-L21)
|
||||
- [internal/repository/device.go:11-17](file://internal/repository/device.go#L11-L17)
|
||||
- [internal/repository/headphone_impedance.go:15-17](file://internal/repository/headphone_impedance.go#L15-L17)
|
||||
- [internal/repository/ota.go:11-17](file://internal/repository/ota.go#L11-L17)
|
||||
- [internal/repository/curve.go:11-17](file://internal/repository/curve.go#L11-L17)
|
||||
- [internal/repository/share_code.go:12-18](file://internal/repository/share_code.go#L12-L18)
|
||||
@@ -231,10 +256,12 @@ SHARE_LOG_TBL --> DB_SQL
|
||||
- [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52)
|
||||
- [internal/config/share_code_ttl.go:12-59](file://internal/config/share_code_ttl.go#L12-L59)
|
||||
- [internal/task/device_persist.go:14-22](file://internal/task/device_persist.go#L14-L22)
|
||||
- [internal/task/impedance_persist.go:22-28](file://internal/task/impedance_persist.go#L22-L28)
|
||||
- [internal/task/share_code_persist.go:14-22](file://internal/task/share_code_persist.go#L14-L22)
|
||||
- [sql/model.sql:24-35](file://sql/model.sql#L24-L35)
|
||||
- [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)
|
||||
- [sql/ota.sql:23-44](file://sql/ota.sql#L23-L44)
|
||||
- [sql/ota_target_device.sql:23-31](file://sql/ota_target_device.sql#L23-L31)
|
||||
- [sql/share_code_log.sql:8-23](file://sql/share_code_log.sql#L8-L23)
|
||||
@@ -243,12 +270,14 @@ SHARE_LOG_TBL --> DB_SQL
|
||||
- **BrandRepository**:负责品牌列表查询,支持缓存感知的全量查询和按名称模糊过滤,返回 Brand 领域对象切片。
|
||||
- **ModelRepository**:负责型号列表查询,支持缓存感知的按品牌精确匹配、按型号名模糊匹配和全量查询,返回 Model 领域对象切片,并对可空字段进行 NullString 到指针字符串的安全转换。
|
||||
- **DeviceRepository**:负责设备信息管理,支持按MAC地址查询设备、插入新设备、更新设备版本信息,以及用户活跃信息的查询和更新。
|
||||
- **HeadphoneImpedanceRepository**:**新增** 负责耳机阻抗数据管理,支持按MAC地址和标准化耳机标识符查找记录、插入新记录和更新现有记录,实现阻抗数据的去重和最新值维护。
|
||||
- **OTARepository**:负责固件升级信息查询,支持按型号、硬件版本、灰度标识查询最新OTA记录,黑名单检查、定向设备筛选等功能。**已精简**:移除了pawVerCode、pawVerName、pawUrl、pawMd5字段,简化了查询逻辑和数据处理流程。
|
||||
- **CurveRepository**:负责频响曲线数据查询,支持按品牌+型号查询Model信息和按标签查询Target信息,为曲线缓存提供数据支撑。
|
||||
- **ShareCodeRepository**:负责分享码日志管理,支持插入分享码操作日志、查询导出日志等数据库操作,提供完整的分享码数据持久化能力。
|
||||
- **CurveCache**:新增的曲线缓存层,支持fr数据分离存储、分布式锁机制、缓存合并逻辑,优化曲线数据访问性能。
|
||||
- **ShareCodeCache**:新增的分享码缓存层,支持Lua原子操作、MAC地址索引、导入导出队列、分布式锁等高级缓存功能,提供完整的分享码生命周期管理。
|
||||
- **设备持久化任务**:DevicePersistTask 从Redis批量读取设备信息,异步写入MySQL user_device 和 user_active 表,支持数据可靠性保障。
|
||||
- **阻抗持久化任务**:**新增** ImpedancePersistTask 从Redis批量读取耳机阻抗数据,异步写入MySQL user_headphone_impedance 表,支持数据去重和最新值更新。
|
||||
- **分享码持久化任务**:ShareCodePersistTask 从Redis批量读取分享码数据,异步写入MySQL share_code_log 表,支持数据可靠性保障。
|
||||
- **数据库连接**:通过 sql.DB 统一管理连接池,设置最大打开连接数、空闲连接数与连接生命周期,并在启动时进行 Ping 校验。
|
||||
- **配置加载**:从环境变量或默认值加载数据库、Redis和分享码TTL配置,生产环境要求提供密码。
|
||||
@@ -258,12 +287,14 @@ SHARE_LOG_TBL --> DB_SQL
|
||||
- [internal/repository/brand.go:23-52](file://internal/repository/brand.go#L23-L52)
|
||||
- [internal/repository/model.go:23-79](file://internal/repository/model.go#L23-L79)
|
||||
- [internal/repository/device.go:19-87](file://internal/repository/device.go#L19-L87)
|
||||
- [internal/repository/headphone_impedance.go:19-99](file://internal/repository/headphone_impedance.go#L19-L99)
|
||||
- [internal/repository/ota.go:19-158](file://internal/repository/ota.go#L19-L158)
|
||||
- [internal/repository/curve.go:19-65](file://internal/repository/curve.go#L19-L65)
|
||||
- [internal/repository/share_code.go:20-58](file://internal/repository/share_code.go#L20-L58)
|
||||
- [internal/cache/curve_cache.go:18-148](file://internal/cache/curve_cache.go#L18-L148)
|
||||
- [internal/cache/share_code_cache.go:127-353](file://internal/cache/share_code_cache.go#L127-L353)
|
||||
- [internal/task/device_persist.go:14-167](file://internal/task/device_persist.go#L14-L167)
|
||||
- [internal/task/impedance_persist.go:30-163](file://internal/task/impedance_persist.go#L30-L163)
|
||||
- [internal/task/share_code_persist.go:14-22](file://internal/task/share_code_persist.go#L14-L22)
|
||||
- [internal/database/mysql.go:14-46](file://internal/database/mysql.go#L14-L46)
|
||||
- [internal/config/database.go:17-72](file://internal/config/database.go#L17-L72)
|
||||
@@ -273,17 +304,21 @@ SHARE_LOG_TBL --> DB_SQL
|
||||
- [internal/handler/brand.go:19-24](file://internal/handler/brand.go#L19-L24)
|
||||
- [internal/handler/model.go:19-24](file://internal/handler/model.go#L19-L24)
|
||||
- [internal/handler/device.go:15-25](file://internal/handler/device.go#L15-L25)
|
||||
- [internal/handler/impedance.go:23-28](file://internal/handler/impedance.go#L23-L28)
|
||||
- [internal/handler/ota.go:14-21](file://internal/handler/ota.go#L14-L21)
|
||||
- [internal/handler/curve.go:27-39](file://internal/handler/curve.go#L27-L39)
|
||||
- [internal/handler/share_code.go:15-23](file://internal/handler/share_code.go#L15-L23)
|
||||
|
||||
## 架构总览
|
||||
数据访问层采用"仓储模式"(Repository Pattern)封装数据库访问,将查询逻辑与业务逻辑解耦。新增的缓存层提供多级缓存策略,支持智能数据刷新和降级机制。新增的设备持久化仓库和OTA仓库进一步完善了数据访问层的功能,支持设备信息上报、固件升级查询等核心业务场景。新增的曲线缓存和分享码缓存层提供了更高级的缓存策略,包括fr数据分离存储、分布式锁机制、Lua原子操作等。通过集成 Redis 缓存层和异步持久化任务,显著提升了系统的响应性能和数据可靠性。整体流程如下:
|
||||
数据访问层采用"仓储模式"(Repository Pattern)封装数据库访问,将查询逻辑与业务逻辑解耦。新增的缓存层提供多级缓存策略,支持智能数据刷新和降级机制。新增的设备持久化仓库、阻抗持久化仓库、OTA仓库、曲线仓库和分享码仓库进一步完善了数据访问层的功能,支持设备信息上报、耳机阻抗数据采集、固件升级查询、频响曲线计算和分享码管理等核心业务场景。新增的CurveCache和ShareCodeCache提供了更高级的缓存策略,包括fr数据分离存储、分布式锁机制、Lua原子操作等。通过集成 Redis 缓存层和异步持久化任务,显著提升了系统的响应性能和数据可靠性。整体流程如下:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as "客户端"
|
||||
participant Router as "Gin 路由"
|
||||
participant ImpedanceHandler as "ImpedanceHandler"
|
||||
participant ImpedanceRepo as "HeadphoneImpedanceRepository"
|
||||
participant ImpedanceTask as "ImpedancePersistTask"
|
||||
participant CurveHandler as "CurveHandler"
|
||||
participant ShareHandler as "ShareCodeHandler"
|
||||
participant CurveRepo as "CurveRepository"
|
||||
@@ -294,6 +329,26 @@ participant DB as "sql.DB"
|
||||
participant Redis as "Redis 服务器"
|
||||
participant MySQL as "MySQL 服务器"
|
||||
Client->>Router : "HTTP 请求"
|
||||
Router->>ImpedanceHandler : "上报阻抗"
|
||||
ImpedanceHandler->>Redis : "HSet headphone_impedances"
|
||||
Redis-->>ImpedanceHandler : "写入成功"
|
||||
ImpedanceHandler-->>Client : "操作成功"
|
||||
Note over ImpedanceTask,MySQL : 定时任务每5分钟执行
|
||||
ImpedanceTask->>Redis : "HGetAll headphone_impedances"
|
||||
Redis-->>ImpedanceTask : "获取待持久化数据"
|
||||
ImpedanceTask->>ImpedanceRepo : "FindByMacAndNorm"
|
||||
ImpedanceRepo->>DB : "QueryRowContext"
|
||||
DB-->>ImpedanceRepo : "查询结果"
|
||||
alt 记录不存在
|
||||
ImpedanceTask->>ImpedanceRepo : "Insert"
|
||||
ImpedanceRepo->>DB : "ExecContext"
|
||||
DB-->>ImpedanceRepo : "插入成功"
|
||||
else 记录存在
|
||||
ImpedanceTask->>ImpedanceRepo : "Update"
|
||||
ImpedanceRepo->>DB : "ExecContext"
|
||||
DB-->>ImpedanceRepo : "更新成功"
|
||||
end
|
||||
ImpedanceTask->>Redis : "HDel 已处理数据"
|
||||
Router->>CurveHandler : "曲线查询"
|
||||
CurveHandler->>CurveCache : "GetWithFR"
|
||||
CurveCache->>Redis : "HGet brand name target"
|
||||
@@ -329,6 +384,9 @@ ShareHandler-->>Client : "操作结果"
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [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/curve.go:214-292](file://internal/handler/curve.go#L214-L292)
|
||||
- [internal/handler/share_code.go:40-125](file://internal/handler/share_code.go#L40-L125)
|
||||
- [internal/repository/curve.go:19-65](file://internal/repository/curve.go#L19-L65)
|
||||
@@ -513,6 +571,57 @@ 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)
|
||||
|
||||
### HeadphoneImpedanceRepository 设计与实现
|
||||
- **设计模式**:仓储模式,面向领域模型 UserHeadphoneImpedance,封装耳机阻抗数据管理逻辑。
|
||||
- **关键特性**:
|
||||
- **按MAC地址和标准化标识符查找**:`FindByMacAndNorm` 方法支持按MAC地址、标准化品牌名和标准化型号名精确查询阻抗记录
|
||||
- **插入新记录**:`Insert` 方法支持插入新的阻抗数据,包含原始输入和标准化字段
|
||||
- **更新现有记录**:`Update` 方法支持更新现有记录的阻抗值、设备型号、IP地址等信息
|
||||
- **标准化处理**:支持耳机品牌和型号的标准化(trim+lower),用于去重和精确匹配
|
||||
- **错误处理**:针对不同操作场景提供详细的错误包装,便于问题定位
|
||||
- **资源管理**:使用 QueryRowContext 和 ExecContext,确保数据库连接正确释放
|
||||
- **表结构要点**:
|
||||
- user_headphone_impedance 表:主键ID,唯一索引(mac_addr, headphone_brand_norm, headphone_model_norm),支持按MAC和设备型号查询
|
||||
- 支持原始输入字段(headphone_brand, headphone_model)和标准化字段(headphone_brand_norm, headphone_model_norm)
|
||||
- 自动时间戳字段(create_at, update_at)
|
||||
- **应用场景**:耳机阻抗数据采集、用户设备配置管理、音频参数个性化等。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class HeadphoneImpedanceRepository {
|
||||
-db : "*sql.DB"
|
||||
+NewHeadphoneImpedanceRepository(db) HeadphoneImpedanceRepository
|
||||
+FindByMacAndNorm(ctx, macAddr, brandNorm, modelNorm) *UserHeadphoneImpedance,error
|
||||
+Insert(ctx, rec) error
|
||||
+Update(ctx, rec) error
|
||||
}
|
||||
class UserHeadphoneImpedance {
|
||||
+int ID
|
||||
+string MacAddr
|
||||
+string DeviceModel
|
||||
+int ImpedanceOhm
|
||||
+string HeadphoneBrand
|
||||
+string HeadphoneModel
|
||||
+string HeadphoneBrandNorm
|
||||
+string HeadphoneModelNorm
|
||||
+string IpAddr
|
||||
+time CreateAt
|
||||
+time UpdateAt
|
||||
}
|
||||
HeadphoneImpedanceRepository --> UserHeadphoneImpedance : "阻抗数据"
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [internal/repository/headphone_impedance.go:11-17](file://internal/repository/headphone_impedance.go#L11-L17)
|
||||
- [internal/repository/headphone_impedance.go:19-99](file://internal/repository/headphone_impedance.go#L19-L99)
|
||||
- [internal/model/user_headphone_impedance.go:5-17](file://internal/model/user_headphone_impedance.go#L5-L17)
|
||||
|
||||
**章节来源**
|
||||
- [internal/repository/headphone_impedance.go:11-17](file://internal/repository/headphone_impedance.go#L11-L17)
|
||||
- [internal/repository/headphone_impedance.go:19-99](file://internal/repository/headphone_impedance.go#L19-L99)
|
||||
- [internal/model/user_headphone_impedance.go:5-17](file://internal/model/user_headphone_impedance.go#L5-L17)
|
||||
- [sql/user_headphone_impedance.sql:20-36](file://sql/user_headphone_impedance.sql#L20-L36)
|
||||
|
||||
### OTARepository 设计与实现
|
||||
- **设计模式**:仓储模式,面向领域模型 OTA、BlackList 和 OTATargetDevice,封装固件升级查询逻辑。
|
||||
- **关键特性**:
|
||||
@@ -682,7 +791,7 @@ ShareCodeRepository --> ShareCodeLog : "日志记录"
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [internal/repository/share_code.go:12-18](file://internal/repository/share_code.go#L12-L18)
|
||||
- [internal/repository/share_code.go:12-18](file://internal/repository/share_code.go#L12-18)
|
||||
- [internal/repository/share_code.go:20-58](file://internal/repository/share_code.go#L20-L58)
|
||||
- [internal/model/share_code.go:14-24](file://internal/model/share_code.go#L14-L24)
|
||||
|
||||
@@ -821,6 +930,53 @@ O --> P[结束]
|
||||
**章节来源**
|
||||
- [internal/task/device_persist.go:14-167](file://internal/task/device_persist.go#L14-L167)
|
||||
|
||||
### 阻抗持久化任务设计与实现
|
||||
- **设计模式**:定时任务模式,基于时间轮询的异步数据同步机制。
|
||||
- **关键特性**:
|
||||
- 定时执行:使用 time.Ticker 按固定间隔(默认5分钟)启动持久化任务
|
||||
- 批量处理:从Redis Hash `headphone_impedances` 一次性读取所有待持久化的阻抗数据
|
||||
- 去重更新:根据MAC地址和标准化耳机标识符判断记录是否存在,决定插入还是更新
|
||||
- 标准化处理:使用标准化后的品牌名和型号名进行精确匹配
|
||||
- 成功确认:批量删除已成功持久化的数据,避免重复处理
|
||||
- 错误隔离:单条数据处理失败不影响其他数据的处理
|
||||
- 配置开关:通过 `ENABLE_IMPEDANCE_PERSIST_TASK` 环境变量控制任务启用
|
||||
- **处理流程**:
|
||||
1. 从Redis读取 `headphone_impedances` Hash 中的所有阻抗数据
|
||||
2. 解析JSON格式的阻抗信息
|
||||
3. 调用 `FindByMacAndNorm` 检查记录是否存在
|
||||
4. 如果不存在则调用 `Insert` 插入新记录
|
||||
5. 如果存在则调用 `Update` 更新现有记录
|
||||
6. 批量删除已成功处理的数据
|
||||
- **应用场景**:高并发阻抗数据采集、数据可靠性保障、异步数据同步等。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[定时器触发] --> B[HGetAll headphone_impedances]
|
||||
B --> C{是否有待持久化数据?}
|
||||
C --> |否| D[结束]
|
||||
C --> |是| E[遍历阻抗数据]
|
||||
E --> F[解析JSON数据]
|
||||
F --> G[FindByMacAndNorm]
|
||||
G --> H{记录存在?}
|
||||
H --> |否| I[Insert 新记录]
|
||||
H --> |是| J[Update 现有记录]
|
||||
I --> K{处理成功?}
|
||||
J --> K
|
||||
K --> |是| L[记录成功字段]
|
||||
K --> |否| M[跳过该记录]
|
||||
L --> N[批量删除成功数据]
|
||||
M --> O[继续下一个]
|
||||
N --> P[记录处理结果]
|
||||
O --> E
|
||||
P --> Q[结束]
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [internal/task/impedance_persist.go:44-163](file://internal/task/impedance_persist.go#L44-L163)
|
||||
|
||||
**章节来源**
|
||||
- [internal/task/impedance_persist.go:30-163](file://internal/task/impedance_persist.go#L30-L163)
|
||||
|
||||
### 分享码持久化任务设计与实现
|
||||
- **设计模式**:定时任务模式,基于时间轮询的异步数据同步机制。
|
||||
- **关键特性**:
|
||||
@@ -864,6 +1020,7 @@ D --> L[结束]
|
||||
### Handler 与 Repository 的协作
|
||||
- Handler 通过 NewXxxHandler 注入相应的 Repository 实例,内部执行具体的业务逻辑。
|
||||
- DeviceHandler 处理设备信息上报,将数据写入Redis Hash,等待异步持久化任务处理。
|
||||
- **ImpedanceHandler**:**新增** 处理耳机阻抗上报,接收参数并进行标准化处理,将数据写入Redis Hash,等待异步持久化任务处理。
|
||||
- OTAHandler 处理OTA升级查询,调用OTARepository执行复杂的业务逻辑,包括黑名单检查、定向设备筛选等。
|
||||
- CurveHandler 处理曲线查询,调用CurveRepository和CurveCache执行数据查询和缓存操作,支持fr数据分离和分布式锁机制。
|
||||
- ShareCodeHandler 处理分享码操作,调用ShareCodeCache执行Lua原子操作和队列管理,支持导入导出流程。
|
||||
@@ -874,44 +1031,61 @@ D --> L[结束]
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as "客户端"
|
||||
participant IH as "ImpedanceHandler"
|
||||
participant IR as "HeadphoneImpedanceRepository"
|
||||
participant IT as "ImpedancePersistTask"
|
||||
participant CH as "CurveHandler"
|
||||
participant CR as "CurveRepository"
|
||||
participante CC as "CurveCache"
|
||||
C->>CH : "GET /audio/modelCurve"
|
||||
CH->>CC : "GetFR"
|
||||
CC->>Redis : "HGet brand name __fr"
|
||||
Redis-->>CC : "fr数据"
|
||||
alt fr缓存命中
|
||||
CH-->>C : "返回fr数据"
|
||||
else fr缓存未命中
|
||||
partcipante CC as "CurveCache"
|
||||
C->>IH : "GET /audio/reportImpedance"
|
||||
IH->>IH : "参数验证和标准化"
|
||||
IH->>Redis : "HSet headphone_impedances"
|
||||
Redis-->>IH : "写入成功"
|
||||
IH-->>C : "操作成功"
|
||||
Note over IT,IR : 定时任务每5分钟执行
|
||||
IT->>Redis : "HGetAll headphone_impedances"
|
||||
Redis-->>IT : "获取待持久化数据"
|
||||
IT->>IR : "FindByMacAndNorm"
|
||||
IR->>DB : "QueryRowContext"
|
||||
DB-->>IR : "查询结果"
|
||||
alt 记录不存在
|
||||
IT->>IR : "Insert"
|
||||
IR->>DB : "ExecContext"
|
||||
DB-->>IR : "插入成功"
|
||||
else 记录存在
|
||||
IT->>IR : "Update"
|
||||
IR->>DB : "ExecContext"
|
||||
DB-->>IR : "更新成功"
|
||||
end
|
||||
IT->>Redis : "HDel 已处理数据"
|
||||
C->>CH : "GET /audio/getCurve"
|
||||
CH->>CC : "GetWithFR"
|
||||
CC->>Redis : "HGet brand name target"
|
||||
Redis-->>CC : "target数据"
|
||||
alt target缓存命中
|
||||
CH->>CC : "GetFR"
|
||||
CC->>Redis : "HGet brand name __fr"
|
||||
Redis-->>CC : "fr数据"
|
||||
CH-->>C : "合并返回"
|
||||
else target缓存未命中
|
||||
Redis-->>CC : "缓存数据"
|
||||
alt 缓存命中
|
||||
CC-->>CH : "合并fr数据"
|
||||
CH-->>C : "返回曲线数据"
|
||||
else 缓存未命中
|
||||
CH->>CC : "AcquireLock"
|
||||
CC->>Redis : "SetNX 分布式锁"
|
||||
Redis-->>CH : "获取锁成功"
|
||||
CH->>CR : "GetModelByBrandAndName"
|
||||
CH->>CC : "双重检查缓存"
|
||||
CC->>Redis : "HGet brand name target"
|
||||
Redis-->>CH : "缓存已更新"
|
||||
CH->>CR : "查询Model和Target"
|
||||
CR->>DB : "QueryRowContext"
|
||||
DB-->>CR : "模型数据"
|
||||
CH->>CR : "GetTargetByLabel"
|
||||
CR->>DB : "QueryRowContext"
|
||||
DB-->>CR : "目标数据"
|
||||
DB-->>CR : "模型和目标数据"
|
||||
CH->>CC : "Set 缓存"
|
||||
CH->>CC : "Set __fr 缓存"
|
||||
CH->>CC : "Set __fr 独立缓存"
|
||||
CH->>CC : "ReleaseLock"
|
||||
CH-->>C : "返回曲线数据"
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
- [internal/handler/curve.go:56-127](file://internal/handler/curve.go#L56-L127)
|
||||
- [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/curve.go:214-292](file://internal/handler/curve.go#L214-L292)
|
||||
- [internal/repository/curve.go:19-65](file://internal/repository/curve.go#L19-L65)
|
||||
- [internal/cache/curve_cache.go:59-148](file://internal/cache/curve_cache.go#L59-L148)
|
||||
@@ -923,6 +1097,8 @@ end
|
||||
- [internal/handler/model.go:25-49](file://internal/handler/model.go#L25-L49)
|
||||
- [internal/handler/device.go:15-25](file://internal/handler/device.go#L15-L25)
|
||||
- [internal/handler/device.go:27-82](file://internal/handler/device.go#L27-L82)
|
||||
- [internal/handler/impedance.go:23-28](file://internal/handler/impedance.go#L23-L28)
|
||||
- [internal/handler/impedance.go:43-107](file://internal/handler/impedance.go#L43-L107)
|
||||
- [internal/handler/ota.go:14-21](file://internal/handler/ota.go#L14-L21)
|
||||
- [internal/handler/ota.go:23-132](file://internal/handler/ota.go#L23-L132)
|
||||
- [internal/handler/curve.go:27-39](file://internal/handler/curve.go#L27-L39)
|
||||
@@ -933,6 +1109,7 @@ end
|
||||
- [internal/repository/brand.go:23-52](file://internal/repository/brand.go#L23-L52)
|
||||
- [internal/repository/model.go:23-79](file://internal/repository/model.go#L23-L79)
|
||||
- [internal/repository/device.go:19-87](file://internal/repository/device.go#L19-L87)
|
||||
- [internal/repository/headphone_impedance.go:19-99](file://internal/repository/headphone_impedance.go#L19-L99)
|
||||
- [internal/repository/ota.go:19-158](file://internal/repository/ota.go#L19-L158)
|
||||
- [internal/repository/curve.go:19-65](file://internal/repository/curve.go#L19-L65)
|
||||
- [internal/repository/share_code.go:20-58](file://internal/repository/share_code.go#L20-L58)
|
||||
@@ -951,6 +1128,7 @@ end
|
||||
- **型号按品牌缓存**:`model:brand:{brandName}` - 存储特定品牌的所有型号
|
||||
- **型号全量缓存**:`model:all` - 存储所有型号信息
|
||||
- **设备活跃缓存**:`device:active:{date}` - 存储特定日期的活跃设备信息
|
||||
- **耳机阻抗缓存**:**新增** `headphone_impedances` Hash - 存储待持久化的阻抗数据,字段格式为 `mac|brand_norm|model_norm`
|
||||
- **曲线缓存**:`brand name` Hash - 存储曲线数据,字段为target或__fr
|
||||
- **分享码缓存**:`share:{code}` Hash - 存储分享码主数据,字段包括mac_addr、ip_addr、model、eq_data、expire_at、persisted
|
||||
- **分享码导入队列**:`share:import:pending` Hash - 存储导入日志,支持幂等性
|
||||
@@ -961,6 +1139,7 @@ end
|
||||
- **自动回写策略**:成功从数据库获取数据后自动回写到缓存
|
||||
- **TTL 策略**:所有缓存设置 30 分钟过期时间,平衡性能和数据新鲜度
|
||||
- **设备数据特殊处理**:设备活跃信息采用分区表存储,缓存策略需考虑日期维度
|
||||
- **耳机阻抗数据特殊处理**:**新增** 阻抗数据采用异步持久化策略,先写入Redis Hash,再由定时任务批量同步到数据库
|
||||
- **曲线数据优化**:fr数据独立存储在__fr字段,避免每个target重复存储
|
||||
- **分享码TTL配置**:支持多种时间单位格式,最大365天,动态配置
|
||||
- **分布式锁机制**:防止缓存击穿和竞态条件,确保数据一致性
|
||||
@@ -977,10 +1156,11 @@ D --> E[写入 brand:all 缓存]
|
||||
E --> F[查询型号全量数据]
|
||||
F --> G[写入 model:all 缓存]
|
||||
G --> H[设备数据无需预热]
|
||||
H --> I[OTA数据无需预热]
|
||||
I --> J[曲线缓存无需预热]
|
||||
J --> K[分享码缓存无需预热]
|
||||
K --> L[缓存预热完成]
|
||||
H --> I[阻抗数据无需预热]
|
||||
I --> J[OTA数据无需预热]
|
||||
J --> K[曲线缓存无需预热]
|
||||
K --> L[分享码缓存无需预热]
|
||||
L --> M[缓存预热完成]
|
||||
```
|
||||
|
||||
**图表来源**
|
||||
@@ -997,7 +1177,7 @@ K --> L[缓存预热完成]
|
||||
- **外部依赖**:Go MySQL Driver、Gin、Zap 日志、Redis 客户端、Meilisearch 客户端、AWS SDK。
|
||||
- **内部依赖**:Handler 依赖 Repository;Repository 依赖 *sql.DB 和缓存实例;缓存层依赖 Redis 客户端;数据库模块负责连接池与 Ping 校验;配置模块负责环境变量解析与校验;任务模块依赖 Redis 和 Repository。
|
||||
- **循环依赖**:未发现循环依赖,职责边界清晰。
|
||||
- **新增依赖**:CurveRepository 和 ShareCodeRepository 直接依赖 *sql.DB;CurveCache 和 ShareCodeCache 依赖 Redis 客户端;分享码持久化任务依赖 ShareCodeCache 和 ShareCodeRepository;CurveHandler 依赖 CurveRepository、CurveCache 和 S3 存储;ShareCodeHandler 依赖 ShareCodeCache。
|
||||
- **新增依赖**:CurveRepository 和 ShareCodeRepository 直接依赖 *sql.DB;CurveCache 和 ShareCodeCache 依赖 Redis 客户端;分享码持久化任务依赖 ShareCodeCache 和 ShareCodeRepository;CurveHandler 依赖 CurveRepository、CurveCache 和 S3 存储;ShareCodeHandler 依赖 ShareCodeCache;**ImpedanceHandler 依赖 Redis 客户端;ImpedancePersistTask 依赖 HeadphoneImpedanceRepository 和 Redis 客户端**。
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
@@ -1016,12 +1196,14 @@ CFG_LOAD --> CFG_SHARE_TTL["internal/config/share_code_ttl.go::parseShareCodeTTL
|
||||
ROUTER["internal/router/router.go"] --> BRAND_H["internal/handler/brand.go"]
|
||||
ROUTER --> MODEL_H["internal/handler/model.go"]
|
||||
ROUTER --> DEVICE_H["internal/handler/device.go"]
|
||||
ROUTER --> IMPEDANCE_H["internal/handler/impedance.go"]
|
||||
ROUTER --> OTA_H["internal/handler/ota.go"]
|
||||
ROUTER --> CURVE_H["internal/handler/curve.go"]
|
||||
ROUTER --> SHARE_H["internal/handler/share_code.go"]
|
||||
BRAND_H --> BR_REPO["internal/repository/brand.go"]
|
||||
MODEL_H --> MD_REPO["internal/repository/model.go"]
|
||||
DEVICE_H --> DEV_REPO["internal/repository/device.go"]
|
||||
IMPEDANCE_H --> IMPEDANCE_REPO["internal/repository/headphone_impedance.go"]
|
||||
OTA_H --> OTA_REPO["internal/repository/ota.go"]
|
||||
CURVE_H --> CURVE_REPO["internal/repository/curve.go"]
|
||||
SHARE_H --> SHARE_REPO["internal/repository/share_code.go"]
|
||||
@@ -1034,11 +1216,14 @@ MD_CACHE --> REDIS_CLIENT
|
||||
CURVE_CACHE --> REDIS_CLIENT
|
||||
SHARE_CACHE --> REDIS_CLIENT
|
||||
DEV_REPO --> DB_SQL["*sql.DB"]
|
||||
IMPEDANCE_REPO --> DB_SQL
|
||||
OTA_REPO --> DB_SQL
|
||||
CURVE_REPO --> DB_SQL
|
||||
SHARE_REPO --> DB_SQL
|
||||
PERSIST_TASK --> DEV_REPO
|
||||
PERSIST_TASK --> REDIS_CLIENT
|
||||
IMPEDANCE_PERSIST_TASK --> IMPEDANCE_REPO
|
||||
IMPEDANCE_PERSIST_TASK --> REDIS_CLIENT
|
||||
SHARE_PERSIST_TASK --> SHARE_REPO
|
||||
SHARE_PERSIST_TASK --> SHARE_CACHE
|
||||
BR_CACHE --> REDIS_CLIENT
|
||||
@@ -1056,12 +1241,14 @@ MD_CACHE --> REDIS_CLIENT
|
||||
- [internal/handler/brand.go:19-24](file://internal/handler/brand.go#L19-L24)
|
||||
- [internal/handler/model.go:19-24](file://internal/handler/model.go#L19-L24)
|
||||
- [internal/handler/device.go:15-25](file://internal/handler/device.go#L15-L25)
|
||||
- [internal/handler/impedance.go:23-28](file://internal/handler/impedance.go#L23-L28)
|
||||
- [internal/handler/ota.go:14-21](file://internal/handler/ota.go#L14-L21)
|
||||
- [internal/handler/curve.go:27-39](file://internal/handler/curve.go#L27-L39)
|
||||
- [internal/handler/share_code.go:15-23](file://internal/handler/share_code.go#L15-L23)
|
||||
- [internal/repository/brand.go:14-21](file://internal/repository/brand.go#L14-L21)
|
||||
- [internal/repository/model.go:14-21](file://internal/repository/model.go#L14-L21)
|
||||
- [internal/repository/device.go:11-17](file://internal/repository/device.go#L11-L17)
|
||||
- [internal/repository/headphone_impedance.go:15-17](file://internal/repository/headphone_impedance.go#L15-L17)
|
||||
- [internal/repository/ota.go:11-17](file://internal/repository/ota.go#L11-L17)
|
||||
- [internal/repository/curve.go:11-17](file://internal/repository/curve.go#L11-L17)
|
||||
- [internal/repository/share_code.go:12-18](file://internal/repository/share_code.go#L12-L18)
|
||||
@@ -1081,12 +1268,14 @@ MD_CACHE --> REDIS_CLIENT
|
||||
- [internal/handler/brand.go:19-24](file://internal/handler/brand.go#L19-L24)
|
||||
- [internal/handler/model.go:19-24](file://internal/handler/model.go#L19-L24)
|
||||
- [internal/handler/device.go:15-25](file://internal/handler/device.go#L15-L25)
|
||||
- [internal/handler/impedance.go:23-28](file://internal/handler/impedance.go#L23-L28)
|
||||
- [internal/handler/ota.go:14-21](file://internal/handler/ota.go#L14-L21)
|
||||
- [internal/handler/curve.go:27-39](file://internal/handler/curve.go#L27-L39)
|
||||
- [internal/handler/share_code.go:15-23](file://internal/handler/share_code.go#L15-L23)
|
||||
- [internal/repository/brand.go:14-21](file://internal/repository/brand.go#L14-L21)
|
||||
- [internal/repository/model.go:14-21](file://internal/repository/model.go#L14-L21)
|
||||
- [internal/repository/device.go:11-17](file://internal/repository/device.go#L11-L17)
|
||||
- [internal/repository/headphone_impedance.go:15-17](file://internal/repository/headphone_impedance.go#L15-L17)
|
||||
- [internal/repository/ota.go:11-17](file://internal/repository/ota.go#L11-L17)
|
||||
- [internal/repository/curve.go:11-17](file://internal/repository/curve.go#L11-L17)
|
||||
- [internal/repository/share_code.go:12-18](file://internal/repository/share_code.go#L12-L18)
|
||||
@@ -1110,10 +1299,12 @@ MD_CACHE --> REDIS_CLIENT
|
||||
- 分布式锁:限制同一时间只有一个请求在计算曲线数据
|
||||
- 分享码缓存优化:Lua原子操作确保数据一致性,避免竞态条件
|
||||
- 分布式锁:防止缓存击穿和竞态条件,确保数据一致性
|
||||
- **阻抗数据优化**:**新增** 采用异步持久化策略,先写入Redis再批量同步,提升响应速度和数据可靠性
|
||||
- **查询优化**
|
||||
- BrandRepository:按名称模糊匹配,建议在 name 上建立合适索引;对大结果集引入分页。
|
||||
- ModelRepository:按品牌名精确匹配或按型号名模糊匹配,建议为 brand_name 与 name 建立索引;对大结果集引入分页。
|
||||
- DeviceRepository:user_device 表的 MAC 地址建立唯一索引,user_active 表建立复合索引(mac_addr, active_date)。
|
||||
- **HeadphoneImpedanceRepository**:**新增** user_headphone_impedance 表建立唯一索引(mac_addr, headphone_brand_norm, headphone_model_norm),支持按MAC和设备型号查询。
|
||||
- OTARepository:ota 表建立复合索引(model, hw, beta, status),black_list 表建立索引(ota_id, mac),ota_target_device 表建立索引(ota_id, mac_addr)。**已优化**:移除了pawVerCode、pawVerName、pawUrl、pawMd5字段,减少了查询字段数量,提升了查询性能。
|
||||
- CurveRepository:model 表建立复合索引(brand_name, name),target 表建立索引(label)。
|
||||
- ShareCodeRepository:share_code_log 表建立索引(mac_addr, share_code, create_at)。
|
||||
@@ -1122,26 +1313,31 @@ MD_CACHE --> REDIS_CLIENT
|
||||
- *sql.DB 是并发安全的,可在多个 goroutine 中共享使用;Repository 实例不持有状态,亦可并发安全使用。
|
||||
- Redis 客户端是并发安全的,支持多goroutine同时访问。
|
||||
- 设备持久化任务使用独立的 goroutine 执行,避免阻塞主线程。
|
||||
- **阻抗持久化任务**:**新增** 使用独立的 goroutine 执行,避免阻塞主线程,支持环境变量控制开关。
|
||||
- 分布式锁机制确保缓存击穿防护,防止多个请求同时计算相同数据。
|
||||
- 分享码缓存使用Lua脚本确保原子性,避免竞态条件。
|
||||
- **资源清理**
|
||||
- 使用 defer rows.Close() 保证结果集关闭;在 main 中 defer db.Close() 保证应用退出时关闭连接池。
|
||||
- Redis 连接在应用退出时自动关闭。
|
||||
- 设备持久化任务使用 time.Ticker,确保正确停止。
|
||||
- **阻抗持久化任务**:**新增** 使用 time.Ticker,确保正确停止,支持优雅关闭。
|
||||
- 分布式锁在释放时自动清理,避免锁泄露。
|
||||
- 分享码导入队列使用TTL自动清理,避免无限增长。
|
||||
- **事务处理**
|
||||
- 当前实现均为只读查询,未涉及事务;如需写操作,应在 Repository 层封装事务,使用 sql.Tx 并在错误时回滚,成功时提交。
|
||||
- 设备持久化任务采用幂等设计,避免重复处理造成的数据不一致。
|
||||
- **阻抗持久化任务**:**新增** 采用幂等设计,通过唯一索引确保数据一致性,避免重复插入。
|
||||
- 分布式锁机制确保缓存更新的一致性。
|
||||
- 分享码持久化任务使用分布式锁确保数据一致性。
|
||||
- **异步处理优化**
|
||||
- 设备信息上报采用异步持久化,提高响应速度
|
||||
- **阻抗数据上报**:**新增** 采用异步持久化,先写入Redis Hash,再由定时任务批量同步到数据库,显著提升响应速度
|
||||
- Redis Hash 存储大量设备数据,内存占用较高,需监控内存使用情况
|
||||
- 定时任务间隔可根据数据量调整,平衡实时性和系统负载
|
||||
- 分布式锁重试机制平衡性能和一致性
|
||||
- Lua原子操作确保数据一致性,避免竞态条件
|
||||
- 分享码导入队列支持幂等性,避免重复导入
|
||||
- **阻抗数据去重**:**新增** 通过标准化字段和唯一索引实现智能去重,确保每条记录反映最新阻抗值
|
||||
|
||||
**章节来源**
|
||||
- [internal/database/mysql.go:33-43](file://internal/database/mysql.go#L33-L43)
|
||||
@@ -1152,6 +1348,7 @@ MD_CACHE --> REDIS_CLIENT
|
||||
- [internal/repository/brand.go:23-52](file://internal/repository/brand.go#L23-L52)
|
||||
- [internal/repository/model.go:23-79](file://internal/repository/model.go#L23-L79)
|
||||
- [internal/repository/device.go:19-87](file://internal/repository/device.go#L19-L87)
|
||||
- [internal/repository/headphone_impedance.go:19-99](file://internal/repository/headphone_impedance.go#L19-L99)
|
||||
- [internal/repository/ota.go:19-158](file://internal/repository/ota.go#L19-L158)
|
||||
- [internal/repository/curve.go:19-65](file://internal/repository/curve.go#L19-L65)
|
||||
- [internal/repository/share_code.go:20-58](file://internal/repository/share_code.go#L20-L58)
|
||||
@@ -1167,6 +1364,9 @@ MD_CACHE --> REDIS_CLIENT
|
||||
- **设备持久化失败**
|
||||
- 现象:设备信息上报成功但数据库中无数据。
|
||||
- 排查:检查设备持久化任务日志,确认Redis连接正常;验证user_device和user_active表结构;检查JSON数据格式;确认定时任务正常运行。
|
||||
- **阻抗持久化失败**:**新增**
|
||||
- 现象:阻抗上报成功但数据库中无数据。
|
||||
- 排查:检查阻抗持久化任务是否启用(ENABLE_IMPEDANCE_PERSIST_TASK=true);查看任务日志确认Redis连接正常;验证user_headphone_impedance表结构;检查JSON数据格式;确认定时任务正常运行;验证标准化字段是否正确处理。
|
||||
- **OTA查询错误**
|
||||
- 现象:OTA升级查询返回错误或结果不符合预期。
|
||||
- 排查:检查OTA表数据完整性,确认status=1且有效时间范围内;验证黑名单和定向设备表数据;检查参数传递(model、hw、beta、mac);**注意**:OTA查询已移除pawVerCode、pawVerName、pawUrl、pawMd5字段,确保查询逻辑正确。
|
||||
@@ -1181,7 +1381,7 @@ MD_CACHE --> REDIS_CLIENT
|
||||
- 排查:查看日志中错误上下文(cache read/write/brand/model/curve/share),定位具体环节;检查 Redis 连接状态;确认缓存数据格式正确。
|
||||
- **查询错误**
|
||||
- 现象:Handler 返回内部错误。
|
||||
- 排查:查看日志中错误上下文(query brand/query model/device/ota/curve/share/scan brand/scan model/iterate brand/iterate model),定位具体环节;检查 SQL 参数绑定与字段映射。
|
||||
- 排查:查看日志中错误上下文(query brand/query model/device/ota/curve/share),定位具体环节;检查 SQL 参数绑定与字段映射。
|
||||
- **结果为空**
|
||||
- 现象:Repository 在查询数据时返回空值。
|
||||
- 排查:确认传入的查询参数是否正确;检查表中是否存在匹配数据;验证MAC地址格式;确认缓存键是否正确。
|
||||
@@ -1200,6 +1400,9 @@ MD_CACHE --> REDIS_CLIENT
|
||||
- **分享码持久化问题**
|
||||
- 现象:分享码操作日志未写入数据库。
|
||||
- 排查:检查分享码持久化任务日志;确认分布式锁获取成功;验证数据库连接;检查HasExportLog查询;确认MarkPersisted执行。
|
||||
- **阻抗数据去重问题**:**新增**
|
||||
- 现象:相同耳机的阻抗数据重复插入或更新失败。
|
||||
- 排查:检查标准化字段(headphone_brand_norm, headphone_model_norm)是否正确处理;验证唯一索引约束;确认查询条件包含正确的标准化字段;检查数据格式转换。
|
||||
|
||||
**章节来源**
|
||||
- [internal/config/database.go:17-72](file://internal/config/database.go#L17-L72)
|
||||
@@ -1207,6 +1410,7 @@ MD_CACHE --> REDIS_CLIENT
|
||||
- [internal/handler/brand.go:25-48](file://internal/handler/brand.go#L25-L48)
|
||||
- [internal/handler/model.go:25-49](file://internal/handler/model.go#L25-L49)
|
||||
- [internal/handler/device.go:27-82](file://internal/handler/device.go#L27-L82)
|
||||
- [internal/handler/impedance.go:43-107](file://internal/handler/impedance.go#L43-L107)
|
||||
- [internal/handler/ota.go:23-132](file://internal/handler/ota.go#L23-L132)
|
||||
- [internal/handler/curve.go:56-127](file://internal/handler/curve.go#L56-L127)
|
||||
- [internal/handler/curve.go:214-292](file://internal/handler/curve.go#L214-L292)
|
||||
@@ -1214,14 +1418,15 @@ MD_CACHE --> REDIS_CLIENT
|
||||
- [internal/repository/brand.go:23-52](file://internal/repository/brand.go#L23-L52)
|
||||
- [internal/repository/model.go:23-79](file://internal/repository/model.go#L23-L79)
|
||||
- [internal/repository/device.go:19-87](file://internal/repository/device.go#L19-L87)
|
||||
- [internal/repository/headphone_impedance.go:19-99](file://internal/repository/headphone_impedance.go#L19-L99)
|
||||
- [internal/repository/ota.go:19-158](file://internal/repository/ota.go#L19-L158)
|
||||
- [internal/repository/curve.go:19-65](file://internal/repository/curve.go#L19-L65)
|
||||
- [internal/repository/share_code.go:20-58](file://internal/repository/share_code.go#L20-L58)
|
||||
|
||||
## 结论
|
||||
Luxsin 的数据访问层经过重构后,采用了更加完善的缓存感知查询逻辑,实现了多级缓存策略和智能数据刷新机制。新增的设备持久化仓库、OTA仓库、曲线仓库和分享码仓库进一步完善了数据访问层的功能,支持设备信息上报、固件升级查询、频响曲线计算和分享码管理等核心业务场景。新增的CurveCache和ShareCodeCache提供了更高级的缓存策略,包括fr数据分离存储、分布式锁机制、Lua原子操作等,显著提升了系统的响应性能和数据一致性。通过集成 Redis 缓存层和异步持久化任务,系统在高并发场景下表现出色。
|
||||
Luxsin 的数据访问层经过重构后,采用了更加完善的缓存感知查询逻辑,实现了多级缓存策略和智能数据刷新机制。新增的设备持久化仓库、**阻抗持久化仓库**、OTA仓库、曲线仓库和分享码仓库进一步完善了数据访问层的功能,支持设备信息上报、**耳机阻抗数据采集**、固件升级查询、频响曲线计算和分享码管理等核心业务场景。新增的CurveCache和ShareCodeCache提供了更高级的缓存策略,包括fr数据分离存储、分布式锁机制、Lua原子操作等,显著提升了系统的响应性能和数据一致性。通过集成 Redis 缓存层和异步持久化任务,系统在高并发场景下表现出色。
|
||||
|
||||
**重要更新**:OTA仓库层已成功移除了pawVerCode、pawVerName、pawUrl、pawMd5等冗余字段,简化了查询逻辑和数据处理流程,提升了系统性能和维护性。OTA模型结构已精简,移除了不必要的字段,提高了数据传输效率。
|
||||
**重要更新**:OTA仓库层已成功移除了pawVerCode、pawVerName、pawUrl、pawMd5等冗余字段,简化了查询逻辑和数据处理流程,提升了系统性能和维护性。**新增了完整的耳机阻抗数据管理功能**,包括HeadphoneImpedanceRepository、ImpedanceHandler、ImpedancePersistTask等组件,实现了按MAC地址和标准化耳机标识符的智能去重和数据更新机制。
|
||||
|
||||
建议后续继续优化缓存策略,增加缓存监控指标,完善缓存失效和更新机制,并持续改进监控与日志体系。
|
||||
|
||||
@@ -1235,6 +1440,8 @@ Luxsin 的数据访问层经过重构后,采用了更加完善的缓存感知
|
||||
- 考虑异步处理场景,设计幂等的持久化逻辑。
|
||||
- 支持分布式锁机制,防止缓存击穿和竞态条件。
|
||||
- 使用Lua脚本确保数据一致性,避免竞态条件。
|
||||
- **对于高频写入场景**:**新增** 考虑采用异步持久化模式,先写入Redis再批量同步到数据库。
|
||||
- **对于需要去重的数据**:**新增** 设计合理的标准化字段和唯一索引,确保数据一致性。
|
||||
- **实现步骤**
|
||||
- 定义领域模型(Model)与 Repository 接口/实现。
|
||||
- 创建对应的缓存结构体,定义缓存键和TTL策略。
|
||||
@@ -1244,6 +1451,8 @@ Luxsin 的数据访问层经过重构后,采用了更加完善的缓存感知
|
||||
- 实现缓存预热逻辑,确保应用启动时缓存可用。
|
||||
- 如涉及异步处理,实现独立的任务调度器。
|
||||
- 配置分享码TTL等配置项,支持动态参数。
|
||||
- **对于异步持久化场景**:**新增** 实现环境变量控制开关,支持任务的启用和禁用。
|
||||
- **对于去重逻辑**:**新增** 实现标准化处理函数,确保数据一致性。
|
||||
- **注意事项**
|
||||
- 必须在每个查询后 defer rows.Close()。
|
||||
- 使用 fmt.Errorf 包裹底层错误,保留调用栈信息。
|
||||
@@ -1259,11 +1468,15 @@ Luxsin 的数据访问层经过重构后,采用了更加完善的缓存感知
|
||||
- Lua脚本需确保原子性和错误处理。
|
||||
- 分享码TTL配置需支持多种时间单位格式。
|
||||
- 分享码长度必须为5位字符,使用字母数字组合(排除易混淆字符)。
|
||||
- **对于异步持久化任务**:**新增** 确保任务能够优雅停止,避免数据丢失。
|
||||
- **对于去重逻辑**:**新增** 确保标准化处理函数正确处理各种边界情况。
|
||||
- **对于高频写入场景**:**新增** 监控Redis内存使用情况,避免内存溢出。
|
||||
|
||||
**章节来源**
|
||||
- [internal/repository/brand.go:14-21](file://internal/repository/brand.go#L14-L21)
|
||||
- [internal/repository/model.go:14-21](file://internal/repository/model.go#L14-L21)
|
||||
- [internal/repository/device.go:11-17](file://internal/repository/device.go#L11-L17)
|
||||
- [internal/repository/headphone_impedance.go:15-17](file://internal/repository/headphone_impedance.go#L15-L17)
|
||||
- [internal/repository/ota.go:11-17](file://internal/repository/ota.go#L11-L17)
|
||||
- [internal/repository/curve.go:11-17](file://internal/repository/curve.go#L11-L17)
|
||||
- [internal/repository/share_code.go:12-18](file://internal/repository/share_code.go#L12-L18)
|
||||
@@ -1274,6 +1487,7 @@ Luxsin 的数据访问层经过重构后,采用了更加完善的缓存感知
|
||||
- [internal/handler/brand.go:19-24](file://internal/handler/brand.go#L19-L24)
|
||||
- [internal/handler/model.go:19-24](file://internal/handler/model.go#L19-L24)
|
||||
- [internal/handler/device.go:15-25](file://internal/handler/device.go#L15-L25)
|
||||
- [internal/handler/impedance.go:23-28](file://internal/handler/impedance.go#L23-L28)
|
||||
- [internal/handler/ota.go:14-21](file://internal/handler/ota.go#L14-L21)
|
||||
- [internal/handler/curve.go:27-39](file://internal/handler/curve.go#L27-L39)
|
||||
- [internal/handler/share_code.go:15-23](file://internal/handler/share_code.go#L15-L23)
|
||||
|
||||
Reference in New Issue
Block a user