Controller层的实现
Controller层主要实现四个方法:
创建订单,订单列表,订单详情,查询订单
@Slf4j
@RestController
@RequestMapping("/buyer/order")
public class BuyerOrderController {
@Autowired
private OrderService orderService;
@Autowired
private BuyerService buyerService;
/**
* 创建订单
*
* 注解验证:@Valid,对OrderForm中加了@NotEmpty注解的参数进行校验
* BindingResult收集验证不通过的错误信息
* BindingResult和@Valid配对使用,如果不加BindingResult会抛出异常信息,加了之后异常信息会放进bindingResult对象中
* @param orderForm
* @param bindingResult
* @return
*/
@PostMapping("/create")
public ResultVO<Map<String,String>> create(@Valid OrderForm orderForm, BindingResult bindingResult){
if(bindingResult.hasErrors()){
log.error("【创建订单】参数不正确,orderForm={}",orderForm);
throw new SellException(ResultEnum.PARAM_ERROR.getCode()
,bindingResult.getFieldError().getDefaultMessage());
}
//因为创建订单传入的参数为OrderDTO,因此需要类型转换
OrderDTO orderDTO = OrderForm2OrderDTO.convert(orderForm);
if(CollectionUtils.isEmpty(orderDTO.getOrderDetailList())){
log.error("【创建订单】购物车不能为空");
throw new SellException(ResultEnum.CART_EMPTY);
}
OrderDTO createResult = orderService.create(orderDTO);
//返回ResultVO<Map<String,String>>结果
Map<String,String> map = new HashMap<>();
map.put("orderId",createResult.getOrderId());
return ResultVOUtil.success(map);
}
//订单列表
@GetMapping("/list")
public ResultVO<List<OrderDTO>> list(
@RequestParam("openid")String openid,
@RequestParam(value = "page",defaultValue = "0")Integer page,
@RequestParam(value = "size",defaultValue = "10")Integer size){
if(StringUtils.isEmpty(openid)){
log.error("【查询订单列表】 openid为空");
throw new SellException(ResultEnum.PARAM_ERROR);
}
PageRequest request = PageRequest.of(page,size);
Page<OrderDTO> orderDTOPage = orderService.findList(openid, request);
return ResultVOUtil.success(orderDTOPage.getContent());
}
//订单详情
@GetMapping("/detail")
public ResultVO<OrderDTO> detail(@RequestParam("openid")String openid,
@RequestParam("orderId")String orderId){
OrderDTO orderDTO = buyerService.findOrderOne(openid, orderId);
return ResultVOUtil.success(orderDTO);
}
//取消订单
@PostMapping("/cancle")
public ResultVO cancle(@RequestParam("openid")String openid,
@RequestParam("orderId")String orderId){
buyerService.cancleOrder(openid, orderId);
return ResultVOUtil.success();
}
}
1. 创建订单API的实现
1、创建订单的API:
下面是创建订单的API,我们要做的就是根据这个API来写Controller层的代码:
2、方法返回值类型:
根据API返回的结果,可以将返回类型写成ResultVO<Map<String,String>>
:
3、方法的参数:
根据API的请求参数:name,phone,openid。。。但是不可能把这些参数都写进去,可以将这些封装在类OrderForm中,其中加上@Empty注解的是要校验的参数,验证注解的元素值不为null且不为空:
@Data
public class OrderForm {
/**
* 买家姓名
*/
@NotEmpty(message = "姓名必填")
private String name;
/**
* 买家手机号
*/
@NotEmpty(message = "手机号必填")
private String phone;
/**
* 买家地址
*/
@NotEmpty(message = "地址必填")
private String address;
/**
* 买家微信openid
*/
@NotEmpty(message = "openid必填")
private String openid;
/**
* 购物车
*/
@NotEmpty(message = "购物车不能为空")
private String items;
}
@Valid注解用于对OrderForm中加了@NotEmpty注解的参数进行校验,BindingResult收集验证不通过的错误信息,然后判断有没有错误:
4、创建订单:
现在创键订单方法的参数和返回值类型都确定了,下一步就是调用Service层的逻辑实现创建订单的操作了:
Service层中create方法传入的参数是OrderDTO,但是这里Controller层create方法传入的参数为OrderForm,因此需要进行类型转换:
@Slf4j
public class OrderForm2OrderDTO {
public static OrderDTO convert(OrderForm orderForm){
OrderDTO orderDTO = new OrderDTO();
orderDTO.setBuyerName(orderForm.getName());
orderDTO.setBuyerPhone(orderForm.getPhone());
orderDTO.setBuyerAddress(orderForm.getAddress());
orderDTO.setBuyerOpenid(orderForm.getOpenid());
//将前端中items属性的json格式转换为List集合
Gson gson = new Gson();
List<OrderDetail> orderDetailList = new ArrayList<>();
try {
orderDetailList = gson.fromJson(orderForm.getItems(),
new TypeToken<List<OrderDetail>>() {
}.getType());
} catch (JsonSyntaxException e) {
log.error("【对象转换】错误,string={}",orderForm.getItems());
throw new SellException(ResultEnum.PARAM_ERROR);
}
orderDTO.setOrderDetailList(orderDetailList);
return orderDTO;
}
}
类型转换后最好判断下前端传来的参数中的购物车内是否有西,有东西就进行创建订单:
5、返回结果:
6、进行前后端联调验证Controller层的代码:
如果使用浏览器访问会报错405,因为发送的是post请求,而我们需要有请求体,但是前段代码已经封装了,因此需要时使用一个软件Postman:
说明:如果中间有错误,建议前后端联调一下,看一下哪里出了问题,在后端打上断点,然后前端发送请求,就会跳到后端代码上去的。
2. 订单列表API的实现
1、订单列表API:
2、方法返回值类型:
根据API返回结果可以判断方法的返回值类型为ResultVO<List<OrderDTO>>
:
3、方法的参数:
根据API可得方法的参数有三个:openid,page,size
这里@RequestParam注解可以不用使用,但是如果想指定默认值需要使用。
判断下前端传来的请求参数openid不为空,如果不为空我们才可以根据openid显示订单列表:
4、查询订单类列表:
现在方法的返回值类型和参数都已经确定了,下面要做的就是根据Service层的逻辑去向数据库中分页查询订单列表:
5、返回查询结果:
orderDTOPage中封装的是查询的订单列表:
6、前后端联调校验:
3. 订单详情API的实现
1、订单详情API:
2、方法返回值类型:
根据API可得方法的返回值类型为ResultVO<OrderDTO>
3、方法参数:
根据API可得方法的参数有两个:openid,orderId
至于@RequestParam可以使用,也可以不使用,这个注解的作用是当前端传来的参数和方法的参数不一致时,还用这个注解来标注前端传来的参数:
4、查询商品详情:
现在方法的返回值和方法的参数已经确定,下面就是调用Service层的逻辑来查询商品详情:
但是为了增强程序的安全,我们希望通过openid和orderId来查询某个订单详情,而不是只通过orderId来查询,否则只要活得orderId就能查到任何一个人的订单了,因此写一个接口Buyer接口来验证买家openid和orderId,(也可以写在Controller层,但是希望业务逻辑都写在Service层):
public interface BuyerService {
//查询一个订单
OrderDTO findOrderOne(String openid,String orderId);
}
BuyerService的实现类:
@Service
@Slf4j
public class BuyerServiceImpl implements BuyerService {
@Autowired
private OrderService orderService;
public OrderDTO checkOrderOwner(String openid,String orderId){
OrderDTO orderDTO = orderService.findOnes(orderId);
if(orderDTO.getOrderId()==null){
return null;
}
//判断是否为自己的订单
if(!orderDTO.getBuyerOpenid().equalsIgnoreCase(openid)){
log.error("【查询订单】订单的openid不一致,openid={}",openid);
throw new SellException(ResultEnum.ORDER_OWNER_ERROR);
}
return orderDTO;
}
@Override
public OrderDTO findOrderOne(String openid, String orderId) {
return checkOrderOwner(openid,orderId);
}
}
下面调用BuyerService的方法实现查询订单,并返回结果:
5、前后端联调:
4. 取消订单API的实现
1、取消订单API:
2、取消订单:
先来看下Service层的业务逻辑:
同样为了增强程序的安全性,在BuyerService接口中加一个取消订单的方法:
public interface BuyerService {
//取消订单
OrderDTO cancleOrder(String openid,String orderId);
}
@Service
@Slf4j
public class BuyerServiceImpl implements BuyerService {
@Autowired
private OrderService orderService;
public OrderDTO checkOrderOwner(String openid,String orderId){
OrderDTO orderDTO = orderService.findOnes(orderId);
if(orderDTO.getOrderId()==null){
return null;
}
//判断是否为自己的订单
if(!orderDTO.getBuyerOpenid().equalsIgnoreCase(openid)){
log.error("【查询订单】订单的openid不一致,openid={}",openid);
throw new SellException(ResultEnum.ORDER_OWNER_ERROR);
}
return orderDTO;
}
@Override
public OrderDTO cancleOrder(String openid, String orderId) {
OrderDTO orderDTO = checkOrderOwner(openid, orderId);
if(orderDTO==null){
log.error("【取消订单】查不到该订单,orderId={}",orderId);
throw new SellException(ResultEnum.ORDER_NOT_EXIST);
}
return orderService.cancel(orderDTO);
}
}
调用BuyerService接口中的方法取消订单:
其次,数据为null的对象我们不想显示,比如data,可以在application.proertites中加上:
#对象为null的字段不显示在页面
spring.jackson.default-property-inclusion=non_null
3、前后端联调:
此时数据库中的订单状态为0:
订单取消成功后,数据库中的订单状态为2: