Help us shape the future of Go! Take the 2026 Go Developer Survey

storage

package
v1.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 13, 2026 License: AGPL-3.0 Imports: 11 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Close

func Close(db *gorm.DB) error

Close 关闭数据库并清理 sqlite-vec 全局状态。

func Open

func Open(dsn string, log *slog.Logger) (*gorm.DB, error)

Open 打开 SQLite 数据库,启用 WAL 模式和 sqlite-vec 向量扩展。

dsn: SQLite 文件路径,如 "/data/my-novel/novel.db"

func PatchAndSave

func PatchAndSave[T any](db *gorm.DB, id int64, novelID int64, input any, entity *T) error

PatchAndSave 读取实体、用 input 的非零值字段覆盖、然后 Save。 Save 会触发 GORM 回调(包括 operation_log),解决了 Model().Where().Updates() 不走回调的问题。

原理:input 的 omitempty tag 使 json.Marshal 只序列化非零字段, json.Unmarshal 到 entity 时只覆盖这些字段,未出现的保持 DB 原值——恰好等价于 PATCH 语义。

【重要约束】input 结构体的所有字段必须带 json:"...,omitempty" tag。 否则 json.Marshal 会序列化零值,json.Unmarshal 会覆盖 entity 的原有字段为零值。 CI 脚本 scripts/check_omitempty 会自动检查所有 Update*Input 结构体。 不走 PatchAndSave 的 Input 可加 //nolint:omitempty 注释跳过检查。

【已知限制】omitempty 导致无法将 string 清空为 ""、将 int 清零。 需要清空字段时用指针类型(如 *string、*int64)或显式 ClearXxx bool flag。

用法:

var ch character.Character
if err := PatchAndSave(db, charID, novelID, &input, &ch); err != nil { ... }

func PatchAndSaveNoNovel

func PatchAndSaveNoNovel[T any](db *gorm.DB, id int64, input any, entity *T) error

PatchAndSaveNoNovel 与 PatchAndSave 相同,但不要求 novel_id 条件。 用于没有 novel_id 字段的实体(如全局偏好)。

func Ping

func Ping(ctx context.Context, db *gorm.DB) error

Ping 检查数据库连接是否正常。

func RegisterOplogHooks

func RegisterOplogHooks(db *gorm.DB) error

RegisterOplogHooks 在 *gorm.DB 上注册写操作拦截回调。 调用时机:storage.Open 初始化 DB 后调用一次。

【注意事项】

  1. 批量更新(db.Where(...).Updates(...))不走单行 callback(无 Schema),不会自动记录。 需要此类操作的 store 方法必须显式调用独立的日志方法。
  2. 裸 SQL(db.Exec(...))不走 callback,同样不会自动记录。 当前所有 store 均无上述两种情况;保留此注释防止后续踩坑。

func RollbackInTx

func RollbackInTx(ctx context.Context, tx *gorm.DB, sessionID string, targetTurn, lastTurn int) error

RollbackInTx 是 RollbackTo 的事务内版本,供外部已有事务时复用(如 rollback.RollbackBeforeTurn)。 回退 [targetTurn, lastTurn] 区间内领域实体、messages、operation_log 自身的记录。

func RollbackTo

func RollbackTo(ctx context.Context, db *gorm.DB, sessionID string, targetTurn, lastTurn int) error

RollbackTo 回退到 targetTurn 开始之前的状态。 即撤销 [ targetTurn, last_turn_id ] 区间内所有 turn 的变更。 传入 15:撤销 turn 15 及之后(≥15)所有变更,回到 turn 15 还没发出时的状态。

回退内容:

  • 领域实体:按 operation_log 逐条逆向执行(create→delete, update→恢复旧值, delete→insert)
  • 消息:直接 DELETE WHERE turn_id >= targetTurn
  • 回退自身的日志记录一并清理

整个操作在单个事务内完成。调用方若已持有事务,应使用 RollbackInTx 传入 tx。

func WithEntityTurn added in v1.6.1

func WithEntityTurn(ctx context.Context, db *gorm.DB, table string, id int64) (context.Context, error)

WithEntityTurn 让附属记录的手动变更继承所属实体的轮次;显式 AI 轮次优先。 调用方必须传入当前事务,避免在单连接数据库中另取连接。

func WithTurn

func WithTurn(ctx context.Context, sessionID string, turnID int) context.Context

WithTurn 将 turn 信息注入 context。agent loop 在 turn 开始时调用。

Types

type OperationLogRecord

type OperationLogRecord struct {
	ID        int64     `gorm:"column:id;primaryKey;autoIncrement"`
	SessionID string    `gorm:"column:session_id;not null;index:idx_oplog_rollback,priority:1"`
	TurnID    int       `gorm:"column:turn_id;not null;index:idx_oplog_rollback,priority:2"`
	Operation string    `gorm:"column:operation;not null"`                                    // "create" | "update" | "delete"
	Table     string    `gorm:"column:table_name;not null;index:idx_oplog_entity,priority:1"` // 目标表名,如 "characters"
	EntityID  string    `gorm:"column:entity_id;not null;index:idx_oplog_entity,priority:2"`  // JSON 化的主键条件,如 {"id":5} 或 {"novel_id":1,"scope":"next"}
	OldValues string    `gorm:"column:old_values"`                                            // JSON,create 时为 ""
	NewValues string    `gorm:"column:new_values"`                                            // JSON,delete 时为 ""
	CreatedAt time.Time `gorm:"column:created_at;autoCreateTime"`
}

OperationLogRecord 操作日志表的一行。 不通过 GORM AutoMigrate 创建,由 migrate.go 统一管理建表。

func (OperationLogRecord) TableName

func (OperationLogRecord) TableName() string

type PageParams

type PageParams struct {
	Page int `json:"page"`
	Size int `json:"size"`
}

PageParams 是所有 List 方法的分页参数,零值即可直接使用(Page=0 时 normalize 为 1)。

func (*PageParams) Normalize

func (p *PageParams) Normalize() *PageParams

Normalize 归一化分页参数,语义对齐 GORM:

  • Size >= 0 原样透传(0=Limit(0) 返回 0 条,快速失败;>0 正常分页)
  • Size < 0 归一化为 -1(GORM Limit(-1) 取消限制,表示全量),并强制 Page=1 保证 offset=0

func (PageParams) Offset

func (p PageParams) Offset() int

Offset 计算分页偏移量。Size=-1(全量)时 Page 已被 Normalize 强制为 1,返回 0。

type PageResult

type PageResult[T any] struct {
	Items      []T   `json:"items"`
	Total      int64 `json:"total"`
	Page       int   `json:"page"`
	Size       int   `json:"size"`
	TotalPages int   `json:"total_pages"`
}

PageResult 泛型分页响应,匹配 Python PageResponse 的语义。

func NewPageResult

func NewPageResult[T any](items []T, total int64, page, size int) *PageResult[T]

NewPageResult 根据 total/size 自动计算 TotalPages,并保证 Items 非 nil。

type TurnInfo

type TurnInfo struct {
	SessionID string
	TurnID    int
}

TurnInfo 记录当前 turn 的 session 和 turn 编号,用于 operation_log 定位回退范围。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL