OPEN, informational updates keep it open, ORDER_DELIVERED completes it, DELIVERY_CANCELLED cancels it, and closed orders should ignore later non-refund updates. OPEN state.OPEN order open: ORDER_GETTING_PREPARED, OUT_FOR_DELIVERY, RAIN_DELAY, DASHER_REACHED_DELIVERY_LOCATION, CUSTOMER_UNREACHABLE, and DASHER_UNREACHABLE.statusCode = ORDER_DELIVERED, the order becomes COMPLETED.statusCode = DELIVERY_CANCELLED, the order becomes CANCELLED.statusCode = WANT_REFUND and the order is OPEN, the order is cancelled after applying the refund rule.CANCELLED, later updates must not change it again.COMPLETED, later non-refund updates must be ignored.by field only affects logs.updateOrderStatus() returns a List<String>.KEY:VALUE.ORDER:<orderId>BY:<by>STATUS:<statusCode>ACTION:CONTINUE_ORDERACTION:COMPLETE_ORDERACTION:CANCEL_ORDERACTION:ISSUE_REFUNDACTION:IGNORED_ORDER_ALREADY_CLOSEDACTION:REFUND_ALREADY_ISSUEDACTION:NO_REFUND_RULE_MATCHLATE:true or LATE:false is present, it must appear immediately after the STATUS:... line.ACTION:ISSUE_REFUND is present, the corresponding REFUND_PERCENT:... line must appear immediately after it.REFUND_PERCENT:50, REFUND_PERCENT:95, or REFUND_PERCENT:100.List.of("ORDER:o1", "BY:DASHER", "STATUS:OUT_FOR_DELIVERY", "ACTION:CONTINUE_ORDER")
List.of("ORDER:o3", "BY:DASHER", "STATUS:ORDER_DELIVERED", "LATE:true", "ACTION:COMPLETE_ORDER", "ACTION:ISSUE_REFUND", "REFUND_PERCENT:50")
ORDER:<orderId>BY:<by>STATUS:<statusCode>ACTION:CONTINUE_ORDERstatusCode = ORDER_GETTING_PREPARED, mark that preparation has started.ORDER:<orderId>BY:<by>STATUS:ORDER_DELIVEREDLATE:true if currentTime > deliveryETA, else LATE:falseACTION:COMPLETE_ORDERACTION:ISSUE_REFUNDREFUND_PERCENT:50 or REFUND_PERCENT:100ORDER:<orderId>BY:<by>STATUS:DELIVERY_CANCELLEDACTION:CANCEL_ORDERACTION:ISSUE_REFUNDREFUND_PERCENT:100ORDER:<orderId>BY:<by>STATUS:WANT_REFUNDACTION:ISSUE_REFUNDREFUND_PERCENT:95ACTION:CANCEL_ORDERORDER:<orderId>BY:<by>STATUS:WANT_REFUNDACTION:ISSUE_REFUNDREFUND_PERCENT:100ACTION:CANCEL_ORDERORDER:<orderId>BY:<by>STATUS:WANT_REFUNDACTION:REFUND_ALREADY_ISSUEDORDER:<orderId>BY:<by>STATUS:WANT_REFUNDACTION:NO_REFUND_RULE_MATCHORDER:<orderId>BY:<by>STATUS:WANT_REFUNDACTION:ISSUE_REFUNDREFUND_PERCENT:50 or REFUND_PERCENT:100CANCELLED, its cancellation-time refund outcome is final. Later WANT_REFUND must not recalculate refund eligibility.COMPLETED or CANCELLED order, return:
ORDER:<orderId>BY:<by>STATUS:<statusCode>ACTION:IGNORED_ORDER_ALREADY_CLOSEDWorkflowAutomator(List<String> existingUsers, List<String> existingRestaurants)
void createOrder(String orderId, String restaurantId, String userId, int deliveryETA, int currentTime)
deliveryETA and currentTime are minutes elapsed since 1 Jan 1970.orderId, restaurantId, and userId are valid and non-blank.currentTime is monotonically non-decreasing across all method calls.List<String> updateOrderStatus(String orderId, String by, int currentTime, String statusCode)
by is one of CUSTOMER, DASHER, or SUPPORT_AGENT.orderId always refers to a previously created order.currentTime is monotonically non-decreasing across all method calls.statusCode is a non-blank string such as ORDER_GETTING_PREPARED, OUT_FOR_DELIVERY, RAIN_DELAY, WANT_REFUND, ORDER_DELIVERED, DASHER_REACHED_DELIVERY_LOCATION, DELIVERY_CANCELLED, CUSTOMER_UNREACHABLE, or DASHER_UNREACHABLE.orderId is unique.updateOrderStatus use an existing order.currentTime is monotonically non-decreasing across the whole class.Input
WorkflowAutomator automator = new WorkflowAutomator(existingUsers = List.of("u1", "u2"), existingRestaurants = List.of("r1", "r2"))
automator.createOrder(orderId = "o1", restaurantId = "r1", userId = "u1", deliveryETA = 100, currentTime = 80)
Method Call 1
automator.updateOrderStatus(orderId = "o1", by = "DASHER", currentTime = 85, statusCode = "OUT_FOR_DELIVERY")
Output 1
List.of("ORDER:o1", "BY:DASHER", "STATUS:OUT_FOR_DELIVERY", "ACTION:CONTINUE_ORDER")
Method Call 2
automator.updateOrderStatus(orderId = "o1", by = "DASHER", currentTime = 115, statusCode = "ORDER_DELIVERED")
Output 2
List.of("ORDER:o1", "BY:DASHER", "STATUS:ORDER_DELIVERED", "LATE:true", "ACTION:COMPLETE_ORDER", "ACTION:ISSUE_REFUND", "REFUND_PERCENT:50")
Method Call 3
automator.updateOrderStatus(orderId = "o1", by = "CUSTOMER", currentTime = 120, statusCode = "WANT_REFUND")
Output 3
List.of("ORDER:o1", "BY:CUSTOMER", "STATUS:WANT_REFUND", "ACTION:REFUND_ALREADY_ISSUED")
Method Call 4
automator.updateOrderStatus(orderId = "o1", by = "SUPPORT_AGENT", currentTime = 125, statusCode = "RAIN_DELAY")
Output 4
List.of("ORDER:o1", "BY:SUPPORT_AGENT", "STATUS:RAIN_DELAY", "ACTION:IGNORED_ORDER_ALREADY_CLOSED")
Input
WorkflowAutomator automator = new WorkflowAutomator(existingUsers = List.of("u1", "u2"), existingRestaurants = List.of("r1", "r2"))
automator.createOrder(orderId = "o2", restaurantId = "r2", userId = "u2", deliveryETA = 220, currentTime = 150)
Method Call 1
automator.updateOrderStatus(orderId = "o2", by = "CUSTOMER", currentTime = 155, statusCode = "WANT_REFUND")
Output 1
List.of("ORDER:o2", "BY:CUSTOMER", "STATUS:WANT_REFUND", "ACTION:ISSUE_REFUND", "REFUND_PERCENT:95", "ACTION:CANCEL_ORDER")
Method Call 2
automator.updateOrderStatus(orderId = "o2", by = "DASHER", currentTime = 160, statusCode = "OUT_FOR_DELIVERY")
Output 2
List.of("ORDER:o2", "BY:DASHER", "STATUS:OUT_FOR_DELIVERY", "ACTION:IGNORED_ORDER_ALREADY_CLOSED")
Input
WorkflowAutomator automator = new WorkflowAutomator(existingUsers = List.of("u1", "u2"), existingRestaurants = List.of("r1", "r2"))
automator.createOrder(orderId = "o3", restaurantId = "r1", userId = "u1", deliveryETA = 300, currentTime = 200)
Method Call 1
automator.updateOrderStatus(orderId = "o3", by = "SUPPORT_AGENT", currentTime = 210, statusCode = "ORDER_GETTING_PREPARED")
Output 1
List.of("ORDER:o3", "BY:SUPPORT_AGENT", "STATUS:ORDER_GETTING_PREPARED", "ACTION:CONTINUE_ORDER")
Method Call 2
automator.updateOrderStatus(orderId = "o3", by = "CUSTOMER", currentTime = 215, statusCode = "WANT_REFUND")
Output 2
List.of("ORDER:o3", "BY:CUSTOMER", "STATUS:WANT_REFUND", "ACTION:ISSUE_REFUND", "REFUND_PERCENT:100", "ACTION:CANCEL_ORDER")
Input
WorkflowAutomator automator = new WorkflowAutomator(existingUsers = List.of("u1", "u2"), existingRestaurants = List.of("r1", "r2"))
automator.createOrder(orderId = "o4", restaurantId = "r2", userId = "u2", deliveryETA = 400, currentTime = 300)
Method Call 1
automator.updateOrderStatus(orderId = "o4", by = "DASHER", currentTime = 408, statusCode = "ORDER_DELIVERED")
Output 1
List.of("ORDER:o4", "BY:DASHER", "STATUS:ORDER_DELIVERED", "LATE:true", "ACTION:COMPLETE_ORDER")
Method Call 2
automator.updateOrderStatus(orderId = "o4", by = "CUSTOMER", currentTime = 415, statusCode = "WANT_REFUND")
Output 2
List.of("ORDER:o4", "BY:CUSTOMER", "STATUS:WANT_REFUND", "ACTION:NO_REFUND_RULE_MATCH")
Input
WorkflowAutomator automator = new WorkflowAutomator(existingUsers = List.of("u1", "u2"), existingRestaurants = List.of("r1", "r2"))
automator.createOrder(orderId = "o5", restaurantId = "r1", userId = "u1", deliveryETA = 500, currentTime = 450)
Method Call 1
automator.updateOrderStatus(orderId = "o5", by = "SUPPORT_AGENT", currentTime = 460, statusCode = "DELIVERY_CANCELLED")
Output 1
List.of("ORDER:o5", "BY:SUPPORT_AGENT", "STATUS:DELIVERY_CANCELLED", "ACTION:CANCEL_ORDER", "ACTION:ISSUE_REFUND", "REFUND_PERCENT:100")