经常和老婆去某大侠火锅店吃饭,贴在桌子上的桌码就是多人点餐的一个入口。就餐的人都可以实时看到当前桌子上点了什么菜。非常的方便。这不、最近我们也在做这样一个功能,大同小异吧,都是能实时的看到同一个桌子上点的什么菜。那么本文也就针对这个功能记录了下实现的个人思路。前端加餐和申请购物车都是直接请求服务接口,服务接口再转发到Socket服务,前端只要保持和Socket正常的接收消息刷新购物车即可。接下的代码片段主要是表示个人的思路,一些业务具体实现代码没有贴出来。
模拟了现实超市购物、结合实现业务场景,把购物车模型和具体的购物车给分离开来。AbstractCart
为模拟的购物车、分配了购物车的基本属性.
表示是否激活,可以认为当前实体类序列化实体是否存储到Db,在实际业务场景中,当给你分配了购物车,就存储到了Db/ 设置为 1
表示即将或者已经分配的购物车Id(结合当前用户、和实际场景),也用于到Db里去检索。一旦分配了,后续的对购物车的操作都基本它
购物车类型、前者说到,我给购物车分配了很多不同类型的购物车,不同类型的购物车有它自己的属性
public enum CartTypeEnum { /** * 购物车类型 */ MULTI_CART(1, "多人点餐"), } //多人点餐购物车模型、它有 就餐方式、桌码信息等 public class MultiTableCart extends AbstractCart { /** * 就餐方式 */ private Integer eatType; /** * 桌子信息 */ private TableInfo tableInfo; }
这个属性重要、理解了这个属性,那么整个购物车的设计都明白了。我们在去超市购物的时候,一般都是推着一个小车子,结合我们现实的业务场景,可能一个我们好几个都共用这一个车子,但是还要区分我们各自的商品,所以在购物车的基础上再设计出购物袋的模型。即一个购物车里面可以放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是怎么处理的吧。
第一步就要检测当前用户有没有购物车。由业务区分如果当前用户没有购物车,是否由场景申请出一个购物车。
定义一个申请购物车的接口、和一个具体实现。那么再定义一个申请购物车工厂,给业务提供具体的申请购物车业务实现类。
@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; } }
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); }
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); }
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); } } }