项目结构:

介绍了一套珠宝首饰智能包装配载系统的Java实现,主要包含以下内容:

  1. 系统架构:采用BFS、DFS和A*三种算法实现包装策略,支持多线程并发处理。

  2. 核心模块:

    • 配置管理(AppConfig):日志路径、线程池大小等
    • 枚举定义(ResultCode):标准化返回码
    • 数据模型:首饰(JewelryItem)、包装规格(PackageSpec)、装箱约束(PackConstraint)等
    • 算法实现:BfsStrategy、DfsStrategy、AStarStrategy三种策略
  3. 业务逻辑:

    • 支持首饰体积计算
    • 装箱约束校验
    • 结果合并与日志记录
    • 交互式控制台界面
  4. 技术特点:

    • 单例模式管理资源
    • 线程安全的任务池
    • 支持多种数据库
    • 详细的日志记录

系统可智能计算最优包装方案,考虑成本、优先级和库存等因素,适用于珠宝行业的包装配载需求。

/**
 * encoding: utf-8
 * 版权所有 2026 ©涂聚文有限公司 ®
 * 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
 * 描述:Breadth First Search Algorithm and Depth First Search Algorithm
 * Author    : geovindu,Geovin Du 涂聚文.
 * IDE       : IntelliJ IDEA 2024.3.6 Java 17
 * # database  : Oracle21c,MySQL 9.0,SQL Server 2019,PostgreSQL 17.1 Neo4j
 * # OS        : window10
 * Datetime  : 2026 - 2026/7/28 - 21:25
 * User      : geovindu
 * Product   : IntelliJ IDEA
 * Project   : JavaAlgorithms
 * File      : AppConfig.java
 * explain   : 学习  类
 **/
 
package BreadthFirst.config;
 
import java.io.File;
 
public class AppConfig {
    public static final String LOG_SAVE_PATH = "./logs";
    public static final String LOG_FILE_NAME = "jewelry_packer.log";
    public static final boolean LOG_CONSOLE_ENABLE = true;
    public static final boolean LOG_FILE_ENABLE = true;
 
    public static final int MAX_WORKER_THREAD = 8;
    public static final int MAX_SEARCH_STATE = 25000;
 
    public static void initLogDir() {
        File dir = new File(LOG_SAVE_PATH);
        if (!dir.exists()) {
            dir.mkdirs();
        }
    }
}
 
 
/**
 * encoding: utf-8
 * 版权所有 2026 ©涂聚文有限公司 ®
 * 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
 * 描述:Breadth First Search Algorithm and Depth First Search Algorithm
 * Author    : geovindu,Geovin Du 涂聚文.
 * IDE       : IntelliJ IDEA 2024.3.6 Java 17
 * # database  : Oracle21c,MySQL 9.0,SQL Server 2019,PostgreSQL 17.1 Neo4j
 * # OS        : window10
 * Datetime  : 2026 - 2026/7/28 - 21:26
 * User      : geovindu
 * Product   : IntelliJ IDEA
 * Project   : JavaAlgorithms
 * File      : ResultCode.java
 * explain   : 学习  类
 **/
 
package BreadthFirst.core.enums;
 
public record ResultCode(int code, String msg) {
    public static final ResultCode SUCCESS = new ResultCode(0, "成功");
    public static final ResultCode PARAM_ERROR = new ResultCode(400, "参数非法");
    public static final ResultCode NO_AVAILABLE_PACKAGE = new ResultCode(401, "无可用包装规格");
    public static final ResultCode CONSTRAINT_FAIL = new ResultCode(402, "装箱约束冲突,无法装载");
    public static final ResultCode SEARCH_OVER_LIMIT = new ResultCode(501, "搜索状态超限,未找到方案");
    public static final ResultCode NO_ANY_LOAD_PLAN = new ResultCode(502, "无法生成配载方案");
}
 
 
/**
 * encoding: utf-8
 * 版权所有 2026 ©涂聚文有限公司 ®
 * 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
 * 描述:Breadth First Search Algorithm and Depth First Search Algorithm
 * Author    : geovindu,Geovin Du 涂聚文.
 * IDE       : IntelliJ IDEA 2024.3.6 Java 17
 * # database  : Oracle21c,MySQL 9.0,SQL Server 2019,PostgreSQL 17.1 Neo4j
 * # OS        : window10
 * Datetime  : 2026 - 2026/7/28 - 21:26
 * User      : geovindu
 * Product   : IntelliJ IDEA
 * Project   : JavaAlgorithms
 * File      : JewelryItem.java
 * explain   : 学习  类
 **/
 
package BreadthFirst.core.models;
 
public class JewelryItem {
    private String jewelryCode;
    private double singleVolume;
    private int quantity;
 
    public double getTotalVolume() {
        return singleVolume * quantity;
    }
 
    // getter & setter
    public String getJewelryCode() {
        return jewelryCode;
    }
 
    public void setJewelryCode(String jewelryCode) {
        this.jewelryCode = jewelryCode;
    }
 
    public double getSingleVolume() {
        return singleVolume;
    }
 
    public void setSingleVolume(double singleVolume) {
        this.singleVolume = singleVolume;
    }
 
    public int getQuantity() {
        return quantity;
    }
 
    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }
}
 
/**
 * encoding: utf-8
 * 版权所有 2026 ©涂聚文有限公司 ®
 * 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
 * 描述:Breadth First Search Algorithm and Depth First Search Algorithm
 * Author    : geovindu,Geovin Du 涂聚文.
 * IDE       : IntelliJ IDEA 2024.3.6 Java 17
 * # database  : Oracle21c,MySQL 9.0,SQL Server 2019,PostgreSQL 17.1 Neo4j
 * # OS        : window10
 * Datetime  : 2026 - 2026/7/28 - 21:27
 * User      : geovindu
 * Product   : IntelliJ IDEA
 * Project   : JavaAlgorithms
 * File      : PackageSpec.java
 * explain   : 学习  类
 **/
 
package BreadthFirst.core.models;
 
public class PackageSpec {
    private String specCode;
    private double volume;
    private int stockQty;
    private double unitCost;
    private int priorityWeight;
 
    public double getValidVolume(double coeff) {
        return volume * coeff;
    }
 
    // getter & setter
    public String getSpecCode() {
        return specCode;
    }
 
    public void setSpecCode(String specCode) {
        this.specCode = specCode;
    }
 
    public double getVolume() {
        return volume;
    }
 
    public void setVolume(double volume) {
        this.volume = volume;
    }
 
    public int getStockQty() {
        return stockQty;
    }
 
    public void setStockQty(int stockQty) {
        this.stockQty = stockQty;
    }
 
    public double getUnitCost() {
        return unitCost;
    }
 
    public void setUnitCost(double unitCost) {
        this.unitCost = unitCost;
    }
 
    public int getPriorityWeight() {
        return priorityWeight;
    }
 
    public void setPriorityWeight(int priorityWeight) {
        this.priorityWeight = priorityWeight;
    }
}
 
/**
 * encoding: utf-8
 * 版权所有 2026 ©涂聚文有限公司 ®
 * 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
 * 描述:Breadth First Search Algorithm and Depth First Search Algorithm
 * Author    : geovindu,Geovin Du 涂聚文.
 * IDE       : IntelliJ IDEA 2024.3.6 Java 17
 * # database  : Oracle21c,MySQL 9.0,SQL Server 2019,PostgreSQL 17.1 Neo4j
 * # OS        : window10
 * Datetime  : 2026 - 2026/7/28 - 21:26
 * User      : geovindu
 * Product   : IntelliJ IDEA
 * Project   : JavaAlgorithms
 * File      : PackConstraint.java
 * explain   : 学习  类
 **/
 
package BreadthFirst.core.models;
 
public class PackConstraint {
    private double bufferCoeff;
    private int singleBoxMaxQty;
    private boolean forbidMixJewelry;
    private boolean forbidSplitJewelry;
 
    // getter & setter
    public double getBufferCoeff() {
        return bufferCoeff;
    }
 
    public void setBufferCoeff(double bufferCoeff) {
        this.bufferCoeff = bufferCoeff;
    }
 
    public int getSingleBoxMaxQty() {
        return singleBoxMaxQty;
    }
 
