Files

69 lines
3.4 KiB
Go
Raw Permalink Normal View History

package service
import (
"context"
"crypto/sha256"
"fmt"
"sort"
"strings"
"github.com/Tencent/WeKnora/internal/types"
)
// Reuse the same access-checked scope resolver as retrieval. Descriptions are
// persisted document summaries, not a claim that every raw chunk was read.
func (s *sessionService) KnowledgeReadingOverview(ctx context.Context, kbs, files []string, scopes []types.TagScope) (*types.KnowledgeReadingOverview, error) {
tenant, ok := types.TenantIDFromContext(ctx)
if !ok { return nil, fmt.Errorf("workspace ID not found in context") }
targets, err := s.buildSearchTargets(ctx, tenant, kbs, files, scopes)
if err != nil { return nil, err }
result := &types.KnowledgeReadingOverview{Entries: []types.KnowledgeReadingEntry{}}
scopeParts := []string{}
seen := map[string]bool{}
for _, target := range targets {
ids, tags := append([]string(nil), target.KnowledgeIDs...), append([]string(nil), target.TagIDs...)
sort.Strings(ids)
sort.Strings(tags)
scopeParts = append(scopeParts, fmt.Sprintf("%s|%s|%v|%v", target.KnowledgeBaseID, target.Type, ids, tags))
base, checkErr := s.knowledgeBaseService.GetKnowledgeBaseByID(ctx, target.KnowledgeBaseID)
if checkErr != nil { return nil, checkErr }
if base == nil { return nil, fmt.Errorf("知识库不存在或无权使用") }
listCtx := ctx
if base.TenantID != tenant {
if s.kbShareService == nil { return nil, fmt.Errorf("知识库不存在或无权使用") }
allowed, shareErr := s.kbShareService.HasTenantKBPermission(ctx, base.ID, tenant, types.TenantRoleFromContext(ctx), types.OrgRoleViewer)
if shareErr != nil || !allowed { return nil, fmt.Errorf("知识库不存在或无权使用") }
listCtx = context.WithValue(ctx, types.TenantIDContextKey, base.TenantID)
}
var rows []*types.Knowledge
if target.Type == types.SearchTargetTypeKnowledge {
rows, err = s.knowledgeService.GetKnowledgeBatchWithSharedAccess(ctx, tenant, target.KnowledgeIDs)
} else {
rows, err = s.knowledgeService.ListKnowledgeByKnowledgeBaseID(listCtx, target.KnowledgeBaseID)
}
if err != nil { return nil, err }
for _, row := range rows {
if row == nil || row.KnowledgeBaseID != target.KnowledgeBaseID || seen[row.ID] || row.EnableStatus == "disabled" || row.ParseStatus != types.ParseStatusCompleted { continue }
seen[row.ID] = true
title := row.Title
if title == "" { title = row.FileName }
summary := ""
// FAQ tags select chunks within one document; its whole-document
// summary could expose content outside the chosen category.
if row.SummaryStatus == types.SummaryStatusCompleted && len(target.TagIDs) == 0 { summary = strings.TrimSpace(row.Description) }
entry := types.KnowledgeReadingEntry{ID: row.ID, KnowledgeBaseID: row.KnowledgeBaseID, Title: title, Folder: row.FolderPath, Summary: summary}
entry.Fingerprint = fmt.Sprintf("%x", sha256.Sum256([]byte(fmt.Sprintf("%s|%s|%s|%s|%s", row.ID, row.FileHash, row.UpdatedAt.UTC().Format("2006-01-02T15:04:05.999999999Z"), title, summary))))
result.Entries = append(result.Entries, entry)
result.DocumentCount++
if summary != "" { result.SummaryCount++ }
}
}
sort.Slice(result.Entries, func(i,j int) bool { return result.Entries[i].ID < result.Entries[j].ID })
hash := sha256.New()
sort.Strings(scopeParts)
for _, part := range scopeParts { fmt.Fprintln(hash, part) }
for _, entry := range result.Entries { fmt.Fprintln(hash, entry.Fingerprint) }
result.Fingerprint = fmt.Sprintf("%x", hash.Sum(nil))
return result, nil
}