瑞吉外卖项目——瑞吉外卖2

用户端登录

短信发送

  • 短信服务介绍

目前市面上有很多第三方提供的短信服务,这些第三方短信服务会和各个运营商(移动、联通、电信)对接,我们只需要注册成为会员并且按照提供的开发文档进行调用就可以发送短信。

需要说明的是,这些短信服务一般都是收费服务。

常用短信服务:

  • 阿里云
  • 华为云
  • 腾讯云
  • 京东
  • 梦网
  • 乐信

此处选择阿里云SMS

  1. 导入Maven坐标

    <!--阿里云短信SDK-->
    <dependency>
        <groupId>com.aliyun</groupId>
        <artifactId>aliyun-java-sdk-core</artifactId>
        <version>4.5.16</version>
    </dependency>
    
    <dependency>
        <groupId>com.aliyun</groupId>
        <artifactId>aliyun-java-sdk-dysmsapi</artifactId>
        <version>2.1.0</version>
    </dependency>
    
  2. 导入并配置短信工具类 (并配置自己的accessKeyID和secret)

    package com.jihua.reggie.common.utils;
    
    import com.aliyuncs.DefaultAcsClient;
    import com.aliyuncs.IAcsClient;
    import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
    import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
    import com.aliyuncs.exceptions.ClientException;
    import com.aliyuncs.profile.DefaultProfile;
    
    /**
     * 短信发送工具类
     */
    public class SMSUtils {
          
          
    
        /**
         * 发送短信
         *
         * @param signName     签名
         * @param templateCode 模板
         * @param phoneNumbers 手机号
         * @param param        参数
         */
        public static void sendMessage(String signName, String templateCode, String phoneNumbers, String param) {
          
          
            DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "输入自己的accessKeyID", "输入自己的secret");
            IAcsClient client = new DefaultAcsClient(profile);
    
            SendSmsRequest request = new SendSmsRequest();
            request.setSysRegionId("cn-hangzhou");
            request.setPhoneNumbers(phoneNumbers);
            request.setSignName(signName);
            request.setTemplateCode(templateCode);
            request.setTemplateParam("{\"code\":\"" + param + "\"}");
            try {
          
          
                SendSmsResponse response = client.getAcsResponse(request);
                System.out.println("短信发送成功");
            } catch (ClientException e) {
          
          
                e.printStackTrace();
            }
        }
    
    }
    

手机验证码登录

为了方便用户登录,移动端通常都会提供通过手机验证码登录的功能。

手机验证码登录的优点:

  • 方便快捷,无需注册,直接登录
  • 使用短信验证码作为登录凭证,无需记忆密码
  • 安全

登录流程:

输入手机号>获取验证码>输入验证码>点击登录>登录成功

  • 注意:通过手机验证码登录,手机号是区分不同用户的标识。