    public void setSingleBoxMaxQty(int singleBoxMaxQty) {
        this.singleBoxMaxQty = singleBoxMaxQty;
    }
 
    public boolean isForbidMixJewelry() {
        return forbidMixJewelry;
    }
 
    public void setForbidMixJewelry(boolean forbidMixJewelry) {
        this.forbidMixJewelry = forbidMixJewelry;
    }
 
    public boolean isForbidSplitJewelry() {
        return forbidSplitJewelry;
    }
 
    public void setForbidSplitJewelry(boolean forbidSplitJewelry) {
        this.forbidSplitJewelry = forbidSplitJewelry;
    }
}
 
/**
 * encoding: utf-8
 * 版权所有 2026 ©涂聚文有限公司 ®
 * 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
 * 描述:Breadth First Search Algorithm and Depth First Search Algorithm
 * Author    : geovindu,Geovin Du 涂聚文.
 * IDE       : IntelliJ IDEA 2024.3.6 Java 17
 * # database  : Oracle21c,MySQL 9.0,SQL Server 2019,PostgreSQL 17.1 Neo4j
 * # OS        : window10
 * Datetime  : 2026 - 2026/7/28 - 21:27
 * User      : geovindu
 * Product   : IntelliJ IDEA
 * Project   : JavaAlgorithms
 * File      : PackQueryCondition.java
 * explain   : 学习  类
 **/
 
package BreadthFirst.core.models;
 
import BreadthFirst.core.strategy.LoadStrategyType;
 
import java.util.List;
 
public class PackQueryCondition {
    private List<JewelryItem> jewelryList;
    private PackConstraint constraint;
    private double totalMaterialVol;
    private List<PackageSpec> packageSpecList;
    private LoadStrategyType strategyType;
    private boolean allowOverLoad;
 
    // getter & setter
    public List<JewelryItem> getJewelryList() {
        return jewelryList;
    }
 
    public void setJewelryList(List<JewelryItem> jewelryList) {
        this.jewelryList = jewelryList;
    }
 
    public PackConstraint getConstraint() {
        return constraint;
    }
 
    public void setConstraint(PackConstraint constraint) {
        this.constraint = constraint;
    }
 
    public double getTotalMaterialVol() {
        return totalMaterialVol;
    }
 
    public void setTotalMaterialVol(double totalMaterialVol) {
        this.totalMaterialVol = totalMaterialVol;
    }
 
    public List<PackageSpec> getPackageSpecList() {
        return packageSpecList;
    }
 
    public void setPackageSpecList(List<PackageSpec> packageSpecList) {
        this.packageSpecList = packageSpecList;
    }
 
    public LoadStrategyType getStrategyType() {
        return strategyType;
    }
 
    public void setStrategyType(LoadStrategyType strategyType) {
        this.strategyType = strategyType;
    }
 
    public boolean isAllowOverLoad() {
        return allowOverLoad;
    }
 
    public void setAllowOverLoad(boolean allowOverLoad) {
        this.allowOverLoad = allowOverLoad;
    }
}
 
/**
 * encoding: utf-8
 * 版权所有 2026 ©涂聚文有限公司 ®
 * 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
 * 描述:Breadth First Search Algorithm and Depth First Search Algorithm
 * Author    : geovindu,Geovin Du 涂聚文.
 * IDE       : IntelliJ IDEA 2024.3.6 Java 17
 * # database  : Oracle21c,MySQL 9.0,SQL Server 2019,PostgreSQL 17.1 Neo4j
 * # OS        : window10
 * Datetime  : 2026 - 2026/7/28 - 21:29
 * User      : geovindu
 * Product   : IntelliJ IDEA
 * Project   : JavaAlgorithms
 * File      : PackResult.java
 * explain   : 学习  类
 **/
 
package BreadthFirst.core.models;
 
import java.util.Dictionary;
import java.util.List;
import java.util.Map;
 
public class PackResult {
    private int code;
    private String msg;
    private List<SingleUsePackage> usePackageList;
    private double remainMaterialVol;
    private Map<String, Integer> remainPackageStock;
    private int totalUsePackageCnt;
    private int searchStateCount;
    private boolean fullLoadFlag;
    private double jewelryTotalCalcVol;
    private double totalCost;
 
    // getter & setter
    public int getCode() {
        return code;
    }
 
    public void setCode(int code) {
        this.code = code;
    }
 
    public String getMsg() {
        return msg;
    }
 
    public void setMsg(String msg) {
        this.msg = msg;
    }
 
    public List<SingleUsePackage> getUsePackageList() {
        return usePackageList;
    }
 
    public void setUsePackageList(List<SingleUsePackage> usePackageList) {
        this.usePackageList = usePackageList;
    }
 
    public double getRemainMaterialVol() {
        return remainMaterialVol;
    }
 
    public void setRemainMaterialVol(double remainMaterialVol) {
        this.remainMaterialVol = remainMaterialVol;
    }
 
    public Map<String, Integer> getRemainPackageStock() {
        return remainPackageStock;
    }
 
    public void setRemainPackageStock(Map<String, Integer> remainPackageStock) {
        this.remainPackageStock = remainPackageStock;
    }
 
    public int getTotalUsePackageCnt() {
        return totalUsePackageCnt;
    }
 
    public void setTotalUsePackageCnt(int totalUsePackageCnt) {
        this.totalUsePackageCnt = totalUsePackageCnt;
    }
 
    public int getSearchStateCount() {
        return searchStateCount;
    }
 
    public void setSearchStateCount(int searchStateCount) {
        this.searchStateCount = searchStateCount;
    }
 
    public boolean isFullLoadFlag() {
        return fullLoadFlag;
    }
 
    public void setFullLoadFlag(boolean fullLoadFlag) {
        this.fullLoadFlag = fullLoadFlag;
    }
 
    public double getJewelryTotalCalcVol() {
        return jewelryTotalCalcVol;
    }
 
    public void setJewelryTotalCalcVol(double jewelryTotalCalcVol) {
        this.jewelryTotalCalcVol = jewelryTotalCalcVol;
    }
 
    public double getTotalCost() {
        return totalCost;
    }
 
    public void setTotalCost(double totalCost) {
        this.totalCost = totalCost;
    }
}
 
/**
 * encoding: utf-8
 * 版权所有 2026 ©涂聚文有限公司 ®
 * 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
 * 描述:Breadth First Search Algorithm and Depth First Search Algorithm
 * Author    : geovindu,Geovin Du 涂聚文.
 * IDE       : IntelliJ IDEA 2024.3.6 Java 17
 * # database  : Oracle21c,MySQL 9.0,SQL Server 2019,PostgreSQL 17.1 Neo4j
 * # OS        : window10
 * Datetime  : 2026 - 2026/7/28 - 21:29
 * User      : geovindu
 * Product   : IntelliJ IDEA
 * Project   : JavaAlgorithms
 * File      : SingleUsePackage.java
 * explain   : 学习  类
 **/
 
package BreadthFirst.core.models;
 
public class SingleUsePackage {
    private String specCode;
    private double volume;
    private double validVolume;
    private int useCount;
    private String belongJewelryCode;
 
    // getter & setter
    public String getSpecCode() {
        return specCode;
    }
 
    public void setSpecCode(String specCode) {
        this.specCode = specCode;
    }
 
    public double getVolume() {
        return volume;
    }
 
    public void setVolume(double volume) {
        this.volume = volume;
    }
 
    public double getValidVolume() {
        return validVolume;
    }
 
    public void setValidVolume(double validVolume) {
        this.validVolume = validVolume;
    }
 
    public int getUseCount() {
        return useCount;
    }
 
    public void setUseCount(int useCount) {
        this.useCount = useCount;
    }
 
    public String getBelongJewelryCode() {
        return belongJewelryCode;
    }
 
    public void setBelongJewelryCode(String belongJewelryCode) {
        this.belongJewelryCode = belongJewelryCode;
    }
}


