从零到一手把手教你用Spring BootMyBatis Plus复刻一个“苍穹外卖”后台管理系统在当今快节奏的数字化时代外卖平台已成为人们日常生活中不可或缺的一部分。对于Java开发者而言构建一个完整的外卖后台管理系统不仅能巩固技术栈更能为简历增添亮眼项目经验。本文将带你从零开始使用Spring Boot和MyBatis Plus等技术一步步复刻一个功能完备的苍穹外卖后台系统。1. 项目准备与环境搭建在开始编码之前合理的项目规划和环境准备是成功的第一步。我们需要确保开发环境的一致性避免后续因环境差异导致的各种问题。首先推荐使用以下开发工具组合JDK 17Java开发的基础环境IntelliJ IDEA高效的Java IDEMaven 3.8项目构建和依赖管理MySQL 8.0关系型数据库Redis 6.2缓存和会话管理创建Spring Boot项目时可以通过Spring Initializr快速生成基础结构。以下是推荐的核心依赖dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies提示在application.yml中配置数据库连接时建议使用连接池如HikariCP并设置合理的连接参数以提高性能。2. 数据库设计与模型构建一个良好的数据库设计是系统稳定运行的基础。外卖系统通常包含以下几个核心表表名主要字段说明employeeid, username, password, name, phone, status员工信息表categoryid, type, name, sort菜品分类表dishid, name, category_id, price, image, description, status菜品信息表setmealid, category_id, name, price, description, image, status套餐信息表orderid, number, status, user_id, address_id, order_time, checkout_time订单主表order_detailid, name, image, order_id, dish_id, setmeal_id, number, amount订单明细表使用MyBatis Plus的代码生成器可以快速创建实体类和Mapper接口public class Dish { TableId(type IdType.AUTO) private Long id; private String name; private Long categoryId; private BigDecimal price; private String image; private String description; private Integer status; // getter/setter省略 }3. 核心业务模块实现3.1 员工管理模块员工管理是后台系统的基础功能主要包括增删改查和状态管理。我们可以利用MyBatis Plus的强大功能简化开发RestController RequestMapping(/employee) public class EmployeeController { Autowired private EmployeeService employeeService; PostMapping public ResultString save(RequestBody Employee employee) { employeeService.save(employee); return Result.success(新增员工成功); } GetMapping(/page) public ResultPageEmployee page(int page, int pageSize, String name) { PageEmployee pageInfo new Page(page, pageSize); LambdaQueryWrapperEmployee queryWrapper new LambdaQueryWrapper(); queryWrapper.like(StringUtils.isNotEmpty(name), Employee::getName, name); employeeService.page(pageInfo, queryWrapper); return Result.success(pageInfo); } }注意在实际项目中密码应该加密存储推荐使用BCryptPasswordEncoder进行加密处理。3.2 菜品管理模块菜品管理涉及图片上传、分类关联等复杂操作。以下是关键实现点文件上传使用Spring的MultipartFile处理图片上传分类关联通过category_id关联菜品分类状态管理控制菜品上下架状态PostMapping public ResultString save(RequestBody DishDto dishDto) { dishService.saveWithFlavor(dishDto); return Result.success(新增菜品成功); } GetMapping(/page) public ResultPageDishDto page(int page, int pageSize, String name) { return dishService.pageWithCategory(page, pageSize, name); }4. 高级功能与性能优化4.1 缓存策略实现使用Redis缓存热门菜品数据可以显著提高系统响应速度GetMapping(/list) public ResultListDishDto list(Dish dish) { String key dish_ dish.getCategoryId() _ dish.getStatus(); // 先从Redis获取 ListDishDto dishDtos (ListDishDto) redisTemplate.opsForValue().get(key); if(dishDtos ! null) { return Result.success(dishDtos); } // Redis没有则查询数据库 LambdaQueryWrapperDish queryWrapper new LambdaQueryWrapper(); queryWrapper.eq(dish.getCategoryId() ! null, Dish::getCategoryId, dish.getCategoryId()); queryWrapper.eq(Dish::getStatus, 1); ListDish dishes dishService.list(queryWrapper); dishDtos dishes.stream().map(item - { DishDto dishDto new DishDto(); BeanUtils.copyProperties(item, dishDto); // 查询口味数据 LambdaQueryWrapperDishFlavor flavorQueryWrapper new LambdaQueryWrapper(); flavorQueryWrapper.eq(DishFlavor::getDishId, item.getId()); ListDishFlavor flavors dishFlavorService.list(flavorQueryWrapper); dishDto.setFlavors(flavors); return dishDto; }).collect(Collectors.toList()); // 存入Redis redisTemplate.opsForValue().set(key, dishDtos, 60, TimeUnit.MINUTES); return Result.success(dishDtos); }4.2 订单统计与数据可视化使用ECharts实现数据可视化可以让运营数据更加直观GetMapping(/statistics) public ResultOrderStatisticsVO statistics(DateTimeFormat(pattern yyyy-MM-dd) LocalDate begin, DateTimeFormat(pattern yyyy-MM-dd) LocalDate end) { OrderStatisticsVO statisticsVO orderService.getStatistics(begin, end); return Result.success(statisticsVO); }前端可以通过以下方式渲染ECharts图表// 基于准备好的dom初始化echarts实例 var myChart echarts.init(document.getElementById(main)); // 指定图表的配置项和数据 var option { title: { text: 订单统计 }, tooltip: {}, legend: { data:[订单数] }, xAxis: { data: [周一,周二,周三,周四,周五,周六,周日] }, yAxis: {}, series: [{ name: 订单数, type: bar, data: [120, 200, 150, 80, 70, 110, 130] }] }; // 使用刚指定的配置项和数据显示图表。 myChart.setOption(option);5. 项目部署与持续集成完成开发后我们需要将项目部署到生产环境。以下是推荐的部署方案打包应用使用Maven打包为可执行JARmvn clean package -DskipTestsDocker化部署FROM openjdk:17-jdk-slim COPY target/sky-take-out.jar app.jar EXPOSE 8080 ENTRYPOINT [java,-jar,/app.jar]CI/CD流程代码提交触发自动化测试测试通过后自动构建Docker镜像滚动更新生产环境容器在实际项目中我发现合理的日志配置对后期维护至关重要。推荐使用Logback并配置合理的滚动策略appender nameFILE classch.qos.logback.core.rolling.RollingFileAppender filelogs/application.log/file rollingPolicy classch.qos.logback.core.rolling.TimeBasedRollingPolicy fileNamePatternlogs/application.%d{yyyy-MM-dd}.log/fileNamePattern maxHistory30/maxHistory /rollingPolicy encoder pattern%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n/pattern /encoder /appender