章节感悟
1.Springboot的接收表单验证操作
2.json类型字符串通过Gson转换为对象
3.对输出到前端的属性做转换,JsonSerialize
4.使返回到前端的参数不能为null的多种解决方法
章节问答
Controller层设计
1.API分析
创建订单 POST /sell/buyer/order/create 参数 name: "张三" phone: "18868822111" address: "慕课网总部" openid: "ew3euwhd7sjw9diwkq" //用户的微信openid items: [{ productId: "1423113435324", productQuantity: 2 //购买数量 }] 返回 { "code": 0, "msg": "成功", "data": { "orderId": "147283992738221" } } 订单列表 GET /sell/buyer/order/list 参数 openid: 18eu2jwk2kse3r42e2e page: 0 //从第0页开始 size: 10 返回 { "code": 0, "msg": "成功", "data": [ { "orderId": "161873371171128075", "buyerName": "张三", "buyerPhone": "18868877111", "buyerAddress": "慕课网总部", "buyerOpenid": "18eu2jwk2kse3r42e2e", "orderAmount": 0, "orderStatus": 0, "payStatus": 0, "createTime": 1490171219, "updateTime": 1490171219, "orderDetailList": null }, { "orderId": "161873371171128076", "buyerName": "张三", "buyerPhone": "18868877111", "buyerAddress": "慕课网总部", "buyerOpenid": "18eu2jwk2kse3r42e2e", "orderAmount": 0, "orderStatus": 0, "payStatus": 0, "createTime": 1490171219, "updateTime": 1490171219, "orderDetailList": null }] } 查询订单详情 GET /sell/buyer/order/detail 参数 openid: 18eu2jwk2kse3r42e2e orderId: 161899085773669363 返回 { "code": 0, "msg": "成功", "data": { "orderId": "161899085773669363", "buyerName": "李四", "buyerPhone": "18868877111", "buyerAddress": "慕课网总部", "buyerOpenid": "18eu2jwk2kse3r42e2e", "orderAmount": 18, "orderStatus": 0, "payStatus": 0, "createTime": 1490177352, "updateTime": 1490177352, "orderDetailList": [ { "detailId": "161899085974995851", "orderId": "161899085773669363", "productId": "157875196362360019", "productName": "招牌奶茶", "productPrice": 9, "productQuantity": 2, "productIcon": "http://xxx.com", "productImage": "http://xxx.com" } ] } } 取消订单 POST /sell/buyer/order/cancel 参数 openid: 18eu2jwk2kse3r42e2e orderId: 161899085773669363 返回 { "code": 0, "msg": "成功", "data": null } 获取openid 重定向到 /sell/wechat/authorize 参数 returnUrl: http://xxx.com/abc //【必填】 返回 http://xxx.com/abc?openid=oZxSYw5ldcxv6H0EU67GgSXOUrVg
2.创建BuyerOrderController类,写上上面四个API方法,别忘记三个标签@RestController@RequestMapping("/buyer/order")@Slf4j,在方法上面可以写
package com.xiong.sell.service.impl; import com.xiong.sell.converter.OrderMaster2OrderDTOConverter; import com.xiong.sell.dataobject.OrderDetail; import com.xiong.sell.dataobject.OrderMaster; import com.xiong.sell.dataobject.ProductInfo; import com.xiong.sell.dto.CartDTO; import com.xiong.sell.dto.OrderDTO; import com.xiong.sell.enums.OrderStatusEnum; import com.xiong.sell.enums.ProductStatusEnum; import com.xiong.sell.enums.ResultEnum; import com.xiong.sell.exception.SellException; import com.xiong.sell.repository.OrderDetailRepository; import com.xiong.sell.repository.OrderMasterRepository; import com.xiong.sell.service.OrderService; import com.xiong.sell.service.ProductInfoService; import com.xiong.sell.utils.KeyUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import javax.transaction.Transactional; import java.math.BigDecimal; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; /** * @author Xiong YuSong * 2019/1/18 13:36 */ @Service @Slf4j public class OrderServiceImpl implements OrderService { @Autowired private ProductInfoService productInfoService; @Autowired private OrderDetailRepository orderDetailRepository; @Autowired private OrderMasterRepository orderMasterRepository; @Override @Transactional(rollbackOn = Exception.class) public OrderDTO create(OrderDTO orderDTO) { BigDecimal orderAmount = new BigDecimal(0); //生成orderID String orderId = KeyUtil.genUniqueKey(); //1.查询商品(这里只有商品编号和数量) for (OrderDetail orderDetail : orderDTO.getOrderDetailList()) { //根据Id查到商品之后 ProductInfo productInfo = productInfoService.findOne(orderDetail.getProductId()); if (productInfo == null) { throw new SellException(ResultEnum.PRODUCT_NOT_EXIST); } //2.计算订单总价 orderAmount = productInfo.getProductPrice() .multiply(new BigDecimal(orderDetail.getProductQuantity())) .add(orderAmount); //将订单详情放入数据库中,这里注意orderDetail里面的数据,时候是完整的,正确的 BeanUtils.copyProperties(productInfo, orderDetail); orderDetail.setDetailId(KeyUtil.genUniqueKey()); orderDetail.setOrderId(orderId); orderDetailRepository.save(orderDetail); } //3.写入订单数据库 OrderMaster orderMaster = new OrderMaster(); orderDTO.setOrderId(orderId); orderDTO.setOrderAmount(orderAmount); BeanUtils.copyProperties(orderDTO, orderMaster); orderMaster.setOrderStatus(OrderStatusEnum.NEW.getCode()); orderMaster.setPayStatus(ProductStatusEnum.PayStatusEnum.WAIT.getCode()); orderMasterRepository.save(orderMaster); //4.扣库存 List<CartDTO> cartDTOList = orderDTO.getOrderDetailList().stream().map(e -> new CartDTO(e.getProductId(), e.getProductQuantity())).collect(Collectors.toList()); productInfoService.decreaseStock(cartDTOList); return orderDTO; } @Override public OrderDTO findOne(String orderId) { Optional<OrderMaster> optional = orderMasterRepository.findById(orderId); if (!optional.isPresent()) { throw new SellException(ResultEnum.ORDER_NOT_EXIST); } OrderMaster orderMaster = optional.get(); List<OrderDetail> orderDetailList = orderDetailRepository.findByOrderId(orderId); if (CollectionUtils.isEmpty(orderDetailList)) { throw new SellException(ResultEnum.ORDERDETAIL_NOT_EXIST); } OrderDTO orderDTO = new OrderDTO(); BeanUtils.copyProperties(orderMaster, orderDTO); orderDTO.setOrderDetailList(orderDetailList); return orderDTO; } @Override public Page<OrderDTO> findList(String buyerOpenid, Pageable pageable) { Page<OrderMaster> orderMasterPage = orderMasterRepository.findByBuyerOpenid(buyerOpenid, pageable); List<OrderDTO> orderDTOList = OrderMaster2OrderDTOConverter.convert(orderMasterPage.getContent()); //PageImpl,泛型OrderDTO,需要参数为列表,pageable,以及总数 return new PageImpl<OrderDTO>(orderDTOList, pageable, orderMasterPage.getTotalElements()); } @Override @Transactional(rollbackOn = Exception.class) public OrderDTO cancel(OrderDTO orderDTO) { OrderMaster orderMaster = new OrderMaster(); //判断订单状态 if (!orderDTO.getOrderStatus().equals(OrderStatusEnum.NEW.getCode())) { log.error("【取消订单】订单状态不正确, orderId={}, orderStatus={}", orderDTO.getOrderId(), orderDTO.getOrderStatus()); throw new SellException(ResultEnum.ORDER_STATUS_ERROR); } //修改订单状态 orderDTO.setOrderStatus(OrderStatusEnum.CANCEL.getCode()); BeanUtils.copyProperties(orderDTO, orderMaster); OrderMaster updateResult = orderMasterRepository.save(orderMaster); if (updateResult == null) { log.error("【取消订单】更新失败, orderMaster={}", orderMaster); throw new SellException(ResultEnum.ORDER_UPDATE_FAIL); } //返回库存 if (CollectionUtils.isEmpty(orderDTO.getOrderDetailList())) { log.error("【取消订单】订单中无商品详情, orderDTO={}", orderDTO); throw new SellException(ResultEnum.ORDER_DETAIL_EMPTY); } List<CartDTO> cartDTOList = orderDTO.getOrderDetailList().stream() .map(e -> new CartDTO(e.getProductId(), e.getProductQuantity())) .collect(Collectors.toList()); productInfoService.increaseStock(cartDTOList); //如果已支付, 需要退款 if (orderDTO.getPayStatus().equals(ProductStatusEnum.PayStatusEnum.SUCCESS.getCode())) { //TODO } return orderDTO; } @Override @Transactional(rollbackOn = Exception.class) public OrderDTO finish(OrderDTO orderDTO) { //判断订单状态 if (!orderDTO.getOrderStatus().equals(OrderStatusEnum.NEW.getCode())) { log.error("【完结订单】订单状态不正确, orderId={}, orderStatus={}", orderDTO.getOrderId(), orderDTO.getOrderStatus()); throw new SellException(ResultEnum.ORDER_STATUS_ERROR); } //修改订单状态 orderDTO.setOrderStatus(OrderStatusEnum.FINISHED.getCode()); OrderMaster orderMaster = new OrderMaster(); BeanUtils.copyProperties(orderDTO, orderMaster); OrderMaster updateResult = orderMasterRepository.save(orderMaster); if (updateResult == null) { log.error("【完结订单】更新失败, orderMaster={}", orderMaster); throw new SellException(ResultEnum.ORDER_UPDATE_FAIL); } return orderDTO; } @Override @Transactional(rollbackOn = Exception.class) public OrderDTO paid(OrderDTO orderDTO) { //判断订单状态 if (!orderDTO.getOrderStatus().equals(OrderStatusEnum.NEW.getCode())) { log.error("【订单支付完成】订单状态不正确, orderId={}, orderStatus={}", orderDTO.getOrderId(), orderDTO.getOrderStatus()); throw new SellException(ResultEnum.ORDER_STATUS_ERROR); } //判断支付状态 if (!orderDTO.getPayStatus().equals(ProductStatusEnum.PayStatusEnum.WAIT.getCode())) { log.error("【订单支付完成】订单支付状态不正确, orderDTO={}", orderDTO); throw new SellException(ResultEnum.ORDER_PAY_STATUS_ERROR); } //修改支付状态 orderDTO.setPayStatus(ProductStatusEnum.PayStatusEnum.SUCCESS.getCode()); OrderMaster orderMaster = new OrderMaster(); BeanUtils.copyProperties(orderDTO, orderMaster); OrderMaster updateResult = orderMasterRepository.save(orderMaster); if (updateResult == null) { log.error("【订单支付完成】更新失败, orderMaster={}", orderMaster); throw new SellException(ResultEnum.ORDER_UPDATE_FAIL); } return orderDTO; } }
3.前台传过来的是一个post是表单数据,于是我们可以创建一个OrderForm类放在form包下进行表单验证,@NotEmpty(message = "姓名必填")
package com.xiong.sell.form; import lombok.Data; import javax.validation.constraints.NotEmpty; /** * @author Xiong YuSong * 2019/1/18 19:19 */ @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; }
4.这里涉及到了OrderForm到OrderDTO的转换,所以再建立一个转换类OrderForm2OrderDTOConverter
package com.xiong.sell.converter; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import com.xiong.sell.dataobject.OrderDetail; import com.xiong.sell.dto.OrderDTO; import com.xiong.sell.enums.ResultEnum; import com.xiong.sell.exception.SellException; import com.xiong.sell.form.OrderForm; import lombok.extern.slf4j.Slf4j; import java.util.ArrayList; import java.util.List; /** * @author Xiong YuSong * 2019/1/18 19:52 */ @Slf4j public class OrderForm2OrderDTOConverter { public static OrderDTO convert(OrderForm orderForm) { Gson gson = new Gson(); OrderDTO orderDTO = new OrderDTO(); orderDTO.setBuyerName(orderForm.getName()); orderDTO.setBuyerPhone(orderForm.getPhone()); orderDTO.setBuyerAddress(orderForm.getAddress()); orderDTO.setBuyerOpenid(orderForm.getOpenid()); List<OrderDetail> orderDetailList = new ArrayList<>(); try { orderDetailList = gson.fromJson(orderForm.getItems(), new TypeToken<List<OrderDetail>>() { }.getType()); } catch (Exception e) { log.error("【对象转换】错误, string={}", orderForm.getItems()); throw new SellException(ResultEnum.PARAM_ERROR); } orderDTO.setOrderDetailList(orderDetailList); } }
5.这里使用到了Json转类的方法,所以引入Gson,来完成
<dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> </dependency>
6.进行Date和Long的转换,新建一个序列化类serializer
package com.xiong.sell.utils.serializer; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import java.io.IOException; import java.util.Date; /** * @author Xiong YuSong * 2019/1/18 20:38 */ public class Date2LongSerializer extends JsonSerializer<Date> { @Override public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException { gen.writeNumber(value.getTime()/1000); } }
然后再需要转换的属行上@JsonSerialize(using = Date2LongSerializer.class)即可
/** * 创建时间. */ @JsonSerialize(using = Date2LongSerializer.class) private Date createTime; /** * 更新时间. */ @JsonSerialize(using = Date2LongSerializer.class) private Date updateTime;
7.此时我们让返回的参数不能为空,这个时候我们有两个方法
1)参数设置一个默认值
2)在类的前面加上标签
@Data @JsonInclude(JsonInclude.Include.NON_NULL) public class OrderDTO { }
或者设置在application.yml中设置
spring: datasource: driver-class-name: com.mysql.jdbc.Driver username: root password: 123456 url: jdbc:mysql://192.168.1.103/sell?characterEncoding=utf-8&useSSl=false jpa: show-sql: true jackson: default-property-inclusion: non_null server: servlet: context-path: /sell
8.在进行查看订单详情和取消订单的时候需要验证订单的主人的微信openid是不是传进来的openid,所以这里需要创建一个新的BuyerServer来进行身份验证
package com.xiong.sell.service; import com.xiong.sell.dto.OrderDTO; /** * @author Xiong YuSong * 2019/1/18 20:59 */ public interface BuyerService { /** * 查询一个订单 * @param openid * @param orderId * @return */ OrderDTO findOrderOne(String openid, String orderId); /** * 取消一个订单 * @param openid * @param orderId * @return */ OrderDTO cancelOrder(String openid, String orderId); }