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