665397ede7
- 新增OTA固件升级处理器,支持固件升级请求及黑名单过滤 - 引入设备持久化仓储,支持设备信息上报与活动记录 - 扩展数据模型,新增UserDevice与UserActive,支持设备版本跟踪 - 实现Redis到MySQL的异步数据同步任务 - 更新路由配置,集成新的API端点以支持OTA功能 - 优化架构图,反映新增的数据流与处理流程
707 lines
36 KiB
Markdown
707 lines
36 KiB
Markdown
# 数据库设计
|
||
|
||
<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)
|
||
</cite>
|
||
|
||
## 更新摘要
|
||
**所做更改**
|
||
- 新增设备持久化子系统:user_device、user_active 表及配套处理器和任务
|
||
- 新增 OTA 固件升级子系统:ota、black_list、ota_target_device 表及配套仓库层
|
||
- 更新架构图以反映新的数据流和处理流程
|
||
- 新增设备上报接口和定时持久化任务
|
||
- 新增 OTA 升级查询接口和复杂业务逻辑处理
|
||
- 更新数据访问模式以支持新的业务场景
|
||
|
||
## 目录
|
||
1. [简介](#简介)
|
||
2. [项目结构](#项目结构)
|
||
3. [核心组件](#核心组件)
|
||
4. [架构总览](#架构总览)
|
||
5. [详细组件分析](#详细组件分析)
|
||
6. [依赖分析](#依赖分析)
|
||
7. [性能考虑](#性能考虑)
|
||
8. [故障排查指南](#故障排查指南)
|
||
9. [结论](#结论)
|
||
10. [附录](#附录)
|
||
|
||
## 简介
|
||
本文件面向 Luxsin 应用 API 的数据库与数据模型,系统化梳理实体关系、字段定义、索引与约束、数据访问模式、缓存与搜索集成、性能优化、数据生命周期与迁移路径,并给出品牌(Brand)与型号(Model)实体的设计理念与业务逻辑说明。文档同时覆盖数据库连接配置、查询优化建议、数据安全与隐私要求以及访问控制要点。
|
||
|
||
**更新** 本次更新反映了数据库架构的重大扩展,新增了设备持久化和 OTA 固件升级两大核心功能模块,包括 user_device、user_active、ota、black_list、ota_target_device 五个新表及其配套的处理逻辑。
|
||
|
||
## 项目结构
|
||
本项目采用分层架构:入口程序负责初始化配置、数据库、搜索引擎与缓存;路由层组织 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_O["internal/handler/ota.go"]
|
||
REPO_B["internal/repository/brand.go"]
|
||
REPO_M["internal/repository/model.go"]
|
||
REPO_D["internal/repository/device.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"]
|
||
TASK_DP["internal/task/device_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_O
|
||
HANDLER_B --> REPO_B
|
||
HANDLER_M --> REPO_M
|
||
HANDLER_D --> REPO_D
|
||
HANDLER_O --> REPO_O
|
||
REPO_B --> MODEL_B
|
||
REPO_M --> MODEL_M
|
||
REPO_D --> MODEL_UD
|
||
REPO_D --> MODEL_UA
|
||
REPO_O --> MODEL_OTA
|
||
TASK_DP --> REPO_D
|
||
```
|
||
|
||
**图表来源**
|
||
- [cmd/server/main.go:1-96](file://cmd/server/main.go#L1-L96)
|
||
- [internal/config/config.go:1-64](file://internal/config/config.go#L1-L64)
|
||
- [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/ota.go:1-133](file://internal/handler/ota.go#L1-L133)
|
||
- [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/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-43](file://internal/model/ota.go#L1-L43)
|
||
- [internal/task/device_persist.go:1-167](file://internal/task/device_persist.go#L1-L167)
|
||
|
||
**章节来源**
|
||
- [cmd/server/main.go:1-96](file://cmd/server/main.go#L1-L96)
|
||
- [internal/config/config.go:1-64](file://internal/config/config.go#L1-L64)
|
||
|
||
## 核心组件
|
||
- 数据库表 model:存储耳机型号信息,包含唯一组合索引以保证品牌+型号的唯一性。
|
||
- 设备持久化表:user_device(设备信息持久化)、user_active(设备活跃度统计)
|
||
- OTA 升级表:ota(固件升级信息)、black_list(黑名单)、ota_target_device(定向设备)
|
||
- 模型对象 Brand 与 Model:分别映射品牌与型号的 JSON 字段与数据库列。
|
||
- 仓库层:提供按品牌名或型号名检索的查询方法,支持模糊匹配与精确匹配。
|
||
- 处理器层:暴露 HTTP 接口,支持返回 JSON 或 Base64 编码响应。
|
||
- 配置层:集中加载数据库、搜索引擎与缓存的连接参数,并进行基本校验。
|
||
- 基础设施:MySQL 连接池配置、Redis 客户端、Meilisearch 搜索客户端。
|
||
|
||
**更新** 新增设备持久化和 OTA 升级两大功能模块,包含完整的数据模型、业务逻辑和处理流程。
|
||
|
||
**章节来源**
|
||
- [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-47](file://sql/ota.sql#L20-L47)
|
||
- [sql/black_list.sql:20-33](file://sql/black_list.sql#L20-L33)
|
||
- [sql/ota_target_device.sql:20-34](file://sql/ota_target_device.sql#L20-L34)
|
||
- [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-43](file://internal/model/ota.go#L1-L43)
|
||
- [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/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/ota.go:1-133](file://internal/handler/ota.go#L1-L133)
|
||
- [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"
|
||
C->>H : "HTTP GET /brands,/models,/device/report,/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 缓存 + 定时持久化"
|
||
```
|
||
|
||
**图表来源**
|
||
- [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/ota.go:23-132](file://internal/handler/ota.go#L23-L132)
|
||
- [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/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)
|
||
|
||
## 详细组件分析
|
||
|
||
### 数据模型与实体关系
|
||
- 实体:Brand(品牌)、Model(型号)、UserDevice(设备)、UserActive(活跃度)、OTA(固件升级)、BlackList(黑名单)、OTATargetDevice(定向设备)
|
||
- 关系:
|
||
- Model 通过字段 brand_name 引导与品牌的关系
|
||
- UserDevice 与 UserActive 通过 mac_addr 关联
|
||
- OTA 与 BlackList 通过 ota_id 关联
|
||
- OTA 与 OTATargetDevice 通过 ota_id 关联
|
||
- 字段与类型:
|
||
- Brand:id(整数,主键)、name(字符串)
|
||
- Model:id(整数,主键)、brand_name(字符串,非空)、name(字符串,非空)、form(字符串,可空)、rig(字符串,可空)、source(字符串,可空)、eq_key(字符串,可空)、create_at(时间戳,默认当前时间)
|
||
- UserDevice:id(整数,主键)、mac_addr(字符串,唯一)、model(字符串)、add_time(时间戳,默认当前时间)、ver(字符串,可空)
|
||
- UserActive:id(整数,主键)、mac_addr(字符串)、model(字符串)、active_date(日期,主键部分)、ip_addr(字符串)、create_at(时间戳,默认当前时间)
|
||
- OTA:id(整数,主键)、verCode(整数)、verName(字符串)、url(字符串)、md5(字符串)、force(整数,默认0)、desc(字符串,可空)、model(字符串,可空)、hw(整数,默认0)、target(整数,默认0)、beta(整数,默认0)、pawVerCode(整数,默认0)、pawVerName(字符串)、pawUrl(字符串)、pawMd5(字符串)、startTime(时间戳,可空)、endTime(时间戳,可空)、status(整数,默认1)
|
||
- BlackList:id(整数,主键)、ota_id(整数)、mac(字符串,默认空)、create_at(时间戳,默认当前时间)
|
||
- OTATargetDevice:id(整数,主键)、ota_id(整数)、mac_addr(字符串)、type(整数,默认1,1-白名单;2-黑名单)、create_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
|
||
int pawVerCode
|
||
string pawVerName
|
||
string pawUrl
|
||
string pawMd5
|
||
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
|
||
int type
|
||
datetime create_at
|
||
}
|
||
BRAND ||--o{ MODEL : "拥有多个型号"
|
||
USER_DEVICE ||--o{ USER_ACTIVE : "每日活跃记录"
|
||
OTA ||--o{ BLACK_LIST : "黑名单关联"
|
||
OTA ||--o{ OTA_TARGET_DEVICE : "定向设备关联"
|
||
```
|
||
|
||
**图表来源**
|
||
- [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:6-25](file://internal/model/ota.go#L6-L25)
|
||
- [internal/model/ota.go:28-42](file://internal/model/ota.go#L28-L42)
|
||
- [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-44](file://sql/ota.sql#L24-L44)
|
||
- [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)
|
||
|
||
**章节来源**
|
||
- [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-43](file://internal/model/ota.go#L1-L43)
|
||
- [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-47](file://sql/ota.sql#L20-L47)
|
||
- [sql/black_list.sql:20-33](file://sql/black_list.sql#L20-L33)
|
||
- [sql/ota_target_device.sql:20-34](file://sql/ota_target_device.sql#L20-L34)
|
||
|
||
### 数据库表结构与约束
|
||
- 表名:model、user_device、user_active、ota、black_list、ota_target_device
|
||
- 主键:各表的 id 字段(自增整数)
|
||
- 唯一索引:
|
||
- model:(brand_name, name)
|
||
- user_device:(mac_addr)
|
||
- user_active:(mac_addr, active_date)
|
||
- 默认值:各表的 create_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-47](file://sql/ota.sql#L20-L47)
|
||
- [sql/black_list.sql:20-33](file://sql/black_list.sql#L20-L33)
|
||
- [sql/ota_target_device.sql:20-34](file://sql/ota_target_device.sql#L20-L34)
|
||
|
||
### 数据访问模式
|
||
- 品牌列表:
|
||
- 支持按品牌名模糊查询(LIKE %brandName%),无匹配时返回空数组
|
||
- 结果按名称升序排列
|
||
- 型号列表:
|
||
- 支持按品牌名精确匹配或按型号名模糊匹配,二者二选一
|
||
- 无匹配条件时返回空数组
|
||
- 结果按名称升序排列
|
||
- 设备持久化:
|
||
- 设备上报:通过 Redis Hash 缓存设备信息
|
||
- 定时持久化:每间隔时间从 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
|
||
```
|
||
|
||
**图表来源**
|
||
- [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/task/device_persist.go:37-91](file://internal/task/device_persist.go#L37-L91)
|
||
- [internal/repository/device.go:19-87](file://internal/repository/device.go#L19-L87)
|
||
|
||
**章节来源**
|
||
- [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/task/device_persist.go:1-167](file://internal/task/device_persist.go#L1-L167)
|
||
- [internal/repository/device.go:1-88](file://internal/repository/device.go#L1-L88)
|
||
|
||
### 处理器与接口行为
|
||
- 品牌接口:接收 query 参数 brandName,支持返回 JSON 或 Base64 编码响应
|
||
- 型号接口:接收 query 参数 brandName 与 modelName,支持返回 JSON 或 Base64 编码响应
|
||
- 设备上报接口:接收 mac、model、ver 参数,将设备信息缓存到 Redis
|
||
- OTA 升级接口:接收 model、hw、mac、beta 参数,返回合适的 OTA 升级信息
|
||
- 错误处理:内部错误统一返回 500 并记录日志
|
||
|
||
**更新** 新增设备上报和 OTA 升级两个核心接口,支持完整的设备管理和固件升级业务流程。
|
||
|
||
**章节来源**
|
||
- [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/ota.go:1-133](file://internal/handler/ota.go#L1-L133)
|
||
|
||
### 搜索与缓存集成
|
||
- 搜索:Meilisearch 客户端按给定关键字检索,限定返回属性,支持空命中返回空数组
|
||
- 缓存:Redis 客户端初始化,用于设备信息缓存和定时持久化
|
||
- 设备缓存:使用 Redis Hash 存储设备上报信息,键为设备 MAC 地址
|
||
- 持久化策略:定时任务批量处理 Redis 缓存数据,避免频繁数据库写入
|
||
|
||
**更新** 新增了设备信息的 Redis 缓存机制,通过定时任务实现高效的数据持久化。
|
||
|
||
**章节来源**
|
||
- [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/task/device_persist.go:37-91](file://internal/task/device_persist.go#L37-L91)
|
||
|
||
### 数据库连接配置
|
||
- 加载顺序:环境变量优先于默认值;生产环境需提供密码
|
||
- 连接参数:用户、主机、端口、数据库名、字符集、时区、超时等
|
||
- 连接池:最大打开连接数、最大空闲连接数、连接最大生命周期
|
||
- 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
|
||
|
||
**章节来源**
|
||
- [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52)
|
||
- [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)
|
||
|
||
### OTA 固件升级子系统
|
||
- 版本查询:支持按型号、硬件版本、灰度标识查询最新可用 OTA 版本
|
||
- 黑名单机制:支持将特定设备加入 OTA 升级黑名单
|
||
- 定向升级:支持针对特定设备集合的定向升级策略
|
||
- 灰度发布:通过 beta 标识实现渐进式升级发布
|
||
- 强制升级:支持强制升级策略,阻止设备继续使用旧版本
|
||
|
||
**新增** OTA 固件升级子系统提供了完整的固件管理能力。
|
||
|
||
**章节来源**
|
||
- [internal/handler/ota.go:23-132](file://internal/handler/ota.go#L23-L132)
|
||
- [internal/repository/ota.go:19-158](file://internal/repository/ota.go#L19-L158)
|
||
- [internal/model/ota.go:6-42](file://internal/model/ota.go#L6-L42)
|
||
|
||
## 依赖分析
|
||
- 处理器依赖仓库;仓库依赖数据库;应用入口依赖配置与基础设施。
|
||
- 搜索与缓存作为独立模块被配置层加载并在处理器中可选使用。
|
||
- 设备持久化依赖 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_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_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
|
||
```
|
||
|
||
**图表来源**
|
||
- [cmd/server/main.go:1-96](file://cmd/server/main.go#L1-L96)
|
||
- [internal/config/config.go:1-64](file://internal/config/config.go#L1-L64)
|
||
- [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/ota.go:1-133](file://internal/handler/ota.go#L1-L133)
|
||
- [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/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-47](file://sql/ota.sql#L20-L47)
|
||
- [sql/black_list.sql:20-33](file://sql/black_list.sql#L20-L33)
|
||
- [sql/ota_target_device.sql:20-34](file://sql/ota_target_device.sql#L20-L34)
|
||
- [internal/task/device_persist.go:1-167](file://internal/task/device_persist.go#L1-L167)
|
||
|
||
## 性能考虑
|
||
- 连接池与生命周期
|
||
- 最大打开连接数:25;最大空闲连接数:5;连接最大生命周期:5 分钟
|
||
- 建议:根据并发与查询负载调整;生产环境结合监控指标动态优化
|
||
- 查询优化
|
||
- 品牌列表:支持模糊匹配,建议在高频搜索场景引入前缀索引或搜索引擎替代
|
||
- 型号列表:按品牌名精确匹配或按型号名模糊匹配;建议对常用过滤字段建立合适索引
|
||
- 设备查询:user_device 表的 mac_addr 唯一索引支持快速查找
|
||
- OTA 查询:按 model、hw、beta、status 组合查询,建议建立复合索引
|
||
- 缓存策略
|
||
- 对品牌列表与热门型号列表进行短期缓存,设置合理过期时间
|
||
- 设备信息使用 Redis 缓存,避免频繁数据库写入
|
||
- 使用缓存穿透防护(空结果也缓存短时间)与缓存雪崩防护(随机过期时间)
|
||
- 搜索优化
|
||
- 使用搜索引擎进行全文检索与高亮,减少数据库 LIKE 查询压力
|
||
- 控制返回字段数量,避免传输冗余数据
|
||
- I/O 与序列化
|
||
- 响应支持 Base64 编码,适合二进制传输场景,但会增加 CPU 开销
|
||
- 分区表优化
|
||
- user_active 表按日期分区,提高大数据量下的查询性能
|
||
- 分区裁剪可以显著减少扫描数据量
|
||
|
||
**更新** 新增了设备持久化和 OTA 升级场景的性能优化建议,包括 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/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)
|
||
|
||
## 故障排查指南
|
||
- 数据库连接失败
|
||
- 检查环境变量是否正确设置;确认主机、端口、用户名、密码与数据库名
|
||
- 启动时会进行 Ping 校验,失败会记录错误并退出
|
||
- 查询异常
|
||
- 仓库层对扫描与迭代过程进行错误包装,定位具体阶段(扫描、迭代)
|
||
- 建议开启数据库慢查询日志与应用日志聚合
|
||
- 搜索与缓存
|
||
- 搜索引擎与缓存客户端初始化后会记录连接信息;若无响应,检查主机、密钥与网络连通性
|
||
- Redis 缓存异常会影响设备持久化功能
|
||
- 响应编码
|
||
- Base64 返回失败时,检查编码流程与日志输出
|
||
- 设备持久化问题
|
||
- 检查 Redis 连接状态和设备缓存数据
|
||
- 定时任务执行失败时,查看任务日志和数据库连接状态
|
||
- OTA 升级问题
|
||
- 检查 OTA 表数据完整性和索引状态
|
||
- 验证黑名单和定向设备表的数据准确性
|
||
|
||
**更新** 新增了设备持久化和 OTA 升级相关的故障排查指南。
|
||
|
||
**章节来源**
|
||
- [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/ota.go:56-132](file://internal/handler/ota.go#L56-L132)
|
||
- [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)
|
||
|
||
## 结论
|
||
本设计以简洁的表结构与清晰的分层架构支撑品牌与型号的查询需求。通过搜索引擎与缓存提升检索性能,配合连接池与错误处理机制保障稳定性。**更新** 新增的设备持久化和 OTA 固件升级子系统进一步完善了系统的业务能力,通过 Redis 缓存和分区表优化提升了大规模数据处理的性能。后续可在索引策略、缓存策略与数据迁移方面进一步细化,以满足生产环境的高可用与高性能要求。
|
||
|
||
## 附录
|
||
|
||
### 数据验证与业务规则
|
||
- 品牌与型号名称必填,型号表对品牌+型号建立唯一索引,防止重复
|
||
- 设备 MAC 地址唯一,支持设备去重和版本跟踪
|
||
- OTA 升级记录包含版本号、MD5 校验、时间范围等完整性约束
|
||
- 黑名单和定向设备表确保 OTA 发布的安全性
|
||
- 时间字段 create_at 默认当前时间,便于审计与排序
|
||
- 处理器层对空结果返回空数组,避免无效数据传播
|
||
|
||
**更新** 新增了设备和 OTA 相关的数据验证规则。
|
||
|
||
**章节来源**
|
||
- [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-44](file://sql/ota.sql#L24-L44)
|
||
- [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)
|
||
- [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)
|
||
|
||
### 示例数据
|
||
- 品牌示例: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
|
||
|
||
**更新** 新增了设备和 OTA 的示例数据。
|
||
|
||
**章节来源**
|
||
- [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:6-25](file://internal/model/ota.go#L6-L25)
|
||
|
||
### 数据生命周期、保留策略与归档规则
|
||
- 建议:基于 create_at 建立分区或定期归档任务,清理长期未使用的型号数据
|
||
- 设备数据:保留最近 30 天的设备活跃记录,历史数据可压缩存储
|
||
- OTA 数据:保留最近 6 个月的升级记录,历史版本可归档
|
||
- 归档策略:保留近一年的活跃型号,历史数据移至冷存储
|
||
- 审计:保留变更日志与备份周期,确保可追溯性
|
||
|
||
**更新** 新增了设备和 OTA 数据的生命周期管理建议。
|
||
|
||
**章节来源**
|
||
- [sql/user_active.sql:33-204](file://sql/user_active.sql#L33-L204)
|
||
- [sql/ota.sql:24-44](file://sql/ota.sql#L24-L44)
|
||
|
||
### 数据迁移路径与版本管理
|
||
- 版本化:每次结构变更生成迁移脚本,记录变更人、时间与目的
|
||
- 回滚:保留逆向迁移脚本;灰度发布逐步切换
|
||
- 兼容:新增字段采用可空策略,旧数据默认值填充
|
||
- 设备数据迁移:支持从临时表导入到正式表,处理重复数据
|
||
- OTA 数据迁移:支持版本号递增和兼容性检查
|
||
|
||
**更新** 新增了设备和 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-47](file://sql/ota.sql#L20-L47)
|
||
|
||
### 数据安全、隐私要求与访问控制
|
||
- 网络安全:数据库与搜索引擎通过内网或 VPC 访问,限制入站 IP
|
||
- 凭证管理:敏感信息通过环境变量注入,禁止硬编码
|
||
- 访问控制:最小权限原则;数据库账号仅授予必要权限;搜索引擎 API Key 严格管理
|
||
- 设备隐私:MAC 地址等个人标识信息需遵循隐私保护法规
|
||
- OTA 安全:固件 MD5 校验确保下载完整性,黑名单机制防止恶意设备升级
|
||
|
||
**更新** 新增了设备和 OTA 场景下的安全要求。
|
||
|
||
**章节来源**
|
||
- [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/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)
|
||
|
||
### 设备持久化与 OTA 升级设计理念
|
||
- 设备持久化:通过 Redis 缓存实现高并发设备上报,定时任务批量持久化降低数据库压力
|
||
- OTA 升级:支持灰度发布、强制升级、定向升级等多种发布策略,确保升级过程可控
|
||
- 数据一致性:通过唯一索引和事务保证数据完整性,通过分区表优化查询性能
|
||
- 可扩展性:模块化设计支持功能扩展,预留接口便于新业务场景接入
|
||
|
||
**新增** 设备持久化和 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/ota.go:6-42](file://internal/model/ota.go#L6-L42)
|
||
- [internal/task/device_persist.go:24-167](file://internal/task/device_persist.go#L24-L167)
|
||
- [internal/handler/ota.go:23-132](file://internal/handler/ota.go#L23-L132) |