package cn.iocoder.yudao.module.finance.service.statistics; import cn.iocoder.yudao.module.erp.api.finance.ErpFinanceStatisticsApi; import cn.iocoder.yudao.module.erp.api.finance.dto.ErpFinanceMonthlyStatisticsRespDTO; import cn.iocoder.yudao.module.finance.controller.admin.statistics.vo.FinStatisticsOverviewRespVO; import cn.iocoder.yudao.module.finance.controller.admin.statistics.vo.FinStatisticsReqVO; import cn.iocoder.yudao.module.finance.controller.admin.statistics.vo.FinStatisticsTrendRespVO; import cn.iocoder.yudao.module.finance.dal.dataobject.asset.FinFixedAssetDO; import cn.iocoder.yudao.module.finance.dal.dataobject.asset.FinIntangibleAssetDO; import cn.iocoder.yudao.module.finance.dal.mysql.subject.FinAccountSubjectMapper; import cn.iocoder.yudao.module.finance.dal.mysql.voucher.FinVoucherMapper; import cn.iocoder.yudao.module.finance.enums.FinVoucherStatusEnum; import cn.iocoder.yudao.module.finance.service.asset.FinFixedAssetService; import cn.iocoder.yudao.module.finance.service.asset.FinIntangibleAssetService; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.YearMonth; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.function.Function; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; import static cn.iocoder.yudao.module.finance.enums.ErrorCodeConstants.STATISTICS_PERIOD_INVALID; /** * 财务报表 / 会计核算看板 Service 实现类 * * @author 超级管理员 */ @Service @Validated public class FinStatisticsServiceImpl implements FinStatisticsService { @Resource private ErpFinanceStatisticsApi erpFinanceStatisticsApi; @Resource private FinVoucherMapper voucherMapper; @Resource private FinAccountSubjectMapper accountSubjectMapper; @Resource private FinFixedAssetService fixedAssetService; @Resource private FinIntangibleAssetService intangibleAssetService; @Override public FinStatisticsOverviewRespVO getStatisticsOverview(FinStatisticsReqVO reqVO) { // 1. 期间 LocalDate startDate = resolveStartDate(reqVO); LocalDate endDate = resolveEndDate(reqVO); validatePeriod(startDate, endDate); // 2. ERP 收支、购销金额 List monthlyList = getErpMonthlyStatistics(startDate, endDate); BigDecimal receiptPrice = sumOf(monthlyList, ErpFinanceMonthlyStatisticsRespDTO::getReceiptPrice); BigDecimal paymentPrice = sumOf(monthlyList, ErpFinanceMonthlyStatisticsRespDTO::getPaymentPrice); BigDecimal saleOrderPrice = sumOf(monthlyList, ErpFinanceMonthlyStatisticsRespDTO::getSaleOrderPrice); BigDecimal purchaseOrderPrice = sumOf(monthlyList, ErpFinanceMonthlyStatisticsRespDTO::getPurchaseOrderPrice); // 3. 固定资产、无形资产(时点数,取全部资产) BigDecimal fixedAssetOriginalValue = BigDecimal.ZERO; BigDecimal fixedAssetAccumulatedDepreciation = BigDecimal.ZERO; BigDecimal fixedAssetNetValue = BigDecimal.ZERO; for (FinFixedAssetDO asset : fixedAssetService.getFixedAssetList()) { fixedAssetOriginalValue = fixedAssetOriginalValue.add(defaultIfNull(asset.getOriginalValue())); fixedAssetAccumulatedDepreciation = fixedAssetAccumulatedDepreciation .add(defaultIfNull(asset.getAccumulatedDepreciation())); fixedAssetNetValue = fixedAssetNetValue.add(defaultIfNull(asset.getNetValue())); } BigDecimal intangibleAssetOriginalValue = BigDecimal.ZERO; BigDecimal intangibleAssetAccumulatedAmortization = BigDecimal.ZERO; BigDecimal intangibleAssetNetValue = BigDecimal.ZERO; for (FinIntangibleAssetDO asset : intangibleAssetService.getIntangibleAssetList()) { intangibleAssetOriginalValue = intangibleAssetOriginalValue.add(defaultIfNull(asset.getOriginalValue())); intangibleAssetAccumulatedAmortization = intangibleAssetAccumulatedAmortization .add(defaultIfNull(asset.getAccumulatedAmortization())); intangibleAssetNetValue = intangibleAssetNetValue.add(defaultIfNull(asset.getNetValue())); } // 4. 凭证统计 Long voucherUnpostedCount = defaultIfNull(voucherMapper.selectCountByStatus(FinVoucherStatusEnum.UNPOSTED.getStatus())); Long voucherPostedCount = defaultIfNull(voucherMapper.selectCountByStatus(FinVoucherStatusEnum.POSTED.getStatus())); Long voucherCancelledCount = defaultIfNull(voucherMapper.selectCountByStatus(FinVoucherStatusEnum.CANCELLED.getStatus())); // 5. 组装 return new FinStatisticsOverviewRespVO() .setStartDate(startDate).setEndDate(endDate) .setReceiptPrice(receiptPrice) .setPaymentPrice(paymentPrice) .setNetProfit(receiptPrice.subtract(paymentPrice)) .setSaleOrderPrice(saleOrderPrice) .setPurchaseOrderPrice(purchaseOrderPrice) .setFixedAssetOriginalValue(fixedAssetOriginalValue) .setFixedAssetAccumulatedDepreciation(fixedAssetAccumulatedDepreciation) .setFixedAssetNetValue(fixedAssetNetValue) .setIntangibleAssetOriginalValue(intangibleAssetOriginalValue) .setIntangibleAssetAccumulatedAmortization(intangibleAssetAccumulatedAmortization) .setIntangibleAssetNetValue(intangibleAssetNetValue) .setTotalAssetNetValue(fixedAssetNetValue.add(intangibleAssetNetValue)) .setSubjectCount(defaultIfNull(accountSubjectMapper.selectCount())) .setVoucherCount(voucherUnpostedCount + voucherPostedCount + voucherCancelledCount) .setVoucherUnpostedCount(voucherUnpostedCount) .setVoucherPostedCount(voucherPostedCount) .setVoucherCancelledCount(voucherCancelledCount); } @Override public List getStatisticsTrend(FinStatisticsReqVO reqVO) { // 1. 期间 LocalDate startDate = resolveStartDate(reqVO); LocalDate endDate = resolveEndDate(reqVO); validatePeriod(startDate, endDate); // 2. ERP 月度数据转 Map,便于按月份补 0 Map monthMap = convertMap( getErpMonthlyStatistics(startDate, endDate), ErpFinanceMonthlyStatisticsRespDTO::getMonth); // 3. 期间内逐月输出,没有数据的月份补 0 List list = new ArrayList<>(); YearMonth endMonth = YearMonth.from(endDate); for (YearMonth month = YearMonth.from(startDate); !month.isAfter(endMonth); month = month.plusMonths(1)) { ErpFinanceMonthlyStatisticsRespDTO dto = monthMap.get(month.toString()); BigDecimal receiptPrice = dto == null ? BigDecimal.ZERO : defaultIfNull(dto.getReceiptPrice()); BigDecimal paymentPrice = dto == null ? BigDecimal.ZERO : defaultIfNull(dto.getPaymentPrice()); list.add(new FinStatisticsTrendRespVO() .setMonth(month.toString()) .setReceiptPrice(receiptPrice) .setPaymentPrice(paymentPrice) .setNetProfit(receiptPrice.subtract(paymentPrice)) .setSaleOrderPrice(dto == null ? BigDecimal.ZERO : defaultIfNull(dto.getSaleOrderPrice())) .setPurchaseOrderPrice(dto == null ? BigDecimal.ZERO : defaultIfNull(dto.getPurchaseOrderPrice()))); } return list; } /** * 调用 ERP 模块 API 获取月度统计,并做兜底解包 */ private List getErpMonthlyStatistics(LocalDate startDate, LocalDate endDate) { List list = erpFinanceStatisticsApi .getMonthlyStatistics(startDate.atStartOfDay(), endDate.plusDays(1).atStartOfDay()) .getCheckedData(); return list == null ? List.of() : list; } private LocalDate resolveStartDate(FinStatisticsReqVO reqVO) { return reqVO.getStartMonth() != null ? reqVO.getStartMonth().atDay(1) : LocalDate.now().withDayOfYear(1); } private LocalDate resolveEndDate(FinStatisticsReqVO reqVO) { return reqVO.getEndMonth() != null ? reqVO.getEndMonth().atEndOfMonth() : LocalDate.now(); } private void validatePeriod(LocalDate startDate, LocalDate endDate) { if (startDate.isAfter(endDate)) { throw exception(STATISTICS_PERIOD_INVALID, YearMonth.from(startDate), YearMonth.from(endDate)); } } private static BigDecimal sumOf(List list, Function getter) { return list.stream().map(getter).map(FinStatisticsServiceImpl::defaultIfNull) .reduce(BigDecimal.ZERO, BigDecimal::add); } private static BigDecimal defaultIfNull(BigDecimal amount) { return amount == null ? BigDecimal.ZERO : amount; } private static Long defaultIfNull(Long value) { return value == null ? 0L : value; } }