package com.java110.job.importData.adapt; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.java110.core.factory.GenerateCodeFactory; import com.java110.dto.log.AssetImportLogDetailDto; import com.java110.dto.msg.MaintenancePaymentPo; import com.java110.dto.msg.MpFifthPaymentRecordPo; import com.java110.dto.msg.MpPaymentRecordPo; import com.java110.fee.api.*; import com.java110.job.dao.IMaintenancePaymentService; import com.java110.job.dao.IMpFifthPaymentRecordService; import com.java110.job.dao.IMpPaymentRecordService; import com.java110.job.importData.DefaultImportData; import com.java110.job.importData.IImportDataAdapt; import com.java110.po.importFee.MaintenancePayment; import com.java110.po.importFee.MpFifthPaymentRecord; import com.java110.utils.util.BeanConvertUtil; import com.java110.utils.util.DateUtil; import com.java110.utils.util.NumberUtil; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.*; /** * 签报收支款数据导入适配器(适配"Z三、模版签报收支款情况-V5-20251127.xlsx"的"荣顺苑签报单"sheet) * 前端请求需传入:param.append('importAdapt', "importReportMainV2QueueData") */ @Service("importReportMainV2QueueData") public class ImportReportMainV2QueueDataAdapt extends DefaultImportData implements IImportDataAdapt { @Autowired MaintenancePaymentApi maintenancePaymentApi; @Autowired AnnouncementTimeRangeApi announcementTimeRangeApi; @Autowired OwnersCommitteeConventionApi ownersCommitteeConventionApi; @Autowired OwnerWithdrawalInfoApi ownerWithdrawalInfoApi; @Autowired OwnerQualityGuaranteeApi ownerQualityGuaranteeApi; @Autowired MpPaymentRecordApi mpPaymentRecordApi; @Autowired MpFifthPaymentRecordApi mpFifthPaymentRecordApi; @Autowired OwnerRemarkInfoApi ownerRemarkInfoApi; // 注入数据库服务(SqlSessionTemplate版Service) @Override @Transactional(rollbackFor = Exception.class) // 事务控制,确保3张表数据一致性 public void importData(List assetImportLogDetailDtos) { for (AssetImportLogDetailDto logDetailDto : assetImportLogDetailDtos) { try { JSONArray cellArray = JSONObject.parseArray(logDetailDto.getContent()); String flowNumber = getCellValue(cellArray, 1); // 第2列:流转编号 String projectName = getCellValue(cellArray, 4); // 第5列:项目名称 String ownersCommitteeAmountStr = getCellValue(cellArray, 12); // 第13列:业委会金额 if (isBlank(flowNumber) || isBlank(projectName) || isBlank(ownersCommitteeAmountStr)) { throw new IllegalArgumentException( String.format("核心字段缺失:流转编号[%s]、项目名称[%s]、业委会金额[%s]", flowNumber, projectName, ownersCommitteeAmountStr) ); } AnnouncementTimeRangePo announcementTimeRangePo = null; OwnersCommitteeConventionPo ocoPo = null; OwnerWithdrawalInfoPo owiPo = null; OwnerQualityGuaranteePo oqgPo = null; OwnerRemarkInfoPo ownerRemarkInfoPo = null; // 3. 构建3张表的PO对象(字段映射+业务计算) MaintenancePayment mainPo = buildMaintenancePaymentPoV2(cellArray);//主表 if (!getCellValue(cellArray, 22).isEmpty() && !getCellValue(cellArray, 23).isEmpty()) { announcementTimeRangePo = buildAnnouncementTimeRangePo(cellArray, Long.valueOf(mainPo.getId()));//公共收益金 } if (!getCellValue(cellArray, 26).isEmpty() && !getCellValue(cellArray, 27).isEmpty()) { ocoPo = buildOwnersCommitteeConventionPo(cellArray, Long.valueOf(mainPo.getId())); } if (!getCellValue(cellArray, 29).isEmpty()) { owiPo = buildOwnerWithdrawalInfoPo(cellArray, Long.valueOf(mainPo.getId())); } if (!getCellValue(cellArray, 39).isEmpty()) { oqgPo = buildOwnerQualityGuaranteePo(cellArray, Long.valueOf(mainPo.getId())); } List paymentRecordPos = buildMpPaymentRecordPos(cellArray, mainPo.getFlowNumber(), String.valueOf(mainPo.getId())); List fifthPos = buildMpFifthPaymentRecordPos(cellArray, mainPo.getFlowNumber(), String.valueOf(mainPo.getId())); if (!getCellValue(cellArray, 137).isEmpty()) { ownerRemarkInfoPo = buildOwnerRemarkInfoPo(cellArray, mainPo.getFlowNumber(), String.valueOf(mainPo.getId())); } importData(mainPo, announcementTimeRangePo, ocoPo, owiPo, oqgPo, paymentRecordPos, fifthPos, ownerRemarkInfoPo); // 5. 更新导入日志状态(成功)- 调用父类方法 super.updateImportLogDetailState(logDetailDto.getDetailId()); } catch (Exception e) { throw new IllegalArgumentException(e.getMessage()); } } } private void importData(MaintenancePayment mainPo, AnnouncementTimeRangePo announcementTimeRangePo, OwnersCommitteeConventionPo ocoPo, OwnerWithdrawalInfoPo owiPo, OwnerQualityGuaranteePo oqgPo, List paymentRecordPos, List fifthPos, OwnerRemarkInfoPo ownerRemarkInfoPo) { maintenancePaymentApi.saveMaintenancePayment(mainPo); if (announcementTimeRangePo != null) { announcementTimeRangeApi.saveAnnouncementTimeRangeInfo(announcementTimeRangePo); } if (ocoPo != null) { ownersCommitteeConventionApi.saveOwnersCommitteeConvention(ocoPo); } if (owiPo != null) { ownerWithdrawalInfoApi.saveOwnerWithdrawalInfo(owiPo); } if (oqgPo != null) { ownerQualityGuaranteeApi.saveOwnerQualityGuarantee(oqgPo); } for (MpPaymentRecordPo paymentRecordPo : paymentRecordPos) { mpPaymentRecordApi.saveMpPaymentRecord(paymentRecordPo); } for (MpFifthPaymentRecord fifthPo : fifthPos) { mpFifthPaymentRecordApi.saveMpFifthPaymentRecord(fifthPo); } if (ownerRemarkInfoPo != null) { ownerRemarkInfoApi.saveOwnerRemarkInfo(ownerRemarkInfoPo); } } private OwnerRemarkInfoPo buildOwnerRemarkInfoPo(JSONArray cellArray, String flowNumber, String id) { OwnerRemarkInfoPo po = new OwnerRemarkInfoPo(); long longId = UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE; // 避免负数 po.setId(longId); po.setMpId(flowNumber); po.setRemarkPerson("导入内容"); po.setRemarkContent(getCellValue(cellArray, 137)); return po; } private OwnerQualityGuaranteePo buildOwnerQualityGuaranteePo(JSONArray cellArray, Long aLong) { OwnerQualityGuaranteePo po = new OwnerQualityGuaranteePo(); long longId = UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE; // 避免负数 po.setId(longId); // 主键ID po.setMpId(aLong.toString()); try { po.setQualityGuaranteePeriod2Start(parseDateToString(getCellValue(cellArray, 37))); po.setQualityGuaranteePeriod2End(parseDateToString(getCellValue(cellArray, 38))); po.setAcceptanceDate(parseDateToString(getCellValue(cellArray, 41))); po.setAvailableWithdrawalDate(parseDate(getCellValue(cellArray, 43))); } catch (ParseException e) { throw new RuntimeException("日期转换错误"); } po.setQualityGuaranteeRatio(getCellNum(cellArray, 39)); po.setQualityGuaranteeAmount(getCellNum(cellArray, 12) * po.getQualityGuaranteeRatio()); po.setAuditStatus(getCellValue(cellArray, 42)); return po; } private OwnerWithdrawalInfoPo buildOwnerWithdrawalInfoPo(JSONArray cellArray, Long aLong) { OwnerWithdrawalInfoPo po = new OwnerWithdrawalInfoPo(); long longId = UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE; // 避免负数 po.setId(longId); // 主键ID po.setMpId(aLong.toString()); po.setWithdrawalEncounteredProblem(getCellValue(cellArray, 28)); po.setShortageOrArrears(getCellNum(cellArray, 29)); po.setRoadName(getCellValue(cellArray, 30)); po.setLane(getCellValue(cellArray, 31)); po.setDoor(getCellValue(cellArray, 32)); po.setRoom(getCellValue(cellArray, 33)); po.setDoorRoomNumber(getCellValue(cellArray, 34)); po.setOwnerAddress(getCellValue(cellArray, 35)); return po; } private OwnersCommitteeConventionPo buildOwnersCommitteeConventionPo(JSONArray cellArray, Long mpId) { OwnersCommitteeConventionPo po = new OwnersCommitteeConventionPo(); long longId = UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE; // 避免负数 po.setId(longId); // 主键ID po.setMpId(mpId); po.setQuota(getCellNum(cellArray, 25)); po.setOwnersCommitteeResolution(getCellValue(cellArray, 26)); po.setOwnersCommitteeConsultation(getCellValue(cellArray, 27)); return po; } private AnnouncementTimeRangePo buildAnnouncementTimeRangePo(JSONArray cellArray, Long mpId) { AnnouncementTimeRangePo po = new AnnouncementTimeRangePo(); long longId = UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE; // 避免负数 po.setId(longId); // 主键ID po.setMpId(mpId); po.setPlannedAnnouncementStart(getCellValue(cellArray, 21)); po.setPlannedAnnouncementEnd(getCellValue(cellArray, 22)); po.setPublishedAnnouncementStart(getCellValue(cellArray, 23)); po.setPublishedAnnouncementEnd(getCellValue(cellArray, 24)); return po; } private MaintenancePayment buildMaintenancePaymentPoV2(JSONArray cellArray) throws ParseException { MaintenancePayment po = new MaintenancePayment(); po.setId(GenerateCodeFactory.getGeneratorId("10")); // 主键ID po.setAuxiliaryColumn(getCellValue(cellArray, 0)); // 第1列:辅助列 po.setFlowNumber(getCellValue(cellArray, 1)); // 第2列:流转编号 po.setSerialNumber(parseInteger(getCellValue(cellArray, 2))); // 第3列:序号 po.setProjectCode(getCellValue(cellArray, 3)); // 第4列:项目编码 po.setProjectName(getCellValue(cellArray, 4)); // 第5列:项目名称 po.setYear(parseInteger(getCellValue(cellArray, 5))); // 第6列:年份 po.setMonth(parseInteger(getCellValue(cellArray, 6))); // 第7列:月份 po.setDay(parseInteger(getCellValue(cellArray, 7))); // 第8列:日 po.setDate(parseDate(getCellValue(cellArray, 8))); // 第9列:日期 po.setProjectContent(getCellValue(cellArray, 9)); // 第10列:工程内容 po.setManagementOfficeAmount(getCellValue(cellArray, 10)); // 第11列:管理处金额 po.setManagementOfficeSeal(convertSealStatus(getCellValue(cellArray, 11))); // 第12列:管理处是否已盖章(√转"是",否则"否") po.setOwnersCommitteeAmount(getCellValue(cellArray, 12)); // 第13列:业委会金额 po.setAuditAmount(getCellNum(cellArray, 13).toString()); // 第14列:审价金额 po.setOwnersCommitteeSeal(convertSealStatus(getCellValue(cellArray, 14))); // 第15列:业委会是否已盖章 po.setReportDepartment(getCellValue(cellArray, 15)); // 第16列:签报部门 po.setFundTypeLevel1(getCellValue(cellArray, 16)); // 第17列:基金类型-一级分类 po.setFundTypeLevel2(getCellValue(cellArray, 17)); // 第18列:基金类型-二级分类 po.setMaintenanceType(getCellValue(cellArray, 18)); // 第19列:幢/全体 po.setBuildingOrAll(getCellValue(cellArray, 19)); // 第20列:维修类型 po.setPayeeName(getCellValue(cellArray, 145)); // 支付公司名称/个人名字 po.setIdCardNumber(getCellValue(cellArray, 146)); // 个人身份证号码 po.setBankName(getCellValue(cellArray, 147)); // 开户银行 po.setBankAccount(getCellValue(cellArray, 148)); // 开户账号 return po; } /** * 构建 MaintenancePaymentPo(维修资金支取信息表) * 字段映射对应"荣顺苑签报单"表头顺序,包含17个自动计算字段 */ private MaintenancePaymentPo buildMaintenancePaymentPo(JSONArray cellArray) throws ParseException { MaintenancePaymentPo po = new MaintenancePaymentPo(); // 基础字段(直接映射Excel单元格) long longId = UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE; // 避免负数 po.setId(longId+""); // 主键ID po.setAuxiliaryColumn(getCellValue(cellArray, 0)); // 第1列:辅助列 po.setFlowNumber(getCellValue(cellArray, 1)); // 第2列:流转编号 po.setSerialNumber(parseInteger(getCellValue(cellArray, 2))); // 第3列:序号 po.setProjectCode(getCellValue(cellArray, 3)); // 第4列:项目编码 po.setProjectName(getCellValue(cellArray, 4)); // 第5列:项目名称 po.setYear(parseInteger(getCellValue(cellArray, 5))); // 第6列:年份 po.setMonth(parseInteger(getCellValue(cellArray, 6))); // 第7列:月份 po.setDay(parseInteger(getCellValue(cellArray, 7))); // 第8列:日 po.setDate(parseDate(getCellValue(cellArray, 8))); // 第9列:日期 po.setProjectContent(getCellValue(cellArray, 9)); // 第10列:工程内容 po.setManagementOfficeAmount(parseBigDecimal(getCellValue(cellArray, 10))); // 第11列:管理处金额 po.setManagementOfficeSeal(convertSealStatus(getCellValue(cellArray, 11))); // 第12列:管理处是否已盖章(√转"是",否则"否") po.setOwnersCommitteeAmount(parseBigDecimal(getCellValue(cellArray, 12))); // 第13列:业委会金额 po.setAuditAmount(parseBigDecimal(getCellValue(cellArray, 13))); // 第14列:审价金额 po.setOwnersCommitteeSeal(convertSealStatus(getCellValue(cellArray, 14))); // 第15列:业委会是否已盖章 po.setReportDepartment(getCellValue(cellArray, 15)); // 第16列:签报部门 po.setFundTypeLevel1(getCellValue(cellArray, 16)); // 第17列:基金类型-一级分类 po.setFundTypeLevel2(getCellValue(cellArray, 17)); // 第18列:基金类型-二级分类 po.setBuildingOrAll(getCellValue(cellArray, 18)); // 第19列:幢/全体 po.setMaintenanceType(getCellValue(cellArray, 19)); // 第20列:维修类型 po.setQualityGuaranteePeriod1(getCellValue(cellArray, 20)); // 第21列:质保期(第一个) po.setPublicIncomeAnnouncement(getCellValue(cellArray, 21)); // 第22列:公共收益金公布情况 // 公共收益金公布子字段(第23-26列) po.setPlannedAnnouncementStart(getCellValue(cellArray, 21)); // 拟公布-起始(年月) po.setPlannedAnnouncementEnd(getCellValue(cellArray, 22)); // 拟公布-止(年月) po.setPublishedAnnouncementStart(getCellValue(cellArray, 23)); // 已公布-起始(年月) po.setPublishedAnnouncementEnd(getCellValue(cellArray, 24)); // 已公布-止(年月) po.setQuota(parseBigDecimal(getCellValue(cellArray, 25))); // 第27列:额度 po.setOwnersCommitteeResolution(convertYesNo(getCellValue(cellArray, 26))); // 第28列:业委会大会决议(是/否) po.setOwnersCommitteeConsultation(convertYesNo(getCellValue(cellArray, 27))); // 第29列:业委会征询表(是否) po.setWithdrawalEncounteredProblem(getCellValue(cellArray, 28)); // 第30列:支取遇到的问题 po.setShortageOrArrears(parseBigDecimal(getCellValue(cellArray, 29))); // 第31列:缺支/欠款(元) // 业主地址子字段(第32-37列) po.setRoadName(getCellValue(cellArray, 30)); // 路名 po.setLane(getCellValue(cellArray, 31)); // 弄 po.setDoor(getCellValue(cellArray, 32)); // 门 po.setRoom(getCellValue(cellArray, 33)); // 室 po.setDoorRoomNumber(getCellValue(cellArray, 34)); // 门室号 po.setOwnerAddress(getCellValue(cellArray, 35)); // 业主地址 // po.setWithdrawalProblem(getCellValue(cellArray, 36)); // 第38列:支取存在问题 po.setProblemDifficulty(getCellValue(cellArray, 36)); // 第39列:问题难度 po.setAvailableWithdrawalDate(parseDate(getCellValue(cellArray, 37))); // 第40列:可启动支取日期(年月日) po.setQualityGuaranteePeriod2(getCellValue(cellArray, 39) + "~" + getCellValue(cellArray, 40)); // 第41列:质保期(第二个) po.setQualityGuaranteeRatio(parseBigDecimal(getCellValue(cellArray, 40))); // 第42列:质保金占比 po.setQualityGuaranteeAmount(parseBigDecimal(getCellValue(cellArray, 41))); // 第43列:质保金金额(元) po.setReceivedQualityGuarantee(parseBigDecimal(getCellValue(cellArray, 42))); // 第44列:已到账质保金 // po.setStartDate(parseDate(getCellValue(cellArray, 44))); // 第45列:起始(年月日) // po.setEndDate(parseDate(getCellValue(cellArray, 45))); // 第46列:终止(年月日) // -------------------------- 17个自动计算字段(核心业务逻辑)-------------------------- // 1. 打印合计 = 第一次~第五次打印金额之和(第47列打印金额+第54列+第61列+第68列+第75列) BigDecimal print1 = parseBigDecimal(getCellValue(cellArray, 45)); // 第一次打印金额(第50列) BigDecimal print2 = parseBigDecimal(getCellValue(cellArray, 57)); // 第二次打印金额(第57列) BigDecimal print3 = parseBigDecimal(getCellValue(cellArray, 69)); // 第三次打印金额(第64列) BigDecimal print4 = parseBigDecimal(getCellValue(cellArray, 81)); // 第四次打印金额(第71列) BigDecimal print5 = parseBigDecimal(getCellValue(cellArray, 93)); // 第五次打印金额(第78列) // BigDecimal printTotal = add(print1, print2, print3, print4, print5); // po.setPrintTotal(printTotal); // 自定义字段:打印合计 // 2. 到账合计 = 第一次~第五次到账金额之和(第51列+第58列+第65列+第72列+第79列) BigDecimal arrival1 = parseBigDecimal(getCellValue(cellArray, 48)); // 第一次到账金额(第52列) BigDecimal arrival2 = parseBigDecimal(getCellValue(cellArray, 60)); // 第二次到账金额(第59列) BigDecimal arrival3 = parseBigDecimal(getCellValue(cellArray, 72)); // 第三次到账金额(第66列) BigDecimal arrival4 = parseBigDecimal(getCellValue(cellArray, 84)); // 第四次到账金额(第73列) BigDecimal arrival5 = parseBigDecimal(getCellValue(cellArray, 96)); // 第五次到账金额(第80列) // BigDecimal arrivalTotal = add(arrival1, arrival2, arrival3, arrival4, arrival5); // po.setArrivalTotal(arrivalTotal); // 自定义字段:到账合计 // 3. 维修基金到账率 = IF(到账合计<>0, 到账合计/业委会金额, 0) BigDecimal ownersAmount = po.getOwnersCommitteeAmount(); // BigDecimal fundArrivalRate = (arrivalTotal.compareTo(BigDecimal.ZERO) != 0 && ownersAmount.compareTo(BigDecimal.ZERO) != 0) // ? arrivalTotal.divide(ownersAmount, 4, BigDecimal.ROUND_HALF_UP) // 保留4位小数 // : BigDecimal.ZERO; // po.setFundArrivalRate(fundArrivalRate); // 自定义字段:维修基金到账率 // 4. 尚缺金额 = IF(基金类型<>"维修基金", 0, 业委会金额-到账合计) String fundType = po.getFundTypeLevel1(); // BigDecimal shortageAmount = ("维修基金".equals(fundType)) // ? subtract(ownersAmount, arrivalTotal) // : BigDecimal.ZERO; // po.setFundShortageAmount(shortageAmount); // 自定义字段:尚缺金额 // 5. 管理费 = 管理处金额 * 管理费占比(百分比转小数) BigDecimal managementFeeRatio = po.getManagementFeeRatio() == null ? BigDecimal.ZERO : po.getManagementFeeRatio().divide(new BigDecimal(100)); BigDecimal managementFee = multiply(po.getManagementOfficeAmount(), managementFeeRatio); // po.setManagementFee(managementFee); // 自定义字段:管理费 // 6. 应付金额A = IF(审计金额为空, 业委会金额-管理费, 审计金额-管理费) BigDecimal auditAmount = po.getAuditAmount() == null ? BigDecimal.ZERO : po.getAuditAmount(); BigDecimal payableA = (auditAmount.compareTo(BigDecimal.ZERO) == 0) ? subtract(ownersAmount, managementFee) : subtract(auditAmount, managementFee); // po.setPayableAmountA(payableA); // 自定义字段:应付金额A // 7. 应付金额B = 业委会金额-管理费-质保金金额 BigDecimal qualityGuaranteeAmount = po.getQualityGuaranteeAmount() == null ? BigDecimal.ZERO : po.getQualityGuaranteeAmount(); BigDecimal payableB = subtract(ownersAmount, managementFee, qualityGuaranteeAmount); // po.setPayableAmountB(payableB); // 自定义字段:应付金额B // 8. 应付金额C = 实收金额-实收金额×管理费比例(实收金额取到账合计) // BigDecimal payableC = multiply(arrivalTotal, BigDecimal.ONE.subtract(managementFeeRatio)); // po.setPayableAmountC(payableC); // 自定义字段:应付金额C // 9. 拟付合计 = 第一次~第五次拟付金额之和(第82列+第88列+第94列+第100列+第106列) BigDecimal plannedPay1 = parseBigDecimal(getCellValue(cellArray, 113)); // 第一次拟付金额(第83列) BigDecimal plannedPay2 = parseBigDecimal(getCellValue(cellArray, 119)); // 第二次拟付金额(第89列) BigDecimal plannedPay3 = parseBigDecimal(getCellValue(cellArray, 125)); // 第三次拟付金额(第95列) BigDecimal plannedPay4 = parseBigDecimal(getCellValue(cellArray, 131)); // 第四次拟付金额(第101列) BigDecimal plannedPay5 = parseBigDecimal(getCellValue(cellArray, 137)); // 第五次拟付金额(第107列) BigDecimal plannedPayTotal = add(plannedPay1, plannedPay2, plannedPay3, plannedPay4, plannedPay5); // po.setPlannedPayTotal(plannedPayTotal); // 自定义字段:拟付合计 // 10. 实付合计 = 第一次~第五次实付金额之和(第85列+第91列+第97列+第103列+第109列) BigDecimal actualPay1 = parseBigDecimal(getCellValue(cellArray, 117)); // 第一次实付金额(第86列) BigDecimal actualPay2 = parseBigDecimal(getCellValue(cellArray, 123)); // 第二次实付金额(第92列) BigDecimal actualPay3 = parseBigDecimal(getCellValue(cellArray, 130)); // 第三次实付金额(第98列) BigDecimal actualPay4 = parseBigDecimal(getCellValue(cellArray, 135)); // 第四次实付金额(第104列) BigDecimal actualPay5 = parseBigDecimal(getCellValue(cellArray, 141)); // 第五次实付金额(第110列) BigDecimal actualPayTotal = add(actualPay1, actualPay2, actualPay3, actualPay4, actualPay5); // po.setActualPayTotal(actualPayTotal); // 自定义字段:实付合计 // 11. 未付合计 = 审定金额-实付合计(审定金额取审计金额,无则取业委会金额) BigDecimal approvedAmount = (auditAmount.compareTo(BigDecimal.ZERO) == 0) ? ownersAmount : auditAmount; BigDecimal unpaidTotal = subtract(approvedAmount, actualPayTotal); // po.setUnpaidTotal(unpaidTotal); // 自定义字段:未付合计 // 12. 垫付金额 = 复杂条件判断(基金类型、到账合计、公布情况) // BigDecimal advanceAmount = calculateAdvanceAmount(fundType, arrivalTotal, actualPayTotal, po.getPublicIncomeAnnouncement()); // po.setAdvanceAmount(advanceAmount); // 自定义字段:垫付金额 // 13. 未付合计(到账口径) = 按公布情况分支计算 // BigDecimal unpaidByArrival = calculateUnpaidByArrival(po.getPublicIncomeAnnouncement(), ownersAmount, managementFee, actualPayTotal, arrivalTotal); // po.setUnpaidByArrival(unpaidByArrival); // 自定义字段:未付合计(到账口径) // 14-16. 待付金额A/B/C // po.setPendingPayA(subtract(auditAmount, managementFee)); // 待付金额A = 审价金额 - 管理费 // po.setPendingPayB(subtract(auditAmount, qualityGuaranteeAmount, managementFee)); // 待付金额B = 审价金额 - 质保金 - 管理费 // po.setPendingPayC(multiply(arrivalTotal, BigDecimal.ONE.subtract(managementFeeRatio))); // 待付金额C = 到账合计*(1-管理费占比) // 支付信息(第111-116列) po.setPayeeName(getCellValue(cellArray, 143)); // 支付公司名称/个人名字 po.setIdCardNumber(getCellValue(cellArray, 144)); // 个人身份证号码 po.setBankName(getCellValue(cellArray, 145)); // 开户银行 po.setBankAccount(getCellValue(cellArray, 146)); // 开户账号 po.setRemarks(getCellValue(cellArray, 155)); // 备注 po.setAcceptanceDate(parseDate(getCellValue(cellArray, 156))); // 验收日期 po.setAuditDate(getCellValue(cellArray, 157)); // 审计情况:年/月 return po; } /** * 构建 MpPaymentRecordPo 列表(5次打印数据) */ private List buildMpPaymentRecordPos(JSONArray cellArray, String flowNumber,String mainPo) throws ParseException { List pos = new ArrayList<>(); // 第一次打印数据(第47-53列) buildSinglePaymentRecord(mainPo, cellArray, flowNumber, 1, 45, pos); // 第二次打印数据(第54-60列) buildSinglePaymentRecord(mainPo, cellArray, flowNumber, 2, 57, pos); // 第三次打印数据(第61-67列) buildSinglePaymentRecord(mainPo, cellArray, flowNumber, 3, 69, pos); // 第四次打印数据(第68-74列) buildSinglePaymentRecord(mainPo, cellArray, flowNumber, 4, 81, pos); // 第五次打印数据(第75-81列) buildSinglePaymentRecord(mainPo, cellArray, flowNumber, 5, 93, pos); return pos; } /** * 构建单次打印的 MpPaymentRecordPo */ private void buildSinglePaymentRecord(String mpId, JSONArray cellArray, String flowNumber, int printTimes, int index, List pos) throws ParseException { if (getCellValue(cellArray, index).isEmpty()) { return; } MpPaymentRecordPo po = new MpPaymentRecordPo(); long longId = UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE; // 避免负数 po.setId(longId+""); po.setMpId(mpId); // mp_id = 流转编号_打印次数(确保唯一) po.setFirstPrintDate(parseDate(getCellValue(cellArray, index))); po.setResolutionNumber(getCellValue(cellArray, index + 1)); po.setPrintAmount(parseBigDecimal(getCellValue(cellArray, index + 2))); po.setArrivalDate(parseDate(getCellValue(cellArray, index + 3))); po.setArrivalAmount(parseBigDecimal(getCellValue(cellArray, index + 4))); po.setWithdrawer(getCellValue(cellArray, index + 5)); po.setShortageOrArrears(parseBigDecimal(getCellValue(cellArray, index + 6))); // 业主地址信息(复用main表的地址) po.setRoadName(getCellValue(cellArray, index + 7)); po.setLane(getCellValue(cellArray, index + 8)); po.setDoor(getCellValue(cellArray, index + 9)); po.setRoom(getCellValue(cellArray, index + 10)); po.setOwnerAddress(getCellValue(cellArray, index + 11)); po.setPrintAmount(new BigDecimal(flowNumber)); po.setSnakeCase(getCellValue(cellArray, 105)); pos.add(po); } /** * 构建 MpFifthPaymentRecordPo(第五次拟付实付) */ private List buildMpFifthPaymentRecordPos(JSONArray cellArray, String flowNumber,String mainPo) throws ParseException { List pos = new ArrayList<>(); buildMpFifthPaymentRecordPo(pos, cellArray, flowNumber, mainPo, 115); buildMpFifthPaymentRecordPo(pos, cellArray, flowNumber, mainPo, 121); buildMpFifthPaymentRecordPo(pos, cellArray, flowNumber, mainPo, 127); buildMpFifthPaymentRecordPo(pos, cellArray, flowNumber, mainPo, 133); buildMpFifthPaymentRecordPo(pos, cellArray, flowNumber, mainPo, 139); return pos; } private void buildMpFifthPaymentRecordPo(List pos, JSONArray cellArray, String flowNumber, String mainPo, int index) { if (getCellValue(cellArray, index).isEmpty()) { return; } MpFifthPaymentRecord po = new MpFifthPaymentRecord(); long longId = UUID.randomUUID().getMostSignificantBits() & Long.MAX_VALUE; // 避免负数 po.setId(longId+""); po.setMpId(flowNumber); po.setFifthPlannedPaymentAmount(new BigDecimal(getCellValue(cellArray, index)).toString()); try { po.setPlannedPaymentDate(parseDate(getCellValue(cellArray, index + 1))); po.setActualPaymentDate(parseDate(getCellValue(cellArray, index + 5))); } catch (ParseException e) { throw new RuntimeException(e); } po.setCategory(getCellValue(cellArray, index + 2)); po.setReimburser(getCellValue(cellArray, index + 3)); po.setActualPaymentAmount(new BigDecimal(getCellValue(cellArray, index + 4)).toString()); } /** * 计算垫付金额(文档第十三条逻辑) */ private BigDecimal calculateAdvanceAmount(String fundType, BigDecimal arrivalTotal, BigDecimal actualPayTotal, String publicIncomeAnnouncement) { // 条件1:基金类型是"公共收益金"或"维修基金" if ("公共收益金".equals(fundType) || "维修基金".equals(fundType)) { if (arrivalTotal.compareTo(BigDecimal.ZERO) != 0) { // 到账合计≠0:实付>到账则垫付=实付-到账,否则0 return (actualPayTotal.compareTo(arrivalTotal) > 0) ? subtract(actualPayTotal, arrivalTotal) : BigDecimal.ZERO; } else { // 到账合计=0:判断"公共收益金公布情况"是否含"公布"且不含"拟" boolean hasPublish = !isBlank(publicIncomeAnnouncement) && publicIncomeAnnouncement.contains("公布"); boolean hasPlanned = !isBlank(publicIncomeAnnouncement) && publicIncomeAnnouncement.contains("拟"); return (hasPublish && !hasPlanned) ? BigDecimal.ZERO : actualPayTotal; } } // 其他基金类型:垫付金额=0 return BigDecimal.ZERO; } /** * 计算未付合计(到账口径)(文档第十四条逻辑) */ private BigDecimal calculateUnpaidByArrival(String publicIncomeAnnouncement, BigDecimal ownersAmount, BigDecimal managementFee, BigDecimal actualPayTotal, BigDecimal arrivalTotal) { // 含"公布":未付=业委会金额-管理费-实付合计 if (!isBlank(publicIncomeAnnouncement) && publicIncomeAnnouncement.contains("公布")) { return subtract(ownersAmount, managementFee, actualPayTotal); } else { // 不含"公布":到账合计=0则0,否则=到账合计-管理费-实付合计 return (arrivalTotal.compareTo(BigDecimal.ZERO) == 0) ? BigDecimal.ZERO : subtract(arrivalTotal, managementFee, actualPayTotal); } } // -------------------------- 工具方法 -------------------------- /** * 获取单元格值(处理null/空字符串) */ private String getCellValue(JSONArray cellArray, int index) { if (index < 0 || index >= cellArray.size()) { return ""; } Object value = cellArray.get(index); return value == null ? "" : value.toString().trim(); } private Double getCellNum(JSONArray cellArray, int index) { if (index < 0 || index >= cellArray.size()) { return 0.0; } Object value = cellArray.get(index); return value == null ? 0 : Double.parseDouble((String) value); } /** * 判断字符串是否为空 */ private boolean isBlank(String str) { return str == null || str.isEmpty() || "null".equalsIgnoreCase(str) || "#REF!".equals(str); } /** * 解析Integer(空值返回null) */ private Integer parseInteger(String str) { if (isBlank(str)) { return null; } try { return Integer.parseInt(str); } catch (NumberFormatException e) { return null; } } /** * 解析BigDecimal(空值返回0,处理千分位逗号) */ private BigDecimal parseBigDecimal(String str) { if (isBlank(str)) { return BigDecimal.ZERO; } try { // 处理Excel中的千分位格式(如133,900.57) String cleanStr = str.replace(",", "").trim(); return new BigDecimal(cleanStr); } catch (NumberFormatException e) { return BigDecimal.ZERO; } } private String parseDateToString(String str) throws ParseException{ return parseDate(str) == null ? "" : str; } /** * 解析日期(支持多种格式,空值返回null) */ // 扩展支持的日期格式:覆盖横线/斜杠分隔、年月日/月日年等常见格式 private static final List DATE_FORMATS = new ArrayList() {{ add(new SimpleDateFormat("yyyy-MM-dd")); // 2025-12-12 add(new SimpleDateFormat("yyyy/MM/dd")); // 2025/12/12 add(new SimpleDateFormat("yyyyMMdd")); // 20251212 add(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); // 带时分秒的格式(兼容) add(new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")); add(new SimpleDateFormat("MM/dd/yyyy")); // 兼容国外格式(可选) }}; /** * 解析日期:支持Excel日期序列号、2025-12-12、2025/12/12等格式 * @param str 待解析的日期字符串(或Excel序列号) * @return 解析后的Date对象,空字符串返回null * @throws ParseException 不支持的格式抛出异常 */ private Date parseDate(String str) throws ParseException { // 空值处理 if (StringUtils.isBlank(str)) { return null; } String trimStr = str.trim(); // 第一步:尝试解析Excel日期序列号(数字格式) try { double excelDateNum = Double.parseDouble(trimStr); return convertExcelSerialToDate(excelDateNum); } catch (NumberFormatException e) { // 不是数字,继续解析文本日期格式 } // 第二步:尝试解析文本格式日期(覆盖-和/分隔符) for (SimpleDateFormat format : DATE_FORMATS) { try { format.setLenient(false); // 严格校验,避免2025-13-32这类无效日期 return format.parse(trimStr); } catch (ParseException e) { // 该格式解析失败,尝试下一个 continue; } } // 所有格式都不匹配,抛异常 throw new ParseException("不支持的日期格式:" + str, 0); } /** * 转换Excel日期序列号为Date(修正1900闰年bug) * Excel序列号规则:1=1900-01-01(错误认为1900是闰年,多算1天) * @param excelSerial Excel日期序列号(如45735) * @return 对应的Date对象 */ private Date convertExcelSerialToDate(double excelSerial) { // 初始化Calendar:时区设为GMT,避免本地时区偏移 Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); // Excel 1900日期系统基准:序列号1对应1900-01-01 cal.set(1900, Calendar.JANUARY, 1, 0, 0, 0); cal.set(Calendar.MILLISECOND, 0); // 修正Excel的1900闰年bug:Excel认为1900是闰年,实际不是,需减2天 // 注:如果序列号<60(对应1900-02-28),减1天即可;通用场景减2天兼容所有情况 int daysToAdd = (int) excelSerial - 2; cal.add(Calendar.DAY_OF_YEAR, daysToAdd); return cal.getTime(); } /** * 转换盖章状态(√→是,其他→否) */ private String convertSealStatus(String str) { return "√".equals(str) || "是".equals(str) ? "是" : "否"; } /** * 转换是/否(空值返回null) */ private String convertYesNo(String str) { if (isBlank(str)) { return null; } return "是".equals(str) || "√".equals(str) ? "是" : "否"; } /** * 多个BigDecimal相加(处理null,null视为0) */ private BigDecimal add(BigDecimal... decimals) { BigDecimal result = BigDecimal.ZERO; for (BigDecimal decimal : decimals) { result = result.add(decimal == null ? BigDecimal.ZERO : decimal); } return result; } /** * 多个BigDecimal相减(处理null,null视为0) */ private BigDecimal subtract(BigDecimal... decimals) { if (decimals == null || decimals.length == 0) { return BigDecimal.ZERO; } BigDecimal result = decimals[0] == null ? BigDecimal.ZERO : decimals[0]; for (int i = 1; i < decimals.length; i++) { result = result.subtract(decimals[i] == null ? BigDecimal.ZERO : decimals[i]); } // 避免出现负数(业务中金额不能为负时启用) return result.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : result; } /** * 两个BigDecimal相乘(处理null,null视为0) */ private BigDecimal multiply(BigDecimal a, BigDecimal b) { if (a == null || b == null) { return BigDecimal.ZERO; } return a.multiply(b).setScale(2, BigDecimal.ROUND_HALF_UP); // 保留2位小数 } }