From c15e67c83394c1734eb4e9802d8f343c6076efc1 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期三, 13 五月 2026 16:16:45 +0800
Subject: [PATCH] Merge branch 'dev_New_pro' into dev_宁夏_英泽防锈

---
 src/main/java/com/ruoyi/account/service/impl/financial/FinLedgerServiceImpl.java |  206 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 206 insertions(+), 0 deletions(-)

diff --git a/src/main/java/com/ruoyi/account/service/impl/financial/FinLedgerServiceImpl.java b/src/main/java/com/ruoyi/account/service/impl/financial/FinLedgerServiceImpl.java
new file mode 100644
index 0000000..a489c67
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/service/impl/financial/FinLedgerServiceImpl.java
@@ -0,0 +1,206 @@
+package com.ruoyi.account.service.impl.financial;
+
+import com.ruoyi.account.bean.dto.financial.FinDetailLedgerQueryDto;
+import com.ruoyi.account.bean.dto.financial.FinLedgerQueryDto;
+import com.ruoyi.account.bean.vo.financial.FinLedgerEntryRecordVo;
+import com.ruoyi.account.bean.vo.financial.FinLedgerRowVo;
+import com.ruoyi.account.mapper.financial.FinVoucherEntryMapper;
+import com.ruoyi.account.service.financial.FinLedgerService;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.StringUtils;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDate;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.*;
+
+/**
+ * 绉戠洰鎬昏处/鏄庣粏璐︽湇鍔″疄鐜般��
+ */
+@Service
+@RequiredArgsConstructor
+public class FinLedgerServiceImpl implements FinLedgerService {
+
+    private static final DateTimeFormatter MONTH_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM");
+    private static final BigDecimal ZERO = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
+
+    private final FinVoucherEntryMapper finVoucherEntryMapper;
+
+    @Override
+    public List<FinLedgerRowVo> queryGeneralLedger(FinLedgerQueryDto queryDto) {
+        if (queryDto == null || StringUtils.isEmpty(queryDto.getSubjectCode())) {
+            return Collections.emptyList();
+        }
+        YearMonth startMonth = parseMonth(queryDto.getStartMonth(), "寮�濮嬫湀浠�");
+        YearMonth endMonth = parseMonth(queryDto.getEndMonth(), "缁撴潫鏈堜唤");
+        if (startMonth.isAfter(endMonth)) {
+            throw new ServiceException("寮�濮嬫湀浠戒笉鑳藉ぇ浜庣粨鏉熸湀浠�");
+        }
+        return buildLedgerRows(queryDto.getSubjectCode(), startMonth, endMonth, null, null);
+    }
+
+    @Override
+    public List<FinLedgerRowVo> queryDetailLedger(FinDetailLedgerQueryDto queryDto) {
+        if (queryDto == null || StringUtils.isEmpty(queryDto.getSubjectCode())) {
+            return Collections.emptyList();
+        }
+        YearMonth startMonth = parseMonth(queryDto.getStartMonth(), "寮�濮嬫湀浠�");
+        YearMonth endMonth = parseMonth(queryDto.getEndMonth(), "缁撴潫鏈堜唤");
+        if (startMonth.isAfter(endMonth)) {
+            throw new ServiceException("寮�濮嬫湀浠戒笉鑳藉ぇ浜庣粨鏉熸湀浠�");
+        }
+        return buildLedgerRows(queryDto.getSubjectCode(), startMonth, endMonth, queryDto.getAuxiliaryType(), queryDto.getAuxiliaryId());
+    }
+
+    /**
+     * 鏋勫缓璐︾翱琛屾暟鎹紝杈撳嚭鏈熷垵銆佸垎褰曘�佹湰鏈堝悎璁°�佹湰骞寸疮璁°��
+     */
+    private List<FinLedgerRowVo> buildLedgerRows(String subjectCode,
+                                                 YearMonth startMonth,
+                                                 YearMonth endMonth,
+                                                 String auxiliaryType,
+                                                 String auxiliaryId) {
+        LocalDate startDate = startMonth.atDay(1);
+        LocalDate endDate = endMonth.atEndOfMonth();
+
+        List<FinLedgerEntryRecordVo> openingEntries = finVoucherEntryMapper.listPostedEntriesBefore(
+                subjectCode, startDate, auxiliaryType, auxiliaryId
+        );
+        BigDecimal openingBalance = calculateBalance(openingEntries);
+
+        List<FinLedgerEntryRecordVo> currentPeriodEntries = finVoucherEntryMapper.listPostedEntries(
+                subjectCode, startDate, endDate, auxiliaryType, auxiliaryId
+        );
+        Map<YearMonth, List<FinLedgerEntryRecordVo>> monthEntriesMap = groupEntriesByMonth(currentPeriodEntries);
+
+        List<FinLedgerRowVo> rows = new ArrayList<>();
+        BigDecimal runningBalance = openingBalance;
+        BigDecimal yearDebit = ZERO;
+        BigDecimal yearCredit = ZERO;
+
+        for (YearMonth month = startMonth; !month.isAfter(endMonth); month = month.plusMonths(1)) {
+            rows.add(buildOpeningRow(month.atDay(1), runningBalance));
+
+            List<FinLedgerEntryRecordVo> monthEntries = monthEntriesMap.getOrDefault(month, Collections.emptyList());
+            BigDecimal monthDebit = ZERO;
+            BigDecimal monthCredit = ZERO;
+            for (FinLedgerEntryRecordVo entry : monthEntries) {
+                BigDecimal debit = money(entry.getDebit());
+                BigDecimal credit = money(entry.getCredit());
+                runningBalance = runningBalance.add(debit).subtract(credit);
+                monthDebit = monthDebit.add(debit);
+                monthCredit = monthCredit.add(credit);
+
+                FinLedgerRowVo row = new FinLedgerRowVo();
+                row.setRowType("entry");
+                row.setDate(entry.getVoucherDate());
+                row.setVoucherNo(entry.getVoucherNo());
+                row.setSummary(StringUtils.isNotEmpty(entry.getSummary()) ? entry.getSummary() : "");
+                row.setDebit(debit);
+                row.setCredit(credit);
+                row.setBalance(money(runningBalance));
+                row.setDirection(resolveDirection(runningBalance));
+                rows.add(row);
+            }
+
+            rows.add(buildMonthlyTotalRow(month.atEndOfMonth(), monthDebit, monthCredit, runningBalance));
+            yearDebit = yearDebit.add(monthDebit);
+            yearCredit = yearCredit.add(monthCredit);
+        }
+
+        rows.add(buildYearlyTotalRow(endMonth.atEndOfMonth(), yearDebit, yearCredit, runningBalance));
+        return rows;
+    }
+
+    private Map<YearMonth, List<FinLedgerEntryRecordVo>> groupEntriesByMonth(List<FinLedgerEntryRecordVo> entries) {
+        Map<YearMonth, List<FinLedgerEntryRecordVo>> map = new LinkedHashMap<>();
+        for (FinLedgerEntryRecordVo entry : entries) {
+            if (entry.getVoucherDate() == null) {
+                continue;
+            }
+            YearMonth month = YearMonth.from(entry.getVoucherDate());
+            map.computeIfAbsent(month, key -> new ArrayList<>()).add(entry);
+        }
+        return map;
+    }
+
+    private FinLedgerRowVo buildOpeningRow(LocalDate date, BigDecimal openingBalance) {
+        FinLedgerRowVo row = new FinLedgerRowVo();
+        row.setRowType("opening");
+        row.setDate(date);
+        row.setVoucherNo("-");
+        row.setSummary("鏈熷垵浣欓");
+        row.setDebit(ZERO);
+        row.setCredit(ZERO);
+        row.setBalance(money(openingBalance));
+        row.setDirection(resolveDirection(openingBalance));
+        return row;
+    }
+
+    private FinLedgerRowVo buildMonthlyTotalRow(LocalDate date,
+                                                BigDecimal monthDebit,
+                                                BigDecimal monthCredit,
+                                                BigDecimal monthBalance) {
+        FinLedgerRowVo row = new FinLedgerRowVo();
+        row.setRowType("monthly_total");
+        row.setDate(date);
+        row.setVoucherNo("-");
+        row.setSummary("鏈湀鍚堣");
+        row.setDebit(money(monthDebit));
+        row.setCredit(money(monthCredit));
+        row.setBalance(money(monthBalance));
+        row.setDirection(resolveDirection(monthBalance));
+        return row;
+    }
+
+    private FinLedgerRowVo buildYearlyTotalRow(LocalDate date,
+                                               BigDecimal yearDebit,
+                                               BigDecimal yearCredit,
+                                               BigDecimal yearBalance) {
+        FinLedgerRowVo row = new FinLedgerRowVo();
+        row.setRowType("yearly_total");
+        row.setDate(date);
+        row.setVoucherNo("-");
+        row.setSummary("鍚堣");
+        row.setDebit(money(yearDebit));
+        row.setCredit(money(yearCredit));
+        row.setBalance(money(yearBalance));
+        row.setDirection(resolveDirection(yearBalance));
+        return row;
+    }
+
+    private BigDecimal calculateBalance(List<FinLedgerEntryRecordVo> entries) {
+        BigDecimal balance = ZERO;
+        for (FinLedgerEntryRecordVo entry : entries) {
+            balance = balance.add(money(entry.getDebit())).subtract(money(entry.getCredit()));
+        }
+        return money(balance);
+    }
+
+    private String resolveDirection(BigDecimal balance) {
+        return money(balance).compareTo(BigDecimal.ZERO) >= 0 ? "鍊�" : "璐�";
+    }
+
+    private YearMonth parseMonth(String value, String fieldLabel) {
+        if (StringUtils.isEmpty(value)) {
+            throw new ServiceException(fieldLabel + "涓嶈兘涓虹┖锛屾牸寮忓簲涓篩YYY-MM");
+        }
+        try {
+            return YearMonth.parse(value, MONTH_FORMATTER);
+        } catch (DateTimeParseException ex) {
+            throw new ServiceException(fieldLabel + "鏍煎紡閿欒锛屾牸寮忓簲涓篩YYY-MM");
+        }
+    }
+
+    private BigDecimal money(BigDecimal value) {
+        if (value == null) {
+            return ZERO;
+        }
+        return value.setScale(2, RoundingMode.HALF_UP);
+    }
+}

--
Gitblit v1.9.3