news 2026/9/3 7:36:08

SpringBoot+Vue外卖系统实战:架构设计与核心技术解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot+Vue外卖系统实战:架构设计与核心技术解析

简介:这是一套面向计算机专业本科生课程设计与毕业设计的全栈外卖点餐系统实战资源,聚焦SpringBoot后端开发与Vue前端工程化实践,解决中小型餐饮商户线上订单管理与用户便捷点餐的双重需求。压缩包共638个文件,含121个Java后端业务类(如UserController、UserServiceImpl)、79个Vue组件页(覆盖登录、商品列表、购物车、订单管理等核心流程)、72个HTML模板、64个JS交互脚本及21个CSS样式文件,辅以SQL建表语句、YML配置、BAT部署脚本等完整工程要素,总大小27.74MB。已有508人学习下载,资源结构清晰,包含可直接运行的前后端源码、分步骤部署说明文档(含install.bat/run.bat双环境支持)及图文并茂的系统功能介绍,特别适合初学者理解RESTful接口设计、JWT权限控制、文件上传、分页查询等典型SpringBoot+Vue集成场景。

1. 项目背景与核心价值:为什么选择SpringBoot+Vue做外卖系统?

最近几年,无论是创业公司还是传统餐饮企业,数字化转型的需求都异常旺盛。一个稳定、高效、易于维护的外卖点餐系统,已经从一个“加分项”变成了“必需品”。我手头这个基于SpringBoot和Vue的外卖点餐系统项目,就是在这个大背景下诞生的一个典型实践。它不是一个简单的玩具Demo,而是一个具备了前后端分离、完整业务流程、可二次开发的实战项目。

为什么是SpringBoot + Vue这个组合?这几乎是当前企业级Web应用开发的“黄金搭档”。SpringBoot以其“约定大于配置”的理念,极大地简化了后端服务的搭建和部署。你不再需要为复杂的XML配置、依赖冲突而头疼,一个@SpringBootApplication注解就能启动一个生产就绪的服务。对于外卖系统这种业务逻辑复杂(涉及用户、商家、骑手、订单、支付、库存等多个模块)、并发要求高(用餐高峰期)的场景,SpringBoot提供的自动配置、内嵌Tomcat、强大的Starter生态(如Spring Security做安全、Spring Data JPA/MyBatis做数据持久化、Spring Cache做缓存)能让你快速构建出健壮的后端API。

而Vue.js作为前端框架,其渐进式的特性和极低的学习曲线,让前端开发变得高效且愉悦。通过组件化开发,我们可以将点餐页面的菜品展示、购物车、订单表单等拆分成独立的、可复用的组件。Vue的响应式数据绑定和虚拟DOM,能确保在用户频繁操作(如添加菜品、修改数量)时,界面依然流畅。更重要的是,前后端分离的架构让团队协作更清晰:后端工程师专注于API设计和业务逻辑,提供RESTful接口;前端工程师则专注于用户体验和界面交互,通过Axios等工具消费这些接口。这种解耦也使得未来移动端App(如用Uni-app)或小程序复用同一套后端API成为可能。

这个项目包(源码+部署说明)的价值,就在于它提供了一个“麻雀虽小,五脏俱全”的完整实现。你拿到的不只是一堆代码,而是一个可以跑起来、能理解其设计思路、并能基于此进行定制开发的起点。无论是用于学习全栈开发技术栈,还是作为课程设计、毕业项目,甚至是为中小型餐厅快速搭建一个线上点餐平台,它都具有很高的参考价值和实用性。

2. 系统架构与核心模块拆解

一个外卖点餐系统,其核心业务逻辑围绕着“用户下单、商家接单、骑手配送”这条主线展开。我们这个项目采用了经典的三层架构(表现层、业务逻辑层、数据访问层)和前后端分离模式,下面我们来深入拆解各个核心模块。

2.1 后端(SpringBoot)模块设计