/**
 * encoding: utf-8
 * 版权所有 2026 ©涂聚文有限公司 ®
 * 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
 * 描述:Breadth First Search Algorithm and Depth First Search Algorithm
 * Author    : geovindu,Geovin Du 涂聚文.
 * IDE       : IntelliJ IDEA 2024.3.6 Java 17
 * # database  : Oracle21c,MySQL 9.0,SQL Server 2019,PostgreSQL 17.1 Neo4j
 * # OS        : window10
 * Datetime  : 2026 - 2026/7/28 - 21:33
 * User      : geovindu
 * Product   : IntelliJ IDEA
 * Project   : JavaAlgorithms
 * File      : AStarStrategy.java
 * explain   : 学习  类
 **/
 
package BreadthFirst.core.strategy;
 
import BreadthFirst.config.AppConfig;
import BreadthFirst.core.AppLogger;
import BreadthFirst.core.enums.ResultCode;
import BreadthFirst.core.models.*;
 
import java.util.*;
 
public class AStarStrategy implements IPackStrategy {
    private final AppLogger logger = AppLogger.getInstance();
 
    private record AStarItem(double f, int g, double loadVol, double cost, Map<String, Integer> usedMap) {
    }
 
    @Override
    public PackResult execute(PackQueryCondition condition) {
        double totalMatVol = condition.getTotalMaterialVol();
        List<PackageSpec> specList = condition.getPackageSpecList();
        PackConstraint cons = condition.getConstraint();
 
        if (totalMatVol <= 0) {
            PackResult res = new PackResult();
            res.setCode(ResultCode.PARAM_ERROR.code());
            res.setMsg(ResultCode.PARAM_ERROR.msg());
            res.setRemainMaterialVol(totalMatVol);
            res.setRemainPackageStock(new HashMap<>());
            return res;
        }
        if (specList.isEmpty()) {
            PackResult res = new PackResult();
            res.setCode(ResultCode.NO_AVAILABLE_PACKAGE.code());
            res.setMsg(ResultCode.NO_AVAILABLE_PACKAGE.msg());
            res.setRemainMaterialVol(totalMatVol);
            res.setRemainPackageStock(new HashMap<>());
            return res;
        }
 
        Map<String, Integer> initStock = new HashMap<>();
        Map<String, Double> specVolMap = new HashMap<>();
        Map<String, Double> specCostMap = new HashMap<>();
        Map<String, Integer> specWeightMap = new HashMap<>();
        double bufferCoeff = cons != null ? cons.getBufferCoeff() : 1.0;
        for (PackageSpec item : specList) {
            initStock.put(item.getSpecCode(), item.getStockQty());
            specVolMap.put(item.getSpecCode(), item.getValidVolume(bufferCoeff));
            specCostMap.put(item.getSpecCode(), item.getUnitCost());
            specWeightMap.put(item.getSpecCode(), item.getPriorityWeight());
        }
 
        int needCount = 0;
        double singleVolume = 0;
        if (condition.getJewelryList() != null && !condition.getJewelryList().isEmpty()) {
            JewelryItem item = condition.getJewelryList().get(0);
            needCount = item.getQuantity();
            singleVolume = item.getSingleVolume();
        }
 
        List<String> validSpecCodes = new ArrayList<>();
        for (String code : specVolMap.keySet()) {
            if (specVolMap.get(code) >= singleVolume) {
                validSpecCodes.add(code);
            }
        }
        if (validSpecCodes.isEmpty()) {
            PackResult res = new PackResult();
            res.setCode(ResultCode.NO_ANY_LOAD_PLAN.code());
            res.setMsg(ResultCode.NO_ANY_LOAD_PLAN.msg());
            res.setRemainMaterialVol(totalMatVol);
            res.setRemainPackageStock(new HashMap<>(initStock));
            res.setSearchStateCount(1);
            return res;
        }
 
        validSpecCodes.sort((a, b) -> {
            int costCompare = Double.compare(specCostMap.get(a), specCostMap.get(b));
            if (costCompare != 0) return costCompare;
            return Integer.compare(specWeightMap.get(b), specWeightMap.get(a));
        });
 
        String bestSpecCode = validSpecCodes.get(0);
        int availableStock = initStock.get(bestSpecCode);
 
        if (needCount <= availableStock) {
            Map<String, Integer> remainStock = new HashMap<>(initStock);
            remainStock.put(bestSpecCode, availableStock - needCount);
 
            List<SingleUsePackage> useList = new ArrayList<>();
            SingleUsePackage sup = new SingleUsePackage();
            sup.setSpecCode(bestSpecCode);
            sup.setVolume(specList.stream().filter(p -> p.getSpecCode().equals(bestSpecCode)).findFirst().get().getVolume());
            sup.setValidVolume(specVolMap.get(bestSpecCode));
            sup.setUseCount(needCount);
            if (condition.getJewelryList() != null && !condition.getJewelryList().isEmpty()) {
                sup.setBelongJewelryCode(condition.getJewelryList().get(0).getJewelryCode());
            }
            useList.add(sup);
 
            PackResult result = new PackResult();
            result.setCode(ResultCode.SUCCESS.code());
            result.setMsg(ResultCode.SUCCESS.msg());
            result.setUsePackageList(useList);
            result.setRemainMaterialVol(0);
            result.setRemainPackageStock(remainStock);
            result.setTotalUsePackageCnt(needCount);
            result.setSearchStateCount(1);
            result.setFullLoadFlag(true);
            result.setTotalCost(specCostMap.get(bestSpecCode) * needCount);
            return result;
        }
 
        Map<String, Integer> startUsed = new HashMap<>();
        for (String k : initStock.keySet()) startUsed.put(k, 0);
 
        Set<String> visited = new HashSet<>();
        int visitedCount = 1;
        PriorityQueue<AStarItem> heap = new PriorityQueue<>(Comparator.comparingDouble(a -> a.f()));
        heap.offer(new AStarItem(0, 0, 0, 0, new HashMap<>(startUsed)));
 
        AStarItem best = null;
 
        while (!heap.isEmpty()) {
            AStarItem top = heap.poll();
            String key = getStateKey(top.usedMap());
            if (visited.contains(key)) continue;
            visited.add(key);
            visitedCount++;
 
            if (top.g() >= needCount) {
                best = top;
                break;
            }
            if (visitedCount >= AppConfig.MAX_SEARCH_STATE) {
                logger.warn("A*搜索达到最大状态限制");
                break;
            }
 
            for (String specCode : validSpecCodes) {
                int stockNum = initStock.get(specCode);
                int usedNum = top.usedMap().get(specCode);
                if (usedNum >= stockNum) continue;
 
                int newG = top.g() + 1;
                double newCost = top.cost() + specCostMap.get(specCode);
                double h = (needCount - newG) * specCostMap.get(bestSpecCode) + newCost * 0.15 - specWeightMap.get(specCode) * 0.005;
                double newF = newG + h;
 
                Map<String, Integer> newUsed = new HashMap<>(top.usedMap);
                newUsed.put(specCode, newUsed.get(specCode) + 1);
                heap.offer(new AStarItem(newF, newG, top.loadVol() + specVolMap.get(specCode), newCost, newUsed));
            }
        }
 
        if (best == null) {
            PackResult res = new PackResult();
            res.setCode(ResultCode.NO_ANY_LOAD_PLAN.code());
            res.setMsg(ResultCode.NO_ANY_LOAD_PLAN.msg());
            res.setRemainMaterialVol(totalMatVol);
            res.setRemainPackageStock(new HashMap<>(initStock));
            res.setSearchStateCount(visitedCount);
            return res;
        }
 
        Map<String, Integer> remainStock = new HashMap<>();
        List<SingleUsePackage> useList = new ArrayList<>();
        for (Map.Entry<String, Integer> kv : initStock.entrySet()) {
            String code = kv.getKey();
            int totalStock = kv.getValue();
            int useCnt = best.usedMap().get(code);
            remainStock.put(code, totalStock - useCnt);
            if (useCnt > 0) {
                SingleUsePackage sup = new SingleUsePackage();
                sup.setSpecCode(code);
                sup.setVolume(specVolMap.get(code));
                sup.setValidVolume(specVolMap.get(code));
                sup.setUseCount(useCnt);
                if (condition.getJewelryList() != null && !condition.getJewelryList().isEmpty()) {
                    sup.setBelongJewelryCode(condition.getJewelryList().get(0).getJewelryCode());
                }
                useList.add(sup);
            }
        }
 
        PackResult result = new PackResult();
        result.setCode(ResultCode.SUCCESS.code());
        result.setMsg(ResultCode.SUCCESS.msg());
        result.setUsePackageList(useList);
        result.setRemainMaterialVol(0);
        result.setRemainPackageStock(remainStock);
        result.setTotalUsePackageCnt(best.g());
        result.setSearchStateCount(visitedCount);
        result.setFullLoadFlag(true);
        result.setTotalCost(best.cost());
        return result;
    }
 
