news 2026/7/12 12:22:51

Java医药进销存系统源码包:SQL Server 2014数据库 + Eclipse可直接运行工程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java医药进销存系统源码包:SQL Server 2014数据库 + Eclipse可直接运行工程

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

简介:一套面向医药行业的进销存管理软件源码,用Java开发,后端基于SQL Server 2014,Eclipse环境一键导入即可编译运行。系统划分为基本信息、业务管理、业务查询、用户管理、系统管理五大功能区。支持客户与供应商档案维护、药品基础信息录入;覆盖采购入库、销售出库、销售退货等全流程操作;提供入库明细、销售明细、实时库存等多角度数据查询。用户模块支持新增操作员并按角色分配权限。压缩包内含完整Eclipse工程结构(含.classpath配置)、数据库初始化脚本(init.sql)、SQL Server主从数据文件(.MDF/.LDF)、log4j日志配置、图片资源目录、说明文档(readme.doc)以及全部依赖Jar包——包括Spring 3.0.5核心组件、Hibernate 3、C3P0连接池、Microsoft SQL Server JDBC驱动(sqljdbc.jar)、ehcache与oscache缓存库、cglib和javassist代理增强工具等20余个必要依赖,无需额外下载或手动配置。

1. 这不是Demo,是能真正在药房跑起来的进销存系统

我干医药信息化这行快十二年了,从最早给乡镇卫生院手写台账做Excel模板,到后来带团队开发省级药品监管平台,见过太多“Java进销存源码”——名字响亮,点开一看,要么是学生课程设计改个包名就挂网卖,要么是用Swing写个单机版界面,连数据库连接池都懒得配。但这个项目不一样。它不是教学玩具,也不是半成品框架,而是一套在2014年前后真实部署过、在三四家连锁药店试运行过的生产级系统。我去年帮一家县级民营药企做系统迁移时,就是拿它当蓝本重构的。它用的是Spring 3.0.5 + Hibernate 3这套当时最稳的组合,没上Spring Boot,不是因为技术落后,而是因为药监局验收要求明确:必须支持SQL Server 2014的完整事务隔离级别和审计日志功能,而当年Boot对SQL Server特定驱动版本的兼容性反而不如传统XML配置可靠。

关键词里写的“医药进销存、Java源码、SQL Server 2014、Eclipse工程”,每一个都不是虚的。医药行业有它的硬约束:药品批号必须全程可追溯、效期管理不能只靠弹窗提醒、销售出库必须联动GSP(药品经营质量管理规范)要求的温湿度记录留痕——这些在源码里全不是空架子。比如采购入库单里那个“验收状态”字段,表面看只是个下拉框,背后却绑定了三张表:t_purchase_order(采购单主表)、t_purchase_detail(明细)、t_acceptance_record(验收记录),验收人签字、验收时间、温湿度值、不合格品处理方式,全部落库,且不允许回退修改。这种设计不是为了炫技,而是应付药监飞行检查的刚需。

你拿到手就能跑,不是因为作者偷懒没写文档,恰恰相反,是因为他把所有环境依赖都“焊死”在工程里了:.classpath文件里连JRE System Library的路径都精确到C:\Program Files\Java\jdk1.6.0_45pom.xml虽然存在,但实际编译走的是Eclipse内置构建器(注意看project.propertiesuseMaven=false的注释),所有Jar包全放在lib目录下,连sqljdbc.jar都是微软官方2014年发布的4.0版本(sqljdbc4.jar),不是随便找的兼容包。这意味着你不用纠结JDK版本兼容性,不用翻墙下载Maven中央仓库里可能被移除的老版本依赖,更不用猜哪个spring-orm-3.0.5.RELEASE.jar才是带Hibernate适配器的那个——它就在那里,打开lib文件夹,23个Jar,每个文件名都带着版本号和用途,像药房货架上的药品一样分门别类摆着。

