1179 lines
54 KiB
Markdown
1179 lines
54 KiB
Markdown
# 数据模型
|
||
|
||
<cite>
|
||
**本文档引用的文件**
|
||
- [internal/model/brand.go](file://internal/model/brand.go)
|
||
- [internal/model/model.go](file://internal/model/model.go)
|
||
- [internal/model/ota.go](file://internal/model/ota.go)
|
||
- [internal/model/share_code.go](file://internal/model/share_code.go)
|
||
- [internal/model/target.go](file://internal/model/target.go)
|
||
- [internal/model/user_device.go](file://internal/model/user_device.go)
|
||
- [internal/model/user_active.go](file://internal/model/user_active.go)
|
||
- [internal/repository/brand.go](file://internal/repository/brand.go)
|
||
- [internal/repository/model.go](file://internal/repository/model.go)
|
||
- [internal/repository/ota.go](file://internal/repository/ota.go)
|
||
- [internal/repository/share_code.go](file://internal/repository/share_code.go)
|
||
- [internal/repository/curve.go](file://internal/repository/curve.go)
|
||
- [internal/repository/device.go](file://internal/repository/device.go)
|
||
- [internal/handler/brand.go](file://internal/handler/brand.go)
|
||
- [internal/handler/model.go](file://internal/handler/model.go)
|
||
- [internal/handler/model_list.go](file://internal/handler/model_list.go)
|
||
- [internal/handler/ota.go](file://internal/handler/ota.go)
|
||
- [internal/handler/share_code.go](file://internal/handler/share_code.go)
|
||
- [internal/handler/device.go](file://internal/handler/device.go)
|
||
- [internal/handler/curve.go](file://internal/handler/curve.go)
|
||
- [internal/router/router.go](file://internal/router/router.go)
|
||
- [internal/config/database.go](file://internal/config/database.go)
|
||
- [internal/config/share_code_ttl.go](file://internal/config/share_code_ttl.go)
|
||
- [internal/response/response.go](file://internal/response/response.go)
|
||
- [internal/task/device_persist.go](file://internal/task/device_persist.go)
|
||
- [internal/task/share_code_persist.go](file://internal/task/share_code_persist.go)
|
||
- [pkg/encode/base64.go](file://pkg/encode/base64.go)
|
||
- [sql/model.sql](file://sql/model.sql)
|
||
- [sql/ota.sql](file://sql/ota.sql)
|
||
- [sql/share_code_log.sql](file://sql/share_code_log.sql)
|
||
- [sql/user_device.sql](file://sql/user_device.sql)
|
||
- [sql/user_active.sql](file://sql/user_active.sql)
|
||
- [sql/ota_target_device.sql](file://sql/ota_target_device.sql)
|
||
- [cmd/server/main.go](file://cmd/server/main.go)
|
||
</cite>
|
||
|
||
## 更新摘要
|
||
**变更内容**
|
||
- 新增分享代码数据模型(ShareCodeData)和相关处理流程
|
||
- 新增目标数据模型(Target),支持曲线目标配置
|
||
- 增强OTA模型,支持布尔值序列化和更多字段
|
||
- 新增分享码持久化任务和Redis缓存机制
|
||
- 扩展路由配置以支持分享码相关API端点
|
||
|
||
## 目录
|
||
1. [简介](#简介)
|
||
2. [项目结构](#项目结构)
|
||
3. [核心组件](#核心组件)
|
||
4. [架构总览](#架构总览)
|
||
5. [详细组件分析](#详细组件分析)
|
||
6. [依赖分析](#依赖分析)
|
||
7. [性能考虑](#性能考虑)
|
||
8. [故障排查指南](#故障排查指南)
|
||
9. [结论](#结论)
|
||
10. [附录](#附录)
|
||
|
||
## 简介
|
||
本文件面向 Luxsin 应用 API 的数据模型,系统性梳理 Brand、Model、OTA、ShareCode、Target、UserDevice 和 UserActive 七类数据模型的设计理念、字段定义、约束条件、业务含义以及与数据库表结构的映射关系。文档还覆盖 ORM 映射配置(基于原生 sql.DB 的扫描映射)、模型验证与序列化机制、模型在各层之间的传递方式与性能考量,并给出扩展、版本管理与向后兼容的最佳实践建议。内容兼顾初学者与高级开发者,既提供高层概览,也包含代码级的可视化图示与来源标注。
|
||
|
||
## 项目结构
|
||
该项目采用分层架构:路由层负责请求入口与中间件;处理器层处理业务逻辑与参数解析;仓库层封装数据库访问;模型层承载数据结构;响应层统一返回格式;编码层提供可选的响应体压缩编码;配置层加载数据库、Redis等外部服务配置。新增的分享码功能、目标曲线模型和增强的OTA模型进一步完善了系统的数据模型体系。
|
||
|
||
```mermaid
|
||
graph TB
|
||
subgraph "应用入口"
|
||
MAIN["cmd/server/main.go"]
|
||
end
|
||
subgraph "路由层"
|
||
ROUTER["internal/router/router.go"]
|
||
end
|
||
subgraph "处理器层"
|
||
BRAND_H["internal/handler/brand.go"]
|
||
MODEL_H["internal/handler/model.go"]
|
||
MODEL_LIST_H["internal/handler/model_list.go"]
|
||
OTA_H["internal/handler/ota.go"]
|
||
DEVICE_H["internal/handler/device.go"]
|
||
SHARE_CODE_H["internal/handler/share_code.go"]
|
||
CURVE_H["internal/handler/curve.go"]
|
||
end
|
||
subgraph "仓库层"
|
||
BRAND_R["internal/repository/brand.go"]
|
||
MODEL_R["internal/repository/model.go"]
|
||
OTA_R["internal/repository/ota.go"]
|
||
DEVICE_R["internal/repository/device.go"]
|
||
SHARE_CODE_R["internal/repository/share_code.go"]
|
||
CURVE_R["internal/repository/curve.go"]
|
||
end
|
||
subgraph "模型层"
|
||
MODEL_M["internal/model/brand.go"]
|
||
MODEL_M2["internal/model/model.go"]
|
||
MODEL_M3["internal/model/ota.go"]
|
||
MODEL_M4["internal/model/user_device.go"]
|
||
MODEL_M5["internal/model/user_active.go"]
|
||
MODEL_M6["internal/model/share_code.go"]
|
||
MODEL_M7["internal/model/target.go"]
|
||
end
|
||
subgraph "缓存层"
|
||
CACHE["internal/cache/*"]
|
||
end
|
||
subgraph "响应与编码"
|
||
RESP["internal/response/response.go"]
|
||
ENCODE["pkg/encode/base64.go"]
|
||
end
|
||
subgraph "配置与数据库"
|
||
DB_CFG["internal/config/database.go"]
|
||
SHARE_TTL["internal/config/share_code_ttl.go"]
|
||
DB_SQL["sql/*.sql"]
|
||
end
|
||
subgraph "任务处理"
|
||
DEVICE_TASK["internal/task/device_persist.go"]
|
||
SHARE_TASK["internal/task/share_code_persist.go"]
|
||
end
|
||
MAIN --> ROUTER
|
||
ROUTER --> BRAND_H
|
||
ROUTER --> MODEL_H
|
||
ROUTER --> MODEL_LIST_H
|
||
ROUTER --> OTA_H
|
||
ROUTER --> DEVICE_H
|
||
ROUTER --> SHARE_CODE_H
|
||
ROUTER --> CURVE_H
|
||
BRAND_H --> BRAND_R
|
||
MODEL_H --> MODEL_R
|
||
MODEL_LIST_H --> MODEL_R
|
||
OTA_H --> OTA_R
|
||
DEVICE_H --> DEVICE_R
|
||
SHARE_CODE_H --> CACHE
|
||
SHARE_CODE_H --> SHARE_CODE_R
|
||
CURVE_H --> CURVE_R
|
||
BRAND_R --> MODEL_M
|
||
MODEL_R --> MODEL_M2
|
||
OTA_R --> MODEL_M3
|
||
DEVICE_R --> MODEL_M4
|
||
DEVICE_R --> MODEL_M5
|
||
SHARE_CODE_R --> MODEL_M6
|
||
CURVE_R --> MODEL_M7
|
||
BRAND_H --> RESP
|
||
MODEL_H --> RESP
|
||
MODEL_LIST_H --> RESP
|
||
OTA_H --> RESP
|
||
DEVICE_H --> RESP
|
||
SHARE_CODE_H --> RESP
|
||
CURVE_H --> RESP
|
||
DEVICE_H --> ENCODE
|
||
SHARE_CODE_H --> ENCODE
|
||
SHARE_TASK --> SHARE_CODE_R
|
||
DEVICE_TASK --> DEVICE_R
|
||
DB_CFG --> DB_SQL
|
||
CACHE --> SHARE_TTL
|
||
```
|
||
|
||
**图表来源**
|
||
- [cmd/server/main.go:22-64](file://cmd/server/main.go#L22-L64)
|
||
- [internal/router/router.go:14-82](file://internal/router/router.go#L14-L82)
|
||
- [internal/handler/brand.go:19-50](file://internal/handler/brand.go#L19-L50)
|
||
- [internal/handler/model.go:19-51](file://internal/handler/model.go#L19-L51)
|
||
- [internal/handler/model_list.go:19-57](file://internal/handler/model_list.go#L19-L57)
|
||
- [internal/handler/ota.go:14-133](file://internal/handler/ota.go#L14-L133)
|
||
- [internal/handler/device.go:15-83](file://internal/handler/device.go#L15-L83)
|
||
- [internal/handler/share_code.go:1-383](file://internal/handler/share_code.go#L1-L383)
|
||
- [internal/handler/curve.go:1-582](file://internal/handler/curve.go#L1-L582)
|
||
- [internal/repository/brand.go:16-51](file://internal/repository/brand.go#L16-L51)
|
||
- [internal/repository/model.go:16-95](file://internal/repository/model.go#L16-L95)
|
||
- [internal/repository/ota.go:11-159](file://internal/repository/ota.go#L11-L159)
|
||
- [internal/repository/device.go:11-88](file://internal/repository/device.go#L11-L88)
|
||
- [internal/repository/share_code.go:1-59](file://internal/repository/share_code.go#L1-L59)
|
||
- [internal/repository/curve.go:1-66](file://internal/repository/curve.go#L1-L66)
|
||
- [internal/model/brand.go:3-7](file://internal/model/brand.go#L3-L7)
|
||
- [internal/model/model.go:5-15](file://internal/model/model.go#L5-L15)
|
||
- [internal/model/ota.go:5-67](file://internal/model/ota.go#L5-L67)
|
||
- [internal/model/user_device.go:5-12](file://internal/model/user_device.go#L5-L12)
|
||
- [internal/model/user_active.go:5-13](file://internal/model/user_active.go#L5-L13)
|
||
- [internal/model/share_code.go:14-25](file://internal/model/share_code.go#L14-L25)
|
||
- [internal/model/target.go:5-14](file://internal/model/target.go#L5-L14)
|
||
- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37)
|
||
- [pkg/encode/base64.go:21-52](file://pkg/encode/base64.go#L21-L52)
|
||
- [internal/config/database.go:17-72](file://internal/config/database.go#L17-L72)
|
||
- [internal/config/share_code_ttl.go:12-60](file://internal/config/share_code_ttl.go#L12-L60)
|
||
- [sql/model.sql:20-38](file://sql/model.sql#L20-L38)
|
||
- [sql/ota.sql:23-44](file://sql/ota.sql#L23-L44)
|
||
- [sql/share_code_log.sql:8-23](file://sql/share_code_log.sql#L8-L23)
|
||
- [sql/user_device.sql:23-32](file://sql/user_device.sql#L23-L32)
|
||
- [sql/user_active.sql:23-32](file://sql/user_active.sql#L23-L32)
|
||
- [sql/ota_target_device.sql:23-31](file://sql/ota_target_device.sql#L23-L31)
|
||
- [internal/task/device_persist.go:14-167](file://internal/task/device_persist.go#L14-L167)
|
||
- [internal/task/share_code_persist.go:1-232](file://internal/task/share_code_persist.go#L1-L232)
|
||
|
||
**章节来源**
|
||
- [cmd/server/main.go:22-64](file://cmd/server/main.go#L22-L64)
|
||
- [internal/router/router.go:14-82](file://internal/router/router.go#L14-L82)
|
||
|
||
## 核心组件
|
||
- **传统模型层(Model)**
|
||
- Brand:承载品牌标识与名称,用于品牌列表查询。
|
||
- Model:承载型号信息,包含品牌名、型号名、形态、设备类型、来源、等价键、创建时间等。
|
||
- **OTA固件升级模型层**
|
||
- OTA:固件升级记录,包含版本信息、下载地址、MD5校验、强制升级标志、灰度发布状态、定向升级配置等。
|
||
- BlackList:OTA黑名单,记录被禁止升级的设备MAC地址。
|
||
- OTATargetDevice:OTA定向设备,记录允许特定设备升级的白名单或黑名单。
|
||
- **分享码模型层**
|
||
- ShareCodeLog:分享码操作日志,记录导出/导入操作及EQ数据快照。
|
||
- ShareCodeData:分享码缓存数据,包含分享码、MAC地址、设备型号、EQ数据、过期时间等。
|
||
- **目标曲线模型层**
|
||
- Target:目标曲线配置,包含标签、CSV读取标志、文件路径、贝斯增强配置等。
|
||
- **用户设备跟踪模型层**
|
||
- UserDevice:用户设备信息,包含设备MAC地址、型号、添加时间、当前版本等。
|
||
- UserActive:用户活动记录,包含设备活跃信息、IP地址、活跃日期等。
|
||
- **仓库层(Repository)**
|
||
- BrandRepository:提供按品牌名模糊查询的品牌列表。
|
||
- ModelRepository:提供按品牌名或型号名模糊查询的型号列表,并处理可空字段的扫描。
|
||
- OTARepository:提供OTA升级信息查询、黑名单检查、定向设备查询等功能。
|
||
- DeviceRepository:提供设备信息查询、插入、更新以及活动记录管理。
|
||
- ShareCodeRepository:提供分享码日志插入、导出记录检查等功能。
|
||
- CurveRepository:提供型号和目标曲线查询功能。
|
||
- **处理器层(Handler)**
|
||
- BrandHandler:接收查询参数,调用仓库层,支持可选的响应体 Base64 编码。
|
||
- ModelHandler:接收品牌与型号查询参数,调用仓库层,支持可选的响应体 Base64 编码。
|
||
- ModelListHandler:通过搜索客户端返回模型列表,支持可选的响应体 Base64 编码。
|
||
- OTAHandler:获取OTA升级信息,支持黑名单检查、定向升级检查等功能。
|
||
- DeviceHandler:处理设备信息上报,将设备活动信息写入Redis缓存。
|
||
- ShareCodeHandler:处理分享码创建、查询、导入、删除等操作,支持Redis缓存和数据库持久化。
|
||
- CurveHandler:处理曲线数据获取,支持目标曲线配置和EQ参数化。
|
||
- **任务处理层**
|
||
- DevicePersistTask:定时从Redis缓存批量持久化设备信息到数据库。
|
||
- ShareCodePersistTask:定时从Redis缓存批量持久化分享码日志到数据库。
|
||
|
||
**章节来源**
|
||
- [internal/model/brand.go:3-7](file://internal/model/brand.go#L3-L7)
|
||
- [internal/model/model.go:5-15](file://internal/model/model.go#L5-L15)
|
||
- [internal/model/ota.go:5-67](file://internal/model/ota.go#L5-L67)
|
||
- [internal/model/share_code.go:14-25](file://internal/model/share_code.go#L14-L25)
|
||
- [internal/model/target.go:5-14](file://internal/model/target.go#L5-L14)
|
||
- [internal/model/user_device.go:5-12](file://internal/model/user_device.go#L5-L12)
|
||
- [internal/model/user_active.go:5-13](file://internal/model/user_active.go#L5-L13)
|
||
- [internal/repository/brand.go:20-51](file://internal/repository/brand.go#L20-L51)
|
||
- [internal/repository/model.go:20-95](file://internal/repository/model.go#L20-L95)
|
||
- [internal/repository/ota.go:19-159](file://internal/repository/ota.go#L19-L159)
|
||
- [internal/repository/device.go:19-88](file://internal/repository/device.go#L19-L88)
|
||
- [internal/repository/share_code.go:12-59](file://internal/repository/share_code.go#L12-L59)
|
||
- [internal/repository/curve.go:11-66](file://internal/repository/curve.go#L11-L66)
|
||
- [internal/handler/brand.go:26-50](file://internal/handler/brand.go#L26-L50)
|
||
- [internal/handler/model.go:26-51](file://internal/handler/model.go#L26-L51)
|
||
- [internal/handler/model_list.go:26-57](file://internal/handler/model_list.go#L26-L57)
|
||
- [internal/handler/ota.go:23-133](file://internal/handler/ota.go#L23-L133)
|
||
- [internal/handler/device.go:27-83](file://internal/handler/device.go#L27-L83)
|
||
- [internal/handler/share_code.go:15-383](file://internal/handler/share_code.go#L15-L383)
|
||
- [internal/handler/curve.go:27-582](file://internal/handler/curve.go#L27-L582)
|
||
- [internal/task/device_persist.go:24-167](file://internal/task/device_persist.go#L24-L167)
|
||
- [internal/task/share_code_persist.go:14-232](file://internal/task/share_code_persist.go#L14-L232)
|
||
|
||
## 架构总览
|
||
下图展示了从请求到响应的关键路径,包括处理器、仓库、数据库与可选编码流程,以及新增的分享码功能、目标曲线配置和增强的OTA固件升级功能。
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant C as "客户端"
|
||
participant R as "路由层"
|
||
participant H as "处理器层"
|
||
participant Repo as "仓库层"
|
||
participant Cache as "缓存层"
|
||
participant DB as "数据库"
|
||
participant Enc as "编码层"
|
||
C->>R : "POST /audio/shareCreate"
|
||
R->>H : "ShareCodeHandler.ExportShareCode"
|
||
H->>Cache : "Create(分享码, EQ数据)"
|
||
Cache-->>H : "ShareCodeData"
|
||
H->>Repo : "InsertLog(分享码日志)"
|
||
Repo->>DB : "插入分享码日志"
|
||
DB-->>Repo : "确认插入"
|
||
Repo-->>H : "确认"
|
||
H-->>C : "200 OK + 分享码信息"
|
||
C->>R : "GET /audio/shareQuery?shareCode=ABCDE"
|
||
R->>H : "ShareCodeHandler.QueryShareCode"
|
||
H->>Cache : "Get(分享码)"
|
||
Cache-->>H : "ShareCodeData"
|
||
H-->>C : "200 OK + EQ数据"
|
||
C->>R : "GET /audio/shareAccept?mac=xx&model=Luxsin-X9&shareCode=ABCDE"
|
||
R->>H : "ShareCodeHandler.ImportShareCode"
|
||
H->>Cache : "EnqueueImportLog(导入日志)"
|
||
Cache-->>H : "排队成功"
|
||
H-->>C : "200 OK + EQ数据"
|
||
C->>R : "GET /audio/getCurve?brand=xxx&name=xxx&target=xxx"
|
||
R->>H : "CurveHandler.GetCurve"
|
||
H->>Repo : "GetTargetByLabel(目标曲线)"
|
||
Repo->>DB : "查询目标曲线"
|
||
DB-->>Repo : "Target配置"
|
||
Repo-->>H : "Target配置"
|
||
H->>Cache : "GetWithFR(缓存)"
|
||
Cache-->>H : "曲线数据"
|
||
H-->>C : "200 OK + parametric_eq"
|
||
```
|
||
|
||
**图表来源**
|
||
- [internal/router/router.go:73-77](file://internal/router/router.go#L73-L77)
|
||
- [internal/handler/share_code.go:40-125](file://internal/handler/share_code.go#L40-L125)
|
||
- [internal/handler/share_code.go:192-211](file://internal/handler/share_code.go#L192-L211)
|
||
- [internal/handler/share_code.go:228-279](file://internal/handler/share_code.go#L228-L279)
|
||
- [internal/handler/curve.go:145-198](file://internal/handler/curve.go#L145-L198)
|
||
- [internal/repository/curve.go:44-66](file://internal/repository/curve.go#L44-L66)
|
||
- [internal/cache/curve_cache.go:27-102](file://internal/cache/curve_cache.go#L27-L102)
|
||
|
||
## 详细组件分析
|
||
|
||
### Brand 数据模型
|
||
- **设计理念**
|
||
- 轻量级品牌实体,仅包含标识与名称,用于品牌筛选与列表展示。
|
||
- **字段定义与约束**
|
||
- id:整数,主键,自增。
|
||
- name:字符串,非空,用于品牌名匹配与排序。
|
||
- **业务含义**
|
||
- 作为 Model 的上游维度,配合 Model 的 brandName 字段形成关联。
|
||
- **数据库映射**
|
||
- 表名:brand(未在本文直接列出,但与 Model 的 brand_name 关联一致)。
|
||
- 字段:id、name。
|
||
- **ORM 映射配置**
|
||
- 使用原生 sql.DB 扫描,通过结构体标签与 Scan 对齐。
|
||
- **验证与序列化**
|
||
- 无显式校验逻辑,依赖数据库约束与上层参数清洗。
|
||
- JSON 标签用于序列化输出。
|
||
- **查询流程**
|
||
- 支持按品牌名模糊查询,使用 LIKE 匹配并按名称升序排列。
|
||
- 返回 []Brand 列表。
|
||
|
||
```mermaid
|
||
classDiagram
|
||
class Brand {
|
||
+int id
|
||
+string name
|
||
}
|
||
class BrandRepository {
|
||
-db *sql.DB
|
||
+List(ctx, brandName) []Brand
|
||
}
|
||
BrandRepository --> Brand : "返回"
|
||
```
|
||
|
||
**图表来源**
|
||
- [internal/model/brand.go:3-7](file://internal/model/brand.go#L3-L7)
|
||
- [internal/repository/brand.go:12-51](file://internal/repository/brand.go#L12-L51)
|
||
|
||
**章节来源**
|
||
- [internal/model/brand.go:3-7](file://internal/model/brand.go#L3-L7)
|
||
- [internal/repository/brand.go:20-51](file://internal/repository/brand.go#L20-L51)
|
||
|
||
### Model 数据模型
|
||
- **设计理念**
|
||
- 型号实体承载品牌名、型号名及可选属性(形态、设备类型、来源、等价键),并记录创建时间。
|
||
- **字段定义与约束**
|
||
- id:整数,主键,自增。
|
||
- brandName:字符串,非空,与品牌维度关联。
|
||
- name:字符串,非空,型号名。
|
||
- form、rig、source、eqKey:字符串(指针),可空,表示形态、设备类型、来源、等价键。
|
||
- createAt:时间戳,非空,记录创建时间。
|
||
- **业务含义**
|
||
- 作为核心产品维度,支持按品牌或型号名检索,可为空的扩展属性满足多样化设备描述。
|
||
- **数据库映射**
|
||
- 表名:model。
|
||
- 字段:id、brand_name、name、form、rig、source、eq_key、create_at。
|
||
- 约束:唯一索引 (brand_name, name),保证同品牌下型号名唯一。
|
||
- **ORM 映射配置**
|
||
- 使用 sql.NullString 扫描可空列,再转换为指针字符串,避免零值歧义。
|
||
- JSON 标签用于序列化输出,可空字段支持 omitempty。
|
||
- **验证与序列化**
|
||
- 无显式校验逻辑,依赖数据库约束与上层参数清洗。
|
||
- JSON 标签用于序列化输出。
|
||
- **查询流程**
|
||
- 支持按品牌名精确匹配或按型号名模糊匹配,返回 []Model 列表。
|
||
- 默认返回空切片而非 nil,便于前端处理。
|
||
|
||
```mermaid
|
||
classDiagram
|
||
class Model {
|
||
+int id
|
||
+string brandName
|
||
+string name
|
||
+*string form
|
||
+*string rig
|
||
+*string source
|
||
+*string eqKey
|
||
+time createAt
|
||
}
|
||
class ModelRepository {
|
||
-db *sql.DB
|
||
+List(ctx, brandName, modelName) []Model
|
||
}
|
||
ModelRepository --> Model : "返回"
|
||
```
|
||
|
||
**图表来源**
|
||
- [internal/model/model.go:5-15](file://internal/model/model.go#L5-L15)
|
||
- [internal/repository/model.go:12-95](file://internal/repository/model.go#L12-L95)
|
||
- [sql/model.sql:24-35](file://sql/model.sql#L24-L35)
|
||
|
||
**章节来源**
|
||
- [internal/model/model.go:5-15](file://internal/model/model.go#L5-L15)
|
||
- [internal/repository/model.go:20-95](file://internal/repository/model.go#L20-L95)
|
||
- [sql/model.sql:24-35](file://sql/model.sql#L24-L35)
|
||
|
||
### OTA 固件升级模型
|
||
- **设计理念**
|
||
- OTA模型用于管理固件升级信息,支持强制升级、灰度发布、定向升级等功能。
|
||
- **字段定义与约束**
|
||
- OTA模型:
|
||
- id:整数,主键,自增。
|
||
- verCode:整数,版本号,用于排序。
|
||
- verName:字符串,版本名称。
|
||
- url:字符串,固件下载地址。
|
||
- md5:字符串,固件MD5校验值。
|
||
- force:BoolInt,是否强制升级(true/false)。
|
||
- desc:字符串指针,升级描述。
|
||
- model:字符串指针,对应设备型号。
|
||
- hw:整数,硬件版本号。
|
||
- target:BoolInt,是否定向升级(true/false)。
|
||
- beta:BoolInt,是否灰度发布(true/false)。
|
||
- pawVerCode:整数,配套软件版本号。
|
||
- pawVerName:字符串,配套软件版本名称。
|
||
- pawUrl:字符串,配套软件下载地址。
|
||
- pawMD5:字符串,配套软件MD5校验值。
|
||
- startTime:时间戳指针,升级开始时间。
|
||
- endTime:时间戳指针,升级结束时间。
|
||
- status:整数,状态(1-可用,0-不可用)。
|
||
- BlackList模型:
|
||
- id:整数,主键,自增。
|
||
- ota_id:整数,关联OTA记录ID。
|
||
- mac:字符串,设备MAC地址。
|
||
- create_at:时间戳,创建时间。
|
||
- OTATargetDevice模型:
|
||
- id:整数,主键,自增。
|
||
- ota_id:整数,关联OTA记录ID。
|
||
- mac_addr:字符串,设备MAC地址。
|
||
- type:整数,类型(1-白名单,2-黑名单)。
|
||
- create_at:时间戳,创建时间。
|
||
- **业务含义**
|
||
- 支持复杂的固件升级策略,包括强制升级、灰度发布、定向升级和黑名单管理。
|
||
- **数据库映射**
|
||
- 表名:ota、black_list、ota_target_device。
|
||
- 字段:完整映射关系详见数据库表结构。
|
||
- **ORM 映射配置**
|
||
- 使用 sql.NullString 和 sql.NullTime 扫描可空列。
|
||
- BoolInt类型支持数据库整型与JSON布尔值的双向转换。
|
||
- JSON 标签用于序列化输出,可空字段支持 omitempty。
|
||
- **查询流程**
|
||
- 支持多种查询策略:最新OTA记录、非黑名单OTA记录、非定向OTA记录等。
|
||
- 支持黑名单检查和定向设备检查。
|
||
|
||
```mermaid
|
||
classDiagram
|
||
class OTA {
|
||
+int id
|
||
+int verCode
|
||
+string verName
|
||
+string url
|
||
+string md5
|
||
+BoolInt force
|
||
+*string desc
|
||
+*string model
|
||
+int hw
|
||
+BoolInt target
|
||
+BoolInt beta
|
||
+int pawVerCode
|
||
+string pawVerName
|
||
+string pawUrl
|
||
+string pawMD5
|
||
+*time startTime
|
||
+*time endTime
|
||
+int status
|
||
}
|
||
class BlackList {
|
||
+int id
|
||
+int ota_id
|
||
+string mac
|
||
+time create_at
|
||
}
|
||
class OTATargetDevice {
|
||
+int id
|
||
+int ota_id
|
||
+string mac_addr
|
||
+int type
|
||
+time create_at
|
||
}
|
||
class OTARepository {
|
||
-db *sql.DB
|
||
+GetLatestOTA(ctx, model, hw, beta) *OTA
|
||
+GetLatestOTANotInBlackList(ctx, model, hw, beta, mac) *OTA
|
||
+GetLatestOTANotTarget(ctx, model, hw, beta) *OTA
|
||
+IsInBlackList(ctx, otaID, mac) bool
|
||
+FindTargetDevice(ctx, otaID, mac) *OTATargetDevice
|
||
}
|
||
OTARepository --> OTA : "返回"
|
||
OTARepository --> BlackList : "返回"
|
||
OTARepository --> OTATargetDevice : "返回"
|
||
```
|
||
|
||
**图表来源**
|
||
- [internal/model/ota.go:29-67](file://internal/model/ota.go#L29-L67)
|
||
- [internal/repository/ota.go:19-159](file://internal/repository/ota.go#L19-L159)
|
||
- [sql/ota.sql:24-44](file://sql/ota.sql#L24-L44)
|
||
- [sql/ota_target_device.sql:24-31](file://sql/ota_target_device.sql#L24-L31)
|
||
|
||
**章节来源**
|
||
- [internal/model/ota.go:5-67](file://internal/model/ota.go#L5-L67)
|
||
- [internal/repository/ota.go:19-159](file://internal/repository/ota.go#L19-L159)
|
||
- [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)
|
||
|
||
### 分享码数据模型
|
||
- **设计理念**
|
||
- ShareCodeLog用于记录分享码的操作日志,支持导出和导入两种操作类型。
|
||
- ShareCodeData用于Redis缓存中的分享码数据,包含完整的分享码信息和缓存状态。
|
||
- **字段定义与约束**
|
||
- ShareCodeLog模型:
|
||
- ID:整数,主键,自增。
|
||
- MacAddr:字符串,设备MAC地址。
|
||
- ShareCode:字符串,5位分享码。
|
||
- Action:枚举,操作类型(export/import)。
|
||
- Model:枚举,设备型号(Luxsin-X9/Luxsin-X8)。
|
||
- IpAddr:字符串,用户IP地址。
|
||
- EqData:JSON,EQ数据快照。
|
||
- ExpireAt:时间戳指针,分享码到期时间。
|
||
- CreateAt:时间戳,创建时间。
|
||
- ShareCodeData结构:
|
||
- ShareCode:字符串,5位分享码。
|
||
- MacAddr:字符串,创建者MAC地址。
|
||
- IpAddr:字符串,创建者IP地址。
|
||
- Model:字符串,设备型号。
|
||
- EqData:字符串,EQ参数JSON。
|
||
- ExpireAt:时间戳,过期时间。
|
||
- Persisted:布尔值,是否已刷入数据库。
|
||
- **业务含义**
|
||
- 支持EQ数据的分享与导入功能,提供完整的操作审计和数据快照。
|
||
- **数据库映射**
|
||
- 表名:share_code_log。
|
||
- 字段:完整映射关系详见数据库表结构。
|
||
- 索引:对mac_addr、share_code、create_at建立索引。
|
||
- **ORM 映射配置**
|
||
- 使用 sql.NullString 和 sql.NullTime 扫描可空列。
|
||
- JSON 标签用于序列化输出,支持复杂JSON数据存储。
|
||
- **查询流程**
|
||
- 支持按分享码查询导出记录存在性检查。
|
||
- 支持分享码日志的插入和查询。
|
||
|
||
```mermaid
|
||
classDiagram
|
||
class ShareCodeLog {
|
||
+int ID
|
||
+string MacAddr
|
||
+string ShareCode
|
||
+string Action
|
||
+string Model
|
||
+string IpAddr
|
||
+[]byte EqData
|
||
+*time ExpireAt
|
||
+time CreateAt
|
||
}
|
||
class ShareCodeData {
|
||
+string ShareCode
|
||
+string MacAddr
|
||
+string IpAddr
|
||
+string Model
|
||
+string EqData
|
||
+time ExpireAt
|
||
+bool Persisted
|
||
}
|
||
class ShareCodeRepository {
|
||
-db *sql.DB
|
||
+InsertLog(ctx, log) error
|
||
+HasExportLog(ctx, shareCode) bool
|
||
}
|
||
ShareCodeRepository --> ShareCodeLog : "返回"
|
||
```
|
||
|
||
**图表来源**
|
||
- [internal/model/share_code.go:14-25](file://internal/model/share_code.go#L14-L25)
|
||
- [internal/repository/share_code.go:20-59](file://internal/repository/share_code.go#L20-L59)
|
||
- [sql/share_code_log.sql:9-23](file://sql/share_code_log.sql#L9-L23)
|
||
|
||
**章节来源**
|
||
- [internal/model/share_code.go:14-25](file://internal/model/share_code.go#L14-L25)
|
||
- [internal/repository/share_code.go:12-59](file://internal/repository/share_code.go#L12-L59)
|
||
- [sql/share_code_log.sql:8-23](file://sql/share_code_log.sql#L8-L23)
|
||
|
||
### 目标曲线数据模型
|
||
- **设计理念**
|
||
- Target模型用于定义目标曲线配置,支持从CSV文件读取目标数据和贝斯增强配置。
|
||
- **字段定义与约束**
|
||
- ID:整数,主键,自增。
|
||
- Label:字符串,目标曲线标签。
|
||
- ReadCSV:BoolInt,是否从CSV文件读取目标数据。
|
||
- File:字符串指针,CSV文件路径。
|
||
- BassBoost:字符串指针,贝斯增强配置JSON。
|
||
- AddTime:时间戳,添加时间。
|
||
- **业务含义**
|
||
- 支持灵活的目标曲线配置,可以使用预定义的曲线名称或从CSV文件读取自定义曲线。
|
||
- **数据库映射**
|
||
- 表名:target。
|
||
- 字段:完整映射关系详见数据库表结构。
|
||
- **ORM 映射配置**
|
||
- 使用 BoolInt 类型支持布尔值序列化。
|
||
- JSON 标签用于序列化输出,可空字段支持 omitempty。
|
||
- **查询流程**
|
||
- 支持按标签查询目标曲线配置。
|
||
- 支持CSV文件路径和贝斯增强配置的动态加载。
|
||
|
||
```mermaid
|
||
classDiagram
|
||
class Target {
|
||
+int ID
|
||
+string Label
|
||
+BoolInt ReadCSV
|
||
+*string File
|
||
+*string BassBoost
|
||
+time AddTime
|
||
}
|
||
class CurveRepository {
|
||
-db *sql.DB
|
||
+GetTargetByLabel(ctx, label) *Target
|
||
}
|
||
CurveRepository --> Target : "返回"
|
||
```
|
||
|
||
**图表来源**
|
||
- [internal/model/target.go:5-14](file://internal/model/target.go#L5-L14)
|
||
- [internal/repository/curve.go:44-66](file://internal/repository/curve.go#L44-L66)
|
||
|
||
**章节来源**
|
||
- [internal/model/target.go:5-14](file://internal/model/target.go#L5-L14)
|
||
- [internal/repository/curve.go:44-66](file://internal/repository/curve.go#L44-L66)
|
||
|
||
### 用户设备跟踪模型
|
||
- **设计理念**
|
||
- UserDevice模型用于跟踪用户设备信息,支持设备注册、版本更新等功能。
|
||
- UserActive模型用于记录用户设备的活跃信息,支持按日期分区存储。
|
||
- **字段定义与约束**
|
||
- UserDevice模型:
|
||
- id:整数,主键,自增。
|
||
- mac_addr:字符串,设备MAC地址,唯一索引。
|
||
- model:字符串,设备型号。
|
||
- add_time:时间戳,默认当前时间。
|
||
- ver:字符串指针,设备当前版本。
|
||
- UserActive模型:
|
||
- id:整数,主键。
|
||
- mac_addr:字符串,设备MAC地址。
|
||
- model:字符串,设备型号。
|
||
- active_date:日期,活跃日期,联合主键。
|
||
- ip_addr:字符串,设备IP地址。
|
||
- create_at:时间戳,默认当前时间。
|
||
- **业务含义**
|
||
- 支持设备信息的实时上报和历史活动记录追踪。
|
||
- **数据库映射**
|
||
- 表名:user_device、user_active。
|
||
- 字段:完整映射关系详见数据库表结构。
|
||
- 索引:user_device的mac_addr唯一索引,user_active的(mac_addr, active_date)联合索引。
|
||
- **ORM 映射配置**
|
||
- 使用 sql.NullString 扫描可空列。
|
||
- JSON 标签用于序列化输出。
|
||
- **查询流程**
|
||
- 支持按MAC地址查询设备信息。
|
||
- 支持按MAC地址和日期查询活跃记录。
|
||
- 支持设备信息的插入、更新和活跃记录的插入、更新。
|
||
|
||
```mermaid
|
||
classDiagram
|
||
class UserDevice {
|
||
+int id
|
||
+string mac_addr
|
||
+string model
|
||
+time add_time
|
||
+*string ver
|
||
}
|
||
class UserActive {
|
||
+int id
|
||
+string mac_addr
|
||
+string model
|
||
+date active_date
|
||
+string ip_addr
|
||
+time create_at
|
||
}
|
||
class DeviceRepository {
|
||
-db *sql.DB
|
||
+FindDeviceByMac(ctx, macAddr) *UserDevice
|
||
+InsertDevice(ctx, device) error
|
||
+UpdateDeviceVer(ctx, id, ver) error
|
||
+FindActiveByMacAndDate(ctx, macAddr, activeDate) *UserActive
|
||
+InsertActive(ctx, active) error
|
||
+UpdateActiveIp(ctx, id, ipAddr) error
|
||
}
|
||
DeviceRepository --> UserDevice : "返回"
|
||
DeviceRepository --> UserActive : "返回"
|
||
```
|
||
|
||
**图表来源**
|
||
- [internal/model/user_device.go:5-12](file://internal/model/user_device.go#L5-L12)
|
||
- [internal/model/user_active.go:5-13](file://internal/model/user_active.go#L5-L13)
|
||
- [internal/repository/device.go:19-88](file://internal/repository/device.go#L19-L88)
|
||
- [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)
|
||
|
||
**章节来源**
|
||
- [internal/model/user_device.go:5-12](file://internal/model/user_device.go#L5-L12)
|
||
- [internal/model/user_active.go:5-13](file://internal/model/user_active.go#L5-L13)
|
||
- [internal/repository/device.go:19-88](file://internal/repository/device.go#L19-L88)
|
||
- [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)
|
||
|
||
### 处理器与序列化机制
|
||
- **统一响应体**
|
||
- 统一响应体包含 code、message、data 字段,便于前端统一处理。
|
||
- **可选 Base64 编码**
|
||
- 支持通过 base64Resp 参数控制是否对 JSON 响应进行自定义 Base64 编码。
|
||
- 自定义映射表将标准 Base64 字符集映射为更紧凑的字符集,减少体积。
|
||
- **OTA处理器特殊逻辑**
|
||
- 支持参数校验、黑名单检查、定向设备检查等复杂业务逻辑。
|
||
- 根据不同情况返回不同的OTA升级信息。
|
||
- **设备处理器特殊逻辑**
|
||
- 将设备信息写入Redis缓存,支持异步持久化。
|
||
- 提供设备信息上报接口。
|
||
- **分享码处理器特殊逻辑**
|
||
- 支持分享码创建、查询、导入、删除等完整生命周期管理。
|
||
- 使用Redis缓存提高性能,支持分布式锁保证数据一致性。
|
||
- 提供分享码数量限制和过期时间控制。
|
||
- **曲线处理器特殊逻辑**
|
||
- 支持目标曲线配置查询和EQ参数化计算。
|
||
- 使用Redis缓存优化曲线数据访问性能。
|
||
- **错误处理**
|
||
- 处理器捕获仓库层错误,记录日志并通过统一响应体返回内部错误。
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant C as "客户端"
|
||
participant H as "ShareCodeHandler"
|
||
participant Cache as "ShareCodeCache"
|
||
participant Repo as "ShareCodeRepository"
|
||
participant DB as "数据库"
|
||
participant Enc as "编码层"
|
||
C->>H : "POST /audio/shareCreate"
|
||
H->>Cache : "Create(mac, model, eqData)"
|
||
Cache-->>H : "ShareCodeData"
|
||
H->>Repo : "InsertLog(export)"
|
||
Repo->>DB : "插入日志"
|
||
DB-->>Repo : "确认"
|
||
Repo-->>H : "确认"
|
||
H-->>C : "200 OK + 分享码信息"
|
||
C->>H : "GET /audio/shareAccept?shareCode=ABCDE"
|
||
H->>Cache : "EnqueueImportLog(mac, model, eqData)"
|
||
Cache-->>H : "排队成功"
|
||
H-->>C : "200 OK + EQ数据"
|
||
```
|
||
|
||
**图表来源**
|
||
- [internal/handler/share_code.go:40-125](file://internal/handler/share_code.go#L40-L125)
|
||
- [internal/handler/share_code.go:228-279](file://internal/handler/share_code.go#L228-L279)
|
||
- [internal/repository/share_code.go:20-59](file://internal/repository/share_code.go#L20-L59)
|
||
- [pkg/encode/base64.go:21-52](file://pkg/encode/base64.go#L21-L52)
|
||
- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37)
|
||
|
||
**章节来源**
|
||
- [internal/handler/brand.go:26-50](file://internal/handler/brand.go#L26-L50)
|
||
- [internal/handler/model.go:26-51](file://internal/handler/model.go#L26-L51)
|
||
- [internal/handler/model_list.go:26-57](file://internal/handler/model_list.go#L26-L57)
|
||
- [internal/handler/ota.go:23-133](file://internal/handler/ota.go#L23-L133)
|
||
- [internal/handler/device.go:27-83](file://internal/handler/device.go#L27-L83)
|
||
- [internal/handler/share_code.go:15-383](file://internal/handler/share_code.go#L15-L383)
|
||
- [internal/handler/curve.go:27-582](file://internal/handler/curve.go#L27-L582)
|
||
- [pkg/encode/base64.go:21-52](file://pkg/encode/base64.go#L21-L52)
|
||
- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37)
|
||
|
||
### 查询与过滤逻辑
|
||
- **Brand 查询**
|
||
- 支持按品牌名模糊匹配,使用 LIKE 并按名称升序排列。
|
||
- **Model 查询**
|
||
- 优先按品牌名精确匹配;若为空则按型号名模糊匹配;否则返回空切片。
|
||
- 扫描时将 sql.NullString 转换为指针字符串,避免零值歧义。
|
||
- **OTA 查询**
|
||
- 支持按model+hw+beta+status=1查询最新OTA记录。
|
||
- 支持黑名单检查和定向设备检查。
|
||
- 提供多种查询策略以适应不同的升级场景。
|
||
- **设备查询**
|
||
- 支持按MAC地址查询设备信息。
|
||
- 支持按MAC地址和日期查询活跃记录。
|
||
- 扫描时将 sql.NullString 转换为指针字符串,避免零值歧义。
|
||
- **分享码查询**
|
||
- 支持按MAC地址查询未过期的分享码列表。
|
||
- 支持按分享码查询单个分享码详情。
|
||
- 支持分享码导入操作的日志排队和持久化。
|
||
- **目标曲线查询**
|
||
- 支持按标签查询目标曲线配置。
|
||
- 支持CSV文件路径和贝斯增强配置的动态加载。
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
Start(["进入分享码查询"]) --> Params["参数校验"]
|
||
Params --> CheckMac{"mac参数是否非空?"}
|
||
CheckMac --> |是| ListByMac["ListByMac 查询"]
|
||
CheckMac --> |否| CheckCode{"shareCode参数是否5位?"}
|
||
CheckCode --> |是| GetByCode["Get 查询"]
|
||
CheckCode --> |否| Error["返回错误"]
|
||
ListByMac --> CleanExpired["清理过期条目"]
|
||
CleanExpired --> GetDetails["逐个获取详情"]
|
||
GetDetails --> ReturnList["返回分享码列表"]
|
||
GetByCode --> ReturnDetail["返回分享码详情"]
|
||
ReturnList --> End(["返回结果"])
|
||
ReturnDetail --> End
|
||
Error --> End
|
||
```
|
||
|
||
**图表来源**
|
||
- [internal/handler/share_code.go:138-177](file://internal/handler/share_code.go#L138-L177)
|
||
- [internal/handler/share_code.go:192-211](file://internal/handler/share_code.go#L192-L211)
|
||
- [internal/cache/share_code_cache.go:281-319](file://internal/cache/share_code_cache.go#L281-L319)
|
||
|
||
**章节来源**
|
||
- [internal/repository/model.go:20-61](file://internal/repository/model.go#L20-L61)
|
||
- [internal/repository/brand.go:20-51](file://internal/repository/brand.go#L20-L51)
|
||
- [internal/repository/ota.go:19-159](file://internal/repository/ota.go#L19-L159)
|
||
- [internal/repository/device.go:19-88](file://internal/repository/device.go#L19-L88)
|
||
- [internal/repository/share_code.go:20-59](file://internal/repository/share_code.go#L20-L59)
|
||
- [internal/repository/curve.go:44-66](file://internal/repository/curve.go#L44-L66)
|
||
- [internal/handler/share_code.go:138-177](file://internal/handler/share_code.go#L138-L177)
|
||
|
||
## 依赖分析
|
||
- **层间耦合**
|
||
- Handler 依赖 Repository;Repository 依赖 sql.DB;Model 为纯数据结构。
|
||
- 统一响应体与编码层被 Handler 调用,降低重复逻辑。
|
||
- 新增的Redis依赖用于分享码缓存和设备信息缓存。
|
||
- 分享码功能依赖Redis缓存和数据库的双重存储。
|
||
- 目标曲线功能依赖CurveRepository和CurveCache。
|
||
- **外部依赖**
|
||
- 数据库:MySQL,通过 sql.DB 访问。
|
||
- 日志:zap。
|
||
- Web 框架:Gin。
|
||
- 搜索:Meilisearch(ModelListHandler)。
|
||
- 缓存:Redis(Device、ShareCode、Curve相关处理器)。
|
||
- 对象存储:S3(曲线数据CSV文件)。
|
||
- 任务调度:定时任务用于设备信息和分享码持久化。
|
||
- **潜在循环依赖**
|
||
- 当前结构清晰,无循环导入迹象。
|
||
- **新增依赖关系**
|
||
- 分享码功能依赖Redis缓存和数据库的双重存储。
|
||
- 目标曲线功能依赖CurveRepository和CurveCache。
|
||
- OTA功能依赖多个表的关联查询。
|
||
|
||
```mermaid
|
||
graph LR
|
||
H_Brand["BrandHandler"] --> Repo_Brand["BrandRepository"]
|
||
H_Model["ModelHandler"] --> Repo_Model["ModelRepository"]
|
||
H_ModelList["ModelListHandler"] --> Search["Meilisearch 客户端"]
|
||
H_OTA["OTAHandler"] --> Repo_OTA["OTARepository"]
|
||
H_Device["DeviceHandler"] --> Repo_Device["DeviceRepository"]
|
||
H_ShareCode["ShareCodeHandler"] --> Cache_Share["ShareCodeCache"]
|
||
H_ShareCode --> Repo_Share["ShareCodeRepository"]
|
||
H_Curve["CurveHandler"] --> Repo_Curve["CurveRepository"]
|
||
Repo_Brand --> DB["sql.DB"]
|
||
Repo_Model --> DB
|
||
Repo_OTA --> DB
|
||
Repo_Device --> DB
|
||
Repo_Share --> DB
|
||
Repo_Curve --> DB
|
||
H_Device --> Redis["Redis Client"]
|
||
H_ShareCode --> Redis
|
||
H_Curve --> Redis
|
||
H_Curve --> S3["S3 Storage"]
|
||
H_Brand --> Resp["统一响应体"]
|
||
H_Model --> Resp
|
||
H_ModelList --> Resp
|
||
H_OTA --> Resp
|
||
H_Device --> Resp
|
||
H_ShareCode --> Resp
|
||
H_Curve --> Resp
|
||
H_Brand --> Encode["Base64 编码"]
|
||
H_Model --> Encode
|
||
H_ModelList --> Encode
|
||
Task_Device["DevicePersistTask"] --> Repo_Device
|
||
Task_Device --> Redis
|
||
Task_Share["ShareCodePersistTask"] --> Repo_Share
|
||
Task_Share --> Redis
|
||
```
|
||
|
||
**图表来源**
|
||
- [internal/handler/brand.go:14-50](file://internal/handler/brand.go#L14-L50)
|
||
- [internal/handler/model.go:14-51](file://internal/handler/model.go#L14-L51)
|
||
- [internal/handler/model_list.go:14-57](file://internal/handler/model_list.go#L14-L57)
|
||
- [internal/handler/ota.go:14-133](file://internal/handler/ota.go#L14-L133)
|
||
- [internal/handler/device.go:15-83](file://internal/handler/device.go#L15-L83)
|
||
- [internal/handler/share_code.go:15-383](file://internal/handler/share_code.go#L15-L383)
|
||
- [internal/handler/curve.go:27-582](file://internal/handler/curve.go#L27-L582)
|
||
- [internal/repository/brand.go:12-51](file://internal/repository/brand.go#L12-L51)
|
||
- [internal/repository/model.go:12-95](file://internal/repository/model.go#L12-L95)
|
||
- [internal/repository/ota.go:11-159](file://internal/repository/ota.go#L11-L159)
|
||
- [internal/repository/device.go:11-88](file://internal/repository/device.go#L11-L88)
|
||
- [internal/repository/share_code.go:12-59](file://internal/repository/share_code.go#L12-L59)
|
||
- [internal/repository/curve.go:11-66](file://internal/repository/curve.go#L11-L66)
|
||
- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37)
|
||
- [pkg/encode/base64.go:21-52](file://pkg/encode/base64.go#L21-L52)
|
||
- [internal/task/device_persist.go:14-167](file://internal/task/device_persist.go#L14-L167)
|
||
- [internal/task/share_code_persist.go:14-232](file://internal/task/share_code_persist.go#L14-L232)
|
||
|
||
**章节来源**
|
||
- [internal/router/router.go:14-82](file://internal/router/router.go#L14-L82)
|
||
- [internal/config/database.go:17-72](file://internal/config/database.go#L17-L72)
|
||
|
||
## 性能考虑
|
||
- **查询优化**
|
||
- Model 表对 (brand_name, name) 建有唯一索引,有利于去重与快速定位。
|
||
- OTA表对(model, hw, beta, status)建立索引,支持快速查询最新OTA记录。
|
||
- UserDevice表对(mac_addr)建立唯一索引,支持快速设备查询。
|
||
- UserActive表对(mac_addr, active_date)建立联合索引,支持按日期分区查询。
|
||
- share_code_log表对(mac_addr, share_code, create_at)建立索引,支持分享码查询。
|
||
- 查询时优先按品牌名匹配,减少 LIKE 的范围。
|
||
- **扫描与内存**
|
||
- 使用 sql.NullString 扫描可空列,避免零值歧义;转换为指针字符串减少冗余存储。
|
||
- OTA模型使用BoolInt类型处理布尔值,支持数据库整型与JSON布尔值的双向转换。
|
||
- ShareCodeLog模型使用[]byte存储JSON数据,减少字符串处理开销。
|
||
- **缓存策略**
|
||
- 设备信息先写入Redis缓存,通过定时任务批量持久化到数据库,减少数据库压力。
|
||
- 品牌和型号信息使用Redis缓存提升查询性能。
|
||
- 分享码数据使用Redis缓存,支持TTL自动过期和分布式锁保证一致性。
|
||
- 目标曲线数据使用Redis缓存,支持fr数据独立存储优化缓存空间。
|
||
- **编码策略**
|
||
- 可选的自定义 Base64 编码可降低响应体积,适合大列表传输场景。
|
||
- **并发与超时**
|
||
- 服务器设置读写超时与优雅关闭,保障稳定性。
|
||
- 设备持久化任务和分享码持久化任务使用定时器定期执行,避免阻塞主线程。
|
||
- 分布式锁机制保证Redis操作的原子性和数据一致性。
|
||
|
||
**章节来源**
|
||
- [sql/model.sql:34-35](file://sql/model.sql#L34-L35)
|
||
- [sql/ota.sql:24-44](file://sql/ota.sql#L24-L44)
|
||
- [sql/share_code_log.sql:20-23](file://sql/share_code_log.sql#L20-L23)
|
||
- [sql/user_device.sql:24-32](file://sql/user_device.sql#L24-L32)
|
||
- [sql/user_active.sql:24-32](file://sql/user_active.sql#L24-L32)
|
||
- [internal/repository/model.go:63-95](file://internal/repository/model.go#L63-L95)
|
||
- [internal/repository/ota.go:20-159](file://internal/repository/ota.go#L20-L159)
|
||
- [internal/repository/device.go:19-88](file://internal/repository/device.go#L19-L88)
|
||
- [internal/repository/share_code.go:20-59](file://internal/repository/share_code.go#L20-L59)
|
||
- [pkg/encode/base64.go:21-52](file://pkg/encode/base64.go#L21-L52)
|
||
- [cmd/server/main.go:66-95](file://cmd/server/main.go#L66-L95)
|
||
- [internal/task/device_persist.go:24-91](file://internal/task/device_persist.go#L24-L91)
|
||
- [internal/task/share_code_persist.go:24-113](file://internal/task/share_code_persist.go#L24-L113)
|
||
|
||
## 故障排查指南
|
||
- **常见问题**
|
||
- 数据库连接失败:检查环境变量与配置加载逻辑。
|
||
- 查询无结果:确认查询参数是否为空或大小写敏感;Model 查询默认返回空切片而非 nil。
|
||
- 编码异常:确认 base64Resp 参数与 JSON 序列化是否成功。
|
||
- OTA查询失败:检查model、hw参数是否正确,确认OTA记录状态是否为1。
|
||
- 设备信息未持久化:检查Redis连接和数据库连接,确认定时任务是否正常运行。
|
||
- 分享码创建失败:检查Redis连接、分享码生成算法、TTL配置。
|
||
- 分享码导入失败:检查导入日志队列、数据库连接、分布式锁状态。
|
||
- 目标曲线查询失败:检查目标曲线配置、CSV文件路径、S3访问权限。
|
||
- **排查步骤**
|
||
- 查看日志:处理器记录错误日志,统一响应体返回错误码。
|
||
- 核对数据库:确认表结构与索引是否存在。
|
||
- 验证参数:确认查询参数是否符合预期。
|
||
- 检查Redis:确认设备信息、分享码数据是否正确写入Redis缓存。
|
||
- 监控任务:确认设备持久化任务和分享码持久化任务是否按预期执行。
|
||
- 检查分布式锁:确认分享码操作的分布式锁状态和超时处理。
|
||
|
||
**章节来源**
|
||
- [internal/config/database.go:57-72](file://internal/config/database.go#L57-L72)
|
||
- [internal/handler/brand.go:31-35](file://internal/handler/brand.go#L31-L35)
|
||
- [internal/handler/model.go:33-36](file://internal/handler/model.go#L33-L36)
|
||
- [internal/handler/model_list.go:39-42](file://internal/handler/model_list.go#L39-L42)
|
||
- [internal/handler/ota.go:31-48](file://internal/handler/ota.go#L31-L48)
|
||
- [internal/handler/device.go:33-39](file://internal/handler/device.go#L33-L39)
|
||
- [internal/handler/share_code.go:46-81](file://internal/handler/share_code.go#L46-81)
|
||
- [internal/handler/curve.go:302-368](file://internal/handler/curve.go#L302-L368)
|
||
- [internal/response/response.go:30-37](file://internal/response/response.go#L30-L37)
|
||
|
||
## 结论
|
||
本项目的数据模型设计简洁明确:Brand 与 Model 分别承担品牌与型号的维度,通过仓库层的原生 SQL 访问实现高效查询;处理器层统一响应与可选编码,提升传输效率与前端体验。新增的分享码功能、目标曲线模型和增强的OTA模型进一步完善了系统的数据模型体系,支持复杂的固件升级策略、设备信息跟踪、用户行为分析和EQ数据分享功能。数据库层面通过唯一索引与合理字段设计保障一致性与性能。Redis缓存层提供了高性能的临时数据存储和分布式锁机制。建议在后续迭代中引入显式的校验与转换层,增强健壮性与可维护性,同时考虑添加更多的监控指标和告警机制。
|
||
|
||
## 附录
|
||
|
||
### 数据库表结构与模型映射对照
|
||
- **表:model**
|
||
- 字段:id(主键)、brand_name、name、form、rig、source、eq_key、create_at。
|
||
- 约束:唯一索引 (brand_name, name)。
|
||
- **表:ota**
|
||
- 字段:id(主键)、verCode、verName、url、md5、force、desc、model、hw、target、beta、pawVerCode、pawVerName、pawUrl、pawMd5、startTime、endTime、status。
|
||
- 约束:无显式约束。
|
||
- **表:black_list**
|
||
- 字段:id(主键)、ota_id、mac、create_at。
|
||
- 约束:无显式约束。
|
||
- **表:ota_target_device**
|
||
- 字段:id(主键)、ota_id、mac_addr、type、create_at。
|
||
- 约束:无显式约束。
|
||
- **表:user_device**
|
||
- 字段:id(主键)、mac_addr、model、add_time、ver。
|
||
- 约束:唯一索引 (mac_addr)。
|
||
- **表:user_active**
|
||
- 字段:id、mac_addr、model、active_date、ip_addr、create_at。
|
||
- 约束:联合主键 (id, active_date),联合索引 (mac_addr, active_date)。
|
||
- **表:share_code_log**
|
||
- 字段:id(主键)、mac_addr、share_code、action、model、ip_addr、eq_data、expire_at、create_at。
|
||
- 约束:索引 idx_mac_addr、idx_share_code、idx_create_at。
|
||
- **表:target**
|
||
- 字段:id(主键)、label、read_csv、file、bassBoost、addtime。
|
||
- 约束:无显式约束。
|
||
- **映射关系**
|
||
- Model.id ↔ model.id
|
||
- Model.brandName ↔ model.brand_name
|
||
- Model.name ↔ model.name
|
||
- OTA.id ↔ ota.id
|
||
- OTA.verCode ↔ ota.verCode
|
||
- OTA.verName ↔ ota.verName
|
||
- OTA.url ↔ ota.url
|
||
- OTA.md5 ↔ ota.md5
|
||
- OTA.force ↔ ota.force
|
||
- OTA.desc ↔ ota.desc
|
||
- OTA.model ↔ ota.model
|
||
- OTA.hw ↔ ota.hw
|
||
- OTA.target ↔ ota.target
|
||
- OTA.beta ↔ ota.beta
|
||
- OTA.pawVerCode ↔ ota.pawVerCode
|
||
- OTA.pawVerName ↔ ota.pawVerName
|
||
- OTA.pawUrl ↔ ota.pawUrl
|
||
- OTA.pawMD5 ↔ ota.pawMD5
|
||
- OTA.startTime ↔ ota.startTime
|
||
- OTA.endTime ↔ ota.endTime
|
||
- OTA.status ↔ ota.status
|
||
- BlackList.id ↔ black_list.id
|
||
- BlackList.ota_id ↔ black_list.ota_id
|
||
- BlackList.mac ↔ black_list.mac
|
||
- BlackList.create_at ↔ black_list.create_at
|
||
- OTATargetDevice.id ↔ ota_target_device.id
|
||
- OTATargetDevice.ota_id ↔ ota_target_device.ota_id
|
||
- OTATargetDevice.mac_addr ↔ ota_target_device.mac_addr
|
||
- OTATargetDevice.type ↔ ota_target_device.type
|
||
- OTATargetDevice.create_at ↔ ota_target_device.create_at
|
||
- UserDevice.id ↔ user_device.id
|
||
- UserDevice.mac_addr ↔ user_device.mac_addr
|
||
- UserDevice.model ↔ user_device.model
|
||
- UserDevice.add_time ↔ user_device.add_time
|
||
- UserDevice.ver ↔ user_device.ver
|
||
- UserActive.id ↔ user_active.id
|
||
- UserActive.mac_addr ↔ user_active.mac_addr
|
||
- UserActive.model ↔ user_active.model
|
||
- UserActive.active_date ↔ user_active.active_date
|
||
- UserActive.ip_addr ↔ user_active.ip_addr
|
||
- UserActive.create_at ↔ user_active.create_at
|
||
- ShareCodeLog.ID ↔ share_code_log.id
|
||
- ShareCodeLog.MacAddr ↔ share_code_log.mac_addr
|
||
- ShareCodeLog.ShareCode ↔ share_code_log.share_code
|
||
- ShareCodeLog.Action ↔ share_code_log.action
|
||
- ShareCodeLog.Model ↔ share_code_log.model
|
||
- ShareCodeLog.IpAddr ↔ share_code_log.ip_addr
|
||
- ShareCodeLog.EqData ↔ share_code_log.eq_data
|
||
- ShareCodeLog.ExpireAt ↔ share_code_log.expire_at
|
||
- ShareCodeLog.CreateAt ↔ share_code_log.create_at
|
||
- Target.ID ↔ target.id
|
||
- Target.Label ↔ target.label
|
||
- Target.ReadCSV ↔ target.read_csv
|
||
- Target.File ↔ target.file
|
||
- Target.BassBoost ↔ target.bassBoost
|
||
- Target.AddTime ↔ target.addtime
|
||
|
||
**章节来源**
|
||
- [sql/model.sql:24-35](file://sql/model.sql#L24-L35)
|
||
- [sql/ota.sql:24-44](file://sql/ota.sql#L24-L44)
|
||
- [sql/ota_target_device.sql:24-31](file://sql/ota_target_device.sql#L24-L31)
|
||
- [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/share_code_log.sql:9-23](file://sql/share_code_log.sql#L9-L23)
|
||
- [sql/target.sql:24-31](file://sql/target.sql#L24-L31)
|
||
- [internal/model/model.go:5-15](file://internal/model/model.go#L5-L15)
|
||
- [internal/model/ota.go:29-67](file://internal/model/ota.go#L29-L67)
|
||
- [internal/model/user_device.go:5-12](file://internal/model/user_device.go#L5-L12)
|
||
- [internal/model/user_active.go:5-13](file://internal/model/user_active.go#L5-L13)
|
||
- [internal/model/share_code.go:14-25](file://internal/model/share_code.go#L14-L25)
|
||
- [internal/model/target.go:5-14](file://internal/model/target.go#L5-L14)
|
||
|
||
### 请求与响应示例(路径参考)
|
||
- **获取品牌列表**
|
||
- 路由:/audio/getBrand
|
||
- 方法:GET
|
||
- 参数:brandName(可选),base64Resp(可选)
|
||
- 返回:[]Brand 或 Base64 编码后的 JSON
|
||
- 参考路径:[internal/handler/brand.go:26-50](file://internal/handler/brand.go#L26-L50)
|
||
- **获取型号列表**
|
||
- 路由:/audio/getModel
|
||
- 方法:GET
|
||
- 参数:brandName(可选)、modelName(可选)、base64Resp(可选)
|
||
- 返回:[]Model 或 Base64 编码后的 JSON
|
||
- 参考路径:[internal/handler/model.go:26-51](file://internal/handler/model.go#L26-L51)
|
||
- **搜索模型列表**
|
||
- 路由:/audio/modelList
|
||
- 方法:GET
|
||
- 参数:key(必需)、count(可选)、base64Resp(可选)
|
||
- 返回:[]Model 或 Base64 编码后的 JSON
|
||
- 参考路径:[internal/handler/model_list.go:26-57](file://internal/handler/model_list.go#L26-L57)
|
||
- **获取OTA升级信息**
|
||
- 路由:/audio/ota
|
||
- 方法:GET
|
||
- 参数:model(必需)、hw(必需)、mac(可选)、beta(可选)、base64Resp(可选)
|
||
- 返回:OTA对象或 Base64 编码后的 JSON
|
||
- 参考路径:[internal/handler/ota.go:23-133](file://internal/handler/ota.go#L23-L133)
|
||
- **设备信息上报**
|
||
- 路由:/audio/reportDevInfo
|
||
- 方法:GET
|
||
- 参数:mac(必需)、model(必需)、ver(可选)
|
||
- 返回:操作结果
|
||
- 参考路径:[internal/handler/device.go:27-83](file://internal/handler/device.go#L27-L83)
|
||
- **创建分享码**
|
||
- 路由:/audio/shareCreate
|
||
- 方法:POST
|
||
- 参数:mac(必需)、model(必需,Luxsin-X9或Luxsin-X8)、eq_data(必需,JSON)
|
||
- 返回:分享码信息和过期时间
|
||
- 参考路径:[internal/handler/share_code.go:40-125](file://internal/handler/share_code.go#L40-L125)
|
||
- **查询分享码**
|
||
- 路由:/audio/shareQuery
|
||
- 方法:GET
|
||
- 参数:shareCode(必需,5位)
|
||
- 返回:EQ数据和设备型号
|
||
- 参考路径:[internal/handler/share_code.go:192-211](file://internal/handler/share_code.go#L192-L211)
|
||
- **导入分享码**
|
||
- 路由:/audio/shareAccept
|
||
- 方法:GET
|
||
- 参数:mac(必需)、model(必需,Luxsin-X9或Luxsin-X8)、shareCode(必需,5位)
|
||
- 返回:EQ数据和设备型号
|
||
- 参考路径:[internal/handler/share_code.go:228-279](file://internal/handler/share_code.go#L228-L279)
|
||
- **获取目标曲线**
|
||
- 路由:/audio/getCurve
|
||
- 方法:GET
|
||
- 参数:brand(必需)、name(必需)、target(必需)、base64Resp(可选)
|
||
- 返回:parametric_eq数据
|
||
- 参考路径:[internal/handler/curve.go:145-198](file://internal/handler/curve.go#L145-L198)
|
||
|
||
**章节来源**
|
||
- [internal/router/router.go:63-78](file://internal/router/router.go#L63-L78)
|
||
- [internal/handler/brand.go:26-50](file://internal/handler/brand.go#L26-L50)
|
||
- [internal/handler/model.go:26-51](file://internal/handler/model.go#L26-L51)
|
||
- [internal/handler/model_list.go:26-57](file://internal/handler/model_list.go#L26-L57)
|
||
- [internal/handler/ota.go:23-133](file://internal/handler/ota.go#L23-L133)
|
||
- [internal/handler/device.go:27-83](file://internal/handler/device.go#L27-L83)
|
||
- [internal/handler/share_code.go:40-125](file://internal/handler/share_code.go#L40-L125)
|
||
- [internal/handler/share_code.go:192-211](file://internal/handler/share_code.go#L192-L211)
|
||
- [internal/handler/share_code.go:228-279](file://internal/handler/share_code.go#L228-L279)
|
||
- [internal/handler/curve.go:145-198](file://internal/handler/curve.go#L145-L198)
|
||
|
||
### 最佳实践建议
|
||
- **模型扩展**
|
||
- 引入显式的校验与转换层,如参数清洗、长度限制、正则校验等。
|
||
- 对可空字段提供默认值策略,避免前端空值判断复杂化。
|
||
- OTA模型支持复杂的升级策略,建议在业务层添加更多的参数验证。
|
||
- 分享码模型支持多种设备型号,建议添加设备型号验证和限制。
|
||
- **版本管理与向后兼容**
|
||
- 通过 API 版本号(如 /api/v1)隔离变更;新增字段采用可选策略,保持旧字段必填。
|
||
- 对于破坏性变更,提供迁移脚本与双写策略。
|
||
- OTA升级策略的变更需要谨慎处理,确保向后兼容性。
|
||
- 分享码功能的新增不影响现有API,保持向前兼容。
|
||
- **性能优化**
|
||
- 为高频查询字段建立索引;避免 SELECT *,仅选择必要字段。
|
||
- 对大列表启用可选 Base64 编码;结合分页与缓存策略。
|
||
- 设备信息使用Redis缓存,定期批量持久化到数据库。
|
||
- OTA查询使用复合索引,支持快速定位最新OTA记录。
|
||
- 分享码使用Redis缓存,支持TTL自动过期和分布式锁。
|
||
- 目标曲线使用Redis缓存,优化fr数据的独立存储。
|
||
- **错误处理与可观测性**
|
||
- 统一错误码与消息格式;记录关键链路日志;对数据库与外部服务增加超时与重试。
|
||
- OTA查询失败需要详细的日志记录,包括参数、查询结果等。
|
||
- 设备持久化任务需要监控执行状态和错误日志。
|
||
- 分享码持久化任务需要监控Redis队列状态和数据库插入结果。
|
||
- 目标曲线查询需要监控S3访问权限和CSV文件读取。
|
||
- **安全考虑**
|
||
- OTA下载链接需要安全性验证,防止恶意下载。
|
||
- 设备信息上报需要参数验证,防止注入攻击。
|
||
- 黑名单和白名单管理需要权限控制和审计日志。
|
||
- 分享码功能需要防止暴力破解,建议添加频率限制。
|
||
- 分享码删除操作需要MAC地址验证,防止越权操作。
|
||
- EQ数据存储需要JSON格式验证,防止恶意数据注入。
|
||
- **监控与运维**
|
||
- 添加Redis连接池监控,确保缓存层稳定运行。
|
||
- 添加数据库连接池监控,避免连接泄漏。
|
||
- 添加任务调度监控,确保持久化任务正常执行。
|
||
- 添加分布式锁监控,防止死锁和资源竞争。
|
||
- 添加S3访问监控,确保CSV文件读取正常。 |