基于JSP+Servlet的在线药店仓储与销售管理系统 - 源码深度解析

JavaJavaScriptHTMLCSSMySQLJSP+Servlet
2026-03-184 浏览

文章摘要

本项目是一款基于JSP和Servlet技术栈构建的在线药店仓储与销售管理系统,旨在解决传统药店在药品信息管理与库存流转环节的痛点。系统通过数字化手段,将药品的采购、入库、销售及库存盘点等核心业务流程整合于一体,有效避免了因人工记录导致的错漏、信息更新不及时以及库存积压或短缺问题,为药店运营者提供了精...

在医药零售行业,高效精准的仓储与销售管理是保障药品供应、控制运营成本的核心环节。传统依赖手工台账和记忆的管理方式,不仅效率低下,且极易因人为疏忽导致库存数据不准确、药品过期或短缺等问题。针对这一痛点,我们设计并实现了一套基于JSP与Servlet技术的“药联智仓”管理系统,旨在通过数字化手段重塑药店的业务流程。

该系统采用经典的J2EE MVC架构,将业务逻辑、数据与界面呈现分离,确保了代码的良好结构和可维护性。Servlet作为系统的控制器中枢,负责拦截和处理所有客户端请求,进行业务逻辑调度与数据封装;JSP页面则专注于视图渲染,通过JSTL标签库和表达式语言动态展示数据,避免了在页面中混入Java代码;模型层由一系列封装了业务数据和行为的JavaBean构成;底层数据持久化则通过JDBC与MySQL数据库进行交互。这一技术选型成熟稳定,非常适合中小型药店快速部署和低成本运营。

数据库架构设计与核心表解析

数据库是整个系统的基石,其设计的合理性直接决定了系统的性能和数据一致性。“药联智仓”系统共设计了9张核心数据表,以下是其中几个关键表的设计亮点分析。

1. 药品信息表 (medicine) 此表是系统的核心实体,记录了药品的所有基础信息。其设计不仅考虑了通用属性,还针对医药行业的特殊要求进行了字段规划。

