namespace MAF1.Decisions; /// /// CLI 版确认框。回车 / 1 = 结束查询;2 = 再输入城市;也可以直接打「成都」。 /// ReadLine 和超时赛跑:到期返回 TimedOut,走默认方案。 /// MAF1 工作流 CLI 与 MAF1.Route 共用。 /// public static class ConsoleDecisionPrompt { public static async Task WaitAsync(DecisionRequest request) { Console.WriteLine(); Console.WriteLine("[需要确认]"); Console.WriteLine(request.Prompt); if (!string.IsNullOrWhiteSpace(request.Reason)) { Console.WriteLine($"原因:{request.Reason}"); } Console.WriteLine($"未及时确认将使用默认方案。等待 {request.TimeoutSeconds} 秒。"); for (int i = 0; i < request.Options.Count; i++) { DecisionOption option = request.Options[i]; string mark = option.IsDefault ? " ← 默认" : ""; Console.WriteLine($" {i + 1}) {option.Label}{mark}"); if (!string.IsNullOrWhiteSpace(option.Description)) { Console.WriteLine($" {option.Description}"); } } Console.Write("输入序号,或直接回车采用默认:"); string? line = await ReadLineOrTimeoutAsync(request.Deadline); if (line is null) { Console.WriteLine(); Console.WriteLine("未及时确认,采用默认方案:结束查询。"); return new DecisionAnswer { OptionId = request.DefaultOptionId, TimedOut = true }; } line = line.Trim(); if (line.Length == 0 || line is "1" || line.Equals("stop", StringComparison.OrdinalIgnoreCase)) { return new DecisionAnswer { OptionId = DecisionOptionIds.Stop }; } if (line is "2" || line.Equals(DecisionOptionIds.QueryCities, StringComparison.OrdinalIgnoreCase)) { Console.Write("请输入城市名(例如 成都、北京):"); string? cities = await ReadLineOrTimeoutAsync(request.Deadline); if (cities is null) { Console.WriteLine(); Console.WriteLine("未及时输入城市,采用默认方案:结束查询。"); return new DecisionAnswer { OptionId = DecisionOptionIds.Stop, TimedOut = true }; } if (EmptyCityDecision.ParseCities(cities).Count == 0) { Console.WriteLine("没有有效城市名,采用默认方案:结束查询。"); return new DecisionAnswer { OptionId = DecisionOptionIds.Stop }; } return new DecisionAnswer { OptionId = DecisionOptionIds.QueryCities, Text = cities }; } if (EmptyCityDecision.ParseCities(line).Count > 0) { return new DecisionAnswer { OptionId = DecisionOptionIds.QueryCities, Text = line }; } Console.WriteLine("无法识别输入,采用默认方案:结束查询。"); return new DecisionAnswer { OptionId = DecisionOptionIds.Stop }; } /// 超时返回 null。注意:超时后后台的 ReadLine 仍可能阻塞,学习项目可接受。 private static async Task ReadLineOrTimeoutAsync(DateTimeOffset deadline) { TimeSpan remain = deadline - DateTimeOffset.UtcNow; if (remain <= TimeSpan.Zero) { return null; } Task read = Task.Run(() => Console.ReadLine()); Task delay = Task.Delay(remain); Task finished = await Task.WhenAny(read, delay); if (finished == delay) { return null; } return await read; } }