How JD Logistics Optimizes Order Merging with Recursive Algorithms

This article explains the background, challenges, and recursive algorithmic solution JD Logistics uses to merge purchase orders, detailing the merging rules, code implementation, deduplication strategy, performance gains, and the resulting operational benefits such as increased merchant satisfaction and faster processing.

JD Cloud Developers
JD Cloud Developers
JD Cloud Developers
How JD Logistics Optimizes Order Merging with Recursive Algorithms

Background

JD Logistics' "to-warehouse" service aims to reduce merchants' need to split and stock goods according to JD purchase orders, align industry flow handover, improve merchant satisfaction, and increase JD's pick‑up app efficiency; thus the order‑merging function was created.

Problem

A batch of purchase orders (e.g., 50‑100 orders) must be merged into multiple orders according to various rules. Each order may have different source types (self‑operated or third‑party), receipt types, multiple SKUs, and each SKU can have multiple levels, leading to complex merging constraints.

Merging Rules

Self‑operated and non‑self‑operated orders cannot be merged.

Physical receipt and document receipt orders cannot be merged.

Orders with the same receiving warehouse and distribution center can be merged.

If merging results in the same SKU having multiple levels, the orders cannot be merged.

Approach

A. Idea

Assume the batch can be merged, then split orders that violate the rules.

Apply rules 1‑3 to create lists of orders that need rule 4 processing.

When all orders share the same attributes for rules 1‑3, the list size is 1 and further merging is based on SKU + level.

Identify the SKU with the most levels (e.g., skuA with 7 levels), partition orders into 7 groups by level, then distribute remaining orders into these groups, forming the basis for recursion.

Recursively process each group until no SKU contains multiple levels (recursion termination condition).

Maintain a list of already processed order sets to prune duplicates and keep time complexity from exploding.

After recursion, sort the merged groups by size, deduplicate, and release single‑order groups only when they cannot be merged later.

B. Algorithm

The solution uses a recursive algorithm that repeatedly breaks the problem into similar sub‑problems, similar to classic recursion examples such as Fibonacci, Hanoi, and factorial calculations.

C. Implementation

1. Recursive Code Block

/** 
 * 指定不同等级不能合单
 *
 * @param poNoSet       采购单号Set
 * @param poMainInfoMap 采购单详情
 * @param calculatedSet 计算过的采购单据列表的集合
 * @return
 */
private List<Set<String>> doMergeClassDifferent(Set<String> poNoSet,
        Map<String, PoOrderFacadeResponse.PoMainInfo> poMainInfoMap,
        Set<String> calculatedSet) {
    // 如果该set已经计算过则不重复计算
    List<Set<String>> resultList = new ArrayList<>();
    String calculatedPoNoKey = buildCalculatedPoNoKey(poNoSet);
    if (calculatedSet.contains(calculatedPoNoKey)) {
        return resultList;
    } else {
        calculatedSet.add(calculatedPoNoKey);
        resultValue.incrementAndGet();
    }

    // 以sku为key的集合
    Set<String> skuSet = new HashSet<>();
    // 以sku 为key, 值为poNos
    Map<String, Set<String>> skuMap = new HashMap<>();
    // 存放同一个sku下有多少个不同等级的集合
    Map<String, Set<String>> skuToskuLevelMap = new HashMap<>();

    // 以sku+level 为key的集合
    Set<String> skuLevelSet = new HashSet<>();
    // 以sku+level 为key, 值为poNos
    Map<String, Set<String>> skuLevelMap = new HashMap<>();

    for (String poNo : poNoSet) {
        PoOrderFacadeResponse.PoMainInfo poMainInfo = poMainInfoMap.get(poNo);
        // 采购单条目
        List<PoOrderFacadeResponse.PoItemInfo> poItemInfos = poMainInfo.getPoItemInfos();
        for (PoOrderFacadeResponse.PoItemInfo poItemInfo : poItemInfos) {
            String skuKey = poItemInfo.getGoodsNo();
            String skuLevelKey = buildSkuLevelKey(poItemInfo);
            skuSet.add(skuKey);
            setKeyMap(skuKey, skuMap, poNo);
            // 存放同一个sku下有多少个不同等级的集合
            Set<String> stringSet = skuToskuLevelMap.get(skuKey);
            if (CollectionUtils.isEmpty(stringSet)) {
                stringSet = new HashSet<>();
                skuToskuLevelMap.put(skuKey, stringSet);
            }
            stringSet.add(skuLevelKey);
            skuLevelSet.add(skuLevelKey);
            setKeyMap(skuLevelKey, skuLevelMap, poNo);
        }
    }

    if (skuSet.size() == skuLevelSet.size()) {
        // 此处sku的数量和sku+level的数量相同,不需要再进行递归运算
        // 方法结束的出口
        resultList.add(poNoSet);
        return resultList;
    } else {
        // 同一个sku下最多等级个数
        int high = MagicCommonConstants.NUM_1;
        // 最多等级个数的对应sku
        String maxLevelSku = "";
        for (String sku : skuToskuLevelMap.keySet()) {
            Set<String> strings = skuToskuLevelMap.get(sku);
            if (strings.size() > high) {
                high = strings.size();
                maxLevelSku = sku;
            }
        }
        if (high > MagicCommonConstants.NUM_1) {
            // 获取该sku下的poNos
            Set<String> strings = skuMap.get(maxLevelSku);
            // 差集
            Set<String> chaJiSet = poNoSet;
            chaJiSet.removeAll(strings);

            Set<String> skuLevels = skuToskuLevelMap.get(maxLevelSku);
            for (String skuLevel : skuLevels) {
                Set<String> poNoTempSet = skuLevelMap.get(skuLevel);
                poNoTempSet.addAll(chaJiSet);
                // 递归计算
                List<Set<String>> clist = doMergeClassDifferent(poNoTempSet, poMainInfoMap, calculatedSet);
                if (CollectionUtils.isNotEmpty(clist)) {
                    resultList.addAll(clist);
                }
            }
        }
    }
    return resultList;
}

