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"></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"></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*/"/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*/"/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。
检查数据库数据,由于是菜品保存的是dish_id。
这时在页面上,我们可以继续点击+号,在购物车中增加该菜品,此时,应该是对现有的购物车菜品数量加1,而不应该插入新的记录。
检查数据库数据:
如果添加的是套餐,该套餐在当前用户的购物车中并不存在,则添加一条数据,数量为1。
检查数据库数据:
2). 查看购物车
点击页面下面的购物车边栏,查看购物车数据列表是否正常展示。
3). 清空购物车
在购物车列表展示页中点击"清空", 查看购物车是否被清空。
并检查数据库中的数据,可以看到数据已经被删除。
4. 下单
4.1 需求分析
移动端用户将菜品或者套餐加入购物车后,可以点击购物车中的 "去结算" 按钮,页面跳转到订单确认页面,点击 "去支付" 按钮则完成
这里,我们需要说明一下,这里并不会去开发支付功能,因为不论是支付宝的支付,还是微信支付,都是需要企业资质的,而我们大家在测试的时候,是没有办法提供企业资质的,所以这一部分支付功能我们就不去实现了。
4.2 数据模型
用户下单业务对应的数据表为orders表和order_detail表(一对多关系,一个订单关联多个订单明细):
表名 | 含义 | 说明 |
orders | 订单表 | 主要存储订单的基本信息(如: 订单号、状态、金额、支付方式、下单用户、收件地址等) |
order_detail | 订单明细表 | 主要存储订单详情信息(如: 该订单关联的套餐及菜品的信息) |
具体的表结构如下:
A. orders 订单表
4.3 前端页面分析
在开发代码之前,需要梳理一下用户下单操作时前端页面和服务端的交互过程:
1). 在购物车中点击按钮,页面跳转到订单确认页面
页面跳转前端已经完成,我们无需操作。
2). 在订单确认页面,发送ajax请求,请求服务端获取当前登录用户的默认地址
3). 在订单确认页面,发送ajax请求,请求服务端获取当前登录用户的购物车数据
4). 在订单确认页面点击按钮,发送ajax请求,请求服务端完成下单操作
经过上述的分析,我们看到前三步的功能我们都已经实现了,我们主要需要实现最后一步的下单功能,该功能具体的请求信息如下:
请求 | 说明 |
请求方式 | 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; /*** 订单*/publicclassOrdersimplementsSerializable { 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; /*** 订单明细*/publicclassOrderDetailimplementsSerializable { 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; publicinterfaceOrderMapperextendsBaseMapper<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; publicinterfaceOrderDetailMapperextendsBaseMapper<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; publicclassOrderServiceImplextendsServiceImpl<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; publicclassOrderDetailServiceImplextendsServiceImpl<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; /*** 订单*/"/order") (publicclassOrderController { privateOrderServiceorderService; }</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.*; /*** 订单明细*/"/orderDetail") (publicclassOrderDetailController { privateOrderDetailServiceorderDetailService; }</span></span>
4.5 代码开发
在OrderController中创建submit方法,处理用户下单的逻辑 :
<spanstyle="background-color:#f8f8f8"><spanstyle="color:#333333">/*** 用户下单* @param orders* @return*/"/submit") (publicR<String>submit(Ordersorders){ 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">privateShoppingCartServiceshoppingCartService; privateUserServiceuserService; privateAddressBookServiceaddressBookService; privateOrderDetailServiceorderDetailService; /*** 用户下单* @param orders*/publicvoidsubmit(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的形式来跟踪代码的正常执行。
检查数据库数据
订单表插入一条记录: