资讯动态

SSM与Vue商城系统毕设开发详解

发布时间:2026/10/14 12:23:22 来源:尧图企业网站定制
毕设选商城系统这套技术组合在国内的毕业设计里出现频率极高。SSM负责后端业务Vue负责前端交互MySQL存数据前后端分离跑通整个购物流程。很多同学第一次拿到这种项目时一脸懵代码量不小文档厚厚一叠还有配套讲解但不知道从哪看起也不知道答辩时老师会问什么。这篇文章把整个基于SSMVue的商城系统从需求拆解到数据库设计、从后端接口到前端页面、从部署打包到答辩准备全部过一遍既适合正在做这个题目的同学拿来对标自查也适合想从零学SSMVue商城开发的人当路线图用。这里有一个比较现实的情况市面上这类“毕设一条龙”服务很多源码、论文、开题报告、PPT全都备好。但如果你只是把代码拿到手跑通没有真正理解每个模块的设计动机答辩时被追问两三句就会露馅。所以本文的重点不是复述代码而是把每个关键设计背后的“为什么”讲透把实际开发中容易踩的坑提前排掉让你拿到项目后既能跑通也能讲明白。1. 项目整体定位与功能模块拆解1.1 为什么SSMVue成了商城类毕设的常客先说技术选型。SSM是Spring、SpringMVC、MyBatis三件套的合称国内Java教学体系里覆盖率极高几乎每个计算机相关专业的同学在校都写过。用SSM做后端意味着什么Spring负责对象的创建和依赖管理SpringMVC负责把浏览器请求映射到具体的Controller方法上MyBatis负责把Java对象和数据库表之间的操作做成接口加XML的形式。这三层分工明确正好对应了一个Web系统从请求进入到数据落库的完整链路。而Vue补上了SSM不太擅长的一环页面交互。传统JSP方案把Java代码嵌在HTML里改个样式要重新部署前端逻辑和后端逻辑搅在一起维护体验比较痛苦。Vue用组件化加数据驱动的方式组织页面一个网页拆成若干个.vue文件每个组件只管自己的数据和样式页面跳转交给Vue Router全局状态交给Vuex网络请求交给Axios。前后端彻底分离之后后端只返回JSON数据前端只负责渲染和交互职责边界非常清晰。为什么这套组合在毕设里热度这么高因为商城系统的业务场景刚好覆盖了框架的核心知识点Spring的事务管理、SpringMVC的拦截器和参数绑定、MyBatis的动态SQL和关联查询、Vue的组件通信和路由守卫。一套项目做完能聊的技术点非常密集比纯增删改查的管理系统有说服力得多。1.2 商城系统的功能边界哪些必须做、哪些可以不做商城系统听起来很大但毕设版本不需要做成大型电商平台。核心功能一般收拢为两条线用户购物线和管理员管理线。用户端必须有的功能是注册登录、商品分类浏览、商品搜索、商品详情、购物车管理、下单结算、订单列表和个人中心。这里的“下单结算”通常走模拟支付用户点击支付按钮后不需要真的跳第三方支付平台而是直接调用一个接口把订单状态从待付款改成待发货。这一点在开题报告和论文里要写清楚属于模拟实现避免答辩时被追问“怎么对接真实支付渠道”。管理员端必须有的功能是后台登录、商品上下架、商品编辑、分类管理、订单管理、用户管理。数据统计可以做简单的柱状图或者表格汇总也可以不做看时间和精力安排。不建议一开始就铺开做一堆页面先把主链路跑通再加扩展点。可以做但不是必须的功能包括多级分类、商品多图展示、优惠券、库存预警、购物车全选和批量删除、订单筛选导出。这些功能每一个都能作为论文里“系统特色”或者“后续展望”的素材但优先级要排在主流程后面。1.3 前台与后台的职责划分前后台分离是这套系统的核心组织思路。前台页面放在Vue项目里路由以 / 开头包含首页、商品列表、商品详情、购物车、结算、登录注册、订单列表后台页面也放在同一个前端项目里路由以 /admin 开头包含商品管理、分类管理、订单管理、用户管理。后台页面通常用一个框架型布局顶部是导航栏左侧是菜单栏中间是内容区。这套布局用Vue Router的嵌套路由实现父路由对应 /admin渲染一个带侧边栏的布局组件子路由对应 /admin/product、/admin/order、/admin/user渲染各自的管理页面。后端接口也需要区分前台接口和后台接口统一放在controller包下按模块命名。一种比较清晰的做法是前台接口用 /api/user/、/api/product/、/api/cart/、/api/order/ 前缀后台接口用 /api/admin/ 前缀。后台接口统一走管理员拦截器校验权限前台接口只对需要登录态的接口做用户校验。这个约定要在动手前定好不然开发到后面接口路径会乱成一团。2. 技术选型与开发环境准备2.1 后端三件套各自负责什么很多同学看到SSM的配置就头疼其实抓住主逻辑就不难。Spring是容器框架项目里所有的Service、Mapper、Controller实例都由Spring统一管理只需要在类上标注 Service、Controller、Mapper 之类的注解Spring启动时会自动扫描并注册这些Bean使用时通过 Autowired 注入。这样做的好处是对象之间的依赖关系不用手动new后续替换实现也方便。SpringMVC解决的是HTTP请求的映射问题。一个HTTP请求进来之后DispatcherServlet充当总入口通过HandlerMapping找到对应的Controller方法再通过HandlerAdapter调用它最后把返回值交给视图解析器处理。在前后端分离的项目里不需要JSP视图只需要在方法上加 ResponseBody 或者类上加 RestController返回的Java对象就会自动序列化成JSON写回响应体。MyBatis解决的是SQL和Java对象的映射问题。在Mapper接口里声明抽象方法在XML文件里写SQL语句通过命名空间和方法名绑定。XML里可以用 if、where、foreach 这些标签拼动态SQL比在Java代码里拼接SQL字符串要优雅得多也避免了拼接错误和注入隐患。需要说明的是MyBatis的SQL是手写的灵活度高但也要对SQL语法和索引比较敏感电商项目里常见的模糊查询、多条件过滤、分页都是它的典型场景。2.2 前端Vue项目结构怎么搭这里建议用Vue 2.x版本。原因很简单案例最多遇到问题搜索到的解决方案最全Element UI等组件库对Vue 2的支持也最稳定。Vue 3虽然更新但毕设场景里Vue 2的容错率更高学校老师也大多熟悉Vue 2的写法。前端源码结构按行业里约定俗成的规范来就好src/api存放所有接口请求方法按模块拆文件比如product.js、cart.js、order.jssrc/router路由配置文件定义所有页面的路径和组件同时配置路由守卫src/storeVuex状态管理至少要有用户信息模块存token和用户资料src/views页面级组件也就是路由直接指向的组件src/components复用型组件比如商品卡片、分页组件、数量输入框src/utils工具函数比如request.js这个axios实例的封装src/App.vue根组件src/main.js入口文件挂载Vue实例页面级组件不需要多复杂但组件内部不要写大段重复代码。商品列表页里的每个商品卡片抽成一个组件后台管理页面的弹窗表单尽量抽成子组件。这块结构清爽了后面加功能才顺手。2.3 环境版本对应关系与开发工具清单SSM项目涉及的环境组件比较多版本对了能少踩一半坑。我按实际验证过的组合列一份组件推荐版本说明JDK1.8很多服务器环境和教学环境都以8为主Maven3.6.x3.8也可以注意配置镜像加速Tomcat8.5或9.0打包成war部署时用MySQL5.7或8.08.0要额外注意时区参数Node.js12或14用于运行Vue脚手架和打包前端脚手架Vue CLI 4.x创建Vue 2项目最稳IDEIDEA后端主力前端也可以直接用IDEA打开JDK版本尽量控制在8不要用17。Spring和Tomcat老版本对更高版本JDK的兼容性存在一些已知问题而毕设项目一般用不到新增特性没必要在这个环节给自己找麻烦。MySQL用8.0的话JDBC驱动要选8.0对应的版本连接串加上?serverTimezoneAsia/Shanghai否则会有时区报错。Node.js版本过高时Vue CLI 4可能提示版本不兼容建议用nvm管理Node版本随时切换。3. 数据库设计与核心表结构3.1 这六张表就够了从业务反推数据模型基础版商城需要的表数量不多核心是用户、商品、订单、订单明细、购物车、分类再加上管理员。先理顺关系一个用户可以有多个购物车记录和多个订单一个订单包含多个商品所以订单明细作为订单的子表存在一个商品属于一个分类。这套关系对应经典的“一对多”模型。用户表是最基础的数据字段包括主键、用户名、密码、昵称、手机号、邮箱、头像、注册时间。密码字段只存MD5加密后的字符串长度为64位足够。用户名要做唯一索引不然重复注册会出问题。商品表需要关注的字段商品名称、分类ID、价格、库存、主图、描述、销量、上下架状态。价格字段用decimal(10,2)不要用float或double。浮点数在累加和比较时会有精度问题订单金额算错一位在演示时非常尴尬。库存字段在扣减时要配合条件更新后面下单模块细说。购物车表相对简单一条记录表示购物车里的一件商品主要字段是用户ID、商品ID、数量、是否勾选。为什么要单独的checked字段因为结算时通常只对勾选的商品生成订单这是一个很加分的细节。订单表和订单明细表是整个系统的核心设计是否合理直接关系到答辩观感。3.2 订单模块为什么拆分主表和明细表有些同学的初版设计会把订单和商品塞在同一张表里一个订单几行记录每行都带一套收货人信息。这样表面看省事实际上后患无穷。正确的设计是拆成两张orders记录一笔订单的整体信息order_item记录这笔订单包含的明细商品。为什么要拆因为订单有“一对多”的自然属性。用户在一笔订单里可能同时买三件不同的商品这三件商品对应同一个收货地址、同一个订单编号、同一个总金额。如果把这些信息重复到每一行明细里会造成大量冗余。更重要的是订单主表更新状态时只需要改一条记录明细表保持稳定不会出现同一订单的几行记录状态不一致的情况。订单明细表还要做“商品快照”。下单那一刻商品名称、价格、图片这些字段要原样复制一份到订单明细中而不是通过商品ID实时去查商品表。因为商品的价格和名称后续可能变动如果订单明细不存快照三个月后查看历史订单显示出来的可能是改版后的价格这在电商系统中是不允许的。这个细节在论文的数据库设计章节提上一句很能体现你理解了订单系统的本质。另一个值得说明的字段是订单状态status一般用数字表示0待付款、1待发货、2待收货、3已完成、4已取消。前端拿到数字后映射中文展示。相比直接用字符串描述数字更省空间也方便在SQL里做统计筛选。3.3 建表脚本与初始化数据准备下面是一套可以直接用的核心表脚本字符集统一utf8mb4避免中文和特殊符号乱码CREATE TABLE user ( id int(11) NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL COMMENT 用户名, password varchar(64) NOT NULL COMMENT MD5加密后的密码, nickname varchar(50) DEFAULT NULL COMMENT 昵称, phone varchar(20) DEFAULT NULL, email varchar(100) DEFAULT NULL, avatar varchar(255) DEFAULT NULL COMMENT 头像路径, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; CREATE TABLE category ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(50) NOT NULL, parent_id int(11) NOT NULL DEFAULT 0 COMMENT 父分类ID0表示顶级, sort int(11) NOT NULL DEFAULT 0, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品分类表; CREATE TABLE product ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(100) NOT NULL, category_id int(11) NOT NULL, price decimal(10,2) NOT NULL DEFAULT 0.00, stock int(11) NOT NULL DEFAULT 0, image varchar(255) DEFAULT NULL COMMENT 主图路径, description text, sales int(11) NOT NULL DEFAULT 0 COMMENT 已售数量, status tinyint(4) NOT NULL DEFAULT 1 COMMENT 1上架0下架, PRIMARY KEY (id), KEY idx_category (category_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品表; CREATE TABLE cart ( id int(11) NOT NULL AUTO_INCREMENT, user_id int(11) NOT NULL, product_id int(11) NOT NULL, quantity int(11) NOT NULL DEFAULT 1, checked tinyint(1) NOT NULL DEFAULT 1 COMMENT 是否勾选, PRIMARY KEY (id), UNIQUE KEY uk_user_product (user_id, product_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT购物车表; CREATE TABLE orders ( id int(11) NOT NULL AUTO_INCREMENT, order_no varchar(32) NOT NULL COMMENT 订单号, user_id int(11) NOT NULL, total_price decimal(10,2) NOT NULL, status tinyint(4) NOT NULL DEFAULT 0 COMMENT 0待付款1待发货2待收货3已完成4已取消, receiver_name varchar(50) DEFAULT NULL, receiver_phone varchar(20) DEFAULT NULL, receiver_address varchar(255) DEFAULT NULL, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), KEY idx_user_id (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单主表; CREATE TABLE order_item ( id int(11) NOT NULL AUTO_INCREMENT, order_id int(11) NOT NULL, product_id int(11) NOT NULL, product_name varchar(100) NOT NULL COMMENT 商品快照名称, product_image varchar(255) DEFAULT NULL COMMENT 商品快照图片, price decimal(10,2) NOT NULL COMMENT 商品快照价格, quantity int(11) NOT NULL, PRIMARY KEY (id), KEY idx_order_id (order_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单明细表;管理员表单独建一张字段更简单CREATE TABLE admin ( id int(11) NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL, password varchar(64) NOT NULL, role varchar(20) DEFAULT ADMIN, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT管理员表;初始化数据至少要准备一个管理员账号、一个测试用户、三个分类和十几条商品记录。管理员密码同样要MD5加密后再插入不要明文存储。商品描述可以留空或写简单文本主图路径建议使用相对路径比如 /images/product1.jpg这样部署之后图片还能正常显示。4. 后端核心功能实现要点4.1 登录鉴权Session方案与拦截器配置SSM项目的登录态有两种常见方案Session和Token。毕设阶段用Session实现起来更直观HttpSession本身是Servlet容器提供的机制登录成功后把用户ID放到Session里后续请求由拦截器检查Session里有没有这个值。Token方案则更适合前后端完全分离且有多端接入的场景需要引入JWT库逻辑稍复杂。单机部署的毕设项目建议直接用Session同时在SpringMVC配置中注册拦截器。拦截器是理解SpringMVC执行流程的重要组件它会请求到达Controller之前先执行preHandle方法在这里做权限校验非常合适public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(); Object userId session.getAttribute(userId); if (userId null) { response.setContentType(application/json;charsetUTF-8); response.getWriter().write({\code\:401,\msg\:\未登录\}); return false; } return true; } }注意拦截器只拦截需要登录态的路径。在SpringMVC配置文件里要放行登录、注册、商品查询等公开接口。很多人在这个环节栽跟头拦截器配置成拦截所有路径结果前端连商品列表都加载不出来响应全是401。最好的做法是后台管理路径 /api/admin/** 全部走管理员校验前台用户相关路径 /api/user/** 和 /api/order/** 走登录校验商品和分类查询直接放行。密码存储方面毕设中使用MD5加密是标准做法。严格来说MD5已经不算安全但如果你在论文里说明它仅用于课程演示并提到了加盐思路答辩老师是可以接受的。加密工具类可以直接用Spring自带的DigestUtils一行调用完成不用自己写算法。4.2 商品查询动态SQL与分页的配合商城首页和商品列表页是用户打开系统最先看到的页面查询性能直接影响体验。商品列表通常支持按分类筛选、按关键字搜索、按销量或价格排序这些条件不是每个请求都带上的所以要用MyBatis动态SQL来组装查询条件。select idqueryProductList resultTypecom.example.pojo.Product select * from product where if testcategoryId ! null and categoryId ! 0 and category_id #{categoryId} /if if testkeyword ! null and keyword ! and name like concat(%, #{keyword}, %) /if /where choose when testsort price_ascorder by price asc/when when testsort price_descorder by price desc/when when testsort salesorder by sales desc/when otherwiseorder by id desc/otherwise /choose /select分页最常用的方案是PageHelper插件。在查询前调用PageHelper.startPage(pageNum, pageSize)紧接着的查询会自动拼接LIMIT并返回PageInfo对象。PageInfo里包含总记录数、总页数、当前页列表等现成字段前端分页组件直接消费这个结构就行。这里有个使用细节要提醒PageHelper只对最近的一条查询生效。如果你的Mapper方法内部先执行了其他查询再执行主查询分页可能作用到错误的SQL上。所以分页查询方法里要保持简单不要塞多余的查询逻辑。排序字段如果允许前端传入最好用白名单机制限制排序方式避免用户构造非法字段名导致SQL异常。4.3 下单链路事务、库存扣减与订单号生成下单是商城系统里最能体现技术含量的环节也是答辩时老师最喜欢深问的部分。一次完整的下单涉及多个数据表的变更生成订单主记录、生成订单明细、扣减商品库存、清空购物车中已购买商品。这四步必须保证要么全部成功要么全部失败绝不允许出现订单生成了但库存没扣的中间状态。Spring的Transactional注解就是解决这个问题的。在Service方法上加这个注解Spring会把方法包裹在事务里任何一步抛出RuntimeException都会触发整体回滚。要注意事务默认只对运行时异常生效如果你在自己的代码里捕获了异常并正常返回事务是不会回滚的。所以事务方法的错误处理要格外谨慎不要在事务方法内部随便try-catch。库存扣减要防超卖。最直接的方式是先查库存判断大于等于购买数量后执行更新。但在并发场景下这个先查后改的操作中间会出现时间差可能导致超卖。更好的写法是一条SQL完成校验和扣减update product set stock stock - #{quantity} where id #{id} and stock #{quantity}这条SQL执行后返回的影响行数如果为0说明库存不足或商品已被并发请求改掉抛出异常终止下单。这个点写进论文的“并发控制”小节整个系统设计的专业度会明显提升。订单号生成也是一个容易被问到的地方。最简单的方案是时间戳加随机数拼接比如 System.currentTimeMillis() 拼接四位随机数再带上用户ID尾号。在演示场景下基本不会重复加上唯一索引兜底后即使极端重复数据库也会阻止插入。如果追求更好效果可以用“日期用户ID自增序列”的组合但毕设里时间戳方案已经够用。创建一个订单的核心流程整理成代码大概是下面这样Transactional public Order createOrder(Long userId, ListLong cartIds) { // 1 查询购物车选中的商品记录 ListCart cartList cartMapper.selectByIds(userId, cartIds); if (cartList.isEmpty()) { throw new RuntimeException(未选择商品); } // 2 遍历购物车记录检查库存并扣减 BigDecimal totalPrice BigDecimal.ZERO; ListOrderItem orderItems new ArrayList(); for (Cart cart : cartList) { Product product productMapper.selectById(cart.getProductId()); if (product null) { throw new RuntimeException(商品不存在); } int rows productMapper.decreaseStock(product.getId(), cart.getQuantity()); if (rows 0) { throw new RuntimeException(商品库存不足); } // 构造订单明细快照 OrderItem item new OrderItem(); item.setProductId(product.getId()); item.setProductName(product.getName()); item.setProductImage(product.getImage()); item.setPrice(product.getPrice()); item.setQuantity(cart.getQuantity()); orderItems.add(item); totalPrice totalPrice.add(product.getPrice().multiply(new BigDecimal(cart.getQuantity()))); } // 3 插入订单主表 Order order new Order(); order.setOrderNo(generateOrderNo(userId)); order.setUserId(userId); order.setTotalPrice(totalPrice); order.setStatus(0); orderMapper.insert(order); // 4 插入订单明细 for (OrderItem item : orderItems) { item.setOrderId(order.getId()); orderItemMapper.insert(item); } // 5 删除购物车记录 cartMapper.deleteByIds(userId, cartIds); return order; }下单之后模拟支付接口只需要把订单状态从0改成1然后管理员在后台发货把状态改为2用户确认收货改为3。整个状态机要保持单向流转不允许从已完成改回待付款之类的乱跳可在Service层加状态判断不满足前置状态就拒绝更新。5. 前端Vue开发与前后端联调5.1 页面路由与组件拆分前端部分从路由设计开始。Vue Router的配置关键点用嵌套路由拆分前台和后台两套布局。前台路由结构大致是/ 渲染主布局包含顶部导航和底部区域子路由 /home 指向首页/product 指向商品列表/product/:id 指向商品详情/cart 指向购物车/order 指向订单列表/login 和 /register 对应登录注册页。后台路由结构则是/admin 渲染后台布局带独立侧边栏子路由包含 /admin/product、/admin/category、/admin/order、/admin/user。这样设计的好处是前台和后台页面结构完全解耦代码目录一目了然。路由守卫方面在main.js或者路由文件里注册全局前置守卫判断目标路由的meta字段。比如购物车、结算、个人订单这些页面要求登录就在meta里设置 requiresAuth: true守卫里检查本地存储有没有token没有就强制跳登录页并带上回跳地址。页面组件拆分按照Vue推荐方式即可。商品列表页由一个列表容器组件和若干个商品卡片子组件组成子组件接收商品对象作为prop点击卡片跳转详情页。购物车页面把每一行购物车商品抽成行组件行组件处理数量加减和勾选状态提升到父组件统一管理。后台管理页面普遍用表格加弹窗结构表格展示数据新增和编辑弹窗里的表单抽成公共组件提交成功后父组件刷新列表。5.2 Axios封装与接口返回约定Vue项目里不会在每个组件里直接写axios.get而是把axios实例集中封装统一处理请求头、错误提示和返回数据。import axios from axios import { Message } from element-ui const service axios.create({ baseURL: /api, timeout: 10000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.token token } return config }) service.interceptors.response.use( res { const data res.data if (data.code ! 200) { Message.error(data.msg || 请求失败) return Promise.reject(new Error(data.msg)) } return data.data }, err { if (err.response err.response.status 401) { localStorage.removeItem(token) router.push(/login) } Message.error(网络异常请稍后重试) return Promise.reject(err) } )前后端接口返回的数据结构需要提前约定通常统一为一个Result对象包含code、msg、data三个字段。code为200表示成功401表示未登录500表示服务端异常。data可以是对象、数组或者分页结构。这个约定一旦定下来前端写起来非常舒服所有响应拦截逻辑都围绕code分支处理即可。请求方法按模块拆分到src/api目录下比如api/product.js导出queryProductList、queryProductDetail两个函数组件里import引入后调用。这样做可维护性比在每个组件里直接写URL高很多接口多了优势更明显。5.3 联调中的三个高频问题前后端分离开发时联调阶段是问题高发期。第一个高频问题是跨域。前端开发服务器默认跑在8080端口后端Tomcat往往也占8080两者不同端口就会产生跨域请求。最省事的方案是Vue CLI的devServer代理在vue.config.js里配置proxy把 /api 开头的请求转发到后端地址前端代码里的请求URL不需要任何跨域处理。上线部署时更简单前端build后的静态文件放到Tomcat的webapps后端war包也放进去同一域名和端口天然不存在跨域。第二个高频问题是时间格式。Java后端默认会把Date序列化成时间戳前端拿到之后如果需要显示成 yyyy-MM-dd HH:mm:ss要么自己写格式化函数要么在后端属性上加JsonFormat注解。为了省事我建议在后端统一加注解标注pattern和timezone直接输出字符串格式前端拿来显示即可。第三个高频问题是图片路径。商品图片通常以相对路径保存在服务器本地目录前端页面加载时要把image字段拼成完整可访问的URL。推荐后端在返回数据时直接拼接好基础地址比如把 image 字段从 images/product1.jpg 拼成 http://localhost:8080/images/product1.jpg前端无脑渲染。如果后端没拼接前端也可以封装一个全局过滤器统一处理相对路径。6. 常见问题排查与答辩准备6.1 数据库与MyBatis常见坑先看连接配置。MyBatis配置数据库连接时MySQL 5.7和8.0的驱动类不同8.0的驱动类改成了com.mysql.cj.jdbc.Driver连接串必须加serverTimezone参数。很多同学拿着旧教程配8.0项目启动直接报通信异常多半就是时区问题。字符集问题也很典型。建库时如果没指定utf8mb4中文数据可能存不进去或者显示乱码。解决方法连接串加characterEncodingutf8建表时显式指定ENGINEInnoDB DEFAULT CHARSETutf8mb4。注意utf8mb4和utf8的区别在于utf8mb4能存emoji字符虽然毕设里大概率用不到但选utf8mb4是更稳妥的选择。MyBatis还有一个经典坑实体类的驼峰属性名和数据库下划线字段名不一致。比如数据库字段是user_id实体属性是userId如果XML里没写resultMap查询结果就映射不上。解决办法是在MyBatis全局配置里开启mapUnderscoreToCamelCase把下划线字段自动映射到驼峰属性。开启之后写resultMap的机会就少了很多代码也简洁不少。6.2 拦截器、跨域、静态资源问题拦截器是最容易让新手崩溃的部件。典型现象是接口单独用浏览器访问正常但前端页面调用时返回401或者404。排查思路很清晰先看请求路径是否在拦截器放行名单里。比如前端访问登录接口登录接口的URL如果写成 /api/login而拦截器拦了 /api/**那这个请求永远进不了Controller。把登录、注册、商品查询接口加入放行名单即可。静态资源访问不到是另一个高频问题。SpringMVC默认不处理静态资源映射如果把图片放在项目的static目录下不配置映射是访问不到的。需要在SpringMVC配置文件中配置资源映射把 /images/、/static/对应到磁盘目录或webapp下的静态文件夹。跨域配置还有一种真实场景前端页面通过Nginx部署后端在独立Tomcat上确实产生了跨域请求。不想用Nginx代理的话后端可以增加CORS配置实现WebMvcConfigurer的addCorsMappings方法指定允许来源跨域。但毕设阶段尽量把前后端部署在同一个域名下彻底绕开跨域问题。6.3 部署打包流程与答辩高频提问部署是整个项目交付前最后一道关。后端在IDEA里执行clean package打war包放到Tomcat的webapps目录下启动即可。也可以打成jar包用命令行启动但Tomcat部署是课堂强调过的知识建议保留war部署路线。前端先npm run build生成dist目录把dist里的静态文件拷贝到Tomcat的webapps/ROOT下重启Tomcat。启动成功之后一定要完整走一遍核心链路注册新用户、登录、浏览商品、搜索、加购、结算、支付、管理员登录、发货、用户确认收货。每一步检查数据是否正常落库。这个完整流程测试的截图可以放进论文的测试章节答辩展示很有说服力。答辩时的高频问题我整理了一下基本绕不开这几个为什么用SSM而不用SpringBootMyBatis和Hibernate的区别订单表和订单明细表为什么分两张表库存扣减怎么防止超卖事务在哪个方法上生效Vue组件之间怎么通信路由守卫的作用是什么。这些问题在本文前面已经拆过按照这套思路用自己的话总结一遍基本能应对大多数追问。另外说句实在话整套项目跑通之后别急着删代码。把初始化SQL脚本单独导出一份把启动说明文档补全包括JDK版本、MySQL版本、数据库账号密码、端口占用情况这些细节。这些看着不起眼但不管是自己以后回顾还是交给别人运行都能省下大把时间。我自己每次调试这类项目最深的体会就是大部分时间不是花在写功能上而是花在版本不匹配、路径配错、状态映射错这类基础问题上。把这些基础问题提前排掉项目跑起来才会顺你答辩的时候也才有底气去聊那些真正有价值的设计。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