| | |
| | | import java.util.Arrays; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.TreeMap; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import java.text.SimpleDateFormat; |
| | |
| | | // ========== 货运量周转量(模板_货运量周转量.xlsx,每月导入) ========== |
| | | |
| | | public int importFreightTurnover(MultipartFile file, String period) throws Exception { |
| | | try (Workbook wb = WorkbookFactory.create(file.getInputStream())) { |
| | | // 多年度工作簿(一年一个 Sheet:整年 1-12 月,如 21-26 年定稿文件)→ 全部年份入库 |
| | | List<String> yearSheets = detectYearSheets(wb); |
| | | if (!yearSheets.isEmpty()) { |
| | | return importFreightTurnoverByYear(wb, file, yearSheets); |
| | | } |
| | | return importFreightTurnoverByMonth(wb, file, period); |
| | | } |
| | | } |
| | | |
| | | /** 每月导入(模板_货运量周转量.xlsx):左半今年 1-N 月,右半去年 1-12 月 */ |
| | | private int importFreightTurnoverByMonth(Workbook wb, MultipartFile file, String period) throws Exception { |
| | | List<FreightTurnoverImport> list = new ArrayList<>(); |
| | | int failRows = 0; |
| | | // 报表期 N 月:左半为今年 1-N 月(列数不固定),右半固定为去年 1-12 月(算同比用) |
| | | int monthCount = Integer.parseInt(period.split("-")[1]); |
| | | try (Workbook wb = WorkbookFactory.create(file.getInputStream())) { |
| | | { |
| | | Sheet sheet = wb.getSheetAt(0); |
| | | Row header = sheet.getRow(0); |
| | | String h0 = header == null ? null : getString(header, 0); |
| | |
| | | } |
| | | list.add(entity); |
| | | } |
| | | } catch (Exception e) { |
| | | throw e; |
| | | } |
| | | Map<String, List<FreightTurnoverImport>> byPeriod = new LinkedHashMap<>(); |
| | | byPeriod.put(period, list); |
| | | return persistFreightTurnover(byPeriod, file.getOriginalFilename()); |
| | | } |
| | | |
| | | // 幂等:同报表期先删除 |
| | | freightTurnoverMapper.delete(new LambdaQueryWrapper<FreightTurnoverImport>() |
| | | .eq(FreightTurnoverImport::getReportPeriod, period)); |
| | | /** 识别「多年度工作簿」:每个 Sheet 一年(Sheet 名 2021年/2022年…),且表头为「货运量/1月…12月」;否则返回空列表 */ |
| | | private List<String> detectYearSheets(Workbook wb) { |
| | | List<String> names = new ArrayList<>(); |
| | | if (wb.getNumberOfSheets() < 2) return names; |
| | | for (int i = 0; i < wb.getNumberOfSheets(); i++) { |
| | | Sheet sheet = wb.getSheetAt(i); |
| | | String nm = sheet.getSheetName() == null ? "" : sheet.getSheetName().trim(); |
| | | if (!nm.matches("\\d{4}\\s*年?")) { names.clear(); return names; } |
| | | Row header = sheet.getRow(0); |
| | | String h0 = header == null ? null : getString(header, 0); |
| | | String h1 = header == null ? null : getString(header, 1); |
| | | if (h0 == null || !h0.contains("货运量") || h1 == null || !h1.contains("月")) { |
| | | names.clear(); |
| | | return names; |
| | | } |
| | | names.add(nm); |
| | | } |
| | | return names; |
| | | } |
| | | |
| | | /** 多年度工作簿导入:逐年一条报表期(报表期=该年最后有数据的月份),上年同期取上一年度 Sheet */ |
| | | private int importFreightTurnoverByYear(Workbook wb, MultipartFile file, List<String> yearSheets) { |
| | | Map<Integer, String> sheetByYear = new TreeMap<>(); |
| | | for (String sheetName : yearSheets) { |
| | | sheetByYear.put(Integer.parseInt(sheetName.replace("年", "").trim()), sheetName); |
| | | } |
| | | Map<Integer, Map<String, FreightTurnoverImport>> byYear = new TreeMap<>(); |
| | | Map<Integer, Integer> maxMonthByYear = new HashMap<>(); |
| | | for (Map.Entry<Integer, String> entry : sheetByYear.entrySet()) { |
| | | Map<String, FreightTurnoverImport> parsed = new LinkedHashMap<>(); |
| | | int maxMonth = parseWholeYearSheet(wb.getSheet(entry.getValue()), parsed); |
| | | if (parsed.isEmpty()) continue; |
| | | byYear.put(entry.getKey(), parsed); |
| | | maxMonthByYear.put(entry.getKey(), Math.max(1, Math.min(12, maxMonth))); |
| | | } |
| | | Map<String, List<FreightTurnoverImport>> byPeriod = new LinkedHashMap<>(); |
| | | for (Map.Entry<Integer, Map<String, FreightTurnoverImport>> entry : byYear.entrySet()) { |
| | | int year = entry.getKey(); |
| | | String yearPeriod = year + "-" + String.format("%02d", maxMonthByYear.get(year)); |
| | | Map<String, FreightTurnoverImport> lastYear = byYear.get(year - 1); |
| | | List<FreightTurnoverImport> rows = new ArrayList<>(); |
| | | for (Map.Entry<String, FreightTurnoverImport> regionEntry : entry.getValue().entrySet()) { |
| | | FreightTurnoverImport entity = regionEntry.getValue(); |
| | | entity.setReportPeriod(yearPeriod); |
| | | // 上年同期 = 上一年度 Sheet(多年度文件没有右半「去年1-12月」区块) |
| | | FreightTurnoverImport last = lastYear == null ? null : lastYear.get(regionEntry.getKey()); |
| | | if (last != null) { |
| | | for (int m = 1; m <= 12; m++) { |
| | | setLastFreight(entity, m, getFreight(last, m)); |
| | | setLastTurnover(entity, m, getTurnover(last, m)); |
| | | } |
| | | } |
| | | rows.add(entity); |
| | | } |
| | | byPeriod.put(yearPeriod, rows); |
| | | } |
| | | return persistFreightTurnover(byPeriod, file.getOriginalFilename()); |
| | | } |
| | | |
| | | /** 解析整年 Sheet(1-12 月列):货运量块 + 周转量块;返回最后一个有货运量数据的月份 */ |
| | | private int parseWholeYearSheet(Sheet sheet, Map<String, FreightTurnoverImport> out) { |
| | | Map<String, FreightTurnoverImport> freightMap = new LinkedHashMap<>(); |
| | | Map<String, FreightTurnoverImport> turnoverMap = new LinkedHashMap<>(); |
| | | Set<String> seen = new HashSet<>(); |
| | | int maxMonth = 0; |
| | | for (int r = 1; r <= sheet.getLastRowNum(); r++) { |
| | | Row row = sheet.getRow(r); |
| | | if (row == null) continue; |
| | | String name = getString(row, 0); |
| | | if (name == null || name.trim().isEmpty()) continue; |
| | | String region = RegionUtil.normalizeCityName(name); |
| | | boolean isCity = RegionUtil.cityList().contains(region); |
| | | if (!isCity && !"湖北省".equals(region)) continue; // 表头/说明行 |
| | | if (seen.contains(region)) { |
| | | // 该名称已出现过 -> 周转量区块 |
| | | FreightTurnoverImport entity = turnoverMap.get(region); |
| | | if (entity == null) { |
| | | entity = new FreightTurnoverImport(); |
| | | entity.setRegionName(name.trim()); |
| | | turnoverMap.put(region, entity); |
| | | } |
| | | for (int m = 1; m <= 12; m++) { |
| | | setTurnover(entity, m, getDecimalOrNull(row, m)); |
| | | } |
| | | } else { |
| | | seen.add(region); |
| | | FreightTurnoverImport entity = freightMap.get(region); |
| | | if (entity == null) { |
| | | entity = new FreightTurnoverImport(); |
| | | entity.setRegionName(name.trim()); |
| | | freightMap.put(region, entity); |
| | | } |
| | | for (int m = 1; m <= 12; m++) { |
| | | Double v = getDecimalOrNull(row, m); |
| | | setFreight(entity, m, v); |
| | | if (v != null && m > maxMonth) maxMonth = m; |
| | | } |
| | | } |
| | | } |
| | | // 合并:一个市州一行(货运量 + 周转量) |
| | | for (Map.Entry<String, FreightTurnoverImport> entry : freightMap.entrySet()) { |
| | | FreightTurnoverImport entity = entry.getValue(); |
| | | FreightTurnoverImport turnover = turnoverMap.get(entry.getKey()); |
| | | if (turnover != null) { |
| | | for (int m = 1; m <= 12; m++) { |
| | | setTurnover(entity, m, getTurnover(turnover, m)); |
| | | } |
| | | } |
| | | out.put(entry.getKey(), entity); |
| | | } |
| | | return maxMonth; |
| | | } |
| | | |
| | | /** 落库:按报表期先删后插(幂等),每个报表期一条导入批次记录,返回成功条数 */ |
| | | private int persistFreightTurnover(Map<String, List<FreightTurnoverImport>> byPeriod, String fileName) { |
| | | int total = 0; |
| | | for (Map.Entry<String, List<FreightTurnoverImport>> entry : byPeriod.entrySet()) { |
| | | String p = entry.getKey(); |
| | | List<FreightTurnoverImport> rows = entry.getValue(); |
| | | freightTurnoverMapper.delete(new LambdaQueryWrapper<FreightTurnoverImport>() |
| | | .eq(FreightTurnoverImport::getReportPeriod, p)); |
| | | int failRows = 0; |
| | | int success = 0; |
| | | for (FreightTurnoverImport entity : list) { |
| | | for (FreightTurnoverImport entity : rows) { |
| | | try { |
| | | freightTurnoverMapper.insert(entity); |
| | | success++; |
| | |
| | | failRows++; |
| | | } |
| | | } |
| | | recordBatch(file.getOriginalFilename(), "FREIGHT_TURNOVER", period, list.size(), success, failRows, null); |
| | | log.info("Freight turnover imported: {} rows for {}", success, period); |
| | | return success; |
| | | recordBatch(fileName, "FREIGHT_TURNOVER", p, rows.size(), success, failRows, null); |
| | | log.info("Freight turnover imported: {} rows for {}", success, p); |
| | | total += success; |
| | | } |
| | | return total; |
| | | } |
| | | |
| | | /** 解析货运量区块行:左半 B 起 N 列为今年 1-N 月,右半为去年 1-12 月 */ |