后端是整个系统的大脑,负责处理所有业务逻辑、数据存储和安全认证。在SpringBoot项目中,我们通常按功能进行模块划分,在src/main/java/com/yourdomain目录下,你会看到类似这样的包结构:

  • entity/domain/model: 实体类包。这里定义了与数据库表一一对应的Java对象。例如User(用户)、Dish(菜品)、Category(菜品分类)、Order(订单)、OrderDetail(订单明细)、Address(地址)等。每个类都使用JPA的@Entity注解,并定义了字段、关系(如@OneToMany)和基本的Getter/Setter。
  • mapper/repository: 数据访问层。如果使用MyBatis,这里存放的是Mapper接口和对应的XML映射文件,用于编写SQL语句。如果使用Spring Data JPA,这里则是继承JpaRepository的接口,通过方法名约定或@Query注解来操作数据。例如OrderRepository.findByUserIdAndStatus
  • service: 业务逻辑层。这里包含了系统的核心业务规则。Service接口定义契约,ServiceImpl类实现具体逻辑。例如OrderService中会有submitOrder(提交订单)、payOrder(支付订单)、getOrderDetail(查询订单详情)等方法。这一层会调用Repository获取数据,进行业务处理(如计算总价、校验库存),并处理事务(使用@Transactional)。
  • controller: 控制层,即API接口层。接收前端HTTP请求,调用对应的Service方法,并返回JSON格式的数据。使用@RestController@RequestMapping等注解。例如OrderController会有/order/submit(POST提交订单)、/order/history(GET订单历史)等端点。
  • config: 配置类。用于集中管理各种配置,如数据库连接池(HikariCP)、Redis缓存、跨域(CORS)、Swagger API文档、安全规则等。
  • common/utils: 通用工具类。包含常量定义、自定义异常、统一响应封装(RResult类)、JWT工具、加密工具、日期处理等。
  • dto: 数据传输对象。用于在不同层之间传递数据,通常比Entity更精简或更聚合,以适应前端需求。例如OrderSubmitDTO可能只包含用户ID、地址ID和购物车条目列表,而不包含数据库生成的ID和状态字段。
  • vo: 视图对象。专门用于返回给前端的对象,可能聚合多个Entity的数据。例如OrderVO除了订单基本信息,还可能包含一个List<OrderDetailVO>来展示订单中的菜品详情。

核心业务流程串联(以下单为例)

  1. 前端(Vue)用户点击提交订单,携带OrderSubmitDTO调用后端/order/submit接口。
  2. OrderController接收请求,进行基本参数校验。
  3. 调用OrderService.submitOrder方法。
  4. OrderService中:
    • 校验用户信息、地址有效性。
    • 遍历购物车,查询DishService获取菜品实时价格和库存,计算总价。
    • 扣减库存(注意并发控制,可使用乐观锁或Redis分布式锁)。
    • 创建OrderOrderDetail实体,设置状态为“待支付”。
    • 调用PaymentService模拟或对接真实支付渠道,生成支付信息。
    • 所有数据库操作在一个@Transactional事务中,保证一致性。
  5. 将生成的订单ID和支付信息封装成OrderSubmitVO返回给前端。
  6. 前端引导用户完成支付,并轮询查询订单状态。

2.2 前端(Vue)组件与路由设计

前端项目通常使用Vue CLI创建,结构清晰。核心在src目录下:

  • views/pages: 页面级组件。每个文件对应一个路由页面。例如:
    • Home.vue: 首页,展示餐厅公告、推荐菜品。
    • Menu.vue: 菜单页,按分类展示菜品,包含搜索和筛选功能。
    • Cart.vue: 购物车页面,管理已选菜品,展示总价。
    • Order.vue: 订单提交与支付页面。
    • Profile.vue: 个人中心,管理地址、查看历史订单。
    • Login.vue/Register.vue: 登录注册页。
  • components: 可复用的小组件。例如:
    • DishItem.vue: 单个菜品的展示卡片(图片、名称、价格、加入购物车按钮)。
    • CategoryTab.vue: 菜品分类的标签页。
    • AddressPicker.vue: 地址选择器组件。
    • PaymentDialog.vue: 支付弹窗组件。 组件化开发的好处是逻辑清晰、易于维护和测试。
  • router: 路由配置。使用Vue Router定义路径与组件的映射关系,并可以配置路由守卫(Navigation Guards)来实现权限控制,例如未登录用户访问个人中心会被重定向到登录页。
    // 示例路由守卫 router.beforeEach((to, from, next) => { const isAuthenticated = store.getters.isLoggedIn; // 从Vuex获取登录状态 if (to.meta.requiresAuth && !isAuthenticated) { next('/login'); } else { next(); } });
  • store: 状态管理。使用Vuex来集中管理跨组件的共享状态。对于外卖系统,核心的state可能包括:
    • userInfo: 当前登录用户信息。
    • cartItems: 购物车中的商品列表。
    • selectedAddress: 当前选中的配送地址。mutations用于同步修改状态,actions用于处理异步逻辑(如调用API更新购物车)。
  • api: 接口封装。将所有的后端API请求集中管理。使用Axios实例,并配置请求拦截器(添加JWT Token)和响应拦截器(统一处理错误)。
    // api/order.js import request from '@/utils/request'; // 这是封装好的Axios实例 export function submitOrder(data) { return request({ url: '/order/submit', method: 'post', data }); }
  • assets/styles: 静态资源与样式。存放图片、图标、全局CSS或SCSS文件。

