【从零开始分析项目实战】13-菜品展示及购物车用户下单的实现

注:本文章基于黑马程序员相关视频及资料进行编写,代码简单,较容易理解,若有问题或者源码资料获取可以在评论区留言或者联系作者!



开篇

接下来将对项目的购物车和下单功能进行编写,以下为效果图
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一、菜品展示

(1)需求分析:

用户登录成功之后跳转到系统首页,在首页需要根据分类来展示菜品和套餐。如果菜品设置了口 味信息,需要展示选择规格按钮,否则展示**+**按钮

(2)代码开发-梳理交互流程

  1. 页面发送ajax请求,获取分类数据(菜品分类和套餐分类)
  2. 页面发送ajax请求,获取第一个分类下的菜品或套餐

注意:首页加载完成之后,还发送了一次ajax请求用于加载购物车数据,此处可以将这次请求的地址暂时改一下,从静态json文件获取数据,等后续购物车开发时再改回来
在这里插入图片描述

(3)代码开发-修改DishController中的list方法

从主页面可以发现,所有的菜品都是以 ** + ** 进行进行选择,并没有达到我们开始规定的若有口味信息,则按照口味信息进行选择,若没有口味信心,则直接可以添加菜品;究其原因,是在与前端页面此处是按照请求菜品返回数据中有无flavor信息,若有,则将其进行渲染,若没有则直接可以进行添加;
而查看DishController中的list方法,发现返回的是Dis对象,而Dish对象中并没有封装flavor信息。所以我们这里需要进行修改;返回一个含有flavor信息的Dishdto对象;

/*根据条件来查询对应的菜品--新*/
    @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);

            //设置DishFlavor中categoryName的值
            Long categoryId = item.getCategoryId();

            //根据菜品分类id到分类列表中查询菜品这条数据
            Category category = categoryService.getById(categoryId);

            if (category!=null){
                //获取菜品分类名
                String categoryName = category.getName();
                //在dishDto中对categoryName属性进行赋值
                dishDto.setCategoryName(categoryName);

            }
            //设置DishFlavor中的Flavor的值
            LambdaQueryWrapper<DishFlavor> flavorLambdaQueryWrapper=new LambdaQueryWrapper<>();
            flavorLambdaQueryWrapper.eq(DishFlavor::getDishId,item.getId());
            List<DishFlavor> dishFlavors = dishFlavorService.list(flavorLambdaQueryWrapper);
            dishDto.setFlavors(dishFlavors);

            return dishDto;
        }).collect(Collectors.toList());
        return R.success(dishDtoList);
    }

运行项目,菜品能够正常选择规格和进行添加;
在这里插入图片描述

(4)代码开发-创建SetmealController的list方法

这里和上面的DIshController中的list方法一样,用于返回当前请求套餐下的数据,然后前端页面进行渲染;

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

    }

运行结果如下:
在这里插入图片描述


二、购物车

(1)需求分析:

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

购物车对应shopping_cart表

(2) 交互过程:

  1. 点击加入购物车或者加号按钮,页面发送ajax请求,请求服务端,将菜品或者套餐添加到购物车
  2. 点击购物车图标,页面发送ajax请求,请求服务端查询购物车中的菜品和套餐
  3. 点击清空购物车按钮,页面发送ajax请求,请求服务端来执行清空购物车操作;

搭建好需要使用的基本框架;

(3)代码开发-添加购物车

将菜品添加到购物车,页面会传来dishid和setmealid,也就是说,用户可能会添加菜品,也可能会添加套餐,但是只会传入两者中的一个值,所以我们可以对此进行判断,如果传来的是菜品id,则根据菜品id构造查询条件,然后查询到这个菜品,反之他套餐也是这样,根据它套餐id构造查询条件,然后根据查询条件查询购物车数据,若能查到数据,说明是购买的第二份,将查到的数据number加一,若不能查到,则将数据加入到购物车中

@PostMapping("/add")
    public R<ShoppingCart> add(@RequestBody ShoppingCart shoppingCart){
        log.info("购物车信息{}",shoppingCart);

        //设置用户id,指定当前是哪个用户的购物车数据
        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 shoppingCartServiceOne = shoppingCartService.getOne(queryWrapper);
        if (shoppingCartServiceOne!=null){
            //如果已经存在,就在原来的数量上加1
            Integer number = shoppingCartServiceOne.getNumber();
            shoppingCartServiceOne.setNumber(number+1);
            shoppingCartService.updateById(shoppingCartServiceOne);

        }
        else {
            //如果不存在,则添加到购物车中,数量默认都是1
            shoppingCart.setNumber(1);
            shoppingCartService.save(shoppingCart);
            shoppingCartServiceOne=shoppingCart;

        }
        return R.success(shoppingCartServiceOne);

    }

在这里插入图片描述

在这里插入图片描述

(4)代码开发-查看购物车&清空购物车

查看购物车相对较容易实现,只需要根据用户id查询购物车信息即可

  @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);
    }

在这里插入图片描述
删除购物车也是和查询一样的原理,只需要根据用户id删除相关信息即可,这里就不再演示


三、导入用户地址簿

(1)需求分析:

地址簿,指的是移动端消费者用户的地址信息,用户登录成功后可以维护自己的地址信息,同一个用户可以有多个地址信息,但是只能有一个默认地址
在这里插入图片描述
用户的地址信息会存储在address__book(地址簿)表中

(2)功能代码大体框架:

  • 实体类AddressBook
  • Mapper接口AddressBookMapper
  • 业务层接口AddressService
  • 业务层实现接口 AddressServiceImpl
  • 控制层 AddressBookController

(3)代码实现

@Slf4j
@RestController
@RequestMapping("/addressBook")
public class AddressBookController {

    @Autowired
    private AddressBookService addressBookService;

    /**
     * 新增
     */
    @PostMapping
    public R<AddressBook> save(@RequestBody AddressBook addressBook) {
        //线程中获取用户id
        addressBook.setUserId(BaseContext.getCurrentId());
        log.info("addressBook:{}", addressBook);
        addressBookService.save(addressBook);
        return R.success(addressBook);
    }

    /**
     * 设置默认地址
     */
    @PutMapping("default")
    public R<AddressBook> setDefault(@RequestBody AddressBook addressBook) {
        log.info("addressBook:{}", addressBook);
        LambdaUpdateWrapper<AddressBook> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(AddressBook::getUserId, BaseContext.getCurrentId());
        wrapper.set(AddressBook::getIsDefault, 0);
        //SQL:update address_book set is_default = 0 where user_id = ?
        //将当前用户 所对应的所有地址的is_default值改为0 (非默认地址)
        addressBookService.update(wrapper);

        addressBook.setIsDefault(1);
        //SQL:update address_book set is_default = 1 where id = ?
        //将此地址的is_default改为1(默认地址)
        addressBookService.updateById(addressBook);
        return R.success(addressBook);
    }

    /**
     * 根据id查询地址
     */
    @GetMapping("/{id}")
    public R get(@PathVariable Long id) {
        AddressBook addressBook = addressBookService.getById(id);
        if (addressBook != null) {
            return R.success(addressBook);
        } else {
            return R.error("没有找到该对象");
        }
    }

    /**
     * 查询默认地址
     */
    @GetMapping("default")
    public R<AddressBook> getDefault() {
        LambdaQueryWrapper<AddressBook> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AddressBook::getUserId, BaseContext.getCurrentId());
        queryWrapper.eq(AddressBook::getIsDefault, 1);

        //SQL:select * from address_book where user_id = ? and is_default = 1
        AddressBook addressBook = addressBookService.getOne(queryWrapper);

        if (null == addressBook) {
            return R.error("没有找到该对象");
        } else {
            return R.success(addressBook);
        }
    }

    /**
     * 查询指定用户的全部地址
     */
    @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);

        //SQL:select * from address_book where user_id = ? order by update_time desc
        return R.success(addressBookService.list(queryWrapper));
    }
}

这里对地址簿的演示可以自行演示


四、用户下单

(1)需求分析:

移动端用户可以将套餐或者菜品加入购物车,可以点击购物车中的去结算按钮,页面跳转到订单确认页面,点击去支付按钮完成下单操作;
在这里插入图片描述
主要涉及到Order表(订单表)和Order_detail表(订单明细表)

(2)代码开发-梳理交互过程

  1. 在购物车中点击 去结算 按钮,页面跳转到订单确认页面(已完成)
  2. 在页面确认页面,发送ajax请求,请求服务端获取当前 登录用户的默认地址(已完成)
  3. 在订单确认页面,发送ajax’请求,请求服务端获取当前登录用户 的购物车数据(已完成)
  4. 在订单确认页面点击 去支付 按钮,发送ajax请求,请求服务端完成下单操作

创建大体的结构(和前面一样controller,mapper,service,serviceimpl)

(3)代码开发

用户下单后需要对orders表进行操作,添加相关信息,也需要对orderdetail表进行数据的添加,然后删除购物车表中该用户的相关数据,要对三张表进行操作,所以可以在 orderServiceImpl中进行相关逻辑的编写;

 @Transactional
    @Override
    public void submit(Orders orders) {
        //获取当前用户id
        Long userId= BaseContext.getCurrentId();

        //根据上传数据查询用户购物车数据
        LambdaQueryWrapper<ShoppingCart> queryWrapper=new LambdaQueryWrapper<>();
        queryWrapper.eq(ShoppingCart::getUserId,userId);
        List<ShoppingCart> shoppingCarts = shoppingCartService.list(queryWrapper);

        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();//mybatisplus随机生成一个订单id

        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.setNumber(String.valueOf(orderId));
        orders.setId(orderId);//id使用订单号
        orders.setUserId(userId);//用户id
        orders.setOrderTime(LocalDateTime.now());//下单时间
        orders.setCheckoutTime(LocalDateTime.now());//支付时间
        orders.setStatus(2);//状态
        orders.setAmount(new BigDecimal(amount.get()));//设置金额
        orders.setUserName(user.getName());//用户名
        orders.setPhone(user.getPhone());//手机号
        orders.setConsignee(addressBook.getConsignee());//收货人
        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(queryWrapper);

在这里插入图片描述
在这里插入图片描述


如果感觉内容写的还不错的话,一键三连不迷路!!!!
后面将会更新更多学习内容,一起学习吧!!!!!!
在这里插入图片描述

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

PoJo123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值