CSharp: Command Pattern

前言

本文基于 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&lt;BusinessOperationCommand&gt; logger)
    {
        _logger = logger;
        _commandId = Guid.NewGuid();
    }

    public string CommandName =&gt; "业务";
    public Guid CommandId =&gt; _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&lt;DesignDrawCommand&gt; logger)
    {
        _logger = logger;
        _commandId = Guid.NewGuid();
    }

    public string CommandName =&gt; "设计制图";
    public Guid CommandId =&gt; _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&lt;FinanceBusinessCommand&gt; logger)
    {
        _logger = logger;
        _commandId = Guid.NewGuid();
    }

    public string CommandName =&gt; "财务";
    public Guid CommandId =&gt; _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&lt;HumanAdminCommand&gt; logger)
    {
        _logger = logger;
        _commandId = Guid.NewGuid();
    }

    public string CommandName =&gt; "人事行政";
    public Guid CommandId =&gt; _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&lt;ItSupportCommand&gt; logger)
    {
        _logger = logger;
        _commandId = Guid.NewGuid();
    }

    public string CommandName =&gt; "IT";
    public Guid CommandId =&gt; _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; }
    /// &lt;summary&gt;
    /// 业务订单号
    /// &lt;/summary&gt;
    public string OrderNo { get; set; } = string.Empty;

    /// &lt;summary&gt;
    /// 产品物料编码
    /// &lt;/summary&gt;
    public string ItemCode { get; set; } = string.Empty;

    /// &lt;summary&gt;
    /// 订单总金额
    /// &lt;/summary&gt;
    public decimal TotalAmount { get; set; }

    /// &lt;summary&gt;
    /// 当前业务流程节点
    /// &lt;/summary&gt;
    public string CurrentProcessNode { get; set; } = string.Empty;

    /// &lt;summary&gt;
    /// 是否完成全流程
    /// &lt;/summary&gt;
    public bool IsWholeFlowFinished { get; set; }

    /// &lt;summary&gt;
    /// 创建时间
    /// &lt;/summary&gt;
    public DateTime CreateTime { get; set; } = DateTime.Now;

    /// &lt;summary&gt;
    /// 更新时间
    /// &lt;/summary&gt;
    public DateTime UpdateTime { get; set; } = DateTime.Now;

    /// &lt;summary&gt;
    /// 更新流程节点状态
    /// &lt;/summary&gt;
    /// &lt;param name="nodeName"&gt;节点名称&lt;/param&gt;
    public void UpdateProcessNode(string nodeName)
    {
        CurrentProcessNode = nodeName;
        UpdateTime = DateTime.Now;
    }

    /// &lt;summary&gt;
    /// 标记整个业务流程完成
    /// &lt;/summary&gt;
    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;
    /// &lt;summary&gt;
    /// 重试策略
    /// &lt;/summary&gt;
    private readonly AsyncRetryPolicy _retryPolicy;

    /// &lt;summary&gt;
    /// 熔断器策略
    /// &lt;/summary&gt;
    private readonly AsyncCircuitBreakerPolicy _circuitBreakerPolicy;
    #endregion

    #region 构造函数
    /// &lt;summary&gt;
    /// 基类构造,初始化重试、熔断策略
    /// &lt;/summary&gt;
    /// &lt;param name="logger"&gt;日志实例&lt;/param&gt;
    protected BaseResilientAdapter(ILogger logger)
    {
        _logger = logger;

        // 重试策略:最大重试次数,指数退避
        _retryPolicy = Policy
            .Handle&lt;Exception&gt;()
            .WaitAndRetryAsync(SystemConstant.AdapterMaxRetryCount,
                retryAttempt =&gt; TimeSpan.FromMilliseconds(200 * retryAttempt),
                (ex, timespan, retryCount, context) =&gt;
                {
                    _logger.LogWarning(ex, "外部适配器调用发生异常,准备第{RetryCount}次重试,等待{WaitMs}ms", retryCount, timespan.TotalMilliseconds);
                });

        // 熔断器策略:失败阈值,断开时间
        _circuitBreakerPolicy = Policy
            .Handle&lt;Exception&gt;()
            .CircuitBreakerAsync(
                exceptionsAllowedBeforeBreaking: SystemConstant.CircuitBreakerFailureThreshold,
                durationOfBreak: TimeSpan.FromSeconds(SystemConstant.CircuitBreakerBreakDurationSeconds),
                onBreak: (ex, ts) =&gt;
                {
                    _logger.LogError(ex, "外部适配器熔断器已打开,熔断时长:{BreakSeconds}秒", SystemConstant.CircuitBreakerBreakDurationSeconds);
                },
                onReset: () =&gt;
                {
                    _logger.LogInformation("外部适配器熔断器已关闭,恢复正常调用");
                },
                onHalfOpen: () =&gt;
                {
                    _logger.LogInformation("外部适配器熔断器半开状态,放行试探请求");
                });
    }
    #endregion

    /// &lt;summary&gt;
    /// 适配器名称,子类实现
    /// &lt;/summary&gt;
    public abstract string AdapterName { get; }

    /// &lt;summary&gt;
    /// 同步业务数据对外入口:包装重试+熔断策略
    /// &lt;/summary&gt;
    /// &lt;param name="businessDto"&gt;珠宝业务传输对象&lt;/param&gt;
    /// &lt;param name="cancellationToken"&gt;取消令牌&lt;/param&gt;
    /// &lt;returns&gt;适配器同步结果DTO&lt;/returns&gt;
    public async Task&lt;AdapterSyncResultDto&gt; SyncBusinessDataAsync(JewelryBusinessDto businessDto, CancellationToken cancellationToken = default)
    {
        _logger.LogInformation("【{AdapterName}】开始同步业务单据 BusinessOrderNo={BusinessOrderNo}", AdapterName, businessDto.BusinessOrderNo);
        var result = new AdapterSyncResultDto();

        try
        {
            // 策略包装:熔断外层,重试内层
            result = await _circuitBreakerPolicy.ExecuteAsync(async () =&gt;
            {
                return await _retryPolicy.ExecuteAsync(async () =&gt;
                {
                    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;
    }

    /// &lt;summary&gt;
    /// 内部实际同步逻辑,由具体子类重写实现
    /// &lt;/summary&gt;
    /// &lt;param name="businessDto"&gt;珠宝业务传输对象&lt;/param&gt;
    /// &lt;param name="cancellationToken"&gt;取消令牌&lt;/param&gt;
    /// &lt;returns&gt;适配器同步结果DTO&lt;/returns&gt;
    protected abstract Task&lt;AdapterSyncResultDto&gt; 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)
{
}
    /// &lt;summary&gt;
    /// 适配器名称
    /// &lt;/summary&gt;
    public override string AdapterName =&gt; "财务ERP适配器";

    /// &lt;summary&gt;
    /// 内部真实同步逻辑
    /// &lt;/summary&gt;
    /// &lt;param name="businessDto"&gt;珠宝业务传输对象&lt;/param&gt;
    /// &lt;param name="cancellationToken"&gt;取消令牌&lt;/param&gt;
    /// &lt;returns&gt;同步结果DTO&lt;/returns&gt;
    protected override async Task&lt;AdapterSyncResultDto&gt; 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)
{
}
    /// &lt;summary&gt;
    /// 适配器名称
    /// &lt;/summary&gt;
    public override string AdapterName =&gt; "物流服务商适配器";

    /// &lt;summary&gt;
    /// 内部真实同步逻辑
    /// &lt;/summary&gt;
    /// &lt;param name="businessDto"&gt;珠宝业务传输对象&lt;/param&gt;
    /// &lt;param name="cancellationToken"&gt;取消令牌&lt;/param&gt;
    /// &lt;returns&gt;同步结果DTO&lt;/returns&gt;
    protected override async Task&lt;AdapterSyncResultDto&gt; 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)
{
}
    /// &lt;summary&gt;
    /// 适配器名称
    /// &lt;/summary&gt;
    public override string AdapterName =&gt; "员工培训平台适配器";

    /// &lt;summary&gt;
    /// 内部真实同步逻辑
    /// &lt;/summary&gt;
    /// &lt;param name="businessDto"&gt;珠宝业务传输对象&lt;/param&gt;
    /// &lt;param name="cancellationToken"&gt;取消令牌&lt;/param&gt;
    /// &lt;returns&gt;同步结果DTO&lt;/returns&gt;
    protected override async Task&lt;AdapterSyncResultDto&gt; 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);
    /// &lt;summary&gt;
    /// 对日志消息内容做脱敏处理
    /// &lt;/summary&gt;
    /// &lt;param name="logMessage"&gt;原始日志消息&lt;/param&gt;
    /// &lt;returns&gt;脱敏之后日志文本&lt;/returns&gt;
    public static string MaskSensitiveData(string logMessage)
    {
        if (string.IsNullOrEmpty(logMessage))
            return logMessage;

        //手机号脱敏:中间4位替换为****
        var masked = _phoneRegex.Replace(logMessage, m =&gt;
        {
            var phone = m.Value;
            return phone[..3] + "****" + phone[7..];
        });
        return masked;
    }

    /// &lt;summary&gt;
    /// 包装ILogger,写日志前自动脱敏消息
    /// &lt;/summary&gt;
    /// &lt;typeparam name="T"&gt;日志泛型类型&lt;/typeparam&gt;
    /// &lt;param name="innerLogger"&gt;原始ILogger&lt;/param&gt;
    /// &lt;returns&gt;脱敏日志包装对象&lt;/returns&gt;
    public static ILogger&lt;T&gt; CreateMaskedLogger&lt;T&gt;(ILogger&lt;T&gt; innerLogger)
    {
        return new MaskedLoggerWrapper&lt;T&gt;(innerLogger);
    }

    /// &lt;summary&gt;
    /// 日志脱敏包装内部类
    /// &lt;/summary&gt;
    /// &lt;typeparam name="T"&gt;&lt;/typeparam&gt;
    private class MaskedLoggerWrapper&lt;T&gt; : ILogger&lt;T&gt;
    {
        private readonly ILogger&lt;T&gt; _inner;

        public MaskedLoggerWrapper(ILogger&lt;T&gt; inner)
        {
            _inner = inner;
        }

        public IDisposable? BeginScope&lt;TState&gt;(TState state) where TState : notnull =&gt; _inner.BeginScope(state);

        public bool IsEnabled(LogLevel logLevel) =&gt; _inner.IsEnabled(logLevel);

        public void Log&lt;TState&gt;(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func&lt;TState, Exception?, string&gt; formatter)
        {
            if (!IsEnabled(logLevel)) return;
            var rawMsg = formatter(state, exception);
            var maskMsg = MaskSensitiveData(rawMsg);
            _inner.Log(logLevel, eventId, maskMsg, exception, (s, e) =&gt; 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();
    }

    /// &lt;summary&gt;
    /// 静态统一日志内容生成方法,控制台日志也复用这套格式
    /// &lt;/summary&gt;
    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;
}
    /// &lt;summary&gt;
    /// 创建业务活动追踪Span
    /// &lt;/summary&gt;
    /// &lt;param name="activitySource"&gt;活动源&lt;/param&gt;
    /// &lt;param name="operationName"&gt;操作名称&lt;/param&gt;
    /// &lt;returns&gt;活动对象&lt;/returns&gt;
    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
{

    /// &lt;summary&gt;
    /// 应用程序活动源,OpenTelemetry链路追踪
    /// &lt;/summary&gt;
    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) =&gt;
            {
                loggingBuilder.ClearProviders();

                //控制台日志
                loggingBuilder.AddConsole();

                //Karambolo v4 文件日志,格式与控制台完全一致
                loggingBuilder.AddFile(fileBuilder =&gt;
                {
                    fileBuilder.RootPath = Path.GetFullPath(SystemConstant.LogRootDirectory);
                    fileBuilder.Files = new[]
                    {
                        new LogFileOptions
                        {
                            Path = $"&lt;date:{SystemConstant.LogDateFolderFormat}&gt;/{SystemConstant.LogFileNamePattern}-&lt;counter&gt;.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) =&gt;
            {
                #region OpenTelemetry链路追踪注册
                services.AddJewelryOpenTelemetry();
                #endregion

                #region 注册领域命令 IJewelryCommand
                services.AddTransient&lt;IJewelryCommand, MaterialPurchaseVerifyCommand&gt;();
                services.AddTransient&lt;IJewelryCommand, DesignDrawCommand&gt;();
                services.AddTransient&lt;IJewelryCommand, ProduceProcessCommand&gt;();
                services.AddTransient&lt;IJewelryCommand, QualityInspectCommand&gt;();
                services.AddTransient&lt;IJewelryCommand, PackagingCommand&gt;();
                services.AddTransient&lt;IJewelryCommand, LogisticsCommand&gt;();
                services.AddTransient&lt;IJewelryCommand, FinanceBusinessCommand&gt;();
                services.AddTransient&lt;IJewelryCommand, MarketingPromoteCommand&gt;();
                services.AddTransient&lt;IJewelryCommand, BusinessOperationCommand&gt;();
                services.AddTransient&lt;IJewelryCommand, HumanAdminCommand&gt;();
                services.AddTransient&lt;IJewelryCommand, ItSupportCommand&gt;();
                services.AddTransient&lt;IJewelryCommand, TrainingCommand&gt;();
                #endregion

                #region 应用层服务注册
                services.AddTransient&lt;JewelryBusinessCommandScheduler&gt;();
                services.AddSingleton&lt;ICommandQueueService, MemoryCommandQueueService&gt;();
                services.AddTransient&lt;HangfireJobService&gt;();
                #endregion

                #region 适配器注册
                services.AddTransient&lt;IExternalSystemAdapter, FinanceSystemAdapter&gt;();
                services.AddTransient&lt;IExternalSystemAdapter, LogisticsSystemAdapter&gt;();
                services.AddTransient&lt;IExternalSystemAdapter, TrainingSystemAdapter&gt;();
                #endregion

                #region Hangfire内存存储,用于演示;生产环境替换SqlServer
                services.AddHangfire(config =&gt;
                {
                    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&lt;ILogger&lt;CommandBll&gt;&gt;();
        var scheduler = sp.GetRequiredService&lt;JewelryBusinessCommandScheduler&gt;();
        var queueService = sp.GetRequiredService&lt;ICommandQueueService&gt;();
        var hangfireJob = sp.GetRequiredService&lt;HangfireJobService&gt;();

        //启动命令队列后台消费循环
        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&lt;IJewelryCommand&gt;();
        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&lt;IExternalSystemAdapter&gt;();
        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();
    }
}
}

输出

相关推荐
心易行者1 小时前
Python自动化测试7步落地法:用python在线运行省掉90%环境配置时间
java·开发语言·人工智能·python·log4j·ai编程
tedcloud1231 小时前
OpenLogi 怎么搭建?用 Rust 打造一个轻量的 Logitech 外设管理工具
linux·运维·服务器·开发语言·后端·rust·开源
Tanjia_kiki2 小时前
谷歌浏览器中F12编辑并重发请求
开发语言·前端·javascript
程序猿编码2 小时前
基于GGML的C++17轻量化语音推理引擎:说话人识别与语音分析技术全解析
开发语言·c++·pytorch·深度学习·神经网络·大模型
学长毕业设计2 小时前
基于SpringBoot的健康食谱管理系统的设计与实现(源码+文档+讲解视频)
java·spring boot·后端
传奇开心果编程2 小时前
【Rust入门知识点学与练】第5课:函数
开发语言·学习·rust
秋田君3 小时前
Qt_QMediaPlayer类与QMediaPlaylist类
开发语言·qt
逃逸线LOF3 小时前
Spring的AOP简介
java·后端·spring
统计学小王子3 小时前
数学建模国赛倒计时6天——《软件工具(R语言精讲)》
开发语言·数学建模·r语言