Use ResolveDocTaskDuty so normalized author-only plans (author ID only) are treated as full A/A/A for access, stage visibility, and readiness. Co-authored-by: Cursor <cursoragent@cursor.com>
109 lines
2.9 KiB
Go
109 lines
2.9 KiB
Go
package projectinstance
|
|
|
|
const (
|
|
DutyRoleAuthor = "author"
|
|
DutyRoleReviewer = "reviewer"
|
|
DutyRoleApprover = "approver"
|
|
|
|
AccessCodeNotActivated = "WORKFLOW_NOT_ACTIVATED"
|
|
|
|
GovernanceBlockedHint = "当前项目尚未激活工作流,文档任务不会启动。请先在设置中配置并激活至少一份纳入人工审批的文档。"
|
|
)
|
|
|
|
// requiredDutyRole maps a document action to the single duty that may perform it.
|
|
func requiredDutyRole(action string) string {
|
|
switch action {
|
|
case DocAccessActionRun, DocAccessActionEdit, DocAccessActionSubmit:
|
|
return DutyRoleAuthor
|
|
case DocAccessActionReview:
|
|
return DutyRoleReviewer
|
|
case DocAccessActionApprove:
|
|
return DutyRoleApprover
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
func userHoldsDuty(d DocDuty, userID int64, role string) bool {
|
|
if userID <= 0 || role == "" {
|
|
return false
|
|
}
|
|
switch role {
|
|
case DutyRoleAuthor:
|
|
return d.AuthorUserID == userID
|
|
case DutyRoleReviewer:
|
|
return d.ReviewerUserID == userID
|
|
case DutyRoleApprover:
|
|
return d.ApproverUserID == userID
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// classifyAccess applies the locked duty-action matrix. govLive=false is pre-activation.
|
|
func classifyAccess(dt DocTaskPlan, action string, userID int64, isCreator bool, govLive bool) DocAccessDecision {
|
|
allow := DocAccessDecision{Allowed: true}
|
|
if action == DocAccessActionView {
|
|
return allow
|
|
}
|
|
if !govLive {
|
|
if action == DocAccessActionRun {
|
|
return DocAccessDecision{
|
|
Allowed: true,
|
|
Code: AccessCodeNotActivated,
|
|
Message: GovernanceBlockedHint,
|
|
}
|
|
}
|
|
return DocAccessDecision{
|
|
Allowed: false,
|
|
Code: AccessCodeNotActivated,
|
|
Message: GovernanceBlockedHint,
|
|
}
|
|
}
|
|
if !dt.Enabled {
|
|
return DocAccessDecision{Allowed: false, Code: AccessCodeDocTaskDisabled, Message: "该文档已被创建者禁用"}
|
|
}
|
|
mode, effective, _ := ResolveDocTaskDuty(dt)
|
|
switch mode {
|
|
case DutyModeNone:
|
|
switch action {
|
|
case DocAccessActionRun, DocAccessActionEdit:
|
|
if isCreator {
|
|
return allow
|
|
}
|
|
return DocAccessDecision{
|
|
Allowed: false,
|
|
Code: AccessCodeForbiddenDuty,
|
|
Message: "未纳入人工审批的文档仅创建者可编辑或执行",
|
|
}
|
|
case DocAccessActionReview, DocAccessActionApprove, DocAccessActionSubmit:
|
|
return DocAccessDecision{
|
|
Allowed: false,
|
|
Code: AccessCodeDutyNotManaged,
|
|
Message: "该文档未纳入人工审批,不能审核、批准或提交审批",
|
|
}
|
|
}
|
|
case DutyModePartial:
|
|
return DocAccessDecision{
|
|
Allowed: false,
|
|
Code: AccessCodeForbiddenDuty,
|
|
Message: "文档职责未完整指派,不能执行该动作",
|
|
}
|
|
case DutyModeFull:
|
|
role := requiredDutyRole(action)
|
|
if userHoldsDuty(effective, userID, role) {
|
|
return allow
|
|
}
|
|
return DocAccessDecision{
|
|
Allowed: false,
|
|
Code: AccessCodeForbiddenDuty,
|
|
Message: "当前用户不是该文档的对应职责人",
|
|
}
|
|
}
|
|
return DocAccessDecision{
|
|
Allowed: false,
|
|
Code: AccessCodeForbiddenDuty,
|
|
Message: "当前用户不是该文档职责人",
|
|
}
|
|
}
|