package core // TaskSummaryView 是 U1 写入 ActivityCard.Body 的权威语义草图。 // 完整 TaskSnapshot(findings[]、revision、streamScope)留待 U2;本类型只冻结 // 运行中/终态的精炼骨架字段与 Body KV 键名,供 Backend 生成、旧客户端回退阅读。 type TaskSummaryView struct { Title string // 业务动宾短标题(通常已在 ActivityCard.Title) Focus string // 正在做什么,≤2 行 FindingsNote string // U1:无成果时诚实占位;结构化 findings 属 U3 Remaining string NextStep string OutcomeLine string // 完成后的结论一行;运行中可空 } // Body KV 键名(U1 冻结;用户默认卡身可见,勿改成内部材料键)。 const ( TaskSummaryKVStatus = "状态" TaskSummaryKVFocus = "正在做" TaskSummaryKVFindings = "当前发现" TaskSummaryKVRemaining = "剩余" TaskSummaryKVNextStep = "下一步" ) // 状态行用户文案(与执行态映射;排队/进行中/完成/失败)。 const ( TaskSummaryStatusQueued = "排队中" TaskSummaryStatusRunning = "进行中" TaskSummaryStatusDone = "已完成" TaskSummaryStatusFailed = "执行失败" ) // 无阶段成果时的诚实占位(R03-a);不得编造业务发现。 const TaskSummaryNoFindingsYet = "尚无阶段结论,正在读取材料" // RunningBody 生成运行中(含排队)进度卡 Body:Markdown 占位 + 固定骨架 KV。 // extraKV(材料键等)保留在同一 UIKV 节点,供系统去重,不充当用户进度正文。 func (v TaskSummaryView) RunningBody(statusLabel string, extraKV [][2]string) []UINode { focus := stringsOr(v.Focus, "处理已登记工作") findings := stringsOr(v.FindingsNote, TaskSummaryNoFindingsYet) remaining := stringsOr(v.Remaining, "待任务进展后更新") next := stringsOr(v.NextStep, "继续处理已登记工作") if statusLabel == "" { statusLabel = TaskSummaryStatusRunning } kv := append(append([][2]string(nil), extraKV...), [2]string{TaskSummaryKVStatus, statusLabel}, [2]string{TaskSummaryKVFocus, focus}, [2]string{TaskSummaryKVFindings, findings}, [2]string{TaskSummaryKVRemaining, remaining}, [2]string{TaskSummaryKVNextStep, next}, ) return []UINode{ {Type: UIMarkdown, Markdown: findings}, {Type: UIKV, KV: kv}, } } // FinishedBody 生成终态卡 Body:结论 Markdown + 骨架 KV(状态为已完成或执行失败)。 func (v TaskSummaryView) FinishedBody(statusLabel string, extraKV [][2]string) []UINode { outcome := stringsOr(v.OutcomeLine, "(无结果摘要)") findings := stringsOr(v.FindingsNote, outcome) if statusLabel == "" { statusLabel = TaskSummaryStatusDone } kv := append(append([][2]string(nil), extraKV...), [2]string{TaskSummaryKVStatus, statusLabel}, [2]string{TaskSummaryKVFocus, stringsOr(v.Focus, "")}, [2]string{TaskSummaryKVFindings, findings}, [2]string{TaskSummaryKVRemaining, stringsOr(v.Remaining, "")}, [2]string{TaskSummaryKVNextStep, stringsOr(v.NextStep, "")}, ) // 去掉空值骨架行,避免完成卡出现空白「正在做」。 compact := make([][2]string, 0, len(kv)) for _, pair := range kv { if pair[1] == "" && (pair[0] == TaskSummaryKVFocus || pair[0] == TaskSummaryKVRemaining || pair[0] == TaskSummaryKVNextStep) { continue } compact = append(compact, pair) } return []UINode{ {Type: UIMarkdown, Markdown: outcome}, {Type: UIKV, KV: compact}, } } // CardDisplaysFailed 判断卡是否应按失败展示:含 CardFailed,以及旧客户端 // done + danger 兼容映射(U1 出口白名单)。 func CardDisplaysFailed(status CardStatus, tone string) bool { if status == CardFailed { return true } return status == CardDone && tone == "danger" } func stringsOr(s, fallback string) string { if s == "" { return fallback } return s }