feat(lyra): 为分类查询添加缓存并同步项目约定
- 实现分类列表缓存查询与故障降级 - 统一缓存键命名 - 同步日志、响应、依赖组装及测试约定 - 移除 postgres 占位目录
This commit is contained in:
@@ -2,5 +2,6 @@
|
||||
|
||||
- 负责参数绑定、API DTO 与内部模型的相互转换、响应输出。
|
||||
- 引用 internal/api 的 DTO,使用 Gin 完成绑定与响应。
|
||||
- 响应统一使用 `httpx.OK` / `httpx.Fail`,遵循 `internal/httpx/AGENTS.md` 的状态码、业务码与错误隔离规则。
|
||||
- 调用 service 时传递标准 context.Context,不下传 gin.Context。
|
||||
- 可按明确业务分包(如 `polaris/`);服务级简单接口保留在根包平铺,不预建空业务包。
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
# httpx 包
|
||||
|
||||
- 提供公共 HTTP 响应辅助。
|
||||
- 出现实际复用需求时再补充具体辅助函数,不预先设计无使用场景的抽象。
|
||||
- 提供公共 HTTP 响应辅助,使用现有 `OK` / `Fail` 输出统一响应。
|
||||
- `Fail` 区分 HTTP 状态码与业务错误码;公开 message 为空时回退到业务码的默认文案。
|
||||
- 不向公开响应写入内部错误或调试信息,避免 debug 泄漏。
|
||||
- 响应结构来自 internal/api,不在这里重复定义 DTO。
|
||||
|
||||
@@ -2,5 +2,9 @@
|
||||
|
||||
- 承载数据结构定义与数据访问:使用 Squirrel/SQL 构造查询、sqlx 执行与映射结果,驱动为 pgx stdlib。
|
||||
- SQL 与查询逻辑显式写在这一层,不使用 ORM 自动持久化。
|
||||
- 支持在 service 传入的同一事务内执行操作。
|
||||
- 事务边界由 service 决定;需要同一事务时由 service 传入事务执行。这是设计目标,当前没有现成的事务接口封装。
|
||||
- 查询缓存采用 cache-aside:先读缓存,命中直接返回(空列表也可命中);未命中或缓存读取失败时回源数据库。
|
||||
- 仅数据库查询成功后回填缓存;数据库失败不回填。缓存读写故障只记录日志并降级,不改变数据库查询结果。
|
||||
- 缓存键使用 `internal/pkg/constant` 中的常量,遵循根 `AGENTS.md` 的 Redis 缓存键约定。
|
||||
- 未来写路径应在事务提交后使相关缓存失效;当前 `cache` 包没有删除接口,失效策略属于后续设计约束。
|
||||
- 使用标准 context.Context,不依赖 Gin,也不依赖 internal/api 的 DTO。
|
||||
|
||||
@@ -3,7 +3,11 @@ package model
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"lyra/internal/cache"
|
||||
"lyra/internal/pkg/constant"
|
||||
"lyra/internal/util"
|
||||
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
"github.com/jmoiron/sqlx"
|
||||
@@ -27,7 +31,17 @@ type defaultCategoryModel struct {
|
||||
func NewCategoryModel(db *sqlx.DB, cache *cache.Cache) CategoryModel {
|
||||
return &defaultCategoryModel{db: db, cache: cache}
|
||||
}
|
||||
|
||||
const categoryListCacheTTL = 5 * time.Minute
|
||||
|
||||
func (m *defaultCategoryModel) FindAll(ctx context.Context) ([]*Category, error) {
|
||||
cached, hit, err := m.cache.Get[[]*Category](ctx, constant.CategoryListCacheKey)
|
||||
if err != nil {
|
||||
util.Logger(ctx).Error("读取分类列表缓存失败", "key", constant.CategoryListCacheKey, "error", err)
|
||||
}
|
||||
if hit {
|
||||
return cached, nil
|
||||
}
|
||||
|
||||
query, args, err := sq.
|
||||
Select("id", "slug", "title", "sort").
|
||||
@@ -45,5 +59,12 @@ func (m *defaultCategoryModel) FindAll(ctx context.Context) ([]*Category, error)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("数据库查询出错: %w", err)
|
||||
}
|
||||
return rows, err
|
||||
if err := m.cache.Set(ctx, cache.Entry[[]*Category]{
|
||||
Key: constant.CategoryListCacheKey,
|
||||
Value: rows,
|
||||
TTL: categoryListCacheTTL,
|
||||
}); err != nil {
|
||||
util.Logger(ctx).Error("写入分类列表缓存失败", "key", constant.CategoryListCacheKey, "error", err)
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
package constant
|
||||
|
||||
const CategoryListCacheKey = "lyra:cache:category:list:v1"
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
# postgres 包
|
||||
|
||||
- 负责建立与管理 PostgreSQL 连接,使用 pgx stdlib 驱动。
|
||||
- 连接参数来自 internal/config,连接供 model 层通过 sqlx 执行查询使用。
|
||||
@@ -1,6 +1,7 @@
|
||||
# service 包
|
||||
|
||||
- 承载业务规则与事务协调,通过 model 完成数据访问。
|
||||
- 需要原子性时,在同一事务内协调多次写入,不把各自独立提交的多次写入当成原子业务。
|
||||
- 当前目录为占位,尚无 Go 实现;新增代码前先确认职责边界。
|
||||
- 预期职责:业务规则与事务协调,通过 model 完成数据访问。
|
||||
- 需要原子性时,在同一事务内协调多次写入;当前没有现成事务接口,需在实现时设计,不把各自独立提交的多次写入当成原子业务。
|
||||
- 使用标准 context.Context,不依赖 Gin,也不依赖 internal/api 的 DTO。
|
||||
- 可按明确业务分包(如 `polaris/`);服务级简单接口保留在根包平铺,不预建空业务包。
|
||||
|
||||
Reference in New Issue
Block a user