Files
Agent-System/internal/server/chat_document_reader.go
T

105 lines
3.1 KiB
Go

package server
import (
"context"
"fmt"
"strings"
"unicode"
"chat-agent-system/internal/auth"
"chat-agent-system/internal/chat"
"chat-agent-system/internal/coordinator"
"chat-agent-system/internal/docgov"
)
// bindSessionDocumentSummaryReader 将正文访问绑定到已认证的用户与项目。
// 读取前先应用与文档列表/正文 API 相同的阶段权限过滤。
func (s *Server) bindSessionDocumentSummaryReader(sess *chat.Session, user *auth.UserAccount) {
if sess == nil || sess.UserCtx == nil {
return
}
sess.UserCtx.DocumentSummaryReader = nil
projectID := sess.ProjectID
userID := int64(0)
if user != nil {
userID = user.ID
}
if projectID <= 0 || userID <= 0 || s.svc == nil || s.svc.DocGov() == nil {
return
}
sess.UserCtx.DocumentSummaryReader = func(ctx context.Context, target string) (*coordinator.ReadableDocument, error) {
if s.authorizer == nil {
return nil, fmt.Errorf("project authorization unavailable")
}
if err := s.authorizer.RequireProjectAccessOrAdmin(ctx, projectID, userID, s.isSystemAdminUser(ctx, user)); err != nil {
return nil, err
}
docs, err := s.svc.DocGov().ListWorkflowDocumentsByProject(ctx, projectID)
if err != nil {
return nil, err
}
docs = s.filterWorkflowDocsByStageAccess(ctx, projectID, userID, docs)
doc, err := selectReadableWorkflowDocument(docs, target)
if err != nil {
return nil, err
}
content, err := s.svc.DocGov().ReadDocumentContent(ctx, doc.DocID, s.repoRoot())
if err != nil {
return nil, err
}
return &coordinator.ReadableDocument{
DocID: doc.DocID,
Title: doc.Title,
TaskKey: doc.TaskKey,
LifecycleState: string(doc.LifecycleState),
Version: doc.Version,
Markdown: content.Markdown,
IsTemplateOnly: doc.IsTemplateOnly || content.IsTemplateOnly,
}, nil
}
}
func selectReadableWorkflowDocument(docs []docgov.WorkflowDocumentSummary, target string) (*docgov.WorkflowDocumentSummary, error) {
if len(docs) == 0 {
return nil, fmt.Errorf("no readable workflow documents")
}
targetKey := normalizeDocumentLookupKey(target)
if targetKey == "" {
if len(docs) == 1 {
return &docs[0], nil
}
return nil, fmt.Errorf("document target required")
}
for i := range docs {
if target == docs[i].DocID || target == docs[i].TaskKey || target == docs[i].Title {
return &docs[i], nil
}
}
for i := range docs {
keys := []string{
normalizeDocumentLookupKey(docs[i].DocID),
normalizeDocumentLookupKey(docs[i].TaskKey),
normalizeDocumentLookupKey(docs[i].Title),
}
for _, key := range keys {
if key != "" && (key == targetKey || strings.Contains(key, targetKey) || strings.Contains(targetKey, key)) {
return &docs[i], nil
}
}
}
return nil, fmt.Errorf("document not found in readable scope")
}
func normalizeDocumentLookupKey(value string) string {
value = strings.ToLower(strings.TrimSpace(value))
value = strings.NewReplacer("收集", "", "采集", "", "正文", "", "文档", "", "《", "", "》", "").Replace(value)
return strings.Map(func(r rune) rune {
if unicode.IsSpace(r) || unicode.IsPunct(r) {
return -1
}
return r
}, value)
}