| | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import com.trafficaudit.dataimport.dto.ImportResult; |
| | | import org.apache.poi.ss.usermodel.DateUtil; |
| | | import java.util.Comparator; |
| | |
| | | |
| | | /** H204 燃料1计量单位码 -> 中文单位 */ |
| | | private static final Map<String, String> ENERGY_FUEL_UNIT_NAMES = new HashMap<>(); |
| | | |
| | | /** 能耗导入按报表期互斥,防止同报表期并发重导互相覆盖 */ |
| | | private final ConcurrentHashMap<String, Object> energyImportLocks = new ConcurrentHashMap<>(); |
| | | |
| | | static { |
| | | ENERGY_VEHICLE_TYPE_NAMES.put("01", "普通货车"); |
| | |
| | | /** 能耗明细每组固定18列,车牌号起始列(0-based):3,21,39,57,75 */ |
| | | private static final int[] ENERGY_GROUP_COLS = {3, 21, 39, 57, 75}; |
| | | |
| | | /** 能耗导入按报表期互斥锁(H204 明细与运政信息共用,避免同报表期并发重导互相覆盖) */ |
| | | private Object energyLock(String period) { |
| | | return energyImportLocks.computeIfAbsent(period == null ? "" : period, k -> new Object()); |
| | | } |
| | | |
| | | public ImportResult importEnergyMonthly(MultipartFile file, String period) throws Exception { |
| | | List<EnergyRow> rows = new ArrayList<>(); |
| | | List<EnergyAuthVehicle> authList = new ArrayList<>(); |
| | |
| | | if (enterpriseName == null || enterpriseName.trim().isEmpty()) continue; |
| | | for (int group : ENERGY_GROUP_COLS) { |
| | | String plate = getString(row, group); |
| | | if (plate == null || plate.trim().isEmpty()) continue; |
| | | if (plate == null || plate.trim().isEmpty() || "0".equals(plate.trim())) continue; |
| | | EnergyVehicleQuarterly e = new EnergyVehicleQuarterly(); |
| | | e.setReportPeriod(period); |
| | | e.setRegionCode(getString(row, 0)); |
| | |
| | | Row row = authSheet.getRow(r); |
| | | if (row == null) continue; |
| | | String plate = getString(row, 0); |
| | | if (plate == null || plate.trim().isEmpty()) continue; |
| | | if (plate == null || plate.trim().isEmpty() || "0".equals(plate.trim())) continue; |
| | | EnergyAuthVehicle auth = new EnergyAuthVehicle(); |
| | | auth.setReportPeriod(period); |
| | | auth.setPlateNo(plate.trim()); |
| | |
| | | } |
| | | |
| | | // 幂等:同报表期重新导入,先删除旧数据(两表 + H204 规则审核结果) |
| | | energyMapper.delete(new LambdaQueryWrapper<EnergyVehicleQuarterly>() |
| | | .eq(EnergyVehicleQuarterly::getReportPeriod, period)); |
| | | energyAuthMapper.delete(new LambdaQueryWrapper<EnergyAuthVehicle>() |
| | | .eq(EnergyAuthVehicle::getReportPeriod, period)); |
| | | List<Long> h204RuleIds = new ArrayList<>(); |
| | | for (com.trafficaudit.rulemanage.entity.AuditRule rule : ruleMapper.selectList(null)) { |
| | | if ("H204".equals(rule.getReportType())) h204RuleIds.add(rule.getId()); |
| | | } |
| | | if (!h204RuleIds.isEmpty()) { |
| | | auditResultMapper.delete(new LambdaQueryWrapper<AuditResult>() |
| | | .eq(AuditResult::getReportPeriod, period) |
| | | .in(AuditResult::getRuleId, h204RuleIds)); |
| | | } |
| | | |
| | | int success = 0; |
| | | List<String> failDetails = new ArrayList<>(); |
| | | for (EnergyRow item : rows) { |
| | | try { |
| | | energyMapper.insert(item.entity); |
| | | success++; |
| | | } catch (Exception e) { |
| | | Throwable cause = e; |
| | | while (cause.getCause() != null) cause = cause.getCause(); |
| | | String reason = cause.getMessage(); |
| | | if (reason == null || reason.trim().isEmpty()) reason = e.getMessage(); |
| | | failDetails.add("第 " + item.excelRow + " 行(" + item.entity.getEnterpriseName() |
| | | + " " + item.entity.getPlateNo() + "):" + reason); |
| | | log.error("Energy insert error: row {}, {}", item.excelRow, item.entity.getPlateNo(), e); |
| | | // 加锁:同报表期并发重导互斥,防止 delete/insert 交错导致数据残留或翻倍 |
| | | synchronized (energyLock(period)) { |
| | | energyMapper.delete(new LambdaQueryWrapper<EnergyVehicleQuarterly>() |
| | | .eq(EnergyVehicleQuarterly::getReportPeriod, period)); |
| | | energyAuthMapper.delete(new LambdaQueryWrapper<EnergyAuthVehicle>() |
| | | .eq(EnergyAuthVehicle::getReportPeriod, period)); |
| | | List<Long> h204RuleIds = new ArrayList<>(); |
| | | for (com.trafficaudit.rulemanage.entity.AuditRule rule : ruleMapper.selectList(null)) { |
| | | if ("H204".equals(rule.getReportType())) h204RuleIds.add(rule.getId()); |
| | | } |
| | | } |
| | | String errorDetail = String.join("\n", failDetails); |
| | | if (errorDetail.length() > 4000) { |
| | | errorDetail = errorDetail.substring(0, 4000) + "\n……"; |
| | | } |
| | | recordBatch(file.getOriginalFilename(), "H204", period, rows.size(), success, failDetails.size(), errorDetail); |
| | | |
| | | int authSuccess = 0; |
| | | for (EnergyAuthVehicle auth : authList) { |
| | | try { |
| | | energyAuthMapper.insert(auth); |
| | | authSuccess++; |
| | | } catch (Exception e) { |
| | | log.error("energy auth insert error: {}", auth.getPlateNo(), e); |
| | | if (!h204RuleIds.isEmpty()) { |
| | | auditResultMapper.delete(new LambdaQueryWrapper<AuditResult>() |
| | | .eq(AuditResult::getReportPeriod, period) |
| | | .in(AuditResult::getRuleId, h204RuleIds)); |
| | | } |
| | | |
| | | int success = 0; |
| | | List<String> failDetails = new ArrayList<>(); |
| | | for (EnergyRow item : rows) { |
| | | try { |
| | | energyMapper.insert(item.entity); |
| | | success++; |
| | | } catch (Exception e) { |
| | | Throwable cause = e; |
| | | while (cause.getCause() != null) cause = cause.getCause(); |
| | | String reason = cause.getMessage(); |
| | | if (reason == null || reason.trim().isEmpty()) reason = e.getMessage(); |
| | | failDetails.add("第 " + item.excelRow + " 行(" + item.entity.getEnterpriseName() |
| | | + " " + item.entity.getPlateNo() + "):" + reason); |
| | | log.error("Energy insert error: row {}, {}", item.excelRow, item.entity.getPlateNo(), e); |
| | | } |
| | | } |
| | | String errorDetail = String.join("\n", failDetails); |
| | | if (errorDetail.length() > 4000) { |
| | | errorDetail = errorDetail.substring(0, 4000) + "\n……"; |
| | | } |
| | | recordBatch(file.getOriginalFilename(), "H204", period, rows.size(), success, failDetails.size(), errorDetail); |
| | | |
| | | int authSuccess = 0; |
| | | for (EnergyAuthVehicle auth : authList) { |
| | | try { |
| | | energyAuthMapper.insert(auth); |
| | | authSuccess++; |
| | | } catch (Exception e) { |
| | | log.error("energy auth insert error: {}", auth.getPlateNo(), e); |
| | | } |
| | | } |
| | | recordBatch(file.getOriginalFilename(), "ENERGY_AUTH", period, authList.size(), authSuccess, 0, null); |
| | | log.info("Energy monthly imported: {} vehicles, auth {} rows for {}", success, authSuccess, period); |
| | | return new ImportResult(success, failDetails.size(), failDetails); |
| | | } |
| | | recordBatch(file.getOriginalFilename(), "ENERGY_AUTH", period, authList.size(), authSuccess, 0, null); |
| | | log.info("Energy monthly imported: {} vehicles, auth {} rows for {}", success, authSuccess, period); |
| | | return new ImportResult(success, failDetails.size(), failDetails); |
| | | } |
| | | |
| | | /** 能耗车辆运政信息(独立模板:车牌号/车辆类型/燃料类型/标记吨位/准牵引质量) */ |
| | |
| | | Row row = sheet.getRow(r); |
| | | if (row == null) continue; |
| | | String plate = getString(row, 0); |
| | | if (plate == null || plate.trim().isEmpty()) continue; |
| | | if (plate == null || plate.trim().isEmpty() || "0".equals(plate.trim())) continue; |
| | | EnergyAuthVehicle auth = new EnergyAuthVehicle(); |
| | | auth.setReportPeriod(period); |
| | | auth.setPlateNo(plate.trim()); |
| | |
| | | log.error("Energy auth parse error", e); |
| | | throw e; |
| | | } |
| | | // 幂等:同报表期重新导入,先删除旧数据 |
| | | energyAuthMapper.delete(new LambdaQueryWrapper<EnergyAuthVehicle>() |
| | | .eq(EnergyAuthVehicle::getReportPeriod, period)); |
| | | deleteRuleResults("H204", period); |
| | | int success = 0; |
| | | List<String> failDetails = new ArrayList<>(); |
| | | for (EnergyAuthVehicle auth : authList) { |
| | | try { |
| | | energyAuthMapper.insert(auth); |
| | | success++; |
| | | } catch (Exception e) { |
| | | Throwable cause = e; |
| | | while (cause.getCause() != null) cause = cause.getCause(); |
| | | String reason = cause.getMessage(); |
| | | if (reason == null || reason.trim().isEmpty()) reason = e.getMessage(); |
| | | failDetails.add("第 " + (authList.indexOf(auth) + 2) + " 行(" + auth.getPlateNo() + "):" + reason); |
| | | log.error("Energy auth insert error: {}", auth.getPlateNo(), e); |
| | | // 幂等:同报表期重新导入,先删除旧数据;与 H204 明细共用报表期锁,防并发互相覆盖 |
| | | synchronized (energyLock(period)) { |
| | | energyAuthMapper.delete(new LambdaQueryWrapper<EnergyAuthVehicle>() |
| | | .eq(EnergyAuthVehicle::getReportPeriod, period)); |
| | | deleteRuleResults("H204", period); |
| | | int success = 0; |
| | | List<String> failDetails = new ArrayList<>(); |
| | | for (EnergyAuthVehicle auth : authList) { |
| | | try { |
| | | energyAuthMapper.insert(auth); |
| | | success++; |
| | | } catch (Exception e) { |
| | | Throwable cause = e; |
| | | while (cause.getCause() != null) cause = cause.getCause(); |
| | | String reason = cause.getMessage(); |
| | | if (reason == null || reason.trim().isEmpty()) reason = e.getMessage(); |
| | | failDetails.add("第 " + (authList.indexOf(auth) + 2) + " 行(" + auth.getPlateNo() + "):" + reason); |
| | | log.error("Energy auth insert error: {}", auth.getPlateNo(), e); |
| | | } |
| | | } |
| | | String errorDetail = String.join("\n", failDetails); |
| | | if (errorDetail.length() > 4000) errorDetail = errorDetail.substring(0, 4000) + "\n……"; |
| | | recordBatch(file.getOriginalFilename(), "ENERGY_AUTH", period, authList.size(), success, failDetails.size(), errorDetail); |
| | | log.info("Energy auth imported: {} rows for {}", success, period); |
| | | return new ImportResult(success, failDetails.size(), failDetails); |
| | | } |
| | | String errorDetail = String.join("\n", failDetails); |
| | | if (errorDetail.length() > 4000) errorDetail = errorDetail.substring(0, 4000) + "\n……"; |
| | | recordBatch(file.getOriginalFilename(), "ENERGY_AUTH", period, authList.size(), success, failDetails.size(), errorDetail); |
| | | log.info("Energy auth imported: {} rows for {}", success, period); |
| | | return new ImportResult(success, failDetails.size(), failDetails); |
| | | } |
| | | |
| | | /** H204 行记录:保留 Excel 行号用于失败定位 */ |