Files
app-api/.qoder/repowiki/zh/content/核心模块/数据模型.md
T
2026-05-27 18:07:55 +08:00

466 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)
- [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/handler/model_list.go](file://internal/handler/model_list.go)
- [internal/router/router.go](file://internal/router/router.go)
- [internal/config/database.go](file://internal/config/database.go)
- [internal/response/response.go](file://internal/response/response.go)
- [pkg/encode/base64.go](file://pkg/encode/base64.go)
- [sql/model.sql](file://sql/model.sql)
- [cmd/server/main.go](file://cmd/server/main.go)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)
## 简介
本文件面向 Luxsin 应用 API 的数据模型,系统性梳理 Brand 与 Model 两类数据模型的设计理念、字段定义、约束条件、业务含义以及与数据库表结构的映射关系。文档还覆盖 ORM 映射配置(基于原生 sql.DB 的扫描映射)、模型验证与序列化机制、模型在各层之间的传递方式与性能考量,并给出扩展、版本管理与向后兼容的最佳实践建议。内容兼顾初学者与高级开发者,既提供高层概览,也包含代码级的可视化图示与来源标注。
## 项目结构
该项目采用分层架构:路由层负责请求入口与中间件;处理器层处理业务逻辑与参数解析;仓库层封装数据库访问;模型层承载数据结构;响应层统一返回格式;编码层提供可选的响应体压缩编码;配置层加载数据库等外部服务配置。
```mermaid
graph TB
subgraph "应用入口"
MAIN["cmd/server/main.go"]
end
subgraph "路由层"
ROUTER["internal/router/router.go"]
end
subgraph "处理器层"
BRAND_H["internal/handler/brand.go"]
MODEL_H["internal/handler/model.go"]
MODEL_LIST_H["internal/handler/model_list.go"]
end
subgraph "仓库层"
BRAND_R["internal/repository/brand.go"]
MODEL_R["internal/repository/model.go"]
end
subgraph "模型层"
MODEL_M["internal/model/brand.go"]
MODEL_M2["internal/model/model.go"]
end
subgraph "响应与编码"
RESP["internal/response/response.go"]
ENCODE["pkg/encode/base64.go"]
end
subgraph "配置与数据库"
DB_CFG["internal/config/database.go"]
DB_SQL["sql/model.sql"]
end
MAIN --> ROUTER
ROUTER --> BRAND_H
ROUTER --> MODEL_H
ROUTER --> MODEL_LIST_H
BRAND_H --> BRAND_R
MODEL_H --> MODEL_R
MODEL_LIST_H --> MODEL_R
BRAND_R --> MODEL_M
MODEL_R --> MODEL_M2
BRAND_H --> RESP
MODEL_H --> RESP
MODEL_LIST_H --> RESP
BRAND_H --> ENCODE
MODEL_H --> ENCODE
MODEL_LIST_H --> ENCODE
DB_CFG --> DB_SQL
```
图表来源
- [cmd/server/main.go:1-96](file://cmd/server/main.go#L1-L96)
- [internal/router/router.go:14-42](file://internal/router/router.go#L14-L42)
- [internal/handler/brand.go:19-50](file://internal/handler/brand.go#L19-L50)
- [internal/handler/model.go:19-51](file://internal/handler/model.go#L19-L51)
- [internal/handler/model_list.go:19-57](file://internal/handler/model_list.go#L19-L57)
- [internal/repository/brand.go:16-51](file://internal/repository/brand.go#L16-L51)
- [internal/repository/model.go:16-95](file://internal/repository/model.go#L16-L95)
- [internal/model/brand.go:3-7](file://internal/model/brand.go#L3-L7)
- [internal/model/model.go:5-15](file://internal/model/model.go#L5-L15)
- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37)
- [pkg/encode/base64.go:21-52](file://pkg/encode/base64.go#L21-L52)
- [internal/config/database.go:17-72](file://internal/config/database.go#L17-L72)
- [sql/model.sql:20-38](file://sql/model.sql#L20-L38)
章节来源
- [cmd/server/main.go:22-64](file://cmd/server/main.go#L22-L64)
- [internal/router/router.go:14-42](file://internal/router/router.go#L14-L42)
## 核心组件
- 模型层(Model
- Brand:承载品牌标识与名称,用于品牌列表查询。
- Model:承载型号信息,包含品牌名、型号名、形态、设备类型、来源、等价键、创建时间等。
- 仓库层(Repository
- BrandRepository:提供按品牌名模糊查询的品牌列表。
- ModelRepository:提供按品牌名或型号名模糊查询的型号列表,并处理可空字段的扫描。
- 处理器层(Handler
- BrandHandler:接收查询参数,调用仓库层,支持可选的响应体 Base64 编码。
- ModelHandler:接收品牌与型号查询参数,调用仓库层,支持可选的响应体 Base64 编码。
- ModelListHandler:通过搜索客户端返回模型列表,支持可选的响应体 Base64 编码。
- 响应与编码
- 统一响应体结构,便于前端解析与错误处理。
- 可选的自定义 Base64 编码,提升传输效率与隐私保护。
章节来源
- [internal/model/brand.go:3-7](file://internal/model/brand.go#L3-L7)
- [internal/model/model.go:5-15](file://internal/model/model.go#L5-L15)
- [internal/repository/brand.go:20-51](file://internal/repository/brand.go#L20-L51)
- [internal/repository/model.go:20-95](file://internal/repository/model.go#L20-L95)
- [internal/handler/brand.go:26-50](file://internal/handler/brand.go#L26-L50)
- [internal/handler/model.go:26-51](file://internal/handler/model.go#L26-L51)
- [internal/handler/model_list.go:26-57](file://internal/handler/model_list.go#L26-L57)
- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37)
- [pkg/encode/base64.go:21-52](file://pkg/encode/base64.go#L21-L52)
## 架构总览
下图展示了从请求到响应的关键路径,包括处理器、仓库、数据库与可选编码流程。
```mermaid
sequenceDiagram
participant C as "客户端"
participant R as "路由层"
participant H as "处理器层"
participant Repo as "仓库层"
participant DB as "数据库"
participant Enc as "编码层"
C->>R : "GET /audio/getBrand?brandName=..."
R->>H : "BrandHandler.GetBrand"
H->>Repo : "List(brandName)"
Repo->>DB : "SELECT id,name FROM brand WHERE name LIKE ? ORDER BY name ASC"
DB-->>Repo : "结果集"
Repo-->>H : "[]Brand"
alt "base64Resp=true"
H->>Enc : "EncodeJSON([]Brand)"
Enc-->>H : "Base64 字符串"
H-->>C : "200 OK + Base64"
else "默认"
H-->>C : "200 OK + JSON"
end
```
图表来源
- [internal/router/router.go:32-38](file://internal/router/router.go#L32-L38)
- [internal/handler/brand.go:26-50](file://internal/handler/brand.go#L26-L50)
- [internal/repository/brand.go:20-51](file://internal/repository/brand.go#L20-L51)
- [pkg/encode/base64.go:35-52](file://pkg/encode/base64.go#L35-L52)
## 详细组件分析
### Brand 数据模型
- 设计理念
- 轻量级品牌实体,仅包含标识与名称,用于品牌筛选与列表展示。
- 字段定义与约束
- id:整数,主键,自增。
- name:字符串,非空,用于品牌名匹配与排序。
- 业务含义
- 作为 Model 的上游维度,配合 Model 的 brandName 字段形成关联。
- 数据库映射
- 表名:brand(未在本文直接列出,但与 Model 的 brand_name 关联一致)。
- 字段:id、name。
- ORM 映射配置
- 使用原生 sql.DB 扫描,通过结构体标签与 Scan 对齐。
- 验证与序列化
- 无显式校验逻辑,依赖数据库约束与上层参数清洗。
- JSON 标签用于序列化输出。
- 查询流程
- 支持按品牌名模糊查询,使用 LIKE 匹配并按名称升序排列。
- 返回 []Brand 列表。
```mermaid
classDiagram
class Brand {
+int id
+string name
}
class BrandRepository {
-db *sql.DB
+List(ctx, brandName) []Brand
}
BrandRepository --> Brand : "返回"
```
图表来源
- [internal/model/brand.go:3-7](file://internal/model/brand.go#L3-L7)
- [internal/repository/brand.go:12-51](file://internal/repository/brand.go#L12-L51)
章节来源
- [internal/model/brand.go:3-7](file://internal/model/brand.go#L3-L7)
- [internal/repository/brand.go:20-51](file://internal/repository/brand.go#L20-L51)
### Model 数据模型
- 设计理念
- 型号实体承载品牌名、型号名及可选属性(形态、设备类型、来源、等价键),并记录创建时间。
- 字段定义与约束
- id:整数,主键,自增。
- brandName:字符串,非空,与品牌维度关联。
- name:字符串,非空,型号名。
- form、rig、source、eqKey:字符串(指针),可空,表示形态、设备类型、来源、等价键。
- createAt:时间戳,非空,记录创建时间。
- 业务含义
- 作为核心产品维度,支持按品牌或型号名检索,可为空的扩展属性满足多样化设备描述。
- 数据库映射
- 表名:model。
- 字段:id、brand_name、name、form、rig、source、eq_key、create_at。
- 约束:唯一索引 (brand_name, name),保证同品牌下型号名唯一。
- ORM 映射配置
- 使用 sql.NullString 扫描可空列,再转换为指针字符串,避免零值歧义。
- JSON 标签用于序列化输出,可空字段支持 omitempty。
- 验证与序列化
- 无显式校验逻辑,依赖数据库约束与上层参数清洗。
- JSON 标签用于序列化输出。
- 查询流程
- 支持按品牌名精确匹配或按型号名模糊匹配,返回 []Model 列表。
- 默认返回空切片而非 nil,便于前端处理。
```mermaid
classDiagram
class Model {
+int id
+string brandName
+string name
+*string form
+*string rig
+*string source
+*string eqKey
+time createAt
}
class ModelRepository {
-db *sql.DB
+List(ctx, brandName, modelName) []Model
}
ModelRepository --> Model : "返回"
```
图表来源
- [internal/model/model.go:5-15](file://internal/model/model.go#L5-L15)
- [internal/repository/model.go:12-95](file://internal/repository/model.go#L12-L95)
- [sql/model.sql:24-35](file://sql/model.sql#L24-L35)
章节来源
- [internal/model/model.go:5-15](file://internal/model/model.go#L5-L15)
- [internal/repository/model.go:20-95](file://internal/repository/model.go#L20-L95)
- [sql/model.sql:24-35](file://sql/model.sql#L24-L35)
### 处理器与序列化机制
- 统一响应体
- 统一响应体包含 code、message、data 字段,便于前端统一处理。
- 可选 Base64 编码
- 支持通过 base64Resp 参数控制是否对 JSON 响应进行自定义 Base64 编码。
- 自定义映射表将标准 Base64 字符集映射为更紧凑的字符集,减少体积。
- 错误处理
- 处理器捕获仓库层错误,记录日志并通过统一响应体返回内部错误。
```mermaid
sequenceDiagram
participant C as "客户端"
participant H as "ModelHandler"
participant Repo as "ModelRepository"
participant DB as "数据库"
participant Enc as "编码层"
C->>H : "GET /audio/getModel?brandName=...&modelName=...&base64Resp=..."
H->>Repo : "List(brandName, modelName)"
Repo->>DB : "根据条件查询"
DB-->>Repo : "结果集"
Repo-->>H : "[]Model"
alt "base64Resp=true"
H->>Enc : "EncodeJSON([]Model)"
Enc-->>H : "Base64 字符串"
H-->>C : "200 OK + Base64"
else "默认"
H-->>C : "200 OK + JSON"
end
```
图表来源
- [internal/handler/model.go:26-51](file://internal/handler/model.go#L26-L51)
- [internal/repository/model.go:20-95](file://internal/repository/model.go#L20-L95)
- [pkg/encode/base64.go:21-52](file://pkg/encode/base64.go#L21-L52)
- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37)
章节来源
- [internal/handler/brand.go:26-50](file://internal/handler/brand.go#L26-L50)
- [internal/handler/model.go:26-51](file://internal/handler/model.go#L26-L51)
- [internal/handler/model_list.go:26-57](file://internal/handler/model_list.go#L26-L57)
- [pkg/encode/base64.go:21-52](file://pkg/encode/base64.go#L21-L52)
- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37)
### 查询与过滤逻辑
- Brand 查询
- 支持按品牌名模糊匹配,使用 LIKE 并按名称升序排列。
- Model 查询
- 优先按品牌名精确匹配;若为空则按型号名模糊匹配;否则返回空切片。
- 扫描时将 sql.NullString 转换为指针字符串,避免零值歧义。
```mermaid
flowchart TD
Start(["进入 List"]) --> Trim["去除前后空白"]
Trim --> CheckBrand{"brandName 是否非空?"}
CheckBrand --> |是| QueryBrand["按品牌名精确匹配"]
CheckBrand --> |否| CheckModel{"modelName 是否非空?"}
CheckModel --> |是| QueryModel["按型号名模糊匹配"]
CheckModel --> |否| ReturnEmpty["返回空切片"]
QueryBrand --> Exec["执行查询并扫描"]
QueryModel --> Exec
Exec --> End(["返回结果"])
ReturnEmpty --> End
```
图表来源
- [internal/repository/model.go:20-61](file://internal/repository/model.go#L20-L61)
- [internal/repository/brand.go:20-51](file://internal/repository/brand.go#L20-L51)
章节来源
- [internal/repository/model.go:20-61](file://internal/repository/model.go#L20-L61)
- [internal/repository/brand.go:20-51](file://internal/repository/brand.go#L20-L51)
## 依赖分析
- 层间耦合
- Handler 依赖 RepositoryRepository 依赖 sql.DBModel 为纯数据结构。
- 统一响应体与编码层被 Handler 调用,降低重复逻辑。
- 外部依赖
- 数据库:MySQL,通过 sql.DB 访问。
- 日志:zap。
- Web 框架:Gin。
- 搜索:MeilisearchModelListHandler)。
- 缓存:RedisDevice 相关处理器)。
- 潜在循环依赖
- 当前结构清晰,无循环导入迹象。
```mermaid
graph LR
H_Brand["BrandHandler"] --> Repo_Brand["BrandRepository"]
H_Model["ModelHandler"] --> Repo_Model["ModelRepository"]
H_ModelList["ModelListHandler"] --> Search["Meilisearch 客户端"]
Repo_Brand --> DB["sql.DB"]
Repo_Model --> DB
H_Brand --> Resp["统一响应体"]
H_Model --> Resp
H_ModelList --> Resp
H_Brand --> Encode["Base64 编码"]
H_Model --> Encode
H_ModelList --> Encode
```
图表来源
- [internal/handler/brand.go:14-50](file://internal/handler/brand.go#L14-L50)
- [internal/handler/model.go:14-51](file://internal/handler/model.go#L14-L51)
- [internal/handler/model_list.go:14-57](file://internal/handler/model_list.go#L14-L57)
- [internal/repository/brand.go:12-51](file://internal/repository/brand.go#L12-L51)
- [internal/repository/model.go:12-95](file://internal/repository/model.go#L12-L95)
- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37)
- [pkg/encode/base64.go:21-52](file://pkg/encode/base64.go#L21-L52)
章节来源
- [internal/router/router.go:14-42](file://internal/router/router.go#L14-L42)
- [internal/config/database.go:17-72](file://internal/config/database.go#L17-L72)
## 性能考虑
- 查询优化
- Model 表对 (brand_name, name) 建有唯一索引,有利于去重与快速定位。
- 查询时优先按品牌名匹配,减少 LIKE 的范围。
- 扫描与内存
- 使用 sql.NullString 扫描可空列,避免零值歧义;转换为指针字符串减少冗余存储。
- 编码策略
- 可选的自定义 Base64 编码可降低响应体积,适合大列表传输场景。
- 并发与超时
- 服务器设置读写超时与优雅关闭,保障稳定性。
章节来源
- [sql/model.sql:34-35](file://sql/model.sql#L34-L35)
- [internal/repository/model.go:63-95](file://internal/repository/model.go#L63-L95)
- [pkg/encode/base64.go:21-52](file://pkg/encode/base64.go#L21-L52)
- [cmd/server/main.go:66-95](file://cmd/server/main.go#L66-L95)
## 故障排查指南
- 常见问题
- 数据库连接失败:检查环境变量与配置加载逻辑。
- 查询无结果:确认查询参数是否为空或大小写敏感;Model 查询默认返回空切片而非 nil。
- 编码异常:确认 base64Resp 参数与 JSON 序列化是否成功。
- 排查步骤
- 查看日志:处理器记录错误日志,统一响应体返回错误码。
- 核对数据库:确认表结构与索引是否存在。
- 验证参数:确认查询参数是否符合预期。
章节来源
- [internal/config/database.go:57-72](file://internal/config/database.go#L57-L72)
- [internal/handler/brand.go:31-35](file://internal/handler/brand.go#L31-L35)
- [internal/handler/model.go:33-36](file://internal/handler/model.go#L33-L36)
- [internal/handler/model_list.go:39-42](file://internal/handler/model_list.go#L39-L42)
- [internal/response/response.go:30-37](file://internal/response/response.go#L30-L37)
## 结论
本项目的数据模型设计简洁明确:Brand 与 Model 分别承担品牌与型号的维度,通过仓库层的原生 SQL 访问实现高效查询;处理器层统一响应与可选编码,提升传输效率与前端体验。数据库层面通过唯一索引与合理字段设计保障一致性与性能。建议在后续迭代中引入显式的校验与转换层,增强健壮性与可维护性。
## 附录
### 数据库表结构与模型映射对照
- 表:model
- 字段:id(主键)、brand_name、name、form、rig、source、eq_key、create_at。
- 约束:唯一索引 (brand_name, name)。
- 映射关系
- Model.id ↔ model.id
- Model.brandName ↔ model.brand_name
- Model.name ↔ model.name
- Model.form ↔ model.form
- Model.rig ↔ model.rig
- Model.source ↔ model.source
- Model.eqKey ↔ model.eq_key
- Model.createAt ↔ model.create_at
章节来源
- [sql/model.sql:24-35](file://sql/model.sql#L24-L35)
- [internal/model/model.go:5-15](file://internal/model/model.go#L5-L15)
### 请求与响应示例(路径参考)
- 获取品牌列表
- 路由:/audio/getBrand
- 方法:GET
- 参数:brandName(可选),base64Resp(可选)
- 返回:[]Brand 或 Base64 编码后的 JSON
- 参考路径:[internal/handler/brand.go:26-50](file://internal/handler/brand.go#L26-L50)
- 获取型号列表
- 路由:/audio/getModel
- 方法:GET
- 参数:brandName(可选)、modelName(可选)、base64Resp(可选)
- 返回:[]Model 或 Base64 编码后的 JSON
- 参考路径:[internal/handler/model.go:26-51](file://internal/handler/model.go#L26-L51)
- 搜索模型列表
- 路由:/audio/modelList
- 方法:GET
- 参数:key(必需)、count(可选)、base64Resp(可选)
- 返回:[]Model 或 Base64 编码后的 JSON
- 参考路径:[internal/handler/model_list.go:26-57](file://internal/handler/model_list.go#L26-L57)
章节来源
- [internal/router/router.go:32-38](file://internal/router/router.go#L32-L38)
- [internal/handler/brand.go:26-50](file://internal/handler/brand.go#L26-L50)
- [internal/handler/model.go:26-51](file://internal/handler/model.go#L26-L51)
- [internal/handler/model_list.go:26-57](file://internal/handler/model_list.go#L26-L57)
### 最佳实践建议
- 模型扩展
- 引入显式的校验与转换层,如参数清洗、长度限制、正则校验等。
- 对可空字段提供默认值策略,避免前端空值判断复杂化。
- 版本管理与向后兼容
- 通过 API 版本号(如 /api/v1)隔离变更;新增字段采用可选策略,保持旧字段必填。
- 对于破坏性变更,提供迁移脚本与双写策略。
- 性能优化
- 为高频查询字段建立索引;避免 SELECT *,仅选择必要字段。
- 对大列表启用可选 Base64 编码;结合分页与缓存策略。
- 错误处理与可观测性
- 统一错误码与消息格式;记录关键链路日志;对数据库与外部服务增加超时与重试。