Ordering system - buyer side - order

Keywords: JSON Mobile Google Attribute

Article catalog

DAO layer design

1. Write two entity classes, OrderMaster and OrderDetail. Because it involves the update of the order modification time, add the annotation @ DynamicUpdate, that is, modify the time data during the update
2. Write two enumeration classes, OrderStatusEnum and PayStatusEnum

//Order status
@Getter
public enum OrderStatusEnum {
    /**
     * New order
     */
    NEW(0, "New order"),
    /**
     * end
     */
    FINISHED(1, "end"),
    /**
     * Cancelled
     */
    CANCEL(2, "Cancelled"),
    ;

    private Integer code;
    private String message;

    OrderStatusEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
}
//Payment status
@Getter
public enum PayStatusEnum {
    /**
     * Waiting for payment
     */
    WAIT(0, "Waiting for payment"),
    /**
     * Payment successful
     */
    SUCCESS(1, "Payment successful");

    private Integer code;
    private String message;

    PayStatusEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
}

3. Write two classes of DAO layer, OrderMasterRepository and OrderDetailRepository

public interface OrderMasterRepository extends JpaRepository<OrderMaster,String> {
    /**
     * Page by page display, through the buyer's openid to find his order information
     */
    Page<OrderMaster> findByBuyerOpenid(String buyerOpenid, Pageable pageable);
}
public interface OrderDetailRepository extends JpaRepository<OrderDetail,String> {
    List<OrderDetail> findByOrderId(String orderId);
}

4. Unit test two classes of persistence layer

Service layer design

1. Create OrderService interface

@Service
public interface OrderService {

    /**
     * Create order
     */
    OrderDTO create(OrderDTO orderDTO);

    /**
     * Query order according to order ID
     */
    OrderDTO findOne(String orderId);

    /**
     * Query all his orders through the buyer's openid, and return by page
     */
    Page<OrderDTO> findList(String buyerOpenid, Pageable pageable);

    /**
     * cancellation of order
     */
    OrderDTO cancel(OrderDTO orderDTO);

    /**
     * Close order
     */
    OrderDTO finish(OrderDTO orderDTO);

    /**
     * Payment order
     */
    OrderDTO paid(OrderDTO orderDTO);
}

2. Create the OrderDTO class, which contains all the data of orderMaster and adds the List collection

@Data
public class OrderDTO {
    /**
     * Order id
     */
    private String orderId;

    /**
     * Buyer's name
     */
    private String buyerName;

    /**
     * Buyer's mobile number
     */
    private String buyerPhone;

    /**
     * Buyer's address
     */
    private String buyerAddress;

    /**
     * Buyer wechat Openid
     */
    private String buyerOpenid;

    /**
     * Total order amount
     */
    private BigDecimal orderAmount;

    /**
     * Order status, default to 0 new order
     */
    private Integer orderStatus ;

    /**
     * Payment status, default to 0 unpaid
     */
    private Integer payStatus ;

    /**
     * Creation time
     */
    private Date createTime;

    /**
     * Update time
     */
    private Date updateTime;

    private List<OrderDetail> orderDetailList;
}

According to order_master and order_ The one to many connection between details should have added a field to the OrderMaster entity class for one to many connection, but in order not to be too confusing here, the dto package (data transmission) was created

3. Create the OrderServiceImpl class (code omitted) to implement the OrderService interface

4. Custom exceptions are involved in the OrderServiceImpl class, so we create a SellException class here and put it into the Exception package

public class SellException extends RuntimeException {

    private Integer code;

    public SellException(ResultEnum resultEnum) {
        super(resultEnum.getMessage());
        this.code = resultEnum.getCode();
    }
}

5. Because there are many types of exceptions, we create an enumeration class, ResultEnum. Put all the available exceptions in the enumeration class and call it. The enumeration class can access the data but cannot modify it. So we use the @ Getter tag

@Getter
public enum ResultEnum {
    /**
     * Item does not exist
     */
    Product_not_exist(10,"Item does not exist"),
    ;
    
    private Integer code;
    private String message;

    ResultEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
}

6. Because the order ID is unique, create a tool class KeyUtil and put it into the Utils package. Because the creation of the unique ID will inevitably involve multiple threads, it is necessary to add synchronized

public class KeyUtil {

    /**
     * Generate unique Key
     */
    public static synchronized String genUniqueKey() {
        Random random = new Random();
        Integer a = random.nextInt(900000) + 10000;
        return System.currentTimeMillis() + String.valueOf(a);
    }
}

7. When it comes to the increase and decrease of inventory in the commodity table, return to ProductService to complete these two functions. Adding and subtracting inventory involves two data, namely commodity ID and quantity, so we can create a new class to store the two data (CartDTO)

@Data
public class CartDTO {

    /**
     * Commodity id
     */
    private String productId;

    /**
     * Quantity of goods
     */
    private Integer productQuantity;
}

Controller layer design

The specific methods are as follows: Specification and usage of api

1.API analysis

Create order
POST /sell/buyer/order/create
//parameter
name: "Zhang San"
phone: "18868822111"
address: "Muke headquarters"
openid: "ew3euwhd7sjw9diwkq" //User's wechat openid
items: [{
    productId: "1423113435324",
    productQuantity: 2 //Purchase quantity
}]

//return
{
  "code": 0,
  "msg": "success",
  "data": {
      "orderId": "147283992738221" 
  }
}

//Order list
GET /sell/buyer/order/list
//parameter
openid: 18eu2jwk2kse3r42e2e
page: 0 //From page 0
size: 10
//return
{
  "code": 0,
  "msg": "success",
  "data": [
    {
      "orderId": "161873371171128075",
      "buyerName": "Zhang San",
      "buyerPhone": "18868877111",
      "buyerAddress": "Muke headquarters",
      "buyerOpenid": "18eu2jwk2kse3r42e2e",
      "orderAmount": 0,
      "orderStatus": 0,
      "payStatus": 0,
      "createTime": 1490171219,
      "updateTime": 1490171219,
      "orderDetailList": null
    }]
}

//Query order details
GET /sell/buyer/order/detail
//parameter
openid: 18eu2jwk2kse3r42e2e
orderId: 161899085773669363
//return
{
    "code": 0,
    "msg": "success",
    "data": {
          "orderId": "161899085773669363",
          "buyerName": "Li Si",
          "buyerPhone": "18868877111",
          "buyerAddress": "Muke headquarters",
          "buyerOpenid": "18eu2jwk2kse3r42e2e",
          "orderAmount": 18,
          "orderStatus": 0,
          "payStatus": 0,
          "createTime": 1490177352,
          "updateTime": 1490177352,
          "orderDetailList": [
            {
                "detailId": "161899085974995851",
                "orderId": "161899085773669363",
                "productId": "157875196362360019",
                "productName": "Signature milk tea",
                "productPrice": 9,
                "productQuantity": 2,
                "productIcon": "http://xxx.com",
                "productImage": "http://xxx.com"
            }
        ]
    }
}

//cancellation of order
POST /sell/buyer/order/cancel
//parameter
openid: 18eu2jwk2kse3r42e2e
orderId: 161899085773669363
//return
{
    "code": 0,
    "msg": "success",
    "data": null
}
//Get openid
//Redirect to /sell/wechat/authorize
//parameter
returnUrl: http://xxx.com/abc  //[required]
//return
http://xxx.com/abc?openid=oZxSYw5ldcxv6H0EU67GgSXOUrVg

2. Create the BuyerOrderController class and complete the four API methods

@RestController
@RequestMapping("/buyer/order")
@Slf4j
public class BuyerOrderController {

    @Autowired
    private OrderService orderService;

    //Create order
    //@Valid: enable data validation. If the field validation fails, the information is bound to the BindingResult defined later;
    //Note that @ Valid and BindingResult correspond one by one. If there are multiple @ Valid, then each @ Valid needs to be followed by a BindingResult to receive the verification information in the bean
    @GetMapping("/create")
    public ResultVO<Map<String,String>> create(@Valid OrderForm orderForm,
                                                BindingResult bindingResult){

        if(bindingResult.hasErrors()){
            log.error("[The parameter of create order] is incorrect, orderForm={}",orderForm);
            throw new SellException(ResultEnum.PARAM_ERROR.getCode(),
                    bindingResult.getFieldError().getDefaultMessage());
        }

        //Convert orderForm to OrderDTO type
        OrderDTO orderDTO = OrderForm2OrderDTOConverter.convert(orderForm);
        if(CollectionUtils.isEmpty(orderDTO.getOrderDetailList())){
            log.error("[Create order] shopping cart cannot be empty");
            throw new SellException(ResultEnum.CART_EMPTY);
        }
        OrderDTO createResult = orderService.create(orderDTO);
        Map<String,String> map = new HashMap<>();
        map.put("orderId",createResult.getOrderId());

        return ResultVOUtil.success(map);
    }

    //Order list
    @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("[Query order list] openid Empty");
            throw new SellException(ResultEnum.PARAM_ERROR);
        }

        PageRequest request = PageRequest.of(page,size);
        Page<OrderDTO> orderDTOPage = orderService.findList(openid,request);

        return ResultVOUtil.success(orderDTOPage.getContent());
    }

    //Order details
    @GetMapping("/detail")
    public ResultVO<OrderDTO> detail(@RequestParam("openid") String openid,
                                     @RequestParam("orderid") String orderid){
        //TODO unsafe, improved
        OrderDTO orderDTO = orderService.findOne(openid);
        return ResultVOUtil.success(orderDTO);

    }

    //cancellation of order
    @PostMapping("/cancel")
    public ResultVO cancel (@RequestParam("openid") String openid,
                            @RequestParam("orderid") String orderid) {

        //TODO is not safe
        OrderDTO orderDTO = orderService.findOne(orderid);
        orderService.cancel(orderDTO);

        return ResultVOUtil.success();
    }
}

3. In the foreground, a post is the form data, so we can create an OrderForm class and put it under the form package to verify the form
See: Form Validation

Here, we use the method of JSON conversion, so we introduce Gson (JSON type string is converted to object through Gson)

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
</dependency>

4. Convert Date and Long, and create a new serialization class serializer (JsonSerializer: convert the attributes output to the front end)

public class Date2LongSerializer extends JsonSerializer<Date> {

    @Override
    public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        gen.writeNumber(value.getTime()/1000);
    }
}

Then annotate the attribute to be converted with @ jsonserialize (using = date2 LongSerializer.class )Just

/**
 * Creation time
 */
@JsonSerialize(using = Date2LongSerializer.class)
private Date createTime;

/**
 * Update time
 */
@JsonSerialize(using = Date2LongSerializer.class)
private Date updateTime;

5. Multiple solutions to make the parameters returned to the front end cannot be null

1) Parameter setting a default value
2) Label the class

@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class OrderDTO {
}

Or set to application.yml Setting in

spring:
  jackson:
    default-property-inclusion: non_null

Posted by joshmaker on Fri, 12 Jun 2020 21:32:50 -0700