Version: 0.9.59.dev.260430

后端:
1. 主动调度预览确认主链路落地——新增主动调度数据模型、DAO 与事件契约;接入 dry-run pipeline 与任务触发的 job upsert/cancel;新增 preview 查询与 confirm API,支持 apply_id 幂等确认并同步写入 task_pool 日程
2. 同步更新主动调度实施文档的阶段状态与验收记录

前端:
3. AssistantPanel 脚本层继续解耦——私有类型迁移到独立类型文件,并抽离会话、工具轨迹、思考摘要、任务表单等纯函数辅助逻辑;保持助手面板模板与样式不变,降低表现层回归风险
This commit is contained in:
LoveLosita
2026-04-30 12:05:15 +08:00
parent 1555042e80
commit e945578fbf
38 changed files with 10267 additions and 580 deletions

View File

@@ -0,0 +1,264 @@
package model
import (
"time"
"gorm.io/gorm"
)
const (
// ActiveScheduleJobStatusPending 表示 job 已创建,等待到达 trigger_at 后扫描。
ActiveScheduleJobStatusPending = "pending"
// ActiveScheduleJobStatusTriggered 表示 job 已生成正式 trigger后续由 trigger 串联状态。
ActiveScheduleJobStatusTriggered = "triggered"
// ActiveScheduleJobStatusCanceled 表示任务已完成或被取消job 不再触发。
ActiveScheduleJobStatusCanceled = "canceled"
// ActiveScheduleJobStatusSkipped 表示扫描时发现已无需主动调度。
ActiveScheduleJobStatusSkipped = "skipped"
// ActiveScheduleJobStatusFailed 表示扫描或触发写入失败,保留错误供重试/排障。
ActiveScheduleJobStatusFailed = "failed"
)
const (
// ActiveScheduleTriggerStatusPending 表示触发信号已持久化,等待 worker 消费。
ActiveScheduleTriggerStatusPending = "pending"
// ActiveScheduleTriggerStatusProcessing 表示 worker 正在处理该触发信号。
ActiveScheduleTriggerStatusProcessing = "processing"
// ActiveScheduleTriggerStatusPreviewGenerated 表示已生成可查询的预览。
ActiveScheduleTriggerStatusPreviewGenerated = "preview_generated"
// ActiveScheduleTriggerStatusSkipped 表示本次触发被判定无需继续处理。
ActiveScheduleTriggerStatusSkipped = "skipped"
// ActiveScheduleTriggerStatusClosed 表示主动观测结论为关闭,不生成预览。
ActiveScheduleTriggerStatusClosed = "closed"
// ActiveScheduleTriggerStatusFailed 表示链路处理失败,可根据错误分类决定是否重试。
ActiveScheduleTriggerStatusFailed = "failed"
// ActiveScheduleTriggerStatusRejected 表示参数或归属校验失败,不进入 pipeline。
ActiveScheduleTriggerStatusRejected = "rejected"
)
const (
// ActiveSchedulePreviewStatusPending 表示预览正在组装,不应展示为可确认。
ActiveSchedulePreviewStatusPending = "pending"
// ActiveSchedulePreviewStatusReady 表示预览可查看、可确认。
ActiveSchedulePreviewStatusReady = "ready"
// ActiveSchedulePreviewStatusApplied 表示用户已确认并成功应用。
ActiveSchedulePreviewStatusApplied = "applied"
// ActiveSchedulePreviewStatusIgnored 表示用户明确忽略本次建议。
ActiveSchedulePreviewStatusIgnored = "ignored"
// ActiveSchedulePreviewStatusExpired 表示预览已过期,不再允许确认。
ActiveSchedulePreviewStatusExpired = "expired"
// ActiveSchedulePreviewStatusFailed 表示预览生成或回写失败。
ActiveSchedulePreviewStatusFailed = "failed"
)
const (
// ActiveScheduleApplyStatusNone 表示尚未发起确认应用。
ActiveScheduleApplyStatusNone = "none"
// ActiveScheduleApplyStatusApplying 表示确认请求正在事务应用中。
ActiveScheduleApplyStatusApplying = "applying"
// ActiveScheduleApplyStatusApplied 表示确认应用成功。
ActiveScheduleApplyStatusApplied = "applied"
// ActiveScheduleApplyStatusFailed 表示应用失败,正式日程不应产生半写状态。
ActiveScheduleApplyStatusFailed = "failed"
// ActiveScheduleApplyStatusRejected 表示请求因过期、幂等冲突等业务规则被拒绝。
ActiveScheduleApplyStatusRejected = "rejected"
// ActiveScheduleApplyStatusExpired 表示预览过期导致不可应用。
ActiveScheduleApplyStatusExpired = "expired"
)
const (
// NotificationRecordStatusPending 表示通知记录已落库,等待投递。
NotificationRecordStatusPending = "pending"
// NotificationRecordStatusSending 表示当前 worker 正在调用 provider。
NotificationRecordStatusSending = "sending"
// NotificationRecordStatusSent 表示 provider 明确返回成功。
NotificationRecordStatusSent = "sent"
// NotificationRecordStatusFailed 表示本次投递失败,但仍可重试。
NotificationRecordStatusFailed = "failed"
// NotificationRecordStatusDead 表示达到重试上限或不可恢复错误。
NotificationRecordStatusDead = "dead"
// NotificationRecordStatusSkipped 表示命中去重或配置关闭,本次不投递。
NotificationRecordStatusSkipped = "skipped"
)
const (
// ActiveScheduleTriggerTypeImportantUrgentTask 是重要且紧急任务到线触发。
ActiveScheduleTriggerTypeImportantUrgentTask = "important_urgent_task"
// ActiveScheduleTriggerTypeUnfinishedFeedback 是用户明确反馈已排任务未完成触发。
ActiveScheduleTriggerTypeUnfinishedFeedback = "unfinished_feedback"
// ActiveScheduleSourceWorkerDueJob 表示后台到期 job 扫描触发。
ActiveScheduleSourceWorkerDueJob = "worker_due_job"
// ActiveScheduleSourceAPITrigger 表示测试/开发 API 正式触发。
ActiveScheduleSourceAPITrigger = "api_trigger"
// ActiveScheduleSourceAPIDryRun 表示测试/开发 API dry-run不应发布正式事件。
ActiveScheduleSourceAPIDryRun = "api_dry_run"
// ActiveScheduleSourceUserFeedback 表示用户反馈入口触发。
ActiveScheduleSourceUserFeedback = "user_feedback"
// ActiveScheduleTargetTypeTaskPool 表示 target_id 指向 tasks.id。
ActiveScheduleTargetTypeTaskPool = "task_pool"
// ActiveScheduleTargetTypeScheduleEvent 表示 target_id 指向 schedule_events.id。
ActiveScheduleTargetTypeScheduleEvent = "schedule_event"
// ActiveScheduleTargetTypeTaskItem 表示 target_id 指向 task_items.id。
ActiveScheduleTargetTypeTaskItem = "task_item"
)
// ActiveScheduleJob 是主动调度 due job 表模型。
//
// 职责边界:
// 1. 负责记录 task 到达 urgency_threshold_at 后是否需要生成主动调度触发;
// 2. 不负责判断 task 当前是否仍重要且紧急,该判断由 worker 扫描时重新读取真实任务状态;
// 3. 不负责发布 outbox 事件,只保存扫描和排障所需状态。
type ActiveScheduleJob struct {
ID string `gorm:"column:id;type:varchar(64);primaryKey"`
UserID int `gorm:"column:user_id;not null;index:idx_active_jobs_user_status_trigger,priority:1;index:idx_active_jobs_task_status,priority:1"`
TaskID int `gorm:"column:task_id;not null;index:idx_active_jobs_task_status,priority:2;comment:对应 tasks.id"`
TriggerType string `gorm:"column:trigger_type;type:varchar(64);not null;default:'important_urgent_task';comment:触发类型"`
Status string `gorm:"column:status;type:varchar(32);not null;default:'pending';index:idx_active_jobs_user_status_trigger,priority:2;index:idx_active_jobs_task_status,priority:3;comment:pending/triggered/canceled/skipped/failed"`
TriggerAt time.Time `gorm:"column:trigger_at;not null;index:idx_active_jobs_user_status_trigger,priority:3;comment:到期触发时间"`
DedupeKey string `gorm:"column:dedupe_key;type:varchar(191);index:idx_active_jobs_dedupe;comment:触发去重窗口键"`
LastTriggerID *string `gorm:"column:last_trigger_id;type:varchar(64);index:idx_active_jobs_last_trigger;comment:最近一次生成的 trigger_id"`
LastErrorCode *string `gorm:"column:last_error_code;type:varchar(64);comment:最近一次扫描错误码"`
LastError *string `gorm:"column:last_error;type:text;comment:最近一次扫描错误详情"`
LastScannedAt *time.Time `gorm:"column:last_scanned_at;comment:最近一次被 worker 扫描时间"`
TraceID string `gorm:"column:trace_id;type:varchar(64);index:idx_active_jobs_trace_id"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime"`
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;index"`
}
func (ActiveScheduleJob) TableName() string { return "active_schedule_jobs" }
// ActiveScheduleTrigger 是主动调度统一触发信号表模型。
//
// 职责边界:
// 1. 负责持久化 worker/API/用户反馈归一后的触发事实;
// 2. 负责串联 trigger -> preview -> notification -> apply 的审计主线;
// 3. 不承载候选生成、LLM 选择或通知投递的业务实现。
type ActiveScheduleTrigger struct {
ID string `gorm:"column:id;type:varchar(64);primaryKey"`
UserID int `gorm:"column:user_id;not null;index:idx_active_triggers_user_created,priority:1"`
TriggerType string `gorm:"column:trigger_type;type:varchar(64);not null;index:idx_active_triggers_dedupe,priority:2"`
Source string `gorm:"column:source;type:varchar(64);not null;comment:worker_due_job/api_trigger/api_dry_run/user_feedback"`
TargetType string `gorm:"column:target_type;type:varchar(64);not null;index:idx_active_triggers_target,priority:1"`
TargetID int `gorm:"column:target_id;not null;index:idx_active_triggers_target,priority:2"`
FeedbackID string `gorm:"column:feedback_id;type:varchar(128);index:idx_active_triggers_feedback;comment:用户反馈来源ID可为空"`
JobID *string `gorm:"column:job_id;type:varchar(64);index:idx_active_triggers_job_id"`
IdempotencyKey string `gorm:"column:idempotency_key;type:varchar(191);index:idx_active_triggers_idempotency;comment:API/用户反馈幂等键"`
DedupeKey string `gorm:"column:dedupe_key;type:varchar(191);index:idx_active_triggers_dedupe,priority:1;comment:触发去重窗口键"`
Status string `gorm:"column:status;type:varchar(32);not null;default:'pending';index:idx_active_triggers_status_updated,priority:1"`
MockNow *time.Time `gorm:"column:mock_now;comment:测试触发模拟时间"`
IsMockTime bool `gorm:"column:is_mock_time;not null;default:false;comment:是否使用模拟时间"`
RequestedAt time.Time `gorm:"column:requested_at;not null;comment:触发请求时间"`
PayloadJSON *string `gorm:"column:payload_json;type:json;comment:触发来源补充信息"`
PreviewID *string `gorm:"column:preview_id;type:varchar(64);index:idx_active_triggers_preview_id"`
LastErrorCode *string `gorm:"column:last_error_code;type:varchar(64);comment:链路错误码"`
LastError *string `gorm:"column:last_error;type:text;comment:链路错误详情"`
ProcessedAt *time.Time `gorm:"column:processed_at;comment:worker 开始处理时间"`
CompletedAt *time.Time `gorm:"column:completed_at;comment:本触发进入终态时间"`
TraceID string `gorm:"column:trace_id;type:varchar(64);index:idx_active_triggers_trace_id"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;index:idx_active_triggers_user_created,priority:2"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime;index:idx_active_triggers_status_updated,priority:2"`
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;index"`
}
func (ActiveScheduleTrigger) TableName() string { return "active_schedule_triggers" }
// ActiveSchedulePreview 是主动调度可确认预览表模型。
//
// 职责边界:
// 1. 负责保存主动调度生成的候选、解释、before/after 摘要与过期时间;
// 2. 负责保存一次确认应用的轻量状态,不新增 apply request 表;
// 3. 不负责正式日程写入,正式写入仍由后续 apply/service port 完成。
type ActiveSchedulePreview struct {
ID string `gorm:"column:preview_id;type:varchar(64);primaryKey;uniqueIndex:uk_active_previews_apply_idempotency,priority:1"`
UserID int `gorm:"column:user_id;not null;index:idx_active_previews_user_created_at,priority:1"`
TriggerID string `gorm:"column:trigger_id;type:varchar(64);not null;index:idx_active_previews_trigger_id"`
TriggerType string `gorm:"column:trigger_type;type:varchar(64);not null"`
TargetType string `gorm:"column:target_type;type:varchar(64);not null"`
TargetID int `gorm:"column:target_id;not null"`
Status string `gorm:"column:status;type:varchar(32);not null;default:'pending';comment:pending/ready/applied/ignored/expired/failed"`
SelectedCandidateID string `gorm:"column:selected_candidate_id;type:varchar(64);comment:LLM 或后端 fallback 选中的候选ID"`
CandidateCount int `gorm:"column:candidate_count;not null;default:0"`
SelectedCandidateJSON *string `gorm:"column:selected_candidate_json;type:json"`
CandidatesJSON *string `gorm:"column:candidates_json;type:json"`
DecisionJSON *string `gorm:"column:decision_json;type:json"`
MetricsJSON *string `gorm:"column:metrics_json;type:json"`
IssuesJSON *string `gorm:"column:issues_json;type:json"`
ContextSummaryJSON *string `gorm:"column:context_summary_json;type:json"`
BeforeSummaryJSON *string `gorm:"column:before_summary_json;type:json"`
PreviewChangesJSON *string `gorm:"column:preview_changes_json;type:json"`
AfterSummaryJSON *string `gorm:"column:after_summary_json;type:json"`
RiskJSON *string `gorm:"column:risk_json;type:json"`
ExplanationText string `gorm:"column:explanation_text;type:text"`
NotificationSummary string `gorm:"column:notification_summary;type:text"`
BaseVersion string `gorm:"column:base_version;type:varchar(128);not null;comment:确认前重校验基准版本"`
ExpiresAt time.Time `gorm:"column:expires_at;not null;index:idx_active_previews_expires_at"`
GeneratedAt time.Time `gorm:"column:generated_at;not null"`
ApplyID *string `gorm:"column:apply_id;type:varchar(64);index:idx_active_previews_apply_id"`
ApplyStatus string `gorm:"column:apply_status;type:varchar(32);not null;default:'none';comment:none/applying/applied/failed/rejected/expired"`
ApplyCandidateID string `gorm:"column:apply_candidate_id;type:varchar(64)"`
ApplyIdempotencyKey string `gorm:"column:apply_idempotency_key;type:varchar(191);uniqueIndex:uk_active_previews_apply_idempotency,priority:2"`
ApplyRequestHash string `gorm:"column:apply_request_hash;type:varchar(128);comment:确认请求体摘要"`
AppliedChangesJSON *string `gorm:"column:applied_changes_json;type:json"`
AppliedEventIDsJSON *string `gorm:"column:applied_event_ids_json;type:json"`
ApplyError *string `gorm:"column:apply_error;type:text"`
AppliedAt *time.Time `gorm:"column:applied_at"`
TraceID string `gorm:"column:trace_id;type:varchar(64);index:idx_active_previews_trace_id"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;index:idx_active_previews_user_created_at,priority:2"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime"`
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;index"`
}
func (ActiveSchedulePreview) TableName() string { return "active_schedule_previews" }
// NotificationRecord 是通知投递记录表模型。
//
// 职责边界:
// 1. 负责记录飞书等通知渠道的幂等、状态流转和 provider 返回;
// 2. 不负责决定是否生成调度预览,也不负责 apply 状态;
// 3. 重试时复用同一条记录,避免短时间重复打扰用户。
type NotificationRecord struct {
ID int64 `gorm:"column:id;primaryKey;autoIncrement"`
Channel string `gorm:"column:channel;type:varchar(32);not null;uniqueIndex:uk_notification_dedupe,priority:1;comment:通知渠道"`
UserID int `gorm:"column:user_id;not null;index:idx_notification_user_created,priority:1"`
TriggerID string `gorm:"column:trigger_id;type:varchar(64);not null;index:idx_notification_trigger"`
PreviewID string `gorm:"column:preview_id;type:varchar(64);not null;index:idx_notification_preview"`
TriggerType string `gorm:"column:trigger_type;type:varchar(64);not null"`
TargetType string `gorm:"column:target_type;type:varchar(64);not null"`
TargetID int `gorm:"column:target_id;not null"`
DedupeKey string `gorm:"column:dedupe_key;type:varchar(191);not null;uniqueIndex:uk_notification_dedupe,priority:2"`
TargetURL string `gorm:"column:target_url;type:varchar(255);not null;comment:站内预览链接"`
SummaryText string `gorm:"column:summary_text;type:text"`
FallbackText string `gorm:"column:fallback_text;type:text"`
FallbackUsed bool `gorm:"column:fallback_used;not null;default:false"`
Status string `gorm:"column:status;type:varchar(32);not null;default:'pending';index:idx_notification_status_retry,priority:1;comment:pending/sending/sent/failed/dead/skipped"`
AttemptCount int `gorm:"column:attempt_count;not null;default:0"`
MaxAttempts int `gorm:"column:max_attempts;not null;default:5"`
NextRetryAt *time.Time `gorm:"column:next_retry_at;index:idx_notification_status_retry,priority:2"`
LastErrorCode *string `gorm:"column:last_error_code;type:varchar(64)"`
LastError *string `gorm:"column:last_error;type:text"`
ProviderMessageID *string `gorm:"column:provider_message_id;type:varchar(128)"`
ProviderRequestJSON *string `gorm:"column:provider_request_json;type:json"`
ProviderResponseJSON *string `gorm:"column:provider_response_json;type:json"`
SentAt *time.Time `gorm:"column:sent_at"`
TraceID string `gorm:"column:trace_id;type:varchar(64);index:idx_notification_trace_id"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime;index:idx_notification_user_created,priority:2"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime"`
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;index"`
}
func (NotificationRecord) TableName() string { return "notification_records" }