diff --git a/internal/server/stage_access_helpers.go b/internal/server/stage_access_helpers.go index de68abb1..949eda82 100644 --- a/internal/server/stage_access_helpers.go +++ b/internal/server/stage_access_helpers.go @@ -4,6 +4,8 @@ import ( "context" "encoding/json" "net/http" + "strconv" + "strings" "chat-agent-system/internal/auth" "chat-agent-system/internal/chat" @@ -63,14 +65,38 @@ func (s *Server) enforceWorkflowDocTaskAction(w http.ResponseWriter, ctx context } func (s *Server) enforceDocGovWorkflowAction(w http.ResponseWriter, ctx context.Context, userID int64, docID, action string) bool { - if s.svc == nil || s.svc.DocGov() == nil || docID == "" { + if docID == "" { return true } - doc, err := s.svc.DocGov().GetDocument(ctx, docID) - if err != nil || doc == nil || doc.ProjectID <= 0 || doc.TaskKey == "" { - return true + if s.svc != nil && s.svc.DocGov() != nil { + doc, err := s.svc.DocGov().GetDocument(ctx, docID) + if err == nil && doc != nil && doc.ProjectID > 0 && doc.TaskKey != "" { + return s.enforceWorkflowDocTaskAction(w, ctx, doc.ProjectID, userID, doc.TaskKey, action) + } } - return s.enforceWorkflowDocTaskAction(w, ctx, doc.ProjectID, userID, doc.TaskKey, action) + if projectID, taskKey, ok := parseCanonicalWorkflowDocID(docID); ok { + return s.enforceWorkflowDocTaskAction(w, ctx, projectID, userID, taskKey, action) + } + return true +} + +// parseCanonicalWorkflowDocID 解析 doc_proj{projectID}_{taskKey}。 +// 模板尚未落 document_records 时,送审/审核/批准仍按任务职责鉴权。 +func parseCanonicalWorkflowDocID(docID string) (int64, string, bool) { + const prefix = "doc_proj" + if !strings.HasPrefix(docID, prefix) { + return 0, "", false + } + rest := strings.TrimPrefix(docID, prefix) + idx := strings.IndexByte(rest, '_') + if idx <= 0 || idx >= len(rest)-1 { + return 0, "", false + } + projectID, err := strconv.ParseInt(rest[:idx], 10, 64) + if err != nil || projectID <= 0 { + return 0, "", false + } + return projectID, rest[idx+1:], true } func (s *Server) writeDocAccessDecision(w http.ResponseWriter, decision projectinstance.DocAccessDecision) { diff --git a/internal/server/stage_access_helpers_test.go b/internal/server/stage_access_helpers_test.go new file mode 100644 index 00000000..4599e91e --- /dev/null +++ b/internal/server/stage_access_helpers_test.go @@ -0,0 +1,26 @@ +package server + +import "testing" + +func TestParseCanonicalWorkflowDocID(t *testing.T) { + t.Parallel() + cases := []struct { + in string + wantID int64 + wantKey string + ok bool + }{ + {"doc_proj6201_intake", 6201, "intake", true}, + {"doc_proj12_feasibility_reg", 12, "feasibility_reg", true}, + {"doc-p6201-intake", 0, "", false}, + {"doc_proj_intake", 0, "", false}, + {"doc_proj0_intake", 0, "", false}, + {"", 0, "", false}, + } + for _, tc := range cases { + gotID, gotKey, ok := parseCanonicalWorkflowDocID(tc.in) + if ok != tc.ok || gotID != tc.wantID || gotKey != tc.wantKey { + t.Fatalf("%q -> (%d,%q,%v) want (%d,%q,%v)", tc.in, gotID, gotKey, ok, tc.wantID, tc.wantKey, tc.ok) + } + } +} diff --git a/internal/taskrun/types.go b/internal/taskrun/types.go index 9f7c707b..6a2a3c83 100644 --- a/internal/taskrun/types.go +++ b/internal/taskrun/types.go @@ -36,7 +36,7 @@ type RunRecord struct { WorkflowInstanceID int64 StageInstanceID int64 WorkflowStageKey string - Status string // pending | running | completed | failed | cancelled | pending_author_confirmation | … + Status string // pending | running | completed | failed | cancelled | pending_author_confirmation | blocked_pending_governance | … WorkspaceID string AssistantMsgID int64 WorkerID string