Files
Agent-System/internal/docgov/types.go
T

230 lines
10 KiB
Go

package docgov
import (
"errors"
"time"
)
var (
ErrDocumentNotFound = errors.New("docgov: document not found")
ErrDocumentLocked = errors.New("docgov: document is locked, direct modification prohibited")
ErrOnlyAuthorCanEdit = errors.New("docgov: only the designated author is allowed to edit or modify document content")
ErrReviewerCannotEdit = errors.New("docgov: reviewers are not allowed to edit document content, only comments and review decisions are permitted")
ErrApproverCannotEdit = errors.New("docgov: approvers are not allowed to edit document content, only comments and approval decisions are permitted")
ErrInvalidStateTransition = errors.New("docgov: invalid document lifecycle state transition")
ErrRejectReasonRequired = errors.New("docgov: rejection reason must not be empty")
ErrUnauthorizedActor = errors.New("docgov: actor is not authorized for this role or action")
ErrProposalNotFound = errors.New("docgov: change proposal not found")
ErrProposalAlreadyDecided = errors.New("docgov: change proposal has already been decided")
ErrChangeProposalPending = errors.New("docgov: document is claimed and has pending change proposal waiting for author approval")
ErrDocumentBusy = errors.New("docgov: document edit lease is held by another writer")
ErrLeaseExpired = errors.New("docgov: document edit lease has expired")
ErrLeaseNotHeld = errors.New("docgov: caller does not hold the document edit lease")
ErrForceEditNotAllowed = errors.New("docgov: force_direct_edit requires an approved_by_author proposal and unlocked drafting document")
)
// LeaseHolderType 编辑租约持有者类型
type LeaseHolderType string
const (
LeaseHolderNone LeaseHolderType = ""
LeaseHolderSquad LeaseHolderType = "squad"
LeaseHolderHuman LeaseHolderType = "human"
)
// DefaultSquadLeaseTTL 小队任务默认租约时长
const DefaultSquadLeaseTTL = 30 * time.Minute
// DefaultHumanLeaseTTL 人工编辑会话默认租约时长
const DefaultHumanLeaseTTL = 15 * time.Minute
// DocumentBusyError 携带租约占用详情,便于 HTTP 409 与 UI 提示
type DocumentBusyError struct {
DocID string `json:"doc_id"`
HolderType LeaseHolderType `json:"holder_type"`
HolderID string `json:"holder_id"`
WorkDir string `json:"work_dir,omitempty"`
ExpiresAt time.Time `json:"expires_at"`
Reason string `json:"reason"`
}
func (e *DocumentBusyError) Error() string {
if e.Reason != "" {
return e.Reason
}
return ErrDocumentBusy.Error()
}
func (e *DocumentBusyError) Unwrap() error { return ErrDocumentBusy }
// DocumentState 文档生命周期流转状态
type DocumentState string
const (
StateDrafting DocumentState = "drafting" // 编制中 (草稿)
StateReviewing DocumentState = "reviewing" // 审核中 (待审核, 锁定)
StateApproving DocumentState = "approving" // 批准中 (待批准, 锁定)
StateApproved DocumentState = "approved" // 已批准 (归档生效, 终态锁定)
StateRejected DocumentState = "rejected" // 已驳回 (中间状态或流转标记)
)
// UserRole 治理责任人角色 (各有且仅有一个责任人)
type UserRole string
const (
RoleAuthor UserRole = "author" // 编制人
RoleReviewer UserRole = "reviewer" // 审核人
RoleApprover UserRole = "approver" // 批准人
RoleSystem UserRole = "system" // 系统/智能体小队
)
// DocumentConfig 受控文档流转与退回配置
type DocumentConfig struct {
RejectRollbackTo DocumentState `json:"reject_rollback_to"` // 驳回退回目标: drafting 或 reviewing
AllowApproverToReviewer bool `json:"allow_approver_to_reviewer"` // 是否允许批准人退回至审核阶段
RequireAuthorConfirmOnReject bool `json:"require_author_confirm_on_reject"` // 驳回退回编制时是否需编制人确认已获悉
}
func DefaultDocumentConfig() DocumentConfig {
return DocumentConfig{
RejectRollbackTo: StateDrafting,
AllowApproverToReviewer: true,
RequireAuthorConfirmOnReject: true,
}
}
// DocumentRecord 受控文档治理台账实体
type DocumentRecord struct {
ID int64 `json:"id"`
DocID string `json:"doc_id"` // 业务唯一ID,如 doc_proj1_feasibility_reg_v1
ProjectID int64 `json:"project_id"` // 项目ID
TaskKey string `json:"task_key"` // 任务标识: feasibility_reg, intake, cdo_quote
Title string `json:"title"` // 标题
Stage string `json:"stage"` // 阶段: V0.5_feasibility
Version int `json:"version"` // 版本号
CurrentState DocumentState `json:"current_state"` // drafting, reviewing, approving, approved
IsLocked bool `json:"is_locked"` // reviewing/approving/approved 期间锁定
IsTemplateOnly bool `json:"is_template_only"` // 是否仅为骨架模板 (模板状态即使查看也不算认领)
ContentSHA256 string `json:"content_sha256"` // 当前版本正文哈希
WorkDir string `json:"work_dir"` // 关联工作区相对路径
// 三级法定责任人 (各有且仅有一个)
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"`
// 认领状态 (Claiming State)
AuthorClaimed bool `json:"author_claimed"` // 编制人是否已认领 (非模板状态查看/编辑后为 true)
AuthorClaimedAt *time.Time `json:"author_claimed_at"`
AuthorLastViewedAt *time.Time `json:"author_last_viewed_at"`
AuthorLastEditedAt *time.Time `json:"author_last_edited_at"`
ReviewerClaimed bool `json:"reviewer_claimed"` // 审核人是否已认领审核
ReviewerClaimedAt *time.Time `json:"reviewer_claimed_at"`
ApproverClaimed bool `json:"approver_claimed"` // 批准人是否已认领批准
ApproverClaimedAt *time.Time `json:"approver_claimed_at"`
// 驳回与决议记录
LastRejectedReason string `json:"last_rejected_reason"`
LastRejectedByRole string `json:"last_rejected_by_role"`
LastRejectedByName string `json:"last_rejected_by_name"`
LastRejectedAt *time.Time `json:"last_rejected_at"`
ApprovedAt *time.Time `json:"approved_at"`
// 编辑租约 (运行时互斥,正交于 IsLocked)
LeaseHolderType LeaseHolderType `json:"lease_holder_type,omitempty"`
LeaseHolderID string `json:"lease_holder_id,omitempty"`
LeaseWorkDir string `json:"lease_work_dir,omitempty"`
LeaseExpiresAt *time.Time `json:"lease_expires_at,omitempty"`
LeaseHeartbeatAt *time.Time `json:"lease_heartbeat_at,omitempty"`
Config DocumentConfig `json:"config"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// HasActiveLease 判断文档是否持有未过期的编辑租约
func (d *DocumentRecord) HasActiveLease(now time.Time) bool {
if d == nil || d.LeaseHolderType == LeaseHolderNone || d.LeaseHolderType == "" {
return false
}
if d.LeaseExpiresAt == nil {
return false
}
return d.LeaseExpiresAt.After(now)
}
// ClearLease 清空租约字段(内存)
func (d *DocumentRecord) ClearLease() {
d.LeaseHolderType = LeaseHolderNone
d.LeaseHolderID = ""
d.LeaseWorkDir = ""
d.LeaseExpiresAt = nil
d.LeaseHeartbeatAt = nil
}
// CommentType 批注类型
type CommentType string
const (
CommentGeneral CommentType = "comment" // 普通批注
CommentSuggestion CommentType = "suggestion" // 修改建议
CommentRejection CommentType = "reject_reason" // 驳回原因
)
// DocumentComment 批注与修改意见实体
type DocumentComment struct {
ID int64 `json:"id"`
DocID string `json:"doc_id"`
UserID int64 `json:"user_id"`
UserName string `json:"user_name"`
UserRole UserRole `json:"user_role"` // author, reviewer, approver
TargetSection string `json:"target_section"` // 目标章节锚点,如 "## 结论"
CommentType CommentType `json:"comment_type"` // comment, suggestion, reject_reason
Content string `json:"content"` // 意见内容
Status string `json:"status"` // open, resolved, accepted
CreatedAt time.Time `json:"created_at"`
}
// ProposalStatus 变更提案状态
type ProposalStatus string
const (
ProposalPendingAuthorApproval ProposalStatus = "pending_author_approval" // 待编制人拍板
ProposalApprovedByAuthor ProposalStatus = "approved_by_author" // 编制人已同意修改
ProposalRejectedByAuthor ProposalStatus = "rejected_by_author" // 编制人已拒绝修改
ProposalApplied ProposalStatus = "applied" // 已应用执行
)
// ChangeProposal 针对已认领文档的智能体修改变更提案实体
type ChangeProposal struct {
ID int64 `json:"id"`
ProposalID string `json:"proposal_id"`
DocID string `json:"doc_id"`
ProjectID int64 `json:"project_id"`
TriggeredBy string `json:"triggered_by"` // agent_squad, stage_sync, upstream_change
ProposedChanges string `json:"proposed_changes"` // 有哪些地方要改 (变更内容与对比摘要)
ImpactAnalysis string `json:"impact_analysis"` // 影响会是什么 (范围、上下游依赖)
RiskAssessment string `json:"risk_assessment"` // 可能产生的风险 (合规、工时、预算)
Status ProposalStatus `json:"status"` // pending_author_approval...
AuthorDecisionReason string `json:"author_decision_reason"` // 编制人批准/拒绝原因
DecidedAt *time.Time `json:"decided_at"`
CreatedAt time.Time `json:"created_at"`
}
// AuditLog 文档治理生命周期审计跟踪
type AuditLog struct {
ID int64 `json:"id"`
DocID string `json:"doc_id"`
Action string `json:"action"` // view, edit, claim, submit_review, review_approve, review_reject...
ActorID int64 `json:"actor_id"`
ActorName string `json:"actor_name"`
ActorRole UserRole `json:"actor_role"`
FromState DocumentState `json:"from_state"`
ToState DocumentState `json:"to_state"`
Details map[string]any `json:"details"`
CreatedAt time.Time `json:"created_at"`
}