news 2026/10/7 3:36:05

Spring Boot + MySQL + Vue 咖啡店管理系统全栈源码实战:从建库到打包部署

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Boot + MySQL + Vue 咖啡店管理系统全栈源码实战:从建库到打包部署

简介:这是一套面向Java全栈学习者与中小型咖啡店数字化需求的春华秋实咖啡店管理系统源码,采用Spring Boot、MyBatis-Plus、MySQL与Vue.js技术栈,适合作为课程设计、毕业设计或企业级后台管理项目的参考案例。压缩包共59个文件,约76KB,其中Java源文件38个承载后端业务逻辑,XML配置15个负责框架与数据源管理,另有yml、html、iml及gitignore等文件分别对应Spring Boot配置、前端页面、IDE工程与版本控制,结构紧凑、层次分明。系统围绕订单处理、库存管理、顾客信息维护等核心场景展开,并预留权限管理与数据统计的扩展空间,可帮助读者理解前后端分离架构下接口设计与持久层封装思路。目前已有505人学习下载,适合希望快速掌握Spring Boot与Vue整合开发、需要完整可运行项目骨架的开发者参考借鉴。

1. 春华秋实咖啡店管理系统:一套能跑通的 Spring Boot + MySQL + Vue 全栈源码长什么样

很多做 Java 课程设计或接私活的朋友,拿到「基于 Spring Boot + MySQL + Vue 的咖啡店管理系统源码」这类标题时,第一反应是去搜现成压缩包,结果下下来一堆跑不起来的半成品:后端接口对不上、前端跨域报错、数据库字段缺一半。我前阵子帮一个开咖啡店的朋友做内部点单和库存管理,顺手把这套技术栈从零搭了一遍,发现真正能落地的关键不在代码量,而在三件事——表结构设计、前后端接口约定、以及 Vue 打包后怎么塞进 Spring Boot 一起部署。这篇笔记就按这个顺序,把「咖啡店管理系统」从建库到联调再到打包上线的完整路径拆开讲,适合有 Java 和 Vue 基础、想拿一套能演示能交付的全栈项目练手的同学。源码结构我会给出目录约定和核心文件写法,你照着改就能变成自己的东西。

2. 先定业务边界:咖啡店管理系统到底要管哪几张表

2.1 从点单场景倒推功能模块

咖啡店和普通电商最大的区别是:商品有规格(中杯/大杯/加糖/去冰),订单高频且客单价低,库存消耗和配方挂钩。所以别一上来就抄电商的 SPU/SKU 那套,容易过度设计。我一般把系统拆成五个模块:用户与权限、商品与分类、订单与支付状态、库存与原料、统计报表。对应到数据库,核心表控制在 8 张以内,新手维护起来不痛苦。

具体表清单如下,字段类型我按 MySQL 8.0 的习惯给:

表名用途关键字段
sys_user员工/管理员账号id, username, password, role, status
category商品分类id, name, sort
product咖啡/甜点id, category_id, name, price, image, status
product_spec规格(杯型/冷热)id, product_id, spec_name, extra_price
orders订单主表id, order_no, user_id, total_amount, pay_status, create_time
order_item订单明细id, order_id, product_id, spec_id, quantity, price
material原料库存id, name, stock, unit, warn_stock
material_log库存流水id, material_id, change_num, type, create_time

建表时有两个细节容易翻车:一是orders的order_no别用自增 id 直接暴露,用时间戳加随机数生成;二是金额字段统一用DECIMAL(10,2),别用 float,否则统计报表会出现 0.30000000000000004 这种玄学数字。

2.2 用 SQL 脚本一次性初始化

把建表和初始数据写成一个init.sql,方便换电脑时一键恢复。下面是我常用的写法,注意字符集和引擎:

