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

news2025/4/15 19:05:43

基于SpringBoot+MyBatisPlus的外卖项目

  • 1、软件开发整体介绍
    • 软件开发流程
    • 角色分工
  • 2、外卖项目介绍
    • 项目介绍
    • 产品展示
      • 后台系统管理
      • 移动端
    • 技术选型
    • 功能结构
    • 角色
  • 3、开发环境的搭建
    • 开发环境说明
    • 建库
    • 建表
    • Maven项目搭建
      • 项目的目录结构
      • pom.xml
      • application.yml
      • ReggieApplication启动类
      • 配置静态资源映射
  • 4、登录功能
    • 4.1、后台登录功能
      • 需求分析
      • 代码编写
        • R.java
        • 实体类
        • EmployeeMapper
        • EmployeeService
        • EmployeeServiceImpl
        • EmployeeController
        • 页面展示
    • 4.2、后台登出功能
      • 需求分析
      • 代码编写
      • 页面展示
    • 4.3、完善登录功能
      • 问题分析
      • 代码编写
      • LoginCheckFilter
  • 5、员工管理
    • 5.1、新增员工
      • 需求分析
      • 数据模型
      • 代码开发
      • 代码编写
      • 异常捕获
    • 小结
    • 5.2、员工信息分页查询
      • 需求分析
      • 代码开发
        • MyBatisPlusConfig配置分页插件
    • 5.3、启用/禁用员工账号
      • 需求分析
      • 代码编写
        • 编写一个通用的update方法
      • 功能测试
      • 功能修复
    • 5.3、编辑员工
      • 需求分析
      • 代码编写
    • 5.4、公共字段自动填充
      • 问题分析
      • 代码实现
      • 功能完善
        • ThreadLocal
  • 6、分类管理
    • 6.1、新增菜品分类
      • 需求分析
      • 数据模型
      • 代码开发
    • 6.1、新增菜品的分页查询
      • 需求分析
      • 代码开发
    • 6.2、删除分类
      • 需求分析
      • 代码开发
      • 功能完善
        • GlobalExceptionHandler
        • **CustomException**
        • CategoryServiceImpl
    • 6.3、修改分类
      • 需求分析
      • 代码编写
  • 7、菜品管理
    • 7.1、文件上传下载
      • 文件上传介绍
      • 文件下载介绍
      • 文件上传代码实现
      • 文件下载代码实现
    • 7.2、新增菜品
      • 需求分析
      • 数据模型
      • 代码开发
        • DishServiceImpl
        • DishController
    • 7.3、菜品信息分页查询
      • 需求分析
      • 代码开发
        • 难点
    • 7.4、修改菜品
      • 需求分析
      • 代码开发
        • controller
        • DishServiceImpl
    • 7.5、修改菜品的停/起售状态
      • DishController
      • DishServiceImpl
    • 7.6、删除菜品
  • 8、套餐管理
    • 8.1、新增套餐
      • 需求分析
      • 数据模型
      • 代码开发
        • DishController
        • SetmealDishController
        • SetmealServiceImpl
    • 8.2、套餐信息分页查询
      • 需求分析
      • 代码开发
    • 8.3、删除套餐
      • 需求分析
      • 代码开发
        • SetmealController
        • SetmealServiceImpl
    • 8.4、修改套餐
        • SetmealController
        • SetmealService
        • SetmealServiceImpl
    • 8.5、停售、启售套餐
      • SetmealController
      • SetmealServiceImpl
  • 9、前端--手机验证码登录
    • 9.1、短信发送
      • 阿里云短信服务
      • 设置签名
      • 添加模板详情
      • 设置AccessKey
      • 添加权限
      • 购买短信免费试用包
      • 代码开发
        • pom
        • 导入utils工具类
    • 9.2、手机验证码登录
      • 需求分析
      • 数据模型
      • 代码开发
        • 移动端页面放行的请求
        • UserController
  • 10、前端--导入用户地址簿
    • 需求分析
    • 数据模型
    • 代码开发
      • AddressBookController
  • 11、前端--菜品展示
    • 需求分析
    • 代码开发
      • 展示flavor口味信息
      • 套餐信息展示
  • 12、前端--购物车
    • 需求分析
    • 数据模型
    • 代码开发
  • 13、前端--用户下单
    • 需求分析
    • 数据模型
    • 代码开发
      • OrderController
      • OrderService
      • OrderServiceImpl
  • 14、代码托管
    • Git版本管理
    • Gitee
    • Github
    • 项目所需资料

申明: 未经许可,禁止以任何形式转载,若要引用,请标注链接地址。 全文共计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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/364196.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

WSO2 apim Subscribe to an API

WSO2 apim Application Subscribe to an API1. Published an Api2. Subscribe to an API using Key Generation Wizard3. Subscribe to an existing application4. AwakeningWSO2安装使用的全过程详解: https://blog.csdn.net/weixin_43916074/article/details/127987099. Offi…

SpringCloud第五讲 Nacos注册中心-服务注册到Nacos

1.引入依赖&#xff1a; 在父工程中添加spring-cloud-alibaba的管理依赖 <!-- Nacos的管理依赖--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version…

Leetcode12. 整数转罗马数字

一、题目描述&#xff1a; 罗马数字包含以下七种字符&#xff1a; I&#xff0c; V&#xff0c; X&#xff0c; L&#xff0c;C&#xff0c;D 和 M。 字符数字I1V5X10L50C100D500M1000 例如&#xff0c; 罗马数字 2 写做 II &#xff0c;即为两个并列的 1。12 写做 XII &…

系列五、事务

一、事务简介 1.1、定义 事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向系 统提交或撤销操作请求&#xff0c;即这些操作要么同时成功&#xff0c;要么同时失败。 例如: 张三给李四转账1000块钱&#xff0c;张…

java获取当前时间的方法:LocalDateTime、Date、Calendar,以及三者的比较

文章目录前言一、LocalDateTime1.1 获取当前时间LocalDate.now()1.2 获取当前时间的年、月、日、时分秒localDateTime.getYear()……1.3 给LocalDateTime赋值LocalDateTime.of()1.4 时间与字符串相互转换LocalDateTime.parse()1.5 时间运算——加上对应时间LocalDateTime.now()…

SEO让Web3的需求更符合用户意图,AI+SEO充满想象

Web3 的基础设施建设现在仍处于前期&#xff0c;并没有出现现象级落地应用可以直接进入Web3,平常学习和交流中的大量信息和需求也只能通过传统互联网或智能手机作为端口&#xff0c;在企业浏览器和网站中寻找机会&#xff0c;这其中如何使企业品牌和原创内容能更好更靠前的呈现…

浅谈ffmpeg 压缩视频

1 首选需要安装ffmpeg 安装ffmpeg Linux 宝塔面板安装FFMpeg和编码库 yum install https://download1.rpmfusion.org/free/el/rpmfusion-free-release-7.noarch.rpm yum install http://rpmfind.net/linux/epel/7/x86_64/Packages/s/SDL2-2.0.14-2.el7.x86_64.rpm yum install …

【以太坊知识】

以太坊知识一、信标链引入1、内容2、合并二、二层网络与一层网络1、概念2、关系3、二层网络的工作原理三、分片1、概念2、分片特性一、信标链引入 1、内容 2022 年 9 月 15 日完成合并升级&#xff0c;将权益证明正式确定为太坊的共识机制。信标链是2020年启动的第一条权益证…

ubuntu 22.04 版本如何安装NCL

ubuntu 22.04 版本如何安装NCL 最近&#xff0c;重新创建了一个linux子系统进行学习。在安装ncl的时候&#xff0c;出现了各种问题。特此记录一下解决的过程。 首先下载了NCL的Linux版本的安装包&#xff0c;进行解压以及环境配置。但是在测试是否安装成功时&#xff0c;出现…

Linux部署java项目

