站点图标 AI技术聚合

基于SpringBoot的外卖项目(详细开发过程)

基于SpringBoot的外卖项目(详细开发过程)

Table of Contents

基于SpringBoot+MyBatisPlus的外卖项目

申明: 未经许可,禁止以任何形式转载,若要引用,请标注链接地址。 全文共计86935字,阅读大概需要3分钟
更多学习内容, 欢迎关注我的个人公众号:不懂开发的程序猿

写在前面的几句话:
【警告】本篇博客较长,若引起阅读不适,建议收藏,稍后再读
【说明】该外卖项目是基于SpringBoot + MyBatisPlus为框架来开发的,前端页面框架都是现成的,只需要Java后端开发程序员编写对应的接口功能和服务,是一个很不错的练手项目。项目也非常适合作为大学生的课设,毕设
【文档】本篇博客详细介绍了该外卖项目的开发步骤,如果需要写课程设计或本科毕业论文文档,建议参考我下面这篇博客,内有详细的文档说明

点餐平台文档说明

1、软件开发整体介绍

软件开发流程

在这里插入图片描述

角色分工

2、外卖项目介绍

项目介绍

分为后台系统管理移动端两部分

后台系统管理供商家:对菜品、套餐、订单等进行管理维护等

移动端供消费者:在线浏览,添加购物车,下单 等

3步开发思路:

第一:主要实现基本需求,其中移动端应用通过H5实现,用户可以通过手机浏览器访问。

第二:主要针对移动端应用进行改进,使用微信小程序实现,用户使用起来更加方便。

第三:主要针对系统进行优化升级,提高系统的访问性能。

产品展示

后台系统管理

登录页

员工管理页

分类管理

菜品管理

套餐管理

订单明细

移动端

登录页

首页

下单确认页

下单成功页

个人中心页

地址管理页

历史订单页

技术选型

功能结构

角色

3、开发环境的搭建

开发环境说明

工具版本
后台SpringBoot + MyBatisPlus
服务器Tomcat 8.5.73
数据库MySQL 8.0.28
Build ToolsMaven 3.8.5
前端Vue + ElementUI
开发工具IDEA 2022.3
版本管理工具Git

建库

建表

/*
SQLyog Ultimate v12.08 (64 bit)
MySQL - 8.0.27 : Database - reggie
*********************************************************************
*/


/*!40101 SET NAMES utf8 */;

/*!40101 SET SQL_MODE=''*/;

/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/`reggie` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */ /*!80016 DEFAULT ENCRYPTION='N' */;

USE `reggie`;

/*Table structure for table `address_book` */

DROP TABLE IF EXISTS `address_book`;

CREATE TABLE `address_book` (
  `id` bigint NOT NULL COMMENT '主键',
  `user_id` bigint NOT NULL COMMENT '用户id',
  `consignee` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '收货人',
  `sex` tinyint NOT NULL COMMENT '性别 0 女 1 男',
  `phone` varchar(11) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '手机号',
  `province_code` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '省级区划编号',
  `province_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '省级名称',
  `city_code` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '市级区划编号',
  `city_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '市级名称',
  `district_code` varchar(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '区级区划编号',
  `district_name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '区级名称',
  `detail` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '详细地址',
  `label` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '标签',
  `is_default` tinyint(1) NOT NULL DEFAULT '0' COMMENT '默认 0 否 1是',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '更新时间',
  `create_user` bigint NOT NULL COMMENT '创建人',
  `update_user` bigint NOT NULL COMMENT '修改人',
  `is_deleted` int NOT NULL DEFAULT '0' COMMENT '是否删除',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='地址管理';

/*Data for the table `address_book` */

insert  into `address_book`(`id`,`user_id`,`consignee`,`sex`,`phone`,`province_code`,`province_name`,`city_code`,`city_name`,`district_code`,`district_name`,`detail`,`label`,`is_default`,`create_time`,`update_time`,`create_user`,`update_user`,`is_deleted`) values (1417414526093082626,1417012167126876162,'小明',1,'13812345678',NULL,NULL,NULL,NULL,NULL,NULL,'昌平区金燕龙办公楼','公司',1,'2021-07-20 17:22:12','2021-07-20 17:26:33',1417012167126876162,1417012167126876162,0),(1417414926166769666,1417012167126876162,'小李',1,'13512345678',NULL,NULL,NULL,NULL,NULL,NULL,'测试','家',0,'2021-07-20 17:23:47','2021-07-20 17:23:47',1417012167126876162,1417012167126876162,0),(1628270733663694849,1627997218788163586,'金阳',1,'17671789248',NULL,NULL,NULL,NULL,NULL,NULL,'湖北工业大学','学校',1,'2023-02-22 13:49:29','2023-02-22 13:49:32',1627997218788163586,1627997218788163586,0);

/*Table structure for table `category` */

DROP TABLE IF EXISTS `category`;

CREATE TABLE `category` (
  `id` bigint NOT NULL COMMENT '主键',
  `type` int DEFAULT NULL COMMENT '类型   1 菜品分类 2 套餐分类',
  `name` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '分类名称',
  `sort` int NOT NULL DEFAULT '0' COMMENT '顺序',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '更新时间',
  `create_user` bigint NOT NULL COMMENT '创建人',
  `update_user` bigint NOT NULL COMMENT '修改人',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `idx_category_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='菜品及套餐分类';

/*Data for the table `category` */

insert  into `category`(`id`,`type`,`name`,`sort`,`create_time`,`update_time`,`create_user`,`update_user`) values (1397844263642378242,1,'湘菜',1,'2021-05-27 09:16:58','2023-02-19 16:51:09',1,1),(1397844303408574465,1,'川菜',2,'2021-05-27 09:17:07','2021-06-02 14:27:22',1,1),(1397844391040167938,1,'粤菜',3,'2021-05-27 09:17:28','2021-07-09 14:37:13',1,1),(1413341197421846529,1,'饮品',11,'2021-07-09 11:36:15','2021-07-09 14:39:15',1,1),(1413342269393674242,2,'商务套餐',5,'2021-07-09 11:40:30','2021-07-09 14:43:45',1,1),(1413384954989060097,1,'主食',12,'2021-07-09 14:30:07','2021-07-09 14:39:19',1,1),(1413386191767674881,2,'儿童套餐',6,'2021-07-09 14:35:02','2021-07-09 14:39:05',1,1),(1627130608250593281,1,'湖北菜',4,'2023-02-19 10:19:02','2023-02-19 10:19:02',1,1);

/*Table structure for table `dish` */

DROP TABLE IF EXISTS `dish`;

CREATE TABLE `dish` (
  `id` bigint NOT NULL COMMENT '主键',
  `name` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '菜品名称',
  `category_id` bigint NOT NULL COMMENT '菜品分类id',
  `price` decimal(10,2) DEFAULT NULL COMMENT '菜品价格',
  `code` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '商品码',
  `image` varchar(200) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '图片',
  `description` varchar(400) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '描述信息',
  `status` int NOT NULL DEFAULT '1' COMMENT '0 停售 1 起售',
  `sort` int NOT NULL DEFAULT '0' COMMENT '顺序',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '更新时间',
  `create_user` bigint NOT NULL COMMENT '创建人',
  `update_user` bigint NOT NULL COMMENT '修改人',
  `is_deleted` int NOT NULL DEFAULT '0' COMMENT '是否删除',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `idx_dish_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='菜品管理';

/*Data for the table `dish` */

insert  into `dish`(`id`,`name`,`category_id`,`price`,`code`,`image`,`description`,`status`,`sort`,`create_time`,`update_time`,`create_user`,`update_user`,`is_deleted`) values (1628019384179052546,'红烧肉',1397844263642378242,'3900.00','','90c9f385-5c18-491a-90f2-a4c8df198376.jpg','红烧肉',1,0,'2023-02-21 21:10:43','2023-02-21 21:10:43',1627997218788163586,1627997218788163586,0),(1628019727558332417,'麻辣鸡丝',1397844303408574465,'3900.00','','b19c64b2-378d-43d9-975f-2367bcc99e70.jpg','麻辣鸡丝',1,0,'2023-02-21 21:12:05','2023-02-21 21:12:05',1627997218788163586,1627997218788163586,0),(1628020011776954369,'辣子鸡',1627130608250593281,'4900.00','','d79ac164-8e43-4478-9d57-539764d1c5a8.jpg','来自鲜嫩美味的小鸡,值得一尝',1,0,'2023-02-21 21:13:13','2023-02-22 14:42:06',1627997218788163586,1627997218788163586,0),(1628020274659151874,'基围虾',1397844263642378242,'5900.00','','05010fb3-c055-41ff-8da8-4d941daea332.jpg','基围虾',1,0,'2023-02-21 21:14:15','2023-02-21 21:14:15',1627997218788163586,1627997218788163586,0),(1628020414488858625,'麻辣兔头',1397844303408574465,'12800.00','','703fb335-5593-49be-a629-e2522344212d.jpg','麻辣兔头',1,0,'2023-02-21 21:14:49','2023-02-21 21:14:49',1627997218788163586,1627997218788163586,0),(1628020624501854210,'邵阳猪血丸子',1397844391040167938,'5900.00','','79f6db2d-99d9-40f0-adee-a6750036d40e.jpg','邵阳猪血丸子',1,0,'2023-02-21 21:15:39','2023-02-21 21:15:39',1627997218788163586,1627997218788163586,0),(1628020855322791938,'烤乳猪',1397844391040167938,'9900.00','','8197826f-8bcd-44a1-9d0e-e742b0265e7d.jpeg','白切鸡',1,0,'2023-02-21 21:16:34','2023-02-21 21:19:39',1627997218788163586,1627997218788163586,0),(1628020978719215617,'脆皮烧鹅',1627130608250593281,'15800.00','','3d6188da-68f1-4299-89fd-04c7ab744110.jpeg','脆皮烧鹅',1,0,'2023-02-21 21:17:03','2023-02-22 14:41:44',1627997218788163586,1627997218788163586,0),(1628021120151146497,'上汤焗龙虾',1627130608250593281,'15800.00','','d2dbe897-3b8b-4e5c-99d8-b7ca159ba5b9.jpeg','上汤焗龙虾',1,0,'2023-02-21 21:17:37','2023-02-22 14:41:25',1627997218788163586,1627997218788163586,0),(1628021265471197185,'宫保鸡丁',1397844303408574465,'6900.00','','95f8b479-ae76-4107-9b08-3c62ae7f7fd0.jpg','宫保鸡丁',1,0,'2023-02-21 21:18:12','2023-02-22 14:40:23',1627997218788163586,1627997218788163586,0),(1628021771191013377,'白切鸡',1397844391040167938,'7900.00','','b5d537cf-0d6c-42ae-9210-94c981087d52.jpeg','白切鸡',1,0,'2023-02-21 21:20:12','2023-02-21 21:20:12',1627997218788163586,1627997218788163586,0),(1628022122845655041,'青椒炖鸡丁',1627130608250593281,'9900.00','','9b7494ee-1714-40ee-a94e-18c769678671.jpg','青椒炖鸡丁',1,0,'2023-02-21 21:21:36','2023-02-21 21:21:36',1627997218788163586,1627997218788163586,0),(1628022255993835522,'老火靓汤',1627130608250593281,'10900.00','','a72af50a-264c-4cf1-9da0-28e1eb98aa5c.jpeg','老火靓汤',1,0,'2023-02-21 21:22:08','2023-02-21 21:22:08',1627997218788163586,1627997218788163586,0),(1628022421907918850,'清蒸河鲜海鲜',1397844303408574465,'2900.00','','06b6c68f-db38-4bff-a8f4-131830291cec.jpg','清蒸河鲜海鲜',1,0,'2023-02-21 21:22:47','2023-02-21 21:22:47',1627997218788163586,1627997218788163586,0),(1628022523112280066,'王老吉',1413341197421846529,'500.00','','3e7ab2fe-01fa-4eb6-828e-b7998583a4e1.png','王老吉',1,0,'2023-02-21 21:23:11','2023-02-21 21:23:11',1627997218788163586,1627997218788163586,0),(1628022754352648193,'麻辣水煮鱼',1397844391040167938,'6500.00','','5f614bfa-f62c-4d5d-a4e3-852d6ce53d62.jpeg','麻辣水煮鱼',1,0,'2023-02-21 21:24:07','2023-02-22 14:40:56',1627997218788163586,1627997218788163586,0),(1628022918689673218,'清炒素食',1627130608250593281,'1900.00','','d8783e07-a8da-4e4e-8826-0c0e6990a08f.jpg','清炒素食',1,0,'2023-02-21 21:24:46','2023-02-21 21:24:46',1627997218788163586,1627997218788163586,0),(1628023021122965506,'啤酒',1413341197421846529,'1000.00','','bbfe22ba-9bd5-486a-ae83-22e108dddc47.png','啤酒',1,0,'2023-02-21 21:25:10','2023-02-21 21:25:10',1627997218788163586,1627997218788163586,0),(1628023133450620930,'麻辣鱼片',1397844303408574465,'3900.00','','ffdbeb37-0fbe-4190-a52a-3a16478f366e.jpg','麻辣鱼片',1,0,'2023-02-21 21:25:37','2023-02-21 21:25:37',1627997218788163586,1627997218788163586,0),(1628023363927625729,'烤乳鸽',1397844391040167938,'7900.00','','a1848e46-eb33-4957-bd77-039caaee79c2.jpeg','烤乳鸽',1,0,'2023-02-21 21:26:32','2023-02-21 21:26:32',1627997218788163586,1627997218788163586,0),(1628023490318782465,'大米饭',1413384954989060097,'500.00','','d19db29f-c016-410d-ac01-a3742ea1ea3c.png','大米饭',1,0,'2023-02-21 21:27:02','2023-02-21 21:27:02',1627997218788163586,1627997218788163586,0),(1628023694518472706,'辣子鸡丁',1397844263642378242,'3900.00','','c67657d7-4cbf-4d0c-b20c-7ab66ad52514.jpg','辣子鸡丁',1,0,'2023-02-21 21:27:51','2023-02-21 21:27:51',1627997218788163586,1627997218788163586,0),(1628023841423970305,'口味蛇',1627130608250593281,'8800.00','','3d486c18-6dd8-4464-a087-bd93cfc987bf.jpg','口味蛇',1,0,'2023-02-21 21:28:26','2023-02-21 21:28:26',1627997218788163586,1627997218788163586,0);

/*Table structure for table `dish_flavor` */

DROP TABLE IF EXISTS `dish_flavor`;

CREATE TABLE `dish_flavor` (
  `id` bigint NOT NULL COMMENT '主键',
  `dish_id` bigint NOT NULL COMMENT '菜品',
  `name` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '口味名称',
  `value` varchar(500) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '口味数据list',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '更新时间',
  `create_user` bigint NOT NULL COMMENT '创建人',
  `update_user` bigint NOT NULL COMMENT '修改人',
  `is_deleted` int NOT NULL DEFAULT '0' COMMENT '是否删除',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='菜品口味关系表';

/*Data for the table `dish_flavor` */

insert  into `dish_flavor`(`id`,`dish_id`,`name`,`value`,`create_time`,`update_time`,`create_user`,`update_user`,`is_deleted`) values (1628019384321658881,1628019384179052546,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]','2023-02-21 21:10:43','2023-02-21 21:10:43',1627997218788163586,1627997218788163586,0),(1628019384321658882,1628019384179052546,'温度','[\"热饮\",\"常温\",\"去冰\",\"少冰\",\"多冰\"]','2023-02-21 21:10:43','2023-02-21 21:10:43',1627997218788163586,1627997218788163586,0),(1628019727621246978,1628019727558332417,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]','2023-02-21 21:12:05','2023-02-21 21:12:05',1627997218788163586,1627997218788163586,0),(1628019727621246979,1628019727558332417,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]','2023-02-21 21:12:05','2023-02-21 21:12:05',1627997218788163586,1627997218788163586,0),(1628019727621246980,1628019727558332417,'温度','[\"热饮\",\"常温\",\"去冰\",\"少冰\",\"多冰\"]','2023-02-21 21:12:05','2023-02-21 21:12:05',1627997218788163586,1627997218788163586,0),(1628020274726260738,1628020274659151874,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]','2023-02-21 21:14:15','2023-02-21 21:14:15',1627997218788163586,1627997218788163586,0),(1628020274726260739,1628020274659151874,'温度','[\"热饮\",\"常温\",\"去冰\",\"少冰\",\"多冰\"]','2023-02-21 21:14:15','2023-02-21 21:14:15',1627997218788163586,1627997218788163586,0),(1628020274726260740,1628020274659151874,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]','2023-02-21 21:14:15','2023-02-21 21:14:15',1627997218788163586,1627997218788163586,0),(1628020414488858626,1628020414488858625,'温度','[\"热饮\",\"常温\",\"去冰\",\"少冰\",\"多冰\"]','2023-02-21 21:14:49','2023-02-21 21:14:49',1627997218788163586,1627997218788163586,0),(1628020414488858627,1628020414488858625,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]','2023-02-21 21:14:49','2023-02-21 21:14:49',1627997218788163586,1627997218788163586,0),(1628020624573157378,1628020624501854210,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]','2023-02-21 21:15:39','2023-02-21 21:15:39',1627997218788163586,1627997218788163586,0),(1628020624573157379,1628020624501854210,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]','2023-02-21 21:15:39','2023-02-21 21:15:39',1627997218788163586,1627997218788163586,0),(1628020855389900802,1628020855322791938,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]','2023-02-21 21:19:39','2023-02-21 21:19:39',1627997218788163586,1627997218788163586,0),(1628020855389900803,1628020855322791938,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]','2023-02-21 21:19:39','2023-02-21 21:19:39',1627997218788163586,1627997218788163586,0),(1628021771191013378,1628021771191013377,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]','2023-02-21 21:20:12','2023-02-21 21:20:12',1627997218788163586,1627997218788163586,0),(1628021771191013379,1628021771191013377,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]','2023-02-21 21:20:12','2023-02-21 21:20:12',1627997218788163586,1627997218788163586,0),(1628022122971484162,1628022122845655041,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]','2023-02-21 21:21:36','2023-02-21 21:21:36',1627997218788163586,1627997218788163586,0),(1628022122971484163,1628022122845655041,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]','2023-02-21 21:21:36','2023-02-21 21:21:36',1627997218788163586,1627997218788163586,0),(1628022256186773505,1628022255993835522,'温度','[\"热饮\",\"常温\",\"去冰\",\"少冰\",\"多冰\"]','2023-02-21 21:22:08','2023-02-21 21:22:08',1627997218788163586,1627997218788163586,0),(1628022256186773506,1628022255993835522,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]','2023-02-21 21:22:08','2023-02-21 21:22:08',1627997218788163586,1627997218788163586,0),(1628022421975027713,1628022421907918850,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]','2023-02-21 21:22:47','2023-02-21 21:22:47',1627997218788163586,1627997218788163586,0),(1628022421975027714,1628022421907918850,'温度','[\"热饮\",\"常温\",\"去冰\",\"少冰\",\"多冰\"]','2023-02-21 21:22:47','2023-02-21 21:22:47',1627997218788163586,1627997218788163586,0),(1628022523175194626,1628022523112280066,'温度','[\"热饮\",\"常温\",\"去冰\",\"少冰\",\"多冰\"]','2023-02-21 21:23:11','2023-02-21 21:23:11',1627997218788163586,1627997218788163586,0),(1628022918823890945,1628022918689673218,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]','2023-02-21 21:24:46','2023-02-21 21:24:46',1627997218788163586,1627997218788163586,0),(1628022918823890946,1628022918689673218,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]','2023-02-21 21:24:46','2023-02-21 21:24:46',1627997218788163586,1627997218788163586,0),(1628023021190074370,1628023021122965506,'温度','[\"热饮\",\"常温\",\"去冰\",\"少冰\",\"多冰\"]','2023-02-21 21:25:10','2023-02-21 21:25:10',1627997218788163586,1627997218788163586,0),(1628023133576450049,1628023133450620930,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]','2023-02-21 21:25:37','2023-02-21 21:25:37',1627997218788163586,1627997218788163586,0),(1628023133576450050,1628023133450620930,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]','2023-02-21 21:25:37','2023-02-21 21:25:37',1627997218788163586,1627997218788163586,0),(1628023364061843457,1628023363927625729,'甜味','[\"无糖\",\"少糖\",\"半糖\",\"多糖\",\"全糖\"]','2023-02-21 21:26:32','2023-02-21 21:26:32',1627997218788163586,1627997218788163586,0),(1628023364061843458,1628023363927625729,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]','2023-02-21 21:26:32','2023-02-21 21:26:32',1627997218788163586,1627997218788163586,0),(1628023490448805890,1628023490318782465,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]','2023-02-21 21:27:02','2023-02-21 21:27:02',1627997218788163586,1627997218788163586,0),(1628023694585581569,1628023694518472706,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]','2023-02-21 21:27:51','2023-02-21 21:27:51',1627997218788163586,1627997218788163586,0),(1628023694585581570,1628023694518472706,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]','2023-02-21 21:27:51','2023-02-21 21:27:51',1627997218788163586,1627997218788163586,0),(1628023841553993729,1628023841423970305,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]','2023-02-21 21:28:26','2023-02-21 21:28:26',1627997218788163586,1627997218788163586,0),(1628023841553993730,1628023841423970305,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]','2023-02-21 21:28:26','2023-02-21 21:28:26',1627997218788163586,1627997218788163586,0),(1628283539888816129,1628021265471197185,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]','2023-02-22 14:40:23','2023-02-22 14:40:23',1627997218788163586,1627997218788163586,0),(1628283539888816130,1628021265471197185,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]','2023-02-22 14:40:23','2023-02-22 14:40:23',1627997218788163586,1627997218788163586,0),(1628283681786314754,1628022754352648193,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]','2023-02-22 14:40:56','2023-02-22 14:40:56',1627997218788163586,1627997218788163586,0),(1628283681786314755,1628022754352648193,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]','2023-02-22 14:40:56','2023-02-22 14:40:56',1627997218788163586,1627997218788163586,0),(1628283800552226817,1628021120151146497,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]','2023-02-22 14:41:25','2023-02-22 14:41:25',1627997218788163586,1627997218788163586,0),(1628283800552226818,1628021120151146497,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]','2023-02-22 14:41:25','2023-02-22 14:41:25',1627997218788163586,1627997218788163586,0),(1628283883154849793,1628020978719215617,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]','2023-02-22 14:41:44','2023-02-22 14:41:44',1627997218788163586,1627997218788163586,0),(1628283883154849794,1628020978719215617,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]','2023-02-22 14:41:44','2023-02-22 14:41:44',1627997218788163586,1627997218788163586,0),(1628283974536151041,1628020011776954369,'忌口','[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]','2023-02-22 14:42:06','2023-02-22 14:42:06',1627997218788163586,1627997218788163586,0),(1628283974536151042,1628020011776954369,'辣度','[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]','2023-02-22 14:42:06','2023-02-22 14:42:06',1627997218788163586,1627997218788163586,0);

/*Table structure for table `employee` */

DROP TABLE IF EXISTS `employee`;

CREATE TABLE `employee` (
  `id` bigint NOT NULL COMMENT '主键',
  `name` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '姓名',
  `username` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '用户名',
  `password` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '密码',
  `phone` varchar(11) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '手机号',
  `sex` varchar(2) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '性别',
  `id_number` varchar(18) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '身份证号',
  `status` int NOT NULL DEFAULT '1' COMMENT '状态 0:禁用,1:正常',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '更新时间',
  `create_user` bigint NOT NULL COMMENT '创建人',
  `update_user` bigint NOT NULL COMMENT '修改人',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `idx_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='员工信息';

/*Data for the table `employee` */

insert  into `employee`(`id`,`name`,`username`,`password`,`phone`,`sex`,`id_number`,`status`,`create_time`,`update_time`,`create_user`,`update_user`) values (1,'管理员','admin','e10adc3949ba59abbe56e057f20f883e','13812312312','1','110101199001010047',1,'2021-05-06 17:20:07','2021-05-10 02:24:09',1,1),(1626857776597762049,'张三1','zhangsan','e10adc3949ba59abbe56e057f20f883e','17671789248','1','421181199805171311',1,'2023-02-18 16:14:54','2023-02-18 22:06:50',1,1),(1626945547559514113,'小李','test001','e10adc3949ba59abbe56e057f20f883e','17612345678','1','421181123456781234',1,'2023-02-18 22:04:04','2023-02-19 08:52:25',1,1);

/*Table structure for table `order_detail` */

DROP TABLE IF EXISTS `order_detail`;

CREATE TABLE `order_detail` (
  `id` bigint NOT NULL COMMENT '主键',
  `name` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '名字',
  `image` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '图片',
  `order_id` bigint NOT NULL COMMENT '订单id',
  `dish_id` bigint DEFAULT NULL COMMENT '菜品id',
  `setmeal_id` bigint DEFAULT NULL COMMENT '套餐id',
  `dish_flavor` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '口味',
  `number` int NOT NULL DEFAULT '1' COMMENT '数量',
  `amount` decimal(10,2) NOT NULL COMMENT '金额',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='订单明细表';

/*Data for the table `order_detail` */

insert  into `order_detail`(`id`,`name`,`image`,`order_id`,`dish_id`,`setmeal_id`,`dish_flavor`,`number`,`amount`) values (1628281691748474882,'儿童套餐A','17fb2dcf-c06a-46c4-8ba6-8cb461d84031.jpg',1628281691555536898,NULL,1628024994765295618,NULL,1,'59.00'),(1628281691748474883,'辣子鸡丁','c67657d7-4cbf-4d0c-b20c-7ab66ad52514.jpg',1628281691555536898,1628023694518472706,NULL,'不要香菜,中辣',1,'39.00'),(1628281691748474884,'麻辣鱼片','ffdbeb37-0fbe-4190-a52a-3a16478f366e.jpg',1628281691555536898,1628023133450620930,NULL,'不要蒜,中辣',1,'39.00'),(1628281691748474885,'宫保鸡丁','95f8b479-ae76-4107-9b08-3c62ae7f7fd0.jpg',1628281691555536898,1628021265471197185,NULL,NULL,1,'69.00'),(1628281691748474886,'麻辣兔头','703fb335-5593-49be-a629-e2522344212d.jpg',1628281691555536898,1628020414488858625,NULL,'去冰,中辣',1,'128.00'),(1628281691811389441,'商务套餐A','fb706fe0-b57f-46da-9f70-f209cc489f39.jpg',1628281691555536898,NULL,1628024830898032642,NULL,1,'99.00'),(1628302223755788290,'辣子鸡丁','c67657d7-4cbf-4d0c-b20c-7ab66ad52514.jpg',1628302223562850306,1628023694518472706,NULL,'不要蒜,微辣',1,'39.00'),(1628302223755788291,'商务套餐A','fb706fe0-b57f-46da-9f70-f209cc489f39.jpg',1628302223562850306,NULL,1628024830898032642,NULL,1,'99.00'),(1628302223755788292,'儿童套餐A','17fb2dcf-c06a-46c4-8ba6-8cb461d84031.jpg',1628302223562850306,NULL,1628024994765295618,NULL,1,'59.00');

/*Table structure for table `orders` */

DROP TABLE IF EXISTS `orders`;

CREATE TABLE `orders` (
  `id` bigint NOT NULL COMMENT '主键',
  `number` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '订单号',
  `status` int NOT NULL DEFAULT '1' COMMENT '订单状态 1待付款,2待派送,3已派送,4已完成,5已取消',
  `user_id` bigint NOT NULL COMMENT '下单用户',
  `address_book_id` bigint NOT NULL COMMENT '地址id',
  `order_time` datetime NOT NULL COMMENT '下单时间',
  `checkout_time` datetime NOT NULL COMMENT '结账时间',
  `pay_method` int NOT NULL DEFAULT '1' COMMENT '支付方式 1微信,2支付宝',
  `amount` decimal(10,2) NOT NULL COMMENT '实收金额',
  `remark` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '备注',
  `phone` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
  `address` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
  `user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
  `consignee` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='订单表';

/*Data for the table `orders` */

insert  into `orders`(`id`,`number`,`status`,`user_id`,`address_book_id`,`order_time`,`checkout_time`,`pay_method`,`amount`,`remark`,`phone`,`address`,`user_name`,`consignee`) values (1628281691555536898,'1628281691555536898',2,1627997218788163586,1628270733663694849,'2023-02-22 14:33:02','2023-02-22 14:33:02',1,'433.00','','17671789248','湖北工业大学',NULL,'金阳'),(1628302223562850306,'1628302223562850306',2,1627997218788163586,1628270733663694849,'2023-02-22 15:54:37','2023-02-22 15:54:37',1,'197.00','','17671789248','湖北工业大学',NULL,'金阳');

/*Table structure for table `setmeal` */

DROP TABLE IF EXISTS `setmeal`;

CREATE TABLE `setmeal` (
  `id` bigint NOT NULL COMMENT '主键',
  `category_id` bigint NOT NULL COMMENT '菜品分类id',
  `name` varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '套餐名称',
  `price` decimal(10,2) NOT NULL COMMENT '套餐价格',
  `status` int DEFAULT NULL COMMENT '状态 0:停用 1:启用',
  `code` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '编码',
  `description` varchar(512) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '描述信息',
  `image` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '图片',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '更新时间',
  `create_user` bigint NOT NULL COMMENT '创建人',
  `update_user` bigint NOT NULL COMMENT '修改人',
  `is_deleted` int NOT NULL DEFAULT '0' COMMENT '是否删除',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `idx_setmeal_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='套餐';

/*Data for the table `setmeal` */

insert  into `setmeal`(`id`,`category_id`,`name`,`price`,`status`,`code`,`description`,`image`,`create_time`,`update_time`,`create_user`,`update_user`,`is_deleted`) values (1628024830898032642,1413342269393674242,'商务套餐A','9900.00',1,'','商务套餐A','fb706fe0-b57f-46da-9f70-f209cc489f39.jpg','2023-02-21 21:32:22','2023-02-21 21:32:22',1627997218788163586,1627997218788163586,0),(1628024994765295618,1413386191767674881,'儿童套餐A','5900.00',1,'','儿童套餐A','17fb2dcf-c06a-46c4-8ba6-8cb461d84031.jpg','2023-02-21 21:33:01','2023-02-21 21:33:01',1627997218788163586,1627997218788163586,0);

/*Table structure for table `setmeal_dish` */

DROP TABLE IF EXISTS `setmeal_dish`;

CREATE TABLE `setmeal_dish` (
  `id` bigint NOT NULL COMMENT '主键',
  `setmeal_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '套餐id ',
  `dish_id` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '菜品id',
  `name` varchar(32) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '菜品名称 (冗余字段)',
  `price` decimal(10,2) DEFAULT NULL COMMENT '菜品原价(冗余字段)',
  `copies` int NOT NULL COMMENT '份数',
  `sort` int NOT NULL DEFAULT '0' COMMENT '排序',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '更新时间',
  `create_user` bigint NOT NULL COMMENT '创建人',
  `update_user` bigint NOT NULL COMMENT '修改人',
  `is_deleted` int NOT NULL DEFAULT '0' COMMENT '是否删除',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='套餐菜品关系';

/*Data for the table `setmeal_dish` */

insert  into `setmeal_dish`(`id`,`setmeal_id`,`dish_id`,`name`,`price`,`copies`,`sort`,`create_time`,`update_time`,`create_user`,`update_user`,`is_deleted`) values (1628024830960947201,'1628024830898032642','1628023694518472706','辣子鸡丁','3900.00',1,0,'2023-02-21 21:32:22','2023-02-21 21:32:22',1627997218788163586,1627997218788163586,0),(1628024830960947202,'1628024830898032642','1628023490318782465','大米饭','500.00',1,0,'2023-02-21 21:32:22','2023-02-21 21:32:22',1627997218788163586,1627997218788163586,0),(1628024830960947203,'1628024830898032642','1628023021122965506','啤酒','1000.00',1,0,'2023-02-21 21:32:22','2023-02-21 21:32:22',1627997218788163586,1627997218788163586,0),(1628024830960947204,'1628024830898032642','1628023363927625729','烤乳鸽','7900.00',1,0,'2023-02-21 21:32:22','2023-02-21 21:32:22',1627997218788163586,1627997218788163586,0),(1628024994832404481,'1628024994765295618','1628023490318782465','大米饭','500.00',1,0,'2023-02-21 21:33:01','2023-02-21 21:33:01',1627997218788163586,1627997218788163586,0),(1628024994832404482,'1628024994765295618','1628022523112280066','王老吉','500.00',1,0,'2023-02-21 21:33:01','2023-02-21 21:33:01',1627997218788163586,1627997218788163586,0),(1628024994832404483,'1628024994765295618','1628022918689673218','清炒素食','1900.00',1,0,'2023-02-21 21:33:01','2023-02-21 21:33:01',1627997218788163586,1627997218788163586,0),(1628024994832404484,'1628024994765295618','1628023363927625729','烤乳鸽','7900.00',1,0,'2023-02-21 21:33:01','2023-02-21 21:33:01',1627997218788163586,1627997218788163586,0);

/*Table structure for table `shopping_cart` */

DROP TABLE IF EXISTS `shopping_cart`;

CREATE TABLE `shopping_cart` (
  `id` bigint NOT NULL COMMENT '主键',
  `name` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '名称',
  `image` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '图片',
  `user_id` bigint NOT NULL COMMENT '主键',
  `dish_id` bigint DEFAULT NULL COMMENT '菜品id',
  `setmeal_id` bigint DEFAULT NULL COMMENT '套餐id',
  `dish_flavor` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '口味',
  `number` int NOT NULL DEFAULT '1' COMMENT '数量',
  `amount` decimal(10,2) NOT NULL COMMENT '金额',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='购物车';

/*Data for the table `shopping_cart` */

/*Table structure for table `user` */

DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (
  `id` bigint NOT NULL COMMENT '主键',
  `name` varchar(50) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '姓名',
  `phone` varchar(100) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '手机号',
  `sex` varchar(2) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '性别',
  `id_number` varchar(18) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '身份证号',
  `avatar` varchar(500) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '头像',
  `status` int DEFAULT '0' COMMENT '状态 0:禁用,1:正常',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin COMMENT='用户信息';

/*Data for the table `user` */

insert  into `user`(`id`,`name`,`phone`,`sex`,`id_number`,`avatar`,`status`) values (1627997218788163586,NULL,'17612349248',NULL,NULL,NULL,1);

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

数据表

Maven项目搭建

项目的目录结构

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.5</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>
  <groupId>com.jerry</groupId>
  <artifactId>reggie</artifactId>
  <version>1.0</version>
  <properties>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
      <scope>compile</scope>
    </dependency>

    <dependency>
      <groupId>com.baomidou</groupId>
      <artifactId>mybatis-plus-boot-starter</artifactId>
      <version>3.4.2</version>
    </dependency>

    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.20</version>
    </dependency>

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.76</version>
    </dependency>

    <dependency>
      <groupId>commons-lang</groupId>
      <artifactId>commons-lang</artifactId>
      <version>2.6</version>
    </dependency>

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <scope>runtime</scope>
    </dependency>

    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid-spring-boot-starter</artifactId>
      <version>1.1.23</version>
    </dependency>

  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <version>2.4.5</version>
      </plugin>
    </plugins>
  </build>
</project>

application.yml

server:
  port: 8080
spring:
  application:
    # 应用名称,可选项
    name: reggie
  datasource:
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/reggie?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
      username: root
      password: root
mybatis-plus:
  configuration:
    #在映射实体或者属性时,将数据库中表名和字段名中的下划线去掉,按照驼峰命名法映射
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      id-type: ASSIGN_ID

ReggieApplication启动类

package com.jerry.reggie;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * ClassName: ReggieApplication
 * Package: com.jerry.reggie
 * Description:
 *
 * @Author jerry_jy
 * @Create 2023-02-16 13:50
 * @Version 1.0
 */
@Slf4j
@SpringBootApplication
public class ReggieApplication {
    public static void main(String[] args) {
        SpringApplication.run(ReggieApplication.class, args);
        log.info("项目启动成功...");
    }
}

配置静态资源映射

SpringBoot访问静态资源默认会去resources/static或resources/templates目录下,如果不需要static或templates目录,那就手动使用配置类进行配置访问路径

package com.jerry.reggie.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

/**
 * ClassName: WebMvcConfig
 * Package: com.jerry.reggie.config
 * Description:
 *
 * @Author jerry_jy
 * @Create 2023-02-16 14:16
 * @Version 1.0
 */
@Slf4j
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
    /**
     * 设置静态资源映射
     * @param registry
     */
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        log.info("开始进行静态资源的映射...");
        registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");
        registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
    }
}

访问:http://localhost:8080/backend/index.html

4、登录功能

4.1、后台登录功能

需求分析

代码编写

vo类:将服务器和前端页面传递的数据封装好

R类是一个通用结果类,服务端响应的所有结果最终都会包装成此种类型返回给前端页面

R.java

package com.jerry.reggie.common;

import lombok.Data;
import java.util.HashMap;
import java.util.Map;

/**
 * 通用返回结果,服务器端响应的数据最终都会封装成此对象
 * @param <T>
 */
@Data
public class R<T> {

    private Integer code; //编码:1成功,0和其它数字为失败

    private String msg; //错误信息

    private T data; //数据

    private Map map = new HashMap(); //动态数据

    public static <T> R<T> success(T object) {
        R<T> r = new R<T>();
        r.data = object;
        r.code = 1;
        return r;
    }

    public static <T> R<T> error(String msg) {
        R r = new R();
        r.msg = msg;
        r.code = 0;
        return r;
    }

    public R<T> add(String key, Object value) {
        this.map.put(key, value);
        return this;
    }

}

实体类

package com.jerry.reggie.entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;

/**
 * 员工实体类
 */
@Data
public class Employee implements Serializable {

    private static final long serialVersionUID = 1L;

    private Long id;

    private String username;

    private String name;

    private String password;

    private String phone;

    private String sex;

    private String idNumber;//身份证号

    private Integer status;

    private LocalDateTime createTime;

    private LocalDateTime updateTime;

    @TableField(fill = FieldFill.INSERT)
    private Long createUser;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;

}

EmployeeMapper

package com.jerry.reggie.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jerry.reggie.entity.Employee;
import org.apache.ibatis.annotations.Mapper;

/**
 * ClassName: EmployeeMApper
 * Package: com.jerry.reggie.mapper
 * Description:
 *
 * @Author jerry_jy
 * @Create 2023-02-16 14:45
 * @Version 1.0
 */
@Mapper
public interface EmployeeMapper extends BaseMapper<Employee> {
}

EmployeeService

package com.jerry.reggie.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.jerry.reggie.entity.Employee;

/**
 * ClassName: EmployeeService
 * Package: com.jerry.reggie.service
 * Description:
 *
 * @Author jerry_jy
 * @Create 2023-02-16 14:46
 * @Version 1.0
 */
public interface EmployeeService extends IService<Employee> {
}

EmployeeServiceImpl

package com.jerry.reggie.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jerry.reggie.entity.Employee;
import com.jerry.reggie.mapper.EmployeeMapper;
import com.jerry.reggie.service.EmployeeService;
import org.springframework.stereotype.Service;

/**
 * ClassName: EmployeeServiceImpl
 * Package: com.jerry.reggie.service.impl
 * Description:
 *
 * @Author jerry_jy
 * @Create 2023-02-16 14:46
 * @Version 1.0
 */

@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements EmployeeService {
}

EmployeeController

package com.jerry.reggie.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jerry.reggie.common.R;
import com.jerry.reggie.entity.Employee;
import com.jerry.reggie.service.EmployeeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpRequest;
import org.springframework.stereotype.Controller;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;

/**
 * ClassName: EmployeeController
 * Package: com.jerry.reggie.controller
 * Description:
 *
 * @Author jerry_jy
 * @Create 2023-02-16 14:52
 * @Version 1.0
 */
@Slf4j
@RestController
@RequestMapping("/employee")
public class EmployeeController {

    @Autowired
    private EmployeeService employeeService;

    @PostMapping("/login")
    public R<Employee> login(@RequestBody Employee employee, HttpServletRequest request) {
        //@RequestBody用来接收前端传递给后端的`json`字符串中的数据的(请求体中的数据的),所以前端只能发送POST请求

        //1、将页面提交的密码password进行md5加密处理
        String pwd = employee.getPassword();
        pwd = DigestUtils.md5DigestAsHex(pwd.getBytes());

        // 2、根据页面提交的用户名username查询数据库]
        /**
         * 我自己写的是
         *         QueryWrapper<Employee> queryWrapper = new QueryWrapper<>();
         *         employeeService.getOne(queryWrapper.select(employee.getName()));
         * 查询出来的是null值
         */
        LambdaQueryWrapper<Employee> wrapper = new LambdaQueryWrapper<>();
        //方法引用的语法格式(语法糖)
        wrapper.eq(Employee::getUsername,employee.getUsername());
        Employee emp = employeeService.getOne(wrapper);

        // 3、如果没有查询到则返回登录失败结果
        if (emp == null) {
            return R.error("登录失败");
        }
        //4、密码比对,如果不一致则返回登录失败结果
        if (!pwd.equals(emp.getPassword())) {
            return R.error("登录失败");
        }

        //5、查看员工状态,如果为已禁用状态,则返回员工已禁用结果
        if (emp.getStatus()!=1){
            return R.error("员工账号已禁用");
        }

        // 6、登录成功,将员工id存入Session并返回登录成功结果
        Long empId = emp.getId();
        request.getSession().setAttribute("empId",empId);
        return R.success(emp);
    }
}

页面展示

http://localhost:8080/backend/page/login/login.html

http://localhost:8080/backend/index.html

4.2、后台登出功能

需求分析

代码编写

/**
 * 员工后台登出功能
 * @param request
 * @return
 */
@PostMapping("/logout")
public R<String> logout(HttpServletRequest request) {
    //1、清理Session中的用户id
    request.getSession().removeAttribute("empId");
    // 2、返回结果
    return R.success("登出成功");
}

页面展示

4.3、完善登录功能

问题分析

代码编写

LoginCheckFilter

package com.jerry.reggie.filter;

import com.alibaba.fastjson.JSON;
import com.jerry.reggie.common.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.AntPathMatcher;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * ClassName: LoginCheckFilter
 * Package: com.jerry.reggie.filter
 * Description:
 *
 * @Author jerry_jy
 * @Create 2023-02-16 21:50
 * @Version 1.0
 */
@Slf4j
@WebFilter(filterName = "loginCheckFilter", urlPatterns = "/*")
public class LoginCheckFilter implements Filter {
    //路径匹配,支持通配符
    public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;


        //1、获取本次请求的URI
        String uri = request.getRequestURI(); //   backend/index.html
        log.info("拦截到请求:{}", uri);

        //定义不需要处理的请求路径
        String[] urls = new String[]{
                "/employee/login",
                "employee/logout",
                "/backend/**",
                "/front/**"
        };
        //2、判断本次请求是否需要处理
        boolean check = check(urls, uri);

        // 3、如果不需要处理,则直接放行
        if (check == true) {
            log.info("本次请求{}不需要处理" + uri);
            filterChain.doFilter(request, response);
            return;
        }

        //4、判断登录状态,如果已登录,则直接放行
        if (request.getSession().getAttribute("empId") != null) {
            log.info("用户已登录,用户id为:{}",request.getSession().getAttribute("empId"));
            filterChain.doFilter(request, response);
            return;
        }

        log.info("用户未登录");
        // 5、如果未登录则返回未登录结果,通过输出流方式向客户端响应数据
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
        return;
    }

    /**
     * 路径匹配,检查本次请求是否需要放行
     *
     * @param urls
     * @param uri
     * @return
     */
    public boolean check(String[] urls, String uri) {
        for (String url : urls) {
            boolean match = PATH_MATCHER.match(url, uri);
            if (match) {
                return true;
            }
        }
        return false;
    }
}

5、员工管理

5.1、新增员工

需求分析

数据模型

代码开发

代码编写

/**
 * 新增员工
 * @param employee
 * @return
 */
@PostMapping
public R<String> save(@RequestBody Employee employee, HttpServletRequest request){
    log.info("新增员工,员工信息:{}",employee.toString());

    //设置员工的初始密码,需要进行MD5 加密处理
    employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));


    employee.setCreateTime(LocalDateTime.now());
    employee.setUpdateTime(LocalDateTime.now());

    //获得当前登录对象的id
    Long empId = (Long) request.getSession().getAttribute("empId");
    employee.setCreateUser(empId);
    employee.setUpdateUser(empId);

    employeeService.save(employee);
    return R.success("添加员工成功");
}

异常捕获

GlobalExceptionHandler

package com.jerry.reggie.common;

/**
 * ClassName: GlobalExceptionHandler
 * Package: com.jerry.reggie.common
 * Description:
 *
 * @Author jerry_jy
 * @Create 2023-02-18 16:21
 * @Version 1.0
 */

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.sql.SQLIntegrityConstraintViolationException;

/**
 * 全局异常捕获
 */
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody //要返回json数据时就写
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 异常处理方法
     * @return
     */
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public R<String> exceptionHandler(SQLIntegrityConstraintViolationException exception){
        log.error(exception.getMessage());

        if (exception.getMessage().contains("Duplicate entry")){
            String[] strings = exception.getMessage().split(" ");
            String msg = strings[2] + "已存在";
            return R.error(msg);
        }
        return R.error("未知错误");
    }
}

小结

5.2、员工信息分页查询

需求分析

代码开发

MyBatisPlusConfig配置分页插件

package com.jerry.reggie.config;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * ClassName: MyBatisPlusConfig
 * Package: com.jerry.reggie.config
 * Description:
 *
 * @Author jerry_jy
 * @Create 2023-02-18 17:16
 * @Version 1.0
 */

/**
 * 配置MP的分页插件
 */
@Configuration
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mybatisPlusInterceptor;
    }
}
/**
 * 员工信息分页查询
 *
 * @param page
 * @param pageSize
 * @param name
 * @return
 */
@GetMapping("/page")
public R<Page> page(int page, int pageSize, String name) {
    log.info("page = {}, pageSize = {}, name = {}", page, pageSize, name);

    //这里:只需要new page对象和构造好lambdaQueryWrapper
    //构造分页构造器
    Page<Employee> pageInfo = new Page<>(page, pageSize);

    //构造条件构造器
    LambdaQueryWrapper<Employee> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    //添加一个过滤条件
    lambdaQueryWrapper.like(StringUtils.isNotEmpty(name), Employee::getName, name);
    //添加一个排序条件
    lambdaQueryWrapper.orderByDesc(Employee::getUpdateTime);
    //执行查询
    employeeService.page(pageInfo, lambdaQueryWrapper);

    return R.success(pageInfo);
}

5.3、启用/禁用员工账号

需求分析

代码编写

编写一个通用的update方法

/**
 * 根据id修改员工信息
 *
 * @param employee
 * @return
 */
@PutMapping
public R<String> update(HttpServletRequest request, @RequestBody Employee employee) {
    log.info(employee.toString());
    Long empId = (Long) request.getSession().getAttribute("empId");
    employee.setUpdateTime(LocalDateTime.now());
    employee.setUpdateUser(empId);
    employeeService.updateById(employee);
    return R.success("更新成功");
}

功能测试

原因:JS在处理Long型数据时,只能处理16位,也就是说,2^53次方,超过后就四舍五入,精度损失

功能修复

/**
 * 扩展mvc消息框架的转换器
 * @param converters
 */
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    log.info("扩展消息转换器...");
    //创建消息转换器对象
    MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
    //设置对象转换器,底层使用Jackson将Java对象转为json
    messageConverter.setObjectMapper(new JacksonObjectMapper());
    //将上面的消息转换器对象追加到mvc框架的转换器集合中
    converters.add(0, messageConverter);
}

5.3、编辑员工

需求分析

代码编写

/**
 * 根据id查询员工信息
 *
 * @param id
 * @return
 */

@GetMapping("/{id}")
public R<Employee> getById(@PathVariable Long id) {
    log.info("根据id 查询员工信息...");
    Employee employee = employeeService.getById(id);
    if (employee != null) {

        return R.success(employee);
    }
    return R.error("没有查询到对应的员工信息");
}

5.4、公共字段自动填充

问题分析

代码实现

功能完善

ThreadLocal

BaseContext

package com.jerry.reggie.common;

/**
 * ClassName: BaseContext
 * Package: com.jerry.reggie.common
 * Description:
 *
 * @Author jerry_jy
 * @Create 2023-02-19 9:00
 * @Version 1.0
 */

/**
 * 基于ThreadLocal封装的工具类,用于保存和获取当前登录用户的id
 */
public class BaseContext {
    private static ThreadLocal<Long> threadLocal= new ThreadLocal<>();

    public static void setCurrentId(Long id){
        threadLocal.set(id);
    }

    public static Long getCurrentId(){
        return threadLocal.get();
    }
}

6、分类管理

6.1、新增菜品分类

需求分析

数据模型

代码开发

6.1、新增菜品的分页查询

需求分析

代码开发

/**
 * 菜品分页查询
 * @param page
 * @param pageSize
 * @return
 */
@GetMapping("/page")
public R<Page> page(int page, int pageSize){
    //分页构造器
    Page<Category> categoryPage = new Page<>();
    //条件构造器
    LambdaQueryWrapper<Category> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        //添加排序条件
    lambdaQueryWrapper.orderByAsc(Category::getSort);

    categoryService.page(categoryPage, lambdaQueryWrapper);

    return R.success(categoryPage);
}

6.2、删除分类

需求分析

代码开发

功能完善

GlobalExceptionHandler

/**
 * 异常处理方法
 * @return
 */
@ExceptionHandler(CustomException.class)
public R<String> exceptionHandler(CustomException exception){
    log.error(exception.getMessage());

    return R.error(exception.getMessage());
}

CustomException

package com.jerry.reggie.common;

/**
 * ClassName: CustomException
 * Package: com.jerry.reggie.common
 * Description:
 *
 * @Author jerry_jy
 * @Create 2023-02-19 11:55
 * @Version 1.0
 */

/**
 * 自定义业务异常类
 */
public class CustomException extends RuntimeException{
    public CustomException(String message){
        super(message);
    }
}

CategoryServiceImpl

package com.jerry.reggie.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jerry.reggie.common.CustomException;
import com.jerry.reggie.entity.Category;
import com.jerry.reggie.entity.Dish;
import com.jerry.reggie.entity.Setmeal;
import com.jerry.reggie.mapper.CategoryMapper;
import com.jerry.reggie.service.CategoryService;
import com.jerry.reggie.service.DishService;
import com.jerry.reggie.service.SetmealService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * ClassName: CategoryServiceImpl
 * Package: com.jerry.reggie.service.impl
 * Description:
 *
 * @Author jerry_jy
 * @Create 2023-02-19 9:30
 * @Version 1.0
 */
@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {
    @Autowired
    private DishService dishService;

    @Autowired
    private SetmealService setmealService;
    /**
     * 根据id删除分类,删除之前需要进行判断
     * @param id
     */
    @Override
    public void remove(Long id) {
        LambdaQueryWrapper<Dish> dishLambdaQueryWrapper = new LambdaQueryWrapper<>();
        //添加查询条件,根据分类id查询
        dishLambdaQueryWrapper.eq(Dish::getCategoryId,id);
        int count1 = dishService.count(dishLambdaQueryWrapper);
        //查询当前分类是否关联了菜品,如果已经关联,抛出一个业务异常
        if (count1>0){
            //已经关联,抛出一个业务异常
            throw new CustomException("当前分类下关联了菜品,不能删除");
        }

        //查询当前分类是否关联了套餐,如果已经关联,抛出一个业务异常
        LambdaQueryWrapper<Setmeal> setmealLambdaQueryWrapper = new LambdaQueryWrapper<>();
        setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId,id);
        int count2 = setmealService.count(setmealLambdaQueryWrapper);
        if (count2>0){
            //已经关联套餐,抛出一个业务异常
            throw new CustomException("当前分类下关联了套餐,不能删除");
        }
        //正常删除分类
        super.removeById(id);

    }
}

    /**
     * 根据id删除分类
     * @param id
     * @return
     */
    @DeleteMapping
    public R<String> delete(Long id){
        log.info("删除分类:{}",id);

//        categoryService.removeById(id);
        categoryService.remove(id);

        return R.success("分类信息删除成功");
    }

6.3、修改分类

需求分析

代码编写

/**
 * 根据id修改分类信息
 * @param category
 * @return
 */
@PutMapping
public R<String> update(@RequestBody Category category){
    log.info("修改分类信息:{}",category);
    categoryService.updateById(category);
    return R.success("修改分类信息成功");
}

7、菜品管理

7.1、文件上传下载

文件上传介绍

文件下载介绍

文件上传代码实现

package com.jerry.reggie.controller;

import com.jerry.reggie.common.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.UUID;

/**
 * ClassName: CommonController
 * Package: com.jerry.reggie.controller
 * Description:
 *
 * @Author jerry_jy
 * @Create 2023-02-19 17:19
 * @Version 1.0
 */

/**
 * 文件上传下载
 */
@RestController
@Slf4j
@RequestMapping("/common")
public class CommonController {
    @Value("${reggie.path}")//这里的value不要导包成了lombok,用${}动态取值
    private String basePath;

    /**
     * 文件上传
     *
     * @param file
     * @return
     */
    @PostMapping("/upload")
    public R<String> uploadFile(MultipartFile file) {
        //file是临时文件,需要转存到指定位置,否则本次请求完成后临时文件会删除
        log.info(file.toString());

        //获取原始文件名
        String originalFilename = file.getOriginalFilename();
        //获取原始文件名的后缀名,这里是带点的
        String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));

        //使用UUID重新生成文件名,防止文件名称重复造成的文件覆盖
        String fileName = UUID.randomUUID().toString() + suffix;

        //创建一个目录对象
        File dir = new File(basePath);
        if (!dir.exists()){
            //目录不存在,创建一个
            dir.mkdirs();
        }

        try {
            //将临时文件转存到指定位置
            file.transferTo(new File(basePath + fileName));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return R.success(fileName);
    }
}

文件下载代码实现

/**
 * 文件下载
 *
 * @param name
 * @param response
 */
@GetMapping("/download")
public void download(String name, HttpServletResponse response) {

    try {
        // 输入流,通过输入流读取文件内容
        FileInputStream fileInputStream = new FileInputStream(new File(basePath + name));

        //输出流,通过输出流将文件写回浏览器,在浏览器显示图片
        ServletOutputStream outputStream = response.getOutputStream();

        //设置输出流的类型为图片
        response.setContentType("image/jpeg");

        int len = 0;
        byte[] bytes = new byte[1024];
        while ((len = fileInputStream.read(bytes)) != -1) {
           outputStream.write(bytes, 0, len);
           outputStream.flush();
        }
        // 关闭资源
        outputStream.close();
        fileInputStream.close();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }

}

7.2、新增菜品

需求分析

数据模型

代码开发

由于新增菜品涉及到多张表的插入操作,因此需要在Service业务层中单独写一个save方法

DishServiceImpl

package com.jerry.reggie.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jerry.reggie.dto.DishDto;
import com.jerry.reggie.entity.Dish;
import com.jerry.reggie.entity.DishFlavor;
import com.jerry.reggie.mapper.DishFlavorMapper;
import com.jerry.reggie.mapper.DishMapper;
import com.jerry.reggie.service.DishFlavorService;
import com.jerry.reggie.service.DishService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

/**
 * ClassName: DishServiceImpl
 * Package: com.jerry.reggie.service.impl
 * Description:
 *
 * @Author jerry_jy
 * @Create 2023-02-19 10:53
 * @Version 1.0
 */
@Service
@Slf4j
public class DishServiceImpl extends ServiceImpl<DishMapper, Dish> implements DishService {
    @Autowired
    private DishFlavorService dishFlavorService;

    /**
     * 新增菜品同时保存口味数据
     * @param dishDto
     */
    @Transactional
    @Override
    public void saveWithFlavor(DishDto dishDto) {
        //保存菜品基本信息到菜品表dish
        this.save(dishDto);

        Long dishId = dishDto.getId();

        //菜品口味
        List<DishFlavor> flavors = dishDto.getFlavors();
        flavors = flavors.stream().map((item) -> {
            item.setDishId(dishId);
            return item;
        }).collect(Collectors.toList());

        //保存菜品口味到到菜品口味表dish_flavor
        dishFlavorService.saveBatch(flavors);
    }
}

DishController

package com.jerry.reggie.controller;

import com.jerry.reggie.common.R;
import com.jerry.reggie.dto.DishDto;
import com.jerry.reggie.entity.Dish;
import com.jerry.reggie.service.DishFlavorService;
import com.jerry.reggie.service.DishService;
import lombok.extern.java.Log;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * ClassName: DishController
 * Package: com.jerry.reggie.controller
 * Description:
 *
 * @Author jerry_jy
 * @Create 2023-02-19 19:11
 * @Version 1.0
 */
@Slf4j
@RestController
@RequestMapping("/dish")
public class DishController {
    @Autowired
    DishFlavorService dishFlavorService;

    @Autowired
    DishService dishService;

    /**
     * 新增菜品
     * @param dishDto
     * @return
     */
    @PostMapping
    public R<String> addMeal(@RequestBody DishDto dishDto){
        log.info(dishDto.toString());

        dishService.saveWithFlavor(dishDto);
        return R.success("保存成功");
    }
}

7.3、菜品信息分页查询

需求分析

代码开发

难点

这里的分页查询涉及到2个对象的拷贝复赋值问题,使用BeanUtils.copyProperties()来完成操作的

把Dish对象赋值给DishDto对象,并设置上categoryName

流式处理的表达式是重点!!!

/**
 * 菜品信息--分页查询
 *
 * @param page
 * @param pageSize
 * @param name
 * @return
 */
@GetMapping("/page")
public R<Page> page(int page, int pageSize, String name) {
    //分页构造器
    Page<Dish> pageInfo = new Page<>(page, pageSize);
    Page<DishDto> dishDtoPage = new Page<>();
    //条件构造器
    LambdaQueryWrapper<Dish> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    //添加过滤条件
    lambdaQueryWrapper.like(name != null, Dish::getName, name);
    //添加过滤条件
    lambdaQueryWrapper.orderByDesc(Dish::getUpdateTime);

    dishService.page(pageInfo, lambdaQueryWrapper);

    //对象拷贝
    BeanUtils.copyProperties(pageInfo, dishDtoPage,"records");
    List<Dish> records = pageInfo.getRecords();
    List<DishDto> list =  records.stream().map((item) -> {
        DishDto dishDto = new DishDto();

        //对象拷贝
        BeanUtils.copyProperties(item, dishDto);

        Long categoryId = item.getCategoryId();//分类id
        Category category = categoryService.getById(categoryId);//分类对象

        if (category!=null){
            String categoryName = category.getName();
            dishDto.setCategoryName(categoryName);
        }
        
        return dishDto;
    }).collect(Collectors.toList());

    dishDtoPage.setRecords(list);
    return R.success(dishDtoPage);
}

7.4、修改菜品

需求分析

代码开发

controller

/**
 *
 * @param dishDto
 * @return
 */
@PutMapping
public R<String> updateMeal(@RequestBody DishDto dishDto){
    log.info(dishDto.toString());

    dishService.updateWithFlavor(dishDto);
    return R.success("保存菜品成功");
}

DishServiceImpl

/**
 * 更新菜品信息,同时更新对应的口味信息
 *
 * @param dishDto
 */
@Override
@Transactional
public void updateWithFlavor(DishDto dishDto) {
    //更新dish表基本信息
    this.updateById(dishDto);

    //先清理当前菜品对应的口味信息---dish_flavor表的 delete 操作
    LambdaQueryWrapper<DishFlavor> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    lambdaQueryWrapper.eq(DishFlavor::getDishId, dishDto.getId());
    dishFlavorService.remove(lambdaQueryWrapper);

    //再添加当前提交过来的口味数据--dish_flavor表的 insert 操作
    List<DishFlavor> flavors = dishDto.getFlavors();

    flavors = flavors.stream().map((item) -> {
        item.setDishId(dishDto.getId());
        return item;
    }).collect(Collectors.toList());

    dishFlavorService.saveBatch(flavors);
}

7.5、修改菜品的停/起售状态

DishController

    /**
     * 修改菜品的 停/启 售状态
     *
     * @param ids
     * @return
     */
    @PostMapping("/status/{status}")
    public R<Dish> status(long ids) {
        Dish dish = dishService.getById(ids);
        dishService.setStatus(dish);
        return R.success(dish);
    }

DishServiceImpl

/**
 * 修改菜品的停/启售状态
 * @param dish
 */
@Override
public void setStatus(Dish dish) {
    if (dish.getStatus()==1){
        dish.setStatus(0);
    }else {
        dish.setStatus(1);
    }
    this.updateById(dish);
}

7.6、删除菜品

    /**
     * 菜品管理--批量删除菜品--跟单个删除一样的,复用同一份代码
     * @param ids
     * @return
     */
    @DeleteMapping
    public R<String> delete(Long[] ids){
        for (Long id : ids) {
            dishService.delete(id);
        }
        return R.success("删除菜品成功!");
    }

8、套餐管理

8.1、新增套餐

需求分析

数据模型

代码开发

DishController

/**
 * 根据条件来查询对应的菜品数据
 * @param dish
 * @return
 */
@GetMapping("/list")
public R<List<Dish>> list(Dish dish){
    //构造查询条件
    LambdaQueryWrapper<Dish> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    lambdaQueryWrapper.eq(dish.getCategoryId()!=null,Dish::getCategoryId,dish.getCategoryId());
    //查询状态为1,启售状态
    lambdaQueryWrapper.eq(Dish::getStatus,1);
    //添加排序条件
    lambdaQueryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);

    List<Dish> list = dishService.list(lambdaQueryWrapper);
    return R.success(list);
}

SetmealDishController

/**
 * 新增套餐
 * @param setmealDto
 * @return
 */
@PostMapping
public R<String> save(@RequestBody SetmealDto setmealDto){

    log.info("套餐信息:{}",setmealDto.toString());
    setmealService.saveWithDish(setmealDto);
    return R.success("新增套餐成功");
}

SetmealServiceImpl

/**
 * 新增套餐,同时需要保存套餐和菜品的关联关系
 * @param setmealDto
 */
@Override
@Transactional
public void saveWithDish(SetmealDto setmealDto) {
    //保存套餐的基本信息 操作setmeal  执行insert
    this.save(setmealDto);

    List<SetmealDish> setmealDishes = setmealDto.getSetmealDishes();
    setmealDishes.stream().map((item)->{
        item.setSetmealId(setmealDto.getId());
        return item;
    }).collect(Collectors.toList());

    //保存套餐和菜品的关联信息,操作setmeal_dish,执行insert
    setmealDishService.saveBatch(setmealDishes);
}

8.2、套餐信息分页查询

需求分析

代码开发

/**
 * 套餐管理--套餐信息分页查询
 * @param page
 * @param pageSize
 * @param name
 * @return
 */
@GetMapping("/page")
public R<Page> page(int page, int pageSize, String name) {
    //分页构造器
    Page<Setmeal> pageInfo = new Page<>(page, pageSize);
    Page<SetmealDto> dtoPage = new Page<>();

    //条件构造器
    LambdaQueryWrapper<Setmeal> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    //添加过滤条件
    lambdaQueryWrapper.like(name != null, Setmeal::getName, name);
    //添加过滤条件
    lambdaQueryWrapper.orderByDesc(Setmeal::getUpdateTime);

    setmealService.page(pageInfo, lambdaQueryWrapper);

    //对象拷贝
    BeanUtils.copyProperties(pageInfo, dtoPage, "records");
    List<Setmeal> records = pageInfo.getRecords();

    List<SetmealDto> list = records.stream().map((item) -> {
        SetmealDto setmealDto = new SetmealDto();

        //对象拷贝
        BeanUtils.copyProperties(item, setmealDto);

        //分类id
        Long categoryId = item.getCategoryId();
        //根据分类id查询对象
        Category category = categoryService.getById(categoryId);
        if (category != null) {
            //获取分类名称
            String categoryName = category.getName();
            setmealDto.setCategoryName(categoryName);
        }
        return setmealDto;
    }).collect(Collectors.toList());

    dtoPage.setRecords(list);

    return R.success(dtoPage);
}

8.3、删除套餐

需求分析

代码开发

SetmealController

/**
 * (批量)删除套餐
 *
 * @param ids
 * @return
 */
@DeleteMapping
public R<String> delete(@RequestParam List<Long> ids) {
    log.info("id: {}", ids);
    setmealService.removeWithDish(ids);
    return R.success("套餐数据删除成功");
}

SetmealServiceImpl

/**
 * 删除套餐,同时输出套餐和菜品关联的数据
 *
 * @param ids
 */
@Override
public void removeWithDish(List<Long> ids) {
    // 先查询套餐状态,确实是否可以删除
    // select count(*) from setmeal where id in (1, 2, 3) and status = 1;
    LambdaQueryWrapper<Setmeal> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    lambdaQueryWrapper.in(Setmeal::getId, ids);
    lambdaQueryWrapper.eq(Setmeal::getStatus, 1);

    int count = this.count(lambdaQueryWrapper);
    if (count > 0) {
        // 如果不能删除,抛出一个业务异常
        throw new CustomException("套餐正在售卖中,不能删除");
    }


    // 如果可以删除,先删除套餐表中的数据-- setmeal
    this.removeByIds(ids);

    //再删除关系表中的数据-- setmeal_dish
    // delete from setmeal_dish where id in (1, 2, 3);
    LambdaQueryWrapper<SetmealDish> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.in(SetmealDish::getSetmealId, ids);
    setmealDishService.remove(queryWrapper);
}

8.4、修改套餐

SetmealController

    /**
     * 根据id查询套餐信息和对应的套餐内容---回显套餐信息
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    public R<SetmealDto> getMealDtoById(@PathVariable long id){
        SetmealDto setmealDto = setmealService.getByIdWithSetmeal(id);
        return R.success(setmealDto);
    }

    /**
     * 修改套餐信息,并保存
     * @param setmealDto
     * @return
     */
    @PutMapping
    public R<String> updateSetmeal(@RequestBody SetmealDto setmealDto) {
        log.info(setmealDto.toString());

        setmealService.updateWithSetmeal(setmealDto);
        return R.success("保存菜品成功");
    }

SetmealService

//根据id查询套餐信息和对应的套餐内容---回显套餐信息
SetmealDto getByIdWithSetmeal(long id);

//修改套餐信息,并保存
void updateWithSetmeal(SetmealDto setmealDto);

SetmealServiceImpl

    /**
     * 根据id查询套餐信息和对应的套餐内容---回显套餐信息
     *
     * @param id
     * @return
     */
    @Override
    public SetmealDto getByIdWithSetmeal(long id) {
        //查询套餐基本信息,从setmeal表查询
        Setmeal setMeal = this.getById(id);
        SetmealDto setmealDto = new SetmealDto();
        BeanUtils.copyProperties(setMeal, setmealDto);

        //查询当前套餐对应的套餐信息,从setmeal_dish表中查
        LambdaQueryWrapper<SetmealDish> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(SetmealDish::getSetmealId, setMeal.getId());
        List<SetmealDish> setmealDishes = setmealDishService.list(lambdaQueryWrapper);

        setmealDto.setSetmealDishes(setmealDishes);
        return setmealDto;
    }

    /**
     * 修改套餐信息,并保存
     *
     * @param setmealDto
     */
    @Override
    @Transactional
    public void updateWithSetmeal(SetmealDto setmealDto) {
        //更新 setmeal 表基本信息
        this.updateById(setmealDto);

        //先清理当前套餐对应的套餐信息--- setmeal_dish 表的 delete 操作
        LambdaQueryWrapper<SetmealDish> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(SetmealDish::getSetmealId, setmealDto.getId());
        setmealDishService.remove(lambdaQueryWrapper);

        //再添加当前提交过来的套餐数据-- setmeal_dish表的 insert 操作
        List<SetmealDish> setmealDishes = setmealDto.getSetmealDishes();
        setmealDishes = setmealDishes.stream().map((item) -> {
            item.setSetmealId(setmealDto.getId());
            return item;
        }).collect(Collectors.toList());

        setmealDishService.saveBatch(setmealDishes);
    }

8.5、停售、启售套餐

SetmealController

/**
 * 修改套餐的 停/启 售状态
 * @param ids
 * @return
 */
@PostMapping("/status/{status}")
public R<Setmeal> status(long ids) {
    Setmeal setmeal = setmealService.getById(ids);
    setmealService.setStatus(setmeal);
    return R.success(setmeal);
}

SetmealServiceImpl

/**
 * 修改套餐的 停/启 售状态
 * @param setmeal
 */
@Override
public void setStatus(Setmeal setmeal) {
    if (setmeal.getStatus() == 1) {
        setmeal.setStatus(0);
    } else {
        setmeal.setStatus(1);
    }
    this.updateById(setmeal);
}

9、前端–手机验证码登录

9.1、短信发送

阿里云短信服务

https://www.aliyun.com/product/sms?spm=5176.19720258.J_3207526240.37.4cf376f4PiAUnY

https://dysms.console.aliyun.com/quickstart?spm=5176.25163407.domtextsigncreate-index-1ec3c_58c50_0.1.5097bb6euk5OnF

设置签名

https://dysms.console.aliyun.com/domestic/text/sign

切换到【模板管理】标签页

添加模板详情

设置AccessKey

创建用户

自己保存好【AccessKey Secret】

添加权限

购买短信免费试用包

代码开发

pom

    <!--阿里云短信服务-->
    <dependency>
        <groupId>com.aliyun</groupId>
        <artifactId>aliyun-java-sdk-core</artifactId>
        <version>4.5.16</version>
    </dependency>
    <dependency>
        <groupId>com.aliyun</groupId>
        <artifactId>aliyun-java-sdk-dysmsapi</artifactId>
        <version>2.1.0</version>
    </dependency>

导入utils工具类

9.2、手机验证码登录

需求分析

数据模型

代码开发

移动端页面放行的请求

LoginCheckFilter

//4-2、判断移动端用户登录状态,如果已登录,则直接放行
if (request.getSession().getAttribute("user") != null) {
    log.info("用户已登录,用户id为:{}",request.getSession().getAttribute("user"));

    Long userId = (Long) request.getSession().getAttribute("user");
    BaseContext.setCurrentId(userId);

    filterChain.doFilter(request, response);
    return;
}

UserController

package com.jerry.reggie.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jerry.reggie.common.R;
import com.jerry.reggie.entity.User;
import com.jerry.reggie.service.UserService;
import com.jerry.reggie.utils.SMSUtils;
import com.jerry.reggie.utils.ValidateCodeUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpSession;
import java.util.Map;

/**
 * ClassName: UserController
 * Package: com.jerry.reggie.controller
 * Description:
 *
 * @Author jerry_jy
 * @Create 2023-02-21 18:00
 * @Version 1.0
 */
@RestController
@Slf4j
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    /**
     * 发送手机验证码短信
     * @param user
     * @return
     */
    @PostMapping("/sendMsg")
    public R<String> sendMsg(@RequestBody User user, HttpSession session){
        //获取手机号
        String phone = user.getPhone();
        if (StringUtils.isNotEmpty(phone)){
            //生成随机的4位验证码
            String code = ValidateCodeUtils.generateValidateCode(4).toString();
            log.info("code={}",code);
            //调用阿里云提供的短信服务API完成短信发送
            // 没有买短信包,就不发手机短信了
//            SMSUtils.sendMessage("reggie外卖","SMS_270890116",phone,code);

            // 需要将生成的验证码保存到 session 中
            session.setAttribute(phone,code);
            return R.success("手机短信验证码发送成功");
        }
        return R.error("短信发送失败");
    }

    /**
     * 移动端用户登录
     * @param map
     * @param session
     * @return
     */
    @PostMapping("/login")
    public R<User> login(@RequestBody Map map, HttpSession session){
        log.info(map.toString());
        //获取手机号
        String phone = map.get("phone").toString();

        // 获取验证码
        String code = map.get("code").toString();

        // 从session中获取保存的验证码
        Object codeInSession = session.getAttribute(phone);

        //进行验证码的比对 (页面提交过来的验证码和session中保存的验证码进行比对)
        if (codeInSession != null && codeInSession.equals(code)){
            // 如果比对成功,说明登录成功
            LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(User::getPhone,phone);

            User user = userService.getOne(lambdaQueryWrapper);

            if (user==null){
                // 判断当前手机号是否为新用户,如果是新用户就能自动完成注册
               user= new User();
               user.setPhone(phone);
               user.setStatus(1);
               userService.save(user);
            }

            session.setAttribute("user", user.getId());
           return R.success(user);
        }
        return R.error("登录失败");
    }
}

10、前端–导入用户地址簿

需求分析

数据模型

代码开发

AddressBookController

package com.jerry.reggie.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.jerry.reggie.common.BaseContext;
import com.jerry.reggie.common.R;
import com.jerry.reggie.entity.AddressBook;
import com.jerry.reggie.service.AddressBookService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * ClassName: AddressBookController
 * Package: com.jerry.reggie.controller
 * Description:
 *
 * @Author jerry_jy
 * @Create 2023-02-21 20:42
 * @Version 1.0
 */

/**
 * 地址簿管理
 */
@Slf4j
@RestController
@RequestMapping("/addressBook")
public class AddressBookController {

    @Autowired
    private AddressBookService addressBookService;

    /**
     * 新增
     */
    @PostMapping
    public R<AddressBook> save(@RequestBody AddressBook addressBook) {
        addressBook.setUserId(BaseContext.getCurrentId());
        log.info("addressBook:{}", addressBook);
        addressBookService.save(addressBook);
        return R.success(addressBook);
    }

    /**
     * 设置默认地址
     */
    @PutMapping("default")
    public R<AddressBook> setDefault(@RequestBody AddressBook addressBook) {
        log.info("addressBook:{}", addressBook);
        LambdaUpdateWrapper<AddressBook> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(AddressBook::getUserId, BaseContext.getCurrentId());
        wrapper.set(AddressBook::getIsDefault, 0);
        //SQL:update address_book set is_default = 0 where user_id = ?
        addressBookService.update(wrapper);

        addressBook.setIsDefault(1);
        //SQL:update address_book set is_default = 1 where id = ?
        addressBookService.updateById(addressBook);
        return R.success(addressBook);
    }

    /**
     * 根据id查询地址
     */
    @GetMapping("/{id}")
    public R get(@PathVariable Long id) {
        AddressBook addressBook = addressBookService.getById(id);
        if (addressBook != null) {
            return R.success(addressBook);
        } else {
            return R.error("没有找到该对象");
        }
    }

    /**
     * 查询默认地址
     */
    @GetMapping("default")
    public R<AddressBook> getDefault() {
        LambdaQueryWrapper<AddressBook> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AddressBook::getUserId, BaseContext.getCurrentId());
        queryWrapper.eq(AddressBook::getIsDefault, 1);

        //SQL:select * from address_book where user_id = ? and is_default = 1
        AddressBook addressBook = addressBookService.getOne(queryWrapper);

        if (null == addressBook) {
            return R.error("没有找到该对象");
        } else {
            return R.success(addressBook);
        }
    }

    /**
     * 查询指定用户的全部地址
     */
    @GetMapping("/list")
    public R<List<AddressBook>> list(AddressBook addressBook) {
        addressBook.setUserId(BaseContext.getCurrentId());
        log.info("addressBook:{}", addressBook);

        //条件构造器
        LambdaQueryWrapper<AddressBook> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(null != addressBook.getUserId(), AddressBook::getUserId, addressBook.getUserId());
        queryWrapper.orderByDesc(AddressBook::getUpdateTime);

        //SQL:select * from address_book where user_id = ? order by update_time desc
        return R.success(addressBookService.list(queryWrapper));
    }
}

11、前端–菜品展示

需求分析

代码开发

展示flavor口味信息

// 前端需要展示flavor口味信息
@GetMapping("/list")
public R<List<DishDto>> list(Dish dish) {
    //构造查询条件
    LambdaQueryWrapper<Dish> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    lambdaQueryWrapper.eq(dish.getCategoryId() != null, Dish::getCategoryId, dish.getCategoryId());
    //查询状态为1,启售状态
    lambdaQueryWrapper.eq(Dish::getStatus, 1);
    //添加排序条件
    lambdaQueryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);

    List<Dish> list = dishService.list(lambdaQueryWrapper);

    List<DishDto> dishDtoList = list.stream().map((item) -> {
        DishDto dishDto = new DishDto();

        //对象拷贝
        BeanUtils.copyProperties(item, dishDto);

        Long categoryId = item.getCategoryId();//分类id
        Category category = categoryService.getById(categoryId);//分类对象

        if (category != null) {
            String categoryName = category.getName();
            dishDto.setCategoryName(categoryName);
        }
        //当前菜品id
        Long dishId = item.getId();

        LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(DishFlavor::getDishId,dishId);

        List<DishFlavor> dishFlavorList = dishFlavorService.list(queryWrapper);
        dishDto.setFlavors(dishFlavorList);

        return dishDto;
    }).collect(Collectors.toList());

    return R.success(dishDtoList);
}

套餐信息展示

/**
 * 根据条件查询套餐数据
 *
 * @param setmeal
 * @return
 */
@GetMapping("/list")
public R<List<Setmeal>> list(Setmeal setmeal) {
    //构造查询条件
    LambdaQueryWrapper<Setmeal> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    lambdaQueryWrapper.eq(setmeal.getCategoryId() != null, Setmeal::getCategoryId, setmeal.getCategoryId());
    //查询状态为1,启售状态
    lambdaQueryWrapper.eq(setmeal.getStatus() != null, Setmeal::getStatus, setmeal.getStatus());
    //添加排序条件
    lambdaQueryWrapper.orderByDesc(Setmeal::getUpdateTime);

    List<Setmeal> list = setmealService.list(lambdaQueryWrapper);

    return R.success(list);
}

12、前端–购物车

需求分析

数据模型

代码开发

购物车需要实现查看/增加/减少/清空商品

package com.jerry.reggie.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jerry.reggie.common.BaseContext;
import com.jerry.reggie.common.R;
import com.jerry.reggie.entity.ShoppingCart;
import com.jerry.reggie.service.ShoppingCartService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.util.List;

/**
 * ClassName: ShoppingCartController
 * Package: com.jerry.reggie.controller
 * Description:
 *
 * @Author jerry_jy
 * @Create 2023-02-22 10:14
 * @Version 1.0
 */
@RestController
@RequestMapping("/shoppingCart")
@Slf4j
public class ShoppingCartController {
    @Autowired
    private ShoppingCartService shoppingCartService;

    /**
     * 添加购物车
     *
     * @param shoppingCart
     * @return
     */
    @PostMapping("/add")
    public R<ShoppingCart> add(@RequestBody ShoppingCart shoppingCart) {
        log.info("购物车数据封装,{}", shoppingCart.toString());

        // 设置用户id,指定当前是哪个用户的购物车数据
        Long currentId = BaseContext.getCurrentId();
        shoppingCart.setUserId(currentId);


        Long dishId = shoppingCart.getDishId();

        LambdaQueryWrapper<ShoppingCart> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(ShoppingCart::getUserId, shoppingCart.getUserId());

        if (dishId != null) {
            // 添加到购物车的是菜品
            lambdaQueryWrapper.eq(ShoppingCart::getDishId, dishId);
        } else {
            // 添加到购物车的是套餐
            lambdaQueryWrapper.eq(ShoppingCart::getSetmealId, shoppingCart.getSetmealId());
        }

        // 查询当前菜品或套餐是否在购物车中,
        ShoppingCart cart = shoppingCartService.getOne(lambdaQueryWrapper);

        if (cart != null) {
            //如果已经存在,就在原来数量基础上加一
            Integer number = cart.getNumber();
            cart.setNumber(number + 1);
            shoppingCartService.updateById(cart);
        } else {
            // 如果不存在,则x添加到购物车,数量默认就是 1
            shoppingCart.setNumber(1);
            shoppingCart.setCreateTime(LocalDateTime.now());
            shoppingCartService.save(shoppingCart);
            cart = shoppingCart;
        }

        return R.success(cart);
    }

    /**
     * 减少购物车的菜品
     *
     * @return
     */
    @PostMapping("/sub")
    public R<ShoppingCart> sub(@RequestBody ShoppingCart shoppingCart) {
        log.info("购物车数据封装,{}", shoppingCart.toString());

        LambdaQueryWrapper<ShoppingCart> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(ShoppingCart::getUserId, BaseContext.getCurrentId());

        if (shoppingCart.getDishId() != null) {
            // 减少到购物车的是菜品
            lambdaQueryWrapper.eq(ShoppingCart::getDishId, shoppingCart.getDishId());
        } else {
            // 减少到购物车的是套餐
            lambdaQueryWrapper.eq(ShoppingCart::getSetmealId, shoppingCart.getSetmealId());
        }

        // 查询当前菜品或套餐是否在购物车中,
        ShoppingCart cart = shoppingCartService.getOne(lambdaQueryWrapper);

        //如果已经存在并且数量 > 1,就在原来数量基础上 - 1
        if ((cart.getNumber() > 1)) {
            Integer number = cart.getNumber();
            cart.setNumber(number - 1);
            shoppingCartService.updateById(cart);
        } else {
            //移除改菜品或套餐
            shoppingCartService.remove(lambdaQueryWrapper);
        }
        return R.success(cart);
    }

    /**
     * 清空购物车
     *
     * @return
     */
    @DeleteMapping("/clean")
    public R<String> clean() {
        LambdaQueryWrapper<ShoppingCart> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(ShoppingCart::getUserId, BaseContext.getCurrentId());

        shoppingCartService.remove(lambdaQueryWrapper);

        return R.success("清空购物车成功");
    }


    /**
     * 查看购物车
     *
     * @return
     */
    @GetMapping("/list")
    public R<List<ShoppingCart>> list() {
        log.info("查看购物车...");

        LambdaQueryWrapper<ShoppingCart> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(ShoppingCart::getUserId, BaseContext.getCurrentId());
        lambdaQueryWrapper.orderByAsc(ShoppingCart::getCreateTime);

        List<ShoppingCart> list = shoppingCartService.list(lambdaQueryWrapper);

        return R.success(list);
    }
}

13、前端–用户下单

需求分析

数据模型

代码开发

OrderController

package com.jerry.reggie.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jerry.reggie.common.R;
import com.jerry.reggie.entity.Orders;
import com.jerry.reggie.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * ClassName: OrderController
 * Package: com.jerry.reggie.controller
 * Description:
 *
 * @Author jerry_jy
 * @Create 2023-02-22 13:53
 * @Version 1.0
 */

@Slf4j
@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private OrderService orderService;

    /**
     * 用户下单
     * @param orders
     * @return
     */
    @PostMapping("/submit")
    public R<String> submit(@RequestBody Orders orders){
        log.info("订单数据:{}",orders);
        orderService.submit(orders);
        return R.success("下单成功");
    }

    /**
     * 订单查询
     * @param page
     * @param pageSize
     * @return
     */
    @GetMapping("/userPage")
    public R<Page> userPage(int page, int pageSize){
        //分页构造器
        Page<Orders> pageInfo = new Page<>();
        //条件构造器
        LambdaQueryWrapper<Orders> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        //添加排序条件
        lambdaQueryWrapper.orderByDesc(Orders::getOrderTime);

        orderService.page(pageInfo, lambdaQueryWrapper);

        return R.success(pageInfo);
    }
}

OrderService

package com.jerry.reggie.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.jerry.reggie.entity.Orders;

/**
 * ClassName: OrderService
 * Package: com.jerry.reggie.service
 * Description:
 *
 * @Author jerry_jy
 * @Create 2023-02-22 13:52
 * @Version 1.0
 */
public interface OrderService extends IService<Orders> {

    //用户下单
    void submit(Orders orders);
}

OrderServiceImpl

package com.jerry.reggie.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jerry.reggie.common.BaseContext;
import com.jerry.reggie.common.CustomException;
import com.jerry.reggie.entity.*;
import com.jerry.reggie.mapper.OrderMapper;
import com.jerry.reggie.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * ClassName: OrderServiceImpl
 * Package: com.jerry.reggie.service.impl
 * Description:
 *
 * @Author jerry_jy
 * @Create 2023-02-22 13:52
 * @Version 1.0
 */

@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Orders> implements OrderService {

    @Autowired
    private ShoppingCartService shoppingCartService;

    @Autowired
    private UserService userService;

    @Autowired
    private AddressBookService addressBookService;

    @Autowired
    private OrderDetailService orderDetailService;

    /**
     * 用户下单
     * @param orders
     */
    @Transactional
    @Override
    public void submit(Orders orders) {

        // 获取用户id
        Long userId = BaseContext.getCurrentId();

        // 查询当前用户购物车的数据
        LambdaQueryWrapper<ShoppingCart> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(ShoppingCart::getUserId,userId);
        List<ShoppingCart> shoppingCartList = shoppingCartService.list(lambdaQueryWrapper);

        if (shoppingCartList == null || shoppingCartList.size()==0){
           throw new CustomException("购物车为空,不能下单");
        }

        // 查询用户数据
        User user = userService.getById(userId);

        // 查询地址数据
        Long addressBookId = orders.getAddressBookId();
        AddressBook addressBook = addressBookService.getById(addressBookId);

        if (addressBook == null){
            throw new CustomException("用户地址信息有误,不能下单");
        }

        long orderId = IdWorker.getId();//生成订单号

        AtomicInteger amount = new AtomicInteger(0);

        List<OrderDetail> orderDetails= shoppingCartList.stream().map((item)->{
            OrderDetail orderDetail = new OrderDetail();
            orderDetail.setOrderId(orderId);
            orderDetail.setNumber(item.getNumber());
            orderDetail.setDishFlavor(item.getDishFlavor());
            orderDetail.setDishId(item.getDishId());
            orderDetail.setSetmealId(item.getSetmealId());
            orderDetail.setName(item.getName());
            orderDetail.setImage(item.getImage());
            orderDetail.setAmount(item.getAmount());
            amount.addAndGet(item.getAmount().multiply(new BigDecimal(item.getNumber())).intValue());
            return orderDetail;
        }).collect(Collectors.toList());


        orders.setId(orderId);
        orders.setOrderTime(LocalDateTime.now());
        orders.setCheckoutTime(LocalDateTime.now());
        orders.setStatus(2);
        orders.setAmount(new BigDecimal(amount.get()));//总金额
        orders.setUserId(userId);
        orders.setNumber(String.valueOf(orderId));
        orders.setUserName(user.getName());
        orders.setConsignee(addressBook.getConsignee());
        orders.setPhone(addressBook.getPhone());
        orders.setAddress((addressBook.getProvinceName() == null ? "" : addressBook.getProvinceName())
                + (addressBook.getCityName() == null ? "" : addressBook.getCityName())
                + (addressBook.getDistrictName() == null ? "" : addressBook.getDistrictName())
                + (addressBook.getDetail() == null ? "" : addressBook.getDetail()));

        // 向订单表插入,一条数据
        this.save(orders);

        // 向订单明细表插入,多条数据
        orderDetailService.saveBatch(orderDetails);

        //清空购物车数据
        shoppingCartService.remove(lambdaQueryWrapper);
    }
}

14、代码托管

Git版本管理

Gitee

https://gitee.com/jinyang-jy/reggie.git

Github

https://github.com/Jerry-jy/reggie.git

项目所需资料

链接:https://pan.baidu.com/s/182tTb1rmGkTCbq9aXrbjMg?pwd=2022
提取码:2022

文章出处登录后可见!

已经登录?立即刷新
退出移动版