天天看点

餐饮、零售等应用程序之购物车设计

前言

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