外卖平台及移动端开发(移动端) 一、手机验证码登陆 1、短信服务介绍 阿里云短信服务:https://cn.aliyun.com/
2、处理业务逻辑 1. 在登录页面(front/page/login.html)输入手机号,点击[获取验证码]按钮,页面发送ajax请求,在服务端调用短信服务API给指定手机号发送验证码短信
2. 在登录页面输入验证码,点击[登录]按钮,发送ajax请求,在服务端处理登录请求
3、编码处理 3.1 准备工作 2.1.1 导入maven坐标 1 2 3 4 5 6 7 8 9 10 <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>
3.1.2 创建utils包并导入工具类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package com.itreggie.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;import com.zhenzi.sms.ZhenziSmsClient;import java.util.HashMap;import java.util.Map;public class SMSUtils { 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(); } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package com.itreggie.reggie.utils;import java.util.Random;public class ValidateCodeUtils { public static Integer generateValidateCode (int length) { Integer code = null ; if (length == 4 ){ code = new Random ().nextInt(9999 ); if (code < 1000 ){ code = code + 1000 ; } }else if (length == 6 ){ code = new Random ().nextInt(999999 ); if (code < 100000 ){ code = code + 100000 ; } }else { throw new RuntimeException ("只能生成4位或6位数字验证码" ); } return code; } public static String generateValidateCode4String (int length) { Random rdm = new Random (); String hash1 = Integer.toHexString(rdm.nextInt()); String capstr = hash1.substring(0 , length); return capstr; } }
3.2 创建所需的类和接口的基本结构
3.3 在全局拦截器LoginCheckFilter中添加代码 1 2 "/user/sendMsg" ,"/user/login"
1 2 3 4 5 6 7 8 9 10 if (request.getSession().getAttribute("user" ) != null ) { log.info("用户已登陆,用户id为:{}" , request.getSession().getAttribute("user" )); Long userId = (Long) request.getSession().getAttribute("user" ); BaseContext.setCurrentId(userId); chain.doFilter(request, response); return ; }
3.4 controller代码开发 在UserController控制层中,添加sendMsg方法和login方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 package com.itreggie.reggie.controller;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;import com.itreggie.reggie.common.R;import com.itreggie.reggie.entity.User;import com.itreggie.reggie.service.UserService;import com.itreggie.reggie.utils.ValidateCodeUtils;import lombok.extern.slf4j.Slf4j;import org.apache.commons.lang.StringUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpSession;import java.util.Map;@RestController @RequestMapping("/user") @Slf4j public class UserController { @Autowired private UserService userService; @PostMapping("/sendMsg") public R<String> sendMsg (@RequestBody User user,HttpSession session) { String phone = user.getPhone(); if (StringUtils.isNotEmpty(phone)){ String code = ValidateCodeUtils.generateValidateCode(4 ).toString(); log.info("code={}" ,code); session.setAttribute(phone,code); return R.success("手机验证码短信发送成功" ); } return R.error("手机验证码短信发送失败" ); } @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(); Object codeInSession = session.getAttribute(phone); 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("登录失败" ); } }
4、功能测试
重启项目,在用户登录页面输入手机号,和获取到的验证码,点击登录
页面成功跳转到服务用户界面
user表中成功添加上测试的手机号码(未注册的手机号码)
二、导入用户地址簿相关功能 1、数据模型 地址簿指移动端消费者用户的地址信息,用户登录成功后可以维护自己的地址信息。每一个用户可以有多个地址,但只能拥有一个默认地址 。用户的地址信息会存储在address_book表中。
2、编码处理 2.1 创建地址簿所需类和接口 实体类AddressBook,接口AddressBookMapper,service方法AddressBookService以及其实现类。
2.2 controller代码实现 2.2.1 新增地址 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package com.itreggie.reggie.controller;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;import com.itreggie.reggie.common.BaseContext;import com.itreggie.reggie.common.R;import com.itreggie.reggie.entity.AddressBook;import com.itreggie.reggie.service.AddressBookService;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;import java.util.List;@RestController @RequestMapping("/addressBook") @Slf4j 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); } }
2.2.2 设置默认地址 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @PutMapping("default") public R<AddressBook> setDefault (@RequestBody AddressBook addressBook) { log.info("addressBook: {}" ,addressBook); LambdaUpdateWrapper<AddressBook> queryWrapper = new LambdaUpdateWrapper <>(); queryWrapper.eq(AddressBook::getUserId,BaseContext.getCurrentId()); queryWrapper.set(AddressBook::getIsDefault,0 ); addressBookService.update(queryWrapper); addressBook.setIsDefault(1 ); addressBookService.updateById(addressBook); return R.success(addressBook); }
2.2.3 根据id查询地址 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @GetMapping("/{id}") public R get (@PathVariable Long id) { AddressBook addressBook = addressBookService.getById(id); if (addressBook != null ){ return R.success(addressBook); }else { return R.error("没有找到该对象" ); } }
2.2.4 查询默认地址 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @GetMapping("/default") public R<AddressBook> getDefault () { LambdaQueryWrapper<AddressBook> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(AddressBook::getUserId,BaseContext.getCurrentId()); queryWrapper.eq(AddressBook::getIsDefault,1 ); AddressBook addressBook = addressBookService.getOne(queryWrapper); if (null == addressBook){ return R.error("没有找到该对象" ); }else { return R.success(addressBook); } }
2.2.5 查询指定用户的全部地址 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @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); return R.success(addressBookService.list(queryWrapper)); }
2.2.6 修改地址 点击修改符号,发现回显信息已经写好了;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @PutMapping public R<String> update (@RequestBody AddressBook addressBook) { if (addressBook == null ){ return R.error("请求异常" ); } addressBookService.updateById(addressBook); return R.success("修改成功" ); }
2.2.7 删除地址 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @DeleteMapping public R<String> delete (@RequestParam("ids") Long id) { if (id == null ){ return R.error("请求异常" ); } LambdaQueryWrapper<AddressBook> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(AddressBook::getId,id).eq(AddressBook::getUserId,BaseContext.getCurrentId()); addressBookService.remove(queryWrapper); return R.success("删除地址成功" ); }
三、菜品展示 1、处理业务逻辑 1. 页面(front/index.html)发送ajax请求,获取分类数据(菜品分类和套餐分类)
1. 页面发送ajax请求,获取第一个分类下的菜品或套餐
注意: 首页加载完成之后,还发送了一条ajax请求用于加载购物车数据,此次请求的地址暂时修改下,从静态json文件获取数据,等后续开发购物车功能时在修改过来。
1 2 3 4 5 6 7 8 9 function cartListApi (data) { return $axios({ 'url' : '/front/cartData.json' , 'method' : 'get' , params: {...data} }) }
2、编码处理 在前面的DishController和SetmealController中,已实现分类方法list,所以只需对原有的list方法进行改进即可。移动端相比页面端展示多了一个口味数据展示,因此需要将dish转换为DishDto,借用DishDto中的flavors参数得到口味数据,进而展示到移动端。
2.1 DishController的list方法修改 原DishController的list方法为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @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); }
改进后的DishController的list方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 @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 (); BeanUtils.copyProperties(item,dishDto); Long categoryId = item.getCategoryId(); Category category = categoryService.getById(categoryId); if (category != null ){ String categoryName = category.getName(); dishDto.setCategoryName(categoryName); } Long dishId = item.getId(); LambdaQueryWrapper<DishFlavor> wrapper = new LambdaQueryWrapper <>(); wrapper.eq(DishFlavor::getDishId,dishId); List<DishFlavor> dishFlavorList = dishFlavorService.list(wrapper); dishDto.setFlavors(dishFlavorList); return dishDto; }).collect(Collectors.toList()); return R.success(dishDtoList); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package com.itreggie.reggie.dto;import com.itreggie.reggie.entity.Dish;import com.itreggie.reggie.entity.DishFlavor;import lombok.Data;import java.util.ArrayList;import java.util.List;@Data public class DishDto extends Dish { private List<DishFlavor> flavors = new ArrayList <>(); private String categoryName; private Integer copies; }
2.2 SetmealController中的list方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @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); }
注意:此处list数据传递的方式为键值对,因此对象不需要加针对json数据的@requestbody
3、点击套餐图片查看具体菜品 根据浏览器的请求数据,可以找到对应的axios请求以及前端展示所需的具体数据类型:
3.1 编码处理 在SetmealController中实现dish方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @GetMapping("/dish/{id}") public R<List<DishDto>> dish (@PathVariable("id") Long SetmealId) { LambdaQueryWrapper<SetmealDish> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(SetmealDish::getSetmealId,SetmealId); List<SetmealDish> list = setmealDishService.list(queryWrapper); List<DishDto> dishDtos = list.stream().map((setmealDish) -> { DishDto dishDto = new DishDto (); BeanUtils.copyProperties(setmealDish, dishDto); Long dishId = setmealDish.getDishId(); Dish dish = dishService.getById(dishId); BeanUtils.copyProperties(dish, dishDto); return dishDto; }).collect(Collectors.toList()); return R.success(dishDtos); }
四、购物车功能开发 1、数据模型 购物车对应的数据表为shopping_cart表
2、处理业务逻辑 1. 点击加入购物车或者“加号”按钮,页面发送ajax请求,请求服务端将菜品或套餐添加到购物车
1. 点击购物车图标,页面发送ajax请求,请求服务端查询购物车中的菜品和套餐
1. 点击清空购物车按钮,页面发送ajax请求,请求服务器执行清空购物车操作
开发购物车功能时需要把main.js中的请求地址改回来:
1 2 3 4 5 6 7 8 9 10 function cartListApi (data) { return $axios({ 'url' : '/shoppingCart/list' , 'method' : 'get' , params: {...data} }) }
3、编码处理 3.1 创建所需的类和接口 实体类ShoppingCart,接口 ShoppingCartMapper,业务层接口 ShoppingCartService及其实现类ShoppingCartServiceImpl。
3.2.1 添加购物车 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 package com.itreggie.reggie.controller;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;import com.itreggie.reggie.common.BaseContext;import com.itreggie.reggie.common.R;import com.itreggie.reggie.entity.ShoppingCart;import com.itreggie.reggie.service.ShoppingCartService;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.*;import java.time.LocalDateTime;import java.util.List;@RestController @RequestMapping("/shoppingCart") @Slf4j public class ShoppingCartController { @Autowired private ShoppingCartService shoppingCartService; @PostMapping("/add") public R<ShoppingCart> add (@RequestBody ShoppingCart shoppingCart) { log.info("购物车数据:{}" , shoppingCart); 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()); } ShoppingCart cartServiceOne = shoppingCartService.getOne(queryWrapper); if (cartServiceOne != null ) { Integer number = cartServiceOne.getNumber(); cartServiceOne.setNumber(number + 1 ); shoppingCartService.updateById(cartServiceOne); } else { shoppingCart.setNumber(1 ); shoppingCart.setCreateTime(LocalDateTime.now()); shoppingCartService.save(shoppingCart); cartServiceOne = shoppingCart; } return R.success(cartServiceOne); } }
3.2.2 删减购物车 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 @PostMapping("/sub") public R<ShoppingCart> sub (@RequestBody ShoppingCart shoppingCart) { log.info("购物车数据。。。" ); 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()); } ShoppingCart cartServiceOne = shoppingCartService.getOne(queryWrapper); if (cartServiceOne != null ) { Integer number = cartServiceOne.getNumber(); if (number > 1 ){ cartServiceOne.setNumber(number - 1 ); shoppingCartService.updateById(cartServiceOne); }else { shoppingCartService.remove(queryWrapper); } } return R.success(cartServiceOne); }
3.2.3 查看购物车 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @GetMapping("/list") public R<List<ShoppingCart>> list () { log.info("查看购物车..." ); 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); }
3.2.4 清空购物车 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @DeleteMapping("/clean") public R<String> clean () { LambdaQueryWrapper<ShoppingCart> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(ShoppingCart::getUserId, BaseContext.getCurrentId()); shoppingCartService.remove(queryWrapper); return R.success("清空购物车成功" ); }
五、用户下单 1、数据模型 用户下单业务对应的数据表为订单表orders表和订单明细表order_detail表。
订单表orders表
订单明细表order_detail表
2、处理业务逻辑 1. 在购物车中点击结算按钮,页面跳转至订单确认页面
1. 订单确认页面发送ajax请求,请求服务端获取当前登录用户的默认地址(如果未设置会跳转至新增地址页面)
1. 订单确认页面发送ajax请求,请求服务端获取当前登录用户的购物车数据
1. 点击支付按钮,发送ajax请求,请求服务端完成下单操作
3、编码处理 3.1 创建所需的类和接口 实体类orders、OrderDetail,Mapper接口,业务层OrderService、OrderDetailService以及其实现类。
3.2 业务实现层OrderServiceImpl 开发 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 package com.itreggie.reggie.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;import com.baomidou.mybatisplus.core.toolkit.IdWorker;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import com.itreggie.reggie.common.BaseContext;import com.itreggie.reggie.common.CustomException;import com.itreggie.reggie.entity.*;import com.itreggie.reggie.mapper.OrderMapper;import com.itreggie.reggie.service.*;import lombok.AllArgsConstructor;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;import java.math.BigDecimal;import java.time.LocalDateTime;import java.util.List;import java.util.concurrent.atomic.AtomicInteger;import java.util.stream.Collectors;@Service public class OrderServiceImpl extends ServiceImpl <OrderMapper, Orders> implements OrderService { @Autowired private ShoppingCartService shoppingCartService; @Autowired private UserService userService; @Autowired private AddressBookService addressBookService; @Autowired private OrderDetailService orderDetailService; @Transactional public void submit (Orders orders) { 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); } }
3.3 控制层OrderController开发 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package com.itreggie.reggie.controller;import com.itreggie.reggie.common.R;import com.itreggie.reggie.entity.Orders;import com.itreggie.reggie.service.OrderService;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@RestController @RequestMapping("/order") @Slf4j public class OrderController { @Autowired private OrderService orderService; @PostMapping("/submit") public R<String> submit (@RequestBody Orders orders) { log.info("订单数据:{}" ,orders); orderService.submit(orders); return R.success("下单成功" ); } }
六、用户查看订单 1、编码处理 在orderController中添加userPage方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @GetMapping("/userPage") public R<Page> page (int page, int pageSize) { Page<Orders> pageInfo = new Page <>(page,pageSize); LambdaQueryWrapper<Orders> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.orderByDesc(Orders::getOrderTime); orderService.page(pageInfo,queryWrapper); return R.success(pageInfo); }
2、功能完善 通过order.html这个页面(第40行)我们可以发现:前端还需要下面这些数据;所以我们后端要传给它
1 2 3 4 5 6 <div class="dishList" > <div v-for ="(item,index) in order.orderDetails" :key="index" class="item" > <span>{{item.name}}</span> <span>x{{item.number}}</span> </div> </div>
2.1 分析前端代码 item是从order.orderDetails里面 获取到的,但是orders实体类里面并没有orderDetails这个属性,而且数据库中这个order表里面也没有这个字段,所以使用的是dto来封装数据给前端。
2.2 创建OrderDto实体类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.itreggie.reggie.dto;import com.itreggie.reggie.entity.OrderDetail;import com.itreggie.reggie.entity.Orders;import lombok.Data;import java.util.List;@Data public class OrdersDto extends Orders { private String userName; private String phone; private String address; private String consignee; private List<OrderDetail> orderDetails; }
2.3 OrderController代码完善 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 public List<OrderDetail> getOrderDetailListByOrderId (Long orderId) { LambdaQueryWrapper<OrderDetail> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(OrderDetail::getOrderId, orderId); List<OrderDetail> orderDetailList = orderDetailService.list(queryWrapper); return orderDetailList; } @GetMapping("/userPage") public R<Page> page (int page, int pageSize) { Page<Orders> pageInfo = new Page <>(page,pageSize); Page<OrderDto> pageDto = new Page <>(page,pageSize); LambdaQueryWrapper<Orders> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(Orders::getUserId,BaseContext.getCurrentId()); queryWrapper.orderByDesc(Orders::getOrderTime); orderService.page(pageInfo,queryWrapper); LambdaQueryWrapper<OrderDetail> queryWrapper2 = new LambdaQueryWrapper <>(); List<Orders> records = pageInfo.getRecords(); List<OrderDto> orderDtoList = records.stream().map((item) ->{ OrderDto orderDto = new OrderDto (); Long orderId = item.getId(); List<OrderDetail> orderDetailList = this .getOrderDetailListByOrderId(orderId); BeanUtils.copyProperties(item,orderDto); orderDto.setOrderDetails(orderDetailList); return orderDto; }).collect(Collectors.toList()); BeanUtils.copyProperties(pageInfo,pageDto,"records" ); pageDto.setRecords(orderDtoList); return R.success(pageDto); }
七、再来一单功能 由于没有写后台的确认订单功能,所以这里通过数据库orders表修改订单状态来完成测试
先把数据库中的订单表中的status改一些为4:这样在前端才能点击这个再来一单的按钮:
在front/page/order.html中可以看到这一段代码:
1 2 3 <div class="btn" v-if ="order.status === 4" > <div class="btnAgain" @click ="addOrderAgain(order)" >再来一单</div> </div>
通过addOrderAgain这个方法:前端使用post请求,请求地址order/again:
1、编码处理 1.1 清空购物车功能完善 将ShoppingCartController中清空购物车clean方法写入service层,便于后续调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @DeleteMapping("/clean") public R<String> clean () { shoppingCartService.clean(); return R.success("清空购物车成功" ); }
将注释的代码封装到ShoppingCartService的clean方法中:
1 2 3 4 5 6 7 8 9 package com.itreggie.reggie.service;import com.baomidou.mybatisplus.extension.service.IService;import com.itreggie.reggie.entity.ShoppingCart;public interface ShoppingCartService extends IService <ShoppingCart> { public void clean () ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package com.itreggie.reggie.service.impl;import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import com.itreggie.reggie.common.BaseContext;import com.itreggie.reggie.common.R;import com.itreggie.reggie.entity.ShoppingCart;import com.itreggie.reggie.mapper.ShoppingCartMapper;import com.itreggie.reggie.service.ShoppingCartService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;@Service public class ShoppingCartServiceImpl extends ServiceImpl <ShoppingCartMapper, ShoppingCart> implements ShoppingCartService { @Autowired private ShoppingCartService shoppingCartService; @Override public void clean () { LambdaQueryWrapper<ShoppingCart> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(ShoppingCart::getUserId, BaseContext.getCurrentId()); shoppingCartService.remove(queryWrapper); } }
1.2 OrderController代码实现 - 前端点击再来一单会直接跳转至购物车,为避免数据有问题,在跳转之前需将购物车数据清除
- 通过orderId获取订单明细
- 将当前用户购物车表中的数据清除后将订单明细的数据放进购物车表中
ps:电商项目不能这么做
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 @PostMapping("/again") public R<String> againSubmit (@RequestBody Map<String,String> map) { String ids = map.get("id" ); long id = Long.parseLong(ids); LambdaQueryWrapper<OrderDetail> queryWrapper = new LambdaQueryWrapper <>(); queryWrapper.eq(OrderDetail::getOrderId,id); List<OrderDetail> orderDetailList = orderDetailService.list(queryWrapper); shoppingCartService.clean(); Long userId = BaseContext.getCurrentId(); List<ShoppingCart> shoppingCartList = orderDetailList.stream().map((item) -> { ShoppingCart shoppingCart = new ShoppingCart (); shoppingCart.setUserId(userId); shoppingCart.setImage(item.getImage()); Long dishId = item.getDishId(); Long setmealId = item.getSetmealId(); if (dishId != null ) { shoppingCart.setDishId(dishId); } else { shoppingCart.setSetmealId(setmealId); } shoppingCart.setName(item.getName()); shoppingCart.setDishFlavor(item.getDishFlavor()); shoppingCart.setNumber(item.getNumber()); shoppingCart.setAmount(item.getAmount()); shoppingCart.setCreateTime(LocalDateTime.now()); return shoppingCart; }).collect(Collectors.toList()); shoppingCartService.saveBatch(shoppingCartList); return R.success("操作成功" ); }
不建议把业务代码写在controller,不然以后想复用的时候就会很麻烦
八、移动端用户登出 1、编码处理 在UserController中实现loginOut方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 @PostMapping("/loginout") public R<String> logout (HttpServletRequest request) { request.getSession().removeAttribute("user" ); return R.success("退出成功" ); }