From fd60ef9d3c3d7c1eee9a6fb458fe4084463b5b81 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期二, 26 五月 2026 10:42:41 +0800
Subject: [PATCH] refactor(account): 重构财务模块DTO包结构并添加总账科目功能

---
 src/main/java/com/ruoyi/account/mapper/financial/FinVoucherMapper.java                    |   12 
 src/main/resources/mapper/account/AccountExpenseMapper.xml                                |    2 
 src/main/java/com/ruoyi/account/bean/dto/financial/FinLedgerQueryDto.java                 |   25 
 src/main/java/com/ruoyi/account/pojo/financial/FinFixedAsset.java                         |   96 +
 src/main/java/com/ruoyi/account/mapper/financial/FinFixedAssetMapper.java                 |   12 
 src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherStatusDto.java               |   15 
 src/main/java/com/ruoyi/account/service/impl/financial/FinLedgerServiceImpl.java          |  237 ++++
 src/main/java/com/ruoyi/account/bean/dto/SalesRefundAmountOrderDto.java                   |    3 
 src/main/java/com/ruoyi/account/controller/financial/AccountSubjectController.java        |   70 +
 src/main/java/com/ruoyi/account/controller/financial/FinFixedAssetController.java         |   63 +
 src/main/java/com/ruoyi/account/mapper/financial/AccountSubjectMapper.java                |   23 
 src/main/java/com/ruoyi/account/service/impl/financial/FinVoucherServiceImpl.java         |  324 ++++++
 src/main/java/com/ruoyi/account/mapper/financial/FinIntangibleAssetMapper.java            |   12 
 src/main/java/com/ruoyi/account/mapper/AccountIncomeMapper.java                           |    4 
 src/main/java/com/ruoyi/account/service/impl/financial/FinIntangibleAssetServiceImpl.java |  253 ++++
 src/main/java/com/ruoyi/account/bean/dto/SalesReceiptReturnDto.java                       |    2 
 src/main/java/com/ruoyi/account/bean/dto/financial/AccountSubjectImportDto.java           |   36 
 src/main/resources/application-dev.yml                                                    |    2 
 src/main/resources/mapper/device/DeviceLedgerMapper.xml                                   |    2 
 src/main/java/com/ruoyi/account/controller/financial/FinLedgerController.java             |   39 
 src/main/java/com/ruoyi/account/controller/SalesReceiptReturnController.java              |    4 
 src/main/java/com/ruoyi/device/mapper/DeviceLedgerMapper.java                             |    3 
 src/main/java/com/ruoyi/account/bean/vo/financial/FinLedgerEntryRecordVo.java             |   43 
 src/main/resources/mapper/account/AccountIncomeMapper.xml                                 |    2 
 src/main/java/com/ruoyi/common/enums/FileNameType.java                                    |    3 
 src/main/resources/mapper/account/SalesRefundAmountOrderMapper.xml                        |    2 
 src/main/java/com/ruoyi/account/pojo/financial/AccountSubject.java                        |  113 ++
 src/main/java/com/ruoyi/account/pojo/financial/FinVoucherEntry.java                       |   79 +
 src/main/java/com/ruoyi/account/service/financial/FinIntangibleAssetService.java          |   26 
 src/main/java/com/ruoyi/account/bean/dto/AccountDto2.java                                 |    5 
 src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnManagementServiceImpl.java   |    4 
 src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherPageDto.java                 |   37 
 src/main/java/com/ruoyi/account/mapper/financial/FinVoucherEntryMapper.java               |   28 
 src/main/java/com/ruoyi/account/bean/vo/financial/FinLedgerRowVo.java                     |   55 +
 src/main/java/com/ruoyi/account/service/impl/AccountingServiceImpl.java                   |    6 
 src/main/java/com/ruoyi/account/bean/dto/financial/FinIntangibleAssetDto.java             |   13 
 src/main/java/com/ruoyi/account/controller/AccountExpenseController.java                  |    5 
 src/main/java/com/ruoyi/account/controller/financial/FinIntangibleAssetController.java    |   63 +
 src/main/java/com/ruoyi/account/service/impl/financial/FinFixedAssetServiceImpl.java      |  234 ++++
 src/main/java/com/ruoyi/account/bean/dto/financial/AccountSubjectDto.java                 |   10 
 src/main/java/com/ruoyi/account/bean/dto/financial/FinDetailLedgerQueryDto.java           |   22 
 src/main/java/com/ruoyi/account/bean/vo/financial/AccountSubjectVo.java                   |   21 
 src/main/java/com/ruoyi/account/service/AccountIncomeService.java                         |    5 
 src/main/java/com/ruoyi/account/service/impl/AccountIncomeServiceImpl.java                |    8 
 src/main/java/com/ruoyi/account/bean/vo/financial/FinVoucherDetailVo.java                 |   25 
 src/main/java/com/ruoyi/account/service/AccountExpenseService.java                        |    8 
 src/main/java/com/ruoyi/account/service/impl/financial/AccountSubjectServiceImpl.java     |  387 +++++++
 src/main/java/com/ruoyi/account/bean/dto/DeviceTypeDistributionVO.java                    |    3 
 src/main/java/com/ruoyi/account/service/financial/AccountSubjectService.java              |   32 
 src/main/java/com/ruoyi/account/bean/dto/financial/FinFixedAssetDto.java                  |   13 
 src/main/java/com/ruoyi/account/service/impl/SalesRefundAmountOrderServiceImpl.java       |    2 
 src/main/java/com/ruoyi/account/bean/dto/ReportDateDto.java                               |    3 
 src/main/java/com/ruoyi/account/service/financial/FinLedgerService.java                   |   17 
 src/main/java/com/ruoyi/account/service/financial/FinVoucherService.java                  |   27 
 src/main/java/com/ruoyi/account/bean/dto/AccountDto3.java                                 |    2 
 src/main/java/com/ruoyi/account/controller/SalesRefundAmountOrderController.java          |    7 
 src/main/java/com/ruoyi/account/bean/dto/AccountDto.java                                  |   14 
 src/main/java/com/ruoyi/account/mapper/AccountExpenseMapper.java                          |    5 
 src/main/java/com/ruoyi/account/service/financial/FinFixedAssetService.java               |   26 
 src/main/java/com/ruoyi/account/service/impl/AccountExpenseServiceImpl.java               |    9 
 src/main/java/com/ruoyi/account/mapper/SalesRefundAmountOrderMapper.java                  |    2 
 src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherEntryDto.java                |   13 
 src/main/java/com/ruoyi/account/service/SalesRefundAmountOrderService.java                |    2 
 src/main/java/com/ruoyi/account/pojo/financial/FinIntangibleAsset.java                    |   92 +
 src/main/java/com/ruoyi/account/bean/dto/financial/FinIdBatchDto.java                     |   17 
 src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherDto.java                     |   27 
 src/main/java/com/ruoyi/account/service/SalesReceiptReturnService.java                    |    2 
 src/main/java/com/ruoyi/account/pojo/financial/FinVoucher.java                            |   77 +
 src/main/java/com/ruoyi/account/controller/financial/FinVoucherController.java            |   69 +
 src/main/resources/mapper/account/financial/AccountSubjectMapper.xml                      |   31 
 src/main/java/com/ruoyi/account/bean/dto/DeviceTypeDetail.java                            |    2 
 src/main/java/com/ruoyi/account/service/impl/SalesReceiptReturnServiceImpl.java           |    2 
 src/main/resources/mapper/account/financial/FinVoucherEntryMapper.xml                     |   74 +
 73 files changed, 2,928 insertions(+), 85 deletions(-)

diff --git a/src/main/java/com/ruoyi/account/dto/AccountDto.java b/src/main/java/com/ruoyi/account/bean/dto/AccountDto.java
similarity index 60%
rename from src/main/java/com/ruoyi/account/dto/AccountDto.java
rename to src/main/java/com/ruoyi/account/bean/dto/AccountDto.java
index c8e0d06..5af0ca6 100644
--- a/src/main/java/com/ruoyi/account/dto/AccountDto.java
+++ b/src/main/java/com/ruoyi/account/bean/dto/AccountDto.java
@@ -1,22 +1,10 @@
-package com.ruoyi.account.dto;
+package com.ruoyi.account.bean.dto;
 
-import com.baomidou.mybatisplus.annotation.*;
-import com.fasterxml.jackson.annotation.JsonFormat;
-import com.ruoyi.account.pojo.AccountExpense;
-import com.ruoyi.account.pojo.AccountIncome;
-import com.ruoyi.dto.DateQueryDto;
-import com.ruoyi.framework.aspectj.lang.annotation.Excel;
-import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
-import org.springframework.format.annotation.DateTimeFormat;
 
-import javax.validation.constraints.NotBlank;
 import java.io.Serializable;
 import java.math.BigDecimal;
-import java.time.LocalDateTime;
-import java.util.Date;
 import java.util.List;