通过手机验证码登录时,涉及的表为user表,即用户表。

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

  • 实体类User

    package com.jihua.reggie.entity;
    
    import lombok.Data;
    
    import java.io.Serializable;
    
    /**
     * 用户信息
     */
    @Data
    public class User implements Serializable {
          
          
    
        private static final long serialVersionUID = 1L;
    
        private Long id;
    
    
        //姓名
        private String name;
    
    
        //手机号
        private String phone;
    
    
        //性别 0 女 1 男
        private String sex;
    
    
        //身份证号
        private String idNumber;
    
    
        //头像
        private String avatar;
    
    
        //状态 0:禁用,1:正常
        private Integer status;
    }
    
  • Mapper接口UserMapper

    package com.jihua.reggie.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.jihua.reggie.entity.User;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface UserMapper extends BaseMapper<User> {
          
          
    }
    
  • 业务层接口UserService

    package com.jihua.reggie.service;
    
    import com.baomidou.mybatisplus.extension.service.IService;
    import com.jihua.reggie.entity.User;
    
    public interface UserService extends IService<User> {
          
          
    }
    
  • 业务层实现类UserServicelmpl

    package com.jihua.reggie.service.impl;
    
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.jihua.reggie.entity.User;
    import com.jihua.reggie.mapper.UserMapper;
    import com.jihua.reggie.service.UserService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Service;
    
    @Service
    @Slf4j
    public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
          
          
    
    }
    
  • 控制层UserController

    package com.jihua.reggie.controller;
    
    import com.jihua.reggie.service.UserService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/user")
    @Slf4j
    public class UserController {
          
          
        @Autowired
        private UserService userService;
    }
    
  • 工具类SMSutils、ValidateCodeUtils

    package com.jihua.reggie.common.utils;
    
    import com.aliyuncs.DefaultAcsClient;
    import com.aliyuncs.IAcsClient;
    import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
    import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
    import com.aliyuncs.exceptions.ClientException;
    import com.aliyuncs.profile.DefaultProfile;
    import org.springframework.beans.factory.annotation.Value;
    
    /**
     * 短信发送工具类
     */
    public class SMSUtils {
          
          
        @Value("${SMS.accessKeyID}")
        private static String accessKeyID;
        @Value("${SMS.secret}")
        private static String secret;
    
    
        /**
         * 发送短信
         *
         * @param signName     签名
         * @param templateCode 模板
         * @param phoneNumbers 手机号
         * @param param        参数
         */
        public static void sendMessage(String signName, String templateCode, String phoneNumbers, String param) {
          
          
            DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyID, secret);
            IAcsClient client = new DefaultAcsClient(profile);
    
            SendSmsRequest request = new SendSmsRequest();
            request.setSysRegionId("cn-hangzhou");
            request.setPhoneNumbers(phoneNumbers);
            request.setSignName(signName);
            request.setTemplateCode(templateCode);
            request.setTemplateParam("{\"code\":\"" + param + "\"}");
            try {
          
          
                SendSmsResponse response = client.getAcsResponse(request);
                System.out.println("短信发送成功");
            } catch (ClientException e) {
          
          
                e.printStackTrace();
            }
        }
    
    }
    
    
    package com.jihua.reggie.common.utils;
    
    import java.util.Random;
    
    /**
     * 随机生成验证码工具类
     */
    public class ValidateCodeUtils {
          
          
        /**
         * 随机生成验证码
         *
         * @param length 长度为4位或者6位
         * @return
         */
        public static Integer generateValidateCode(int length) {
          
          
            Integer code = null;
            if (length == 4) {
          
          
                code = new Random().nextInt(9999);//生成随机数,最大为9999
                if (code < 1000) {
          
          
                    code = code + 1000;//保证随机数为4位数字
                }
            } else if (length == 6) {
          
          
                code = new Random().nextInt(999999);//生成随机数,最大为999999
                if (code < 100000) {
          
          
                    code = code + 100000;//保证随机数为6位数字
                }
            } else {
          
          
                throw new RuntimeException("只能生成4位或6位数字验证码");
            }
            return code;
        }
    
        /**
         * 随机生成指定长度字符串验证码
         *
         * @param length 长度
         * @return
         */
        public static String generateValidateCode4String(int length) {
          
          
            Random rdm = new Random();
            String hash1 = Integer.toHexString(rdm.nextInt());
            String capstr = hash1.substring(0, length);
            return capstr;
        }
    }
    
  • 代码开发 - 修改LoginCheckFilter

前面我们已经完成了LoginCheckFilter过滤器的开发,此过滤器用于检查用户的登录状态。

我们在进行手机验证码登录时,发送的请求需要在此过滤器处理时直接放行。

//定义不需要处理的请求路径
String[] urls = new String[]{
    
    
        "/employee/login",
        "/employee/logout",
        "/backend/**",
        "/front/**",
        "/user/login",//移动端登录
        "/user/sendMsg"//移动端发送短信
};
//4-1. 判断商家端登录状态,如果已登录,则直接放行
//获取当前登录用户的id
Long loginID = (Long) request.getSession().getAttribute("employee");
BaseContext.setCurrentId(loginID);
if (loginID != null) {
    
    
    log.info("商家已登陆,当前请求路径为:{},当前用户id为:{}", request.getRequestURI(), loginID);
    filterChain.doFilter(request, response);
    return;
}
//4-2. 判断用户端登录状态,如果已登录,则直接放行
//获取当前登录用户的id
Long userId = (Long) request.getSession().getAttribute("user");
BaseContext.setCurrentId(userId);
if (userId != null) {
    
    
    log.info("用户已登陆,当前请求路径为:{},当前用户id为:{}", request.getRequestURI(), userId);
    filterChain.doFilter(request, response);
    return;
}
  • 发送手机验证码
@Value("${SMS.signName}")
private String signName;
@Value("${SMS.templateCode}")
private String templateCode;
/**
 * 发送手机短信验证码
 *
 * @param user
 * @param session
 * @return
 */
