news 2026/10/8 19:56:05

医药管理系统源码实战:从部署到二次开发的进销存核心设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
医药管理系统源码实战:从部署到二次开发的进销存核心设计

简介:这是一套基于Java Web技术栈的医药管理系统后台源码,面向计算机专业学生、课程设计开发者及需要练手SSM/JSP项目的初学者,可帮助快速搭建药品进销存管理场景。系统围绕药品、类别、库存与销售展开,实现了添加与查看药品、高级查询、库存查看、类别添加与统计、购买药品、销售管理、进货需求管理及系统管理等模块,业务链路较为完整。压缩包共162个文件,约9.34MB,以31个jsp页面、30个java源文件、30个class编译文件、26个jar依赖包及12个xml配置为主,另含sql建库脚本、properties配置与少量图片资源,结构清晰便于二次开发。开发环境为JDK1.8搭配MySQL数据库,导入后即可对照源码理解分层设计与数据库交互逻辑。目前已有2058人学习下载,适合作为课程设计、毕业设计或Java Web入门实战的参考案例。

1. 医药管理系统源码.zip:一套能跑起来的进销存骨架长什么样

很多做企业信息化的朋友拿到「医药管理系统源码.zip」的第一反应是解压看目录,然后被几十个文件夹和上百个 PHP 文件劝退。我最初接触这类系统是在给一家区域连锁药房做二次开发时,当时的需求很明确:药品批号要能追溯、近效期要能预警、GSP 规范里的首营企业和首营品种要能留痕。市面上的成品 SaaS 按门店收年费,数据还不在自己手里,于是转向源码自建这条路。

医药管理系统源码本质上是一套面向药店、医药批发或门诊药房的进销存系统,核心模块通常包括药品档案、采购入库、销售出库、库存盘点、批号效期管理和基础报表。它和普通商品进销存的区别在于:药品有批号、有效期、批准文号、生产厂家这些强监管字段,库存不能只记数量,还要记批次。这套源码适合有 PHP 或 Java 基础、想自己掌控数据和二次开发节奏的团队,也适合作为课程设计或毕业设计的参考工程。下面我按实际落地顺序,把选型、部署、核心表结构和踩坑点拆开讲。

2. 拿到源码先别急着跑:技术栈盘点和环境选型

2.1 从目录结构判断这套源码的技术栈

解压后先看根目录,不要急着找 index.php。我一般按这个顺序扫一遍:有没有composer.json或pom.xml,有没有application或app目录,有没有sql文件夹,有没有README或install目录。这四个位置基本能确定技术栈和部署方式。

常见的医药管理系统源码分两类。一类是 ThinkPHP 或 Laravel 写的 PHP 项目,目录里会有application、public、think或artisan;另一类是 SpringBoot 写的 Java 项目,会有src/main/java、pom.xml、application.yml。热搜词里「php源码」和「java课程设计案例源码」出现频率很高,说明这两类都是主流。判断清楚再选环境,别用 PHP 的环境去跑 Java 包。

# 查看根目录关键文件,快速判断技术栈 ls -la # 有 composer.json -> PHP 项目 # 有 pom.xml 或 build.gradle -> Java 项目 # 有 package.json 且无后端框架 -> 可能只是前端模板 find . -maxdepth 2 -name "*.sql" -o -name "application.yml" -o -name "config.php"

这段命令的作用是快速定位配置文件和数据库脚本。-maxdepth 2限制搜索深度,避免在大目录里卡住。如果找到.sql文件,说明作者提供了建表脚本,这是最省事的情况;如果只有.sql的碎片或没有,就要从实体类反推表结构,工作量会大很多。

2.2 PHP 和 Java 两条路的环境准备

如果确认是 PHP 项目,推荐 PHP 7.4 到 8.1 之间,MySQL 5.7 或 8.0。PHP 8.2 以上有些老源码会因为动态属性废弃而报错,这是血泪经验。用宝塔或 phpstudy 搭环境最快,但生产环境我建议手动配 Nginx + PHP-FPM,因为医药系统涉及数据合规,面板工具的默认配置往往开了不必要的端口。

