新增功能
This commit is contained in:
@@ -0,0 +1,374 @@
|
||||
# 数据访问层
|
||||
|
||||
<cite>
|
||||
**本文引用的文件**
|
||||
- [internal/repository/brand.go](file://internal/repository/brand.go)
|
||||
- [internal/repository/model.go](file://internal/repository/model.go)
|
||||
- [internal/database/mysql.go](file://internal/database/mysql.go)
|
||||
- [internal/config/database.go](file://internal/config/database.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/handler/brand.go](file://internal/handler/brand.go)
|
||||
- [internal/handler/model.go](file://internal/handler/model.go)
|
||||
- [internal/router/router.go](file://internal/router/router.go)
|
||||
- [cmd/server/main.go](file://cmd/server/main.go)
|
||||
- [sql/model.sql](file://sql/model.sql)
|
||||
- [go.mod](file://go.mod)
|
||||
</cite>
|
||||
|
||||
## 目录
|
||||
1. [简介](#简介)
|
||||
2. [项目结构](#项目结构)
|
||||
3. [核心组件](#核心组件)
|
||||
4. [架构总览](#架构总览)
|
||||
5. [详细组件分析](#详细组件分析)
|
||||
6. [依赖关系分析](#依赖关系分析)
|
||||
7. [性能考量](#性能考量)
|
||||
8. [故障排查指南](#故障排查指南)
|
||||
9. [结论](#结论)
|
||||
10. [附录:扩展新 Repository 指南](#附录扩展新-repository-指南)
|
||||
|
||||
## 简介
|
||||
本文件聚焦于 Luxsin 应用 API 的数据访问层(Repository 层),系统性阐述 BrandRepository 与 ModelRepository 的设计模式、实现原理与职责边界;解释其在整体架构中的位置与交互方式;深入分析数据库连接管理、SQL 查询优化与事务处理机制;给出使用示例路径、错误处理与异常管理策略;总结并发安全、连接池管理与资源清理的最佳实践,并提供扩展新 Repository 的指导原则与注意事项。文档兼顾初学者与资深开发者的需求,既提供高层架构视图,也给出可落地的实现细节。
|
||||
|
||||
## 项目结构
|
||||
数据访问层位于 internal/repository 目录,配合 internal/database 提供底层数据库连接,internal/model 定义领域模型,internal/handler 通过注入的 Repository 执行业务逻辑,最终由 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/>型号处理器"]
|
||||
end
|
||||
subgraph "数据访问层"
|
||||
BR_REPO["internal/repository/brand.go<br/>BrandRepository"]
|
||||
MD_REPO["internal/repository/model.go<br/>ModelRepository"]
|
||||
end
|
||||
subgraph "数据库与配置"
|
||||
DB_SQL["internal/database/mysql.go<br/>sql.DB 连接与池配置"]
|
||||
CFG_DB["internal/config/database.go<br/>数据库配置加载"]
|
||||
CFG_APP["internal/config/config.go<br/>应用配置聚合"]
|
||||
MODEL_TBL["sql/model.sql<br/>表结构定义"]
|
||||
end
|
||||
subgraph "领域模型"
|
||||
M_BRAND["internal/model/brand.go<br/>Brand 模型"]
|
||||
M_MODEL["internal/model/model.go<br/>Model 模型"]
|
||||
end
|
||||
MAIN --> ROUTER
|
||||
ROUTER --> BRAND_H
|
||||
ROUTER --> MODEL_H
|
||||
BRAND_H --> BR_REPO
|
||||
MODEL_H --> MD_REPO
|
||||
BR_REPO --> DB_SQL
|
||||
MD_REPO --> DB_SQL
|
||||
DB_SQL --> CFG_DB
|
||||
CFG_APP --> CFG_DB
|
||||
BR_REPO --> M_BRAND
|
||||
MD_REPO --> M_MODEL
|
||||
MODEL_TBL --> DB_SQL
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [cmd/server/main.go:38-48](file://cmd/server/main.go#L38-L48)
|
||||
- [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/repository/brand.go:12-18](file://internal/repository/brand.go#L12-L18)
|
||||
- [internal/repository/model.go:12-18](file://internal/repository/model.go#L12-L18)
|
||||
- [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/config.go:18-52](file://internal/config/config.go#L18-L52)
|
||||
- [sql/model.sql:24-35](file://sql/model.sql#L24-L35)
|
||||
|
||||
章节来源
|
||||
- [cmd/server/main.go:38-48](file://cmd/server/main.go#L38-L48)
|
||||
- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41)
|
||||
- [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/repository/brand.go:12-18](file://internal/repository/brand.go#L12-L18)
|
||||
- [internal/repository/model.go:12-18](file://internal/repository/model.go#L12-L18)
|
||||
- [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/config.go:18-52](file://internal/config/config.go#L18-L52)
|
||||
- [sql/model.sql:24-35](file://sql/model.sql#L24-L35)
|
||||
|
||||
## 核心组件
|
||||
- BrandRepository:负责品牌列表查询,支持按名称模糊过滤,返回 Brand 领域对象切片。
|
||||
- ModelRepository:负责型号列表查询,支持按品牌名精确匹配或按型号名模糊匹配,返回 Model 领域对象切片,并对可空字段进行 NullString 到指针字符串的安全转换。
|
||||
- 数据库连接:通过 sql.DB 统一管理连接池,设置最大打开连接数、空闲连接数与连接生命周期,并在启动时进行 Ping 校验。
|
||||
- 配置加载:从环境变量或默认值加载数据库配置,生产环境要求提供密码。
|
||||
- Handler 注入:Gin 控制器通过 NewXxxHandler 构造函数注入 *sql.DB,再由 Handler 内部构造对应的 Repository 实例,形成清晰的依赖注入链路。
|
||||
|
||||
章节来源
|
||||
- [internal/repository/brand.go:20-50](file://internal/repository/brand.go#L20-L50)
|
||||
- [internal/repository/model.go:20-95](file://internal/repository/model.go#L20-L95)
|
||||
- [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/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)
|
||||
|
||||
## 架构总览
|
||||
数据访问层采用“仓储模式”(Repository Pattern)封装数据库访问,将查询逻辑与业务逻辑解耦。Handler 仅依赖 Repository 接口,Repository 依赖 *sql.DB,配置与数据库模块负责基础设施初始化。整体流程如下:
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as "客户端"
|
||||
participant Router as "Gin 路由"
|
||||
participant Handler as "BrandHandler/ModelHandler"
|
||||
participant Repo as "BrandRepository/ModelRepository"
|
||||
participant DB as "sql.DB"
|
||||
participant MySQL as "MySQL 服务器"
|
||||
Client->>Router : "HTTP 请求"
|
||||
Router->>Handler : "分发到对应处理器"
|
||||
Handler->>Repo : "调用 List(...)"
|
||||
Repo->>DB : "QueryContext(ctx, query, args...)"
|
||||
DB->>MySQL : "执行 SQL"
|
||||
MySQL-->>DB : "返回结果集"
|
||||
DB-->>Repo : "Rows"
|
||||
Repo-->>Handler : "领域对象切片"
|
||||
Handler-->>Client : "JSON 或 Base64 响应"
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [internal/handler/brand.go:30-35](file://internal/handler/brand.go#L30-L35)
|
||||
- [internal/handler/model.go:31-36](file://internal/handler/model.go#L31-L36)
|
||||
- [internal/repository/brand.go:31-35](file://internal/repository/brand.go#L31-L35)
|
||||
- [internal/repository/model.go:42-46](file://internal/repository/model.go#L42-L46)
|
||||
- [internal/database/mysql.go:28-43](file://internal/database/mysql.go#L28-L43)
|
||||
|
||||
## 详细组件分析
|
||||
|
||||
### BrandRepository 设计与实现
|
||||
- 设计模式:仓储模式,面向领域模型 Brand,封装查询逻辑。
|
||||
- 关键点:
|
||||
- 支持按品牌名称模糊过滤,无参数时返回全部记录并按名称升序排序。
|
||||
- 使用 QueryContext 传递请求上下文,便于超时与取消控制。
|
||||
- 使用 defer rows.Close() 确保资源释放。
|
||||
- 使用 fmt.Errorf 包裹底层错误,保留调用栈信息。
|
||||
- 错误处理:对查询、扫描、迭代阶段分别进行错误包装,便于定位问题。
|
||||
- 性能优化建议:
|
||||
- 若品牌名称查询频繁,可在 name 字段建立索引(当前表结构未见显式索引,但可考虑)。
|
||||
- 对于大结果集,建议引入分页参数(limit/offset)避免一次性返回过多数据。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class BrandRepository {
|
||||
-db : "*sql.DB"
|
||||
+NewBrandRepository(db) BrandRepository
|
||||
+List(ctx, brandName) []Brand,error
|
||||
}
|
||||
class Brand {
|
||||
+int ID
|
||||
+string Name
|
||||
}
|
||||
BrandRepository --> Brand : "返回领域对象"
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [internal/repository/brand.go:12-18](file://internal/repository/brand.go#L12-L18)
|
||||
- [internal/repository/brand.go:20-50](file://internal/repository/brand.go#L20-L50)
|
||||
- [internal/model/brand.go:3-6](file://internal/model/brand.go#L3-L6)
|
||||
|
||||
章节来源
|
||||
- [internal/repository/brand.go:12-18](file://internal/repository/brand.go#L12-L18)
|
||||
- [internal/repository/brand.go:20-50](file://internal/repository/brand.go#L20-L50)
|
||||
- [internal/model/brand.go:3-6](file://internal/model/brand.go#L3-L6)
|
||||
|
||||
### ModelRepository 设计与实现
|
||||
- 设计模式:仓储模式,面向领域模型 Model,封装查询逻辑。
|
||||
- 关键点:
|
||||
- 支持两种查询条件:按品牌名精确匹配或按型号名模糊匹配;两者皆为空时返回空切片。
|
||||
- 使用自定义 scanModel(rows) 将 sql.NullString 安全转换为 *string,避免空值导致的序列化问题。
|
||||
- 使用 QueryContext 传递上下文,defer rows.Close() 保证资源释放。
|
||||
- 对扫描与迭代阶段进行错误包装。
|
||||
- 表结构要点:model 表包含唯一索引 model_name(brand_name, name),有利于去重与高效检索。
|
||||
- 性能优化建议:
|
||||
- 为 brand_name 建立索引以提升按品牌筛选性能。
|
||||
- 为 name 建立前缀匹配索引(如使用 LIKE '%pattern%' 的场景)。
|
||||
- 引入分页参数,限制单次查询返回数量。
|
||||
|
||||
```mermaid
|
||||
classDiagram
|
||||
class ModelRepository {
|
||||
-db : "*sql.DB"
|
||||
+NewModelRepository(db) ModelRepository
|
||||
+List(ctx, brandName, modelName) []Model,error
|
||||
}
|
||||
class Model {
|
||||
+int ID
|
||||
+string BrandName
|
||||
+string Name
|
||||
+*string Form
|
||||
+*string Rig
|
||||
+*string Source
|
||||
+*string EqKey
|
||||
+time CreateAt
|
||||
}
|
||||
ModelRepository --> Model : "返回领域对象"
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [internal/repository/model.go:12-18](file://internal/repository/model.go#L12-L18)
|
||||
- [internal/repository/model.go:20-95](file://internal/repository/model.go#L20-L95)
|
||||
- [internal/model/model.go:5-14](file://internal/model/model.go#L5-L14)
|
||||
|
||||
章节来源
|
||||
- [internal/repository/model.go:12-18](file://internal/repository/model.go#L12-L18)
|
||||
- [internal/repository/model.go:20-95](file://internal/repository/model.go#L20-L95)
|
||||
- [internal/model/model.go:5-14](file://internal/model/model.go#L5-L14)
|
||||
- [sql/model.sql:34](file://sql/model.sql#L34)
|
||||
|
||||
### Handler 与 Repository 的协作
|
||||
- Handler 通过 NewBrandHandler/NewModelHandler 注入 *sql.DB,内部构造对应 Repository 实例。
|
||||
- Handler 在 GetBrand/GetModel 中读取查询参数,调用 Repository.List(...),并将结果以 JSON 或 Base64 编码返回。
|
||||
- 错误处理:若 Repository 返回错误,Handler 记录日志并返回统一的内部错误响应。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as "客户端"
|
||||
participant H as "BrandHandler"
|
||||
participant R as "BrandRepository"
|
||||
participant DB as "sql.DB"
|
||||
C->>H : "GET /audio/getBrand?brandName=..."
|
||||
H->>R : "List(ctx, brandName)"
|
||||
R->>DB : "QueryContext(ctx, ...)"
|
||||
DB-->>R : "rows"
|
||||
R-->>H : "[]Brand"
|
||||
H-->>C : "JSON 或 Base64"
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [internal/handler/brand.go:26-49](file://internal/handler/brand.go#L26-L49)
|
||||
- [internal/repository/brand.go:20-50](file://internal/repository/brand.go#L20-L50)
|
||||
- [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:26-49](file://internal/handler/brand.go#L26-L49)
|
||||
- [internal/handler/model.go:19-24](file://internal/handler/model.go#L19-L24)
|
||||
- [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-95](file://internal/repository/model.go#L20-L95)
|
||||
|
||||
## 依赖关系分析
|
||||
- 外部依赖:Go MySQL Driver、Gin、Zap 日志、Redis 客户端、Meilisearch 客户端。
|
||||
- 内部依赖:Handler 依赖 Repository;Repository 依赖 *sql.DB;数据库模块负责连接池与 Ping 校验;配置模块负责环境变量解析与校验。
|
||||
- 循环依赖:未发现循环依赖,职责边界清晰。
|
||||
|
||||
```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"]
|
||||
CFG_LOAD --> CFG_DB["internal/config/database.go::loadDatabase"]
|
||||
ROUTER["internal/router/router.go"] --> BRAND_H["internal/handler/brand.go"]
|
||||
ROUTER --> MODEL_H["internal/handler/model.go"]
|
||||
BRAND_H --> BR_REPO["internal/repository/brand.go"]
|
||||
MODEL_H --> MD_REPO["internal/repository/model.go"]
|
||||
BR_REPO --> DB_SQL["*sql.DB"]
|
||||
MD_REPO --> DB_SQL
|
||||
```
|
||||
|
||||
图表来源
|
||||
- [go.mod:5-11](file://go.mod#L5-L11)
|
||||
- [cmd/server/main.go:38-48](file://cmd/server/main.go#L38-L48)
|
||||
- [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/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/repository/brand.go:12-18](file://internal/repository/brand.go#L12-L18)
|
||||
- [internal/repository/model.go:12-18](file://internal/repository/model.go#L12-L18)
|
||||
|
||||
章节来源
|
||||
- [go.mod:5-11](file://go.mod#L5-L11)
|
||||
- [cmd/server/main.go:38-48](file://cmd/server/main.go#L38-L48)
|
||||
- [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/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/repository/brand.go:12-18](file://internal/repository/brand.go#L12-L18)
|
||||
- [internal/repository/model.go:12-18](file://internal/repository/model.go#L12-L18)
|
||||
|
||||
## 性能考量
|
||||
- 连接池配置
|
||||
- 最大打开连接数:25
|
||||
- 最大空闲连接数:5
|
||||
- 连接最大生命周期:5 分钟
|
||||
- 启动时使用 PingContext 进行健康检查,失败则关闭连接并返回错误
|
||||
- 查询优化
|
||||
- BrandRepository:按名称模糊匹配,建议在 name 上建立合适索引;对大结果集引入分页。
|
||||
- ModelRepository:按品牌名精确匹配或按型号名模糊匹配,建议为 brand_name 与 name 建立索引;对大结果集引入分页。
|
||||
- 并发安全
|
||||
- *sql.DB 是并发安全的,可在多个 goroutine 中共享使用;Repository 实例不持有状态,亦可并发安全使用。
|
||||
- 资源清理
|
||||
- 使用 defer rows.Close() 保证结果集关闭;在 main 中 defer db.Close() 保证应用退出时关闭连接池。
|
||||
- 事务处理
|
||||
- 当前实现均为只读查询,未涉及事务;如需写操作,应在 Repository 层封装事务,使用 sql.Tx 并在错误时回滚,成功时提交。
|
||||
|
||||
章节来源
|
||||
- [internal/database/mysql.go:33-43](file://internal/database/mysql.go#L33-L43)
|
||||
- [internal/repository/brand.go:31-35](file://internal/repository/brand.go#L31-L35)
|
||||
- [internal/repository/model.go:42-46](file://internal/repository/model.go#L42-L46)
|
||||
- [cmd/server/main.go:42](file://cmd/server/main.go#L42)
|
||||
|
||||
## 故障排查指南
|
||||
- 连接失败
|
||||
- 现象:启动时 Ping 失败或无法连接数据库。
|
||||
- 排查:检查 DATABASE_HOST/DATABASE_PORT/DATABASE_NAME/DATABASE_USER/DATABASE_PASSWORD 等环境变量;确认网络连通性;核对生产环境必须提供 DATABASE_PASSWORD。
|
||||
- 查询错误
|
||||
- 现象:Handler 返回内部错误。
|
||||
- 排查:查看日志中错误上下文(query brand/query model/scan brand/scan model/iterate brand/iterate model),定位具体环节;检查 SQL 参数绑定与字段映射。
|
||||
- 结果为空
|
||||
- 现象:ModelRepository 在两种条件都为空时返回空切片。
|
||||
- 排查:确认传入的查询参数是否正确;检查表中是否存在匹配数据。
|
||||
- 资源泄漏
|
||||
- 现象:长时间运行后连接数异常。
|
||||
- 排查:确认是否遗漏 rows.Close();检查连接池配置是否合理;观察连接生命周期与空闲连接上限。
|
||||
|
||||
章节来源
|
||||
- [internal/config/database.go:17-72](file://internal/config/database.go#L17-L72)
|
||||
- [internal/handler/brand.go:30-35](file://internal/handler/brand.go#L30-L35)
|
||||
- [internal/handler/model.go:31-36](file://internal/handler/model.go#L31-L36)
|
||||
- [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)
|
||||
|
||||
## 结论
|
||||
Luxsin 的数据访问层采用清晰的仓储模式,将数据库访问与业务逻辑解耦,具备良好的可维护性与扩展性。通过合理的连接池配置、上下文传播与错误包装,实现了稳定可靠的查询能力。建议后续在查询性能上引入索引与分页,在写操作上引入事务封装,并持续完善监控与日志体系。
|
||||
|
||||
## 附录:扩展新 Repository 指南
|
||||
- 设计原则
|
||||
- 保持 Repository 无状态,仅依赖 *sql.DB。
|
||||
- 查询方法接收 context.Context,便于超时与取消控制。
|
||||
- 对外返回领域模型(Model),避免直接暴露数据库结构。
|
||||
- 对可空字段使用 sql.NullString 到指针字符串的安全转换。
|
||||
- 实现步骤
|
||||
- 定义领域模型(Model)与 Repository 接口/实现。
|
||||
- 在 Handler 中注入 *sql.DB,构造 Repository 实例。
|
||||
- 在路由中注册对应处理器。
|
||||
- 在 main 中确保 *sql.DB 注入到 Handler。
|
||||
- 注意事项
|
||||
- 必须在每个查询后 defer rows.Close()。
|
||||
- 使用 fmt.Errorf 包裹底层错误,保留调用栈信息。
|
||||
- 生产环境务必提供数据库密码等敏感配置。
|
||||
- 如需写操作,封装事务并在错误时回滚,成功时提交。
|
||||
- 对高频查询建立合适的索引,必要时引入分页参数。
|
||||
|
||||
章节来源
|
||||
- [internal/repository/brand.go:12-18](file://internal/repository/brand.go#L12-L18)
|
||||
- [internal/repository/model.go:12-18](file://internal/repository/model.go#L12-L18)
|
||||
- [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/router/router.go:22-25](file://internal/router/router.go#L22-L25)
|
||||
- [cmd/server/main.go:64](file://cmd/server/main.go#L64)
|
||||
Reference in New Issue
Block a user