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

资讯详情

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

基于SSM+Vue的宠物商城与领养管理系统设计与实战

基于SSM+Vue的宠物商城与领养管理系统设计与实战 SSM218的宠物商城及领养管理系统vue今年帮一个朋友做了套“宠物商城及领养管理系统”技术栈就是标题里的那套后端SSMSpring、SpringMVC、MyBatis前端Vue。做之前我以为就是个普通的管理系统做完了最大感受是——它其实是一个“双核心业务”的项目商城和领养一个偏电商交易、一个偏流程审核管理拆开做都不容易揉进一个系统里更有意思而且特别适合拿来练实战、填简历。先说给谁看如果你是刚学完SSM框架但一直只写过“单表CRUD”的初学者或者你在学校做毕业设计、项目实训想找一个能覆盖完整前后端分离开发的案例甚至你已经上班了想看看别人怎么设计“多角色权限复杂业务流程”的系统。这篇内容都值得你花十几分钟读完。我会把整个系统的设计思路、数据库表结构、后端核心实现、前端Vue的搭建联调、以及踩过的坑全部拆开讲尽量做到你照着就能复现一版。1. 项目定位与整体设计思路1.1 为什么是“商城领养”双核心很多人第一眼看到“宠物商城及领养管理系统”会觉得业务不复杂不就是卖宠物用品再加个领养申请吗但真正动手做你才发现这两个业务模块的性质完全不同。商城侧的本质是“交易系统”核心关注点是商品、购物车、订单、库存、支付状态这里面有明确的金额计算、库存扣减、订单状态流转跟一般的B2C电商没什么区别。而领养侧的本质是“审核流程系统”核心关注点是宠物信息发布、领养申请、资质审核、协议签署、后续回访它更像OA审批或者政务办事流程每一步都牵扯角色审批和数据状态迁移。这两个业务放一起系统就必须具备更强的通用设计能力。比如用户体系要统一同一个用户既能在商城下单也能提交领养申请权限体系要分层普通用户、宠物管理员、订单管理员、系统管理员各自看到的界面和可执行操作完全不同数据模型要兼顾商品要有分类、图片、价格、库存宠物要有品种、年龄、健康状态、领养条件二者不能互相污染但又要共用一套用户主数据。所以我在项目设计阶段就定了一个原则商城和领养两个领域相对独立各自维护自己的业务表和状态字段只在“用户”“系统管理”层面做统一。这样既保证了代码结构清晰也让每个功能模块都能独立扩展。1.2 技术选型为什么还选SSM而不是Spring Boot这个我猜很多人会问。现在Spring Boot都快成Java Web开发的默认选项了为什么还用SSM我得说句公道话Spring Boot是“约定优于配置”的封装它确实把SSM的很多繁琐配置简化了但这不代表SSM就没价值了。相反SSM是理解Java Web底层配置的最佳教材。你自己手写一遍Spring容器、SpringMVC拦截器、MyBatis的SqlSessionFactory你才能真正理解Spring Boot帮你省了哪些事、SpringBootApplication背后到底做了什么。写简历、面试的时候Spring Boot项目遍地都是但能讲清楚SSM整合过程反而能证明基础功扎实。另外很多学校的课程设计和毕业设计还会继续要求SSM框架企业里也有一批存量老项目跑在SSM架构上。这套系统的技术栈选SSM覆盖了经典三层架构的完整实现换个Spring Boot就是一次平滑迁移属于“进可攻退可守”的选型。前端选Vue就不用多解释了Vue在上手门槛、生态成熟度、中文资料丰富度上对这类中小型管理系统来说非常合适。它自带的响应式体系、组件化开发、vue-router路由、vuex/pinia状态管理完全能撑起“商城领养”这种规模的前端应用。1.3 系统角色与权限设计这个系统的角色权限我分了四类这也是双向业务系统最关键的设计点。角色核心职责可操作范围普通用户逛商城、下单、收藏宠物、提交领养申请前台所有浏览操作、个人订单管理、自己的领养申请商品管理员管理宠物商品信息商品CRUD、上下架、库存调整、类目维护领养管理员审核领养资格、管理宠物档案宠物档案管理、领养申请审核、回访记录维护系统管理员全局配置与数据总览用户管理、角色分配、数据统计、所有模块的最终权限在实际开发里我通过SpringMVC拦截器自定义注解来做后端权限控制前端Vue则用路由守卫来控制页面访问。后端控制是安全底线前端控制只是用户体验优化这个原则我建议所有做管理系统的人记住。2. 数据库设计与核心表结构解析这套系统的数据库我一共设计了12张核心表分四组用户权限组、商城交易组、领养业务组、系统支撑组。以下挑重点讲。2.1 用户模块表设计用户表是最基础的主数据我设计为user表主键id、用户名、密码MD5加密存储、昵称、手机号、邮箱、头像URL、注册时间、状态正常/禁用role表角色id、角色编码USER/GOODS_ADMIN/ADOPT_ADMIN/SUPER_ADMIN、角色名称user_role关联表userId、roleId一个用户多角色这里有一点要提醒密码一定不要存明文哪怕项目只是课程设计也要做加密处理。我用的MD5加固定盐虽然不够安全但至少符合基本规范。企业级项目会用BCrypt但它依赖Spring Security框架如果不想引入额外框架MD5加盐也够用。地址信息我单独建了user_address表因为后面商城下单要选收货地址领养审核也要核实居住信息。一个用户有多个地址默认地址用is_default字段标记。2.2 商城模块表设计商城模块四张核心表category表宠物商品分类id、父级id支持二级分类、分类名称、排序号product表商品id、商品名称、副标题、分类id、主图URL、轮播图URL用逗号分隔、原价、现价、库存、销量、详情文本、上下架状态、创建时间cart表购物车主键id、userId、productId、商品数量、加入时间。实际开发中购物车设计要考虑“用户未登录也能加购”的场景但这里为了逻辑清晰我只做了登录后加购order表订单id、订单编号、userId、总金额、收货人、收货电话、收货地址、订单状态待付款/待发货/待收货/已完成/已取消/退款中、创建时间、支付时间、发货时间order_item表订单项id、orderId、productId、商品快照名称、商品快照图片、商品快照价格、数量、小计金额商品快照是电商系统里最容易被初学者忽略的设计。为什么要有快照因为商品价格和名称后续可能会改如果在订单项里只存productId等用户下单后你改了商品价格订单历史数据就乱了。我的做法是在生成订单时把当时的商品名称、图片URL、单价原样复制一份存入order_item这样订单就像一张永不更改的“购物凭证”。2.3 领养模块表设计领养模块是这个系统区别于普通电商的核心也是业务复杂度最高的地方。四张表pet表宠物档案id、宠物名称、品种、年龄、性别、疫苗状态、绝育状态、健康描述、宠物图片URL、所在城市、领养状态待领养/审核中/已领养、发布人userId、创建时间adopt_application表领养申请id、petId、申请userId、申请人的居住情况、养宠经验、申请理由、月收入审核参考、状态待审核/已通过/已拒绝/已取消、提交时间、审批人、审批备注、审批时间adopt_record表领养记录id、applicationId、petId、userId、协议签订时间、协议附件URL、备注follow_up_record表回访记录id、adoptRecordId、回访人、回访时间、回访内容、宠物近况描述pet表的设计要特别注意“领养状态”和“申请状态”的联动。比如一只宠物被提交领养申请后它的状态应该从“待领养”变成“审核中”防止多个用户重复申请同一只宠物。等申请通过后它再变成“已领养”。这个状态流转在后面的业务逻辑里会细讲。2.4 表关系与设计要点讲一下表之间的核心关联以及我踩过的一个坑。订单表和订单项表是一对多关系pet表和adopt_application表是一对多关系。这些都好理解。重点说下“订单状态”这个字段。一开始我用的是int类型0待付款、1待发货、2待收货……后来发现代码里到处是魔法数字看得自己都晕。后面我改成用varchar存状态编码比如PAY_STATUS_0这样的枚举编码代码里定义常量可读性大幅提升。加上查错时对不上号直接在数据库里看业务状态很难受。所以我的建议是状态字段能用编码就用编码并且一定要在代码里抽成常量或枚举类。另外所有业务表我都加了create_time和update_time两个时间字段。MyBatis里用自动填充或者Java代码set创建时间别看这个动作小等你要统计数据报表、排查业务异常时时间字段是救命稻草。3. 后端核心功能实现详解3.1 SSM框架整合的关键点SSM整合对新手来说最劝退的是配置我把当时的核心配置思路捋一遍。Maven依赖上基础的三件套是Spring、SpringMVC、MyBatis然后需要数据库驱动我用MySQL 5.7 mysql-connector-java 5.1.49版本这个组合比较稳、Druid连接池、fastjson或jackson做JSON序列化、pagehelper分页插件以及文件上传用的commons-fileupload。Spring配置文件里要扫描service层和dao层SpringMVC配置文件要扫描controller层同时配置注解驱动、静态资源映射、视图解析器这些是基础。有个细节值得提Spring和SpringMVC的容器扫描范围要分开。如果Spring的context:component-scan把Controller也扫了会导致事务失效或者Bean重复创建的问题。我当时就是图省事在Spring配置里统一扫描了所有包结果做订单功能时发现声明式事务完全没生效排查了整整半天。MyBatis的配置要注意驼峰映射。表字段如果用的是下划线风格create_time实体类是驼峰createTime那么必须在mybatis-config.xml里开启mapUnderscoreToCamelCase参数。这一个小配置能让你省下无数个resultMap。Druid连接池的配置建议开启监控页面。虽然项目本身不需要多强的监控能力但Druid自带的监控面板能实时看到SQL执行情况、慢SQL统计对排查性能问题极其有用。3.2 登录认证与权限拦截器实现登录认证我用的是Session方案没有引入Shiro或Spring Security。原因很简单SSM项目引入安全框架的配置成本太高对这套业务来说属于“杀鸡用牛刀”而且面试官也更想看你手写拦截器的思路。具体实现分两步。第一步是登录接口接收用户名和密码后先根据用户名查用户再比对MD5加密后的密码通过后把userId和用户名存入Session同时把该用户拥有的角色标识存入Session。第二步是写一个LoginInterceptor拦截器继承HandlerInterceptorAdapter在preHandle方法里判断Session是否存在不存在就返回401状态码或者重定向到登录页。角色权限控制我用的自定义注解RequireRole注解上标注所需角色编码。拦截器里先校验登录再校验当前用户角色列表是否包含注解要求的值不满足就返回403状态码。这样控制器上只需要写一行注解权限控制清晰又优雅。前端Vue那边配合路由守卫检查本地存储里有没有token我用的是Session但前端统一存一个loginFlag字段做判断没有就跳转登录页。真正操作接口时后端拦截器才是安全底线。3.3 商城下单流程实现商城下单是整个后端业务里我最想分享的部分因为它的“事务”特性特别典型。用户从购物车结算的接口后端接收用户id、收货地址id、购物车勾选的商品id列表。整个事务方法里做了这样几件事根据购物车id列表查询商品信息和购物车数量计算出总金额校验每个商品的库存是否充足生成订单主表记录和订单项明细逐件商品扣减库存清空对应的购物车记录返回订单id给前端前端跳到支付模拟页面这里最关键的坑是“超卖问题”。如果两个用户同时下单都读到库存剩余1件都通过了校验然后都执行扣库存最终库存会变成-1这就超卖了。解决方案是“乐观锁数据库层面的原子更新”扣库存的SQL写成“update product set stock stock - #{count} where id #{id} and stock #{count}”如果更新后的受影响行数为0说明库存不足或者并发冲突直接抛异常回滚整个事务。这种方案没有引入分布式锁或Redis纯靠数据库行锁和原子更新就能解决大部分并发问题对小项目来说完全够了。订单状态流转我用了一个订单状态常量类定义所有合法状态迁移路径。比如待付款只能流转到待发货或已取消待发货只能流转到待收货。后端每次更新状态前校验当前状态和迁移目标是否合法防止前端乱传状态值篡改数据。3.4 领养申请的完整生命周期领养模块的业务逻辑我设计了四级状态流转待审核用户提交申请后的初始状态已通过领养管理员审核通过预约线下领养已拒绝管理员审核不通过用户根据拒绝原因修改后可重新申请已取消用户自己主动取消申请同时pet表本身也有状态待领养、审核中、已领养、已下架。提交领养申请接口的事务逻辑是先校验该宠物状态是否为“待领养”再检查当前用户是否有重复申请且未完结的记录防止一个用户反复刷申请然后创建adopt_application记录同时把pet表状态改为“审核中”。这两步在一个事务里任何一个失败都要回滚否则会出现宠物还显示待领养但已有人提交申请的脏数据。审核通过接口的逻辑是管理员传入审核结论通过/拒绝和备注将application状态改为对应状态如果通过同时把pet表状态改为“已领养”并创建一条adopt_record领养记录生成唯一的领养编号模拟一份领养协议附件如果拒绝pet表状态改回“待领养”用户可以重新申请。回访记录则是由领养审核员在约定时间后录入设计成一表简单的增删改查即可。整体来看领养流程比商城交易更强调“状态机角色审批”这也是这个系统值得用心做的地方。4. 前端Vue实现与前后端联调4.1 Vue项目结构与工程化配置我用Vue CLI创建项目版本的Node.js尽量用14或16长期支持版npm源如果下载慢可以切换为国内镜像源这在地理位置上能明显提速。整个项目的src目录我划分为views、components、router、store、api、utils六个目录。views放页面级组件components放公共组件比如商品卡片、宠物卡片、分页组件router放路由配置store放状态管理我用的是Vuex虽然是网上的“老选择”但Vuex的生态稳定、中文文档完善用来做这套系统完全够用api目录按模块划分文件每个文件导出对应的请求函数比如good.js、order.js、adopt.js、user.js。utils里放工具函数比如统一的时间格式化方法。路由设计上我采用嵌套路由主页是一个布局组件内部有顶部导航栏和侧边栏嵌套的子路由指向真正的页面组件。这样侧边栏菜单跳转时不会重新加载整个页面体验更好。Vue Router采用history模式还是hash模式我在开发时用的是history模式url更漂亮。但产品环境下需要配置Nginx的try_files否则用户直接访问某个子页面路径会404。这个问题我在后面的踩坑记录里还会说。4.2 核心页面实现思路首页重点展示宠物商品和待领养宠物两个板块通过分类Tab切换。我写了一个独立的轮播图组件用Vue的transition做淡入淡出效果。商品列表页支持按分类筛选、按价格排序、分页展示这些交互用element-ui的el-table和el-pagination很快就能实现。购物车页面要特别注意数量的增减限制不能超过库存。前端不能只靠el-input-number的max属性限制因为用户可以手动输入一个超大值绕过UI校验。我的做法是从接口获取该商品的库存字段后在提交订单前再次校验数量不超过库存双保险。领养专区页面的核心是一个卡片列表每张卡片展示宠物照片、品种、年龄、所在城市、领养状态待领养状态显示“申请领养”按钮。点击按钮跳转到宠物详情页详情页除了基本信息外还有领养条件说明和一个领养申请表单表单包含居住情况、养宠经验、申请理由等字段。管理后台页面则是典型的表格弹窗模式商品管理用el-table展示商品列表点击编辑弹出一个el-dialog表单领养审核页面展示所有申请记录点击审核弹出审核窗口填写审核结论和备注。这部分没什么技术难度但工作量很大建议组件化开发比如把分页组件、搜索栏抽成公共组件能少写很多重复代码。4.3 API封装与Axios拦截器前后端分离开发中API请求的规范性决定联调效率。我在api目录下建了一个request.js封装了Axios实例统一配置基础地址和拦截器。基础地址我用环境变量管理开发模式配成Vite的代理地址或者后端接口地址生产模式配成Nginx部署地址。如果不在环境变量里配置直接写死接口地址等部署环境一变改起来要哭。请求拦截器里主要做两件事从localStorage中取出登录凭证放入请求头以及开启一个统一的Loading动画防止用户重复点击提交按钮。响应拦截器里处理业务状态码约定后端返回格式为{ code: 200, message: success, data: {...} }code为200时走业务成功流程其他code统一弹错误提示。关于状态码这里有个坑要讲HTTP状态码和业务状态码不要混为一谈。我在开发时后端的Controller不管业务成功失败都返回HTTP 200通过响应体里的code来区分业务结果这样前端的Axios就不会因为HTTP 4xx/5xx进到catch分支统一在响应拦截器里处理业务逻辑更简单可控。但接口报500这种严重异常后端还是返回HTTP 500前端单独做一次“系统繁忙请稍后重试”的全局提示。4.4 前后端联调与打包部署前端开发时通过devServer的proxy配置把/api开头的请求代理到后端地址这样前端代码里不需要写完整的后端IP和端口只写相对路径。代理配置在vue.config.js里写大概长这样devServer: { port: 8081, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } }后端接口统一使用/api前缀比如/api/product/list、/api/order/create这样代理规则简单清晰也能和后端的SpringMVC映射做一眼对应的关系。打包部署时前端执行npm run build生成dist目录我把dist目录和后端项目放同一台服务器。后端用Maven打成war包部署到Tomcat也可以用spring-boot的jar包方式如果以后迁移Spring Boot前端dist目录用Nginx托管Nginx配置接口/api的反向代理到后端Tomcat地址。这里要注意生产环境中Nginx的proxy_pass后面是否带斜杠这决定了路径是否会拼接前缀我第一次部署时就因为多了一个斜杠接口404排查了好久。5. 常见问题排查与踩坑记录5.1 跨域问题的三种解法前后端分离开发中跨域问题是人人都躲不开的。虽然我在开发阶段用devServer的proxy解决了但如果前端直接请求后端地址比如前端跑在8081后端跑在8080端口不同就产生跨域。我当时给后端同时写了CORS全局配置在SpringMVC里实现WebMvcConfigurer接口的addCorsMappings方法允许所有来源、所有方法、所有请求头。这里要注意allowedOrigin不要用“*”配合allowCredentials同时使用部分浏览器会拦截这个组合导致前端自带cookie的请求失败。要么允许具体来源要么不携带请求凭证二者不能兼得。还有一个小坑是如果你的SpringMVC版本较低自定义拦截器拦截了预检OPTIONS请求会把CORS预检请求拦截下来导致真正的跨域请求失败。解决方法是拦截器里直接放行OPTIONS请求。5.2 MyBatis动态SQL与参数传递的坑MyBatis写动态SQL时最容易踩的是参数传递问题。比如多条件查询商品列表参数需要同时传商品名称模糊查询的字符串、分类id、上下架状态直接用Param注解给每个参数命名然后XML里用#{paramName}引用这个是最稳妥的。如果只传一个参数且是对象类型XML里可以直接用对象的属性名但这种情况我建议也加上Param代码语义更清晰。还有一个坑是MySQL的模糊查询。在MySQL里用like查询时如果直接在SQL里用concat(%, #{keyword}, %)拼接性能可能没问题但要注意字段是否有索引否则全表扫描在数据量大时很慢。我当时的商品表只有几百条数据无所谓但如果你要对接大一点的数据集建议考虑全文索引方案。分页插件pagehelper的使用也有坑。它是在执行SQL前自动拼接limit但如果查询语句后面跟着其他语句比如按销售量排序排序字段没有索引分页越深越慢。这种适合加索引的字段加上普通索引问题就解决了。5.3 路由模式与刷新404问题这个问题我在部署到Nginx时遇到。vue-router采用history模式打包上线后用户点击页面内的链接跳转一切正常但直接在浏览器输入网址访问某个子页面比如直接访问/order/list就会404。原因是Nginx默认找不到对应的静态文件路径。解决方案是在Nginx配置里加try_files指令location / { try_files $uri $uri/ /index.html; }意思是如果请求的路径匹配不到真实文件就把请求重定向到index.html由Vue Router接管路由解析。这个配置加上后刷新404的问题就解决了。5.4 常见问题速查表现象可能原因解决办法前端登录后刷新页面状态丢失Session存储超时或前端localStorage未存登录标记后端延长Session超时时间前端在localStorage存用户信息及登录标记商品列表接口返回404路径前缀不一致或Nginx代理少配置斜杠检查前端代理配置、后端RequestMapping、Nginx proxy_pass末尾斜杠Vue打包后布局异常静态资源路径用了绝对路径在vue.config.js中配置publicPath为相对路径“./”数据库中文乱码JDBC连接未指定编码在jdbc url添加characterEncodingutf8时间字段少8小时时区问题MySQL和Java默认时区不同JDBC连接添加serverTimezoneAsia/Shanghai订单提交后库存没变事务未生效可能Service方法被同类调用检查Spring配置扫描范围确保Service方法走代理调用前端跨域请求后端拿不到Session后端CORS配置没有允许携带凭证allowedCredentials设为true并指定具体允许来源商城订单重复提交前端未做防抖用户多次点击按钮后端接口做幂等校验比如生成唯一订单号防重如果你在开发过程中也踩过别的坑比如Vue路由参数传递时刷新丢失或者Element UI组件样式覆盖不生效欢迎在评论区和大家一起交流。这类实战项目真正让人成长的地方从来不是代码本身而是排查问题过程中建立起来的那种“能根据现象反推原因”的工程感觉。我个人在实际开发这套系统时最大的体会是做项目一定要先设计清楚状态流转和表结构再动手写代码。商城和领养这种双核心业务状态的边界定义得越清楚后面的代码写起来就越顺。你可以先把这个系统跑通然后在轮子基础上继续扩展比如接一个支付模拟、加一个后台数据报表、把Spring Boot的依赖替换掉每一步都是很好的技术练习。这套SSM加Vue的组合虽然不算新潮但它依然是很多学习者和一线开发者的主力技术栈把它彻底吃透比你跟风追十个新框架都值。
返回列表