505 lines
25 KiB
Markdown
505 lines
25 KiB
Markdown
# 数据库设计
|
||
|
||
<cite>
|
||
**本文引用的文件**
|
||
- [backend/src/models/Brand.js](file://backend/src/models/Brand.js)
|
||
- [backend/src/models/Model.js](file://backend/src/models/Model.js)
|
||
- [backend/src/models/Ota.js](file://backend/src/models/Ota.js)
|
||
- [backend/src/models/DashboardUser.js](file://backend/src/models/DashboardUser.js)
|
||
- [backend/src/models/ShareCodeLog.js](file://backend/src/models/ShareCodeLog.js)
|
||
- [backend/src/models/index.js](file://backend/src/models/index.js)
|
||
- [backend/src/config/database.js](file://backend/src/config/database.js)
|
||
- [backend/src/validators/brand.js](file://backend/src/validators/brand.js)
|
||
- [backend/src/validators/model.js](file://backend/src/validators/model.js)
|
||
- [backend/src/validators/ota.js](file://backend/src/validators/ota.js)
|
||
- [backend/src/services/eqCacheStorage.js](file://backend/src/services/eqCacheStorage.js)
|
||
- [backend/src/services/otaStorage.js](file://backend/src/services/otaStorage.js)
|
||
- [backend/src/routes/brands.js](file://backend/src/routes/brands.js)
|
||
- [backend/src/routes/models.js](file://backend/src/routes/models.js)
|
||
- [backend/src/routes/ota.js](file://backend/src/routes/ota.js)
|
||
</cite>
|
||
|
||
## 目录
|
||
1. [简介](#简介)
|
||
2. [项目结构](#项目结构)
|
||
3. [核心组件](#核心组件)
|
||
4. [架构总览](#架构总览)
|
||
5. [详细组件分析](#详细组件分析)
|
||
6. [依赖分析](#依赖分析)
|
||
7. [性能考量](#性能考量)
|
||
8. [故障排查指南](#故障排查指南)
|
||
9. [结论](#结论)
|
||
10. [附录](#附录)
|
||
|
||
## 简介
|
||
本文件系统性梳理后端数据库模型与相关数据访问模式,覆盖实体关系、字段定义、数据类型、主键/外键、索引与约束、数据验证与业务规则,并给出数据库模式图、示例数据、缓存策略、性能优化建议、数据生命周期与归档策略、迁移与版本管理思路,以及数据安全与访问控制要点。重点实体包括 Brand、Model、Ota、DashboardUser、ShareCodeLog。
|
||
|
||
## 项目结构
|
||
- 数据模型采用 Sequelize ORM 定义,位于 backend/src/models,统一通过 backend/src/models/index.js 暴露。
|
||
- 数据库连接在 backend/src/config/database.js 中配置,使用 MySQL,关闭默认时间戳与表名冻结,便于与现有表结构对齐。
|
||
- 校验层使用 Zod 在 backend/src/validators 下定义,确保入参合法性。
|
||
- 业务访问通过 Express 路由在 backend/src/routes 下实现,结合服务层完成复杂流程(如 OTA 包上传、S3 存储、Redis 缓存读取等)。
|
||
|
||
```mermaid
|
||
graph TB
|
||
subgraph "模型层"
|
||
M_Brand["Brand<br/>品牌"]
|
||
M_Model["Model<br/>型号"]
|
||
M_Ota["Ota<br/>OTA 版本"]
|
||
M_User["DashboardUser<br/>后台用户"]
|
||
M_Share["ShareCodeLog<br/>分享码日志"]
|
||
end
|
||
subgraph "配置与入口"
|
||
C_DB["database.js<br/>MySQL 连接"]
|
||
I_Index["models/index.js<br/>模型导出"]
|
||
end
|
||
subgraph "校验层"
|
||
V_Brand["validators/brand.js"]
|
||
V_Model["validators/model.js"]
|
||
V_Ota["validators/ota.js"]
|
||
end
|
||
subgraph "服务层"
|
||
S_Eq["eqCacheStorage.js<br/>Redis EQ 缓存"]
|
||
S_Ota["otaStorage.js<br/>OTA 存储(S3/本地)"]
|
||
end
|
||
subgraph "路由层"
|
||
R_Brand["routes/brands.js"]
|
||
R_Model["routes/models.js"]
|
||
R_Ota["routes/ota.js"]
|
||
end
|
||
C_DB --> M_Brand
|
||
C_DB --> M_Model
|
||
C_DB --> M_Ota
|
||
C_DB --> M_User
|
||
C_DB --> M_Share
|
||
I_Index --> M_Brand
|
||
I_Index --> M_Model
|
||
I_Index --> M_Ota
|
||
I_Index --> M_User
|
||
I_Index --> M_Share
|
||
V_Brand --> R_Brand
|
||
V_Model --> R_Model
|
||
V_Ota --> R_Ota
|
||
R_Model --> S_Eq
|
||
R_Ota --> S_Ota
|
||
```
|
||
|
||
图表来源
|
||
- [backend/src/config/database.js:1-24](file://backend/src/config/database.js#L1-L24)
|
||
- [backend/src/models/index.js:1-8](file://backend/src/models/index.js#L1-L8)
|
||
- [backend/src/models/Brand.js:1-23](file://backend/src/models/Brand.js#L1-L23)
|
||
- [backend/src/models/Model.js:1-53](file://backend/src/models/Model.js#L1-L53)
|
||
- [backend/src/models/Ota.js:1-97](file://backend/src/models/Ota.js#L1-L97)
|
||
- [backend/src/models/DashboardUser.js:1-58](file://backend/src/models/DashboardUser.js#L1-L58)
|
||
- [backend/src/models/ShareCodeLog.js:1-60](file://backend/src/models/ShareCodeLog.js#L1-L60)
|
||
- [backend/src/validators/brand.js:1-12](file://backend/src/validators/brand.js#L1-L12)
|
||
- [backend/src/validators/model.js:1-22](file://backend/src/validators/model.js#L1-L22)
|
||
- [backend/src/validators/ota.js:1-36](file://backend/src/validators/ota.js#L1-L36)
|
||
- [backend/src/services/eqCacheStorage.js:1-73](file://backend/src/services/eqCacheStorage.js#L1-L73)
|
||
- [backend/src/services/otaStorage.js:1-113](file://backend/src/services/otaStorage.js#L1-L113)
|
||
- [backend/src/routes/brands.js:1-147](file://backend/src/routes/brands.js#L1-L147)
|
||
- [backend/src/routes/models.js:1-569](file://backend/src/routes/models.js#L1-L569)
|
||
- [backend/src/routes/ota.js:1-292](file://backend/src/routes/ota.js#L1-L292)
|
||
|
||
章节来源
|
||
- [backend/src/config/database.js:1-24](file://backend/src/config/database.js#L1-L24)
|
||
- [backend/src/models/index.js:1-8](file://backend/src/models/index.js#L1-L8)
|
||
|
||
## 核心组件
|
||
本节聚焦五个核心实体的字段、类型、约束与业务含义。
|
||
|
||
- 品牌 Brand
|
||
- 字段与类型:id(INTEGER, 主键, 自增)、name(STRING(100), 唯一, 非空)
|
||
- 约束:唯一索引(name)
|
||
- 业务规则:品牌名称唯一;用于型号归属
|
||
- 参考路径:[Brand 模型定义:1-23](file://backend/src/models/Brand.js#L1-L23)
|
||
|
||
- 型号 Model
|
||
- 字段与类型:id(INTEGER, 主键, 自增)、brand_name(STRING(100), 非空)、name(STRING(100), 非空)、form(STRING(100))、rig(STRING(100))、source(STRING(100))、eq_key(STRING(255))、create_at(DATE, 默认 NOW)
|
||
- 约束:无显式外键;但业务上以 brand_name 关联品牌
|
||
- 业务规则:同一品牌下型号名称唯一;create_at 默认当前时间
|
||
- 参考路径:[Model 模型定义:1-53](file://backend/src/models/Model.js#L1-L53)
|
||
|
||
- OTA 版本 Ota
|
||
- 字段与类型:id(INTEGER, 主键, 自增)、verCode(INTEGER, 非空)、verName(STRING(20), 非空)、url(STRING(255), 非空)、md5(STRING(32), 非空)、force(SMALLINT, 默认0)、desc(STRING(255))、model(STRING(100))、hw(INTEGER, 默认0)、target(SMALLINT, 默认0)、beta(SMALLINT, 默认0)、startTime/endTime(DATE)、status(SMALLINT, 默认1)、create_at(DATE, 默认 NOW)
|
||
- 约束:verCode+model 组合唯一(业务逻辑保证)
|
||
- 业务规则:verCode 必须大于当前设备版本才视为可用升级;target/beta 控制定向/灰度;status=1 表示可用
|
||
- 参考路径:[Ota 模型定义:1-97](file://backend/src/models/Ota.js#L1-L97)
|
||
|
||
- 后台用户 DashboardUser
|
||
- 字段与类型:id(INTEGER, 主键, 自增)、username(STRING(64), 唯一, 非空)、password_hash(STRING(255), 非空)、is_super_admin(TINYINT, 默认0)、status(TINYINT, 默认1)、last_login_at(DATE)、create_at/update_at(DATE, 默认 NOW)
|
||
- 约束:username 唯一
|
||
- 业务规则:is_super_admin=1 表示超级管理员;status=1 启用
|
||
- 参考路径:[DashboardUser 模型定义:1-58](file://backend/src/models/DashboardUser.js#L1-L58)
|
||
|
||
- 分享码日志 ShareCodeLog
|
||
- 字段与类型:id(INTEGER, 主键, 自增)、mac_addr(STRING(17), 非空)、share_code(CHAR(5), 非空)、action(ENUM('export','import'), 非空)、ip_addr(STRING(45), 默认'')、eq_data(JSON, 非空)、expire_at(DATE)、create_at(DATE, 默认 NOW)
|
||
- 索引:idx_mac_addr、idx_share_code、idx_create_at
|
||
- 业务规则:记录导出/导入分享码的操作明细;expire_at 与导出快照关联
|
||
- 参考路径:[ShareCodeLog 模型定义:1-60](file://backend/src/models/ShareCodeLog.js#L1-L60)
|
||
|
||
章节来源
|
||
- [backend/src/models/Brand.js:1-23](file://backend/src/models/Brand.js#L1-L23)
|
||
- [backend/src/models/Model.js:1-53](file://backend/src/models/Model.js#L1-L53)
|
||
- [backend/src/models/Ota.js:1-97](file://backend/src/models/Ota.js#L1-L97)
|
||
- [backend/src/models/DashboardUser.js:1-58](file://backend/src/models/DashboardUser.js#L1-L58)
|
||
- [backend/src/models/ShareCodeLog.js:1-60](file://backend/src/models/ShareCodeLog.js#L1-L60)
|
||
|
||
## 架构总览
|
||
数据库层采用 MySQL,ORM 层为 Sequelize。路由层负责请求接入与参数校验,服务层封装外部存储(S3、本地文件系统、Redis)与业务逻辑。核心实体间的关系如下:
|
||
|
||
```mermaid
|
||
erDiagram
|
||
BRAND {
|
||
int id PK
|
||
varchar name UK
|
||
}
|
||
MODEL {
|
||
int id PK
|
||
varchar brand_name
|
||
varchar name
|
||
varchar form
|
||
varchar rig
|
||
varchar source
|
||
varchar eq_key
|
||
datetime create_at
|
||
}
|
||
OTA {
|
||
int id PK
|
||
int verCode
|
||
varchar verName
|
||
varchar url
|
||
char md5
|
||
smallint force
|
||
varchar desc
|
||
varchar model
|
||
int hw
|
||
smallint target
|
||
smallint beta
|
||
datetime startTime
|
||
datetime endTime
|
||
smallint status
|
||
datetime create_at
|
||
}
|
||
DASHBOARD_USER {
|
||
int id PK
|
||
varchar username UK
|
||
varchar password_hash
|
||
tinyint is_super_admin
|
||
tinyint status
|
||
datetime last_login_at
|
||
datetime create_at
|
||
datetime update_at
|
||
}
|
||
SHARE_CODE_LOG {
|
||
int id PK
|
||
varchar mac_addr
|
||
char share_code
|
||
enum action
|
||
varchar ip_addr
|
||
json eq_data
|
||
datetime expire_at
|
||
datetime create_at
|
||
}
|
||
MODEL }o--|| BRAND : "brand_name 关联"
|
||
OTA ||--o{ OTA : "同 model 的多版本"
|
||
DASHBOARD_USER ||--o{ SHARE_CODE_LOG : "操作记录"
|
||
```
|
||
|
||
图表来源
|
||
- [backend/src/models/Brand.js:1-23](file://backend/src/models/Brand.js#L1-L23)
|
||
- [backend/src/models/Model.js:1-53](file://backend/src/models/Model.js#L1-L53)
|
||
- [backend/src/models/Ota.js:1-97](file://backend/src/models/Ota.js#L1-L97)
|
||
- [backend/src/models/DashboardUser.js:1-58](file://backend/src/models/DashboardUser.js#L1-L58)
|
||
- [backend/src/models/ShareCodeLog.js:1-60](file://backend/src/models/ShareCodeLog.js#L1-L60)
|
||
|
||
## 详细组件分析
|
||
|
||
### 品牌 Brand
|
||
- 设计理念:最小化品牌实体,仅保留 id 与 name,确保品牌唯一性,降低跨表关联复杂度。
|
||
- 关系映射:被 Model 以 brand_name 文本字段引用,业务上保持一致性。
|
||
- 索引与约束:name 唯一;无外键约束(文本引用)。
|
||
- 参考路径:[Brand 模型定义:1-23](file://backend/src/models/Brand.js#L1-L23)
|
||
|
||
章节来源
|
||
- [backend/src/models/Brand.js:1-23](file://backend/src/models/Brand.js#L1-L23)
|
||
|
||
### 型号 Model
|
||
- 设计理念:集中存储型号元信息与 EQ 快照键,便于搜索与展示;频响文件通过外部存储(S3/本地)管理。
|
||
- 关系映射:与 Brand 通过 brand_name 文本关联;与 EQ 缓存通过 eq_key 或 Redis key 约定关联。
|
||
- 索引与约束:无外键;同一品牌内型号名称唯一(业务约束)。
|
||
- 数据访问模式:
|
||
- 列表/详情:路由层提供分页、排序、过滤与详情查询。
|
||
- EQ 缓存:通过服务层读取 Redis Hash 字段列表与具体值。
|
||
- 测量文件:仅允许特定来源与佩戴方式,且通过 S3 获取 CSV 内容。
|
||
- 参考路径:
|
||
- [Model 模型定义:1-53](file://backend/src/models/Model.js#L1-L53)
|
||
- [models 路由:133-569](file://backend/src/routes/models.js#L133-L569)
|
||
- [eqCacheStorage 服务:1-73](file://backend/src/services/eqCacheStorage.js#L1-L73)
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant Client as "客户端"
|
||
participant Route as "models 路由"
|
||
participant Model as "Model 模型"
|
||
participant EqSvc as "eqCacheStorage 服务"
|
||
participant Redis as "Redis"
|
||
Client->>Route : "GET /api/models/ : model_id/eq-cache"
|
||
Route->>Model : "findByPk(modelId)"
|
||
Model-->>Route : "型号对象"
|
||
Route->>EqSvc : "getEqCacheKeys(brand_name, name)"
|
||
EqSvc->>Redis : "hkeys(redisKey)"
|
||
Redis-->>EqSvc : "field_keys"
|
||
EqSvc-->>Route : "{redis_key, field_keys}"
|
||
Route-->>Client : "返回字段列表"
|
||
```
|
||
|
||
图表来源
|
||
- [backend/src/routes/models.js:183-201](file://backend/src/routes/models.js#L183-L201)
|
||
- [backend/src/services/eqCacheStorage.js:24-42](file://backend/src/services/eqCacheStorage.js#L24-L42)
|
||
|
||
章节来源
|
||
- [backend/src/models/Model.js:1-53](file://backend/src/models/Model.js#L1-L53)
|
||
- [backend/src/routes/models.js:133-569](file://backend/src/routes/models.js#L133-L569)
|
||
- [backend/src/services/eqCacheStorage.js:1-73](file://backend/src/services/eqCacheStorage.js#L1-L73)
|
||
|
||
### OTA 版本 Ota
|
||
- 设计理念:以 verCode 为主键语义(实际仍由数据库自增 id 保证唯一),verCode+model 组合唯一,确保按版本与设备维度的幂等管理。
|
||
- 关系映射:无外键;通过 model 字段标识目标设备型号。
|
||
- 数据访问模式:
|
||
- 上传升级包:根据设备型号选择 S3 或本地存储,计算 MD5 并生成下载地址。
|
||
- 查询最新可用版本:按 currentVerCode、model、hw 过滤,取最大 verCode。
|
||
- 参考路径:
|
||
- [Ota 模型定义:1-97](file://backend/src/models/Ota.js#L1-L97)
|
||
- [ota 路由:1-292](file://backend/src/routes/ota.js#L1-L292)
|
||
- [otaStorage 服务:1-113](file://backend/src/services/otaStorage.js#L1-L113)
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant Device as "设备端"
|
||
participant Route as "ota 路由"
|
||
participant Ota as "Ota 模型"
|
||
participant Store as "otaStorage 服务"
|
||
Device->>Route : "GET /api/ota/latest/check?currentVerCode&model&hw"
|
||
Route->>Ota : "findOne({status=1, verCode>current, model, hw})"
|
||
Ota-->>Route : "最新 OTA 记录"
|
||
Route-->>Device : "返回升级信息"
|
||
```
|
||
|
||
图表来源
|
||
- [backend/src/routes/ota.js:68-102](file://backend/src/routes/ota.js#L68-L102)
|
||
- [backend/src/models/Ota.js:1-97](file://backend/src/models/Ota.js#L1-L97)
|
||
|
||
章节来源
|
||
- [backend/src/models/Ota.js:1-97](file://backend/src/models/Ota.js#L1-L97)
|
||
- [backend/src/routes/ota.js:1-292](file://backend/src/routes/ota.js#L1-L292)
|
||
- [backend/src/services/otaStorage.js:1-113](file://backend/src/services/otaStorage.js#L1-L113)
|
||
|
||
### 后台用户 DashboardUser
|
||
- 设计理念:集中管理后台账号,支持超级管理员与启用/禁用状态,记录最近登录时间。
|
||
- 访问控制:所有品牌/型号/OTA 接口均需登录中间件保护。
|
||
- 参考路径:
|
||
- [DashboardUser 模型定义:1-58](file://backend/src/models/DashboardUser.js#L1-L58)
|
||
- [brands 路由:11-147](file://backend/src/routes/brands.js#L11-L147)
|
||
- [models 路由:70-569](file://backend/src/routes/models.js#L70-L569)
|
||
- [ota 路由:21-292](file://backend/src/routes/ota.js#L21-L292)
|
||
|
||
章节来源
|
||
- [backend/src/models/DashboardUser.js:1-58](file://backend/src/models/DashboardUser.js#L1-L58)
|
||
- [backend/src/routes/brands.js:11-147](file://backend/src/routes/brands.js#L11-L147)
|
||
- [backend/src/routes/models.js:70-569](file://backend/src/routes/models.js#L70-L569)
|
||
- [backend/src/routes/ota.js:21-292](file://backend/src/routes/ota.js#L21-L292)
|
||
|
||
### 分享码日志 ShareCodeLog
|
||
- 设计理念:记录分享码导出/导入行为,包含 MAC、分享码、操作类型、IP、EQ 快照与过期时间。
|
||
- 索引策略:针对高频查询字段建立索引,提升筛选效率。
|
||
- 参考路径:
|
||
- [ShareCodeLog 模型定义:1-60](file://backend/src/models/ShareCodeLog.js#L1-L60)
|
||
|
||
章节来源
|
||
- [backend/src/models/ShareCodeLog.js:1-60](file://backend/src/models/ShareCodeLog.js#L1-L60)
|
||
|
||
## 依赖分析
|
||
- 模型依赖:各模型通过 Sequelize 连接 MySQL;models/index.js 统一导出,供路由层引用。
|
||
- 校验依赖:路由层在写入操作前使用 Zod Schema 校验请求体,减少脏数据进入数据库。
|
||
- 服务依赖:路由层调用服务层完成外部存储与缓存交互,解耦业务逻辑与基础设施。
|
||
- 外部依赖:S3 SDK、Redis 客户端、Meilisearch HTTP 客户端。
|
||
|
||
```mermaid
|
||
graph LR
|
||
R_B["brands.js"] --> M_B["Brand.js"]
|
||
R_M["models.js"] --> M_M["Model.js"]
|
||
R_O["ota.js"] --> M_O["Ota.js"]
|
||
R_M --> S_Eq["eqCacheStorage.js"]
|
||
R_O --> S_Ota["otaStorage.js"]
|
||
V_B["validators/brand.js"] --> R_B
|
||
V_Mod["validators/model.js"] --> R_M
|
||
V_O["validators/ota.js"] --> R_O
|
||
```
|
||
|
||
图表来源
|
||
- [backend/src/routes/brands.js:1-147](file://backend/src/routes/brands.js#L1-L147)
|
||
- [backend/src/routes/models.js:1-569](file://backend/src/routes/models.js#L1-L569)
|
||
- [backend/src/routes/ota.js:1-292](file://backend/src/routes/ota.js#L1-L292)
|
||
- [backend/src/models/Brand.js:1-23](file://backend/src/models/Brand.js#L1-L23)
|
||
- [backend/src/models/Model.js:1-53](file://backend/src/models/Model.js#L1-L53)
|
||
- [backend/src/models/Ota.js:1-97](file://backend/src/models/Ota.js#L1-L97)
|
||
- [backend/src/validators/brand.js:1-12](file://backend/src/validators/brand.js#L1-L12)
|
||
- [backend/src/validators/model.js:1-22](file://backend/src/validators/model.js#L1-L22)
|
||
- [backend/src/validators/ota.js:1-36](file://backend/src/validators/ota.js#L1-L36)
|
||
- [backend/src/services/eqCacheStorage.js:1-73](file://backend/src/services/eqCacheStorage.js#L1-L73)
|
||
- [backend/src/services/otaStorage.js:1-113](file://backend/src/services/otaStorage.js#L1-L113)
|
||
|
||
章节来源
|
||
- [backend/src/models/index.js:1-8](file://backend/src/models/index.js#L1-L8)
|
||
- [backend/src/config/database.js:1-24](file://backend/src/config/database.js#L1-L24)
|
||
|
||
## 性能考量
|
||
- 查询性能
|
||
- Model 列表:支持按 brand_name/name 过滤与按 id/create_at 排序,建议在相应列建立索引以优化分页查询。
|
||
- ShareCodeLog:已建立 idx_mac_addr、idx_share_code、idx_create_at,满足常见筛选场景。
|
||
- 缓存策略
|
||
- EQ 缓存:使用 Redis Hash 存储型号 EQ 数据,提供字段列表与单字段读取接口,避免一次性传输大体积 JSON。
|
||
- OTA 包:X8 使用 S3,X9 使用本地目录,结合 MD5 前缀组织目录,便于快速定位与去重。
|
||
- IO 与网络
|
||
- 测量文件与 OTA 包上传/下载涉及大量 IO 与网络,建议:
|
||
- 限制上传文件大小与格式;
|
||
- 使用内存存储配合流式处理;
|
||
- 对热点数据进行本地缓存或 CDN 加速。
|
||
- 时间字段
|
||
- create_at/update_at 默认使用数据库时间,注意时区与同步问题。
|
||
|
||
章节来源
|
||
- [backend/src/models/ShareCodeLog.js:51-56](file://backend/src/models/ShareCodeLog.js#L51-L56)
|
||
- [backend/src/services/eqCacheStorage.js:1-73](file://backend/src/services/eqCacheStorage.js#L1-L73)
|
||
- [backend/src/services/otaStorage.js:1-113](file://backend/src/services/otaStorage.js#L1-L113)
|
||
- [backend/src/routes/models.js:133-181](file://backend/src/routes/models.js#L133-L181)
|
||
- [backend/src/routes/ota.js:107-143](file://backend/src/routes/ota.js#L107-L143)
|
||
|
||
## 故障排查指南
|
||
- 品牌/型号重复
|
||
- 现象:创建/更新时报“已存在”。
|
||
- 排查:确认品牌名称或型号组合是否已在数据库中存在。
|
||
- 参考路径:
|
||
- [brands 路由重复校验:68-72](file://backend/src/routes/brands.js#L68-L72)
|
||
- [models 路由重复校验:313-317](file://backend/src/routes/models.js#L313-L317)
|
||
- OTA 版本冲突
|
||
- 现象:创建/更新时报“该版本已存在”。
|
||
- 排查:verCode+model 组合唯一,检查是否已有相同版本。
|
||
- 参考路径:
|
||
- [ota 路由重复校验:175-181](file://backend/src/routes/ota.js#L175-L181)
|
||
- [ota 路由更新冲突检查:221-228](file://backend/src/routes/ota.js#L221-L228)
|
||
- Redis 缓存异常
|
||
- 现象:读取 EQ 缓存报错或字段不存在。
|
||
- 排查:确认 Redis 连接、key 是否存在、字段是否正确。
|
||
- 参考路径:
|
||
- [eqCacheStorage 异常处理:38-66](file://backend/src/services/eqCacheStorage.js#L38-L66)
|
||
- S3 上传失败
|
||
- 现象:OTA 包上传到 S3 失败。
|
||
- 排查:检查 AWS 凭证、Bucket 权限、区域设置。
|
||
- 参考路径:
|
||
- [otaStorage S3 上传:72-103](file://backend/src/services/otaStorage.js#L72-L103)
|
||
- 登录态缺失
|
||
- 现象:访问受保护接口返回错误。
|
||
- 排查:确认鉴权中间件是否生效、Token 是否有效。
|
||
- 参考路径:
|
||
- [brands/ota/models 路由中间件:12-12](file://backend/src/routes/brands.js#L12-L12)
|
||
- [backend/src/routes/ota.js#L105-L105:105-105](file://backend/src/routes/ota.js#L105-L105)
|
||
- [backend/src/routes/models.js:71-71](file://backend/src/routes/models.js#L71-L71)
|
||
|
||
章节来源
|
||
- [backend/src/routes/brands.js:68-72](file://backend/src/routes/brands.js#L68-L72)
|
||
- [backend/src/routes/models.js:313-317](file://backend/src/routes/models.js#L313-L317)
|
||
- [backend/src/routes/ota.js:175-181](file://backend/src/routes/ota.js#L175-L181)
|
||
- [backend/src/routes/ota.js:221-228](file://backend/src/routes/ota.js#L221-L228)
|
||
- [backend/src/services/eqCacheStorage.js:38-66](file://backend/src/services/eqCacheStorage.js#L38-L66)
|
||
- [backend/src/services/otaStorage.js:95-98](file://backend/src/services/otaStorage.js#L95-L98)
|
||
- [backend/src/routes/brands.js:12-12](file://backend/src/routes/brands.js#L12-L12)
|
||
- [backend/src/routes/ota.js:105-105](file://backend/src/routes/ota.js#L105-L105)
|
||
- [backend/src/routes/models.js:71-71](file://backend/src/routes/models.js#L71-L71)
|
||
|
||
## 结论
|
||
本数据库设计以简洁的实体与清晰的业务边界为核心,通过校验层与服务层实现输入约束与外部集成解耦。Model 与 Brand 通过文本字段关联,Ota 以 verCode+model 组合唯一,ShareCodeLog 提供完整审计轨迹。建议后续完善外键约束、补充索引与分区策略,并制定数据生命周期与归档规范。
|
||
|
||
## 附录
|
||
|
||
### 数据验证与业务规则摘要
|
||
- 品牌
|
||
- 名称必填且唯一
|
||
- 参考路径:[品牌校验:1-12](file://backend/src/validators/brand.js#L1-L12)
|
||
- 型号
|
||
- 品牌名、型号名必填;同一品牌下型号名唯一
|
||
- 参考路径:[型号校验:1-22](file://backend/src/validators/model.js#L1-L22)
|
||
- OTA
|
||
- verCode 整数;verName、url、md5 长度限制;force/target/beta/status 0/1 限定
|
||
- 参考路径:[OTA 校验:1-36](file://backend/src/validators/ota.js#L1-L36)
|
||
|
||
章节来源
|
||
- [backend/src/validators/brand.js:1-12](file://backend/src/validators/brand.js#L1-L12)
|
||
- [backend/src/validators/model.js:1-22](file://backend/src/validators/model.js#L1-L22)
|
||
- [backend/src/validators/ota.js:1-36](file://backend/src/validators/ota.js#L1-L36)
|
||
|
||
### 示例数据
|
||
- 品牌 Brand
|
||
- id: 1, name: "Luxsin"
|
||
- 型号 Model
|
||
- id: 1, brand_name: "Luxsin", name: "X8", form: "in-ear", rig: "32Ω", source: "Eafonyoung", eq_key: "luxsin_x8_eq", create_at: "2025-01-01 12:00:00"
|
||
- OTA 版本 Ota
|
||
- id: 1, verCode: 101, verName: "v1.0.1", url: "http://.../LUXSIN_X8.PKG", md5: "d41d8cd98f00b204e9800998ecf8427e", force: 1, model: "Luxsin-X8", hw: 1, target: 0, beta: 0, status: 1, create_at: "2025-01-01 12:00:00"
|
||
- 后台用户 DashboardUser
|
||
- id: 1, username: "admin", password_hash: "$2b$...", is_super_admin: 1, status: 1, create_at: "2025-01-01 12:00:00", update_at: "2025-01-01 12:00:00"
|
||
- 分享码日志 ShareCodeLog
|
||
- id: 1, mac_addr: "AA:BB:CC:DD:EE:FF", share_code: "ABCDE", action: "export", ip_addr: "::ffff:127.0.0.1", eq_data: "{}", create_at: "2025-01-01 12:00:00"
|
||
|
||
### 数据访问模式与缓存策略
|
||
- Model
|
||
- 列表/详情:分页、过滤、排序
|
||
- EQ 缓存:字段列表与单字段读取
|
||
- 参考路径:[models 路由:133-569](file://backend/src/routes/models.js#L133-L569), [eqCacheStorage:1-73](file://backend/src/services/eqCacheStorage.js#L1-L73)
|
||
- OTA
|
||
- 上传包:S3 或本地存储,返回下载地址
|
||
- 最新版本查询:按 verCode 与 hw 过滤
|
||
- 参考路径:[ota 路由:24-102](file://backend/src/routes/ota.js#L24-L102), [otaStorage:1-113](file://backend/src/services/otaStorage.js#L1-L113)
|
||
|
||
章节来源
|
||
- [backend/src/routes/models.js:133-569](file://backend/src/routes/models.js#L133-L569)
|
||
- [backend/src/services/eqCacheStorage.js:1-73](file://backend/src/services/eqCacheStorage.js#L1-L73)
|
||
- [backend/src/routes/ota.js:24-102](file://backend/src/routes/ota.js#L24-L102)
|
||
- [backend/src/services/otaStorage.js:1-113](file://backend/src/services/otaStorage.js#L1-L113)
|
||
|
||
### 数据生命周期、保留策略与归档规则
|
||
- 建议
|
||
- ShareCodeLog:按月清理历史日志,保留必要审计周期(如 90 天)。
|
||
- OTA:旧版本保留 3-6 个月或按产品策略归档;S3/本地定期清理过期包。
|
||
- Model:测量文件与 EQ 缓存按访问频率与容量阈值进行轮转。
|
||
- 实施要点
|
||
- 为 ShareCodeLog 增加 expire_at 字段与自动清理任务。
|
||
- 为 OTA 增加归档标记与版本保留策略。
|
||
- 对 S3/本地存储建立配额与过期清理机制。
|
||
|
||
[本节为通用建议,不直接分析具体文件]
|
||
|
||
### 数据迁移路径与版本管理
|
||
- 建议
|
||
- 使用数据库迁移工具(如 Sequelize CLI)管理结构变更。
|
||
- 新增字段采用非空默认值或分阶段上线,避免影响在线服务。
|
||
- 对于索引与约束,先添加再回填数据,最后替换为严格约束。
|
||
- 参考路径
|
||
- [database.js 定义:1-24](file://backend/src/config/database.js#L1-L24)
|
||
|
||
章节来源
|
||
- [backend/src/config/database.js:1-24](file://backend/src/config/database.js#L1-L24)
|
||
|
||
### 数据安全、隐私与访问控制
|
||
- 访问控制
|
||
- 所有受保护接口均需登录中间件;超级管理员具备更高权限。
|
||
- 参考路径:[brands/ota/models 路由中间件:12-12](file://backend/src/routes/brands.js#L12-L12), [backend/src/routes/ota.js#L105-L105:105-105](file://backend/src/routes/ota.js#L105-L105), [backend/src/routes/models.js:71-71](file://backend/src/routes/models.js#L71-L71)
|
||
- 数据脱敏
|
||
- 日志中避免输出敏感字段(如密码哈希);对外接口仅返回必要字段。
|
||
- 合规
|
||
- 对用户数据与操作日志遵循最小化原则,遵守隐私政策与数据保留期限。
|
||
|
||
章节来源
|
||
- [backend/src/routes/brands.js:12-12](file://backend/src/routes/brands.js#L12-L12)
|
||
- [backend/src/routes/ota.js:105-105](file://backend/src/routes/ota.js#L105-L105)
|
||
- [backend/src/routes/models.js:71-71](file://backend/src/routes/models.js#L71-L71) |