背景
公司需要对商城中所有商品加入配送标签(同城配送,当日达,门店自提,全国包邮,全国配送.....)。我想项目比较旧了,很多技术都很老是不是可以看看还有别的设计方式?
思路方式
旧项目采用的是product_tag表=>记录各种商品标签,里面定制 标签名称以及状态..然后通过代码逻辑来动态展示 (新品,热门,包邮....)

马上可以实现的办法: 直接product添加 delivery_flag 字段,然后存入逗号分隔的字符串
其实数据库 varchar 存逗号也有好处,那就是可以通过sql里面的find_in_set去查询里面的指定值,这是 in (?,?...) 所做不到的
比如 delivery_flag=> '同城配送,当日达,门店自提,全国包邮,全国配送'
如果是 select * from product where delivery_flag in ('同城配送') => result => empty
但是通过find_in_set
select * from product where find_in_set('同城配送',delivery_flag) => result => 1
find_in_set 大致就是 mysql 专门为英文逗号分隔查询的函数,他可以查询某个字段下通过,分隔后是否包含某个指定值
ps: 但是因为,分隔本身内容越大开销越多,从函数的功能就能看出他也是通过一个一个值得遍历匹配来判断,所以用这个最好where 后多加筛选条件,精准匹配压力才小
通过Int类型字段+位运算的操作
具体实现
先定义枚举类,提供规范,以及转化方法
Getter
public enum PDeliveryFlagEnums {
// 系统默认标签 快递 预留系统标签 2~5
DEFAULT_SHIP(1<<0,"快递"),
// 蛋糕标签
NATIONWIDE_FREE_SHIP(1<<6,"全国包邮"),
NATIONWIDE_DELIVERY(1<<7,"全国配送"),
LOCAL_SAME_CITY(1<<8,"同城配送"),
SAME_DAY_DELIVERY(1<<9,"当日达"),
STORE_PICKUP(1<<10,"门店自提"),
//其他供应商后续得标签
;
// 初始化成Map类型,方便处理
private static final Map<String, Integer> NAME_TO_INDEX =
Arrays.stream(values())
.collect(Collectors.toMap(e -> e.name, e -> e.index));
private Integer index;
private String name;
PDeliveryFlagEnums(Integer index, String name){
this.index = index;
this.name = name;
}
// parseData 数据库 转 response
public static Set<String> parseData(Integer deliveryFlags){
if(ObjectUtil.isNull(deliveryFlags)){
return Collections.emptySet();
}
return Arrays.stream(PDeliveryFlagEnums.values()).filter(
item -> (item.index & deliveryFlags) == item.index
).map(item -> item.name).collect(Collectors.toSet());
}
public static void main(String[] args) {
// 测试1
Set<String> strings = PDeliveryFlagEnums.parseData(1);
System.out.println( strings);
Set<String> strings2 = PDeliveryFlagEnums.parseData(1984);
System.out.println( strings2);
// 测试2
// Integer flags = toFlags("门店自提,全国配送,同城配送,全国包邮,当日达");
// System.out.println( flags);
// Integer flags1 = toFlags("门店自提, 全国配送, 同城配送, 全国包邮, 当日达");
// System.out.println( flags1);
}
// mergeData request 转 数据库
@Null
public static Integer toFlags(String deliveryFlags){
if(ObjectUtil.isEmpty(deliveryFlags)){
return null;
}
String[] split = deliveryFlags.split(",");
// 未识别的类型也不会存入数据库,避免供应商的失误导致我们展示奇怪的文字
return Arrays.stream(split).map(String::trim).map(NAME_TO_INDEX::get).filter(Objects::nonNull)
.mapToInt(Integer::intValue)
.sum();
}
}分析:
通过位运算可以大幅度提高查询效率,因为实际数据库存储的是Integer,且相比于逗号分隔,可以建立索引
因为每个标签记录的都是偏移量,所以不会存在多个标签重复或者错误的问题
1<<0 == 0001(1)
1<<1 == 0010(2)
那如果我要同时存标签1和2 其实就是0011 也就是3,可以发现首先 偏移量并不会占用其他的位置,2就是在第二位上