资讯动态

C# WinForm医院挂号系统:生产级三层架构实战

发布时间:2026/10/20 9:27:45 来源:尧图企业网站定制
简介这是一套基于C# WinForm开发的医院挂号管理系统源码面向.NET初学者与课程设计学习者解决中小型医疗机构挂号流程数字化、用户权限分级管理及基础数据可视化等实际问题。资源包共185个文件含64个核心C#业务逻辑与界面代码文件、42张医生照片JPG及UI资源、19个本地化资源文件resources/resx、3个SQL脚本用于数据库初始化与存储过程部署另有可执行程序exe、帮助文档chm/htm、项目配置config及完整VS解决方案sln/vdproj总大小10.03MB。已有1265人学习下载。读者可直接编译运行获得一个具备用户/科室/医生管理、门急诊挂号、实时查询、MD5密码加密、挂号单打印、图文统计报表及医生照片上传等功能的完整C/S架构系统代码严格遵循MVC分层设计数据访问层调用存储过程业务逻辑清晰是理解WinForm企业级分层开发与医疗信息化基础建模的优质参考案例。1. 一个 WinForm 医院挂号管理系统不是 Demo是能进真实科室跑通的生产级骨架上周帮某三甲医院信息科同事紧急修复一个老系统——挂号窗口卡在“正在连接数据库”超过两分钟。他们翻出十年前外包公司留下的 C# WinForm 源码包发现连基础的患者号重复校验都没做SQL 查询直接拼接字符串登录界面居然还硬编码了 admin/123。这不是教学 Demo而是真正在分诊台、收费处、医生工作站里跑过五年的业务系统。这个「使用C#语言winform做的医院挂号管理系统」项目就是这类场景的典型落地产物它不追求炫酷 UI 或微服务架构但完整覆盖了患者建档、科室/医生排班、预约挂号、现场挂号、退号、挂号单打印、基础统计等核心链路技术栈干净.NET Framework 4.7.2 SQL Server LocalDB Entity Framework 6结构清晰三层架构简单工厂所有窗体、数据访问、业务逻辑全部可调试、可打断点、可替换。如果你正要接手一个类似的老系统维护任务或需要快速搭建一个可交付的中小型医疗挂号原型这份源码不是玩具是能立刻拆开、改参数、连真实数据库、当天就让护士站跑起来的工程包。2. 项目结构与技术选型为什么 WinForm 还在一线医院活得好好的这个项目没有用 WPF、Blazor 或 Electron坚持 WinForm不是守旧而是对部署环境、运维成本和终端兼容性的务实选择。某高校附属医院信息科反馈全院 200 台挂号终端操作系统从 Windows 7 到 Windows 11 都有其中 30% 是老旧工控机显卡驱动不全、.NET Core 运行时安装失败率高而 WinForm 应用双击即用、无依赖安装、资源占用低护士换班时重启电脑后点开 exe 就能干活——这才是真实世界里的“高可用”。2.1 目录结构三层分离 窗体驱动一眼看懂数据流向项目解压后共 12 个文件夹核心结构如下已剔除 bin/obj 等生成目录HospitalRegistrationSystem/ ├── HospitalRegistrationSystem.sln # 解决方案入口 ├── HospitalRegistrationSystem/ # 主项目WinForm UI 层 │ ├── FormMain.cs # 主窗体左侧导航树 右侧 TabControl │ ├── Forms/ # 所有业务窗体 │ │ ├── FrmPatientRegister.cs # 患者建档窗体 │ │ ├── FrmRegistration.cs # 挂号主界面含科室/医生筛选、号源列表、挂号操作 │ │ ├── FrmScheduleManage.cs # 排班管理管理员专用 │ │ └── ... │ ├── Controls/ # 自定义控件如带搜索的 ComboBox、挂号状态灯 │ └── Program.cs # 入口含启动时数据库连接检测 ├── HospitalRegistrationSystem.BLL/ # 业务逻辑层Business Logic Layer │ ├── PatientService.cs # 患者相关业务建档、查询、去重 │ ├── RegistrationService.cs # 挂号核心预约、现场挂、退号、号源锁定 │ └── ScheduleService.cs # 排班业务按日/周生成、医生排班冲突检测 ├── HospitalRegistrationSystem.DAL/ # 数据访问层Data Access Layer │ ├── DbContextFactory.cs # EF6 上下文工厂支持多连接字符串切换 │ ├── Entities/ # 实体类Patient, Doctor, Department, Schedule, RegistrationRecord │ └── Repositories/ # 仓储实现IRepositoryT GenericRepositoryT ├── HospitalRegistrationSystem.Model/ # 实体模型层纯 POCO无引用 EF └── HospitalRegistrationSystem.Common/ # 公共工具日期格式化、打印模板、MD5 加密、日志写入提示所有 DAL 层方法均返回ListT或T不返回 IQueryable。这是为避免 UI 层意外触发延迟加载Lazy Loading导致跨线程异常——WinForm 的 UI 线程和数据库线程必须严格隔离EF6 默认开启 Lazy Loading这里已在 DbContext 构造函数中显式关闭this.Configuration.LazyLoadingEnabled false;2.2 技术栈详解每个选择背后都是血泪经验组件版本/实现为什么选它替代方案为什么不选UI 框架WinForm.NET Framework 4.7.2终端兼容性好、开发效率高、VS Designer 支持成熟医院 IT 人员熟悉度高WPF 学习成本高、渲染在老旧显卡上易花屏Blazor 需 IIS 部署终端无法直连ORMEntity Framework 6.4对 SQL Server 支持最稳.Include()预加载语法清晰生成的 SQL 可读性强便于 DBA 审计Dapper 虽快但手写 SQL 多挂号系统涉及复杂关联患者-就诊记录-医生-排班维护成本陡增数据库SQL Server LocalDB开发SQL Server Express生产LocalDB 零配置、随应用启动Express 免费、支持 10GB 数据库满足中小医院 5 年数据量需求SQLite 不支持存储过程和复杂事务MySQL 在 Windows 下服务管理不如 SQL Server 原生顺滑打印System.Drawing.Printing直接调用 Windows GDI无需额外驱动挂号单、发票模板用PrintDocumentGraphics.DrawString控制像素级位置Crystal Reports 依赖运行时、设计器臃肿iTextSharp 生成 PDF 但窗口打印需额外预览步骤日志自研LogHelper.WriteLog()写入本地 txt简单可靠不依赖第三方组件日志按日期分文件log_20240520.txt方便运维排查挂号失败时间点log4net 配置复杂医院服务器常禁用 XML 解析Serilog 需 .NET Core与当前框架不兼容2.3 核心依赖项packages.config 关键片段!-- HospitalRegistrationSystem.DAL/packages.config -- packages package idEntityFramework version6.4.4 targetFrameworknet472 / package idMicrosoft.SqlServer.Compact version4.0.8876.1 targetFrameworknet472 / package idNewtonsoft.Json version13.0.3 targetFrameworknet472 / /packages注意Microsoft.SqlServer.Compact是为 LocalDB 提供的轻量级 SQL Server 连接支持不是 SQL Server Compact Edition已淘汰。它仅用于开发阶段连接(LocalDB)\MSSQLLocalDB生产环境切换连接字符串后自动走标准 SQL Server 驱动。3. 核心模块拆解挂号流程如何从 UI 点击落到数据库插入挂号不是点一下“确认”就完事。这个系统把一次挂号拆成 5 个原子操作患者存在性校验 → 号源余量检查 → 号源锁定防超挂→ 挂号记录写入 → 打印单据。我们以FrmRegistration.cs中的btnConfirm_Click事件为入口逐层下钻。3.1 UI 层挂号窗体的关键控件与状态管理FrmRegistration.cs不是简单表单它包含三个关键区域左侧科室树TreeView绑定Department实体点击展开显示该科室下所有在职医生中部号源列表DataGridView显示选中医生当日可挂号时段如 08:00-08:30、08:30-09:00每行含SlotTime、RemainCount、IsLocked字段右侧挂号信息面板GroupBox动态加载患者信息支持刷身份证或手动输入含姓名、性别、年龄、联系方式、医保类型。关键代码在按钮点击事件// FrmRegistration.cs private void btnConfirm_Click(object sender, EventArgs e) { // 1. 前置校验患者是否已选、医生是否已选、时段是否已选 if (selectedPatient null || selectedDoctor null || selectedSlot null) { MessageBox.Show(请先选择患者、医生和就诊时段, 提示, MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } // 2. 调用 BLL 层挂号方法传入患者ID、医生ID、时段ID、挂号类型 var result registrationService.RegisterPatient( selectedPatient.Id, selectedDoctor.Id, selectedSlot.Id, cmbRegistrationType.SelectedIndex 0 ? 预约 : 现场 ); // 3. 根据结果更新 UI 状态 if (result.IsSuccess) { MessageBox.Show($挂号成功就诊号{result.RegistrationNo}, 成功, MessageBoxButtons.OK, MessageBoxIcon.Information); PrintRegistrationTicket(result.RegistrationNo); // 触发打印 RefreshSlotList(); // 刷新号源列表减少余量 } else { MessageBox.Show($挂号失败{result.ErrorMessage}, 错误, MessageBoxButtons.OK, MessageBoxIcon.Error); } }说明registrationService是通过构造函数注入的RegistrationService实例非静态确保每次请求独立上下文result是自定义的RegistrationResult类含IsSuccess、ErrorMessage、RegistrationNo三个属性避免用 bool string 返回值这种反模式。3.2 BLL 层挂号业务的核心逻辑与事务边界RegistrationService.RegisterPatient()是整个流程的中枢它必须保证号源不被超卖、患者不被重复挂号同天同医生、数据库写入原子性。代码如下// HospitalRegistrationSystem.BLL/RegistrationService.cs public RegistrationResult RegisterPatient(int patientId, int doctorId, int slotId, string regType) { using (var context DbContextFactory.Create()) // 每次调用新建 DbContext { try { // 1. 开启事务必须否则号源检查和插入之间可能被并发抢占 using (var transaction context.Database.BeginTransaction()) { try { // 2. 检查患者今日是否已挂该医生防重复 var existingReg context.RegistrationRecords .FirstOrDefault(r r.PatientId patientId r.DoctorId doctorId DbFunctions.TruncateTime(r.RegistrationTime) DateTime.Today r.Status 已挂号); if (existingReg ! null) { return new RegistrationResult { IsSuccess false, ErrorMessage 您今天已预约过该医生请勿重复挂号 }; } // 3. 查询号源记录并加锁SQL Server 行锁防止并发超卖 var slot context.Schedules .Include(s s.Department) .FirstOrDefault(s s.Id slotId s.DoctorId doctorId); if (slot null || slot.RemainCount 0) { return new RegistrationResult { IsSuccess false, ErrorMessage 号源已满请选择其他时段 }; } // 4. 扣减余量直接 Update不 Load-Modify-Save context.Database.ExecuteSqlCommand( UPDATE Schedules SET RemainCount RemainCount - 1 WHERE Id {0}, slotId); // 5. 插入挂号记录 var record new RegistrationRecord { PatientId patientId, DoctorId doctorId, ScheduleId slotId, RegistrationNo GenerateRegistrationNo(), // 生成8位流水号如 REG202405200001 RegistrationTime DateTime.Now, Status 已挂号, RegistrationType regType, CreateTime DateTime.Now }; context.RegistrationRecords.Add(record); context.SaveChanges(); transaction.Commit(); // 提交事务 return new RegistrationResult { IsSuccess true, RegistrationNo record.RegistrationNo }; } catch { transaction.Rollback(); // 出错回滚 throw; } } } catch (Exception ex) { LogHelper.WriteLog($挂号失败患者{patientId}挂医生{doctorId}时段{slotId}异常{ex.Message}); return new RegistrationResult { IsSuccess false, ErrorMessage 系统繁忙请稍后重试 }; } } }参数说明patientId患者主键来自PatientService.GetOrCreateByCardNo()刷身份证时自动建档doctorIdslotId由 UI 层从 DataGridView 的SelectedRows[0].Cells[Id].Value获取regType枚举值区分“预约”需提前填就诊时间和“现场”即刻挂号GenerateRegistrationNo()非 GUID而是REG YYYYMMDD 4位序号便于人工核对和导出 Excel。3.3 DAL 层实体关系与关键约束设计数据库表结构直接映射到Entities/下的类。重点看Schedule和RegistrationRecord的关系设计// HospitalRegistrationSystem.Model/Entities/Schedule.cs public class Schedule { public int Id { get; set; } public int DoctorId { get; set; } public int DepartmentId { get; set; } public DateTime WorkDate { get; set; } // 排班日期如 2024-05-20 public TimeSpan StartTime { get; set; } // 08:00:00 public TimeSpan EndTime { get; set; } // 08:30:00 public int TotalCount { get; set; } // 该时段总号源数如 20 public int RemainCount { get; set; } // 剩余号源数初始TotalCount挂号时 -1 public string Status { get; set; } // 启用/停用 // 导航属性EF6 显式声明避免 Lazy Loading public virtual Doctor Doctor { get; set; } public virtual Department Department { get; set; } } // HospitalRegistrationSystem.Model/Entities/RegistrationRecord.cs public class RegistrationRecord { public int Id { get; set; } public int PatientId { get; set; } public int DoctorId { get; set; } public int ScheduleId { get; set; } // 关联 Schedule 表非 DoctorIdWorkDateStartTime 组合键 public string RegistrationNo { get; set; } public DateTime RegistrationTime { get; set; } public string Status { get; set; } // 已挂号/已退号/已过期 public string RegistrationType { get; set; } // 预约/现场 public DateTime CreateTime { get; set; } // 导航属性 public virtual Patient Patient { get; set; } public virtual Doctor Doctor { get; set; } public virtual Schedule Schedule { get; set; } }设计理由ScheduleId作为外键而非组合条件是因为挂号必须精确到“某个排班记录”而非模糊的“某医生某天某时段”。例如医生张三 5月20日 08:00-08:30 有两个排班记录上午门诊、下午特需号源独立管理。若用组合键无法区分这两个场景。4. 运行与调试从零开始跑通挂号流程的完整步骤别急着编译。这个项目对环境有明确要求跳过一步就卡在“找不到 System.Data.SqlClient”或“LocalDB 启动失败”。以下是我在某社区卫生服务中心实测通过的步骤Windows 10/11Visual Studio 2019/2022。4.1 环境准备三步搞定依赖第一步安装 .NET Framework 4.7.2 运行时下载地址https://dotnet.microsoft.com/download/dotnet-framework/net472注意不要只装 SDK必须装Runtime。很多医院电脑只装了 VS 却没装 Framework 运行时双击 exe 会直接报错“未能加载文件或程序集”。第二步启用并启动 SQL Server LocalDB以管理员身份运行 PowerShell# 检查 LocalDB 是否已安装 sqllocaldb info # 若未安装下载 SQL Server Express with Tools含 LocalDB # 安装后启动实例 sqllocaldb start MSSQLLocalDB # 验证连接应返回版本号 sqlcmd -S (localdb)\MSSQLLocalDB -Q SELECT VERSION第三步还原数据库关键项目根目录下有Database/HospitalDB.bak约 12MB。用 SSMS 或 sqlcmd 还原-- 在 SSMS 中执行或用 sqlcmd RESTORE DATABASE HospitalDB FROM DISK D:\path\to\HospitalDB.bak WITH MOVE HospitalDB TO C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\DATA\HospitalDB.mdf, MOVE HospitalDB_log TO C:\Program Files\Microsoft SQL Server\MSSQL15.MSSQLSERVER\MSSQL\DATA\HospitalDB_log.ldf, REPLACE;提示如果还原失败大概率是路径权限问题。把 bak 文件复制到C:\temp\下再还原或右键 SSMS → “以管理员身份运行”。4.2 修改连接字符串指向你的 LocalDB 实例打开HospitalRegistrationSystem/App.config找到connectionStrings节点connectionStrings !-- 开发环境LocalDB -- add nameHospitalDBConnectionString connectionStringData Source(localdb)\MSSQLLocalDB;Initial CatalogHospitalDB;Integrated SecurityTrue; providerNameSystem.Data.SqlClient / !-- 生产环境注释掉上面取消下面注释并修改 Server 和 Database -- !-- add nameHospitalDBConnectionString connectionStringData Source192.168.1.100;Initial CatalogHospitalDB;User IDsa;PasswordYourStrongPass1; providerNameSystem.Data.SqlClient / -- /connectionStrings注意Integrated SecurityTrue表示 Windows 身份验证LocalDB 默认只支持此模式。如果改用 SQL Server 账户密码必须在 LocalDB 中创建 SQL 登录用户不推荐增加部署复杂度。4.3 编译与首次运行验证核心流程用 Visual Studio 打开HospitalRegistrationSystem.sln右键解决方案 → “还原 NuGet 包”确保 EntityFramework 6.4.4 安装成功设为启动项目右键HospitalRegistrationSystem项目 → “设为启动项目”按CtrlF5不调试启动观察控制台输出若看到数据库连接成功共加载 12 个科室→ 连接 OK若弹出登录窗口→ 输入默认账号admin / 123456密码明文存于App.config的appSettings中生产环境务必加密登录后在左侧科室树点“内科” → 展开 → 点“张医生” → 中部号源列表显示今日可挂号时段 → 右侧点“新建患者” → 填姓名张三 → 点“挂号” → 弹出成功提示 → 打印窗口弹出若无打印机会提示“未找到打印机”不影响流程。血泪经验第一次运行时Program.cs中的CheckDatabaseConnection()方法会尝试连接并初始化数据如插入默认科室。如果连接字符串错会卡在“正在初始化...”不动。此时打开 VS 的“输出”窗口View → Output筛选Debug看具体报错。4.4 快速验证挂号数据落库挂号成功后立即查数据库确认-- 查挂号记录 SELECT TOP 5 r.RegistrationNo, p.Name as PatientName, d.Name as DoctorName, s.WorkDate, s.StartTime, s.EndTime, r.Status FROM RegistrationRecords r JOIN Patients p ON r.PatientId p.Id JOIN Doctors d ON r.DoctorId d.Id JOIN Schedules s ON r.ScheduleId s.Id ORDER BY r.CreateTime DESC;预期结果能看到刚挂的号Status已挂号RemainCount在Schedules表中已减 1。5. 避坑指南五个让我加班到凌晨的真实问题与解法这个项目在真实环境中跑过三年踩过的坑都固化成了文档。以下是最常被问、最易忽略、一错就全线崩的五个点按发生频率排序。5.1 现象挂号成功但号源余量没减少导致超卖原因RegistrationService.cs中的ExecuteSqlCommand更新语句未生效。常见于两种情况一是slotId传入错误UI 层取SelectedRows[0].Cells[Id].Value时该列未绑定DataPropertyNameId取到的是显示文本二是 LocalDB 实例名不匹配连接字符串写成(localdb)\v11.0旧版而实际是MSSQLLocalDB新版导致更新操作静默失败EF 不抛异常只返回 0 行影响。解决在ExecuteSqlCommand后加断点检查返回值int rowsAffected。若为 0立刻检查slotId值和连接字符串用 SQL Server Profiler 抓取实际执行的 SQL确认WHERE条件是否命中。5.2 现象患者刷身份证后姓名/性别乱码如“王小明”原因身份证读卡器 SDK 返回的字符串编码是 GB2312而 C# 默认用 UTF-8 解析。FrmPatientRegister.cs中调用读卡 DLL 后未做编码转换。解决在读卡回调函数中显式转码// 假设 rawBytes 是读卡器返回的 byte[] string name Encoding.GetEncoding(GB2312).GetString(rawBytes); txtName.Text name;5.3 现象打印挂号单时中文显示为方块或空白原因PrintDocument.PrintPage事件中使用的字体不支持中文。默认new Font(Arial, 10)在无中文字体的精简版 Windows 上会 fallback 到不支持中文的字体。解决强制指定中文字体// 在 PrintRegistrationTicket() 方法中 using (var font new Font(SimSun, 10, FontStyle.Regular)) // 宋体Windows 自带 { e.Graphics.DrawString(患者姓名 patientName, font, Brushes.Black, x, y); }5.4 现象排班管理窗体FrmScheduleManage保存时报“违反 PRIMARY KEY 约束”原因Schedules表的主键是Id自增但排班导入功能允许用户手动输入Id。当导入 Excel 时若某行Id已存在EF 尝试Add()会触发主键冲突。解决在ScheduleService.ImportFromExcel()中对每一行先Find()存在则Update不存在才Addforeach (var row in excelRows) { var existing context.Schedules.Find(row.Id); if (existing ! null) { // 更新字段 existing.TotalCount row.TotalCount; existing.RemainCount row.RemainCount; } else { context.Schedules.Add(new Schedule { /* ... */ }); } } context.SaveChanges();5.5 现象多用户同时挂号时偶尔出现“号源已满”误报但查数据库RemainCount 0原因RegistrationService中的号源检查FirstOrDefault和扣减ExecuteSqlCommand不在同一事务内中间有毫秒级时间差被另一个请求抢占。解决将两步合并为一条 SQL用UPDATE ... OUTPUT原子操作// 替换原来的两步 var result context.Database.SqlQuerySchedule( UPDATE Schedules SET RemainCount RemainCount - 1 OUTPUT INSERTED.* WHERE Id {0} AND RemainCount 0, slotId).FirstOrDefault(); if (result null) { return new RegistrationResult { IsSuccess false, ErrorMessage 号源已满 }; } // result.RemainCount 是扣减后的值可直接用于日志6. 二次开发实战如何安全地接入微信公众号预约和扩展医保结算接到需求“下周要上线微信公众号预约挂号患者在手机上选医生、时段生成预约码到院扫码激活”。这不是加个 Web API 就完事——得保证微信端和 WinForm 端共享同一套号源池、同一套冲突检测逻辑且不能改坏现有窗口业务。我的做法是不动 BLL 核心只新增适配层用接口抽象隔离变化。6.1 第一步抽象挂号能力为接口解耦实现在HospitalRegistrationSystem.Common/Interfaces/下新建public interface IRegistrationProvider { /// summary /// 执行挂号通用契约 /// /summary RegistrationResult Register(RegistrationRequest request); /// summary /// 预约挂号微信专用含预约码生成 /// /summary RegistrationResult Reserve(ReservationRequest request); } public class RegistrationRequest { public int PatientId { get; set; } public int DoctorId { get; set; } public int ScheduleId { get; set; } public string Type { get; set; } // 现场/预约 } public class ReservationRequest : RegistrationRequest { public string WeChatOpenId { get; set; } public DateTime ReserveTime { get; set; } // 预约时间非就诊时间 public string ReserveCode { get; set; } // 8位随机码如 WX20240520 }然后让原有RegistrationService实现该接口public class RegistrationService : IRegistrationProvider { public RegistrationResult Register(RegistrationRequest request) { // 复用原有逻辑 return RegisterPatient(request.PatientId, request.DoctorId, request.ScheduleId, request.Type); } public RegistrationResult Reserve(ReservationRequest request) { // 新增逻辑生成预约码、写入预约表、不扣减号源到院扫码才扣 // ... return new RegistrationResult { IsSuccess true, RegistrationNo request.ReserveCode }; } }好处UI 层FrmRegistration.cs只依赖IRegistrationProvider未来换微信、支付宝、自助机只需注入不同实现零修改现有窗体代码。6.2 第二步微信预约模块开发独立项目不污染主系统新建一个HospitalRegistrationSystem.WebApi项目ASP.NET Web API 2只引用HospitalRegistrationSystem.Common和HospitalRegistrationSystem.BLL不引用 WinForm 项目。控制器如下// Controllers/ReservationController.cs [RoutePrefix(api/reservation)] public class ReservationController : ApiController { private readonly IRegistrationProvider _provider; public ReservationController() { // 用 SimpleInjector 或直接 new避免引入 DI 容器复杂度 _provider new RegistrationService(); } [HttpPost] [Route(create)] public IHttpActionResult CreateReservation([FromBody] ReservationRequest request) { // 1. 校验微信 OpenId 有效性调用微信接口 if (!WeChatHelper.ValidateOpenId(request.WeChatOpenId)) { return BadRequest(无效的微信用户); } // 2. 调用统一挂号提供者 var result _provider.Reserve(request); // 3. 返回预约码和二维码图片 URL if (result.IsSuccess) { var qrCodeUrl QrCodeGenerator.Generate($RESERVE:{request.ReserveCode}); return Ok(new { Code result.RegistrationNo, QrCodeUrl qrCodeUrl }); } return BadRequest(result.ErrorMessage); } }关键点ReservationRequest中的ScheduleId由微信前端调用/api/schedule/available接口获取该接口也复用RegistrationService的号源查询逻辑号源数据完全一致无同步延迟。6.3 第三步到院扫码激活WinForm 端增强在FrmRegistration.cs中新增一个按钮“扫码激活”调用本地摄像头扫描二维码解析出RESERVE:WX20240520然后private void btnScanActivate_Click(object sender, EventArgs e) { var code ScanQrCode(); // 调用 ZXing.Net 扫码 if (code.StartsWith(RESERVE:)) { var reserveCode code.Substring(8); // 调用新方法根据预约码激活此时才真正扣减号源 var result registrationService.ActivateReservation(reserveCode); if (result.IsSuccess) { MessageBox.Show($激活成功就诊号{result.RegistrationNo}); } } }ActivateReservation方法在RegistrationService中新增它查Reservations表校验预约码有效性、时间是否在有效期内然后调用原有RegisterPatient扣减号源并生成正式挂号号。6.4 医保结算扩展用策略模式应对多地区医保接口医保结算更复杂A省用 HTTP POST XMLB市用 WebServiceC区要对接 HIS 系统。硬编码必死。我采用策略模式public interface IInsuranceSettlementStrategy { SettlementResult Settle(InsuranceSettlementRequest request); } public class ProvinceAHttpStrategy : IInsuranceSettlementStrategy { /* ... */ } public class CityBWebServiceStrategy : IInsuranceSettlementStrategy { /* ... */ } // 在 BLL 层注入策略工厂 public class InsuranceService { private readonly Dictionarystring, IInsuranceSettlementStrategy _strategies; public InsuranceService() { _strategies new Dictionarystring, IInsuranceSettlementStrategy { [province_a] new ProvinceAHttpStrategy(), [city_b] new CityBWebServiceStrategy() }; } public SettlementResult Settle(string regionCode, InsuranceSettlementRequest request) { if (_strategies.TryGetValue(regionCode, out var strategy)) { return strategy.Settle(request); } throw new NotSupportedException($不支持的医保地区{regionCode}); } }配置由App.config的appSettings控制add keyCurrentInsuranceRegion valueprovince_a /从那以后我每次接到新医保对接需求都强制走一遍“新增 Strategy 类 → 实现接口 → 注册到字典 → 配置开关”绝不碰原有结算逻辑。这套机制已支撑了 7 个地市的医保上线零故障。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