Java 项目则看pom.xml里的spring-boot-starter-parent版本。如果是 2.x,JDK 用 8 或 11;如果是 3.x,JDK 必须 17 以上。数据库连接池常见 Druid 或 HikariCP,配置文件里搜url:就能找到数据库地址。下面是一个典型的 PHP 项目数据库配置片段,改完就能连库。

// config/database.php 常见结构 return [ 'type' => 'mysql', 'hostname' => '127.0.0.1', 'database' => 'medicine_db', // 数据库名,按 sql 脚本里的 CREATE DATABASE 填 'username' => 'med_user', // 不要用 root,单独建账号 'password' => 'YourStrongPwd', // 生产环境必须强密码 'hostport' => '3306', 'charset' => 'utf8mb4', // 药品名有生僻字,必须 utf8mb4 'prefix' => 'med_', // 表前缀,和 sql 脚本保持一致 ];

参数说明:charset必须是utf8mb4,因为药品通用名里有「䓬」「啶」这类字,utf8存进去会变问号。prefix要和导入 SQL 时的表前缀一致,不一致会报「表不存在」。username单独建账号并只授权该库,避免源码里有后门时被拖全库。

2.3 导入数据库和首次访问的完整流程

建库、导表、改配置、配伪静态,这四步顺序不能乱。先建一个空库,字符集选utf8mb4_general_ci,然后用命令行导入 SQL,不要用 phpMyAdmin 导大文件,容易超时截断。

# 1. 建库 mysql -u root -p -e "CREATE DATABASE medicine_db DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci;" # 2. 导入表结构和初始数据 mysql -u root -p medicine_db < install/medicine_db.sql # 3. 检查表数量,正常医药系统在 30 到 60 张表之间 mysql -u root -p medicine_db -e "SHOW TABLES;" | wc -l

导入后如果表数量明显偏少,比如只有十几张,说明 SQL 脚本不完整,可能作者只导了部分表。这时候要去install目录或doc目录找有没有分卷的 SQL 文件。首次访问一般是http://你的域名/install或http://你的域名/public/index.php,具体看框架。ThinkPHP 的入口在public下,Nginx 要配伪静态,否则除首页外全是 404。

提示:部署完成后第一件事是改默认管理员密码,第二件事是删掉install目录。我见过太多源码装完不删安装目录,被人重新跑一遍安装流程直接接管后台。

3. 药品、批号、库存三张核心表怎么设计才算能用

3.1 药品档案表:批准文号和通用名的字段取舍

医药管理系统的数据地基是药品档案表。普通进销存一张goods表就够了,但医药系统必须拆出「药品通用名」「商品名」「批准文号」「生产企业」「剂型」「规格」这些字段。我见过一些源码把通用名和商品名混在一个字段里,结果做 GSP 报表时根本拆不出来。

CREATE TABLE `med_drug` ( `id` int NOT NULL AUTO_INCREMENT, `drug_code` varchar(32) NOT NULL COMMENT '内部编码', `generic_name` varchar(128) NOT NULL COMMENT '通用名,如阿莫西林胶囊', `trade_name` varchar(128) DEFAULT NULL COMMENT '商品名', `approval_number` varchar(64) NOT NULL COMMENT '批准文号,国药准字Hxxxx', `manufacturer` varchar(128) NOT NULL COMMENT '生产企业', `dosage_form` varchar(32) DEFAULT NULL COMMENT '剂型', `spec` varchar(64) DEFAULT NULL COMMENT '规格', `is_prescription` tinyint(1) DEFAULT '0' COMMENT '是否处方药', `storage_condition` varchar(64) DEFAULT NULL COMMENT '储存条件', PRIMARY KEY (`id`), UNIQUE KEY `uk_approval` (`approval_number`), KEY `idx_generic` (`generic_name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

approval_number加唯一索引,因为一个批准文号对应一个药品,重复录入会导致批号追溯混乱。generic_name加普通索引,因为销售开单时按通用名搜索是最高频操作。is_prescription字段决定销售时是否强制登记处方信息,这是医药系统区别于普通商城的硬性逻辑。

3.2 批号库存表:为什么不能只存一个数量

普通库存表存「商品 ID + 数量」,医药系统必须存「药品 ID + 批号 + 生产日期 + 有效期 + 数量」。同一盒阿莫西林,批号 A 和批号 B 的效期不同,出库时要按先进先出或近效期先出。如果源码里库存表没有批号字段,这套系统基本不能用于真实药房。

CREATE TABLE `med_stock_batch` ( `id` bigint NOT NULL AUTO_INCREMENT, `drug_id` int NOT NULL, `batch_no` varchar(64) NOT NULL COMMENT '生产批号', `production_date` date DEFAULT NULL, `expiry_date` date NOT NULL COMMENT '有效期至', `quantity` int NOT NULL DEFAULT '0', `warehouse_id` int NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_drug_batch_wh` (`drug_id`,`batch_no`,`warehouse_id`), KEY `idx_expiry` (`expiry_date`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

uk_drug_batch_wh保证同一药品同一批号在同一仓库只有一条记录,入库时用ON DUPLICATE KEY UPDATE quantity = quantity + ?累加。idx_expiry是近效期预警的查询索引,没有这个索引,效期报表在几万条库存下会慢到不可用。

3.3 入库出库单:主表和明细表的事务边界

采购入库和销售出库都是「一张主单 + 多条明细」的结构。主表存供应商、单据号、总金额、状态;明细表存药品、批号、数量、单价。关键点是:出库时扣减库存和写明细必须在同一个事务里,否则会出现单据写了但库存没扣,或者库存扣了单据没写。

// 出库事务示例,ThinkPHP 风格 Db::startTrans(); try { $orderId = Db::name('sale_order')->insertGetId($orderData); foreach ($items as $item) { // 扣减对应批号库存,quantity >= 出库量才允许 $affected = Db::name('stock_batch') ->where('drug_id', $item['drug_id']) ->where('batch_no', $item['batch_no']) ->where('quantity', '>=', $item['qty']) ->dec('quantity', $item['qty']) ->update(); if (!$affected) { throw new Exception('批号库存不足:' . $item['batch_no']); } Db::name('sale_order_item')->insert($item); } Db::commit(); } catch (Exception $e) { Db::rollback(); return json(['code' => 0, 'msg' => $e->getMessage()]); }

where('quantity', '>=', $item['qty'])这个条件很关键,它把库存检查放在 UPDATE 语句里,利用数据库行锁避免并发超卖。如果先 SELECT 查库存再 UPDATE,两个收银台同时出库同一批号就会扣成负数。dec是 ThinkPHP 的原子递减方法,等价于quantity = quantity - N。

注意:事务里不要做远程调用或文件写入,否则事务持有时间过长,高并发下会锁等待。打印小票、发短信这类操作放到事务提交之后。

4. 从源码到能用的系统:部署、初始化和权限配置

4.1 后台菜单和权限节点怎么对应

医药管理系统的后台通常有管理员、店长、收银员、库管这几个角色。源码里的权限控制一般基于「节点」实现,每个控制器方法对应一个权限节点,角色勾选节点后才有权访问。部署后第一件事是进「权限管理」把菜单和节点对齐,否则会出现菜单看得见但点进去 403 的情况。

角色药品档案采购入库销售出库库存盘点报表
管理员增删改查增删改查增删改查增删改查全部
店长查改增查增查增查本店
收银员只读无增查无无
库管查改增删改查只读增删改查库存类

这张表是我给连锁药房配权限时的实际方案。收银员不给采购权限,库管不给销售改价权限,这是内控基本要求。源码如果只有「超级管理员」一个角色,需要自己在角色表里加记录,并在节点表里补全控制器方法名。

4.2 初始化数据:仓库、供应商、药品分类

系统装好后是空库,直接开单会报错。按这个顺序初始化:先建仓库(至少一个「主仓库」),再建药品分类(处方药、非处方药、中药饮片、医疗器械),再建供应商和客户,最后录药品档案。药品档案可以手工录,也可以用 Excel 导入,源码一般带导入模板。

# 检查初始化数据是否完整 mysql -u med_user -p medicine_db -e " SELECT '仓库' AS type, COUNT(*) AS cnt FROM med_warehouse UNION ALL SELECT '分类', COUNT(*) FROM med_category UNION ALL SELECT '供应商', COUNT(*) FROM med_supplier UNION ALL SELECT '药品', COUNT(*) FROM med_drug;"

如果仓库数量为 0,先去后台「仓库管理」加一条。如果药品为 0 但分类有数据,说明导入模板的列名和表字段没对上,检查 Excel 表头是否包含「通用名」「批准文号」「生产企业」这三列,缺一列都会导入失败。

4.3 近效期预警和批号追溯的验证方法

部署完不算完,要验证两个医药系统的核心能力。第一,近效期预警:录一个有效期在 90 天内的批号,看首页或报表是否出现预警。第二,批号追溯:随便选一个批号,看能不能查到它的入库单、出库单、当前库存和流向客户。

-- 批号全链路追溯查询 SELECT '入库' AS flow, i.order_no, i.create_time, i.quantity FROM med_purchase_item i WHERE i.batch_no = '20240501A' UNION ALL SELECT '出库', o.order_no, o.create_time, o.quantity FROM med_sale_item o WHERE o.batch_no = '20240501A';

这条 SQL 把某批号的入库和出库记录合并展示。如果查出来只有入库没有出库,但库存显示为 0,说明源码的库存扣减逻辑有 bug,可能出库时没写明细或者写错了批号。这种情况在二次开发过的源码里很常见,必须逐单核对。

5. 二次开发避坑:那些让我加班到凌晨的坑

5.1 现象:入库后库存不增加,但单据显示成功

原因:库存表用了drug_id + warehouse_id做唯一键,没有把batch_no纳入唯一键,导致同一药品不同批号互相覆盖。解决:检查med_stock_batch的唯一索引,必须是drug_id + batch_no + warehouse_id三字段联合。如果源码建表时漏了批号,需要改表结构并重算历史库存。

5.2 现象:销售开单搜索药品,输入通用名搜不到

原因:药品档案的generic_name字段没有建索引,或者搜索用的是LIKE '%关键词%'导致全表扫描,数据量一大就超时。解决:先加索引,再把搜索逻辑改成前缀匹配LIKE '关键词%',或者接入全文索引。如果源码用的是FIND_IN_SET存多关键词,建议改成独立搜索表。

5.3 现象:并发出库时库存扣成负数

原因:出库逻辑先SELECT查库存,再UPDATE扣减,两个操作之间没有锁。解决:把库存检查合并进UPDATE的WHERE条件,如第 3 章示例;或者用SELECT ... FOR UPDATE锁行。前者性能更好,后者适合扣减逻辑复杂的场景。

5.4 现象:PHP 8 下后台白屏,日志报动态属性废弃

原因:老源码用了$this->xxx = $yyy动态赋值,PHP 8.2 起标记为废弃,部分框架会抛异常。解决:降级到 PHP 7.4 或 8.1,或者在模型基类加#[\AllowDynamicProperties]注解。生产环境不建议为了跑老源码而长期停留在低版本,最好逐步重构。

5.5 现象:导入 Excel 药品档案,中文全部乱码

原因:Excel 默认编码是 GBK,而数据库是 utf8mb4,导入时没有转码。解决:导入前用iconv('GBK', 'UTF-8', $value)转码,或者要求模板保存为 CSV UTF-8 格式。我一般直接在导入类里统一转码,避免用户操作差异。

6. 让这套源码真正值钱的三个进阶动作

第一个动作是给批号库存加「锁定库存」字段。销售开单时先锁库存,付款后再实扣,取消订单则释放。这样能解决「下单未付款期间库存被其他单占用」的问题。实现上在med_stock_batch加locked_quantity,可用库存等于quantity - locked_quantity,出库时校验可用库存。

第二个动作是把近效期预警做成定时任务,而不是每次打开首页才查。用 crontab 每天凌晨跑一次,把 90 天内到期的批号写进预警表,并推送给库管。这样即使没人登录后台,预警也不会漏。

# 每天凌晨 2 点执行效期扫描 0 2 * * * /usr/bin/php /www/medicine/think expiry:scan >> /var/log/expiry.log 2>&1

第三个动作是给关键操作加审计日志。药品档案修改、库存手动调整、价格改动这三类操作必须记录操作人、时间、修改前后值。GSP 检查时这是必查项,临时补日志根本来不及。源码如果没有审计表,自己加一张med_audit_log,在模型事件里统一写入。

我自己的习惯是:拿到任何一套医药管理系统源码,先跑通「采购入库 → 销售出库 → 批号追溯」这条最小闭环,再谈二次开发。闭环跑不通,后面加再多功能都是空中楼阁。希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/8 19:54:58

Java Web图书管理系统课设实战源码(Servlet+JSP+MySQL)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/8 19:52:46

华为9006C麒麟V10SP1 LiveCD救援指南:不进系统修复与数据备份

简介&#xff1a;这份PDF文档面向具备一定Linux操作基础的技术人员与开发者&#xff0c;针对银河麒麟桌面操作系统V10SP1&#xff08;华为9006C版本&#xff09;进入LiveCD模式这一具体需求&#xff0c;给出可落地的操作指引。当用户希望在不安装系统的前提下体验或测试该系统功…

作者头像 李华
网站建设 2026/10/8 19:52:46

深拷贝与链表排序:LeetCode Hot 100 经典题的指针操作全解析

先声明一下&#xff1a;这两道题我在刷 LeetCode Hot 100 的时候反复遇到&#xff0c;后来在周赛、模拟面试里也经常能瞥见它们的影子。T138 随机链表的复制考的是你对“深拷贝”这件事的理解&#xff0c;以及链表中“指针映射关系”怎么处理&#xff1b;T148 排序链表则是把链…

作者头像 李华
网站建设 2026/10/8 19:51:12

TR101290总结:码流健康度三优先级量化与排障实践

简介&#xff1a;一份围绕数字电视传输标准 TR101290 的技术总结文档&#xff0c;面向音视频开发、数字电视协议分析及嵌入式电视接收调试人群。内容系统梳理 MPEG-2 传输流中的 ES、PES、TS、PS 概念&#xff0c;说明 TS 分组 188 字节结构、PES 与 TS 的封装关系&#xff0c;…

作者头像 李华
网站建设 2026/10/8 19:51:12

基于Python的小学成绩信息管理系统:从Flask到SQLite的全栈开发实战

做毕业设计选 "基于Python的小学成绩信息管理系统" 这个题目的人&#xff0c;十有八九是第一次正儿八经写一个能跑通的全栈项目。很多同学拿到这个题目第一反应是"不就是CRUD嘛"&#xff0c;真上手才发现&#xff0c;光是把成绩数据从Excel里弄进去再查出来…

作者头像 李华
网站建设 2026/10/8 19:51:11

退货季下的连衣裙高退货率:物流应对与逆向链路全解

开门见山说个数字&#xff1a;女士连衣裙退货率接近90%&#xff0c;这已经不是某个品牌的小范围烦恼&#xff0c;而是全球物流业每年都要经历一次的“退货季”里最典型的缩影。我做电商物流这行有些年头了&#xff0c;每年七八月看着退货包裹像潮水一样涌进分拨中心&#xff0c…

作者头像 李华