Files
eafonyang 27ee791891 refactor(config): 重构配置系统实现环境变量驱动
- 移除所有硬编码环境特定值,实现配置完全环境变量化
- 强化配置加载与校验,确保生产环境变量完整有效
- 更新文档说明,突出环境变量配置最佳实践
- 支持通过容器环境变量或配置中心注入敏感配置
- 统一环境差异管理,避免硬编码导致的环境耦合
- 配置管理集成至应用启动流程,提升配置灵活性与安全性
2026-07-15 16:41:09 +08:00

860 lines
48 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 数据库设计
<cite>
**本文引用的文件**
- [internal/model/brand.go](file://internal/model/brand.go)
- [internal/model/model.go](file://internal/model/model.go)
- [sql/model.sql](file://sql/model.sql)
- [internal/repository/brand.go](file://internal/repository/brand.go)
- [internal/repository/model.go](file://internal/repository/model.go)
- [internal/handler/brand.go](file://internal/handler/brand.go)
- [internal/handler/model.go](file://internal/handler/model.go)
- [internal/config/config.go](file://internal/config/config.go)
- [internal/config/database.go](file://internal/config/database.go)
- [internal/config/meilisearch.go](file://internal/config/meilisearch.go)
- [internal/config/redis.go](file://internal/config/redis.go)
- [internal/database/mysql.go](file://internal/database/mysql.go)
- [internal/cache/redis.go](file://internal/cache/redis.go)
- [internal/search/meilisearch.go](file://internal/search/meilisearch.go)
- [cmd/server/main.go](file://cmd/server/main.go)
- [sql/user_device.sql](file://sql/user_device.sql)
- [sql/user_active.sql](file://sql/user_active.sql)
- [sql/ota.sql](file://sql/ota.sql)
- [sql/black_list.sql](file://sql/black_list.sql)
- [sql/ota_target_device.sql](file://sql/ota_target_device.sql)
- [internal/model/user_device.go](file://internal/model/user_device.go)
- [internal/model/user_active.go](file://internal/model/user_active.go)
- [internal/model/ota.go](file://internal/model/ota.go)
- [internal/repository/device.go](file://internal/repository/device.go)
- [internal/repository/ota.go](file://internal/repository/ota.go)
- [internal/handler/device.go](file://internal/handler/device.go)
- [internal/handler/ota.go](file://internal/handler/ota.go)
- [internal/task/device_persist.go](file://internal/task/device_persist.go)
- [sql/user_headphone_impedance.sql](file://sql/user_headphone_impedance.sql)
- [internal/model/user_headphone_impedance.go](file://internal/model/user_headphone_impedance.go)
- [internal/handler/impedance.go](file://internal/handler/impedance.go)
- [internal/repository/headphone_impedance.go](file://internal/repository/headphone_impedance.go)
- [internal/task/impedance_persist.go](file://internal/task/impedance_persist.go)
- [internal/router/router.go](file://internal/router/router.go)
</cite>
## 更新摘要
**所做更改**
- 确认 user_headphone_impedance 表结构与实现完全同步
- 验证阻抗持久化子系统的完整集成状态
- 更新相关章节以反映最新的实现细节
- 增强阻抗数据处理的详细说明
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)
## 简介
本文件面向 Luxsin 应用 API 的数据库与数据模型,系统化梳理实体关系、字段定义、索引与约束、数据访问模式、缓存与搜索集成、性能优化、数据生命周期与迁移路径,并给出品牌(Brand)与型号(Model)实体的设计理念与业务逻辑说明。文档同时覆盖数据库连接配置、查询优化建议、数据安全与隐私要求以及访问控制要点。
**更新** 本次更新确认了用户耳机阻抗功能模块的完整实现,包括数据库表设计、API接口、Redis缓存机制和定时持久化任务。该功能通过唯一复合索引确保数据完整性,使用Redis缓存提高写入性能,并通过批量处理优化数据库写入效率。
## 项目结构
本项目采用分层架构:入口程序负责初始化配置、数据库、搜索引擎与缓存;路由层组织 HTTP 接口;处理器层封装业务接口;仓库层实现数据访问;模型层承载数据结构;搜索与缓存模块作为外部依赖集成。
```mermaid
graph TB
subgraph "应用入口"
MAIN["cmd/server/main.go"]
end
subgraph "配置层"
CFG["internal/config/config.go"]
DB_CFG["internal/config/database.go"]
MS_CFG["internal/config/meilisearch.go"]
RD_CFG["internal/config/redis.go"]
end
subgraph "基础设施"
MYSQL["internal/database/mysql.go"]
REDIS["internal/cache/redis.go"]
MEILI["internal/search/meilisearch.go"]
end
subgraph "业务层"
ROUTER["路由(未在本文展开)"]
HANDLER_B["internal/handler/brand.go"]
HANDLER_M["internal/handler/model.go"]
HANDLER_D["internal/handler/device.go"]
HANDLER_I["internal/handler/impedance.go"]
HANDLER_O["internal/handler/ota.go"]
REPO_B["internal/repository/brand.go"]
REPO_M["internal/repository/model.go"]
REPO_D["internal/repository/device.go"]
REPO_I["internal/repository/headphone_impedance.go"]
REPO_O["internal/repository/ota.go"]
MODEL_B["internal/model/brand.go"]
MODEL_M["internal/model/model.go"]
MODEL_UD["internal/model/user_device.go"]
MODEL_UA["internal/model/user_active.go"]
MODEL_OTA["internal/model/ota.go"]
MODEL_I["internal/model/user_headphone_impedance.go"]
TASK_DP["internal/task/device_persist.go"]
TASK_IP["internal/task/impedance_persist.go"]
end
MAIN --> CFG
CFG --> DB_CFG
CFG --> MS_CFG
CFG --> RD_CFG
MAIN --> MYSQL
MAIN --> REDIS
MAIN --> MEILI
ROUTER --> HANDLER_B
ROUTER --> HANDLER_M
ROUTER --> HANDLER_D
ROUTER --> HANDLER_I
ROUTER --> HANDLER_O
HANDLER_B --> REPO_B
HANDLER_M --> REPO_M
HANDLER_D --> REPO_D
HANDLER_I --> REPO_I
HANDLER_O --> REPO_O
REPO_B --> MODEL_B
REPO_M --> MODEL_M
REPO_D --> MODEL_UD
REPO_D --> MODEL_UA
REPO_I --> MODEL_I
REPO_O --> MODEL_OTA
TASK_DP --> REPO_D
TASK_IP --> REPO_I
```
**图表来源**
- [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)
- [internal/config/database.go:1-72](file://internal/config/database.go#L1-L72)
- [internal/config/meilisearch.go:1-51](file://internal/config/meilisearch.go#L1-L51)
- [internal/config/redis.go:1-57](file://internal/config/redis.go#L1-L57)
- [internal/database/mysql.go:1-47](file://internal/database/mysql.go#L1-L47)
- [internal/cache/redis.go:1-17](file://internal/cache/redis.go#L1-L17)
- [internal/search/meilisearch.go:1-46](file://internal/search/meilisearch.go#L1-L46)
- [internal/handler/brand.go:1-50](file://internal/handler/brand.go#L1-L50)
- [internal/handler/model.go:1-51](file://internal/handler/model.go#L1-L51)
- [internal/handler/device.go:1-83](file://internal/handler/device.go#L1-L83)
- [internal/handler/impedance.go:1-116](file://internal/handler/impedance.go#L1-L116)
- [internal/handler/ota.go:1-145](file://internal/handler/ota.go#L1-L145)
- [internal/repository/brand.go:1-51](file://internal/repository/brand.go#L1-L51)
- [internal/repository/model.go:1-95](file://internal/repository/model.go#L1-L95)
- [internal/repository/device.go:1-88](file://internal/repository/device.go#L1-L88)
- [internal/repository/headphone_impedance.go:1-100](file://internal/repository/headphone_impedance.go#L1-L100)
- [internal/repository/ota.go:1-159](file://internal/repository/ota.go#L1-L159)
- [internal/model/brand.go:1-7](file://internal/model/brand.go#L1-L7)
- [internal/model/model.go:1-15](file://internal/model/model.go#L1-L15)
- [internal/model/user_device.go:1-12](file://internal/model/user_device.go#L1-L12)
- [internal/model/user_active.go:1-13](file://internal/model/user_active.go#L1-L13)
- [internal/model/ota.go:1-63](file://internal/model/ota.go#L1-L63)
- [internal/model/user_headphone_impedance.go:1-18](file://internal/model/user_headphone_impedance.go#L1-L18)
- [internal/task/device_persist.go:1-167](file://internal/task/device_persist.go#L1-L167)
- [internal/task/impedance_persist.go:1-163](file://internal/task/impedance_persist.go#L1-L163)
**章节来源**
- [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)
## 核心组件
- 数据库表 model:存储耳机型号信息,包含唯一组合索引以保证品牌+型号的唯一性。
- 设备持久化表:user_device(设备信息持久化)、user_active(设备活跃度统计)
- OTA 升级表:ota(固件升级信息)、black_list(黑名单)、ota_target_device(定向设备)
- 用户耳机阻抗表:user_headphone_impedance(用户上报的耳机阻抗最新值)
- 模型对象 Brand 与 Model:分别映射品牌与型号的 JSON 字段与数据库列。
- 阻抗模型对象 UserHeadphoneImpedance:映射用户耳机阻抗数据的JSON字段与数据库列。
- 仓库层:提供按品牌名或型号名检索的查询方法,支持模糊匹配与精确匹配。
- 阻抗仓库层:提供按MAC地址和归一化品牌/型号查询、插入和更新操作。
- 处理器层:暴露 HTTP 接口,支持返回 JSON 或 Base64 编码响应。
- 阻抗处理器:处理耳机阻抗上报请求,参数校验和数据缓存。
- 配置层:集中加载数据库、搜索引擎与缓存的连接参数,并进行基本校验。
- 基础设施:MySQL 连接池配置、Redis 客户端、Meilisearch 搜索客户端。
- 定时任务:设备持久化任务和阻抗持久化任务,定期从Redis同步数据到数据库。
**更新** 确认了完整的用户耳机阻抗功能模块实现,包括数据库表设计、API接口、缓存机制和定时持久化任务的完整集成。
**章节来源**
- [sql/model.sql:20-38](file://sql/model.sql#L20-L38)
- [sql/user_device.sql:20-35](file://sql/user_device.sql#L20-L35)
- [sql/user_active.sql:20-208](file://sql/user_active.sql#L20-L208)
- [sql/ota.sql:20-40](file://sql/ota.sql#L20-L40)
- [sql/black_list.sql:20-30](file://sql/black_list.sql#L20-30)
- [sql/ota_target_device.sql:20-31](file://sql/ota_target_device.sql#L20-L31)
- [sql/user_headphone_impedance.sql:20-36](file://sql/user_headphone_impedance.sql#L20-L36)
- [internal/model/brand.go:1-7](file://internal/model/brand.go#L1-L7)
- [internal/model/model.go:1-15](file://internal/model/model.go#L1-L15)
- [internal/model/user_device.go:1-12](file://internal/model/user_device.go#L1-L12)
- [internal/model/user_active.go:1-13](file://internal/model/user_active.go#L1-L13)
- [internal/model/ota.go:1-63](file://internal/model/ota.go#L1-L63)
- [internal/model/user_headphone_impedance.go:1-18](file://internal/model/user_headphone_impedance.go#L1-L18)
- [internal/repository/brand.go:1-51](file://internal/repository/brand.go#L1-L51)
- [internal/repository/model.go:1-95](file://internal/repository/model.go#L1-L95)
- [internal/repository/device.go:1-88](file://internal/repository/device.go#L1-L88)
- [internal/repository/headphone_impedance.go:1-100](file://internal/repository/headphone_impedance.go#L1-L100)
- [internal/repository/ota.go:1-159](file://internal/repository/ota.go#L1-L159)
- [internal/handler/brand.go:1-50](file://internal/handler/brand.go#L1-L50)
- [internal/handler/model.go:1-51](file://internal/handler/model.go#L1-L51)
- [internal/handler/device.go:1-83](file://internal/handler/device.go#L1-L83)
- [internal/handler/impedance.go:1-116](file://internal/handler/impedance.go#L1-L116)
- [internal/handler/ota.go:1-145](file://internal/handler/ota.go#L1-L145)
- [internal/config/database.go:1-72](file://internal/config/database.go#L1-L72)
- [internal/config/meilisearch.go:1-51](file://internal/config/meilisearch.go#L1-L51)
- [internal/config/redis.go:1-57](file://internal/config/redis.go#L1-L57)
- [internal/database/mysql.go:1-47](file://internal/database/mysql.go#L1-L47)
- [internal/cache/redis.go:1-17](file://internal/cache/redis.go#L1-L17)
- [internal/search/meilisearch.go:1-46](file://internal/search/meilisearch.go#L1-L46)
## 架构总览
下图展示从 HTTP 请求到数据库与搜索引擎的调用链路,以及缓存的使用位置。新增了设备上报、阻抗上报和 OTA 升级的完整处理流程。
```mermaid
sequenceDiagram
participant C as "客户端"
participant H as "处理器(品牌/型号/设备/阻抗/OTA)"
participant R as "仓库(品牌/型号/设备/阻抗/OTA)"
participant DB as "MySQL 数据库"
participant S as "Meilisearch"
participant RC as "Redis"
participant T as "定时任务"
C->>H : "HTTP GET /brands,/models,/device/report,/audio/reportImpedance,/audio/ota"
H->>R : "List(过滤条件)"
alt "数据库查询"
R->>DB : "执行 SQL 查询"
DB-->>R : "结果集"
else "搜索引擎查询"
H->>S : "SearchWithContext(key, attributes)"
S-->>H : "命中结果"
end
H-->>C : "JSON 或 Base64 响应"
note over H,RC : "可选:对热点数据进行缓存读写<br/>设备上报:Redis 缓存 + 定时持久化<br/>阻抗上报:Redis Hash 缓存 + 定时持久化"
T->>RC : "读取缓存数据"
T->>R : "批量写入数据库"
R->>DB : "INSERT/UPDATE 操作"
DB-->>R : "确认写入成功"
R-->>T : "删除已处理的缓存数据"
```
**图表来源**
- [internal/handler/brand.go:26-49](file://internal/handler/brand.go#L26-L49)
- [internal/handler/model.go:26-50](file://internal/handler/model.go#L26-L50)
- [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-144](file://internal/handler/ota.go#L23-L144)
- [internal/repository/brand.go:20-50](file://internal/repository/brand.go#L20-L50)
- [internal/repository/model.go:20-61](file://internal/repository/model.go#L20-L61)
- [internal/repository/device.go:19-87](file://internal/repository/device.go#L19-L87)
- [internal/repository/headphone_impedance.go:19-100](file://internal/repository/headphone_impedance.go#L19-L100)
- [internal/repository/ota.go:19-158](file://internal/repository/ota.go#L19-L158)
- [internal/search/meilisearch.go:22-45](file://internal/search/meilisearch.go#L22-L45)
- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16)
- [internal/task/device_persist.go:44-108](file://internal/task/device_persist.go#L44-L108)
- [internal/task/impedance_persist.go:44-108](file://internal/task/impedance_persist.go#L44-L108)
## 详细组件分析
### 数据模型与实体关系
- 实体:Brand(品牌)、Model(型号)、UserDevice(设备)、UserActive(活跃度)、OTA(固件升级)、BlackList(黑名单)、OTATargetDevice(定向设备)、UserHeadphoneImpedance(用户耳机阻抗)
- 关系:
- Model 通过字段 brand_name 引导与品牌的关系
- UserDevice 与 UserActive 通过 mac_addr 关联
- OTA 与 BlackList 通过 ota_id 关联
- OTA 与 OTATargetDevice 通过 ota_id 关联
- UserHeadphoneImpedance 通过 mac_addr 与用户设备关联
- 字段与类型:
- Brand:id(整数,主键)、name(字符串)
- Modelid(整数,主键)、brand_name(字符串,非空)、name(字符串,非空)、form(字符串,可空)、rig(字符串,可空)、source(字符串,可空)、eq_key(字符串,可空)、create_at(时间戳,默认当前时间)
- UserDeviceid(整数,主键)、mac_addr(字符串,唯一)、model(字符串)、add_time(时间戳,默认当前时间)、ver(字符串,可空)
- UserActiveid(整数,主键)、mac_addr(字符串)、model(字符串)、active_date(日期,主键部分)、ip_addr(字符串)、create_at(时间戳,默认当前时间)
- OTAid(整数,主键)、verCode(整数)、verName(字符串)、url(字符串)、md5(字符串)、force(整数,默认0)、desc(字符串,可空)、model(字符串,可空)、hw(整数,默认0)、target(整数,默认0)、beta(整数,默认0)、startTime(时间戳,可空)、endTime(时间戳,可空)、status(整数,默认1)
- BlackListid(整数,主键)、ota_id(整数)、mac(字符串,默认空)、create_at(时间戳,默认当前时间)
- OTATargetDeviceid(整数,主键)、ota_id(整数)、mac_addr(字符串)、create_at(时间戳,默认当前时间)
- UserHeadphoneImpedanceid(整数,主键)、mac_addr(字符串,非空)、device_model(字符串,非空)、impedance_ohm(整数,非空)、headphone_brand(字符串,非空)、headphone_model(字符串,非空)、headphone_brand_norm(字符串,非空)、headphone_model_norm(字符串,非空)、ip_addr(字符串,非空)、create_at(时间戳,默认当前时间)、update_at(时间戳,默认当前时间)
```mermaid
erDiagram
BRAND {
int id PK
string name
}
MODEL {
int id PK
string brand_name
string name
string form
string rig
string source
string eq_key
datetime create_at
}
USER_DEVICE {
int id PK
string mac_addr UK
string model
datetime add_time
string ver
}
USER_ACTIVE {
int id PK
string mac_addr
string model
date active_date PK
string ip_addr
datetime create_at
}
OTA {
int id PK
int verCode
string verName
string url
string md5
int force
string desc
string model
int hw
int target
int beta
datetime startTime
datetime endTime
int status
}
BLACK_LIST {
int id PK
int ota_id
string mac
datetime create_at
}
OTA_TARGET_DEVICE {
int id PK
int ota_id
string mac_addr
datetime create_at
}
USER_HEADPHONE_IMPEDANCE {
int id PK
string mac_addr
string device_model
int impedance_ohm
string headphone_brand
string headphone_model
string headphone_brand_norm
string headphone_model_norm
string ip_addr
datetime create_at
datetime update_at
}
BRAND ||--o{ MODEL : "拥有多个型号"
USER_DEVICE ||--o{ USER_ACTIVE : "每日活跃记录"
OTA ||--o{ BLACK_LIST : "黑名单关联"
OTA ||--o{ OTA_TARGET_DEVICE : "定向设备关联"
USER_DEVICE ||--o{ USER_HEADPHONE_IMPEDANCE : "用户设备阻抗记录"
```
**图表来源**
- [internal/model/brand.go:3-6](file://internal/model/brand.go#L3-L6)
- [internal/model/model.go:5-14](file://internal/model/model.go#L5-L14)
- [internal/model/user_device.go:5-11](file://internal/model/user_device.go#L5-L11)
- [internal/model/user_active.go:5-12](file://internal/model/user_active.go#L5-L12)
- [internal/model/ota.go:30-45](file://internal/model/ota.go#L30-L45)
- [internal/model/ota.go:47-62](file://internal/model/ota.go#L47-L62)
- [internal/model/user_headphone_impedance.go:5-17](file://internal/model/user_headphone_impedance.go#L5-L17)
- [sql/model.sql:24-35](file://sql/model.sql#L24-L35)
- [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/ota.sql:24-40](file://sql/ota.sql#L24-L40)
- [sql/black_list.sql:24-30](file://sql/black_list.sql#L24-L30)
- [sql/ota_target_device.sql:24-31](file://sql/ota_target_device.sql#L24-L31)
- [sql/user_headphone_impedance.sql:20-36](file://sql/user_headphone_impedance.sql#L20-L36)
**章节来源**
- [internal/model/brand.go:1-7](file://internal/model/brand.go#L1-L7)
- [internal/model/model.go:1-15](file://internal/model/model.go#L1-L15)
- [internal/model/user_device.go:1-12](file://internal/model/user_device.go#L1-L12)
- [internal/model/user_active.go:1-13](file://internal/model/user_active.go#L1-L13)
- [internal/model/ota.go:1-63](file://internal/model/ota.go#L1-L63)
- [internal/model/user_headphone_impedance.go:1-18](file://internal/model/user_headphone_impedance.go#L1-L18)
- [sql/model.sql:20-38](file://sql/model.sql#L20-L38)
- [sql/user_device.sql:20-35](file://sql/user_device.sql#L20-L35)
- [sql/user_active.sql:20-208](file://sql/user_active.sql#L20-L208)
- [sql/ota.sql:20-40](file://sql/ota.sql#L20-L40)
- [sql/black_list.sql:20-30](file://sql/black_list.sql#L20-L30)
- [sql/ota_target_device.sql:20-31](file://sql/ota_target_device.sql#L20-L31)
- [sql/user_headphone_impedance.sql:20-36](file://sql/user_headphone_impedance.sql#L20-L36)
### 数据库表结构与约束
- 表名:model、user_device、user_active、ota、black_list、ota_target_device、user_headphone_impedance
- 主键:各表的 id 字段(自增整数)
- 唯一索引:
- model(brand_name, name)
- user_device(mac_addr)
- user_active(mac_addr, active_date)
- user_headphone_impedance(mac_addr, headphone_brand_norm, headphone_model_norm)
- 普通索引:
- user_headphone_impedanceidx_mac(mac_addr)、idx_device_model(device_model)
- 默认值:各表的 create_at 字段默认当前时间,user_headphone_impedance 表的 update_at 字段也默认当前时间
- 分区表:user_active 表按 active_date 进行范围分区,包含 170 个分区
- 存储引擎与字符集:InnoDB、utf8mb4、排序规则 0900_ai_ci
**更新** 确认了用户耳机阻抗表的完整设计,包含唯一复合索引确保数据完整性,并提供辅助索引优化查询性能。
**章节来源**
- [sql/model.sql:20-38](file://sql/model.sql#L20-L38)
- [sql/user_device.sql:20-35](file://sql/user_device.sql#L20-L35)
- [sql/user_active.sql:20-208](file://sql/user_active.sql#L20-L208)
- [sql/ota.sql:20-40](file://sql/ota.sql#L20-L40)
- [sql/black_list.sql:20-30](file://sql/black_list.sql#L20-L30)
- [sql/ota_target_device.sql:20-31](file://sql/ota_target_device.sql#L20-L31)
- [sql/user_headphone_impedance.sql:20-36](file://sql/user_headphone_impedance.sql#L20-L36)
### 数据访问模式
- 品牌列表:
- 支持按品牌名模糊查询(LIKE %brandName%),无匹配时返回空数组
- 结果按名称升序排列
- 型号列表:
- 支持按品牌名精确匹配或按型号名模糊匹配,二者二选一
- 无匹配条件时返回空数组
- 结果按名称升序排列
- 设备持久化:
- 设备上报:通过 Redis Hash 缓存设备信息
- 定时持久化:每间隔时间从 Redis 读取并批量写入数据库
- 支持设备首次注册和版本更新
- 阻抗持久化:
- 阻抗上报:通过 Redis Hash 缓存阻抗信息,键格式为"mac|brand_norm|model_norm"
- 定时持久化:每5分钟从 Redis 读取并批量写入数据库
- 支持阻抗值更新和设备信息覆盖更新
- OTA 升级:
- 支持按型号、硬件版本、灰度标识查询最新 OTA 版本
- 支持黑名单检查和定向设备检查
- 自动处理灰度发布和强制升级逻辑
```mermaid
flowchart TD
Start(["进入仓库方法"]) --> CheckBrand["检查 brandName 是否为空"]
CheckBrand --> CheckModel["检查 modelName 是否为空"]
CheckModel --> Branch{"分支选择"}
Branch --> |brandName 非空| Q1["按品牌名精确匹配"]
Branch --> |modelName 非空| Q2["按型号名模糊匹配"]
Branch --> |均为空| Empty["返回空数组"]
Q1 --> Exec["执行查询并扫描结果"]
Q2 --> Exec
Empty --> End(["结束"])
subgraph "设备持久化流程"
Report["设备上报"] --> Cache["Redis 缓存"]
Cache --> Persist["定时持久化"]
Persist --> DBWrite["写入 user_device"]
DBWrite --> Active["写入 user_active"]
end
subgraph "阻抗持久化流程"
ImpReport["阻抗上报"] --> ImpCache["Redis Hash 缓存"]
ImpCache --> ImpPersist["定时持久化"]
ImpPersist --> ImpCheck["检查是否存在记录"]
ImpCheck --> |不存在| ImpInsert["INSERT 新记录"]
ImpCheck --> |存在| ImpUpdate["UPDATE 更新记录"]
ImpInsert --> ImpDone["完成"]
ImpUpdate --> ImpDone
end
```
**图表来源**
- [internal/repository/brand.go:20-50](file://internal/repository/brand.go#L20-L50)
- [internal/repository/model.go:20-61](file://internal/repository/model.go#L20-L61)
- [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/task/device_persist.go:37-91](file://internal/task/device_persist.go#L37-L91)
- [internal/task/impedance_persist.go:44-108](file://internal/task/impedance_persist.go#L44-L108)
- [internal/repository/device.go:19-87](file://internal/repository/device.go#L19-L87)
- [internal/repository/headphone_impedance.go:19-100](file://internal/repository/headphone_impedance.go#L19-L100)
**章节来源**
- [internal/repository/brand.go:1-51](file://internal/repository/brand.go#L1-L51)
- [internal/repository/model.go:1-95](file://internal/repository/model.go#L1-L95)
- [internal/handler/device.go:1-83](file://internal/handler/device.go#L1-L83)
- [internal/handler/impedance.go:1-116](file://internal/handler/impedance.go#L1-L116)
- [internal/task/device_persist.go:1-167](file://internal/task/device_persist.go#L1-L167)
- [internal/task/impedance_persist.go:1-163](file://internal/task/impedance_persist.go#L1-L163)
- [internal/repository/device.go:1-88](file://internal/repository/device.go#L1-L88)
- [internal/repository/headphone_impedance.go:1-100](file://internal/repository/headphone_impedance.go#L1-L100)
### 处理器与接口行为
- 品牌接口:接收 query 参数 brandName,支持返回 JSON 或 Base64 编码响应
- 型号接口:接收 query 参数 brandName 与 modelName,支持返回 JSON 或 Base64 编码响应
- 设备上报接口:接收 mac、model、ver 参数,将设备信息缓存到 Redis
- 阻抗上报接口:
- 接收参数:mac(设备MAC地址)、name(设备型号)、brand(耳机品牌)、model(耳机型号)、value(阻抗值)
- 参数校验:所有参数必填,阻抗值必须为整数
- 数据处理:品牌/型号进行归一化处理(trim+lower)
- 缓存策略:使用Redis Hash存储,键格式为"mac|brand_norm|model_norm"
- OTA 升级接口:接收 model、hw、mac、beta 参数,返回合适的 OTA 升级信息
- 错误处理:内部错误统一返回 500 并记录日志
**更新** 确认了阻抗上报接口的完整实现,支持设备上报耳机阻抗值,具备完善的参数校验和数据处理逻辑。
**章节来源**
- [internal/handler/brand.go:1-50](file://internal/handler/brand.go#L1-L50)
- [internal/handler/model.go:1-51](file://internal/handler/model.go#L1-L51)
- [internal/handler/device.go:1-83](file://internal/handler/device.go#L1-L83)
- [internal/handler/impedance.go:1-116](file://internal/handler/impedance.go#L1-L116)
- [internal/handler/ota.go:1-145](file://internal/handler/ota.go#L1-L145)
### 搜索与缓存集成
- 搜索:Meilisearch 客户端按给定关键字检索,限定返回属性,支持空命中返回空数组
- 缓存:Redis 客户端初始化,用于设备信息缓存和定时持久化
- 设备缓存:使用 Redis Hash 存储设备上报信息,键为设备 MAC 地址
- 阻抗缓存:
- 使用 Redis Hash 存储阻抗上报信息,键为"headphone_impedances"
- 字段格式为"mac|brand_norm|model_norm",值为JSON格式的阻抗信息
- 持久化策略:定时任务批量处理 Redis 缓存数据,避免频繁数据库写入
**更新** 确认了阻抗数据的Redis缓存机制,采用Hash结构存储,支持高效的批量处理和去重更新。
**章节来源**
- [internal/search/meilisearch.go:1-46](file://internal/search/meilisearch.go#L1-L46)
- [internal/cache/redis.go:1-17](file://internal/cache/redis.go#L1-L17)
- [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/task/device_persist.go:37-91](file://internal/task/device_persist.go#L37-L91)
- [internal/task/impedance_persist.go:44-108](file://internal/task/impedance_persist.go#L44-L108)
### 数据库连接配置
- 加载顺序:环境变量优先于默认值;生产环境需提供密码
- 连接参数:用户、主机、端口、数据库名、字符集、时区、超时等
- 连接池:最大打开连接数、最大空闲连接数、连接最大生命周期
- Ping 校验:启动时进行可达性检测
**章节来源**
- [internal/config/database.go:17-72](file://internal/config/database.go#L17-L72)
- [internal/database/mysql.go:14-47](file://internal/database/mysql.go#L14-L47)
### 配置与环境变量
- 应用:APP_ENV、APP_HOST、APP_PORT
- 数据库:DATABASE_HOST、DATABASE_PORT、DATABASE_NAME、DATABASE_USER、DATABASE_PASSWORD
- 搜索引擎:MEILISEARCH_HOST、MEILISEARCH_API_KEY、MEILISEARCH_INDEX
- 缓存:REDIS_HOST、REDIS_PORT、REDIS_DATABASE、REDIS_PASSWORD
- 阻抗持久化任务:ENABLE_IMPEDANCE_PERSIST_TASK(默认false
**更新** 确认了阻抗持久化任务的配置开关,允许按需启用该功能。
**章节来源**
- [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52)
- [internal/config/config.go:68](file://internal/config/config.go#L68)
- [internal/config/database.go:42-55](file://internal/config/database.go#L42-L55)
- [internal/config/meilisearch.go:14-37](file://internal/config/meilisearch.go#L14-L37)
- [internal/config/redis.go:39-49](file://internal/config/redis.go#L39-L49)
### 设备持久化子系统
- 设备上报:客户端通过 /device/report 接口上报设备信息,包含 MAC 地址、设备型号、软件版本
- Redis 缓存:设备信息以 JSON 格式存储在 Redis Hash 中,键为设备 MAC 地址
- 定时持久化:后台任务每间隔时间从 Redis 读取设备数据,批量写入数据库
- 数据一致性:支持设备首次注册和版本更新,自动处理 IP 地址更新
**章节来源**
- [internal/handler/device.go:27-82](file://internal/handler/device.go#L27-L82)
- [internal/task/device_persist.go:24-167](file://internal/task/device_persist.go#L24-L167)
- [internal/repository/device.go:19-87](file://internal/repository/device.go#L19-L87)
### 阻抗持久化子系统
- 阻抗上报:客户端通过 /audio/reportImpedance 接口上报耳机阻抗信息,包含设备MAC、设备型号、耳机品牌、耳机型号、阻抗值
- Redis 缓存:阻抗信息以 JSON 格式存储在 Redis Hash 中,键为"headphone_impedances",字段格式为"mac|brand_norm|model_norm"
- 定时持久化:后台任务每5分钟从 Redis 读取阻抗数据,批量写入数据库
- 数据一致性:支持阻抗值更新和设备信息覆盖更新,通过唯一复合索引确保数据去重
- 数据归一化:品牌/型号在入库前进行trim+lower处理,确保大小写不敏感的去重
**更新** 确认了阻抗持久化子系统的完整实现,提供了完整的耳机阻抗数据管理能力,支持高并发上报和批量持久化。
**章节来源**
- [internal/handler/impedance.go:43-107](file://internal/handler/impedance.go#L43-L107)
- [internal/task/impedance_persist.go:31-163](file://internal/task/impedance_persist.go#L31-L163)
- [internal/repository/headphone_impedance.go:19-100](file://internal/repository/headphone_impedance.go#L19-L100)
### OTA 固件升级子系统
- 版本查询:支持按型号、硬件版本、灰度标识查询最新可用 OTA 版本
- 黑名单机制:支持将特定设备加入 OTA 升级黑名单
- 定向升级:支持针对特定设备集合的定向升级策略
- 灰度发布:通过 beta 标识实现渐进式升级发布
- 强制升级:支持强制升级策略,阻止设备继续使用旧版本
**章节来源**
- [internal/handler/ota.go:23-144](file://internal/handler/ota.go#L23-L144)
- [internal/repository/ota.go:19-158](file://internal/repository/ota.go#L19-L158)
- [internal/model/ota.go:30-45](file://internal/model/ota.go#L30-L45)
## 依赖分析
- 处理器依赖仓库;仓库依赖数据库;应用入口依赖配置与基础设施。
- 搜索与缓存作为独立模块被配置层加载并在处理器中可选使用。
- 设备持久化依赖 Redis 缓存和定时任务。
- 阻抗持久化依赖 Redis 缓存、数据库仓库和定时任务。
- OTA 升级依赖复杂的业务逻辑和多表关联查询。
- 代码内未发现循环依赖。
```mermaid
graph LR
MAIN["cmd/server/main.go"] --> CFG["internal/config/config.go"]
MAIN --> MYSQL["internal/database/mysql.go"]
MAIN --> REDIS["internal/cache/redis.go"]
MAIN --> MEILI["internal/search/meilisearch.go"]
HANDLER_B["internal/handler/brand.go"] --> REPO_B["internal/repository/brand.go"]
HANDLER_M["internal/handler/model.go"] --> REPO_M["internal/repository/model.go"]
HANDLER_D["internal/handler/device.go"] --> REPO_D["internal/repository/device.go"]
HANDLER_I["internal/handler/impedance.go"] --> REPO_I["internal/repository/headphone_impedance.go"]
HANDLER_O["internal/handler/ota.go"] --> REPO_O["internal/repository/ota.go"]
REPO_B --> DB["sql/model.sql(表结构)"]
REPO_M --> DB
REPO_D --> DB_UD["sql/user_device.sql"]
REPO_D --> DB_UA["sql/user_active.sql"]
REPO_I --> DB_I["sql/user_headphone_impedance.sql"]
REPO_O --> DB_OTA["sql/ota.sql"]
REPO_O --> DB_BL["sql/black_list.sql"]
REPO_O --> DB_OTD["sql/ota_target_device.sql"]
TASK_DP["internal/task/device_persist.go"] --> REPO_D
TASK_IP["internal/task/impedance_persist.go"] --> REPO_I
```
**图表来源**
- [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)
- [internal/database/mysql.go:1-47](file://internal/database/mysql.go#L1-L47)
- [internal/cache/redis.go:1-17](file://internal/cache/redis.go#L1-L17)
- [internal/search/meilisearch.go:1-46](file://internal/search/meilisearch.go#L1-L46)
- [internal/handler/brand.go:1-50](file://internal/handler/brand.go#L1-L50)
- [internal/handler/model.go:1-51](file://internal/handler/model.go#L1-L51)
- [internal/handler/device.go:1-83](file://internal/handler/device.go#L1-L83)
- [internal/handler/impedance.go:1-116](file://internal/handler/impedance.go#L1-L116)
- [internal/handler/ota.go:1-145](file://internal/handler/ota.go#L1-L145)
- [internal/repository/brand.go:1-51](file://internal/repository/brand.go#L1-L51)
- [internal/repository/model.go:1-95](file://internal/repository/model.go#L1-L95)
- [internal/repository/device.go:1-88](file://internal/repository/device.go#L1-L88)
- [internal/repository/headphone_impedance.go:1-100](file://internal/repository/headphone_impedance.go#L1-L100)
- [internal/repository/ota.go:1-159](file://internal/repository/ota.go#L1-L159)
- [sql/model.sql:20-38](file://sql/model.sql#L20-L38)
- [sql/user_device.sql:20-35](file://sql/user_device.sql#L20-L35)
- [sql/user_active.sql:20-208](file://sql/user_active.sql#L20-L208)
- [sql/ota.sql:20-40](file://sql/ota.sql#L20-L40)
- [sql/black_list.sql:20-30](file://sql/black_list.sql#L20-L30)
- [sql/ota_target_device.sql:20-31](file://sql/ota_target_device.sql#L20-L31)
- [sql/user_headphone_impedance.sql:20-36](file://sql/user_headphone_impedance.sql#L20-L36)
- [internal/task/device_persist.go:1-167](file://internal/task/device_persist.go#L1-L167)
- [internal/task/impedance_persist.go:1-163](file://internal/task/impedance_persist.go#L1-L163)
## 性能考虑
- 连接池与生命周期
- 最大打开连接数:25;最大空闲连接数:5;连接最大生命周期:5 分钟
- 建议:根据并发与查询负载调整;生产环境结合监控指标动态优化
- 查询优化
- 品牌列表:支持模糊匹配,建议在高频搜索场景引入前缀索引或搜索引擎替代
- 型号列表:按品牌名精确匹配或按型号名模糊匹配;建议对常用过滤字段建立合适索引
- 设备查询:user_device 表的 mac_addr 唯一索引支持快速查找
- 阻抗查询:user_headphone_impedance 表的唯一复合索引(mac_addr, headphone_brand_norm, headphone_model_norm)支持高效去重查询
- OTA 查询:按 model、hw、beta、status 组合查询,建议建立复合索引
- 缓存策略
- 对品牌列表与热门型号列表进行短期缓存,设置合理过期时间
- 设备信息使用 Redis 缓存,避免频繁数据库写入
- 阻抗信息使用 Redis Hash 缓存,支持高效的批量处理和去重更新
- 使用缓存穿透防护(空结果也缓存短时间)与缓存雪崩防护(随机过期时间)
- 搜索优化
- 使用搜索引擎进行全文检索与高亮,减少数据库 LIKE 查询压力
- 控制返回字段数量,避免传输冗余数据
- I/O 与序列化
- 响应支持 Base64 编码,适合二进制传输场景,但会增加 CPU 开销
- 分区表优化
- user_active 表按日期分区,提高大数据量下的查询性能
- 分区裁剪可以显著减少扫描数据量
- 阻抗持久化优化
- 批量处理:每次定时任务批量处理所有缓存的阻抗数据
- 去重更新:通过唯一复合索引避免重复插入,仅更新现有记录
- 失败重试:处理失败的记录保留在Redis中,下次任务继续尝试
**更新** 确认了阻抗数据的性能优化策略,包括Redis缓存、批量处理和去重更新机制的完整实现。
**章节来源**
- [internal/database/mysql.go:33-36](file://internal/database/mysql.go#L33-L36)
- [internal/repository/brand.go:24-27](file://internal/repository/brand.go#L24-L27)
- [internal/repository/model.go:31-40](file://internal/repository/model.go#L31-L40)
- [internal/repository/device.go:19-87](file://internal/repository/device.go#L19-L87)
- [internal/repository/headphone_impedance.go:19-100](file://internal/repository/headphone_impedance.go#L19-L100)
- [internal/repository/ota.go:19-158](file://internal/repository/ota.go#L19-L158)
- [internal/search/meilisearch.go:22-26](file://internal/search/meilisearch.go#L22-L26)
- [sql/user_active.sql:33-204](file://sql/user_active.sql#L33-L204)
- [sql/user_headphone_impedance.sql:33-35](file://sql/user_headphone_impedance.sql#L33-L35)
- [internal/task/impedance_persist.go:44-108](file://internal/task/impedance_persist.go#L44-L108)
## 故障排查指南
- 数据库连接失败
- 检查环境变量是否正确设置;确认主机、端口、用户名、密码与数据库名
- 启动时会进行 Ping 校验,失败会记录错误并退出
- 查询异常
- 仓库层对扫描与迭代过程进行错误包装,定位具体阶段(扫描、迭代)
- 建议开启数据库慢查询日志与应用日志聚合
- 搜索与缓存
- 搜索引擎与缓存客户端初始化后会记录连接信息;若无响应,检查主机、密钥与网络连通性
- Redis 缓存异常会影响设备持久化功能和阻抗持久化功能
- 响应编码
- Base64 返回失败时,检查编码流程与日志输出
- 设备持久化问题
- 检查 Redis 连接状态和设备缓存数据
- 定时任务执行失败时,查看任务日志和数据库连接状态
- 阻抗持久化问题
- 检查 Redis 连接状态和阻抗缓存数据
- 验证唯一复合索引是否正常工作
- 检查品牌/型号归一化处理是否正确
- 定时任务执行失败时,查看任务日志和数据库连接状态
- OTA 升级问题
- 检查 OTA 表数据完整性和索引状态
- 验证黑名单和定向设备表的数据准确性
**更新** 确认了阻抗持久化功能的故障排查指南,包括Redis缓存、索引和数据处理相关的常见问题。
**章节来源**
- [internal/config/database.go:57-71](file://internal/config/database.go#L57-L71)
- [internal/database/mysql.go:40-43](file://internal/database/mysql.go#L40-L43)
- [internal/repository/brand.go:32-47](file://internal/repository/brand.go#L32-L47)
- [internal/repository/model.go:43-58](file://internal/repository/model.go#L43-L58)
- [internal/handler/brand.go:31-43](file://internal/handler/brand.go#L31-L43)
- [internal/handler/model.go:33-43](file://internal/handler/model.go#L33-L43)
- [internal/handler/device.go:45-82](file://internal/handler/device.go#L45-L82)
- [internal/handler/impedance.go:43-107](file://internal/handler/impedance.go#L43-L107)
- [internal/handler/ota.go:56-144](file://internal/handler/ota.go#L56-L144)
- [internal/search/meilisearch.go:17-20](file://internal/search/meilisearch.go#L17-L20)
- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16)
- [internal/task/impedance_persist.go:44-108](file://internal/task/impedance_persist.go#L44-L108)
## 结论
本设计以简洁的表结构与清晰的分层架构支撑品牌与型号的查询需求。通过搜索引擎与缓存提升检索性能,配合连接池与错误处理机制保障稳定性。**更新** 本次更新确认了完整的用户耳机阻抗功能模块实现,包括数据库表设计、API接口、Redis缓存机制和定时持久化任务。该功能通过唯一复合索引确保数据完整性,使用Redis缓存提高写入性能,并通过批量处理优化数据库写入效率。新增的设备持久化和阻抗持久化子系统进一步完善了系统的业务能力,通过Redis缓存和分区表优化提升了大规模数据处理的性能。后续可在索引策略、缓存策略与数据迁移方面进一步细化,以满足生产环境的高可用与高性能要求。
## 附录
### 数据验证与业务规则
- 品牌与型号名称必填,型号表对品牌+型号建立唯一索引,防止重复
- 设备 MAC 地址唯一,支持设备去重和版本跟踪
- 阻抗数据验证:
- 所有参数必填:mac、name、brand、model、value
- 阻抗值必须为整数
- 品牌/型号进行归一化处理:trim+lower,确保大小写不敏感
- 唯一性约束:同一设备的同一耳机品牌+型号只保留最新记录
- OTA 升级记录包含版本号、MD5 校验、时间范围等完整性约束
- 黑名单和定向设备表确保 OTA 发布的安全性
- 时间字段 create_at 默认当前时间,便于审计与排序
- 处理器层对空结果返回空数组,避免无效数据传播
**更新** 确认了阻抗数据的验证规则和业务逻辑,支持数据归一化和去重更新的完整实现。
**章节来源**
- [sql/model.sql:24-35](file://sql/model.sql#L24-L35)
- [sql/user_device.sql:24-32](file://sql/user_device.sql#L24-L32)
- [sql/ota.sql:24-40](file://sql/ota.sql#L24-L40)
- [sql/black_list.sql:24-30](file://sql/black_list.sql#L24-L30)
- [sql/ota_target_device.sql:24-31](file://sql/ota_target_device.sql#L24-L31)
- [sql/user_headphone_impedance.sql:20-36](file://sql/user_headphone_impedance.sql#L20-L36)
- [internal/repository/brand.go:24-27](file://internal/repository/brand.go#L24-L27)
- [internal/repository/model.go:31-40](file://internal/repository/model.go#L31-L40)
- [internal/handler/impedance.go:51-66](file://internal/handler/impedance.go#L51-L66)
### 示例数据
- 品牌示例:id=1, name="Sony"
- 型号示例:id=1001, brand_name="Sony", name="WH-1000XM4", form="头戴式", rig="主动降噪", source="官方", eq_key="sony_xm4", create_at="2026-01-01 12:00:00"
- 设备示例:id=1214, mac_addr="AA:BB:CC:DD:EE:FF", model="WH-1000XM4", add_time="2026-05-28 17:08:30", ver="1.2.3"
- OTA 示例:id=48, verCode=100, verName="v1.2.3", url="http://example.com/firmware.bin", md5="d41d8cd98f00b204e9800998ecf8427e", force=1, desc="重大安全更新", model="WH-1000XM4", hw=1, target=1, beta=0, status=1
- 阻抗示例:id=1, mac_addr="AA:BB:CC:DD:EE:FF", 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", create_at="2026-01-01 12:00:00", update_at="2026-01-01 12:00:00"
**更新** 确认了阻抗数据的示例,展示了完整的字段结构和数据格式。
**章节来源**
- [internal/model/brand.go:4-5](file://internal/model/brand.go#L4-L5)
- [internal/model/model.go:6-13](file://internal/model/model.go#L6-L13)
- [internal/model/user_device.go:5-11](file://internal/model/user_device.go#L5-L11)
- [internal/model/ota.go:30-45](file://internal/model/ota.go#L30-L45)
- [internal/model/user_headphone_impedance.go:5-17](file://internal/model/user_headphone_impedance.go#L5-L17)
### 数据生命周期、保留策略与归档规则
- 建议:基于 create_at 建立分区或定期归档任务,清理长期未使用的型号数据
- 设备数据:保留最近 30 天的设备活跃记录,历史数据可压缩存储
- 阻抗数据:
- 保留策略:每个设备的每个耳机品牌+型号只保留最新记录
- 数据更新:相同耳机的阻抗值更新时,覆盖原有记录
- 归档策略:可根据业务需求定期归档历史阻抗数据
- OTA 数据:保留最近 6 个月的升级记录,历史版本可归档
- 归档策略:保留近一年的活跃型号,历史数据移至冷存储
- 审计:保留变更日志与备份周期,确保可追溯性
**更新** 确认了阻抗数据的生命周期管理策略,支持数据更新和归档的完整实现。
**章节来源**
- [sql/user_active.sql:33-204](file://sql/user_active.sql#L33-L204)
- [sql/ota.sql:24-40](file://sql/ota.sql#L24-L40)
- [sql/user_headphone_impedance.sql:20-36](file://sql/user_headphone_impedance.sql#L20-L36)
### 数据迁移路径与版本管理
- 版本化:每次结构变更生成迁移脚本,记录变更人、时间与目的
- 回滚:保留逆向迁移脚本;灰度发布逐步切换
- 兼容:新增字段采用可空策略,旧数据默认值填充
- 设备数据迁移:支持从临时表导入到正式表,处理重复数据
- 阻抗数据迁移:
- 建表脚本:sql/user_headphone_impedance.sql
- 索引创建:唯一复合索引和普通索引
- 数据初始化:支持从其他数据源导入阻抗数据
- OTA 数据迁移:支持版本号递增和兼容性检查
**更新** 确认了阻抗数据的迁移路径和版本管理策略的完整实现。
**章节来源**
- [sql/user_device.sql:20-35](file://sql/user_device.sql#L20-L35)
- [sql/user_active.sql:20-208](file://sql/user_active.sql#L20-L208)
- [sql/ota.sql:20-40](file://sql/ota.sql#L20-L40)
- [sql/user_headphone_impedance.sql:20-36](file://sql/user_headphone_impedance.sql#L20-L36)
### 数据安全、隐私要求与访问控制
- 网络安全:数据库与搜索引擎通过内网或 VPC 访问,限制入站 IP
- 凭证管理:敏感信息通过环境变量注入,禁止硬编码
- 访问控制:最小权限原则;数据库账号仅授予必要权限;搜索引擎 API Key 严格管理
- 设备隐私:MAC 地址等个人标识信息需遵循隐私保护法规
- 阻抗数据隐私:
- 设备MAC地址属于个人标识信息,需遵循隐私保护法规
- 阻抗数据可能反映用户的听力健康状况,需要特别保护
- 数据传输过程中应进行加密处理
- OTA 安全:固件 MD5 校验确保下载完整性,黑名单机制防止恶意设备升级
**更新** 确认了阻抗数据的安全要求和隐私保护措施的实现。
**章节来源**
- [internal/config/database.go:67-69](file://internal/config/database.go#L67-L69)
- [internal/config/meilisearch.go:39-50](file://internal/config/meilisearch.go#L39-L50)
- [internal/config/redis.go:51-56](file://internal/config/redis.go#L51-L56)
- [internal/handler/device.go:33-48](file://internal/handler/device.go#L33-L48)
- [internal/handler/impedance.go:43-107](file://internal/handler/impedance.go#L43-L107)
- [internal/handler/ota.go:32-48](file://internal/handler/ota.go#L32-L48)
### 品牌与型号实体设计理念
- 品牌(Brand):最小可用模型,仅包含标识与名称,便于快速检索与展示
- 型号(Model):承载产品特性字段(form、rig、source、eq_key),并与品牌建立弱关联,利于扩展与维护
- 业务逻辑:通过仓库层的过滤条件实现灵活查询;处理器层统一响应格式,支持 Base64 编码
**章节来源**
- [internal/model/brand.go:3-6](file://internal/model/brand.go#L3-L6)
- [internal/model/model.go:5-14](file://internal/model/model.go#L5-L14)
- [internal/repository/brand.go:20-50](file://internal/repository/brand.go#L20-L50)
- [internal/repository/model.go:20-61](file://internal/repository/model.go#L20-L61)
### 设备持久化与阻抗持久化设计理念
- 设备持久化:通过 Redis 缓存实现高并发设备上报,定时任务批量持久化降低数据库压力
- 阻抗持久化:
- 通过 Redis Hash 缓存实现高并发阻抗上报,定时任务批量持久化降低数据库压力
- 支持数据归一化处理,确保大小写不敏感的去重
- 通过唯一复合索引确保数据完整性,避免重复记录
- 支持阻抗值更新和设备信息覆盖更新
- OTA 升级:支持灰度发布、强制升级、定向升级等多种发布策略,确保升级过程可控
- 数据一致性:通过唯一索引和事务保证数据完整性,通过分区表优化查询性能
- 可扩展性:模块化设计支持功能扩展,预留接口便于新业务场景接入
**更新** 确认了阻抗持久化的设计理念,体现了高并发处理和数据一致性的设计思想的完整实现。
**章节来源**
- [internal/model/user_device.go:5-11](file://internal/model/user_device.go#L5-L11)
- [internal/model/user_active.go:5-12](file://internal/model/user_active.go#L5-L12)
- [internal/model/user_headphone_impedance.go:5-17](file://internal/model/user_headphone_impedance.go#L5-L17)
- [internal/model/ota.go:30-45](file://internal/model/ota.go#L30-L45)
- [internal/task/device_persist.go:24-167](file://internal/task/device_persist.go#L24-L167)
- [internal/task/impedance_persist.go:31-163](file://internal/task/impedance_persist.go#L31-L163)
- [internal/handler/ota.go:23-144](file://internal/handler/ota.go#L23-L144)