ai_xiaopei
9 days ago 9c98026d5490ebb2ac9465d565319d77f6d505b4
internal/draft/intake.go
@@ -27,7 +27,6 @@
   Title   string   `yaml:"title"`
   Type    string   `yaml:"type"`
   Status  string   `yaml:"status"`
   Draft   bool     `yaml:"draft"`
   Source  string   `yaml:"source,omitempty"`
   Tags    []string `yaml:"tags"`
   Created string   `yaml:"created"`
@@ -39,6 +38,27 @@
   Next    int    `json:"next"`
   ResetDay string `json:"reset_day"`
   LastUpdated string `json:"last_updated"`
}
// UnmarshalJSON 自定义反序列化,兼容 week 字段为数字或字符串
func (c *Counter) UnmarshalJSON(data []byte) error {
   type Alias Counter
   aux := &struct {
      Week interface{} `json:"week"`
      *Alias
   }{
      Alias: (*Alias)(c),
   }
   if err := json.Unmarshal(data, aux); err != nil {
      return err
   }
   switch v := aux.Week.(type) {
   case float64:
      c.Week = fmt.Sprintf("%d", int(v))
   case string:
      c.Week = v
   }
   return nil
}
// NewIntake 创建草稿录入器
@@ -92,10 +112,15 @@
      return fmt.Errorf("创建草稿目录失败: %w", err)
   }
   // 5. 写入 merge.md
   // 5. 写入 merge.md(无论是否有候选文档都生成)
   if mergeHint != nil {
      if err := i.writeMergeHint(draftDir, mergeHint, candidates); err != nil {
         fmt.Printf("警告: 写入合并指示失败: %v\n", err)
      }
   } else {
      // 没有候选文档时,生成默认的 merge.md
      if err := i.writeDefaultMergeHint(draftDir); err != nil {
         fmt.Printf("警告: 写入默认合并指示失败: %v\n", err)
      }
   }
@@ -109,14 +134,25 @@
      return nil, nil
   }
   // 使用所有 tags 作为关键词搜索
   // 使用所有 tags 作为关键词搜索,使用 OR 逻辑(任意一个匹配即可)
   // 这样即使某些 tag 匹配不到,其他 tag 也能找到结果
   opts := search.SearchOptions{
      TopN: 3,
   }
   results, err := search.Search(i.store, tags, opts)
   if err != nil {
      return nil, err
      // 如果搜索失败,尝试逐个 tag 搜索,返回第一个有结果的
      for _, tag := range tags {
         singleResults, singleErr := search.Search(i.store, []string{tag}, opts)
         if singleErr == nil && len(singleResults) > 0 {
            results = singleResults
            break
         }
      }
      if len(results) == 0 {
         return nil, nil // 所有 tag 都搜索失败,返回空而不是错误
      }
   }
   candidates := make([]llm.SearchCandidate, 0, len(results))
@@ -295,10 +331,10 @@
   return maxSeq + 1, nil
}
// getWeekNumber 获取周数
// getWeekNumber 获取周数(返回纯数字周数,与 counter.json 格式一致)
func (i *Intake) getWeekNumber(t time.Time) string {
   year, week := t.ISOWeek()
   return fmt.Sprintf("%d-W%02d", year, week)
   _, week := t.ISOWeek()
   return fmt.Sprintf("%d", week)
}
// sanitizeTitle 清理标题
@@ -321,8 +357,7 @@
   meta := DraftMeta{
      Title:   title,
      Type:    draftType,
      Status:  "待确认",
      Draft:   true,
      Status:  "draft",
      Source:  source,
      Tags:    tags,
      Created: now,
@@ -333,7 +368,6 @@
   sb.WriteString(fmt.Sprintf("title: %q\n", meta.Title))
   sb.WriteString(fmt.Sprintf("type: %q\n", meta.Type))
   sb.WriteString(fmt.Sprintf("status: %q\n", meta.Status))
   sb.WriteString(fmt.Sprintf("draft: %v\n", meta.Draft))
   if meta.Source != "" {
      sb.WriteString(fmt.Sprintf("source: %q\n", meta.Source))
   }
@@ -383,6 +417,32 @@
   return os.WriteFile(mergePath, []byte(sb.String()), 0644)
}
// writeDefaultMergeHint 写入默认的 merge.md(当没有候选文档时)
func (i *Intake) writeDefaultMergeHint(draftDir string) error {
   mergePath := filepath.Join(draftDir, "merge.md")
   now := time.Now().Format("2006-01-02 15:04:05")
   var sb strings.Builder
   sb.WriteString("---\n")
   sb.WriteString(fmt.Sprintf("generated_at: %q\n", now))
   sb.WriteString("confidence: \"low\"\n")
   sb.WriteString("---\n\n")
   sb.WriteString("## 合并指示\n\n")
   sb.WriteString("**操作类型**: new\n")
   sb.WriteString("**目标**: 新建独立文档\n")
   sb.WriteString("**理由**: 未找到相关文档,建议新建\n\n")
   sb.WriteString("## 依据\n\n")
   sb.WriteString("### 搜索到的相关文档\n\n")
   sb.WriteString("未找到相关文档。\n\n")
   sb.WriteString("## 建议\n\n")
   sb.WriteString("审阅时请根据草稿内容确定合适的分类和存放位置。\n")
   return os.WriteFile(mergePath, []byte(sb.String()), 0644)
}
// RebuildTags 批量重建 tags
func (i *Intake) RebuildTags(limit int, dryRun bool) error {
   // 扫描知识库所有 .md 文件