@PostMapping("/sendMsg")
public R<String> sendMsg(@RequestBody User user, HttpSession session) {
    
    
    //获取手机号
    String phone = user.getPhone();
    if (StringUtils.isNotEmpty(phone)) {
    
    
        //生成随机的4位验证码
        String code = ValidateCodeUtils.generateValidateCode(4).toString();
        log.info("验证码为:{}", code);

        //调用阿里云带内心服务api发送验证码
        SMSUtils.sendMessage(signName, templateCode, phone, code);
        //需要将生成的验证码保存到Session
        session.setAttribute(phone, code);
        return R.success("发送验证码成功");
    }
    return R.error("短信发送失败");
}
  • 移动端用户登录
/**
 * 移动端用户登录
 *
 * @param map
 * @param session
 * @return
 */
@PostMapping("/login")
public R<User> login(@RequestBody Map<String, String> map, HttpSession session) {
    
    
    log.info("用户端登录:{}", map);
    //获取手机号
    String phone = map.get("phone");
    //获取验证码
    String code = map.get("code");
    //从Session中获取保存的验证码
    String codeInSession = (String) session.getAttribute(phone);
    //判断验证码是否正确
    if (codeInSession != null && codeInSession.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);
            user.setStatus(1);
            userService.save(user);
        }
        session.setAttribute("user", user.getId());
        return R.success(user);
    }
    return R.error("验证码错误");
}

菜品展示

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

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

  2. 页面发送ajax请求,获取第一个分类下的菜品或者套餐

    • 因为需要根据当前菜品是否有口味选择,区别显示为”加号“或者“选择口味” ,所有需要将DishController中的list方法的返回值改为List<DishDto>
    /**
     * 根据条件查询对应的菜品数据
     *
     * @param dish
     * @return
     */
    @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() != null, Dish::getStatus, dish.getStatus());
        //添加name搜索条件
        queryWrapper.like(dish.getName() != null, Dish::getName, dish.getName());
        //添加排序条件
        queryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);
    
        List<Dish> list = dishService.list(queryWrapper);
    
        List<DishDto> listDto = 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> dishFlavorLambdaQueryWrapper = new LambdaQueryWrapper<>();
            dishFlavorLambdaQueryWrapper.eq(DishFlavor::getDishId, dishID);
            List<DishFlavor> dishFlavorList = dishFlavorService.list(dishFlavorLambdaQueryWrapper);
            dishDto.setFlavors(dishFlavorList);
            return dishDto;
        }).collect(Collectors.toList());
    
        return R.success(listDto);
    }
    

开发菜品展示功能,其实就是在服务端编写代码去处理前端页面发送的这2次请求即可。

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

//main.js
//获取购物车内商品的集合
function cartListApi(data) {
    
    
    return $axios({
    
    
        //TODO: 购物车功能开发后修改此处
        // 'url': '/shoppingCart/list',
        'url':'/front/carData.json',
        'method': 'get',
        params:{
    
    ...data}
    })
}
{
    
    
  "code": 1,
  "message": null,
  "data": [],
  "map": {
    
    }
}
  • 套餐查询(在SetmealController中完善list方法)
/**
 * 根据条件查询套餐数据
 * @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);
}

购物车

购物车对应的数据表为shopping cart表

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

  • 实体类ShoppingCart

    package com.jihua.reggie.entity;
    
    import lombok.Data;
    
    import java.io.Serializable;
    import java.math.BigDecimal;
    import java.time.LocalDateTime;
    
    /**
     * 购物车
     */
    @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;
    }
    
  • Mapper接口ShoppingcartMapper

    package com.jihua.reggie.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.jihua.reggie.entity.ShoppingCart;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface ShoppingCartMapper extends BaseMapper<ShoppingCart> {
          
          
    }
    
  • 业务层接口ShoppingCartService

    package com.jihua.reggie.service;
    
    import com.baomidou.mybatisplus.extension.service.IService;
    import com.jihua.reggie.entity.ShoppingCart;
    
    public interface ShoppingCartService extends IService<ShoppingCart> {
          
          
    }
    
  • 业务层实现类ShoppingCartServicelmpl

    package com.jihua.reggie.service.impl;
    
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.jihua.reggie.entity.ShoppingCart;
    import com.jihua.reggie.mapper.ShoppingCartMapper;
    import com.jihua.reggie.service.ShoppingCartService;
    import org.springframework.stereotype.Service;
    
    @Service
    public class ShoppingCartServiceImpl extends ServiceImpl<ShoppingCartMapper, ShoppingCart> implements ShoppingCartService {
          
          
    }
    
  • 控制层ShoppingcartController

    package com.jihua.reggie.controller;
    
    import com.jihua.reggie.service.ShoppingCartService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @Slf4j
    @RestController
    @RequestMapping("/shoppingCart")
    public class ShoppingCartController {
          
          
        @Autowired
        private ShoppingCartService shoppingCartService;
    }
    

移动端用户可以将菜品或者套餐添加到购物车。

对于菜品来说,如果设置了口味信息,则需要选择规格后才能加入购物车

对于套餐来说,可以直接点击+将当前套餐加入购物车。

在购物车中可以修改菜品和套餐的数量,也可以清空购物车。

最终的 ShoppingcartController:

package com.jihua.reggie.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.jihua.reggie.common.BaseContext;
import com.jihua.reggie.common.R;
import com.jihua.reggie.entity.ShoppingCart;
import com.jihua.reggie.service.ShoppingCartService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.util.List;

@Slf4j
@RestController
@RequestMapping("/shoppingCart")
public class ShoppingCartController {
    
    
    @Autowired
    private ShoppingCartService shoppingCartService;

    /**
     * 添加购物车
     *
     * @param shoppingCart
     * @return
     */
    @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());
        }

        //查询菜品或者套餐是否在购物车中
        //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);
    }

    /**
     * 查看购物车
     *
     * @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);
    }

    /**
     * 购物车数量减一
     *
     * @param shoppingCart
     * @return
     */
    @PostMapping("/sub")
    public R<String> sub(@RequestBody ShoppingCart shoppingCart) {
    
    
        log.info("购物车数量减一:{}", shoppingCart);
        LambdaQueryWrapper<ShoppingCart> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ShoppingCart::getUserId, BaseContext.getCurrentId());
        //区分是菜品还是套餐
        if (shoppingCart.getDishId() != null) {
    
    
            queryWrapper.eq(ShoppingCart::getDishId, shoppingCart.getDishId());
        }
        if (shoppingCart.getSetmealId() != null) {
    
    
            queryWrapper.eq(ShoppingCart::getSetmealId, shoppingCart.getSetmealId());
        }
        //查询当前菜品或者套餐的数量
        ShoppingCart one = shoppingCartService.getOne(queryWrapper);
        Integer number = one.getNumber();
        if (number == 1) {
    
    
            shoppingCartService.removeById(one.getId());
            return R.success("菜品删除成功");
        }
        if (number > 1) {
    
    
            one.setNumber(number - 1);
            shoppingCartService.updateById(one);
            return R.success("套餐删除成功");
        }

        return R.error("没有查询到当前菜品或套餐");
    }

    /**
     * 清空购物车
     *
     * @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("清空购物车成功");
    }
}

用户下单

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

  • orders:订单表
  • order_detail:订单明细表

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

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

开发用户下单功能,其实就是在服务端编写代码去处理前端页面发送的请求即可。

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

  • 实体类Orders、OrderDetail

    package com.jihua.reggie.entity;
    
    import lombok.Data;
    
    import java.io.Serializable;
    import java.math.BigDecimal;
    import java.time.LocalDateTime;
    
    /**
     * 订单
     */
    @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;
    }
    
    package com.jihua.reggie.entity;
    
    import lombok.Data;
    
    import java.io.Serializable;
    import java.math.BigDecimal;
    
    /**
     * 订单明细
     */
    @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;
    }
    
  • Mapper接口OrderMapper、OrderDetailMapper

    package com.jihua.reggie.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.jihua.reggie.entity.Orders;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface OrderMapper extends BaseMapper<Orders> {
          
          
    
    }
    
    package com.jihua.reggie.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.jihua.reggie.entity.OrderDetail;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface OrderDetailMapper extends BaseMapper<OrderDetail> {
          
          
    
    }
    
  • 业务层接口OrderService、OrderDetailService

    package com.jihua.reggie.service;
    
    import com.baomidou.mybatisplus.extension.service.IService;
    import com.jihua.reggie.entity.Orders;
    
    public interface OrderService extends IService<Orders> {
          
          
    
        /**
         * 用户下单
         *
         * @param orders
         */
        public void submit(Orders orders);
    }
    
    package com.jihua.reggie.service;
    
    import com.baomidou.mybatisplus.extension.service.IService;
    import com.jihua.reggie.entity.OrderDetail;
    
    public interface OrderDetailService extends IService<OrderDetail> {
          
          
    
    }
    
  • 业务层实现类OrderServicelmpl、OrderDetailServicelmpl

    package com.jihua.reggie.service.impl;
    
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.baomidou.mybatisplus.core.toolkit.IdWorker;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.jihua.reggie.common.BaseContext;
    import com.jihua.reggie.common.exception.CustomException;
    import com.jihua.reggie.entity.*;
    import com.jihua.reggie.mapper.OrderMapper;
    import com.jihua.reggie.service.*;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import java.math.BigDecimal;
    import java.time.LocalDateTime;
    import java.util.List;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.stream.Collectors;
    
    @Service
    @Slf4j
    public class OrderServiceImpl extends ServiceImpl<OrderMapper, Orders> implements OrderService {
          
          
    
    }
    
    package com.jihua.reggie.service.impl;
    
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.jihua.reggie.entity.OrderDetail;
    import com.jihua.reggie.mapper.OrderDetailMapper;
    import com.jihua.reggie.service.OrderDetailService;
    import org.springframework.stereotype.Service;
    
    @Service
    public class OrderDetailServiceImpl extends ServiceImpl<OrderDetailMapper, OrderDetail> implements OrderDetailService {
          
          
    
    }
    
  • 控制层OrderController、OrderDetailController

    package com.jihua.reggie.controller;
    
    import com.jihua.reggie.common.R;
    import com.jihua.reggie.entity.Orders;
    import com.jihua.reggie.service.OrderService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * 订单
     */
    @Slf4j
    @RestController
    @RequestMapping("/order")
    public class OrderController {
          
          
    
        @Autowired
        private OrderService orderService;
    
    }
    
    package com.jihua.reggie.controller;
    
    import com.jihua.reggie.service.OrderDetailService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * 订单明细
     */
    @Slf4j
    @RestController
    @RequestMapping("/orderDetail")
    public class OrderDetailController {
          
          
    
        @Autowired
        private OrderDetailService orderDetailService;
    
    }
    

地址簿

地址簿,指的是移动端消费者用户的地址信息,用户登录成功后可以维护自己的地址信息。

同一个用户可以有多个地址信息,但是只能有一个默认地址。

功能代码清单:

  • 实体类AddressBook

    package com.jihua.reggie.entity;
    
    import com.baomidou.mybatisplus.annotation.FieldFill;
    import com.baomidou.mybatisplus.annotation.TableField;
    import lombok.Data;
    
    import java.io.Serializable;
    import java.time.LocalDateTime;
    
    /**
     * 地址簿
     */
    @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;
    }
    
  • Mapper接口AddressBookMapper

    package com.jihua.reggie.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.jihua.reggie.entity.AddressBook;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface AddressBookMapper extends BaseMapper<AddressBook> {
          
          
    
    }
    
  • 业务层接口AddressBookService

    package com.jihua.reggie.service;
    
    import com.baomidou.mybatisplus.extension.service.IService;
    import com.jihua.reggie.entity.AddressBook;
    
    public interface AddressBookService extends IService<AddressBook> {
          
          
    
    }
    
  • 业务层实现类AddressBookServicelmpl

    package com.jihua.reggie.service.impl;
    
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.jihua.reggie.entity.AddressBook;
    import com.jihua.reggie.mapper.AddressBookMapper;
    import com.jihua.reggie.service.AddressBookService;
    import org.springframework.stereotype.Service;
    
    @Service
    public class AddressBookServiceImpl extends ServiceImpl<AddressBookMapper, AddressBook> implements AddressBookService {
          
          
    
    }
    
  • 控制层AddressBookController

    package com.jihua.reggie.controller;
    
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
    import com.jihua.reggie.common.BaseContext;
    import com.jihua.reggie.common.R;
    import com.jihua.reggie.entity.AddressBook;
    import com.jihua.reggie.service.AddressBookService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    
    import java.util.List;
    
    /**
     * 地址簿管理
     */
    @Slf4j
    @RestController
    @RequestMapping("/addressBook")
    public class AddressBookController {
          
          
    
        @Autowired
        private AddressBookService addressBookService;
    
        /**
         * 新增
         */
        @PostMapping
        public R<AddressBook> save(@RequestBody AddressBook addressBook) {
          
          
            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);
            //先将该用户所有地址的is_default改为0(因为只能有一个默认地址)
            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 = ?
            addressBookService.update(wrapper);
    
            //再将当前地址的is_default改为1
            addressBook.setIsDefault(1);
            //SQL:update address_book set is_default = 1 where id = ?
            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));
        }
    }
    

猜你喜欢

转载自blog.csdn.net/jihuaTEL/article/details/130255463