做毕业设计和课程设计这些年,SSM框架的管理系统我见得实在太多了,但“java_ssm131龙康药店库存进销存管理系统”这个项目,我第一次拿到手的时候还是觉得有点意思。一般的课设系统大多是单后台、纯增删改查,这个项目不一样,它带了一个完整的前台商城,药品分类展示、搜索、购物车、模拟下单全都有,后台还要管采购入库、销售出库、库存预警、统计报表,这已经算是“进销存 + 电商前台”的双端闭环了。
整份源码基于Spring + SpringMVC + MyBatis这套经典SSM组合,开发工具是IntelliJ IDEA,数据库用的MySQL,非常适合Java Web刚入门、想系统看一个完整项目的同学。你在github或网盘上搜“龙康药店”也能找到不少流传版本,但网上大部分资源要么缺文档,要么跑不起来,真正能把项目导入IDEA、配置好环境、把页面调通的人其实不多。这篇文章就围绕这套源码,从项目设计、技术选型、核心业务逻辑、IDEA部署实操到常见报错排查,完整拆一遍。无论你是要做课程设计、毕业设计,还是想练手一套带前台的进销存系统,这篇文章都能帮你少走不少弯路。
1. 项目到底做什么:龙康药店进销存系统的构建思路
1.1 医药零售场景下的库存管理痛点
刚开始看这类系统,很多人会把它当成普通超市进销存来理解,但药店和超市有个本质区别:药品是严格受监管的商品,有批号、有效期、生产日期,而且库存多了会过期,库存少了影响销售,所以对“进出有据、账实相符”的要求比一般商品高得多。龙康药店这套系统虽然没有做GSP(药品经营质量管理规范)级别的复杂效期管理,但从表结构和业务设计上,已经具备了一套进销存系统最核心的骨架。
系统要解决的问题其实很朴素:药店里面每天有采购进来的药品,有顾客买走的药,有退货,有临期处理,如果全用Excel记,日子一久必然对不上账。所以这套系统把“采购入库 -> 库存增加 -> 前台销售 -> 库存扣减 -> 库存预警 -> 报表统计”这条链路用代码串了起来。后台管理员负责维护药品、供应商、采购单、库存;前台用户注册登录之后可以浏览药品、加入购物车、下单,整个流程都是通的。
1.2 功能模块全景图与用户角色
这套系统从前台到后台,核心角色分三类:管理员、员工、普通用户。管理员是后台的最高权限,负责药品分类、药品信息、供应商、采购单、销售单、库存管理;员工可以帮忙处理订单发货,但一般不能动系统基础配置;普通用户在商城前台注册登录,浏览商品、加购物车、确认订单。
功能模块拆开来看,大概是这样:
- 系统登录与权限控制:后台入口需要管理员/员工登录,前台用户单独注册登录。
- 药品分类管理:维护中药、西药、医疗器械等分类,方便前台分类展示。
- 药品信息管理:包括药品名称、规格、生产厂家、批准文号、进价、零售价、库存上下限。
- 供应商管理:记录供应商名称、联系人、联系电话,采购单要从这里选供应商。
- 采购入库管理:创建采购单,选择药品、填写数量、进价,入库后自动增加库存。
- 销售出库管理:前台用户下单生成销售单,后台管理员/员工处理订单,出库后扣减库存。
- 库存查询与预警:库存列表展示当前数量,低于预警阈值的药品高亮显示。
- 销售统计报表:按日、按月统计销售金额与数量,用于了解店铺经营情况。
这些模块放在一起,就是一个非常标准的药店进销存闭环。你如果把这个项目吃透,市面上绝大多数后台管理系统的开发套路基本就能摸清了。
2. 技术选型与设计思路:SSM框架为什么至今仍是经典
2.1 SSM三层架构与项目代码结构
先聊聊技术栈。现在很多人一谈起Java Web,张嘴就是Spring Boot + Vue,但为什么这套源码还在用SSM?其实理由很简单:大多数高校的Java Web课程、毕业设计,至今仍然以SSM为教学主线。SSM本身并不老旧,它只是把Spring、SpringMVC、MyBatis这三个框架组合起来,各司其职,分工非常清晰。
- Spring:负责对象管理,把Service层、Mapper层、事务管理等对象交给Spring容器管理。你不用到处new对象,依赖注入就够了。
- SpringMVC:负责Web层的请求转发与参数绑定。前端页面发来请求,DispatcherServlet分发到对应的Controller方法,处理完再返回视图或JSON。
- MyBatis:负责持久层 SQL 操作。SQL写在XML文件里面,Mapper接口定义方法,调用时动态拼SQL,既灵活也直观。
这套项目打开之后,典型的目录结构长这样:
src/main/java ├── com.longkang.controller // Controller层 ├── com.longkang.service // Service接口 ├── com.longkang.service.impl // Service实现类 ├── com.longkang.mapper // MyBatis Mapper接口 ├── com.longkang.pojo // 实体类 ├── com.longkang.common // 通用工具、常量 └── com.longkang.interceptor // 拦截器 src/main/resources ├── mapper // MyBatis XML映射文件 ├── jdbc.properties // 数据库连接配置 ├── spring-mybatis.xml // Spring整合MyBatis └── spring-mvc.xml // SpringMVC配置 src/main/webapp ├── jsp / pages // 后台JSP页面 ├── front / 前台页面 ├── static / css, js, images └── WEB-INF / web.xml新手面对这个结构,最容易犯的一个错误是:拿到源码直接往IDEA里扔,然后找不到启动入口。SSM项目的入口不在某个main方法,而在web.xml配置的DispatcherServlet,以及Spring的ApplicationContext。理解这一点,后面部署排查才有方向。
2.2 数据库表设计:进销存的核心是“流水账”
很多课设项目的数据库就三五张表,但进销存系统如果想做到逻辑自洽,表设计至少要覆盖“主数据 + 单据 + 流水 + 关系表”这四类。龙康药店这套项目的表大致是这个思路:
- 主数据表:admin(管理员/员工)、user(前台用户)、supplier(供应商)、medicine_type(药品分类)、medicine(药品)
- 单据表:purchase_order(采购单)、purchase_order_detail(采购单明细)、sale_order(销售单)、sale_order_detail(销售单明细)
- 购物车表:cart(购物车)
- 库存与流水表:stock(库存表)、inventory_log(库存流水)
这里最核心的设计点,是“单据和单据明细分离”。为什么不能直接在一张采购表里存药品名称、数量、进价?因为一张采购单可能会同时采购几十种药,一行完全存不下;而且单据有表头信息(供应商、采购员、总金额、下单时间)和明细信息(哪批次、哪种药、多少数量、多少钱),这两者天然是一对多的关系,所以必须拆成主表和子表。
另一个很关键的设计是库存流水表。每次采购入库、销售出库、退货,除了改库存表里的数量,还应该往库存流水表里插一条记录。这样做的好处是,哪天库存对不上了,可以通过流水倒查,是哪个环节出的问题。很多半吊子源码没有流水表,出库就UPDATE一下库存,看起来没问题,但一旦出现并发订单或程序报错,账目就成一锅粥了。
药品表里通常会设几个关键字段:stock_min(最低库存)、stock_max(最高库存)、price_in(进价)、price_out(零售价)。预警功能就是拿当前库存和stock_min做比较,低于阈值就提醒补货。
2.3 前后台分离的设计取舍
既然是“带前台”的药店系统,就涉及前台和后台的展示区分。SSM项目里最常见的做法是:后台用一套JSP放在WEB-INF或者单独/admin目录下面,前台商城用另一套JSP放在根目录下面,两边共用同一套Service和Mapper,但Controller分开。
这样做的好处首先是权限隔离。后台入口设置管理员登录拦截,普通用户访问不了;前台页面则允许游客浏览,只有在加购物车、下单时才要求登录。其次,两套页面的风格完全不同,后台是密集的表格+表单,前台是商品卡片+购物车,放在不同目录下,代码维护起来不会互相干扰。
要注意的是,这套源码的前台和后台,前端渲染都由JSP完成,并没有单独做前后端分离接口。如果你以后想改成Vue前端,可以把Controller改成返回JSON,再配合前端的axios请求,这个改造也不算难,后面我会单独讲。
3. 核心业务实现详解:从采购入库到前台下单
3.1 采购入库:库存更新与事务控制
采购入库这个功能,表面上就是“填一张采购单,点一下提交,库存增加”,但代码实现上必须保证原子性。我看了源码里的流程,大致是这样:
- 管理员在后台选择供应商,填写采购单表头信息。
- 在明细列表中添加一种或多种药品,输入采购数量和进价。
- 提交时,后端先insert采购单主表,再批量insert采购单明细表。
- 最后循环遍历明细,UPDATE库存表,让库存数量加上采购数量。
这个流程里最容易出问题的是第四步。如果三个药品的采购明细,前两个都执行成功,第三个UPDATE库存时报错了,那数据库里就会留下“采购单存在、但库存只加了一半”的脏数据。所以Service层必须加@Transactional事务注解,让这四个步骤要么全部成功,要么全部回滚。
Service实现里大概是这样的写法:
@Transactional(rollbackFor = Exception.class) public boolean addPurchaseOrder(PurchaseOrder order, List<PurchaseOrderDetail> details) { int count = purchaseOrderMapper.insertSelective(order); if (count == 0) { return false; } for (PurchaseOrderDetail detail : details) { purchaseOrderDetailMapper.insert(detail); stockMapper.increaseStock(detail.getMedicineId(), detail.getPurchaseNum()); } return true; }这里还有一个容易踩的坑:库存表初始数据哪里来?正常系统在第一次采购入库之前,库存表里应该已经有药品的基础库存,或者在采购入库时判断库存不存在则新增一条。很多新手在测试时发现:采购单建成功了,库存却没变,多半就是库存表里根本没有对应药品的初始记录,UPDATE语句影响行数为0。代码里最好处理成“有则加、无则insert”,否则会让人排查很久。
3.2 前台购物:购物车、下单与库存扣减
前台的商城流程比较接近真实电商的简化版:游客浏览药品 -> 点击加入购物车 -> 跳转到购物车页面 -> 确认结算 -> 生成订单 -> 后台订单列表中能看到。这里有几个细节值得说一说。
购物车表的核心设计是userId + medicineId + quantity三要素,一个用户对同一种药品只保留一条记录。用户重复加入同一个药品时,不应该在购物车中新增一行,而是把quantity累加。我见过不少新手源码在这里直接insert,导致购物车里出现两行一模一样的药,这种低级错误很影响体验。
结账生成销售单的时候,要考虑库存是否充足。用户下单数量不能超过当前库存,否则应该提示“库存不足”。严谨一点的写法是在SQL层面做条件更新:
UPDATE stock SET stock_num = stock_num - #{num} WHERE medicine_id = #{medicineId} AND stock_num >= #{num}这条语句是利用数据库的行锁做并发控制,即使两个人同时下同一款药的订单,也不会把库存扣成负数。先查询库存再在Java里判断,在高并发场景下是有风险的,所以写销售出库的update时最好带上stock_num >= #{num}这个条件,然后判断影响行数是否为0。如果影响行数为0,就说明库存已经被别人抢光了。
3.3 库存预警与销售统计报表
库存预警的实现不复杂,核心还是SQL聚合。后台每个页面加载后,会去查一遍库存表中所有低于最低库存阈值的药品:
SELECT m.medicine_name, s.stock_num, m.stock_min FROM stock s LEFT JOIN medicine m ON s.medicine_id = m.id WHERE s.stock_num <= m.stock_min这类预警一般放在首页仪表盘,显示一个红色提醒列表,同时在库存管理页面把低库存药品标红。对药店来说,这个功能很重要,因为药品断货会直接影响门店营收,所以做演示的时候一定要把预警做明显一点,答辩时也容易成为加分项。
销售统计报表这块,源码里一般是统计订单的聚合数据,比如当天营业额、本月营业额、销量Top5药品。SQL用GROUP BY + SUM + DATE_FORMAT函数即可。例如统计每天的销售总额:
SELECT DATE_FORMAT(create_time, '%Y-%m-%d') AS day, SUM(total_price) AS total_sales FROM sale_order WHERE order_status = 2 -- 已完成 GROUP BY day ORDER BY day DESC如果页面需要更直观的图表展示,通常会引入ECharts,把聚合结果转成JSON返回前端,画折线图或柱状图。基础课设做到这一步已经绰绰有余了。
3.4 权限控制与会话管理
后台访问控制通常靠拦截器实现。写一个LoginInterceptor,实现HandlerInterceptor接口,在preHandle方法里判断session里有没有登录用户,没有就重定向到登录页。
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object user = request.getSession().getAttribute("admin"); if (user == null) { response.sendRedirect(request.getContextPath() + "/login.jsp"); return false; } return true; }然后在spring-mvc.xml里配置拦截规则,最核心的一条是要把登录页、静态资源、前台页面放行,否则会出现“登录页都进不去”的尴尬。我见过有同学把拦截器配置成/**,结果静态CSS、JS全部被拦,页面样式全丢了,排查半天才发现是拦截器放行路径没写好。
<mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/admin/**"/> <mvc:exclude-mapping path="/admin/login"/> <mvc:exclude-mapping path="/static/**"/> </mvc:interceptor> </mvc:interceptors>前台部分则相对宽松,游客可以浏览药品,但加入购物车和结算时Controller判断用户是否登录。session里没有userId时,直接跳回登录页,登录成功再返回购物车。
4. 源码落地:IDEA导入部署的完整实操记录
4.1 环境准备清单
拿到源码,第一件事不是急着打开IDEA,而是先把环境准备好。这套项目属于传统SSM,依赖的运行时环境跟Spring Boot不太一样,Java版本、Tomcat版本、Maven版本如果对不上,启动时会冒出各种奇奇怪怪的错误。
建议环境如下:
- JDK 1.8:SSM的老项目几乎都基于JDK8开发,用太高版本反而可能编译不过。
- Maven 3.6.x:用于拉取Spring、MyBatis等依赖,版本太新偶尔会跟旧插件不兼容。
- Tomcat 8.5或9.0:重要的事说三遍,不要直接用Tomcat 10,Tomcat 10把javax.包改成了jakarta.,老SSM项目会直接报ClassNotFoundError。
- MySQL 5.7或8.0:最好版本不低于5.5,另外连接MySQL8要记得用新驱动。
- IntelliJ IDEA:社区版或正式版都可以,社区版装个Tomcat插件就能跑。
注意:不要在这类老项目上强行追求“所有版本最新”。JDK17 + Tomcat10 + MySQL8.0 + 最新Maven组合,对于SSM项目来说不仅没有性能提升,反而会让你在环境配置上耗费大量时间。
4.2 IDEA导入源码到运行
IDEA导入SSM项目,最高效的方式是直接打开项目的pom.xml,让IDEA识别为Maven项目。具体步骤很简单:
- 打开IDEA,选择
File -> Open,找到项目根目录下的pom.xml,点击OK。 - IDEA询问“Open as Project or File?”时,选择Open as Project。
- 确认Project SDK选择1.8,如果没有JDK1.8,先在
Project Structure -> SDKs里添加。 - 等待右侧Maven面板把依赖全部下载完毕。这一步取决于网络环境,依赖下载失败的话,后面启动就会疯狂报错。
- 配置Tomcat:下拉框选择
Edit Configurations -> Add New -> Tomcat Server -> Local,在Deployment选项卡里添加Artifact: xxx:war exploded。
这里有个细节,如果是第一次用IDEA跑传统Web项目,很可能发现Deployment里没有Artifact可选。这是因为项目还没有被识别为Web项目。解决办法是打开Project Structure -> Facets,点击加号添加Web,把Web Resource Directory指定到src/main/webapp,然后在Artifacts里新建一个Web Application: Exploded。配好之后,IDEA才能把项目打成可部署的war包。
Tomcat配置中,Application context通常设置为/,这样访问地址直接是http://localhost:8080,不用额外拼接项目名。如果项目里用相对路径写了很多跳转,context设置为空会省很多麻烦。
4.3 数据库初始化与连接配置
接下来要创建数据库并导入表结构和初始数据。一般源码包里会带一个db.sql或longkang.sql文件。打开Navicat或MySQL命令行:
mysql -uroot -p CREATE DATABASE longkang DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci; USE longkang; SOURCE D:/path/to/longkang.sql;导入成功之后,修改项目里的jdbc.properties,把数据库的账号密码改成你自己的。这里有两个非常典型的版本差异:
MySQL 5.7版本:
jdbc.driver=com.mysql.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/longkang?characterEncoding=utf8 jdbc.username=root jdbc.password=123456MySQL 8.0版本:
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://localhost:3306/longkang?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true jdbc.username=root jdbc.password=你的密码MySQL8的密码加密规则是caching_sha2_password,老驱动连接时会报Public Key Retrieval is not allowed,所以URL里必须带上allowPublicKeyRetrieval=true和useSSL=false。如果密码配置不对,启动时日志里会出现Access denied for user 'root'@'localhost',这时候不要去改代码,先去确认密码。
4.4 启动验证与关键节点检查
配置完成后,点击Tomcat旁边的绿色三角启动。第一次启动成功后,浏览器会自动打开首页。这时候建议按下面几个节点做一次完整验证:
- 前台首页能不能打开:正常的药店前台应该有分类菜单、药品列表、搜索框。
- 用户注册/登录:能注册新账号,登录后session能正常写入。
- 加入购物车并下单:能生成销售订单,库存数量对应扣减。
- 后台登录:用admin账号进入后台,看能否看到带预警信息的首页仪表盘。
- 新增一笔采购单:后台增加供应商、选择药品、提交采购单,库存能不能同步增加。
如果这些流程都通了,说明项目已经完整跑起来了。但现实往往是,第一次启动就会卡在某个报错上。所以接下来这部分,我把跑项目过程中最常见的坑整理出来,你对照排查即可。
5. 从0到1跑通项目的各类坑与排查技巧
5.1 数据库连接类问题
这类报错是启动时出现频率最高的。常见问题与定位手段如下:
Access denied for user 'root'@'localhost' (using password: YES):连接数据库的账号密码不对。用Navicat测试能否用同一个账号密码连上数据库。UnknownHostException或Communications link failure:数据库服务没启动,或者URL里的localhost写错,检查3306端口是否被占用。Public Key Retrieval is not allowed:MySQL8的URL里少了allowPublicKeyRetrieval=true。Unknown database 'longkang':数据库没有创建成功,或者名字跟jdbc.properties里的不一致。Table 'xxx' doesn't exist:说明.sql文件没有完全导入,或者导到了别的库。用SHOW TABLES;检查一遍。
排查数据库问题的黄金命令:复制jdbc.properties里的URL、用户名、密码,在Navicat新建连接里试一次。如果Navicat能连,项目连不上,大概率是驱动或URL问题;如果Navicat都连不上,那就是MySQL服务或密码问题。
5.2 Tomcat启动与404问题
- 启动报
java.lang.NoClassDefFoundError: javax/servlet/...:Tomcat版本太高,换成Tomcat 8.5或9.0。 - 启动报
ClassNotFoundException: org.springframework.web.context.ContextLoaderListener:Spring的jar包没打包进Artifact。打开Project Structure -> Artifacts -> Output Layout,把Available Elements里的Maven依赖全部加入。 - 启动正常但访问后台404:检查Application context是不是
/,同时确认Controller里的请求映射路径和前端跳转路径是否一致。SSM项目经常出现前端写/admin/toAdd.jsp,但实际Controller映射是/admin/add的情况,这类路径问题只能靠仔细对比。 - 页面能打开但样式全丢:多半是JSP里引入CSS用了绝对路径,但项目部署context没带项目名,或者在拦截器里把静态资源拦截了。建议统一用
${pageContext.request.contextPath}拼接静态资源路径。
5.3 中文乱码问题
中文乱码在这个项目里很容易出现,因为涉及的环节太多:JSP页面编码、数据库编码、请求编码、响应编码,任何一个环节不一致都会乱。
- 数据库层面:建库时用
DEFAULT CHARACTER SET utf8,表字段也统一用utf8。 - JSP页面:
pageEncoding="UTF-8"和contentType="text/html; charset=UTF-8"都写上。 - 请求参数乱码:在web.xml里配置Spring自带的CharacterEncodingFilter,强制请求和响应都走UTF-8。
<filter> <filter-name>encodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>如果上述都配置了还是乱码,再看看数据库连接URL里有没有characterEncoding=utf8,SQL文件导入时是否选择了正确的字符集。
5.4 Maven依赖与构建问题
SSM项目常见的Maven报错,一是依赖迟迟下载不下来,二是jar包冲突。
依赖下载慢,在settings.xml里配置阿里云镜像:
<mirror> <id>aliyunmaven</id> <mirrorOf>central</mirrorOf> <name>阿里云公共仓库</name> <url>https://maven.aliyun.com/repository/public</url> </mirror>jar包冲突最常见的是javax.servlet-api和Tomcat自带的servlet-api冲突,会报ClassCastException或NoSuchMethodError。解决办法是把pom.xml里servlet-api的scope改成provided,让它只在编译时存在,运行时不打包进war。
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency>我当时跑的时候还遇到过一个问题:IDEA里Maven面板一片红,所有依赖都显示invalid。后来发现是项目里pom.xml的groupId和artifactId有特殊字符,加上本地Maven仓库里有坏包,删掉C:\Users\xxx\.m2\repository下对应的目录重新下载就好了。
6. 常见问题速查表与避坑清单
这里把部署和运行时最常见的问题整理成一张表,方便你直接对照。这些内容都是我在实际跑这套源码时踩过的坑,网上很多教程不会提到。
| 现象 | 根本原因 | 解决方案 |
|---|---|---|
| 启动报NoClassDefFoundError: javax/servlet | Tomcat 10不兼容旧servlet包 | 换Tomcat 8.5/9.0 |
| 访问后台404 | Application context或Controller路径不对 | 将context设为/,检查请求映射 |
| 前端样式丢失 | 静态资源被拦截或路径写死 | 拦截器放行static目录,用${pageContext.request.contextPath} |
| 数据库中文乱码 | 字符集不一致 | 统一UTF-8,URL加characterEncoding=utf8 |
| MySQL8连接报Public Key Retrieval | MySQL8新加密协议 | JDBC URL加allowPublicKeyRetrieval=true |
| 事务不生效 | 忘了加@Transactional或类没被Spring扫描 | Service实现类加事务注解与@Component |
| 库存为负 | 扣减库存未加数量条件 | UPDATE带stock_num >= #{num} |
| 采购单成功但库存没变 | 库存表无对应药品初始记录 | 入库逻辑处理为“有则加、无则insert” |
| 页面提示包不存在 | Maven依赖未完整下载 | 配置阿里云镜像后重新reimport |
避坑清单总的来说就三条:第一,不要盲目追求最新版本,Tomcat选对,JDK老老实实用8;第二,外部资源要先确认可用再启动,数据库能连上再点运行;第三,改任何配置前先备份,尤其是jdbc.properties和web.xml,这两个文件是系统能跑起来的命根子。
7. 个人经验与项目扩展思考
7.1 这类管理系统最容易忽略的几个设计细节
把这套系统完整跑通之后,我回过头想了一下,其实进销存类项目要做得好,“能跑”只是第一步,真正拉开差距的是几个容易被忽略的细节。
第一个是幂等性。用户连续点两次提交订单,系统不应该生成两笔一模一样的订单。严谨做法是在后端生成唯一订单号,并在数据库订单表中给订单号加唯一索引,重复提交时直接报错或忽略。
第二个是库存流水可追溯。有的版本没有库存流水表,销售出库后无法知道历史库存变动。我建议你在这个项目里自己加一张inventory_log表,每次出入库都记录操作时间、操作类型、变动数量、操作人,哪怕只是给答辩加分,这个表都很值。
第三个是搜索和筛选条件。药品列表页如果只能翻页,没有任何条件筛选,系统会显得特别业余。加一个按药品名称模糊查询、按分类下拉筛选,代码量不大,但用户体验提升非常明显。
第四个是前端校验与后端校验的结合。我在测试这个项目的时候,试着在价格输入框里填了负数,如果后端没有做校验,进价是-100的药也能入库。正规系统必须做“前端提示 + 后端校验”双重保障,后端的校验绝对不能省。
7.2 如果重新做一遍,我会怎么升级
如果你已经把这套SSM源码跑通,想进一步练手,我建议可以往这几个方向升级,难度逐渐递增:
- 引入Maven多模块拆分:把pojo、mapper、service、controller拆成独立模块,虽然工程结构变复杂,但能让你对分层架构的理解更深刻。
- 把JSP替换成前后端分离:后端只返回JSON,前端用Vue或纯HTML+Ajax。改造过程中你会更清楚Controller返回数据与返回视图的区别。
- 增加药品效期管理:给药品加生产日期、有效期字段,在库存查询时自动计算临期药品并预警,这才是药店系统真正的业务难点。
- 引入Redis缓存热点药品:前台首页的爆款药品列表可以缓存起来,体验一下传统SSM项目集成Redis的过程。
- 用Spring Boot重写一遍:这是最经典的进阶路线,把XML配置改成注解和application.yml,你会发现Spring Boot对开发者有多友好。
做了这么多套管理系统,我个人最深的体会是:SSM框架本身并不难,难的是把“数据在页面、Controller、Service、Mapper、数据库之间完整流转”这件事理解透。龙康药店这套项目虽然代码量不算少,但胜在业务链路完整,从前台购物车到后台库存管理,每一步都有真实的业务动机。你能把它完整跑起来、能说清每个表为什么存在、每个事务为什么加,这套系统才算真正变成你自己的东西。第一次跑不通过很正常,不要急着删代码,先看日志、再查配置、最后看路径,绝大部分问题都是这三个环节里的低级错误。