前后端数据交互流程

  1. 用户在Menu.vue点击“加号”,触发addToCart方法。
  2. 该方法dispatch一个Vuex的Action,例如addCartItem
  3. 在Action中,可能会先调用本地api/cart.js中的addItem方法。
  4. addItem方法使用Axios向/cart/add发送POST请求,携带菜品ID和数量。
  5. 后端CartController处理请求,更新数据库或Redis中的购物车数据,返回成功结果。
  6. Axios响应拦截器收到成功响应,Action继续执行,commit一个Mutation来更新Vuex中的cartItems状态。
  7. 由于Vue的响应式系统,所有依赖cartItems的组件(如购物车图标上的数量徽标)会自动更新视图。

3. 关键技术与难点实现解析

在实现这样一个系统的过程中,有几个技术点是必须啃下来的硬骨头,它们直接关系到系统的稳定性、性能和用户体验。

3.1 购物车与库存的并发控制

这是外卖系统最经典的并发问题。假设某热门菜品只剩最后一份,两个用户同时点击购买。如果没有控制,两份订单都会成功,导致超卖。

解决方案一:数据库乐观锁Dish实体中增加一个版本号字段version

@Entity public class Dish { @Id private Long id; private String name; private Integer stock; // 库存 @Version // JPA乐观锁注解 private Integer version; // ... getters and setters }

在扣减库存的Service方法中:

@Transactional public boolean reduceStock(Long dishId, Integer quantity) { // 1. 查询菜品,此时version为v1 Dish dish = dishRepository.findById(dishId).orElseThrow(...); // 2. 检查库存是否充足 if (dish.getStock() < quantity) { throw new BusinessException("库存不足"); } // 3. 扣减库存 dish.setStock(dish.getStock() - quantity); // 4. 更新,此时SQL类似:UPDATE dish SET stock=?, version=version+1 WHERE id=? AND version=v1 dishRepository.save(dish); // 如果更新影响行数为0,说明version已被其他事务修改,抛出异常,事务回滚 return true; }

乐观锁在冲突不频繁的场景下性能很好。但在秒杀等高并发场景,大量失败重试会给数据库和用户带来压力。

解决方案二:Redis分布式锁在扣减库存前,先尝试获取一个基于菜品ID的Redis锁。

public boolean reduceStockWithRedisLock(Long dishId, Integer quantity) { String lockKey = "lock:dish:" + dishId; String requestId = UUID.randomUUID().toString(); // 唯一标识,防止误删 try { // 尝试加锁,SET key value NX PX 30000 Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, 30, TimeUnit.SECONDS); if (!locked) { // 获取锁失败,可重试或直接返回失败 return false; } // 获取锁成功,执行库存扣减 Dish dish = dishRepository.findById(dishId).orElseThrow(...); if (dish.getStock() < quantity) { return false; } dish.setStock(dish.getStock() - quantity); dishRepository.save(dish); return true; } finally { // 释放锁,使用Lua脚本保证原子性,判断requestId是否匹配 String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"; redisTemplate.execute(new DefaultRedisScript<>(luaScript, Long.class), Arrays.asList(lockKey), requestId); } }

Redis锁能更好地应对分布式环境下的并发,但引入了外部依赖,复杂度更高。实际项目中,通常会采用“缓存库存+异步落库”的方案来应对极致并发:将菜品库存预热到Redis,下单时在Redis中预扣减,生成临时订单,然后通过消息队列异步处理数据库的最终扣减和订单创建,这里就不展开了。

3.2 订单状态机与超时处理

一个订单的生命周期包含多个状态:待支付->已支付/待接单->已接单/制作中->配送中->已完成。也可能有已取消退款中等异常状态。必须清晰地定义状态流转规则,避免出现非法状态切换(比如从已完成回到配送中)。

实现方案

  1. 枚举定义状态
    public enum OrderStatus { PENDING_PAYMENT, // 待支付 PAID, // 已支付 ACCEPTED, // 商家已接单 COOKING, // 制作中 DELIVERING, // 配送中 COMPLETED, // 已完成 CANCELLED, // 已取消 REFUNDING // 退款中 }
  2. 在Service中实现状态转换逻辑,并可能使用状态模式(State Pattern)来封装不同状态的行为。
  3. 超时处理:这是关键。对于待支付订单,通常有15-30分钟的支付超时时间。实现方式有两种:
    • 定时任务扫描:使用Spring的@Scheduled注解,每隔一分钟扫描所有状态为待支付且创建时间超过阈值的订单,将其状态改为已取消,并释放库存。
      @Scheduled(cron = "0 */1 * * * ?") // 每分钟执行一次 public void cancelUnpaidOrders() { List<Order> unpaidOrders = orderRepository.findByStatusAndCreateTimeBefore(OrderStatus.PENDING_PAYMENT, LocalDateTime.now().minusMinutes(15)); for (Order order : unpaidOrders) { cancelOrder(order); // 内部处理状态变更和库存释放 } }
    • 延迟消息:在订单创建时,向消息队列(如RabbitMQ、RocketMQ)发送一条延迟消息。消息在15分钟后被消费,消费者检查订单状态,如果仍是待支付则执行取消逻辑。这种方式更精确、对数据库压力小,但引入了消息队列的复杂度。

3.3 前后端分离下的身份认证与安全

前后端分离后,传统的Session-Cookie机制不再适用(涉及跨域等问题)。JWT(JSON Web Token)成为了主流选择。

工作流程

  1. 用户在前端输入用户名密码登录。
  2. 前端调用/auth/login接口。
  3. 后端验证凭证,成功后,使用密钥生成一个JWT Token(通常包含用户ID、角色、过期时间等信息)。
  4. 后端将JWT Token返回给前端(通常放在响应体的一个字段里,如data.token)。
  5. 前端收到Token后,将其存储在本地(LocalStorage或Vuex),并在后续的每一次请求的HTTP Header中携带:Authorization: Bearer <your_token>
  6. 后端通过一个JwtAuthenticationFilter拦截所有需要认证的请求,从Header中提取Token,验证其有效性和过期时间,并从中解析出用户信息,设置到Spring Security的上下文中。
  7. Controller中的方法可以通过@AuthenticationPrincipal注解直接获取当前用户信息。

安全注意事项

  • Token存储:前端不要将Token放在容易被XSS攻击读取的地方(如全局变量)。LocalStorage有被XSS窃取的风险,但更常见。可以考虑使用HttpOnly Cookie(防XSS,但需处理CSRF)。本项目为简化,通常用LocalStorage。
  • 密钥保护:JWT的签名密钥必须足够复杂,并妥善保存在后端配置文件中(如application.yml),绝不能泄露。
  • 短期令牌与刷新令牌:为了安全,Access Token(访问令牌)有效期应较短(如2小时)。同时颁发一个有效期较长的Refresh Token(刷新令牌)。当Access Token过期后,前端用Refresh Token去换取新的Access Token,无需用户重新登录。这需要在后端维护一个Refresh Token的白名单或黑名单。
  • 接口权限控制:使用Spring Security的@PreAuthorize注解或方法级安全控制,实现基于角色的访问控制(RBAC)。例如,@PreAuthorize("hasRole('ADMIN')")表示只有管理员能访问该接口。

4. 本地开发环境搭建与运行指南

拿到源码后,第一步就是让它在你自己的机器上跑起来。这里提供一份详细的本地环境搭建步骤。

4.1 后端(SpringBoot)环境准备

  1. 基础环境

    • JDK:确保安装JDK 8或11(建议11),配置好JAVA_HOME环境变量。
    • Maven:安装Maven 3.6+,用于管理项目依赖和构建。
    • IDE:推荐使用IntelliJ IDEA(社区版或旗舰版),它对SpringBoot支持极好。Eclipse with STS插件也可。
    • 数据库:安装MySQL 5.7+或8.0。创建一个空的数据库,例如takeaway_db。字符集建议utf8mb4,排序规则utf8mb4_general_ci
  2. 导入与配置项目

    • 解压项目包,用IDEA打开根目录下的pom.xml文件,以Maven项目导入。
    • 等待IDEA自动下载所有依赖(观察右下角进度条)。这个过程可能因网络状况需要几分钟。
    • 找到src/main/resources/application.yml(或application.properties)文件。这是SpringBoot的核心配置文件。
    • 修改数据库连接配置:
      spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/takeaway_db?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai username: root # 你的MySQL用户名 password: yourpassword # 你的MySQL密码 jpa: hibernate: ddl-auto: update # 首次启动可以用update,自动根据Entity创建/更新表结构。生产环境务必改为validate或none。 show-sql: true # 开发时开启,方便看生成的SQL
    • 检查并修改其他配置,如Redis连接信息(如果项目用了缓存)、文件上传路径、JWT密钥等。
  3. 运行与测试

    • 找到主启动类(通常名为XxxApplication,带有@SpringBootApplication注解),右键点击Run
    • 观察控制台日志,如果没有报错,看到类似Tomcat started on port(s): 8080的日志,说明后端启动成功。
    • 打开浏览器,访问http://localhost:8080/swagger-ui.html(如果集成了Swagger),可以看到所有API接口的文档,并可以直接测试。或者使用Postman等工具测试接口。

4.2 前端(Vue)环境准备

  1. 基础环境

    • Node.js:安装Node.js LTS版本(如16.x, 18.x)。这自带了npm包管理器。
    • 包管理器:可以使用npm,但更推荐yarnpnpm,速度更快、依赖管理更清晰。安装Node.js后,可以通过npm install -g yarn安装yarn。
  2. 导入与安装依赖

    • 进入前端项目目录(通常是一个包含package.json的文件夹,如frontendvue-project)。
    • 打开终端,执行yarn installnpm install。这会根据package.jsonyarn.lock/package-lock.json安装所有依赖包。
    • 常见坑点:如果遇到网络问题导致依赖下载失败,可以配置淘宝镜像源:
      # 对于npm npm config set registry https://registry.npmmirror.com # 对于yarn yarn config set registry https://registry.npmmirror.com
  3. 配置与运行

    • 找到前端项目的配置文件,通常是src目录下的某个文件,如.env.developmentvue.config.js。需要配置后端API的代理,解决开发时的跨域问题。
    • vue.config.js中配置:
      module.exports = { devServer: { proxy: { '/api': { // 所有以/api开头的请求 target: 'http://localhost:8080', // 你的后端地址 changeOrigin: true, pathRewrite: { '^/api': '' // 重写路径,去掉/api前缀(根据你后端接口实际路径调整) } } } } }
    • 在终端执行yarn servenpm run serve。命令成功后,会输出本地开发服务器的地址,通常是http://localhost:8081
    • 打开浏览器访问该地址,应该能看到前端页面。此时,前端发起的API请求会被代理到localhost:8080的后端服务。

4.3 数据库初始化与数据准备

项目源码中通常会包含数据库的初始化脚本(sql文件夹下的.sql文件)或通过Flyway/Liquibase这样的数据库迁移工具来管理。

  1. 使用SQL脚本

    • 找到docsql目录下的schema.sql(表结构)和data.sql(初始数据)。
    • 在MySQL客户端(如Navicat、MySQL Workbench或命令行)中,连接到之前创建的takeaway_db数据库。
    • 依次执行这两个SQL文件。先执行schema.sql创建所有表,再执行data.sql插入管理员账号、菜品分类、示例菜品等数据。
  2. 使用JPA ddl-auto

    • 如果配置了spring.jpa.hibernate.ddl-auto=update,SpringBoot启动时会自动检查Entity与数据库表的差异,并创建或更新表结构。但不会插入初始数据
    • 为了插入初始数据,可以在项目中创建一个CommandLineRunnerBean,在应用启动后执行数据插入逻辑。
    • 或者,更规范的做法是使用data.sql文件,SpringBoot会在启动时自动执行该文件(需确保spring.sql.init.mode设置为always)。

验证:启动前后端后,尝试用初始数据中的账号(如admin/123456)登录系统,浏览菜品、加入购物车、模拟下单,走通核心流程。

5. 生产环境部署要点与优化建议

让项目在本地运行只是第一步,要真正上线提供服务,还需要进行一系列的生产级配置和优化。

5.1 后端部署(以Linux服务器为例)

  1. 打包:在项目根目录下,使用Maven命令打包。

    mvn clean package -DskipTests

    这会在target目录下生成一个可执行的JAR文件(如takeaway-0.0.1-SNAPSHOT.jar)。这个JAR包内嵌了Tomcat服务器。

  2. 服务器准备

    • 购买一台云服务器(如阿里云ECS、腾讯云CVM),安装好JDK。
    • 安装MySQL、Redis(如果用到)并完成安全配置(修改默认端口、设置强密码、限制访问IP)。
    • 将打包好的JAR文件上传到服务器,例如/app目录下。
  3. 运行与守护

    • 直接使用java -jar命令运行在后台:
      nohup java -Xms512m -Xmx1024m -jar takeaway-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod > app.log 2>&1 &
      • -Xms-Xmx设置JVM堆内存。
      • --spring.profiles.active=prod指定使用application-prod.yml配置文件。
      • nohup&让进程在后台运行,> app.log将日志输出到文件。
    • 更推荐的方式是使用系统服务(如Systemd)来管理,实现开机自启和故障重启。 创建文件/etc/systemd/system/takeaway.service
      [Unit] Description=Takeaway Backend Service After=network.target [Service] Type=simple User=appuser # 建议用一个非root用户运行 WorkingDirectory=/app ExecStart=/usr/bin/java -Xms512m -Xmx1024m -jar takeaway-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target
      然后执行:
      sudo systemctl daemon-reload sudo systemctl start takeaway sudo systemctl enable takeaway # 开机自启
  4. 生产配置文件:创建application-prod.yml,覆盖开发配置。

    spring: datasource: url: jdbc:mysql://生产数据库内网地址:3306/takeaway_db?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=Asia/Shanghai username: prod_user # 生产环境专用账号,权限最小化 password: strong_password # 强密码 hikari: maximum-pool-size: 20 # 连接池大小根据实际情况调整 jpa: hibernate: ddl-auto: validate # 生产环境务必不要用update,改为validate或none show-sql: false # 关闭SQL日志,提升性能 # Redis配置 redis: host: 生产Redis内网地址 port: 6379 password: redis_password # 日志配置 logging: file: name: /var/log/takeaway/app.log level: com.yourdomain: INFO # 自定义配置 jwt: secret: 一个非常复杂且长的生产环境密钥 # 务必与开发环境不同! expiration: 7200 # token过期时间(秒)

5.2 前端部署

  1. 构建:在前端项目目录下,运行构建命令,生成静态文件。

    yarn build # 或 npm run build

    这会在项目下生成一个dist目录,里面是压缩优化后的HTML、CSS、JS文件。

  2. 部署到Web服务器

    • 方案A:使用Nginx(最常用)。将dist目录下的所有文件上传到服务器某个目录,如/var/www/takeaway
    • 配置Nginx:
      server { listen 80; server_name your-domain.com; # 你的域名 root /var/www/takeaway; index index.html; # 处理前端路由(history模式) location / { try_files $uri $uri/ /index.html; } # 反向代理后端API location /api/ { proxy_pass http://localhost:8080/; # 代理到后端服务 proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # 静态资源缓存 location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ { expires 1y; add_header Cache-Control "public, immutable"; } }
    • 重启Nginx:sudo systemctl restart nginx
    • 方案B:与后端一起部署。将dist目录内容复制到SpringBoot项目的src/main/resources/static目录下,重新打包。这样前后端就在同一个JAR包里,通过同一个端口访问。这种方式更简单,但不利于前后端独立部署和缓存策略优化。

5.3 性能与安全优化建议

  • 数据库
    • 为高频查询的字段建立索引,如order表的user_idstatuscreate_time
    • 避免SELECT *,只查询需要的字段。
    • 对大数据量表进行分库分表考虑(订单表随时间增长极快)。
  • 缓存
    • 使用Redis缓存热点数据,如菜品信息、用户会话(如果不用JWT)、首页配置等。
    • 注意缓存穿透(查询不存在的数据)、缓存击穿(热点key过期)、缓存雪崩(大量key同时过期)问题,采用布隆过滤器、互斥锁、随机过期时间等策略应对。
  • 图片/文件存储
    • 不要将上传的菜品图片保存在应用服务器本地。应使用对象存储服务(如阿里云OSS、腾讯云COS),它们提供高可用、高扩展的存储和CDN加速。
  • 安全加固
    • HTTPS:为域名申请SSL证书,在Nginx中配置HTTPS,强制HTTP跳转。
    • SQL注入:使用MyBatis时,务必用#{}而非${}。JPA使用参数化查询。
    • XSS:后端对用户输入进行过滤/转义,前端使用Vue的{{ }}插值或v-text指令会自动转义HTML。
    • CSRF:如果使用Cookie存储Token,需要配置CSRF保护。使用Header(Authorization)方式则无此问题。
    • 接口限流与防刷:对登录、短信验证码等接口进行限流(如使用Guava RateLimiter或Redis实现),防止恶意攻击。
  • 监控与日志
    • 接入APM工具(如SkyWalking、Pinpoint)监控应用性能。
    • 使用ELK(Elasticsearch, Logstash, Kibana)或Loki+Grafana集中管理日志,便于问题排查。

部署上线不是终点,而是一个新阶段的开始。你需要持续监控系统运行状态,根据实际业务量进行扩容和优化。这个SpringBoot+Vue的外卖点餐系统项目,为你提供了一个坚实的起点和一套完整的技术实践。理解它、运行它、改造它,你就能掌握全栈开发的精髓,并具备构建真实商业应用的能力。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/3 7:34:39

TLS、国密、PQC 共存过渡方案与政企系统迁移风险评估清单

1. 引言 随着量子计算技术的快速发展&#xff0c;传统公钥密码体系&#xff08;RSA、ECC 等&#xff09;面临严峻挑战。与此同时&#xff0c;国家密码管理局持续推进商用密码应用安全性评估&#xff0c;要求政企系统逐步采用国密算法&#xff08;SM2、SM3、SM4&#xff09;。在…

作者头像 李华
网站建设 2026/9/3 7:33:43

海外机器人首单交付全流程:从销售签约到现场验收与远程运维

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/3 7:32:27

企业广域网选型实战:SD-WAN与MPLS在多分支场景下的成本与性能对比

一、企业广域网选型背景&#xff1a;多分支组网的成本压力 一家连锁餐饮&#xff0c;门店从十几家开到六十多家&#xff0c;负责信息化的老周最近为网络的事发愁。每开一家新店就要拉一条专线&#xff0c;运营商排期动辄一个多月&#xff1b;单店月费几千块&#xff0c;全年网络…

作者头像 李华
网站建设 2026/9/3 7:32:21

交易认知:用好技术指标的前提与自查框架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/3 7:32:10

小波神经网络在交通流量预测中的Matlab仿真实现

简介&#xff1a;本资源是一套面向本硕博层次科研与工程实践者的交通流量预测教学仿真方案&#xff0c;聚焦小波神经网络在智能交通系统中的建模与应用&#xff0c;助力学习者掌握该算法的MATLAB实现原理与工程调试技巧。压缩包共6个文件&#xff08;3个核心M函数、1段全程操作…

作者头像 李华
网站建设 2026/9/3 7:31:46

从管线思维到批量自动化:Houdini艺术家必备Python实战

进入影视特效和游戏CG行业后&#xff0c;会经常听到一个词&#xff1a;Pipeline&#xff08;管线&#xff09;。管线的本质并不是程序员的专利&#xff0c;它是一套让重复劳动变得可复用、可维护、可追踪的工作方法。对于 Houdini 艺术家来说&#xff0c;学会 Python 最大的价值…

作者头像 李华