C#实现石头剪刀布游戏:从基础语法到设计模式实战
1. 项目概述与核心价值最近在带新人或者自己回顾基础的时候发现“石头剪刀布”这个小游戏是个绝佳的练手项目。别看它规则简单但用C#完整实现一遍尤其是要处理好玩家与电脑的对战逻辑能串联起从基础语法到面向对象设计再到随机数生成和状态判断的多个核心知识点。很多面试里所谓的“设计模式”考察比如状态机其最直观的入门理解就可以从这个游戏开始。我这次就带大家从头到尾用控制台应用的形式实现一个结构清晰、可扩展性强的石头剪刀布游戏。我们不止于实现功能更会探讨如何写出易于维护和扩展的代码比如未来如果想增加“蜥蜴、史波克”这种扩展规则或者改成图形界面现在的结构是否能轻松应对。这个项目非常适合C#初学者巩固基础也适合有一定经验的开发者思考如何将简单需求设计得更加优雅。我们会从最直白的“if-else”堆叠开始然后逐步重构引入枚举、类设计甚至触摸一下策略模式的思想让你看到代码是如何一步步“进化”的。整个过程你会清晰地看到变量定义、方法封装、随机数应用、条件判断以及控制台输入输出这些基础技能是如何被有机组合起来的。2. 游戏核心逻辑与初始设计2.1 规则定义与状态枚举任何游戏开始前明确定义规则是第一步。石头剪刀布的规则很简单石头赢剪刀剪刀赢布布赢石头相同则为平局。在代码里我们首先需要定义出“手势”这个核心概念。用整数0、1、2来代表虽然直接但代码可读性极差。这时候C#的枚举enum就派上用场了。public enum HandGesture { Rock, // 石头 Scissors, // 剪刀 Paper // 布 }使用枚举后代码里到处都是HandGesture.Rock、HandGesture.Scissors这样的字眼意图一目了然完全避免了“魔法数字”的出现。接下来我们需要一个裁判它的职责是根据两个手势判断胜负。最直接的实现就是一个方法里面用switch或if-else穷举所有情况。public static class GameRule { public static GameResult Judge(HandGesture player, HandGesture computer) { if (player computer) return GameResult.Draw; if ((player HandGesture.Rock computer HandGesture.Scissors) || (player HandGesture.Scissors computer HandGesture.Paper) || (player HandGesture.Paper computer HandGesture.Rock)) { return GameResult.PlayerWin; } return GameResult.ComputerWin; } } public enum GameResult { PlayerWin, ComputerWin, Draw }注意这里将胜负判断逻辑单独封装在GameRule静态类中是一个很好的实践。它遵循了单一职责原则未来如果规则变化比如加入新的手势我们只需要修改这一个类而不会影响到玩家或电脑类的代码。2.2 玩家与电脑的抽象游戏中有两个参与者玩家和电脑。他们有一些共同行为比如“出拳”GetGesture但具体实现方式截然不同。玩家需要通过控制台输入而电脑则需要随机生成。这立刻让我们想到面向对象中的继承与多态。我们可以设计一个基类Player。public abstract class Player { public string Name { get; protected set; } public abstract HandGesture GetGesture(); }基类Player包含一个名字属性和一个抽象方法GetGesture。这样具体的玩家类和电脑类就可以继承它并实现各自获取手势的逻辑。public class HumanPlayer : Player { public HumanPlayer(string name) { Name name; } public override HandGesture GetGesture() { while (true) { Console.WriteLine(${Name}请出拳 (0-石头, 1-剪刀, 2-布)); string input Console.ReadLine(); if (int.TryParse(input, out int choice) choice 0 choice 2) { return (HandGesture)choice; // 利用枚举和整数的转换 } Console.WriteLine(输入无效请输入0、1或2); } } } public class ComputerPlayer : Player { private readonly Random _random new Random(); public ComputerPlayer() { Name 电脑; } public override HandGesture GetGesture() { // Random.Next(3) 生成 0, 1, 2 之间的随机数 int choice _random.Next(Enum.GetValues(typeof(HandGesture)).Length); return (HandGesture)choice; } }这里有几个关键点输入验证在HumanPlayer.GetGesture()中我们用一个while循环来确保玩家输入的是有效数字并给出了清晰的提示。这是提升用户体验和程序健壮性的基本操作。随机数生成ComputerPlayer使用Random类来生成随机手势。这里_random被定义为只读字段并在构造函数中初始化这是一个好习惯。注意Random的实例化不宜在方法内频繁进行否则在极短时间内连续调用可能导致生成的随机数不够“随机”。枚举转换我们巧妙利用了枚举底层是整型的特性通过(HandGesture)choice进行转换代码非常简洁。3. 游戏主流程与循环结构实现3.1 游戏引擎类的设计有了玩家和规则我们需要一个“导演”来串联整个流程这就是游戏引擎或主控类GameEngine。它的核心是一个游戏循环。public class GameEngine { private readonly HumanPlayer _human; private readonly ComputerPlayer _computer; private int _playerScore; private int _computerScore; private int _drawCount; public GameEngine(string playerName) { _human new HumanPlayer(playerName); _computer new ComputerPlayer(); _playerScore 0; _computerScore 0; _drawCount 0; } public void Start() { Console.WriteLine($ 石头剪刀布游戏开始 ); Console.WriteLine($玩家{_human.Name} vs {_computer.Name}); bool continuePlaying true; while (continuePlaying) { PlayOneRound(); Console.WriteLine($当前比分{_human.Name} {_playerScore} : {_computerScore} {_computer.Name} 平局 {_drawCount}); Console.WriteLine(是否继续(y/n)); continuePlaying Console.ReadLine().ToLower() y; } PrintFinalResult(); } private void PlayOneRound() { // 获取手势 HandGesture playerGesture _human.GetGesture(); HandGesture computerGesture _computer.GetGesture(); Console.WriteLine(${_human.Name} 出了 {playerGesture}); Console.WriteLine(${_computer.Name} 出了 {computerGesture}); // 判定胜负 GameResult result GameRule.Judge(playerGesture, computerGesture); // 更新比分并显示结果 switch (result) { case GameResult.PlayerWin: _playerScore; Console.WriteLine(${_human.Name} 赢了); break; case GameResult.ComputerWin: _computerScore; Console.WriteLine(${_computer.Name} 赢了); break; case GameResult.Draw: _drawCount; Console.WriteLine(平局); break; } Console.WriteLine(); // 空行分隔 } private void PrintFinalResult() { Console.WriteLine(\n 游戏结束 ); Console.WriteLine($最终比分{_human.Name} {_playerScore} : {_computerScore} {_computer.Name}); if (_playerScore _computerScore) Console.WriteLine($恭喜 {_human.Name} 获得最终胜利); else if (_playerScore _computerScore) Console.WriteLine($很遗憾{_computer.Name} 获得了胜利。); else Console.WriteLine(最终双方战平); } }GameEngine类封装了游戏的所有状态比分和行为开始、进行一轮、结束。Start方法中的while循环构成了游戏的主循环每轮结束后询问玩家是否继续。这种结构清晰地将初始化、游戏循环和结束处理分开。3.2 程序入口与启动最后在控制台应用的Main方法中我们只需要创建游戏引擎并启动它。class Program { static void Main(string[] args) { Console.WriteLine(欢迎来到石头剪刀布游戏); Console.Write(请输入你的名字); string playerName Console.ReadLine(); GameEngine game new GameEngine(playerName); game.Start(); Console.WriteLine(按任意键退出...); Console.ReadKey(); } }至此一个功能完整、结构清晰的石头剪刀布游戏就完成了。你可以编译并运行它体验与电脑对战的乐趣。4. 代码优化与设计模式初探上面的实现已经是一个合格的作品。但如果我们从软件工程和可维护性的角度审视还有不少可以优化和深入思考的地方。这部分内容对于希望提升代码质量的开发者尤为重要。4.1 引入“策略模式”优化电脑AI目前的电脑出拳是完全随机的这很公平但也很“笨”。如果我们想让电脑有点“智能”比如根据历史出拳记录来猜测玩家的习惯该怎么办直接修改ComputerPlayer类的GetGesture方法会违反“开闭原则”对扩展开放对修改关闭。这时策略模式Strategy Pattern就非常适合。策略模式定义了一系列算法并将每个算法封装起来使它们可以相互替换。对于电脑玩家来说“出拳策略”就是一个可以替换的算法。我们首先定义一个策略接口public interface IGestureStrategy { HandGesture GetNextGesture(GameHistory history); }GameHistory是一个新类用于记录历史对战数据比如最近10轮双方的手势。然后我们可以实现不同的策略// 随机策略原来的逻辑 public class RandomStrategy : IGestureStrategy { private static readonly Random _rand new Random(); public HandGesture GetNextGesture(GameHistory history) { return (HandGesture)_rand.Next(3); } } // 简单模式策略总是出石头 public class RockOnlyStrategy : IGestureStrategy { public HandGesture GetNextGesture(GameHistory history) HandGesture.Rock; } // 基于历史的策略分析玩家最常出的手势并出能克制它的手势 public class HistoryBasedStrategy : IGestureStrategy { private static readonly Random _rand new Random(); public HandGesture GetNextGesture(GameHistory history) { if (history.Rounds.Count 0) return (HandGesture)_rand.Next(3); // 简化逻辑找出玩家最频繁出的手势 var playerGestures history.Rounds.Select(r r.PlayerGesture); var mostFrequent playerGestures.GroupBy(g g) .OrderByDescending(g g.Count()) .First().Key; // 假设玩家会继续出这个手势我们出能赢它的手势 // 这是一个非常简单的反制逻辑 return mostFrequent switch { HandGesture.Rock HandGesture.Paper, HandGesture.Scissors HandGesture.Rock, HandGesture.Paper HandGesture.Scissors, _ (HandGesture)_rand.Next(3) }; } }然后修改ComputerPlayer类使其接受一个策略接口public class ComputerPlayer : Player { private readonly IGestureStrategy _strategy; private readonly GameHistory _history; public ComputerPlayer(IGestureStrategy strategy, GameHistory history) { Name 电脑; _strategy strategy; _history history; } public override HandGesture GetGesture() { HandGesture gesture _strategy.GetNextGesture(_history); // 电脑出手后也可以选择是否记录到历史这里电脑是后手记录由引擎统一处理更合适 return gesture; } }最后在GameEngine中创建电脑玩家时注入具体的策略// 在GameEngine构造函数中 GameHistory history new GameHistory(); _computer new ComputerPlayer(new HistoryBasedStrategy(), history); // 或者 new RandomStrategy()这样我们无需改动ComputerPlayer的核心代码只需创建不同的策略对象就能让电脑拥有截然不同的行为模式。这极大地增强了程序的扩展性。4.2 游戏历史的记录与持久化为了支持基于历史的策略以及让玩家可以回顾战绩我们需要记录游戏历史。创建一个GameRound记录单轮信息和一个GameHistory管理所有记录。public class GameRound { public HandGesture PlayerGesture { get; set; } public HandGesture ComputerGesture { get; set; } public GameResult Result { get; set; } public DateTime PlayTime { get; set; } DateTime.Now; } public class GameHistory { public ListGameRound Rounds { get; private set; } new ListGameRound(); public void AddRound(HandGesture player, HandGesture computer, GameResult result) { Rounds.Add(new GameRound { PlayerGesture player, ComputerGesture computer, Result result }); // 可选只保留最近N条记录防止内存无限增长 if (Rounds.Count 100) Rounds.RemoveAt(0); } public void PrintHistory() { Console.WriteLine(\n 对战历史 ); foreach (var round in Rounds) { Console.WriteLine(${round.PlayTime:HH:mm:ss} - 玩家:{round.PlayerGesture} vs 电脑:{round.ComputerGesture} - {round.Result}); } } }在GameEngine.PlayOneRound方法中在判定胜负后调用_history.AddRound(...)将本轮记录保存下来。你还可以在游戏结束时或者在游戏中增加一个命令如输入“history”来调用PrintHistory方法展示所有记录。实操心得历史记录功能看似简单但引入了状态管理。在设计时要考虑清楚GameHistory的生命周期和归属。是作为GameEngine的成员还是通过依赖注入传递给需要它的对象如策略类这里我们将其作为GameEngine的成员并传递给电脑策略是一个比较清晰的设计。4.3 配置化与扩展性思考一个健壮的程序应该易于配置。例如游戏总局数、是否允许平局、不同的计分规则等都可以从硬编码中提取出来放到配置文件如appsettings.json或通过启动参数设置。我们可以创建一个GameSettings类public class GameSettings { public int MaxRounds { get; set; } 10; // 默认最多10局 public bool EnableExtendedRules { get; set; } false; // 是否启用“蜥蜴史波克”扩展规则 public string ComputerStrategy { get; set; } Random; // 电脑策略配置 }在GameEngine中读取这个配置。当EnableExtendedRules为true时我们需要动态切换GameRule和HandGesture枚举。这提示我们最初的GameRule.Judge方法写死了三种手势的判断扩展性不足。更好的做法是定义一个规则接口IGameRule并为基本规则和扩展规则分别实现。这又一次体现了面向接口编程和策略模式的威力。虽然由于篇幅我们不会在此完全实现扩展规则但这个思考过程至关重要。它迫使你思考当需求变化时你的代码哪些地方需要大动干戈哪些地方可以像换零件一样轻松适配提前进行这种抽象是区分普通代码和优秀设计的关键。5. 常见问题、调试技巧与进阶方向5.1 开发中常见问题与解决输入处理异常玩家可能输入非数字字符或者超出范围。我们的HumanPlayer类中已经通过int.TryParse和范围检查做了处理。这是一个必须有的防御性编程步骤。随机数“不随机”在快速循环中创建new Random()由于系统时钟作为种子变化不大可能导致生成的随机数序列高度相关。解决方案是像我们之前做的那样将Random实例作为类的静态或实例字段只初始化一次。枚举转换的陷阱我们使用(HandGesture)choice将整数转为枚举。但如果choice的值不在枚举定义的范围内比如-1或3转换仍然会成功得到一个无效的枚举值这可能在后续比较中引发难以察觉的错误。更安全的方法是使用Enum.IsDefined方法进行检查。int choice ...; if (Enum.IsDefined(typeof(HandGesture), choice)) { return (HandGesture)choice; } else { // 处理无效输入例如返回一个默认值或抛出异常 return HandGesture.Rock; }对象引用与状态管理确保在正确的时机初始化对象。例如GameHistory应该在GameEngine开始前创建并贯穿整个游戏生命周期传递给需要它的对象如策略类而不是在每轮游戏中新建。5.2 调试与测试技巧单元测试这个项目是练习单元测试的绝佳对象。你可以为GameRule.Judge方法编写测试用例覆盖所有9种3x3手势组合确保胜负判断逻辑百分百正确。也可以为不同的IGestureStrategy实现编写测试验证其输出是否符合预期。控制台输出调试在策略类中添加详细的日志输出例如Console.WriteLine($[策略] 分析历史玩家常出{mostFrequent}因此我出{gesture})这能帮助你直观理解AI的“思考”过程。使用断点在GetGesture、Judge等关键方法内部设置断点逐步执行观察变量的值是理解程序流程和排查逻辑错误最有效的方法。5.3 项目进阶方向如果你已经完成了基础版本并尝试了上述优化还可以挑战以下方向让这个项目成为你简历上的一个亮点图形界面WPF/WinForms将控制台应用迁移到图形界面。玩家通过点击按钮出拳电脑的手势和结果以图片形式展示。这涉及到事件处理、UI线程与后台逻辑的分离。网络对战将项目改造成一个简单的客户端-服务器应用支持两个真人玩家通过网络对战。这会引入Socket编程、异步通信、数据序列化等网络编程核心知识。数据持久化将玩家的历史战绩、胜率等信息保存到数据库如SQLite或文件中。每次游戏后更新数据并可以提供一个排行榜功能。更复杂的AI实现更强大的电脑AI例如使用马尔可夫链预测玩家的出拳序列或者使用简单的机器学习模型虽然有点杀鸡用牛刀但作为学习很有趣。依赖注入使用像Microsoft.Extensions.DependencyInjection这样的库来管理GameEngine、Player、IGestureStrategy等对象的创建和生命周期使代码耦合度更低更易于测试。从一个小小的石头剪刀布游戏出发竟然可以深入到设计模式、架构设计、网络通信、数据持久化等多个层面。这正是编程的魅力所在从一个简单的需求开始不断挖掘、重构和扩展你的代码和设计能力就在这个过程中得到了实实在在的锻炼。