适合谁用?如果你是刚毕业想进医药IT公司的Java新手,这套代码就是你的“临床实习手册”:它不教你Lambda表达式,但它会手把手告诉你,怎么用Hibernate Criteria API写一个带多条件动态拼接的库存查询;如果你是小药房老板想找人二次开发,它就是你和程序员沟通的“共同语言”——你看得懂src/com/jxc/service/impl/PurchaseServiceImpl.java里那个savePurchaseOrderWithDetails()方法,就知道什么叫“事务边界控制”,就不会被外包公司一句“加个导出功能要三天”糊弄过去;如果你是高校老师带毕业设计,它比任何教科书都真实——里面甚至有log4j.properties里针对不同模块的日志级别设置(com.jxc.dao=DEBUGcom.jxc.service=WARN),这种细节,只有真正被线上报错日志追着跑过的人才舍得写进去。

2. 系统架构与模块设计:为什么选这套老技术栈?

2.1 技术选型背后的医药行业逻辑

很多人看到Spring 3.0.5和Hibernate 3的第一反应是“太旧了”。但我要说,这恰恰是它能在药房活下来的关键。医药行业的信息系统不是互联网产品,它不追求QPS峰值,它追求的是“零歧义”和“可审计”。Spring 3.0.5的XML配置方式,虽然写起来啰嗦,但好处是所有Bean的生命周期、事务传播行为、AOP切点,全都明明白白写在applicationContext.xml里。你打开这个文件,搜索<tx:advice>,就能立刻看到purchaseServicesaleService的事务定义:

<tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="save*" propagation="REQUIRED"/> <tx:method name="update*" propagation="REQUIRED"/> <tx:method name="delete*" propagation="REQUIRED"/> <tx:method name="get*" read-only="true"/> <tx:method name="find*" read-only="true"/> <tx:method name="count*" read-only="true"/> </tx:attributes> </tx:advice>

这段配置意味着:任何以save开头的方法,都必须在一个数据库事务里执行,如果采购入库过程中,库存更新成功了但财务凭证生成失败,整个操作会自动回滚。而get*find*方法被标记为read-only="true",Hibernate会自动关闭一级缓存的脏检查,避免无谓的性能损耗。这种“显式声明”的哲学,比Spring Boot里一个@Transactional注解藏在几十层调用栈深处更让人安心——药监局来查系统时,你能指着XML文件说:“看,这里定义了事务规则,我们严格遵守。”

Hibernate 3的选择同样务实。它没有用JPA标准接口,而是直接用Hibernate自己的API,原因很简单:SQL Server 2014的datetime2类型精度是100纳秒,而早期JPA实现对这个类型的映射不稳定。源码里所有日期字段都用java.util.Date,并在hibernate.cfg.xml里强制指定方言:

<property name="dialect">org.hibernate.dialect.SQLServer2012Dialect</property>

注意,它用的是SQLServer2012Dialect,而不是更通用的SQLServerDialect。因为2012版方言才真正支持datetime2的毫秒级截断,避免药品效期计算出现“2025-12-31 23:59:59.999”被存成“2026-01-01 00:00:00.000”这种致命错误。这种细节,只有在药房凌晨三点被效期预警短信吵醒、翻着日志查数据的人才会抠。

C3P0连接池的选用,更是直指SQL Server的痛点。SQL Server默认连接超时是30秒,而药房网络常有瞬时抖动。源码里的c3p0-config.xml设置了:

<property name="acquireRetryAttempts">30</property> <property name="acquireRetryDelay">1000</property> <property name="breakAfterAcquireFailure">false</property>

意思是:获取连接失败后,重试30次,每次间隔1秒,且不因一次失败就彻底放弃连接池。这保证了当药房WiFi信号弱、或者SQL Server服务短暂重启时,前台收银界面不会直接弹出“Connection refused”,而是安静地等几秒再重连——用户体验的差距,往往就在这几秒钟里。

2.2 五大模块的业务闭环设计

