天天看點

餐飲、零售等應用程式之購物車設計

前言

經常和老婆去某大俠火鍋店吃飯,貼在桌子上的桌碼就是多人點餐的一個入口。就餐的人都可以實時看到目前桌子上點了什麼菜。非常的友善。這不、最近我們也在做這樣一個功能,大同小異吧,都是能實時的看到同一個桌子上點的什麼菜。那麼本文也就針對這個功能記錄了下實作的個人思路。前端加餐和申請購物車都是直接請求服務接口,服務接口再轉發到Socket服務,前端隻要保持和Socket正常的接收消息重新整理購物車即可。接下的代碼片段主要是表示個人的思路,一些業務具體實作代碼沒有貼出來。

1.購物車實體類關系

餐飲、零售等應用程式之購物車設計
餐飲、零售等應用程式之購物車設計
餐飲、零售等應用程式之購物車設計

模拟了現實超市購物、結合實作業務場景,把購物車模型和具體的購物車給分離開來。​

​AbstractCart​

​為模拟的購物車、配置設定了購物車的基本屬性.

1.1 重要基本屬性介紹

1.1.1 active

表示是否激活,可以認為目前實體類序列化實體是否存儲到Db,在實際業務場景中,當給你配置設定了購物車,就存儲到了Db/ 設定為 1

1.1.2 cartId

表示即将或者已經配置設定的購物車Id(結合目前使用者、和實際場景),也用于到Db裡去檢索。一旦配置設定了,後續的對購物車的操作都基本它

1.1.3 cartType

購物車類型、前者說到,我給購物車配置設定了很多不同類型的購物車,不同類型的購物車有它自己的屬性

public enum CartTypeEnum {
/**
* 購物車類型
*/
MULTI_CART(1, "多人點餐"),
}

//多人點餐購物車模型、它有 就餐方式、桌碼資訊等
public class MultiTableCart extends AbstractCart {
/**
* 就餐方式
*/
private Integer eatType;
/**
* 桌子資訊
*/
private TableInfo tableInfo;
}
      

1.1.4 bags

這個屬性重要、了解了這個屬性,那麼整個購物車的設計都明白了。我們在去超市購物的時候,一般都是推着一個小車子,結合我們現實的業務場景,可能一個我們好幾個都共用這一個車子,但是還要區分我們各自的商品,是以在購物車的基礎上再設計出購物袋的模型。即一個購物車裡面可以放N個購物車袋(為每個人配置設定),每個購物袋放對應人的商品 這樣就能友善的區分出每個人買的商品。

public class Bag implements Serializable {


private static final long serialVersionUID = -2114201035393187375L;

/**
* 購物袋Id 初始化的時候 IdUtil
*/
private String id;

/**
* 購物袋名 沒有什麼實在意義,就是給購物車加個名字
*/
private String name;

/**
* 目前購物袋所屬使用者
*/
private User user;


/**
* 購物袋裡的商品
*/
private List<Item> items = new LinkedList<>();

}
      

到此購物車的模型就設計好了、那麼具體看Service是怎麼處理的吧。

2. 購物車業務

2.1 申請購物車

第一步就要檢測目前使用者有沒有購物車。由業務區分如果目前使用者沒有購物車,是否由場景申請出一個購物車。

餐飲、零售等應用程式之購物車設計

定義一個申請購物車的接口、和一個具體實作。那麼再定義一個申請購物車工廠,給業務提供具體的申請購物車業務實作類。

@Component
public class CartFactory implements BeanPostProcessor {

/**
* 定義購物車配置設定方式
*/
private final ConcurrentHashMap<CartTypeEnum, ApplyCartService<?>> cartCols = new ConcurrentHashMap<>();

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ApplyCartService) {
ApplyCartService<?> applyCartService = (ApplyCartService<?>) bean;
cartCols.put(applyCartService.cartType(), applyCartService);
        }
return bean;
    }

/**
* get a applyCartService
* @return
*/
public ApplyCartService<?> applyCartService(CartTypeEnum cartType) {
return cartCols.get(cartType);
    }

}
      

使用者接口向外提供申請購物車接口方法

@Service
public class CustomCartServiceImpl implements CustomCartService {

//購物車工廠
@Resource
CartFactory cartFactory;

@Override
public Result<AbstractCart> applyCart(ApplyCartDTO applyCart) {
//由前端告知想要申請的購物車類型
CartTypeEnum typeEnum = CartTypeEnum.CART_TYPE_MAPPINGS.get(applyCart.getCartType());
if (Objects.isNull(typeEnum)) {
return Result.failureData("非法操作!");
        }
//由購物車功工廠提供配置設定購物車執行個體
ApplyCartService<AbstractCart> abstractCartApplyCartService = (ApplyCartService<AbstractCart>) cartFactory.applyCartService(typeEnum);
Result<AbstractCart> cartResult = abstractCartApplyCartService.applyCart(applyCart);

if (!cartResult.isStatus()) {
return cartResult;
        }

//父類的引用
AbstractCart cart = cartResult.getData();

//需要激活、且沒有激活過、存入Db
if (Objects.equals(cart.getActive(), OnOffEnum.OFF.getCode())) {
//激活購物車
cart.setActive(OnOffEnum.ON.getCode());
cartService.flushDb(cart);
        }

return cartResult;
    }

}
      

2.2 購物車具體的操作

2.2.1 抽象購物車操作的接口定義

public interface CartService<T extends AbstractCart> {

/**
* 向某個購物車裡添加商品
* @param t
* @param bagId
* @param clientIdentity
* @param items
* @return
*/
Result<T> insertItem(T t, String bagId, ClientIdentity clientIdentity, List<Item> items);


/**
* 删除某些商品
* @param t
* @param bagId
* @param itemUniqIds
* @return
*/
Result<T> updateBags(T t, String bagId, List<ItemNumDTO> itemUniqIds);


/**
* 擷取購物車
* @param shopId
* @param cartId
* @return
*/
Result<T> getCart(Long shopId, String cartId);


/**
* 重新整理到存儲
* @param t
* @return
*/
Result<T> flushDb(T t);


/**
*  清空購物車
* @param t
* @return
*/
Result<T> clearUpCart(T t);


/**
* 銷毀購物車
* @param t
* @return
*/
Result<T> destroy(T t);
}
      

2.2.2 業務購物車操作接口的定義

public interface CustomCartService {

/**
* 用于檢測購物車是否激活
* @param applyCart
* @return
*/
Result<AbstractCart> checkCartActive(ApplyCartDTO applyCart);


/**
* 擷取一個購物車
* @param applyCart
* @return
*/
Result<AbstractCart> applyCart(ApplyCartDTO applyCart);

/**
* 删除某些商品
* @param addBagItem
* @return
*/
Result<String> updateBags(AddBagItemDTO addBagItem);


/**
* 清空購物車
* @param shopId
* @param cartId
* @return
*/
Result<String> clearUpCart(Long shopId, String cartId);


/**
* 銷毀購物車
* 銷毀的人
* @param userId
* @param orderNo
* @param cart
* @return
*/
void destroyCartAndPushSocket(Long userId, String orderNo, AbstractCart cart);
}

      

2.2.3 業務購物車操作事件監聽

public class CartEvent extends ApplicationEvent {

private static final long serialVersionUID = -8248110180060447856L;

/**
* 購物車資料
*/
private final AbstractCart cart;

/**
* 其他參數
*/
private final Object[] objects;


/**
* Create a new {@code ApplicationEvent}.
*
* @param source the object on which the event initially occurred or with
*               which the event is associated (never {@code null})
*/
public CartEvent(Object source, AbstractCart cart, Object... objects) {
super(source);
this.cart = cart;
this.objects = objects;
    }

/**
*  getter
* @return
*/
public AbstractCart getCart() {
return cart;
    }

/**
* 其他參數
* @return
*/
public Object[] getObjects() {
return objects;
    }
}
      
@Component
public class CartListener {

//一個推送服務、推送Socket
@Resource
SocketCenterSpi socketCenterSpi;

/**
* 添加商品
*/
@EventListener(
classes = CartEvent.class,
condition = "#cartEvent.getSource() == T(com.zm.baking.biz.service.cart.event.CartEventSourceEnum).ADD_ITEM " +
"and #cartEvent.getCart().getCartType() == T(com.zm.baking.common.enums.CartTypeEnum).MULTI_CART.getCode()"
    )
public void afterInsertCartItemToSocket(CartEvent cartEvent) {
AbstractCart cart = cartEvent.getCart();
this.pushTag(Collections.singletonList(cart.getCartId()), JSON.toJSONString(cart));
    }


/**
* 更新商品
*/
@EventListener(
classes = CartEvent.class,
condition = "#cartEvent.getSource() == T(com.zm.baking.biz.service.cart.event.CartEventSourceEnum).UPDATE_ITEM " +
"and #cartEvent.getCart().getCartType() == T(com.zm.baking.common.enums.CartTypeEnum).MULTI_CART.getCode()"
    )
public void afterDelCartItemToSocket(CartEvent cartEvent) {
AbstractCart cart = cartEvent.getCart();
this.pushTag(Collections.singletonList(cart.getCartId()), JSON.toJSONString(cart));
    }




/**
* 清空商品
*/
@EventListener(
classes = CartEvent.class,
condition = "#cartEvent.getSource() == T(com.zm.baking.biz.service.cart.event.CartEventSourceEnum).CLEAR_UP " +
"and #cartEvent.getCart().getCartType() == T(com.zm.baking.common.enums.CartTypeEnum).MULTI_CART.getCode()"
    )
public void clearUpCartItemToSocket(CartEvent cartEvent) {
AbstractCart cart = cartEvent.getCart();
this.pushTag(Collections.singletonList(cart.getCartId()), JSON.toJSONString(cart));
    }




/**
* 銷毀購物車
*/
@EventListener(
classes = CartEvent.class,
condition = "#cartEvent.getSource() == T(com.zm.baking.biz.service.cart.event.CartEventSourceEnum).DESTROY " +
"and #cartEvent.getCart().getCartType() == T(com.zm.baking.common.enums.CartTypeEnum).MULTI_CART.getCode()"
    )
public void distroyCartToSocket(CartEvent cartEvent) {
AbstractCart cart = cartEvent.getCart();
Object[] objects = cartEvent.getObjects();
Long userId  = (Long) objects[0];
String orderNo = (String)objects[1];
SubmitOrder submitOrder = new SubmitOrder(userId.toString(), orderNo);
pushTag(Collections.singletonList(cart.getCartId()), submitOrder.toString());
    }


/**
* 推送資料
* @param tags
* @param message
*/
private void pushTag(List<String> tags, String message) {
//推送資料
final ClientTagPushRequest pushRequest = new ClientTagPushRequest();
pushRequest.setMessage(message);
pushRequest.setTags(tags);
socketCenterSpi.tagPush(pushRequest);
    }


static class SubmitOrder {
private String submitUserId;
private String orderNo;
public SubmitOrder() {
        }
public SubmitOrder(String submitUserId, String orderNo) {
this.submitUserId = submitUserId;
this.orderNo = orderNo;
        }

public String getSubmitUserId() {
return submitUserId;
        }

public void setSubmitUserId(String submitUserId) {
this.submitUserId = submitUserId;
        }

public String getOrderNo() {
return orderNo;
        }

public void setOrderNo(String orderNo) {
this.orderNo = orderNo;
        }

@Override
public String toString() {
return JSON.toJSONString(this);
        }
    }
}
      

作者:M78-Seven​