一、引言
本文围绕珠宝行业企业级全程业务流程的工程化实现展开,重点介绍如何基于 C# .NET 10 与条件变量模式(Condition Variable Pattern)构建一套可扩展、可观测、具备弹性容错能力的订单全流程流水线。文章将从业务建模、流水线引擎设计、弹性适配与日志体系四个维度逐步拆解,帮助读者理解复杂业务系统在并发与故障场景下的落地思路。
二、核心章节
2.1 业务领域建模与实体设计
本节介绍珠宝订单全流程涉及的领域实体划分,包括主订单聚合根、生产主线环节实体(原料、设计、生产、质检、包装、物流)以及支持环节实体(财务、营销、业务、人事、IT、培训),并说明各实体之间的协作关系与状态流转。
- **要点 1:**主订单聚合根与订单状态机设计。
- **要点 2:**生产主线 6 环节与支持环节 6 实体的职责边界。
- **要点 3:**实体字段设计与业务规则(如质检未通过不得进入生产)。
2.2 条件变量流水线引擎与并发协调
本节深入条件变量模式(Condition Variable Pattern)在流水线引擎中的实现原理,讲解如何通过条件变量协调多个生产环节的串行推进与支持环节的并行执行,并给出核心代码片段。
- **要点 1:**条件变量模式的核心思想与线程同步机制。
- **要点 2:**主线流水线(ConditionVariablePipeline)与支持流程并行执行器(SupportWorkflowRunner)的协作方式。
- **要点 3:**节点容量、并发上限与取消令牌(CancellationToken)的工程化处理。
2.3 弹性容错:重试与熔断机制
本节演示如何为外部依赖(如供应商核验、宝石实验室)接入弹性适配器,通过瞬时故障重试与持续故障熔断两种策略,保障流水线在外部服务不稳定时仍能快速失败或自动恢复。
- **要点 1:**适配器模式封装外部服务调用。
- **要点 2:**瞬时故障重试策略(前 2 次失败、第 3 次成功)。
- **要点 3:**熔断器打开与快速失败机制。
三、实战示例
本节给出完整的可运行示例:从项目结构、依赖注入装配、12 个业务环节服务注册,到三阶段演示(正常流程、重试演示、熔断演示)的完整代码与运行结果截图,帮助读者直接复现并观察日志输出。
- **要点 1:**项目结构说明与关键文件清单。
- **要点 2:**通用主机构建与日志分级配置(控制台 + 分级日期文件 + 综合文件)。
- **要点 3:**三阶段运行结果与日志目录结构展示。
四、总结
总结全文核心要点,回顾条件变量模式在业务流程编排中的价值,并给出后续可扩展方向(如持久化、分布式协调、监控告警等)。
4.1 流水线引擎持久化到数据库
当前演示版本中,订单上下文与各环节产出均保存在内存仓储(InMemoryRepository)中,进程重启后数据即丢失。要支撑生产环境,需要将流水线状态持久化到数据库。核心思路是引入「流程实例表」与「环节执行记录表」两张核心表:流程实例表保存订单主键、当前所处环节、整体状态与版本号;环节执行记录表保存每个环节的输入快照、输出结果、执行时间与异常信息。这样既能支持断点续跑,也能为后续审计与对账提供数据基础。
实现上,可以在 WorkflowContext 中增加一个持久化仓储接口,并在每个环节执行完成后异步落库。下面给出一个基于 EF Core 的持久化仓储示例:
cs
public interface IWorkflowStore
{
Task<WorkflowInstance> GetAsync(long orderId, CancellationToken ct);
Task SaveAsync(WorkflowInstance instance, CancellationToken ct);
Task AppendStageAsync(StageExecutionRecord record, CancellationToken ct);
}
public sealed class WorkflowInstance
{
public long OrderId { get; set; }
public int CurrentStageIndex { get; set; }
public string Status { get; set; } = string.Empty;
public string? PayloadJson { get; set; }
public int Version { get; set; }
public DateTime UpdatedAt { get; set; }
}
public sealed class StageExecutionRecord
{
public long Id { get; set; }
public long OrderId { get; set; }
public string StageName { get; set; } = string.Empty;
public bool IsSuccess { get; set; }
public string? ErrorMessage { get; set; }
public DateTime ExecutedAt { get; set; }
}
public sealed class WorkflowDbContext : DbContext
{
public DbSet<WorkflowInstance> WorkflowInstances => Set<WorkflowInstance>();
public DbSet<StageExecutionRecord> StageRecords => Set<StageExecutionRecord>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<WorkflowInstance>(e =>
{
e.HasKey(x => x.OrderId);
e.Property(x => x.PayloadJson).HasColumnType("jsonb");
e.Property(x => x.Version).IsConcurrencyToken();
});
modelBuilder.Entity<StageExecutionRecord>(e =>
{
e.HasKey(x => x.Id);
e.HasIndex(x => new { x.OrderId, x.StageName }).IsUnique();
});
}
}
在流水线引擎中,每个环节执行完成后调用 AppendStageAsync 写入执行记录,并在整条主线推进时更新 WorkflowInstance 的 CurrentStageIndex 与 PayloadJson。利用 Version 作为乐观并发令牌,可避免多实例并发推进同一订单时产生脏写。
4.2 引入分布式协调(Redis 或 ZooKeeper)
当流水线从单机扩展到多节点部署时,条件变量模式依赖的进程内锁与信号量将不再适用。此时需要引入分布式协调组件来统一管理节点容量、分布式锁与环节推进顺序。Redis 与 ZooKeeper 是两种主流选择:Redis 适合对吞吐要求高、允许最终一致性的场景,可通过分布式锁(RedLock)与 Lua 脚本实现原子化的容量扣减;ZooKeeper 则适合强一致、需要临时节点与 Watch 机制感知节点上下线的场景。
以 Redis 为例,可以用分布式锁替代进程内锁,保证同一订单在同一时刻只被一个工作节点推进。下面给出基于 StackExchange.Redis 的分布式锁示例:
cs
public sealed class RedisDistributedLock : IAsyncDisposable
{
private readonly IDatabase _db;
private readonly string _key;
private readonly string _token;
private static readonly TimeSpan Expiry = TimeSpan.FromSeconds(30);
private RedisDistributedLock(IDatabase db, string key, string token)
{
_db = db;
_key = key;
_token = token;
}
public static async Task<RedisDistributedLock?> AcquireAsync(
IDatabase db, string lockKey, TimeSpan? timeout = null)
{
string token = Guid.NewGuid().ToString("N");
bool ok = await db.LockTakeAsync(lockKey, token, Expiry);
if (!ok) return null;
// 若未在超时时间内拿到锁,则返回 null 表示获取失败。
return new RedisDistributedLock(db, lockKey, token);
}
public async ValueTask DisposeAsync()
{
// 仅当 token 匹配时才释放锁,避免误删他人持有的锁。
await _db.LockReleaseAsync(_key, _token);
}
}
// 使用示例:在推进某个订单前先获取分布式锁。
await using var lockHandle = await RedisDistributedLock.AcquireAsync(db, $"order:{orderId}:lock");
if (lockHandle is null)
{
logger.LogWarning("订单#{OrderId} 分布式锁获取失败,稍后重试。", orderId);
return;
}
await pipeline.AdvanceAsync(orderId, ct);
若选择 ZooKeeper,则可以利用其临时顺序节点实现公平锁,并通过 Watch 机制在锁释放时唤醒等待线程。无论选择哪种方案,都需要把原先 ConditionVariablePipeline 中的 SemaphoreSlim 与 Monitor 替换为分布式锁,同时将节点容量计数迁移到 Redis 的原子计数器或 ZooKeeper 的共享节点上。
4.3 集成监控告警系统
生产环境的流水线必须可观测。建议从指标(Metrics)、日志(Logging)与链路追踪(Tracing)三个维度接入监控告警系统。指标方面,可以使用 Prometheus 暴露流水线的核心计数器,例如每秒处理的订单数、各环节平均耗时、重试次数、熔断打开次数与当前熔断状态;日志方面,现有分级文件日志可以继续保留,同时通过 Serilog 或 OpenTelemetry 把结构化日志转发到 Elasticsearch 或 Loki;链路追踪方面,使用 OpenTelemetry 为每个订单生成 TraceId,贯穿主线与支持环节,便于在 Jaeger 或 Zipkin 中定位瓶颈。
下面给出一个基于 Prometheus 的指标采集示例,在流水线引擎中埋点:
cs
public sealed class PipelineMetrics
{
private readonly Counter _ordersStarted;
private readonly Counter _ordersCompleted;
private readonly Counter _stageRetries;
private readonly Counter _circuitBreakerOpened;
private readonly Histogram _stageDuration;
public PipelineMetrics()
{
var meter = new Meter("JewelryWorkflow.Pipeline", "1.0.0");
_ordersStarted = meter.CreateCounter<long>("pipeline.orders.started", "订单启动总数");
_ordersCompleted = meter.CreateCounter<long>("pipeline.orders.completed", "订单完成总数");
_stageRetries = meter.CreateCounter<long>("pipeline.stage.retries", "环节重试次数");
_circuitBreakerOpened = meter.CreateCounter<long>("pipeline.circuit.opened", "熔断器打开次数");
_stageDuration = meter.CreateHistogram<double>("pipeline.stage.duration", "秒", "环节执行耗时");
}
public void OrderStarted() => _ordersStarted.Add(1);
public void OrderCompleted() => _ordersCompleted.Add(1);
public void StageRetried() => _stageRetries.Add(1);
public void CircuitOpened() => _circuitBreakerOpened.Add(1);
public void RecordStageDuration(double seconds) => _stageDuration.Record(seconds);
}
告警规则可以基于这些指标配置,例如:当熔断器打开次数在 5 分钟内超过 10 次时触发 P1 告警;当某环节平均耗时超过基线 3 倍时触发 P2 告警;当订单失败率超过 5% 时触发 P1 告警。通过 Grafana 配置看板,可以实时观察流水线健康度,并在异常发生时第一时间通知值班人员。
总结全文核心要点,回顾条件变量模式在业务流程编排中的价值,并给出后续可扩展方向(如持久化、分布式协调、监控告警等)。
项目结构:

cs
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : BusinessOrder.cs
*/
namespace JewelryWorkflow.Domain.Entities;
/// <summary>
/// 业务跟单实体(业务环节)。
/// <para>记录客户跟单与商务信息:客户等级、商务条款、跟单员与业务状态。</para>
/// </summary>
public sealed class BusinessOrder
{
/// <summary>
/// 业务跟单编号(唯一标识)。
/// </summary>
public string BusinessNo { get; set; } = string.Empty;
/// <summary>
/// 客户等级(例如:VIP、普通、潜在)。
/// </summary>
public string CustomerLevel { get; set; } = string.Empty;
/// <summary>
/// 商务条款(例如:预付 30%,尾款发货前结清)。
/// </summary>
public string Terms { get; set; } = string.Empty;
/// <summary>
/// 跟单员姓名。
/// </summary>
public string AccountManager { get; set; } = string.Empty;
/// <summary>
/// 客户意向度评分(0-100)。
/// </summary>
public int IntentionScore { get; set; }
/// <summary>
/// 是否已确认商务条款。
/// </summary>
public bool IsConfirmed { get; set; }
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:结构型模式 Structural Patterns 装饰器模式 Decorator Pattern 演示业务层
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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : DesignDrawing.cs
*/
namespace JewelryWorkflow.Domain.Entities;
/// <summary>
/// 设计制图实体(设计制图环节)。
/// <para>记录珠宝款式设计图:图纸编号、设计版本、工艺要求与审核状态。</para>
/// </summary>
public sealed class DesignDrawing
{
/// <summary>
/// 图纸编号(唯一标识)。
/// </summary>
public string DrawingNo { get; set; } = string.Empty;
/// <summary>
/// 设计款式名称。
/// </summary>
public string StyleName { get; set; } = string.Empty;
/// <summary>
/// 设计版本号(设计迭代递增)。
/// </summary>
public int Version { get; set; } = 1;
/// <summary>
/// 设计师姓名。
/// </summary>
public string DesignerName { get; set; } = string.Empty;
/// <summary>
/// 工艺要求描述(例如:爪镶、抛光、刻字)。
/// </summary>
public string CraftRequirement { get; set; } = string.Empty;
/// <summary>
/// 是否已审核通过(未通过不得进入生产环节)。
/// </summary>
public bool IsApproved { get; set; }
/// <summary>
/// 审核人姓名。
/// </summary>
public string ApprovedBy { get; set; } = string.Empty;
/// <summary>
/// 设计完成时间。
/// </summary>
public DateTime DesignedAt { get; set; } = DateTime.Now;
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : FinanceInvoice.cs
*/
namespace JewelryWorkflow.Domain.Entities;
/// <summary>
/// 财务发票/结算实体(财务环节)。
/// <para>记录订单的财务结算信息:采购付款、销售收款、发票开具与结算状态。</para>
/// </summary>
public sealed class FinanceInvoice
{
/// <summary>
/// 财务结算单号(唯一标识)。
/// </summary>
public string SettlementNo { get; set; } = string.Empty;
/// <summary>
/// 采购付款金额(元)。
/// </summary>
public decimal PurchasePayment { get; set; }
/// <summary>
/// 销售收款金额(元)。
/// </summary>
public decimal SalesReceipt { get; set; }
/// <summary>
/// 毛利金额(元,销售收款 - 采购付款)。
/// </summary>
public decimal GrossProfit { get; set; }
/// <summary>
/// 发票号码(增值税发票)。
/// </summary>
public string InvoiceNo { get; set; } = string.Empty;
/// <summary>
/// 是否已结算完成。
/// </summary>
public bool IsSettled { get; set; }
/// <summary>
/// 结算完成时间。
/// </summary>
public DateTime? SettledAt { get; set; }
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : HrRecord.cs
*/
namespace JewelryWorkflow.Domain.Entities;
/// <summary>
/// 人事行政记录实体(人事行政环节)。
/// <para>记录订单相关的行政与人事协同信息:经办员工、考勤与行政事务状态。</para>
/// </summary>
public sealed class HrRecord
{
/// <summary>
/// 人事行政记录编号(唯一标识)。
/// </summary>
public string HrNo { get; set; } = string.Empty;
/// <summary>
/// 经办员工工号。
/// </summary>
public string EmployeeNo { get; set; } = string.Empty;
/// <summary>
/// 经办员工姓名。
/// </summary>
public string EmployeeName { get; set; } = string.Empty;
/// <summary>
/// 所属部门(例如:生产部、销售部、设计部)。
/// </summary>
public string Department { get; set; } = string.Empty;
/// <summary>
/// 行政事务描述(例如:会议室预订、差旅安排、盖章用印)。
/// </summary>
public string AdminTask { get; set; } = string.Empty;
/// <summary>
/// 行政事务是否已完成。
/// </summary>
public bool IsCompleted { get; set; }
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : ItAsset.cs
*/
namespace JewelryWorkflow.Domain.Entities;
/// <summary>
/// IT 资产/系统记录实体(IT 环节)。
/// <para>记录订单相关的 IT 协同信息:业务系统单据、设备资产与权限处理状态。</para>
/// </summary>
public sealed class ItAsset
{
/// <summary>
/// IT 记录编号(唯一标识)。
/// </summary>
public string ItNo { get; set; } = string.Empty;
/// <summary>
/// 涉及的业务系统名称(例如:ERP、MES、CRM、WMS)。
/// </summary>
public string SystemName { get; set; } = string.Empty;
/// <summary>
/// 系统单据号(ERP 订单号 / MES 工单号等)。
/// </summary>
public string SystemDocNo { get; set; } = string.Empty;
/// <summary>
/// 涉及的设备资产编号(可为空)。
/// </summary>
public string AssetNo { get; set; } = string.Empty;
/// <summary>
/// 权限处理描述(例如:为跟单员开通 CRM 订单查看权限)。
/// </summary>
public string PermissionTask { get; set; } = string.Empty;
/// <summary>
/// 是否已完成。
/// </summary>
public bool IsCompleted { get; set; }
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : JewelryOrder.cs
*/
using JewelryWorkflow.Domain.Enums;
namespace JewelryWorkflow.Domain.Entities;
/// <summary>
/// 珠宝订单(主订单聚合根)。
/// <para>贯穿全程业务流程的核心实体:一个订单从原料采购核验开始,
/// 依次经过设计、生产、质检、包装、物流,并由财务/营销/业务/人事/IT/培训
/// 等支持环节协同处理。聚合根负责维护订单级状态机。</para>
/// </summary>
public sealed class JewelryOrder
{
/// <summary>
/// 订单唯一编号(流水线上下文与日志中使用的业务主键)。
/// </summary>
public long OrderId { get; set; }
/// <summary>
/// 客户名称。
/// </summary>
public string CustomerName { get; set; } = string.Empty;
/// <summary>
/// 订单商品描述(例如:18K金镶钻戒指 1 克拉)。
/// </summary>
public string ProductDescription { get; set; } = string.Empty;
/// <summary>
/// 订单金额(元)。
/// </summary>
public decimal OrderAmount { get; set; }
/// <summary>
/// 订单创建时间。
/// </summary>
public DateTime CreatedAt { get; set; } = DateTime.Now;
/// <summary>
/// 订单状态(状态机流转:已创建 → 流程执行中 → 已完成/已失败)。
/// </summary>
public OrderStatus Status { get; set; } = OrderStatus.Created;
/// <summary>
/// 当前已完成的流程环节数(用于进度展示与日志输出)。
/// </summary>
public int CompletedStageCount { get; set; }
/// <summary>
/// 全流程完成时间(为 null 表示尚未完成)。
/// </summary>
public DateTime? CompletedAt { get; set; }
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : LogisticsWaybill.cs
*/
namespace JewelryWorkflow.Domain.Entities;
/// <summary>
/// 物流运单实体(物流环节)。
/// <para>记录成品出库后的物流运输信息:承运商、运单号、收件信息与运输状态。</para>
/// </summary>
public sealed class LogisticsWaybill
{
/// <summary>
/// 物流运单号(唯一标识)。
/// </summary>
public string WaybillNo { get; set; } = string.Empty;
/// <summary>
/// 关联的包装单号。
/// </summary>
public string PackageNo { get; set; } = string.Empty;
/// <summary>
/// 承运商名称(例如:顺丰、EMS、DHL)。
/// </summary>
public string CarrierName { get; set; } = string.Empty;
/// <summary>
/// 收货人姓名。
/// </summary>
public string ReceiverName { get; set; } = string.Empty;
/// <summary>
/// 收货地址。
/// </summary>
public string ReceiverAddress { get; set; } = string.Empty;
/// <summary>
/// 是否已发货。
/// </summary>
public bool IsShipped { get; set; }
/// <summary>
/// 发货时间。
/// </summary>
public DateTime? ShippedAt { get; set; }
/// <summary>
/// 运输状态描述(例如:运输中、已签收)。
/// </summary>
public string TrackingStatus { get; set; } = "待发货";
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:结# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : MarketingCampaign.cs
*/
namespace JewelryWorkflow.Domain.Entities;
/// <summary>
/// 营销推广活动实体(营销推广环节)。
/// <para>记录面向该订单/产品的营销活动信息:推广渠道、活动主题、预算与状态。</para>
/// </summary>
public sealed class MarketingCampaign
{
/// <summary>
/// 营销活动编号(唯一标识)。
/// </summary>
public string CampaignNo { get; set; } = string.Empty;
/// <summary>
/// 活动主题名称。
/// </summary>
public string CampaignName { get; set; } = string.Empty;
/// <summary>
/// 推广渠道(例如:抖音、小红书、天猫、线下门店)。
/// </summary>
public string Channel { get; set; } = string.Empty;
/// <summary>
/// 投放预算(元)。
/// </summary>
public decimal Budget { get; set; }
/// <summary>
/// 预估曝光量(次)。
/// </summary>
public long EstimatedImpressions { get; set; }
/// <summary>
/// 活动是否已上线。
/// </summary>
public bool IsLaunched { get; set; }
/// <summary>
/// 活动上线时间。
/// </summary>
public DateTime? LaunchedAt { get; set; }
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : MaterialBatch.cs
*/
using JewelryWorkflow.Domain.Enums;
namespace JewelryWorkflow.Domain.Entities;
/// <summary>
/// 原料批次实体(原料采购核验环节)。
/// <para>记录一批珠宝原料(金料/银料/钻石/宝石等)的采购信息、证书信息与核验状态。</para>
/// </summary>
public sealed class MaterialBatch
{
/// <summary>
/// 原料批次号(唯一标识)。
/// </summary>
public string BatchNo { get; set; } = string.Empty;
/// <summary>
/// 原料类型(例如:足金、18K金、钻石、红宝石)。
/// </summary>
public string MaterialType { get; set; } = string.Empty;
/// <summary>
/// 原料重量(克)。
/// </summary>
public decimal WeightGram { get; set; }
/// <summary>
/// 原料成色(例如足金 999、18K、750)。
/// </summary>
public string Purity { get; set; } = string.Empty;
/// <summary>
/// 供应商名称。
/// </summary>
public string SupplierName { get; set; } = string.Empty;
/// <summary>
/// 采购价格(元)。
/// </summary>
public decimal PurchasePrice { get; set; }
/// <summary>
/// 原料质检证书编号(采购核验时校验)。
/// </summary>
public string CertificateNo { get; set; } = string.Empty;
/// <summary>
/// 核验状态(待核验 → 核验中 → 合格入库/不合格退货)。
/// </summary>
public MaterialStatus Status { get; set; } = MaterialStatus.Pending;
/// <summary>
/// 核验备注(记录核验结果与操作人信息)。
/// </summary>
public string VerifyRemark { get; set; } = string.Empty;
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : PackageInfo.cs
*/
namespace JewelryWorkflow.Domain.Entities;
/// <summary>
/// 包装信息实体(包装环节)。
/// <para>记录成品的包装方案:包装类型、防伪标识、装箱信息。</para>
/// </summary>
public sealed class PackageInfo
{
/// <summary>
/// 包装单号(唯一标识)。
/// </summary>
public string PackageNo { get; set; } = string.Empty;
/// <summary>
/// 关联的质检报告编号。
/// </summary>
public string QualityReportNo { get; set; } = string.Empty;
/// <summary>
/// 包装类型(例如:礼盒装、精品盒、裸钻袋)。
/// </summary>
public string PackageType { get; set; } = string.Empty;
/// <summary>
/// 是否包含防伪标识(二维码/镭射标)。
/// </summary>
public bool HasAntiFakeTag { get; set; }
/// <summary>
/// 装箱数量(件)。
/// </summary>
public int BoxCount { get; set; }
/// <summary>
/// 包装完成时间。
/// </summary>
public DateTime PackagedAt { get; set; } = DateTime.Now;
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : ProductionOrder.cs
*/
namespace JewelryWorkflow.Domain.Entities;
/// <summary>
/// 生产工单实体(加工生产环节)。
/// <para>记录一条珠宝的加工生产任务:工艺路线、投入原料、产出重量与生产状态。</para>
/// </summary>
public sealed class ProductionOrder
{
/// <summary>
/// 生产工单号(唯一标识)。
/// </summary>
public string WorkOrderNo { get; set; } = string.Empty;
/// <summary>
/// 关联的设计图纸编号。
/// </summary>
public string DrawingNo { get; set; } = string.Empty;
/// <summary>
/// 使用的原料批次号。
/// </summary>
public string MaterialBatchNo { get; set; } = string.Empty;
/// <summary>
/// 工艺路线(用分号分隔的工序列表,例如:倒模;执模;镶石;抛光)。
/// </summary>
public string ProcessRoute { get; set; } = string.Empty;
/// <summary>
/// 投入原料重量(克)。
/// </summary>
public decimal InputWeightGram { get; set; }
/// <summary>
/// 产出成品重量(克,一般略小于投入,存在加工损耗)。
/// </summary>
public decimal OutputWeightGram { get; set; }
/// <summary>
/// 加工损耗率(百分比,用于成本核算)。
/// </summary>
public decimal LossRatePercent { get; set; }
/// <summary>
/// 是否生产完成。
/// </summary>
public bool IsFinished { get; set; }
/// <summary>
/// 生产完成时间。
/// </summary>
public DateTime? FinishedAt { get; set; }
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : QualityReport.cs
*/
using JewelryWorkflow.Domain.Enums;
namespace JewelryWorkflow.Domain.Entities;
/// <summary>
/// 质检报告实体(质检环节)。
/// <para>记录成品的质检结果:成色、重量复称、工艺评分与质检等级。
/// 质检等级决定是否放行进入包装环节。</para>
/// </summary>
public sealed class QualityReport
{
/// <summary>
/// 质检报告编号(唯一标识)。
/// </summary>
public string ReportNo { get; set; } = string.Empty;
/// <summary>
/// 关联的生产工单号。
/// </summary>
public string WorkOrderNo { get; set; } = string.Empty;
/// <summary>
/// 质检员姓名。
/// </summary>
public string InspectorName { get; set; } = string.Empty;
/// <summary>
/// 实测成色(例如 999、18K)。
/// </summary>
public string MeasuredPurity { get; set; } = string.Empty;
/// <summary>
/// 复称重量(克)。
/// </summary>
public decimal MeasuredWeightGram { get; set; }
/// <summary>
/// 工艺评分(0-100 分)。
/// </summary>
public int CraftScore { get; set; }
/// <summary>
/// 质检等级(优等/合格/次品/废品)。
/// </summary>
public QualityGrade Grade { get; set; } = QualityGrade.Qualified;
/// <summary>
/// 是否通过质检(优等与合格放行,次品返工,废品报废)。
/// </summary>
public bool IsPassed { get; set; }
/// <summary>
/// 质检完成时间。
/// </summary>
public DateTime InspectedAt { get; set; } = DateTime.Now;
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : TrainingRecord.cs
*/
namespace JewelryWorkflow.Domain.Entities;
/// <summary>
/// 培训记录实体(培训环节)。
/// <para>记录与该订单关联的员工培训信息:课程、学时、讲师与考核结果。</para>
/// </summary>
public sealed class TrainingRecord
{
/// <summary>
/// 培训记录编号(唯一标识)。
/// </summary>
public string TrainingNo { get; set; } = string.Empty;
/// <summary>
/// 课程名称(例如:珠宝鉴定基础、销售话术训练)。
/// </summary>
public string CourseName { get; set; } = string.Empty;
/// <summary>
/// 参训员工姓名。
/// </summary>
public string TraineeName { get; set; } = string.Empty;
/// <summary>
/// 讲师姓名。
/// </summary>
public string LecturerName { get; set; } = string.Empty;
/// <summary>
/// 培训学时(小时)。
/// </summary>
public decimal Hours { get; set; }
/// <summary>
/// 考核是否通过。
/// </summary>
public bool IsPassed { get; set; }
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : MaterialStatus.cs
*/
namespace JewelryWorkflow.Domain.Enums;
/// <summary>
/// 原料批次状态枚举。
/// <para>用于原料采购核验环节的状态流转:待核验 → 核验中 → 合格入库 / 不合格退货。</para>
/// </summary>
public enum MaterialStatus
{
/// <summary>
/// 待核验:原料已到货,等待质检与证书核验。
/// </summary>
Pending = 0,
/// <summary>
/// 核验中:正在执行供应商资质与原料证书核验。
/// </summary>
Verifying = 1,
/// <summary>
/// 合格入库:核验通过,允许进入生产环节。
/// </summary>
Approved = 2,
/// <summary>
/// 不合格退货:核验不通过,退回供应商。
/// </summary>
Rejected = 3
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : BusinessRequestDto.cs
*/
namespace JewelryWorkflow.Domain.Dtos;
/// <summary>
/// 业务环节输入 DTO。
/// <para>由业务端提交:客户等级、商务条款与跟单信息。</para>
/// </summary>
public sealed class BusinessRequestDto
{
/// <summary>
/// 客户等级(例如:VIP、普通、潜在)。
/// </summary>
public string CustomerLevel { get; set; } = string.Empty;
/// <summary>
/// 商务条款(例如:预付 30%,尾款发货前结清)。
/// </summary>
public string Terms { get; set; } = string.Empty;
/// <summary>
/// 跟单员姓名。
/// </summary>
public string AccountManager { get; set; } = string.Empty;
/// <summary>
/// 客户意向度评分(0-100)。
/// </summary>
public int IntentionScore { get; set; }
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : DesignRequestDto.cs
*/
namespace JewelryWorkflow.Domain.Dtos;
/// <summary>
/// 设计制图环节输入 DTO。
/// <para>由设计端提交:款式信息与工艺要求。</para>
/// </summary>
public sealed class DesignRequestDto
{
/// <summary>
/// 设计款式名称(例如:经典六爪钻戒)。
/// </summary>
public string StyleName { get; set; } = string.Empty;
/// <summary>
/// 设计师姓名。
/// </summary>
public string DesignerName { get; set; } = string.Empty;
/// <summary>
/// 工艺要求描述(例如:爪镶、抛光、内圈刻字)。
/// </summary>
public string CraftRequirement { get; set; } = string.Empty;
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : FinanceRequestDto.cs
*/
namespace JewelryWorkflow.Domain.Dtos;
/// <summary>
/// 财务环节输入 DTO。
/// <para>由财务端提交:采购付款、销售收款与开票要求。</para>
/// </summary>
public sealed class FinanceRequestDto
{
/// <summary>
/// 采购付款金额(元)。
/// </summary>
public decimal PurchasePayment { get; set; }
/// <summary>
/// 销售收款金额(元)。
/// </summary>
public decimal SalesReceipt { get; set; }
/// <summary>
/// 是否需要开具增值税发票。
/// </summary>
public bool NeedInvoice { get; set; } = true;
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : HrRequestDto.cs
*/
namespace JewelryWorkflow.Domain.Dtos;
/// <summary>
/// 人事行政环节输入 DTO。
/// <para>由行政端提交:经办员工与行政事务信息。</para>
/// </summary>
public sealed class HrRequestDto
{
/// <summary>
/// 经办员工工号。
/// </summary>
public string EmployeeNo { get; set; } = string.Empty;
/// <summary>
/// 经办员工姓名。
/// </summary>
public string EmployeeName { get; set; } = string.Empty;
/// <summary>
/// 所属部门。
/// </summary>
public string Department { get; set; } = string.Empty;
/// <summary>
/// 行政事务描述(例如:会议室预订、差旅安排、盖章用印)。
/// </summary>
public string AdminTask { get; set; } = string.Empty;
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : ItRequestDto.cs
*/
namespace JewelryWorkflow.Domain.Dtos;
/// <summary>
/// IT 环节输入 DTO。
/// <para>由 IT 端提交:涉及的系统、设备与权限任务。</para>
/// </summary>
public sealed class ItRequestDto
{
/// <summary>
/// 涉及的业务系统名称(例如:ERP、MES、CRM、WMS)。
/// </summary>
public string SystemName { get; set; } = string.Empty;
/// <summary>
/// 涉及的设备资产编号(可为空)。
/// </summary>
public string AssetNo { get; set; } = string.Empty;
/// <summary>
/// 权限处理描述(例如:为跟单员开通 CRM 订单查看权限)。
/// </summary>
public string PermissionTask { get; set; } = string.Empty;
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : LogisticsRequestDto.cs
*/
namespace JewelryWorkflow.Domain.Dtos;
/// <summary>
/// 物流环节输入 DTO。
/// <para>由销售/业务端提交:承运商与收件信息。</para>
/// </summary>
public sealed class LogisticsRequestDto
{
/// <summary>
/// 承运商名称(例如:顺丰、EMS、DHL)。
/// </summary>
public string CarrierName { get; set; } = string.Empty;
/// <summary>
/// 收货人姓名。
/// </summary>
public string ReceiverName { get; set; } = string.Empty;
/// <summary>
/// 收货地址。
/// </summary>
public string ReceiverAddress { get; set; } = string.Empty;
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : MarketingRequestDto.cs
*/
namespace JewelryWorkflow.Domain.Dtos;
/// <summary>
/// 营销推广环节输入 DTO。
/// <para>由市场端提交:推广渠道、活动主题与预算。</para>
/// </summary>
public sealed class MarketingRequestDto
{
/// <summary>
/// 活动主题名称。
/// </summary>
public string CampaignName { get; set; } = string.Empty;
/// <summary>
/// 推广渠道(例如:抖音、小红书、天猫、线下门店)。
/// </summary>
public string Channel { get; set; } = string.Empty;
/// <summary>
/// 投放预算(元)。
/// </summary>
public decimal Budget { get; set; }
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : MaterialProcurementDto.cs
*/
namespace JewelryWorkflow.Domain.Dtos;
/// <summary>
/// 原料采购核验环节输入 DTO。
/// <para>由业务端提交:待采购/核验原料的基本信息与预算。</para>
/// </summary>
public sealed class MaterialProcurementDto
{
/// <summary>
/// 原料类型(例如:足金、18K金、钻石、红宝石)。
/// </summary>
public string MaterialType { get; set; } = string.Empty;
/// <summary>
/// 原料重量(克)。
/// </summary>
public decimal WeightGram { get; set; }
/// <summary>
/// 原料成色(例如 999、18K、750)。
/// </summary>
public string Purity { get; set; } = string.Empty;
/// <summary>
/// 供应商名称。
/// </summary>
public string SupplierName { get; set; } = string.Empty;
/// <summary>
/// 原料质检证书编号(核验时与第三方核验结果比对)。
/// </summary>
public string CertificateNo { get; set; } = string.Empty;
/// <summary>
/// 采购预算(元)。
/// </summary>
public decimal Budget { get; set; }
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : PackagingRequestDto.cs
*/
namespace JewelryWorkflow.Domain.Dtos;
/// <summary>
/// 包装环节输入 DTO。
/// <para>由包装端提交:包装类型、防伪与装箱要求。</para>
/// </summary>
public sealed class PackagingRequestDto
{
/// <summary>
/// 包装类型(例如:礼盒装、精品盒、裸钻袋)。
/// </summary>
public string PackageType { get; set; } = string.Empty;
/// <summary>
/// 是否需要防伪标识(二维码/镭射标)。
/// </summary>
public bool HasAntiFakeTag { get; set; } = true;
/// <summary>
/// 装箱数量(件)。
/// </summary>
public int BoxCount { get; set; } = 1;
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : ProductionRequestDto.cs
/
namespace JewelryWorkflow.Domain.Dtos;
/// <summary>
/// 加工生产环节输入 DTO。
/// <para>由生产端提交:工艺路线与目标产出重量。</para>
/// </summary>
public sealed class ProductionRequestDto
{
/// <summary>
/// 工艺路线(分号分隔的工序列表,例如:倒模;执模;镶石;抛光)。
/// </summary>
public string ProcessRoute { get; set; } = string.Empty;
/// <summary>
/// 目标产出重量(克)。
/// </summary>
public decimal ExpectedWeightGram { get; set; }
}
/
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : QualityInspectionDto.cs
/
using JewelryWorkflow.Domain.Enums;
namespace JewelryWorkflow.Domain.Dtos;
/// <summary>
/// 质检环节输入 DTO。
/// <para>由质检端提交:质检员与最低放行等级(低于该等级视为不合格)。</para>
/// </summary>
public sealed class QualityInspectionDto
{
/// <summary>
/// 质检员姓名。
/// </summary>
public string InspectorName { get; set; } = string.Empty;
/// <summary>
/// 最低放行等级(默认合格品;低于该等级进入返工/报废分支)。
/// </summary>
public QualityGrade MinimumPassGrade { get; set; } = QualityGrade.Qualified;
}
/
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : TrainingRequestDto.cs
*/
namespace JewelryWorkflow.Domain.Dtos;
/// <summary>
/// 培训环节输入 DTO。
/// <para>由人力资源/培训端提交:课程、学员、讲师与学时信息。</para>
/// </summary>
public sealed class TrainingRequestDto
{
/// <summary>
/// 课程名称(例如:珠宝鉴定基础、销售话术训练)。
/// </summary>
public string CourseName { get; set; } = string.Empty;
/// <summary>
/// 参训员工姓名。
/// </summary>
public string TraineeName { get; set; } = string.Empty;
/// <summary>
/// 讲师姓名。
/// </summary>
public string LecturerName { get; set; } = string.Empty;
/// <summary>
/// 培训学时(小时)。
/// </summary>
public decimal Hours { get; set; }
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : WorkflowContext.cs
*/
using System.Collections.Concurrent;
using JewelryWorkflow.Domain.Entities;
using JewelryWorkflow.Domain.Enums;
namespace JewelryWorkflow.Domain.Dtos;
/// <summary>
/// 工作流上下文(贯穿全程业务流程的数据载体,等价于"流程总线")。
/// <para>每个订单在流水线中拥有独立的上下文实例:
/// 携带主订单、12 个环节的输入 DTO 与产出实体、流程状态。
/// 支持线程安全的快照读取(支持流程并行读取),并预留扩展数据槽。</para>
/// </summary>
public sealed class WorkflowContext
{
// 状态保护锁:保证多线程(主线线程与支持流程线程)安全读写状态字段。
private readonly object _stateLock = new();
/// <summary>
/// 订单唯一编号(与 <see cref="Order"/> 的 OrderId 保持一致)。
/// </summary>
public long OrderId { get; }
/// <summary>
/// 主订单聚合根。
/// </summary>
public JewelryOrder Order { get; }
/// <summary>
/// 流程开始时间。
/// </summary>
public DateTime StartedAt { get; } = DateTime.Now;
/// <summary>
/// 流程完成时间(为 null 表示尚未完成)。
/// </summary>
public DateTime? FinishedAt { get; private set; }
/// <summary>
/// 流程是否已全部成功完成。
/// </summary>
public bool IsCompleted { get; private set; }
/// <summary>
/// 失败信息(任一环节失败后记录)。
/// </summary>
public string? FailureMessage { get; private set; }
// ==================== 12 个环节的输入 DTO ====================
/// <summary>
/// 原料采购核验环节输入(供应商、证书、重量、预算)。
/// </summary>
public MaterialProcurementDto? Procurement { get; set; }
/// <summary>
/// 设计制图环节输入(款式、设计师、工艺要求)。
/// </summary>
public DesignRequestDto? Design { get; set; }
/// <summary>
/// 加工生产环节输入(工艺路线、目标重量)。
/// </summary>
public ProductionRequestDto? Production { get; set; }
/// <summary>
/// 质检环节输入(质检员、最低放行等级)。
/// </summary>
public QualityInspectionDto? Inspection { get; set; }
/// <summary>
/// 包装环节输入(包装类型、防伪、装箱数)。
/// </summary>
public PackagingRequestDto? Packaging { get; set; }
/// <summary>
/// 物流环节输入(承运商、收件人)。
/// </summary>
public LogisticsRequestDto? Logistics { get; set; }
/// <summary>
/// 财务环节输入(采购付款、销售收款、是否开票)。
/// </summary>
public FinanceRequestDto? Finance { get; set; }
/// <summary>
/// 营销推广环节输入(渠道、预算、活动名)。
/// </summary>
public MarketingRequestDto? Marketing { get; set; }
/// <summary>
/// 业务环节输入(客户等级、商务条款、跟单员)。
/// </summary>
public BusinessRequestDto? Business { get; set; }
/// <summary>
/// 人事行政环节输入(经办员工、部门、行政事务)。
/// </summary>
public HrRequestDto? HrAdmin { get; set; }
/// <summary>
/// IT 环节输入(系统、设备、权限任务)。
/// </summary>
public ItRequestDto? InformationTechnology { get; set; }
/// <summary>
/// 培训环节输入(课程、学员、讲师、学时)。
/// </summary>
public TrainingRequestDto? Training { get; set; }
// ==================== 12 个环节的产出实体 ====================
/// <summary>
/// 原料采购核验环节产出(原料批次)。
/// </summary>
public MaterialBatch? MaterialBatchResult { get; set; }
/// <summary>
/// 设计制图环节产出(设计图纸)。
/// </summary>
public DesignDrawing? DesignResult { get; set; }
/// <summary>
/// 加工生产环节产出(生产工单)。
/// </summary>
public ProductionOrder? ProductionResult { get; set; }
/// <summary>
/// 质检环节产出(质检报告)。
/// </summary>
public QualityReport? QualityResult { get; set; }
/// <summary>
/// 包装环节产出(包装信息)。
/// </summary>
public PackageInfo? PackagingResult { get; set; }
/// <summary>
/// 物流环节产出(物流运单)。
/// </summary>
public LogisticsWaybill? LogisticsResult { get; set; }
/// <summary>
/// 财务环节产出(结算单)。
/// </summary>
public FinanceInvoice? FinanceResult { get; set; }
/// <summary>
/// 营销推广环节产出(营销活动)。
/// </summary>
public MarketingCampaign? MarketingResult { get; set; }
/// <summary>
/// 业务环节产出(跟单记录)。
/// </summary>
public BusinessOrder? BusinessResult { get; set; }
/// <summary>
/// 人事行政环节产出(人事记录)。
/// </summary>
public HrRecord? HrResult { get; set; }
/// <summary>
/// IT 环节产出(IT 记录)。
/// </summary>
public ItAsset? ItResult { get; set; }
/// <summary>
/// 培训环节产出(培训记录)。
/// </summary>
public TrainingRecord? TrainingResult { get; set; }
/// <summary>
/// 扩展数据槽:供后续业务扩展挂载自定义数据(线程安全字典)。
/// </summary>
public ConcurrentDictionary<string, object> Extensions { get; } = new();
/// <summary>
/// 构造函数。
/// </summary>
/// <param name="orderId">订单编号。</param>
/// <param name="order">主订单聚合根。</param>
public WorkflowContext(long orderId, JewelryOrder order)
{
OrderId = orderId;
Order = order;
}
/// <summary>
/// 标记流程成功完成(线程安全)。
/// </summary>
public void MarkCompleted()
{
lock (_stateLock)
{
IsCompleted = true;
FinishedAt = DateTime.Now;
Order.Status = OrderStatus.Completed;
Order.CompletedAt = FinishedAt;
Order.CompletedStageCount = 12;
}
}
/// <summary>
/// 标记流程失败(线程安全)。
/// </summary>
/// <param name="stage">失败环节。</param>
/// <param name="message">失败原因。</param>
public void MarkFailed(WorkflowStage stage, string message)
{
lock (_stateLock)
{
IsCompleted = false;
FinishedAt = DateTime.Now;
Order.Status = OrderStatus.Failed;
Order.CompletedAt = FinishedAt;
FailureMessage = $"[{stage}] {message}";
}
}
/// <summary>
/// 创建只读快照(供支持流程并行线程安全读取,避免主流程后续写入造成竞态)。
/// </summary>
/// <returns>当前上下文的浅拷贝快照。</returns>
public WorkflowContext Snapshot()
{
// 浅拷贝:引用型产出实体为只读使用,拷贝后不再由主流程修改(各环节产出后即固定)。
return (WorkflowContext)MemberwiseClone();
}
/// <summary>
/// 已完成的环节数(进度展示用)。
/// </summary>
/// <returns>已产出实体的环节数量。</returns>
public int ProgressCount()
=> (MaterialBatchResult != null ? 1 : 0)
+ (DesignResult != null ? 1 : 0)
+ (ProductionResult != null ? 1 : 0)
+ (QualityResult != null ? 1 : 0)
+ (PackagingResult != null ? 1 : 0)
+ (LogisticsResult != null ? 1 : 0)
+ (FinanceResult != null ? 1 : 0)
+ (MarketingResult != null ? 1 : 0)
+ (BusinessResult != null ? 1 : 0)
+ (HrResult != null ? 1 : 0)
+ (ItResult != null ? 1 : 0)
+ (TrainingResult != null ? 1 : 0);
/// <summary>
/// 输出上下文摘要(用于日志打印)。
/// </summary>
/// <returns>摘要字符串。</returns>
public override string ToString()
=> $"Order#{OrderId} {Order.ProductDescription} 进度={ProgressCount()}/12 状态={Order.Status}";
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : WorkflowRunSummary.cs
*/
namespace JewelryWorkflow.Domain.Dtos;
/// <summary>
/// 工作流批量运行汇总结果。
/// <para>记录一批订单在流水线中的总体执行情况,供宿主层展示与监控。</para>
/// </summary>
public sealed class WorkflowRunSummary
{
/// <summary>
/// 本批订单总数。
/// </summary>
public int Total { get; set; }
/// <summary>
/// 成功订单数。
/// </summary>
public int SuccessCount { get; set; }
/// <summary>
/// 失败订单数。
/// </summary>
public int FailureCount { get; set; }
/// <summary>
/// 全部订单耗时(毫秒)。
/// </summary>
public long ElapsedMilliseconds { get; set; }
/// <summary>
/// 输出汇总字符串。
/// </summary>
public override string ToString()
=> $"总订单={Total} 成功={SuccessCount} 失败={FailureCount} 耗时={ElapsedMilliseconds}ms";
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : IRepository.cs
*/
using JewelryWorkflow.Domain.Common;
namespace JewelryWorkflow.Domain.Contracts;
/// <summary>
/// 通用仓储契约接口(领域层依赖倒置)。
/// <para>基础设施层(如内存仓储)实现本接口,应用层只依赖抽象,
/// 便于将来替换为 EF Core / Dapper / NoSQL 实现而不改动业务代码。</para>
/// </summary>
/// <typeparam name="TKey">主键类型。</typeparam>
/// <typeparam name="TEntity">实体类型。</typeparam>
public interface IRepository<TKey, TEntity> where TEntity : class
{
/// <summary>
/// 按主键查询实体。
/// </summary>
/// <param name="key">主键。</param>
/// <returns>实体或 null。</returns>
Task<TEntity?> GetByIdAsync(TKey key);
/// <summary>
/// 保存实体(新增或更新,由实现决定 Upsert 语义)。
/// </summary>
/// <param name="entity">实体。</param>
/// <returns>保存结果。</returns>
Task<Result> SaveAsync(TEntity entity);
/// <summary>
/// 按主键删除实体。
/// </summary>
/// <param name="key">主键。</param>
/// <returns>是否删除成功。</returns>
Task<bool> DeleteAsync(TKey key);
/// <summary>
/// 查询全部实体。
/// </summary>
/// <returns>实体集合。</returns>
Task<IReadOnlyList<TEntity>> GetAllAsync();
}
cs
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : FileLogOptions.cs
*/
using Microsoft.Extensions.Logging;
namespace JewelryWorkflow.Infrastructure.Logging;
/// <summary>
/// 分级日期文件日志配置(强类型 Options,可从 appsettings.json 绑定)。
/// <para>目标目录结构:
/// Logs/
/// yyyy-MM-dd/
/// app-info.log (Information 级)
/// app-error.log (Error / Critical 级)
/// app-debug.log (Debug / Trace 级)
/// app-warn.log (Warning 级)
/// </para>
/// </summary>
public sealed class FileLogOptions
{
/// <summary>
/// 日志根目录(相对运行目录或绝对路径,默认 "Logs")。
/// </summary>
public string RootPath { get; set; } = "Logs";
/// <summary>
/// 是否启用分级文件日志(默认启用;调试时可通过配置关闭)。
/// </summary>
public bool Enabled { get; set; } = true;
/// <summary>
/// 是否记录作用域(Scopes),默认关闭。
/// </summary>
public bool IncludeScopes { get; set; }
/// <summary>
/// 批量刷盘间隔(毫秒):消费者攒批后周期性写入磁盘,减少 IO 次数。
/// </summary>
public int FlushIntervalMilliseconds { get; set; } = 200;
/// <summary>
/// 批量刷盘阈值(条数):队列中待写条数达到该值时立即刷盘。
/// </summary>
public int FlushThreshold { get; set; } = 50;
/// <summary>
/// 单个日志文件最大字节数(超过后归档为 app-xxx.log.1 等,避免无限增长)。
/// </summary>
public long MaxFileBytes { get; set; } = 5 * 1024 * 1024;
/// <summary>
/// 根据日志等级计算应写入的文件名(app-info.log / app-error.log / app-debug.log / app-warn.log)。
/// <para>映射规则:Trace/Debug → debug;Information → info;Warning → warn;Error/Critical → error。</para>
/// </summary>
/// <param name="level">日志等级。</param>
/// <returns>等级文件名(不含目录)。</returns>
public static string FileNameForLevel(LogLevel level) => level switch
{
LogLevel.Trace or LogLevel.Debug => "app-debug.log",
LogLevel.Information => "app-info.log",
LogLevel.Warning => "app-warn.log",
LogLevel.Error or LogLevel.Critical => "app-error.log",
_ => "app-info.log"
};
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : LeveledFileLogger.cs
*/
using System.Text;
using Microsoft.Extensions.Logging;
namespace JewelryWorkflow.Infrastructure.Logging;
/// <summary>
/// 分级日期文件日志器(实现 <see cref="ILogger"/>)。
/// <para>所有等级/分类的日志统一进入全局 Channel 队列(先入队、立即返回),
/// 由 <see cref="LeveledFileLoggerProvider"/> 的消费者线程异步落盘,
/// 从而保证高并发调用日志时写入方不被磁盘 IO 阻塞。</para>
/// </summary>
internal sealed class LeveledFileLogger : ILogger
{
// 日志分类(ILogger<T> 的 T 类型全名)。
private readonly string _categoryName;
// 宿主 Provider(持有队列与配置)。
private readonly LeveledFileLoggerProvider _provider;
/// <summary>
/// 构造函数。
/// </summary>
/// <param name="categoryName">日志分类。</param>
/// <param name="provider">宿主 Provider。</param>
public LeveledFileLogger(string categoryName, LeveledFileLoggerProvider provider)
{
_categoryName = categoryName;
_provider = provider;
}
/// <summary>
/// 是否启用指定等级(由配置文件日志最小等级控制)。
/// </summary>
/// <param name="logLevel">日志等级。</param>
/// <returns>是否启用。</returns>
public bool IsEnabled(LogLevel logLevel)
=> logLevel >= _provider.MinimumLevel && logLevel != LogLevel.None;
/// <summary>
/// 写入一条日志:格式化消息后入队(不阻塞调用线程)。
/// </summary>
/// <typeparam name="TState">状态类型。</typeparam>
/// <param name="logLevel">日志等级。</param>
/// <param name="eventId">事件编号。</param>
/// <param name="state">状态对象(含结构化参数)。</param>
/// <param name="exception">异常。</param>
/// <param name="formatter">格式化委托。</param>
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
// 等级过滤:低于最小等级直接丢弃。
if (!IsEnabled(logLevel))
{
return;
}
// 构造日志条目并入队(Channel 写入,O(1) 不阻塞)。
_provider.Enqueue(new LogEntry
{
Time = DateTime.Now,
Level = logLevel,
Category = _categoryName,
Message = formatter(state, exception),
Exception = exception,
EventId = eventId
});
}
/// <summary>
/// 创建作用域(本实现未启用作用域写入,返回空作用域对象)。
/// </summary>
/// <typeparam name="TState">状态类型。</typeparam>
/// <param name="state">状态对象。</param>
/// <returns>空作用域。</returns>
public IDisposable? BeginScope<TState>(TState state)
where TState : notnull
=> NullScope.Instance;
/// <summary>
/// 空作用域(BeginScope 的默认返回)。
/// </summary>
private sealed class NullScope : IDisposable
{
/// <summary>单例实例。</summary>
public static NullScope Instance { get; } = new();
/// <summary>空实现。</summary>
public void Dispose()
{
}
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : LeveledFileLoggerProvider.cs
*/
using System.Collections.Concurrent;
using System.Threading.Channels;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace JewelryWorkflow.Infrastructure.Logging;
/// <summary>
/// 分级日期文件日志提供器(实现 <see cref="ILoggerProvider"/>)。
/// <para>高并发架构:
/// 1. 无界 Channel 队列承接所有日志写入(SingleReader 优化);
/// 2. 单消费者后台线程批量出队,按"日期目录 + 等级文件"路由;
/// 3. 每文件一个 <see cref="LogFileWriter"/>(ConcurrentDictionary 缓存,含锁与批量刷盘);
/// 4. 定时刷盘(默认 200ms)+ 条数阈值刷盘(默认 50 条),显著降低磁盘 IO。
/// 目录结构:Logs/yyyy-MM-dd/app-{info,error,debug,warn}.log。</para>
/// </summary>
public sealed class LeveledFileLoggerProvider : ILoggerProvider
{
/// <summary>
/// 配置(强类型 Options)。
/// </summary>
private readonly FileLogOptions _options;
/// <summary>
/// 日志队列(无界,单消费者)。
/// </summary>
private readonly Channel<LogEntry> _queue;
/// <summary>
/// 文件写入器缓存:完整文件路径 → 写入器(按日期+等级维度唯一)。
/// </summary>
private readonly ConcurrentDictionary<string, LogFileWriter> _writers = new();
/// <summary>
/// 消费者线程(后台常驻,负责批量落盘)。
/// </summary>
private readonly Thread _consumerThread;
/// <summary>
/// 当前写入的日期键(yyyy-MM-dd;日期变化时清空写入器缓存,切到新目录)。
/// </summary>
private string _currentDateKey = string.Empty;
/// <summary>
/// 已停止标记(Dispose 后不再入队)。
/// </summary>
private volatile bool _stopped;
/// <summary>
/// 文件日志最小等级(低于该等级的日志直接丢弃,避免无效 IO)。
/// </summary>
public LogLevel MinimumLevel { get; }
/// <summary>
/// 构造函数(由 DI 通过 IOptions 注入配置)。
/// </summary>
/// <param name="options">分级文件日志配置。</param>
public LeveledFileLoggerProvider(IOptions<FileLogOptions> options)
{
_options = options.Value;
MinimumLevel = LogLevel.Trace;
// 无界队列 + 单消费者(高频写入、低频落盘)。
_queue = Channel.CreateUnbounded<LogEntry>(new UnboundedChannelOptions
{
SingleReader = true,
SingleWriter = false
});
// 启动消费者线程(后台守护线程)。
_consumerThread = new Thread(ConsumeLoop)
{
IsBackground = true,
Name = "LeveledFileLogConsumer"
};
_consumerThread.Start();
}
/// <summary>
/// 创建日志器实例(每个 ILogger<T> 分类一个)。
/// </summary>
/// <param name="categoryName">日志分类(T 类型全名)。</param>
/// <returns>分级文件日志器。</returns>
public ILogger CreateLogger(string categoryName)
=> new LeveledFileLogger(categoryName, this);
/// <summary>
/// 入队一条日志(写入方调用,不阻塞)。
/// </summary>
/// <param name="entry">日志条目。</param>
internal void Enqueue(LogEntry entry)
{
if (!_stopped)
{
_queue.Writer.TryWrite(entry);
}
}
/// <summary>
/// 消费者主循环:批量出队 → 按日期/等级路由 → 写入文件 → 定时刷盘。
/// </summary>
private void ConsumeLoop()
{
try
{
// 批量缓冲区:攒批后统一写入(减少锁竞争与 IO 次数)。
var batch = new List<LogEntry>(64);
var reader = _queue.Reader;
while (true)
{
batch.Clear();
// 条件等待:队列空时挂起线程,写入或关闭后恢复。
bool hasData = reader.WaitToReadAsync().AsTask().GetAwaiter().GetResult();
if (!hasData && reader.Completion.IsCompleted)
{
// 队列已关闭:写空剩余项后退出。
while (reader.TryRead(out LogEntry? entry))
{
batch.Add(entry);
}
FlushBatch(batch);
break;
}
// 尽量取空当前队列(批量处理,提升吞吐)。
while (batch.Count < 256 && reader.TryRead(out LogEntry? item))
{
batch.Add(item);
}
if (batch.Count > 0)
{
FlushBatch(batch);
}
else
{
// 队列短暂为空:让出 CPU。
Thread.Sleep(1);
}
}
}
catch (Exception ex)
{
// 消费者异常兜底:尝试把异常写入控制台(文件日志已不可用)。
Console.Error.WriteLine($"[LeveledFileLog] 消费者线程异常: {ex}");
}
finally
{
// 退出前刷盘全部写入器。
foreach (LogFileWriter writer in _writers.Values)
{
writer.Flush();
writer.Dispose();
}
_writers.Clear();
}
}
/// <summary>
/// 批量落盘一批日志条目。
/// </summary>
/// <param name="batch">日志条目列表。</param>
private void FlushBatch(List<LogEntry> batch)
{
// 日期切换检查:新的一天 → 清空写入器缓存(旧文件写入器关闭,新目录生效)。
string dateKey = DateTime.Now.ToString("yyyy-MM-dd");
if (!string.Equals(dateKey, _currentDateKey, StringComparison.Ordinal))
{
foreach (LogFileWriter writer in _writers.Values)
{
writer.Flush();
writer.Dispose();
}
_writers.Clear();
_currentDateKey = dateKey;
// 预创建当日全部等级文件(贴合"按日期文件夹、按等级分文件"的目录结构约定)。
EnsureBaseFiles(dateKey);
}
foreach (LogEntry entry in batch)
{
// 路由:Logs/{yyyy-MM-dd}/app-{level}.log。
string filePath = Path.Combine(_options.RootPath, dateKey, FileLogOptions.FileNameForLevel(entry.Level));
// 获取(或创建)对应写入器:ConcurrentDictionary 保证线程安全。
LogFileWriter writer = _writers.GetOrAdd(filePath, path => new LogFileWriter(path, _options));
writer.WriteLine(entry.FormatLine());
}
// 定时刷盘:未达条数阈值时由此处保证最终落盘(周期 200ms)。
foreach (LogFileWriter writer in _writers.Values)
{
writer.Flush();
}
}
/// <summary>
/// 预创建当日 4 个等级文件(app-info/app-error/app-debug/app-warn),
/// 并写入一行文件头注释,保证目录结构完整、直观。
/// </summary>
/// <param name="dateKey">日期键(yyyy-MM-dd)。</param>
private void EnsureBaseFiles(string dateKey)
{
if (!_options.Enabled)
{
return;
}
string[] levelFiles =
{
"app-info.log", "app-error.log", "app-debug.log", "app-warn.log"
};
foreach (string fileName in levelFiles)
{
string filePath = Path.Combine(_options.RootPath, dateKey, fileName);
// GetOrAdd 同时完成创建与注册(幂等,多次调用安全)。
_writers.GetOrAdd(filePath, path =>
{
var writer = new LogFileWriter(path, _options);
writer.WriteLine($"# 分级日志文件:{fileName}(JewelryWorkflow 自研分级日期文件日志)");
return writer;
});
}
}
/// <summary>
/// 释放资源:关闭队列并等待消费者线程退出。
/// </summary>
public void Dispose()
{
_stopped = true;
_queue.Writer.TryComplete();
// 等待消费者线程完成收尾(上限 2 秒,避免长时间阻塞进程退出)。
if (_consumerThread.IsAlive && !_consumerThread.Join(TimeSpan.FromSeconds(2)))
{
Console.Error.WriteLine("[LeveledFileLog] 消费者线程未能在 2 秒内退出。");
}
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : LogEntry.cs
*/
using Microsoft.Extensions.Logging;
namespace JewelryWorkflow.Infrastructure.Logging;
/// <summary>
/// 日志条目(进入日志队列的最小单元)。
/// <para>由 <see cref="LeveledFileLogger"/> 创建,经无界 Channel 交给
/// <see cref="LeveledFileLoggerProvider"/> 的消费者线程批量落盘。</para>
/// </summary>
internal sealed class LogEntry
{
/// <summary>
/// 日志时间(本地时间)。
/// </summary>
public DateTime Time { get; init; }
/// <summary>
/// 日志等级。
/// </summary>
public LogLevel Level { get; init; }
/// <summary>
/// 日志分类(通常是 ILogger<T> 的泛型类型全名)。
/// </summary>
public string Category { get; init; } = string.Empty;
/// <summary>
/// 格式化后的日志消息正文(含结构化参数展开)。
/// </summary>
public string Message { get; init; } = string.Empty;
/// <summary>
/// 异常对象(可为 null)。
/// </summary>
public Exception? Exception { get; init; }
/// <summary>
/// 事件编号(可为 null)。
/// </summary>
public EventId? EventId { get; init; }
/// <summary>
/// 等级缩写(与控制台输出保持一致)。
/// </summary>
/// <param name="level">日志等级。</param>
/// <returns>缩写字符串。</returns>
public static string LevelShortName(LogLevel level) => level switch
{
LogLevel.Trace => "TRC",
LogLevel.Debug => "DBG",
LogLevel.Information => "INF",
LogLevel.Warning => "WRN",
LogLevel.Error => "ERR",
LogLevel.Critical => "CRT",
_ => "???"
};
/// <summary>
/// 输出与 Microsoft.Extensions.Logging.Console 一致格式的日志行。
/// </summary>
/// <returns>完整日志行(不含换行)。</returns>
public string FormatLine()
{
// 时间戳 + 等级缩写 + 分类 + 消息 + 异常。
string line = $"{Time:yyyy-MM-dd HH:mm:ss.fff} [{LevelShortName(Level)}] [{Category}] {Message}";
if (Exception is not null)
{
line += $"{Environment.NewLine} 异常: {Exception}";
}
return line;
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : LogFileWriter.cs
*/
using System.Text;
namespace JewelryWorkflow.Infrastructure.Logging;
/// <summary>
/// 单个日志文件写入器。
/// <para>线程安全设计:内部持有独立的 <see cref="StreamWriter"/> 与写锁,
/// 只允许消费者线程通过 <see cref="WriteLine"/> 写入,配合批量刷盘策略
/// (达到条数阈值或时间间隔时调用 <see cref="Flush"/>)减少磁盘 IO。</para>
/// </summary>
internal sealed class LogFileWriter : IDisposable
{
// 写锁:保证同一文件的串行写入(消费者线程单线程写入,此处为双重保险)。
private readonly object _writeLock = new();
// 底层文本写入器(UTF-8 无 BOM,追加模式,关闭自动刷盘)。
private StreamWriter? _writer;
// 累计未刷盘的行数(达到阈值触发 Flush)。
private int _pendingCount;
/// <summary>
/// 文件绝对路径(含日期目录与等级文件名,如 Logs\2026-09-14\app-info.log)。
/// </summary>
public string FilePath { get; }
/// <summary>
/// 配置(根目录、刷盘阈值、最大文件字节数)。
/// </summary>
public FileLogOptions Options { get; }
/// <summary>
/// 构造函数。
/// </summary>
/// <param name="filePath">文件绝对路径。</param>
/// <param name="options">配置。</param>
public LogFileWriter(string filePath, FileLogOptions options)
{
FilePath = filePath;
Options = options;
}
/// <summary>
/// 写入一行日志(消费者线程调用;内部维护 StreamWriter 并累积刷盘计数)。
/// </summary>
/// <param name="line">日志行(含换行符)。</param>
public void WriteLine(string line)
{
// 惰性创建写入器:确保日期目录存在,避免"目录不存在"异常。
if (_writer is null)
{
string? directory = Path.GetDirectoryName(FilePath);
if (!string.IsNullOrEmpty(directory))
{
Directory.CreateDirectory(directory);
}
_writer = new StreamWriter(
new FileStream(FilePath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite),
new UTF8Encoding(encoderShouldEmitUTF8Identifier: false))
{
AutoFlush = false
};
}
lock (_writeLock)
{
_writer.WriteLine(line);
_pendingCount++;
// 达到条数阈值:立即刷盘(批量写入)。
if (_pendingCount >= Options.FlushThreshold)
{
_writer.Flush();
_pendingCount = 0;
}
// 文件超限:归档轮转(app-info.log → app-info.log.1,保留旧文件)。
if (_writer.BaseStream.Length > Options.MaxFileBytes)
{
Rotate();
}
}
}
/// <summary>
/// 强制刷盘(定时器调用,把残余缓冲写入磁盘)。
/// </summary>
public void Flush()
{
lock (_writeLock)
{
if (_writer is not null && _pendingCount > 0)
{
_writer.Flush();
_pendingCount = 0;
}
}
}
/// <summary>
/// 文件超限归档:把当前文件改名为 .1,新建空文件继续写入。
/// </summary>
private void Rotate()
{
try
{
_writer?.Dispose();
_writer = null;
string archive = $"{FilePath}.1";
File.Move(FilePath, archive, overwrite: true);
}
catch (IOException)
{
// 归档失败(如文件被占用):忽略,继续写入当前文件。
}
}
/// <summary>
/// 释放资源(关闭写入器并刷盘)。
/// </summary>
public void Dispose()
{
lock (_writeLock)
{
_writer?.Flush();
_writer?.Dispose();
_writer = null;
}
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : LoggingExtensions.cs
*/
using Karambolo.Extensions.Logging.File;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
namespace JewelryWorkflow.Infrastructure.Logging;
/// <summary>
/// 日志扩展方法:向 <see cref="ILoggingBuilder"/> 注册分级日期文件日志与 Karambolo 综合日志。
/// </summary>
public static class LoggingExtensions
{
/// <summary>
/// 注册分级日期文件日志(自研:Logs/yyyy-MM-dd/app-{info,error,debug,warn}.log)。
/// <para>说明:因 Karambolo 按"单文件模板"切分,无法直接实现"日期文件夹 × 等级文件"
/// 的嵌套结构,故自研 <see cref="LeveledFileLoggerProvider"/> 精确满足该目录需求;
/// Karambolo 作为点名库另行注册为综合日志通道(见 <see cref="AddKaramboloCombinedFile"/>)。</para>
/// </summary>
/// <param name="builder">日志构建器。</param>
/// <param name="configure">配置委托(可选,覆盖默认值)。</param>
/// <returns>日志构建器(链式调用)。</returns>
public static ILoggingBuilder AddLeveledFile(this ILoggingBuilder builder, Action<FileLogOptions>? configure = null)
{
// 配置绑定(支持 appsettings.json 的 "Logging:LeveledFile" 节)。
if (configure is not null)
{
builder.Services.Configure(configure);
}
// Provider 注册为单例(一个消费者线程 + 一个队列,全局共享)。
builder.Services.TryAddSingleton<LeveledFileLoggerProvider>();
builder.Services.AddSingleton<ILoggerProvider>(sp => sp.GetRequiredService<LeveledFileLoggerProvider>());
return builder;
}
/// <summary>
/// 注册 Karambolo 综合文件日志(Logs/app-combined-2026-09-14.log,按日切分)。
/// <para>作为分级日志的补充通道,记录全量日志(便于按时间整体检索)。
/// Karambolo 3.4.0 的文件路径模板使用尖括号占位符:
/// - <date:yyyy-MM-dd>:当前日期(按日切分文件名);
/// - <counter:000>:滚动计数器(文件超限轮转时递增,可省略)。</para>
/// </summary>
/// <param name="builder">日志构建器。</param>
/// <returns>日志构建器(链式调用)。</returns>
public static ILoggingBuilder AddKaramboloCombinedFile(this ILoggingBuilder builder)
{
// Karambolo 官方扩展:RootPath 指定根目录,Files 指定文件模板(<date:yyyy-MM-dd> 自动按日切分)。
builder.AddFile(options =>
{
string root = Path.Combine(Environment.CurrentDirectory, "Logs");
// Karambolo 的 RootPath 内部使用 PhysicalFileProvider,要求目录已存在,故先创建。
Directory.CreateDirectory(root);
options.RootPath = root;
options.Files = new[]
{
new LogFileOptions
{
// <date:yyyy-MM-dd> = 2026-09-14;带 counter 使 MaxFileSize 大小滚动生效。
Path = "app-combined-<date:yyyy-MM-dd>-<counter>.log",
CounterFormat = "000",
MaxFileSize = 5L * 1024 * 1024 // 单文件 5MB 滚动(需 counter 占位符配合)。
}
};
});
return builder;
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : InMemoryRepository.cs
*/
using System.Collections.Concurrent;
using JewelryWorkflow.Domain.Common;
using JewelryWorkflow.Domain.Contracts;
namespace JewelryWorkflow.Infrastructure.Repositories;
/// <summary>
/// 线程安全的内存仓储(泛型实现 IRepository<TKey, TEntity>)。
/// <para>基于 <see cref="ConcurrentDictionary{TKey, TValue}"/>:
/// - 高并发读写下线程安全(GetOrAdd / TryGetValue / TryRemove);
/// - 通过 key 提取委托实现泛型 Upsert;
/// - 适用于演示与单机轻量场景;生产环境可无缝替换为 EF Core/Dapper 实现(依赖倒置)。</para>
/// </summary>
/// <typeparam name="TKey">主键类型。</typeparam>
/// <typeparam name="TEntity">实体类型。</typeparam>
public sealed class InMemoryRepository<TKey, TEntity> : IRepository<TKey, TEntity>
where TKey : notnull
where TEntity : class
{
/// <summary>
/// 底层存储(ConcurrentDictionary:原子读改写)。
/// </summary>
private readonly ConcurrentDictionary<TKey, TEntity> _store = new();
/// <summary>
/// 主键提取委托(由构造方提供,用于 Save 时定位主键)。
/// </summary>
private readonly Func<TEntity, TKey> _keySelector;
/// <summary>
/// 构造函数。
/// </summary>
/// <param name="keySelector">主键提取委托。</param>
public InMemoryRepository(Func<TEntity, TKey> keySelector)
{
_keySelector = keySelector;
}
/// <summary>
/// 按主键查询。
/// </summary>
/// <param name="key">主键。</param>
/// <returns>实体或 null。</returns>
public Task<TEntity?> GetByIdAsync(TKey key)
{
_store.TryGetValue(key, out TEntity? entity);
return Task.FromResult(entity);
}
/// <summary>
/// 保存(Upsert:存在则覆盖,不存在则新增)。
/// </summary>
/// <param name="entity">实体。</param>
/// <returns>保存结果。</returns>
public Task<Result> SaveAsync(TEntity entity)
{
if (entity is null)
{
return Task.FromResult(Result.Fail("实体不能为 null。", "ENTITY_NULL"));
}
TKey key = _keySelector(entity);
_store[key] = entity; // ConcurrentDictionary 索引器:原子 Upsert。
return Task.FromResult(Result.Ok());
}
/// <summary>
/// 按主键删除。
/// </summary>
/// <param name="key">主键。</param>
/// <returns>是否删除成功。</returns>
public Task<bool> DeleteAsync(TKey key)
=> Task.FromResult(_store.TryRemove(key, out _));
/// <summary>
/// 查询全部实体(返回快照集合,外部修改不影响内部存储)。
/// </summary>
/// <returns>实体集合。</returns>
public Task<IReadOnlyList<TEntity>> GetAllAsync()
=> Task.FromResult<IReadOnlyList<TEntity>>(_store.Values.ToArray());
}
cs
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : CircuitBreaker.cs
*/
using JewelryWorkflow.Domain.Common;
namespace JewelryWorkflow.Application.Adapters;
/// <summary>
/// 熔断器(Circuit Breaker)。
/// <para>状态机:关闭(Closed) → 打开(Open) → 半开(HalfOpen) → 关闭/打开。
/// - 关闭:正常放行请求,连续失败达阈值后进入打开;
/// - 打开:快速失败(不发起真实调用),熔断时间过后进入半开;
/// - 半开:允许少量探测请求,成功则关闭,失败则重新打开。
/// 目的:外部服务不可用时快速失败并保护下游,避免级联雪崩。</para>
/// </summary>
public sealed class CircuitBreaker
{
// 状态机锁:所有状态切换必须持有该锁,保证多线程安全。
private readonly object _lock = new();
// 熔断器状态枚举(私有,避免暴露内部细节)。
private enum BreakerState { Closed, Open, HalfOpen }
/// <summary>
/// 熔断器名称(用于日志)。
/// </summary>
public string Name { get; }
/// <summary>
/// 连续失败多少次后打开熔断器。
/// </summary>
public int FailureThreshold { get; }
/// <summary>
/// 熔断打开持续时长(毫秒),过后进入半开探测。
/// </summary>
public int OpenDurationMilliseconds { get; }
/// <summary>
/// 半开状态下,连续成功多少次后恢复关闭。
/// </summary>
public int HalfOpenSuccessThreshold { get; }
// 当前状态。
private BreakerState _state = BreakerState.Closed;
// 连续失败计数。
private int _consecutiveFailures;
// 连续成功计数(半开探测用)。
private int _consecutiveSuccesses;
// 打开状态的开始时间戳(用于判定是否可进入半开)。
private long _openSinceTicks;
/// <summary>
/// 构造函数。
/// </summary>
/// <param name="name">熔断器名称。</param>
/// <param name="failureThreshold">失败阈值(默认 3)。</param>
/// <param name="openDurationMilliseconds">熔断时长毫秒(默认 10000)。</param>
/// <param name="halfOpenSuccessThreshold">半开恢复阈值(默认 1)。</param>
public CircuitBreaker(string name, int failureThreshold = 3, int openDurationMilliseconds = 10_000, int halfOpenSuccessThreshold = 1)
{
Name = name;
FailureThreshold = Math.Max(1, failureThreshold);
OpenDurationMilliseconds = Math.Max(100, openDurationMilliseconds);
HalfOpenSuccessThreshold = Math.Max(1, halfOpenSuccessThreshold);
}
/// <summary>
/// 当前是否允许发起真实调用(熔断打开时不允许)。
/// </summary>
public bool IsOpen
{
get
{
lock (_lock)
{
// 打开状态下,若已超过熔断时长则自动转半开(允许探测)。
if (_state == BreakerState.Open)
{
long elapsed = DateTime.UtcNow.Ticks - _openSinceTicks;
if (elapsed >= OpenDurationMilliseconds * TimeSpan.TicksPerMillisecond)
{
_state = BreakerState.HalfOpen;
_consecutiveSuccesses = 0;
}
}
return _state == BreakerState.Open;
}
}
}
/// <summary>
/// 带熔断保护的异步调用:熔断打开时快速失败,不执行真实操作。
/// </summary>
/// <typeparam name="T">返回值类型。</typeparam>
/// <param name="operation">真实操作。</param>
/// <returns>调用结果(熔断打开时返回失败结果,不抛异常)。</returns>
public async Task<Result<T>> ExecuteAsync<T>(Func<Task<Result<T>>> operation)
{
// 1) 熔断检查:打开则快速失败。
if (IsOpen)
{
return Result<T>.Fail($"熔断器【{Name}】处于打开状态,请求被快速拒绝。", "CIRCUIT_OPEN");
}
// 2) 执行真实操作并记录结果。
Result<T> result;
try
{
result = await operation().ConfigureAwait(false);
}
catch (Exception ex)
{
// 异常视为失败,并计入熔断统计。
RecordFailure();
return Result<T>.Fail($"外部服务【{Name}】调用异常:{ex.Message}", "ADAPTER_EXCEPTION");
}
// 3) 按业务结果记录成功/失败。
if (result.IsSuccess)
{
RecordSuccess();
}
else
{
RecordFailure();
}
return result;
}
/// <summary>
/// 记录一次成功:关闭态清空失败计数;半开态累计成功,达到阈值恢复关闭。
/// </summary>
private void RecordSuccess()
{
lock (_lock)
{
if (_state == BreakerState.HalfOpen)
{
_consecutiveSuccesses++;
if (_consecutiveSuccesses >= HalfOpenSuccessThreshold)
{
// 半开探测成功:恢复关闭,清空全部计数。
_state = BreakerState.Closed;
_consecutiveFailures = 0;
_consecutiveSuccesses = 0;
}
}
else if (_state == BreakerState.Closed)
{
// 关闭态成功:重置失败计数。
_consecutiveFailures = 0;
}
}
}
/// <summary>
/// 记录一次失败:关闭态累计失败,达阈值打开;半开态失败立即重新打开。
/// </summary>
private void RecordFailure()
{
lock (_lock)
{
switch (_state)
{
case BreakerState.Closed:
_consecutiveFailures++;
if (_consecutiveFailures >= FailureThreshold)
{
// 连续失败达阈值:打开熔断器。
_state = BreakerState.Open;
_openSinceTicks = DateTime.UtcNow.Ticks;
}
break;
case BreakerState.HalfOpen:
// 半开探测失败:立即重新打开并重置计时。
_state = BreakerState.Open;
_openSinceTicks = DateTime.UtcNow.Ticks;
break;
case BreakerState.Open:
// 已打开:无需处理(IsOpen 会周期性转入半开)。
break;
}
}
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : GemLabAdapter.cs
*/
using Microsoft.Extensions.Logging;
namespace JewelryWorkflow.Application.Adapters;
/// <summary>
/// 宝石/贵金属检测机构适配器(对接第三方检测实验室)。
/// <para>用于质检环节获取权威检测数据(实测成色、重量),
/// 同样具备重试 + 熔断弹性,模拟检测平台偶发抖动场景。</para>
/// </summary>
public sealed class GemLabAdapter : ResilientAdapterBase<
GemLabAdapter.LabRequest, GemLabAdapter.LabResponse>
{
/// <summary>
/// 请求 DTO:送检入参。
/// </summary>
public sealed class LabRequest
{
/// <summary>送检样品编号(工单号)。</summary>
public string SampleNo { get; set; } = string.Empty;
/// <summary>原料/成品类型。</summary>
public string MaterialType { get; set; } = string.Empty;
/// <summary>申报成色。</summary>
public string DeclaredPurity { get; set; } = string.Empty;
}
/// <summary>
/// 响应 DTO:检测结果。
/// </summary>
public sealed class LabResponse
{
/// <summary>实测成色。</summary>
public string MeasuredPurity { get; set; } = string.Empty;
/// <summary>实测重量(克)。</summary>
public decimal MeasuredWeightGram { get; set; }
/// <summary>检测报告编号。</summary>
public string ReportNo { get; set; } = string.Empty;
}
/// <summary>
/// 故障注入模式(用于演示,生产环境可移除)。
/// </summary>
public enum FailureMode
{
/// <summary>正常:始终成功。</summary>
None,
/// <summary>瞬时故障:前 1 次失败,之后恢复(演示重试成功)。</summary>
Transient
}
/// <summary>
/// 当前故障模式。
/// </summary>
public FailureMode Mode { get; set; } = FailureMode.None;
// 调用次数计数。
private int _callCount;
/// <summary>
/// 构造函数。
/// </summary>
/// <param name="logger">标准日志器(DI 注入)。</param>
public GemLabAdapter(ILogger<GemLabAdapter> logger)
: base(logger, retryPolicy: new RetryPolicy(maxRetry: 2, baseDelayMilliseconds: 80), breaker: new CircuitBreaker("检测机构", failureThreshold: 3, openDurationMilliseconds: 8000))
{
}
/// <summary>
/// 适配器名称。
/// </summary>
public override string AdapterName => "宝石检测机构";
/// <summary>
/// 真实调用检测机构服务(模拟网络延迟与故障注入)。
/// </summary>
/// <param name="request">送检请求。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>检测响应。</returns>
protected override async Task<LabResponse> InvokeCoreAsync(LabRequest request, CancellationToken cancellationToken)
{
// 模拟检测平台网络耗时(40 毫秒)。
await Task.Delay(40, cancellationToken);
// 原子自增调用次数。
int call = Interlocked.Increment(ref _callCount);
// 瞬时故障:第一次抛异常,第二次起恢复。
if (Mode == FailureMode.Transient && call == 1)
{
throw new IOException("检测平台网络抖动(模拟瞬时故障)。");
}
// 返回权威检测数据。
return new LabResponse
{
MeasuredPurity = request.DeclaredPurity,
MeasuredWeightGram = 18.5m, // 模拟实测重量(克)
ReportNo = $"LAB-{request.SampleNo}-{DateTime.Now:yyyyMMdd}"
};
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : ResilientAdapterBase.cs
*/
using JewelryWorkflow.Domain.Common;
using JewelryWorkflow.Domain.Contracts;
using Microsoft.Extensions.Logging;
namespace JewelryWorkflow.Application.Adapters;
/// <summary>
/// 弹性适配器基类(组合重试 + 熔断)。
/// <para>子类只需实现 <see cref="InvokeCoreAsync"/>(真实的外部调用),
/// 基类统一提供:熔断前置检查 → 指数退避重试 → 异常/失败结果标准化转换,
/// 并统一输出结构化日志。符合"适配器职责单一、完全可扩展"的企业级要求。</para>
/// </summary>
/// <typeparam name="TRequest">请求类型。</typeparam>
/// <typeparam name="TResponse">响应类型。</typeparam>
public abstract class ResilientAdapterBase<TRequest, TResponse> : IResilientAdapter<TRequest, TResponse>
{
/// <summary>
/// 熔断器(进程内单例,保护同一外部服务)。
/// </summary>
protected CircuitBreaker Breaker { get; }
/// <summary>
/// 重试策略。
/// </summary>
protected RetryPolicy Retry { get; }
/// <summary>
/// 标准日志器(由 DI 注入,控制台与文件统一输出)。
/// </summary>
protected ILogger Logger { get; }
/// <summary>
/// 构造函数。
/// </summary>
/// <param name="logger">标准日志器。</param>
/// <param name="retryPolicy">重试策略(可自定义)。</param>
/// <param name="breaker">熔断器(可自定义)。</param>
protected ResilientAdapterBase(ILogger logger, RetryPolicy? retryPolicy = null, CircuitBreaker? breaker = null)
{
Logger = logger;
Retry = retryPolicy ?? new RetryPolicy();
Breaker = breaker ?? new CircuitBreaker(AdapterName);
}
/// <summary>
/// 适配器名称(默认取类名,子类可重写为业务名称)。
/// </summary>
public virtual string AdapterName => GetType().Name;
/// <summary>
/// 弹性调用入口:熔断检查 → 重试执行 → 结果标准化。
/// </summary>
/// <param name="request">请求对象。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>调用结果(永不抛异常,失败包装为 Result)。</returns>
public async Task<Result<TResponse>> CallAsync(TRequest request, CancellationToken cancellationToken)
{
// 记录一次外部调用开始(结构化日志)。
Logger.LogInformation("[适配器: {Adapter}] 开始调用外部服务,请求类型={RequestType}",
AdapterName, typeof(TRequest).Name);
// 熔断保护执行。
return await Breaker.ExecuteAsync(async () =>
{
try
{
// 重试执行真实调用。
TResponse response = await Retry.ExecuteAsync(
() => InvokeCoreAsync(request, cancellationToken),
onRetry: (attempt, ex, delayMs) =>
Logger.LogWarning("[适配器: {Adapter}] 调用失败,第 {Attempt} 次重试,等待 {Delay}ms,原因:{Reason}",
AdapterName, attempt, delayMs, ex.Message),
cancellationToken: cancellationToken);
// 成功路径。
Logger.LogInformation("[适配器: {Adapter}] 调用成功,响应类型={ResponseType}", AdapterName, typeof(TResponse).Name);
return Result<TResponse>.Ok(response);
}
catch (OperationCanceledException)
{
// 取消:包装为失败结果。
return Result<TResponse>.Fail("调用被取消。", "CANCELLED");
}
catch (Exception ex)
{
// 重试耗尽后仍失败:记录错误并返回失败结果(熔断器已在 Breaker 层计数)。
Logger.LogError(ex, "[适配器: {Adapter}] 调用最终失败(重试耗尽)。", AdapterName);
return Result<TResponse>.Fail($"外部服务调用失败:{ex.Message}", "ADAPTER_FAILED");
}
});
}
/// <summary>
/// 子类实现真实的外部服务调用。
/// </summary>
/// <param name="request">请求对象。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>响应对象。</returns>
protected abstract Task<TResponse> InvokeCoreAsync(TRequest request, CancellationToken cancellationToken);
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : RetryPolicy.cs
*/
using JewelryWorkflow.Domain.Common;
namespace JewelryWorkflow.Application.Adapters;
/// <summary>
/// 重试策略(Retry Policy)。
/// <para>实现指数退避重试:外部服务抖动(瞬时失败)时自动重试,
/// 退避时间按 2^n 指数增长并封顶,避免重试风暴。</para>
/// </summary>
public sealed class RetryPolicy
{
/// <summary>
/// 最大重试次数(不含首次尝试)。
/// </summary>
public int MaxRetry { get; }
/// <summary>
/// 基础退避延迟(毫秒),第 n 次重试延迟 = baseDelay * 2^(n-1)。
/// </summary>
public int BaseDelayMilliseconds { get; }
/// <summary>
/// 退避延迟上限(毫秒),防止单次等待过长。
/// </summary>
public int MaxDelayMilliseconds { get; }
/// <summary>
/// 随机抖动比例(0-0.5),避免多线程同时重试造成同步重试风暴。
/// </summary>
public double JitterRatio { get; }
/// <summary>
/// 构造函数。
/// </summary>
/// <param name="maxRetry">最大重试次数(默认 3)。</param>
/// <param name="baseDelayMilliseconds">基础退避毫秒数(默认 200)。</param>
/// <param name="maxDelayMilliseconds">退避上限毫秒数(默认 3000)。</param>
/// <param name="jitterRatio">抖动比例(默认 0.3)。</param>
public RetryPolicy(int maxRetry = 3, int baseDelayMilliseconds = 200, int maxDelayMilliseconds = 3000, double jitterRatio = 0.3)
{
MaxRetry = Math.Max(0, maxRetry);
BaseDelayMilliseconds = Math.Max(10, baseDelayMilliseconds);
MaxDelayMilliseconds = Math.Max(BaseDelayMilliseconds, maxDelayMilliseconds);
JitterRatio = Math.Clamp(jitterRatio, 0, 0.5);
}
/// <summary>
/// 带重试地执行异步操作。
/// </summary>
/// <typeparam name="T">返回值类型。</typeparam>
/// <param name="operation">待执行操作(可能抛出异常)。</param>
/// <param name="onRetry">每次重试前的回调(参数:第几次重试、异常、预计等待毫秒数)。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>操作结果。</returns>
/// <exception cref="OperationCanceledException">取消时抛出。</exception>
public async Task<T> ExecuteAsync<T>(
Func<Task<T>> operation,
Action<int, Exception, int>? onRetry = null,
CancellationToken cancellationToken = default)
{
// 首次尝试不计入重试次数。
for (int attempt = 0; ; attempt++)
{
try
{
// 执行真实操作。
return await operation().ConfigureAwait(false);
}
catch (OperationCanceledException)
{
// 用户取消:直接向上抛出,不做重试。
throw;
}
catch (Exception ex) when (attempt < MaxRetry)
{
// 计算第 attempt+1 次重试的退避时间:baseDelay * 2^attempt,封顶 maxDelay,叠加抖动。
int backoff = Math.Min(
MaxDelayMilliseconds,
BaseDelayMilliseconds * (1 << attempt));
double jitter = 1.0 + (Random.Shared.NextDouble() * 2 - 1) * JitterRatio;
int delayMs = (int)Math.Round(backoff * jitter);
onRetry?.Invoke(attempt + 1, ex, delayMs);
await Task.Delay(delayMs, cancellationToken).ConfigureAwait(false);
}
catch (Exception)
{
// 已达最大重试次数:不再重试,直接抛出。
throw;
}
}
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : SupplierInspectionAdapter.cs
*/
using Microsoft.Extensions.Logging;
namespace JewelryWorkflow.Application.Adapters;
/// <summary>
/// 供应商资质核验适配器(对接第三方供应商征信/质检机构服务)。
/// <para>演示外部服务适配器的"重试 + 熔断"弹性:
/// 支持故障注入模式(None=正常 / Transient=瞬时故障几次后恢复 / Broken=持续不可用),
/// 配合 <see cref="RetryPolicy"/> 与 <see cref="CircuitBreaker"/> 展示重试成功与熔断快速失败。</para>
/// </summary>
public sealed class SupplierInspectionAdapter : ResilientAdapterBase<
SupplierInspectionAdapter.InspectionRequest, SupplierInspectionAdapter.InspectionResponse>
{
/// <summary>
/// 请求 DTO:供应商核验入参。
/// </summary>
public sealed class InspectionRequest
{
/// <summary>供应商名称。</summary>
public string SupplierName { get; set; } = string.Empty;
/// <summary>原料质检证书编号。</summary>
public string CertificateNo { get; set; } = string.Empty;
/// <summary>原料类型。</summary>
public string MaterialType { get; set; } = string.Empty;
/// <summary>原料重量(克)。</summary>
public decimal WeightGram { get; set; }
}
/// <summary>
/// 响应 DTO:供应商核验结果。
/// </summary>
public sealed class InspectionResponse
{
/// <summary>是否核验通过。</summary>
public bool Passed { get; set; }
/// <summary>核验备注。</summary>
public string Remark { get; set; } = string.Empty;
}
/// <summary>
/// 故障注入模式(用于演示,生产环境可移除)。
/// </summary>
public enum FailureMode
{
/// <summary>正常:始终成功。</summary>
None,
/// <summary>瞬时故障:前 2 次失败,之后恢复(演示重试成功)。</summary>
Transient,
/// <summary>持续故障:始终失败(演示熔断打开)。</summary>
Broken
}
/// <summary>
/// 当前故障模式(演示中可在运行时切换)。
/// </summary>
public FailureMode Mode { get; set; } = FailureMode.None;
// 调用次数计数(跨线程安全,用于 Transient 模式判定)。
private int _callCount;
/// <summary>
/// 构造函数。
/// </summary>
/// <param name="logger">标准日志器(DI 注入)。</param>
public SupplierInspectionAdapter(ILogger<SupplierInspectionAdapter> logger)
: base(logger, retryPolicy: new RetryPolicy(maxRetry: 3, baseDelayMilliseconds: 100), breaker: new CircuitBreaker("供应商核验", failureThreshold: 2, openDurationMilliseconds: 8000))
{
}
/// <summary>
/// 适配器名称。
/// </summary>
public override string AdapterName => "供应商资质核验";
/// <summary>
/// 真实调用供应商核验服务(模拟网络延迟与故障注入)。
/// </summary>
/// <param name="request">核验请求。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>核验响应。</returns>
protected override async Task<InspectionResponse> InvokeCoreAsync(InspectionRequest request, CancellationToken cancellationToken)
{
// 模拟外部服务网络耗时(30 毫秒)。
await Task.Delay(30, cancellationToken);
// 原子自增调用次数(多线程安全)。
int call = Interlocked.Increment(ref _callCount);
// 按故障模式注入失败。
switch (Mode)
{
case FailureMode.Transient when call <= 2:
// 前两次抛"超时"模拟瞬时抖动,第三次起恢复正常。
throw new TimeoutException("供应商服务响应超时(模拟瞬时故障)。");
case FailureMode.Broken:
// 持续抛异常,触发熔断器累计失败并打开。
throw new HttpRequestException("供应商服务不可用(模拟持续故障)。");
default:
// 核验通过:返回证书核验结果。
return new InspectionResponse
{
Passed = true,
Remark = $"证书【{request.CertificateNo}】核验通过,供应商【{request.SupplierName}】资质有效。"
};
}
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : BusinessService.cs
*/
using JewelryWorkflow.Domain.Common;
using JewelryWorkflow.Domain.Contracts;
using JewelryWorkflow.Domain.Dtos;
using JewelryWorkflow.Domain.Entities;
using JewelryWorkflow.Domain.Enums;
using Microsoft.Extensions.Logging;
namespace JewelryWorkflow.Application.Services;
/// <summary>
/// 业务服务(支持环节:客户跟单与商务确认)。
/// <para>职责:校验业务输入 → 生成业务跟单实体(客户等级、商务条款、意向度)→ 写回上下文。</para>
/// </summary>
public sealed class BusinessService : IWorkflowStep
{
// 标准日志器(DI 注入)。
private readonly ILogger<BusinessService> _logger;
/// <summary>
/// 构造函数(依赖注入)。
/// </summary>
/// <param name="logger">标准日志器。</param>
public BusinessService(ILogger<BusinessService> logger)
{
_logger = logger;
}
/// <summary>
/// 步骤名称。
/// </summary>
public string StepName => "业务跟单";
/// <summary>
/// 环节枚举。
/// </summary>
public WorkflowStage Stage => WorkflowStage.Business;
/// <summary>
/// 执行业务跟单。
/// </summary>
/// <param name="context">订单工作流上下文。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>执行结果。</returns>
public async Task<Result> ExecuteAsync(WorkflowContext context, CancellationToken cancellationToken)
{
// 1) 校验输入 DTO。
BusinessRequestDto? dto = context.Business;
if (dto is null)
{
return Result.Fail("缺少业务输入 DTO。", "DTO_MISSING");
}
_logger.LogInformation("【{Stage}】订单#{OrderId} 开始:客户={Customer} 等级={Level} 跟单员={Manager}",
StepName, context.OrderId, context.Order.CustomerName, dto.CustomerLevel, dto.AccountManager);
// 2) 模拟客户沟通与条款确认。
await Task.Delay(Random.Shared.Next(50, 130), cancellationToken);
// 3) 生成业务跟单实体。
context.BusinessResult = new BusinessOrder
{
BusinessNo = $"BIZ-{context.OrderId}",
CustomerLevel = dto.CustomerLevel,
Terms = dto.Terms,
AccountManager = dto.AccountManager,
IntentionScore = dto.IntentionScore,
IsConfirmed = true
};
_logger.LogInformation("【{Stage}】订单#{OrderId} 完成:商务条款已确认,意向度={Score}",
StepName, context.OrderId, dto.IntentionScore);
return Result.Ok();
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : DesignService.cs
*/
using JewelryWorkflow.Domain.Common;
using JewelryWorkflow.Domain.Contracts;
using JewelryWorkflow.Domain.Dtos;
using JewelryWorkflow.Domain.Entities;
using JewelryWorkflow.Domain.Enums;
using Microsoft.Extensions.Logging;
namespace JewelryWorkflow.Application.Services;
/// <summary>
/// 设计制图服务(第 2 环节)。
/// <para>职责:校验设计输入 → 生成设计图纸实体(含工艺要求、审核通过标记)→ 写回上下文。</para>
/// </summary>
public sealed class DesignService : IWorkflowStep
{
// 标准日志器(DI 注入)。
private readonly ILogger<DesignService> _logger;
/// <summary>
/// 构造函数(依赖注入)。
/// </summary>
/// <param name="logger">标准日志器。</param>
public DesignService(ILogger<DesignService> logger)
{
_logger = logger;
}
/// <summary>
/// 步骤名称。
/// </summary>
public string StepName => "设计制图";
/// <summary>
/// 环节枚举。
/// </summary>
public WorkflowStage Stage => WorkflowStage.DesignDrawing;
/// <summary>
/// 执行设计制图。
/// </summary>
/// <param name="context">订单工作流上下文。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>执行结果。</returns>
public async Task<Result> ExecuteAsync(WorkflowContext context, CancellationToken cancellationToken)
{
// 1) 校验输入 DTO。
DesignRequestDto? dto = context.Design;
if (dto is null)
{
return Result.Fail("缺少设计制图输入 DTO。", "DTO_MISSING");
}
_logger.LogInformation("【{Stage}】订单#{OrderId} 开始:款式={Style} 设计师={Designer}",
StepName, context.OrderId, dto.StyleName, dto.DesignerName);
// 2) 生成设计图纸实体(首次版本 v1,模拟设计审核通过)。
context.DesignResult = new DesignDrawing
{
DrawingNo = $"DRW-{context.OrderId}",
StyleName = dto.StyleName,
Version = 1,
DesignerName = dto.DesignerName,
CraftRequirement = dto.CraftRequirement,
IsApproved = true,
ApprovedBy = "设计总监",
DesignedAt = DateTime.Now
};
// 3) 模拟设计工时(体现并发处理)。
await Task.Delay(Random.Shared.Next(80, 200), cancellationToken);
_logger.LogInformation("【{Stage}】订单#{OrderId} 完成:图纸={DrawingNo} 已审核通过",
StepName, context.OrderId, context.DesignResult.DrawingNo);
return Result.Ok();
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : FinanceService.cs
*/
using JewelryWorkflow.Domain.Common;
using JewelryWorkflow.Domain.Contracts;
using JewelryWorkflow.Domain.Dtos;
using JewelryWorkflow.Domain.Entities;
using JewelryWorkflow.Domain.Enums;
using Microsoft.Extensions.Logging;
namespace JewelryWorkflow.Application.Services;
/// <summary>
/// 财务服务(支持环节:财务结算)。
/// <para>职责:校验财务输入 → 生成财务结算单实体(采购付款/销售收款/毛利/发票)→ 写回上下文。</para>
/// </summary>
public sealed class FinanceService : IWorkflowStep
{
// 标准日志器(DI 注入)。
private readonly ILogger<FinanceService> _logger;
/// <summary>
/// 构造函数(依赖注入)。
/// </summary>
/// <param name="logger">标准日志器。</param>
public FinanceService(ILogger<FinanceService> logger)
{
_logger = logger;
}
/// <summary>
/// 步骤名称。
/// </summary>
public string StepName => "财务结算";
/// <summary>
/// 环节枚举。
/// </summary>
public WorkflowStage Stage => WorkflowStage.Finance;
/// <summary>
/// 执行财务结算。
/// </summary>
/// <param name="context">订单工作流上下文。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>执行结果。</returns>
public async Task<Result> ExecuteAsync(WorkflowContext context, CancellationToken cancellationToken)
{
// 1) 校验输入 DTO。
FinanceRequestDto? dto = context.Finance;
if (dto is null)
{
return Result.Fail("缺少财务输入 DTO。", "DTO_MISSING");
}
_logger.LogInformation("【{Stage}】订单#{OrderId} 开始:采购付款={Purchase} 销售收款={Sales}",
StepName, context.OrderId, dto.PurchasePayment, dto.SalesReceipt);
// 2) 模拟财务核算。
await Task.Delay(Random.Shared.Next(60, 140), cancellationToken);
// 3) 生成结算单:毛利 = 销售收款 - 采购付款;需要开票则生成发票号。
decimal grossProfit = dto.SalesReceipt - dto.PurchasePayment;
context.FinanceResult = new FinanceInvoice
{
SettlementNo = $"FIN-{context.OrderId}",
PurchasePayment = dto.PurchasePayment,
SalesReceipt = dto.SalesReceipt,
GrossProfit = grossProfit,
InvoiceNo = dto.NeedInvoice ? $"INV-{context.OrderId}-{DateTime.Now:yyyyMMdd}" : string.Empty,
IsSettled = true,
SettledAt = DateTime.Now
};
_logger.LogInformation("【{Stage}】订单#{OrderId} 完成:毛利={GrossProfit}元 发票={InvoiceNo}",
StepName, context.OrderId, grossProfit, context.FinanceResult.InvoiceNo);
return Result.Ok();
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : HrService.cs
*/
using JewelryWorkflow.Domain.Common;
using JewelryWorkflow.Domain.Contracts;
using JewelryWorkflow.Domain.Dtos;
using JewelryWorkflow.Domain.Entities;
using JewelryWorkflow.Domain.Enums;
using Microsoft.Extensions.Logging;
namespace JewelryWorkflow.Application.Services;
/// <summary>
/// 人事行政服务(支持环节:行政协同)。
/// <para>职责:校验人事输入 → 生成人事行政记录实体(经办员工、部门、行政事务)→ 写回上下文。</para>
/// </summary>
public sealed class HrService : IWorkflowStep
{
// 标准日志器(DI 注入)。
private readonly ILogger<HrService> _logger;
/// <summary>
/// 构造函数(依赖注入)。
/// </summary>
/// <param name="logger">标准日志器。</param>
public HrService(ILogger<HrService> logger)
{
_logger = logger;
}
/// <summary>
/// 步骤名称。
/// </summary>
public string StepName => "人事行政";
/// <summary>
/// 环节枚举。
/// </summary>
public WorkflowStage Stage => WorkflowStage.HrAdmin;
/// <summary>
/// 执行人事行政协同。
/// </summary>
/// <param name="context">订单工作流上下文。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>执行结果。</returns>
public async Task<Result> ExecuteAsync(WorkflowContext context, CancellationToken cancellationToken)
{
// 1) 校验输入 DTO。
HrRequestDto? dto = context.HrAdmin;
if (dto is null)
{
return Result.Fail("缺少人事行政输入 DTO。", "DTO_MISSING");
}
_logger.LogInformation("【{Stage}】订单#{OrderId} 开始:经办={Employee}({Dept}) 事务={Task}",
StepName, context.OrderId, dto.EmployeeName, dto.Department, dto.AdminTask);
// 2) 模拟行政事务处理。
await Task.Delay(Random.Shared.Next(40, 110), cancellationToken);
// 3) 生成人事行政记录实体。
context.HrResult = new HrRecord
{
HrNo = $"HR-{context.OrderId}",
EmployeeNo = dto.EmployeeNo,
EmployeeName = dto.EmployeeName,
Department = dto.Department,
AdminTask = dto.AdminTask,
IsCompleted = true
};
_logger.LogInformation("【{Stage}】订单#{OrderId} 完成:行政事务已办结",
StepName, context.OrderId);
return Result.Ok();
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : ItService.cs
*/
using JewelryWorkflow.Domain.Common;
using JewelryWorkflow.Domain.Contracts;
using JewelryWorkflow.Domain.Dtos;
using JewelryWorkflow.Domain.Entities;
using JewelryWorkflow.Domain.Enums;
using Microsoft.Extensions.Logging;
namespace JewelryWorkflow.Application.Services;
/// <summary>
/// IT 服务(支持环节:信息系统与权限协同)。
/// <para>职责:校验 IT 输入 → 生成 IT 记录实体(系统单据、设备资产、权限任务)→ 写回上下文。</para>
/// </summary>
public sealed class ItService : IWorkflowStep
{
// 标准日志器(DI 注入)。
private readonly ILogger<ItService> _logger;
/// <summary>
/// 构造函数(依赖注入)。
/// </summary>
/// <param name="logger">标准日志器。</param>
public ItService(ILogger<ItService> logger)
{
_logger = logger;
}
/// <summary>
/// 步骤名称。
/// </summary>
public string StepName => "IT协同";
/// <summary>
/// 环节枚举。
/// </summary>
public WorkflowStage Stage => WorkflowStage.InformationTechnology;
/// <summary>
/// 执行 IT 协同。
/// </summary>
/// <param name="context">订单工作流上下文。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>执行结果。</returns>
public async Task<Result> ExecuteAsync(WorkflowContext context, CancellationToken cancellationToken)
{
// 1) 校验输入 DTO。
ItRequestDto? dto = context.InformationTechnology;
if (dto is null)
{
return Result.Fail("缺少 IT 输入 DTO。", "DTO_MISSING");
}
_logger.LogInformation("【{Stage}】订单#{OrderId} 开始:系统={System} 权限任务={Task}",
StepName, context.OrderId, dto.SystemName, dto.PermissionTask);
// 2) 模拟系统单据创建与权限开通。
await Task.Delay(Random.Shared.Next(40, 110), cancellationToken);
// 3) 生成 IT 记录实体。
context.ItResult = new ItAsset
{
ItNo = $"IT-{context.OrderId}",
SystemName = dto.SystemName,
SystemDocNo = $"{dto.SystemName}-{context.OrderId}",
AssetNo = dto.AssetNo,
PermissionTask = dto.PermissionTask,
IsCompleted = true
};
_logger.LogInformation("【{Stage}】订单#{OrderId} 完成:单据={DocNo} 权限已开通",
StepName, context.OrderId, context.ItResult.SystemDocNo);
return Result.Ok();
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : LogisticsService.cs
*/
using JewelryWorkflow.Domain.Common;
using JewelryWorkflow.Domain.Contracts;
using JewelryWorkflow.Domain.Dtos;
using JewelryWorkflow.Domain.Entities;
using JewelryWorkflow.Domain.Enums;
using Microsoft.Extensions.Logging;
namespace JewelryWorkflow.Application.Services;
/// <summary>
/// 物流服务(第 6 环节,生产主线收尾)。
/// <para>职责:校验物流输入 → 生成物流运单实体(承运商、收件信息、发货)→ 写回上下文。</para>
/// </summary>
public sealed class LogisticsService : IWorkflowStep
{
// 标准日志器(DI 注入)。
private readonly ILogger<LogisticsService> _logger;
/// <summary>
/// 构造函数(依赖注入)。
/// </summary>
/// <param name="logger">标准日志器。</param>
public LogisticsService(ILogger<LogisticsService> logger)
{
_logger = logger;
}
/// <summary>
/// 步骤名称。
/// </summary>
public string StepName => "物流";
/// <summary>
/// 环节枚举。
/// </summary>
public WorkflowStage Stage => WorkflowStage.Logistics;
/// <summary>
/// 执行物流发货。
/// </summary>
/// <param name="context">订单工作流上下文。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>执行结果。</returns>
public async Task<Result> ExecuteAsync(WorkflowContext context, CancellationToken cancellationToken)
{
// 1) 校验输入 DTO 与上游包装单。
LogisticsRequestDto? dto = context.Logistics;
PackageInfo? pkg = context.PackagingResult;
if (dto is null || pkg is null)
{
return Result.Fail("缺少物流输入或上游包装单。", "PRECONDITION_MISSING");
}
_logger.LogInformation("【{Stage}】订单#{OrderId} 开始:承运商={Carrier} 收件人={Receiver}",
StepName, context.OrderId, dto.CarrierName, dto.ReceiverName);
// 2) 模拟运单创建与发货。
await Task.Delay(Random.Shared.Next(50, 130), cancellationToken);
// 3) 生成物流运单并写回上下文。
context.LogisticsResult = new LogisticsWaybill
{
WaybillNo = $"WB-{context.OrderId}",
PackageNo = pkg.PackageNo,
CarrierName = dto.CarrierName,
ReceiverName = dto.ReceiverName,
ReceiverAddress = dto.ReceiverAddress,
IsShipped = true,
ShippedAt = DateTime.Now,
TrackingStatus = "运输中"
};
_logger.LogInformation("【{Stage}】订单#{OrderId} 完成:运单={WaybillNo} 已发货",
StepName, context.OrderId, context.LogisticsResult.WaybillNo);
return Result.Ok();
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : MarketingService.cs
*/
using JewelryWorkflow.Domain.Common;
using JewelryWorkflow.Domain.Contracts;
using JewelryWorkflow.Domain.Dtos;
using JewelryWorkflow.Domain.Entities;
using JewelryWorkflow.Domain.Enums;
using Microsoft.Extensions.Logging;
namespace JewelryWorkflow.Application.Services;
/// <summary>
/// 营销推广服务(支持环节:市场推广)。
/// <para>职责:校验营销输入 → 生成营销活动实体(渠道、预算、曝光预估)→ 写回上下文。</para>
/// </summary>
public sealed class MarketingService : IWorkflowStep
{
// 标准日志器(DI 注入)。
private readonly ILogger<MarketingService> _logger;
/// <summary>
/// 构造函数(依赖注入)。
/// </summary>
/// <param name="logger">标准日志器。</param>
public MarketingService(ILogger<MarketingService> logger)
{
_logger = logger;
}
/// <summary>
/// 步骤名称。
/// </summary>
public string StepName => "营销推广";
/// <summary>
/// 环节枚举。
/// </summary>
public WorkflowStage Stage => WorkflowStage.Marketing;
/// <summary>
/// 执行营销推广。
/// </summary>
/// <param name="context">订单工作流上下文。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>执行结果。</returns>
public async Task<Result> ExecuteAsync(WorkflowContext context, CancellationToken cancellationToken)
{
// 1) 校验输入 DTO。
MarketingRequestDto? dto = context.Marketing;
if (dto is null)
{
return Result.Fail("缺少营销推广输入 DTO。", "DTO_MISSING");
}
_logger.LogInformation("【{Stage}】订单#{OrderId} 开始:活动={Campaign} 渠道={Channel} 预算={Budget}",
StepName, context.OrderId, dto.CampaignName, dto.Channel, dto.Budget);
// 2) 模拟活动配置与上线。
await Task.Delay(Random.Shared.Next(50, 130), cancellationToken);
// 3) 生成营销活动实体(曝光量按预算 × 100 预估)。
context.MarketingResult = new MarketingCampaign
{
CampaignNo = $"MKT-{context.OrderId}",
CampaignName = dto.CampaignName,
Channel = dto.Channel,
Budget = dto.Budget,
EstimatedImpressions = (long)(dto.Budget * 100),
IsLaunched = true,
LaunchedAt = DateTime.Now
};
_logger.LogInformation("【{Stage}】订单#{OrderId} 完成:活动={CampaignNo} 已上线,预估曝光={Impressions}",
StepName, context.OrderId, context.MarketingResult.CampaignNo, context.MarketingResult.EstimatedImpressions);
return Result.Ok();
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : MaterialProcurementService.cs
*/
using JewelryWorkflow.Application.Adapters;
using JewelryWorkflow.Domain.Common;
using JewelryWorkflow.Domain.Contracts;
using JewelryWorkflow.Domain.Dtos;
using JewelryWorkflow.Domain.Entities;
using JewelryWorkflow.Domain.Enums;
using Microsoft.Extensions.Logging;
namespace JewelryWorkflow.Application.Services;
/// <summary>
/// 原料采购核验服务(第 1 环节:生产主线入口)。
/// <para>职责:校验原料采购请求 → 调用供应商资质核验适配器(重试+熔断)→
/// 生成原料批次实体(合格入库/不合格退货)→ 写回工作流上下文。</para>
/// </summary>
public sealed class MaterialProcurementService : IWorkflowStep
{
// 标准日志器(DI 注入:控制台与文件统一输出)。
private readonly ILogger<MaterialProcurementService> _logger;
// 供应商资质核验适配器(弹性:重试+熔断)。
private readonly SupplierInspectionAdapter _inspectionAdapter;
/// <summary>
/// 构造函数(依赖注入)。
/// </summary>
/// <param name="logger">标准日志器。</param>
/// <param name="inspectionAdapter">供应商核验适配器。</param>
public MaterialProcurementService(ILogger<MaterialProcurementService> logger, SupplierInspectionAdapter inspectionAdapter)
{
_logger = logger;
_inspectionAdapter = inspectionAdapter;
}
/// <summary>
/// 步骤名称。
/// </summary>
public string StepName => "原料采购核验";
/// <summary>
/// 环节枚举。
/// </summary>
public WorkflowStage Stage => WorkflowStage.MaterialProcurement;
/// <summary>
/// 执行原料采购核验。
/// </summary>
/// <param name="context">订单工作流上下文。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>执行结果。</returns>
public async Task<Result> ExecuteAsync(WorkflowContext context, CancellationToken cancellationToken)
{
// 1) 校验输入 DTO。
MaterialProcurementDto? dto = context.Procurement;
if (dto is null)
{
return Result.Fail("缺少原料采购核验输入 DTO。", "DTO_MISSING");
}
Guard.NotNull(context.Order, nameof(context.Order));
_logger.LogInformation("【{Stage}】订单#{OrderId} 开始:{Type} {Weight}g 供应商={Supplier}",
StepName, context.OrderId, dto.MaterialType, dto.WeightGram, dto.SupplierName);
// 2) 调用供应商资质核验适配器(自动重试 + 熔断保护)。
var inspection = await _inspectionAdapter.CallAsync(
new SupplierInspectionAdapter.InspectionRequest
{
SupplierName = dto.SupplierName,
CertificateNo = dto.CertificateNo,
MaterialType = dto.MaterialType,
WeightGram = dto.WeightGram
}, cancellationToken);
// 3) 核验失败:原料批次标记不合格退货,环节失败(阻断下游)。
if (!inspection.IsSuccess)
{
context.MaterialBatchResult = new MaterialBatch
{
BatchNo = $"MB-{context.OrderId}",
MaterialType = dto.MaterialType,
WeightGram = dto.WeightGram,
Purity = dto.Purity,
SupplierName = dto.SupplierName,
CertificateNo = dto.CertificateNo,
Status = MaterialStatus.Rejected,
VerifyRemark = inspection.ErrorMessage ?? string.Empty
};
_logger.LogError("【{Stage}】订单#{OrderId} 原料核验失败:{Reason}",
StepName, context.OrderId, inspection.ErrorMessage);
return Result.Fail(inspection.ErrorMessage ?? "原料核验失败。", inspection.ErrorCode);
}
// 4) 核验通过:生成合格原料批次并写回上下文。
context.MaterialBatchResult = new MaterialBatch
{
BatchNo = $"MB-{context.OrderId}",
MaterialType = dto.MaterialType,
WeightGram = dto.WeightGram,
Purity = dto.Purity,
SupplierName = dto.SupplierName,
CertificateNo = dto.CertificateNo,
PurchasePrice = dto.Budget,
Status = MaterialStatus.Approved,
VerifyRemark = inspection.Data!.Remark
};
// 5) 模拟业务处理耗时(体现多线程并发流水线)。
await Task.Delay(Random.Shared.Next(60, 160), cancellationToken);
_logger.LogInformation("【{Stage}】订单#{OrderId} 完成:批次={BatchNo} 状态=合格入库",
StepName, context.OrderId, context.MaterialBatchResult.BatchNo);
return Result.Ok();
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : PackagingService.cs
*/
using JewelryWorkflow.Domain.Common;
using JewelryWorkflow.Domain.Contracts;
using JewelryWorkflow.Domain.Dtos;
using JewelryWorkflow.Domain.Entities;
using JewelryWorkflow.Domain.Enums;
using Microsoft.Extensions.Logging;
namespace JewelryWorkflow.Application.Services;
/// <summary>
/// 包装服务(第 5 环节)。
/// <para>职责:校验包装输入 → 生成包装信息实体(包装类型、防伪标识、装箱)→ 写回上下文。</para>
/// </summary>
public sealed class PackagingService : IWorkflowStep
{
// 标准日志器(DI 注入)。
private readonly ILogger<PackagingService> _logger;
/// <summary>
/// 构造函数(依赖注入)。
/// </summary>
/// <param name="logger">标准日志器。</param>
public PackagingService(ILogger<PackagingService> logger)
{
_logger = logger;
}
/// <summary>
/// 步骤名称。
/// </summary>
public string StepName => "包装";
/// <summary>
/// 环节枚举。
/// </summary>
public WorkflowStage Stage => WorkflowStage.Packaging;
/// <summary>
/// 执行包装。
/// </summary>
/// <param name="context">订单工作流上下文。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>执行结果。</returns>
public async Task<Result> ExecuteAsync(WorkflowContext context, CancellationToken cancellationToken)
{
// 1) 校验输入 DTO 与上游质检报告。
PackagingRequestDto? dto = context.Packaging;
QualityReport? quality = context.QualityResult;
if (dto is null || quality is null)
{
return Result.Fail("缺少包装输入或上游质检报告。", "PRECONDITION_MISSING");
}
_logger.LogInformation("【{Stage}】订单#{OrderId} 开始:类型={Type} 防伪={AntiFake}",
StepName, context.OrderId, dto.PackageType, dto.HasAntiFakeTag);
// 2) 模拟包装作业。
await Task.Delay(Random.Shared.Next(50, 120), cancellationToken);
// 3) 生成包装信息并写回上下文。
context.PackagingResult = new PackageInfo
{
PackageNo = $"PKG-{context.OrderId}",
QualityReportNo = quality.ReportNo,
PackageType = dto.PackageType,
HasAntiFakeTag = dto.HasAntiFakeTag,
BoxCount = dto.BoxCount,
PackagedAt = DateTime.Now
};
_logger.LogInformation("【{Stage}】订单#{OrderId} 完成:包装单={PackageNo}",
StepName, context.OrderId, context.PackagingResult.PackageNo);
return Result.Ok();
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : ProductionService.cs
*/
using JewelryWorkflow.Domain.Common;
using JewelryWorkflow.Domain.Contracts;
using JewelryWorkflow.Domain.Dtos;
using JewelryWorkflow.Domain.Entities;
using JewelryWorkflow.Domain.Enums;
using Microsoft.Extensions.Logging;
namespace JewelryWorkflow.Application.Services;
/// <summary>
/// 加工生产服务(第 3 环节)。
/// <para>职责:校验生产输入 → 依据工艺路线与原料批次生成生产工单实体(含损耗核算)→ 写回上下文。</para>
/// </summary>
public sealed class ProductionService : IWorkflowStep
{
// 标准日志器(DI 注入)。
private readonly ILogger<ProductionService> _logger;
/// <summary>
/// 构造函数(依赖注入)。
/// </summary>
/// <param name="logger">标准日志器。</param>
public ProductionService(ILogger<ProductionService> logger)
{
_logger = logger;
}
/// <summary>
/// 步骤名称。
/// </summary>
public string StepName => "加工生产";
/// <summary>
/// 环节枚举。
/// </summary>
public WorkflowStage Stage => WorkflowStage.Production;
/// <summary>
/// 执行加工生产。
/// </summary>
/// <param name="context">订单工作流上下文。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>执行结果。</returns>
public async Task<Result> ExecuteAsync(WorkflowContext context, CancellationToken cancellationToken)
{
// 1) 校验输入 DTO 与上游产出。
ProductionRequestDto? dto = context.Production;
MaterialBatch? batch = context.MaterialBatchResult;
if (dto is null || batch is null)
{
return Result.Fail("缺少生产输入或上游原料批次。", "PRECONDITION_MISSING");
}
_logger.LogInformation("【{Stage}】订单#{OrderId} 开始:工艺={Route} 目标重量={Weight}g",
StepName, context.OrderId, dto.ProcessRoute, dto.ExpectedWeightGram);
// 2) 模拟工艺执行(倒模/执模/镶石/抛光)。
await Task.Delay(Random.Shared.Next(120, 260), cancellationToken);
// 3) 生成生产工单:产出重量按 2% 加工损耗核算。
decimal outputWeight = dto.ExpectedWeightGram * 0.98m;
context.ProductionResult = new ProductionOrder
{
WorkOrderNo = $"WO-{context.OrderId}",
DrawingNo = context.DesignResult?.DrawingNo ?? string.Empty,
MaterialBatchNo = batch.BatchNo,
ProcessRoute = dto.ProcessRoute,
InputWeightGram = batch.WeightGram,
OutputWeightGram = outputWeight,
LossRatePercent = 2.0m,
IsFinished = true,
FinishedAt = DateTime.Now
};
_logger.LogInformation("【{Stage}】订单#{OrderId} 完成:工单={WorkOrderNo} 产出={Output}g 损耗率=2%",
StepName, context.OrderId, context.ProductionResult.WorkOrderNo, outputWeight);
return Result.Ok();
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : QualityService.cs
*/
using JewelryWorkflow.Application.Adapters;
using JewelryWorkflow.Domain.Common;
using JewelryWorkflow.Domain.Contracts;
using JewelryWorkflow.Domain.Dtos;
using JewelryWorkflow.Domain.Entities;
using JewelryWorkflow.Domain.Enums;
using Microsoft.Extensions.Logging;
namespace JewelryWorkflow.Application.Services;
/// <summary>
/// 质检服务(第 4 环节,质量把关)。
/// <para>职责:调用检测机构适配器获取权威检测数据 → 按工艺评分评定质检等级 →
/// 低于最低放行等级则判为不合格(次品返工/废品报废)→ 写回上下文。</para>
/// </summary>
public sealed class QualityService : IWorkflowStep
{
// 标准日志器(DI 注入)。
private readonly ILogger<QualityService> _logger;
// 宝石检测机构适配器(弹性:重试+熔断)。
private readonly GemLabAdapter _gemLabAdapter;
/// <summary>
/// 构造函数(依赖注入)。
/// </summary>
/// <param name="logger">标准日志器。</param>
/// <param name="gemLabAdapter">检测机构适配器。</param>
public QualityService(ILogger<QualityService> logger, GemLabAdapter gemLabAdapter)
{
_logger = logger;
_gemLabAdapter = gemLabAdapter;
}
/// <summary>
/// 步骤名称。
/// </summary>
public string StepName => "质检";
/// <summary>
/// 环节枚举。
/// </summary>
public WorkflowStage Stage => WorkflowStage.QualityInspection;
/// <summary>
/// 执行质检。
/// </summary>
/// <param name="context">订单工作流上下文。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>执行结果。</returns>
public async Task<Result> ExecuteAsync(WorkflowContext context, CancellationToken cancellationToken)
{
// 1) 校验输入 DTO 与上游产出。
QualityInspectionDto? dto = context.Inspection;
ProductionOrder? workOrder = context.ProductionResult;
if (dto is null || workOrder is null)
{
return Result.Fail("缺少质检输入或上游生产工单。", "PRECONDITION_MISSING");
}
_logger.LogInformation("【{Stage}】订单#{OrderId} 开始:质检员={Inspector} 最低放行等级={Grade}",
StepName, context.OrderId, dto.InspectorName, dto.MinimumPassGrade);
// 2) 调用检测机构适配器获取权威检测数据(自动重试 + 熔断保护)。
var lab = await _gemLabAdapter.CallAsync(
new GemLabAdapter.LabRequest
{
SampleNo = workOrder.WorkOrderNo,
MaterialType = context.MaterialBatchResult?.MaterialType ?? "贵金属",
DeclaredPurity = context.MaterialBatchResult?.Purity ?? "999"
}, cancellationToken);
// 3) 检测机构不可用:质检失败(阻断下游,进入失败分支)。
if (!lab.IsSuccess)
{
_logger.LogError("【{Stage}】订单#{OrderId} 检测机构调用失败:{Reason}",
StepName, context.OrderId, lab.ErrorMessage);
return Result.Fail(lab.ErrorMessage ?? "检测机构调用失败。", lab.ErrorCode);
}
// 4) 模拟质检耗时。
await Task.Delay(Random.Shared.Next(60, 150), cancellationToken);
// 5) 评定质检等级:工艺评分随机 85-100,90 分以上优等,其余合格。
int craftScore = Random.Shared.Next(85, 101);
QualityGrade grade = craftScore >= 90 ? QualityGrade.Excellent : QualityGrade.Qualified;
// 6) 低于最低放行等级:判定不合格(本演示中最低为合格,故合格以上放行)。
bool passed = grade >= dto.MinimumPassGrade;
context.QualityResult = new QualityReport
{
ReportNo = $"QC-{context.OrderId}",
WorkOrderNo = workOrder.WorkOrderNo,
InspectorName = dto.InspectorName,
MeasuredPurity = lab.Data!.MeasuredPurity,
MeasuredWeightGram = lab.Data.MeasuredWeightGram,
CraftScore = craftScore,
Grade = grade,
IsPassed = passed,
InspectedAt = DateTime.Now
};
if (!passed)
{
_logger.LogWarning("【{Stage}】订单#{OrderId} 质检未通过:等级={Grade} 低于要求={Minimum}",
StepName, context.OrderId, grade, dto.MinimumPassGrade);
return Result.Fail($"质检等级 {grade} 低于放行要求 {dto.MinimumPassGrade}。", "QUALITY_REJECTED");
}
_logger.LogInformation("【{Stage}】订单#{OrderId} 完成:报告={ReportNo} 等级={Grade} 评分={Score}",
StepName, context.OrderId, context.QualityResult.ReportNo, grade, craftScore);
return Result.Ok();
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : TrainingService.cs
*/
using JewelryWorkflow.Domain.Common;
using JewelryWorkflow.Domain.Contracts;
using JewelryWorkflow.Domain.Dtos;
using JewelryWorkflow.Domain.Entities;
using JewelryWorkflow.Domain.Enums;
using Microsoft.Extensions.Logging;
namespace JewelryWorkflow.Application.Services;
/// <summary>
/// 培训服务(支持环节:员工培训协同)。
/// <para>职责:校验培训输入 → 生成培训记录实体(课程、学时、考核)→ 写回上下文。</para>
/// </summary>
public sealed class TrainingService : IWorkflowStep
{
// 标准日志器(DI 注入)。
private readonly ILogger<TrainingService> _logger;
/// <summary>
/// 构造函数(依赖注入)。
/// </summary>
/// <param name="logger">标准日志器。</param>
public TrainingService(ILogger<TrainingService> logger)
{
_logger = logger;
}
/// <summary>
/// 步骤名称。
/// </summary>
public string StepName => "培训";
/// <summary>
/// 环节枚举。
/// </summary>
public WorkflowStage Stage => WorkflowStage.Training;
/// <summary>
/// 执行培训协同。
/// </summary>
/// <param name="context">订单工作流上下文。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>执行结果。</returns>
public async Task<Result> ExecuteAsync(WorkflowContext context, CancellationToken cancellationToken)
{
// 1) 校验输入 DTO。
TrainingRequestDto? dto = context.Training;
if (dto is null)
{
return Result.Fail("缺少培训输入 DTO。", "DTO_MISSING");
}
_logger.LogInformation("【{Stage}】订单#{OrderId} 开始:课程={Course} 学员={Trainee} 讲师={Lecturer}",
StepName, context.OrderId, dto.CourseName, dto.TraineeName, dto.LecturerName);
// 2) 模拟培训执行与考核。
await Task.Delay(Random.Shared.Next(40, 110), cancellationToken);
// 3) 生成培训记录实体(模拟考核通过)。
context.TrainingResult = new TrainingRecord
{
TrainingNo = $"TRN-{context.OrderId}",
CourseName = dto.CourseName,
TraineeName = dto.TraineeName,
LecturerName = dto.LecturerName,
Hours = dto.Hours,
IsPassed = true
};
_logger.LogInformation("【{Stage}】订单#{OrderId} 完成:学时={Hours}h 考核通过",
StepName, context.OrderId, dto.Hours);
return Result.Ok();
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : ConditionVariable.cs
*/
namespace JewelryWorkflow.Application.Workflows;
/// <summary>
/// 条件变量(Condition Variable Pattern)核心实现。
/// <para>基于 .NET 的 <see cref="Monitor"/>(Enter/Wait/PulseAll)实现经典条件变量语义:
/// - <see cref="Signal"/>:发出信号并唤醒所有等待线程(PulseAll);
/// - <see cref="Wait()"/>:阻塞当前线程直到收到信号;
/// - <see cref="Wait(TimeSpan)"/>:带超时的等待,防止永久阻塞。
/// 用途:珠宝业务流程流水线中,上游环节完成后 Signal 下游环节,下游环节 Wait 后继续,
/// 实现"前序未完成,后序不启动"的严格依赖编排。</para>
/// </summary>
public sealed class ConditionVariable
{
// 状态保护锁:所有等待与通知都必须在持有该锁时进行(Monitor 语义要求)。
private readonly object _gate = new();
// 信号位:true 表示已有未消费的信号;false 表示等待者需要继续阻塞。
private bool _signaled;
/// <summary>
/// 发出信号:唤醒所有正在等待的线程。
/// <para>典型用法:生产者完成生产后调用,通知消费者可以继续。</para>
/// </summary>
public void Signal()
{
lock (_gate)
{
_signaled = true;
// PulseAll 唤醒所有等待线程(比 Pulse 更安全,避免"唤醒丢失")。
Monitor.PulseAll(_gate);
}
}
/// <summary>
/// 无条件等待:阻塞当前线程,直到收到信号。
/// <para>典型用法:消费者在条件不满足时阻塞,收到信号后继续。</para>
/// </summary>
public void Wait()
{
lock (_gate)
{
// 使用 while 循环防"虚假唤醒":即使被唤醒,只要信号未置位就继续等待。
while (!_signaled)
{
Monitor.Wait(_gate);
}
// 消费信号:本次唤醒对应一次 Signal。
_signaled = false;
}
}
/// <summary>
/// 带超时的等待:阻塞当前线程,直到收到信号或超时。
/// </summary>
/// <param name="timeout">最大等待时长。</param>
/// <returns>true 表示在超时前收到信号;false 表示等待超时。</returns>
public bool Wait(TimeSpan timeout)
{
lock (_gate)
{
// 记录等待截止时间(支持循环等待中的超时判定)。
DateTime deadline = DateTime.UtcNow + timeout;
while (!_signaled)
{
// 计算剩余可等待时间。
TimeSpan remaining = deadline - DateTime.UtcNow;
if (remaining <= TimeSpan.Zero)
{
// 超时且仍未收到信号 → 返回失败。
return false;
}
// Monitor.Wait 支持超时参数,超时后自动重新获取锁返回。
Monitor.Wait(_gate, remaining);
}
_signaled = false;
return true;
}
}
/// <summary>
/// 重置信号位(用于批次开始前清理历史信号,避免批次间串扰)。
/// </summary>
public void Reset()
{
lock (_gate)
{
_signaled = false;
}
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : ConditionVariablePipeline.cs
*/
using JewelryWorkflow.Domain.Contracts;
using JewelryWorkflow.Domain.Dtos;
using Microsoft.Extensions.Logging;
namespace JewelryWorkflow.Application.Workflows;
/// <summary>
/// 条件变量流水线引擎(IWorkflowEngine 实现)。
/// <para>编排生产主线环节为一条有界流水线:
/// - 每个环节一个 <see cref="PipelineNode"/>(有界队列 + 专用工作线程),环节间并行;
/// - 整批完成通知使用 <see cref="ConditionVariable"/>(Monitor.Wait/PulseAll 条件变量模式):
/// 末节点每完成一个订单就累加完成计数,达到总数时 Signal,
/// 主线程 Wait 等待整批完成(带超时保护,防止死等)。
/// - 具备背压:上游环节产出过快时,下游有界队列会阻塞写入,实现全链路流控。
/// 适用于"前序环节完成才能执行后序环节"的严格依赖编排,且支持多订单高并发吞吐。</para>
/// </summary>
public sealed class ConditionVariablePipeline : IWorkflowEngine
{
/// <summary>
/// 生产主线环节步骤集合(按执行顺序,由 DI 注入)。
/// </summary>
private readonly IReadOnlyList<IWorkflowStep> _steps;
/// <summary>
/// 每个节点的输入队列容量(并发上限,由宿主配置)。
/// </summary>
private readonly int _nodeCapacity;
/// <summary>
/// 标准日志器(DI 注入)。
/// </summary>
private readonly ILogger<ConditionVariablePipeline> _logger;
/// <summary>
/// 整批完成条件变量(主线程等待所有订单流过全部环节)。
/// </summary>
private readonly ConditionVariable _allDone = new();
// 整批统计(Interlocked 原子更新,多线程安全)。
private int _expectedTotal; // 本批订单总数。
private int _completedCount; // 已完成的订单数。
private int _successCount; // 成功订单数。
private int _failureCount; // 失败订单数。
/// <summary>
/// 构造函数(依赖注入)。
/// </summary>
/// <param name="steps">生产主线环节步骤(有序集合)。</param>
/// <param name="nodeCapacity">每节点队列容量(默认 16)。</param>
/// <param name="logger">标准日志器。</param>
public ConditionVariablePipeline(IEnumerable<IWorkflowStep> steps, int nodeCapacity, ILogger<ConditionVariablePipeline> logger)
{
_steps = steps.ToArray();
_nodeCapacity = Math.Max(1, nodeCapacity);
_logger = logger;
if (_steps.Count == 0)
{
throw new ArgumentException("流水线至少需要一个环节步骤。", nameof(steps));
}
}
/// <summary>
/// 将一批订单上下文送入流水线并等待全部完成。
/// </summary>
/// <param name="contexts">订单上下文集合。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>完成统计。</returns>
public async Task<WorkflowRunSummary> RunBatchAsync(IReadOnlyList<WorkflowContext> contexts, CancellationToken cancellationToken)
{
Guard_Contexts(contexts);
// 记录批次开始时间(用于总耗时统计)。
var sw = System.Diagnostics.Stopwatch.StartNew();
_expectedTotal = contexts.Count;
Interlocked.Exchange(ref _completedCount, 0);
Interlocked.Exchange(ref _successCount, 0);
Interlocked.Exchange(ref _failureCount, 0);
// 1) 构建节点链:前一节点指向后一节点,末节点挂完成回调。
PipelineNode? head = null;
PipelineNode? prev = null;
foreach (IWorkflowStep step in _steps)
{
var node = new PipelineNode(step, _nodeCapacity, next: null, OnOrderCompleted, _logger);
if (head is null)
{
head = node;
}
else
{
prev!.SetNext(node);
}
prev = node;
}
// 2) 启动全部环节工作线程。
var nodes = new List<PipelineNode>();
for (var n = head; n is not null; n = n.Next)
{
n.Start();
nodes.Add(n);
}
// 3) 重置完成信号并投递全部订单到首节点。
// 首节点在节点链构建后必然非空(_steps 已校验非空),此处显式断言以消除可空警告。
if (head is null)
{
throw new InvalidOperationException("流水线节点链构建失败:首节点为空。");
}
_allDone.Reset();
_logger.LogInformation("【流水线】本批 {Count} 个订单开始投递,环节={Steps}",
contexts.Count, string.Join(" → ", _steps.Select(s => s.StepName)));
foreach (WorkflowContext context in contexts)
{
// 有界队列背压:写入方在队列满时自动阻塞,控制进入速率。
await head.Input.WriteAsync(context, cancellationToken);
}
// 投递完成:关闭首节点输入,触发全链级联退出。
head.CompleteInput();
// 4) 条件变量等待整批完成(带超时保护,防止某个环节死锁导致永久阻塞)。
bool finished = _allDone.Wait(TimeSpan.FromSeconds(120));
sw.Stop();
// 5) 汇总统计并输出。
var summary = new WorkflowRunSummary
{
Total = _expectedTotal,
SuccessCount = Volatile.Read(ref _successCount),
FailureCount = Volatile.Read(ref _failureCount),
ElapsedMilliseconds = sw.ElapsedMilliseconds
};
if (!finished)
{
// 超时:记录错误(完成数不足,可能有环节异常)。
_logger.LogError("【流水线】整批等待超时:已完成 {Completed}/{Total},请检查环节线程状态。",
Volatile.Read(ref _completedCount), _expectedTotal);
}
else
{
_logger.LogInformation("【流水线】整批完成:{Summary}", summary);
}
return summary;
}
/// <summary>
/// 末节点完成回调(由流水线工作线程调用,条件变量信号在此触发)。
/// </summary>
/// <param name="context">订单上下文。</param>
/// <param name="success">订单是否成功。</param>
private void OnOrderCompleted(WorkflowContext context, bool success)
{
// 原子更新统计。
if (success)
{
Interlocked.Increment(ref _successCount);
context.MarkCompleted();
}
else
{
Interlocked.Increment(ref _failureCount);
}
int completed = Interlocked.Increment(ref _completedCount);
_logger.LogInformation("【流水线】订单#{OrderId} 出流水线:{Result}({Completed}/{Total})",
context.OrderId, success ? "成功" : "失败", completed, _expectedTotal);
// 整批全部完成 → 条件变量发出信号,唤醒主线程。
if (completed >= _expectedTotal)
{
_allDone.Signal();
}
}
/// <summary>
/// 校验批次输入。
/// </summary>
/// <param name="contexts">订单上下文集合。</param>
private static void Guard_Contexts(IReadOnlyList<WorkflowContext> contexts)
{
if (contexts is null || contexts.Count == 0)
{
throw new ArgumentException("批次订单上下文不能为空。", nameof(contexts));
}
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : PipelineNode.cs
*/
using System.Threading.Channels;
using JewelryWorkflow.Domain.Contracts;
using JewelryWorkflow.Domain.Dtos;
using Microsoft.Extensions.Logging;
namespace JewelryWorkflow.Application.Workflows;
/// <summary>
/// 流水线节点(生产主线上的一个环节)。
/// <para>每个节点 = 一个业务环节步骤 + 一个有界输入队列(Channel)+ 一个专用工作线程。
/// - Channel 提供异步条件队列语义(队列空时消费者线程自动阻塞等待,即"条件等待");
/// - 有界容量提供背压(上游过快时写入阻塞),防止无界堆积;
/// - 单工作线程保证"环节内串行、环节间并行"的流水线并发模型。
/// 上游节点处理完成后将上下文转发给下游节点的输入队列,实现环节依赖编排。</para>
/// </summary>
internal sealed class PipelineNode
{
/// <summary>
/// 本节点承载的业务环节步骤。
/// </summary>
private readonly IWorkflowStep _step;
/// <summary>
/// 输入队列(有界 Channel,容量由引擎统一配置)。
/// </summary>
private readonly Channel<WorkflowContext> _input;
/// <summary>
/// 下游节点(为 null 表示本节点是末节点;构建链时通过 <see cref="SetNext"/> 连接)。
/// </summary>
private PipelineNode? _next;
/// <summary>
/// 末节点完成回调(参数:上下文、是否成功)。
/// </summary>
private readonly Action<WorkflowContext, bool> _onCompleted;
/// <summary>
/// 标准日志器(DI 注入)。
/// </summary>
private readonly ILogger _logger;
/// <summary>
/// 本节点专用工作线程(条件变量模式的消费者线程,Monitor/Channel 条件等待)。
/// </summary>
private readonly Thread _worker;
/// <summary>
/// 构造函数。
/// </summary>
/// <param name="step">业务环节步骤。</param>
/// <param name="capacity">输入队列容量。</param>
/// <param name="next">下游节点。</param>
/// <param name="onCompleted">末节点完成回调。</param>
/// <param name="logger">标准日志器。</param>
public PipelineNode(IWorkflowStep step, int capacity, PipelineNode? next, Action<WorkflowContext, bool> onCompleted, ILogger logger)
{
_step = step;
_next = next;
_onCompleted = onCompleted;
_logger = logger;
// 有界 Channel:FullMode.Wait 表示满时写入方阻塞等待(背压),SingleReader 优化单消费者性能。
_input = Channel.CreateBounded<WorkflowContext>(new BoundedChannelOptions(Math.Max(1, capacity))
{
FullMode = BoundedChannelFullMode.Wait,
SingleReader = true,
SingleWriter = false
});
// 每个环节一个专用后台线程(真实的多线程流水线)。
_worker = new Thread(RunLoop)
{
IsBackground = true,
Name = $"Pipeline-{step.StepName}"
};
}
/// <summary>
/// 输入队列写入端(上游节点/引擎向本节点投递订单上下文)。
/// </summary>
public ChannelWriter<WorkflowContext> Input => _input.Writer;
/// <summary>
/// 下游节点(为 null 表示末节点)。
/// </summary>
public PipelineNode? Next => _next;
/// <summary>
/// 连接下游节点(仅在启动前由引擎构建链时调用,运行期只读,无并发问题)。
/// </summary>
/// <param name="next">下游节点。</param>
public void SetNext(PipelineNode next) => _next = next;
/// <summary>
/// 启动本节点工作线程。
/// </summary>
public void Start() => _worker.Start();
/// <summary>
/// 关闭输入队列(引擎投递完成后调用;本节点消费完剩余项后级联关闭下游)。
/// </summary>
public void CompleteInput() => _input.Writer.TryComplete();
/// <summary>
/// 工作线程主循环:条件等待输入 → 处理 → 转发下游/完成回调。
/// </summary>
private void RunLoop()
{
try
{
// 条件等待:WaitToReadAsync 在队列空时挂起当前线程(条件变量语义),
// 上游写入或队列被 Complete 后恢复。
while (true)
{
bool hasData;
try
{
hasData = _input.Reader.WaitToReadAsync().AsTask().GetAwaiter().GetResult();
}
catch
{
// 队列异常关闭:结束循环。
break;
}
if (!hasData)
{
// 队列已关闭且无剩余数据:退出。
break;
}
// 批量取走当前队列中的全部上下文(提升吞吐)。
while (_input.Reader.TryRead(out WorkflowContext? context))
{
Process(context);
}
}
}
finally
{
// 本节点消费完输入后,级联关闭下游输入,保证全链 worker 优雅退出。
_next?.CompleteInput();
}
}
/// <summary>
/// 处理单个订单上下文。
/// </summary>
/// <param name="context">订单上下文。</param>
private void Process(WorkflowContext context)
{
bool success = true;
// 若上游环节已失败(失败快速通道),本环节不再执行业务,直接转发。
if (string.IsNullOrEmpty(context.FailureMessage))
{
try
{
// 在专用线程上同步等待异步业务执行(线程内串行,天然线程安全)。
var result = _step.ExecuteAsync(context, CancellationToken.None).GetAwaiter().GetResult();
if (!result.IsSuccess)
{
// 环节业务失败:标记订单失败(阻断语义:失败订单不再继续执行业务环节)。
context.MarkFailed(_step.Stage, result.ErrorMessage ?? "未知错误");
_logger.LogWarning("【{Stage}】订单#{OrderId} 环节失败:{Reason}",
_step.StepName, context.OrderId, result.ErrorMessage);
success = false;
}
}
catch (Exception ex)
{
// 未预期异常:同样标记失败,避免线程崩溃。
context.MarkFailed(_step.Stage, ex.Message);
_logger.LogError(ex, "【{Stage}】订单#{OrderId} 环节异常。", _step.StepName, context.OrderId);
success = false;
}
}
if (_next is not null)
{
// 非末节点:转发给下游(TryWrite;因容量为 Wait 模式,多数情况直接写入)。
while (!_next.Input.TryWrite(context))
{
// 下游队列满(背压):短暂让出 CPU 后重试,直到写入成功。
Thread.Yield();
}
}
else
{
// 末节点:订单最终成败以整链失败标记为准(任一环节失败即订单失败;
// 下游环节经由"失败快速通道"跳过执行,不覆盖失败状态)。
_onCompleted(context, string.IsNullOrEmpty(context.FailureMessage));
}
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:# 描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : SupportWorkflowRunner.cs
*/
using JewelryWorkflow.Domain.Contracts;
using JewelryWorkflow.Domain.Dtos;
using JewelryWorkflow.Domain.Enums;
using Microsoft.Extensions.Logging;
namespace JewelryWorkflow.Application.Workflows;
/// <summary>
/// 支持流程并行执行器(财务/营销推广/业务/人事行政/IT/培训)。
/// <para>生产主线(6 个环节)完成后,对每个订单并行执行 6 个支持环节:
/// - 为每个"订单 × 环节"组合提交并行任务,使用 SemaphoreSlim 限制最大并发度;
/// - 每个支持环节读取订单快照(<see cref="WorkflowContext.Snapshot"/>,线程安全只读),
/// 执行完成后将产出实体复制回原上下文(不同环节写不同属性,天然安全);
/// - 支持环节失败仅记录警告,不阻断其他环节(职责独立)。</para>
/// </summary>
public sealed class SupportWorkflowRunner
{
/// <summary>
/// 支持环节步骤集合(6 个,由 DI 注入)。
/// </summary>
private readonly IReadOnlyList<IWorkflowStep> _supportSteps;
/// <summary>
/// 最大并发度(同时执行的环节任务数上限)。
/// </summary>
private readonly int _maxConcurrency;
/// <summary>
/// 标准日志器(DI 注入)。
/// </summary>
private readonly ILogger<SupportWorkflowRunner> _logger;
/// <summary>
/// 构造函数(依赖注入)。
/// </summary>
/// <param name="supportSteps">支持环节步骤集合。</param>
/// <param name="maxConcurrency">最大并发度(默认 8)。</param>
/// <param name="logger">标准日志器。</param>
public SupportWorkflowRunner(IEnumerable<IWorkflowStep> supportSteps, int maxConcurrency, ILogger<SupportWorkflowRunner> logger)
{
_supportSteps = supportSteps.ToArray();
_maxConcurrency = Math.Max(1, maxConcurrency);
_logger = logger;
}
/// <summary>
/// 对一批订单并行执行全部支持环节。
/// </summary>
/// <param name="contexts">订单上下文集合(生产主线已完成的订单)。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>成功执行的环节任务数。</returns>
public async Task<int> RunForOrdersAsync(IReadOnlyList<WorkflowContext> contexts, CancellationToken cancellationToken)
{
if (contexts is null || contexts.Count == 0)
{
return 0;
}
_logger.LogInformation("【支持流程】开始为 {Orders} 个订单并行执行 {Steps} 个支持环节(并发上限 {Max})",
contexts.Count, string.Join("、", _supportSteps.Select(s => s.StepName)), _maxConcurrency);
// 并发度信号量(限制同时运行的环节任务数,避免线程池过载)。
using var semaphore = new SemaphoreSlim(_maxConcurrency);
var tasks = new List<Task>();
int successCount = 0;
foreach (WorkflowContext context in contexts)
{
// 每个订单一份只读快照(避免多环节并发写同一上下文造成竞态)。
WorkflowContext snapshot = context.Snapshot();
foreach (IWorkflowStep step in _supportSteps)
{
// 为每个"订单 × 环节"提交一个并行任务。
tasks.Add(Task.Run(async () =>
{
await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
// 执行支持环节(快照只读,产出挂到快照上)。
var result = await step.ExecuteAsync(snapshot, cancellationToken).ConfigureAwait(false);
if (result.IsSuccess)
{
// 将产出实体复制回原上下文(不同环节写不同属性,安全)。
CopyResult(context, snapshot, step.Stage);
Interlocked.Increment(ref successCount);
_logger.LogInformation("【支持流程】订单#{OrderId} 环节[{Step}]完成",
context.OrderId, step.StepName);
}
else
{
// 支持环节失败:仅记录警告,不阻断其他环节。
_logger.LogWarning("【支持流程】订单#{OrderId} 环节[{Step}]未完成:{Reason}",
context.OrderId, step.StepName, result.ErrorMessage);
}
}
catch (OperationCanceledException)
{
// 取消:静默退出。
}
catch (Exception ex)
{
_logger.LogError(ex, "【支持流程】订单#{OrderId} 环节[{Step}]异常。",
context.OrderId, step.StepName);
}
finally
{
semaphore.Release();
}
}, cancellationToken));
}
}
// 等待全部支持环节任务完成。
await Task.WhenAll(tasks).ConfigureAwait(false);
_logger.LogInformation("【支持流程】全部完成:成功执行 {Success} 个环节任务", Volatile.Read(ref successCount));
return Volatile.Read(ref successCount);
}
/// <summary>
/// 按环节类型把快照上的产出实体复制回原上下文。
/// </summary>
/// <param name="target">原上下文。</param>
/// <param name="source">快照上下文。</param>
/// <param name="stage">环节枚举。</param>
private static void CopyResult(WorkflowContext target, WorkflowContext source, WorkflowStage stage)
{
switch (stage)
{
case WorkflowStage.Finance:
target.FinanceResult = source.FinanceResult;
break;
case WorkflowStage.Marketing:
target.MarketingResult = source.MarketingResult;
break;
case WorkflowStage.Business:
target.BusinessResult = source.BusinessResult;
break;
case WorkflowStage.HrAdmin:
target.HrResult = source.HrResult;
break;
case WorkflowStage.InformationTechnology:
target.ItResult = source.ItResult;
break;
case WorkflowStage.Training:
target.TrainingResult = source.TrainingResult;
break;
}
}
}
调用:
cs
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:Condition Variable Pattern 条件变量模式 Synchronization 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/09/05 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : Program.cs
*/
using JewelryWorkflow.Application.Adapters;
using JewelryWorkflow.Application.Services;
using JewelryWorkflow.Application.Workflows;
using JewelryWorkflow.Domain.Contracts;
using JewelryWorkflow.Domain.Dtos;
using JewelryWorkflow.Domain.Entities;
using JewelryWorkflow.Infrastructure.Logging;
using JewelryWorkflow.Infrastructure.Repositories;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace JewelryWorkflow.Host;
/// <summary>
/// 程序入口(宿主层)。
/// <para>演示流程:
/// 1. 构建通用主机(HostBuilder):装配配置、标准日志(控制台 + 分级日期文件 + Karambolo 综合文件)、
/// 依赖注入全部服务(12 个业务环节服务、2 个弹性适配器、条件变量流水线引擎、支持流程执行器、内存仓储);
/// 2. 三阶段演示珠宝行业全程业务流程:
/// - 阶段一【正常流程】:5 个订单跑通"原料采购核验 → 设计制图 → 加工生产 → 质检 → 包装 → 物流"主线,
/// 随后 6 个支持环节(财务/营销/业务/人事/IT/培训)并行执行;
/// - 阶段二【重试演示】:供应商核验适配器注入瞬时故障(前 2 次失败),观察指数退避重试后成功;
/// - 阶段三【熔断演示】:供应商核验适配器注入持续故障,观察连续失败后熔断器打开、
/// 后续订单快速失败(CIRCUIT_OPEN);
/// 3. 输出汇总并打印日志目录结构(Logs/yyyy-MM-dd/app-{等级}.log)。</para>
/// </summary>
public static class Program
{
// ===== 辅助说明 =====
// 因静态类不能作为泛型参数,日志分类使用下方标记类型:ILogger<JewelryWorkflowHostMarker>,
// 控制台与文件输出分类显示为 "JewelryWorkflow.Host.JewelryWorkflowHostMarker"。
/// <summary>
/// 程序主入口。
/// </summary>
/// <param name="args">命令行参数。</param>
/// <returns>退出码(0 成功)。</returns>
public static async Task<int> Main(string[] args)
{
// ========== 1. 构建通用主机 ==========
using IHost host = BuildHost(args);
await host.StartAsync();
// 从 DI 解析日志器与核心服务。
ILogger<JewelryWorkflowHostMarker> logger = host.Services.GetRequiredService<ILogger<JewelryWorkflowHostMarker>>();
IWorkflowEngine pipeline = host.Services.GetRequiredService<IWorkflowEngine>();
SupportWorkflowRunner supportRunner = host.Services.GetRequiredService<SupportWorkflowRunner>();
SupplierInspectionAdapter supplierAdapter = host.Services.GetRequiredService<SupplierInspectionAdapter>();
InMemoryRepository<long, JewelryOrder> orderRepo = host.Services.GetRequiredService<InMemoryRepository<long, JewelryOrder>>();
logger.LogInformation("========== 珠宝行业企业级全程业务流程演示启动 ==========");
logger.LogInformation("生产主线:原料采购核验 → 设计制图 → 加工生产 → 质检 → 包装 → 物流(条件变量流水线)");
logger.LogInformation("支持环节:财务 / 营销推广 / 业务 / 人事行政 / IT / 培训(并行执行)");
try
{
// ========== 2. 阶段一:正常流程(适配器无故障) ==========
logger.LogInformation("【阶段一】正常流程:提交 5 个订单(无外部故障)");
supplierAdapter.Mode = SupplierInspectionAdapter.FailureMode.None;
await RunPipelinePhase(pipeline, supportRunner, orderRepo, logger, BuildOrders(1, 5));
// ========== 3. 阶段二:重试演示(瞬时故障 2 次后恢复) ==========
logger.LogInformation("【阶段二】重试演示:供应商核验服务瞬时抖动(前 2 次失败,第 3 次成功)");
supplierAdapter.Mode = SupplierInspectionAdapter.FailureMode.Transient;
await RunPipelinePhase(pipeline, supportRunner, orderRepo, logger, BuildOrders(6, 2));
// ========== 4. 阶段三:熔断演示(持续故障 → 熔断打开 → 快速失败) ==========
logger.LogInformation("【阶段三】熔断演示:供应商核验服务持续不可用(观察熔断打开与快速失败)");
supplierAdapter.Mode = SupplierInspectionAdapter.FailureMode.Broken;
await RunPipelinePhase(pipeline, supportRunner, orderRepo, logger, BuildOrders(8, 3));
// ========== 5. 汇总与日志结构展示 ==========
await PrintSummaryAsync(logger, orderRepo);
}
catch (Exception ex)
{
// 全局兜底:任何未预期异常都走标准日志。
logger.LogError(ex, "演示执行过程中发生未预期异常。");
return 1;
}
finally
{
// 优雅停止主机(触发各日志 Provider 的 Dispose,确保文件刷盘)。
await host.StopAsync();
logger.LogInformation("========== 演示结束 ==========");
}
// 等待消费者线程把最终日志刷盘(分级文件日志 200ms 定时刷盘 + Dispose 强刷)。
await Task.Delay(500);
return 0;
}
/// <summary>
/// 构建通用主机:配置、日志、依赖注入。
/// </summary>
/// <param name="args">命令行参数。</param>
/// <returns>主机实例。</returns>
private static IHost BuildHost(string[] args)
{
return Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args)
// 加载 appsettings.json(复制到输出目录)。
.ConfigureAppConfiguration((context, config) =>
{
config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
})
// 标准日志装配:控制台 + 分级日期文件 + Karambolo 综合文件。
.ConfigureLogging((context, logging) =>
{
logging.ClearProviders();
// 1) 控制台输出(Microsoft.Extensions.Logging.Console)。
logging.AddConsole();
// 2) 分级日期文件日志(自研:Logs/yyyy-MM-dd/app-{info,error,debug,warn}.log)。
logging.AddLeveledFile(options =>
{
IConfigurationSection section = context.Configuration.GetSection("Logging:LeveledFile");
options.RootPath = section["RootPath"] ?? "Logs";
options.Enabled = section.GetValue("Enabled", true);
options.FlushIntervalMilliseconds = section.GetValue("FlushIntervalMilliseconds", 200);
options.FlushThreshold = section.GetValue("FlushThreshold", 50);
options.MaxFileBytes = section.GetValue("MaxFileBytes", 5L * 1024 * 1024);
});
// 3) Karambolo 综合文件日志(Logs/app-combined-{yyyy-MM-dd}.log)。
logging.AddKaramboloCombinedFile();
// 全局最小等级(Trace 全部记录;控制台按此输出)。
logging.SetMinimumLevel(LogLevel.Trace);
})
// 依赖注入装配。
.ConfigureServices((context, services) =>
{
IConfigurationSection wf = context.Configuration.GetSection("Workflow");
int nodeCapacity = wf.GetValue("PipelineNodeCapacity", 16);
int supportMax = wf.GetValue("SupportMaxConcurrency", 8);
// ------ 弹性适配器(单例:熔断器状态跨调用共享,才能演示熔断效果)------
services.AddSingleton&lt;SupplierInspectionAdapter&gt;();
services.AddSingleton&lt;GemLabAdapter&gt;();
// ------ 12 个业务环节服务(注入 ILogger&lt;T&gt; 与适配器)------
services.AddSingleton&lt;MaterialProcurementService&gt;();
services.AddSingleton&lt;DesignService&gt;();
services.AddSingleton&lt;ProductionService&gt;();
services.AddSingleton&lt;QualityService&gt;();
services.AddSingleton&lt;PackagingService&gt;();
services.AddSingleton&lt;LogisticsService&gt;();
services.AddSingleton&lt;FinanceService&gt;();
services.AddSingleton&lt;MarketingService&gt;();
services.AddSingleton&lt;BusinessService&gt;();
services.AddSingleton&lt;HrService&gt;();
services.AddSingleton&lt;ItService&gt;();
services.AddSingleton&lt;TrainingService&gt;();
// ------ 条件变量流水线引擎(单例:装配生产主线 6 环节)------
services.AddSingleton&lt;IWorkflowEngine&gt;(sp =&gt;
new ConditionVariablePipeline(MainSteps(sp), nodeCapacity,
sp.GetRequiredService&lt;ILogger&lt;ConditionVariablePipeline&gt;&gt;()));
// ------ 支持流程并行执行器(装配支持环节 6 环节)------
services.AddSingleton(sp =&gt;
new SupportWorkflowRunner(SupportSteps(sp), supportMax,
sp.GetRequiredService&lt;ILogger&lt;SupportWorkflowRunner&gt;&gt;()));
// ------ 线程安全内存仓储(演示:保存完成的订单)------
services.AddSingleton(_ =&gt; new InMemoryRepository&lt;long, JewelryOrder&gt;(o =&gt; o.OrderId));
})
.Build();
}
/// <summary>
/// 生产主线步骤集合(按执行顺序)。
/// </summary>
/// <param name="sp">服务提供器。</param>
/// <returns>主线环节步骤数组。</returns>
private static IReadOnlyList<IWorkflowStep> MainSteps(IServiceProvider sp)
=> new IWorkflowStep[]
{
sp.GetRequiredService<MaterialProcurementService>(),
sp.GetRequiredService<DesignService>(),
sp.GetRequiredService<ProductionService>(),
sp.GetRequiredService<QualityService>(),
sp.GetRequiredService<PackagingService>(),
sp.GetRequiredService<LogisticsService>()
};
/// <summary>
/// 支持环节步骤集合(并行执行)。
/// </summary>
/// <param name="sp">服务提供器。</param>
/// <returns>支持环节步骤数组。</returns>
private static IReadOnlyList<IWorkflowStep> SupportSteps(IServiceProvider sp)
=> new IWorkflowStep[]
{
sp.GetRequiredService<FinanceService>(),
sp.GetRequiredService<MarketingService>(),
sp.GetRequiredService<BusinessService>(),
sp.GetRequiredService<HrService>(),
sp.GetRequiredService<ItService>(),
sp.GetRequiredService<TrainingService>()
};
/// <summary>
/// 构造一批订单上下文(每个订单包含主订单 + 12 个环节输入 DTO)。
/// </summary>
/// <param name="startId">起始订单编号。</param>
/// <param name="count">订单数量。</param>
/// <returns>订单上下文列表。</returns>
private static IReadOnlyList<WorkflowContext> BuildOrders(int startId, int count)
{
var contexts = new List<WorkflowContext>(count);
for (int i = 0; i < count; i++)
{
long id = startId + i;
var order = new JewelryOrder
{
OrderId = id,
CustomerName = $"客户{id}",
ProductDescription = $"18K金镶钻戒指 #{id}",
OrderAmount = 15000 + id * 1000,
CreatedAt = DateTime.Now
};
var ctx = new WorkflowContext(id, order)
{
// 原料采购核验输入。
Procurement = new MaterialProcurementDto
{
MaterialType = "18K金",
WeightGram = 18.5m,
Purity = "750",
SupplierName = $"金料供应商{id % 3 + 1}",
CertificateNo = $"CERT-{id}",
Budget = 6000 + id * 200
},
// 设计制图输入。
Design = new DesignRequestDto
{
StyleName = $"经典六爪款 {id}",
DesignerName = "设计师A",
CraftRequirement = "爪镶;抛光;内圈刻字"
},
// 加工生产输入。
Production = new ProductionRequestDto
{
ProcessRoute = "倒模;执模;镶石;抛光",
ExpectedWeightGram = 18.5m
},
// 质检输入。
Inspection = new QualityInspectionDto
{
InspectorName = "质检员B",
MinimumPassGrade = JewelryWorkflow.Domain.Enums.QualityGrade.Qualified
},
// 包装输入。
Packaging = new PackagingRequestDto
{
PackageType = "礼盒装",
HasAntiFakeTag = true,
BoxCount = 1
},
// 物流输入。
Logistics = new LogisticsRequestDto
{
CarrierName = "顺丰速运",
ReceiverName = $"收件人{id}",
ReceiverAddress = $"深圳市罗湖区水贝珠宝园 {id} 号"
},
// 财务输入。
Finance = new FinanceRequestDto
{
PurchasePayment = 6000 + id * 200,
SalesReceipt = 15000 + id * 1000,
NeedInvoice = true
},
// 营销推广输入。
Marketing = new MarketingRequestDto
{
CampaignName = $"新品首发 {id}",
Channel = "抖音电商",
Budget = 2000
},
// 业务输入。
Business = new BusinessRequestDto
{
CustomerLevel = id % 2 == 0 ? "VIP" : "普通",
Terms = "预付 30%,尾款发货前结清",
AccountManager = "跟单员C",
IntentionScore = 80 + (int)(id % 10)
},
// 人事行政输入。
HrAdmin = new HrRequestDto
{
EmployeeNo = $"E{100 + id}",
EmployeeName = "员工D",
Department = "销售部",
AdminTask = "为订单盖章用印"
},
// IT 输入。
InformationTechnology = new ItRequestDto
{
SystemName = "ERP",
PermissionTask = "开通订单查看权限"
},
// 培训输入。
Training = new TrainingRequestDto
{
CourseName = "珠宝销售话术训练",
TraineeName = "员工D",
LecturerName = "讲师E",
Hours = 4
}
};
contexts.Add(ctx);
}
return contexts;
}
/// <summary>
/// 运行一个阶段:主线流水线 + 支持流程并行执行 + 结果入仓储。
/// </summary>
/// <param name="pipeline">条件变量流水线引擎。</param>
/// <param name="supportRunner">支持流程执行器。</param>
/// <param name="orderRepo">订单仓储。</param>
/// <param name="logger">日志器。</param>
/// <param name="contexts">订单上下文。</param>
private static async Task RunPipelinePhase(
IWorkflowEngine pipeline,
SupportWorkflowRunner supportRunner,
InMemoryRepository<long, JewelryOrder> orderRepo,
ILogger<JewelryWorkflowHostMarker> logger,
IReadOnlyList<WorkflowContext> contexts)
{
// 1) 主线流水线(条件变量协调 6 个生产环节)。
WorkflowRunSummary summary = await pipeline.RunBatchAsync(contexts, CancellationToken.None);
logger.LogInformation("阶段主线汇总:{Summary}", summary);
// 2) 支持流程并行执行(财务/营销/业务/人事/IT/培训)。
int supportOk = await supportRunner.RunForOrdersAsync(contexts, CancellationToken.None);
logger.LogInformation("阶段支持流程汇总:成功执行 {SupportOk} 个环节任务", supportOk);
// 3) 把订单实体保存到内存仓储(演示仓储线程安全写)。
foreach (WorkflowContext ctx in contexts)
{
await orderRepo.SaveAsync(ctx.Order);
}
}
/// <summary>
/// 打印各订单最终结果表与日志目录结构。
/// </summary>
/// <param name="logger">日志器。</param>
/// <param name="orderRepo">订单仓储。</param>
private static async Task PrintSummaryAsync(ILogger<JewelryWorkflowHostMarker> logger, InMemoryRepository<long, JewelryOrder> orderRepo)
{
logger.LogInformation("========== 订单结果汇总 ==========");
IReadOnlyList<JewelryOrder> orders = await orderRepo.GetAllAsync();
foreach (JewelryOrder o in orders.OrderBy(x => x.OrderId))
{
// 控制台与文件统一输出(同一 ILogger 实例)。
logger.LogInformation("订单#{OrderId} [{Status}] 客户={Customer} 商品={Product} 金额={Amount}元 完成={CompletedAt}",
o.OrderId, o.Status, o.CustomerName, o.ProductDescription, o.OrderAmount,
o.CompletedAt?.ToString("HH:mm:ss") ?? "-");
}
logger.LogInformation("========== 日志文件结构(Logs 目录) ==========");
ListLogTree("Logs", 0, logger);
}
/// <summary>
/// 递归打印日志目录树(验证"按日期分文件夹、按等级分文件")。
/// </summary>
/// <param name="path">目录路径。</param>
/// <param name="depth">递归深度。</param>
/// <param name="logger">日志器。</param>
private static void ListLogTree(string path, int depth, ILogger<JewelryWorkflowHostMarker> logger)
{
string indent = new string(' ', depth * 2);
if (Directory.Exists(path))
{
foreach (string dir in Directory.GetDirectories(path))
{
logger.LogInformation("{Indent}📁 {Dir}", indent, Path.GetFileName(dir));
ListLogTree(dir, depth + 1, logger);
}
foreach (string file in Directory.GetFiles(path))
{
logger.LogInformation("{Indent}📄 {File} ({Size} 字节)", indent, Path.GetFileName(file), new FileInfo(file).Length);
}
}
else
{
logger.LogWarning("日志目录不存在:{Path}(可能尚未产生日志)", path);
}
}
}
/// <summary>
/// 宿主日志标记类型(供 <see cref="ILogger{T}"/> 作为泛型参数使用)。
/// </summary>
public sealed class JewelryWorkflowHostMarker
{
}
输出:
