Files
app-api/.qoder/repowiki/zh/content/数据库设计.md
T
2026-05-27 18:07:55 +08:00

403 lines
19 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/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)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)
## 简介
本文件面向 Luxsin 应用 API 的数据库与数据模型,系统化梳理实体关系、字段定义、索引与约束、数据访问模式、缓存与搜索集成、性能优化、数据生命周期与迁移路径,并给出品牌(Brand)与型号(Model)实体的设计理念与业务逻辑说明。文档同时覆盖数据库连接配置、查询优化建议、数据安全与隐私要求以及访问控制要点。
## 项目结构
本项目采用分层架构:入口程序负责初始化配置、数据库、搜索引擎与缓存;路由层组织 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"]
REPO_B["internal/repository/brand.go"]
REPO_M["internal/repository/model.go"]
MODEL_B["internal/model/brand.go"]
MODEL_M["internal/model/model.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
HANDLER_B --> REPO_B
HANDLER_M --> REPO_M
REPO_B --> MODEL_B
REPO_M --> MODEL_M
```
图表来源
- [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/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/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)
章节来源
- [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:存储耳机型号信息,包含唯一组合索引以保证品牌+型号的唯一性。
- 模型对象 Brand 与 Model:分别映射品牌与型号的 JSON 字段与数据库列。
- 仓库层:提供按品牌名或型号名检索的查询方法,支持模糊匹配与精确匹配。
- 处理器层:暴露 HTTP 接口,支持返回 JSON 或 Base64 编码响应。
- 配置层:集中加载数据库、搜索引擎与缓存的连接参数,并进行基本校验。
- 基础设施:MySQL 连接池配置、Redis 客户端、Meilisearch 搜索客户端。
章节来源
- [sql/model.sql:20-38](file://sql/model.sql#L20-L38)
- [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/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/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/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 请求到数据库与搜索引擎的调用链路,以及缓存的使用位置。
```mermaid
sequenceDiagram
participant C as "客户端"
participant H as "处理器(品牌/型号)"
participant R as "仓库(品牌/型号)"
participant DB as "MySQL 数据库"
participant S as "Meilisearch"
participant RC as "Redis"
C->>H : "HTTP GET /brands 或 /models"
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 : "可选:对热点数据进行缓存读写"
```
图表来源
- [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/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/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(型号)
- 关系:Model 通过字段 brand_name 引导与品牌的关系;当前仓库未显式声明外键约束,但数据库层通过唯一索引确保品牌+型号组合唯一。
- 字段与类型:
- Brand:id(整数,主键)、name(字符串)
- Modelid(整数,主键)、brand_name(字符串,非空)、name(字符串,非空)、form(字符串,可空)、rig(字符串,可空)、source(字符串,可空)、eq_key(字符串,可空)、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
}
BRAND ||--o{ MODEL : "拥有多个型号"
```
图表来源
- [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)
- [sql/model.sql:24-35](file://sql/model.sql#L24-L35)
章节来源
- [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)
- [sql/model.sql:20-38](file://sql/model.sql#L20-L38)
### 数据库表结构与约束
- 表名:model
- 主键:id(自增整数)
- 唯一索引:(brand_name, name),用于保证同一品牌下的型号名称唯一
- 默认值:create_at 默认当前时间
- 注释:耳机型号
- 存储引擎与字符集:InnoDB、utf8mb4、排序规则 0900_ai_ci
章节来源
- [sql/model.sql:20-38](file://sql/model.sql#L20-L38)
### 数据访问模式
- 品牌列表:
- 支持按品牌名模糊查询(LIKE %brandName%),无匹配时返回空数组
- 结果按名称升序排列
- 型号列表:
- 支持按品牌名精确匹配或按型号名模糊匹配,二者二选一
- 无匹配条件时返回空数组
- 结果按名称升序排列
```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(["结束"])
Exec --> 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/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)
### 处理器与接口行为
- 品牌接口:接收 query 参数 brandName,支持返回 JSON 或 Base64 编码响应
- 型号接口:接收 query 参数 brandName 与 modelName,支持返回 JSON 或 Base64 编码响应
- 错误处理:内部错误统一返回 500 并记录日志
章节来源
- [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)
### 搜索与缓存集成
- 搜索:Meilisearch 客户端按给定关键字检索,限定返回属性,支持空命中返回空数组
- 缓存: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)
### 数据库连接配置
- 加载顺序:环境变量优先于默认值;生产环境需提供密码
- 连接参数:用户、主机、端口、数据库名、字符集、时区、超时等
- 连接池:最大打开连接数、最大空闲连接数、连接最大生命周期
- 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)
## 依赖分析
- 处理器依赖仓库;仓库依赖数据库;应用入口依赖配置与基础设施。
- 搜索与缓存作为独立模块被配置层加载并在处理器中可选使用。
- 代码内未发现循环依赖。
```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"]
REPO_B --> DB["sql/model.sql(表结构)"]
REPO_M --> DB
```
图表来源
- [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/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)
- [sql/model.sql:20-38](file://sql/model.sql#L20-L38)
## 性能考虑
- 连接池与生命周期
- 最大打开连接数:25;最大空闲连接数:5;连接最大生命周期:5 分钟
- 建议:根据并发与查询负载调整;生产环境结合监控指标动态优化
- 查询优化
- 品牌列表:支持模糊匹配,建议在高频搜索场景引入前缀索引或搜索引擎替代
- 型号列表:按品牌名精确匹配或按型号名模糊匹配;建议对常用过滤字段建立合适索引
- 缓存策略
- 对品牌列表与热门型号列表进行短期缓存,设置合理过期时间
- 使用缓存穿透防护(空结果也缓存短时间)与缓存雪崩防护(随机过期时间)
- 搜索优化
- 使用搜索引擎进行全文检索与高亮,减少数据库 LIKE 查询压力
- 控制返回字段数量,避免传输冗余数据
- I/O 与序列化
- 响应支持 Base64 编码,适合二进制传输场景,但会增加 CPU 开销
章节来源
- [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/search/meilisearch.go:22-26](file://internal/search/meilisearch.go#L22-L26)
## 故障排查指南
- 数据库连接失败
- 检查环境变量是否正确设置;确认主机、端口、用户名、密码与数据库名
- 启动时会进行 Ping 校验,失败会记录错误并退出
- 查询异常
- 仓库层对扫描与迭代过程进行错误包装,定位具体阶段(扫描、迭代)
- 建议开启数据库慢查询日志与应用日志聚合
- 搜索与缓存
- 搜索引擎与缓存客户端初始化后会记录连接信息;若无响应,检查主机、密钥与网络连通性
- 响应编码
- Base64 返回失败时,检查编码流程与日志输出
章节来源
- [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/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)
## 结论
本设计以简洁的表结构与清晰的分层架构支撑品牌与型号的查询需求。通过搜索引擎与缓存提升检索性能,配合连接池与错误处理机制保障稳定性。后续可在索引策略、缓存策略与数据迁移方面进一步细化,以满足生产环境的高可用与高性能要求。
## 附录
### 数据验证与业务规则
- 品牌与型号名称必填,型号表对品牌+型号建立唯一索引,防止重复
- 时间字段 create_at 默认当前时间,便于审计与排序
- 处理器层对空结果返回空数组,避免无效数据传播
章节来源
- [sql/model.sql:24-35](file://sql/model.sql#L24-L35)
- [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"
章节来源
- [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)
### 数据生命周期、保留策略与归档规则
- 建议:基于 create_at 建立分区或定期归档任务,清理长期未使用的型号数据
- 归档策略:保留近一年的活跃型号,历史数据移至冷存储
- 审计:保留变更日志与备份周期,确保可追溯性
[本节为通用实践建议,不直接对应特定源文件]
### 数据迁移路径与版本管理
- 版本化:每次结构变更生成迁移脚本,记录变更人、时间与目的
- 回滚:保留逆向迁移脚本;灰度发布逐步切换
- 兼容:新增字段采用可空策略,旧数据默认值填充
[本节为通用实践建议,不直接对应特定源文件]
### 数据安全、隐私要求与访问控制
- 网络安全:数据库与搜索引擎通过内网或 VPC 访问,限制入站 IP
- 凭证管理:敏感信息通过环境变量注入,禁止硬编码
- 访问控制:最小权限原则;数据库账号仅授予必要权限;搜索引擎 API Key 严格管理
章节来源
- [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)
### 品牌与型号实体设计理念
- 品牌(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)