SpringBoot+Vue超市管理系统开发实战:从零构建完整项目

📅 2026/7/19 6:07:00
SpringBoot+Vue超市管理系统开发实战:从零构建完整项目
超市管理系统作为企业日常运营的核心工具在传统模式下常常面临数据同步不及时、库存管理混乱、销售统计效率低下等痛点。本文将基于SpringBootVue技术栈从零开始构建一个功能完整的鲜享超市管理系统涵盖商品管理、库存监控、销售统计、会员管理等核心模块提供可运行的完整代码和详细配置说明适合毕业设计、简历项目实践及中小型超市信息化改造参考。1. 项目背景与技术选型1.1 超市管理系统核心需求分析现代超市管理系统需要处理商品进货、销售、库存、会员、收银等多个业务环节。传统Excel手工记录方式存在数据孤岛、统计滞后、易出错等问题。一个完整的管理系统应具备以下核心功能商品基础信息管理分类、定价、供应商库存实时监控与预警机制销售数据统计与分析报表会员积分与折扣管理收银台快速结账接口多角色权限控制管理员、收银员、采购员1.2 技术栈选择依据SpringBootVue前后端分离架构已成为现代Web开发的主流方案。SpringBoot简化了后端配置内置Tomcat服务器支持快速构建RESTful APIVue.js作为渐进式前端框架组件化开发体验优秀与Element UI结合可快速搭建管理界面。MySQL作为成熟稳定的关系型数据库完全满足超市业务的数据存储需求。技术版本选择原则SpringBoot 2.7.xLTS版本、Vue 3.0、Element Plus、MySQL 8.0这些组合具有长期支持、社区活跃、文档完善的特点适合学习和生产使用。2. 开发环境准备2.1 基础软件安装与配置后端开发环境JDK 1.8或11推荐OpenJDK 11Maven 3.6依赖管理IntelliJ IDEA或EclipseIDEMySQL 8.0数据库前端开发环境Node.js 14.0运行时Vue CLI 5.0脚手架VS Code或WebStorm编辑器2.2 数据库初始化创建数据库并导入初始表结构-- 创建数据库 CREATE DATABASE IF NOT EXISTS supermarket CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 使用数据库 USE supermarket; -- 商品分类表 CREATE TABLE product_category ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL COMMENT 分类名称, sort_order INT DEFAULT 0 COMMENT 排序, status TINYINT DEFAULT 1 COMMENT 状态0-禁用 1-启用, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ); -- 商品信息表 CREATE TABLE product ( id INT PRIMARY KEY AUTO_INCREMENT, category_id INT NOT NULL COMMENT 分类ID, name VARCHAR(100) NOT NULL COMMENT 商品名称, price DECIMAL(10,2) NOT NULL COMMENT 售价, cost DECIMAL(10,2) COMMENT 成本价, stock INT DEFAULT 0 COMMENT 库存数量, unit VARCHAR(20) COMMENT 单位, barcode VARCHAR(50) COMMENT 条形码, supplier_id INT COMMENT 供应商ID, status TINYINT DEFAULT 1 COMMENT 状态0-下架 1-上架, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (category_id) REFERENCES product_category(id) ); -- 插入测试数据 INSERT INTO product_category(name, sort_order) VALUES (饮料酒水, 1), (休闲食品, 2), (生鲜果蔬, 3), (日用百货, 4);3. SpringBoot后端工程搭建3.1 项目结构规划采用标准Maven多模块结构提高代码复用性和可维护性supermarket-backend/ ├── supermarket-common/ # 通用工具类 ├── supermarket-domain/ # 实体类 ├── supermarket-dao/ # 数据访问层 ├── supermarket-service/ # 业务逻辑层 ├── supermarket-web/ # Web控制层 └── pom.xml # 父POM3.2 核心依赖配置父pom.xml统一管理依赖版本?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 modelVersion4.0.0/modelVersion groupIdcom.supermarket/groupId artifactIdsupermarket-backend/artifactId version1.0.0/version packagingpom/packaging properties java.version11/java.version spring-boot.version2.7.18/spring-boot.version mybatis-plus.version3.5.3.1/mybatis-plus.version /properties dependencyManagement dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-dependencies/artifactId version${spring-boot.version}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement /projectweb模块pom.xml配置具体依赖dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version${mybatis-plus.version}/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency /dependencies3.3 数据库连接配置application.yml配置数据源和MyBatis-Plusspring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/supermarket?useUnicodetruecharacterEncodingutf8zeroDateTimeBehaviorconvertToNulluseSSLtrueserverTimezoneGMT%2B8 username: root password: 123456 jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8 mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: db-config: id-type: auto logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 04. 核心业务模块实现4.1 商品管理模块实体类设计domain模块Data TableName(product) public class Product { TableId(type IdType.AUTO) private Integer id; private Integer categoryId; private String name; private BigDecimal price; private BigDecimal cost; private Integer stock; private String unit; private String barcode; private Integer supplierId; private Integer status; TableField(fill FieldFill.INSERT) private LocalDateTime createTime; TableField(fill FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; TableField(exist false) private String categoryName; }数据访问层dao模块Mapper public interface ProductMapper extends BaseMapperProduct { Select(SELECT p.*, pc.name as categoryName FROM product p LEFT JOIN product_category pc ON p.category_id pc.id ${ew.customSqlSegment}) IPageProductVO selectProductPage(PageProductVO page, Param(Constants.WRAPPER) WrapperProductVO wrapper); }业务逻辑层service模块public interface ProductService extends IServiceProduct { PageResultProductVO getProductPage(ProductQuery query); boolean updateStock(Integer productId, Integer quantity); } Service public class ProductServiceImpl extends ServiceImplProductMapper, Product implements ProductService { Override public PageResultProductVO getProductPage(ProductQuery query) { PageProductVO page new Page(query.getPageNum(), query.getPageSize()); LambdaQueryWrapperProductVO wrapper new LambdaQueryWrapper(); wrapper.like(StringUtils.isNotBlank(query.getName()), ProductVO::getName, query.getName()); wrapper.eq(query.getCategoryId() ! null, ProductVO::getCategoryId, query.getCategoryId()); wrapper.eq(query.getStatus() ! null, ProductVO::getStatus, query.getStatus()); IPageProductVO result baseMapper.selectProductPage(page, wrapper); return new PageResult(result.getRecords(), result.getTotal()); } Override Transactional public boolean updateStock(Integer productId, Integer quantity) { if (quantity 0) return true; Product product getById(productId); if (product null) { throw new BusinessException(商品不存在); } int newStock product.getStock() quantity; if (newStock 0) { throw new BusinessException(库存不足); } return update().setSql(stock stock quantity).eq(id, productId).update(); } }Web控制层web模块RestController RequestMapping(/api/product) public class ProductController { Autowired private ProductService productService; GetMapping(/page) public ResultPageResultProductVO getProductPage(ProductQuery query) { return Result.success(productService.getProductPage(query)); } PostMapping public ResultString addProduct(Valid RequestBody Product product) { productService.save(product); return Result.success(添加成功); } PutMapping(/stock/{id}) public ResultString updateStock(PathVariable Integer id, RequestParam Integer quantity) { productService.updateStock(id, quantity); return Result.success(库存更新成功); } }4.2 库存管理模块实现库存预警和入库出库记录Entity Table(name inventory_log) public class InventoryLog { Id GeneratedValue(strategy GenerationType.IDENTITY) private Integer id; private Integer productId; private Integer quantity; // 正数表示入库负数表示出库 private String type; // 采购入库、销售出库、盘点调整 private String remark; private LocalDateTime createTime; // 关联商品信息 Transient private String productName; }库存预警服务Service public class InventoryAlertService { Value(${inventory.alert.threshold:10}) private Integer alertThreshold; Scheduled(cron 0 0 8 * * ?) // 每天8点执行 public void checkLowInventory() { ListProduct lowStockProducts productService.list( new LambdaQueryWrapperProduct() .lt(Product::getStock, alertThreshold) .eq(Product::getStatus, 1) ); if (!lowStockProducts.isEmpty()) { // 发送预警通知可集成邮件、短信等 sendAlertNotification(lowStockProducts); } } }5. Vue前端项目搭建5.1 前端工程初始化使用Vue CLI创建项目vue create supermarket-frontend cd supermarket-frontend vue add element-plus npm install axios vue-router4 vuex4项目结构规划src/ ├── api/ # 接口请求 ├── components/ # 通用组件 ├── views/ # 页面组件 ├── router/ # 路由配置 ├── store/ # 状态管理 ├── utils/ # 工具函数 └── assets/ # 静态资源5.2 前端路由配置router/index.jsimport { createRouter, createWebHistory } from vue-router const routes [ { path: /, redirect: /dashboard }, { path: /login, name: Login, component: () import(/views/Login.vue) }, { path: /dashboard, name: Dashboard, component: () import(/views/layout/Layout.vue), redirect: /dashboard/home, children: [ { path: home, name: Home, component: () import(/views/Home.vue) }, { path: product, name: Product, component: () import(/views/product/ProductList.vue) }, { path: inventory, name: Inventory, component: () import(/views/inventory/InventoryList.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 router5.3 商品管理页面实现ProductList.vue组件template div classproduct-container el-card template #header div classcard-header span商品管理/span el-button typeprimary clickhandleAdd新增商品/el-button /div /template !-- 搜索条件 -- el-form :modelqueryParams inline el-form-item label商品名称 el-input v-modelqueryParams.name placeholder请输入商品名称 clearable / /el-form-item el-form-item label商品分类 el-select v-modelqueryParams.categoryId placeholder请选择分类 clearable el-option v-forcategory in categoryList :keycategory.id :labelcategory.name :valuecategory.id / /el-select /el-form-item el-form-item el-button typeprimary clickloadData查询/el-button el-button clickresetQuery重置/el-button /el-form-item /el-form !-- 商品表格 -- el-table :datatableData v-loadingloading el-table-column propid labelID width60 / el-table-column propname label商品名称 min-width120 / el-table-column propcategoryName label分类 width100 / el-table-column propprice label售价 width100 template #defaultscope¥{{ scope.row.price }}/template /el-table-column el-table-column propstock label库存 width80 / el-table-column propstatus label状态 width80 template #defaultscope el-tag :typescope.row.status ? success : danger {{ scope.row.status ? 上架 : 下架 }} /el-tag /template /el-table-column el-table-column label操作 width200 fixedright template #defaultscope el-button sizesmall clickhandleEdit(scope.row)编辑/el-button el-button sizesmall typewarning clickhandleInventory(scope.row)库存调整/el-button el-button sizesmall typedanger clickhandleDelete(scope.row)删除/el-button /template /el-table-column /el-table !-- 分页 -- div classpagination-container el-pagination v-model:current-pagequeryParams.pageNum v-model:page-sizequeryParams.pageSize :totaltotal :page-sizes[10, 20, 50, 100] layouttotal, sizes, prev, pager, next, jumper size-changeloadData current-changeloadData / /div /el-card !-- 新增/编辑对话框 -- product-dialog v-modeldialogVisible :form-datacurrentRow successhandleDialogSuccess / /div /template script setup import { ref, onMounted } from vue import { ElMessage, ElMessageBox } from element-plus import { getProductPage, deleteProduct } from /api/product import { getCategoryList } from /api/category import ProductDialog from ./components/ProductDialog.vue const loading ref(false) const tableData ref([]) const categoryList ref([]) const total ref(0) const dialogVisible ref(false) const currentRow ref(null) const queryParams ref({ pageNum: 1, pageSize: 10, name: , categoryId: null, status: null }) // 加载数据 const loadData async () { loading.value true try { const res await getProductPage(queryParams.value) tableData.value res.data.list total.value res.data.total } catch (error) { ElMessage.error(数据加载失败) } finally { loading.value false } } // 加载分类数据 const loadCategoryList async () { try { const res await getCategoryList() categoryList.value res.data } catch (error) { console.error(分类加载失败, error) } } // 处理新增 const handleAdd () { currentRow.value null dialogVisible.value true } // 处理编辑 const handleEdit (row) { currentRow.value { ...row } dialogVisible.value true } // 处理库存调整 const handleInventory (row) { // 库存调整逻辑 } // 处理删除 const handleDelete async (row) { try { await ElMessageBox.confirm(确认删除该商品吗, 提示, { type: warning }) await deleteProduct(row.id) ElMessage.success(删除成功) loadData() } catch (error) { // 用户取消删除 } } // 对话框成功回调 const handleDialogSuccess () { dialogVisible.value false loadData() } // 重置查询条件 const resetQuery () { queryParams.value { pageNum: 1, pageSize: 10, name: , categoryId: null, status: null } loadData() } onMounted(() { loadData() loadCategoryList() }) /script6. 前后端数据交互配置6.1 Axios请求封装utils/request.jsimport axios from axios import { ElMessage } from element-plus import router from /router // 创建axios实例 const service axios.create({ baseURL: process.env.VUE_APP_BASE_API, timeout: 10000 }) // 请求拦截器 service.interceptors.request.use( config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer token } return config }, error { return Promise.reject(error) } ) // 响应拦截器 service.interceptors.response.use( response { const res response.data if (res.code ! 200) { ElMessage.error(res.message || Error) // token过期处理 if (res.code 401) { localStorage.removeItem(token) router.push(/login) } return Promise.reject(new Error(res.message || Error)) } else { return res } }, error { ElMessage.error(error.message) return Promise.reject(error) } ) export default service6.2 API接口统一管理api/product.jsimport request from /utils/request export function getProductPage(params) { return request({ url: /api/product/page, method: get, params }) } export function addProduct(data) { return request({ url: /api/product, method: post, data }) } export function updateProduct(data) { return request({ url: /api/product, method: put, data }) } export function deleteProduct(id) { return request({ url: /api/product/${id}, method: delete }) }7. 系统部署与运维7.1 后端项目打包部署使用Maven打包SpringBoot应用mvn clean package -Dmaven.test.skiptrue生成的可执行JAR包包含内嵌Tomcat服务器直接运行java -jar supermarket-web-1.0.0.jar --spring.profiles.activeprod生产环境配置文件application-prod.ymlserver: port: 8080 servlet: context-path: /api spring: datasource: url: jdbc:mysql://生产数据库IP:3306/supermarket?useSSLfalse username: 生产用户名 password: 生产密码 logging: level: com.supermarket: INFO file: name: logs/supermarket.log7.2 前端项目构建部署构建生产版本npm run build配置Nginx反向代理server { listen 80; server_name your-domain.com; # 前端静态资源 location / { root /usr/share/nginx/html; index index.html index.htm; 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; } }8. 常见问题与解决方案8.1 开发环境问题排查问题1前端请求跨域错误解决方案后端配置CORS或使用代理Configuration public class CorsConfig { Bean public CorsFilter corsFilter() { CorsConfiguration config new CorsConfiguration(); config.addAllowedOriginPattern(*); config.addAllowedHeader(*); config.addAllowedMethod(*); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration(/**, config); return new CorsFilter(source); } }问题2MyBatis-Plus分页不生效解决方案添加分页插件配置Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }8.2 生产环境性能优化数据库优化建议为常用查询字段添加索引商品名称、分类ID、状态等定期清理历史数据使用分区表处理大量数据配置数据库连接池参数最大连接数、超时时间应用层优化建议使用Redis缓存热点数据商品分类、基础信息配置静态资源CDN加速开启Gzip压缩减少传输体积9. 项目扩展与进阶功能9.1 收银台模块实现收银台需要处理商品扫码、价格计算、支付集成等复杂逻辑Service public class CheckoutService { Transactional public CheckoutResult checkout(CheckoutRequest request) { // 1. 验证商品库存 ListCartItem cartItems validateStock(request.getItems()); // 2. 计算总金额考虑会员折扣 BigDecimal totalAmount calculateTotalAmount(cartItems, request.getMemberId()); // 3. 生成订单记录 Order order createOrder(cartItems, totalAmount, request); // 4. 扣减库存 updateInventory(cartItems); // 5. 记录销售流水 recordSales(order); return new CheckoutResult(order, totalAmount); } }9.2 数据报表与分析基于销售数据生成各类统计报表Service public class ReportService { public SalesReport generateSalesReport(DateRange range) { // 销售趋势分析 ListSalesTrend trends salesMapper.getSalesTrend(range); // 商品销售排行 ListProductSalesRank ranks salesMapper.getProductSalesRank(range); // 会员消费分析 ListMemberConsumption consumption memberMapper.getMemberConsumption(range); return new SalesReport(trends, ranks, consumption); } }9.3 系统安全加固接口权限控制PreAuthorize(hasRole(ADMIN) or hasRole(CASHIER)) PostMapping(/checkout) public ResultCheckoutResult checkout(RequestBody CheckoutRequest request) { // 收银接口需要特定权限 }数据敏感信息保护密码使用BCrypt加密存储敏感操作记录审计日志定期更换数据库连接密码本项目完整源码包含数据库脚本、后端API接口、前端Vue组件、部署文档等材料可作为毕业设计答辩、个人技能展示、中小企业实际应用的参考方案。在实际部署时请根据具体业务需求调整功能模块特别注意数据备份和权限控制等生产环境要求。