前言
本文基于 C# .NET 10 与 Visual Studio 2026,深入讲解命令模式(Command Pattern)在珠宝企业级业务系统中的应用。通过一个完整的珠宝业务流水线案例,展示命令模式如何解耦请求发送者与执行者,并结合依赖注入、日志、OpenTelemetry、Hangfire 与 Polly 等企业级技术栈,构建可扩展、可回滚、可监控的业务系统。
一、命令模式基础
**写作重点:**从设计模式的定义出发,说明命令模式属于行为型模式,核心目标是「将请求封装为对象」,从而支持参数化、排队、记录日志以及撤销操作。
- 1.1 命令模式的定义与适用场景
- 1.2 命令模式的四个核心角色:Command、ConcreteCommand、Invoker、Receiver
- 1.3 命令模式 UML 类图与调用时序
- 1.4 命令模式与策略模式、模板方法模式的区别
二、珠宝业务系统架构设计
**写作重点:**结合项目结构图,说明如何将命令模式落地到分层架构(领域层、应用层、基础设施层),并介绍命令接口、具体命令、调度器与命令队列的设计思路。
- 2.1 项目整体结构:Contracts、Domain、Application、Infrastructure 分层
- 2.2 命令接口 IJewelryCommand 与具体命令实现(业务、设计、财务、人事、IT 等 12 个命令)
- 2.3 命令调度器 JewelryBusinessCommandScheduler:流水线编排与异常回滚
- 2.4 内存命令队列 MemoryCommandQueueService:异步消费与后台循环
- 2.5 领域实体 JewelryOrderDomain 与业务状态流转
三、企业级技术栈集成
**写作重点:**展示命令模式如何与 .NET 生态中的日志、链路追踪、后台任务、容错机制无缝集成,体现生产级代码的工程化能力。
- 3.1 依赖注入:瞬态命令注册与服务域隔离
- 3.2 日志体系:控制台 + Karambolo 文件日志,统一格式与脱敏
- 3.3 OpenTelemetry 链路追踪:ActivitySource 与 Span 埋点
- 3.4 Hangfire 后台任务:延迟任务调度与内存存储演示
- 3.5 Polly 容错:重试策略与熔断器在外部适配器中的应用
四、运行演示与效果
**写作重点:**给出完整的调用代码(CommandBll.Demo),展示四种执行方式:同步流水线、内存队列、Hangfire 延迟任务、适配器同步,并附上运行输出截图。
- 4.1 完整调用代码与依赖包清单
- 4.2 四种执行方式对比与适用场景
- 4.3 运行结果截图与日志解读
总结
**写作重点:**回顾命令模式在本项目中的价值:解耦、可扩展、可回滚、可监控;总结工程化要点(DI、日志、追踪、容错),并给出后续优化方向(如改用 Redis 队列、持久化命令日志、分布式事务补偿)。
参考资料
- GoF《设计模式:可复用面向对象软件的基础》
- Microsoft 官方文档:.NET 依赖注入、日志、OpenTelemetry、Hangfire、Polly
- 命令模式相关博客与开源项目(CSharpDesignPattern)
项目结构:

