随便看看
发布于 2026-06-12 / 9 阅读
0

背景

公司需要对商城中所有商品加入配送标签(同城配送,当日达,门店自提,全国包邮,全国配送.....)。我想项目比较旧了,很多技术都很老是不是可以看看还有别的设计方式?

思路方式

  1. 旧项目采用的是product_tag表=>记录各种商品标签,里面定制 标签名称以及状态..然后通过代码逻辑来动态展示 (新品,热门,包邮....)

  1. 马上可以实现的办法: 直接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 后多加筛选条件,精准匹配压力才小

  1. 通过Int类型字段+位运算的操作

具体实现

  1. 先定义枚举类,提供规范,以及转化方法

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();
    }
}

分析:

  1. 通过位运算可以大幅度提高查询效率,因为实际数据库存储的是Integer,且相比于逗号分隔,可以建立索引

  2. 因为每个标签记录的都是偏移量,所以不会存在多个标签重复或者错误的问题

1<<0 == 0001(1)

1<<1 == 0010(2)

那如果我要同时存标签1和2 其实就是0011 也就是3,可以发现首先 偏移量并不会占用其他的位置,2就是在第二位上