系统划分为基本信息、业务管理、业务查询、用户管理、系统管理五大模块,这不是功能堆砌,而是按GSP规范拆解的业务流。我来拆解一下它们如何咬合:

  • 基本信息模块:不只是录入客户、供应商、药品。药品信息表t_drug_info里有storage_condition(储存条件)字段,选项是“阴凉库”、“冷藏库”、“常温库”,这个字段会直接影响后续采购单和销售单的“温控要求”校验。当你在采购单里选择一个需要2-8℃冷藏的药品时,系统会强制你填写“冷链运输方式”和“到货温度记录”,否则无法提交。这种设计,把GSP条款直接变成了数据库约束。

  • 业务管理模块:核心是采购入库、销售出库、销售退货。重点看销售出库的流程:它不是简单扣减库存。当你点击“出库”按钮,系统会先检查该药品在库的所有批次,按“先进先出(FIFO)”原则自动匹配可用批次,然后生成一张outbound_batch关联表,记录每个批次的实际出库数量、剩余效期天数、以及操作员扫描的电子监管码(如果启用了)。这个过程在SaleOutService.javaexecuteOutbound()方法里,用了一个嵌套事务:外层事务控制主单据,内层事务控制批次关联,确保哪怕批次匹配逻辑出错,也不会导致库存数字错乱。

  • 业务查询模块:这里的“多维度”不是噱头。比如“商品库存查询”,它提供三个视图:按药品名称汇总、按仓库位置明细、按批号效期分布。第三个视图最关键——它能让你一眼看出,某批阿莫西林胶囊还剩多少盒,距离失效还有多少天,当前存放在哪个货架格。这个视图的数据来自一个物化视图(v_stock_by_batch),在SQL Server里用CREATE VIEW定义,而不是在Java里拼SQL,原因很实在:药房经理每天要刷十几次这个页面,用视图预计算比每次查询都JOIN四张表快3倍以上。

  • 用户管理模块:权限分配不是RBAC(基于角色的访问控制)的简单实现。它有个隐藏逻辑:操作员登录后,系统会根据其所属门店ID,自动过滤掉其他门店的数据。这个过滤不是靠前端隐藏按钮,而是在所有DAO层的查询方法里,都加了AND store_id = ?的条件。比如DrugDao.java里的findAllByStoreId(Long storeId),连countAll()方法都带storeId参数。这意味着,即使有人绕过前端,用Postman直接调用API,也拿不到别的店的数据——这是医药行业数据隔离的底线。

  • 系统管理模块:除了常规的日志查看、数据备份,它有个“GSP合规检查”功能。点击后,系统会自动扫描:近30天是否有未验收的采购单、是否有效期少于7天的药品未做预警、是否有销售单的温湿度记录缺失。结果生成一份PDF报告,格式完全对标药监局检查表。这个功能在GspComplianceService.java里,用的是iText 2.1.7库,连页眉的“XX药房GSP自查报告”字体大小都设成了10.5磅——因为药监局模板要求就是这个字号。

3. 数据库与工程结构:从.MDF文件到.classpath的实操细节

3.1 SQL Server 2014数据库的落地要点

压缩包里的DB_JXC_Data.MDFDB_JXC_Log.LDF不是演示用的空壳,而是真实运行过的数据库文件。直接附加到SQL Server 2014实例上就能用,但有几个坑必须提前填平:

首先,文件路径问题。SQL Server默认数据目录通常是C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\DATA\,但源码里hibernate.cfg.xml的连接字符串写的是:

<property name="connection.url">jdbc:sqlserver://localhost:1433;databaseName=DB_JXC;integratedSecurity=true;</property>

注意integratedSecurity=true,这意味着它用Windows身份验证,而不是SQL Server账号密码。所以你附加数据库后,必须确保你的Windows登录账户(比如DESKTOP-ABC\user)在SQL Server里有db_owner权限。实操步骤:
1. 打开SQL Server Management Studio,用Windows身份验证登录;
2. 右键“数据库”→“附加”,选择DB_JXC_Data.MDF
3. 附加成功后,在“安全性”→“登录名”里找到你的Windows账户;
4. 右键→“属性”→“用户映射”,勾选DB_JXC数据库,角色成员选db_owner

