ARTICLE DETAIL

资讯详情

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

SSM+Vue乐勤网书店毕设全流程实战:数据库设计、订单事务与前后端联调

SSM+Vue乐勤网书店毕设全流程实战:数据库设计、订单事务与前后端联调 说起毕设里的经典组合SSMVue这套前后端分离方案绝对排得上号。乐勤网书店这个题目我前前后后见过不少自己也亲手帮人改过好几版从数据库设计、后端接口到前端页面再到最后论文排版整条链路踩过的坑基本都摸清了。今天干脆把这条完整路线从头到尾捋一遍给准备做类似项目的朋友做个参考。不管你是第一次动手的毕设党还是想拿这种成熟业务练手攒经验的这篇应该都能帮你省下不少时间。先把这个项目能做什么说清楚它就是一个在线书店用户端可以注册登录、浏览分类、搜索图书、看详情、加购物车、下单结算、管理个人订单管理端能做图书上下架、分类维护、订单处理和用户管理。业务模型不复杂但功能链条特别完整适合拿来当毕设也适合用来理解前后端分离项目到底是怎么协作的。下面我会按项目全貌、数据库设计、后端实现、前端落地、论文写作、问题排查六个部分来拆全都是实操视角。1. 先看清这个项目的全貌它到底在做什么1.1 乐勤网书店的业务闭环做任何毕设项目第一步千万别急着写代码先把业务闭环理清楚。乐勤网书店表面上就是一个卖书的网站但仔细拆开看它的核心流程是用户注册登录后在首页浏览图书通过分类或搜索找到目标图书点进详情页查看内容和库存加入购物车结算时填写收货信息生成订单之后在“我的订单”里查看状态管理员则在后台维护图书和分类处理用户订单。这个流程本质上就是一个典型电商的最小闭环和天猫、京东那些大型系统的核心链路是同一个模型。区别只在于体量大小。为什么很多老师会认可这种题目因为它的业务逻辑清晰每个模块都能讲清楚“输入—处理—输出”论文的需求分析、系统设计、系统测试环节全都有内容可写。相比之下如果你选一个“推荐系统”之类的题目光算法部分就可能把自己埋进去答辩还不好解释。有一个点需要注意项目里的角色权限要分开。普通用户和管理员看到的页面完全不同。用户端是商城购买流程管理端是商品和订单管理。这体现在代码上就是后台的接口需要拦截器校验管理员身份前端的路由也需要做权限控制。这些细节做好了答辩时讲角色权限设计会显得项目很完整。1.2 为什么SSMVue这套组合值得选现在很多同学纠结技术栈看到别人用SpringCloud、微服务、Redis就觉得高大上。我的建议是毕设项目首先要考虑的是“你能否把每个技术点讲清楚”。SSMSpringSpringMVCMyBatis虽然听起来不那么新但它恰好覆盖了Java后端最核心的三块Spring负责对象管理和事务SpringMVC负责请求分发和参数绑定MyBatis负责数据库操作。你能把这套组合跑顺了基本Java后端的主线就通了。Vue这边负责前端页面和交互它采用组件化开发页面上每个独立模块都可以抽成组件复用比如图书卡片在首页用了、列表页用、后台管理也用写一次就能到处放。而且Vue生态成熟Element UI这类组件库拿来就能用界面做出来不会太丑。很多学校说的“SSM项目”其实允许用SpringBoot来搭底子因为SpringBoot本质上就是SSM的快速封装并不冲突。只要你的论文里技术选型部分能说清楚Spring、SpringMVC、MyBatis各负责什么用什么方式整合的都不会有问题。再谈一个问题前后端分离还是不分早年的SSM项目喜欢用JSP直接渲染页面但现在主流做法是后端只提供JSON接口前端用Vue独立开发。这两种方式区别很大前后端分离后后端开发人员只关心接口前端只关心页面联调时通过HTTP请求交互。毕设推荐做分离式原因有三个一是界面更现代二是论文里能写“前后端分离架构”这个亮点三是在简历上这个项目经验更贴近企业真实开发模式。2. 数据库设计先定表再谈功能2.1 核心表的结构与字段设计乐勤网书店的数据库设计是整个项目的地基表的数量和结构直接决定后续功能好不好写。我一般建议设计8张表左右既能覆盖完整流程又不至于把自己累死。核心表如下表名关键字段说明userid, username, password, nickname, phone, email, avatar, create_time用户表存注册信息和基本资料categoryid, name, parent_id, sort图书分类表支持二级分类bookid, category_id, title, author, publisher, isbn, price, original_price, cover, description, stock, sales, status图书表核心商品信息cartid, user_id, book_id, quantity, create_time, update_time购物车表临时购买数据ordersid, order_no, user_id, total_amount, status, receiver_name, receiver_phone, receiver_address, create_time, pay_time订单表下单核心表order_itemid, order_id, book_id, title, cover, price, quantity, subtotal订单明细表存下单时的商品快照commentid, user_id, book_id, content, rating, create_time图书评论表adminid, username, password管理员表后台登录用这里有几个字段值得单独说明。订单表里一定要有order_no这个唯一订单号它的作用不只是好看后续模拟支付、物流状态、对账都要靠它关联。生成方式可以用时间戳加随机数也可以用数据库序列毕设项目不需要太复杂保证唯一即可。order_item表我强烈建议存一份商品标题、封面、价格的快照而不是下单后实时去查book表。为什么因为商品价格会变图书也可能下架如果订单明细只存book_id用户查历史订单时看到的价格就可能是当前价格而不是他当时付的钱。这是电商系统里非常基础的设计思想也是论文里可以写出来的细节。金额字段统一用decimal(10,2)别用float。float在计算机里是二进制浮点数计算金额会出现0.10.2不等于0.3之类的精度问题到时候订单总金额对不上排查起来非常头疼。2.2 设计背后要避开的几个坑第一外键到底用不用很多教程喜欢强调物理外键但在实际项目里包括企业项目用得更多的反而是逻辑外键也就是在业务层维护表间关系不强制数据库去做约束。用物理外键的问题是删除父表记录时会受子表限制写分页查询时也容易因为关联查询遇到坑。乐勤网书店里用户表和订单表、订单表和订单明细表我建议都用逻辑外键靠Java代码保证一致性。第二购物车表一定要加唯一索引建议在user_id和book_id上建联合唯一索引。不然用户多点击几次“加入购物车”同一个图书就会在购物车里出现好几条记录逻辑混乱。加了唯一索引之后用INSERT ... ON DUPLICATE KEY UPDATE就能实现重复加入时数量累加的效果一行SQL解决这个麻烦。第三图书表的stock和sales两个字段要分开存。库存是当前可卖数量销量是历史累计卖出数量。很多新手会把这两个搞混或者只留一个后面做“按销量排序”功能时就发现没法排了。第四所有表的字符集统一用utf8mb4排序规则用utf8mb4_general_ci就行。别小看这个设置图书书名里经常会有特殊符号如果建库时用错字符集插入数据时直接报错或者存进去显示乱码。建库语句有点长但这条是最省心的一条经验。3. 后端从配置到接口一步一步把骨架搭起来3.1 SSM整合里最容易漏掉的三个点后端这块如果是用传统SSM整合方式需要同时维护web.xml、spring-mvc.xml、mybatis-config.xml这些配置文件第一次搭建很容易漏东西。我在这里把最容易导致项目跑不起来的三个点单独拎出来说。第一MyBatis的驼峰映射必须开启。数据库字段是create_time这种下划线风格Java实体类是createTime这种驼峰风格如果mybatis-config.xml里不配置mapUnderscoreToCamelCasetrue那么你查出来的createTime字段永远是null其他字段也有部分是对的。这个坑藏得很深因为项目能启动接口也能返回数据你就是莫名其妙发现时间字段是空的。第二事务注解的位置。下单这种涉及多表写入的操作必须在Service层加Transactional保证减库存、生成订单、清空购物车这三个操作要么全部成功要么全部回滚。如果事务加在Controller层Spring的代理机制很多时候不会生效如果干脆不加一旦减了库存后生成订单失败库存就白白扣掉了对不上账。第三Controller返回的JSON序列化问题。如果项目里配置了Jackson要注意对Date类型的格式化不然返回给前端的时间是一长串数字。在applicationContext.xml或SpringBoot的配置里加一个时间格式器全局统一成yyyy-MM-dd HH:mm:ss前端展示就省事很多。传统SSM整合还有一个让人头疼的点是jar包依赖。我建议如果学校允许直接用Maven管理依赖把spring-webmvc、mybatis、mybatis-spring、druid这些写进pom.xml里配合SpringBoot的starter更方便。项目能不能跑起来依赖版本冲突占了很大一部分原因。3.2 订单生成“这一个”事务比整个项目都重要如果只选一个功能重点突击我一定会选“提交订单”。这个流程最能体现一个开发者对业务和数据一致性的理解。我按实际代码拆解一下核心逻辑。用户点击提交订单后后端要做这样几件事接收前端传来的购物车选中项ID列表和收货人信息遍历选中项根据book_id查图书表核对当前库存是否足够计算订单总金额每个明细的价格乘数量后累加生成订单号插入订单主表循环插入订单明细表逐本扣减图书库存、增加销量清空购物车中已下单的商品。这里最关键的一步是扣减库存。很多新手会写成“先查询库存判断是否大于0再执行update”但这种方式在高并发下会出问题。理论上两个请求同时查到库存为1都判断可以买然后都执行update就超卖了。更稳的写法是直接在一条SQL里做条件更新UPDATE book SET stock stock - #{quantity}, sales sales #{quantity} WHERE id #{bookId} AND stock #{quantity}返回值是影响行数。如果返回0说明库存不够或者商品被下架了就抛出异常触发整个事务回滚。有人可能觉得毕设项目没有高并发不用考虑这些。但我会告诉你写这种带条件扣减的SQL一是代码确实更健壮二是答辩时老师问到“并发下怎么防止超卖”你就有了一个能对答如流的方案。这种细节是论文里“系统设计”部分最能加分的内容。3.3 统一返回体、拦截器与登录状态后端接口设计有一个常被忽略但其实特别重要的东西统一返回体。如果不做统一封装每个接口返回的数据结构都不一样前端处理起来会非常痛苦。定义一个Result类public class Result { private Integer code; // 200成功 500失败 401未登录 private String msg; private Object data; public static Result success(Object data) { ... } public static Result error(String msg) { ... } }所有的Controller都返回这个结构前端在axios响应拦截器里统一判断code如果是401就跳转到登录页。这个设计思路在简历上和论文里都非常好写而且确实能让我们少写很多重复代码。登录状态这块我建议用最直接的思路用户登录成功后后端生成一个token返回给前端前端把token存到localStorage里之后每次请求都在请求头里带上Authorization字段。后端用一个拦截器统一校验token除了登录、注册、图书列表、图书详情这些无需登录的接口外其他接口都校验。拦截器里要注意放行规则。有些同学写拦截器时搞错了白名单比如把“加入购物车”也放行了结果用户没登录就报错或者把“获取图书列表”也拦截了导致未登录用户连首页都打不开。我的经验是先写全拦截再一条条放行必须公开的接口多测试几次就稳定了。4. 前端Vue落地页面不是堆出来的是组织出来的4.1 路由与页面结构Vue项目我习惯用Vue CLI或者Vite脚手架初始化然后直接装Element UI组件库。页面上看乐勤网书店主要由以下路由组成路由路径页面说明/Home首页轮播图分类导航热门图书/listBookList图书列表页支持筛选排序/detail/:idBookDetail图书详情页/loginLogin登录/registerRegister注册/cartCart购物车/orderOrderList我的订单/admin/booksAdminBooks后台图书管理/admin/ordersAdminOrders后台订单管理路由建议用懒加载写法不要一次性把所以组件都打包进去const routes [ { path: /, name: Home, component: () import(../views/Home.vue) }, { path: /detail/:id, name: BookDetail, component: () import(../views/BookDetail.vue) } ]懒加载最直接的好处是首屏加载更快打包出来的js文件会按路由拆分成多个小块。这个细节放在论文的性能优化章节里也能算一小条。页面结构上建议把顶部导航栏和底部信息抽成公共组件所有页面复用。这样整体布局统一代码也不冗余。4.2 axios封装、跨域与联调细节前后端分离项目在本地开发时最头疼的就是跨域。前端运行在8080端口后端运行在8081端口浏览器的同源策略会让请求直接报错。解决办法有两种我建议在开发阶段用代理发布阶段用静态文件方案。开发阶段的代理配置几乎是一劳永逸的。在vue.config.js里这样写module.exports { devServer: { port: 8080, proxy: { /api: { target: http://localhost:8081, changeOrigin: true } } } }这样前端请求/api/books时开发服务器会自动转发到后端的8081端口前端觉得自己请求的是同源的 /api 路径就不会有跨域问题。注意后端Controller的路径请统一加上/api前缀和代理配置一致。axios封装我这里给一个非常精简的思路。新建src/utils/request.js核心做三件事设置baseURL为/api请求拦截器里从localStorage取token塞到请求头响应拦截器里统一处理code和错误提示。import axios from axios import router from /router const request axios.create({ baseURL: /api, timeout: 10000 }) 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 401) { router.push(/login) return Promise.reject(new Error(未登录)) } return res }, error { return Promise.reject(error) } ) export default request实际项目中可以根据需要再丰富这个封装但核心思路就是这三个。把请求层做好之后业务代码里调用接口就非常干净比如获取图书列表const res await request.get(/books, { params: { page, size, keyword } })4.3 两个必做的前端交互细节第一个是购物车的选中与金额计算。购物车列表里每个商品前面都有复选框还要有全选按钮底部实时显示“已选商品件数”和“合计金额”。这个功能看起来简单但涉及状态管理建议用计算属性来维护。在我的实现里购物车数据存在Vuex中当用户勾选或取消勾选某个商品时通过mutation更新选中状态合计金额用计算属性实时算出来。这个交互做完整个购物车页面就显得非常完整。第二个是按钮防连点。下单接口通常比普通查询慢如果用户手快点了两次提交订单就会重复生成订单并扣两次库存。解决方式很朴素点击后把按钮设置为loading状态禁用点击等请求返回后再恢复。在Element UI里Button组件自带loading属性加上就行。还有一个更底板的方法是前端做个变量锁请求发出去后置为true回来后再置为false。搜索功能可以做一个小优化在搜索框上加入防抖。用户持续输入时不立刻发起请求等停止输入300毫秒后再查避免每敲一个字母就请求一次后端。用setTimeout就能实现十几行代码用户体验提升明显。5. 论文怎么写才不浪费这个项目5.1 论文目录和项目进度怎么对应很多同学项目做完了论文却憋不出来原因就在于做项目时没有同步积累素材。其实论文目录和开发进度是可以一一对应的我列一下常见结构第一章绪论写选题背景、国内外研究现状、研究内容与意义。这一章可以在写完项目后回顾整理重点是别空谈把“书店行业线上化需求”和“当前购买体验痛点”写清楚。第二章相关技术介绍对应你项目用到的SSM、Vue、MySQL、Element UI。注意这里不是抄概念每门技术要写出它在项目里具体负责什么。比如Vue那节你要写“Vue用于构建用户界面通过组件化拆分页面通过Vue Router管理前端路由通过Axios与后端交互”这样老师能看出你真的用了。第三章需求分析对应你在开发前梳理的业务流程。画用例图、写功能需求、分析非功能需求。管理员和普通用户两个角色的用例要分开画权限设计在这里体现。第四章系统设计包括总体架构图、模块设计、数据库ER图和表结构。这里放SQL建表语句最合适但要删减字段注释别整篇都是代码。第五章系统实现对应每个功能模块展示页面截图和核心代码片段。这一章最好写也最容易写崩后面会说怎么控制篇幅。第六章系统测试写测试用例表覆盖登录、搜索、下单、库存扣减这几个核心流程。别写太多10到15个用例足够。5.2 让论文“看起来有工作量”的细节论文最容易犯的问题是贴了一堆代码页面截图却很少。老师评审时更关心的是“你能不能演示系统”而不是“你有没有把整个Controller粘过来”。我的经验是每个功能模块放一张截图加三五行说明关键代码只贴核心方法能贴SQL的贴SQL比如那条带条件扣减库存的UPDATE语句解释一下为什么这样写能防超卖。就这一个点足够让答辩老师觉得你有深度。数据库设计章节不要只贴建表语句要把每张表的字段说明写清楚我建议做成表格字段名、类型、允许为空、说明列出来。这种表格式的描述是最标准的学术表达。系统测试章节写用例表时格式要规范用例编号测试模块测试步骤预期结果实际结果是否通过TC-01用户登录输入正确用户名密码登录成功跳转首页与预期一致通过TC-02用户登录输入错误密码提示密码错误与预期一致通过TC-03图书搜索输入存在的书名关键词显示匹配结果与预期一致通过TC-04提交订单购物车勾选2本图书并结算订单生成、库存减少与预期一致通过TC-05提交订单商品库存为0时提交提示库存不足与预期一致通过5.3 交付程序时的资料清单毕设答辩前整个交付物不是只有代码我建议整理一个文件夹至少包含这些内容项目源码后端和前端分开目录清楚标注、数据库脚本一个完整的SQL文件包含建库建表语句和测试数据、论文Word档、答辩PPT、演示视频录一个两分钟的核心流程操作、README写清楚环境要求、启动步骤、默认账号密码。演示视频很多人懒得做但真到线上答辩或者老师收材料时它可能是最有用的一个文件。用系统自带录屏功能录制一遍注册登录、浏览图书、下单、后台管理这些核心操作注意别录到真实的身份证号、手机号这类信息系统里的测试数据尽量用假的。6. 毕设期间高频踩坑与排查方法6.1 我见过最多的几类报错前后端联调时跨域报错这是最典型的。症状是浏览器控制台出现“Access to XMLHttpRequest ... has been blocked by CORS policy”。原因就是前端端口和后端端口不一致。解决办法二选一开发阶段按前面说的vue.config.js配置代理或者在后端Controller上加CrossOrigin注解。但代理方式更接近真实项目推荐优先用代理。第二种是MyBatis查询结果字段为null。这个太常见了数据库字段是snake_caseJava属性是camelCase没开启驼峰映射查出来就是null。解决方式是在mybatis-config.xml里配置settings setting namemapUnderscoreToCamelCase valuetrue/ /settings第三种是前端构建报错比如热词里经常出现的“failed to load tsconfig vue/tsconfig/tsconfig.web.json: tsconfig not found”。这种问题多半是项目模板的依赖没装完整或者版本不一致。我的习惯是先删掉node_modules和package-lock.json重新执行npm install如果还不行检查package.json里vue/tsconfig的版本锁定一个稳定版本。这类报错八成是依赖版本问题不值得花大量时间研究底层原理。第四种是图片上传后前端访问不到。多数原因是你把图片保存到了本地磁盘的某个绝对路径比如D:/upload但网页访问的是http://localhost:8080/upload路径根本对不上。解决办法有两种一是把文件保存在项目里的static目录下二是配置虚拟路径映射让那个磁盘路径能通过URL访问。毕设项目最简单的方式就是把图片Base64存到数据库或者放在public目录下省去这种路径烦恼。6.2 速查表症状、原因、解决我把开发过程中常见的几个问题整理成速查表方便你对照排查症状大概率原因快速解决办法控制台CORS报错前端端口与后端端口不一致vue.config.js配置proxy代理或后端加CrossOrigin时间字段查出来是nullMyBatis未开启驼峰映射配置mapUnderscoreToCamelCasetrue打包后Vue的history路由刷新404后端没有做路由fallback改用hash模式或Controller转发到index.html数据库插入中文乱码数据库或连接串字符集不对建库使用utf8mb4url加characterEncodingutf-8页面登录后刷新状态丢失token存内存没持久化token存localStorage并在路由守卫里恢复同一图书购物车重复出现cart表缺唯一索引建(user_id, book_id)联合唯一索引前端样式不生效或互相污染组件style标签未加scopedstyle标签添加scoped属性图片上传后打不开文件路径与访问路径不一致统一使用相对路径或配置虚拟映射最后分享一点我个人做这类项目的体会整个乐勤网书店看起来功能不少但真正的核心链路其实只有一条就是“用户下单”。把这条链路从数据库到后端再到前端完整打通其余功能都是围绕它展开的枝叶。如果时间紧张先做这条主链路再补后台管理最后加评论、销量排序这些加分项。按这个优先级做哪怕最后几天赶工系统也是完整可演示的。我帮别人改过好几版这类项目凡是最后出问题的基本都是在细枝末节上死磕主线反而没跑通。这个顺序问题你提前规划好后边就能少熬几个夜。
返回列表