FEATURED · 精选文章

C#微信SDK封装实战:架构设计与核心实现解析

发布时间 / 2026/8/31 20:44:42
来源 / 创域科博编辑部
栏目 / 资讯中心
C#微信SDK封装实战:架构设计与核心实现解析 简介这是一套面向 .NET 开发者的企业级微信生态集成解决方案专为需要快速对接微信全平台能力的 C# 项目设计覆盖公众平台订阅号、服务号、小程序、小游戏、小商店、开放平台、微信支付、企业微信、广点通广告平台及微信智能对话等核心场景显著降低多端 API 调用与鉴权管理复杂度。资源包共7577个文件主体为4041个C#类库源码含WechatApiClient、WechatWorkClient等模块化客户端、3270个JSON配置与Mock数据、164个XML文档说明辅以CSProj工程文件、MD使用文档及配置文件结构清晰、分层明确支持.NET Core/.NET 5跨平台部署压缩后仅5.93MB轻量易集成。已有4133人学习下载开发者可直接复用封装好的HTTP请求链路、签名算法、消息加解密、OAuth2授权、支付回调验签等关键逻辑并基于预置的CardCreateRequest等标准请求模型快速构建业务功能。1. 项目概述与核心价值最近在整理一个老项目时翻出了一个尘封已久的压缩包名字就叫“C# 版微信 SDK封装全部已知的微信 API.zip”。这玩意儿当年可是耗费了我不少心血从微信公众号到微信支付再到小程序和企业微信几乎把所有能调用的微信官方接口都封装了一遍。今天把它拿出来晒晒太阳也正好聊聊在C#这个生态里自己动手封装一套完整的微信SDK到底有哪些门道、踩过哪些坑以及它对于一个.NET开发者来说真正的价值在哪里。简单来说这个SDK的目标就是让C#开发者能够用最熟悉、最舒服的方式与微信生态进行交互。你不用再去反复查阅微信那略显晦涩的官方文档也不用为每个接口手动拼接XML或JSON、处理签名、管理AccessToken。你只需要像调用本地服务一样引入几个DLL初始化一个配置然后就可以通过强类型的对象和方法完成从发送客服消息、处理支付回调到管理用户标签等一系列操作。它解决的核心问题就是“效率”和“可靠性”。对于需要快速对接微信功能的中小项目或者是在.NET技术栈下维护多个微信相关应用的企业拥有这样一套统一的、经过实战检验的底层工具能省下大量的开发和联调时间把精力聚焦在业务逻辑本身。2. 整体架构设计与核心思路当初决定自己造这个轮子而不是直接用市面上已有的开源库主要是基于几个考量。第一是可控性微信接口更新频繁且不同模块公众号、支付、开放平台的文档风格和参数设计时有差异自己封装能确保对每一个接口的实现细节都了如指掌出问题时能快速定位。第二是统一性我希望所有模块遵循同一套设计哲学和编码规范降低团队成员的学习和使用成本。第三是扩展性为未来可能出现的新的微信产品线如视频号、微信小商店预留接入空间。整个SDK的架构采用了清晰的分层和模块化设计。最底层是核心通信层它不关心具体的业务API只负责两件事一是HTTP请求的发送与接收包括连接管理、超时重试、日志记录二是AccessToken的全局管理这是调用所有微信API的“门票”必须实现自动、高效、线程安全的获取与刷新机制。这一层是稳定的基石一旦写好几乎不需要改动。中间层是业务模块层这是SDK的主体。我严格按照微信官方文档的产品线划分模块公众号模块涵盖消息管理、用户管理、素材管理、菜单管理、模板消息等。微信支付模块包含统一下单、支付结果通知、退款、企业付款到零钱等。小程序模块聚焦于小程序特有的API如登录凭证校验、内容安全、订阅消息等。企业微信模块独立封装企业微信的通讯录、应用消息、客户联系等接口。每个模块内部又按照功能点进一步细分。例如公众号模块下会有MessageService、UserService、MaterialService等。每个Service类都只负责一个明确的功能域遵循单一职责原则。最上层是模型与工具层。这里定义了所有请求和响应参数的强类型模型类POCO。比如发送模板消息的请求不再是一个充满魔术字符串的Dictionarystring, object而是一个TemplateMessageRequest对象其属性名与微信文档一一对应且有清晰的注释和数据类型约束。工具层则提供了一些公共方法如XML/JSON的序列化与反序列化针对微信不同接口的不同数据格式、SHA1/MD5签名生成、AES加解密用于消息体加解密等。这种架构的好处是显而易见的。对于使用者而言他们只需要关心业务模块层的Service类通过依赖注入或简单实例化即可使用代码意图清晰。对于维护者而言各层之间耦合度低新增一个API接口通常只需要在对应的模块下添加一个方法并在模型层定义好相关的请求/响应类即可不会影响其他已有功能。3. 核心实现细节与关键技术点解析3.1 AccessToken的全局管理策略AccessToken是调用微信API的凭证有效期通常为2小时且调用次数有限制。如何高效、安全地管理它是SDK稳定性的关键。我设计了一个IAccessTokenContainer接口及其默认实现LocalAccessTokenContainer。它的核心逻辑是“内存缓存为主被动刷新为辅”。在SDK初始化时会传入AppId和AppSecret。当任何业务方法需要调用API时会首先向AccessTokenContainer请求Token。容器内部维护着一个静态的ConcurrentDictionary以AppId为键存储Token字符串及其过期时间戳。注意这里使用ConcurrentDictionary是为了保证在多线程环境下同一个AppId的Token读写是线程安全的避免重复刷新。当请求Token时容器会检查内存中是否存在对应AppId的Token。如果存在检查其过期时间是否在未来的5分钟之外预留一个安全缓冲期。 如果两个条件都满足则直接返回缓存的Token。否则容器会锁定当前AppId的刷新操作使用lock关键字或SemaphoreSlim防止多个线程同时触发刷新请求。然后它向微信服务器发起HTTP请求获取新的Token并更新缓存。// 伪代码示例展示核心逻辑 public class LocalAccessTokenContainer : IAccessTokenContainer { private static readonly ConcurrentDictionarystring, AccessTokenBag _tokenCache new(); private readonly object _refreshLock new object(); public async Taskstring GetTokenAsync(string appId, string appSecret) { if (_tokenCache.TryGetValue(appId, out var bag) bag.ExpireTime DateTime.Now.AddMinutes(5)) { return bag.AccessToken; } lock (_refreshLock) { // 双重检查防止锁内重复刷新 if (_tokenCache.TryGetValue(appId, out bag) bag.ExpireTime DateTime.Now.AddMinutes(5)) { return bag.AccessToken; } // 调用微信接口获取新Token var newToken FetchNewTokenFromWeChat(appId, appSecret).Result; bag new AccessTokenBag { AccessToken newToken, ExpireTime DateTime.Now.AddHours(2) }; _tokenCache[appId] bag; return newToken; } } }对于分布式应用内存缓存就不够用了。为此我定义了IAccessTokenContainer接口你可以轻松实现一个基于Redis或数据库的DistributedAccessTokenContainer只需实现GetTokenAsync和SetTokenAsync等方法SDK的核心逻辑无需改动。3.2 强类型模型与动态序列化的平衡微信接口的请求和响应格式主要是JSON和XML。早期我尝试为每一个接口的每一个字段都定义严格的C#模型类这带来了极佳的智能提示和编译时检查但很快遇到了问题微信接口的响应中有时会包含一些文档未说明的额外字段或者某些字段在某些条件下不会返回。如果模型类定义死了所有属性反序列化时遇到未知字段会报错或者缺少字段时属性为null导致后续代码空引用异常。我的解决方案是采用“核心字段强类型扩展字段动态处理”的策略。对于响应模型我使用Json.NETNewtonsoft.Json库并利用[JsonProperty]特性来映射字段名。同时在模型基类中定义一个JObject ExtensionData { get; set; }属性并使用[JsonExtensionData]特性标记。这样所有未被模型明确定义的JSON字段在反序列化时都会自动存入ExtensionData这个字典中既不会丢失数据也不会导致反序列化失败。public class WeChatApiResponseBase { [JsonProperty(errcode)] public int ErrCode { get; set; } [JsonProperty(errmsg)] public string ErrMsg { get; set; } [JsonExtensionData] public IDictionarystring, JToken ExtensionData { get; set; } public bool IsSuccess() ErrCode 0; } public class UserInfoResponse : WeChatApiResponseBase { [JsonProperty(openid)] public string OpenId { get; set; } [JsonProperty(nickname)] public string Nickname { get; set; } // ... 其他明确知道的字段 }对于请求模型则保持强类型因为发送什么是我们可控的。这套机制确保了SDK在微信接口发生微小变动如增加字段时具有更好的向前兼容性。3.3 异常处理与错误码转译微信API的调用结果成功时通常返回JSON错误时也返回JSON但包含errcode和errmsg。SDK不能简单地把HTTP 200响应都当作成功处理。我的做法是在核心通信层对所有响应进行统一拦截。首先检查HTTP状态码如果不是200直接抛出包含状态码和内容的WeChatHttpException。如果是200则将响应体反序列化为WeChatApiResponseBase基类检查ErrCode是否为0。如果不为0则抛出一个特定的WeChatApiException这个异常里不仅包含错误码和消息我还内置了一个错误码转译表将常见的微信错误码如40001-无效的AccessToken40029-无效的code转译为更易读的中文描述和推荐处理动作。public class WeChatApiException : Exception { public int ErrorCode { get; } public string ErrorMessage { get; } public string SuggestedAction { get; } // 例如“请检查AppSecret是否正确”或“AccessToken已过期正在自动刷新重试” public WeChatApiException(int errorCode, string errorMessage) : base($微信API错误: {errorCode} - {errorMessage}) { ErrorCode errorCode; ErrorMessage errorMessage; SuggestedAction ErrorCodeTranslator.GetSuggestedAction(errorCode); } }这样上层业务代码捕获到WeChatApiException后不仅能知道错了还能大致知道为什么错、该怎么办极大地提升了调试效率。3.4 消息加解密与安全通信对于公众号的服务器配置以及企业微信的回调需要启用消息加解密模式。微信使用的是AES-256-CBC加密算法并且自己有一套PKCS#7填充和Base64编码的规则。这部分逻辑独立封装在一个WeChatCryptography类中。实现的关键点在于密钥处理微信提供的EncodingAESKey是43位的Base64编码字符串需要先解码成32字节的二进制密钥。随机字符串与网络字节序生成16位的随机字符串作为加密使用的随机向量IV并且消息长度需要转为大端序Network Byte Order的4字节整数。PKCS#7填充需要手动实现或使用支持PKCS7的加密库确保明文长度为32字节的倍数。签名验证加密后的消息会生成一个SHA1签名用于验证消息的完整性。在解密时需要重新计算签名并与传入的签名对比。这部分代码非常底层且要求精确我参考了微信官方的多种语言示例并编写了详尽的单元测试覆盖了加密、解密、签名验证的各种边界情况确保其稳定可靠。4. 主要模块的封装与使用示例4.1 公众号模块以发送模板消息为例公众号模块是使用最频繁的。以发送模板消息这个典型场景为例展示了如何通过SDK简化操作。首先你需要初始化一个WeChatOfficialAccountApi对象实际使用中可通过依赖注入。var config new WeChatOfficialAccountConfig { AppId 你的AppId, AppSecret 你的AppSecret }; var api new WeChatOfficialAccountApi(config);然后准备你的模板消息数据。SDK提供了强类型的TemplateMessageRequest类。var request new TemplateMessageRequest { ToUserOpenId o6_bmjrPTlm6_2sgVt7hMZOPfL2M, // 接收者OpenId TemplateId 模板ID, Url https://yourdomain.com/redirect, // 可选点击后跳转的链接 Data new Dictionarystring, TemplateMessageDataItem { [first] new TemplateMessageDataItem(您好您的订单已发货。, #173177), [orderNumber] new TemplateMessageDataItem(202310270001, #173177), [orderTime] new TemplateMessageDataItem(2023-10-27 15:30:00, #173177), [remark] new TemplateMessageDataItem(请及时查收您的包裹。, #FF0000) } };最后调用API发送。SDK内部会自动处理AccessToken的获取、请求的序列化、响应的反序列化和错误处理。try { var result await api.Message.SendTemplateAsync(request); if (result.IsSuccess()) { Console.WriteLine($模板消息发送成功MsgId: {result.MsgId}); } } catch (WeChatApiException ex) { // 处理业务错误如无效的OpenId、模板ID等 Console.WriteLine($发送失败: {ex.ErrorCode} - {ex.ErrorMessage}); Console.WriteLine($建议操作: {ex.SuggestedAction}); } catch (Exception ex) { // 处理网络异常等其他错误 Console.WriteLine($发生未知错误: {ex.Message}); }可以看到业务代码非常干净几乎就是声明数据、调用方法、处理结果三步所有脏活累活都被SDK隐藏了。4.2 微信支付模块统一下单与回调处理微信支付模块的封装重点在于签名和回调验证。以Native支付扫码支付为例。统一下单var payApi new WeChatPayApi(new WeChatPayConfig { AppId 公众号AppId, MchId 商户号, ApiKey 商户密钥, CertPath apiclient_cert.p12, // 退款等操作需要证书 CertPassword 商户号 }); var request new UnifiedOrderRequest { Body 测试商品, OutTradeNo Guid.NewGuid().ToString(N), TotalFee 1, // 单位是分这里是1分钱 SpbillCreateIp 用户端IP, NotifyUrl https://yourdomain.com/pay/notify, TradeType NATIVE }; var response await payApi.UnifiedOrderAsync(request); if (response.IsSuccess()) { // 对于NATIVE支付response.CodeUrl就是用于生成二维码的链接 string qrCodeUrl response.CodeUrl; // 将这个url生成二维码图片展示给用户扫描支付 }支付结果通知回调处理支付成功后微信服务器会向你的NotifyUrl发送一个XML格式的POST请求。SDK提供了WeChatPayNotify工具类来安全地处理这个回调。// 在ASP.NET Core的Controller中 [HttpPost(/pay/notify)] public async TaskIActionResult PayNotify() { using var reader new StreamReader(Request.Body); string xmlData await reader.ReadToEndAsync(); // 解析并验证通知的签名 var notifyResult WeChatPayNotify.ParseAndVerify(xmlData, _payConfig.ApiKey); if (!notifyResult.IsSignatureValid) { return BadRequest(签名验证失败); } // 签名验证通过可以安全地使用通知数据 string outTradeNo notifyResult.GetValue(out_trade_no); string transactionId notifyResult.GetValue(transaction_id); // ... 更新你的订单状态为已支付 // 必须返回指定格式的XML给微信告知处理成功否则微信会重复通知 return Content(xmlreturn_code![CDATA[SUCCESS]]/return_codereturn_msg![CDATA[OK]]/return_msg/xml, text/xml); }WeChatPayNotify.ParseAndVerify方法内部完成了XML解析、字典排序、二次签名验证等所有安全校验步骤确保回调请求确实来自微信且数据未被篡改。4.3 小程序模块登录凭证校验小程序前端通过wx.login()获取code传给后端。后端需要用此code调用微信接口换取openid和session_key。var miniProgramApi new WeChatMiniProgramApi(new WeChatMiniProgramConfig { AppId 小程序AppId, AppSecret 小程序AppSecret }); var jsCode 前端传来的code; var result await miniProgramApi.Auth.Code2SessionAsync(jsCode); if (result.IsSuccess()) { string openId result.OpenId; // 用户唯一标识 string sessionKey result.SessionKey; // 会话密钥用于解密用户信息 // 通常这里会用自己的规则生成一个自定义登录态如Token将openid和sessionKey关联存储如Redis // 然后将Token返回给小程序前端用于后续需要用户身份的接口验证。 string customToken GenerateCustomToken(openId, sessionKey); // ... 返回 customToken 给客户端 }session_key非常重要且敏感它用于解密小程序端获取的加密用户数据如手机号。SDK也提供了对应的解密方法WeChatMiniProgramCryptography.DecryptEncryptedData确保这部分逻辑的安全和正确。4.4 企业微信模块发送应用消息企业微信的API风格与公众号类似但有自己的独立域名和Token管理使用CorpId和CorpSecret。var workApi new WeChatWorkApi(new WeChatWorkConfig { CorpId 企业ID, CorpSecret 应用的Secret, AgentId 1000002 // 应用ID }); var message new TextMessageRequest { ToUser UserID1|UserID2, ToParty PartyID1|PartyID2, ToTag TagID1 | TagID2, MsgType text, AgentId 1000002, Text new TextMessageContent { Content 你好这是一条测试消息。 }, Safe 0 // 非保密消息 }; var result await workApi.Message.SendAsync(message);企业微信的AccessToken管理机制与公众号不同需要区分不同的应用Agent。SDK内部为每个(CorpId, CorpSecret)对维护独立的Token缓存逻辑与公众号模块类似但请求的URL和参数不同。5. 部署、配置与最佳实践5.1 项目引入与初始化推荐通过NuGet包管理器安装编译好的SDK包如果已发布。在项目中你需要在启动时如ASP.NET Core的Program.cs或Startup.cs进行一次性配置。对于ASP.NET Core项目可以使用依赖注入将各个API客户端注册为单例或作用域服务。// 在Program.cs或Startup.ConfigureServices中 services.AddWeChatOfficialAccount(options { options.AppId Configuration[WeChat:OfficialAccount:AppId]; options.AppSecret Configuration[WeChat:OfficialAccount:AppSecret]; }); services.AddWeChatPay(options { options.AppId Configuration[WeChat:Pay:AppId]; options.MchId Configuration[WeChat:Pay:MchId]; options.ApiKey Configuration[WeChat:Pay:ApiKey]; options.CertPath Configuration[WeChat:Pay:CertPath]; }); // ... 类似地添加小程序、企业微信等然后在控制器或服务中通过构造函数注入IWeChatOfficialAccountApi,IWeChatPayApi等接口即可使用。5.2 配置管理要点所有敏感信息AppId, AppSecret, ApiKey, MchId等必须从配置文件如appsettings.json或环境变量中读取绝不要硬编码在代码里。对于支付证书.p12文件应将其放在服务器安全的目录下并通过配置指定路径。在开发、测试、生产环境中应使用不同的配置。// appsettings.Production.json 示例 { WeChat: { OfficialAccount: { AppId: 生产环境公众号AppId, AppSecret: 生产环境公众号AppSecret }, Pay: { AppId: 生产环境支付关联的AppId, MchId: 生产环境商户号, ApiKey: 生产环境商户API密钥, CertPath: /secure/certs/apiclient_cert_prod.p12 } } }5.3 日志与监控一个健壮的SDK必须提供完善的日志输出。我在核心通信层和各个关键节点如获取Token、发送请求、收到响应集成了ILogger接口。你只需要在项目中配置好日志提供程序如NLog, SerilogSDK的运行情况就会自动记录。这对于排查“为什么接口调不通”这类问题至关重要。你可以看到完整的请求URL、请求体、响应状态码和响应体敏感信息如AccessToken会自动脱敏。此外建议对关键的API调用尤其是支付、发送消息添加业务层的监控和告警。例如当模板消息发送连续失败或者支付回调验证签名失败时应及时通知开发人员。5.4 性能优化与缓存策略除了内存级的AccessToken缓存对于某些不常变化的数据也可以考虑加入更长时间的缓存。例如微信公众号的素材列表、用户标签列表等。SDK本身不强制实现这些但提供了良好的扩展点。你可以在调用SDK获取这些数据后将其存入分布式缓存如Redis并设置合理的过期时间如5-10分钟从而减少对微信服务器的请求提升响应速度。对于高频调用的接口要特别注意微信的频率限制。例如获取AccessToken的频率限制是每日2000次。SDK的全局管理机制已经避免了不必要的重复获取。但对于像“获取用户信息”这样的接口如果业务上需要频繁查询同一个用户你也应该在业务层实现缓存。6. 常见问题、故障排查与实战心得在实际开发和运维过程中会遇到各种各样的问题。下面是一些最常见的情况和我的处理经验。6.1 AccessToken相关错误错误码 40001invalid credential, access_token is invalid or not latest原因这是最经典的错误。要么是AccessToken确实过期了虽然SDK有自动刷新但在极端并发下可能使用了刚过期的Token要么是微信服务器的问题导致Token提前失效。排查首先检查SDK的日志看最近一次获取Token的时间和当前时间。如果Token已过期说明自动刷新逻辑可能未触发或失败。如果Token未过期可能是微信侧的问题。解决对于SDK我增加了“Token失效重试”机制。当捕获到40001错误时会强制清除当前缓存的Token然后重试一次业务请求。对于业务代码调用SDK的方法时可以包装一个简单的重试逻辑。错误码 40164invalid ip, not in whitelist原因调用获取AccessToken接口的服务器IP不在微信公众号或小程序的IP白名单中。解决登录微信公众平台在“开发 - 基本配置”中将你的服务器公网IP地址添加到“IP白名单”中。这是很多新手部署到服务器后遇到的第一个坑。6.2 微信支付相关错误签名错误现象统一下单失败返回“签名错误”或支付回调验证签名失败。原因99%的情况是商户密钥ApiKey配置错误或者在生成签名时参数排序、编码方式不对。排查仔细核对微信商户平台设置的APIv2密钥32位是否与代码中配置的ApiKey完全一致注意大小写和空格。确保SDK使用的签名算法与微信要求一致通常是HMAC-SHA256。在开发环境可以打开SDK的Debug日志查看它实际用于签名的参数字符串是什么然后与微信官方提供的签名校验工具或自己用其他语言写的脚本进行比对。实操心得我曾在SDK中内置了一个“签名调试模式”当开启时会将待签名的字典和生成的签名明文输出到日志这对排查签名问题有奇效。支付成功但未收到回调原因你的回调地址notify_url无法从公网访问本地开发、内网测试。回调处理逻辑有异常没有在5秒内返回正确的XML响应给微信。网络波动导致微信的通知请求丢失罕见。解决开发测试时使用内网穿透工具如ngrok将本地服务暴露到公网。确保回调处理Action是[HttpPost]并且逻辑尽可能简单、快速。处理完成后必须返回微信指定的成功XML字符串。任何未处理的异常都可能导致响应超时或格式错误。在商户平台可以手动补发通知但更可靠的做法是业务上要有对账机制定期查询未支付成功的订单状态。6.3 消息加解密失败现象公众号服务器配置提交失败提示“Token验证失败”或接收消息时解密失败。原因Token/EncodingAESKey不一致代码中配置的Token、EncodingAESKey与公众平台后台设置的不一致。时间戳误差微信服务器会检查请求中的时间戳如果与服务器时间相差太大会直接拒绝。需要确保你的服务器时间已同步使用NTP。加解密算法实现有误特别是PKCS#7填充和Base64编码环节。排查使用微信官方提供的“消息加解密调试工具”输入你的Token、EncodingAESKey和收到的消息明文/密文与你的SDK计算结果进行比对可以快速定位是哪个环节出了问题。6.4 网络与超时问题微信的服务器偶尔会出现响应慢或不可用的情况。SDK的HTTP客户端必须配置合理的超时时间我通常设置连接超时为10秒读写超时为30秒和重试策略。对于非幂等的请求如支付不能简单重试但对于获取Token、查询信息等GET请求可以加入指数退避的轻量重试。另外确保你的服务器有稳定的网络环境能够正常访问api.weixin.qq.com、api.mch.weixin.qq.com等微信域名。在云服务器上有时需要检查安全组和防火墙设置。6.5 代码层面的实践建议异步化SDK的所有API调用都提供了异步方法Async后缀。在ASP.NET Core等现代框架中务必使用async/await来调用避免阻塞线程池线程提升应用的并发能力。依赖注入强烈建议通过依赖注入来使用SDK的客户端。这便于管理生命周期、进行单元测试和配置管理。单元测试与集成测试为SDK的核心工具类如加密解密、签名生成编写充分的单元测试。为主要的Service类编写集成测试调用微信的沙箱环境如果提供或使用模拟的HTTP响应。关注官方更新微信接口并非一成不变。需要定期关注微信开放社区的公告和文档更新。一个好的SDK应该有一个机制能够相对平滑地兼容接口的增减和字段的变更。这也是为什么我在模型设计中采用了ExtensionData来容纳未知字段。封装这样一个全面的微信SDK是一个系统工程远不止是调用HTTP客户端那么简单。它涉及架构设计、模块划分、安全通信、异常处理、缓存策略、日志监控等多个方面。这个过程虽然繁琐但带来的收益是巨大的统一的代码风格、集中的错误处理、提升的开发效率、以及更稳定的线上表现。当团队里的所有成员都能用三两行代码就完成一个复杂的微信交互时你就会觉得当初投入的时间是完全值得的。这个“C# 版微信 SDK.zip”对我来说不仅仅是一个工具包更像是一本记录了与微信生态多年“打交道”经验的笔记。本文还有配套的精品资源点击获取
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