尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

小型超市商品管理系统设计与实现——基于Spring Boot+MyBatis

小型超市商品管理系统设计与实现——基于Spring Boot+MyBatis 很多搞技术的同学尤其是计算机相关专业的朋友应该都遇到过这种场景课程设计要交一个系统图书馆里翻半天借不到合适的网上下载的“源码”要么跑不起来要么前后端缺胳膊少腿。今天要拆解的这套“小型超市商品管理系统的设计与实现”就是那种你拿到手就能直接用的完整项目还带源码。项目编号43914属于典型的Java Web课程设计/毕业设计选题核心场景是模拟一家小型超市的进货、销售、库存、商品管理等日常业务功能不多不少刚刚好。这套源码最值钱的地方在于它的“完整度”不是概念讲解而是真正能运行、能演示、能答辩的项目。适合三类人一是正在选课设题目、需要快速搞定系统的在校学生二是想学习Spring Boot整合实战、但对项目结构还没形成概念的初学者三是想把手头业务快速信息化的个体经营者拿它改改就能当个免费的进销存工具用。下面我把这套“设计与实现”从思路、选型、源码到实操逐一拆开讲清楚顺便把踩过的坑也一并整理了。1. 项目全景拆解这套源码到底解决了什么问题1.1 小型超市的日常管理痛点做这个项目之前得先搞清楚“小型超市”和“大超市”的管理差别。大超市有完整的ERP系统有专业的收银软件员工、供应商、会员、促销活动全都是模块化运营。但小型超市比如校园超市、社区便利店往往只有一两台收银机甚至根本没有收银系统进货靠本子记库存靠眼睛看销售流水全凭老板脑子记。这套系统的设计目标就是把这些手工活搬到线上。你在电脑上建好商品档案录入一次商品名称、进价、售价、库存预警值之后每次进货只需要选择商品、填写数量库存自动增加每次销售只需要选择商品、填写数量库存自动扣减销售记录和营业额自动汇总。老板不用再每天扒拉着纸质台账盘库存月底对账也能直接导报表。所以从需求角度来看它不是一个“炫技”项目而是一个贴着小店日常场景做的实用工具。这一点也决定了它后续的模块划分不会太复杂——不需要财务模块、不需要复杂的权限体系、不需要消息队列能跑、好用、数据准确就是最大的成功。1.2 系统功能模块与核心业务流程从完整源码的页面结构和后台接口来看这套系统的功能大致可以分为五个核心模块首页看板展示今日销售额、订单数量、库存预警商品数、低库存商品列表等关键指标。商品管理商品分类维护、商品档案的增删改查、按名称或条码搜索、库存预警值设置。库存管理进货入库、退货出库、库存变动流水、当前库存查询。销售管理新建销售单、销售历史记录、销售明细查看。系统管理用户管理、密码修改常见版本还会拆出供应商管理和会员管理。核心业务链条是“建档 → 进货 → 销售 → 统计”四个环节围绕商品这个主数据展开。商品档案是所有业务的基础没有建档就没法进货没有进货库存就是零销售时也就无法扣减库存。进货和销售的数据变动最终都会落到库存流水表里这样出了问题也方便追溯。多角色这个点也值得注意。最基本版本通常是管理员和收银员两个角色管理员能做全部操作收银员只能操作销售和查询商品。别小看这个简单的权限区分它在答辩时是很好的加分项也贴合真实场景——你不会希望随便一个收银员都能改商品价格和删除库存记录。1.3 从课设到生产这套源码能用到什么程度必须实事求是地说它的定位是“能跑的完整课设项目”不是商业级SaaS系统。但如果你真的把它用在一家小型便利店里只要数据量不大几千个SKU以内、单机使用它完全能胜任。我实测下来单机部署、每天几百笔销售流水以内性能和稳定性都够用。它的局限也明显没有多门店支持没有自动备份机制没有复杂的促销规则比如满减、会员折扣前端也不够精美。但作为“白嫖”来的源码你能用它学到Spring Boot项目的基本分层架构、MyBatis的操作方式、CRUD和简单业务事务的实现这些都是后续做复杂项目的地基。2. 技术选型与设计思路为什么是Spring Boot MyBatis MySQL2.1 技术栈骨架与选型逻辑我见过很多同学拿到源码第一件事就是急着启动结果报错一堆原因之一就是没搞懂技术栈。这套系统的后端用的是Spring Boot搭配MyBatis部分版本用MyBatis-Plus操作数据库前端有服务端渲染Thymeleaf和前后端分离Vue Element UI两种常见形态。如果你拿到的源码里有application.yml配置上写着spring.thymeleaf.prefix那多半是Thymeleaf版本如果源码里有package.json和src/views目录那就是Vue版本。为什么选这套组合核心原因是四个字省心、通用。Spring Boot最擅长解决Spring项目繁琐的配置问题内嵌了Tomcat没有传统SSH项目一堆XML配置的麻烦打成一个Jar包就能跑这对课程设计和中小型管理系统非常友好。MyBatis则把SQL和Java代码解耦给了开发者完全控制SQL的能力查询复杂报表时可以直接手写SQL比JPA那种自动生成的查询更灵活排查问题也更直观。MySQL的普及度不用多说免费、成熟几乎是国内Java项目的默认选择。另外很重要的一个原因是简历价值。如果你是学生用Spring Boot MyBatis MySQL这套组合企业招聘时看到不会觉得陌生。这是国内中小团队最主流的组合之一学一遍这个源码等于提前适应了实际工作里经常遇到的技术环境。2.2 数据库设计核心表结构与关系源码配套的SQL脚本一般是sql目录下的.sql文件里通常有6到8张表。我拆开看了一遍最核心的是这几张user用户表字段包含id、username、password、role是最基础的登录认证表。category商品分类表一般就是id和name结构极简。goods商品表核心字段包括id、category_id、name、barcode、spec、price售价、cost_price进价、stock当前库存、min_stock库存预警值、unit单位。supplier供应商表记录供应商名称、联系人、电话。purchase_order进货单表有的版本拆成主表和明细表字段包含id、goods_id、supplier_id、quantity、price、total、purchase_time。sale_order销售单表包含id、goods_id、quantity、price、total、sale_time、operator_id等。stock_log库存变动流水表用type字段区分是进货in还是销售out记录变动前后的库存快照。表之间的核心关系是goods.category_id关联category.idpurchase_order.goods_id和sale_order.goods_id关联goods.idstock_log.goods_id也关联goods.id。业务上最重要的约束是所有库存变动都必须写流水不允许只改goods.stock而不写日志。这样做的好处是数据出现问题时有据可查答辩时也是亮点——说明你考虑了系统的可追溯性。这里要特别注意数据库的字符集设置。有些同学启动后页面商品名称是乱码就是当年建库时用的不是utf8mb4。执行SQL脚本之前先确认或者直接用Navicat/DBeaver新建数据库并选择字符集utf8mb4再导入脚本可以少踩一个坑。2.3 前后端交互与权限设计接口设计上这套系统遵循的是常见的Controller → Service → Mapper三级架构。Controller层负责接收请求、参数校验、返回结果Service层写业务逻辑比如进货时同时更新商品库存和写流水Mapper层只负责和数据库打交道。用生活化的比喻来说Controller是前台接待Service是业务专员Mapper是档案管理员客户前端页面直接跟接待沟通但真正办成事要业务专员去调档案。权限设计这块最基本的版本通常用拦截器HandlerInterceptor实现比较完善一点的版本会用Spring Security。用拦截器的逻辑很简单用户登录后把用户信息放Session定义一个LoginInterceptor拦截所有除登录页之外的请求检查Session里有没有登录用户再结合user.role字段判断当前用户能不能访问管理类接口。实测下来这种轻量级方案在小系统里完全够用代码量少、容易读懂比引入Security全家桶更适合初学者。如果你拿到的源码是Vue前后端分离版本前端会用Axios请求后端接口并在请求拦截器里带上Token通常是登录成功后返回的一个UUID或JWT。这一点在二次开发时要注意如果自己改了后端端口或增加了拦截规则前端也要同步调整否则会出现明明登录成功了请求却一直被拦截的诡异情况。3. 源码落地实操从下载到跑通的完整步骤3.1 环境准备清单开始运行前先把环境准备好。如果你拿到的源码说明里有技术栈清单严格照着来如果没有按我下面这套组合一般不会出问题软件推荐版本备注JDK1.8 或 11看pom.xml里的java.versionMaven3.6.x 以上IDEA自带也可以MySQL5.7 或 8.0建议5.7兼容性更稳IDEA2020.3 以上社区版也能跑前端依赖Node.js 14仅Vue版需要环境这里最容易翻车的是JDK版本不匹配。pom.xml里如果写着java.version1.8/java.version你却装了JDK 17编译时经常会出现一些莫名其妙的报错比如cannot find symbol或者invalid source release。如果你在Linux或macOS装了多个JDK建议用java -version命令确认当前默认的版本IDEA里也要在Project Structure里把SDK选对。Vue版本还额外需要配置Node.js环境。npm的下载速度在国内有时会很慢这个不是项目本身的问题我建议把npm registry换成国内镜像源配置方式是执行npm config set registry https://registry.npmmirror.com然后重新npm install速度会有质的提升。这一步不算什么违规操作只是国内开发者的常规优化手段。3.2 数据库初始化与配置修改拿到源码包先别急着点启动按钮把目录结构看一遍。一个完整的源码包通常包含project/ ├── pom.xml // Maven配置声明依赖 ├── sql/ │ └── supermarket.sql // 数据库初始化脚本 ├── src/ │ ├── main/ │ │ ├── java/ // 后端代码 │ │ │ └── com/xxx/supermarket/ │ │ ├── resources/ │ │ │ ├── application.yml // 核心配置文件 │ │ │ └── mapper/ // MyBatis的XML映射文件 │ │ └── webapp/ 或 static/ // 页面资源或前端打包产物 │ └── test/ └── README.md // 说明文档很重要初始化数据库的流程是打开数据库管理工具新建一个数据库名字可以保持和脚本里的保持一致通常是supermarket也有的叫shop_db字符集选utf8mb4排序规则选utf8mb4_general_ci。然后通过“运行SQL文件”功能把supermarket.sql导入进去。导入完成后检查一下看user表里有没有初始账号比如admin/admin123如果没有那说明脚本可能不完整需要在后续手动插入一条。接下来改application.yml。你需要改的无非是三个地方数据库地址url、数据库账号username、数据库密码password。需要注意的是MySQL 8.0的驱动名和5.7不一样8.0用com.mysql.cj.jdbc.Driver5.7用com.mysql.jdbc.Driver。如果你的pom.xml里依赖的是mysql-connector-java的8.x版本连接串里最好加上serverTimezoneAsia/Shanghai否则经常报The server time zone value错误。3.3 启动服务与常见踩坑后端启动很简单找到主类SupermarketApplication.java名字可能不同右键Run。看到控制台输出Tomcat started on port(s): 8080就说明启动成功了。前端如果是Vue版本在idea中打开前端目录npm install装完依赖后执行npm run dev默认会启动在localhost:9528常见配置。一个我反复遇到的坑是端口冲突。我本地装了太多服务8080端口经常被其他进程占掉。判断和解决的办法很直接启动失败日志里如果出现Port 8080 was already in use说明端口被占用打开任务管理器或者命令行执行netstat -ano | findstr 8080找到占用进程结束它或者干脆改application.yml里的server.port。我更推荐改端口省事比如改成8081但要记得前端请求地址里的端口也要一起改。再说一个和Lombok有关的坑。项目代码里大量用了Data注解简化实体类的getter/setter。如果你启动时报错说什么“找不到符号setId/getId”那多半是IDEA没有启用Lombok插件或者没开Annotation Processing。解决办法IDEA插件市场安装Lombok插件然后在Settings → Build, Compiler → Annotation Processors里勾选Enable annotation processing重启IDEA再跑。最后是页面登录。默认账号一般就写在README.md里常见的组合是admin/123456、admin/admin123也有不用数据库账号、直接在代码里写死一个默认账号的版本。如果登录页面报密码错误先去user表查一下初始化的账号信息不行就自己UPDATE一条记录进去。数据库里存的密码一般是MD5加密后的值不会明文存放这个要注意你UPDATE的时候不能直接改成123456要么用同样加密算法算出密文要么看一下登录逻辑是否支持明文再决定怎么改。4. 核心模块实现细节与代码解读4.1 商品管理模块分页搜索与CRUD的实现套路商品管理是整套系统的入口模块。它的核心功能不外乎分页查询、关键词搜索、新增商品、编辑商品、删除商品。这里我重点讲分页搜索的实现套路因为几乎所有管理系统的列表页都是这个套路。以MyBatis-Plus版本为例ServiceImpl里通常是这样写的public PageGoods listGoods(PageGoods page, String keyword, Long categoryId) { LambdaQueryWrapperGoods wrapper new LambdaQueryWrapper(); // 关键词模糊查询按名称或条码 if (StringUtils.hasText(keyword)) { wrapper.and(q - q.like(Goods::getName, keyword) .or().like(Goods::getBarcode, keyword)); } // 分类筛选 if (categoryId ! null) { wrapper.eq(Goods::getCategoryId, categoryId); } wrapper.orderByDesc(Goods::getId); return goodsMapper.selectPage(page, wrapper); }这个写法的好处是利用了MyBatis-Plus的LambdaQueryWrapper不用手写XML查询条件用Lambda方式表达类型安全写起来也简单。Controller里接收pageNum、pageSize、keyword、categoryId构造出Page对象传给Service最终返回给前端的数据里会包含总记录数total、当前页数据records前端表格就能渲染了。如果你拿到的是原生MyBatis版本那Mapper XML里对应分页查询的SQL大致是select idselectGoodsPage resultTypecom.example.supermarket.entity.Goods SELECT * FROM goods where if testkeyword ! null and keyword ! AND (name LIKE CONCAT(%, #{keyword}, %) OR barcode LIKE CONCAT(%, #{keyword}, %)) /if if testcategoryId ! null AND category_id #{categoryId} /if /where ORDER BY id DESC /select注意LIKE查询时不要自己拼字符串一定要用CONCAT或者#{keyword}参数绑定不然会有SQL注入风险。这一点在答辩时如果被问到“安全性”是一个很好的回答素材。4.2 库存与进货模块事务控制是灵魂进货模块是整个库存变动的起点也是检验这套源码水平的关键地方。做得好的版本进货时一定会同时做两件事加商品库存、写一条进货流水和库存变动日志。这两件事必须是一个原子操作要么一起成功要么一起回滚否则就会出现库存变了但没有流水对不上的脏数据。我贴一段典型实现逻辑值得反复看Transactional(rollbackFor Exception.class) public void purchase(PurchaseDTO dto) { Goods goods goodsMapper.selectById(dto.getGoodsId()); if (goods null) { throw new BusinessException(商品不存在); } BigDecimal total dto.getPrice().multiply(new BigDecimal(dto.getQuantity())); // 1. 增加库存 goods.setStock(goods.getStock() dto.getQuantity()); goodsMapper.updateById(goods); // 2. 写入进货单 PurchaseOrder order new PurchaseOrder(); order.setGoodsId(dto.getGoodsId()); order.setSupplierId(dto.getSupplierId()); order.setQuantity(dto.getQuantity()); order.setPrice(dto.getPrice()); order.setTotal(total); order.setOperatorName(dto.getOperatorName()); purchaseOrderMapper.insert(order); // 3. 写入库存流水 StockLog log new StockLog(); log.setGoodsId(dto.getGoodsId()); log.setType(IN); log.setQuantity(dto.getQuantity()); log.setBeforeStock(goods.getStock() - dto.getQuantity()); log.setAfterStock(goods.getStock()); log.setCreateTime(new Date()); stockLogMapper.insert(log); }Transactional这个注解是关键一旦方法中任何一步抛出异常数据库事务会回滚之前执行的update和insert都视为无效。这套流程里还隐含了一个细节beforeStock和afterStock的关系。先更新库存再取差值做流水逻辑上务必保证前后一致。我见过有人图省事不写流水日志系统中短期看不出问题一旦库存账实不符就会非常难查。所以哪怕源码版本里没有流水表也强烈建议自己加一张不管从业务还是答辩角度都有百利而无一害。销售模块的逻辑其实是进货的反向操作核心点在“防超卖”Transactional(rollbackFor Exception.class) public void sale(SaleDTO dto) { Goods goods goodsMapper.selectById(dto.getGoodsId()); if (goods null || goods.getStock() dto.getQuantity()) { throw new BusinessException(库存不足); } // 扣库存 写销售记录 写库存流水略 }先查库存、再判断、再更新这个在单机起步量级是够用的。如果要应对高并发那得加锁或用数据库乐观锁但小型超市管理系统完全没有这个必要。对于答辩来说能说清楚“为什么这么写”比单纯说“我用了事务”要加分得多。4.3 销售统计模块一段SQL搞定日营收很多同学看到统计功能就慌觉得要写很复杂的代码。实际上这类系统的统计报表核心就是几条SQL配合日期处理。拿“今日销售额”举例它的本质是查询销售明细表中当天的所有记录然后求总价和再统计商品件数。用MyBatis原生SQL可以这样写select idstatTodaySale resultTypemap SELECT IFNULL(SUM(total), 0) AS totalAmount, IFNULL(SUM(quantity), 0) AS totalQuantity FROM sale_order WHERE DATE(sale_time) CURDATE() /select如果是按月份统计或按商品排行改动也无非是换成DATE_FORMAT(sale_time, %Y-%m)做分组或者按goods_id分组后ORDER BY SUM(quantity) DESC LIMIT 10。权限问题要留意查询接口尽量不要直接用*返回全部数据后端做一层字段筛选会更安全也更显得你懂设计。首页看板的那几个数字基本也都是围绕类似的统计SQL来的。有些版本还会做“近七日销售趋势”实现方式是把DATEDIFF或日期区间写到SQL里再在Java里拼成图表要的结构化数据前端用ECharts画折线图。这块是整系统里技术含量比较高的部分答辩拿出来讲效果很好。5. 常见问题排查与避坑指南5.1 启动失败问题速查表部署运行这套系统时最常见的错误基本都集中在“环境不匹配”和“配置不对”两件事上。我把实际调试中遇到的高频问题整理成一张速查表症状可能原因解决办法启动类找不到项目没被识别为Maven工程右键pom.xml选“Add as Maven Project”Invalid source release: 8IDEA的Java编译器版本不对检查Project Structure里的SDK确认是JDK 8Cannot resolve symbol依赖没下载完整执行mvn clean install -DskipTests重新拉取Access denied for user数据库账号或密码错误核application.yml里的账号密码Unknown database库名不一致新建同名数据库或修改URL里的库名Public Key Retrieval is not allowedMySQL 8.0连接问题URL后加allowPublicKeyRetrievaltrue页面404前后端分离部署前端没启动确认Vue的npm run dev是否正常页面样式全丢静态资源路径配置错检查前端资源baseUrl或context-path配置登录后马上又跳回登录页拦截器把登录接口也拦截了检查LoginInterceptor的excludePathPatterns日期或金额显示乱码数据库字符集问题重建库并设置utf8mb4检查characterEncoding写表不算完重点说两个最容易被忽略的。第一个是拦截器排除路径。很多源码的WebMvcConfigurer里只放行了/login和/api/login但页面加载时还会请求/css/**、/js/**、/images/**这些静态资源一旦忘了放行就会看到登录页面光秃秃的只有一个HTML框架但不带任何样式。排查时打开浏览器F12看Network哪里红了就是没放行的路径加到排除列表里即可。第二个是MySQL 8.0的时区问题。新版本MySQL的时区默认是系统时区但Java连接时传了serverTimezoneAsia/Shanghai之后有时反而会报Unable to load异常。解决办法是在MySQL里执行SET GLOBAL time_zone 8:00再做一次持久化配置或者在连接串里只保留useSSLfalseserverTimezoneAsia/Shanghai别多带其他参数。5.2 二次开发的经验与方向源码跑通只是一个开始真正有价值的是你基于它做的二次开发。我建议按以下顺序去动手每做一步都有明显的增量收获先加“导出Excel功能”。用EasyPOI或Apache POI把商品列表、销售记录导出成Excel文件这个功能在课设答辩里的展示效果很直观以后工作中也高频使用。具体做法是给Controller加一个导出接口查询数据后通过EasyPOI注解或手动写Workbook输出到响应流。再加“库存预警提醒”。当前goods表已经有min_stock字段了前端首页看板通常也会显示预警数。但更进一步的做法是在登录后的页面顶部做一个全局弹窗提醒或者做一个“预警商品”的专门列表页把库存低于预警值的商品按紧缺程度排序放在最前面。做得再深一点可以把登录认证升级成Spring Security JWT。这个改造虽然工作量不小但对简历的含金量提升非常明显。步骤大致是引入spring-boot-starter-security和jjwt依赖写UserDetailsService实现用户加载自定义一个JwtAuthenticationFilter在请求头中解析Token最后配置SecurityConfig放行登录接口和静态资源。改造过程中你会对“认证”和“鉴权”这两个概念有远超课设水平的理解。5.3 拿到源码后的第一件事不是运行而是“审代码”最后分享一条我自己的习惯也是给所有白嫖源码玩家的忠告别急着跑先花30分钟把源码结构和关键代码过一遍。具体做三件事第一打开pom.xml或package.json确认依赖版本和你的本地环境是否匹配第二打开application.yml看数据库配置、端口配置、文件上传路径配置是否合理第三打开主业务表对应的Mapper和Service看它的事务注解、参数校验、日志记录是否到位。这套源码如果在这三点上都做得规范那它大概率是个靠谱项目如果某一点有明显缺陷你也能提前知道后续会遇到什么坑。我曾经帮同学排查过一套源码启动时报错的原因是一个application.yml里的上传文件大小限制写得太小导致图片上传功能怎么测都是“文件超出大小限制”。这类问题不看代码完全猜不到所以养成审代码的习惯对以后接手任何项目都有帮助。这套源码在我看过的同类课设项目里属于麻雀虽小五脏俱全的类型。从商品建档到库存流水从销售单到统计报表业务闭环是完整的代码分层思路也清晰。如果你能把它从头到尾跑通再动手改一两个功能模块你对Spring Boot项目的理解会比刷十套教学视频都扎实。后面你完全可以把这套骨架扩展到餐饮点餐、校园二手交易、小型图书管理等场景核心的进货出货逻辑本质都是通的改改表结构和页面文案就能换一个全新的项目。白嫖到的源码是起点能消化吸收变成自己的东西才是这套43914项目真正教你的事。
返回列表