-import java.util.Map;
 
 /**
  * 璐㈠姟绠$悊--璐㈠姟鎶ヨ〃
diff --git a/src/main/java/com/ruoyi/account/dto/AccountDto2.java b/src/main/java/com/ruoyi/account/bean/dto/AccountDto2.java
similarity index 72%
rename from src/main/java/com/ruoyi/account/dto/AccountDto2.java
rename to src/main/java/com/ruoyi/account/bean/dto/AccountDto2.java
index 235278b..7c4e9e5 100644
--- a/src/main/java/com/ruoyi/account/dto/AccountDto2.java
+++ b/src/main/java/com/ruoyi/account/bean/dto/AccountDto2.java
@@ -1,12 +1,9 @@
-package com.ruoyi.account.dto;
+package com.ruoyi.account.bean.dto;
 
-import com.ruoyi.account.pojo.AccountExpense;
-import com.ruoyi.account.pojo.AccountIncome;
 import lombok.Data;
 
 import java.io.Serializable;
 import java.math.BigDecimal;
-import java.util.List;
 
 /**
  * 璐㈠姟绠$悊--璐㈠姟鎶ヨ〃(绫诲瀷)
diff --git a/src/main/java/com/ruoyi/account/dto/AccountDto3.java b/src/main/java/com/ruoyi/account/bean/dto/AccountDto3.java
similarity index 91%
rename from src/main/java/com/ruoyi/account/dto/AccountDto3.java
rename to src/main/java/com/ruoyi/account/bean/dto/AccountDto3.java
index 33b4b8e..117fa77 100644
--- a/src/main/java/com/ruoyi/account/dto/AccountDto3.java
+++ b/src/main/java/com/ruoyi/account/bean/dto/AccountDto3.java
@@ -1,4 +1,4 @@
-package com.ruoyi.account.dto;
+package com.ruoyi.account.bean.dto;
 
 import lombok.Data;
 
diff --git a/src/main/java/com/ruoyi/account/dto/DeviceTypeDetail.java b/src/main/java/com/ruoyi/account/bean/dto/DeviceTypeDetail.java
similarity index 88%
rename from src/main/java/com/ruoyi/account/dto/DeviceTypeDetail.java
rename to src/main/java/com/ruoyi/account/bean/dto/DeviceTypeDetail.java
index ccbc897..1ea4837 100644
--- a/src/main/java/com/ruoyi/account/dto/DeviceTypeDetail.java
+++ b/src/main/java/com/ruoyi/account/bean/dto/DeviceTypeDetail.java
@@ -1,4 +1,4 @@
-package com.ruoyi.account.dto;
+package com.ruoyi.account.bean.dto;
 
 import lombok.Data;
 
diff --git a/src/main/java/com/ruoyi/account/dto/DeviceTypeDistributionVO.java b/src/main/java/com/ruoyi/account/bean/dto/DeviceTypeDistributionVO.java
similarity index 90%
rename from src/main/java/com/ruoyi/account/dto/DeviceTypeDistributionVO.java
rename to src/main/java/com/ruoyi/account/bean/dto/DeviceTypeDistributionVO.java
index f4c487b..f540b42 100644
--- a/src/main/java/com/ruoyi/account/dto/DeviceTypeDistributionVO.java
+++ b/src/main/java/com/ruoyi/account/bean/dto/DeviceTypeDistributionVO.java
@@ -1,6 +1,5 @@
-package com.ruoyi.account.dto;
+package com.ruoyi.account.bean.dto;
 
-import com.mchange.v1.util.ListUtils;
 import lombok.Data;
 
 import java.math.BigDecimal;
diff --git a/src/main/java/com/ruoyi/account/dto/ReportDateDto.java b/src/main/java/com/ruoyi/account/bean/dto/ReportDateDto.java
similarity index 92%
rename from src/main/java/com/ruoyi/account/dto/ReportDateDto.java
rename to src/main/java/com/ruoyi/account/bean/dto/ReportDateDto.java
index ab3799a..5e89162 100644
--- a/src/main/java/com/ruoyi/account/dto/ReportDateDto.java
+++ b/src/main/java/com/ruoyi/account/bean/dto/ReportDateDto.java
@@ -1,11 +1,10 @@
-package com.ruoyi.account.dto;
+package com.ruoyi.account.bean.dto;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
 import lombok.Data;
 import org.springframework.format.annotation.DateTimeFormat;
 
 import java.time.LocalDate;
-import java.util.Date;
 
 /**
  * @author :yys
diff --git a/src/main/java/com/ruoyi/account/dto/SalesReceiptReturnDto.java b/src/main/java/com/ruoyi/account/bean/dto/SalesReceiptReturnDto.java
similarity index 79%
rename from src/main/java/com/ruoyi/account/dto/SalesReceiptReturnDto.java
rename to src/main/java/com/ruoyi/account/bean/dto/SalesReceiptReturnDto.java
index 728d9a7..e0d9e81 100644
--- a/src/main/java/com/ruoyi/account/dto/SalesReceiptReturnDto.java
+++ b/src/main/java/com/ruoyi/account/bean/dto/SalesReceiptReturnDto.java
@@ -1,4 +1,4 @@
-package com.ruoyi.account.dto;
+package com.ruoyi.account.bean.dto;
 
 import com.ruoyi.account.pojo.SalesReceiptReturn;
 import lombok.Data;
diff --git a/src/main/java/com/ruoyi/account/dto/SalesRefundAmountOrderDto.java b/src/main/java/com/ruoyi/account/bean/dto/SalesRefundAmountOrderDto.java
similarity index 88%
rename from src/main/java/com/ruoyi/account/dto/SalesRefundAmountOrderDto.java
rename to src/main/java/com/ruoyi/account/bean/dto/SalesRefundAmountOrderDto.java
index 698e0e1..e7f49c9 100644
--- a/src/main/java/com/ruoyi/account/dto/SalesRefundAmountOrderDto.java
+++ b/src/main/java/com/ruoyi/account/bean/dto/SalesRefundAmountOrderDto.java
@@ -1,7 +1,6 @@
-package com.ruoyi.account.dto;
+package com.ruoyi.account.bean.dto;
 
 import com.ruoyi.account.pojo.SalesRefundAmountOrder;
-import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
diff --git a/src/main/java/com/ruoyi/account/bean/dto/financial/AccountSubjectDto.java b/src/main/java/com/ruoyi/account/bean/dto/financial/AccountSubjectDto.java
new file mode 100644
index 0000000..4d55666
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/dto/financial/AccountSubjectDto.java
@@ -0,0 +1,10 @@
+package com.ruoyi.account.bean.dto.financial;
+
+import com.ruoyi.account.pojo.financial.AccountSubject;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class AccountSubjectDto extends AccountSubject {
+}
diff --git a/src/main/java/com/ruoyi/account/bean/dto/financial/AccountSubjectImportDto.java b/src/main/java/com/ruoyi/account/bean/dto/financial/AccountSubjectImportDto.java
new file mode 100644
index 0000000..7ea5225
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/dto/financial/AccountSubjectImportDto.java
@@ -0,0 +1,36 @@
+package com.ruoyi.account.bean.dto.financial;
+
+import com.ruoyi.framework.aspectj.lang.annotation.Excel;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+public class AccountSubjectImportDto {
+
+    @Schema(description = "绉戠洰缂栫爜")
+    @Excel(name = "绉戠洰缂栫爜")
+    private String subjectCode;
+
+    @Schema(description = "绉戠洰鍚嶇О")
+    @Excel(name = "绉戠洰鍚嶇О")
+    private String subjectName;
+
+    @Schema(description = "绉戠洰绫诲瀷")
+    @Excel(name = "绉戠洰绫诲瀷")
+    private String subjectType;
+
+    @Schema(description = "浣欓鏂瑰悜")
+    @Excel(name = "浣欓鏂瑰悜")
+    private String balanceDirection;
+
+    /**
+     * 鐘舵�� 0鍚敤 1绂佺敤
+     */
+    @Schema(description = "鐘舵��")
+    @Excel(name = "鐘舵��",readConverterExp = "0=鍚敤,1=绂佺敤")
+    private Integer status;
+
+    @Schema(description = "澶囨敞")
+    @Excel(name = "澶囨敞")
+    private String remark;
+}
diff --git a/src/main/java/com/ruoyi/account/bean/dto/financial/FinDetailLedgerQueryDto.java b/src/main/java/com/ruoyi/account/bean/dto/financial/FinDetailLedgerQueryDto.java
new file mode 100644
index 0000000..84a3676
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/dto/financial/FinDetailLedgerQueryDto.java
@@ -0,0 +1,22 @@
+package com.ruoyi.account.bean.dto.financial;
+
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 绉戠洰鏄庣粏璐︽煡璇㈠弬鏁帮紙鍚緟鍔╂牳绠楁潯浠讹級銆�
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class FinDetailLedgerQueryDto extends FinLedgerQueryDto {
+
+    /**
+     * 杈呭姪鏍哥畻绫诲瀷锛歝ustomer/supplier/department/employee/project銆�
+     */
+    private String auxiliaryType;
+
+    /**
+     * 杈呭姪鏍哥畻瀵硅薄ID銆�
+     */
+    private String auxiliaryId;
+}
diff --git a/src/main/java/com/ruoyi/account/bean/dto/financial/FinFixedAssetDto.java b/src/main/java/com/ruoyi/account/bean/dto/financial/FinFixedAssetDto.java
new file mode 100644
index 0000000..c6baeca
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/dto/financial/FinFixedAssetDto.java
@@ -0,0 +1,13 @@
+package com.ruoyi.account.bean.dto.financial;
+
+import com.ruoyi.account.pojo.financial.FinFixedAsset;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 鍥哄畾璧勪骇鏌ヨ涓庝繚瀛� DTO銆�
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class FinFixedAssetDto extends FinFixedAsset {
+}
diff --git a/src/main/java/com/ruoyi/account/bean/dto/financial/FinIdBatchDto.java b/src/main/java/com/ruoyi/account/bean/dto/financial/FinIdBatchDto.java
new file mode 100644
index 0000000..e3023ed
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/dto/financial/FinIdBatchDto.java
@@ -0,0 +1,17 @@
+package com.ruoyi.account.bean.dto.financial;
+
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 鎵归噺ID璇锋眰鍙傛暟銆�
+ */
+@Data
+public class FinIdBatchDto {
+
+    /**
+     * ID闆嗗悎銆�
+     */
+    private List<Long> ids;
+}
diff --git a/src/main/java/com/ruoyi/account/bean/dto/financial/FinIntangibleAssetDto.java b/src/main/java/com/ruoyi/account/bean/dto/financial/FinIntangibleAssetDto.java
new file mode 100644
index 0000000..60dd50e
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/dto/financial/FinIntangibleAssetDto.java
@@ -0,0 +1,13 @@
+package com.ruoyi.account.bean.dto.financial;
+
+import com.ruoyi.account.pojo.financial.FinIntangibleAsset;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 鏃犲舰璧勪骇鏌ヨ涓庝繚瀛� DTO銆�
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class FinIntangibleAssetDto extends FinIntangibleAsset {
+}
diff --git a/src/main/java/com/ruoyi/account/bean/dto/financial/FinLedgerQueryDto.java b/src/main/java/com/ruoyi/account/bean/dto/financial/FinLedgerQueryDto.java
new file mode 100644
index 0000000..cfc0553
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/dto/financial/FinLedgerQueryDto.java
@@ -0,0 +1,25 @@
+package com.ruoyi.account.bean.dto.financial;
+
+import lombok.Data;
+
+/**
+ * 绉戠洰璐︽煡璇㈠弬鏁般��
+ */
+@Data
+public class FinLedgerQueryDto {
+
+    /**
+     * 绉戠洰缂栫爜锛堟敮鎸佹湯绾ф垨鎸囧畾绉戠洰锛夈��
+     */
+    private String subjectCode;
+
+    /**
+     * 寮�濮嬫湀浠斤紝鏍煎紡锛歒YYY-MM銆�
+     */
+    private String startMonth;
+
+    /**
+     * 缁撴潫鏈堜唤锛屾牸寮忥細YYYY-MM銆�
+     */
+    private String endMonth;
+}
diff --git a/src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherDto.java b/src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherDto.java
new file mode 100644
index 0000000..d010dc2
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherDto.java
@@ -0,0 +1,27 @@
+package com.ruoyi.account.bean.dto.financial;
+
+import com.ruoyi.account.pojo.financial.FinVoucher;
+import com.ruoyi.basic.dto.StorageBlobDTO;
+import com.ruoyi.sales.pojo.CommonFile;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+/**
+ * 鍑瘉淇濆瓨 DTO锛堜富琛� + 鍒嗗綍锛夈��
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class FinVoucherDto extends FinVoucher {
+
+    /**
+     * 鍑瘉鏄庣粏鍒嗗綍銆�
+     */
+    private List<FinVoucherEntryDto> entries;
+
+    private List<StorageBlobDTO> storageBlobDTOs;
+
+    private List<String> tempFileIds;
+
+}
diff --git a/src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherEntryDto.java b/src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherEntryDto.java
new file mode 100644
index 0000000..f722d79
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherEntryDto.java
@@ -0,0 +1,13 @@
+package com.ruoyi.account.bean.dto.financial;
+
+import com.ruoyi.account.pojo.financial.FinVoucherEntry;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 鍑瘉鍒嗗綍 DTO銆�
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class FinVoucherEntryDto extends FinVoucherEntry {
+}
diff --git a/src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherPageDto.java b/src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherPageDto.java
new file mode 100644
index 0000000..9955bcc
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherPageDto.java
@@ -0,0 +1,37 @@
+package com.ruoyi.account.bean.dto.financial;
+
+import lombok.Data;
+
+import java.time.LocalDate;
+
+/**
+ * 鍑瘉鍒嗛〉鏌ヨ鍙傛暟銆�
+ */
+@Data
+public class FinVoucherPageDto {
+
+    /**
+     * 鍑瘉瀛楀彿锛堟ā绯婂尮閰嶏級銆�
+     */
+    private String voucherNo;
+
+    /**
+     * 鍒跺崟浜恒��
+     */
+    private String creator;
+
+    /**
+     * 鍑瘉鐘舵�併��
+     */
+    private String status;
+
+    /**
+     * 寮�濮嬫棩鏈燂紙鍚級銆�
+     */
+    private LocalDate startDate;
+
+    /**
+     * 缁撴潫鏃ユ湡锛堝惈锛夈��
+     */
+    private LocalDate endDate;
+}
diff --git a/src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherStatusDto.java b/src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherStatusDto.java
new file mode 100644
index 0000000..47c0900
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/dto/financial/FinVoucherStatusDto.java
@@ -0,0 +1,15 @@
+package com.ruoyi.account.bean.dto.financial;
+
+import lombok.Data;
+
+/**
+ * 鍑瘉鐘舵�佸彉鏇村弬鏁般��
+ */
+@Data
+public class FinVoucherStatusDto {
+
+    /**
+     * 鍑瘉ID銆�
+     */
+    private Long id;
+}
diff --git a/src/main/java/com/ruoyi/account/bean/vo/financial/AccountSubjectVo.java b/src/main/java/com/ruoyi/account/bean/vo/financial/AccountSubjectVo.java
new file mode 100644
index 0000000..106eb17
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/vo/financial/AccountSubjectVo.java
@@ -0,0 +1,21 @@
+package com.ruoyi.account.bean.vo.financial;
+
+import com.ruoyi.account.pojo.financial.AccountSubject;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Data
+public class AccountSubjectVo extends AccountSubject {
+
+    /**
+     * 瀛愮鐩垪琛紙閫掑綊缁撴瀯锛夈��
+     */
+    private List<AccountSubjectVo> children = new ArrayList<>();
+
+    /**
+     * 鏄惁鍙跺瓙鑺傜偣銆�
+     */
+    private Boolean leaf;
+}
diff --git a/src/main/java/com/ruoyi/account/bean/vo/financial/FinLedgerEntryRecordVo.java b/src/main/java/com/ruoyi/account/bean/vo/financial/FinLedgerEntryRecordVo.java
new file mode 100644
index 0000000..846b350
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/vo/financial/FinLedgerEntryRecordVo.java
@@ -0,0 +1,43 @@
+package com.ruoyi.account.bean.vo.financial;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+/**
+ * 绉戠洰璐﹀熀纭�鍒嗗綍鏌ヨ瀵硅薄锛圫QL鏄犲皠浣跨敤锛夈��
+ */
+@Data
+public class FinLedgerEntryRecordVo {
+
+    /**
+     * 鍑瘉鏃ユ湡銆�
+     */
+    private LocalDate voucherDate;
+
+    /**
+     * 鍑瘉瀛楀彿銆�
+     */
+    private String voucherNo;
+
+    /**
+     * 鎽樿銆�
+     */
+    private String summary;
+
+    /**
+     * 鍊熸柟閲戦銆�
+     */
+    private BigDecimal debit;
+
+    /**
+     * 璐锋柟閲戦銆�
+     */
+    private BigDecimal credit;
+
+    /**
+     * 琛屽彿锛堟帓搴忓瓧娈碉級銆�
+     */
+    private Integer rowNo;
+}
diff --git a/src/main/java/com/ruoyi/account/bean/vo/financial/FinLedgerRowVo.java b/src/main/java/com/ruoyi/account/bean/vo/financial/FinLedgerRowVo.java
new file mode 100644
index 0000000..da4131f
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/vo/financial/FinLedgerRowVo.java
@@ -0,0 +1,55 @@
+package com.ruoyi.account.bean.vo.financial;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+/**
+ * 绉戠洰璐﹁鏁版嵁杩斿洖瀵硅薄銆�
+ */
+@Data
+@JsonInclude(JsonInclude.Include.NON_NULL)
+public class FinLedgerRowVo {
+
+    /**
+     * 琛岀被鍨嬶細opening/entry/monthly_total/yearly_total銆�
+     */
+    private String rowType;
+
+    /**
+     * 鏃ユ湡銆�
+     */
+    private LocalDate date;
+
+    /**
+     * 鍑瘉瀛楀彿銆�
+     */
+    private String voucherNo;
+
+    /**
+     * 鎽樿銆�
+     */
+    private String summary;
+
+    /**
+     * 鍊熸柟閲戦銆�
+     */
+    private BigDecimal debit;
+
+    /**
+     * 璐锋柟閲戦銆�
+     */
+    private BigDecimal credit;
+
+    /**
+     * 浣欓鏂瑰悜锛氬��/璐枫��
+     */
+    private String direction;
+
+    /**
+     * 浣欓锛堝�熸璐疯礋锛夈��
+     */
+    private BigDecimal balance;
+}
diff --git a/src/main/java/com/ruoyi/account/bean/vo/financial/FinVoucherDetailVo.java b/src/main/java/com/ruoyi/account/bean/vo/financial/FinVoucherDetailVo.java
new file mode 100644
index 0000000..024adc9
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/bean/vo/financial/FinVoucherDetailVo.java
@@ -0,0 +1,25 @@
+package com.ruoyi.account.bean.vo.financial;
+
+import com.ruoyi.account.pojo.financial.FinVoucher;
+import com.ruoyi.account.pojo.financial.FinVoucherEntry;
+import com.ruoyi.sales.pojo.CommonFile;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+/**
+ * 鍑瘉璇︽儏杩斿洖瀵硅薄銆�
+ */
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class FinVoucherDetailVo extends FinVoucher {
+
+    /**
+     * 鍑瘉鍒嗗綍鍒楄〃銆�
+     */
+    private List<FinVoucherEntry> entries;
+//    private List<StorageBlobVO> storageBlobVOList;
+
+    private List<CommonFile> SalesLedgerFiles;
+}
diff --git a/src/main/java/com/ruoyi/account/controller/AccountExpenseController.java b/src/main/java/com/ruoyi/account/controller/AccountExpenseController.java
index 23e2508..55e3cbb 100644
--- a/src/main/java/com/ruoyi/account/controller/AccountExpenseController.java
+++ b/src/main/java/com/ruoyi/account/controller/AccountExpenseController.java
@@ -1,12 +1,9 @@
 package com.ruoyi.account.controller;
 
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.account.dto.ReportDateDto;
-import com.ruoyi.account.mapper.AccountIncomeMapper;
+import com.ruoyi.account.bean.dto.ReportDateDto;
 import com.ruoyi.account.pojo.AccountExpense;
-import com.ruoyi.account.pojo.AccountIncome;
 import com.ruoyi.account.service.AccountExpenseService;
-import com.ruoyi.account.service.AccountFileService;
 import com.ruoyi.account.service.AccountIncomeService;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.dto.DateQueryDto;
diff --git a/src/main/java/com/ruoyi/account/controller/SalesReceiptReturnController.java b/src/main/java/com/ruoyi/account/controller/SalesReceiptReturnController.java
index 7f256ab..122b45c 100644
--- a/src/main/java/com/ruoyi/account/controller/SalesReceiptReturnController.java
+++ b/src/main/java/com/ruoyi/account/controller/SalesReceiptReturnController.java
@@ -1,10 +1,8 @@
 package com.ruoyi.account.controller;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.ruoyi.account.dto.SalesReceiptReturnDto;
-import com.ruoyi.account.pojo.SalesReceiptReturn;
+import com.ruoyi.account.bean.dto.SalesReceiptReturnDto;
 import com.ruoyi.account.service.SalesReceiptReturnService;
-import com.ruoyi.account.service.impl.SalesReceiptReturnServiceImpl;
 import com.ruoyi.framework.web.domain.R;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