2. Deduplication Code Block

/** 
 * 去重 合单之后的采购单号
 *
 * @param sets
 * @param dooModel
 */
private List<Set<String>> uniqueRepeatPoNo(List<Set<String>> sets, DooModel dooModel) {
    sets.sort(new Comparator<Set<String>>() {
        @Override
        public int compare(Set<String> o1, Set<String> o2) {
            return o2.size() - o1.size();
        }
    });

    List<Set<String>> resultList = new ArrayList<>();
    Set<String> allMergedSet = new HashSet<>();

    Set<String> allSet = new HashSet<>();
    for (Set<String> set : sets) {
        Set<String> tempSet = new HashSet<>();
        for (String poNo : set) {
            if (!allSet.contains(poNo)) {
                tempSet.add(poNo);
                allMergedSet.add(poNo);
            }
        }
        if (!tempSet.isEmpty()) {
            if (tempSet.size() > 1) {
                allSet.addAll(tempSet);
                resultList.add(tempSet);
            }
            // 此处的单条后面不一定不能合单
        }
    }

    // 差集
    allMergedSet.removeAll(allSet);
    if (allMergedSet.size() > 0) {
        for (String poNo: allMergedSet) {
            putPoNoToSet(dooModel, poNo);
        }
    }
    return resultList;
}

Value

After 45 days of launch, the feature is used by 22 customers across Zhejiang, Henan, Shanghai, Jiangsu, Anhui, Tianjin, Sichuan, and Beijing, generating an additional 5 million revenue, with stable operations. During peak periods, the merging function improves merchant satisfaction and boosts JD’s pick‑up app efficiency by 30%, while warehouse and inbound efficiency increase by 10%.

Conclusion

Difficulty: Distributing remaining orders into different SKU groups after partitioning and implementing the recursive calculation.

Performance: Most recursive calculations are duplicated, but by recording intermediate results and pruning already‑processed order sets, the algorithm avoids exponential growth and remains robust as order volume, SKU count, and level variety increase.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Javaorder mergingRecursive AlgorithmLogistics Optimization
JD Cloud Developers
Written by

JD Cloud Developers

JD Cloud Developers (Developer of JD Technology) is a JD Technology Group platform offering technical sharing and communication for AI, cloud computing, IoT and related developers. It publishes JD product technical information, industry content, and tech event news. Embrace technology and partner with developers to envision the future.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.