☰
PHP开源中医诊所后台系统:挂号处方库存全流程拆解与二次开发指南
2026/10/18 14:46:48 网站建设 项目流程

简介:这是一套面向PHP开发者与医疗信息化学习者的开源中医诊所后台管理系统,基于PHP与MySQL构建,采用MVC架构,涵盖患者信息、预约记录、药品库存等核心业务模块,并集成身份验证、角色权限控制、RESTful API与数据安全等常见后台能力,适合用于学习Web应用架构或进行二次开发定制。压缩包共约2000个文件,整体12.45MB,其中JavaScript文件1290个、Less样式258个、HTML页面185个、PHP脚本163个,另有JSON配置、Markdown说明、CSS样式及图片字体等资源,结构完整、层次清晰。目前已有928人学习下载。项目附带预配置数据库,开发者可快速搭建部署,对照源码理解路由、模板引擎与ORM的协作方式,并借鉴缓存、日志与错误处理等工程实践,对掌握PHP全栈开发与医疗信息化管理具有较高参考价值。

1. 接手一套中医诊所后台:从挂号到抓药,这套 PHP 系统把流程全串起来了

朋友的中医诊所最近换了套管理系统,让我帮忙看看能不能二次开发。我拿到的是一个 PHP 开源后台管理项目,带完整数据库,部署完打开浏览器那一刻,说实话有点意外——挂号、就诊、处方、药房、库存、收费,一条线全在后台里跑通了。它不是那种只给你几个空页面的脚手架,而是把中医诊所日常运转的核心环节都做成了可操作的模块。如果你手头正好有小型诊所、理疗馆、养生馆这类场景,需要一套能快速跑起来、又能按自己业务改的后台系统,这套 PHP 项目值得花时间拆一拆。它用的是经典 LNMP/LAMP 组合,前端走 Bootstrap 系的后台模板,后端 PHP 直连 MySQL,没有上重型框架,改起来门槛低,适合想快速落地又不想被框架绑死的开发者。

2. 技术选型与目录结构:为什么它用原生 PHP 而不是上框架

2.1 技术栈拆解:LNMP 组合下的轻量后台

这套系统的技术选型非常务实。后端是 PHP 原生写法,没有 Laravel、ThinkPHP 这类全栈框架的影子,数据库是 MySQL,前端后台模板基于 Bootstrap 加 jQuery,图表部分可能用到 ECharts 或类似库。为什么一个诊所后台会选原生 PHP?我拆完代码后的判断是:这类项目的使用者往往是小团队甚至个人开发者,服务器环境参差不齐,原生 PHP 加 MySQL 的组合几乎在任何虚拟主机、轻量服务器上都能跑,不需要 Composer 依赖管理,不需要复杂的路由配置,上传即用。代价是代码组织上不如框架规整,但对二次开发来说,反而少了一层“框架黑匣子”,你想改哪个页面,直接找到对应文件就行。

常见做法是,这类项目会在根目录放一个config.php或includes/db.php来集中管理数据库连接,所有业务页面通过require引入。我一般会先看这个文件,确认数据库配置和字符集设置,因为中医诊所系统里药材名称、处方备注经常有生僻字,字符集没设对,存进去就是乱码。

2.2 目录结构:从入口文件到模块划分

拿到源码包解压后,典型的目录结构大致如下(不同版本可能略有差异,但核心逻辑一致):

/ (项目根目录) ├── index.php # 登录入口 ├── config.php # 数据库配置 ├── /admin/ # 后台管理主目录 │ ├── dashboard.php # 首页概览 │ ├── patient/ # 患者管理模块 │ ├── appointment/ # 挂号预约模块 │ ├── prescription/ # 处方管理模块 │ ├── pharmacy/ # 药房与库存模块 │ └── finance/ # 收费与统计模块 ├── /assets/ # 静态资源 │ ├── css/ │ ├── js/ │ └── images/ ├── /includes/ # 公共函数与数据库连接 │ ├── db.php │ ├── functions.php │ └── auth.php └── /sql/ # 数据库脚本 └── clinic.sql

