feat(docgov): project document summary returns authoritative duties
ListWorkflowDocumentsByProject now projects author/reviewer/approver id+name so clients cannot invent the current user as author. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -27,6 +27,12 @@ type WorkflowDocumentSummary struct {
|
||||
LeaseHolderID string `json:"lease_holder_id,omitempty"`
|
||||
LeaseExpiresAt *time.Time `json:"lease_expires_at,omitempty"`
|
||||
LeaseHeartbeatAt *time.Time `json:"lease_heartbeat_at,omitempty"`
|
||||
AuthorID int64 `json:"author_id"`
|
||||
AuthorName string `json:"author_name"`
|
||||
ReviewerID int64 `json:"reviewer_id"`
|
||||
ReviewerName string `json:"reviewer_name"`
|
||||
ApproverID int64 `json:"approver_id"`
|
||||
ApproverName string `json:"approver_name"`
|
||||
}
|
||||
|
||||
// DocumentContent 受控文档正文读取结果。
|
||||
@@ -49,7 +55,10 @@ func (e *GovernanceEngine) ListWorkflowDocumentsByProject(ctx context.Context, p
|
||||
SELECT doc_id, project_id, task_key, title, current_state, is_template_only,
|
||||
COALESCE(content_sha256, ''), COALESCE(work_dir, ''), version, updated_at,
|
||||
COALESCE(lease_holder_type, ''), COALESCE(lease_holder_id, ''),
|
||||
lease_expires_at, lease_heartbeat_at
|
||||
lease_expires_at, lease_heartbeat_at,
|
||||
COALESCE(author_id, 0), COALESCE(author_name, ''),
|
||||
COALESCE(reviewer_id, 0), COALESCE(reviewer_name, ''),
|
||||
COALESCE(approver_id, 0), COALESCE(approver_name, '')
|
||||
FROM document_records
|
||||
WHERE project_id = $1 AND task_key <> ''
|
||||
ORDER BY task_key ASC, doc_id ASC
|
||||
@@ -67,6 +76,8 @@ func (e *GovernanceEngine) ListWorkflowDocumentsByProject(ctx context.Context, p
|
||||
&item.DocID, &item.ProjectID, &item.TaskKey, &item.Title, &state,
|
||||
&item.IsTemplateOnly, &item.ContentSHA256, &item.WorkDir, &item.Version, &updatedAt,
|
||||
&item.LeaseHolderType, &item.LeaseHolderID, &item.LeaseExpiresAt, &item.LeaseHeartbeatAt,
|
||||
&item.AuthorID, &item.AuthorName, &item.ReviewerID, &item.ReviewerName,
|
||||
&item.ApproverID, &item.ApproverName,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -98,6 +109,12 @@ func (e *GovernanceEngine) ListWorkflowDocumentsByProject(ctx context.Context, p
|
||||
LeaseHolderID: doc.LeaseHolderID,
|
||||
LeaseExpiresAt: doc.LeaseExpiresAt,
|
||||
LeaseHeartbeatAt: doc.LeaseHeartbeatAt,
|
||||
AuthorID: doc.AuthorID,
|
||||
AuthorName: doc.AuthorName,
|
||||
ReviewerID: doc.ReviewerID,
|
||||
ReviewerName: doc.ReviewerName,
|
||||
ApproverID: doc.ApproverID,
|
||||
ApproverName: doc.ApproverName,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,3 +38,44 @@ func TestListWorkflowDocumentsIncludesActiveLease(t *testing.T) {
|
||||
t.Fatalf("lease timestamps missing: %+v", docs[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestListWorkflowDocumentsIncludesAuthoritativeDuties(t *testing.T) {
|
||||
engine := docgov.NewGovernanceEngine(nil)
|
||||
ctx := context.Background()
|
||||
const (
|
||||
docID = "doc_proj143_intake"
|
||||
authorID = int64(9)
|
||||
reviewerID = int64(2)
|
||||
approverID = int64(3)
|
||||
)
|
||||
if _, err := engine.CreateOrGetDocument(
|
||||
ctx, docID, 143, "intake", "用户初始需求收集表", "V0.1", "",
|
||||
authorID, "编制人A", reviewerID, "审核人B", approverID, "批准人C",
|
||||
); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
docs, err := engine.ListWorkflowDocumentsByProject(ctx, 143)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(docs) != 1 {
|
||||
t.Fatalf("documents=%d", len(docs))
|
||||
}
|
||||
got := docs[0]
|
||||
if got.AuthorID != authorID || got.AuthorName != "编制人A" {
|
||||
t.Fatalf("author not projected: id=%d name=%q", got.AuthorID, got.AuthorName)
|
||||
}
|
||||
if got.ReviewerID != reviewerID || got.ReviewerName != "审核人B" {
|
||||
t.Fatalf("reviewer not projected: id=%d name=%q", got.ReviewerID, got.ReviewerName)
|
||||
}
|
||||
if got.ApproverID != approverID || got.ApproverName != "批准人C" {
|
||||
t.Fatalf("approver not projected: id=%d name=%q", got.ApproverID, got.ApproverName)
|
||||
}
|
||||
if got.AuthorID == reviewerID {
|
||||
t.Fatalf("reviewer B must not become author: %+v", got)
|
||||
}
|
||||
if got.AuthorID == 99 {
|
||||
t.Fatalf("stranger must not become author: %+v", got)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user