瑞吉外卖业务开发(3)https://developer.aliyun.com/article/1530407
新增套餐
需求分析
套餐就是菜品的集合。
后台系统中可以管理套餐信息,通过新增套餐功能来添加一个新的套餐,在添加套餐时需要选择当前套餐所属的套餐分类和包含的菜品,并且需要上传套餐对应的图片,在移动端会按照套餐分类来展示对应的套餐。
数据模型
新增套餐,其实就是将新增页面录入的套餐信息插入到setmeal表,还需要向setmeal_dish表插入套餐和菜品关联数据所以在新增套餐时,涉及到两个表:
- setmeal 套餐表
- setmeal_dish套餐菜品关系表
代码开发
准备工作
需要准备好的类和接口
- 实体类SetmealDish,直接导入即可
- DTO SetmealDto 直接导入即可
- Mapper接口SetmealDishMapper
- 业务层接口SetmealDishService
- 业务层实现类SetmealDishServiceImpl
- 控制层SetmealController
梳理交互过程
在开发代码之前,需要梳理一下新增套餐时前端页面和服务端的交互过程:
1、页面(backend/page/combo/add.html)发送ajax请求,请求服务端获取套餐分类数据并展示到下拉框中
2、页面发送ajax请求,请求服务端获取菜品分类数据并展示到添加菜品窗口中
3、页面发送ajax请求,请求服务端,根据菜品分类查询对应的菜品数据并展示到添加菜品窗口中
4、页面发送请求进行图片上传,请求服务端将图片保存到服务器
5、页面发送请求进行图片下载,将上传的图片进行回显
6、点击保存按钮,发送ajax请求,将套餐相关数据以json形式提交到服务端
开发新增套餐功能,其实就是在服务端编写代码去处理前端页面发送的这6次请求即可。
/** * 根据条件查询对应的菜品数据 * @param dish * @return */ @GetMapping("/list") public R<List<Dish>> list(Dish dish){ LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(dish.getCategoryId()!=null,Dish::getCategoryId,dish.getCategoryId()); //设置查询条件为状态为启用的 queryWrapper.eq(Dish::getStatus,1); queryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime); List<Dish> list = dishService.list(queryWrapper); return R.success(list); }
@Autowired private SetmealDishService setmealDishService; @Transactional @Override public void saveWithDish(SetmealDto setmealDto) { //保存套餐的基本信息 this.save(setmealDto); //保存套餐和菜品的关联信息 List<SetmealDish> setmealDishes = setmealDto.getSetmealDishes(); setmealDishes.stream().map((item)->{ item.setSetmealId(setmealDto.getId()); return item; }).collect(Collectors.toList()); setmealDishService.saveBatch(setmealDishes); }
controller
/** * 新增套餐 * @param setmealDto * @return */ @PostMapping public R<String> save(@RequestBody SetmealDto setmealDto){ setmealService.saveWithDish(setmealDto); return R.success("新建套餐成功"); }
套餐列表分页信息
在开发代码之前,需要梳理一下套餐分页查询时前端页面和服务端的交互过程:
1、页面(backend/page/combo/list.html)发送ajax请求,将分页查询参数(page.pageSize,name)提交到服务端,获取分页数据
2、页面发送请求,请求服务端进行图片下载,用于页面图片展示
开发套餐信息分页查询功能,其实就是在服务端编写代码去处理前端页面发送的这2次请求即可。
@GetMapping("/page") public R<Page> page(int page,int pageSize,String name){ Page<Setmeal> pageInfo = new Page<>(); Page<SetmealDto> setmealDtoPage=new Page<>(); LambdaQueryWrapper<Setmeal> queryWrapper=new LambdaQueryWrapper<>(); queryWrapper.like(name!=null,Setmeal::getName,name); queryWrapper.orderByDesc(Setmeal::getUpdateTime); setmealService.page(pageInfo,queryWrapper); BeanUtils.copyProperties(pageInfo,setmealDtoPage,"records"); List<Setmeal> records = pageInfo.getRecords(); List<SetmealDto> list= records.stream().map((item)->{ SetmealDto setmealDto = new SetmealDto(); BeanUtils.copyProperties(item,setmealDto); Long categoryId = item.getCategoryId(); Category category = categoryService.getById(categoryId); if(category!=null){ String name1 = category.getName(); setmealDto.setCategoryName(name1); } return setmealDto; }).collect(Collectors.toList()); setmealDtoPage.setRecords(list); return R.success(setmealDtoPage);
删除套餐
需求分析
删除套餐有批量删除和单个删除,我们可以用数组接收传过来的id,这样就可以使用一个来实现批量删除和单个删除的功能,还要注意的是,我们在删除套餐的时候还要删除与之对应的套餐,菜品关系表的相关信息,(我套餐都没了还要套餐菜品关系干啥)
手机验证码登录
阿里云短信服务-介绍
阿里云短信服务(Short Message Service)是广大企业客户快速触达手机用户所优选使用的通信能力。调用API或用群发助手,即可发送验证码、通知类和营销类短信;国内验证短信秒级触达,到达率最高可达99%;国际/港澳台短信覆盖200多个国家和地区,安全稳定,广受出海企业选用。
应用场景:
- 验证码
- 短信通知
- 推广短信
步骤
- 导入maven坐标
- 调用api
<dependency> <groupId>com.aliyun</ groupId> <artifactId>aliyun-java-sdk-core< / artifactId><version>4.5.16</version> < / dependency> <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-dysmsapi< / artifactId><version>2.1.0</version> </ dependency>
需求分析
为了方便用户登录,移动端通常都会提供通过手机验证码登录的功能。
手机验证码登录的优点:
- 方便快捷,无需注册,直接登录
- 使用短信验证码作为登录凭证,无需记忆密码
- 安全
登录流程:
输入手机号>获取验证码>输入验证码>点击登录>登录成功
注意:通过手机验证码登录,手机号是区分不同用户的标识。
数据模型
代码开发
流程梳理
在开发代码之前,需要梳理一下登录时前端页面和服务端的交互过程:
1、在登录页面(front/page/login.html)输入手机号,点击【获取验证码】按钮,页面发送ajax请求,在服务端调用短信服务API给指定手机号发送验证码短信
2、在登录页面输入验证码,点击【登录】按钮,发送ajax请求,在服务端处理登录请求
开发手机验证码登录功能,其实就是在服务端编写代码去处理前端页面发送的这2次请求即可。
地址簿
需求分析
地址簿,指的是移动端消费者用户的地址信息,用户登录成功后可以维护自己的地址信息。同一个用户可以有多个地址信息,但是只能有一个默认地址。
数据模型
导入功能代码
功能代码清单
- 实体类AddressBook(直接从课程资料中导入即可)
- Mapper接口AddressBookMapper
- 业务层接口AddressBookService
- 业务层实现类AddressBookServicelmpl
- 控制层AddressBookController (直接从课程资料中导入即可)
菜品展示
用户登录成功后跳转到系统首页,在首页需要根据分类来展示菜品和套餐。如果菜品设置了口味信息,需要展示
选择规格按钮,否则显示按钮。
代码开发
在开发代码之前,需要梳理一下前端页面和服务端的交互过程:
1、页面(front/index.html)发送ajax请求,获取分类数据(菜品分类和套餐分类)
2、页面发送ajax请求,获取第一个分类下的菜品或者套餐
开发菜品展示功能,其实就是在服务端编写代码去处理前端页面发送的这2次请求即可。
注意:首页加载完成后,还发送了一次ajax请求用于加载购物车数据,此处可以将这次请求的地址暂时修改一下,从静态json文件获取数据,等后续开发购物车功能时再修改回来,如下:
菜品展示的同时获得菜品的口味信息
@GetMapping("/list") public R<List<DishDto>> list(Dish dish){ LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(dish.getCategoryId()!=null,Dish::getCategoryId,dish.getCategoryId()); //设置查询条件为状态为启用的 queryWrapper.eq(Dish::getStatus,1); queryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime); List<Dish> list = dishService.list(queryWrapper); List<DishDto> dishDtoList = list.stream().map((item) -> { DishDto dishDto = new DishDto(); //拷贝Dish里面的属性到DishDto BeanUtils.copyProperties(item, dishDto); //查询到分类id Long categoryId = item.getCategoryId(); Category category = categoryService.getById(categoryId); if (category != null) { //通过分类id查询到分类名称 String categoryName = category.getName(); dishDto.setCategoryName(categoryName); } Long dishid = item.getId();//获取dishid //获取口味表 LambdaQueryWrapper<DishFlavor> dishFlavorLambdaQueryWrapper = new LambdaQueryWrapper<>(); dishFlavorLambdaQueryWrapper.eq(DishFlavor::getDishId,dishid); List<DishFlavor> dishFlavors = dishFlavorService.list(dishFlavorLambdaQueryWrapper); dishDto.setFlavors(dishFlavors); return dishDto; //最后通过collect收集成集合 }).collect(Collectors.toList()); return R.success(dishDtoList); }
查询套餐信息
@GetMapping("/list") public R<List<Setmeal>> listR(@RequestBody 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()); List<Setmeal> list = setmealService.list(queryWrapper); return R.success(list); }
购物车
需求分析
移动端用户可以将菜品或者套餐添加到购物车。对于菜品来说,如果设置了口味信息
则需要选择规格后才能
加入购物车;对于套餐来说,可以直接点击将当前套餐加入购物车。在购物车中可以修改菜品和套餐的数量,也可以清空购物车。
代码实现
1、点击[加入购物车]
或者[+]按钮,页面发送ajax请求,请求服务端,将菜品或者套餐添加到购物车
2、点击购物车图标,页面发送ajax请求,请求服务端查询购物车中的菜品和套餐
3、点击清空购物车按钮,页面发送ajax请求,请求服务端来执行清空购物车操作
开发购物车功能,其实就是在服务端编写代码去处理前端页面发送的这3次请求即可。
准备工作
在开发业务功能前,先将需要用到的类和接口基本结构创建好:
●实体类ShoppingCart(直接从课程资料中导入即可)
●Mapper接口ShoppingCartMapper
●业务层接口ShoppingCartService
●业务层实现类ShoppingCartServicelmpl
●控制层ShoppingCartController
controller
@PostMapping("/add") public R<ShoppingCart> add(@RequestBody Shopping //设置用户id Long currentId = BaseContext.getCurrentId(); shoppingCart.setUserId(currentId); //查询当前商品是否在购物车里面 Long dishId = shoppingCart.getDishId(); // LambdaQueryWrapper<ShoppingCart> queryWrappe queryWrapper.eq(ShoppingCart::getUserId, sho if (dishId != null) { //参加到购物车的是菜品 queryWrapper.eq(ShoppingCart::getDishId, } else { queryWrapper.eq(ShoppingCart::getSetmeal } ShoppingCart one = shoppingCartService.getOn if (one != null) { //如果已经存在只需要商品数量增加即可 Integer number = one.getNumber(); one.setNumber(number+1); shoppingCartService.updateById(one); } else { //如果不存在,则添加购物车,数量默认就是1 shoppingCart.setNumber(1); shoppingCartService.save(shoppingCart); one=shoppingCart; } //不存在则添加到购物车 return R.success(one); }
查看购物车
* @GetMapping("/list") public R<List<ShoppingCart>> listR() { 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); }
用户下单
需求分析
数据模型
用户下单业务对应的数据表为orders表和order_detail表:
- orders:订单表
- order_detail:订单明细表
在开发代码之前,需要梳理一下用户下单操作时前端页面和服务端的交互过程:1、在购物车中点击
去结算按钮,页面跳转到订单确认页面
2、在订单确认页面,发送ajax请求,请求服务端获取当前登录用户的默认地址
3、在订单确认页面,发送ajax请求,请求服务端获取当前登录用户的购物车数据
4、在订单确认页面点击去支付去支付按钮,发送ajax请求,请求服务端完成下单操作
开发用户下单功能,其实就是在服务端编写代码去处理前端页面发送的请求即可。
准备工作
在开发业务功能前,先将需要用到的类和接口基本结构创建好:
实体类Orders、OrderDetail (直接从课程资料中导入即可)
Mapper接口OrderMapper、OrderDetailMapper
业务层接口 OrderService、OrderDetailService
业务层实现类OrderServicelmpl、OrderDetailServicelmpl
控制层OrderController、OrderDetailController