
简介这是一套面向PHP开发者与医疗信息化学习者的开源中医诊所后台管理系统基于PHP构建配套预配置数据库可快速搭建部署适合用于课程设计、二次开发或诊所业务管理场景。压缩包共约2000个文件整体12.45MB以JavaScript、Less、HTML、PHP为主辅以JSON配置、Markdown说明、CSS样式及图片字体等静态资源前后端结构完整。项目涵盖MySQL数据存储、MVC分层架构、身份验证与RBAC权限控制、RESTful API设计、前端响应式界面以及数据加密、防注入等安全实践并涉及缓存优化、错误日志与Git版本管理等工程要点对理解Web应用架构与医疗信息化流程有较高参考价值。目前已有928人学习下载开发者可据此研究后台模块组织方式、数据库表结构与权限逻辑并在此基础上按诊所实际业务进行定制扩展。1. 从一份 PHP 中医诊所后台说起谁在用它能解决什么前阵子帮一个做中医理疗的朋友看店他抱怨每天用 Excel 记患者档案、开方记录和药材库存月底对账时经常发现某味药材的库存对不上患者复诊时翻半天找不到上次的方子。这种场景其实很典型——中小型中医诊所、养生馆、理疗店业务量不大但手工记账的混乱程度一点不比大医院少。这份 PHP 开源中医诊所后台管理源码就是冲着这类需求去的患者建档、就诊记录、处方管理、药材库存、收费统计一套后台把日常运营的几件事串起来还自带数据库文件省去了自己设计表结构的功夫。它适合谁懂一点 PHP 和 MySQL、想给诊所或养生馆搭个内部管理系统的开发者或者拿它当毕业设计、课程作业的底座来二次开发。技术栈不花哨PHP MySQL 原生或轻量框架部署门槛低改起来也顺手。2. 技术栈与目录结构先看清这套后台的骨架2.1 技术选型为什么是 PHP MySQL 而不是别的拿到源码包第一件事不是急着往服务器上扔而是先看它用了什么。这套中医诊所后台的技术栈很明确服务端 PHP数据库 MySQL前端大概率是 Bootstrap 或类似的轻量 UI 框架配合 jQuery 做交互。为什么这类项目普遍选 PHP原因很实际——中小诊所的服务器预算有限虚拟主机就能跑PHP 环境几乎零配置MySQL 更是标配数据量不大单库足够撑住几千到几万条患者记录。从项目标题里的「带数据库」能推断出压缩包里应该包含.sql文件导入后表结构、初始数据都有了。常见做法是config.php或database.php里集中配置数据库连接用 PDO 或 mysqli 做数据访问。我一般会先打开这个配置文件确认连接方式再决定后续怎么改。提示如果源码里用的是 mysqli 而不是 PDO二次开发时建议逐步替换成 PDO预处理语句对防 SQL 注入更友好尤其是患者姓名、电话这类用户输入字段。2.2 目录结构从入口文件到模块划分虽然没看到实际文件列表但这类 PHP 后台的目录结构有规律可循。典型的布局是这样/ 根目录 ├── index.php # 登录入口或仪表盘 ├── config.php # 数据库连接与全局配置 ├── /admin # 后台模块目录 │ ├── patient.php # 患者管理 │ ├── visit.php # 就诊记录 │ ├── prescription.php # 处方管理 │ ├── stock.php # 药材库存 │ └── report.php # 统计报表 ├── /includes # 公共函数与头部尾部 │ ├── header.php │ ├── footer.php │ └── functions.php ├── /assets # CSS、JS、图片 └── clinic.sql # 数据库导出文件这个结构的好处是模块清晰每个业务功能一个文件改哪个模块就动哪个文件不会牵一发动全身。includes/functions.php通常是公共函数集中地比如权限校验、数据过滤、分页逻辑二次开发时优先看这里能省不少重复代码。2.3 数据库表设计核心几张表的关系「带数据库」意味着表结构已经定好了。中医诊所后台的核心表一般包括表名用途关键字段patients患者档案id, name, gender, phone, age, created_atvisits就诊记录id, patient_id, visit_date, doctor, symptomsprescriptions处方id, visit_id, herbs, dosage, notesherbs药材库存id, name, stock_qty, unit, priceusers后台用户id, username, password, rolevisits通过patient_id关联patientsprescriptions通过visit_id关联visits这样一次就诊对应一张处方患者复诊时能按时间线拉出全部记录。药材库存单独一张表处方里的药材用量可以反向扣减库存——这是很多简易系统容易漏掉的一环如果源码里没做二次开发时值得补上。3. 跑起来从导入数据库到登录后台的完整步骤3.1 环境准备PHP 版本与扩展检查在部署之前先确认环境。PHP 建议 7.4 以上8.x 也能跑但要注意有些老代码用了mysql_*函数在 PHP 7 之后已经移除如果源码里出现这类函数得改成 mysqli 或 PDO。MySQL 用 5.7 或 8.0 都行字符集选utf8mb4避免中文姓名或药材名出现乱码。需要开启的扩展pdo_mysql或mysqli、mbstring、json。如果是宝塔面板或类似环境这些默认都有不用额外折腾。本地开发的话用 XAMPP 或 phpStudy 一键装好把源码丢进htdocs或www目录即可。3.2 导入数据库命令行与图形化两种方式拿到.sql文件后先建库再导入。命令行方式# 登录 MySQL mysql -u root -p # 创建数据库字符集用 utf8mb4 CREATE DATABASE clinic_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; # 退出后导入 mysql -u root -p clinic_db clinic.sql如果习惯图形化工具phpMyAdmin 或 Navicat 里新建数据库右键「运行 SQL 文件」选clinic.sql导入即可。导入完成后用SHOW TABLES;确认表都建好了再SELECT * FROM users;看看默认管理员账号是什么——通常源码说明里会写或者密码字段是 MD5 加密的初始值。注意导入前先看一眼.sql文件头部有没有CREATE DATABASE语句。如果有直接导入就行如果没有必须先手动建库否则会报「No database selected」。3.3 配置连接与登录测试数据库导入后改config.php里的连接参数?php // 数据库配置 define(DB_HOST, localhost); define(DB_USER, root); // 改成你的 MySQL 用户名 define(DB_PASS, your_password); // 改成你的密码 define(DB_NAME, clinic_db); // 刚才建的库名 // 连接数据库 $conn new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME); if ($conn-connect_error) { die(连接失败: . $conn-connect_error); } $conn-set_charset(utf8mb4); // 防止中文乱码 ?这段代码做了三件事定义常量方便统一修改、建立 mysqli 连接、设置字符集。改完保存浏览器访问http://localhost/你的目录/index.php应该能看到登录页。用默认账号登录如果提示密码错误去users表里看password字段可能是 MD5 值用在线工具反查或直接改成明文再 MD5 一次。3.4 常见启动失败排查跑不起来的情况无非几种白屏、报错、登录后跳回登录页。白屏通常是 PHP 报错被关了显示在php.ini里把display_errors设为On或者代码顶部加ini_set(display_errors, 1); error_reporting(E_ALL);。报「Access denied」就是数据库账号密码不对。登录后跳回登录页多半是 session 没配好检查session_start()有没有在输出前调用或者session.save_path是否可写。4. 核心模块拆解患者建档、处方管理与库存扣减4.1 患者管理从表单提交到入库的完整链路患者管理是使用频率最高的模块。一个典型的添加患者流程前端表单提交姓名、性别、电话、年龄后端接收后先做数据清洗再插入patients表。看一段简化后的代码?php // patient_add.php 处理患者新增 session_start(); include config.php; // 接收表单数据 $name trim($_POST[name] ?? ); $gender $_POST[gender] ?? 未知; $phone trim($_POST[phone] ?? ); $age intval($_POST[age] ?? 0); // 简单校验姓名和电话不能为空 if ($name || $phone ) { die(姓名和电话为必填项); } // 预处理插入防止 SQL 注入 $stmt $conn-prepare(INSERT INTO patients (name, gender, phone, age, created_at) VALUES (?, ?, ?, ?, NOW())); $stmt-bind_param(sssi, $name, $gender, $phone, $age); if ($stmt-execute()) { header(Location: patient_list.php?msg添加成功); } else { echo 添加失败: . $stmt-error; } $stmt-close(); $conn-close(); ?这段代码的关键点trim去掉首尾空格intval强制年龄为整数preparebind_param做参数绑定。sssi表示四个参数类型分别是字符串、字符串、字符串、整数。很多新手直接拼 SQL 字符串遇到患者姓名里带单引号就翻车预处理是必须的。created_at用NOW()由数据库生成避免 PHP 和 MySQL 时间不一致。4.2 处方管理关联就诊记录与药材明细处方模块比患者管理复杂一层因为它要关联就诊记录还要处理药材明细。常见做法是两张表prescriptions存处方主信息关联visit_idprescription_items存每味药材的用量。但简易版可能把药材直接存成文本字段比如「黄芪 15g当归 10g」。如果源码是后者二次开发时建议拆成明细表方便统计药材用量和扣库存。看一段处方保存的逻辑?php // prescription_save.php session_start(); include config.php; $visit_id intval($_POST[visit_id]); $herbs $_POST[herbs]; // 数组药材名 $dosages $_POST[dosages]; // 数组对应用量 $notes trim($_POST[notes] ?? ); // 开启事务保证主表和明细表同时成功 $conn-begin_transaction(); try { // 插入处方主记录 $stmt $conn-prepare(INSERT INTO prescriptions (visit_id, notes, created_at) VALUES (?, ?, NOW())); $stmt-bind_param(is, $visit_id, $notes); $stmt-execute(); $prescription_id $conn-insert_id; // 拿到刚插入的处方 ID // 循环插入药材明细 $itemStmt $conn-prepare(INSERT INTO prescription_items (prescription_id, herb_name, dosage) VALUES (?, ?, ?)); for ($i 0; $i count($herbs); $i) { $herb trim($herbs[$i]); $dosage trim($dosages[$i]); if ($herb ) continue; // 跳过空行 $itemStmt-bind_param(iss, $prescription_id, $herb, $dosage); $itemStmt-execute(); } $conn-commit(); header(Location: prescription_list.php?msg处方已保存); } catch (Exception $e) { $conn-rollback(); echo 保存失败: . $e-getMessage(); } ?事务是这里的关键。处方主表和明细表必须同时写入成功否则会出现「有处方没明细」的脏数据。insert_id拿到刚插入的主键再作为外键写入明细表。循环里跳过空行避免用户误提交空药材。如果源码里没用事务二次开发时补上这是数据一致性的基本保障。4.3 库存扣减处方开出后药材数量怎么变库存扣减是中医诊所后台里最容易出 bug 的地方。理想流程是处方保存成功后根据药材明细扣减herbs表的stock_qty。但实际操作中药材名称可能和库存表里的名称不完全匹配比如处方写「黄芪」库存表里是「炙黄芪」直接扣减就会失败。常见做法是加一个名称匹配逻辑或者让开方时从库存表下拉选择而不是手输。如果源码里没做二次开发时可以加一个「库存校准」功能定期对比处方用量和库存余量。扣减代码大致这样?php // 在处方保存成功后调用 function deductStock($conn, $herbName, $dosage) { // 从用量字符串里提取数字比如 15g - 15 preg_match(/(\d)/, $dosage, $matches); $qty intval($matches[1] ?? 0); if ($qty 0) return; // 扣减库存同时保证不会扣成负数 $stmt $conn-prepare(UPDATE herbs SET stock_qty stock_qty - ? WHERE name ? AND stock_qty ?); $stmt-bind_param(isi, $qty, $herbName, $qty); $stmt-execute(); if ($stmt-affected_rows 0) { // 库存不足或药材名不匹配记录日志 error_log(库存扣减失败: {$herbName} 用量 {$qty}); } } ?stock_qty ?这个条件很重要防止库存扣成负数。affected_rows 0说明要么药材名对不上要么库存不够记日志方便后续排查。如果源码里没有这个逻辑建议加上不然月底盘点时库存对不上又是一笔糊涂账。5. 避坑与常见问题部署和二次开发中容易翻车的地方5.1 中文乱码从数据库到页面的字符集链路现象患者姓名显示成「??」或「æŽå°」。原因通常是某一环字符集不统一——数据库建库时用了latin1或者连接时没设utf8mb4或者 PHP 文件本身不是 UTF-8 编码。解决建库用utf8mb4连接后执行$conn-set_charset(utf8mb4)PHP 文件保存为无 BOM 的 UTF-8。三处都对齐乱码基本消失。5.2 登录 session 丢失为什么登录后跳回登录页现象输入正确账号密码点登录后又回到登录页。原因通常是session_start()没有在页面最顶部调用或者session.save_path目录不可写。解决确保session_start()在任何输出之前检查php.ini里的session.save_path路径是否存在且可写。如果是虚拟主机可能需要在代码里指定session_save_path(./tmp)。5.3 SQL 注入患者姓名里带单引号就报错现象添加患者时姓名填「OBrien」提交后报 SQL 语法错误。原因是用字符串拼接 SQL单引号破坏了语句结构。解决所有用户输入都用preparebind_param处理。如果源码里还有拼接的地方逐个改成预处理这是安全底线。5.4 库存扣减失败药材名对不上导致库存不变现象处方开出去了但库存数量没变。原因通常是处方里的药材名和库存表里的名称不完全一致或者扣减逻辑根本没写。解决开方时从库存表下拉选择药材避免手输扣减时加日志记录哪些药材没匹配上定期人工校准。5.5 权限控制缺失普通用户能删患者记录现象用普通账号登录后居然能删除患者或修改处方。原因源码可能只做了登录校验没做角色权限。解决在users表里加role字段删除、修改等敏感操作前判断$_SESSION[role] admin。如果源码里没有权限模块这是二次开发优先补的功能。6. 二次开发从能用变成好用几个改造方向这套后台的底子不错但要用得顺手还得按自己的业务改。第一个方向是权限细化。源码可能只有「登录/未登录」两种状态实际诊所里前台、医生、库管、管理员的操作范围完全不同。在users表加role字段includes/functions.php里加一个checkRole($allowed)函数每个敏感页面顶部调用不匹配就跳转。这个改动不大但能避免很多误操作。第二个方向是处方模板。中医开方有常用组合比如「四君子汤」「六味地黄丸」的加减每次手输药材效率低。加一张prescription_templates表存模板名称和药材明细开方时一键载入再微调用量。这个功能对老中医特别友好能省不少敲键盘的时间。第三个方向是数据导出。诊所经常需要把某段时间的就诊记录导出来做统计或上报。用 PHP 的fputcsv生成 CSV或者引入 PhpSpreadsheet 生成 Excel按日期范围查询visits和prescriptions导出成表格。代码不复杂但实用价值高。第四个方向是库存预警。在herbs表加一个min_qty字段库存低于这个值时后台首页显示提醒。查询逻辑很简单SELECT * FROM herbs WHERE stock_qty min_qty结果渲染成红色列表。这个小功能能帮诊所避免「开方时才发现没药」的尴尬。改造时有个习惯我一直保持每次改完一个模块先在本地用测试数据跑一遍完整流程从添加患者到开方到扣库存确认数据链路没断。有次改库存扣减逻辑忘了处理药材名带空格的情况结果测试时发现「黄芪 」和「黄芪」匹配不上库存没扣。从那以后我每次改数据库相关代码都强制走一遍「增删改查 边界值」的测试流程。希望这些经验帮到你拿到源码后先跑通再按自己的业务慢慢磨。本文还有配套的精品资源点击获取