这个结构的好处是模块边界清晰。/admin/下按业务分目录,每个目录里通常有list.php(列表)、add.php(新增)、edit.php(编辑)、delete.php(删除)四个文件,这是原生 PHP 后台最经典的 CRUD 组织方式。/includes/放公共逻辑,/sql/放建表脚本。你拿到源码后,第一件事应该是把clinic.sql导入数据库,然后改config.php里的连接信息。

2.3 数据库设计:核心表与字段含义

数据库是这套系统的骨架。我导完 SQL 后重点看了几张核心表,这里用表格列一下关键结构:

表名用途关键字段
patients患者档案id, name, gender, age, phone, id_card, address, created_at
appointments挂号记录id, patient_id, doctor_id, appoint_time, status, fee
prescriptions处方主表id, patient_id, doctor_id, diagnosis, created_at
prescription_items处方明细id, prescription_id, herb_id, dosage, unit, price
herbs药材库存id, name, stock, unit, price, warning_stock
users系统用户id, username, password, role, real_name
payments收费记录id, patient_id, amount, pay_type, pay_time

prescriptions和prescription_items是一对多关系,一张处方对应多味药材,这是中医处方系统的核心设计。herbs表里的warning_stock字段用来做库存预警,低于这个值后台应该给出提示。users表的role字段区分管理员、医生、药房人员等角色,权限控制就靠它。

注意:导入 SQL 时如果遇到utf8mb4相关报错,检查 MySQL 版本是否低于 5.5.3,老版本不支持这个字符集,需要改成utf8。

3. 核心模块拆解:挂号、处方、库存三条主线的代码实现

3.1 挂号模块:从患者建档到排班分配

挂号是诊所系统的入口。我翻到/admin/appointment/add.php,核心逻辑大致是这样:

<?php require_once '../../includes/db.php'; require_once '../../includes/auth.php'; // 接收表单提交 if ($_SERVER['REQUEST_METHOD'] === 'POST') { $patient_id = intval($_POST['patient_id']); $doctor_id = intval($_POST['doctor_id']); $appoint_time = $_POST['appoint_time']; $fee = floatval($_POST['fee']); // 检查该时段是否已被占用(简单排班冲突检测) $checkSql = "SELECT COUNT(*) FROM appointments WHERE doctor_id = ? AND appoint_time = ? AND status != 'cancelled'"; $stmt = $pdo->prepare($checkSql); $stmt->execute([$doctor_id, $appoint_time]); if ($stmt->fetchColumn() > 0) { $error = "该医生在此时间段已有挂号记录"; } else { $insertSql = "INSERT INTO appointments (patient_id, doctor_id, appoint_time, fee, status, created_at) VALUES (?, ?, ?, ?, 'pending', NOW())"; $pdo->prepare($insertSql)->execute([$patient_id, $doctor_id, $appoint_time, $fee]); header("Location: list.php?msg=success"); exit; } }

这段代码有几个值得注意的点。第一,它用了 PDO 预处理语句,?占位符能有效防止 SQL 注入,这在原生 PHP 项目里算是比较规范的做法。第二,排班冲突检测只做了“同一医生同一时间”的简单判断,没有考虑就诊时长、医生排班表等复杂因素。如果你要改成更真实的排班系统,需要引入doctor_schedules表,记录医生每周的出诊时段,挂号时先查排班再查冲突。

参数说明:patient_id来自患者选择下拉框,doctor_id来自医生列表,appoint_time是 datetime 格式,fee是挂号费。status默认pending,就诊后改为completed,取消则改为cancelled。

3.2 处方模块:主从表写入与药材关联

