瑞吉外卖-day06

YangeIT大约 25 分钟瑞吉外卖地址簿模块菜品展示购物车下单

瑞吉外卖-day06

课程内容

  • 用户地址簿功能
  • 菜品展示
  • 购物车
  • 下单

1. 用户地址簿功能 🍐

1.1 需求分析

地址簿,指的是移动端消费者用户的地址信息,用户登录成功后可以维护自己的地址信息。同一个用户可以有多个地址信息,但是只能有一个默认地址

image-20210812191332892image-20210812191822693

对于地址簿管理,我们需要实现以下几个功能:

1.2 数据模型

用户的地址信息会存储在 address_book 表,即地址簿表中。具体表结构如下:

image-20210812192228678
image-20210812192228678

这里面有一个字段 is_default,实际上我们在设置默认地址时,只需要更新这个字段就可以了。

1.3 导入功能代码 ✏️ 👈

对于这一类的单表的增删改查,我们已经写过很多了,基本的开发思路都是一样的,那么本小节的用户地址簿管理的增删改查功能,我们就不再一一实现了,基本的代码我们都已经提供了,直接导入进来,做一个测试即可。

对于下面的地址管理的代码,我们可以直接从资料拷贝,也可以直接从下面的讲义中复制。

1). 实体类 AddressBook(直接从课程资料中导入即可)

所属包: com.itheima.reggie.entity

/**
 * 地址簿
 */
@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;
}














































 


保存修改和删除代码

1️⃣ 修改地址
2️⃣ 删除地址

所属类: com.itheima.reggie.controller.AddressBookController

点击查看代码

/**
* 修改
*/
@PutMapping
public R<AddressBook> update(@RequestBody AddressBook addressBook) {
    log.info("addressBook:{}", addressBook);
    addressBookService.updateById(addressBook);
    return R.success(addressBook);
}

//地址删除
@DeleteMapping
public R<String> deleteByIds(@RequestParam("ids") List<Long> ids){

    boolean isdelete = addressBookService.removeByIds(ids);
    return isdelete?R.success("删除成功"):R.error("删除失败");

}

1.4 功能测试

代码导入进来,并且去阅读了一下地址管理各个功能的逻辑实现,接下来,我们就可以启动项目,进行一个测试。测试过程中,通过 debug 断点调试观察服务端程序的执行过程,在浏览器中使用调试工具查看页面和服务端的交互过程和请求响应数据。

1). 新增

填写表单数据,点击保存地址,查看网络请求。

image-20210812201636567
image-20210812201636567

测试完毕之后,检查数据库中的数据,是否正常插入。

image-20210812201845336
image-20210812201845336

2. 菜品展示 ✏️

2.1 需求分析

用户登录成功后跳转到系统首页,在首页需要根据分类来展示菜品和套餐。如果菜品设置了口味信息,需要展示image-20210812205330291按钮,否则显示image-20210812205346846按钮。

image-20210812210328249
image-20210812210328249

2.2 前端页面分析

在开发代码之前,需要梳理一下前端页面和服务端的交互过程:

1). 页面(front/index.html)发送 ajax 请求,获取分类数据(菜品分类和套餐分类)

image-20210812215624794
image-20210812215624794

该功能在之前的业务开发中,我们都已经实现了。通过请求响应的数据,我们也可以看到数据是可以正确获取到的。

image-20210812221107947
image-20210812221107947

注意:首页加载时,不仅发送请求获取分类列表,还发送了一次 ajax 请求用于加载购物车数据,而这两次请求必须全部成功,页面才可以正常渲染,而当前购物车列表查询功能还未实现(报 404),所以列表目前并未渲染。此处可以将这次请求的地址暂时修改一下,从静态 json 文件获取数据,等后续开发购物车功能时再修改回来,如下:

image-20210812221835628

修改之后(idea maven 要执行 clean,浏览器清除缓存 ),我们再次测试:

image-20210812222713700
image-20210812222713700

目前该部分的功能我们已经调通,左侧的分类菜单,和右侧的菜品信息我们都可以看到,后续我们只需要将购物车列表的数据改成调用服务端接口查询即可。

经过上述的分析,我们可以看到,服务端我们主要提供两个方法,分别用来:

相关信息

请求说明
请求方式GET
请求路径/dish/list
请求参数?categoryId=1397844263642378242&status=1
请求说明
请求方式GET
请求路径/setmeal/list
请求参数?categoryId=1397844263642378242&status=1

2.3 代码开发 ✏️ 👈

2.3.1 查询菜品方法修改

提示

由于之前我们实现的根据分类查询菜品列表,仅仅查询了菜品的基本信息,未查询菜品口味信息,而移动端用户在点餐时,是需要选择口味信息的,所以我们需要对之前的代码实现进行完善,那么如何完善呢?

我们需要修改 DishController 的 list 方法,原来此方法的返回值类型为:R<List<Dish>>。为了满足移动端对数据的要求(菜品基本信息和菜品对应的口味信息),现在需要将方法的返回值类型改为:R<List<DishDto>> ,因为在 DishDto 中封装了菜品对应的口味信息:

image-20210812231825043

代码逻辑:

A. 根据分类 ID 查询,查询目前正在启售的菜品列表 (已实现)

B. 遍历菜品列表,并查询菜品的分类信息及菜品的口味列表

C. 组装数据 DishDto,并返回

代码实现:

类:com.itheima.reggie.controller.DishController

@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).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();//分类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);
    }












 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 



2.3.2 根据分类 ID 查询套餐

在 SetmealController 中创建 list 方法,根据条件查询套餐数据。

类:com.itheima.reggie.controller.SetmealController

/**
 * 根据条件查询套餐数据
 * @param setmeal
 * @return
 */
@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);
}

2.4 功能测试

把菜品展示的功能代码完善完成之后,我们重新启动服务,来测试一个菜品展示的功能。测试过程中可以使用浏览器的监控工具查看页面和服务端的数据交互细节。

image-20210813175554516
image-20210813175554516

点击分类,根据分类查询菜品列表/套餐列表:

image-20210813175835304
image-20210813175835304

3. 购物车

3.1 需求分析

提示

移动端用户可以将菜品或者套餐添加到购物车。对于菜品来说,如果设置了口味信息,则需要选择规格后才能加入购物车;对于套餐来说,可以直接点击image-20210813181916235将当前套餐加入购物车。在购物车中可以修改菜品和套餐的数量,也可以清空购物车。

image-20210813182828045
image-20210813182828045

这里面我们需要实现的功能包括:

1). 添加购物车

2). 查询购物车

3). 清空购物车

3.2 数据模型

用户的购物车数据,也是需要保存在数据库中的,购物车对应的数据表为 ,具体表结构如下:

image-20210813183334933

说明:

  • ,每一个用户的购物车数据是哪些
  • 菜品列表展示出来的既有套餐,又有菜品,如果 APP 端选择的是,就保存,如果 APP 端选择的是,就保存
  • ,如果选择多份不需要添加多条记录,增加数量 number 即可

最终 shopping_cart 表中存储的数据示例:

image-20210815183440051

3.3 前端页面分析

在开发代码之前,需要梳理一下购物车操作时前端页面和服务端的交互过程:

1). 点击 "加入购物车" 或者 "+" 按钮,页面发送 ajax 请求,请求服务端,将菜品或者套餐添加到购物车

image-20210813185414102
image-20210813185414102
image-20210813185731809
image-20210813185731809

经过上述的分析,我们可以看到,对于购物车的功能,我们主要需要开发以下几个功能,具体的请求信息如下:

相关信息

1). 加入购物车 👈

请求说明
请求方式POST
请求路径/shoppingCart/add
请求参数json 格式
菜品数据:
{"amount":118,"dishFlavor":"不要蒜,微辣","dishId":"1397851099502260226","name":"全家福","image":"a53a4e6a-3b83-4044-87f9-9d49b30a8fdc.jpg"}

套餐数据:
{"amount":38,"setmealId":"1423329486060957698","name":"营养超值工作餐","image":"9cd7a80a-da54-4f46-bf33-af3576514cec.jpg"}

2). 查询购物车列表 👈

请求说明
请求方式GET
请求路径/shoppingCart/list

3). 清空购物车功能 👈

请求说明
请求方式DELETE
请求路径/shoppingCart/clean

3.4 准备工作

分析完毕购物车的业务需求和实现思路之后,在开发业务功能前,先将需要用到的类和接口基本结构创建好:

1). 实体类 ShoppingCart(直接从课程资料中导入即可)

所属包: com.itheima.reggie.entity

//购物车
@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;
}

3.5 代码开发 ✏️ 👈

3.5.1 添加购物车

提示

在 ShoppingCartController 中创建 ,来完成添加购物车的逻辑实现,具体的逻辑如下:

  1. 获取当前登录用户,为购物车对象赋值
  2. 根据当前登录用户 ID 及 本次添加的菜品 ID/套餐 ID,查询购物车数据是否存在
  3. 如果已经存在,就在原来数量基础上加 1
  4. 如果不存在,则添加到购物车,数量默认就是 1

代码实现如下:

点击查看代码
/**
* 添加购物车
* @param shoppingCart
* @return
*/
@PostMapping("/add")
public R<ShoppingCart> add(@RequestBody ShoppingCart shoppingCart){
    log.info("购物车数据:{}",shoppingCart);

    //设置用户id,指定当前是哪个用户的购物车数据
    Long currentId = BaseContext.getCurrentId();
    shoppingCart.setUserId(currentId);
    //菜品id
    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{
        //如果不存在,则添加到购物车,数量默认就是一
        shoppingCart.setNumber(1);
        shoppingCart.setCreateTime(LocalDateTime.now());
        shoppingCartService.save(shoppingCart);
        cartServiceOne = shoppingCart;
    }
    return R.success(cartServiceOne);
}

3.5.2 查询购物车

提示

在 ShoppingCartController 中创建 ,根据当前登录用户 ID 查询购物车列表,并对查询的结果进行

代码实现如下:

点击查看代码
/**
* 查看购物车
* @return
*/
@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.5.3 清空购物车

提示

在 ShoppingCartController 中创建 ,在方法中获取当前登录用户,根据登录用户 ID,删除购物车数据。

代码实现如下:

点击查看代码
/**
* 清空购物车
* @return
*/
@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("清空购物车成功");
}

3.6 功能测试

按照前面分析的操作流程进行测试,测试功能以及数据库中的数据是否是否正常。

1). 添加购物车

当添加的是菜品信息,而这个用户的购物车中当前并没有这个菜品时,添加一条数据,数量为 1。

image-20210814070930745

检查数据库数据,由于是菜品保存的是 dish_id。

image-20210814071113382
image-20210814071113382

这时在页面上,我们可以继续点击+号,在购物车中增加该菜品,此时,应该是对现有的购物车菜品数量加 1,而不应该插入新的记录。

image-20210814071613706

检查数据库数据:

image-20210814071707767
image-20210814071707767

如果添加的是套餐,该套餐在当前用户的购物车中并不存在,则添加一条数据,数量为 1。

image-20210814071742125

检查数据库数据:

image-20210814071850689
image-20210814071850689

4. 下单

4.1 需求分析

相关信息

移动端用户将菜品或者套餐加入购物车后,可以点击购物车中的 按钮,页面跳转到订单确认页面,点击 按钮则完成下单操作。

image-20210814072533469

这里,我们需要说明一下,这里并不会去开发支付功能,因为不论是支付宝的支付,还是微信支付,都是需要的,而我们大家在测试的时候,是没有办法提供企业资质的,所以这一部分支付功能我们就不去实现了。

4.2 数据模型

用户下单业务对应的数据表为 orders 表和 order_detail 表():

表名含义说明
orders订单表主要存储订单的基本信息(如: 订单号、状态、金额、支付方式、下单用户、收件地址等)
order_detail订单明细表主要存储订单详情信息(如: 该订单关联的套餐及菜品的信息)

具体的表结构如下:

A. orders 订单表

image-20210814095559935
image-20210814095559935

数据示例:

image-20210815224918077
image-20210815224918077

用户提交订单时,需要往订单表 orders 中插入,并且需要往

4.3 前端页面分析

在开发代码之前,需要梳理一下用户下单操作时前端页面和服务端的交互过程:

1). 在购物车中点击image-20210814073907767按钮,页面跳转到订单确认页面

image-20210814075105094

页面跳转前端已经完成,我们无需操作。

经过上述的分析,我们看到前三步的功能我们都已经实现了,我们主要需要实现最后一步的,该功能具体的请求信息如下:

提示

请求说明
请求方式POST
请求路径/order/submit
请求参数{"remark":"老板,记得带一次性筷子","payMethod":1,"addressBookId":"1425792459560005634"}

4.4 准备工作

在开发业务功能前,先将需要用到的类和接口基本结构创建好:

1). 实体类 Orders、OrderDetail(直接从课程资料中导入即可)

所属包: com.itheima.reggie.entity


/**
 * 订单
 */
@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;
}
/**
 * 订单明细
 */
@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;
}

4.5 代码开发 ✏️ 👈

,处理用户下单的逻辑 :

/**
 * 用户下单
 * @param orders
 * @return
 */
@PostMapping("/submit")
public R<String> submit(@RequestBody Orders orders){
    log.info("订单数据:{}",orders);
    orderService.submit(orders);
    return R.success("下单成功");
}

由于下单的逻辑相对复杂,我们可以在 OrderService 中定义 submit 方法,来处理下单的具体逻辑:

/**
* 用户下单
* @param orders
*/
public void submit(Orders orders);

然后在 OrderServiceImpl 中完成下单功能的具体实现,下单功能的具体逻辑如下:

提示

  • A. 获得当前用户 id, 查询当前用户的购物车数据
  • B. 根据当前登录用户 id, 查询用户数据
  • C. 根据地址 ID, 查询地址数据
  • D. 组装订单明细数据, 批量保存订单明细
  • E. 组装订单数据, 批量保存订单数据
  • F. 删除当前用户的购物车列表数据

具体代码实现如下:

@Autowired
private ShoppingCartService shoppingCartService;

@Autowired
private UserService userService;

@Autowired
private AddressBookService addressBookService;

@Autowired
private OrderDetailService orderDetailService;

/**
 * 用户下单
 * @param orders
 */
@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();//订单号

        //订单的实收金额
        final int[] amount = {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());

        int permoney=s.getAmount().intValue()*s.getNumber();
        amount[0] = amount[0] +permoney;

        return orderDetail;
        }).collect(Collectors.toList());

        //组装订单数据
        orders.setId(orderId);
        orders.setOrderTime(LocalDateTime.now());
        orders.setCheckoutTime(LocalDateTime.now());
        orders.setStatus(2);
        //todo        金额是等于:订单详情表之和
        orders.setAmount(new BigDecimal(amount[0]));
        orders.setUserId(userId);
        orders.setNumber(String.valueOf(orderId));
        orders.setUserName(user.getName());
        orders.setConsignee(addressBook.getConsignee());
        orders.setPhone(addressBook.getPhone());
        orders.setAddress(addressBook.getDetail());
        //向订单表插入数据,一条数据
        this.save(orders);

        //向订单明细表插入数据,多条数据
        orderDetailService.saveBatch(orderDetails);

        //清空购物车数据
        shoppingCartService.remove(wrapper);
        }

备注

上述逻辑处理中,计算购物车商品的总金额时,为保证我们每一次执行的累加计算是一个原子操作,我们这里用到了 JDK 中提供的一个原子类 AtomicInteger

AtomicInteger amount = new AtomicInteger(0);

//组装订单明细信息
amount.addAndGet(item.getAmount().multiply(new BigDecimal(item.getNumber())).intValue());

 //组装订单数据
orders.setAmount(new BigDecimal(amount.get()));//总金额

原子类 demo


public class AtomicDemo {
    public static void main(String[] args) {

        //原子类
        AtomicInteger atomicInteger = new AtomicInteger(0);

        //橙子每个的价格2块
        BigDecimal bigDecimal = new BigDecimal(2);
        //每次买10个
        BigDecimal bigDecimal2 = new BigDecimal(10);

        //总共买3次
        for (int i = 0; i < 3; i++) {
            atomicInteger.addAndGet(bigDecimal.multiply(bigDecimal2).intValue());

        }

        // 橙子总共多少钱
        System.out.println(atomicInteger.get());
    }
}
点击查看原子类后修改代码
@Autowired
private ShoppingCartService shoppingCartService;

@Autowired
private UserService userService;

@Autowired
private AddressBookService addressBookService;

@Autowired
private OrderDetailService orderDetailService;

/**
* 用户下单
* @param orders
*/
@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((orders.setAddress(addressBook.getDetail());
    //向订单表插入数据,一条数据
    this.save(orders);

    //向订单明细表插入数据,多条数据
    orderDetailService.saveBatch(orderDetails);

    //清空购物车数据
    shoppingCartService.remove(wrapper);
}











































 












 








 
















4.6 功能测试

代码编写完成,我们重新启动服务,按照前面分析的操作流程进行测试,查看数据是否正常即可。在测试过程中,我们可以通过 debug 的形式来跟踪代码的正常执行。

image-20210814084822573

检查数据库数据

订单表插入一条记录:

image-20210814084925524
image-20210814084925524

订单明细表插入四条记录():

image-20210814085019401
image-20210814085019401

同时,购物车的数据被删除:

image-20210814085058814
image-20210814085058814

流程图绘制open in new window