package com.ruoyi.home.controller; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ruoyi.approve.pojo.ApproveProcess; import com.ruoyi.device.mapper.DeviceLedgerMapper; import com.ruoyi.device.mapper.DeviceRepairMapper; import com.ruoyi.device.pojo.DeviceRepair; import com.ruoyi.dto.MapDto; import com.ruoyi.framework.aspectj.lang.annotation.Log; import com.ruoyi.framework.aspectj.lang.enums.BusinessType; import com.ruoyi.framework.web.controller.BaseController; import com.ruoyi.framework.web.domain.R; import com.ruoyi.home.annotation.DefaultType; import com.ruoyi.home.dto.*; import com.ruoyi.home.service.HomeService; import com.ruoyi.production.mapper.ProductionOrderMapper; import com.ruoyi.production.mapper.ProductionProductOutputMapper; import com.ruoyi.production.pojo.ProductionOrder; import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.Operation; import lombok.AllArgsConstructor; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.math.BigDecimal; import java.math.RoundingMode; import java.text.ParseException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.Date; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; /** * @author :yys * @date : 2025/7/25 9:15 */ @RestController @Tag(name = "首页统计") @RequestMapping("/home") @AllArgsConstructor public class HomeController extends BaseController { private final HomeService homeService; private final ProductionOrderMapper productionOrderMapper; private final ProductionProductOutputMapper productionProductOutputMapper; private final DeviceLedgerMapper deviceLedgerMapper; private final DeviceRepairMapper deviceRepairMapper; private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); private static final Integer ORDER_STATUS_WAIT = 1; private static final Integer ORDER_STATUS_RUNNING = 2; private static final Integer ORDER_STATUS_COMPLETED = 3; private static final Integer ORDER_STATUS_PAUSED = 4; /********************************************************基础类*****************************************************/ @GetMapping("/todos") @Log(title = "待办事项", businessType = BusinessType.OTHER) @Operation(summary = "待办事项") public R todos() throws ParseException { List approveProcessList = homeService.todos(); return R.ok(approveProcessList); } @GetMapping("/approveAndDeviceTodos") @Operation(summary = "审批协同,设备报修待办事项") public R approveAndDeviceTodos(){ Map map = homeService.approveAndDeviceTodos(); return R.ok(map); } @GetMapping("/noticesCount") @Operation(summary = "未过期的公告数量") public R noticesCount(){ Long count = homeService.noticesCount(); return R.ok(count); } @GetMapping("/deptStaffDistribution") @Operation(summary = "各部门人员分布") public R deptStaffDistribution() { DeptStaffDistributionDto dto = homeService.deptStaffDistribution(); return R.ok(dto); } @GetMapping("/summaryStatistics") @Operation(summary = "员工-客户-供应商总数") public R summaryStatistics() { HomeSummaryDto homeSummaryDto = homeService.summaryStatistics(); return R.ok(homeSummaryDto); } /********************************************************营销采购类**************************************************/ @GetMapping("/supplierPurchaseRanking") @Operation(summary = "供应商采购排名") public R supplierPurchaseRanking(@DefaultType Integer type) { List list = homeService.supplierPurchaseRanking(type); return R.ok(list); } @GetMapping("/customerRevenueAnalysis") @Operation(summary = "客户营收贡献数值分析") public R customerRevenueAnalysis(Long customerId, @DefaultType Integer type) { CustomerRevenueAnalysisDto dto = homeService.customerRevenueAnalysis(customerId, type); return R.ok(dto); } @GetMapping("/customerContributionRanking") @Operation(summary = "客户金额贡献排名") public R customerContributionRanking(@DefaultType Integer type) { List list = homeService.customerContributionRanking(type); return R.ok(list); } @GetMapping("/productSalesAnalysis") @Operation(summary = "各产品销售金额分析") public R productSalesAnalysis() { List list = homeService.productSalesAnalysis(); return R.ok(list); } @GetMapping("/rawMaterialPurchaseAmountRatio") @Operation(summary = "原材料采购金额占比") public R rawMaterialPurchaseAmountRatio(){ List list = homeService.rawMaterialPurchaseAmountRatio(); return R.ok(list); } @GetMapping("/business") @Log(title = "销售-采购-库存数据", businessType = BusinessType.OTHER) @Operation(summary = "销售-采购-库存数据") public R business() { HomeBusinessDto homeBusinessDto = homeService.business(); return R.ok(homeBusinessDto); } @GetMapping("/analysisCustomerContractAmounts") @Log(title = "客户合同金额分析", businessType = BusinessType.OTHER) @Operation(summary = "客户合同金额分析") public R analysisCustomerContractAmounts() { AnalysisCustomerContractAmountsDto analysisCustomerContractAmounts = homeService.analysisCustomerContractAmounts(); return R.ok(analysisCustomerContractAmounts); } /********************************************************生产类*****************************************************/ @GetMapping("/inputOutputAnalysis") @Operation(summary = "投入产出分析") public R inputOutputAnalysis(@DefaultType Integer type){ List list = homeService.inputOutputAnalysis(type); return R.ok(list); } @GetMapping("/processOutputAnalysis") @Operation(summary = "工序产出分析") public R processOutputAnalysis(@DefaultType Integer type){ List list = homeService.processOutputAnalysis(type); return R.ok(list); } @GetMapping("/workOrderEfficiencyAnalysis") @Operation(summary = "工单执行效率分析") public R workOrderEfficiencyAnalysis(@DefaultType Integer type){ List list = homeService.workOrderEfficiencyAnalysis(type); return R.ok(list); } @GetMapping("/productionAccountingAnalysis") @Operation(summary = "生产核算分析") public R productionAccountingAnalysis(@DefaultType Integer type){ List list = homeService.productionAccountingAnalysis(type); return R.ok(list); } @GetMapping("/orderCount") @Operation(summary = "订单数") public R orderCount(){ return R.ok(homeService.orderCount()); } @GetMapping("/progressStatistics") @Operation(summary = "各生产订单的完成进度统计") public R progressStatistics(){ ProductionProgressDto productionProgressDto = homeService.productionProgress(); return R.ok(productionProgressDto); } @GetMapping("/workInProcessTurnover") @Operation(summary = "在制品周转情况") public R workInProcessTurnover(){ ProductionTurnoverDto productionTurnoverDto = homeService.workInProcessTurnover(); return R.ok(productionTurnoverDto); } @GetMapping("/processDataProductionStatistics") @Operation(summary = "工序数据生产统计数据") public R processDataProductionStatistics(@DefaultType Integer type,@RequestParam(required = false) List processIds) { List list = homeService.processDataProductionStatistics(type, processIds); return R.ok(list); } /********************************************************质量类*****************************************************/ @GetMapping("/productionOverview") @Operation(summary = "Production Overview") public R productionOverview() { LocalDate today = LocalDate.now(); Map totalStats = loadOutputStats(LocalDate.of(2000, 1, 1), today.plusDays(1)); BigDecimal totalOutput = totalStats.get("quantity"); BigDecimal totalScrap = totalStats.get("scrapQty"); BigDecimal yieldRate = calcRate(totalOutput, totalOutput.add(totalScrap)); Map result = new LinkedHashMap<>(); result.put("totalOutput", scale(totalOutput)); result.put("totalScrap", scale(totalScrap)); result.put("yieldRate", scale(yieldRate)); return R.ok(result); } @GetMapping("/productionRealtimeBoard") @Operation(summary = "Production Realtime Board") public R productionRealtimeBoard() { LocalDate today = LocalDate.now(); LocalDate yesterday = today.minusDays(1); BigDecimal todayDeviceOee = calcDeviceOee(today); BigDecimal yesterdayDeviceOee = calcDeviceOee(yesterday); BigDecimal todayOrderAchievementRate = calcOrderAchievementRate(today); BigDecimal yesterdayOrderAchievementRate = calcOrderAchievementRate(yesterday); BigDecimal todayDefectRate = calcDefectRate(today); BigDecimal yesterdayDefectRate = calcDefectRate(yesterday); Map result = new LinkedHashMap<>(); result.put("deviceOee", buildRealtimeMetric(todayDeviceOee, todayDeviceOee.subtract(yesterdayDeviceOee))); result.put("orderAchievementRate", buildRealtimeMetric(todayOrderAchievementRate, todayOrderAchievementRate.subtract(yesterdayOrderAchievementRate))); result.put("defectRate", buildRealtimeMetric(todayDefectRate, todayDefectRate.subtract(yesterdayDefectRate))); return R.ok(result); } @GetMapping("/productionOrderProgress") @Operation(summary = "Production Order Progress") public R productionOrderProgress(@RequestParam(defaultValue = "all") String tab, @RequestParam(required = false) String status, @RequestParam(required = false) String bizDate, @RequestParam(defaultValue = "1") Long pageNum, @RequestParam(defaultValue = "10") Long pageSize) { LocalDate queryDate = parseDateOrNull(bizDate); if (!isBlank(bizDate) && queryDate == null) { return R.fail("bizDate格式错误,请使用yyyy-MM-dd"); } Integer statusFromParam = parseOrderStatus(status); if (!isBlank(status) && statusFromParam == null && !"all".equalsIgnoreCase(status.trim())) { return R.fail("status参数不合法,可选值:all/waiting/inProgress/completed/paused 或 1/2/3/4"); } Integer queryStatus = resolveOrderStatus(status, tab); long safePageNum = pageNum == null || pageNum < 1 ? 1 : pageNum; long safePageSize = pageSize == null || pageSize < 1 ? 10 : Math.min(pageSize, 50); long offset = (safePageNum - 1) * safePageSize; LocalDateTime startTime = queryDate == null ? null : queryDate.atStartOfDay(); LocalDateTime endTime = queryDate == null ? null : queryDate.plusDays(1).atStartOfDay(); List> rawRows = productionOrderMapper.selectHomeOrderProgressPage(queryStatus, offset, safePageSize, startTime, endTime); List> records = new ArrayList<>(); if (rawRows != null) { for (Map rawRow : rawRows) { records.add(buildOrderProgressRow(rawRow)); } } long waitingCount = 0L; long inProgressCount = 0L; long completedCount = 0L; long pausedCount = 0L; List> statusCountRows = productionOrderMapper.countHomeOrderProgressByStatus(startTime, endTime); if (statusCountRows != null) { for (Map countRow : statusCountRows) { Integer statusKey = toInteger(countRow.get("status")); long cnt = toLong(countRow.get("cnt")); if (Objects.equals(statusKey, ORDER_STATUS_WAIT)) { waitingCount = cnt; } else if (Objects.equals(statusKey, ORDER_STATUS_RUNNING)) { inProgressCount = cnt; } else if (Objects.equals(statusKey, ORDER_STATUS_COMPLETED)) { completedCount = cnt; } else if (Objects.equals(statusKey, ORDER_STATUS_PAUSED)) { pausedCount = cnt; } } } Map result = new LinkedHashMap<>(); result.put("tab", mapOrderTab(queryStatus)); result.put("status", mapOrderStatus(queryStatus)); result.put("bizDate", queryDate == null ? null : queryDate.format(DATE_FORMATTER)); result.put("total", toLong(productionOrderMapper.countHomeOrderProgress(queryStatus, startTime, endTime))); result.put("pageNum", safePageNum); result.put("pageSize", safePageSize); result.put("waitingCount", waitingCount); result.put("inProgressCount", inProgressCount); result.put("completedCount", completedCount); result.put("pausedCount", pausedCount); result.put("records", records); return R.ok(result); } @GetMapping("/todayProductionPlan") @Operation(summary = "Today Production Plan") public R todayProductionPlan(@RequestParam(defaultValue = "4") Long limit, @RequestParam(required = false) String planDate) { LocalDate queryDate = parseDateOrNull(planDate); if (!isBlank(planDate) && queryDate == null) { return R.fail("planDate格式错误,请使用yyyy-MM-dd"); } long safeLimit = limit == null || limit < 1 ? 4 : Math.min(limit, 20); LocalDateTime planStart = queryDate == null ? null : queryDate.atStartOfDay(); LocalDateTime planEnd = queryDate == null ? null : queryDate.plusDays(1).atStartOfDay(); List> records = new ArrayList<>(); List> rawRows = productionOrderMapper.selectHomeTodayProductionPlan(safeLimit, planStart, planEnd); if (rawRows != null) { for (Map rawRow : rawRows) { Map row = new LinkedHashMap<>(); Integer rowStatus = toInteger(rawRow.get("status")); row.put("orderNo", rawRow.get("orderNo")); row.put("productName", rawRow.get("productName")); row.put("plannedQuantity", scale(toBigDecimal(rawRow.get("plannedQuantity")))); row.put("dueDate", rawRow.get("dueDate")); row.put("status", rowStatus); row.put("statusLabel", mapOrderStatusLabel(rowStatus)); records.add(row); } } Map result = new LinkedHashMap<>(); result.put("planDate", queryDate == null ? null : queryDate.format(DATE_FORMATTER)); result.put("total", toLong(productionOrderMapper.countHomeTodayProductionPlan(planStart, planEnd))); result.put("records", records); return R.ok(result); } @GetMapping("/rawMaterialDetection") @Operation(summary = "原材料检测") public R rawMaterialDetection(@DefaultType Integer type){ return R.ok(homeService.rawMaterialDetection(type)); } @GetMapping("/processDetection") @Operation(summary = "过程检测") public R processDetection(@DefaultType Integer type){ return R.ok(homeService.processDetection(type)); } @GetMapping("/factoryDetection") @Operation(summary = "成品出厂检测") public R factoryDetection(@DefaultType Integer type){ return R.ok(homeService.factoryDetection(type)); } @GetMapping("/qualityInspectionCount") @Operation(summary = "质量检验数量") public R qualityInspectionCount(){ QualityInspectionCountDto qualityInspectionCountDto = homeService.qualityInspectionCount(); return R.ok(qualityInspectionCountDto); } @GetMapping("/nonComplianceWarning") @Operation(summary = "不合格预警") public R nonComplianceWarning(){ NonComplianceWarningDto nonComplianceWarningDto = homeService.nonComplianceWarning(); return R.ok(nonComplianceWarningDto); } @GetMapping("/completedInspectionCount") @Operation(summary = "完成检验数") public R completedInspectionCount(){ List list = homeService.completedInspectionCount(); return R.ok(list); } @GetMapping("/unqualifiedProductRanking") @Operation(summary = "不合格产品排名") public R unqualifiedProductRanking(){ List list = homeService.unqualifiedProductRanking(); return R.ok(list); } @GetMapping("/unqualifiedProductProcessingAnalysis") @Operation(summary = "不合格检品处理分析") public R unqualifiedProductProcessingAnalysis(){ List list = homeService.unqualifiedProductProcessingAnalysis(); return R.ok(list); } @GetMapping("/qualityStatistics") @Log(title = "质量分析", businessType = BusinessType.OTHER) @Operation(summary = "质量分析") public R qualityStatistics() { QualityStatisticsDto qualityStatisticsDto = homeService.qualityStatistics(); return R.ok(qualityStatisticsDto); } @GetMapping("/qualityInspectionStatistics") @Operation(summary = "质量统计") public R qualityInspectionStatistics(@DefaultType Integer type) { QualityStatisticsDto dto = homeService.qualityInspectionStatistics(type); return R.ok(dto); } /********************************************************财务类*****************************************************/ @GetMapping("/incomeExpenseAnalysis") @Operation(summary = "支收对比分析") public R incomeExpenseAnalysis(@DefaultType Integer type) { List> result = homeService.incomeExpenseAnalysis(type); return R.ok(result); } @GetMapping("/profitTrendAnalysis") @Operation(summary = "利润趋势分析") public R profitTrendAnalysis(){ List list = homeService.profitTrendAnalysis(); return R.ok(list); } @GetMapping("/expenseCompositionAnalysis") @Operation(summary = "构成分析") public R expenseCompositionAnalysis(@DefaultType Integer type) { List list = homeService.expenseCompositionAnalysis(type); return R.ok(list); } @GetMapping("/monthlyIncome") @Operation(summary = "月度收入") public R monthlyIncome(){ MonthlyIncomeDto dto = homeService.monthlyIncome(); return R.ok(dto); } @GetMapping("/monthlyExpenditure") @Operation(summary = "月度支出") public R monthlyExpenditure(){ MonthlyExpenditureDto dto = homeService.monthlyExpenditure(); return R.ok(dto); } @GetMapping("/statisticsReceivablePayable") @Log(title = "应收应付统计", businessType = BusinessType.OTHER) @Operation(summary = "应收应付统计") public R statisticsReceivablePayable(@DefaultType Integer type ) { StatisticsReceivablePayableDto statisticsReceivablePayable = homeService.statisticsReceivablePayable(type); return R.ok(statisticsReceivablePayable); } /********************************************************仓储类*****************************************************/ @GetMapping("/productCategoryDistribution") @Operation(summary = "产品大类分布") public R productCategoryDistribution() { ProductCategoryDistributionDto dto = homeService.productCategoryDistribution(); return R.ok(dto); } @GetMapping("/salesPurchaseStorageProductCount") @Operation(summary = "销售-采购-储存产品数") public R salesPurchaseStorageProductCount(){ List list = homeService.salesPurchaseStorageProductCount(); return R.ok(list); } @GetMapping("/productInOutAnalysis") @Operation(summary = "产品出入库分析") public R productInOutAnalysis(@DefaultType Integer type){ List> result = homeService.productInOutAnalysis(type); return R.ok(result); } @GetMapping("/productTurnoverDays") @Operation(summary = "产品周转天数") public R productTurnoverDays(){ List list = homeService.productTurnoverDays(); return R.ok(list); } private Map buildOrderProgressRow(Map rawRow) { Map row = new LinkedHashMap<>(); Integer rowStatus = toInteger(rawRow.get("status")); row.put("orderNo", rawRow.get("orderNo")); row.put("productName", rawRow.get("productName")); row.put("plannedQuantity", scale(toBigDecimal(rawRow.get("plannedQuantity")))); row.put("completedQuantity", scale(toBigDecimal(rawRow.get("completedQuantity")))); row.put("completionRate", scale(toBigDecimal(rawRow.get("completionRate")))); row.put("dueDate", rawRow.get("dueDate")); row.put("status", rowStatus); row.put("statusLabel", mapOrderStatusLabel(rowStatus)); return row; } private Integer resolveOrderStatus(String status, String tab) { if (!isBlank(status)) { return parseOrderStatus(status); } return parseOrderStatus(tab); } private Integer parseOrderStatus(String rawStatus) { if (isBlank(rawStatus)) { return null; } String normalized = rawStatus.trim().toLowerCase(); if ("all".equals(normalized)) { return null; } if ("1".equals(normalized) || "waiting".equals(normalized) || "wait".equals(normalized)) { return ORDER_STATUS_WAIT; } if ("2".equals(normalized) || "inprogress".equals(normalized) || "running".equals(normalized)) { return ORDER_STATUS_RUNNING; } if ("3".equals(normalized) || "completed".equals(normalized)) { return ORDER_STATUS_COMPLETED; } if ("4".equals(normalized) || "paused".equals(normalized)) { return ORDER_STATUS_PAUSED; } return null; } private String mapOrderTab(Integer status) { if (Objects.equals(status, ORDER_STATUS_RUNNING)) { return "inProgress"; } if (Objects.equals(status, ORDER_STATUS_COMPLETED)) { return "completed"; } if (Objects.equals(status, ORDER_STATUS_PAUSED)) { return "paused"; } if (Objects.equals(status, ORDER_STATUS_WAIT)) { return "waiting"; } return "all"; } private String mapOrderStatus(Integer status) { if (Objects.equals(status, ORDER_STATUS_WAIT)) { return "waiting"; } if (Objects.equals(status, ORDER_STATUS_RUNNING)) { return "inProgress"; } if (Objects.equals(status, ORDER_STATUS_COMPLETED)) { return "completed"; } if (Objects.equals(status, ORDER_STATUS_PAUSED)) { return "paused"; } return "all"; } private String mapOrderStatusLabel(Integer status) { if (Objects.equals(status, ORDER_STATUS_WAIT)) { return "待开始"; } if (Objects.equals(status, ORDER_STATUS_RUNNING)) { return "进行中"; } if (Objects.equals(status, ORDER_STATUS_COMPLETED)) { return "已完成"; } if (Objects.equals(status, ORDER_STATUS_PAUSED)) { return "已暂停"; } return "未知"; } private Map loadOutputStats(LocalDate startDate, LocalDate endDateExclusive) { String start = startDate.atStartOfDay().format(DATE_TIME_FORMATTER); String end = endDateExclusive.atStartOfDay().format(DATE_TIME_FORMATTER); BigDecimal quantity = BigDecimal.ZERO; BigDecimal scrapQty = BigDecimal.ZERO; List> rows = productionProductOutputMapper.selectDailyOutputStats(start, end); if (rows != null) { for (Map row : rows) { quantity = quantity.add(toBigDecimal(row.get("quantity"))); scrapQty = scrapQty.add(toBigDecimal(row.get("scrapQty"))); } } Map stats = new LinkedHashMap<>(); stats.put("quantity", quantity); stats.put("scrapQty", scrapQty); return stats; } private BigDecimal calcDeviceOee(LocalDate day) { long totalDeviceCount = deviceLedgerMapper.selectCount(new LambdaQueryWrapper<>()); if (totalDeviceCount <= 0) { return BigDecimal.ZERO; } Date start = Date.from(day.atStartOfDay(ZoneId.systemDefault()).toInstant()); Date end = Date.from(day.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant()); List repairList = deviceRepairMapper.selectList(new LambdaQueryWrapper() .select(DeviceRepair::getDeviceLedgerId) .ge(DeviceRepair::getRepairTime, start) .lt(DeviceRepair::getRepairTime, end) .in(DeviceRepair::getStatus, 0, 3)); long repairingDeviceCount = repairList == null ? 0 : repairList.stream() .map(DeviceRepair::getDeviceLedgerId) .filter(Objects::nonNull) .distinct() .count(); long availableDeviceCount = Math.max(totalDeviceCount - repairingDeviceCount, 0); return new BigDecimal(availableDeviceCount) .multiply(new BigDecimal("100")) .divide(new BigDecimal(totalDeviceCount), 2, RoundingMode.HALF_UP); } private BigDecimal calcOrderAchievementRate(LocalDate day) { List orderList = productionOrderMapper.selectList(new LambdaQueryWrapper() .select(ProductionOrder::getQuantity, ProductionOrder::getCompleteQuantity) .ge(ProductionOrder::getCreateTime, day.atStartOfDay()) .lt(ProductionOrder::getCreateTime, day.plusDays(1).atStartOfDay()) .ne(ProductionOrder::getStatus, ORDER_STATUS_PAUSED)); BigDecimal totalQuantity = BigDecimal.ZERO; BigDecimal totalCompleteQuantity = BigDecimal.ZERO; if (orderList != null) { for (ProductionOrder order : orderList) { totalQuantity = totalQuantity.add(zeroIfNull(order.getQuantity())); totalCompleteQuantity = totalCompleteQuantity.add(zeroIfNull(order.getCompleteQuantity())); } } return calcRate(totalCompleteQuantity, totalQuantity); } private BigDecimal calcDefectRate(LocalDate day) { Map stats = loadOutputStats(day, day.plusDays(1)); BigDecimal quantity = stats.get("quantity"); BigDecimal scrapQty = stats.get("scrapQty"); return calcRate(scrapQty, quantity.add(scrapQty)); } private Map buildRealtimeMetric(BigDecimal value, BigDecimal change) { Map metric = new LinkedHashMap<>(); metric.put("value", scale(value)); metric.put("compareYesterday", scale(change)); return metric; } private BigDecimal calcRate(BigDecimal numerator, BigDecimal denominator) { if (denominator == null || denominator.compareTo(BigDecimal.ZERO) <= 0) { return BigDecimal.ZERO; } return zeroIfNull(numerator) .multiply(new BigDecimal("100")) .divide(denominator, 2, RoundingMode.HALF_UP); } private BigDecimal toBigDecimal(Object value) { if (value == null) { return BigDecimal.ZERO; } if (value instanceof BigDecimal) { return (BigDecimal) value; } if (value instanceof Number) { return BigDecimal.valueOf(((Number) value).doubleValue()); } try { return new BigDecimal(String.valueOf(value)); } catch (Exception ex) { return BigDecimal.ZERO; } } private Integer toInteger(Object value) { if (value == null) { return null; } if (value instanceof Integer) { return (Integer) value; } if (value instanceof Number) { return ((Number) value).intValue(); } try { return Integer.valueOf(String.valueOf(value)); } catch (Exception ex) { return null; } } private long toLong(Object value) { if (value == null) { return 0L; } if (value instanceof Long) { return (Long) value; } if (value instanceof Number) { return ((Number) value).longValue(); } try { return Long.parseLong(String.valueOf(value)); } catch (Exception ex) { return 0L; } } private LocalDate parseDateOrNull(String rawDate) { if (isBlank(rawDate)) { return null; } try { return LocalDate.parse(rawDate.trim(), DATE_FORMATTER); } catch (DateTimeParseException ex) { return null; } } private boolean isBlank(String value) { return value == null || value.trim().isEmpty(); } private BigDecimal zeroIfNull(BigDecimal value) { return value == null ? BigDecimal.ZERO : value; } private BigDecimal scale(BigDecimal value) { return zeroIfNull(value).setScale(2, RoundingMode.HALF_UP); } }