资讯动态

Python与医院管理系统:基于Django的门诊挂号收费全栈实现

发布时间:2026/9/23 20:16:50 来源:尧图企业网站定制
简介一份面向毕业设计场景的街区医院管理系统完整资料基于Python与Django框架实现适合计算机相关专业学生用于课题参考、系统开发与论文撰写。资料包含完整毕业论文与配套源码论文围绕系统从引言、技术选型、需求分析到总体设计、功能实现、系统测试等章节展开详细记录了Python、Django、MySQL及B/S架构的实际应用。压缩包大小约34.66MB以zip格式整理主要包含论文文档与Python项目源码。目前已有54人学习对正在筹备医院管理类毕设或希望了解Python Web系统开发流程的读者可借助论文结构梳理设计思路并直接运行、调试源码验证管理员、医生、患者等模块功能节省从零搭建与排错的时间。1. Python与街区医院管理系统为什么这个“小系统”最适合练全栈基于 Python 的街区医院管理系统是课程设计和毕业设计里最高频出现的项目形态之一。它不像电商系统那样有复杂的促销链路也不像物联网平台那样要处理流式数据却同时拥有医生、患者、药品、收费四类核心对象以及挂号、诊断、处方、划价、收费、退费之间的严格流程约束。把这套链路完整走通等于把管理信息系统的需求分析、数据库建模、权限管理和事务处理全练了一遍。街区医院的业务量级不大通常只涉及门诊、药房、收费处和基础管理几类角色相比三甲医院的 HIS 系统少了一个数量级但科室、药品、库存、收费这些环节一个都不缺。这种“小而全”的特征让它特别适合用 Python 快速实现一个能演示、能答辩、也能继续扩展的完整系统。下文从技术选型、模块实现、数据库设计到交付维护按实际能写出来的粒度展开。2. 技术选型与架构边界用 Python 搭一个街区医院信息系统的骨架2.1 为什么 Django 比 Flask 更适合交付选框架时看的是“要交付什么”。街区医院管理系统最需要的是后台管理界面、权限模型、数据迁移和一套够用的 ORM而不是每秒几千请求的接口性能。所以 Django 是这类项目的常见起点。对比维度DjangoFlaskFastAPI自带后台管理有开箱即用需要额外拼装需要额外拼装ORM 与迁移内置迁移工具成熟习惯搭配 SQLAlchemy习惯搭配 SQLAlchemy权限模型Group/Permission 完整自己实现自己实现适用位置管理型系统全栈实现轻量接口/原型高并发 API 服务Flask 的优点是自由缺点是自由带来的选择成本FastAPI 的异步模型对报表、导表这类任务帮助不大。街区医院系统的真实瓶颈在业务约束是否正确不在框架吞吐量因此用 Django 把后台、模板、Admin 管起来是更稳的路线。如果只做一个后端 API前端用 Vue 做单页Flask 或 FastAPI 完全可行“论文源码”常见的交付是整体部署到一台机器上直接演示Django 在模板、Admin、权限三方面能省下最容易被质疑的工作量。2.2 一个可直接落地的 Django 项目目录结构django_hospital/ ├── manage.py ├── requirements.txt ├── config/ # 项目配置 │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── apps/ │ ├── accounts/ # 用户、角色、权限 │ ├── outpatient/ # 挂号、分诊、处方 │ ├── pharmacy/ # 药品资料、批次库存 │ ├── billing/ # 收费、退费、发票记录 │ └── dashboard/ # 统计报表与首页 ├── static/ └── templates/这个结构的关键是按业务模块拆分而不是按功能点拆。挂号、分诊、处方都跟门诊流程相关放入 outpatient药品资料、入库、批次放 pharmacy收费和退费放 billing。好处是论文里画模块图时一个 app 对应一个功能模块需求分析和数据库设计部分可以一对一对上。模块间的依赖关系不决定启动顺序Django 通过 INSTALLED_APPS 配置加载它们实际依赖靠 ORM 外键关联。设置 settings 时把各 app 名称写进 INSTALLED_APPS后面执行 migrate 时数据表会自动按依赖顺序创建。2.3 需求范围哪些必须做哪些先不做街区医院规模不大但业务链路完整如果没有边界很容易把项目做成“小 HIS”。常见做法是把范围卡在门诊一块必须做挂号管理、医生分诊与处方开立、药品划价、收费与退费、药品库存和批次管理、基础统计报表。先不做住院管理、检验检查设备对接、医保实时结算、短信通知。范围边界的意义不只是减少工作量。论文的“需求分析”和“可行性分析”章节需要明确主要用户角色和业务范围。先界定“门诊流程 药品库存 收费核算”把患者主索引、收费台账、药品台账三个核心数据域说通评审时才不会被“你为什么不做住院”这类问题带偏。3. 核心业务模块的实现挂号、医嘱与收费的主流程3.1 挂号状态机与号源控制挂号是整条链路的入口决定了后面的处方和收费是否归属于同一次就诊。设计上不能只存“挂号时间医生”要有一个状态字段让一次挂号在“已挂号、接诊中、已完成、已退号”之间流转并明确每个状态允许哪些后续操作。status 值含义允许的后续操作registered已完成挂号分诊、退号in_consultation医生开始接诊开立处方、结束接诊finished本次就诊结束查看、收费复核cancelled已退号无挂号接口里最基本的操作是退号。退号不光是改一个状态还要释放号源并检查是否已经有处方或者收费记录。下面是一个服务层方法的写法from django.db import transaction from django.core.exceptions import PermissionDenied class RegistrationService: staticmethod def cancel(registration, operator): if registration.status not in (registered, in_consultation): raise PermissionDenied(当前状态不允许退号) if registration.consultation_started: raise PermissionDenied(医生已接诊如需结束请走退费流程) with transaction.atomic(): registration.status cancelled registration.save(update_fields[status, updated_at]) RegistrationService._release_slot(registration)逻辑说明先做状态校验再进入事务修改状态和释放号源。update_fields 限定只写变化的字段避免并发下覆盖其他字段。退号后释放号源是后台记录数和实际业务挂号的唯一约束。这里要特别注意一个坑如果直接在 Django Admin 里把退号记录改成 finished状态约束就失效了。所以对外提供 Service 方法而不是让视图直接改字段是确保状态约束不散落到各处的关键。3.2 医嘱与处方价格快照与明细拆分医生开立处方时系统要同时处理两件事药品目录的选择和处方明细的价格锁定。药品价格定期会调价如果收费时再去查药品当前价格历史处方金额就会被悄悄改写。因此处方明细表里需要冗余一个价格快照字段。class PrescriptionItem(models.Model): prescription models.ForeignKey( outpatient.Prescription, on_deletemodels.CASCADE, related_nameitems ) drug models.ForeignKey( pharmacy.Drug, on_deletemodels.PROTECT, nullTrue, blankTrue ) drug_name models.CharField(药品名称快照, max_length128) price models.DecimalField(开立时单价, max_digits10, decimal_places2) quantity models.PositiveIntegerField(数量, default1) dose models.CharField(用法用量, max_length64, blankTrue)参数说明drug 外键允许为空且使用 PROTECT 级联是为了防止药品资料被误删后历史处方查询直接报错drug_name 和 price 保存开立时刻的快照报表统计和打印处方都以这两列为准。dose 记录“一日三次每次一片”这类医嘱文本不参与金额计算。处方与收费的关系不是用外键把处方和收费主表硬连在一起而是通过处方明细的外键来推导。如果一张收费单覆盖两张处方用 JSON 字段存处方 id 列表会给对账带来麻烦更规范的做法是建一张收费明细表与处方明细逐一对应。3.3 收费与退费库存扣减必须跟着事务走收费是整个系统里最容易写错并发逻辑的地方。最简单的实现是“创建收费记录 → 扣库存 → 返回成功”但如果没有事务保护扣库存成功、收费记录写入失败药品就被无声无息扣掉了。这两步必须放进同一个数据库事务transaction.atomic def settle(billing_items, operator): bill BillingRecord.objects.create( patientbilling_items[0].prescription.patient, operatoroperator, total_amountsum(item.amount for item in billing_items), statuspaid, ) for item in billing_items: drug_stock DrugBatch.objects.select_for_update().filter( drugitem.drug, remaining_qty__gteitem.quantity ).first() if drug_stock is None: raise ValueError(f{item.drug_name} 库存不足) drug_stock.remaining_qty - item.quantity drug_stock.save(update_fields[remaining_qty]) BillingItem.objects.create(billbill, prescription_itemitem, amountitem.amount)select_for_update() 是这里的关键它在事务内对库存批次记录加行级锁两个收费请求并发时第二个会等第一个提交后再读库存避免超卖。注意 select_for_update 只在事务里生效所以函数上的 transaction.atomic 不能省略。同一张处方不能被收费两次拦截逻辑可以有两种事务开始前查“该处方明细是否已有 billing_item”更稳的是在 prescription_item 上建唯一约束。退费方向相反更新收费状态为 refunded再把对应库存批次的数量加回来同时保留一条退费记录。退费必须退回原购买价格也就是 BillingItem 上的 amount而不是药品当前价。这条规则可以直接写进退费接口的注释里避免后来维护的人改错。3.4 用 Group 承载医院角色权限角色权限用 Django 自带的 auth 就够。初始化脚本里创建医生、收费员、药房、管理员四个 Group再通过权限集合控制访问。视图层用装饰器而不是在函数里手写 if 判断权限变动时不需要改代码from django.contrib.auth.decorators import permission_required from django.views.decorators.http import require_POST require_POST permission_required(outpatient.cancel_registration, raise_exceptionTrue) def cancel_registration_view(request, registration_id): registration Registration.objects.select_related(patient).get(pkregistration_id) RegistrationService.cancel(registration, request.user) return JsonResponse({ok: True})raise_exceptionTrue 让没有权限的请求直接返回 403而不是跳转到登录页。整个权限体系的粒度控制在“模块动作”级别比如开立处方、作废收费单不需要细到按钮级。街区医院系统内部人员角色固定按钮级权限只会增加配置工作量。4. 数据库设计与事务边界从 ER 到 Python ORM 的关键决策4.1 核心表结构与关系街区医院管理系统是典型的关系型数据核心表一般有 8 张左右。表结构关系如下表名关键字段关系说明hospital_usersusername, role_group, is_activeDjango auth 的 User 扩展patientsname, id_card, phone, medical_history患者主索引可一人多次就诊registrationspatient, doctor, register_time, status挂号记录属于某患者prescriptionsregistration, doctor, created_at每次就诊可有一条或多条处方prescription_itemsprescription, drug, price_snapshot, quantity处方明细对应具体药品drugsname, spec, approval_number, current_price药品目录drug_batchesdrug, batch_no, expiry_date, remaining_qty批次库存用于效期管理billing_recordspatient, operator, total_amount, status收费主记录billing_itemsbill, prescription_item, amount收费与处方明细关联表设计有三个容易出错的点第一患者表是否与用户表合并。不要合并。患者可能在挂号之前建档医生和收费员是平台用户两者语义不同合并会让权限模型和密码字段都变得别扭。第二drug_batches 加批次的目的。街区医院的药品入库要按批次记录效期扣库存时优先扣最近效期批次如果不建批次表后续做近效期提醒会非常被动。第三billing_items 必须保留 prescription_item 外键。它既是“哪些处方被收费”的对账依据也是退费时定位原明细的唯一索引。4.2 Django Models 对应的约束落地在 Django 里写 model 时要把上表的约束直接落到字段定义上。下面是患者与挂号两个 model 的关键部分class Patient(models.Model): name models.CharField(姓名, max_length64) id_card models.CharField(身份证号, max_length18, uniqueTrue) phone models.CharField(max_length20, blankTrue) created_at models.DateTimeField(auto_now_addTrue) class Registration(models.Model): STATUS [ (registered, 已挂号), (in_consultation, 接诊中), (finished, 已完成), (cancelled, 已退号), ] patient models.ForeignKey(Patient, on_deletemodels.CASCADE, related_nameregistrations) doctor models.ForeignKey(accounts.User, on_deletemodels.PROTECT) register_time models.DateTimeField(auto_now_addTrue) status models.CharField(max_length20, choicesSTATUS, defaultregistered, db_indexTrue)patient 外键使用 CASCADE患者资料删除时挂号记录跟着删这在现实中其实有争议。更稳妥的做法是患者表只做逻辑删除加一个 is_active 字段历史挂号记录保留用于统计。id_card 的 uniqueTrue 用于防止重复建档但真实场景里身份证录入难免有错误所以代码里还要提供建档信息合并工具。注意id_card 的 uniqueTrue 是数据库层约束重复建档在写入时会抛 IntegrityError应用层必须捕获并提示而不是让用户看到 500 页面。4.3 并发扣库存的两种处理方式上一章的收费代码用了 select_for_update 悲观锁。另一种是乐观锁在 drug_batches 上放 version 整数更新时带版本条件受影响行数为 0 则重试。from django.db.models import F updated DrugBatch.objects.filter(idbatch_id, versionversion).update( remaining_qtyF(remaining_qty) - qty, versionF(version) 1, ) if updated 0: raise ConflictError(库存已被其他操作变更请重试)F 表达式让数据库在原子层面同时扣减数量并自增版本避免先查后改的竞态。update 返回 0 时说明版本不匹配由上层决定重试还是提示用户。这种方法不依赖事务中的行锁但要应用层处理重试逻辑写起来比 select_for_update 多几行。扣批次库存这类短事务用悲观锁行锁足够不会成为瓶颈如果以后做多窗口并发收费压力测试再考虑改成乐观锁。4.4 数据库设计与论文各章节怎么对应“论文源码”的交付场景里数据库设计能不能与文字对上是评审最容易挑的点。ER 图里把患者、挂号、处方、收费四张主表放在中央药品与批次作为供给域用户与权限作为使用者域。系统设计章节按单据流描述挂号单 → 处方单 → 收费单库存与药品资料作为支撑基础数据。数据库设计章节写表结构和 E-R 关系需求分析写角色与边界详细设计写状态机与事务边界——这四块正好让论文主线与源码实现一一对应。5. 交付细节源码包之外的对账脚本与运行保障5.1 从 ZIP 到可运行系统收到一个源码包本地先建虚拟环境按 requirements.txt 安装依赖再迁移数据库、建管理员最后跑开发服务器python -m venv .venv # Windows: .venv\Scripts\activate # macOS/Linux: source .venv/bin/activate pip install -r requirements.txt python manage.py migrate python manage.py createsuperuser python manage.py runserver 0.0.0.0:8000参数说明venv 避免依赖污染系统 Pythonmigrate 把 models 变更同步到数据库createsuperuser 建的管理员可登录 Django Admin 做角色初始化。0.0.0.0 允许局域网内其他机器访问演示时方便用另一台电脑打开。用 PyCharm 打开 manage.py 所在目录也能直接配置解释器环境变量和运行参数在 RUN Configuration 里一次配好。5.2 一个不可省略的每日对账脚本系统上线后最容易出现的数据不一致源于手工调单收费处改了收费金额、药房手工改了库存、或者退费操作走了后台。作为兜底我会在源码包之外额外提供一个独立对账脚本跑三组数字from datetime import date from apps.outpatient.models import Registration from apps.billing.models import BillingItem def daily_reconciliation(day: date): registered Registration.objects.filter( register_time__dateday, status__in[finished, in_consultation] ).count() paid_items BillingItem.objects.filter( bill__created_at__dateday, bill__statuspaid ) return { 挂号数: registered, 收费明细数: paid_items.count(), 收费总额: float(sum(item.amount for item in paid_items)), }逻辑说明对账脚本不校验业务规则只输出挂号数、收费明细数和收费总额三个指标。如果明细数大于挂号数说明某次就诊被重复收费如果收费总额与处方划价总额不一致说明存在手工改价。把结果写入日志并按天保存可以给管理员一个明确的巡检入口。5.3 运行保障的最后一个细节开发环境用 runserver 够用长期运行要换 gunicorn 或 uwsgi。常见做法是打包一份 docker-compose 文件内部同时跑 web 容器和 MySQL 容器部署变成一条 docker compose up如果现场只有 Python 和 SQLite以最小依赖方式运行也行但生产长期使用要换 MySQL 或 PostgreSQL因为 SQLite 在并发写和备份粒度上有天然限制。查询慢时优先检查 Registration.patient、Registration.status、BillingRecord.created_at、BillingItem.bill 这些字段的 db_index几千条数据的系统基本无感。把对账脚本挂到每日定时任务里每天早上生成差异表这套系统才算能真正交给非研发同事用。本文还有配套的精品资源点击获取

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

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

免费获取报价