让我看看几天能完成
瑞吉外卖项目
项目介绍
- 本项目(瑞吉外卖)是专门为餐饮企业(餐厅、饭店)定制的一款软件产品,包括系统管理后台和移动端应用两部分。
- 其中系统管理后台主要提供给餐饮企业内部员工使用,可以对餐厅的菜品、套餐、订单等进行管理维护。移动端应用主要提供给消费者使用,可以在线浏览菜品、添加购物车、下单等。
本项目共分为3期进行开发:
- 第一期主要实现基本需求,其中移动端应用通过H5实现,用户可以通过手机浏览器访问。
- 第二期主要针对移动端应用进行改进,使用微信小程序实现,用户使用起来更加方便。
- 第三期主要针对系统进行优化升级,提高系统的访问性能。
技术选型
功能架构
角色
- 后台系统管理员:登录后台管理系统,拥有后台系统中的所有操作权限。
- 后台系统普通员工:登录后台管理系统,对菜品、套餐、订单等进行管理。
- C端用户:登录移动端应用,可以浏览菜品、添加购物车、设置地址、在线下单等。
MySQL
/*
Navicat MySQL Data Transfer
Source Server : localhost
Source Server Version : 50728
Source Host : localhost:3306
Source Database : reggie
Target Server Type : MYSQL
Target Server Version : 50728
File Encoding : 65001
Date: 2021-07-23 10:41:41
*/
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='用户信息';
数据表
pom.xml(SpringBoot、MP、MySQL)
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.li</groupId>
<artifactId>ruiji</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ruiji</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.3.7.RELEASE</spring-boot.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<!--<version>1.18.12</version>-->
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.18</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.7.RELEASE</version>
<configuration>
<mainClass>com.li.RuijiApplication</mainClass>
</configuration>
<executions>
<execution>
<id>repackage</id>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
application.yml
- 黑马的有些错误。
server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3307/ruiji?serverTimezone=UTC
username: root
password: 1234
type: com.alibaba.druid.pool.DruidDataSource
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
启动类(RuijiApplication)
//@Slf4j注解需要在maven里配置lombok
@Slf4j
@SpringBootApplication
public class RuijiApplication {
public static void main(String[] args) {
SpringApplication.run(RuijiApplication.class, args);
log.info("项目启动成功");
}
}
静态资源
- 静态资源需要放在
resources 下的 static
下。 - 不通过static也可以,但要配置springmvc拦截器。
- 复制静态资源如果不能访问,请重启idea。
- 测试:
http://localhost:8080/backend/index.html
后台登录功能开发
登录页面(login.html)
http://localhost:8080/backend/page/login/login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>辛巴外卖管理端</title>
<link rel="shortcut icon" href="../../favicon.ico">
<!-- 引入样式 -->
<link rel="stylesheet" href="../../plugins/element-ui/index.css" />
<link rel="stylesheet" href="../../styles/common.css">
<link rel="stylesheet" href="../../styles/login.css">
<link rel="stylesheet" href="../../styles/icon/iconfont.css" />
<style>
.body{
min-width: 1366px;
}
</style>
</head>
<body>
<div class="login" id="login-app">
<div class="login-box">
<img src="../../images/login/login-l.png" alt="">
<div class="login-form">
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" >
<div class="login-form-title">
<img src="../../images/login/logo.png" style="width:139px;height:42px;" alt="" />
</div>
<el-form-item prop="username">
<el-input v-model="loginForm.username" type="text" auto-complete="off" placeholder="账号" maxlength="20"
prefix-icon="iconfont icon-user" />
</el-form-item>
<el-form-item prop="password">
<el-input v-model="loginForm.password" type="password" placeholder="密码" prefix-icon="iconfont icon-lock" maxlength="20"
@keyup.enter.native="handleLogin" />
</el-form-item>
<el-form-item style="width:100%;">
<el-button :loading="loading" class="login-btn" size="medium" type="primary" style="width:100%;"
@click.native.prevent="handleLogin">
<span v-if="!loading">登录</span>
<span v-else>登录中...</span>
</el-button>
</el-form-item>
</el-form>
</div>
</div>
</div>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="../../plugins/vue/vue.js"></script>
<!-- 引入组件库 -->
<script src="../../plugins/element-ui/index.js"></script>
<!-- 引入axios -->
<script src="../../plugins/axios/axios.min.js"></script>
<script src="../../js/request.js"></script>
<script src="../../js/validate.js"></script>
<script src="../../api/login.js"></script>
<script>
new Vue({
el: '#login-app',
data() {
return {
loginForm:{
username: 'admin',
password: '123456'
},
loading: false
}
},
computed: {
loginRules() {
const validateUsername = (rule, value, callback) => {
if (value.length < 1 ) {
callback(new Error('请输入用户名'))
} else {
callback()
}
}
const validatePassword = (rule, value, callback) => {
if (value.length < 6) {
callback(new Error('密码必须在6位以上'))
} else {
callback()
}
}
return {
'username': [{ 'validator': validateUsername, 'trigger': 'blur' }],
'password': [{ 'validator': validatePassword, 'trigger': 'blur' }]
}
}
},
created() {
},
methods: {
async handleLogin() {
this.$refs.loginForm.validate(async (valid) => {
if (valid) {
this.loading = true
let res = await loginApi(this.loginForm)
if (String(res.code) === '1') {
//“1”表示登录成功
localStorage.setItem('userInfo',JSON.stringify(res.data))
window.location.href= '/backend/index.html'
} else {
//表示登录失败
this.$message.error(res.msg)
this.loading = false
}
}
})
}
}
})
</script>
</body>
</html>
新建包(controller、entity、mapper、service、serviceimpl)
controller —— EmployeeController
package com.li.ruiji.controller;
import com.li.ruiji.service.EmployeeService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Slf4j
@RestController
@RequestMapping("/employee")
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
}
entity —— Employee
package com.li.ruiji.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;
}
mapper —— EmployeeMapper
package com.li.ruiji.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.li.ruiji.entity.Employee;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface EmployeeMapper extends BaseMapper<Employee> {
}
service —— EmployeeService
package com.li.ruiji.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.li.ruiji.entity.Employee;
public interface EmployeeService extends IService<Employee> {
}
serviceimpl —— EmployeeServiceImpl
package com.li.ruiji.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.li.ruiji.entity.Employee;
import com.li.ruiji.mapper.EmployeeMapper;
import com.li.ruiji.service.EmployeeService;
import org.springframework.stereotype.Service;
@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements EmployeeService {
}
导入返回结果类R
- 服务端返回结果类
- 此类是一个通用结果类,服务端响应的所有结果最终都会包装成此种类型返回给前端页面。
- 新建common包,新建R.java类
package com.li.ruiji.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;
}
}
在Controller中创建登录方法
处理逻辑如下:
EmployeeController
@Slf4j
@RestController
@RequestMapping("/employee")
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
//编写登录方法
//员工登录
@PostMapping("/login")
public R<Employee> login(HttpServletRequest request, @RequestBody Employee employee){
// 处理逻辑如下:
// * 将页面提交的密码password进行md5加密处理
String password = employee.getPassword();
password = DigestUtils.md5DigestAsHex(password.getBytes());
// * 根据页面提交的用户名username查询数据库
LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(Employee::getUsername,employee.getUsername());
Employee emp = employeeService.getOne(queryWrapper);
// * 如果没有查询到则返回登录失败结果
if (emp == null){
return R.error("登录失败");
}
// * 密码比对,如果不一致则返回登录失败结果
if (!emp.getPassword().equals(password)){
return R.error("登录失败");
}
// * 查看员工状态,如果为已禁用状态,则返回员工已禁用结果
if (emp.getStatus() == 0){
return R.error("账号已禁用");
}
// * 登录成功,将员工id存入Session并返回登录成功结果
request.getSession().setAttribute("employee",emp.getId());
return R.success(emp);
}
}
注意项
- 如果报错
java.sql.SQLFeatureNotSupportedException: null
- 升级Druid版本,可以是1.1.18
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.18</version>
</dependency>
- 如果登录页面一直在转圈,则将
datasource
的password
加上双引号
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3307/ruiji?serverTimezone=UTC
username: root
#如果登录页面一直在转圈,则将`datasource`的`password`加上双引号
password: "1234"
type: com.alibaba.druid.pool.DruidDataSource
后台退出功能开发
- 用户点击页面中退出按钮,发送请求,请求地址为/employee/logout,请求方式为POST。
- 我们只需要在Controller中创建对应的处理方法即可。
- 具体的处理逻辑:
1、清理Session中的用户id
2、返回结果
EmployeeController
//员工退出
@PostMapping("/logout")
public R<String> logout(HttpServletRequest request){
//清理session员工id
request.getSession().removeAttribute("employee");
return R.success("退出成功");
}
完善登录功能
实现步骤:
- 创建自定义过滤器
LoginCheckFilter
。 - 在启动类上加入注解
@ServletComponentScan
。 - 完善过滤器的处理逻辑。
- 创建自定义过滤器
过滤器具体的处理逻辑如下:
新建filter包和LoginCheckFilter类
- 同时要在启动类RuijiApplication下新加注解
@ServletComponentScan
。 - 需要引入
fastjson
包。
import com.alibaba.fastjson.JSON;
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
package com.li.ruiji.filter;
import com.alibaba.fastjson.JSON;
import com.li.ruiji.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 {
//路径比较(匹配器),支持通配符
//用于步骤一(获取本次请求的URI)
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;
// * 获取本次请求的URI。
String requestURI = request.getRequestURI();
//定义不需要的处理的请求路径
String[] urls =new String[]{
"/employee/login",
"/employee/logout",
"/backend/**",
"/front/**"
};
//* 判断本次请求是否需要处理。
boolean check = check(urls,requestURI);
//* 如果不需要处理,则直接放行。
if (check){
filterChain.doFilter(request,response);
return;
}
//* 判断登录状态,如果已登录,则直接放行。
if (request.getSession().getAttribute("employee") !=null){
filterChain.doFilter(request,response);
return;
}
//* 如果未登录则返回未登录结果。
//输出流向客户端页面响应数据
response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
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、页面发送ajax请求,将新增员工页面中输入的数据以json的形式提交到服务端。
- 2、服务端Controller接收页面提交的数据并调用Service将数据进行保存。
- 3、Service调用Mapper操作数据库,保存数据。
//新增员工
@PostMapping
public R<String> save(HttpServletRequest request,@RequestBody Employee employee){
//设置初始密码123456,进行MD5加密
employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
employee.setCreateTime(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("新增员工成功");
}
- 账号重复报异常。
程序进行异常捕获,通常有两种处理方式:
- 1、在Controller方法中加入try、catch进行异常捕获。
- 2、使用异常处理器进行全局异常捕获。
- 在common包下新建
GlobalExceptionHandler
类
//全局异常处理
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {
//异常处理方法
@ExceptionHandler(SQLIntegrityConstraintViolationException.class)
public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
log.error(ex.getMessage());
if (ex.getMessage().contains("Duplicate entry")){
String[] split = ex.getMessage().split(" ");
String msg = split[2]+"已存在";
return R.error(msg);
}
//账号重复报异常
return R.error("未知错误");
}
}
员工信息分页查询
执行过程
- 页面发送ajax请求,将分页查询参数(page、pageSize、name)提交到服务端。
- 服务端Controller接收页面提交的数据并调用Service查询数据。
- Service调用Mapper操作数据库,查询分页数据。
- Controller将查询到的分页数据响应给页面。
- 页面接收到分页数据并通过ElementUI的Table组件展示到页面上。
配置MP的分页插件
//配置MP的分页插件
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return mybatisPlusInterceptor;
}
}
员工信息分页查询
//员工信息分页查询
@GetMapping("/page")
public R<Page> page(int page, int pageSize, String name){
//构造分页构造器
Page pageInfo = new Page(page,pageSize);
//构造条件构造器
LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
// 添加过滤条件
queryWrapper.like(StringUtils.isNotBlank(name),Employee::getName,name);
// 添加排序条件
queryWrapper.orderByDesc(Employee::getUpdateTime);
//执行查询
employeeService.page(pageInfo,queryWrapper);
return R.success(pageInfo);
}
启用/禁用员工账号
执行过程
- 页面发送ajax请求,将参数(id、status)提交到服务端。
- 服务端Controller接收页面提交的数据并调用Service更新数据。
- Service调用Mapper操作数据库。
代码开发(根据id修改员工信息)
- 启用、禁用员工账号,本质上就是一个更新操作,也就是对status状态字段进行操作。
- 在Controller中创建update方法,此方法是一个通用的修改员工信息的方法。
//根据id修改员工信息
@PutMapping
public R<String> update(HttpServletRequest request,@RequestBody Employee employee){
Long empId = (Long)request.getSession().getAttribute("employee");
employee.setUpdateTime(LocalDateTime.now());
employee.setUpdateUser(empId);
employeeService.updateById(employee);
return R.success("员工信息修改成功!");
}
代码修复
- 提供对象转换器JacksonObjectMapper,基于Jackson进行Java对象到json数据的转换。
- 在WebMvcConfig配置类中扩展Spring mvc的消息转换器,在此消息转换器中使用提供的对象转换器进行]ava对象到json数据的转换。
对象映射器
package com.li.ruiji.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框架的消息转换器(WebMvcConfig)
@Slf4j
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
/**
* 设置静态资源映射
* @param registry
*/
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
log.info("开始进行静态资源映射...");
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
}
/**
* 扩展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);
}
}
编辑员工信息
代码开发
- 点击编辑按钮时,页面跳转到add.html,并在url中携带参数[员工id]。
- 在add.html页面获取url中的参数[员工id]。
- 发送ajax请求,请求服务端,同时提交员工id参数。
- 服务端接收请求,根据员工id查询员工信息,将员工信息以json形式响应给页面。
- 页面接收服务端响应的json数据,通过VUE的数据绑定进行员工信息回显。
- 点击保存按钮,发送ajax请求,将页面中的员工信息以json方式提交给服务端。
- 服务端接收员工信息,并进行处理,完成后给页面响应。
- 页面接收到服务端响应信息后进行相应处理。
//根据id查询员工信息
@GetMapping("/{id}")
public R<Employee> getById(@PathVariable Long id){
Employee employee = employeeService.getById(id);
if (employee !=null){
return R.success(employee);
}
return R.error("没有查询到对应的员工信息");
}
分类管理业务开发
公共字段自动填充
- Mybatis Plus公共字段自动填充,也就是在插入或者更新的时候为指定字段赋予指定的值。
- 使用它的好处就是可以统一对这些字段进行处理,避免了重复代码。
实现步骤
- 在实体类的属性上加入
@TableField
注解,指定自动填充的策略。 - 按照框架要求编写元数据对象处理器,在此类中统一为公共字段赋值,此类需要实现
MetaObjectHandler
接口。
Employee实体类
@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;
自定义元数据对象处理器(MyMetaObjectHandler)
//自定义元数据对象处理器
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
//插入操作,自动填充
//公共字段自动填充
public void insertFill(MetaObject metaObject) {
metaObject.setValue("createTime", LocalDateTime.now());
metaObject.setValue("updateTime", LocalDateTime.now());
metaObject.setValue("createUser", new Long(1));
metaObject.setValue("updateUser", new Long(1));
}
@Override
//更新操作,自动填充
//公共字段自动填充
public void updateFill(MetaObject metaObject) {
metaObject.setValue("updateTime", LocalDateTime.now());
metaObject.setValue("updateUser", new Long(1));
}
}
- 原先的
EmployeeController
类下的新增员工模块
的一段代码可以删除(注释掉)了。
//新增员工
@PostMapping
public R<String> save(HttpServletRequest request,@RequestBody Employee employee){
//设置初始密码123456,进行MD5加密
employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes()));
// employee.setCreateTime(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("新增员工成功");
}
- 原先的
EmployeeController
类下的根据id修改员工信息
的一段代码可以删除(注释掉)了。
//根据id修改员工信息
@PutMapping
public R<String> update(HttpServletRequest request,@RequestBody Employee employee){
// Long empId = (Long)request.getSession().getAttribute("employee");
// employee.setUpdateTime(LocalDateTime.now());
// employee.setUpdateUser(empId);
employeeService.updateById(employee);
return R.success("员工信息修改成功!");
}
公共字段自动填充 - 功能完善
什么是ThreadLocal?
- ThreadLocal并不是一个Thread,而是Thread的局部变量。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
- ThreadLocal为每个线程提供单独一份存储空间,具有线程隔离的效果,只有在线程内才能获取到对应的值,线程外则不能访问。
ThreadLocal常用方法:
public void set(T value)
- 设置当前线程的线程局部变量的值
public T get()
- 返回当前线程所对应的线程局部变量的值
- 我们可以在
LoginCheckFilter
的doFilter
方法中获取当前登录用户id,并调用ThreadLocal
的set方法
来设置当前线程的线程局部变量的值(用户id),然后在MyMetaObjectHandler
的updateFill
方法中调用ThreadLocal
的get方法来获得当前线程所对应的线程局部变量的值(用户id)。
实现步骤:
- 编写BaseContext工具类,基于ThreadLocal封装的工具类。
- 在LogincheckFilter的doFilter方法中调用BaseContext来设置当前登录用户的id。
- 在MyMetaObjectHandler的方法中调用BaseContext获取登录用户的id。
基于threadlocal封装工具类(BaseContext)
package com.li.ruiji.common;
//基于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();
}
}
LoginCheckFilter调用BaseContext
//* 判断登录状态,如果已登录,则直接放行。
if (request.getSession().getAttribute("employee") !=null){
//LoginCheckFilter调用BaseContext
Long empId = (Long) request.getSession().getAttribute("employee");
BaseContext.setCurrentId(empId);
//-----------------------------------------------------
filterChain.doFilter(request,response);
return;
}
自定义元数据对象处理器 - 修改(MyMetaObjectHandler)
//自定义元数据对象处理器
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
//插入操作,自动填充
//公共字段自动填充
public void insertFill(MetaObject metaObject) {
metaObject.setValue("createTime", LocalDateTime.now());
metaObject.setValue("updateTime", LocalDateTime.now());
//自定义元数据对象处理器 - 修改
metaObject.setValue("createUser", BaseContext.getCurrentId());
metaObject.setValue("updateUser", BaseContext.getCurrentId());
//----------------------------------------------------------------
}
@Override
//更新操作,自动填充
//公共字段自动填充
public void updateFill(MetaObject metaObject) {
metaObject.setValue("updateTime", LocalDateTime.now());
//自定义元数据对象处理器 - 修改
metaObject.setValue("updateUser", BaseContext.getCurrentId());
//----------------------------------------------------------------
}
}
新增分类
类和接口基本结构
- 实体类Category
- Mapper接口CategoryMapper
- 业务层接口CategoryService
- 业务层实现类CategoryServicelmpl
- 控制层CategoryController
实体类Category
package com.li.ruiji.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 分类
*/
@Data
public class Category implements Serializable {
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;
//是否删除
private Integer isDeleted;
}
Mapper接口CategoryMapper
package com.li.ruiji.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.li.ruiji.entity.Category;
import com.li.ruiji.entity.Employee;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface CategoryMapper extends BaseMapper<Category> {
}
业务层接口CategoryService
package com.li.ruiji.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.li.ruiji.entity.Category;
import com.li.ruiji.entity.Employee;
public interface CategoryService extends IService<Category> {
}
业务层实现类CategoryServicelmpl
package com.li.ruiji.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.li.ruiji.entity.Category;
import com.li.ruiji.entity.Employee;
import com.li.ruiji.mapper.CategoryMapper;
import com.li.ruiji.mapper.EmployeeMapper;
import com.li.ruiji.service.CategoryService;
import com.li.ruiji.service.EmployeeService;
import org.springframework.stereotype.Service;
@Service
public class CategoryServicelmpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {
}
控制层CategoryController
//分类管理
@RestController
@RequestMapping("/category")
public class CategoryController {
@Autowired
private CategoryService categoryService;
}
“新增分类”功能(控制层CategoryController)
//分类管理
@RestController
@RequestMapping("/category")
public class CategoryController {
@Autowired
private CategoryService categoryService;
// 新增分类
@PostMapping
public R<String> save(@RequestBody Category category){
categoryService.save(category);
return R.success("新增分类成功");
}
}
分类信息分页查询
程序执行过程
- 页面发送ajax请求,将分页查询参数(page、pageSize)提交到服务端
- 服务端Controller接收页面提交的数据并调用Service查询数据
- Service调用Mapper操作数据库,查询分页数据
- Controller将查询到的分页数据响应给页面
- 页面接收到分页数据并通过ElementUl的Table组件展示到页面上
分页查询(CategoryController)
// 分页查询
@GetMapping("/page")
public R<Page> page(int page, int pageSize){
//分页构造器
Page<Category> pageInfo = new Page<>(page,pageSize);
//条件构造器
LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
//添加排序条件,根据sort进行排序
queryWrapper.orderByAsc(Category::getSort);
//进行分页查询
categoryService.page(pageInfo,queryWrapper);
return R.success(pageInfo);
}
- 注:如果报
Unknown column 'is_deleted' in 'field list'
或者500
的需要把Category实体类
的逻辑删除那个字段注释。
/**
* 分类
*/
@Data
public class Category implements Serializable {
.....................................
// //是否删除
// private Integer isDeleted;
}
删除分类
程序执行过程
- 页面发送ajax请求,将参数(id)提交到服务端。
- 服务端Controller接收页面提交的数据并调用Service删除数据。
- Service调用Mapper操作数据库。
根据id删除分类(CategoryController)
// 根据id删除分类
@DeleteMapping
public R<String> delete(Long ids){
categoryService.removeById(ids);
return R.success("分类信息删除成功");
}
修改或原封不动category.js
- 注意:这里的是id要写为
ids
。因为list.html
中调用的category.js
的api里的删除当前列的接口代码是写的ids
。 - 也可以修改category.js的ids为id,那么在
CategoryController
中需要传的值就是(Long id)
了
// 删除当前列的接口
const deleCategory = (ids) => {
return $axios({
url: '/category',
method: 'delete',
params: { ids }
})
}
创建-类和接口基本结构
- 实体类Dish和Setmeal
- Mapper接口DishMapper和SetmealMapper
- Service接口DishService和SetmealService
- Service实现类DishServicelmpl和SetmealServicelmpl
实体类Dish
package com.li.ruiji.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
菜品
*/
@Data
public class Dish implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
//菜品名称
private String name;
//菜品分类id
private Long categoryId;
//菜品价格
private BigDecimal price;
//商品码
private String code;
//图片
private String image;
//描述信息
private String description;
//0 停售 1 起售
private Integer status;
//顺序
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;
//是否删除
private Integer isDeleted;
}
实体类Setmeal
package com.li.ruiji.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 套餐
*/
@Data
public class Setmeal implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
//分类id
private Long categoryId;
//套餐名称
private String name;
//套餐价格
private BigDecimal price;
//状态 0:停用 1:启用
private Integer status;
//编码
private String code;
//描述信息
private String description;
//图片
private String image;
@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;
//是否删除
private Integer isDeleted;
}
Mapper接口DishMapper
@Mapper
public interface DishMapper extends BaseMapper<Dish> {
}
Mapper接口SetmealMapper
@Mapper
public interface SetmealMapper extends BaseMapper<Setmeal>{
}
Service接口DishService
public interface DishService extends IService<Dish> {
}
Service接口SetmealService
public interface SetmealService extends IService<Setmeal> {
}
Service实现类DishServicelmpl
@Service
public class DishServicelmpl extends ServiceImpl<DishMapper, Dish> implements DishService {
}
Service实现类SetmealServicelmpl
@Service
public class SetmealServicelmpl extends ServiceImpl<SetmealMapper, Setmeal> implements SetmealService {
}
修改分类(CategoryService)
public interface CategoryService extends IService<Category> {
public void remove(Long id);
}
CategoryServicelmpl
@Service
public class CategoryServicelmpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {
@Autowired
private DishService dishService;
@Autowired
private SetmealService setmealService;
//根据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);
}
}
在common包下新建自定义业务异常类(CustomException)
/**
* 自定义业务异常类
*/
public class CustomException extends RuntimeException {
public CustomException(String message){
super(message);
}
}
新增异常处理方法(GlobalExceptionHandler)
//全局异常处理
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {
------------------------------------------
//异常处理方法
@ExceptionHandler(CustomException.class)
public R<String> exceptionHandler(CustomException ex){
log.error(ex.getMessage());
return R.error(ex.getMessage());
}
}
修改CategoryController根据id删除分类的removeById方法
categoryService.removeById(id);
→→→→→categoryService.remove(id);
// 根据id删除分类
@DeleteMapping
public R<String> delete(Long id){
// categoryService.removeById(id);
categoryService.remove(id);
return R.success("分类信息删除成功");
}
修改分类(CategoryController)
// 根据id删除分类信息
@PutMapping
public R<String> update(@RequestBody Category category){
categoryService.updateById(category);
return R.success("修改分类信息成功!");
}
菜品管理业务开发
文件上传下载
文件上传
服务端要接收客户端页面上传的文件,通常都会使用Apache的两个组件
- commons-fileupload
- commons-io
- Spring框架在spring-web包中对文件上传进行了封装,大大简化了服务端代码,我们只需要在Controller的方法中声明一个MultipartFile类型的参数即可接收上传的文件。
文件下载
- 通过浏览器进行文件下载,本质上就是服务端将文件以流的形式写回浏览器的过程。
文件上传代码实现
- 在
backend\page
下新建demo文件夹,并新建upload.html
。
upload.html
- 默认给的会被拦截器拦截,访问不到页面。
- 加上这个:
<link rel="shortcut icon" href="../../favicon.ico">
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文件上传</title>
<!-- 引入样式 -->
<link rel="stylesheet" href="../../plugins/element-ui/index.css" />
<link rel="stylesheet" href="../../styles/common.css" />
<link rel="stylesheet" href="../../styles/page.css" />
<!-- 默认给的会被拦截器拦截,访问不到页面。加上这个-->
<link rel="shortcut icon" href="../../favicon.ico">
<!-- ----------------------------------------------------->
</head>
<body>
<div class="addBrand-container" id="food-add-app">
<div class="container">
<el-upload class="avatar-uploader"
action="/common/upload"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeUpload"
ref="upload">
<img v-if="imageUrl" :src="imageUrl" class="avatar"></img>
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</div>
</div>
<!-- 开发环境版本,包含了有帮助的命令行警告 -->
<script src="../../plugins/vue/vue.js"></script>
<!-- 引入组件库 -->
<script src="../../plugins/element-ui/index.js"></script>
<!-- 引入axios -->
<script src="../../plugins/axios/axios.min.js"></script>
<script src="../../js/index.js"></script>
<script>
new Vue({
el: '#food-add-app',
data() {
return {
imageUrl: ''
}
},
methods: {
handleAvatarSuccess (response, file, fileList) {
this.imageUrl = `/common/download?name=${response.data}`
},
beforeUpload (file) {
if(file){
const suffix = file.name.split('.')[1]
const size = file.size / 1024 / 1024 < 2
if(['png','jpeg','jpg'].indexOf(suffix) < 0){
this.$message.error('上传图片只支持 png、jpeg、jpg 格式!')
this.$refs.upload.clearFiles()
return false
}
if(!size){
this.$message.error('上传文件大小不能超过 2MB!')
return false
}
return file
}
}
}
})
</script>
</body>
</html>
CommonController
- 在Controller包中新建
CommonController
类。
修改LoginCheckFilter部分内容
- 这样测试的话就不用去登录了。
//定义不需要的处理的请求路径
String[] urls =new String[]{
"/employee/login",
"/employee/logout",
"/backend/**",
"/front/**",
//修改LoginCheckFilter部分内容
"/common/**"
//-----------------------------
};
application.yml
- 在application.yml中添加图片临时保存路径(D:testImage)
ruiji:
path: D:\testImage\
文件上传代码实现(CommonController)(完整)
//文件上传和下载
@RestController
@RequestMapping("/common")
public class CommonController {
@Value("${ruiji.path}")
private String basePath;
//文件上传
@PostMapping("/upload")
public R<String> upload(MultipartFile file){
//file是一个临时文件,需要转存到指定位置,否则本次请求完成后临时文件会删除
//原始文件名
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){
e.printStackTrace() ;
}
return R.success(fileName);
}
}
文件下载代码实现(CommonController)(完整)
//文件下载
@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();
}
}
新增菜品
- 新增菜品,其实就是将新增页面录入的菜品信息插入到dish表。
- 如果添加了口味做法,还需要向dish_flavor表插入数据。
所以在新增菜品时,涉及到两个表:
dish
—— 菜品表dish_flavor
—— 菜品口味表
类和接口基本结构
- 实体类
DishFlavor
- Mapper接口
DishFlavorMapper
- 业务层接口
DishFlavorService
- 业务层实现类
DishFlavorServicelmpl
- 控制层
DishController
实体类DishFlavor
package com.li.ruiji.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
菜品口味
*/
@Data
public class DishFlavor implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
//菜品id
private Long dishId;
//口味名称
private String name;
//口味数据list
private String value;
@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;
//是否删除
private Integer isDeleted;
}
Mapper接口DishFlavorMapper
@Mapper
public interface DishFlavorMapper extends BaseMapper<DishFlavor>{
}
业务层接口DishFlavorService
public interface DishFlavorService extends IService<DishFlavor>{
}
业务层实现类DishFlavorServicelmpl
@Service
public class DishFlavorServicelmpl extends ServiceImpl<DishFlavorMapper, DishFlavor> implements DishFlavorService {
}
控制层DishController
//菜品管理
@RestController
@RequestMapping("/dish")
public class DishController {
@Autowired
private DishService dishService;
@Autowired
private DishFlavorService dishFlavorService;
}
新增菜品时前端页面和服务端的交互过程
(开发新增菜品功能,其实就是在服务端编写代码去处理前端页面发送的这4次请求即可)
- 页面
(backend/page/food/add.html)
发送ajax请求,请求服务端获取菜品分类数据并展示到下拉框中。 - 页面发送请求进行图片上传,请求服务端将图片保存到服务器。
- 页面发送请求进行图片下载,将上传的图片进行回显。
- 点击保存按钮,发送ajax请求,将菜品相关数据以json形式提交到服务端。
菜品分类 —— 根据条件查询分类数据(CategoryController)
//菜品分类 —— 根据条件查询分类数据
@GetMapping("/list")
public R<List<Category>> list(Category category){
//条件构造器
LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
//添加条件
queryWrapper.eq(category.getType() !=null,Category::getType,category.getType());
//添加排序条件
queryWrapper.orderByAsc(Category::getSort).orderByAsc(Category::getUpdateTime);
List<Category> list = categoryService.list(queryWrapper);
return R.success(list);
}
导入DTO
@Data
public class DishDto extends Dish {
private List<DishFlavor> flavors = new ArrayList<>();
private String categoryName;
private Integer copies;
}
新增菜品(DishController)
//新增菜品
@PostMapping
public R<String> save(@RequestBody DishDto dishDto){
dishService.saveWithFlavor(dishDto);
return R.success("新增菜品成功!");
}
DishService(接口)
public interface DishService extends IService<Dish> {
//新增菜品,同时插入菜品对应的口味数据,需要操作两张表: dish、dish_flavor
public void saveWithFlavor(DishDto dishDto);
}
实现DishService接口方法(DishServicelmpl)
@Service
public class DishServicelmpl extends ServiceImpl<DishMapper, Dish> implements DishService {
@Autowired
private DishFlavorService dishFlavorService;
//新增菜品,同时保存对应的口味数据
@Transactional
public void saveWithFlavor(DishDto dishDto){
//保存菜品的基本信息到菜品表dish
this.save(dishDto);
Long dishId = dishDto.getId();//菜品ID
//菜品口味
List<DishFlavor> flavors =dishDto.getFlavors();
flavors = flavors.stream().map((item) ->{
item.setDishId(dishId);
return item;
}).collect(Collectors.toList());
//保存菜品口味数据到菜品口味表dish_flavor
dishFlavorService.saveBatch(flavors);
}
}
为支持@Transactional需在启动类中添加@EnableTransactionManagement
注解
- 开启事物注解
@Slf4j
@SpringBootApplication
@ServletComponentScan
//新增//新增//新增//新增//新增//新增
@EnableTransactionManagement
//-------------------------------------
public class RuijiApplication {
public static void main(String[] args) {
SpringApplication.run(RuijiApplication.class, args);
log.info("项目启动成功");
}
}
菜品信息分页查询
代码开发 —— 交互过程
开发菜品信息分页查询功能,其实就是在服务端编写代码去处理前端页面发送的这2次请求即可:
- 页面(backend/page/food/list.html)发送ajax请求,将分页查询参数(page、pageSize、name)提交到服务端,获取分页数据。
- 页面发送请求,请求服务端进行图片下载,用于页面图片展示。
菜品信息分页查询(DishController)
- 能查询到数据,但页面效果不佳,需改进。
//菜品信息分页查询
@GetMapping("/page")
public R<Page> page(int page,int pageSize,String name){
//分页构造器
Page<Dish> pageInfo = new Page<>(page,pageSize);
//条件构造器
LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
//添加过滤条件
queryWrapper.like(name !=null,Dish::getName,name);
//添加排序条件
queryWrapper.orderByAsc(Dish::getUpdateTime);
//执行分页查询
dishService.page(pageInfo,queryWrapper);
return R.success(pageInfo);
}
代码改造
//菜品管理
@RestController
@RequestMapping("/dish")
public class DishController {
@Autowired
private DishService dishService;
@Autowired
private DishFlavorService dishFlavorService;
//-----------------新增----------------------
@Autowired
private CategoryService categoryService;
//---------------------------------------
//新增菜品
@PostMapping
public R<String> save(@RequestBody DishDto dishDto){
dishService.saveWithFlavor(dishDto);
return R.success("新增菜品成功!");
}
//菜品信息分页查询
@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> queryWrapper = new LambdaQueryWrapper<>();
//添加过滤条件
queryWrapper.like(name !=null,Dish::getName,name);
//添加排序条件
queryWrapper.orderByAsc(Dish::getUpdateTime);
//执行分页查询
dishService.page(pageInfo,queryWrapper);
//-----------------新增----------------------
// 对象拷贝
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
//根据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);
//---------------------------------------
}
}
修改菜品
代码交互过程
开发修改菜品功能,其实就是在服务端编写代码去处理前端页面发送的这4次请求即可:
- 页面发送ajax请求,请求服务端获取分类数据,用于菜品分类下拉框中数据展示。
- 页面发送ajax请求,请求服务端,根据id查询当前菜品信息,用于菜品信息回显。
- 页面发送请求,请求服务端进行图片下载,用于页图片回显。
- 点击保存按钮,页面发送ajax请求,将修改后的菜品相关数据以json形式提交到服务端。
根据id查询菜品信息和对应的口味信息(DishController)
//根据id查询菜品信息和对应的口味信息
@GetMapping("/{id}")
public R<DishDto> get(@PathVariable Long id){
DishDto dishDto = dishService.getByIdWithFlavor(id);
return R.success(dishDto);
}
根据id查询菜品信息和对应的口味信息(DishService)
//根据id查询菜品信息和对应的口味信息
public DishDto getByIdWithFlavor(Long id);
根据id查询菜品信息和对应的口味信息(DishServicelmpl)
//根据id查询菜品信息和对应的口味信息
public DishDto getByIdWithFlavor(Long id){
//查询菜品基本信息,从dish表查讪
Dish dish = this.getById(id);
DishDto dishDto = new DishDto();
BeanUtils.copyProperties(dish,dishDto);
//查询当前菜品对应的口味信息,从dish_flavor表查询
LambdaQueryWrapper<DishFlavor> queryWrapper =new LambdaQueryWrapper<>();
queryWrapper.eq(DishFlavor::getDishId,dish.getId());
List<DishFlavor> flavors = dishFlavorService.list(queryWrapper);
dishDto.setFlavors(flavors);
return dishDto;
}
修改菜品(DishController)
//修改菜品
@PutMapping
public R<String> update(@RequestBody DishDto dishDto){
dishService.updateWithFlavor(dishDto);
return R.success("修改菜品成功!");
}
修改菜品(DishService)
//更新菜品信息,同时更新对应的口味信息
public void updateWithFlavor(DishDto dishDto);
修改菜品(DishServicelmpl)
//更新菜品信息,同时更新对应的口味信息
@Override
public void updateWithFlavor(DishDto dishDto){
//更新dish表基本信息
this.updateById(dishDto);
//清理当前菜品对应口味数据 -- dish_flavor表的delete操作
LambdaQueryWrapper<DishFlavor> queryWrapper =new LambdaQueryWrapper<>();
queryWrapper.eq(DishFlavor::getDishId,dishDto.getId());
dishFlavorService.remove(queryWrapper);
//添加当前提交过来的口味数据 -- 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);
}
套餐管理
新增套餐
数据模型
- 新增套餐,其实就是将新增页面录入的套餐信息插入到
setmeal表
; 还需要向
setmeal_dish表
插入套餐和菜品关联数据所以在新增套餐时,涉及到两个表:setmeal
—— 套餐表setmeal_dish
—— 套餐菜品关系表
类和接口基本结构
- 实体类
SetmealDish
DTO SetmealDto
- Mapper接口
SetmealDishMapper
- 业务层接口
SetmealDishService
- 业务层实现类
SetmealDishServicelmpl
- 控制层
SetmealController
实体类SetmealDish
package com.li.ruiji.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 套餐菜品关系
*/
@Data
public class SetmealDish implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
//套餐id
private Long setmealId;
//菜品id
private Long dishId;
//菜品名称 (冗余字段)
private String name;
//菜品原价
private BigDecimal price;
//份数
private Integer copies;
//排序
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;
//是否删除
private Integer isDeleted;
}
DTO (SetmealDto
)
package com.li.ruiji.dto;
import com.itheima.reggie.entity.Setmeal;
import com.itheima.reggie.entity.SetmealDish;
import lombok.Data;
import java.util.List;
@Data
public class SetmealDto extends Setmeal {
private List<SetmealDish> setmealDishes;
private String categoryName;
}
Mapper接口SetmealDishMapper
@Mapper
public interface SetmealDishMapper extends BaseMapper<SetmealDish>{
}
业务层接口SetmealDishService
public interface SetmealDishService extends IService<SetmealDish>{
}
业务层实现类SetmealDishServicelmpl
@Service
public class SetmealDishServicelmpl extends ServiceImpl<SetmealDishMapper, SetmealDish> implements SetmealDishService{
}
控制层SetmealController
//套餐管理
@RestController
@RequestMapping("/setmeal")
public class SetmealController {
@Autowired
private SetmealService setmealService;
@Autowired
private SetmealDishService setmealDishService;
}
代码交互过程
开开发新增套餐功能,其实就是在服务端编写代码去处理前端页面发送的这6次请求即可:
- 页面
(backend/page/combo/add.html)
发送ajax请求,请求服务端获取套餐分类数据并展示到下拉框中。 - 页面发送ajax请求,请求服务端获取菜品分类数据并展示到添加菜品窗口中。
- 页面发送ajax请求,请求服务端,根据菜品分类查询对应的菜品数据并展示到添加菜品窗口中。
- 页面发送请求进行图片上传,请求服务端将图片保存到服务器。
- 页面发送请求进行图片下载,将上传的图片进行回显。
- 点击保存按钮,发送ajax请求,将套餐相关数据以json形式提交到服务端。
- 页面
根据条件查询对应的菜品数据(DishController)
- 注解不能忘
//根据条件查询对应的菜品数据
@GetMapping("/list")
public R<List<Dish>> list(Dish dish){
//构造查询条件
LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(dish.getCategoryId() != null,Dish::getCategoryId,dish.getCategoryId());
//添加条件,查询状态为1(起售状态)的菜品
queryWrapper.eq(Dish::getStatus,1);
//添加排序条件
queryWrapper.orderByAsc(Dish::getSort).orderByAsc(Dish::getUpdateTime);
List<Dish> list = dishService.list(queryWrapper);
return R.success(list);
}
新增套餐业务功能开发(SetmealController)
注:
@Autowired
部分的字段是之前已存在的。- 新增套餐注释下的才为新增的代码部分。
//套餐管理
@RestController
@RequestMapping("/setmeal")
public class SetmealController {
@Autowired
private SetmealService setmealService;
@Autowired
private SetmealDishService setmealDishService;
// 新增套餐
@PostMapping
public R<String> save(@RequestBody SetmealDto setmealDto){
setmealService.saveWithDish(setmealDto);
return R.success("新增套餐成功");
}
}
新增套餐,同时需要保存套餐和菜品的关联关系(SetmealService)(SetmealServicelmpl)
public interface SetmealService extends IService<Setmeal> {
//新增套餐,同时需要保存套餐和菜品的关联关系
public void saveWithDish(SetmealDto setmealDto);
}
@Service
public class SetmealServicelmpl extends ServiceImpl<SetmealMapper, Setmeal> implements SetmealService {
@Autowired
private SetmealDishService setmealDishService;
//新增套餐,同时需要保存套餐和菜品的关联关系
@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);
}
}
套餐信息分页查询
代码交互过程
- 页面(backend/page/combo/list.html)发送ajax请求,将分页查询参数(page、pageSize、name)提交到服务端,获取分页数据。
- 页面发送请求,请求服务端进行图片下载,用于页面图片展示。
SetmealController
- 注:需添加一个新的
@Autowired
。 private CategoryService categoryService;
//套餐管理
@RestController
@RequestMapping("/setmeal")
public class SetmealController {
@Autowired
private SetmealService setmealService;
@Autowired
private SetmealDishService setmealDishService;
//------------------------------新增
@Autowired
private CategoryService categoryService;
//------------------------------新增
// 新增套餐
@PostMapping
public R<String> save(@RequestBody SetmealDto setmealDto){
setmealService.saveWithDish(setmealDto);
return R.success("新增套餐成功");
}
//------------------------------新增-----------------------------------------------------------------------------------------
//套餐信息分页查询
@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> queryWrapper = new LambdaQueryWrapper<>();
//添加查询对象,根据name进行like模糊查询
queryWrapper.like(name !=null,Setmeal::getName,name);
//添加排序条件,根据更新时间降序排列
queryWrapper.orderByDesc(Setmeal::getUpdateTime) ;
setmealService.page(pageInfo, queryWrapper);
//对象拷贝
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);
}
//------------------------------新增-----------------------------------------------------------------------------------------
}
删除套餐
代码交互过程
删除套餐,同时需要删除套餐和菜品的关联数据(SetmealService)(SetmealServicelmpl)
public interface SetmealService extends IService<Setmeal> {
//新增套餐,同时需要保存套餐和菜品的关联关系
public void saveWithDish(SetmealDto setmealDto);
//------------------------------新增-----------------------------------------------------------------------------------------
//删除套餐,同时需要删除套餐和菜品的关联数据
public void removeWithDish(List<Long> ids);
//------------------------------新增-----------------------------------------------------------------------------------------
}
//删除套餐,同时需要删除套餐和菜品的关联数据
@Transactional
public void removeWithDish(List<Long> ids){
//查询套餐状态,确定是否可用删除
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("套餐正在售卖中,不能删除");
}
//如果可以删除,先删除套餐表中的数据 _ setmeal
this.removeByIds(ids);
LambdaQueryWrapper<SetmealDish> LambdaQueryWrapper = new LambdaQueryWrapper<>();
LambdaQueryWrapper.in(SetmealDish::getSetmealId,ids);
//删除关系表中的数据 _ setmeal_dish
setmealDishService.remove(LambdaQueryWrapper);
}
删除套餐(SetmealController)
//删除套餐
@DeleteMapping
public R<String> delete(@RequestParam List<Long> ids){
setmealService.removeWithDish(ids);
return R.success("删除套餐成功");
}
管理后台总结
- 员工管理 —— 完美。
- 分类管理 —— 完美。
- 菜品管理 ——
批量删除 批量启售 批量停售
未做,停售和删除
未做。 - 套餐管理 ——
批量删除 批量启售 批量停售
未做,停售和删除
未做。 - 订单明细 —— 未做。
手机验证码登录
- 了解即可,可以用邮箱或者腾讯云替代。
- 要钱,而且个人用户申请不了,后期会在
手机验证码登录功能
开发中会直接传一个模拟验证码进行验证登录。
短信发送
阿里云短信服务
- 阿里云短信服务
- 申请AccessKey
代码开发
具体开发步骤
- 导入maven坐标
- 调用API
阿里云短信服务SDK
导入maven坐标
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>alibabacloud-dysmsapi20170525</artifactId>
<version>1.0.1</version>
</dependency>
调用API
// This file is auto-generated, don't edit it. Thanks.
package demo;
import com.aliyun.auth.credentials.Credential;
import com.aliyun.auth.credentials.provider.StaticCredentialProvider;
import com.aliyun.core.http.HttpClient;
import com.aliyun.core.http.HttpMethod;
import com.aliyun.core.http.ProxyOptions;
import com.aliyun.httpcomponent.httpclient.ApacheAsyncHttpClientBuilder;
import com.aliyun.sdk.service.dysmsapi20170525.models.*;
import com.aliyun.sdk.service.dysmsapi20170525.*;
import com.google.gson.Gson;
import darabonba.core.RequestConfiguration;
import darabonba.core.client.ClientOverrideConfiguration;
import darabonba.core.utils.CommonUtil;
import darabonba.core.TeaPair;
//import javax.net.ssl.KeyManager;
//import javax.net.ssl.X509TrustManager;
import java.net.InetSocketAddress;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.CompletableFuture;
public class SendSms {
public static void main(String[] args) throws Exception {
// HttpClient Configuration
/*HttpClient httpClient = new ApacheAsyncHttpClientBuilder()
.connectionTimeout(Duration.ofSeconds(10)) // Set the connection timeout time, the default is 10 seconds
.responseTimeout(Duration.ofSeconds(10)) // Set the response timeout time, the default is 20 seconds
.maxConnections(128) // Set the connection pool size
.maxIdleTimeOut(Duration.ofSeconds(50)) // Set the connection pool timeout, the default is 30 seconds
// Configure the proxy
.proxy(new ProxyOptions(ProxyOptions.Type.HTTP, new InetSocketAddress("<your-proxy-hostname>", 9001))
.setCredentials("<your-proxy-username>", "<your-proxy-password>"))
// If it is an https connection, you need to configure the certificate, or ignore the certificate(.ignoreSSL(true))
.x509TrustManagers(new X509TrustManager[]{})
.keyManagers(new KeyManager[]{})
.ignoreSSL(false)
.build();*/
// Configure Credentials authentication information, including ak, secret, token
StaticCredentialProvider provider = StaticCredentialProvider.create(Credential.builder()
.accessKeyId("<your-accessKeyId>")
.accessKeySecret("<your-accessKeySecret>")
//.securityToken("<your-token>") // use STS token
.build());
// Configure the Client
AsyncClient client = AsyncClient.builder()
.region("cn-hangzhou") // Region ID
//.httpClient(httpClient) // Use the configured HttpClient, otherwise use the default HttpClient (Apache HttpClient)
.credentialsProvider(provider)
//.serviceConfiguration(Configuration.create()) // Service-level configuration
// Client-level configuration rewrite, can set Endpoint, Http request parameters, etc.
.overrideConfiguration(
ClientOverrideConfiguration.create()
.setEndpointOverride("dysmsapi.aliyuncs.com")
//.setReadTimeout(Duration.ofSeconds(30))
)
.build();
// Parameter settings for API request
SendSmsRequest sendSmsRequest = SendSmsRequest.builder()
.signName("阿里云短信测试")
.templateCode("SMS_154950909")
.phoneNumbers("13502544380")
.templateParam("{\"code\":\"1234\"}")
// Request-level configuration rewrite, can set Http request parameters, etc.
// .requestConfiguration(RequestConfiguration.create().setHttpHeaders(new HttpHeaders()))
.build();
// Asynchronously get the return value of the API request
CompletableFuture<SendSmsResponse> response = client.sendSms(sendSmsRequest);
// Synchronously get the return value of the API request
SendSmsResponse resp = response.get();
System.out.println(new Gson().toJson(resp));
// Asynchronous processing of return values
/*response.thenAccept(resp -> {
System.out.println(new Gson().toJson(resp));
}).exceptionally(throwable -> { // Handling exceptions
System.out.println(throwable.getMessage());
return null;
});*/
// Finally, close the client
client.close();
}
}
手机验证码登录
代码交互过程
- 在登录页面(front/page/login.html)输入手机号,点击【获取验证码】按钮,页面发送ajax请求,在服务端调用短信服务API给指定手机号发送验证码短信。
- 在登录页面输入验证码,点击【登录】按钮,发送ajax请求,在服务端处理登录请求。
类和接口基本结构
- 实体类
User
- Mapper接口
UserMapper
- 业务层接口
UserService
- 业务层实现类
UserServicelmpl
- 控制层
Usercontroller
- 工具类
SMSUtils
、ValidateCodeutils
实体类User
package com.li.ruiji.entity;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
/**
* 用户信息
*/
@Data
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
//姓名
private String name;
//手机号
private String phone;
//性别 0 女 1 男
private String sex;
//身份证号
private String idNumber;
//头像
private String avatar;
//状态 0:禁用,1:正常
private Integer status;
}
Mapper接口UserMapper
@Mapper
public interface UserMapper extends BaseMapper<User>{
}
业务层接口UserService
public interface UserService extends IService<User>{
}
业务层实现类UserServicelmpl
@Service
public class UserServicelmpl extends ServiceImpl<UserMapper, User> implements UserService {
}
控制层Usercontroller
@RestController
@RequestMapping("/user")
public class Usercontroller {
@Autowired
private UserService userService;
}
新建utils包
- 因为黑马给的短信发送工具类是原版api,所以导入的依赖也是旧版的。
- 这样他才不会报错。
maven
<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>1.1.0</version>
</dependency>
短信发送工具类SMSUtils
package com.li.ruiji.utils;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
/**
* 短信发送工具类
*/
public class SMSUtils {
/**
* 发送短信
* @param signName 签名
* @param templateCode 模板
* @param phoneNumbers 手机号
* @param param 参数
*/
public static void sendMessage(String signName, String templateCode,String phoneNumbers,String param){
DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "", "");
IAcsClient client = new DefaultAcsClient(profile);
SendSmsRequest request = new SendSmsRequest();
request.setSysRegionId("cn-hangzhou");
request.setPhoneNumbers(phoneNumbers);
request.setSignName(signName);
request.setTemplateCode(templateCode);
request.setTemplateParam("{\"code\":\""+param+"\"}");
try {
SendSmsResponse response = client.getAcsResponse(request);
System.out.println("短信发送成功");
}catch (ClientException e) {
e.printStackTrace();
}
}
}
随机生成验证码工具类ValidateCodeutils
package com.li.ruiji.utils;
import java.util.Random;
/**
* 随机生成验证码工具类
*/
public class ValidateCodeUtils {
/**
* 随机生成验证码
* @param length 长度为4位或者6位
* @return
*/
public static Integer generateValidateCode(int length){
Integer code =null;
if(length == 4){
code = new Random().nextInt(9999);//生成随机数,最大为9999
if(code < 1000){
code = code + 1000;//保证随机数为4位数字
}
}else if(length == 6){
code = new Random().nextInt(999999);//生成随机数,最大为999999
if(code < 100000){
code = code + 100000;//保证随机数为6位数字
}
}else{
throw new RuntimeException("只能生成4位或6位数字验证码");
}
return code;
}
/**
* 随机生成指定长度字符串验证码
* @param length 长度
* @return
*/
public static String generateValidateCode4String(int length){
Random rdm = new Random();
String hash1 = Integer.toHexString(rdm.nextInt());
String capstr = hash1.substring(0, length);
return capstr;
}
}
代码开发
修改LogincheckFilter
- LoginCheckFilter过滤器用于检查用户的登录状态。
- 我们在进行手机验证码登录时,发送的请求需要在此过滤器处理时直接放行。
//定义不需要的处理的请求路径
String[] urls =new String[]{
"/employee/login",
"/employee/logout",
"/backend/**",
"/front/**",
"/common/**",
//------------------------------新增-----------------------------------------------------------------------------------------
"/user/sendMsg",//移动端发送短信
"/user/login" //移动端登录
//------------------------------新增-----------------------------------------------------------------------------------------
};
- 在LoginCheckFilter过滤器中扩展逻辑,判断移动端用户登录状态:
//前台 —— 判断登录状态,如果已登录,则直接放行。
if (request.getSession().getAttribute("user") !=null){
Long userId = (Long) request.getSession().getAttribute("user");
BaseContext.setCurrentId(userId);
filterChain.doFilter(request,response);
return;
}
之前的前端代码(front)需要修改
- 之前的前端代码(front)有点问题,建议直接复制粘贴day5的front文件夹代码来进行替换。
发送手机短信验证码(Usercontroller)
需要将生成的验证码保存到Session:
HttpSession session
不能忘咯。
@RestController
@RequestMapping("/user")
@Slf4j
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();
//StringUtils导的包是org.apache.commons.lang3.StringUtils;
if(StringUtils.isNotEmpty(phone)){
//生成随机的4位验证码
String code = ValidateCodeUtils.generateValidateCode(4).toString();
log.info("code={}",code);
//调用阿里云提供的短信服务API完成发送短信
//SMSUtils.sendMessage("瑞吉外卖","",phone,code);
//需要将生成的验证码保存到Session
session.setAttribute(phone,code);
return R.success("手机验证码短信发送成功");
}
return R.error("手机验证码短信发送失败");
}
}
- 因为这里的
StringUtils
使用到了org.apache.commons.lang3.StringUtils
; - 所以需要导入
commons-lang3
的依赖。
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
移动端用户登录
/**
* 移动端用户登录
* @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> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getPhone,phone);
User user = userService.getOne(queryWrapper);
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("登录失败");
}
菜品展示
用户地址簿相关功能代码
数据模型
- 用户的地址信息会存储在address_book表,即地址簿表中。
类和接口基本结构
- 实体类
AddressBook
- Mapper接口
AddressBookMapper
- 业务层接口
AddressBookService
- 业务层实现类
AddressBookServicelmpl
- 控制层
AddressBookController
实体类AddressBook
package com.li.ruiji.controller;
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 AddressBook implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
//用户id
private Long userId;
//收货人
private String consignee;
//手机号
private String phone;
//性别 0 女 1 男
private String sex;
//省级区划编号
private String provinceCode;
//省级名称
private String provinceName;
//市级区划编号
private String cityCode;
//市级名称
private String cityName;
//区级区划编号
private String districtCode;
//区级名称
private String districtName;
//详细地址
private String detail;
//标签
private String label;
//是否默认 0 否 1是
private Integer isDefault;
//创建时间
@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;
//是否删除
private Integer isDeleted;
}
Mapper接口AddressBookMapper
@Mapper
public interface AddressBookMapper extends BaseMapper<AddressBook>{
}
业务层接口AddressBookService
public interface AddressBookService extends IService<AddressBook>{
}
业务层实现类AddressBookServicelmpl
@Service
public class AddressBookServicelmpl extends ServiceImpl<AddressBookMapper, AddressBook> implements AddressBookService {
}
控制层AddressBookController
(地址簿管理)(这个代码细节要重视)
- 这里控制层
AddressBookController
(地址簿管理)导致了后期下单模块出错。 - 注释的是原代码,应该是==的,结果写成!=,逻辑错误,这样他就查询不到默认地址了。
// if (null != addressBook) {
//// return R.error("没有找到该对象");
//// } else {
//// return R.success(addressBook);
//// }
if (null == addressBook) {
return R.error("没有找到该对象");
} else {
return R.success(addressBook);
}
package com.li.ruiji.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.li.ruiji.common.BaseContext;
import com.li.ruiji.common.R;
import com.li.ruiji.entity.AddressBook;
import com.li.ruiji.service.AddressBookService;
import com.li.ruiji.service.AddressBookService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 地址簿管理
*/
@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));
}
}
菜品展示
代码交互过程
代码开发
暂时修改一下从静态json文件获取数据(front/api/main.js)
- 注意:首页加载完成后,还发送了一次ajax请求用于加载购物车数据,此处可以将这次请求的地址暂时修改一下从静态json文件获取数据,等后续开发购物车功能时再修改回来。
//获取购物车内商品的集合
function cartListApi(data) {
return $axios({
// 'url': '/shoppingCart/list',
//假数据
'url': '/front/cartData.json',
'method': 'get',
params:{...data}
})
}
- 请求cartData.json(假数据),
cartData.json
的代码示例:
{
"code": 1,
"msg": null,
"data": [],
"map": {}
}
DishDto添加注释
//菜品对应的口味数据
@Data
public class DishDto extends Dish {
//菜品对应的口味数据
private List<DishFlavor> flavors = new ArrayList<>();
private String categoryName;
private Integer copies;
}
修改DishController中的list方法(根据条件查询对应的菜品数据)
- 注释原有方法,改
R<List<Dish>>
为R<List<DishDto>>
。 - 新增一行代码:
List<DishDto> dishDtoList = null;
- 返回
list
的改为dishDtoList
:return R.success(dishDtoList);
- 复制前段对象拷贝部分代码,进行改造:
//复制该段对象拷贝部分代码,并复制到“根据条件查询对应的菜品数据方法”下进行改造
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
//根据ID查询分类对象
Category category = categoryService.getById(categoryId);
if(category != null){
String categoryName = category.getName();
dishDto.setCategoryName(categoryName) ;
}
return dishDto;
}).collect(Collectors.toList());
- 在
return dishDto;
前添加 ”当前菜品id “ ——Long dishId = item.getId();
//当前菜品id
Long dishId = item.getId();
LambdaQueryWrapper<DishFlavor> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(DishFlavor::getDishId,dishId);
//SQL:select * from dish_flavor where dish_id = ?
List<DishFlavor> dishFlavorList = dishFlavorService.list(lambdaQueryWrapper);
dishDto.setFlavors(dishFlavorList);
return dishDto;
修改DishController中的list方法(根据条件查询对应的菜品数据)(2)
- 注释掉的为原有list方法。
- 上述步骤做完的代码如下所示:
// //根据条件查询对应的菜品数据
// @GetMapping("/list")
// public R<List<Dish>> list(Dish dish){
// //构造查询条件
// LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
// queryWrapper.eq(dish.getCategoryId() != null,Dish::getCategoryId,dish.getCategoryId());
//
// //添加条件,查询状态为1(起售状态)的菜品
// queryWrapper.eq(Dish::getStatus,1);
//
// //添加排序条件
// queryWrapper.orderByAsc(Dish::getSort).orderByAsc(Dish::getUpdateTime);
//
// List<Dish> list = dishService.list(queryWrapper);
//
// return R.success(list);
// }
//根据条件查询对应的菜品数据
@GetMapping("/list")
public R<List<DishDto>> list(Dish dish){
//构造查询条件
LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(dish.getCategoryId() != null,Dish::getCategoryId,dish.getCategoryId());
//添加条件,查询状态为1(起售状态)的菜品
queryWrapper.eq(Dish::getStatus,1);
//添加排序条件
queryWrapper.orderByAsc(Dish::getSort).orderByAsc(Dish::getUpdateTime);
List<Dish> list = dishService.list(queryWrapper);
List<DishDto> dishDtoList = list.stream().map((item) ->{
DishDto dishDto = new DishDto();
BeanUtils.copyProperties(item,dishDto);
Long categoryId = item.getCategoryId(); //分类id
//根据ID查询分类对象
Category category = categoryService.getById(categoryId);
if(category != null){
String categoryName = category.getName();
dishDto.setCategoryName(categoryName) ;
}
//当前菜品id
Long dishId = item.getId();
LambdaQueryWrapper<DishFlavor> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(DishFlavor::getDishId,dishId);
//SQL:select * from dish_flavor where dish_id = ?
List<DishFlavor> dishFlavorList = dishFlavorService.list(lambdaQueryWrapper);
dishDto.setFlavors(dishFlavorList);
return dishDto;
}).collect(Collectors.toList());
return R.success(dishDtoList);
}
创建SetmealController的list方法
//根据条件查询套餐数据
@GetMapping("/list")
public R<List<Setmeal>> list(Setmeal setmeal){
LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(setmeal.getCategoryId() != null,Setmeal::getCategoryId,setmeal.getCategoryId());
queryWrapper.eq(setmeal.getStatus() != null,Setmeal::getStatus,setmeal.getStatus());
queryWrapper.orderByDesc(Setmeal::getUpdateTime);
List<Setmeal> list = setmealService.list(queryWrapper);
return R.success(list);
}
购物车
数据模型
- 用户的地址信息会存储在shopping_card表,即地址簿表中。
代码交互过程
类和接口基本结构
- 实体类
Shoppingcart
- Mapper接口
ShoppingCartMapper
- 业务层接口
ShoppingCartService
- 业务层实现类
ShoppingCartServicelmpl
- 控制层
ShoppingCartController
实体类Shoppingcart
package com.li.ruiji.entity;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 购物车
*/
@Data
public class ShoppingCart implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
//名称
private String name;
//用户id
private Long userId;
//菜品id
private Long dishId;
//套餐id
private Long setmealId;
//口味
private String dishFlavor;
//数量
private Integer number;
//金额
private BigDecimal amount;
//图片
private String image;
private LocalDateTime createTime;
}
Mapper接口ShoppingCartMapper
@Mapper
public interface ShoppingCartMapper extends BaseMapper<ShoppingCart>{
}
业务层接口ShoppingCartService
public interface ShoppingCartService extends IService<ShoppingCart>{
}
业务层实现类ShoppingCartServicelmpl
@Service
public class ShoppingCartServicelmpl extends ServiceImpl<ShoppingCartMapper, ShoppingCart> implements ShoppingCartService {
}
控制层ShoppingCartController
//购物车
@RestController
@RequestMapping("/shoppingCart")
public class ShoppingCartController {
@Autowired
private ShoppingCartService shoppingCartService;
}
代码开发
添加购物车(ShoppingCartController)
//添加购物车
@PostMapping("/add")
public R<ShoppingCart> add(@RequestBody ShoppingCart shoppingCart){
//设置用户id,指定当前是哪个用户的购物车数据
Long currentId = BaseContext.getCurrentId();
shoppingCart.setUserId(currentId);
Long dishId = shoppingCart.getDishId();
LambdaQueryWrapper<ShoppingCart> queryWrapper =new LambdaQueryWrapper<>();
queryWrapper.eq(ShoppingCart::getUserId,currentId);
if (dishId != null) {
//添加到购物车的是菜品
queryWrapper.eq(ShoppingCart::getDishId,dishId);
}else {
//添加到购物车的是套餐
queryWrapper.eq(ShoppingCart::getSetmealId,shoppingCart.getSetmealId());
}
//查询当前菜品或者套餐是否在购物车中
//SQL:select * from shopping_cart where user_id = ? and dish_id/setmeal_id = ?
ShoppingCart cartServiceOne = shoppingCartService.getOne(queryWrapper);
if (cartServiceOne != null){
//如果已经存在,就在原来数量基础上加一
Integer number =cartServiceOne.getNumber();
cartServiceOne.setNumber(number+1);
shoppingCartService.updateById(cartServiceOne);
}else{
// 如果不存在,则添加到购物车,数量默认就是1
shoppingCart.setNumber(1);
shoppingCart. setCreateTime (LocalDateTime.now()) ;
shoppingCartService.save(shoppingCart);
cartServiceOne = shoppingCart;
}
return R.success(cartServiceOne);
}
修改请求地址(front/api/main.js)
- 在”菜品展示“中为了临时调试,修改了请求的地址,从静态json文件获取数据,现在开发购物车功能修改回来。
- 现在的请求地址:
'url': '/shoppingCart/list'
//获取购物车内商品的集合
function cartListApi(data) {
return $axios({
'url': '/shoppingCart/list',
// 'url': '/front/cartData.json',
'method': 'get',
params:{...data}
})
}
查看购物车(ShoppingCartController)
//查看购物车
@GetMapping("/list")
public R<List<ShoppingCart>> list(){
LambdaQueryWrapper<ShoppingCart> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ShoppingCart::getUserId,BaseContext.getCurrentId());
queryWrapper.orderByAsc(ShoppingCart::getCreateTime);
List<ShoppingCart> list = shoppingCartService.list(queryWrapper);
return R.success(list);
}
清空购物车(ShoppingCartController)
//清空购物车
@DeleteMapping("/clean")
public R<String> clean(){
//SQL : delete from shopping_cart where user_id = ?
LambdaQueryWrapper<ShoppingCart> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ShoppingCart::getUserId,BaseContext.getCurrentId());
shoppingCartService.remove(queryWrapper);
return R.success("清空购物车成功");
}
用户下单
数据模型
用户下单业务对应的数据表为
orders表
和order_detail表
orders
—— 订单表order_detail
—— 订单明细表
代码交互过程
类和接口基本结构
- 实体类
Orders
、OrderDetail
- Mapper接口
OrderMapper
、OrderDetailMapper
- 业务层接口
OrderService
、OrderDetailService
- 业务层实现类
OrderServicelmpl
、OrderDetailServicelmpl
- 控制层
OrderController
、OrderDetailController
实体类Orders
、OrderDetail
package com.li.ruiji.entity;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 订单
*/
@Data
public class Orders implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
//订单号
private String number;
//订单状态 1待付款,2待派送,3已派送,4已完成,5已取消
private Integer status;
//下单用户id
private Long userId;
//地址id
private Long addressBookId;
//下单时间
private LocalDateTime orderTime;
//结账时间
private LocalDateTime checkoutTime;
//支付方式 1微信,2支付宝
private Integer payMethod;
//实收金额
private BigDecimal amount;
//备注
private String remark;
//用户名
private String userName;
//手机号
private String phone;
//地址
private String address;
//收货人
private String consignee;
}
package com.li.ruiji.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 订单明细
*/
@Data
public class OrderDetail implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
//名称
private String name;
//订单id
private Long orderId;
//菜品id
private Long dishId;
//套餐id
private Long setmealId;
//口味
private String dishFlavor;
//数量
private Integer number;
//金额
private BigDecimal amount;
//图片
private String image;
}
Mapper接口OrderMapper
、OrderDetailMapper
@Mapper
public interface OrderMapper extends BaseMapper<Orders>{
}
@Mapper
public interface OrderDetailMapper extends BaseMapper<OrderDetail>{
}
业务层接口OrderService
、OrderDetailService
public interface OrderService extends IService<Orders>{
}
public interface OrderDetailService extends IService<OrderDetail> {
}
业务层实现类OrderServicelmpl
、OrderDetailServicelmpl
@Service
public class OrderServicelmpl extends ServiceImpl<OrderMapper, Orders> implements OrderService {
}
@Service
public class OrderDetailServicelmpl extends ServiceImpl<OrderDetailMapper, OrderDetail> implements OrderDetailService{
}
控制层OrderController
、OrderDetailController
//订单(用户下单)
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
}
//订单明细(用户下单)
@RestController
@RequestMapping("/orderDetail")
public class OrderDetailController {
@Autowired
private OrderDetailService orderDetailService;
}
代码开发
用户下单(OrderController)
//订单(用户下单)
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
//用户下单
@PostMapping("/submit")
public R<String> submit(@RequestBody Orders orders){
orderService.submit(orders);
return R.success("下单成功");
}
}
在OrderService中扩展方法
public interface OrderService extends IService<Orders>{
//用户下单
public void submit(Orders orders) ;
}
在OrderServicelmpl中实现这个“用户下单”这个方法
@Service
public class OrderServicelmpl extends ServiceImpl<OrderMapper, Orders> implements OrderService {
@Autowired
private ShoppingCartService shoppingCartService;
@Autowired
private UserService userService;
@Autowired
private AddressBookService addressBookService;
@Autowired
private OrderDetailService orderDetailService;
//用户下单
@Transactional
public void submit(Orders orders) {
//获得当前用户id
Long userId = BaseContext.getCurrentId();
//查询当前用户的购物车数据
LambdaQueryWrapper<ShoppingCart> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ShoppingCart::getUserId, userId) ;
List<ShoppingCart> shoppingCarts = shoppingCartService.list(wrapper);
if (shoppingCarts == null || shoppingCarts.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 = shoppingCarts.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(wrapper);
}
}
- 在这里修改下控制层
AddressBookController
(地址簿管理),之前我写错了。
总结
基础篇大体上完成了,接下来便是进阶篇了。
Comments | NOTHING