    private String getStateKey(Map<String, Integer> map) {
        List<String> list = new ArrayList<>();
        for (Map.Entry<String, Integer> e : map.entrySet()) {
            list.add(e.getKey() + ":" + e.getValue());
        }
        Collections.sort(list);
        return String.join(",", list);
    }
}
 
/**
 * encoding: utf-8
 * 版权所有 2026 ©涂聚文有限公司 ®
 * 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
 * 描述:Breadth First Search Algorithm and Depth First Search Algorithm
 * Author    : geovindu,Geovin Du 涂聚文.
 * IDE       : IntelliJ IDEA 2024.3.6 Java 17
 * # database  : Oracle21c,MySQL 9.0,SQL Server 2019,PostgreSQL 17.1 Neo4j
 * # OS        : window10
 * Datetime  : 2026 - 2026/7/28 - 21:31
 * User      : geovindu
 * Product   : IntelliJ IDEA
 * Project   : JavaAlgorithms
 * File      : BfsStrategy.java
 * explain   : 学习  类
 **/
 
package BreadthFirst.core.strategy;
 
import BreadthFirst.config.AppConfig;
import BreadthFirst.core.AppLogger;
import BreadthFirst.core.enums.ResultCode;
import BreadthFirst.core.models.*;
 
import java.util.*;
 
public class BfsStrategy implements IPackStrategy {
    private final AppLogger logger = AppLogger.getInstance();
 
    private record BfsNode(double loadVol, Map<String, Integer> usedMap, double totalCost, int weightSum, int packCount) {
    }
 
    @Override
    public PackResult execute(PackQueryCondition condition) {
        double totalMatVol = condition.getTotalMaterialVol();
        List<PackageSpec> specList = condition.getPackageSpecList();
        boolean allowOver = condition.isAllowOverLoad();
        int maxState = AppConfig.MAX_SEARCH_STATE;
        PackConstraint cons = condition.getConstraint();
 
        if (totalMatVol <= 0) {
            PackResult res = new PackResult();
            res.setCode(ResultCode.PARAM_ERROR.code());
            res.setMsg(ResultCode.PARAM_ERROR.msg());
            res.setRemainMaterialVol(totalMatVol);
            res.setRemainPackageStock(new HashMap<>());
            return res;
        }
        if (specList.isEmpty()) {
            PackResult res = new PackResult();
            res.setCode(ResultCode.NO_AVAILABLE_PACKAGE.code());
            res.setMsg(ResultCode.NO_AVAILABLE_PACKAGE.msg());
            res.setRemainMaterialVol(totalMatVol);
            res.setRemainPackageStock(new HashMap<>());
            return res;
        }
 
        Map<String, Integer> initStock = new HashMap<>();
        Map<String, Double> specVolMap = new HashMap<>();
        Map<String, Double> specCostMap = new HashMap<>();
        Map<String, Integer> specWeightMap = new HashMap<>();
        double bufferCoeff = cons != null ? cons.getBufferCoeff() : 1.0;
        for (PackageSpec item : specList) {
            initStock.put(item.getSpecCode(), item.getStockQty());
            specVolMap.put(item.getSpecCode(), item.getValidVolume(bufferCoeff));
            specCostMap.put(item.getSpecCode(), item.getUnitCost());
            specWeightMap.put(item.getSpecCode(), item.getPriorityWeight());
        }
 
        int totalJewelryCount = 0;
        double singleVolume = 0;
        if (condition.getJewelryList() != null && !condition.getJewelryList().isEmpty()) {
            JewelryItem item = condition.getJewelryList().get(0);
            totalJewelryCount = item.getQuantity();
            singleVolume = item.getSingleVolume();
        }
 
        List<String> validSpecCodes = new ArrayList<>();
        for (String code : specVolMap.keySet()) {
            if (specVolMap.get(code) >= singleVolume) {
                validSpecCodes.add(code);
            }
        }
        if (validSpecCodes.isEmpty()) {
            PackResult res = new PackResult();
            res.setCode(ResultCode.NO_ANY_LOAD_PLAN.code());
            res.setMsg(ResultCode.NO_ANY_LOAD_PLAN.msg());
            res.setRemainMaterialVol(totalMatVol);
            res.setRemainPackageStock(cloneMap(initStock));
            res.setSearchStateCount(1);
            return res;
        }
 
        validSpecCodes.sort((a, b) -> {
            int costCompare = Double.compare(specCostMap.get(a), specCostMap.get(b));
            if (costCompare != 0) return costCompare;
            return Integer.compare(specWeightMap.get(b), specWeightMap.get(a));
        });
 
        String bestSpecCode = validSpecCodes.get(0);
        int needCount = totalJewelryCount;
        int availableStock = initStock.get(bestSpecCode);
 
        if (needCount > availableStock) {
            logger.warn(String.format("【库存不足】规格%s库存%d,需要%d,尝试其他规格组合",
                    bestSpecCode, availableStock, needCount));
            return findCombinedSolution(needCount, validSpecCodes, initStock, specVolMap, specCostMap, specWeightMap, totalMatVol);
        }
 
        Map<String, Integer> remainStock = cloneMap(initStock);
        remainStock.put(bestSpecCode, availableStock - needCount);
 
        List<SingleUsePackage> useList = new ArrayList<>();
        SingleUsePackage sup = new SingleUsePackage();
        sup.setSpecCode(bestSpecCode);
        sup.setVolume(specList.stream().filter(p -> p.getSpecCode().equals(bestSpecCode)).findFirst().get().getVolume());
        sup.setValidVolume(specVolMap.get(bestSpecCode));
        sup.setUseCount(needCount);
        if (condition.getJewelryList() != null && !condition.getJewelryList().isEmpty()) {
            sup.setBelongJewelryCode(condition.getJewelryList().get(0).getJewelryCode());
        }
        useList.add(sup);
 
        PackResult result = new PackResult();
        result.setCode(ResultCode.SUCCESS.code());
        result.setMsg(ResultCode.SUCCESS.msg());
        result.setUsePackageList(useList);
        result.setRemainMaterialVol(0);
        result.setRemainPackageStock(remainStock);
        result.setTotalUsePackageCnt(needCount);
        result.setSearchStateCount(1);
        result.setFullLoadFlag(true);
        result.setTotalCost(specCostMap.get(bestSpecCode) * needCount);
        return result;
    }
 
    private PackResult findCombinedSolution(int needCount, List<String> validSpecCodes, Map<String, Integer> initStock,
                                            Map<String, Double> specVolMap, Map<String, Double> specCostMap,
                                            Map<String, Integer> specWeightMap, double totalMatVol) {
        Map<String, Integer> startUsed = new HashMap<>();
        for (String k : initStock.keySet()) startUsed.put(k, 0);
 
        Set<String> visited = new HashSet<>();
        int visitedCount = 1;
        Queue<BfsNode> queue = new LinkedList<>();
        BfsNode startNode = new BfsNode(0, cloneMap(startUsed), 0, 0, 0);
        queue.offer(startNode);
        visited.add(getStateKey(startUsed));
 
        BfsNode bestNode = null;
 
        while (!queue.isEmpty()) {
            BfsNode node = queue.poll();
            if (node.packCount() >= needCount) {
                if (bestNode == null) {
                    bestNode = node;
                } else {
                    boolean better = node.totalCost() < bestNode.totalCost()
                            || (node.totalCost() == bestNode.totalCost() && node.weightSum() > bestNode.weightSum());
                    if (better) {
                        bestNode = node;
                    }
                }
                continue;
            }
            if (visitedCount >= AppConfig.MAX_SEARCH_STATE) {
                logger.warn("搜索达到最大状态限制 " + AppConfig.MAX_SEARCH_STATE + ",终止搜索");
                break;
            }
 
            for (String specCode : validSpecCodes) {
                int stockNum = initStock.get(specCode);
                int usedNum = node.usedMap().get(specCode);
                if (usedNum >= stockNum) continue;
 
                Map<String, Integer> newUsed = cloneMap(node.usedMap());
                newUsed.put(specCode, newUsed.get(specCode) + 1);
                String key = getStateKey(newUsed);
                if (visited.contains(key)) continue;
 
                visited.add(key);
                visitedCount++;
                BfsNode newNode = new BfsNode(
                        node.loadVol() + specVolMap.get(specCode),
                        newUsed,
                        node.totalCost() + specCostMap.get(specCode),
                        node.weightSum() + specWeightMap.get(specCode),
                        node.packCount() + 1
                );
                queue.offer(newNode);
            }
        }
 
        if (bestNode == null) {
            PackResult res = new PackResult();
            res.setCode(ResultCode.NO_ANY_LOAD_PLAN.code());
            res.setMsg(ResultCode.NO_ANY_LOAD_PLAN.msg());
            res.setRemainMaterialVol(totalMatVol);
            res.setRemainPackageStock(cloneMap(initStock));
            res.setSearchStateCount(visitedCount);
            return res;
        }
 
        Map<String, Integer> remainStock = new HashMap<>();
        List<SingleUsePackage> useList = new ArrayList<>();
        for (Map.Entry<String, Integer> kv : initStock.entrySet()) {
            String code = kv.getKey();
            int totalStock = kv.getValue();
            int useCnt = bestNode.usedMap().get(code);
            remainStock.put(code, totalStock - useCnt);
            if (useCnt > 0) {
                SingleUsePackage sup = new SingleUsePackage();
                sup.setSpecCode(code);
                sup.setVolume(specVolMap.get(code));
                sup.setValidVolume(specVolMap.get(code));
                sup.setUseCount(useCnt);
                useList.add(sup);
            }
        }
 
        PackResult result = new PackResult();
        result.setCode(ResultCode.SUCCESS.code());
        result.setMsg(ResultCode.SUCCESS.msg());
        result.setUsePackageList(useList);
        result.setRemainMaterialVol(0);
        result.setRemainPackageStock(remainStock);
        result.setTotalUsePackageCnt(bestNode.packCount());
        result.setSearchStateCount(visitedCount);
        result.setFullLoadFlag(true);
        result.setTotalCost(bestNode.totalCost());
        return result;
    }
 
    private Map<String, Integer> cloneMap(Map<String, Integer> src) {
        return new HashMap<>(src);
    }
 
    private String getStateKey(Map<String, Integer> map) {
        List<String> list = new ArrayList<>();
        for (Map.Entry<String, Integer> e : map.entrySet()) {
            list.add(e.getKey() + ":" + e.getValue());
        }
        Collections.sort(list);
        return String.join(",", list);
    }
}
 
/**
 * encoding: utf-8
 * 版权所有 2026 ©涂聚文有限公司 ®
 * 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
 * 描述:Breadth First Search Algorithm and Depth First Search Algorithm
 * Author    : geovindu,Geovin Du 涂聚文.
 * IDE       : IntelliJ IDEA 2024.3.6 Java 17
 * # database  : Oracle21c,MySQL 9.0,SQL Server 2019,PostgreSQL 17.1 Neo4j
 * # OS        : window10
 * Datetime  : 2026 - 2026/7/28 - 21:32
 * User      : geovindu
 * Product   : IntelliJ IDEA
 * Project   : JavaAlgorithms
 * File      : DfsStrategy.java
 * explain   : 学习  类
 **/
 
package BreadthFirst.core.strategy;
 
import BreadthFirst.config.AppConfig;
import BreadthFirst.core.AppLogger;
import BreadthFirst.core.enums.ResultCode;
import BreadthFirst.core.models.*;
 
import java.util.*;
 
public class DfsStrategy implements IPackStrategy {
    private final AppLogger logger = AppLogger.getInstance();
    private Map<String, Double> specVolMap = new HashMap<>();
    private Map<String, Double> specCostMap = new HashMap<>();
    private Map<String, Integer> initStock = new HashMap<>();
    private DfsNode bestNode;
    private Set<String> visited = new HashSet<>();
    private int visitedCnt;
    private int needCount;
 
    private record DfsNode(double loadVol, Map<String, Integer> usedMap, double totalCost, int packCnt) {
    }
 
    @Override
    public PackResult execute(PackQueryCondition condition) {
        double totalMatVol = condition.getTotalMaterialVol();
        List<PackageSpec> specList = condition.getPackageSpecList();
        PackConstraint cons = condition.getConstraint();
 
        if (totalMatVol <= 0) {
            PackResult res = new PackResult();
            res.setCode(ResultCode.PARAM_ERROR.code());
            res.setMsg(ResultCode.PARAM_ERROR.msg());
            res.setRemainMaterialVol(totalMatVol);
            res.setRemainPackageStock(new HashMap<>());
            return res;
        }
        if (specList.isEmpty()) {
            PackResult res = new PackResult();
            res.setCode(ResultCode.NO_AVAILABLE_PACKAGE.code());
            res.setMsg(ResultCode.NO_AVAILABLE_PACKAGE.msg());
            res.setRemainMaterialVol(totalMatVol);
            res.setRemainPackageStock(new HashMap<>());
            return res;
        }
 
        initStock.clear();
        specVolMap.clear();
        specCostMap.clear();
        double bufferCoeff = cons != null ? cons.getBufferCoeff() : 1.0;
        for (PackageSpec item : specList) {
            initStock.put(item.getSpecCode(), item.getStockQty());
            specVolMap.put(item.getSpecCode(), item.getValidVolume(bufferCoeff));
            specCostMap.put(item.getSpecCode(), item.getUnitCost());
        }
 
        needCount = 0;
        double singleVolume = 0;
        if (condition.getJewelryList() != null && !condition.getJewelryList().isEmpty()) {
            JewelryItem item = condition.getJewelryList().get(0);
            needCount = item.getQuantity();
            singleVolume = item.getSingleVolume();
        }
 
        List<String> validSpecCodes = new ArrayList<>();
        for (String code : specVolMap.keySet()) {
            if (specVolMap.get(code) >= singleVolume) {
                validSpecCodes.add(code);
            }
        }
        if (validSpecCodes.isEmpty()) {
            PackResult res = new PackResult();
            res.setCode(ResultCode.NO_ANY_LOAD_PLAN.code());
            res.setMsg(ResultCode.NO_ANY_LOAD_PLAN.msg());
            res.setRemainMaterialVol(totalMatVol);
            res.setRemainPackageStock(new HashMap<>(initStock));
            res.setSearchStateCount(1);
            return res;
        }
 
        validSpecCodes.sort(Comparator.comparingDouble(specCostMap::get));
        String bestSpecCode = validSpecCodes.get(0);
        int availableStock = initStock.get(bestSpecCode);
 
        if (needCount <= availableStock) {
            Map<String, Integer> remainStock = new HashMap<>(initStock);
            remainStock.put(bestSpecCode, availableStock - needCount);
 
            List<SingleUsePackage> useList = new ArrayList<>();
            SingleUsePackage sup = new SingleUsePackage();
            sup.setSpecCode(bestSpecCode);
            sup.setVolume(specList.stream().filter(p -> p.getSpecCode().equals(bestSpecCode)).findFirst().get().getVolume());
            sup.setValidVolume(specVolMap.get(bestSpecCode));
            sup.setUseCount(needCount);
            if (condition.getJewelryList() != null && !condition.getJewelryList().isEmpty()) {
                sup.setBelongJewelryCode(condition.getJewelryList().get(0).getJewelryCode());
            }
            useList.add(sup);
 
            PackResult result = new PackResult();
            result.setCode(ResultCode.SUCCESS.code());
            result.setMsg(ResultCode.SUCCESS.msg());
            result.setUsePackageList(useList);
            result.setRemainMaterialVol(0);
            result.setRemainPackageStock(remainStock);
            result.setTotalUsePackageCnt(needCount);
            result.setSearchStateCount(1);
            result.setFullLoadFlag(true);
            result.setTotalCost(specCostMap.get(bestSpecCode) * needCount);
            return result;
        }
 
        Map<String, Integer> startUsed = new HashMap<>();
        for (String k : initStock.keySet()) startUsed.put(k, 0);
        visited.clear();
        visitedCnt = 0;
        bestNode = null;
 
        dfs(0, startUsed, 0, 0, validSpecCodes);
 
        if (bestNode == null) {
            PackResult res = new PackResult();
            res.setCode(ResultCode.NO_ANY_LOAD_PLAN.code());
            res.setMsg(ResultCode.NO_ANY_LOAD_PLAN.msg());
            res.setRemainMaterialVol(totalMatVol);
            res.setRemainPackageStock(new HashMap<>(initStock));
            res.setSearchStateCount(visitedCnt);
            return res;
        }
 
        Map<String, Integer> remainStock = new HashMap<>();
        List<SingleUsePackage> useList = new ArrayList<>();
        for (Map.Entry<String, Integer> kv : initStock.entrySet()) {
            String code = kv.getKey();
            int totalStock = kv.getValue();
            int useCnt = bestNode.usedMap().get(code);
            remainStock.put(code, totalStock - useCnt);
            if (useCnt > 0) {
                SingleUsePackage sup = new SingleUsePackage();
                sup.setSpecCode(code);
                sup.setVolume(specVolMap.get(code));
                sup.setValidVolume(specVolMap.get(code));
                sup.setUseCount(useCnt);
                if (condition.getJewelryList() != null && !condition.getJewelryList().isEmpty()) {
                    sup.setBelongJewelryCode(condition.getJewelryList().get(0).getJewelryCode());
                }
                useList.add(sup);
            }
        }
 
        PackResult result = new PackResult();
        result.setCode(ResultCode.SUCCESS.code());
        result.setMsg(ResultCode.SUCCESS.msg());
        result.setUsePackageList(useList);
        result.setRemainMaterialVol(0);
        result.setRemainPackageStock(remainStock);
        result.setTotalUsePackageCnt(bestNode.packCnt());
        result.setSearchStateCount(visitedCnt);
        result.setFullLoadFlag(true);
        result.setTotalCost(bestNode.totalCost());
        return result;
    }
 
    private void dfs(double loadVol, Map<String, Integer> usedMap, double cost, int packCnt, List<String> validSpecCodes) {
        if (packCnt >= needCount) {
            if (bestNode == null || cost < bestNode.totalCost()) {
                bestNode = new DfsNode(loadVol, new HashMap<>(usedMap), cost, packCnt);
            }
            return;
        }
        if (visitedCnt >= AppConfig.MAX_SEARCH_STATE) return;
 
        String key = getStateKey(usedMap);
        if (visited.contains(key)) return;
        visited.add(key);
        visitedCnt++;
 
        for (String specCode : validSpecCodes) {
            int stockNum = initStock.get(specCode);
            int usedNum = usedMap.get(specCode);
            if (usedNum >= stockNum) continue;
 
            usedMap.put(specCode, usedMap.get(specCode) + 1);
            dfs(loadVol + specVolMap.get(specCode), usedMap, cost + specCostMap.get(specCode), packCnt + 1, validSpecCodes);
            usedMap.put(specCode, usedMap.get(specCode) - 1);
        }
    }
 
    private String getStateKey(Map<String, Integer> map) {
        List<String> list = new ArrayList<>();
        for (Map.Entry<String, Integer> e : map.entrySet()) {
            list.add(e.getKey() + ":" + e.getValue());
        }
        Collections.sort(list);
        return String.join(",", list);
    }
}
 
 
/**
 * encoding: utf-8
 * 版权所有 2026 ©涂聚文有限公司 ®
 * 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
 * 描述:Breadth First Search Algorithm and Depth First Search Algorithm
 * Author    : geovindu,Geovin Du 涂聚文.
 * IDE       : IntelliJ IDEA 2024.3.6 Java 17
 * # database  : Oracle21c,MySQL 9.0,SQL Server 2019,PostgreSQL 17.1 Neo4j
 * # OS        : window10
 * Datetime  : 2026 - 2026/7/28 - 21:29
 * User      : geovindu
 * Product   : IntelliJ IDEA
 * Project   : JavaAlgorithms
 * File      : IPackStrategy.java
 * explain   : 学习  类
 **/
 
package BreadthFirst.core.strategy;
 
import BreadthFirst.core.models.PackQueryCondition;
import BreadthFirst.core.models.PackResult;
 
public interface IPackStrategy {
    PackResult execute(PackQueryCondition condition);
}
 
/**
 * encoding: utf-8
 * 版权所有 2026 ©涂聚文有限公司 ®
 * 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
 * 描述:Breadth First Search Algorithm and Depth First Search Algorithm
 * Author    : geovindu,Geovin Du 涂聚文.
 * IDE       : IntelliJ IDEA 2024.3.6 Java 17
 * # database  : Oracle21c,MySQL 9.0,SQL Server 2019,PostgreSQL 17.1 Neo4j
 * # OS        : window10
 * Datetime  : 2026 - 2026/7/28 - 21:28
 * User      : geovindu
 * Product   : IntelliJ IDEA
 * Project   : JavaAlgorithms
 * File      : LoadStrategyType.java
 * explain   : 学习  类
 **/
 
package BreadthFirst.core.strategy;
 
public enum LoadStrategyType {
    BFS,
    DFS,
    ASTAR
}
 
 
/**
 * encoding: utf-8
 * 版权所有 2026 ©涂聚文有限公司 ®
 * 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
 * 描述:Breadth First Search Algorithm and Depth First Search Algorithm
 * Author    : geovindu,Geovin Du 涂聚文.
 * IDE       : IntelliJ IDEA 2024.3.6 Java 17
 * # database  : Oracle21c,MySQL 9.0,SQL Server 2019,PostgreSQL 17.1 Neo4j
 * # OS        : window10
 * Datetime  : 2026 - 2026/7/28 - 21:30
 * User      : geovindu
 * Product   : IntelliJ IDEA
 * Project   : JavaAlgorithms
 * File      : AppLogger.java
 * explain   : 学习  类
 **/
 
package BreadthFirst.core;
 
import BreadthFirst.config.AppConfig;
 
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
 
public class AppLogger {
    private static AppLogger instance;
    private static final Object lock = new Object();
    private final String logPath;
    private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 
    private AppLogger() {
        AppConfig.initLogDir();
        logPath = AppConfig.LOG_SAVE_PATH + File.separator + AppConfig.LOG_FILE_NAME;
    }
 
    public static AppLogger getInstance() {
        if (instance == null) {
            synchronized (lock) {
                if (instance == null) {
                    instance = new AppLogger();
                }
            }
        }
        return instance;
    }
 
    private void write(String level, String message) {
        String time = sdf.format(new Date());
        String line = String.format("[%s] [%s] %s", time, level, message);
        if (AppConfig.LOG_CONSOLE_ENABLE) {
            System.out.println(line);
        }
        if (AppConfig.LOG_FILE_ENABLE) {
            try (PrintWriter pw = new PrintWriter(new FileWriter(logPath, StandardCharsets.UTF_8), true)) {
                pw.println(line);
            } catch (IOException ignored) {
            }
        }
    }
 
    public void info(String msg) {
        write("INFO", msg);
    }
 
    public void warn(String msg) {
        write("WARN", msg);
    }
 
    public void error(String msg) {
        write("ERROR", msg);
    }
}
 
/**
 * encoding: utf-8
 * 版权所有 2026 ©涂聚文有限公司 ®
 * 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
 * 描述:Breadth First Search Algorithm and Depth First Search Algorithm
 * Author    : geovindu,Geovin Du 涂聚文.
 * IDE       : IntelliJ IDEA 2024.3.6 Java 17
 * # database  : Oracle21c,MySQL 9.0,SQL Server 2019,PostgreSQL 17.1 Neo4j
 * # OS        : window10
 * Datetime  : 2026 - 2026/7/28 - 21:35
 * User      : geovindu
 * Product   : IntelliJ IDEA
 * Project   : JavaAlgorithms
 * File      : JewelryPackageService.java
 * explain   : 学习  类
 **/
 