Linux部署java项目启动虚拟机这部分的操作之前学习虚拟机时已经做过,可以参照之前的笔记即可推荐大家重新解压纯净版的RockyLinux来实现启动后登录rockylinuxsudo su -修改root用户密码passwd下面就切换到客户端软件连接虚拟机ifconfigifconfig | more查看ip地址使用Bvssh软件连…

如何判断机器学习数据集是否是线性的

首先,线性和非线性函数之间的区别: 左边是线性函数,右边是非线性函数。 线性函数:可以简单定义为始终遵循以下原则的函数: 输入/输出=常数。 线性方程总是1次多项式(例如x+2y+3=0)。在二维情况下,它们总是形成直线;在其他维度中,它们也可以形成平面、点或超平面。它们的…

追梦之旅【数据结构篇】——详解C语言实现动态版顺序栈

详解C语言动态实现顺序栈~&#x1f60e;前言&#x1f64c;预备小知识&#x1f49e;栈的概念及结构整体实现内容分析&#x1f49e;1.头文件编码实现&#x1f64c;2.功能文件编码实现&#x1f64c;3.测试文件的编写&#xff1a;&#x1f64c;总结撒花&#x1f49e;&#x1f60e;博…

ToB 产品拆解—Temu 商家管理后台

Temu 是拼多多旗下的跨境电商平台&#xff0c;平台产品于9月1日上线&#xff0c;9月1日到9月15日为测试期&#xff0c;之后全量全品类放开售卖。短短几个月的时间&#xff0c;Temu 在 App Store 冲上了购物类榜首&#xff0c;引起了国内的广泛关注。本文将以 B 端产品经理的角度…

opencv图片处理

目录1 图片处理1.1 显示图片1.2 旋转图片1.3 合并图片1.4、Mat类1.4.1、像素的储存结构1.4.2、访问像素数据1.6、rgb转灰度图1.7、二值化1.8、对比度和亮度1.9、图片缩放1.9.1、resize临近点算法双线性内插值1.9.2、金字塔缩放1.10、图片叠加1 图片处理 1.1 显示图片 #includ…

系统架构——分布式架构负载均衡系统设计实战

摘要 关于“负载均衡”的解释&#xff0c;百度词条里&#xff1a;负载均衡&#xff0c;英文叫Load Balance&#xff0c;意思就是将请求或者数据分摊到多个操作单元上进行执行&#xff0c;共同完成工作任务。负载均衡&#xff08;Load Balance&#xff09;建立在现有网络结构之…

HTTP2.0特性

HTTP2.0特性1. 二进制分帧1.1 关于帧的知识1.2 什么是二进制分帧1.3 二进制分帧如何工作1.4 二进制分帧对性能优化工作的贡献2. 首部压缩2.1 什么是首部压缩2.2 首部压缩如何工作2.3 首部压缩性能优化工作的贡献3. 流量控制4. 多路复用4.1 什么是多路复用4.2 多路复用如何工作4…

Another FasterRunner接口自动化测试平台

基于httprunner的接口自动化测试平台体验地址&#xff1a;http://119.91.147.215/fastrunner/login 操作手册https://www.yuque.com/lihuacai/fasterunner/wn5ync整体架构功能项目管理API模板支持添加接口和从yapi导入接口。同时支持接口调试。其中的提取&#xff0c;验证&…

【java基础】java八大基本数据类型和运算符

文章目录说明八大基本数据类型整型浮点型字符型布尔类型类型转换java运算符基础运算符二元运算符自增自减运算符关系和boolean运算符三元运算符位运算符运算符优先级说明 这里介绍java的八大基本数据类型和运算符 八大基本数据类型 java中有八大数据类型&#xff0c;4个整型…

PTA L1-054 福到了(详解)

前言&#xff1a;内容包括&#xff1a;题目&#xff0c;代码实现&#xff0c;大致思路&#xff0c;代码解读 题目&#xff1a; “福”字倒着贴&#xff0c;寓意“福到”。不论到底算不算民俗&#xff0c;本题且请你编写程序&#xff0c;把各种汉字倒过来输出。这里要处理的每…