如何优化Java供应商库存Feed处理程序以适配更多供应商?
多供应商库存Feed适配优化方案(Java)
针对你当前的需求,核心优化方向是配置化管理映射规则+策略模式解耦解析逻辑,实现新增供应商无需修改核心代码的目标,具体方案如下:
1. 配置化存储供应商映射规则
把每个供应商的Feed解析规则从硬编码抽离到配置(推荐用YAML/Properties文件,或持久化到数据库),包含以下关键信息:
- 供应商标识(ID/名称)
- 文件类型(CSV/TXT/EXCEL)
- 可选格式参数(CSV分隔符、Excel工作表名等)
- Product ID/Quantity的列映射(支持列名或列索引两种方式)
示例YAML配置:
suppliers: - id: supplier_x file_type: CSV delimiter: "|" mappings: product_id: {column_name: "SKU_CODE"} quantity: {column_index: 2} - id: supplier_y file_type: EXCEL sheet_name: "每日库存" mappings: product_id: {column_index: 0} quantity: {column_name: "当前库存"}
2. 策略模式实现多格式解析
定义统一解析接口,针对不同文件类型实现具体解析类,通过工厂类动态获取解析实例:
核心接口与DTO
// 封装解析后的库存记录 public class StockRecord { private String productId; private Integer quantity; // 构造器、getter/setter } // 统一解析接口 public interface FeedParser { List<StockRecord> parse(File feedFile, SupplierMapping mapping) throws IOException; } // 供应商映射配置的实体类 public class SupplierMapping { private String supplierId; private String fileType; private String delimiter; private String sheetName; private ColumnMapping productIdMapping; private ColumnMapping quantityMapping; // 构造器、getter/setter } public class ColumnMapping { private String columnName; private Integer columnIndex; // 构造器、getter/setter }
具体解析实现(以CSV为例)
public class CsvFeedParser implements FeedParser { @Override public List<StockRecord> parse(File feedFile, SupplierMapping mapping) throws IOException { List<StockRecord> records = new ArrayList<>(); try (CSVReader reader = new CSVReader(new FileReader(feedFile), mapping.getDelimiter().charAt(0))) { String[] headers = reader.readNext(); int productColIdx = resolveColumnIndex(headers, mapping.getProductIdMapping()); int quantityColIdx = resolveColumnIndex(headers, mapping.getQuantityMapping()); String[] line; while ((line = reader.readNext()) != null) { String productId = line[productColIdx].trim(); Integer quantity = parseQuantity(line[quantityColIdx].trim()); records.add(new StockRecord(productId, quantity)); } } return records; } // 通用列索引解析:优先用配置的索引,无则按列名匹配(忽略大小写) private int resolveColumnIndex(String[] headers, ColumnMapping mapping) { if (mapping.getColumnIndex() != null) { return mapping.getColumnIndex(); } for (int i = 0; i < headers.length; i++) { if (headers[i].equalsIgnoreCase(mapping.getColumnName())) { return i; } } throw new IllegalArgumentException("未找到指定列:" + mapping.getColumnName()); } // 安全解析库存数量(处理空值、非数字情况) private Integer parseQuantity(String value) { try { return value.isEmpty() ? 0 : Integer.parseInt(value); } catch (NumberFormatException e) { throw new IllegalArgumentException("库存数量格式错误:" + value); } } }
解析工厂类
public class FeedParserFactory { private static final Map<String, FeedParser> PARSER_MAP = new HashMap<>(); static { PARSER_MAP.put("CSV", new CsvFeedParser()); PARSER_MAP.put("EXCEL", new ExcelFeedParser()); PARSER_MAP.put("TXT", new CsvFeedParser()); // TXT可复用CSV逻辑,只需指定分隔符 } public static FeedParser getParser(String fileType) { FeedParser parser = PARSER_MAP.get(fileType.toUpperCase()); if (parser == null) { throw new UnsupportedOperationException("不支持的文件类型:" + fileType); } return parser; } }
3. 核心业务流程解耦
将解析与入库逻辑分离,新增入库服务只处理数据持久化:
public class StockStorageService { public void batchSave(List<StockRecord> records) { // 实现批量入库逻辑(JDBC/MyBatis/JPA等) } }
4. 异常处理与可观测性
- 针对每个供应商的解析过程记录详细日志(解析条数、失败原因、耗时)
- 对解析失败的记录暂存到错误表,方便后续回溯
- 配置告警规则(如连续3次解析失败触发通知)
方案优势
- 开闭原则:新增供应商仅需添加配置,无需修改核心代码
- 可扩展性:新增文件类型只需实现对应的
FeedParser - 可维护性:映射规则集中管理,排查问题更高效
内容的提问来源于stack exchange,提问作者devlperMoose
相关产品推荐
相关产品推荐

