前言
經常和老婆去某大俠火鍋店吃飯,貼在桌子上的桌碼就是多人點餐的一個入口。就餐的人都可以實時看到目前桌子上點了什麼菜。非常的友善。這不、最近我們也在做這樣一個功能,大同小異吧,都是能實時的看到同一個桌子上點的什麼菜。那麼本文也就針對這個功能記錄了下實作的個人思路。前端加餐和申請購物車都是直接請求服務接口,服務接口再轉發到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