后端: 1. 日程暂存接口——前端拖拽调整后保存到 Redis 快照 - api/agent.go:新增 SaveScheduleState handler,解析绝对时间格式请求体,3 秒超时保护 - routers/routers.go:注册 POST /schedule-state - model/agent.go:新增 SaveScheduleStatePlacedItem / SaveScheduleStateRequest 结构体 - respond/respond.go:新增 5 个排程状态错误码(40058~40062) - 新增 service/agentsvc/agent_schedule_state.go:Load 快照 → ApplyPlacedItems → Save 回 Redis,校验归属 - 新增 newAgent/conv/schedule_state_apply.go:ApplyPlacedItems 绝对坐标→相对 day_index 转换,去重/坐标/嵌入关系校验 2. SchedulePersistor 持久化层全面下线 - 删除 newAgent/conv/schedule_persist.go(280 行,DiffScheduleState → applyChange → 事务写库整条链路) - model/state_store.go:移除 SchedulePersistor 接口 - model/graph_run_state.go / node/execute.go / node/agent_nodes.go / service/agent.go / service/agent_newagent.go / cmd/start.go:移除 SchedulePersistor 字段、参数、注入六处 3. schedule_completed 事件推送——deliver 节点排程完毕信号 - model/common_state.go:新增 HasScheduleChanges 标记,ResetForNextRun 清理 - node/execute.go / node/rough_build.go:写工具和粗排成功后置 HasScheduleChanges=true - node/deliver.go:IsCompleted && HasScheduleChanges 时调用 EmitScheduleCompleted - stream/emitter.go:新增 EmitScheduleCompleted 方法 - stream/openai.go:新增 StreamExtraKindScheduleCompleted + NewScheduleCompletedExtra 4. 预览接口补全 task_class_id - model/agent.go:GetSchedulePlanPreviewResponse 新增 TaskClassIDs - model/schedule.go:HybridScheduleEntry 新增 TaskClassID - conv/schedule_preview.go / service/agent_schedule_preview.go / service/schedule.go:三处透传填充 前端: 5. 排程完毕卡片 + 精排弹窗集成 - 新增 api/schedule_agent.ts:getSchedulePreview / saveScheduleState / applyBatchIntoSchedule - types/dashboard.ts:新增 HybridScheduleEntry / SchedulePreviewData / PlacedItem 类型 - components/dashboard/AssistantPanel.vue:监听 schedule_completed 事件异步拉取排程渲染卡片,集成 ScheduleResultCard + ScheduleFineTuneModal;confirm 交互从文本消息改为 resume 协议(approve/reject/cancel) 6. ToolTracePrototypeView 原型页新增日程小卡片 + 拖拽编排弹窗演示 7. DashboardView import 区域尺寸微调
123 lines
3.3 KiB
Go
123 lines
3.3 KiB
Go
package newagentconv
|
||
|
||
import (
|
||
"fmt"
|
||
"time"
|
||
|
||
"github.com/LoveLosita/smartflow/backend/model"
|
||
schedule "github.com/LoveLosita/smartflow/backend/newAgent/tools/schedule"
|
||
)
|
||
|
||
// ScheduleStateToPreview 将 newAgent 的 ScheduleState 转换为前端预览缓存格式。
|
||
//
|
||
// 职责边界:
|
||
// 1. 只做数据格式转换,不做业务逻辑;
|
||
// 2. 将每个 ScheduleTask 的每个 TaskSlot 转为一条 HybridScheduleEntry;
|
||
// 3. Day → (Week, DayOfWeek) 通过 ScheduleState.DayToWeekDay 转换;
|
||
// 4. 转换失败的 slot(day_index 无效)静默跳过。
|
||
func ScheduleStateToPreview(
|
||
state *schedule.ScheduleState,
|
||
userID int,
|
||
conversationID string,
|
||
taskClassIDs []int,
|
||
summary string,
|
||
) *model.SchedulePlanPreviewCache {
|
||
if state == nil {
|
||
return nil
|
||
}
|
||
|
||
entries := make([]model.HybridScheduleEntry, 0, len(state.Tasks))
|
||
for i := range state.Tasks {
|
||
t := &state.Tasks[i]
|
||
// 待安排且无位置的任务不生成 entry。
|
||
if schedule.IsPendingTask(*t) {
|
||
continue
|
||
}
|
||
|
||
for _, slot := range t.Slots {
|
||
week, dayOfWeek, ok := state.DayToWeekDay(slot.Day)
|
||
if !ok {
|
||
continue
|
||
}
|
||
|
||
entry := model.HybridScheduleEntry{
|
||
Week: week,
|
||
DayOfWeek: dayOfWeek,
|
||
SectionFrom: slot.SlotStart,
|
||
SectionTo: slot.SlotEnd,
|
||
Name: t.Name,
|
||
}
|
||
|
||
// Type 映射。
|
||
if t.Source == "event" {
|
||
if t.EventType != "" {
|
||
entry.Type = t.EventType
|
||
} else {
|
||
entry.Type = "course"
|
||
}
|
||
} else {
|
||
entry.Type = "task"
|
||
}
|
||
|
||
// Status 映射:existing 不变,suggested / 兼容建议态统一输出为 suggested。
|
||
if shouldMarkSuggestedInPreview(*t) {
|
||
entry.Status = "suggested"
|
||
} else {
|
||
entry.Status = "existing"
|
||
}
|
||
|
||
// ID 映射。
|
||
if t.Source == "event" {
|
||
entry.EventID = t.SourceID
|
||
} else {
|
||
entry.TaskItemID = t.SourceID
|
||
entry.TaskClassID = t.TaskClassID
|
||
}
|
||
|
||
// 嵌入与阻塞语义。
|
||
entry.CanBeEmbedded = t.CanEmbed
|
||
if t.Source == "event" && t.CanEmbed && t.EmbeddedBy == nil {
|
||
// 可嵌入且当前无嵌入任务 → 不阻塞 suggested 占位。
|
||
entry.BlockForSuggested = false
|
||
} else {
|
||
entry.BlockForSuggested = true
|
||
}
|
||
|
||
entries = append(entries, entry)
|
||
}
|
||
}
|
||
|
||
// 生成摘要(若调用方未提供)。
|
||
if summary == "" {
|
||
existingCount := 0
|
||
suggestedCount := 0
|
||
for _, e := range entries {
|
||
if e.Status == "existing" {
|
||
existingCount++
|
||
} else {
|
||
suggestedCount++
|
||
}
|
||
}
|
||
summary = fmt.Sprintf("共 %d 个日程条目,其中已确定 %d 个,新安排 %d 个。", len(entries), existingCount, suggestedCount)
|
||
}
|
||
|
||
return &model.SchedulePlanPreviewCache{
|
||
UserID: userID,
|
||
ConversationID: conversationID,
|
||
Summary: summary,
|
||
HybridEntries: entries,
|
||
TaskClassIDs: taskClassIDs,
|
||
GeneratedAt: time.Now(),
|
||
}
|
||
}
|
||
|
||
// shouldMarkSuggestedInPreview 判断某条 ScheduleTask 在预览层是否应标记为 suggested。
|
||
//
|
||
// 规则说明:
|
||
// 1. 新语义下,显式 suggested 直接输出为建议态;
|
||
// 2. 兼容旧快照:pending+Slots、existing+Duration>0 的 task_item 也继续按 suggested 输出;
|
||
// 3. 这样前端预览口径可以在迁移期保持稳定,不会因为状态枚举切换而抖动。
|
||
func shouldMarkSuggestedInPreview(t schedule.ScheduleTask) bool {
|
||
return schedule.IsSuggestedTask(t)
|
||
}
|