Files
app-api/.qoder/repowiki/zh/content/核心模块/数据访问层.md
T
yangy 665397ede7 feat(ota): 增强设备管理与OTA功能模块
- 新增OTA固件升级处理器,支持固件升级请求及黑名单过滤
- 引入设备持久化仓储,支持设备信息上报与活动记录
- 扩展数据模型,新增UserDevice与UserActive,支持设备版本跟踪
- 实现Redis到MySQL的异步数据同步任务
- 更新路由配置,集成新的API端点以支持OTA功能
- 优化架构图,反映新增的数据流与处理流程
2026-05-31 09:52:11 +08:00

879 lines
45 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 数据访问层
<cite>
**本文引用的文件**
- [internal/repository/brand.go](file://internal/repository/brand.go)
- [internal/repository/model.go](file://internal/repository/model.go)
- [internal/repository/device.go](file://internal/repository/device.go)
- [internal/repository/ota.go](file://internal/repository/ota.go)
- [internal/cache/brand_cache.go](file://internal/cache/brand_cache.go)
- [internal/cache/model_cache.go](file://internal/cache/model_cache.go)
- [internal/cache/redis.go](file://internal/cache/redis.go)
- [internal/database/mysql.go](file://internal/database/mysql.go)
- [internal/config/database.go](file://internal/config/database.go)
- [internal/config/redis.go](file://internal/config/redis.go)
- [internal/config/config.go](file://internal/config/config.go)
- [internal/model/brand.go](file://internal/model/brand.go)
- [internal/model/model.go](file://internal/model/model.go)
- [internal/model/user_device.go](file://internal/model/user_device.go)
- [internal/model/user_active.go](file://internal/model/user_active.go)
- [internal/model/ota.go](file://internal/model/ota.go)
- [internal/handler/brand.go](file://internal/handler/brand.go)
- [internal/handler/model.go](file://internal/handler/model.go)
- [internal/handler/device.go](file://internal/handler/device.go)
- [internal/handler/ota.go](file://internal/handler/ota.go)
- [internal/router/router.go](file://internal/router/router.go)
- [internal/task/device_persist.go](file://internal/task/device_persist.go)
- [cmd/server/main.go](file://cmd/server/main.go)
- [sql/model.sql](file://sql/model.sql)
- [sql/user_device.sql](file://sql/user_device.sql)
- [sql/user_active.sql](file://sql/user_active.sql)
- [sql/ota.sql](file://sql/ota.sql)
- [sql/ota_target_device.sql](file://sql/ota_target_device.sql)
- [go.mod](file://go.mod)
</cite>
## 更新摘要
**变更内容**
- 新增设备持久化仓库(DeviceRepository)和OTA仓库(OTARepository),支持设备信息上报和固件升级查询
- 引入设备活跃信息持久化任务,实现Redis到MySQL的异步数据同步
- 新增用户设备和用户活跃两个核心数据模型,支持设备版本跟踪和用户活跃统计
- 扩展OTA查询逻辑,支持黑名单检查、定向设备筛选和灰度发布控制
- 更新架构图以反映新增的设备数据流和OTA查询流程
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [缓存策略详解](#缓存策略详解)
7. [依赖关系分析](#依赖关系分析)
8. [性能考量](#性能考量)
9. [故障排查指南](#故障排查指南)
10. [结论](#结论)
11. [附录:扩展新 Repository 指南](#附录扩展新-repository-指南)
## 简介
本文件聚焦于 Luxsin 应用 API 的数据访问层(Repository 层),系统性阐述 BrandRepository、ModelRepository、DeviceRepository 和 OTARepository 的设计模式、实现原理与职责边界;解释其在整体架构中的位置与交互方式;深入分析数据库连接管理、SQL 查询优化与事务处理机制;详细介绍新增的缓存感知查询逻辑、多级缓存策略和智能数据刷新机制;给出使用示例路径、错误处理与异常管理策略;总结并发安全、连接池管理与资源清理的最佳实践,并提供扩展新 Repository 的指导原则与注意事项。文档兼顾初学者与资深开发者的需求,既提供高层架构视图,也给出可落地的实现细节。
## 项目结构
数据访问层位于 internal/repository 目录,配合 internal/cache 提供缓存层,internal/database 提供底层数据库连接,internal/model 定义领域模型,internal/handler 通过注入的 Repository 执行业务逻辑,internal/task 提供设备数据持久化任务,最终由 Gin 路由暴露接口。
```mermaid
graph TB
subgraph "应用入口"
MAIN["cmd/server/main.go<br/>启动服务与依赖初始化"]
END
subgraph "路由与控制器"
ROUTER["internal/router/router.go<br/>注册路由与中间件"]
BRAND_H["internal/handler/brand.go<br/>品牌处理器"]
MODEL_H["internal/handler/model.go<br/>型号处理器"]
DEVICE_H["internal/handler/device.go<br/>设备处理器"]
OTA_H["internal/handler/ota.go<br/>OTA处理器"]
end
subgraph "数据访问层"
BR_REPO["internal/repository/brand.go<br/>BrandRepository"]
MD_REPO["internal/repository/model.go<br/>ModelRepository"]
DEV_REPO["internal/repository/device.go<br/>DeviceRepository"]
OTA_REPO["internal/repository/ota.go<br/>OTARepository"]
end
subgraph "缓存层"
BR_CACHE["internal/cache/brand_cache.go<br/>BrandCache"]
MD_CACHE["internal/cache/model_cache.go<br/>ModelCache"]
REDIS["internal/cache/redis.go<br/>Redis 客户端"]
end
subgraph "任务调度"
PERSIST_TASK["internal/task/device_persist.go<br/>设备持久化任务"]
end
subgraph "数据库与配置"
DB_SQL["internal/database/mysql.go<br/>sql.DB 连接与池配置"]
CFG_DB["internal/config/database.go<br/>数据库配置加载"]
CFG_REDIS["internal/config/redis.go<br/>Redis 配置加载"]
CFG_APP["internal/config/config.go<br/>应用配置聚合"]
MODEL_TBL["sql/model.sql<br/>表结构定义"]
USER_DEVICE_TBL["sql/user_device.sql<br/>用户设备表"]
USER_ACTIVE_TBL["sql/user_active.sql<br/>用户活跃表"]
OTA_TBL["sql/ota.sql<br/>OTA表"]
OTA_TARGET_TBL["sql/ota_target_device.sql<br/>OTA定向设备表"]
end
subgraph "领域模型"
M_BRAND["internal/model/brand.go<br/>Brand 模型"]
M_MODEL["internal/model/model.go<br/>Model 模型"]
M_USER_DEVICE["internal/model/user_device.go<br/>UserDevice 模型"]
M_USER_ACTIVE["internal/model/user_active.go<br/>UserActive 模型"]
M_OTA["internal/model/ota.go<br/>OTA 模型"]
end
MAIN --> ROUTER
ROUTER --> BRAND_H
ROUTER --> MODEL_H
ROUTER --> DEVICE_H
ROUTER --> OTA_H
BRAND_H --> BR_REPO
MODEL_H --> MD_REPO
DEVICE_H --> DEV_REPO
OTA_H --> OTA_REPO
BR_REPO --> BR_CACHE
MD_REPO --> MD_CACHE
BR_CACHE --> REDIS
MD_CACHE --> REDIS
DEV_REPO --> DB_SQL
OTA_REPO --> DB_SQL
PERSIST_TASK --> DEV_REPO
PERSIST_TASK --> REDIS
DB_SQL --> CFG_DB
CFG_APP --> CFG_DB
CFG_APP --> CFG_REDIS
BR_REPO --> M_BRAND
MD_REPO --> M_MODEL
DEV_REPO --> M_USER_DEVICE
DEV_REPO --> M_USER_ACTIVE
OTA_REPO --> M_OTA
MODEL_TBL --> DB_SQL
USER_DEVICE_TBL --> DB_SQL
USER_ACTIVE_TBL --> DB_SQL
OTA_TBL --> DB_SQL
OTA_TARGET_TBL --> DB_SQL
```
**图表来源**
- [cmd/server/main.go:103-130](file://cmd/server/main.go#L103-L130)
- [internal/router/router.go:22-25](file://internal/router/router.go#L22-L25)
- [internal/handler/brand.go:19-24](file://internal/handler/brand.go#L19-L24)
- [internal/handler/model.go:19-24](file://internal/handler/model.go#L19-L24)
- [internal/handler/device.go:15-25](file://internal/handler/device.go#L15-L25)
- [internal/handler/ota.go:14-21](file://internal/handler/ota.go#L14-L21)
- [internal/repository/brand.go:14-21](file://internal/repository/brand.go#L14-L21)
- [internal/repository/model.go:14-21](file://internal/repository/model.go#L14-L21)
- [internal/repository/device.go:11-17](file://internal/repository/device.go#L11-L17)
- [internal/repository/ota.go:11-17](file://internal/repository/ota.go#L11-L17)
- [internal/cache/brand_cache.go:18-24](file://internal/cache/brand_cache.go#L18-L24)
- [internal/cache/model_cache.go:19-25](file://internal/cache/model_cache.go#L19-L25)
- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16)
- [internal/database/mysql.go:14-46](file://internal/database/mysql.go#L14-L46)
- [internal/config/database.go:17-40](file://internal/config/database.go#L17-L40)
- [internal/config/redis.go:9-57](file://internal/config/redis.go#L9-L57)
- [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52)
- [internal/task/device_persist.go:14-22](file://internal/task/device_persist.go#L14-L22)
- [sql/model.sql:24-35](file://sql/model.sql#L24-L35)
- [sql/user_device.sql:23-32](file://sql/user_device.sql#L23-L32)
- [sql/user_active.sql:23-32](file://sql/user_active.sql#L23-L32)
- [sql/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)
**章节来源**
- [cmd/server/main.go:103-130](file://cmd/server/main.go#L103-L130)
- [internal/router/router.go:14-56](file://internal/router/router.go#L14-L56)
- [internal/handler/brand.go:19-24](file://internal/handler/brand.go#L19-L24)
- [internal/handler/model.go:19-24](file://internal/handler/model.go#L19-L24)
- [internal/handler/device.go:15-25](file://internal/handler/device.go#L15-L25)
- [internal/handler/ota.go:14-21](file://internal/handler/ota.go#L14-L21)
- [internal/repository/brand.go:14-21](file://internal/repository/brand.go#L14-L21)
- [internal/repository/model.go:14-21](file://internal/repository/model.go#L14-L21)
- [internal/repository/device.go:11-17](file://internal/repository/device.go#L11-L17)
- [internal/repository/ota.go:11-17](file://internal/repository/ota.go#L11-L17)
- [internal/cache/brand_cache.go:18-24](file://internal/cache/brand_cache.go#L18-L24)
- [internal/cache/model_cache.go:19-25](file://internal/cache/model_cache.go#L19-L25)
- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16)
- [internal/database/mysql.go:14-46](file://internal/database/mysql.go#L14-L46)
- [internal/config/database.go:17-40](file://internal/config/database.go#L17-L40)
- [internal/config/redis.go:9-57](file://internal/config/redis.go#L9-L57)
- [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52)
- [internal/task/device_persist.go:14-22](file://internal/task/device_persist.go#L14-L22)
- [sql/model.sql:24-35](file://sql/model.sql#L24-L35)
- [sql/user_device.sql:23-32](file://sql/user_device.sql#L23-L32)
- [sql/user_active.sql:23-32](file://sql/user_active.sql#L23-L32)
- [sql/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)
## 核心组件
- **BrandRepository**:负责品牌列表查询,支持缓存感知的全量查询和按名称模糊过滤,返回 Brand 领域对象切片。
- **ModelRepository**:负责型号列表查询,支持缓存感知的按品牌精确匹配、按型号名模糊匹配和全量查询,返回 Model 领域对象切片,并对可空字段进行 NullString 到指针字符串的安全转换。
- **DeviceRepository**:负责设备信息管理,支持按MAC地址查询设备、插入新设备、更新设备版本信息,以及用户活跃信息的查询和更新。
- **OTARepository**:负责固件升级信息查询,支持按型号、硬件版本、灰度标识查询最新OTA记录,黑名单检查、定向设备筛选等功能。
- **缓存层**BrandCache 和 ModelCache 提供多级缓存策略,支持全量缓存和按品牌缓存,具有智能刷新和降级机制。
- **设备持久化任务**DevicePersistTask 从Redis批量读取设备信息,异步写入MySQL user_device 和 user_active 表,支持数据可靠性保障。
- **数据库连接**:通过 sql.DB 统一管理连接池,设置最大打开连接数、空闲连接数与连接生命周期,并在启动时进行 Ping 校验。
- **配置加载**:从环境变量或默认值加载数据库和 Redis 配置,生产环境要求提供密码。
- **Handler 注入**Gin 控制器通过 NewXxxHandler 构造函数注入相应的 Repository 实例,再由 Handler 内部执行业务逻辑,形成清晰的依赖注入链路。
**章节来源**
- [internal/repository/brand.go:23-52](file://internal/repository/brand.go#L23-L52)
- [internal/repository/model.go:23-79](file://internal/repository/model.go#L23-L79)
- [internal/repository/device.go:11-88](file://internal/repository/device.go#L11-L88)
- [internal/repository/ota.go:11-159](file://internal/repository/ota.go#L11-L159)
- [internal/cache/brand_cache.go:26-44](file://internal/cache/brand_cache.go#L26-L44)
- [internal/cache/model_cache.go:31-69](file://internal/cache/model_cache.go#L31-L69)
- [internal/task/device_persist.go:14-167](file://internal/task/device_persist.go#L14-L167)
- [internal/database/mysql.go:14-46](file://internal/database/mysql.go#L14-L46)
- [internal/config/database.go:17-72](file://internal/config/database.go#L17-L72)
- [internal/config/redis.go:16-57](file://internal/config/redis.go#L16-L57)
- [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52)
- [internal/handler/brand.go:19-24](file://internal/handler/brand.go#L19-L24)
- [internal/handler/model.go:19-24](file://internal/handler/model.go#L19-L24)
- [internal/handler/device.go:15-25](file://internal/handler/device.go#L15-L25)
- [internal/handler/ota.go:14-21](file://internal/handler/ota.go#L14-L21)
## 架构总览
数据访问层采用"仓储模式"Repository Pattern)封装数据库访问,将查询逻辑与业务逻辑解耦。新增的缓存层提供多级缓存策略,支持智能数据刷新和降级机制。新增的设备持久化任务实现Redis到MySQL的异步数据同步。Handler 仅依赖 Repository 接口,Repository 依赖 *sql.DB 和缓存实例,配置与数据库模块负责基础设施初始化。整体流程如下:
```mermaid
sequenceDiagram
participant Client as "客户端"
participant Router as "Gin 路由"
participant DeviceHandler as "DeviceHandler"
participant OTAHandler as "OTAHandler"
participant DeviceRepo as "DeviceRepository"
participant OTARepo as "OTARepository"
participant DB as "sql.DB"
participant Redis as "Redis 服务器"
participant MySQL as "MySQL 服务器"
Client->>Router : "HTTP 请求"
Router->>DeviceHandler : "设备信息上报"
DeviceHandler->>Redis : "HSet devices"
Redis-->>DeviceHandler : "存储成功"
Note over DeviceHandler : "异步持久化任务"
DevicePersistTask->>Redis : "HGetAll devices"
Redis-->>DevicePersistTask : "批量数据"
DevicePersistTask->>DeviceRepo : "FindDeviceByMac"
DeviceRepo->>DB : "QueryContext"
DB-->>DeviceRepo : "设备信息"
alt 设备不存在
DevicePersistTask->>DeviceRepo : "InsertDevice"
DeviceRepo->>DB : "ExecContext"
DB-->>DeviceRepo : "插入成功"
else 设备存在
DevicePersistTask->>DeviceRepo : "UpdateDeviceVer"
DeviceRepo->>DB : "ExecContext"
DB-->>DeviceRepo : "更新成功"
end
Router->>OTAHandler : "OTA升级查询"
OTAHandler->>OTARepo : "GetLatestOTA"
OTARepo->>DB : "QueryRowContext"
DB-->>OTARepo : "最新OTA记录"
OTAHandler->>OTARepo : "IsInBlackList"
OTARepo->>DB : "QueryRowContext"
DB-->>OTARepo : "黑名单检查结果"
alt 在黑名单中
OTAHandler->>OTARepo : "GetLatestOTANotInBlackList"
OTARepo->>DB : "QueryRowContext"
DB-->>OTARepo : "非黑名单最新记录"
else 不在黑名单
OTAHandler->>OTARepo : "FindTargetDevice"
OTARepo->>DB : "QueryRowContext"
DB-->>OTARepo : "定向设备检查结果"
end
OTAHandler-->>Client : "OTA升级信息"
```
**图表来源**
- [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/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/task/device_persist.go:37-91](file://internal/task/device_persist.go#L37-L91)
- [internal/database/mysql.go:28-43](file://internal/database/mysql.go#L28-L43)
## 详细组件分析
### BrandRepository 设计与实现
- **设计模式**:仓储模式,面向领域模型 Brand,封装查询逻辑,现已集成缓存感知功能。
- **关键特性**
- 支持缓存感知的全量品牌查询,优先从 Redis 获取全量品牌数据
- 缓存未命中或 Redis 异常时自动降级到数据库查询
- 支持按品牌名称模糊过滤,无参数时返回全部记录并按名称升序排序
- 使用 QueryContext 传递请求上下文,便于超时与取消控制
- 使用 defer rows.Close() 确保资源释放
- 使用 fmt.Errorf 包裹底层错误,保留调用栈信息
- 缓存回写机制:成功从数据库获取数据后自动回写全量缓存
- **缓存策略**
- 缓存键:`brand:all`
- TTL30分钟
- 应用层过滤:在缓存命中时进行模糊过滤,减少数据库压力
- **错误处理**:对缓存读取、数据库查询、扫描、迭代阶段分别进行错误包装,便于定位问题。
```mermaid
classDiagram
class BrandRepository {
-db : "*sql.DB"
-cache : "*cache.BrandCache"
+NewBrandRepository(db, brandCache) BrandRepository
+List(ctx, brandName) []Brand,error
+ListFromDB(ctx, brandName) []Brand,error
-filterBrands(list, brandName) []Brand
}
class BrandCache {
-rdb : "*redis.Client"
+GetAll(ctx) []Brand,error
+SetAll(ctx, list) error
}
class Brand {
+int ID
+string Name
}
BrandRepository --> Brand : "返回领域对象"
BrandRepository --> BrandCache : "使用缓存"
BrandCache --> Brand : "返回缓存数据"
```
**图表来源**
- [internal/repository/brand.go:14-21](file://internal/repository/brand.go#L14-L21)
- [internal/repository/brand.go:23-52](file://internal/repository/brand.go#L23-L52)
- [internal/cache/brand_cache.go:18-24](file://internal/cache/brand_cache.go#L18-L24)
- [internal/model/brand.go:3-6](file://internal/model/brand.go#L3-L6)
**章节来源**
- [internal/repository/brand.go:14-21](file://internal/repository/brand.go#L14-L21)
- [internal/repository/brand.go:23-52](file://internal/repository/brand.go#L23-L52)
- [internal/cache/brand_cache.go:18-24](file://internal/cache/brand_cache.go#L18-L24)
- [internal/model/brand.go:3-6](file://internal/model/brand.go#L3-L6)
### ModelRepository 设计与实现
- **设计模式**:仓储模式,面向领域模型 Model,封装查询逻辑,现已集成缓存感知功能。
- **关键特性**
- 支持三种查询条件:按品牌精确匹配、按型号名模糊匹配、全量查询;两者皆为空时返回空切片
- 缓存感知查询:按品牌查询优先从缓存获取,按型号模糊查询从全量缓存过滤
- 缓存未命中或 Redis 异常时自动降级到数据库查询
- 使用自定义 scanModel(rows) 将 sql.NullString 安全转换为 *string,避免空值导致的序列化问题
- 使用 QueryContext 传递上下文,defer rows.Close() 保证资源释放
- 对扫描与迭代阶段进行错误包装
- 缓存回写机制:成功从数据库获取数据后自动回写相应缓存
- **缓存策略**
- 按品牌缓存:键格式为 `model:brand:{brandName}`TTL 30分钟
- 全量缓存:键为 `model:all`TTL 30分钟
- 应用层过滤:在全量缓存命中时进行模糊过滤,减少数据库压力
- **表结构要点**model 表包含唯一索引 model_namebrand_name, name),有利于去重与高效检索。
```mermaid
classDiagram
class ModelRepository {
-db : "*sql.DB"
-cache : "*cache.ModelCache"
+NewModelRepository(db, modelCache) ModelRepository
+List(ctx, brandName, modelName) []Model,error
+ListAllFromDB(ctx) []Model,error
+ListByBrandFromDB(ctx, brandName) []Model,error
+ListByModelFromDB(ctx, modelName) []Model,error
-filterModels(list, modelName) []Model
}
class ModelCache {
-rdb : "*redis.Client"
+GetByBrand(ctx, brandName) []Model,error
+SetByBrand(ctx, brandName, list) error
+GetAll(ctx) []Model,error
+SetAll(ctx, list) error
}
class Model {
+int ID
+string BrandName
+string Name
+*string Form
+*string Rig
+*string Source
+*string EqKey
+time CreateAt
}
ModelRepository --> Model : "返回领域对象"
ModelRepository --> ModelCache : "使用缓存"
ModelCache --> Model : "返回缓存数据"
```
**图表来源**
- [internal/repository/model.go:14-21](file://internal/repository/model.go#L14-L21)
- [internal/repository/model.go:23-79](file://internal/repository/model.go#L23-L79)
- [internal/cache/model_cache.go:19-25](file://internal/cache/model_cache.go#L19-L25)
- [internal/model/model.go:5-14](file://internal/model/model.go#L5-L14)
**章节来源**
- [internal/repository/model.go:14-21](file://internal/repository/model.go#L14-L21)
- [internal/repository/model.go:23-79](file://internal/repository/model.go#L23-L79)
- [internal/cache/model_cache.go:19-25](file://internal/cache/model_cache.go#L19-L25)
- [internal/model/model.go:5-14](file://internal/model/model.go#L5-L14)
- [sql/model.sql:34](file://sql/model.sql#L34)
### DeviceRepository 设计与实现
- **设计模式**:仓储模式,面向领域模型 UserDevice 和 UserActive,封装设备信息管理逻辑。
- **关键特性**
- 设备查询:按MAC地址精确查询用户设备信息,支持版本号的可空字段处理
- 设备管理:支持插入新设备、更新设备版本信息
- 用户活跃:按MAC地址和日期查询用户活跃信息,支持IP地址更新
- 可空字段处理:使用 sql.NullString 和 sql.NullTime 安全转换数据库可空字段
- 错误处理:针对不同查询场景提供详细的错误包装,便于问题定位
- 资源管理:使用 QueryRowContext 和 ExecContext,确保数据库连接正确释放
- **表结构要点**
- user_device 表:主键ID,唯一MAC地址索引,支持设备版本跟踪
- user_active 表:复合主键(ID, active_date),唯一MAC+日期索引,支持分区存储
- **应用场景**:设备信息上报、用户活跃统计、设备版本管理等业务场景。
```mermaid
classDiagram
class DeviceRepository {
-db : "*sql.DB"
+NewDeviceRepository(db) DeviceRepository
+FindDeviceByMac(ctx, macAddr) *UserDevice,error
+InsertDevice(ctx, d) error
+UpdateDeviceVer(ctx, id, ver) error
+FindActiveByMacAndDate(ctx, macAddr, activeDate) *UserActive,error
+InsertActive(ctx, a) error
+UpdateActiveIp(ctx, id, ipAddr) error
}
class UserDevice {
+int ID
+string MacAddr
+string Model
+time AddTime
+*string Ver
}
class UserActive {
+int ID
+string MacAddr
+string Model
+string ActiveDate
+string IpAddr
+time CreateAt
}
DeviceRepository --> UserDevice : "设备信息"
DeviceRepository --> UserActive : "活跃信息"
```
**图表来源**
- [internal/repository/device.go:11-17](file://internal/repository/device.go#L11-L17)
- [internal/repository/device.go:19-87](file://internal/repository/device.go#L19-L87)
- [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/repository/device.go:11-17](file://internal/repository/device.go#L11-L17)
- [internal/repository/device.go:19-87](file://internal/repository/device.go#L19-L87)
- [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)
- [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)
### OTARepository 设计与实现
- **设计模式**:仓储模式,面向领域模型 OTA、BlackList 和 OTATargetDevice,封装固件升级查询逻辑。
- **关键特性**
- 最新OTA查询:按型号、硬件版本、灰度标识查询最新一条可用OTA记录
- 黑名单检查:检查设备是否在指定OTA的黑名单中
- 非黑名单查询:查询不在黑名单中的最新OTA记录
- 非定向查询:查询非定向(target=0)的最新OTA记录
- 定向设备检查:检查设备是否在指定OTA的定向设备列表中
- 可空字段处理:使用 sql.NullString 和 sql.NullTime 安全处理数据库可空字段
- 复杂查询:支持子查询、条件筛选、时间范围检查等复杂业务逻辑
- **业务逻辑**
- 支持灰度发布(beta=1)和正式发布(beta=0)两种模式
- 支持强制升级(force=1)和普通升级(force=0
- 支持定向升级和非定向升级两种分发策略
- 支持时间窗口限制(startTime/endTime
- **应用场景**:固件升级推送、灰度发布控制、定向设备管理等。
```mermaid
classDiagram
class OTARepository {
-db : "*sql.DB"
+NewOTARepository(db) OTARepository
+GetLatestOTA(ctx, modelVal, hw, beta) *OTA,error
+GetLatestOTANotInBlackList(ctx, modelVal, hw, beta, mac) *OTA,error
+GetLatestOTANotTarget(ctx, modelVal, hw, beta) *OTA,error
+IsInBlackList(ctx, otaID, mac) bool,error
+FindTargetDevice(ctx, otaID, mac) *OTATargetDevice,error
}
class OTA {
+int ID
+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
+*time StartTime
+*time EndTime
+int Status
}
class BlackList {
+int ID
+int OTAID
+string Mac
+time CreateAt
}
class OTATargetDevice {
+int ID
+int OTAID
+string MacAddr
+int Type
+time CreateAt
}
OTARepository --> OTA : "OTA记录"
OTARepository --> BlackList : "黑名单"
OTARepository --> OTATargetDevice : "定向设备"
```
**图表来源**
- [internal/repository/ota.go:11-17](file://internal/repository/ota.go#L11-L17)
- [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)
**章节来源**
- [internal/repository/ota.go:11-17](file://internal/repository/ota.go#L11-L17)
- [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)
- [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)
### 设备持久化任务设计与实现
- **设计模式**:定时任务模式,基于时间轮询的异步数据同步机制。
- **关键特性**
- 定时执行:使用 time.Ticker 按固定间隔启动持久化任务
- 批量处理:从Redis Hash一次性读取所有待持久化的设备数据
- 数据修复:自动修复历史数据中的无效日期格式
- 双表写入:同时处理 user_device 和 user_active 两张表
- 成功确认:批量删除已成功持久化的数据,避免重复处理
- 错误隔离:单条数据处理失败不影响其他数据的处理
- **处理流程**
1. 从Redis读取 devices Hash 中的所有设备数据
2. 解析JSON格式的设备信息
3. 修复无效的日期格式
4. 分别处理设备信息和活跃信息
5. 批量删除已成功处理的数据
- **应用场景**:高并发设备信息上报、数据可靠性保障、异步数据同步等。
```mermaid
flowchart TD
A[定时器触发] --> B[HGetAll devices]
B --> C{是否有数据?}
C --> |否| D[结束]
C --> |是| E[遍历设备数据]
E --> F[解析JSON数据]
F --> G[修复日期格式]
G --> H[处理设备信息]
H --> I[处理活跃信息]
I --> J{处理成功?}
J --> |是| K[记录成功MAC]
J --> |否| L[跳过该设备]
K --> M[批量删除成功数据]
L --> N[继续下一个]
M --> O[记录处理结果]
N --> E
O --> P[结束]
```
**图表来源**
- [internal/task/device_persist.go:24-91](file://internal/task/device_persist.go#L24-L91)
**章节来源**
- [internal/task/device_persist.go:14-167](file://internal/task/device_persist.go#L14-L167)
### Handler 与 Repository 的协作
- Handler 通过 NewXxxHandler 注入相应的 Repository 实例,内部执行具体的业务逻辑。
- DeviceHandler 处理设备信息上报,将数据写入Redis Hash,等待异步持久化任务处理。
- OTAHandler 处理OTA升级查询,调用OTARepository执行复杂的业务逻辑,包括黑名单检查、定向设备筛选等。
- BrandHandler 和 ModelHandler 继续使用原有的缓存感知查询逻辑。
- 错误处理:各 Handler 对 Repository 返回的错误进行统一处理和日志记录。
- 缓存感知:DeviceHandler 和 OTAHandler 不需要关心缓存逻辑,只需调用相应的 Repository 方法。
```mermaid
sequenceDiagram
participant C as "客户端"
participant DH as "DeviceHandler"
participant DR as "DeviceRepository"
participant RT as "Redis 服务器"
C->>DH : "POST /audio/reportDevInfo"
DH->>RT : "HSet devices"
RT-->>DH : "存储成功"
DH-->>C : "操作成功"
participant OH as "OTAHandler"
participant OR as "OTARepository"
OH->>OR : "GetLatestOTA"
OR-->>OH : "最新OTA记录"
OH->>OR : "IsInBlackList"
OR-->>OH : "黑名单检查结果"
alt 在黑名单中
OH->>OR : "GetLatestOTANotInBlackList"
OR-->>OH : "非黑名单最新记录"
else 不在黑名单
OH->>OR : "FindTargetDevice"
OR-->>OH : "定向设备检查结果"
end
OH-->>C : "OTA升级信息"
```
**图表来源**
- [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/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/database/mysql.go:28-43](file://internal/database/mysql.go#L28-L43)
**章节来源**
- [internal/handler/brand.go:19-24](file://internal/handler/brand.go#L19-L24)
- [internal/handler/brand.go:25-48](file://internal/handler/brand.go#L25-L48)
- [internal/handler/model.go:19-24](file://internal/handler/model.go#L19-L24)
- [internal/handler/model.go:25-49](file://internal/handler/model.go#L25-L49)
- [internal/handler/device.go:15-25](file://internal/handler/device.go#L15-L25)
- [internal/handler/device.go:27-82](file://internal/handler/device.go#L27-L82)
- [internal/handler/ota.go:14-21](file://internal/handler/ota.go#L14-L21)
- [internal/handler/ota.go:23-132](file://internal/handler/ota.go#L23-L132)
- [internal/repository/brand.go:23-52](file://internal/repository/brand.go#L23-L52)
- [internal/repository/model.go:23-79](file://internal/repository/model.go#L23-L79)
- [internal/repository/device.go:19-87](file://internal/repository/device.go#L19-L87)
- [internal/repository/ota.go:19-158](file://internal/repository/ota.go#L19-L158)
## 缓存策略详解
### 多级缓存架构
数据访问层采用了多级缓存策略,分为以下层级:
1. **应用层缓存**:Redis 缓存,提供高性能的数据访问
2. **内存缓存**:应用进程内的缓存,提供最快的访问速度
3. **数据库缓存**:MySQL 查询缓存,利用数据库自身的缓存机制
### 缓存键设计
- **品牌全量缓存**`brand:all` - 存储所有品牌信息
- **型号按品牌缓存**`model:brand:{brandName}` - 存储特定品牌的所有型号
- **型号全量缓存**`model:all` - 存储所有型号信息
- **设备活跃缓存**`device:active:{date}` - 存储特定日期的活跃设备信息
### 缓存策略实现
- **缓存优先策略**:优先从缓存获取数据,缓存未命中时才查询数据库
- **智能降级策略**:Redis 异常时自动降级到数据库查询,保证系统可用性
- **自动回写策略**:成功从数据库获取数据后自动回写到缓存
- **TTL 策略**:所有缓存设置 30 分钟过期时间,平衡性能和数据新鲜度
- **设备数据特殊处理**:设备活跃信息采用分区表存储,缓存策略需考虑日期维度
### 预热机制
应用启动时会自动预热缓存,从数据库加载全量数据到 Redis:
```mermaid
flowchart TD
A[应用启动] --> B[创建缓存实例]
B --> C[创建仓库实例]
C --> D[查询品牌全量数据]
D --> E[写入 brand:all 缓存]
E --> F[查询型号全量数据]
F --> G[写入 model:all 缓存]
G --> H[设备数据无需预热]
H --> I[OTA数据无需预热]
I --> J[缓存预热完成]
```
**图表来源**
- [cmd/server/main.go:103-130](file://cmd/server/main.go#L103-L130)
**章节来源**
- [internal/cache/brand_cache.go:13-16](file://internal/cache/brand_cache.go#L13-L16)
- [internal/cache/model_cache.go:13-16](file://internal/cache/model_cache.go#L13-L16)
- [cmd/server/main.go:103-130](file://cmd/server/main.go#L103-L130)
## 依赖关系分析
- **外部依赖**Go MySQL Driver、Gin、Zap 日志、Redis 客户端、Meilisearch 客户端。
- **内部依赖**Handler 依赖 RepositoryRepository 依赖 *sql.DB 和缓存实例;缓存层依赖 Redis 客户端;数据库模块负责连接池与 Ping 校验;配置模块负责环境变量解析与校验;任务模块依赖 Redis 和 Repository。
- **循环依赖**:未发现循环依赖,职责边界清晰。
- **新增依赖**DeviceRepository 和 OTARepository 直接依赖 *sql.DB;设备持久化任务依赖 Redis 和 RepositoryOTA 查询逻辑依赖复杂的表关联。
```mermaid
graph LR
GO_MOD["go.mod 依赖声明"] --> MYSQL["github.com/go-sql-driver/mysql"]
GO_MOD --> GIN["github.com/gin-gonic/gin"]
GO_MOD --> ZAP["go.uber.org/zap"]
GO_MOD --> REDIS["github.com/redis/go-redis/v9"]
GO_MOD --> MEILI["github.com/meilisearch/meilisearch-go"]
MAIN["cmd/server/main.go"] --> DB_OPEN["internal/database/mysql.go::Open"]
MAIN --> CFG_LOAD["internal/config/config.go::Load"]
MAIN --> WARM_UP["warmUpCache 预热缓存"]
CFG_LOAD --> CFG_DB["internal/config/database.go::loadDatabase"]
CFG_LOAD --> CFG_REDIS["internal/config/redis.go::loadRedis"]
ROUTER["internal/router/router.go"] --> BRAND_H["internal/handler/brand.go"]
ROUTER --> MODEL_H["internal/handler/model.go"]
ROUTER --> DEVICE_H["internal/handler/device.go"]
ROUTER --> OTA_H["internal/handler/ota.go"]
BRAND_H --> BR_REPO["internal/repository/brand.go"]
MODEL_H --> MD_REPO["internal/repository/model.go"]
DEVICE_H --> DEV_REPO["internal/repository/device.go"]
OTA_H --> OTA_REPO["internal/repository/ota.go"]
BR_REPO --> BR_CACHE["internal/cache/brand_cache.go"]
MD_REPO --> MD_CACHE["internal/cache/model_cache.go"]
BR_CACHE --> REDIS_CLIENT["internal/cache/redis.go::NewClient"]
MD_CACHE --> REDIS_CLIENT
DEV_REPO --> DB_SQL["*sql.DB"]
OTA_REPO --> DB_SQL
PERSIST_TASK --> DEV_REPO
PERSIST_TASK --> REDIS_CLIENT
BR_CACHE --> REDIS_CLIENT
MD_CACHE --> REDIS_CLIENT
```
**图表来源**
- [go.mod:5-11](file://go.mod#L5-L11)
- [cmd/server/main.go:25-69](file://cmd/server/main.go#L25-L69)
- [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52)
- [internal/config/database.go:17-40](file://internal/config/database.go#L17-L40)
- [internal/config/redis.go:16-57](file://internal/config/redis.go#L16-L57)
- [internal/router/router.go:22-25](file://internal/router/router.go#L22-L25)
- [internal/handler/brand.go:19-24](file://internal/handler/brand.go#L19-L24)
- [internal/handler/model.go:19-24](file://internal/handler/model.go#L19-L24)
- [internal/handler/device.go:15-25](file://internal/handler/device.go#L15-L25)
- [internal/handler/ota.go:14-21](file://internal/handler/ota.go#L14-L21)
- [internal/repository/brand.go:14-21](file://internal/repository/brand.go#L14-L21)
- [internal/repository/model.go:14-21](file://internal/repository/model.go#L14-L21)
- [internal/repository/device.go:11-17](file://internal/repository/device.go#L11-L17)
- [internal/repository/ota.go:11-17](file://internal/repository/ota.go#L11-L17)
- [internal/cache/brand_cache.go:18-24](file://internal/cache/brand_cache.go#L18-L24)
- [internal/cache/model_cache.go:19-25](file://internal/cache/model_cache.go#L19-L25)
**章节来源**
- [go.mod:5-11](file://go.mod#L5-L11)
- [cmd/server/main.go:25-69](file://cmd/server/main.go#L25-L69)
- [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52)
- [internal/config/database.go:17-40](file://internal/config/database.go#L17-L40)
- [internal/config/redis.go:16-57](file://internal/config/redis.go#L16-L57)
- [internal/router/router.go:22-25](file://internal/router/router.go#L22-L25)
- [internal/handler/brand.go:19-24](file://internal/handler/brand.go#L19-L24)
- [internal/handler/model.go:19-24](file://internal/handler/model.go#L19-L24)
- [internal/handler/device.go:15-25](file://internal/handler/device.go#L15-L25)
- [internal/handler/ota.go:14-21](file://internal/handler/ota.go#L14-L21)
- [internal/repository/brand.go:14-21](file://internal/repository/brand.go#L14-L21)
- [internal/repository/model.go:14-21](file://internal/repository/model.go#L14-L21)
- [internal/repository/device.go:11-17](file://internal/repository/device.go#L11-L17)
- [internal/repository/ota.go:11-17](file://internal/repository/ota.go#L11-L17)
- [internal/cache/brand_cache.go:18-24](file://internal/cache/brand_cache.go#L18-L24)
- [internal/cache/model_cache.go:19-25](file://internal/cache/model_cache.go#L19-L25)
## 性能考量
- **连接池配置**
- 最大打开连接数:25
- 最大空闲连接数:5
- 连接最大生命周期:5 分钟
- 启动时使用 PingContext 进行健康检查,失败则关闭连接并返回错误
- **缓存性能优化**
- 缓存命中率:通过多级缓存策略最大化缓存命中率
- TTL 设置:30分钟的TTL平衡了性能和数据新鲜度
- 预热机制:启动时预热缓存,避免冷启动时的缓存穿透
- 降级机制:Redis异常时自动降级到数据库,保证系统可用性
- **查询优化**
- BrandRepository:按名称模糊匹配,建议在 name 上建立合适索引;对大结果集引入分页。
- ModelRepository:按品牌名精确匹配或按型号名模糊匹配,建议为 brand_name 与 name 建立索引;对大结果集引入分页。
- DeviceRepositoryuser_device 表的 MAC 地址建立唯一索引,user_active 表建立复合索引(mac_addr, active_date)。
- OTARepositoryota 表建立复合索引(model, hw, beta, status),black_list 表建立索引(ota_id, mac),ota_target_device 表建立索引(ota_id, mac_addr)。
- 应用层过滤:在缓存命中时进行过滤,减少数据库压力。
- **并发安全**
- *sql.DB 是并发安全的,可在多个 goroutine 中共享使用;Repository 实例不持有状态,亦可并发安全使用。
- Redis 客户端是并发安全的,支持多goroutine同时访问。
- 设备持久化任务使用独立的 goroutine 执行,避免阻塞主线程。
- **资源清理**
- 使用 defer rows.Close() 保证结果集关闭;在 main 中 defer db.Close() 保证应用退出时关闭连接池。
- Redis 连接在应用退出时自动关闭。
- 设备持久化任务使用 time.Ticker,确保正确停止。
- **事务处理**
- 当前实现均为只读查询,未涉及事务;如需写操作,应在 Repository 层封装事务,使用 sql.Tx 并在错误时回滚,成功时提交。
- 设备持久化任务采用幂等设计,避免重复处理造成的数据不一致。
- **异步处理优化**
- 设备信息上报采用异步持久化,提高响应速度
- Redis Hash 存储大量设备数据,内存占用较高,需监控内存使用情况
- 定时任务间隔可根据数据量调整,平衡实时性和系统负载
**章节来源**
- [internal/database/mysql.go:33-43](file://internal/database/mysql.go#L33-L43)
- [internal/cache/brand_cache.go:13-16](file://internal/cache/brand_cache.go#L13-L16)
- [internal/cache/model_cache.go:13-16](file://internal/cache/model_cache.go#L13-L16)
- [internal/repository/brand.go:23-52](file://internal/repository/brand.go#L23-L52)
- [internal/repository/model.go:23-79](file://internal/repository/model.go#L23-L79)
- [internal/repository/device.go:19-87](file://internal/repository/device.go#L19-L87)
- [internal/repository/ota.go:19-158](file://internal/repository/ota.go#L19-L158)
- [cmd/server/main.go:103-130](file://cmd/server/main.go#L103-L130)
## 故障排查指南
- **连接失败**
- 现象:启动时 Ping 失败或无法连接数据库。
- 排查:检查 DATABASE_HOST/DATABASE_PORT/DATABASE_NAME/DATABASE_USER/DATABASE_PASSWORD 等环境变量;确认网络连通性;核对生产环境必须提供 DATABASE_PASSWORD。
- **Redis 连接失败**
- 现象:应用启动时无法连接 Redis 或缓存预热失败。
- 排查:检查 REDIS_HOST/REDIS_PORT/REDIS_PASSWORD/REDIS_DATABASE 等环境变量;确认 Redis 服务正常运行;验证认证信息正确。
- **设备持久化失败**
- 现象:设备信息上报成功但数据库中无数据。
- 排查:检查设备持久化任务日志,确认Redis连接正常;验证user_device和user_active表结构;检查JSON数据格式;确认定时任务正常运行。
- **OTA查询错误**
- 现象:OTA升级查询返回错误或结果不符合预期。
- 排查:检查OTA表数据完整性,确认status=1且有效时间范围内;验证黑名单和定向设备表数据;检查参数传递(model、hw、beta、mac)。
- **缓存查询错误**
- 现象:Handler 返回内部错误或缓存数据格式异常。
- 排查:查看日志中错误上下文(cache read/write/brand/model),定位具体环节;检查 Redis 连接状态;确认缓存数据格式正确。
- **查询错误**
- 现象:Handler 返回内部错误。
- 排查:查看日志中错误上下文(query brand/query model/device/ota/scan brand/scan model/iterate brand/iterate model),定位具体环节;检查 SQL 参数绑定与字段映射。
- **结果为空**
- 现象:DeviceRepository 在查询设备或活跃信息时返回空值。
- 排查:确认传入的查询参数是否正确;检查表中是否存在匹配数据;验证MAC地址格式。
- **资源泄漏**
- 现象:长时间运行后连接数异常。
- 排查:确认是否遗漏 rows.Close();检查连接池配置是否合理;观察连接生命周期与空闲连接上限。
- **缓存未命中**
- 现象:频繁出现缓存未命中,数据库压力过大。
- 排查:检查缓存键是否正确;确认 TTL 设置是否合理;验证缓存预热是否成功;检查 Redis 内存使用情况。
- **性能问题**
- 现象:OTA查询响应慢或设备持久化延迟。
- 排查:检查相关表的索引是否完整;监控Redis内存使用;评估定时任务执行频率;分析数据库查询计划。
**章节来源**
- [internal/config/database.go:17-72](file://internal/config/database.go#L17-L72)
- [internal/config/redis.go:16-57](file://internal/config/redis.go#L16-L57)
- [internal/handler/brand.go:25-48](file://internal/handler/brand.go#L25-L48)
- [internal/handler/model.go:25-49](file://internal/handler/model.go#L25-L49)
- [internal/handler/device.go:27-82](file://internal/handler/device.go#L27-L82)
- [internal/handler/ota.go:23-132](file://internal/handler/ota.go#L23-L132)
- [internal/repository/brand.go:23-52](file://internal/repository/brand.go#L23-L52)
- [internal/repository/model.go:23-79](file://internal/repository/model.go#L23-L79)
- [internal/repository/device.go:19-87](file://internal/repository/device.go#L19-L87)
- [internal/repository/ota.go:19-158](file://internal/repository/ota.go#L19-L158)
## 结论
Luxsin 的数据访问层经过重构后,采用了更加完善的缓存感知查询逻辑,实现了多级缓存策略和智能数据刷新机制。新增的设备持久化仓库和OTA仓库进一步完善了数据访问层的功能,支持设备信息上报、固件升级查询等核心业务场景。通过集成 Redis 缓存层和异步持久化任务,显著提升了系统的响应性能和数据可靠性。缓存感知查询逻辑、智能降级策略、预热机制、自动回写功能和异步数据同步共同构成了一个健壮、高效的现代化数据访问层。建议后续继续优化缓存策略,增加缓存监控指标,完善缓存失效和更新机制,并持续改进监控与日志体系。
## 附录:扩展新 Repository 指南
- **设计原则**
- 保持 Repository 无状态,仅依赖 *sql.DB 和缓存实例。
- 查询方法接收 context.Context,便于超时与取消控制。
- 对外返回领域模型(Model),避免直接暴露数据库结构。
- 对可空字段使用 sql.NullString、sql.NullInt64、sql.NullTime 等安全转换。
- 集成缓存感知查询逻辑,提供智能降级和自动回写功能。
- 考虑异步处理场景,设计幂等的持久化逻辑。
- **实现步骤**
- 定义领域模型(Model)与 Repository 接口/实现。
- 创建对应的缓存结构体,定义缓存键和TTL策略。
- 在 Handler 中注入 *sql.DB 和缓存实例,构造 Repository 实例。
- 在路由中注册对应处理器。
- 在 main 中确保 *sql.DB 和缓存实例注入到 Handler。
- 实现缓存预热逻辑,确保应用启动时缓存可用。
- 如涉及异步处理,实现独立的任务调度器。
- **注意事项**
- 必须在每个查询后 defer rows.Close()。
- 使用 fmt.Errorf 包裹底层错误,保留调用栈信息。
- 生产环境务必提供数据库密码等敏感配置。
- 如需写操作,封装事务并在错误时回滚,成功时提交。
- 对高频查询建立合适的索引,必要时引入分页参数。
- 实现缓存降级机制,确保Redis异常时系统仍可正常工作。
- 设计合理的缓存键和TTL策略,平衡性能和数据新鲜度。
- 实现缓存预热和自动回写机制,提升用户体验。
- 异步任务需考虑幂等性,避免重复处理造成数据不一致。
- 监控异步任务的执行状态和错误日志。
**章节来源**
- [internal/repository/brand.go:14-21](file://internal/repository/brand.go#L14-L21)
- [internal/repository/model.go:14-21](file://internal/repository/model.go#L14-L21)
- [internal/repository/device.go:11-17](file://internal/repository/device.go#L11-L17)
- [internal/repository/ota.go:11-17](file://internal/repository/ota.go#L11-L17)
- [internal/cache/brand_cache.go:18-24](file://internal/cache/brand_cache.go#L18-L24)
- [internal/cache/model_cache.go:19-25](file://internal/cache/model_cache.go#L19-L25)
- [internal/handler/brand.go:19-24](file://internal/handler/brand.go#L19-L24)
- [internal/handler/model.go:19-24](file://internal/handler/model.go#L19-L24)
- [internal/handler/device.go:15-25](file://internal/handler/device.go#L15-L25)
- [internal/handler/ota.go:14-21](file://internal/handler/ota.go#L14-L21)
- [internal/router/router.go:22-25](file://internal/router/router.go#L22-L25)
- [cmd/server/main.go:103-130](file://cmd/server/main.go#L103-L130)