1. 项目概述
作为一名有10年开发经验的Java全栈工程师,最近我完成了一个基于SpringBoot的成人小饭桌微信小程序毕业设计项目。这个项目从需求分析到最终上线历时3个月,采用了当前主流的技术栈,包括SpringBoot后端框架、Vue.js前端框架和MySQL数据库。
这个项目最核心的价值在于解决了都市上班族的午餐难题。通过这个小程序,用户可以方便地预订附近商家提供的营养套餐,商家也能高效管理订单和库存。我在开发过程中特别注重用户体验和系统稳定性,采用了MVC分层架构和前后端分离的设计模式。
技术选型心得:选择SpringBoot+Vue的组合主要考虑到团队技术储备和社区支持度。SpringBoot的自动配置特性大幅减少了XML配置工作量,而Vue的组件化开发则让前端代码更易维护。
2. 系统架构设计
2.1 技术栈解析
后端技术栈:
- Spring Boot 2.7.15:提供自动配置、启动器依赖等特性
- MyBatis-Plus 3.5.3:简化CRUD操作,内置分页插件
- Shiro 1.11.0:处理认证授权
- Redis 6.2:缓存热点数据,提高响应速度
- MySQL 8.0:主数据库,采用InnoDB引擎
前端技术栈:
- Vue 3.2:组合式API开发
- Vant 4.0:移动端组件库
- Axios 1.3:HTTP请求库
- ECharts 5.4:数据可视化
2.2 系统架构图
┌───────────────────────────────────────┐ │ 客户端层 │ │ ┌───────────┐ ┌───────────┐ │ │ │ 微信小程序 │ │ 管理后台 │ │ │ └───────────┘ └───────────┘ │ └───────────────┬──────────────┬───────┘ ┌───────────────┐ │ HTTP/HTTPS │ │ 第三方服务 │ ┌───────────────▼──────────────▼───────┐ │ ┌───────────┐ │ │ 网关层 │◄───OAuth2.0────►│ │ 微信支付 │ │ │ ┌─────────────────────────────────┐ │ │ └───────────┘ │ │ │ Nginx │ │ │ ┌───────────┐ │ │ └─────────────────────────────────┘ │ │ │ 腾讯地图 │ │ └───────────────┬──────────────┬───────┘ │ └───────────┘ │ │ REST API │ └───────────────┘ ┌───────────────▼──────────────▼───────┐ │ 应用层 │ │ ┌───────────┐ ┌───────────┐ │ │ │ 业务逻辑 │ │ 缓存层 │ │ │ │ (Service) │ │ (Redis) │ │ │ └───────────┘ └───────────┘ │ └───────────────┬─────────────────────┘ │ JDBC/JPA ┌───────────────▼─────────────────────┐ │ 数据层 │ │ ┌───────────┐ ┌───────────┐ │ │ │ MySQL │ │ 文件存储 │ │ │ │ (主从) │ │ (MinIO) │ │ │ └───────────┘ └───────────┘ │ └─────────────────────────────────────┘2.3 数据库设计要点
数据库设计遵循第三范式,主要包含以下核心表:
用户表(user):
- 字段:id, username, password(salt+MD5), phone, avatar, status
- 索引:username(unique), phone(unique)
商家表(merchant):
- 字段:id, name, address, contact, license_img, status
- 空间索引:location(GEOMETRY)
餐品表(food):
- 字段:id, name, price, stock, description, images(JSON)
- 索引:merchant_id
订单表(order):
- 字段:id, user_id, merchant_id, total_amount, status, create_time
- 索引:user_id, merchant_id, create_time
数据库优化技巧:对高频查询的字段建立组合索引,如(order表的user_id + status)。大文本字段使用垂直分表,图片等资源使用MinIO对象存储。
3. 核心功能实现
3.1 微信登录集成
微信小程序登录流程实现:
// AuthController.java @RestController @RequestMapping("/auth") public class AuthController { @Autowired private WxService wxService; @PostMapping("/wxLogin") public Result wxLogin(@RequestParam String code) { // 1. 使用code换取openid String openid = wxService.getOpenid(code); // 2. 查询或创建用户 User user = userService.getOrCreate(openid); // 3. 生成JWT token String token = JwtUtil.generateToken(user.getId()); return Result.success(token); } } // WxService.java @Service public class WxService { @Value("${wx.appid}") private String appid; @Value("${wx.secret}") private String secret; public String getOpenid(String code) { String url = "https://api.weixin.qq.com/sns/jscode2session" + "?appid=" + appid + "&secret=" + secret + "&js_code=" + code + "&grant_type=authorization_code"; String response = restTemplate.getForObject(url, String.class); JSONObject json = JSON.parseObject(response); return json.getString("openid"); } }3.2 订单状态机设计
订单状态流转采用状态机模式:
// OrderStatus.java public enum OrderStatus { UNPAID(0, "待支付"), PAID(1, "已支付"), PREPARING(2, "准备中"), DELIVERING(3, "配送中"), COMPLETED(4, "已完成"), CANCELLED(5, "已取消"), REFUNDED(6, "已退款"); // 状态转换规则 private static final Map<OrderStatus, Set<OrderStatus>> TRANSITIONS = Map.of( UNPAID, Set.of(PAID, CANCELLED), PAID, Set.of(PREPARING, REFUNDED), PREPARING, Set.of(DELIVERING), DELIVERING, Set.of(COMPLETED) ); public static boolean canTransition(OrderStatus from, OrderStatus to) { return TRANSITIONS.getOrDefault(from, Set.of()).contains(to); } }3.3 定时任务设计
使用Spring的@Scheduled实现定时任务:
// OrderTask.java @Component public class OrderTask { @Autowired private OrderMapper orderMapper; // 每天23点执行 @Scheduled(cron = "0 0 23 * * ?") public void cancelUnpaidOrders() { LocalDateTime deadline = LocalDateTime.now().minusMinutes(30); List<Order> orders = orderMapper.selectUnpaidBefore(deadline); orders.forEach(order -> { order.setStatus(OrderStatus.CANCELLED.getCode()); orderMapper.updateById(order); // 释放库存 inventoryService.releaseStock(order); }); } }4. 性能优化实践
4.1 缓存策略
采用多级缓存架构:
- 本地缓存(Caffeine):缓存用户基本信息,TTL=5分钟
- 分布式缓存(Redis):
- 热点数据:商家信息、餐品详情,TTL=1小时
- 分布式锁:订单创建、库存扣减
- HTTP缓存:静态资源设置Cache-Control
// MerchantServiceImpl.java @Service public class MerchantServiceImpl implements MerchantService { @Autowired private RedisTemplate<String, Object> redisTemplate; @Override @Cacheable(value = "merchant", key = "#id") public Merchant getById(Long id) { // 缓存未命中时查询数据库 return merchantMapper.selectById(id); } @Override @CachePut(value = "merchant", key = "#merchant.id") public Merchant update(Merchant merchant) { merchantMapper.updateById(merchant); return merchant; } }4.2 数据库优化
读写分离:
- 主库:写操作+核心业务读
- 从库:报表查询、非实时数据
SQL优化:
-- 优化前 SELECT * FROM order WHERE user_id = 123 AND status = 1; -- 优化后(添加组合索引) ALTER TABLE order ADD INDEX idx_user_status (user_id, status);分库分表:
- 按用户ID哈希分表(order_0, order_1,...)
- 历史订单归档
4.3 并发控制
使用Redis实现分布式锁:
// InventoryService.java @Service public class InventoryService { @Autowired private RedissonClient redissonClient; public boolean reduceStock(Long foodId, int quantity) { String lockKey = "lock:food:" + foodId; RLock lock = redissonClient.getLock(lockKey); try { // 尝试加锁,最多等待100ms,锁持有时间30s if (lock.tryLock(100, 30000, TimeUnit.MILLISECONDS)) { Food food = foodMapper.selectById(foodId); if (food.getStock() >= quantity) { food.setStock(food.getStock() - quantity); foodMapper.updateById(food); return true; } return false; } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } return false; } }5. 安全防护措施
5.1 接口安全
JWT认证:
- 有效期:2小时
- 刷新机制:旧token过期前30分钟可刷新
- 黑名单:注销的token加入Redis黑名单
参数校验:
@PostMapping("/create") public Result createOrder(@Valid @RequestBody OrderCreateDTO dto) { // 自动校验DTO中的@NotNull等注解 } // OrderCreateDTO.java public class OrderCreateDTO { @NotNull private Long userId; @NotEmpty private List<OrderItemDTO> items; @DecimalMin("0.01") private BigDecimal totalAmount; }
5.2 数据安全
敏感数据加密:
- 密码:PBKDF2WithHmacSHA256 + 随机盐
- 手机号:AES加密存储
SQL防护:
- 强制使用MyBatis参数绑定
- 定期SQL审计
XSS防护:
- 前端:DOMPurify过滤
- 后端:Jackson转义HTML
6. 部署方案
6.1 容器化部署
Docker Compose编排:
version: '3.8' services: app: image: myapp:1.0 ports: - "8080:8080" environment: - SPRING_PROFILES_ACTIVE=prod depends_on: - redis - mysql redis: image: redis:6.2 ports: - "6379:6379" volumes: - redis_data:/data mysql: image: mysql:8.0 ports: - "3306:3306" environment: - MYSQL_ROOT_PASSWORD=123456 - MYSQL_DATABASE=app_db volumes: - mysql_data:/var/lib/mysql volumes: redis_data: mysql_data:6.2 CI/CD流程
GitLab CI配置示例:
stages: - build - test - deploy build: stage: build script: - mvn clean package -DskipTests artifacts: paths: - target/*.jar test: stage: test script: - mvn test deploy_prod: stage: deploy script: - scp target/app.jar user@prod-server:/app/ - ssh user@prod-server "cd /app && docker-compose up -d --build" only: - master7. 项目总结与心得
在开发这个成人小饭桌小程序的过程中,我积累了以下几点重要经验:
技术选型要务实:不必盲目追求最新技术,SpringBoot+Vue的组合已经能很好满足中小型项目需求,社区资源丰富,遇到问题容易找到解决方案。
缓存策略要分层:本地缓存+分布式缓存配合使用,热点数据缓存时间不宜过长,要注意缓存击穿和雪崩问题。
订单系统要健壮:状态机设计要严谨,支付回调要做好幂等处理,库存扣减要用分布式锁。
监控不能少:上线后要配置好Prometheus监控和ELK日志系统,及时发现问题。
这个项目从技术角度来说不算复杂,但完整走完需求分析、系统设计、编码实现、测试部署的全流程,让我对软件工程有了更深刻的理解。特别是性能优化部分,通过压力测试发现并解决了不少瓶颈问题。
对于想学习SpringBoot和微信小程序开发的同学们,我的建议是:
- 先掌握核心基础(Spring IOC/AOP、MyBatis、Vue组件化)
- 再学习常用中间件(Redis、RabbitMQ等)
- 最后研究分布式架构(Spring Cloud)
这个项目的完整源码和数据库脚本我已经整理好,包含详细的开发文档和API说明。对于毕业设计选择类似题目的同学,可以参考这个架构进行定制开发。