Version: 0.9.62.dev.260502

后端:
1. 主动调度补齐 `unfinished_feedback` 定位闭环——用户补充信息先在滚动窗口内定位到可校验的日程块,定位失败则继续 ask_user,不再硬猜 target_id 或直接跑 graph。
2. 聊天占管重跑链路加并发保护——`waiting_user_reply -> rerunning` 改为 DB CAS 抢占,重复补充只返回可见等待提示,避免并发生成多份 preview。
3. rerun 结果回写继续收口——新 preview_id 同步回 trigger 审计指针,session 只在拿到新 preview 时更新当前预览,ready_preview 后清空追问状态并释放回普通聊天。
4. 主动调度事件校验放宽 unfinished_feedback 的空 target 场景,允许先触发、后定位,再进入 graph + preview 主链路。
This commit is contained in:
Losita
2026-05-02 12:41:50 +08:00
parent a3eaa9b2c2
commit ba23ebd201
12 changed files with 891 additions and 103 deletions

View File

@@ -192,6 +192,44 @@ func (d *ActiveScheduleSessionDAO) UpdateActiveScheduleSessionFieldsBySessionID(
Updates(normalizedUpdates).Error
}
// TryTransitionActiveScheduleSessionStatusBySessionID 按 session_id 原子切换主动调度会话状态。
//
// 职责边界:
// 1. 只负责“当前状态仍为 fromStatus 时才切到 toStatus”的轻量 CAS不写 state_json 和 preview_id
// 2. 返回 true 表示本次调用抢到了状态推进权,可以继续执行后续 rerun
// 3. 返回 false 表示已有其他请求先推进了状态,调用方应降级为占管提示,避免重复生成 preview。
func (d *ActiveScheduleSessionDAO) TryTransitionActiveScheduleSessionStatusBySessionID(ctx context.Context, sessionID string, fromStatus string, toStatus string) (bool, error) {
if err := d.ensureDB(); err != nil {
return false, err
}
normalizedSessionID := strings.TrimSpace(sessionID)
if normalizedSessionID == "" {
return false, errors.New("session_id is empty")
}
normalizedFrom, err := normalizeActiveScheduleSessionStatus(fromStatus)
if err != nil {
return false, fmt.Errorf("invalid active schedule session from status: %w", err)
}
normalizedTo, err := normalizeActiveScheduleSessionStatus(toStatus)
if err != nil {
return false, fmt.Errorf("invalid active schedule session to status: %w", err)
}
result := d.db.WithContext(ctx).
Model(&model.ActiveScheduleSession{}).
Where("session_id = ? AND status = ?", normalizedSessionID, normalizedFrom).
Updates(map[string]any{
"status": normalizedTo,
"updated_at": time.Now(),
})
if result.Error != nil {
return false, result.Error
}
return result.RowsAffected > 0, nil
}
// UpdateActiveScheduleSessionFieldsByConversationID 按 user_id + conversation_id 更新最新记录的局部字段。
//
// 步骤化说明: