前言
在珠宝企业的信息化建设中,业务流程往往横跨原料核验、设计出图、生产加工、质检包装、物流发货、财务记账与营销推广等多个环节,且不同业务(如标准订单、加急订单、定制订单)在整体流程一致的前提下,又存在各自差异化的处理步骤。如何在保证流程骨架统一、可复用的同时,又能灵活应对各业务环节的变化,是系统设计面临的核心问题。模板方法模式(Template Method Pattern)通过把不变的业务算法骨架封装在基类中,将可变步骤延迟到子类实现,为这类场景提供了一种结构清晰、扩展性强的解决方案。本文将以一个基于 C# .NET 10 的珠宝企业业务系统为例,从模式原理、领域建模、流程编排到并发调度与多业务扩展,系统讲解模板方法模式在真实业务系统中的落地实践。
一、模板方法模式核心原理
1.1 模式定义与适用场景
模板方法模式属于行为型设计模式,其核心思想是在抽象基类中定义一个稳定的算法骨架(模板方法),并将其中某些步骤的具体实现延迟到子类中完成。子类可以在不改变算法整体结构的前提下,重定义算法中的特定步骤,从而实现「复用公共流程、扩展差异逻辑」的目标。该模式特别适用于以下场景:多个业务流程共享相同的执行顺序与公共逻辑,但其中部分环节的处理方式各不相同;或者需要在流程的固定位置统一插入日志、权限校验、事务控制等横切逻辑,同时避免各子类重复编写相同代码。
1.2 模式结构与角色划分
模板方法模式通常包含两类核心角色。其一是抽象模板类(AbstractClass),负责定义算法的骨架方法(TemplateMethod),该方法按固定顺序调用若干步骤方法,其中既包含已实现的具体方法(ConcreteMethod),也包含交由子类实现的抽象方法(AbstractMethod)或钩子方法(HookMethod)。其二是具体模板类(ConcreteClass),继承抽象模板类并实现其中可变步骤的具体逻辑。通过这种结构,算法骨架被固化在基类中,而业务差异被收敛到各个子类内部,从而避免重复代码并提升扩展性。
1.3 模板方法模式与策略模式的区别
模板方法模式与策略模式在结构上具有一定相似性,但二者的关注点存在明显差异。模板方法模式侧重于在继承体系内固定算法的整体执行顺序,通过子类重写可变步骤来实现差异,属于「继承 + 重写」的复用方式;而策略模式则侧重于将可互换的算法族封装为独立策略对象,通过组合方式在运行时动态切换,属于「接口 + 组合」的复用方式。在实际的珠宝业务系统中,模板方法模式适合固定业务流程骨架,而策略模式则适合处理如运费计算、折扣规则等可动态替换的算法,二者常常配合使用。
二、珠宝业务领域建模与流程骨架设计
2.1 业务背景与核心流程梳理
珠宝企业的业务链条较长,一笔订单从客户下单到最终交付,通常需要依次经过原料核验、设计出图、生产加工、质量检测、成品包装、物流发货、财务记账与营销同步等八个核心阶段。不同业务类型在这些阶段上的处理细节存在差异,例如加急订单需要缩短生产周期并优先排产,高端定制订单需要额外的工艺审核环节,而普通标准订单则按常规流程执行。为了在统一流程骨架下承载这些差异,系统需要先对业务领域进行建模,明确各阶段共享的数据结构与状态流转规则。
2.2 领域实体与业务上下文设计
在领域建模层面,系统围绕业务流转设计了若干核心实体。JewelleryBusinessContext 作为贯穿全流程的业务上下文对象,集中承载客户信息、原料编码、设计图纸、成品信息、物流单号、财务凭证、营销活动编号等跨阶段共享数据,并通过 MoveToStage 与 MarkCompleted 方法记录阶段流转历史与最终执行结果。DesignDrawing、Product、LogisticsOrder 等实体分别描述设计图纸、成品与物流订单的详细信息,而 RawMaterial、Money、Address 等值对象则用于表达原料、金额与地址等不可变业务概念。通过将共享状态收敛到上下文对象中,模板方法各步骤之间可以方便地传递与读取数据,保证流程执行的一致性与可追溯性。
2.3 流程骨架的模板方法抽象
在完成领域建模后,系统通过抽象基类定义珠宝业务流程的统一骨架。该骨架以模板方法的形式依次编排原料核验、设计出图、生产加工、质量检测、包装、物流、财务与营销等步骤,其中公共逻辑(如日志记录、上下文状态更新、异常捕获)在基类中统一实现,而各业务类型的差异化处理则通过抽象方法或钩子方法暴露给子类。以标准珠宝订单流程为例,其具体子类只需实现各阶段差异化的业务规则,即可复用基类中完整的流程编排与公共处理逻辑,从而在保证流程一致性的同时,将业务扩展成本降到最低。
三、模板方法模式在业务系统中的落地实现
3.1 标准珠宝订单流程的模板方法实现
在具体实现层面,系统以 StandardJewelleryOrderFlow 作为标准珠宝订单流程的具体模板类,继承自抽象流程基类并实现各业务阶段的差异化逻辑。该流程覆盖从原料核验到营销同步的完整八步链路,每一步都通过上下文对象读取前置阶段产生的数据,并写入当前阶段的处理结果。例如在原料核验阶段,系统根据 RawMaterialCode 校验原料库存与质量状态;在设计出图阶段,系统生成 DesignDrawing 并登记图纸编号;在生产加工阶段,系统根据订单类型选择对应的排产策略并产出成品。通过模板方法模式,标准订单的完整业务链路被封装为一个可复用的流程组件,业务方只需调用统一的执行入口即可驱动整条流程。
3.2 基于依赖注入的服务注册与流程编排
为了让业务流程能够被灵活组装与调用,系统采用依赖注入(DI)容器对各类服务进行统一注册与管理。在 Program 入口中,基础设施层的外部系统适配器与仓储实现、应用层的流程服务与任务调度器均通过 AddSingleton 等方式注册到容器中。其中 StandardJewelleryOrderFlow 作为无状态流程服务被注册为单例,BusinessFlowService 作为应用服务对外提供 ExecuteFlowAsync 入口,而 BusinessTaskScheduler 则被配置为最大并发数为 8 的任务调度器。通过 DI 容器,业务流程的各个组件实现了松耦合组装,既便于单元测试替换依赖,也为后续扩展新的业务类型提供了清晰的接入点。
3.3 批量并发调度与多业务场景扩展
在真实企业环境中,业务系统往往需要同时处理大量订单,并覆盖人事、IT 运维、员工培训等非核心业务。为此,系统在模板方法流程之上进一步封装了 BusinessTaskScheduler 调度器,支持将多个业务请求批量提交并以受限并发方式执行,同时统计任务成功率与最大并发数等运行指标。此外,系统还通过 HumanResourceService、ItOperationService、StaffTrainService 等服务,将模板方法模式的应用范围从珠宝订单流程扩展到员工入职考勤、系统健康巡检、数据备份与培训课程管理等通用业务场景,充分展示了该模式在复杂企业应用中的通用价值与扩展能力。
总结
本文以珠宝企业业务系统为背景,系统讲解了模板方法模式从原理到落地的完整过程。通过将稳定的业务流程骨架封装在抽象基类中,并将差异化的业务步骤延迟到子类实现,系统在保证流程一致性与代码复用性的同时,显著降低了多业务场景下的扩展成本。结合领域建模、依赖注入与任务调度等工程实践,模板方法模式不仅适用于珠宝订单流程,也可平滑扩展到人事、运维、培训等通用业务领域。在实际项目中,建议读者根据业务复杂度合理划分流程步骤,善用钩子方法控制扩展点,并配合策略模式处理可动态替换的算法逻辑,从而构建出结构清晰、易于维护的企业业务系统。
参考资料
- Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides 著,李英军等译:《设计模式:可复用面向对象软件的基础》,机械工业出版社。
- Microsoft 官方文档:依赖注入(Dependency Injection)与 .NET 通用主机(Generic Host)使用指南。
- Robert C. Martin:《敏捷软件开发:原则、模式与实践》,人民邮电出版社。
- 涂聚文(Geovin Du):模板方法模式 TemplateMethod 行为模式系列示例代码与项目文档。
项目结构:

cs
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/07/04 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : DesignDrawing.cs
*/
using System;
using System.Collections.Generic;
using System.Text;
namespace TemplateMethodPattern.Domain.Entities
{
/// <summary>
/// 珠宝设计图纸实体
/// 表示珠宝产品的设计图纸信息,包含图纸编号、设计风格、设计师与版本管理
/// </summary>
public class DesignDrawing
{
/// <summary>
/// 图纸唯一标识ID
/// </summary>
public Guid DrawingId { get; set; }
/// <summary>
/// 图纸业务编号,如DR-20260901001
/// </summary>
public string DrawingNo { get; set; } = string.Empty;
/// <summary>
/// 图纸名称,如"经典六爪钻戒设计图"
/// </summary>
public string DrawingName { get; set; } = string.Empty;
/// <summary>
/// 设计风格描述,如"经典简约"、"复古宫廷"、"现代几何"
/// </summary>
public string DesignStyle { get; set; } = string.Empty;
/// <summary>
/// 设计师姓名
/// </summary>
public string DesignerName { get; set; } = string.Empty;
/// <summary>
/// 图纸版本号,用于设计迭代管理,如V1.0、V2.1
/// </summary>
public string Version { get; set; } = "V1.0";
/// <summary>
/// 图纸文件存储路径或URL
/// </summary>
public string FilePath { get; set; } = string.Empty;
/// <summary>
/// 设计说明文字,描述设计理念与工艺要求
/// </summary>
public string Description { get; set; } = string.Empty;
/// <summary>
/// 图纸是否已审核通过
/// </summary>
public bool IsApproved { get; set; }
/// <summary>
/// 审核人姓名
/// </summary>
public string? ApproverName { get; set; }
/// <summary>
/// 图纸创建时间
/// </summary>
public DateTime CreateTime { get; set; }
/// <summary>
/// 最后更新时间
/// </summary>
public DateTime UpdateTime { get; set; }
/// <summary>
/// 审核通过图纸,标记审核状态与审核人
/// </summary>
/// <param name="approverName">审核人姓名</param>
public void Approve(string approverName)
{
IsApproved = true;
ApproverName = approverName;
UpdateTime = DateTime.Now;
}
/// <summary>
/// 升级图纸版本号,用于设计迭代
/// </summary>
/// <param name="newVersion">新版本号</param>
public void UpgradeVersion(string newVersion)
{
Version = newVersion;
UpdateTime = DateTime.Now;
}
}
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
Author : geovindu,Geovin Du 涂聚文.
IDE : vs2026 c# .net 10
os : windows 10
database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
Datetime : 2026/07/04 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : JewelleryBusinessContext.cs
*/
using System;
using System.Collections.Generic;
using System.Text;
using TemplateMethodPattern.Domain.Enums;
using TemplateMethodPattern.Domain.ValueObjects;
namespace TemplateMethodPattern.Domain.Entities
{
/// <summary>
/// 珠宝业务流转上下文实体
/// 贯穿整个珠宝业务流程的核心上下文对象,携带所有业务阶段的共享数据与状态信息,
/// 在模板方法模式的各步骤间传递,确保流程数据的一致性与可追溯性
/// </summary>
public class JewelleryBusinessContext
{
/// <summary>
/// 业务上下文唯一标识ID
/// </summary>
public Guid ContextId { get; set; } = Guid.NewGuid();
/// <summary>
/// 业务订单编号,业务层面唯一标识,如ORD-20260901-001
/// </summary>
public string BusinessNo { get; set; } = string.Empty;
/// <summary>
/// 客户名称
/// </summary>
public string CustomerName { get; set; } = string.Empty;
/// <summary>
/// 客户联系电话
/// </summary>
public string CustomerPhone { get; set; } = string.Empty;
/// <summary>
/// 当前业务流程所处阶段
/// </summary>
public BusinessStage CurrentStage { get; set; } = BusinessStage.NotStarted;
/// <summary>
/// 关联的原料物料编码
/// </summary>
public string RawMaterialCode { get; set; } = string.Empty;
/// <summary>
/// 原料实体对象引用,核验阶段填充
/// </summary>
public RawMaterial? RawMaterial { get; set; }
/// <summary>
/// 原料核验是否通过
/// </summary>
public bool RawMaterialVerifyPassed { get; set; }
/// <summary>
/// 关联的设计图纸编号
/// </summary>
public string DesignDrawingNo { get; set; } = string.Empty;
/// <summary>
/// 设计图纸实体对象引用,设计阶段填充
/// </summary>
public DesignDrawing? DesignDrawing { get; set; }
/// <summary>
/// 关联的成品产品编号
/// </summary>
public string ProductCode { get; set; } = string.Empty;
/// <summary>
/// 成品产品实体对象引用,生产阶段填充
/// </summary>
public Product? Product { get; set; }
/// <summary>
/// 加工生产是否完成
/// </summary>
public bool ProduceFinished { get; set; }
/// <summary>
/// 质检是否合格
/// </summary>
public bool QualityCheckPassed { get; set; }
/// <summary>
/// 包装是否完成
/// </summary>
public bool PackFinished { get; set; }
/// <summary>
/// 物流订单实体对象引用,物流阶段填充
/// </summary>
public LogisticsOrder? LogisticsOrder { get; set; }
/// <summary>
/// 物流运单号
/// </summary>
public string LogisticsNo { get; set; } = string.Empty;
/// <summary>
/// 物流是否已发货
/// </summary>
public bool LogisticsDispatched { get; set; }
/// <summary>
/// 业务总金额(使用金额值对象,包含币种信息)
/// </summary>
public Money TotalAmount { get; set; } = new Money(0);
/// <summary>
/// 财务记账是否完成
/// </summary>
public bool FinanceBookkeepingDone { get; set; }
/// <summary>
/// 财务凭证编号
/// </summary>
public string? FinanceVoucherNo { get; set; }
/// <summary>
/// 营销推广数据是否已同步
/// </summary>
public bool MarketingSynced { get; set; }
/// <summary>
/// 营销活动编号
/// </summary>
public string? MarketingCampaignNo { get; set; }
/// <summary>
/// 收货地址信息
/// </summary>
public Address? ShippingAddress { get; set; }
/// <summary>
/// 业务备注信息
/// </summary>
public string? Remark { get; set; }
/// <summary>
/// 业务执行异常消息,流程异常时填充
/// </summary>
public string? ErrorMessage { get; set; }
/// <summary>
/// 业务是否整体执行成功
/// </summary>
public bool IsSuccess { get; set; }
/// <summary>
/// 业务流程创建时间
/// </summary>
public DateTime CreateTime { get; set; } = DateTime.Now;
/// <summary>
/// 业务流程完成时间
/// </summary>
public DateTime? CompleteTime { get; set; }
/// <summary>
/// 流程执行历史记录集合,按时间顺序记录每个阶段的执行情况
/// </summary>
public List<BusinessStageHistory> StageHistories { get; set; } = new List<BusinessStageHistory>();
/// <summary>
/// 切换业务流程到指定阶段,并记录阶段历史
/// </summary>
/// <param name="stage">目标业务阶段</param>
/// <param name="message">阶段执行说明消息</param>
public void MoveToStage(BusinessStage stage, string message)
{
CurrentStage = stage;
var history = new BusinessStageHistory
{
HistoryId = Guid.NewGuid(),
Stage = stage,
Message = message,
ExecuteTime = DateTime.Now
};
StageHistories.Add(history);
}
/// <summary>
/// 标记业务流程执行完成
/// </summary>
/// <param name="isSuccess">是否成功完成</param>
/// <param name="message">完成说明消息</param>
public void MarkCompleted(bool isSuccess, string message)
{
IsSuccess = isSuccess;
CompleteTime = DateTime.Now;
CurrentStage = isSuccess ? BusinessStage.Completed : BusinessStage.Terminated;
StageHistories.Add(new BusinessStageHistory
{
HistoryId = Guid.NewGuid(),
Stage = CurrentStage,
Message = message,
ExecuteTime = DateTime.Now
});
}
}
/// <summary>
/// 业务阶段历史记录实体
/// 记录业务流程在每个阶段的执行情况,用于流程审计与问题追溯
/// </summary>
public class BusinessStageHistory
{
/// <summary>
/// 历史记录唯一标识ID
/// </summary>
public Guid HistoryId { get; set; }
/// <summary>
/// 对应的业务阶段
/// </summary>
public BusinessStage Stage { get; set; }
/// <summary>
/// 阶段执行说明消息
/// </summary>
public string Message { get; set; } = string.Empty;
/// <summary>
/// 阶段执行时间
/// </summary>
public DateTime ExecuteTime { get; set; }
}
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
Author : geovindu,Geovin Du 涂聚文.
IDE : vs2026 c# .net 10
os : windows 10
database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
Datetime : 2026/07/04 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : LogisticsOrder.cs
*/
using System;
using System.Collections.Generic;
using System.Text;
using TemplateMethodPattern.Domain.ValueObjects;
namespace TemplateMethodPattern.Domain.Entities
{
/// <summary>
/// 物流订单实体
/// 表示珠宝成品的物流发货订单,包含运单信息、收发地址与配送状态
/// </summary>
public class LogisticsOrder
{
/// <summary>
/// 物流订单唯一标识ID
/// </summary>
public Guid LogisticsId { get; set; }
/// <summary>
/// 物流运单号,由第三方物流系统生成
/// </summary>
public string TrackingNo { get; set; } = string.Empty;
/// <summary>
/// 关联的业务订单编号
/// </summary>
public string BusinessNo { get; set; } = string.Empty;
/// <summary>
/// 物流公司名称,如"顺丰速运"、"京东物流"
/// </summary>
public string CarrierName { get; set; } = string.Empty;
/// <summary>
/// 物流服务类型,如"标准快递"、"次日达"、"保价专递"
/// </summary>
public string ServiceType { get; set; } = string.Empty;
/// <summary>
/// 发货地址信息
/// </summary>
public Address? SenderAddress { get; set; }
/// <summary>
/// 收货地址信息
/// </summary>
public Address? ReceiverAddress { get; set; }
/// <summary>
/// 物流运费金额
/// </summary>
public decimal FreightAmount { get; set; }
/// <summary>
/// 保价金额,珠宝高价值商品通常需要保价
/// </summary>
public decimal InsuranceAmount { get; set; }
/// <summary>
/// 物流当前状态描述,如"已揽收"、"运输中"、"派送中"、"已签收"
/// </summary>
public string CurrentStatus { get; set; } = "待发货";
/// <summary>
/// 是否已签收
/// </summary>
public bool IsDelivered { get; set; }
/// <summary>
/// 物流下单时间
/// </summary>
public DateTime CreateTime { get; set; }
/// <summary>
/// 发货时间
/// </summary>
public DateTime? ShipTime { get; set; }
/// <summary>
/// 签收时间
/// </summary>
public DateTime? DeliverTime { get; set; }
/// <summary>
/// 物流轨迹记录集合,按时间顺序记录物流节点状态
/// </summary>
public List<LogisticsTrack> Tracks { get; set; } = new List<LogisticsTrack>();
/// <summary>
/// 添加物流轨迹节点
/// </summary>
/// <param name="status">节点状态描述</param>
/// <param name="location">当前所在地点</param>
/// <param name="description">节点详细描述</param>
public void AddTrack(string status, string location, string description)
{
var track = new LogisticsTrack
{
TrackId = Guid.NewGuid(),
Status = status,
Location = location,
Description = description,
TrackTime = DateTime.Now
};
Tracks.Add(track);
CurrentStatus = status;
}
/// <summary>
/// 标记物流订单已发货
/// </summary>
public void MarkAsShipped()
{
ShipTime = DateTime.Now;
CurrentStatus = "已发货";
}
/// <summary>
/// 标记物流订单已签收
/// </summary>
public void MarkAsDelivered()
{
IsDelivered = true;
DeliverTime = DateTime.Now;
CurrentStatus = "已签收";
}
}
/// <summary>
/// 物流轨迹节点实体
/// 表示物流订单在某个时间点的状态与位置信息
/// </summary>
public class LogisticsTrack
{
/// <summary>
/// 轨迹节点唯一标识ID
/// </summary>
public Guid TrackId { get; set; }
/// <summary>
/// 节点状态描述
/// </summary>
public string Status { get; set; } = string.Empty;
/// <summary>
/// 节点所在地点
/// </summary>
public string Location { get; set; } = string.Empty;
/// <summary>
/// 节点详细描述
/// </summary>
public string Description { get; set; } = string.Empty;
/// <summary>
/// 轨迹记录时间
/// </summary>
public DateTime TrackTime { get; set; }
}
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
Author : geovindu,Geovin Du 涂聚文.
IDE : vs2026 c# .net 10
os : windows 10
database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
Datetime : 2026/07/04 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : Product.cs
*/
using System;
using System.Collections.Generic;
using System.Text;
using TemplateMethodPattern.Domain.Enums;
namespace TemplateMethodPattern.Domain.Entities
{
/// <summary>
/// 珠宝成品实体
/// 表示加工完成并经过质检的珠宝首饰成品,包含产品信息、品质等级与包装状态
/// </summary>
public class Product
{
/// <summary>
/// 产品唯一标识ID
/// </summary>
public Guid ProductId { get; set; }
/// <summary>
/// 产品业务编号,如PD-20260901001
/// </summary>
public string ProductCode { get; set; } = string.Empty;
/// <summary>
/// 产品名称,如"18K金钻石戒指"
/// </summary>
public string ProductName { get; set; } = string.Empty;
/// <summary>
/// 关联的设计图纸编号
/// </summary>
public string DrawingNo { get; set; } = string.Empty;
/// <summary>
/// 关联的原料物料编码
/// </summary>
public string MaterialCode { get; set; } = string.Empty;
/// <summary>
/// 产品品类,如"戒指"、"项链"、"耳环"、"手镯"
/// </summary>
public string Category { get; set; } = string.Empty;
/// <summary>
/// 成品重量,单位为克
/// </summary>
public decimal FinishedWeight { get; set; }
/// <summary>
/// 质检质量等级
/// </summary>
public QualityLevel QualityLevel { get; set; }
/// <summary>
/// 质检是否合格
/// </summary>
public bool IsQualityPassed { get; set; }
/// <summary>
/// 质检报告编号
/// </summary>
public string? QualityReportNo { get; set; }
/// <summary>
/// 是否已完成包装
/// </summary>
public bool IsPackaged { get; set; }
/// <summary>
/// 包装规格描述,如"高端礼盒+证书+擦金布"
/// </summary>
public string? PackageSpecification { get; set; }
/// <summary>
/// 产品出厂价
/// </summary>
public decimal FactoryPrice { get; set; }
/// <summary>
/// 产品零售价
/// </summary>
public decimal RetailPrice { get; set; }
/// <summary>
/// 加工生产完成时间
/// </summary>
public DateTime? ManufactureTime { get; set; }
/// <summary>
/// 质检完成时间
/// </summary>
public DateTime? QualityCheckTime { get; set; }
/// <summary>
/// 包装完成时间
/// </summary>
public DateTime? PackageTime { get; set; }
/// <summary>
/// 执行质量检测,设置质检结果与等级
/// </summary>
/// <param name="isPassed">质检是否通过</param>
/// <param name="level">质量等级</param>
/// <param name="reportNo">质检报告编号</param>
public void PerformQualityCheck(bool isPassed, QualityLevel level, string? reportNo = null)
{
IsQualityPassed = isPassed;
QualityLevel = level;
QualityReportNo = reportNo;
QualityCheckTime = DateTime.Now;
}
/// <summary>
/// 执行成品包装,标记包装状态
/// </summary>
/// <param name="specification">包装规格描述</param>
public void PerformPackaging(string specification)
{
IsPackaged = true;
PackageSpecification = specification;
PackageTime = DateTime.Now;
}
}
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
Author : geovindu,Geovin Du 涂聚文.
IDE : vs2026 c# .net 10
os : windows 10
database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
Datetime : 2026/07/04 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : RawMaterial.cs
*/
using System;
using System.Collections.Generic;
using System.Text;
using TemplateMethodPattern.Domain.Enums;
namespace TemplateMethodPattern.Domain.Entities
{
/// <summary>
/// 珠宝原料实体
/// 表示采购入库的珠宝原材料信息,包含物料编码、类型、品质与核验状态
/// </summary>
public class RawMaterial
{
/// <summary>
/// 原料唯一标识ID
/// </summary>
public Guid MaterialId { get; set; }
/// <summary>
/// 原料物料编码,业务层面唯一标识,如RAW-00101
/// </summary>
public string MaterialCode { get; set; } = string.Empty;
/// <summary>
/// 原料名称,如"18K金原料"、"GIA裸钻1.0ct"
/// </summary>
public string MaterialName { get; set; } = string.Empty;
/// <summary>
/// 原料类型枚举
/// </summary>
public MaterialType MaterialType { get; set; }
/// <summary>
/// 原料重量,单位为克(贵金属)或克拉(宝石)
/// </summary>
public decimal Weight { get; set; }
/// <summary>
/// 原料纯度或品质描述,如"Au750"、"VVS1 D色"
/// </summary>
public string Purity { get; set; } = string.Empty;
/// <summary>
/// 供应商名称
/// </summary>
public string SupplierName { get; set; } = string.Empty;
/// <summary>
/// 采购单价
/// </summary>
public decimal UnitPrice { get; set; }
/// <summary>
/// 原料核验是否通过
/// </summary>
public bool IsVerified { get; set; }
/// <summary>
/// 核验意见或备注信息
/// </summary>
public string? VerificationRemark { get; set; }
/// <summary>
/// 原料入库时间
/// </summary>
public DateTime InboundTime { get; set; }
/// <summary>
/// 核验完成时间
/// </summary>
public DateTime? VerificationTime { get; set; }
/// <summary>
/// 执行原料品质核验,标记核验结果
/// </summary>
/// <param name="isPassed">核验是否通过</param>
/// <param name="remark">核验意见或不合格原因</param>
public void Verify(bool isPassed, string? remark = null)
{
IsVerified = isPassed;
VerificationRemark = remark;
VerificationTime = DateTime.Now;
}
}
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
Author : geovindu,Geovin Du 涂聚文.
IDE : vs2026 c# .net 10
os : windows 10
database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
Datetime : 2026/07/04 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : BusinessStage.cs
*/
using System;
using System.Collections.Generic;
using System.Text;
namespace TemplateMethodPattern.Domain.Enums
{
/// <summary>
/// 珠宝业务流程阶段枚举
/// 标识业务当前所处的执行阶段,用于流程追踪与状态审计
/// </summary>
public enum BusinessStage
{
/// <summary>
/// 未开始:业务流程尚未启动
/// </summary>
NotStarted = 0,
/// <summary>
/// 原料采购核验阶段:对采购的珠宝原料进行品质与合规核验
/// </summary>
RawMaterialVerification = 1,
/// <summary>
/// 设计制图阶段:根据客户需求或产品规划进行珠宝设计与图纸绘制
/// </summary>
DesignDrawing = 2,
/// <summary>
/// 加工生产阶段:依据设计图纸进行珠宝首饰的加工与制造
/// </summary>
Manufacturing = 3,
/// <summary>
/// 质量检验阶段:对成品珠宝进行品质检测与合规校验
/// </summary>
QualityInspection = 4,
/// <summary>
/// 包装阶段:对质检合格的珠宝成品进行品牌包装与配件组装
/// </summary>
Packaging = 5,
/// <summary>
/// 物流发货阶段:安排物流运输并跟踪配送状态
/// </summary>
LogisticsDispatch = 6,
/// <summary>
/// 财务记账阶段:业务收入与成本的财务核算与凭证生成
/// </summary>
FinanceBookkeeping = 7,
/// <summary>
/// 营销推广阶段:将业务数据同步至营销系统用于推广与客户运营
/// </summary>
MarketingPromotion = 8,
/// <summary>
/// 已完成:全部业务流程执行完毕
/// </summary>
Completed = 99,
/// <summary>
/// 已终止:业务流程因异常或人工干预而终止
/// </summary>
Terminated = 100
}
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
Author : geovindu,Geovin Du 涂聚文.
IDE : vs2026 c# .net 10
os : windows 10
database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
Datetime : 2026/07/04 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : MaterialType.cs
*/
using System;
using System.Collections.Generic;
using System.Text;
namespace TemplateMethodPattern.Domain.Enums
{
/// <summary>
/// 珠宝原料类型枚举
/// 区分珠宝业务中不同类别的原料物料,用于采购核验与库存管理
/// </summary>
public enum MaterialType
{
/// <summary>
/// 未知类型:未明确指定原料类别
/// </summary>
Unknown = 0,
/// <summary>
/// 黄金原料:包括足金、K金等黄金类原材料
/// </summary>
Gold = 1,
/// <summary>
/// 铂金原料:铂金类贵金属原材料
/// </summary>
Platinum = 2,
/// <summary>
/// 白银原料:白银类贵金属原材料
/// </summary>
Silver = 3,
/// <summary>
/// 钻石原料:裸钻及钻石类宝石原材料
/// </summary>
Diamond = 4,
/// <summary>
/// 彩色宝石原料:红宝石、蓝宝石、祖母绿等彩色宝石
/// </summary>
ColoredGemstone = 5,
/// <summary>
/// 珍珠原料:淡水珍珠、海水珍珠等珍珠类原材料
/// </summary>
Pearl = 6,
/// <summary>
/// 翡翠玉石原料:翡翠、和田玉等玉石类原材料
/// </summary>
Jade = 7
}
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
Author : geovindu,Geovin Du 涂聚文.
IDE : vs2026 c# .net 10
os : windows 10
database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
Datetime : 2026/07/04 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : QualityLevel.cs
*/
using System;
using System.Collections.Generic;
using System.Text;
namespace TemplateMethodPattern.Domain.Enums
{
/// <summary>
/// 珠宝成品质量等级枚举
/// 用于质检阶段对成品品质进行分级评定
/// </summary>
public enum QualityLevel
{
/// <summary>
/// 未评定:尚未进行质量检测
/// </summary>
NotEvaluated = 0,
/// <summary>
/// 特级品质:最高品质等级,工艺与材质均达到顶级标准
/// </summary>
Premium = 1,
/// <summary>
/// 一级品质:高品质等级,符合高端产品标准
/// </summary>
GradeA = 2,
/// <summary>
/// 二级品质:中等品质等级,符合常规产品标准
/// </summary>
GradeB = 3,
/// <summary>
/// 不合格:品质未达到出厂标准,需返工或报废
/// </summary>
Rejected = 4
}
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
Author : geovindu,Geovin Du 涂聚文.
IDE : vs2026 c# .net 10
os : windows 10
database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
Datetime : 2026/07/04 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : Address.cs
*/
using System;
using System.Collections.Generic;
using System.Text;
namespace TemplateMethodPattern.Domain.ValueObjects
{
/// <summary>
/// 地址值对象
/// 封装物流收货或发货地址信息,不可变,用于物流配送与客户档案
/// </summary>
public sealed class Address : IEquatable<Address>
{
/// <summary>
/// 收件人/发件人姓名
/// </summary>
public string ContactName { get; }
/// <summary>
/// 联系电话号码
/// </summary>
public string PhoneNumber { get; }
/// <summary>
/// 国家/地区名称
/// </summary>
public string Country { get; }
/// <summary>
/// 省份/州名称
/// </summary>
public string Province { get; }
/// <summary>
/// 城市名称
/// </summary>
public string City { get; }
/// <summary>
/// 区/县名称
/// </summary>
public string District { get; }
/// <summary>
/// 详细街道地址信息
/// </summary>
public string DetailAddress { get; }
/// <summary>
/// 邮政编码
/// </summary>
public string PostalCode { get; }
/// <summary>
/// 构造地址值对象
/// </summary>
/// <param name="contactName">联系人姓名</param>
/// <param name="phoneNumber">联系电话</param>
/// <param name="country">国家/地区</param>
/// <param name="province">省份/州</param>
/// <param name="city">城市</param>
/// <param name="district">区/县</param>
/// <param name="detailAddress">详细地址</param>
/// <param name="postalCode">邮政编码</param>
public Address(
string contactName,
string phoneNumber,
string country,
string province,
string city,
string district,
string detailAddress,
string postalCode)
{
ContactName = contactName ?? throw new ArgumentNullException(nameof(contactName));
PhoneNumber = phoneNumber ?? throw new ArgumentNullException(nameof(phoneNumber));
Country = country ?? throw new ArgumentNullException(nameof(country));
Province = province ?? throw new ArgumentNullException(nameof(province));
City = city ?? throw new ArgumentNullException(nameof(city));
District = district ?? throw new ArgumentNullException(nameof(district));
DetailAddress = detailAddress ?? throw new ArgumentNullException(nameof(detailAddress));
PostalCode = postalCode ?? throw new ArgumentNullException(nameof(postalCode));
}
/// <summary>
/// 获取完整地址字符串(不含联系人信息)
/// </summary>
/// <returns>拼接后的完整地址字符串</returns>
public string GetFullAddress()
{
return $"{Country}{Province}{City}{District}{DetailAddress}";
}
/// <summary>
/// 判断两个地址对象是否相等
/// </summary>
/// <param name="other">待比较的地址对象</param>
/// <returns>true表示地址完全相同</returns>
public bool Equals(Address? other)
{
if (other is null)
{
return false;
}
return ContactName == other.ContactName &&
PhoneNumber == other.PhoneNumber &&
Country == other.Country &&
Province == other.Province &&
City == other.City &&
District == other.District &&
DetailAddress == other.DetailAddress &&
PostalCode == other.PostalCode;
}
/// <summary>
/// 重写Equals方法
/// </summary>
/// <param name="obj">待比较对象</param>
/// <returns>true表示相等</returns>
public override bool Equals(object? obj)
{
return Equals(obj as Address);
}
/// <summary>
/// 重写GetHashCode方法
/// </summary>
/// <returns>哈希值</returns>
public override int GetHashCode()
{
return HashCode.Combine(ContactName, PhoneNumber, Country, Province, City, District, DetailAddress, PostalCode);
}
/// <summary>
/// 重写ToString方法,输出完整地址信息
/// </summary>
/// <returns>包含联系人与完整地址的字符串</returns>
public override string ToString()
{
return $"{ContactName},{PhoneNumber},{GetFullAddress()},邮编:{PostalCode}";
}
}
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
Author : geovindu,Geovin Du 涂聚文.
IDE : vs2026 c# .net 10
os : windows 10
database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
Datetime : 2026/07/04 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : Money.cs
*/
using System;
using System.Collections.Generic;
using System.Text;
namespace TemplateMethodPattern.Domain.ValueObjects
{
/// <summary>
/// 金额值对象
/// 封装货币金额与币种信息,不可变,用于财务核算与业务金额表示
/// </summary>
public sealed class Money : IEquatable<Money>
{
/// <summary>
/// 金额数值,保留两位小数
/// </summary>
public decimal Amount { get; }
/// <summary>
/// 币种代码,遵循ISO 4217标准(如CNY、USD、HKD)
/// </summary>
public string CurrencyCode { get; }
/// <summary>
/// 构造金额值对象
/// </summary>
/// <param name="amount">金额数值</param>
/// <param name="currencyCode">币种代码,默认为人民币CNY</param>
/// <exception cref="ArgumentException">当金额为负数或币种代码为空时抛出</exception>
public Money(decimal amount, string currencyCode = "CNY")
{
if (amount < 0)
{
throw new ArgumentException("金额不能为负数", nameof(amount));
}
if (string.IsNullOrWhiteSpace(currencyCode))
{
throw new ArgumentException("币种代码不能为空", nameof(currencyCode));
}
Amount = Math.Round(amount, 2, MidpointRounding.AwayFromZero);
CurrencyCode = currencyCode.ToUpperInvariant();
}
/// <summary>
/// 金额加法运算,要求币种相同
/// </summary>
/// <param name="other">待相加的金额对象</param>
/// <returns>相加后的新金额对象</returns>
/// <exception cref="InvalidOperationException">币种不一致时抛出</exception>
public Money Add(Money other)
{
EnsureSameCurrency(other);
return new Money(Amount + other.Amount, CurrencyCode);
}
/// <summary>
/// 金额减法运算,要求币种相同且结果不为负
/// </summary>
/// <param name="other">待相减的金额对象</param>
/// <returns>相减后的新金额对象</returns>
/// <exception cref="InvalidOperationException">币种不一致或结果为负时抛出</exception>
public Money Subtract(Money other)
{
EnsureSameCurrency(other);
if (Amount < other.Amount)
{
throw new InvalidOperationException("减法运算结果不能为负数");
}
return new Money(Amount - other.Amount, CurrencyCode);
}
/// <summary>
/// 校验两个金额对象的币种是否一致
/// </summary>
/// <param name="other">待比较的金额对象</param>
/// <exception cref="InvalidOperationException">币种不一致时抛出</exception>
private void EnsureSameCurrency(Money other)
{
if (!string.Equals(CurrencyCode, other.CurrencyCode, StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException($"币种不一致,无法进行运算:当前币种{CurrencyCode},目标币种{other.CurrencyCode}");
}
}
/// <summary>
/// 判断两个金额对象是否相等
/// </summary>
/// <param name="other">待比较的金额对象</param>
/// <returns>true表示相等,false表示不相等</returns>
public bool Equals(Money? other)
{
if (other is null)
{
return false;
}
return Amount == other.Amount &&
string.Equals(CurrencyCode, other.CurrencyCode, StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// 重写Equals方法,支持对象级比较
/// </summary>
/// <param name="obj">待比较的对象</param>
/// <returns>true表示相等,false表示不相等</returns>
public override bool Equals(object? obj)
{
return Equals(obj as Money);
}
/// <summary>
/// 重写GetHashCode方法
/// </summary>
/// <returns>哈希值</returns>
public override int GetHashCode()
{
return HashCode.Combine(Amount, CurrencyCode);
}
/// <summary>
/// 重写ToString方法,输出带币种的金额字符串
/// </summary>
/// <returns>格式化的金额字符串,如"1,234.56 CNY"</returns>
public override string ToString()
{
return $"{Amount:N2} {CurrencyCode}";
}
/// <summary>
/// 相等运算符重载
/// </summary>
/// <param name="left">左侧金额对象</param>
/// <param name="right">右侧金额对象</param>
/// <returns>true表示相等</returns>
public static bool operator ==(Money? left, Money? right)
{
if (left is null)
{
return right is null;
}
return left.Equals(right);
}
/// <summary>
/// 不等运算符重载
/// </summary>
/// <param name="left">左侧金额对象</param>
/// <param name="right">右侧金额对象</param>
/// <returns>true表示不相等</returns>
public static bool operator !=(Money? left, Money? right)
{
return !(left == right);
}
}
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
Author : geovindu,Geovin Du 涂聚文.
IDE : vs2026 c# .net 10
os : windows 10
database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
Datetime : 2026/07/04 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : IBusinessFlowRepository.cs
*/
using System;
using System.Collections.Generic;
using System.Text;
using TemplateMethodPattern.Domain.Entities;
namespace TemplateMethodPattern.Domain.Interfaces
{
/// <summary>
/// 业务流程仓储接口(领域端口)
/// 定义业务上下文数据的持久化契约,支持流程状态的保存与查询,
/// 由基础设施层提供具体实现(内存、数据库、缓存等)
/// </summary>
public interface IBusinessFlowRepository
{
/// <summary>
/// 保存业务上下文数据
/// 将业务流程的当前状态持久化存储,支持新增与更新
/// </summary>
/// <param name="context">业务上下文对象</param>
/// <returns>true表示保存成功</returns>
bool Save(JewelleryBusinessContext context);
/// <summary>
/// 根据业务编号查询业务上下文
/// </summary>
/// <param name="businessNo">业务订单编号</param>
/// <returns>业务上下文对象,若不存在则返回null</returns>
JewelleryBusinessContext? GetByBusinessNo(string businessNo);
/// <summary>
/// 查询所有业务上下文列表
/// </summary>
/// <returns>业务上下文集合</returns>
IReadOnlyList<JewelleryBusinessContext> GetAll();
/// <summary>
/// 异步保存业务上下文数据
/// 支持高并发场景下的异步持久化
/// </summary>
/// <param name="context">业务上下文对象</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务,结果为true表示保存成功</returns>
Task<bool> SaveAsync(JewelleryBusinessContext context, CancellationToken cancellationToken = default);
/// <summary>
/// 异步根据业务编号查询业务上下文
/// </summary>
/// <param name="businessNo">业务订单编号</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务,结果为业务上下文对象</returns>
Task<JewelleryBusinessContext?> GetByBusinessNoAsync(string businessNo, CancellationToken cancellationToken = default);
}
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
Author : geovindu,Geovin Du 涂聚文.
IDE : vs2026 c# .net 10
os : windows 10
database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
Datetime : 2026/07/04 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : IExternalFinanceAdapter.cs
*/
using System;
using System.Collections.Generic;
using System.Text;
namespace TemplateMethodPattern.Domain.Interfaces
{
/// <summary>
/// 外部财务报税系统适配接口(领域端口)
/// 定义内部业务系统与外部财务/税务系统交互的统一契约,
/// 隔离财务系统异构性,由基础设施层提供具体适配器实现
/// </summary>
public interface IExternalFinanceAdapter
{
/// <summary>
/// 提交业务记账凭证
/// 将业务收入与成本数据提交至外部财务系统生成记账凭证
/// </summary>
/// <param name="businessNo">业务订单编号</param>
/// <param name="amount">业务总金额</param>
/// <param name="currencyCode">币种代码</param>
/// <returns>财务凭证编号,提交失败返回空字符串</returns>
string SubmitBusinessAccount(string businessNo, decimal amount, string currencyCode);
/// <summary>
/// 查询财务凭证状态
/// 根据凭证编号查询财务系统中的凭证审核与记账状态
/// </summary>
/// <param name="voucherNo">财务凭证编号</param>
/// <returns>凭证状态描述,如"已记账"、"审核中"、"已驳回"</returns>
string QueryVoucherStatus(string voucherNo);
/// <summary>
/// 异步提交业务记账凭证
/// 支持高并发场景下的异步调用
/// </summary>
/// <param name="businessNo">业务订单编号</param>
/// <param name="amount">业务总金额</param>
/// <param name="currencyCode">币种代码</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务,结果为财务凭证编号</returns>
Task<string> SubmitBusinessAccountAsync(
string businessNo,
decimal amount,
string currencyCode,
CancellationToken cancellationToken = default);
}
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
Author : geovindu,Geovin Du 涂聚文.
IDE : vs2026 c# .net 10
os : windows 10
database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
Datetime : 2026/07/04 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : IExternalLogisticsAdapter.cs
*/
using System;
using System.Collections.Generic;
using System.Text;
using TemplateMethodPattern.Domain.Entities;
using TemplateMethodPattern.Domain.ValueObjects;
namespace TemplateMethodPattern.Domain.Interfaces
{
/// <summary>
/// 外部物流系统适配接口(领域端口)
/// 定义内部业务系统与外部第三方物流系统交互的统一契约,
/// 支持多家物流服务商的统一接入,由基础设施层提供具体适配器实现
/// </summary>
public interface IExternalLogisticsAdapter
{
/// <summary>
/// 创建物流运单
/// 调用外部物流系统下单,生成运单号并返回物流订单信息
/// </summary>
/// <param name="businessNo">内部业务订单编号</param>
/// <param name="senderAddress">发货地址</param>
/// <param name="receiverAddress">收货地址</param>
/// <param name="insuranceAmount">保价金额</param>
/// <returns>物流订单实体对象,包含运单号与物流信息</returns>
LogisticsOrder CreateShipment(
string businessNo,
Address senderAddress,
Address receiverAddress,
decimal insuranceAmount);
/// <summary>
/// 查询物流轨迹
/// 根据运单号从外部物流系统获取最新的物流轨迹信息
/// </summary>
/// <param name="trackingNo">物流运单号</param>
/// <returns>物流订单实体,包含最新轨迹信息;若不存在则返回null</returns>
LogisticsOrder? QueryTracking(string trackingNo);
/// <summary>
/// 异步创建物流运单
/// 支持高并发场景下的异步调用
/// </summary>
/// <param name="businessNo">内部业务订单编号</param>
/// <param name="senderAddress">发货地址</param>
/// <param name="receiverAddress">收货地址</param>
/// <param name="insuranceAmount">保价金额</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务,结果为物流订单实体对象</returns>
Task<LogisticsOrder> CreateShipmentAsync(
string businessNo,
Address senderAddress,
Address receiverAddress,
decimal insuranceAmount,
CancellationToken cancellationToken = default);
}
}
/*
encoding: utf-8
版权所有 2026 ©涂聚文有限公司™ ®
许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
Author : geovindu,Geovin Du 涂聚文.
IDE : vs2026 c# .net 10
os : windows 10
database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
Datetime : 2026/07/04 22:16
User : geovindu
Product : Visual Studio 2026
Project : CSharpDesignPattern
File : IExternalSupplierAdapter.cs
*/
using System;
using System.Collections.Generic;
using System.Text;
using TemplateMethodPattern.Domain.Entities;
namespace TemplateMethodPattern.Domain.Interfaces
{
/// <summary>
/// 外部原料供应商系统适配接口(领域端口)
/// 定义内部业务系统与外部第三方原料供应商系统交互的统一契约,
/// 隔离外部异构系统差异,由基础设施层提供具体适配器实现
/// </summary>
public interface IExternalSupplierAdapter
{
/// <summary>
/// 校验原料物料是否合法合规
/// 调用外部供应商系统核验原料的来源、品质证书与合规状态
/// </summary>
/// <param name="materialCode">原料物料编码</param>
/// <returns>true表示原料核验通过,false表示核验不通过</returns>
bool CheckMaterialValid(string materialCode);
/// <summary>
/// 获取原料详细信息
/// 从外部供应商系统获取指定原料的完整信息,包括品质参数、证书与来源
/// </summary>
/// <param name="materialCode">原料物料编码</param>
/// <returns>原料实体对象,若不存在则返回null</returns>
RawMaterial? GetMaterialInfo(string materialCode);
/// <summary>
/// 异步校验原料物料是否合法合规
/// 支持高并发场景下的异步调用,避免阻塞业务线程
/// </summary>
/// <param name="materialCode">原料物料编码</param>
/// <param name="cancellationToken">取消令牌,用于支持调用方取消操作</param>
/// <returns>异步任务,结果为true表示核验通过</returns>
Task<bool> CheckMaterialValidAsync(string materialCode, CancellationToken cancellationToken = default);
}
}
cs
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/07/04 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : ThirdPartyFinanceAdapter.cs
*/
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Text;
using TemplateMethodPattern.Domain.Interfaces;
namespace TemplateMethodPattern.Infrastructure.Adapters
{
/// <summary>
/// 第三方财务报税系统适配器【适配器模式】
/// 实现IExternalFinanceAdapter接口,将内部统一财务契约适配到第三方财务/税务系统的异构API,
/// 封装记账凭证提交、凭证状态查询、税务申报等外部调用细节,
/// 隔离财务系统异构性,确保财务数据的准确性与合规性。
/// 具备线程安全与高并发处理能力,支持多币种财务核算。
/// </summary>
public class ThirdPartyFinanceAdapter : IExternalFinanceAdapter
{
/// <summary>
/// 适配器日志记录器实例
/// </summary>
private readonly ILogger<ThirdPartyFinanceAdapter> _logger;
/// <summary>
/// 第三方财务系统API基础地址
/// </summary>
private readonly string _apiBaseUrl;
/// <summary>
/// 财务系统租户编号
/// </summary>
private readonly string _tenantNo;
/// <summary>
/// 模拟财务凭证缓存字典,Key为凭证编号,Value为凭证状态描述
/// 实际项目中此处应调用真实第三方财务API
/// </summary>
private readonly Dictionary<string, FinanceVoucherInfo> _mockVoucherCache;
/// <summary>
/// 缓存读写锁,保护并发访问
/// </summary>
private readonly ReaderWriterLockSlim _cacheLock = new();
/// <summary>
/// 凭证号生成计数器,用于线程安全的凭证号生成
/// </summary>
private int _voucherNoCounter = 0;
/// <summary>
/// 构造函数,通过DI注入日志与配置
/// </summary>
/// <param name="logger">日志记录器实例</param>
public ThirdPartyFinanceAdapter(ILogger<ThirdPartyFinanceAdapter> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_apiBaseUrl = "https://api.finance.example.com/v3";
_tenantNo = "JEWELRY-2026";
_mockVoucherCache = new Dictionary<string, FinanceVoucherInfo>();
_logger.LogInformation("第三方财务系统适配器初始化完成,租户编号:{TenantNo},API地址:{ApiBaseUrl}",
_tenantNo, _apiBaseUrl);
}
/// <summary>
/// 同步提交业务记账凭证
/// 将业务收入与成本数据提交至外部财务系统生成记账凭证
/// </summary>
/// <param name="businessNo">业务订单编号</param>
/// <param name="amount">业务总金额</param>
/// <param name="currencyCode">币种代码</param>
/// <returns>财务凭证编号,提交失败返回空字符串</returns>
public string SubmitBusinessAccount(string businessNo, decimal amount, string currencyCode)
{
_logger.LogDebug("适配器同步调用第三方财务系统提交记账凭证,业务编号:{BusinessNo},金额:{Amount},币种:{Currency}",
businessNo, amount, currencyCode);
if (string.IsNullOrWhiteSpace(businessNo))
{
_logger.LogWarning("业务编号为空,凭证提交失败");
return string.Empty;
}
if (amount <= 0)
{
_logger.LogWarning("业务金额无效,凭证提交失败,金额:{Amount}", amount);
return string.Empty;
}
// 线程安全地生成凭证编号
int sequence = Interlocked.Increment(ref _voucherNoCounter);
string voucherNo = $"PZ{DateTime.Now:yyyyMMdd}{sequence:D8}";
var voucherInfo = new FinanceVoucherInfo
{
VoucherNo = voucherNo,
BusinessNo = businessNo,
Amount = amount,
CurrencyCode = currencyCode,
Status = "已记账",
Summary = $"珠宝销售业务收入凭证-业务编号{businessNo}",
CreateTime = DateTime.Now
};
// 缓存凭证信息
_cacheLock.EnterWriteLock();
try
{
_mockVoucherCache[voucherNo] = voucherInfo;
}
finally
{
_cacheLock.ExitWriteLock();
}
_logger.LogInformation("第三方财务凭证提交成功,凭证编号:{VoucherNo},金额:{Amount} {Currency}",
voucherNo, amount, currencyCode);
return voucherNo;
}
/// <summary>
/// 查询财务凭证状态
/// 根据凭证编号查询财务系统中的凭证审核与记账状态
/// </summary>
/// <param name="voucherNo">财务凭证编号</param>
/// <returns>凭证状态描述,如"已记账"、"审核中"、"已驳回"</returns>
public string QueryVoucherStatus(string voucherNo)
{
_logger.LogDebug("适配器调用第三方财务系统查询凭证状态,凭证编号:{VoucherNo}", voucherNo);
if (string.IsNullOrWhiteSpace(voucherNo))
{
_logger.LogWarning("凭证编号为空,无法查询");
return "未知状态";
}
_cacheLock.EnterReadLock();
try
{
if (_mockVoucherCache.TryGetValue(voucherNo, out var voucher))
{
_logger.LogInformation("财务凭证状态查询成功,凭证编号:{VoucherNo},状态:{Status}",
voucherNo, voucher.Status);
return voucher.Status;
}
_logger.LogWarning("未找到财务凭证,凭证编号:{VoucherNo}", voucherNo);
return "凭证不存在";
}
finally
{
_cacheLock.ExitReadLock();
}
}
/// <summary>
/// 异步提交业务记账凭证
/// 支持高并发场景下的异步调用
/// </summary>
/// <param name="businessNo">业务订单编号</param>
/// <param name="amount">业务总金额</param>
/// <param name="currencyCode">币种代码</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务,结果为财务凭证编号</returns>
public async Task<string> SubmitBusinessAccountAsync(
string businessNo,
decimal amount,
string currencyCode,
CancellationToken cancellationToken = default)
{
_logger.LogDebug("适配器异步调用第三方财务系统提交记账凭证,业务编号:{BusinessNo}", businessNo);
// 模拟异步HTTP调用延迟
await Task.Delay(25, cancellationToken);
// 复用同步提交逻辑
return SubmitBusinessAccount(businessNo, amount, currencyCode);
}
}
/// <summary>
/// 财务凭证信息实体
/// 记录提交至第三方财务系统的凭证详细信息
/// </summary>
public class FinanceVoucherInfo
{
/// <summary>
/// 财务凭证编号
/// </summary>
public string VoucherNo { get; set; } = string.Empty;
/// <summary>
/// 关联的业务订单编号
/// </summary>
public string BusinessNo { get; set; } = string.Empty;
/// <summary>
/// 凭证金额
/// </summary>
public decimal Amount { get; set; }
/// <summary>
/// 币种代码
/// </summary>
public string CurrencyCode { get; set; } = string.Empty;
/// <summary>
/// 凭证状态
/// </summary>
public string Status { get; set; } = string.Empty;
/// <summary>
/// 凭证摘要
/// </summary>
public string Summary { get; set; } = string.Empty;
/// <summary>
/// 凭证创建时间
/// </summary>
public DateTime CreateTime { get; set; }
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/07/04 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : ThirdPartyLogisticsAdapter.cs
*/
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Text;
using TemplateMethodPattern.Domain.Entities;
using TemplateMethodPattern.Domain.Interfaces;
using TemplateMethodPattern.Domain.ValueObjects;
namespace TemplateMethodPattern.Infrastructure.Adapters
{
/// <summary>
/// 第三方物流系统适配器【适配器模式】
/// 实现IExternalLogisticsAdapter接口,将内部统一物流契约适配到第三方物流公司的异构系统API,
/// 封装运单创建、轨迹查询、物流状态同步等外部调用细节,支持多家物流服务商的统一接入。
/// 具备线程安全与高并发处理能力,支持珠宝高价值商品的保价运输。
/// </summary>
public class ThirdPartyLogisticsAdapter : IExternalLogisticsAdapter
{
/// <summary>
/// 适配器日志记录器实例
/// </summary>
private readonly ILogger<ThirdPartyLogisticsAdapter> _logger;
/// <summary>
/// 第三方物流系统API基础地址
/// </summary>
private readonly string _apiBaseUrl;
/// <summary>
/// 物流服务商名称
/// </summary>
private readonly string _carrierName;
/// <summary>
/// 模拟物流订单缓存字典,Key为运单号,Value为物流订单实体
/// 实际项目中此处应调用真实第三方物流API
/// </summary>
private readonly Dictionary<string, LogisticsOrder> _mockOrderCache;
/// <summary>
/// 缓存读写锁,保护并发访问
/// </summary>
private readonly ReaderWriterLockSlim _cacheLock = new();
/// <summary>
/// 运单号生成计数器,用于线程安全的运单号生成
/// </summary>
private int _trackingNoCounter = 0;
/// <summary>
/// 构造函数,通过DI注入日志与配置
/// </summary>
/// <param name="logger">日志记录器实例</param>
public ThirdPartyLogisticsAdapter(ILogger<ThirdPartyLogisticsAdapter> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_apiBaseUrl = "https://api.logistics.example.com/v2";
_carrierName = "顺丰速运(珠宝专递)";
_mockOrderCache = new Dictionary<string, LogisticsOrder>();
_logger.LogInformation("第三方物流适配器初始化完成,物流公司:{CarrierName},API地址:{ApiBaseUrl}",
_carrierName, _apiBaseUrl);
}
/// <summary>
/// 同步创建物流运单
/// 调用第三方物流系统下单,生成运单号并返回物流订单信息
/// </summary>
/// <param name="businessNo">内部业务订单编号</param>
/// <param name="senderAddress">发货地址</param>
/// <param name="receiverAddress">收货地址</param>
/// <param name="insuranceAmount">保价金额</param>
/// <returns>物流订单实体对象,包含运单号与物流信息</returns>
public LogisticsOrder CreateShipment(
string businessNo,
Address senderAddress,
Address receiverAddress,
decimal insuranceAmount)
{
_logger.LogDebug("适配器同步调用第三方物流创建运单,业务编号:{BusinessNo},保价金额:{InsuranceAmount}",
businessNo, insuranceAmount);
if (senderAddress == null)
{
throw new ArgumentNullException(nameof(senderAddress), "发货地址不能为空");
}
if (receiverAddress == null)
{
throw new ArgumentNullException(nameof(receiverAddress), "收货地址不能为空");
}
// 线程安全地生成运单号
int sequence = Interlocked.Increment(ref _trackingNoCounter);
string trackingNo = $"SF{DateTime.Now:yyyyMMddHHmmss}{sequence:D6}";
// 计算运费(基础运费+保价费)
decimal baseFreight = 28.00m; // 珠宝专递基础运费
decimal insuranceFee = Math.Round(insuranceAmount * 0.005m, 2); // 保价费率0.5%
decimal totalFreight = baseFreight + insuranceFee;
var logisticsOrder = new LogisticsOrder
{
LogisticsId = Guid.NewGuid(),
TrackingNo = trackingNo,
BusinessNo = businessNo,
CarrierName = _carrierName,
ServiceType = "珠宝保价专递",
SenderAddress = senderAddress,
ReceiverAddress = receiverAddress,
FreightAmount = totalFreight,
InsuranceAmount = insuranceAmount,
CurrentStatus = "已下单",
IsDelivered = false,
CreateTime = DateTime.Now
};
// 添加初始轨迹
logisticsOrder.AddTrack("已下单", "深圳珠宝产业园", "物流订单已创建,等待揽收");
// 缓存物流订单
_cacheLock.EnterWriteLock();
try
{
_mockOrderCache[trackingNo] = logisticsOrder;
}
finally
{
_cacheLock.ExitWriteLock();
}
_logger.LogInformation("第三方物流运单创建成功,运单号:{TrackingNo},运费:{Freight},保价费:{InsuranceFee}",
trackingNo, totalFreight, insuranceFee);
return logisticsOrder;
}
/// <summary>
/// 查询物流轨迹
/// 根据运单号从第三方物流系统获取最新的物流轨迹信息
/// </summary>
/// <param name="trackingNo">物流运单号</param>
/// <returns>物流订单实体,包含最新轨迹信息;若不存在则返回null</returns>
public LogisticsOrder? QueryTracking(string trackingNo)
{
_logger.LogDebug("适配器调用第三方物流查询轨迹,运单号:{TrackingNo}", trackingNo);
if (string.IsNullOrWhiteSpace(trackingNo))
{
_logger.LogWarning("运单号为空,无法查询");
return null;
}
_cacheLock.EnterReadLock();
try
{
if (_mockOrderCache.TryGetValue(trackingNo, out var order))
{
_logger.LogInformation("物流轨迹查询成功,运单号:{TrackingNo},当前状态:{Status}",
trackingNo, order.CurrentStatus);
return order;
}
_logger.LogWarning("未找到物流订单,运单号:{TrackingNo}", trackingNo);
return null;
}
finally
{
_cacheLock.ExitReadLock();
}
}
/// <summary>
/// 异步创建物流运单
/// 支持高并发场景下的异步调用
/// </summary>
/// <param name="businessNo">内部业务订单编号</param>
/// <param name="senderAddress">发货地址</param>
/// <param name="receiverAddress">收货地址</param>
/// <param name="insuranceAmount">保价金额</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务,结果为物流订单实体对象</returns>
public async Task<LogisticsOrder> CreateShipmentAsync(
string businessNo,
Address senderAddress,
Address receiverAddress,
decimal insuranceAmount,
CancellationToken cancellationToken = default)
{
_logger.LogDebug("适配器异步调用第三方物流创建运单,业务编号:{BusinessNo}", businessNo);
// 模拟异步HTTP调用延迟
await Task.Delay(30, cancellationToken);
// 复用同步创建逻辑
return CreateShipment(businessNo, senderAddress, receiverAddress, insuranceAmount);
}
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/07/04 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : ThirdPartySupplierAdapter.cs
*/
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Text;
using TemplateMethodPattern.Domain.Entities;
using TemplateMethodPattern.Domain.Enums;
using TemplateMethodPattern.Domain.Interfaces;
namespace TemplateMethodPattern.Infrastructure.Adapters
{
/// <summary>
/// 第三方原料供应商系统适配器【适配器模式】
/// 实现IExternalSupplierAdapter接口,将内部统一契约适配到第三方原料供应商的异构系统API,
/// 封装外部系统的调用细节、数据转换与异常处理,使内部业务层无需感知外部系统差异。
/// 支持同步与异步调用,具备线程安全与高并发处理能力。
/// </summary>
public class ThirdPartySupplierAdapter : IExternalSupplierAdapter
{
/// <summary>
/// 适配器日志记录器实例
/// </summary>
private readonly ILogger<ThirdPartySupplierAdapter> _logger;
/// <summary>
/// 第三方供应商系统API基础地址
/// </summary>
private readonly string _apiBaseUrl;
/// <summary>
/// API访问密钥
/// </summary>
private readonly string _apiKey;
/// <summary>
/// 模拟原料信息缓存字典,Key为原料编码,Value为原料实体
/// 实际项目中此处应调用真实第三方API,此处用内存数据模拟
/// </summary>
private readonly Dictionary<string, RawMaterial> _mockMaterialCache;
/// <summary>
/// 缓存读写锁,保护并发访问
/// </summary>
private readonly ReaderWriterLockSlim _cacheLock = new();
/// <summary>
/// 构造函数,通过DI注入日志与配置
/// </summary>
/// <param name="logger">日志记录器实例</param>
public ThirdPartySupplierAdapter(ILogger<ThirdPartySupplierAdapter> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_apiBaseUrl = "https://api.supplier.example.com/v1";
_apiKey = "mock-api-key-for-demo";
_mockMaterialCache = InitializeMockMaterials();
_logger.LogInformation("第三方原料供应商适配器初始化完成,API地址:{ApiBaseUrl}", _apiBaseUrl);
}
/// <summary>
/// 初始化模拟原料数据
/// 实际项目中此处不需要,数据来自真实第三方API
/// </summary>
/// <returns>模拟原料数据字典</returns>
private Dictionary<string, RawMaterial> InitializeMockMaterials()
{
var materials = new Dictionary<string, RawMaterial>
{
["RAW-00101"] = new RawMaterial
{
MaterialId = Guid.NewGuid(),
MaterialCode = "RAW-00101",
MaterialName = "18K玫瑰金原料",
MaterialType = MaterialType.Gold,
Weight = 500.00m,
Purity = "Au750",
SupplierName = "深圳贵金属供应商",
UnitPrice = 320.00m,
IsVerified = false,
InboundTime = DateTime.Now.AddDays(-3)
},
["RAW-00201"] = new RawMaterial
{
MaterialId = Guid.NewGuid(),
MaterialCode = "RAW-00201",
MaterialName = "GIA裸钻1.0克拉",
MaterialType = MaterialType.Diamond,
Weight = 1.00m,
Purity = "VVS1 D色 3EX",
SupplierName = "上海钻石交易所",
UnitPrice = 68000.00m,
IsVerified = false,
InboundTime = DateTime.Now.AddDays(-5)
},
["RAW-00301"] = new RawMaterial
{
MaterialId = Guid.NewGuid(),
MaterialCode = "RAW-00301",
MaterialName = "Pt950铂金原料",
MaterialType = MaterialType.Platinum,
Weight = 300.00m,
Purity = "Pt950",
SupplierName = "广州贵金属供应商",
UnitPrice = 280.00m,
IsVerified = false,
InboundTime = DateTime.Now.AddDays(-2)
}
};
return materials;
}
/// <summary>
/// 同步校验原料物料是否合法合规
/// 调用第三方供应商系统核验原料的来源、品质证书与合规状态
/// </summary>
/// <param name="materialCode">原料物料编码</param>
/// <returns>true表示原料核验通过,false表示核验不通过</returns>
public bool CheckMaterialValid(string materialCode)
{
_logger.LogDebug("适配器同步调用第三方原料供应商核验接口,原料编码:{MaterialCode}", materialCode);
if (string.IsNullOrWhiteSpace(materialCode))
{
_logger.LogWarning("原料编码为空,核验失败");
return false;
}
// 模拟调用第三方API,实际项目中此处使用HttpClient调用真实接口
_cacheLock.EnterReadLock();
try
{
bool exists = _mockMaterialCache.ContainsKey(materialCode);
bool isValid = exists && materialCode.StartsWith("RAW-", StringComparison.OrdinalIgnoreCase);
_logger.LogInformation("第三方原料核验结果,原料编码:{MaterialCode},是否存在:{Exists},是否合法:{IsValid}",
materialCode, exists, isValid);
return isValid;
}
finally
{
_cacheLock.ExitReadLock();
}
}
/// <summary>
/// 获取原料详细信息
/// 从第三方供应商系统获取指定原料的完整信息,包括品质参数、证书与来源
/// </summary>
/// <param name="materialCode">原料物料编码</param>
/// <returns>原料实体对象,若不存在则返回null</returns>
public RawMaterial? GetMaterialInfo(string materialCode)
{
_logger.LogDebug("适配器调用第三方原料供应商获取原料信息,原料编码:{MaterialCode}", materialCode);
_cacheLock.EnterReadLock();
try
{
if (_mockMaterialCache.TryGetValue(materialCode, out var material))
{
// 返回原料对象的副本,避免外部修改内部缓存
return new RawMaterial
{
MaterialId = material.MaterialId,
MaterialCode = material.MaterialCode,
MaterialName = material.MaterialName,
MaterialType = material.MaterialType,
Weight = material.Weight,
Purity = material.Purity,
SupplierName = material.SupplierName,
UnitPrice = material.UnitPrice,
IsVerified = material.IsVerified,
VerificationRemark = material.VerificationRemark,
InboundTime = material.InboundTime,
VerificationTime = material.VerificationTime
};
}
_logger.LogWarning("未找到原料信息,原料编码:{MaterialCode}", materialCode);
return null;
}
finally
{
_cacheLock.ExitReadLock();
}
}
/// <summary>
/// 异步校验原料物料是否合法合规
/// 支持高并发场景下的异步调用,避免阻塞业务线程
/// </summary>
/// <param name="materialCode">原料物料编码</param>
/// <param name="cancellationToken">取消令牌,用于支持调用方取消操作</param>
/// <returns>异步任务,结果为true表示核验通过</returns>
public async Task<bool> CheckMaterialValidAsync(string materialCode, CancellationToken cancellationToken = default)
{
_logger.LogDebug("适配器异步调用第三方原料供应商核验接口,原料编码:{MaterialCode}", materialCode);
// 模拟异步HTTP调用延迟
await Task.Delay(20, cancellationToken);
// 复用同步核验逻辑
return CheckMaterialValid(materialCode);
}
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/07/04 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : AsyncLock.cs
*/
using System;
using System.Collections.Generic;
using System.Text;
namespace TemplateMethodPattern.Infrastructure.Concurrency
{
/// <summary>
/// 异步锁辅助类
/// 提供基于SemaphoreSlim的异步互斥锁,支持在异步场景下保护共享资源的线程安全访问,
/// 适用于高并发环境下需要串行执行的临界区代码,使用using模式自动释放锁。
/// </summary>
public sealed class AsyncLock : IDisposable
{
/// <summary>
/// 内部信号量实例,初始计数为1,最大计数为1,实现互斥锁
/// </summary>
private readonly SemaphoreSlim _semaphore;
/// <summary>
/// 标识对象是否已被释放
/// </summary>
private bool _isDisposed;
/// <summary>
/// 构造函数,初始化异步锁
/// </summary>
public AsyncLock()
{
_semaphore = new SemaphoreSlim(1, 1);
_isDisposed = false;
}
/// <summary>
/// 异步获取锁
/// 等待获取锁的所有权,支持取消令牌
/// </summary>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>可释放的锁持有者,使用using模式自动释放</returns>
public async Task<IDisposable> AcquireAsync(CancellationToken cancellationToken = default)
{
if (_isDisposed)
{
throw new ObjectDisposedException(nameof(AsyncLock));
}
await _semaphore.WaitAsync(cancellationToken);
return new LockReleaser(this);
}
/// <summary>
/// 同步获取锁
/// 同步等待获取锁的所有权
/// </summary>
/// <returns>可释放的锁持有者</returns>
public IDisposable Acquire()
{
if (_isDisposed)
{
throw new ObjectDisposedException(nameof(AsyncLock));
}
_semaphore.Wait();
return new LockReleaser(this);
}
/// <summary>
/// 释放锁资源
/// </summary>
public void Dispose()
{
if (!_isDisposed)
{
_semaphore.Dispose();
_isDisposed = true;
}
}
/// <summary>
/// 锁持有者内部类
/// 实现IDisposable接口,在Dispose时释放信号量,支持using模式自动释放锁
/// </summary>
private sealed class LockReleaser : IDisposable
{
/// <summary>
/// 关联的异步锁实例引用
/// </summary>
private readonly AsyncLock _asyncLock;
/// <summary>
/// 标识是否已释放
/// </summary>
private bool _isReleased;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="asyncLock">关联的异步锁实例</param>
public LockReleaser(AsyncLock asyncLock)
{
_asyncLock = asyncLock;
_isReleased = false;
}
/// <summary>
/// 释放锁,释放信号量
/// </summary>
public void Dispose()
{
if (!_isReleased)
{
_asyncLock._semaphore.Release();
_isReleased = true;
}
}
}
}
}
using Karambolo.Extensions.Logging.File;
using Microsoft.Extensions.Logging;
using System;
using System.Globalization;
using System.Text;
namespace TemplateMethodPattern.Infrastructure.Logging
{
/// <summary>
/// 保证文件日志与控制台输出格式完全一致的文本构建器
/// 输出格式:yyyy-MM-dd HH:mm:ss.fff [log-level] 类别: 消息
/// </summary>
public sealed class ConsoleFormatTextBuilder : FileLogEntryTextBuilder
{
public new static readonly ConsoleFormatTextBuilder Instance = new();
private const string TimestampFormat = "yyyy-MM-dd HH:mm:ss.fff";
public override void BuildEntryText(
StringBuilder sb,
string categoryName,
LogLevel logLevel,
EventId eventId,
string? message,
Exception? exception,
IExternalScopeProvider? scopeProvider,
DateTimeOffset timestamp)
{
sb.Append(timestamp.ToLocalTime().ToString(TimestampFormat, CultureInfo.InvariantCulture));
sb.Append(' ');
sb.Append('[');
sb.Append(GetLogLevelString(logLevel));
sb.Append("] ");
sb.Append(categoryName);
sb.Append(": ");
sb.Append(message);
if (exception is not null)
{
sb.AppendLine();
sb.Append(exception);
}
sb.AppendLine();
}
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/07/04 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : LoggingExtensions.cs
*/
using Karambolo.Extensions.Logging.File;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace TemplateMethodPattern.Infrastructure.Logging
{
/// <summary>
/// 企业级日志配置扩展类
/// 提供统一的日志配置扩展方法,确保控制台输出与文件日志输出格式完全一致。
/// 说明:基于 Karambolo.Extensions.Logging.File v4 实现(API 已与 v3 大不相同),
/// v3 中的 FileNamePattern / TextFormatterOptions / RollingPolicy / RetainedFileCountLimit / LevelAliases
/// 等属性均已移除,改为:BasePath + Files(路径模板) 定义文件位置,TextBuilder 定义文本格式,
/// MaxFileSize + 路径中的 <counter> 占位符实现滚动,单个 FileLoggerProvider 对应一类文件。
/// </summary>
public static class LoggingExtensions
{
/// <summary>
/// 配置企业级统一日志
/// 同时配置控制台日志与 Karambolo 文件日志,确保两者输出格式完全一致,
/// 文件按日期分文件夹存储,并按日志等级分别写入独立文件:
/// app-debug.log / app-info.log / app-warn.log / app-error.log。
/// </summary>
/// <param name="loggingBuilder">日志构建器实例</param>
/// <returns>配置完成的日志构建器实例,支持链式调用</returns>
public static ILoggingBuilder ConfigureEnterpriseLogging(this ILoggingBuilder loggingBuilder)
{
// 清除默认提供的日志提供者,确保日志配置的纯净性
loggingBuilder.ClearProviders();
// 设置全局最低日志级别为 Trace,让各文件提供者自行决定哪些级别落盘
loggingBuilder.SetMinimumLevel(LogLevel.Trace);
// 配置控制台日志,使用统一的时间戳输出格式
loggingBuilder.AddConsole(options =>
{
// 时间戳格式,与文件日志保持一致
options.TimestampFormat = "yyyy-MM-dd HH:mm:ss.fff ";
// Error及以上级别输出到标准错误流
options.LogToStandardErrorThreshold = LogLevel.Error;
});
// Karambolo v4:每个 FileLoggerProvider 对应一类文件,
// 通过限制各自允许输出的最高日志级别来把不同级别路由到不同文件。
// Debug 及以下 → app-debug.log
loggingBuilder.AddFile<DebugFileLoggerProvider>(
configure: options => ApplyFileLogOptions(options, "debug"));
// Information 及以下 → app-info.log
loggingBuilder.AddFile<InfoFileLoggerProvider>(
configure: options => ApplyFileLogOptions(options, "info"));
// Warning 及以下 → app-warn.log
loggingBuilder.AddFile<WarnFileLoggerProvider>(
configure: options => ApplyFileLogOptions(options, "warn"));
// Error/Critical → app-error.log
loggingBuilder.AddFile<ErrorFileLoggerProvider>(
configure: options => ApplyFileLogOptions(options, "error"));
return loggingBuilder;
}
/// <summary>
/// 按文件等级应用公共的文件日志选项。
/// </summary>
/// <param name="options">文件日志选项</param>
/// <param name="levelFileToken">文件名的日志等级片段(debug/info/warn/error)</param>
private static void ApplyFileLogOptions(FileLoggerOptions options, string levelFileToken)
{
// 日志文件根目录
options.BasePath = "Logs";
// 文件名模板:日期子文件夹使用 <date> 占位符,文件按等级命名,并带 <counter> 以支持按大小滚动
// 输出路径示例:Logs/2026-09-01/app-info-0.log
options.Files = new[]
{
new LogFileOptions
{
Path = $"<date:yyyy-MM-dd>/app-{levelFileToken}-<counter>.log"
}
};
// 文件访问模式:OpenTemporarily ------ 仅在写入时打开、写完立即关闭,
// 避免长期占用文件句柄,支持多线程/多进程并发写入
options.FileAccessMode = LogFileAccessMode.OpenTemporarily;
// 单个日志文件最大大小(10MB),超过后借助 <counter> 占位符滚动到新文件
options.MaxFileSize = 10 * 1024 * 1024;
// 使用自定义文本构建器,确保文件与控制台输出格式完全一致
options.TextBuilder = ConsoleFormatTextBuilder.Instance;
// 使用 UTF-8 编码
options.FileEncodingName = "utf-8";
}
/// <summary>
/// 添加企业级日志服务到依赖注入容器
/// 为 IServiceCollection 提供便捷的日志配置扩展方法
/// </summary>
/// <param name="services">依赖注入服务集合</param>
/// <returns>配置完成的服务集合实例,支持链式调用</returns>
public static IServiceCollection AddEnterpriseLogging(this IServiceCollection services)
{
// 配置日志服务
services.AddLogging(loggingBuilder =>
{
loggingBuilder.ConfigureEnterpriseLogging();
});
return services;
}
}
}
using Karambolo.Extensions.Logging.File;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
namespace TemplateMethodPattern.Infrastructure.Logging
{
/// <summary>
/// 按日志等级拆分的文件日志提供者基类(配合 LoggingExtensions 使用)
/// 因为 Karambolo v4 已移除按等级命名文件的 LevelAliases / FileNamePattern 配置,
/// 改为一个 FileLoggerProvider 对应一类文件的注册方式,通过限制允许输出的最高日志级别
/// 来实现 app-debug.log / app-info.log / app-warn.log / app-error.log 分开存储。
/// </summary>
public abstract class MaxLogLevelFileLoggerProvider : FileLoggerProvider
{
protected MaxLogLevelFileLoggerProvider(
FileLoggerContext context,
IOptionsMonitor<FileLoggerOptions> options,
string optionsName)
: base(context, options, optionsName)
{
}
/// <summary>本提供者允许输出的最高日志级别(越往上越严重,因此级别 <= 该值才会落盘)。</summary>
protected abstract LogLevel MaxLogLevel { get; }
protected override FileLogger CreateLoggerCore(string categoryName)
{
return new MaxLogLevelFileLogger(categoryName, Processor, Settings, GetScopeProvider(), Context.GetTimestamp, MaxLogLevel);
}
/// <summary>
/// 限制输出级别的 FileLogger
/// </summary>
private sealed class MaxLogLevelFileLogger : FileLogger
{
private readonly LogLevel _maxLogLevel;
public MaxLogLevelFileLogger(
string categoryName,
IFileLoggerProcessor processor,
IFileLoggerSettings settings,
IExternalScopeProvider? scopeProvider,
Func<DateTimeOffset>? timestampProvider,
LogLevel maxLogLevel)
: base(categoryName, processor, settings, scopeProvider, timestampProvider)
{
_maxLogLevel = maxLogLevel;
}
public override bool IsEnabled(LogLevel logLevel)
{
return logLevel <= _maxLogLevel && base.IsEnabled(logLevel);
}
}
}
/// <summary>Debug 及以下级别 → app-debug.log</summary>
public sealed class DebugFileLoggerProvider : MaxLogLevelFileLoggerProvider
{
public DebugFileLoggerProvider(FileLoggerContext context, IOptionsMonitor<FileLoggerOptions> options, string optionsName)
: base(context, options, optionsName) { }
protected override LogLevel MaxLogLevel => LogLevel.Debug;
}
/// <summary>Information 及以下级别 → app-info.log</summary>
public sealed class InfoFileLoggerProvider : MaxLogLevelFileLoggerProvider
{
public InfoFileLoggerProvider(FileLoggerContext context, IOptionsMonitor<FileLoggerOptions> options, string optionsName)
: base(context, options, optionsName) { }
protected override LogLevel MaxLogLevel => LogLevel.Information;
}
/// <summary>Warning 及以下级别 → app-warn.log</summary>
public sealed class WarnFileLoggerProvider : MaxLogLevelFileLoggerProvider
{
public WarnFileLoggerProvider(FileLoggerContext context, IOptionsMonitor<FileLoggerOptions> options, string optionsName)
: base(context, options, optionsName) { }
protected override LogLevel MaxLogLevel => LogLevel.Warning;
}
/// <summary>Error / Critical → app-error.log</summary>
public sealed class ErrorFileLoggerProvider : MaxLogLevelFileLoggerProvider
{
public ErrorFileLoggerProvider(FileLoggerContext context, IOptionsMonitor<FileLoggerOptions> options, string optionsName)
: base(context, options, optionsName) { }
protected override LogLevel MaxLogLevel => LogLevel.Error;
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/07/04 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : InMemoryBusinessFlowRepository.cs
*/
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
using TemplateMethodPattern.Domain.Entities;
using TemplateMethodPattern.Domain.Interfaces;
using Microsoft.Extensions.Logging;
namespace TemplateMethodPattern.Infrastructure.Repositories
{
/// <summary>
/// 内存版业务流程仓储实现
/// 实现IBusinessFlowRepository接口,使用线程安全的并发字典在内存中存储业务流程上下文数据,
/// 适用于演示、测试与无数据库场景,具备完整的线程安全与高并发处理能力。
/// 实际生产环境中可替换为数据库仓储实现(如SQL Server、MySQL),无需修改上层业务代码。
/// </summary>
public class InMemoryBusinessFlowRepository : IBusinessFlowRepository
{
/// <summary>
/// 仓储日志记录器实例
/// </summary>
private readonly ILogger<InMemoryBusinessFlowRepository> _logger;
/// <summary>
/// 业务上下文并发存储字典,Key为业务编号,Value为业务上下文对象
/// 使用ConcurrentDictionary确保多线程并发读写的线程安全
/// </summary>
private readonly ConcurrentDictionary<string, JewelleryBusinessContext> _contextStore;
/// <summary>
/// 构造函数,注入日志记录器
/// </summary>
/// <param name="logger">日志记录器实例</param>
public InMemoryBusinessFlowRepository(ILogger<InMemoryBusinessFlowRepository> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_contextStore = new ConcurrentDictionary<string, JewelleryBusinessContext>();
_logger.LogInformation("内存版业务流程仓储初始化完成");
}
/// <summary>
/// 同步保存业务上下文数据
/// 将业务流程的当前状态持久化到内存存储,支持新增与更新
/// </summary>
/// <param name="context">业务上下文对象</param>
/// <returns>true表示保存成功</returns>
public bool Save(JewelleryBusinessContext context)
{
if (context == null)
{
_logger.LogWarning("保存业务上下文失败,上下文对象为空");
return false;
}
if (string.IsNullOrWhiteSpace(context.BusinessNo))
{
_logger.LogWarning("保存业务上下文失败,业务编号为空");
return false;
}
// 使用AddOrUpdate实现线程安全的新增或更新
_contextStore.AddOrUpdate(
key: context.BusinessNo,
addValue: context,
updateValueFactory: (key, existing) => context);
_logger.LogDebug("业务上下文已保存,业务编号:{BusinessNo},当前阶段:{Stage}",
context.BusinessNo, context.CurrentStage);
return true;
}
/// <summary>
/// 根据业务编号同步查询业务上下文
/// </summary>
/// <param name="businessNo">业务订单编号</param>
/// <returns>业务上下文对象,若不存在则返回null</returns>
public JewelleryBusinessContext? GetByBusinessNo(string businessNo)
{
if (string.IsNullOrWhiteSpace(businessNo))
{
_logger.LogWarning("查询业务上下文失败,业务编号为空");
return null;
}
if (_contextStore.TryGetValue(businessNo, out var context))
{
_logger.LogDebug("业务上下文查询成功,业务编号:{BusinessNo}", businessNo);
return context;
}
_logger.LogDebug("未找到业务上下文,业务编号:{BusinessNo}", businessNo);
return null;
}
/// <summary>
/// 同步查询所有业务上下文列表
/// </summary>
/// <returns>业务上下文只读集合</returns>
public IReadOnlyList<JewelleryBusinessContext> GetAll()
{
var allContexts = _contextStore.Values.ToList();
_logger.LogDebug("查询全部业务上下文,总数:{Count}", allContexts.Count);
return allContexts.AsReadOnly();
}
/// <summary>
/// 异步保存业务上下文数据
/// 支持高并发场景下的异步持久化
/// </summary>
/// <param name="context">业务上下文对象</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务,结果为true表示保存成功</returns>
public Task<bool> SaveAsync(JewelleryBusinessContext context, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
// 内存操作本身是同步的,使用Task.FromResult包装为异步接口
bool result = Save(context);
return Task.FromResult(result);
}
/// <summary>
/// 异步根据业务编号查询业务上下文
/// </summary>
/// <param name="businessNo">业务订单编号</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务,结果为业务上下文对象</returns>
public Task<JewelleryBusinessContext?> GetByBusinessNoAsync(string businessNo, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
var context = GetByBusinessNo(businessNo);
return Task.FromResult(context);
}
/// <summary>
/// 获取仓储中存储的业务上下文总数
/// </summary>
/// <returns>业务上下文数量</returns>
public int GetCount()
{
return _contextStore.Count;
}
/// <summary>
/// 清空仓储中所有业务上下文数据
/// </summary>
public void ClearAll()
{
_logger.LogInformation("清空内存仓储中所有业务上下文数据");
_contextStore.Clear();
}
}
}
cs
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/07/04 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : BusinessFlowRequest.cs
*/
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using TemplateMethodPattern.Application.Workflows;
using TemplateMethodPattern.Domain.Entities;
using TemplateMethodPattern.Domain.Interfaces;
using TemplateMethodPattern.Domain.ValueObjects;
using TemplateMethodPattern.Application.DTOs;
namespace TemplateMethodPattern.Application.DTOs
{
/// <summary>
/// 业务流程执行请求DTO
/// 用于从外部调用方接收业务流程启动参数,隔离外部输入与领域实体
/// </summary>
public class BusinessFlowRequest
{
/// <summary>
/// 业务订单编号,调用方传入的唯一业务标识
/// </summary>
public string BusinessNo { get; set; } = string.Empty;
/// <summary>
/// 客户名称
/// </summary>
public string CustomerName { get; set; } = string.Empty;
/// <summary>
/// 客户联系电话
/// </summary>
public string CustomerPhone { get; set; } = string.Empty;
/// <summary>
/// 原料物料编码,指定本次业务使用的原料
/// </summary>
public string RawMaterialCode { get; set; } = string.Empty;
/// <summary>
/// 业务总金额数值
/// </summary>
public decimal TotalAmount { get; set; }
/// <summary>
/// 币种代码,默认人民币CNY
/// </summary>
public string CurrencyCode { get; set; } = "CNY";
/// <summary>
/// 收货省份
/// </summary>
public string Province { get; set; } = string.Empty;
/// <summary>
/// 收货城市
/// </summary>
public string City { get; set; } = string.Empty;
/// <summary>
/// 收货区/县
/// </summary>
public string District { get; set; } = string.Empty;
/// <summary>
/// 收货详细地址
/// </summary>
public string DetailAddress { get; set; } = string.Empty;
/// <summary>
/// 收货邮政编码
/// </summary>
public string PostalCode { get; set; } = string.Empty;
/// <summary>
/// 业务备注信息
/// </summary>
public string? Remark { get; set; }
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/07/04 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : BusinessFlowResult.cs
*/
using System;
using System.Collections.Generic;
using System.Text;
using TemplateMethodPattern.Domain.Enums;
namespace TemplateMethodPattern.Application.DTOs
{
/// <summary>
/// 业务流程执行结果DTO
/// 用于向调用方返回业务流程的执行结果与状态信息,隔离内部领域实体
/// </summary>
public class BusinessFlowResult
{
/// <summary>
/// 业务订单编号
/// </summary>
public string BusinessNo { get; set; } = string.Empty;
/// <summary>
/// 业务流程是否执行成功
/// </summary>
public bool IsSuccess { get; set; }
/// <summary>
/// 业务当前所处阶段
/// </summary>
public BusinessStage CurrentStage { get; set; }
/// <summary>
/// 设计图纸编号,设计阶段完成后生成
/// </summary>
public string DesignDrawingNo { get; set; } = string.Empty;
/// <summary>
/// 成品产品编号,生产阶段完成后生成
/// </summary>
public string ProductCode { get; set; } = string.Empty;
/// <summary>
/// 物流运单号,物流发货后生成
/// </summary>
public string LogisticsNo { get; set; } = string.Empty;
/// <summary>
/// 财务凭证编号,财务记账完成后生成
/// </summary>
public string FinanceVoucherNo { get; set; } = string.Empty;
/// <summary>
/// 业务总金额描述字符串
/// </summary>
public string TotalAmountDisplay { get; set; } = string.Empty;
/// <summary>
/// 执行结果消息,成功或失败的说明
/// </summary>
public string Message { get; set; } = string.Empty;
/// <summary>
/// 异常错误消息,流程异常时填充
/// </summary>
public string? ErrorMessage { get; set; }
/// <summary>
/// 业务流程创建时间
/// </summary>
public DateTime CreateTime { get; set; }
/// <summary>
/// 业务流程完成时间
/// </summary>
public DateTime? CompleteTime { get; set; }
/// <summary>
/// 流程执行耗时(毫秒)
/// </summary>
public long ElapsedMilliseconds { get; set; }
/// <summary>
/// 各阶段执行历史记录列表
/// </summary>
public List<StageHistoryItem> StageHistories { get; set; } = new List<StageHistoryItem>();
}
/// <summary>
/// 阶段历史记录项DTO
/// 用于向调用方返回每个业务阶段的执行情况
/// </summary>
public class StageHistoryItem
{
/// <summary>
/// 业务阶段名称
/// </summary>
public string StageName { get; set; } = string.Empty;
/// <summary>
/// 阶段执行说明消息
/// </summary>
public string Message { get; set; } = string.Empty;
/// <summary>
/// 阶段执行时间
/// </summary>
public DateTime ExecuteTime { get; set; }
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/07/04 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : BusinessTaskScheduler.cs
*/
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text;
using TemplateMethodPattern.Application.DTOs;
using TemplateMethodPattern.Application.Services;
namespace TemplateMethodPattern.Application.Scheduler
{
/// <summary>
/// 珠宝业务任务调度器
/// 负责任务的统一调度与执行管理,支持单任务调度、批量并发调度、
/// 任务状态跟踪与调度统计,全部使用标准ILogger记录调度日志。
/// 采用线程安全的并发集合与信号量控制,确保高并发场景下的稳定运行。
/// </summary>
public class BusinessTaskScheduler
{
/// <summary>
/// 调度器日志记录器实例
/// </summary>
private readonly ILogger<BusinessTaskScheduler> _logger;
/// <summary>
/// 业务流程应用服务实例
/// </summary>
private readonly BusinessFlowService _businessFlowService;
/// <summary>
/// 并发任务结果字典,Key为业务编号,Value为执行结果
/// 使用ConcurrentDictionary确保多线程并发写入的线程安全
/// </summary>
private readonly ConcurrentDictionary<string, BusinessFlowResult> _taskResults = new();
/// <summary>
/// 并发度控制信号量,限制同时执行的最大任务数,防止系统资源耗尽
/// </summary>
private readonly SemaphoreSlim _concurrencySemaphore;
/// <summary>
/// 最大并发任务数
/// </summary>
private readonly int _maxConcurrency;
/// <summary>
/// 调度器统计信息对象
/// </summary>
private readonly SchedulerStatistics _statistics;
/// <summary>
/// 统计信息更新锁
/// </summary>
private readonly object _statisticsLock = new();
/// <summary>
/// 构造函数,通过DI注入依赖
/// </summary>
/// <param name="logger">调度器日志记录器</param>
/// <param name="businessFlowService">业务流程应用服务</param>
/// <param name="maxConcurrency">最大并发任务数,默认为10</param>
public BusinessTaskScheduler(
ILogger<BusinessTaskScheduler> logger,
BusinessFlowService businessFlowService,
int maxConcurrency = 10)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_businessFlowService = businessFlowService ?? throw new ArgumentNullException(nameof(businessFlowService));
_maxConcurrency = maxConcurrency > 0 ? maxConcurrency : 10;
_concurrencySemaphore = new SemaphoreSlim(_maxConcurrency, _maxConcurrency);
_statistics = new SchedulerStatistics
{
MaxConcurrency = _maxConcurrency
};
_logger.LogInformation("业务任务调度器初始化完成,最大并发数:{MaxConcurrency}", _maxConcurrency);
}
/// <summary>
/// 调度执行单个业务任务
/// </summary>
/// <param name="request">业务流程执行请求</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>业务流程执行结果</returns>
public async Task<BusinessFlowResult> DispatchSingleTaskAsync(BusinessFlowRequest request, CancellationToken cancellationToken = default)
{
_logger.LogInformation("调度器收到单个业务任务,业务编号:{BusinessNo}", request.BusinessNo);
// 等待并发信号量,控制最大并发数
await _concurrencySemaphore.WaitAsync(cancellationToken);
try
{
var result = await _businessFlowService.ExecuteFlowAsync(request, cancellationToken);
// 记录任务结果
_taskResults[request.BusinessNo] = result;
// 更新统计信息
UpdateStatistics(result.IsSuccess);
_logger.LogInformation("调度器完成单个任务调度,业务编号:{BusinessNo},执行结果:{Result}",
request.BusinessNo, result.IsSuccess ? "成功" : "失败");
return result;
}
finally
{
// 释放信号量
_concurrencySemaphore.Release();
}
}
/// <summary>
/// 并发批量调度业务任务
/// 使用Task.WhenAll实现高并发批量执行,同时通过信号量控制最大并发度
/// </summary>
/// <param name="requests">业务流程请求列表</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>所有任务的执行结果列表</returns>
public async Task<IReadOnlyList<BusinessFlowResult>> DispatchBatchTasksAsync(
IReadOnlyList<BusinessFlowRequest> requests,
CancellationToken cancellationToken = default)
{
if (requests == null || requests.Count == 0)
{
_logger.LogWarning("批量调度任务列表为空,无需调度");
return Array.Empty<BusinessFlowResult>();
}
_logger.LogInformation("调度器启动批量并发任务调度,任务数量:{TaskCount},最大并发数:{MaxConcurrency}",
requests.Count, _maxConcurrency);
// 创建所有任务的执行计划
var taskList = requests.Select(request => ExecuteTaskWithConcurrencyControlAsync(request, cancellationToken)).ToList();
// 等待所有任务完成
var results = await Task.WhenAll(taskList);
int successCount = results.Count(r => r.IsSuccess);
int failCount = results.Length - successCount;
_logger.LogInformation("调度器批量并发任务执行完毕,总任务数:{TotalCount},成功:{SuccessCount},失败:{FailCount}",
results.Length, successCount, failCount);
return results;
}
/// <summary>
/// 带并发控制的单个任务执行
/// 每个任务在执行前获取信号量,执行后释放,确保全局最大并发数不超限
/// </summary>
/// <param name="request">业务流程请求</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>业务流程执行结果</returns>
private async Task<BusinessFlowResult> ExecuteTaskWithConcurrencyControlAsync(
BusinessFlowRequest request,
CancellationToken cancellationToken)
{
await _concurrencySemaphore.WaitAsync(cancellationToken);
try
{
_logger.LogDebug("任务获取并发许可,开始执行,业务编号:{BusinessNo},当前并发数:{CurrentConcurrency}",
request.BusinessNo, _maxConcurrency - _concurrencySemaphore.CurrentCount + 1);
var result = await _businessFlowService.ExecuteFlowAsync(request, cancellationToken);
_taskResults[request.BusinessNo] = result;
UpdateStatistics(result.IsSuccess);
return result;
}
catch (Exception ex)
{
_logger.LogError(ex, "任务执行异常,业务编号:{BusinessNo}", request.BusinessNo);
var failResult = new BusinessFlowResult
{
BusinessNo = request.BusinessNo,
IsSuccess = false,
ErrorMessage = ex.Message,
Message = "任务执行异常"
};
_taskResults[request.BusinessNo] = failResult;
UpdateStatistics(false);
return failResult;
}
finally
{
_concurrencySemaphore.Release();
_logger.LogDebug("任务释放并发许可,业务编号:{BusinessNo}", request.BusinessNo);
}
}
/// <summary>
/// 更新调度器统计信息
/// </summary>
/// <param name="isSuccess">任务是否执行成功</param>
private void UpdateStatistics(bool isSuccess)
{
lock (_statisticsLock)
{
_statistics.TotalTasks++;
if (isSuccess)
{
_statistics.SuccessTasks++;
}
else
{
_statistics.FailedTasks++;
}
_statistics.LastUpdateTime = DateTime.Now;
}
}
/// <summary>
/// 获取指定业务编号的任务执行结果
/// </summary>
/// <param name="businessNo">业务编号</param>
/// <returns>任务执行结果,若不存在则返回null</returns>
public BusinessFlowResult? GetTaskResult(string businessNo)
{
if (_taskResults.TryGetValue(businessNo, out var result))
{
return result;
}
return null;
}
/// <summary>
/// 获取所有已执行任务的结果列表
/// </summary>
/// <returns>任务结果只读列表</returns>
public IReadOnlyList<BusinessFlowResult> GetAllTaskResults()
{
return _taskResults.Values.ToList().AsReadOnly();
}
/// <summary>
/// 获取调度器统计信息
/// </summary>
/// <returns>调度器统计信息对象的副本</returns>
public SchedulerStatistics GetStatistics()
{
lock (_statisticsLock)
{
return new SchedulerStatistics
{
TotalTasks = _statistics.TotalTasks,
SuccessTasks = _statistics.SuccessTasks,
FailedTasks = _statistics.FailedTasks,
MaxConcurrency = _statistics.MaxConcurrency,
CurrentConcurrency = _maxConcurrency - _concurrencySemaphore.CurrentCount,
LastUpdateTime = _statistics.LastUpdateTime
};
}
}
/// <summary>
/// 清空所有任务结果与统计信息
/// </summary>
public void ClearAllResults()
{
_logger.LogInformation("调度器清空所有任务结果与统计信息");
_taskResults.Clear();
lock (_statisticsLock)
{
_statistics.TotalTasks = 0;
_statistics.SuccessTasks = 0;
_statistics.FailedTasks = 0;
_statistics.LastUpdateTime = DateTime.Now;
}
}
}
/// <summary>
/// 调度器统计信息实体
/// 记录任务调度器的运行统计数据,用于监控与性能分析
/// </summary>
public class SchedulerStatistics
{
/// <summary>
/// 累计执行任务总数
/// </summary>
public int TotalTasks { get; set; }
/// <summary>
/// 执行成功的任务数
/// </summary>
public int SuccessTasks { get; set; }
/// <summary>
/// 执行失败的任务数
/// </summary>
public int FailedTasks { get; set; }
/// <summary>
/// 最大并发任务数
/// </summary>
public int MaxConcurrency { get; set; }
/// <summary>
/// 当前正在执行的并发任务数
/// </summary>
public int CurrentConcurrency { get; set; }
/// <summary>
/// 最后更新时间
/// </summary>
public DateTime LastUpdateTime { get; set; }
/// <summary>
/// 计算任务成功率
/// </summary>
/// <returns>成功率百分比</returns>
public double GetSuccessRate()
{
if (TotalTasks == 0)
{
return 0;
}
return Math.Round((double)SuccessTasks / TotalTasks * 100, 2);
}
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/07/04 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : BusinessFlowService.cs
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using TemplateMethodPattern.Application.DTOs;
using TemplateMethodPattern.Application.Workflows;
using TemplateMethodPattern.Domain.Entities;
using TemplateMethodPattern.Domain.Interfaces;
using TemplateMethodPattern.Domain.ValueObjects;
using Microsoft.Extensions.Logging;
namespace TemplateMethodPattern.Application.Services
{
/// <summary>
/// 珠宝业务流程应用服务
/// 作为业务流程的应用层入口,负责接收外部请求、构建业务上下文、
/// 调用工作流模板执行流程,并将执行结果转换为DTO返回给调用方。
/// 实现应用层与领域层的隔离,遵循职责单一原则。
/// </summary>
public class BusinessFlowService
{
/// <summary>
/// 日志记录器实例
/// </summary>
private readonly ILogger<BusinessFlowService> _logger;
/// <summary>
/// 标准珠宝订单流程工作流实例
/// </summary>
private readonly StandardJewelleryOrderFlow _orderFlow;
/// <summary>
/// 业务流程仓储实例
/// </summary>
private readonly IBusinessFlowRepository _repository;
/// <summary>
/// 构造函数,通过DI注入所有依赖
/// </summary>
/// <param name="logger">日志记录器</param>
/// <param name="orderFlow">标准珠宝订单流程工作流</param>
/// <param name="repository">业务流程仓储</param>
public BusinessFlowService(
ILogger<BusinessFlowService> logger,
StandardJewelleryOrderFlow orderFlow,
IBusinessFlowRepository repository)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_orderFlow = orderFlow ?? throw new ArgumentNullException(nameof(orderFlow));
_repository = repository ?? throw new ArgumentNullException(nameof(repository));
}
/// <summary>
/// 启动并执行业务流程
/// 将请求DTO转换为领域上下文,调用工作流执行完整业务流程,返回执行结果DTO
/// </summary>
/// <param name="request">业务流程执行请求DTO</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>业务流程执行结果DTO</returns>
public async Task<BusinessFlowResult> ExecuteFlowAsync(BusinessFlowRequest request, CancellationToken cancellationToken = default)
{
_logger.LogInformation("收到业务流程执行请求,业务编号:{BusinessNo},客户:{CustomerName}",
request.BusinessNo, request.CustomerName);
var stopwatch = Stopwatch.StartNew();
// 构建业务上下文对象
var context = BuildContext(request);
try
{
// 调用工作流模板执行完整业务流程
await _orderFlow.ExecuteBusinessFlowAsync(context, cancellationToken);
stopwatch.Stop();
_logger.LogInformation("业务流程执行成功,业务编号:{BusinessNo},总耗时:{Elapsed}毫秒",
request.BusinessNo, stopwatch.ElapsedMilliseconds);
// 转换为结果DTO返回
return MapToResult(context, stopwatch.ElapsedMilliseconds, "业务流程执行成功");
}
catch (Exception ex)
{
stopwatch.Stop();
_logger.LogError(ex, "业务流程执行失败,业务编号:{BusinessNo},异常:{Message}",
request.BusinessNo, ex.Message);
var failResult = MapToResult(context, stopwatch.ElapsedMilliseconds, "业务流程执行失败");
failResult.IsSuccess = false;
failResult.ErrorMessage = ex.Message;
return failResult;
}
}
/// <summary>
/// 根据业务编号查询业务流程执行结果
/// </summary>
/// <param name="businessNo">业务订单编号</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>业务流程执行结果DTO,若不存在则返回null</returns>
public async Task<BusinessFlowResult?> GetFlowResultAsync(string businessNo, CancellationToken cancellationToken = default)
{
_logger.LogDebug("查询业务流程结果,业务编号:{BusinessNo}", businessNo);
var context = await _repository.GetByBusinessNoAsync(businessNo, cancellationToken);
if (context == null)
{
_logger.LogWarning("未找到业务流程记录,业务编号:{BusinessNo}", businessNo);
return null;
}
return MapToResult(context, 0, "查询成功");
}
/// <summary>
/// 查询所有业务流程记录列表
/// </summary>
/// <returns>业务流程结果DTO列表</returns>
public Task<IReadOnlyList<BusinessFlowResult>> GetAllFlowResultsAsync()
{
_logger.LogDebug("查询全部业务流程记录");
var contexts = _repository.GetAll();
var results = contexts.Select(ctx => MapToResult(ctx, 0, "查询成功")).ToList();
return Task.FromResult<IReadOnlyList<BusinessFlowResult>>(results);
}
/// <summary>
/// 构建业务上下文对象,将请求DTO转换为领域实体
/// </summary>
/// <param name="request">业务流程请求DTO</param>
/// <returns>构建完成的业务上下文对象</returns>
private JewelleryBusinessContext BuildContext(BusinessFlowRequest request)
{
// 构建金额值对象
var totalAmount = new Money(request.TotalAmount, request.CurrencyCode);
// 构建收货地址(如果提供了地址信息)
Address? shippingAddress = null;
if (!string.IsNullOrWhiteSpace(request.Province) && !string.IsNullOrWhiteSpace(request.City))
{
shippingAddress = new Address(
contactName: request.CustomerName,
phoneNumber: request.CustomerPhone,
country: "中国",
province: request.Province,
city: request.City,
district: request.District,
detailAddress: request.DetailAddress,
postalCode: request.PostalCode);
}
var context = new JewelleryBusinessContext
{
ContextId = Guid.NewGuid(),
BusinessNo = request.BusinessNo,
CustomerName = request.CustomerName,
CustomerPhone = request.CustomerPhone,
RawMaterialCode = request.RawMaterialCode,
TotalAmount = totalAmount,
ShippingAddress = shippingAddress,
Remark = request.Remark,
CreateTime = DateTime.Now
};
_logger.LogDebug("业务上下文构建完成,业务编号:{BusinessNo},金额:{Amount}",
context.BusinessNo, context.TotalAmount);
return context;
}
/// <summary>
/// 将业务上下文领域实体映射为结果DTO
/// </summary>
/// <param name="context">业务上下文对象</param>
/// <param name="elapsedMilliseconds">执行耗时(毫秒)</param>
/// <param name="message">结果消息</param>
/// <returns>业务流程结果DTO</returns>
private BusinessFlowResult MapToResult(JewelleryBusinessContext context, long elapsedMilliseconds, string message)
{
var result = new BusinessFlowResult
{
BusinessNo = context.BusinessNo,
IsSuccess = context.IsSuccess,
CurrentStage = context.CurrentStage,
DesignDrawingNo = context.DesignDrawingNo,
ProductCode = context.ProductCode,
LogisticsNo = context.LogisticsNo,
FinanceVoucherNo = context.FinanceVoucherNo ?? string.Empty,
TotalAmountDisplay = context.TotalAmount.ToString(),
Message = message,
ErrorMessage = context.ErrorMessage,
CreateTime = context.CreateTime,
CompleteTime = context.CompleteTime,
ElapsedMilliseconds = elapsedMilliseconds
};
// 映射阶段历史记录
foreach (var history in context.StageHistories)
{
result.StageHistories.Add(new StageHistoryItem
{
StageName = history.Stage.ToString(),
Message = history.Message,
ExecuteTime = history.ExecuteTime
});
}
return result;
}
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/07/04 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : HumanResourceService.cs
*/
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Extensions.Logging;
namespace TemplateMethodPattern.Application.Services
{
/// <summary>
/// 人事行政应用服务
/// 负责珠宝企业的人事行政管理业务,包括员工考勤、档案管理、薪酬核算、行政事务等,
/// 与珠宝业务主流程配套运行,支持企业全链路运营管理。
/// </summary>
public class HumanResourceService
{
/// <summary>
/// 日志记录器实例
/// </summary>
private readonly ILogger<HumanResourceService> _logger;
/// <summary>
/// 线程安全的员工考勤记录字典,Key为员工编号,Value为今日考勤状态
/// </summary>
private readonly Dictionary<string, StaffAttendanceRecord> _attendanceRecords = new();
/// <summary>
/// 考勤记录读写锁,保护并发访问
/// </summary>
private readonly ReaderWriterLockSlim _attendanceLock = new();
/// <summary>
/// 构造函数,注入日志记录器
/// </summary>
/// <param name="logger">日志记录器实例</param>
public HumanResourceService(ILogger<HumanResourceService> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
/// <summary>
/// 处理员工考勤打卡
/// 记录员工的上班或下班打卡时间,支持高并发场景下的线程安全写入
/// </summary>
/// <param name="staffNo">员工编号</param>
/// <param name="staffName">员工姓名</param>
/// <param name="isCheckIn">true为上班打卡,false为下班打卡</param>
/// <returns>true表示打卡成功</returns>
public bool ProcessStaffAttendance(string staffNo, string staffName, bool isCheckIn)
{
_logger.LogInformation("人事行政模块处理员工考勤,员工编号:{StaffNo},员工姓名:{StaffName},打卡类型:{CheckType}",
staffNo, staffName, isCheckIn ? "上班打卡" : "下班打卡");
if (string.IsNullOrWhiteSpace(staffNo))
{
_logger.LogWarning("员工编号为空,考勤打卡失败");
return false;
}
_attendanceLock.EnterWriteLock();
try
{
if (!_attendanceRecords.TryGetValue(staffNo, out var record))
{
record = new StaffAttendanceRecord
{
StaffNo = staffNo,
StaffName = staffName,
AttendanceDate = DateTime.Today
};
_attendanceRecords[staffNo] = record;
}
if (isCheckIn)
{
record.CheckInTime = DateTime.Now;
record.IsCheckedIn = true;
}
else
{
record.CheckOutTime = DateTime.Now;
record.IsCheckedOut = true;
}
_logger.LogDebug("员工考勤记录已更新,员工编号:{StaffNo}", staffNo);
return true;
}
finally
{
_attendanceLock.ExitWriteLock();
}
}
/// <summary>
/// 处理员工入职登记
/// </summary>
/// <param name="staffNo">员工编号</param>
/// <param name="staffName">员工姓名</param>
/// <param name="department">所属部门</param>
/// <param name="position">职位</param>
public void ProcessStaffOnboarding(string staffNo, string staffName, string department, string position)
{
_logger.LogInformation("人事行政模块处理员工入职,员工编号:{StaffNo},姓名:{StaffName},部门:{Department},职位:{Position}",
staffNo, staffName, department, position);
// 实际项目中此处会调用仓储层持久化员工档案
_logger.LogInformation("员工入职登记完成,员工编号:{StaffNo}", staffNo);
}
/// <summary>
/// 核算员工薪酬
/// </summary>
/// <param name="staffNo">员工编号</param>
/// <param name="baseSalary">基本工资</param>
/// <param name="bonus">奖金</param>
/// <param name="deduction">扣款</param>
/// <returns>实发工资</returns>
public decimal CalculateStaffSalary(string staffNo, decimal baseSalary, decimal bonus, decimal deduction)
{
_logger.LogInformation("人事行政模块核算员工薪酬,员工编号:{StaffNo},基本工资:{BaseSalary},奖金:{Bonus},扣款:{Deduction}",
staffNo, baseSalary, bonus, deduction);
decimal netSalary = baseSalary + bonus - deduction;
_logger.LogInformation("员工薪酬核算完成,员工编号:{StaffNo},实发工资:{NetSalary}", staffNo, netSalary);
return netSalary;
}
/// <summary>
/// 获取今日所有员工考勤记录
/// </summary>
/// <returns>员工考勤记录只读列表</returns>
public IReadOnlyList<StaffAttendanceRecord> GetTodayAttendanceRecords()
{
_attendanceLock.EnterReadLock();
try
{
return _attendanceRecords.Values.ToList().AsReadOnly();
}
finally
{
_attendanceLock.ExitReadLock();
}
}
}
/// <summary>
/// 员工考勤记录实体
/// 记录员工每日的上下班打卡时间与考勤状态
/// </summary>
public class StaffAttendanceRecord
{
/// <summary>
/// 员工编号
/// </summary>
public string StaffNo { get; set; } = string.Empty;
/// <summary>
/// 员工姓名
/// </summary>
public string StaffName { get; set; } = string.Empty;
/// <summary>
/// 考勤日期
/// </summary>
public DateTime AttendanceDate { get; set; }
/// <summary>
/// 上班打卡时间
/// </summary>
public DateTime? CheckInTime { get; set; }
/// <summary>
/// 下班打卡时间
/// </summary>
public DateTime? CheckOutTime { get; set; }
/// <summary>
/// 是否已上班打卡
/// </summary>
public bool IsCheckedIn { get; set; }
/// <summary>
/// 是否已下班打卡
/// </summary>
public bool IsCheckedOut { get; set; }
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/07/04 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : ItOperationService.cs
*/
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Extensions.Logging;
namespace TemplateMethodPattern.Application.Services
{
/// <summary>
/// IT运维应用服务
/// 负责珠宝企业的IT基础设施运维管理,包括系统健康检查、监控告警、
/// 数据备份、权限管理、系统部署等运维事务,保障业务系统稳定运行。
/// </summary>
public class ItOperationService
{
/// <summary>
/// 日志记录器实例
/// </summary>
private readonly ILogger<ItOperationService> _logger;
/// <summary>
/// 系统健康状态记录字典,Key为系统名称,Value为健康状态
/// </summary>
private readonly Dictionary<string, SystemHealthStatus> _systemHealthStatus = new();
/// <summary>
/// 健康状态读写锁,保护并发访问
/// </summary>
private readonly ReaderWriterLockSlim _healthLock = new();
/// <summary>
/// 构造函数,注入日志记录器
/// </summary>
/// <param name="logger">日志记录器实例</param>
public ItOperationService(ILogger<ItOperationService> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
/// <summary>
/// 执行系统健康巡检
/// 检查各业务系统的运行状态,包括CPU、内存、磁盘、数据库连接、服务可用性等
/// </summary>
/// <returns>true表示全部系统健康,false表示存在异常系统</returns>
public async Task<bool> PerformSystemHealthCheckAsync()
{
_logger.LogInformation("IT运维模块开始执行系统健康巡检");
var systemsToCheck = new[]
{
"珠宝业务主系统",
"原料管理系统",
"设计管理系统",
"生产管理系统",
"物流管理系统",
"财务系统",
"营销系统",
"人事系统",
"数据库服务",
"缓存服务"
};
bool allHealthy = true;
foreach (var systemName in systemsToCheck)
{
bool isHealthy = await CheckSingleSystemHealthAsync(systemName);
if (!isHealthy)
{
allHealthy = false;
_logger.LogWarning("系统健康检查发现异常,系统名称:{SystemName}", systemName);
}
}
if (allHealthy)
{
_logger.LogInformation("系统健康巡检完成,全部系统运行正常");
}
else
{
_logger.LogError("系统健康巡检完成,存在异常系统,需要及时处理");
}
return allHealthy;
}
/// <summary>
/// 检查单个系统的健康状态
/// </summary>
/// <param name="systemName">系统名称</param>
/// <returns>true表示系统健康</returns>
private async Task<bool> CheckSingleSystemHealthAsync(string systemName)
{
_logger.LogDebug("检查系统健康状态,系统名称:{SystemName}", systemName);
// 模拟系统健康检查耗时
await Task.Delay(5);
// 模拟检查结果(实际项目中此处会调用真实的健康检查接口)
bool isHealthy = true;
var status = new SystemHealthStatus
{
SystemName = systemName,
IsHealthy = isHealthy,
CpuUsage = new Random().Next(10, 80),
MemoryUsageMB = new Random().Next(512, 8192),
DiskUsagePercent = new Random().Next(20, 70),
LastCheckTime = DateTime.Now
};
_healthLock.EnterWriteLock();
try
{
_systemHealthStatus[systemName] = status;
}
finally
{
_healthLock.ExitWriteLock();
}
return isHealthy;
}
/// <summary>
/// 执行数据备份
/// </summary>
/// <param name="backupType">备份类型,如"全量备份"、"增量备份"</param>
/// <returns>备份文件路径</returns>
public async Task<string> PerformDataBackupAsync(string backupType)
{
_logger.LogInformation("IT运维模块执行数据备份,备份类型:{BackupType}", backupType);
string backupFileName = $"backup_{backupType}_{DateTime.Now:yyyyMMddHHmmss}.bak";
string backupPath = $"/data/backups/{backupFileName}";
// 模拟备份耗时
await Task.Delay(50);
_logger.LogInformation("数据备份完成,备份文件:{BackupPath}", backupPath);
return backupPath;
}
/// <summary>
/// 获取所有系统的健康状态
/// </summary>
/// <returns>系统健康状态只读列表</returns>
public IReadOnlyList<SystemHealthStatus> GetAllSystemHealthStatus()
{
_healthLock.EnterReadLock();
try
{
return _systemHealthStatus.Values.ToList().AsReadOnly();
}
finally
{
_healthLock.ExitReadLock();
}
}
}
/// <summary>
/// 系统健康状态实体
/// 记录单个系统的运行健康指标
/// </summary>
public class SystemHealthStatus
{
/// <summary>
/// 系统名称
/// </summary>
public string SystemName { get; set; } = string.Empty;
/// <summary>
/// 系统是否健康
/// </summary>
public bool IsHealthy { get; set; }
/// <summary>
/// CPU使用率(百分比)
/// </summary>
public int CpuUsage { get; set; }
/// <summary>
/// 内存使用量(MB)
/// </summary>
public int MemoryUsageMB { get; set; }
/// <summary>
/// 磁盘使用率(百分比)
/// </summary>
public int DiskUsagePercent { get; set; }
/// <summary>
/// 最后检查时间
/// </summary>
public DateTime LastCheckTime { get; set; }
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/07/04 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : StaffTrainService.cs
*/
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Extensions.Logging;
namespace TemplateMethodPattern.Application.Services
{
// <summary>
/// 员工培训应用服务
/// 负责珠宝企业的员工培训管理业务,包括培训课程管理、培训计划制定、
/// 培训进度跟踪、培训考核评估、培训档案管理等,提升员工专业技能与服务水平。
/// </summary>
public class StaffTrainService
{
/// <summary>
/// 日志记录器实例
/// </summary>
private readonly ILogger<StaffTrainService> _logger;
/// <summary>
/// 培训课程记录字典,Key为课程编号,Value为课程信息
/// </summary>
private readonly Dictionary<string, TrainingCourse> _trainingCourses = new();
/// <summary>
/// 培训课程读写锁,保护并发访问
/// </summary>
private readonly ReaderWriterLockSlim _courseLock = new();
/// <summary>
/// 构造函数,注入日志记录器
/// </summary>
/// <param name="logger">日志记录器实例</param>
public StaffTrainService(ILogger<StaffTrainService> logger)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
/// <summary>
/// 发起珠宝工艺培训课程
/// 创建新的培训课程并分配课程编号,记录培训基本信息
/// </summary>
/// <param name="courseName">培训课程名称</param>
/// <param name="trainerName">讲师姓名</param>
/// <param name="durationHours">培训时长(小时)</param>
/// <param name="targetDepartment">目标参训部门</param>
/// <returns>培训课程编号</returns>
public string LaunchCraftTraining(string courseName, string trainerName, int durationHours, string targetDepartment)
{
_logger.LogInformation("员工培训模块启动培训课程,课程名称:{CourseName},讲师:{TrainerName},时长:{Duration}小时,目标部门:{Department}",
courseName, trainerName, durationHours, targetDepartment);
// 生成课程编号
string courseNo = $"TC-{DateTime.Now:yyyyMMdd}-{Guid.NewGuid():N6}";
var course = new TrainingCourse
{
CourseNo = courseNo,
CourseName = courseName,
TrainerName = trainerName,
DurationHours = durationHours,
TargetDepartment = targetDepartment,
StartTime = DateTime.Now,
Status = TrainingStatus.InProgress
};
_courseLock.EnterWriteLock();
try
{
_trainingCourses[courseNo] = course;
}
finally
{
_courseLock.ExitWriteLock();
}
_logger.LogInformation("培训课程创建完成,课程编号:{CourseNo}", courseNo);
return courseNo;
}
/// <summary>
/// 记录员工培训进度
/// 更新指定培训课程的参训员工进度与考核结果
/// </summary>
/// <param name="courseNo">培训课程编号</param>
/// <param name="staffNo">员工编号</param>
/// <param name="staffName">员工姓名</param>
/// <param name="progressPercent">培训完成进度(百分比)</param>
/// <param name="examScore">考核分数(可选,未考核为null)</param>
public void RecordTrainingProgress(string courseNo, string staffNo, string staffName, int progressPercent, int? examScore = null)
{
_logger.LogInformation("员工培训模块记录培训进度,课程编号:{CourseNo},员工:{StaffName},进度:{Progress}%,考核分数:{Score}",
courseNo, staffName, progressPercent, examScore?.ToString() ?? "未考核");
_courseLock.EnterWriteLock();
try
{
if (_trainingCourses.TryGetValue(courseNo, out var course))
{
var record = course.TraineeRecords.FirstOrDefault(r => r.StaffNo == staffNo);
if (record == null)
{
record = new TraineeRecord
{
StaffNo = staffNo,
StaffName = staffName
};
course.TraineeRecords.Add(record);
}
record.ProgressPercent = progressPercent;
record.ExamScore = examScore;
record.IsCompleted = progressPercent >= 100;
record.LastUpdateTime = DateTime.Now;
_logger.LogDebug("员工培训进度已更新,课程编号:{CourseNo},员工编号:{StaffNo}", courseNo, staffNo);
}
else
{
_logger.LogWarning("未找到培训课程,课程编号:{CourseNo}", courseNo);
}
}
finally
{
_courseLock.ExitWriteLock();
}
}
/// <summary>
/// 完成培训课程并进行总结评估
/// </summary>
/// <param name="courseNo">培训课程编号</param>
/// <param name="summary">培训总结</param>
/// <returns>true表示课程完成成功</returns>
public bool CompleteTrainingCourse(string courseNo, string summary)
{
_logger.LogInformation("员工培训模块完成培训课程,课程编号:{CourseNo},总结:{Summary}", courseNo, summary);
_courseLock.EnterWriteLock();
try
{
if (_trainingCourses.TryGetValue(courseNo, out var course))
{
course.Status = TrainingStatus.Completed;
course.EndTime = DateTime.Now;
course.Summary = summary;
int completedCount = course.TraineeRecords.Count(r => r.IsCompleted);
_logger.LogInformation("培训课程完成,课程编号:{CourseNo},参训人数:{TotalCount},完成人数:{CompletedCount}",
courseNo, course.TraineeRecords.Count, completedCount);
return true;
}
_logger.LogWarning("未找到培训课程,课程编号:{CourseNo}", courseNo);
return false;
}
finally
{
_courseLock.ExitWriteLock();
}
}
/// <summary>
/// 获取所有培训课程列表
/// </summary>
/// <returns>培训课程只读列表</returns>
public IReadOnlyList<TrainingCourse> GetAllTrainingCourses()
{
_courseLock.EnterReadLock();
try
{
return _trainingCourses.Values.ToList().AsReadOnly();
}
finally
{
_courseLock.ExitReadLock();
}
}
}
/// <summary>
/// 培训课程实体
/// 记录珠宝企业员工培训课程的完整信息
/// </summary>
public class TrainingCourse
{
/// <summary>
/// 培训课程编号
/// </summary>
public string CourseNo { get; set; } = string.Empty;
/// <summary>
/// 培训课程名称
/// </summary>
public string CourseName { get; set; } = string.Empty;
/// <summary>
/// 讲师姓名
/// </summary>
public string TrainerName { get; set; } = string.Empty;
/// <summary>
/// 培训时长(小时)
/// </summary>
public int DurationHours { get; set; }
/// <summary>
/// 目标参训部门
/// </summary>
public string TargetDepartment { get; set; } = string.Empty;
/// <summary>
/// 培训开始时间
/// </summary>
public DateTime StartTime { get; set; }
/// <summary>
/// 培训结束时间
/// </summary>
public DateTime? EndTime { get; set; }
/// <summary>
/// 培训状态
/// </summary>
public TrainingStatus Status { get; set; }
/// <summary>
/// 培训总结
/// </summary>
public string? Summary { get; set; }
/// <summary>
/// 参训员工记录列表
/// </summary>
public List<TraineeRecord> TraineeRecords { get; set; } = new List<TraineeRecord>();
}
/// <summary>
/// 参训员工记录实体
/// 记录单个员工在某培训课程中的学习进度与考核结果
/// </summary>
public class TraineeRecord
{
/// <summary>
/// 员工编号
/// </summary>
public string StaffNo { get; set; } = string.Empty;
/// <summary>
/// 员工姓名
/// </summary>
public string StaffName { get; set; } = string.Empty;
/// <summary>
/// 培训完成进度(百分比)
/// </summary>
public int ProgressPercent { get; set; }
/// <summary>
/// 考核分数
/// </summary>
public int? ExamScore { get; set; }
/// <summary>
/// 是否已完成培训
/// </summary>
public bool IsCompleted { get; set; }
/// <summary>
/// 最后更新时间
/// </summary>
public DateTime LastUpdateTime { get; set; }
}
/// <summary>
/// 培训状态枚举
/// </summary>
public enum TrainingStatus
{
/// <summary>
/// 未开始
/// </summary>
NotStarted = 0,
/// <summary>
/// 进行中
/// </summary>
InProgress = 1,
/// <summary>
/// 已完成
/// </summary>
Completed = 2,
/// <summary>
/// 已取消
/// </summary>
Cancelled = 3
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/07/04 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : JewelleryBusinessTemplate.cs
*/
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using TemplateMethodPattern.Domain.Entities;
using TemplateMethodPattern.Domain.Enums;
using TemplateMethodPattern.Domain.Interfaces;
namespace TemplateMethodPattern.Application.Workflows
{
/// <summary>
/// 珠宝业务流程抽象模板基类【模板方法模式】
/// 定义珠宝业务完整执行流程的骨架结构,固定各业务步骤的执行顺序,
/// 子类通过重写各抽象步骤实现具体业务逻辑,而不改变流程整体结构。
/// 支持异步执行、线程安全与高并发场景,所有步骤均通过ILogger记录运行日志。
/// </summary>
public abstract class JewelleryBusinessTemplate
{
/// <summary>
/// 日志记录器实例,通过DI注入,用于记录流程运行信息
/// </summary>
protected readonly ILogger<JewelleryBusinessTemplate> _logger;
/// <summary>
/// 业务流程仓储接口,用于持久化流程状态
/// </summary>
protected readonly IBusinessFlowRepository _repository;
/// <summary>
/// 异步锁对象,用于保护流程状态变更的线程安全
/// </summary>
private readonly SemaphoreSlim _stateLock = new SemaphoreSlim(1, 1);
/// <summary>
/// 构造函数,注入日志记录器与仓储接口
/// </summary>
/// <param name="logger">日志记录器实例</param>
/// <param name="repository">业务流程仓储实例</param>
protected JewelleryBusinessTemplate(
ILogger<JewelleryBusinessTemplate> logger,
IBusinessFlowRepository repository)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
_repository = repository ?? throw new ArgumentNullException(nameof(repository));
}
/// <summary>
/// 【模板主方法】执行业务完整流程
/// 固定执行顺序:原料核验→设计制图→加工生产→质检→包装→物流→财务记账→营销同步
/// 该方法为模板方法模式的核心,不允许子类重写,确保流程骨架的稳定性。
/// </summary>
/// <param name="context">业务流转上下文对象</param>
/// <param name="cancellationToken">取消令牌,支持调用方取消流程执行</param>
/// <returns>异步任务</returns>
public async Task ExecuteBusinessFlowAsync(JewelleryBusinessContext context, CancellationToken cancellationToken = default)
{
// 启动流程计时,用于性能监控
var stopwatch = Stopwatch.StartNew();
_logger.LogInformation("业务流程开始执行,业务编号:{BusinessNo},客户:{CustomerName}", context.BusinessNo, context.CustomerName);
try
{
// 步骤1:原料采购核验
await ExecuteStepAsync(context, BusinessStage.RawMaterialVerification, "原料采购核验",
StepRawMaterialVerifyAsync, cancellationToken);
// 步骤2:设计制图
await ExecuteStepAsync(context, BusinessStage.DesignDrawing, "设计制图",
StepDesignDrawAsync, cancellationToken);
// 步骤3:加工生产
await ExecuteStepAsync(context, BusinessStage.Manufacturing, "加工生产",
StepManufactureProduceAsync, cancellationToken);
// 步骤4:质量检验
await ExecuteStepAsync(context, BusinessStage.QualityInspection, "质量检验",
StepQualityInspectAsync, cancellationToken);
// 步骤5:成品包装
await ExecuteStepAsync(context, BusinessStage.Packaging, "成品包装",
StepPackageGoodsAsync, cancellationToken);
// 步骤6:物流发货
await ExecuteStepAsync(context, BusinessStage.LogisticsDispatch, "物流发货",
StepLogisticsDispatchAsync, cancellationToken);
// 步骤7:财务记账
await ExecuteStepAsync(context, BusinessStage.FinanceBookkeeping, "财务记账",
StepFinanceBookkeepingAsync, cancellationToken);
// 步骤8:营销推广同步
await ExecuteStepAsync(context, BusinessStage.MarketingPromotion, "营销推广同步",
StepMarketingSyncAsync, cancellationToken);
// 全部步骤完成,标记业务成功
stopwatch.Stop();
await SafeUpdateStateAsync(context, ctx =>
{
ctx.MarkCompleted(true, $"业务流程全部执行完成,总耗时{stopwatch.ElapsedMilliseconds}毫秒");
});
_logger.LogInformation("业务流程执行完成,业务编号:{BusinessNo},总耗时:{Elapsed}毫秒", context.BusinessNo, stopwatch.ElapsedMilliseconds);
}
catch (OperationCanceledException)
{
stopwatch.Stop();
_logger.LogWarning("业务流程被取消,业务编号:{BusinessNo}", context.BusinessNo);
await SafeUpdateStateAsync(context, ctx =>
{
ctx.ErrorMessage = "业务流程被手动取消";
ctx.MarkCompleted(false, "业务流程被取消");
});
throw;
}
catch (Exception ex)
{
stopwatch.Stop();
_logger.LogError(ex, "业务流程发生异常,业务编号:{BusinessNo},异常信息:{Message}", context.BusinessNo, ex.Message);
await SafeUpdateStateAsync(context, ctx =>
{
ctx.ErrorMessage = ex.Message;
ctx.MarkCompleted(false, $"业务流程异常终止:{ex.Message}");
});
throw;
}
finally
{
// 最终持久化流程状态
try
{
await _repository.SaveAsync(context, cancellationToken);
}
catch (Exception saveEx)
{
_logger.LogError(saveEx, "业务流程状态持久化失败,业务编号:{BusinessNo}", context.BusinessNo);
}
}
}
/// <summary>
/// 执行单个流程步骤的通用包装方法
/// 统一处理步骤切换、日志记录、异常处理与状态持久化,确保各步骤执行的一致性
/// </summary>
/// <param name="context">业务上下文对象</param>
/// <param name="stage">当前步骤对应的业务阶段枚举</param>
/// <param name="stepName">步骤名称,用于日志输出</param>
/// <param name="stepAction">步骤实际执行的异步委托</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务</returns>
private async Task ExecuteStepAsync(
JewelleryBusinessContext context,
BusinessStage stage,
string stepName,
Func<JewelleryBusinessContext, CancellationToken, Task> stepAction,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
_logger.LogDebug("开始执行步骤:{StepName},业务编号:{BusinessNo}", stepName, context.BusinessNo);
// 切换到当前阶段
await SafeUpdateStateAsync(context, ctx =>
{
ctx.MoveToStage(stage, $"开始执行{stepName}");
});
// 执行具体步骤
await stepAction(context, cancellationToken);
_logger.LogInformation("步骤执行完成:{StepName},业务编号:{BusinessNo}", stepName, context.BusinessNo);
// 步骤完成后持久化状态
await _repository.SaveAsync(context, cancellationToken);
}
/// <summary>
/// 线程安全地更新业务上下文状态
/// 使用信号量锁保护状态变更操作,确保多线程并发场景下的数据一致性
/// </summary>
/// <param name="context">业务上下文对象</param>
/// <param name="updateAction">状态更新委托</param>
/// <returns>异步任务</returns>
protected async Task SafeUpdateStateAsync(JewelleryBusinessContext context, Action<JewelleryBusinessContext> updateAction)
{
await _stateLock.WaitAsync();
try
{
updateAction(context);
}
finally
{
_stateLock.Release();
}
}
#region 抽象步骤方法 - 子类必须实现
/// <summary>
/// 步骤1:原料采购核验(抽象方法)
/// 子类实现具体的原料核验逻辑,包括调用外部供应商系统核验原料品质与合规性
/// </summary>
/// <param name="context">业务上下文对象</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务</returns>
protected abstract Task StepRawMaterialVerifyAsync(JewelleryBusinessContext context, CancellationToken cancellationToken);
/// <summary>
/// 步骤2:设计制图(抽象方法)
/// 子类实现具体的珠宝设计与图纸生成逻辑
/// </summary>
/// <param name="context">业务上下文对象</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务</returns>
protected abstract Task StepDesignDrawAsync(JewelleryBusinessContext context, CancellationToken cancellationToken);
/// <summary>
/// 步骤3:加工生产(抽象方法)
/// 子类实现具体的珠宝加工生产逻辑
/// </summary>
/// <param name="context">业务上下文对象</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务</returns>
protected abstract Task StepManufactureProduceAsync(JewelleryBusinessContext context, CancellationToken cancellationToken);
/// <summary>
/// 步骤4:质量检验(抽象方法)
/// 子类实现具体的成品质检逻辑,包括品质检测与等级评定
/// </summary>
/// <param name="context">业务上下文对象</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务</returns>
protected abstract Task StepQualityInspectAsync(JewelleryBusinessContext context, CancellationToken cancellationToken);
/// <summary>
/// 步骤5:成品包装(抽象方法)
/// 子类实现具体的成品包装逻辑
/// </summary>
/// <param name="context">业务上下文对象</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务</returns>
protected abstract Task StepPackageGoodsAsync(JewelleryBusinessContext context, CancellationToken cancellationToken);
/// <summary>
/// 步骤6:物流发货(抽象方法)
/// 子类实现具体的物流发货逻辑,包括调用外部物流系统创建运单
/// </summary>
/// <param name="context">业务上下文对象</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务</returns>
protected abstract Task StepLogisticsDispatchAsync(JewelleryBusinessContext context, CancellationToken cancellationToken);
/// <summary>
/// 步骤7:财务记账(抽象方法)
/// 子类实现具体的财务记账逻辑,包括调用外部财务系统生成凭证
/// </summary>
/// <param name="context">业务上下文对象</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务</returns>
protected abstract Task StepFinanceBookkeepingAsync(JewelleryBusinessContext context, CancellationToken cancellationToken);
/// <summary>
/// 步骤8:营销推广同步(抽象方法)
/// 子类实现具体的营销数据同步逻辑
/// </summary>
/// <param name="context">业务上下文对象</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务</returns>
protected abstract Task StepMarketingSyncAsync(JewelleryBusinessContext context, CancellationToken cancellationToken);
#endregion
}
}
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/07/04 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : StandardJewelleryOrderFlow.cs
*/
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Text;
using TemplateMethodPattern.Domain.Entities;
using TemplateMethodPattern.Domain.Enums;
using TemplateMethodPattern.Domain.Interfaces;
using TemplateMethodPattern.Domain.ValueObjects;
namespace TemplateMethodPattern.Application.Workflows
{
/// <summary>
/// 标准珠宝订单业务流程实现类
/// 继承珠宝业务流程抽象模板基类,实现全部8个业务步骤的具体逻辑,
/// 涵盖原料采购核验、设计制图、加工生产、质检、包装、物流、财务记账、营销推广全流程。
/// 通过适配器接口与外部异构系统交互,实现业务逻辑与外部系统的解耦。
/// </summary>
public class StandardJewelleryOrderFlow : JewelleryBusinessTemplate
{
/// <summary>
/// 外部原料供应商系统适配器
/// </summary>
private readonly IExternalSupplierAdapter _supplierAdapter;
/// <summary>
/// 外部物流系统适配器
/// </summary>
private readonly IExternalLogisticsAdapter _logisticsAdapter;
/// <summary>
/// 外部财务系统适配器
/// </summary>
private readonly IExternalFinanceAdapter _financeAdapter;
/// <summary>
/// 当前流程实例的日志记录器
/// </summary>
private readonly ILogger<StandardJewelleryOrderFlow> _flowLogger;
/// <summary>
/// 构造函数,通过DI注入所有依赖
/// </summary>
/// <param name="logger">当前流程类的日志记录器</param>
/// <param name="baseLogger">基类所需的日志记录器</param>
/// <param name="repository">业务流程仓储实例</param>
/// <param name="supplierAdapter">原料供应商适配器</param>
/// <param name="logisticsAdapter">物流系统适配器</param>
/// <param name="financeAdapter">财务系统适配器</param>
public StandardJewelleryOrderFlow(
ILogger<StandardJewelleryOrderFlow> logger,
ILogger<JewelleryBusinessTemplate> baseLogger,
IBusinessFlowRepository repository,
IExternalSupplierAdapter supplierAdapter,
IExternalLogisticsAdapter logisticsAdapter,
IExternalFinanceAdapter financeAdapter)
: base(baseLogger, repository)
{
_flowLogger = logger ?? throw new ArgumentNullException(nameof(logger));
_supplierAdapter = supplierAdapter ?? throw new ArgumentNullException(nameof(supplierAdapter));
_logisticsAdapter = logisticsAdapter ?? throw new ArgumentNullException(nameof(logisticsAdapter));
_financeAdapter = financeAdapter ?? throw new ArgumentNullException(nameof(financeAdapter));
}
/// <summary>
/// 步骤1:原料采购核验
/// 调用外部供应商适配器核验原料的合法性与品质,获取原料详细信息并更新上下文
/// </summary>
/// <param name="context">业务上下文对象</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务</returns>
protected override async Task StepRawMaterialVerifyAsync(JewelleryBusinessContext context, CancellationToken cancellationToken)
{
_flowLogger.LogDebug("开始原料采购核验,原料编码:{RawMaterialCode}", context.RawMaterialCode);
if (string.IsNullOrWhiteSpace(context.RawMaterialCode))
{
throw new InvalidOperationException("原料物料编码不能为空,无法执行核验");
}
// 调用外部供应商系统异步核验原料
bool verifyResult = await _supplierAdapter.CheckMaterialValidAsync(context.RawMaterialCode, cancellationToken);
// 获取原料详细信息
RawMaterial? material = _supplierAdapter.GetMaterialInfo(context.RawMaterialCode);
await SafeUpdateStateAsync(context, ctx =>
{
ctx.RawMaterialVerifyPassed = verifyResult;
ctx.RawMaterial = material;
if (material != null)
{
material.Verify(verifyResult, verifyResult ? "核验通过" : "核验不通过");
}
});
if (verifyResult)
{
_flowLogger.LogInformation("原料核验通过,原料编码:{RawMaterialCode},原料名称:{MaterialName}",
context.RawMaterialCode, material?.MaterialName ?? "未知");
}
else
{
_flowLogger.LogWarning("原料核验不通过,原料编码:{RawMaterialCode},后续生产步骤将被跳过", context.RawMaterialCode);
}
}
/// <summary>
/// 步骤2:设计制图
/// 根据业务需求生成珠宝设计图纸,分配图纸编号并记录设计信息
/// </summary>
/// <param name="context">业务上下文对象</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务</returns>
protected override async Task StepDesignDrawAsync(JewelleryBusinessContext context, CancellationToken cancellationToken)
{
_flowLogger.LogDebug("开始珠宝设计制图,业务编号:{BusinessNo}", context.BusinessNo);
// 生成设计图纸编号
string drawingNo = $"DR-{DateTime.Now:yyyyMMddHHmmss}-{context.BusinessNo.GetHashCode():X8}";
var drawing = new DesignDrawing
{
DrawingId = Guid.NewGuid(),
DrawingNo = drawingNo,
DrawingName = $"珠宝定制设计图-{context.BusinessNo}",
DesignStyle = "经典高端定制",
DesignerName = "高级珠宝设计师",
Version = "V1.0",
FilePath = $"/designs/{drawingNo}.cad",
Description = $"根据客户需求定制设计,原料编码:{context.RawMaterialCode}",
IsApproved = true,
ApproverName = "设计总监",
CreateTime = DateTime.Now,
UpdateTime = DateTime.Now
};
await SafeUpdateStateAsync(context, ctx =>
{
ctx.DesignDrawingNo = drawingNo;
ctx.DesignDrawing = drawing;
});
_flowLogger.LogInformation("设计制图完成,图纸编号:{DrawingNo},设计师:{DesignerName}", drawingNo, drawing.DesignerName);
// 模拟设计耗时
await Task.Delay(10, cancellationToken);
}
/// <summary>
/// 步骤3:加工生产
/// 依据设计图纸进行珠宝加工生产,仅在原料核验通过时执行
/// </summary>
/// <param name="context">业务上下文对象</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务</returns>
protected override async Task StepManufactureProduceAsync(JewelleryBusinessContext context, CancellationToken cancellationToken)
{
_flowLogger.LogDebug("开始珠宝加工生产,业务编号:{BusinessNo}", context.BusinessNo);
if (!context.RawMaterialVerifyPassed)
{
_flowLogger.LogWarning("原料未核验通过,跳过加工生产步骤,业务编号:{BusinessNo}", context.BusinessNo);
return;
}
// 生成产品编号
string productCode = $"PD-{DateTime.Now:yyyyMMddHHmmss}-{Guid.NewGuid():N6}";
var product = new Product
{
ProductId = Guid.NewGuid(),
ProductCode = productCode,
ProductName = $"定制珠宝成品-{context.BusinessNo}",
DrawingNo = context.DesignDrawingNo,
MaterialCode = context.RawMaterialCode,
Category = "定制首饰",
FinishedWeight = context.RawMaterial?.Weight * 0.95m ?? 0m, // 假设加工损耗5%
QualityLevel = QualityLevel.NotEvaluated,
IsQualityPassed = false,
IsPackaged = false,
FactoryPrice = context.TotalAmount.Amount * 0.6m,
RetailPrice = context.TotalAmount.Amount,
ManufactureTime = DateTime.Now
};
await SafeUpdateStateAsync(context, ctx =>
{
ctx.ProductCode = productCode;
ctx.Product = product;
ctx.ProduceFinished = true;
});
_flowLogger.LogInformation("珠宝加工生产完成,产品编号:{ProductCode},成品重量:{Weight}克",
productCode, product.FinishedWeight);
// 模拟加工耗时
await Task.Delay(20, cancellationToken);
}
/// <summary>
/// 步骤4:质量检验
/// 对成品珠宝进行品质检测与等级评定,仅在生产完成时执行
/// </summary>
/// <param name="context">业务上下文对象</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务</returns>
protected override async Task StepQualityInspectAsync(JewelleryBusinessContext context, CancellationToken cancellationToken)
{
_flowLogger.LogDebug("开始成品质检,业务编号:{BusinessNo}", context.BusinessNo);
if (!context.ProduceFinished || context.Product == null)
{
_flowLogger.LogWarning("产品未生产完成,跳过质检步骤,业务编号:{BusinessNo}", context.BusinessNo);
return;
}
// 执行质检,生成质检报告编号
string reportNo = $"QR-{DateTime.Now:yyyyMMdd}-{Guid.NewGuid():N8}";
bool isPassed = true; // 模拟质检通过
QualityLevel level = QualityLevel.GradeA;
context.Product.PerformQualityCheck(isPassed, level, reportNo);
await SafeUpdateStateAsync(context, ctx =>
{
ctx.QualityCheckPassed = isPassed;
});
if (isPassed)
{
_flowLogger.LogInformation("成品质检合格,质量等级:{Level},质检报告:{ReportNo}", level, reportNo);
}
else
{
_flowLogger.LogError("成品质检不合格,质检报告:{ReportNo},后续包装步骤将被跳过", reportNo);
}
await Task.Delay(10, cancellationToken);
}
/// <summary>
/// 步骤5:成品包装
/// 对质检合格的珠宝成品进行品牌包装,仅在质检合格时执行
/// </summary>
/// <param name="context">业务上下文对象</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务</returns>
protected override async Task StepPackageGoodsAsync(JewelleryBusinessContext context, CancellationToken cancellationToken)
{
_flowLogger.LogDebug("开始成品包装,业务编号:{BusinessNo}", context.BusinessNo);
if (!context.QualityCheckPassed || context.Product == null)
{
_flowLogger.LogWarning("质检不合格,跳过包装步骤,业务编号:{BusinessNo}", context.BusinessNo);
return;
}
string packageSpec = "高端实木礼盒+珠宝鉴定证书+擦金布+品牌手提袋+防伪封签";
context.Product.PerformPackaging(packageSpec);
await SafeUpdateStateAsync(context, ctx =>
{
ctx.PackFinished = true;
});
_flowLogger.LogInformation("成品包装完成,包装规格:{PackageSpec}", packageSpec);
await Task.Delay(10, cancellationToken);
}
/// <summary>
/// 步骤6:物流发货
/// 调用外部物流适配器创建运单,安排珠宝成品发货,仅在包装完成时执行
/// </summary>
/// <param name="context">业务上下文对象</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务</returns>
protected override async Task StepLogisticsDispatchAsync(JewelleryBusinessContext context, CancellationToken cancellationToken)
{
_flowLogger.LogDebug("开始物流发货,业务编号:{BusinessNo}", context.BusinessNo);
if (!context.PackFinished)
{
_flowLogger.LogWarning("商品未包装完成,跳过物流发货步骤,业务编号:{BusinessNo}", context.BusinessNo);
return;
}
if (context.ShippingAddress == null)
{
_flowLogger.LogWarning("收货地址为空,跳过物流发货步骤,业务编号:{BusinessNo}", context.BusinessNo);
return;
}
// 构造发货地址(品牌仓库地址)
var senderAddress = new Address(
contactName: "珠宝品牌仓储中心",
phoneNumber: "400-888-8888",
country: "中国",
province: "广东省",
city: "深圳市",
district: "罗湖区",
detailAddress: "水贝珠宝产业园A座18层",
postalCode: "518000");
// 调用外部物流系统异步创建运单,保价金额等于业务总金额
var logisticsOrder = await _logisticsAdapter.CreateShipmentAsync(
businessNo: context.BusinessNo,
senderAddress: senderAddress,
receiverAddress: context.ShippingAddress,
insuranceAmount: context.TotalAmount.Amount,
cancellationToken: cancellationToken);
await SafeUpdateStateAsync(context, ctx =>
{
ctx.LogisticsOrder = logisticsOrder;
ctx.LogisticsNo = logisticsOrder.TrackingNo;
ctx.LogisticsDispatched = true;
});
_flowLogger.LogInformation("物流发货成功,运单号:{TrackingNo},物流公司:{CarrierName},保价金额:{InsuranceAmount}",
logisticsOrder.TrackingNo, logisticsOrder.CarrierName, logisticsOrder.InsuranceAmount);
}
/// <summary>
/// 步骤7:财务记账
/// 调用外部财务适配器提交业务记账凭证,生成财务凭证编号
/// </summary>
/// <param name="context">业务上下文对象</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务</returns>
protected override async Task StepFinanceBookkeepingAsync(JewelleryBusinessContext context, CancellationToken cancellationToken)
{
_flowLogger.LogDebug("开始财务记账,业务编号:{BusinessNo},金额:{Amount}",
context.BusinessNo, context.TotalAmount);
// 调用外部财务系统异步提交记账凭证
string voucherNo = await _financeAdapter.SubmitBusinessAccountAsync(
businessNo: context.BusinessNo,
amount: context.TotalAmount.Amount,
currencyCode: context.TotalAmount.CurrencyCode,
cancellationToken: cancellationToken);
await SafeUpdateStateAsync(context, ctx =>
{
ctx.FinanceBookkeepingDone = true;
ctx.FinanceVoucherNo = voucherNo;
});
_flowLogger.LogInformation("财务记账完成,凭证编号:{VoucherNo},记账金额:{Amount}",
voucherNo, context.TotalAmount);
}
/// <summary>
/// 步骤8:营销推广同步
/// 将业务数据同步至营销推广系统,用于客户运营与营销活动
/// </summary>
/// <param name="context">业务上下文对象</param>
/// <param name="cancellationToken">取消令牌</param>
/// <returns>异步任务</returns>
protected override async Task StepMarketingSyncAsync(JewelleryBusinessContext context, CancellationToken cancellationToken)
{
_flowLogger.LogDebug("开始营销推广数据同步,业务编号:{BusinessNo}", context.BusinessNo);
// 生成营销活动编号,用于后续客户跟进与推广
string campaignNo = $"MK-{DateTime.Now:yyyyMM}-{context.BusinessNo.GetHashCode():X6}";
await SafeUpdateStateAsync(context, ctx =>
{
ctx.MarketingSynced = true;
ctx.MarketingCampaignNo = campaignNo;
});
_flowLogger.LogInformation("营销推广数据同步完成,营销活动编号:{CampaignNo},客户:{CustomerName}",
campaignNo, context.CustomerName);
await Task.Delay(10, cancellationToken);
}
}
}
调用:
cs
/*
# encoding: utf-8
# 版权所有 2026 ©涂聚文有限公司™ ®
# 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
# 描述:模板方法模式 TemplateMethod 行为模式 Behavioral Patterns
# Author : geovindu,Geovin Du 涂聚文.
# IDE : vs2026 c# .net 10
# os : windows 10
# database : mysql 9.0 sql server 2019, postgreSQL 17.0 Oracle 21c Neo4j
# Datetime : 2026/07/04 22:16
# User : geovindu
# Product : Visual Studio 2026
# Project : CSharpDesignPattern
# File : TemplateMethodBll.cs
*/
using System;
using System.Collections.Generic;
using System.Text;
using TemplateMethodPattern.Application.DTOs;
using TemplateMethodPattern.Application.Scheduler;
using TemplateMethodPattern.Application.Services;
using TemplateMethodPattern.Application.Workflows;
using TemplateMethodPattern.Domain.Interfaces;
using TemplateMethodPattern.Infrastructure.Adapters;
using TemplateMethodPattern.Infrastructure.Logging;
using TemplateMethodPattern.Infrastructure.Repositories;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace BLL
{
/// <summary>
///
/// </summary>
public class TemplateMethodBll
{
public async Task Demo()
{
Console.WriteLine("========================================");
Console.WriteLine(" 珠宝企业全业务流程系统启动中...");
Console.WriteLine(" 设计模式:模板方法模式 + 适配器模式");
Console.WriteLine(" 架构:企业级严格分层架构");
Console.WriteLine("========================================");
Console.WriteLine();
// 构建通用Host宿主,配置DI容器与日志
var host = Host.CreateDefaultBuilder()
.ConfigureServices((hostContext, services) =>
{
// 注册基础设施层服务
ConfigureInfrastructureServices(services);
// 注册应用层服务
ConfigureApplicationServices(services);
})
.ConfigureLogging((hostCtx, loggingBuilder) =>
{
// 配置企业级统一日志(控制台+文件,格式一致)
loggingBuilder.ConfigureEnterpriseLogging();
})
.Build();
// 获取入口日志记录器
var logger = host.Services.GetRequiredService<ILogger<TemplateMethodBll>>();
logger.LogInformation("珠宝企业业务系统启动成功,所有服务已初始化");
try
{
// 从DI容器获取所需服务
var businessFlowService = host.Services.GetRequiredService<BusinessFlowService>();
var scheduler = host.Services.GetRequiredService<BusinessTaskScheduler>();
var hrService = host.Services.GetRequiredService<HumanResourceService>();
var itService = host.Services.GetRequiredService<ItOperationService>();
var trainService = host.Services.GetRequiredService<StaffTrainService>();
// ========== 演示1:执行单条珠宝业务全流程 ==========
logger.LogInformation("========== 演示1:执行单条珠宝业务全流程 ==========");
await ExecuteSingleBusinessFlowAsync(businessFlowService, logger);
// ========== 演示2:批量并发调度业务任务 ==========
logger.LogInformation("========== 演示2:批量并发调度业务任务 ==========");
await ExecuteBatchConcurrentTasksAsync(scheduler, logger);
// ========== 演示3:人事行政管理业务 ==========
logger.LogInformation("========== 演示3:人事行政管理业务 ==========");
ExecuteHumanResourceBusiness(hrService, logger);
// ========== 演示4:IT运维管理业务 ==========
logger.LogInformation("========== 演示4:IT运维管理业务 ==========");
await ExecuteItOperationBusinessAsync(itService, logger);
// ========== 演示5:员工培训管理业务 ==========
logger.LogInformation("========== 演示5:员工培训管理业务 ==========");
ExecuteStaffTrainingBusiness(trainService, logger);
// ========== 输出调度器统计信息 ==========
logger.LogInformation("========== 调度器运行统计 ==========");
var statistics = scheduler.GetStatistics();
logger.LogInformation("总任务数:{TotalTasks},成功:{SuccessTasks},失败:{FailedTasks},成功率:{SuccessRate}%",
statistics.TotalTasks, statistics.SuccessTasks, statistics.FailedTasks, statistics.GetSuccessRate());
logger.LogInformation("最大并发数:{MaxConcurrency},当前并发数:{CurrentConcurrency}",
statistics.MaxConcurrency, statistics.CurrentConcurrency);
logger.LogInformation("所有业务演示执行完毕,系统运行正常");
}
catch (Exception ex)
{
logger.LogError(ex, "系统运行过程中发生未处理异常:{Message}", ex.Message);
}
finally
{
// 优雅关闭Host
await host.StopAsync();
logger.LogInformation("珠宝企业业务系统已关闭");
}
Console.WriteLine();
Console.WriteLine("========================================");
Console.WriteLine(" 系统运行完毕,日志文件已保存至 Logs/ 目录");
Console.WriteLine(" 按日期分文件夹,按日志等级分文件");
Console.WriteLine("========================================");
}
/// <summary>
/// 配置基础设施层服务注册
/// 注册外部系统适配器、仓储实现等基础设施服务
/// </summary>
/// <param name="services">依赖注入服务集合</param>
private static void ConfigureInfrastructureServices(IServiceCollection services)
{
// 注册外部系统适配器(适配器模式)
services.AddSingleton<IExternalSupplierAdapter, ThirdPartySupplierAdapter>();
services.AddSingleton<IExternalLogisticsAdapter, ThirdPartyLogisticsAdapter>();
services.AddSingleton<IExternalFinanceAdapter, ThirdPartyFinanceAdapter>();
// 注册业务流程仓储(内存版实现)
services.AddSingleton<IBusinessFlowRepository, InMemoryBusinessFlowRepository>();
}
/// <summary>
/// 配置应用层服务注册
/// 注册工作流、应用服务、调度器等应用层服务
/// </summary>
/// <param name="services">依赖注入服务集合</param>
private static void ConfigureApplicationServices(IServiceCollection services)
{
// 注册模板方法工作流(无状态服务,注册为单例)
services.AddSingleton<StandardJewelleryOrderFlow>();
// 注册应用服务(无状态服务,注册为单例)
services.AddSingleton<BusinessFlowService>();
services.AddSingleton<HumanResourceService>();
services.AddSingleton<ItOperationService>();
services.AddSingleton<StaffTrainService>();
// 注册业务任务调度器(配置最大并发数为8)
services.AddSingleton<BusinessTaskScheduler>(sp =>
{
var logger = sp.GetRequiredService<ILogger<BusinessTaskScheduler>>();
var flowService = sp.GetRequiredService<BusinessFlowService>();
return new BusinessTaskScheduler(logger, flowService, maxConcurrency: 8);
});
}
/// <summary>
/// 演示1:执行单条珠宝业务全流程
/// 构建业务请求,调用业务流程服务执行完整的8步业务流程
/// </summary>
/// <param name="businessFlowService">业务流程应用服务</param>
/// <param name="logger">日志记录器</param>
/// <returns>异步任务</returns>
private static async Task ExecuteSingleBusinessFlowAsync(BusinessFlowService businessFlowService, ILogger logger)
{
var request = new BusinessFlowRequest
{
BusinessNo = "ORD-20260901-0001",
CustomerName = "张三",
CustomerPhone = "13800138001",
RawMaterialCode = "RAW-00101",
TotalAmount = 52600.00m,
CurrencyCode = "CNY",
Province = "广东省",
City = "深圳市",
District = "南山区",
DetailAddress = "科技园南区T3栋1801",
PostalCode = "518000",
Remark = "高端定制钻戒,客户要求加急处理"
};
logger.LogInformation("开始执行单条业务流程,业务编号:{BusinessNo}", request.BusinessNo);
var result = await businessFlowService.ExecuteFlowAsync(request);
logger.LogInformation("单条业务流程执行结果:");
logger.LogInformation(" 业务编号:{BusinessNo}", result.BusinessNo);
logger.LogInformation(" 执行状态:{Status}", result.IsSuccess ? "成功" : "失败");
logger.LogInformation(" 当前阶段:{Stage}", result.CurrentStage);
logger.LogInformation(" 设计图纸编号:{DrawingNo}", result.DesignDrawingNo);
logger.LogInformation(" 产品编号:{ProductCode}", result.ProductCode);
logger.LogInformation(" 物流运单号:{LogisticsNo}", result.LogisticsNo);
logger.LogInformation(" 财务凭证号:{VoucherNo}", result.FinanceVoucherNo);
logger.LogInformation(" 业务金额:{Amount}", result.TotalAmountDisplay);
logger.LogInformation(" 执行耗时:{Elapsed}毫秒", result.ElapsedMilliseconds);
if (!string.IsNullOrEmpty(result.ErrorMessage))
{
logger.LogError(" 错误信息:{Error}", result.ErrorMessage);
}
// 输出各阶段执行历史
logger.LogInformation(" 流程执行历史:");
foreach (var history in result.StageHistories)
{
logger.LogInformation(" [{Time}] {Stage} - {Message}",
history.ExecuteTime.ToString("HH:mm:ss.fff"), history.StageName, history.Message);
}
}
/// <summary>
/// 演示2:批量并发调度业务任务
/// 创建多个业务请求,使用调度器进行高并发批量执行
/// </summary>
/// <param name="scheduler">业务任务调度器</param>
/// <param name="logger">日志记录器</param>
/// <returns>异步任务</returns>
private static async Task ExecuteBatchConcurrentTasksAsync(BusinessTaskScheduler scheduler, ILogger logger)
{
const int taskCount = 8;
logger.LogInformation("开始批量并发调度,任务数量:{TaskCount}", taskCount);
// 构建批量业务请求
var requests = new List<BusinessFlowRequest>();
for (int i = 1; i <= taskCount; i++)
{
requests.Add(new BusinessFlowRequest
{
BusinessNo = $"ORD-20260901-{i:D4}",
CustomerName = $"客户{i}",
CustomerPhone = $"138001380{i:D2}",
RawMaterialCode = i % 3 == 0 ? "RAW-00201" : "RAW-00101",
TotalAmount = 20000 + i * 5000,
CurrencyCode = "CNY",
Province = "广东省",
City = "深圳市",
District = "福田区",
DetailAddress = $"福华路{i}号",
PostalCode = "518000",
Remark = $"批量并发任务第{i}号"
});
}
// 使用调度器并发执行批量任务
var results = await scheduler.DispatchBatchTasksAsync(requests);
int successCount = results.Count(r => r.IsSuccess);
int failCount = results.Count - successCount;
logger.LogInformation("批量并发调度完成,总任务数:{Total},成功:{Success},失败:{Fail}",
results.Count, successCount, failCount);
// 输出每个任务的简要结果
foreach (var result in results)
{
logger.LogInformation(" 业务编号:{BusinessNo},状态:{Status},物流单号:{LogisticsNo},凭证号:{VoucherNo}",
result.BusinessNo,
result.IsSuccess ? "成功" : "失败",
result.LogisticsNo,
result.FinanceVoucherNo);
}
}
/// <summary>
/// 演示3:人事行政管理业务
/// 演示员工考勤、入职登记、薪酬核算等人事行政业务
/// </summary>
/// <param name="hrService">人事行政服务</param>
/// <param name="logger">日志记录器</param>
private static void ExecuteHumanResourceBusiness(HumanResourceService hrService, ILogger logger)
{
logger.LogInformation("开始执行人事行政管理业务演示");
// 员工入职登记
hrService.ProcessStaffOnboarding("EMP-001", "李四", "设计部", "高级珠宝设计师");
hrService.ProcessStaffOnboarding("EMP-002", "王五", "生产部", "高级工艺师");
hrService.ProcessStaffOnboarding("EMP-003", "赵六", "销售部", "珠宝顾问");
// 员工上班打卡
hrService.ProcessStaffAttendance("EMP-001", "李四", isCheckIn: true);
hrService.ProcessStaffAttendance("EMP-002", "王五", isCheckIn: true);
hrService.ProcessStaffAttendance("EMP-003", "赵六", isCheckIn: true);
// 员工下班打卡
hrService.ProcessStaffAttendance("EMP-001", "李四", isCheckIn: false);
hrService.ProcessStaffAttendance("EMP-002", "王五", isCheckIn: false);
// 薪酬核算
decimal salary1 = hrService.CalculateStaffSalary("EMP-001", baseSalary: 15000, bonus: 3000, deduction: 500);
decimal salary2 = hrService.CalculateStaffSalary("EMP-002", baseSalary: 12000, bonus: 2500, deduction: 300);
logger.LogInformation("人事行政管理业务演示完成,员工李四实发工资:{Salary1},员工王五实发工资:{Salary2}", salary1, salary2);
// 输出今日考勤记录
var attendanceRecords = hrService.GetTodayAttendanceRecords();
logger.LogInformation("今日考勤记录总数:{Count}", attendanceRecords.Count);
foreach (var record in attendanceRecords)
{
logger.LogInformation(" 员工:{Name}({No}),上班:{CheckIn},下班:{CheckOut}",
record.StaffName, record.StaffNo,
record.CheckInTime?.ToString("HH:mm:ss") ?? "未打卡",
record.CheckOutTime?.ToString("HH:mm:ss") ?? "未打卡");
}
}
/// <summary>
/// 演示4:IT运维管理业务
/// 演示系统健康巡检、数据备份等IT运维业务
/// </summary>
/// <param name="itService">IT运维服务</param>
/// <param name="logger">日志记录器</param>
/// <returns>异步任务</returns>
private static async Task ExecuteItOperationBusinessAsync(ItOperationService itService, ILogger logger)
{
logger.LogInformation("开始执行IT运维管理业务演示");
// 执行系统健康巡检
bool isHealthy = await itService.PerformSystemHealthCheckAsync();
logger.LogInformation("系统健康巡检结果:{Result}", isHealthy ? "全部正常" : "存在异常");
// 执行数据备份
string backupPath = await itService.PerformDataBackupAsync("全量备份");
logger.LogInformation("数据备份完成,备份文件路径:{BackupPath}", backupPath);
// 输出各系统健康状态
var healthStatuses = itService.GetAllSystemHealthStatus();
logger.LogInformation("已监控系统数量:{Count}", healthStatuses.Count);
foreach (var status in healthStatuses)
{
logger.LogInformation(" 系统:{SystemName},状态:{Status},CPU:{Cpu}%,内存:{Memory}MB,磁盘:{Disk}%",
status.SystemName,
status.IsHealthy ? "正常" : "异常",
status.CpuUsage,
status.MemoryUsageMB,
status.DiskUsagePercent);
}
}
/// <summary>
/// 演示5:员工培训管理业务
/// 演示培训课程创建、培训进度记录、培训完成评估等员工培训业务
/// </summary>
/// <param name="trainService">员工培训服务</param>
/// <param name="logger">日志记录器</param>
private static void ExecuteStaffTrainingBusiness(StaffTrainService trainService, ILogger logger)
{
logger.LogInformation("开始执行员工培训管理业务演示");
// 发起珠宝工艺培训课程
string courseNo = trainService.LaunchCraftTraining(
courseName: "高级珠宝镶嵌工艺培训",
trainerName: "国家级工艺大师-陈师傅",
durationHours: 16,
targetDepartment: "生产部、设计部");
// 记录员工培训进度
trainService.RecordTrainingProgress(courseNo, "EMP-001", "李四", progressPercent: 100, examScore: 95);
trainService.RecordTrainingProgress(courseNo, "EMP-002", "王五", progressPercent: 85, examScore: null);
trainService.RecordTrainingProgress(courseNo, "EMP-003", "赵六", progressPercent: 60, examScore: null);
// 完成培训课程
bool isCompleted = trainService.CompleteTrainingCourse(courseNo, "本次培训覆盖高级珠宝镶嵌核心工艺,参训员工考核通过率良好");
logger.LogInformation("员工培训管理业务演示完成,课程编号:{CourseNo},完成状态:{Status}", courseNo, isCompleted ? "已完成" : "未完成");
// 输出培训课程信息
var courses = trainService.GetAllTrainingCourses();
foreach (var course in courses)
{
logger.LogInformation(" 培训课程:{CourseName}({CourseNo})");
logger.LogInformation(" 讲师:{Trainer},时长:{Duration}小时,目标部门:{Department}");
logger.LogInformation(" 状态:{Status},参训人数:{TraineeCount}");
foreach (var trainee in course.TraineeRecords)
{
logger.LogInformation(" 员工:{Name},进度:{Progress}%,考核分数:{Score},是否完成:{Completed}",
trainee.StaffName, trainee.ProgressPercent,
trainee.ExamScore?.ToString() ?? "未考核",
trainee.IsCompleted ? "是" : "否");
}
}
}
}
}
输出:
