十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

民族婚纱预定系统开发实战:SpringBoot+Vue前后端分离架构

民族婚纱预定系统开发实战:SpringBoot+Vue前后端分离架构 跟婚纱预定系统这种业务项目打交道多了你会发现一个很有意思的现象单纯写CRUD的教程遍地都是但把一个项目从零到一完整落地的过程尤其是那些藏在代码背后的设计决策和部署坑点反而没人愿意讲透。我自己做过几个前后端分离的业务系统之后深感这种“看起来简单、做起来全是细节”的项目才是最考验功力的。这次借着一个民族婚纱预定系统的完整开发过程我把整套技术方案、代码结构和部署思路都梳理一遍。这个项目用的是SpringBootVueMyBatisMySQL这套经典组合该踩的坑我都踩过了该填的坑我也都填好了你拿到源码之后能直接跑、能照着改造也能从里面学到不少前后端分离项目的实战套路。不管你是准备做毕业设计还是刚进公司要接手类似的管理系统这篇内容都值得你花点时间看完。1. 项目定位与技术选型思路1.1 这套系统到底解决什么问题民族婚纱预定系统本质上是一个面向特定人群少数民族婚礼场景、传统服饰租赁/定制商家的预约管理系统。它要管的不是“婚纱好看不好看”这种审美问题而是实实在在的业务流用户浏览服饰、查看档期、提交预定、商家确认订单、后台管理服饰库存和订单状态。所以核心需求拆下来就是三块。第一前台展示与预定用户能按民族风格、适用场景、价格区间筛选服饰查看详情和可预约时间第二订单全流程管理包括提交预定、支付占位或者线下支付占位、商家审核、订单状态流转第三后台管理服饰信息维护、分类管理、轮播图配置、订单处理、用户管理。这三块合在一起就是一个典型的“C端展示B端管理”双端系统。这种系统的难点不在于某个功能单点有多难写而在于前后端如何分工、状态如何流转、数据如何组织。1.2 为什么选SpringBootVueMyBatis这套组合很多人问我现在新项目为什么不上Spring Cloud微服务为什么不用MyBatis-Plus或者JPA我的回答很简单看业务复杂度。这个项目的数据量级、并发规模和团队协作方式完全不需要微服务那一套。SpringBoot单应用就能搞定所有后端接口部署运维成本低本地跑起来也快。Vue做前端SPA前后端通过JSON交互天然契合这种管理类系统的开发节奏。至于MyBatis它的优势是SQL可控复杂查询可以手动优化特别适合这种业务表结构相对固定、查询逻辑五花八门的系统。MySQL则是久经考验的关系型数据库存储订单、用户、服饰这类结构化数据非常合适。说实话这个组合现在看起来“传统”但它最大的价值是技术栈足够通用网上资料多招人也好招。你在牛客上搜SpringBoot面试题搜出来的高频考点就是这套东西。把这个项目吃透了面试聊项目经验的时候非常有料。2. 核心功能模块与设计思路拆解2.1 用户端的三个关键页面用户端最核心的页面是服饰列表页、服饰详情页和预定下单页。列表页要解决的核心问题是“筛选和分页”。民族服饰有很多维度——民族藏族、苗族、蒙古族等、场景婚礼、写真、仪式、价格区间。如果只用MySQL的LIKE查询一旦数据量上来接口响应会很慢。所以我在设计时采用了动态SQL拼接根据前端传来的筛选条件组合查询语句同时用PageHelper做物理分页。这里有个细节值得说服饰列表的封面图不要用数据库的BLOB字段直接存图片二进制而是存图片URL路径。图片文件走独立的上传接口落盘到服务器本地数据库只记路径。这样做的好处是前端img标签直接绑定路径就能显示不需要额外写二进制流转换接口而且后续迁移到OSS也方便。详情页要处理的则是“图片展示档期查询”。一个民族婚纱套系往往有五六张展示图数据表设计成主表服饰基本信息子表图片列表的方式查询时用一条SQL把主表和子表数据联合查出来组装成带图片列表的VO对象返回给前端。下单页的核心是“防重复预定”。用户选好服饰、填了婚期、提交预定后端必须校验这条档期是否已被别人占用。我的做法是在数据库层面加唯一约束用occasion_date档期日期dress_id服饰ID作为联合唯一索引。这样就算两个用户同时提交数据库也会拒绝其中一条比单靠代码判断靠谱得多。2.2 管理后台的功能划分管理后台我按角色权限拆成两块管理员和运营人员。管理员负责用户管理、角色分配、系统配置。运营人员负责服饰管理、订单审核、轮播图维护。权限控制这块SpringBoot集成Spring Security或者Sa-Token都可以。我这个项目用的是基于Token的认证方案——前端登录后拿到Token之后每个请求都在Header里带上Token后端通过拦截器统一校验身份和权限。后台界面我用的是VueElement UI。Element UI的表格组件、表单组件、对话框组件都是现成的写后台管理页面效率非常高。比如服饰管理页面一个el-table绑定列表数据el-dialog里放表单提交后刷新列表这一套组合拳能覆盖80%的后台页面场景。2.3 为什么强调“前端分离”这个关键词“前后端分离”这个关键词现在被说烂了但很多人其实没搞明白它到底分离了什么。分离的不仅仅是代码仓库和开发人员更重要的是交互协议。前端不再通过服务端模板比如JSP、Thymeleaf拿数据而是通过HTTP接口拿JSON。这意味着前端可以独立部署Nginx或Node服务器后端可以独立部署Tomcat或Docker容器两边只要约定好接口文档就可以并行开发。这个模式有个必须处理的痛点就是跨域。前端跑在8080端口后端跑在9090端口浏览器默认会拦截跨域的Ajax请求。解决办法有两个后端配置CrossOrigin注解或CORS全局配置或者通过Nginx反向代理把/api路径转发到后端服务。生产环境我推荐用Nginx方案因为前后端同源之后连Cookie、Token传递都不用额外处理。3. 数据库设计与后端核心实现3.1 四张核心表的结构设计整个系统的数据库我用了一个很克制的设计核心业务表就四张用户表、服饰表、订单表、图片表。用户表sys_user字段包括id、username、password、nickname、avatar、role、create_time。密码用BCrypt加密存储绝对不允许明文入库。角色字段用字符串标识比如ADMIN、OPERATOR、CUSTOMER简单直白。服饰表dress字段包括id、name、ethnic_type、scene_type、price、description、status、cover_image。ethnic_type存民族分类比如“藏族”“苗族”scene_type存适用场景比如“婚礼”“写真”。这两个字段在列表页的筛选中起着关键作用。订单表order_info字段包括id、order_no、user_id、dress_id、occasion_date、contact_name、contact_phone、status、remark、create_time。order_no用时间戳随机数生成保证唯一方便后续对账。status用整数表示0待审核、1已确认、2已取消、3已完成。图片表dress_image字段包括id、dress_id、image_url、sort_order。这个表主要解决一件服饰多张展示图的问题sort_order用来控制前端展示顺序。这四张表的关联关系很清晰用户1对多订单服饰1对多订单服饰1对多图片。不需要复杂的中间表也不需要过度设计够用且好理解。3.2 后端接口设计规范后端接口我统一采用/api前缀。比如/api/user/login、/api/dress/list、/api/order/create、/api/order/list、/api/admin/dress/add。统一前缀的好处是前端封装的Axios实例可以统一配置baseURLNginx反向代理也只需要转发一个路径。返回格式也做了统一封装。写了一个Result类结构是{code: 200, message: success, data: {...}}。成功时code为200业务异常时code为500或自定义错误码前端Axios拦截器统一判断不是200就弹错误提示。Token认证这块我用的是JWT。用户登录成功后后端生成一个有效期为24小时的JWT返回给前端。前端把它存在localStorage里每次请求在Axios请求拦截器中添加service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] token } return config })后端收到请求后拦截器先解析Token如果Token无效或过期直接返回401状态码。前端Axios响应拦截器看到401自动跳转到登录页。这套流程就是热词里说的“vue前后端分离请求token处理”实际跑起来非常丝滑。3.3 MyBatis动态SQL的几个关键写法MyBatis是这个项目的数据访问层核心动态SQL用得好不好直接决定代码优雅不优雅。服饰列表查询是我重点展示的部分。因为前端传来的是不确定的筛选条件——可能只传了民族可能只传了场景可能什么都没传。如果为每种情况写一个SQL方法代码会膨胀到没法维护。动态SQL的where标签完美解决了这个问题select idselectDressList resultTypecom.example.entity.Dress SELECT * FROM dress where if testethnicType ! null and ethnicType ! AND ethnic_type #{ethnicType} /if if testsceneType ! null and sceneType ! AND scene_type #{sceneType} /if if testkeyword ! null and keyword ! AND name LIKE CONCAT(%, #{keyword}, %) /if AND status 1 /where ORDER BY create_time DESC /selectwhere标签会自动处理掉第一个AND不会出现WHERE AND这种SQL语法错误。这也是MyBatis的一个高频考点面试时经常被问到。订单创建接口就必须加事务控制。创建订单时要做两件事插入订单记录、更新服饰状态。如果只插入订单而更新状态失败数据就不一致了。所以我在Service层加了Transactional注解任何一步异常都能回滚。这里插一句Transactional有几个容易踩的坑默认只回滚RuntimeException如果是自定义异常需要指定rollbackFor同类内部调用会让代理失效事务不生效。这些都值得单独写一篇细说但在这个项目里我直接用最简单的方式——把事务方法放在Controller调用的Service实现类里。我在这个项目的数据库脚本中还专门为订单表加了一个状态索引CREATE INDEX idx_order_status ON order_info(status);订单状态流转查询是高频操作加了这个索引之后后台订单列表页的响应速度会明显提升。4. 前端实现与项目部署全流程4.1 Vue工程结构与路由配置前端工程我基于Vue CLIWebpack搭建目前Vue 2.6依然是我在这个项目中偏稳妥的选择——组件生态丰富Element UI适配度高相关的坑早就被填平了。工程结构如下src/ api/ // 封装所有接口请求 assets/ // 静态资源 components/ // 公共组件 router/ // 路由配置 store/ // Vuex状态管理 views/ // 页面组件 admin/ // 后台管理页面 user/ // 用户端页面 App.vue // 根组件 main.js // 入口文件路由配置里需要区分用户端和管理后台。用户端页面放在/路径下比如/、/dress/:id、/order/create。管理后台统一挂在/admin路径下并且配置路由守卫未登录或非管理员角色访问时跳转到登录页。router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.path.startsWith(/admin) !token) { next(/login) } else { next() } })这里有个细节想提醒用户端和管理后台最好用不同的Layout组件。用户端是导航栏内容的布局管理后台是侧边栏顶部栏内容区的布局。不要混用一套后期调整起来非常痛苦。4.2 Axios封装与接口调用Axios封装是前端工程的基础设施。我的做法是建一个request.js文件导出配置好的Axios实例所有API请求都基于这个实例发出。import axios from axios const service axios.create({ baseURL: process.env.VUE_APP_BASE_URL || /api, timeout: 10000 }) service.interceptors.response.use( response { const res response.data if (res.code ! 200) { this.$message.error(res.message) return Promise.reject(new Error(res.message)) } return res.data }, error { if (error.response error.response.status 401) { localStorage.removeItem(token) window.location.href /login } return Promise.reject(error) } )这样封装之后业务代码里调用接口就非常清爽。比如服饰详情页getDressDetail(id).then(data { this.dress data this.imageList data.imageList })不用在每个页面重复处理错误提示和登录失效问题一套拦截器全部搞定。关于Vue项目中播放m3u8视频的需求我在这个项目的服饰展示模块里也有涉及。因为部分高端婚纱套系会提供试穿视频或动态展示视频视频源格式是m3u8。处理方案是前端引入hls.js库在Vue组件里动态创建video标签并加载m3u8流。这个方案在PC端兼容性很好移动端的话可以优先用原生video标签配合x5-playsinline属性处理。4.3 完整部署流程部署是我要详细展开的部分。很多人项目写完了部署却卡住我能理解那种感觉。所以我整理了一套相对完整的流程从环境准备到正式上线。第一步环境准备。需要准备一台Linux服务器我这边测试用的是CentOS 7.9。安装JDK 1.8、MySQL 5.7、Nginx这些直接用包管理器安装即可。Node.js环境用于前端打包版本要注意Vue CLI项目用Node 14或16都比较稳。第二步初始化数据库。把项目自带的dress_booking.sql脚本上传到服务器执行mysql -u root -p dress_booking.sql脚本里建好了库和表还有一个默认管理员账号admin/admin123。用Navicat或命令行检查一下表是否创建成功。第三步修改后端配置并打包。找到application.yml修改数据库连接信息spring: datasource: url: jdbc:mysql://localhost:3306/dress_booking?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 你的数据库密码然后执行Maven打包命令mvn clean package -DskipTests打包完成后target目录下会生成一个dressbooking-0.0.1-SNAPSHOT.jar。第四步启动后端服务。直接使用java -jar命令启动搭配nohupnohup java -jar dressbooking-0.0.1-SNAPSHOT.jar app.log 21 启动之后看日志确认没有报错。测试接口是否可以访问curl http://localhost:9090/api/dress/list能返回JSON数据就说明后端没问题。第五步构建前端并配置Nginx。在前端工程目录执行npm install npm run build构建完成后dist目录就是前端静态文件。把它上传到服务器的/usr/share/nginx/html/dress目录然后修改Nginx配置server { listen 80; server_name your_domain.com; location / { root /usr/share/nginx/html/dress; index index.html; try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:9090/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }注意try_files $uri $uri/ /index.html这行它的作用是处理Vue Router的history模式。如果不加这行刷新某个子页面路由时Nginx会返回404。/api反向代理配置则是把前端的API请求转发到后端Java服务解决跨域问题。第六步重启Nginx并验证。nginx -t nginx -s reload浏览器访问服务器IP能看到系统首页登录、查询、下单流程都跑通部署就算完成了。5. 常见问题与排查技巧实录5.1 数据库自动断开导致的“服务不可用”这个问题我在这个项目里遇到过。MySQL的wait_timeout默认是8小时如果长时间没有请求MySQL会自动断开空闲连接。数据库连接池里的连接还在被使用但MySQL已经断开了就会出现连接异常。排查思路其实很直接启动项目后一切正常但过了一晚上再访问接口报错。看后端日志关键报错是Communications link failure或者Connection is not available, request timed out。解决办法有两个方向。一是给连接池配置合适的参数比如HikariCPSpringBoot默认连接池可以设置spring: datasource: hikari: max-lifetime: 1800000 idle-timeout: 600000二是调整MySQL的wait_timeout把它改大为28800秒8小时以上。我两个都改了目前运行稳定。5.2 “端口被占用”引发的部署失败第一次部署时后端启动失败报Port 9090 was already in use。排查方法是lsof -i :9090 kill -9 PID然后重新启动SpringBoot服务。这个操作很基础但新手经常被卡在这里值得单独提一下。如果是多次部署建议用脚本统一管理启动、停止、重启操作避免每次都手动找PID。5.3 Vue打包后布局异常“vue 打包后布局异常”也是热词里出现频率很高的一个问题。开发环境跑得好好的npm run build之后CSS错乱、图片不显示。这个大概率是静态资源路径问题。需要在vue.config.js里设置publicPath为相对路径module.exports { publicPath: ./ }或者干脆用绝对路径/dress/配合Nginx的root配置来使用。这个坑很容易踩尤其是部署到子目录时更明显。5.4 MyBatis字符串比较的“隐形坑”热词里有个“mybatis 单个数字字符比较”这个坑我在项目中实际遇到过。在XML里写条件判断时比如判断订单状态是否为某个值if teststatus 1 AND status 1 /if此时status是前端传过来的整数1没问题。但如果是字符串类型的参数比如1status 1这种写法在某些MyBatis版本中会触发数字开头的字符串比较问题。解决方法是统一用status 1.toString()或者干脆在入参时就转为Integer类型避免使用单引号包裹数字比较。5.5 SpringBoot版本选择引发的连锁问题热词里有“springboot版本太高”这个表述说明很多人在版本选择上吃过亏。SpringBoot 3.x对JDK版本有要求必须JDK 17以上而且很多第三方组件的兼容性还没完全跟上。如果你本地是JDK 8就不要强行用SpringBoot 3.x老老实实用SpringBoot 2.7.x。这个项目我选的就是2.7.6版本稳定且兼容面广。这里给大家一张简单的版本对照表JDK版本推荐SpringBoot版本说明JDK 82.5.x ~ 2.7.x主流稳定版资料多JDK 112.7.x兼容性好JDK 173.0.x新特性多但需确认生态兼容5.6 前端请求BaseURL如何按环境切换开发时前端走http://localhost:9090部署时走Nginx的/api实现方式是.env.development和.env.production两个环境变量文件// .env.development VUE_APP_BASE_URL http://localhost:9090 // .env.production VUE_APP_BASE_URL /api打包时Vue CLI会自动读取对应的环境变量。这样代码里只需要写axios.create({ baseURL: process.env.VUE_APP_BASE_URL })不同环境下自动切换。6. 项目后续扩展方向与个人经验总结6.1 这个项目还能怎么玩如果想把项目做得更完整或者说想在面试时有更多亮点可说我建议在下面几个方向选一个做延伸。支付模块是当前项目的一个明显短板。目前订单只是预约占位没有真正接入支付。可以接微信支付H5Native支付在用户提交订单后生成支付二维码支付完成后通过回调更新订单状态。这个功能一加上整个系统的商业闭环就完整了。集成微信支付官方SDK文档写得很详细照着流程走一遍基本能通。消息通知也可以做一下。用户提交预定后给管理员发送通知管理员审核后给用户发送通知。如果不想引第三方推送服务最简单的做法是在系统内部做个站内信表用户在消息中心查看通知不用搞复杂了但能体现你对业务流程的思考。还有权限细化。现在只有Admin和User两种角色可以扩展成RBAC模型引入角色表、菜单表、权限表用Spring Security的注解控制接口权限。6.2 写代码之外我说句实在话这个项目让我最满意的不是代码本身而是整套方案是可复制、可迁移的。技术选型不花哨但每一块都能讲清楚为什么这么选业务流程不复杂但每个环节的前后端交互都考虑到了部署方式很简单但生产环境下遇到的问题也都覆盖到了。我在实际开发中体会很深的一点是很多人学SpringBoot天天背面试题什么自动配置原理、Bean生命周期倒背如流但真让他从零搭一个能跑的前后端分离项目他会卡在跨域上卡在Token校验上卡在Nginx配置上。这个项目就是用来填平“理论”和“实践”之间的鸿沟的。最后再分享一个小技巧拿到任何一套前后端分离项目源码不要急着跑起来先看数据库脚本把表结构理清楚再看后端接口文档或Controller层搞清楚有哪些接口、每个接口做什么然后看前端路由和API封装理解页面和接口的对应关系最后再启动项目把核心流程走一遍。按这个顺序走下来一套陌生的系统你一天之内就能上手。做项目慢就是快理顺了再动手比盲目改代码高效得多。
返回列表