大致内容:
根据上篇笔记阿里云获取验证码进行登录移动端
一、在移动端显示地址簿(接收外卖的地址)、菜品展示(包括分类以及菜品详情)、购物车(点击购物车图标显示添加的商品)、提交订单(点击去结算,到订单详情)、退出当前账户、查看最近订单、再来一单(订单已完成时)
二、在客户端显示订单详情、操作订单状态
目录
1、在移动端显示地址簿(接收外卖的地址)
1.1、需求分析
--地址簿,指的是移动端消费者用户的地址信息,用户登录成功后可以维护自己的地址信息。同一个用户可以有多个地址信息,但是只能有一个默认地址。
--导入数据模型,配置相关接口和类
- 实体类AddressBook(直接从课程资料中导入即可)
- Mapper接口AddressBookMapper
- 业务层接口AddressBookService
- 业务层实现类AddressBookServicelmpl
- 控制层AddressBookController(直接从课程资料中导入即可)
1.2、代码开发
/**
* 地址簿管理
*/
@Slf4j
@RestController
@RequestMapping("/addressBook")
public class AddressBookController {
@Autowired
private AddressBookService addressBookService;
/**
* 新增
*/
@PostMapping
public R<AddressBook> save(@RequestBody AddressBook addressBook) {
addressBook.setUserId(BaseContextUtil.getNowId());
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, BaseContextUtil.getNowId());
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, BaseContextUtil.getNowId());
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(BaseContextUtil.getNowId());
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、菜品展示(包括分类以及菜品详情)
2.1、需求分析
用户登录成功后跳转到系统首页,在首页需要根据分类来展示菜品和套餐。如果菜品设置了口味信息需要展示 [选择规格] 按钮,否则显示 [+] 按钮。
2.2、代码开发
注意:首页加载完成后,还发送了一次ajax请求用于加载购物车数据,此处可以将这次请求的地址暂时修改一下,从静态json文件获取数据,等后续开发购物车功能时再修改回来,如下:
//获取购物车内商品的集合
function cartListApi(data) {
return $axios({
// 'url': '/shoppingCart/list',
'url':'/front/cartData.json',
'method': 'get',
params:{...data}
})
}
--需要显示具体的口味信息,所以我们在此改造DishController中的list方法
/**
* 根据分类id查询对应的菜品,并回显数据
* @param dish
* @return
*/
@GetMapping("/list")
public R<List<DishDto>> list(Dish dish){
LambdaQueryWrapper<Dish> qw = new LambdaQueryWrapper<>();
qw.eq(dish.getCategoryId()!=null,Dish::getCategoryId,dish.getCategoryId());
List<Dish> list = dishService.list(qw);
ArrayList<DishDto> dishDtos = new ArrayList<>();
for (Dish dish1:list){
Long id = dish1.getId();
DishDto dishDto = dishService.getWithFlavor(id);
dishDtos.add(dishDto);
}
return R.success(dishDtos);
}
}
--在SetmealController里添加list方法显示套餐信息
/**
* 在移动端显示套餐里面的套餐1、套餐2............
* @param setmeal
* @return
*/
@GetMapping("/list")
public R<List<Setmeal>>list(Setmeal setmeal){
LambdaQueryWrapper<Setmeal> qw = new LambdaQueryWrapper<>();
qw.eq(setmeal.getCategoryId()!=null,Setmeal::getCategoryId,setmeal.getCategoryId());
qw.eq(setmeal.getStatus()!=null,Setmeal::getStatus,setmeal.getStatus());
List<Setmeal> setmeals = setmealService.list(qw);
return R.success(setmeals);
}
/**
* 点击套餐显示套餐内部菜品详情
* @param id
* @return
*/
@GetMapping("/dish/{id}")
public R<SetmealDto>getSetMeal(@PathVariable Long id){
SetmealDto setmealDto = setmealService.getByIdWithDish(id);
return R.success(setmealDto);
}
效果展示:
3、购物车(点击购物车图标显示添加的商品)
3.1、需求分析
移动端用户可以将菜品或者套餐添加到购物车。对于菜品来说,如果设置了口味信息,则需要选择规格后才能加入购物车;对于套餐来说,可以直接点击 [+] 将当前套餐加入购物车。在购物车中可以修改菜品和套餐的数量,也可以清空购物车。
3.2、代码开发
- 实体类ShoppingCart(直接从课程资料中导入即可)
- Mapper接口ShoppingCartMapper
- 业务层接口ShoppingcartService
- 业务层实现类ShoppingCartServicelmpl
- 控制层ShoppingCartController
@RestController
@Slf4j
@RequestMapping("/shoppingCart")
public class ShoppingCartController{
@Autowired
private ShoppingCartService shoppingCartService;
/**
* 将商品添加购物车,需要注意:判断用户id,每个用户的购物车内容不一样,确保添加数据时准确对应数据库
* 注意判断数据库中是否存在该用户的该菜品/套餐数据,若存在直接在num加一,不存在则新建
* @param shoppingCart
* @return
*/
@PostMapping("/add")
public R<ShoppingCart>add(@RequestBody ShoppingCart shoppingCart){
log.info("加入购物车的内容:{}",shoppingCart);
//获取当前用户id,确保在数据库中添加购物车内容时不出错
Long userId = BaseContextUtil.getNowId();
shoppingCart.setUserId(userId);
LambdaQueryWrapper<ShoppingCart> qw = new LambdaQueryWrapper<>();
qw.eq(ShoppingCart::getUserId,userId);
if(shoppingCart.getDishId() != null){
//添加到购物车的是菜品------锁定当前用户userid,查找对应菜品id
qw.eq(ShoppingCart::getDishId,shoppingCart.getDishId());
}else {
//添加到购物车的是套餐--------锁定当前用户userid,查找对应套餐id
qw.eq(ShoppingCart::getSetmealId,shoppingCart.getSetmealId());
}
//已存在,根据条件查询
ShoppingCart shoppingCartOne = shoppingCartService.getOne(qw);
if (shoppingCartOne != null){
//当前用户数据库中已有该添加内容,直接在数量上加一
Integer number = shoppingCartOne.getNumber();
shoppingCartOne.setNumber(number+1);
shoppingCartService.updateById(shoppingCartOne);
}else {
//如果不存在
shoppingCart.setNumber(1);
shoppingCart.setCreateTime(LocalDateTime.now());
shoppingCartService.save(shoppingCart);
shoppingCartOne = shoppingCart;
}
return R.success(shoppingCartOne);
}
/**
* 点击购物车图标,显示购物车里面的商品
* @return
*/
@GetMapping("/list")
public R<List<ShoppingCart>>list(){
log.info("购物车.。。。。。。。。");
Long userId = BaseContextUtil.getNowId();
LambdaQueryWrapper<ShoppingCart> qw = new LambdaQueryWrapper<>();
qw.eq(ShoppingCart::getUserId,userId);
qw.orderByDesc(ShoppingCart::getCreateTime);
List<ShoppingCart> list = shoppingCartService.list(qw);
return R.success(list);
}
/**
* 删除购物车商品
* @param shoppingCart
* @return
*/
@PostMapping("/sub")
public R<ShoppingCart>sub(@RequestBody ShoppingCart shoppingCart){
LambdaQueryWrapper<ShoppingCart> qw = new LambdaQueryWrapper<>();
if (shoppingCart.getDishId()!=null) {
//菜品
qw.eq(ShoppingCart::getDishId,shoppingCart.getDishId());
}else {
//套餐
qw.eq(ShoppingCart::getSetmealId,shoppingCart.getSetmealId());
}
ShoppingCart cartServiceOne = shoppingCartService.getOne(qw);
if (cartServiceOne.getNumber()>1) {
cartServiceOne.setNumber(cartServiceOne.getNumber() - 1);
shoppingCartService.updateById(cartServiceOne);
}else {
cartServiceOne.setNumber(cartServiceOne.getNumber() - 1);
shoppingCartService.removeById(cartServiceOne);
}
return R.success(cartServiceOne);
}
/**
* 清空商品
* @return
*/
@DeleteMapping("/clean")
public R<String>clean(){
Long userId = BaseContextUtil.getNowId();
LambdaQueryWrapper<ShoppingCart> qw = new LambdaQueryWrapper<>();
qw.eq(ShoppingCart::getUserId,userId);
shoppingCartService.remove(qw);
// List<ShoppingCart> list = shoppingCartService.list();
// for (ShoppingCart shoppingCart:list){
// Long id = shoppingCart.getId();
// shoppingCartService.removeById(id);
// }
return R.success("清空成功");
}
}
4、提交订单(点击去结算,到订单详情)
4.1、需求分析
--移动端用户将菜品或者套餐加入购物车后,可以点击购物车中的 【去结算】 按钮,页面跳转到订单确认页面,点击 【去支付】 按钮则完成下单操作。
--用户下单业务对应的数据表为orders表和order_detail表:
4.2、代码开发
在开发代码之前,需要梳理一下用户下单操作时前端页面和服务端的交互过程:
1、在购物车中点击 【去结算】 按钮,页面跳转到订单确认页面
2、在订单确认页面,发送ajax请求,请求服务端获取当前登录用户的默认地址
3、在订单确认页面,发送ajax请求,请求服务端获取当前登录用户的购物车数据
4、在订单确认页面点击 【去支付】 按钮,发送ajax请求,请求服务端完成下单操作
开发用户下单功能,其实就是在服务端编写代码去处理前端页面发送的请求即可。
代码开发-准备工作
在开发业务功能前,先将需要用到的类和接口基本结构创建好:
- 实体类Orders、OrderDetail(直接从课程资料中导入即可)
- Mapper接口OrderMapper、OrderDetailMapper
- 业务层接口OrderService、OrderDetailService
- 业务层实现类OrderServicelmpl、OrderDetailServicelmpl
- 控制层OrderController、OrderDetailController
操作如下:
设计多表,自定义方法
在OrderController的submit方法处理post请求实现上面的方法
/**
* 提交订单--支付
* @param orders
* @return
*/
@PostMapping("/submit")
public R<String>submit(@RequestBody Orders orders){
log.info("去支付{}", orders);
ordersService.submit(orders);
return R.success("支付成功");
}
在OrderService添加submit方法用于用户下单
/**
* 提交订单:也就是在订单表和订单明细表插入数据
* @param orders
*/
// @Transactional
public void submit(Orders orders) {
//获得当前用户id
Long userId = BaseContextUtil.getNowId();
//查询当前用户的购物车数据
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();//MP提供的自动生成id订单号
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(wrapper);
}
}
5、代码优化-功能补充
5.1、需求分析
退出当前账户、查看最近订单、再来一单(订单已完成时)
在客户端显示订单详情、操作订单状态
5.2、代码开发
退出-清除session中保存的userid
/**
* 退出登录
* @param httpServletRequest
* @return
*/
@PostMapping("/loginout")
public R<String>loginOut(HttpServletRequest httpServletRequest){
//用户信息保存在当前会话session中,清除即可
httpServletRequest.getSession().removeAttribute("user");
return R.success("退出成功");
}
查看最近订单
导入OrderDto需手动添加private int sumNum;
(前端会计算数量)
在OrderController添加userPage方法
/**
* 查询订单详情,分页展示
*
* @param page
* @param pageSize
* @return
*/
@Transactional
@GetMapping("/userPage")
public R<Page> getOrderWithDetail(int page, int pageSize) {
//构造分页构造器
Page<Orders> pageInfo = new Page<>(page, pageSize);
Page<OrdersDto> ordersDtoPage = new Page<>();
//构造条件构造器
LambdaQueryWrapper<Orders> queryWrapper = new LambdaQueryWrapper<>();
//添加排序条件
queryWrapper.orderByDesc(Orders::getOrderTime);
//进行分页查询
ordersService.page(pageInfo,queryWrapper);
//对象拷贝
BeanUtils.copyProperties(pageInfo,ordersDtoPage,"records");
//获取原页面数据
List<Orders> records=pageInfo.getRecords();
//原数据缺少订单数量,进行更改
List<OrdersDto> list = records.stream().map((item) -> {
OrdersDto ordersDto = new OrdersDto();
BeanUtils.copyProperties(item, ordersDto);
Long Id = item.getId();
//根据id查分类对象
Orders orders = ordersService.getById(Id);
String number = orders.getNumber();
LambdaQueryWrapper<OrderDetail> lambdaQueryWrapper=new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(OrderDetail::getOrderId,number);
List<OrderDetail> orderDetailList = orderDetailService.list(lambdaQueryWrapper);
//初始化订单数
int num=0;
//从订单详情表OrderDetail获取number
for(OrderDetail detail:orderDetailList){
num+=detail.getNumber().intValue();
}
ordersDto.setSumNum(num);
return ordersDto;
}).collect(Collectors.toList());
//将修改之后的页面数据赋值
ordersDtoPage.setRecords(list);
return R.success(ordersDtoPage);
}
再来一单(订单已完成时)
在OrderController中添加again方法
//再来一单
@PostMapping("/again")
public R<String>again(@RequestBody Orders order){
log.info("再来一单");
//更改订单表
Long id = order.getId();
Orders orders = ordersService.getById(id);
long orderId = IdWorker.getId();//MP提供的自动生成id订单号
//将获取到的订单信息中的id 时间 等进行更改
//设置订单id
orders.setId(orderId);
orders.setNumber(String.valueOf(orderId));
orders.setOrderTime(LocalDateTime.now());
orders.setCheckoutTime(LocalDateTime.now());
orders.setStatus(2);
//将改好信息之后的订单表提交
ordersService.save(orders);
//更改订单详情表
LambdaQueryWrapper<OrderDetail> qw = new LambdaQueryWrapper<>();
qw.eq(OrderDetail::getOrderId,id);
List<OrderDetail> list = orderDetailService.list(qw);
list.stream().map((item)->{
//订单明细表id
long detailId = IdWorker.getId();
//设置订单表id
item.setOrderId(orderId);
//设置订单详情表id
item.setId(detailId);
return item;
}).collect(Collectors.toList());
orderDetailService.saveBatch(list);
return R.success("再来一单成功");
}
在客户端显示订单详情、操作订单状态
OrderController添加page方法处理get请求--显示订单详情
在OrderController处理post请求修改status--操作订单状态
/**
* 客户端显示订单详情
* @param page
* @param pageSize
* @param number
* @param beginTime
* @param endTime
* @return
*/
@GetMapping("/page")
public R<Page>page(int page,int pageSize,String number,String beginTime,String endTime){
Page<Orders> ordersPage = new Page<>(page,pageSize);
// Page<OrdersDto> ordersDtoPage = new Page<>();
LambdaQueryWrapper<Orders> qw = new LambdaQueryWrapper<>();
//条件1、根据number进行模糊查询
qw.like(StringUtils.isNotEmpty(number),Orders::getNumber,number);
//条件2、根据Datetime进行时间范围查询
if (beginTime!=null&&endTime!=null){
qw.le(Orders::getOrderTime,endTime);
qw.le(Orders::getOrderTime,beginTime);
}
//条件3、根据订单创建时间排序
qw.orderByDesc(Orders::getOrderTime);
//根据前面三个条件查询分页数据
ordersService.page(ordersPage,qw);
//原订单表里无UserName属性值,在此处添加
List<Orders> records = ordersPage.getRecords();
records.stream().map((item)->{
item.setUserName("用户"+item.getUserId());
return item;
}).collect(Collectors.toList());
return R.success(ordersPage);
}
/**、
* 修改订单状态
* @param orders
* @return
*/
@PutMapping
public R<String> send(@RequestBody Orders orders){
Long id = orders.getId();
Integer status = orders.getStatus();
Orders orders1 = ordersService.getById(id);
orders1.setStatus(status);
ordersService.updateById(orders1);
return R.success("派送成功");
}