基于C# asp.net的体检信息管理系统设计与实现

发布时间:2026/9/14 14:45:27
基于C# asp.net的体检信息管理系统设计与实现 简介在医疗信息化进程中体检中心通常面临预约登记、套餐管理、结果录入与报告生成等多环节协同问题传统Excel流转模式形成信息孤岛。如何利用成熟的Web开发框架构建一套规范化的管理系统基于ASP.NET MVC的分层架构结合SQL Server数据库设计通过仓储模式实现业务逻辑与数据访问解耦能够有效支撑体检业务流程。该系统不仅涵盖患者建档、套餐勾选、结果录入、异常校验等核心功能还可通过iText7生成PDF体检报告并具备权限安全与性能优化设计。从工程实践角度看这类系统既适合医院信息科部署也常作为.NET方向毕业设计的选题。本文围绕系统分层、数据库建模、核心代码实现与论文章节组织展开为开发者提供可复用的设计与源码参考。1. 从体检科的信息孤岛到C# asp.net管理系统体检中心的业务流程往往比普通门诊更琐碎个人和团体的预约登记、不同套餐的项目勾选、各科室分头录入结果、总检医生综合判定、最后还要生成一份可打印的 PDF 报告。很多体检中心还在用 Excel 传文件每个月的统计和异常追踪要耗费大量人工。用 C# 基于 asp.net 做一套医院体检信息管理系统正是为了把这条链路从“人传人”改成“数据流”。这类项目在医疗信息化招聘和毕业设计要求里反复出现所以“源码”和“论文”两个词经常会绑在一起。对有经验的 .NET 工程师来说这套系统的难点不在 CURD而在体检行业的状态模型和报告生成对打算拿它写论文的人来说难点则是把功能代码转成条理清晰的系统设计论据。下文从数据库建模开始逐步落地一套可运行的核心骨架。2. 系统分层设计与数据库建模兼顾业务和论文2.1 为什么选 asp.net MVC 而不是 Web Forms体检信息管理系统的交互以表单录入、列表筛选和报表展示为主asp.net MVC 比 Web Forms 更容易控制 HTML 输出和 URL 语义也方便前后端分离。MVC 的路由映射规则让每个业务功能都对应一个清晰的 Controller 和 Action例如/PhysicalExam/Register、/PhysicalExam/ResultEntry这对论文中的功能模块划分非常有利。asp.net 的安全性在 MVC 5 / Core 中比 Web Forms 更可控后者靠 ViewState 回传一旦配置不当容易引入反序列化风险。在实际项目里我一般会采用分层的解决方案Exam.WebMVC 表现层、Exam.Application服务层、Exam.Domain领域模型、Exam.InfrastructureEF Core 或 ADO.NET 数据访问。论文写作时这份结构可以直接映射到“表现层—业务层—数据访问层—数据库”的经典四层架构图中。2.2 面向论文的五表数据模型含 SQL一套体检系统的核心数据模型可以压缩为五张表体检者Patient、套餐Package、套餐明细PackageItem、体检登记Examination、检查结果ExaminationResult。这样设计既能支撑“开单—登记—分检—录入—汇总”的流程又不会让论文里的 ER 图失控。以下是精简后的 SQL Server 建表脚本CREATE TABLE Patient ( PatientId INT IDENTITY(1,1) PRIMARY KEY, Name NVARCHAR(50) NOT NULL, Gender CHAR(1) CHECK (Gender IN (M,F)), Age INT, IDCardNo VARCHAR(18) UNIQUE, Phone VARCHAR(20), CreateTime DATETIME DEFAULT GETDATE() ); CREATE TABLE Package ( PackageId INT IDENTITY(1,1) PRIMARY KEY, PackageName NVARCHAR(80) NOT NULL, Price DECIMAL(10,2), Description NVARCHAR(500) ); CREATE TABLE PackageItem ( ItemId INT IDENTITY(1,1) PRIMARY KEY, PackageId INT FOREIGN KEY REFERENCES Package(PackageId), ItemCode VARCHAR(20), -- 如 BLOOD_SUGAR ItemName NVARCHAR(100), -- 血糖 ReferenceRange NVARCHAR(200) -- 参考范围 ); CREATE TABLE Examination ( ExamId INT IDENTITY(1,1) PRIMARY KEY, ExamNo VARCHAR(20) UNIQUE NOT NULL, -- 体检编号 PatientId INT FOREIGN KEY REFERENCES Patient(PatientId), PackageId INT FOREIGN KEY REFERENCES Package(PackageId), Status TINYINT DEFAULT 0, -- 0登记 1进行中 2完成 TotalPrice DECIMAL(10,2), ExamDate DATE, Summary NVARCHAR(MAX) -- 总检结论 ); CREATE TABLE ExaminationResult ( ResultId INT IDENTITY(1,1) PRIMARY KEY, ExamId INT FOREIGN KEY REFERENCES Examination(ExamId), ItemCode VARCHAR(20), ItemName NVARCHAR(100), ResultValue NVARCHAR(200), -- 检验数值/结论 IsAbnormal BIT DEFAULT 0, CompletedBy VARCHAR(30), CompletedTime DATETIME DEFAULT GETDATE() );这里的关键字段是Examination.Status和ExaminationResult.IsAbnormal。状态字段用TINYINT而不是字符串是为了让流程控制简洁比如当所有项目录入完毕程序自动把状态置为 2。IsAbnormal供总检医生快速筛选异常项。IDCardNo 设为唯一索引用于防止同一个身份证重复建档但要注意体检行业里可能存在团检替检的情况业务上还需要人工核对。2.3 服务层和仓储模式的 C# 落地服务层负责把控制器整理得不那么臃肿。下面用 C# 定义体检登记服务接口这是源码里最容易体现论文“业务逻辑模块化”的部分public interface IExaminationService { Examination CreateExamination(int patientId, int packageId); IReadOnlyListExaminationResult GetResults(int examId); void SaveResult(ExaminationResult result); Examination CompleteExamination(int examId, string summary); } public class ExaminationService : IExaminationService { private readonly IRepositoryExamination _examRepo; private readonly IRepositoryExaminationResult _resultRepo; private readonly IRepositoryPackage _packageRepo; public ExaminationService(IRepositoryExamination examRepo, IRepositoryExaminationResult resultRepo, IRepositoryPackage packageRepo) { _examRepo examRepo; _resultRepo resultRepo; _packageRepo packageRepo; } public Examination CreateExamination(int patientId, int packageId) { var package _packageRepo.GetById(packageId); var exam new Examination { ExamNo GenerateExamNo(), PatientId patientId, PackageId packageId, TotalPrice package.Price, Status 0, ExamDate DateTime.Today }; _examRepo.Add(exam); return exam; } }仓储模式在这里的价值是把 EF Core 的DbSet包装起来测试时可以替换为内存仓储。如果要向论文的“关键技术”章节描述依赖注入这个ExaminationService就是最好的例子——它的构造函数不依赖具体DbContext只依赖IRepositoryT接口这能写出不涉及 ASP.NET 具体工艺的单元测试。需要注意的是GenerateExamNo()的生成逻辑最好用日期加自增序号比如PE20250521001避免用 GUID 直接暴露给用户。体检编号经常要打印到条码上越短越容易识别。3. 用 C# 实现体检全流程登记、套餐、结果录入3.1 控制器里的业务编排与路由在 MVC 项目中路由默认按/{controller}/{action}/{id}匹配。体检登记的入口控制器可以这样写[RoutePrefix(PhysicalExam)] public class PhysicalExamController : Controller { private readonly IExaminationService _examService; private readonly IPatientService _patientService; public PhysicalExamController(IExaminationService examService, IPatientService patientService) { _examService examService; _patientService patientService; } [HttpGet] [Route(Register)] public ActionResult Register() { var model new RegisterViewModel { PackageList LoadPackages() }; return View(model); } [HttpPost] [Route(Register)] [ValidateAntiForgeryToken] public ActionResult Register(RegisterViewModel model) { if (!ModelState.IsValid) return View(model); var patient _patientService.FindByIdCard(model.IDCardNo); if (patient null) { patient new Patient { Name model.Name, Gender model.Gender, IDCardNo model.IDCardNo, Phone model.Phone }; _patientService.Create(patient); } var exam _examService.CreateExamination(patient.PatientId, model.PackageId); return RedirectToAction(Detail, new { id exam.ExamId }); } }代码里的[RoutePrefix]和[Route]是属性路由比传统配置更能体现 asp.net MVC 工作原理里“约定与配置并存”的特点。ValidateAntiForgeryToken是 asp.net 内置的防跨站请求伪造机制体检系统会部署在内网但该加的校验不能省。3.2 体检登记和套餐选择的核心代码套餐选择不能只存一个套餐 ID因为受检者可能会加项。下面用视图模型RegisterViewModel展示复选套餐的处理逻辑public class RegisterViewModel { public string Name { get; set; } public string Gender { get; set; } public string IDCardNo { get; set; } public string Phone { get; set; } public int SelectedPackageId { get; set; } public ListPackageViewModel PackageList { get; set; } public Listint ExtraItemIds { get; set; } // 自选项目 } [HttpPost] [Route(CalculatePrice)] public JsonResult CalculatePrice(int packageId, Listint extraItemIds) { var package _packageService.GetById(packageId); var extraItems _packageService.GetItemsByIds(extraItemIds); var total package.Price extraItems.Sum(i i.Price ?? 0); return Json(new { Total total, Items extraItems.Select(i i.ItemName) }); }这里CalculatePrice是异步请求返回 JSON前端用 jQuery 或者fetch更新页面价格。写论文时可以把这种交互归纳为“基于 JSON 的局部刷新减轻服务端视图渲染压力”。不过有个坑extraItemIds里的自选项目不一定来自同一个套餐数据库里应该把自选项目单独加进ExaminationResult表并归属到原始套餐编号下不然总检报告会丢失项目来源。3.3 结果录入与异常数据校验体检各科室录入结果时最常见的操作是按检查项目逐条录入。下面是一段结果服务代码包含事务逻辑public void SaveResult(ExaminationResult result) { if (string.IsNullOrWhiteSpace(result.ResultValue)) throw new ArgumentException(结果值不能为空); var existing _resultRepo.FirstOrDefault(r r.ExamId result.ExamId r.ItemCode result.ItemCode); if (existing ! null) { existing.ResultValue result.ResultValue; existing.IsAbnormal CheckAbnormal(result.ItemCode, result.ResultValue); existing.CompletedBy result.CompletedBy; existing.CompletedTime DateTime.Now; _resultRepo.Update(existing); } else { result.IsAbnormal CheckAbnormal(result.ItemCode, result.ResultValue); _resultRepo.Add(result); } }CheckAbnormal方法负责比对参考范围比如血糖范围是 3.9 到 6.1 mmol/L。参考范围可以存储在PackageItem.ReferenceRange里格式用半角逗号分隔上下限不要直接存标签文本。这样在 C# 代码里就能用简单的Split解析而不是依赖正则表达式。异常数据除了要做数值范围校验还要考虑性别差异。比如血肌酐的参考范围男女不同那么数据结构里就应该有Gender相关标记否则系统会把男性正常值误判为异常导致总检医生被大量假阳性骚扰。4. 从源码提炼论文关键技术章节怎么组织4.1 把 EF 的 CRUD 映射成论文的“系统设计”毕业论文里的“系统设计”章节不需要堆砌所有代码而是画清楚数据流和模块调用。一个实用的做法是把每个 Controller 的 Action 按“用户操作—系统响应—数据变化”三列制表作为论文章节的主线。下表是一个示例用户操作Controller/Action数据表变化提交体检登记ExaminationController.RegisterPatient 插入或查询Examination 插入在套餐列表点击加项ExaminationController.CalculatePrice不写库仅会话里保存录入血糖值ResultController.SaveExaminationResult 更新IsAbnormal 更新完成总检ExaminationController.CompleteExamination.Status2Summary 写入在论文里这张表能让答辩老师马上看出你对 asp.net MVC 中模型绑定和数据库事务的掌握程度。源码中的 EF 迁移Migration文件则用来证明数据库设计是演进式的而不是一次性脚本。4.2 权限与安全ViewState 和防篡改只要是 asp.net 的 Web Forms 版本论文里绕不开 ViewState。体检系统里即便用 MVC也可能会在当前会话中暂存体检者临时数据。很多源码示范会使用ViewState存对象但在 asp.net MVC 中没有服务端 ViewState应该用TempData或Session。如果系统采用混合模式需要理解视图状态的加密机制。默认情况下 asp.net 会对 ViewState 进行哈希签名防止客户端篡改。源码里的machineKey配置如果使用 AutoGenerate在负载均衡部署时会导致不同服务器产生的 ViewState 无法解密。论文中应对此做一段安全设计说明将machineKey固定为独立的随机字节并且启用ViewStateEncryptionModeAlways。注意不要讨论反序列化漏洞的利用代码只需强调校验和超时。以下是web.config的最小配置system.web machineKey compatibilityModeFramework20SP2 validationKey3F6A2C...64字节十六进制 decryptionKeyB9A4... validationSHA1 decryptionAES / pages enableViewStateMactrue viewStateEncryptionModeAlways / /system.webvalidationKey和decryptionKey可以放在论文的部署配置章节做成表格和说明。这会让论文看起来有“生产环境意识”而非只是学生作品。4.3 环境配置与可复现性问题论文里还要考虑评审老师是否能复现运行。不要只贴源码要说明开发环境Visual Studio 2019/2022、.NET Framework 4.8 或 .NET 6/8、SQL Server 2016 以上。重点区分三层版本asp.net 的经典.NET Framework版本和 asp.net core 版本。虽然标题写的是“asp.net”但现在的医院客户大多还在用 Windows Server IIS 部署 .NET Framework 版本而新开发项目往往选择 asp.net core。论文可以在“技术选型”里做对比例如下表对比项asp.net (.NET Framework)asp.net core部署环境IIS 应用池集成管线IIS / Kestrel / Docker跨平台仅 WindowsLinux Windows性能普通高吞吐异步友好第三方库生态老库齐全新库为主部分兼容这套对比放在论文里可以防止答辩时被问“为什么不直接用 Excel”时无话可说。关键点在于体检系统的硬件环境往往老旧内网打印机、读卡器调用通常依赖 32 位 COM 组件老框架兼容性更好这是很多医院信息科坚持 asp.net 的客观理由。5. 体检报告 PDF 生成与性能调优5.1 用 iText7 将体检结论写入 PDF 矩形框体检系统最有技术含量的部分往往在报告导出。用 iText7 可以直接控制文本和图片的位置把报告按固定版式输出。以排版为例先用PdfCanvas绘制一条红线下划线然后在指定的矩形框内写入检查结论避免文字溢出。以下代码展示了如何实现文本和图片的分层输出public byte[] GenerateReportPdf(Examination exam, ListExaminationResult results) { using var ms new MemoryStream(); var writer new PdfWriter(ms); var pdf new PdfDocument(writer); var document new Document(pdf, PageSize.A4); var font PdfFontFactory.CreateFont(C:\fonts\msyh.ttf, PdfEncodings.IDENTITY_H, PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED); // 第一层图片 logo var logo ImageDataFactory.Create(Server.MapPath(~/Content/images/hospital_logo.png)); var img new Image(logo); img.SetFixedPosition(40, 760); img.ScaleToFit(80, 80); document.Add(img); // 第二层文本标题 var title new Paragraph(体检报告) .SetFont(font) .SetFontSize(18) .SetTextAlignment(TextAlignment.CENTER); document.Add(title); // 第三层矩形区域内的总检结论 var summaryRect new Rectangle(40, 200, 500, 300); var canvas new PdfCanvas(pdf.GetFirstPage()); canvas.Rectangle(summaryRect); canvas.Stroke(); var summary new Text(exam.Summary ?? 无异常) .SetFont(font) .SetFontSize(10); var paragraph new Paragraph(summary) .SetMarginTop(10) .SetPaddingLeft(5); document.Add(paragraph); document.Close(); return ms.ToArray(); }这段代码里SetFixedPosition和SetTextAlignment分别控制图片与文本的定位Rectangle负责绘制提示框但document.Add会自动排版需要手动确认文本不超出矩形边界。实际生产中建议使用Div搭配SetHeight以及SetOverflowBehavioriText7 会基于 content 自动拆分跨页比我们手工画框更可靠。另外中文字体必须嵌入否则目标机器没有字体时导出乱码这是 iText7 里最容易踩的坑。体检报告的生成通常放在阻塞线程里不可直接写到二进制字段。例如保存为数据库varbinary(max)字段同时生成一份 PDF 文件到文件服务器再把相对路径存在Examination.ReportUrl字段里。该字段在论文中可以作为扩展字段提但不建议把它放进核心 ER 图。5.2 体检系统的性能优化指标做完功能后调优应当盯三个指标登记页的加载时间、按身份证查询患者的速度以及结果批量保存的时间。SQL 层面ExaminationResult表要给ExamId ItemCode建联合索引。EF Core 默认会把导航属性延迟加载在列表页如果不需要显示结果明细应该用AsNoTracking()避免代理带来的额外查询。var list _context.Examination .Include(e e.Patient) .AsNoTracking() .Where(e e.ExamDate startDate e.ExamDate endDate) .Select(e new ExaminationListDto { ExamId e.ExamId, PatientName e.Patient.Name, Status e.Status }) .ToList();AsNoTracking()只负责收割只读数据Select投影成 DTO 后EF 只生成两表连接的字段而不是整行所有列。如果体检中心同时有多个科室并发录入需要把ExaminationResult的更新粒度保持在“单条结果”避免把整张报告锁住。最后再给一个实用技巧用Stopwatch做一次简单的性能剖析统计每个接口的Action执行时间把超过 500ms 的接口列入优化清单。这个做法也适合写进论文的“系统测试”章节用图表展示体检登记并发场景下响应时间的分布远比贴一堆 SQL 日志更有说服力。本文还有配套的精品资源点击获取