目录
难点-代码复杂下单功能
项目基于spring boot,同样都是需要mvc三层架构+实体bean类。
在这先抛出几个问题。如何添加购物车,如何查询购物车,如何删除购物车。
- 在数据库这块,先分析前段保存的数据,与数据库对应的关系
- F12查看前端传输的数据,可以查看到传输的路径和相对应的信息
———————————————————————————————————————————
在理清逻辑之后,开始bean类的构建,继承Serializable接口,Long类型需要序列化的注解格式为:@JsonSerialize(using = ToStringSerializer.class)
*然后是mapper接口。
@Mapper
public interface ShoppingCartMapper extends BaseMapper<ShoppingCart> { }
*业务层接口 ShoppingCartService
public interface ShoppingCartService extends IService<ShoppingCart> { }
*业务层实现类 ShoppingCartServiceImpl
@Service
public class ShoppingCartServiceImpl extends ServiceImpl<ShoppingCartMapper, ShoppingCart> implements ShoppingCartService { }
控制层 ShoppingCartController
@Slf4j
@RestController
@RequestMapping("/shoppingCart")
public class ShoppingCartController {
@Autowired
private ShoppingCartService shoppingCartService;
}
———————————————————————————————————————————
添加查询删除购物车逻辑及其代码
在ShoppingCartController中创建add方法,
由于表的特殊性,来完成添加购物车的逻辑实现。
添加购物车。
@PostMapping("/add")
public R<ShoppingCart> add(@RequestBody ShoppingCart shoppingCart){
log.info("添加到购物车中的购物项:"+shoppingCart);
// 目标 shoppingCart 数据补全 存到数据库中
// 1:购物项少 登录人id id在呢呢 session域中
// 啥时候存的 登录时候存的 可以从session取 没毛病
// 过滤器在做登录权限校验的时候,把用户id放到了线程中。
// 还可以从当前线程中取出
Long userId = BaseContext.getCurrentId();
//存进去
shoppingCart.setUserId(userId);
//2: 这个购物项 用户点了几份?
/*
去数据库查,如果数据库没有,这是第一份。
数据库添加数据
如果数据库有,在原有的份数上+1。
数据库进行更新份数
*/
// 怎么查询该用户有没有添加过 该套餐/菜品呢?
// select * from shopping_cart where user_id=? and dish_id=?
// select * from shopping_cart where user_id=? and setmeal_id=?
LambdaQueryWrapper<ShoppingCart> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ShoppingCart::getUserId,userId);// select * from shopping_cart where user_id=?
//shoppingCart 添加购物项数据
if(shoppingCart.getDishId()!=null){ //添加的菜品
wrapper.eq(ShoppingCart::getDishId,shoppingCart.getDishId());
}else{//添加的套餐
wrapper.eq(ShoppingCart::getSetmealId,shoppingCart.getSetmealId());
}
// select * from shopping_cart where user_id=? and dish_id=?
// select * from shopping_cart where user_id=? and setmeal_id=?
// 查询 上面两个sql其中一个
ShoppingCart one = shoppingCartService.getOne(wrapper);
// one 就是 从数据库查出来 购物项(菜品or套餐)数据
if(one==null){//说明没有 需要添加 份数是1
shoppingCart.setNumber(1);
shoppingCart.setCreateTime(LocalDateTime.now());
//完成添加
shoppingCartService.save(shoppingCart);
// return R.success(shoppingCart);//返回添加到数据库的数据
}else{
// one 查出来的 购物项 原来的信息
// 需要更新数量
one.setNumber(one.getNumber()+1);
// 更新
shoppingCartService.updateById(one);
// return R.success(one);//返回 更新到数据库的数据
shoppingCart=one;
}
return R.success(shoppingCart);
}
删除购物车:
/**
* 清空指定用户的购物项
*/
@DeleteMapping("/clean")
public R<String> clean(){
log.info("清空用户的所有购物项");
// 当前用户指的是 谁登录是谁
Long userId = BaseContext.getCurrentId();
// delete from shoppingCart where user_id=?
LambdaQueryWrapper<ShoppingCart> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ShoppingCart::getUserId,userId);
shoppingCartService.remove(wrapper);
return R.success("清空成功");
}
查询购物车:
/**
* 查询 某个用户所有的购物项信息 在页面进行展示
*/
@GetMapping("/list")
public R<List> list(){
log.info("查询当前用户的 购物项信息!!");
// 当前用户指的是 谁登录是谁
Long userId = BaseContext.getCurrentId();
// 怎么查询该用户的购物项
// select * from shopping_cart where user_id = ?
LambdaQueryWrapper<ShoppingCart> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ShoppingCart::getUserId,userId);
List<ShoppingCart> list = shoppingCartService.list(wrapper);
return R.success(list);
}
购物车-1:
@PostMapping("/sub")
public R<ShoppingCart> sub(@RequestBody ShoppingCart shoppingCart){
log.info("需要更新的购物项(可能是菜品可能是套餐):"+shoppingCart);
/*
去数据库 根据 菜品id/套餐id 查询指定用户 的购物项信息
*/
Long userId = BaseContext.getCurrentId();
//2: 这个购物项 用户点了几份?
/*
去数据库查,查询当前购物项,更新份数 份数-1
检查更新后的份数
如果是 0 说明没了,数据库要进行删除。
如果不是0, 把最新的购物项数据更新到数据库
*/
LambdaQueryWrapper<ShoppingCart> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ShoppingCart::getUserId,userId);// select * from shopping_cart where user_id=?
//shoppingCart 添加购物项数据
if(shoppingCart.getDishId()!=null){ //添加的菜品
wrapper.eq(ShoppingCart::getDishId,shoppingCart.getDishId());
}else{//添加的套餐
wrapper.eq(ShoppingCart::getSetmealId,shoppingCart.getSetmealId());
}
// select * from shopping_cart where user_id=? and dish_id=?
// select * from shopping_cart where user_id=? and setmeal_id=?
// 查询 上面两个sql其中一个
ShoppingCart one = shoppingCartService.getOne(wrapper);
// 先做了更新 份数 份数-1
one.setNumber(one.getNumber()-1);
if(one.getNumber()==0){//没了 删除
// 根据条件删 delete from shopping_cart where user_id=? and dish_id=?
// delete from shopping_cart where user_id=? and setmeal_id=?
shoppingCartService.remove(wrapper);
}else{ // 还有 更新
shoppingCartService.updateById(one);
}
return R.success(one);
}
下单功能:
同样的,新建或者导入bean类还有三层结构。
bean 数据库表 mapper接口 service接口 cerviceImpl实现类 还有controller类
需求分析:
移动端用户将菜品或者套餐加入购物车后,可以点击购物车中的 "去结算" 按钮,页面跳转到订单确认页面,点击 "去支付" 按钮则完成下单操作。
个人的话由于没有企业资质,支付的接口先不做开发。
在开发代码之前,需要梳理一下用户下单操作时前端页面和服务端的交互过程:
基本逻辑:
A. 获得当前用户id, 查询当前用户的购物车数据
B. 根据地址ID, 查询地址数据
C. 组装订单明细数据, 计算订单总价值,批量保存订单明细
D. 组装订单数据(订单Id ,日期、状态、价值、订单编号等), 保存订单数据
E. 删除当前用户的购物车列表数据
由于下单的逻辑相对复杂,我们可以在OrderService中定义submit方法,来处理下单的具体逻辑
@Service
@Slf4j
public class OrdersServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements OrdersService {
@Autowired
private ShoppingCartService shoppingCartService;
@Autowired
private AddressBookService addressBookService;
@Autowired
private UserService userService;
@Autowired
private OrderDetailService orderDetailService;
@Override
@Transactional
public void submit(Orders orders) {
//1.1 查看 有什么数据
// 地址簿id
Long addressBookId = orders.getAddressBookId();
// 备注
String remark = orders.getRemark();
// 支付方式
Integer payMethod = orders.getPayMethod();
// 1.2 缺什么 根据已知条件能不能找到缺失内容
// 解决跟地址相关的数据 因为知道地址簿 就可找到地址相关数据
AddressBook addressBook = addressBookService.getById(addressBookId);
// 谁登录 就根据谁查询
//先获取用户id 登录的用户
Long userId = BaseContext.getCurrentId();
User user = userService.getById(userId);
//用uuid完成
//String number = UUID.randomUUID().toString().replace("-", "");
//String number = IdWorker.get32UUID(); 32位uuid
String number = IdWorker.getId()+"";// 雪花算法19位
LocalDateTime now = LocalDateTime.now();
// 1.3 把缺的补上
// 订单号
orders.setNumber(number);
// 用户id 用户名字
orders.setUserId(userId);
orders.setUserName(user.getName());// 没有名字不用做这一步...
// 地址簿相关信息
orders.setConsignee(addressBook.getConsignee());
orders.setPhone(addressBook.getPhone());
orders.setAddress(addressBook.getDetail());
// 下单和 结算时间
orders.setOrderTime(now);
orders.setCheckoutTime(now);
log.info("开始计算总金额");
//开始sql语句拼总账单金额。定义累加的总金额数据 初始值是0
BigDecimal amount = new BigDecimal(0);
// 总金额的获取 就 结合 购物项和订单转换了....
//2:把购物项信息转换成 订单信息 存到数据库中
// 2.1 查询 当前用户的 指定的所有的购物项信息
LambdaQueryWrapper<ShoppingCart> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ShoppingCart::getUserId,userId);
List<ShoppingCart> shoppingCarts = shoppingCartService.list(wrapper);
log.info("同时开始进行购物项和订单明细转换");
// shoppingCarts 是该用户的所有购物项信息
// 把所有的购物项 全部变成 所有的订单(明细)项集合
List<OrderDetail> orderDetails = new ArrayList<>();
//开始遍历了。
for (ShoppingCart shoppingCart : shoppingCarts) {
OrderDetail orderDetail = new OrderDetail();
BeanUtils.copyProperties(shoppingCart,orderDetail,"id");
// orderDetail 除了订单id shoppingCart里面都有
orderDetail.setOrderId(Long.parseLong(number));
orderDetails.add(orderDetail);
// 计算总金额
// 单价 和 份数
BigDecimal danjia = shoppingCart.getAmount();
BigDecimal fenshu = new BigDecimal(shoppingCart.getNumber());
BigDecimal xiaoji = danjia.multiply(fenshu);
//已经算出小计的价钱
amount = amount.add(xiaoji);
}
orders.setAmount(amount);
//得到了有数据的 orderDetails集合 保存
// 保存两个数据
//orders数据
this.save(orders);
orderDetailService.saveBatch(orderDetails);
// 3:清空购物车 写过这个功能
log.info("3:清空该用户的所有购物项");
// delete from shopping_cart where user_id=用户id
shoppingCartService.remove(wrapper);
}
}
需要仔细看看其中的逻辑信息。