黑马程序员-瑞吉外卖-前六章

黑马程序员瑞吉外卖

文章目录

  • 1、开发环境搭建
    • 1.1 数据库环境
    • 1.2 maven项目搭建
      • 1.2.1 新建项目
      • 1.2.2 整合MyBatisPlus
      • 1.2.3 整合Druid
      • 1.2.4 配置文件application.yml
      • 1.2.5 尝试启动项目
      • 1.2.6 将前端静态页面引入到项目中
  • 2、 后台系统登录功能
    • 2.1 需求分析
    • 2.2 代码开发
      • 2.2.1 创建实体类
      • 2.2.2 创建Controller、Service、Mapper
      • 2.2.3
  • 3、后台退出功能开发
    • 3.1 需求分析
    • 3.2 代码开发
  • 第二章
    • 1. 完善登录功能
      • 1.1 问题分析
      • 1.2 代码实现
      • 1.3 功能测试
    • 2. 新增员工
      • 2.1 需求分析
      • 2.2 数据模型
      • 2.3 代码开发
      • 2.4 异常处理
    • 3. 员工信息分页查询
      • 3.1 需求分析
      • 3.2 代码开发
        • 3.2.1 分页查步骤
      • 3.3 功能测试
    • 4. 启用/禁用员工账号
      • 4.1 需求分析
      • 4.2 代码开发
      • 4.3 功能测试
      • 4.4 代码修复
    • 5. 编辑员工信息
      • 5.1 需求分析
      • 5.2 代码开发
      • 5.3 功能测试
  • 第三章 分类管理业务
    • 1、公共字段自动填充
      • 1.1 问题分析
      • 1.2 代码实现
      • 1.3 功能测试
      • 1.4 功能完善
        • 1.4.1 ThreadLocal
    • 2、 新增分类
    • 2.1 需求分析
    • 2.2 数据模型
    • 2.3 代码开发
      • 2.3.1 搭建框架
      • 2.3.2 逻辑代码开发
    • 2.4 功能测试
    • 3、 分类信息分页查询
      • 3.1 需求分析
      • 3.2 代码开发
      • 3.3. 功能测试
    • 4、 删除分类
      • 4.1 需求分析
      • 4.2 代码开发
      • 4.3 功能测试
      • 4.4 功能完善
    • 5、 修改分类
      • 5.1 需求分析
      • 5.2 代码开发
      • 5.3 功能测试
  • 第四章 菜品管理业务开发
    • 1. 文件上传下载
      • 1.1 文件上传介绍
      • 1.2 文件下载介绍
      • 1.3 文件上传代码实现
      • 1.4 文件下载代码实现
    • 2. 新增菜品
      • 2.1 需求分析
      • 2.2 数据模型
      • 2.3 代码开发
      • 2.4 功能测试
    • 3. 菜品信息分页查询
      • 3.1 需求分析
      • 3.2 代码开发
      • 3.3 功能测试
    • 4. 修改菜品
      • 4.1 需求分析
      • 4.2 代码开发
      • 4.3 功能测试
  • 第五章 套餐管理业务开发
    • 1. 新增套餐
      • 1.1 需求分析
      • 1.2 数据模型
      • 1.3 代码开发
      • 1.4 功能测试
    • 2. 套餐信息分页查询
      • 2.1 需求分析
      • 2.2 代码开发
      • 2.3 功能测试
    • 3. 删除套餐
      • 3.1 需求分析
      • 3.2 代码开发
      • 3.3 功能测试
  • 第六章 手机验证码登录
    • 1. 短信发送
    • 2. 手机验证码登录
      • 2.1 需求分析
      • 2.2 代码开发
        • 2.2.1. 代码开发-修改LoginCheckFilter
        • 2.2.2 UserController.java
      • 2.3 功能测试

1、开发环境搭建

1.1 数据库环境

  1. 新建数据表

在这里插入图片描述

SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for address_book
-- ----------------------------
DROP TABLE IF EXISTS `address_book`;
CREATE TABLE `address_book` (
  `id` bigint(20) NOT NULL COMMENT '主键',
  `user_id` bigint(20) NOT NULL COMMENT '用户id',
  `consignee` varchar(50) COLLATE utf8_bin NOT NULL COMMENT '收货人',
  `sex` tinyint(4) NOT NULL COMMENT '性别 0 女 1 男',
  `phone` varchar(11) COLLATE utf8_bin NOT NULL COMMENT '手机号',
  `province_code` varchar(12) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '省级区划编号',
  `province_name` varchar(32) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '省级名称',
  `city_code` varchar(12) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '市级区划编号',
  `city_name` varchar(32) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '市级名称',
  `district_code` varchar(12) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '区级区划编号',
  `district_name` varchar(32) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '区级名称',
  `detail` varchar(200) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '详细地址',
  `label` varchar(100) CHARACTER SET utf8mb4 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(20) NOT NULL COMMENT '创建人',
  `update_user` bigint(20) NOT NULL COMMENT '修改人',
  `is_deleted` int(11) NOT NULL DEFAULT '0' COMMENT '是否删除',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='地址管理';
-- ----------------------------
-- Records of address_book
-- ----------------------------
INSERT INTO `address_book` 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');
INSERT INTO `address_book` VALUES ('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');
-- ----------------------------
-- Table structure for category
-- ----------------------------
DROP TABLE IF EXISTS `category`;
CREATE TABLE `category` (
  `id` bigint(20) NOT NULL COMMENT '主键',
  `type` int(11) DEFAULT NULL COMMENT '类型   1 菜品分类 2 套餐分类',
  `name` varchar(64) COLLATE utf8_bin NOT NULL COMMENT '分类名称',
  `sort` int(11) NOT NULL DEFAULT '0' COMMENT '顺序',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '更新时间',
  `create_user` bigint(20) NOT NULL COMMENT '创建人',
  `update_user` bigint(20) NOT NULL COMMENT '修改人',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `idx_category_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='菜品及套餐分类';
-- ----------------------------
-- Records of category
-- ----------------------------
INSERT INTO `category` VALUES ('1397844263642378242', '1', '湘菜', '1', '2021-05-27 09:16:58', '2021-07-15 20:25:23', '1', '1');
INSERT INTO `category` VALUES ('1397844303408574465', '1', '川菜', '2', '2021-05-27 09:17:07', '2021-06-02 14:27:22', '1', '1');
INSERT INTO `category` VALUES ('1397844391040167938', '1', '粤菜', '3', '2021-05-27 09:17:28', '2021-07-09 14:37:13', '1', '1');
INSERT INTO `category` VALUES ('1413341197421846529', '1', '饮品', '11', '2021-07-09 11:36:15', '2021-07-09 14:39:15', '1', '1');
INSERT INTO `category` VALUES ('1413342269393674242', '2', '商务套餐', '5', '2021-07-09 11:40:30', '2021-07-09 14:43:45', '1', '1');
INSERT INTO `category` VALUES ('1413384954989060097', '1', '主食', '12', '2021-07-09 14:30:07', '2021-07-09 14:39:19', '1', '1');
INSERT INTO `category` VALUES ('1413386191767674881', '2', '儿童套餐', '6', '2021-07-09 14:35:02', '2021-07-09 14:39:05', '1', '1');
-- ----------------------------
-- Table structure for dish
-- ----------------------------
DROP TABLE IF EXISTS `dish`;
CREATE TABLE `dish` (
  `id` bigint(20) NOT NULL COMMENT '主键',
  `name` varchar(64) COLLATE utf8_bin NOT NULL COMMENT '菜品名称',
  `category_id` bigint(20) NOT NULL COMMENT '菜品分类id',
  `price` decimal(10,2) DEFAULT NULL COMMENT '菜品价格',
  `code` varchar(64) COLLATE utf8_bin NOT NULL COMMENT '商品码',
  `image` varchar(200) COLLATE utf8_bin NOT NULL COMMENT '图片',
  `description` varchar(400) COLLATE utf8_bin DEFAULT NULL COMMENT '描述信息',
  `status` int(11) NOT NULL DEFAULT '1' COMMENT '0 停售 1 起售',
  `sort` int(11) NOT NULL DEFAULT '0' COMMENT '顺序',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '更新时间',
  `create_user` bigint(20) NOT NULL COMMENT '创建人',
  `update_user` bigint(20) NOT NULL COMMENT '修改人',
  `is_deleted` int(11) NOT NULL DEFAULT '0' COMMENT '是否删除',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `idx_dish_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='菜品管理';
-- ----------------------------
-- Records of dish
-- ----------------------------
INSERT INTO `dish` VALUES ('1397849739276890114', '辣子鸡', '1397844263642378242', '7800.00', '222222222', 'f966a38e-0780-40be-bb52-5699d13cb3d9.jpg', '来自鲜嫩美味的小鸡,值得一尝', '1', '0', '2021-05-27 09:38:43', '2021-05-27 09:38:43', '1', '1', '0');
INSERT INTO `dish` VALUES ('1397850140982161409', '毛氏红烧肉', '1397844263642378242', '6800.00', '123412341234', '0a3b3288-3446-4420-bbff-f263d0c02d8e.jpg', '毛氏红烧肉毛氏红烧肉,确定不来一份?', '1', '0', '2021-05-27 09:40:19', '2021-05-27 09:40:19', '1', '1', '0');
INSERT INTO `dish` VALUES ('1397850392090947585', '组庵鱼翅', '1397844263642378242', '4800.00', '123412341234', '740c79ce-af29-41b8-b78d-5f49c96e38c4.jpg', '组庵鱼翅,看图足以表明好吃程度', '1', '0', '2021-05-27 09:41:19', '2021-05-27 09:41:19', '1', '1', '0');
INSERT INTO `dish` VALUES ('1397850851245600769', '霸王别姬', '1397844263642378242', '12800.00', '123412341234', '057dd338-e487-4bbc-a74c-0384c44a9ca3.jpg', '还有什么比霸王别姬更美味的呢?', '1', '0', '2021-05-27 09:43:08', '2021-05-27 09:43:08', '1', '1', '0');
INSERT INTO `dish` VALUES ('1397851099502260226', '全家福', '1397844263642378242', '11800.00', '23412341234', 'a53a4e6a-3b83-4044-87f9-9d49b30a8fdc.jpg', '别光吃肉啦,来份全家福吧,让你长寿又美味', '1', '0', '2021-05-27 09:44:08', '2021-05-27 09:44:08', '1', '1', '0');
INSERT INTO `dish` VALUES ('1397851370462687234', '邵阳猪血丸子', '1397844263642378242', '13800.00', '1246812345678', '2a50628e-7758-4c51-9fbb-d37c61cdacad.jpg', '看,美味不?来嘛来嘛,这才是最爱吖', '1', '0', '2021-05-27 09:45:12', '2021-05-27 09:45:12', '1', '1', '0');
INSERT INTO `dish` VALUES ('1397851668262465537', '口味蛇', '1397844263642378242', '16800.00', '1234567812345678', '0f4bd884-dc9c-4cf9-b59e-7d5958fec3dd.jpg', '爬行界的扛把子,东兴-口味蛇,让你欲罢不能', '1', '0', '2021-05-27 09:46:23', '2021-05-27 09:46:23', '1', '1', '0');
INSERT INTO `dish` VALUES ('1397852391150759938', '辣子鸡丁', '1397844303408574465', '8800.00', '2346812468', 'ef2b73f2-75d1-4d3a-beea-22da0e1421bd.jpg', '辣子鸡丁,辣子鸡丁,永远的魂', '1', '0', '2021-05-27 09:49:16', '2021-05-27 09:49:16', '1', '1', '0');
INSERT INTO `dish` VALUES ('1397853183287013378', '麻辣兔头', '1397844303408574465', '19800.00', '123456787654321', '2a2e9d66-b41d-4645-87bd-95f2cfeed218.jpg', '麻辣兔头的详细制作,麻辣鲜香,色泽红润,回味悠长', '1', '0', '2021-05-27 09:52:24', '2021-05-27 09:52:24', '1', '1', '0');
INSERT INTO `dish` VALUES ('1397853709101740034', '蒜泥白肉', '1397844303408574465', '9800.00', '1234321234321', 'd2f61d70-ac85-4529-9b74-6d9a2255c6d7.jpg', '多么的有食欲啊', '1', '0', '2021-05-27 09:54:30', '2021-05-27 09:54:30', '1', '1', '0');
INSERT INTO `dish` VALUES ('1397853890262118402', '鱼香肉丝', '1397844303408574465', '3800.00', '1234212321234', '8dcfda14-5712-4d28-82f7-ae905b3c2308.jpg', '鱼香肉丝简直就是我们童年回忆的一道经典菜,上学的时候点个鱼香肉丝盖饭坐在宿舍床上看着肥皂剧,绝了!现在完美复刻一下上学的时候感觉', '1', '0', '2021-05-27 09:55:13', '2021-05-27 09:55:13', '1', '1', '0');
INSERT INTO `dish` VALUES ('1397854652581064706', '麻辣水煮鱼', '1397844303408574465', '14800.00', '2345312·345321', '1fdbfbf3-1d86-4b29-a3fc-46345852f2f8.jpg', '鱼片是买的切好的鱼片,放几个虾,增加味道', '1', '0', '2021-05-27 09:58:15', '2021-05-27 09:58:15', '1', '1', '0');
INSERT INTO `dish` VALUES ('1397854865672679425', '鱼香炒鸡蛋', '1397844303408574465', '2000.00', '23456431·23456', '0f252364-a561-4e8d-8065-9a6797a6b1d3.jpg', '鱼香菜也是川味的特色。里面没有鱼却鱼香味', '1', '0', '2021-05-27 09:59:06', '2021-05-27 09:59:06', '1', '1', '0');
INSERT INTO `dish` VALUES ('1397860242057375745', '脆皮烧鹅', '1397844391040167938', '12800.00', '123456786543213456', 'e476f679-5c15-436b-87fa-8c4e9644bf33.jpeg', '“广东烤鸭美而香,却胜烧鹅说古冈(今新会),燕瘦环肥各佳妙,君休偏重便宜坊”,可见烧鹅与烧鸭在粤菜之中已早负盛名。作为广州最普遍和最受欢迎的烧烤肉食,以它的“色泽金红,皮脆肉嫩,味香可口”的特色,在省城各大街小巷的烧卤店随处可见。', '1', '0', '2021-05-27 10:20:27', '2021-05-27 10:20:27', '1', '1', '0');
INSERT INTO `dish` VALUES ('1397860578738352129', '白切鸡', '1397844391040167938', '6600.00', '12345678654', '9ec6fc2d-50d2-422e-b954-de87dcd04198.jpeg', '白切鸡是一道色香味俱全的特色传统名肴,又叫白斩鸡,是粤菜系鸡肴中的一种,始于清代的民间。白切鸡通常选用细骨农家鸡与沙姜、蒜茸等食材,慢火煮浸白切鸡皮爽肉滑,清淡鲜美。著名的泮溪酒家白切鸡,曾获商业部优质产品金鼎奖。湛江白切鸡更是驰名粤港澳。粤菜厨坛中,鸡的菜式有200余款之多,而最为人常食不厌的正是白切鸡,深受食家青睐。', '1', '0', '2021-05-27 10:21:48', '2021-05-27 10:21:48', '1', '1', '0');
INSERT INTO `dish` VALUES ('1397860792492666881', '烤乳猪', '1397844391040167938', '38800.00', '213456432123456', '2e96a7e3-affb-438e-b7c3-e1430df425c9.jpeg', '广式烧乳猪主料是小乳猪,辅料是蒜,调料是五香粉、芝麻酱、八角粉等,本菜品主要通过将食材放入炭火中烧烤而成。烤乳猪是广州最著名的特色菜,并且是“满汉全席”中的主打菜肴之一。烤乳猪也是许多年来广东人祭祖的祭品之一,是家家都少不了的应节之物,用乳猪祭完先人后,亲戚们再聚餐食用。', '1', '0', '2021-05-27 10:22:39', '2021-05-27 10:22:39', '1', '1', '0');
INSERT INTO `dish` VALUES ('1397860963880316929', '脆皮乳鸽', '1397844391040167938', '10800.00', '1234563212345', '3fabb83a-1c09-4fd9-892b-4ef7457daafa.jpeg', '“脆皮乳鸽”是广东菜中的一道传统名菜,属于粤菜系,具有皮脆肉嫩、色泽红亮、鲜香味美的特点,常吃可使身体强健,清肺顺气。随着菜品制作工艺的不断发展,逐渐形成了熟炸法、生炸法和烤制法三种制作方法。无论那种制作方法,都是在鸽子经过一系列的加工,挂脆皮水后再加工而成,正宗的“脆皮乳鸽皮脆肉嫩、色泽红亮、鲜香味美、香气馥郁。这三种方法的制作过程都不算复杂,但想达到理想的效果并不容易。', '1', '0', '2021-05-27 10:23:19', '2021-05-27 10:23:19', '1', '1', '0');
INSERT INTO `dish` VALUES ('1397861683434139649', '清蒸河鲜海鲜', '1397844391040167938', '38800.00', '1234567876543213456', '1405081e-f545-42e1-86a2-f7559ae2e276.jpeg', '新鲜的海鲜,清蒸是最好的处理方式。鲜,体会为什么叫海鲜。清蒸是广州最经典的烹饪手法,过去岭南地区由于峻山大岭阻隔,交通不便,经济发展起步慢,自家打的鱼放在锅里煮了就吃,没有太多的讲究,但却发现这清淡的煮法能使鱼的鲜甜跃然舌尖。', '1', '0', '2021-05-27 10:26:11', '2021-05-27 10:26:11', '1', '1', '0');
INSERT INTO `dish` VALUES ('1397862198033297410', '老火靓汤', '1397844391040167938', '49800.00', '123456786532455', '583df4b7-a159-4cfc-9543-4f666120b25f.jpeg', '老火靓汤又称广府汤,是广府人传承数千年的食补养生秘方,慢火煲煮的中华老火靓汤,火候足,时间长,既取药补之效,又取入口之甘甜。 广府老火汤种类繁多,可以用各种汤料和烹调方法,烹制出各种不同口味、不同功效的汤来。', '1', '0', '2021-05-27 10:28:14', '2021-05-27 10:28:14', '1', '1', '0');
INSERT INTO `dish` VALUES ('1397862477831122945', '上汤焗龙虾', '1397844391040167938', '108800.00', '1234567865432', '5b8d2da3-3744-4bb3-acdc-329056b8259d.jpeg', '上汤焗龙虾是一道色香味俱全的传统名菜,属于粤菜系。此菜以龙虾为主料,配以高汤制成的一道海鲜美食。本品肉质洁白细嫩,味道鲜美,蛋白质含量高,脂肪含量低,营养丰富。是色香味俱全的传统名菜。', '1', '0', '2021-05-27 10:29:20', '2021-05-27 10:29:20', '1', '1', '0');
INSERT INTO `dish` VALUES ('1413342036832100354', '北冰洋', '1413341197421846529', '500.00', '', 'c99e0aab-3cb7-4eaa-80fd-f47d4ffea694.png', '', '1', '0', '2021-07-09 11:39:35', '2021-07-09 15:12:18', '1', '1', '0');
INSERT INTO `dish` VALUES ('1413384757047271425', '王老吉', '1413341197421846529', '500.00', '', '00874a5e-0df2-446b-8f69-a30eb7d88ee8.png', '', '1', '0', '2021-07-09 14:29:20', '2021-07-12 09:09:16', '1', '1', '0');
INSERT INTO `dish` VALUES ('1413385247889891330', '米饭', '1413384954989060097', '200.00', '', 'ee04a05a-1230-46b6-8ad5-1a95b140fff3.png', '', '1', '0', '2021-07-09 14:31:17', '2021-07-11 16:35:26', '1', '1', '0');
-- ----------------------------
-- Table structure for dish_flavor
-- ----------------------------
DROP TABLE IF EXISTS `dish_flavor`;
CREATE TABLE `dish_flavor` (
  `id` bigint(20) NOT NULL COMMENT '主键',
  `dish_id` bigint(20) NOT NULL COMMENT '菜品',
  `name` varchar(64) COLLATE utf8_bin NOT NULL COMMENT '口味名称',
  `value` varchar(500) COLLATE utf8_bin DEFAULT NULL COMMENT '口味数据list',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '更新时间',
  `create_user` bigint(20) NOT NULL COMMENT '创建人',
  `update_user` bigint(20) NOT NULL COMMENT '修改人',
  `is_deleted` int(11) NOT NULL DEFAULT '0' COMMENT '是否删除',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='菜品口味关系表';
-- ----------------------------
-- Records of dish_flavor
-- ----------------------------
INSERT INTO `dish_flavor` VALUES ('1397849417888346113', '1397849417854791681', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-27 09:37:27', '2021-05-27 09:37:27', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397849739297861633', '1397849739276890114', '忌口', '[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]', '2021-05-27 09:38:43', '2021-05-27 09:38:43', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397849739323027458', '1397849739276890114', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-27 09:38:43', '2021-05-27 09:38:43', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397849936421761025', '1397849936404983809', '忌口', '[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]', '2021-05-27 09:39:30', '2021-05-27 09:39:30', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397849936438538241', '1397849936404983809', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-27 09:39:30', '2021-05-27 09:39:30', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397850141015715841', '1397850140982161409', '忌口', '[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]', '2021-05-27 09:40:19', '2021-05-27 09:40:19', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397850141040881665', '1397850140982161409', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-27 09:40:19', '2021-05-27 09:40:19', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397850392120307713', '1397850392090947585', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-27 09:41:19', '2021-05-27 09:41:19', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397850392137084929', '1397850392090947585', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-27 09:41:19', '2021-05-27 09:41:19', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397850630734262274', '1397850630700707841', '忌口', '[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]', '2021-05-27 09:42:16', '2021-05-27 09:42:16', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397850630755233794', '1397850630700707841', '辣度', '[\"微辣\",\"中辣\",\"重辣\"]', '2021-05-27 09:42:16', '2021-05-27 09:42:16', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397850851274960898', '1397850851245600769', '忌口', '[\"不要蒜\",\"不要香菜\",\"不要辣\"]', '2021-05-27 09:43:08', '2021-05-27 09:43:08', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397850851283349505', '1397850851245600769', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-27 09:43:08', '2021-05-27 09:43:08', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397851099523231745', '1397851099502260226', '忌口', '[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]', '2021-05-27 09:44:08', '2021-05-27 09:44:08', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397851099527426050', '1397851099502260226', '辣度', '[\"不辣\",\"微辣\",\"中辣\"]', '2021-05-27 09:44:08', '2021-05-27 09:44:08', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397851370483658754', '1397851370462687234', '温度', '[\"热饮\",\"常温\",\"去冰\",\"少冰\",\"多冰\"]', '2021-05-27 09:45:12', '2021-05-27 09:45:12', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397851370483658755', '1397851370462687234', '忌口', '[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]', '2021-05-27 09:45:12', '2021-05-27 09:45:12', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397851370483658756', '1397851370462687234', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-27 09:45:12', '2021-05-27 09:45:12', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397851668283437058', '1397851668262465537', '温度', '[\"热饮\",\"常温\",\"去冰\",\"少冰\",\"多冰\"]', '2021-05-27 09:46:23', '2021-05-27 09:46:23', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397852391180120065', '1397852391150759938', '忌口', '[\"不要葱\",\"不要香菜\",\"不要辣\"]', '2021-05-27 09:49:16', '2021-05-27 09:49:16', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397852391196897281', '1397852391150759938', '辣度', '[\"不辣\",\"微辣\",\"重辣\"]', '2021-05-27 09:49:16', '2021-05-27 09:49:16', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397853183307984898', '1397853183287013378', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-27 09:52:24', '2021-05-27 09:52:24', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397853423486414850', '1397853423461249026', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-27 09:53:22', '2021-05-27 09:53:22', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397853709126905857', '1397853709101740034', '忌口', '[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]', '2021-05-27 09:54:30', '2021-05-27 09:54:30', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397853890283089922', '1397853890262118402', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-27 09:55:13', '2021-05-27 09:55:13', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397854133632413697', '1397854133603053569', '温度', '[\"热饮\",\"常温\",\"去冰\",\"少冰\",\"多冰\"]', '2021-05-27 09:56:11', '2021-05-27 09:56:11', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397854652623007745', '1397854652581064706', '忌口', '[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]', '2021-05-27 09:58:15', '2021-05-27 09:58:15', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397854652635590658', '1397854652581064706', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-27 09:58:15', '2021-05-27 09:58:15', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397854865735593986', '1397854865672679425', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-27 09:59:06', '2021-05-27 09:59:06', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397855742303186946', '1397855742273826817', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-27 10:02:35', '2021-05-27 10:02:35', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397855906497605633', '1397855906468245506', '忌口', '[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]', '2021-05-27 10:03:14', '2021-05-27 10:03:14', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397856190573621250', '1397856190540066818', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-27 10:04:21', '2021-05-27 10:04:21', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397859056709316609', '1397859056684150785', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-27 10:15:45', '2021-05-27 10:15:45', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397859277837217794', '1397859277812051969', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-27 10:16:37', '2021-05-27 10:16:37', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397859487502086146', '1397859487476920321', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-27 10:17:27', '2021-05-27 10:17:27', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397859757061615618', '1397859757036449794', '甜味', '[\"无糖\",\"少糖\",\"半躺\",\"多糖\",\"全糖\"]', '2021-05-27 10:18:32', '2021-05-27 10:18:32', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397860242086735874', '1397860242057375745', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-27 10:20:27', '2021-05-27 10:20:27', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397860963918065665', '1397860963880316929', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-27 10:23:19', '2021-05-27 10:23:19', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397861135754506242', '1397861135733534722', '甜味', '[\"无糖\",\"少糖\",\"半躺\",\"多糖\",\"全糖\"]', '2021-05-27 10:24:00', '2021-05-27 10:24:00', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397861370035744769', '1397861370010578945', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-27 10:24:56', '2021-05-27 10:24:56', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397861683459305474', '1397861683434139649', '忌口', '[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]', '2021-05-27 10:26:11', '2021-05-27 10:26:11', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397861898467717121', '1397861898438356993', '忌口', '[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]', '2021-05-27 10:27:02', '2021-05-27 10:27:02', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397862198054268929', '1397862198033297410', '忌口', '[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]', '2021-05-27 10:28:14', '2021-05-27 10:28:14', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1397862477835317250', '1397862477831122945', '辣度', '[\"不辣\",\"微辣\",\"中辣\"]', '2021-05-27 10:29:20', '2021-05-27 10:29:20', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1398089545865015297', '1398089545676271617', '温度', '[\"热饮\",\"常温\",\"去冰\",\"少冰\",\"多冰\"]', '2021-05-28 01:31:38', '2021-05-28 01:31:38', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1398089782323097601', '1398089782285348866', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-28 01:32:34', '2021-05-28 01:32:34', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1398090003262255106', '1398090003228700673', '忌口', '[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]', '2021-05-28 01:33:27', '2021-05-28 01:33:27', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1398090264554811394', '1398090264517062657', '忌口', '[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]', '2021-05-28 01:34:29', '2021-05-28 01:34:29', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1398090455399837698', '1398090455324340225', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-28 01:35:14', '2021-05-28 01:35:14', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1398090685449023490', '1398090685419663362', '温度', '[\"热饮\",\"常温\",\"去冰\",\"少冰\",\"多冰\"]', '2021-05-28 01:36:09', '2021-05-28 01:36:09', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1398090825358422017', '1398090825329061889', '忌口', '[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]', '2021-05-28 01:36:43', '2021-05-28 01:36:43', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1398091007051476993', '1398091007017922561', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-28 01:37:26', '2021-05-28 01:37:26', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1398091296164851713', '1398091296131297281', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-28 01:38:35', '2021-05-28 01:38:35', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1398091546531246081', '1398091546480914433', '忌口', '[\"不要葱\",\"不要蒜\",\"不要香菜\",\"不要辣\"]', '2021-05-28 01:39:35', '2021-05-28 01:39:35', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1398091729809747969', '1398091729788776450', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-28 01:40:18', '2021-05-28 01:40:18', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1398091889499484161', '1398091889449152513', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-28 01:40:56', '2021-05-28 01:40:56', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1398092095179763713', '1398092095142014978', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-28 01:41:45', '2021-05-28 01:41:45', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1398092283877306370', '1398092283847946241', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-28 01:42:30', '2021-05-28 01:42:30', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1398094018939236354', '1398094018893099009', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-28 01:49:24', '2021-05-28 01:49:24', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1398094391494094850', '1398094391456346113', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-05-28 01:50:53', '2021-05-28 01:50:53', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1399574026165727233', '1399305325713600514', '辣度', '[\"不辣\",\"微辣\",\"中辣\",\"重辣\"]', '2021-06-01 03:50:25', '2021-06-01 03:50:25', '1399309715396669441', '1399309715396669441', '0');
INSERT INTO `dish_flavor` VALUES ('1413389540592263169', '1413384757047271425', '温度', '[\"常温\",\"冷藏\"]', '2021-07-12 09:09:16', '2021-07-12 09:09:16', '1', '1', '0');
INSERT INTO `dish_flavor` VALUES ('1413389684020682754', '1413342036832100354', '温度', '[\"常温\",\"冷藏\"]', '2021-07-09 15:12:18', '2021-07-09 15:12:18', '1', '1', '0');
-- ----------------------------
-- Table structure for employee
-- ----------------------------
DROP TABLE IF EXISTS `employee`;
CREATE TABLE `employee` (
  `id` bigint(20) NOT NULL COMMENT '主键',
  `name` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '姓名',
  `username` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '用户名',
  `password` varchar(64) COLLATE utf8_bin NOT NULL COMMENT '密码',
  `phone` varchar(11) COLLATE utf8_bin NOT NULL COMMENT '手机号',
  `sex` varchar(2) COLLATE utf8_bin NOT NULL COMMENT '性别',
  `id_number` varchar(18) COLLATE utf8_bin NOT NULL COMMENT '身份证号',
  `status` int(11) NOT NULL DEFAULT '1' COMMENT '状态 0:禁用,1:正常',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '更新时间',
  `create_user` bigint(20) NOT NULL COMMENT '创建人',
  `update_user` bigint(20) NOT NULL COMMENT '修改人',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `idx_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='员工信息';
-- ----------------------------
-- Records of employee
-- ----------------------------
INSERT INTO `employee` VALUES ('1', '管理员', 'admin', 'e10adc3949ba59abbe56e057f20f883e', '13812312312', '1', '110101199001010047', '1', '2021-05-06 17:20:07', '2021-05-10 02:24:09', '1', '1');
-- ----------------------------
-- Table structure for orders
-- ----------------------------
DROP TABLE IF EXISTS `orders`;
CREATE TABLE `orders` (
  `id` bigint(20) NOT NULL COMMENT '主键',
  `number` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '订单号',
  `status` int(11) NOT NULL DEFAULT '1' COMMENT '订单状态 1待付款,2待派送,3已派送,4已完成,5已取消',
  `user_id` bigint(20) NOT NULL COMMENT '下单用户',
  `address_book_id` bigint(20) NOT NULL COMMENT '地址id',
  `order_time` datetime NOT NULL COMMENT '下单时间',
  `checkout_time` datetime NOT NULL COMMENT '结账时间',
  `pay_method` int(11) NOT NULL DEFAULT '1' COMMENT '支付方式 1微信,2支付宝',
  `amount` decimal(10,2) NOT NULL COMMENT '实收金额',
  `remark` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '备注',
  `phone` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `address` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `user_name` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  `consignee` varchar(255) COLLATE utf8_bin DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='订单表';
-- ----------------------------
-- Records of orders
-- ----------------------------
-- ----------------------------
-- Table structure for order_detail
-- ----------------------------
DROP TABLE IF EXISTS `order_detail`;
CREATE TABLE `order_detail` (
  `id` bigint(20) NOT NULL COMMENT '主键',
  `name` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '名字',
  `image` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '图片',
  `order_id` bigint(20) NOT NULL COMMENT '订单id',
  `dish_id` bigint(20) DEFAULT NULL COMMENT '菜品id',
  `setmeal_id` bigint(20) DEFAULT NULL COMMENT '套餐id',
  `dish_flavor` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '口味',
  `number` int(11) NOT NULL DEFAULT '1' COMMENT '数量',
  `amount` decimal(10,2) NOT NULL COMMENT '金额',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='订单明细表';
-- ----------------------------
-- Records of order_detail
-- ----------------------------
-- ----------------------------
-- Table structure for setmeal
-- ----------------------------
DROP TABLE IF EXISTS `setmeal`;
CREATE TABLE `setmeal` (
  `id` bigint(20) NOT NULL COMMENT '主键',
  `category_id` bigint(20) NOT NULL COMMENT '菜品分类id',
  `name` varchar(64) COLLATE utf8_bin NOT NULL COMMENT '套餐名称',
  `price` decimal(10,2) NOT NULL COMMENT '套餐价格',
  `status` int(11) DEFAULT NULL COMMENT '状态 0:停用 1:启用',
  `code` varchar(32) COLLATE utf8_bin DEFAULT NULL COMMENT '编码',
  `description` varchar(512) COLLATE utf8_bin DEFAULT NULL COMMENT '描述信息',
  `image` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '图片',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '更新时间',
  `create_user` bigint(20) NOT NULL COMMENT '创建人',
  `update_user` bigint(20) NOT NULL COMMENT '修改人',
  `is_deleted` int(11) NOT NULL DEFAULT '0' COMMENT '是否删除',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `idx_setmeal_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='套餐';
-- ----------------------------
-- Records of setmeal
-- ----------------------------
INSERT INTO `setmeal` VALUES ('1415580119015145474', '1413386191767674881', '儿童套餐A计划', '4000.00', '1', '', '', '61d20592-b37f-4d72-a864-07ad5bb8f3bb.jpg', '2021-07-15 15:52:55', '2021-07-15 15:52:55', '1415576781934608386', '1415576781934608386', '0');
-- ----------------------------
-- Table structure for setmeal_dish
-- ----------------------------
DROP TABLE IF EXISTS `setmeal_dish`;
CREATE TABLE `setmeal_dish` (
  `id` bigint(20) NOT NULL COMMENT '主键',
  `setmeal_id` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '套餐id ',
  `dish_id` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '菜品id',
  `name` varchar(32) COLLATE utf8_bin DEFAULT NULL COMMENT '菜品名称 (冗余字段)',
  `price` decimal(10,2) DEFAULT NULL COMMENT '菜品原价(冗余字段)',
  `copies` int(11) NOT NULL COMMENT '份数',
  `sort` int(11) NOT NULL DEFAULT '0' COMMENT '排序',
  `create_time` datetime NOT NULL COMMENT '创建时间',
  `update_time` datetime NOT NULL COMMENT '更新时间',
  `create_user` bigint(20) NOT NULL COMMENT '创建人',
  `update_user` bigint(20) NOT NULL COMMENT '修改人',
  `is_deleted` int(11) NOT NULL DEFAULT '0' COMMENT '是否删除',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='套餐菜品关系';
-- ----------------------------
-- Records of setmeal_dish
-- ----------------------------
INSERT INTO `setmeal_dish` VALUES ('1415580119052894209', '1415580119015145474', '1397862198033297410', '老火靓汤', '49800.00', '1', '0', '2021-07-15 15:52:55', '2021-07-15 15:52:55', '1415576781934608386', '1415576781934608386', '0');
INSERT INTO `setmeal_dish` VALUES ('1415580119061282817', '1415580119015145474', '1413342036832100354', '北冰洋', '500.00', '1', '0', '2021-07-15 15:52:55', '2021-07-15 15:52:55', '1415576781934608386', '1415576781934608386', '0');
INSERT INTO `setmeal_dish` VALUES ('1415580119069671426', '1415580119015145474', '1413385247889891330', '米饭', '200.00', '1', '0', '2021-07-15 15:52:55', '2021-07-15 15:52:55', '1415576781934608386', '1415576781934608386', '0');
-- ----------------------------
-- Table structure for shopping_cart
-- ----------------------------
DROP TABLE IF EXISTS `shopping_cart`;
CREATE TABLE `shopping_cart` (
  `id` bigint(20) NOT NULL COMMENT '主键',
  `name` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '名称',
  `image` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '图片',
  `user_id` bigint(20) NOT NULL COMMENT '主键',
  `dish_id` bigint(20) DEFAULT NULL COMMENT '菜品id',
  `setmeal_id` bigint(20) DEFAULT NULL COMMENT '套餐id',
  `dish_flavor` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '口味',
  `number` int(11) 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=utf8 COLLATE=utf8_bin COMMENT='购物车';
-- ----------------------------
-- Records of shopping_cart
-- ----------------------------
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` bigint(20) NOT NULL COMMENT '主键',
  `name` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '姓名',
  `phone` varchar(100) COLLATE utf8_bin NOT NULL COMMENT '手机号',
  `sex` varchar(2) COLLATE utf8_bin DEFAULT NULL COMMENT '性别',
  `id_number` varchar(18) COLLATE utf8_bin DEFAULT NULL COMMENT '身份证号',
  `avatar` varchar(500) COLLATE utf8_bin DEFAULT NULL COMMENT '头像',
  `status` int(11) DEFAULT '0' COMMENT '状态 0:禁用,1:正常',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='用户信息';

1.2 maven项目搭建

1.2.1 新建项目

  1. file > new > project

在这里插入图片描述

  1. 选择技术集
  • Web 中的Spring Web
  • SQL 中的MySQL Driver
  • MyBatis Framework

在这里插入图片描述

1.2.2 整合MyBatisPlus

  1. 访问maven仓库将MyBatisPlus的坐标复制到pom.xml中
  2. 选择版本 3.4.3 坐标如下
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.3</version>
</dependency>

1.2.3 整合Druid

  1. 访问maven仓库将Druid的坐标复制到pom.xml中

  2. 选择第二条搜索结果 Druid Spring Boot Starter
    在这里插入图片描述

  3. 选择版本1.2.15 坐标如下

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

  1. 之后刷新maven等待刷新成功后

在这里插入图片描述

1.2.4 配置文件application.yml

  1. 配置数据库连接信息
spring:
  application:
    name: reggie_take_out
  datasource:
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/reggie
      username: root
      password: 123456


在这里插入图片描述

  1. 配置 mybatisplus 开启驼峰命名
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

在这里插入图片描述

1.2.5 尝试启动项目

  1. 在启动类中使用注解 @Slf4j ,使用日志打印提示信息,查看项目是否能够正常启动

1.2.6 将前端静态页面引入到项目中

  1. 将前端资源 backend 、front 放到resources目录下

在这里插入图片描述

  1. 由于默认情况下项目是到resourcees目录下的static目录下寻找前端静态资源的,所以需要编写一个配置类,指定MVC框架下静态资源的访问
  2. 在这里插入图片描述
package com.itheima.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;

@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/");

    }
}

2、 后台系统登录功能

2.1 需求分析

  • 一个需要用户名和密码的登录界面

  • 在这里插入图片描述

2.2 代码开发

2.2.1 创建实体类

  1. 在entity包下创建实体类Employee.java
package com.itheima.entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;

import java.time.LocalDateTime;

/**
 * 员工实体类
 */
@Data
public class Employee {
    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; // 状态 0:禁用,1:正常

    private LocalDateTime createTime; // 创建时间

    private LocalDateTime updateTime; // 更新时间

    @TableField(fill = FieldFill.INSERT)
    private Long createUser; // 创建人

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser; // 修改人

}

2.2.2 创建Controller、Service、Mapper

  1. controller
package com.itheima.controller;

import com.itheima.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/employee")
public class EmployeeController {
    @Autowired
    EmployeeService employeeService;
}



  1. service
package com.itheima.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.entity.Employee;

public interface EmployeeService extends IService<Employee> {
}

  1. serviceimpl
package com.itheima.service.impl;

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

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




  1. mapper
package com.itheima.mapper;

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

@Mapper
public interface EmployeeMapper extends BaseMapper<Employee> {
}

  1. 导入统一响应结果类R
  • 此类是一个通用结果类,服务端响应的所有结果最终都会包装称此种类型返回给前端页面
package com.itheima.common;

import lombok.Data;

import java.util.HashMap;
import java.util.Map;

@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;
    }

}

2.2.3

  • 在controller中创建登录方法,处理逻辑如下
  1. 将页面提交的密码password进行md5加密处理
  2. 根据页面提交的用户名username查询数据库
  3. 如果没有查询到则返回登录失败结果
  4. 密码对比,如果不一致则返回登录失败结果
  5. 查看员工状态,如果是已禁用状态,则返回员工已禁用结果
  6. 登录成功,将员工id存入Session并返回登录成功结果

在这里插入图片描述
代码

package com.itheima.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.itheima.common.R;
import com.itheima.entity.Employee;
import com.itheima.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.DigestUtils;
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.HttpServletRequest;

@RestController
@RequestMapping("/employee")
public class EmployeeController {
    @Autowired
    EmployeeService employeeService;

    /**
     * 员工登录
     *
     * @param request
     * @param employee
     * @return
     */
    @PostMapping("/login")
    public R<Employee> login(HttpServletRequest request, @RequestBody Employee employee) {

        //1. 将页面提交的密码password进行md5加密处理
        String password = employee.getPassword();//得到前端发过来的用户密码
        password = DigestUtils.md5DigestAsHex(password.getBytes());//将密码进行md5加码,并重新赋值给原来的变量

        //2. 根据页面提交的用户名username查询数据库
        LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Employee::getUsername, employee.getUsername());
        Employee emp = employeeService.getOne(queryWrapper);

        //3. 如果没有查询到则返回登录失败结果
        if (emp == null) {
            return R.error("没有此用户");
        }

        //4. 密码对比,如果不一致则返回登录失败结果
        if (!emp.getPassword().equals(password)) {
            return R.error("密码错误");
        }

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

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



3、后台退出功能开发

3.1 需求分析

3.2 代码开发

  • 用户点击页面退出按钮,发送请求,请求地址为/employee/logout,请求方式为POST,沃恩只需要在Controller中创建对应的水方法即可,具体的处理逻辑:
  1. 清理Session中的用户id
  2. 返回结果
/**
     * 员工退出
     * @param request
     * @return
     */
    @PostMapping("/logout")
    public R<String> logout(HttpServletRequest request){
        //清理Session中保存的当前登录员工的id
        request.getSession().removeAttribute("employee");
        return R.success("退出成功");
    }

第二章

1. 完善登录功能

1.1 问题分析

  • 问题:前面已经完成了后台系统的员工登录功能开发,但是还存在一个问题:用户如果不登录,直接访问系统首页,照样可以正常访问。这种设计很不合理,我们希望看到的是,只有登陆成功之后才可以访问系统中的页面,如果没有登录就跳转到登录界面
  • 解决方法:使用过滤器或者拦截器,在过滤器或者拦截器中判断用户是否已经完成登录,如果没有登录则跳转到登录页面。

1.2 代码实现

  1. 实现步骤
    • 创建自定义过滤器LoginCheckFilter
    • 在启动类上加入注解@ServletComponentScan
    • 完善过滤器的处理逻辑
  2. 过滤器的具体处理逻辑如下
    • 获取本次请求的URI
    • 判断本次请求是否需要处理
    • 如果不需要处理,则直接放行
    • 判断登录状态,如果已登录,则直接放行
    • 如果未登录则返回未登录结果
      在这里插入图片描述
package com.itheima.filter;
import com.alibaba.fastjson.JSON;
import com.itheima.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;

/**
 * 检查用户是否已经完成登录
 */
@WebFilter(filterName = "loginCheckFilter",urlPatterns = "/*")
@Slf4j
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 requestURI = request.getRequestURI();// /backend/index.html

        log.info("拦截到请求:{}",requestURI);

        //定义不需要处理的请求路径
        String[] urls = new String[]{
                "/employee/login",
                "/employee/logout",
                "/backend/**",
                "/front/**"
        };


        //2、判断本次请求是否需要处理
        boolean check = check(urls, requestURI);

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

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

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

    }

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

1.3 功能测试

2. 新增员工

2.1 需求分析

  • 后台系统中可以管理员工信息,通过新增员工来添加后台系统用户。点击【添加员工】按钮跳转到新增页面如下:
    在这里插入图片描述

2.2 数据模型

  • 新增员工,其实就是将我们新增页面录入的盐工数据插入到employee表。需要注意的是,employee表中对username字段加入了唯一约束,因为username是员工的登录账号,必须是唯一的
    在这里插入图片描述
  • employee 表中的 status 字段已经设置了默认值1,表示状态正常。

2.3 代码开发

  • 整个程序的执行过程
  1. 页面发送ajax 请求,将新增员工页面中输入的数据已json的形式提交到服务端
  2. 服务端Controller接受页面提交的数据并调用Service将数据进行保存
  3. Service调用Mapper操作数据库,保存数据。
  /**
     * 新增员工
     * @param request
     * @param employee
     * @return
     */
    @PostMapping
    public R<String> save(HttpServletRequest request,@RequestBody Employee employee){
        log.info("新增员工,员工信息:{}",employee.toString());
        //设置初始密码123456,需要进行md5加密
        employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
        //设置创建时间和更改时间
        employee.setCreateTime(LocalDateTime.now());// LocalDateTime.now()获得系统当前时间
        employee.setUpdateTime(LocalDateTime.now());

        // 获得当前登录用户的id
        Long empId = (Long) request.getSession().getAttribute("employee");

        //设置创建人和创建时间
        employee.setCreateUser(empId);
        employee.setUpdateUser(empId);

        // 掉用方法将新增用户信息存储到数据库中
        employeeService.save(employee);
        return R.success("新增用户成功");
    }

2.4 异常处理

  • 前面的程序还存在一个问题,就是当我们在新增员工时输入的账号已经存在,由于employee表中对该字段加入了唯一约束,此时程序会抛出异常:
  • 此时需要我们的程序进行异常捕获,通常有两种处理方式:
    • 在Controller方法中加入try、catch进行异常捕获
    • 使用异常处理器进行全局异常捕获【下面我们采用本方法】
  1. 在common包中新建类GlobalExceptionHandler .java,代码如下
package com.itheima.common;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.sql.SQLIntegrityConstraintViolationException;

@Slf4j
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
public class GlobalExceptionHandler {
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex) {
        log.error(ex.getMessage());//获得当前错误信息:Duplicate entry 'zhangsan' for key 'employee.idx_username'
        if (ex.getMessage().contains("Duplicate entry")) {//如果错误信息是这种重复的类型的话
            String[] split = ex.getMessage().split(" ");//将错误信息按照空格进行分割
            String msg = split[2] + "已存在";// 拿到错误信息当中的用户名,制作提示信息
            return R.error(msg);
        }
        return R.error("未知错误");
    }
}

3. 员工信息分页查询

3.1 需求分析

  • 系统中的员工很多的时候,如果在一个页面中全部展示出来会显得比较乱,不便于查看,所以一般系统中都会以分页的方式来展示列表数据

3.2 代码开发

  • 在开发之前需要梳理一下整个程序的执行过程
  1. 页面发送 ajax 请求,将分页查询参数(page,pageSize,name)提交到服务端【前端页面已经完成这一步】
  2. 服务端Controller 接收页面提交的数据并调用Service查询数据
  3. Service 调用MApper操作数据库,查询分页数据
  4. Controller 将查询到的分页数据响应给页面
  5. 页面接收到分页数据并通过ElementUI的Table组件展示到页面上
3.2.1 分页查步骤
  1. 配置MyBatisPlus 的分页插件
  • 在config包下新建MybatisPlusConfig.java类
package com.itheima.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;

/**
 * 配置MP的分页插件
 */
@Configuration
public class MybatisPlusConfig {
    @Bean//交给IoC容器管理
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
        MybatisPlusInterceptor mybatisPlusInterceptor=new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mybatisPlusInterceptor;
    }
}

3.3 功能测试

4. 启用/禁用员工账号

4.1 需求分析

  • 在员工管理列表页面,可以对某个员工账号进行启用或者禁用操作。账号禁用的员工不能登录系统,启用后的员工可以正常登录
  • 需要注意,只有管理员(admin用户)可以对其他普通用户进行启用、禁用操作,所以普通用户登录系统后启用、禁用操作按钮不显示。
  • 如果某个员工账号状态为正常,则按钮显示为“禁用”,如果员工账号状态为已禁用,则按钮显示为“启用”
    在这里插入图片描述

4.2 代码开发

  • 开发之前,梳理一下整个程序的执行过程
  1. 页面发送 ajax 请求,将参数(id,status)提交到服务端
  2. 服务端Controller 接收页面提交的数据并调用Service更新数据
  3. Service调用Mapper操作数据库
  • 启用、禁用员工账号,本质上就是一个更新操作,也就是对status状态字段进行操作。在Controller中创建update方法,此方法是一个通用的修改员工信息的方法
/**
     * 修改员工信息:修改账号状态
     *
     * @param request  获取当前登录用户的id,泳衣更新被修改用户的更新人
     * @param employee 前端传来的被修改用户的相关信息
     * @return
     */
    @PutMapping
    public R<String> update(HttpServletRequest request, @RequestBody Employee employee) {
        log.info("被修改员工:{}", employee.toString());

        //获取当前登录员工的id
        Long empId = (Long) request.getSession().getAttribute("employee");
        //更新一下被修改员工的更新时间以及更新人
        employee.setUpdateTime(LocalDateTime.now());
        employee.setUpdateUser(empId);
        //调用service执行更新操作
        employeeService.updateById(employee);
        return R.success("员工信息修改成功");
    }

4.3 功能测试

  • 前端页面测试通过,并且程序也没有错误,但是查看数据库发现员工的账号信息并没有被修改,
  • 原因:Js对long形数据进行处理室丢失精度,导致提交的id和数据库中的id不一样
  • 解决方法:可以在服务端给页面响应json数据时进行处理,将long型数据同意转为String字符串

4.4 代码修复

  • 具体步骤
  1. 提供对象转换器 jacksonObjectMapper,基于 Jackson 进行 java 对象到 json 数据的转换
  2. 在 WebMvcConfig 配置类中扩展Spring mvc 的消息转换器,在此信息转换器中使用提供的对象转换器进行 java 对象到 json 数据的转换。
package com.itheima.common;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;

/**
 * 对象映射器:基于jackson将Java对象转为json,或者将json转为Java对象
 * 将JSON解析为Java对象的过程称为 [从JSON反序列化Java对象]
 * 从Java对象生成JSON的过程称为 [序列化Java对象到JSON]
 */
public class JacksonObjectMapper extends ObjectMapper {

    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";

    public JacksonObjectMapper() {
        super();
        //收到未知属性时不报异常
        this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);

        //反序列化时,属性不存在的兼容处理
        this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);


        SimpleModule simpleModule = new SimpleModule()
                .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))

                .addSerializer(BigInteger.class, ToStringSerializer.instance)
                .addSerializer(Long.class, ToStringSerializer.instance)
                .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));

        //注册功能模块 例如,可以添加自定义序列化器和反序列化器
        this.registerModule(simpleModule);
    }
}

/**
     * 扩展MVC 框架的消息转换器【为了解决id问题】
     * @param converters the list of configured converters to extend
     */
    @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. 编辑员工信息

5.1 需求分析

  • 在员工列表页面吊机编辑按钮,跳转到编辑页面,在编辑页面辉县员工信息并进行修改,最后点击保存按钮完成编辑操作。
    在这里插入图片描述

5.2 代码开发

  • 程序执行流程
  1. 点击编辑按钮时,页面跳转到add.html,并在url中携带参数【员工id】
  2. 在add.html页面获取url中的参数【员工id】
  3. 发送 ajax 请求,请求服务端,同时提交员工id参数
  4. 服务吨接受请求,根据员工id查询员工信息,将员工信息已json形式响应给页面
  5. 页面接收服务端响应的json数据,通过VUE的数据绑定进行员工信息回显
  6. 点击保存按钮,发送ajax请求,将页面中的员工信息以json方式提交给服务端
  7. 服务端接收员工信息,并进行处理,完成后给页面响应
  8. 页面接收到服务端响应信息后进行相应处理
    /**
     * 根据id查询员工信息
     * @param id 员工id,路径出纳火速
     * @return
     */
    @GetMapping("/{id}")
    public R<Employee> getById(@PathVariable Long id){
        log.info("根据id查询员工信息");
        //调用service根据id查询员工信息
        Employee employee=employeeService.getById(id);
        //如果查到的话就将信息回显
        if(employee!=null){
            return R.success(employee);
        }
        return R.error("没有查询到对应员工信息");
    }

5.3 功能测试

第三章 分类管理业务

1、公共字段自动填充

1.1 问题分析

  • 前面已经完成了后台系统员工管理功能开发,在新增员工时需要设置创建时间、创建人、修改时间、修改人等字段,在编辑员工时需要设置修改时间和修改人等字段。这些字段属于公共字段,也就是很多表中都有这些字段。能不能对于这些字段在某个地方同意处理,赖建华开发呢,大那就是使用mybatisplus提供的公共字段自动填充功能。

1.2 代码实现

  • Mybatis Plus公共字段自动填充,也就是在插入或者更新的时候为指定字段赋予指定的值,使用它的好处就是可以统一对这些字段进行处理,避免了重复代码。
  • 实现步骤
    • 在实体类的属性上加入@TableField注解,指定自动填充的策略

    • 按照框架要求编写元数据对象处理器,在此类中统一为公共字段赋值,此类需要实现MetaObjectHandler接口。

在这里插入图片描述

  • 在 common 包下新建类 MyMetaObjecthandler ,代码如下
package com.itheima.common;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

@Component//交由容器管理
@Slf4j
public class MyMetaObjecthandler implements MetaObjectHandler {
    /**
     * 插入数据时自动填充
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("公共字段自动填充[insert]...");
        log.info(metaObject.toString());
        metaObject.setValue("createTime", LocalDateTime.now());
        metaObject.setValue("updateTime", LocalDateTime.now());
        metaObject.setValue("createUser", new Long(1));
        metaObject.setValue("updateUser", new Long(1));

    }

    /**
     * 更新数据时自动填充
     * @param metaObject
     */
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("公共字段自动填充[update]...");
        log.info(metaObject.toString());
        metaObject.setValue("updateTime" ,LocalDateTime.now());
        metaObject.setValue("updateUser",new Long(1));

    }
}

1.3 功能测试

1.4 功能完善

  • 问题:在自动填充createUser 和updateUser 的时候需要改造成动态获取当前登录用户的id。但是在MyMetaObjectHandler类中是不能够获得httpSession对象的,岁需要通过其他的方式来获取登录用户的id
  • 解决方法:可以使用 ThreadLocal来解决此问题,是JDK中提供的一个类
1.4.1 ThreadLocal
  • 客户端发送的每次http请求,对应的在服务端都会分配一个新的线程来处理,在处理过程中设计到下面类中的方法都属于相同一个线程。
    • LoginCheckFilter的doFilter方法
    • EmployeeController的update 方法
    • MyMetaObjectHandler的updateFill方法
  1. 什么是ThreadLocal
    • ThreadLocal并不是一个Thread,而是Thread的局部变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。
  2. ThreadLocal常用方法
    • public void set(T value) 设置当前线程的线程局部变量的值
    • public T get() 返回当前线程所对应的线程局部变量的值
  • 所以我们可以在LoainCheckfilter的doFilter方法中获取当前登录用户id,并调用ThreadLocal的set方法来设置当前线程的线程局部变量的值(用户id),然后在MyMeta0bjectHandler的updateFil方法中调用ThreadLocal的get方法来获得当前线程所对应的线程局部变量的值(用户id)。
  1. 实现步骤
    • 编写BaseContext工具类,基于ThreadLocal封装的工具类
    • 在LoginCheckFilter的doFilter方法中调用BaseContext来设置当前登录用户的id
    • 在MyMetaObjectHandler的方法中调用BaseContext获取登录用户的id

2、 新增分类

2.1 需求分析

  • 后台系统中可以管理分类信息,分类包括两种类型,分别是菜品分类和套餐分类。当我们在后台系统中添加菜品时需要选择一个菜品分类,当我们在后台系统中添加一个套餐时需要选择一个套餐分类,在移动端也会按照菜品分类和套餐分类来展示对应的菜品和套餐。
    在这里插入图片描述
  • 可以在后台系统的分类管理页面分别添加菜品分类和套餐分类,如下

在这里插入图片描述

2.2 数据模型

  • 新增分类,其实就是将我们新增窗口录入的分类数据插入到Category表,表结构 如下:
    在这里插入图片描述
  • 需要注意的是category表中对name字段加入了唯一约束,保证分类的名称是唯一的。

2.3 代码开发

  • 在开发业务功能之前先将需要用到的类和接口基本结构创建好
    • 实体类Category
    • Mapper接口 CategoryMapper
    • 业务层接口CategoryService
    • 业务层实现类CategoryServiceImpl控制层CategoryController

2.3.1 搭建框架

  1. Category
package com.itheima.entity;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Category {
    private static final long serialVersionUID = 1L;

    private Long id;

    //类型 1 菜品分类 2 套餐分类
    private Integer type;

    //分类名称
    private String name;

    //顺序
    private Integer sort;

    //创建时间
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    //更新时间
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;

    //创建人
    @TableField(fill = FieldFill.INSERT)
    private Long createUser;

    //修改人
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;
}

  1. CategoryMapper
package com.itheima.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.itheima.entity.Category;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface CategoryMapper extends BaseMapper<Category> {
}

  1. CategoryService
package com.itheima.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.entity.Category;

public interface CategoryService extends IService<Category> {
}

  1. CategoryServiceImpl
package com.itheima.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.entity.Category;
import com.itheima.mapper.CategoryMapper;
import com.itheima.service.CategoryService;
import org.springframework.stereotype.Service;

@Service
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {
}

  1. CategoryController
package com.itheima.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
@RequestMapping("/category")
public class CategoryController {
}

2.3.2 逻辑代码开发

  • 程序执行过程
    • 页面(backend/page/category/list.html)发送ajax请求,将新增分类窗口输入的数据以json形式提交到服务端
    • 服务端Controller接收页面提交的数据并调用Service将数据进行保存
    • Service调用Mapper操作数据库,保存数据
  • 新增菜品分类和新增套餐分类请求的服务端地址和提交的json数据结构相同,所以服务端只需要提供一个方法统一处理即可
package com.itheima.controller;

import com.itheima.common.R;
import com.itheima.entity.Category;
import com.itheima.service.CategoryService;
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;

@Slf4j
@RestController
@RequestMapping("/category")
public class CategoryController {
    @Autowired
    CategoryService categoryService;

    /**
     * 新增分类
     * @param category
     * @return
     */
    @PostMapping
    public R<String> save(@RequestBody Category category){
        log.info("category:{}",category );
        categoryService.save(category);
        return R.success("新增分类成功");
    }
}

2.4 功能测试

3、 分类信息分页查询

3.1 需求分析

  • 系统中的分类很多的时候,如果在一个页面中全部展示出来会显得比较乱,不便于查看,所以一般的系统中都会以分页的方式来展示列表数据。
    在这里插入图片描述

3.2 代码开发

  • 程序执行过程
    • 页面发送ajax请求,将分页查询参数(page、pageSize)提交到服务端
    • 服务端Controller接收页面提交的数据并调用Service查询数据
    • Service调用Mapper操作数据库,查询分页数据
    • Controller将查询到的分页数据响应给页面
    • 页面接收到分页数据并通过ElementUl的Table组件展示到页面上
    /**
     * 分类信息分页查询
     * @param page
     * @param pageSize
     * @return
     */
    @GetMapping("/page")
    public R<Page> page(int page,int pageSize){
        //分页构造器
        Page pageInfo=new Page(page,pageSize);
        //条件构造器
        LambdaQueryWrapper<Category> queryWrapper=new LambdaQueryWrapper();
        //添加排序条件,根据sort进行排序
        queryWrapper.orderByAsc(Category::getSort);
        //分页查询
        categoryService.page(pageInfo,queryWrapper);
        return R.success(pageInfo);
    }

3.3. 功能测试

4、 删除分类

4.1 需求分析

  • 在分类管理列表页面,可以对某个分类进行删除操作。需要注意的是当分类关联了菜品或者套餐时,此分类不允许删除。
    在这里插入图片描述

4.2 代码开发

  • 梳理程序执行过程
    • 页面发送ajax请求,将参数(id)提交到服务端
    • 服务端Controller接收页面提交的数据并调用Service删除数据
    • Service调用Mapper操作数据库
/**
     * 根据id删除分类
     * @param id
     * @return
     */
    @DeleteMapping("/{id}")
    public R<String> deleteById(Long id){
        log.info("删除分类,id为:{}",id);
        //调用sevice将数据在数据库中删除
        categoryService.removeById(id);
        return R.success("删除分类成功");
    }

4.3 功能测试

注意:将资料中的backend.api.category.js中的两处ids改为id,之后在页面使用Ctrl+F5清除缓存
在这里插入图片描述

4.4 功能完善

  • 前面我们已经实现了根据id删除分类的功能,但是并没有检查删除的分类是否关联了菜品或者套餐,所以我们需要进行功能完善。
  • 要完善分类删除功能,需要先准备基础的类和接口
    • 实体类Dish和Setmeal
    • Mapper接口DishMapper和SetmealMapper
    • Service接口DishService和SetmealService
    • Service实现类DishServicelmpl和SetmealServicelmpl
  • 最主要的代码,在CategoryServiceImpl中实现方法remove
package com.itheima.service.impl;

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

@Slf4j
@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<>();
        //添加查询条件,根据分类id进行查询
        setmealLambdaQueryWrapper.eq(Setmeal::getCategoryId,id);
        int count2 = setmealService.count(setmealLambdaQueryWrapper);
        if(count2 > 0){
            //已经关联套餐,抛出一个业务异常
            throw new CustomException("当前分类下关联了套餐,不能删除");
        }

        //正常删除分类
        super.removeById(id);
    }
}

5、 修改分类

5.1 需求分析

  • 在分类管理列表页面点击修改按钮,弹出修改窗口,在修改窗口回显分类信息并进行修改,最后点击确定按钮完成修改操作
  • 在这里插入图片描述

5.2 代码开发

/**
     * 根据id更新分类
     * @param category 前端传过来的JSON数据
     * @return
     */
    @PutMapping
    public R<String> update(@RequestBody Category category){
        categoryService.updateById(category);
        return R.success("更新成功");
    }

5.3 功能测试

第四章 菜品管理业务开发

1. 文件上传下载

1.1 文件上传介绍

  • 文件上传,也称为upload,是指将本地图片、视频、音频等文件上传到服务器上,可以供其他用户浏览或下载的过程。
  • 文件上传时,对页面的form表单有如下要求:
    • method=“post” 采用post方式提交数据
    • enctype=“multipart/form-data” 采用multipart格式上传文件
    • type=“file” 使用input的file控件上传
  • 举例
    在这里插入图片描述
  • 服务端要接收客户端页面上传的文件,通常都会使用Apache的两个组件
    • commons-fileupload
    • commons-io
  • Spring框架在spring-web包中对文件上传进行了封装,大大简化了服务端代码,我们只需要在Controller的方法中声明一个MultipartFile类型的参数即可接收上传的文件,例如:
    在这里插入图片描述

1.2 文件下载介绍

  • 文件下载,也称为download,是指将文件从服务器传输到本地计算机的过程
  • 通过浏览器进行文件下载,通常有两种表现形式
    • 以附件形式下载,弹出保存对话框,将文件保存到指定磁盘目录
    • 直接在浏览器中打开
  • 通过浏览器进行文件下载,本质上就是服务端将文件以流的形式写回浏览器

1.3 文件上传代码实现

  1. 将upload.html页面放到page/demo/upload.html 下
  2. 根据请求路径可是需要的路径是"/common/upload" ,在 controller 文件夹下新建类 CommonController.java 代码如下
  3. 可以在过滤器中放行这一部分页面,并在配置文件 applicaton.yml当中配置好上传的文件要存放的基本路径
package com.itheima.controller;

import com.itheima.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;

@Slf4j
@RestController
@RequestMapping("/common")
public class CommonController {
    //引入配置文件中的基本路径
    @Value("${reggie.path}")
    private String basePath;

    /**
     * 文件上传
     *
     * @param file
     * @return
     */
    @PostMapping("/upload")
    public R<String> upload(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 + "hello.jpg"));
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null;
    }
}

在这里插入图片描述

1.4 文件下载代码实现

  • 文件下载,页面端可以使用<img> 标签展示下载的图片
    在这里插入图片描述
/**
     * 文件下载
     * @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) {
            e.printStackTrace();
        }

    }

2. 新增菜品

2.1 需求分析

  • 后台系统中可以管理菜品信息,通过新增功能来添加一个新的菜品,在添加菜品时需要选择当前菜品所属的菜品分类并且需要上传菜品图片,在移动端会按照菜品分类来展示对应的菜品信息。

2.2 数据模型

  • 新增菜品,其实就是将新增页面录入的菜品信息插入到dish表,如果添加了口味做法,还需要向dish_flavor表插入数据。所以在新增菜品时,涉及到两个表:
    • dish 菜品表
    • dish_flavor 菜品口味表
      在这里插入图片描述

在这里插入图片描述

2.3 代码开发

  • 在开发业务之前,现将需要用到的类和接口基本结构创建好
    • 实体类 DishFlavor
    • Mapper接口 DishFlavorMapper
    • 业务层接口 DishFlavorService
    • 业务层实现类 DishFlavorServicelmpl
    • 控制层 DishController【对于dish的操作和dish_flavor的操作都在DishCotroller中进行,不需要创建DishFlavorController.java】
  • 在代码开发之前,需要梳理一下新增菜品时前端页面和服务器的交互过程
    • 页面(backend/page/food/add.html)发送ajax请求,请求服务端获取菜品分类数据并展示到下拉框中
    • 页面发送请求进行图片上传,请求服务端将图片保存到服务器
    • 页面发送请求进行图片下载,将上传的图片进行回显
    • 点击保存按钮,发送ajax请求,将菜品相关数据以ison形式提交到服务端
  • 因为页面传给后台的数据相当于包含两个实体的数据,导入DTO,原始是因为页面提交的数据太过复杂,是两个实体类的集合,所以我们需要一个合并的类,来封装页面提交的数据。
package com.itheima.dto;

import com.itheima.entity.Dish;
import com.itheima.entity.DishFlavor;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.ArrayList;
import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class DishDto extends Dish {
    private List<DishFlavor> flavors=new ArrayList<>();
    private String categoryName;
    private Integer copies;
}

  • 因为前端传来的数据复杂,所以需要重写保存方法,在DishServiceImpl中重写
  1. DishService
package com.itheima.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.dto.DishDto;
import com.itheima.entity.Dish;

public interface DishService extends IService<Dish> {
    //新增菜品,同时插入菜品对应的口味数据,需要操作两张表:dish,dish_flavor
    public void saveWithFlavor(DishDto dishDto);
}

  1. DishServiceImpl
package com.itheima.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.dto.DishDto;
import com.itheima.entity.Dish;
import com.itheima.entity.DishFlavor;
import com.itheima.mapper.DishMApper;
import com.itheima.service.DishFlavorService;
import com.itheima.service.DishService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
public class DishServiceImpl extends ServiceImpl<DishMApper, Dish> implements DishService {
    @Autowired
    private DishFlavorService dishFlavorService;
    /**
     * 新增菜品,同时保存对应的口味数据
     * @param dishDto
     */
    @Override
    @Transactional //因为遥操作两个表所以需要加上事务管理,并且徐亚在启动类加上相应的注解@EnableTransactionManagement
    public void saveWithFlavor(DishDto dishDto) {
        //保存菜品的基本信息到菜品表dish
        this.save(dishDto);
        //菜品id
        Long dishId = dishDto.getId();
        //菜品口味
        List<DishFlavor> flavors=dishDto.getFlavors();
        //给菜品口味加上菜品id
        for(int i=0;i<flavors.size();i++){
            flavors.get(i).setDishId(dishId);
        }
        //保存菜品口味数据,到菜品口味表
        dishFlavorService.saveBatch(flavors);

    }
}

  1. DishController
package com.itheima.controller;

import com.itheima.common.R;
import com.itheima.dto.DishDto;
import com.itheima.entity.Dish;
import com.itheima.entity.DishFlavor;
import com.itheima.service.DishFlavorService;
import com.itheima.service.DishService;
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;

@Slf4j
@RestController
@RequestMapping("/dish")
public class DishController {
    @Autowired
    DishService dishService;
    @Autowired
    DishFlavorService dishFlavorService;
    @PostMapping
    public R<String> save(@RequestBody DishDto dishDto){
        //调用service保存数据
//        log.info("新增的菜品信息:{}",dishDto);
//        dishService.save(dishDto);
        dishService.saveWithFlavor(dishDto);
        return R.success("添加成功");

    }
}

  1. 因为使用了事务管理,所以启动类上需要加上相应的注解 @EnableTransactionManagement
    在这里插入图片描述

2.4 功能测试

3. 菜品信息分页查询

3.1 需求分析

  • 系统中的菜品数据很多的时候,如果在一个页面中全部展示出来会显得比较乱,不便于查看,所以一般的系统中都会以分页的方式来展示列表数据。
  • 并且这个分页查询需要查询两个表的数据
  • 在这里插入图片描述

3.2 代码开发

  • 在开发代码之前,需要梳理一下菜品分页查询时前端页面和服务端的交互过程
    • 页面发送ajax请求,将分页查询参数(page、pageSize、name)提交到服务端,获取分页数据
    • 页面发送请求,请求服务端进行图片下载,用于页面图片展示
/**
     * 菜品信息分页查询
     * @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> dtoPageInfo=new Page<>();
        //菜品查询构造器
        LambdaQueryWrapper<Dish> queryWrapper=new LambdaQueryWrapper();
        //模糊查询
        queryWrapper.like(StringUtils.isNotEmpty(name),Dish::getName,name);
        //分类查询构造器
        //调用service分页查询
        dishService.page(pageInfo,queryWrapper);
        //对象拷贝将 pageInfo 的所有属性拷贝到 dtoPageInfo 忽略属性 records
        BeanUtils.copyProperties(pageInfo,dtoPageInfo,"records");
        //自己设置records属性
        List<Dish> records = pageInfo.getRecords();
        List<DishDto> list=new ArrayList<>();
        for(int i=0;i<records.size();i++){
            Dish dish = records.get(i);
            //获取分类id
            Long categoryId = dish.getCategoryId();
            //根据分类id查询分类对象
            Category category = categoryService.getById(categoryId);
            DishDto dishDto=new DishDto();
            //将其余的对象赋值给dishdto
            BeanUtils.copyProperties(dish,dishDto);
            if(category!=null){
                //获取分类的名称
                String categoryName = category.getName();
                //将分类名称赋值给DishDto
                dishDto.setCategoryName(categoryName);
                //将dishdto加入到列表list中
                list.add(dishDto);
            }
        }
        //设置Records【之前对象拷贝的时候除去了records属性,现在自己设置】
        dtoPageInfo.setRecords(list);
        return R.success(dtoPageInfo);
    }

3.3 功能测试

4. 修改菜品

4.1 需求分析

  • 在菜品管理列表页面点击“修改”按钮,跳转到修改菜品页面,在修改页面辉县菜品相关信息并进行修改,最后点击确定按钮完成操作。
    在这里插入图片描述

4.2 代码开发

  • 梳理一下修改菜品是前端页面和服务端的交互过程
    • 页面发送ajax请求,请求服务端获取分类数据,用于菜品分类下拉框中数据展示【已实现】
    • 页面发送ajax请求,请求服务端,根据id查询当前菜品信息,用于菜品信息回显
    • 页面发送请求,请求服务端进行图片下载,用于页图片回显
    • 点击保存按钮,页面发送ajax请求,将修改后的菜品相关数据以json形式提交到服务端
  1. 回显controller
/**
     * 根据id查询并回显数据【修改菜品时】
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    public R<DishDto> get(@PathVariable Long id){

        DishDto dishDto = dishService.getByIdWithFlavor(id);
        //返回结果
        return R.success(dishDto);
    }

  1. 回显serviceimpl
/**
     * 根据id查询菜品信息和对应的口味信息
     * @param id
     * @return
     */
    @Override
    public DishDto getByIdWithFlavor(Long id) {
        //使用DishService根据id查询数据库
        Dish dish = this.getById(id);
        //得到dishId
        Long dishId = dish.getId();

        //根据dishId,查询DishFlavor得到dish的口味存放到list当中
        //查询构造器
        LambdaQueryWrapper<DishFlavor> dishFlavorLambdaQueryWrapper=new LambdaQueryWrapper<>();
        dishFlavorLambdaQueryWrapper.eq(DishFlavor::getDishId,dishId);
        List<DishFlavor> list = dishFlavorService.list(dishFlavorLambdaQueryWrapper);

        //构造dishDto对象
        DishDto dishDto=new DishDto();
        //将dish复制给dishdto
        BeanUtils.copyProperties(dish,dishDto);
        //list将值赋值给dishDto
        dishDto.setFlavors(list);
        return dishDto;
    }
  1. 修改内容controller
/**
     * 修改菜品
     * @param dishDto
     * @return
     */
    @PutMapping
    public R<String> update(@RequestBody DishDto dishDto){
        //调用service将数据保存到数据库
        dishService.updateWithFlavor(dishDto);

        //返回结果
        return R.success("数据保存成功");
    }
  1. 修改内容service
/**
     * 修改菜品的同时对口味进行修改
     * @param dishDto
     */
    @Override
    @Transactional
    public void updateWithFlavor(DishDto dishDto) {
        //修改菜品的基本信息到菜品表
        this.updateById(dishDto);

        //清理当前菜品对应的口味数据
        //创建一个查询构造器
        LambdaQueryWrapper<DishFlavor> queryWrapper=new LambdaQueryWrapper<>();
        //添加查询条件【select * from dish_flavor where dish_id=id】
        queryWrapper.eq(DishFlavor::getDishId,dishDto.getId());
        //删除查询结果
        dishFlavorService.remove(queryWrapper);

        //添加当前提交的口味数据
        //获得当前菜品口味
        List<DishFlavor> list = dishDto.getFlavors();
        //给当前菜品口味添加上对应的dishId
        Long dishId = dishDto.getId();
        for(int i=0;i<list.size();i++){
            list.get(i).setDishId(dishId);
        }
        
        //调用service 保存数据到数据库
        dishFlavorService.saveBatch(list);
        
    }

4.3 功能测试

第五章 套餐管理业务开发

1. 新增套餐

1.1 需求分析

  • 套餐就是菜品的集合。后台系统中可以管理套餐信息,通过新增套餐功能来添加一个新的套餐,在添加套餐时需要选择当前套餐所属的套餐分类和包含的菜品,并且需要上传套餐对应的图片,在移动端会按照套餐分类来展示对应的套餐。

1.2 数据模型

  • 所以在新增套餐时,涉及到两个表: 新增套餐,其实就是将新增页面录入的套餐信息插入到setmeal表,还需要向setmeal _dish表插入套餐和菜品关联数据。
    • setmeal 套餐表
    • setmeal_dish 套餐菜品关系表

1.3 代码开发

  • 开发之前现将需要用到的类和接口基本结构创建好
    • 实体类 SetmealDish
    • DTO SetmealDto
    • Mapper接囗 SetmealDishMapper
    • 业务层接口 SetmealDishService
    • 业务层实现类 SetmealDishServicelmpl
    • 控制层 Setmealcontroller
  • 前端页面和服务端的交互过程
    • 页面(backend/page/combo/add.html)发送ajax请求,请求服务端获取套餐分类数据并展示到下拉框中
    • 页面发送ajax请求,请求服务端获取菜品分类数据并展示到添加菜品窗口中
    • 页面发送ajax请求,请求服务端,根据菜品分类查询对应的菜品数据并展示到添加菜品窗口中
    • 页面发送请求进行图片上传,请求服务端将图片保存到服务器
    • 页面发送请求进行图片下载,将上传的图片进行回显
    • 点击保存按钮,发送ajax请求,将套餐相关数据以json形式提交到服务端
  1. setmealserviceimpl
 /**
     * 保存套餐的同时保存套餐中的相关菜品
     * @param setmealDto
     */
    @Override
    @Transactional
    public void saveWithSetmealDish(SetmealDto setmealDto) {
        //首先保存套餐的基本信息
        this.save(setmealDto);

        //保存套餐中的菜品信息
        //获取菜品信息
        List<SetmealDish> setmealDishes = setmealDto.getSetmealDishes();
        //获取本套餐的id
        Long setmealId = setmealDto.getId();
        //将菜品信息加上套餐id
        for(int i=0;i<setmealDishes.size();i++){
            setmealDishes.get(i).setSetmealId(setmealId);
        }

        //调用service 批量保存信息
        setmealDishService.saveBatch(setmealDishes);

    }
  1. setmealController
/**
     * 新增套餐
     * @param setmealDto
     * @return
     */
    @PostMapping
    public R<String> save(@RequestBody SetmealDto setmealDto){
        //调用service 保存信息
        setmealService.saveWithSetmealDish(setmealDto);

        return R.success("新增套餐成功");
    }

1.4 功能测试

2. 套餐信息分页查询

2.1 需求分析

  • 系统中的套餐数据很多的时候,如果在一个页面中全部展示出来会显得比较乱,不便于查看,所以一般的系统中都会以分页的方式来展示列表数据。

2.2 代码开发

  • 梳理前端页面和服务端交互过程
    • 页面发送 ajax 请求,将分页查询参数(page,pageSize,name)提交到服务端,获取分页数据
    • 页面发送请求,请求服务端进行图片下载,用于页面图片展示
/**
     * 套餐分页查询
     * @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<>();

        //对象copy
        BeanUtils.copyProperties(pageInfo,dtoPage,"records");

        //查询构造器
        LambdaQueryWrapper<Setmeal> queryWrapper=new LambdaQueryWrapper<>();
        //添加查询条件【套餐名模糊查询、排序条件】
        queryWrapper.like(name!=null,Setmeal::getName,name);
        queryWrapper.orderByDesc(Setmeal::getUpdateTime);

        //调用service 查询数据库
        setmealService.page(pageInfo,queryWrapper);

        //自定义 dtopage 中的 records
        //获得pageInfo 的records
        List<Setmeal> records = pageInfo.getRecords();
        List<SetmealDto> list=new ArrayList<>();
        //遍历将records中的每一个都加上categoryname
        //将其余属性复制到新建的对象中
        BeanUtils.copyProperties(records,list);
        for(int i=0;i<records.size();i++){
            //获得当前对象
            Setmeal setmeal = records.get(i);
            //获得当前对象的分类id
            Long categoryId = setmeal.getCategoryId();
            //新建一个对象
            SetmealDto setmealDto=new SetmealDto();
            //现将其余属性赋值到目标对象
            BeanUtils.copyProperties(setmeal,setmealDto);
            if(categoryId!=null){
                //根据id查询数据库获得分类
                Category category = categoryService.getById(categoryId);
                //获得分类的name
                String categoryName = category.getName();
                //将name赋值给setmealDto
                setmealDto.setCategoryName(categoryName);
                list.add(setmealDto);
            }
        }
        dtoPage.setRecords(list);
        return R.success(dtoPage);
    }

2.3 功能测试

3. 删除套餐

3.1 需求分析

  • 在套餐管理列表页面点击删除按钮,可以删除对应的套餐信息。也可以通过复选框选择多个套餐,点击批量删除按钮一次删除多个套餐。注意,对于状态为售卖中的套餐不能删除,需要先停售,然后才能删除
  • 在这里插入图片描述

3.2 代码开发

  • 梳理一下删除套餐时前端页面和服务端的交互过程
    • 删除单个套餐是,页面发送ajax请求,根据套餐id删除对应套餐
      在这里插入图片描述

    • 删除多个套餐时,页面发送ajax请求,根据提交的多个套餐id删除对应套餐
      在这里插入图片描述

  • 观察易知删除单个和删除一个的请求地址和请求方式都是相同的,服务端可以提供一个方法来统一处理
  1. SetmealSerbiceImpl
 /**
     * 删除套餐的同时删除套餐相关联的菜品
     * @param ids 套餐id
     */
    @Override
    public void deleteWithSetmealDish( List<Long> ids) {
        //查询套餐状态,判断是否可以删除【正在售卖中的套餐不可以删除】
        //删除套餐信息
        //根据id查询数据库
        LambdaQueryWrapper<Setmeal> queryWrapper=new LambdaQueryWrapper<>();
        queryWrapper.in(Setmeal::getId,ids);
        //根据售卖状态查询数据库
        queryWrapper.eq(Setmeal::getStatus,1);
        int count=this.count(queryWrapper);
        if(count>0){
            throw new CustomException(("套餐正在售卖中,不能删除"));
        }
        //首先调用service删除套餐基本信息
        this.removeByIds(ids);

        //删除相关菜品
        //查询构造器
        LambdaQueryWrapper<SetmealDish> setmealDishLambdaQueryWrapper=new LambdaQueryWrapper<>();
        setmealDishLambdaQueryWrapper.in(SetmealDish::getSetmealId,ids);
        setmealDishService.remove(setmealDishLambdaQueryWrapper);
    }
  1. SetmealController
/**
     * 删除套餐或者批量删除
     * @param ids
     * @return
     */
    @DeleteMapping
    public R<String> deleteById(@RequestParam List<Long> ids){
        //调用service删除当前套餐
        setmealService.deleteWithSetmealDish(ids);

        //返回结果
        return R.success("删除套餐成功");
    }

3.3 功能测试

第六章 手机验证码登录

1. 短信发送

2. 手机验证码登录

2.1 需求分析

  • 为了方便用户登录,移动端通常都会提供通过手机验证码登录的功能
  • 手机验证码登录的优点
    • 方便快捷,无需注册,直接登录
    • 使用短信验证码作为登录凭证,无需记忆密码
    • 安全
  • 登录流程
    • 输入手机号-》获取验证码-》输入验证码-》点击登录-》登录成功
      注意:通过手机验证码登录,手机号是区分不同用户的标识

2.2 代码开发

  • 开发之前现将用到的类和接口基本结构创建好
    • 实体类 User
    • Mapper接口 UserMapper
    • 业务层接口 UserService
    • 业务层实现类 UserServicelmpl
    • 控制层 UserController
    • 工具类SMSUtils、ValidatecodeUtils
  • 前端页面和服务端的交互过程
    • 在登录页面输入手机号,点击【获取验证码】按钮,页面发送ajax请求,在服务端调用短信服务API给指定手机号发送短信验证码短信
    • 在登录页面输入验证码,点击【登录】按钮,发送ajax请求,在服务端处理登录请求
2.2.1. 代码开发-修改LoginCheckFilter

在这里插入图片描述

在这里插入图片描述

2.2.2 UserController.java
package com.itheima.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.itheima.common.CustomException;
import com.itheima.common.R;
import com.itheima.entity.User;
import com.itheima.service.UserService;
import com.itheima.utils.ValidateCodeUtils;
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;

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

@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;

    /**
     * 发送短信
     * @param user
     * @param session
     * @return
     */
    @PostMapping("/sendMsg")
    public R<String> senMsg(@RequestBody User user,HttpSession session){
        //获取手机号
        String phone=user.getPhone();

        if(phone!=null){
            //生成随机的4为验证码
            String code=ValidateCodeUtils.generateValidateCode(4).toString();
            log.info("code={}",code);

            //调用阿里云提供的短信服务API完成发送短信


            //需要将生成的验证码保存到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();

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

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

        //进项验证码的比对(页面提交的验证码和session中保存的验证码)
        if(codeInSession!=null&&codeInSession.equals(code)){
            //如果能够比对成功说明登录成功
            //根据手机号查询数据库,如果有的话说明已经注册了,如果没有的话注册该用户
            LambdaQueryWrapper<User> userLambdaQueryWrapper=new LambdaQueryWrapper<>();
            userLambdaQueryWrapper.eq(User::getPhone,phone);
            User user = userService.getOne(userLambdaQueryWrapper);
            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("登录失败");

    }
}

2.3 功能测试

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

解析网页数据并且处理网页正则表达式与re模块

目录 一、解析网页数据的技术 1&#xff0e;正则表达式 2&#xff0e;XPath 3&#xff0e;Beautiful Soup 4&#xff0e;JSONPath 二、正则表达式的语法 1&#xff0e;元字符 2&#xff0e;预定义字符集 三、 re 模块的使用 1&#xff0e;创建 Pattern 对象 2&#…

libreoffice报: error while loading shared libraries: libcups.so.2: cannot

切换到cd /opt/libreoffice7.1/programlibreoffice查看版本的时候报:/opt/libreoffice7.1/program/soffice.bin: error while loading shared libraries: libcups.so.2: cannot open shared object file: No such file or directory ./soffice --version解决办法&#xff1a; …

MySQL之复制(六)

复制 复制拓扑 拥有备库的主-主结构 另外一种相关的配置是为每个主库增加一个备库&#xff0c;如图所示。这种配置的优点是增加了冗余&#xff0c;对于不同地理位置的复制拓扑&#xff0c;能够消除站点单点失效的问题。你也可以像平常一样&#xff0c;将读查询分配到备库上。…

深入JVM:详解JIT即时编译器

文章目录 深入JVM&#xff1a;详解JIT即时编译器一、序言二、基础概念1、何为JIT即时编译2、热点代码 三、HotSpot内置的即时编译器1、C1编译器2、C2编译器3、分层编译3.1 协作流程 四、常见JIT优化技术1、方法内联2、逃逸分析&#xff08;1&#xff09;同步锁消除&#xff08;…

从零开始学习Linux(10)----动静态库

目录 1.制作静态库 1.编写源代码 2.编译 3.打包库 4.使用库 2.制作动态库 1.编译 2.打包库 3.总结 3.理解动态库加载 1.站在系统角度理解 2.谈谈编址&#xff0c;可执行程序 1.制作静态库 1.编写源代码 假设你编写了两个源代码文件mymath.c和myprintf.c以及它们对应…

vuex的actions返回结果类型是promise及actions方法互相调用

this.$store.dispatch(‘logout’)返回的结果是Promise类型的 调用成功的情况下&#xff0c;返回状态为fulfilled&#xff0c;值为undefined。 所以可以直接进行.then操作&#xff1a; this.$store.dispatch(logout).then((result) > {console.log(result); });因为 Vuex …

正则表达式;grep、sed、awk、soft、uniq、tr 详解

正则表达式 概念 正则表达式&#xff08;Regular Expression&#xff0c;常简写为regex、regexp或RE&#xff09;是一种强大的文本处理工具&#xff0c;它使用一种特殊的字符序列来帮助用户检查一个字符串是否与某种模式匹配。 标准正则表达式 首先安装正则表达式pcre库 创…

草莓不是莓,西瓜才是莓——解读 Kubernetes 中被驱逐的 Pod

每天都有成千上万的 Pod 从节点中被驱逐。它们无家可归、困惑不已&#xff0c;不得不放弃之前的生活方式。其中一些甚至变成无节点状态。 在 Kubernetes 中 Pod 被驱逐意味着什么&#xff1f;我们常常能看到 Pod 因为资源不足被终止。但为什么会发生这种情况呢&#xff1f; 驱…

ChatGPT API技术教程OpenAI APIKey在线对接-Chat Completion对象

表示模型根据提供的输入返回的聊天完成响应。 {"id": "chatcmpl-123","object": "chat.completion","created": 1677652288,"model": "gpt-3.5-turbo-0125","system_fingerprint": "fp…

ElementUI组件库,分页组件靠右显示

ElementUI组件库&#xff0c;分页组件靠右显示 分页组件 &#xff0c;el-pagination组件默认靠左显示&#xff0c;靠右显示的话只需给layout添加一个“->”属性值即可, 看图

AMEYA360代理品牌江苏润石:RS8661/2/4系列高压精密低噪声运算放大器

继RS8651/2/4系列高压精密低噪声运算放大器成功推向市场&#xff0c;润石科技再次成功量产RS8661/2/4系列高压精密低噪声运算放大器。 RS8661/2/4系列产品将工作电压提升到最高36V(18V)、失调电压进一步优化到5μV、在工业现场数据采集、各种仪器仪表测量设备\分析设备上有着广…

IDEA services模块无法启动springboot服务(添加了springboot但是为空白)

https://blog.csdn.net/m0_54042402/article/details/117918995 https://blog.csdn.net/qq_46550964/article/details/122235235 Alt8 显示services模块 发现有springboot启动模块&#xff0c;点一下springboot之后&#xff0c;这个模块就消失了 会自动在.idea文件夹下的work…

选专业填志愿,家庭经济条件是必须考虑因素

对比过去&#xff0c;大部分家庭的物质条件已经好很多了&#xff0c;但也有一部分家庭条件较为困难&#xff0c;对于家庭条件较为困难的高考学生而言&#xff0c;高考志愿填报需要考虑的因素更多&#xff0c;因为自己就读的专业&#xff0c;绝对不能是自己的家庭无法负担的专业…

数值分析笔记(三)函数逼近

最佳平方逼近 函数逼近是使用一种简单易算的函数来近似表示一个复杂函数。 该问题可转化为求解线性方程组 G n C F n ​ G_{n}CF_{n}​ Gn​CFn​​ 其中&#xff0c;系数 C ( c 0 , c 1 , ⋯ , c n ) T , F n ( ( f , φ 0 ) , ( f , φ 1 ) , ⋯ , ( f , φ n ) ) T C(c…

Java网络编程之UDP通信与TCP通信交互代码实现

​import java.net.InetAddress; import java.io.IOException; class Main {public static void main(String[] args) {try { InetAddress localAddress InetAddress.getLocalHost(); //获得本地主机 InetAddress remoteAddress InetAddress.getByName("www.itcast.cn&qu…

非强化学习的对齐方法

在文章《LLM对齐“3H原则”》和《深入理解RLHF技术》中&#xff0c;我们介绍了大语言模型与人类对齐的“3H原则”&#xff0c;以及基于人类反馈的强化学习方法&#xff08;RLHF&#xff09;&#xff0c;本文将继续介绍另外一种非强化学习的对齐方法&#xff1a;直接偏好优化&am…

【Java】解决Java报错:IllegalMonitorStateException in Synchronization

文章目录 引言一、IllegalMonitorStateException的定义与概述1. 什么是IllegalMonitorStateException&#xff1f;2. IllegalMonitorStateException的常见触发场景3. 示例代码 二、解决方案1. 确保在同步代码块或方法中调用wait()、notify()和notifyAll()2. 使用同步方法3. 使用…

鸿蒙开发系统基础能力:【@ohos.inputMethod (输入法框架)】

输入法框架 说明&#xff1a; 本模块首批接口从API version 6开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 import inputMethod from ohos.inputMethod;inputMethod8 常量值。 系统能力&#xff1a;以下各项对应的系统能力均为Sy…

NetSuite Account Merge 科目合并功能分析

最近项目中&#xff0c;客户有提到过能否将不用的Account与新建的Account进行合并&#xff0c;即我们所说的Merge功能&#xff5e;可以&#xff0c;但是该功能有使用的限制&#xff0c;比如最直接的一点需要注意&#xff0c;不同类型的Account是不可以使用Merge功能的&#xff…

Linux系统学习——指令三

Linux系统学习——指令三 Linux系统学习——指令三chmod — 文件执行权限添加文件执行权限去除文件执行权限 查找文件中特定关键字使用vi编辑文件并查找特定关键字文本文件查找特定关键字1: 使用 grep 命令2: 使用 find 命令3: 使用 awk 命令4: 使用 sed 命令5: 使用 ag 命令&a…