瑞吉外卖 |
您所在的位置:网站首页 › 瑞吉外卖前端用的什么技术 › 瑞吉外卖 |
业务开发Day5-01-本章内容介绍
效果展示
套餐管理界面 新增套餐界面 客户端展示 目录 新增套餐套餐信息分页查询删除套餐 业务开发Day5-02-新增套餐_需求分析&数据模型 需求分析 套餐就是菜品的集合后台系统中可以管理套餐信息,通过新增套餐功能来添加一个新的套餐在添加套餐时需要选择当前套餐所属的套餐分类和包含的菜品,并且需要上传套餐对应的图片在移动端会按照套餐分类来展示对应的套餐。 数据模型 新增套餐,其实就是将新增页面录入的套餐信息插入到setmeal表,还需要向setmeal_dish表插入套餐和菜品关联数据所以在新增套餐时,涉及到两个表: setmeal----套餐表setmeal_dish----套餐菜品关系表setmeal setmeal_dish 业务开发Day5-03-新增套餐_代码开发_准备工作&梳理交互过程 代码开发-准备工作在开发业务功能前,先将需要用到的类和接口基本结构创建好: 实体类SetmealDish(直接从课程资料中导入即可,Setmeal实体前面课程中已经导入过了)DTO SetmealDto (直接从课程资料中导入即可)Mapper接口SetmealDishMapper业务层接口SetmealDishService业务层实现类SetmealDishservicelmpl控制层SetmealControllerSetmealDish—实体类 package com.itzq.reggie.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.itzq.reggie.dto; import com.itzq.reggie.entity.Setmeal; import com.itzq.reggie.entity.SetmealDish; import lombok.Data; import java.util.List; @Data public class SetmealDto extends Setmeal { private List setmealDishes; private String categoryName; }SetmealDishMapper接口 package com.itzq.reggie.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.itzq.reggie.entity.SetmealDish; import org.apache.ibatis.annotations.Mapper; @Mapper public interface SetmealDishMapper extends BaseMapper { }SetmealDishService接口 package com.itzq.reggie.service; import com.baomidou.mybatisplus.extension.service.IService; import com.itzq.reggie.entity.SetmealDish; public interface SetmealDishService extends IService { }SetmealDishservicelmpl实现类 package com.itzq.reggie.service.Impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.itzq.reggie.entity.SetmealDish; import com.itzq.reggie.mapper.SetmealDishMapper; import com.itzq.reggie.service.SetmealDishService; import org.springframework.stereotype.Service; @Service public class SetmealDishServiceImpl extends ServiceImpl implements SetmealDishService { }SetmealController控制层 package com.itzq.reggie.controller; import com.itzq.reggie.service.SetmealDishService; import com.itzq.reggie.service.SetmealService; 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; @RestController @RequestMapping("/setmeal") @Slf4j public class SetmealController { @Autowired private SetmealService setmealService; @Autowired private SetmealDishService setmealDishService; } 代码开发-梳理交互过程在开发代码之前,需要梳理一下新增套餐时前端页面和服务端的交互过程: 页面(backend/page/combo/add.html)发送ajax请求,请求服务端获取套餐分类数据并展示到下拉框中(已完成)页面发送ajax请求,请求服务端,获取菜品分类数据并展示到添加菜品窗口中页面发送ajax请求,请求服务端,根据菜品分类查询对应的菜品数据并展示到添加菜品窗口中页面发送请求进行图片上传,请求服务端将图片保存到服务器(已完成)页面发送请求进行图片下载,将上传的图片进行回显(已完成)点击保存按钮,发送ajax请求,将套餐相关数据以json形式提交到服务端开发新增套餐功能,其实就是在服务端编写代码去处理前端页面发送的这6次请求即可 业务开发Day5-04-新增套餐_代码开发_根据分类查询菜品 前端分析启动项目,进入套餐管理,点击新建套餐,会发现页面发送的请求未被服务端接收 爆系统接口异常,服务端未定义查询菜品的方法 相关代码在DishController类中,添加list方法 注意:需要添加额外的查询条件,只查询status为1的数据,表示该菜品为起售状态,才能被加入套餐中,供用户选择 @GetMapping("/list") public R list(Dish dish){ //构造查询条件 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper(); queryWrapper.eq(dish.getCategoryId() != null, Dish::getCategoryId, dish.getCategoryId()); //添加条件,查询状态为1(1为起售,0为停售)的菜品 queryWrapper.eq(Dish::getStatus,1); List list = dishService.list(queryWrapper); //添加排序条件 return R.success(list); }重启项目,发现查询数据成功,并回显到前端页面 业务开发Day5-05-新增套餐_代码开发_服务端接收页面提交的数据 前端分析启动项目,来到添加套餐页面,输入数据,点击保存 查看前端页面发送的请求,请求方式 前端页面传输json数据给服务端 相关代码(测试版)在SetmealController类中添加save方法 @PostMapping public R save(@RequestBody SetmealDto setmealDto){ log.info("数据传输对象setmealDto:{}",setmealDto.toString()); return null; }添加断点 debug方式重启项目,来到添加套餐页面,输入数据,点击保存 跳转到服务端,查看是否接收到客服端提交的数据,发现数据成功接收 业务开发Day5-06-新增套餐_代码开发_保存数据到对应表 相关代码在SetmealService接口,添加saveWithDish方法 实现类SetmealServicelmpl,实现接口添加的方法,并向方法中添加代码逻辑 保存套餐的基本信息保存套餐和菜品的关联信息 @Override @Transactional public void saveWithDish(SetmealDto setmealDto) { //保存套餐的基本信息,操作setmeal,执行insert操作 save(setmealDto); List setmealDishes = setmealDto.getSetmealDishes(); setmealDishes = setmealDishes.stream().map((item) -> { item.setSetmealId(setmealDto.getId()); return item; }).collect(Collectors.toList()); //保存套餐和菜品的关联信息 setmealDishService.saveBatch(setmealDishes); }在SetmealController控制层的save方法中,调用saveWithDish方法,将数据保存至数据库 @PostMapping public R save(@RequestBody SetmealDto setmealDto){ log.info("数据传输对象setmealDto:{}",setmealDto.toString()); setmealService.saveWithDish(setmealDto); return R.success("新增套餐成功"); } 业务开发Day5-07-新增套餐_代码开发_功能测试 功能测试来到新增套餐页面,输入数据,点击保存 setmeal_dish表—数据插入成功 setmeal—数据插入成功 业务开发Day5-08-套餐信息分页查询_需求分析&梳理交互过程 需求分析 系统中的套餐数据很多的时候,如果在一个页面中全部展示出来会显得比较乱,不便于查看一般的系统中都会以分页的方式来展示列表数据 梳理交互过程在开发代码之前,需要梳理一下套餐分页查询时前端页面和服务端的交互过程: 页面(backend/page/combo/list.html)发送ajax请求,将分页查询参数(page、pageSize、name)提交到服务端,获取分页数据页面发送请求,请求服务端进行图片下载,用于页面图片展示开发套餐信息分页查询功能,其实就是在服务端编写代码去处理前端页面发送的这2次请求即可 业务开发Day5-09-套餐信息分页查询_代码开发&功能测试 前端分析点击套餐管理,前端页面发送ajax请求,请求方式:get 代码开发SetmealController类中,添加list方法 @GetMapping("/page") public R list(int page,int pageSize,String name){ //分页构造器对象 Page pageInfo = new Page(page, pageSize); Page dtoPage = new Page(); //构造查询条件对象 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper(); queryWrapper.eq(name != null, Setmeal::getName, name); //操作数据库 setmealService.page(pageInfo,queryWrapper); //对象拷贝 BeanUtils.copyProperties(pageInfo,dtoPage,"records"); List records = pageInfo.getRecords(); List list = records.stream().map((item) -> { SetmealDto setmealDto = new SetmealDto(); BeanUtils.copyProperties(item, setmealDto); //获取categoryId Long categoryId = item.getCategoryId(); 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); }注意 在套餐管理界面,套餐分类字段显示的是categoryId对应的中文,但在数据库里查询到的是categoryId,因此需要利用categoryId查询到categoryName,并赋值给数据传输对象SetmealDto 功能测试启动项目,点击套餐管理,前端发送ajax请求,服务端接收前端发出的请求,并做相应的处理,向页面返回数据 数据成功回显到页面 业务开发Day5-10-删除套餐_需求分析&梳理交互过程 需求分析 在套餐管理列表页面点击删除按钮,可以删除对应的套餐信息也可以通过复选框选择多个套餐,点击批量删除按钮一次删除多个套餐注意,对于状态为售卖中的套餐不能删除,需要先停售,然后才能删除。 梳理交互过程在开发代码之前,需要梳理一下删除套餐时前端页面和服务端的交互过程: 删除单个套餐时,页面发送ajax请求,根据套餐id删除对应套餐 删除多个套餐时,页面发送ajax请求,根据提交的多个套餐id删除对应套餐开发删除套餐功能 其实就是在服务端编写代码去处理前端页面发送的这2次请求即可 注意 观察删除单个套餐和批量删除套餐的请求信息可以发现,两种请求的地址和请求方式都是相同的不同的则是传递的id个数,所以在服务端可以提供一个方法来统一处理。 业务开发Day5-11-删除套餐_代码开发&功能测试 代码开发(测试)在SetmealController中添加delete方法 @DeleteMapping public R delete(@RequestParam List ids){ log.info("ids为:",ids); return null; }在delete方法上,添加断点 debug方式启动项目,来到套餐管理页面,点击删除按钮 跳转到服务端,查询ids可知服务端成功接收到前端传来的数据信息 代码开发(完善)在SetmealService接口中添加removeWithDish方法 在SetmealServicelmpl实现类中实现对应接口中添加的方法 @Override @Transactional public void removeWithDish(List ids) { //select count(*) from setmeal where ids in(1,2,3) and status = 1 //查询套餐状态,确定是否可以删除 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper(); queryWrapper.in(Setmeal::getId,ids); queryWrapper.eq(Setmeal::getStatus,1); int count = super.count(queryWrapper); if (count > 0){ //如果不能删除,抛出一个业务异常 throw new CustomException("套餐正在售卖中,不能删除"); } //如果可以删除,先删除套餐表中的数据 super.removeByIds(ids); //删除关系表中的数据 //delete from setmeal_dish where setmeal_id in(1,2,3) LambdaQueryWrapper dishLambdaQueryWrapper = new LambdaQueryWrapper(); dishLambdaQueryWrapper.in(SetmealDish::getSetmealId,ids); setmealDishService.remove(dishLambdaQueryWrapper); }在SetmealController中完善代码—调用removeWithDish方法,实现套餐数据删除成功 @DeleteMapping public R delete(@RequestParam List ids){ log.info("ids为:",ids); setmealService.removeWithDish(ids); return R.success("套餐数据删除成功"); }注意:将setmeal表中status字段值改为0—为停售状态,方便测试 重启项目,来到套餐管理界面,点击删除按钮 页面显示删除成功 setmeal表中该行已被删除 业务开发Day5-12-本章内容介绍 手机验证码登录 点击获取验证码收到短信,并输入验证码点击登录,登录成功客户端登录成功页面 本章内容介绍 短信发送手机验证码登录(基于阿里云讲解) 业务开发Day5-13-短信发送_短信服务介绍和阿里云短信服务介绍 短信服务介绍 目前市面上有很多第三方提供的短信服务,这些第三方短信服务会和各个运营商(移动、联通、电信)对接我们只需要注册成为会员并且按照提供的开发文档进行调用就可以发送短信 -* 需要说明的是*,这些短信服务一般都是收费服务常用短信服务: 阿里云华为云腾讯云京东梦网乐信阿里云短信服务(Short Message Service)是广大企业客户快速触达手机用户所优选使用的通信能力。调用API或用群发助手,即可发送验证码、通知类和营销类短信;国内验证短信秒级触达,到达率最高可达99%;国际/港澳台短信覆盖200多个国家和地区,安全稳定,广受出海企业选用。 应用场景: 验证码推广短信推广短信 阿里云短信服务介绍打开浏览器,登录阿里云—网址:https://cn.aliyun.com/ 点击产品,在搜索框中输入短信服务,并点击搜索 来到短信息服务界面 选择符合自己业务需求的短信套餐包 业务开发Day5-14-短信发送_阿里云短信服务 设置短信签名开通短信服务之后,进入短信服务管理页面,选择国内消息菜单,我们需要在这里添加短信签名 什么是短信签名? 短信签名是短信发送者的署名,表示发送方的身份我们要调用阿里云短信服务发送短信,签名是必不可少的部分添加短信签名方式 注意:个人申请签名是有一定的难度的,所以我们只需要了解一下使用短信签名的具体流程 设置短信模板切换到【模板管理】标签页: 短信模板包含短信发送内容、场景、变量信息 每一个被设置好的模板有一个短信模板详情,模板详情包含了模板的6条信息 添加模板,并且提交后审核通过 设置AccessKeyAccessKey 是访问阿里云 API 的密钥,具有账户的完全权限,我们要想在后面通过API调用阿里云短信服务的接口发送短信,那么就必须要设置AccessKey。 光标移动到用户头像上,在弹出的窗口中点击【AccessKey管理】︰ 进入到AccessKey的管理界面之后,提示两个选项: 继续使用AccessKey开始使用子用户AccessKey区别: 继续使用AccessKey 如果选择的是该选项,我们创建的是阿里云账号的AccessKey,是具有账户的完全权限有了这个AccessKey之后,我们就可以通过API调用阿里云服务,不仅是短信服务,其他服务也可以调用相对来说,并不安全,当前的AccessKey泄露,会影响到当前账户的其他云服务。 开始使用子用户AccessKey 可以创建一个子用户,这个子用户可以分配比较低的权限,比如仅分配短信发送的权限,不具备操作其他的服务的权限即使这个AccessKey泄漏了,也不会影响其他的云服务, 相对安全。创建子用户AccessKey。 点击创建用户 输入登录名称和显示名称(都是自定义),选择—Open API调用访问 注意:在java代码当中使用这个用户,所以我们选择—Open API调用访问 成功创建子用户AccessKey AccessKey ID:用户名AccessKey Secret:密码需要将这对用户名和密码保存起来,后面在我们的程序当中会使用 权限管理在新创建的子用户下点击添加权限 因为我们只需要使用短信服务,所以我们在搜索框输入sms,点击需要添加的权限 授权成功 表示当前我们只给该用户授予了两个权限,即使用户名和密码泄露,其他人也只能调用短信服务 授权成功之后就可以用代码的方式来调用短信服务 AccessKey泄露需要进行的处理 AccessKey泄露出去,别人就可以使用我们的 AccessKey来发送短信,我们就需要收回我们的 AccessKey我们可以禁用或者删除对应的 AccessKey操作之后,相当于这个 AccessKey就作废了 业务开发Day5-15-短信发送_代码开发_参照官方文档封装发送短信工具类 参照官方文档使用阿里云短信服务发送短信,可以参照官方提供的文档即可。 具体开发步骤: 导入maven坐标 com.aliyun aliyun-java-sdk-core 4.5.16 com.aliyun aliyun-java-sdk-dysmsapi 2.1.0 在reggie包下新建utils包,导入该工具类 package com.itzq.reggie.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(); } } }查看短信服务产品文档的java SDK,了解短信服务java SDK的使用方法以及示例 业务开发Day5-16-手机验证码登录_需求分析_数据模型 需求分析为了方便用户登录,移动端通常都会提供通过手机验证码登录的功能 手机验证码登录的优点: 方便快捷,无需注册,直接登录使用短信验证码作为登录凭证,无需记忆密码安全登录流程: 输入手机号 > 获取验证码 > 输入验证码 > 点击登录 > 登录成功注意:通过手机验证码登录,手机号是区分不同用户的标识 用户登录端界面 数据模型通过手机验证码登录时,涉及的表为user表,即用户表。结构如下: 注意: 手机号是区分不同用户的标识,在用户登录的时候判断所输入的手机号是否存储在表中如果不在表中,说明该用户为一个新的用户,将该用户自动保在user表中 业务开发Day5-17-手机验证码登录_代码开发_梳理交互过程&修改LoginCheckFilter 梳理交互过程在开发代码之前,需要梳理一下登录时前端页面和服务端的交互过程: 在登录页面(front/page/login.html)输入手机号,点击【获取验证码】按钮,页面发送ajax请求,在服务端调用短信服务API给指定手机号发送验证码短信在登录页面输入验证码,点击【登录】按钮,发送ajax请求,在服务端处理登录请求开发手机验证码登录功能,其实就是在服务端编写代码去处理前端页面发送的这2次请求即可。 代码开发-准备工作在开发业务功能前,先将需要用到的类和接口基本结构创建好: 实体类user (直接从课程资料中导入即可)Mapper接口UserMapper业务层接口UserService业务层实现类UserServicelmpl控制层UserController工具类SMSutils、ValidateCodeutils(直接从课程资料中导入即可)实体类user package com.itzq.reggie.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 package com.itzq.reggie.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.itzq.reggie.entity.User; import org.apache.ibatis.annotations.Mapper; @Mapper public interface UserMapper extends BaseMapper { }业务层接口UserService package com.itzq.reggie.service; import com.baomidou.mybatisplus.extension.service.IService; import com.itzq.reggie.entity.User; public interface UserService extends IService { }业务层实现类UserServicelmpl package com.itzq.reggie.service.Impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.itzq.reggie.entity.User; import com.itzq.reggie.mapper.UserMapper; import com.itzq.reggie.service.UserService; import org.springframework.stereotype.Service; @Service public class UserServicelmpl extends ServiceImpl implements UserService { }控制层UserController package com.itzq.reggie.controller; import com.itzq.reggie.service.UserService; 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; @RestController @RequestMapping("/user") @Slf4j public class UserController { @Autowired private UserService userService; }工具类SMSutils、ValidateCodeutils(直接从课程资料中导入即可) SMSutils类 package com.itzq.reggie.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.itzq.reggie.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 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |