前言
经常和老婆去某大侠火锅店吃饭,贴在桌子上的桌码就是多人点餐的一个入口。就餐的人都可以实时看到当前桌子上点了什么菜。非常的方便。这不、最近我们也在做这样一个功能,大同小异吧,都是能实时的看到同一个桌子上点的什么菜。那么本文也就针对这个功能记录了下实现的个人思路。前端加餐和申请购物车都是直接请求服务接口,服务接口再转发到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);
}
}
}