ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

SSM+Vue.js网上订餐系统开发实战:前后端分离架构详解

SSM+Vue.js网上订餐系统开发实战:前后端分离架构详解 在开发网上订餐系统时很多初学者会面临技术栈选择困难、前后端分离架构不熟悉、数据库设计不合理等问题。本文将基于SSM框架Vue.js完整实现一个功能完备的网上订餐管理系统包含前端用户界面、后端业务逻辑和数据库设计提供可直接运行的代码示例和详细配置说明。1. 系统概述与技术选型网上订餐管理系统是一个典型的电子商务应用需要处理用户注册登录、菜品浏览、购物车管理、订单处理等核心业务。针对这些需求我们选择以下技术栈1.1 技术架构说明系统采用前后端分离架构后端使用SSM框架SpringSpring MVCMyBatis提供RESTful API接口前端使用Vue.js构建用户界面数据库采用MySQL存储业务数据。后端技术栈Spring Framework负责依赖注入和事务管理Spring MVC处理HTTP请求和响应MyBatis数据库ORM映射框架MySQL关系型数据库Maven项目构建和依赖管理前端技术栈Vue.js渐进式JavaScript框架Element UI基于Vue的组件库AxiosHTTP请求库Vue Router路由管理1.2 系统功能模块系统主要包含以下功能模块用户管理注册、登录、个人信息管理菜品管理菜品分类、菜品信息维护购物车管理添加商品、修改数量、清空购物车订单管理下单、支付、订单状态跟踪后台管理数据统计、系统配置2. 开发环境准备2.1 软件版本要求在开始开发前需要确保本地环境已安装以下软件后端环境JDK 1.8或更高版本Apache Maven 3.6MySQL 5.7或8.0版本Tomcat 8.5或使用Spring Boot内嵌容器前端环境Node.js 14.0npm 6.0或yarn包管理器Vue CLI 4.02.2 数据库环境配置首先创建数据库并配置连接信息-- 创建数据库 CREATE DATABASE IF NOT EXISTS food_order DEFAULT CHARACTER SET utf8mb4; -- 使用数据库 USE food_order; -- 创建用户表 CREATE TABLE user ( id int(11) NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL UNIQUE, password varchar(100) NOT NULL, email varchar(100) NOT NULL, phone varchar(20) DEFAULT NULL, create_time datetime DEFAULT CURRENT_TIMESTAMP, update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 创建菜品分类表 CREATE TABLE category ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(50) NOT NULL, description varchar(200) DEFAULT NULL, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 创建菜品表 CREATE TABLE dish ( id int(11) NOT NULL AUTO_INCREMENT, name varchar(100) NOT NULL, category_id int(11) NOT NULL, price decimal(10,2) NOT NULL, description text, image_url varchar(200) DEFAULT NULL, status tinyint(1) DEFAULT 1 COMMENT 1-上架,0-下架, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), FOREIGN KEY (category_id) REFERENCES category(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 创建订单表 CREATE TABLE order ( id int(11) NOT NULL AUTO_INCREMENT, order_no varchar(50) NOT NULL UNIQUE, user_id int(11) NOT NULL, total_amount decimal(10,2) NOT NULL, status tinyint(1) DEFAULT 0 COMMENT 0-待支付,1-已支付,2-已取消, create_time datetime DEFAULT CURRENT_TIMESTAMP, pay_time datetime DEFAULT NULL, PRIMARY KEY (id), FOREIGN KEY (user_id) REFERENCES user(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 创建订单明细表 CREATE TABLE order_item ( id int(11) NOT NULL AUTO_INCREMENT, order_id int(11) NOT NULL, dish_id int(11) NOT NULL, quantity int(11) NOT NULL, price decimal(10,2) NOT NULL, PRIMARY KEY (id), FOREIGN KEY (order_id) REFERENCES order(id), FOREIGN KEY (dish_id) REFERENCES dish(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;3. 后端项目搭建3.1 Maven项目结构创建使用Maven创建多模块项目结构如下food-order-system/ ├── food-order-api/ # 接口定义模块 ├── food-order-service/ # 业务逻辑模块 ├── food-order-dao/ # 数据访问模块 ├── food-order-common/ # 公共工具模块 └── food-order-web/ # Web控制器模块pom.xml核心依赖配置?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion groupIdcom.foodorder/groupId artifactIdfood-order-system/artifactId version1.0.0/version packagingpom/packaging properties spring.version5.3.18/spring.version mybatis.version3.5.9/mybatis.version mybatis-spring.version2.0.7/mybatis-spring.version mysql.version8.0.28/mysql.version jackson.version2.13.3/jackson.version /properties dependencies !-- Spring核心依赖 -- dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version${spring.version}/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version${spring.version}/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-jdbc/artifactId version${spring.version}/version /dependency !-- MyBatis相关 -- dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version${mybatis.version}/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version${mybatis-spring.version}/version /dependency !-- 数据库连接 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version${mysql.version}/version /dependency dependency groupIdcom.alibaba/groupId artifactIddruid/artifactId version1.2.8/version /dependency !-- JSON处理 -- dependency groupIdcom.fasterxml.jackson.core/groupId artifactIdjackson-databind/artifactId version${jackson.version}/version /dependency !-- 其他工具 -- dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId version1.18.24/version scopeprovided/scope /dependency /dependencies /project3.2 Spring配置文件创建Spring核心配置文件applicationContext.xml?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beans xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xmlns:contexthttp://www.springframework.org/schema/context xmlns:txhttp://www.springframework.org/schema/tx xsi:schemaLocationhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd !-- 组件扫描 -- context:component-scan base-packagecom.foodorder/ !-- 数据源配置 -- bean iddataSource classcom.alibaba.druid.pool.DruidDataSource init-methodinit destroy-methodclose property nameurl valuejdbc:mysql://localhost:3306/food_order?useUnicodetrueamp;characterEncodingutf8amp;serverTimezoneAsia/Shanghai/ property nameusername valueroot/ property namepassword value123456/ property nameinitialSize value5/ property nameminIdle value5/ property namemaxActive value20/ /bean !-- MyBatis SqlSessionFactory -- bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namedataSource refdataSource/ property namemapperLocations valueclasspath:mapper/*.xml/ property nametypeAliasesPackage valuecom.foodorder.entity/ /bean !-- Mapper扫描 -- bean classorg.mybatis.spring.mapper.MapperScannerConfigurer property namebasePackage valuecom.foodorder.dao/ property namesqlSessionFactoryBeanName valuesqlSessionFactory/ /bean !-- 事务管理 -- bean idtransactionManager classorg.springframework.jdbc.datasource.DataSourceTransactionManager property namedataSource refdataSource/ /bean !-- 注解驱动事务 -- tx:annotation-driven transaction-managertransactionManager/ /beans3.3 实体类设计创建对应的Java实体类使用Lombok简化代码// User.java package com.foodorder.entity; import lombok.Data; import java.util.Date; Data public class User { private Integer id; private String username; private String password; private String email; private String phone; private Date createTime; private Date updateTime; } // Dish.java package com.foodorder.entity; import lombok.Data; import java.math.BigDecimal; import java.util.Date; Data public class Dish { private Integer id; private String name; private Integer categoryId; private BigDecimal price; private String description; private String imageUrl; private Integer status; private Date createTime; // 关联字段 private String categoryName; } // Order.java package com.foodorder.entity; import lombok.Data; import java.math.BigDecimal; import java.util.Date; import java.util.List; Data public class Order { private Integer id; private String orderNo; private Integer userId; private BigDecimal totalAmount; private Integer status; private Date createTime; private Date payTime; // 关联字段 private ListOrderItem orderItems; private String username; } // OrderItem.java package com.foodorder.entity; import lombok.Data; import java.math.BigDecimal; Data public class OrderItem { private Integer id; private Integer orderId; private Integer dishId; private Integer quantity; private BigDecimal price; // 关联字段 private String dishName; private String imageUrl; }4. 核心业务实现4.1 用户管理模块用户管理包含注册、登录、信息修改等功能// UserService.java package com.foodorder.service; import com.foodorder.entity.User; public interface UserService { User register(User user); User login(String username, String password); User getUserById(Integer id); boolean updateUser(User user); boolean checkUsernameExists(String username); } // UserServiceImpl.java package com.foodorder.service.impl; import com.foodorder.dao.UserDao; import com.foodorder.entity.User; import com.foodorder.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.DigestUtils; Service Transactional public class UserServiceImpl implements UserService { Autowired private UserDao userDao; Override public User register(User user) { // 检查用户名是否已存在 if (userDao.findByUsername(user.getUsername()) ! null) { throw new RuntimeException(用户名已存在); } // 密码加密 String encryptedPassword DigestUtils.md5DigestAsHex(user.getPassword().getBytes()); user.setPassword(encryptedPassword); // 保存用户 userDao.insert(user); return userDao.findById(user.getId()); } Override public User login(String username, String password) { User user userDao.findByUsername(username); if (user null) { throw new RuntimeException(用户不存在); } String encryptedPassword DigestUtils.md5DigestAsHex(password.getBytes()); if (!user.getPassword().equals(encryptedPassword)) { throw new RuntimeException(密码错误); } return user; } Override public User getUserById(Integer id) { return userDao.findById(id); } Override public boolean updateUser(User user) { return userDao.update(user) 0; } Override public boolean checkUsernameExists(String username) { return userDao.findByUsername(username) ! null; } }4.2 菜品管理模块菜品管理包含菜品查询、添加、修改、上下架等功能// DishService.java package com.foodorder.service; import com.foodorder.entity.Dish; import java.util.List; public interface DishService { ListDish getDishesByCategory(Integer categoryId); ListDish getAllDishes(); Dish getDishById(Integer id); boolean addDish(Dish dish); boolean updateDish(Dish dish); boolean updateDishStatus(Integer id, Integer status); } // DishController.java package com.foodorder.web; import com.foodorder.entity.Dish; import com.foodorder.service.DishService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.HashMap; import java.util.List; import java.util.Map; RestController RequestMapping(/api/dish) public class DishController { Autowired private DishService dishService; GetMapping(/list) public MapString, Object getDishList(RequestParam(required false) Integer categoryId) { MapString, Object result new HashMap(); try { ListDish dishes; if (categoryId ! null) { dishes dishService.getDishesByCategory(categoryId); } else { dishes dishService.getAllDishes(); } result.put(success, true); result.put(data, dishes); } catch (Exception e) { result.put(success, false); result.put(message, e.getMessage()); } return result; } PostMapping(/add) public MapString, Object addDish(RequestBody Dish dish) { MapString, Object result new HashMap(); try { boolean success dishService.addDish(dish); result.put(success, success); result.put(message, success ? 添加成功 : 添加失败); } catch (Exception e) { result.put(success, false); result.put(message, e.getMessage()); } return result; } }4.3 订单管理模块订单管理是系统的核心功能包含下单、支付、取消等操作// OrderService.java package com.foodorder.service; import com.foodorder.entity.Order; import java.util.List; public interface OrderService { Order createOrder(Order order); Order getOrderById(Integer id); ListOrder getOrdersByUserId(Integer userId); boolean cancelOrder(Integer orderId); boolean payOrder(Integer orderId); } // OrderServiceImpl.java package com.foodorder.service.impl; import com.foodorder.dao.OrderDao; import com.foodorder.dao.OrderItemDao; import com.foodorder.entity.Order; import com.foodorder.entity.OrderItem; import com.foodorder.service.OrderService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.util.Date; import java.util.List; import java.util.UUID; Service Transactional public class OrderServiceImpl implements OrderService { Autowired private OrderDao orderDao; Autowired private OrderItemDao orderItemDao; Override public Order createOrder(Order order) { // 生成订单号 String orderNo generateOrderNo(); order.setOrderNo(orderNo); order.setCreateTime(new Date()); order.setStatus(0); // 待支付 // 计算总金额 BigDecimal totalAmount BigDecimal.ZERO; for (OrderItem item : order.getOrderItems()) { totalAmount totalAmount.add(item.getPrice().multiply(new BigDecimal(item.getQuantity()))); } order.setTotalAmount(totalAmount); // 保存订单 orderDao.insert(order); // 保存订单明细 for (OrderItem item : order.getOrderItems()) { item.setOrderId(order.getId()); orderItemDao.insert(item); } return orderDao.findById(order.getId()); } Override public Order getOrderById(Integer id) { Order order orderDao.findById(id); if (order ! null) { order.setOrderItems(orderItemDao.findByOrderId(id)); } return order; } Override public ListOrder getOrdersByUserId(Integer userId) { ListOrder orders orderDao.findByUserId(userId); for (Order order : orders) { order.setOrderItems(orderItemDao.findByOrderId(order.getId())); } return orders; } Override public boolean cancelOrder(Integer orderId) { Order order orderDao.findById(orderId); if (order null || order.getStatus() ! 0) { return false; } order.setStatus(2); // 已取消 return orderDao.update(order) 0; } Override public boolean payOrder(Integer orderId) { Order order orderDao.findById(orderId); if (order null || order.getStatus() ! 0) { return false; } order.setStatus(1); // 已支付 order.setPayTime(new Date()); return orderDao.update(order) 0; } private String generateOrderNo() { return ORDER System.currentTimeMillis() UUID.randomUUID().toString().substring(0, 8).toUpperCase(); } }5. 前端Vue.js实现5.1 前端项目初始化使用Vue CLI创建前端项目# 创建Vue项目 vue create food-order-frontend # 进入项目目录 cd food-order-frontend # 安装必要依赖 npm install element-ui axios vue-router5.2 前端项目结构src/ ├── components/ # 公共组件 ├── views/ # 页面组件 ├── router/ # 路由配置 ├── api/ # API接口 ├── utils/ # 工具函数 └── assets/ # 静态资源5.3 主要页面组件实现登录页面组件template div classlogin-container el-form :modelloginForm :rulesrules refloginForm classlogin-form h2网上订餐系统/h2 el-form-item propusername el-input v-modelloginForm.username placeholder用户名/el-input /el-form-item el-form-item proppassword el-input v-modelloginForm.password typepassword placeholder密码/el-input /el-form-item el-form-item el-button typeprimary clickhandleLogin stylewidth:100%登录/el-button /el-form-item el-form-item el-button clickhandleRegister stylewidth:100%注册/el-button /el-form-item /el-form /div /template script import { login } from /api/user export default { name: Login, data() { return { loginForm: { username: , password: }, rules: { username: [ { required: true, message: 请输入用户名, trigger: blur } ], password: [ { required: true, message: 请输入密码, trigger: blur } ] } } }, methods: { handleLogin() { this.$refs.loginForm.validate(valid { if (valid) { login(this.loginForm).then(response { if (response.data.success) { this.$message.success(登录成功) localStorage.setItem(user, JSON.stringify(response.data.data)) this.$router.push(/home) } else { this.$message.error(response.data.message) } }).catch(error { this.$message.error(登录失败) }) } }) }, handleRegister() { this.$router.push(/register) } } } /script style scoped .login-container { height: 100vh; display: flex; justify-content: center; align-items: center; background-color: #f5f5f5; } .login-form { width: 400px; padding: 30px; background: white; border-radius: 5px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); } /style菜品列表页面组件template div classdish-container el-row :gutter20 el-col :span6 v-fordish in dishList :keydish.id el-card classdish-card img :srcdish.imageUrl classdish-image alt菜品图片 div classdish-info h3{{ dish.name }}/h3 p classprice¥{{ dish.price }}/p p classdescription{{ dish.description }}/p el-button typeprimary clickaddToCart(dish)加入购物车/el-button /div /el-card /el-col /el-row /div /template script import { getDishList } from /api/dish export default { name: DishList, data() { return { dishList: [] } }, mounted() { this.loadDishes() }, methods: { loadDishes() { getDishList().then(response { if (response.data.success) { this.dishList response.data.data } }) }, addToCart(dish) { this.$store.dispatch(addToCart, dish) this.$message.success(已加入购物车) } } } /script style scoped .dish-card { margin-bottom: 20px; } .dish-image { width: 100%; height: 200px; object-fit: cover; } .dish-info h3 { margin: 10px 0; } .price { color: #ff6b35; font-size: 18px; font-weight: bold; } .description { color: #666; font-size: 14px; margin: 10px 0; } /style5.4 API接口封装创建统一的API接口管理文件// api/dish.js import request from /utils/request export function getDishList(params) { return request({ url: /api/dish/list, method: get, params }) } export function addDish(data) { return request({ url: /api/dish/add, method: post, data }) } // api/order.js import request from /utils/request export function createOrder(data) { return request({ url: /api/order/create, method: post, data }) } export function getOrderList(params) { return request({ url: /api/order/list, method: get, params }) } // utils/request.js import axios from axios import { Message } from element-ui // 创建axios实例 const service axios.create({ baseURL: process.env.VUE_APP_BASE_API, timeout: 10000 }) // 请求拦截器 service.interceptors.request.use( config { const user JSON.parse(localStorage.getItem(user) || {}) if (user.token) { config.headers[Authorization] Bearer user.token } return config }, error { return Promise.reject(error) } ) // 响应拦截器 service.interceptors.response.use( response { const res response.data if (!res.success) { Message.error(res.message || 请求失败) return Promise.reject(new Error(res.message || Error)) } return response }, error { Message.error(error.message || 请求失败) return Promise.reject(error) } ) export default service6. 系统集成与部署6.1 前后端联调配置配置前端代理解决跨域问题// vue.config.js module.exports { devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true, pathRewrite: { ^/api: } } } } }6.2 数据库连接优化在生产环境中建议使用连接池配置# application.properties spring.datasource.urljdbc:mysql://localhost:3306/food_order?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai spring.datasource.usernameroot spring.datasource.password123456 spring.datasource.driver-class-namecom.mysql.cj.jdbc.Driver # 连接池配置 spring.datasource.druid.initial-size5 spring.datasource.druid.min-idle5 spring.datasource.druid.max-active20 spring.datasource.druid.max-wait600006.3 项目打包部署前后端分别打包后部署到服务器后端打包mvn clean package -Dmaven.test.skiptrue前端打包npm run build7. 常见问题与解决方案7.1 数据库连接问题问题现象启动时报数据库连接失败解决方案检查MySQL服务是否启动确认数据库连接URL、用户名、密码正确检查防火墙设置确保3306端口可访问7.2 跨域问题问题现象前端请求后端接口时报CORS错误解决方案后端配置CORS过滤器使用Nginx反向代理开发环境配置webpack代理7.3 事务管理问题问题现象数据库操作未回滚解决方案确保使用Transactional注解检查异常类型默认只回滚RuntimeException配置事务传播行为7.4 前端路由问题问题现象刷新页面后404解决方案配置Nginx try_files指令使用history模式时配置fallback检查路由配置是否正确8. 系统优化建议8.1 性能优化使用Redis缓存热点数据如菜品信息、用户信息数据库查询优化添加合适索引图片资源使用CDN加速前端组件懒加载减少首屏加载时间8.2 安全优化密码加密存储使用BCrypt等强哈希算法API接口添加权限验证防止SQL注入MyBatis使用#{}占位符XSS攻击防护前端输入过滤8.3 功能扩展添加第三方登录微信、支付宝集成支付功能支付宝、微信支付实现推荐算法基于用户历史订单添加后台管理系统支持多种配送方式通过以上完整的实现方案我们可以构建一个功能完备、性能稳定的网上订餐管理系统。在实际开发过程中建议根据具体业务需求进行调整和优化确保系统能够满足实际的运营需求。
返回列表