Files
Agent-System/internal/projectinstance/document_access.go
T
weichen bb138475e3 fix(governance): 统一文档访问判定与上下文快照排除计数
补齐 DocumentAccessDecider HTTP 409、projectcontext 私有对话排除统计,并更新整改 gap matrix 与测试报告。
2026-09-12 15:10:23 +00:00

133 lines
3.1 KiB
Go

package projectinstance
import (
"context"
)
const (
DocAccessActionView = "view"
DocAccessActionEdit = "edit"
DocAccessActionSubmit = "submit"
DocAccessActionReview = "review"
DocAccessActionApprove = "approve"
DocAccessActionRun = "run"
AccessCodeDocTaskDisabled = "DOC_TASK_DISABLED"
AccessCodeNotCurrentStage = "DOC_TASK_NOT_IN_ACTIVE_STAGE"
AccessCodeForbiddenDuty = "DOC_DUTY_FORBIDDEN"
AccessCodeInstanceClosed = "WORKFLOW_INSTANCE_CLOSED"
)
// DocAccessDecision 统一文档任务访问判定结果。
type DocAccessDecision struct {
Allowed bool `json:"allowed"`
Code string `json:"code,omitempty"`
Message string `json:"message,omitempty"`
}
// CheckDocTaskAccess 对 live 治理实例下的文档任务做 fail-closed 判定。
// 无 live 实例时不限制(legacy 兼容)。
func (s *Service) CheckDocTaskAccess(
ctx context.Context,
projectID int64,
userID int64,
taskKey string,
action string,
isCreator bool,
) DocAccessDecision {
allow := DocAccessDecision{Allowed: true}
if s == nil || s.store == nil || projectID <= 0 || userID <= 0 || taskKey == "" {
return allow
}
live, err := s.store.GetLiveInstance(ctx, projectID)
if err != nil || live == nil {
return allow
}
if live.Status == InstanceCompleted || live.Status == "terminated" {
return DocAccessDecision{
Allowed: false,
Code: AccessCodeInstanceClosed,
Message: "工作流实例已结束",
}
}
detail, err := s.store.GetInstanceByID(ctx, live.ID)
if err != nil || detail == nil {
return DocAccessDecision{Allowed: false, Code: AccessCodeInstanceClosed, Message: "工作流实例不可用"}
}
var docInst *DocTaskInstance
var stageKey string
for _, st := range detail.Stages {
for i := range st.DocTasks {
if st.DocTasks[i].TaskKey == taskKey {
copyDoc := st.DocTasks[i]
docInst = &copyDoc
stageKey = st.StageKey
break
}
}
if docInst != nil {
break
}
}
if docInst == nil {
return allow
}
if docInst.Status == "skipped_disabled" {
return DocAccessDecision{
Allowed: false,
Code: AccessCodeDocTaskDisabled,
Message: "该文档已被创建者禁用",
}
}
if stageKey != "" && stageKey != detail.ActiveStageKey {
if action != DocAccessActionView {
return DocAccessDecision{
Allowed: false,
Code: AccessCodeNotCurrentStage,
Message: "文档不在当前活动阶段",
}
}
}
if action == DocAccessActionView {
return allow
}
planTask := findDocTaskPlan(live.ConfigSnapshot, taskKey)
if planTask == nil || !planTask.Enabled {
return DocAccessDecision{
Allowed: false,
Code: AccessCodeDocTaskDisabled,
Message: "该文档已被创建者禁用",
}
}
if isCreator {
return allow
}
if docTaskDutyForUser(*planTask, userID) {
return allow
}
return DocAccessDecision{
Allowed: false,
Code: AccessCodeForbiddenDuty,
Message: "当前用户不是该文档职责人",
}
}
func findDocTaskPlan(plan WorkflowPlan, taskKey string) *DocTaskPlan {
for _, st := range plan.Stages {
for i := range st.DocTasks {
if st.DocTasks[i].TaskKey == taskKey {
return &st.DocTasks[i]
}
}
}
return nil
}