package BreadthFirst.service;
 
import BreadthFirst.concurrency.SafeTaskPool;
import BreadthFirst.core.AppLogger;
import BreadthFirst.core.models.*;
import BreadthFirst.core.strategy.AStarStrategy;
import BreadthFirst.core.strategy.BfsStrategy;
import BreadthFirst.core.strategy.DfsStrategy;
import BreadthFirst.core.strategy.IPackStrategy;
import BreadthFirst.core.strategy.LoadStrategyType;
import BreadthFirst.core.enums.ResultCode;
 
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
public class JewelryPackageService {
    private final AppLogger logger = AppLogger.getInstance();
 
    public double calcJewelryTotalVolume(List<JewelryItem> list) {
        double total = 0;
        logger.info("=====开始计算各类首饰物料体积明细=====");
        for (JewelryItem item : list) {
            double vol = item.getTotalVolume();
            logger.info(String.format("货号:%s | 单件体积:%.2f cm³ | 数量:%d | 品类合计:%.2f cm³",
                    item.getJewelryCode(), item.getSingleVolume(), item.getQuantity(), vol));
            total += vol;
        }
        logger.info(String.format("【汇总完成】所有首饰物料理论总体积 = %.2f cm³", total));
        return total;
    }
 
    public boolean checkForbidSplitConstraint(JewelryItem item, List<PackageSpec> packages, double coeff) {
        double singleV = item.getSingleVolume();
        for (PackageSpec pkg : packages) {
            double valid = pkg.getValidVolume(coeff);
            if (singleV <= valid)
                return true;
        }
        logger.error(String.format("【约束冲突】首饰%s单件体积%.2f,无盒子可容纳,禁止拆盒模式无解",
                item.getJewelryCode(), singleV));
        return false;
    }
 
    public List<PackQueryCondition> splitByJewelry(PackQueryCondition origin) {
        PackConstraint cons = origin.getConstraint();
        if (cons == null || !cons.isForbidMixJewelry()) {
            List<PackQueryCondition> list = new ArrayList<>();
            list.add(origin);
            return list;
        }
 
        logger.info("【启用禁止混装规则】各首饰品类独立计算包装方案");
        List<PackQueryCondition> subList = new ArrayList<>();
        for (JewelryItem j : origin.getJewelryList()) {
            if (cons.isForbidSplitJewelry()) {
                if (!checkForbidSplitConstraint(j, origin.getPackageSpecList(), cons.getBufferCoeff()))
                    continue;
            }
            PackQueryCondition subCond = new PackQueryCondition();
            List<JewelryItem> subItemList = new ArrayList<>();
            subItemList.add(j);
            subCond.setJewelryList(subItemList);
            subCond.setConstraint(cons);
            subCond.setTotalMaterialVol(j.getTotalVolume());
            subCond.setPackageSpecList(origin.getPackageSpecList());
            subCond.setStrategyType(origin.getStrategyType());
            subCond.setAllowOverLoad(origin.isAllowOverLoad());
            subList.add(subCond);
        }
        return subList;
    }
 
    public IPackStrategy createStrategy(LoadStrategyType type) {
        return switch (type) {
            case BFS -> new BfsStrategy();
            case DFS -> new DfsStrategy();
            case ASTAR -> new AStarStrategy();
        };
    }
 
    public PackResult calcSinglePlan(PackQueryCondition condition) throws Exception {
        if (condition.getConstraint() != null) {
            PackConstraint c = condition.getConstraint();
            logger.info("=======装箱约束参数=======");
            logger.info("缓冲空隙系数:" + c.getBufferCoeff());
            logger.info("单盒最大首饰件数:" + c.getSingleBoxMaxQty());
            logger.info("禁止不同品类混装:" + c.isForbidMixJewelry());
            logger.info("禁止同品类拆盒:" + c.isForbidSplitJewelry());
        }
 
        double calcVol = 0;
        if (condition.getJewelryList() != null && !condition.getJewelryList().isEmpty()) {
            calcVol = calcJewelryTotalVolume(condition.getJewelryList());
            condition.setTotalMaterialVol(calcVol);
        }
 
        List<PackQueryCondition> subConditions = splitByJewelry(condition);
        IPackStrategy strategy = createStrategy(condition.getStrategyType());
        SafeTaskPool pool = SafeTaskPool.getInstance();
        List<PackResult> subResults = new ArrayList<>();
 
        for (PackQueryCondition subCond : subConditions) {
            subResults.add(pool.submit(strategy, subCond));
        }
 
        PackResult mergeResult = mergeSubResult(subResults, condition);
        mergeResult.setJewelryTotalCalcVol(calcVol);
        printResultLog(mergeResult);
        return mergeResult;
    }
 
    public PackResult mergeSubResult(List<PackResult> subList, PackQueryCondition origin) {
        List<SingleUsePackage> usePackages = new ArrayList<>();
        Map<String, Integer> remainStock = new HashMap<>();
        double remainMatSum = 0;
        int totalSearch = 0;
        double totalCostSum = 0;
        boolean allSuccess = true;
 
        for (PackageSpec pkg : origin.getPackageSpecList())
            remainStock.put(pkg.getSpecCode(), pkg.getStockQty());
 
        for (PackResult sub : subList) {
            if (sub.getCode() != ResultCode.SUCCESS.code()) allSuccess = false;
            remainMatSum += sub.getRemainMaterialVol();
            totalSearch += sub.getSearchStateCount();
            totalCostSum += sub.getTotalCost();
            usePackages.addAll(sub.getUsePackageList());
 
            for (SingleUsePackage pu : sub.getUsePackageList())
                remainStock.put(pu.getSpecCode(), remainStock.get(pu.getSpecCode()) - pu.getUseCount());
        }
 
        int totalPackCnt = 0;
        for (SingleUsePackage p : usePackages) totalPackCnt += p.getUseCount();
        boolean fullFlag = remainMatSum <= 0.01;
 
        if (!allSuccess) {
            PackResult res = new PackResult();
            res.setCode(ResultCode.NO_ANY_LOAD_PLAN.code());
            res.setMsg("部分品类无法找到可行包装方案");
            res.setUsePackageList(usePackages);
            res.setRemainMaterialVol(Math.round(remainMatSum * 100) / 100.0);
            res.setRemainPackageStock(remainStock);
            res.setTotalUsePackageCnt(totalPackCnt);
            res.setSearchStateCount(totalSearch);
            res.setFullLoadFlag(fullFlag);
            res.setTotalCost(totalCostSum);
            return res;
        }
 
        PackResult res = new PackResult();
        res.setCode(ResultCode.SUCCESS.code());
        res.setMsg(ResultCode.SUCCESS.msg());
        res.setUsePackageList(usePackages);
        res.setRemainMaterialVol(Math.round(remainMatSum * 100) / 100.0);
        res.setRemainPackageStock(remainStock);
        res.setTotalUsePackageCnt(totalPackCnt);
        res.setSearchStateCount(totalSearch);
        res.setFullLoadFlag(fullFlag);
        res.setTotalCost(totalCostSum);
        return res;
    }
 
    public void printResultLog(PackResult res) {
        logger.info("=====================配载结果=====================");
        logger.info(String.format("首饰明细汇总理论总体积:%.2f cm³", res.getJewelryTotalCalcVol()));
        logger.info(String.format("返回码:%d 消息:%s", res.getCode(), res.getMsg()));
        logger.info("搜索遍历状态总数:" + res.getSearchStateCount());
        logger.info("总共使用包装数量:" + res.getTotalUsePackageCnt());
        logger.info(String.format("方案预估总成本:%.2f", res.getTotalCost()));
        logger.info("物料是否完全装满:" + res.isFullLoadFlag());
        logger.info(String.format("剩余未装入物料体积:%.2f cm³", res.getRemainMaterialVol()));
        logger.info("本次选用包装清单:");
        for (SingleUsePackage item : res.getUsePackageList()) {
            logger.info(String.format("    编码:%s 标称容积:%.2fcm³ 有效容积:%.2fcm³ 使用数量:%d 装载品类:%s",
                    item.getSpecCode(), item.getVolume(), item.getValidVolume(), item.getUseCount(), item.getBelongJewelryCode()));
        }
        logger.info("包装库存剩余情况:");
        for (Map.Entry<String, Integer> kv : res.getRemainPackageStock().entrySet()) {
            logger.info(String.format("    %s 剩余库存:%d", kv.getKey(), kv.getValue()));
        }
        logger.info("==================================================");
    }
}
 
 
/**
 * encoding: utf-8
 * 版权所有 2026 ©涂聚文有限公司 ®
 * 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
 * 描述:Breadth First Search Algorithm and Depth First Search Algorithm
 * Author    : geovindu,Geovin Du 涂聚文.
 * IDE       : IntelliJ IDEA 2024.3.6 Java 17
 * # database  : Oracle21c,MySQL 9.0,SQL Server 2019,PostgreSQL 17.1 Neo4j
 * # OS        : window10
 * Datetime  : 2026 - 2026/7/28 - 21:34
 * User      : geovindu
 * Product   : IntelliJ IDEA
 * Project   : JavaAlgorithms
 * File      : SafeTaskPool.java
 * explain   : 学习  类
 **/
 
package BreadthFirst.concurrency;
 
import BreadthFirst.config.AppConfig;
import BreadthFirst.core.strategy.IPackStrategy;
import BreadthFirst.core.models.PackQueryCondition;
import BreadthFirst.core.models.PackResult;
 
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
 
public class SafeTaskPool {
    private static SafeTaskPool instance;
    private static final Object lock = new Object();
    private final ExecutorService executor;
 
    private SafeTaskPool(int maxWorker) {
        executor = Executors.newFixedThreadPool(maxWorker);
    }
 
    public static SafeTaskPool getInstance() {
        if (instance == null) {
            synchronized (lock) {
                if (instance == null) {
                    instance = new SafeTaskPool(AppConfig.MAX_WORKER_THREAD);
                }
            }
        }
        return instance;
    }
 
    public PackResult submit(IPackStrategy strategy, PackQueryCondition condition) throws Exception {
        Future<PackResult> future = executor.submit(() -> strategy.execute(condition));
        return future.get();
    }
}



/**
 * encoding: utf-8
 * 版权所有 2026 ©涂聚文有限公司 ®
 * 许可信息查看:言語成了邀功盡責的功臣,還需要行爲每日來值班嗎
 * 描述:Breadth First Search Algorithm and Depth First Search Algorithm
 * Author    : geovindu,Geovin Du 涂聚文.
 * IDE       : IntelliJ IDEA 2024.3.6 Java 17
 * # database  : Oracle21c,MySQL 9.0,SQL Server 2019,PostgreSQL 17.1 Neo4j
 * # OS        : window10
 * Datetime  : 2026 - 2026/7/28 - 21:36
 * User      : geovindu
 * Product   : IntelliJ IDEA
 * Project   : JavaAlgorithms
 * File      : BreadthFirstBll.java
 * explain   : 学习  类
 **/
 
package Bll;
import BreadthFirst.core.AppLogger;
import BreadthFirst.core.models.JewelryItem;
import BreadthFirst.core.models.PackConstraint;
import BreadthFirst.core.models.PackQueryCondition;
import BreadthFirst.core.models.PackageSpec;
import BreadthFirst.core.strategy.LoadStrategyType;
import BreadthFirst.service.JewelryPackageService;
 
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class BreadthFirstBll {
 
 
    private static final AppLogger log = AppLogger.getInstance();
    private static final JewelryPackageService service = new JewelryPackageService();
    private static final Scanner scanner = new Scanner(System.in);
 
    static double readDouble(String prompt) {
        while (true) {
            System.out.print(prompt);
            String line = scanner.nextLine().trim();
            try {
                double v = Double.parseDouble(line);
                if (v > 0) return v;
            } catch (Exception ignored) {
            }
            System.out.println("输入错误,请输入大于0数字");
        }
    }
 
    static int readInt(String prompt) {
        while (true) {
            System.out.print(prompt);
            String line = scanner.nextLine().trim();
            try {
                int v = Integer.parseInt(line);
                if (v >= 0) return v;
            } catch (Exception ignored) {
            }
            System.out.println("输入错误,请输入非负整数");
        }
    }
 
    static boolean readBool(String prompt) {
        while (true) {
            System.out.print(prompt + " y/n:");
            String line = scanner.nextLine().trim().toLowerCase();
            if ("y".equals(line)) return true;
            if ("n".equals(line)) return false;
            System.out.println("只能输入 y / n");
        }
    }
 
 
    public void Demo()
    {
        List<JewelryItem> jewelryList = new ArrayList<>();
        List<PackageSpec> pkgList = new ArrayList<>();
        PackConstraint constraint = null;
 
        while (true) {
            System.out.println("\n================珠宝首饰智能包装配载系统 Java版================");
            System.out.println("1. 录入首饰品类清单");
            System.out.println("2. 录入包装规格(容积、成本、优先级权重、库存)");
            System.out.println("3. 设置装箱约束");
            System.out.println("4. BFS配载(推荐)");
            System.out.println("5. DFS配载");
            System.out.println("6. A*启发配载");
            System.out.println("0. 退出");
            System.out.print("请输入选项:");
            String opt = scanner.nextLine().trim();
            switch (opt) {
                case "1":
                    jewelryList.clear();
                    while (true) {
                        System.out.print("首饰货号:");
                        String code = scanner.nextLine().trim();
                        double sv = readDouble("单件首饰体积 cm³:");
                        int qty = readInt("品类数量:");
                        JewelryItem item = new JewelryItem();
                        item.setJewelryCode(code);
                        item.setSingleVolume(sv);
                        item.setQuantity(qty);
                        jewelryList.add(item);
                        if (!readBool("继续录入首饰?")) break;
                    }
                    break;
                case "2":
                    pkgList.clear();
                    while (true) {
                        System.out.print("包装编码:");
                        String code = scanner.nextLine().trim();
                        double vol = readDouble("标称容积 cm³:");
                        double cost = readDouble("单个包装成本:");
                        int weight = readInt("优先级权重(1~100):");
                        int stock = readInt("可用库存:");
                        PackageSpec pkg = new PackageSpec();
                        pkg.setSpecCode(code);
                        pkg.setVolume(vol);
                        pkg.setUnitCost(cost);
                        pkg.setPriorityWeight(weight);
                        pkg.setStockQty(stock);
                        pkgList.add(pkg);
                        if (!readBool("继续录入包装?")) break;
                    }
                    break;
                case "3":
                    double bufCoeff = readDouble("缓冲空隙系数(0.7~0.95):");
                    int maxBoxQty = readInt("单个礼盒最大首饰件数:");
                    boolean forbidMix = readBool("禁止不同品类混装?");
                    boolean forbidSplit = readBool("禁止同一品类拆分多个盒子?");
                    constraint = new PackConstraint();
                    constraint.setBufferCoeff(bufCoeff);
                    constraint.setSingleBoxMaxQty(maxBoxQty);
                    constraint.setForbidMixJewelry(forbidMix);
                    constraint.setForbidSplitJewelry(forbidSplit);
                    log.info("装箱约束参数设置完成!");
                    break;
                case "4":
                case "5":
                case "6":
                    if (jewelryList.isEmpty() || pkgList.isEmpty() || constraint == null) {
                        System.out.println("请先录入首饰、包装、装箱约束!");
                        break;
                    }
                    LoadStrategyType st = switch (opt) {
                        case "4" -> LoadStrategyType.BFS;
                        case "5" -> LoadStrategyType.DFS;
                        case "6" -> LoadStrategyType.ASTAR;
                        default -> LoadStrategyType.BFS;
                    };
                    try {
                        PackQueryCondition cond = new PackQueryCondition();
                        cond.setJewelryList(jewelryList);
                        cond.setConstraint(constraint);
                        cond.setPackageSpecList(pkgList);
                        cond.setStrategyType(st);
                        cond.setAllowOverLoad(false);
                        service.calcSinglePlan(cond);
                    }catch (Exception e) {
                        e.printStackTrace();
                    }
 
                    break;
                case "0":
                    log.info("程序退出");
                    scanner.close();
                    return;
                default:
                    System.out.println("无效选项");
                    break;
            }
        }
 
    }
}


输出:

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