1. 项目概述校园外卖服务系统是当前高校信息化建设的重要组成部分。这个基于SpringBootVue的全栈项目完美契合了大学生群体对便捷餐饮服务的需求痛点。作为一名在校园信息化领域深耕多年的开发者我发现传统的外卖平台往往无法满足校园场景的特殊性——比如配送范围精确到宿舍楼栋、支持校园卡支付、适应课程表订餐高峰等特性。这个系统采用前后端分离架构后端使用SpringBootMyBatisMySQL技术栈前端采用Vue.js框架。相比市面上通用的外卖平台我们特别针对校园场景做了深度定制配送范围精确到具体楼栋避免校外骑手找不到位置整合校园一卡通支付系统课程表智能推荐功能根据学生空课时间推荐最佳送餐时段食堂档口管理系统帮助校内餐饮商户高效运营2. 技术架构设计2.1 后端技术选型SpringBoot 2.7.x作为后端框架是经过深思熟虑的选择。相比传统的SSM框架SpringBoot的自动配置特性大幅减少了XML配置内嵌Tomcat服务器也简化了部署流程。我在实际开发中发现使用SpringBoot可以将项目启动时间缩短40%以上。数据库选用MySQL 8.0主要考虑到校园场景下数据量适中预计日订单量1万事务处理需求明确支付、订单状态变更等运维成本低与校园IT基础设施兼容性好持久层采用MyBatis而非JPA主要因为需要编写复杂SQL处理订单统计报表历史订单查询涉及多表关联对SQL优化有较高要求2.2 前端技术方案Vue 3.x Element Plus的组合提供了极佳的开发体验。特别值得一提的是我们使用了以下优化方案路由懒加载将不同功能模块打包成独立chunk按需引入Element Plus组件减小打包体积使用axios拦截器统一处理HTTP异常采用Vuex进行状态管理处理全局用户信息// 典型API请求示例 async function getOrderList(params) { try { const res await axios.get(/api/orders, { params }) return res.data } catch (error) { showErrorToast(error.response.data.message) throw error } }3. 核心功能实现3.1 订单状态机设计订单生命周期管理是系统的核心难点。我们设计了一个严谨的状态转换机制stateDiagram-v2 [*] -- 待支付 待支付 -- 已取消: 超时未支付 待支付 -- 已支付: 支付成功 已支付 -- 制作中: 商家接单 制作中 -- 配送中: 制作完成 配送中 -- 已完成: 送达确认 配送中 -- 已退款: 用户申请退款实际代码实现中我们使用枚举定义状态public enum OrderStatus { PENDING_PAYMENT(待支付), PAID(已支付), PROCESSING(制作中), DELIVERING(配送中), COMPLETED(已完成), CANCELLED(已取消), REFUNDED(已退款); private final String desc; // 构造方法、getter省略 }3.2 分布式锁应用在高并发场景下如中午订餐高峰我们使用Redis分布式锁防止库存超卖public boolean reduceInventory(Long itemId, int quantity) { String lockKey lock:inventory: itemId; String requestId UUID.randomUUID().toString(); try { // 获取锁 Boolean locked redisTemplate.opsForValue() .setIfAbsent(lockKey, requestId, 30, TimeUnit.SECONDS); if (!locked) { return false; } // 执行业务逻辑 int affected itemMapper.reduceInventory(itemId, quantity); return affected 0; } finally { // 释放锁 if (requestId.equals(redisTemplate.opsForValue().get(lockKey))) { redisTemplate.delete(lockKey); } } }4. 数据库设计要点4.1 主要表结构用户表(users)设计考虑了校园特色字段CREATE TABLE users ( id bigint NOT NULL AUTO_INCREMENT, student_id varchar(20) NOT NULL COMMENT 学号, name varchar(50) NOT NULL, password varchar(100) NOT NULL, phone varchar(20) NOT NULL, dormitory varchar(50) COMMENT 宿舍楼栋, room varchar(10) COMMENT 寝室号, balance decimal(10,2) DEFAULT 0.00 COMMENT 账户余额, status tinyint DEFAULT 1 COMMENT 1-正常 0-禁用, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY idx_student_id (student_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;4.2 订单表优化订单表(orders)设计采用了垂直分表策略主表存储核心字段约20个扩展表存储物流信息、退款记录等使用ShardingSphere实现按月分表5. 部署实践5.1 后端部署方案我们采用Docker Compose部署后端服务version: 3 services: app: image: campus-food:1.0 ports: - 8080:8080 environment: - SPRING_PROFILES_ACTIVEprod - DB_URLjdbc:mysql://mysql:3306/campus_food depends_on: - mysql - redis mysql: image: mysql:8.0 environment: - MYSQL_ROOT_PASSWORD123456 - MYSQL_DATABASEcampus_food volumes: - mysql_data:/var/lib/mysql redis: image: redis:6.2 ports: - 6379:6379 volumes: mysql_data:5.2 前端部署技巧使用Nginx部署Vue项目时我推荐以下配置优化server { listen 80; server_name food.campus.edu; # 开启gzip压缩 gzip on; gzip_types text/plain application/xml application/javascript; location / { root /usr/share/nginx/html; index index.html; try_files $uri $uri/ /index.html; } # API代理 location /api { proxy_pass http://backend:8080; proxy_set_header Host $host; } # 静态资源缓存 location /static { expires 1y; add_header Cache-Control public; } }6. 踩坑实录6.1 微信支付集成问题校园场景要求同时支持微信支付和校园卡支付。在对接微信支付时我们遇到了证书加载问题。解决方案是将商户证书放在resources/cert目录下使用ClassPathResource加载证书配置正确的证书密码Bean public WXPay wxPay() throws Exception { InputStream certStream new ClassPathResource(cert/apiclient_cert.p12).getInputStream(); WXPayConfig config new MyWXPayConfig(certStream, 商户证书密码); return new WXPay(config); }6.2 高并发下的MySQL死锁在压力测试阶段我们发现当多个用户同时抢购限量商品时会出现死锁。通过以下方案解决调整事务隔离级别为READ COMMITTED为库存更新操作添加SKIP LOCKED提示引入Redis预减库存机制UPDATE items SET stock stock - 1 WHERE id ? AND stock 17. 性能优化实践7.1 缓存策略我们采用多级缓存架构本地缓存Caffeine缓存静态数据如食堂信息Redis缓存热点数据如菜品详情MySQL持久化存储Cacheable(value dishes, key #id) public Dish getDishById(Long id) { return dishMapper.selectById(id); }7.2 SQL优化案例订单查询接口经过以下优化后响应时间从1200ms降至200ms添加复合索引(user_id, create_time)使用覆盖索引避免回表拆分大查询为多个小查询优化前的慢查询SELECT * FROM orders WHERE user_id ? ORDER BY create_time DESC LIMIT 10优化后的查询SELECT id FROM orders WHERE user_id ? ORDER BY create_time DESC LIMIT 108. 安全防护措施8.1 防XSS攻击前端使用vue-sanitize过滤用户输入import sanitizeHTML from sanitize-html Vue.prototype.$sanitize (dirty) { return sanitizeHTML(dirty, { allowedTags: [b, i, em, strong] }) }8.2 接口防刷使用Guava RateLimiter实现API限流Aspect Component public class RateLimitAspect { private final RateLimiter limiter RateLimiter.create(100); // 100请求/秒 Around(annotation(rateLimit)) public Object around(ProceedingJoinPoint joinPoint) throws Throwable { if (limiter.tryAcquire()) { return joinPoint.proceed(); } throw new BusinessException(请求过于频繁); } }9. 监控与运维9.1 SpringBoot Actuator配置application.yml中启用健康检查management: endpoints: web: exposure: include: health,info,metrics endpoint: health: show-details: always9.2 日志收集方案使用ELK栈收集日志Logstash配置input { file { path /var/log/campus-food/*.log type springboot } } filter { grok { match { message %{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} %{NUMBER:pid} --- \[%{DATA:thread}\] %{DATA:class} : %{GREEDYDATA:message} } } }10. 项目扩展方向在实际运营过程中我们发现以下几个有价值的扩展点智能推荐系统基于用户历史订单和课程表数据使用协同过滤算法推荐菜品配送路径优化结合校园地图API为骑手规划最优配送路线语音订餐功能集成语音识别SDK支持语音下单# 伪代码推荐算法示例 def recommend_dishes(user): history get_order_history(user.id) similar_users find_similar_users(history) return aggregate_dishes(similar_users)