diff --git a/src/main/java/com/ruoyi/account/controller/SalesRefundAmountOrderController.java b/src/main/java/com/ruoyi/account/controller/SalesRefundAmountOrderController.java
index daf35a4..9f754c8 100644
--- a/src/main/java/com/ruoyi/account/controller/SalesRefundAmountOrderController.java
+++ b/src/main/java/com/ruoyi/account/controller/SalesRefundAmountOrderController.java
@@ -1,17 +1,12 @@
 package com.ruoyi.account.controller;
 
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.account.dto.SalesRefundAmountOrderDto;
-import com.ruoyi.account.pojo.SalesRefundAmountOrder;
+import com.ruoyi.account.bean.dto.SalesRefundAmountOrderDto;
 import com.ruoyi.account.service.SalesRefundAmountOrderService;
 import com.ruoyi.framework.web.domain.R;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.stream.Collectors;
 
 /**
  * <p>
diff --git a/src/main/java/com/ruoyi/account/controller/financial/AccountSubjectController.java b/src/main/java/com/ruoyi/account/controller/financial/AccountSubjectController.java
new file mode 100644
index 0000000..b671905
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/controller/financial/AccountSubjectController.java
@@ -0,0 +1,70 @@
+package com.ruoyi.account.controller.financial;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.account.bean.dto.financial.AccountSubjectDto;
+import com.ruoyi.account.bean.vo.financial.AccountSubjectVo;
+import com.ruoyi.account.service.financial.AccountSubjectService;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.domain.R;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.Arrays;
+
+/**
+ * <p>
+ * 鎬昏处绉戠洰琛� 鍓嶇鎺у埗鍣�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-07 04:45:30
+ */
+@RestController
+@RequestMapping("/accountSubject")
+@RequiredArgsConstructor
+@Tag(name = "鎬昏处绉戠洰")
+public class AccountSubjectController {
+    private final AccountSubjectService accountSubjectService;
+
+    @GetMapping("/list")
+    @Log(title = "鎬昏处绉戠洰鏁版嵁闆嗗悎", businessType = BusinessType.OTHER)
+    @Operation(summary = "鎬昏处绉戠洰鏍戝舰鏌ヨ锛堥�掑綊锛�")
+    public R<IPage<AccountSubjectVo>> AccountSubjectDtoList(Page<AccountSubjectDto> page, AccountSubjectDto accountSubjectDto) {
+        IPage<AccountSubjectVo> paramList = accountSubjectService.baseList(page, accountSubjectDto);
+        return R.ok(paramList);
+    }
+
+    @PostMapping("/add")
+    @Log(title = "鏂板鎬昏处绉戠洰", businessType = BusinessType.INSERT)
+    @Operation(summary = "鏂板鎬昏处绉戠洰")
+    public R AccountSubjectDtoAdd(@RequestBody AccountSubjectDto accountSubjectDto) {
+        return R.ok(accountSubjectService.saveAccountSubject(accountSubjectDto));
+    }
+
+    @PutMapping("/edit")
+    @Log(title = "淇敼鎬昏处绉戠洰", businessType = BusinessType.UPDATE)
+    @Operation(summary = "淇敼鎬昏处绉戠洰")
+    public R AccountSubjectDtoEdit(@RequestBody AccountSubjectDto accountSubjectDto) {
+        return R.ok(accountSubjectService.updateAccountSubject(accountSubjectDto));
+    }
+
+    @DeleteMapping("/remove/{ids}")
+    @Log(title = "鍒犻櫎鎬昏处绉戠洰", businessType = BusinessType.DELETE)
+    @Operation(summary = "鍒犻櫎鎬昏处绉戠洰")
+    public R AccountSubjectDtooRemove(@PathVariable Long[] ids) {
+        return R.ok(accountSubjectService.removeAccountSubjectByIds(Arrays.asList(ids)));
+    }
+
+    @PostMapping("/export")
+    @Operation(summary = "瀵煎嚭鎬昏处绉戠洰鏂囦欢")
+    @Log(title = "瀵煎嚭鎬昏处绉戠洰鏂囦欢", businessType = BusinessType.EXPORT)
+    public void exportAccountSubject(HttpServletResponse response) {
+        accountSubjectService.exportAccountSubject(response);
+    }
+
+}
diff --git a/src/main/java/com/ruoyi/account/controller/financial/FinFixedAssetController.java b/src/main/java/com/ruoyi/account/controller/financial/FinFixedAssetController.java
new file mode 100644
index 0000000..a18c9da
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/controller/financial/FinFixedAssetController.java
@@ -0,0 +1,63 @@
+package com.ruoyi.account.controller.financial;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.account.bean.dto.financial.FinFixedAssetDto;
+import com.ruoyi.account.bean.dto.financial.FinIdBatchDto;
+import com.ruoyi.account.pojo.financial.FinFixedAsset;
+import com.ruoyi.account.service.financial.FinFixedAssetService;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.domain.R;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Arrays;
+
+/**
+ * 鍥哄畾璧勪骇鎺у埗鍣ㄣ��
+ */
+@RestController
+@RequestMapping("/financial/fixedAsset")
+@RequiredArgsConstructor
+@Tag(name = "璐㈠姟绠$悊-鍥哄畾璧勪骇")
+public class FinFixedAssetController {
+
+    private final FinFixedAssetService finFixedAssetService;
+
+    @GetMapping("/page")
+    @Operation(summary = "鍥哄畾璧勪骇鍒嗛〉鏌ヨ")
+    public R<IPage<FinFixedAsset>> page(Page<FinFixedAsset> page, FinFixedAssetDto queryDto) {
+        return R.ok(finFixedAssetService.pageList(page, queryDto));
+    }
+
+    @PostMapping("/add")
+    @Log(title = "鍥哄畾璧勪骇", businessType = BusinessType.INSERT)
+    @Operation(summary = "鏂板鍥哄畾璧勪骇")
+    public R<Boolean> add(@RequestBody FinFixedAssetDto dto) {
+        return R.ok(finFixedAssetService.add(dto));
+    }
+
+    @PutMapping("/update")
+    @Log(title = "鍥哄畾璧勪骇", businessType = BusinessType.UPDATE)
+    @Operation(summary = "淇敼鍥哄畾璧勪骇")
+    public R<Boolean> update(@RequestBody FinFixedAssetDto dto) {
+        return R.ok(finFixedAssetService.update(dto));
+    }
+
+    @DeleteMapping("/delete")
+    @Log(title = "鍥哄畾璧勪骇", businessType = BusinessType.DELETE)
+    @Operation(summary = "鍒犻櫎鍥哄畾璧勪骇")
+    public R<Boolean> delete(@RequestParam("ids") Long[] ids) {
+        return R.ok(finFixedAssetService.deleteByIds(Arrays.asList(ids)));
+    }
+
+    @PostMapping("/depreciate")
+    @Log(title = "鍥哄畾璧勪骇鎶樻棫璁℃彁", businessType = BusinessType.UPDATE)
+    @Operation(summary = "鍥哄畾璧勪骇鎸夋湀璁℃彁鎶樻棫")
+    public R depreciate(@RequestBody(required = false) FinIdBatchDto dto) {
+        return R.ok(finFixedAssetService.depreciate(dto == null ? null : dto.getIds()));
+    }
+}
diff --git a/src/main/java/com/ruoyi/account/controller/financial/FinIntangibleAssetController.java b/src/main/java/com/ruoyi/account/controller/financial/FinIntangibleAssetController.java
new file mode 100644
index 0000000..cb12f8c
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/controller/financial/FinIntangibleAssetController.java
@@ -0,0 +1,63 @@
+package com.ruoyi.account.controller.financial;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.account.bean.dto.financial.FinIdBatchDto;
+import com.ruoyi.account.bean.dto.financial.FinIntangibleAssetDto;
+import com.ruoyi.account.pojo.financial.FinIntangibleAsset;
+import com.ruoyi.account.service.financial.FinIntangibleAssetService;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.domain.R;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Arrays;
+
+/**
+ * 鏃犲舰璧勪骇鎺у埗鍣ㄣ��
+ */
+@RestController
+@RequestMapping("/financial/intangibleAsset")
+@RequiredArgsConstructor
+@Tag(name = "璐㈠姟绠$悊-鏃犲舰璧勪骇")
+public class FinIntangibleAssetController {
+
+    private final FinIntangibleAssetService finIntangibleAssetService;
+
+    @GetMapping("/page")
+    @Operation(summary = "鏃犲舰璧勪骇鍒嗛〉鏌ヨ")
+    public R<IPage<FinIntangibleAsset>> page(Page<FinIntangibleAsset> page, FinIntangibleAssetDto queryDto) {
+        return R.ok(finIntangibleAssetService.pageList(page, queryDto));
+    }
+
+    @PostMapping("/add")
+    @Log(title = "鏃犲舰璧勪骇", businessType = BusinessType.INSERT)
+    @Operation(summary = "鏂板鏃犲舰璧勪骇")
+    public R<Boolean> add(@RequestBody FinIntangibleAssetDto dto) {
+        return R.ok(finIntangibleAssetService.add(dto));
+    }
+
+    @PutMapping("/update")
+    @Log(title = "鏃犲舰璧勪骇", businessType = BusinessType.UPDATE)
+    @Operation(summary = "淇敼鏃犲舰璧勪骇")
+    public R<Boolean> update(@RequestBody FinIntangibleAssetDto dto) {
+        return R.ok(finIntangibleAssetService.update(dto));
+    }
+
+    @DeleteMapping("/delete")
+    @Log(title = "鏃犲舰璧勪骇", businessType = BusinessType.DELETE)
+    @Operation(summary = "鍒犻櫎鏃犲舰璧勪骇")
+    public R<Boolean> delete(@RequestParam("ids") Long[] ids) {
+        return R.ok(finIntangibleAssetService.deleteByIds(Arrays.asList(ids)));
+    }
+
+    @PostMapping("/amortize")
+    @Log(title = "鏃犲舰璧勪骇鎽婇攢璁℃彁", businessType = BusinessType.UPDATE)
+    @Operation(summary = "鏃犲舰璧勪骇鎸夋湀璁℃彁鎽婇攢")
+    public R amortize(@RequestBody(required = false) FinIdBatchDto dto) {
+        return R.ok(finIntangibleAssetService.amortize(dto == null ? null : dto.getIds()));
+    }
+}
diff --git a/src/main/java/com/ruoyi/account/controller/financial/FinLedgerController.java b/src/main/java/com/ruoyi/account/controller/financial/FinLedgerController.java
new file mode 100644
index 0000000..423030a
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/controller/financial/FinLedgerController.java
@@ -0,0 +1,39 @@
+package com.ruoyi.account.controller.financial;
+
+import com.ruoyi.account.bean.dto.financial.FinDetailLedgerQueryDto;
+import com.ruoyi.account.bean.dto.financial.FinLedgerQueryDto;
+import com.ruoyi.account.bean.vo.financial.FinLedgerRowVo;
+import com.ruoyi.account.service.financial.FinLedgerService;
+import com.ruoyi.framework.web.domain.R;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * 绉戠洰鎬昏处/鏄庣粏璐︽帶鍒跺櫒銆�
+ */
+@RestController
+@RequestMapping("/financial/ledger")
+@RequiredArgsConstructor
+@Tag(name = "璐㈠姟绠$悊-绉戠洰璐�")
+public class FinLedgerController {
+
+    private final FinLedgerService finLedgerService;
+
+    @GetMapping("/general")
+    @Operation(summary = "绉戠洰鎬昏处鏌ヨ")
+    public R<List<FinLedgerRowVo>> general(FinLedgerQueryDto queryDto) {
+        return R.ok(finLedgerService.queryGeneralLedger(queryDto));
+    }
+
+    @GetMapping("/detail")
+    @Operation(summary = "绉戠洰鏄庣粏璐︽煡璇�")
+    public R<List<FinLedgerRowVo>> detail(FinDetailLedgerQueryDto queryDto) {
+        return R.ok(finLedgerService.queryDetailLedger(queryDto));
+    }
+}
diff --git a/src/main/java/com/ruoyi/account/controller/financial/FinVoucherController.java b/src/main/java/com/ruoyi/account/controller/financial/FinVoucherController.java
new file mode 100644
index 0000000..beeaafa
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/controller/financial/FinVoucherController.java
@@ -0,0 +1,69 @@
+package com.ruoyi.account.controller.financial;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.account.bean.dto.financial.FinVoucherDto;
+import com.ruoyi.account.bean.dto.financial.FinVoucherPageDto;
+import com.ruoyi.account.bean.dto.financial.FinVoucherStatusDto;
+import com.ruoyi.account.bean.vo.financial.FinVoucherDetailVo;
+import com.ruoyi.account.pojo.financial.FinVoucher;
+import com.ruoyi.account.service.financial.FinVoucherService;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.domain.R;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 鍑瘉鎺у埗鍣ㄣ��
+ */
+@RestController
+@RequestMapping("/financial/voucher")
+@RequiredArgsConstructor
+@Tag(name = "璐㈠姟绠$悊-鍑瘉")
+public class FinVoucherController {
+
+    private final FinVoucherService finVoucherService;
+
+    @GetMapping("/page")
+    @Operation(summary = "鍑瘉鍒嗛〉鏌ヨ")
+    public R<IPage<FinVoucher>> page(Page<FinVoucher> page, FinVoucherPageDto queryDto) {
+        return R.ok(finVoucherService.pageList(page, queryDto));
+    }
+
+    @PostMapping("/add")
+    @Log(title = "鍑瘉", businessType = BusinessType.INSERT)
+    @Operation(summary = "鏂板鍑瘉")
+    public R<Boolean> add(@RequestBody FinVoucherDto dto) {
+        return R.ok(finVoucherService.addVoucher(dto));
+    }
+
+    @PutMapping("/update")
+    @Log(title = "鍑瘉", businessType = BusinessType.UPDATE)
+    @Operation(summary = "淇敼鍑瘉")
+    public R<Boolean> update(@RequestBody FinVoucherDto dto) {
+        return R.ok(finVoucherService.updateVoucher(dto));
+    }
+
+    @PostMapping("/post")
+    @Log(title = "鍑瘉杩囪处", businessType = BusinessType.UPDATE)
+    @Operation(summary = "鍑瘉杩囪处")
+    public R<Boolean> post(@RequestBody FinVoucherStatusDto dto) {
+        return R.ok(finVoucherService.postVoucher(dto.getId()));
+    }
+
+    @PostMapping("/cancel")
+    @Log(title = "鍑瘉浣滃簾", businessType = BusinessType.UPDATE)
+    @Operation(summary = "鍑瘉浣滃簾")
+    public R<Boolean> cancel(@RequestBody FinVoucherStatusDto dto) {
+        return R.ok(finVoucherService.cancelVoucher(dto.getId()));
+    }
+
+    @GetMapping("/detail/{id}")
+    @Operation(summary = "鍑瘉璇︽儏")
+    public R<FinVoucherDetailVo> detail(@PathVariable("id") Long id) {
+        return R.ok(finVoucherService.detail(id));
+    }
+}
diff --git a/src/main/java/com/ruoyi/account/mapper/AccountExpenseMapper.java b/src/main/java/com/ruoyi/account/mapper/AccountExpenseMapper.java
index 3cd3fc8..07f6c00 100644
--- a/src/main/java/com/ruoyi/account/mapper/AccountExpenseMapper.java
+++ b/src/main/java/com/ruoyi/account/mapper/AccountExpenseMapper.java
@@ -3,10 +3,8 @@
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.account.dto.AccountDto;
-import com.ruoyi.account.dto.AccountDto2;
+import com.ruoyi.account.bean.dto.AccountDto2;
 import com.ruoyi.account.pojo.AccountExpense;
-import com.ruoyi.account.pojo.AccountIncome;
 import com.ruoyi.dto.DateQueryDto;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
@@ -14,7 +12,6 @@
 
 import java.math.BigDecimal;
 import java.util.List;
