| | |
| | | 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"` |
| | |
| | | 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 创建草稿录入器 |
| | |
| | | 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) |
| | | } |
| | | } |
| | | |
| | |
| | | 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)) |
| | |
| | | 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 清理标题 |
| | |
| | | meta := DraftMeta{ |
| | | Title: title, |
| | | Type: draftType, |
| | | Status: "待确认", |
| | | Draft: true, |
| | | Status: "draft", |
| | | Source: source, |
| | | Tags: tags, |
| | | Created: now, |
| | |
| | | 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)) |
| | | } |
| | |
| | | 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 文件 |