Wire intent→coordinator DocResolver confirm cards→action_executor kinds for comment, reopen, pending-supplements query, and real submit-review execution.
591 lines
20 KiB
Go
591 lines
20 KiB
Go
package coordinator
|
|
|
|
import (
|
|
"chat-agent-system/internal/contracts/vocabulary"
|
|
"context"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"chat-agent-system/internal/intent"
|
|
)
|
|
|
|
// handleDocGovReject 处理受控文档审核/批准驳回与二次确认交互卡片挂载(角色硬门禁)
|
|
func (e *CoordinatorEngine) handleDocGovReject(ctx context.Context, cand *intent.CandidateIntent, userCtx *UserProjectContext) (*CoordinationResult, error) {
|
|
docName := cand.FilledSlots[intent.SlotTargetDoc]
|
|
if docName == "" {
|
|
if userCtx.InteractiveCard != nil && userCtx.InteractiveCard.TargetDocID != "" {
|
|
docName = userCtx.InteractiveCard.TargetDocID
|
|
} else {
|
|
docName = "当前受控文档"
|
|
}
|
|
}
|
|
|
|
rawReason := cand.FilledSlots[intent.SlotRejectReason]
|
|
section := cand.FilledSlots[intent.SlotTargetSection]
|
|
if section == "" {
|
|
section = extractSectionClues(rawReason)
|
|
}
|
|
|
|
role := effectiveDocRole(userCtx)
|
|
isFinal := cand.Intent == intent.IntentDocGovFinalReject
|
|
|
|
// 与批准对称的硬门禁
|
|
if isFinal {
|
|
if !isApproverAuthorized(userCtx.User) && role != "approver" {
|
|
return &CoordinationResult{
|
|
Status: StatusRejected,
|
|
Intent: cand.Intent,
|
|
Branch: cand.Branch,
|
|
RejectReason: "UNAUTHORIZED_REJECT_ROLE",
|
|
NaturalReply: fmt.Sprintf("权限不足:终审驳回需批准人角色。当前您的身份为「%s」。若需了解驳回原因,可直接询问。", roleLabelCN(role)),
|
|
UpdatedContext: userCtx,
|
|
}, nil
|
|
}
|
|
} else {
|
|
if !isReviewerAuthorized(userCtx.User) && role != "reviewer" {
|
|
return &CoordinationResult{
|
|
Status: StatusRejected,
|
|
Intent: cand.Intent,
|
|
Branch: cand.Branch,
|
|
RejectReason: "UNAUTHORIZED_REJECT_ROLE",
|
|
NaturalReply: fmt.Sprintf("权限不足:审核驳回需审核人角色。当前您的身份为「%s」。若需了解驳回原因,可直接询问「被驳回的原因是什么」。", roleLabelCN(role)),
|
|
UpdatedContext: userCtx,
|
|
}, nil
|
|
}
|
|
}
|
|
|
|
// 规范化策略角色(授权通过后按意图选审核/批准确认卡)
|
|
policyRole := role
|
|
if isFinal {
|
|
policyRole = "approver"
|
|
} else if policyRole != "reviewer" && isReviewerAuthorized(userCtx.User) {
|
|
policyRole = "reviewer"
|
|
}
|
|
|
|
lifecycle := "reviewing"
|
|
if isFinal {
|
|
lifecycle = "approving"
|
|
}
|
|
|
|
resolved := ResolveRejectCard(policyRole, lifecycle, docName, docName, section, rawReason, RejectUtteranceExecute)
|
|
if resolved.Kind == "deny_text" || resolved.Card == nil {
|
|
return &CoordinationResult{
|
|
Status: StatusRejected,
|
|
Intent: cand.Intent,
|
|
Branch: cand.Branch,
|
|
RejectReason: resolved.DenyReason,
|
|
NaturalReply: resolved.Reply,
|
|
UpdatedContext: userCtx,
|
|
}, nil
|
|
}
|
|
|
|
userCtx.applyCard(resolved.Card)
|
|
userCtx.applyPending(resolved.Expectation)
|
|
|
|
return &CoordinationResult{
|
|
Status: StatusAccepted,
|
|
Intent: cand.Intent,
|
|
Branch: cand.Branch,
|
|
NaturalReply: resolved.Reply,
|
|
InteractiveCard: resolved.Card,
|
|
NextExpectation: resolved.Expectation,
|
|
UpdatedContext: userCtx,
|
|
}, nil
|
|
}
|
|
|
|
// isReviewerAuthorized 校验审核人或主导工程师权限
|
|
func isReviewerAuthorized(u UserInfo) bool {
|
|
r := strings.ToLower(strings.TrimSpace(u.Role))
|
|
pr := strings.ToLower(strings.TrimSpace(u.ProjectRole))
|
|
return r == "reviewer" || pr == "reviewer" ||
|
|
r == "lead_engineer" || pr == "lead_engineer" ||
|
|
strings.Contains(pr, "审核") || strings.Contains(r, "审核") ||
|
|
strings.Contains(pr, "主导") || strings.Contains(r, "主导")
|
|
}
|
|
|
|
// isApproverAuthorized 校验批准人或项目经理权限
|
|
func isApproverAuthorized(u UserInfo) bool {
|
|
r := strings.ToLower(strings.TrimSpace(u.Role))
|
|
pr := strings.ToLower(strings.TrimSpace(u.ProjectRole))
|
|
return r == "approver" || pr == "approver" ||
|
|
r == "project_manager" || pr == "project_manager" ||
|
|
strings.Contains(pr, "批准") || strings.Contains(r, "批准") ||
|
|
strings.Contains(pr, "总监") || strings.Contains(r, "总监") ||
|
|
r == "director"
|
|
}
|
|
|
|
// handleDocGovApprove 处理受控文档审核/批准通过与终审批准二次确认
|
|
func (e *CoordinatorEngine) handleDocGovApprove(ctx context.Context, cand *intent.CandidateIntent, userCtx *UserProjectContext) (*CoordinationResult, error) {
|
|
docName := cand.FilledSlots[intent.SlotTargetDoc]
|
|
if docName == "" {
|
|
if userCtx.InteractiveCard != nil && userCtx.InteractiveCard.TargetDocID != "" {
|
|
docName = userCtx.InteractiveCard.TargetDocID
|
|
} else {
|
|
docName = "当前受控文档"
|
|
}
|
|
}
|
|
|
|
currentRole := userCtx.User.ProjectRole
|
|
if currentRole == "" {
|
|
currentRole = userCtx.User.Role
|
|
}
|
|
if currentRole == "" {
|
|
currentRole = "未知角色"
|
|
}
|
|
|
|
isFinal := cand.Intent == intent.IntentDocGovFinalApprove
|
|
|
|
if isFinal {
|
|
// 校验终审批准权限: approver 或 project_manager
|
|
if !isApproverAuthorized(userCtx.User) {
|
|
return &CoordinationResult{
|
|
Status: StatusRejected,
|
|
Intent: cand.Intent,
|
|
Branch: cand.Branch,
|
|
RejectReason: "UNAUTHORIZED_APPROVAL_ROLE",
|
|
NaturalReply: fmt.Sprintf("权限不足:受控文档终审批准需要批准人 (approver) 或项目经理 (project_manager) 权限。当前您的身份为「%s」,无权执行终审批准操作。", currentRole),
|
|
UpdatedContext: userCtx,
|
|
}, nil
|
|
}
|
|
|
|
cardID := fmt.Sprintf("card_final_appr_%d", time.Now().UnixNano())
|
|
cardPayload := &intent.InteractiveCardPayload{
|
|
CardID: cardID,
|
|
CardType: "CONFIRMATION",
|
|
Title: "受控文档终审批准生效确认",
|
|
TargetDocID: docName,
|
|
TargetDocTitle: fmt.Sprintf("《%s》", docName),
|
|
ProposedContent: "正式终审批准受控文档,签署电子签名并受控归档生效",
|
|
ImpactRisk: "终审批准后文档正文将永久锁定为基线版本,后续非受控修改将被严格拦截,任何变更必须走变更审批流程",
|
|
CurrentStatus: "active",
|
|
TTLTurns: 3,
|
|
CreatedAt: time.Now(),
|
|
Actions: []intent.CardAction{
|
|
{
|
|
ActionID: "confirm_final_approve",
|
|
Label: "确认终审批准生效",
|
|
Style: "primary",
|
|
IsAffirmativeDefault: true,
|
|
Payload: map[string]string{
|
|
"target_doc": docName,
|
|
"target_action": "confirm_final_approve",
|
|
},
|
|
},
|
|
{
|
|
ActionID: "cancel",
|
|
Label: "取消",
|
|
Style: "subtle",
|
|
Payload: map[string]string{
|
|
"action": "cancel",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
nextExp := &intent.PendingExpectation{
|
|
ExpectationID: "exp_" + cardPayload.CardID,
|
|
ExpectedIntent: intent.IntentDocGovProposalDecide,
|
|
TargetAction: "confirm_final_approve",
|
|
Prompt: "请确认是否执行终审批准操作",
|
|
AffirmativeWord: "确认",
|
|
DeclineWord: "取消",
|
|
ExpiresAt: time.Now().Add(5 * time.Minute),
|
|
Payload: cardPayload.Actions[0].Payload,
|
|
AllowAffirmative: true,
|
|
CreatedAt: time.Now(),
|
|
DefaultOption: &intent.ExpectationOption{
|
|
OptionID: "confirm_final_approve",
|
|
Title: "确认终审批准生效",
|
|
Intent: intent.IntentDocGovProposalDecide,
|
|
Slots: cardPayload.Actions[0].Payload,
|
|
},
|
|
}
|
|
|
|
userCtx.applyCard(cardPayload)
|
|
userCtx.applyPending(nextExp)
|
|
|
|
reply := fmt.Sprintf("已为您起草针对《%s》的终审批准确认单。终审批准将使文档正式归档并永久锁定,请审阅下方卡片并在确认无误后点击确认或回复‘确认’。", docName)
|
|
|
|
return &CoordinationResult{
|
|
Status: StatusAccepted,
|
|
Intent: cand.Intent,
|
|
Branch: cand.Branch,
|
|
NaturalReply: reply,
|
|
InteractiveCard: cardPayload,
|
|
NextExpectation: nextExp,
|
|
UpdatedContext: userCtx,
|
|
}, nil
|
|
}
|
|
|
|
// 校验审核通过权限: reviewer 或 lead_engineer
|
|
if !isReviewerAuthorized(userCtx.User) {
|
|
return &CoordinationResult{
|
|
Status: StatusRejected,
|
|
Intent: cand.Intent,
|
|
Branch: cand.Branch,
|
|
RejectReason: "UNAUTHORIZED_REVIEW_ROLE",
|
|
NaturalReply: fmt.Sprintf("权限不足:受控文档审核通过需要审核人 (reviewer) 或主导工程师 (lead_engineer) 权限。当前您的身份为「%s」,无权执行审核通过操作。", currentRole),
|
|
UpdatedContext: userCtx,
|
|
}, nil
|
|
}
|
|
|
|
reply := fmt.Sprintf("正在处理您对《%s》的审核通过请求…", docName)
|
|
|
|
return &CoordinationResult{
|
|
Status: StatusAccepted,
|
|
Intent: cand.Intent,
|
|
Branch: cand.Branch,
|
|
NaturalReply: reply,
|
|
UpdatedContext: userCtx,
|
|
PendingAction: &PendingAction{
|
|
Kind: "docgov.review_approve",
|
|
DocID: docName,
|
|
Comment: cand.FilledSlots[intent.SlotRejectReason],
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// handleDocGovClaim 解析门 → 确认卡 → 拍板后 PendingAction(docgov.claim)
|
|
func (e *CoordinatorEngine) handleDocGovClaim(ctx context.Context, cand *intent.CandidateIntent, userCtx *UserProjectContext) (*CoordinationResult, error) {
|
|
doc, gate := e.resolveTargetDocGate(ctx, cand, userCtx)
|
|
if gate != nil {
|
|
return gate, nil
|
|
}
|
|
title := displayDocTitle(doc)
|
|
card, exp := buildConfirmCard(
|
|
"受控文档认领确认",
|
|
doc.DocID, title, "",
|
|
"确认认领该文档并开始编制责任",
|
|
"认领将记录您已查看并承接该文档的编制责任;不会静默选择其他文档。",
|
|
"confirm_claim", "确认认领",
|
|
map[string]string{"task_key": doc.TaskKey},
|
|
)
|
|
userCtx.applyCard(card)
|
|
userCtx.applyPending(exp)
|
|
reply := fmt.Sprintf("已为《%s》起草认领确认单。请确认文档无误后点击确认或回复「确认」。", title)
|
|
return &CoordinationResult{
|
|
Status: StatusAccepted,
|
|
Intent: cand.Intent,
|
|
Branch: cand.Branch,
|
|
NaturalReply: reply,
|
|
InteractiveCard: card,
|
|
NextExpectation: exp,
|
|
UpdatedContext: userCtx,
|
|
}, nil
|
|
}
|
|
|
|
// handleDocGovSubmitReview 解析门 → drafting 校验 → CONFIRMATION 卡(含锁定后果)→ 拍板后 PendingAction
|
|
func (e *CoordinatorEngine) handleDocGovSubmitReview(ctx context.Context, cand *intent.CandidateIntent, userCtx *UserProjectContext) (*CoordinationResult, error) {
|
|
doc, gate := e.resolveTargetDocGate(ctx, cand, userCtx)
|
|
if gate != nil {
|
|
return gate, nil
|
|
}
|
|
state := doc.State
|
|
// ContextSnapshot hardcodes DocLifecycleState="reviewing" whenever an interactive
|
|
// card has ActiveDocID — do not treat that as authoritative unless the resolver
|
|
// already supplied a real lifecycle state.
|
|
if state != "" && state != vocabulary.StateDrafting {
|
|
return &CoordinationResult{
|
|
Status: StatusRejected,
|
|
Intent: cand.Intent,
|
|
Branch: cand.Branch,
|
|
RejectReason: "SUBMIT_STATE_DENIED",
|
|
NaturalReply: fmt.Sprintf("当前文档处于「%s」阶段,仅编制中(drafting)可送审。", state),
|
|
UpdatedContext: userCtx,
|
|
}, nil
|
|
}
|
|
title := displayDocTitle(doc)
|
|
card, exp := buildConfirmCard(
|
|
"受控文档送审确认",
|
|
doc.DocID, title, "",
|
|
"编制完成,提交审核并锁定正文",
|
|
"送审后文档进入审核锁定:非审核职责人不可改正文;锁定期间补充将进入积压台账,待退回编制后再消化。",
|
|
"confirm_submit_review", "确认送审",
|
|
map[string]string{"task_key": doc.TaskKey},
|
|
)
|
|
userCtx.applyCard(card)
|
|
userCtx.applyPending(exp)
|
|
reply := fmt.Sprintf("已为《%s》起草送审确认单。送审将锁定正文并通知审核人,请确认无误后点击确认或回复「确认」。", title)
|
|
return &CoordinationResult{
|
|
Status: StatusAccepted,
|
|
Intent: cand.Intent,
|
|
Branch: cand.Branch,
|
|
NaturalReply: reply,
|
|
InteractiveCard: card,
|
|
NextExpectation: exp,
|
|
UpdatedContext: userCtx,
|
|
}, nil
|
|
}
|
|
|
|
// handleDocGovProposalDecide 处理智能体修改建议与提案裁决,以及驳回/终审批准确认执行
|
|
func (e *CoordinatorEngine) handleDocGovProposalDecide(ctx context.Context, cand *intent.CandidateIntent, userCtx *UserProjectContext) (*CoordinationResult, error) {
|
|
targetAction := cand.FilledSlots["target_action"]
|
|
if targetAction == "" {
|
|
targetAction = cand.FilledSlots["action"]
|
|
}
|
|
if targetAction == "" && cand.MatchedOption != nil {
|
|
targetAction = cand.MatchedOption.OptionID
|
|
}
|
|
|
|
docName := cand.FilledSlots[intent.SlotTargetDoc]
|
|
if docName == "" && userCtx.InteractiveCard != nil && userCtx.InteractiveCard.TargetDocID != "" {
|
|
docName = userCtx.InteractiveCard.TargetDocID
|
|
}
|
|
if docName == "" {
|
|
docName = "当前受控文档"
|
|
}
|
|
|
|
if targetAction == "confirm_reject" {
|
|
reason := cand.FilledSlots[intent.SlotRejectReason]
|
|
if reason == "" && userCtx.InteractiveCard != nil {
|
|
reason = userCtx.InteractiveCard.ProposedContent
|
|
}
|
|
|
|
if userCtx.InteractiveCard != nil {
|
|
userCtx.dismissCard("resolved")
|
|
}
|
|
userCtx.applyPending(nil)
|
|
|
|
rollback := vocabulary.StateDrafting
|
|
if userCtx.InteractiveCard != nil && strings.Contains(userCtx.InteractiveCard.ImpactRisk, "reviewing") {
|
|
rollback = vocabulary.StateDrafting
|
|
}
|
|
|
|
return &CoordinationResult{
|
|
Status: StatusAccepted,
|
|
Intent: cand.Intent,
|
|
Branch: cand.Branch,
|
|
NaturalReply: fmt.Sprintf("正在处理您对《%s》的驳回确认…", docName),
|
|
UpdatedContext: userCtx,
|
|
ClearExpectation: true,
|
|
PendingAction: &PendingAction{
|
|
Kind: "docgov.reject",
|
|
DocID: docName,
|
|
Reason: reason,
|
|
Rollback: rollback,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
if targetAction == "confirm_final_approve" {
|
|
if userCtx.InteractiveCard != nil {
|
|
userCtx.dismissCard("resolved")
|
|
}
|
|
userCtx.applyPending(nil)
|
|
|
|
return &CoordinationResult{
|
|
Status: StatusAccepted,
|
|
Intent: cand.Intent,
|
|
Branch: cand.Branch,
|
|
NaturalReply: fmt.Sprintf("正在处理您对《%s》的终审批准…", docName),
|
|
UpdatedContext: userCtx,
|
|
ClearExpectation: true,
|
|
PendingAction: &PendingAction{
|
|
Kind: "docgov.approver_approve",
|
|
DocID: docName,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
if targetAction == "confirm_claim" {
|
|
payload := mergeDecidePayload(cand, userCtx)
|
|
if userCtx.InteractiveCard != nil {
|
|
userCtx.dismissCard("resolved")
|
|
}
|
|
userCtx.applyPending(nil)
|
|
return &CoordinationResult{
|
|
Status: StatusAccepted,
|
|
Intent: cand.Intent,
|
|
Branch: cand.Branch,
|
|
NaturalReply: fmt.Sprintf("正在处理《%s》的认领…", docName),
|
|
UpdatedContext: userCtx,
|
|
ClearExpectation: true,
|
|
PendingAction: &PendingAction{
|
|
Kind: "docgov.claim",
|
|
DocID: docName,
|
|
TaskKey: payload["task_key"],
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
if targetAction == "confirm_submit_review" {
|
|
payload := mergeDecidePayload(cand, userCtx)
|
|
if userCtx.InteractiveCard != nil {
|
|
userCtx.dismissCard("resolved")
|
|
}
|
|
userCtx.applyPending(nil)
|
|
taskKey := payload["task_key"]
|
|
return &CoordinationResult{
|
|
Status: StatusAccepted, Intent: cand.Intent, Branch: cand.Branch,
|
|
NaturalReply: fmt.Sprintf("正在处理《%s》的送审…", docName),
|
|
UpdatedContext: userCtx,
|
|
ClearExpectation: true,
|
|
PendingAction: &PendingAction{Kind: "docgov.submit_review", DocID: docName, TaskKey: taskKey},
|
|
}, nil
|
|
}
|
|
|
|
if targetAction == "confirm_workflow_run" {
|
|
payload := mergeDecidePayload(cand, userCtx)
|
|
if userCtx.InteractiveCard != nil {
|
|
userCtx.dismissCard("resolved")
|
|
}
|
|
userCtx.applyPending(nil)
|
|
wfKey := payload[intent.SlotTargetWorkflow]
|
|
if wfKey == "" {
|
|
wfKey = payload["workflow_key"]
|
|
}
|
|
if wfKey == "" && userCtx != nil {
|
|
wfKey = userCtx.Project.ActiveWorkflowKey
|
|
}
|
|
if wfKey == "" {
|
|
wfKey = "main"
|
|
}
|
|
return &CoordinationResult{
|
|
Status: StatusAccepted,
|
|
Intent: cand.Intent,
|
|
Branch: cand.Branch,
|
|
NaturalReply: fmt.Sprintf("正在启动工作流「%s」…", workflowDisplayName(wfKey)),
|
|
UpdatedContext: userCtx,
|
|
ClearExpectation: true,
|
|
PendingAction: &PendingAction{
|
|
Kind: "workflow.run",
|
|
WorkflowKey: wfKey,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
if targetAction == "confirm_comment_add" {
|
|
payload := mergeDecidePayload(cand, userCtx)
|
|
if userCtx.InteractiveCard != nil {
|
|
userCtx.dismissCard("resolved")
|
|
}
|
|
userCtx.applyPending(nil)
|
|
return &CoordinationResult{
|
|
Status: StatusAccepted, Intent: cand.Intent, Branch: cand.Branch,
|
|
NaturalReply: fmt.Sprintf("正在为《%s》添加批注…", docName),
|
|
UpdatedContext: userCtx, ClearExpectation: true,
|
|
PendingAction: &PendingAction{
|
|
Kind: "docgov.comment_add",
|
|
DocID: docName,
|
|
Role: payload["role"],
|
|
Section: payload["target_section"],
|
|
Comment: payload["comment_content"],
|
|
Reason: payload["comment_type"],
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
if targetAction == "confirm_comment_resolve" {
|
|
payload := mergeDecidePayload(cand, userCtx)
|
|
if userCtx.InteractiveCard != nil {
|
|
userCtx.dismissCard("resolved")
|
|
}
|
|
userCtx.applyPending(nil)
|
|
return &CoordinationResult{
|
|
Status: StatusAccepted, Intent: cand.Intent, Branch: cand.Branch,
|
|
NaturalReply: fmt.Sprintf("正在解决《%s》的批注…", docName),
|
|
UpdatedContext: userCtx, ClearExpectation: true,
|
|
PendingAction: &PendingAction{
|
|
Kind: "docgov.comment_resolve",
|
|
DocID: docName,
|
|
Role: payload["role"],
|
|
Section: payload["target_section"],
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
if targetAction == "confirm_reopen_request" {
|
|
payload := mergeDecidePayload(cand, userCtx)
|
|
if userCtx.InteractiveCard != nil {
|
|
userCtx.dismissCard("resolved")
|
|
}
|
|
userCtx.applyPending(nil)
|
|
return &CoordinationResult{
|
|
Status: StatusAccepted, Intent: cand.Intent, Branch: cand.Branch,
|
|
NaturalReply: fmt.Sprintf("正在提交《%s》的重开申请…", docName),
|
|
UpdatedContext: userCtx, ClearExpectation: true,
|
|
PendingAction: &PendingAction{
|
|
Kind: "docgov.reopen_request",
|
|
DocID: docName,
|
|
TaskKey: payload["task_key"],
|
|
Reason: payload["reopen_reason"],
|
|
Role: payload["role"],
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
if targetAction == "confirm_reopen_approve" {
|
|
payload := mergeDecidePayload(cand, userCtx)
|
|
if userCtx.InteractiveCard != nil {
|
|
userCtx.dismissCard("resolved")
|
|
}
|
|
userCtx.applyPending(nil)
|
|
reqID, _ := strconv.ParseInt(payload["request_id"], 10, 64)
|
|
return &CoordinationResult{
|
|
Status: StatusAccepted, Intent: cand.Intent, Branch: cand.Branch,
|
|
NaturalReply: "正在批准重开申请…",
|
|
UpdatedContext: userCtx, ClearExpectation: true,
|
|
PendingAction: &PendingAction{
|
|
Kind: "docgov.reopen_approve",
|
|
DocID: docName,
|
|
RequestID: reqID,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
if targetAction == "cancel" {
|
|
if userCtx.InteractiveCard != nil {
|
|
userCtx.dismissCard("cancelled")
|
|
}
|
|
userCtx.applyPending(nil)
|
|
|
|
return &CoordinationResult{
|
|
Status: StatusAccepted,
|
|
Intent: cand.Intent,
|
|
Branch: cand.Branch,
|
|
NaturalReply: fmt.Sprintf("已取消针对《%s》的操作,文档状态保持不变。", docName),
|
|
UpdatedContext: userCtx,
|
|
ClearExpectation: true,
|
|
}, nil
|
|
}
|
|
|
|
// 默认分支:禁止假成功——缺确认动作时诚实澄清
|
|
clarify := "请明确您要执行的文档操作(例如确认驳回、确认终审批准、确认认领或确认送审)。未确认前不会修改文档状态。"
|
|
return &CoordinationResult{
|
|
Status: StatusClarificationRequired,
|
|
Intent: cand.Intent,
|
|
Branch: cand.Branch,
|
|
ClarificationPrompt: clarify,
|
|
NaturalReply: clarify,
|
|
UpdatedContext: userCtx,
|
|
}, nil
|
|
}
|
|
|
|
func mergeDecidePayload(cand *intent.CandidateIntent, userCtx *UserProjectContext) map[string]string {
|
|
out := map[string]string{}
|
|
if userCtx != nil && userCtx.InteractiveCard != nil {
|
|
for _, a := range userCtx.InteractiveCard.Actions {
|
|
if a.IsAffirmativeDefault && a.Payload != nil {
|
|
for k, v := range a.Payload {
|
|
out[k] = v
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if userCtx != nil && userCtx.PendingExpectation != nil && userCtx.PendingExpectation.Payload != nil {
|
|
for k, v := range userCtx.PendingExpectation.Payload {
|
|
out[k] = v
|
|
}
|
|
}
|
|
if cand != nil && cand.FilledSlots != nil {
|
|
for k, v := range cand.FilledSlots {
|
|
if v != "" {
|
|
out[k] = v
|
|
}
|
|
}
|
|
}
|
|
return out
|
|
}
|