-- 建库,字符集用 utf8mb4 支持 emoji(咖啡店名字可能带符号) CREATE DATABASE coffee_shop DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE coffee_shop; -- 商品表,status 用 1 上架 0 下架 CREATE TABLE product ( id INT PRIMARY KEY AUTO_INCREMENT, category_id INT NOT NULL, name VARCHAR(64) NOT NULL, price DECIMAL(10,2) NOT NULL DEFAULT 0.00, image VARCHAR(255) DEFAULT '', status TINYINT DEFAULT 1, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_category (category_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 订单主表,order_no 唯一索引防止重复提交 CREATE TABLE orders ( id INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL, user_id INT NOT NULL, total_amount DECIMAL(10,2) NOT NULL, pay_status TINYINT DEFAULT 0 COMMENT '0待支付 1已支付 2已取消', create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_order_no (order_no) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 插入测试分类和商品 INSERT INTO category (name, sort) VALUES ('经典咖啡', 1), ('果茶', 2), ('甜点', 3); INSERT INTO product (category_id, name, price) VALUES (1, '美式', 15.00), (1, '拿铁', 18.00), (2, '满杯百香果', 16.00);

执行方式:命令行mysql -u root -p < init.sql,或者在 Navicat 里直接运行。参数上唯一要改的是数据库名和字符集,如果你本地 MySQL 是 5.7,utf8mb4_general_ci也支持,但DEFAULT CURRENT_TIMESTAMP在 5.7 的 DATETIME 上需要显式写DEFAULT CURRENT_TIMESTAMP才生效,8.0 没这个问题。这一步做完,用SHOW TABLES;能看到 8 张表就算过了。

3. Spring Boot 后端:接口分层与 MyBatis 映射的落地写法

3.1 项目结构与依赖选型

后端我用 Spring Boot 2.7 + MyBatis-Plus,不引 JPA。原因很直接:咖啡店管理系统的查询条件杂(按时间、按分类、按支付状态),MyBatis 写动态 SQL 更顺手,而且 MyBatis-Plus 的LambdaQueryWrapper能省掉大量样板代码。pom.xml核心依赖就四个:

<dependencies> <!-- web 提供 REST 接口 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- mybatis-plus 简化单表 CRUD --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> </dependency> <!-- mysql 驱动,8.0 用 com.mysql.cj.jdbc.Driver --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.33</version> </dependency> <!-- lombok 省 getter/setter --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> </dependencies>

application.yml里数据源和端口这样配:

server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/coffee_shop?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 你的密码 mybatis-plus: configuration: map-underscore-to-camel-case: true # 数据库下划线转 Java 驼峰 global-config: db-config: logic-delete-field: deleted # 逻辑删除字段,可选

serverTimezone必须写,否则连接 MySQL 8 会报时区错误,这是新手最常见的翻车点之一。

3.2 订单接口的 Controller 与 Service 写法

以「创建订单」为例,前端传过来的是商品 id、规格 id、数量,后端要算总价、扣库存、生成订单号。Controller 只做参数校验和转发,业务逻辑放 Service:

@RestController @RequestMapping("/api/order") public class OrderController { @Autowired private OrderService orderService; // 创建订单,接收前端 JSON @PostMapping("/create") public Result create(@RequestBody OrderCreateDTO dto) { // dto 里包含 userId、items 列表 if (dto.getItems() == null || dto.getItems().isEmpty()) { return Result.fail("订单不能为空"); } String orderNo = orderService.createOrder(dto); return Result.ok(orderNo); } }

Service 实现里,生成订单号用System.currentTimeMillis()加三位随机数,扣库存用UPDATE material SET stock = stock - ? WHERE id = ? AND stock >= ?,靠数据库行锁保证不超卖。这里有个参数要留意:@Transactional注解必须加在 Service 方法上,且异常要抛出RuntimeException才会回滚,捕获后不抛等于白加。

@Service public class OrderServiceImpl implements OrderService { @Autowired private OrdersMapper ordersMapper; @Autowired private MaterialMapper materialMapper; @Override @Transactional(rollbackFor = Exception.class) public String createOrder(OrderCreateDTO dto) { String orderNo = System.currentTimeMillis() + String.format("%03d", new Random().nextInt(1000)); BigDecimal total = BigDecimal.ZERO; for (OrderItemDTO item : dto.getItems()) { // 查商品价格,累加总价 Product p = productMapper.selectById(item.getProductId()); total = total.add(p.getPrice().multiply(new BigDecimal(item.getQuantity()))); // 扣原料库存,影响行数为 0 说明库存不足 int rows = materialMapper.reduceStock(item.getMaterialId(), item.getQuantity()); if (rows == 0) { throw new RuntimeException("原料库存不足"); } } Orders order = new Orders(); order.setOrderNo(orderNo); order.setUserId(dto.getUserId()); order.setTotalAmount(total); ordersMapper.insert(order); return orderNo; } }

这段代码的逻辑说明:先循环明细算总价并扣库存,任何一步失败抛异常触发回滚,最后插入订单主表。参数上reduceStock的 SQL 要写成stock >= #{num}的条件更新,不能先查再改,否则并发下会超卖。

3.3 跨域配置与统一返回格式

前端 Vue 跑在 5173 或 8081 端口,后端 8080,浏览器会拦跨域。最省事的做法是加一个全局配置类,别在每个 Controller 上写@CrossOrigin:

@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOriginPatterns("*") // 开发阶段放开,上线改成具体域名 .allowedMethods("GET", "POST", "PUT", "DELETE") .allowCredentials(true) .maxAge(3600); } }

统一返回格式用Result<T>类,包含code、msg、data三个字段,前端判断code === 200再取 data。这样前端拦截器写起来简单,不用每个接口单独处理。

4. Vue 前端:从路由到接口封装的完整链路

4.1 环境搭建与项目初始化

Vue 我用 Vue 3 + Vite + Element Plus,比 Vue 2 的 webpack 快很多。初始化命令:

npm create vite@latest coffee-admin -- --template vue cd coffee-admin npm install npm install element-plus axios vue-router pinia npm run dev

装完后package.json里会有vue、vue-router、axios依赖。Vite 默认端口 5173,如果被占用会自动换,看终端输出即可。这里有个坑:Node 版本建议 16 以上,低于 14 装 Vite 会报crypto.getRandomValues相关错误。

4.2 路由与页面结构

咖啡店管理系统的页面不多,路由表控制在 6 个以内:登录、点单收银、商品管理、订单列表、库存管理、数据看板。router/index.js写法:

import { createRouter, createWebHistory } from 'vue-router' const routes = [ { path: '/login', component: () => import('../views/Login.vue') }, { path: '/', component: () => import('../views/Layout.vue'), redirect: '/order', children: [ { path: 'order', component: () => import('../views/Order.vue') }, { path: 'product', component: () => import('../views/Product.vue') }, { path: 'stock', component: () => import('../views/Stock.vue') } ] } ] const router = createRouter({ history: createWebHistory(), routes }) // 全局前置守卫,没登录跳登录页 router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') if (to.path !== '/login' && !token) { next('/login') } else { next() } }) export default router

createWebHistory是 history 模式,打包部署到 Spring Boot 后刷新页面会 404,解决办法见第 6 章。开发阶段用这个模式没问题。

4.3 Axios 封装与接口调用

所有请求走一个request.js,统一加 token、统一处理错误码:

import axios from 'axios' import { ElMessage } from 'element-plus' const request = axios.create({ baseURL: '/api', // 开发时配合 vite 代理 timeout: 5000 }) // 请求拦截器,带上 token request.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers.Authorization = token } return config }) // 响应拦截器,code 非 200 弹提示 request.interceptors.response.use( res => { if (res.data.code !== 200) { ElMessage.error(res.data.msg || '请求失败') return Promise.reject(res.data) } return res.data.data }, err => { ElMessage.error('网络异常') return Promise.reject(err) } ) export default request

vite.config.js里配代理,解决开发跨域:

export default defineConfig({ server: { proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } })

这样前端写request.get('/order/list')实际请求的是http://localhost:8080/api/order/list,和后端接口路径对上。参数上changeOrigin: true必须加,否则后端拿到的 Host 还是前端地址,某些鉴权逻辑会出错。

5. 避坑与排查:这套技术栈最容易翻车的五个地方

5.1 现象:启动报Access denied for user 'root'@'localhost'

原因:application.yml里的密码和本地 MySQL 实际密码不一致,或者 MySQL 8 的 root 用户默认用caching_sha2_password插件,旧驱动连不上。解决:先确认密码,再用ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '新密码';改插件,最后FLUSH PRIVILEGES;。驱动版本用 8.0.33 以上基本不用改插件,但 5.x 驱动必改。

5.2 现象:前端请求返回 404,但浏览器 Network 里 URL 是对的

原因:Vite 代理没生效,或者后端接口路径多了/少了/api前缀。解决:检查vite.config.js的proxy配置是否在server下,改完必须重启npm run dev,热更新不会重载代理配置。另外确认 Controller 上的@RequestMapping和前端baseURL拼起来是否一致。

5.3 现象:订单金额统计出现小数位异常

原因:金额字段用了FLOAT或DOUBLE。解决:建表时改成DECIMAL(10,2),Java 里用BigDecimal,且BigDecimal比较用compareTo不用equals。这个坑我在两个项目里都踩过,改表结构最彻底。

5.4 现象:Vue 打包后放 Spring Boot 里,刷新页面 404

原因:Vue Router 用了 history 模式,Spring Boot 没有对应的转发规则。解决:在 Spring Boot 里加一个配置,把所有非/api开头的请求转发到index.html:

@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/**") .addResourceLocations("classpath:/static/"); } }

同时前端路由改成createWebHashHistory最省事,URL 带#,刷新不会 404。如果坚持 history 模式,需要额外写ErrorController或 Nginx 的try_files。

5.5 现象:MyBatis-Plus 查询返回字段全是 null

原因:数据库字段是下划线命名(create_time),Java 实体是驼峰(createTime),但没开自动映射。解决:application.yml里加mybatis-plus.configuration.map-underscore-to-camel-case: true,或者实体字段上加@TableField("create_time")。前者一劳永逸。

6. 打包部署与一个提效技巧:把 Vue 塞进 Spring Boot 的 jar

6.1 前端打包与静态资源合并

前端npm run build后生成dist目录,里面是index.html和assets。把这俩直接复制到 Spring Boot 的src/main/resources/static/下,Spring Boot 默认就会把static当静态资源根目录。注意vite.config.js里要设base: './',否则打包后资源路径是绝对路径/assets/xxx.js,放子目录会 404。

// vite.config.js 生产环境配置 export default defineConfig({ base: './', build: { outDir: 'dist', assetsDir: 'assets' } })

6.2 用 Maven 插件一键打包

在pom.xml里加spring-boot-maven-plugin,然后mvn clean package生成可执行 jar。启动命令java -jar coffee-admin-0.0.1-SNAPSHOT.jar,浏览器访问http://localhost:8080就能看到前端页面。这里有个验证方法:打包后先本地跑一遍,点单、加商品、看库存,全流程走通再交付。

6.3 一个我常用的提效习惯

每次改完数据库字段,我会先跑一句SHOW CREATE TABLE 表名;把最新结构贴到项目根目录的db.md里,再同步改实体类和前端表单。这个习惯帮我省掉了至少三次「字段对不上导致接口 500」的排查时间。另外,接口联调阶段我会在 Postman 里存一份集合,前端报错时先跑 Postman,能快速定位是后端问题还是前端传参问题。这套 Spring Boot + MySQL + Vue 的组合,难点从来不是写代码,而是把三层的约定对齐——表结构、接口路径、字段命名,对齐了,剩下的就是体力活。希望帮到你。

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

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

滞回与窗口比较器实战:阈值抖动与抗干扰设计全解析

我有一次给一条生产线的电机做过温保护改造&#xff0c;用的就是最普通的单限比较器。采样电路、基准电压都调得好好的&#xff0c;结果一通电&#xff0c;继电器在温度接近设定值的时候开始“哒哒哒”地疯狂抖动&#xff0c;触点火花四溅。后来用示波器一抓&#xff0c;发现温…

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

期货策略参数外部化:从回测到实盘的配置管理实战

1. 为什么要把策略参数从代码里拆出来&#xff1a;回测与实盘的隐性痛点做期货量化时间稍长的人&#xff0c;大概率会遇到这么个场景&#xff1a;策略在回测里跑得干干净净&#xff0c;净值曲线看着也挺顺眼&#xff0c;可一旦准备切到实盘&#xff0c;心里就开始打鼓——手续费…

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

黄山市12.5米DEM数据处理全流程:从解压校验到坡度提取与避坑指南

简介&#xff1a;这份资源面向GIS从业者、地形分析研究者及城市规划相关人员&#xff0c;提供安徽省黄山市12.5米分辨率数字高程模型数据&#xff0c;并附带市级行政范围矢量边界&#xff0c;可支撑地形分析、洪水模拟、环境评估与工程设计等需要精细高程信息的场景。压缩包共1…

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

智能家居毕设实战:Java+SpringBoot+Vue+MySQL源码跑通与避坑指南

简介&#xff1a;这是一套面向高校计算机相关专业学生的智能家居系统毕业设计完整资料包&#xff0c;基于Java语言开发&#xff0c;采用SpringBoot、Vue与MySQL技术栈&#xff0c;适合用作毕设、课程设计或期末大作业&#xff0c;下载后无需修改即可运行。压缩包共369个文件&am…

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

OFDM峰均比问题根源与降PAPR方案工程实践对比

做OFDM系统的人&#xff0c;几乎都被PAPR这个指标教育过。无论是实验室里用信号分析仪抓OFDM时域波形&#xff0c;还是在芯片验证阶段盯着功放电流找原因&#xff0c;最后都会回到同一个问题&#xff1a;多载波叠加出来的时域包络怎么就这么不平坦。拿一段很普通的16QAM-OFDM信…

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

SpringBoot+Vue小区物业系统:毕设高分工程实践指南

简介&#xff1a;这是一套面向计算机专业本科生的高分毕业设计实战资源&#xff0c;基于SpringBoot后端与Vue前端构建的小区物业管理系统&#xff0c;适用于毕设开发、课程大作业及Java全栈项目练手。资源包含完整可运行源码、配套MySQL数据库脚本及详细部署说明&#xff0c;所…

作者头像 李华