Untitled
unknown
java
2 months ago
11 kB
7
Indexable
package org.example;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
public class FoodOrderingApplication {
public enum OrderStatus{
PLACED, PREPARING, FULFILLED
}
public static class User{
String userId;
String name;
public User(String userId, String name) {
this.userId = userId;
this.name = name;
}
@Override
public String toString() {
return "User{" +
"userId='" + userId + '\'' +
", name='" + name + '\'' +
'}';
}
}
public static class MenuItem{
String name;
long price;
int prepTimeSeconds;
public MenuItem(String name, long price, int prepTimeSeconds) {
this.name = name;
this.price = price;
this.prepTimeSeconds = prepTimeSeconds;
}
}
public static class Restaurant{
private String name;
private Map<String, MenuItem> menu = new ConcurrentHashMap<>();
private AtomicInteger currentCapacity;
private int maxCapacity;
public Restaurant(String name, List<MenuItem> items, int totalCapacity) {
this.name = name;
this.currentCapacity = new AtomicInteger(totalCapacity);
this.maxCapacity = totalCapacity;
items.forEach(item -> menu.put(item.name, item));
}
public boolean hasItem(String name){
return menu.containsKey(name);
}
public MenuItem getItem(String name){
return menu.get(name);
}
public boolean reserve(int quantity){
while(true){
int current = currentCapacity.get();
if(current<quantity) return false;
if(currentCapacity.compareAndSet(current, current-quantity)) return true;
}
}
public void release(int quantity){
currentCapacity.getAndUpdate(capacity -> Math.min(maxCapacity, capacity+quantity));
}
public String getName(){
return name;
}
public int getAvailableCapacity() {
return currentCapacity.get();
}
}
public static class Order{
String orderId;
User user;
Map<String, Integer> itemCounts;
Map<String, String> itemToRestaurantMap;
OrderStatus orderStatus;
AtomicInteger itemsRemaining;
public Order(User user, Map<String, Integer> itemCounts, Map<String, String> itemToRestaurantMap) {
this.user = user;
this.itemCounts = itemCounts;
this.itemToRestaurantMap = itemToRestaurantMap;
this.orderStatus = OrderStatus.PLACED;
this.itemsRemaining = new AtomicInteger(itemCounts.values().stream().mapToInt(Integer::intValue).sum());
}
@Override
public String toString() {
return "Order{" +
"orderId='" + orderId + '\'' +
", user=" + user +
", itemCounts=" + itemCounts +
", itemToRestaurantMap=" + itemToRestaurantMap +
", orderStatus=" + orderStatus +
", itemsRemaining=" + itemsRemaining +
'}';
}
}
public interface RestaurantRepository{
void save(Restaurant restaurant);
List<Restaurant> findAll();
}
public static class InMemoryRestaurantRepository implements RestaurantRepository{
Map<String, Restaurant> map = new ConcurrentHashMap<>();
@Override
public void save(Restaurant restaurant) {
map.put(restaurant.getName(), restaurant);
}
@Override
public List<Restaurant> findAll() {
return new ArrayList<>(map.values());
}
}
public interface OrderRepository{
void save(Order order);
Optional<Order> findById(String id);
}
public static class InMemoryOrderRepository implements OrderRepository{
Map<String, Order> map = new ConcurrentHashMap<>();
AtomicInteger orderIdCounter = new AtomicInteger();
@Override
public void save(Order order) {
String id = "ORD-" + orderIdCounter.incrementAndGet();
order.orderId = id;
map.put(id, order);
}
@Override
public Optional<Order> findById(String id) {
return Optional.ofNullable(map.get(id));
}
}
public interface SelectionStrategy{
Map<String, Restaurant> select(Map<String, Integer> itemCounts, List<Restaurant> restaurants);
}
public static class LowestPriceStrategy implements SelectionStrategy{
@Override
public Map<String, Restaurant> select(Map<String, Integer> itemCounts, List<Restaurant> restaurants) {
Map<String, Restaurant> map = new HashMap<>();
for(String itemName: itemCounts.keySet()){
int required = itemCounts.get(itemName);
restaurants.stream()
.filter(r-> r.hasItem(itemName) && r.getAvailableCapacity()>=required)
.min(Comparator.comparingLong(r-> r.getItem(itemName).price))
.ifPresent(r-> map.put(itemName, r));
}
return map;
}
}
public static class FoodOrderingService{
RestaurantRepository restaurantRepository;
OrderRepository orderRepository;
Set<String> idempotencyKeys = Collections.newSetFromMap(new ConcurrentHashMap<>());
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(4);
public FoodOrderingService(RestaurantRepository restaurantRepository, OrderRepository orderRepository) {
this.restaurantRepository = restaurantRepository;
this.orderRepository = orderRepository;
}
public void addRestaurant(String name, List<MenuItem> menu, int capacity){
restaurantRepository.save(new Restaurant(name, menu, capacity));
}
public void placeOrder(String requestId, User user, Map<String,Integer> itemCount, SelectionStrategy strategy){
if(!idempotencyKeys.add(requestId)){
System.out.println("Rejecting, Duplicate request."+requestId);
return;
}
Map<String, Restaurant> plan = strategy.select(itemCount, restaurantRepository.findAll());
if(plan.size()<itemCount.size()){
System.out.println("Items insufficient");
idempotencyKeys.remove(requestId);
return;
}
List<Map.Entry<Restaurant, Integer>> comitted = new ArrayList<>();
boolean success = true;
for(Map.Entry<String, Restaurant> entry: plan.entrySet()){
Restaurant r = entry.getValue();
int qty = itemCount.get(entry.getKey());
if(r.reserve(qty)){
comitted.add(new AbstractMap.SimpleEntry<>(r, qty));
}else{
success = false;
break;
}
}
if(!success){
comitted.forEach(e-> e.getKey().release(e.getValue()));
System.out.println("Items quantity insufficient");
idempotencyKeys.remove(requestId);
return;
}
Map<String, String> assignmentNames = plan.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, e-> e.getValue().getName()));
Order order = new Order(user, itemCount, assignmentNames);
order.orderStatus = OrderStatus.PREPARING;
orderRepository.save(order);
System.out.println("Order accepted"+ order +" for user "+user);
plan.forEach((itemName, r) -> {
int qty = itemCount.get(itemName);
int prepTime = r.getItem(itemName).prepTimeSeconds;
for(int i=0;i<qty;i++){
scheduler.schedule(()-> {
r.release(1);
if(order.itemsRemaining.decrementAndGet()==0){
order.orderStatus = OrderStatus.FULFILLED;
System.out.println(order + " is fully prepared");
}
}, prepTime, TimeUnit.SECONDS);
}
});
}
public void printStats(){
System.out.println("\n Stats: ");
restaurantRepository.findAll().forEach(r-> {
System.out.println(r.getName()+" -> Available Capacity: "+ r.getAvailableCapacity());
});
System.out.println("------------");
}
}
public static void main(String[] args) throws InterruptedException {
RestaurantRepository restaurantRepository = new InMemoryRestaurantRepository();
OrderRepository orderRepository = new InMemoryOrderRepository();
FoodOrderingService foodOrderingService = new FoodOrderingService(restaurantRepository, orderRepository);
SelectionStrategy lowestPrice = new LowestPriceStrategy();
foodOrderingService.addRestaurant("A2B", Arrays.asList(
new MenuItem("Idly", 40, 1),
new MenuItem("Vada", 30, 2),
new MenuItem("Paper Plain Dosa", 50, 1)), 4);
foodOrderingService.addRestaurant("Rasaganga", Arrays.asList(
new MenuItem("Idly", 45, 1),
new MenuItem("Set Dosa", 60, 1),
new MenuItem("Poori", 25, 1)), 6);
foodOrderingService.addRestaurant("Eat Fit", Arrays.asList(
new MenuItem("Idly", 30, 3),
new MenuItem("Vada", 40, 1)), 2);
User user = new User("U1", "Kush");
Map<String, Integer> req1 = new HashMap<>();
req1.put("Idly", 1); req1.put("Poori", 1);
foodOrderingService.placeOrder("REQ1", user, req1, lowestPrice);
Map<String, Integer> req2 = new HashMap<>();
req2.put("Idly", 1); req2.put("Vada", 1);
foodOrderingService.placeOrder("REQ2", user, req2, lowestPrice);
foodOrderingService.printStats();
Map<String, Integer> req3 = new HashMap<>();
req3.put("Idly", 1);
foodOrderingService.placeOrder("REQ3", user, req3, lowestPrice);
Thread.sleep(4000);
System.out.println();
}
}
Editor is loading...
Leave a Comment