资讯动态

C# WinForm医院挂号管理系统:从排班到退号的完整实现与避坑指南

发布时间:2026/10/24 6:48:35 来源:尧图企业网站定制
简介这是一份基于C#语言与WinForm技术栈开发的医院挂号管理系统源码项目采用C/S架构与MVC分层设计面向计算机专业学生、.NET初学者及需要课程设计或毕业设计参考的开发者可用于学习桌面端信息管理系统的完整实现思路。系统功能覆盖用户管理、科室管理、医生管理、门急诊挂号、挂号查询、修改口令、挂号单打印与帮助文档等模块表示层、业务层与数据访问层职责分明数据库操作借助存储过程完成用户密码采用MD5加密统计信息以图文并茂的报表形式呈现并支持医生照片上传以便查看详细信息。压缩包共185个文件约10.03MB以64个cs源码文件为核心辅以jpg图片、resx与resources资源文件、sql脚本、exe程序及chm帮助文档等结构完整便于对照研读。目前已有1267人学习下载适合作为分层架构、存储过程与报表展示等知识点的实践参考。1. 从一张排班表说起这套医院挂号管理系统到底解决什么问题门诊高峰期最怕什么不是医生不够而是号源分配全靠一张 Excel 排班表加两个窗口患者凌晨排队、护士手工划号、退号靠电话通知最后数据对不上账。这套用 C# WinForm 做的医院挂号管理系统瞄准的就是中小型门诊这个真实场景把科室、医生、排班、号源、挂号、退号、收费、统计串成一条闭环让窗口人员在一个客户端里完成全部操作。它适合两类人——正在做课程设计或毕业设计、需要一个结构完整且能跑起来的桌面系统的学生以及要给小型门诊搭一套轻量级挂号工具、不想上重型平台的开发者。整套方案不依赖浏览器和服务器部署单机或局域网内即可运行交付门槛低但业务逻辑一点不含糊。2. 需求与角色拆解先把业务边界划清楚再动手2.1 三类角色与核心用例做这类系统最容易翻车的地方是一上来就画界面、连数据库结果做到一半发现「退号要不要退费」「一个医生能不能跨科室排班」这些规则没定代码反复推翻。我一般的做法是先把角色和用例钉死再谈技术。系统里通常只有三类角色权限边界清晰角色核心用例数据可见范围挂号员窗口查号源、挂号、退号、打印凭条全部科室号源与挂号记录医生查看本人当日号序、叫号、标记已就诊仅本人排班与挂号管理员科室/医生/排班维护、号源生成、收费统计全量数据挂号员是高频操作者界面响应必须快医生端只读为主重点是号序清晰管理员负责基础数据操作频率低但一旦出错影响全局所以基础数据的增删改要加校验和二次确认。把这张表贴在需求文档第一页后面所有模块设计都围着它转就不会跑偏。2.2 号源模型整个系统的地基号源是这套系统的核心实体设计错了后面全乱。我的做法是把「排班」和「号源」拆成两层排班描述某医生某天上午/下午出诊、限号多少号源是排班按规则展开后的一个个可挂号位。这样退号、加号、停诊都好处理。-- 排班表一个医生一天最多两条上午/下午 CREATE TABLE Schedule ( ScheduleId INT IDENTITY PRIMARY KEY, DoctorId INT NOT NULL, WorkDate DATE NOT NULL, TimeSlot TINYINT NOT NULL, -- 1上午 2下午 TotalQuota INT NOT NULL, -- 限号数 RegFee DECIMAL(8,2) NOT NULL, -- 挂号费 Status TINYINT DEFAULT 1 -- 1正常 0停诊 ); -- 号源表排班展开后的具体号位 CREATE TABLE RegSlot ( SlotId INT IDENTITY PRIMARY KEY, ScheduleId INT NOT NULL, SerialNo INT NOT NULL, -- 序号 1..N Status TINYINT DEFAULT 0, -- 0可挂 1已挂 2已退 3已就诊 PatientId INT NULL, RegTime DATETIME NULL );排班和号源分离的好处是停诊时只需把 Schedule 状态置 0关联号源批量置为不可挂退号时只改单个 Slot 的状态不动排班。SerialNo 保证叫号顺序稳定不会因为退号出现空号错乱。这里有个血泪经验——号源状态千万别用「删除记录」表示退号一旦删了就没法统计退号率也没法追溯状态位才是正解。2.3 技术选型为什么是 WinForm ADO.NET有人会问2024 年了还做 WinForm 是不是过时。放到这个场景里选型逻辑其实很实在目标用户是窗口人员和学生答辩环境不需要跨平台不需要浏览器兼容部署一台机器装个 exe 加数据库就能跑。WinForm 拖控件快、事件模型直观、调试成本低对课程设计来说能在有限周期内做出完整闭环这比追新技术栈更划算。数据访问层用 ADO.NET 而不是重型 ORM原因是挂号业务 SQL 不复杂但要求可控——号源扣减、退号回滚这类操作需要精确控制事务和锁手写 SQL 反而清楚。连接串放配置文件方便换机器部署!-- App.config -- connectionStrings add nameHospitalDb connectionStringServer.;DatabaseHospitalReg;User Idsa;Password***; providerNameSystem.Data.SqlClient / /connectionStrings数据库选 SQL Server Express 版即可免费且和 ADO.NET 天然契合。如果答辩环境不方便装 SQL Server把连接层抽象成接口、换 SQLite 也能跑这一点在 2.4 会讲怎么留口子。2.4 分层结构UI、BLL、DAL 三层怎么切WinForm 项目最容易写成「窗体里塞满 SQL」改一个字段要翻十个文件。我一般强制三层UI 只负责取值和展示BLL 放业务规则比如「同一患者同科室当天不能重复挂」DAL 只管增删改查。层间用实体类传递不传 DataTable避免 UI 和数据库结构耦合。// BLL 层挂号核心逻辑事务 号源校验 public class RegisterService { public RegResult Register(int slotId, int patientId) { using (var conn DbHelper.CreateConnection()) { conn.Open(); using (var tran conn.BeginTransaction()) { try { // 行锁防止并发抢同一号 var slot SlotDal.GetForUpdate(conn, tran, slotId); if (slot null || slot.Status ! 0) return RegResult.Fail(该号源已被占用); SlotDal.MarkTaken(conn, tran, slotId, patientId); RegRecordDal.Insert(conn, tran, slotId, patientId); tran.Commit(); return RegResult.Ok(slot.SerialNo); } catch (Exception ex) { tran.Rollback(); LogHelper.Error(挂号失败, ex); return RegResult.Fail(系统繁忙请重试); } } } } }这段代码的关键点有三个一是GetForUpdate在查询时加行锁SQL Server 用WITH (UPDLOCK, ROWLOCK)防止两个窗口同时抢最后一个号二是整个挂号动作放在一个事务里号源状态和挂号记录要么都成功要么都回滚三是异常统一捕获并记日志UI 层只拿到「成功/失败」和提示语不暴露堆栈。参数上 slotId 是号源主键patientId 是患者主键返回结果里带序号方便打印凭条。这套写法在答辩时能讲清楚「并发怎么防」是加分项。3. 核心模块实现挂号、退号、排班与统计怎么落地3.1 挂号流程从选科室到出凭条挂号是主流程界面通常做成「科室树 → 医生列表 → 日期时段 → 号源网格」四段式。用户点科室右侧刷新医生选医生和日期下方 DataGridView 展示该排班下所有号源已挂的置灰。双击可挂号源弹出患者信息确认支持身份证读卡或手输确认后调 BLL 的 Register 方法成功后弹出凭条预览。这里有个交互细节值得说号源网格刷新要用「差量更新」而不是每次全量重绑否则高峰期频繁刷新会闪屏。我的做法是挂号成功后只改对应行的 Status 单元格颜色和文本不重新查库。凭条打印用System.Drawing.Printing的 PrintDocument模板固定几个字段科室、医生、日期时段、序号、挂号费、患者姓名。打印失败不能影响挂号结果所以打印逻辑放在事务提交之后单独 try-catch。3.2 退号与费用处理状态回滚不能只改一个字段退号看着简单实际坑最多。合格的做法是校验该号是否已就诊已就诊不允许退、是否在可退时间窗内比如就诊当日开诊前可退然后在一个事务里把 Slot 状态从 1 改回 2已退同时写一条退号记录如果涉及退费再写一条收费流水负数。public RegResult Cancel(int slotId, string reason) { using (var conn DbHelper.CreateConnection()) { conn.Open(); using (var tran conn.BeginTransaction()) { var slot SlotDal.GetForUpdate(conn, tran, slotId); if (slot.Status 3) return RegResult.Fail(已就诊不能退号); if (slot.Status ! 1) return RegResult.Fail(该号源状态异常); SlotDal.SetStatus(conn, tran, slotId, 2); // 已退 CancelLogDal.Insert(conn, tran, slotId, reason); // 退号留痕 if (slot.RegFee 0) FeeDal.InsertRefund(conn, tran, slotId, slot.RegFee); tran.Commit(); return RegResult.Ok(0); } } }注意退号后号源状态是 2 而不是回到 0这样统计时能区分「从未挂过」和「挂了又退」退号率才算得准。如果业务允许退号后重新放出再加一步把状态置 0 并清空患者信息但要在日志里保留原挂号人方便追溯。3.3 排班与号源生成批量操作要防重复管理员维护排班时通常是选医生、选日期范围、选时段、填限号一键生成。这里最容易出的问题是重复生成——同一医生同一天上午点了两次生成号源翻倍。解决办法是在 Schedule 表上建唯一索引(DoctorId, WorkDate, TimeSlot)插入前先查重冲突就提示「该时段已排班」。号源生成用循环插入数量等于 TotalQuotaSerialNo 从 1 递增。批量插入建议用SqlBulkCopy或拼批量 INSERT逐条插在限号 50 以内问题不大但养成批量习惯没坏处。停诊操作要把关联的未挂号源置为不可用已挂的号源要触发通知系统内消息或打印停诊公告这部分在课程设计里可以简化为状态标记加列表提示。3.4 统计报表挂号量、退号率、医生工作量统计模块是答辩时最能体现「系统完整度」的部分。通常做三张报表按日/科室的挂号量、退号率趋势、医生工作量排名。实现上用一条分组 SQL 出数据DataGridView 展示再配一个 Chart 控件画柱状图。-- 某日各科室挂号量与退号量 SELECT d.DeptName, SUM(CASE WHEN s.Status IN (1,3) THEN 1 ELSE 0 END) AS RegCount, SUM(CASE WHEN s.Status 2 THEN 1 ELSE 0 END) AS CancelCount FROM RegSlot s JOIN Schedule sc ON s.ScheduleId sc.ScheduleId JOIN Doctor doc ON sc.DoctorId doc.DoctorId JOIN Department d ON doc.DeptId d.DeptId WHERE sc.WorkDate date GROUP BY d.DeptName;参数 date 由日期控件传入统计口径要写进文档RegCount 含已挂和已就诊CancelCount 只算已退。口径不写清楚答辩老师一问「你这个数怎么来的」就答不上来。4. 避坑与常见问题这些坑我替你踩过了4.1 并发挂号导致超卖现象两个窗口同时点最后一个号都提示成功实际卖出两个。原因查询和更新之间没有锁两个事务都读到 Status0。解决查询号源时加UPDLOCK, ROWLOCK把「读—判—改」放进同一事务必要时对 Schedule 行也加锁。测试时开两个客户端同时点能稳定复现才算修好。4.2 退号后统计数字对不上现象报表里挂号量比实际少退号率算出来是负数。原因退号时直接删了 Slot 记录或者状态位用错。解决坚持状态位方案退号置 2 不删记录统计 SQL 口径统一在文档里定义所有报表共用同一段 WHERE 条件。4.3 窗体卡死与跨线程更新现象点挂号后界面白屏几秒或者后台线程更新 DataGridView 抛异常。原因数据库操作放在 UI 线程或者子线程直接碰控件。解决耗时操作放Task.Run回 UI 用Invoke/BeginInvoke简单场景也可以先禁用按钮加等待光标但根治还是异步。WinForm 的跨线程检查是玄学重灾区CheckForIllegalCrossThreadCalls false能压住异常但压不住问题别用。4.4 连接串写死导致换机部署失败现象在自己电脑跑得好好的拷到答辩机器上连不上库。原因连接串硬编码在代码里或者用了 Windows 身份验证但目标机账号不同。解决连接串放 App.config部署时只改配置文件数据库脚本单独提供初始化时建库建表插基础数据别指望对方手工建。4.5 打印凭条格式错位现象预览正常实际打印偏移或截断。原因没按打印机实际纸张尺寸设置PaperSize或者用了固定坐标没考虑 DPI。解决打印前设置自定义纸张如 80mm 小票坐标用e.Graphics.DpiX换算打印前先出预览让用户确认。5. 验收与答辩怎么证明这套系统真的能用验收这件事我一般分两步走先自测跑通业务闭环再准备答辩话术。自测清单按角色走一遍——管理员建科室、建医生、排一周班、生成号源挂号员挂三个号、退一个、再挂一个医生端看号序、标记就诊最后打开统计看数字是否自洽。每一步都截图存档答辩时按流程演示比口头讲十遍都管用。答辩时老师最爱问的三个问题提前准备好答案一是「并发怎么处理」答事务加行锁能现场开两个客户端演示最好二是「数据一致性怎么保证」答号源状态和挂号记录同事务提交退号留痕不删记录三是「你的系统跟真实医院差在哪」坦诚说清边界——本系统面向中小门诊不含医保结算和线上支付这是范围控制不是缺陷。把「不做什么」讲清楚反而显得你想过。演示节奏上有个技巧别从登录页开始慢慢点提前把基础数据准备好直接进挂号主界面演示核心流程统计报表放最后压轴。演示前把数据库备份一份万一现场点崩了能快速恢复这是后悔药。提示答辩机器上提前装好 .NET Framework 对应版本和 SQL Server Express连接串改成目标机实例名演示前完整跑一遍别到现场才发现环境不对。从那以后我每次交这类桌面系统都强制自己按「角色—用例—数据—异常」四步走一遍自测任何一步没过就不算完成。希望这套结构和踩坑记录能帮到你拿去改改就能变成你自己的方案。本文还有配套的精品资源点击获取

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

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

免费获取报价 →
↑