恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

SpringBoot+Vue私房菜定制上门服务管理系统设计与实现

  • 首页
  • 资讯中心
  • /
  • SpringBoot+Vue私房菜定制上门服务管理系统设计与实现

相关资讯

企业智能体与AI安全,谷歌Gemini Agent落地启示 2026/10/14 7:28:21
验证码安全设计实战:从攻防对抗到动态熵值防护 2026/10/14 7:28:21
Jev浏览器Agent:视觉语义双驱动的自动化新范式 2026/10/14 7:23:21

最新资讯

PyTorch ImageFolder实战:从目录结构到数据加载的避坑指南
平板型永磁同步直线电机Maxwell仿真:反电动势、推力与磁阻力分离
LiteLLM大模型网关实战:统一API接入、负载均衡与预算管理
Win7 x64轻量OCR服务:JSON接口调用套件
CAD/CAE工业智能体:从侧边栏走向设计流程主笔
GTK 3.24 Windows环境配置:从解压到编译的路径避坑指南

今日推荐

IBM Verify Identity Access 修复 22 个漏洞:两个 9.8 严重 RCE 无需认证
小区物业管理系统
小型超市仓库管理系统

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

SpringBoot+Vue私房菜定制上门服务管理系统设计与实现

发布时间:2026/10/14 7:28:21
SpringBoot+Vue私房菜定制上门服务管理系统设计与实现 做私房菜定制上门服务这个项目一开始并不是纯粹为了“写一套管理系统”而写。我身边有朋友在做私厨上门接单全靠微信聊天今天谁订了什么菜、几点上门、收了多少定金、做完有没有评价全都散落在聊天记录里。漏单、错单是常事更别提结算和复盘了。所以我花了大概三周时间从需求梳理到数据库设计从后端接口到前端页面完整做了一套基于SpringBootVue的前后端分离管理系统技术栈就是标题里那套Java SpringBoot MySQL MyBatis Vue。这套系统解决的问题很明确用户在线浏览私房菜菜品、提交定制需求、选择上门服务时间并下单厨师接收订单、安排上门、更新服务状态管理员审核厨师、管理菜品和订单、查看平台经营数据。对于正在做毕业设计、课程设计或者想完整走一遍全栈开发流程的同学来说这套东西的参考价值很高。这篇博客我会把整个项目的设计思路、数据库建模、后端核心代码、前端关键逻辑、部署踩坑全部摊开来讲尽量让你看完能自己复现一套而不是只停留在“我大概懂了”的层面。1. 项目整体设计先理清楚这套系统的业务边界拿到一个系统需求尤其是这种带“定制”“上门服务”字眼的项目第一件事不是建工程而是把业务边界画出来。什么是边界就是谁在用、用哪些功能、哪些场景必须支持、哪些可以做简化。1.1 三个角色和它们各自的诉求这套系统里一共有三类角色普通用户、厨师、平台管理员。普通用户关心的是“我能不能方便地吃到私房菜”。他需要浏览菜品、查看厨师信息、填写上门地址、选择服务时间、提交定制备注、在线支付、查看订单状态、服务完成后评价厨师的菜品和态度。所以用户端的功能模块大致是菜品浏览、厨师列表、购物车或者直接下单、地址管理、订单管理、评价管理。厨师是服务提供方。他关心的是“我今天有没有单、几点去、做什么菜、客人有什么忌口”。所以厨师端要能看到被指派的订单能修改订单状态接单、开始服务、完成服务能维护自己的基本信息、擅长的菜系和服务价格。这个我做了简化没有给厨师做独立的App或小程序直接复用管理后台的账号体系厨师登录后进入厨师工作台。管理员是平台运营方。他做三件事审核入驻厨师、管理菜品分类与上下架、处理用户下单后的异常流程比如退款申请、改期申请。另外数据统计是管理系统里很加分的模块管理员首页可以看到近7天订单量、营业额、热门菜品排行、用户增长趋势。这些统计图我用了ECharts来做后端提供聚合查询接口前端直接渲染折线图和柱状图。1.2 核心业务流程与订单状态机这个系统最核心的流程是“下单上门服务”这一条主链路。整条链路我设计成浏览菜品 → 加入定制清单 → 填写服务地址和时间 → 提交订单 → 在线支付 → 平台分配/厨师接单 → 上门服务 → 用户确认完成 → 评价。流程一旦涉及多个状态就一定要有状态机。我定义了订单状态如下状态值含义触发操作0待支付用户提交订单后1待接单支付成功后等待厨师接单2已接单厨师点击接单等待上门3服务中厨师到达并开始服务4待确认服务完成等待用户确认5已完成用户确认完成订单闭环-1已取消支付前取消或超时未支付这里有个细节用户支付成功后不能自己取消订单只能申请取消由管理员审核。原因是上门服务涉及厨师的行程安排厨师可能已经备菜甚至出门了不能像电商退货那样随意取消。这个规则的业务意义在于保护服务提供方的利益也避免用户滥用下单功能。状态流转的合法性校验放在后端Service层后面我会给出代码。1.3 功能模块的取舍哪些必须做哪些可以砍管理系统最容易犯的毛病就是“功能堆砌”。明明是一个私房菜上门服务系统非要把拼团、秒杀、会员等级全塞进去开发周期拖长不说还把核心链路搞得很乱。我在设计时做了明显的取舍。必须做的是用户登录注册、菜品分类与列表、菜品详情、定制下单、订单状态流转、厨师审核与管理、评价。可以暂时砍掉的是优惠券系统我保留了表结构但前端没有强推、厨师自主报价统一用平台价服务费的方式结算、实时聊天接单沟通直接用平台留言或用线下电话。如果你做毕设答辩砍掉的部分可以写成“系统可扩展方向”反而比硬做一堆半成品更能说明你懂业务设计。2. 技术栈选型这套方案为什么这么搭配选型没有绝对的对错但一定有“为什么”。这套系统的技术栈是Java SpringBoot MySQL MyBatis Vue基本是目前单体全栈项目最主流的组合之一。2.1 SpringBoot MyBatis经典组合的真实优势先说后端。SpringBoot 2.x 的约定大于配置让开发效率提升非常明显内嵌Tomcat打包成jar直接跑不需要额外部署外部容器。对于单体管理系统来说它的体量、社区生态、资料丰富程度都是最优解。JDK我用的1.8稳定而且和现有大多数教程兼容。MyBatis 是这项目里绕不开的点。国内很多课程和毕设都要求用MyBatis它最大的特点是SQL由自己控制动态SQL标签非常灵活。比如菜品列表要做多条件筛选——按分类、按价格区间、按上架状态用MyBatis的where加if标签组合比JPA那种全自动拼SQL的方式更直观、更好排查问题。而且MyBatis对SQL优化的掌控力更强比如订单查询要连表查用户昵称和厨师姓名自己写联表SQL一目了然。我没有用MyBatis-Plus原因很简单一是很多毕设课题明确要求原生MyBatis二是MyBatis-Plus帮我们自动生成通用CRUD之后很多人反而连SQL都不会写了这违背了学习目的。当然如果你是自己做小项目图快MP确实省事这个看场景选。2.2 前端用Vue2还是Vue3实际项目的选择依据前端我用的Vue2 Element UI Axios ECharts Vue Router Vuex。这里可能有人会问现在都2024年了为什么不用Vue3理由很实在Vue2 Element UI 的组合在管理后台类项目里极其成熟坑少资料多随便搜一个“Vue2 后台管理系统模板”能出来一大堆可以参考的代码结构。而Vue3 Element Plus虽然新但在Node版本、插件兼容、组件API上对新手没那么友好毕设周期就这么点时间稳定压倒一切。当然如果你对Composition API已经很熟Vue3完全可以用不影响这套系统的后端接口设计。前后端分离的核心是接口约定和前端框架版本无关。我的接口设计统一为RESTful风格返回格式是{ code, msg, data }前端拿到这个结构后统一在axios拦截器里处理业务代码里不用到处写try-catch。2.3 工程目录结构前后端分离怎么组织才不乱很多同学前后端项目混在一个文件夹后来改代码都不知道去哪改。我的习惯是建立两个顶层目录server放后端web放前端数据库初始化SQL脚本单独放在sql目录下。后端包结构是这样的com.example.privatechef ├── config // 配置类跨域、拦截器注册、MyBatis配置 ├── controller // 接口层接收参数返回结果 ├── service // 业务层事务边界、状态流转校验 │ └── impl ├── mapper // MyBatis数据访问层接口 ├── entity // 数据库表对应的实体类 ├── dto // 前端传参对象 ├── vo // 返给前端的视图对象 ├── common // 通用结果类、常量、异常处理 │ ├── result │ ├── exception │ └── utils // JWT工具类等 └── interceptor // 登录拦截器、角色拦截器前端结构更常规views按角色拆成user、chef、admin三个目录components放公共组件api放接口请求定义router放路由和路由守卫store放Vuex状态管理存用户信息和Token。3. 数据库设计把定制和上门服务建模到底数据库是这套系统里最值得细讲的部分。私房菜定制上门服务和普通的电商点餐有本质区别普通点餐卖的是“已做好的食品”而私房菜上门卖的是“服务按需定制的菜品”。所以订单表不能只存菜品ID列表还得存服务时间、上门地址、联系人、定制需求。3.1 核心表结构总览我总共设计了10张核心表这里先给个总览表名用途关键字段t_user用户表username, password, nickname, phone, avatart_chef厨师表user_id, real_name, work_years, specialty, service_price, statust_category菜品分类name, sortt_dish菜品表category_id, name, image, price, description, status, salest_order订单主表order_no, user_id, chef_id, serve_time, address, total_amount, statust_order_item订单明细表order_id, dish_id, dish_name, quantity, price, taste_typet_address用户地址簿user_id, name, phone, province, city, district, detail, is_defaultt_evaluation评价表order_id, user_id, chef_id, rating, content, imagest_coupon优惠券表name, amount, threshold, total_count, received_count, statust_admin管理员表username, password厨师表单独建而不是直接挂在user表上是因为厨师有一套自己的业务属性服务年限、擅长菜系、服务单价、审核状态。厨师的登录账号和用户共用user表通过在t_chef里存user_id关联同时在chef表中加status字段表示待审核、审核通过、禁用。这样的好处是以后不管是用户还是厨师登录逻辑都走同一套用户体系权限区分放在角色字段上。3.2 订单表与金额字段的那些细节订单主表是整个系统里字段最多的表一笔订单要回答这几个问题谁买的user_id、谁服务chef_id、买什么order_item、什么时候服务serve_time、在哪里服务address、多少钱total_amount、现在什么状态status、支付了没有pay_status。关于金额字段有一个必须要说的点——绝对不要用double或者float存金额。JS里浮点数精度问题大家应该都听说过Java里的double做加减乘除同样有精度丢失问题。金额统一用decimal(10,2)在DTO和VO里我用BigDecimal来接。如果你用double存订单金额99.99经常变成99.98999999排查起来会非常痛苦。订单号我用的格式是“日期随机数”比如20240520153000123456具体生成规则yyyyMMddHHmmss 6位随机数。这样做的好处一是便于作为业务编号给用户看二是避免了数据库自增ID直接暴露订单量的信息。服务时间字段用datetime存储并且在serve_time上建了普通索引。这个字段的查询频率很高——厨师端要查“某一天有哪些待服务的单”管理员要看“某一时段下单量”不建索引在大数据量下会很吃亏。3.3 菜品定制需求的存储方案私房菜定制的核心是“定制”二字。用户在选完基础菜品之后还可以填写“口味偏好”和“定制备注”比如“少辣”“多放蒜”“海鲜过敏不要放虾”“孩子周岁宴希望做个生日主题摆盘”。我设计了两种定制维度一是标准菜品带着固定价格加入订单明细每道菜可以选口味taste_type默认、微辣、中辣、特辣、不辣二是整个订单级用户填写一段custom_requirements文本厨师接单后可以看到。这种方案简单实用能覆盖绝大多数私房菜定制场景。如果你要做得更精细可以再抽象一层把定制选项做成“规格组”比如荤菜类可选食材、做法、口味三个维度每个维度有选项价格前端动态渲染。但那样会大大增加表结构和前端交互的复杂度。对于这套毕设系统我建议先用“明细菜品的口味字段订单级需求文本”的方案把核心流程跑通再谈扩展。4. 后端核心实现从登录鉴权到订单流转后端代码是整个系统的重头戏。我按核心链路挑几个重点来讲不是为了罗列代码而是告诉你每一段代码是在解决什么问题。4.1 JWT登录鉴权与角色权限拦截登录模块我用的JWTJSON Web Token而不是传统的Session。原因很简单前后端分离项目后端接口是无状态的用Session需要处理跨域携带Cookie、Session共享等问题而JWT把用户信息加密放在Token里前端每次请求在Header里带上Token后端校验签名即可。Token生成我封装了一个工具类核心逻辑如下public class JwtUtil { private static final String SECRET private-chef-system-secret-key; private static final long EXPIRE_TIME 7 * 24 * 60 * 60 * 1000L; // 7天 public static String createToken(Integer userId, String role) { return Jwts.builder() .claim(userId, userId) .claim(role, role) .setExpiration(new Date(System.currentTimeMillis() EXPIRE_TIME)) .signWith(SignatureAlgorithm.HS256, SECRET) .compact(); } public static Claims parseToken(String token) { return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody(); } }密码加密用的BCrypt哈希时自动加盐数据库中不存明文密码。注册接口里加密逻辑就一行BCrypt.hashpw(password, BCrypt.gensalt())登录校验用BCrypt.checkpw(rawPassword, encodedPassword)。你如果在演示系统时把密码字段亮给老师看加盐密码也完全不暴露明文信息。权限控制我用了拦截器。先写一个JwtInterceptor继承HandlerInterceptor在preHandle里取Header中的Token解析失败直接返回401解析成功把userId放进request对象。然后写一个RoleInterceptor在注解或路径匹配的维度限制角色访问。public class JwtInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (token null || !token.startsWith(Bearer )) { throw new BizException(401, 未登录或登录已过期); } Claims claims JwtUtil.parseToken(token.substring(7)); request.setAttribute(userId, claims.get(userId)); request.setAttribute(role, claims.get(role)); return true; } }注册拦截器时要注意放行路径登录接口、注册接口、菜品列表、菜品详情、图片资源这些都不需要登录。其余的/api/**接口全部走拦截器校验。4.2 订单状态机的代码实现订单状态流转最容易写出的问题是“前端随便调一个接口就能把状态改来改去”。状态机要解决的核心问题就是你只能从当前状态流转到合法的下一个状态除此之外全部拒绝。我的实现是这样的在OrderService里维护一张流转表private static final MapInteger, ListInteger STATUS_TRANSITIONS new HashMap(); static { STATUS_TRANSITIONS.put(0, Arrays.asList(1, -1)); // 待支付 - 待接单 / 取消 STATUS_TRANSITIONS.put(1, Arrays.asList(2, -1)); // 待接单 - 已接单 / 取消 STATUS_TRANSITIONS.put(2, Arrays.asList(3, -1)); // 已接单 - 服务中 / 取消 STATUS_TRANSITIONS.put(3, Arrays.asList(4)); // 服务中 - 待确认 STATUS_TRANSITIONS.put(4, Arrays.asList(5)); // 待确认 - 已完成 }然后提供统一的状态更新方法public void updateOrderStatus(Integer orderId, Integer targetStatus, Integer operatorId, String role) { Order order orderMapper.selectById(orderId); if (order null) { throw new BizException(订单不存在); } ListInteger allowed STATUS_TRANSITIONS.get(order.getStatus()); if (allowed null || !allowed.contains(targetStatus)) { throw new BizException(非法状态流转 order.getStatus() - targetStatus); } // 角色校验厨师只能接单/开始/完成服务用户只能确认完成管理员可以取消订单 if (chef.equals(role) !Arrays.asList(2, 3, 4).contains(targetStatus)) { throw new BizException(厨师无权执行该操作); } // 业务校验确认服务完成的订单厨师必须与订单关联 // ... order.setStatus(targetStatus); orderMapper.updateById(order); }把所有状态流转的合法性收敛在Service层前端不管怎么调接口都绕不过这道校验。你写毕设时这一块是很容易拿分的点因为很多人的订单状态就是一个能随便改的字段没有“合法流转”的概念。4.3 定制菜品下单的校验与金额计算定制菜品的下单不止是把购物车里的菜提交上去那么简单。它和普通电商最大的区别在于订单金额要覆盖两个部分——菜品费用和服务费。菜品费用是明细里所有菜品单价乘数量之和服务费用是厨师的service_price字段。下单接口的流程大致是校验用户地址是否有效、服务时间是否在未来24小时之后。逐条校验购物车里的菜品是否处于上架状态、价格是否有变动。查询指定厨师的service_price计算服务费。总金额 菜品费用 服务费如果有优惠券再减优惠券金额。生成订单号和订单明细插入两张表使用Transactional保证一致性。Override Transactional(rollbackFor Exception.class) public OrderVO createOrder(OrderCreateDTO dto) { // 1. 校验地址 Address address addressMapper.selectById(dto.getAddressId()); if (address null) { throw new BizException(地址不存在); } // 2. 校验菜品并计算金额 BigDecimal dishAmount new BigDecimal(0); ListOrderItem items new ArrayList(); for (OrderItemDTO itemDTO : dto.getItems()) { Dish dish dishMapper.selectById(itemDTO.getDishId()); if (dish null || dish.getStatus() ! 1) { throw new BizException(菜品[ itemDTO.getDishName() ]已下架或不存在); } BigDecimal subtotal dish.getPrice().multiply(new BigDecimal(itemDTO.getQuantity())); dishAmount dishAmount.add(subtotal); OrderItem item new OrderItem(); item.setDishId(dish.getId()); item.setDishName(dish.getName()); item.setPrice(dish.getPrice()); item.setQuantity(itemDTO.getQuantity()); item.setTasteType(itemDTO.getTasteType()); item.setSubtotal(subtotal); items.add(item); } // 3. 服务费 Chef chef chefMapper.selectById(dto.getChefId()); BigDecimal serviceFee chef.getServicePrice(); // 4. 总金额 BigDecimal totalAmount dishAmount.add(serviceFee); // 5. 生成订单并落库 // ... return orderVO; }这里要注意一个细节订单明细里的dish_name和price必须在下单那一刻“快照”下来不能下单之后通过外键再去查菜品表。为什么因为菜品价格和名称是会变的如果厨师接单时再看菜品表价格早就不是用户下单时看到的价格了。订单历史数据要能回溯就不能依赖实时表。MyBatis的动态SQL在这个业务里也经常用到。比如后台订单列表要支持按状态筛选、按日期区间筛选、按订单号模糊搜索select idselectOrderList resultTypecom.example.privatechef.vo.OrderVO SELECT o.*, u.nickname AS userName, c.real_name AS chefName FROM t_order o LEFT JOIN t_user u ON o.user_id u.id LEFT JOIN t_chef c ON o.chef_id c.id where if teststatus ! null AND o.status #{status} /if if testorderNo ! null and orderNo ! AND o.order_no LIKE CONCAT(%, #{orderNo}, %) /if if teststartTime ! null AND o.create_time gt; #{startTime} /if if testendTime ! null AND o.create_time lt; #{endTime} /if /where ORDER BY o.create_time DESC /select这种写法好在哪条件是多变的用where标签自动处理AND前缀拼出来的SQL在日志里很干净排查问题也容易。MyBatis的mapUnderscoreToCamelCase配置一定要开这样数据库的order_no字段能自动映射到VO里的orderNo属性不然查出来的数据全是null。5. 前端实现用户下单和管理后台的关键页面后端接口就绪之后前端是另一大块。这部分的难点不在某个组件写不出来而在于“用户下单的流程要顺”“管理后台的效率要高”。5.1 Axios封装与路由守卫所有接口请求我都走一个统一封装的axios实例核心逻辑是请求拦截器统一带Token响应拦截器统一处理错误码。import axios from axios import { Message } from element-ui import router from /router const request axios.create({ baseURL: process.env.VUE_APP_BASE_API, timeout: 10000 }) request.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer token } return config }) request.interceptors.response.use( response { const res response.data if (res.code ! 200) { Message.error(res.msg || 请求失败) return Promise.reject(new Error(res.msg)) } return res }, error { if (error.response error.response.status 401) { localStorage.removeItem(token) router.push(/login) Message.error(登录已过期请重新登录) } else { Message.error(网络异常请稍后重试) } return Promise.reject(error) } ) export default request路由守卫做的是另一件事根据本地存的role字段控制页面访问权限。用户没登录强制跳登录页厨师访问管理员的用户管理页面直接拦截。router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.path /login) { next() return } if (!token) { next(/login) return } const role localStorage.getItem(role) if (to.meta.roles !to.meta.roles.includes(role)) { next(/403) return } next() })这些代码看着简单但对系统的安全性至关重要。后端要做接口鉴权前端要做路由控制两层都堵上才叫完整的权限方案。5.2 下单页面的核心交互逻辑用户下单页是整个前端交互最重的页面。我把它拆成四个步骤选择菜品 → 选择厨师 → 填写服务信息 → 确认订单。选择菜品区域用菜品分类做左侧Tab右侧展示菜品卡片。每张卡片上有缩略图、名称、价格和“添加”按钮。已选菜品在底部栏显示明细列表支持修改数量。选择厨师区域展示厨师的擅长菜系、从业年限、服务价格用户点击选中后高亮显示。选择厨师这个步骤不是通过搜索框找而是管理员先在后台“推荐”一个可服务的厨师上首页用户可以切换。填写服务信息包含上门地址从地址簿选择或新增、服务时间用Element UI的el-date-picker限制只能选未来7天、定制需求textarea输入框。确认订单页把之前的菜品明细、服务费、总金额汇总成一张表用户点击“提交订单”后调用后端接口拿到orderId后进入支付模拟页。系统没有对接真实支付我做了“模拟支付”按钮前端调支付接口后端直接标记pay_status1并把订单状态从0推进到1。毕设演示这就够了如果你要接真实支付支付宝沙箱或者微信支付Native模式都支持逻辑并不难接。金额计算在前端有一个实时更新公式totalAmount dishAmount serviceFee - couponAmount。有个坑要提醒你前端算的金额只能用于展示后端一定要重新算一遍而且要以下单那一刻数据库里的菜品价格为准。绝不信任前端传过来的金额。5.3 管理后台订单处理与数据统计管理后台订单列表页我用了Element UI的el-table加分页。列表每行显示订单号、用户昵称、厨师姓名、服务时间、总金额、状态。状态用了el-tag显示不同颜色待支付是灰色待接单是蓝色服务中是橙色已完成是绿色已取消是红色。操作列根据状态显示不同按钮待接单状态显示“分配厨师”和“取消订单”待支付状态显示“关闭订单”已完成状态显示“查看评价”。按钮的显隐逻辑和后端状态机要保持一致否则前端显示了非法操作按钮点击后后端返回“非法状态流转”体验很差。统计模块我做了三个图近7天订单量趋势折线图、订单状态分布饼图、菜品销量排行Top10横向柱状图。后端对应的聚合SQL用到了GROUP BY和DATE_FORMAT。比如统计近7天每日订单数的SQLSELECT DATE_FORMAT(create_time, %Y-%m-%d) AS day, COUNT(*) AS count FROM t_order WHERE create_time DATE_SUB(CURDATE(), INTERVAL 6 DAY) GROUP BY DATE_FORMAT(create_time, %Y-%m-%d) ORDER BY dayECharts的option配置我在所有页面统一封装了一个公共方法把数据源传入后自动渲染。这里不展开前端组件的每一行代码但要注意图表容器必须有固定高度否则ECharts初始化时拿到的是0高度图表白屏。6. 联调部署与常见问题排查项目写完到真正跑起来中间还有一段“联调期”。这段时间的问题往往比写代码时还多我把最常遇到的坑整理一下。6.1 本地联调跨域和代理配置前端开发服务器默认跑在8080端口或Vue CLI随机的端口后端SpringBoot跑在8080两者域名和端口不同必然产生跨域问题。解决办法有两个。第一个是后端配置CorsFilter允许所有跨域请求。第二个是前端用Vue CLI的proxy代理把/api开头的请求转发到后端的8080端口。我两个方法都配了但推荐你在开发阶段主要用前端代理// vue.config.js module.exports { devServer: { port: 3000, proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }这样前端请求/api/user/login时开发服务器会把它代理到http://localhost:8080/api/user/login浏览器看到的请求是同源的不存在跨域。生产环境部署时用Nginx做一个反向代理把/api转发到SpringBoot应用即可。6.2 部署与数据初始化注意事项SpringBoot部署很简单mvn clean package后得到一个jar包放到服务器上执行java -jar privatechef-server.jar就行。但有几个细节必须注意。MySQL连接串一定带上时区参数不然会报The server time zone value йʱ is unrecognized的错误。正确写法是spring.datasource.urljdbc:mysql://localhost:3306/private_chef?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai数据库初始化我用了一个init.sql脚本包含建库、建表、插入管理员默认账号、插入测试菜品数据和厨师数据。脚本里每条建表语句要加IF NOT EXISTS方便重复执行不报错。前端打包是npm run build产物在dist目录。如果用的是history路由模式部署到Nginx后刷新子页面会404必须在Nginx配置里加location / { try_files $uri $uri/ /index.html; }这个坑几乎每个人都会踩一次提前写上能省很多时间。MySQL驱动版本也要注意。SpringBoot 2.7.x默认管理的是mysql-connector-java 8.0.x如果你依赖里写的是5.x版本连接时驱动类要改成com.mysql.jdbc.Driver8.x则用com.mysql.cj.jdbc.Driver。6.3 常见问题速查表我把开发过程中遇到的典型问题整理成一张速查表方便你对照排查现象原因解决方案前端请求后端404接口路径不对或Nginx代理没配置检查axios baseURL和proxy配置返回的JSON中字段为nullMyBatis驼峰映射未开启在application.yml设置mapUnderscoreToCamelCasetruemysql时区报错连接串缺少serverTimezone加上serverTimezoneAsia/Shanghai图片上传后访问403静态资源映射未配置在SpringBoot配置类中addResourceHandlers映射本地磁盘路径前端刷新页面404history路由模式部署配置不对Nginx加try_files配置订单金额算出99.9900000001使用了double计算统一用BigDecimal计算并在VO转字符串打包后找不到main类Maven项目未指定启动类在pom.xml的build中加mainClass配置菜品图片不显示路径带了中文或本地磁盘路径写错图片规范命名用UUID访问用相对路径返回这张表里的问题我几乎全部踩过。尤其那个“MyBatis返回字段为null”的问题看着像数据没插进去实际是配置没开排查起来非常迷惑。你如果遇到类似“数据存在但接口返回null”的情况第一个想到的就应该是驼峰映射配置。6.4 做这类项目我的一些个人体会整套系统做下来我最深的感受是这种管理系统真正难的不在于某个技术点而在于“把业务流程理清楚然后用技术手段闭环”。你见到的很多烂尾毕设项目通常不是代码写不出来而是需求没想明白就开始建表建到一半发现表结构对不上业务又推翻重来。所以我在文章最开始才强调“先画订单状态机再写代码”。状态机这张图是整个系统的灵魂谁改状态、状态怎么流转、非法流转怎么办全在这张图里。你把这张图想清楚了后端Service写起来就是顺着图翻译成代码的事想不清楚后端就会变成一坨到处改状态字段的散沙代码。另外还有一点前后端接口的字段命名要尽量统一。后端VO用orderNo前端就别用order_number否则每次组装数据都要做一次字段映射浪费大量时间。我在项目里统一用驼峰命名JSON序列化时直接同名映射前端拿到的数据结构和页面展示完全对得上开发效率提高非常明显。最后给你一个实操建议开发时先用Postman把后端所有接口测通再开始写前端页面。先测通的意思是每个接口的正常返回、异常返回都要看一遍把返回结构固化下来。前端开发时就不会出现“这接口怎么又报500了”的频繁联调焦虑。等前后端都稳定了再整体过一遍业务流程检查状态流转是否闭环、金额计算是否正确、权限是否透传完整这样交付出来的系统才是真正能演示、能答辩、能展示的完整项目。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号