新闻详情

新闻详情

首页 / 资讯中心 / 详情

命令模式实战:把“下单后的动作“打包成可执行的任务

发布时间:2026/11/15 16:07:56来源:尧图网络
命令模式实战:把“下单后的动作“打包成可执行的任务
一句话定位将一个请求封装为一个对象从而可用不同的请求对客户进行参数化对请求排队或记录请求日志以及支持可撤销的操作。一、实战场景用户下单成功后系统要做一连串后置动作发送订单确认短信/邮件发放积分更新会员等级通知仓储系统备货记录运营埋点。如果直接在主流程里逐个调用publicasyncTaskOnOrderCreatedAsync(Orderorder){await_smsService.SendAsync(order.CustomerPhone,下单成功……);await_pointsService.GrantAsync(order.CustomerId,100);await_memberService.UpgradeAsync(order.CustomerId);await_warehouseService.NotifyAsync(order);await_trackingService.TrackAsync(order.created,order);// 每加一个动作这里就多一行失败处理也各不相同……}问题主流程被业务动作塞满订单核心逻辑和后置动作耦合在一起这些动作往往需要异步执行、失败重试、排队削峰直接内联调用做不到想批量重放比如系统恢复后重跑某天的后置动作无从下手每个动作的失败处理策略不同短信失败可忽略、积分发放失败要重试散落在主流程里。命令模式的解法把每个动作封装成一个命令对象实现统一接口主流程只负责创建命令并提交。命令可以被排队、被记录、被重放、被批量执行——动作本身变成了数据。二、设计思路调用方下单服务 │ 创建并提交 ▼ ┌─────────────────────────────┐ │ CommandQueue命令队列 │ │ 排队 → 逐个执行 → 失败重试 │ └──────────────┬──────────────┘ ▼ 执行Invoke ┌─────────────────────────────┐ │ IOrderCommand命令接口 │ │ ├─ SendSmsCommand │ │ ├─ GrantPointsCommand │ │ ├─ UpgradeMemberCommand │ │ ├─ NotifyWarehouseCommand │ │ └─ TrackEventCommand │ └─────────────────────────────┘命令模式的精髓做什么被封装成对象可以存储、传输、排队、重放。三、代码实现.NET 10 / C# 143.1 命令接口与执行上下文namespaceOrders.AfterOrder;publicinterfaceIOrderCommand{stringName{get;}TaskExecuteAsync(OrderContextcontext,CancellationTokenctdefault);TaskUndoAsync(OrderContextcontext,CancellationTokenctdefault);// 可选的补偿}publicsealedrecordOrderContext(OrderSnapshotOrder);publicsealedrecordOrderSnapshot(stringOrderId,stringCustomerId,stringCustomerPhone,decimalAmount);3.2 具体命令们每个动作一个命令namespaceOrders.AfterOrder.Commands;// 命令1发短信publicsealedclassSendSmsCommand(ISmsSendersms):IOrderCommand{publicstringNameSendSms;publicTaskExecuteAsync(OrderContextctx,CancellationTokenctdefault)sms.SendAsync(ctx.Order.CustomerPhone,$您已成功下单{ctx.Order.OrderId}金额{ctx.Order.Amount:C},ct);publicTaskUndoAsync(OrderContextctx,CancellationTokenctdefault)Task.CompletedTask;// 短信无法撤回}// 命令2发积分失败可重试publicsealedclassGrantPointsCommand(IPointsServicepoints):IOrderCommand{publicstringNameGrantPoints;publicTaskExecuteAsync(OrderContextctx,CancellationTokenctdefault)points.GrantAsync(ctx.Order.CustomerId,(int)(ctx.Order.Amount/10),ct);publicTaskUndoAsync(OrderContextctx,CancellationTokenctdefault)points.RecoverAsync(ctx.Order.CustomerId,(int)(ctx.Order.Amount/10),ct);}// 命令3通知仓储publicsealedclassNotifyWarehouseCommand(IWarehouseServicewarehouse):IOrderCommand{publicstringNameNotifyWarehouse;publicTaskExecuteAsync(OrderContextctx,CancellationTokenctdefault)warehouse.NotifyAsync(ctx.Order.OrderId,ct);publicTaskUndoAsync(OrderContextctx,CancellationTokenctdefault)Task.CompletedTask;}// 命令4运营埋点可忽略失败publicsealedclassTrackEventCommand(ITrackingServicetracking):IOrderCommand{publicstringNameTrackEvent;publicTaskExecuteAsync(OrderContextctx,CancellationTokenctdefault)tracking.TrackAsync(order.created,ctx.Order,ct);publicTaskUndoAsync(OrderContextctx,CancellationTokenctdefault)Task.CompletedTask;}3.3 执行器命令队列排队、重试、记录namespaceOrders.AfterOrder;publicsealedclassCommandQueue(ILoggerlogger){privatereadonlyQueueIOrderCommand_commandsnew();publicvoidEnqueue(IOrderCommandcommand)_commands.Enqueue(command);publicasyncTaskIReadOnlyListCommandExecutionResultFlushAsync(CancellationTokenctdefault){varresultsnewListCommandExecutionResult();while(_commands.Count0){varcommand_commands.Dequeue();varswStopwatch.StartNew();try{awaitExecuteWithRetryAsync(command,ct);results.Add(newCommandExecutionResult(command.Name,true,sw.ElapsedMilliseconds,null));}catch(Exceptionex){logger.LogError(ex,命令 {Name} 执行失败,command.Name);results.Add(newCommandExecutionResult(command.Name,false,sw.ElapsedMilliseconds,ex.Message));}}returnresults;}privateasyncTaskExecuteWithRetryAsync(IOrderCommandcommand,CancellationTokenct,intmaxRetries3){for(varattempt1;;attempt){try{awaitcommand.ExecuteAsync(CommandContext.Current,ct);return;}catchwhen(attemptmaxRetries){awaitTask.Delay(TimeSpan.FromSeconds(attempt),ct);// 指数退避的简化版}}}}publicsealedrecordCommandExecutionResult(stringName,boolSuccess,longElapsedMs,string?Error);// 简单模拟全局执行上下文真实项目可用 AsyncLocal / DI ScopepublicstaticclassCommandContext{publicstaticOrderContextCurrent{get;set;}default!;}3.4 主流程下单后把动作打包提交namespaceOrders;publicsealedclassOrderService(CommandQueuequeue,SendSmsCommandsmsCommand,GrantPointsCommandpointsCommand,NotifyWarehouseCommandwarehouseCommand,TrackEventCommandtrackCommand){publicasyncTaskOrderSnapshotCreateOrderAsync(CreateOrderRequestreq,CancellationTokenctdefault){// 1. 核心下单逻辑校验、扣库存、建单……省略varordernewOrderSnapshot(OrderId:$ORD-{Guid.NewGuid():N}[..18],CustomerId:req.CustomerId,CustomerPhone:req.Phone,Amount:req.Amount);// 2. 后置动作全部打包成命令进队列CommandContext.CurrentnewOrderContext(order);queue.Enqueue(smsCommand);queue.Enqueue(pointsCommand);queue.Enqueue(warehouseCommand);queue.Enqueue(trackCommand);// 3. 统一执行真实项目队列可换成 Hangfire / RabbitMQ实现异步削峰varresultsawaitqueue.FlushAsync(ct);returnorderwith{/* 可附加 results 状态 */};}}3.5 组装与运行// Program.cs.NET 10 顶层语句usingOrders;usingOrders.AfterOrder;usingOrders.AfterOrder.Commands;varbuilderWebApplication.CreateBuilder(args);builder.Services.AddLogging();// 注册各种服务与命令builder.Services.AddSingletonISmsSender,FakeSmsSender();builder.Services.AddSingletonIPointsService,FakePointsService();builder.Services.AddSingletonIWarehouseService,FakeWarehouseService();builder.Services.AddSingletonITrackingService,FakeTrackingService();builder.Services.AddSingletonSendSmsCommand();builder.Services.AddSingletonGrantPointsCommand();builder.Services.AddSingletonNotifyWarehouseCommand();builder.Services.AddSingletonTrackEventCommand();builder.Services.AddSingletonCommandQueue();builder.Services.AddScopedOrderService();varappbuilder.Build();app.MapPost(/orders,async(CreateOrderRequestreq,OrderServiceorders){varorderawaitorders.CreateOrderAsync(req);returnResults.Ok(new{order.OrderId,Statuscreated});});app.Run();// 各 Fake 服务的实现演示用真实项目替换为实际客户端publicinterfaceISmsSender{TaskSendAsync(stringphone,stringtext,CancellationTokenct);}publicsealedclassFakeSmsSender:ISmsSender{publicTaskSendAsync(stringphone,stringtext,CancellationTokenct){Console.WriteLine($[短信]{phone}:{text});returnTask.CompletedTask;}}publicinterfaceIPointsService{TaskGrantAsync(stringcustomer,intpoints,CancellationTokenct);TaskRecoverAsync(stringcustomer,intpoints,CancellationTokenct);}publicsealedclassFakePointsService:IPointsService{publicTaskGrantAsync(stringcustomer,intpoints,CancellationTokenct){Console.WriteLine($[积分] 用户{customer}{points});returnTask.CompletedTask;}publicTaskRecoverAsync(stringcustomer,intpoints,CancellationTokenct)Task.CompletedTask;}publicinterfaceIWarehouseService{TaskNotifyAsync(stringorderId,CancellationTokenct);}publicsealedclassFakeWarehouseService:IWarehouseService{publicTaskNotifyAsync(stringorderId,CancellationTokenct){Console.WriteLine($[仓储] 备货通知{orderId});returnTask.CompletedTask;}}publicinterfaceITrackingService{TaskTrackAsync(stringevt,objectpayload,CancellationTokenct);}publicsealedclassFakeTrackingService:ITrackingService{publicTaskTrackAsync(stringevt,objectpayload,CancellationTokenct){Console.WriteLine($[埋点]{evt});returnTask.CompletedTask;}}publicsealedrecordCreateOrderRequest(stringCustomerId,stringPhone,decimalAmount);四、应用要点与注意事项命令模式的三大价值解耦主流程不再知道后置动作的实现、可排队命令是对象能进队列、异步执行、削峰、可重放命令可记录、可批量重跑、可做补偿。与消息队列的关系命令模式是代码层面的封装消息队列是执行层面的载体。两者常组合使用——把命令序列化成消息投递到 RabbitMQ/Kafka消费者反序列化后执行。理解命令模式就理解了任务系统的核心。Undo 是可选能力不是所有命令都能撤销短信发出去撤不回。UndoAsync返回空实现即可但设计命令接口时保留这个钩子未来做补偿/回滚时不用改接口。区分命令 vs 事件命令是明确的意图期望被执行发积分事件是已经发生的事实可以触发副作用订单已创建。下单后置动作既可以用命令模式也可以用观察者模式事件驱动实现——命令模式更强调可排队、可重放事件驱动更强调解耦发布订阅。两者可以并存。每个命令保持单一职责一个命令只做一件事失败重试策略可以在执行器层面统一如示例的ExecuteWithRetryAsync不要让命令内部各自实现重试。五、小结命令模式把动作从代码升级成了数据可排队、可记录、可重放、可补偿。下单后置动作、定时任务、批量操作、撤销重做都是它的经典舞台。配合消息队列、Hangfire 这类执行载体它几乎构成了现代后端异步任务体系的全部雏形。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

【外设】之大彩串口显示屏 2026/11/15 4:54:16

【外设】之大彩串口显示屏

大彩串口屏初步使用 1 .官网下载 STM32 屏幕 GUI 设计资料 http://www.gz-dc.com/category/typeid/4112 找到 STM32 Keil 工程,移植相关代码因项目而异进行移植,由于项目简单,本人只对用到的指令接口进行修改。 比如:注意事项&…

阅读更多 →
无法下载Windows系统iso文件 2026/11/15 2:04:12

无法下载Windows系统iso文件

当我遇到这个问题的时候,我打开了一个网站: 登录 然后我打算下载的时候: 突然那个官方的连接就可以下载了:

阅读更多 →
【清华代码熊】DeepSeek V4.1 Flash 后训练详解 2026/11/14 10:11:15

【清华代码熊】DeepSeek V4.1 Flash 后训练详解

📌 上期解析了 DeepSeek V4.1 Flash 模型架构改进,本期解析 DeepSeek V4.1 Flash 预训练/后训练技术: 🌟 预训练:45T 文本 多模态混合语料、直接训练 sparse attention(取消 DeepSeek V4 的 dense 冷启动&…

阅读更多 →
Shuffle-R1: Efficient RL framework for Multimodal Large Language Models via Data-centric Dynamic ... 2026/11/14 17:14:38

Shuffle-R1: Efficient RL framework for Multimodal Large Language Models via Data-centric Dynamic ...

文章主要内容和创新点 主要内容 本文聚焦于多模态大语言模型(MLLM)强化学习(RL)训练中的效率问题,提出了一个名为Shuffle-R1的框架。研究发现,当前RL训练存在两个关键缺陷: 优势值坍缩(Advantage Collapsing):批次中大多数优势值集中在零附近,导致有效梯度信号被淹…

阅读更多 →
PRvL: Quantifying the Capabilities and Risks of Large Language Models for PII Redaction 2026/11/14 16:35:50

PRvL: Quantifying the Capabilities and Risks of Large Language Models for PII Redaction

一、文章主要内容总结 本文聚焦于利用大型语言模型(LLMs)实现个人身份信息(PII)脱敏的研究,旨在解决传统脱敏方法(如基于规则的系统、领域特定命名实体识别(NER)模型)泛化能力差、跨格式/跨语境适应性弱的问题。 研究通过全面评估多种LLM架构(包括密集型LLM(D-LLM…

阅读更多 →
LLaVA-RE: Binary Image-Text Relevancy Evaluation with Multimodal Large Language Model 2026/11/15 4:06:36

LLaVA-RE: Binary Image-Text Relevancy Evaluation with Multimodal Large Language Model

文章主要内容和创新点 主要内容 本文聚焦于二进制图像-文本相关性评估任务(判断图像与文本“相关”或“不相关”),针对该任务中文本格式多样、相关性定义随场景变化等挑战,提出了基于多模态大语言模型(MLLM)的解决方案LLaVA-RE。 模型设计:LLaVA-RE基于LLaVA 1.5架构,…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