【瑞吉外卖day06】

2023-11-12

1.手机验证码登录

1.1 需求分析

客户输入手机号码,获取验证码后,输入后能成功登录。
在这里插入图片描述
在这里插入图片描述

1.2 用户登录

在这里插入图片描述
在这里插入图片描述
首先修改拦截器,将手机端的发送短信和登录请求加到过滤器的放行请求中。
在这里插入图片描述
然后在拦截器中加入这些,利用session判断用户是否登录,用户如果没有登录就进行拦截。

//4-2、判断移动端登录状态,如果已登录,则直接放行
        if(request.getSession().getAttribute("user") != null){
            log.info("用户已登录,用户id为:{}",request.getSession().getAttribute("user"));

            //利用基于ThreadLocal的工具类来获取用户id
            Long userId=(Long) request.getSession().getAttribute("user");
            BaseContext.setCurrentId(userId);

            long id = Thread.currentThread().getId();
            log.info("线程id:{}",id);

            filterChain.doFilter(request,response);
            return;
        }

在这里插入图片描述
然后就是处理手机端发出的请求。首先是发送短信请求,我们在userController中进行处理。

/**
     * 发送验证码
     * @return
     */
    @PostMapping("/sendMsg")
    public R<String> sendMsg(@RequestBody User user, HttpSession session){
        //获取手机号
        String phone = user.getPhone();
        //生成4位验证码
        if(StringUtils.isNotEmpty(phone)){
            String code = ValidateCodeUtils.generateValidateCode(4).toString();
            log.info("验证码:{}",code);

            //调用阿里云的短信发送api
            //将验证码保存到session
            session.setAttribute(phone,code);
            return R.success("短信发送成功");
        }else{
            return R.error("短信发送失败");
        }

    }

然后是登录请求:
在这里插入图片描述
请求体:
在这里插入图片描述
我们使用Map来接收数据:
在这里插入图片描述
总体逻辑就是
1.比对map中获取的验证码和我们在session中暂存的phone对应的验证码是否相等,相等则登录
2.如果是新用户(即user表中没有相同phone的记录),要自动完成注册(即存入user表中)
3.设置session的user

/**
     * 移动端用户登录
     * @param map
     * @param session
     * @return
     */
    @PostMapping("/login")
    public R<User> login(@RequestBody Map map, HttpSession session){
        log.info(map.toString());
        String phone = (String) map.get("phone");
        String code = (String) map.get("code");

        String relCode = (String) session.getAttribute(phone);
        if(relCode!=null&&relCode.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);
                userService.save(user);
            }
            session.setAttribute("user",user.getId());
            return R.success(user);
        }else{
            return R.error("验证码错误");
        }


    }

测试。发现新用户已经存入user表中。
在这里插入图片描述

1.3 用户退出

/**
     * 员工退出
     * @param request
     * @return
     */
    @PostMapping("/loginout")
    public R<String> logout(HttpServletRequest request){
        //清理session
        request.getSession().removeAttribute("user");
        return R.success("退出成功");
    }

2.用户地址簿

2.1 需求分析

在这里插入图片描述

导入AddressBook实体类,创建Mapper、Service、ServiceImpl,此处不详细赘述

2.2 新增用户地址簿

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

请求URL:
在这里插入图片描述
请求体:
在这里插入图片描述
编写controller方法:
利用BaseContext的ThreadLocal与当前线程绑定的userId作为userid属性的值。

/**
     * 新增
     */
    @PostMapping
    public R<AddressBook> save(@RequestBody AddressBook addressBook) {
        addressBook.setUserId(BaseContext.getCurrentId());
        log.info("userId:{}",BaseContext.getCurrentId());
        log.info("addressBook:{}", addressBook);
        addressBookService.save(addressBook);
        return R.success(addressBook);
    }

测试,添加地址成功。
在这里插入图片描述

2.3 将某地址设为默认地址

在这里插入图片描述

addressBook实体类有一个属性isDefault,为1则是默认地址,为0则不是。
看看请求:
在这里插入图片描述
编写controller代码
总体思路为:
1.获取所有userId与当前用户id相同的记录;
2.将这些记录的isDefault字段全部设置为0;
3.再将传进来的addressBook的usDefault设置为1,去更新数据库。

