很多同学在做毕业设计时,面对一个完整的项目需求常常感到无从下手,尤其是需要整合前后端技术栈时。本文将手把手带你从零开始,完成一个功能完整、代码规范、可直接运行的“鲜享超市管理系统”。这个项目采用主流的SpringBoot后端和Vue前端,涵盖了用户管理、商品管理、订单处理等核心业务模块。无论你是Java初学者,还是对Vue不太熟悉,通过本文的详细拆解和完整源码,你都能一步步搭建起来,轻松搞定毕设,同时掌握企业级项目开发的核心流程。
1. 项目背景与核心概念
1.1 什么是超市管理系统?
超市管理系统是一个典型的企业级信息管理系统(MIS),它通过计算机技术对超市的日常运营活动,如商品进货、销售、库存、员工及会员信息等进行统一、高效的管理。其核心目标是替代传统的手工记账模式,减少人为错误,提高运营效率,并为管理者提供数据支持以辅助决策。
1.2 为什么选择 SpringBoot + Vue 技术栈?
这是一个非常经典且高效的“前后端分离”架构组合,在当前的Web开发中占据主流地位。
- SpringBoot (后端):作为Java领域最流行的微服务框架,它极大地简化了Spring应用的初始搭建和开发过程。通过“约定大于配置”的理念和丰富的Starter依赖,开发者可以快速构建独立运行、生产级别的应用,无需过多关注繁琐的XML配置。它内置了Tomcat服务器,并完美集成了MyBatis、Spring Security等常用组件。
- Vue.js (前端):是一套用于构建用户界面的渐进式JavaScript框架。它易于上手,核心库只关注视图层,并且拥有非常活跃的生态系统(如Vue Router、Vuex、Element UI)。Vue的数据驱动和组件化开发模式,使得构建复杂、交互丰富的前端应用变得清晰且高效。
- 前后端分离的优势:前后端通过RESTful API接口进行数据交互,职责清晰。后端专注于业务逻辑、数据安全和接口提供;前端专注于页面渲染和用户体验。这种模式有利于并行开发、独立部署和后期维护。
1.3 鲜享超市管理系统功能概述
我们的项目将实现一个基础但功能完备的超市管理后台,主要包含以下模块:
- 系统管理:用户登录/注销、角色权限管理(如管理员、普通员工)。
- 商品管理:商品的增删改查、分类管理、库存数量与预警。
- 采购管理:记录商品采购入库信息,关联供应商。
- 销售管理:前台收银模拟,生成销售订单,更新库存。
- 会员管理:会员信息注册、积分管理。
- 数据统计:简单的销售报表、商品销量排行等可视化图表。
2. 环境准备与版本说明
在开始编码之前,请确保你的开发环境已就绪。以下是本文示例所使用的环境,你可以根据实际情况进行调整。
2.1 后端开发环境
- 操作系统:Windows 10/11, macOS 或 Linux (本文命令以Windows为例)
- JDK:Java 17 (推荐) 或 Java 8。确保
java -version命令能正确输出。 - 构建工具:Apache Maven 3.6+。确保
mvn -v命令能正确输出。 - 集成开发环境 (IDE):IntelliJ IDEA (社区版或旗舰版) 或 Eclipse。IDEA对SpringBoot支持更好,强烈推荐。
- 数据库:MySQL 5.7 或 8.0。本文使用 MySQL 8.0。
- 数据库管理工具:Navicat, DBeaver 或 IDEA 自带的数据库工具。
2.2 前端开发环境
- Node.js:版本 16.x 或 18.x (LTS版本)。这是运行Vue和npm的基础。安装后可通过
node -v和npm -v检查。 - 包管理工具:npm (随Node.js安装) 或 yarn。
- 前端IDE:Visual Studio Code (VSCode) 或 WebStorm。VSCode轻量且插件丰富,是前端开发首选。
- Vue脚手架:Vue CLI 或 Vite。本文使用更现代的 Vite 作为构建工具。
2.3 项目技术栈版本明细
为了确保依赖兼容性,以下是核心依赖的版本,你可以在创建项目时参考。
后端 (SpringBoot) 主要依赖:
<!-- 在 pom.xml 中 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> <!-- 选择一个稳定的版本 --> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency> <!-- 使用 MyBatis-Plus 简化数据库操作 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> </dependency> <!-- MySQL 驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>前端 (Vue 3) 主要依赖:通过Vite创建项目时选择 Vue 3 和 TypeScript/JavaScript。我们将使用 Element Plus 作为UI组件库。
# 项目创建后,安装核心依赖 npm install element-plus --save npm install axios --save # 用于HTTP请求 npm install vue-router@4 --save # 路由 npm install pinia --save # 状态管理,替代Vuex3. 项目初始化与数据库设计
3.1 创建SpringBoot后端项目
- 打开 IntelliJ IDEA,选择
File -> New -> Project。 - 选择
Spring Initializr,填写项目信息:- Name: supermarket-manage
- Location: 你的项目存放路径
- Type: Maven
- Language: Java
- Group: com.fresh
- Artifact: supermarket
- Package name: com.fresh.supermarket
- Java: 17
- 在
Dependencies中,勾选Spring Web,Spring Data JDBC,MySQL Driver,Lombok。其他依赖我们稍后在pom.xml中手动添加。 - 点击
Finish,IDEA会自动生成项目并下载依赖。
3.2 设计数据库表结构
在MySQL中创建一个名为fresh_supermarket的数据库。以下是几个核心表的设计示例:
用户表 (sys_user):
CREATE TABLE `sys_user` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键', `username` varchar(50) NOT NULL COMMENT '用户名', `password` varchar(100) NOT NULL COMMENT '密码', `nickname` varchar(50) DEFAULT NULL COMMENT '昵称', `email` varchar(100) DEFAULT NULL COMMENT '邮箱', `phone` varchar(20) DEFAULT NULL COMMENT '手机号', `avatar` varchar(200) DEFAULT NULL COMMENT '头像', `status` tinyint DEFAULT '1' COMMENT '状态(1:正常,0:禁用)', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', PRIMARY KEY (`id`), UNIQUE KEY `uk_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户表';商品表 (product):
CREATE TABLE `product` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT '商品ID', `product_code` varchar(50) NOT NULL COMMENT '商品编码', `product_name` varchar(100) NOT NULL COMMENT '商品名称', `category_id` bigint DEFAULT NULL COMMENT '分类ID', `price` decimal(10,2) NOT NULL COMMENT '售价', `cost_price` decimal(10,2) DEFAULT NULL COMMENT '成本价', `stock` int NOT NULL DEFAULT '0' COMMENT '库存', `stock_warn` int DEFAULT '10' COMMENT '库存预警值', `unit` varchar(20) DEFAULT NULL COMMENT '单位(如:瓶,袋)', `image` varchar(200) DEFAULT NULL COMMENT '商品图片', `description` text COMMENT '商品描述', `status` tinyint DEFAULT '1' COMMENT '状态(1:上架,0:下架)', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_product_code` (`product_code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='商品表';商品分类表 (category)、订单表 (order)、订单详情表 (order_item)、会员表 (member)等表结构类似,可根据业务逻辑扩展。建议先画出E-R图理清关系。
3.3 配置后端项目
- 添加 MyBatis-Plus 依赖:将上一节
pom.xml中的mybatis-plus-boot-starter依赖添加到项目的pom.xml文件中。 - 配置数据库连接:编辑
src/main/resources/application.yml文件。
server: port: 8080 # 后端服务端口 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/fresh_supermarket?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai username: root # 你的数据库用户名 password: 123456 # 你的数据库密码 jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 # MyBatis-Plus 配置 mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台打印SQL,生产环境关闭 global-config: db-config: id-type: auto # 主键自增 logic-delete-field: deleted # 全局逻辑删除字段名(如果表中有此字段) logic-delete-value: 1 # 逻辑已删除值 logic-not-delete-value: 0 # 逻辑未删除值 mapper-locations: classpath*:/mapper/**/*.xml # XML映射文件位置4. 后端核心功能开发
我们以商品管理模块为例,演示完整的后端开发流程。
4.1 创建实体类 (Entity)
在com.fresh.supermarket.entity包下创建Product.java。
package com.fresh.supermarket.entity; import com.baomidou.mybatisplus.annotation.*; import lombok.Data; import java.math.BigDecimal; import java.time.LocalDateTime; @Data @TableName("product") // 对应数据库表名 public class Product { @TableId(type = IdType.AUTO) // 主键自增 private Long id; private String productCode; private String productName; private Long categoryId; private BigDecimal price; private BigDecimal costPrice; private Integer stock; private Integer stockWarn; private String unit; private String image; private String description; private Integer status; @TableField(fill = FieldFill.INSERT) // 插入时自动填充 private LocalDateTime createTime; @TableField(fill = FieldFill.INSERT_UPDATE) // 插入和更新时自动填充 private LocalDateTime updateTime; }4.2 创建Mapper接口
在com.fresh.supermarket.mapper包下创建ProductMapper.java。MyBatis-Plus 提供了强大的通用 Mapper,无需编写 XML。
package com.fresh.supermarket.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.fresh.supermarket.entity.Product; import org.apache.ibatis.annotations.Mapper; @Mapper public interface ProductMapper extends BaseMapper<Product> { // 继承 BaseMapper 后,基本的 CRUD 方法已自动实现 // 如需复杂查询,可在此定义方法,并在对应 XML 中实现 }4.3 创建服务层接口和实现
接口:com.fresh.supermarket.service.ProductService.java
package com.fresh.supermarket.service; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.IService; import com.fresh.supermarket.entity.Product; import com.fresh.supermarket.vo.ProductQueryVO; public interface ProductService extends IService<Product> { // 分页条件查询商品 Page<Product> pageQuery(Page<Product> page, ProductQueryVO queryVO); }实现类:com.fresh.supermarket.service.impl.ProductServiceImpl.java
package com.fresh.supermarket.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.fresh.supermarket.entity.Product; import com.fresh.supermarket.mapper.ProductMapper; import com.fresh.supermarket.service.ProductService; import com.fresh.supermarket.vo.ProductQueryVO; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; @Service public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService { @Override public Page<Product> pageQuery(Page<Product> page, ProductQueryVO queryVO) { LambdaQueryWrapper<Product> wrapper = new LambdaQueryWrapper<>(); // 根据查询条件动态构建SQL if (StringUtils.isNotBlank(queryVO.getProductName())) { wrapper.like(Product::getProductName, queryVO.getProductName()); } if (queryVO.getCategoryId() != null) { wrapper.eq(Product::getCategoryId, queryVO.getCategoryId()); } if (queryVO.getStatus() != null) { wrapper.eq(Product::getStatus, queryVO.getStatus()); } // 按创建时间倒序 wrapper.orderByDesc(Product::getCreateTime); return this.page(page, wrapper); } }4.4 创建控制器 (Controller)
在com.fresh.supermarket.controller包下创建ProductController.java,提供 RESTful API。
package com.fresh.supermarket.controller; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.fresh.supermarket.common.Result; import com.fresh.supermarket.entity.Product; import com.fresh.supermarket.service.ProductService; import com.fresh.supermarket.vo.ProductQueryVO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/api/product") public class ProductController { @Autowired private ProductService productService; // 新增商品 @PostMapping public Result save(@RequestBody Product product) { boolean success = productService.save(product); return success ? Result.success("新增成功") : Result.error("新增失败"); } // 根据ID删除商品 @DeleteMapping("/{id}") public Result delete(@PathVariable Long id) { boolean success = productService.removeById(id); return success ? Result.success("删除成功") : Result.error("删除失败"); } // 更新商品信息 @PutMapping public Result update(@RequestBody Product product) { boolean success = productService.updateById(product); return success ? Result.success("更新成功") : Result.error("更新失败"); } // 根据ID查询商品详情 @GetMapping("/{id}") public Result getById(@PathVariable Long id) { Product product = productService.getById(id); return Result.success(product); } // 分页条件查询商品列表 @GetMapping("/page") public Result page(@RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "10") Integer pageSize, ProductQueryVO queryVO) { Page<Product> page = new Page<>(pageNum, pageSize); Page<Product> resultPage = productService.pageQuery(page, queryVO); return Result.success(resultPage); } }其中,Result是一个统一返回结果封装类,ProductQueryVO是查询条件视图对象,需要自行创建。
4.5 运行与测试后端API
- 在IDEA中,找到主启动类
SupermarketApplication,运行main方法。 - 控制台看到
Tomcat started on port(s): 8080即启动成功。 - 使用Postman或Apifox等工具测试接口。
- GET
http://localhost:8080/api/product/page?pageNum=1&pageSize=10 - POST
http://localhost:8080/api/product(Body: JSON格式的商品数据) - 测试增删改查接口是否正常。
- GET
5. 前端Vue项目开发
5.1 创建Vue项目并安装依赖
打开终端(如VSCode的终端),执行以下命令:
# 使用 Vite 创建 Vue 项目 npm create vue@latest # 按照提示操作,项目名设为 supermarket-frontend # 选择 Vue, TypeScript(可选),不选其他默认配置即可。 cd supermarket-frontend # 安装核心依赖 npm install npm install element-plus axios vue-router@4 pinia # 安装 Element Plus 图标库(可选) npm install @element-plus/icons-vue # 启动开发服务器 npm run dev访问http://localhost:5173看到Vue欢迎页即成功。
5.2 配置路由和状态管理
- 路由配置:在
src/router/index.ts中配置页面路由。
import { createRouter, createWebHistory } from 'vue-router' import Login from '../views/Login.vue' import Layout from '../layout/Index.vue' import ProductList from '../views/product/List.vue' const routes = [ { path: '/login', name: 'Login', component: Login }, { path: '/', component: Layout, redirect: '/product', children: [ { path: '/product', name: 'Product', component: ProductList }, // ... 其他路由,如会员管理、订单管理等 ] } ] const router = createRouter({ history: createWebHistory(), routes }) export default router- 状态管理 (Pinia):创建用户状态存储
src/stores/user.ts,用于管理登录状态、token等。 - 全局配置:在
src/main.ts中引入 Element Plus 和 路由。
import { createApp } from 'vue' import ElementPlus from 'element-plus' import 'element-plus/dist/index.css' import * as ElementPlusIconsVue from '@element-plus/icons-vue' import App from './App.vue' import router from './router' import { createPinia } from 'pinia' const app = createApp(App) app.use(createPinia()) app.use(router) app.use(ElementPlus) // 注册图标组件 for (const [key, component] of Object.entries(ElementPlusIconsVue)) { app.component(key, component) } app.mount('#app')5.3 开发商品列表页面
创建src/views/product/List.vue,实现商品的分页查询和表格展示。
<template> <div class="product-container"> <el-card> <!-- 搜索区域 --> <el-form :inline="true" :model="queryParams" class="demo-form-inline"> <el-form-item label="商品名称"> <el-input v-model="queryParams.productName" placeholder="请输入商品名称" clearable /> </el-form-item> <el-form-item label="商品状态"> <el-select v-model="queryParams.status" placeholder="请选择" clearable> <el-option label="上架" :value="1" /> <el-option label="下架" :value="0" /> </el-select> </el-form-item> <el-form-item> <el-button type="primary" @click="handleQuery">查询</el-button> <el-button @click="resetQuery">重置</el-button> <el-button type="success" @click="handleAdd">新增商品</el-button> </el-form-item> </el-form> <!-- 商品表格 --> <el-table :data="tableData" border style="width: 100%"> <el-table-column prop="productCode" label="商品编码" width="120" /> <el-table-column prop="productName" label="商品名称" /> <el-table-column prop="price" label="售价(元)" width="100" /> <el-table-column prop="stock" label="库存" width="80" /> <el-table-column prop="status" label="状态" width="80"> <template #default="scope"> <el-tag :type="scope.row.status === 1 ? 'success' : 'info'"> {{ scope.row.status === 1 ? '上架' : '下架' }} </el-tag> </template> </el-table-column> <el-table-column prop="createTime" label="创建时间" width="180" /> <el-table-column label="操作" width="200" fixed="right"> <template #default="scope"> <el-button size="small" @click="handleEdit(scope.row)">编辑</el-button> <el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button> </template> </el-table-column> </el-table> <!-- 分页组件 --> <div class="pagination-container"> <el-pagination v-model:current-page="queryParams.pageNum" v-model:page-size="queryParams.pageSize" :page-sizes="[10, 20, 50, 100]" :total="total" layout="total, sizes, prev, pager, next, jumper" @size-change="handleSizeChange" @current-change="handleCurrentChange" /> </div> </el-card> <!-- 新增/编辑商品对话框 --> <ProductDialog ref="productDialogRef" @refresh="getList" /> </div> </template> <script setup lang="ts"> import { ref, reactive, onMounted } from 'vue' import { ElMessage, ElMessageBox } from 'element-plus' import ProductDialog from './components/ProductDialog.vue' import { getProductPage, deleteProduct } from '@/api/product' // 表格数据 const tableData = ref([]) const total = ref(0) // 查询参数 const queryParams = reactive({ pageNum: 1, pageSize: 10, productName: '', status: null }) // 获取商品列表 const getList = async () => { try { const res = await getProductPage(queryParams) tableData.value = res.data.records total.value = res.data.total } catch (error) { console.error('获取商品列表失败:', error) } } // 查询 const handleQuery = () => { queryParams.pageNum = 1 getList() } // 重置查询 const resetQuery = () => { queryParams.productName = '' queryParams.status = null handleQuery() } // 分页大小改变 const handleSizeChange = (val: number) => { queryParams.pageSize = val getList() } // 当前页改变 const handleCurrentChange = (val: number) => { queryParams.pageNum = val getList() } // 新增商品 const productDialogRef = ref() const handleAdd = () => { productDialogRef.value.open() } // 编辑商品 const handleEdit = (row: any) => { productDialogRef.value.open(row) } // 删除商品 const handleDelete = (row: any) => { ElMessageBox.confirm('确认删除该商品吗?', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(async () => { await deleteProduct(row.id) ElMessage.success('删除成功') getList() }).catch(() => {}) } // 初始化 onMounted(() => { getList() }) </script> <style scoped> .pagination-container { margin-top: 20px; display: flex; justify-content: flex-end; } </style>对应的API请求层src/api/product.ts需要封装axios。
import request from '@/utils/request' import type { ProductQueryVO } from '@/types/product' // 分页查询商品 export function getProductPage(params: ProductQueryVO) { return request({ url: '/api/product/page', method: 'get', params }) } // 根据ID删除商品 export function deleteProduct(id: number) { return request({ url: `/api/product/${id}`, method: 'delete' }) } // 新增商品 export function addProduct(data: any) { return request({ url: '/api/product', method: 'post', data }) } // 更新商品 export function updateProduct(data: any) { return request({ url: '/api/product', method: 'put', data }) }6. 前后端联调与跨域处理
当前后端运行在不同端口(前端5173,后端8080)时,浏览器会因同源策略阻止请求,需要解决跨域问题。
6.1 后端解决跨域 (推荐)
在SpringBoot后端添加一个全局跨域配置类。
package com.fresh.supermarket.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; @Configuration public class CorsConfig { @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); // 允许所有域名进行跨域调用,生产环境应指定具体前端地址 config.addAllowedOriginPattern("*"); // 允许跨越发送cookie config.setAllowCredentials(true); // 放行全部原始头信息 config.addAllowedHeader("*"); // 允许所有请求方法跨域调用(GET, POST, PUT, DELETE等) config.addAllowedMethod("*"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } }6.2 前端配置代理 (开发环境)
在Vue项目的vite.config.ts中配置代理,将API请求转发到后端。
import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], server: { port: 5173, proxy: { '/api': { target: 'http://localhost:8080', // 后端地址 changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, '') // 根据后端路径决定是否重写 } } } })配置后,前端发起的/api/product/page请求会被代理到http://localhost:8080/api/product/page。
6.3 联调测试
- 确保后端SpringBoot应用在8080端口运行。
- 前端运行
npm run dev。 - 在前端页面操作(如点击查询),观察浏览器开发者工具(F12)的“网络(Network)”标签页,查看请求是否成功,数据是否正确返回。
7. 常见问题与排查思路
在开发过程中,你可能会遇到以下典型问题:
| 问题现象 | 可能原因 | 排查思路与解决方案 |
|---|---|---|
| 后端启动失败,端口被占用 | 8080端口已被其他程序(如另一个SpringBoot应用、Tomcat)使用。 | 1. 在application.yml中修改server.port。2. 命令行执行 netstat -ano | findstr :8080(Windows) 或lsof -i:8080(Mac/Linux) 找到进程并结束。 |
前端npm run dev报错,端口被占用 | 5173端口被占用。 | 1. 在vite.config.ts中修改server.port。2. 同样使用命令查找占用进程。 |
| 数据库连接失败 | 1. MySQL服务未启动。 2. application.yml中数据库连接信息(URL、用户名、密码)错误。3. 数据库驱动版本不匹配。 | 1. 检查MySQL服务状态并启动。 2. 仔细核对配置,注意时区参数 serverTimezone。3. 确认 pom.xml中MySQL驱动版本与安装的MySQL版本兼容。 |
| 前端请求后端API返回404 | 1. 后端Controller路径映射错误。 2. 跨域未配置或代理配置错误。 3. 后端服务未启动。 | 1. 检查Controller的@RequestMapping和方法的@GetMapping等注解路径。2. 检查后端CorsConfig是否生效,或前端代理配置是否正确。 3. 确认后端控制台无报错且已成功启动。 |
| 前端页面空白,控制台JS报错 | 1. 组件引入路径错误。 2. Vue/Element Plus版本兼容性问题。 3. TypeScript类型错误。 | 1. 检查import语句路径是否正确。 2. 检查 package.json依赖版本,尝试重新安装node_modules(rm -rf node_modules && npm install)。3. 根据控制台错误信息定位代码。 |
| MyBatis-Plus 插入/更新时,自动填充时间字段不生效 | 未配置元对象处理器。 | 创建一个配置类实现MetaObjectHandler接口,重写insertFill和updateFill方法,并在实体类字段上使用@TableField(fill = FieldFill.INSERT)等注解。 |
| 前端表格数据不显示 | 1. API请求未成功,数据未获取到。 2. 表格绑定的 prop与接口返回字段名不一致。3. 响应数据结构与预期不符。 | 1. 在浏览器开发者工具“网络”中查看API请求响应。 2. 核对表格列 prop与接口返回的字段名。3. 检查后端 Result封装类结构,前端axios响应拦截器是否正确提取了data。 |
8. 项目部署与上线建议
8.1 后端打包与运行
- 打包:在项目根目录执行
mvn clean package -DskipTests,会在target目录生成supermarket-0.0.1-SNAPSHOT.jar。 - 运行:将jar包上传至服务器,使用命令
java -jar supermarket-0.0.1-SNAPSHOT.jar运行。可使用nohup或配置为系统服务保持后台运行。 - 生产环境配置:创建
application-prod.yml,覆盖数据库连接、日志级别等配置,运行时可指定激活该配置文件:java -jar supermarket-0.0.1-SNAPSHOT.jar --spring.profiles.active=prod。
8.2 前端打包与部署
- 打包:在前端项目根目录执行
npm run build,生成静态文件在dist目录。 - 部署:将
dist目录下的所有文件放置到任何静态Web服务器中,如Nginx、Apache,或SpringBoot项目的static目录。 - Nginx配置示例:
server { listen 80; server_name your-domain.com; # 你的域名或IP location / { root /path/to/your/dist; # dist目录路径 index index.html index.htm; try_files $uri $uri/ /index.html; # 支持Vue Router的history模式 } # 代理后端API请求 location /api/ { proxy_pass http://localhost:8080/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }8.3 安全与性能最佳实践
- 接口安全:
- 身份认证:集成Spring Security + JWT,为API接口添加登录校验。
- 权限控制:实现基于角色的访问控制(RBAC),使用注解如
@PreAuthorize("hasRole('ADMIN')")控制接口访问。 - 参数校验:使用
@Validated和@NotBlank、@Size等注解对入参进行校验。 - SQL防注入:坚持使用MyBatis-Plus的Wrapper或
#{}预编译,严禁字符串拼接SQL。
- 代码规范:
- 分层清晰:严格遵循Controller -> Service -> Mapper的分层架构,各司其职。
- 统一响应:使用
Result类统一封装所有接口的返回格式。 - 全局异常处理:使用
@ControllerAdvice和@ExceptionHandler捕获并处理异常,返回友好的错误信息。 - 日志记录:使用SLF4J + Logback记录关键业务日志、错误日志,便于排查问题。
- 数据库优化:
- 索引:为高频查询条件(如
product_code,product_name)和关联字段建立索引。 - 字段设计:选择合适的数据类型,避免使用
text类型作为查询条件。 - 事务管理:在涉及多个写操作(如创建订单同时扣减库存)的方法上添加
@Transactional注解,保证数据一致性。
- 索引:为高频查询条件(如
- 前端优化:
- API封装:所有HTTP请求应通过统一的axios实例发出,便于添加请求/响应拦截器(处理token、错误等)。
- 组件复用:将可复用的UI和逻辑抽离成组件。
- 路由懒加载:使用
() => import('...')语法实现路由懒加载,提升首屏加载速度。 - 构建优化:合理配置Vite的打包策略,对第三方库使用CDN。
通过以上步骤,你已经完成了一个SpringBoot+Vue超市管理系统从零到有的核心开发流程。这个项目麻雀虽小,五脏俱全,涵盖了企业级应用开发的主要环节。你可以在此基础上继续扩展会员、订单、统计报表等功能模块,并引入更高级的技术如Redis缓存、Elasticsearch搜索、WebSocket消息通知等,使其成为一个更加丰满的毕业设计作品。