修复了EQ 曲线接口中,+没有转义成空格的 bug

This commit is contained in:
eafonyang
2026-07-02 12:06:10 +08:00
parent 6e8aa239e8
commit 455bea8380
6 changed files with 63 additions and 75 deletions
+21 -22
View File
@@ -34,10 +34,10 @@
## 更新摘要 ## 更新摘要
**所做更改** **所做更改**
- 更新OTA表结构定义:删除了pawVerCode、pawVerName、pawUrl、pawMd5四列,OTA表现在包含id、verCode、verName、url、md5、force、desc、model、hw、target、beta、startTime、endTime、status等核心字段,简化了数据库结构 - 更新OTA目标设备表结构:ota_target_device 表已简化,移除了 type 字段,现在包含 id、ota_id、mac_addr、create_at 字段
- 更新数据模型文档以反映OTA表结构简化 - 更新实体关系图和字段定义以反映表结构简化
- 更新实体关系图和字段定义
- 更新数据访问模式和业务逻辑说明 - 更新数据访问模式和业务逻辑说明
- 更新OTA升级子系统的实现细节
## 目录 ## 目录
1. [简介](#简介) 1. [简介](#简介)
@@ -54,7 +54,7 @@
## 简介 ## 简介
本文件面向 Luxsin 应用 API 的数据库与数据模型,系统化梳理实体关系、字段定义、索引与约束、数据访问模式、缓存与搜索集成、性能优化、数据生命周期与迁移路径,并给出品牌(Brand)与型号(Model)实体的设计理念与业务逻辑说明。文档同时覆盖数据库连接配置、查询优化建议、数据安全与隐私要求以及访问控制要点。 本文件面向 Luxsin 应用 API 的数据库与数据模型,系统化梳理实体关系、字段定义、索引与约束、数据访问模式、缓存与搜索集成、性能优化、数据生命周期与迁移路径,并给出品牌(Brand)与型号(Model)实体的设计理念与业务逻辑说明。文档同时覆盖数据库连接配置、查询优化建议、数据安全与隐私要求以及访问控制要点。
**更新** 本次更新反映了OTA表结构的重大简化,除了pawVerCode、pawVerName、pawUrl、pawMd5四个冗余字段,保留了核心的固件升级信息字段,提高了数据库结构的简洁性和可维护性。 **更新** 本次更新反映了 OTA 目标设备表结构的重大简化,除了 type 字段,现在采用更简洁的单表架构设计,提高了数据库结构的简洁性和可维护性。
## 项目结构 ## 项目结构
本项目采用分层架构:入口程序负责初始化配置、数据库、搜索引擎与缓存;路由层组织 HTTP 接口;处理器层封装业务接口;仓库层实现数据访问;模型层承载数据结构;搜索与缓存模块作为外部依赖集成。 本项目采用分层架构:入口程序负责初始化配置、数据库、搜索引擎与缓存;路由层组织 HTTP 接口;处理器层封装业务接口;仓库层实现数据访问;模型层承载数据结构;搜索与缓存模块作为外部依赖集成。
@@ -146,14 +146,14 @@ TASK_DP --> REPO_D
## 核心组件 ## 核心组件
- 数据库表 model:存储耳机型号信息,包含唯一组合索引以保证品牌+型号的唯一性。 - 数据库表 model:存储耳机型号信息,包含唯一组合索引以保证品牌+型号的唯一性。
- 设备持久化表:user_device(设备信息持久化)、user_active(设备活跃度统计) - 设备持久化表:user_device(设备信息持久化)、user_active(设备活跃度统计)
- OTA 升级表:ota(固件升级信息,已简化结构)、black_list(黑名单)、ota_target_device(定向设备) - OTA 升级表:ota(固件升级信息)、black_list(黑名单)、ota_target_device(定向设备)
- 模型对象 Brand 与 Model:分别映射品牌与型号的 JSON 字段与数据库列。 - 模型对象 Brand 与 Model:分别映射品牌与型号的 JSON 字段与数据库列。
- 仓库层:提供按品牌名或型号名检索的查询方法,支持模糊匹配与精确匹配。 - 仓库层:提供按品牌名或型号名检索的查询方法,支持模糊匹配与精确匹配。
- 处理器层:暴露 HTTP 接口,支持返回 JSON 或 Base64 编码响应。 - 处理器层:暴露 HTTP 接口,支持返回 JSON 或 Base64 编码响应。
- 配置层:集中加载数据库、搜索引擎与缓存的连接参数,并进行基本校验。 - 配置层:集中加载数据库、搜索引擎与缓存的连接参数,并进行基本校验。
- 基础设施:MySQL 连接池配置、Redis 客户端、Meilisearch 搜索客户端。 - 基础设施:MySQL 连接池配置、Redis 客户端、Meilisearch 搜索客户端。
**更新** OTA表结构已简化,除了pawVerCode、pawVerName、pawUrl、pawMd5四个冗余字段,保留了核心的固件升级信息字段 **更新** OTA 目标设备表结构已简化,除了 type 字段,现在采用更简洁的单表架构设计
**章节来源** **章节来源**
- [sql/model.sql:20-38](file://sql/model.sql#L20-L38) - [sql/model.sql:20-38](file://sql/model.sql#L20-L38)
@@ -234,9 +234,9 @@ note over H,RC : "可选:对热点数据进行缓存读写<br/>设备上报:
- UserActiveid(整数,主键)、mac_addr(字符串)、model(字符串)、active_date(日期,主键部分)、ip_addr(字符串)、create_at(时间戳,默认当前时间) - 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) - 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(时间戳,默认当前时间) - BlackListid(整数,主键)、ota_id(整数)、mac(字符串,默认空)、create_at(时间戳,默认当前时间)
- OTATargetDeviceid(整数,主键)、ota_id(整数)、mac_addr(字符串)、type(整数,默认1,1-白名单;2-黑名单)、create_at(时间戳,默认当前时间) - OTATargetDeviceid(整数,主键)、ota_id(整数)、mac_addr(字符串)、create_at(时间戳,默认当前时间)
**更新** OTA表结构已简化,除了pawVerCode、pawVerName、pawUrl、pawMd5四个冗余字段,保留了核心的固件升级信息字段 **更新** OTA 目标设备表结构已简化,除了 type 字段,现在采用更简洁的单表架构设计,type 字段不再存在于 ota_target_device 表中
```mermaid ```mermaid
erDiagram erDiagram
@@ -295,7 +295,6 @@ OTA_TARGET_DEVICE {
int id PK int id PK
int ota_id int ota_id
string mac_addr string mac_addr
int type
datetime create_at datetime create_at
} }
BRAND ||--o{ MODEL : "拥有多个型号" BRAND ||--o{ MODEL : "拥有多个型号"
@@ -342,7 +341,7 @@ OTA ||--o{ OTA_TARGET_DEVICE : "定向设备关联"
- 分区表:user_active 表按 active_date 进行范围分区,包含 170 个分区 - 分区表:user_active 表按 active_date 进行范围分区,包含 170 个分区
- 存储引擎与字符集:InnoDB、utf8mb4、排序规则 0900_ai_ci - 存储引擎与字符集:InnoDB、utf8mb4、排序规则 0900_ai_ci
**更新** OTA表结构已简化,除了pawVerCode、pawVerName、pawUrl、pawMd5四个冗余字段,保留了核心的固件升级信息字段 **更新** OTA 目标设备表结构已简化,除了 type 字段,现在采用更简洁的单表架构设计
**章节来源** **章节来源**
- [sql/model.sql:20-38](file://sql/model.sql#L20-L38) - [sql/model.sql:20-38](file://sql/model.sql#L20-L38)
@@ -409,7 +408,7 @@ end
- OTA 升级接口:接收 model、hw、mac、beta 参数,返回合适的 OTA 升级信息 - OTA 升级接口:接收 model、hw、mac、beta 参数,返回合适的 OTA 升级信息
- 错误处理:内部错误统一返回 500 并记录日志 - 错误处理:内部错误统一返回 500 并记录日志
**更新** OTA接口现在使用简化的OTA表结构,不再需要处理pawVerCode、pawVerName、pawUrl、pawMd5字段。 **更新** OTA 接口现在使用简化的 OTA 目标设备表结构,不再需要处理 type 字段。
**章节来源** **章节来源**
- [internal/handler/brand.go:1-50](file://internal/handler/brand.go#L1-L50) - [internal/handler/brand.go:1-50](file://internal/handler/brand.go#L1-L50)
@@ -423,7 +422,7 @@ end
- 设备缓存:使用 Redis Hash 存储设备上报信息,键为设备 MAC 地址 - 设备缓存:使用 Redis Hash 存储设备上报信息,键为设备 MAC 地址
- 持久化策略:定时任务批量处理 Redis 缓存数据,避免频繁数据库写入 - 持久化策略:定时任务批量处理 Redis 缓存数据,避免频繁数据库写入
**更新** OTA缓存策略保持不变,但查询逻辑已简化。 **更新** OTA 缓存策略保持不变,但查询逻辑已简化。
**章节来源** **章节来源**
- [internal/search/meilisearch.go:1-46](file://internal/search/meilisearch.go#L1-L46) - [internal/search/meilisearch.go:1-46](file://internal/search/meilisearch.go#L1-L46)
@@ -473,7 +472,7 @@ end
- 灰度发布:通过 beta 标识实现渐进式升级发布 - 灰度发布:通过 beta 标识实现渐进式升级发布
- 强制升级:支持强制升级策略,阻止设备继续使用旧版本 - 强制升级:支持强制升级策略,阻止设备继续使用旧版本
**更新** OTA表结构已简化,除了pawVerCode、pawVerName、pawUrl、pawMd5四个冗余字段,查询逻辑相应简化。 **更新** OTA 目标设备表结构已简化,除了 type 字段,查询逻辑相应简化。
**章节来源** **章节来源**
- [internal/handler/ota.go:23-144](file://internal/handler/ota.go#L23-L144) - [internal/handler/ota.go:23-144](file://internal/handler/ota.go#L23-L144)
@@ -551,7 +550,7 @@ TASK_DP["internal/task/device_persist.go"] --> REPO_D
- user_active 表按日期分区,提高大数据量下的查询性能 - user_active 表按日期分区,提高大数据量下的查询性能
- 分区裁剪可以显著减少扫描数据量 - 分区裁剪可以显著减少扫描数据量
**更新** OTA查询逻辑已简化,减少了不必要的字段扫描,提高了查询效率。 **更新** OTA 查询逻辑已简化,减少了不必要的字段扫描,提高了查询效率。
**章节来源** **章节来源**
- [internal/database/mysql.go:33-36](file://internal/database/mysql.go#L33-L36) - [internal/database/mysql.go:33-36](file://internal/database/mysql.go#L33-L36)
@@ -581,7 +580,7 @@ TASK_DP["internal/task/device_persist.go"] --> REPO_D
- 检查 OTA 表数据完整性和索引状态 - 检查 OTA 表数据完整性和索引状态
- 验证黑名单和定向设备表的数据准确性 - 验证黑名单和定向设备表的数据准确性
**更新** OTA故障排查已简化,不再需要检查pawVerCode、pawVerName、pawUrl、pawMd5字段。 **更新** OTA 故障排查已简化,不再需要检查 type 字段。
**章节来源** **章节来源**
- [internal/config/database.go:57-71](file://internal/config/database.go#L57-L71) - [internal/config/database.go:57-71](file://internal/config/database.go#L57-L71)
@@ -596,7 +595,7 @@ TASK_DP["internal/task/device_persist.go"] --> REPO_D
- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) - [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16)
## 结论 ## 结论
本设计以简洁的表结构与清晰的分层架构支撑品牌与型号的查询需求。通过搜索引擎与缓存提升检索性能,配合连接池与错误处理机制保障稳定性。**更新** OTA表结构的简化显著提高了数据库的简洁性和可维护性,除了pawVerCode、pawVerName、pawUrl、pawMd5四个冗余字段,保留了核心的固件升级信息。新增的设备持久化和 OTA 固件升级子系统进一步完善了系统的业务能力,通过 Redis 缓存和分区表优化提升了大规模数据处理的性能。后续可在索引策略、缓存策略与数据迁移方面进一步细化,以满足生产环境的高可用与高性能要求。 本设计以简洁的表结构与清晰的分层架构支撑品牌与型号的查询需求。通过搜索引擎与缓存提升检索性能,配合连接池与错误处理机制保障稳定性。**更新** OTA 目标设备表结构的简化显著提高了数据库的简洁性和可维护性,除了 type 字段,现在采用更简洁的单表架构设计。新增的设备持久化和 OTA 固件升级子系统进一步完善了系统的业务能力,通过 Redis 缓存和分区表优化提升了大规模数据处理的性能。后续可在索引策略、缓存策略与数据迁移方面进一步细化,以满足生产环境的高可用与高性能要求。
## 附录 ## 附录
@@ -608,7 +607,7 @@ TASK_DP["internal/task/device_persist.go"] --> REPO_D
- 时间字段 create_at 默认当前时间,便于审计与排序 - 时间字段 create_at 默认当前时间,便于审计与排序
- 处理器层对空结果返回空数组,避免无效数据传播 - 处理器层对空结果返回空数组,避免无效数据传播
**更新** OTA数据验证规则已简化,不再需要验证pawVerCode、pawVerName、pawUrl、pawMd5字段。 **更新** OTA 数据验证规则已简化,不再需要验证 type 字段。
**章节来源** **章节来源**
- [sql/model.sql:24-35](file://sql/model.sql#L24-L35) - [sql/model.sql:24-35](file://sql/model.sql#L24-L35)
@@ -625,7 +624,7 @@ TASK_DP["internal/task/device_persist.go"] --> REPO_D
- 设备示例: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" - 设备示例: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 - 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
**更新** OTA示例数据已简化,移除了pawVerCode、pawVerName、pawUrl、pawMd5字段。 **更新** OTA 示例数据已简化,移除了 type 字段。
**章节来源** **章节来源**
- [internal/model/brand.go:4-5](file://internal/model/brand.go#L4-L5) - [internal/model/brand.go:4-5](file://internal/model/brand.go#L4-L5)
@@ -640,7 +639,7 @@ TASK_DP["internal/task/device_persist.go"] --> REPO_D
- 归档策略:保留近一年的活跃型号,历史数据移至冷存储 - 归档策略:保留近一年的活跃型号,历史数据移至冷存储
- 审计:保留变更日志与备份周期,确保可追溯性 - 审计:保留变更日志与备份周期,确保可追溯性
**更新** OTA数据生命周期管理保持不变,但查询逻辑已简化。 **更新** OTA 数据生命周期管理保持不变,但查询逻辑已简化。
**章节来源** **章节来源**
- [sql/user_active.sql:33-204](file://sql/user_active.sql#L33-L204) - [sql/user_active.sql:33-204](file://sql/user_active.sql#L33-L204)
@@ -653,7 +652,7 @@ TASK_DP["internal/task/device_persist.go"] --> REPO_D
- 设备数据迁移:支持从临时表导入到正式表,处理重复数据 - 设备数据迁移:支持从临时表导入到正式表,处理重复数据
- OTA 数据迁移:支持版本号递增和兼容性检查 - OTA 数据迁移:支持版本号递增和兼容性检查
**更新** OTA数据迁移已简化,不需要处理pawVerCode、pawVerName、pawUrl、pawMd5字段的迁移。 **更新** OTA 数据迁移已简化,不需要处理 type 字段的迁移。
**章节来源** **章节来源**
- [sql/user_device.sql:20-35](file://sql/user_device.sql#L20-L35) - [sql/user_device.sql:20-35](file://sql/user_device.sql#L20-L35)
@@ -667,7 +666,7 @@ TASK_DP["internal/task/device_persist.go"] --> REPO_D
- 设备隐私:MAC 地址等个人标识信息需遵循隐私保护法规 - 设备隐私:MAC 地址等个人标识信息需遵循隐私保护法规
- OTA 安全:固件 MD5 校验确保下载完整性,黑名单机制防止恶意设备升级 - OTA 安全:固件 MD5 校验确保下载完整性,黑名单机制防止恶意设备升级
**更新** OTA安全机制保持不变,但查询逻辑已简化。 **更新** OTA 安全机制保持不变,但查询逻辑已简化。
**章节来源** **章节来源**
- [internal/config/database.go:67-69](file://internal/config/database.go#L67-L69) - [internal/config/database.go:67-69](file://internal/config/database.go#L67-L69)
@@ -693,7 +692,7 @@ TASK_DP["internal/task/device_persist.go"] --> REPO_D
- 数据一致性:通过唯一索引和事务保证数据完整性,通过分区表优化查询性能 - 数据一致性:通过唯一索引和事务保证数据完整性,通过分区表优化查询性能
- 可扩展性:模块化设计支持功能扩展,预留接口便于新业务场景接入 - 可扩展性:模块化设计支持功能扩展,预留接口便于新业务场景接入
**更新** OTA设计理念保持不变,但实现逻辑已简化。 **更新** OTA 设计理念保持不变,但实现逻辑已简化。
**章节来源** **章节来源**
- [internal/model/user_device.go:5-11](file://internal/model/user_device.go#L5-L11) - [internal/model/user_device.go:5-11](file://internal/model/user_device.go#L5-L11)
@@ -317,8 +317,8 @@ BrandRepository --> Brand : "返回"
### 型号处理器(ModelHandler ### 型号处理器(ModelHandler
- **职责**:根据品牌或型号关键字查询型号列表。 - **职责**:根据品牌或型号关键字查询型号列表。
- **输入参数** - **输入参数**
- 查询字符串:brandName(可选 - 查询字符串:brandName(可选)
- 查询字符串:modelName(可选 - 查询字符串:modelName(可选)
- 查询字符串:base64Resp(可选,默认开启) - 查询字符串:base64Resp(可选,默认开启)
- **处理流程** - **处理流程**
- 读取查询参数并解析 base64Resp。 - 读取查询参数并解析 base64Resp。
@@ -42,9 +42,9 @@
## 更新摘要 ## 更新摘要
**变更内容** **变更内容**
- OTA模型字段简化:移除了pawVerCode、pawVerName、pawUrl、pawMd5四个冗余字段,OTA结构现在更加简洁 - OTA模型字段简化:移除了pawVerCode、pawVerName、pawUrl、pawMd5四个冗余字段,OTA结构现在更加简洁
- OTA模型现在包含核心字段:verCode、verName、url、md5、force、desc、model、hw、target、beta、startTime、endTime、status - OTA灰度升级设备模型重构:从包含Type字段的定向设备改为不包含类型字段的灰度升级设备,简化了设备管理逻辑
- 数据库表结构同步更新,移除了对应的冗余列 - 数据库表结构同步更新ota_target_device表移除了type字段,OTATargetDevice结构体不再包含Type字段
- 仓库层和处理器层相应调整,移除了对冗余字段的处理 - 业务逻辑调整:OTA升级策略从"定向设备"概念转变为"灰度升级设备"概念,简化了升级控制逻辑
## 目录 ## 目录
1. [简介](#简介) 1. [简介](#简介)
@@ -200,7 +200,7 @@ CACHE --> SHARE_TTL
- **OTA固件升级模型层** - **OTA固件升级模型层**
- OTA:固件升级记录,包含版本信息、下载地址、MD5校验、强制升级标志、灰度发布状态、定向升级配置等。 - OTA:固件升级记录,包含版本信息、下载地址、MD5校验、强制升级标志、灰度发布状态、定向升级配置等。
- BlackList:OTA黑名单,记录被禁止升级的设备MAC地址。 - BlackList:OTA黑名单,记录被禁止升级的设备MAC地址。
- OTATargetDeviceOTA定向设备,记录允许特定设备升级的白名单或黑名单 - OTATargetDeviceOTA灰度升级设备,记录允许参与灰度升级的设备列表
- **分享码模型层** - **分享码模型层**
- ShareCodeLog:分享码操作日志,记录导出/导入操作及EQ数据快照。 - ShareCodeLog:分享码操作日志,记录导出/导入操作及EQ数据快照。
- ShareCodeData:分享码缓存数据,包含分享码、MAC地址、设备型号、EQ数据、过期时间等。 - ShareCodeData:分享码缓存数据,包含分享码、MAC地址、设备型号、EQ数据、过期时间等。
@@ -212,7 +212,7 @@ CACHE --> SHARE_TTL
- **仓库层(Repository** - **仓库层(Repository**
- BrandRepository:提供按品牌名模糊查询的品牌列表。 - BrandRepository:提供按品牌名模糊查询的品牌列表。
- ModelRepository:提供按品牌名或型号名模糊查询的型号列表,并处理可空字段的扫描。 - ModelRepository:提供按品牌名或型号名模糊查询的型号列表,并处理可空字段的扫描。
- OTARepository:提供OTA升级信息查询、黑名单检查、定向设备查询等功能。 - OTARepository:提供OTA升级信息查询、黑名单检查、灰度升级设备查询等功能。
- DeviceRepository:提供设备信息查询、插入、更新以及活动记录管理。 - DeviceRepository:提供设备信息查询、插入、更新以及活动记录管理。
- ShareCodeRepository:提供分享码日志插入、导出记录检查等功能。 - ShareCodeRepository:提供分享码日志插入、导出记录检查等功能。
- CurveRepository:提供型号和目标曲线查询功能。 - CurveRepository:提供型号和目标曲线查询功能。
@@ -220,7 +220,7 @@ CACHE --> SHARE_TTL
- BrandHandler:接收查询参数,调用仓库层,支持可选的响应体 Base64 编码。 - BrandHandler:接收查询参数,调用仓库层,支持可选的响应体 Base64 编码。
- ModelHandler:接收品牌与型号查询参数,调用仓库层,支持可选的响应体 Base64 编码。 - ModelHandler:接收品牌与型号查询参数,调用仓库层,支持可选的响应体 Base64 编码。
- ModelListHandler:通过搜索客户端返回模型列表,支持可选的响应体 Base64 编码。 - ModelListHandler:通过搜索客户端返回模型列表,支持可选的响应体 Base64 编码。
- OTAHandler:获取OTA升级信息,支持黑名单检查、定向升级检查等功能。 - OTAHandler:获取OTA升级信息,支持黑名单检查、灰度升级设备检查等功能。
- DeviceHandler:处理设备信息上报,将设备活动信息写入Redis缓存。 - DeviceHandler:处理设备信息上报,将设备活动信息写入Redis缓存。
- ShareCodeHandler:处理分享码创建、查询、导入、删除等操作,支持Redis缓存和数据库持久化。 - ShareCodeHandler:处理分享码创建、查询、导入、删除等操作,支持Redis缓存和数据库持久化。
- CurveHandler:处理曲线数据获取,支持目标曲线配置和EQ参数化。 - CurveHandler:处理曲线数据获取,支持目标曲线配置和EQ参数化。
@@ -368,7 +368,7 @@ BrandRepository --> Brand : "返回"
- 无显式校验逻辑,依赖数据库约束与上层参数清洗。 - 无显式校验逻辑,依赖数据库约束与上层参数清洗。
- JSON 标签用于序列化输出。 - JSON 标签用于序列化输出。
- **查询流程** - **查询流程**
- 支持按品牌名精确匹配或按型号名模糊匹配,返回 []Model 列表。 - ostartTime 支持按品牌名精确匹配或按型号名模糊匹配,返回 []Model 列表。
- 默认返回空切片而非 nil,便于前端处理。 - 默认返回空切片而非 nil,便于前端处理。
```mermaid ```mermaid
@@ -402,8 +402,9 @@ ModelRepository --> Model : "返回"
### OTA 固件升级模型 ### OTA 固件升级模型
- **设计理念** - **设计理念**
- OTA模型用于管理固件升级信息,支持强制升级、灰度发布、定向升级等功能。 - OTA模型用于管理固件升级信息,支持强制升级、灰度发布、灰度升级设备等功能。
- **更新**OTA模型已简化,移除了pawVerCode、pawVerName、pawUrl、pawMd5四个冗余字段,现在只包含核心升级信息字段。 - **更新**OTA模型已简化,移除了pawVerCode、pawVerName、pawUrl、pawMd5四个冗余字段,现在只包含核心升级信息字段。
- **更新**OTATargetDevice模型已重构,移除了Type字段,从"定向设备"概念转变为"灰度升级设备"概念。
- **字段定义与约束** - **字段定义与约束**
- OTA模型: - OTA模型:
- id:整数,主键,自增。 - id:整数,主键,自增。
@@ -429,22 +430,23 @@ ModelRepository --> Model : "返回"
- id:整数,主键,自增。 - id:整数,主键,自增。
- OTAID:整数,关联OTA记录ID。 - OTAID:整数,关联OTA记录ID。
- MacAddr:字符串,设备MAC地址。 - MacAddr:字符串,设备MAC地址。
- Type:整数,类型(1-白名单,2-黑名单)。
- CreateAt:时间戳,创建时间。 - CreateAt:时间戳,创建时间。
- **业务含义** - **业务含义**
- 支持复杂的固件升级策略,包括强制升级、灰度发布、定向升级和黑名单管理 - 支持复杂的固件升级策略,包括强制升级、灰度发布、灰度升级设备管理等功能
- 简化的字段结构提高了数据模型的清晰度和维护性。 - 简化的字段结构提高了数据模型的清晰度和维护性。
- 灰度升级设备概念简化了设备管理逻辑,不再区分白名单和黑名单类型。
- **数据库映射** - **数据库映射**
- 表名:ota、black_list、ota_target_device。 - 表名:ota、black_list、ota_target_device。
- 字段:完整映射关系详见数据库表结构。 - 字段:完整映射关系详见数据库表结构。
- **更新**OTA表不再包含pawVerCode、pawVerName、pawUrl、pawMd5字段。 - **更新**OTA表不再包含pawVerCode、pawVerName、pawUrl、pawMd5字段。
- **更新**ota_target_device表不再包含type字段。
- **ORM 映射配置** - **ORM 映射配置**
- 使用 sql.NullString 和 sql.NullTime 扫描可空列。 - 使用 sql.NullString 和 sql.NullTime 扫描可空列。
- BoolInt类型支持数据库整型与JSON布尔值的双向转换。 - BoolInt类型支持数据库整型与JSON布尔值的双向转换。
- JSON 标签用于序列化输出,可空字段支持 omitempty。 - JSON 标签用于序列化输出,可空字段支持 omitempty。
- **查询流程** - **查询流程**
- 支持多种查询策略:最新OTA记录、非黑名单OTA记录、非定向OTA记录等。 - 支持多种查询策略:最新OTA记录、非黑名单OTA记录、非定向OTA记录等。
- 支持黑名单检查和定向设备检查。 - 支持黑名单检查和灰度升级设备检查。
```mermaid ```mermaid
classDiagram classDiagram
@@ -474,7 +476,6 @@ class OTATargetDevice {
+int ID +int ID
+int OTAID +int OTAID
+string MacAddr +string MacAddr
+int Type
+time CreateAt +time CreateAt
} }
class OTARepository { class OTARepository {
@@ -708,9 +709,10 @@ DeviceRepository --> UserActive : "返回"
- 支持通过 base64Resp 参数控制是否对 JSON 响应进行自定义 Base64 编码。 - 支持通过 base64Resp 参数控制是否对 JSON 响应进行自定义 Base64 编码。
- 自定义映射表将标准 Base64 字符集映射为更紧凑的字符集,减少体积。 - 自定义映射表将标准 Base64 字符集映射为更紧凑的字符集,减少体积。
- **OTA处理器特殊逻辑** - **OTA处理器特殊逻辑**
- 支持参数校验、黑名单检查、定向设备检查等复杂业务逻辑。 - 支持参数校验、黑名单检查、灰度升级设备检查等复杂业务逻辑。
- 根据不同情况返回不同的OTA升级信息。 - 根据不同情况返回不同的OTA升级信息。
- **更新**:OTA处理器现在处理简化的字段结构,移除了对paw*字段的处理。 - **更新**:OTA处理器现在处理简化的字段结构,移除了对paw*字段的处理。
- **更新**:OTA处理器现在使用灰度升级设备概念,不再区分白名单和黑名单类型。
- **设备处理器特殊逻辑** - **设备处理器特殊逻辑**
- 将设备信息写入Redis缓存,支持异步持久化。 - 将设备信息写入Redis缓存,支持异步持久化。
- 提供设备信息上报接口。 - 提供设备信息上报接口。
@@ -772,9 +774,10 @@ H-->>C : "200 OK + EQ数据"
- 扫描时将 sql.NullString 转换为指针字符串,避免零值歧义。 - 扫描时将 sql.NullString 转换为指针字符串,避免零值歧义。
- **OTA 查询** - **OTA 查询**
- 支持按model+hw+beta+status=1查询最新OTA记录。 - 支持按model+hw+beta+status=1查询最新OTA记录。
- 支持黑名单检查和定向设备检查。 - 支持黑名单检查和灰度升级设备检查。
- 提供多种查询策略以适应不同的升级场景。 - 提供多种查询策略以适应不同的升级场景。
- **更新**:OTA查询现在使用简化的字段列表,移除了对paw*字段的查询。 - **更新**:OTA查询现在使用简化的字段列表,移除了对paw*字段的查询。
- **更新**:OTA查询现在使用灰度升级设备概念,简化了设备匹配逻辑。
- **设备查询** - **设备查询**
- 支持按MAC地址查询设备信息。 - 支持按MAC地址查询设备信息。
- 支持按MAC地址和日期查询活跃记录。 - 支持按MAC地址和日期查询活跃记录。
@@ -908,6 +911,7 @@ Task_Share --> Redis
- share_code_log表对(mac_addr, share_code, create_at)建立索引,支持分享码查询。 - share_code_log表对(mac_addr, share_code, create_at)建立索引,支持分享码查询。
- 查询时优先按品牌名匹配,减少 LIKE 的范围。 - 查询时优先按品牌名匹配,减少 LIKE 的范围。
- **更新**:OTA查询现在使用简化的字段列表,减少了不必要的字段扫描。 - **更新**:OTA查询现在使用简化的字段列表,减少了不必要的字段扫描。
- **更新**:OTA查询现在使用灰度升级设备概念,简化了设备匹配逻辑,提高了查询性能。
- **扫描与内存** - **扫描与内存**
- 使用 sql.NullString 扫描可空列,避免零值歧义;转换为指针字符串减少冗余存储。 - 使用 sql.NullString 扫描可空列,避免零值歧义;转换为指针字符串减少冗余存储。
- OTA模型使用BoolInt类型处理布尔值,支持数据库整型与JSON布尔值的双向转换。 - OTA模型使用BoolInt类型处理布尔值,支持数据库整型与JSON布尔值的双向转换。
@@ -949,6 +953,7 @@ Task_Share --> Redis
- 分享码创建失败:检查Redis连接、分享码生成算法、TTL配置。 - 分享码创建失败:检查Redis连接、分享码生成算法、TTL配置。
- 分享码导入失败:检查导入日志队列、数据库连接、分布式锁状态。 - 分享码导入失败:检查导入日志队列、数据库连接、分布式锁状态。
- 目标曲线查询失败:检查目标曲线配置、CSV文件路径、S3访问权限。 - 目标曲线查询失败:检查目标曲线配置、CSV文件路径、S3访问权限。
- **更新**:OTA灰度升级设备查询失败:检查ota_target_device表结构,确认type字段已被移除。
- **排查步骤** - **排查步骤**
- 查看日志:处理器记录错误日志,统一响应体返回错误码。 - 查看日志:处理器记录错误日志,统一响应体返回错误码。
- 核对数据库:确认表结构与索引是否存在。 - 核对数据库:确认表结构与索引是否存在。
@@ -957,6 +962,7 @@ Task_Share --> Redis
- 监控任务:确认设备持久化任务和分享码持久化任务是否按预期执行。 - 监控任务:确认设备持久化任务和分享码持久化任务是否按预期执行。
- 检查分布式锁:确认分享码操作的分布式锁状态和超时处理。 - 检查分布式锁:确认分享码操作的分布式锁状态和超时处理。
- **更新**:检查OTA表结构,确认paw*字段不存在,验证简化的字段结构。 - **更新**:检查OTA表结构,确认paw*字段不存在,验证简化的字段结构。
- **更新**:检查ota_target_device表结构,确认type字段已被移除,验证灰度升级设备查询逻辑。
**章节来源** **章节来源**
- [internal/config/database.go:57-72](file://internal/config/database.go#L57-L72) - [internal/config/database.go:57-72](file://internal/config/database.go#L57-L72)
@@ -972,7 +978,7 @@ Task_Share --> Redis
## 结论 ## 结论
本项目的数据模型设计简洁明确:Brand 与 Model 分别承担品牌与型号的维度,通过仓库层的原生 SQL 访问实现高效查询;处理器层统一响应与可选编码,提升传输效率与前端体验。新增的分享码功能、目标曲线模型和增强的OTA模型进一步完善了系统的数据模型体系,支持复杂的固件升级策略、设备信息跟踪、用户行为分析和EQ数据分享功能。数据库层面通过唯一索引与合理字段设计保障一致性与性能。Redis缓存层提供了高性能的临时数据存储和分布式锁机制。 本项目的数据模型设计简洁明确:Brand 与 Model 分别承担品牌与型号的维度,通过仓库层的原生 SQL 访问实现高效查询;处理器层统一响应与可选编码,提升传输效率与前端体验。新增的分享码功能、目标曲线模型和增强的OTA模型进一步完善了系统的数据模型体系,支持复杂的固件升级策略、设备信息跟踪、用户行为分析和EQ数据分享功能。数据库层面通过唯一索引与合理字段设计保障一致性与性能。Redis缓存层提供了高性能的临时数据存储和分布式锁机制。
**更新**:OTA模型经过简化重构,移除了pawVerCode、pawVerName、pawUrl、pawMd5四个冗余字段,现在只包含核心升级信息,提高了数据模型的清晰度和维护性。新的字段结构更加简洁,减少了不必要的数据冗余,同时保持了完整的升级策略支持能力。 **更新**:OTA模型经过简化重构,移除了pawVerCode、pawVerName、pawUrl、pawMd5四个冗余字段,现在只包含核心升级信息,提高了数据模型的清晰度和维护性。同时,OTATargetDevice模型从"定向设备"概念重构为"灰度升级设备"概念,移除了Type字段,简化了设备管理逻辑,提高了系统的易用性和维护性。新的字段结构更加简洁,减少了不必要的数据冗余,同时保持了完整的升级策略支持能力。
建议在后续迭代中引入显式的校验与转换层,增强健壮性与可维护性,同时考虑添加更多的监控指标和告警机制。 建议在后续迭代中引入显式的校验与转换层,增强健壮性与可维护性,同时考虑添加更多的监控指标和告警机制。
@@ -990,8 +996,9 @@ Task_Share --> Redis
- 字段:id(主键)、ota_id、mac、create_at。 - 字段:id(主键)、ota_id、mac、create_at。
- 约束:无显式约束。 - 约束:无显式约束。
- **表:ota_target_device** - **表:ota_target_device**
- 字段:id(主键)、ota_id、mac_addr、type、create_at。 - 字段:id(主键)、ota_id、mac_addr、create_at。
- 约束:无显式约束。 - 约束:无显式约束。
- **更新**:移除了type字段,简化了灰度升级设备管理。
- **表:user_device** - **表:user_device**
- 字段:id(主键)、mac_addr、model、add_time、ver。 - 字段:id(主键)、mac_addr、model、add_time、ver。
- 约束:唯一索引 (mac_addr)。 - 约束:唯一索引 (mac_addr)。
@@ -1029,7 +1036,6 @@ Task_Share --> Redis
- OTATargetDevice.ID ↔ ota_target_device.id - OTATargetDevice.ID ↔ ota_target_device.id
- OTATargetDevice.OTAID ↔ ota_target_device.ota_id - OTATargetDevice.OTAID ↔ ota_target_device.ota_id
- OTATargetDevice.MacAddr ↔ ota_target_device.mac_addr - OTATargetDevice.MacAddr ↔ ota_target_device.mac_addr
- OTATargetDevice.Type ↔ ota_target_device.type
- OTATargetDevice.CreateAt ↔ ota_target_device.create_at - OTATargetDevice.CreateAt ↔ ota_target_device.create_at
- UserDevice.id ↔ user_device.id - UserDevice.id ↔ user_device.id
- UserDevice.mac_addr ↔ user_device.mac_addr - UserDevice.mac_addr ↔ user_device.mac_addr
@@ -1099,6 +1105,7 @@ Task_Share --> Redis
- 参数:model(必需)、hw(必需)、mac(可选)、beta(可选)、base64Resp(可选) - 参数:model(必需)、hw(必需)、mac(可选)、beta(可选)、base64Resp(可选)
- 返回:OTA对象或 Base64 编码后的 JSON - 返回:OTA对象或 Base64 编码后的 JSON
- **更新**:返回的OTA对象现在包含简化的字段结构 - **更新**:返回的OTA对象现在包含简化的字段结构
- **更新**:OTA查询现在使用灰度升级设备概念,简化了设备匹配逻辑
- 参考路径:[internal/handler/ota.go:23-145](file://internal/handler/ota.go#L23-L145) - 参考路径:[internal/handler/ota.go:23-145](file://internal/handler/ota.go#L23-L145)
- **设备信息上报** - **设备信息上报**
- 路由:/audio/reportDevInfo - 路由:/audio/reportDevInfo
@@ -1150,12 +1157,14 @@ Task_Share --> Redis
- OTA模型支持复杂的升级策略,建议在业务层添加更多的参数验证。 - OTA模型支持复杂的升级策略,建议在业务层添加更多的参数验证。
- 分享码模型支持多种设备型号,建议添加设备型号验证和限制。 - 分享码模型支持多种设备型号,建议添加设备型号验证和限制。
- **更新**:OTA模型简化后,减少了字段验证的复杂度,但仍需确保核心字段的完整性。 - **更新**:OTA模型简化后,减少了字段验证的复杂度,但仍需确保核心字段的完整性。
- **更新**OTATargetDevice模型简化后,灰度升级设备管理更加直观,减少了类型判断的复杂性。
- **版本管理与向后兼容** - **版本管理与向后兼容**
- 通过 API 版本号(如 /api/v1)隔离变更;新增字段采用可选策略,保持旧字段必填。 - 通过 API 版本号(如 /api/v1)隔离变更;新增字段采用可选策略,保持旧字段必填。
- 对于破坏性变更,提供迁移脚本与双写策略。 - 对于破坏性变更,提供迁移脚本与双写策略。
- OTA升级策略的变更需要谨慎处理,确保向后兼容性。 - OTA升级策略的变更需要谨慎处理,确保向后兼容性。
- 分享码功能的新增不影响现有API,保持向前兼容。 - 分享码功能的新增不影响现有API,保持向前兼容。
- **更新**:OTA模型的简化属于向后兼容的重构,不会影响现有API调用。 - **更新**:OTA模型的简化属于向后兼容的重构,不会影响现有API调用。
- **更新**OTATargetDevice模型的重构属于概念性重构,从"定向设备"到"灰度升级设备",保持了API的向后兼容性。
- **性能优化** - **性能优化**
- 为高频查询字段建立索引;避免 SELECT *,仅选择必要字段。 - 为高频查询字段建立索引;避免 SELECT *,仅选择必要字段。
- 对大列表启用可选 Base64 编码;结合分页与缓存策略。 - 对大列表启用可选 Base64 编码;结合分页与缓存策略。
@@ -1164,23 +1173,27 @@ Task_Share --> Redis
- 分享码使用Redis缓存,支持TTL自动过期和分布式锁。 - 分享码使用Redis缓存,支持TTL自动过期和分布式锁。
- 目标曲线使用Redis缓存,优化fr数据的独立存储。 - 目标曲线使用Redis缓存,优化fr数据的独立存储。
- **更新**:OTA查询现在使用简化的字段列表,减少了查询开销。 - **更新**:OTA查询现在使用简化的字段列表,减少了查询开销。
- **更新**:OTA查询现在使用灰度升级设备概念,简化了设备匹配逻辑,提高了查询性能。
- **错误处理与可观测性** - **错误处理与可观测性**
- 统一错误码与消息格式;记录关键链路日志;对数据库与外部服务增加超时与重试。 - 统一错误码与消息格式;记录关键链路日志;对数据库与外部服务增加超时与重试。
- OTA查询失败需要详细的日志记录,包括参数、查询结果等。 - OTA查询失败需要详细的日志记录,包括参数、查询结果等。
- 设备持久化任务需要监控执行状态和错误日志。 - 设备持久化任务需要监控执行状态和错误日志。
- 分享码持久化任务需要监控Redis队列状态和数据库插入结果。 - 分享码持久化任务需要监控Redis队列状态和数据库插入结果。
- 目标曲线查询需要监控S3访问权限和CSV文件读取。 - 目标曲线查询需要监控S3访问权限和CSV文件读取。
- **更新**:OTA灰度升级设备查询失败需要详细的日志记录,包括设备MAC地址和OTA ID。
- **安全考虑** - **安全考虑**
- OTA下载链接需要安全性验证,防止恶意下载。 - OTA下载链接需要安全性验证,防止恶意下载。
- 设备信息上报需要参数验证,防止注入攻击。 - 设备信息上报需要参数验证,防止注入攻击。
- 黑名单和白名单管理需要权限控制和审计日志。 - 黑名单和灰度升级设备管理需要权限控制和审计日志。
- 分享码功能需要防止暴力破解,建议添加频率限制。 - 分享码功能需要防止暴力破解,建议添加频率限制。
- 分享码删除操作需要MAC地址验证,防止越权操作。 - 分享码删除操作需要MAC地址验证,防止越权操作。
- EQ数据存储需要JSON格式验证,防止恶意数据注入。 - EQ数据存储需要JSON格式验证,防止恶意数据注入。
- **更新**:灰度升级设备管理需要MAC地址验证,防止越权访问。
- **监控与运维** - **监控与运维**
- 添加Redis连接池监控,确保缓存层稳定运行。 - 添加Redis连接池监控,确保缓存层稳定运行。
- 添加数据库连接池监控,避免连接泄漏。 - 添加数据库连接池监控,避免连接泄漏。
- 添加任务调度监控,确保持久化任务正常执行。 - 添加任务调度监控,确保持久化任务正常执行。
- 添加分布式锁监控,防止死锁和资源竞争。 - 添加分布式锁监控,防止死锁和资源竞争。
- 添加S3访问监控,确保CSV文件读取正常。 - 添加S3访问监控,确保CSV文件读取正常。
- **更新**:监控OTA表结构变化,确保简化的字段结构得到正确应用。 - **更新**:监控OTA表结构变化,确保简化的字段结构得到正确应用。
- **更新**:监控ota_target_device表结构变化,确保type字段已被移除,验证灰度升级设备查询逻辑。
File diff suppressed because one or more lines are too long
+4 -28
View File
@@ -8,7 +8,6 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"net/url"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@@ -54,8 +53,8 @@ func NewCurveHandler(repo *repository.CurveRepository, curveCache *cache.CurveCa
// //
// GET /audio/modelCurve?brand=xxx&name=xxx&base64Resp=true // GET /audio/modelCurve?brand=xxx&name=xxx&base64Resp=true
func (h *CurveHandler) ModelCurve(c *gin.Context) { func (h *CurveHandler) ModelCurve(c *gin.Context) {
brand := strings.TrimSpace(queryParam(c, "brand")) brand := strings.TrimSpace(c.Query("brand"))
name := strings.TrimSpace(queryParam(c, "name")) name := strings.TrimSpace(c.Query("name"))
base64Resp := encode.ParseBase64Param(c) base64Resp := encode.ParseBase64Param(c)
if brand == "" || name == "" { if brand == "" || name == "" {
@@ -143,9 +142,8 @@ func (h *CurveHandler) ModelCurve(c *gin.Context) {
// //
// GET /audio/getCurve?brand=xxx&name=xxx&target=xxx&base64Resp=true // GET /audio/getCurve?brand=xxx&name=xxx&target=xxx&base64Resp=true
func (h *CurveHandler) GetCurve(c *gin.Context) { func (h *CurveHandler) GetCurve(c *gin.Context) {
brand := strings.TrimSpace(queryParam(c, "brand")) brand := strings.TrimSpace(c.Query("brand"))
name := strings.TrimSpace(queryParam(c, "name")) name := strings.TrimSpace(c.Query("name"))
// target 使用标准 query 解码:+ 表示空格(如 Harman+over-ear+2018 → Harman over-ear 2018
target := strings.TrimSpace(c.Query("target")) target := strings.TrimSpace(c.Query("target"))
base64Resp := encode.ParseBase64Param(c) base64Resp := encode.ParseBase64Param(c)
@@ -557,25 +555,3 @@ func intVal(m map[string]any, key string, defaultVal int) int {
return defaultVal return defaultVal
} }
} }
// queryParam 从 URL 原始 query 中获取参数,保留 + 为字面量而非空格
func queryParam(c *gin.Context, key string) string {
vals, ok := c.Request.URL.Query()[key]
if !ok || len(vals) == 0 {
return ""
}
// c.Query() 会把 + 解码为空格,这里从原始 query 手动解码,+ 保留为 +
if strings.Contains(vals[0], " ") {
rawQuery := c.Request.URL.RawQuery
for _, pair := range strings.Split(rawQuery, "&") {
kv := strings.SplitN(pair, "=", 2)
if len(kv) == 2 && kv[0] == key {
decoded, err := url.PathUnescape(strings.ReplaceAll(kv[1], "+", "%2B"))
if err == nil {
return decoded
}
}
}
}
return vals[0]
}
+3 -3
View File
@@ -44,9 +44,9 @@ func NewModelCSVHandler(s3 *storage.S3Storage, log *zap.Logger) *ModelCSVHandler
// //
// GET /audio/getModelCSV?brand=Abyss&model=Dinan DZ&form=over-ear&base64Resp=true // GET /audio/getModelCSV?brand=Abyss&model=Dinan DZ&form=over-ear&base64Resp=true
func (h *ModelCSVHandler) GetModelCSV(c *gin.Context) { func (h *ModelCSVHandler) GetModelCSV(c *gin.Context) {
brand := strings.TrimSpace(queryParam(c, "brand")) brand := strings.TrimSpace(c.Query("brand"))
model := strings.TrimSpace(queryParam(c, "model")) model := strings.TrimSpace(c.Query("model"))
form := strings.TrimSpace(queryParam(c, "form")) form := strings.TrimSpace(c.Query("form"))
base64Resp := encode.ParseBase64Param(c) base64Resp := encode.ParseBase64Param(c)
if brand == "" || model == "" || form == "" { if brand == "" || model == "" || form == "" {