补齐 DocumentAccessDecider HTTP 409、projectcontext 私有对话排除统计,并更新整改 gap matrix 与测试报告。
133 lines
3.1 KiB
Go
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 = ©Doc
|
|
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
|
|
}
|