1. 项目概述与设计思路
外卖点餐管理系统是现代餐饮行业数字化转型的核心工具,我们采用前后端分离架构实现了一套高性能、易扩展的解决方案。这个系统主要解决三个核心问题:一是提升餐厅运营效率,二是优化顾客点餐体验,三是实现精准的订单管理。
技术选型上,后端采用Spring Boot框架,版本为2.7.5,主要考虑其快速启动、自动配置和丰富的起步依赖特性。前端选择Vue 3组合式API,配合Element Plus组件库,确保界面响应速度和开发效率。数据库使用MySQL 8.0,充分利用其事务处理能力和JSON字段支持。
系统架构分为四层:
- 表现层:Vue构建的响应式Web界面和移动端适配页面
- 应用层:Spring Boot提供的RESTful API服务
- 业务逻辑层:领域驱动设计(DDD)实现的订单处理核心
- 数据访问层:MyBatis-Plus + 多数据源配置
2. 核心模块实现细节
2.1 用户认证与权限控制
采用JWT+Spring Security的安全方案,关键实现如下:
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/api/auth/**").permitAll() .antMatchers("/api/admin/**").hasRole("ADMIN") .anyRequest().authenticated() .and() .addFilter(new JwtAuthenticationFilter(authenticationManager())) .addFilter(new JwtAuthorizationFilter(authenticationManager())); } }权限设计采用RBAC模型,包含5种角色:
- 顾客:浏览菜单、下单、支付
- 商家:菜品管理、订单处理
- 配送员:接单、配送状态更新
- 店长:数据统计、员工管理
- 超级管理员:系统配置
关键点:JWT令牌设置30分钟过期时间,配合refreshToken机制实现无感刷新。权限验证通过自定义注解@PreAuthorize实现方法级控制。
2.2 订单状态机设计
订单生命周期管理采用状态模式,定义6种核心状态:
stateDiagram [*] --> 待支付 待支付 --> 已取消: 超时/用户取消 待支付 --> 已支付: 支付成功 已支付 --> 制作中: 商家接单 制作中 --> 配送中: 制作完成 配送中 --> 已完成: 送达确认 已完成 --> [*]状态转换通过Spring StateMachine实现:
public class OrderStateMachineConfig extends EnumStateMachineConfigurerAdapter<OrderState, OrderEvent> { @Override public void configure(StateMachineStateConfigurer<OrderState, OrderEvent> states) throws Exception { states .withStates() .initial(OrderState.待支付) .states(EnumSet.allOf(OrderState.class)); } @Override public void configure(StateMachineTransitionConfigurer<OrderState, OrderEvent> transitions) throws Exception { transitions .withExternal() .source(OrderState.待支付).target(OrderState.已取消) .event(OrderEvent.取消订单) .and() .withExternal() .source(OrderState.待支付).target(OrderState.已支付) .event(OrderEvent.支付成功); } }2.3 高并发订单处理
采用多级缓存策略应对高峰时段:
- 本地缓存:Caffeine缓存热门菜品信息
- 分布式缓存:Redis集群存储库存余量
- 数据库:MySQL最终持久化
库存扣减使用Redis Lua脚本保证原子性:
local key = KEYS[1] local change = tonumber(ARGV[1]) local current = tonumber(redis.call('GET', key) or "0") if current + change >= 0 then redis.call('INCRBY', key, change) return 1 else return 0 end3. 关键业务逻辑实现
3.1 购物车设计
购物车服务采用混合存储策略:
- 登录用户:数据持久化到MySQL
- 未登录用户:存储在localStorage
核心数据结构:
{ "cartId": "UUID", "items": [ { "dishId": 123, "quantity": 2, "selectedOptions": [ {"optionId": 1, "value": "微辣"}, {"optionId": 2, "value": "不要香菜"} ] } ], "lastUpdated": "2023-07-20T08:30:00Z" }合并逻辑处理:
public Cart mergeCarts(Cart persistentCart, Cart tempCart) { Map<CartItemKey, CartItem> itemMap = persistentCart.getItems().stream() .collect(Collectors.toMap( item -> new CartItemKey(item.getDishId(), item.getOptionSignature()), Function.identity())); tempCart.getItems().forEach(tempItem -> { CartItemKey key = new CartItemKey(tempItem.getDishId(), tempItem.getOptionSignature()); if (itemMap.containsKey(key)) { itemMap.get(key).addQuantity(tempItem.getQuantity()); } else { persistentCart.addItem(tempItem); } }); return persistentCart; }3.2 支付系统集成
支付流程采用状态机+策略模式:
- 选择支付方式(微信/支付宝/余额)
- 创建支付订单
- 调用对应支付网关
- 异步通知处理
防重复支付设计:
@Transactional public PaymentResult handlePayment(PaymentRequest request) { // 分布式锁防止重复提交 String lockKey = "payment:" + request.getOrderId(); try { if (!redisLock.tryLock(lockKey, 10, TimeUnit.SECONDS)) { throw new BusinessException("操作过于频繁"); } Order order = orderService.getById(request.getOrderId()); if (order.getStatus() != OrderStatus.待支付) { throw new BusinessException("订单状态异常"); } PaymentStrategy strategy = paymentStrategyFactory.getStrategy(request.getPaymentType()); PaymentResult result = strategy.pay(request); if (result.isSuccess()) { orderService.updateOrderStatus(order.getId(), OrderStatus.已支付); paymentRecordService.save(result.toPaymentRecord()); } return result; } finally { redisLock.unlock(lockKey); } }4. 性能优化实践
4.1 数据库优化方案
索引策略:
- 订单表:复合索引(user_id, create_time)
- 菜品表:全文索引(name)、普通索引(category_id)
- 使用EXPLAIN分析慢查询
分库分表:
- 按餐厅ID水平分片
- 订单表按月分表
- 使用ShardingSphere实现透明访问
读写分离:
- 一主三从架构
- 使用Spring AbstractRoutingDataSource动态切换
4.2 前端性能提升
- 代码分割:
const Menu = () => import(/* webpackChunkName: "menu" */ './views/Menu.vue') const Order = () => import(/* webpackChunkName: "order" */ './views/Order.vue')- 图片优化:
- WebP格式替代PNG/JPG
- 懒加载实现:
<img v-lazy="item.image" alt="菜品图片">- API请求优化:
- 使用GraphQL减少请求字段
- 批量获取接口设计:
query { menu { categories { id name dishes { id name price } } } }5. 部署与监控方案
5.1 容器化部署
Docker Compose编排方案:
version: '3.8' services: backend: image: registry.example.com/takeaway:${TAG} environment: - SPRING_PROFILES_ACTIVE=prod deploy: resources: limits: cpus: '2' memory: 2G healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"] interval: 30s timeout: 10s retries: 3 frontend: image: registry.example.com/takeaway-front:${TAG} ports: - "80:80" depends_on: - backend5.2 监控体系搭建
- 指标收集:
- Prometheus采集Spring Boot Actuator指标
- 自定义业务指标:
@Bean public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() { return registry -> registry.config().commonTags( "application", "takeaway-system", "region", System.getenv("REGION")); }- 日志收集:
- ELK栈集中管理
- 结构化日志配置:
<Pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</Pattern>- 告警规则:
groups: - name: business.rules rules: - alert: HighFailedPaymentRate expr: rate(payment_failed_total[5m]) / rate(payment_attempted_total[5m]) > 0.1 for: 10m labels: severity: warning annotations: summary: "High payment failure rate ({{ $value }})"6. 项目演进路线
6.1 技术债解决方案
- 代码重构计划:
- 领域模型精炼:使用事件风暴重新划分限界上下文
- 测试覆盖率提升:从60%到85%
- 技术栈升级:Spring Boot 2.x → 3.x迁移路径
- 架构改进:
- 引入CQRS模式分离读写模型
- 关键模块服务化:支付服务独立部署
- 事件驱动架构改造
6.2 扩展功能规划
- 智能推荐系统:
- 基于用户历史的协同过滤
- 实时推荐使用Redis向量搜索
- 冷启动解决方案
- 配送路线优化:
- 地理围栏技术
- 骑手路径规划算法
- 实时交通数据接入
- 厨房显示系统(KDS):
- WebSocket实时推送
- 订单优先级算法
- 多屏协同工作
在实际开发过程中,我们发现状态机的引入虽然增加了前期设计复杂度,但显著降低了后期维护成本。特别是在处理退款流程时,状态模式的优势体现得淋漓尽致。一个经验之谈:在定义状态转换规则时,一定要与业务专家充分沟通,把所有的异常流程都考虑进去,这能避免后续80%的状态相关bug。