-import java.util.Map;
 
 @Mapper
 public interface AccountExpenseMapper extends BaseMapper<AccountExpense> {
diff --git a/src/main/java/com/ruoyi/account/mapper/AccountIncomeMapper.java b/src/main/java/com/ruoyi/account/mapper/AccountIncomeMapper.java
index 9a9f951..fab7e2e 100644
--- a/src/main/java/com/ruoyi/account/mapper/AccountIncomeMapper.java
+++ b/src/main/java/com/ruoyi/account/mapper/AccountIncomeMapper.java
@@ -3,8 +3,7 @@
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.account.dto.AccountDto2;
-import com.ruoyi.account.pojo.AccountFile;
+import com.ruoyi.account.bean.dto.AccountDto2;
 import com.ruoyi.account.pojo.AccountIncome;
 import com.ruoyi.dto.DateQueryDto;
 import com.ruoyi.home.dto.IncomeExpenseAnalysisDto;
@@ -13,7 +12,6 @@
 
 import java.math.BigDecimal;
 import java.util.List;
-import java.util.Map;
 
 @Mapper
 public interface AccountIncomeMapper extends BaseMapper<AccountIncome> {
diff --git a/src/main/java/com/ruoyi/account/mapper/SalesRefundAmountOrderMapper.java b/src/main/java/com/ruoyi/account/mapper/SalesRefundAmountOrderMapper.java
index adfae86..e2b0761 100644
--- a/src/main/java/com/ruoyi/account/mapper/SalesRefundAmountOrderMapper.java
+++ b/src/main/java/com/ruoyi/account/mapper/SalesRefundAmountOrderMapper.java
@@ -2,7 +2,7 @@
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.account.dto.SalesRefundAmountOrderDto;
+import com.ruoyi.account.bean.dto.SalesRefundAmountOrderDto;
 import com.ruoyi.account.pojo.SalesRefundAmountOrder;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import org.apache.ibatis.annotations.Mapper;
diff --git a/src/main/java/com/ruoyi/account/mapper/financial/AccountSubjectMapper.java b/src/main/java/com/ruoyi/account/mapper/financial/AccountSubjectMapper.java
new file mode 100644
index 0000000..a5dd4fc
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/mapper/financial/AccountSubjectMapper.java
@@ -0,0 +1,23 @@
+package com.ruoyi.account.mapper.financial;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.account.pojo.financial.AccountSubject;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 鎬昏处绉戠洰琛� Mapper 鎺ュ彛
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-07 04:45:30
+ */
+@Mapper
+public interface AccountSubjectMapper extends BaseMapper<AccountSubject> {
+
+    Long countReferencedBySubjectCodes(@Param("subjectCodes") List<String> subjectCodes);
+
+}
diff --git a/src/main/java/com/ruoyi/account/mapper/financial/FinFixedAssetMapper.java b/src/main/java/com/ruoyi/account/mapper/financial/FinFixedAssetMapper.java
new file mode 100644
index 0000000..da2ae49
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/mapper/financial/FinFixedAssetMapper.java
@@ -0,0 +1,12 @@
+package com.ruoyi.account.mapper.financial;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.account.pojo.financial.FinFixedAsset;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 鍥哄畾璧勪骇 Mapper銆�
+ */
+@Mapper
+public interface FinFixedAssetMapper extends BaseMapper<FinFixedAsset> {
+}
diff --git a/src/main/java/com/ruoyi/account/mapper/financial/FinIntangibleAssetMapper.java b/src/main/java/com/ruoyi/account/mapper/financial/FinIntangibleAssetMapper.java
new file mode 100644
index 0000000..8a7bbb2
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/mapper/financial/FinIntangibleAssetMapper.java
@@ -0,0 +1,12 @@
+package com.ruoyi.account.mapper.financial;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.account.pojo.financial.FinIntangibleAsset;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 鏃犲舰璧勪骇 Mapper銆�
+ */
+@Mapper
+public interface FinIntangibleAssetMapper extends BaseMapper<FinIntangibleAsset> {
+}
diff --git a/src/main/java/com/ruoyi/account/mapper/financial/FinVoucherEntryMapper.java b/src/main/java/com/ruoyi/account/mapper/financial/FinVoucherEntryMapper.java
new file mode 100644
index 0000000..2fa2b73
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/mapper/financial/FinVoucherEntryMapper.java
@@ -0,0 +1,28 @@
+package com.ruoyi.account.mapper.financial;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.account.bean.vo.financial.FinLedgerEntryRecordVo;
+import com.ruoyi.account.pojo.financial.FinVoucherEntry;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.time.LocalDate;
+import java.util.List;
+
+/**
+ * 鍑瘉鍒嗗綍 Mapper銆�
+ */
+@Mapper
+public interface FinVoucherEntryMapper extends BaseMapper<FinVoucherEntry> {
+
+    List<FinLedgerEntryRecordVo> listPostedEntries(@Param("subjectCode") String subjectCode,
+                                                   @Param("startDate") LocalDate startDate,
+                                                   @Param("endDate") LocalDate endDate,
+                                                   @Param("auxiliaryType") String auxiliaryType,
+                                                   @Param("auxiliaryId") String auxiliaryId);
+
+    List<FinLedgerEntryRecordVo> listPostedEntriesBefore(@Param("subjectCode") String subjectCode,
+                                                         @Param("beforeDate") LocalDate beforeDate,
+                                                         @Param("auxiliaryType") String auxiliaryType,
+                                                         @Param("auxiliaryId") String auxiliaryId);
+}
diff --git a/src/main/java/com/ruoyi/account/mapper/financial/FinVoucherMapper.java b/src/main/java/com/ruoyi/account/mapper/financial/FinVoucherMapper.java
new file mode 100644
index 0000000..b528b7c
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/mapper/financial/FinVoucherMapper.java
@@ -0,0 +1,12 @@
+package com.ruoyi.account.mapper.financial;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.account.pojo.financial.FinVoucher;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 鍑瘉涓昏〃 Mapper銆�
+ */
+@Mapper
+public interface FinVoucherMapper extends BaseMapper<FinVoucher> {
+}
diff --git a/src/main/java/com/ruoyi/account/pojo/financial/AccountSubject.java b/src/main/java/com/ruoyi/account/pojo/financial/AccountSubject.java
new file mode 100644
index 0000000..8da5260
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/pojo/financial/AccountSubject.java
@@ -0,0 +1,113 @@
+package com.ruoyi.account.pojo.financial;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * <p>
+ * 鎬昏处绉戠洰琛�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-07 04:45:30
+ */
+@Getter
+@Setter
+@ToString
+@TableName("account_subject")
+@ApiModel(value = "AccountSubject瀵硅薄", description = "鎬昏处绉戠洰琛�")
+public class AccountSubject implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 涓婚敭ID
+     */
+    @ApiModelProperty("涓婚敭ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 鐖剁鐩甀D锛堜负绌鸿〃绀烘牴鑺傜偣锛�
+     */
+    @ApiModelProperty("鐖剁鐩甀D锛堜负绌鸿〃绀烘牴鑺傜偣锛�")
+    private Long parentId;
+
+    /**
+     * 绉戠洰缂栫爜(鍞竴鏍囪瘑)
+     */
+    @ApiModelProperty("绉戠洰缂栫爜(鍞竴鏍囪瘑)")
+    private String subjectCode;
+
+    /**
+     * 绉戠洰鍚嶇О
+     */
+    @ApiModelProperty("绉戠洰鍚嶇О")
+    private String subjectName;
+
+    /**
+     * 绉戠洰绫诲瀷
+     */
+    @ApiModelProperty("绉戠洰绫诲瀷")
+    private String subjectType;
+
+    /**
+     * 浣欓鏂瑰悜
+     */
+    @ApiModelProperty("浣欓鏂瑰悜")
+    private String balanceDirection;
+
+    /**
+     * 鐘舵�� 0鍚敤 1绂佺敤
+     */
+    @ApiModelProperty("鐘舵�� 0鍚敤 1绂佺敤")
+    private Integer status;
+
+    /**
+     * 澶囨敞
+     */
+    @ApiModelProperty("澶囨敞")
+    private String remark;
+
+    /**
+     * 鍒涘缓浜�
+     */
+    @ApiModelProperty("鍒涘缓浜�")
+    @TableField(fill = FieldFill.INSERT)
+    private Integer createUser;
+
+    /**
+     * 鍒涘缓鏃堕棿
+     */
+    @ApiModelProperty("鍒涘缓鏃堕棿")
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    /**
+     * 淇敼浜�
+     */
+    @ApiModelProperty("淇敼浜�")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Integer updateUser;
+
+    /**
+     * 淇敼鏃堕棿
+     */
+    @ApiModelProperty("淇敼鏃堕棿")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+
+    /**
+     * 閮ㄩ棬ID
+     */
+    @ApiModelProperty("閮ㄩ棬ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+}
diff --git a/src/main/java/com/ruoyi/account/pojo/financial/FinFixedAsset.java b/src/main/java/com/ruoyi/account/pojo/financial/FinFixedAsset.java
new file mode 100644
index 0000000..971e6c3
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/pojo/financial/FinFixedAsset.java
@@ -0,0 +1,96 @@
+package com.ruoyi.account.pojo.financial;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+/**
+ * 鍥哄畾璧勪骇瀹炰綋銆�
+ */
+@Getter
+@Setter
+@ToString
+@TableName("fin_fixed_asset")
+@ApiModel(value = "FinFixedAsset瀵硅薄", description = "鍥哄畾璧勪骇")
+public class FinFixedAsset implements Serializable {
+
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("涓婚敭ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty("璧勪骇缂栧彿")
+    private String assetCode;
+
+    @ApiModelProperty("璧勪骇鍚嶇О")
+    private String assetName;
+
+    @ApiModelProperty("璧勪骇绫诲埆")
+    private String category;
+
+    @ApiModelProperty("瑙勬牸鍨嬪彿")
+    private String specification;
+
+    @ApiModelProperty("璐疆鏃ユ湡")
+    private LocalDate purchaseDate;
+
+    @ApiModelProperty("璧勪骇鍘熷��")
+    private BigDecimal originalValue;
+
+    @ApiModelProperty("浣跨敤骞撮檺(骞�)")
+    private Integer usefulLife;
+
+    @ApiModelProperty("娈嬪�肩巼(%)")
+    private BigDecimal residualRate;
+
+    @ApiModelProperty("绱鎶樻棫")
+    private BigDecimal accumulatedDepreciation;
+
+    @ApiModelProperty("鍑�鍊�")
+    private BigDecimal netValue;
+
+    @ApiModelProperty("瀛樻斁鍦扮偣")
+    private String location;
+
+    @ApiModelProperty("浣跨敤閮ㄩ棬")
+    private String department;
+
+    @ApiModelProperty("淇濈浜�")
+    private String keeper;
+
+    @ApiModelProperty("鐘舵��")
+    private String status;
+
+    @ApiModelProperty("澶囨敞")
+    private String remark;
+
+    @ApiModelProperty("鍒涘缓浜�")
+    @TableField(fill = FieldFill.INSERT)
+    private String createUser;
+
+    @ApiModelProperty("鍒涘缓鏃堕棿")
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("淇敼浜�")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private String updateUser;
+
+    @ApiModelProperty("淇敼鏃堕棿")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+
+    @ApiModelProperty("閮ㄩ棬ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+}
diff --git a/src/main/java/com/ruoyi/account/pojo/financial/FinIntangibleAsset.java b/src/main/java/com/ruoyi/account/pojo/financial/FinIntangibleAsset.java
new file mode 100644
index 0000000..4bb07a3
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/pojo/financial/FinIntangibleAsset.java
@@ -0,0 +1,92 @@
+package com.ruoyi.account.pojo.financial;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+/**
+ * 鏃犲舰璧勪骇瀹炰綋銆�
+ */
+@Getter
+@Setter
+@ToString
+@TableName("fin_intangible_asset")
+@ApiModel(value = "FinIntangibleAsset瀵硅薄", description = "鏃犲舰璧勪骇")
+public class FinIntangibleAsset implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("涓婚敭ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty("璧勪骇缂栧彿")
+    private String assetCode;
+
+    @ApiModelProperty("璧勪骇鍚嶇О")
+    private String assetName;
+
+    @ApiModelProperty("璧勪骇绫诲埆")
+    private String category;
+
+    @ApiModelProperty("璇佷功缂栧彿")
+    private String certificateNo;
+
+    @ApiModelProperty("鍙栧緱鏃ユ湡")
+    private LocalDate acquisitionDate;
+
+    @ApiModelProperty("璧勪骇鍘熷��")
+    private BigDecimal originalValue;
+
+    @ApiModelProperty("鎽婇攢骞撮檺(骞�)")
+    private Integer amortizationPeriod;
+
+    @ApiModelProperty("娈嬪�肩巼(%)")
+    private BigDecimal residualRate;
+
+    @ApiModelProperty("绱鎽婇攢")
+    private BigDecimal accumulatedAmortization;
+
+    @ApiModelProperty("鍑�鍊�")
+    private BigDecimal netValue;
+
+    @ApiModelProperty("鏈夋晥鏈熻嚦")
+    private LocalDate validityDate;
+
+    @ApiModelProperty("鐘舵��")
+    private String status;
+
+    @ApiModelProperty("璧勪骇鎻忚堪")
+    private String description;
+
+    @ApiModelProperty("澶囨敞")
+    private String remark;
+
+    @ApiModelProperty("鍒涘缓浜�")
+    @TableField(fill = FieldFill.INSERT)
+    private String createUser;
+
+    @ApiModelProperty("鍒涘缓鏃堕棿")
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("淇敼浜�")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private String updateUser;
+
+    @ApiModelProperty("淇敼鏃堕棿")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+
+    @ApiModelProperty("閮ㄩ棬ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+}
diff --git a/src/main/java/com/ruoyi/account/pojo/financial/FinVoucher.java b/src/main/java/com/ruoyi/account/pojo/financial/FinVoucher.java
new file mode 100644
index 0000000..ea1392d
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/pojo/financial/FinVoucher.java
@@ -0,0 +1,77 @@
+package com.ruoyi.account.pojo.financial;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+/**
+ * 鍑瘉涓昏〃瀹炰綋銆�
+ */
+@Getter
+@Setter
+@ToString
+@TableName("fin_voucher")
+@ApiModel(value = "FinVoucher瀵硅薄", description = "鍑瘉涓昏〃")
+public class FinVoucher implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("涓婚敭ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty("鍑瘉瀛楀彿")
+    private String voucherNo;
+
+    @ApiModelProperty("鍑瘉鏃ユ湡")
+    private LocalDate voucherDate;
+
+    @ApiModelProperty("鎽樿")
+    private String summary;
+
+    @ApiModelProperty("鍊熸柟鍚堣")
+    private BigDecimal debit;
+
+    @ApiModelProperty("璐锋柟鍚堣")
+    private BigDecimal credit;
+
+    @ApiModelProperty("鍒跺崟浜�")
+    private String creator;
+
+    @ApiModelProperty("鐘舵��: unposted/posted/cancelled")
+    private String status;
+
+    @ApiModelProperty("闄勪欢鏁伴噺")
+    private Integer attachmentCount;
+
+    @ApiModelProperty("澶囨敞")
+    private String remark;
+
+    @ApiModelProperty("鍒涘缓浜�")
+    @TableField(fill = FieldFill.INSERT)
+    private String createUser;
+
+    @ApiModelProperty("鍒涘缓鏃堕棿")
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("淇敼浜�")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private String updateUser;
+
+    @ApiModelProperty("淇敼鏃堕棿")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+
+    @ApiModelProperty("閮ㄩ棬ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+}
diff --git a/src/main/java/com/ruoyi/account/pojo/financial/FinVoucherEntry.java b/src/main/java/com/ruoyi/account/pojo/financial/FinVoucherEntry.java
new file mode 100644
index 0000000..46cab2e
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/pojo/financial/FinVoucherEntry.java
@@ -0,0 +1,79 @@
+package com.ruoyi.account.pojo.financial;
+
+import com.baomidou.mybatisplus.annotation.*;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 鍑瘉鍒嗗綍瀹炰綋銆�
+ */
+@Getter
+@Setter
+@ToString
+@TableName("fin_voucher_entry")
+@ApiModel(value = "FinVoucherEntry瀵硅薄", description = "鍑瘉鍒嗗綍")
+public class FinVoucherEntry implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty("涓婚敭ID")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    @ApiModelProperty("鍑瘉ID")
+    private Long voucherId;
+
+    @ApiModelProperty("琛屽彿")
+    private Integer rowNo;
+
+    @ApiModelProperty("绉戠洰缂栫爜")
+    private String subjectCode;
+
+    @ApiModelProperty("绉戠洰鍚嶇О")
+    private String subjectName;
+
+    @ApiModelProperty("鎽樿")
+    private String summary;
+
+    @ApiModelProperty("鍊熸柟閲戦")
+    private BigDecimal debit;
+
+    @ApiModelProperty("璐锋柟閲戦")
+    private BigDecimal credit;
+
+    @ApiModelProperty("杈呭姪鏍哥畻绫诲瀷")
+    private String auxiliaryType;
+
+    @ApiModelProperty("杈呭姪鏍哥畻瀵硅薄ID")
+    private String auxiliaryId;
+
+    @ApiModelProperty("杈呭姪鏍哥畻瀵硅薄鍚嶇О")
+    private String auxiliaryName;
+
+    @ApiModelProperty("鍒涘缓浜�")
+    @TableField(fill = FieldFill.INSERT)
+    private String createUser;
+
+    @ApiModelProperty("鍒涘缓鏃堕棿")
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    @ApiModelProperty("淇敼浜�")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private String updateUser;
+
+    @ApiModelProperty("淇敼鏃堕棿")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+
+    @ApiModelProperty("閮ㄩ棬ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+}
diff --git a/src/main/java/com/ruoyi/account/service/AccountExpenseService.java b/src/main/java/com/ruoyi/account/service/AccountExpenseService.java
index ac3e0b2..07b129b 100644
--- a/src/main/java/com/ruoyi/account/service/AccountExpenseService.java
+++ b/src/main/java/com/ruoyi/account/service/AccountExpenseService.java
@@ -3,12 +3,10 @@
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
-import com.ruoyi.account.dto.AccountDto;
-import com.ruoyi.account.dto.AccountDto2;
-import com.ruoyi.account.dto.AccountDto3;
-import com.ruoyi.account.dto.ReportDateDto;
+import com.ruoyi.account.bean.dto.AccountDto;
+import com.ruoyi.account.bean.dto.AccountDto3;
+import com.ruoyi.account.bean.dto.ReportDateDto;
 import com.ruoyi.account.pojo.AccountExpense;
-import com.ruoyi.account.pojo.AccountIncome;
 import com.ruoyi.dto.DateQueryDto;
 
 import javax.servlet.http.HttpServletResponse;
diff --git a/src/main/java/com/ruoyi/account/service/AccountIncomeService.java b/src/main/java/com/ruoyi/account/service/AccountIncomeService.java
index 1004f28..207415f 100644
--- a/src/main/java/com/ruoyi/account/service/AccountIncomeService.java
+++ b/src/main/java/com/ruoyi/account/service/AccountIncomeService.java
@@ -3,9 +3,8 @@
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
-import com.ruoyi.account.dto.AccountDto2;
-import com.ruoyi.account.dto.AccountDto3;
-import com.ruoyi.account.dto.ReportDateDto;
+import com.ruoyi.account.bean.dto.AccountDto3;
+import com.ruoyi.account.bean.dto.ReportDateDto;
 import com.ruoyi.account.pojo.AccountIncome;
 
 import javax.servlet.http.HttpServletResponse;
diff --git a/src/main/java/com/ruoyi/account/service/SalesReceiptReturnService.java b/src/main/java/com/ruoyi/account/service/SalesReceiptReturnService.java
index 77c6aa9..456415d 100644
--- a/src/main/java/com/ruoyi/account/service/SalesReceiptReturnService.java
+++ b/src/main/java/com/ruoyi/account/service/SalesReceiptReturnService.java
@@ -1,7 +1,7 @@
 package com.ruoyi.account.service;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.ruoyi.account.dto.SalesReceiptReturnDto;
+import com.ruoyi.account.bean.dto.SalesReceiptReturnDto;
 import com.ruoyi.account.pojo.SalesReceiptReturn;
 import com.baomidou.mybatisplus.extension.service.IService;
 
diff --git a/src/main/java/com/ruoyi/account/service/SalesRefundAmountOrderService.java b/src/main/java/com/ruoyi/account/service/SalesRefundAmountOrderService.java
index bf45755..4465913 100644
--- a/src/main/java/com/ruoyi/account/service/SalesRefundAmountOrderService.java
+++ b/src/main/java/com/ruoyi/account/service/SalesRefundAmountOrderService.java
@@ -2,7 +2,7 @@
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.account.dto.SalesRefundAmountOrderDto;
+import com.ruoyi.account.bean.dto.SalesRefundAmountOrderDto;
 import com.ruoyi.account.pojo.SalesRefundAmountOrder;
 import com.baomidou.mybatisplus.extension.service.IService;
 
diff --git a/src/main/java/com/ruoyi/account/service/financial/AccountSubjectService.java b/src/main/java/com/ruoyi/account/service/financial/AccountSubjectService.java
new file mode 100644
index 0000000..16f5601
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/service/financial/AccountSubjectService.java
@@ -0,0 +1,32 @@
+package com.ruoyi.account.service.financial;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.account.bean.dto.financial.AccountSubjectDto;
+import com.ruoyi.account.bean.vo.financial.AccountSubjectVo;
+import com.ruoyi.account.pojo.financial.AccountSubject;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * <p>
+ * 鎬昏处绉戠洰琛� 鏈嶅姟绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-07 04:45:30
+ */
+public interface AccountSubjectService extends IService<AccountSubject> {
+
+    IPage<AccountSubjectVo> baseList(Page<AccountSubjectDto> page, AccountSubjectDto accountSubjectDto);
+
+    Boolean saveAccountSubject(AccountSubjectDto accountSubjectDto);
+
+    Boolean updateAccountSubject(AccountSubjectDto accountSubjectDto);
+
+    Boolean removeAccountSubjectByIds(List<Long> ids);
+
+    void exportAccountSubject(HttpServletResponse response);
+}
diff --git a/src/main/java/com/ruoyi/account/service/financial/FinFixedAssetService.java b/src/main/java/com/ruoyi/account/service/financial/FinFixedAssetService.java
new file mode 100644
index 0000000..0b2c1cc
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/service/financial/FinFixedAssetService.java
@@ -0,0 +1,26 @@
+package com.ruoyi.account.service.financial;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.account.bean.dto.financial.FinFixedAssetDto;
+import com.ruoyi.account.pojo.financial.FinFixedAsset;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 鍥哄畾璧勪骇鏈嶅姟銆�
+ */
+public interface FinFixedAssetService extends IService<FinFixedAsset> {
+
+    IPage<FinFixedAsset> pageList(Page<FinFixedAsset> page, FinFixedAssetDto queryDto);
+
+    Boolean add(FinFixedAssetDto dto);
+
+    Boolean update(FinFixedAssetDto dto);
+
+    Boolean deleteByIds(List<Long> ids);
+
+    Map<String, Object> depreciate(List<Long> ids);
+}
diff --git a/src/main/java/com/ruoyi/account/service/financial/FinIntangibleAssetService.java b/src/main/java/com/ruoyi/account/service/financial/FinIntangibleAssetService.java
new file mode 100644
index 0000000..46d946a
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/service/financial/FinIntangibleAssetService.java
@@ -0,0 +1,26 @@
+package com.ruoyi.account.service.financial;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.account.bean.dto.financial.FinIntangibleAssetDto;
+import com.ruoyi.account.pojo.financial.FinIntangibleAsset;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 鏃犲舰璧勪骇鏈嶅姟銆�
+ */
+public interface FinIntangibleAssetService extends IService<FinIntangibleAsset> {
+
+    IPage<FinIntangibleAsset> pageList(Page<FinIntangibleAsset> page, FinIntangibleAssetDto queryDto);
+
+    Boolean add(FinIntangibleAssetDto dto);
+
+    Boolean update(FinIntangibleAssetDto dto);
+
+    Boolean deleteByIds(List<Long> ids);
+
+    Map<String, Object> amortize(List<Long> ids);
+}
diff --git a/src/main/java/com/ruoyi/account/service/financial/FinLedgerService.java b/src/main/java/com/ruoyi/account/service/financial/FinLedgerService.java
new file mode 100644
index 0000000..c4e9fa4
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/service/financial/FinLedgerService.java
@@ -0,0 +1,17 @@
+package com.ruoyi.account.service.financial;
+
+import com.ruoyi.account.bean.dto.financial.FinDetailLedgerQueryDto;
+import com.ruoyi.account.bean.dto.financial.FinLedgerQueryDto;
+import com.ruoyi.account.bean.vo.financial.FinLedgerRowVo;
+
+import java.util.List;
+
+/**
+ * 绉戠洰璐︽湇鍔°��
+ */
+public interface FinLedgerService {
+
+    List<FinLedgerRowVo> queryGeneralLedger(FinLedgerQueryDto queryDto);
+
+    List<FinLedgerRowVo> queryDetailLedger(FinDetailLedgerQueryDto queryDto);
+}
diff --git a/src/main/java/com/ruoyi/account/service/financial/FinVoucherService.java b/src/main/java/com/ruoyi/account/service/financial/FinVoucherService.java
new file mode 100644
index 0000000..078bac4
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/service/financial/FinVoucherService.java
@@ -0,0 +1,27 @@
+package com.ruoyi.account.service.financial;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.account.bean.dto.financial.FinVoucherDto;
+import com.ruoyi.account.bean.dto.financial.FinVoucherPageDto;
+import com.ruoyi.account.bean.vo.financial.FinVoucherDetailVo;
+import com.ruoyi.account.pojo.financial.FinVoucher;
+
+/**
+ * 鍑瘉鏈嶅姟銆�
+ */
+public interface FinVoucherService extends IService<FinVoucher> {
+
+    IPage<FinVoucher> pageList(Page<FinVoucher> page, FinVoucherPageDto queryDto);
+
+    Boolean addVoucher(FinVoucherDto dto);
+
+    Boolean updateVoucher(FinVoucherDto dto);
+
+    Boolean postVoucher(Long id);
+
+    Boolean cancelVoucher(Long id);
+
+    FinVoucherDetailVo detail(Long id);
+}
diff --git a/src/main/java/com/ruoyi/account/service/impl/AccountExpenseServiceImpl.java b/src/main/java/com/ruoyi/account/service/impl/AccountExpenseServiceImpl.java
index b7791a9..82cf64f 100644
--- a/src/main/java/com/ruoyi/account/service/impl/AccountExpenseServiceImpl.java
+++ b/src/main/java/com/ruoyi/account/service/impl/AccountExpenseServiceImpl.java
@@ -5,10 +5,10 @@
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.ruoyi.account.dto.AccountDto;
-import com.ruoyi.account.dto.AccountDto2;
-import com.ruoyi.account.dto.AccountDto3;
-import com.ruoyi.account.dto.ReportDateDto;
+import com.ruoyi.account.bean.dto.AccountDto;
+import com.ruoyi.account.bean.dto.AccountDto2;
+import com.ruoyi.account.bean.dto.AccountDto3;
+import com.ruoyi.account.bean.dto.ReportDateDto;
 import com.ruoyi.account.mapper.AccountExpenseMapper;
 import com.ruoyi.account.mapper.AccountIncomeMapper;
 import com.ruoyi.account.pojo.AccountExpense;
@@ -23,7 +23,6 @@
 
 import javax.servlet.http.HttpServletResponse;
 import java.math.BigDecimal;
-import java.time.DayOfWeek;
 import java.time.LocalDate;
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
diff --git a/src/main/java/com/ruoyi/account/service/impl/AccountIncomeServiceImpl.java b/src/main/java/com/ruoyi/account/service/impl/AccountIncomeServiceImpl.java
index 4a83ecb..706a560 100644
--- a/src/main/java/com/ruoyi/account/service/impl/AccountIncomeServiceImpl.java
+++ b/src/main/java/com/ruoyi/account/service/impl/AccountIncomeServiceImpl.java
@@ -5,13 +5,11 @@
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.ruoyi.account.dto.AccountDto2;
-import com.ruoyi.account.dto.AccountDto3;
-import com.ruoyi.account.dto.ReportDateDto;
+import com.ruoyi.account.bean.dto.AccountDto3;
+import com.ruoyi.account.bean.dto.ReportDateDto;
 import com.ruoyi.account.mapper.AccountIncomeMapper;
 import com.ruoyi.account.pojo.AccountIncome;
 import com.ruoyi.account.service.AccountIncomeService;
-import com.ruoyi.common.utils.DateUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.dto.DateQueryDto;
 import com.ruoyi.project.system.domain.SysDictData;
@@ -24,9 +22,7 @@
 import java.time.LocalDate;
 import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 @AllArgsConstructor
 @Service
diff --git a/src/main/java/com/ruoyi/account/service/impl/AccountingServiceImpl.java b/src/main/java/com/ruoyi/account/service/impl/AccountingServiceImpl.java
index e9a586f..41723a1 100644
--- a/src/main/java/com/ruoyi/account/service/impl/AccountingServiceImpl.java
+++ b/src/main/java/com/ruoyi/account/service/impl/AccountingServiceImpl.java
@@ -4,8 +4,8 @@
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.account.dto.DeviceTypeDetail;
-import com.ruoyi.account.dto.DeviceTypeDistributionVO;
+import com.ruoyi.account.bean.dto.DeviceTypeDetail;
+import com.ruoyi.account.bean.dto.DeviceTypeDistributionVO;
 import com.ruoyi.account.mapper.BorrowInfoMapper;
 import com.ruoyi.account.pojo.BorrowInfo;
 import com.ruoyi.device.mapper.DeviceLedgerMapper;
@@ -17,8 +17,6 @@
 import com.ruoyi.procurementrecord.pojo.CustomStorage;
 import com.ruoyi.procurementrecord.pojo.ProcurementRecordOut;
 import com.ruoyi.procurementrecord.pojo.ProcurementRecordStorage;
-import com.ruoyi.procurementrecord.service.impl.ProcurementRecordOutServiceImpl;
-import com.ruoyi.procurementrecord.service.impl.ProcurementRecordServiceImpl;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
diff --git a/src/main/java/com/ruoyi/account/service/impl/SalesReceiptReturnServiceImpl.java b/src/main/java/com/ruoyi/account/service/impl/SalesReceiptReturnServiceImpl.java
index 3aa201e..c39697f 100644
--- a/src/main/java/com/ruoyi/account/service/impl/SalesReceiptReturnServiceImpl.java
+++ b/src/main/java/com/ruoyi/account/service/impl/SalesReceiptReturnServiceImpl.java
@@ -1,7 +1,7 @@
 package com.ruoyi.account.service.impl;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.ruoyi.account.dto.SalesReceiptReturnDto;
+import com.ruoyi.account.bean.dto.SalesReceiptReturnDto;
 import com.ruoyi.account.pojo.SalesReceiptReturn;
 import com.ruoyi.account.mapper.SalesReceiptReturnMapper;
 import com.ruoyi.account.service.SalesReceiptReturnService;
diff --git a/src/main/java/com/ruoyi/account/service/impl/SalesRefundAmountOrderServiceImpl.java b/src/main/java/com/ruoyi/account/service/impl/SalesRefundAmountOrderServiceImpl.java
index c04fdb5..93ffcc6 100644
--- a/src/main/java/com/ruoyi/account/service/impl/SalesRefundAmountOrderServiceImpl.java
+++ b/src/main/java/com/ruoyi/account/service/impl/SalesRefundAmountOrderServiceImpl.java
@@ -2,7 +2,7 @@
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.account.dto.SalesRefundAmountOrderDto;
+import com.ruoyi.account.bean.dto.SalesRefundAmountOrderDto;
 import com.ruoyi.account.pojo.SalesRefundAmountOrder;
 import com.ruoyi.account.mapper.SalesRefundAmountOrderMapper;
 import com.ruoyi.account.service.SalesRefundAmountOrderService;
diff --git a/src/main/java/com/ruoyi/account/service/impl/financial/AccountSubjectServiceImpl.java b/src/main/java/com/ruoyi/account/service/impl/financial/AccountSubjectServiceImpl.java
new file mode 100644
index 0000000..f868579
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/service/impl/financial/AccountSubjectServiceImpl.java
@@ -0,0 +1,387 @@
+package com.ruoyi.account.service.impl.financial;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.account.bean.dto.financial.AccountSubjectDto;
+import com.ruoyi.account.bean.dto.financial.AccountSubjectImportDto;
+import com.ruoyi.account.bean.vo.financial.AccountSubjectVo;
+import com.ruoyi.account.mapper.financial.AccountSubjectMapper;
+import com.ruoyi.account.pojo.financial.AccountSubject;
+import com.ruoyi.account.service.financial.AccountSubjectService;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+
+import javax.servlet.http.HttpServletResponse;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * <p>
+ * 鎬昏处绉戠洰琛� 鏈嶅姟瀹炵幇绫�
+ * </p>
+ *
+ * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
+ * @since 2026-05-07 04:45:30
+ */
+@Service
+@RequiredArgsConstructor
+public class AccountSubjectServiceImpl extends ServiceImpl<AccountSubjectMapper, AccountSubject> implements AccountSubjectService {
+
+    private final AccountSubjectMapper accountSubjectMapper;
+
+    @Override
+    public IPage<AccountSubjectVo> baseList(Page<AccountSubjectDto> page, AccountSubjectDto accountSubjectDto) {
+        Page<AccountSubjectDto> requestPage = page == null ? new Page<>(1, 10) : page;
+        List<AccountSubject> allSubjects = list(loadBaseQueryWrapper(accountSubjectDto));
+        List<AccountSubject> filteredSubjects = applyTreeFilter(allSubjects, accountSubjectDto);
+        List<AccountSubjectVo> fullTree = buildTree(filteredSubjects);
+
+        long current = requestPage.getCurrent() <= 0 ? 1 : requestPage.getCurrent();
+        long size = requestPage.getSize() <= 0 ? 10 : requestPage.getSize();
+        int fromIndex = (int) Math.min((current - 1) * size, fullTree.size());
+        int toIndex = (int) Math.min(fromIndex + size, fullTree.size());
+        List<AccountSubjectVo> pagedRoots = fromIndex >= toIndex
+                ? Collections.emptyList()
+                : fullTree.subList(fromIndex, toIndex);
+
+        Page<AccountSubjectVo> resultPage = new Page<>(current, size, fullTree.size());
+        resultPage.setRecords(pagedRoots);
+        return resultPage;
+    }
+
+    @Override
+    public Boolean saveAccountSubject(AccountSubjectDto accountSubjectDto) {
+        validateSubjectRequiredFields(accountSubjectDto);
+        validateSubjectCodeUnique(accountSubjectDto, false);
+        validateParent(accountSubjectDto.getParentId(), null);
+        if (accountSubjectDto.getStatus() == null) {
+            accountSubjectDto.setStatus(0);
+        }
+        return save(accountSubjectDto);
+    }
+
+    @Override
+    public Boolean updateAccountSubject(AccountSubjectDto accountSubjectDto) {
+        if (accountSubjectDto == null || accountSubjectDto.getId() == null) {
+            throw new ServiceException("淇敼澶辫触锛岀鐩甀D涓嶈兘涓虹┖");
+        }
+        if (getById(accountSubjectDto.getId()) == null) {
+            throw new ServiceException("淇敼澶辫触锛屾湭鎵惧埌瀵瑰簲绉戠洰");
+        }
+        validateParent(accountSubjectDto.getParentId(), accountSubjectDto.getId());
+        validateSubjectRequiredFields(accountSubjectDto);
+        validateSubjectCodeUnique(accountSubjectDto, true);
+        return updateById(accountSubjectDto);
+    }
+
+    @Override
+    public Boolean removeAccountSubjectByIds(List<Long> ids) {
+        if (ids == null || ids.isEmpty()) {
+            return true;
+        }
+        List<AccountSubject> allSubjects = list();
+        if (allSubjects == null || allSubjects.isEmpty()) {
+            return true;
+        }
+        Map<Long, List<Long>> childrenIdMap = buildChildrenIdMap(allSubjects);
+        Set<Long> removeIds = new LinkedHashSet<>();
+        for (Long id : ids) {
+            collectDescendantIds(id, childrenIdMap, removeIds);
+        }
+        if (removeIds.isEmpty()) {
+            return true;
+        }
+        List<String> subjectCodes = allSubjects.stream()
+                .filter(subject -> removeIds.contains(subject.getId()))
+                .map(AccountSubject::getSubjectCode)
+                .filter(StringUtils::isNotEmpty)
+                .collect(Collectors.toList());
+        if (!subjectCodes.isEmpty()) {
+            Long referencedCount = accountSubjectMapper.countReferencedBySubjectCodes(subjectCodes);
+            if (referencedCount != null && referencedCount > 0) {
+                throw new ServiceException("鍒犻櫎澶辫触锛岀鐩凡琚嚟璇佸垎褰曞紩鐢�");
+            }
+        }
+        return removeByIds(removeIds);
+    }
+
+    @Override
+    public void exportAccountSubject(HttpServletResponse response) {
+        List<AccountSubject> list = accountSubjectMapper.selectList(null);
+        List<AccountSubjectImportDto> importDtos = list.stream().map(accountSubject -> {
+            AccountSubjectImportDto accountSubjectImportDto = new AccountSubjectImportDto();
+            BeanUtils.copyProperties(accountSubject, accountSubjectImportDto);
+            return accountSubjectImportDto;
+        }).collect(Collectors.toList());
+        ExcelUtil<AccountSubjectImportDto> util = new ExcelUtil<>(AccountSubjectImportDto.class);
+        util.exportExcel(response, importDtos , "鎬昏处绉戠洰");
+    }
+
+    /**
+     * 鏍¢獙绉戠洰蹇呭~瀛楁锛岄伩鍏嶈剰鏁版嵁鍐欏叆銆�
+     */
+    private void validateSubjectRequiredFields(AccountSubjectDto accountSubjectDto) {
+        if (accountSubjectDto == null) {
+            throw new ServiceException("鎬昏处绉戠洰鏁版嵁涓嶈兘涓虹┖");
+        }
+        if (StringUtils.isEmpty(accountSubjectDto.getSubjectCode())) {
+            throw new ServiceException("绉戠洰缂栫爜涓嶈兘涓虹┖");
+        }
+        if (StringUtils.isEmpty(accountSubjectDto.getSubjectName())) {
+            throw new ServiceException("绉戠洰鍚嶇О涓嶈兘涓虹┖");
+        }
+        if (StringUtils.isEmpty(accountSubjectDto.getSubjectType())) {
+            throw new ServiceException("绉戠洰绫诲瀷涓嶈兘涓虹┖");
+        }
+    }
+
+    /**
+     * 鏍¢獙绉戠洰缂栫爜鍞竴锛屾柊澧炲拰淇敼閮借鎵ц銆�
+     */
+    private void validateSubjectCodeUnique(AccountSubjectDto accountSubjectDto, boolean isUpdate) {
+        LambdaQueryWrapper<AccountSubject> codeQueryWrapper = new LambdaQueryWrapper<>();
+        codeQueryWrapper.eq(AccountSubject::getSubjectCode, accountSubjectDto.getSubjectCode());
+        if (isUpdate) {
+            codeQueryWrapper.ne(AccountSubject::getId, accountSubjectDto.getId());
+        }
+        AccountSubject exists = getOne(codeQueryWrapper, false);
+        if (Objects.nonNull(exists)) {
+            throw new ServiceException("绉戠洰缂栫爜宸插瓨鍦紝璇峰嬁閲嶅鎻愪氦");
+        }
+    }
+
+    /**
+     * 浠呮寜閫氱敤杩囨护鏉′欢鏌ヨ鍩虹鏁版嵁锛堟爲褰㈣繃婊ゅ悗缁啀鍋氾級銆�
+     */
+    private LambdaQueryWrapper<AccountSubject> loadBaseQueryWrapper(AccountSubjectDto accountSubjectDto) {
+        LambdaQueryWrapper<AccountSubject> queryWrapper = new LambdaQueryWrapper<>();
+        if (accountSubjectDto != null && accountSubjectDto.getStatus() != null) {
+            queryWrapper.eq(AccountSubject::getStatus, accountSubjectDto.getStatus());
+        }
+        queryWrapper.orderByAsc(AccountSubject::getSubjectCode).orderByAsc(AccountSubject::getId);
+        return queryWrapper;
+    }
+
+    /**
+     * 鏍戝舰杩囨护锛氬懡涓妭鐐瑰悗淇濈暀鍏剁埗閾句笌瀛愭爲锛屼繚璇侀�掑綊缁撴瀯瀹屾暣銆�
+     */
+    private List<AccountSubject> applyTreeFilter(List<AccountSubject> allSubjects, AccountSubjectDto queryDto) {
+        if (allSubjects == null || allSubjects.isEmpty()) {
+            return Collections.emptyList();
+        }
+        boolean hasFilter = queryDto != null && (
+                StringUtils.isNotEmpty(queryDto.getSubjectCode())
+                        || StringUtils.isNotEmpty(queryDto.getSubjectName())
+                        || StringUtils.isNotEmpty(queryDto.getSubjectType())
+        );
+        if (!hasFilter) {
+            return allSubjects;
+        }
+
+        Map<Long, AccountSubject> subjectMap = allSubjects.stream()
+                .filter(item -> item.getId() != null)
+                .collect(Collectors.toMap(AccountSubject::getId, item -> item, (a, b) -> a, LinkedHashMap::new));
+        Map<Long, List<AccountSubject>> childrenMap = buildChildrenMap(allSubjects);
+
+        Set<Long> matchedIds = new LinkedHashSet<>();
+        for (AccountSubject subject : allSubjects) {
+            if (subject.getId() == null) {
+                continue;
+            }
+            if (matchesFilter(subject, queryDto)) {
+                matchedIds.add(subject.getId());
+            }
+        }
+        if (matchedIds.isEmpty()) {
+            return Collections.emptyList();
+        }
+
+        Set<Long> resultIds = new LinkedHashSet<>(matchedIds);
+        for (Long matchedId : matchedIds) {
+            addAncestors(matchedId, subjectMap, resultIds);
+            addDescendants(matchedId, childrenMap, resultIds);
+        }
+
+        return allSubjects.stream()
+                .filter(item -> item.getId() != null && resultIds.contains(item.getId()))
+                .collect(Collectors.toList());
+    }
+
+    private boolean matchesFilter(AccountSubject subject, AccountSubjectDto queryDto) {
+        if (queryDto == null) {
+            return true;
+        }
+        if (StringUtils.isNotEmpty(queryDto.getSubjectCode())
+                && (subject.getSubjectCode() == null || !subject.getSubjectCode().contains(queryDto.getSubjectCode()))) {
+            return false;
+        }
+        if (StringUtils.isNotEmpty(queryDto.getSubjectName())
+                && (subject.getSubjectName() == null || !subject.getSubjectName().contains(queryDto.getSubjectName()))) {
+            return false;
+        }
+        if (StringUtils.isNotEmpty(queryDto.getSubjectType())
+                && !queryDto.getSubjectType().equals(subject.getSubjectType())) {
+            return false;
+        }
+        return true;
+    }
+
+    private void addAncestors(Long subjectId, Map<Long, AccountSubject> subjectMap, Set<Long> resultIds) {
+        AccountSubject current = subjectMap.get(subjectId);
+        if (current == null) {
+            return;
+        }
+        Long parentId = current.getParentId();
+        while (parentId != null && parentId > 0) {
+            AccountSubject parent = subjectMap.get(parentId);
+            if (parent == null) {
+                break;
+            }
+            if (!resultIds.add(parent.getId())) {
+                break;
+            }
+            parentId = parent.getParentId();
+        }
+    }
+
+    private void addDescendants(Long subjectId, Map<Long, List<AccountSubject>> childrenMap, Set<Long> resultIds) {
+        List<AccountSubject> children = childrenMap.getOrDefault(subjectId, Collections.emptyList());
+        for (AccountSubject child : children) {
+            if (child.getId() == null) {
+                continue;
+            }
+            if (resultIds.add(child.getId())) {
+                addDescendants(child.getId(), childrenMap, resultIds);
+            }
+        }
+    }
+
+    private Map<Long, List<AccountSubject>> buildChildrenMap(List<AccountSubject> subjects) {
+        Map<Long, List<AccountSubject>> childrenMap = new HashMap<>();
+        for (AccountSubject subject : subjects) {
+            if (subject.getId() == null) {
+                continue;
+            }
+            Long parentId = subject.getParentId();
+            if (parentId == null || parentId <= 0) {
+                continue;
+            }
+            childrenMap.computeIfAbsent(parentId, key -> new ArrayList<>()).add(subject);
+        }
+        return childrenMap;
+    }
+
+    /**
+     * 鍩轰簬 parentId 閫掑綊鏋勫缓绉戠洰鏍戙��
+     */
+    private List<AccountSubjectVo> buildTree(List<AccountSubject> subjects) {
+        if (subjects == null || subjects.isEmpty()) {
+            return Collections.emptyList();
+        }
+        List<AccountSubject> sortedSubjects = new ArrayList<>(subjects);
+        sortedSubjects.sort(Comparator
+                .comparing(AccountSubject::getSubjectCode, Comparator.nullsLast(String::compareTo))
+                .thenComparing(AccountSubject::getId, Comparator.nullsLast(Long::compareTo)));
+
+        Map<Long, AccountSubjectVo> subjectVoMap = new LinkedHashMap<>();
+        for (AccountSubject subject : sortedSubjects) {
+            if (subject.getId() == null) {
+                continue;
+            }
+            AccountSubjectVo vo = new AccountSubjectVo();
+            BeanUtils.copyProperties(subject, vo);
+            subjectVoMap.put(subject.getId(), vo);
+        }
+
+        List<AccountSubjectVo> roots = new ArrayList<>();
+        for (AccountSubject subject : sortedSubjects) {
+            if (subject.getId() == null) {
+                continue;
+            }
+            AccountSubjectVo current = subjectVoMap.get(subject.getId());
+            Long parentId = subject.getParentId();
+            if (parentId != null && parentId > 0 && subjectVoMap.containsKey(parentId)) {
+                subjectVoMap.get(parentId).getChildren().add(current);
+            } else {
+                roots.add(current);
+            }
+        }
+
+        markLeafRecursively(roots);
+        return roots;
+    }
+
+    private void markLeafRecursively(List<AccountSubjectVo> nodes) {
+        for (AccountSubjectVo node : nodes) {
+            List<AccountSubjectVo> children = node.getChildren();
+            node.setLeaf(children == null || children.isEmpty());
+            if (children != null && !children.isEmpty()) {
+                markLeafRecursively(children);
+            }
+        }
+    }
+
+    /**
+     * 鏍¢獙鐖跺瓙鍏崇郴锛氱埗鑺傜偣蹇呴』瀛樺湪锛屼笖涓嶈兘褰㈡垚寰幆寮曠敤銆�
+     */
+    private void validateParent(Long parentId, Long currentId) {
+        if (parentId == null || parentId <= 0) {
+            return;
+        }
+        if (currentId != null && parentId.equals(currentId)) {
+            throw new ServiceException("鐖剁鐩笉鑳介�夋嫨鑷韩");
+        }
+        AccountSubject parent = getById(parentId);
+        if (parent == null) {
+            throw new ServiceException("鐖剁鐩笉瀛樺湪锛岃閲嶆柊閫夋嫨");
+        }
+        // 闃叉褰㈡垚鐜細鏇存柊鏃讹紝鐖惰妭鐐逛笉鑳芥槸褰撳墠鑺傜偣鐨勪换鎰忓瓙瀛欒妭鐐广��
+        if (currentId != null) {
+            Set<Long> visited = new HashSet<>();
+            Long traceParentId = parentId;
+            while (traceParentId != null && traceParentId > 0) {
+                if (!visited.add(traceParentId)) {
+                    throw new ServiceException("绉戠洰灞傜骇瀛樺湪寰幆寮曠敤锛岃妫�鏌ョ埗绉戠洰璁剧疆");
+                }
+                if (traceParentId.equals(currentId)) {
+                    throw new ServiceException("鐖剁鐩笉鑳芥槸褰撳墠绉戠洰鎴栧叾瀛愮鐩�");
+                }
+                AccountSubject traceNode = getById(traceParentId);
+                if (traceNode == null) {
+                    break;
+                }
+                traceParentId = traceNode.getParentId();
+            }
+        }
+    }
+
+    private Map<Long, List<Long>> buildChildrenIdMap(List<AccountSubject> subjects) {
+        Map<Long, List<Long>> map = new HashMap<>();
+        for (AccountSubject subject : subjects) {
+            if (subject.getId() == null || subject.getParentId() == null || subject.getParentId() <= 0) {
+                continue;
+            }
+            map.computeIfAbsent(subject.getParentId(), key -> new ArrayList<>()).add(subject.getId());
+        }
+        return map;
+    }
+
+    /**
+     * 鏀堕泦寰呭垹闄よ妭鐐瑰強鍏舵墍鏈夊瓙瀛欒妭鐐广��
+     */
+    private void collectDescendantIds(Long id, Map<Long, List<Long>> childrenIdMap, Set<Long> result) {
+        if (id == null || !result.add(id)) {
+            return;
+        }
+        List<Long> children = childrenIdMap.getOrDefault(id, Collections.emptyList());
+        for (Long childId : children) {
+            collectDescendantIds(childId, childrenIdMap, result);
+        }
+    }
+}
diff --git a/src/main/java/com/ruoyi/account/service/impl/financial/FinFixedAssetServiceImpl.java b/src/main/java/com/ruoyi/account/service/impl/financial/FinFixedAssetServiceImpl.java
new file mode 100644
index 0000000..383c906
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/service/impl/financial/FinFixedAssetServiceImpl.java
@@ -0,0 +1,234 @@
+package com.ruoyi.account.service.impl.financial;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.account.bean.dto.financial.FinFixedAssetDto;
+import com.ruoyi.account.mapper.financial.FinFixedAssetMapper;
+import com.ruoyi.account.pojo.financial.FinFixedAsset;
+import com.ruoyi.account.service.financial.FinFixedAssetService;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.StringUtils;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+/**
+ * 鍥哄畾璧勪骇鏈嶅姟瀹炵幇銆�
+ */
+@Service
+@RequiredArgsConstructor
+public class FinFixedAssetServiceImpl extends ServiceImpl<FinFixedAssetMapper, FinFixedAsset> implements FinFixedAssetService {
+
+    private static final BigDecimal ONE_HUNDRED = new BigDecimal("100");
+    private static final BigDecimal ZERO = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
+    private static final DateTimeFormatter CODE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
+
+    @Override
+    public IPage<FinFixedAsset> pageList(Page<FinFixedAsset> page, FinFixedAssetDto queryDto) {
+        LambdaQueryWrapper<FinFixedAsset> wrapper = new LambdaQueryWrapper<>();
+        if (queryDto != null && StringUtils.isNotEmpty(queryDto.getAssetCode())) {
+            wrapper.like(FinFixedAsset::getAssetCode, queryDto.getAssetCode());
+        }
+        if (queryDto != null && StringUtils.isNotEmpty(queryDto.getAssetName())) {
+            wrapper.like(FinFixedAsset::getAssetName, queryDto.getAssetName());
+        }
+        if (queryDto != null && StringUtils.isNotEmpty(queryDto.getCategory())) {
+            wrapper.eq(FinFixedAsset::getCategory, queryDto.getCategory());
+        }
+        if (queryDto != null && StringUtils.isNotEmpty(queryDto.getStatus())) {
+            wrapper.eq(FinFixedAsset::getStatus, queryDto.getStatus());
+        }
+        wrapper.orderByDesc(FinFixedAsset::getId);
+        return page(page, wrapper);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean add(FinFixedAssetDto dto) {
+        validateForSave(dto, false);
+        if (StringUtils.isEmpty(dto.getAssetCode())) {
+            dto.setAssetCode(generateAssetCode());
+        }
+        BigDecimal residualRate = normalizeResidualRate(dto.getResidualRate());
+        dto.setResidualRate(residualRate);
+        BigDecimal accumulatedDepreciation = defaultMoney(dto.getAccumulatedDepreciation());
+        dto.setAccumulatedDepreciation(accumulatedDepreciation);
+        dto.setNetValue(calculateNetValue(dto.getOriginalValue(), accumulatedDepreciation));
+        if (StringUtils.isEmpty(dto.getStatus())) {
+            dto.setStatus("in_use");
+        }
+        return save(dto);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean update(FinFixedAssetDto dto) {
+        if (dto == null || dto.getId() == null) {
+            throw new ServiceException("淇敼澶辫触锛岃祫浜D涓嶈兘涓虹┖");
+        }
+        FinFixedAsset existed = getById(dto.getId());
+        if (existed == null) {
+            throw new ServiceException("淇敼澶辫触锛屽浐瀹氳祫浜т笉瀛樺湪");
+        }
+        if (StringUtils.isEmpty(dto.getAssetCode())) {
+            dto.setAssetCode(existed.getAssetCode());
+        }
+        if (StringUtils.isEmpty(dto.getStatus())) {
+            dto.setStatus(existed.getStatus());
+        }
+        validateForSave(dto, true);
+        BigDecimal residualRate = normalizeResidualRate(dto.getResidualRate());
+        dto.setResidualRate(residualRate);
+        if (dto.getAccumulatedDepreciation() == null) {
+            dto.setAccumulatedDepreciation(defaultMoney(existed.getAccumulatedDepreciation()));
+        }
+        dto.setNetValue(calculateNetValue(dto.getOriginalValue(), dto.getAccumulatedDepreciation()));
+        return updateById(dto);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean deleteByIds(List<Long> ids) {
+        if (ids == null || ids.isEmpty()) {
+            throw new ServiceException("鍒犻櫎澶辫触锛岃閫夋嫨瑕佸垹闄ょ殑鏁版嵁");
+        }
+        return removeByIds(ids);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Map<String, Object> depreciate(List<Long> ids) {
+        LambdaQueryWrapper<FinFixedAsset> wrapper = new LambdaQueryWrapper<>();
+        if (ids != null && !ids.isEmpty()) {
+            wrapper.in(FinFixedAsset::getId, ids);
+        } else {
+            wrapper.eq(FinFixedAsset::getStatus, "in_use");
+        }
+        List<FinFixedAsset> assets = list(wrapper);
+        BigDecimal totalMonthlyDepreciation = ZERO;
+        int processedCount = 0;
+        for (FinFixedAsset asset : assets) {
+            if (!"in_use".equals(asset.getStatus())) {
+                continue;
+            }
+            BigDecimal monthlyDepreciation = calculateMonthlyDepreciation(
+                    asset.getOriginalValue(),
+                    asset.getResidualRate(),
+                    asset.getUsefulLife()
+            );
+            BigDecimal accumulatedDepreciation = defaultMoney(asset.getAccumulatedDepreciation()).add(monthlyDepreciation);
+            if (accumulatedDepreciation.compareTo(defaultMoney(asset.getOriginalValue())) > 0) {
+                accumulatedDepreciation = defaultMoney(asset.getOriginalValue());
+            }
+            asset.setAccumulatedDepreciation(roundMoney(accumulatedDepreciation));
+            asset.setNetValue(calculateNetValue(asset.getOriginalValue(), asset.getAccumulatedDepreciation()));
+            updateById(asset);
+            processedCount++;
+            totalMonthlyDepreciation = totalMonthlyDepreciation.add(monthlyDepreciation);
+        }
+        Map<String, Object> result = new HashMap<>(4);
+        result.put("processedCount", processedCount);
+        result.put("totalMonthlyDepreciation", roundMoney(totalMonthlyDepreciation));
+        result.put("executionTime", LocalDateTime.now());
+        return result;
+    }
+
+    /**
+     * 鎸夋枃妗h鍒欐牎楠屽浐瀹氳祫浜ф暟鎹��
+     */
+    private void validateForSave(FinFixedAssetDto dto, boolean isUpdate) {
+        if (dto == null) {
+            throw new ServiceException("鍥哄畾璧勪骇鏁版嵁涓嶈兘涓虹┖");
+        }
+        if (isUpdate && dto.getId() == null) {
+            throw new ServiceException("淇敼澶辫触锛岃祫浜D涓嶈兘涓虹┖");
+        }
+        if (StringUtils.isEmpty(dto.getAssetName())) {
+            throw new ServiceException("璧勪骇鍚嶇О涓嶈兘涓虹┖");
+        }
+        if (StringUtils.isEmpty(dto.getCategory())) {
+            throw new ServiceException("璧勪骇绫诲埆涓嶈兘涓虹┖");
+        }
+        if (dto.getPurchaseDate() == null) {
+            throw new ServiceException("璐疆鏃ユ湡涓嶈兘涓虹┖");
+        }
+        if (dto.getOriginalValue() == null || dto.getOriginalValue().compareTo(BigDecimal.ZERO) < 0) {
+            throw new ServiceException("璧勪骇鍘熷�间笉鑳戒负绌轰笖涓嶈兘灏忎簬0");
+        }
+        if (dto.getUsefulLife() == null || dto.getUsefulLife() <= 0) {
+            throw new ServiceException("浣跨敤骞撮檺蹇呴』澶т簬0");
+        }
+        if (dto.getResidualRate() != null && dto.getResidualRate().compareTo(BigDecimal.ZERO) < 0) {
+            throw new ServiceException("娈嬪�肩巼涓嶈兘灏忎簬0");
+        }
+        if (dto.getResidualRate() != null && dto.getResidualRate().compareTo(ONE_HUNDRED) > 0) {
+            throw new ServiceException("娈嬪�肩巼涓嶈兘澶т簬100%");
+        }
+        if (StringUtils.isNotEmpty(dto.getAssetCode())) {
+            LambdaQueryWrapper<FinFixedAsset> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(FinFixedAsset::getAssetCode, dto.getAssetCode());
+            if (isUpdate) {
+                wrapper.ne(FinFixedAsset::getId, dto.getId());
+            }
+            if (count(wrapper) > 0) {
+                throw new ServiceException("璧勪骇缂栧彿宸插瓨鍦紝璇峰嬁閲嶅鎻愪氦");
+            }
+        }
+    }
+
+    /**
+     * 鍥哄畾璧勪骇鎶樻棫鍏紡锛�
+     * monthlyDepreciation = originalValue * (1 - residualRate/100) / (usefulLife*12)
+     */
+    private BigDecimal calculateMonthlyDepreciation(BigDecimal originalValue, BigDecimal residualRate, Integer usefulLife) {
+        BigDecimal normalizedOriginalValue = defaultMoney(originalValue);
+        BigDecimal normalizedResidualRate = normalizeResidualRate(residualRate);
+        BigDecimal depreciableRatio = BigDecimal.ONE.subtract(normalizedResidualRate.divide(ONE_HUNDRED, 8, RoundingMode.HALF_UP));
+        BigDecimal months = BigDecimal.valueOf((long) usefulLife * 12L);
+        if (months.compareTo(BigDecimal.ZERO) <= 0) {
+            throw new ServiceException("浣跨敤骞撮檺鏃犳晥锛屾棤娉曡鎻愭姌鏃�");
+        }
+        return roundMoney(normalizedOriginalValue.multiply(depreciableRatio).divide(months, 8, RoundingMode.HALF_UP));
+    }
+
+    /**
+     * 鍑�鍊� = 鍘熷�� - 绱鎶樻棫銆�
+     */
+    private BigDecimal calculateNetValue(BigDecimal originalValue, BigDecimal accumulatedDepreciation) {
+        BigDecimal value = defaultMoney(originalValue).subtract(defaultMoney(accumulatedDepreciation));
+        if (value.compareTo(BigDecimal.ZERO) < 0) {
+            value = BigDecimal.ZERO;
+        }
+        return roundMoney(value);
+    }
+
+    private BigDecimal normalizeResidualRate(BigDecimal residualRate) {
+        return residualRate == null ? BigDecimal.ZERO : residualRate;
+    }
+
+    private BigDecimal defaultMoney(BigDecimal value) {
+        return value == null ? ZERO : roundMoney(value);
+    }
+
+    private BigDecimal roundMoney(BigDecimal value) {
+        if (value == null) {
+            return ZERO;
+        }
+        return value.setScale(2, RoundingMode.HALF_UP);
+    }
+
+    private String generateAssetCode() {
+        return "GD" + LocalDateTime.now().format(CODE_TIME_FORMATTER) + new Random().nextInt(10);
+    }
+}
diff --git a/src/main/java/com/ruoyi/account/service/impl/financial/FinIntangibleAssetServiceImpl.java b/src/main/java/com/ruoyi/account/service/impl/financial/FinIntangibleAssetServiceImpl.java
new file mode 100644
index 0000000..151cf9e
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/service/impl/financial/FinIntangibleAssetServiceImpl.java
@@ -0,0 +1,253 @@
+package com.ruoyi.account.service.impl.financial;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.account.bean.dto.financial.FinIntangibleAssetDto;
+import com.ruoyi.account.mapper.financial.FinIntangibleAssetMapper;
+import com.ruoyi.account.pojo.financial.FinIntangibleAsset;
+import com.ruoyi.account.service.financial.FinIntangibleAssetService;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.StringUtils;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+/**
+ * 鏃犲舰璧勪骇鏈嶅姟瀹炵幇銆�
+ */
+@Service
+@RequiredArgsConstructor
+public class FinIntangibleAssetServiceImpl extends ServiceImpl<FinIntangibleAssetMapper, FinIntangibleAsset> implements FinIntangibleAssetService {
+
+    private static final BigDecimal ONE_HUNDRED = new BigDecimal("100");
+    private static final BigDecimal ZERO = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
+    private static final DateTimeFormatter CODE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
+
+    @Override
+    public IPage<FinIntangibleAsset> pageList(Page<FinIntangibleAsset> page, FinIntangibleAssetDto queryDto) {
+        LambdaQueryWrapper<FinIntangibleAsset> wrapper = new LambdaQueryWrapper<>();
+        if (queryDto != null && StringUtils.isNotEmpty(queryDto.getAssetCode())) {
+            wrapper.like(FinIntangibleAsset::getAssetCode, queryDto.getAssetCode());
+        }
+        if (queryDto != null && StringUtils.isNotEmpty(queryDto.getAssetName())) {
+            wrapper.like(FinIntangibleAsset::getAssetName, queryDto.getAssetName());
+        }
+        if (queryDto != null && StringUtils.isNotEmpty(queryDto.getCategory())) {
+            wrapper.eq(FinIntangibleAsset::getCategory, queryDto.getCategory());
+        }
+        if (queryDto != null && StringUtils.isNotEmpty(queryDto.getStatus())) {
+            wrapper.eq(FinIntangibleAsset::getStatus, queryDto.getStatus());
+        }
+        wrapper.orderByDesc(FinIntangibleAsset::getId);
+        return page(page, wrapper);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean add(FinIntangibleAssetDto dto) {
+        validateForSave(dto, false);
+        if (StringUtils.isEmpty(dto.getAssetCode())) {
+            dto.setAssetCode(generateAssetCode());
+        }
+        BigDecimal residualRate = normalizeResidualRate(dto.getResidualRate());
+        dto.setResidualRate(residualRate);
+        BigDecimal accumulatedAmortization = defaultMoney(dto.getAccumulatedAmortization());
+        dto.setAccumulatedAmortization(accumulatedAmortization);
+        dto.setNetValue(calculateNetValue(dto.getOriginalValue(), accumulatedAmortization));
+        if (StringUtils.isEmpty(dto.getStatus())) {
+            dto.setStatus("in_use");
+        }
+        return save(dto);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean update(FinIntangibleAssetDto dto) {
+        if (dto == null || dto.getId() == null) {
+            throw new ServiceException("淇敼澶辫触锛岃祫浜D涓嶈兘涓虹┖");
+        }
+        FinIntangibleAsset existed = getById(dto.getId());
+        if (existed == null) {
+            throw new ServiceException("淇敼澶辫触锛屾棤褰㈣祫浜т笉瀛樺湪");
+        }
+        if (StringUtils.isEmpty(dto.getAssetCode())) {
+            dto.setAssetCode(existed.getAssetCode());
+        }
+        if (StringUtils.isEmpty(dto.getStatus())) {
+            dto.setStatus(existed.getStatus());
+        }
+        validateForSave(dto, true);
+        BigDecimal residualRate = normalizeResidualRate(dto.getResidualRate());
+        dto.setResidualRate(residualRate);
+        if (dto.getAccumulatedAmortization() == null) {
+            dto.setAccumulatedAmortization(defaultMoney(existed.getAccumulatedAmortization()));
+        }
+        dto.setNetValue(calculateNetValue(dto.getOriginalValue(), dto.getAccumulatedAmortization()));
+        if (dto.getNetValue().compareTo(BigDecimal.ZERO) <= 0) {
+            dto.setStatus("amortized");
+        } else if ("amortized".equals(dto.getStatus())) {
+            dto.setStatus("in_use");
+        }
+        if (dto.getValidityDate() != null
+                && dto.getValidityDate().isBefore(LocalDate.now())
+                && !"amortized".equals(dto.getStatus())) {
+            dto.setStatus("expired");
+        }
+        return updateById(dto);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean deleteByIds(List<Long> ids) {
+        if (ids == null || ids.isEmpty()) {
+            throw new ServiceException("鍒犻櫎澶辫触锛岃閫夋嫨瑕佸垹闄ょ殑鏁版嵁");
+        }
+        return removeByIds(ids);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Map<String, Object> amortize(List<Long> ids) {
+        LambdaQueryWrapper<FinIntangibleAsset> wrapper = new LambdaQueryWrapper<>();
+        if (ids != null && !ids.isEmpty()) {
+            wrapper.in(FinIntangibleAsset::getId, ids);
+        } else {
+            wrapper.eq(FinIntangibleAsset::getStatus, "in_use");
+        }
+        List<FinIntangibleAsset> assets = list(wrapper);
+        BigDecimal totalMonthlyAmortization = ZERO;
+        int processedCount = 0;
+        for (FinIntangibleAsset asset : assets) {
+            if (!"in_use".equals(asset.getStatus())) {
+                continue;
+            }
+            BigDecimal monthlyAmortization = calculateMonthlyAmortization(
+                    asset.getOriginalValue(),
+                    asset.getResidualRate(),
+                    asset.getAmortizationPeriod()
+            );
+            BigDecimal accumulatedAmortization = defaultMoney(asset.getAccumulatedAmortization()).add(monthlyAmortization);
+            if (accumulatedAmortization.compareTo(defaultMoney(asset.getOriginalValue())) > 0) {
+                accumulatedAmortization = defaultMoney(asset.getOriginalValue());
+            }
+            asset.setAccumulatedAmortization(roundMoney(accumulatedAmortization));
+            asset.setNetValue(calculateNetValue(asset.getOriginalValue(), asset.getAccumulatedAmortization()));
+
+            // 瑙勫垯锛氬綋鍑�鍊� <= 0 鏃讹紝鍑�鍊煎綊闆跺苟鏍囪涓哄凡鎽婇攢瀹屻��
+            if (asset.getNetValue().compareTo(BigDecimal.ZERO) <= 0) {
+                asset.setNetValue(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
+                asset.setStatus("amortized");
+            } else if (asset.getValidityDate() != null && asset.getValidityDate().isBefore(LocalDate.now())) {
+                asset.setStatus("expired");
+            }
+            updateById(asset);
+            processedCount++;
+            totalMonthlyAmortization = totalMonthlyAmortization.add(monthlyAmortization);
+        }
+        Map<String, Object> result = new HashMap<>(4);
+        result.put("processedCount", processedCount);
+        result.put("totalMonthlyAmortization", roundMoney(totalMonthlyAmortization));
+        result.put("executionTime", LocalDateTime.now());
+        return result;
+    }
+
+    /**
+     * 鎸夋枃妗h鍒欐牎楠屾棤褰㈣祫浜ф暟鎹��
+     */
+    private void validateForSave(FinIntangibleAssetDto dto, boolean isUpdate) {
+        if (dto == null) {
+            throw new ServiceException("鏃犲舰璧勪骇鏁版嵁涓嶈兘涓虹┖");
+        }
+        if (isUpdate && dto.getId() == null) {
+            throw new ServiceException("淇敼澶辫触锛岃祫浜D涓嶈兘涓虹┖");
+        }
+        if (StringUtils.isEmpty(dto.getAssetName())) {
+            throw new ServiceException("璧勪骇鍚嶇О涓嶈兘涓虹┖");
+        }
+        if (StringUtils.isEmpty(dto.getCategory())) {
+            throw new ServiceException("璧勪骇绫诲埆涓嶈兘涓虹┖");
+        }
+        if (dto.getAcquisitionDate() == null) {
+            throw new ServiceException("鍙栧緱鏃ユ湡涓嶈兘涓虹┖");
+        }
+        if (dto.getOriginalValue() == null || dto.getOriginalValue().compareTo(BigDecimal.ZERO) < 0) {
+            throw new ServiceException("璧勪骇鍘熷�间笉鑳戒负绌轰笖涓嶈兘灏忎簬0");
+        }
+        if (dto.getAmortizationPeriod() == null || dto.getAmortizationPeriod() <= 0) {
+            throw new ServiceException("鎽婇攢骞撮檺蹇呴』澶т簬0");
+        }
+        if (dto.getResidualRate() != null && dto.getResidualRate().compareTo(BigDecimal.ZERO) < 0) {
+            throw new ServiceException("娈嬪�肩巼涓嶈兘灏忎簬0");
+        }
+        if (dto.getResidualRate() != null && dto.getResidualRate().compareTo(ONE_HUNDRED) > 0) {
+            throw new ServiceException("娈嬪�肩巼涓嶈兘澶т簬100%");
+        }
+        if (StringUtils.isNotEmpty(dto.getAssetCode())) {
+            LambdaQueryWrapper<FinIntangibleAsset> wrapper = new LambdaQueryWrapper<>();
+            wrapper.eq(FinIntangibleAsset::getAssetCode, dto.getAssetCode());
+            if (isUpdate) {
+                wrapper.ne(FinIntangibleAsset::getId, dto.getId());
+            }
+            if (count(wrapper) > 0) {
+                throw new ServiceException("璧勪骇缂栧彿宸插瓨鍦紝璇峰嬁閲嶅鎻愪氦");
+            }
+        }
+    }
+
+    /**
+     * 鏃犲舰璧勪骇鎽婇攢鍏紡锛�
+     * monthlyAmortization = originalValue * (1 - residualRate/100) / (amortizationPeriod*12)
+     */
+    private BigDecimal calculateMonthlyAmortization(BigDecimal originalValue, BigDecimal residualRate, Integer amortizationPeriod) {
+        BigDecimal normalizedOriginalValue = defaultMoney(originalValue);
+        BigDecimal normalizedResidualRate = normalizeResidualRate(residualRate);
+        BigDecimal amortizableRatio = BigDecimal.ONE.subtract(normalizedResidualRate.divide(ONE_HUNDRED, 8, RoundingMode.HALF_UP));
+        BigDecimal months = BigDecimal.valueOf((long) amortizationPeriod * 12L);
+        if (months.compareTo(BigDecimal.ZERO) <= 0) {
+            throw new ServiceException("鎽婇攢骞撮檺鏃犳晥锛屾棤娉曡鎻愭憡閿�");
+        }
+        return roundMoney(normalizedOriginalValue.multiply(amortizableRatio).divide(months, 8, RoundingMode.HALF_UP));
+    }
+
+    /**
+     * 鍑�鍊� = 鍘熷�� - 绱鎽婇攢銆�
+     */
+    private BigDecimal calculateNetValue(BigDecimal originalValue, BigDecimal accumulatedAmortization) {
+        BigDecimal value = defaultMoney(originalValue).subtract(defaultMoney(accumulatedAmortization));
+        if (value.compareTo(BigDecimal.ZERO) < 0) {
+            value = BigDecimal.ZERO;
+        }
+        return roundMoney(value);
+    }
+
+    private BigDecimal normalizeResidualRate(BigDecimal residualRate) {
+        return residualRate == null ? BigDecimal.ZERO : residualRate;
+    }
+
+    private BigDecimal defaultMoney(BigDecimal value) {
+        return value == null ? ZERO : roundMoney(value);
+    }
+
+    private BigDecimal roundMoney(BigDecimal value) {
+        if (value == null) {
+            return ZERO;
+        }
+        return value.setScale(2, RoundingMode.HALF_UP);
+    }
+
+    private String generateAssetCode() {
+        return "WX" + LocalDateTime.now().format(CODE_TIME_FORMATTER) + new Random().nextInt(10);
+    }
+}
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..a36b241
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/service/impl/financial/FinLedgerServiceImpl.java
@@ -0,0 +1,237 @@
+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 Collections.singletonList(buildGeneralLedgerTotalRow(queryDto.getSubjectCode(), startMonth, endMonth));
+    }
+
+    @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 FinLedgerRowVo buildGeneralLedgerTotalRow(String subjectCode, YearMonth startMonth, YearMonth endMonth) {
+        LocalDate startDate = startMonth.atDay(1);
+        LocalDate endDate = endMonth.atEndOfMonth();
+
+        List<FinLedgerEntryRecordVo> openingEntries = finVoucherEntryMapper.listPostedEntriesBefore(
+                subjectCode, startDate, null, null
+        );
+        BigDecimal openingBalance = calculateBalance(openingEntries);
+
+        List<FinLedgerEntryRecordVo> currentPeriodEntries = finVoucherEntryMapper.listPostedEntries(
+                subjectCode, startDate, endDate, null, null
+        );
+
+        BigDecimal totalDebit = ZERO;
+        BigDecimal totalCredit = ZERO;
+        for (FinLedgerEntryRecordVo entry : currentPeriodEntries) {
+            totalDebit = totalDebit.add(money(entry.getDebit()));
+            totalCredit = totalCredit.add(money(entry.getCredit()));
+        }
+
+        BigDecimal endingBalance = openingBalance.add(totalDebit).subtract(totalCredit);
+        FinLedgerRowVo totalRow = new FinLedgerRowVo();
+        totalRow.setRowType("yearly_total");
+        totalRow.setDate(endDate);
+        totalRow.setDebit(money(totalDebit));
+        totalRow.setCredit(money(totalCredit));
+        totalRow.setBalance(money(endingBalance));
+        totalRow.setDirection(resolveDirection(endingBalance));
+        return totalRow;
+    }
+
+    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);
+    }
+}
diff --git a/src/main/java/com/ruoyi/account/service/impl/financial/FinVoucherServiceImpl.java b/src/main/java/com/ruoyi/account/service/impl/financial/FinVoucherServiceImpl.java
new file mode 100644
index 0000000..6bd4648
--- /dev/null
+++ b/src/main/java/com/ruoyi/account/service/impl/financial/FinVoucherServiceImpl.java
@@ -0,0 +1,324 @@
+package com.ruoyi.account.service.impl.financial;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.account.bean.dto.financial.FinVoucherDto;
+import com.ruoyi.account.bean.dto.financial.FinVoucherEntryDto;
+import com.ruoyi.account.bean.dto.financial.FinVoucherPageDto;
+import com.ruoyi.account.bean.vo.financial.FinVoucherDetailVo;
+import com.ruoyi.account.mapper.financial.AccountSubjectMapper;
+import com.ruoyi.account.mapper.financial.FinVoucherEntryMapper;
+import com.ruoyi.account.mapper.financial.FinVoucherMapper;
+import com.ruoyi.account.pojo.financial.AccountSubject;
+import com.ruoyi.account.pojo.financial.FinVoucher;
+import com.ruoyi.account.pojo.financial.FinVoucherEntry;
+import com.ruoyi.account.service.financial.FinVoucherService;
+import com.ruoyi.common.enums.FileNameType;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.other.service.impl.TempFileServiceImpl;
+import com.ruoyi.sales.service.impl.CommonFileServiceImpl;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * 鍑瘉鏈嶅姟瀹炵幇銆�
+ */
+@Service
+@RequiredArgsConstructor
+public class FinVoucherServiceImpl extends ServiceImpl<FinVoucherMapper, FinVoucher> implements FinVoucherService {
+
+    private static final BigDecimal ZERO = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
+
+    private final FinVoucherEntryMapper finVoucherEntryMapper;
+    private final AccountSubjectMapper accountSubjectMapper;
+    private final CommonFileServiceImpl commonFileService;
+
+    @Override
+    public IPage<FinVoucher> pageList(Page<FinVoucher> page, FinVoucherPageDto queryDto) {
+        LambdaQueryWrapper<FinVoucher> wrapper = new LambdaQueryWrapper<>();
+        if (queryDto != null && StringUtils.isNotEmpty(queryDto.getVoucherNo())) {
+            wrapper.like(FinVoucher::getVoucherNo, queryDto.getVoucherNo());
+        }
+        if (queryDto != null && StringUtils.isNotEmpty(queryDto.getCreator())) {
+            wrapper.eq(FinVoucher::getCreator, queryDto.getCreator());
+        }
+        if (queryDto != null && StringUtils.isNotEmpty(queryDto.getStatus())) {
+            wrapper.eq(FinVoucher::getStatus, queryDto.getStatus());
+        }
+        if (queryDto != null && queryDto.getStartDate() != null) {
+            wrapper.ge(FinVoucher::getVoucherDate, queryDto.getStartDate());
+        }
+        if (queryDto != null && queryDto.getEndDate() != null) {
+            wrapper.le(FinVoucher::getVoucherDate, queryDto.getEndDate());
+        }
+        wrapper.orderByDesc(FinVoucher::getVoucherDate).orderByDesc(FinVoucher::getId);
+        return page(page, wrapper);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean addVoucher(FinVoucherDto dto) {
+        validateVoucherBasicInfo(dto, false);
+        List<FinVoucherEntry> validEntries = buildAndValidateEntries(dto);
+
+        FinVoucher voucher = new FinVoucher();
+        BeanUtils.copyProperties(dto, voucher);
+        voucher.setStatus("unposted");
+        voucher.setAttachmentCount(voucher.getAttachmentCount() == null ? 0 : voucher.getAttachmentCount());
+        BigDecimal totalDebit = calculateTotalDebit(validEntries);
+        BigDecimal totalCredit = calculateTotalCredit(validEntries);
+        voucher.setDebit(totalDebit);
+        voucher.setCredit(totalCredit);
+        if (StringUtils.isEmpty(voucher.getSummary())) {
+            voucher.setSummary(findDefaultSummary(validEntries));
+        }
+        save(voucher);
+        saveEntries(voucher.getId(), validEntries);
+        // 5. 淇濆瓨闄勪欢
+        try {
+            if(!CollectionUtils.isEmpty(dto.getTempFileIds())){
+                commonFileService.migrateTempFilesToFormal(voucher.getId(),dto.getTempFileIds());
+            }
+        }catch (Exception e){
+            throw new ServiceException("淇濆瓨闄勪欢澶辫触");
+        }
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean updateVoucher(FinVoucherDto dto) {
+        validateVoucherBasicInfo(dto, true);
+        FinVoucher existed = getById(dto.getId());
+        if (existed == null) {
+            throw new ServiceException("淇敼澶辫触锛屽嚟璇佷笉瀛樺湪");
+        }
+        if (!"unposted".equals(existed.getStatus())) {
+            throw new ServiceException("浠呮湭杩囪处鍑瘉鍏佽淇敼");
+        }
+        List<FinVoucherEntry> validEntries = buildAndValidateEntries(dto);
+
+        FinVoucher voucher = new FinVoucher();
+        BeanUtils.copyProperties(dto, voucher);
+        voucher.setStatus(existed.getStatus());
+        voucher.setAttachmentCount(voucher.getAttachmentCount() == null ? 0 : voucher.getAttachmentCount());
+        BigDecimal totalDebit = calculateTotalDebit(validEntries);
+        BigDecimal totalCredit = calculateTotalCredit(validEntries);
+        voucher.setDebit(totalDebit);
+        voucher.setCredit(totalCredit);
+        if (StringUtils.isEmpty(voucher.getSummary())) {
+            voucher.setSummary(findDefaultSummary(validEntries));
+        }
+        updateById(voucher);
+
+        LambdaQueryWrapper<FinVoucherEntry> deleteWrapper = new LambdaQueryWrapper<>();
+        deleteWrapper.eq(FinVoucherEntry::getVoucherId, voucher.getId());
+        finVoucherEntryMapper.delete(deleteWrapper);
+        saveEntries(voucher.getId(), validEntries);
+        // 5. 淇濆瓨闄勪欢
+        try {
+            if(!CollectionUtils.isEmpty(dto.getTempFileIds())){
+                commonFileService.migrateTempFilesToFormal(voucher.getId(),dto.getTempFileIds());
+            }
+        }catch (Exception e){
+            throw new ServiceException("淇濆瓨闄勪欢澶辫触");
+        }
+        return true;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean postVoucher(Long id) {
+        FinVoucher voucher = getById(id);
+        if (voucher == null) {
+            throw new ServiceException("杩囪处澶辫触锛屽嚟璇佷笉瀛樺湪");
+        }
+        if (!"unposted".equals(voucher.getStatus())) {
+            throw new ServiceException("浠呮湭杩囪处鍑瘉鍏佽杩囪处");
+        }
+        voucher.setStatus("posted");
+        return updateById(voucher);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Boolean cancelVoucher(Long id) {
+        FinVoucher voucher = getById(id);
+        if (voucher == null) {
+            throw new ServiceException("浣滃簾澶辫触锛屽嚟璇佷笉瀛樺湪");
+        }
+        if (!"unposted".equals(voucher.getStatus())) {
+            throw new ServiceException("浠呮湭杩囪处鍑瘉鍏佽浣滃簾");
+        }
+        voucher.setStatus("cancelled");
+        return updateById(voucher);
+    }
+
+    @Override
+    public FinVoucherDetailVo detail(Long id) {
+        FinVoucher voucher = getById(id);
+        if (voucher == null) {
+            throw new ServiceException("鏌ヨ澶辫触锛屽嚟璇佷笉瀛樺湪");
+        }
+        LambdaQueryWrapper<FinVoucherEntry> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(FinVoucherEntry::getVoucherId, id)
+                .orderByAsc(FinVoucherEntry::getRowNo)
+                .orderByAsc(FinVoucherEntry::getId);
+        List<FinVoucherEntry> entries = finVoucherEntryMapper.selectList(wrapper);
+
+        FinVoucherDetailVo vo = new FinVoucherDetailVo();
+        BeanUtils.copyProperties(voucher, vo);
+        vo.setEntries(entries);
+        vo.setSalesLedgerFiles(commonFileService.getFileListByBusinessId(id, FileNameType.FIN_VOUCHER.getValue()));
+        return vo;
+    }
+
+    /**
+     * 鏍¢獙鍑瘉涓昏〃瀛楁銆佺姸鎬佸瓧娈典笌鍞竴鎬с��
+     */
+    private void validateVoucherBasicInfo(FinVoucherDto dto, boolean isUpdate) {
+        if (dto == null) {
+            throw new ServiceException("鍑瘉鏁版嵁涓嶈兘涓虹┖");
+        }
+        if (isUpdate && dto.getId() == null) {
+            throw new ServiceException("淇敼澶辫触锛屽嚟璇両D涓嶈兘涓虹┖");
+        }
+        if (StringUtils.isEmpty(dto.getVoucherNo())) {
+            throw new ServiceException("鍑瘉瀛楀彿涓嶈兘涓虹┖");
+        }
+        if (dto.getVoucherDate() == null) {
+            throw new ServiceException("鍑瘉鏃ユ湡涓嶈兘涓虹┖");
+        }
+        LambdaQueryWrapper<FinVoucher> uniqueWrapper = new LambdaQueryWrapper<>();
+        uniqueWrapper.eq(FinVoucher::getVoucherNo, dto.getVoucherNo());
+        if (isUpdate) {
+            uniqueWrapper.ne(FinVoucher::getId, dto.getId());
+        }
+        if (count(uniqueWrapper) > 0) {
+            throw new ServiceException("鍑瘉瀛楀彿宸插瓨鍦紝璇峰嬁閲嶅鎻愪氦");
+        }
+    }
+
+    /**
+     * 杩囨护鏈夋晥鍒嗗綍骞舵墽琛屽�熻捶骞宠 鏍¢獙銆�
+     */
+    private List<FinVoucherEntry> buildAndValidateEntries(FinVoucherDto dto) {
+        List<FinVoucherEntryDto> rawEntries = dto.getEntries();
+        if (rawEntries == null || rawEntries.isEmpty()) {
+            throw new ServiceException("鍒嗗綍涓嶈兘涓虹┖锛岃嚦灏戦渶瑕佷竴鏉℃湁鏁堝垎褰�");
+        }
+        List<FinVoucherEntry> validEntries = new ArrayList<>();
+        int rowNo = 1;
+        for (FinVoucherEntryDto entryDto : rawEntries) {
+            if (entryDto == null || StringUtils.isEmpty(entryDto.getSubjectCode())) {
+                continue;
+            }
+            BigDecimal debit = defaultMoney(entryDto.getDebit());
+            BigDecimal credit = defaultMoney(entryDto.getCredit());
+            if (debit.compareTo(BigDecimal.ZERO) <= 0 && credit.compareTo(BigDecimal.ZERO) <= 0) {
+                continue;
+            }
+            if (debit.compareTo(BigDecimal.ZERO) > 0 && credit.compareTo(BigDecimal.ZERO) > 0) {
+                throw new ServiceException("鍒嗗綍鍊熸柟鍜岃捶鏂逛笉鑳藉悓鏃跺ぇ浜�0");
+            }
+            FinVoucherEntry entry = new FinVoucherEntry();
+            BeanUtils.copyProperties(entryDto, entry);
+            entry.setDebit(debit);
+            entry.setCredit(credit);
+            entry.setRowNo(rowNo++);
+            validEntries.add(entry);
+        }
+        if (validEntries.isEmpty()) {
+            throw new ServiceException("鍒嗗綍鑷冲皯闇�瑕佷竴鏉℃湁鏁堣锛堢鐩笉绌猴紝涓斿�熸柟鎴栬捶鏂瑰ぇ浜�0锛�");
+        }
+
+        // 鍒嗗綍绉戠洰蹇呴』瀛樺湪锛岄伩鍏嶈剰绉戠洰缂栫爜鍏ヨ处銆�
+        Set<String> subjectCodes = validEntries.stream()
+                .map(FinVoucherEntry::getSubjectCode)
+                .filter(StringUtils::isNotEmpty)
+                .collect(Collectors.toSet());
+        if (subjectCodes.isEmpty()) {
+            throw new ServiceException("鍒嗗綍绉戠洰涓嶈兘涓虹┖");
+        }
+        LambdaQueryWrapper<AccountSubject> subjectWrapper = new LambdaQueryWrapper<>();
+        subjectWrapper.in(AccountSubject::getSubjectCode, subjectCodes);
+        List<AccountSubject> subjects = accountSubjectMapper.selectList(subjectWrapper);
+        Map<String, AccountSubject> subjectMap = subjects.stream()
+                .collect(Collectors.toMap(AccountSubject::getSubjectCode, it -> it, (a, b) -> a));
+        for (FinVoucherEntry entry : validEntries) {
+            AccountSubject accountSubject = subjectMap.get(entry.getSubjectCode());
+            if (accountSubject == null) {
+                throw new ServiceException("绉戠洰缂栫爜涓嶅瓨鍦細" + entry.getSubjectCode());
+            }
+            if (StringUtils.isEmpty(entry.getSubjectName())) {
+                entry.setSubjectName(accountSubject.getSubjectName());
+            }
+        }
+
+        BigDecimal totalDebit = calculateTotalDebit(validEntries);
+        BigDecimal totalCredit = calculateTotalCredit(validEntries);
+        if (totalDebit.compareTo(BigDecimal.ZERO) <= 0 || totalCredit.compareTo(BigDecimal.ZERO) <= 0) {
+            throw new ServiceException("鍊熻捶閲戦蹇呴』澶т簬0");
+        }
+        if (totalDebit.compareTo(totalCredit) != 0) {
+            throw new ServiceException("鍊熻捶涓嶅钩琛★紝绂佹淇濆瓨");
+        }
+        return validEntries;
+    }
+
+    private void saveEntries(Long voucherId, List<FinVoucherEntry> entries) {
+        if (voucherId == null) {
+            throw new ServiceException("鍑瘉ID涓嶈兘涓虹┖");
+        }
+        for (FinVoucherEntry entry : entries) {
+            entry.setVoucherId(voucherId);
+            finVoucherEntryMapper.insert(entry);
+        }
+    }
+
+    private String findDefaultSummary(List<FinVoucherEntry> entries) {
+        for (FinVoucherEntry entry : entries) {
+            if (StringUtils.isNotEmpty(entry.getSummary())) {
+                return entry.getSummary();
+            }
+        }
+        return "";
+    }
+
+    private BigDecimal calculateTotalDebit(List<FinVoucherEntry> entries) {
+        BigDecimal total = BigDecimal.ZERO;
+        for (FinVoucherEntry entry : entries) {
+            total = total.add(defaultMoney(entry.getDebit()));
+        }
+        return total.setScale(2, RoundingMode.HALF_UP);
+    }
+
+    private BigDecimal calculateTotalCredit(List<FinVoucherEntry> entries) {
+        BigDecimal total = BigDecimal.ZERO;
+        for (FinVoucherEntry entry : entries) {
+            total = total.add(defaultMoney(entry.getCredit()));
+        }
+        return total.setScale(2, RoundingMode.HALF_UP);
+    }
+
+    private BigDecimal defaultMoney(BigDecimal value) {
+        if (value == null) {
+            return ZERO;
+        }
+        return value.setScale(2, RoundingMode.HALF_UP);
+    }
+}
diff --git a/src/main/java/com/ruoyi/common/enums/FileNameType.java b/src/main/java/com/ruoyi/common/enums/FileNameType.java
index 2deb5d6..95b44ea 100644
--- a/src/main/java/com/ruoyi/common/enums/FileNameType.java
+++ b/src/main/java/com/ruoyi/common/enums/FileNameType.java
@@ -17,7 +17,8 @@
     INSPECTION_PRODUCTION_BEFORE(10),
     INSPECTION_PRODUCTION_AFTER(11),
     INSPECTION(12),//宸℃ 鐢熶骇鍓�
-    APP(13);
+    APP(13),
+    FIN_VOUCHER(14);
 
     private final int value;
 
diff --git a/src/main/java/com/ruoyi/device/mapper/DeviceLedgerMapper.java b/src/main/java/com/ruoyi/device/mapper/DeviceLedgerMapper.java
index a7d1a37..00c1617 100644
--- a/src/main/java/com/ruoyi/device/mapper/DeviceLedgerMapper.java
+++ b/src/main/java/com/ruoyi/device/mapper/DeviceLedgerMapper.java
@@ -4,8 +4,7 @@
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
-import com.ruoyi.account.dto.DeviceTypeDetail;
-import com.ruoyi.account.dto.DeviceTypeDistributionVO;
+import com.ruoyi.account.bean.dto.DeviceTypeDetail;
 import com.ruoyi.device.dto.DeviceLedgerDto;
 import com.ruoyi.device.execl.DeviceLedgerExeclDto;
 import com.ruoyi.device.pojo.DeviceLedger;
diff --git a/src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnManagementServiceImpl.java b/src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnManagementServiceImpl.java
index edb70f9..b398edb 100644
--- a/src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnManagementServiceImpl.java
+++ b/src/main/java/com/ruoyi/procurementrecord/service/impl/ReturnManagementServiceImpl.java
@@ -1,13 +1,11 @@
 package com.ruoyi.procurementrecord.service.impl;
 
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.ruoyi.account.dto.SalesRefundAmountOrderDto;
+import com.ruoyi.account.bean.dto.SalesRefundAmountOrderDto;
 import com.ruoyi.account.mapper.AccountExpenseMapper;
 import com.ruoyi.account.pojo.AccountExpense;
-import com.ruoyi.account.pojo.SalesRefundAmountOrder;
 import com.ruoyi.account.service.SalesRefundAmountOrderService;
 import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum;
 import com.ruoyi.common.enums.StockInUnQualifiedRecordTypeEnum;
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
index 1ad20a0..fb3121f 100644
--- a/src/main/resources/application-dev.yml
+++ b/src/main/resources/application-dev.yml
@@ -74,7 +74,7 @@
     druid:
       # 涓诲簱鏁版嵁婧�
       master:
-        url: jdbc:mysql://localhost:3306/product-inventory-management-new?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+        url: jdbc:mysql://localhost:3306/product-inventory-management-fbdzsw?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
         username: root
         password: 123456
       # 浠庡簱鏁版嵁婧�
diff --git a/src/main/resources/mapper/account/AccountExpenseMapper.xml b/src/main/resources/mapper/account/AccountExpenseMapper.xml
index f4039c8..77eed00 100644
--- a/src/main/resources/mapper/account/AccountExpenseMapper.xml
+++ b/src/main/resources/mapper/account/AccountExpenseMapper.xml
@@ -45,7 +45,7 @@
             AND expense_method = #{accountExpense.expenseMethod}
         </if>
     </select>
-    <select id="report" resultType="com.ruoyi.account.dto.AccountDto2">
+    <select id="report" resultType="com.ruoyi.account.bean.dto.AccountDto2">
         SELECT
         sdd.dict_label typeName,
         sum(expense_money) account
diff --git a/src/main/resources/mapper/account/AccountIncomeMapper.xml b/src/main/resources/mapper/account/AccountIncomeMapper.xml
index 53c8cf3..492834f 100644
--- a/src/main/resources/mapper/account/AccountIncomeMapper.xml
+++ b/src/main/resources/mapper/account/AccountIncomeMapper.xml
@@ -47,7 +47,7 @@
             AND income_method = #{accountIncome.incomeMethod}
         </if>
     </select>
-    <select id="report" resultType="com.ruoyi.account.dto.AccountDto2">
+    <select id="report" resultType="com.ruoyi.account.bean.dto.AccountDto2">
         SELECT
         sdd.dict_label typeName,
         ifnull(sum(income_money),0) account
diff --git a/src/main/resources/mapper/account/SalesRefundAmountOrderMapper.xml b/src/main/resources/mapper/account/SalesRefundAmountOrderMapper.xml
index a58e90f..403267c 100644
--- a/src/main/resources/mapper/account/SalesRefundAmountOrderMapper.xml
+++ b/src/main/resources/mapper/account/SalesRefundAmountOrderMapper.xml
@@ -15,7 +15,7 @@
         <result column="create_user_id" property="createUserId" />
         <result column="update_user_id" property="updateUserId" />
     </resultMap>
-    <select id="pageSalesRefundAmountOrderDto" resultType="com.ruoyi.account.dto.SalesRefundAmountOrderDto">
+    <select id="pageSalesRefundAmountOrderDto" resultType="com.ruoyi.account.bean.dto.SalesRefundAmountOrderDto">
         select sl.sales_contract_no,
         sl.customer_contract_no,
         slp.specification_model,
diff --git a/src/main/resources/mapper/account/financial/AccountSubjectMapper.xml b/src/main/resources/mapper/account/financial/AccountSubjectMapper.xml
new file mode 100644
index 0000000..469691f
--- /dev/null
+++ b/src/main/resources/mapper/account/financial/AccountSubjectMapper.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.account.mapper.financial.AccountSubjectMapper">
+
+    <!-- 閫氱敤鏌ヨ鏄犲皠缁撴灉 -->
+    <resultMap id="BaseResultMap" type="com.ruoyi.account.pojo.financial.AccountSubject">
+        <id column="id" property="id" />
+        <result column="parent_id" property="parentId" />
+        <result column="subject_code" property="subjectCode" />
+        <result column="subject_name" property="subjectName" />
+        <result column="subject_type" property="subjectType" />
+        <result column="balance_direction" property="balanceDirection" />
+        <result column="status" property="status" />
+        <result column="remark" property="remark" />
+        <result column="create_user" property="createUser" />
+        <result column="create_time" property="createTime" />
+        <result column="update_user" property="updateUser" />
+        <result column="update_time" property="updateTime" />
+        <result column="dept_id" property="deptId" />
+    </resultMap>
+
+    <select id="countReferencedBySubjectCodes" resultType="java.lang.Long">
+        SELECT COUNT(1)
+        FROM fin_voucher_entry
+        WHERE subject_code IN
+        <foreach collection="subjectCodes" item="item" open="(" separator="," close=")">
+            #{item}
+        </foreach>
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/account/financial/FinVoucherEntryMapper.xml b/src/main/resources/mapper/account/financial/FinVoucherEntryMapper.xml
new file mode 100644
index 0000000..56633ba
--- /dev/null
+++ b/src/main/resources/mapper/account/financial/FinVoucherEntryMapper.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.account.mapper.financial.FinVoucherEntryMapper">
+
+    <resultMap id="BaseResultMap" type="com.ruoyi.account.pojo.financial.FinVoucherEntry">
+        <id column="id" property="id"/>
+        <result column="voucher_id" property="voucherId"/>
+        <result column="row_no" property="rowNo"/>
+        <result column="subject_code" property="subjectCode"/>
+        <result column="subject_name" property="subjectName"/>
+        <result column="summary" property="summary"/>
+        <result column="debit" property="debit"/>
+        <result column="credit" property="credit"/>
+        <result column="auxiliary_type" property="auxiliaryType"/>
+        <result column="auxiliary_id" property="auxiliaryId"/>
+        <result column="auxiliary_name" property="auxiliaryName"/>
+        <result column="create_user" property="createUser"/>
+        <result column="create_time" property="createTime"/>
+        <result column="update_user" property="updateUser"/>
+        <result column="update_time" property="updateTime"/>
+        <result column="dept_id" property="deptId"/>
+    </resultMap>
+
+    <select id="listPostedEntries" resultType="com.ruoyi.account.bean.vo.financial.FinLedgerEntryRecordVo">
+        SELECT
+            v.voucher_date AS voucherDate,
+            v.voucher_no AS voucherNo,
+            CASE
+                WHEN e.summary IS NOT NULL AND e.summary != '' THEN e.summary
+                ELSE v.summary
+            END AS summary,
+            e.debit AS debit,
+            e.credit AS credit,
+            e.row_no AS rowNo
+        FROM fin_voucher_entry e
+        INNER JOIN fin_voucher v ON e.voucher_id = v.id
+        WHERE v.status = 'posted'
+          AND (e.subject_code = #{subjectCode} OR e.subject_code LIKE CONCAT(#{subjectCode}, '%'))
+          AND v.voucher_date <![CDATA[>=]]> #{startDate}
+          AND v.voucher_date <![CDATA[<=]]> #{endDate}
+        <if test="auxiliaryType != null and auxiliaryType != ''">
+          AND e.auxiliary_type = #{auxiliaryType}
+        </if>
+        <if test="auxiliaryId != null and auxiliaryId != ''">
+          AND e.auxiliary_id = #{auxiliaryId}
+        </if>
+        ORDER BY v.voucher_date ASC, v.id ASC, e.row_no ASC, e.id ASC
+    </select>
+
+    <select id="listPostedEntriesBefore" resultType="com.ruoyi.account.bean.vo.financial.FinLedgerEntryRecordVo">
+        SELECT
+            v.voucher_date AS voucherDate,
+            v.voucher_no AS voucherNo,
+            CASE
+                WHEN e.summary IS NOT NULL AND e.summary != '' THEN e.summary
+                ELSE v.summary
+            END AS summary,
+            e.debit AS debit,
+            e.credit AS credit,
+            e.row_no AS rowNo
+        FROM fin_voucher_entry e
+        INNER JOIN fin_voucher v ON e.voucher_id = v.id
+        WHERE v.status = 'posted'
+          AND (e.subject_code = #{subjectCode} OR e.subject_code LIKE CONCAT(#{subjectCode}, '%'))
+          AND v.voucher_date <![CDATA[<]]> #{beforeDate}
+        <if test="auxiliaryType != null and auxiliaryType != ''">
+          AND e.auxiliary_type = #{auxiliaryType}
+        </if>
+        <if test="auxiliaryId != null and auxiliaryId != ''">
+          AND e.auxiliary_id = #{auxiliaryId}
+        </if>
+    </select>
+
+</mapper>
diff --git a/src/main/resources/mapper/device/DeviceLedgerMapper.xml b/src/main/resources/mapper/device/DeviceLedgerMapper.xml
index c980120..f3b674e 100644
--- a/src/main/resources/mapper/device/DeviceLedgerMapper.xml
+++ b/src/main/resources/mapper/device/DeviceLedgerMapper.xml
@@ -88,7 +88,7 @@
         where id = #{id}
     </select>
     <select id="getDeviceTypeDistributionByYear"
-            resultType="com.ruoyi.account.dto.DeviceTypeDetail"
+            resultType="com.ruoyi.account.bean.dto.DeviceTypeDetail"
             parameterType="java.lang.Integer">
         SELECT
             `type`,

--
Gitblit v1.9.3