/**
     * 设置默认地址
     */
    @PutMapping("default")
    public R<AddressBook> setDefault(@RequestBody AddressBook addressBook) {
        log.info("addressBook:{}", addressBook);
        LambdaUpdateWrapper<AddressBook> wrapper = new LambdaUpdateWrapper<>();
        log.info(BaseContext.getCurrentId().toString());
        wrapper.eq(AddressBook::getUserId, BaseContext.getCurrentId());
        wrapper.set(AddressBook::getIsDefault, 0);
        //先将所有这个用户的isdefault字段改为0
        //SQL:update address_book set is_default = 0 where user_id = ?
        addressBookService.update(wrapper);
        //再将所有这个用户的isdefault字段改为1
        addressBook.setIsDefault(1);
        //SQL:update address_book set is_default = 1 where id = ?
        addressBookService.updateById(addressBook);
        return R.success(addressBook);
    }

测试,变成了默认地址
在这里插入图片描述

2.4 查询全部地址

在这里插入图片描述

在这里插入图片描述
服务代码:

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

2.5 修改地址(自己写的)

在这里插入图片描述
修改地址总体分两步:
1.根据id查询地址详细信息,用于回显;
2.提交修改后的地址详细信息。

其中第一个请求已经写好了:
在这里插入图片描述
controller:

/**
     * 根据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("没有找到该对象");
        }
    }

我们只需要写第二个请求:
在这里插入图片描述

/**
     * 提交修改
     * @param addressBook 
     * @return
     */
    @PutMapping
    public R<AddressBook> update(@RequestBody AddressBook addressBook){
        addressBookService.updateById(addressBook);
        return R.success(addressBook);
    }

测试,修改成功

2.6 删除地址(自己写的)

在这里插入图片描述

在这里插入图片描述

@DeleteMapping
    public R<String> delAddress(Long ids){
        addressBookService.removeById(ids);
        return R.success("删除成功");
    }

测试,删除成功。

3.菜品展示

3.1 需求分析

在这里插入图片描述

3.2 代码开发

在这里插入图片描述
在登录到首页的过程中,前端发出了两次请求:
首先是category/list请求,这个请求之前已经开发好了,能够获取所有的分类数据
在这里插入图片描述
第二个请求是获取购物车数据
在这里插入图片描述
这里我们先使用假数据代替。
在这里插入图片描述
测试
在这里插入图片描述

点击每个分类,还会发出按分类条件查询菜品信息的请求,这个请求我们之前也写过了。
在这里插入图片描述
搬出来之前的dishcontroller代码

 @GetMapping("/list")
    public R<List<Dish>> queryList(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> dishList = dishService.list(queryWrapper);
        return R.success(dishList);
    }

但是这里我们希望每个菜品后面能够选取对应口味,而不是只是加到购物车。而我们目前的这个请求,只能获取到菜品信息,没有办法拿到口味信息返给前端。为此,我们需要改造这个方法,返回值类型从Dish改为DishDto,用stream流修改内容。

@GetMapping("/list")
    public R<List<DishDto>> queryList(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> dishList = dishService.list(queryWrapper);
        //stream流处理每个dish对象,去口味表中查询对应菜品的口味数据,最终包装为DishDto类型
        List<DishDto> dishDtoList = dishList.stream().map((item) -> {
            //新建DishDto对象
            DishDto dishDto = new DishDto();
            //属性拷贝
            BeanUtils.copyProperties(item, dishDto);
            //根据dishId查出口味list
            Long dishId = item.getId();
            LambdaQueryWrapper<DishFlavor> queryWrapper1 = new LambdaQueryWrapper<>();
            queryWrapper1.eq(DishFlavor::getDishId, dishId);
            List<DishFlavor> flavorList = dishFlavorService.list(queryWrapper1);
            //赋值
            dishDto.setFlavors(flavorList);
            return dishDto;
        }).collect(Collectors.toList());

        return R.success(dishDtoList);
    }

在这里插入图片描述
现在就能选对应的菜品口味啦。
但是目前仍然有问题,就是选择套餐的话会报400
在这里插入图片描述
编写controller代码

/**
     * 根据条件查询套餐数据
     * @param setmeal
     * @return
     */
    @GetMapping("/list")
    public R<List<Setmeal>> getSetDishes(Setmeal setmeal){
        //get请求没有请求体,不能加requestBody注解

        LambdaQueryWrapper<Setmeal> queryWrapper=new LambdaQueryWrapper<>();
        queryWrapper.eq(setmeal.getCategoryId()!=null,Setmeal::getCategoryId,setmeal.getCategoryId());
        queryWrapper.eq(setmeal.getStatus()!=null,Setmeal::getStatus,setmeal.getStatus());
        queryWrapper.orderByAsc(Setmeal::getUpdateTime);
        List<Setmeal> setmealList = setmealService.list(queryWrapper);
        return R.success(setmealList);
    }

发现套餐也能点开查看了
在这里插入图片描述

4.购物车功能

4.1 需求分析

在这里插入图片描述

4.2 添加购物车

在这里插入图片描述
将菜品添加到购物车请求:
在这里插入图片描述
准备工作:
在这里插入图片描述
编写添加购物车controller代码
总体步骤为:
1.利用在threadLocal上绑定的userId设置userid;
2.查数据库套餐表中有没有相同的菜或套餐(id);
3.有则获取原数量,update时+1;
4.没有则新增

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

        //设置用户id(利用在threadLocal上绑定的userId)
        Long currentId = BaseContext.getCurrentId();
        shoppingCart.setUserId(currentId);
        //查数据库套餐表中有没有相同的菜或套餐(id)
        //根据userId和dishId/setmealId查询
        LambdaQueryWrapper<ShoppingCart> queryWrapper=new LambdaQueryWrapper<>();
        queryWrapper.eq(ShoppingCart::getUserId,currentId);

        Long dishId = shoppingCart.getDishId();
        if(dishId==null){
            //添加的是套餐
            queryWrapper.eq(ShoppingCart::getSetmealId,shoppingCart.getSetmealId());
        }else {
            //添加的是菜品
            queryWrapper.eq(ShoppingCart::getDishId,dishId);
        }

        //查询
        ShoppingCart cartGetOne = shoppingCartService.getOne(queryWrapper);
        if(cartGetOne !=null){
            //如果有,相同记录数量直接加1
            Integer number = cartGetOne.getNumber();
            cartGetOne.setNumber(number+1);
            shoppingCartService.updateById(cartGetOne);
        }else {
            //没有的话就插入一条数据
            shoppingCart.setNumber(1);
            shoppingCartService.save(shoppingCart);
            cartGetOne =shoppingCart;
        }

        return R.success(cartGetOne);
    }

4.3 查看购物车

然后就是点击购物车图标,需要显示当前用户购物车商品。页面发送ajax请求(假数据改回去):
在这里插入图片描述
编写controller代码:

 /**
     * 查看购物车
     * @return
     */
    @GetMapping("/list")
    public R<List<ShoppingCart>> list(){
        //根据userid
        Long userId = BaseContext.getCurrentId();
        LambdaQueryWrapper<ShoppingCart> queryWrapper=new LambdaQueryWrapper<>();
        queryWrapper.eq(ShoppingCart::getUserId,userId);
        queryWrapper.orderByDesc(ShoppingCart::getCreateTime);
        List<ShoppingCart> list = shoppingCartService.list(queryWrapper);
        return R.success(list);
    }

测试,购物车商品显示成功。
在这里插入图片描述

4.4 清空购物车

点击删除按钮,页面发送请求:
在这里插入图片描述
本质是根据userid去删除对应购物车数据
编写controller代码:

/**
     * 清空购物车
     * @return
     */
    @DeleteMapping("/clean")
    public R<String> del(){
        Long userId = BaseContext.getCurrentId();
        LambdaQueryWrapper<ShoppingCart> queryWrapper=new LambdaQueryWrapper<>();
        queryWrapper.eq(ShoppingCart::getUserId,userId);
        shoppingCartService.remove(queryWrapper);
        return R.success("购物车清空成功");
    }

测试,清空成功。

5.用户下单

5.1需求分析

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

5.2 代码开发

在这里插入图片描述
前三个请求功能已经有了:
在这里插入图片描述
只有第四个功能,点击“去支付”,完成下单操作:

在这里插入图片描述
在这里插入图片描述
开发具体代码之前仍然是订单类和订单明细类的基础功能:
在这里插入图片描述
接下来就是开发用户下单功能,由于下单业务稍微复杂点,我们在orderService层编写主要的业务逻辑代码。

总体有几个步骤:
1.获取当前用户id;
2.根据当前用户id,查询用户数据、购物车数据、地址簿数据;
3.向订单表插入一条数据;
4.向订单明细表插入多条数据;
5.清空用户购物车数据;

业务代码如下:

/**
     * 用户下单
     * @param orders
     */
    @Transactional
    @Override
    public void submit(Orders orders) {
        //获得当前用户id
        Long userId = BaseContext.getCurrentId();
        //获取当前用户购物车数据
        LambdaQueryWrapper<ShoppingCart> shopQueryWrapper=new LambdaQueryWrapper<>();
        shopQueryWrapper.eq(ShoppingCart::getUserId,userId);
        List<ShoppingCart> shopList = shoppingCartService.list(shopQueryWrapper);

        if(shopList==null||shopList.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("地址信息有误,不能下单");
        }
        //向订单表插入数据(1条)
        //计算购物车总金额,同时获取订单明细list
        long orderId = IdWorker.getId();//生成订单号
        AtomicInteger amount=new AtomicInteger(0);//原子int
        List<OrderDetail> orderDetails=shopList.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赋值

        orders.setId(orderId);
        orders.setNumber(String.valueOf(orderId));

        orders.setOrderTime(LocalDateTime.now());
        orders.setCheckoutTime(LocalDateTime.now());
        orders.setStatus(2);
        orders.setAmount(new BigDecimal(amount.get()));//总金额
        orders.setUserId(userId);

        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(shopQueryWrapper);
    }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【瑞吉外卖day06】 的相关文章

随机推荐

  • 基础算法:前缀和

    前缀和 定义 s i 表示原数组前i个数的和 作用 求任意区间 l r 的和的时间复杂度从循环加的O n 到 s r s l 1 的时间复杂度O 1 eg s 3 a1 a2 a3 s 5 a1 a2 a3 a4 a5 s 4 5 s 5
  • 数字SOC设计之低功耗设计入门(二)——功耗的分析

    前面学习了进行低功耗的目的个功耗的构成 今天就来分享一下功耗的分析 由于是面向数字IC前端设计的学习 所以这里的功耗分析是基于DC中的power compiler工具 更精确的功耗分析可以采用PT 关于PT的功耗分析可以查阅其他资料 这里不
  • Bugku-game WP 一道有意思的题

    开启环境后 进入到了一个页面内的游戏界面 在页面中没有发现什么提示 开始游戏后 发现游戏内玩家通过玩游戏可以获得一定的积分 那么试想是否可以通过将积分更改得很大得做法来通关获得flag 查看源码 众所周知 分数一般会取名为score 那么在
  • Java Web应用开发常用网上资源

    前言 为了方便学习 下面为大家推荐一些学习Java Web开发的相关资源 使用这些资源 可以帮助你找到精通Java Web应用开发的捷径 常用资源下载网 在开发Java Web应用程序时 通常需要到相关资源的官方网站下载一些资源 下面将给出
  • jenkins如何同一jar包部署到多台服务器

    文章目录 安装插件 配置ssh服务 构建完成后执行 没有部署过可以跟这个下面的步骤先部署一遍 我这篇主要讲jenkins同一jar包部署到多台服务器 Jenkins 部署Springboot项目https blog csdn net qq
  • 步进电机五根线怎么接_软启动怎么接电机?软启动电机实物接线图

    电工学习网 www diangon com 关注电工学习网官方微信公众号 电工电气学习 收获更多经验知识 50万 维修电工关注的微信平台 技术分享 学习交流 资料下载 常用的五种电机软启动器 接线图 一 CMC L系列数码型电机软启动器是一
  • 学习笔记 JavaScript ES6 Proxy

    学习内容 代理 常用拦截方法 ES 5当中实现拦截的方法 let obj let newValue Object defineProperty obj name get return newValue set val console log
  • 安卓的多选框CheckBox

    选中选项中的内容并且点击按键 就会在顶头的title显示出来 1 做好布局控件 2 绑定控件 3 button设置了监听 一旦点击了button就跳到checkbox里面去执行 4 checkbox需要if语句进行判断 选中就显示 XML文
  • 你真的知道如何在 ESXi 上安装 Linux 吗?

    1 分区4K对齐以获得最佳存储性能 如果分区没有4K对齐 这对单个磁盘来说不是什么大问题 但对于共享存储来说 共享存储中的一个LUN实际上是跨越多个不同的磁盘条带化的 所以虚拟机操作系统的一次读或写操作会导致存储阵列上的I O翻倍 未对齐的
  • 设计模式(1) 创建型模式和抽象工厂(Abstract Factory)

    问题聚焦 分别用一句话概括这节的几个知识点 什么是创建型模式 抽象了实例化过程 创建型模式有哪些 抽象工厂 工厂方法 原型模式 生成器模式 什么是抽象工厂 AbstractFactory 模式 与接口交互 获得一系列相关或互相依赖的对象实例
  • 【ESP32】VSCode添加驱动文件

    1 创建文件夹并添加驱动文件 2 修改main文件夹下的 CMakeLists txt
  • 扑克牌游戏中, 使用python实现一个查找顺子的算法

    在扑克牌游戏中 需要实现一个查找顺子的算法 连续的数字即为顺子 随机发N张牌 从中挑出最长的顺子 并返回其长度 如果没有顺子返回0 现在手上的牌为 12 3 4 10 6 5 6 8 11 9 11 11 9 12 1 1 去重 排序 m
  • python实现读取目录文件夹下的所有指定格式文件路径

    import os def get img file file name imagelist for parent dirnames filenames in os walk file name for filename in filena
  • lucene 学习笔记之一亢龙有悔

    一 Lucene的介绍 Lucene是一个全文检索的框架 apache组织提供了一个用Java实现的全文搜索引擎的开源项目 其功能非常的强大 api非常简单 并且有了全文检索的功能支持可以非常方便的实现根据关键字来搜索整个应用系统的内容 大
  • 阿里巴巴java开发规范

    一 编码规约 1 强制 代码中的命名均不能以下划线或美元符号开始 也不能以下划线或美元符号结束 反例 name name Object name name Object 2 强制 代码中的命名严禁使用拼音与英文混合的方式 更不允许直接使用中
  • 基于canvas的前端图片压缩

    common canvas图片压缩 param Object opt 配置参数 param Function cbk 回调函数 return Void example var opt type 1 为1为预览 建议不为1或后期进行改进 fi
  • Windows系统中Mysql目录迁移

    1 为什么迁移 最近发现我笔记本电脑C盘的内存空间有点儿吃紧 有以下目录可以直接清理 1 电脑运行产生的临时文件目录 C Windows Temp 2 Windows系统日常更新的数据包和安装包目录 一般可以释放10G左右空间 C Wind
  • package-lock.json 作用

    参照 https www cnblogs com honkerzh p 16767566 html
  • GMS程序调试指南

    代码获得处 https github com JiawangBian GMS Feature Matcher 作者交流处 https jwbian net gms comment 55 可以查看出现同样的问题 也有作者声明论文中表达错误的地
  • 【瑞吉外卖day06】

    1 手机验证码登录 1 1 需求分析 客户输入手机号码 获取验证码后 输入后能成功登录 1 2 用户登录 首先修改拦截器 将手机端的发送短信和登录请求加到过滤器的放行请求中 然后在拦截器中加入这些 利用session判断用户是否登录 用户如