Wire intent→coordinator DocResolver confirm cards→action_executor kinds for comment, reopen, pending-supplements query, and real submit-review execution.
102 lines
3.4 KiB
Go
102 lines
3.4 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log/slog"
|
|
"time"
|
|
|
|
"chat-agent-system/internal/app/docnotify"
|
|
"chat-agent-system/internal/docgov"
|
|
"chat-agent-system/internal/intent"
|
|
"chat-agent-system/internal/notification"
|
|
"chat-agent-system/internal/supplementimpact"
|
|
)
|
|
|
|
func (r *Runtime) wireSupplementImpact() {
|
|
if r == nil || r.Pool == nil || r.Gov == nil || r.Trace == nil {
|
|
return
|
|
}
|
|
if r.SupplementImpact != nil {
|
|
return
|
|
}
|
|
svc := &supplementimpact.Service{
|
|
Store: supplementimpact.NewPGStore(r.Pool),
|
|
Docs: r.Gov,
|
|
Analyze: r.Trace,
|
|
Notify: r.Notify,
|
|
Propose: r.Gov,
|
|
Pending: r.Gov,
|
|
}
|
|
r.SupplementImpact = svc
|
|
if r.Biz != nil && r.Notify != nil {
|
|
r.Biz.SetDigestNotifier(r.Notify)
|
|
}
|
|
if r.Gov != nil && r.Notify != nil {
|
|
r.Gov.SetPendingDigestNotify(func(ctx context.Context, doc *docgov.DocumentRecord, pending []docgov.PendingSupplement) {
|
|
if doc == nil || doc.AuthorID <= 0 || len(pending) == 0 {
|
|
return
|
|
}
|
|
summary := pending[0].SourceSummary
|
|
if summary == "" {
|
|
summary = fmt.Sprintf("%d 条积压补充", len(pending))
|
|
}
|
|
_ = r.Notify.UpsertDocumentDigest(ctx, notification.UpsertDigestParams{
|
|
UserID: doc.AuthorID, ProjectID: doc.ProjectID,
|
|
DigestKind: notification.DigestKindPendingSupplements,
|
|
DocID: doc.DocID, DocTitle: doc.Title,
|
|
Severity: notification.SeverityActionRequired,
|
|
Summary: summary,
|
|
})
|
|
})
|
|
r.Gov.SetLifecycleNotify(func(ctx context.Context, action string, doc *docgov.DocumentRecord, actorID int64, actorName, detail string) {
|
|
docnotify.EmitLifecycle(ctx, docnotify.EmitLifecycleParams{
|
|
Notify: r.Notify, Digest: r.Notify, Chat: r.Ctx,
|
|
Action: docnotify.LifecycleAction(action), Doc: doc,
|
|
ActorID: actorID, ActorName: actorName, Detail: detail,
|
|
})
|
|
})
|
|
}
|
|
if r.Engine != nil {
|
|
r.Engine.SetOnSupplement(func(ctx context.Context, projectID, userID int64, sessionID, summary, markdown string) {
|
|
b, err := svc.CreateFromSupplement(ctx, projectID, userID, sessionID, summary, markdown)
|
|
if err != nil {
|
|
slog.Warn("supplementimpact sync create failed", "error", err, "project_id", projectID)
|
|
return
|
|
}
|
|
r.Engine.SetPendingSupplementCard(batchToCard(b))
|
|
})
|
|
}
|
|
if r.mutateOwner != nil {
|
|
r.mutateOwner.OnSupplement = func(ctx context.Context, projectID, userID int64, sessionID, summary, markdown string) {
|
|
supplementimpact.TriggerAsync(svc, projectID, userID, sessionID, summary, markdown)
|
|
}
|
|
}
|
|
}
|
|
|
|
func batchToCard(b *supplementimpact.Batch) *intent.InteractiveCardPayload {
|
|
if b == nil {
|
|
return nil
|
|
}
|
|
title, summary := supplementimpact.BuildConfirmCardMeta(b)
|
|
actions := []intent.CardAction{
|
|
{ActionID: "confirm_supplement_impact", Label: "确认更新所选文档", Style: "primary", IsAffirmativeDefault: true},
|
|
{ActionID: "cancel_supplement_impact", Label: "取消", Style: "subtle"},
|
|
}
|
|
body := summary + "\n" + fmt.Sprintf("batch_id=%s\nnonce=%s\nrevision=%d\ncoverage=%s", b.BatchID, b.DecisionNonce, b.Revision, b.CoverageStatus)
|
|
for _, it := range b.Items {
|
|
body += fmt.Sprintf("\n- %s | claimed=%v busy=%v locked=%v | %s", it.DocID, it.AuthorClaimedSnapshot, it.BusySnapshot, it.LockedSnapshot, it.Title)
|
|
}
|
|
return &intent.InteractiveCardPayload{
|
|
CardID: b.BatchID,
|
|
CardType: "SUPPLEMENT_IMPACT_CONFIRM",
|
|
Title: title,
|
|
ProposedContent: body,
|
|
ImpactRisk: b.CoverageStatus,
|
|
CurrentStatus: "active",
|
|
TTLTurns: 8,
|
|
CreatedAt: time.Now().UTC(),
|
|
Actions: actions,
|
|
}
|
|
}
|