refactor: standardize Hook signatures to use *Document

This commit is contained in:
AI Engineer 2026-05-16 01:07:42 +08:00
parent cfabbab466
commit 158907f36a
4 changed files with 50 additions and 35 deletions

View File

@ -1,5 +1,10 @@
# CHANGELOG # CHANGELOG
## v1.0.9 (2026-05-16)
- **Hook 系统重构**:
- **参数标准化**: `Hooks` 结构体中的所有回调函数(`OnUpdatedDoc`, `OnRemovedDoc`, `OnUpdatedToC`, `OnRemovedToC`)现在统一接收 `*Document` 对象作为参数,极大地方便了外部处理逻辑直接获取文档元数据与路径。
- **触发逻辑对齐**: 确保在文档删除、重命名及 ToC 更新时,钩子都能正确获取到相关的文档上下文。
## [1.0.0] - 2026-05-15 ## [1.0.0] - 2026-05-15
### Added ### Added
- 从 `knowbase` 项目剥离生成的独立 `docDB` 项目。 - 从 `knowbase` 项目剥离生成的独立 `docDB` 项目。

View File

@ -1,4 +1,4 @@
# @go/docDB # @go/docDB v1.0.9
`docDB` 是一个独立的高级文档存储引擎,基于 `@go/tableDB` 构建,提供全自动版本管理、历史存证、流式大文件处理及精细化生命周期钩子。 `docDB` 是一个独立的高级文档存储引擎,基于 `@go/tableDB` 构建,提供全自动版本管理、历史存证、流式大文件处理及精细化生命周期钩子。
@ -38,27 +38,27 @@ db := engine.Auth("user_123")
- **`SetDoc(doc *Document) error`**:保存文档,提升版本。触发 `OnUpdatedDoc``OnUpdatedToC` - **`SetDoc(doc *Document) error`**:保存文档,提升版本。触发 `OnUpdatedDoc``OnUpdatedToC`
- **`SetMeta(path string, meta map[string]any) error`**:更新元数据,**不提升版本**。若包含 `textContent` 且为 Markdown会自动触发 `OnRemovedToC``OnUpdatedToC` - **`SetMeta(path string, meta map[string]any) error`**:更新元数据,**不提升版本**。若包含 `textContent` 且为 Markdown会自动触发 `OnRemovedToC``OnUpdatedToC`
- **`Move(oldPath, newPath string) error`**:重命名。触发 `OnRemovedDoc(oldPath)` -> `OnUpdatedDoc(newPath)`。 - **`Move(oldPath, newPath string) error`**:重命名。触发 `OnRemovedDoc(oldDoc)` -> `OnUpdatedDoc(newDoc)`。
- **`Get(path string) (*Document, error)`**:获取最新版。 - **`Get(path string) (*Document, error)`**:获取最新版。
- **`GetByVersion(path string, version uint64) (*Document, error)`**:获取历史版(含已删除)。 - **`GetByVersion(path string, version uint64) (*Document, error)`**:获取历史版(含已删除)。
- **`Remove(path string) error`**:删除文档。触发 `OnRemovedDoc`。 - **`Remove(path string) error`**:删除文档。触发 `OnRemovedDoc(doc)`。
### 3. 生命周期钩子 (Hooks) ### 3. 生命周期钩子 (Hooks)
```go ```go
// 文档级事件 // 所有 Hook 统一返回 *Document 对象,方便外部获取 Path, ToC, Metadata 等完整上下
engine.Hooks.OnUpdatedDoc = func(doc *docDB.Document) { engine.Hooks.OnUpdatedDoc = func(doc *docDB.Document) {
// 文档创建或内容更新时触发 (SetDoc 或 Move 触发) // 文档创建或内容更新时触发 (SetDoc 或 Move 触发)
} }
engine.Hooks.OnRemovedDoc = func(path string) { engine.Hooks.OnRemovedDoc = func(doc *docDB.Document) {
// 文档被物理删除或 Move 移除旧路径时触发 // 文档被物理删除或 Move 移除旧路径时触发
} }
// 目录(ToC)级事件 // 目录(ToC)级事件
engine.Hooks.OnUpdatedToC = func(path string, toc []docDB.ToCNode) { engine.Hooks.OnUpdatedToC = func(doc *docDB.Document) {
// 目录生成或变更时触发 // 目录生成或变更时触发
} }
engine.Hooks.OnRemovedToC = func(path string) { engine.Hooks.OnRemovedToC = func(doc *docDB.Document) {
// 目录失效时触发 // 目录失效时触发
} }
``` ```

