1.手机验证码登录
在开发代码之前,需要梳理一下登录时前端页面和服务端的交互过程:
1、在登录页面(front/page/login.html)输入手机号,点击【获取验证码】按钮,页面发送ajax请求,在服务端调用短信服务API给指定手机号发送验证码短信
2、在登录页面输入验证码,点击【登录】按钮,发送ajax请求,在服务端处理登录请求
开发手机验证码登录功能,其实就是在服务端编写代码去处理前端页面发送的这2次请求即可。
使用阿里云短信服务发送短信,可以参照官方提供的文档即可。
具体开发步骤:
1、导入maven坐标
<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、调用API
在开发业务功能前,先将需要用到的类和接口基本结构创建好:
实体类User
package com.itheima.reggie.entity; import lombok.Data; import java.time.LocalDateTime; import java.util.Date; import java.util.List; import java.io.Serializable; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; /** * 用户信息 */ @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
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.itheima.reggie.entity.User; import org.apache.ibatis.annotations.Mapper; @Mapper public interface UserMapper extends BaseMapper<User> { }
业务层接口UserService
import com.baomidou.mybatisplus.extension.service.IService; import com.itheima.reggie.entity.User; public interface UserService extends IService<User> { }
业务层实现类UserServicelmpl
@Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { }
控制层UserController
@RestController @RequestMapping("/user") @Slf4j public class UserController { @Autowired private UserService userService; //发送手机验证码 @PostMapping("/sendMsg") private 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={}",code); //调用阿里云的短信服务apl完成发送短信 //SMSUtils.sendMessage("瑞吉外卖","SMS_273795337","17807941605",code); //需要将生成的验证码保存到session中 session.setAttribute(phone,code); return R.success("手机验证码短信发送成功"); } return R.error("短信发送失败"); } //移动端用户登录 @PostMapping("/login") private R<User> login(@RequestBody Map map, HttpSession session){ log.info(map.toString()); //获取手机号 String phone = map.get("phone").toString(); //获取验证码 String code = map.get("code").toString(); //从session获取保存的验证码 Object codeInSession = session.getAttribute(phone); //进行验证码的比对(页面提交的验证码和session中保存的验证码比对) 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("登录失败"); }
工具类SMSutils、ValidateCodeutils
package com.itheima.reggie.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", "", ""); 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.itheima.reggie.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; } }
2.导入用户地址簿
实体类AddressBook
package com.itheima.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
@Mapper public interface AddressBookMapper extends BaseMapper<AddressBook> { }
业务层接口AddressBookService
public interface AddressBookService extends IService<AddressBook> { }
业务层实现类AddressBookServicelmpl
@Service public class AddressBookServiceImpl extends ServiceImpl<AddressBookMapper, AddressBook> implements AddressBookService { }
控制层AddressBookController
/** * 地址簿管理 */ @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); 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); 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)); } }
3.菜品展示
在开发代码之前,需要梳理一下前端页面和服务端的交互过程:
1、页面(front/index.html)发送ajax请求,获取分类数据(菜品分类和套餐分类)
2、页面发送ajax请求,获取第一个分类下的菜品或者套餐
开发菜品展示功能,其实就是在服务端编写代码去处理前端页面发送的这2次请求即可。
注意:首页加载完成后,还发送了一次ajax请求用于加载购物车数据,此处可以将这次请求的地址暂时修改一下,从静态json文件获取数据,等后续开发购物车功能时再修改回来,如下:
在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); }
修改 dishController中的list方法
@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); 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> lambdaQueryWrapper=new LambdaQueryWrapper<>(); lambdaQueryWrapper.eq(DishFlavor::getDishId,dishId); List<DishFlavor> dishFlavorList=dishFlavorService.list(lambdaQueryWrapper); dishDto.setFlavors(dishFlavorList); return dishDto; }).collect(Collectors.toList()); return R.success(dishDtoList); }
4.购物车
在开发代码之前,需要梳理一下购物车操作时前端页面和服务端的交互过程:
1、点击加入购物车或者+按钮,页面发送ajax请求,请求服务端,将菜品或者套餐添加到购物车
2、点击购物车图标,页面发送ajax请求,请求服务端查询购物车中的菜品和套餐
3、点击清空购物车按钮,页面发送ajax请求,请求服务端来执行清空购物车操作
开发购物车功能,其实就是在服务端编写代码去处理前端页面发送的这3次请求即可。
在开发业务功能前,先将需要用到的类和接口基本结构创建好:
实体类ShopplingCart
package com.itheima.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
@Mapper public interface ShoppingCartMapper extends BaseMapper<ShoppingCart> { }
业务层接口ShoppingCartService
public interface ShoppingCartService extends IService<ShoppingCart> { }
业务层实现类ShoppingCartServicelmpl
@Service public class ShoppingCartServiceImpl extends ServiceImpl<ShoppingCartMapper, ShoppingCart> implements ShoppingCartService { }
控制层ShoppingCartController
//购物车 @Slf4j @RestController @RequestMapping("/shoppingCart") public class ShoppingCartController { @Autowired private ShoppingCartService shoppingCartService; //添加购物车 @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 cartServiceOne = shoppingCartService.getOne(queryWrapper); if (cartServiceOne!=null){ //如果已经存在,就原来数量的基础上加1 Integer number = cartServiceOne.getNumber(); cartServiceOne.setNumber(number+1); shoppingCartService.updateById(cartServiceOne); }else { //如果不存在就添加到购物车,数量默认为1 shoppingCart.setNumber(1); shoppingCart.setCreateTime(LocalDateTime.now()); shoppingCartService.save(shoppingCart); cartServiceOne=shoppingCart; } return R.success(cartServiceOne); } //购物车减少 @PostMapping("/sub") @Transactional public R<ShoppingCart> sub(@RequestBody ShoppingCart shoppingCart){ Long dishId = shoppingCart.getDishId(); LambdaQueryWrapper<ShoppingCart> queryWrapper = new LambdaQueryWrapper<>(); //代表数量减少的是菜品数量 if (dishId != null){ //通过dishId查出购物车对象 queryWrapper.eq(ShoppingCart::getDishId,dishId); //这里必须要加两个条件,否则会出现用户互相修改对方与自己购物车中相同套餐或者是菜品的数量 queryWrapper.eq(ShoppingCart::getUserId,BaseContext.getCurrentId()); ShoppingCart cart1 = shoppingCartService.getOne(queryWrapper); cart1.setNumber(cart1.getNumber()-1); Integer LatestNumber = cart1.getNumber(); if (LatestNumber > 0){ //对数据进行更新操作 shoppingCartService.updateById(cart1); }else if(LatestNumber == 0){ //如果购物车的菜品数量减为0,那么就把菜品从购物车删除 shoppingCartService.removeById(cart1.getId()); }else if (LatestNumber < 0){ return R.error("操作异常"); } return R.success(cart1); } Long setmealId = shoppingCart.getSetmealId(); if (setmealId != null){ //代表是套餐数量减少 queryWrapper.eq(ShoppingCart::getSetmealId,setmealId).eq(ShoppingCart::getUserId,BaseContext.getCurrentId()); ShoppingCart cart2 = shoppingCartService.getOne(queryWrapper); cart2.setNumber(cart2.getNumber()-1); Integer LatestNumber = cart2.getNumber(); if (LatestNumber > 0){ //对数据进行更新操作 shoppingCartService.updateById(cart2); }else if(LatestNumber == 0){ //如果购物车的套餐数量减为0,那么就把套餐从购物车删除 shoppingCartService.removeById(cart2.getId()); }else if (LatestNumber < 0){ return R.error("操作异常"); } return R.success(cart2); } //如果两个大if判断都进不去 return R.error("操作异常"); } //查看购物车 @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); } //清空购物车 @DeleteMapping("/clean") public R<String> clean(){ LambdaQueryWrapper<ShoppingCart> queryWrapper=new LambdaQueryWrapper<>(); queryWrapper.eq(ShoppingCart::getUserId,BaseContext.getCurrentId()); shoppingCartService.remove(queryWrapper); return R.success("清空购物车成功"); }
5.用户下单
在开发业务功能前,先将需要用到的类和接口基本结构创建好:
实体类Orders、OrderDetail
package com.itheima.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.itheima.reggie.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; 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
@Mapper public interface OrderMapper extends BaseMapper<Orders> { }
@Mapper public interface OrderDetaiMapper extends BaseMapper<OrderDetail> { }
业务层接口OrderService、OrderDetailService
public interface OrderService extends IService<Orders> { //用户下单 public void submit(Orders orders); }
public interface OrderDetailService extends IService<OrderDetail> { }
业务层实现类OrderServicelmpl、OrderDetailServicelmpl
@Service public class OrderServiceImpl extends ServiceImpl<OrderMapper, Orders> implements OrderService { @Autowired private ShoppingCartService shoppingCartService; @Autowired private UserService userService; @Autowired private AddressBookService addressBookService; @Autowired private OrderDetailService orderDetailService; //用户下单 @Override @Transactional public void submit(Orders orders) { //获得当前用户id Long userId = BaseContext.getCurrentId(); //查询当前用户的购物车数据 LambdaQueryWrapper<ShoppingCart> wrapper=new LambdaQueryWrapper<>(); wrapper.eq(ShoppingCart::getUserId,userId); List<ShoppingCart> shoppingCarts = shoppingCartService.list(wrapper); 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(); 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.setOrderTime(LocalDateTime.now()); orders.setCheckoutTime(LocalDateTime.now()); orders.setStatus(2); orders.setAmount(new BigDecimal(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); } }
@Service public class OrderDateilServiceImpl extends ServiceImpl<OrderDetaiMapper, OrderDetail> implements OrderDetailService { }
控制层OrderController、OrderDetailController
@RestController @Slf4j @RequestMapping("/order") public class OrderController { @Autowired private OrderService orderService; //用户下单 @PostMapping("/submit") public R<String> submit(@RequestBody Orders orders){ log.info("订单数据:{}",orders); orderService.submit(orders); return R.success("下单成功"); } }
@RequestMapping("/orderDetail") @RestController @Slf4j public class OrderDetailController { @Autowired private OrderDetailService orderDetailService; }
美好的一天,到此结束,下次继续努力!