From 36c6ca276628c62822c5b35e3772f1e2f1340f71 Mon Sep 17 00:00:00 2001 From: yangy Date: Wed, 27 May 2026 18:07:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 6 +- .../zh/content/API 接口文档/API 接口文档.md | 466 +++++++++++++++ .../zh/content/API 接口文档/品牌管理接口.md | 523 +++++++++++++++++ .../zh/content/API 接口文档/型号管理接口.md | 426 ++++++++++++++ .../zh/content/API 接口文档/系统监控接口.md | 518 +++++++++++++++++ .../zh/content/API 接口文档/设备管理接口.md | 414 +++++++++++++ .../repowiki/zh/content/基础设施/基础设施.md | 383 ++++++++++++ .../repowiki/zh/content/基础设施/搜索引擎.md | 355 ++++++++++++ .../repowiki/zh/content/基础设施/日志系统.md | 382 ++++++++++++ .../repowiki/zh/content/基础设施/缓存系统.md | 326 +++++++++++ .qoder/repowiki/zh/content/开发指南.md | 410 +++++++++++++ .qoder/repowiki/zh/content/快速开始.md | 317 ++++++++++ .qoder/repowiki/zh/content/故障排除.md | 545 ++++++++++++++++++ .qoder/repowiki/zh/content/数据库设计.md | 403 +++++++++++++ .../zh/content/核心模块/业务处理器.md | 457 +++++++++++++++ .../repowiki/zh/content/核心模块/数据模型.md | 466 +++++++++++++++ .../zh/content/核心模块/数据访问层.md | 374 ++++++++++++ .../repowiki/zh/content/核心模块/核心模块.md | 513 +++++++++++++++++ .../repowiki/zh/content/核心模块/统一响应.md | 366 ++++++++++++ .../zh/content/系统架构/中间件模式.md | 336 +++++++++++ .../zh/content/系统架构/依赖注入模式.md | 371 ++++++++++++ .../zh/content/系统架构/分层架构设计.md | 357 ++++++++++++ .../repowiki/zh/content/系统架构/系统架构.md | 484 ++++++++++++++++ .../zh/content/系统架构/组件交互机制.md | 383 ++++++++++++ .../repowiki/zh/content/系统架构/路由系统.md | 437 ++++++++++++++ .qoder/repowiki/zh/content/部署运维.md | 344 +++++++++++ .qoder/repowiki/zh/content/项目概述.md | 335 +++++++++++ .../repowiki/zh/meta/repowiki-metadata.json | 1 + cmd/server/main.go | 43 +- internal/cache/brand_cache.go | 44 ++ internal/cache/model_cache.go | 69 +++ internal/config/redis.go | 6 +- internal/handler/brand.go | 5 +- internal/handler/model.go | 5 +- internal/repository/brand.go | 55 +- internal/repository/model.go | 118 +++- internal/router/router.go | 19 +- 37 files changed, 11023 insertions(+), 39 deletions(-) create mode 100644 .qoder/repowiki/zh/content/API 接口文档/API 接口文档.md create mode 100644 .qoder/repowiki/zh/content/API 接口文档/品牌管理接口.md create mode 100644 .qoder/repowiki/zh/content/API 接口文档/型号管理接口.md create mode 100644 .qoder/repowiki/zh/content/API 接口文档/系统监控接口.md create mode 100644 .qoder/repowiki/zh/content/API 接口文档/设备管理接口.md create mode 100644 .qoder/repowiki/zh/content/基础设施/基础设施.md create mode 100644 .qoder/repowiki/zh/content/基础设施/搜索引擎.md create mode 100644 .qoder/repowiki/zh/content/基础设施/日志系统.md create mode 100644 .qoder/repowiki/zh/content/基础设施/缓存系统.md create mode 100644 .qoder/repowiki/zh/content/开发指南.md create mode 100644 .qoder/repowiki/zh/content/快速开始.md create mode 100644 .qoder/repowiki/zh/content/故障排除.md create mode 100644 .qoder/repowiki/zh/content/数据库设计.md create mode 100644 .qoder/repowiki/zh/content/核心模块/业务处理器.md create mode 100644 .qoder/repowiki/zh/content/核心模块/数据模型.md create mode 100644 .qoder/repowiki/zh/content/核心模块/数据访问层.md create mode 100644 .qoder/repowiki/zh/content/核心模块/核心模块.md create mode 100644 .qoder/repowiki/zh/content/核心模块/统一响应.md create mode 100644 .qoder/repowiki/zh/content/系统架构/中间件模式.md create mode 100644 .qoder/repowiki/zh/content/系统架构/依赖注入模式.md create mode 100644 .qoder/repowiki/zh/content/系统架构/分层架构设计.md create mode 100644 .qoder/repowiki/zh/content/系统架构/系统架构.md create mode 100644 .qoder/repowiki/zh/content/系统架构/组件交互机制.md create mode 100644 .qoder/repowiki/zh/content/系统架构/路由系统.md create mode 100644 .qoder/repowiki/zh/content/部署运维.md create mode 100644 .qoder/repowiki/zh/content/项目概述.md create mode 100644 .qoder/repowiki/zh/meta/repowiki-metadata.json create mode 100644 internal/cache/brand_cache.go create mode 100644 internal/cache/model_cache.go diff --git a/.env.example b/.env.example index 5953c70..721e2a3 100644 --- a/.env.example +++ b/.env.example @@ -19,7 +19,7 @@ GIN_MODE=debug # MEILISEARCH_INDEX=models # Redis (development defaults apply when APP_ENV=development and vars are unset) -# REDIS_HOST=ec2-3-69-138-29.eu-central-1.compute.amazonaws.com -# REDIS_PORT=16279 -# REDIS_PASSWORD=eafon123! +# REDIS_HOST=localhost +# REDIS_PORT=6379 +# REDIS_PASSWORD= # REDIS_DATABASE=1 diff --git a/.qoder/repowiki/zh/content/API 接口文档/API 接口文档.md b/.qoder/repowiki/zh/content/API 接口文档/API 接口文档.md new file mode 100644 index 0000000..2905b40 --- /dev/null +++ b/.qoder/repowiki/zh/content/API 接口文档/API 接口文档.md @@ -0,0 +1,466 @@ +# API 接口文档 + + +**本文引用的文件** +- [cmd/server/main.go](file://cmd/server/main.go) +- [internal/router/router.go](file://internal/router/router.go) +- [internal/handler/health.go](file://internal/handler/health.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/handler/device.go](file://internal/handler/device.go) +- [internal/response/response.go](file://internal/response/response.go) +- [internal/middleware/cors.go](file://internal/middleware/cors.go) +- [internal/middleware/logger.go](file://internal/middleware/logger.go) +- [internal/middleware/request_id.go](file://internal/middleware/request_id.go) +- [internal/search/meilisearch.go](file://internal/search/meilisearch.go) +- [internal/repository/brand.go](file://internal/repository/brand.go) +- [internal/repository/model.go](file://internal/repository/model.go) +- [pkg/encode/base64.go](file://pkg/encode/base64.go) +- [internal/config/config.go](file://internal/config/config.go) +- [README.md](file://README.md) + + +## 目录 +1. [简介](#简介) +2. [项目结构](#项目结构) +3. [核心组件](#核心组件) +4. [架构总览](#架构总览) +5. [详细接口说明](#详细接口说明) +6. [依赖关系分析](#依赖关系分析) +7. [性能与可用性](#性能与可用性) +8. [故障排查指南](#故障排查指南) +9. [结论](#结论) +10. [附录](#附录) + +## 简介 +本文件为 Luxsin 应用 API 的完整接口文档,覆盖品牌管理、型号管理、设备上报与系统健康检查等接口。文档提供各接口的 HTTP 方法、URL 模式、请求参数、响应格式、错误处理、使用示例与最佳实践,并说明认证方式、速率限制策略、版本信息、客户端实现建议、调试与监控方法,以及向后兼容性与迁移注意事项。 + +## 项目结构 +- 入口程序负责初始化配置、数据库、搜索引擎与缓存,构建路由并启动 HTTP 服务。 +- 路由层按版本与业务域分组,注册各处理器。 +- 处理器负责参数解析、调用仓储或搜索客户端、返回统一响应格式。 +- 统一响应体封装了状态码、消息与数据字段,便于前端一致化处理。 +- 中间件提供 CORS、日志与请求 ID 注入,便于跨域、可观测与追踪。 +- 搜索与缓存分别对接 Meilisearch 与 Redis,支撑型号检索与设备上报数据存储。 + +```mermaid +graph TB +subgraph "服务进程" +MAIN["cmd/server/main.go
启动与生命周期"] +ROUTER["internal/router/router.go
路由注册"] +end +subgraph "HTTP 层" +CORS["internal/middleware/cors.go
CORS"] +LOGMW["internal/middleware/logger.go
访问日志"] +RID["internal/middleware/request_id.go
请求ID注入"] +end +subgraph "业务层" +HEALTH["internal/handler/health.go
健康检查"] +BRAND["internal/handler/brand.go
品牌列表"] +MODEL["internal/handler/model.go
型号详情"] +MODELLIST["internal/handler/model_list.go
型号检索"] +DEVICE["internal/handler/device.go
设备上报"] +end +subgraph "数据与外部服务" +RESP["internal/response/response.go
统一响应"] +DB["MySQL(由配置驱动)"] +MS["Meilisearch(由配置驱动)"] +REDIS["Redis(由配置驱动)"] +end +MAIN --> ROUTER +ROUTER --> CORS --> LOGMW --> RID +ROUTER --> HEALTH +ROUTER --> BRAND +ROUTER --> MODEL +ROUTER --> MODELLIST +ROUTER --> DEVICE +BRAND --> DB +MODEL --> DB +MODELLIST --> MS +DEVICE --> REDIS +HEALTH --> RESP +BRAND --> RESP +MODEL --> RESP +MODELLIST --> RESP +DEVICE --> RESP +``` + +图表来源 +- [cmd/server/main.go:22-96](file://cmd/server/main.go#L22-L96) +- [internal/router/router.go:14-42](file://internal/router/router.go#L14-L42) +- [internal/middleware/cors.go:7-21](file://internal/middleware/cors.go#L7-L21) +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) +- [internal/middleware/request_id.go:20-31](file://internal/middleware/request_id.go#L20-L31) +- [internal/handler/health.go:14-19](file://internal/handler/health.go#L14-L19) +- [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/handler/device.go:26-85](file://internal/handler/device.go#L26-L85) +- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37) + +章节来源 +- [cmd/server/main.go:22-96](file://cmd/server/main.go#L22-L96) +- [internal/router/router.go:14-42](file://internal/router/router.go#L14-L42) +- [README.md:1-123](file://README.md#L1-L123) + +## 核心组件 +- 路由与中间件 + - 路由在 /api/v1 下提供健康检查,在 /audio 下提供品牌、型号、型号检索与设备上报接口。 + - 中间件链:恢复、请求 ID、日志、CORS。 +- 统一响应 + - 所有接口返回统一结构,包含 code、message、data 字段;错误时 code 为非零。 +- 搜索与缓存 + - 型号检索通过 Meilisearch 客户端完成;设备上报通过 Redis Hash 存储。 +- 编码扩展 + - 支持 base64Resp 参数,将响应体 JSON 再做自定义 Base64 编码传输,便于某些网络环境或协议需求。 + +章节来源 +- [internal/router/router.go:14-42](file://internal/router/router.go#L14-L42) +- [internal/middleware/cors.go:7-21](file://internal/middleware/cors.go#L7-L21) +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) +- [internal/middleware/request_id.go:20-31](file://internal/middleware/request_id.go#L20-L31) +- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37) +- [internal/search/meilisearch.go:17-46](file://internal/search/meilisearch.go#L17-L46) +- [pkg/encode/base64.go:44-52](file://pkg/encode/base64.go#L44-L52) + +## 架构总览 +以下序列图展示一次典型请求从接入到响应的全链路: + +```mermaid +sequenceDiagram +participant C as "客户端" +participant G as "Gin 路由" +participant MW as "中间件(CORS/日志/请求ID)" +participant H as "处理器" +participant S as "搜索/缓存/数据库" +C->>G : "HTTP 请求" +G->>MW : "中间件链" +MW-->>G : "注入请求ID/日志/CORS" +G->>H : "匹配路由并调用处理器" +H->>S : "读取/写入(数据库/搜索/缓存)" +S-->>H : "结果/错误" +H-->>G : "统一响应体" +G-->>C : "HTTP 响应(JSON/可选base64)" +``` + +图表来源 +- [internal/router/router.go:27-38](file://internal/router/router.go#L27-L38) +- [internal/middleware/cors.go:7-21](file://internal/middleware/cors.go#L7-L21) +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) +- [internal/middleware/request_id.go:20-31](file://internal/middleware/request_id.go#L20-L31) +- [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/handler/device.go:26-85](file://internal/handler/device.go#L26-L85) +- [internal/response/response.go:15-37](file://internal/response/response.go#L15-L37) + +## 详细接口说明 + +### 版本与基础信息 +- 版本分组 + - /api/v1:健康检查 + - /audio:品牌、型号、型号检索、设备上报 +- 认证方式 + - 当前实现未内置鉴权逻辑,未发现显式的 Token/签名/密钥校验流程。 +- 速率限制 + - 当前实现未内置全局限流策略,如需请结合网关或中间件扩展。 +- 基础响应结构 + - 成功:code=0,message="ok",data 为业务数据 + - 失败:code 非 0,message 为错误描述 +- base64Resp + - 可选查询参数 base64Resp=true|1|空 时,将响应体 JSON 再进行自定义 Base64 编码返回;默认开启。 + +章节来源 +- [internal/router/router.go:27-38](file://internal/router/router.go#L27-L38) +- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37) +- [pkg/encode/base64.go:44-52](file://pkg/encode/base64.go#L44-L52) + +### 健康检查 /api/v1/health +- 方法与路径 + - GET /api/v1/health +- 请求参数 + - 无 +- 响应 + - data.status = "up" +- 错误处理 + - 无内部错误路径,始终返回成功 +- 使用场景 + - 服务存活探测、负载均衡探活 +- 示例 + - curl: [README.md:83-99](file://README.md#L83-L99) + +章节来源 +- [internal/router/router.go:29](file://internal/router/router.go#L29) +- [internal/handler/health.go:14-19](file://internal/handler/health.go#L14-L19) +- [README.md:83-99](file://README.md#L83-L99) + +### 品牌管理 /audio/getBrand +- 方法与路径 + - GET /audio/getBrand +- 请求参数 + - brandName: 可选,用于模糊过滤品牌名称 + - base64Resp: 可选,是否对响应体进行二次 Base64 编码 +- 响应数据 + - 列表项包含品牌 id 与 name +- 错误处理 + - 数据库查询异常:统一返回内部错误 + - 编码异常:统一返回内部错误 +- 使用场景 + - 获取品牌列表,支持按名称模糊筛选 +- 示例 + - curl: [README.md:101-109](file://README.md#L101-L109) + +章节来源 +- [internal/router/router.go:34](file://internal/router/router.go#L34) +- [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) +- [internal/response/response.go:15-37](file://internal/response/response.go#L15-L37) +- [pkg/encode/base64.go:44-52](file://pkg/encode/base64.go#L44-L52) +- [README.md:101-109](file://README.md#L101-L109) + +### 型号管理 /audio/getModel +- 方法与路径 + - GET /audio/getModel +- 请求参数 + - brandName: 可选,按品牌过滤 + - modelName: 可选,按型号名称模糊过滤 + - base64Resp: 可选,是否对响应体进行二次 Base64 编码 +- 响应数据 + - 列表项包含 id、brand_name、name、form、rig、source、eq_key、create_at;部分字段可能为空 +- 错误处理 + - 数据库查询异常:统一返回内部错误 + - 编码异常:统一返回内部错误 +- 使用场景 + - 获取指定品牌或型号的详细列表 +- 示例 + - 无直接示例,但可通过组合 brandName 与 modelName 参数实现不同维度查询 + +章节来源 +- [internal/router/router.go:35](file://internal/router/router.go#L35) +- [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) +- [internal/response/response.go:15-37](file://internal/response/response.go#L15-L37) +- [pkg/encode/base64.go:44-52](file://pkg/encode/base64.go#L44-L52) + +### 型号检索 /audio/modelList +- 方法与路径 + - GET /audio/modelList +- 请求参数 + - key: 必填,检索关键词 + - count: 可选,最大返回条数,默认 100 + - base64Resp: 可选,是否对响应体进行二次 Base64 编码 +- 响应数据 + - 列表项包含 rig、form、name、brand_name、source、eq_key 等字段 +- 错误处理 + - 搜索异常:统一返回内部错误 + - 编码异常:统一返回内部错误 +- 使用场景 + - 基于关键词的型号检索,适用于推荐、搜索框等 +- 示例 + - 无直接示例,但可参考 Meilisearch 的检索行为 + +章节来源 +- [internal/router/router.go:36](file://internal/router/router.go#L36) +- [internal/handler/model_list.go:26-57](file://internal/handler/model_list.go#L26-L57) +- [internal/search/meilisearch.go:22-46](file://internal/search/meilisearch.go#L22-L46) +- [internal/response/response.go:15-37](file://internal/response/response.go#L15-L37) +- [pkg/encode/base64.go:44-52](file://pkg/encode/base64.go#L44-L52) + +### 设备上报 /audio/reportDevInfo +- 方法与路径 + - GET /audio/reportDevInfo +- 请求参数 + - mac: 必填,设备 MAC 地址 + - model: 必填,设备型号 + - ver: 可选,版本号 + - X-Forwarded-For: 可选,客户端 IP(由代理注入) +- 响应 + - 成功:code=200,msg="操作成功" + - 参数缺失:code=400,msg="参数校验失败" + - 系统错误:code=500,msg="系统错误" +- 存储行为 + - 将设备信息以 JSON 形式写入 Redis Hash,键为 devices,field 为 mac +- 错误处理 + - 参数校验失败:返回业务错误 + - JSON 序列化失败:返回内部错误 + - Redis 写入失败:返回内部错误 +- 使用场景 + - 客户端上报设备活跃信息,便于后续统计与分析 +- 示例 + - 无直接示例,可参考响应结构与参数说明 + +章节来源 +- [internal/router/router.go:37](file://internal/router/router.go#L37) +- [internal/handler/device.go:26-85](file://internal/handler/device.go#L26-L85) +- [internal/response/response.go:15-37](file://internal/response/response.go#L15-L37) + +## 依赖关系分析 + +```mermaid +classDiagram +class Router { ++New(log, db, search, redis) Engine +} +class HealthHandler +class BrandHandler { +-repo BrandRepository ++GetBrand(c) +} +class ModelHandler { +-repo ModelRepository ++GetModel(c) +} +class ModelListHandler { +-search SearchClient ++ModelList(c) +} +class DeviceHandler { +-redis RedisClient ++ReportDevInfo(c) +} +class BrandRepository { ++List(ctx, brandName) []Brand +} +class ModelRepository { ++List(ctx, brandName, modelName) []Model +} +class SearchClient { ++ModelList(ctx, key, count) []map +} +class Response { ++OK(c, data) ++Fail(c, httpStatus, code, msg) ++BadRequest(c, msg) ++InternalError(c, msg) +} +class Middleware_CORS +class Middleware_Logger +class Middleware_RequestID +Router --> HealthHandler +Router --> BrandHandler +Router --> ModelHandler +Router --> ModelListHandler +Router --> DeviceHandler +BrandHandler --> BrandRepository +ModelHandler --> ModelRepository +ModelListHandler --> SearchClient +DeviceHandler --> Response +BrandHandler --> Response +ModelHandler --> Response +ModelListHandler --> Response +HealthHandler --> Response +Router --> Middleware_CORS +Router --> Middleware_Logger +Router --> Middleware_RequestID +``` + +图表来源 +- [internal/router/router.go:14-42](file://internal/router/router.go#L14-L42) +- [internal/handler/health.go:8-18](file://internal/handler/health.go#L8-L18) +- [internal/handler/brand.go:14-24](file://internal/handler/brand.go#L14-L24) +- [internal/handler/model.go:14-24](file://internal/handler/model.go#L14-L24) +- [internal/handler/model_list.go:14-24](file://internal/handler/model_list.go#L14-L24) +- [internal/handler/device.go:14-24](file://internal/handler/device.go#L14-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/search/meilisearch.go:13-20](file://internal/search/meilisearch.go#L13-L20) +- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37) +- [internal/middleware/cors.go:7-21](file://internal/middleware/cors.go#L7-L21) +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) +- [internal/middleware/request_id.go:20-31](file://internal/middleware/request_id.go#L20-L31) + +## 性能与可用性 +- 并发与超时 + - 服务器设置读/写超时与空闲超时,避免慢请求占用资源。 +- 日志与追踪 + - 中间件记录状态码、方法、路径、延迟、IP、请求 ID,便于定位问题。 +- 缓存与搜索 + - 型号检索使用 Meilisearch,具备高性能全文检索能力;设备上报使用 Redis Hash,适合高并发写入。 +- 响应体积控制 + - base64Resp 可降低传输体积,但会增加 CPU 开销;建议在带宽受限或协议限制场景启用。 +- 建议 + - 对高频接口增加本地缓存(如品牌/型号列表)与 CDN 加速静态资源。 + - 对设备上报接口增加幂等与去重策略,避免重复写入。 + +章节来源 +- [cmd/server/main.go:66-72](file://cmd/server/main.go#L66-L72) +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) +- [internal/search/meilisearch.go:22-46](file://internal/search/meilisearch.go#L22-L46) +- [pkg/encode/base64.go:44-52](file://pkg/encode/base64.go#L44-L52) + +## 故障排查指南 +- 健康检查失败 + - 检查 /api/v1/health 是否返回 status=up;若失败,查看服务日志与数据库连接状态。 +- 品牌/型号接口异常 + - 关注数据库连接与查询异常日志;确认 SQL 查询条件与参数传递正确。 +- 型号检索失败 + - 检查 Meilisearch 连接、索引配置与 API Key;确认关键词与返回字段。 +- 设备上报失败 + - 检查 Redis 连接与 HSET 写入;确认 mac 与 model 参数必填;关注 JSON 序列化错误。 +- 统一错误响应 + - 所有错误均通过统一响应体返回,code 非 0 表示失败,message 描述错误原因。 + +章节来源 +- [internal/handler/health.go:14-19](file://internal/handler/health.go#L14-L19) +- [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/handler/model_list.go:37-42](file://internal/handler/model_list.go#L37-L42) +- [internal/handler/device.go:61-78](file://internal/handler/device.go#L61-L78) +- [internal/response/response.go:23-37](file://internal/response/response.go#L23-L37) + +## 结论 +本项目采用清晰的分层架构与统一响应体设计,覆盖品牌、型号、检索与设备上报等核心业务场景。当前未内置鉴权与限流机制,建议在生产环境中补充安全与容量治理措施。通过中间件与日志体系,系统具备良好的可观测性,便于运维与排障。 + +## 附录 + +### 端到端调用流程(示例) + +```mermaid +sequenceDiagram +participant Client as "客户端" +participant Router as "路由" +participant Handler as "处理器" +participant DBMS as "数据库" +participant Search as "Meilisearch" +participant Cache as "Redis" +Client->>Router : "GET /audio/getBrand?brandName=sony" +Router->>Handler : "GetBrand" +Handler->>DBMS : "查询品牌列表" +DBMS-->>Handler : "品牌列表" +Handler-->>Router : "统一响应" +Router-->>Client : "JSON/可选base64" +Client->>Router : "GET /audio/modelList?key=wha&count=50" +Router->>Handler : "ModelList" +Handler->>Search : "关键词检索" +Search-->>Handler : "检索结果" +Handler-->>Router : "统一响应" +Router-->>Client : "JSON/可选base64" +Client->>Router : "GET /audio/reportDevInfo?mac=xx&model=yy&ver=v1" +Router->>Handler : "ReportDevInfo" +Handler->>Cache : "HSET devices : mac -> JSON" +Cache-->>Handler : "写入成功" +Handler-->>Router : "统一响应" +Router-->>Client : "JSON" +``` + +图表来源 +- [internal/router/router.go:34-37](file://internal/router/router.go#L34-L37) +- [internal/handler/brand.go:26-50](file://internal/handler/brand.go#L26-L50) +- [internal/handler/model_list.go:26-57](file://internal/handler/model_list.go#L26-L57) +- [internal/handler/device.go:26-85](file://internal/handler/device.go#L26-L85) +- [internal/repository/brand.go:20-51](file://internal/repository/brand.go#L20-L51) +- [internal/search/meilisearch.go:22-46](file://internal/search/meilisearch.go#L22-L46) + +### 配置与部署要点 +- 环境变量 + - APP_ENV、APP_HOST、APP_PORT、GIN_MODE 等;数据库、Meilisearch、Redis 配置通过独立模块加载。 +- 启动与运行 + - 支持开发与生产模式;生产模式下 Gin 运行模式切换为 Release。 +- 健康检查 + - 提供 /api/v1/health 作为探活端点。 + +章节来源 +- [internal/config/config.go:18-64](file://internal/config/config.go#L18-L64) +- [cmd/server/main.go:28-30](file://cmd/server/main.go#L28-L30) +- [README.md:83-99](file://README.md#L83-L99) \ No newline at end of file diff --git a/.qoder/repowiki/zh/content/API 接口文档/品牌管理接口.md b/.qoder/repowiki/zh/content/API 接口文档/品牌管理接口.md new file mode 100644 index 0000000..820c013 --- /dev/null +++ b/.qoder/repowiki/zh/content/API 接口文档/品牌管理接口.md @@ -0,0 +1,523 @@ +# 品牌管理接口 + + +**本文引用的文件** +- [brand.go](file://internal/handler/brand.go) +- [brand.go](file://internal/repository/brand.go) +- [brand.go](file://internal/model/brand.go) +- [base64.go](file://pkg/encode/base64.go) +- [router.go](file://internal/router/router.go) +- [response.go](file://internal/response/response.go) +- [main.go](file://cmd/server/main.go) +- [config.go](file://internal/config/config.go) +- [mysql.go](file://internal/database/mysql.go) +- [cors.go](file://internal/middleware/cors.go) +- [logger.go](file://internal/middleware/logger.go) +- [README.md](file://README.md) + + +## 目录 +1. [简介](#简介) +2. [项目结构](#项目结构) +3. [核心组件](#核心组件) +4. [架构概览](#架构概览) +5. [详细组件分析](#详细组件分析) +6. [依赖关系分析](#依赖关系分析) +7. [性能考虑](#性能考虑) +8. [故障排除指南](#故障排除指南) +9. [结论](#结论) +10. [附录](#附录) + +## 简介 + +品牌管理接口是 Luxsin 应用 API 的核心功能模块之一,提供品牌信息的查询服务。该接口支持按品牌名称进行模糊搜索,并提供了灵活的响应格式选项,包括标准 JSON 格式和 Base64 编码格式。 + +本接口采用分层架构设计,包含处理器层、存储库层、模型层和编码层,确保了良好的代码组织和可维护性。接口支持多种响应格式,满足不同客户端的需求,特别是在需要安全传输或特殊字符处理的场景中。 + +## 项目结构 + +该项目采用清晰的分层架构,主要目录结构如下: + +```mermaid +graph TB +subgraph "应用入口" +CMD[cmd/server/main.go] +end +subgraph "核心业务层" +Handler[internal/handler/] +Repository[internal/repository/] +Model[internal/model/] +end +subgraph "基础设施层" +Router[internal/router/router.go] +Middleware[internal/middleware/] +Database[internal/database/] +Config[internal/config/] +end +subgraph "工具包" +Encode[pkg/encode/] +Response[internal/response/] +end +CMD --> Router +Router --> Handler +Handler --> Repository +Repository --> Model +Handler --> Encode +Handler --> Response +Router --> Middleware +CMD --> Database +CMD --> Config +``` + +**图表来源** +- [main.go:1-96](file://cmd/server/main.go#L1-L96) +- [router.go:1-42](file://internal/router/router.go#L1-L42) + +**章节来源** +- [main.go:1-96](file://cmd/server/main.go#L1-L96) +- [router.go:1-42](file://internal/router/router.go#L1-L42) + +## 核心组件 + +### 品牌处理器 (BrandHandler) + +品牌处理器负责处理品牌相关的 HTTP 请求,实现了完整的品牌查询逻辑: + +- **职责分离**:专门处理品牌查询请求,不涉及其他业务逻辑 +- **参数验证**:从查询字符串中提取品牌名称参数 +- **响应控制**:根据 base64Resp 参数决定响应格式 +- **错误处理**:统一的错误处理机制,返回标准化的错误响应 + +### 品牌存储库 (BrandRepository) + +存储库层负责与数据库交互,实现数据访问逻辑: + +- **SQL 查询构建**:动态构建 SQL 查询语句,支持条件过滤 +- **参数化查询**:使用参数化查询防止 SQL 注入攻击 +- **结果映射**:将数据库结果映射到 Brand 结构体 +- **上下文支持**:支持超时和取消的上下文操作 + +### 品牌模型 (Brand) + +简单的数据传输对象,定义了品牌的基本属性: + +- **ID 字段**:整数类型的唯一标识符 +- **Name 字段**:字符串类型的品牌名称 +- **JSON 标签**:支持自动序列化为 JSON 格式 + +### Base64 编码器 + +提供自定义 Base64 编码功能,支持特殊字符的安全传输: + +- **自定义字符映射**:使用特殊的字符集替换标准 Base64 字符 +- **JSON 序列化**:先序列化为 JSON 再进行 Base64 编码 +- **参数解析**:支持 base64Resp 查询参数的解析 + +**章节来源** +- [brand.go:1-50](file://internal/handler/brand.go#L1-L50) +- [brand.go:1-51](file://internal/repository/brand.go#L1-L51) +- [brand.go:1-7](file://internal/model/brand.go#L1-L7) +- [base64.go:1-52](file://pkg/encode/base64.go#L1-L52) + +## 架构概览 + +品牌管理接口采用经典的三层架构模式,各层职责明确,耦合度低: + +```mermaid +sequenceDiagram +participant Client as 客户端 +participant Router as 路由器 +participant Handler as 品牌处理器 +participant Repo as 品牌存储库 +participant DB as MySQL 数据库 +participant Encoder as Base64 编码器 +Client->>Router : GET /audio/getBrand?brandName=sony&base64Resp=true +Router->>Handler : 调用 GetBrand 方法 +Handler->>Handler : 解析查询参数 +Handler->>Repo : List(brandName) +Repo->>DB : 执行 SQL 查询 +DB-->>Repo : 返回查询结果 +Repo-->>Handler : 品牌列表 +alt base64Resp = true +Handler->>Encoder : EncodeJSON(品牌列表) +Encoder-->>Handler : Base64 编码结果 +Handler-->>Client : 返回编码后的响应 +else base64Resp = false +Handler-->>Client : 返回 JSON 格式响应 +end +``` + +**图表来源** +- [brand.go:26-49](file://internal/handler/brand.go#L26-L49) +- [brand.go:20-50](file://internal/repository/brand.go#L20-L50) +- [base64.go:35-42](file://pkg/encode/base64.go#L35-L42) + +## 详细组件分析 + +### 接口规范 + +#### HTTP 方法和 URL 模式 +- **HTTP 方法**:GET +- **基础路径**:/audio +- **端点**:/getBrand +- **完整 URL**:`/audio/getBrand` + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 默认值 | 描述 | +|--------|------|------|--------|------| +| brandName | string | 否 | 空 | 品牌名称查询参数,支持模糊匹配 | +| base64Resp | boolean | 否 | true | 响应格式控制参数 | + +**Base64 响应选项详解**: +- **默认行为**:base64Resp=true(启用 Base64 编码) +- **禁用方式**:base64Resp=false 或 base64Resp=0 +- **适用场景**:需要安全传输或特殊字符处理的场景 + +#### 响应格式 + +**标准 JSON 格式**: +```json +{ + "code": 0, + "message": "ok", + "data": [ + { + "id": 1, + "name": "Sony" + }, + { + "id": 2, + "name": "Apple" + } + ] +} +``` + +**Base64 编码格式**: +响应内容为 JSON 序列化后经过自定义 Base64 编码的字符串。 + +#### 错误处理 + +系统提供统一的错误处理机制: + +| 错误类型 | HTTP 状态码 | 错误代码 | 描述 | +|----------|-------------|----------|------| +| 参数错误 | 400 | 40000 | 请求参数无效 | +| 服务器错误 | 500 | 50000 | 服务器内部错误 | +| 数据库错误 | 500 | 50000 | 数据库操作失败 | + +**章节来源** +- [brand.go:26-49](file://internal/handler/brand.go#L26-L49) +- [response.go:15-37](file://internal/response/response.go#L15-L37) + +### 数据流分析 + +```mermaid +flowchart TD +Start([请求到达]) --> ParseParam["解析查询参数
brandName, base64Resp"] +ParseParam --> ValidateParam{"参数验证"} +ValidateParam --> |有效| BuildQuery["构建 SQL 查询"] +ValidateParam --> |无效| ReturnBadRequest["返回 400 错误"] +BuildQuery --> ExecuteQuery["执行数据库查询"] +ExecuteQuery --> QuerySuccess{"查询成功?"} +QuerySuccess --> |否| ReturnServerError["返回 500 错误"] +QuerySuccess --> |是| CheckFormat{"检查响应格式"} +CheckFormat --> |Base64| EncodeResponse["JSON 编码 + Base64 编码"] +CheckFormat --> |标准| ReturnJSON["直接返回 JSON"] +EncodeResponse --> SendResponse["发送响应"] +ReturnJSON --> SendResponse +ReturnBadRequest --> End([结束]) +ReturnServerError --> End +SendResponse --> End +``` + +**图表来源** +- [brand.go:26-49](file://internal/handler/brand.go#L26-L49) +- [brand.go:20-50](file://internal/repository/brand.go#L20-L50) + +### 类关系图 + +```mermaid +classDiagram +class BrandHandler { +-repo : BrandRepository +-log : Logger ++NewBrandHandler(db, log) BrandHandler ++GetBrand(c) void +} +class BrandRepository { +-db : sql.DB ++NewBrandRepository(db) BrandRepository ++List(ctx, brandName) []Brand +} +class Brand { ++int ID ++string Name +} +class Base64Encoder { ++CustomBase64Encode(data) string ++EncodeJSON(v) string ++ParseBase64Param(c) bool +} +BrandHandler --> BrandRepository : 使用 +BrandRepository --> Brand : 返回 +BrandHandler --> Base64Encoder : 使用 +``` + +**图表来源** +- [brand.go:14-24](file://internal/handler/brand.go#L14-L24) +- [brand.go:12-18](file://internal/repository/brand.go#L12-L18) +- [brand.go:3-6](file://internal/model/brand.go#L3-L6) +- [base64.go:21-51](file://pkg/encode/base64.go#L21-L51) + +**章节来源** +- [brand.go:1-50](file://internal/handler/brand.go#L1-L50) +- [brand.go:1-51](file://internal/repository/brand.go#L1-L51) +- [brand.go:1-7](file://internal/model/brand.go#L1-L7) +- [base64.go:1-52](file://pkg/encode/base64.go#L1-L52) + +## 依赖关系分析 + +### 外部依赖 + +系统使用的主要外部库: + +```mermaid +graph LR +subgraph "Web 框架" +Gin[Gin Web Framework] +end +subgraph "数据库" +MySQL[MySQL Driver] +SQL[Go SQL 包] +end +subgraph "日志" +Zap[Zap Logger] +end +subgraph "缓存" +Redis[Redis Client] +end +subgraph "搜索引擎" +Meilisearch[Meilisearch Client] +end +BrandHandler --> Gin +BrandRepository --> MySQL +BrandRepository --> SQL +BrandHandler --> Zap +BrandHandler --> Redis +BrandHandler --> Meilisearch +``` + +**图表来源** +- [main.go:3-19](file://cmd/server/main.go#L3-L19) +- [brand.go:3-12](file://internal/handler/brand.go#L3-L12) + +### 内部依赖关系 + +```mermaid +graph TD +Router[路由层] --> Handler[处理器层] +Handler --> Repository[存储库层] +Repository --> Model[模型层] +Handler --> Encoder[编码器] +Handler --> Response[响应处理] +Router --> Middleware[中间件] +Middleware --> Logger[日志中间件] +Middleware --> CORS[CORS 中间件] +CMD[应用入口] --> Router +CMD --> Database[数据库连接] +CMD --> Config[配置管理] +``` + +**图表来源** +- [router.go:14-41](file://internal/router/router.go#L14-L41) +- [main.go:64-64](file://cmd/server/main.go#L64-L64) + +**章节来源** +- [router.go:1-42](file://internal/router/router.go#L1-L42) +- [main.go:1-96](file://cmd/server/main.go#L1-L96) + +## 性能考虑 + +### 数据库优化 + +- **连接池配置**:最大连接数 25,空闲连接 5,连接最大生命周期 5 分钟 +- **参数化查询**:防止 SQL 注入,提高查询安全性 +- **索引优化**:建议在品牌表的 name 字段上建立索引以提升模糊查询性能 + +### 编码性能 + +- **Base64 编码开销**:编码后数据大小约为原数据的 1.33 倍 +- **内存使用**:Base64 编码会增加临时内存占用 +- **CPU 消耗**:额外的编码步骤会增加 CPU 开销 + +### 缓存策略 + +虽然当前实现未集成缓存,但可以考虑以下优化方案: +- **查询结果缓存**:热门品牌查询结果可缓存 5-10 分钟 +- **配置缓存**:数据库连接配置可缓存避免重复初始化 +- **响应缓存**:静态品牌数据可考虑缓存 + +## 故障排除指南 + +### 常见问题及解决方案 + +#### 1. 数据库连接失败 + +**症状**:启动时出现数据库连接错误 +**原因**: +- 数据库配置不正确 +- 网络连接问题 +- 认证凭据错误 + +**解决方法**: +- 检查数据库配置参数 +- 验证网络连通性 +- 确认用户名和密码正确 + +#### 2. 查询结果为空 + +**症状**:查询返回空数组 +**可能原因**: +- 品牌名称参数不匹配 +- 数据库中无对应数据 +- 查询条件过于严格 + +**解决方法**: +- 检查品牌名称拼写 +- 验证数据库中是否存在数据 +- 调整查询条件 + +#### 3. Base64 编码错误 + +**症状**:Base64 编码失败 +**原因**: +- JSON 序列化失败 +- 编码过程中的异常 + +**解决方法**: +- 检查数据结构的有效性 +- 查看服务器日志获取详细错误信息 + +### 调试技巧 + +#### 日志分析 + +系统提供了详细的日志记录功能: + +```mermaid +flowchart TD +Request[HTTP 请求] --> LogStart["记录请求开始
时间戳, IP, 方法, 路径"] +LogStart --> Process[处理请求] +Process --> LogEnd["记录请求结束
状态码, 延迟, 错误信息"] +LogEnd --> Response[返回响应] +``` + +**图表来源** +- [logger.go:10-45](file://internal/middleware/logger.go#L10-L45) + +#### 错误监控 + +- **状态码分类**:4xx 错误记录为警告,5xx 错误记录为错误 +- **请求 ID 追踪**:每个请求都有唯一的 ID 便于追踪 +- **查询参数记录**:记录原始查询参数便于调试 + +**章节来源** +- [logger.go:1-46](file://internal/middleware/logger.go#L1-L46) +- [mysql.go:14-47](file://internal/database/mysql.go#L14-L47) + +## 结论 + +品牌管理接口设计合理,实现了清晰的分层架构和良好的错误处理机制。接口提供了灵活的响应格式选项,能够满足不同客户端的需求。通过合理的性能优化和完善的故障排除机制,该接口能够在生产环境中稳定运行。 + +建议在未来版本中考虑添加: +- 品牌数据缓存机制 +- 更详细的查询参数验证 +- 增强的日志记录和监控功能 +- 单元测试覆盖率提升 + +## 附录 + +### API 使用示例 + +#### 获取所有品牌 +```bash +curl "http://localhost:8080/audio/getBrand" +``` + +#### 按名称查询品牌 +```bash +curl "http://localhost:8080/audio/getBrand?brandName=sony" +``` + +#### 禁用 Base64 编码 +```bash +curl "http://localhost:8080/audio/getBrand?brandName=sony&base64Resp=false" +``` + +### 客户端实现指南 + +#### JavaScript 实现 +```javascript +// 基础查询 +async function getBrands(brandName = '', useBase64 = true) { + const params = new URLSearchParams(); + if (brandName) params.append('brandName', brandName); + params.append('base64Resp', useBase64.toString()); + + const response = await fetch(`/audio/getBrand?${params}`); + const data = await response.json(); + + if (useBase64 && typeof data === 'string') { + // 如果是 Base64 编码,需要解码 + return JSON.parse(atob(data)); + } + + return data; +} +``` + +#### Python 实现 +```python +import requests +import base64 +import json + +def get_brands(brand_name='', base64_resp=True): + url = "http://localhost:8080/audio/getBrand" + params = { + 'brandName': brand_name, + 'base64Resp': str(base64_resp).lower() + } + + response = requests.get(url, params=params) + data = response.json() + + if base64_resp and isinstance(data, str): + # Base64 解码 + decoded_data = base64.b64decode(data).decode('utf-8') + return json.loads(decoded_data) + + return data +``` + +### 性能优化建议 + +1. **数据库层面** + - 为品牌名称字段添加索引 + - 实施查询结果缓存 + - 优化模糊查询性能 + +2. **应用层面** + - 合理设置连接池大小 + - 实施请求限流 + - 添加健康检查端点 + +3. **网络层面** + - 启用 HTTP/2 支持 + - 实施 Gzip 压缩 + - 配置 CDN 加速 + +**章节来源** +- [README.md:101-109](file://README.md#L101-L109) \ No newline at end of file diff --git a/.qoder/repowiki/zh/content/API 接口文档/型号管理接口.md b/.qoder/repowiki/zh/content/API 接口文档/型号管理接口.md new file mode 100644 index 0000000..86a0b7f --- /dev/null +++ b/.qoder/repowiki/zh/content/API 接口文档/型号管理接口.md @@ -0,0 +1,426 @@ +# 型号管理接口 + + +**本文档引用的文件** +- [model.go](file://internal/handler/model.go) +- [model_list.go](file://internal/handler/model_list.go) +- [router.go](file://internal/router/router.go) +- [model.go](file://internal/repository/model.go) +- [model.go](file://internal/model/model.go) +- [meilisearch.go](file://internal/search/meilisearch.go) +- [response.go](file://internal/response/response.go) +- [base64.go](file://pkg/encode/base64.go) +- [model.sql](file://sql/model.sql) +- [logger.go](file://internal/middleware/logger.go) +- [cors.go](file://internal/middleware/cors.go) +- [meilisearch.go](file://internal/config/meilisearch.go) +- [README.md](file://README.md) + + +## 目录 +1. [简介](#简介) +2. [项目结构](#项目结构) +3. [核心组件](#核心组件) +4. [架构概览](#架构概览) +5. [详细组件分析](#详细组件分析) +6. [依赖关系分析](#依赖关系分析) +7. [性能考虑](#性能考虑) +8. [故障排除指南](#故障排除指南) +9. [结论](#结论) + +## 简介 + +本文档详细描述了型号管理接口的完整 API 规范,包括获取型号信息和搜索型号列表两个核心接口。该系统采用 Gin 框架构建,支持 MySQL 数据库和 Meilisearch 全文搜索引擎,提供高性能的型号数据检索能力。 + +系统主要功能: +- 获取特定品牌或型号的详细信息 +- 基于关键词的型号搜索 +- 支持 Base64 响应编码优化传输 +- 统一的错误处理和日志记录 + +## 项目结构 + +应用程序采用清晰的分层架构设计,主要目录结构如下: + +```mermaid +graph TB +subgraph "应用层" +Handler[处理器层] +Router[路由层] +Middleware[中间件层] +end +subgraph "业务逻辑层" +Repository[仓储层] +Search[搜索服务] +end +subgraph "数据访问层" +Database[(MySQL数据库)] +MeiliSearch[(Meilisearch搜索引擎)] +end +subgraph "工具层" +Response[响应格式化] +Encode[编码工具] +end +Handler --> Repository +Handler --> Search +Router --> Handler +Middleware --> Router +Repository --> Database +Search --> MeiliSearch +Handler --> Response +Handler --> Encode +``` + +**图表来源** +- [router.go:14-42](file://internal/router/router.go#L14-L42) +- [model.go:14-24](file://internal/handler/model.go#L14-L24) +- [model_list.go:14-24](file://internal/handler/model_list.go#L14-L24) + +**章节来源** +- [router.go:14-42](file://internal/router/router.go#L14-L42) +- [README.md:5-17](file://README.md#L5-L17) + +## 核心组件 + +### 数据模型 + +型号实体包含以下字段结构: + +| 字段名 | 类型 | 描述 | 是否可空 | +|--------|------|------|----------| +| id | int | 型号唯一标识符 | 否 | +| brandName | string | 品牌名称 | 否 | +| name | string | 型号名称 | 否 | +| form | *string | 产品形态 | 是 | +| rig | *string | 适用场景 | 是 | +| source | *string | 来源信息 | 是 | +| eqKey | *string | 等效键值 | 是 | +| createAt | time.Time | 创建时间 | 否 | + +### 统一响应格式 + +所有 API 响应遵循统一的 JSON 结构: + +```json +{ + "code": 0, + "message": "ok", + "data": {} +} +``` + +错误响应格式: +```json +{ + "code": 50000, + "message": "failed to get model list" +} +``` + +**章节来源** +- [model.go:5-15](file://internal/model/model.go#L5-L15) +- [response.go:9-37](file://internal/response/response.go#L9-L37) + +## 架构概览 + +系统采用分层架构,将业务逻辑与数据访问分离: + +```mermaid +sequenceDiagram +participant Client as 客户端 +participant Router as 路由器 +participant Handler as 处理器 +participant Repo as 仓储层 +participant DB as MySQL数据库 +participant Search as 搜索引擎 +participant Meili as Meilisearch +Client->>Router : HTTP请求 +Router->>Handler : 调用相应处理器 +alt 获取型号列表 +Handler->>Repo : List(brandName, modelName) +Repo->>DB : 执行SQL查询 +DB-->>Repo : 返回查询结果 +Repo-->>Handler : 返回型号列表 +else 搜索型号列表 +Handler->>Search : ModelList(key, count) +Search->>Meili : 执行全文搜索 +Meili-->>Search : 返回搜索结果 +Search-->>Handler : 返回搜索列表 +end +Handler-->>Client : 统一JSON响应 +``` + +**图表来源** +- [router.go:32-38](file://internal/router/router.go#L32-L38) +- [model.go:26-50](file://internal/handler/model.go#L26-L50) +- [model_list.go:26-56](file://internal/handler/model_list.go#L26-L56) + +## 详细组件分析 + +### 获取型号信息接口 + +#### 接口规范 + +**HTTP 方法**: GET +**URL 模式**: `/audio/getModel` +**请求参数**: + +| 参数名 | 类型 | 必填 | 描述 | 示例 | +|--------|------|------|------|------| +| brandName | string | 否 | 品牌名称过滤条件 | sony | +| modelName | string | 否 | 型号名称模糊匹配 | wh-1000xm4 | +| base64Resp | boolean | 否 | 是否返回Base64编码响应 | true | + +**响应格式**: JSON 数组,每个元素包含完整的型号信息 + +#### 处理流程 + +```mermaid +flowchart TD +Start([请求进入]) --> ParseParams["解析查询参数
brandName, modelName, base64Resp"] +ParseParams --> ValidateParams{"验证参数有效性"} +ValidateParams --> |有效| CallRepo["调用仓储层查询"] +ValidateParams --> |无效| ReturnError["返回错误响应"] +CallRepo --> ExecQuery["执行数据库查询"] +ExecQuery --> BuildResult["构建响应数据"] +BuildResult --> CheckBase64{"base64Resp为真?"} +CheckBase64 --> |是| EncodeBase64["Base64编码响应"] +CheckBase64 --> |否| ReturnJSON["直接返回JSON"] +EncodeBase64 --> End([响应客户端]) +ReturnJSON --> End +ReturnError --> End +``` + +**图表来源** +- [model.go:26-50](file://internal/handler/model.go#L26-L50) +- [model.go:20-61](file://internal/repository/model.go#L20-L61) + +#### 数据库查询逻辑 + +查询支持三种模式: + +1. **按品牌过滤**: `WHERE brand_name = ? ORDER BY name ASC` +2. **按型号模糊匹配**: `WHERE name LIKE ? ORDER BY name ASC` +3. **无条件查询**: 返回空数组 + +查询结果按型号名称升序排列。 + +**章节来源** +- [model.go:26-50](file://internal/handler/model.go#L26-L50) +- [model.go:20-61](file://internal/repository/model.go#L20-L61) +- [model.sql:24-35](file://sql/model.sql#L24-L35) + +### 搜索型号列表接口 + +#### 接口规范 + +**HTTP 方法**: GET +**URL 模式**: `/audio/modelList` +**请求参数**: + +| 参数名 | 类型 | 必填 | 描述 | 默认值 | +|--------|------|------|------|--------| +| key | string | 是 | 搜索关键词 | - | +| count | int | 否 | 返回结果数量限制 | 100 | +| base64Resp | boolean | 否 | 是否返回Base64编码响应 | true | + +#### 搜索条件 + +搜索支持以下字段的全文检索: +- `rig` (适用场景) +- `form` (产品形态) +- `name` (型号名称) +- `brand_name` (品牌名称) +- `source` (来源信息) +- `eq_key` (等效键值) + +#### 结果排序规则 + +搜索结果按照 Meilisearch 的相关性评分排序,最相关的条目排在前面。 + +#### 处理流程 + +```mermaid +sequenceDiagram +participant Client as 客户端 +participant Handler as ModelListHandler +participant Search as Search.Client +participant Meili as Meilisearch +Client->>Handler : GET /audio/modelList?key=&count= +Handler->>Handler : 解析查询参数 +Handler->>Handler : 设置默认count=100 +Handler->>Search : ModelList(ctx, key, count) +Search->>Meili : 执行全文搜索 +Meili-->>Search : 返回搜索结果 +Search-->>Handler : 返回命中列表 +Handler->>Handler : 处理Base64响应 +Handler-->>Client : 统一JSON响应 +``` + +**图表来源** +- [model_list.go:26-56](file://internal/handler/model_list.go#L26-L56) +- [meilisearch.go:22-45](file://internal/search/meilisearch.go#L22-L45) + +**章节来源** +- [model_list.go:26-56](file://internal/handler/model_list.go#L26-L56) +- [meilisearch.go:22-45](file://internal/search/meilisearch.go#L22-L45) + +### Base64 响应编码 + +系统支持可选的 Base64 响应编码,用于优化传输效率: + +```mermaid +flowchart TD +Start([开始编码]) --> MarshalJSON["序列化为JSON"] +MarshalJSON --> StandardBase64["标准Base64编码"] +StandardBase64 --> CustomMap["应用自定义字符映射"] +CustomMap --> ReturnEncoded["返回编码后的字符串"] +subgraph "字符映射表" +A["ABCDEFGHIJKLMNOPQRSTUVWXYZ"] --> K["KLMPQRSTUVWXYZABC"] +B["abcdefghijklmnopqrstuvwxyz"] --> G["GHdefIJjkNOlmnp"] +C["0123456789+"] --> C1["34501289+/"] +D["="] --> D1["="] +end +``` + +**图表来源** +- [base64.go:21-42](file://pkg/encode/base64.go#L21-L42) + +**章节来源** +- [base64.go:21-52](file://pkg/encode/base64.go#L21-L52) + +## 依赖关系分析 + +### 组件依赖图 + +```mermaid +graph TB +subgraph "外部依赖" +Gin[Gin Web框架] +MySQL[MySQL驱动] +Meili[Meilisearch SDK] +Redis[Redis客户端] +Zap[Zap日志] +end +subgraph "内部模块" +Router[路由模块] +Handler[处理器模块] +Repository[仓储模块] +Search[搜索模块] +Response[响应模块] +Encode[编码模块] +Config[配置模块] +end +Router --> Handler +Handler --> Repository +Handler --> Search +Handler --> Response +Handler --> Encode +Repository --> MySQL +Search --> Meili +Handler --> Gin +Handler --> Zap +Config --> Meili +``` + +**图表来源** +- [router.go:3-12](file://internal/router/router.go#L3-L12) +- [model.go:3-12](file://internal/handler/model.go#L3-L12) +- [model_list.go:3-12](file://internal/handler/model_list.go#L3-L12) + +### 错误处理机制 + +系统采用统一的错误处理策略: + +```mermaid +flowchart TD +Request[HTTP请求] --> Handler[处理器处理] +Handler --> TryOperation{尝试操作} +TryOperation --> |成功| Success[返回成功响应] +TryOperation --> |失败| LogError[记录错误日志] +LogError --> ReturnError[返回错误响应] +subgraph "错误类型" +ValidationError[参数验证错误] +DatabaseError[数据库操作错误] +SearchError[搜索操作错误] +EncodeError[编码操作错误] +end +TryOperation --> |失败| ValidationError +TryOperation --> |失败| DatabaseError +TryOperation --> |失败| SearchError +TryOperation --> |失败| EncodeError +``` + +**图表来源** +- [response.go:23-37](file://internal/response/response.go#L23-L37) +- [logger.go:10-46](file://internal/middleware/logger.go#L10-L46) + +**章节来源** +- [response.go:23-37](file://internal/response/response.go#L23-L37) +- [logger.go:10-46](file://internal/middleware/logger.go#L10-L46) + +## 性能考虑 + +### 查询优化建议 + +1. **索引优化** + - 品牌名称和型号名称已建立唯一索引 + - 建议在高频查询字段上建立适当索引 + +2. **分页处理** + - 搜索接口支持 `count` 参数限制返回数量 + - 建议设置合理的默认值和最大值限制 + +3. **缓存策略** + - 可考虑在 Redis 中缓存热门查询结果 + - 对于频繁访问的品牌和型号数据建立缓存层 + +4. **连接池管理** + - 合理配置数据库连接池大小 + - 监控连接池使用情况避免过度连接 + +### 性能监控 + +系统内置日志中间件,自动记录请求状态、延迟和错误信息: + +- **日志级别**: 根据 HTTP 状态码自动分级 +- **关键指标**: 请求路径、方法、延迟、IP 地址 +- **错误追踪**: 自动记录异常和错误详情 + +**章节来源** +- [logger.go:10-46](file://internal/middleware/logger.go#L10-L46) +- [model.sql:34](file://sql/model.sql#L34) + +## 故障排除指南 + +### 常见问题及解决方案 + +| 问题类型 | 症状 | 可能原因 | 解决方案 | +|----------|------|----------|----------| +| 数据库连接失败 | HTTP 500 错误 | 数据库配置错误 | 检查 DATABASE_* 环境变量 | +| 搜索服务不可用 | 搜索接口超时 | Meilisearch 服务离线 | 检查 MEILISEARCH_HOST 和 API Key | +| 参数验证失败 | HTTP 400 错误 | 请求参数格式不正确 | 验证必需参数和数据类型 | +| 编码错误 | 响应内容乱码 | Base64 编码失败 | 检查 JSON 序列化和编码过程 | + +### 调试技巧 + +1. **启用详细日志**: 设置 `GIN_MODE=debug` 环境变量 +2. **检查请求ID**: 使用 `X-Request-ID` 头部关联日志 +3. **监控响应时间**: 关注日志中的延迟字段 +4. **验证数据库连接**: 确认 MySQL 服务正常运行 + +**章节来源** +- [response.go:30-37](file://internal/response/response.go#L30-L37) +- [logger.go:22-43](file://internal/middleware/logger.go#L22-L43) + +## 结论 + +型号管理接口提供了完整的耳机型号数据检索能力,具有以下特点: + +1. **双模式查询**: 支持精确的品牌过滤和全文搜索 +2. **灵活的响应格式**: 支持标准 JSON 和 Base64 编码响应 +3. **统一的错误处理**: 标准化的错误响应格式便于客户端处理 +4. **完善的日志记录**: 内置详细的请求日志和错误追踪 +5. **可扩展的架构**: 清晰的分层设计便于功能扩展和维护 + +推荐的最佳实践包括:合理设置搜索参数、利用缓存机制提升性能、监控系统指标、以及建立完善的错误处理流程。这些接口为上层应用提供了稳定可靠的型号数据服务基础。 \ No newline at end of file diff --git a/.qoder/repowiki/zh/content/API 接口文档/系统监控接口.md b/.qoder/repowiki/zh/content/API 接口文档/系统监控接口.md new file mode 100644 index 0000000..ecba782 --- /dev/null +++ b/.qoder/repowiki/zh/content/API 接口文档/系统监控接口.md @@ -0,0 +1,518 @@ +# 系统监控接口 + + +**本文档引用的文件** +- [internal/handler/health.go](file://internal/handler/health.go) +- [internal/router/router.go](file://internal/router/router.go) +- [internal/response/response.go](file://internal/response/response.go) +- [cmd/server/main.go](file://cmd/server/main.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) +- [pkg/logger/logger.go](file://pkg/logger/logger.go) +- [README.md](file://README.md) + + +## 目录 +1. [简介](#简介) +2. [项目结构](#项目结构) +3. [核心组件](#核心组件) +4. [架构概览](#架构概览) +5. [详细组件分析](#详细组件分析) +6. [依赖关系分析](#依赖关系分析) +7. [性能考虑](#性能考虑) +8. [故障排除指南](#故障排除指南) +9. [结论](#结论) +10. [附录](#附录) + +## 简介 + +本项目是一个基于 Gin 框架的 Go HTTP API 脚手架,提供了系统监控接口功能。当前版本实现了基础的健康检查接口,用于监控应用的核心服务状态,包括数据库连接、缓存服务和搜索引擎的状态。 + +健康检查接口采用统一的响应格式,返回标准的 JSON 结构,便于与各种监控系统集成。接口设计遵循 RESTful API 规范,使用 HTTP GET 方法访问指定的 URL 路径。 + +## 项目结构 + +该项目采用模块化的分层架构设计,主要分为以下几个层次: + +```mermaid +graph TB +subgraph "应用入口层" +Main[cmd/server/main.go] +end +subgraph "配置管理层" +Config[internal/config/] +ConfigGo[config.go] +DatabaseGo[database.go] +MeiliGo[meilisearch.go] +RedisGo[redis.go] +end +subgraph "数据访问层" +Database[internal/database/] +MySQL[mysql.go] +Cache[internal/cache/] +Redis[redis.go] +Search[internal/search/] +Meili[meilisearch.go] +end +subgraph "业务逻辑层" +Handler[internal/handler/] +HealthHandler[health.go] +Response[internal/response/] +ResponseGo[response.go] +end +subgraph "网络层" +Router[internal/router/] +RouterGo[router.go] +Middleware[middleware/] +end +subgraph "工具层" +Logger[pkg/logger/] +LoggerGo[logger.go] +end +Main --> Config +Main --> Database +Main --> Cache +Main --> Search +Main --> Router +Router --> Handler +Handler --> Response +Config --> Database +Config --> Cache +Config --> Search +``` + +**图表来源** +- [cmd/server/main.go:1-96](file://cmd/server/main.go#L1-L96) +- [internal/router/router.go:1-42](file://internal/router/router.go#L1-L42) +- [internal/handler/health.go:1-19](file://internal/handler/health.go#L1-L19) + +**章节来源** +- [cmd/server/main.go:1-96](file://cmd/server/main.go#L1-L96) +- [internal/router/router.go:1-42](file://internal/router/router.go#L1-L42) + +## 核心组件 + +### 健康检查处理器 + +健康检查处理器是系统监控的核心组件,负责处理健康检查请求并返回系统状态信息。 + +**章节来源** +- [internal/handler/health.go:8-19](file://internal/handler/health.go#L8-L19) + +### 统一响应格式 + +系统采用统一的 JSON 响应格式,确保所有 API 接口的一致性。 + +**章节来源** +- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37) + +### 路由配置 + +路由系统负责将 HTTP 请求映射到相应的处理器函数。 + +**章节来源** +- [internal/router/router.go:27-30](file://internal/router/router.go#L27-L30) + +## 架构概览 + +系统采用分层架构设计,各层职责明确,便于维护和扩展: + +```mermaid +graph TD +Client[客户端] --> API[HTTP API 层] +API --> Handler[处理器层] +Handler --> Service[业务逻辑层] +Service --> DataAccess[数据访问层] +subgraph "数据访问层" +MySQL[MySQL 数据库] +Redis[Redis 缓存] +Meili[Meilisearch 搜索引擎] +end +subgraph "配置管理" +Config[配置加载] +Env[环境变量] +end +subgraph "日志系统" +Zap[Zap 日志] +Logger[日志封装] +end +Handler --> MySQL +Handler --> Redis +Handler --> Meili +Config --> Env +Logger --> Zap +``` + +**图表来源** +- [cmd/server/main.go:38-62](file://cmd/server/main.go#L38-L62) +- [internal/router/router.go:14-30](file://internal/router/router.go#L14-L30) + +## 详细组件分析 + +### 健康检查接口规范 + +#### HTTP 接口定义 + +健康检查接口遵循 RESTful API 设计原则,提供简洁明了的接口规范: + +| 属性 | 描述 | +|------|------| +| HTTP 方法 | GET | +| URL 路径 | `/api/v1/health` | +| 内容类型 | `application/json` | +| 认证要求 | 无需认证 | +| 响应状态码 | 200 | + +#### 请求示例 + +```bash +# 基础请求 +curl http://localhost:8080/api/v1/health + +# 指定主机和端口 +curl http://127.0.0.1:8080/api/v1/health + +# 使用浏览器访问 +http://localhost:8080/api/v1/health +``` + +#### 响应格式 + +健康检查接口返回统一的 JSON 格式响应: + +```json +{ + "code": 0, + "message": "ok", + "data": { + "status": "up" + } +} +``` + +**章节来源** +- [internal/router/router.go:29](file://internal/router/router.go#L29) +- [internal/handler/health.go:14-18](file://internal/handler/health.go#L14-L18) +- [internal/response/response.go:15-21](file://internal/response/response.go#L15-L21) + +### 当前监控维度 + +目前系统健康检查接口仅提供基础的可用性检测,返回系统整体状态信息。完整的监控体系需要在现有基础上进行扩展。 + +#### 已支持的监控维度 + +1. **系统可用性检测** + - 应用程序运行状态 + - Web 服务器监听状态 + - 基础服务连通性 + +#### 待扩展的监控维度 + +1. **数据库连接状态** + - MySQL 连接池健康 + - 数据库查询性能指标 + - 连接数统计 + +2. **缓存服务状态** + - Redis 连接状态 + - 缓存命中率 + - 内存使用情况 + +3. **搜索引擎状态** + - Meilisearch 服务状态 + - 索引同步状态 + - 搜索性能指标 + +**章节来源** +- [internal/handler/health.go:14-18](file://internal/handler/health.go#L14-L18) +- [cmd/server/main.go:38-62](file://cmd/server/main.go#L38-L62) + +### 扩展点设计 + +为了支持更全面的监控需求,系统提供了多个扩展点: + +```mermaid +classDiagram +class HealthHandler { ++Check(c *gin.Context) ++extendDatabaseCheck() ++extendRedisCheck() ++extendSearchCheck() +} +class ExtendedHealthHandler { ++Check(c *gin.Context) ++checkDatabase() HealthStatus ++checkRedis() HealthStatus ++checkSearchEngine() HealthStatus ++aggregateResults() HealthResponse +} +class HealthStatus { ++string service ++string status ++string message ++int statusCode ++timestamp timestamp +} +class HealthResponse { ++string overallStatus ++HealthStatus[] checks ++int totalChecks ++int failedChecks ++timestamp timestamp +} +HealthHandler <|-- ExtendedHealthHandler +ExtendedHealthHandler --> HealthStatus +ExtendedHealthHandler --> HealthResponse +``` + +**图表来源** +- [internal/handler/health.go:8-19](file://internal/handler/health.go#L8-L19) + +## 依赖关系分析 + +系统依赖关系清晰,各组件职责分离: + +```mermaid +graph LR +subgraph "外部依赖" +Gin[Gin Web Framework] +MySQL[MySQL Driver] +Redis[Redis Client] +Meili[Meilisearch Client] +Zap[Zap Logger] +end +subgraph "内部模块" +Main[main.go] +Router[router.go] +Handler[health.go] +Response[response.go] +Config[config.go] +MySQLModule[mysql.go] +RedisModule[redis.go] +MeiliModule[meilisearch.go] +end +Main --> Gin +Main --> Config +Main --> MySQLModule +Main --> RedisModule +Main --> MeiliModule +Router --> Gin +Router --> Handler +Handler --> Response +Handler --> Gin +Config --> MySQL +Config --> Redis +Config --> Meili +MySQLModule --> MySQL +RedisModule --> Redis +MeiliModule --> Meili +Main --> Zap +``` + +**图表来源** +- [cmd/server/main.go:3-20](file://cmd/server/main.go#L3-L20) +- [internal/router/router.go:3-12](file://internal/router/router.go#L3-L12) + +**章节来源** +- [cmd/server/main.go:3-20](file://cmd/server/main.go#L3-L20) +- [internal/router/router.go:3-12](file://internal/router/router.go#L3-L12) + +## 性能考虑 + +### 健康检查性能特性 + +当前健康检查接口具有以下性能特点: + +1. **低延迟响应** + - 无数据库查询操作 + - 无外部服务调用 + - 直接返回预定义状态 + +2. **资源占用最小化** + - 不建立新的数据库连接 + - 不执行缓存操作 + - 不进行搜索查询 + +3. **并发处理能力** + - Gin 框架内置 goroutine 支持 + - 无阻塞 I/O 操作 + - 高并发请求处理 + +### 性能优化建议 + +对于未来的扩展版本,建议考虑以下优化措施: + +1. **连接池管理** + - 实现数据库连接池健康检查 + - 监控连接池使用率 + - 动态调整连接数 + +2. **缓存性能监控** + - 监控 Redis 连接状态 + - 统计缓存命中率 + - 分析响应时间 + +3. **异步检查机制** + - 异步执行外部服务检查 + - 实现超时控制 + - 错误重试机制 + +## 故障排除指南 + +### 常见问题诊断 + +#### 健康检查失败 + +**症状**: 健康检查返回非 200 状态码或错误响应 + +**可能原因**: +1. 应用程序未正确启动 +2. 网络连接问题 +3. 端口被占用 + +**解决步骤**: +1. 检查应用程序日志 +2. 验证端口监听状态 +3. 测试本地回环连接 + +#### 数据库连接问题 + +**症状**: 数据库相关功能无法正常使用 + +**诊断方法**: +1. 检查数据库配置参数 +2. 验证网络连通性 +3. 测试数据库凭据 + +**章节来源** +- [internal/database/mysql.go:37-43](file://internal/database/mysql.go#L37-L43) +- [cmd/server/main.go:38-48](file://cmd/server/main.go#L38-L48) + +### 日志分析 + +系统使用 Zap 日志库提供结构化日志输出: + +```mermaid +sequenceDiagram +participant Client as 客户端 +participant Server as 应用服务器 +participant Logger as 日志系统 +participant DB as 数据库 +Client->>Server : 健康检查请求 +Server->>Logger : 记录请求信息 +Server->>Server : 处理健康检查 +Server->>Logger : 记录处理结果 +Server-->>Client : 返回健康检查响应 +Logger->>DB : 记录数据库连接信息 +``` + +**图表来源** +- [cmd/server/main.go:44-48](file://cmd/server/main.go#L44-L48) +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) + +**章节来源** +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) + +### 监控集成配置 + +#### Prometheus 集成 + +```yaml +# Prometheus 配置示例 +scrape_configs: + - job_name: 'app-api' + static_configs: + - targets: ['localhost:8080'] + metrics_path: '/api/v1/health' + scrape_interval: 15s +``` + +#### Grafana 仪表板 + +```json +{ + "dashboard": { + "title": "应用健康监控", + "panels": [ + { + "type": "singlestat", + "title": "系统状态", + "targets": [ + { + "expr": "app_health_status{service='app-api'}", + "legendFormat": "健康状态" + } + ] + } + ] + } +} +``` + +## 结论 + +本系统监控接口为应用程序提供了基础的健康检查能力。当前版本专注于系统可用性检测,为后续扩展更全面的监控功能奠定了良好基础。 + +### 主要优势 + +1. **简单易用**: 接口设计简洁,易于集成和使用 +2. **标准化**: 采用统一的响应格式,便于自动化处理 +3. **可扩展**: 提供清晰的扩展点,支持功能增强 +4. **性能友好**: 低开销的设计适合高频监控场景 + +### 发展方向 + +未来可以考虑以下改进方向: +1. 实现多维度健康检查 +2. 集成更多监控系统 +3. 提供更详细的性能指标 +4. 增强告警和通知功能 + +## 附录 + +### 环境配置 + +系统支持多种环境配置,包括开发环境和生产环境: + +| 环境变量 | 默认值 | 说明 | +|----------|--------|------| +| `APP_ENV` | `development` | 运行环境 | +| `APP_HOST` | `0.0.0.0` | 监听地址 | +| `APP_PORT` | `8080` | 监听端口 | +| `GIN_MODE` | `debug` | Gin 运行模式 | + +### 数据库配置 + +| 环境变量 | 默认值 | 说明 | +|----------|--------|------| +| `DATABASE_HOST` | `localhost` | MySQL 主机 | +| `DATABASE_PORT` | `3306` | MySQL 端口 | +| `DATABASE_NAME` | `audio` | 数据库名称 | +| `DATABASE_USER` | `root` | 用户名 | +| `DATABASE_PASSWORD` | `root123` | 密码 | + +### 缓存配置 + +| 环境变量 | 默认值 | 说明 | +|----------|--------|------| +| `REDIS_HOST` | `ec2-3-69-138-29.eu-central-1.compute.amazonaws.com` | Redis 主机 | +| `REDIS_PORT` | `16279` | Redis 端口 | +| `REDIS_PASSWORD` | `eafon123!` | 密码 | +| `REDIS_DATABASE` | `1` | 数据库编号 | + +### 搜索引擎配置 + +| 环境变量 | 默认值 | 说明 | +|----------|--------|------| +| `MEILISEARCH_HOST` | `http://ec2-18-184-205-87.eu-central-1.compute.amazonaws.com:7700` | 搜索引擎主机 | +| `MEILISEARCH_API_KEY` | `young9#!UJsD219921031` | API 密钥 | +| `MEILISEARCH_INDEX` | `models` | 索引名称 | + +**章节来源** +- [README.md:39-73](file://README.md#L39-L73) +- [internal/config/config.go:18-56](file://internal/config/config.go#L18-L56) \ No newline at end of file diff --git a/.qoder/repowiki/zh/content/API 接口文档/设备管理接口.md b/.qoder/repowiki/zh/content/API 接口文档/设备管理接口.md new file mode 100644 index 0000000..72a972b --- /dev/null +++ b/.qoder/repowiki/zh/content/API 接口文档/设备管理接口.md @@ -0,0 +1,414 @@ +# 设备管理接口 + + +**本文引用的文件** +- [internal/handler/device.go](file://internal/handler/device.go) +- [internal/router/router.go](file://internal/router/router.go) +- [internal/cache/redis.go](file://internal/cache/redis.go) +- [internal/config/redis.go](file://internal/config/redis.go) +- [internal/response/response.go](file://internal/response/response.go) +- [internal/middleware/logger.go](file://internal/middleware/logger.go) +- [internal/middleware/cors.go](file://internal/middleware/cors.go) +- [cmd/server/main.go](file://cmd/server/main.go) +- [README.md](file://README.md) + + +## 目录 +1. [简介](#简介) +2. [项目结构](#项目结构) +3. [核心组件](#核心组件) +4. [架构概览](#架构概览) +5. [详细组件分析](#详细组件分析) +6. [依赖关系分析](#依赖关系分析) +7. [性能考虑](#性能考虑) +8. [故障排除指南](#故障排除指南) +9. [结论](#结论) + +## 简介 + +本文档详细描述了设备管理接口的完整规范,特别是设备信息上报接口。该接口允许设备向服务器上报其基本信息,包括 MAC 地址、型号、版本号等,并将数据存储在 Redis 缓存中。文档涵盖了 HTTP 方法、URL 模式、请求参数结构、响应格式、错误处理、数据验证规则、重复上报处理和数据一致性保证等方面。 + +## 项目结构 + +该项目采用分层架构设计,主要分为以下层次: +- **入口层**: `cmd/server/main.go` - 应用程序入口点,负责初始化配置、数据库连接、搜索引擎和缓存客户端 +- **路由层**: `internal/router/router.go` - 路由定义和中间件配置 +- **处理器层**: `internal/handler/device.go` - 设备信息上报业务逻辑 +- **缓存层**: `internal/cache/redis.go` 和 `internal/config/redis.go` - Redis 客户端配置和连接管理 +- **响应层**: `internal/response/response.go` - 统一响应格式 +- **中间件层**: `internal/middleware/logger.go` 和 `internal/middleware/cors.go` - 日志记录和跨域支持 + +```mermaid +graph TB +subgraph "应用入口" +Main[cmd/server/main.go] +end +subgraph "HTTP框架" +Gin[Gin Web框架] +Router[路由层] +Middleware[中间件层] +end +subgraph "业务处理" +DeviceHandler[设备处理器] +Response[响应层] +end +subgraph "数据存储" +Redis[Redis缓存] +Config[配置管理] +end +Main --> Gin +Gin --> Router +Router --> Middleware +Router --> DeviceHandler +DeviceHandler --> Response +DeviceHandler --> Redis +Main --> Config +Main --> Redis +``` + +**图表来源** +- [cmd/server/main.go:22-64](file://cmd/server/main.go#L22-L64) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) +- [internal/handler/device.go:14-24](file://internal/handler/device.go#L14-L24) + +**章节来源** +- [cmd/server/main.go:1-96](file://cmd/server/main.go#L1-L96) +- [internal/router/router.go:1-42](file://internal/router/router.go#L1-L42) + +## 核心组件 + +### 设备信息上报接口 + +设备信息上报接口是本项目的核心功能,负责接收设备上报的信息并进行处理。该接口实现了以下关键功能: + +- **参数验证**: 对必需参数进行验证,确保数据完整性 +- **数据格式化**: 将设备信息转换为统一的 JSON 格式 +- **缓存存储**: 使用 Redis Hash 结构存储设备信息 +- **错误处理**: 提供统一的错误响应格式 + +### 接口规范 + +#### HTTP 请求规范 + +- **方法**: GET +- **路径**: `/audio/reportDevInfo` +- **协议**: HTTP/1.1 +- **内容类型**: application/x-www-form-urlencoded + +#### 请求参数 + +| 参数名 | 类型 | 必填 | 描述 | 示例 | +|--------|------|------|------|------| +| mac | string | 是 | 设备 MAC 地址 | `00:1A:2B:3C:4D:5E` | +| model | string | 是 | 设备型号 | `ES900` | +| ver | string | 否 | 设备版本号 | `v2.1.0` | + +#### 请求头 + +| 头部名称 | 描述 | 示例 | +|----------|------|------| +| X-Forwarded-For | 客户端真实 IP 地址 | `192.168.1.100` | + +#### 响应格式 + +接口返回统一的 JSON 格式响应: + +```json +{ + "code": 200, + "msg": "操作成功" +} +``` + +#### 错误响应 + +当请求参数无效或系统发生错误时,接口返回相应的错误码: + +| 状态码 | 错误码 | 描述 | 响应示例 | +|--------|--------|------|----------| +| 200 | 400 | 参数校验失败 | `{"code": 400, "msg": "参数校验失败"}` | +| 200 | 500 | 系统错误 | `{"code": 500, "msg": "系统错误"}` | + +**章节来源** +- [internal/handler/device.go:26-84](file://internal/handler/device.go#L26-L84) +- [internal/router/router.go:32-38](file://internal/router/router.go#L32-L38) + +## 架构概览 + +设备管理接口在整个系统架构中的位置如下: + +```mermaid +sequenceDiagram +participant Client as 设备客户端 +participant API as API网关 +participant Handler as 设备处理器 +participant Redis as Redis缓存 +participant Logger as 日志系统 +Client->>API : GET /audio/reportDevInfo?mac=&model=&ver= +API->>Handler : 调用 ReportDevInfo() +Handler->>Handler : 参数验证 +alt 参数验证失败 +Handler->>Client : 返回错误响应 +else 参数验证成功 +Handler->>Handler : 格式化设备信息 +Handler->>Redis : HSET devices {mac} : {json_data} +alt Redis操作失败 +Handler->>Logger : 记录错误日志 +Handler->>Client : 返回系统错误 +else Redis操作成功 +Handler->>Client : 返回成功响应 +end +end +``` + +**图表来源** +- [internal/handler/device.go:26-84](file://internal/handler/device.go#L26-L84) +- [internal/router/router.go:32-38](file://internal/router/router.go#L32-L38) + +## 详细组件分析 + +### 设备处理器类图 + +```mermaid +classDiagram +class DeviceHandler { +-redis : redis.Client +-log : zap.Logger ++NewDeviceHandler(redis, log) DeviceHandler ++ReportDevInfo(c) void +} +class RedisClient { ++HSet(ctx, key, field, value) error ++Close() error +} +class Logger { ++Info(message, fields) void ++Error(message, error) void +} +DeviceHandler --> RedisClient : 使用 +DeviceHandler --> Logger : 记录日志 +``` + +**图表来源** +- [internal/handler/device.go:14-24](file://internal/handler/device.go#L14-L24) +- [internal/handler/device.go:70-78](file://internal/handler/device.go#L70-L78) + +### 参数验证流程 + +设备信息上报接口的参数验证流程如下: + +```mermaid +flowchart TD +Start([开始处理请求]) --> GetParams["获取查询参数
mac, model, ver"] +GetParams --> TrimParams["去除空白字符"] +TrimParams --> ValidateMac{"mac是否为空?"} +ValidateMac --> |是| ReturnError["返回参数校验失败"] +ValidateMac --> |否| ValidateModel{"model是否为空?"} +ValidateModel --> |是| ReturnError +ValidateModel --> |否| CheckVer{"ver是否为空?"} +CheckVer --> |是| SetEmptyVer["设置ver为空字符串"] +CheckVer --> |否| GetIP["获取X-Forwarded-For头部"] +SetEmptyVer --> GetIP +GetIP --> LogInfo["记录日志信息"] +LogInfo --> FormatData["格式化设备信息"] +FormatData --> MarshalJSON["序列化为JSON"] +MarshalJSON --> JSONSuccess{"JSON序列化成功?"} +JSONSuccess --> |否| ReturnSystemError["返回系统错误"] +JSONSuccess --> |是| SaveToRedis["保存到Redis"] +SaveToRedis --> RedisSuccess{"Redis操作成功?"} +RedisSuccess --> |否| ReturnSystemError +RedisSuccess --> |是| ReturnSuccess["返回成功响应"] +ReturnError --> End([结束]) +ReturnSystemError --> End +ReturnSuccess --> End +``` + +**图表来源** +- [internal/handler/device.go:26-84](file://internal/handler/device.go#L26-L84) + +### Redis 缓存交互 + +设备信息上报接口使用 Redis Hash 结构存储设备数据: + +- **键名**: `devices` +- **字段**: 设备 MAC 地址 +- **值**: JSON 格式的设备信息对象 + +存储的数据结构示例: +```json +{ + "mac_addr": "00:1A:2B:3C:4D:5E", + "model": "ES900", + "active_date": "2024-01-15", + "ip_addr": "192.168.1.100", + "ver": "v2.1.0" +} +``` + +**章节来源** +- [internal/handler/device.go:52-78](file://internal/handler/device.go#L52-L78) + +### 中间件集成 + +系统集成了多个中间件来增强功能: + +#### 日志中间件 +- 记录每个请求的状态、方法、路径、延迟时间 +- 区分不同级别的日志输出 +- 包含请求 ID 和错误信息 + +#### CORS 中间件 +- 支持跨域请求 +- 允许的方法:GET, POST, PUT, PATCH, DELETE, OPTIONS +- 允许的头部:Origin, Content-Type, Accept, Authorization, X-Request-ID + +**章节来源** +- [internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45) +- [internal/middleware/cors.go:7-20](file://internal/middleware/cors.go#L7-L20) + +## 依赖关系分析 + +### 外部依赖 + +项目的主要外部依赖包括: + +```mermaid +graph LR +subgraph "Go标准库" +StdLib[标准库] +end +subgraph "第三方库" +Gin[Gin Web框架] +Redis[Redis客户端] +Zap[Zap日志库] +end +subgraph "内部模块" +Handler[处理器层] +Router[路由层] +Cache[缓存层] +Response[响应层] +Middleware[中间件层] +end +Handler --> Gin +Handler --> Redis +Handler --> Zap +Router --> Gin +Cache --> Redis +Response --> Gin +Middleware --> Gin +Middleware --> Zap +``` + +**图表来源** +- [internal/handler/device.go:3-12](file://internal/handler/device.go#L3-L12) +- [internal/router/router.go:3-12](file://internal/router/router.go#L3-L12) + +### 内部模块依赖 + +```mermaid +graph TB +subgraph "入口模块" +Main[cmd/server/main.go] +end +subgraph "配置模块" +Config[internal/config/config.go] +RedisConfig[internal/config/redis.go] +end +subgraph "服务模块" +Router[internal/router/router.go] +Handler[internal/handler/device.go] +Cache[internal/cache/redis.go] +end +subgraph "工具模块" +Response[internal/response/response.go] +Logger[internal/middleware/logger.go] +CORS[internal/middleware/cors.go] +end +Main --> Config +Main --> Router +Main --> Cache +Router --> Handler +Handler --> Response +Router --> Logger +Router --> CORS +Cache --> RedisConfig +``` + +**图表来源** +- [cmd/server/main.go:13-18](file://cmd/server/main.go#L13-L18) +- [internal/router/router.go:6-12](file://internal/router/router.go#L6-L12) + +**章节来源** +- [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) + +## 性能考虑 + +### Redis 性能优化 + +1. **连接池管理**: 使用单个 Redis 客户端实例,避免频繁创建连接 +2. **内存使用**: 设备信息以 JSON 字符串形式存储,占用内存较小 +3. **键设计**: 使用简单的哈希结构,查询效率高 + +### 请求处理优化 + +1. **参数预处理**: 使用 `strings.TrimSpace()` 去除多余空白字符 +2. **早期返回**: 在参数验证失败时立即返回错误 +3. **上下文传递**: 使用请求上下文进行异步操作 + +### 监控建议 + +1. **Redis 监控**: 监控键数量、内存使用率、命令执行时间 +2. **应用监控**: 监控请求延迟、错误率、并发请求数 +3. **日志监控**: 设置适当的日志级别,避免过多的日志输出影响性能 + +## 故障排除指南 + +### 常见问题及解决方案 + +#### 参数验证失败 +**症状**: 返回 `{"code": 400, "msg": "参数校验失败"}` +**原因**: 缺少必需参数 `mac` 或 `model` +**解决方法**: 确保在请求中包含完整的参数 + +#### Redis 连接错误 +**症状**: 返回 `{"code": 500, "msg": "系统错误"}` +**原因**: Redis 服务器不可达或认证失败 +**解决方法**: 检查 Redis 配置和网络连接 + +#### JSON 序列化错误 +**症状**: 返回 `{"code": 500, "msg": "系统错误"}` +**原因**: 设备信息格式化过程中出现异常 +**解决方法**: 检查设备信息的数据类型和格式 + +### 调试步骤 + +1. **启用详细日志**: 检查日志中间件输出的请求信息 +2. **验证参数**: 确认请求参数的完整性和正确性 +3. **测试 Redis**: 验证 Redis 服务的可用性和权限设置 +4. **查看响应**: 分析接口返回的具体错误信息 + +**章节来源** +- [internal/handler/device.go:32-38](file://internal/handler/device.go#L32-L38) +- [internal/handler/device.go:60-68](file://internal/handler/device.go#L60-L68) +- [internal/handler/device.go:70-78](file://internal/handler/device.go#L70-L78) + +## 结论 + +设备管理接口提供了简单而高效的设备信息上报功能。通过合理的参数验证、统一的响应格式和可靠的 Redis 存储机制,该接口能够满足大多数设备管理场景的需求。 + +### 主要优势 + +1. **简洁明了**: 接口设计简单,易于理解和使用 +2. **可靠性强**: 完善的错误处理和日志记录机制 +3. **性能优秀**: 使用 Redis 缓存,响应速度快 +4. **扩展性强**: 基于 Gin 框架,便于功能扩展 + +### 改进建议 + +1. **数据验证增强**: 可以添加更严格的数据格式验证 +2. **限流机制**: 可以添加请求频率限制,防止恶意刷取 +3. **数据持久化**: 可以考虑将重要数据同时存储到数据库中 +4. **API 版本控制**: 可以添加 API 版本号,便于后续升级 + +该接口为设备管理系统提供了坚实的基础,可以根据具体需求进一步扩展和完善。 \ No newline at end of file diff --git a/.qoder/repowiki/zh/content/基础设施/基础设施.md b/.qoder/repowiki/zh/content/基础设施/基础设施.md new file mode 100644 index 0000000..2344f40 --- /dev/null +++ b/.qoder/repowiki/zh/content/基础设施/基础设施.md @@ -0,0 +1,383 @@ +# 基础设施 + + +**本文引用的文件** +- [cmd/server/main.go](file://cmd/server/main.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/cache/redis.go](file://internal/cache/redis.go) +- [internal/search/meilisearch.go](file://internal/search/meilisearch.go) +- [internal/database/mysql.go](file://internal/database/mysql.go) +- [internal/router/router.go](file://internal/router/router.go) +- [internal/middleware/logger.go](file://internal/middleware/logger.go) +- [internal/middleware/request_id.go](file://internal/middleware/request_id.go) +- [internal/handler/model_list.go](file://internal/handler/model_list.go) +- [internal/repository/model.go](file://internal/repository/model.go) +- [pkg/logger/logger.go](file://pkg/logger/logger.go) +- [README.md](file://README.md) + + +## 目录 +1. [简介](#简介) +2. [项目结构](#项目结构) +3. [核心组件](#核心组件) +4. [架构总览](#架构总览) +5. [详细组件分析](#详细组件分析) +6. [依赖分析](#依赖分析) +7. [性能考量](#性能考量) +8. [故障排除指南](#故障排除指南) +9. [结论](#结论) +10. [附录](#附录) + +## 简介 +本文件聚焦于 Luxsin 应用 API 的基础设施组件,系统性阐述缓存系统(Redis)、搜索引擎(Meilisearch)与日志系统(Zap)的配置、初始化流程、连接管理、配置项与运行时行为,并结合实际代码路径说明组件间的协作关系与数据流向。同时提供性能优化建议、监控告警与扩展性设计思路、安全注意事项以及可操作的排障指引。 + +## 项目结构 +应用采用分层与按功能模块划分的组织方式: +- 入口层:cmd/server/main.go 负责加载配置、初始化日志、数据库、搜索引擎与缓存客户端,并启动 HTTP 服务。 +- 配置层:internal/config/* 提供配置加载与校验逻辑,支持从环境变量或默认值读取数据库、Redis、Meilisearch 参数。 +- 基础设施接入:internal/database/mysql.go、internal/cache/redis.go、internal/search/meilisearch.go 封装底层驱动与 SDK 初始化。 +- Web 层:internal/router/router.go 注册路由与中间件;internal/middleware/* 提供请求 ID、日志与 CORS 中间件。 +- 业务处理:internal/handler/* 与 internal/repository/* 实现具体业务逻辑。 +- 日志封装:pkg/logger/logger.go 提供生产/开发两种日志配置。 + +```mermaid +graph TB +main["cmd/server/main.go
应用入口"] --> cfg["internal/config/config.go
配置加载"] +main --> logpkg["pkg/logger/logger.go
日志初始化"] +main --> dbinit["internal/database/mysql.go
数据库初始化"] +main --> msinit["internal/search/meilisearch.go
搜索引擎初始化"] +main --> rdinit["internal/cache/redis.go
缓存初始化"] +main --> router["internal/router/router.go
路由与中间件"] +router --> handlers["internal/handler/*
处理器"] +handlers --> repos["internal/repository/*
仓储层"] +``` + +图示来源 +- [cmd/server/main.go:22-96](file://cmd/server/main.go#L22-L96) +- [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52) +- [internal/database/mysql.go:14-46](file://internal/database/mysql.go#L14-L46) +- [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) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) + +章节来源 +- [cmd/server/main.go:22-96](file://cmd/server/main.go#L22-L96) +- [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) + +## 核心组件 +本节概述三大基础设施组件的职责、初始化与配置要点。 + +- Redis 缓存 + - 职责:提供键值存储能力,用于热点数据缓存、会话或临时状态存储。 + - 初始化:在入口处依据配置创建客户端实例,随后在处理器中注入使用。 + - 关键配置:主机、端口、密码、数据库索引。 + - 连接管理:入口处创建客户端并在进程退出时关闭;未见显式的连接池参数设置。 + +- Meilisearch 搜索引擎 + - 职责:提供全文检索能力,当前用于模型列表的搜索与结果返回。 + - 初始化:在入口处创建客户端并绑定到指定索引;处理器调用其搜索方法。 + - 关键配置:主机地址、API 密钥、索引名。 + - 数据流:HTTP 请求 -> 处理器 -> 搜索客户端 -> 搜索引擎 -> 结果解码 -> 响应。 + +- 日志系统(Zap) + - 职责:统一输出结构化日志,区分开发与生产环境的编码风格。 + - 初始化:入口处按环境创建日志实例;中间件在每次请求结束时输出请求级日志。 + - 关键配置:环境变量控制生产/开发模式,时间编码等细节可定制。 + +章节来源 +- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) +- [internal/config/redis.go:16-56](file://internal/config/redis.go#L16-L56) +- [internal/search/meilisearch.go:17-45](file://internal/search/meilisearch.go#L17-L45) +- [internal/config/meilisearch.go:14-50](file://internal/config/meilisearch.go#L14-L50) +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) +- [internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45) + +## 架构总览 +下图展示应用启动阶段如何初始化三大基础设施,并在运行期如何被业务层调用。 + +```mermaid +sequenceDiagram +participant Entrypoint as "入口(main.go)" +participant Cfg as "配置(config.go)" +participant Log as "日志(pkg/logger)" +participant DB as "数据库(mysql.go)" +participant MS as "搜索引擎(meilisearch.go)" +participant RD as "缓存(redis.go)" +participant Router as "路由(router.go)" +Entrypoint->>Cfg : 加载配置 +Entrypoint->>Log : 创建日志实例 +Entrypoint->>DB : 初始化数据库连接 +Entrypoint->>MS : 初始化搜索引擎客户端 +Entrypoint->>RD : 初始化缓存客户端 +Entrypoint->>Router : 注册路由与中间件 +Router-->>Entrypoint : 返回 HTTP 引擎 +``` + +图示来源 +- [cmd/server/main.go:22-64](file://cmd/server/main.go#L22-L64) +- [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52) +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) +- [internal/database/mysql.go:14-46](file://internal/database/mysql.go#L14-L46) +- [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) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) + +## 详细组件分析 + +### Redis 缓存组件 +- 初始化流程 + - 入口处依据配置创建 Redis 客户端实例,随后在处理器中注入使用。 + - 未见显式的连接池参数配置,如最大空闲/活动连接数、超时等。 +- 连接管理 + - 进程启动时建立连接;进程退出时关闭客户端。 + - 未见自动重连与健康检查逻辑。 +- 配置项 + - 主机、端口、密码、数据库索引。 + - 支持从环境变量覆盖默认值。 +- 使用场景 + - 当前路由中存在设备信息上报处理器,但未在现有代码中看到直接使用 Redis 的示例。建议在需要缓存的场景(如热门查询结果、限流令牌、会话状态)引入缓存策略。 + +```mermaid +flowchart TD +Start(["应用启动"]) --> LoadCfg["加载 Redis 配置"] +LoadCfg --> NewClient["创建 Redis 客户端"] +NewClient --> Inject["注入到处理器/服务"] +Inject --> UseCase{"是否命中缓存?"} +UseCase --> |是| ReturnCache["返回缓存数据"] +UseCase --> |否| ExecOp["执行业务操作"] +ExecOp --> StoreCache["写入缓存"] +StoreCache --> ReturnResult["返回结果"] +ReturnCache --> End(["完成"]) +ReturnResult --> End +``` + +图示来源 +- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) +- [internal/config/redis.go:16-56](file://internal/config/redis.go#L16-L56) +- [cmd/server/main.go:56-57](file://cmd/server/main.go#L56-L57) + +章节来源 +- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) +- [internal/config/redis.go:16-56](file://internal/config/redis.go#L16-L56) +- [cmd/server/main.go:56-57](file://cmd/server/main.go#L56-L57) + +### Meilisearch 搜索组件 +- 初始化流程 + - 入口处依据配置创建客户端并绑定到指定索引。 + - 处理器调用搜索客户端的搜索方法,限制返回字段与数量。 +- 搜索优化 + - 仅检索必要字段,减少网络与序列化开销。 + - 通过查询参数控制返回条数,避免一次性返回过多数据。 +- 错误处理 + - 对搜索失败与结果解码失败进行包装与错误返回。 +- 数据流向 + - HTTP 请求 -> 处理器 -> 搜索客户端 -> 搜索引擎 -> 解码 -> 响应。 + +```mermaid +sequenceDiagram +participant Client as "客户端" +participant Handler as "ModelListHandler" +participant Search as "search.Client" +participant Engine as "Meilisearch" +Client->>Handler : GET /audio/modelList?key=&count= +Handler->>Search : ModelList(ctx, key, count) +Search->>Engine : Index.SearchWithContext(...) +Engine-->>Search : Hits +Search->>Search : DecodeInto(map) +Search-->>Handler : 列表 +Handler-->>Client : JSON 或 Base64 响应 +``` + +图示来源 +- [internal/handler/model_list.go:26-56](file://internal/handler/model_list.go#L26-L56) +- [internal/search/meilisearch.go:22-45](file://internal/search/meilisearch.go#L22-L45) +- [internal/config/meilisearch.go:14-50](file://internal/config/meilisearch.go#L14-L50) + +章节来源 +- [internal/search/meilisearch.go:17-45](file://internal/search/meilisearch.go#L17-L45) +- [internal/handler/model_list.go:26-56](file://internal/handler/model_list.go#L26-L56) +- [internal/config/meilisearch.go:14-50](file://internal/config/meilisearch.go#L14-L50) + +### 日志系统(Zap) +- 初始化 + - 根据环境变量选择生产或开发配置,时间编码与级别编码可定制。 +- 中间件日志 + - 记录状态码、方法、路径、耗时、客户端 IP、请求 ID、错误集合等。 + - 按状态码分级输出(错误、警告、信息)。 +- 请求 ID + - 自动生成或透传请求 ID,便于跨服务链路追踪。 + +```mermaid +flowchart TD +ReqStart["请求进入"] --> GenRID["生成或透传请求ID"] +GenRID --> Next["继续中间件链"] +Next --> AfterReq["请求处理完成"] +AfterReq --> Fields["组装日志字段"] +Fields --> Level{"状态码分级"} +Level --> |>=5xx| LogErr["记录错误日志"] +Level --> |>=4xx| LogWarn["记录警告日志"] +Level --> |<4xx| LogInfo["记录信息日志"] +LogErr --> End["完成"] +LogWarn --> End +LogInfo --> End +``` + +图示来源 +- [internal/middleware/request_id.go:20-30](file://internal/middleware/request_id.go#L20-L30) +- [internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45) +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) + +章节来源 +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) +- [internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45) +- [internal/middleware/request_id.go:20-30](file://internal/middleware/request_id.go#L20-L30) + +### 数据库(MySQL)与缓存/搜索的协作 +- 数据库连接 + - 初始化时设置最大打开连接数、最大空闲连接数与连接最大生命周期,并进行超时探测。 +- 仓储层 + - 仓储层负责 SQL 查询与结果扫描,为上层处理器提供稳定的数据访问接口。 +- 协作关系 + - 处理器在需要时从数据库读取数据,随后可将热点数据写入缓存;搜索用于全文检索场景。 + +```mermaid +sequenceDiagram +participant Handler as "处理器" +participant Repo as "仓储(ModelRepository)" +participant DB as "MySQL(sql.DB)" +participant RD as "Redis" +participant MS as "Meilisearch" +Handler->>Repo : 查询数据 +Repo->>DB : 执行 SQL +DB-->>Repo : 结果集 +Repo-->>Handler : 结构化数据 +Handler->>RD : 写入/读取缓存可选 +Handler->>MS : 全文搜索可选 +``` + +图示来源 +- [internal/database/mysql.go:14-46](file://internal/database/mysql.go#L14-L46) +- [internal/repository/model.go:20-61](file://internal/repository/model.go#L20-L61) +- [internal/handler/model_list.go:26-56](file://internal/handler/model_list.go#L26-L56) + +章节来源 +- [internal/database/mysql.go:14-46](file://internal/database/mysql.go#L14-L46) +- [internal/repository/model.go:20-61](file://internal/repository/model.go#L20-L61) + +## 依赖分析 +- 组件耦合 + - 入口层集中初始化三大基础设施并向路由层注入。 + - 处理器通过依赖注入的方式使用数据库、搜索引擎与缓存客户端。 +- 外部依赖 + - Gin:Web 框架与路由。 + - go-sql-driver/mysql:MySQL 驱动。 + - redis/go-redis/v9:Redis 客户端。 + - meilisearch/meilisearch-go:Meilisearch 客户端。 + - zap:结构化日志。 +- 潜在循环依赖 + - 当前结构清晰,无明显循环导入。 + +```mermaid +graph LR +Entrypoint["入口(main.go)"] --> Gin["Gin 路由"] +Entrypoint --> Zap["Zap 日志"] +Entrypoint --> MySQL["MySQL 驱动"] +Entrypoint --> Redis["Redis 客户端"] +Entrypoint --> Meili["Meilisearch 客户端"] +Gin --> Handlers["处理器"] +Handlers --> Repos["仓储"] +``` + +图示来源 +- [cmd/server/main.go:22-64](file://cmd/server/main.go#L22-L64) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) + +章节来源 +- [cmd/server/main.go:22-64](file://cmd/server/main.go#L22-L64) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) + +## 性能考量 +- Redis + - 建议增加连接池参数配置(最大空闲/活动连接、超时),以提升高并发下的稳定性与吞吐。 + - 对热点键设置合理的过期策略,避免内存膨胀。 + - 使用 pipeline 或批量操作降低 RTT。 +- Meilisearch + - 控制返回字段与数量,减少序列化与传输开销。 + - 对高频查询建立索引与排序规则,优化查询性能。 + - 合理设置分页与缓存策略,避免重复检索。 +- 日志 + - 生产环境建议异步落盘或使用缓冲队列,避免阻塞请求。 + - 控制日志字段数量,避免过度编码。 +- 数据库 + - 已设置连接池参数与生命周期,建议结合压测调整最大连接数与空闲连接数。 + - 对复杂查询添加索引,避免全表扫描。 + +## 故障排除指南 +- Redis + - 症状:连接失败或超时。 + - 排查:确认主机、端口、密码与数据库索引配置正确;检查网络连通性与防火墙策略。 + - 建议:增加连接超时与重试机制。 +- Meilisearch + - 症状:搜索报错或返回空结果。 + - 排查:确认主机、API 密钥与索引名配置;检查索引是否存在且已同步。 + - 建议:在处理器中增加重试与降级策略。 +- 日志 + - 症状:日志缺失或格式异常。 + - 排查:确认环境变量与日志配置;检查中间件是否正确挂载。 +- 数据库 + - 症状:连接超时或连接池耗尽。 + - 排查:核对连接参数与密码;检查最大连接数与空闲连接数设置;查看慢查询日志。 + +章节来源 +- [internal/config/redis.go:51-56](file://internal/config/redis.go#L51-L56) +- [internal/config/meilisearch.go:39-50](file://internal/config/meilisearch.go#L39-L50) +- [internal/database/mysql.go:33-35](file://internal/database/mysql.go#L33-L35) + +## 结论 +本项目在基础设施层面实现了清晰的分层与职责分离:入口集中初始化、配置统一加载、日志结构化输出、数据库与搜索引擎按需接入。当前代码已具备可扩展的基础,建议在 Redis 与搜索层面补充连接池与缓存策略,在日志层面增强异步与采样能力,并完善监控与告警体系以支撑生产环境的稳定性与可观测性。 + +## 附录 + +### 配置清单与示例 +- 应用配置 + - 环境变量:APP_ENV、APP_HOST、APP_PORT。 + - 示例:参见项目自述文件中的环境变量表格与示例。 +- 数据库配置 + - 支持通过 DATABASE_HOST/PORT/NAME/USER/PASSWORD 覆盖默认值。 + - 生产环境必须提供 DATABASE_PASSWORD。 +- Redis 配置 + - 支持通过 REDIS_HOST/PORT/DATABASE/PASSWORD 覆盖默认值。 +- Meilisearch 配置 + - 支持通过 MEILISEARCH_HOST/API_KEY/INDEX 覆盖默认值。 + +章节来源 +- [README.md:39-73](file://README.md#L39-L73) +- [internal/config/database.go:17-55](file://internal/config/database.go#L17-L55) +- [internal/config/redis.go:16-48](file://internal/config/redis.go#L16-L48) +- [internal/config/meilisearch.go:14-36](file://internal/config/meilisearch.go#L14-L36) + +### 最佳实践 +- 安全 + - 生产环境敏感配置(数据库密码、搜索引擎密钥)务必通过环境变量注入。 + - Redis 与 Meilisearch 建议启用鉴权与网络隔离。 +- 可靠性 + - 为 Redis、数据库与搜索引擎增加健康检查与熔断策略。 + - 对外部依赖调用增加超时与重试。 +- 可观测性 + - 结合请求 ID 串联日志、指标与链路追踪。 + - 对关键路径埋点,关注延迟分布与错误率。 + +### 扩展性设计 +- 缓存层 + - 引入多级缓存(本地 LRU + 远端 Redis)与失效策略。 + - 对热点数据预热与定期刷新。 +- 搜索层 + - 建立索引更新流水线,保证数据一致性。 + - 引入搜索结果缓存与冷热数据分离。 +- 日志与监控 + - 增加指标采集(QPS、P95/P99、错误率)与告警阈值。 + - 使用分布式追踪定位慢调用。 \ No newline at end of file diff --git a/.qoder/repowiki/zh/content/基础设施/搜索引擎.md b/.qoder/repowiki/zh/content/基础设施/搜索引擎.md new file mode 100644 index 0000000..617dc08 --- /dev/null +++ b/.qoder/repowiki/zh/content/基础设施/搜索引擎.md @@ -0,0 +1,355 @@ +# 搜索引擎 + + +**本文引用的文件** +- [internal/search/meilisearch.go](file://internal/search/meilisearch.go) +- [internal/config/meilisearch.go](file://internal/config/meilisearch.go) +- [internal/config/config.go](file://internal/config/config.go) +- [cmd/server/main.go](file://cmd/server/main.go) +- [internal/router/router.go](file://internal/router/router.go) +- [internal/handler/model_list.go](file://internal/handler/model_list.go) +- [internal/handler/model.go](file://internal/handler/model.go) +- [internal/repository/model.go](file://internal/repository/model.go) +- [internal/model/model.go](file://internal/model/model.go) +- [internal/database/mysql.go](file://internal/database/mysql.go) +- [internal/cache/redis.go](file://internal/cache/redis.go) +- [internal/response/response.go](file://internal/response/response.go) +- [README.md](file://README.md) + + +## 目录 +1. [简介](#简介) +2. [项目结构](#项目结构) +3. [核心组件](#核心组件) +4. [架构总览](#架构总览) +5. [详细组件分析](#详细组件分析) +6. [依赖关系分析](#依赖关系分析) +7. [性能考虑](#性能考虑) +8. [故障排除指南](#故障排除指南) +9. [结论](#结论) +10. [附录](#附录) + +## 简介 +本文件面向 Luxsin 应用 API 项目中的搜索引擎集成,系统性介绍 Meilisearch 的配置与初始化、搜索客户端的创建流程、索引管理策略、全文搜索实现原理以及搜索 API 的使用示例。同时提供性能优化建议、高亮显示、自动补全与相关性排序的扩展思路,以及连接问题诊断、索引重建与性能调优的故障排除指南。内容从基础概念到高级应用,兼顾不同层次开发者需求。 + +## 项目结构 +该项目采用分层架构,围绕 Gin HTTP 框架组织模块: +- 配置层:集中加载运行环境、数据库、Meilisearch、Redis 等配置 +- 数据访问层:MySQL 数据库连接与模型扫描 +- 搜索层:Meilisearch 客户端与搜索请求封装 +- 处理层:HTTP 处理器与路由注册 +- 缓存层:Redis 客户端(用于查询缓存等) +- 工具层:统一响应体、日志、编码工具 + +```mermaid +graph TB +subgraph "应用入口" +MAIN["cmd/server/main.go"] +end +subgraph "配置层" +CFG["internal/config/config.go"] +MS_CFG["internal/config/meilisearch.go"] +end +subgraph "数据访问层" +MYSQL["internal/database/mysql.go"] +REPO["internal/repository/model.go"] +MODEL["internal/model/model.go"] +end +subgraph "搜索层" +SEARCH_CLIENT["internal/search/meilisearch.go"] +end +subgraph "处理层" +ROUTER["internal/router/router.go"] +HANDLER_MODEL["internal/handler/model.go"] +HANDLER_MODEL_LIST["internal/handler/model_list.go"] +end +subgraph "缓存层" +REDIS["internal/cache/redis.go"] +end +MAIN --> CFG +CFG --> MS_CFG +MAIN --> MYSQL +MAIN --> SEARCH_CLIENT +MAIN --> REDIS +ROUTER --> HANDLER_MODEL +ROUTER --> HANDLER_MODEL_LIST +HANDLER_MODEL --> REPO +SEARCH_CLIENT --> MS_CFG +``` + +图表来源 +- [cmd/server/main.go:22-64](file://cmd/server/main.go#L22-L64) +- [internal/config/config.go:18-51](file://internal/config/config.go#L18-L51) +- [internal/config/meilisearch.go:14-37](file://internal/config/meilisearch.go#L14-L37) +- [internal/database/mysql.go:14-46](file://internal/database/mysql.go#L14-L46) +- [internal/search/meilisearch.go:17-20](file://internal/search/meilisearch.go#L17-L20) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) +- [internal/handler/model.go:19-24](file://internal/handler/model.go#L19-L24) +- [internal/handler/model_list.go:19-24](file://internal/handler/model_list.go#L19-L24) +- [internal/repository/model.go:20-61](file://internal/repository/model.go#L20-L61) +- [internal/model/model.go:5-14](file://internal/model/model.go#L5-L14) +- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) + +章节来源 +- [README.md:5-17](file://README.md#L5-L17) +- [cmd/server/main.go:22-64](file://cmd/server/main.go#L22-L64) +- [internal/config/config.go:18-51](file://internal/config/config.go#L18-L51) + +## 核心组件 +- 配置加载与验证:集中加载运行环境、数据库、Meilisearch、Redis 配置,并进行必填项校验 +- Meilisearch 客户端:封装搜索请求,限定返回字段,支持上下文取消 +- 路由与处理器:对外提供 /audio/modelList 搜索接口,支持分页数量控制与可选 Base64 响应 +- 数据访问层:提供按品牌名或型号名的 SQL 查询能力(当前未直接使用 Meilisearch) +- 缓存层:Redis 客户端,可用于查询结果缓存 + +章节来源 +- [internal/config/meilisearch.go:8-12](file://internal/config/meilisearch.go#L8-L12) +- [internal/config/meilisearch.go:39-50](file://internal/config/meilisearch.go#L39-L50) +- [internal/search/meilisearch.go:13-15](file://internal/search/meilisearch.go#L13-L15) +- [internal/search/meilisearch.go:22-45](file://internal/search/meilisearch.go#L22-L45) +- [internal/handler/model_list.go:26-56](file://internal/handler/model_list.go#L26-L56) +- [internal/repository/model.go:20-61](file://internal/repository/model.go#L20-L61) +- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) + +## 架构总览 +应用启动时加载配置,建立数据库与 Meilisearch 连接,注册路由并启动 HTTP 服务。搜索请求通过 /audio/modelList 路由进入处理器,处理器调用搜索客户端执行全文检索,最终以 JSON 或 Base64 编码形式返回。 + +```mermaid +sequenceDiagram +participant Client as "客户端" +participant Router as "路由" +participant Handler as "ModelListHandler" +participant Search as "Meilisearch Client" +participant Engine as "HTTP 引擎" +Client->>Engine : "GET /audio/modelList?key=...&count=..." +Engine->>Router : "匹配 /audio/modelList" +Router->>Handler : "调用 ModelList()" +Handler->>Handler : "解析查询参数
解析 count 与 Base64 选项" +Handler->>Search : "ModelList(ctx, key, count)" +Search->>Search : "构造 SearchRequest
限制返回字段" +Search-->>Handler : "返回 hits 列表" +Handler-->>Client : "JSON 或 Base64 响应" +``` + +图表来源 +- [internal/router/router.go:32-38](file://internal/router/router.go#L32-L38) +- [internal/handler/model_list.go:26-56](file://internal/handler/model_list.go#L26-L56) +- [internal/search/meilisearch.go:22-45](file://internal/search/meilisearch.go#L22-L45) + +## 详细组件分析 + +### 配置加载与验证 +- 配置结构包含运行环境、监听地址、端口、数据库、Meilisearch、Redis 等字段 +- 加载逻辑按环境选择默认值,支持通过环境变量覆盖 +- Meilisearch 配置包含 Host、APIKey、Index,均需校验非空 +- 启动日志记录已配置的 Meilisearch 主机与索引 + +章节来源 +- [internal/config/config.go:9-16](file://internal/config/config.go#L9-L16) +- [internal/config/config.go:18-51](file://internal/config/config.go#L18-L51) +- [internal/config/meilisearch.go:8-12](file://internal/config/meilisearch.go#L8-L12) +- [internal/config/meilisearch.go:39-50](file://internal/config/meilisearch.go#L39-L50) +- [cmd/server/main.go:50-54](file://cmd/server/main.go#L50-L54) + +### Meilisearch 客户端 +- 客户端封装了 IndexManager,负责全文搜索 +- 初始化时传入 Host 与 APIKey,选择指定 Index +- ModelList 方法接收 key 与 count,限制返回字段,解码为 map 并返回 +- 错误处理统一包装,便于上层捕获 + +```mermaid +classDiagram +class Client { +-index : "IndexManager" ++NewClient(cfg) : "Client" ++ModelList(ctx, key, count) : "[]map[string]any, error" +} +class MeilisearchConfig { ++Host : "string" ++APIKey : "string" ++Index : "string" ++validate() : "error" +} +Client --> MeilisearchConfig : "依赖" +``` + +图表来源 +- [internal/search/meilisearch.go:13-20](file://internal/search/meilisearch.go#L13-L20) +- [internal/search/meilisearch.go:22-45](file://internal/search/meilisearch.go#L22-L45) +- [internal/config/meilisearch.go:8-12](file://internal/config/meilisearch.go#L8-L12) +- [internal/config/meilisearch.go:39-50](file://internal/config/meilisearch.go#L39-L50) + +章节来源 +- [internal/search/meilisearch.go:17-20](file://internal/search/meilisearch.go#L17-L20) +- [internal/search/meilisearch.go:22-45](file://internal/search/meilisearch.go#L22-L45) + +### 路由与处理器 +- 路由在 /audio 下注册 /modelList 接口 +- ModelListHandler 解析查询参数 key、count,并支持 Base64 响应 +- 调用搜索客户端执行搜索,错误时返回统一错误响应 + +```mermaid +sequenceDiagram +participant C as "客户端" +participant R as "路由" +participant H as "ModelListHandler" +participant S as "Meilisearch Client" +C->>R : "GET /audio/modelList?key=...&count=..." +R->>H : "ModelList()" +H->>H : "解析 key/count/base64" +H->>S : "ModelList(ctx, key, count)" +S-->>H : "hits" +H-->>C : "JSON 或 Base64" +``` + +图表来源 +- [internal/router/router.go:32-38](file://internal/router/router.go#L32-L38) +- [internal/handler/model_list.go:26-56](file://internal/handler/model_list.go#L26-L56) +- [internal/search/meilisearch.go:22-45](file://internal/search/meilisearch.go#L22-L45) + +章节来源 +- [internal/router/router.go:32-38](file://internal/router/router.go#L32-L38) +- [internal/handler/model_list.go:26-56](file://internal/handler/model_list.go#L26-L56) + +### 数据访问层(SQL 查询) +- ModelRepository 支持按品牌名精确匹配与按型号名模糊匹配 +- 默认不使用 Meilisearch,直接走数据库查询 +- 可作为搜索降级路径或补充场景 + +```mermaid +flowchart TD +Start(["进入 List"]) --> Trim["去除前后空格"] +Trim --> Switch{"条件分支"} +Switch --> |brandName 非空| BrandQuery["按品牌名精确查询
按名称升序"] +Switch --> |modelName 非空| LikeQuery["按型号名模糊查询
按名称升序"] +Switch --> |否则| Empty["返回空数组"] +BrandQuery --> Exec["执行查询"] +LikeQuery --> Exec +Exec --> Scan["逐行扫描并组装模型"] +Scan --> End(["返回结果"]) +Empty --> End +``` + +图表来源 +- [internal/repository/model.go:20-61](file://internal/repository/model.go#L20-L61) +- [internal/model/model.go:5-14](file://internal/model/model.go#L5-L14) + +章节来源 +- [internal/repository/model.go:20-61](file://internal/repository/model.go#L20-L61) +- [internal/model/model.go:5-14](file://internal/model/model.go#L5-L14) + +### 数据库连接与模型扫描 +- 使用 go-sql-driver/mysql 建立连接,设置字符集、时区与连接池参数 +- PingContext 超时检测连接可用性 +- 扫描函数将 NullString 转换为指针类型,避免空值污染 + +章节来源 +- [internal/database/mysql.go:14-46](file://internal/database/mysql.go#L14-L46) +- [internal/repository/model.go:63-86](file://internal/repository/model.go#L63-L86) + +### 缓存层(Redis) +- 提供 NewClient 工厂方法,按配置创建 Redis 客户端 +- 可用于搜索结果缓存、热门关键词缓存等 + +章节来源 +- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) + +## 依赖关系分析 +- 入口 main 依赖配置加载、数据库、Meilisearch、Redis 与路由 +- 路由依赖处理器,处理器依赖搜索客户端与数据库 +- 搜索客户端依赖配置模块与 Meilisearch SDK +- 数据库层依赖 MySQL 驱动与配置 + +```mermaid +graph LR +MAIN["cmd/server/main.go"] --> CFG["internal/config/config.go"] +MAIN --> MYSQL["internal/database/mysql.go"] +MAIN --> SEARCH["internal/search/meilisearch.go"] +MAIN --> REDIS["internal/cache/redis.go"] +ROUTER["internal/router/router.go"] --> HANDLER1["internal/handler/model.go"] +ROUTER --> HANDLER2["internal/handler/model_list.go"] +HANDLER2 --> SEARCH +HANDLER1 --> REPO["internal/repository/model.go"] +REPO --> MODEL["internal/model/model.go"] +``` + +图表来源 +- [cmd/server/main.go:22-64](file://cmd/server/main.go#L22-L64) +- [internal/config/config.go:18-51](file://internal/config/config.go#L18-L51) +- [internal/database/mysql.go:14-46](file://internal/database/mysql.go#L14-L46) +- [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) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) +- [internal/handler/model.go:19-24](file://internal/handler/model.go#L19-L24) +- [internal/handler/model_list.go:19-24](file://internal/handler/model_list.go#L19-L24) +- [internal/repository/model.go:20-61](file://internal/repository/model.go#L20-L61) +- [internal/model/model.go:5-14](file://internal/model/model.go#L5-L14) + +## 性能考虑 +- 索引预处理 + - 在导入数据前,确保字段映射与排序规则已配置,减少运行时开销 + - 对高频查询字段(如品牌名、型号名)建立合适字段权重 +- 查询缓存 + - 使用 Redis 缓存热点搜索结果,设置合理过期时间 + - 对于稳定不变的数据(如品牌列表),可缓存静态结果 +- 分页策略 + - 控制 count 参数上限,避免一次性返回过多数据 + - 结合游标分页或基于主键的分页,提升大结果集性能 +- 连接池与超时 + - 数据库连接池参数已设置,搜索客户端使用短连接或复用连接视场景而定 + - 设置合理的读写超时与上下文取消,防止阻塞 +- 字段裁剪 + - 仅返回必要字段,降低网络传输与序列化成本 +- 相关性与排序 + - 使用 Meilisearch 的排序与过滤能力,避免后端二次排序 +- 日志与监控 + - 记录搜索耗时、命中率与错误统计,便于定位性能瓶颈 + +## 故障排除指南 +- 连接问题诊断 + - 检查 MEILISEARCH_HOST、MEILISEARCH_API_KEY、MEILISEARCH_INDEX 是否正确设置 + - 确认 Meilisearch 服务可达且端口开放 + - 查看启动日志中 Meilisearch 配置是否正确加载 +- 索引重建 + - 如需重建索引,先清空旧索引,再批量导入数据,最后更新字段映射与排序规则 + - 导入完成后进行回归测试,确保搜索结果符合预期 +- 性能调优 + - 逐步增加 count 上限,观察延迟变化,找到平衡点 + - 对热点查询启用缓存,减少重复请求 + - 优化数据库查询(如品牌/型号过滤)作为降级方案 +- 错误处理 + - 搜索失败时返回统一错误响应,便于前端提示 + - 对 decode 失败、上下文取消等异常进行分类处理 + +章节来源 +- [internal/config/meilisearch.go:39-50](file://internal/config/meilisearch.go#L39-L50) +- [cmd/server/main.go:50-54](file://cmd/server/main.go#L50-L54) +- [internal/handler/model_list.go:37-42](file://internal/handler/model_list.go#L37-L42) +- [internal/response/response.go:34-36](file://internal/response/response.go#L34-L36) + +## 结论 +本项目已实现 Meilisearch 的基础集成:配置加载、客户端初始化与搜索接口。当前搜索接口主要返回指定字段的命中结果,未涉及高亮、自动补全与相关性排序的高级特性。建议后续在以下方面增强: +- 高亮显示:利用 Meilisearch 的高亮能力,返回匹配片段 +- 自动补全:结合前缀匹配与热门词,提供输入建议 +- 相关性排序:配置字段权重与排序规则,提升搜索体验 +- 索引策略:按业务维度拆分索引,优化写入与查询性能 +- 缓存策略:引入 Redis 缓存,显著降低重复查询延迟 + +## 附录 + +### 搜索 API 使用示例 +- 设备型号搜索 + - 请求:GET /audio/modelList?key=型号关键词&count=100 + - 响应:返回命中的设备列表(仅包含指定字段) +- 品牌过滤 + - 当前未直接使用 Meilisearch 实现品牌过滤,可通过数据库查询替代 + - 请求:GET /audio/getModel?brandName=品牌名 +- 结果排序 + - 当前未显式设置排序,可结合 Meilisearch 的排序规则实现 +- Base64 响应 + - 可通过查询参数 base64=true 获取 Base64 编码的 JSON + +章节来源 +- [internal/handler/model_list.go:26-56](file://internal/handler/model_list.go#L26-L56) +- [internal/handler/model.go:26-50](file://internal/handler/model.go#L26-L50) +- [internal/search/meilisearch.go:22-45](file://internal/search/meilisearch.go#L22-L45) \ No newline at end of file diff --git a/.qoder/repowiki/zh/content/基础设施/日志系统.md b/.qoder/repowiki/zh/content/基础设施/日志系统.md new file mode 100644 index 0000000..80542dd --- /dev/null +++ b/.qoder/repowiki/zh/content/基础设施/日志系统.md @@ -0,0 +1,382 @@ +# 日志系统 + + +**本文引用的文件** +- [pkg/logger/logger.go](file://pkg/logger/logger.go) +- [internal/middleware/logger.go](file://internal/middleware/logger.go) +- [internal/middleware/request_id.go](file://internal/middleware/request_id.go) +- [cmd/server/main.go](file://cmd/server/main.go) +- [internal/router/router.go](file://internal/router/router.go) +- [internal/handler/device.go](file://internal/handler/device.go) +- [internal/handler/model.go](file://internal/handler/model.go) +- [internal/handler/brand.go](file://internal/handler/brand.go) +- [internal/config/config.go](file://internal/config/config.go) +- [go.mod](file://go.mod) + + +## 目录 +1. [简介](#简介) +2. [项目结构](#项目结构) +3. [核心组件](#核心组件) +4. [架构总览](#架构总览) +5. [详细组件分析](#详细组件分析) +6. [依赖关系分析](#依赖关系分析) +7. [性能与轮转配置](#性能与轮转配置) +8. [最佳实践与规范](#最佳实践与规范) +9. [故障排查与调试](#故障排查与调试) +10. [结论](#结论) + +## 简介 +本文件面向 Luxsin 应用 API 项目的日志系统,围绕基于 Zap 的结构化日志进行系统性梳理。内容覆盖日志客户端初始化流程、配置选项与输出格式、日志级别使用场景、在中间件与处理器中的使用方式、以及与配置加载、请求链路追踪的集成。同时给出日志聚合、监控告警与故障排查的实践建议,帮助开发者建立完善且可维护的日志体系。 + +## 项目结构 +日志系统在本项目中采用分层设计: +- 初始化层:在服务启动时根据环境变量创建全局日志器。 +- 中间件层:统一记录请求生命周期的关键指标,按状态码选择日志级别。 +- 处理器层:在业务逻辑中记录关键事件与错误,携带上下文信息。 +- 配置层:通过环境变量控制运行模式与端口等,间接影响日志输出风格。 + +```mermaid +graph TB +subgraph "启动阶段" +MAIN["cmd/server/main.go
读取配置/创建日志器"] +CFG["internal/config/config.go
环境与端口配置"] +end +subgraph "中间件层" +REQID["internal/middleware/request_id.go
生成/透传请求ID"] +LOGMW["internal/middleware/logger.go
请求日志中间件"] +ROUTER["internal/router/router.go
注册中间件与路由"] +end +subgraph "处理器层" +DEV["internal/handler/device.go
设备上报日志"] +MODEL["internal/handler/model.go
模型查询日志"] +BRAND["internal/handler/brand.go
品牌查询日志"] +end +subgraph "日志库封装" +ZAP["pkg/logger/logger.go
Zap配置与构建"] +end +CFG --> MAIN +MAIN --> ZAP +MAIN --> ROUTER +ROUTER --> REQID +ROUTER --> LOGMW +LOGMW --> DEV +LOGMW --> MODEL +LOGMW --> BRAND +``` + +图表来源 +- [cmd/server/main.go:22-95](file://cmd/server/main.go#L22-L95) +- [internal/config/config.go:18-56](file://internal/config/config.go#L18-L56) +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) +- [internal/middleware/request_id.go:20-30](file://internal/middleware/request_id.go#L20-L30) +- [internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45) +- [internal/handler/device.go:26-84](file://internal/handler/device.go#L26-L84) +- [internal/handler/model.go:26-50](file://internal/handler/model.go#L26-L50) +- [internal/handler/brand.go:26-49](file://internal/handler/brand.go#L26-L49) + +章节来源 +- [cmd/server/main.go:22-95](file://cmd/server/main.go#L22-L95) +- [internal/config/config.go:18-56](file://internal/config/config.go#L18-L56) +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) +- [internal/middleware/request_id.go:20-30](file://internal/middleware/request_id.go#L20-L30) +- [internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45) +- [internal/handler/device.go:26-84](file://internal/handler/device.go#L26-L84) +- [internal/handler/model.go:26-50](file://internal/handler/model.go#L26-L50) +- [internal/handler/brand.go:26-49](file://internal/handler/brand.go#L26-L49) + +## 核心组件 +- 日志器工厂(Zap 封装):根据环境变量选择生产或开发配置,定制时间键与编码器,返回全局日志器实例。 +- 请求日志中间件:在请求完成后收集状态码、方法、路径、延迟、IP、请求ID、查询串与错误信息,按状态码映射到不同日志级别。 +- 请求ID中间件:生成唯一请求ID并在请求头中透传,便于跨服务/模块关联日志。 +- 启动入口:加载配置、设置 Gin 模式、创建日志器、连接数据库/缓存/搜索引擎、启动 HTTP 服务器并优雅关闭。 +- 处理器层:在业务关键点记录 Info/Warn/Error 等日志,携带上下文字段,如远程IP、时间戳、错误详情等。 + +章节来源 +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) +- [internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45) +- [internal/middleware/request_id.go:20-30](file://internal/middleware/request_id.go#L20-L30) +- [cmd/server/main.go:22-95](file://cmd/server/main.go#L22-L95) +- [internal/handler/device.go:47-78](file://internal/handler/device.go#L47-L78) +- [internal/handler/model.go:33-43](file://internal/handler/model.go#L33-L43) +- [internal/handler/brand.go:32-41](file://internal/handler/brand.go#L32-L41) + +## 架构总览 +下图展示了从启动到请求处理的完整日志链路,强调日志器的创建、中间件的注入与处理器中的使用。 + +```mermaid +sequenceDiagram +participant Client as "客户端" +participant Server as "HTTP服务器" +participant Router as "Gin路由" +participant ReqID as "请求ID中间件" +participant LogMW as "请求日志中间件" +participant Handler as "业务处理器" +participant Logger as "Zap日志器" +Client->>Server : "发起HTTP请求" +Server->>Router : "进入路由" +Router->>ReqID : "生成/透传请求ID" +ReqID->>LogMW : "继续处理" +LogMW->>Handler : "调用业务处理器" +Handler->>Logger : "记录Info/Warn/Error" +Handler-->>Client : "返回响应" +LogMW->>Logger : "记录请求日志(按状态码)" +LogMW-->>Router : "完成" +``` + +图表来源 +- [cmd/server/main.go:64-64](file://cmd/server/main.go#L64-L64) +- [internal/router/router.go:17-18](file://internal/router/router.go#L17-L18) +- [internal/middleware/request_id.go:20-30](file://internal/middleware/request_id.go#L20-L30) +- [internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45) +- [internal/handler/device.go:47-78](file://internal/handler/device.go#L47-L78) + +## 详细组件分析 + +### 日志器工厂(Zap 封装) +- 功能:根据环境变量选择生产或开发配置,定制时间键与编码器,构建并返回全局日志器。 +- 关键点: + - 生产环境:使用生产配置,时间键为“time”,时间编码为 ISO8601。 + - 开发环境:使用开发配置,级别编码为带颜色的大写形式。 +- 使用方式:在启动入口调用工厂创建日志器,并在退出前同步缓冲区。 + +```mermaid +flowchart TD +Start(["函数入口"]) --> CheckEnv{"环境是否为生产?"} +CheckEnv --> |是| ProdCfg["使用生产配置
设置时间键与编码器"] +CheckEnv --> |否| DevCfg["使用开发配置
设置级别编码为彩色"] +ProdCfg --> Build["构建日志器"] +DevCfg --> Build +Build --> Return(["返回日志器"]) +``` + +图表来源 +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) + +章节来源 +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) + +### 请求日志中间件 +- 功能:在请求完成后记录关键指标,按状态码选择日志级别。 +- 字段规范: + - 状态码、方法、路径、延迟、客户端IP、请求ID。 + - 可选字段:查询串、错误信息。 +- 级别映射: + - 5xx:Error + - 4xx:Warn + - 其他:Info + +```mermaid +flowchart TD +Enter(["进入中间件"]) --> Collect["收集开始时间/路径/查询串"] +Collect --> Next["调用后续处理器"] +Next --> After["计算延迟/获取状态码/提取请求ID"] +After --> HasErrors{"是否存在错误?"} +HasErrors --> |是| AddErrors["附加错误字段"] +HasErrors --> |否| SkipErrors["跳过错误字段"] +AddErrors --> Level["按状态码选择级别"] +SkipErrors --> Level +Level --> Log["记录请求日志"] +Log --> Exit(["返回响应"]) +``` + +图表来源 +- [internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45) + +章节来源 +- [internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45) + +### 请求ID中间件 +- 功能:生成随机十六进制字符串作为请求ID,若请求头未提供则自动生成并透传。 +- 作用:贯穿请求链路,便于聚合与关联日志。 + +章节来源 +- [internal/middleware/request_id.go:20-30](file://internal/middleware/request_id.go#L20-L30) + +### 启动入口与日志集成 +- 加载配置:从环境变量读取运行环境、主机、端口等。 +- 设置模式:生产环境设置 Gin 为 Release 模式。 +- 创建日志器:根据配置环境调用日志工厂。 +- 连接外部组件:数据库、搜索引擎、缓存;连接成功后记录 Info 日志。 +- 启动服务器:记录启动日志,捕获异常并记录 Fatal 日志。 +- 优雅关闭:记录关闭与停止日志。 + +```mermaid +sequenceDiagram +participant Main as "main.go" +participant Cfg as "config.Load()" +participant Log as "logger.New()" +participant DB as "database.Open()" +participant Redis as "cache.NewClient()" +participant MS as "search.NewClient()" +participant Engine as "router.New()" +Main->>Cfg : "加载配置" +Cfg-->>Main : "返回配置" +Main->>Log : "创建日志器" +Log-->>Main : "返回日志器" +Main->>DB : "连接数据库" +DB-->>Main : "连接结果" +Main->>MS : "配置搜索引擎" +MS-->>Main : "配置结果" +Main->>Redis : "连接缓存" +Redis-->>Main : "连接结果" +Main->>Engine : "创建路由引擎" +Engine-->>Main : "返回引擎" +``` + +图表来源 +- [cmd/server/main.go:22-95](file://cmd/server/main.go#L22-L95) +- [internal/config/config.go:18-56](file://internal/config/config.go#L18-L56) +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) + +章节来源 +- [cmd/server/main.go:22-95](file://cmd/server/main.go#L22-L95) +- [internal/config/config.go:18-56](file://internal/config/config.go#L18-L56) + +### 处理器中的日志使用 +- 设备上报处理器:记录上报事件与远程IP、时间等字段;对序列化与缓存写入失败记录 Error 日志。 +- 模型/品牌处理器:对数据库查询失败与响应编码失败记录 Error 日志,并返回统一错误响应。 + +```mermaid +classDiagram +class DeviceHandler { +-redis : "redis.Client" +-log : "zap.Logger" ++ReportDevInfo(c) +} +class ModelHandler { +-repo : "ModelRepository" +-log : "zap.Logger" ++GetModel(c) +} +class BrandHandler { +-repo : "BrandRepository" +-log : "zap.Logger" ++GetBrand(c) +} +class ZapLogger { ++Info(msg, fields) ++Warn(msg, fields) ++Error(msg, fields) ++Fatal(msg, fields) +} +DeviceHandler --> ZapLogger : "记录Info/Error" +ModelHandler --> ZapLogger : "记录Error" +BrandHandler --> ZapLogger : "记录Error" +``` + +图表来源 +- [internal/handler/device.go:14-24](file://internal/handler/device.go#L14-L24) +- [internal/handler/model.go:14-24](file://internal/handler/model.go#L14-L24) +- [internal/handler/brand.go:14-24](file://internal/handler/brand.go#L14-L24) +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) + +章节来源 +- [internal/handler/device.go:47-78](file://internal/handler/device.go#L47-L78) +- [internal/handler/model.go:33-43](file://internal/handler/model.go#L33-L43) +- [internal/handler/brand.go:32-41](file://internal/handler/brand.go#L32-L41) + +## 依赖关系分析 +- 组件耦合: + - 路由器依赖日志器、数据库、搜索引擎、缓存客户端;通过构造函数注入,降低耦合度。 + - 中间件依赖日志器;请求ID中间件独立于日志器。 + - 处理器依赖日志器与数据源;通过构造函数注入。 +- 外部依赖: + - Gin:Web 框架,提供中间件与路由能力。 + - Zap:结构化日志库,提供高性能日志记录。 + - Redis、MySQL、Meilisearch:外部服务,通过连接客户端访问。 + +```mermaid +graph LR +MAIN["cmd/server/main.go"] --> ROUTER["internal/router/router.go"] +MAIN --> ZAP["pkg/logger/logger.go"] +ROUTER --> REQID["internal/middleware/request_id.go"] +ROUTER --> LOGMW["internal/middleware/logger.go"] +ROUTER --> DEV["internal/handler/device.go"] +ROUTER --> MODEL["internal/handler/model.go"] +ROUTER --> BRAND["internal/handler/brand.go"] +DEV --> ZAP +MODEL --> ZAP +BRAND --> ZAP +``` + +图表来源 +- [cmd/server/main.go:64-64](file://cmd/server/main.go#L64-L64) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) +- [internal/middleware/request_id.go:20-30](file://internal/middleware/request_id.go#L20-L30) +- [internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45) +- [internal/handler/device.go:14-24](file://internal/handler/device.go#L14-L24) +- [internal/handler/model.go:14-24](file://internal/handler/model.go#L14-L24) +- [internal/handler/brand.go:14-24](file://internal/handler/brand.go#L14-L24) +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) + +章节来源 +- [go.mod:5-11](file://go.mod#L5-L11) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) + +## 性能与轮转配置 +- 当前实现: + - 日志器在启动时创建并使用默认输出(标准输出),未显式配置文件输出与轮转策略。 + - 在退出时执行同步,确保缓冲区落盘。 +- 建议扩展(概念性指导): + - 文件输出与轮转:通过自定义 Zap 编码器与输出目标,结合文件轮转策略(大小/时间/数量限制)提升可维护性。 + - 异步写入:启用异步日志器,减少阻塞;结合队列长度与丢弃策略平衡性能与可靠性。 + - 采样与速率限制:对高频日志进行采样,避免在峰值流量下产生过多 I/O。 + - 结构化字段规范化:统一字段命名与类型,便于下游检索与聚合。 +- 本节为通用建议,不直接对应具体代码文件。 + +## 最佳实践与规范 +- 字段命名规范 + - 使用语义明确的键名,如状态码、方法、路径、延迟、IP、请求ID、错误等。 + - 时间字段建议统一为 ISO8601 或 Unix 时间戳,便于排序与解析。 +- 上下文传递 + - 通过请求ID贯穿请求链路,便于跨模块聚合日志。 + - 在处理器中记录关键业务事件与错误详情,包含上下文字段。 +- 日志级别使用 + - Info:常规业务事件、连接成功、启动/关闭等。 + - Warn:客户端错误(4xx)、潜在问题但不影响功能。 + - Error:服务端错误(5xx)、数据库/缓存/序列化失败等。 + - Fatal:致命错误导致进程退出,通常用于不可恢复的初始化失败。 +- 性能考虑 + - 避免在热路径上进行昂贵的字符串拼接或格式化。 + - 对高频日志进行采样或降级。 + - 控制日志字段数量,仅记录必要信息。 +- 实践示例 + - 启动阶段记录连接信息与环境配置。 + - 中间件按状态码选择级别,统一记录请求指标。 + - 处理器在错误分支记录 Error 日志并返回统一错误响应。 + +章节来源 +- [internal/middleware/logger.go:22-43](file://internal/middleware/logger.go#L22-L43) +- [internal/handler/device.go:47-78](file://internal/handler/device.go#L47-L78) +- [internal/handler/model.go:33-43](file://internal/handler/model.go#L33-L43) +- [internal/handler/brand.go:32-41](file://internal/handler/brand.go#L32-L41) +- [cmd/server/main.go:44-48](file://cmd/server/main.go#L44-L48) +- [cmd/server/main.go:75-78](file://cmd/server/main.go#L75-L78) + +## 故障排查与调试 +- 常见问题 + - 数据库连接失败:启动阶段记录 Fatal 日志,检查配置与网络连通性。 + - Redis 连接失败:记录 Error 日志,确认地址、端口与认证配置。 + - 请求日志缺失:确认中间件已注册且顺序正确。 + - 请求ID未透传:检查请求头是否被上游代理或网关修改。 +- 调试技巧 + - 切换到开发环境以获得彩色输出与更详细的级别编码,便于本地调试。 + - 在关键业务点增加 Info 日志,记录输入参数与关键中间结果。 + - 使用统一错误响应包装,确保错误信息一致且可检索。 +- 建议的排查步骤 + - 查看启动日志,确认各组件连接成功。 + - 在中间件层观察请求日志,定位异常状态码与耗时。 + - 在处理器层查看 Error 日志,结合请求ID定位具体请求。 + - 检查下游服务(数据库/缓存/搜索引擎)的可用性与配置。 + +章节来源 +- [cmd/server/main.go:40-41](file://cmd/server/main.go#L40-L41) +- [cmd/server/main.go:56-62](file://cmd/server/main.go#L56-L62) +- [internal/middleware/logger.go:37-43](file://internal/middleware/logger.go#L37-L43) +- [internal/handler/device.go:62-78](file://internal/handler/device.go#L62-L78) + +## 结论 +本项目采用简洁而高效的日志体系:通过工厂封装统一创建日志器,中间件集中记录请求指标,处理器在关键节点记录业务与错误日志。结合请求ID与结构化字段,能够有效支撑日志聚合、监控告警与故障排查。建议在生产环境中进一步引入文件输出与轮转策略、异步写入与采样机制,以满足更高的可靠性与性能要求。 \ No newline at end of file diff --git a/.qoder/repowiki/zh/content/基础设施/缓存系统.md b/.qoder/repowiki/zh/content/基础设施/缓存系统.md new file mode 100644 index 0000000..cf974b0 --- /dev/null +++ b/.qoder/repowiki/zh/content/基础设施/缓存系统.md @@ -0,0 +1,326 @@ +# 缓存系统 + + +**本文引用的文件** +- [cmd/server/main.go](file://cmd/server/main.go) +- [internal/cache/redis.go](file://internal/cache/redis.go) +- [internal/config/config.go](file://internal/config/config.go) +- [internal/config/redis.go](file://internal/config/redis.go) +- [internal/router/router.go](file://internal/router/router.go) +- [internal/handler/device.go](file://internal/handler/device.go) +- [go.mod](file://go.mod) + + +## 目录 +1. [简介](#简介) +2. [项目结构](#项目结构) +3. [核心组件](#核心组件) +4. [架构总览](#架构总览) +5. [详细组件分析](#详细组件分析) +6. [依赖关系分析](#依赖关系分析) +7. [性能考虑](#性能考虑) +8. [故障排查指南](#故障排查指南) +9. [结论](#结论) +10. [附录](#附录) + +## 简介 +本文件面向 Luxsin 应用 API 的缓存系统,聚焦 Redis 缓存客户端的初始化流程、连接配置与使用方式。当前代码库实现了最小可用的 Redis 客户端封装与配置加载,并在设备上报接口中演示了哈希写入的典型用法。本文将从系统架构、组件职责、数据流、错误处理到性能优化与故障恢复进行系统化梳理,帮助初学者快速上手,同时为高级用户提供深入的技术细节与最佳实践参考。 + +## 项目结构 +与缓存系统直接相关的模块分布如下: +- 配置层:负责加载环境变量与默认值,生成 RedisConfig 并进行基础校验 +- 缓存层:基于 RedisConfig 构造 Redis 客户端实例 +- 应用入口:在启动时加载配置、初始化缓存客户端并注入路由 +- 路由与处理器:将 Redis 客户端注入到需要缓存能力的处理器中 +- 外部依赖:通过 go.mod 指定 Redis 客户端版本 + +```mermaid +graph TB +subgraph "应用入口" +MAIN["cmd/server/main.go
启动与生命周期管理"] +end +subgraph "配置层" +CFG["internal/config/config.go
统一配置加载"] +REDIS_CFG["internal/config/redis.go
Redis 配置与校验"] +end +subgraph "缓存层" +CACHE["internal/cache/redis.go
NewClient 构造 Redis 客户端"] +end +subgraph "路由与处理器" +ROUTER["internal/router/router.go
路由注册与依赖注入"] +DEVICE["internal/handler/device.go
设备上报使用 Redis"] +end +subgraph "外部依赖" +MOD["go.mod
Redis 客户端版本"] +end +MAIN --> CFG +CFG --> REDIS_CFG +MAIN --> CACHE +CACHE --> DEVICE +ROUTER --> DEVICE +MOD --> CACHE +``` + +图表来源 +- [cmd/server/main.go:22-62](file://cmd/server/main.go#L22-L62) +- [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52) +- [internal/config/redis.go:16-56](file://internal/config/redis.go#L16-L56) +- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) +- [internal/router/router.go:14-25](file://internal/router/router.go#L14-L25) +- [internal/handler/device.go:26-78](file://internal/handler/device.go#L26-L78) +- [go.mod:9](file://go.mod#L9) + +章节来源 +- [cmd/server/main.go:22-62](file://cmd/server/main.go#L22-L62) +- [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52) +- [internal/config/redis.go:16-56](file://internal/config/redis.go#L16-L56) +- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) +- [internal/router/router.go:14-25](file://internal/router/router.go#L14-L25) +- [internal/handler/device.go:26-78](file://internal/handler/device.go#L26-L78) +- [go.mod:9](file://go.mod#L9) + +## 核心组件 +- RedisConfig:定义 Redis 连接所需的主机、端口、密码与数据库编号,并提供校验逻辑 +- NewClient:根据 RedisConfig 创建 Redis 客户端实例,设置 Addr、Password、DB 等选项 +- 配置加载:Load 统一加载应用配置,其中包含 Redis 配置加载与校验 +- 启动流程:main 在启动阶段创建 Redis 客户端并注入路由,服务关闭时释放连接 +- 使用示例:设备上报接口在请求上下文中向 Redis 写入哈希字段 + +章节来源 +- [internal/config/redis.go:9-14](file://internal/config/redis.go#L9-L14) +- [internal/config/redis.go:51-56](file://internal/config/redis.go#L51-L56) +- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) +- [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52) +- [cmd/server/main.go:22-62](file://cmd/server/main.go#L22-L62) +- [internal/handler/device.go:70-78](file://internal/handler/device.go#L70-L78) + +## 架构总览 +下图展示了从应用启动到 Redis 客户端被注入处理器的整体流程: + +```mermaid +sequenceDiagram +participant Boot as "启动器(main)" +participant Cfg as "配置加载(Config.Load)" +participant RdsCfg as "Redis 配置(loadRedis)" +participant Cache as "NewClient" +participant Router as "路由(router)" +participant Dev as "设备处理器(DeviceHandler)" +Boot->>Cfg : 加载配置 +Cfg->>RdsCfg : 读取 Redis 配置并校验 +RdsCfg-->>Cfg : 返回 RedisConfig +Cfg-->>Boot : 返回完整 Config +Boot->>Cache : 基于 RedisConfig 创建客户端 +Cache-->>Boot : 返回 *redis.Client +Boot->>Router : 注入 Redis 客户端 +Router-->>Dev : 初始化处理器并传入 Redis 客户端 +``` + +图表来源 +- [cmd/server/main.go:22-62](file://cmd/server/main.go#L22-L62) +- [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52) +- [internal/config/redis.go:16-56](file://internal/config/redis.go#L16-L56) +- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) +- [internal/router/router.go:14-25](file://internal/router/router.go#L14-L25) + +## 详细组件分析 + +### Redis 客户端初始化与配置 +- NewClient 实现要点 + - 将 Host 与 Port 组合为 Addr 字符串 + - 设置 Password 与 DB + - 返回 redis.Client 实例供后续调用 +- 配置来源与优先级 + - 若存在环境变量 REDIS_HOST,则以环境变量 REDIS_HOST/REDIS_PORT/REDIS_PASSWORD/REDIS_DATABASE 为准 + - 否则根据运行环境(开发/生产)选择默认主机与端口,并从环境变量读取密码与数据库编号 +- 校验规则 + - RedisConfig.validate 校验 Host 必填;其他字段如 Password、Database 可为空或按需提供 + +```mermaid +flowchart TD +Start(["开始"]) --> CheckEnv["检查是否存在 REDIS_HOST"] +CheckEnv --> |是| FromEnv["从环境变量读取 Redis 配置"] +CheckEnv --> |否| SwitchEnv["根据运行环境选择默认配置"] +FromEnv --> Validate["执行 validate 校验 Host"] +SwitchEnv --> Validate +Validate --> |通过| BuildClient["调用 NewClient 构造客户端"] +Validate --> |失败| Error["返回错误"] +BuildClient --> End(["结束"]) +Error --> End +``` + +图表来源 +- [internal/config/redis.go:16-56](file://internal/config/redis.go#L16-L56) +- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) + +章节来源 +- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) +- [internal/config/redis.go:16-56](file://internal/config/redis.go#L16-L56) +- [internal/config/redis.go:51-56](file://internal/config/redis.go#L51-L56) + +### 启动流程与生命周期 +- 启动阶段 + - 加载配置后,创建 Redis 客户端并记录连接信息 + - 将客户端注入路由,随后启动 HTTP 服务器 +- 关闭阶段 + - 优雅关闭时调用 Close 释放连接 + +```mermaid +sequenceDiagram +participant M as "main" +participant R as "Redis 客户端" +participant S as "HTTP 服务器" +M->>M : 加载配置 +M->>R : NewClient(cfg.Redis) +M->>S : 启动监听 +S-->>M : 服务运行中 +M->>R : 关闭时调用 Close +``` + +图表来源 +- [cmd/server/main.go:22-62](file://cmd/server/main.go#L22-L62) +- [cmd/server/main.go:87-94](file://cmd/server/main.go#L87-L94) + +章节来源 +- [cmd/server/main.go:22-62](file://cmd/server/main.go#L22-L62) +- [cmd/server/main.go:87-94](file://cmd/server/main.go#L87-L94) + +### 设备上报接口中的缓存使用 +- 典型场景 + - 从请求参数构造设备信息,序列化为 JSON + - 在请求上下文基础上向 Redis 写入哈希字段 devices,键为 MAC 地址 +- 错误处理 + - HSet 失败时记录日志并返回系统错误响应 + +```mermaid +sequenceDiagram +participant Client as "客户端" +participant Handler as "DeviceHandler" +participant Redis as "Redis 客户端" +Client->>Handler : GET /audio/reportDevInfo?mac=...&model=... +Handler->>Handler : 参数校验与日志记录 +Handler->>Handler : 构造设备信息并序列化 +Handler->>Redis : HSet(ctx, "devices", mac, json) +alt 成功 +Handler-->>Client : 返回成功响应 +else 失败 +Handler-->>Client : 返回系统错误 +end +``` + +图表来源 +- [internal/handler/device.go:26-78](file://internal/handler/device.go#L26-L78) + +章节来源 +- [internal/handler/device.go:26-78](file://internal/handler/device.go#L26-L78) + +### 类图:配置与客户端关系 +```mermaid +classDiagram +class RedisConfig { ++string Host ++int Port ++string Password ++int Database ++validate() error +} +class NewClient { ++NewClient(cfg RedisConfig) *redis.Client +} +class Config { ++string Env ++string Host ++int Port ++Database DatabaseConfig ++Meilisearch MeilisearchConfig ++Redis RedisConfig ++Addr() string +} +NewClient --> RedisConfig : "接收配置" +Config --> RedisConfig : "包含" +``` + +图表来源 +- [internal/config/redis.go:9-14](file://internal/config/redis.go#L9-L14) +- [internal/config/redis.go:51-56](file://internal/config/redis.go#L51-L56) +- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) +- [internal/config/config.go:9-16](file://internal/config/config.go#L9-L16) + +## 依赖关系分析 +- Redis 客户端版本 + - 通过 go.mod 指定 github.com/redis/go-redis/v9 版本 +- 模块耦合 + - cache/redis.go 仅依赖 internal/config 中的 RedisConfig + - cmd/server/main.go 依赖 cache/redis.go 与 internal/config + - internal/router/router.go 依赖 redis.go 与 handler 层 + - internal/handler/device.go 依赖 redis.go 与 zap 日志 + +```mermaid +graph LR +MAIN["cmd/server/main.go"] --> CACHE["internal/cache/redis.go"] +MAIN --> CFG["internal/config/config.go"] +CFG --> REDISCFG["internal/config/redis.go"] +ROUTER["internal/router/router.go"] --> DEVICE["internal/handler/device.go"] +DEVICE --> CACHE +MOD["go.mod"] --> CACHE +``` + +图表来源 +- [cmd/server/main.go:13-18](file://cmd/server/main.go#L13-L18) +- [internal/cache/redis.go:6-7](file://internal/cache/redis.go#L6-L7) +- [internal/router/router.go:10](file://internal/router/router.go#L10) +- [internal/handler/device.go:10](file://internal/handler/device.go#L10) +- [go.mod:9](file://go.mod#L9) + +章节来源 +- [cmd/server/main.go:13-18](file://cmd/server/main.go#L13-L18) +- [internal/cache/redis.go:6-7](file://internal/cache/redis.go#L6-L7) +- [internal/router/router.go:10](file://internal/router/router.go#L10) +- [internal/handler/device.go:10](file://internal/handler/device.go#L10) +- [go.mod:9](file://go.mod#L9) + +## 性能考虑 +- 连接池与并发 + - 当前 NewClient 未显式设置连接池参数,Redis 客户端默认行为将复用连接;在高并发场景建议结合业务压力测试评估连接数上限 +- 超时与上下文 + - 所有 Redis 操作均使用请求上下文,便于在上游取消或超时控制 +- 数据过期与键空间 + - 当前示例未设置过期时间;对于临时数据可考虑在写入时设置 TTL,避免无界增长 +- 键命名规范 + - 示例使用固定字段名 devices;建议采用前缀+业务域+标识的命名规范,便于运维与清理 +- 批量与流水线 + - 对于批量写入场景,可考虑使用 Pipeline 或 MSET/MGET 提升吞吐 + +## 故障排查指南 +- 连接失败 + - 检查 REDIS_HOST/REDIS_PORT/REDIS_PASSWORD/REDIS_DATABASE 是否正确 + - 确认 validate 校验未返回“redis host is required” +- 写入失败 + - 查看 HSet 返回的错误并结合日志定位 + - 确认 Redis 服务状态与网络连通性 +- 优雅关闭 + - 确保在服务关闭时调用 Close,避免资源泄漏 + +章节来源 +- [internal/config/redis.go:51-56](file://internal/config/redis.go#L51-L56) +- [internal/handler/device.go:70-78](file://internal/handler/device.go#L70-L78) +- [cmd/server/main.go:87-94](file://cmd/server/main.go#L87-L94) + +## 结论 +当前缓存系统以简洁的方式完成了 Redis 客户端的初始化与注入,满足基本的键值写入需求。建议在后续迭代中补充连接池配置、过期策略、键命名规范与监控告警,以提升稳定性与可观测性。同时,可在更多处理器中引入缓存读取与写入,形成统一的缓存访问模式。 + +## 附录 + +### 配置项一览 +- 应用层 + - APP_ENV:运行环境(development/production) + - APP_HOST:监听地址 + - APP_PORT:监听端口 +- Redis 层 + - REDIS_HOST:Redis 主机(优先级最高) + - REDIS_PORT:Redis 端口(默认 16279) + - REDIS_PASSWORD:Redis 密码(默认 eafon123!) + - REDIS_DATABASE:数据库编号(默认 1) + +章节来源 +- [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52) +- [internal/config/redis.go:16-56](file://internal/config/redis.go#L16-L56) \ No newline at end of file diff --git a/.qoder/repowiki/zh/content/开发指南.md b/.qoder/repowiki/zh/content/开发指南.md new file mode 100644 index 0000000..d45b456 --- /dev/null +++ b/.qoder/repowiki/zh/content/开发指南.md @@ -0,0 +1,410 @@ +# 开发指南 + + +**本文引用的文件** +- [README.md](file://README.md) +- [go.mod](file://go.mod) +- [Makefile](file://Makefile) +- [cmd/server/main.go](file://cmd/server/main.go) +- [internal/config/config.go](file://internal/config/config.go) +- [internal/router/router.go](file://internal/router/router.go) +- [internal/handler/health.go](file://internal/handler/health.go) +- [internal/handler/brand.go](file://internal/handler/brand.go) +- [internal/handler/model.go](file://internal/handler/model.go) +- [internal/middleware/logger.go](file://internal/middleware/logger.go) +- [internal/middleware/cors.go](file://internal/middleware/cors.go) +- [internal/middleware/request_id.go](file://internal/middleware/request_id.go) +- [internal/response/response.go](file://internal/response/response.go) +- [internal/repository/brand.go](file://internal/repository/brand.go) +- [internal/repository/model.go](file://internal/repository/model.go) +- [pkg/logger/logger.go](file://pkg/logger/logger.go) + + +## 目录 +1. [简介](#简介) +2. [项目结构](#项目结构) +3. [核心组件](#核心组件) +4. [架构总览](#架构总览) +5. [详细组件分析](#详细组件分析) +6. [依赖分析](#依赖分析) +7. [性能考虑](#性能考虑) +8. [故障排查指南](#故障排查指南) +9. [结论](#结论) +10. [附录](#附录) + +## 简介 +本开发指南面向新加入的开发者,帮助你快速理解并参与 Luxsin 应用 API 项目的开发。内容涵盖代码规范、测试策略、调试技巧、新增接口流程、代码审查要点、单元/集成/性能测试编写指南、调试工具与常见问题排查、扩展开发(插件机制与自定义中间件)、重构与性能优化、安全加固建议,以及开发环境配置与团队协作工作流。 + +## 项目结构 +项目采用分层清晰的组织方式,围绕 Gin HTTP 框架构建,主要目录职责如下: +- cmd/server:应用入口,负责初始化配置、数据库、搜索引擎、缓存与路由,并启动 HTTP 服务。 +- internal/config:集中加载与校验运行时配置(环境、主机、端口、数据库、搜索引擎、Redis)。 +- internal/router:路由注册与中间件装配,按路径分组组织接口。 +- internal/handler:HTTP 处理器,调用仓库层获取数据,使用统一响应体返回。 +- internal/repository:数据访问层,封装 SQL 查询与扫描逻辑。 +- internal/middleware:通用中间件(日志、CORS、请求 ID)。 +- internal/response:统一 JSON 响应体结构与便捷函数。 +- internal/search、internal/cache:外部服务客户端封装(搜索引擎、缓存)。 +- pkg/logger:日志配置与初始化。 +- sql:数据库初始化脚本。 +- Makefile:常用命令(运行、构建、测试、依赖整理)。 +- go.mod:模块与依赖声明。 + +```mermaid +graph TB +subgraph "入口与配置" +MAIN["cmd/server/main.go"] +CFG["internal/config/config.go"] +end +subgraph "网络层" +ROUTER["internal/router/router.go"] +MW_REQID["internal/middleware/request_id.go"] +MW_LOG["internal/middleware/logger.go"] +MW_CORS["internal/middleware/cors.go"] +end +subgraph "业务层" +HANDLER_HEALTH["internal/handler/health.go"] +HANDLER_BRAND["internal/handler/brand.go"] +HANDLER_MODEL["internal/handler/model.go"] +end +subgraph "数据访问层" +REPO_BRAND["internal/repository/brand.go"] +REPO_MODEL["internal/repository/model.go"] +end +subgraph "外部服务" +DB["MySQL"] +MS["Meilisearch"] +REDIS["Redis"] +end +MAIN --> CFG +MAIN --> ROUTER +ROUTER --> MW_REQID +ROUTER --> MW_LOG +ROUTER --> MW_CORS +ROUTER --> HANDLER_HEALTH +ROUTER --> HANDLER_BRAND +ROUTER --> HANDLER_MODEL +HANDLER_BRAND --> REPO_BRAND +HANDLER_MODEL --> REPO_MODEL +MAIN --> DB +MAIN --> MS +MAIN --> REDIS +``` + +图表来源 +- [cmd/server/main.go:22-95](file://cmd/server/main.go#L22-L95) +- [internal/config/config.go:18-56](file://internal/config/config.go#L18-L56) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) +- [internal/middleware/request_id.go:20-30](file://internal/middleware/request_id.go#L20-L30) +- [internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45) +- [internal/middleware/cors.go:7-20](file://internal/middleware/cors.go#L7-L20) +- [internal/handler/brand.go:19-49](file://internal/handler/brand.go#L19-L49) +- [internal/handler/model.go:19-50](file://internal/handler/model.go#L19-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) + +章节来源 +- [README.md:5-17](file://README.md#L5-L17) +- [cmd/server/main.go:22-95](file://cmd/server/main.go#L22-L95) +- [internal/config/config.go:18-56](file://internal/config/config.go#L18-L56) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) + +## 核心组件 +- 配置加载:集中读取环境变量并校验,支持开发/生产环境差异化。 +- 路由与中间件:统一装配 Recovery、RequestID、Logger、CORS,按 /api/v1 与 /audio 分组注册接口。 +- 处理器:品牌与型号查询,支持可选 Base64 响应编码。 +- 仓库层:SQL 查询封装,上下文超时控制,错误包装。 +- 统一响应:标准化 JSON 结构,内置 OK/Fail/BadRequest/InternalError 等便捷函数。 +- 日志:开发/生产不同配置,输出请求级指标与错误信息。 +- 外部服务:MySQL、Meilisearch、Redis 客户端初始化与使用。 + +章节来源 +- [internal/config/config.go:18-56](file://internal/config/config.go#L18-L56) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) +- [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/response/response.go:15-36](file://internal/response/response.go#L15-L36) +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) + +## 架构总览 +下图展示从请求进入、中间件处理、路由分发到处理器与仓库层的完整链路,以及外部服务交互。 + +```mermaid +sequenceDiagram +participant C as "客户端" +participant G as "Gin引擎" +participant M1 as "中间件 : RequestID" +participant M2 as "中间件 : Logger" +participant M3 as "中间件 : CORS" +participant R as "路由分组" +participant H as "处理器" +participant RP as "仓库层" +participant DB as "MySQL" +C->>G : "HTTP 请求" +G->>M1 : "注入/透传 RequestID" +M1->>M2 : "记录请求开始" +M2->>M3 : "设置跨域头" +M3->>R : "匹配路由" +R->>H : "分发到具体处理器" +H->>RP : "执行查询" +RP->>DB : "执行 SQL" +DB-->>RP : "结果集" +RP-->>H : "领域模型列表" +H-->>C : "统一响应体" +``` + +图表来源 +- [cmd/server/main.go:64-64](file://cmd/server/main.go#L64-L64) +- [internal/router/router.go:16-19](file://internal/router/router.go#L16-L19) +- [internal/middleware/request_id.go:20-30](file://internal/middleware/request_id.go#L20-L30) +- [internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45) +- [internal/middleware/cors.go:7-20](file://internal/middleware/cors.go#L7-L20) +- [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) + +## 详细组件分析 + +### 组件:统一响应体 +- 设计目标:所有接口返回一致的 JSON 结构,便于前端解析与错误处理。 +- 关键点:OK 成功、Fail 自定义状态码与消息、BadRequest/InternalServerError 快捷函数。 +- 使用建议:错误路径统一使用 Fail/内部错误函数,避免直接写原生 JSON。 + +```mermaid +classDiagram +class ResponseBody { ++int Code ++string Message ++any Data +} +class ResponseFuncs { ++OK(c, data) ++Fail(c, httpStatus, code, message) ++BadRequest(c, message) ++InternalError(c, message) +} +ResponseFuncs --> ResponseBody : "构造" +``` + +图表来源 +- [internal/response/response.go:9-36](file://internal/response/response.go#L9-L36) + +章节来源 +- [internal/response/response.go:15-36](file://internal/response/response.go#L15-L36) + +### 组件:中间件(日志、CORS、请求 ID) +- RequestID:生成或透传请求 ID,贯穿日志与追踪。 +- Logger:记录状态码、方法、路径、延迟、IP、请求 ID、查询参数与错误。 +- CORS:允许通配来源与常用方法/头,预检请求直接返回。 + +```mermaid +flowchart TD +Start(["进入中间件链"]) --> ReqID["注入/透传 RequestID"] +ReqID --> LogStart["记录请求开始信息"] +LogStart --> CORS["设置跨域头
OPTIONS 预检短路"] +CORS --> Next["继续下一个处理器"] +Next --> LogEnd["计算耗时与状态码
按级别输出日志"] +LogEnd --> End(["完成"]) +``` + +图表来源 +- [internal/middleware/request_id.go:20-30](file://internal/middleware/request_id.go#L20-L30) +- [internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45) +- [internal/middleware/cors.go:7-20](file://internal/middleware/cors.go#L7-L20) + +章节来源 +- [internal/middleware/request_id.go:10-30](file://internal/middleware/request_id.go#L10-L30) +- [internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45) +- [internal/middleware/cors.go:7-20](file://internal/middleware/cors.go#L7-L20) + +### 组件:处理器(品牌、型号、健康检查) +- 健康检查:返回固定结构的“up”状态。 +- 品牌列表:支持按名称模糊查询,可选 Base64 编码响应。 +- 型号列表:支持按品牌或型号名过滤,可选 Base64 编码响应。 +- 错误处理:捕获仓库层错误,记录日志并返回统一错误响应。 + +```mermaid +sequenceDiagram +participant Client as "客户端" +participant Handler as "BrandHandler" +participant Repo as "BrandRepository" +participant DB as "MySQL" +Client->>Handler : "GET /audio/getBrand?brandName=..." +Handler->>Repo : "List(ctx, brandName)" +Repo->>DB : "QueryContext" +DB-->>Repo : "rows" +Repo-->>Handler : "[]Brand" +Handler-->>Client : "OK(data) 或 Base64(JSON)" +``` + +图表来源 +- [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/handler/health.go:14-18](file://internal/handler/health.go#L14-L18) +- [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) + +### 组件:仓库层(SQL 查询封装) +- 上下文支持:所有查询使用 QueryContext,便于超时与取消。 +- 条件拼接:根据输入动态拼接 WHERE 子句,避免无效查询。 +- 扫描与空值:使用 NullString 包装可空字段,转换为指针类型。 +- 错误包装:对查询、迭代、扫描阶段分别包装错误,便于定位。 + +```mermaid +flowchart TD +Enter(["进入 List(ctx, filters)"]) --> Build["拼接基础查询与参数"] +Build --> Exec["db.QueryContext(ctx, query, args...)"] +Exec --> Rows{"rows 是否为空?"} +Rows -- 是 --> ReturnEmpty["返回空列表"] +Rows -- 否 --> Iterate["遍历 rows 并 scanModel()"] +Iterate --> ScanOK{"scan 是否成功?"} +ScanOK -- 否 --> WrapErr["包装扫描错误并返回"] +ScanOK -- 是 --> Append["追加到结果切片"] +Append --> Done["返回结果"] +``` + +图表来源 +- [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/model.go:63-86](file://internal/repository/model.go#L63-L86) + +章节来源 +- [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/model.go:63-95](file://internal/repository/model.go#L63-L95) + +### 组件:配置加载与日志 +- 配置加载:读取环境变量,自动选择数据库配置,校验各组件配置。 +- 日志:开发使用彩色开发配置,生产使用生产配置并调整时间键与编码。 + +```mermaid +flowchart TD +Start(["Load()"]) --> Env["读取 APP_ENV/PORT/HOST"] +Env --> DB["loadDatabase(env) validate()"] +DB --> MS["loadMeilisearch validate()"] +MS --> RD["loadRedis validate()"] +RD --> Build["组装 Config 并返回"] +``` + +图表来源 +- [internal/config/config.go:18-56](file://internal/config/config.go#L18-L56) +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) + +章节来源 +- [internal/config/config.go:18-56](file://internal/config/config.go#L18-L56) +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) + +## 依赖分析 +- 模块与依赖:使用 Gin、MySQL 驱动、Meilisearch SDK、Redis 客户端、Zap 日志。 +- 间接依赖:大量第三方库用于 JSON、加密、并发、压缩等。 +- 版本管理:通过 go.mod 管理,使用 Makefile 的 tidy 命令维护依赖一致性。 + +```mermaid +graph LR +MOD["go.mod"] --> GIN["github.com/gin-gonic/gin"] +MOD --> MYSQL["github.com/go-sql-driver/mysql"] +MOD --> MEILI["github.com/meilisearch/meilisearch-go"] +MOD --> REDIS["github.com/redis/go-redis/v9"] +MOD --> ZAP["go.uber.org/zap"] +``` + +图表来源 +- [go.mod:5-11](file://go.mod#L5-L11) + +章节来源 +- [go.mod:1-47](file://go.mod#L1-L47) +- [Makefile:12-13](file://Makefile#L12-L13) + +## 性能考虑 +- 超时控制:仓库层使用 QueryContext,建议在入口处设置合理超时,避免慢查询拖垮服务。 +- 日志开销:生产环境日志级别更高,避免在高频路径中进行昂贵的日志格式化。 +- 响应体积:当数据量较大时,可启用 Base64 响应以减少传输体积,但需权衡 CPU 开销。 +- 连接池:确保数据库、Redis、搜索引擎连接池参数合理,避免连接争用。 +- 中间件顺序:将轻量中间件前置,重逻辑后置,减少对正常路径的影响。 +- 缓存策略:对热点查询结果进行缓存,降低数据库压力。 + +## 故障排查指南 +- 健康检查失败:确认 /api/v1/health 能返回状态 up;若失败,查看日志中 server startup/shutdown 相关条目。 +- 数据库连接失败:核对 DATABASE_* 环境变量与网络连通性;生产环境密码必须通过环境变量注入。 +- 跨域问题:确认中间件已装配且 OPTIONS 预检返回 204;检查前端请求头是否包含允许的字段。 +- 请求无日志:确认 Logger 中间件已正确装配,且 RequestID 已透传。 +- 响应异常:检查处理器是否使用统一响应函数;错误路径是否调用了 InternalError/BadRequest。 +- 接口无响应:检查路由分组与路径是否正确;确认处理器实例已创建并注册。 + +章节来源 +- [README.md:83-99](file://README.md#L83-L99) +- [internal/router/router.go:27-38](file://internal/router/router.go#L27-L38) +- [internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45) +- [internal/middleware/cors.go:7-20](file://internal/middleware/cors.go#L7-L20) +- [internal/response/response.go:15-36](file://internal/response/response.go#L15-L36) + +## 结论 +本指南提供了从项目结构、核心组件到扩展开发与运维排错的完整指引。建议在新增接口时严格遵循“处理器-仓库-统一响应”的分层设计,配合中间件与配置体系,确保可维护性与可观测性。后续可在此基础上引入鉴权、限流、熔断与可观测性组件,逐步完善系统能力。 + +## 附录 + +### 新增接口标准流程 +- 在 internal/handler 下新建处理器文件,定义 NewXxxHandler 与 XxxHandler 方法。 +- 在 internal/router/router.go 的相应分组下注册路由与处理器。 +- 在 internal/repository 中补充必要的查询方法,使用 QueryContext 并包装错误。 +- 使用 internal/response 统一返回响应。 +- 编写单元测试与集成测试,覆盖正常与异常路径。 +- 如需跨域或特殊头部,更新中间件或在处理器内设置。 + +章节来源 +- [README.md:111-116](file://README.md#L111-L116) +- [internal/router/router.go:27-38](file://internal/router/router.go#L27-L38) + +### 代码规范与审查要点 +- 命名规范:包名小写、结构体与方法首字母大写、常量与全局变量见名知意。 +- 错误处理:所有错误必须被记录或向上返回;避免忽略错误。 +- 日志:区分 Info/Warn/Error 级别;记录关键上下文(如请求 ID、状态码、耗时)。 +- 配置:敏感信息通过环境变量注入;生产环境禁止硬编码。 +- 中间件:保持单一职责;避免在中间件中做重逻辑。 +- 测试:每个处理器至少包含正常与错误场景;仓库层覆盖条件分支。 + +### 测试策略与编写指南 +- 单元测试:针对处理器与仓库层,使用内存数据库或模拟对象隔离外部依赖。 +- 集成测试:启动最小化服务,验证路由、中间件、配置与外部服务连通性。 +- 性能测试:使用压测工具对关键接口施压,观察 P95/P99 延迟与错误率,结合日志定位瓶颈。 +- 建议工具:go test、vegeta、hey、pprof。 + +章节来源 +- [Makefile:9-10](file://Makefile#L9-L10) + +### 调试工具与技巧 +- 日志:开发环境开启彩色日志,生产环境使用 ISO 时间与生产配置;关注请求耗时与错误堆栈。 +- 请求追踪:通过 Request ID 关联一次请求在多组件中的日志。 +- 网络抓包:使用 curl/Postman 验证路由与参数;检查 CORS 与状态码。 +- 性能剖析:使用 pprof 分析 CPU/内存热点;结合日志定位慢查询。 + +章节来源 +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) +- [internal/middleware/request_id.go:10-30](file://internal/middleware/request_id.go#L10-L30) +- [internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45) + +### 扩展开发与中间件 +- 插件机制:当前未内置插件框架,建议通过接口抽象与依赖注入的方式扩展功能(如鉴权、限流)。 +- 自定义中间件:遵循 gin.HandlerFunc 签名,在中间件中只做横切关注点,避免业务逻辑。 +- 路由扩展:在 router 分组下新增子路由,注册处理器并确保统一响应。 + +章节来源 +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) +- [internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45) + +### 重构与优化建议 +- 代码重构:拆分过长函数、消除重复代码、统一错误处理与日志风格。 +- 性能优化:索引优化、查询去 N+1、连接池参数调优、缓存热点数据。 +- 安全加固:强制 HTTPS、限制请求大小、参数校验、敏感日志脱敏。 + +### 开发环境配置与团队协作 +- 环境变量:开发使用 .env,生产通过环境变量注入;数据库密码必须来自环境。 +- 启动方式:使用 make run 或 go run ./cmd/server;构建产物位于 bin/server。 +- 团队协作:提交前执行 go test 与 go mod tidy;PR 需要至少一名 reviewer 通过。 + +章节来源 +- [README.md:21-81](file://README.md#L21-L81) +- [Makefile:3-7](file://Makefile#L3-L7) \ No newline at end of file diff --git a/.qoder/repowiki/zh/content/快速开始.md b/.qoder/repowiki/zh/content/快速开始.md new file mode 100644 index 0000000..5ab41c9 --- /dev/null +++ b/.qoder/repowiki/zh/content/快速开始.md @@ -0,0 +1,317 @@ +# 快速开始 + + +**本文引用的文件** +- [README.md](file://README.md) +- [go.mod](file://go.mod) +- [Makefile](file://Makefile) +- [cmd/server/main.go](file://cmd/server/main.go) +- [internal/config/config.go](file://internal/config/config.go) +- [internal/config/database.go](file://internal/config/database.go) +- [internal/config/redis.go](file://internal/config/redis.go) +- [internal/config/meilisearch.go](file://internal/config/meilisearch.go) +- [internal/router/router.go](file://internal/router/router.go) +- [internal/handler/health.go](file://internal/handler/health.go) +- [internal/handler/brand.go](file://internal/handler/brand.go) +- [internal/response/response.go](file://internal/response/response.go) +- [internal/database/mysql.go](file://internal/database/mysql.go) +- [internal/search/meilisearch.go](file://internal/search/meilisearch.go) +- [internal/cache/redis.go](file://internal/cache/redis.go) +- [internal/middleware/logger.go](file://internal/middleware/logger.go) + + +## 目录 +1. [简介](#简介) +2. [项目结构](#项目结构) +3. [核心组件](#核心组件) +4. [架构总览](#架构总览) +5. [详细组件分析](#详细组件分析) +6. [依赖分析](#依赖分析) +7. [性能考虑](#性能考虑) +8. [故障排查指南](#故障排查指南) +9. [结论](#结论) +10. [附录](#附录) + +## 简介 +本指南面向新手开发者,帮助你在本地快速搭建 Luxsin 应用 API 的开发与运行环境。你将了解环境要求、依赖安装、配置设置(含 .env 示例)、开发与生产环境差异、数据库与搜索引擎/缓存连接配置,以及如何使用 Makefile 进行构建与运行。同时提供健康检查、品牌查询等基础接口的使用示例,确保你能顺利启动服务并验证功能。 + +## 项目结构 +项目采用分层与按功能模块组织的结构,核心入口在 cmd/server,配置、路由、处理器、中间件、数据库、缓存、搜索引擎、统一响应体等均按职责划分到 internal 与 pkg 下。 + +```mermaid +graph TB +subgraph "入口" +MAIN["cmd/server/main.go"] +end +subgraph "配置层" +CFG["internal/config/*"] +end +subgraph "数据访问" +DB["internal/database/mysql.go"] +REDIS["internal/cache/redis.go"] +MS["internal/search/meilisearch.go"] +end +subgraph "业务层" +ROUTER["internal/router/router.go"] +HANDLERS["internal/handler/*"] +RESP["internal/response/response.go"] +MW["internal/middleware/*"] +end +MAIN --> CFG +MAIN --> DB +MAIN --> REDIS +MAIN --> MS +MAIN --> ROUTER +ROUTER --> HANDLERS +HANDLERS --> RESP +ROUTER --> MW +``` + +图表来源 +- [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/router/router.go:1-42](file://internal/router/router.go#L1-L42) + +章节来源 +- [README.md:5-17](file://README.md#L5-L17) +- [cmd/server/main.go:22-64](file://cmd/server/main.go#L22-L64) + +## 核心组件 +- 入口程序:负责加载配置、初始化日志、数据库、搜索引擎与缓存客户端,创建路由并启动 HTTP 服务器。 +- 配置系统:集中读取环境变量,支持开发与生产环境默认值,以及通过 DATABASE_*、REDIS_*、MEILISEARCH_* 等前缀覆盖。 +- 路由与处理器:定义 /api/v1/health 与 /audio/* 等业务接口。 +- 统一响应体:规范所有接口返回结构,便于前端消费与错误处理。 +- 中间件:日志、CORS、请求 ID 等通用能力。 +- 数据库/搜索引擎/缓存:MySQL、Meilisearch、Redis 客户端初始化与校验。 + +章节来源 +- [cmd/server/main.go:22-96](file://cmd/server/main.go#L22-L96) +- [internal/config/config.go:18-56](file://internal/config/config.go#L18-L56) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) +- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37) + +## 架构总览 +下图展示了从入口到各子系统的调用关系与数据流。 + +```mermaid +sequenceDiagram +participant Entrypoint as "入口(main)" +participant Cfg as "配置(Config)" +participant Log as "日志(Logger)" +participant DB as "数据库(MySQL)" +participant MS as "搜索引擎(Meilisearch)" +participant RC as "缓存(Redis)" +participant RT as "路由(Router)" +participant H as "处理器(Handlers)" +Entrypoint->>Cfg : 加载配置 +Entrypoint->>Log : 初始化日志 +Entrypoint->>DB : 打开连接 +Entrypoint->>MS : 创建客户端 +Entrypoint->>RC : 创建客户端 +Entrypoint->>RT : 注册路由 +RT->>H : 调用处理器 +H-->>Entrypoint : 统一响应体 +``` + +图表来源 +- [cmd/server/main.go:22-64](file://cmd/server/main.go#L22-L64) +- [internal/router/router.go:21-38](file://internal/router/router.go#L21-L38) +- [internal/response/response.go:15-21](file://internal/response/response.go#L15-L21) + +## 详细组件分析 + +### 启动流程与控制循环 +- 入口加载配置,按环境设置 Gin 模式。 +- 初始化日志、数据库、搜索引擎与缓存。 +- 创建 HTTP 服务器并启动协程监听。 +- 通过信号量优雅关闭,设置超时上下文。 + +```mermaid +flowchart TD +Start(["启动"]) --> LoadCfg["加载配置"] +LoadCfg --> SetMode["设置运行模式"] +SetMode --> InitLog["初始化日志"] +InitLog --> OpenDB["打开数据库连接"] +OpenDB --> InitSearch["初始化搜索引擎"] +InitSearch --> InitRedis["初始化缓存"] +InitRedis --> BuildRouter["构建路由"] +BuildRouter --> StartHTTP["启动HTTP服务"] +StartHTTP --> WaitSignal["等待退出信号"] +WaitSignal --> Graceful["优雅关闭(带超时)"] +Graceful --> End(["结束"]) +``` + +图表来源 +- [cmd/server/main.go:22-96](file://cmd/server/main.go#L22-L96) + +章节来源 +- [cmd/server/main.go:22-96](file://cmd/server/main.go#L22-L96) + +### 配置系统与环境变量 +- 关键变量 + - APP_ENV:运行环境(development/production),影响数据库、搜索引擎、缓存默认值与 Gin 模式。 + - APP_HOST:监听地址,默认 0.0.0.0。 + - APP_PORT:监听端口,默认 8080。 + - GIN_MODE:Gin 运行模式(debug/release),生产环境自动切换为 release。 +- 数据库覆盖优先级:若设置 DATABASE_HOST 则完全以 DATABASE_* 环境变量为准;否则按 APP_ENV 选择默认值。 +- 生产环境必须提供 DATABASE_PASSWORD;搜索引擎与缓存同理,可通过 MEILISEARCH_* 与 REDIS_* 覆盖。 + +章节来源 +- [internal/config/config.go:18-56](file://internal/config/config.go#L18-L56) +- [internal/config/database.go:17-72](file://internal/config/database.go#L17-L72) +- [internal/config/meilisearch.go:14-51](file://internal/config/meilisearch.go#L14-L51) +- [internal/config/redis.go:16-57](file://internal/config/redis.go#L16-L57) +- [README.md:39-45](file://README.md#L39-L45) +- [README.md:46-56](file://README.md#L46-L56) + +### 路由与接口 +- /api/v1/health:健康检查接口,返回状态 up。 +- /audio/getBrand:品牌列表查询,支持按 brandName 参数模糊过滤。 +- /audio/getModel:型号查询(具体参数与行为见处理器实现)。 +- /audio/modelList:基于搜索引擎的型号列表检索。 +- /audio/reportDevInfo:设备信息上报(具体参数与行为见处理器实现)。 + +章节来源 +- [internal/router/router.go:27-38](file://internal/router/router.go#L27-L38) +- [internal/handler/health.go:14-18](file://internal/handler/health.go#L14-L18) +- [internal/handler/brand.go:26-49](file://internal/handler/brand.go#L26-L49) + +### 统一响应体 +- 所有接口返回统一结构,包含 code、message、data 字段,便于前端统一处理。 +- 提供 OK、Fail、BadRequest、InternalError 等便捷函数。 + +章节来源 +- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37) + +### 中间件链路 +- Recovery:异常恢复。 +- RequestID:注入请求 ID。 +- Logger:记录请求日志,区分不同状态等级。 +- CORS:跨域支持。 + +章节来源 +- [internal/router/router.go:14-20](file://internal/router/router.go#L14-L20) +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) + +### 数据库连接 +- 使用标准库 database/sql 与 MySQL 驱动,设置连接池参数与超时。 +- 通过配置生成 DSN 并 Ping 校验连通性。 + +章节来源 +- [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) + +### 搜索引擎与缓存 +- Meilisearch:按索引执行搜索,限制返回字段,解码为映射列表。 +- Redis:按配置创建客户端,用于设备信息上报等场景。 + +章节来源 +- [internal/search/meilisearch.go:17-46](file://internal/search/meilisearch.go#L17-L46) +- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) +- [internal/config/meilisearch.go:14-51](file://internal/config/meilisearch.go#L14-L51) +- [internal/config/redis.go:16-57](file://internal/config/redis.go#L16-L57) + +## 依赖分析 +- 运行时依赖:Gin、MySQL 驱动、Meilisearch SDK、Redis SDK、Zap 日志。 +- 构建工具:Makefile 提供 run/build/test/tidy 命令。 +- 版本要求:Go 1.24.0(go.mod 中声明),但 README 要求 1.22+。 + +```mermaid +graph LR +GO_MOD["go.mod"] --> Gin["github.com/gin-gonic/gin"] +GO_MOD --> MySQL["github.com/go-sql-driver/mysql"] +GO_MOD --> Meili["github.com/meilisearch/meilisearch-go"] +GO_MOD --> RedisSDK["github.com/redis/go-redis/v9"] +GO_MOD --> Zap["go.uber.org/zap"] +MK["Makefile"] --> Run["make run"] +MK --> Build["make build"] +MK --> Test["make test"] +MK --> Tidy["make tidy"] +``` + +图表来源 +- [go.mod:1-47](file://go.mod#L1-L47) +- [Makefile:1-14](file://Makefile#L1-L14) + +章节来源 +- [go.mod:1-47](file://go.mod#L1-L47) +- [Makefile:1-14](file://Makefile#L1-L14) +- [README.md:21-29](file://README.md#L21-L29) + +## 性能考虑 +- 数据库连接池:最大并发、空闲连接数与连接生命周期已设置,建议结合实际 QPS 调优。 +- HTTP 超时:读取、写入、空闲超时已设定,可根据网络与硬件条件调整。 +- 搜索引擎:限制 AttributesToRetrieve,减少传输与解析开销。 +- 缓存:合理设置过期策略与键空间,避免热点键导致抖动。 + +## 故障排查指南 +- 数据库连接失败 + - 检查 DATABASE_HOST/DATABASE_PORT/DATABASE_NAME/DATABASE_USER/DATABASE_PASSWORD 是否正确。 + - 生产环境必须提供 DATABASE_PASSWORD。 +- 搜索引擎或缓存配置错误 + - 确认 MEILISEARCH_HOST/API_KEY/INDEX 与 REDIS_* 配置完整。 +- 健康检查失败 + - 访问 /api/v1/health,确认返回状态 up。 +- 品牌查询无结果 + - 使用 /audio/getBrand?brandName=xxx 进行模糊查询,确认数据库中存在相关数据。 +- 日志定位 + - 中间件会记录请求状态、耗时、IP、请求 ID 等,便于问题追踪。 + +章节来源 +- [internal/config/database.go:57-72](file://internal/config/database.go#L57-L72) +- [internal/config/meilisearch.go:39-51](file://internal/config/meilisearch.go#L39-L51) +- [internal/config/redis.go:51-57](file://internal/config/redis.go#L51-L57) +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) + +## 结论 +通过本指南,你可以完成环境准备、依赖安装、配置覆盖与运行验证。建议先在开发环境跑通健康检查与品牌查询,再逐步接入数据库、搜索引擎与缓存。生产环境务必通过环境变量提供敏感配置,避免硬编码。 + +## 附录 + +### 环境要求与安装 +- 环境要求:Go 1.22+(仓库 go.mod 为 1.24.0,满足要求) +- 安装依赖:执行 go mod tidy +- 运行方式:make run 或 go run ./cmd/server + +章节来源 +- [README.md:21-29](file://README.md#L21-L29) +- [go.mod:3](file://go.mod#L3) +- [Makefile:3-4](file://Makefile#L3-L4) + +### 配置与环境变量说明 +- APP_ENV:运行环境(development/production) +- APP_HOST:监听地址 +- APP_PORT:监听端口 +- GIN_MODE:Gin 运行模式(debug/release) +- DATABASE_*:数据库连接(可覆盖默认值) +- MEILISEARCH_*:搜索引擎连接(可覆盖默认值) +- REDIS_*:缓存连接(可覆盖默认值) + +章节来源 +- [README.md:39-45](file://README.md#L39-L45) +- [internal/config/config.go:18-56](file://internal/config/config.go#L18-L56) +- [internal/config/database.go:17-72](file://internal/config/database.go#L17-L72) +- [internal/config/meilisearch.go:14-51](file://internal/config/meilisearch.go#L14-L51) +- [internal/config/redis.go:16-57](file://internal/config/redis.go#L16-L57) + +### 健康检查与基础接口示例 +- 健康检查:GET /api/v1/health +- 品牌查询:GET /audio/getBrand + - 全量:GET /audio/getBrand + - 模糊:GET /audio/getBrand?brandName=xxx + +章节来源 +- [README.md:83-109](file://README.md#L83-L109) +- [internal/router/router.go:27-38](file://internal/router/router.go#L27-L38) +- [internal/handler/health.go:14-18](file://internal/handler/health.go#L14-L18) +- [internal/handler/brand.go:26-49](file://internal/handler/brand.go#L26-L49) + +### 构建与运行 +- 构建:make build,产物位于 bin/server +- 运行:make run 或 go run ./cmd/server +- 测试:make test +- 依赖整理:make tidy + +章节来源 +- [Makefile:1-14](file://Makefile#L1-L14) +- [README.md:117-122](file://README.md#L117-L122) \ No newline at end of file diff --git a/.qoder/repowiki/zh/content/故障排除.md b/.qoder/repowiki/zh/content/故障排除.md new file mode 100644 index 0000000..b8d8721 --- /dev/null +++ b/.qoder/repowiki/zh/content/故障排除.md @@ -0,0 +1,545 @@ +# 故障排除 + + +**本文引用的文件** +- [cmd/server/main.go](file://cmd/server/main.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) +- [internal/router/router.go](file://internal/router/router.go) +- [internal/middleware/logger.go](file://internal/middleware/logger.go) +- [pkg/logger/logger.go](file://pkg/logger/logger.go) +- [internal/handler/health.go](file://internal/handler/health.go) +- [internal/handler/model.go](file://internal/handler/model.go) +- [internal/repository/model.go](file://internal/repository/model.go) +- [internal/response/response.go](file://internal/response/response.go) +- [go.mod](file://go.mod) +- [README.md](file://README.md) + + +## 目录 +1. [简介](#简介) +2. [项目结构](#项目结构) +3. [核心组件](#核心组件) +4. [架构总览](#架构总览) +5. [详细组件分析](#详细组件分析) +6. [依赖分析](#依赖分析) +7. [性能考虑](#性能考虑) +8. [故障排除指南](#故障排除指南) +9. [结论](#结论) +10. [附录](#附录) + +## 简介 +本故障排除文档面向运维与开发人员,围绕 Luxsin 应用 API 的常见问题提供系统化的诊断与修复路径。重点覆盖以下方面: +- 数据库连接问题:参数校验、连接池、超时与 Ping 校验 +- 缓存失效:Redis 连接、认证与可用性 +- 搜索异常:Meilisearch 连接、索引与查询 +- 日志分析:日志级别、字段与采样策略 +- 网络与配置:端口、主机、环境变量与路由 +- 权限与安全:生产环境敏感配置与最小暴露原则 +- 监控与告警:关键指标与告警流程 +- 应急响应:优雅停机、回滚策略与数据恢复指引 + +## 项目结构 +应用采用分层架构,入口在命令行模块,配置集中于内部配置包,服务通过中间件、路由、处理器、仓储与外部组件协作。 + +```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
连接、Ping、连接池"] +REDIS["internal/cache/redis.go
客户端创建"] +MEILI["internal/search/meilisearch.go
搜索客户端与查询"] +end +subgraph "服务层" +ROUTER["internal/router/router.go
路由注册与中间件"] +LOGMW["internal/middleware/logger.go
请求日志中间件"] +HEALTH["internal/handler/health.go
健康检查"] +MODEL_H["internal/handler/model.go
模型查询处理器"] +MODEL_R["internal/repository/model.go
模型仓储"] +RESP["internal/response/response.go
统一响应体"] +end +MAIN --> CFG +MAIN --> MYSQL +MAIN --> REDIS +MAIN --> MEILI +MAIN --> ROUTER +ROUTER --> LOGMW +ROUTER --> HEALTH +ROUTER --> MODEL_H +MODEL_H --> MODEL_R +MODEL_H --> RESP +``` + +**图表来源** +- [cmd/server/main.go:22-96](file://cmd/server/main.go#L22-L96) +- [internal/config/config.go:18-56](file://internal/config/config.go#L18-L56) +- [internal/config/database.go:17-72](file://internal/config/database.go#L17-L72) +- [internal/config/meilisearch.go:14-51](file://internal/config/meilisearch.go#L14-L51) +- [internal/config/redis.go:16-57](file://internal/config/redis.go#L16-L57) +- [internal/database/mysql.go:14-47](file://internal/database/mysql.go#L14-L47) +- [internal/cache/redis.go:10-17](file://internal/cache/redis.go#L10-L17) +- [internal/search/meilisearch.go:17-46](file://internal/search/meilisearch.go#L17-L46) +- [internal/router/router.go:14-42](file://internal/router/router.go#L14-L42) +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) +- [internal/handler/health.go:14-19](file://internal/handler/health.go#L14-L19) +- [internal/handler/model.go:26-51](file://internal/handler/model.go#L26-L51) +- [internal/repository/model.go:20-61](file://internal/repository/model.go#L20-L61) +- [internal/response/response.go:15-37](file://internal/response/response.go#L15-L37) + +**章节来源** +- [README.md:5-17](file://README.md#L5-L17) +- [cmd/server/main.go:22-96](file://cmd/server/main.go#L22-L96) +- [internal/router/router.go:14-42](file://internal/router/router.go#L14-L42) + +## 核心组件 +- 启动与生命周期:读取配置、初始化日志、建立数据库/搜索/缓存连接、启动 HTTP 服务器、信号监听与优雅停机 +- 配置加载:支持从环境变量覆盖默认配置,并进行必要校验 +- 中间件:日志、CORS、Request ID、恢复 +- 路由与处理器:健康检查、品牌/模型查询、设备上报、模型列表搜索 +- 统一响应:标准化返回码与消息 + +**章节来源** +- [cmd/server/main.go:22-96](file://cmd/server/main.go#L22-L96) +- [internal/config/config.go:18-56](file://internal/config/config.go#L18-L56) +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) +- [internal/router/router.go:14-42](file://internal/router/router.go#L14-L42) +- [internal/response/response.go:15-37](file://internal/response/response.go#L15-L37) + +## 架构总览 +应用通过 Gin 路由组织 API,中间件负责日志与跨域等横切关注点,处理器调用仓储访问数据库或调用搜索/缓存客户端。日志采用 Zap,按环境输出不同编码与时间格式。 + +```mermaid +sequenceDiagram +participant Client as "客户端" +participant Router as "Gin 路由" +participant MW as "日志中间件" +participant Handler as "处理器" +participant Repo as "仓储" +participant DB as "MySQL" +participant Log as "Zap 日志" +Client->>Router : "HTTP 请求" +Router->>MW : "进入中间件链" +MW->>Log : "记录请求开始" +Router->>Handler : "匹配到处理器" +Handler->>Repo : "执行查询" +Repo->>DB : "执行 SQL" +DB-->>Repo : "结果集" +Repo-->>Handler : "领域对象列表" +Handler-->>Client : "统一响应" +MW->>Log : "记录状态/耗时/错误" +``` + +**图表来源** +- [internal/router/router.go:14-42](file://internal/router/router.go#L14-L42) +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) +- [internal/handler/model.go:26-51](file://internal/handler/model.go#L26-L51) +- [internal/repository/model.go:20-61](file://internal/repository/model.go#L20-L61) +- [internal/database/mysql.go:14-47](file://internal/database/mysql.go#L14-L47) +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) + +## 详细组件分析 + +### 数据库连接(MySQL) +- 连接参数:用户、密码、主机、端口、数据库名、字符集与时区参数 +- 连接池:最大并发、空闲连接数、连接最大存活时间 +- Ping 校验:启动阶段 5 秒超时验证连通性 +- 错误处理:失败时关闭连接并返回带包装的错误 + +```mermaid +flowchart TD +Start(["启动"]) --> BuildDSN["构建 DSN 参数"] +BuildDSN --> Open["打开连接 sql.Open"] +Open --> SetPool["设置连接池参数"] +SetPool --> PingCtx["5 秒超时 PingContext"] +PingCtx --> PingOK{"Ping 成功?"} +PingOK --> |否| Close["关闭连接并报错"] +PingOK --> |是| Ready["数据库就绪"] +Close --> End(["结束"]) +Ready --> End +``` + +**图表来源** +- [internal/database/mysql.go:14-47](file://internal/database/mysql.go#L14-L47) + +**章节来源** +- [internal/database/mysql.go:14-47](file://internal/database/mysql.go#L14-L47) +- [internal/config/database.go:17-72](file://internal/config/database.go#L17-L72) + +### 缓存(Redis) +- 客户端创建:根据配置 Addr、Password、DB 初始化 +- 连接验证:通过 PingContext 在启动阶段进行 +- 关闭:服务优雅停机时关闭连接 + +```mermaid +sequenceDiagram +participant Main as "main.go" +participant Cfg as "Redis 配置" +participant Redis as "Redis 客户端" +participant Log as "Zap 日志" +Main->>Cfg : "读取 Redis 配置" +Main->>Redis : "NewClient(Addr, Password, DB)" +Redis-->>Main : "返回客户端实例" +Main->>Log : "记录连接成功" +``` + +**图表来源** +- [cmd/server/main.go:56-62](file://cmd/server/main.go#L56-L62) +- [internal/cache/redis.go:10-17](file://internal/cache/redis.go#L10-L17) +- [internal/config/redis.go:16-57](file://internal/config/redis.go#L16-L57) + +**章节来源** +- [internal/cache/redis.go:10-17](file://internal/cache/redis.go#L10-L17) +- [internal/config/redis.go:16-57](file://internal/config/redis.go#L16-L57) + +### 搜索(Meilisearch) +- 客户端:基于 Host 与 API Key 创建索引管理器 +- 查询:限制返回条数、指定检索字段 +- 解码:将命中结果解码为映射列表 +- 错误:对查询与解码过程进行包装并返回 + +```mermaid +sequenceDiagram +participant Handler as "处理器" +participant Search as "Meilisearch 客户端" +participant Index as "索引" +participant Resp as "搜索响应" +Handler->>Search : "ModelList(key, count)" +Search->>Index : "SearchWithContext(req)" +Index-->>Search : "响应" +Search->>Search : "DecodeInto 映射列表" +Search-->>Handler : "结果或错误" +``` + +**图表来源** +- [internal/search/meilisearch.go:22-46](file://internal/search/meilisearch.go#L22-L46) +- [internal/config/meilisearch.go:14-51](file://internal/config/meilisearch.go#L14-L51) + +**章节来源** +- [internal/search/meilisearch.go:22-46](file://internal/search/meilisearch.go#L22-L46) +- [internal/config/meilisearch.go:14-51](file://internal/config/meilisearch.go#L14-L51) + +### 日志与中间件 +- 日志:生产环境使用生产配置,开发环境使用开发配置;时间键与编码可定制 +- 请求日志:记录状态码、方法、路径、延迟、客户端 IP、请求 ID、错误集合 +- 级别策略:>=500 记为错误,>=400 记为警告,否则为信息 + +```mermaid +flowchart TD +Enter(["进入中间件"]) --> Start["记录开始时间与路径"] +Start --> Next["调用下一个处理器"] +Next --> Latency["计算耗时"] +Latency --> Status["获取状态码"] +Status --> Fields["组装日志字段"] +Fields --> Level{"状态码级别"} +Level --> |>=500| Error["记录错误日志"] +Level --> |>=400| Warn["记录警告日志"] +Level --> |<400| Info["记录信息日志"] +Error --> Exit(["退出"]) +Warn --> Exit +Info --> Exit +``` + +**图表来源** +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) + +**章节来源** +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) + +### 路由与处理器 +- 路由:健康检查、品牌、模型、模型列表、设备上报 +- 处理器:模型查询处理器调用仓储执行 SQL 查询 +- 统一响应:OK/Fail/BadRequest/InternalError + +```mermaid +classDiagram +class Router { ++New(log, db, search, redis) Engine +} +class HealthHandler { ++Check(c) +} +class ModelHandler { ++GetModel(c) +} +class ModelRepository { ++List(ctx, brandName, modelName) []Model +} +class Response { ++OK(c, data) ++Fail(c, httpStatus, code, message) ++BadRequest(c, message) ++InternalError(c, message) +} +Router --> HealthHandler : "注册" +Router --> ModelHandler : "注册" +ModelHandler --> ModelRepository : "调用" +ModelHandler --> Response : "返回" +``` + +**图表来源** +- [internal/router/router.go:14-42](file://internal/router/router.go#L14-L42) +- [internal/handler/health.go:14-19](file://internal/handler/health.go#L14-L19) +- [internal/handler/model.go:26-51](file://internal/handler/model.go#L26-L51) +- [internal/repository/model.go:20-61](file://internal/repository/model.go#L20-L61) +- [internal/response/response.go:15-37](file://internal/response/response.go#L15-L37) + +**章节来源** +- [internal/router/router.go:14-42](file://internal/router/router.go#L14-L42) +- [internal/handler/model.go:26-51](file://internal/handler/model.go#L26-L51) +- [internal/repository/model.go:20-61](file://internal/repository/model.go#L20-L61) +- [internal/response/response.go:15-37](file://internal/response/response.go#L15-L37) + +## 依赖分析 +- 运行时依赖:Gin、MySQL 驱动、Meilisearch SDK、Redis SDK、Zap +- 版本与间接依赖:go.mod 明确列出直接依赖及部分间接依赖 + +```mermaid +graph LR +APP["app-api"] --> GIN["github.com/gin-gonic/gin"] +APP --> MYSQL["github.com/go-sql-driver/mysql"] +APP --> MEILI["github.com/meilisearch/meilisearch-go"] +APP --> REDIS["github.com/redis/go-redis/v9"] +APP --> ZAP["go.uber.org/zap"] +``` + +**图表来源** +- [go.mod:5-11](file://go.mod#L5-L11) + +**章节来源** +- [go.mod:5-11](file://go.mod#L5-L11) + +## 性能考虑 +- 连接池:数据库设置最大并发、空闲连接与连接最大存活时间,有助于控制资源占用与抖动 +- 超时:启动阶段 PingContext 设置 5 秒超时,避免阻塞启动 +- 日志:生产环境使用生产配置,减少开销;仅在错误级别输出详细字段 +- 搜索:限制返回条数,避免过大数据传输 + +**章节来源** +- [internal/database/mysql.go:33-36](file://internal/database/mysql.go#L33-L36) +- [internal/database/mysql.go:37-43](file://internal/database/mysql.go#L37-L43) +- [pkg/logger/logger.go:10-17](file://pkg/logger/logger.go#L10-L17) +- [internal/search/meilisearch.go:22-29](file://internal/search/meilisearch.go#L22-L29) + +## 故障排除指南 + +### 一、数据库连接问题 +- 症状 + - 启动即报“database connect failed” + - 健康检查正常但接口报错 +- 诊断步骤 + 1) 检查环境变量是否正确:APP_ENV、DATABASE_HOST/PORT/NAME/USER/PASSWORD + 2) 校验配置加载:确认 loadDatabase 与 validate 是否通过 + 3) 观察启动日志中数据库连接信息与 Ping 结果 + 4) 使用数据库客户端验证凭据与网络连通性 +- 解决策略 + - 生产环境必须提供 DATABASE_PASSWORD + - 如使用自定义配置,确保所有必填项非空 + - 调整连接池参数以适配负载 +- 相关实现 + - [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) + - [cmd/server/main.go:38-48](file://cmd/server/main.go#L38-L48) + +**章节来源** +- [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) +- [cmd/server/main.go:38-48](file://cmd/server/main.go#L38-L48) + +### 二、缓存失效(Redis) +- 症状 + - 启动日志显示“redis connected”后立即报错 + - 接口出现缓存相关错误 +- 诊断步骤 + 1) 检查 REDIS_HOST/PORT/PASSWORD/DATABASE 环境变量 + 2) 校验 Redis 实例可达性与认证 + 3) 查看启动日志中的连接信息 +- 解决策略 + - 确保 REDIS_HOST 非空并通过 validate + - 如使用自定义配置,确保端口与数据库编号正确 +- 相关实现 + - [internal/config/redis.go:16-57](file://internal/config/redis.go#L16-L57) + - [internal/cache/redis.go:10-17](file://internal/cache/redis.go#L10-L17) + - [cmd/server/main.go:56-62](file://cmd/server/main.go#L56-L62) + +**章节来源** +- [internal/config/redis.go:16-57](file://internal/config/redis.go#L16-L57) +- [internal/cache/redis.go:10-17](file://internal/cache/redis.go#L10-L17) +- [cmd/server/main.go:56-62](file://cmd/server/main.go#L56-L62) + +### 三、搜索异常(Meilisearch) +- 症状 + - 搜索接口返回空结果或报错 + - 查询日志显示 decode 或 search 包装错误 +- 诊断步骤 + 1) 检查 MEILISEARCH_HOST/API_KEY/INDEX 是否正确 + 2) 校验索引是否存在且已同步 + 3) 查看查询请求参数与返回字段映射 +- 解决策略 + - 确保 HOST/API_KEY/INDEX 均非空并通过 validate + - 控制 Limit 并确认 AttributesToRetrieve 正确 +- 相关实现 + - [internal/config/meilisearch.go:14-51](file://internal/config/meilisearch.go#L14-L51) + - [internal/search/meilisearch.go:22-46](file://internal/search/meilisearch.go#L22-L46) + +**章节来源** +- [internal/config/meilisearch.go:14-51](file://internal/config/meilisearch.go#L14-L51) +- [internal/search/meilisearch.go:22-46](file://internal/search/meilisearch.go#L22-L46) + +### 四、日志分析 +- 症状 + - 无法定位错误来源或耗时异常 +- 诊断步骤 + 1) 区分日志级别:>=500 错误、>=400 警告、其他信息 + 2) 关注字段:status、method、path、latency、ip、request_id、errors + 3) 生产环境使用 ISO8601 时间与无色编码,便于机器解析 +- 解决策略 + - 将关键错误与 request_id 关联到追踪链路 + - 对高频错误增加采样或降级 +- 相关实现 + - [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) + - [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) + +**章节来源** +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) + +### 五、网络连接问题 +- 症状 + - 服务启动后无法访问或超时 +- 诊断步骤 + 1) 检查 APP_HOST/APP_PORT 与防火墙策略 + 2) 确认路由注册与路径正确 + 3) 使用 curl 或浏览器访问 /api/v1/health 验证 +- 解决策略 + - 生产环境建议绑定内网地址并经反向代理对外暴露 + - 为健康检查与业务接口分别设置超时 +- 相关实现 + - [internal/config/config.go:54-56](file://internal/config/config.go#L54-L56) + - [internal/router/router.go:27-38](file://internal/router/router.go#L27-L38) + - [README.md:83-99](file://README.md#L83-L99) + +**章节来源** +- [internal/config/config.go:54-56](file://internal/config/config.go#L54-L56) +- [internal/router/router.go:27-38](file://internal/router/router.go#L27-L38) +- [README.md:83-99](file://README.md#L83-L99) + +### 六、配置错误 +- 症状 + - 启动时报 invalid APP_PORT、database host is required 等 +- 诊断步骤 + 1) 检查环境变量类型与默认值 + 2) 确认 validate 返回的错误信息 +- 解决策略 + - 使用 .env 示例文件补齐缺失项 + - 生产环境敏感项不写入仓库 +- 相关实现 + - [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52) + - [internal/config/database.go:57-71](file://internal/config/database.go#L57-L71) + - [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) + +**章节来源** +- [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52) +- [internal/config/database.go:57-71](file://internal/config/database.go#L57-L71) +- [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) + +### 七、权限问题 +- 症状 + - 数据库/搜索/缓存连接被拒绝 +- 诊断步骤 + 1) 校验用户权限与白名单 + 2) 确认 API Key 与密码正确 + 3) 检查网络 ACL 与 VPC 策略 +- 解决策略 + - 最小权限原则分配账号 + - 生产环境使用只读账号用于查询 +- 相关实现 + - [internal/config/database.go:67-69](file://internal/config/database.go#L67-L69) + - [internal/config/meilisearch.go:43-48](file://internal/config/meilisearch.go#L43-L48) + - [internal/config/redis.go:52-55](file://internal/config/redis.go#L52-L55) + +**章节来源** +- [internal/config/database.go:67-69](file://internal/config/database.go#L67-L69) +- [internal/config/meilisearch.go:43-48](file://internal/config/meilisearch.go#L43-L48) +- [internal/config/redis.go:52-55](file://internal/config/redis.go#L52-L55) + +### 八、系统监控指标与告警 +- 指标建议 + - QPS、P95/P99 延迟、错误率(4xx/5xx)、数据库连接池使用率、Redis 命中率、搜索查询耗时 +- 告警策略 + - 错误率超过阈值、延迟持续升高、连接池耗尽、搜索/缓存不可用 +- 日志采集 + - 生产环境使用结构化日志,结合 request_id 做关联分析 + +[本节为通用指导,无需特定文件引用] + +### 九、应急响应与回滚 +- 优雅停机 + - 监听 SIGINT/SIGTERM,10 秒超时优雅关闭 HTTP 服务器 +- 回滚策略 + - 保留上一个版本二进制,变更配置文件即可快速回退 +- 数据恢复 + - 数据库:基于备份进行时间点恢复;搜索/缓存:重建索引或重载数据 + +**章节来源** +- [cmd/server/main.go:81-94](file://cmd/server/main.go#L81-L94) + +### 十、安全事件处理 +- 事件类型 + - 凭证泄露、未授权访问、DDoS、搜索/缓存被滥用 +- 处置流程 + - 立即冻结受影响账号与 API Key,变更密钥,审查日志,封禁来源 IP,升级防护策略 +- 预防措施 + - 强密码与多因子、最小权限、HTTPS、WAF、速率限制 + +[本节为通用指导,无需特定文件引用] + +## 结论 +通过规范的配置校验、连接池与超时控制、结构化日志与中间件链路,以及明确的应急流程,可以有效降低故障发生概率并缩短恢复时间。建议在生产环境中严格执行最小暴露与最小权限原则,并建立完善的监控与告警体系。 + +## 附录 + +### A. 错误代码对照表(统一响应) +- 200:操作成功(code=0) +- 400:请求参数错误(code=40000) +- 500:服务器内部错误(code=50000) + +**章节来源** +- [internal/response/response.go:15-37](file://internal/response/response.go#L15-L37) + +### B. 常见环境变量清单 +- 应用:APP_ENV、APP_HOST、APP_PORT、GIN_MODE +- 数据库:DATABASE_HOST、DATABASE_PORT、DATABASE_NAME、DATABASE_USER、DATABASE_PASSWORD +- 搜索:MEILISEARCH_HOST、MEILISEARCH_API_KEY、MEILISEARCH_INDEX +- 缓存:REDIS_HOST、REDIS_PORT、REDIS_PASSWORD、REDIS_DATABASE + +**章节来源** +- [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52) +- [internal/config/database.go:17-55](file://internal/config/database.go#L17-L55) +- [internal/config/meilisearch.go:14-21](file://internal/config/meilisearch.go#L14-L21) +- [internal/config/redis.go:16-49](file://internal/config/redis.go#L16-L49) + +### C. 健康检查与基本验证 +- 健康检查:GET /api/v1/health +- 响应:code=0,message="ok",data.status="up" + +**章节来源** +- [internal/router/router.go:27-30](file://internal/router/router.go#L27-L30) +- [internal/handler/health.go:14-19](file://internal/handler/health.go#L14-L19) +- [README.md:83-99](file://README.md#L83-L99) \ No newline at end of file diff --git a/.qoder/repowiki/zh/content/数据库设计.md b/.qoder/repowiki/zh/content/数据库设计.md new file mode 100644 index 0000000..fa0b7cd --- /dev/null +++ b/.qoder/repowiki/zh/content/数据库设计.md @@ -0,0 +1,403 @@ +# 数据库设计 + + +**本文引用的文件** +- [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) + + +## 目录 +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(字符串) + - Model:id(整数,主键)、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) \ No newline at end of file diff --git a/.qoder/repowiki/zh/content/核心模块/业务处理器.md b/.qoder/repowiki/zh/content/核心模块/业务处理器.md new file mode 100644 index 0000000..504095d --- /dev/null +++ b/.qoder/repowiki/zh/content/核心模块/业务处理器.md @@ -0,0 +1,457 @@ +# 业务处理器 + + +**本文引用的文件** +- [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/handler/device.go](file://internal/handler/device.go) +- [internal/handler/health.go](file://internal/handler/health.go) +- [internal/repository/brand.go](file://internal/repository/brand.go) +- [internal/repository/model.go](file://internal/repository/model.go) +- [internal/response/response.go](file://internal/response/response.go) +- [pkg/encode/base64.go](file://pkg/encode/base64.go) +- [internal/search/meilisearch.go](file://internal/search/meilisearch.go) +- [internal/router/router.go](file://internal/router/router.go) +- [cmd/server/main.go](file://cmd/server/main.go) +- [internal/config/config.go](file://internal/config/config.go) +- [internal/middleware/logger.go](file://internal/middleware/logger.go) +- [internal/middleware/cors.go](file://internal/middleware/cors.go) +- [internal/middleware/request_id.go](file://internal/middleware/request_id.go) +- [internal/model/brand.go](file://internal/model/brand.go) +- [internal/model/model.go](file://internal/model/model.go) + + +## 目录 +1. [简介](#简介) +2. [项目结构](#项目结构) +3. [核心组件](#核心组件) +4. [架构总览](#架构总览) +5. [详细组件分析](#详细组件分析) +6. [依赖分析](#依赖分析) +7. [性能考虑](#性能考虑) +8. [故障排查指南](#故障排查指南) +9. [结论](#结论) +10. [附录](#附录) + +## 简介 +本文件面向 Luxsin 应用 API 的业务处理器模块,系统性梳理各处理器的职责、接口设计与实现细节,覆盖 BrandHandler、ModelHandler、ModelListHandler、DeviceHandler 与 HealthHandler。文档同时阐述依赖注入模式、错误处理机制、响应格式化策略,以及与 Gin 框架的集成方式;并通过流程图与类图展示处理器间的协作关系与数据流转过程,帮助初学者快速上手,同时为高级开发者提供深入的技术参考。 + +## 项目结构 +业务处理器位于 internal/handler 目录,围绕“控制器-仓储-搜索-缓存-响应”的分层组织,配合中间件与路由装配,形成清晰的控制流与依赖注入入口。 + +```mermaid +graph TB +subgraph "应用入口" +MAIN["cmd/server/main.go
启动与配置加载"] +end +subgraph "路由与中间件" +ROUTER["internal/router/router.go
路由注册与中间件装配"] +CORS["internal/middleware/cors.go"] +LOGMW["internal/middleware/logger.go"] +REQID["internal/middleware/request_id.go"] +end +subgraph "业务处理器" +HEALTH["internal/handler/health.go"] +BRAND["internal/handler/brand.go"] +MODEL["internal/handler/model.go"] +MODELLIST["internal/handler/model_list.go"] +DEVICE["internal/handler/device.go"] +end +subgraph "基础设施" +RESP["internal/response/response.go
统一响应体"] +ENCODE["pkg/encode/base64.go
自定义Base64编码"] +SEARCH["internal/search/meilisearch.go
Meilisearch客户端"] +end +MAIN --> ROUTER +ROUTER --> CORS +ROUTER --> LOGMW +ROUTER --> REQID +ROUTER --> HEALTH +ROUTER --> BRAND +ROUTER --> MODEL +ROUTER --> MODELLIST +ROUTER --> DEVICE +BRAND --> RESP +MODEL --> RESP +MODELLIST --> RESP +DEVICE --> RESP +MODELLIST --> SEARCH +BRAND --> ENCODE +MODEL --> ENCODE +MODELLIST --> ENCODE +``` + +图表来源 +- [cmd/server/main.go:22-96](file://cmd/server/main.go#L22-L96) +- [internal/router/router.go:14-42](file://internal/router/router.go#L14-L42) +- [internal/handler/health.go:8-19](file://internal/handler/health.go#L8-L19) +- [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/handler/device.go:14-85](file://internal/handler/device.go#L14-L85) +- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37) +- [pkg/encode/base64.go:8-52](file://pkg/encode/base64.go#L8-L52) +- [internal/search/meilisearch.go:13-46](file://internal/search/meilisearch.go#L13-L46) + +章节来源 +- [internal/router/router.go:14-42](file://internal/router/router.go#L14-L42) +- [cmd/server/main.go:22-96](file://cmd/server/main.go#L22-L96) + +## 核心组件 +- 健康检查处理器:提供轻量级健康状态返回,便于外部探活与编排。 +- 品牌查询处理器:按品牌名模糊查询品牌列表,支持可选的自定义 Base64 响应。 +- 型号查询处理器:按品牌或型号关键字查询型号列表,支持可选的自定义 Base64 响应。 +- 型号检索处理器:基于 Meilisearch 执行全文检索,支持可选的自定义 Base64 响应。 +- 设备上报处理器:接收设备信息(MAC、型号、版本、来源 IP),写入 Redis Hash。 + +章节来源 +- [internal/handler/health.go:8-19](file://internal/handler/health.go#L8-L19) +- [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/handler/device.go:14-85](file://internal/handler/device.go#L14-L85) + +## 架构总览 +下图展示从请求进入至响应返回的关键路径,以及处理器与仓储、搜索、缓存、日志等组件的交互。 + +```mermaid +sequenceDiagram +participant C as "客户端" +participant R as "Gin 路由" +participant H as "业务处理器" +participant REPO as "仓储层" +participant S as "Meilisearch" +participant RC as "Redis" +participant L as "Zap 日志" +C->>R : "HTTP 请求" +R->>H : "匹配到处理器并调用" +alt "品牌/型号查询" +H->>REPO : "List(ctx, filters)" +REPO-->>H : "结果集" +else "型号检索" +H->>S : "SearchWithContext(key, opts)" +S-->>H : "命中结果" +else "设备上报" +H->>RC : "HSet(mac, json)" +RC-->>H : "OK 或错误" +end +H->>L : "记录日志/错误" +H-->>C : "JSON 或自定义Base64响应" +``` + +图表来源 +- [internal/router/router.go:27-38](file://internal/router/router.go#L27-L38) +- [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/handler/model_list.go:26-56](file://internal/handler/model_list.go#L26-L56) +- [internal/handler/device.go:26-84](file://internal/handler/device.go#L26-L84) +- [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) + +## 详细组件分析 + +### 健康检查处理器(HealthHandler) +- 职责:对外暴露健康检查端点,返回统一响应体中的状态字段。 +- 接口设计:无状态对象,构造函数仅初始化空实例。 +- 实现要点: + - 使用统一响应体封装返回值。 + - 适合被反向代理或编排系统定期探测。 +- 典型调用路径:/api/v1/health + +章节来源 +- [internal/handler/health.go:8-19](file://internal/handler/health.go#L8-L19) +- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37) +- [internal/router/router.go:29](file://internal/router/router.go#L29) + +### 品牌处理器(BrandHandler) +- 职责:根据品牌名称模糊查询品牌列表。 +- 输入参数: + - 查询字符串:brandName(可选) + - 查询字符串:base64Resp(可选,默认开启) +- 处理流程: + - 读取查询参数并解析 base64Resp。 + - 调用仓储层执行数据库查询。 + - 若开启 base64Resp,则对结果进行 JSON 编码后返回字符串;否则直接返回 JSON。 +- 错误处理: + - 仓储查询失败时记录错误并返回统一内部错误。 + - 编码失败时同样返回统一内部错误。 +- 数据模型:Brand + +```mermaid +classDiagram +class BrandHandler { +-repo : "BrandRepository" +-log : "zap.Logger" ++GetBrand(c) +} +class BrandRepository { +-db : "sql.DB" ++List(ctx, brandName) : "[]Brand,error" +} +class Brand { ++int id ++string name +} +BrandHandler --> BrandRepository : "依赖" +BrandRepository --> Brand : "返回" +``` + +图表来源 +- [internal/handler/brand.go:14-50](file://internal/handler/brand.go#L14-L50) +- [internal/repository/brand.go:12-50](file://internal/repository/brand.go#L12-L50) +- [internal/model/brand.go:3-7](file://internal/model/brand.go#L3-L7) + +章节来源 +- [internal/handler/brand.go:14-50](file://internal/handler/brand.go#L14-L50) +- [internal/repository/brand.go:20-50](file://internal/repository/brand.go#L20-L50) +- [internal/model/brand.go:3-7](file://internal/model/brand.go#L3-L7) + +### 型号处理器(ModelHandler) +- 职责:根据品牌或型号关键字查询型号列表。 +- 输入参数: + - 查询字符串:brandName(可选) + - 查询字符串:modelName(可选) + - 查询字符串:base64Resp(可选,默认开启) +- 处理流程: + - 读取查询参数并解析 base64Resp。 + - 调用仓储层执行数据库查询。 + - 结果处理与品牌处理器一致。 +- 错误处理: + - 仓储查询失败时记录错误并返回统一内部错误。 + - 编码失败时同样返回统一内部错误。 +- 数据模型:Model + +```mermaid +classDiagram +class ModelHandler { +-repo : "ModelRepository" +-log : "zap.Logger" ++GetModel(c) +} +class ModelRepository { +-db : "sql.DB" ++List(ctx, brandName, modelName) : "[]Model,error" +} +class Model { ++int id ++string brandName ++string name ++*string form ++*string rig ++*string source ++*string eqKey ++time createAt +} +ModelHandler --> ModelRepository : "依赖" +ModelRepository --> Model : "返回" +``` + +图表来源 +- [internal/handler/model.go:14-51](file://internal/handler/model.go#L14-L51) +- [internal/repository/model.go:12-61](file://internal/repository/model.go#L12-L61) +- [internal/model/model.go:5-15](file://internal/model/model.go#L5-L15) + +章节来源 +- [internal/handler/model.go:14-51](file://internal/handler/model.go#L14-L51) +- [internal/repository/model.go:20-61](file://internal/repository/model.go#L20-L61) +- [internal/model/model.go:5-15](file://internal/model/model.go#L5-L15) + +### 型号检索处理器(ModelListHandler) +- 职责:基于 Meilisearch 执行全文检索,返回匹配的型号元数据。 +- 输入参数: + - 查询字符串:key(必需) + - 查询字符串:count(可选,默认 100) + - 查询字符串:base64Resp(可选,默认开启) +- 处理流程: + - 读取查询参数并解析 base64Resp。 + - 限制 count 的最大值以避免过大的返回量。 + - 调用搜索客户端执行检索。 + - 结果处理与前两个处理器一致。 +- 错误处理: + - 检索失败时记录错误并返回统一内部错误。 + - 编码失败时同样返回统一内部错误。 +- 数据模型:map[string]any(由搜索结果解码而来) + +```mermaid +sequenceDiagram +participant C as "客户端" +participant ML as "ModelListHandler" +participant S as "Meilisearch 客户端" +participant E as "自定义Base64" +participant R as "统一响应" +C->>ML : "GET /audio/modelList?key=...&count=..." +ML->>ML : "解析参数与count限制" +ML->>S : "SearchWithContext(key, limit, attributes)" +S-->>ML : "hits" +ML->>E : "可选:EncodeJSON(hits)" +ML-->>C : "JSON 或 Base64(JSON)" +``` + +图表来源 +- [internal/handler/model_list.go:26-56](file://internal/handler/model_list.go#L26-L56) +- [internal/search/meilisearch.go:22-45](file://internal/search/meilisearch.go#L22-L45) +- [pkg/encode/base64.go:35-51](file://pkg/encode/base64.go#L35-L51) +- [internal/response/response.go:15-36](file://internal/response/response.go#L15-L36) + +章节来源 +- [internal/handler/model_list.go:14-57](file://internal/handler/model_list.go#L14-L57) +- [internal/search/meilisearch.go:22-45](file://internal/search/meilisearch.go#L22-L45) +- [pkg/encode/base64.go:35-51](file://pkg/encode/base64.go#L35-L51) + +### 设备上报处理器(DeviceHandler) +- 职责:接收设备上报信息(MAC、型号、版本、来源 IP),写入 Redis Hash。 +- 输入参数: + - 查询字符串:mac(必填) + - 查询字符串:model(必填) + - 查询字符串:ver(可选) +- 处理流程: + - 校验必填参数,若缺失则返回统一错误响应。 + - 组装设备信息结构体(含活跃日期、来源 IP、版本)。 + - 将结构体序列化为 JSON 并写入 Redis Hash。 + - 返回统一成功响应。 +- 错误处理: + - 参数校验失败返回统一错误。 + - JSON 序列化失败返回统一错误。 + - Redis 写入失败返回统一错误。 +- 并发与安全: + - Redis HSet 是原子操作,适合高并发场景。 + - 建议对 MAC 去除空白字符,避免重复键。 + +```mermaid +flowchart TD +Start(["进入 ReportDevInfo"]) --> ReadParams["读取参数 mac/model/ver/X-Forwarded-For"] +ReadParams --> Validate{"参数校验通过?"} +Validate -- 否 --> RespFail["返回统一错误响应"] +Validate -- 是 --> BuildInfo["组装设备信息结构体"] +BuildInfo --> Marshal{"JSON序列化成功?"} +Marshal -- 否 --> RespFail +Marshal -- 是 --> HSet["Redis HSet(mac, json)"] +HSet --> SetOK{"写入成功?"} +SetOK -- 否 --> RespFail +SetOK -- 是 --> RespOK["返回统一成功响应"] +RespFail --> End(["结束"]) +RespOK --> End +``` + +图表来源 +- [internal/handler/device.go:26-84](file://internal/handler/device.go#L26-L84) + +章节来源 +- [internal/handler/device.go:14-85](file://internal/handler/device.go#L14-L85) + +## 依赖分析 +- 依赖注入模式: + - 控制器通过构造函数注入仓储、搜索客户端、Redis 客户端与日志器。 + - 路由在应用启动时集中装配,保证依赖一次性构建与共享。 +- 组件耦合: + - 处理器与仓储之间为单向依赖,职责清晰。 + - 搜索与缓存作为外部服务,通过客户端封装接入。 +- 可能的循环依赖: + - 当前结构未见循环导入,符合 Go 包管理最佳实践。 + +```mermaid +graph LR +MAIN["main.go"] --> ROUTER["router.go"] +ROUTER --> HEALTH["health.go"] +ROUTER --> BRAND["brand.go"] +ROUTER --> MODEL["model.go"] +ROUTER --> MODELLIST["model_list.go"] +ROUTER --> DEVICE["device.go"] +BRAND --> BREPO["repository/brand.go"] +MODEL --> MREPO["repository/model.go"] +MODELLIST --> SEARCH["search/meilisearch.go"] +DEVICE --> REDIS["Redis 客户端"] +BRAND --> ENCODE["pkg/encode/base64.go"] +MODEL --> ENCODE +MODELLIST --> ENCODE +ALL["各处理器"] --> RESP["internal/response/response.go"] +ALL --> LOG["zap.Logger"] +``` + +图表来源 +- [cmd/server/main.go:64](file://cmd/server/main.go#L64) +- [internal/router/router.go:21-25](file://internal/router/router.go#L21-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/model_list.go:19-24](file://internal/handler/model_list.go#L19-L24) +- [internal/handler/device.go:19-24](file://internal/handler/device.go#L19-L24) +- [internal/repository/brand.go:16](file://internal/repository/brand.go#L16) +- [internal/repository/model.go:16](file://internal/repository/model.go#L16) +- [internal/search/meilisearch.go:17](file://internal/search/meilisearch.go#L17) +- [pkg/encode/base64.go:8](file://pkg/encode/base64.go#L8) +- [internal/response/response.go:9](file://internal/response/response.go#L9) + +章节来源 +- [internal/router/router.go:14-42](file://internal/router/router.go#L14-L42) +- [cmd/server/main.go:22-96](file://cmd/server/main.go#L22-L96) + +## 性能考虑 +- 响应体积优化 + - 对于大结果集,优先启用自定义 Base64 响应,减少传输体积与字符转义开销。 + - 检索处理器对 count 进行上限控制,避免超大数据量返回。 +- 数据库查询 + - 品牌与型号查询均使用 ORDER BY 与 LIKE,建议在数据库侧建立合适索引以提升模糊查询性能。 +- 搜索与缓存 + - Meilisearch 适合全文检索,建议合理设置属性检索范围与分页大小。 + - Redis 写入为单键 HSet,具备良好吞吐能力;建议评估内存占用与持久化策略。 +- 中间件与日志 + - 开启 Recovery、CORS、Logger、RequestID 中间件,有助于可观测性与稳定性;注意日志级别与输出频率对性能的影响。 +- 并发安全 + - Redis HSet 为线程安全操作;处理器方法本身无共享可变状态,天然并发安全。 +- 最佳实践 + - 在生产环境启用 Release 模式,降低框架开销。 + - 对外部依赖(数据库、搜索、缓存)增加超时与重试策略,提升鲁棒性。 + +## 故障排查指南 +- 健康检查失败 + - 确认路由已正确注册到 /api/v1/health。 + - 查看统一响应体是否返回状态字段。 +- 品牌/型号查询异常 + - 检查数据库连接与 SQL 查询逻辑。 + - 关注日志中“query brand”、“query model”的错误堆栈。 +- 型号检索异常 + - 检查 Meilisearch 配置与索引可用性。 + - 关注“meilisearch search”与“decode meilisearch hit”的错误。 +- 设备上报异常 + - 校验必填参数 mac 与 model 是否传入。 + - 关注 JSON 序列化与 Redis HSet 的错误日志。 +- 统一响应与错误码 + - 使用 internal/response/response.go 提供的 OK/Fail/BadRequest/InternalError 方法,确保错误码与消息格式一致。 +- 日志与追踪 + - 通过 RequestID 中间件串联一次请求的全链路日志,结合 Logger 中间件定位问题。 + +章节来源 +- [internal/handler/health.go:14-18](file://internal/handler/health.go#L14-L18) +- [internal/handler/brand.go:31-35](file://internal/handler/brand.go#L31-L35) +- [internal/handler/model.go:32-36](file://internal/handler/model.go#L32-L36) +- [internal/handler/model_list.go:38-42](file://internal/handler/model_list.go#L38-L42) +- [internal/handler/device.go:61-78](file://internal/handler/device.go#L61-L78) +- [internal/response/response.go:15-36](file://internal/response/response.go#L15-L36) +- [internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45) +- [internal/middleware/request_id.go:20-31](file://internal/middleware/request_id.go#L20-L31) + +## 结论 +本处理器模块遵循清晰的分层与依赖注入原则,通过统一响应体与自定义 Base64 编码实现灵活的输出策略,结合 Gin 中间件与外部服务(数据库、Meilisearch、Redis)形成稳定高效的业务处理链路。建议在生产环境中进一步完善超时与重试、索引优化与缓存策略,持续提升性能与可靠性。 + +## 附录 +- 路由与处理器映射 + - /api/v1/health -> HealthHandler.Check + - /audio/getBrand -> BrandHandler.GetBrand + - /audio/getModel -> ModelHandler.GetModel + - /audio/modelList -> ModelListHandler.ModelList + - /audio/reportDevInfo -> DeviceHandler.ReportDevInfo +- 常用调用示例(路径引用) + - 品牌查询:[internal/handler/brand.go:26](file://internal/handler/brand.go#L26) + - 型号查询:[internal/handler/model.go:26](file://internal/handler/model.go#L26) + - 型号检索:[internal/handler/model_list.go:26](file://internal/handler/model_list.go#L26) + - 设备上报:[internal/handler/device.go:26](file://internal/handler/device.go#L26) + - 健康检查:[internal/handler/health.go:14](file://internal/handler/health.go#L14) +- 配置与启动 + - 配置加载与环境变量:[internal/config/config.go:18-64](file://internal/config/config.go#L18-L64) + - 服务器启动与优雅关闭:[cmd/server/main.go:22-96](file://cmd/server/main.go#L22-L96) +- 中间件 + - CORS:[internal/middleware/cors.go:7-21](file://internal/middleware/cors.go#L7-L21) + - Logger:[internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45) + - RequestID:[internal/middleware/request_id.go:20-31](file://internal/middleware/request_id.go#L20-L31) \ No newline at end of file diff --git a/.qoder/repowiki/zh/content/核心模块/数据模型.md b/.qoder/repowiki/zh/content/核心模块/数据模型.md new file mode 100644 index 0000000..ff1c153 --- /dev/null +++ b/.qoder/repowiki/zh/content/核心模块/数据模型.md @@ -0,0 +1,466 @@ +# 数据模型 + + +**本文引用的文件** +- [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) + + +## 目录 +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 依赖 Repository;Repository 依赖 sql.DB;Model 为纯数据结构。 + - 统一响应体与编码层被 Handler 调用,降低重复逻辑。 +- 外部依赖 + - 数据库:MySQL,通过 sql.DB 访问。 + - 日志:zap。 + - Web 框架:Gin。 + - 搜索:Meilisearch(ModelListHandler)。 + - 缓存:Redis(Device 相关处理器)。 +- 潜在循环依赖 + - 当前结构清晰,无循环导入迹象。 + +```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 编码;结合分页与缓存策略。 +- 错误处理与可观测性 + - 统一错误码与消息格式;记录关键链路日志;对数据库与外部服务增加超时与重试。 \ No newline at end of file diff --git a/.qoder/repowiki/zh/content/核心模块/数据访问层.md b/.qoder/repowiki/zh/content/核心模块/数据访问层.md new file mode 100644 index 0000000..e6c70d9 --- /dev/null +++ b/.qoder/repowiki/zh/content/核心模块/数据访问层.md @@ -0,0 +1,374 @@ +# 数据访问层 + + +**本文引用的文件** +- [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) + + +## 目录 +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
启动服务与依赖初始化"] +end +subgraph "路由与控制器" +ROUTER["internal/router/router.go
注册路由与中间件"] +BRAND_H["internal/handler/brand.go
品牌处理器"] +MODEL_H["internal/handler/model.go
型号处理器"] +end +subgraph "数据访问层" +BR_REPO["internal/repository/brand.go
BrandRepository"] +MD_REPO["internal/repository/model.go
ModelRepository"] +end +subgraph "数据库与配置" +DB_SQL["internal/database/mysql.go
sql.DB 连接与池配置"] +CFG_DB["internal/config/database.go
数据库配置加载"] +CFG_APP["internal/config/config.go
应用配置聚合"] +MODEL_TBL["sql/model.sql
表结构定义"] +end +subgraph "领域模型" +M_BRAND["internal/model/brand.go
Brand 模型"] +M_MODEL["internal/model/model.go
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) \ No newline at end of file diff --git a/.qoder/repowiki/zh/content/核心模块/核心模块.md b/.qoder/repowiki/zh/content/核心模块/核心模块.md new file mode 100644 index 0000000..4c6e25d --- /dev/null +++ b/.qoder/repowiki/zh/content/核心模块/核心模块.md @@ -0,0 +1,513 @@ +# 核心模块 + + +**本文引用的文件** +- [cmd/server/main.go](file://cmd/server/main.go) +- [internal/router/router.go](file://internal/router/router.go) +- [internal/config/config.go](file://internal/config/config.go) +- [internal/database/mysql.go](file://internal/database/mysql.go) +- [internal/cache/redis.go](file://internal/cache/redis.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/handler/device.go](file://internal/handler/device.go) +- [internal/handler/health.go](file://internal/handler/health.go) +- [internal/repository/brand.go](file://internal/repository/brand.go) +- [internal/repository/model.go](file://internal/repository/model.go) +- [internal/model/brand.go](file://internal/model/brand.go) +- [internal/model/model.go](file://internal/model/model.go) +- [internal/response/response.go](file://internal/response/response.go) +- [internal/search/meilisearch.go](file://internal/search/meilisearch.go) +- [pkg/encode/base64.go](file://pkg/encode/base64.go) +- [pkg/logger/logger.go](file://pkg/logger/logger.go) + + +## 目录 +1. [简介](#简介) +2. [项目结构](#项目结构) +3. [核心组件](#核心组件) +4. [架构总览](#架构总览) +5. [详细组件分析](#详细组件分析) +6. [依赖分析](#依赖分析) +7. [性能考量](#性能考量) +8. [故障排查指南](#故障排查指南) +9. [结论](#结论) +10. [附录](#附录) + +## 简介 +本文件围绕 Luxsin 应用 API 的核心模块进行系统化文档化,重点覆盖以下方面: +- 业务处理器(Handler):负责接收请求、解析参数、调用仓库层、封装统一响应。 +- 数据访问层(Repository):负责与数据库交互,执行查询与扫描逻辑。 +- 数据模型(Model):定义持久化与对外传输的数据结构。 +- 统一响应(Response):规范 HTTP 响应体格式,提供便捷的构造方法。 +- 依赖注入与启动流程:从配置加载到服务启动、中间件装配、路由注册与资源管理。 +- 错误处理与日志记录:在各层中的一致性错误处理与可观测性。 +- 扩展最佳实践:如何新增模块、如何复用编码器、如何接入缓存与搜索引擎。 +- 性能优化与并发安全:连接池、超时控制、上下文传播、Redis 并发安全。 + +## 项目结构 +项目采用分层与按功能域组织的结构: +- cmd/server:应用入口,负责配置加载、外部依赖初始化、HTTP 服务器启动与优雅关闭。 +- internal/config:集中式配置加载与校验。 +- internal/database:数据库连接与连接池配置。 +- internal/cache:Redis 客户端初始化。 +- internal/search:搜索引擎客户端封装。 +- internal/router:路由注册与中间件装配。 +- internal/handler:业务处理器,面向具体 API 路由。 +- internal/repository:数据访问层,封装 SQL 查询。 +- internal/model:领域模型与传输模型。 +- internal/response:统一响应体封装。 +- pkg/*:通用工具包(如编码器、日志)。 + +```mermaid +graph TB +subgraph "入口与配置" +MAIN["cmd/server/main.go
应用入口"] +CFG["internal/config/config.go
配置加载"] +end +subgraph "基础设施" +DB["internal/database/mysql.go
MySQL 连接与池"] +RDS["internal/cache/redis.go
Redis 客户端"] +SRCH["internal/search/meilisearch.go
搜索引擎客户端"] +LOG["pkg/logger/logger.go
日志"] +end +subgraph "服务编排" +RT["internal/router/router.go
路由与中间件"] +end +subgraph "业务层" +H_BRAND["internal/handler/brand.go
品牌处理器"] +H_MODEL["internal/handler/model.go
型号处理器"] +H_MLIST["internal/handler/model_list.go
型号搜索处理器"] +H_DEV["internal/handler/device.go
设备上报处理器"] +H_HEALTH["internal/handler/health.go
健康检查处理器"] +end +subgraph "数据访问与模型" +REPO_BRAND["internal/repository/brand.go
品牌仓库"] +REPO_MODEL["internal/repository/model.go
型号仓库"] +MODEL_BRAND["internal/model/brand.go
品牌模型"] +MODEL_MODEL["internal/model/model.go
型号模型"] +RESP["internal/response/response.go
统一响应"] +ENC["pkg/encode/base64.go
Base64 编码器"] +end +MAIN --> CFG +MAIN --> DB +MAIN --> RDS +MAIN --> SRCH +MAIN --> LOG +MAIN --> RT +RT --> H_HEALTH +RT --> H_BRAND +RT --> H_MODEL +RT --> H_MLIST +RT --> H_DEV +H_BRAND --> REPO_BRAND +H_MODEL --> REPO_MODEL +H_MLIST --> SRCH +H_DEV --> RDS +REPO_BRAND --> DB +REPO_MODEL --> DB +H_BRAND --> RESP +H_MODEL --> RESP +H_MLIST --> RESP +H_HEALTH --> RESP +H_BRAND --> ENC +H_MODEL --> ENC +H_MLIST --> ENC +``` + +图表来源 +- [cmd/server/main.go:22-95](file://cmd/server/main.go#L22-L95) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) +- [internal/config/config.go:18-64](file://internal/config/config.go#L18-L64) +- [internal/database/mysql.go:14-46](file://internal/database/mysql.go#L14-L46) +- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) +- [internal/handler/brand.go:19-49](file://internal/handler/brand.go#L19-L49) +- [internal/handler/model.go:19-50](file://internal/handler/model.go#L19-L50) +- [internal/handler/model_list.go:19-56](file://internal/handler/model_list.go#L19-L56) +- [internal/handler/device.go:19-84](file://internal/handler/device.go#L19-L84) +- [internal/handler/health.go:10-18](file://internal/handler/health.go#L10-L18) +- [internal/repository/brand.go:16-50](file://internal/repository/brand.go#L16-L50) +- [internal/repository/model.go:16-94](file://internal/repository/model.go#L16-L94) +- [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/response/response.go:9-36](file://internal/response/response.go#L9-L36) +- [pkg/encode/base64.go](file://pkg/encode/base64.go) + +章节来源 +- [cmd/server/main.go:22-95](file://cmd/server/main.go#L22-L95) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) + +## 核心组件 +本节聚焦 Handler、Repository、Model、Response 四大核心模块的职责、协作方式与实现要点。 + +- Handler(业务处理器) + - 职责:解析请求参数、调用仓库或外部服务、封装统一响应;必要时进行参数校验与基础编码处理。 + - 典型实现位置: + - 品牌列表:[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/handler/model_list.go:26-56](file://internal/handler/model_list.go#L26-L56) + - 设备上报:[internal/handler/device.go:26-84](file://internal/handler/device.go#L26-L84) + - 健康检查:[internal/handler/health.go:14-18](file://internal/handler/health.go#L14-L18) + +- Repository(数据访问层) + - 职责:封装 SQL 查询、参数拼装、结果扫描与错误包装;支持上下文传播与超时控制。 + - 典型实现位置: + - 品牌仓库:[internal/repository/brand.go:20-50](file://internal/repository/brand.go#L20-L50) + - 型号仓库:[internal/repository/model.go:20-94](file://internal/repository/model.go#L20-L94) + +- Model(数据模型) + - 职责:定义数据库字段映射与 JSON 序列化字段名;可选字段使用指针以区分空值与缺省。 + - 典型实现位置: + - 品牌模型:[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) + +- Response(统一响应) + - 职责:统一响应体结构与常用状态构造方法,保证前后端契约一致。 + - 典型实现位置: + - 统一响应:[internal/response/response.go:9-36](file://internal/response/response.go#L9-L36) + +章节来源 +- [internal/handler/brand.go:19-49](file://internal/handler/brand.go#L19-L49) +- [internal/handler/model.go:19-50](file://internal/handler/model.go#L19-L50) +- [internal/handler/model_list.go:19-56](file://internal/handler/model_list.go#L19-L56) +- [internal/handler/device.go:19-84](file://internal/handler/device.go#L19-L84) +- [internal/handler/health.go:10-18](file://internal/handler/health.go#L10-L18) +- [internal/repository/brand.go:16-50](file://internal/repository/brand.go#L16-L50) +- [internal/repository/model.go:16-94](file://internal/repository/model.go#L16-L94) +- [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/response/response.go:9-36](file://internal/response/response.go#L9-L36) + +## 架构总览 +下图展示从请求进入至响应返回的关键路径,以及各层之间的依赖关系与调用方向。 + +```mermaid +sequenceDiagram +participant C as "客户端" +participant G as "Gin 路由
internal/router/router.go" +participant H as "业务处理器
internal/handler/*" +participant R as "仓库层
internal/repository/*" +participant DB as "数据库
internal/database/mysql.go" +participant S as "搜索引擎
internal/search/meilisearch.go" +participant RC as "Redis
internal/cache/redis.go" +participant RESP as "统一响应
internal/response/response.go" +C->>G : "HTTP 请求" +G->>H : "匹配路由并调用处理器" +alt "品牌/型号列表" +H->>R : "调用仓库查询" +R->>DB : "执行 SQL 查询" +DB-->>R : "返回结果集" +R-->>H : "返回模型切片" +else "型号搜索" +H->>S : "调用搜索引擎" +S-->>H : "返回搜索结果" +else "设备上报" +H->>RC : "写入 Redis Hash" +RC-->>H : "返回写入结果" +end +H->>RESP : "构造统一响应" +RESP-->>C : "HTTP 响应" +``` + +图表来源 +- [internal/router/router.go:21-38](file://internal/router/router.go#L21-L38) +- [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/handler/model_list.go:37-42](file://internal/handler/model_list.go#L37-L42) +- [internal/handler/device.go:70-78](file://internal/handler/device.go#L70-L78) +- [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/response/response.go:15-21](file://internal/response/response.go#L15-L21) + +## 详细组件分析 + +### Handler 层 +- 品牌处理器(BrandHandler) + - 关键点:读取查询参数、调用仓库、错误日志与统一错误响应、可选 Base64 响应。 + - 参考路径:[internal/handler/brand.go:26-49](file://internal/handler/brand.go#L26-L49) +- 型号处理器(ModelHandler) + - 关键点:多条件查询(品牌/名称模糊)、上下文传播、统一响应与错误处理。 + - 参考路径:[internal/handler/model.go:26-50](file://internal/handler/model.go#L26-L50) +- 型号搜索处理器(ModelListHandler) + - 关键点:搜索引擎客户端调用、count 参数限制、统一响应。 + - 参考路径:[internal/handler/model_list.go:26-56](file://internal/handler/model_list.go#L26-L56) +- 设备上报处理器(DeviceHandler) + - 关键点:参数校验、日志记录、Redis Hash 写入、错误处理。 + - 参考路径:[internal/handler/device.go:26-84](file://internal/handler/device.go#L26-L84) +- 健康检查处理器(HealthHandler) + - 关键点:简单响应,使用统一响应体。 + - 参考路径:[internal/handler/health.go:14-18](file://internal/handler/health.go#L14-L18) + +```mermaid +classDiagram +class BrandHandler { +-repo : BrandRepository +-log : Logger ++GetBrand(c) +} +class ModelHandler { +-repo : ModelRepository +-log : Logger ++GetModel(c) +} +class ModelListHandler { +-search : SearchClient +-log : Logger ++ModelList(c) +} +class DeviceHandler { +-redis : RedisClient +-log : Logger ++ReportDevInfo(c) +} +class HealthHandler { ++Check(c) +} +BrandHandler --> BrandRepository : "依赖" +ModelHandler --> ModelRepository : "依赖" +ModelListHandler --> SearchClient : "依赖" +DeviceHandler --> RedisClient : "依赖" +``` + +图表来源 +- [internal/handler/brand.go:14-24](file://internal/handler/brand.go#L14-L24) +- [internal/handler/model.go:14-24](file://internal/handler/model.go#L14-L24) +- [internal/handler/model_list.go:14-24](file://internal/handler/model_list.go#L14-L24) +- [internal/handler/device.go:14-24](file://internal/handler/device.go#L14-L24) +- [internal/handler/health.go:8-12](file://internal/handler/health.go#L8-L12) + +章节来源 +- [internal/handler/brand.go:19-49](file://internal/handler/brand.go#L19-L49) +- [internal/handler/model.go:19-50](file://internal/handler/model.go#L19-L50) +- [internal/handler/model_list.go:19-56](file://internal/handler/model_list.go#L19-L56) +- [internal/handler/device.go:19-84](file://internal/handler/device.go#L19-L84) +- [internal/handler/health.go:10-18](file://internal/handler/health.go#L10-L18) + +### Repository 层 +- 品牌仓库(BrandRepository) + - 关键点:条件查询、排序、结果扫描。 + - 参考路径:[internal/repository/brand.go:20-50](file://internal/repository/brand.go#L20-L50) +- 型号仓库(ModelRepository) + - 关键点:多条件拼装、LIKE 模糊匹配、NullString 处理、上下文传播。 + - 参考路径:[internal/repository/model.go:20-94](file://internal/repository/model.go#L20-L94) + +```mermaid +classDiagram +class BrandRepository { +-db : sql.DB ++List(ctx, brandName) []Brand +} +class ModelRepository { +-db : sql.DB ++List(ctx, brandName, modelName) []Model +} +class Brand { ++id : int ++name : string +} +class Model { ++id : int ++brandName : string ++name : string ++form : *string ++rig : *string ++source : *string ++eqKey : *string ++createAt : time +} +BrandRepository --> Brand : "返回" +ModelRepository --> Model : "返回" +``` + +图表来源 +- [internal/repository/brand.go:12-50](file://internal/repository/brand.go#L12-L50) +- [internal/repository/model.go:12-94](file://internal/repository/model.go#L12-L94) +- [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:16-50](file://internal/repository/brand.go#L16-L50) +- [internal/repository/model.go:16-94](file://internal/repository/model.go#L16-L94) +- [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) + +### Model 层 +- 品牌模型(Brand) + - 字段:id、name + - 参考路径:[internal/model/brand.go:3-6](file://internal/model/brand.go#L3-L6) +- 型号模型(Model) + - 字段:id、brandName、name、form、rig、source、eqKey、createAt(可选字段使用指针) + - 参考路径:[internal/model/model.go:5-14](file://internal/model/model.go#L5-L14) + +章节来源 +- [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) + +### Response 层 +- 统一响应体(Body) + - 字段:code、message、data + - 方法:OK、Fail、BadRequest、InternalError + - 参考路径:[internal/response/response.go:9-36](file://internal/response/response.go#L9-L36) + +```mermaid +flowchart TD +Start(["进入处理器"]) --> Build["构造响应体
OK/Fail/BadRequest/InternalError"] +Build --> Send["通过 Gin 写入 HTTP 响应"] +Send --> End(["完成"]) +``` + +图表来源 +- [internal/response/response.go:15-36](file://internal/response/response.go#L15-L36) + +章节来源 +- [internal/response/response.go:9-36](file://internal/response/response.go#L9-L36) + +### 编码与日志 +- Base64 编码器(pkg/encode/base64.go) + - 用途:在处理器中对响应进行 Base64 编码后输出字符串。 + - 使用场景:品牌与型号列表的可选 Base64 输出。 + - 参考路径:[internal/handler/brand.go:37-44](file://internal/handler/brand.go#L37-L44),[internal/handler/model.go:38-47](file://internal/handler/model.go#L38-L47),[internal/handler/model_list.go:44-53](file://internal/handler/model_list.go#L44-L53) +- 日志(pkg/logger/logger.go) + - 用途:全局日志初始化与结构化日志记录。 + - 参考路径:[cmd/server/main.go:32-36](file://cmd/server/main.go#L32-L36),[internal/handler/brand.go:32-34](file://internal/handler/brand.go#L32-L34),[internal/handler/model.go:33-35](file://internal/handler/model.go#L33-L35),[internal/handler/model_list.go:39-41](file://internal/handler/model_list.go#L39-L41),[internal/handler/device.go:47-49](file://internal/handler/device.go#L47-L49),[internal/handler/device.go:71-77](file://internal/handler/device.go#L71-L77) + +章节来源 +- [internal/handler/brand.go:37-44](file://internal/handler/brand.go#L37-L44) +- [internal/handler/model.go:38-47](file://internal/handler/model.go#L38-L47) +- [internal/handler/model_list.go:44-53](file://internal/handler/model_list.go#L44-L53) +- [cmd/server/main.go:32-36](file://cmd/server/main.go#L32-L36) +- [internal/handler/brand.go:32-34](file://internal/handler/brand.go#L32-L34) +- [internal/handler/model.go:33-35](file://internal/handler/model.go#L33-L35) +- [internal/handler/model_list.go:39-41](file://internal/handler/model_list.go#L39-L41) +- [internal/handler/device.go:47-49](file://internal/handler/device.go#L47-L49) +- [internal/handler/device.go:71-77](file://internal/handler/device.go#L71-L77) + +## 依赖分析 +- 启动与依赖注入 + - 配置加载:[internal/config/config.go:18-64](file://internal/config/config.go#L18-L64) + - 数据库连接与池:[internal/database/mysql.go:14-46](file://internal/database/mysql.go#L14-L46) + - Redis 客户端:[internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) + - 路由与中间件:[internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) + - 入口程序:[cmd/server/main.go:22-95](file://cmd/server/main.go#L22-L95) + +- 处理器与仓库的依赖 + - 品牌处理器依赖品牌仓库:[internal/handler/brand.go:19-23](file://internal/handler/brand.go#L19-L23) → [internal/repository/brand.go:16](file://internal/repository/brand.go#L16) + - 型号处理器依赖型号仓库:[internal/handler/model.go:19-23](file://internal/handler/model.go#L19-L23) → [internal/repository/model.go:16](file://internal/repository/model.go#L16) + - 型号搜索处理器依赖搜索引擎客户端:[internal/handler/model_list.go:19-23](file://internal/handler/model_list.go#L19-L23) → [internal/search/meilisearch.go](file://internal/search/meilisearch.go) + - 设备上报处理器依赖 Redis 客户端:[internal/handler/device.go:19-23](file://internal/handler/device.go#L19-L23) → [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) + +```mermaid +graph LR +MAIN["cmd/server/main.go"] --> CFG["internal/config/config.go"] +MAIN --> DB["internal/database/mysql.go"] +MAIN --> RDS["internal/cache/redis.go"] +MAIN --> RT["internal/router/router.go"] +RT --> H_BRAND["internal/handler/brand.go"] +RT --> H_MODEL["internal/handler/model.go"] +RT --> H_MLIST["internal/handler/model_list.go"] +RT --> H_DEV["internal/handler/device.go"] +H_BRAND --> REPO_BRAND["internal/repository/brand.go"] +H_MODEL --> REPO_MODEL["internal/repository/model.go"] +H_MLIST --> SRCH["internal/search/meilisearch.go"] +H_DEV --> RDS +REPO_BRAND --> DB +REPO_MODEL --> DB +``` + +图表来源 +- [cmd/server/main.go:22-95](file://cmd/server/main.go#L22-L95) +- [internal/router/router.go:21-38](file://internal/router/router.go#L21-L38) +- [internal/handler/brand.go:19-23](file://internal/handler/brand.go#L19-L23) +- [internal/handler/model.go:19-23](file://internal/handler/model.go#L19-L23) +- [internal/handler/model_list.go:19-23](file://internal/handler/model_list.go#L19-L23) +- [internal/handler/device.go:19-23](file://internal/handler/device.go#L19-L23) +- [internal/repository/brand.go:16](file://internal/repository/brand.go#L16) +- [internal/repository/model.go:16](file://internal/repository/model.go#L16) +- [internal/database/mysql.go:14-46](file://internal/database/mysql.go#L14-L46) +- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) + +章节来源 +- [cmd/server/main.go:22-95](file://cmd/server/main.go#L22-L95) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) +- [internal/handler/brand.go:19-23](file://internal/handler/brand.go#L19-L23) +- [internal/handler/model.go:19-23](file://internal/handler/model.go#L19-L23) +- [internal/handler/model_list.go:19-23](file://internal/handler/model_list.go#L19-L23) +- [internal/handler/device.go:19-23](file://internal/handler/device.go#L19-L23) +- [internal/repository/brand.go:16](file://internal/repository/brand.go#L16) +- [internal/repository/model.go:16](file://internal/repository/model.go#L16) +- [internal/database/mysql.go:14-46](file://internal/database/mysql.go#L14-L46) +- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) + +## 性能考量 +- 数据库连接与池 + - 最大并发连接数、最大空闲连接数、连接生命周期设置,有助于避免连接争用与资源泄漏。 + - 参考路径:[internal/database/mysql.go:33-35](file://internal/database/mysql.go#L33-L35) +- 上下文与超时 + - 仓库层使用 QueryContext 与 PingContext,确保超时控制与取消传播。 + - 参考路径:[internal/repository/model.go:42-46](file://internal/repository/model.go#L42-L46),[internal/database/mysql.go:37-43](file://internal/database/mysql.go#L37-L43) +- Redis 并发安全 + - 单个 Redis 客户端实例在 Go 中是并发安全的,可在多个 goroutine 中共享。 + - 参考路径:[internal/handler/device.go:70-78](file://internal/handler/device.go#L70-L78) +- 响应体积与 Base64 + - 对于大列表响应,可启用 Base64 输出以减少传输体积,但会增加 CPU 开销。 + - 参考路径:[internal/handler/brand.go:37-44](file://internal/handler/branch.go#L37-L44),[internal/handler/model.go:38-47](file://internal/handler/model.go#L38-L47),[internal/handler/model_list.go:44-53](file://internal/handler/model_list.go#L44-L53) +- 搜索性能 + - 搜索结果数量限制(count)可有效控制响应规模,避免过量数据传输。 + - 参考路径:[internal/handler/model_list.go:30-35](file://internal/handler/model_list.go#L30-L35) + +章节来源 +- [internal/database/mysql.go:33-35](file://internal/database/mysql.go#L33-L35) +- [internal/repository/model.go:42-46](file://internal/repository/model.go#L42-L46) +- [internal/database/mysql.go:37-43](file://internal/database/mysql.go#L37-L43) +- [internal/handler/device.go:70-78](file://internal/handler/device.go#L70-L78) +- [internal/handler/brand.go:37-44](file://internal/handler/brand.go#L37-L44) +- [internal/handler/model.go:38-47](file://internal/handler/model.go#L38-L47) +- [internal/handler/model_list.go:30-35](file://internal/handler/model_list.go#L30-L35) +- [internal/handler/model_list.go:44-53](file://internal/handler/model_list.go#L44-L53) + +## 故障排查指南 +- 常见错误类型与处理 + - 数据库查询失败:在仓库层包装错误并在处理器记录日志与返回统一错误响应。 + - 参考路径:[internal/repository/model.go:43-44](file://internal/repository/model.go#L43-L44),[internal/handler/model.go:33-35](file://internal/handler/model.go#L33-L35) + - 编码失败:在处理器捕获编码错误并返回统一错误响应。 + - 参考路径:[internal/handler/brand.go:40-43](file://internal/handler/brand.go#L40-L43),[internal/handler/model.go:40-43](file://internal/handler/model.go#L40-L43) + - Redis 写入失败:记录错误并返回统一错误响应。 + - 参考路径:[internal/handler/device.go:71-77](file://internal/handler/device.go#L71-L77) + - 搜索失败:记录错误并返回统一错误响应。 + - 参考路径:[internal/handler/model_list.go:39-41](file://internal/handler/model_list.go#L39-L41) +- 日志定位 + - 使用结构化日志记录关键上下文(如远程 IP、时间戳),便于问题追踪。 + - 参考路径:[internal/handler/device.go:47-50](file://internal/handler/device.go#L47-L50) + +章节来源 +- [internal/repository/model.go:43-44](file://internal/repository/model.go#L43-L44) +- [internal/handler/model.go:33-35](file://internal/handler/model.go#L33-L35) +- [internal/handler/brand.go:40-43](file://internal/handler/brand.go#L40-L43) +- [internal/handler/model.go:40-43](file://internal/handler/model.go#L40-L43) +- [internal/handler/device.go:71-77](file://internal/handler/device.go#L71-L77) +- [internal/handler/model_list.go:39-41](file://internal/handler/model_list.go#L39-L41) +- [internal/handler/device.go:47-50](file://internal/handler/device.go#L47-L50) + +## 结论 +本项目通过清晰的分层与职责分离,实现了可维护、可扩展且具备良好性能特征的 API 服务: +- Handler 专注于业务编排与响应封装; +- Repository 将数据访问细节抽象化; +- Model 明确数据契约; +- Response 提供统一的对外接口; +- 配合中间件、日志与统一错误处理,形成完整的可观测与可诊断体系; +- 在数据库连接池、上下文超时、Redis 并发安全与可选 Base64 响应等方面体现了工程化细节。 + +## 附录 +- 扩展新模块的最佳实践 + - 新增处理器:在 internal/handler 下创建处理器文件,定义结构体与依赖注入函数,实现业务方法并调用仓库或外部服务。 + - 参考路径:[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 下创建仓库文件,实现查询方法、参数拼装与结果扫描,使用上下文与错误包装。 + - 参考路径:[internal/repository/brand.go:20-50](file://internal/repository/brand.go#L20-L50),[internal/repository/model.go:20-94](file://internal/repository/model.go#L20-L94) + - 新增模型:在 internal/model 下定义结构体,注意可选字段使用指针;在仓库扫描函数中正确映射 NullString。 + - 参考路径:[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/model.go:63-86](file://internal/repository/model.go#L63-L86) + - 统一响应:优先使用 internal/response 提供的方法,保持前后端一致性。 + - 参考路径:[internal/response/response.go:15-36](file://internal/response/response.go#L15-L36) + - 参数与编码:利用 pkg/encode/base64.go 实现可选的 Base64 响应;在处理器中进行必要的参数校验与日志记录。 + - 参考路径:[internal/handler/brand.go:27-29](file://internal/handler/brand.go#L27-L29),[internal/handler/model.go:27-29](file://internal/handler/model.go#L27-L29),[internal/handler/model_list.go:26-28](file://internal/handler/model_list.go#L26-L28),[pkg/encode/base64.go](file://pkg/encode/base64.go) + - 资源管理:在入口程序中集中初始化外部依赖(数据库、Redis、搜索引擎),并在退出时优雅关闭。 + - 参考路径:[cmd/server/main.go:38-62](file://cmd/server/main.go#L38-L62),[cmd/server/main.go:87-94](file://cmd/server/main.go#L87-L94) \ No newline at end of file diff --git a/.qoder/repowiki/zh/content/核心模块/统一响应.md b/.qoder/repowiki/zh/content/核心模块/统一响应.md new file mode 100644 index 0000000..9cfea3a --- /dev/null +++ b/.qoder/repowiki/zh/content/核心模块/统一响应.md @@ -0,0 +1,366 @@ +# 统一响应 + + +**本文引用的文件** +- [internal/response/response.go](file://internal/response/response.go) +- [internal/handler/health.go](file://internal/handler/health.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) +- [internal/middleware/logger.go](file://internal/middleware/logger.go) +- [internal/middleware/request_id.go](file://internal/middleware/request_id.go) +- [internal/middleware/cors.go](file://internal/middleware/cors.go) +- [pkg/encode/base64.go](file://pkg/encode/base64.go) +- [pkg/logger/logger.go](file://pkg/logger/logger.go) +- [cmd/server/main.go](file://cmd/server/main.go) +- [README.md](file://README.md) + + +## 目录 +1. [简介](#简介) +2. [项目结构](#项目结构) +3. [核心组件](#核心组件) +4. [架构总览](#架构总览) +5. [详细组件分析](#详细组件分析) +6. [依赖分析](#依赖分析) +7. [性能考虑](#性能考虑) +8. [故障排查指南](#故障排查指南) +9. [结论](#结论) +10. [附录](#附录) + +## 简介 +本文件系统化阐述 Luxsin 应用 API 的“统一响应”模块,围绕响应格式设计原则、实现机制与使用规范展开,覆盖响应结构、状态码策略、错误处理、国际化与调试信息、扩展与自定义选项、性能优化与最佳实践。目标是帮助初学者快速理解统一响应在 API 设计中的一致性价值,同时为高级开发者提供实现细节与扩展路径。 + +## 项目结构 +统一响应模块位于 internal/response,配合各 handler 在业务层统一输出 JSON 结构;路由与中间件负责请求生命周期管理与日志记录;编码工具提供可选的 Base64 响应能力;日志与服务器入口负责运行时配置与生命周期控制。 + +```mermaid +graph TB +subgraph "应用入口" +MAIN["cmd/server/main.go"] +end +subgraph "HTTP 层" +ROUTER["internal/router/router.go"] +M_REQID["internal/middleware/request_id.go"] +M_LOG["internal/middleware/logger.go"] +M_CORS["internal/middleware/cors.go"] +end +subgraph "业务层" +HANDLER_HEALTH["internal/handler/health.go"] +HANDLER_BRAND["internal/handler/brand.go"] +HANDLER_MODEL["internal/handler/model.go"] +end +subgraph "通用能力" +RESP["internal/response/response.go"] +ENCODE["pkg/encode/base64.go"] +LOGPKG["pkg/logger/logger.go"] +end +MAIN --> ROUTER +ROUTER --> M_REQID +ROUTER --> M_LOG +ROUTER --> M_CORS +ROUTER --> HANDLER_HEALTH +ROUTER --> HANDLER_BRAND +ROUTER --> HANDLER_MODEL +HANDLER_HEALTH --> RESP +HANDLER_BRAND --> RESP +HANDLER_MODEL --> RESP +HANDLER_BRAND --> ENCODE +HANDLER_MODEL --> ENCODE +MAIN --> LOGPKG +``` + +图表来源 +- [cmd/server/main.go:22-96](file://cmd/server/main.go#L22-L96) +- [internal/router/router.go:14-42](file://internal/router/router.go#L14-L42) +- [internal/middleware/request_id.go:20-31](file://internal/middleware/request_id.go#L20-L31) +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) +- [internal/middleware/cors.go:7-21](file://internal/middleware/cors.go#L7-L21) +- [internal/handler/health.go:14-19](file://internal/handler/health.go#L14-L19) +- [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/response/response.go:9-37](file://internal/response/response.go#L9-L37) +- [pkg/encode/base64.go:44-52](file://pkg/encode/base64.go#L44-L52) +- [pkg/logger/logger.go:8-20](file://pkg/logger/logger.go#L8-L20) + +章节来源 +- [README.md:1-123](file://README.md#L1-L123) +- [cmd/server/main.go:22-96](file://cmd/server/main.go#L22-L96) +- [internal/router/router.go:14-42](file://internal/router/router.go#L14-L42) + +## 核心组件 +- 统一响应体结构 + - 字段:code、message、data(可选) + - 语义:code 为业务/协议态码;message 为人类可读消息;data 承载具体业务数据 +- 响应函数族 + - OK:成功响应,code=0,message="ok" + - Fail:通用失败响应,传入 HTTP 状态码、业务态码与消息 + - BadRequest:快捷失败,HTTP 400,业务态码 40000 + - InternalError:快捷失败,HTTP 500,业务态码 50000 + +这些函数统一封装了 JSON 输出,确保所有 handler 以一致的结构返回,便于客户端解析与前端统一处理。 + +章节来源 +- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37) + +## 架构总览 +统一响应贯穿“路由 -> 中间件 -> 处理器 -> 统一响应”的调用链路。中间件负责请求标识、跨域与日志;处理器完成参数解析、业务执行与错误处理;统一响应模块负责最终的 JSON 输出。 + +```mermaid +sequenceDiagram +participant C as "客户端" +participant R as "路由/中间件" +participant H as "处理器" +participant S as "统一响应" +C->>R : "HTTP 请求" +R->>R : "请求ID/日志/CORS" +R->>H : "进入处理器" +H->>H : "参数解析/业务执行" +alt "成功" +H->>S : "OK(data)" +S-->>C : "{code,message,data}" +else "失败" +H->>S : "Fail/400/500(...)" +S-->>C : "{code,message}" +end +``` + +图表来源 +- [internal/router/router.go:14-42](file://internal/router/router.go#L14-L42) +- [internal/middleware/request_id.go:20-31](file://internal/middleware/request_id.go#L20-L31) +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) +- [internal/middleware/cors.go:7-21](file://internal/middleware/cors.go#L7-L21) +- [internal/handler/health.go:14-19](file://internal/handler/health.go#L14-L19) +- [internal/response/response.go:15-37](file://internal/response/response.go#L15-L37) + +## 详细组件分析 + +### 统一响应模块(internal/response) +- 设计要点 + - 结构体 Body 仅包含三字段,简洁明确,避免冗余 + - OK/Fail/BadRequest/InternalError 一组函数覆盖常见场景 + - 通过 gin.Context 输出 JSON,保持与框架一致的 Content-Type 与状态码 +- 数据流 + - 输入:gin.Context、业务数据或错误信息 + - 输出:标准化 JSON 响应 +- 错误处理策略 + - 成功路径:OK(data) + - 失败路径:Fail(HTTP状态, 业务态码, 消息),或快捷 BadRequest/InternalError +- 可扩展性 + - 可新增更多快捷函数(如 Unauthorized、NotFound 等) + - 可引入国际化消息映射,按语言返回 message + +```mermaid +classDiagram +class Body { ++int code ++string message ++any data +} +class ResponseAPI { ++OK(c, data) ++Fail(c, httpStatus, code, message) ++BadRequest(c, message) ++InternalError(c, message) +} +ResponseAPI --> Body : "构造并输出" +``` + +图表来源 +- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37) + +章节来源 +- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37) + +### 处理器与统一响应的协作 +- 健康检查处理器 + - 使用 OK 返回简单健康状态 +- 品牌与型号处理器 + - 从仓库层获取数据,发生错误时统一调用 InternalError + - 支持可选的 Base64 响应(通过参数 base64Resp 控制),内部先 JSON 编码再自定义 Base64 转换 + +```mermaid +sequenceDiagram +participant C as "客户端" +participant H as "品牌/型号处理器" +participant Repo as "仓库层" +participant Resp as "统一响应" +C->>H : "GET /audio/getBrand?base64Resp=true" +H->>Repo : "List(...)" +Repo-->>H : "数据或错误" +alt "错误" +H->>Resp : "InternalError(...)" +Resp-->>C : "{code,message}" +else "成功" +H->>H : "按需JSON/Base64编码" +H-->>C : "JSON或Base64字符串" +end +``` + +图表来源 +- [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) +- [pkg/encode/base64.go:35-52](file://pkg/encode/base64.go#L35-L52) +- [internal/response/response.go:34-37](file://internal/response/response.go#L34-L37) + +章节来源 +- [internal/handler/health.go:14-19](file://internal/handler/health.go#L14-L19) +- [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) +- [pkg/encode/base64.go:35-52](file://pkg/encode/base64.go#L35-L52) + +### 中间件与日志 +- 请求 ID 中间件 + - 自动生成或透传 X-Request-ID,便于全链路追踪 +- 日志中间件 + - 记录状态码、方法、路径、耗时、IP、请求 ID、错误等 + - 按状态码分级(info/warn/error)输出 +- CORS 中间件 + - 设置允许的源、方法、头,并暴露 X-Request-ID + +```mermaid +flowchart TD +Start(["进入中间件链"]) --> ReqID["生成/透传请求ID"] +ReqID --> Logger["记录请求开始"] +Logger --> Next["继续下一个处理器"] +Next --> Status{"状态码>=500?"} +Status --> |是| LogErr["记录错误日志"] +Status --> |否| Status400{"状态码>=400?"} +Status400 --> |是| LogWarn["记录告警日志"] +Status400 --> |否| LogInfo["记录正常日志"] +LogErr --> End(["结束"]) +LogWarn --> End +LogInfo --> End +``` + +图表来源 +- [internal/middleware/request_id.go:20-31](file://internal/middleware/request_id.go#L20-L31) +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) +- [internal/middleware/cors.go:7-21](file://internal/middleware/cors.go#L7-L21) + +章节来源 +- [internal/middleware/request_id.go:20-31](file://internal/middleware/request_id.go#L20-L31) +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) +- [internal/middleware/cors.go:7-21](file://internal/middleware/cors.go#L7-L21) + +### 编码与响应格式扩展 +- Base64 响应 + - 通过参数 base64Resp 控制是否返回自定义 Base64 编码的 JSON + - 内部先 JSON 编码,再使用自定义字符映射表进行 Base64 转换 +- 与统一响应的结合 + - 当启用 Base64 时,处理器直接输出字符串,不走统一响应的 JSON 结构 + - 当未启用时,统一响应负责输出标准 JSON 结构 + +章节来源 +- [pkg/encode/base64.go:44-52](file://pkg/encode/base64.go#L44-L52) +- [internal/handler/brand.go:37-47](file://internal/handler/brand.go#L37-L47) +- [internal/handler/model.go:38-48](file://internal/handler/model.go#L38-L48) + +## 依赖分析 +- 统一响应依赖 Gin 上下文输出 JSON +- 处理器依赖统一响应与仓库/服务层 +- 中间件依赖 Gin 与日志库 +- 编码工具独立于响应模块,但被处理器条件使用 + +```mermaid +graph LR +Gin["github.com/gin-gonic/gin"] --> RESP["internal/response/response.go"] +RESP --> HANDLER_HEALTH["internal/handler/health.go"] +RESP --> HANDLER_BRAND["internal/handler/brand.go"] +RESP --> HANDLER_MODEL["internal/handler/model.go"] +HANDLER_BRAND --> ENCODE["pkg/encode/base64.go"] +HANDLER_MODEL --> ENCODE +ROUTER["internal/router/router.go"] --> M_REQID["internal/middleware/request_id.go"] +ROUTER --> M_LOG["internal/middleware/logger.go"] +ROUTER --> M_CORS["internal/middleware/cors.go"] +MAIN["cmd/server/main.go"] --> LOGPKG["pkg/logger/logger.go"] +``` + +图表来源 +- [internal/response/response.go:3-7](file://internal/response/response.go#L3-L7) +- [internal/handler/health.go:4-6](file://internal/handler/health.go#L4-L6) +- [internal/handler/brand.go:7-11](file://internal/handler/brand.go#L7-L11) +- [internal/handler/model.go:7-12](file://internal/handler/model.go#L7-L12) +- [pkg/encode/base64.go:3-6](file://pkg/encode/base64.go#L3-L6) +- [internal/router/router.go:3-12](file://internal/router/router.go#L3-L12) +- [internal/middleware/request_id.go:7](file://internal/middleware/request_id.go#L7) +- [internal/middleware/logger.go:6-8](file://internal/middleware/logger.go#L6-L8) +- [internal/middleware/cors.go:4](file://internal/middleware/cors.go#L4) +- [cmd/server/main.go:12-19](file://cmd/server/main.go#L12-L19) +- [pkg/logger/logger.go:3-5](file://pkg/logger/logger.go#L3-L5) + +章节来源 +- [internal/response/response.go:3-7](file://internal/response/response.go#L3-L7) +- [internal/handler/brand.go:7-11](file://internal/handler/brand.go#L7-L11) +- [internal/handler/model.go:7-12](file://internal/handler/model.go#L7-L12) +- [pkg/encode/base64.go:3-6](file://pkg/encode/base64.go#L3-L6) +- [internal/router/router.go:3-12](file://internal/router/router.go#L3-L12) +- [internal/middleware/request_id.go:7](file://internal/middleware/request_id.go#L7) +- [internal/middleware/logger.go:6-8](file://internal/middleware/logger.go#L6-L8) +- [internal/middleware/cors.go:4](file://internal/middleware/cors.go#L4) +- [cmd/server/main.go:12-19](file://cmd/server/main.go#L12-L19) +- [pkg/logger/logger.go:3-5](file://pkg/logger/logger.go#L3-L5) + +## 性能考虑 +- 统一响应本身开销极低,主要成本在序列化与网络传输 +- 对大对象响应,优先评估是否需要 Base64 压缩,注意额外的编码/解码成本 +- 使用中间件的日志分级与请求 ID,有助于定位慢请求与异常点 +- 生产环境建议开启 Gin Release 模式与合适的日志级别 + +## 故障排查指南 +- 常见问题 + - 业务错误未统一返回:检查处理器是否调用统一响应函数 + - Base64 响应异常:确认参数 base64Resp 的取值与编码流程 + - 日志缺失:确认中间件顺序与日志中间件是否正确设置 +- 排查步骤 + - 通过请求 ID 在日志中检索整条链路 + - 关注状态码分级日志,定位 4xx/5xx 场景 + - 若出现内部错误,统一响应会返回固定业务态码,便于前端/监控识别 + +章节来源 +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) +- [internal/middleware/request_id.go:20-31](file://internal/middleware/request_id.go#L20-L31) +- [internal/response/response.go:34-37](file://internal/response/response.go#L34-L37) + +## 结论 +统一响应模块通过简洁一致的 JSON 结构与函数族,显著提升了 API 的可读性、可维护性与可观测性。配合中间件与日志体系,能够快速定位问题并保障用户体验。对于国际化与调试信息,建议在现有基础上扩展消息映射与上下文元数据,进一步增强一致性与可诊断性。 + +## 附录 + +### 响应结构与状态码定义 +- 响应体字段 + - code:业务/协议态码(成功通常为 0,失败为正整数) + - message:人类可读消息 + - data:业务数据(可选) +- 常用状态码策略 + - 成功:HTTP 200 + code=0 + - 客户端错误:HTTP 400 + 业务态码 40000 + - 服务器错误:HTTP 500 + 业务态码 50000 +- 国际化与调试 + - message 可按语言映射,data 可附加 traceId/requestId 等调试信息 +- Base64 响应 + - 通过参数 base64Resp 控制;启用后返回自定义 Base64 编码的 JSON 字符串 + +章节来源 +- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37) +- [pkg/encode/base64.go:44-52](file://pkg/encode/base64.go#L44-L52) + +### 使用示例(路径指引) +- 健康检查 + - 路径:[internal/handler/health.go:14-19](file://internal/handler/health.go#L14-L19) + - 统一响应调用:[internal/response/response.go:15-21](file://internal/response/response.go#L15-L21) +- 获取品牌列表(含 Base64 选项) + - 路径:[internal/handler/brand.go:26-50](file://internal/handler/brand.go#L26-L50) + - 统一响应调用:[internal/response/response.go:23-28](file://internal/response/response.go#L23-L28) + - 编码逻辑:[pkg/encode/base64.go:35-52](file://pkg/encode/base64.go#L35-L52) +- 获取型号列表(含 Base64 选项) + - 路径:[internal/handler/model.go:26-51](file://internal/handler/model.go#L26-L51) + - 统一响应调用:[internal/response/response.go:23-28](file://internal/response/response.go#L23-L28) + - 编码逻辑:[pkg/encode/base64.go:35-52](file://pkg/encode/base64.go#L35-L52) + +### 最佳实践 +- 所有处理器统一使用统一响应函数输出 +- 错误路径必须记录日志并返回统一响应 +- 对大对象优先评估是否需要 Base64 响应 +- 生产环境启用 Release 模式与合适的日志级别 +- 通过中间件保证请求 ID 与跨域配置一致生效 \ No newline at end of file diff --git a/.qoder/repowiki/zh/content/系统架构/中间件模式.md b/.qoder/repowiki/zh/content/系统架构/中间件模式.md new file mode 100644 index 0000000..fbe449b --- /dev/null +++ b/.qoder/repowiki/zh/content/系统架构/中间件模式.md @@ -0,0 +1,336 @@ +# 中间件模式 + + +**本文引用的文件** +- [cmd/server/main.go](file://cmd/server/main.go) +- [internal/router/router.go](file://internal/router/router.go) +- [internal/middleware/cors.go](file://internal/middleware/cors.go) +- [internal/middleware/logger.go](file://internal/middleware/logger.go) +- [internal/middleware/request_id.go](file://internal/middleware/request_id.go) +- [pkg/logger/logger.go](file://pkg/logger/logger.go) +- [internal/config/config.go](file://internal/config/config.go) +- [internal/handler/health.go](file://internal/handler/health.go) +- [internal/response/response.go](file://internal/response/response.go) + + +## 目录 +1. [简介](#简介) +2. [项目结构](#项目结构) +3. [核心组件](#核心组件) +4. [架构总览](#架构总览) +5. [详细组件分析](#详细组件分析) +6. [依赖关系分析](#依赖关系分析) +7. [性能考量](#性能考量) +8. [故障排查指南](#故障排查指南) +9. [结论](#结论) +10. [附录:最佳实践与自定义指南](#附录最佳实践与自定义指南) + +## 简介 +本文件系统性阐述 Luxsin 应用 API 的中间件模式,围绕 Gin 中间件的执行顺序、CORS 安全策略、结构化日志记录、请求 ID 分布式追踪以及自定义中间件开发与调试进行深入解析。文档同时提供性能优化建议与最佳实践,帮助开发者在保证安全性与可观测性的前提下,构建可维护、可扩展的中间件体系。 + +## 项目结构 +中间件相关代码位于 internal/middleware 目录,路由注册在 internal/router/router.go,服务启动入口在 cmd/server/main.go,日志初始化在 pkg/logger/logger.go,配置在 internal/config/config.go。 + +```mermaid +graph TB +subgraph "应用入口" +MAIN["cmd/server/main.go
启动 HTTP 服务器"] +CFG["internal/config/config.go
加载运行配置"] +PLOG["pkg/logger/logger.go
创建日志器"] +end +subgraph "路由与中间件" +ROUTER["internal/router/router.go
注册路由与中间件"] +MID_REQID["internal/middleware/request_id.go
请求 ID 中间件"] +MID_LOG["internal/middleware/logger.go
结构化日志中间件"] +MID_CORS["internal/middleware/cors.go
CORS 中间件"] +end +subgraph "业务层" +HANDLER_HEALTH["internal/handler/health.go
健康检查处理器"] +RESP["internal/response/response.go
统一响应封装"] +end +MAIN --> CFG +MAIN --> PLOG +MAIN --> ROUTER +ROUTER --> MID_REQID +ROUTER --> MID_LOG +ROUTER --> MID_CORS +ROUTER --> HANDLER_HEALTH +HANDLER_HEALTH --> RESP +``` + +图表来源 +- [cmd/server/main.go:22-96](file://cmd/server/main.go#L22-L96) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) +- [internal/middleware/request_id.go:20-31](file://internal/middleware/request_id.go#L20-L31) +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) +- [internal/middleware/cors.go:7-21](file://internal/middleware/cors.go#L7-L21) +- [internal/handler/health.go:14-19](file://internal/handler/health.go#L14-L19) +- [internal/response/response.go:15-37](file://internal/response/response.go#L15-L37) + +章节来源 +- [cmd/server/main.go:22-96](file://cmd/server/main.go#L22-L96) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) + +## 核心组件 +- 请求 ID 中间件:生成或透传请求 ID,贯穿整个请求链路,便于分布式追踪与问题定位。 +- 日志中间件:在请求完成后输出结构化日志,按状态码分级,包含路径、方法、耗时、客户端 IP、请求 ID、查询参数与错误信息。 +- CORS 中间件:设置跨域相关响应头,处理预检请求(OPTIONS),并暴露请求 ID 头给前端。 + +章节来源 +- [internal/middleware/request_id.go:10-31](file://internal/middleware/request_id.go#L10-L31) +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) +- [internal/middleware/cors.go:7-21](file://internal/middleware/cors.go#L7-L21) + +## 架构总览 +Gin 中间件采用“洋葱模型”,请求从外向内依次进入中间件,再由内向外返回。本项目中间件注册顺序如下: +1) 恢复中间件(panic 恢复) +2) 请求 ID 中间件(注入/透传 X-Request-ID) +3) 结构化日志中间件(计算耗时、读取状态码与请求 ID) +4) CORS 中间件(设置跨域头,处理 OPTIONS) + +```mermaid +sequenceDiagram +participant Client as "客户端" +participant Engine as "Gin 引擎" +participant Recovery as "恢复中间件" +participant ReqID as "请求 ID 中间件" +participant Logger as "日志中间件" +participant CORS as "CORS 中间件" +participant Handler as "业务处理器" +Client->>Engine : "HTTP 请求" +Engine->>Recovery : "进入中间件栈" +Recovery->>ReqID : "Next()" +ReqID->>Logger : "Next()" +Logger->>CORS : "Next()" +CORS->>Handler : "Next()" +Handler-->>CORS : "写入响应" +CORS-->>Logger : "返回" +Logger-->>ReqID : "返回" +ReqID-->>Recovery : "返回" +Recovery-->>Engine : "完成" +Note over Logger : "在 Next() 后计算耗时与状态码" +``` + +图表来源 +- [internal/router/router.go:16-19](file://internal/router/router.go#L16-L19) +- [internal/middleware/logger.go:16-44](file://internal/middleware/logger.go#L16-L44) +- [internal/middleware/request_id.go:20-31](file://internal/middleware/request_id.go#L20-L31) +- [internal/middleware/cors.go:7-21](file://internal/middleware/cors.go#L7-L21) + +## 详细组件分析 + +### 请求 ID 中间件 +- 设计要点 + - 常量键名用于在上下文中存储与传递请求 ID。 + - 若请求头未携带 ID,则生成随机十六进制字符串;若生成失败则回退为固定值。 + - 将请求 ID 写入响应头,便于前端与下游服务识别。 +- 执行流程 + - 读取请求头中的请求 ID。 + - 若为空则生成新的请求 ID 并写入上下文与响应头。 + - 调用下一个中间件或处理器。 +- 分布式追踪支持 + - 通过在请求头与响应头中透传同一请求 ID,可在日志、链路追踪系统中串联一次请求的所有节点。 + +```mermaid +flowchart TD +Start(["进入 RequestID 中间件"]) --> ReadHeader["读取请求头中的 X-Request-ID"] +ReadHeader --> HasID{"是否已存在?"} +HasID --> |是| SetResp["写入响应头 X-Request-ID"] +HasID --> |否| GenID["生成新的请求 ID"] +GenID --> SetCtx["设置到上下文"] +SetCtx --> SetResp +SetResp --> Next["调用下一个中间件/处理器"] +Next --> End(["返回"]) +``` + +图表来源 +- [internal/middleware/request_id.go:20-31](file://internal/middleware/request_id.go#L20-L31) + +章节来源 +- [internal/middleware/request_id.go:10-31](file://internal/middleware/request_id.go#L10-L31) + +### 日志中间件 +- 设计要点 + - 在中间件栈中使用两次调用:先执行 Next(),再在返回后收集状态码、耗时、请求 ID 等信息。 + - 使用结构化日志,按状态码范围输出不同级别(info/warn/error)。 + - 自动记录查询参数与错误集合,便于快速定位问题。 +- 输出字段 + - 状态码、HTTP 方法、路径、耗时、客户端 IP、请求 ID、查询参数、错误信息。 +- 性能与可读性 + - 仅在错误或异常时输出更详细信息,避免对正常请求造成日志风暴。 + - 通过统一字段命名与编码风格,提升日志检索效率。 + +```mermaid +flowchart TD +Enter(["进入 Logger 中间件"]) --> StartTimer["记录开始时间"] +StartTimer --> CallNext["调用 Next() 执行后续中间件/处理器"] +CallNext --> CalcLatency["计算耗时"] +CalcLatency --> ReadStatus["读取响应状态码"] +ReadStatus --> ReadReqID["从上下文读取请求 ID"] +ReadReqID --> BuildFields["构建结构化字段"] +BuildFields --> Level{"状态码级别"} +Level --> |>=500| LogErr["输出错误日志"] +Level --> |>=400| LogWarn["输出警告日志"] +Level --> |<400| LogInfo["输出信息日志"] +LogErr --> Exit(["返回"]) +LogWarn --> Exit +LogInfo --> Exit +``` + +图表来源 +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) + +章节来源 +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) +- [pkg/logger/logger.go:8-20](file://pkg/logger/logger.go#L8-L20) + +### CORS 中间件 +- 设计要点 + - 设置允许来源、方法、头部与暴露头部,满足常见跨域场景。 + - 对预检请求(OPTIONS)直接返回状态码并中断后续处理,减少无效开销。 + - 显式暴露请求 ID 头,便于前端在跨域场景下读取。 +- 安全策略 + - 允许来源为通配符,需结合业务场景评估风险;生产环境建议限制具体来源。 + - 允许的方法与头部应最小化,仅开放必要接口所需项。 + - 暴露头部包含请求 ID,有助于跨域场景下的追踪。 + +```mermaid +flowchart TD +Enter(["进入 CORS 中间件"]) --> SetHeaders["设置跨域相关响应头"] +SetHeaders --> IsOptions{"是否为 OPTIONS 预检请求?"} +IsOptions --> |是| Abort["返回状态码并中断"] +IsOptions --> |否| Next["继续下一个中间件/处理器"] +Abort --> Exit(["返回"]) +Next --> Exit +``` + +图表来源 +- [internal/middleware/cors.go:7-21](file://internal/middleware/cors.go#L7-L21) + +章节来源 +- [internal/middleware/cors.go:7-21](file://internal/middleware/cors.go#L7-L21) + +### 路由与中间件注册 +- 注册顺序 + - 恢复中间件优先于其他中间件,确保异常被正确捕获。 + - 请求 ID 中间件在日志之前,确保日志中包含请求 ID。 + - CORS 放置于最后,避免对上游中间件产生不必要的影响。 +- 组路由 + - 将健康检查等公共接口放入独立组,便于统一管理与扩展。 + +章节来源 +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) + +### 与业务层的交互 +- 处理器示例 + - 健康检查处理器通过统一响应封装返回标准 JSON。 +- 中间件对业务的影响 + - 中间件不改变业务逻辑,仅负责横切关注点(追踪、日志、跨域)。 + - 业务层无需感知中间件的存在,只需专注于数据处理与响应构造。 + +章节来源 +- [internal/handler/health.go:14-19](file://internal/handler/health.go#L14-L19) +- [internal/response/response.go:15-37](file://internal/response/response.go#L15-L37) + +## 依赖关系分析 +- 中间件依赖 + - 请求 ID 中间件依赖 Gin 上下文进行请求头读取与响应头写入。 + - 日志中间件依赖 Zap 日志库与 Gin 上下文的状态码与错误集合。 + - CORS 中间件依赖 Gin 上下文的请求方法判断与响应头设置。 +- 路由与中间件耦合 + - 路由层集中注册中间件,降低各处理器对中间件的感知,提高内聚性与可测试性。 +- 启动与配置 + - 服务器启动时根据环境变量选择日志配置与运行模式,确保日志输出风格与性能符合预期。 + +```mermaid +graph LR +REQID["请求 ID 中间件"] --> GIN["Gin 上下文"] +LOG["日志中间件"] --> ZAP["Zap 日志库"] +LOG --> GIN +CORS["CORS 中间件"] --> GIN +ROUTER["路由注册"] --> REQID +ROUTER --> LOG +ROUTER --> CORS +MAIN["服务器启动"] --> ROUTER +MAIN --> PLOG["日志初始化"] +MAIN --> CFG["配置加载"] +``` + +图表来源 +- [internal/middleware/request_id.go:20-31](file://internal/middleware/request_id.go#L20-L31) +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) +- [internal/middleware/cors.go:7-21](file://internal/middleware/cors.go#L7-L21) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) +- [cmd/server/main.go:22-96](file://cmd/server/main.go#L22-L96) +- [pkg/logger/logger.go:8-20](file://pkg/logger/logger.go#L8-L20) +- [internal/config/config.go:18-64](file://internal/config/config.go#L18-L64) + +章节来源 +- [cmd/server/main.go:22-96](file://cmd/server/main.go#L22-L96) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) + +## 性能考量 +- 中间件顺序 + - 将耗时操作(如日志)放在靠后位置,尽量减少对高频路径的影响。 + - 预检请求(OPTIONS)短路返回,避免不必要的数据库或缓存访问。 +- 日志成本 + - 结构化日志在错误时输出更详细字段,正常请求仅输出必要字段,降低 IO 压力。 + - 生产环境建议使用异步日志或批量刷盘策略,避免阻塞请求处理。 +- 请求 ID 生成 + - 使用安全随机源生成请求 ID,避免碰撞;在高并发场景下注意随机数生成的性能与熵。 +- CORS 开销 + - 仅在需要跨域时启用 CORS 中间件;同源请求可移除以减少额外头设置。 + +## 故障排查指南 +- 请求 ID 缺失 + - 检查请求头是否正确传递 X-Request-ID;若为空,确认请求 ID 中间件是否在路由注册中。 + - 在日志中确认请求 ID 是否写入响应头。 +- 日志缺失或异常 + - 确认日志中间件在请求 ID 之后注册,以便读取到请求 ID。 + - 检查日志级别配置与环境变量,确保日志输出符合预期。 +- CORS 失败 + - 确认请求方法与头部是否在允许列表中;检查预检请求是否被正确短路。 + - 如需限制来源,请调整允许来源策略,避免通配符带来的安全风险。 +- 错误统计 + - 日志中间件会自动记录错误集合,可通过查询参数与错误字段定位问题。 + +章节来源 +- [internal/middleware/request_id.go:20-31](file://internal/middleware/request_id.go#L20-L31) +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) +- [internal/middleware/cors.go:7-21](file://internal/middleware/cors.go#L7-L21) + +## 结论 +本项目通过清晰的中间件分层与严格的注册顺序,实现了请求 ID 追踪、结构化日志与跨域控制三大核心能力。请求 ID 中间件提供端到端的唯一标识,日志中间件保障可观测性,CORS 中间件兼顾易用与安全。建议在生产环境中进一步收紧 CORS 来源与头部白名单,并结合链路追踪系统实现端到端的请求链路可视化。 + +## 附录:最佳实践与自定义指南 + +### 中间件开发最佳实践 +- 单一职责 + - 每个中间件只负责一个横切关注点,避免“大杂烩”式中间件。 +- 可测试性 + - 通过注入依赖(如日志器、配置)与接口抽象,便于单元测试与集成测试。 +- 性能优先 + - 将昂贵操作放在中间件栈靠后位置;对高频路径进行短路与缓存。 +- 可观测性 + - 统一日志字段命名与编码风格;在错误时输出必要上下文信息。 +- 安全性 + - CORS 策略最小化;敏感头与方法仅在必要时暴露。 + - 避免在日志中输出敏感信息(如密码、令牌)。 + +### 自定义中间件开发步骤 +- 明确目标 + - 确定中间件要解决的问题(鉴权、限流、埋点等)。 +- 设计签名 + - 返回类型为 Gin 的处理函数,遵循“读取上下文 -> Next() -> 后续处理”的模式。 +- 注册顺序 + - 将中间件插入到路由注册中合适的位置,避免破坏已有中间件的语义。 +- 测试与验证 + - 编写单元测试覆盖正常与异常分支;使用集成测试验证端到端行为。 +- 文档与规范 + - 记录中间件的行为、配置项与副作用,形成团队共识。 + +### 调试技巧 +- 使用 Zap 的开发/生产配置区分日志风格与级别。 +- 在关键中间件前后打印上下文信息(如请求 ID、状态码、耗时)辅助定位。 +- 利用浏览器网络面板与后端日志联动,快速定位跨域与权限问题。 +- 对预检请求进行单独断点或日志标记,确保 CORS 行为符合预期。 \ No newline at end of file diff --git a/.qoder/repowiki/zh/content/系统架构/依赖注入模式.md b/.qoder/repowiki/zh/content/系统架构/依赖注入模式.md new file mode 100644 index 0000000..29e4d21 --- /dev/null +++ b/.qoder/repowiki/zh/content/系统架构/依赖注入模式.md @@ -0,0 +1,371 @@ +# 依赖注入模式 + + +**本文引用的文件** +- [cmd/server/main.go](file://cmd/server/main.go) +- [internal/router/router.go](file://internal/router/router.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) +- [pkg/logger/logger.go](file://pkg/logger/logger.go) +- [internal/handler/health.go](file://internal/handler/health.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/handler/device.go](file://internal/handler/device.go) + + +## 目录 +1. [引言](#引言) +2. [项目结构](#项目结构) +3. [核心组件](#核心组件) +4. [架构总览](#架构总览) +5. [详细组件分析](#详细组件分析) +6. [依赖关系分析](#依赖关系分析) +7. [性能考虑](#性能考虑) +8. [故障排查指南](#故障排查指南) +9. [结论](#结论) +10. [附录](#附录) + +## 引言 +本文件围绕 Luxsin 应用 API 的依赖注入模式展开,重点说明项目采用的“构造函数注入”与“手动依赖注入(工厂模式)”。我们将从入口程序开始,逐步剖析路由初始化过程中如何注入数据库连接、日志记录器、Redis 客户端与搜索客户端;同时给出替代方案(手动工厂),解释其带来的测试性增强、松耦合设计与配置灵活性,并总结生命周期管理与循环依赖规避策略。 + +## 项目结构 +该项目采用按领域分层与功能模块划分相结合的方式组织代码: +- 入口与启动:cmd/server/main.go 负责加载配置、构建日志、数据库、搜索与缓存实例,并交由路由工厂生成 HTTP 引擎。 +- 配置层:internal/config 下包含数据库、Redis、Meilisearch 的配置加载与校验逻辑。 +- 基础设施:internal/database、internal/cache、internal/search 提供连接与客户端工厂。 +- Web 层:internal/router 路由工厂负责注册中间件与控制器。 +- 控制器层:internal/handler 各处理器通过构造函数接收依赖,体现构造函数注入。 +- 日志:pkg/logger 提供基于环境的 Zap 日志工厂。 + +```mermaid +graph TB +subgraph "启动层" +MAIN["cmd/server/main.go
加载配置/构建基础设施/启动HTTP服务"] +end +subgraph "配置层" +CFG["internal/config/*.go
配置加载与校验"] +end +subgraph "基础设施" +DBF["internal/database/mysql.go
数据库工厂"] +RCF["internal/cache/redis.go
Redis工厂"] +MSF["internal/search/meilisearch.go
搜索工厂"] +LOG["pkg/logger/logger.go
日志工厂"] +end +subgraph "Web层" +RT["internal/router/router.go
路由工厂"] +end +subgraph "控制器层" +H1["internal/handler/health.go"] +H2["internal/handler/brand.go"] +H3["internal/handler/model.go"] +H4["internal/handler/model_list.go"] +H5["internal/handler/device.go"] +end +MAIN --> CFG +MAIN --> DBF +MAIN --> RCF +MAIN --> MSF +MAIN --> LOG +MAIN --> RT +RT --> H1 +RT --> H2 +RT --> H3 +RT --> H4 +RT --> H5 +``` + +图表来源 +- [cmd/server/main.go:22-64](file://cmd/server/main.go#L22-L64) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) +- [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52) +- [internal/database/mysql.go:14-46](file://internal/database/mysql.go#L14-L46) +- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) +- [internal/search/meilisearch.go:17-20](file://internal/search/meilisearch.go#L17-L20) +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) + +章节来源 +- [cmd/server/main.go:22-64](file://cmd/server/main.go#L22-L64) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) +- [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52) + +## 核心组件 +- 配置加载与校验:集中于 internal/config,支持从环境变量或默认值加载数据库、Redis、Meilisearch 参数,并进行必要校验。 +- 基础设施工厂: + - 数据库工厂:打开连接池并执行健康检查。 + - Redis 工厂:创建 Redis 客户端。 + - 搜索工厂:创建 Meilisearch 客户端并绑定索引。 + - 日志工厂:根据环境选择生产/开发配置。 +- 路由工厂:注册全局中间件与各控制器,控制器通过构造函数注入依赖。 +- 控制器:每个处理器以结构体形式持有依赖(数据库、日志、搜索、Redis),通过 NewXxxHandler 构造函数完成注入。 + +章节来源 +- [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52) +- [internal/config/database.go:17-71](file://internal/config/database.go#L17-L71) +- [internal/config/meilisearch.go:14-50](file://internal/config/meilisearch.go#L14-L50) +- [internal/config/redis.go:16-56](file://internal/config/redis.go#L16-L56) +- [internal/database/mysql.go:14-46](file://internal/database/mysql.go#L14-L46) +- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) +- [internal/search/meilisearch.go:17-20](file://internal/search/meilisearch.go#L17-L20) +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) + +## 架构总览 +下图展示了从入口到路由与控制器的依赖注入流程:入口程序负责构建所有外部依赖,然后将这些依赖作为参数传入路由工厂,再由路由工厂注入到各个控制器中。 + +```mermaid +sequenceDiagram +participant Entrypoint as "入口(main.go)" +participant Cfg as "配置(config.Load)" +participant Log as "日志(logger.New)" +participant DB as "数据库(database.Open)" +participant MS as "搜索(search.NewClient)" +participant RC as "缓存(cache.NewClient)" +participant RT as "路由(router.New)" +participant H as "控制器(handler.*)" +Entrypoint->>Cfg : 加载配置 +Entrypoint->>Log : 创建日志实例 +Entrypoint->>DB : 打开数据库连接 +Entrypoint->>MS : 初始化搜索客户端 +Entrypoint->>RC : 初始化Redis客户端 +Entrypoint->>RT : 传入依赖构建引擎 +RT->>H : 为各控制器注入依赖 +Entrypoint->>Entrypoint : 启动HTTP服务 +``` + +图表来源 +- [cmd/server/main.go:22-64](file://cmd/server/main.go#L22-L64) +- [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52) +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) +- [internal/database/mysql.go:14-46](file://internal/database/mysql.go#L14-L46) +- [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) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) + +## 详细组件分析 + +### 入口程序中的依赖注入与生命周期 +入口程序负责: +- 加载配置并校验; +- 构建日志、数据库、搜索与缓存实例; +- 将这些实例注入路由工厂,生成 HTTP 引擎; +- 启动服务并在退出信号到来时优雅关闭。 + +```mermaid +flowchart TD +Start(["程序启动"]) --> LoadCfg["加载配置(config.Load)"] +LoadCfg --> BuildLogger["创建日志(logger.New)"] +BuildLogger --> OpenDB["打开数据库(database.Open)"] +OpenDB --> InitSearch["初始化搜索(search.NewClient)"] +InitSearch --> InitRedis["初始化Redis(cache.NewClient)"] +InitRedis --> BuildRouter["构建路由(router.New)"] +BuildRouter --> RunServer["启动HTTP服务"] +RunServer --> WaitSignal["等待退出信号"] +WaitSignal --> GracefulShutdown["优雅关闭(Shutdown)"] +GracefulShutdown --> End(["结束"]) +``` + +图表来源 +- [cmd/server/main.go:22-95](file://cmd/server/main.go#L22-L95) +- [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52) +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) +- [internal/database/mysql.go:14-46](file://internal/database/mysql.go#L14-L46) +- [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) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) + +章节来源 +- [cmd/server/main.go:22-95](file://cmd/server/main.go#L22-L95) + +### 路由工厂与控制器注入 +路由工厂统一注册中间件并创建各控制器实例,控制器通过各自的 NewXxxHandler 构造函数注入依赖。该模式体现了“构造函数注入”,确保依赖在对象创建时明确、不可变且可替换。 + +```mermaid +classDiagram +class RouterFactory { ++New(log, db, searchClient, redis) *gin.Engine +} +class HealthHandler { ++Check(c) +} +class BrandHandler { +-repo BrandRepository +-log Logger ++GetBrand(c) +} +class ModelHandler { +-repo ModelRepository +-log Logger ++GetModel(c) +} +class ModelListHandler { +-search SearchClient +-log Logger ++ModelList(c) +} +class DeviceHandler { +-redis RedisClient +-log Logger ++ReportDevInfo(c) +} +RouterFactory --> HealthHandler : "创建" +RouterFactory --> BrandHandler : "创建" +RouterFactory --> ModelHandler : "创建" +RouterFactory --> ModelListHandler : "创建" +RouterFactory --> DeviceHandler : "创建" +``` + +图表来源 +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) +- [internal/handler/health.go:10-18](file://internal/handler/health.go#L10-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/handler/model_list.go:19-24](file://internal/handler/model_list.go#L19-L24) +- [internal/handler/device.go:19-24](file://internal/handler/device.go#L19-L24) + +章节来源 +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) + +### 数据库连接注入与生命周期 +- 入口程序调用数据库工厂打开连接并执行健康检查,随后将 *sql.DB 注入路由工厂。 +- 路由工厂将数据库实例注入到需要持久化的控制器(如品牌、型号列表)。 +- 入口程序在程序退出前关闭数据库连接,保证资源回收。 + +章节来源 +- [cmd/server/main.go:38-42](file://cmd/server/main.go#L38-L42) +- [internal/database/mysql.go:14-46](file://internal/database/mysql.go#L14-L46) +- [internal/router/router.go:22-23](file://internal/router/router.go#L22-L23) + +### 日志记录器注入 +- 入口程序创建日志实例后,将其注入到路由工厂与各控制器,用于统一的日志输出与上下文记录。 + +章节来源 +- [cmd/server/main.go:32-36](file://cmd/server/main.go#L32-L36) +- [internal/router/router.go:18](file://internal/router/router.go#L18) + +### Redis 客户端注入 +- 入口程序创建 Redis 客户端后,注入到路由工厂与设备上报控制器,用于存储设备信息。 +- 控制器在处理请求时使用 Redis 客户端执行 HSet 等操作。 + +章节来源 +- [cmd/server/main.go:56-57](file://cmd/server/main.go#L56-L57) +- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) +- [internal/router/router.go:25](file://internal/router/router.go#L25) +- [internal/handler/device.go:71](file://internal/handler/device.go#L71) + +### 搜索客户端注入 +- 入口程序创建搜索客户端后,注入到模型列表控制器,用于检索模型数据。 +- 控制器在处理请求时调用搜索客户端执行查询。 + +章节来源 +- [cmd/server/main.go:50-54](file://cmd/server/main.go#L50-L54) +- [internal/search/meilisearch.go:17-20](file://internal/search/meilisearch.go#L17-L20) +- [internal/router/router.go:24](file://internal/router/router.go#L24) +- [internal/handler/model_list.go:37](file://internal/handler/model_list.go#L37) + +### 手动依赖注入(工厂模式)替代方案 +当不使用第三方 DI 容器时,可通过“手动依赖注入(工厂模式)”实现相同目标: +- 在入口程序中集中创建所有依赖(配置、日志、数据库、搜索、缓存)。 +- 将这些依赖作为参数传递给路由工厂,再由路由工厂注入到控制器。 +- 优点:无需引入额外依赖,控制权完全在应用内,便于单元测试替换依赖。 + +章节来源 +- [cmd/server/main.go:22-64](file://cmd/server/main.go#L22-L64) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) + +## 依赖关系分析 +- 入口程序对配置、日志、数据库、搜索、缓存与路由有直接依赖。 +- 路由工厂对日志、数据库、搜索、Redis 与控制器有依赖。 +- 控制器对各自使用的基础设施(数据库、日志、搜索、Redis)有依赖。 +- 配置层对环境变量与默认值有依赖,负责参数校验。 + +```mermaid +graph LR +MAIN["入口(main.go)"] --> CFG["配置(config)"] +MAIN --> LOG["日志(logger)"] +MAIN --> DB["数据库(factory)"] +MAIN --> MS["搜索(factory)"] +MAIN --> RC["缓存(factory)"] +MAIN --> RT["路由(router)"] +RT --> H1["健康控制器"] +RT --> H2["品牌控制器"] +RT --> H3["型号控制器"] +RT --> H4["模型列表控制器"] +RT --> H5["设备控制器"] +H2 --> DB +H3 --> DB +H4 --> MS +H5 --> RC +``` + +图表来源 +- [cmd/server/main.go:22-64](file://cmd/server/main.go#L22-L64) +- [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/handler/model_list.go:19-24](file://internal/handler/model_list.go#L19-L24) +- [internal/handler/device.go:19-24](file://internal/handler/device.go#L19-L24) + +章节来源 +- [cmd/server/main.go:22-64](file://cmd/server/main.go#L22-L64) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) + +## 性能考虑 +- 数据库连接池:入口程序在数据库工厂中设置最大并发连接数、空闲连接数与连接生命周期,有助于提升并发场景下的稳定性与性能。 +- 日志级别:根据环境选择生产/开发配置,减少不必要的编码开销。 +- 搜索与缓存:合理设置搜索客户端与 Redis 客户端的超时与重试策略,避免阻塞请求线程。 + +章节来源 +- [internal/database/mysql.go:33-35](file://internal/database/mysql.go#L33-L35) +- [pkg/logger/logger.go:10-17](file://pkg/logger/logger.go#L10-L17) + +## 故障排查指南 +- 配置加载失败:检查环境变量是否正确设置,确认配置加载函数返回的错误信息。 +- 数据库连接失败:查看数据库工厂的健康检查与错误返回,确认主机、端口、用户名与密码。 +- 搜索客户端异常:确认搜索主机、API 密钥与索引名称是否正确。 +- Redis 连接异常:确认 Redis 主机、端口与数据库编号。 +- 日志初始化失败:确认环境变量与日志工厂的配置分支。 + +章节来源 +- [internal/config/config.go:18-52](file://internal/config/config.go#L18-L52) +- [internal/config/database.go:57-71](file://internal/config/database.go#L57-L71) +- [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) +- [internal/database/mysql.go:40-43](file://internal/database/mysql.go#L40-L43) +- [internal/search/meilisearch.go:27-29](file://internal/search/meilisearch.go#L27-L29) +- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) + +## 结论 +本项目通过“构造函数注入 + 手动工厂”的方式实现了清晰、可控的依赖管理。入口程序集中构建依赖,路由工厂统一注入,控制器职责单一且易于测试。该模式具备以下优势: +- 测试性增强:可在测试中轻松替换依赖(数据库、搜索、缓存)。 +- 松耦合设计:控制器仅依赖接口抽象,具体实现通过构造函数注入。 +- 配置灵活性:通过配置层集中管理环境变量与默认值,支持多环境部署。 + +## 附录 + +### 依赖生命周期管理 +- 入口程序负责创建与销毁:日志、数据库、搜索与缓存均在入口处创建并在退出时关闭。 +- 路由与控制器仅持有依赖引用,不负责释放底层资源。 + +章节来源 +- [cmd/server/main.go:36-42](file://cmd/server/main.go#L36-L42) +- [cmd/server/main.go:56-57](file://cmd/server/main.go#L56-L57) +- [cmd/server/main.go:87-92](file://cmd/server/main.go#L87-L92) + +### 循环依赖的避免策略 +- 将共享依赖集中在入口程序,避免控制器之间相互创建对方所需的依赖。 +- 使用接口抽象(例如数据库、搜索、缓存)降低耦合度,防止编译期循环导入。 +- 将业务逻辑与基础设施解耦,控制器只持有必要的最小依赖集。 + +章节来源 +- [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/handler/model_list.go:19-24](file://internal/handler/model_list.go#L19-L24) +- [internal/handler/device.go:19-24](file://internal/handler/device.go#L19-L24) \ No newline at end of file diff --git a/.qoder/repowiki/zh/content/系统架构/分层架构设计.md b/.qoder/repowiki/zh/content/系统架构/分层架构设计.md new file mode 100644 index 0000000..5307db1 --- /dev/null +++ b/.qoder/repowiki/zh/content/系统架构/分层架构设计.md @@ -0,0 +1,357 @@ +# 分层架构设计 + + +**本文档引用的文件** +- [main.go](file://cmd/server/main.go) +- [router.go](file://internal/router/router.go) +- [config.go](file://internal/config/config.go) +- [database.go](file://internal/config/database.go) +- [meilisearch_config.go](file://internal/config/meilisearch.go) +- [redis_config.go](file://internal/config/redis.go) +- [mysql.go](file://internal/database/mysql.go) +- [redis.go](file://internal/cache/redis.go) +- [meilisearch.go](file://internal/search/meilisearch.go) +- [health.go](file://internal/handler/health.go) +- [brand.go](file://internal/handler/brand.go) +- [model.go](file://internal/handler/model.go) +- [brand_repository.go](file://internal/repository/brand.go) +- [brand_model.go](file://internal/model/brand.go) +- [logger_middleware.go](file://internal/middleware/logger.go) + + +## 目录 +1. [引言](#引言) +2. [项目结构](#项目结构) +3. [核心组件](#核心组件) +4. [架构总览](#架构总览) +5. [详细组件分析](#详细组件分析) +6. [依赖分析](#依赖分析) +7. [性能考虑](#性能考虑) +8. [故障排除指南](#故障排除指南) +9. [结论](#结论) + +## 引言 +本项目采用分层架构设计,围绕表现层(Router/Handler)、业务层(Repository)、数据访问层(Database/Cache/Search)与基础设施层(Middleware/Logger)进行职责分离。通过构造函数注入依赖的方式,实现清晰的依赖关系与可测试性。MVC 的变体实现体现在:表现层负责路由与请求响应;业务层封装领域逻辑与查询;数据访问层抽象数据库、缓存与搜索引擎;基础设施层提供横切关注点如日志、CORS、请求 ID 等。 + +## 项目结构 +项目按功能域分层组织,核心目录如下: +- cmd/server:应用入口,负责初始化配置、连接外部服务、启动 HTTP 服务器 +- internal/config:配置加载与校验,支持环境变量覆盖 +- internal/router:路由注册与中间件装配 +- internal/handler:HTTP 处理器,面向路由端点,调用仓库与工具 +- internal/repository:业务仓储,封装数据库查询与映射 +- internal/database:数据库连接与池化配置 +- internal/cache:Redis 客户端初始化 +- internal/search:Meilisearch 客户端初始化与搜索接口 +- internal/middleware:Gin 中间件,统一处理日志、CORS、请求 ID +- internal/model:领域模型定义 +- internal/response:统一响应封装 +- pkg/encode:编码工具(如 Base64) +- pkg/logger:日志初始化 + +```mermaid +graph TB +subgraph "表现层" +Router["Router
internal/router/router.go"] +Handlers["Handlers
internal/handler/*"] +end +subgraph "业务层" +Repositories["Repositories
internal/repository/*"] +Models["Models
internal/model/*"] +end +subgraph "数据访问层" +DB["MySQL 连接
internal/database/mysql.go"] +Redis["Redis 客户端
internal/cache/redis.go"] +Search["Meilisearch 客户端
internal/search/meilisearch.go"] +end +subgraph "基础设施层" +Middleware["中间件
internal/middleware/*"] +LoggerPkg["日志包
pkg/logger/*"] +Encode["编码工具
pkg/encode/*"] +end +subgraph "入口" +Main["主程序
cmd/server/main.go"] +Config["配置
internal/config/*"] +end +Main --> Config +Main --> DB +Main --> Redis +Main --> Search +Main --> Router +Router --> Middleware +Router --> Handlers +Handlers --> Repositories +Repositories --> DB +Handlers --> Redis +Handlers --> Search +Handlers --> Encode +Handlers --> LoggerPkg +``` + +**图示来源** +- [main.go:1-96](file://cmd/server/main.go#L1-L96) +- [router.go:1-42](file://internal/router/router.go#L1-L42) +- [mysql.go:1-47](file://internal/database/mysql.go#L1-L47) +- [redis.go:1-17](file://internal/cache/redis.go#L1-L17) +- [meilisearch.go:1-46](file://internal/search/meilisearch.go#L1-L46) +- [config.go:1-64](file://internal/config/config.go#L1-L64) + +**章节来源** +- [main.go:1-96](file://cmd/server/main.go#L1-L96) +- [router.go:1-42](file://internal/router/router.go#L1-L42) +- [config.go:1-64](file://internal/config/config.go#L1-L64) + +## 核心组件 +- 表现层(Router/Handler) + - 路由器负责注册路由组与中间件,将 HTTP 请求映射到处理器 + - 处理器接收请求参数,调用仓库或外部服务,返回统一响应 +- 业务层(Repository) + - 封装数据库查询逻辑,负责数据读取与映射 +- 数据访问层(Database/Cache/Search) + - 提供数据库连接池、Redis 客户端与 Meilisearch 客户端 +- 基础设施层(Middleware/Logger) + - 提供日志、CORS、请求 ID 等横切能力 + +**章节来源** +- [router.go:14-41](file://internal/router/router.go#L14-L41) +- [brand.go:19-24](file://internal/handler/brand.go#L19-L24) +- [brand_repository.go:16-18](file://internal/repository/brand.go#L16-L18) +- [mysql.go:14-46](file://internal/database/mysql.go#L14-L46) +- [redis.go:10-16](file://internal/cache/redis.go#L10-L16) +- [meilisearch.go:17-20](file://internal/search/meilisearch.go#L17-L20) +- [logger_middleware.go:10-45](file://internal/middleware/logger.go#L10-L45) + +## 架构总览 +下图展示了从入口到各层的完整调用链路与依赖方向: + +```mermaid +sequenceDiagram +participant Client as "客户端" +participant Main as "主程序
cmd/server/main.go" +participant Router as "路由器
internal/router/router.go" +participant Handler as "处理器
internal/handler/*" +participant Repo as "仓库
internal/repository/*" +participant DB as "数据库
internal/database/mysql.go" +participant Redis as "Redis
internal/cache/redis.go" +participant Search as "Meilisearch
internal/search/meilisearch.go" +Client->>Main : 启动进程 +Main->>Main : 加载配置/初始化日志 +Main->>DB : 打开数据库连接 +Main->>Redis : 创建客户端 +Main->>Search : 创建客户端 +Main->>Router : 注册路由与中间件 +Client->>Router : 发起 HTTP 请求 +Router->>Handler : 调用对应处理器 +Handler->>Repo : 执行业务查询 +Repo->>DB : 执行 SQL 查询 +DB-->>Repo : 返回结果集 +Repo-->>Handler : 返回领域对象列表 +Handler-->>Client : 统一响应 +``` + +**图示来源** +- [main.go:22-64](file://cmd/server/main.go#L22-L64) +- [router.go:14-41](file://internal/router/router.go#L14-L41) +- [brand.go:30-35](file://internal/handler/brand.go#L30-L35) +- [brand_repository.go:31-50](file://internal/repository/brand.go#L31-L50) +- [mysql.go:28-46](file://internal/database/mysql.go#L28-L46) + +## 详细组件分析 + +### 表现层(Router/Handler) +- 路由器在入口中被创建,并注入日志、数据库、搜索与 Redis 客户端 +- 注册健康检查、品牌、型号、设备等路由组与端点 +- 处理器通过构造函数注入数据库连接与日志实例,确保单一职责与可测试性 + +```mermaid +classDiagram +class Router { ++New(log, db, searchClient, redis) Engine +} +class HealthHandler { ++Check(c) +} +class BrandHandler { +-repo BrandRepository +-log Logger ++GetBrand(c) +} +class ModelHandler { +-repo ModelRepository +-log Logger ++GetModel(c) +} +Router --> HealthHandler : "注册路由" +Router --> BrandHandler : "注册路由" +Router --> ModelHandler : "注册路由" +``` + +**图示来源** +- [router.go:14-41](file://internal/router/router.go#L14-L41) +- [health.go:8-18](file://internal/handler/health.go#L8-L18) +- [brand.go:14-24](file://internal/handler/brand.go#L14-L24) +- [model.go:14-24](file://internal/handler/model.go#L14-L24) + +**章节来源** +- [router.go:14-41](file://internal/router/router.go#L14-L41) +- [health.go:8-18](file://internal/handler/health.go#L8-L18) +- [brand.go:19-24](file://internal/handler/brand.go#L19-L24) +- [model.go:19-24](file://internal/handler/model.go#L19-L24) + +### 业务层(Repository) +- 仓库封装数据库查询逻辑,使用上下文传递取消信号与超时控制 +- 支持条件查询与排序,将结果映射到领域模型 +- 通过构造函数注入数据库连接,保持与底层实现解耦 + +```mermaid +classDiagram +class BrandRepository { +-db sql.DB ++NewBrandRepository(db) ++List(ctx, brandName) []Brand +} +class BrandModel { ++ID int ++Name string +} +BrandRepository --> BrandModel : "返回领域对象" +``` + +**图示来源** +- [brand_repository.go:12-50](file://internal/repository/brand.go#L12-L50) +- [brand_model.go:3-6](file://internal/model/brand.go#L3-L6) + +**章节来源** +- [brand_repository.go:16-18](file://internal/repository/brand.go#L16-L18) +- [brand_repository.go:20-50](file://internal/repository/brand.go#L20-L50) +- [brand_model.go:3-6](file://internal/model/brand.go#L3-L6) + +### 数据访问层(Database/Cache/Search) +- 数据库连接通过 DSN 配置,设置连接池大小与生命周期,并进行健康检查 +- Redis 客户端基于配置创建,用于后续缓存操作 +- Meilisearch 客户端封装搜索请求,限制检索字段并解码结果 + +```mermaid +flowchart TD +Start(["初始化数据访问层"]) --> DBInit["创建数据库连接
internal/database/mysql.go"] +DBInit --> DBPool["设置连接池参数"] +DBPool --> DBPing["执行健康检查"] +DBPing --> RedisInit["创建 Redis 客户端
internal/cache/redis.go"] +DBPing --> SearchInit["创建 Meilisearch 客户端
internal/search/meilisearch.go"] +RedisInit --> End(["完成"]) +SearchInit --> End +``` + +**图示来源** +- [mysql.go:14-46](file://internal/database/mysql.go#L14-L46) +- [redis.go:10-16](file://internal/cache/redis.go#L10-L16) +- [meilisearch.go:17-20](file://internal/search/meilisearch.go#L17-L20) + +**章节来源** +- [mysql.go:14-46](file://internal/database/mysql.go#L14-L46) +- [redis.go:10-16](file://internal/cache/redis.go#L10-L16) +- [meilisearch.go:17-20](file://internal/search/meilisearch.go#L17-L20) + +### 基础设施层(Middleware/Logger) +- 日志中间件记录状态码、路径、耗时、IP、请求 ID 等信息,并按级别输出 +- 请求 ID 中间件为每个请求生成唯一标识,便于追踪 +- CORS 中间件允许跨域访问 + +```mermaid +flowchart TD +Req["请求进入"] --> ReqID["生成/获取请求ID"] +ReqID --> LogMW["日志中间件记录"] +LogMW --> CORS["CORS 中间件"] +CORS --> Handler["处理器执行"] +Handler --> Resp["响应返回"] +``` + +**图示来源** +- [logger_middleware.go:10-45](file://internal/middleware/logger.go#L10-L45) + +**章节来源** +- [logger_middleware.go:10-45](file://internal/middleware/logger.go#L10-L45) + +### 配置管理(Config) +- 配置加载支持环境变量覆盖,分别加载数据库、Meilisearch、Redis 的配置 +- 在生产环境与开发环境之间切换默认值 +- 提供地址格式化方法与环境变量读取辅助函数 + +```mermaid +flowchart TD +Load["加载配置"] --> Env["解析环境变量"] +Env --> DB["加载数据库配置"] +Env --> MS["加载 Meilisearch 配置"] +Env --> RD["加载 Redis 配置"] +DB --> ValidateDB["校验数据库配置"] +MS --> ValidateMS["校验 Meilisearch 配置"] +RD --> ValidateRD["校验 Redis 配置"] +ValidateDB --> Done["返回配置对象"] +ValidateMS --> Done +ValidateRD --> Done +``` + +**图示来源** +- [config.go:18-56](file://internal/config/config.go#L18-L56) +- [database.go:17-71](file://internal/config/database.go#L17-L71) +- [meilisearch_config.go:14-50](file://internal/config/meilisearch.go#L14-L50) +- [redis_config.go:16-56](file://internal/config/redis.go#L16-L56) + +**章节来源** +- [config.go:18-56](file://internal/config/config.go#L18-L56) +- [database.go:17-71](file://internal/config/database.go#L17-L71) +- [meilisearch_config.go:14-50](file://internal/config/meilisearch.go#L14-L50) +- [redis_config.go:16-56](file://internal/config/redis.go#L16-L56) + +## 依赖分析 +- 入口依赖配置、数据库、缓存与搜索引擎,然后构建路由器 +- 路由器依赖中间件与处理器,处理器依赖仓库与日志 +- 仓库依赖数据库连接,处理器可选依赖缓存与搜索引擎 +- 配置模块独立于业务层,提供环境化参数 + +```mermaid +graph LR +Main["cmd/server/main.go"] --> Config["internal/config/*"] +Main --> DB["internal/database/mysql.go"] +Main --> Redis["internal/cache/redis.go"] +Main --> Search["internal/search/meilisearch.go"] +Main --> Router["internal/router/router.go"] +Router --> Middleware["internal/middleware/*"] +Router --> Handlers["internal/handler/*"] +Handlers --> Repositories["internal/repository/*"] +Repositories --> DB +Handlers --> Redis +Handlers --> Search +Handlers --> Logger["pkg/logger/*"] +``` + +**图示来源** +- [main.go:22-64](file://cmd/server/main.go#L22-L64) +- [router.go:14-41](file://internal/router/router.go#L14-L41) + +**章节来源** +- [main.go:22-64](file://cmd/server/main.go#L22-L64) +- [router.go:14-41](file://internal/router/router.go#L14-L41) + +## 性能考虑 +- 数据库连接池:设置最大打开连接数、空闲连接数与连接生命周期,减少连接开销 +- 上下文超时:仓库查询使用上下文传递超时,避免阻塞 +- 编码优化:处理器支持 Base64 编码响应,降低传输体积但增加 CPU 开销,需按场景权衡 +- 中间件顺序:日志中间件应置于末尾以统计真实耗时 + +[本节为通用指导,无需特定文件来源] + +## 故障排除指南 +- 数据库连接失败:检查配置中的主机、端口、用户名与密码;确认网络可达与安全组放行 +- Meilisearch 搜索异常:确认索引名称与 API Key 正确,检查网络连通性 +- Redis 连接问题:核对主机、端口与认证信息,验证目标数据库编号 +- 日志输出异常:确认日志初始化成功与环境变量设置正确 +- 响应编码错误:当启用 Base64 编码时,确保编码流程无异常并返回合适的状态码 + +**章节来源** +- [main.go:38-58](file://cmd/server/main.go#L38-L58) +- [brand.go:32-43](file://internal/handler/brand.go#L32-L43) +- [meilisearch.go:27-29](file://internal/search/meilisearch.go#L27-L29) + +## 结论 +该分层架构通过清晰的职责划分与依赖注入,实现了表现层、业务层、数据访问层与基础设施层的解耦。MVC 变体在本项目中体现为:路由与处理器承担表现层职责,仓库承载业务逻辑,数据库/缓存/搜索引擎作为数据访问层,中间件与日志作为基础设施。建议在扩展新功能时遵循“高层不依赖低层”的原则,保持构造函数注入与接口隔离,持续优化连接池与查询性能,并完善监控与告警体系。 \ No newline at end of file diff --git a/.qoder/repowiki/zh/content/系统架构/系统架构.md b/.qoder/repowiki/zh/content/系统架构/系统架构.md new file mode 100644 index 0000000..d07673d --- /dev/null +++ b/.qoder/repowiki/zh/content/系统架构/系统架构.md @@ -0,0 +1,484 @@ +# 系统架构 + + +**本文引用的文件** +- [cmd/server/main.go](file://cmd/server/main.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/router/router.go](file://internal/router/router.go) +- [internal/handler/health.go](file://internal/handler/health.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/handler/device.go](file://internal/handler/device.go) +- [internal/middleware/logger.go](file://internal/middleware/logger.go) +- [internal/middleware/cors.go](file://internal/middleware/cors.go) +- [internal/middleware/request_id.go](file://internal/middleware/request_id.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) +- [internal/response/response.go](file://internal/response/response.go) +- [pkg/logger/logger.go](file://pkg/logger/logger.go) +- [go.mod](file://go.mod) +- [README.md](file://README.md) +- [Makefile](file://Makefile) + + +## 目录 +1. [引言](#引言) +2. [项目结构](#项目结构) +3. [核心组件](#核心组件) +4. [架构总览](#架构总览) +5. [详细组件分析](#详细组件分析) +6. [依赖关系分析](#依赖关系分析) +7. [性能考量](#性能考量) +8. [故障排查指南](#故障排查指南) +9. [结论](#结论) +10. [附录](#附录) + +## 引言 +本文件为 Luxsin 应用 API 项目的系统架构文档,聚焦于高层设计、架构模式与系统边界。项目采用分层架构(表现层、业务层、数据访问层、基础设施层),结合依赖注入、中间件与工厂等模式,形成清晰的职责划分与可维护性。系统围绕 Gin HTTP 框架构建,集成 MySQL、Redis 与 Meilisearch,提供健康检查、品牌与型号查询、设备信息上报以及模型检索等能力。 + +## 项目结构 +项目采用按领域与层次混合的组织方式: +- cmd/server:程序入口,负责配置加载、资源初始化、HTTP 服务启动与优雅关闭 +- internal:核心业务域 + - config:集中式配置加载与校验 + - router:路由注册与中间件装配 + - handler:HTTP 处理器,面向具体接口 + - middleware:横切关注点(日志、CORS、Request ID) + - database:数据库连接工厂与生命周期管理 + - cache:Redis 客户端工厂 + - search:Meilisearch 客户端与搜索逻辑 + - response:统一响应体封装 + - model/repository:模型与仓储抽象(当前未在路由中使用) +- pkg:可复用工具模块(日志封装) +- sql:数据库初始化脚本 +- 根目录:构建与依赖声明 + +```mermaid +graph TB +subgraph "应用进程" +MAIN["cmd/server/main.go
程序入口"] +ROUTER["internal/router/router.go
路由与中间件"] +HANDLERS["internal/handler/*
HTTP处理器"] +RESP["internal/response/response.go
统一响应"] +end +subgraph "配置与基础设施" +CFG["internal/config/*.go
配置加载/校验"] +LOGPKG["pkg/logger/logger.go
日志封装"] +MYSQL["internal/database/mysql.go
MySQL工厂"] +REDIS["internal/cache/redis.go
Redis工厂"] +MS["internal/search/meilisearch.go
Meilisearch客户端"] +end +MAIN --> CFG +MAIN --> MYSQL +MAIN --> REDIS +MAIN --> MS +MAIN --> ROUTER +ROUTER --> HANDLERS +HANDLERS --> RESP +MAIN --> LOGPKG +``` + +图表来源 +- [cmd/server/main.go:1-96](file://cmd/server/main.go#L1-L96) +- [internal/router/router.go:1-42](file://internal/router/router.go#L1-L42) +- [internal/config/config.go:1-64](file://internal/config/config.go#L1-L64) +- [pkg/logger/logger.go:1-20](file://pkg/logger/logger.go#L1-L20) +- [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/response/response.go:1-37](file://internal/response/response.go#L1-L37) + +章节来源 +- [README.md:5-17](file://README.md#L5-L17) +- [go.mod:1-47](file://go.mod#L1-L47) + +## 核心组件 +- 配置中心:集中加载运行环境、主机、端口、数据库、搜索引擎与缓存参数,并进行必要校验 +- 日志封装:按环境输出不同编码风格的日志,便于生产与开发调试 +- 数据库工厂:构造 MySQL 连接池,设置连接上限、空闲数与生命周期,并进行超时探测 +- 缓存工厂:构造 Redis 客户端实例 +- 搜索引擎客户端:封装 Meilisearch 搜索请求,限定返回字段 +- 路由与中间件:装配 Recovery、Request ID、Logger、CORS,并注册各业务路由 +- HTTP 处理器:面向具体接口(健康检查、品牌、型号、模型列表、设备信息上报) +- 统一响应:标准化返回结构,简化错误码与消息传递 + +章节来源 +- [internal/config/config.go:18-56](file://internal/config/config.go#L18-L56) +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) +- [internal/database/mysql.go:14-46](file://internal/database/mysql.go#L14-L46) +- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) +- [internal/search/meilisearch.go:17-45](file://internal/search/meilisearch.go#L17-L45) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) +- [internal/handler/health.go:14-18](file://internal/handler/health.go#L14-L18) +- [internal/response/response.go:9-36](file://internal/response/response.go#L9-L36) + +## 架构总览 +系统采用“入口—配置—基础设施—路由—处理器”的分层结构,入口负责组装依赖并通过工厂创建基础设施客户端,路由层装配中间件并注册业务接口,处理器层完成业务逻辑与数据访问。 + +```mermaid +graph TB +CLIENT["客户端/调用方"] +ENTRY["cmd/server/main.go
启动与依赖注入"] +CONF["internal/config/*.go
配置加载/校验"] +LOG["pkg/logger/logger.go
日志"] +DBF["internal/database/mysql.go
MySQL工厂"] +REDF["internal/cache/redis.go
Redis工厂"] +MSF["internal/search/meilisearch.go
Meilisearch工厂"] +RT["internal/router/router.go
路由与中间件"] +H1["internal/handler/health.go"] +H2["internal/handler/brand.go"] +H3["internal/handler/model.go"] +H4["internal/handler/model_list.go"] +H5["internal/handler/device.go"] +CLIENT --> ENTRY +ENTRY --> CONF +ENTRY --> DBF +ENTRY --> REDF +ENTRY --> MSF +ENTRY --> LOG +ENTRY --> RT +RT --> H1 +RT --> H2 +RT --> H3 +RT --> H4 +RT --> H5 +``` + +图表来源 +- [cmd/server/main.go:22-64](file://cmd/server/main.go#L22-L64) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) +- [internal/handler/health.go:10-18](file://internal/handler/health.go#L10-L18) +- [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/handler/device.go](file://internal/handler/device.go) + +## 详细组件分析 + +### 入口与控制流(main) +- 加载配置并根据环境设置 Gin 运行模式 +- 初始化日志、数据库、搜索引擎与缓存客户端 +- 创建 Gin 引擎并启动 HTTP 服务器 +- 支持优雅关闭,处理系统信号 + +```mermaid +sequenceDiagram +participant OS as "操作系统" +participant Main as "main.go" +participant Cfg as "config.Load()" +participant Log as "logger.New()" +participant DB as "database.Open()" +participant MS as "search.NewClient()" +participant RC as "cache.NewClient()" +participant RT as "router.New()" +OS->>Main : 启动进程 +Main->>Cfg : 加载配置 +Main->>Log : 初始化日志 +Main->>DB : 打开数据库连接 +Main->>MS : 初始化搜索引擎 +Main->>RC : 初始化缓存 +Main->>RT : 创建路由引擎 +Main->>OS : 启动HTTP服务 +``` + +图表来源 +- [cmd/server/main.go:22-96](file://cmd/server/main.go#L22-L96) + +章节来源 +- [cmd/server/main.go:22-96](file://cmd/server/main.go#L22-L96) + +### 配置加载与校验 +- 从环境变量读取运行参数,支持默认值 +- 分别加载数据库、搜索引擎与缓存配置并执行校验 +- 提供 Addr() 计算监听地址 + +```mermaid +flowchart TD +Start(["启动"]) --> LoadEnv["读取环境变量"] +LoadEnv --> LoadDB["加载数据库配置并校验"] +LoadDB --> LoadMS["加载搜索引擎配置并校验"] +LoadMS --> LoadRD["加载Redis配置并校验"] +LoadRD --> BuildCfg["构建Config对象"] +BuildCfg --> End(["完成"]) +``` + +图表来源 +- [internal/config/config.go:18-56](file://internal/config/config.go#L18-L56) + +章节来源 +- [internal/config/config.go:18-56](file://internal/config/config.go#L18-L56) + +### 路由与中间件 +- 使用 Gin.New() 创建引擎,启用 Recovery +- 注册中间件:Request ID、Logger、CORS +- 注册业务路由分组与接口 + +```mermaid +sequenceDiagram +participant RT as "router.New()" +participant MW1 as "RequestID" +participant MW2 as "Logger" +participant MW3 as "CORS" +participant H as "handlers" +RT->>MW1 : Use(RequestID) +RT->>MW2 : Use(Logger) +RT->>MW3 : Use(CORS) +RT->>H : 注册健康检查/品牌/型号/模型列表/设备上报 +``` + +图表来源 +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) +- [internal/middleware/request_id.go:20-30](file://internal/middleware/request_id.go#L20-L30) +- [internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45) +- [internal/middleware/cors.go:7-20](file://internal/middleware/cors.go#L7-L20) + +章节来源 +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) +- [internal/middleware/request_id.go:10-31](file://internal/middleware/request_id.go#L10-L31) +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) +- [internal/middleware/cors.go:7-21](file://internal/middleware/cors.go#L7-L21) + +### 数据访问层(数据库/缓存/搜索) +- 数据库:通过工厂函数创建 sql.DB,设置连接池参数并 Ping 校验 +- 缓存:通过工厂函数创建 Redis 客户端 +- 搜索:通过工厂函数创建 Meilisearch 客户端,提供模型列表检索 + +```mermaid +classDiagram +class DBFactory { ++Open(cfg) *sql.DB +} +class RedisFactory { ++NewClient(cfg) *redis.Client +} +class SearchClient { ++ModelList(ctx, key, count) []map[string]any +} +DBFactory --> "*sql.DB" : "创建" +RedisFactory --> "*redis.Client" : "创建" +SearchClient --> "meilisearch.IndexManager" : "封装" +``` + +图表来源 +- [internal/database/mysql.go:14-46](file://internal/database/mysql.go#L14-L46) +- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) +- [internal/search/meilisearch.go:17-45](file://internal/search/meilisearch.go#L17-L45) + +章节来源 +- [internal/database/mysql.go:14-46](file://internal/database/mysql.go#L14-L46) +- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) +- [internal/search/meilisearch.go:17-45](file://internal/search/meilisearch.go#L17-L45) + +### 处理器与统一响应 +- 处理器:健康检查、品牌、型号、模型列表、设备信息上报 +- 统一响应:OK/Fail/BadRequest/InternalError 等方法,保证返回结构一致 + +```mermaid +classDiagram +class Handler { +<> ++Check(c) +} +class BrandHandler { ++GetBrand(c) +} +class ModelHandler { ++GetModel(c) +} +class ModelListHandler { ++ModelList(c) +} +class DeviceHandler { ++ReportDevInfo(c) +} +class Response { ++OK(c,data) ++Fail(c,status,code,msg) ++BadRequest(c,msg) ++InternalError(c,msg) +} +Handler <|.. BrandHandler +Handler <|.. ModelHandler +Handler <|.. ModelListHandler +Handler <|.. DeviceHandler +BrandHandler --> Response : "使用" +ModelHandler --> Response : "使用" +ModelListHandler --> Response : "使用" +DeviceHandler --> Response : "使用" +``` + +图表来源 +- [internal/handler/health.go:8-18](file://internal/handler/health.go#L8-L18) +- [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/handler/device.go](file://internal/handler/device.go) +- [internal/response/response.go:9-36](file://internal/response/response.go#L9-L36) + +章节来源 +- [internal/handler/health.go:8-18](file://internal/handler/health.go#L8-L18) +- [internal/response/response.go:9-36](file://internal/response/response.go#L9-L36) + +### 关键流程示例:健康检查 +- 请求进入 /api/v1/health +- 经过中间件链路后交由 HealthHandler 处理 +- 使用统一响应返回状态 + +```mermaid +sequenceDiagram +participant C as "客户端" +participant G as "Gin引擎" +participant MW as "中间件链" +participant H as "HealthHandler" +participant R as "Response" +C->>G : GET /api/v1/health +G->>MW : 执行中间件 +MW-->>G : 继续 +G->>H : Check(c) +H->>R : OK(c, {status : "up"}) +R-->>C : JSON响应 +``` + +图表来源 +- [internal/router/router.go:27-30](file://internal/router/router.go#L27-L30) +- [internal/handler/health.go:14-18](file://internal/handler/health.go#L14-L18) +- [internal/response/response.go:15-21](file://internal/response/response.go#L15-L21) + +章节来源 +- [internal/router/router.go:27-30](file://internal/router/router.go#L27-L30) +- [internal/handler/health.go:14-18](file://internal/handler/health.go#L14-L18) +- [internal/response/response.go:15-21](file://internal/response/response.go#L15-L21) + +## 依赖关系分析 +- 技术栈与版本 + - Go 1.24.0 + - Gin 1.10.0、MySQL Driver 1.10.0、Meilisearch Go SDK 0.36.2、Redis Go-Redis 9.19.0、Zap 1.27.0 +- 间接依赖:大量通过 go.mod 标注的间接依赖,确保高性能与安全更新 +- 版本兼容性:Go 1.24 与各依赖版本在 go.mod 中明确声明,遵循语义化版本 + +```mermaid +graph LR +GO["Go 1.24.0"] +GIN["Gin 1.10.0"] +MYSQLDRV["MySQL Driver 1.10.0"] +MS["Meilisearch SDK 0.36.2"] +REDIS["Redis Go-Redis 9.19.0"] +ZAP["Zap 1.27.0"] +GO --> GIN +GO --> MYSQLDRV +GO --> MS +GO --> REDIS +GO --> ZAP +``` + +图表来源 +- [go.mod:3-11](file://go.mod#L3-L11) + +章节来源 +- [go.mod:3-47](file://go.mod#L3-L47) + +## 性能考量 +- 连接池与生命周期 + - 数据库连接池:最大并发、空闲连接数与连接最长存活时间已设置,有助于控制资源占用与抖动 + - Redis 客户端:通过工厂创建,建议在处理器中复用以减少连接开销 +- 超时与稳定性 + - HTTP 服务器设置读/写/空闲超时,避免慢请求导致资源泄漏 + - 数据库 Ping 设置超时上下文,防止启动阻塞 +- 日志级别与开销 + - 生产环境使用生产配置,减少编码开销;开发环境彩色编码提升可观测性 +- 搜索性能 + - 搜索限制返回字段与数量,降低网络与解析成本 + +章节来源 +- [internal/database/mysql.go:33-43](file://internal/database/mysql.go#L33-L43) +- [cmd/server/main.go:66-72](file://cmd/server/main.go#L66-L72) +- [pkg/logger/logger.go:10-17](file://pkg/logger/logger.go#L10-L17) +- [internal/search/meilisearch.go:22-29](file://internal/search/meilisearch.go#L22-L29) + +## 故障排查指南 +- 启动失败 + - 检查配置加载是否成功(环境变量、默认值、校验) + - 查看数据库连接日志与错误信息 +- 接口异常 + - 通过 Request ID 定位请求链路 + - 根据日志中的状态码、路径、延迟与错误信息定位问题 +- 搜索无结果 + - 确认索引与 API Key 配置正确 + - 检查检索关键字与返回字段映射 +- 缓存不可用 + - 校验 Redis 地址、端口与认证信息 + +章节来源 +- [cmd/server/main.go:32-58](file://cmd/server/main.go#L32-L58) +- [internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45) +- [internal/middleware/request_id.go:20-30](file://internal/middleware/request_id.go#L20-L30) +- [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) + +## 结论 +本项目通过清晰的分层架构与工厂/中间件/依赖注入模式,实现了高内聚、低耦合的服务结构。入口负责依赖装配,路由与中间件提供横切能力,处理器与统一响应保障接口一致性。结合数据库连接池、日志与超时策略,系统具备良好的性能与可维护性。后续可在安全、监控与灾备方面进一步增强。 + +## 附录 + +### 系统上下文图 +```mermaid +graph TB +U["用户/客户端"] +S["应用进程"] +D["MySQL"] +K["Redis"] +E["Meilisearch"] +U --> S +S --> D +S --> K +S --> E +``` + +图表来源 +- [cmd/server/main.go:38-57](file://cmd/server/main.go#L38-L57) +- [internal/database/mysql.go:14-46](file://internal/database/mysql.go#L14-L46) +- [internal/cache/redis.go:10-16](file://internal/cache/redis.go#L10-L16) +- [internal/search/meilisearch.go:17-20](file://internal/search/meilisearch.go#L17-L20) + +### 组件分解图 +```mermaid +graph TB +MAIN["main.go"] +CFG["config/*.go"] +LOG["pkg/logger/logger.go"] +DB["internal/database/mysql.go"] +RC["internal/cache/redis.go"] +MS["internal/search/meilisearch.go"] +RT["internal/router/router.go"] +H["internal/handler/*"] +RESP["internal/response/response.go"] +MAIN --> CFG +MAIN --> LOG +MAIN --> DB +MAIN --> RC +MAIN --> MS +MAIN --> RT +RT --> H +H --> RESP +``` + +图表来源 +- [cmd/server/main.go:22-64](file://cmd/server/main.go#L22-L64) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) +- [internal/response/response.go:9-36](file://internal/response/response.go#L9-L36) + +### 部署拓扑与基础设施要求 +- 运行环境:Go 1.24+ +- 依赖服务:MySQL、Redis、Meilisearch +- 监听地址与端口:由配置决定,默认 0.0.0.0:8080 +- 构建与运行:Makefile 提供 run/build/test/tidy 目标 + +章节来源 +- [README.md:21-29](file://README.md#L21-L29) +- [README.md:75-81](file://README.md#L75-L81) +- [Makefile:3-14](file://Makefile#L3-L14) \ No newline at end of file diff --git a/.qoder/repowiki/zh/content/系统架构/组件交互机制.md b/.qoder/repowiki/zh/content/系统架构/组件交互机制.md new file mode 100644 index 0000000..0dc0e4d --- /dev/null +++ b/.qoder/repowiki/zh/content/系统架构/组件交互机制.md @@ -0,0 +1,383 @@ +# 组件交互机制 + + +**本文引用的文件** +- [cmd/server/main.go](file://cmd/server/main.go) +- [internal/router/router.go](file://internal/router/router.go) +- [internal/handler/device.go](file://internal/handler/device.go) +- [internal/handler/model.go](file://internal/handler/model.go) +- [internal/handler/brand.go](file://internal/handler/brand.go) +- [internal/handler/model_list.go](file://internal/handler/model_list.go) +- [internal/repository/model.go](file://internal/repository/model.go) +- [internal/repository/brand.go](file://internal/repository/brand.go) +- [internal/database/mysql.go](file://internal/database/mysql.go) +- [internal/search/meilisearch.go](file://internal/search/meilisearch.go) +- [internal/cache/redis.go](file://internal/cache/redis.go) +- [internal/response/response.go](file://internal/response/response.go) +- [internal/middleware/logger.go](file://internal/middleware/logger.go) +- [internal/middleware/request_id.go](file://internal/middleware/request_id.go) +- [pkg/encode/base64.go](file://pkg/encode/base64.go) + + +## 目录 +1. [简介](#简介) +2. [项目结构](#项目结构) +3. [核心组件](#核心组件) +4. [架构总览](#架构总览) +5. [详细组件分析](#详细组件分析) +6. [依赖关系分析](#依赖关系分析) +7. [性能考量](#性能考量) +8. [故障排查指南](#故障排查指南) +9. [结论](#结论) +10. [附录](#附录) + +## 简介 +本文件面向 Luxsin 应用 API 项目,系统性梳理从请求接收到响应返回的完整数据流路径,重点覆盖 Router → Handler → Repository → Database 的调用链路与参数传递;解释各组件间的解耦机制与接口设计原则;文档化错误传播机制与异常处理策略;提供组件交互的时序图与数据流图;说明异步处理与并发控制的实现方式,并给出性能优化与监控建议。 + +## 项目结构 +该项目采用分层与按功能域组织的混合结构: +- 入口层:cmd/server/main.go 负责配置加载、服务启动与优雅停机。 +- 路由层:internal/router/router.go 定义路由组与中间件栈。 +- 处理器层:internal/handler/* 提供业务端点逻辑,负责参数解析、调用仓库与外部服务、封装响应。 +- 仓储层:internal/repository/* 实现数据库访问与查询封装。 +- 数据库层:internal/database/mysql.go 提供 MySQL 连接与连接池配置。 +- 搜索层:internal/search/meilisearch.go 封装 Meilisearch 客户端。 +- 缓存层:internal/cache/redis.go 封装 Redis 客户端。 +- 响应与编码:internal/response/response.go、pkg/encode/base64.go 提供统一响应体与可选的自定义 Base64 编码。 +- 中间件:internal/middleware/* 提供日志、CORS、请求 ID 等横切能力。 + +```mermaid +graph TB +subgraph "入口" +MAIN["cmd/server/main.go"] +end +subgraph "路由与中间件" +ROUTER["internal/router/router.go"] +MID_REQ["internal/middleware/request_id.go"] +MID_LOG["internal/middleware/logger.go"] +end +subgraph "处理器" +H_BRAND["internal/handler/brand.go"] +H_MODEL["internal/handler/model.go"] +H_MODEL_LIST["internal/handler/model_list.go"] +H_DEVICE["internal/handler/device.go"] +end +subgraph "仓储" +R_BRAND["internal/repository/brand.go"] +R_MODEL["internal/repository/model.go"] +end +subgraph "数据库/搜索/缓存" +DB["internal/database/mysql.go"] +MS["internal/search/meilisearch.go"] +RD["internal/cache/redis.go"] +end +RESP["internal/response/response.go"] +ENC["pkg/encode/base64.go"] +MAIN --> ROUTER +ROUTER --> MID_REQ +ROUTER --> MID_LOG +ROUTER --> H_BRAND +ROUTER --> H_MODEL +ROUTER --> H_MODEL_LIST +ROUTER --> H_DEVICE +H_BRAND --> R_BRAND +H_MODEL --> R_MODEL +H_MODEL_LIST --> MS +H_DEVICE --> RD +R_BRAND --> DB +R_MODEL --> DB +H_BRAND --> RESP +H_MODEL --> RESP +H_MODEL_LIST --> RESP +H_DEVICE --> RESP +H_BRAND --> ENC +H_MODEL --> ENC +H_MODEL_LIST --> ENC +``` + +图表来源 +- [cmd/server/main.go:1-96](file://cmd/server/main.go#L1-L96) +- [internal/router/router.go:1-42](file://internal/router/router.go#L1-L42) +- [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/handler/model_list.go:1-57](file://internal/handler/model_list.go#L1-L57) +- [internal/handler/device.go:1-85](file://internal/handler/device.go#L1-L85) +- [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/database/mysql.go:1-47](file://internal/database/mysql.go#L1-L47) +- [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) +- [internal/response/response.go:1-37](file://internal/response/response.go#L1-L37) +- [pkg/encode/base64.go:1-52](file://pkg/encode/base64.go#L1-L52) + +章节来源 +- [cmd/server/main.go:1-96](file://cmd/server/main.go#L1-L96) +- [internal/router/router.go:1-42](file://internal/router/router.go#L1-L42) + +## 核心组件 +- 入口与服务生命周期:main 负责加载配置、初始化日志、数据库、搜索引擎与缓存客户端,构建 Gin 引擎并启动 HTTP 服务器,同时注册信号量以支持优雅停机。 +- 路由与中间件:路由层集中注册各业务路由与全局中间件(恢复、请求 ID、日志、CORS),确保所有请求具备一致的横切能力。 +- 处理器:按业务域拆分,分别处理品牌、型号、型号列表(搜索)、设备上报(Redis)等端点,统一使用上下文传递取消/超时信号。 +- 仓储:封装 SQL 查询细节,提供类型安全的数据读取与扫描逻辑,向上游处理器暴露清晰的领域模型集合。 +- 数据库:集中配置连接池大小、空闲连接数与连接最大生命周期,确保高并发下的稳定性。 +- 搜索与缓存:Meilisearch 用于全文检索,Redis 用于设备信息的快速写入与存储。 +- 响应与编码:统一响应体结构与错误码语义;可选自定义 Base64 编码以降低传输体积或满足特定协议要求。 + +章节来源 +- [cmd/server/main.go:22-96](file://cmd/server/main.go#L22-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/handler/device.go:19-85](file://internal/handler/device.go#L19-L85) +- [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/database/mysql.go:14-47](file://internal/database/mysql.go#L14-L47) +- [internal/search/meilisearch.go:17-46](file://internal/search/meilisearch.go#L17-L46) +- [internal/cache/redis.go:10-17](file://internal/cache/redis.go#L10-L17) +- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37) +- [pkg/encode/base64.go:13-52](file://pkg/encode/base64.go#L13-L52) + +## 架构总览 +下图展示一次典型请求从进入路由到返回响应的全链路交互,涵盖 Router、Handler、Repository、Database、Search、Cache 以及响应与编码模块。 + +```mermaid +sequenceDiagram +participant C as "客户端" +participant G as "Gin 路由" +participant M1 as "请求ID中间件" +participant M2 as "日志中间件" +participant H as "处理器" +participant R as "仓储" +participant D as "数据库" +participant S as "搜索引擎" +participant K as "缓存" +participant E as "编码/响应" +C->>G : "HTTP 请求" +G->>M1 : "注入/透传请求ID" +M1->>M2 : "继续处理" +M2->>H : "匹配路由并调用处理器" +alt "品牌/型号查询" +H->>R : "List(ctx, filters)" +R->>D : "QueryContext(ctx, sql, args)" +D-->>R : "Rows" +R-->>H : "领域模型列表" +else "型号列表搜索" +H->>S : "ModelList(ctx, key, count)" +S-->>H : "搜索结果" +else "设备上报" +H->>K : "HSet(ctx, key, field, value)" +K-->>H : "OK" +end +H->>E : "根据参数选择JSON或Base64编码" +E-->>C : "HTTP 响应" +``` + +图表来源 +- [internal/router/router.go:14-42](file://internal/router/router.go#L14-L42) +- [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/handler/device.go:26-85](file://internal/handler/device.go#L26-L85) +- [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/database/mysql.go:14-47](file://internal/database/mysql.go#L14-L47) +- [internal/search/meilisearch.go:22-46](file://internal/search/meilisearch.go#L22-L46) +- [internal/cache/redis.go:10-17](file://internal/cache/redis.go#L10-L17) +- [pkg/encode/base64.go:35-52](file://pkg/encode/base64.go#L35-L52) +- [internal/response/response.go:15-37](file://internal/response/response.go#L15-L37) + +## 详细组件分析 + +### 路由与中间件 +- 路由注册:在路由层集中注册健康检查、品牌、型号、型号列表、设备上报等端点,并通过分组划分版本与业务域。 +- 中间件栈:恢复、请求 ID、日志、CORS 依次执行,确保异常不中断服务、请求具备唯一标识、日志包含耗时与状态码、跨域策略生效。 +- 请求 ID 设计:若客户端未提供 X-Request-ID,则生成随机十六进制字符串并回传,便于端到端追踪。 +- 日志中间件:记录状态码、方法、路径、延迟、客户端 IP、请求 ID、查询参数与错误集合,按状态分级输出。 + +章节来源 +- [internal/router/router.go:14-42](file://internal/router/router.go#L14-L42) +- [internal/middleware/request_id.go:10-31](file://internal/middleware/request_id.go#L10-L31) +- [internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) + +### 处理器层 +- 品牌处理器:接收品牌名称过滤参数,调用品牌仓储查询,支持可选 Base64 响应编码。 +- 型号处理器:接收品牌与型号名称过滤参数,调用型号仓储查询,支持可选 Base64 响应编码。 +- 型号列表处理器:接收关键词与数量参数,调用搜索引擎客户端查询,支持可选 Base64 响应编码。 +- 设备上报处理器:接收 MAC、型号、版本与 X-Forwarded-For 等参数,进行参数校验与日志记录,将设备信息序列化后写入 Redis Hash。 + +```mermaid +classDiagram +class Router { ++New(log, db, search, redis) Engine +} +class BrandHandler { +-repo BrandRepository +-log Logger ++GetBrand(c) +} +class ModelHandler { +-repo ModelRepository +-log Logger ++GetModel(c) +} +class ModelListHandler { +-search SearchClient +-log Logger ++ModelList(c) +} +class DeviceHandler { +-redis RedisClient +-log Logger ++ReportDevInfo(c) +} +Router --> BrandHandler : "注册路由" +Router --> ModelHandler : "注册路由" +Router --> ModelListHandler : "注册路由" +Router --> DeviceHandler : "注册路由" +``` + +图表来源 +- [internal/router/router.go:21-25](file://internal/router/router.go#L21-L25) +- [internal/handler/brand.go:14-24](file://internal/handler/brand.go#L14-L24) +- [internal/handler/model.go:14-24](file://internal/handler/model.go#L14-L24) +- [internal/handler/model_list.go:14-24](file://internal/handler/model_list.go#L14-L24) +- [internal/handler/device.go:14-24](file://internal/handler/device.go#L14-L24) + +章节来源 +- [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/handler/device.go:26-85](file://internal/handler/device.go#L26-L85) + +### 仓储层 +- 品牌仓储:支持模糊过滤的品牌列表查询,返回领域模型集合。 +- 型号仓储:支持按品牌名精确过滤或按型号名模糊过滤,返回领域模型集合;默认无过滤时返回空列表。 +- 扫描逻辑:统一使用数据库 Rows 扫描,将 Null 字段转换为指针类型,避免空值污染。 + +```mermaid +flowchart TD +Start(["进入仓储方法"]) --> Normalize["标准化输入参数"] +Normalize --> BuildQuery{"构建查询条件"} +BuildQuery --> |品牌过滤| QBrand["SQL: 按品牌名过滤"] +BuildQuery --> |型号过滤| QModel["SQL: 模糊匹配型号名"] +BuildQuery --> |无过滤| Empty["返回空列表"] +QBrand --> Exec["QueryContext(ctx, sql, args)"] +QModel --> Exec +Exec --> ScanLoop["逐行扫描并构造领域模型"] +ScanLoop --> Done(["返回模型列表"]) +Empty --> Done +``` + +图表来源 +- [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/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) + +### 数据库层 +- 连接配置:基于 DSN 设置字符集、时区、时间解析等参数。 +- 连接池:设置最大打开连接数、最大空闲连接数与连接最大生命周期,降低连接抖动与资源占用。 +- 健康检查:启动阶段通过 PingContext 验证连通性,失败则关闭并报错。 + +章节来源 +- [internal/database/mysql.go:14-47](file://internal/database/mysql.go#L14-L47) + +### 搜索层 +- 客户端封装:基于配置创建索引管理器实例,限定检索字段集合。 +- 检索流程:接收关键词与数量参数,调用搜索接口返回命中项,解码为映射列表。 + +章节来源 +- [internal/search/meilisearch.go:17-46](file://internal/search/meilisearch.go#L17-L46) + +### 缓存层 +- 客户端封装:基于配置创建 Redis 客户端实例,支持密码与数据库选择。 +- 设备上报:处理器将设备信息序列化后写入 Redis Hash,键为 devices,field 为 MAC 地址。 + +章节来源 +- [internal/cache/redis.go:10-17](file://internal/cache/redis.go#L10-L17) +- [internal/handler/device.go:52-85](file://internal/handler/device.go#L52-L85) + +### 响应与编码 +- 统一响应体:包含 code、message、data 字段;提供 OK、Fail、BadRequest、InternalError 等便捷函数。 +- Base64 编码:支持将任意 JSON 结构先 JSON 编码,再进行自定义字符映射的 Base64 转换;解析时默认开启 Base64 响应。 +- 处理器侧:根据参数决定直接返回 JSON 或返回 Base64 字符串。 + +章节来源 +- [internal/response/response.go:9-37](file://internal/response/response.go#L9-L37) +- [pkg/encode/base64.go:13-52](file://pkg/encode/base64.go#L13-L52) +- [internal/handler/brand.go:38-50](file://internal/handler/brand.go#L38-L50) +- [internal/handler/model.go:38-50](file://internal/handler/model.go#L38-L50) +- [internal/handler/model_list.go:44-56](file://internal/handler/model_list.go#L44-L56) + +## 依赖关系分析 +- 组件耦合度:处理器仅依赖仓储接口或外部客户端,仓储仅依赖 sql.DB 或搜索/缓存客户端,保持低耦合。 +- 接口设计原则:统一使用 context 传递取消/超时信号;查询方法返回 error 以便上层统一处理;响应体结构固定,便于前端消费。 +- 错误传播:仓储与外部客户端均返回包装后的错误,处理器捕获后统一记录日志并返回内部错误响应。 +- 并发控制:数据库连接池由 sql.DB 统一管理;Redis 客户端为线程安全;Gin 默认并发处理请求。 + +```mermaid +graph LR +H1["BrandHandler"] --> R1["BrandRepository"] +H2["ModelHandler"] --> R2["ModelRepository"] +H3["ModelListHandler"] --> S1["Search.Client"] +H4["DeviceHandler"] --> K1["Redis.Client"] +R1 --> DB["sql.DB"] +R2 --> DB +``` + +图表来源 +- [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/model_list.go:19-24](file://internal/handler/model_list.go#L19-L24) +- [internal/handler/device.go:19-24](file://internal/handler/device.go#L19-L24) +- [internal/repository/brand.go:16-18](file://internal/repository/brand.go#L16-L18) +- [internal/repository/model.go:16-18](file://internal/repository/model.go#L16-L18) +- [internal/database/mysql.go:14-47](file://internal/database/mysql.go#L14-L47) +- [internal/search/meilisearch.go:17-20](file://internal/search/meilisearch.go#L17-L20) +- [internal/cache/redis.go:10-17](file://internal/cache/redis.go#L10-L17) + +章节来源 +- [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/model_list.go:19-24](file://internal/handler/model_list.go#L19-L24) +- [internal/handler/device.go:19-24](file://internal/handler/device.go#L19-L24) +- [internal/repository/brand.go:16-18](file://internal/repository/brand.go#L16-L18) +- [internal/repository/model.go:16-18](file://internal/repository/model.go#L16-L18) + +## 性能考量 +- 连接池与生命周期:数据库连接池参数已配置,建议结合压测结果调整最大打开/空闲连接数与连接最大生命周期。 +- 上下文超时:处理器统一使用请求上下文,建议在路由层或中间件为长耗时操作设置合理超时。 +- 编码优化:Base64 编码会增加体积,仅在必要场景启用;可考虑压缩或分页策略。 +- 搜索限制:搜索端 count 参数默认上限为 100,建议根据业务需求与索引规模动态调整。 +- 缓存写入:Redis 写入为单键写入,建议评估批量写入或管道命令以减少 RTT。 +- 日志开销:日志中间件会记录请求详情,生产环境建议降低采样率或使用异步日志。 + +## 故障排查指南 +- 数据库连接失败:检查 DSN 参数、网络连通性与 Ping 超时;查看连接池配置是否合理。 +- 搜索异常:确认索引存在、API Key 正确、Host 可达;关注搜索返回的错误码与消息。 +- Redis 写入失败:确认地址、密码、数据库编号正确;检查键空间与过期策略。 +- 处理器错误:查看处理器日志中记录的错误堆栈;确认参数校验与编码流程是否正常。 +- 响应异常:确认响应体结构与编码开关;核对前端是否正确解析 Base64。 + +章节来源 +- [internal/database/mysql.go:37-47](file://internal/database/mysql.go#L37-L47) +- [internal/search/meilisearch.go:22-46](file://internal/search/meilisearch.go#L22-L46) +- [internal/cache/redis.go:10-17](file://internal/cache/redis.go#L10-L17) +- [internal/handler/brand.go:30-36](file://internal/handler/brand.go#L30-L36) +- [internal/handler/model.go:31-44](file://internal/handler/model.go#L31-L44) +- [internal/handler/model_list.go:37-42](file://internal/handler/model_list.go#L37-L42) +- [internal/handler/device.go:60-78](file://internal/handler/device.go#L60-L78) +- [internal/response/response.go:30-37](file://internal/response/response.go#L30-L37) + +## 结论 +该架构通过清晰的分层与职责分离,实现了 Router → Handler → Repository → Database 的稳定数据流;借助中间件统一横切能力、响应体与编码策略,提升了可观测性与兼容性;错误传播与异常处理遵循统一模式,便于维护与扩展。建议在生产环境中进一步完善超时控制、缓存批量写入与日志采样,以获得更优的吞吐与稳定性。 + +## 附录 +- 入口与服务生命周期:参考 [cmd/server/main.go:22-96](file://cmd/server/main.go#L22-L96) +- 路由与中间件:参考 [internal/router/router.go:14-42](file://internal/router/router.go#L14-L42)、[internal/middleware/request_id.go:10-31](file://internal/middleware/request_id.go#L10-L31)、[internal/middleware/logger.go:10-46](file://internal/middleware/logger.go#L10-L46) +- 处理器与响应编码:参考 [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/handler/device.go:26-85](file://internal/handler/device.go#L26-L85)、[internal/response/response.go:9-37](file://internal/response/response.go#L9-L37)、[pkg/encode/base64.go:13-52](file://pkg/encode/base64.go#L13-L52) +- 仓储与数据库:参考 [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/database/mysql.go:14-47](file://internal/database/mysql.go#L14-L47) +- 搜索与缓存:参考 [internal/search/meilisearch.go:17-46](file://internal/search/meilisearch.go#L17-L46)、[internal/cache/redis.go:10-17](file://internal/cache/redis.go#L10-L17) \ No newline at end of file diff --git a/.qoder/repowiki/zh/content/系统架构/路由系统.md b/.qoder/repowiki/zh/content/系统架构/路由系统.md new file mode 100644 index 0000000..9b341af --- /dev/null +++ b/.qoder/repowiki/zh/content/系统架构/路由系统.md @@ -0,0 +1,437 @@ +# 路由系统 + + +**本文引用的文件** +- [router.go](file://internal/router/router.go) +- [main.go](file://cmd/server/main.go) +- [cors.go](file://internal/middleware/cors.go) +- [logger.go](file://internal/middleware/logger.go) +- [request_id.go](file://internal/middleware/request_id.go) +- [health.go](file://internal/handler/health.go) +- [brand.go](file://internal/handler/brand.go) +- [model.go](file://internal/handler/model.go) +- [model_list.go](file://internal/handler/model_list.go) +- [device.go](file://internal/handler/device.go) +- [response.go](file://internal/response/response.go) +- [config.go](file://internal/config/config.go) +- [base64.go](file://pkg/encode/base64.go) +- [README.md](file://README.md) + + +## 目录 +1. [简介](#简介) +2. [项目结构](#项目结构) +3. [核心组件](#核心组件) +4. [架构总览](#架构总览) +5. [详细组件分析](#详细组件分析) +6. [依赖关系分析](#依赖关系分析) +7. [性能考量](#性能考量) +8. [故障排查指南](#故障排查指南) +9. [结论](#结论) +10. [附录](#附录) + +## 简介 +本文件系统性梳理 Luxsin 应用 API 的路由体系与中间件机制,重点覆盖: +- Gin 路由工厂函数 New 的实现与控制流 +- 路由分组策略(/api/v1 与 /audio) +- 中间件执行顺序与职责(CORS、日志、请求 ID) +- 各端点功能、参数与返回规范 +- 最佳实践与性能优化建议 +- 典型调用序列与中间件链路分析 + +## 项目结构 +路由系统位于 internal/router,入口在 cmd/server/main.go,配合中间件、处理器、统一响应体与配置模块协同工作。 + +```mermaid +graph TB +subgraph "服务入口" +MAIN["cmd/server/main.go
启动 HTTP 服务器"] +end +subgraph "路由与中间件" +ROUTER["internal/router/router.go
路由工厂与分组注册"] +CORS["internal/middleware/cors.go
CORS 中间件"] +LOGMW["internal/middleware/logger.go
日志中间件"] +REQID["internal/middleware/request_id.go
请求 ID 中间件"] +end +subgraph "处理器层" +HEALTH["internal/handler/health.go"] +BRAND["internal/handler/brand.go"] +MODEL["internal/handler/model.go"] +MODELLIST["internal/handler/model_list.go"] +DEVICE["internal/handler/device.go"] +end +subgraph "基础设施" +RESP["internal/response/response.go
统一响应体"] +ENCODE["pkg/encode/base64.go
Base64 编解码"] +CONFIG["internal/config/config.go
配置加载"] +end +MAIN --> ROUTER +ROUTER --> CORS +ROUTER --> LOGMW +ROUTER --> REQID +ROUTER --> HEALTH +ROUTER --> BRAND +ROUTER --> MODEL +ROUTER --> MODELLIST +ROUTER --> DEVICE +BRAND --> RESP +MODEL --> RESP +MODELLIST --> RESP +DEVICE --> RESP +MODELLIST --> ENCODE +BRAND --> ENCODE +MODEL --> ENCODE +``` + +图表来源 +- [router.go:14-41](file://internal/router/router.go#L14-L41) +- [main.go:64-69](file://cmd/server/main.go#L64-L69) +- [cors.go:7-19](file://internal/middleware/cors.go#L7-L19) +- [logger.go:10-44](file://internal/middleware/logger.go#L10-L44) +- [request_id.go:20-29](file://internal/middleware/request_id.go#L20-L29) +- [health.go:14-18](file://internal/handler/health.go#L14-L18) +- [brand.go:26-49](file://internal/handler/brand.go#L26-L49) +- [model.go:26-49](file://internal/handler/model.go#L26-L49) +- [model_list.go:26-55](file://internal/handler/model_list.go#L26-L55) +- [device.go:26-84](file://internal/handler/device.go#L26-L84) +- [response.go:15-36](file://internal/response/response.go#L15-L36) +- [base64.go:44-51](file://pkg/encode/base64.go#L44-L51) +- [config.go:18-56](file://internal/config/config.go#L18-L56) + +章节来源 +- [router.go:14-41](file://internal/router/router.go#L14-L41) +- [main.go:22-95](file://cmd/server/main.go#L22-L95) +- [README.md:19-123](file://README.md#L19-L123) + +## 核心组件 +- 路由工厂函数 New:集中初始化 Gin 引擎、注册全局中间件、创建各处理器实例,并完成路由分组与注册。 +- 路由分组: + - /api/v1:版本化健康检查端点 + - /audio:音频设备相关业务端点 +- 中间件链:Recovery -> RequestID -> Logger -> CORS +- 统一响应体:OK/Fail/BadRequest/InternalError,保证一致的返回结构 + +章节来源 +- [router.go:14-41](file://internal/router/router.go#L14-L41) +- [response.go:15-36](file://internal/response/response.go#L15-L36) + +## 架构总览 +下图展示了从请求进入至响应返回的完整链路,包括中间件执行顺序与处理器调用。 + +```mermaid +sequenceDiagram +participant C as "客户端" +participant G as "Gin 引擎" +participant R as "路由分组(/api/v1 或 /audio)" +participant M1 as "中间件 : Recovery" +participant M2 as "中间件 : RequestID" +participant M3 as "中间件 : Logger" +participant M4 as "中间件 : CORS" +participant H as "处理器" +C->>G : "HTTP 请求" +G->>M1 : "进入中间件链" +M1->>M2 : "继续" +M2->>M3 : "继续" +M3->>M4 : "继续" +M4->>R : "匹配路由分组" +R->>H : "调用对应处理器" +H-->>G : "写入响应" +G-->>C : "HTTP 响应" +``` + +图表来源 +- [router.go:14-41](file://internal/router/router.go#L14-L41) +- [logger.go:10-44](file://internal/middleware/logger.go#L10-L44) +- [request_id.go:20-29](file://internal/middleware/request_id.go#L20-L29) +- [cors.go:7-19](file://internal/middleware/cors.go#L7-L19) + +## 详细组件分析 + +### 路由工厂与中间件链 +- 工厂函数 New 创建 Gin 引擎并按顺序注册中间件,确保异常恢复、请求追踪、日志记录与跨域支持贯穿所有路由。 +- 中间件顺序决定日志统计的粒度与错误兜底能力,建议保持现有顺序以获得最佳可观测性与稳定性。 + +章节来源 +- [router.go:14-19](file://internal/router/router.go#L14-L19) +- [logger.go:10-44](file://internal/middleware/logger.go#L10-L44) +- [request_id.go:20-29](file://internal/middleware/request_id.go#L20-L29) +- [cors.go:7-19](file://internal/middleware/cors.go#L7-L19) + +### 路由分组与端点清单 +- /api/v1 分组 + - GET /api/v1/health → 健康检查 +- /audio 分组 + - GET /audio/getBrand → 品牌列表 + - GET /audio/getModel → 型号列表 + - GET /audio/modelList → 模型搜索列表 + - GET /audio/reportDevInfo → 设备信息上报 + +章节来源 +- [router.go:27-38](file://internal/router/router.go#L27-L38) +- [README.md:83-110](file://README.md#L83-L110) + +### 中间件详解 + +#### CORS 中间件 +- 设置允许来源、方法、头字段与暴露头 +- 对预检请求直接返回状态码 +- 放行后续处理器执行 + +章节来源 +- [cors.go:7-19](file://internal/middleware/cors.go#L7-L19) + +#### 日志中间件 +- 记录请求路径、方法、耗时、状态码、客户端 IP、请求 ID、查询参数与错误信息 +- 按状态码分级输出(info/warn/error) + +章节来源 +- [logger.go:10-44](file://internal/middleware/logger.go#L10-L44) + +#### 请求 ID 中间件 +- 从请求头读取或生成唯一标识,注入上下文并回传给客户端 +- 用于全链路追踪与问题定位 + +章节来源 +- [request_id.go:20-29](file://internal/middleware/request_id.go#L20-L29) + +### 处理器与端点行为 + +#### 健康检查 /api/v1/health +- 功能:返回服务运行状态 +- 参数:无 +- 返回:统一响应体,data 包含状态字段 + +章节来源 +- [health.go:14-18](file://internal/handler/health.go#L14-L18) +- [response.go:15-21](file://internal/response/response.go#L15-L21) + +#### 品牌列表 /audio/getBrand +- 功能:按品牌名模糊查询品牌列表 +- 查询参数: + - brandName:品牌名称(可选) + - base64Resp:是否返回 Base64 编码结果(默认 true) +- 行为: + - 调用仓库层查询 + - 可选地对结果进行 JSON 编码后返回 + +章节来源 +- [brand.go:26-49](file://internal/handler/brand.go#L26-L49) +- [base64.go:44-51](file://pkg/encode/base64.go#L44-L51) + +#### 型号列表 /audio/getModel +- 功能:按品牌或型号关键词查询型号列表 +- 查询参数: + - brandName:品牌名称(可选) + - modelName:型号名称(可选) + - base64Resp:是否返回 Base64 编码结果(默认 true) +- 行为: + - 调用仓库层查询 + - 可选地对结果进行 JSON 编码后返回 + +章节来源 +- [model.go:26-49](file://internal/handler/model.go#L26-L49) +- [base64.go:44-51](file://pkg/encode/base64.go#L44-L51) + +#### 模型搜索列表 /audio/modelList +- 功能:基于关键字与数量限制进行模型检索 +- 查询参数: + - key:搜索关键字 + - count:返回条数上限(可选,默认较大值) + - base64Resp:是否返回 Base64 编码结果(默认 true) +- 行为: + - 调用搜索引擎客户端查询 + - 可选地对结果进行 JSON 编码后返回 + +章节来源 +- [model_list.go:26-55](file://internal/handler/model_list.go#L26-L55) +- [base64.go:44-51](file://pkg/encode/base64.go#L44-L51) + +#### 设备信息上报 /audio/reportDevInfo +- 功能:接收设备 MAC、型号、版本与来源 IP,写入缓存 +- 查询参数: + - mac:设备 MAC 地址(必填) + - model:设备型号(必填) + - ver:版本号(可选) +- 行为: + - 校验必填参数 + - 组装设备信息并写入缓存 + - 返回统一响应体 + +章节来源 +- [device.go:26-84](file://internal/handler/device.go#L26-L84) + +### 统一响应体 +- OK:成功响应,code=0,message="ok" +- Fail:通用错误,携带业务 code 与 message +- BadRequest:客户端错误 +- InternalError:服务端错误 + +章节来源 +- [response.go:15-36](file://internal/response/response.go#L15-L36) + +### 路由注册流程(工厂模式) +- 初始化 Gin 引擎 +- 注册全局中间件(Recovery、RequestID、Logger、CORS) +- 实例化各处理器(数据库/搜索引擎/缓存客户端注入) +- 创建路由分组并注册端点 +- 返回引擎供 HTTP 服务器使用 + +```mermaid +flowchart TD +Start(["调用 New"]) --> Init["创建 Gin 引擎"] +Init --> UseRecovery["注册 Recovery 中间件"] +UseRecovery --> UseReqID["注册 RequestID 中间件"] +UseReqID --> UseLogger["注册 Logger 中间件"] +UseLogger --> UseCORS["注册 CORS 中间件"] +UseCORS --> NewHandlers["创建各处理器实例"] +NewHandlers --> GroupV1["创建 /api/v1 分组"] +GroupV1 --> RegHealth["注册 /api/v1/health"] +NewHandlers --> GroupAudio["创建 /audio 分组"] +GroupAudio --> RegBrand["注册 /audio/getBrand"] +GroupAudio --> RegModel["注册 /audio/getModel"] +GroupAudio --> RegModelList["注册 /audio/modelList"] +GroupAudio --> RegDevice["注册 /audio/reportDevInfo"] +RegDevice --> ReturnEngine["返回引擎"] +``` + +图表来源 +- [router.go:14-41](file://internal/router/router.go#L14-L41) + +## 依赖关系分析 +- 路由层依赖中间件层与处理器层 +- 处理器层依赖响应体与编码工具 +- 服务启动层负责装配配置、数据库、搜索引擎与缓存,并将它们注入路由工厂 + +```mermaid +graph LR +MAIN["cmd/server/main.go"] --> ROUTER["internal/router/router.go"] +ROUTER --> MW_REQID["internal/middleware/request_id.go"] +ROUTER --> MW_LOG["internal/middleware/logger.go"] +ROUTER --> MW_CORS["internal/middleware/cors.go"] +ROUTER --> H_HEALTH["internal/handler/health.go"] +ROUTER --> H_BRAND["internal/handler/brand.go"] +ROUTER --> H_MODEL["internal/handler/model.go"] +ROUTER --> H_MODELLIST["internal/handler/model_list.go"] +ROUTER --> H_DEVICE["internal/handler/device.go"] +H_BRAND --> RESP["internal/response/response.go"] +H_MODEL --> RESP +H_MODELLIST --> RESP +H_DEVICE --> RESP +H_MODELLIST --> ENCODE["pkg/encode/base64.go"] +H_BRAND --> ENCODE +H_MODEL --> ENCODE +``` + +图表来源 +- [main.go:64](file://cmd/server/main.go#L64) +- [router.go:21-25](file://internal/router/router.go#L21-L25) +- [response.go:15-36](file://internal/response/response.go#L15-L36) +- [base64.go:44-51](file://pkg/encode/base64.go#L44-L51) + +章节来源 +- [main.go:32-62](file://cmd/server/main.go#L32-L62) +- [router.go:14-41](file://internal/router/router.go#L14-L41) + +## 性能考量 +- 中间件顺序与开销 + - Recovery 放在首位,避免重复包裹 + - RequestID/CORS/LR 等轻量中间件顺序合理,尽量减少阻塞 +- 日志中间件 + - 建议仅在必要时记录查询参数,避免大对象日志 + - 控制错误日志频率,防止雪崩 +- 编码优化 + - base64Resp 仅在需要时开启,避免不必要的编码成本 +- 路由分组 + - 将版本化与业务域分离,便于未来扩展与限流策略落地 + +## 故障排查指南 +- 健康检查失败 + - 检查 /api/v1/health 是否可达 + - 关注日志中间件输出的状态码与耗时 +- CORS 相关问题 + - 确认浏览器预检请求已正确处理 + - 核对允许的方法与头字段 +- 请求 ID 未返回 + - 检查客户端是否正确传递与读取 X-Request-ID +- 处理器错误 + - 查看处理器日志中的错误字段 + - 使用统一响应体的 code/message 定位问题 +- 编码异常 + - base64Resp 开启时确认客户端解析逻辑 + +章节来源 +- [logger.go:33-43](file://internal/middleware/logger.go#L33-L43) +- [request_id.go:22-27](file://internal/middleware/request_id.go#L22-L27) +- [cors.go:9-12](file://internal/middleware/cors.go#L9-L12) +- [response.go:23-36](file://internal/response/response.go#L23-L36) + +## 结论 +本路由系统采用清晰的工厂模式与中间件链设计,结合版本化与业务域分组,具备良好的可维护性与扩展性。遵循现有中间件顺序与统一响应体规范,有助于提升可观测性与稳定性。建议在新增端点时严格复用现有中间件与响应体,确保一致性与性能。 + +## 附录 + +### 端点一览与参数说明 +- /api/v1/health + - 方法:GET + - 参数:无 + - 返回:统一响应体 +- /audio/getBrand + - 方法:GET + - 参数: + - brandName:字符串(可选) + - base64Resp:布尔或数字字符串(可选,默认 true) + - 返回:统一响应体 +- /audio/getModel + - 方法:GET + - 参数: + - brandName:字符串(可选) + - modelName:字符串(可选) + - base64Resp:布尔或数字字符串(可选,默认 true) + - 返回:统一响应体 +- /audio/modelList + - 方法:GET + - 参数: + - key:字符串(必填) + - count:整数(可选,默认较大值) + - base64Resp:布尔或数字字符串(可选,默认 true) + - 返回:统一响应体 +- /audio/reportDevInfo + - 方法:GET + - 参数: + - mac:字符串(必填) + - model:字符串(必填) + - ver:字符串(可选) + - 返回:统一响应体 + +章节来源 +- [router.go:29](file://internal/router/router.go#L29) +- [brand.go:27-28](file://internal/handler/brand.go#L27-L28) +- [model.go:27-28](file://internal/handler/model.go#L27-L28) +- [model_list.go:27-35](file://internal/handler/model_list.go#L27-L35) +- [device.go:27-28](file://internal/handler/device.go#L27-L28) + +### 中间件链路分析(序列图) +```mermaid +sequenceDiagram +participant Client as "客户端" +participant Engine as "Gin 引擎" +participant Recovery as "Recovery" +participant ReqID as "RequestID" +participant Logger as "Logger" +participant CORS as "CORS" +participant Handler as "处理器" +Client->>Engine : "请求" +Engine->>Recovery : "进入" +Recovery->>ReqID : "继续" +ReqID->>Logger : "继续" +Logger->>CORS : "继续" +CORS->>Handler : "匹配路由并调用" +Handler-->>Engine : "写入响应" +Engine-->>Client : "响应" +``` + +图表来源 +- [router.go:16-19](file://internal/router/router.go#L16-L19) +- [logger.go:16](file://internal/middleware/logger.go#L16) +- [request_id.go:28](file://internal/middleware/request_id.go#L28) +- [cors.go:18](file://internal/middleware/cors.go#L18) \ No newline at end of file diff --git a/.qoder/repowiki/zh/content/部署运维.md b/.qoder/repowiki/zh/content/部署运维.md new file mode 100644 index 0000000..58ffa1b --- /dev/null +++ b/.qoder/repowiki/zh/content/部署运维.md @@ -0,0 +1,344 @@ +# 部署运维 + + +**本文引用的文件** +- [cmd/server/main.go](file://cmd/server/main.go) +- [internal/config/config.go](file://internal/config/config.go) +- [internal/config/database.go](file://internal/config/database.go) +- [internal/config/redis.go](file://internal/config/redis.go) +- [internal/config/meilisearch.go](file://internal/config/meilisearch.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) +- [internal/router/router.go](file://internal/router/router.go) +- [internal/middleware/cors.go](file://internal/middleware/cors.go) +- [internal/middleware/logger.go](file://internal/middleware/logger.go) +- [pkg/logger/logger.go](file://pkg/logger/logger.go) +- [Makefile](file://Makefile) +- [go.mod](file://go.mod) +- [README.md](file://README.md) + + +## 目录 +1. [简介](#简介) +2. [项目结构](#项目结构) +3. [核心组件](#核心组件) +4. [架构总览](#架构总览) +5. [详细组件分析](#详细组件分析) +6. [依赖分析](#依赖分析) +7. [性能考虑](#性能考虑) +8. [故障排除指南](#故障排除指南) +9. [结论](#结论) +10. [附录](#附录) + +## 简介 +本运维文档面向 Luxsin 应用 API 项目的部署与运行维护,覆盖构建流程、环境配置与差异、数据库与外部服务连接、容器化与编排部署建议、监控与日志、性能优化、故障排查、备份恢复与版本升级、安全与合规以及自动化与 CI/CD 集成要点。目标是帮助运维人员快速、稳定地完成部署与日常运维。 + +## 项目结构 +该应用采用分层与功能模块化组织,核心入口在命令行程序,配置集中于内部包,业务路由与中间件位于独立模块,日志封装在可复用包中。关键目录与职责概览: +- cmd/server:应用入口,负责初始化配置、连接数据库与缓存、启动 HTTP 服务器、优雅关闭 +- internal/config:集中加载与校验环境变量,生成运行所需配置 +- internal/database:MySQL 连接与连接池配置 +- internal/cache:Redis 客户端初始化 +- internal/search:Meilisearch 客户端初始化与检索封装 +- internal/router:路由注册与中间件装配 +- internal/middleware:CORS、请求日志、请求 ID 等中间件 +- pkg/logger:Zap 日志配置(开发/生产差异化) +- Makefile:常用构建与测试命令 +- go.mod:Go 模块与依赖声明 + +```mermaid +graph TB +subgraph "应用进程" +MAIN["cmd/server/main.go"] +ROUTER["internal/router/router.go"] +MW_CORS["internal/middleware/cors.go"] +MW_LOGGER["internal/middleware/logger.go"] +CFG["internal/config/config.go"] +LOGPKG["pkg/logger/logger.go"] +end +subgraph "外部服务" +MYSQL["MySQL 数据库"] +REDIS["Redis 缓存"] +MEILI["Meilisearch 搜索"] +end +MAIN --> CFG +MAIN --> LOGPKG +MAIN --> ROUTER +ROUTER --> MW_CORS +ROUTER --> MW_LOGGER +MAIN --> MYSQL +MAIN --> REDIS +MAIN --> MEILI +``` + +图表来源 +- [cmd/server/main.go:1-96](file://cmd/server/main.go#L1-L96) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) +- [internal/middleware/cors.go:7-19](file://internal/middleware/cors.go#L7-L19) +- [internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45) +- [internal/config/config.go:18-56](file://internal/config/config.go#L18-L56) +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) + +章节来源 +- [README.md:5-17](file://README.md#L5-L17) +- [go.mod:1-47](file://go.mod#L1-L47) + +## 核心组件 +- 配置加载与校验:集中于 config 包,支持从环境变量覆盖默认值,并对生产环境进行强制校验(如数据库密码) +- 数据库连接:使用 MySQL 驱动,配置连接池参数并在启动时进行连通性校验 +- 缓存连接:Redis 客户端初始化,支持主机、端口、密码、库号 +- 搜索服务:Meilisearch 客户端初始化,提供模型列表检索能力 +- 路由与中间件:Gin 路由注册,内置 CORS、请求日志、请求 ID、恢复中间件 +- 日志:Zap 生产/开发差异化配置,按状态输出不同级别日志 + +章节来源 +- [internal/config/config.go:18-56](file://internal/config/config.go#L18-L56) +- [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/meilisearch.go:14-51](file://internal/config/meilisearch.go#L14-L51) +- [internal/database/mysql.go:14-47](file://internal/database/mysql.go#L14-L47) +- [internal/cache/redis.go:10-17](file://internal/cache/redis.go#L10-L17) +- [internal/search/meilisearch.go:17-46](file://internal/search/meilisearch.go#L17-L46) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) +- [internal/middleware/cors.go:7-19](file://internal/middleware/cors.go#L7-L19) +- [internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45) +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) + +## 架构总览 +应用启动流程:读取配置 → 初始化日志 → 连接数据库/缓存/搜索 → 注册路由与中间件 → 启动 HTTP 服务器 → 监听系统信号优雅退出。 + +```mermaid +sequenceDiagram +participant OS as "操作系统" +participant MAIN as "main.go" +participant CFG as "config.Load()" +participant LOG as "logger.New()" +participant DB as "database.Open()" +participant RS as "cache.NewClient()" +participant MS as "search.NewClient()" +participant RT as "router.New()" +participant HTTP as "http.Server" +OS->>MAIN : 启动进程 +MAIN->>CFG : 加载配置 +MAIN->>LOG : 创建日志实例 +MAIN->>DB : 打开数据库连接 +MAIN->>RS : 初始化 Redis 客户端 +MAIN->>MS : 初始化 Meilisearch 客户端 +MAIN->>RT : 构建路由引擎 +MAIN->>HTTP : 启动 HTTP 服务器 +OS-->>MAIN : 发送终止信号 +MAIN->>HTTP : 优雅关闭 +``` + +图表来源 +- [cmd/server/main.go:22-96](file://cmd/server/main.go#L22-L96) +- [internal/config/config.go:18-56](file://internal/config/config.go#L18-L56) +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) +- [internal/database/mysql.go:14-47](file://internal/database/mysql.go#L14-L47) +- [internal/cache/redis.go:10-17](file://internal/cache/redis.go#L10-L17) +- [internal/search/meilisearch.go:17-20](file://internal/search/meilisearch.go#L17-L20) +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) + +## 详细组件分析 + +### 配置与环境管理 +- 环境变量键与默认值 + - 运行环境:APP_ENV(默认 development),用于切换生产模式与日志配置 + - 监听地址与端口:APP_HOST、APP_PORT(默认 0.0.0.0:8080) + - Gin 运行模式:GIN_MODE(由 APP_ENV 控制,生产模式使用 ReleaseMode) + - 数据库:DATABASE_HOST、DATABASE_PORT、DATABASE_NAME、DATABASE_USER、DATABASE_PASSWORD(生产环境必须提供 DATABASE_PASSWORD) + - Redis:REDIS_HOST、REDIS_PORT、REDIS_PASSWORD、REDIS_DATABASE + - Meilisearch:MEILISEARCH_HOST、MEILISEARCH_API_KEY、MEILISEARCH_INDEX +- 环境差异 + - 开发环境:本地 MySQL、公网 Meilisearch、默认 Redis 地址 + - 生产环境:AWS RDS 主机、内网 Meilisearch 主机、固定 Redis 参数 +- 校验规则 + - 数据库:生产环境必须提供密码;必填项校验 + - Redis:必填主机 + - Meilisearch:必填主机、API Key、索引名 + +章节来源 +- [internal/config/config.go:18-56](file://internal/config/config.go#L18-L56) +- [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/meilisearch.go:14-51](file://internal/config/meilisearch.go#L14-L51) +- [README.md:39-73](file://README.md#L39-L73) + +### 数据库连接与连接池 +- 连接参数:用户、密码、TCP 地址、数据库名、字符集与时区参数 +- 连接池:最大并发、空闲连接数、连接生命周期 +- 启动校验:超时上下文下执行 Ping,失败则关闭并返回错误 + +章节来源 +- [internal/database/mysql.go:14-47](file://internal/database/mysql.go#L14-L47) + +### 缓存与搜索 +- Redis:按主机:端口、密码、库号初始化客户端 +- Meilisearch:按主机与 API Key 初始化索引客户端,提供模型列表检索方法 + +章节来源 +- [internal/cache/redis.go:10-17](file://internal/cache/redis.go#L10-L17) +- [internal/search/meilisearch.go:17-46](file://internal/search/meilisearch.go#L17-L46) + +### 路由与中间件 +- 路由分组:/api/v1(健康检查)、/audio(品牌、型号、设备上报、模型列表等) +- 中间件:恢复、请求 ID、日志、CORS +- 日志字段:状态码、方法、路径、耗时、客户端 IP、请求 ID、查询参数、错误信息 + +章节来源 +- [internal/router/router.go:14-41](file://internal/router/router.go#L14-L41) +- [internal/middleware/logger.go:10-45](file://internal/middleware/logger.go#L10-L45) +- [internal/middleware/cors.go:7-19](file://internal/middleware/cors.go#L7-L19) + +### 日志与运行模式 +- 开发模式:开发配置,彩色日志等级编码 +- 生产模式:生产配置,ISO 时间编码 +- Gin 模式:生产模式启用 ReleaseMode + +章节来源 +- [pkg/logger/logger.go:8-19](file://pkg/logger/logger.go#L8-L19) +- [cmd/server/main.go:28-30](file://cmd/server/main.go#L28-L30) + +## 依赖分析 +- 外部依赖:Gin、MySQL 驱动、Meilisearch SDK、Redis SDK、Zap +- 内部耦合:main.go 依赖 config、database、cache、search、router、logger;router 依赖 handler、middleware、search、redis;middleware 依赖 Gin 与 Zap + +```mermaid +graph LR +MAIN["cmd/server/main.go"] --> CFG["internal/config/*"] +MAIN --> DB["internal/database/mysql.go"] +MAIN --> RC["internal/cache/redis.go"] +MAIN --> SRCH["internal/search/meilisearch.go"] +MAIN --> RT["internal/router/router.go"] +RT --> MW["internal/middleware/*"] +RT --> HND["internal/handler/*"] +MAIN --> LOG["pkg/logger/logger.go"] +``` + +图表来源 +- [cmd/server/main.go:13-18](file://cmd/server/main.go#L13-L18) +- [internal/router/router.go:3-12](file://internal/router/router.go#L3-L12) + +章节来源 +- [go.mod:5-46](file://go.mod#L5-L46) + +## 性能考虑 +- 连接池与超时 + - 数据库连接池:最大并发、空闲连接、连接生命周期,减少连接抖动与资源占用 + - 启动 Ping 超时:避免冷启动阻塞 +- Gin 服务器超时 + - 读取超时、写入超时、空闲超时,防止慢请求与资源泄漏 +- 日志级别 + - 错误与警告输出到生产日志,降低高基数日志对性能影响 +- 搜索与缓存 + - 合理设置搜索 Limit 与 AttributesToRetrieve,避免返回过多字段 + - 缓存命中率优先,避免频繁访问上游服务 + +章节来源 +- [internal/database/mysql.go:33-35](file://internal/database/mysql.go#L33-L35) +- [cmd/server/main.go:66-72](file://cmd/server/main.go#L66-L72) +- [internal/middleware/logger.go:37-43](file://internal/middleware/logger.go#L37-L43) + +## 故障排除指南 +- 启动失败(数据库连接) + - 现象:启动日志显示数据库连接失败 + - 排查:确认 DATABASE_HOST/PORT/NAME/USER/PASSWORD;生产环境必须提供 DATABASE_PASSWORD;检查网络连通与安全组 +- 启动失败(Redis 连接) + - 现象:无法连接缓存 + - 排查:确认 REDIS_HOST/PORT/Password/Database;检查网络与认证 +- 启动失败(Meilisearch 连接) + - 现象:搜索初始化失败或查询报错 + - 排查:确认 MEILISEARCH_HOST/APIKey/Index;检查索引是否存在与权限 +- 健康检查 + - 访问 /api/v1/health,确认服务可用 +- 日志定位 + - 查看请求日志中的状态码、路径、耗时、请求 ID,结合错误字段定位问题 + +章节来源 +- [cmd/server/main.go:40-48](file://cmd/server/main.go#L40-L48) +- [internal/config/database.go:57-71](file://internal/config/database.go#L57-L71) +- [internal/config/redis.go:51-56](file://internal/config/redis.go#L51-L56) +- [internal/config/meilisearch.go:39-50](file://internal/config/meilisearch.go#L39-L50) +- [internal/middleware/logger.go:22-43](file://internal/middleware/logger.go#L22-L43) +- [README.md:83-99](file://README.md#L83-L99) + +## 结论 +本项目提供了清晰的配置加载、中间件与路由结构,以及对数据库、缓存与搜索服务的标准化接入。通过明确的环境变量与校验规则,配合生产/开发差异化日志与 Gin 模式,可实现稳定高效的部署与运维。建议在生产环境中严格管理密钥与网络访问控制,并结合监控与日志体系完善可观测性。 + +## 附录 + +### 构建与运行 +- 构建:使用 Makefile 的 build 目标生成二进制 +- 运行:使用 Makefile 的 run 目标或直接运行二进制 +- 测试:使用 Makefile 的 test 目标 + +章节来源 +- [Makefile:3-13](file://Makefile#L3-L13) +- [README.md:75-81](file://README.md#L75-L81) + +### 环境变量与默认值 +- APP_ENV、APP_HOST、APP_PORT、GIN_MODE +- DATABASE_*、REDIS_*、MEILISEARCH_* + +章节来源 +- [internal/config/config.go:18-56](file://internal/config/config.go#L18-L56) +- [README.md:39-73](file://README.md#L39-L73) + +### Docker 容器化部署建议 +- 基础镜像:使用官方 Go 镜像进行多阶段构建,最终运行精简基础镜像 +- 构建步骤:在构建阶段执行 go build 产出二进制,运行阶段仅拷贝二进制与必要资源 +- 环境变量:通过镜像启动参数注入 APP_ENV、DATABASE_*、REDIS_*、MEILISEARCH_* 等 +- 健康检查:暴露 /api/v1/health,使用 HTTP 方式探测 +- 日志:容器标准输出采集,结合日志驱动输出到集中日志系统 + +[本节为通用容器化建议,不直接对应具体源文件] + +### Kubernetes 部署示例要点 +- Deployment:副本数、资源限制、探针(Liveness/Readiness) +- Service:ClusterIP/LoadBalancer,暴露监听端口 +- ConfigMap:存放非敏感配置(如 APP_ENV) +- Secret:存放数据库密码、Redis 密码、Meilisearch API Key +- Ingress:域名与 TLS(如需要) +- HPA:基于 CPU/自定义指标扩缩容 + +[本节为通用编排建议,不直接对应具体源文件] + +### 监控与告警 +- 指标:QPS、P95/P99 延迟、错误率、连接池使用率、搜索延迟 +- 日志:请求日志、错误日志、启动/关闭事件 +- 告警:错误率阈值、延迟阈值、连接池耗尽、外部服务不可用 + +[本节为通用监控建议,不直接对应具体源文件] + +### 备份与恢复 +- 数据库:定期逻辑备份与增量备份,验证恢复流程 +- 缓存:关注热数据重建策略,避免单点失效 +- 配置:Secret/ConfigMap 版本化管理,变更审计 + +[本节为通用备份建议,不直接对应具体源文件] + +### 版本升级流程 +- 预发布:灰度最小集群,验证健康检查与关键接口 +- 升级:滚动更新,观察指标与日志 +- 回滚:快速回滚至上一个稳定版本 +- 文档:记录变更与回滚步骤 + +[本节为通用升级建议,不直接对应具体源文件] + +### 安全与合规 +- 最小权限:数据库、缓存、搜索服务账号只授予必要权限 +- 网络隔离:生产网络与开发网络分离,安全组放通最小范围 +- 密钥管理:通过 Secret 管理密钥,禁用明文存储 +- 合规:日志保留策略、访问审计、数据加密传输 + +[本节为通用安全建议,不直接对应具体源文件] + +### 自动化部署与 CI/CD 集成 +- 构建:在 CI 中执行 go mod tidy、go test、go build +- 扫描:静态扫描与依赖漏洞扫描 +- 镜像:构建镜像并推送制品库 +- 部署:Kubernetes 应用清单与版本标签管理 +- 回滚:支持一键回滚至上一个版本 + +[本节为通用 CI/CD 建议,不直接对应具体源文件] \ No newline at end of file diff --git a/.qoder/repowiki/zh/content/项目概述.md b/.qoder/repowiki/zh/content/项目概述.md new file mode 100644 index 0000000..400001a --- /dev/null +++ b/.qoder/repowiki/zh/content/项目概述.md @@ -0,0 +1,335 @@ +# 项目概述 + + +**本文引用的文件** +- [README.md](file://README.md) +- [main.go](file://cmd/server/main.go) +- [config.go](file://internal/config/config.go) +- [router.go](file://internal/router/router.go) +- [health.go](file://internal/handler/health.go) +- [brand.go](file://internal/handler/brand.go) +- [model.go](file://internal/handler/model.go) +- [device.go](file://internal/handler/device.go) +- [brand_repo.go](file://internal/repository/brand.go) +- [response.go](file://internal/response/response.go) +- [logger_mw.go](file://internal/middleware/logger.go) +- [cors_mw.go](file://internal/middleware/cors.go) +- [brand_model.go](file://internal/model/brand.go) +- [model_model.go](file://internal/model/model.go) +- [go.mod](file://go.mod) + + +## 目录 +1. [简介](#简介) +2. [项目结构](#项目结构) +3. [核心组件](#核心组件) +4. [架构总览](#架构总览) +5. [详细组件分析](#详细组件分析) +6. [依赖分析](#依赖分析) +7. [性能考虑](#性能考虑) +8. [故障排查指南](#故障排查指南) +9. [结论](#结论) +10. [附录](#附录) + +## 简介 +本项目是一个基于 Gin 框架的 Go 语言 Web API 服务,专注于音频设备(耳机)数据管理。它采用清晰的分层架构,围绕“配置—路由—中间件—处理器—仓储—模型—响应”的结构组织代码,提供健康检查、品牌与型号查询、设备信息上报等能力,并通过统一响应体、日志与跨域中间件保证易用性与可观测性。 + +项目目标与定位: +- 提供稳定、可扩展的音频设备数据 API 能力 +- 以 Gin 为核心,结合 MySQL、Redis、Meilisearch 等外部组件,满足查询、缓存与搜索需求 +- 通过统一响应体与中间件体系,降低接入成本,提升调试与运维效率 + +## 项目结构 +项目采用按职责分层的目录组织方式,便于维护与扩展: +- cmd/server:应用入口,负责初始化配置、数据库、搜索引擎与缓存客户端,构建路由引擎并启动 HTTP 服务器 +- internal/config:集中加载与校验运行时配置(环境、主机、端口、数据库、搜索引擎、Redis) +- internal/router:路由注册与中间件装配,划分 /api/v1 与 /audio 两组路径空间 +- internal/handler:HTTP 处理器,承载业务逻辑,调用仓储与外部服务 +- internal/repository:数据访问层,封装 SQL 查询与结果映射 +- internal/model:领域模型定义,用于序列化与传输 +- internal/response:统一响应体封装,规范返回结构 +- internal/middleware:通用中间件(日志、CORS、请求 ID) +- internal/search、internal/cache:对外部搜索与缓存服务的薄封装 +- pkg:可复用工具模块(如编码、日志) +- sql:数据库初始化脚本 + +```mermaid +graph TB +subgraph "应用入口" +MAIN["cmd/server/main.go"] +end +subgraph "配置层" +CFG["internal/config/config.go"] +end +subgraph "路由与中间件" +RT["internal/router/router.go"] +MW_LOG["internal/middleware/logger.go"] +MW_CORS["internal/middleware/cors.go"] +end +subgraph "处理器" +H_HEALTH["internal/handler/health.go"] +H_BRAND["internal/handler/brand.go"] +H_MODEL["internal/handler/model.go"] +H_DEVICE["internal/handler/device.go"] +end +subgraph "仓储层" +REPO_BRAND["internal/repository/brand.go"] +end +subgraph "模型与响应" +M_BRAND["internal/model/brand.go"] +M_MODEL["internal/model/model.go"] +RESP["internal/response/response.go"] +end +subgraph "外部服务" +MYSQL["MySQL"] +REDIS["Redis"] +MEILI["Meilisearch"] +end +MAIN --> CFG +MAIN --> RT +RT --> MW_LOG +RT --> MW_CORS +RT --> H_HEALTH +RT --> H_BRAND +RT --> H_MODEL +RT --> H_DEVICE +H_BRAND --> REPO_BRAND +REPO_BRAND --> MYSQL +H_BRAND --> RESP +H_MODEL --> RESP +H_HEALTH --> RESP +H_DEVICE --> REDIS +``` + +图表来源 +- [main.go:22-95](file://cmd/server/main.go#L22-L95) +- [config.go:18-56](file://internal/config/config.go#L18-L56) +- [router.go:14-41](file://internal/router/router.go#L14-L41) +- [brand.go:19-24](file://internal/handler/brand.go#L19-L24) +- [brand_repo.go:16-18](file://internal/repository/brand.go#L16-L18) +- [response.go:15-21](file://internal/response/response.go#L15-L21) + +章节来源 +- [README.md:5-17](file://README.md#L5-L17) +- [main.go:22-95](file://cmd/server/main.go#L22-L95) +- [router.go:14-41](file://internal/router/router.go#L14-L41) + +## 核心组件 +- 配置加载与校验:集中读取环境变量,自动选择开发/生产数据库配置,校验搜索引擎与 Redis 配置的有效性 +- 路由与中间件:统一装配恢复、请求 ID、日志与跨域中间件;按 /api/v1 与 /audio 分组注册端点 +- 处理器层:品牌与型号查询处理器、健康检查处理器、设备信息上报处理器 +- 仓储层:SQL 查询封装,支持模糊匹配与排序 +- 统一响应体:标准化返回结构,简化前端对接 +- 外部集成:MySQL(持久化)、Redis(设备上报缓存/会话)、Meilisearch(可选搜索) + +章节来源 +- [config.go:18-56](file://internal/config/config.go#L18-L56) +- [router.go:14-41](file://internal/router/router.go#L14-L41) +- [brand_repo.go:20-50](file://internal/repository/brand.go#L20-L50) +- [response.go:9-37](file://internal/response/response.go#L9-L37) + +## 架构总览 +系统采用“入口初始化—配置—路由装配—处理器—仓储—外部服务”的线性控制流。入口负责加载配置、建立数据库与外部服务连接、构建 Gin 引擎并启动 HTTP 服务器;路由层装配中间件并注册各业务端点;处理器负责参数解析、调用仓储或外部服务、输出统一响应;仓储层封装 SQL 访问;响应体统一返回结构。 + +```mermaid +sequenceDiagram +participant Client as "客户端" +participant Main as "入口(main.go)" +participant Cfg as "配置(config.go)" +participant Router as "路由(router.go)" +participant Handler as "处理器(handler)" +participant Repo as "仓储(repository)" +participant DB as "MySQL" +Client->>Main : 启动进程 +Main->>Cfg : 加载配置 +Main->>Router : 构建引擎并注册路由 +Client->>Router : 发起请求 /audio/getBrand +Router->>Handler : 调用处理器 +Handler->>Repo : 执行查询 +Repo->>DB : 执行 SQL +DB-->>Repo : 返回结果 +Repo-->>Handler : 结果集 +Handler-->>Client : 统一响应体 +``` + +图表来源 +- [main.go:22-95](file://cmd/server/main.go#L22-L95) +- [config.go:18-56](file://internal/config/config.go#L18-L56) +- [router.go:14-41](file://internal/router/router.go#L14-L41) +- [brand.go:26-49](file://internal/handler/brand.go#L26-L49) +- [brand_repo.go:20-50](file://internal/repository/brand.go#L20-L50) + +## 详细组件分析 + +### 入口与生命周期管理 +- 初始化阶段:加载配置、设置 Gin 运行模式、初始化日志、建立数据库连接、配置搜索引擎与 Redis 客户端 +- 服务启动:创建 HTTP 服务器,设置超时参数,异步启动监听 +- 优雅关闭:捕获系统信号,执行超时上下文下的优雅停机 + +```mermaid +flowchart TD +Start(["进程启动"]) --> LoadCfg["加载配置"] +LoadCfg --> SetMode["设置 Gin 模式"] +SetMode --> InitLogger["初始化日志"] +InitLogger --> OpenDB["打开数据库连接"] +OpenDB --> InitSearch["初始化搜索引擎"] +InitSearch --> InitRedis["初始化 Redis"] +InitRedis --> BuildEngine["构建路由引擎"] +BuildEngine --> StartHTTP["启动 HTTP 服务"] +StartHTTP --> WaitSignal["等待系统信号"] +WaitSignal --> Graceful["优雅关闭"] +Graceful --> Stop(["进程结束"]) +``` + +图表来源 +- [main.go:22-95](file://cmd/server/main.go#L22-L95) + +章节来源 +- [main.go:22-95](file://cmd/server/main.go#L22-L95) + +### 配置模块 +- 支持从环境变量读取运行环境、主机、端口、数据库、搜索引擎与 Redis 配置 +- 自动选择开发/生产数据库默认值,支持通过 DATABASE_* 环境变量覆盖 +- 对搜索引擎与 Redis 配置进行有效性校验 + +章节来源 +- [config.go:18-56](file://internal/config/config.go#L18-L56) +- [README.md:39-73](file://README.md#L39-L73) + +### 路由与中间件 +- 中间件链:Recovery → RequestID → Logger → CORS +- 路由分组:/api/v1(健康检查)、/audio(品牌/型号/设备相关接口) +- 统一日志记录:记录状态码、方法、路径、延迟、客户端 IP、请求 ID 等 + +章节来源 +- [router.go:14-41](file://internal/router/router.go#L14-L41) +- [logger_mw.go:10-45](file://internal/middleware/logger.go#L10-L45) +- [cors_mw.go:7-20](file://internal/middleware/cors.go#L7-L20) + +### 品牌查询处理器 +- 功能:支持按品牌名称模糊查询,返回品牌列表;可选 Base64 编码响应 +- 流程:解析查询参数 → 调用仓储 → 错误处理 → 统一响应 + +```mermaid +sequenceDiagram +participant Client as "客户端" +participant Router as "路由" +participant Handler as "BrandHandler" +participant Repo as "BrandRepository" +participant DB as "MySQL" +Client->>Router : GET /audio/getBrand?brandName=... +Router->>Handler : 调用 GetBrand +Handler->>Repo : List(ctx, brandName) +Repo->>DB : 执行查询 +DB-->>Repo : 结果集 +Repo-->>Handler : 列表 +Handler-->>Client : 统一响应体 +``` + +图表来源 +- [brand.go:26-49](file://internal/handler/brand.go#L26-L49) +- [brand_repo.go:20-50](file://internal/repository/brand.go#L20-L50) + +章节来源 +- [brand.go:26-49](file://internal/handler/brand.go#L26-L49) +- [brand_repo.go:20-50](file://internal/repository/brand.go#L20-L50) + +### 型号查询处理器 +- 功能:支持按品牌与型号名称组合查询,返回型号列表;可选 Base64 编码响应 +- 流程:解析查询参数 → 调用仓储 → 错误处理 → 统一响应 + +章节来源 +- [model.go:26-50](file://internal/handler/model.go#L26-L50) + +### 设备信息上报处理器 +- 功能:接收设备 MAC 地址、型号、版本与来源 IP,写入 Redis Hash 存储 +- 参数校验:必填字段校验,缺失时返回错误响应 +- 日志记录:记录远程 IP 与时间戳,便于审计与排障 + +章节来源 +- [device.go:26-84](file://internal/handler/device.go#L26-L84) + +### 健康检查处理器 +- 功能:返回服务健康状态,统一响应体 +- 适用:容器编排与负载均衡探活 + +章节来源 +- [health.go:14-18](file://internal/handler/health.go#L14-L18) + +### 统一响应体 +- 规范:包含 code、message、data 字段 +- 工具函数:OK、Fail、BadRequest、InternalError,便于在处理器中快速返回 + +章节来源 +- [response.go:9-37](file://internal/response/response.go#L9-L37) + +### 数据模型 +- 品牌模型:包含 id 与 name +- 型号模型:包含品牌名、名称、形态、刚性、来源、EQ 键、创建时间等可空字段 + +章节来源 +- [brand_model.go:3-6](file://internal/model/brand.go#L3-L6) +- [model_model.go:5-14](file://internal/model/model.go#L5-L14) + +## 依赖分析 +- 技术栈概览:Go 1.24+、Gin、MySQL、Redis、Meilisearch、Zap +- 模块依赖:入口依赖配置、数据库、缓存、路由与搜索模块;路由依赖处理器与中间件;处理器依赖仓储与响应体;仓储依赖数据库驱动;统一响应体被所有处理器使用 + +```mermaid +graph LR +MAIN["cmd/server/main.go"] --> CFG["internal/config/config.go"] +MAIN --> RT["internal/router/router.go"] +MAIN --> DB["internal/database/*"] +MAIN --> RC["internal/cache/*"] +MAIN --> SRCH["internal/search/*"] +RT --> H1["internal/handler/health.go"] +RT --> H2["internal/handler/brand.go"] +RT --> H3["internal/handler/model.go"] +RT --> H4["internal/handler/device.go"] +H2 --> REPO["internal/repository/brand.go"] +H3 --> RESP["internal/response/response.go"] +H4 --> REDIS["Redis"] +REPO --> MYSQL["MySQL"] +``` + +图表来源 +- [go.mod:1-47](file://go.mod#L1-L47) +- [main.go:13-18](file://cmd/server/main.go#L13-L18) +- [router.go:21-25](file://internal/router/router.go#L21-L25) + +章节来源 +- [go.mod:1-47](file://go.mod#L1-L47) + +## 性能考虑 +- Gin 运行模式:生产环境启用 ReleaseMode,减少调试开销 +- 超时配置:HTTP 服务器设置读取、写入与空闲超时,避免资源占用 +- 日志级别:按状态码区分 Info/Warn/Error,避免高频错误日志影响性能 +- 查询优化:仓储层使用参数化查询与 LIKE 模糊匹配,建议在数据库侧为常用查询列建立索引 +- 缓存策略:设备上报使用 Redis Hash,建议结合过期策略与键空间清理 + +## 故障排查指南 +- 健康检查:通过 /api/v1/health 快速确认服务可用性 +- 日志定位:中间件记录请求详情与错误,结合请求 ID 快速定位问题 +- 数据库连接:入口日志会打印数据库连接信息,若连接失败需检查环境变量与网络连通性 +- Redis 写入:设备上报失败通常为 Redis HSet 异常,需检查 Redis 服务状态与键空间权限 +- 统一错误:处理器内部错误通过统一响应体返回,前端可根据 code/message 快速识别 + +章节来源 +- [README.md:83-99](file://README.md#L83-L99) +- [logger_mw.go:37-43](file://internal/middleware/logger.go#L37-L43) +- [device.go:71-78](file://internal/handler/device.go#L71-L78) + +## 结论 +本项目以 Gin 为核心,结合配置、路由、中间件、处理器、仓储与统一响应体的清晰分层,构建了面向音频设备数据管理的 API 服务。通过 MySQL、Redis、Meilisearch 的合理集成,满足查询、缓存与可扩展搜索的需求。项目结构清晰、易于扩展,适合初学者快速上手与资深开发者深度定制。 + +## 附录 +- 常见使用场景示例(基于现有端点) + - 健康检查:GET /api/v1/health + - 品牌列表:GET /audio/getBrand + - 品牌模糊查询:GET /audio/getBrand?brandName=sony + - 型号查询:GET /audio/getModel?brandName=sony&modelName=wh1000xm4 + - 设备信息上报:GET /audio/reportDevInfo?mac=XX:XX:XX:XX:XX:XX&model=WH-XXXX&ver=1.0 + +章节来源 +- [README.md:101-116](file://README.md#L101-L116) +- [router.go:27-38](file://internal/router/router.go#L27-L38) \ No newline at end of file diff --git a/.qoder/repowiki/zh/meta/repowiki-metadata.json b/.qoder/repowiki/zh/meta/repowiki-metadata.json new file mode 100644 index 0000000..8c21e30 --- /dev/null +++ b/.qoder/repowiki/zh/meta/repowiki-metadata.json @@ -0,0 +1 @@ +{"knowledge_relations":[{"id":1,"source_id":"ab98c251-c5f7-40df-bba1-21b2edf5d220","target_id":"523107ca-244f-43c5-a4fe-4649e36a88ad","source_type":"WIKI_ITEM","target_type":"WIKI_ITEM","relationship_type":"PARENT_CHILD","extra":"Wiki parent-child relationship: ab98c251-c5f7-40df-bba1-21b2edf5d220 -\u003e 523107ca-244f-43c5-a4fe-4649e36a88ad","gmt_create":"2026-05-27T16:00:38.0397713+08:00","gmt_modified":"2026-05-27T16:00:38.0397713+08:00"},{"id":2,"source_id":"ab98c251-c5f7-40df-bba1-21b2edf5d220","target_id":"f4014d47-467e-4cfe-8549-1dfb69cbc325","source_type":"WIKI_ITEM","target_type":"WIKI_ITEM","relationship_type":"PARENT_CHILD","extra":"Wiki parent-child relationship: ab98c251-c5f7-40df-bba1-21b2edf5d220 -\u003e f4014d47-467e-4cfe-8549-1dfb69cbc325","gmt_create":"2026-05-27T16:00:38.0417677+08:00","gmt_modified":"2026-05-27T16:00:38.0417677+08:00"},{"id":3,"source_id":"ab98c251-c5f7-40df-bba1-21b2edf5d220","target_id":"5910d326-0408-450d-b403-06a19f3da81b","source_type":"WIKI_ITEM","target_type":"WIKI_ITEM","relationship_type":"PARENT_CHILD","extra":"Wiki parent-child relationship: ab98c251-c5f7-40df-bba1-21b2edf5d220 -\u003e 5910d326-0408-450d-b403-06a19f3da81b","gmt_create":"2026-05-27T16:00:38.0417677+08:00","gmt_modified":"2026-05-27T16:00:38.0417677+08:00"},{"id":4,"source_id":"ab98c251-c5f7-40df-bba1-21b2edf5d220","target_id":"e8e5fe7e-cc7c-4259-95d0-f6b46d888ed8","source_type":"WIKI_ITEM","target_type":"WIKI_ITEM","relationship_type":"PARENT_CHILD","extra":"Wiki parent-child relationship: ab98c251-c5f7-40df-bba1-21b2edf5d220 -\u003e e8e5fe7e-cc7c-4259-95d0-f6b46d888ed8","gmt_create":"2026-05-27T16:00:38.0417677+08:00","gmt_modified":"2026-05-27T16:00:38.0417677+08:00"},{"id":5,"source_id":"ab98c251-c5f7-40df-bba1-21b2edf5d220","target_id":"5a04f737-b877-4908-a4fc-67a88614aafc","source_type":"WIKI_ITEM","target_type":"WIKI_ITEM","relationship_type":"PARENT_CHILD","extra":"Wiki parent-child relationship: ab98c251-c5f7-40df-bba1-21b2edf5d220 -\u003e 5a04f737-b877-4908-a4fc-67a88614aafc","gmt_create":"2026-05-27T16:00:38.0427691+08:00","gmt_modified":"2026-05-27T16:00:38.0427691+08:00"},{"id":6,"source_id":"7563e383-3ea0-4630-baeb-e86efcaeefb8","target_id":"d1a696b8-5a9e-4ad2-8777-04eecc0bb424","source_type":"WIKI_ITEM","target_type":"WIKI_ITEM","relationship_type":"PARENT_CHILD","extra":"Wiki parent-child relationship: 7563e383-3ea0-4630-baeb-e86efcaeefb8 -\u003e d1a696b8-5a9e-4ad2-8777-04eecc0bb424","gmt_create":"2026-05-27T16:00:38.0427691+08:00","gmt_modified":"2026-05-27T16:00:38.0427691+08:00"},{"id":7,"source_id":"7563e383-3ea0-4630-baeb-e86efcaeefb8","target_id":"599c0bda-3332-415c-a535-362503079473","source_type":"WIKI_ITEM","target_type":"WIKI_ITEM","relationship_type":"PARENT_CHILD","extra":"Wiki parent-child relationship: 7563e383-3ea0-4630-baeb-e86efcaeefb8 -\u003e 599c0bda-3332-415c-a535-362503079473","gmt_create":"2026-05-27T16:00:38.0437677+08:00","gmt_modified":"2026-05-27T16:00:38.0437677+08:00"},{"id":8,"source_id":"7563e383-3ea0-4630-baeb-e86efcaeefb8","target_id":"97c42095-5b4f-4cec-bbbb-1ba8bb9981d4","source_type":"WIKI_ITEM","target_type":"WIKI_ITEM","relationship_type":"PARENT_CHILD","extra":"Wiki parent-child relationship: 7563e383-3ea0-4630-baeb-e86efcaeefb8 -\u003e 97c42095-5b4f-4cec-bbbb-1ba8bb9981d4","gmt_create":"2026-05-27T16:00:38.0437677+08:00","gmt_modified":"2026-05-27T16:00:38.0437677+08:00"},{"id":9,"source_id":"7563e383-3ea0-4630-baeb-e86efcaeefb8","target_id":"699a84c5-5140-4267-861e-62bffbffccb4","source_type":"WIKI_ITEM","target_type":"WIKI_ITEM","relationship_type":"PARENT_CHILD","extra":"Wiki parent-child relationship: 7563e383-3ea0-4630-baeb-e86efcaeefb8 -\u003e 699a84c5-5140-4267-861e-62bffbffccb4","gmt_create":"2026-05-27T16:00:38.0442739+08:00","gmt_modified":"2026-05-27T16:00:38.0442739+08:00"},{"id":10,"source_id":"6159e8a9-0f6e-43af-b097-bfcd16c8b223","target_id":"6f1fe170-ae35-4660-91dc-ab89735274b7","source_type":"WIKI_ITEM","target_type":"WIKI_ITEM","relationship_type":"PARENT_CHILD","extra":"Wiki parent-child relationship: 6159e8a9-0f6e-43af-b097-bfcd16c8b223 -\u003e 6f1fe170-ae35-4660-91dc-ab89735274b7","gmt_create":"2026-05-27T16:00:38.0452805+08:00","gmt_modified":"2026-05-27T16:00:38.0452805+08:00"},{"id":11,"source_id":"6159e8a9-0f6e-43af-b097-bfcd16c8b223","target_id":"6af33e3a-220d-4e68-87ed-bfb96bda4c3a","source_type":"WIKI_ITEM","target_type":"WIKI_ITEM","relationship_type":"PARENT_CHILD","extra":"Wiki parent-child relationship: 6159e8a9-0f6e-43af-b097-bfcd16c8b223 -\u003e 6af33e3a-220d-4e68-87ed-bfb96bda4c3a","gmt_create":"2026-05-27T16:00:38.0452805+08:00","gmt_modified":"2026-05-27T16:00:38.0452805+08:00"},{"id":12,"source_id":"6159e8a9-0f6e-43af-b097-bfcd16c8b223","target_id":"e1ed4fc8-f1d6-4e8a-907b-9c39bbe13b92","source_type":"WIKI_ITEM","target_type":"WIKI_ITEM","relationship_type":"PARENT_CHILD","extra":"Wiki parent-child relationship: 6159e8a9-0f6e-43af-b097-bfcd16c8b223 -\u003e e1ed4fc8-f1d6-4e8a-907b-9c39bbe13b92","gmt_create":"2026-05-27T16:00:38.0452805+08:00","gmt_modified":"2026-05-27T16:00:38.0452805+08:00"},{"id":13,"source_id":"6159e8a9-0f6e-43af-b097-bfcd16c8b223","target_id":"0fc3d59c-d219-4f97-af10-473ee62f4de5","source_type":"WIKI_ITEM","target_type":"WIKI_ITEM","relationship_type":"PARENT_CHILD","extra":"Wiki parent-child relationship: 6159e8a9-0f6e-43af-b097-bfcd16c8b223 -\u003e 0fc3d59c-d219-4f97-af10-473ee62f4de5","gmt_create":"2026-05-27T16:00:38.0462819+08:00","gmt_modified":"2026-05-27T16:00:38.0462819+08:00"},{"id":14,"source_id":"cfbcaa79-26f5-402f-8746-9fe524993684","target_id":"795a5202-49be-4c11-a2b4-44312eca8ae7","source_type":"WIKI_ITEM","target_type":"WIKI_ITEM","relationship_type":"PARENT_CHILD","extra":"Wiki parent-child relationship: cfbcaa79-26f5-402f-8746-9fe524993684 -\u003e 795a5202-49be-4c11-a2b4-44312eca8ae7","gmt_create":"2026-05-27T16:00:38.0462819+08:00","gmt_modified":"2026-05-27T16:00:38.0462819+08:00"},{"id":15,"source_id":"cfbcaa79-26f5-402f-8746-9fe524993684","target_id":"d0c2dea0-a71a-4f16-9889-b37e1e7996e3","source_type":"WIKI_ITEM","target_type":"WIKI_ITEM","relationship_type":"PARENT_CHILD","extra":"Wiki parent-child relationship: cfbcaa79-26f5-402f-8746-9fe524993684 -\u003e d0c2dea0-a71a-4f16-9889-b37e1e7996e3","gmt_create":"2026-05-27T16:00:38.0462819+08:00","gmt_modified":"2026-05-27T16:00:38.0462819+08:00"},{"id":16,"source_id":"cfbcaa79-26f5-402f-8746-9fe524993684","target_id":"23a2ffc3-fa50-41ba-b7a6-55354d68a95a","source_type":"WIKI_ITEM","target_type":"WIKI_ITEM","relationship_type":"PARENT_CHILD","extra":"Wiki parent-child relationship: cfbcaa79-26f5-402f-8746-9fe524993684 -\u003e 23a2ffc3-fa50-41ba-b7a6-55354d68a95a","gmt_create":"2026-05-27T16:00:38.0472824+08:00","gmt_modified":"2026-05-27T16:00:38.0472824+08:00"}],"wiki_catalogs":[{"id":"78b906f9-f81c-4bb2-b076-5c5609c7eb41","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"项目概述","description":"project-overview","prompt":"为 Luxsin 应用 API 项目创建全面的项目概述内容。详细介绍项目的目标、核心功能和技术架构。解释这是一个基于 Gin 框架的 Go 语言 Web API 服务,专注于音频设备(耳机)数据管理。阐述项目的整体设计理念、分层架构模式和设计原则。包含技术栈概览(Go 1.22+、Gin、MySQL、Redis、Meilisearch),项目结构说明,以及与其他组件的关系。提供适合初学者的概念性介绍和面向经验丰富开发者的技术细节。使用与代码库一致的术语,展示常见使用场景的实际示例。","progress_status":"completed","dependent_files":"README.md,cmd/server/main.go,go.mod","gmt_create":"2026-05-27T15:42:25.0250549+08:00","gmt_modified":"2026-05-27T15:44:16.3667749+08:00","raw_data":"WikiEncrypted:0MI1/XkBoMl0lTbK6t0Cn/+8FdvqrJ62ianMLvZj02elJtBUgH0Ns0veIx1WPtM9wwkxI+/XAqHLxJLlHB8wV7ojrH0+b4JMz5OewwhBN1HmP/0rRww9hUnTCEZmNJO3yBTSwzDP2RxlM1vNSo2miB47OWpHJe/fbeK8YpuBtRbc1xpai4QWWh4AweGy0HEvW9apev3dOSAiTr8UoRTgRToJyEaxWrqZcs+MtHKRAsIrcUTt6ZOh39uAbnNwRxK9M2/DiD+kawezpj/AwiANKfd4KpHSMbzYKq9YTP0ksJkGEAjH/XwM331T75uP1oZ/psvBENTK57EgxUJQIxxl/qi9stybX0Smmey1LJ+iJDAfMqZYbs5BbkxPPLpvmXX8GX/lHaSvSv8bbbhTLnYiN+DawAvAhSIRex/qStKvjVXbzHUwrLiXLR5XSvCX75e6L5cIB4b+Mx86121DipgZvZjF7zEMzArfk0N5yZkwD5XI7a3kzfmH2BaXJoEdULrvDFX7ssODrPjklVgc2gbh9DYJXJ/eua5QA3Xx1lsmOuAQB7a98gCmD6wgY1fk9B5rsKGk7vjeRpn+VjuxS6i0t9dcHlhvXCrR7IAI7ElFcmatn18jtjyzt08iJ/2HJB3lwVKEHvC/D131ZrYoMax215Ho63ABv5IhWhlOacH3EHlX6kLvYaCiaCq7fkoW8IUfIUEJGPYMNiPdNXw8BHHJaE8Emy4eKyw7+C0K//+SPmGgkpVUXe5QBHZUCVde9qeHWFvjX7tIKhxv6DuRjUgaCTjlup8gJ49u24zpnUWcnA8qT/jl0wsGHfJi8eD0LLh0kE8XKQQ/x7QONSqCBwUYpVE0TaFITaD9jPqrfRmH7pcAVggsg7CrxVJKwukwntI7yyj4r0gvzmECsd41Q9FYOoPHu3IjRxN+OlXsp7yXlO8Bk3HtmqmpIVzy3rg5kNXkrwf98MEdjBWvI1SfpRXoliuppIkKLvshYbDq9zj7Gt5l1fDkWJrFTA59HAAa3qxQ1UvtlTV3x563VZo0DZofv2rHQaWxyL8gslH21B3jt1SClPzpPPC2ERp1waFgf/K6NltgqZg+qvCivqr8SOjn4ASj8olJuvcUYIqGIh4Glws6tboVkX7ItQuueGikKKhSflv4RIB28KosR6Y5EvB8oDFTNknPtjjj8fDMAdLye5AhzMlBtH3Ozvd7tR8DbP77ZPX3WIncMv8HHSXGmn8IwwggXYIKJ5fpuepkStOcJLSafRsijI2Jji/9y8ULNSvIJgGHlfsd+ytl0t8xFik3QfYqBtbBaYKauIWmP3opbaM="},{"id":"67d62b58-6970-45ca-a17f-f48cb2ea679a","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"业务处理器","description":"business-handlers","prompt":"为 Luxsin 应用 API 项目的业务处理器模块创建详细文档。深入解释各个处理器的职责分工、接口设计和实现细节。详细说明 BrandHandler、ModelHandler、ModelListHandler、DeviceHandler 和 HealthHandler 的具体功能和使用方法。包含依赖注入模式的实现、错误处理机制和响应格式化。提供具体的代码示例展示如何调用各个处理器,以及它们与 Gin 框架的集成方式。解释处理器之间的协作关系和数据流转过程。涵盖性能优化建议、并发安全考虑和最佳实践。确保内容对初学者易懂,同时为高级开发者提供深入的技术细节。","parent_id":"8d7c257e-b299-4adb-adfe-bc0ddbf112b7","progress_status":"completed","dependent_files":"internal/handler/brand.go,internal/handler/model.go,internal/handler/model_list.go,internal/handler/device.go,internal/handler/health.go","gmt_create":"2026-05-27T15:42:38.0124453+08:00","gmt_modified":"2026-05-27T15:51:42.2445399+08:00","raw_data":"WikiEncrypted:mHLzoBKW/ak+KVdbda4A2TGAInZDJgXlECT8druWKh8OykgsyIoYcoUTqr4Fw4DzZjw9/0iaTuDDaCPF2bZgsMHxE08kUup5hhcoUYECp7d99GeMS8R4CTVERl7REkP2utRFDlwT9Jh0g0hSxDMtpdcR958E75ZBS/W2oWkIWPAto9enmDcw/sSxKCuMQXp9fsT1MXN9kOOiVzWleDEY0U3oTuHKLmEvs+dWYfSsDFTXDRcIVEALSBQVqRIEd/c92EKkhvg/lLEO7L++t+YXcsZTkRHoDJEIh+OIC+pkdL0MIwxYY4wahv5b4uzryipaEvpnxxfPOFygc5zYX9CHqL2Btxke54KRUKEajsMBPOxaTbtDyLRwU+132qz+vehrvktcPAcSuQyBUgAf29NhKu5oQthYO8cpbrkOAWdi/Ygch6bORtmWxWLVMbtJ61Y0yXjwQeO8PnygcWa8Iez2tpvLK24vFSF/8Sqe+oh43zL3nziMqBPAdUVZpdLVqIzLkNKJSxjKTYFKp55K1Yi+mLCzwS82tNnR9FDzpH71pSQYLb5w8CoRhfHaUX2fWGQDgPUFinfC1h49jYb8WvCPmw5SFP9hvGjETRu8Pv78NWur40ODvurxm5kpdWlQLMfB4XXHy5bEXYHwDW4Jd29ACDCnc0UO8wI8/nayfLjwMLuGIsS2U+6ma3cLaEj5pPEOVIXl1WFCj63pTaGf9bOIu+P8j9ZmphdohoCVkEKbVWSHAanwB8tIuKxG+v93M9qpoIhkzilwT8n33wFinGv/kTHivLXzTie9NBTW31qrWjdRiYEpBsvmIbCi196i19NlSzuE/n82ArLDwkBwiMgTU+fcOq9L9EPhNHUJx3FOK4JIO6wqNpMwoBx0mDq2y426Vr/62xFnwLZKZgtNKMvxXhfsyGvqtweslKECMH5FzCgWdME/yyrXGjv7JOaLAYjZsTu4PUDYIaVQcvG37AzS4ij8xV81jhsAnHn4gAgPHvGFh2oysEMDEjnlb1H7a1zsTLpcmrVupZ4fR/PdK9FgUJc275uwCGUAv2va940tBBStlC8lFA/yLTEabls3eY++qFdm7fnn0O+NUustVzyLTVCIkGRisgMJciRjFUTn3buxuhYGb+6/TZnz0/f1YmIGrrfSCx427GNPglWuztbwVgSJMHP9mllbS8jP+Yz5eqHFV8CKefug3SgyjwjadCvkx4ykxwu69lNxPPTPhfFnxet6Irz9yB4C5NdnpQxeeIFARl7LWYTb8pZBuwGx9IHG/+PObGh+pzpoXEljzdsoVJ5RUaIpeqI2mjw47chGdNfpVjPO9aCUW/tVEpJunWwFCPD9SrGnofnOh7b5Ojur+auqtRdDv3RCef4lkmdz3qZzRHWgkO4/420Di1JCJPFAuZBrt0QLwcmCyTVrAaukE+YJk0wXynCPh5Qkw0HU9OShCva8Z2NxEy2gXL59E5phikfBQzdXTY6mnEqri4EJ4HpG5GadVZxqY+e78wWoqyykG4eonHnoUlMvlCN9sV+MFBSox11gnGZZFglKwPxhxRdu1ygdSH4EjCh+0tjdpq6fFoFUZKnTlZYdhyfHCID9pnxb5okiUgp4G1SaJ6zsjABPhnl5Bb3iO1zs2OxaRoITJ7BZaUbnTrCk8cf00asF","layer_level":1},{"id":"591ea147-ba05-4d86-a671-941af668ca2f","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"品牌管理接口","description":"brand-management-api","prompt":"为品牌管理接口创建详细的 API 文档。记录获取品牌列表的接口规范,包括 HTTP 方法、URL 模式、请求参数(brandName 查询参数)、响应格式和错误处理。提供具体的请求示例和响应示例,说明如何查询特定品牌或所有品牌信息。详细说明 Base64 编码响应选项的使用方法和适用场景。包含常见的使用场景、客户端实现指南和性能优化建议。提供错误处理策略和调试方法。","parent_id":"8085e1b7-3658-4490-8b52-9a61f3e79976","progress_status":"completed","dependent_files":"internal/handler/brand.go","gmt_create":"2026-05-27T15:42:40.190927+08:00","gmt_modified":"2026-05-27T15:52:20.2742155+08:00","raw_data":"WikiEncrypted:MpYke5d2PXdqtxI+drCrLtTQMJR2qutlXYM1eplHCPm9/Q2P6fWxhBRJ5bi1YZPWaHS/NqlGVpUFmW1DTLhf/76xGG0D1/eLqVfyND560PlQohHkNSJ7tG8q8rNN0PSoYa8X/dNeOTyE1A+uNuvpiB8VEfTp3tDUy+z3wJjo7Mac9XZxl3yz7HAEroJflv+CMaOLDNpuuFAizP+9X1/8yMamdkg455SQiJ4MA3tO8lvrplKeDtAsHnZMOuRb1k8H2J1Okf9ERLFkl/hajhx/ErZkMXJcVDAafq7dea3WDtuhkapO0hPBNT0jBx23jA9D7bdwpP8HUMhRQofKSrXSGWsNkOXGoTUbtKPcvqSXhQc/9ZnwEEWeVTez5dnt0TzIm14zjRSuX8WW7fn2EC5NSDXky9F0+kI+r1SW0lJAbI3S7/PzXMppdYjT5CIvUr4oUNfXpKuj0hpO8TqMLZU7tVSVTfLxNXN9GhtdNUotltCy+fTfIRE9oX+iVuqgGtGw1XfkviF0TKtN2+lC3tYHFIpPZl0lv2u1hS9qg5UQt86ZqNo0oyYG4iMTW1/PcJjvEh+ZouSCdeb1/NZXAghNVhgC+8CAFMY8ARl4571OoYbQgSJas0Ved/ljSKxbViyBTgk/eadCH+WuFgEUJPidhbhNV95LyTxjvd4d98Fz4GQj2038lVoj9NivalyW4v3H3DG7fUSW/wraV29VqX1jG8ICAA3c60Q5kE1oNPSUR1elSjYrLy3on9k9r8INUoZBOoRUyflFm5B6kmZvm44JlzVpRLRlNpa3erHjDw9LI3thWQq/NO7w72+KvFuF8zaQB3H1VyMGzou5eP9dokkthFbhCLnEofzO48Sc+x4zNRb2ub0rcxs3APyeA95SJ/c3J9UaFr94I+ITvJ+DlDyvoZNjoHZ4/PsNi22elB2RuhO7SsbQKIU3Nh9ngyjEqTmG6c+HSPzeLezD9npjOYrsjxTcnZkMSsIAqZRSPeW1VwP+1y42+EAhmnF9vQgYw4YM","layer_level":1},{"id":"fb8ae541-8bbc-4544-9133-e797be1433e0","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"分层架构设计","description":"layered-architecture","prompt":"为 Luxsin 应用 API 项目创建详细的分层架构设计文档。深入解释 MVC 模式的变体实现,包括表现层(Router/Handler)、业务层(Repository)、数据访问层(Database)和基础设施层(Middleware/Cache/Search)的设计理念和职责划分。详细说明各层之间的依赖关系和数据流向。文档化依赖注入模式的实现方式,展示如何通过构造函数注入依赖。解释分层架构的优势、约束和最佳实践。包含具体的代码示例展示层间通信机制。提供架构图表说明组件间的交互关系。","parent_id":"0506348d-993b-48f2-936c-908972f149bc","progress_status":"completed","dependent_files":"internal/router/router.go,internal/handler/brand.go,internal/repository/brand.go,internal/database/mysql.go","gmt_create":"2026-05-27T15:42:44.0573625+08:00","gmt_modified":"2026-05-27T15:53:50.4735252+08:00","raw_data":"WikiEncrypted:Cbw4rSSRJFTkFIVuyRiw4IoOVu9h4U/miV8kuFep2zRCWdjPBNlat3QQ8xqzDjxqnwEXQuPFqO2rdKF2PS2SXAKzDQAtOrwT0N0Bb1cVz1ybTiUFKF4yLIfZkQvmd4aTaafAMRNgGZZmW7p4Ef5Gb7g6VLCa1ky2b8mBvNur7HulgiwcdOzHhqi+XnXEdfba/+4TOqQvhA8OEJsD8r89uJDnH3WUxmiulVTnh8wTsjZ/sG6IRexTh8wnXF2Z3Pp3eoQrztwivE/v2tdi/tM+xK1ZLq6CQH87mEfFjtJjTQg257NwlL8OIUf7otektOR1U4nLWYMIIFT9ZnLxCRer9o+2+pnhIDCkzxWjQGwexhyS9T7Sq09AQ78taySEDIKsmPoPyWrRxYn284OLFkbbnE1kES3YlzGpQ1BX1HzOz4I69saOR3KsmELHh7P7qZT9s8U17TqLBKRo4LWPb9lfHPc2n9CNtaojQSYBresLm4Ta7KRF27FZXJKCUr2Y7A4p1QODq8Xxz7oxjv9ItKra6FFGv7FB+vqalxx2iPAp824obAuXeSRgvdRFeIQJ6R5siQRJFuZpQJW/aigbXEWcoP3A9m/wfRe0jaxEI100Oj9QQGRz3OGUeAoz62DjwUXCX4AUuMqRKjmjvBQP/xSKnA3j/lKtduUs9QoLJ/g3nrBj+jDR9Q6FoGxuKVUCy9+0QH35x8onGEu4z2bXyyujjA6eCclxoq7ck1+KWQfmShbEqnk+bd9dTRVQMaDBv2VgymZnIMvJEvLJFyAlmW6Bqc3qZkv9C8OH+uS90l66JlXniuUHlPinlQ+3/eJiqqMldDvT3oLvYeXLWjHUquxR0LFTQ9XFrzdxuOzsjbxhKaN+FjNZWN1d/J2TdK3LtYlEMaaxGQGsUxdTQuCIQqyQTc6n+yoXpTJ7xGtIitcxvZ6lOtQnLu2VN9ID29ySBKUeNTEDo6F486yfARaPMVjKVssURgEKlt5pEWVPnPPxd5YgYqn0dNm3g+hU3Esrx9pH0r2T8ity7CL28QWZWdthEpldnZLRdQHK7yf5ZTWBjij9h7ozRglKkjrqYtDyBCwmGNGnlUN/zURSFldO1RvnFPaGPmkGSGZaMA5jBaIqKElGCIIeGQcHEvM5reoBNq6Y71tWRTS7ei1SPsvnw2EYGGgUPzY8HealmML7QlT7utxN8WnldmX+B9tvQ0HNKwQBmMzgxPI0ycy7vcx4Pb95uK2tkKZfyxCAqZBZo2/oFspYlsMKwqPF7RMfFxnD5cQi90gF48iJKU/WEb6aXUbCDZrVdhXzg6IMu6iuziM3fdFSU20A8HaJB5hfLpDSE9S7","layer_level":1},{"id":"6e42204e-3d78-4997-a95a-3311c8fcaa43","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"缓存系统","description":"cache-system","prompt":"为 Luxsin 应用 API 项目的缓存系统创建详细文档。深入介绍 Redis 缓存客户端的初始化过程、连接配置和使用方法。详细说明 NewClient 函数的实现原理,包括地址配置、密码认证、数据库选择等参数设置。解释缓存策略的设计思路和应用场景,包括数据过期时间设置、键值命名规范、缓存穿透防护等。提供具体的使用示例,展示如何在业务逻辑中集成缓存操作。包含性能优化建议、连接池配置和故障恢复机制。涵盖缓存失效策略、数据一致性保证和监控告警配置。确保内容既适合初学者快速上手,又为高级用户提供深入的技术细节和最佳实践指导。","parent_id":"0ba3accc-326a-4433-930c-5b951d4e5dcf","progress_status":"completed","dependent_files":"internal/cache/redis.go,internal/config/redis.go","gmt_create":"2026-05-27T15:42:51.5485979+08:00","gmt_modified":"2026-05-27T15:53:16.9929309+08:00","raw_data":"WikiEncrypted:dAMSC70bWva9SabC1uic9iE9nBlehG2+ui1IUMoCFjuHkunhijRkYjoVeVmKZwBm6ViPeVjUgdsTTfn5QNXituCAV63Dn4Vd4VU86FgHDUU8FFzBLhAO5CuaH9Y/oW+aNZ7pULLF6O4VpGo1zKxd+l1Dcbs5xEnd4y5BAwWyvogeJAGi8RKD5jKJiiqqH/bM5ltk+Bn4SyxvdAjHmAvipyhqqByv3c8d4DUOGnuj3Qn0OZLqSBw5+OaTL+t8G9q7B3WLFshxuJEcrrfg8XGMKetFXb45MPrgd2bqP2G57trgchjth6a0arYWIZxaBkVE514Xr2z4V+ugHohpYYQryj6JOR0A/u+L+C7bkdSMLt1jh0fu8lnj52FVwZo/Y4OjPy87RixxGb19v+mI+G/jWsXbn1FTFbTD1n3G0MevS6sxuH68ipZGNTb6E9KYJ2tySmMUuRIEojJIIP4frWcu7tArF8UR05c5poOHDXBfz3T6gH4+4BHaa1G5IMYHYTLY9oPiOxeOHZXFlXUamTUYcJG+ZuEqKK4k3eluLx4Fb3n98xZ3kzMiNMzXK8G/VwwmEeLhklpt+BjEfEzIim59fRGFRmn/LDw8ltlZBcJ+rfSGbV7VYPZ2lyW5F6D2xii+Ixww4jJ9iavDQdfJm6GvtqZC/Qzv/sbAv46jKeJ7BmQ9yjVRL4MfqnfXgmTWzSeZARfXK7ybkMW1a39ADJVqiPe8oGdrjjUxNBVwljylOYcVsVHUR26fiEi2Rugi0q2M4q6CV2djPU3eQhQewJLiJPgMEYKuUJnr5E5js1KPIJyW/HErpZKKm4oLHxAjeoZKl8bLpctLBTgckr2UTtsLSjixopn3Mo40PK2N0gj8N+XnvvqO06uSTQtsaWv4uWQoeRA9DC6Fe/kx02moWmzIZ/PAxxOy3qj0JA2qXMByM7QwJQhKAd7GKk7FnUitpTLu912fxM9BERyuHQZfFHIXIbuhzuJWx1wd19H0zlK5pnPbncZ1ULiQAYxwxZkVS8MiktlyUe/XCcrTJNahdhjsDsmLovXEJh7NefBWeN20O8qvXoOcawQJgCj0rVc7T/Vd/tWQcl55pHgjz3K5qkHTZatA70BAiIM9P0ywZLJSKA5UUqqPxnDmGfeTIyHhW6JGX8jlXpb7twh7ESw8auy7UbJhUGnOwaJFk/6JEtbd7VQ1cGnoCE0ULPmSyHTXzXDM67SJ8aNutInzdaO+QLIfPfk1Zi1f8TMlmQRwlAtu3fnQW4c8LLiIlAaMOG9d03OqmwpuIdzGIqTScDr8ihEYS6VFl70eJL9grTMc+GlNgeDDBjqKlyL9OBGopmouTorddrNJ88fo02rfiUxL2aXDgPJHAW7fVBSe7keArddrISpvv8OLWkDdlLY3bAICA42wCZEdsHbuUNBkKbe9EiNEQko8nE1qsEif5Ob5ATgpXGFxu4DwCWqsXyDrejUAPK25","layer_level":1},{"id":"6de9a03e-f360-4daa-97ac-dc1cd1937916","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"快速开始","description":"getting-started","prompt":"创建 Luxsin 应用 API 项目的快速开始指南。详细说明环境要求(Go 1.22+)、依赖安装步骤(go mod tidy)、配置设置(.env 文件配置)和运行方法。包含开发环境和生产环境的不同配置方式,数据库连接配置,以及如何使用 Makefile 进行构建和运行。提供具体的命令行示例和配置文件模板。解释 APP_ENV、APP_HOST、APP_PORT 等关键环境变量的作用。包含健康检查、品牌查询等基本 API 接口的使用示例。确保新手开发者能够快速搭建开发环境并运行项目。","order":1,"progress_status":"completed","dependent_files":"README.md,Makefile,cmd/server/main.go","gmt_create":"2026-05-27T15:42:25.0297402+08:00","gmt_modified":"2026-05-27T15:45:02.5503172+08:00","raw_data":"WikiEncrypted:qfgbutC7oyxR6nMxrwk1ODnNMBEQ3/sG78fQT1yXWju83LC7H/jAM8CEAvWT8huE5OClmQiIfNYNxHQ9s7RRS0qKOV1Xow/Q40Lf5w81YuuThtu0J6OFTGB2L0Nm95mISVZMGD73nREHXw2e/m/hzrb+N8ZuuUYDQ1FQZZcmT6YP+CSB9cZx9b8UZDJ08MCgp50aJvLLku23MEdwFc0gj3Vn9X1djLPuceJ7dwpUG3aeQmdLHCtxnEu4d1LuWHOx9SORMidWdUkvBpxZh1Mhj875BKw8zi1Bb10CuOVR0o/umWOKMxS7pa+P+krzS6ZwgQk/HiWvQgzcfGvyyQkIUoEtVfRQGP8gB4azIvJ540SopfP4dNI7lFdjUOwyTR0oyYuzDc8Y4dPgrwjMNGFkwRmsbMB5f9fKw1JT8+BoOVDOPoK1gHP/uXWcZx9Yg92aHtloj/wlf0IvlSbGhTfmFJrO/wjuSVP5sooJD9GCuVzNgSDitGoZkvsycpn320vlVFUPXtnf/VH0JOo5PM7BMj/zshAL+vsXkYjllDwZWM6DYEyETEwa0kfHBm7pTZ2bMQ4goUU1UQAPAjjbYf9VJAWxLZYVJf25SxXbn1u/PDKifLJNxgXRJg5OyBhyIL6jTJf2fuXyNcczL0GyfRvunp1WzofE6bAl15iIFcNABHPqdtKOV347seHgIIbxupRt/eFgrh5u1k/i5XAKbSXEquVhICmh4waR9rf3HeYisd/Nvj/4cpJISkhNXsY6Y4AsIpbPl0jiMSVt6jMRFgTGG51WgeuZoVPzdIDJMhk5Rc9mot1q4rK434Mrv6TJamzkwU5v7z7zy3gNx1C7wf2f4tHTVCj/Gc4KfVU+uuMRKcAAPB9bBC4uoO6zcPRroMIr+NLZN+g2CdsZ3FbJvsw/j0KEtYP8EeQ3bYak8dmhOc3c/A8LN+8UIlfyxmY1xVgbu/fvkIh9NW+GktKxQZcPfzS9ZE4KL1oKMxW9uavN/xE3hjtivmOAdhyqBhchbo6uwaAtv18ZMNOPoWLrgOW58/NlHdF4abXrwuX3YUwJHiMsjsgV6C7ZnTsYDrwXYRETPr7XXOmztvG+nYXkaHujEQtdAh/mf1KjWTbyY9qBthEG3zBTZ4AVcKt15anR6l8x7Of1pepg67m1lo31a+QlxkvmZdH6vV16tMnYHUR/UHYdSIb1yknP96PdGpWChgEZL4bynWLBFveZQSEhM057qMj39UgMZjHQd6YGbwA3ma+BnBwAMHKZSvFAMWN5arlTN9ZP2iLxhhiNniYPMQXdTviDJPYW2mRXPAgEhheln74="},{"id":"948e0792-2de7-47b1-b73b-e7b246e749ff","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"数据访问层","description":"data-access-layer","prompt":"为 Luxsin 应用 API 项目的数据访问层创建综合文档。详细介绍 BrandRepository 和 ModelRepository 的设计模式和实现原理。解释数据访问层在整体架构中的作用和职责边界。深入分析数据库连接管理、SQL 查询优化和事务处理机制。包含具体的代码示例展示如何使用 Repository 进行数据操作,以及错误处理和异常管理策略。说明 Repository 与数据库层的交互模式和性能优化技巧。涵盖并发安全、连接池管理和资源清理的最佳实践。提供扩展新 Repository 的指导原则和注意事项。确保内容既适合初学者理解架构概念,又为经验丰富的开发者提供实现细节。","parent_id":"8d7c257e-b299-4adb-adfe-bc0ddbf112b7","order":1,"progress_status":"completed","dependent_files":"internal/repository/brand.go,internal/repository/model.go","gmt_create":"2026-05-27T15:42:38.0130833+08:00","gmt_modified":"2026-05-27T15:54:10.7298081+08:00","raw_data":"WikiEncrypted:M8uljGDGJ//mcfRJly0hh2/mWMVZAXEARazEIIgMSV+7zh4rlxI5/PWj1ISX6NLHyPgijvBuT/wC8NCYThAUwGbCq0wFFWqYUW2q3UwppLUwwfZ0AIqAHKU2Gc3xbc16+/wux1miLsKUaF5mG7PT6uyZcDb0OdT1doaZdo6WGBLTuHp6Aea5nbksPwxOZOGkdhjve82LoYA5uDLJTHF+SzQ9TP8XLA4REY4I7GnrxuYVQMaPG9LXMwedj1gN+sco3muL2Gq4BiyTrodBizQeJh749NlNh713Oy84ZVmE4/3qrYufhYh7eMwUSf8nk/EnfOKXjLWJc6VCfqFwkGs6HBxK4lbTdLwrrP6ZBJAs7DJ37qA/0xvE0o6EX8/BlsM7gIL+/jRuLOrg3AdlyZGOWcqxG6U25NSgmt0oZnVeaB9y1Dt/f4a0eZzUyqS5BS74xR+oXWwaeqgQWkJCJ2CwMA48yuiv1X7JLfC480rolCVAVNZrB1pyhPdhNqcKupCjsZOzMzt3e4Y6McRCJs+2HSENGVOoZn1dn6kZaSVvuzpdJzcmv9M0J5JFo0WgHhnjm/PG/Exz8na+Cz0Vuh5FwFjR3qL3w+7WfyY908JoRd4TvTvdTcuoTBafKPAncBRD6geMAGcEP/YJQr1Knjh0AYEKNzdIQfu0FId6ViGxikPsTwGFt3aJDxNnBlM68ZN3H48+Xj0esfIPBW1UT+5dKIKWyhBtceP7BCJvwk45uQPufdCajPzlpUw0ivRtr8vkyww7zr5KkK2rXNFESFvuNwO4esKDleLUuAhG323ZPMzK4LDx+Ch2oPjoiPEzQiwtUYrI7IGd8ytgU1ia2mHvPqY6oovuN+it5sgt4M440EjJH2W3onCugmVb/yH3BiIQJOgtGroHKlsUzCZm1siDdMUTjmn1oVEzHd43wzJEzqjU55i7wxWOeXngHAqPT8WXATEGYWNbfE4sfGPgS3gvI/IEGBF/7//NiWRqpXn5xtg1qCTaI1WtnpGvWwOXrRji8CkIEEAyfsjIx5DhYrnolPwuNYsY35p9eKbmu9sNIObZVxUiKF1XORBniHfieK9Tj0rF4/Xz+kWH6QOV4OTqvFXOZdeN685Lj6n2PsL9iPmgcB2yL6OV6+Z6BFLfamg/+OgLuo1UGWYSOk2p8oJpiLtevo0WyGCeIXatzCPpFx1g5gcqwR5w3q1hXeT66Ud+NZ8gq1KHUtAFwZ27WEkCJg343JPXcpRw/DlnH+mb5qJGFsoqnvY4E4PMllw8Y4H1hpoLYSpncRooKOqotsZbZwq6N/HI/LjAqtcvS4gBQml6GKZZNn16/MKKnJhl9+PaSc1lxs8c9DmWY5EuEovW2T5hMbx4RAjhz2pNQHaEX0The69OwE5DgDcuNBx9D6HP3x7J1ctrh5AViYhj7H5B7p/Ia4y31GHo+ucVq7CwLZWfmsvRnAjXc0uLXuL7HWGKWKsND1WMkid+R4BXENMi8Q==","layer_level":1},{"id":"8b3ba473-1cf0-4471-a203-9a87732dc227","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"型号管理接口","description":"model-management-api","prompt":"为型号管理接口创建完整的 API 文档。详细记录获取型号信息和搜索型号列表的接口规范。对于获取型号信息接口,说明 HTTP 方法、URL 模式、请求参数和响应格式。对于搜索型号列表接口,详细说明搜索参数、搜索条件和结果排序规则。提供具体的请求示例和响应示例,展示不同查询场景下的使用方法。包含搜索优化建议、分页处理和性能考虑。说明与其他组件的集成方式和最佳实践。","parent_id":"8085e1b7-3658-4490-8b52-9a61f3e79976","order":1,"progress_status":"completed","dependent_files":"internal/handler/model.go,internal/handler/model_list.go","gmt_create":"2026-05-27T15:42:40.190927+08:00","gmt_modified":"2026-05-27T15:54:37.2236089+08:00","raw_data":"WikiEncrypted:o5cr6Bo4m333A3G+lRdTT5dIP53cxEH82N4y2QAmkutwkd0KP3KAvEyUQN0CgerucfElkuIsDGzyGuldlZZBSDX//XqOqGZKP+QnmlgMLhVVCpCNWw9EHW/qmIVTQJxBtumOVtR8RGgSMrfzxaMVsu0eL8ZY2bOrDST6r17lVTgdrEdY1nQ8/+B+bkzQ65abRsvYiYrspSgJteye8d8XhEaqyOpAsROLv0P50tvHHJp3pH0uNG7lrGmAIe93f1OqwLCINSCxfZ8qPHLxTT/1qsC8mBTfx6bux1+J+Dfuz0Fr25e9dCzBo06yz2oIHMpYHNOV9TTNo0Cb2bY4Etp7T7BAVAWYdmCPqkIp4muaajavVH/dsG3SIH9RE15781XFeLnZpjK3/6SyXbQzhkntGT6UDzd1f7YIF/wsYN+uji3Ytz2tKz+YuJAr2Eu+YAeR5ONhXxZJufNXZqE6lFG0h7UGc4EYAADRkXK2AefSD9qXzxvGet5VJqYDwvW+BBTRWQB3HtSC4RgwTj0pGPjrHyjdXRtYabw+1Z/3qmXraEjCkcBZqY83T3pyfSdpygE/Tp7yYYmPu94xft9AqhG0xbLEJMTwW0ad3sFR22W7PkIMegzWRGtSSosiRq883B7ZAuea6PE+DdpILgCSAnEaNFgOXp9T09825iKyPsJ7V/ePwhLdj5Lg1Sf2Ar4u2WoKKzKmVNMqngve7AIqYVhGA6hn2tryumcq512jTxKeeMrtTR1kI+kuiFb4598z0y5j8dx7XsYW4ySYNYOvKIRMNvH2r3hnqBb5pvFv9gmk5vsVg/gSNlN4VOQsszd/o2lXQfYUaon0zYsdmR73UvkjhZMnufNa5tiCXZIFdcP1BTWDeBT/f2uOsftIky+JZFoyv9ZKzVesewLPArJaSxOT++MD/ggK/zn1vWZEgWgNcONx3sbyJnPoWEw1TiNMe6pxO3VXzMjnyIL0cie5BeVP1kiQ4S3XzkediOL2bQt4hjEMPkSuuLR7ryE23zbkEZpvwV/2TORoBY12Ii6KK5Ezg9F+aFO5SG2MvF2IZyMgpmnH7KIBfag8sIb3o2RSK6zo9lgRYbXsw7lfxPpBtPyHNpHfqIYgOLYptNzC/GBTVdTQYRLULRlp1XeXxGw8mlViYBltq/GwBlmKbMKrLjsczQPuxFX9mgcnTntc2Op+0ITGX/cW8pMa+UlI2Jgvay8k","layer_level":1},{"id":"ccdbaf47-6cde-4ad7-95f8-5b92d33e2440","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"路由系统","description":"routing-system","prompt":"为 Luxsin 应用 API 项目创建详细的路由系统文档。深入解释 Gin 框架的路由配置和中间件机制。详细说明路由组的组织方式(/api/v1 和 /audio),以及各路由端点的功能和参数。文档化中间件的执行顺序和作用,包括 CORS 处理、日志记录、请求 ID 生成等。解释路由工厂模式的实现,展示如何通过 New 函数创建路由实例。提供路由配置的最佳实践和性能优化建议。包含具体的路由注册示例和中间件链路分析。","parent_id":"0506348d-993b-48f2-936c-908972f149bc","order":1,"progress_status":"completed","dependent_files":"internal/router/router.go,internal/middleware/cors.go,internal/middleware/logger.go,internal/middleware/request_id.go","gmt_create":"2026-05-27T15:42:44.0579461+08:00","gmt_modified":"2026-05-27T15:56:02.5839243+08:00","raw_data":"WikiEncrypted:SO0rhsjN3o9FBa91nwv0dk3VK0BayJJDenMPYPQ2W6nsAe4EsEoXai/KYnlADa535L7x3WaIsKFkMYttaagselo7oWM7opNJkcwivprxnWIphyBYwD26ctwJUwDo1KsugbEiaXda/z7cUfwKCOeX1dHuc8SyfobvirT1iA8qBPBd3DGmXR/P0ueb9Zxbqiv/8ewGdP+K4kCitCLeXMp0YPxLL7IgFBZY3NBIXsoX2M0H8v85XPrqQwGU5YVXEGCU1Gmm+0RB0sL0zh29UP6pX6qaaUB2bP4nhDCIIl24jYHGA4tXrgQjE7imxnwjFfMIFF4iKTGXCt2kdaR80fUv5jLH/6w7JohL6eiAEj5J6uD5VIo3ckOFTPcdvhi96uuyWDnj7mD8INXL159q4j5N0RaZv+FtCToPAu71s1dumj4kEgKjj+4aJeG/d+4k/m6nmrf6ej3xQCIu1e7a7bUlWGB/SDsTBx1CDgP8Jvf/IJFfRtO5uoX/4ampar7/3Vw63QJlIfgxhqBR2pwtVB8j+lxLHd1zKklTfEtUuioZRaWBcbrqTp47y99/Nl94kLoG/v7l4ehL9QMCUmhwktpzJ9LWJOj6QJS68TksNHWGYr0Ezcs1pIOFk+ek5Kp7IMWOq4625jBUKj03wUuUcL16LYBRkZNl4RdCY5fkjWqc5tiDY1k8ygrLDCjgv6WPUGtPmXz77etCt2D+NCH7tlFw1yylQFFmXe3JFrW76cwmUbSTgTb1bJ1Dn3ku4fy+vaOZ4A0di3ZGvTqximo1UjyeY38t4jgHHXzFXbAOoBrDyLOjkearcgj40ei2Jc5zu7yeB+ZCiueDFmuJo53DLfCa5ZsBya6HnxPmGyEsb6TGRbNrJ+d7e4xH8FNXwxEZ1ywqyg6kc565Elr5QbezNtafHoS6rEaMtwSFsV9L+oGlPx7fTsNkTH7RJx9B6l69ASP7845lrEInMB5xwOScxqfif960tgRuds/au3r/FPAvjUfGHzrIYHNvuMYNWNADBIwB0MBO79bxuO5yZ+j7UQVGvWttpfVuxTv98YHIOsLEaqKn0UtEzTw+W61zTdclPiTSy/H9C0oUP+WxXDnXIhVFMHrBFswS0b73fCFHKy4dkSbqHi6WlMc1af0MlLM8b/pELQAN0kPhAQuSa2VUmQBghO2XvCE8eSMjOYLwSoIL/wj0dNgu56qSrGtjR5dQegYcXORsnPCCWb1yCBhl17JBUKey1UcyhTEILZUvg8iqB3o=","layer_level":1},{"id":"5b9847ff-f7b8-488b-821b-6fdfe2dbde40","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"搜索引擎","description":"search-system","prompt":"为 Luxsin 应用 API 项目的搜索引擎创建综合文档。详细介绍 Meilisearch 搜索引擎的集成方式、初始化配置和核心功能。说明搜索客户端的创建过程、连接参数设置和索引管理策略。解释全文搜索的实现原理,包括文本分词、索引构建、查询优化等关键技术。提供搜索 API 的使用示例,展示如何进行设备型号搜索、品牌过滤和结果排序。包含搜索性能优化技巧,如索引预处理、查询缓存、分页策略等。涵盖搜索结果的高亮显示、自动补全和相关性排序等功能。提供故障排除指南,包括连接问题诊断、索引重建和性能调优。确保内容从基础概念到高级应用的完整覆盖,满足不同层次开发者的需求。","parent_id":"0ba3accc-326a-4433-930c-5b951d4e5dcf","order":1,"progress_status":"completed","dependent_files":"internal/search/meilisearch.go,internal/config/meilisearch.go","gmt_create":"2026-05-27T15:42:51.549218+08:00","gmt_modified":"2026-05-27T15:55:50.6500526+08:00","raw_data":"WikiEncrypted:uPtIvUbMezH0ofnqqRSEthHE63LUAQzlPWMsHoJP+/Xvzp1zu9yMhdQo5+5IhHnrHX4ZOoo974Mq5SbaZHfjDGkMYXk9L6JwG6+nNMpoyLz7Yvtg+8V5E/tIsJ5Kc6+iYBU1An8SAJrGuA0Rg8PKKB2Lomw8RjlQO6aFo5tapv4T8ZcTSCEzPetfEDt4Ire/BS8D0NCO9C5peTRlECqqOr+U+OC5BfaXaEvAAPhu0+2LKV5pUWuqhJB8wS0sZ1C5WaKdq3GSgEsQPwvpw2BQ1jRzPp5Gw92/L6CiOkLBRkvHdCH9dpObT+Fc2bTPTxchCzPA1QxWkqYjFOEzIs4+c9QWuFqGuEbrHq+Ferc28nVGyeEv7Tu6l6m0Xe3CTg9tqdBgkd6r9Y4uV4bh0didngLNjO9xEjQiYo/ssQ7aeCgh5CY89JkKjbhy8iWFLtMr0REWD9B4Tb/USBDRMoljXzRJ+rOWL0o28fL4U5wl0ZRerKn1Z4oHbe2uKMx1+wXerr7aDOXN5JCqdJfUuX0X6xn/ZHUrsqiQHGXkRCXBeXGZYjdzLtsrL01/BtT1EzCiD0xUVjPPE9dWXXwHexSANSMPsC/RJmvwwlMon5lzHXlWN+FkX1N1OEax+N8FY1eBxIWLiwUWps0/IwghwilZeXcNGiuUf7skRlvM6iTjpc9QQeyE1h7qrU5x4xCrlcYc2S4pQv+WEFB6Va33skBqhNrCO1vIUjki6nAesFzcnPJkaWwnmEBipsjs1BuIUK3wxXWrnpgcFUZbb6WvFF2WRI15NCO+jImJ4SSo2vAvVRKF5JFtZlABOifDyJCkfMwUyU227ch0TmMp55pYqp3NoLQ7ixrqRxaPvxFda40SlBqKe2Yzuiqm/73r4Oc0T1zXequgzmTkaZtRaq5yqAJ115HA1HLcYITiZngQbSWTnPeVbWsb+DCPd33lIKZ8fXEGiNBs7vdikdYx+6vF+1EsrQeSjHfD0Ihmk87cLn4d9La5SoLzDfOVJEy5iZpWO3AaUZMZ4EcPE1r6ZMeZ8EUGqXQ9Nlpb4InQHP4W7ghwsYHtL5FFeja4OeMJNBxHgnOAypiJ8tPVobpLX600FfUsZDv09Mcr1xhbylBxsRrlqA7Gn3sOfbt0DZUG0uo0Rk1ME1x2rP5Xfa5lQH0t+tS5FpyP34T6dwqMGEtcccz6Je3BiN5IyE+vUyFHw+FdZ7gNtZ0hDEfzwP2qOWZT+c3Qsu+6MyhWHPs437C1isiJNawoEnOiqCV9kWhig5+ztneMD35zhcCTEgPu/Bm/bEThy3Rdd7iZFBdoU4vYaqKC7n7/DByj71DHngfpIkzvddxnNY3z+MxRPwwc3khnmY7jvA+4rooEThszp2Uu51YkzKA3R8OCZX8akGsfWwzUIQK2BB/CZUj5Y837fb7vEzAC6+ILo7N1OoawujgMXPweHQI6XDnpbNfOnLfNxXqPfgUApYzsPZoGGtPp5WSaiLcCw6qMXPNJVzEQq2wysa7phID6rq9Pl1LQxzlIH6h9p8v0IkxQMMloAiN3ufObHM9ygq7zggt971iApCCae3R6XOA=","layer_level":1},{"id":"0506348d-993b-48f2-936c-908972f149bc","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"系统架构","description":"architecture-design","prompt":"为 Luxsin 应用 API 项目创建详细的系统架构文档。描述高层设计、架构模式和系统边界。详细说明分层架构(表现层、业务层、数据访问层、基础设施层)的设计理念和实现方式。解释依赖注入模式、中间件模式和工厂模式的应用。文档化组件交互、数据流和集成模式。说明技术决策、权衡和约束条件。包含基础设施要求、可扩展性考虑和部署拓扑。提供系统上下文图和组件分解图。涉及横切关注点如安全性、监控和灾难恢复。记录技术栈、第三方依赖和版本兼容性。","order":2,"progress_status":"completed","dependent_files":"internal/router/router.go,internal/config/config.go,internal/middleware/logger.go","gmt_create":"2026-05-27T15:42:25.0297402+08:00","gmt_modified":"2026-05-27T15:45:15.0922502+08:00","raw_data":"WikiEncrypted:EPw1VhZSv2AMLpYzHbCG5QXmatSKh1iukhkKRz99kcWlEvnOwuqXvA4ZgQ6tN+ytovQp8yiqJicl84VZMQ0QFU2VsQju8IpcRws9R+O5UqWqyS/p2d/juMO7CcvH5Z71CG7eM55PPWPy+SJ+QNdupFqroK3bd/LSj0CxhfnVajICVNkUN5WBZxRD776A1wRS/9o+f2rU9gRAuwITsrHMLAbGKP2R4CWvSRvdssG+ipznfcduSvnmuao6OKPuV4jqBbRKw4zcI6RvA8OzqjvEzd71LQGHM4pSLSZHCpXNsVLsyx66RKMz2ehGMgRX9mmwX6tatCTNGRawqOaeHsAdZQVRO5F/B6eOmBUnLjBLXMFaj9NiCqZXNSC4hsNcV2NHNsgCUG4d5ZrRt08xhKOHc0qnU6uPcmCoIrcH7BzrDe0ZgsfQ5XPv2VLskL9VH7jP/iok3665UNTbFEMp+0VrHrtj3MGwZoOPjci63/k3b7LG2CemZFYRL/bBs4bMFcslJ5zofo0Udc21Z14UtDMqmtYl8JWFBrQXBiQmeSQ1WnHwlcbw4u7v98svD3w+hug0ZVtbBIzPxwFMvWxUfRg4bYDfaYuu9TTbJDDZVS6Ob7p0LyJNbyCA5QPrEI1J8F8PhZ4Q4jLG7aMJugVeyzmc86juCVMOMHEp/N4hziN6xK/qRiJHM3tNljxXReJi4IufNYJTXLNXYKvDPvBIyDFn2dE2mCCXqEozsEjZ+v1q0e/qlU1dunYg8xPxrsTLDcF9K1BoyCvSx+UQiB4ONUP+JxX64QXTtp+IU0v0seB2KMrqPn0/L+A8OWuXVZTvRwPavFOv5itZOdJAwA/V5N2eufZbLHqMglD8xPwtGKDElVmGnIo3BZVNSoRZySK0fotmIw3xMqHf/TIgp731ZvXaZCaKCnXvHmW1NCCQkqFwBHBcq3S04NwWv8UMSj+HNCHd++vSABXVmaVxgqblXcKffFrA/gNTjqBgt3JGZZI97/WtLhJ5rCuiZXimpuKRBCG9Mt4/TZBFvL8EtkrKKRM7s3qaiXo4+rxMwzBW5q7TSyuCkv7qZLVNg0tvqAaU87jlHeLK1szXHS95TJbbbwjv67i8S5YYoSfZZg4iWUWizvHjGv9xeSfB5oZEfizx8EkEkD6rutxWWUarg7NVOH0fJqslwz3lkocZU2+RLK2kbH+JbCdYfCoZ6SYU+W5JDYAfSxLyvGhW5sDb3XXYFtD9X30uW8TOYGcvIRawRJbAjmJmdlrT+9rFcYHVaBJHtLDAkEnSUMNTxZgsSUCulSQNjxAV66NfhnKRsTJ2+M3dqXj4LldBrlK0CwEZUrmzutKSXsYqB/x3yFnXshPVlWG0HqDXi77z/VH2PwAlmUZi30yCiL3c+D3rtHVruIK0y7+TJeQIcHCK8c8WmqIMeA6Zauq0wgWtGrI4ShyR+BKhcQEtCGA0ZPrOitzD+oByp9nJy/p/jyDoJ2KbQk+UK+DxYjyyeOGMh023Ioq+pZwhbHjNF8MPtVdr/ZTBpHSWIfgC"},{"id":"e81027fa-c24e-4492-8100-f051facb89c9","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"数据模型","description":"data-models","prompt":"为 Luxsin 应用 API 项目的数据模型创建详细文档。全面介绍 Brand 和 Model 数据模型的设计理念和字段定义。详细解释每个字段的数据类型、约束条件和业务含义。包含模型与数据库表结构的映射关系和 ORM 映射配置。提供具体的代码示例展示如何创建、更新和查询模型实例。说明模型验证规则、数据转换和序列化机制。涵盖模型扩展、版本管理和向后兼容性的最佳实践。解释模型在不同层之间的传递方式和性能考虑。确保内容对初学者友好,帮助理解数据建模的基本概念,同时为高级开发者提供深入的设计考量。","parent_id":"8d7c257e-b299-4adb-adfe-bc0ddbf112b7","order":2,"progress_status":"completed","dependent_files":"internal/model/brand.go,internal/model/model.go","gmt_create":"2026-05-27T15:42:38.0130833+08:00","gmt_modified":"2026-05-27T15:56:31.7808704+08:00","raw_data":"WikiEncrypted:klcgW2PbPxJambbKMzvFtzxL0nPpsuPMPxr5hj83lS7zLbVY013R4TC8XNEUYbb5qxIyzNIzP0UIL5gcgWgb2JN3lLqRFiolKSir3QefPwOA9n9PyEgt3UyqdWtF063zcOhUCuBNMrBuPrwe5CLRNYPjpeT80bmhf1WCbW0Wc4soOdqZfNatqkw2uzcHw6lRPr2OtTCqznVFik3UW5X7OSPl2Ttad8DOmCF+RIgP5/y9rHCmETZIEnQAjeyLbBXbw6UgR8f5iLkYVBNC7MFGFybSCmqrbPvzuYIucnF8DA/OBQbccvz8He0YhGp5Ug4qlUVtOLmA0SeYTnT3k5tD1obbgsTO4qQabKiW63BMDIgOoTKlnAxC1I4m+7qzShvq8Br3qYdH05fujRftdqvZ3vwdt/v22MmEtMVDy773q7E6UAWYYM5ZFpzQm0FMEmNSl01+1H3g/6NLTH9eP8bwh87Xu2V+U7zgrLU0hd3RZ0OahjypNpLHcMl2lXk/R7L7r4rqIN25hBQ6UmXVTgJaiLUx3de0ar0mjZESOIFPklc2p7+a+gJKnsuZ302S24Iqoj7AavR3Pi+VN/ZiDIP5Jeq/kr9vnhFkMn6zcXxG9uhrYtLkssRve9T9PQea2j7e01+V/IRE1erymaIw4LR2+ujdhB0V60mQlH0Z/YHMAaj2SBzGB+QTLKTIYabYR2UvD5j+cjFoa+Gbv0JVt7WMtqYL+CA4hSSdAuR8tMIXc2t2CcnnQa5O6Tn0MfMrcJ7Vdd5GeLy3KLSJMiyAdGf8tQ+JZ89A+1fQXxuEAa7TZZ3BB2Y832o5/9JIpgOQz/5oUXiHMQMoJPg6JwkfMJgMgWRzYBfhqRR15ceTQaxuMvii3FPvdiqa6G37kowws9zAmlHMZWT58FopOSivkLJCWodvWMrHAkMS8z8bai5nPSMfxSVyZw1PIyfCY16u8WPVBvxU4hqjJyB5Bp8wM0yN5480l3wa1Q3fuEzaJKj7nPjtU4N9gPI8QOahBMHpDjUDymEe2pFTNRoGiNEP45Jp4RyxlYSX4cFgV4meLKhbXpXHiY0dnAaKfBc9jAtgWdWnmviK0GLQ/jILxdrqE0PICXi9+4yqSRI2Ry0zpRi3qp0Y//pb6taUy+j56Vo8zoIsZTAucPdhoOaagkLsFMjiBClhwGTCL4ddbbDqwou29yxIdZ74gd0bOsbOrVf2Cle+fOknD4NoXKDGYaQ+KHd3gixYUsoET/IXmUN0vFu/IGEzI0R0XrwFDPtjlVbg16+Sht26Ty4kWon1FNcLN3BCZRX167pLM/oB/mh/MdCRdaEZ0DoXfwegSeXYwGho7HJZDjiwFdafr+1MXjd4UgRw874LAKL9N5H4GLBDCG6lvCQ=","layer_level":1},{"id":"8b7558c2-be32-47b8-99e2-1964009b4337","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"设备管理接口","description":"device-management-api","prompt":"为设备管理接口创建详细的 API 文档。记录设备信息上报接口的完整规范,包括 HTTP 方法、URL 模式、请求参数结构、响应格式和错误处理。详细说明设备信息的数据格式要求、必填字段和可选字段。提供具体的上报示例和验证示例,展示不同类型设备的上报流程。包含数据验证规则、重复上报处理和数据一致性保证。说明与 Redis 缓存系统的交互方式和性能优化策略。提供故障排除指南和监控建议。","parent_id":"8085e1b7-3658-4490-8b52-9a61f3e79976","order":2,"progress_status":"completed","dependent_files":"internal/handler/device.go","gmt_create":"2026-05-27T15:42:40.190927+08:00","gmt_modified":"2026-05-27T15:57:09.2032126+08:00","raw_data":"WikiEncrypted:W4RZ6LTz4rg+RteR0TdbvOUiHqohKn6BWshWHivzY4jjRdHovfIvtJ2sB9MDcSrupgUsB93CF5gLCd86HZ+9K0WcrorZ9lipKx/OS3vd+kUisB7ExUabBXlss9QTm91m5fLHe0jeTIgrcbTRB/E6TiCq7LQKIbDT/vX8e7gGsXm7c6bJS+oGkiX4dvfSGGxPsTJAzByQh9zKq0+EB1Cdehda7LhobHHhD/zrFU8gTYLmATkOqk43kMtWFXfUxZx4vfa6jkkrn2Yy9ZXvHFDce0ofKtOVLfq3hKZi9k4owDfhOWo1Diy53e4Nq65RlOEImu+zThrRgYRk0za0MnIJRjTpwcuh6BKDwprXShIUp4xbiPG3TFnnTMAA93o2zAcKGb2Ca0Qt/QT8wY2uCxaVwD0GXGQGpI93ttiS+EfxOpjoxc+xkTy77AI8niJq+SBImduJC+de4cefpYZZm2E9MmZSUbEbDEvXBi0kIgnFiSH/BigSv1UA9JM4cWf7m3AvwqqCGKN7CNtERiJxTyhs8xDKqxeoR4CRHtZ0Po30nTlEXMrgkyjlnvdlkRokDUi1ZrRl7UvaUc7IAQFie+6dHuwt4/iZkuQJMydlgb3ysz010zMUXCSWtnqb6yQi0Y16jkKXmPgBl1ifYkUYzIrMzr+UZZHq7EwLVVjj0SWvnNnHeWgSst509csZcp36C60qyP/+8XjGBbVMUoTVonu89qUrotxb4Dr07k7YzT100WjET+IOcrQfLKiHM2SO/HyTfGKSpS9/T3JoW4mI+tVwFQ0QFEey51AmMN72D9I83sYo/INqWjC4cAhHr/IiAxVTtcj0s5xI5pGgOAv7XNXGFPmc4X+rf4C8k6zxIWMCOirykNyiIF2cm8i/LUJFrD+sqdGCVdilSQlsEgs0Gf0faUDYAUNQdylOw+rNDrJMNPGktUsoxfdb2WBc63/L7nV6pPm7NnpU0xCDhtdG0/jq9hEyEESN6V7L7bOvG/csrwRuKnJZ65RFZNlodDK+JNRUhn6yPOkrG9uC0NU12cyBY+r8Su9MV/6ozEnnNWpxhuM=","layer_level":1},{"id":"bc6825c8-9aa3-4f0e-96ca-ef4925af0570","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"依赖注入模式","description":"dependency-injection","prompt":"为 Luxsin 应用 API 项目创建详细的依赖注入模式文档。深入解释构造函数注入的实现方式,展示如何在路由初始化时注入数据库连接、日志记录器、Redis 客户端和搜索客户端。详细说明依赖注入容器的替代方案,即手动依赖注入的工厂模式实现。解释依赖注入带来的好处,包括测试性增强、松耦合设计和配置灵活性。提供具体的代码示例展示不同组件的依赖注入过程。文档化依赖生命周期管理和循环依赖的避免策略。","parent_id":"0506348d-993b-48f2-936c-908972f149bc","order":2,"progress_status":"completed","dependent_files":"internal/router/router.go,internal/handler/brand.go,internal/config/config.go,internal/database/mysql.go","gmt_create":"2026-05-27T15:42:44.058506+08:00","gmt_modified":"2026-05-27T15:57:20.0074643+08:00","raw_data":"WikiEncrypted:Mh1qeXd3vDrFfJakTA2LVPk895/C8yZ/nxc225NJgc/vaOsdHkI/4otOCFkjHjzGbvDeu3NfvNrTVuHfsGzx3dTon6sTOx2eqYzt8f1TpTMBkynZ464lQq/2wtKdG64LJ046YBidJFw0ZIeaCnDrSGq0XciHKDBtepVmFvc3BWHO81I8gofhmEgiJhls+KdsMfDjsRa7M0G749YeIbqdVds5ERm7uOgH8tBY2YpTkxOsuOlArfwWtnc/XtjHNW1JrezQd9Mid0eOpxaMqgE8yaFeTmGavv8vNNdwkPtoQaRIZjrbbiqxTvEUceUQYbCYQRtN8pZ3IPUSyl/t5LmjfI8kcvGH9WDuz9YzeYHPRMxg8qhmGItUtY5Mpls5ivH/EIhLrZokNcrV5iUHEjmqb+XCRa4yG0StkWI/9ZNKERgMX67LixsijqQn0vqlkUufnCRPCP87EnYdQqECcP+43/+HHmvfgNL2YqVlrJqTwAbRfkIOiR3RzC77UVxXk9TDkvoSu55Qg5T1DUnOUSH3YHIkfOdMt11bVZaQveJDn8eKF6fBu1S2w0nsgVRJUYN4xWDBb67St1qj/284vBE3o80iLbKGZuKALFUmDfFJrRWZxKz0EkrEzGEHLZlY8gqCMlDvpIDasl1U8U3c4akb4qk+YO/zjLohUFROnbeKRIrU66oQ5vJmycIgrt6M4+rjAVUkaEguxq+ntt2xZNdA99vs2cNQGXIFbb3h6nnwxQfKvVVV//1SMfISmnCs34BYxkwVXBcadfbrEOM6CtcYBMqCXvzWJ33/FXvJZmH/A2KAbLgQ/kpz2RPmPzOUT7cKpJqB9MhC3DXdQfZ4kFUFzF+13sJwXPOJvel1igPgOiAs1rrZV85YJXrrc9ZY46pVGQKQ3yekIrqt0EK0vaAgj5oZyrRLz94KdhvIpCIUMd+M05SmsY00TRk827U95uJFIInVhtoOKMS1CqgSM1yQIa/IIKz/s4LEl2UelFpJEOmaXLT9gan8Bsdu2mXOWE2iDgTUhjhHSx1rYeN5EPajgxJq2tyvfti61Ohcm+s2CPxSV7LBjGbAZq0X/r6myA7lLsQQo8uf5F/GMloMEPtiXUyyLW0EJI19rEx68+ZtpIUPNoESaao/sS5ANWs2d2VH+YVdF+I/ZvuMKKkfIgFbf+JvjYdCHS0qZ94BTAzNJdhWzOqdw0ImHLop6nEwvJI2oJ5GWhZGw9jP0WFmwV65RfKVPOZRZTPvo9W2tCb0ZQo=","layer_level":1},{"id":"f5a7db4b-765d-46e4-9395-af7bbe8d8fd3","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"日志系统","description":"logging-system","prompt":"为 Luxsin 应用 API 项目的日志系统创建全面文档。详细介绍基于 Zap 的结构化日志系统的设计和实现。说明日志客户端的初始化过程、配置选项和输出格式设置。解释不同日志级别的使用场景,包括 Debug、Info、Warn、Error 等级别在系统中的应用。提供日志记录的最佳实践,包括字段命名规范、上下文传递和性能考虑。展示如何在中间件、处理器和业务逻辑中正确使用日志功能。包含日志轮转配置、文件管理和存储策略。涵盖日志聚合、监控告警和故障排查的实际应用。提供调试技巧和常见问题解决方案。确保内容既包含理论概念又提供实用的操作指南,帮助开发者建立完善的日志体系。","parent_id":"0ba3accc-326a-4433-930c-5b951d4e5dcf","order":2,"progress_status":"completed","dependent_files":"pkg/logger/logger.go","gmt_create":"2026-05-27T15:42:51.5497484+08:00","gmt_modified":"2026-05-27T15:58:35.6424946+08:00","raw_data":"WikiEncrypted:U7R4DE1TfAUvjZuFqogRLnOghdG8YONf1+EXGFfCCKN64XPQEDdUeRHtBVRbgq1RTfu+PHdhhtH3Ub4edjV7OQOmQQeOhgljoi3HGtSqPVCQvF+mHbx8hRLjpVdCwHdXLvW1Ns4CDnqx/UVLCA1IVbunG2no8Z4rHywszGt/xnqU1gTE2kg8t6xAVG7XfQvH3irlIr1G7/Y2/+RWxJWLz/5H7tsKGpmrGhw8RSd3hVQPhG3iSp62UpI4Jxk/d+OrEUWPuwqJKtZElc7QKOmFaL2Ou0Xm32XwADHOmjYzJyNTlscSdcecUG5KctRPumjoy0gNP7qCfrs8gUDdHMzL6ttn23qdEEZPNbh+TYWVIWnXigmIb8ZizIpBBvi8lgdSE6OX/M698aF13vXXWYsV/gWx1aMFvyo0U2/RQWwm33tMeBdIwK+36gcgiSFDbE0S4b5hb02VMCuasWvPPhMQloVwTmpRX9jLlSWIxElvqhN6JW9Sj3tn9asGgB2y7sZ5hd9DmoUifCyNwMYaKDr87XcM5YtUgyXFquhGaFcY2+CEND5OlVHePB/BTWoyBiDCLgXpicJborNdI7/Nscjq6w3smcCa0ShFdrQ2c1oxL/nowS+0Np2YD/mGUBQfJGct62LTGIm3ejRTFJcHn9bsk38oD3Cjniur0JFTE+jhM3c2BIpd1A8qaNgOfFPuLR038U+ZzBwSYPk0ujoLLSuHOTfKvppASu1MMZuPFcqe8LG+ceVp0xjkx0BKLaD6HrjbpSDMXXMVbNnJU9shT522RABkZEeCFibRDEdFCO6l4jLtyCPaHZCPmGOvHOj5lizmbQtmuBYHEEG+eGYYgOBv/wev3hPI59DkbhTIXpIUwxQ1+0vjVdabnEPEBVxCuGVGDC1Aea1CzWgqA3FLHFtIDExYaEo5YGKlWQn85tDudAmcoD/Ru2NWdGGcW1Ypp7/NWX/VOzYfaGmEEYVbZm41M/SfkgFawUphZc5M5cRj4OG4/tI8ZvDx7IcwIwu4FwPm+21LyFqD1gm86CbkOJ7lK1BH1kl9o+SMXw9VA3snaZ4GGhpaHvgBFvZLH1oT5ZI8GXrTUvuCfq+i3DrDdnuhElM+h3ewW+lMfyvo6DUCbSK3PvLQbsO8EEhz7rAUVsLkZVc1MS4u5dgtYaZte4H71gQszMPb5kdmZW8ys3JTlZlpjsCOMDj+LregbU7ysvd1xZC+GFB1f9RtMNfyIvB0/60vzV8BkymNhb+K5giaTcSKv94wsV+wIVqxR3DGo/CyCs7AbdIof2wAC5ApaWIxrX7XM87JVZkSCS1I8KGN2fVloq5p/lFHvEn+z0UdPd7f4wsFgOlczB3xEz0FolPe/utaK7QT5lmhGjbdBcGumMq8QwGZYYsZk8z3RCjTxi+NXI9NIx1lGzzdjHtr/l0gIU/8oiKa8KYSVjsH58LX3gtmmM2UImLU6+FrVEaFDK8wBB23SJgK41j/mcvuuwJNAg==","layer_level":1},{"id":"8d7c257e-b299-4adb-adfe-bc0ddbf112b7","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"核心模块","description":"core-modules","prompt":"为 Luxsin 应用 API 项目的核心模块创建综合文档。详细介绍业务处理器(Handler)、数据访问层(Repository)、数据模型(Model)和统一响应(Response)的设计和实现。解释各模块之间的依赖关系和协作模式。包含具体的代码示例、接口定义和使用模式。说明模块的职责分离、依赖注入实现和错误处理机制。提供扩展新模块的最佳实践和注意事项。涉及性能优化、缓存策略和并发安全考虑。确保内容对初学者友好,同时为经验丰富的开发者提供足够的技术深度。","order":3,"progress_status":"completed","dependent_files":"internal/handler,internal/repository,internal/model,internal/response","gmt_create":"2026-05-27T15:42:25.030488+08:00","gmt_modified":"2026-05-27T15:48:30.8195851+08:00","raw_data":"WikiEncrypted:bpQ1QlMq61uqjeQzqQaeFJ8lEiuSQp7TFZGpUgKRJgaf7PT+zt7MmzNkqVEU0hd1ivmcFoQ5NrDuJye6YrJgMhvUC8x94MX9xPrGNOjZI+5X4c+vLlR4Y9di8UWaiF3ePMWFHZ+l5JpFZY14y0Gmj+mg2jg6ynoSHd4F8iD94Ksscd/cHjiFkz5KV1ZDKSVo0Zk43RijuZRD+ZvrVDu0XmtFrZ3alp/u48vDDtq64kS9QZdDuR6Wlu7qiT9NwgX7AiaDJqJ4R7dPgxgm6Mui+BQ08Oeq08iURJiXTO07UxLp+uzs2JqJsTrShJlYr/1DePotYHyji38fNP/IbITC4bmDtRqP/5BMbZ7k9hlh8bGx0EWkS6uNkxCgFDuaNHgykTVXSiKrKRDSWgLmKInyhnzBbCcH73bVc+oVtcoSBXixSUbTl5gP1Ig+Y7wyWeOoPHPDdUn77U/OcrSdTkZNXYm4+/I1eIujWEUg4gc3eye74LI568bySPdOk6ZoKG7u8TBv2E0BC3tLW6pJFD6RRArV26sWYSdHsaCumsfOVUgYjGHkhQhsYwKt6buEJZTy7sv9olRDHoi1qy0k9PbRIDacX2cJll7l0u0jXnnztZGx+9+HQ3H08P26RSQB9mNCVtihrb3zqn+nn/fHaVP6TLlA/fFESM0VIGwPT/3NXgKCrGhgeLaw610W73Kv7Ad4zTf/FWsHJ6uv/LC2qfCo9Zt20Voo8N5CmmqTJah6KgVEvW8v7BoekBUzC7ZPGH4oCJeTk4kIoVfGbX7ACMu5mplivQVLaQWQfMElRG+s7oBrbN95CCShpR7S7J59DpnFX/IbgIScrW6X9QGEyoBrPe4S0LA4tnTBU7d2g8RsZDWloZJZbIBi+isLt1JA6fWqwfZ//t8ItkkzPmQJIFJNOR30i9z3wqQAHliGK0F+EqacD6YYn1Ryvt/d8nhRJJDZ526J2xz8X2pZKja+oeGAwU+edSxkfqjTZ87fqFunqnQMbZ5zjs/JzrFvTHlR43EYFU2vuK9YgZ9UJIHtxvPBAubNcuFc5aP4J6GqrxUrF6h/ZAfD1Zpj43GWztuTnhRy2w8l4xy/B722d+K8VBXpE6k7/b8KNnkM/5fSrqgzE1pAlbi0AWtXWfP8pgWRLo+r+wE+BALUxNC80jMVfYGnnIvWU2rzgcJ1YmWOyznQOZu/mNvoUI5AqEDSCzI/ow+21CW0sEvfypb9HKynhsg0JfiAAPvdBZlGmCYs1rcrjp5RoagygMzTGEu3jQYQqwRC8ctiOr0SIvoGaRfcZ2XBEPnjYwAQv2ZdUT7MB3cDleI6Eevvvz1GwXLldlBytA6hHLrkC3mCCZW0+qJiwahbJ5Ayi4uuem08ScBJFqUVxqndMyTN7+7v98xiEKLumUia0Wn4eDjobEwAEosPbMS2pI94b5xVXHj/QHiBfCTdbnlX+vTiLOFU1YW+322ZmO8m/7EPabziKO20Mgff19WPSw=="},{"id":"e2d8da89-04c9-4fee-8dfe-adfaff79c9c1","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"统一响应","description":"unified-response","prompt":"为 Luxsin 应用 API 项目的统一响应模块创建详细文档。深入解释统一响应格式的设计原则和实现机制。详细介绍响应结构、状态码定义和错误处理策略。包含具体的代码示例展示如何使用统一响应格式,以及如何处理不同类型的业务场景。说明响应格式在 API 设计中的重要性和一致性保证。涵盖错误码定义、国际化支持和调试信息的处理方式。解释响应格式的扩展机制和自定义选项。提供性能优化建议和最佳实践指导。确保内容既适合初学者理解响应设计的重要性,又为高级开发者提供实现细节和扩展方法。","parent_id":"8d7c257e-b299-4adb-adfe-bc0ddbf112b7","order":3,"progress_status":"completed","dependent_files":"internal/response/response.go","gmt_create":"2026-05-27T15:42:38.0136404+08:00","gmt_modified":"2026-05-27T15:58:41.4408299+08:00","raw_data":"WikiEncrypted:iaIcfwdLscso9uJYzNYkBwQmMSDrN359f9FMHjOmg7ClR23tbv3dTo6S+mZPVOhFdDzmxIISHKSsp2mIHtSHucIsRTVIuVJBLVgxbLtBw+vYpBthrkKp7ZsmnvjWGy5He35k2/5gy4bT+0fHVhDxJakT8MktDZ/BR/eSlGlnIY5RrC7zFB41KSVM+DwzGHJ6wFsSBTONLyyruobUVduEU7vzMZj9sgtAiHvFL9P2SHh3KcDKpQJaSF6kzMxfuHz+RjE2JrSS7jCHMh0pHTgddYdXs0A21pKoq1Cz9ewRb59irUcIZIJum0fCoYabjhhlkabgVWpIZZl9yI9tYbNT404kkRaJxst4Jxb567lxVXj7z99S7Tl5HxaCXddNPmiUawv0vJsTzd40DhW2ZDAviR3GRAUDUyfw3/+7dQvIEQR/e/MrQlblG6A55TurcS4CgAI1GLwDIWQneG1pO+BCAgXtQPSmpqB7BgjGxlBSb0Mc7Ozk3IATXCWF2Gz/3rMiwdu/lCfL7wtHHu7F/8j5tAKZVzSyjHmJfXLPZ5kHt6Y7AdIc5TZNDyi/LrJoXnYx7u7cHY15CrJc/tEbrztzQ1iElkaTNZ08r7RV7F32Es2t0ynCOMCVirx+HAUHpAOaAdrSai+26wUse4xRm/FkwNZtv+w2OCnTZs5KwT9H5tUVsmqVWyBhSeXHtGkuvUyqx0IINIHQnBhkIpxCdOwzkXTumXWy8tolLEPQSJECMUeYwxFHpvtC6vAQckxS2amrDMWK/GTvxacGzKQom2peJWZZJ0bUQ3pgm7a7YDJZHCC3jH0cq0BoCXXUU9d95pauWlQP2/IZMyuUcH0X72/xZ5xKYPNJuyeVPVzFTjkaZDzPAbyCCKrXJFs8tjyuNkSuRpPaAh4kSgxL6/G5WXR2Fwe/QG2rpK9IUOEv17HBevzn6Dbty0HUFB3zxObA10u7Kx+3rfpfr6yzO3k7y7wWuKpzPhYJhrIS+TCgBZjcTIteFPYWuoj87paGFovSmvqEB9DkBU7xawc9cRduspe3fMlYvFSHJJnaIDWeejeQuwwFd0JoBgOkeMPtCVTkj/SaZimKyefQLYyw5AAu0rj+5IqJ+HGhiB456prnmou8yIC9ws2NUc/LeK3qMNaM1Tkf4lPPKffZ282PoNA1rMelkw4Vu6gXVHkuVRLdCqcUZteXbuH+Qyr/pATu1oc3mOV34LzJDJs8AC1k47iZmX/sSGNZ957wolJ0WWZow24CiUWZtP4/Pg51r9qwhsqMAp7p602Sup+i5M11Gg09u0cyRanD+hMX7HCnsebpyCyxZVvB9jhaZJ4sNdDLWZKhRcBfm4gGm5v3l1Ep8tABF5fwEQ==","layer_level":1},{"id":"4b0e33d8-28d6-44dd-b90a-57af5c6cfaa9","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"系统监控接口","description":"system-monitoring-api","prompt":"为系统监控接口创建 API 文档。详细记录健康检查接口的规范,包括 HTTP 方法、URL 模式、响应格式和状态码含义。说明健康检查的各个维度,如数据库连接状态、缓存服务状态、搜索引擎状态等。提供具体的请求示例和响应示例,展示正常状态和异常状态的返回格式。包含监控集成指南、告警配置和故障诊断方法。说明与其他监控系统的对接方式和自定义扩展点。","parent_id":"8085e1b7-3658-4490-8b52-9a61f3e79976","order":3,"progress_status":"completed","dependent_files":"internal/handler/health.go","gmt_create":"2026-05-27T15:42:40.1914576+08:00","gmt_modified":"2026-05-27T15:58:27.2611572+08:00","raw_data":"WikiEncrypted:gWB8HBj+8+/15rQhXgtMjCM7vFjnlRAhPoaEN0ftdCdBUTnleag+HHtjerxF8Yo/aNSQCvWghRAmFaNP5Ate+OnNgrZtWxUTlolKrwVliK7qFYneTgLhtk2O0DExsEj8rE3wciXyMoLthA27VSFS1hEh1vYjSsYzEvIq9/P7ebtCx3WZIPnPyfLgw1cXSj9siM5kzk0nmt4Ccf6gZaDZuw0Bp7WplXSset9XbcxKPL+MMIzax7BxVHZuxA+Xr0km0IickRzt8NBUEER7TSRBv327JwCiAS8fQqpTdjIoYZaoZD0fB/NTnHP+9Ak0Q0/FKYlXGSAVzh3CgkU+Bn+Cg5ZTP9yyUsZCouns8PsujUZ2npUR1PLdj9hkBS03HbNE8lSbaX8OU3ViNNk1DL+T4pp2AQ8kjAuM3wbk0fgx0dNKLVy5WY3PlBQ20Fx/tZebK5ym8FUGSHChyldzRq6ZYkNg/glvq7d32Xl8fO5iVdMwkqg1pt1O211CYPFuEOhq+EKjmlkG0WCs/2ee5aQCsJwhqN/Ya6IeDgL+9Db+zeM6x5OiHG8FmYl+N582mzAbn2+xbvZqxlMTJOHjWoHb2LewryAHz2W6QEWwOHKtx09+ET8stLXhUowUkW1gy8qjK+E8U+dj3UNXDeo8cAiAnEvjdmuNwCC15pArY6OHVC9X2U5Vpj5U5NUQu2JMoFgwLq4N3PTIxjJ0A+VzuBk/QcYMjmo7LrfP3y+wJaUZn2vsFEIBQ2eTRD5a1Pe13jvgLYJNHUEI8RJfy1p2pZIQyFO8NTC8GoKJhnSQ+gTu14mmS4S08TCy0fu79yKHrmq1Mxpx9FQeGvkkDg0fwa8pu4D5GP0ERsG/CD6v72/U0UcOO7d7Ncz6OJ+WItEmGuCm3eGoUCNtiJjI5slrLxxlLM3hEF/nte/d/5N2XBVG/xn6/YDvrzqEyAFgYUT/UCUiuGiHkv23qhBSjI24q6ODYIHl3EPfycQLGVtjFwMMFxs=","layer_level":1},{"id":"f351f338-eeb0-4243-921a-eed11d063072","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"中间件模式","description":"middleware-pattern","prompt":"为 Luxsin 应用 API 项目创建详细的中间件模式文档。深入解释 Gin 中间件的工作原理和执行顺序。详细说明 CORS 中间件的安全策略配置,包括跨域请求处理和安全头设置。解释日志中间件的结构化日志记录机制,展示如何统一记录请求信息和响应状态。文档化请求 ID 中间件的分布式追踪支持,说明如何在整个请求链路中保持唯一标识符。提供中间件开发的最佳实践和性能考虑。包含自定义中间件的开发指南和调试技巧。","parent_id":"0506348d-993b-48f2-936c-908972f149bc","order":3,"progress_status":"completed","dependent_files":"internal/middleware/cors.go,internal/middleware/logger.go,internal/middleware/request_id.go","gmt_create":"2026-05-27T15:42:44.058506+08:00","gmt_modified":"2026-05-27T16:00:02.1508768+08:00","raw_data":"WikiEncrypted:KoBWq/Q7Adld3nvKT6YAXjbeYGdbE/D4CStunsqJjgl0jPyMgacfbAcsG69IjV6W+YAUulkiH6yJh6guJ8xsLC0r674enptjON7n1TyvEmDvQ0ts2hwoFwrID/aMzsWc6ykTfCUAJOOlgk4w+ArbzUldUGx6slnJqOHO15q0hPQH7GaO0RDoayr+zF5Md89ka3FMbDJBSe7XpA6zdP+wNXSd04XmKPO4VRjZqmJkxAGC8VFm4yVB97+PFqSyh68UCvChI56FzzpLF/5BFK7vdhB4RKqA66aNRDf4DW25UaKG5whiuqjk/HYzMhxcvIfnxNNQENyjxYlMX1sZy4HsMgkgx/uFM4++tYIvF03t17sV6inMrzPeJGGPuhlz/8276En11T0U+vrBLHt5cuHwwfT9/TbnmX6ii+DQAK6bIQ4TouGACknzbAKKYD527I1FVSQgI9EoSN0pbR8fm6ONx31WoIHCHWp/YV+KoNG4BqHkATQ6ytaki/qaUL/ng+odBZvDmXBjiEPo39VVePVK04bIdD705zKajkzvowecnVXm2AKaMJ69AGERz8Yh8e6TmJNgzoBcYyY4mEun80jcRmapIRl8c8YHgH0NMwaVSwcLqiudxnv3oQr59HZzPqbA2lsjZbIb2PrDETb2QeWum0Fm6rN2Un2jJWVpT0EznojYQrzEaNxrIPMOL2v27AiWIqzQv0GQ3sqciy6SmZiuq2wPG7HlS2A2zZkroeKz2FOR+HQnQPJAYla49YW7f6gn/xufwFLQ2P09Vv1PN1pyxXpNDEuiTqHaVp2kzhv9oqymB7xkMgUWjzJxA3LCPmaLv++QPEBFdy6p8cW4yb4I4EhhEKGmNSkUraKRcmosg4NWFDcipy8qHZXRbF6teMWiB3RRRisWUnfoZUcGgAFFGscCtU6FRbrdh8FZz1dO6dwy+o2iUkSDe5kR8tnRRbjY/fvtiPqMjoBsHuvLo8VgUj/8p4Z2FzifMlFm47HXFcnhNcTyNHadq/BKt32asbZMkyf5mwAazzbaJZ9zE72kKw3V+IXEq2k8PQFilZeDtHzxWba+6jseqSydPEZeopZAtR8inj9373angdQRcahgfTWP2Tu1JM7pp/nx4e/XGXgcV/eAY6K6bheHMiVhe8uqpViCaPM56DFwiJSu8jM4uJ7c2z87Ru/IU7jBjcCel2qrMTwxGrIxl3LJQWNXYmFvWXKFkw1YzGjfH/CiNBiu4ygHy7YalDmDnEePypwhiyZ25shL/pX1OxbV8m/tNZmv4AAk3Q9YjR6tRGu+hpIeQQ==","layer_level":1},{"id":"8085e1b7-3658-4490-8b52-9a61f3e79976","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"API 接口文档","description":"api-documentation","prompt":"为 Luxsin 应用 API 项目创建完整的 API 接口文档。详细记录所有 RESTful API 端点,包括 HTTP 方法、URL 模式、请求参数、响应格式和错误处理。针对每个接口提供具体的请求示例、响应示例和使用场景。包含品牌管理接口(获取品牌列表)、型号管理接口(获取型号信息、搜索型号列表)、设备管理接口(上报设备信息)和系统接口(健康检查)。详细说明认证方法、速率限制和版本信息。提供客户端实现指南和性能优化建议。包含协议特定的调试工具和监控方法。如果适用,提供已弃用功能的迁移指南和向后兼容性说明。","order":4,"progress_status":"completed","dependent_files":"internal/handler/brand.go,internal/handler/model.go,internal/handler/model_list.go,internal/handler/device.go,internal/handler/health.go","gmt_create":"2026-05-27T15:42:25.0310242+08:00","gmt_modified":"2026-05-27T15:49:27.8891715+08:00","raw_data":"WikiEncrypted:aJsVvsatVVT+Oax9nwx6A6Ka1Sy3/Ax3fbdr8CSPsFwpSinOHdKPUiaO43Q3eH/wzCti2uFNYtfdvQWqQWpEsnbUBpqKCbRzHWFAT9Lm270y0Q6B1UE3nx+gx/6onwH05kgU4aSL6Vyf/ZaJTFKBZpR/hASnZEkssdX1yTnZq4HGLCbeLR5p3acQeZtqIab449ZqM5BfhYg1nC4veZGNU0ZbTVY2/+9U92yhi84zrKmyuO7gZe6RpeJW+3R+pbFduijY8FtGcUobIv+f3QcPEH7ZWqwynHO+v7PrlM8OHoMZ4jNowBv+/rK9cg9v/ZMBmZfMQEKxsZsOp3fl4F4qYtSm10mD/w81n+As4xiJhZiEZyEFs5J1d8Z9Vy8wXGEIR7fqm+a9zy8Fsu5byzXecYMhxZS2ERHZx240veYTNST0o1BZbq+K36QEct8vD7FCALNKAcaZ6AOkQ3ZeFQRkF77ySrit0wubK5Y8hJZIiie/lo/zKRe7AcNPttvBv8m/fwMlN9KEVR4OUGV/gJU1svwYIsZZEnrCMvG+y67NRx0KVjqb6L6X54uvguKz7kxDUMczgIfViz8ratyBmUtc4FfEAPRsjCROGGkVoDEN/5w+ZyQXhLxP3xXUKd1/P4J1avMlerh1CuO5ejlxXeucDZUHgRDjHUwBXfwGOMvhjOjwHiCV+WGml1tKFYFwCTMROq63PKS+JNp0RfgSHK+FtVbauDr08rz2vyPg98+5DPKjnqfdczGey2EZtF3aUyS0f+eIwXUXUtiq/w14fn2wmUy4I611KQjjMiUaXGeOEzE6IIDLjUY2tTTxx/aPpqJvhEJsora8N2/6a3xsw6yT6z46cc9FiIhyEApe9UKpdjI1fdeQuYjErjck7jRAWTUhfVXtQU4SeD3ehMV7Y6r1A8qOMU0h/oidZfO1nAE/JYP3YBXGYHS5oK7bg1MAtFEpo1o6sSWrginCktGk52hMNSGoV8nehuwtZxdhXoyY4IXjvpfAFsXH1Hb/mkNuAue8aL73FijK+a6EhDh03TFe0zLWJHxx6x6Auvo7AXWOBCnZmEjakamzlcsky5YeF79LZz5GwUfWnRavEY3T6FwU1CLhWG4vTlOctHSDbxPcek4EYQrGDGh3pm9EKWAbxxjSxaOj+mjfVS/BResVW35tX7QJikLDMO3bAgq1R7pKwJkOq6LKlFGQid7IX3oBWU3vnf68+OZYTo3XbhF1ibJBtGKJo9LJnus5LWKyXL8OlPBZ6O9My/LGSv/u8SgYidk1IqHF9E0GEd1n1XivRudgVPhOyyChOZRTjrfkGYZyCG9JFtb07mHqu1GTttkO+yrSl02PBotGLbcN6fVq9tA+nKBe9hyXj1jRm77qabudCbBq1WfADA3BI1naBKwjd/hkC2WRFu+OobhwEa5BWjLR0S5hhbkX9l8NggWqYhlkNoAGqfQHPfV6TRRUMT+YxiAtQS4xyBRe3hdSlLTfFn2DqhwtS2Lz3mdjaYxot+rBGShwUkEN2YW8NTWAPTsPtxYLGPhsJEGRMeC0e/0pGacNmr/a7f0BDYZipNYXZEpoLO6TRDR2814xSzHlyQHssJNrGjQ7GSSOz1iSfL3l8GvofUZpMwsaNKmd0njucNMBp0oxVTp+o5hLPEwLl45Rkmvpz8NLlhicrw4UvoIUrMW1vLOUsZsH+zfQu//Xuf5MvVE="},{"id":"c79882f1-420e-4d6c-a40c-dff8c7e8988f","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"组件交互机制","description":"component-interaction","prompt":"为 Luxsin 应用 API 项目创建详细的组件交互机制文档。深入解释从请求接收到响应返回的完整数据流路径。详细说明 Router → Handler → Repository → Database 的调用链路和参数传递。解释各组件间的解耦机制和接口设计原则。文档化错误传播机制和异常处理策略。提供组件交互的时序图和数据流图。解释异步处理和并发控制的实现方式。包含组件间通信的性能优化和监控策略。","parent_id":"0506348d-993b-48f2-936c-908972f149bc","order":4,"progress_status":"completed","dependent_files":"internal/router/router.go,internal/handler/brand.go,internal/repository/brand.go,internal/database/mysql.go","gmt_create":"2026-05-27T15:42:44.058506+08:00","gmt_modified":"2026-05-27T16:00:37.8856299+08:00","raw_data":"WikiEncrypted:4Zqjl5aZEo1Vv5NKBpebTvIjOs8PtjUiIDyDFsBcQ7xcxPCSMrQFGywF92hdzdj0/Ns2K9dSEDwOkKxz9EMCD21sNknfTIWpC88CWR1X7WuNlX6UdseDM8NhczwhCNaZN3vgjI2RCnPQ3swJEmcffGwsCSYh9whfm7CRrmT1BsJAsL0IISPTl0j7Dypz7NiXQSwlVYOWLyyU9AbzCZDbJ3PpxFYDDzPxaDuVWPVPXLf/wiv9mA5vqzDDmAe1dkhs/NMef3G3n7dFGNy8bE2Kb89TGY1mWlLXsD5yz8TOX2FASKkcwVpddx7H1G4BCCeVxnXrAcc8YtLwtNLqhtYwO9sMQvaZN2pzzBQEBT+bF1KsPSISX/kKBTBc8lKQzbtXgZmpqxMtrqwNvB4AFvHvruBwy5i5usgPxShB0x2ZSOxEQKepTRqJ0sEJxpCG19CRKrqMYxxZMufZecyJcMUUEey9yDRO/hzyy2/pZqn2ZFzkp5KdT+gamSrlq3mJSAJwW1wr584b5P2Bept3By31QHtVKY46YC4WYYzzF1oCjY4mxHTxeyK3KpEfyLa/UEH4JnKUxpA+XrgAxuGhWNQ1XSDiZSY0bXbpklSWH5wVZx9XrhNt1Uv4/Y71JhvdR+D6xb0dEQGbyblvvsAJ/FVnU0+D7VEY0HA3iF8VkSWyCEqfQ/JIf8Ek9Q5LAgQNfEMhoPlzjGIQgoKYiJiPUQYjn0iYa7VDL7waXMyHlwcRIghsmI3fkNu8lRCggKYPTT7Q5qbf+1dABN8HQfKSQ4G3Fyki8uSSBxsxPNBAv1YZhpejX8VnKJ4bcTlb7j+urjrCDfKdzBS3GxW7JA4R3YybG4pIpCHM1znO6FBc0Vw6nOB3lfGW47z0MjpFN3fcV2sOG8ckI47G7KXLi5mabdHP+LyR6s3mSWlLLu59L/RXid0QNasUkkPc7dDbDL9DeRvMlBo5s/BB0qZ3OplmHq79N7OvD8hC8Exo5pNRlCuQXyk/ki56Z2SI5K/AGP1lMNV0tGKe91nJRDGKcxar2YY7tk+dtyGPslSVCeJXmY6OBED+ODDNJ8Ro8sqnENFk2pjIC2z7i8EinBYXDVCn8Fne/tZy4ylO64uHF66MPf3pwmepe2hwAyEDF06K+eddiX3JlmF9xi7EaZS+y+R6h3ktk3ku4KD0Qd86UyfJ5b+r1X4JFTmE2KfIaTeUpCRtOdXJ","layer_level":1},{"id":"cc6acec0-b687-417f-9c93-00f59162cfed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"数据库设计","description":"database-design","prompt":"为 Luxsin 应用 API 项目创建全面的数据模型文档。详细说明实体关系、字段定义和数据类型。记录主键/外键、索引和约束条件。解释数据验证规则和业务规则。包含数据库模式图和示例数据。说明数据访问模式、缓存策略和性能考虑。指定数据生命周期、保留策略和归档规则。包含数据迁移路径和版本管理。涉及数据安全、隐私要求和访问控制。详细说明 Brand 和 Model 实体的设计理念、字段含义和业务逻辑。提供数据库连接配置和查询优化建议。","order":5,"progress_status":"completed","dependent_files":"sql/model.sql,internal/model/brand.go,internal/model/model.go,internal/database/mysql.go","gmt_create":"2026-05-27T15:42:25.031564+08:00","gmt_modified":"2026-05-27T15:47:52.3231001+08:00","raw_data":"WikiEncrypted:veTYwq2y4io5qXerCTrkkBwdhOAmcMQTz+AJLOFORhNFeFTAN7xitXLxeRr3T1sKrh0z/6NFLuxfhi+XJSCnWiA4bOgOqwoJp44HwLqumks3fTrub2qfkx3kGo8Vv0zwaj7mBYt8gy+IsUDExaTSt3z0xSYoiMJRoiBXhAlNc3py65rPX7myyVo7X5aWc6AOTgywn0pmK3XweYJTp/OGuhexu3owdi0ojlyjLM5fhrVaPCMSjZlCmYDKjdRDiJgH8SUT81h6jSMp8DVFvov1TzHKQO6fEt1yGV1rxGiKDrIs4QHO32th+o7k9tQYH6CSnDoMYk4ZYZUaCxNKZcIp1I2u8cv/mKHnKyIHV6o157UJotCffdSghARHRgXNbZRFJKw2LlW0eFfRB4rXMpkNhwxyCyfJuS9HOpH2lBPYNg37sMa1V/Eu3/Q6COF5ALxno0JO2w0V8DANenW4lwqay9TduAWeV/Wd4/dgnc0kGDAAqO5EpRQJ05PlZiAK5WPSeJcE1FMCShp43SRUeu0Is+TvJKih/ixtdr5ZN0vVE+0okdcGzxP1GZGfVlqDy80vdMZ4WJdIHu89JzvzkS2ptiMw834+utWhBxxpJRRDvJl4Zrs1QcGgN5sZCKlUYb1Fb2NOabQvc5fyx8yVZnMaEZpko17DLilQmJCCgrVdi3p2OeIBmnS81Vz5vmsF+754mz9081paU05Zvu1Rlqpklsnxlsn1KOJaq7CxyN1Vcn7SR3cBEu7L2hL7vLqTSlTilbnVUdfG+udOd7z4QHQfU7qOkWy5HkYrcuXrTycl2ufPlCeAr479dPNQ0Xk947AQs7w9xu97C9uZA8SNXmqVZxe/h+O5HjlMfIT6tt8mxvEaeqK8VOrAJKUIyPjs/joJZGQbF1Elfpx5bcIUUpIPc/JZZJaeEsgIox2ZEwOooQJ8nd8HwCEY747ONvgtaJ5q1ezLwIyrBIAmhB7hha3NW5XM3pTSnhhVF5uzeW/SrLPa7/kLcN+z6qciY2+1f1Di3u1j5vjLI3vVhWYxtJeb1HFtQ00iXdel0aXgFh1WDymjQp+3kFFEFC7ujRqludkNuXwc4VxtV1bCbiMRuM9Me/1hxMYQsxfUqeYWXsbBN+TUkt5+5dKDAfjIrRn1uSgSIvVIhez+1s+xqnAoLrGJnH10tSz3gtZIy6HHkEXNgV+uGynd+yFYQPyVzqFl6ycwI4QJr3nkZe2cmn6FJQbfcuksp/k6A7IRaJ1aeEl9xJZ2aGXJYapB8ZgUNClFNIABpvB/G/YA2vmhfZp0uQPvIRc1ND/6GmGWg8sdS3QLzprJuywzZHaPbYAlocEixTYrXFTgWuXbhn803vpaRzzwcwy39VcvjxmM8EPgA8UchNi9kMeRVrOORtsa4ws4iQcDlrdOvw9lPOco6pAgMVqHWw=="},{"id":"0ba3accc-326a-4433-930c-5b951d4e5dcf","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"基础设施","description":"infrastructure","prompt":"为 Luxsin 应用 API 项目创建基础设施组件文档。详细介绍缓存系统(Redis)、搜索引擎(Meilisearch)和日志系统(Zap)的配置和使用。说明各组件的初始化过程、连接管理和配置选项。包含缓存策略、搜索优化和日志记录的具体实现。解释组件间的协作关系和数据流向。提供性能优化建议和故障排除指南。涉及监控告警、扩展性和可靠性设计。包含具体的配置示例、最佳实践和安全考虑。确保内容既适合初学者理解,又为专家提供足够的技术细节。","order":6,"progress_status":"completed","dependent_files":"internal/cache/redis.go,internal/search/meilisearch.go,internal/config/redis.go,internal/config/meilisearch.go,pkg/logger/logger.go","gmt_create":"2026-05-27T15:42:25.031564+08:00","gmt_modified":"2026-05-27T15:49:51.0048099+08:00","raw_data":"WikiEncrypted:sbHxgqwNaIQfuetky2uLRyIOX0QcV4F0AILzzQc9GWKM9RbXWkeyZypYurT6q6/jdshRRky9i8euWVz+RwzDEjLSf/5TBE5hqVfwtdoniolHNIvg/V4U6o0Uy5LnDr8i+8lIxu2oIBTUlsaoAv7w5iNfekHSt0q6goXepa4aMrqdtCXd78BSUFzGGoqTjFCImp9K2wkx2G0tXqpmpVp7tEiupbmaekaydnQiLfWCTsuAhiIixmNf7zhMvQddDdz/vFMxg8EgQjBYCRrLJfa59w1dwNOsh7LD7B0xejH7YnQkazsXQPDc/l1CAXfLcIcNw18T1VJ8pdgRZVu74q4BhQ1oz+mkyNXOXO7uFYyROvZ9+SHCV/p2M5/xkEo3CqCAP+CEI+59/1ozZqaYwitDifyO7wkSXdFb88+pXlAGZCDRQ9iQ/cMLmgrf0UWW3ZQdLAgo9PRlh3nTWGEe+/rkwMoVKFhvAJBooUPS4OIGSMvMAHQrbwvx/poFrNYVDC84lwkm0R3PHjhB1K9cOk9+6tZ/6F7kp3XdCVAhc2U62dBlI8hDoyGxT1Jl75o1y6mLlxFRleUEYQIFasnt0X1GgKG3oeRJfDQBnoW5H7CZ2Q2VQG/Mam6+pMMK/r9diCnIYM5ogiqUj7tr7iypIS+cfkJESScxOnrfQlCN0Qkd8sEGP4K1s0MOXaL2n/I8jL0akBuCdC6r/XwkXXVuwyvONhBo8AKhjlw4Z80l8g9IfCLE516hSHdAvPrqJdQ+uv2kxoDUCbdnki86NLEmUIu9E8Cg/ctAE8bB/3EZ8W0jCiwjkJMG/7zWgjmRnEZEB3qTVgtRa2KlCUd2QJmlRplYoHxZpXr+SKdJwnlA3vad9yfoc20k1McD/WIU1xyYcMO0RlCh7D9YFySaMEU0ajxvhY0IaEzEkSksTTGrckLMEjYGic+cCNwIIo2yh4+RiMTixfUw95/Beb5C6YA4wOzhKhEYwBsyKI1mRNwEf9R87hDhmK7a83qw/cRy9X8q37PVUu4kfKUP00SAvuQVxgyEp5jO+UOmYubsAjFLqt+BLYCvX1BLVs34bTjTZ7UdPkyCewtgeK88zHgLljGSzyR1bspq4eozKjqoiqavFLVqYzTtwTuTtg5HzkqhFBkHceFCQzoxpbcgdyvxHj7rIeuao76nbRqwWFxM0CL/w2bwd3YhIHOUtNX1PHq5deeuo3macM8moQOj2mgBCoh3lJdc8w+9qpT/nooQSlJfOQ8o9+an8h4sgvMbAbGt6Xt34gkJnbQ57qn+h6fXefwWrxaekNiXWaEYYRQlaazmSk/IH+Shr0OZVtpWlkFn7FjYCqe68ScAMyhmZUJ5Pkw+Y1rENFipss0BlI+diw2G12+r1GuERworaWCvas6w5A3FkFKC66BELhQjLk0vsY9c19ea9GihiHshbDr4OSKya8FZ5nM1jK+4vN63WFEnIS/vVAg4NxpzpBa91cZOB3hHRNKWuG9Zv2L5ASdR8D+2EwNxXV+A0vv1YgBt6lCQ/uDGyRePhYr0xyoQ7QfWpRQ+EkUPCA=="},{"id":"f4f8585d-1446-4f49-85b2-efc715bda6cf","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"部署运维","description":"deployment-operations","prompt":"为 Luxsin 应用 API 项目创建部署运维文档。详细说明构建过程、部署配置和环境管理。包含开发环境和生产环境的配置差异、环境变量设置和数据库连接配置。提供 Docker 容器化部署方案和 Kubernetes 部署示例。说明监控告警、日志收集和性能优化策略。包含故障排除指南、备份恢复和版本升级流程。涉及安全配置、权限管理和合规要求。提供自动化部署脚本和 CI/CD 集成方案。确保运维人员能够高效地部署、监控和维护系统。","order":7,"progress_status":"completed","dependent_files":"Makefile,internal/config/config.go,.gitignore","gmt_create":"2026-05-27T15:42:25.0321059+08:00","gmt_modified":"2026-05-27T15:49:39.6683467+08:00","raw_data":"WikiEncrypted:0IKfLNOWe9mZfG1jVts3w2XbjO+yyEmY3ONOjrL5GjA3YcCk7HFAQ1etKc5i1P7UH3oVY8Um0qaAPkZpvBj4kR5+MfLB2lNNE8Rq4eigRbWcSc/FwkPYo+Lgq1jG1nwAdVBamYbeC5rJp9miML32uAYGdDFNBUjcZautOKk/RnMVJbDBhxk06H3YR9uha+agF827jtBxrCN+YUD41ewpmu39XkuD0mIkISY0mw2kTQ3+d3o8egfEhqEtpYEFWDr4W3bjpZzdlqibicqkrLymwjSHLQ25Yk3RDRLQh41MEe9NzR7Dq9pho9nnzV6lVFI/+D3aLuQmEW30BSE9ANU6hjMLS2wzjZI20qiE3EYf9+/cetjKX3PuzziuE6FOa9zkRQET5Avno4lvS+dZE+/NCsoLeLBSpEpHChwy1FLMangpeABL3DMfOcjKBI10Zckgxc6knhlzmH7BYem/MGjE3gGbpXcSOYO91tlzjpTzkA6o8IN0BBKnVEm5ZsnA1/bpvtl8iPNcBLSvEh8r9nsvT61wFloxMMEwoqk4pfGxfYLYpLzHK4/B7kcP2F2ZxPsLNKguH2P+W5Onf/VEoeoeUie2shjfuxNczO0o+tk08s7YDF0suC+hauvXM8A5MtTJFjGAcXk0PepsGbBA15NaUxXQL+T9g4ZmHiM+HpXAVuFUtKMwAoMX7ejSmhK0Uu3abxpuatBFVQMyMn2CtFqbZ7qowCiFy8R+N8iJK+CNvcQX2UDetV403Hy9t8wz9Mb7zjigKo8EExeqBR+6tdzkd2jCxuo8oGTUNED9jyF3yEdLlhvryX6R8fmWeX6XaIUdEfwqd3lefqvppQLJ082CWs43k5Sa3wBYSrhvhINvH+qH2TvKZHwT6jdxjjHkPO23f0RWwLtVr0KwjqIYa2C5ujlLLUzPccXXZozTajzaXs4y1GIXIkjQyN9gkvew0CQdmqcwIakk6B1cd6GSvR2R7jI9aORlvoBPkZZZZaWccD23SJpY8S49pk6J1f4pp8SY58Cs5Tyhzyr9Jx/g0sdQhX5jT4NOD4vFTMDi/QwlxJVK5aAMEoni1Xixkn86Mvy6YO+C0syQmDIeaWwTw3oaJtKkp0rbEph8ikP8GV+vYas27rEHT5ikXy+qKaSpRTrS6pIvqMDTr46wYVA48CocbQCKuCxz+8aN1rxEOyVvF4BZIOGK94UT/86do17dVZDlKWyrMHyapzdcsG77AQVqx3wFdVBRhW+LLPtZ7DjrJ9MbZlWCzqTN+MPtV2pqJpf2QM5tK7xYlv8Yb6jOUmKXAJ+GSoB6Lnv55zJALFR7zYrYJqdnAOfLrd0Tx7cB5v+9"},{"id":"29142a7a-fa93-4a61-8ad8-c35c1b047599","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"开发指南","description":"development-guide","prompt":"为 Luxsin 应用 API 项目创建开发指南文档。详细说明代码规范、测试策略和调试技巧。包含添加新接口的标准流程、代码审查要求和最佳实践。提供单元测试、集成测试和性能测试的编写指南。说明调试工具的使用方法和常见问题的排查步骤。包含扩展开发的指导原则、插件机制和自定义中间件的开发。涉及代码重构、性能优化和安全加固的建议。提供开发环境的配置和团队协作的工作流程。确保新加入的开发者能够快速上手并遵循项目的开发标准。","order":8,"progress_status":"completed","dependent_files":"internal/response/response.go,pkg/encode/base64.go,internal/middleware,Makefile","gmt_create":"2026-05-27T15:42:25.0321059+08:00","gmt_modified":"2026-05-27T15:50:56.1828091+08:00","raw_data":"WikiEncrypted:F3QgleoEfoy16cQggYe9C/ucQxiwh0okVA48AmODs5RKNS8ZgNngqmnHSFcG/1UaJxx+3dUn+EkMTpYUsOg40sht54cXVXsXhnq+u5RoF+QCaIHA1bGkkQVYVJAIxj7oosqEK59VnFAy3/dJV3KPSb/Ak2NMFBLBF1vGpBDxfvX3bT23bfnsRo4s15uorwKaV7X3HasHsjQoW0eNqTYHiGBulek8S0+90Xknrv9THoJiqpJqkBeofpQiXqX5V618tXTFFsKRJ6phlpbQWf2cbMabajttNJ3B44KtoJSJow48koViUqq1GW3BavJLaIXKMSMHmg9oSjXlTiTYcEEZKa/kYEu4HzH+CTxYhmAZitafJJSGarLyIbemD6c9dKLK9Frtj68/dJRxA2JDgVe81LYeb/3xRsBom0r4YzT8A6hHZcszh4H3wvsJMywPaPsmmBX1tXvgqCTmKeNH7dyLyLi3kWUTw9ok4pV8mMbQVe+tyq+dAjkFZ19Gg1pa48jOuq3XNtyOUDkFwzf8ENVW6mMjhd6eh1Wngr55aY62gP2kA69TRUUa7xCg4pPwMd7kukU9I+311WRjxwqdx+K9z918Nh+Ehzce//3AN16rOPpfJiqCBsWoIGofgkkvOrhJXb7ZBSV8svzmyNFJAEwluR6iW6JSQF8aYcFWeQvuFotN33nkyOjukcXDEGZsNzH5UtAJyLrlT7/Or9ByIMARY/bLEe2ttw4LkzwPX0zVri6275Z+Tr+vS1tQvbizhQX+2d6jRwB57dWTT5wiNTTauC0iaRxAQAQpIiilb6edtE9uJDim04tl8b0GkMJvSCCVUwIKIl495jl1QK4bnCVwbXf+Nm9M8b2DoPU91RhXbRUH0oa9hiK/MMmM1pNPt8sIQPodxmby96ls9G7B9FZz54hyvdDRZXOoxueOW+qswnlAT8Pjzr7WYDEFWLqhVHl3YjAnYEyvqPyjLV10UlsnTu74OIH6TGCiFnRNdEUNrebnly9HRW/qPROmHDa6/S39zhxd/Wve8OTESmdoItuQITLhTn+hUnhZoQBVVqWJwAYFrayIms6xJZY8R31ZKxaxYGd9xNCp3P+7To8IS9yboFTqky2xkebLSLfn+HS7xe0MqZXcpzdCKfivdIbm1ebxh/61TCdDH2Wx1T0uqMlwy70jnT6dzzS860fOuXLUw+3uEIg/ohj+iqYOEEPHAIG/ADJxSwbTR4/RjVzy+yaWUiKWxLukEG89+389O2WLfiqMNBTDEZ4+iXaBOjZ2i8yTTW0xGChxJcCUGPmug6KyNPvoEDLiPot5cTWScQG04DSfIGUnVEqHKDxX3mS/pZDnfV2OkxwUfNtqXdYzFlWBWnI9TxnL3wxXTu3eI8SYHxflga+4BF6OXBwi8eu8DXYDarKNvnEn0JHi0Fh8VVcAoZmJeQkJUhcvnhyAKmJ0tD21XmK4/7ZcFZ8RuBkIEpDy"},{"id":"4d1d9a48-feab-42a0-815a-edf76dcaaf69","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"故障排除","description":"troubleshooting","prompt":"为 Luxsin 应用 API 项目创建故障排除文档。详细说明常见问题的诊断方法和解决方案。包含数据库连接问题、缓存失效、搜索异常和日志分析的排查步骤。提供错误代码对照表、日志分析技巧和性能诊断工具的使用方法。说明网络连接问题、配置错误和权限问题的解决策略。包含系统监控指标的解读和告警处理流程。提供生产环境问题的应急响应方案和回滚策略。涉及安全事件的处理和数据恢复的操作指南。确保运维和开发人员能够快速定位和解决系统问题。","order":9,"progress_status":"completed","dependent_files":"pkg/logger/logger.go,internal/response/response.go,internal/config/config.go","gmt_create":"2026-05-27T15:42:25.0326686+08:00","gmt_modified":"2026-05-27T15:51:51.4638338+08:00","raw_data":"WikiEncrypted:CaKOW8OSSWs4aEYk06Hu0hGGy7zXJE5t3XZJK+9x87Wqy/1Xzi+22LbOfczFZkieMj5WEpLZAHMOxW2WrQJBBBBLWDFSQSfMzyLUONYz2Nl7KEGwStUbBydq7XRqsFITwnt/V34PJe33lfOUHLZdte9ZHqIcBUofSz417l0pyB/LxfxiLi3iV7uxs4nc6n2EFO0hgr4tRmmgaEM1CEq4EgiztB5945G3QMRbLtVRjxhkZe9eWYo9vc2zRYWsyCp6nsjGToiqmfJff57lDOhyn0MhEnJamhFrscVK3DVyGf1SpxoYkDkFQ8OxuxWIHhAN31k2cpGQv9LmsRHcL8ratSFNuE0JPDtSTdY/ZWlXZReIKt/Phb2FMvUA2BI6cCF2Evsk8rpZnwjGsdOlJ+sGevLXEX+KO/9vz0AE6s+rE5u71DR5usC6vuuFgFV4FP7o+3Lcj81SBIu9V+wYU4GkJfPm9eZdFSFgsvHSuqFzrpdlcAeV0XMnU6wZcRaZy/8ZdaUJaIWp/K5jIqfGuxtPSOD1zDJ9Vw0jCLMjDlUTY0Wkzwykxog7i2SnG61vtu/eLORV6aTh8tjNhzdBwbROp3XlsxD0Jxg41JcDSyt4dpyQ35FAPBMPr7Mb3YeD4W1JWqrn8tR5Q1goBmPFlekKRd1XmyYQSZsK4FvBVOUBeCzX1UO7I505eXz/lN/ZG62xC3zhdCt+qCOxJogd4T1f1Nux404SmdsvewY4mg5PnyoinMtMIMX67YY+iFfZEedlNURipIOxssz0zcp+qeJXI9lwR58sgAvpu9JbTbVQJv4I7n1XM9CayA7Nhd0WXIh+yChGMyFdfq6BDje0NtSgi44k/h+mFUk3BqzfQWasqg7rPzAKjx23DY3Wf/8AvNN7IpP2CH4yRynuCGZim6B1AEqKfL6RCsXi3dpi6y6HmEO4DRk7XL8l9c+vWr+u/yI6A9OGZtzl8B7MoVhhEuuTvPbLCrwnHsU3VReA05fImoZPThjC0vYsQuP5jijDtjnjnShV/o1rLBat0xPAhTT0+Ba23IeZGODiU8yg7NtgprzmcJSS/wFzeapmPZYCB70qjxvW/HgkOhwl1TsfHNP/T6k6i1j6gSPIxgCbZzvcJrH9sumu84lcS6Yfg9Qvr7PpOAuDKDj7riCajk2RbdUVxrptqpZkD0kYIKictTHLnL48Uvp97xnspGQYkJA4BAv0grWVZFzqj7o0BlwD7oL/FLdBMSbXkNRCvjQjGnIZ2nq8MEZP3aofJTmW7e63/JTbcplJzE6wdft6ZBF4hroZE8TbT/pMlEMDkQDxYbgPETx9z/quiPJ+0p6dAD2TjJdiV2GfAFz6WEnJ3NljmFPBV2/5DJ4ugFlz6oBXJZArZyUzJlbmfIXasvwycIUFMx470AIl4o7bGCbGGL+tWgljzbkg7dly6G5lMTM+gWVGvWdUtXCOvYLq+oqCr5Uddgt0"}],"wiki_items":[{"catalog_id":"78b906f9-f81c-4bb2-b076-5c5609c7eb41","title":"项目概述","description":"project-overview","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"f30115a0-52c0-4f22-8486-ec2e6c334e6d","gmt_create":"2026-05-27T15:44:16.3635684+08:00","gmt_modified":"2026-05-27T15:44:16.367324+08:00"},{"catalog_id":"6de9a03e-f360-4daa-97ac-dc1cd1937916","title":"快速开始","description":"getting-started","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"61e1b102-9ad1-44f6-accb-0e27be48ceb7","gmt_create":"2026-05-27T15:45:02.5387528+08:00","gmt_modified":"2026-05-27T15:45:02.550849+08:00"},{"catalog_id":"0506348d-993b-48f2-936c-908972f149bc","title":"系统架构","description":"architecture-design","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"ab98c251-c5f7-40df-bba1-21b2edf5d220","gmt_create":"2026-05-27T15:45:15.0879497+08:00","gmt_modified":"2026-05-27T15:45:15.0922502+08:00"},{"catalog_id":"cc6acec0-b687-417f-9c93-00f59162cfed","title":"数据库设计","description":"database-design","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"120f3824-43f7-4a38-9680-43b5b2f59e05","gmt_create":"2026-05-27T15:47:52.3181698+08:00","gmt_modified":"2026-05-27T15:47:52.3231001+08:00"},{"catalog_id":"8d7c257e-b299-4adb-adfe-bc0ddbf112b7","title":"核心模块","description":"core-modules","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"7563e383-3ea0-4630-baeb-e86efcaeefb8","gmt_create":"2026-05-27T15:48:30.8129369+08:00","gmt_modified":"2026-05-27T15:48:30.8195851+08:00"},{"catalog_id":"8085e1b7-3658-4490-8b52-9a61f3e79976","title":"API 接口文档","description":"api-documentation","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"6159e8a9-0f6e-43af-b097-bfcd16c8b223","gmt_create":"2026-05-27T15:49:27.8849802+08:00","gmt_modified":"2026-05-27T15:49:27.8891715+08:00"},{"catalog_id":"f4f8585d-1446-4f49-85b2-efc715bda6cf","title":"部署运维","description":"deployment-operations","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"1da760f2-e12f-4b50-bd2e-539ff08c2047","gmt_create":"2026-05-27T15:49:39.6657096+08:00","gmt_modified":"2026-05-27T15:49:39.6688705+08:00"},{"catalog_id":"0ba3accc-326a-4433-930c-5b951d4e5dcf","title":"基础设施","description":"infrastructure","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"cfbcaa79-26f5-402f-8746-9fe524993684","gmt_create":"2026-05-27T15:49:51.0000417+08:00","gmt_modified":"2026-05-27T15:49:51.0048099+08:00"},{"catalog_id":"29142a7a-fa93-4a61-8ad8-c35c1b047599","title":"开发指南","description":"development-guide","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"081f97c7-d774-43b7-aff8-4973d577a059","gmt_create":"2026-05-27T15:50:56.179658+08:00","gmt_modified":"2026-05-27T15:50:56.1833555+08:00"},{"catalog_id":"67d62b58-6970-45ca-a17f-f48cb2ea679a","title":"业务处理器","description":"business-handlers","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"d1a696b8-5a9e-4ad2-8777-04eecc0bb424","gmt_create":"2026-05-27T15:51:42.2402901+08:00","gmt_modified":"2026-05-27T15:51:42.2445399+08:00"},{"catalog_id":"4d1d9a48-feab-42a0-815a-edf76dcaaf69","title":"故障排除","description":"troubleshooting","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"3ce5cfd0-f4f6-439a-b60d-6fb4c36a26bd","gmt_create":"2026-05-27T15:51:51.4591126+08:00","gmt_modified":"2026-05-27T15:51:51.4638338+08:00"},{"catalog_id":"591ea147-ba05-4d86-a671-941af668ca2f","title":"品牌管理接口","description":"brand-management-api","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"6f1fe170-ae35-4660-91dc-ab89735274b7","gmt_create":"2026-05-27T15:52:20.2715607+08:00","gmt_modified":"2026-05-27T15:52:20.2747568+08:00"},{"catalog_id":"6e42204e-3d78-4997-a95a-3311c8fcaa43","title":"缓存系统","description":"cache-system","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"795a5202-49be-4c11-a2b4-44312eca8ae7","gmt_create":"2026-05-27T15:53:16.9891352+08:00","gmt_modified":"2026-05-27T15:53:16.9929309+08:00"},{"catalog_id":"fb8ae541-8bbc-4544-9133-e797be1433e0","title":"分层架构设计","description":"layered-architecture","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"523107ca-244f-43c5-a4fe-4649e36a88ad","gmt_create":"2026-05-27T15:53:50.4703345+08:00","gmt_modified":"2026-05-27T15:53:50.4735252+08:00"},{"catalog_id":"948e0792-2de7-47b1-b73b-e7b246e749ff","title":"数据访问层","description":"data-access-layer","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"599c0bda-3332-415c-a535-362503079473","gmt_create":"2026-05-27T15:54:10.7247654+08:00","gmt_modified":"2026-05-27T15:54:10.7298081+08:00"},{"catalog_id":"8b3ba473-1cf0-4471-a203-9a87732dc227","title":"型号管理接口","description":"model-management-api","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"6af33e3a-220d-4e68-87ed-bfb96bda4c3a","gmt_create":"2026-05-27T15:54:37.220316+08:00","gmt_modified":"2026-05-27T15:54:37.2236089+08:00"},{"catalog_id":"5b9847ff-f7b8-488b-821b-6fdfe2dbde40","title":"搜索引擎","description":"search-system","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"d0c2dea0-a71a-4f16-9889-b37e1e7996e3","gmt_create":"2026-05-27T15:55:50.6464041+08:00","gmt_modified":"2026-05-27T15:55:50.6500526+08:00"},{"catalog_id":"ccdbaf47-6cde-4ad7-95f8-5b92d33e2440","title":"路由系统","description":"routing-system","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"f4014d47-467e-4cfe-8549-1dfb69cbc325","gmt_create":"2026-05-27T15:56:02.5802842+08:00","gmt_modified":"2026-05-27T15:56:02.5839243+08:00"},{"catalog_id":"e81027fa-c24e-4492-8100-f051facb89c9","title":"数据模型","description":"data-models","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"97c42095-5b4f-4cec-bbbb-1ba8bb9981d4","gmt_create":"2026-05-27T15:56:31.7739386+08:00","gmt_modified":"2026-05-27T15:56:31.7808704+08:00"},{"catalog_id":"8b7558c2-be32-47b8-99e2-1964009b4337","title":"设备管理接口","description":"device-management-api","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"e1ed4fc8-f1d6-4e8a-907b-9c39bbe13b92","gmt_create":"2026-05-27T15:57:09.1993166+08:00","gmt_modified":"2026-05-27T15:57:09.2037555+08:00"},{"catalog_id":"bc6825c8-9aa3-4f0e-96ca-ef4925af0570","title":"依赖注入模式","description":"dependency-injection","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"5910d326-0408-450d-b403-06a19f3da81b","gmt_create":"2026-05-27T15:57:20.0015238+08:00","gmt_modified":"2026-05-27T15:57:20.0074643+08:00"},{"catalog_id":"4b0e33d8-28d6-44dd-b90a-57af5c6cfaa9","title":"系统监控接口","description":"system-monitoring-api","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"0fc3d59c-d219-4f97-af10-473ee62f4de5","gmt_create":"2026-05-27T15:58:27.2590583+08:00","gmt_modified":"2026-05-27T15:58:27.261717+08:00"},{"catalog_id":"f5a7db4b-765d-46e4-9395-af7bbe8d8fd3","title":"日志系统","description":"logging-system","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"23a2ffc3-fa50-41ba-b7a6-55354d68a95a","gmt_create":"2026-05-27T15:58:35.6388373+08:00","gmt_modified":"2026-05-27T15:58:35.6424946+08:00"},{"catalog_id":"e2d8da89-04c9-4fee-8dfe-adfaff79c9c1","title":"统一响应","description":"unified-response","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"699a84c5-5140-4267-861e-62bffbffccb4","gmt_create":"2026-05-27T15:58:41.4359+08:00","gmt_modified":"2026-05-27T15:58:41.4408299+08:00"},{"catalog_id":"f351f338-eeb0-4243-921a-eed11d063072","title":"中间件模式","description":"middleware-pattern","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"e8e5fe7e-cc7c-4259-95d0-f6b46d888ed8","gmt_create":"2026-05-27T16:00:02.1459491+08:00","gmt_modified":"2026-05-27T16:00:02.1508768+08:00"},{"catalog_id":"c79882f1-420e-4d6c-a40c-dff8c7e8988f","title":"组件交互机制","description":"component-interaction","extend":"{}","progress_status":"completed","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","reference_count":0,"id":"5a04f737-b877-4908-a4fc-67a88614aafc","gmt_create":"2026-05-27T16:00:37.8802374+08:00","gmt_modified":"2026-05-27T16:00:37.8856299+08:00"}],"wiki_overview":{"content":"基于我对项目的深入分析,现在我可以生成一个全面的项目概述:\n\n\u003cblog\u003e\n\n# Luxsin 应用 API 项目架构分析\n\n## 1. 项目介绍\n\n### 目标与定位\nLuxsin 应用 API 是一个基于 Go 语言构建的音频设备(耳机)数据管理服务,主要为移动应用提供品牌、型号、设备信息等数据查询接口。该项目采用现代化的微服务架构设计,专注于音频设备数据的高效检索和管理。\n\n### 核心目标\n- 提供 RESTful API 接口,支持音频设备数据的查询和管理\n- 实现高性能的数据检索,结合 MySQL 和 Meilisearch 搜索引擎\n- 构建可扩展的缓存层,使用 Redis 提升系统性能\n- 确保生产环境的稳定性和可靠性\n\n### 目标用户\n- 移动应用开发者\n- 音频设备数据管理系统\n- 第三方集成商\n\n## 2. 技术架构\n\n### 整体架构设计\n\n```mermaid\ngraph TD\n A[客户端应用] --\u003e B[Gin Web框架]\n B --\u003e C[中间件层]\n C --\u003e D[路由层]\n D --\u003e E[处理器层]\n E --\u003e F[仓库层]\n F --\u003e G[数据库层]\n \n H[Meilisearch搜索] --\u003e E\n I[Redis缓存] --\u003e E\n \n C --\u003e J[CORS中间件]\n C --\u003e K[日志中间件]\n C --\u003e L[请求ID中间件]\n \n M[配置管理] --\u003e N[数据库连接]\n M --\u003e O[Redis连接]\n M --\u003e P[搜索客户端]\n```\n\n### 设计模式\n- **分层架构模式**:清晰的业务逻辑分层,便于维护和测试\n- **依赖注入模式**:通过构造函数注入依赖,提升代码可测试性\n- **工厂模式**:用于创建各种客户端和服务实例\n- **中间件模式**:统一处理跨域、日志、请求ID等功能\n\n### 系统关系图\n\n```mermaid\nflowchart TD\n Client[客户端请求] --\u003e Gin[Gin Engine]\n Gin --\u003e Middleware[中间件链]\n Middleware --\u003e Handler[业务处理器]\n Handler --\u003e Repository[数据仓库]\n Repository --\u003e DB[(MySQL数据库)]\n Handler --\u003e Search[Meilisearch]\n Handler --\u003e Cache[Redis缓存]\n \n subgraph \"核心组件\"\n Gin\n Middleware\n Handler\n Repository\n end\n \n subgraph \"外部服务\"\n DB\n Search\n Cache\n end\n```\n\n## 3. 关键实现\n\n### 主要入口点\n- **服务器启动**:`cmd/server/main.go` - 应用程序的主入口,负责初始化所有服务组件\n- **路由配置**:`internal/router/router.go` - 定义 API 路由和中间件配置\n- **配置管理**:`internal/config/config.go` - 统一的配置加载和验证机制\n\n### 核心模块\n\n#### 数据访问层\n- **数据库连接**:`internal/database/mysql.go` - MySQL 数据库连接管理\n- **品牌仓库**:`internal/repository/brand.go` - 品牌数据的 CRUD 操作\n- **型号仓库**:`internal/repository/model.go` - 型号数据的查询和管理\n\n#### 业务处理层\n- **品牌处理器**:`internal/handler/brand.go` - 品牌数据的业务逻辑处理\n- **型号处理器**:`internal/handler/model.go` - 型号数据的业务逻辑处理\n- **模型列表处理器**:`internal/handler/model_list.go` - 结合搜索功能的型号查询\n- **设备信息处理器**:`internal/handler/device.go` - 设备上报信息处理\n\n#### 缓存和搜索\n- **Redis客户端**:`internal/cache/redis.go` - Redis 连接和操作封装\n- **Meilisearch客户端**:`internal/search/meilisearch.go` - 搜索功能实现\n\n### 配置管理\n- **环境配置**:`internal/config/config.go` - 支持开发和生产环境的配置切换\n- **数据库配置**:`internal/config/database.go` - MySQL 连接参数配置\n- **Redis配置**:`internal/config/redis.go` - Redis 连接配置\n- **搜索配置**:`internal/config/meilisearch.go` - Meilisearch 搜索配置\n\n### 中间件系统\n- **CORS中间件**:`internal/middleware/cors.go` - 跨域资源共享处理\n- **日志中间件**:`internal/middleware/logger.go` - 请求日志记录\n- **请求ID中间件**:`internal/middleware/request_id.go` - 请求追踪标识\n\n### 日志系统\n- **日志配置**:`pkg/logger/logger.go` - 基于 Zap 的日志系统,支持开发和生产环境不同配置\n\n### 数据模型\n- **品牌模型**:`internal/model/brand.go` - 品牌数据结构定义\n- **型号模型**:`internal/model/model.go` - 型号数据结构定义\n\n### 响应处理\n- **统一响应格式**:`internal/response/response.go` - API 响应的标准格式化\n\n## 4. 核心特性\n\n### 功能架构图\n\n```mermaid\nstateDiagram-v2\n [*] --\u003e 接收请求\n 接收请求 --\u003e 验证参数\n 验证参数 --\u003e 处理业务逻辑\n 处理业务逻辑 --\u003e 查询数据库\n 查询数据库 --\u003e 返回结果\n 处理业务逻辑 --\u003e 使用缓存\n 使用缓存 --\u003e 返回结果\n 处理业务逻辑 --\u003e 执行搜索\n 执行搜索 --\u003e 返回结果\n \n 返回结果 --\u003e 记录日志\n 记录日志 --\u003e [*]\n \n 验证参数 --\u003e 错误处理\n 错误处理 --\u003e 返回错误\n 返回错误 --\u003e 记录日志\n```\n\n### 实现亮点\n\n#### 多数据源架构\n- **混合存储策略**:结合传统关系型数据库和现代搜索引擎\n- **缓存优化**:使用 Redis 缓存热点数据,提升响应速度\n- **异步处理**:支持设备信息上报的异步处理机制\n\n#### 高可用设计\n- **优雅关闭**:支持服务器优雅启动和关闭\n- **信号处理**:正确处理系统信号,确保资源正确释放\n- **连接池管理**:数据库和缓存连接的生命周期管理\n\n#### 开发体验优化\n- **环境隔离**:开发和生产环境的配置分离\n- **统一日志**:结构化的日志输出,便于问题排查\n- **错误处理**:完善的错误处理和返回机制\n\n### 性能考虑\n\n```mermaid\ngraph LR\n A[请求到达] --\u003e B[中间件处理]\n B --\u003e C[缓存检查]\n C --\u003e D{缓存命中?}\n D --\u003e|是| E[直接返回缓存]\n D --\u003e|否| F[查询数据库]\n F --\u003e G[更新缓存]\n G --\u003e H[返回响应]\n E --\u003e H\n H --\u003e I[记录日志]\n```\n\n## 5. 项目特色\n\n### 架构优势\n- **模块化设计**:清晰的职责分离,便于团队协作和维护\n- **可扩展性**:支持添加新的数据源和业务功能\n- **可观测性**:完整的日志记录和监控支持\n- **安全性**:中间件层提供统一的安全控制\n\n### 技术选型\n- **Go 1.24**:现代化的编程语言,提供优秀的性能和并发能力\n- **Gin 框架**:高性能 HTTP Web 框架,适合构建 RESTful API\n- **MySQL**:成熟的关系型数据库,适合结构化数据存储\n- **Meilisearch**:快速的全文搜索引擎,提供优秀的搜索体验\n- **Redis**:高性能的内存数据结构存储,用于缓存和会话管理\n\n### 开发最佳实践\n- **依赖注入**:通过构造函数注入依赖,提升代码可测试性\n- **错误处理**:统一的错误处理机制,避免错误传播\n- **配置管理**:环境变量驱动的配置管理,支持容器化部署\n- **日志标准化**:结构化日志输出,便于日志分析和监控\n\n## 6. 总结与建议\n\n### 项目特点总结\nLuxsin 应用 API 项目展现了现代 Go 语言 Web 服务的最佳实践,采用了清晰的分层架构、依赖注入和中间件模式。项目在性能、可维护性和可扩展性方面都表现出色,为音频设备数据管理提供了可靠的基础设施。\n\n### 架构优势\n- **清晰的层次结构**:从路由到数据访问的完整分层设计\n- **现代化技术栈**:选择合适的技术解决特定问题\n- **完善的基础设施**:包含缓存、搜索、日志等完整的系统组件\n- **良好的工程实践**:遵循 Go 语言的最佳实践和编码规范\n\n### 改进建议\n1. **增加单元测试**:为关键业务逻辑添加更多的单元测试覆盖\n2. **API 文档**:集成 Swagger 或类似工具生成 API 文档\n3. **监控指标**:添加 Prometheus 指标收集,完善运维监控\n4. **健康检查**:扩展健康检查端点,提供更详细的系统状态信息\n5. **配置热更新**:支持部分配置的动态更新,减少重启需求\n\n### 下一步发展方向\n- **微服务拆分**:根据业务领域进一步拆分服务\n- **消息队列**:引入消息队列处理异步任务\n- **容器化部署**:完善 Docker 和 Kubernetes 部署配置\n- **性能优化**:持续优化查询性能和缓存策略\n\n---\n\n**Sources:**\n- [main.go](cmd/server/main.go)\n- [go.mod](go.mod)\n- [config.go](internal/config/config.go)\n- [router.go](internal/router/router.go)\n- [brand.go](internal/handler/brand.go)\n- [brand_repository.go](internal/repository/brand.go)\n- [brand_model.go](internal/model/brand.go)\n- [meilisearch.go](internal/search/meilisearch.go)\n- [redis.go](internal/cache/redis.go)\n- [cors.go](internal/middleware/cors.go)\n- [logger.go](pkg/logger/logger.go)\n- [database_config.go](internal/config/database.go)\n- [model.sql](sql/model.sql)\n\n\u003c/blog\u003e","gmt_create":"2026-05-27T15:40:06.9577463+08:00","gmt_modified":"2026-05-27T15:40:06.9577463+08:00","id":"5a60eb9b-0b2d-4d2f-99c6-dd1e4cd34dd3","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc"},"wiki_readme":{"content":"No readme file","gmt_create":"2026-05-27T15:38:58.8115445+08:00","gmt_modified":"2026-05-27T15:38:58.8115445+08:00","id":"cf8cd5f4-1600-41c4-9afb-1db3a1464c48","repo_id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc"},"wiki_repo":{"id":"a6c92e0a-0eb8-4b81-8beb-0169c32d76bc","name":"app-api","progress_status":"completed","wiki_present_status":"COMPLETED","optimized_catalog":"\".\\n├── _tmp_desc\\\\\\n├── cmd\\\\server\\\\\\n│ └── main.go\\n├── internal\\\\\\n│ ├── cache\\\\\\n│ │ └── redis.go\\n│ ├── config\\\\\\n│ │ ├── config.go\\n│ │ ├── database.go\\n│ │ ├── meilisearch.go\\n│ │ └── redis.go\\n│ ├── database\\\\\\n│ │ └── mysql.go\\n│ ├── handler\\\\\\n│ │ ├── brand.go\\n│ │ ├── device.go\\n│ │ ├── health.go\\n│ │ ├── model.go\\n│ │ └── model_list.go\\n│ ├── middleware\\\\\\n│ │ ├── cors.go\\n│ │ ├── logger.go\\n│ │ └── request_id.go\\n│ ├── model\\\\\\n│ │ ├── brand.go\\n│ │ └── model.go\\n│ ├── repository\\\\\\n│ │ ├── brand.go\\n│ │ └── model.go\\n│ ├── response\\\\\\n│ │ └── response.go\\n│ ├── router\\\\\\n│ │ └── router.go\\n│ └── search\\\\\\n│ └── meilisearch.go\\n├── pkg\\\\\\n│ ├── encode\\\\\\n│ │ └── base64.go\\n│ └── logger\\\\\\n│ └── logger.go\\n├── sql\\\\\\n│ └── model.sql\\n├── .gitignore\\n├── Makefile\\n├── README.md\\n├── go.mod\\n└── go.sum\\n\"","current_document_structure":"WikiEncrypted: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","catalogue_think_content":"WikiEncrypted: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","recovery_checkpoint":"wiki_generation_completed","last_commit_id":"fe7b61d8d1f3a4d9d1ed750a4cd1f015e613dbba","last_commit_update":"2026-05-27T16:00:38.1409279+08:00","gmt_create":"2026-05-27T15:32:45.7755498+08:00","gmt_modified":"2026-05-27T16:00:38.1409279+08:00","extend_info":"{\"language\":\"zh\",\"active\":true,\"branch\":\"main\",\"shareStatus\":\"\",\"server_error_code\":\"\",\"cosy_version\":\"1.2.3\"}"}} \ No newline at end of file diff --git a/cmd/server/main.go b/cmd/server/main.go index 82ea2b3..423ce28 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -2,6 +2,7 @@ package main import ( "context" + "database/sql" "errors" "net/http" "os" @@ -10,12 +11,14 @@ import ( "time" "github.com/gin-gonic/gin" + "github.com/luxsin/app-api/internal/cache" "github.com/luxsin/app-api/internal/config" "github.com/luxsin/app-api/internal/database" - "github.com/luxsin/app-api/internal/cache" + "github.com/luxsin/app-api/internal/repository" "github.com/luxsin/app-api/internal/router" "github.com/luxsin/app-api/internal/search" "github.com/luxsin/app-api/pkg/logger" + "github.com/redis/go-redis/v9" "go.uber.org/zap" ) @@ -53,15 +56,18 @@ func main() { zap.String("index", cfg.Meilisearch.Index), ) - redisClient := cache.NewClient(cfg.Redis) - defer redisClient.Close() + rdb := cache.NewClient(cfg.Redis) + defer rdb.Close() log.Info("redis connected", zap.String("host", cfg.Redis.Host), zap.Int("port", cfg.Redis.Port), zap.Int("db", cfg.Redis.Database), ) - engine := router.New(log, db, searchClient, redisClient) + // 启动时预热缓存 + warmUpCache(log, db, rdb) + + engine := router.New(log, db, searchClient, rdb) srv := &http.Server{ Addr: cfg.Addr(), @@ -93,3 +99,32 @@ func main() { log.Info("server stopped") } + +// warmUpCache 启动时从数据库加载数据到 Redis +func warmUpCache(log *zap.Logger, db *sql.DB, rdb *redis.Client) { + ctx := context.Background() + + brandCache := cache.NewBrandCache(rdb) + modelCache := cache.NewModelCache(rdb) + + brandRepo := repository.NewBrandRepository(db, brandCache) + modelRepo := repository.NewModelRepository(db, modelCache) + + // 预热 brand:all + if brands, err := brandRepo.ListFromDB(ctx, ""); err != nil { + log.Warn("brand warm-up failed", zap.Error(err)) + } else if err := brandCache.SetAll(ctx, brands); err != nil { + log.Warn("brand cache set failed", zap.Error(err)) + } else { + log.Info("brand cache warmed up", zap.Int("count", len(brands))) + } + + // 预热 model:all + if allModels, err := modelRepo.ListAllFromDB(ctx); err != nil { + log.Warn("model warm-up failed", zap.Error(err)) + } else if err := modelCache.SetAll(ctx, allModels); err != nil { + log.Warn("model cache set failed", zap.Error(err)) + } else { + log.Info("model cache warmed up", zap.Int("count", len(allModels))) + } +} diff --git a/internal/cache/brand_cache.go b/internal/cache/brand_cache.go new file mode 100644 index 0000000..9168cdd --- /dev/null +++ b/internal/cache/brand_cache.go @@ -0,0 +1,44 @@ +package cache + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "github.com/luxsin/app-api/internal/model" + "github.com/redis/go-redis/v9" +) + +const ( + brandAllKey = "brand:all" + brandTTL = 30 * time.Minute +) + +type BrandCache struct { + rdb *redis.Client +} + +func NewBrandCache(rdb *redis.Client) *BrandCache { + return &BrandCache{rdb: rdb} +} + +func (c *BrandCache) GetAll(ctx context.Context) ([]model.Brand, error) { + data, err := c.rdb.Get(ctx, brandAllKey).Bytes() + if err != nil { + return nil, err + } + var list []model.Brand + if err := json.Unmarshal(data, &list); err != nil { + return nil, fmt.Errorf("unmarshal brands: %w", err) + } + return list, nil +} + +func (c *BrandCache) SetAll(ctx context.Context, list []model.Brand) error { + data, err := json.Marshal(list) + if err != nil { + return fmt.Errorf("marshal brands: %w", err) + } + return c.rdb.Set(ctx, brandAllKey, data, brandTTL).Err() +} diff --git a/internal/cache/model_cache.go b/internal/cache/model_cache.go new file mode 100644 index 0000000..a4dcdfe --- /dev/null +++ b/internal/cache/model_cache.go @@ -0,0 +1,69 @@ +package cache + +import ( + "context" + "encoding/json" + "fmt" + "time" + + "github.com/luxsin/app-api/internal/model" + "github.com/redis/go-redis/v9" +) + +const ( + modelBrandPrefix = "model:brand:" + modelAllKey = "model:all" + modelTTL = 30 * time.Minute +) + +type ModelCache struct { + rdb *redis.Client +} + +func NewModelCache(rdb *redis.Client) *ModelCache { + return &ModelCache{rdb: rdb} +} + +func modelBrandKey(brandName string) string { + return modelBrandPrefix + brandName +} + +func (c *ModelCache) GetByBrand(ctx context.Context, brandName string) ([]model.Model, error) { + data, err := c.rdb.Get(ctx, modelBrandKey(brandName)).Bytes() + if err != nil { + return nil, err + } + var list []model.Model + if err := json.Unmarshal(data, &list); err != nil { + return nil, fmt.Errorf("unmarshal models: %w", err) + } + return list, nil +} + +func (c *ModelCache) SetByBrand(ctx context.Context, brandName string, list []model.Model) error { + data, err := json.Marshal(list) + if err != nil { + return fmt.Errorf("marshal models: %w", err) + } + return c.rdb.Set(ctx, modelBrandKey(brandName), data, modelTTL).Err() +} + +func (c *ModelCache) GetAll(ctx context.Context) ([]model.Model, error) { + data, err := c.rdb.Get(ctx, modelAllKey).Bytes() + if err != nil { + return nil, err + } + var list []model.Model + if err := json.Unmarshal(data, &list); err != nil { + return nil, fmt.Errorf("unmarshal models: %w", err) + } + return list, nil +} + +func (c *ModelCache) SetAll(ctx context.Context, list []model.Model) error { + data, err := json.Marshal(list) + if err != nil { + return fmt.Errorf("marshal models: %w", err) + } + return c.rdb.Set(ctx, modelAllKey, data, modelTTL).Err() +} diff --git a/internal/config/redis.go b/internal/config/redis.go index dc2ae83..1a04799 100644 --- a/internal/config/redis.go +++ b/internal/config/redis.go @@ -28,9 +28,9 @@ func loadRedis(env string) RedisConfig { } default: return RedisConfig{ - Host: "ec2-3-69-138-29.eu-central-1.compute.amazonaws.com", - Port: 16279, - Password: getEnv("REDIS_PASSWORD", "eafon123!"), + Host: "localhost", + Port: 6379, + Password: "", Database: 1, } } diff --git a/internal/handler/brand.go b/internal/handler/brand.go index db25151..52b8714 100644 --- a/internal/handler/brand.go +++ b/internal/handler/brand.go @@ -1,7 +1,6 @@ package handler import ( - "database/sql" "net/http" "github.com/gin-gonic/gin" @@ -16,9 +15,9 @@ type BrandHandler struct { log *zap.Logger } -func NewBrandHandler(db *sql.DB, log *zap.Logger) *BrandHandler { +func NewBrandHandler(repo *repository.BrandRepository, log *zap.Logger) *BrandHandler { return &BrandHandler{ - repo: repository.NewBrandRepository(db), + repo: repo, log: log, } } diff --git a/internal/handler/model.go b/internal/handler/model.go index 2a37508..5d5a9fd 100644 --- a/internal/handler/model.go +++ b/internal/handler/model.go @@ -1,7 +1,6 @@ package handler import ( - "database/sql" "net/http" "github.com/gin-gonic/gin" @@ -16,9 +15,9 @@ type ModelHandler struct { log *zap.Logger } -func NewModelHandler(db *sql.DB, log *zap.Logger) *ModelHandler { +func NewModelHandler(repo *repository.ModelRepository, log *zap.Logger) *ModelHandler { return &ModelHandler{ - repo: repository.NewModelRepository(db), + repo: repo, log: log, } } diff --git a/internal/repository/brand.go b/internal/repository/brand.go index 219ba4c..76aa89e 100644 --- a/internal/repository/brand.go +++ b/internal/repository/brand.go @@ -6,18 +6,53 @@ import ( "fmt" "strings" + "github.com/luxsin/app-api/internal/cache" "github.com/luxsin/app-api/internal/model" + "github.com/redis/go-redis/v9" ) type BrandRepository struct { - db *sql.DB + db *sql.DB + cache *cache.BrandCache } -func NewBrandRepository(db *sql.DB) *BrandRepository { - return &BrandRepository{db: db} +func NewBrandRepository(db *sql.DB, brandCache *cache.BrandCache) *BrandRepository { + return &BrandRepository{db: db, cache: brandCache} } func (r *BrandRepository) List(ctx context.Context, brandName string) ([]model.Brand, error) { + // 尝试从 Redis 获取全量品牌 + list, err := r.cache.GetAll(ctx) + if err == nil { + // 缓存命中,在应用层过滤 + if brandName = strings.TrimSpace(brandName); brandName != "" { + list = filterBrands(list, brandName) + } + return list, nil + } + + // 缓存未命中或 Redis 异常,降级到数据库 + if err != redis.Nil { + fmt.Printf("brand cache read failed, fallback to db: %v\n", err) + } + + list, err = r.ListFromDB(ctx, brandName) + if err != nil { + return nil, err + } + + // 回写缓存(全量数据) + if allBrands, dbErr := r.ListFromDB(ctx, ""); dbErr == nil { + if cacheErr := r.cache.SetAll(ctx, allBrands); cacheErr != nil { + fmt.Printf("brand cache write failed: %v\n", cacheErr) + } + } + + return list, nil +} + +// ListFromDB 直接从数据库查询(用于预热和缓存回写) +func (r *BrandRepository) ListFromDB(ctx context.Context, brandName string) ([]model.Brand, error) { query := "SELECT id, name FROM brand" args := []any{} @@ -48,3 +83,17 @@ func (r *BrandRepository) List(ctx context.Context, brandName string) ([]model.B return list, nil } + +// filterBrands 在应用层做模糊过滤 +func filterBrands(list []model.Brand, brandName string) []model.Brand { + filtered := make([]model.Brand, 0) + for _, b := range list { + if strings.Contains( + strings.ToLower(b.Name), + strings.ToLower(brandName), + ) { + filtered = append(filtered, b) + } + } + return filtered +} diff --git a/internal/repository/model.go b/internal/repository/model.go index 92ced9d..285b8db 100644 --- a/internal/repository/model.go +++ b/internal/repository/model.go @@ -6,45 +6,132 @@ import ( "fmt" "strings" + "github.com/luxsin/app-api/internal/cache" "github.com/luxsin/app-api/internal/model" + "github.com/redis/go-redis/v9" ) type ModelRepository struct { - db *sql.DB + db *sql.DB + cache *cache.ModelCache } -func NewModelRepository(db *sql.DB) *ModelRepository { - return &ModelRepository{db: db} +func NewModelRepository(db *sql.DB, modelCache *cache.ModelCache) *ModelRepository { + return &ModelRepository{db: db, cache: modelCache} } func (r *ModelRepository) List(ctx context.Context, brandName, modelName string) ([]model.Model, error) { brandName = strings.TrimSpace(brandName) modelName = strings.TrimSpace(modelName) - const baseQuery = `SELECT id, brand_name, name, form, rig, source, eq_key, create_at FROM model` - - var ( - query string - args []any - ) - switch { case brandName != "": - query = baseQuery + " WHERE brand_name = ? ORDER BY name ASC" - args = []any{brandName} + // 按品牌精确查询:优先从缓存获取 + list, err := r.cache.GetByBrand(ctx, brandName) + if err == nil { + return list, nil + } + // 缓存未命中或 Redis 异常,降级到数据库 + if err != redis.Nil { + fmt.Printf("model cache read failed, fallback to db: %v\n", err) + } + + list, err = r.ListByBrandFromDB(ctx, brandName) + if err != nil { + return nil, err + } + + // 回写缓存 + if cacheErr := r.cache.SetByBrand(ctx, brandName, list); cacheErr != nil { + fmt.Printf("model cache write failed: %v\n", cacheErr) + } + + return list, nil + case modelName != "": - query = baseQuery + " WHERE name LIKE ? ORDER BY name ASC" - args = []any{"%" + modelName + "%"} + // 按型号模糊查询:从全量缓存中过滤 + list, err := r.cache.GetAll(ctx) + if err == nil { + return filterModels(list, modelName), nil + } + // 缓存未命中或 Redis 异常,降级到数据库 + if err != redis.Nil { + fmt.Printf("model cache read failed, fallback to db: %v\n", err) + } + + list, err = r.ListByModelFromDB(ctx, modelName) + if err != nil { + return nil, err + } + + // 回写全量缓存 + if allModels, dbErr := r.ListAllFromDB(ctx); dbErr == nil { + if cacheErr := r.cache.SetAll(ctx, allModels); cacheErr != nil { + fmt.Printf("model cache write failed: %v\n", cacheErr) + } + } + + return list, nil + default: return []model.Model{}, nil } +} - rows, err := r.db.QueryContext(ctx, query, args...) +// ListAllFromDB 从数据库查询全部型号(用于预热和缓存回写) +func (r *ModelRepository) ListAllFromDB(ctx context.Context) ([]model.Model, error) { + const query = `SELECT id, brand_name, name, form, rig, source, eq_key, create_at FROM model ORDER BY name ASC` + + rows, err := r.db.QueryContext(ctx, query) if err != nil { return nil, fmt.Errorf("query model: %w", err) } defer rows.Close() + return scanModels(rows) +} + +// ListByBrandFromDB 从数据库按品牌查询型号 +func (r *ModelRepository) ListByBrandFromDB(ctx context.Context, brandName string) ([]model.Model, error) { + const query = `SELECT id, brand_name, name, form, rig, source, eq_key, create_at FROM model WHERE brand_name = ? ORDER BY name ASC` + + rows, err := r.db.QueryContext(ctx, query, brandName) + if err != nil { + return nil, fmt.Errorf("query model: %w", err) + } + defer rows.Close() + + return scanModels(rows) +} + +// ListByModelFromDB 从数据库按型号名称模糊查询 +func (r *ModelRepository) ListByModelFromDB(ctx context.Context, modelName string) ([]model.Model, error) { + const query = `SELECT id, brand_name, name, form, rig, source, eq_key, create_at FROM model WHERE name LIKE ? ORDER BY name ASC` + + rows, err := r.db.QueryContext(ctx, query, "%"+modelName+"%") + if err != nil { + return nil, fmt.Errorf("query model: %w", err) + } + defer rows.Close() + + return scanModels(rows) +} + +// filterModels 在应用层做模糊过滤 +func filterModels(list []model.Model, modelName string) []model.Model { + filtered := make([]model.Model, 0) + for _, m := range list { + if strings.Contains( + strings.ToLower(m.Name), + strings.ToLower(modelName), + ) { + filtered = append(filtered, m) + } + } + return filtered +} + +func scanModels(rows *sql.Rows) ([]model.Model, error) { list := make([]model.Model, 0) for rows.Next() { m, err := scanModel(rows) @@ -56,7 +143,6 @@ func (r *ModelRepository) List(ctx context.Context, brandName, modelName string) if err := rows.Err(); err != nil { return nil, fmt.Errorf("iterate model: %w", err) } - return list, nil } diff --git a/internal/router/router.go b/internal/router/router.go index 8aea660..1faa6cc 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -4,25 +4,36 @@ import ( "database/sql" "github.com/gin-gonic/gin" + "github.com/luxsin/app-api/internal/cache" "github.com/luxsin/app-api/internal/handler" "github.com/luxsin/app-api/internal/middleware" + "github.com/luxsin/app-api/internal/repository" "github.com/luxsin/app-api/internal/search" "github.com/redis/go-redis/v9" "go.uber.org/zap" ) -func New(log *zap.Logger, db *sql.DB, searchClient *search.Client, redis *redis.Client) *gin.Engine { +func New(log *zap.Logger, db *sql.DB, searchClient *search.Client, rdb *redis.Client) *gin.Engine { r := gin.New() r.Use(gin.Recovery()) r.Use(middleware.RequestID()) r.Use(middleware.Logger(log)) r.Use(middleware.CORS()) + // Cache + brandCache := cache.NewBrandCache(rdb) + modelCache := cache.NewModelCache(rdb) + + // Repository + brandRepo := repository.NewBrandRepository(db, brandCache) + modelRepo := repository.NewModelRepository(db, modelCache) + + // Handler health := handler.NewHealthHandler() - brand := handler.NewBrandHandler(db, log) - model := handler.NewModelHandler(db, log) + brand := handler.NewBrandHandler(brandRepo, log) + model := handler.NewModelHandler(modelRepo, log) modelList := handler.NewModelListHandler(searchClient, log) - device := handler.NewDeviceHandler(redis, log) + device := handler.NewDeviceHandler(rdb, log) v1 := r.Group("/api/v1") {