处方录入是中医系统最有特色的部分。一张处方包含诊断信息和多味药材,写入时需要同时操作prescriptions和prescription_items两张表。核心代码在/admin/prescription/add.php:

<?php require_once '../../includes/db.php'; require_once '../../includes/auth.php'; if ($_SERVER['REQUEST_METHOD'] === 'POST') { $patient_id = intval($_POST['patient_id']); $doctor_id = intval($_SESSION['user_id']); $diagnosis = trim($_POST['diagnosis']); $herbs = $_POST['herbs']; // 数组:药材ID $dosages = $_POST['dosages']; // 数组:对应剂量 try { $pdo->beginTransaction(); // 1. 写入处方主表 $stmt = $pdo->prepare("INSERT INTO prescriptions (patient_id, doctor_id, diagnosis, created_at) VALUES (?, ?, ?, NOW())"); $stmt->execute([$patient_id, $doctor_id, $diagnosis]); $prescription_id = $pdo->lastInsertId(); // 2. 循环写入处方明细,同时扣减库存 $itemStmt = $pdo->prepare("INSERT INTO prescription_items (prescription_id, herb_id, dosage, unit, price) SELECT ?, id, ?, unit, price FROM herbs WHERE id = ?"); $stockStmt = $pdo->prepare("UPDATE herbs SET stock = stock - ? WHERE id = ? AND stock >= ?"); foreach ($herbs as $index => $herb_id) { $dosage = floatval($dosages[$index]); $itemStmt->execute([$prescription_id, $dosage, $herb_id]); $stockStmt->execute([$dosage, $herb_id, $dosage]); if ($stockStmt->rowCount() === 0) { throw new Exception("药材ID {$herb_id} 库存不足"); } } $pdo->commit(); header("Location: list.php?msg=success"); } catch (Exception $e) { $pdo->rollBack(); $error = "处方保存失败:" . $e->getMessage(); } }

这段代码是整套系统的核心。它用事务保证了处方写入和库存扣减的原子性——要么全部成功,要么全部回滚。INSERT ... SELECT的写法直接从herbs表读取单价和单位,避免前端传参被篡改。库存扣减时加了AND stock >= ?条件,如果库存不够,rowCount()返回 0,抛出异常触发回滚。

我一般会在二次开发时把库存扣减改成“处方审核通过后才扣减”,因为医生开方时可能只是草稿,直接扣库存会导致数据不准。这需要加一个status字段到prescriptions表,审核通过后再执行扣减逻辑。

3.3 库存模块:预警机制与入库出库记录

药材库存管理在/admin/pharmacy/下。除了基础的增删改查,我注意到系统有一个库存预警的查询逻辑:

<?php // 库存预警列表 $warningSql = "SELECT id, name, stock, unit, warning_stock FROM herbs WHERE stock <= warning_stock ORDER BY stock ASC"; $warningList = $pdo->query($warningSql)->fetchAll(PDO::FETCH_ASSOC); // 入库操作 if ($_SERVER['REQUEST_METHOD'] === 'POST' && $_POST['action'] === 'stock_in') { $herb_id = intval($_POST['herb_id']); $quantity = floatval($_POST['quantity']); $batch_no = trim($_POST['batch_no']); $expire_date = $_POST['expire_date']; $pdo->beginTransaction(); try { // 更新库存 $pdo->prepare("UPDATE herbs SET stock = stock + ? WHERE id = ?") ->execute([$quantity, $herb_id]); // 记录入库流水 $pdo->prepare("INSERT INTO stock_records (herb_id, type, quantity, batch_no, expire_date, created_at) VALUES (?, 'in', ?, ?, ?, NOW())") ->execute([$herb_id, $quantity, $batch_no, $expire_date]); $pdo->commit(); } catch (Exception $e) { $pdo->rollBack(); $error = "入库失败:" . $e->getMessage(); } }

预警逻辑很简单:stock <= warning_stock就列入预警列表。实际使用中,我建议把预警做成首页仪表盘的红色角标,医生登录后一眼能看到哪些药材快断了。入库操作同时更新herbs.stock和写入stock_records流水表,这样既能查当前库存,也能追溯每一笔出入库记录。batch_no和expire_date是药材批次管理的常见字段,中药饮片有保质期要求,这个设计是合理的。

4. 部署与运行:从零把系统跑起来的完整步骤

4.1 环境准备与数据库导入

部署这套系统不需要太复杂的环境。我一般用宝塔面板或者手动装 LNMP,PHP 版本建议 7.0 以上,MySQL 5.6 以上。具体步骤:

# 1. 创建数据库(假设数据库名为 clinic_db) mysql -u root -p -e "CREATE DATABASE clinic_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" # 2. 导入 SQL 脚本 mysql -u root -p clinic_db < /path/to/sql/clinic.sql # 3. 确认导入结果 mysql -u root -p clinic_db -e "SHOW TABLES;"

导入完成后,打开config.php或includes/db.php,修改数据库连接信息:

<?php // 数据库配置 define('DB_HOST', '127.0.0.1'); define('DB_PORT', 3306); define('DB_NAME', 'clinic_db'); define('DB_USER', 'clinic_user'); define('DB_PASS', 'your_password'); define('DB_CHARSET', 'utf8mb4'); // PDO 连接 try { $dsn = "mysql:host=" . DB_HOST . ";port=" . DB_PORT . ";dbname=" . DB_NAME . ";charset=" . DB_CHARSET; $pdo = new PDO($dsn, DB_USER, DB_PASS, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, ]); } catch (PDOException $e) { die("数据库连接失败:" . $e->getMessage()); }

参数说明:DB_HOST一般是127.0.0.1或localhost,DB_CHARSET必须和建库时一致,否则中文乱码。PDO::ATTR_EMULATE_PREPARES => false让预处理真正在数据库层执行,安全性更高。

4.2 Web 服务器配置与访问测试

如果用的是 Nginx,需要配置一个 server 块指向项目根目录,并确保 PHP 文件能被正确解析:

server { listen 80; server_name clinic.local; root /www/wwwroot/clinic; index index.php index.html; location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { fastcgi_pass unix:/tmp/php-cgi-74.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }

配置完成后重启 Nginx,浏览器访问http://clinic.local,应该能看到登录页。默认管理员账号通常在 SQL 脚本的users表里,密码可能是 MD5 加密的,用SELECT * FROM users;查一下,如果密码字段是 MD5 值,可以临时插入一条新记录:

INSERT INTO users (username, password, role, real_name) VALUES ('admin_test', MD5('123456'), 'admin', '测试管理员');

登录后先别急着录数据,把每个菜单点一遍,确认没有 500 错误或空白页。常见问题是 PHP 扩展缺失,比如pdo_mysql、mbstring、gd,缺哪个装哪个。

4.3 初始配置与基础数据录入

系统跑起来后,需要先录入基础数据才能正常使用。顺序建议是:先加医生和角色用户,再加药材目录,最后加患者。药材录入时注意单位和价格,单位一般是“克”或“剂”,价格按克或按剂算。库存预警值根据实际用量设置,比如常用药材设 500 克,贵细药材设 50 克。

提示:录入药材时如果名称有生僻字,确保数据库和页面都是 utf8mb4,否则存进去显示问号,后面处方打印会出问题。

5. 避坑与常见问题:部署和二次开发中容易翻车的地方

5.1 中文乱码:从数据库到页面的字符集链路

现象:录入药材名称或患者姓名后,列表页显示乱码或问号。原因:字符集链路中某一环没统一。数据库建库时用了utf8而不是utf8mb4,或者 PHP 连接时没设charset=utf8mb4,又或者 HTML 页面没声明<meta charset="utf-8">。解决:按“数据库 → 连接 → 页面”顺序逐一检查。建库用utf8mb4,PDO 连接串加charset=utf8mb4,页面 head 里加 meta 声明。三步都对了,乱码基本消失。

5.2 登录后跳转空白:session 与权限文件引入问题

现象:输入账号密码后页面空白,没有报错也没有跳转。原因:auth.php里可能用了session_start()但前面有输出,或者权限验证失败后没有给出提示直接exit。解决:打开 PHP 错误显示,在php.ini里设display_errors = On,然后刷新页面看具体报错。常见的是headers already sent,检查auth.php或db.php文件末尾是否有空格或空行在<?php之前。

5.3 库存扣减为负数:并发下的超卖问题

现象:两个用户同时开方,同一味药材库存被扣成负数。原因:库存检查stock >= ?和扣减stock = stock - ?是两条独立语句,中间有时间窗口。解决:把扣减改成UPDATE herbs SET stock = stock - ? WHERE id = ? AND stock >= ?,用rowCount()判断是否扣减成功。如果返回 0,说明库存不足,回滚事务。这个写法在单条 SQL 里完成了检查和扣减,避免了并发窗口。

5.4 处方打印格式错位:CSS 打印样式缺失

现象:处方打印出来排版混乱,药材明细对不齐。原因:页面用的是屏幕 CSS,没有专门的@media print样式。解决:在处方详情页加一段打印样式:

@media print { .no-print { display: none; } .prescription-table { width: 100%; border-collapse: collapse; } .prescription-table th, .prescription-table td { border: 1px solid #000; padding: 4px; } }

把不需要打印的导航、按钮加上no-print类,打印时自动隐藏。

5.5 数据库备份恢复后自增 ID 冲突

现象:从备份恢复数据后,新增记录报主键冲突。原因:备份时只导了数据没导表结构,或者导入时自增计数器没重置。解决:用mysqldump导出时加--add-drop-table,恢复后执行ALTER TABLE 表名 AUTO_INCREMENT = 1;重置计数器。更稳妥的做法是每次备份都导出完整结构和数据。

6. 二次开发改造:从能用变成好用,我的几个实操习惯

这套系统底子不错,但直接拿去用,总有些地方不顺手。我一般会从三个方向动手改。

第一个方向是权限细化。原生的role字段只有 admin、doctor、pharmacy 几个粗粒度角色,实际诊所里可能有“前台只能挂号不能看处方”“药房只能看库存不能改价格”这类需求。我的做法是加一张permissions表,字段为role、module、action,然后在auth.php里加一个checkPermission($module, $action)函数,每个页面入口调用一次。这样改起来不复杂,但权限控制立刻精细了一个档次。

第二个方向是处方模板。中医开方有很多经典方剂,医生每次手动选药材很费时间。我一般会加一张prescription_templates表,存方剂名称和默认药材组合,开方页面加一个“载入模板”下拉框,选中后自动填充药材明细,医生再根据患者情况加减。这个功能对老中医特别友好,能省不少事。

第三个方向是数据统计。原系统可能只有简单的收费汇总,我会用 ECharts 加几个图表:每日挂号量趋势、药材消耗排行、医生工作量对比。数据从appointments、prescription_items、payments表里聚合,SQL 不复杂,但给管理者看很直观。

改造时有个血泪经验:每次改数据库结构前,先mysqldump备份一份,然后在本地改完测试通过再上服务器。我有一次直接在生产库加字段,结果字段类型和代码不匹配,导致处方页面全部报错,最后靠备份才恢复。从那以后我每次动数据库都强制走一遍“备份 → 本地改 → 测试 → 上线”的流程,再也没翻过车。

如果你也在找一套能快速跑起来、又能按自己想法改的中医诊所后台,这套 PHP 开源系统值得下载下来拆一拆。先从导入数据库、跑通登录开始,然后挑一个你最想改的模块动手,改着改着,整套系统的脉络就清楚了。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询