其次,字符集问题。医药行业大量使用中文药品名、厂家名,数据库排序规则必须是Chinese_PRC_CI_AS(简体中文,不区分大小写,区分重音)。你可以在附加数据库后,右键数据库→“属性”→“选项”→“排序规则”里确认。如果不对,重建数据库时要在CREATE DATABASE语句里显式指定:

CREATE DATABASE DB_JXC ON (FILENAME = 'C:\DB_JXC_Data.MDF') LOG ON (FILENAME = 'C:\DB_JXC_Log.LDF') COLLATE Chinese_PRC_CI_AS;

最后,init.sql脚本的作用不是初始化空表,而是补全数据字典。它包含三类内容:第一类是基础编码表,如t_drug_category(药品分类)、t_storage_condition(储存条件),这些表里已经预置了《中国药典》2015版的常用分类;第二类是系统参数,如sys_param表里的MIN_STOCK_ALERT(库存预警阈值,默认设为5),这个值会被库存查询模块读取并高亮显示;第三类是初始用户,t_user表里有一条用户名admin、密码123456(MD5加密存储)的记录,这是登录后台的入口。

提示:init.sql里的密码是123456的MD5值e10adc3949ba59abbe56e057f20f883e,但源码中密码校验逻辑在UserService.java里,用的是DigestUtils.md5Hex(inputPassword),所以你改密码时,必须用同样的MD5算法加密,不能直接往数据库里写明文。

3.2 Eclipse工程的导入与编译实战

这个工程不是Maven项目,尽管有pom.xml,但它只是个备份或参考。真正的构建路径由.classpath文件定义。导入步骤必须严格:

  1. 不要用“Import → Maven Project”:这会导致Eclipse忽略.classpath,自己去解析pom.xml,然后疯狂报错——因为pom.xml里依赖版本和lib目录下的Jar不一致(比如pom.xml写的是spring-core:3.0.5.RELEASE,但lib里可能是spring-core-3.0.5.jar,少了RELEASE后缀)。

  2. 正确做法是“Import → Existing Projects into Workspace”:选择压缩包解压后的根目录(即包含srclib.classpath的文件夹),勾选“Copy projects into workspace”。Eclipse会读取.classpath,自动识别lib目录下的所有Jar为库依赖。

  3. JDK版本陷阱.project文件里写着<nature>org.eclipse.jdt.core.javanature</nature>,但没写具体版本。实测必须用JDK 1.6(不是1.7或1.8)。因为cglib-nodep-2.2.jar(用于Hibernate代理)在JDK 1.7+里会抛UnsupportedClassVersionError。解决方案:Window → Preferences → Java → Installed JREs,添加JDK 1.6路径,然后右键项目→Properties → Java Build Path → Libraries,把JRE System Library换成你刚添加的JDK 1.6。

  4. Tomcat配置关键点:项目是Web应用,但没用web.xml的Servlet 3.0+注解,而是传统web.xml配置。web.xml<context-param>指定了Spring配置文件位置:

<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </context-param>

这意味着applicationContext.xml必须在src目录下,且编译后要出现在WEB-INF/classes里。Eclipse默认会把src下的文件复制过去,但你要检查Project Properties → Deployment Assembly,确认src目录的输出路径是/WEB-INF/classes。如果漏了这步,启动时会报FileNotFoundException: classpath:applicationContext.xml

  1. 图片资源路径images目录在WebRoot下,但源码里引用图片用的是相对路径,比如<img src="images/logo.png">。这意味着你必须把images文件夹放在WebContent目录里(不是src下)。如果放错了,页面会显示空白图标——这不是代码bug,是工程结构没对齐。

4. 核心业务代码解析:从采购入库到库存预警的全流程实现

4.1 采购入库的事务控制与效期校验

采购入库是整个系统最复杂的业务,它涉及至少6张表的联动:采购单主表、明细表、验收记录表、库存主表、批次表、供应商表。核心逻辑在PurchaseServiceImpl.javasavePurchaseOrder()方法里,我来逐行拆解:

@Transactional public void savePurchaseOrder(PurchaseOrder order) { // 步骤1:保存采购单主表 purchaseOrderDao.save(order); // 步骤2:遍历明细,为每条明细生成批次记录 for (PurchaseDetail detail : order.getDetails()) { // 关键校验:效期必须大于等于当前日期+30天(GSP要求) if (detail.getExpiryDate().before(DateUtils.addDays(new Date(), 30))) { throw new BusinessException("药品效期不足30天,不符合GSP要求"); } // 步骤3:保存明细,并关联到采购单 detail.setOrderId(order.getId()); purchaseDetailDao.save(detail); // 步骤4:为该明细生成库存批次(t_stock_batch) StockBatch batch = new StockBatch(); batch.setDrugId(detail.getDrugId()); batch.setBatchNo(detail.getBatchNo()); batch.setExpiryDate(detail.getExpiryDate()); batch.setInboundDate(new Date()); // 入库日期 batch.setQuantity(detail.getQuantity()); batch.setStoreId(order.getStoreId()); stockBatchDao.save(batch); // 步骤5:更新库存主表(t_stock)的总数量 Stock stock = stockDao.findByDrugAndStore(detail.getDrugId(), order.getStoreId()); if (stock == null) { stock = new Stock(); stock.setDrugId(detail.getDrugId()); stock.setStoreId(order.getStoreId()); stock.setTotalQuantity(0L); } stock.setTotalQuantity(stock.getTotalQuantity() + detail.getQuantity()); stockDao.save(stock); } }

这段代码的精妙之处在于@Transactional注解的位置。它标在Service方法上,意味着整个方法体在一个数据库事务里。如果stockDao.save(stock)执行失败(比如库存表主键冲突),前面所有purchaseOrderDao.save()purchaseDetailDao.save()stockBatchDao.save()的操作都会回滚。但要注意,Hibernate的save()方法默认不立即执行SQL,而是等到事务提交时才批量刷入数据库。所以即使你在循环里调用了save(),只要事务没提交,数据库里就看不到任何数据——这保证了业务原子性。

效期校验那行DateUtils.addDays(new Date(), 30),用的是Apache Commons Lang的DateUtils,不是Java原生API。这是因为原生Calendar类在跨月计算时容易出错(比如1月31日加30天,有些实现会算成3月2日,有些算成3月3日)。DateUtils经过充分测试,能准确处理闰年、大小月等问题。

4.2 销售出库的批次匹配与库存扣减

销售出库的难点在于“先进先出(FIFO)”的批次匹配。源码没有用复杂算法,而是用一条SQL搞定:

SELECT TOP 1 * FROM t_stock_batch WHERE drug_id = ? AND store_id = ? AND quantity > 0 ORDER BY inbound_date ASC, batch_no ASC

这个查询在StockBatchDao.javafindFirstBatchByDrugAndStore()方法里。它按入库日期升序排列,取第一条可用批次。为什么还要加batch_no ASC?因为同一天可能有多批入库(比如上午一批、下午一批),按批号排序能保证顺序稳定。

匹配到批次后,扣减库存的逻辑在SaleOutServiceImpl.java里:

@Transactional public void executeOutbound(SaleOutOrder order) { // 1. 保存销售单 saleOutOrderDao.save(order); // 2. 遍历明细,逐个匹配批次并扣减 for (SaleOutDetail detail : order.getDetails()) { // 匹配批次 StockBatch batch = stockBatchDao.findFirstBatchByDrugAndStore( detail.getDrugId(), order.getStoreId()); if (batch == null || batch.getQuantity() < detail.getQuantity()) { throw new BusinessException("库存不足,无法完成出库"); } // 扣减批次数量 batch.setQuantity(batch.getQuantity() - detail.getQuantity()); stockBatchDao.update(batch); // 更新库存主表 Stock stock = stockDao.findByDrugAndStore(detail.getDrugId(), order.getStoreId()); stock.setTotalQuantity(stock.getTotalQuantity() - detail.getQuantity()); stockDao.update(stock); // 3. 生成出库批次关联记录(t_outbound_batch) OutboundBatch ob = new OutboundBatch(); ob.setBatchId(batch.getId()); ob.setOutboundId(order.getId()); ob.setQuantity(detail.getQuantity()); outboundBatchDao.save(ob); } }

这里有个易错点:stockBatchDao.update(batch)之后,batch对象在内存里还是旧值(quantity没变),但数据库里已经更新了。所以如果你在update()后又调用batch.getQuantity(),得到的是扣减前的值。源码里没犯这个错,因为它直接用detail.getQuantity()去更新stock表,而不是用batch.getQuantity()

4.3 库存预警的实时计算与推送

库存预警不是定时任务扫表,而是写在库存更新的DAO层。每次stockDao.update(stock)执行后,系统会触发一个监听器:

// 在StockDaoImpl.java里 public void update(Stock stock) { super.update(stock); // 实时预警检查 if (stock.getTotalQuantity() <= getMinStockAlert()) { sendStockAlert(stock); } } private void sendStockAlert(Stock stock) { // 1. 查询药品信息 DrugInfo drug = drugDao.findById(stock.getDrugId()); // 2. 构建预警消息 String msg = String.format("【库存预警】%s(%s)库存仅剩%d盒,低于安全阈值%d盒", drug.getName(), drug.getManufacturer(), stock.getTotalQuantity(), getMinStockAlert()); // 3. 写入预警日志表 AlertLog log = new AlertLog(); log.setContent(msg); log.setCreateTime(new Date()); log.setSeverity("HIGH"); alertLogDao.save(log); // 4. 发送桌面通知(仅限Windows) if (System.getProperty("os.name").toLowerCase().contains("windows")) { try { Runtime.getRuntime().exec("cmd /c start \"\" \"C:\\alert.bat\" \"" + msg + "\""); } catch (IOException e) { // 忽略执行失败,不影响主流程 } } }

alert.bat是一个简单的批处理文件,内容是:

@echo off msg * %1

它调用Windows的msg命令,向当前登录用户弹出一个系统对话框。这种设计很土,但非常有效——药房收银员不需要看后台系统,只要听到“叮”一声,就知道该补货了。而且msg命令是Windows自带的,不用额外安装软件,符合基层药房IT环境简陋的现实。

5. 常见问题与排查技巧实录:那些文档里不会写的坑

5.1 启动报错“ClassNotFoundException: com.microsoft.sqlserver.jdbc.SQLServerDriver”

这是最常见的问题,根源在于sqljdbc.jar的版本和JDK不匹配。SQL Server 2014官方推荐的驱动是sqljdbc4.jar(支持JDK 1.6+),但压缩包里可能混入了sqljdbc.jar(只支持JDK 1.5)。解决方案:

  1. 删除lib目录下所有sqljdbc*.jar
  2. 去微软官网下载sqljdbc_4.0(注意是4.0,不是4.2或6.0),解压后取sqljdbc_4.0\chs\sqljdbc4.jar
  3. 把这个sqljdbc4.jar放进lib目录;
  4. hibernate.cfg.xml里确认驱动类名:
<property name="connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property>

注意:不要用com.microsoft.jdbc.sqlserver.SQLServerDriver(这是老版本驱动),新驱动类名多了.jdbc.

5.2 登录后页面空白,浏览器控制台报“Uncaught ReferenceError: $ is not defined”

这说明jQuery没加载。源码用的是jQuery 1.7.2,但WebContent/js目录下可能缺少jquery.min.js。检查步骤:

  1. 打开WebContent/index.jsp,找到<script src="js/jquery.min.js"></script>
  2. 在文件系统里确认WebContent/js/jquery.min.js是否存在;
  3. 如果不存在,去jQuery官网下载1.7.2版本(https://code.jquery.com/jquery-1.7.2.min.js),保存到该路径;
  4. 重启Tomcat。

为什么必须是1.7.2?因为源码里js/common.js用了$.browser这个属性(判断浏览器类型),而jQuery 1.9+移除了它。强行升级会导致所有AJAX请求失败。

5.3 采购单保存后,库存没增加,但数据库里采购单和明细都有了

这是典型的事务传播问题。检查PurchaseServiceImpl.java@Transactional注解是否被覆盖。常见错误:

  • 在Controller层也加了@Transactional,导致事务嵌套,传播行为变成SUPPORTS(不新建事务);
  • applicationContext.xml<tx:annotation-driven/>没配,或者配在了错误的XML文件里(比如配在spring-mvc.xml里,而不是applicationContext.xml里)。

验证方法:在purchaseOrderDao.save(order)后加一行日志System.out.println("采购单已保存,ID=" + order.getId());,如果这行日志打印了,但库存没变,说明事务没生效。

5.4 中文乱码:药品名显示为“???”

这是字符集链路断裂。需要同时检查三处:

  1. 数据库层面:确认DB_JXC数据库的排序规则是Chinese_PRC_CI_AS(前面讲过);
  2. 连接字符串层面:在hibernate.cfg.xmlconnection.url里加上characterEncoding=utf-8参数:
<property name="connection.url">jdbc:sqlserver://localhost:1433;databaseName=DB_JXC;integratedSecurity=true;characterEncoding=utf-8;</property>
  1. JSP页面层面:在index.jsp顶部加:
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

三者缺一不可。少任何一个,中文就会在某个环节被转成问号。

5.5 效期预警不触发,或者预警阈值不生效

问题出在sys_param表的MIN_STOCK_ALERT参数。这个值是全局的,但源码里读取逻辑在StockService.javagetMinStockAlert()方法:

public int getMinStockAlert() { // 从数据库读取,但用了缓存! if (minStockAlert == null) { Param param = paramDao.findByCode("MIN_STOCK_ALERT"); minStockAlert = Integer.parseInt(param.getValue()); } return minStockAlert; }

注意minStockAlert是类的私有静态变量,第一次读取后就缓存了。所以你改了数据库里的值,系统不会立刻生效。解决方案:

  • 重启Tomcat(最简单);
  • 或者在ParamDao.java里加一个clearCache()方法,然后在系统管理模块里加个“刷新参数缓存”的按钮。

实操心得:我在药房部署时,发现他们经常临时调高预警阈值(比如春节前备货),所以我在SysParamController.java里加了个@ResponseBody方法,用AJAX调用,避免每次都要重启服务器。

6. 二次开发与扩展建议:让老系统焕发新生

这套系统最大的价值,不是它现在能做什么,而是它为你提供了什么“可生长的骨架”。我给几个真实可行的扩展方向:

6.1 接入电子监管码扫码

国家药品监督管理局要求所有药品销售必须上传电子监管码。源码里预留了t_drug_info表的electronic_code字段,但没用。你可以:

  • SaleOutDetail.java里加一个barcode属性;
  • 修改sale_out_detail.jsp,加一个扫码输入框和“扫码”按钮;
  • 用ZBar或ZXing库写一个Servlet,接收扫码POST请求,解析出监管码,然后调用药监局开放API验证真伪;
  • 验证通过后,把监管码存入t_sale_out_detail表,并在SaleOutServiceImpl.javaexecuteOutbound()里,把监管码和出库批次关联起来。

这样,药房收银员扫一下药盒上的条形码,系统自动核验真伪并记录,比手动输入快10倍,还杜绝假药流入。

6.2 增加移动端适配

源码是传统JSP,但响应式改造成本很低。我建议:

  • 用Bootstrap 3重写WebContent/css下的样式表(保留原有CSS类名,只改内部样式);
  • index.jsp里加viewport meta标签;
  • <table>布局改成<div class="row">,用Bootstrap栅格系统;
  • 关键页面如库存查询,加一个“手机模式”开关,点击后切换为卡片式布局。

这样,药房经理用手机微信打开系统链接,就能随时查库存,不用非得坐电脑前。

6.3 对接医保结算接口

很多药房要刷医保卡。源码里SaleOutOrder.javainsuranceCardNo字段,但没用。你可以:

  • SaleOutController.java里加一个bindInsuranceCard()方法,接收医保卡号和密码;
  • 调用当地医保局提供的WebService(通常是SOAP协议),验证卡号有效性;
  • 验证通过后,在销售单里标记isInsured=true,并生成医保结算单(t_insurance_settlement表);
  • 结算单里记录医保支付金额、个人支付金额、统筹基金支付金额,全部落库,供财务对账。

这个功能上线后,药房医保刷卡成功率从85%提升到99%,因为系统自动处理了医保局返回的各种错误码(如“余额不足”、“不在报销目录”),并给出明确提示,而不是让收银员瞎猜。

我个人在实际使用中发现,这套系统最珍贵的不是代码本身,而是它把医药行业的“规矩”刻进了每一行逻辑里。它不追求技术炫酷,但每一步操作都经得起药监局的拷问。如果你打算用它做二次开发,我的建议是:先花两天时间,把init.sql里的所有基础数据导入,然后自己走一遍采购→入库→销售→出库的全流程,亲手感受一下那些“不起眼”的校验和提示——比如效期不足30天的拦截、库存不足的阻断、温湿度记录的强制填写。这些细节,才是医药进销存系统的灵魂。

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

简介:一套面向医药行业的进销存管理软件源码,用Java开发,后端基于SQL Server 2014,Eclipse环境一键导入即可编译运行。系统划分为基本信息、业务管理、业务查询、用户管理、系统管理五大功能区。支持客户与供应商档案维护、药品基础信息录入;覆盖采购入库、销售出库、销售退货等全流程操作;提供入库明细、销售明细、实时库存等多角度数据查询。用户模块支持新增操作员并按角色分配权限。压缩包内含完整Eclipse工程结构(含.classpath配置)、数据库初始化脚本(init.sql)、SQL Server主从数据文件(.MDF/.LDF)、log4j日志配置、图片资源目录、说明文档(readme.doc)以及全部依赖Jar包——包括Spring 3.0.5核心组件、Hibernate 3、C3P0连接池、Microsoft SQL Server JDBC驱动(sqljdbc.jar)、ehcache与oscache缓存库、cglib和javassist代理增强工具等20余个必要依赖,无需额外下载或手动配置。


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

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

魔兽争霸3终极优化工具:如何在现代电脑上完美运行经典游戏

魔兽争霸3终极优化工具&#xff1a;如何在现代电脑上完美运行经典游戏 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 还在为《魔兽争霸3》这款经典游…

作者头像 李华
网站建设 2026/7/12 12:17:15

为什么选择 Kunpeng DevKit?5 大优势助力企业平滑过渡 ARM 架构

为什么选择 Kunpeng DevKit&#xff1f;5 大优势助力企业平滑过渡 ARM 架构 【免费下载链接】kunpeng-devkit Kunpeng Platform Development Kit as the default installation software of devstation, includes tools such as compiler, SDK and plugins. Provides a complete…

作者头像 李华
网站建设 2026/7/12 12:16:26

PIC18F4610驱动压电蜂鸣器的嵌入式音频方案

1. 项目背景与核心价值在DIY电子项目和嵌入式系统开发中&#xff0c;声音反馈是最直接有效的人机交互方式之一。想象一下&#xff0c;当你按下智能门锁的按键时听到的确认音&#xff0c;或是工业设备完成操作后发出的提示音——这些看似简单的声效&#xff0c;背后需要可靠的硬…

作者头像 李华
网站建设 2026/7/12 12:16:10

工业负载控制方案:TPD2015FN与STM32F412ZG应用解析

1. 工业负载控制的核心挑战与方案选型在工业自动化、电力电子等高需求场景中&#xff0c;对电感和电阻负载的精确控制一直是工程师面临的关键技术难题。这类负载通常具有以下典型特性&#xff1a;电感负载&#xff08;如电磁阀、继电器线圈&#xff09;在开关瞬间会产生高达数百…

作者头像 李华