目录
一、地址簿管理
1.1 需求分析
1.2 数据模型
1.3 代码开发
准备工作,导入AddressBook实体类,检查一下实体类的属性是否和数据库中的表字段一一对应;
创建mapper:
package com.learn.reggie.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.learn.reggie.entity.AddressBook; import org.apache.ibatis.annotations.Mapper; @Mapper public interface AddressBookMapper extends BaseMapper<AddressBook> { }
package com.learn.reggie.service; import com.baomidou.mybatisplus.extension.service.IService; import com.learn.reggie.entity.AddressBook; public interface AddressBookService extends IService<AddressBook> { }
实现类:
package com.learn.reggie.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.learn.reggie.entity.AddressBook; import com.learn.reggie.mapper.AddressBookMapper; import com.learn.reggie.service.AddressBookService; import org.springframework.stereotype.Service; @Service public class AddressBookServiceImpl extends ServiceImpl<AddressBookMapper, AddressBook> implements AddressBookService { }
controller层编写:
package com.learn.reggie.controller; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.learn.reggie.common.BaseContext; import com.learn.reggie.common.R; import com.learn.reggie.entity.AddressBook; import com.learn.reggie.service.AddressBookService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.CollectionUtils; 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); 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)); } }
二、手机端展示
2.1 菜品展示
前端重点代码:
//获取所有的菜品分类 function categoryListApi() { return $axios({ 'url': '/category/list', 'method': 'get', }) } //获取购物车内商品的集合 function cartListApi(data) { return $axios({ 'url': '/shoppingCart/list', 'method': 'get', params:{...data} }) }
我们发现前端的展示页面中请求到了category的数据,服务器也响应了数据给前端页面,但是我们也看见了手机端并没有展示相关的套餐数据;这是因为在加载页面的时候,前端一共发了两个list请求,具体的请求看上面的前端代码,请求购物车信息的请求返回404,所以导致category的数据也没有被展示;
这里我们先在其他地方静态的接收购物车的请求,这样就可以先显示category的数据;先用假数据测试一下:
修改购物车的请求地址,
//获取购物车内商品的集合 function cartListApi(data) { return $axios({ //'url': '/shoppingCart/list', 'url':'/front/cartData.json', 'method': 'get', params:{...data} }) }
// 假数据文件 {"code":1,"msg":null,"data":[],"map":{}}
但是我们也发现了bug,就是展示的菜品没有对应的口味信息,比如甜度,辣度。。。我们之前是添加过相关的口味数据的;
这是因为我们在请求获取菜品信息的时候,我们返回的数据是R<List<Dish>> ,Dish这个类是没有相关的口味信息的,所以即便前端请求了这个口味的信息,但是后端是没有给它返回的,所以体现在前端就是口味信息没有展示出来;所以我们需要对DishController中的list接口进行修改;
bug修复:
//方法改造 @GetMapping("/list") public R<List<DishDto>> list(Dish dish){ //会自动映射的 //这里可以传categoryId,但是为了代码通用性更强,这里直接使用dish类来接受(因为dish里面是有categoryId的),以后传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(); //为一个新的对象赋值,一定要考虑你为它赋过几个值,否则你自己都不知道就返回了null的数据 //为dishDto对象的基本属性拷贝 BeanUtils.copyProperties(item,dishDto); Long categoryId = item.getCategoryId(); Category category = categoryService.getById(categoryId); if (category != null){ String categoryName = category.getName(); dishDto.setCategoryName(categoryName); } //为dishdto赋值flavors属性 //当前菜品的id Long dishId = item.getId(); //创建条件查询对象 LambdaQueryWrapper<DishFlavor> lambdaQueryWrapper = new LambdaQueryWrapper(); lambdaQueryWrapper.eq(DishFlavor::getDishId,dishId); //select * from dish_flavor where dish_id = ? //这里之所以使用list来条件查询那是因为同一个dish_id 可以查出不同的口味出来,就是查询的结果不止一个 List<DishFlavor> dishFlavorList = dishFlavorService.list(lambdaQueryWrapper); dishDto.setFlavors(dishFlavorList); return dishDto; }).collect(Collectors.toList()); return R.success(dishDtoList); }
2.2 套餐展示
套餐展示的前端请求地址和携带的参数:
在SetmealController中添加相应的方法来接收前端的请求:
/** * 根据条件来查询套餐数据 * @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); }
三、购物车功能
3.1 需求分析
3.2 数据模型
3.3 代码开发
加入购物车功能:
加入购物车的前端请求和携带的参数:
开发准备工作:
导入实体类ShoppingCart;
创建对应的mapper:
package com.learn.reggie.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.learn.reggie.entity.ShoppingCart; import org.apache.ibatis.annotations.Mapper; /** * @author 咕咕猫 * @version 1.0 */ @Mapper public interface ShoppingCartMapper extends BaseMapper<ShoppingCart> { }
创建service:
package com.learn.reggie.service; import com.baomidou.mybatisplus.extension.service.IService; import com.learn.reggie.entity.ShoppingCart; /** * @author 咕咕猫 * @version 1.0 */ public interface ShoppingCartService extends IService<ShoppingCart> { }
实现类:
package com.learn.reggie.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.learn.reggie.entity.ShoppingCart; import com.learn.reggie.mapper.ShoppingCartMapper; import com.learn.reggie.service.ShoppingCartService; import org.springframework.stereotype.Service; /** * @author 咕咕猫 * @version 1.0 */ @Service public class ShoppingCartServiceImpl extends ServiceImpl<ShoppingCartMapper, ShoppingCart> implements ShoppingCartService { }
controller层开发:
package com.learn.reggie.controller; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.learn.reggie.common.BaseContext; import com.learn.reggie.common.R; import com.learn.reggie.entity.ShoppingCart; import com.learn.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; /** * 购物车 * @author 咕咕猫 * @version 1.0 */ @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) { //如果已经存在,就在原来的数量上+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); } }
功能测试:
不过这个只能把菜品或者是套餐加入到购物车,那个减号点了是没效果的;
查看购物车
把相关的前端请求地址给改掉, 不再请求假数据,至于存放假数据的json文件是否删除,看你自己,删不删都没什么影响;
//获取购物车内商品的集合 function cartListApi(data) { return $axios({ 'url': '/shoppingCart/list', //'url':'/front/cartData.json', 'method': 'get', params:{...data} }) }
注意:一定要有用户的概念,不同用户看到的购物车是不一样的!!!
controller:
/** * 查看购物车 * @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); }
功能测试:
清空购物车
/** * 清空购物车 * @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("清空购物车成功"); }
四、用户下单
4.1 需求分析
注意:这里只是把用户的支付订单保存到数据库,因为真正的支付功能是需要去申请支付资质的,个人用户很难申请到;
4.2 数据模型
用户下单业务对应的数据表为order表和order_detail表:
order:订单表;
order_detail:订单明细表
4.3 代码开发
前端和服务器的交互过程:
第一次交互:
然后点击去支付:然后前端就会发生http://localhost:8080/order/submit这个请求;并且携带三个参数:一个是地址,一个是字符方式,一个是客户的备注;
开发准备工作:
导入实体类:order,order_details
创建mapper:
package com.learn.reggie.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.learn.reggie.entity.Orders; import org.apache.ibatis.annotations.Mapper; /** * @author 咕咕猫 * @version 1.0 */ @Mapper public interface OrderMapper extends BaseMapper<Orders> { }
package com.learn.reggie.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.learn.reggie.entity.OrderDetail; import org.apache.ibatis.annotations.Mapper; /** * @author 咕咕猫 * @version 1.0 */ @Mapper public interface OrderDetailMapper extends BaseMapper<OrderDetail> { }
创建service及其实现:
package com.learn.reggie.service; import com.baomidou.mybatisplus.extension.service.IService; import com.learn.reggie.entity.Orders; /** * @author 咕咕猫 * @version 1.0 */ public interface OrderService extends IService<Orders> { //用户下单 public void submit(Orders orders); }
package com.learn.reggie.service; import com.baomidou.mybatisplus.extension.service.IService; import com.learn.reggie.entity.OrderDetail; /** * @author 咕咕猫 * @version 1.0 */ public interface OrderDetailService extends IService<OrderDetail> { }
package com.learn.reggie.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.itheima.reggie.entity.*; import com.itheima.reggie.mapper.OrderMapper; import com.itheima.reggie.service.*; import org.springframework.stereotype.Service; @Service public class OrderServiceImpl extends ServiceImpl<OrderMapper, Orders> implements OrderService { }
package com.learn.reggie.service.impl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.itheima.reggie.entity.OrderDetail; import com.itheima.reggie.mapper.OrderDetailMapper; import com.itheima.reggie.service.OrderDetailService; import org.springframework.stereotype.Service; @Service public class OrderDetailServiceImpl extends ServiceImpl<OrderDetailMapper, OrderDetail> implements OrderDetailService { }
controller层开发:
前端请求的地址和携带的参数前面已经分析过了;
package com.learn.reggie.controller; import com.learn.reggie.common.R; import com.learn.reggie.entity.Orders; import com.learn.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; /** * 订单 * @author 咕咕猫 * @version 1.0 */ @Slf4j @RestController @RequestMapping("/order") public class OrderController { @Autowired private OrderService orderService; /** * 用户下单 * @param orders * @return */ @PostMapping("/submit") public R<String> submit(@RequestBody Orders orders){ log.info("订单数据:{}",orders); orderService.submit(orders); return R.success("下单成功"); } }
service添加submit方法:
/** * 用户下单 * @param orders */ public void submit(Orders orders);
方法的实现:
package com.learn.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.learn.reggie.common.BaseContext; import com.learn.reggie.common.CustomException; import com.learn.reggie.entity.*; import com.learn.reggie.mapper.OrderMapper; import com.learn.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; /** * @author 咕咕猫 * @version 1.0 */ @Slf4j @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; /** * 用户下单 * @param orders * @return */ @Transactional 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(); //订单号 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.setId(orderId); 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(queryWrapper); } }