【瑞吉外卖】day09:用户地址簿功能、菜品展示、购物车、下单(三)

简介: 【瑞吉外卖】day09:用户地址簿功能、菜品展示、购物车、下单

3.5 代码开发


3.5.1 添加购物车


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

A. 获取当前登录用户,为购物车对象赋值

B. 根据当前登录用户ID 及 本次添加的菜品ID/套餐ID,查询购物车数据是否存在

C. 如果已经存在,就在原来数量基础上加1

D. 如果不存在,则添加到购物车,数量默认就是1

代码实现如下:

<spanstyle="background-color:#f8f8f8"><spanstyle="color:#333333"><spanstyle="color:#aa5500">/**</span><span style="color:#aa5500">* 添加购物车</span><span style="color:#aa5500">* @param shoppingCart</span><span style="color:#aa5500">* @return</span><span style="color:#aa5500">*/</span><spanstyle="color:#555555">@PostMapping</span>(<spanstyle="color:#aa1111">"/add"</span>)
<spanstyle="color:#770088">public</span><spanstyle="color:#000000">R</span><spanstyle="color:#981a1a"><</span><spanstyle="color:#000000">ShoppingCart</span><spanstyle="color:#981a1a">></span><spanstyle="color:#0000ff">add</span>(<spanstyle="color:#555555">@RequestBody</span><spanstyle="color:#000000">ShoppingCart</span><spanstyle="color:#000000">shoppingCart</span>){
<spanstyle="color:#000000">log</span>.<spanstyle="color:#000000">info</span>(<spanstyle="color:#aa1111">"购物车数据:{}"</span>,<spanstyle="color:#000000">shoppingCart</span>);
<spanstyle="color:#aa5500">//设置用户id,指定当前是哪个用户的购物车数据</span><spanstyle="color:#008855">Long</span><spanstyle="color:#000000">currentId</span><spanstyle="color:#981a1a">=</span><spanstyle="color:#000000">BaseContext</span>.<spanstyle="color:#000000">getCurrentId</span>();
<spanstyle="color:#000000">shoppingCart</span>.<spanstyle="color:#000000">setUserId</span>(<spanstyle="color:#000000">currentId</span>);
<spanstyle="color:#008855">Long</span><spanstyle="color:#000000">dishId</span><spanstyle="color:#981a1a">=</span><spanstyle="color:#000000">shoppingCart</span>.<spanstyle="color:#000000">getDishId</span>();
<spanstyle="color:#000000">LambdaQueryWrapper</span><spanstyle="color:#981a1a"><</span><spanstyle="color:#000000">ShoppingCart</span><spanstyle="color:#981a1a">></span><spanstyle="color:#000000">queryWrapper</span><spanstyle="color:#981a1a">=</span><spanstyle="color:#770088">new</span><spanstyle="color:#000000">LambdaQueryWrapper</span><spanstyle="color:#981a1a"><></span>();
<spanstyle="color:#000000">queryWrapper</span>.<spanstyle="color:#000000">eq</span>(<spanstyle="color:#000000">ShoppingCart</span>::<spanstyle="color:#000000">getUserId</span>,<spanstyle="color:#000000">currentId</span>);
<spanstyle="color:#770088">if</span>(<spanstyle="color:#000000">dishId</span><spanstyle="color:#981a1a">!=</span><spanstyle="color:#221199">null</span>){
<spanstyle="color:#aa5500">//添加到购物车的是菜品</span><spanstyle="color:#000000">queryWrapper</span>.<spanstyle="color:#000000">eq</span>(<spanstyle="color:#000000">ShoppingCart</span>::<spanstyle="color:#000000">getDishId</span>,<spanstyle="color:#000000">dishId</span>);
    }<spanstyle="color:#770088">else</span>{
<spanstyle="color:#aa5500">//添加到购物车的是套餐</span><spanstyle="color:#000000">queryWrapper</span>.<spanstyle="color:#000000">eq</span>(<spanstyle="color:#000000">ShoppingCart</span>::<spanstyle="color:#000000">getSetmealId</span>,<spanstyle="color:#000000">shoppingCart</span>.<spanstyle="color:#000000">getSetmealId</span>());
    }
<spanstyle="color:#aa5500">//查询当前菜品或者套餐是否在购物车中</span><spanstyle="color:#aa5500">//SQL:select * from shopping_cart where user_id = ? and dish_id/setmeal_id = ?</span><spanstyle="color:#000000">ShoppingCart</span><spanstyle="color:#000000">cartServiceOne</span><spanstyle="color:#981a1a">=</span><spanstyle="color:#000000">shoppingCartService</span>.<spanstyle="color:#000000">getOne</span>(<spanstyle="color:#000000">queryWrapper</span>);
<spanstyle="color:#770088">if</span>(<spanstyle="color:#000000">cartServiceOne</span><spanstyle="color:#981a1a">!=</span><spanstyle="color:#221199">null</span>){
<spanstyle="color:#aa5500">//如果已经存在,就在原来数量基础上加一</span><spanstyle="color:#008855">Integer</span><spanstyle="color:#000000">number</span><spanstyle="color:#981a1a">=</span><spanstyle="color:#000000">cartServiceOne</span>.<spanstyle="color:#000000">getNumber</span>();
<spanstyle="color:#000000">cartServiceOne</span>.<spanstyle="color:#000000">setNumber</span>(<spanstyle="color:#000000">number</span><spanstyle="color:#981a1a">+</span><spanstyle="color:#116644">1</span>);
<spanstyle="color:#000000">shoppingCartService</span>.<spanstyle="color:#000000">updateById</span>(<spanstyle="color:#000000">cartServiceOne</span>);
    }<spanstyle="color:#770088">else</span>{
<spanstyle="color:#aa5500">//如果不存在,则添加到购物车,数量默认就是一</span><spanstyle="color:#000000">shoppingCart</span>.<spanstyle="color:#000000">setNumber</span>(<spanstyle="color:#116644">1</span>);
<spanstyle="color:#000000">shoppingCart</span>.<spanstyle="color:#000000">setCreateTime</span>(<spanstyle="color:#000000">LocalDateTime</span>.<spanstyle="color:#000000">now</span>());
<spanstyle="color:#000000">shoppingCartService</span>.<spanstyle="color:#000000">save</span>(<spanstyle="color:#000000">shoppingCart</span>);
<spanstyle="color:#000000">cartServiceOne</span><spanstyle="color:#981a1a">=</span><spanstyle="color:#000000">shoppingCart</span>;
    }
<spanstyle="color:#770088">return</span><spanstyle="color:#000000">R</span>.<spanstyle="color:#000000">success</span>(<spanstyle="color:#000000">cartServiceOne</span>);
}</span></span>

3.5.2 查询购物车


在ShoppingCartController中创建list方法,根据当前登录用户ID查询购物车列表,并对查询的结果进行创建时间的倒序排序。

代码实现如下:

<spanstyle="background-color:#f8f8f8"><spanstyle="color:#333333">/*** 查看购物车* @return*/@GetMapping("/list")
publicR<List<ShoppingCart>>list(){
log.info("查看购物车...");
LambdaQueryWrapper<ShoppingCart>queryWrapper=newLambdaQueryWrapper<>();
queryWrapper.eq(ShoppingCart::getUserId,BaseContext.getCurrentId());
queryWrapper.orderByAsc(ShoppingCart::getCreateTime);
List<ShoppingCart>list=shoppingCartService.list(queryWrapper);
returnR.success(list);
}</span></span>

3.5.3 清空购物车


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

代码实现如下:

<spanstyle="background-color:#f8f8f8"><spanstyle="color:#333333">/*** 清空购物车* @return*/@DeleteMapping("/clean")
publicR<String>clean(){
//SQL:delete from shopping_cart where user_id = ?LambdaQueryWrapper<ShoppingCart>queryWrapper=newLambdaQueryWrapper<>();
queryWrapper.eq(ShoppingCart::getUserId,BaseContext.getCurrentId());
shoppingCartService.remove(queryWrapper);
returnR.success("清空购物车成功");
}</span></span>

3.6 功能测试


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

1). 添加购物车

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

image.png

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

image.png

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

image.png

检查数据库数据:

image.png

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

image.png

检查数据库数据:

image.png

2). 查看购物车

点击页面下面的购物车边栏,查看购物车数据列表是否正常展示。

image.png

3). 清空购物车

在购物车列表展示页中点击"清空", 查看购物车是否被清空。

image.png

并检查数据库中的数据,可以看到数据已经被删除。

4. 下单


4.1 需求分析


移动端用户将菜品或者套餐加入购物车后,可以点击购物车中的 "去结算" 按钮,页面跳转到订单确认页面,点击 "去支付" 按钮则完成

image.png

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

4.2 数据模型


用户下单业务对应的数据表为orders表和order_detail表(一对多关系,一个订单关联多个订单明细):

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

具体的表结构如下:

A. orders 订单表

image.png

image.png

image.png

4.3 前端页面分析


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

image.png

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

image.png

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

2). 在订单确认页面,发送ajax请求,请求服务端获取当前登录用户的默认地址

image.png

3). 在订单确认页面,发送ajax请求,请求服务端获取当前登录用户的购物车数据

image.png

4). 在订单确认页面点击按钮,发送ajax请求,请求服务端完成下单操作

image.png

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

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

4.4 准备工作


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

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

所属包: com.itheima.reggie.entity

<spanstyle="background-color:#f8f8f8"><spanstyle="color:#333333">importlombok.Data;
importjava.io.Serializable;
importjava.math.BigDecimal;
importjava.time.LocalDateTime;
/*** 订单*/@DatapublicclassOrdersimplementsSerializable {
privatestaticfinallongserialVersionUID=1L;
privateLongid;
//订单号privateStringnumber;
//订单状态 1待付款,2待派送,3已派送,4已完成,5已取消privateIntegerstatus;
//下单用户idprivateLonguserId;
//地址idprivateLongaddressBookId;
//下单时间privateLocalDateTimeorderTime;
//结账时间privateLocalDateTimecheckoutTime;
//支付方式 1微信,2支付宝privateIntegerpayMethod;
//实收金额privateBigDecimalamount;
//备注privateStringremark;
//用户名privateStringuserName;
//手机号privateStringphone;
//地址privateStringaddress;
//收货人privateStringconsignee;
}</span></span>
<spanstyle="background-color:#f8f8f8"><spanstyle="color:#333333">importlombok.Data;
importjava.io.Serializable;
importjava.math.BigDecimal;
/*** 订单明细*/@DatapublicclassOrderDetailimplementsSerializable {
privatestaticfinallongserialVersionUID=1L;
privateLongid;
//名称privateStringname;
//订单idprivateLongorderId;
//菜品idprivateLongdishId;
//套餐idprivateLongsetmealId;
//口味privateStringdishFlavor;
//数量privateIntegernumber;
//金额privateBigDecimalamount;
//图片privateStringimage;
}</span></span>

2). Mapper接口 OrderMapper、OrderDetailMapper

所属包: com.itheima.reggie.mapper

<spanstyle="background-color:#f8f8f8"><spanstyle="color:#333333">importcom.baomidou.mybatisplus.core.mapper.BaseMapper;
importcom.itheima.reggie.entity.Orders;
importorg.apache.ibatis.annotations.Mapper;
@MapperpublicinterfaceOrderMapperextendsBaseMapper<Orders> {
}</span></span>


<spanstyle="background-color:#f8f8f8"><spanstyle="color:#333333">importcom.baomidou.mybatisplus.core.mapper.BaseMapper;
importcom.itheima.reggie.entity.OrderDetail;
importorg.apache.ibatis.annotations.Mapper;
@MapperpublicinterfaceOrderDetailMapperextendsBaseMapper<OrderDetail> {
}</span></span>

3). 业务层接口 OrderService、OrderDetailService

所属包: com.itheima.reggie.service

<spanstyle="background-color:#f8f8f8"><spanstyle="color:#333333">importcom.baomidou.mybatisplus.extension.service.IService;
importcom.itheima.reggie.entity.Orders;
publicinterfaceOrderServiceextendsIService<Orders> {
}</span></span><spanstyle="background-color:#f8f8f8"><spanstyle="color:#333333">importcom.baomidou.mybatisplus.extension.service.IService;
importcom.itheima.reggie.entity.OrderDetail;
publicinterfaceOrderDetailServiceextendsIService<OrderDetail> {
}</span></span>

4). 业务层实现类 OrderServiceImpl、OrderDetailServiceImpl

所属包: com.itheima.reggie.service.impl

<spanstyle="background-color:#f8f8f8"><spanstyle="color:#333333">importcom.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
importcom.itheima.reggie.entity.*;
importcom.itheima.reggie.mapper.OrderMapper;
importcom.itheima.reggie.service.*;
importlombok.extern.slf4j.Slf4j;
importorg.springframework.stereotype.Service;
@Service@Slf4jpublicclassOrderServiceImplextendsServiceImpl<OrderMapper, Orders>implementsOrderService {
}</span></span><spanstyle="background-color:#f8f8f8"><spanstyle="color:#333333">importcom.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
importcom.itheima.reggie.entity.OrderDetail;
importcom.itheima.reggie.mapper.OrderDetailMapper;
importcom.itheima.reggie.service.OrderDetailService;
importorg.springframework.stereotype.Service;
@ServicepublicclassOrderDetailServiceImplextendsServiceImpl<OrderDetailMapper, OrderDetail>implementsOrderDetailService {
}</span></span>

5). 控制层 OrderController、OrderDetailController

所属包: com.itheima.reggie.controller

<spanstyle="background-color:#f8f8f8"><spanstyle="color:#333333">importcom.itheima.reggie.service.OrderService;
importlombok.extern.slf4j.Slf4j;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.web.bind.annotation.RequestMapping;
importorg.springframework.web.bind.annotation.RestController;
/*** 订单*/@Slf4j@RestController@RequestMapping("/order")
publicclassOrderController {
@AutowiredprivateOrderServiceorderService;
}</span></span><spanstyle="background-color:#f8f8f8"><spanstyle="color:#333333">importcom.itheima.reggie.service.OrderDetailService;
importlombok.extern.slf4j.Slf4j;
importorg.springframework.beans.factory.annotation.Autowired;
importorg.springframework.web.bind.annotation.*;
/*** 订单明细*/@Slf4j@RestController@RequestMapping("/orderDetail")
publicclassOrderDetailController {
@AutowiredprivateOrderDetailServiceorderDetailService;
}</span></span>

4.5 代码开发


在OrderController中创建submit方法,处理用户下单的逻辑 :

<spanstyle="background-color:#f8f8f8"><spanstyle="color:#333333">/*** 用户下单* @param orders* @return*/@PostMapping("/submit")
publicR<String>submit(@RequestBodyOrdersorders){
log.info("订单数据:{}",orders);
orderService.submit(orders);
returnR.success("下单成功");
}</span></span>

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

<spanstyle="background-color:#f8f8f8"><spanstyle="color:#333333">/*** 用户下单* @param orders*/publicvoidsubmit(Ordersorders);</span></span>

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


A. 获得当前用户id, 查询当前用户的购物车数据


B. 根据当前登录用户id, 查询用户数据


C. 根据地址ID, 查询地址数据


D. 组装订单明细数据, 批量保存订单明细


E. 组装订单数据, 批量保存订单数据


F. 删除当前用户的购物车列表数据


具体代码实现如下:

<spanstyle="background-color:#f8f8f8"><spanstyle="color:#333333">@AutowiredprivateShoppingCartServiceshoppingCartService;
@AutowiredprivateUserServiceuserService;
@AutowiredprivateAddressBookServiceaddressBookService;
@AutowiredprivateOrderDetailServiceorderDetailService;
/*** 用户下单* @param orders*/@Transactionalpublicvoidsubmit(Ordersorders) {
//获得当前用户idLonguserId=BaseContext.getCurrentId();
//查询当前用户的购物车数据LambdaQueryWrapper<ShoppingCart>wrapper=newLambdaQueryWrapper<>();
wrapper.eq(ShoppingCart::getUserId,userId);
List<ShoppingCart>shoppingCarts=shoppingCartService.list(wrapper);
if(shoppingCarts==null||shoppingCarts.size() ==0){
thrownewCustomException("购物车为空,不能下单");
    }
//查询用户数据Useruser=userService.getById(userId);
//查询地址数据LongaddressBookId=orders.getAddressBookId();
AddressBookaddressBook=addressBookService.getById(addressBookId);
if(addressBook==null){
thrownewCustomException("用户地址信息有误,不能下单");
    }
longorderId=IdWorker.getId();//订单号AtomicIntegeramount=newAtomicInteger(0);
//组装订单明细信息List<OrderDetail>orderDetails=shoppingCarts.stream().map((item) -> {
OrderDetailorderDetail=newOrderDetail();
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(newBigDecimal(item.getNumber())).intValue());
returnorderDetail;
    }).collect(Collectors.toList());
//组装订单数据orders.setId(orderId);
orders.setOrderTime(LocalDateTime.now());
orders.setCheckoutTime(LocalDateTime.now());
orders.setStatus(2);
orders.setAmount(newBigDecimal(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);
}</span></span>

备注:

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

4.6 功能测试


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

image.png

检查数据库数据

订单表插入一条记录:

image.png

image.png

相关文章
|
5月前
|
前端开发 数据库
电商购物商城项目商品详情页设置
电商购物商城项目商品详情页设置
|
前端开发 容器
从零玩转系列之微信支付实战PC端装修下单页面2
从零玩转系列之微信支付实战PC端装修下单页面
80 0
|
SQL JSON 前端开发
加入购物车【项目 商城】
加入购物车【项目 商城】
54 0
|
存储 JSON 前端开发
从零玩转系列之微信支付实战PC端装修我的订单页面2
从零玩转系列之微信支付实战PC端装修我的订单页面
120 0
|
前端开发 小程序 安全
从零玩转系列之微信支付实战PC端装修我的订单页面1
从零玩转系列之微信支付实战PC端装修我的订单页面
92 0
|
JavaScript
从零玩转系列之微信支付实战PC端装修下单页面3
从零玩转系列之微信支付实战PC端装修下单页面
42 0
|
JavaScript 前端开发 安全
从零玩转系列之微信支付实战PC端装修下单页面1
从零玩转系列之微信支付实战PC端装修下单页面
78 0
超市购物车功能
超市购物车功能
88 0
|
前端开发 JavaScript
【畅购商城】购物车模块之修改购物车以及结算
【畅购商城】购物车模块之修改购物车以及结算
173 0
【畅购商城】购物车模块之修改购物车以及结算
Axure教程:外卖订单平台——用中继器做商品列表购物车
Axure教程:外卖订单平台——用中继器做商品列表购物车
Axure教程:外卖订单平台——用中继器做商品列表购物车