相关新闻

TI bq2750x电量计开发实战:从评估软件到Golden Image生成

TI bq2750x电量计开发实战:从评估软件到Golden Image生成

1. 项目概述与核心价值如果你正在开发一款使用锂电池供电的产品,无论是智能手表、无线耳机还是电动工具,那么电池管理的精度和可靠性直接决定了用户体验和产品口碑。在电池管理系统的核心,有一个被称为“电量计”或“Gas Gauge”的芯片&#…

2026/7/24 4:37:15阅读更多 →
基于TI阻抗跟踪技术的主机侧高精度电量计系统设计与实践

基于TI阻抗跟踪技术的主机侧高精度电量计系统设计与实践

1. 项目概述:为什么我们需要更聪明的电池“管家”?你有没有遇到过这种情况:手机明明显示还有20%的电,结果一个电话进来或者打开相机,屏幕瞬间就黑了。或者,新买的蓝牙耳机用了一年,感觉续航时间…

2026/7/24 4:37:15阅读更多 →
单节锂电阻抗跟踪电量计PCB设计:从原理到实战的可靠性指南

单节锂电阻抗跟踪电量计PCB设计:从原理到实战的可靠性指南

1. 项目概述与核心挑战在便携式电子设备,尤其是智能手机、TWS耳机、智能手表等单节锂离子电池供电的产品中,电池管理单元(BMU)的精度和可靠性直接决定了用户体验。其中,基于阻抗跟踪(Impedance Track™&…

2026/7/24 4:37:15阅读更多 →
2024主流AI写作工具深度评测与选型指南

2024主流AI写作工具深度评测与选型指南

1. AI写作工具市场现状与核心需求2024年的AI写作领域已经形成了国内外产品同台竞技的局面。从学术论文到商业文案,从创意写作到技术文档,不同场景下的写作需求催生了各具特色的AI工具。ChatGPT作为国际标杆产品,DeepSeek代表国内技术新锐&…

2026/7/24 6:13:34阅读更多 →
Nginx与Apache服务器配置安全加固实战指南

Nginx与Apache服务器配置安全加固实战指南

1. 项目概述:当配置成为攻击者的“后门”在Web安全领域,我们常常将目光聚焦在应用框架的漏洞、数据库的注入攻击或是业务逻辑的缺陷上。这没错,它们是攻击的高频目标。但作为一名运维老兵,我见过太多因为“地基”不稳而导致的系统…

2026/7/24 6:13:34阅读更多 →
深入解析TI TPS65810/11 PMIC:从电源管理原理到嵌入式系统实战

深入解析TI TPS65810/11 PMIC:从电源管理原理到嵌入式系统实战

1. 项目概述与PMIC核心价值在嵌入式系统,尤其是智能手机、平板电脑这类对功耗和空间都极其敏感的设备里,电源管理单元的设计往往是决定产品成败的关键。你可能会觉得,不就是几个降压电路和LDO吗?但当你真正上手,面对处…

2026/7/24 6:13:34阅读更多 →
AI跨域训练性能暴跌?C++通信库的7大缺陷与优化实战

AI跨域训练性能暴跌?C++通信库的7大缺陷与优化实战

1. 项目概述:当AI撞上C的“墙”最近在跟几个做AI平台架构的朋友聊天,大家不约而同地提到了一个痛点:辛辛苦苦搭建的分布式AI训练系统,一到跨域(比如从公司北京机房到上海机房,或者从公有云A迁移到公有云B&a…

2026/7/24 6:13:34阅读更多 →
微信支付授权—扫码/押金授权操作教程—东方仙盟

微信支付授权—扫码/押金授权操作教程—东方仙盟

第一步:电脑上打开 微信支付微信支付 - 中国领先的第三方支付平台 | 微信支付提供安全快捷的支付方式微信支付是腾讯公司的支付业务品牌,微信支付商户平台支持线下场所、公众号、小程序、PC网站、APP、企业微信等经营场景快速接入微信支付。微…

2026/7/24 6:13:34阅读更多 →
华为OD机试C++题解:滑动窗口与哈希集合破解字符串解密

华为OD机试C++题解:滑动窗口与哈希集合破解字符串解密

1. 项目概述:从一道机试题看华为OD的选拔逻辑最近在技术社区和求职圈里,华为OD(Outsourcing Dispatch)的机试成了一个绕不开的话题。很多朋友,尤其是刚接触C不久或者准备转行做开发的,一听到“机试”两个字…

2026/7/24 6:11:34阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/24 0:58:53阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/24 0:58:53阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/24 0:58:53阅读更多 →
我的编程之路:第一篇博客

我的编程之路:第一篇博客

大家好,我是一名编程初学者,同时这也是我编程学习之路上的第一篇博客。在这里,我想要向大家介绍我的一些想法和规划。a.自我介绍我是一个刚刚接触编程的新手,目前在学习c语言,我对编程世界充满了强烈的好奇。当然&…

2026/7/24 0:00:06阅读更多 →
【LeetCode 54】螺旋矩阵

【LeetCode 54】螺旋矩阵

问题描述: 解法: 1、模拟(参考自【LeetCode 54】螺旋矩阵-CSDN博客) int *spiralOrder(int **matrix, int matrixSize, int *matrixColSize, int *returnSize) {static const int dirs[4][2] {{0, 1}, {1, 0}, {0, -1}, {-1, …

2026/7/24 0:00:06阅读更多 →
2026 WAIC:模型隐身、智能体疯野,厂商竞赛聚焦办公场景与商业闭环

2026 WAIC:模型隐身、智能体疯野,厂商竞赛聚焦办公场景与商业闭环

知春路不相信模型领先今年WAIC大会,昔日AI六小龙来了五家,分别是Kimi、阶跃星辰、Minimax、百川智能、零一万物。连放弃基模的百川和零一万物都来了,唯一缺席的竟是近几个月来风光无限的智谱。(DeepSeek一直不参加)WAI…

2026/7/24 0:00:06阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/23 22:58:43阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/23 18:58:18阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/23 18:58:18阅读更多 →