cs
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:命令模式 Command Pattern 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/08/08 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : BusinessOperationCommand.cs
*/
using CommandPattern.Contracts.Commands;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Text;
namespace CommandPattern.Domain.Commands
{
/// <summary>
/// 命令模式具体命令:门店业务
/// 职责:销售接单、客户接待、订单处理
/// </summary>
public class BusinessOperationCommand : IJewelryCommand
{
private readonly ILogger<BusinessOperationCommand> _logger;
private readonly Guid _commandId;
public BusinessOperationCommand(ILogger<BusinessOperationCommand> logger)
{
_logger = logger;
_commandId = Guid.NewGuid();
}
public string CommandName => "业务";
public Guid CommandId => _commandId;
public async Task ExecuteAsync(CancellationToken cancellationToken = default)
{
_logger.LogDebug("【{CommandName}】 CommandId={CommandId},门店业务:客户接待、销售接单、订单处理", CommandName, CommandId);
await Task.Delay(15, cancellationToken);
_logger.LogInformation("【{CommandName}】 CommandId={CommandId},门店业务处理完成", CommandName, CommandId);
}
public async Task UndoAsync(CancellationToken cancellationToken = default)
{
_logger.LogWarning("【{CommandName}】 CommandId={CommandId},回滚:作废业务订单", CommandName, CommandId);
await Task.Delay(8, cancellationToken);
}
}
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:命令模式 Command Pattern 行为模式 Behavioral Patterns
Author : geovindu,Geovin Du 涂聚文.
IDE : vs2026 c# .net 10
os : windows 10
database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
Datetime : 2026/08/08 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : DesignDrawCommand.cs
*/
using CommandPattern.Contracts.Commands;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Text;
namespace CommandPattern.Domain.Commands
{
/// <summary>
/// 命令模式具体命令:设计制图
/// 职责:首饰3D建模、宝石点位排版、输出加工图纸归档
/// </summary>
public class DesignDrawCommand : IJewelryCommand
{
private readonly ILogger<DesignDrawCommand> _logger;
private readonly Guid _commandId;
public DesignDrawCommand(ILogger<DesignDrawCommand> logger)
{
_logger = logger;
_commandId = Guid.NewGuid();
}
public string CommandName => "设计制图";
public Guid CommandId => _commandId;
public async Task ExecuteAsync(CancellationToken cancellationToken = default)
{
_logger.LogDebug("【{CommandName}】 CommandId={CommandId},开始3D首饰建模、宝石点位排版,输出加工图纸", CommandName, CommandId);
await Task.Delay(15, cancellationToken);
_logger.LogInformation("【{CommandName}】 CommandId={CommandId},首饰设计制图完成,3D模型文件归档", CommandName, CommandId);
}
public async Task UndoAsync(CancellationToken cancellationToken = default)
{
_logger.LogWarning("【{CommandName}】 CommandId={CommandId},回滚:作废本次设计图纸与模型", CommandName, CommandId);
await Task.Delay(8, cancellationToken);
}
}
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:命令模式 Command Pattern 行为模式 Behavioral Patterns
Author : geovindu,Geovin Du 涂聚文.
IDE : vs2026 c# .net 10
os : windows 10
database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
Datetime : 2026/08/08 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : FinanceBusinessCommand.cs
*/
using CommandPattern.Contracts.Commands;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Text;
namespace CommandPattern.Domain.Commands
{
/// <summary>
/// 命令模式具体命令:财务
/// 职责:采购结算、销售记账、成本核算、发票处理
/// </summary>
public class FinanceBusinessCommand : IJewelryCommand
{
private readonly ILogger<FinanceBusinessCommand> _logger;
private readonly Guid _commandId;
public FinanceBusinessCommand(ILogger<FinanceBusinessCommand> logger)
{
_logger = logger;
_commandId = Guid.NewGuid();
}
public string CommandName => "财务";
public Guid CommandId => _commandId;
public async Task ExecuteAsync(CancellationToken cancellationToken = default)
{
_logger.LogDebug("【{CommandName}】 CommandId={CommandId},财务业务处理:采购结算、销售记账、成本核算", CommandName, CommandId);
await Task.Delay(15, cancellationToken);
_logger.LogInformation("【{CommandName}】 CommandId={CommandId},账务处理完成,同步外部财务适配器", CommandName, CommandId);
}
public async Task UndoAsync(CancellationToken cancellationToken = default)
{
_logger.LogWarning("【{CommandName}】 CommandId={CommandId},回滚:冲销本次业务账务", CommandName, CommandId);
await Task.Delay(8, cancellationToken);
}
}
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:命令模式 Command Pattern 行为模式 Behavioral Patterns
Author : geovindu,Geovin Du 涂聚文.
IDE : vs2026 c# .net 10
os : windows 10
database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
Datetime : 2026/08/08 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : HumanAdminCommand.cs
*/
using CommandPattern.Contracts.Commands;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Text;
namespace CommandPattern.Domain.Commands
{
/// <summary>
/// 命令模式具体命令:人事行政
/// 职责:考勤、人事档案、行政办公流程
/// </summary>
public class HumanAdminCommand : IJewelryCommand
{
private readonly ILogger<HumanAdminCommand> _logger;
private readonly Guid _commandId;
public HumanAdminCommand(ILogger<HumanAdminCommand> logger)
{
_logger = logger;
_commandId = Guid.NewGuid();
}
public string CommandName => "人事行政";
public Guid CommandId => _commandId;
public async Task ExecuteAsync(CancellationToken cancellationToken = default)
{
_logger.LogDebug("【{CommandName}】 CommandId={CommandId},人事行政业务:考勤、人事档案、行政流程", CommandName, CommandId);
await Task.Delay(15, cancellationToken);
_logger.LogInformation("【{CommandName}】 CommandId={CommandId},人事行政业务处理完成", CommandName, CommandId);
}
public async Task UndoAsync(CancellationToken cancellationToken = default)
{
_logger.LogWarning("【{CommandName}】 CommandId={CommandId},回滚:撤销人事行政变更", CommandName, CommandId);
await Task.Delay(8, cancellationToken);
}
}
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:命令模式 Command Pattern 行为模式 Behavioral Patterns
Author : geovindu,Geovin Du 涂聚文.
IDE : vs2026 c# .net 10
os : windows 10
database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
Datetime : 2026/08/08 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : ItSupportCommand.cs
*/
using CommandPattern.Contracts.Commands;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Text;
namespace CommandPattern.Domain.Commands
{
/// <summary>
/// 命令模式具体命令:IT运维
/// 职责:系统维护、设备运维、数据备份
/// </summary>
public class ItSupportCommand : IJewelryCommand
{
private readonly ILogger<ItSupportCommand> _logger;
private readonly Guid _commandId;
public ItSupportCommand(ILogger<ItSupportCommand> logger)
{
_logger = logger;
_commandId = Guid.NewGuid();
}
public string CommandName => "IT";
public Guid CommandId => _commandId;
public async Task ExecuteAsync(CancellationToken cancellationToken = default)
{
_logger.LogDebug("【{CommandName}】 CommandId={CommandId},IT运维:系统维护、设备巡检、数据备份", CommandName, CommandId);
await Task.Delay(15, cancellationToken);
_logger.LogInformation("【{CommandName}】 CommandId={CommandId},IT运维业务处理完成", CommandName, CommandId);
}
public async Task UndoAsync(CancellationToken cancellationToken = default)
{
_logger.LogWarning("【{CommandName}】 CommandId={CommandId},回滚:撤销IT系统变更", CommandName, CommandId);
await Task.Delay(8, cancellationToken);
}
}
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:命令模式 Command Pattern 行为模式 Behavioral Patterns
Author : geovindu,Geovin Du 涂聚文.
IDE : vs2026 c# .net 10
os : windows 10
database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
Datetime : 2026/08/08 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : JewelryOrderDomain.cs
*/
using System;
using System.Collections.Generic;
using System.Text;
namespace CommandPattern.Domain.DomainModels
{
/// <summary>
/// 珠宝订单领域实体
/// 领域层业务模型,承载业务状态
/// </summary>
public class JewelryOrderDomain
{
/// <summary>
/// 订单内部主键ID
/// </summary>
public long Id { get; set; }
/// <summary>
/// 业务订单号
/// </summary>
public string OrderNo { get; set; } = string.Empty;
/// <summary>
/// 产品物料编码
/// </summary>
public string ItemCode { get; set; } = string.Empty;
/// <summary>
/// 订单总金额
/// </summary>
public decimal TotalAmount { get; set; }
/// <summary>
/// 当前业务流程节点
/// </summary>
public string CurrentProcessNode { get; set; } = string.Empty;
/// <summary>
/// 是否完成全流程
/// </summary>
public bool IsWholeFlowFinished { get; set; }
/// <summary>
/// 创建时间
/// </summary>
public DateTime CreateTime { get; set; } = DateTime.Now;
/// <summary>
/// 更新时间
/// </summary>
public DateTime UpdateTime { get; set; } = DateTime.Now;
/// <summary>
/// 更新流程节点状态
/// </summary>
/// <param name="nodeName">节点名称</param>
public void UpdateProcessNode(string nodeName)
{
CurrentProcessNode = nodeName;
UpdateTime = DateTime.Now;
}
/// <summary>
/// 标记整个业务流程完成
/// </summary>
public void MarkWholeFlowCompleted()
{
IsWholeFlowFinished = true;
UpdateTime = DateTime.Now;
}
}
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:命令模式 Command Pattern 行为模式 Behavioral Patterns
Author : geovindu,Geovin Du 涂聚文.
IDE : vs2026 c# .net 10
os : windows 10
database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
Datetime : 2026/08/08 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : BaseResilientAdapter.cs
*/
using CommandPattern.Contracts.Adapters;
using CommandPattern.Contracts.Constants;
using CommandPattern.Contracts.Dto;
using Microsoft.Extensions.Logging;
using Polly;
using Polly.CircuitBreaker;
using Polly.Retry;
using System;
using System.Collections.Generic;
using System.Text;
namespace CommandPattern.Infrastructure.Adapters
{
/// <summary>
/// 适配器基类:内置Polly重试、熔断降级逻辑
/// 所有外部系统适配器继承该基类,统一拥有容错能力
/// </summary>
public abstract class BaseResilientAdapter : IExternalSystemAdapter
{
#region 私有字段
/// <summary>
/// 日志对象
/// </summary>
protected readonly ILogger _logger;
/// <summary>
/// 重试策略
/// </summary>
private readonly AsyncRetryPolicy _retryPolicy;
/// <summary>
/// 熔断器策略
/// </summary>
private readonly AsyncCircuitBreakerPolicy _circuitBreakerPolicy;
#endregion
#region 构造函数
/// <summary>
/// 基类构造,初始化重试、熔断策略
/// </summary>
/// <param name="logger">日志实例</param>
protected BaseResilientAdapter(ILogger logger)
{
_logger = logger;
// 重试策略:最大重试次数,指数退避
_retryPolicy = Policy
.Handle<Exception>()
.WaitAndRetryAsync(SystemConstant.AdapterMaxRetryCount,
retryAttempt => TimeSpan.FromMilliseconds(200 * retryAttempt),
(ex, timespan, retryCount, context) =>
{
_logger.LogWarning(ex, "外部适配器调用发生异常,准备第{RetryCount}次重试,等待{WaitMs}ms", retryCount, timespan.TotalMilliseconds);
});
// 熔断器策略:失败阈值,断开时间
_circuitBreakerPolicy = Policy
.Handle<Exception>()
.CircuitBreakerAsync(
exceptionsAllowedBeforeBreaking: SystemConstant.CircuitBreakerFailureThreshold,
durationOfBreak: TimeSpan.FromSeconds(SystemConstant.CircuitBreakerBreakDurationSeconds),
onBreak: (ex, ts) =>
{
_logger.LogError(ex, "外部适配器熔断器已打开,熔断时长:{BreakSeconds}秒", SystemConstant.CircuitBreakerBreakDurationSeconds);
},
onReset: () =>
{
_logger.LogInformation("外部适配器熔断器已关闭,恢复正常调用");
},
onHalfOpen: () =>
{
_logger.LogInformation("外部适配器熔断器半开状态,放行试探请求");
});
}
#endregion
/// <summary>
/// 适配器名称,子类实现
/// </summary>
public abstract string AdapterName { get; }
/// <summary>
/// 同步业务数据对外入口:包装重试+熔断策略
/// </summary>
/// <param name="businessDto">珠宝业务传输对象</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>适配器同步结果DTO</returns>
public async Task<AdapterSyncResultDto> SyncBusinessDataAsync(JewelryBusinessDto businessDto, CancellationToken cancellationToken = default)
{
_logger.LogInformation("【{AdapterName}】开始同步业务单据 BusinessOrderNo={BusinessOrderNo}", AdapterName, businessDto.BusinessOrderNo);
var result = new AdapterSyncResultDto();
try
{
// 策略包装:熔断外层,重试内层
result = await _circuitBreakerPolicy.ExecuteAsync(async () =>
{
return await _retryPolicy.ExecuteAsync(async () =>
{
return await InnerSyncBusinessAsync(businessDto, cancellationToken);
});
});
}
catch (BrokenCircuitException circuitEx)
{
result.IsSuccess = false;
result.Message = $"适配器[{AdapterName}]熔断器打开,拒绝请求";
result.ExceptionDetail = circuitEx.ToString();
_logger.LogError(circuitEx, result.Message);
}
catch (Exception ex)
{
result.IsSuccess = false;
result.Message = $"适配器[{AdapterName}]同步发生未知异常";
result.ExceptionDetail = ex.ToString();
_logger.LogError(ex, result.Message);
}
_logger.LogInformation("【{AdapterName}】同步结束 IsSuccess={IsSuccess} Message={Message}", AdapterName, result.IsSuccess, result.Message);
return result;
}
/// <summary>
/// 内部实际同步逻辑,由具体子类重写实现
/// </summary>
/// <param name="businessDto">珠宝业务传输对象</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>适配器同步结果DTO</returns>
protected abstract Task<AdapterSyncResultDto> InnerSyncBusinessAsync(JewelryBusinessDto businessDto, CancellationToken cancellationToken);
}
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:命令模式 Command Pattern 行为模式 Behavioral Patterns
Author : geovindu,Geovin Du 涂聚文.
IDE : vs2026 c# .net 10
os : windows 10
database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
Datetime : 2026/08/08 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : FinanceSystemAdapter.cs
*/
using CommandPattern.Contracts.Dto;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Text;
namespace CommandPattern.Infrastructure.Adapters
{
/// <summary>
/// 财务系统适配器
/// 对接外部财务ERP系统,推送珠宝业务记账单据
/// </summary>
public class FinanceSystemAdapter : BaseResilientAdapter
{
/// <summary>
/// 构造函数注入日志
/// </summary>
/// <param name="logger">日志实例</param>
public FinanceSystemAdapter(ILogger<FinanceSystemAdapter> logger) : base(logger)
{
}
/// <summary>
/// 适配器名称
/// </summary>
public override string AdapterName => "财务ERP适配器";
/// <summary>
/// 内部真实同步逻辑
/// </summary>
/// <param name="businessDto">珠宝业务传输对象</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>同步结果DTO</returns>
protected override async Task<AdapterSyncResultDto> InnerSyncBusinessAsync(JewelryBusinessDto businessDto, CancellationToken cancellationToken)
{
//模拟调用外部财务ERP接口
await Task.Delay(80, cancellationToken);
return new AdapterSyncResultDto
{
IsSuccess = true,
OuterBusinessNo = $"FIN{Guid.NewGuid():N}",
Message = "财务单据推送成功"
};
}
}
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:命令模式 Command Pattern 行为模式 Behavioral Patterns
Author : geovindu,Geovin Du 涂聚文.
IDE : vs2026 c# .net 10
os : windows 10
database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
Datetime : 2026/08/08 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : LogisticsSystemAdapter.cs
*/
using CommandPattern.Contracts.Dto;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Text;
namespace CommandPattern.Infrastructure.Adapters
{
/// <summary>
/// 物流系统适配器
/// 对接外部物流服务商,生成珠宝货品运单
/// </summary>
public class LogisticsSystemAdapter : BaseResilientAdapter
{
/// <summary>
/// 构造注入日志
/// </summary>
/// <param name="logger">日志实例</param>
public LogisticsSystemAdapter(ILogger<LogisticsSystemAdapter> logger) : base(logger)
{
}
/// <summary>
/// 适配器名称
/// </summary>
public override string AdapterName => "物流服务商适配器";
/// <summary>
/// 内部真实同步逻辑
/// </summary>
/// <param name="businessDto">珠宝业务传输对象</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>同步结果DTO</returns>
protected override async Task<AdapterSyncResultDto> InnerSyncBusinessAsync(JewelryBusinessDto businessDto, CancellationToken cancellationToken)
{
//模拟调用外部物流接口
await Task.Delay(100, cancellationToken);
return new AdapterSyncResultDto
{
IsSuccess = true,
OuterBusinessNo = $"WL{Guid.NewGuid():N}",
Message = "物流运单创建成功"
};
}
}
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:命令模式 Command Pattern 行为模式 Behavioral Patterns
Author : geovindu,Geovin Du 涂聚文.
IDE : vs2026 c# .net 10
os : windows 10
database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
Datetime : 2026/08/08 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : TrainingSystemAdapter.cs
*/
using CommandPattern.Contracts.Dto;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Text;
namespace CommandPattern.Infrastructure.Adapters
{
/// <summary>
/// 培训系统适配器
/// 对接外部员工培训平台,同步珠宝培训记录
/// </summary>
public class TrainingSystemAdapter : BaseResilientAdapter
{
/// <summary>
/// 构造注入日志
/// </summary>
/// <param name="logger">日志实例</param>
public TrainingSystemAdapter(ILogger<TrainingSystemAdapter> logger) : base(logger)
{
}
/// <summary>
/// 适配器名称
/// </summary>
public override string AdapterName => "员工培训平台适配器";
/// <summary>
/// 内部真实同步逻辑
/// </summary>
/// <param name="businessDto">珠宝业务传输对象</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>同步结果DTO</returns>
protected override async Task<AdapterSyncResultDto> InnerSyncBusinessAsync(JewelryBusinessDto businessDto, CancellationToken cancellationToken)
{
//模拟调用外部培训平台接口
await Task.Delay(60, cancellationToken);
return new AdapterSyncResultDto
{
IsSuccess = true,
OuterBusinessNo = $"TR{Guid.NewGuid():N}",
Message = "培训记录同步完成"
};
}
}
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:命令模式 Command Pattern 行为模式 Behavioral Patterns
Author : geovindu,Geovin Du 涂聚文.
IDE : vs2026 c# .net 10
os : windows 10
database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
Datetime : 2026/08/08 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : LogMaskingProcessor.cs
*/
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
namespace CommandPattern.Infrastructure.Logging
{
/// <summary>
/// 日志脱敏处理器
/// 手机号、姓名等敏感信息脱敏,输出日志前替换
/// </summary>
public static class LogMaskingProcessor
{
/// <summary>
/// 手机号正则
/// </summary>
private static readonly Regex _phoneRegex = new Regex(@"1[3-9]\d{9}", RegexOptions.Compiled);
/// <summary>
/// 对日志消息内容做脱敏处理
/// </summary>
/// <param name="logMessage">原始日志消息</param>
/// <returns>脱敏之后日志文本</returns>
public static string MaskSensitiveData(string logMessage)
{
if (string.IsNullOrEmpty(logMessage))
return logMessage;
//手机号脱敏:中间4位替换为****
var masked = _phoneRegex.Replace(logMessage, m =>
{
var phone = m.Value;
return phone[..3] + "****" + phone[7..];
});
return masked;
}
/// <summary>
/// 包装ILogger,写日志前自动脱敏消息
/// </summary>
/// <typeparam name="T">日志泛型类型</typeparam>
/// <param name="innerLogger">原始ILogger</param>
/// <returns>脱敏日志包装对象</returns>
public static ILogger<T> CreateMaskedLogger<T>(ILogger<T> innerLogger)
{
return new MaskedLoggerWrapper<T>(innerLogger);
}
/// <summary>
/// 日志脱敏包装内部类
/// </summary>
/// <typeparam name="T"></typeparam>
private class MaskedLoggerWrapper<T> : ILogger<T>
{
private readonly ILogger<T> _inner;
public MaskedLoggerWrapper(ILogger<T> inner)
{
_inner = inner;
}
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => _inner.BeginScope(state);
public bool IsEnabled(LogLevel logLevel) => _inner.IsEnabled(logLevel);
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
if (!IsEnabled(logLevel)) return;
var rawMsg = formatter(state, exception);
var maskMsg = MaskSensitiveData(rawMsg);
_inner.Log(logLevel, eventId, maskMsg, exception, (s, e) => maskMsg);
}
}
}
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:命令模式 Command Pattern 行为模式 Behavioral Patterns
Author : geovindu,Geovin Du 涂聚文.
IDE : vs2026 c# .net 10
os : windows 10
database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
Datetime : 2026/08/08 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : UniformLogTextBuilder.cs
*/
using Karambolo.Extensions.Logging.File;
using Microsoft.Extensions.Logging;
using System;
using System.Globalization;
using System.Text;
namespace CommandPattern.Infrastructure.Logging
{
/// <summary>
/// 统一日志格式化器:控制台输出与文件日志输出格式完全保持一致
/// </summary>
public class UniformLogTextBuilder : FileLogEntryTextBuilder
{
public new static readonly UniformLogTextBuilder Instance = new();
private const string TimestampFormat = "yyyy-MM-dd HH:mm:ss.fff";
public override void BuildEntryText(
StringBuilder sb,
string categoryName,
LogLevel logLevel,
EventId eventId,
string? message,
Exception? exception,
IExternalScopeProvider? scopeProvider,
DateTimeOffset timestamp)
{
sb.Append(timestamp.ToLocalTime().ToString(TimestampFormat, CultureInfo.InvariantCulture));
sb.Append(" | ");
sb.Append(logLevel.ToString().PadRight(6));
sb.Append(" | ");
sb.Append(categoryName);
sb.Append(" | ");
sb.Append(message);
if (exception != null)
sb.Append(" | Exception:").Append(exception);
sb.AppendLine();
}
/// <summary>
/// 静态统一日志内容生成方法,控制台日志也复用这套格式
/// </summary>
public static string BuildUniformLogContent(DateTimeOffset timestamp, LogLevel logLevel, string category, string message, Exception? exception)
{
var timeText = timestamp.ToLocalTime().ToString(TimestampFormat, CultureInfo.InvariantCulture);
var levelText = logLevel.ToString().PadRight(6);
var exceptionText = exception != null ? $" | Exception:{exception}" : string.Empty;
return $"{timeText} | {levelText} | {category} | {message}{exceptionText}";
}
}
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:命令模式 Command Pattern 行为模式 Behavioral Patterns
Author : geovindu,Geovin Du 涂聚文.
IDE : vs2026 c# .net 10
os : windows 10
database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
Datetime : 2026/08/08 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : OpenTelemetrySetup.cs
*/
using Microsoft.Extensions.DependencyInjection;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;
using System;
using System.Collections.Generic;
using System.Text;
namespace CommandPattern.Infrastructure.Telemetry
{
/// <summary>
/// OpenTelemetry链路追踪配置类
/// 负责注册Trace追踪,生成链路Id,供日志输出链路标识
/// </summary>
public static class OpenTelemetrySetup
{
/// <summary>
/// 扩展方法:注册OpenTelemetry链路追踪服务
/// </summary>
/// <param name="services">服务集合</param>
/// <returns>服务集合</returns>
public static IServiceCollection AddJewelryOpenTelemetry(this IServiceCollection services)
{
services.AddOpenTelemetry()
.WithTracing(builder =>
{
builder
.SetResourceBuilder(ResourceBuilder.CreateDefault()
.AddService("JewelryEnterpriseSystem", serviceVersion: "1.0.0"))
.AddSource("JewelryEnterpriseSystem.*")
.AddConsoleExporter();
});
return services;
}
/// <summary>
/// 创建业务活动追踪Span
/// </summary>
/// <param name="activitySource">活动源</param>
/// <param name="operationName">操作名称</param>
/// <returns>活动对象</returns>
public static System.Diagnostics.Activity? StartBusinessActivity(System.Diagnostics.ActivitySource activitySource, string operationName)
{
return activitySource.StartActivity(operationName, System.Diagnostics.ActivityKind.Internal);
}
}
}
cs
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:命令模式 Command Pattern 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/08/08 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : IExternalSystemAdapter.cs
*/
using CommandPattern.Contracts.Dto;
using System;
using System.Collections.Generic;
using System.Text;
namespace CommandPattern.Contracts.Adapters
{
/// <summary>
/// 适配器模式统一抽象接口
/// 对接外部异构第三方系统,屏蔽外部接口差异
/// </summary>
public interface IExternalSystemAdapter
{
/// <summary>
/// 适配器名称
/// </summary>
string AdapterName { get; }
/// <summary>
/// 同步业务数据至外部第三方系统
/// </summary>
/// <param name="businessDto">珠宝业务传输对象</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>适配器同步结果DTO</returns>
Task<AdapterSyncResultDto> SyncBusinessDataAsync(JewelryBusinessDto businessDto, CancellationToken cancellationToken = default);
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:命令模式 Command Pattern 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/08/08 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : IJewelryCommand.cs
*/
using System;
using System.Collections.Generic;
using System.Text;
namespace CommandPattern.Contracts.Commands
{
/// <summary>
/// 命令模式抽象接口
/// 所有珠宝业务命令必须实现该接口,职责单一:执行业务、业务回滚
/// </summary>
public interface IJewelryCommand
{
/// <summary>
/// 获取业务命令显示名称
/// </summary>
string CommandName { get; }
/// <summary>
/// 命令唯一标识
/// </summary>
Guid CommandId { get; }
/// <summary>
/// 执行业务逻辑
/// </summary>
/// <param name="cancellationToken">取消令牌,支持任务取消</param>
/// <returns>异步执行任务</returns>
Task ExecuteAsync(CancellationToken cancellationToken = default);
/// <summary>
/// 业务回滚撤销操作,发生异常时调用
/// </summary>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步回滚任务</returns>
Task UndoAsync(CancellationToken cancellationToken = default);
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:命令模式 Command Pattern 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/08/08 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : SystemConstant.cs
*/
using System;
using System.Collections.Generic;
using System.Text;
namespace CommandPattern.Contracts.Constants
{
/// <summary>
/// 系统全局常量定义
/// </summary>
public static class SystemConstant
{
/// <summary>
/// 日志根目录
/// </summary>
public const string LogRootDirectory = "Logs";
/// <summary>
/// 日志日期子文件夹格式
/// </summary>
public const string LogDateFolderFormat = "yyyy-MM-dd";
/// <summary>
/// 日志文件名模板
/// </summary>
public const string LogFileNamePattern = "app-{level}.log";
/// <summary>
/// 适配器最大重试次数
/// </summary>
public const int AdapterMaxRetryCount = 3;
/// <summary>
/// 熔断器允许失败次数
/// </summary>
public const int CircuitBreakerFailureThreshold = 5;
/// <summary>
/// 熔断器断开时长 秒
/// </summary>
public const int CircuitBreakerBreakDurationSeconds = 10;
/// <summary>
/// 命令队列后台消费间隔 ms
/// </summary>
public const int CommandQueueConsumeIntervalMs = 200;
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:命令模式 Command Pattern 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/08/08 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : AdapterSyncResultDto.cs
*/
using System;
using System.Collections.Generic;
using System.Text;
namespace CommandPattern.Contracts.Dto
{
/// <summary>
/// 适配器同步外部系统返回结果DTO
/// </summary>
public class AdapterSyncResultDto
{
/// <summary>
/// 是否同步成功
/// </summary>
public bool IsSuccess { get; set; }
/// <summary>
/// 外部系统返回业务编号
/// </summary>
public string OuterBusinessNo { get; set; } = string.Empty;
/// <summary>
/// 消息描述
/// </summary>
public string Message { get; set; } = string.Empty;
/// <summary>
/// 异常堆栈信息
/// </summary>
public string? ExceptionDetail { get; set; }
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:命令模式 Command Pattern 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/08/08 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : CommandQueueItemDto.cs
*/
using CommandPattern.Contracts.Commands;
using System;
using System.Collections.Generic;
using System.Text;
namespace CommandPattern.Contracts.Dto
{
/// <summary>
/// 命令队列队列项DTO
/// 用于内存队列存放待执行的业务命令
/// </summary>
public class CommandQueueItemDto
{
/// <summary>
/// 队列项唯一ID
/// </summary>
public Guid QueueItemId { get; set; }
/// <summary>
/// 命令实例
/// </summary>
public IJewelryCommand Command { get; set; }
/// <summary>
/// 关联业务DTO
/// </summary>
public JewelryBusinessDto BusinessDto { get; set; }
/// <summary>
/// 入队时间
/// </summary>
public DateTime EnqueueTime { get; set; }
/// <summary>
/// 是否延迟执行
/// </summary>
public bool IsDelayExecute { get; set; }
/// <summary>
/// 延迟执行时间戳
/// </summary>
public DateTimeOffset DelayExecuteAt { get; set; }
/// <summary>
/// 重试次数
/// </summary>
public int RetryCount { get; set; }
public CommandQueueItemDto(IJewelryCommand command, JewelryBusinessDto businessDto)
{
QueueItemId = Guid.NewGuid();
Command = command;
BusinessDto = businessDto;
EnqueueTime = DateTime.Now;
RetryCount = 0;
}
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:命令模式 Command Pattern 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/08/08 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : JewelryBusinessDto.cs
*/
using System;
using System.Collections.Generic;
using System.Text;
namespace CommandPattern.Contracts.Dto
{
/// <summary>
/// 珠宝业务通用传输DTO
/// 在命令、适配器、队列之间流转业务数据
/// </summary>
public class JewelryBusinessDto
{
/// <summary>
/// 业务单据编号
/// </summary>
public string BusinessOrderNo { get; set; } = string.Empty;
/// <summary>
/// 业务类型编码
/// </summary>
public string BusinessTypeCode { get; set; } = string.Empty;
/// <summary>
/// 客户手机号(敏感字段,日志需要脱敏)
/// </summary>
public string CustomerPhone { get; set; } = string.Empty;
/// <summary>
/// 客户姓名(敏感字段)
/// </summary>
public string CustomerName { get; set; } = string.Empty;
/// <summary>
/// 原料物料编码
/// </summary>
public string MaterialCode { get; set; } = string.Empty;
/// <summary>
/// 业务金额
/// </summary>
public decimal BusinessAmount { get; set; }
/// <summary>
/// 业务发生时间
/// </summary>
public DateTime BusinessOccurTime { get; set; } = DateTime.Now;
/// <summary>
/// 扩展自定义字典
/// </summary>
public Dictionary<string, object> ExtendData { get; set; } = new();
}
}
cs
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:命令模式 Command Pattern 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/08/08 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : HangfireJobService.cs
*/
using CommandPattern.Contracts.Dto;
using Hangfire;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Text;
namespace CommandPattern.Application.BackgroundJobs
{
/// <summary>
/// Hangfire后台任务服务
/// 用于延时、定时执行珠宝业务命令,持久化后台任务
/// </summary>
public class HangfireJobService
{
#region 私有字段
/// <summary>
/// 日志对象
/// </summary>
private readonly ILogger<HangfireJobService> _logger;
#endregion
#region 构造函数
/// <summary>
/// 构造注入日志
/// </summary>
/// <param name="logger">日志实例</param>
public HangfireJobService(ILogger<HangfireJobService> logger)
{
_logger = logger;
}
#endregion
/// <summary>
/// 将业务命令入Hangfire延迟后台任务
/// </summary>
/// <param name="queueItemDto">队列项</param>
/// <param name="delay">延迟时间</param>
/// <returns>Hangfire任务ID</returns>
public string ScheduleDelayJob(CommandQueueItemDto queueItemDto, TimeSpan delay)
{
_logger.LogInformation("提交Hangfire延迟后台任务 QueueItemId={QueueItemId} Delay={Delay}", queueItemDto.QueueItemId, delay);
var jobId = BackgroundJob.Schedule(() => ExecuteHangfireCommandJob(queueItemDto), delay);
return jobId;
}
/// <summary>
/// Hangfire执行任务方法,供Hangfire调度调用
/// </summary>
/// <param name="queueItemDto">队列项</param>
public async Task ExecuteHangfireCommandJob(CommandQueueItemDto queueItemDto)
{
_logger.LogInformation("Hangfire后台任务开始执行 QueueItemId={QueueItemId} CommandName={CommandName}",
queueItemDto.QueueItemId, queueItemDto.Command.CommandName);
try
{
await queueItemDto.Command.ExecuteAsync();
_logger.LogInformation("Hangfire后台任务执行成功 QueueItemId={QueueItemId}", queueItemDto.QueueItemId);
}
catch (Exception ex)
{
_logger.LogError(ex, "Hangfire后台任务执行失败 QueueItemId={QueueItemId}", queueItemDto.QueueItemId);
await queueItemDto.Command.UndoAsync();
}
}
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:命令模式 Command Pattern 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/08/08 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : ICommandQueueService.cs
*/
using CommandPattern.Contracts.Dto;
using System;
using System.Collections.Generic;
using System.Text;
namespace CommandPattern.Application.CommandQueue
{
/// <summary>
/// 命令队列服务抽象接口
/// 支持入队、出队、消费,用于异步业务命令处理
/// </summary>
public interface ICommandQueueService
{
/// <summary>
/// 将命令队列项入队
/// </summary>
/// <param name="queueItem">队列项DTO</param>
void Enqueue(CommandQueueItemDto queueItem);
/// <summary>
/// 尝试取出队列项
/// </summary>
/// <returns>队列项,无数据返回null</returns>
CommandQueueItemDto? TryDequeue();
/// <summary>
/// 获取队列当前数量
/// </summary>
int GetQueueCount();
/// <summary>
/// 启动后台消费循环
/// </summary>
/// <param name="cancellationToken">取消令牌</param>
Task StartConsumeLoopAsync(CancellationToken cancellationToken);
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:命令模式 Command Pattern 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/08/08 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : MemoryCommandQueueService.cs
*/
using CommandPattern.Contracts.Constants;
using CommandPattern.Contracts.Dto;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
namespace CommandPattern.Application.CommandQueue
{
/// <summary>
/// 内存命令队列实现,基于ConcurrentQueue,多线程安全,高并发
/// </summary>
public class MemoryCommandQueueService : ICommandQueueService
{
#region 私有字段
/// <summary>
/// 线程安全并发队列
/// </summary>
private readonly ConcurrentQueue<CommandQueueItemDto> _innerQueue;
/// <summary>
/// 日志对象
/// </summary>
private readonly ILogger<MemoryCommandQueueService> _logger;
#endregion
#region 构造函数
/// <summary>
/// 构造注入日志
/// </summary>
/// <param name="logger">日志实例</param>
public MemoryCommandQueueService(ILogger<MemoryCommandQueueService> logger)
{
_logger = logger;
_innerQueue = new ConcurrentQueue<CommandQueueItemDto>();
}
#endregion
/// <summary>
/// 入队操作,线程安全
/// </summary>
/// <param name="queueItem">队列项</param>
public void Enqueue(CommandQueueItemDto queueItem)
{
_innerQueue.Enqueue(queueItem);
_logger.LogDebug("命令队列入队成功 QueueItemId={QueueItemId} CommandName={CommandName}",
queueItem.QueueItemId, queueItem.Command.CommandName);
}
/// <summary>
/// 尝试出队
/// </summary>
/// <returns></returns>
public CommandQueueItemDto? TryDequeue()
{
if (_innerQueue.TryDequeue(out var item))
{
return item;
}
return null;
}
/// <summary>
/// 获取队列当前数量
/// </summary>
/// <returns></returns>
public int GetQueueCount()
{
return _innerQueue.Count;
}
/// <summary>
/// 后台循环消费队列
/// </summary>
/// <param name="cancellationToken">取消令牌</param>
/// <returns></returns>
public async Task StartConsumeLoopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("命令队列后台消费循环已启动");
while (!cancellationToken.IsCancellationRequested)
{
var item = TryDequeue();
if (item != null)
{
try
{
_logger.LogDebug("开始消费队列项 QueueItemId={QueueItemId} CommandName={CommandName}",
item.QueueItemId, item.Command.CommandName);
await item.Command.ExecuteAsync(cancellationToken);
_logger.LogInformation("队列项消费完成 QueueItemId={QueueItemId}", item.QueueItemId);
}
catch (Exception ex)
{
_logger.LogError(ex, "队列项消费异常 QueueItemId={QueueItemId}", item.QueueItemId);
try
{
await item.Command.UndoAsync(cancellationToken);
}
catch
{
//回滚异常记录日志即可
}
}
}
else
{
await Task.Delay(SystemConstant.CommandQueueConsumeIntervalMs, cancellationToken);
}
}
_logger.LogInformation("命令队列后台消费循环已退出");
}
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:命令模式 Command Pattern 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/08/08 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : JewelryBusinessCommandScheduler.cs
*/
using CommandPattern.Contracts.Commands;
using CommandPattern.Contracts.Dto;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Text;
namespace CommandPattern.Application.Schedulers
{
/// <summary>
/// 应用层:珠宝业务命令调度器(命令模式调用者)
/// 职责:编排完整珠宝业务流水线,调度命令执行,异常捕获与回滚;线程安全
/// </summary>
public class JewelryBusinessCommandScheduler
{
#region 私有字段
/// <summary>
/// 调度器日志对象
/// </summary>
private readonly ILogger<JewelryBusinessCommandScheduler> _logger;
/// <summary>
/// 服务域工厂,用于瞬态命令解析,避免单例捕获瞬态服务
/// </summary>
private readonly IServiceScopeFactory _serviceScopeFactory;
#endregion
#region 构造函数
/// <summary>
/// 构造注入依赖
/// </summary>
/// <param name="logger">调度器日志</param>
/// <param name="serviceScopeFactory">服务域工厂</param>
public JewelryBusinessCommandScheduler(ILogger<JewelryBusinessCommandScheduler> logger, IServiceScopeFactory serviceScopeFactory)
{
_logger = logger;
_serviceScopeFactory = serviceScopeFactory;
}
#endregion
/// <summary>
/// 执行珠宝完整业务流水线:原料采购核验 →设计制图→加工生产→质检→包装→物流→财务→营销推广→业务→人事行政→IT→培训
/// </summary>
/// <param name="businessDto">业务传输对象</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns></returns>
public async Task RunFullJewelryBusinessPipelineAsync(JewelryBusinessDto businessDto, CancellationToken cancellationToken = default)
{
_logger.LogInformation("开始执行珠宝完整业务流水线,BusinessOrderNo={BusinessOrderNo}", businessDto.BusinessOrderNo);
// 创建独立服务域,解析瞬时命令实例,保证并发隔离
using var scope = _serviceScopeFactory.CreateScope();
var sp = scope.ServiceProvider;
// 业务执行顺序
var commandList = new List<IJewelryCommand>
{
sp.GetRequiredService<IJewelryCommand>(),
sp.GetRequiredService<IJewelryCommand>(),
sp.GetRequiredService<IJewelryCommand>(),
sp.GetRequiredService<IJewelryCommand>(),
sp.GetRequiredService<IJewelryCommand>(),
sp.GetRequiredService<IJewelryCommand>(),
sp.GetRequiredService<IJewelryCommand>(),
sp.GetRequiredService<IJewelryCommand>(),
sp.GetRequiredService<IJewelryCommand>(),
sp.GetRequiredService<IJewelryCommand>(),
sp.GetRequiredService<IJewelryCommand>(),
sp.GetRequiredService<IJewelryCommand>()
};
foreach (var cmd in commandList)
{
if (cancellationToken.IsCancellationRequested)
{
_logger.LogWarning("业务流水线收到取消信号,终止执行");
break;
}
_logger.LogDebug("准备执行业务命令:{CommandName} CommandId={CommandId}", cmd.CommandName, cmd.CommandId);
try
{
await cmd.ExecuteAsync(cancellationToken);
_logger.LogInformation("业务命令执行成功:{CommandName} CommandId={CommandId}", cmd.CommandName, cmd.CommandId);
}
catch (Exception ex)
{
_logger.LogError(ex, "业务命令执行异常:{CommandName} CommandId={CommandId}", cmd.CommandName, cmd.CommandId);
try
{
await cmd.UndoAsync(cancellationToken);
_logger.LogWarning("业务命令已执行回滚:{CommandName} CommandId={CommandId}", cmd.CommandName, cmd.CommandId);
}
catch (Exception undoEx)
{
_logger.LogError(undoEx, "业务命令回滚失败:{CommandName} CommandId={CommandId}", cmd.CommandName, cmd.CommandId);
}
throw;
}
}
_logger.LogInformation("珠宝完整业务流水线全部业务单元处理完毕 BusinessOrderNo={BusinessOrderNo}", businessDto.BusinessOrderNo);
}
}
}
调用:
cs
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:命令模式 Command Pattern 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/07/04 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : CommandBll.cs
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="10.0.0" />
<PackageReference Include="Karambolo.Extensions.Logging.File" Version="3.4.0" />
<PackageReference Include="Hangfire" Version="1.8.14" />
<PackageReference Include="Hangfire.MemoryStorage" Version="1.8.14" />
<PackageReference Include="Polly" Version="8.4.2" />
<PackageReference Include="OpenTelemetry" Version="1.11.2" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.11.2" />
<PackageReference Include="OpenTelemetry.Trace" Version="1.11.2" />
dotnet add package OpenTelemetry
dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Instrumentation.AspNetCore
dotnet add package OpenTelemetry.Instrumentation.Http
dotnet add package OpenTelemetry.Exporter.Console
*/
using System;
using System.Collections.Generic;
using System.Text;
using Hangfire;
using Hangfire.MemoryStorage;
using CommandPattern.Application.BackgroundJobs;
using CommandPattern.Application.CommandQueue;
using CommandPattern.Application.Schedulers;
using CommandPattern.Contracts.Adapters;
using CommandPattern.Contracts.Commands;
using CommandPattern.Contracts.Constants;
using CommandPattern.Contracts.Dto;
using CommandPattern.Domain.Commands;
using CommandPattern.Infrastructure.Adapters;
using CommandPattern.Infrastructure.Logging;
using CommandPattern.Infrastructure.Telemetry;
using Karambolo.Extensions.Logging.File;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace BLL
{
public class CommandBll
{
/// <summary>
/// 应用程序活动源,OpenTelemetry链路追踪
/// </summary>
public static readonly System.Diagnostics.ActivitySource AppActivitySource = new System.Diagnostics.ActivitySource("CSharpDesignPatterns");
public async Task Demo()
{
Console.WriteLine("====珠宝企业级业务系统启动====");
//构建主机
var host = Host.CreateDefaultBuilder()
.ConfigureLogging((context, loggingBuilder) =>
{
loggingBuilder.ClearProviders();
//控制台日志
loggingBuilder.AddConsole();
//Karambolo v4 文件日志,格式与控制台完全一致
loggingBuilder.AddFile(fileBuilder =>
{
fileBuilder.RootPath = Path.GetFullPath(SystemConstant.LogRootDirectory);
fileBuilder.Files = new[]
{
new LogFileOptions
{
Path = $"<date:{SystemConstant.LogDateFolderFormat}>/{SystemConstant.LogFileNamePattern}-<counter>.log"
}
};
fileBuilder.TextBuilder = UniformLogTextBuilder.Instance;
fileBuilder.FileEncodingName = "utf-8";
fileBuilder.FileAccessMode = LogFileAccessMode.OpenTemporarily;
fileBuilder.MaxFileSize = 10 * 1024 * 1024;
});
loggingBuilder.SetMinimumLevel(LogLevel.Debug);
})
.ConfigureServices((context, services) =>
{
#region OpenTelemetry链路追踪注册
services.AddJewelryOpenTelemetry();
#endregion
#region 注册领域命令 IJewelryCommand
services.AddTransient<IJewelryCommand, MaterialPurchaseVerifyCommand>();
services.AddTransient<IJewelryCommand, DesignDrawCommand>();
services.AddTransient<IJewelryCommand, ProduceProcessCommand>();
services.AddTransient<IJewelryCommand, QualityInspectCommand>();
services.AddTransient<IJewelryCommand, PackagingCommand>();
services.AddTransient<IJewelryCommand, LogisticsCommand>();
services.AddTransient<IJewelryCommand, FinanceBusinessCommand>();
services.AddTransient<IJewelryCommand, MarketingPromoteCommand>();
services.AddTransient<IJewelryCommand, BusinessOperationCommand>();
services.AddTransient<IJewelryCommand, HumanAdminCommand>();
services.AddTransient<IJewelryCommand, ItSupportCommand>();
services.AddTransient<IJewelryCommand, TrainingCommand>();
#endregion
#region 应用层服务注册
services.AddTransient<JewelryBusinessCommandScheduler>();
services.AddSingleton<ICommandQueueService, MemoryCommandQueueService>();
services.AddTransient<HangfireJobService>();
#endregion
#region 适配器注册
services.AddTransient<IExternalSystemAdapter, FinanceSystemAdapter>();
services.AddTransient<IExternalSystemAdapter, LogisticsSystemAdapter>();
services.AddTransient<IExternalSystemAdapter, TrainingSystemAdapter>();
#endregion
#region Hangfire内存存储,用于演示;生产环境替换SqlServer
services.AddHangfire(config =>
{
config.SetDataCompatibilityLevel(CompatibilityLevel.Version_180);
config.UseSimpleAssemblyNameTypeSerializer();
config.UseRecommendedSerializerSettings();
config.UseMemoryStorage();
});
services.AddHangfireServer();
#endregion
}).Build();
//获取服务
var scope = host.Services.CreateScope();
var sp = scope.ServiceProvider;
var logger = sp.GetRequiredService<ILogger<CommandBll>>();
var scheduler = sp.GetRequiredService<JewelryBusinessCommandScheduler>();
var queueService = sp.GetRequiredService<ICommandQueueService>();
var hangfireJob = sp.GetRequiredService<HangfireJobService>();
//启动命令队列后台消费循环
var queueCts = new CancellationTokenSource();
_ = queueService.StartConsumeLoopAsync(queueCts.Token);
//构造示例业务DTO,手机号为敏感字段,日志会自动脱敏
var demoBusinessDto = new JewelryBusinessDto
{
BusinessOrderNo = "JEW202609020001",
BusinessTypeCode = "FULL_PROCESS",
CustomerPhone = "13812345678",
CustomerName = "张某某",
MaterialCode = "GEM‑0088",
BusinessAmount = 12800.00m,
BusinessOccurTime = DateTime.Now
};
//示例1:直接执行完整业务流水线
logger.LogInformation("【演示】开始同步执行完整珠宝业务流程");
await scheduler.RunFullJewelryBusinessPipelineAsync(demoBusinessDto);
//示例2:构造命令入内存命令队列异步消费
var demoCommand = sp.GetRequiredService<IJewelryCommand>();
var queueItem = new CommandQueueItemDto(demoCommand, demoBusinessDto);
queueService.Enqueue(queueItem);
//示例3:提交Hangfire延迟后台任务(延迟10秒执行)
var hangFireTaskId = hangfireJob.ScheduleDelayJob(queueItem, TimeSpan.FromSeconds(10));
logger.LogInformation("【演示】Hangfire延迟任务已提交 JobId={JobId}", hangFireTaskId);
//示例4:调用适配器(带重试熔断)
var financeAdapter = sp.GetRequiredService<IExternalSystemAdapter>();
var syncResult = await financeAdapter.SyncBusinessDataAsync(demoBusinessDto);
logger.LogInformation("【演示】适配器同步结果 IsSuccess={IsSuccess}", syncResult.IsSuccess);
logger.LogInformation("系统初始化演示全部完成,按任意键退出程序");
Console.ReadKey();
//释放资源
queueCts.Cancel();
queueCts.Dispose();
scope.Dispose();
await host.StopAsync();
}
}
}
输出
