A new recheck mark is posted into the actor's existing project session, and a downstream-impact event does the same. Asking about what a change affects still resolves to impact analysis. Co-authored-by: Cursor <cursoragent@cursor.com>
92 lines
3.0 KiB
Go
92 lines
3.0 KiB
Go
package docnotify
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
|
|
"chat-agent-system/internal/ctxmgr"
|
|
"chat-agent-system/internal/events"
|
|
"chat-agent-system/internal/notification"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
)
|
|
|
|
// EventConsumer is the durable notify worker. Closures remain the unique writer until live cutover.
|
|
type EventConsumer struct {
|
|
Notify *notification.Service
|
|
Docs DocumentLookup
|
|
Chat *ctxmgr.ContextManager
|
|
Env string
|
|
}
|
|
|
|
func (c *EventConsumer) HandleLease(ctx context.Context, lease events.Lease, tx pgx.Tx) error {
|
|
if c == nil || c.Notify == nil {
|
|
return events.ErrUnknownConsumer
|
|
}
|
|
principal := events.ServicePrincipal{
|
|
ID: "notify-" + c.Env, Environment: c.Env, Consumer: events.ConsumerNotify,
|
|
Actions: []string{"notify.emit"},
|
|
}
|
|
if err := events.AuthorizePrincipal(principal, lease.Environment, lease.ProjectID, "notify.emit"); err != nil {
|
|
return events.ErrQuarantine
|
|
}
|
|
kind, title, body := notifyCopy(lease.EventName)
|
|
if kind == "" {
|
|
return nil
|
|
}
|
|
docID := lease.Envelope.AggregateID
|
|
var payload map[string]any
|
|
_ = json.Unmarshal(lease.Envelope.Payload, &payload)
|
|
if payload != nil {
|
|
if id, ok := payload["doc_id"].(string); ok && id != "" {
|
|
docID = id
|
|
}
|
|
}
|
|
var recipient int64
|
|
projectID := lease.ProjectID
|
|
if c.Docs != nil && docID != "" {
|
|
doc, err := c.Docs.GetDocument(ctx, docID)
|
|
if err != nil || doc == nil {
|
|
return events.ErrQuarantine
|
|
}
|
|
if doc.ProjectID != lease.ProjectID {
|
|
return events.ErrQuarantine
|
|
}
|
|
recipient = doc.AuthorID
|
|
projectID = doc.ProjectID
|
|
}
|
|
if recipient <= 0 {
|
|
return events.ErrQuarantine
|
|
}
|
|
p := notification.DocumentEventPayload{ProjectID: projectID, DocID: docID}
|
|
if err := c.Notify.EmitVisibleDocumentEvent(ctx, tx, lease.EventID, kind, title, body, recipient, p); err != nil {
|
|
return err
|
|
}
|
|
if lease.EventName == events.EventDownstreamImpact && c.Chat != nil {
|
|
impacted, _ := payload["impacted_doc_id"].(string)
|
|
_ = PostProjectNotice(ctx, c.Chat, projectID, recipient, downstreamChatBody(docID, impacted), nil)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func IntendedNotifyDigest(eventID, kind string, recipient int64) string {
|
|
return events.VisibleOutcomeDigest(eventID, kind, recipient)
|
|
}
|
|
|
|
func notifyCopy(eventName string) (kind, title, body string) {
|
|
switch eventName {
|
|
case events.EventCandidateReady:
|
|
return notification.TypeDocAgentCandidateReady, "智能体变更待确认", "文档有高风险智能体候选变更,请确认后生效。"
|
|
case events.EventVersionCommitted:
|
|
return notification.TypeDocAgentVersionCommitted, "智能体版本已生效", "新的智能体文档版本已提交。"
|
|
case events.EventDownstreamImpact:
|
|
return notification.TypeDocDownstreamImpact, "下游文档受影响", "上游文档变更,请复核关联文档。"
|
|
case events.EventChangeReportDegraded:
|
|
return notification.TypeDocChangeReportDegraded, "变更报告未完整评估", "变更报告降级,受控文档暂不可送审。"
|
|
case events.EventApproved:
|
|
return notification.TypeDocApproved, "文档已批准", "文档已完成终审批准。"
|
|
default:
|
|
return "", "", ""
|
|
}
|
|
}
|