目录
一、导入用户地址簿相关功能代码
1.1 需求分析
1.2 数据库对应的表
1.3 实体类
1.4 控制层
二、菜品展示
2.1 修改列表接口
2.2 设置对应接口查询套餐信息
三、购物车
3.1 购物车数据模型
3.2 代码开发
3.2.1 实体类
3.2.2 添加购物车
3.2.3 查看购物车
3.2.4 清空购物车
四、用户下单
4.1 数据模型
4.1.1 订单表
4.1.2 订单明细表
4.2 用户下单代码开发
4.2.1 订单实体类
4.2.2 订单明细实体类
4.2.3 代码开发
一、导入用户地址簿相关功能代码
1.1 需求分析
地址簿,指的是移动消费者用户的地址信息,用户登录成功后可以维护自己的地址信息。同一个用户可以有多个地址信息,但是只能有一个默认地址。
1.2 数据库对应的表
1.3 实体类
/**
* 地址簿
*/
@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;
}
1.4 控制层
/**
* 地址簿管理
*/
@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 修改列表接口
// /**
// * 根据条件查询对应的菜品数据
// * <p>
// * 传入一个Dish对象 比传入一个 categoryId通用性更好
// *
// * @param dish
// * @return
// */
// @GetMapping("/list")
// public R<List<Dish>> list(Dish dish) {
// LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
// queryWrapper.eq(dish.getCategoryId() != null, Dish::getCategoryId, dish.getCategoryId())
起售
// .eq(Dish::getStatus, 1)
// .orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);
// List<Dish> dishList = dishService.list(queryWrapper);
//
// return R.success(dishList);
// }
/**
* 根据条件查询对应的菜品数据
* <p>
* 传入一个Dish对象 比传入一个 categoryId通用性更好
*
* @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())
// 起售
.eq(Dish::getStatus, 1)
.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);
List<Dish> dishList = dishService.list(queryWrapper);
List<DishDto> dishDtoList = dishList.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);
}
// 查询对应的口味数据
Long dishId = item.getId();// 当前菜品id
LambdaUpdateWrapper<DishFlavor> queryWrapperFlavor = new LambdaUpdateWrapper<>();
queryWrapperFlavor.eq(DishFlavor::getDishId, dishId);
List<DishFlavor> list = dishFlavorService.list(queryWrapperFlavor);
dishDto.setFlavors(list);
return dishDto;
}).collect(Collectors.toList());
return R.success(dishDtoList);
}
}
2.2 设置对应接口查询套餐信息
/**
* 根据条件查询套餐数据
* @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())
.eq(setmeal.getStatus()!=null,Setmeal::getStatus,setmeal.getStatus())
.orderByDesc(Setmeal::getUpdateTime);
List<Setmeal> list = setmealService.list(queryWrapper);
return R.success(list);
}
三、购物车
移动端用户可以将菜品或者套餐添加到购物车。
对于菜品来说,如果设置了口味信息,则需要选择规格后才能加入购物车;
对于套餐来说,可以直接点击“+”将当前套餐加入购物车。在购物车中可以修改菜品和套餐的数量也可以清空购物车。
3.1 购物车数据模型
3.2 代码开发
页面需要发送三次请求
- 点击 “加入购物车” 或者 “+”按钮,页面发送ajax请求,请求服务端,将菜品或者套餐添加到购物车
- 点击购物车图标,页面发送ajax请求,请求服务端查询购物车中的菜品和套餐
- 点击清空购物车按钮,页面发送ajax请求,请求服务端来执行清空购物车操作
3.2.1 实体类
/**
* 购物车
*/
@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;
}
3.2.2 添加购物车
/**
* 添加购物车
*
* @param shoppingCart
* @return
*/
@PostMapping("/add")
public R<ShoppingCart> add(@RequestBody ShoppingCart shoppingCart) {
log.info("购物车数据:{}", shoppingCart);
// 设置用户id,指定当前是哪个用户的购物车数据
Long userId = BaseContext.getCurrentId();
// 查询当前菜品或者套餐是否在购物车当中
// 判断是菜品还是套餐的方法很简单,如果dishId是空,那就是套餐,反之则是菜品
Long dishId = shoppingCart.getDishId();
LambdaQueryWrapper<ShoppingCart> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ShoppingCart::getUserId, userId);
if (dishId != null) {
// 添加菜品
queryWrapper.eq(ShoppingCart::getDishId, dishId);
// ShoppingCart cartServiceOne = shoppingCartService.getOne(queryWrapper);
} else {
// 添加套餐
queryWrapper.eq(ShoppingCart::getSetmealId, shoppingCart.getSetmealId());
// ShoppingCart cartServiceOne = shoppingCartService.getOne(queryWrapper);
}
// 查询
ShoppingCart cartServiceOne = shoppingCartService.getOne(queryWrapper);
// 如果有,就在原来数量的基础上+1
if (cartServiceOne!=null){
Integer number = cartServiceOne.getNumber();
cartServiceOne.setNumber(number+1);
shoppingCartService.updateById(cartServiceOne);
}else {
// 如果没有,就默认数量为1
shoppingCart.setNumber(1);
shoppingCart.setUserId(userId);
shoppingCart.setCreateTime(LocalDateTime.now());
shoppingCartService.save(shoppingCart);
// 这一步是为了下面的返回值铺垫
cartServiceOne = shoppingCart;
}
return R.success(cartServiceOne);
}
3.2.3 查看购物车
/**
* 查看购物车
* @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);
}
3.2.4 清空购物车
/**
* 清空购物车
*
* @return
*/
@DeleteMapping
public R<String> clean() {
LambdaQueryWrapper<ShoppingCart> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ShoppingCart::getUserId, BaseContext.getCurrentId());
shoppingCartService.remove(queryWrapper);
return R.success("清空购物车成功");
}
四、用户下单
移动端用户将菜品或者套餐加入购物车后,可以点击购物车中的“去结算”按钮,页面跳转到订单确认页面,点击“去支付”按钮则完成下单的操作。
4.1 数据模型
用户下单业务对应的数据表为orders表和order_detail表:
- orders: 订单表
- order_detail: 订单明细表
4.1.1 订单表
4.1.2 订单明细表
4.2 用户下单代码开发
- 在购物车中点击 “去结算” 按钮,页面跳转到订单确认页面
- 在订单确认页面,发送ajax请求,请求服务端获取当前登录用户的默认地址
- 在订单确认页面,发送ajax请求,请求服务端获取当前登录用户的购物车数据
- 在订单确认页面点击"去支付",发送ajax请求,请求服务端完成下单操作(还没有写)
4.2.1 订单实体类
/**
* 订单
*/
@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;
}
4.2.2 订单明细实体类
/**
* 订单明细
*/
@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;
}
4.2.3 代码开发
/**
*
* @param orders 仅提交addressBookId、payMethod、remark三项
* 并没有提交购物车中的数据和用户的id
* 对于用户的id,我们可以从登录的用户的session中获取
* 对于购物车的数据,当我们知道用户id后,我们就可以进入数据库查询
* @return
*/
@PostMapping("/submit")
public R<String> submit(@RequestBody Orders orders){
ordersService.submit(orders);
return R.success("下单成功");
}
@Autowired
private OrderDetailService orderDetailService;
@Override
@Transactional
public void submit(Orders orders) {
// 获取当前用户id
Long currentId = BaseContext.getCurrentId();
// 查询当前用户的购物车数据
LambdaQueryWrapper<ShoppingCart> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ShoppingCart::getUserId, currentId);
List<ShoppingCart> shoppingCarts = shoppingCartService.list(queryWrapper);
if (shoppingCarts == null || shoppingCarts.size() == 0) {
throw new CustomException("购物车为空,不能下单");
}
// 查询用户数据
User user = userService.getById(currentId);
// 查询地址数据
Long addressBookId = orders.getAddressBookId();
AddressBook addressBook = addressBookService.getById(addressBookId);
if (addressBook == null) {
throw new CustomException("地址信息有误,不能下单");
}
// AtomicInteger是Java中的一个原子变量类,用于实现原子操作,保证多线程环境下的线程安全。它在数值操作上是整型的。
AtomicInteger amount = new AtomicInteger(0); //初始值为0,能保证在多线程的计算下也是没有问题的,使用int或者double在多线程的情况下会出现计算错误
List<OrderDetail> OrderDetails = shoppingCarts.stream().map((item) -> {
OrderDetail orderDetail = new OrderDetail();
orderDetail.setOrderId(currentId);
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()); //单份金额
// addAndGet 进行累加的,每次遍历都会加上item.getAmount().multiply(new BigDecimal(item.getNumber())).intValue()这个值
// item.getAmount() 是单份的金额, multiply是乘 new BigDecimal(item.getNumber()) 这个是份数
// intValue() 最后转成intValue
amount.addAndGet(item.getAmount().multiply(new BigDecimal(item.getNumber())).intValue());
return orderDetail;
}).collect(Collectors.toList());
// 向订单表插入数据(一条数据)
long orderId = IdWorker.getId(); //订单号
orders.setNumber(String.valueOf(orderId));
orders.setId(orderId);
orders.setOrderTime(LocalDateTime.now());
orders.setCheckoutTime(LocalDateTime.now());
orders.setStatus(2); //待派送
orders.setAmount(new BigDecimal(amount.get())); //订单总价
orders.setUserId(currentId);
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);
}
}