ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SpringBoot+Vue图书进销存系统:从数据库设计到部署的完整实战指南

SpringBoot+Vue图书进销存系统:从数据库设计到部署的完整实战指南 每年到毕设季我都能看到大量同学在问同一个问题图书管理这种题目已经做烂了还能不能选我的答案是能而且它恰恰是最适合拿来当毕设/课设的题目之一。图书馆管理系统、图书进销存这类项目业务上是一个完整闭环——采购、入库、出库、库存、统计全串起来了技术上又刚好踩在SpringBoot Vue MySQL这套Java Web主流组合上。既不像电商系统那样庞大到一个人写不完也不像增删改查的Demo那样没有深度。我自己帮学生改过不少类似源码也辅导过好几套从零实现的SpringBoot Vue图书进销存项目今天就把整个项目从需求拆分、表结构设计、后端接口、前端页面到部署避坑的完整思路过一遍。这套博文主要写给三类人第一类是真的要拿它做毕设或课设的在校学生第二类是刚学完SpringBoot和Vue、想找一个完整项目练手的初学者第三类是接手了这类源码但改不明白、部署不起来的同学。我会把核心逻辑讲透给出可以直接抄的数据库设计和代码思路也会把那些文档里不会写、但你一定会遇到的坑都列出来。1. 项目到底要做什么从业务痛点聊到模块拆分1.1 图书进销存的业务闭环先别急着写代码先把“进销存”这三个字拆开。图书进销存系统处理的不是“摆个书架卖书”这么简单它的核心业务流程是采购人员从供应商进货图书进入仓库仓库里的库存数量随之增加销售人员在门店或线上卖出图书库存随之减少在这个过程里管理员需要随时知道仓库里有哪些书、每种书还剩多少、哪些书快卖完了需要补货、哪些书积压很久卖不动。这是一个标准的供应链场景翻译成IT系统语言就是三个核心操作**进货入库对应入库单操作结果是库存增加。销售出库对应出库单操作结果是库存减少。库存盘点与预警对应库存查询和库存状态维护。很多人把这个系统做成纯粹的“单表增删改查”图书表里放一个stock字段卖一本书就update一下。这种写法应付答辩也许可以但业务逻辑是残缺的——你没有入库记录、没有出库流水、没有操作审计将来想扩展统计报表和预警功能就得推倒重来。一个合格的图书进销存系统必须把每一次库存变动都落到一条“流水”上图书表的库存字段只是一个计算结果而不是数据源头。这也是为什么我在设计这个项目时坚持把“出入库单”作为整个系统的业务核心而不是把“图书表”当成核心。理解了这一点你就理解了整个项目的骨架——图书、供应商、客户只是基础档案入库单和出库单才是每天发生的核心业务活动。1.2 功能模块怎么拆才合理对于毕设项目来说功能不是越多越好而是“闭环完整、界面看着像样、答辩有话可说”。一个合理的图书进销存系统个人建议砍成七个模块每个模块都对应明确的操作场景模块核心功能对应页面系统管理登录、用户管理、修改密码登录页、用户管理页图书管理图书新增、编辑、删除、分页搜索图书列表页分类管理图书分类维护分类列表页供应商管理供应商维护供应商列表页采购入库生成入库单、入库记录查询入库单页销售出库生成出库单、出库记录查询出库单页库存管理实时库存查询、库存预警、盘点调整库存页、仪表盘其中系统管理、图书管理、采购入库、销售出库、库存管理这五个是核心分类和供应商属于配套的基础数据。分类可以合并到图书页里用下拉框选择供应商也同理但独立页面在答辩时更容易展示“系统功能全面”。我见过不少学生一上来就要做“报表导出Excel”“导入Excel批量录入”“角色权限细粒度控制”这些功能不是不能做但每一块都会明显拉长开发周期。建议第一版先把主线业务跑通让入库、出库、库存减少、预警这几个动作在页面上一气呵成再考虑锦上添花。毕设答辩时老师更看重的是你对这个系统有没有完整理解而不是功能清单有多长。2. 技术选型解析SpringBoot Vue MySQL为什么是黄金组合2.1 三个核心组件各自解决了什么问题先回答一个最常被问的问题为什么这套技术栈成了毕业设计的事实标准SpringBoot解决的是后端开发效率的问题。它对Spring生态做了大量自动配置内嵌了Tomcat不用打war包部署到外部容器一个java -jar就能跑起来。对于学生来说这意味着从零到能跑通接口的成本极低。更关键的是SpringBoot社区资料极其丰富遇到任何报错基本都能搜到答案这对项目工期紧、经验又不多的毕设场景来说太重要了。Vue解决的是前端交互复杂度的问题。图书进销存这种后台管理系统页面上大量是表格、表单、弹窗、下拉选择Vue的组件化开发加上Element UI这套现成组件库能把“图书管理页”这类CRUD页面的开发时间压缩到一两天。Vue 2的双向绑定语法对新手也很友好v-model一行就能搞定表单数据绑定比原生JavaScript操作DOM省太多功夫。MySQL解决的是数据持久化的问题。它是目前用的最广的开源关系型数据库免费、稳定、书多而且在面试里被问到的频率极高。图书进销存的数据结构非常规整——图书、供应商、单据、明细天然适合关系型数据库的范式建模。顺便说一下前后端分离这是这套方案的核心架构思想。原来JSP时代是后端把HTML页面拼好再发给浏览器现在是后端只提供JSON数据接口前端Vue负责渲染页面。分离的好处是后端只关注业务逻辑前端只关注交互展示开发可以并行代码职责清楚将来项目扩展也好维护。2.2 版本搭配建议版本坑是我最先想提醒的。很多同学SpringBoot版本选成了3.x然后发现JDK必须17起步MyBatis-Plus的配置方式也变了网上教程大部分是2.x的写法对不上号折腾一晚上连环境都没跑起来。这里直接给一套我实测稳定、资料最多的版本组合组件推荐版本说明JDK1.8大部分学校机房和教程都兼容SpringBoot2.7.x2.x最终维护版本稳定MyBatis-Plus3.5.x简化CRUD和分页MySQL8.0.x主流驱动注意加时区参数Vue2.6.x配Element UI最稳定Element UI2.15.x后台管理页面组件库Node.js16.xVue2项目构建稳定版本Maven3.6依赖管理有同学会问为什么不上Vue 3 Element Plus。我的答案是如果你是为了学习新技术Vue 3确实更好但如果你是为了在有限时间内把毕设做完Vue 2 Element UI的教程密度远大于Vue 3遇到问题几乎一搜就有答案。项目能顺利跑完、答辩能讲清楚比用多新的版本重要得多。2.3 环境准备清单动手写代码前先把环境准备齐全顺序也建议按这个来安装JDK 1.8配置JAVA_HOME环境变量命令行执行java -version验证。安装Maven 3.6配置阿里云镜像加速依赖下载mvn -v验证。安装MySQL 8.0注意设置root密码时不要设得太复杂本地开发推荐设为root或123456避免后面连接报错排查半天。安装Node.js 16.x自带npm用node -v和npm -v验证。开发工具建议后端用IntelliJ IDEA前端用VS Code两个工具各司其职。数据库连接时有个必踩的坑MySQL 8.0的JDBC驱动对SSL和时区有严格校验连接URL上必须加上useSSLfalseserverTimezoneAsia/Shanghai否则直接报SSL connection error。下单驱动也要注意用com.mysql.cj.jdbc.Driver老版本的com.mysql.jdbc.Driver在8.0下同样会报错。这个问题我在后面常见问题章节还会细讲。3. 数据库设计进销存系统的地基3.1 核心表结构与字段含义数据库设计是这类项目最见功力的地方也是最值得在答辩时展开讲的部分。一套好书进销存表结构不需要五花八门的设计核心就八张表左右我来逐一说明它们的作用。第一张是图书表book这是基础档案表字段包括字段类型说明idbigint主键自增isbnvarchar(32)图书ISBN唯一索引namevarchar(128)书名authorvarchar(64)作者publishervarchar(128)出版社publish_datedate出版日期pricedecimal(10,2)定价用decimal不用floatcategory_idbigint分类ID关联分类表stockint当前库存数量statustinyint状态0下架1上架注意price字段很多新手会用float或double答辩时老师问你“为什么用decimal”你只要回答“浮点数存在精度丢失问题涉及金额必须用定点数”这一句话就能加分。isbn建唯一索引也很重要它本质上是图书的商品编码重复录入会污染整个库存数据。第二张是用户表sys_user字段包括id, username, password, real_name, role, status, create_time。密码字段存储的是BCrypt加密后的密文不是明文这一点答辩也爱问。role字段用字符串比如ADMIN表示管理员USER表示普通操作员简单够用。第三张和第四张是供应商表supplier和客户表customer字段基本是id, name, contact, phone, address, status属于基础档案不需要复杂设计。第五、六、七、八张是业务核心表入库单stock_in、入库单明细stock_in_detail、出库单stock_out、出库单明细stock_out_detail。主表记录单据编号、操作员、入库/出库时间、备注明细表记录具体是哪本书、数量、单价、金额。3.2 为什么入库单要拆主子表这是数据库设计里最值得讲清楚的一步。一个入库单比如“今天从某供应商进了一批书”可能同时包含《Java编程思想》50本、《Spring实战》30本、《MySQL必知必会》20本——一张单据对应多本图书。如果不用主子表直接在图书表上记录“入库50本”那么“这批货是什么时候进的、从哪个供应商进的、当时的进价是多少”这些信息就全丢了。主子表的设计本质是把“一次业务事件”和“这次事件涉及的多条明细”分别建模。主表stock_in存单据头一个字段叫total_amount汇总总金额明细表stock_in_detail存单据体每行一条图书记录。查询时用stock_in_id关联逻辑清晰也符合纸质进货单的习惯。出库单同理一张销售单可能包含多本书所以也是主表和明细表的结构。这种设计在答辩时太常被问了我建议你自己主动讲出来为什么拆表因为一对多关系因为要保留业务流水和操作轨迹。库存流水其实是这套设计顺带得到的红利。只要所有库存变动都经由“入库单”和“出库单”走图书表里的stock只是一个汇总值真正可追溯的数据全在明细表里。将来你想加一个“库存变更历史”页面SQL一查就有不需要重新设计表结构。我用Navicat做表设计时的习惯是先建主表、再建明细表、最后建基础档案表建表时把每张表的注释写得清清楚楚。MySQL的comment字段一定要用不只是为了规范也方便答辩时直接展示给老师看。字符集统一用utf8mb4否则存中文书名或特殊符号会出现乱码。4. 后端核心实现从接口设计到关键业务逻辑4.1 项目分层与包结构后端工程建好后包结构我建议这样划分com.bookstore ├── controller # 接口层接收前端请求返回统一结果 ├── service # 业务逻辑层核心业务都写在这里 │ └── impl # 接口实现类 ├── mapper # MyBatis-Plus的Mapper层原名Dao ├── entity # 数据库实体类 ├── dto # 请求对象封装前端传参 ├── vo # 响应对象返回给前端的数据 ├── config # 配置类跨域、分页插件、拦截器 ├── common # 统一返回结果类、异常处理、工具类 └── BookstoreApplication.java为什么要分层每一层各司其职Controller只负责接收参数、调用Service、返回结果不写业务逻辑Service只负责业务规则比如“入库时要校验图书存在、更新库存、写明细”Mapper只负责SQL操作加上MyBatis-Plus之后甚至可以不用写SQL。答辩时老师问“为什么分层设计”核心答案是解耦和可维护性。统一返回结果类ResultT也是必写的它保证前后端对接时的格式统一Data public class ResultT { private Integer code; // 200成功500失败 private String message; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.code 200; result.message 操作成功; result.data data; return result; } public static T ResultT error(String message) { ResultT result new Result(); result.code 500; result.message message; return result; } }4.2 登录认证与权限控制登录功能是这个项目“看起来专业”的关键一环。最简单的方案是用JWT生成Token后端登录接口校验用户名密码成功后返回一个Token给前端前端存到localStorage里之后的每次请求都带上这个Token后端用拦截器校验。这个方案不需要引入Spring Security那一堆复杂配置对毕设来说足够了。核心代码分三部分。第一部分是登录接口PostMapping(/login) public ResultLoginVO login(RequestBody LoginDTO loginDTO) { // 1. 根据用户名查用户 SysUser user userService.getByUsername(loginDTO.getUsername()); // 2. 使用BCrypt校验密码 if (user null || !BCrypt.checkpw(loginDTO.getPassword(), user.getPassword())) { return Result.error(用户名或密码错误); } // 3. 生成JWT Token并返回 String token JwtUtil.createToken(user.getId(), user.getUsername(), user.getRole()); LoginVO vo new LoginVO(); vo.setToken(token); vo.setUsername(user.getUsername()); vo.setRole(user.getRole()); return Result.success(vo); }第二部分是JWT校验拦截器拦截除了/api/login之外的所有/api/**请求。这里有个细节拦截器验证Token失败后返回401但前端拿到的HTTP状态码可能无法正确识别所以我在拦截器里统一返回一个JSON格式的Result让前端能识别并跳回登录页。第三部分是密码加密。建库时不要手写明文密码用BCryptPasswordEncoder加密后入库。推荐用一段简单的初始化代码生成初始密码密文比如admin用户初始密码是123456在库中长什么样要心里有数。4.3 入库出库的关键代码实现入库业务是整个系统最核心的逻辑。简单说就是前端提交入库单后端校验图书是否存在计算入库总金额插入入库主表记录插入明细表记录最后批量更新图书库存。这四步任何一个失败数据就会处于半一致状态所以必须加事务Transactional(rollbackFor Exception.class) public void stockIn(StockInDTO dto) { // 1. 插入入库主表 StockIn stockIn new StockIn(); stockIn.setInNo(generateInNo()); // 生成入库单号如RK202401010001 stockIn.setSupplierId(dto.getSupplierId()); stockIn.setOperatorId(dto.getOperatorId()); stockIn.setTotalAmount(dto.getItems().stream() .mapToInt(item - item.getPrice() * item.getQuantity()).sum()); stockInMapper.insert(stockIn); // 2. 插入明细表每条明细对应一本图书 for (StockInItemDTO item : dto.getItems()) { StockInDetail detail new StockInDetail(); detail.setStockInId(stockIn.getId()); detail.setBookId(item.getBookId()); detail.setQuantity(item.getQuantity()); detail.setPrice(item.getPrice()); stockInDetailMapper.insert(detail); // 3. 更新图书库存stock 入库数量 bookMapper.increaseStock(item.getBookId(), item.getQuantity()); } }这段代码最值得注意的地方是Transactional(rollbackFor Exception.class)。如果你不加这个注解或者只写Transactional默认只在运行时异常时回滚一旦插入明细第二步出错主表记录已经写了后面不会回滚。加上了rollbackFor以后任何异常都会让整个方法回滚保证数据一致。出库逻辑和入库基本对称但多了一个关键校验扣减库存前必须检查库存是否充足否则会出现库存负数的尴尬。更严谨的做法是使用乐观锁更新最快的时候加一个condition。我在项目里用了UPDATE book SET stock stock - #{quantity} WHERE id #{bookId} AND stock #{quantity}这样的条件更新SQL数据库层面直接保证不会扣成负数返回影响行数为0就说明库存不足直接抛异常让事务回滚。这个“条件更新”的思路也很容易在答辩时展开讲它本质上是数据库层的原子操作避免了并发场景下两个线程同时卖最后一本书的问题。5. 前端Vue实现页面架构与交互逻辑5.1 路由菜单与页面规划前端工程用Vue CLI初始化入口页面是登录页登录后进入主布局。主布局我用Element UI的Container容器做左右结构左侧Aside是菜单栏右侧Header加MainHeader顶部放用户信息和退出登录按钮Main区域放路由组件。路由规划很直观const routes [ { path: /login, component: Login }, { path: /, component: Layout, redirect: /dashboard, children: [ { path: dashboard, component: Dashboard, meta: { title: 首页概览 } }, { path: book, component: BookList, meta: { title: 图书管理 } }, { path: stock/in, component: StockIn, meta: { title: 采购入库 } }, { path: stock/out, component: StockOut, meta: { title: 销售出库 } }, { path: inventory, component: Inventory, meta: { title: 库存查询 } }, { path: supplier, component: SupplierList, meta: { title: 供应商管理 } } ] } ];路由模式上我建议直接用hash模式而不是history模式。原因很实际hash模式打包部署后不需要后端做任何配置刷新不会出现404。history模式虽然地址更干净但打包后必须配合Nginx的try_files配置或SpringBoot的转发处理对于毕设来说完全没必要多踩这个坑。5.2 Axios封装与拦截器前端和后端对接的关节在于Axios封装。我见过太多学生代码里到处写this.$http.get、this.$http.post换来换去极不规范。正确做法是封装一个统一请求实例把BaseURL、请求头、Token注入、响应拦截都放在一个文件里// request.js import axios from axios; import { Message } from element-ui; import router from ../router; const request axios.create({ baseURL: /api, // 开发环境走vue.config.js代理 timeout: 10000 }); // 请求拦截每个请求自动携带Token request.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers.Authorization token; } return config; }); // 响应拦截统一处理返回结果和错误 request.interceptors.response.use( response { const res response.data; if (res.code 200) { return res; } else { Message.error(res.message || 请求失败); return Promise.reject(new Error(res.message)); } }, error { if (error.response error.response.status 401) { Message.error(登录已过期请重新登录); localStorage.removeItem(token); router.push(/login); } else { Message.error(网络异常请稍后重试); } return Promise.reject(error); } ); export default request;响应拦截器里处理401跳登录页这个设计让你不需要在每个页面里单独检查Token有效性写业务页面时干净很多。调用后端接口时组件里只写request.get(/books?page1)就行返回的response直接就是已经拆掉外层数据的结果非常好用。5.3 图书管理页的完整套路后台管理系统90%的页面都是同一个模式搜索条件表格分页新增/编辑弹窗。我以图书管理页为例给你一个可以直接复用的套路。页面Data部分需要维护四个核心状态搜索表单、表格数据、分页信息、弹窗表单和编辑状态。加载数据时调用分页查询接口async loadData() { const { data } await request.get(/books, { params: { page: this.pageNum, size: this.pageSize, keyword: this.searchForm.keyword, categoryId: this.searchForm.categoryId } }); this.bookList data.records; this.total data.total; }后端返回的格式是MyBatis-Plus分页结构的兼容格式records是列表total是总条数。这个字段名在前端写死之前最好先看一眼后端返回的真实JSON很多同学就是这里没对齐前端取不到数据页面一直空白。表格使用el-table操作列有“编辑”“删除”按钮。新增和编辑共用一个el-dialog弹窗区别是编辑时表单回填当前行数据用一个editMode布尔值区分。弹窗里的el-form需要配置:rules校验书名、ISBN、价格是必填项价格还要用自定义校验器确保是数字且大于0。分页组件用el-pagination注意配置current-change和size-change两个事件页码和每页条数变化时重新调用loadData()。整套逻辑写熟了以后一天写完一个完整模块不成问题。6. 前后端联调与部署6.1 开发环境联调前后端分离开发时最常碰到的第一个问题是跨域。你前端跑在http://localhost:8080后端跑在http://localhost:9090浏览器会拦截不同源的请求。解决办法有两种后端加CORS配置放开跨域或者前端用开发服务器代理。我推荐后者因为生产环境前后端同源部署时根本不需要CORS代理只在开发环境生效// vue.config.js module.exports { devServer: { port: 8080, proxy: { /api: { target: http://localhost:9090, // 后端服务地址 changeOrigin: true } } } };这样前端代码里所有请求都写相对路径/api/xxx开发环境下由Vue的devServer帮忙转发到后端生产环境下由Nginx或SpringBoot接管同一个域下的/api代码完全不用改。联调时需要特别注意前后端对接的JSON字段大小写和命名风格。Java后端默认返回camelCase命名比如createTime前端取数据时也要写成createTime。如果用了什么转换插件改成snake_case前后端必须统一。6.2 打包部署毕设演示和答辩最好直接打包成一个jar文件一键启动这样在老师电脑上也方便展示。操作流程很简单前端执行npm run build生成dist目录。将dist目录下的所有文件复制到后端项目的src/main/resources/static/目录下。后端执行mvn clean package -DskipTests生成可执行jar文件。命令行执行java -jar 项目名.jar浏览器访问http://localhost:8080就能看到完整系统。这里有个细节Vue打包后的静态资源路径默认是/根路径单独部署没问题但如果你的SpringBoot项目有server.servlet.context-path配置页面会变成白屏。解决办法是打包前改vue.config.js里的publicPath: ./用相对路径加载资源就能解决。打包进jar的方式适合毕设演示因为一个文件复制走就能跑。如果你想把前后端分离部署前端dist放到Nginx后端jar单独跑也没问题但做毕设演示往往没这个必要。7. 常见问题与排查实录7.1 后端常见问题速查表问题现象原因分析解决方案启动报SSL connection errorMySQL 8驱动要求显式SSL设置连接URL加useSSLfalseserverTimezoneAsia/ShanghaiMaven依赖下载慢或失败默认中央仓库网络不稳定配置阿里云镜像mirrors.aliyun.com分页查询失效返回全部数据没配置MyBatis-Plus分页插件配置PaginationInnerInterceptorMySQL方言选MySqlDialectLocalDateTime序列化报错默认序列化不支持Java8时间加JsonFormat(pattern yyyy-MM-dd HH:mm:ss, timezone GMT8)数据库导入SQL后中文乱码建库字符集不是utf8mb4建库语句指定DEFAULT CHARSETutf8mb4首当其冲的坑是MySQL 8的连接。我见过太多学生在这里卡住报错里写着SSL connection error或者Public Key Retrieval is not allowed第一反应是装了一个假MySQL。实际上90%的情况都是JDBC连接URL的问题把参数补上就解决了。分页不生效也是高频问题。MyBatis-Plus的分页需要手动注册PaginationInnerInterceptor拦截器很多人以为引入依赖就万事大吉结果分页接口忽略page参数一次返回全表数据。项目初期就把这个配置类写好后面所有分页查询都省心。7.2 前端常见问题速查表问题现象原因分析解决方案页面白屏控制台报错打包后publicPath路径不对vue.config.js里设置publicPath: ./重新build接口请求跨域报错开发环境端口不同配置devServer代理避免内网跨域刷新页面404用了history路由模式改成hash模式或后端配置history fallback登录后跳转不了路由守卫写法问题beforeEach里检查Token不存在时重定向/login表格数据取不到后端返回字段层级理解错误先在后端JSON里确认{code, message, data}结构再取data.records前端最常见的白屏问题看起来像是代码写错了其实只是路径问题。我排查这个问题的固定套路是先看浏览器网络面板确认静态资源有没有加载成功再看Console报错区分是Failed to load resource还是Cannot read property of undefined。前者基本是路径问题后者才是代码问题。7.3 答辩时的几个高频问题做图书进销存这类题目答辩老师翻来覆去就那几个问题提前准备好答案哪怕代码有小瑕疵也能从容应对。第一个必问题库存扣减的并发安全怎么保证千万不要回答“我用synchronized锁了方法”你只需要说我用的是数据库条件更新UPDATE book SET stock stock - #{quantity} WHERE id #{id} AND stock #{quantity}数据库原子地完成判断和扣减不会超卖。这一句就能体现你对并发控制有底层理解。第二个常问题为什么拆分入库单主表和明细表两张表回答思路是一个入库单包含多本图书是一对多关系拆表符合数据库范式同时保留完整业务流水方便后期统计每个供应商供货情况、每本书的进出货历史。第三个必问题密码为什么不存明文答案是使用BCrypt加密存储即使数据库泄露也无法直接还原出用户口令。这一步点出安全意识比单纯背功能列表好得多。还有个隐藏加分项主动说你的系统用了Transactional保证入库出库的事务一致性。老师听到这一句就知道你是真的写过代码、不是从网上扒下来改个名字的。8. 我做这个项目的具体心得与扩展方向这套图书进销存系统我带过的学生最快三周从零写完最慢的拖了两个月差别不在代码能力而在有没有先想清楚“数据怎么流转”。我反复跟他们强调一句话先画业务流程图再画数据库ER图最后才动手写代码。连供应商和图书都没有分开建模的项目后面每加一个功能都是灾难。我给准备拿这套题做毕设的同学一点实操建议第一源码可以看但核心逻辑一定要自己敲一遍入库、出库、事务、分页这四个点可能被老师抽查第二页面上的提示信息别干巴巴写“操作成功”写成“图书《xxx》入库成功库存变为xx本”这比功能本身更让人眼前一亮第三数据初始化时多造一些真实感强的数据比如书名、ISBN、出版社都用真实图书演示时老师一看就知道不是随手乱填的。最后再分享一个我一直在用的扩展思路这个系统骨架其实放到任何“进销存”场景都能用把图书换成商品就秒变成超市进销存加上客户前台就变成电商后台。我自己做完这套之后又基于同样的表结构把图书销售统计做成了ECharts图表——折线图看月度销售趋势、柱状图看分类热销排行效果相当直观。等要把书卖到线上时再接个微信扫码登录和订单模块也不费劲因为地基打好了。做完一个项目收获最大的往往不是那一行行代码而是“从业务里拆模型、从模型里建表、从表里写接口、再从接口做页面”的完整链条。把这套思路走通一遍后面无论遇到什么管理系统题目你都会发现它们骨子里长得差不多——这也正是这类经典题目至今仍在毕设里流行的真正原因。
返回列表