60
doc.go
View File

@ -16,9 +16,9 @@ import (
// Hooks 生命周期钩子 // Hooks 生命周期钩子
type Hooks struct { type Hooks struct {
OnUpdatedDoc func(doc *Document) OnUpdatedDoc func(doc *Document)
OnRemovedDoc func(path string) OnRemovedDoc func(doc *Document)
OnUpdatedToC func(path string, toc []ToCNode) OnUpdatedToC func(doc *Document)
OnRemovedToC func(path string) OnRemovedToC func(doc *Document)
} }
// DocDBUnauthorized 实例 // DocDBUnauthorized 实例
@ -135,7 +135,7 @@ func (a *DocDB) SetDoc(docObj *Document) error {
var internalID string var internalID string
if existing != nil { if existing != nil {
a.triggerRemovedToC(docObj.Path) a.triggerRemovedToC(a.toDoc(existing))
a.archive(existing) a.archive(existing)
internalID = cast.String(existing["id"]) internalID = cast.String(existing["id"])
docObj.Version = cast.Uint64(existing["version"]) + 1 docObj.Version = cast.Uint64(existing["version"]) + 1
@ -168,7 +168,7 @@ func (a *DocDB) SetDoc(docObj *Document) error {
if err == nil { if err == nil {
a.triggerUpdatedDoc(docObj) a.triggerUpdatedDoc(docObj)
if docObj.ToC != nil { if docObj.ToC != nil {
a.triggerUpdatedToC(docObj.Path, docObj.ToC) a.triggerUpdatedToC(docObj)
} }
} }
return err return err
@ -185,10 +185,9 @@ func (a *DocDB) SetMeta(path string, meta map[string]any) error {
if text, ok := meta["textContent"].(string); ok { if text, ok := meta["textContent"].(string); ok {
docType := cast.String(existing["type"]) docType := cast.String(existing["type"])
if docType == "markdown" || strings.HasSuffix(strings.ToLower(path), ".md") { if docType == "markdown" || strings.HasSuffix(strings.ToLower(path), ".md") {
a.triggerRemovedToC(path) a.triggerRemovedToC(a.toDoc(existing))
newToC := ExtractToC(text) newToC := ExtractToC(text)
meta["toc"] = newToC meta["toc"] = newToC
a.triggerUpdatedToC(path, newToC)
} }
} }
@ -196,7 +195,11 @@ func (a *DocDB) SetMeta(path string, meta map[string]any) error {
existing[k] = v existing[k] = v
} }
return a.table.Set(existing) err = a.table.Set(existing)
if err == nil && meta["toc"] != nil {
a.triggerUpdatedToC(a.toDoc(existing))
}
return err
} }
// Move 修改路径 (重命名) // Move 修改路径 (重命名)
@ -207,9 +210,10 @@ func (a *DocDB) Move(oldPath, newPath string) error {
} }
// 触发删除旧路径事件 // 触发删除旧路径事件
a.triggerRemovedDoc(oldPath) oldDoc := a.toDoc(existing)
a.triggerRemovedDoc(oldDoc)
if existing["toc"] != nil { if existing["toc"] != nil {
a.triggerRemovedToC(oldPath) a.triggerRemovedToC(oldDoc)
} }
existing["path"] = newPath existing["path"] = newPath
@ -219,7 +223,7 @@ func (a *DocDB) Move(oldPath, newPath string) error {
if newDoc != nil { if newDoc != nil {
a.triggerUpdatedDoc(newDoc) a.triggerUpdatedDoc(newDoc)
if newDoc.ToC != nil { if newDoc.ToC != nil {
a.triggerUpdatedToC(newPath, newDoc.ToC) a.triggerUpdatedToC(newDoc)
} }
} }
} }
@ -234,6 +238,15 @@ func (a *DocDB) getRaw(path string) (map[string]any, error) {
return res[0], nil return res[0], nil
} }
func (a *DocDB) toDoc(record map[string]any) *Document {
if record == nil {
return nil
}
var doc Document
cast.Convert(&doc, record)
return &doc
}
func (a *DocDB) archive(existing map[string]any) { func (a *DocDB) archive(existing map[string]any) {
histRecord := make(map[string]any) histRecord := make(map[string]any)
for k, v := range existing { for k, v := range existing {
@ -284,9 +297,7 @@ func (a *DocDB) Get(path string) (*Document, error) {
if err != nil || res == nil { if err != nil || res == nil {
return nil, err return nil, err
} }
var doc Document return a.toDoc(res), nil
cast.Convert(&doc, res)
return &doc, nil
} }
// GetByVersion 按版本获取文档 // GetByVersion 按版本获取文档
@ -316,9 +327,7 @@ func (a *DocDB) GetByVersion(path string, version uint64) (*Document, error) {
return nil, err return nil, err
} }
var doc Document return a.toDoc(histRes[0]), nil
cast.Convert(&doc, histRes[0])
return &doc, nil
} }
// Remove 删除文档 (自动保存最后一版到历史) // Remove 删除文档 (自动保存最后一版到历史)
@ -328,9 +337,10 @@ func (a *DocDB) Remove(path string) error {
return nil return nil
} }
a.triggerRemovedDoc(path) doc := a.toDoc(existing)
a.triggerRemovedDoc(doc)
if existing["toc"] != nil { if existing["toc"] != nil {
a.triggerRemovedToC(path) a.triggerRemovedToC(doc)
} }
a.archive(existing) a.archive(existing)
@ -343,21 +353,21 @@ func (a *DocDB) triggerUpdatedDoc(doc *Document) {
} }
} }
func (a *DocDB) triggerRemovedDoc(path string) { func (a *DocDB) triggerRemovedDoc(doc *Document) {
if a.parent.Hooks.OnRemovedDoc != nil { if a.parent.Hooks.OnRemovedDoc != nil {
a.parent.Hooks.OnRemovedDoc(path) a.parent.Hooks.OnRemovedDoc(doc)
} }
} }
func (a *DocDB) triggerUpdatedToC(path string, toc []ToCNode) { func (a *DocDB) triggerUpdatedToC(doc *Document) {
if a.parent.Hooks.OnUpdatedToC != nil { if a.parent.Hooks.OnUpdatedToC != nil {
a.parent.Hooks.OnUpdatedToC(path, toc) a.parent.Hooks.OnUpdatedToC(doc)
} }
} }
func (a *DocDB) triggerRemovedToC(path string) { func (a *DocDB) triggerRemovedToC(doc *Document) {
if a.parent.Hooks.OnRemovedToC != nil { if a.parent.Hooks.OnRemovedToC != nil {
a.parent.Hooks.OnRemovedToC(path) a.parent.Hooks.OnRemovedToC(doc)
} }
} }

View File

@ -21,9 +21,9 @@ func TestDocDB(t *testing.T) {
var updatedCalled, removedDocCalled, updatedToCCalled, removedToCCalled bool var updatedCalled, removedDocCalled, updatedToCCalled, removedToCCalled bool
docDBInst.Hooks.OnUpdatedDoc = func(doc *Document) { updatedCalled = true } docDBInst.Hooks.OnUpdatedDoc = func(doc *Document) { updatedCalled = true }
docDBInst.Hooks.OnRemovedDoc = func(path string) { removedDocCalled = true } docDBInst.Hooks.OnRemovedDoc = func(doc *Document) { removedDocCalled = true }
docDBInst.Hooks.OnUpdatedToC = func(path string, toc []ToCNode) { updatedToCCalled = true } docDBInst.Hooks.OnUpdatedToC = func(doc *Document) { updatedToCCalled = true }
docDBInst.Hooks.OnRemovedToC = func(path string) { removedToCCalled = true } docDBInst.Hooks.OnRemovedToC = func(doc *Document) { removedToCCalled = true }
// 1. 测试 SetDoc (创建) // 1. 测试 SetDoc (创建)
docPath := "/test/doc1.md" docPath := "/test/doc1.md"