CREATE TABLE `medicine` (
  `mid` int(11) NOT NULL AUTO_INCREMENT,
  `medicineSn` varchar(64) NOT NULL,
  `medicineName` varchar(64) NOT NULL,
  `medicineSort` int(11) NOT NULL,
  `medicineCost` double NOT NULL,
  `medicinePrice` double NOT NULL,
  `medicineUnit` varchar(32) NOT NULL,
  `medicineIntro` varchar(512) DEFAULT NULL,
  `medicinePro` varchar(32) NOT NULL,
  `medicineProDate` date NOT NULL,
  `medicineExpDate` date NOT NULL,
  `medicineStock` int(11) NOT NULL,
  `medicineStockDanger` int(11) NOT NULL,
  `medicineSupplier` varchar(64) NOT NULL,
  PRIMARY KEY (`mid`),
  UNIQUE KEY `medicineSn` (`medicineSn`)
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8;
  • 设计亮点
    • 唯一性约束medicineSn(药品编码)字段添加了唯一索引,确保了每一种药品在系统中的唯一标识,有效防止重复录入。
    • 生命周期管理:专门设计了medicineProDate(生产日期)和medicineExpDate(有效期至)字段,为后续实现近效期药品预警功能提供了数据支持。
    • 库存安全预警medicineStock(当前库存)与medicineStockDanger(库存预警线)的搭配设计,使得系统可以轻松实现库存预警逻辑。当medicineStock低于medicineStockDanger时,系统可自动触发提醒,提示管理员进行采购。
    • 成本与售价分离:明确区分medicineCost(成本价)和medicinePrice(售价),便于精确计算毛利和进行财务分析。

2. 销售订单表 (sale) 该表记录了每一笔销售交易的详细信息,是进行销售统计和业绩分析的关键。

CREATE TABLE `sale` (
  `saleId` int(11) NOT NULL AUTO_INCREMENT,
  `saleNumber` varchar(64) NOT NULL,
  `saleMedicine` int(11) NOT NULL,
  `saleCount` int(11) NOT NULL,
  `salePrice` double NOT NULL,
  `saleTotal` double NOT NULL,
  `saleCustomer` varchar(32) NOT NULL,
  `saleOperator` varchar(32) NOT NULL,
  `saleTime` datetime NOT NULL,
  PRIMARY KEY (`saleId`),
  UNIQUE KEY `saleNumber` (`saleNumber`),
  KEY `saleMedicine` (`saleMedicine`),
  CONSTRAINT `sale_ibfk_1` FOREIGN KEY (`saleMedicine`) REFERENCES `medicine` (`mid`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
  • 设计亮点
    • 外键约束:通过saleMedicine字段与medicine表的主键mid建立外键约束,保证了每一条销售记录都对应一个有效的药品,维护了数据的参照完整性。
    • 交易可追溯性:记录了saleOperator(操作员)和saleTime(销售时间),实现了操作的审计追踪。
    • 冗余设计以提升性能:虽然可以通过salePrice * saleCount计算出saleTotal(销售总额),但将其作为独立字段存储是一种合理的冗余。这避免了在需要进行大量统计查询时进行频繁的计算,用空间换取了时间,提升了报表生成的速度。

3. 采购订单表 (purchase) 与销售表相对应,采购表管理药品的入库流程。

CREATE TABLE `purchase` (
  `purchaseId` int(11) NOT NULL AUTO_INCREMENT,
  `purchaseNumber` varchar(64) NOT NULL,
  `purchaseMedicine` int(11) NOT NULL,
  `purchaseCount` int(11) NOT NULL,
  `purchasePrice` double NOT NULL,
  `purchaseTotal` double NOT NULL,
  `purchaseSupplier` varchar(64) NOT NULL,
  `purchaseOperator` varchar(32) NOT NULL,
  `purchaseTime` datetime NOT NULL,
  PRIMARY KEY (`purchaseId`),
  UNIQUE KEY `purchaseNumber` (`purchaseNumber`),
  KEY `purchaseMedicine` (`purchaseMedicine`),
  CONSTRAINT `purchase_ibfk_1` FOREIGN KEY (`purchaseMedicine`) REFERENCES `medicine` (`mid`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;

其设计理念与销售表类似,通过外键关联药品,并详细记录采购的各个环节,确保了入库流程的规范性和数据准确性。

核心功能模块深度解析

1. 药品信息综合管理 这是系统最基础也是最重要的模块,实现了对药品档案的全面管理。其核心Servlet控制器负责处理列表查询、新增、修改和删除等请求。

// MedicineListServlet.java 处理药品列表查询与分页
public class MedicineListServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int page = 1;
        int limit = 10;
        try {
            page = Integer.parseInt(request.getParameter("page"));
            limit = Integer.parseInt(request.getParameter("limit"));
        } catch (NumberFormatException e) {
            // 使用默认值
        }

        MedicineService medicineService = new MedicineService();
        Page<Medicine> medicinePage = medicineService.getMedicineList(page, limit);
        
        request.setAttribute("medicinePage", medicinePage);
        request.getRequestDispatcher("/admin/medicine_list.jsp").forward(request, response);
    }
}

在JSP页面中,使用JSTL循环展示药品列表,并利用EL表达式动态绑定数据。

<!-- medicine_list.jsp 药品列表展示片段 -->
<table class="table">
    <thead>
        <tr>
            <th>药品编码</th>
            <th>药品名称</th>
            <th>生产厂家</th>
            <th>当前库存</th>
            <th>操作</th>
        </tr>
    </thead>
    <tbody>
        <c:forEach items="${medicinePage.list}" var="medicine">
            <tr>
                <td>${medicine.medicineSn}</td>
                <td>${medicine.medicineName}</td>
                <td>${medicine.medicinePro}</td>
                <td>
                    <span class="badge ${medicine.medicineStock lt medicine.medicineStockDanger ? 'badge-danger' : 'badge-success'}">
                        ${medicine.medicineStock}
                    </span>
                </td>
                <td>
                    <a href="medicine_edit?id=${medicine.mid}" class="btn btn-sm btn-primary">编辑</a>
                    <a href="javascript:void(0)" onclick="confirmDelete(${medicine.mid})" class="btn btn-sm btn-danger">删除</a>
                </td>
            </tr>
        </c:forEach>
    </tbody>
</table>

药品信息管理 图示:药品信息管理界面,清晰展示库存状态(正常/预警),并提供便捷的编辑入口。

2. 库存动态更新与销售出库 销售出库是系统业务流程的关键一环,其核心在于保证“事务性”:即生成销售记录的同时,必须原子性地更新药品库存。这项工作在Service层完成。

// SaleService.java 销售出库服务逻辑
public class SaleService {
    public boolean addSale(Sale sale) {
        Connection conn = DbUtil.getConnection();
        try {
            conn.setAutoCommit(false); // 开启事务

            // 1. 插入销售记录
            String sqlInsertSale = "INSERT INTO sale (saleNumber, saleMedicine, saleCount, salePrice, saleTotal, saleCustomer, saleOperator, saleTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
            PreparedStatement pstmtSale = conn.prepareStatement(sqlInsertSale);
            // ... 设置参数
            pstmtSale.executeUpdate();

            // 2. 更新药品库存
            String sqlUpdateStock = "UPDATE medicine SET medicineStock = medicineStock - ? WHERE mid = ?";
            PreparedStatement pstmtStock = conn.prepareStatement(sqlUpdateStock);
            pstmtStock.setInt(1, sale.getSaleCount());
            pstmtStock.setInt(2, sale.getSaleMedicine());
            int affectedRows = pstmtStock.executeUpdate();

            if (affectedRows == 0) {
                throw new SQLException("更新库存失败,药品可能不存在。");
            }

            conn.commit(); // 提交事务
            return true;
        } catch (SQLException e) {
            try {
                conn.rollback(); // 回滚事务
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
            return false;
        } finally {
            DbUtil.closeConnection(conn);
        }
    }
}

药品销售管理 图示:销售出库操作界面,需填写客户信息、选择药品及数量,系统自动计算金额。

3. 采购入库与库存统计 采购入库是库存增加的入口,其逻辑与销售出库相反但同样需要事务保证。入库后,系统提供了多维度的统计查询功能。

// PurchaseService.java 采购入库服务逻辑
public class PurchaseService {
    public boolean addPurchase(Purchase purchase) {
        Connection conn = DbUtil.getConnection();
        try {
            conn.setAutoCommit(false);

            // 1. 插入采购记录
            String sqlInsert = "INSERT INTO purchase (purchaseNumber, purchaseMedicine, purchaseCount, purchasePrice, purchaseTotal, purchaseSupplier, purchaseOperator, purchaseTime) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
            // ... 参数设置与执行

            // 2. 增加药品库存
            String sqlUpdate = "UPDATE medicine SET medicineStock = medicineStock + ? WHERE mid = ?";
            // ... 参数设置与执行

            conn.commit();
            return true;
        } catch (Exception e) {
            // ... 异常处理与回滚
        } finally {
            DbUtil.closeConnection(conn);
        }
    }

    // 统计某时间段内的入库总量
    public double getTotalInbound(String startDate, String endDate) {
        String sql = "SELECT SUM(purchaseTotal) FROM purchase WHERE purchaseTime BETWEEN ? AND ?";
        // ... 执行查询并返回结果
    }
}

仓库入库统计 图示:入库统计报表,可按时间范围筛选,直观展示采购金额趋势。

4. 实体模型(JavaBean) 模型层实体类严格遵循JavaBean规范,封装数据属性及其getter/setter方法,是MVC模型中数据流转的载体。

// Medicine.java 药品实体类
public class Medicine {
    private int mid;
    private String medicineSn;
    private String medicineName;
    private int medicineSort;
    private double medicineCost;
    private double medicinePrice;
    private String medicineUnit;
    private String medicineIntro;
    private String medicinePro;
    private Date medicineProDate;
    private Date medicineExpDate;
    private int medicineStock;
    private int medicineStockDanger;
    private String medicineSupplier;

    // 无参构造器
    public Medicine() {}

    // 全参构造器
    public Medicine(int mid, String medicineSn, String medicineName, ...) {
        this.mid = mid;
        this.medicineSn = medicineSn;
        this.medicineName = medicineName;
        // ... 其他属性赋值
    }

    // Getter and Setter 方法
    public int getMid() { return mid; }
    public void setMid(int mid) { this.mid = mid; }

    public String getMedicineSn() { return medicineSn; }
    public void setMedicineSn(String medicineSn) { this.medicineSn = medicineSn; }

    // ... 其他属性的Getter和Setter
}
// Sale.java 销售订单实体类
public class Sale {
    private int saleId;
    private String saleNumber;
    private int saleMedicine;
    private int saleCount;
    private double salePrice;
    private double saleTotal;
    private String saleCustomer;
    private String saleOperator;
    private Date saleTime;

    // ... 构造器、Getter和Setter方法
}

库存查询 图示:库存查询界面,支持按药品名称、编码等多条件筛选,实时反映库存健康状况。

功能展望与系统优化方向

尽管“药联智仓”系统已实现了核心的仓储与销售管理功能,但在实际企业级应用中仍有广阔的优化和扩展空间。

  1. 引入连接池与ORM框架:目前使用基础的JDBC操作数据库。未来可集成Druid等数据库连接池,有效管理数据库连接,提升系统性能。同时,可以考虑引入MyBatis或Hibernate等ORM框架,简化数据库操作代码,提高开发效率和数据访问层的可维护性。
  2. 实现分布式会话管理:当前系统可能依赖Servlet原生HttpSession。在需要横向扩展(如部署多台服务器)时,可将会话数据存储到Redis等分布式缓存中,实现应用服务器的无状态化,增强系统的伸缩性和高可用性。
  3. 构建RESTful API接口:为适应移动端应用(如店员APP、小程序)或与其他系统(如ERP、财务软件)集成,可将核心业务模块重构为RESTful API接口,实现前后端分离架构。
  4. 增强数据分析与BI看板:在现有统计功能基础上,引入ECharts等可视化库,构建管理者驾驶舱(Dashboard),提供销售趋势分析、毛利分析、库存周转率、畅销/滞销药品排名等深度商业智能分析。
  5. 完善权限控制模型:从简单的角色功能控制,升级为基于RBAC的精细化权限管理体系,支持功能权限、数据权限(如不同门店管理员只能管理本店数据)的动态配置,满足连锁药店的复杂管理需求。

该系统通过严谨的MVC架构和数据库设计,成功地将药品管理的核心业务流程数字化、自动化,为药店运营者提供了一个稳定、高效的管理工具。其清晰的代码结构为后续的功能增强和技术演进奠定了坚实的基础。随着技术的不断迭代,“药联智仓”有望发展成为更智能、更集成化的医药新零售解决方案。

本文关键词
JSPServlet在线药店仓储管理销售管理系统

上下篇

上一篇
没有更多文章
下一篇
没有更多文章