From 90b3ad820eea45d021835197eac5bc3ad55b12ed Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期二, 13 十月 2026 11:31:17 +0800
Subject: [PATCH] feat(finance): 新增财务核算模块及 ERP 统计接口

---
 docs/project-business/data/roles.json                                                                                                          |    2 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/dataobject/asset/FinFixedAssetDO.java                                   |  114 +
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinSubjectTypeEnum.java                                               |   40 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/subject/FinAccountSubjectController.java                   |  148 +
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/ledger/FinLedgerServiceImpl.java                                    |  227 ++
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/redis/no/FinNoRedisDAO.java                                             |   46 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinIntangibleAssetSaveReqVO.java                  |   81 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/ledger/vo/FinLedgerRespVO.java                             |   73 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/asset/FinFixedAssetServiceImpl.java                                 |  203 ++
 docs/project-business/mermaid/08-module-relation.mmd                                                                                           |    3 
 yudao-server/pom.xml                                                                                                                           |    7 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/mysql/ledger/FinLedgerMapper.java                                       |   49 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/mysql/voucher/FinVoucherMapper.java                                     |   58 
 sql/mysql/finance_module_init.sql                                                                                                              |  331 +++
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/voucher/FinVoucherService.java                                      |  129 +
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/statistics/vo/FinStatisticsTrendRespVO.java                |   30 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/ledger/FinLedgerController.java                            |   69 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/statistics/FinStatisticsController.java                    |   46 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/subject/FinAccountSubjectService.java                               |  101 +
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/dataobject/subject/FinAccountSubjectDO.java                             |   66 
 docs/project-business/index.html                                                                                                               |   99 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinVoucherStatusEnum.java                                             |   51 
 yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/finance/ErpFinanceStatisticsApi.java                                        |   31 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/mysql/subject/FinAccountSubjectMapper.java                              |   48 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinIntangibleAssetRespVO.java                     |   91 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinFixedAssetStatusEnum.java                                          |   39 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/asset/FinIntangibleAssetService.java                                |   94 
 yudao-module-finance/src/main/resources/mapper/ledger/FinLedgerMapper.xml                                                                      |   42 
 yudao-module-finance/pom.xml                                                                                                                   |   70 
 docs/project-business/data/flows.json                                                                                                          |    9 
 docs/project-business/README.md                                                                                                                |    7 
 sql/mysql/finance_menu_init.sql                                                                                                                |   85 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/dataobject/ledger/FinLedgerEntryDO.java                                 |   56 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/asset/FinIntangibleAssetServiceImpl.java                            |  215 ++
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinIntangibleAssetPageReqVO.java                  |   39 
 yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/finance/dto/ErpFinanceMonthlyStatisticsRespDTO.java                         |   27 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/voucher/vo/FinVoucherPageReqVO.java                        |   36 
 yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java                                           |    4 
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/api/finance/ErpFinanceStatisticsApiImpl.java                                        |   35 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/dataobject/asset/FinIntangibleAssetDO.java                              |   93 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/redis/RedisKeyConstants.java                                            |   18 
 yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java                                            |    8 
 yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/ColumnWidthMatchStyleStrategy.java |    8 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinAuxiliaryTypeEnum.java                                             |   40 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinIntangibleAssetAmortizeRespVO.java             |   18 
 yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java                                                    |    8 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinFixedAssetDepreciateRespVO.java                |   18 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/statistics/vo/FinStatisticsReqVO.java                      |   22 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/voucher/vo/FinVoucherSaveReqVO.java                        |   51 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/asset/FinFixedAssetService.java                                     |   93 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinBalanceDirectionEnum.java                                          |   49 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/statistics/FinStatisticsService.java                                |   39 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinFixedAssetPageReqVO.java                       |   42 
 docs/project-business/data/modules.json                                                                                                        |    8 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/subject/vo/FinAccountSubjectSaveReqVO.java                 |   52 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/dataobject/voucher/FinVoucherDO.java                                    |   71 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/DictTypeConstants.java                                                |   17 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/statistics/FinStatisticsServiceImpl.java                            |  182 +
 pom.xml                                                                                                                                        |    2 
 yudao-server/src/main/resources/application-trt.yaml                                                                                           |    2 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/asset/FinAssetAmountUtils.java                                      |   53 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/ErrorCodeConstants.java                                               |   56 
 docs/project-business/mermaid/09-data-flow.mmd                                                                                                 |   18 
 yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java                                                |    6 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinFixedAssetSaveReqVO.java                       |   83 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/subject/vo/FinAccountSubjectRespVO.java                    |   63 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/voucher/vo/FinVoucherEntryRespVO.java                      |   56 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/mysql/asset/FinIntangibleAssetMapper.java                               |   41 
 yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpFinanceStatisticsMapper.java                                   |   32 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/voucher/vo/FinVoucherRespVO.java                           |   89 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/FinFixedAssetController.java                         |  188 +
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinIntangibleAssetStatusEnum.java                                     |   38 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/voucher/FinVoucherServiceImpl.java                                  |  389 +++
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/dataobject/voucher/FinVoucherEntryDO.java                               |   82 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/ledger/FinLedgerService.java                                        |   36 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/ledger/vo/FinLedgerReqVO.java                              |   36 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/FinIntangibleAssetController.java                    |  139 +
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinFixedAssetRespVO.java                          |  106 +
 docs/project-business/data/business.json                                                                                                       |    9 
 docs/project-business/mermaid/01-business-mindmap.mmd                                                                                          |    8 
 docs/project-business/data/objects.json                                                                                                        |    5 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/subject/FinAccountSubjectServiceImpl.java                           |  157 +
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/mysql/asset/FinFixedAssetMapper.java                                    |   42 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/voucher/FinVoucherController.java                          |  204 ++
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinLedgerRowTypeEnum.java                                             |   37 
 yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java                                        |    5 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/mysql/voucher/FinVoucherEntryMapper.java                                |   47 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/framework/web/config/FinanceWebConfiguration.java                           |   24 
 docs/project-business/mermaid/10-business-object.mmd                                                                                           |    4 
 yudao-module-erp/src/main/resources/mapper/finance/ErpFinanceStatisticsMapper.xml                                                              |   65 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/statistics/vo/FinStatisticsOverviewRespVO.java             |   78 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/voucher/vo/FinVoucherEntrySaveReqVO.java                   |   43 
 yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/subject/vo/FinAccountSubjectPageReqVO.java                 |   27 
 93 files changed, 5,991 insertions(+), 17 deletions(-)

diff --git a/docs/project-business/README.md b/docs/project-business/README.md
index 3dd56c5..4768f1b 100644
--- a/docs/project-business/README.md
+++ b/docs/project-business/README.md
@@ -14,12 +14,12 @@
 ```text
 docs/project-business/
 鈹溾攢鈹� index.html      # 鍙鍖栭〉闈細鍙屽嚮鐩存帴鎵撳紑锛坒ile:// 鍗冲彲锛屾棤闇�浠讳綍鏈嶅姟锛�
-鈹溾攢鈹� mermaid/        # 11 寮� Mermaid 涓氬姟鍥撅紙鏉冨▉婧愶紝鏀硅繖閲岋級
+鈹溾攢鈹� mermaid/        # 12 寮� Mermaid 涓氬姟鍥撅紙鏉冨▉婧愶紝鏀硅繖閲岋級
 鈹溾攢鈹� data/           # 涓氬姟鏁版嵁锛堟潈濞佹簮锛屾敼杩欓噷锛�
 鈹�   鈹溾攢鈹� business.json  # 椤圭洰鍏冧俊鎭�/涓氬姟鍗侀棶/AI鑳藉姏/寰呯‘璁や簨椤�
-鈹�   鈹溾攢鈹� modules.json   # 12 涓笟鍔℃ā鍧楀畾涔�
+鈹�   鈹溾攢鈹� modules.json   # 14 涓笟鍔℃ā鍧楀畾涔�
 鈹�   鈹溾攢鈹� objects.json   # 鏍稿績涓氬姟瀵硅薄锛堜綔鐢�/涓婃父/涓嬫父/琛ㄥ悕锛�
-鈹�   鈹溾攢鈹� flows.json     # 7 鏉℃祦绋嬶紙鎽樿+姝ラ+瑙掕壊锛�
+鈹�   鈹溾攢鈹� flows.json     # 8 鏉℃祦绋嬶紙鎽樿+姝ラ+瑙掕壊锛�
 鈹�   鈹斺攢鈹� roles.json     # 瑙掕壊鏉冮檺
 鈹斺攢鈹� README.md
 ```
@@ -45,6 +45,7 @@
 | 09-data-flow | 璺ㄦā鍧楁暟鎹祦锛堝崟鎹敓鎴愩�佸瓧娈靛甫鍏ャ�佹暟閲忓洖鍐欍�佷簨浠讹級 |
 | 10-business-object | 鏍稿績涓氬姟瀵硅薄 ER锛堝彧鍚笟鍔″璞★紝闈炲叏搴撹〃锛� |
 | 11-ai-business-flow | AI 涓氬姟閾捐矾锛圤CR/璐ㄦ鍒ゅ畾/鍥涚被棰勬祴/RAG锛屽惈 API 渚濊禆锛� |
+| 12-finance-flow | 浼氳鏍哥畻锛氱鐩缓璐︹啋鍑瘉褰曞叆(鍊熻捶骞宠 鏍¢獙)鈫掕繃璐�/浣滃簾鈫掓�昏处鏄庣粏璐︹啋璧勪骇鎶樻棫鎽婇攢鈫掔湅鏉垮彇鏁� ERP 宸插鏍稿崟鎹� |
 
 鍥句腑鐘舵�佹暟瀛楀潎涓烘簮鐮佹灇涓剧湡瀹炲�硷紙濡傚伐鍗� 0鑽夌/1宸茬‘璁�/2瀹屾垚/3鍙栨秷锛汦RP 瀹℃壒 15瀹℃壒涓�/20宸插鏍革級銆�
 
diff --git a/docs/project-business/data/business.json b/docs/project-business/data/business.json
index 8442228..658e9c8 100644
--- a/docs/project-business/data/business.json
+++ b/docs/project-business/data/business.json
@@ -21,9 +21,9 @@
     "frontend": "mom-pro2-before锛圴ue3 绠$悊鍚庡彴锛屽悓浠撳簱瀛樻斁锛屽彧璇诲弬鑰冿級"
   },
   "stats": {
-    "businessModules": 13,
-    "coreObjects": 48,
-    "documentFlows": 6,
+    "businessModules": 14,
+    "coreObjects": 53,
+    "documentFlows": 7,
     "approvalListeners": 13,
     "aiCapabilities": 8,
     "wmBillTypes": 15
@@ -143,7 +143,8 @@
     "MesWmSnDO 鏂板鐨� uuid 瀛楁鐢熸垚/娑堣垂閫昏緫鏈湪鏍稿績 Service 涓‘璁�",
     "杞鐢宠(hrm_regularize_application)銆佸煿璁瘉涔︾瓑浠呮湁 SQL 涓庤彍鍗曞垵濮嬪寲锛孞ava 渚� Service/Controller 鏈疄鐜�",
     "NCR 澶勭疆缁撴灉锛堣繑宸�/閫�璐э級鏈洖鍐欒Е鍙戞簮鍗曟嵁锛屼粎鍙拌处璁板綍",
-    "MPS 鐘舵�佹灇涓剧嫭绔嬬紪鍙�(0/1/2/3)锛屼笌閫氱敤鍗曟嵁甯搁噺(0/1/4/5)涓嶄竴鑷�"
+    "MPS 鐘舵�佹灇涓剧嫭绔嬬紪鍙�(0/1/2/3)锛屼笌閫氱敤鍗曟嵁甯搁噺(0/1/4/5)涓嶄竴鑷�",
+    "璐㈠姟绠$悊锛堜細璁℃牳绠楋級妯″潡 yudao-module-finance 浠g爜宸茶惤鍦帮紝浣嗏憼鑿滃崟/鏉冮檺鏈厤缃紙闇�鑷鍦ㄨ彍鍗曠鐞嗗缓 finance:* 鏉冮檺锛夆憽寤鸿〃涓庡瓧鍏歌剼鏈湭鍚屾杩� docs/sql/config_export_all_<鏃ユ湡>.sql 闃舵涓�锛屾寜鐜版湁鍚堝苟鑴氭湰鍒濆鍖栨柊搴撲細缂� fin_* 琛ㄤ笌 6 缁� finance_* 瀛楀吀銆愬緟纭銆�"
   ],
   "generatedAt": "2026-09-10",
   "analysisBasis": "鍩轰簬 dev_business 鍒嗘敮 87bf4a99 婧愮爜锛�6 缁勫苟琛屾簮鐮佽蛋璇伙紙MES 鐢熶骇璐ㄩ噺 / MES 浠撳偍 / ERP / CRM+SRM / 鍞悗+HRM / AI+BI+MDM+BPM+搴曞骇锛�"
diff --git a/docs/project-business/data/flows.json b/docs/project-business/data/flows.json
index 496b348..4efbd74 100644
--- a/docs/project-business/data/flows.json
+++ b/docs/project-business/data/flows.json
@@ -54,6 +54,15 @@
        {"stage": "鍒ゅ畾", "action": "闂鍒ゅ畾: 涓�鑸�/缁翠慨/閫�璐э紝涓ラ噸搴� 0-3", "roles": ["鍞悗宸ョ▼甯�"]},
        {"stage": "缁翠慨", "action": "鐜板満/杩斿巶/杩滅▼缁翠慨锛岃垂鐢ㄤ笌缁撴灉璁板綍锛屽畬鎴愯仈鍔ㄥ伐鍗曞畬缁�", "roles": ["缁翠慨宸ョ▼甯�"]},
        {"stage": "閫�璐�", "action": "閫�璐х敵璇峰鏍糕啋鑷姩寤� MES 閿�鍞��璐у崟(RQC)鈫掕川妫�/鍏ュ簱鍥炶皟鈫掗��娆惧洖璋冣啋鍏抽棴", "roles": ["鍞悗", "璐ㄦ", "璐㈠姟"]}]},
+    {"id": "finance", "name": "浼氳鏍哥畻娴佺▼", "mermaid": "mermaid/12-finance-flow.mmd", "color": "#3ec9a7",
+     "summary": "鐙珛鏍哥畻妯″潡 yudao-module-finance锛氫細璁$鐩缓璐� 鈫� 璁拌处鍑瘉褰曞叆(鍊熻捶骞宠 鏍¢獙) 鈫� 杩囪处 鈫� 鎬昏处/鏄庣粏璐� 鈫� 璧勪骇璁℃彁鎶樻棫涓庢憡閿� 鈫� 璐㈠姟鎶ヨ〃鐪嬫澘鍙栨暟 ERP 宸插鏍稿崟鎹��",
+     "steps": [
+       {"stage": "寤鸿处", "action": "缁存姢浼氳绉戠洰 tree(subjectCode 鍞竴锛屽彲寤轰笅绾�)锛岄娆″彲瀵煎叆绾� 68 鏉′竴绾х鐩瀛�", "roles": ["浼氳"]},
+       {"stage": "璁拌处", "action": "褰曞叆鍑瘉(瀛楀彿鐣欑┖鑷姩鐢熸垚)+鍒嗗綍锛屽垎褰曢�愯閫夌鐩苟濉��/璐凤紱鏍¢獙鑷冲皯涓�琛屻�佸�熻捶閮戒笉寰楀悓鏃跺ぇ浜�0銆佸悎璁$浉绛変笖澶т簬0", "roles": ["浼氳"]},
+       {"stage": "杩囪处", "action": "鏈繃璐�0 鈫� post 杩囪处 鈫� 宸茶繃璐�10(涓嶅彲鍐嶄慨鏀�)锛涙湭杩囪处0 鎴� 宸茶繃璐�10 鍧囧彲 cancel 浣滃簾 鈫� 宸蹭綔搴�20", "roles": ["浼氳", "璐㈠姟涓荤"]},
+       {"stage": "鏌ヨ处", "action": "鎬昏处鎸夋湡闂撮�愭湀姹囨�汇�佹槑缁嗚处閫愭潯鍒嗗綍骞剁粨杞綑棰濓紝涓よ�呭彧缁熻宸茶繃璐�10鐨勫嚟璇侊紝骞舵敮鎸佸鍑�", "roles": ["浼氳", "璐㈠姟涓荤"]},
+       {"stage": "璧勪骇", "action": "鍥哄畾璧勪骇(1鍦ㄧ敤/2闂茬疆/3缁翠慨涓�/4宸叉姤搴�)涓庢棤褰㈣祫浜�(1鍦ㄧ敤/2宸插け鏁�/3宸叉憡閿�瀹�)璁℃彁锛氫紶 ids 鎸夐�変腑璁℃彁锛宨ds 涓虹┖瀵瑰叏閮ㄦ湭鎶ュ簾/鏈け鏁堣祫浜ц鎻愪竴涓湀", "roles": ["璧勪骇浼氳"]},
+       {"stage": "鎶ヨ〃", "action": "鐪嬫澘姹囨�昏祫浜т笌鍑瘉鏃剁偣鏁� + 缁� ErpFinanceStatisticsApi 鍙� ERP 宸插鏍告敹璐т粯娆句笌璐攢閲戦锛岃緭鍑烘湀搴︽敹鏀秼鍔�", "roles": ["璐㈠姟涓荤", "绠$悊灞�"]}]},
     {"id": "ai", "name": "AI 涓氬姟娴佺▼", "mermaid": "mermaid/11-ai-business-flow.mmd", "color": "#56c8d8",
      "summary": "缁熶竴妯″紡锛氫笟鍔′晶鏀堕泦涓婁笅鏂�(蹇呰鏃惰皟 MDM/SRM/CRM API 鍙栧悕绉�)鈫掔粍瑁� Prompt鈫抍hat/chatWithImage鈫掕В鏋� JSON鈫掑墠绔濉垨寤鸿锛涙棤 Function Calling锛汻AG 鐭ヨ瘑搴撳凡寤烘垚浣嗘殏鏃犱笟鍔℃秷璐广��",
      "steps": [
diff --git a/docs/project-business/data/modules.json b/docs/project-business/data/modules.json
index 4c1407b..152fdaf 100644
--- a/docs/project-business/data/modules.json
+++ b/docs/project-business/data/modules.json
@@ -32,6 +32,14 @@
      "roles": ["閲囪喘鍛�", "閿�鍞憳", "鍑虹撼", "浼氳"],
      "upstream": ["CRM(鍚堝悓)", "SRM(瀹氭爣)", "涓绘暟鎹�"], "downstream": ["鐢熶骇(MPS)", "浠撳偍(鏀跺彂)"],
      "coreTables": ["erp_purchase_request", "erp_purchase_order", "erp_purchase_invoice", "erp_finance_payment", "erp_sale_order", "erp_finance_receipt", "erp_account"]},
+    {"key": "finance", "name": "璐㈠姟绠$悊锛堜細璁℃牳绠楋級", "color": "#3ec9a7", "tag": "FIN", "flow": "finance",
+     "positioning": "鐙珛鏍哥畻妯″潡锛氫細璁℃牳绠� + 璧勪骇鏍哥畻 + 璐㈠姟鎶ヨ〃鐪嬫澘锛堜笌 ERP 杩涢攢瀛樿储鍔″崟鎹垎绂伙紝yudao-module-finance锛�",
+     "problem": "杩涢攢瀛樺彧绠″崟鎹笉绠¤处锛岀己灏戜細璁$鐩�/鍑瘉/璐︾翱涓庤祫浜ф姌鏃ф憡閿�娌夋穩锛屾棤娉曞嚭璐﹁〃涓庤祫浜у噣鍊�",
+     "features": ["浼氳绉戠洰鏍�(绾�68鏉′竴绾х鐩瀛惵峰彲寤轰笅绾�)", "璁拌处鍑瘉(瀛楀彿鑷姩鐢熸垚路鍒嗗綍鍊熻捶骞宠 鍓嶅悗绔弻閲嶆牎楠�)", "鍑瘉鐘舵�佹満 鏈繃璐�0鈫掕繃璐�10/浣滃簾20锛屽凡杩囪处涓嶅彲鏀�", "鎬昏处鎸夋湡闂撮�愭湀姹囨��", "鏄庣粏璐﹂�愭潯鍒嗗綍骞剁粨杞綑棰�", "鍥哄畾璧勪骇+璁℃彁鎶樻棫", "鏃犲舰璧勪骇+璁℃彁鎽婇攢", "浼氳鏍哥畻鐪嬫澘(鏀舵敮/璧勪骇/鍑瘉/鏈堝害瓒嬪娍)", "闄勪欢璧扮郴缁熼檮浠朵腑蹇� recordType=fin_voucher"],
+     "objects": ["浼氳绉戠洰", "璁拌处鍑瘉", "鍑瘉鍒嗗綍", "鍥哄畾璧勪骇", "鏃犲舰璧勪骇"],
+     "roles": ["浼氳", "璐㈠姟涓荤", "璧勪骇浼氳", "绠$悊灞�"],
+     "upstream": ["杩涢攢瀛�(ERP 宸插鏍告敹璐�/浠樻/璐攢鍗曟嵁)"], "downstream": ["璐㈠姟鎶ヨ〃鐪嬫澘", "BI 绠$悊椹鹃┒鑸�"],
+     "coreTables": ["fin_account_subject", "fin_voucher", "fin_voucher_entry", "fin_fixed_asset", "fin_intangible_asset"]},
     {"key": "pro", "name": "鐢熶骇绠$悊", "color": "#e0b34f", "tag": "MES-PRO", "flow": "production",
      "positioning": "鐢熶骇鎵ц鏍稿績锛氳鍒�-宸ュ崟-浠诲姟-鎶ュ伐",
      "problem": "璁㈠崟鍒拌溅闂存墽琛岀殑閫愬眰鎷嗚В涓庤繃绋嬪彲鎺�",
diff --git a/docs/project-business/data/objects.json b/docs/project-business/data/objects.json
index 97cab45..36a96df 100644
--- a/docs/project-business/data/objects.json
+++ b/docs/project-business/data/objects.json
@@ -19,6 +19,11 @@
     {"name": "閲囪喘鏉ョエ", "module": "杩涢攢瀛�", "table": "erp_purchase_invoice", "doc": "FPD缂栧彿锛孉I OCR 棰勫~锛岀疮璁♀墹璁㈠崟鎬讳环锛屼粯娆惧崟鐨勫墠缃�", "upstream": ["閲囪喘璁㈠崟"], "downstream": ["浠樻鍗�"], "keywords": "鏉ョエ 鍙戠エ 閲囪喘鍙戠エ"},
     {"name": "浠樻鍗�", "module": "璐㈠姟", "table": "erp_finance_payment", "doc": "FKD缂栧彿锛屽繀椤诲叧鑱旀潵绁�+闄勪欢锛岀疮璁♀墹鏉ョエ閲戦锛孊PM 瀹℃壒", "upstream": ["閲囪喘鏉ョエ"], "downstream": ["缁撶畻璐︽埛"], "keywords": "浠樻 搴斾粯 payment"},
     {"name": "鏀舵鍗�", "module": "璐㈠姟", "table": "erp_finance_receipt", "doc": "SKD缂栧彿锛屽叧鑱� CRM 瀹㈡埛鏍¢獙锛屾棤 BPM(鎵嬪姩瀹℃牳)", "upstream": ["閿�鍞�(寮卞叧鑱�)"], "downstream": ["缁撶畻璐︽埛"], "keywords": "鏀舵 搴旀敹 receipt"},
+    {"name": "浼氳绉戠洰", "module": "浼氳鏍哥畻", "table": "fin_account_subject", "doc": "绉戠洰鏍戯紝parent_id 鑷叧鑱旓紱绉戠洰绫诲瀷1璧勪骇2璐熷��3鏉冪泭4鎴愭湰5鎹熺泭锛屼綑棰濇柟鍚�1鍊�2璐凤紝鐘舵��0鍚敤1绂佺敤锛涜处琛ㄤ笌鍒嗗綍缁熶竴鎸� subject_code 鍙栨暟", "upstream": ["浼氳绉戠洰绉嶅瓙绾�68鏉′竴绾х鐩�"], "downstream": ["璁拌处鍑瘉鍒嗗綍", "鎬昏处", "鏄庣粏璐�"], "keywords": "浼氳绉戠洰 绉戠洰 subject account 璧勪骇绫� 璐熷�虹被 鎹熺泭绫�"},
+    {"name": "璁拌处鍑瘉", "module": "浼氳鏍哥畻", "table": "fin_voucher", "doc": "鍑瘉瀛楀彿鍙暀绌虹敱鍚庣鑷姩鐢熸垚锛涚姸鎬� 鏈繃璐�0/宸茶繃璐�10/宸蹭綔搴�20锛涘凡杩囪处涓嶅彲鏀癸紝鏈繃璐︿笌宸茶繃璐﹀潎鍙綔搴燂紝鏈繃璐︿笌宸蹭綔搴熷彲鍒犻櫎锛涢檮浠舵寜 recordType=fin_voucher 鎸傜郴缁熼檮浠朵腑蹇�", "upstream": ["浼氳绉戠洰"], "downstream": ["鎬昏处", "鏄庣粏璐�", "璐㈠姟鎶ヨ〃鐪嬫澘"], "keywords": "璁拌处鍑瘉 鍑瘉 voucher 杩囪处 浣滃簾 鍊熻捶骞宠 "},
+    {"name": "鍑瘉鍒嗗綍", "module": "浼氳鏍哥畻", "table": "fin_voucher_entry", "doc": "鍑瘉涓嬮�愯鍒嗗綍锛氱鐩紪鐮�+鍊�/璐烽噾棰�+杈呭姪鏍哥畻(1瀹㈡埛2渚涘簲鍟�3閮ㄩ棬4鍛樺伐5椤圭洰)锛涗竴琛屼笉鍙�熻捶鍚屾椂澶т簬0锛屽叏琛ㄥ�熻捶鍚堣蹇呴』鐩哥瓑涓斿ぇ浜�0", "upstream": ["璁拌处鍑瘉", "浼氳绉戠洰"], "downstream": ["鎬昏处", "鏄庣粏璐�"], "keywords": "鍑瘉鍒嗗綍 鍒嗗綍 entry 鍊熸柟 璐锋柟 杈呭姪鏍哥畻"},
+    {"name": "鍥哄畾璧勪骇", "module": "浼氳鏍哥畻", "table": "fin_fixed_asset", "doc": "璧勪骇鍗$墖锛岀姸鎬� 1鍦ㄧ敤/2闂茬疆/3缁翠慨涓�/4宸叉姤搴燂紱鏈堟姌鏃ч=(鍘熷�济�(1-娈嬪�肩巼))梅(骞撮檺脳12)锛岀疮璁℃姌鏃т笌鍑�鍊肩敱璁℃彁绱姞锛屼笉鎺ユ敹鍓嶇浼犲��", "upstream": [], "downstream": ["璐㈠姟鎶ヨ〃鐪嬫澘", "鎬昏处"], "keywords": "鍥哄畾璧勪骇 鎶樻棫 璧勪骇鍗$墖 fixed asset 娈嬪�肩巼"},
+    {"name": "鏃犲舰璧勪骇", "module": "浼氳鏍哥畻", "table": "fin_intangible_asset", "doc": "鏃犲舰璧勪骇鍗$墖(璇佷功缂栧彿/鏈夋晥鏈�)锛岀姸鎬� 1鍦ㄧ敤/2宸插け鏁�/3宸叉憡閿�瀹岋紱鏈堟憡閿�棰�=(鍘熷�济�(1-娈嬪�肩巼))梅(鎽婇攢骞撮檺脳12)锛岀疮璁℃憡閿�涓庡噣鍊肩敱璁℃彁绱姞", "upstream": [], "downstream": ["璐㈠姟鎶ヨ〃鐪嬫澘", "鎬昏处"], "keywords": "鏃犲舰璧勪骇 鎽婇攢 杞欢钁椾綔鏉� 涓撳埄 intangible 鏈夋晥鏈�"},
     {"name": "閿�鍞鍗�", "module": "杩涢攢瀛�", "table": "erp_sale_order", "doc": "XSDD缂栧彿锛孊PM 瀹℃壒锛岄渶鐢熶骇琛岃嚜鍔ㄥ缓 MPS锛屽嚭搴撶姸鎬�:鏈�0宸查鐣�1閮ㄥ垎2鍏ㄩ儴3锛岀敓浜у畬鎴愪簨浠跺洖鍐�", "upstream": ["鍚堝悓"], "downstream": ["MPS", "鍙戣揣閫氱煡", "閿�鍞嚭搴�", "鍞悗宸ュ崟"], "keywords": "閿�鍞鍗� sale"},
     {"name": "涓荤敓浜ц鍒�", "module": "鐢熶骇", "table": "mes_pro_mps", "doc": "MPS璁″垝锛岃崏绋�0鈫掑凡涓嬪彂1鈫掑畬鎴�2/鍙栨秷3锛屽彲鍚堝苟涓嬪彂", "upstream": ["閿�鍞鍗�"], "downstream": ["鐢熶骇宸ュ崟"], "keywords": "MPS 涓昏鍒� 鐢熶骇璁″垝"},
     {"name": "鐢熶骇宸ュ崟", "module": "鐢熶骇", "table": "mes_pro_work_order", "doc": "鑽夌0鈫掑凡纭1鈫掑畬鎴�2/鍙栨秷3锛岀‘璁ゆ椂鎸夊伐鑹鸿矾绾跨敓鎴愪换鍔★紝鏈�鍚庝竴閬撳伐搴忔姤宸ュ洖鍐欏凡鐢熶骇鏁伴噺", "upstream": ["MPS"], "downstream": ["鐢熶骇浠诲姟", "棰嗘枡", "瀹屽伐鍏ュ簱"], "keywords": "宸ュ崟 鐢熶骇宸ュ崟 work order"},
diff --git a/docs/project-business/data/roles.json b/docs/project-business/data/roles.json
index 4980d2b..bf74e87 100644
--- a/docs/project-business/data/roles.json
+++ b/docs/project-business/data/roles.json
@@ -17,7 +17,7 @@
     {"role": "璐ㄦ鍛�", "modules": ["MES-QC"], "operations": ["IQC/IPQC/OQC/RQC 妫�楠�", "NCR 璇勫澶勭疆"], "data": ["mes_qc_*"], "scope": "inspectorUserId 褰掑睘"},
     {"role": "璁惧鍛�/缁翠慨宸�", "modules": ["MES-DV"], "operations": ["鐐规淇濆吇", "缁翠慨宸ュ崟", "宸ヨ棰嗙敤"], "data": ["mes_dv_*", "mes_tm_*"], "scope": "chargeUserId 褰掑睘"},
     {"role": "瀹㈡湇/鍞悗宸ョ▼甯�", "modules": ["鍞悗"], "operations": ["宸ュ崟鍙楃悊娲惧伐", "缁翠慨", "閫�璐х敵璇�"], "data": ["after_sale_*"], "scope": "琛岀骇鏁版嵁鏉冮檺鍥涜鍥撅細鎴戣礋璐�/鎴戝弬涓�/涓嬪睘璐熻矗/鍏ㄩ儴(aftersales:view-all)"},
-    {"role": "璐㈠姟", "modules": ["ERP", "CRM"], "operations": ["鏀舵鍗�", "浠樻鍗�", "寮�绁ㄥ鏍�", "缁撶畻璐︽埛"], "data": ["erp_finance_*", "crm_invoice", "crm_receivable"], "scope": "financeUserId 褰掑睘"},
+    {"role": "璐㈠姟", "modules": ["ERP", "CRM", "FIN"], "operations": ["鏀舵鍗�", "浠樻鍗�", "寮�绁ㄥ鏍�", "缁撶畻璐︽埛", "浼氳绉戠洰缁存姢", "璁拌处鍑瘉褰曞叆涓庤繃璐�/浣滃簾", "鎬昏处涓庢槑缁嗚处鏌ヨ", "鍥哄畾璧勪骇鎶樻棫璁℃彁", "鏃犲舰璧勪骇鎽婇攢璁℃彁", "浼氳鏍哥畻鐪嬫澘"], "data": ["erp_finance_*", "crm_invoice", "crm_receivable", "fin_account_subject", "fin_voucher", "fin_voucher_entry", "fin_fixed_asset", "fin_intangible_asset"], "scope": "financeUserId 褰掑睘锛涜储鍔℃牳绠楁ā鍧楁寜 finance:* 鎺ュ彛鏉冮檺 + system_menu 鎺堟潈锛屾殏鏃犳暟鎹潈闄愬瓧娈�"},
     {"role": "HR", "modules": ["HRM"], "operations": ["鍛樺伐妗f鍏ヨ浆璋冪", "鑰冨嫟瑙勫垯鎺掔彮", "钖叕鏍哥畻鍙戞斁", "绂昏亴浜ゆ帴"], "data": ["hrm_*"], "scope": "閮ㄩ棬鑼冨洿 dataScope"},
     {"role": "绠$悊灞�", "modules": ["BI"], "operations": ["鏌ョ湅閲囪喘閿�鍞�/浠撳簱/鐢熶骇璁惧/璐ㄩ噺/浜哄姏浜斿ぇ涓婚澶у睆"], "data": ["bi_dashboard"], "scope": "鍙"}
   ]
diff --git a/docs/project-business/index.html b/docs/project-business/index.html
index f9036ad..82db651 100644
--- a/docs/project-business/index.html
+++ b/docs/project-business/index.html
@@ -111,6 +111,7 @@
   <a data-t="flow-quality"><span class="dot" style="background:var(--c-qc)"></span>璐ㄩ噺娴佺▼</a>
   <a data-t="flow-warehouse"><span class="dot" style="background:var(--c-wm)"></span>浠撳偍娴佺▼</a>
   <a data-t="flow-aftersales"><span class="dot" style="background:var(--c-as)"></span>鍞悗娴佺▼</a>
+  <a data-t="flow-finance"><span class="dot" style="background:var(--c-fin)"></span>浼氳鏍哥畻</a>
   <div class="group">缁撴瀯涓庢暟鎹�</div>
   <a data-t="relation"><span class="dot" style="background:var(--c-bpm)"></span>绯荤粺鍏崇郴</a>
   <a data-t="dataflow"><span class="dot" style="background:var(--c-fin)"></span>鏁版嵁娴�</a>
@@ -137,6 +138,7 @@
 <section class="page" id="flow-quality"></section>
 <section class="page" id="flow-warehouse"></section>
 <section class="page" id="flow-aftersales"></section>
+<section class="page" id="flow-finance"></section>
 <section class="page" id="relation">
   <h2 class="pt">绯荤粺鍏崇郴</h2>
   <p class="desc">妯″潡涔嬮棿鐨勮皟鐢ㄤ笌鏁版嵁渚濊禆锛氬疄绾�=鍗曟嵁/鏁版嵁娴佸悜锛岃櫄绾�=瀹℃壒鍥炶皟涓庤緟鍔╄兘鍔涖��</p>
@@ -201,9 +203,9 @@
     "frontend": "mom-pro2-before锛圴ue3 绠$悊鍚庡彴锛屽悓浠撳簱瀛樻斁锛屽彧璇诲弬鑰冿級"
   },
   "stats": {
-    "businessModules": 13,
-    "coreObjects": 48,
-    "documentFlows": 6,
+    "businessModules": 14,
+    "coreObjects": 53,
+    "documentFlows": 7,
     "approvalListeners": 13,
     "aiCapabilities": 8,
     "wmBillTypes": 15
@@ -323,7 +325,8 @@
     "MesWmSnDO 鏂板鐨� uuid 瀛楁鐢熸垚/娑堣垂閫昏緫鏈湪鏍稿績 Service 涓‘璁�",
     "杞鐢宠(hrm_regularize_application)銆佸煿璁瘉涔︾瓑浠呮湁 SQL 涓庤彍鍗曞垵濮嬪寲锛孞ava 渚� Service/Controller 鏈疄鐜�",
     "NCR 澶勭疆缁撴灉锛堣繑宸�/閫�璐э級鏈洖鍐欒Е鍙戞簮鍗曟嵁锛屼粎鍙拌处璁板綍",
-    "MPS 鐘舵�佹灇涓剧嫭绔嬬紪鍙�(0/1/2/3)锛屼笌閫氱敤鍗曟嵁甯搁噺(0/1/4/5)涓嶄竴鑷�"
+    "MPS 鐘舵�佹灇涓剧嫭绔嬬紪鍙�(0/1/2/3)锛屼笌閫氱敤鍗曟嵁甯搁噺(0/1/4/5)涓嶄竴鑷�",
+    "璐㈠姟绠$悊锛堜細璁℃牳绠楋級妯″潡 yudao-module-finance 浠g爜宸茶惤鍦帮紝浣嗏憼鑿滃崟/鏉冮檺鏈厤缃紙闇�鑷鍦ㄨ彍鍗曠鐞嗗缓 finance:* 鏉冮檺锛夆憽寤鸿〃涓庡瓧鍏歌剼鏈湭鍚屾杩� docs/sql/config_export_all_<鏃ユ湡>.sql 闃舵涓�锛屾寜鐜版湁鍚堝苟鑴氭湰鍒濆鍖栨柊搴撲細缂� fin_* 琛ㄤ笌 6 缁� finance_* 瀛楀吀銆愬緟纭銆�"
   ],
   "generatedAt": "2026-09-10",
   "analysisBasis": "鍩轰簬 dev_business 鍒嗘敮 87bf4a99 婧愮爜锛�6 缁勫苟琛屾簮鐮佽蛋璇伙紙MES 鐢熶骇璐ㄩ噺 / MES 浠撳偍 / ERP / CRM+SRM / 鍞悗+HRM / AI+BI+MDM+BPM+搴曞骇锛�"
@@ -362,6 +365,14 @@
      "roles": ["閲囪喘鍛�", "閿�鍞憳", "鍑虹撼", "浼氳"],
      "upstream": ["CRM(鍚堝悓)", "SRM(瀹氭爣)", "涓绘暟鎹�"], "downstream": ["鐢熶骇(MPS)", "浠撳偍(鏀跺彂)"],
      "coreTables": ["erp_purchase_request", "erp_purchase_order", "erp_purchase_invoice", "erp_finance_payment", "erp_sale_order", "erp_finance_receipt", "erp_account"]},
+    {"key": "finance", "name": "璐㈠姟绠$悊锛堜細璁℃牳绠楋級", "color": "#3ec9a7", "tag": "FIN", "flow": "finance",
+     "positioning": "鐙珛鏍哥畻妯″潡锛氫細璁℃牳绠� + 璧勪骇鏍哥畻 + 璐㈠姟鎶ヨ〃鐪嬫澘锛堜笌 ERP 杩涢攢瀛樿储鍔″崟鎹垎绂伙紝yudao-module-finance锛�",
+     "problem": "杩涢攢瀛樺彧绠″崟鎹笉绠¤处锛岀己灏戜細璁$鐩�/鍑瘉/璐︾翱涓庤祫浜ф姌鏃ф憡閿�娌夋穩锛屾棤娉曞嚭璐﹁〃涓庤祫浜у噣鍊�",
+     "features": ["浼氳绉戠洰鏍�(绾�68鏉′竴绾х鐩瀛惵峰彲寤轰笅绾�)", "璁拌处鍑瘉(瀛楀彿鑷姩鐢熸垚路鍒嗗綍鍊熻捶骞宠 鍓嶅悗绔弻閲嶆牎楠�)", "鍑瘉鐘舵�佹満 鏈繃璐�0鈫掕繃璐�10/浣滃簾20锛屽凡杩囪处涓嶅彲鏀�", "鎬昏处鎸夋湡闂撮�愭湀姹囨��", "鏄庣粏璐﹂�愭潯鍒嗗綍骞剁粨杞綑棰�", "鍥哄畾璧勪骇+璁℃彁鎶樻棫", "鏃犲舰璧勪骇+璁℃彁鎽婇攢", "浼氳鏍哥畻鐪嬫澘(鏀舵敮/璧勪骇/鍑瘉/鏈堝害瓒嬪娍)", "闄勪欢璧扮郴缁熼檮浠朵腑蹇� recordType=fin_voucher"],
+     "objects": ["浼氳绉戠洰", "璁拌处鍑瘉", "鍑瘉鍒嗗綍", "鍥哄畾璧勪骇", "鏃犲舰璧勪骇"],
+     "roles": ["浼氳", "璐㈠姟涓荤", "璧勪骇浼氳", "绠$悊灞�"],
+     "upstream": ["杩涢攢瀛�(ERP 宸插鏍告敹璐�/浠樻/璐攢鍗曟嵁)"], "downstream": ["璐㈠姟鎶ヨ〃鐪嬫澘", "BI 绠$悊椹鹃┒鑸�"],
+     "coreTables": ["fin_account_subject", "fin_voucher", "fin_voucher_entry", "fin_fixed_asset", "fin_intangible_asset"]},
     {"key": "pro", "name": "鐢熶骇绠$悊", "color": "#e0b34f", "tag": "MES-PRO", "flow": "production",
      "positioning": "鐢熶骇鎵ц鏍稿績锛氳鍒�-宸ュ崟-浠诲姟-鎶ュ伐",
      "problem": "璁㈠崟鍒拌溅闂存墽琛岀殑閫愬眰鎷嗚В涓庤繃绋嬪彲鎺�",
@@ -462,6 +473,11 @@
     {"name": "閲囪喘鏉ョエ", "module": "杩涢攢瀛�", "table": "erp_purchase_invoice", "doc": "FPD缂栧彿锛孉I OCR 棰勫~锛岀疮璁♀墹璁㈠崟鎬讳环锛屼粯娆惧崟鐨勫墠缃�", "upstream": ["閲囪喘璁㈠崟"], "downstream": ["浠樻鍗�"], "keywords": "鏉ョエ 鍙戠エ 閲囪喘鍙戠エ"},
     {"name": "浠樻鍗�", "module": "璐㈠姟", "table": "erp_finance_payment", "doc": "FKD缂栧彿锛屽繀椤诲叧鑱旀潵绁�+闄勪欢锛岀疮璁♀墹鏉ョエ閲戦锛孊PM 瀹℃壒", "upstream": ["閲囪喘鏉ョエ"], "downstream": ["缁撶畻璐︽埛"], "keywords": "浠樻 搴斾粯 payment"},
     {"name": "鏀舵鍗�", "module": "璐㈠姟", "table": "erp_finance_receipt", "doc": "SKD缂栧彿锛屽叧鑱� CRM 瀹㈡埛鏍¢獙锛屾棤 BPM(鎵嬪姩瀹℃牳)", "upstream": ["閿�鍞�(寮卞叧鑱�)"], "downstream": ["缁撶畻璐︽埛"], "keywords": "鏀舵 搴旀敹 receipt"},
+    {"name": "浼氳绉戠洰", "module": "浼氳鏍哥畻", "table": "fin_account_subject", "doc": "绉戠洰鏍戯紝parent_id 鑷叧鑱旓紱绉戠洰绫诲瀷1璧勪骇2璐熷��3鏉冪泭4鎴愭湰5鎹熺泭锛屼綑棰濇柟鍚�1鍊�2璐凤紝鐘舵��0鍚敤1绂佺敤锛涜处琛ㄤ笌鍒嗗綍缁熶竴鎸� subject_code 鍙栨暟", "upstream": ["浼氳绉戠洰绉嶅瓙绾�68鏉′竴绾х鐩�"], "downstream": ["璁拌处鍑瘉鍒嗗綍", "鎬昏处", "鏄庣粏璐�"], "keywords": "浼氳绉戠洰 绉戠洰 subject account 璧勪骇绫� 璐熷�虹被 鎹熺泭绫�"},
+    {"name": "璁拌处鍑瘉", "module": "浼氳鏍哥畻", "table": "fin_voucher", "doc": "鍑瘉瀛楀彿鍙暀绌虹敱鍚庣鑷姩鐢熸垚锛涚姸鎬� 鏈繃璐�0/宸茶繃璐�10/宸蹭綔搴�20锛涘凡杩囪处涓嶅彲鏀癸紝鏈繃璐︿笌宸茶繃璐﹀潎鍙綔搴燂紝鏈繃璐︿笌宸蹭綔搴熷彲鍒犻櫎锛涢檮浠舵寜 recordType=fin_voucher 鎸傜郴缁熼檮浠朵腑蹇�", "upstream": ["浼氳绉戠洰"], "downstream": ["鎬昏处", "鏄庣粏璐�", "璐㈠姟鎶ヨ〃鐪嬫澘"], "keywords": "璁拌处鍑瘉 鍑瘉 voucher 杩囪处 浣滃簾 鍊熻捶骞宠 "},
+    {"name": "鍑瘉鍒嗗綍", "module": "浼氳鏍哥畻", "table": "fin_voucher_entry", "doc": "鍑瘉涓嬮�愯鍒嗗綍锛氱鐩紪鐮�+鍊�/璐烽噾棰�+杈呭姪鏍哥畻(1瀹㈡埛2渚涘簲鍟�3閮ㄩ棬4鍛樺伐5椤圭洰)锛涗竴琛屼笉鍙�熻捶鍚屾椂澶т簬0锛屽叏琛ㄥ�熻捶鍚堣蹇呴』鐩哥瓑涓斿ぇ浜�0", "upstream": ["璁拌处鍑瘉", "浼氳绉戠洰"], "downstream": ["鎬昏处", "鏄庣粏璐�"], "keywords": "鍑瘉鍒嗗綍 鍒嗗綍 entry 鍊熸柟 璐锋柟 杈呭姪鏍哥畻"},
+    {"name": "鍥哄畾璧勪骇", "module": "浼氳鏍哥畻", "table": "fin_fixed_asset", "doc": "璧勪骇鍗$墖锛岀姸鎬� 1鍦ㄧ敤/2闂茬疆/3缁翠慨涓�/4宸叉姤搴燂紱鏈堟姌鏃ч=(鍘熷�济�(1-娈嬪�肩巼))梅(骞撮檺脳12)锛岀疮璁℃姌鏃т笌鍑�鍊肩敱璁℃彁绱姞锛屼笉鎺ユ敹鍓嶇浼犲��", "upstream": [], "downstream": ["璐㈠姟鎶ヨ〃鐪嬫澘", "鎬昏处"], "keywords": "鍥哄畾璧勪骇 鎶樻棫 璧勪骇鍗$墖 fixed asset 娈嬪�肩巼"},
+    {"name": "鏃犲舰璧勪骇", "module": "浼氳鏍哥畻", "table": "fin_intangible_asset", "doc": "鏃犲舰璧勪骇鍗$墖(璇佷功缂栧彿/鏈夋晥鏈�)锛岀姸鎬� 1鍦ㄧ敤/2宸插け鏁�/3宸叉憡閿�瀹岋紱鏈堟憡閿�棰�=(鍘熷�济�(1-娈嬪�肩巼))梅(鎽婇攢骞撮檺脳12)锛岀疮璁℃憡閿�涓庡噣鍊肩敱璁℃彁绱姞", "upstream": [], "downstream": ["璐㈠姟鎶ヨ〃鐪嬫澘", "鎬昏处"], "keywords": "鏃犲舰璧勪骇 鎽婇攢 杞欢钁椾綔鏉� 涓撳埄 intangible 鏈夋晥鏈�"},
     {"name": "閿�鍞鍗�", "module": "杩涢攢瀛�", "table": "erp_sale_order", "doc": "XSDD缂栧彿锛孊PM 瀹℃壒锛岄渶鐢熶骇琛岃嚜鍔ㄥ缓 MPS锛屽嚭搴撶姸鎬�:鏈�0宸查鐣�1閮ㄥ垎2鍏ㄩ儴3锛岀敓浜у畬鎴愪簨浠跺洖鍐�", "upstream": ["鍚堝悓"], "downstream": ["MPS", "鍙戣揣閫氱煡", "閿�鍞嚭搴�", "鍞悗宸ュ崟"], "keywords": "閿�鍞鍗� sale"},
     {"name": "涓荤敓浜ц鍒�", "module": "鐢熶骇", "table": "mes_pro_mps", "doc": "MPS璁″垝锛岃崏绋�0鈫掑凡涓嬪彂1鈫掑畬鎴�2/鍙栨秷3锛屽彲鍚堝苟涓嬪彂", "upstream": ["閿�鍞鍗�"], "downstream": ["鐢熶骇宸ュ崟"], "keywords": "MPS 涓昏鍒� 鐢熶骇璁″垝"},
     {"name": "鐢熶骇宸ュ崟", "module": "鐢熶骇", "table": "mes_pro_work_order", "doc": "鑽夌0鈫掑凡纭1鈫掑畬鎴�2/鍙栨秷3锛岀‘璁ゆ椂鎸夊伐鑹鸿矾绾跨敓鎴愪换鍔★紝鏈�鍚庝竴閬撳伐搴忔姤宸ュ洖鍐欏凡鐢熶骇鏁伴噺", "upstream": ["MPS"], "downstream": ["鐢熶骇浠诲姟", "棰嗘枡", "瀹屽伐鍏ュ簱"], "keywords": "宸ュ崟 鐢熶骇宸ュ崟 work order"},
@@ -549,6 +565,15 @@
        {"stage": "鍒ゅ畾", "action": "闂鍒ゅ畾: 涓�鑸�/缁翠慨/閫�璐э紝涓ラ噸搴� 0-3", "roles": ["鍞悗宸ョ▼甯�"]},
        {"stage": "缁翠慨", "action": "鐜板満/杩斿巶/杩滅▼缁翠慨锛岃垂鐢ㄤ笌缁撴灉璁板綍锛屽畬鎴愯仈鍔ㄥ伐鍗曞畬缁�", "roles": ["缁翠慨宸ョ▼甯�"]},
        {"stage": "閫�璐�", "action": "閫�璐х敵璇峰鏍糕啋鑷姩寤� MES 閿�鍞��璐у崟(RQC)鈫掕川妫�/鍏ュ簱鍥炶皟鈫掗��娆惧洖璋冣啋鍏抽棴", "roles": ["鍞悗", "璐ㄦ", "璐㈠姟"]}]},
+    {"id": "finance", "name": "浼氳鏍哥畻娴佺▼", "mermaid": "mermaid/12-finance-flow.mmd", "color": "#3ec9a7",
+     "summary": "鐙珛鏍哥畻妯″潡 yudao-module-finance锛氫細璁$鐩缓璐� 鈫� 璁拌处鍑瘉褰曞叆(鍊熻捶骞宠 鏍¢獙) 鈫� 杩囪处 鈫� 鎬昏处/鏄庣粏璐� 鈫� 璧勪骇璁℃彁鎶樻棫涓庢憡閿� 鈫� 璐㈠姟鎶ヨ〃鐪嬫澘鍙栨暟 ERP 宸插鏍稿崟鎹��",
+     "steps": [
+       {"stage": "寤鸿处", "action": "缁存姢浼氳绉戠洰 tree(subjectCode 鍞竴锛屽彲寤轰笅绾�)锛岄娆″彲瀵煎叆绾� 68 鏉′竴绾х鐩瀛�", "roles": ["浼氳"]},
+       {"stage": "璁拌处", "action": "褰曞叆鍑瘉(瀛楀彿鐣欑┖鑷姩鐢熸垚)+鍒嗗綍锛屽垎褰曢�愯閫夌鐩苟濉��/璐凤紱鏍¢獙鑷冲皯涓�琛屻�佸�熻捶閮戒笉寰楀悓鏃跺ぇ浜�0銆佸悎璁$浉绛変笖澶т簬0", "roles": ["浼氳"]},
+       {"stage": "杩囪处", "action": "鏈繃璐�0 鈫� post 杩囪处 鈫� 宸茶繃璐�10(涓嶅彲鍐嶄慨鏀�)锛涙湭杩囪处0 鎴� 宸茶繃璐�10 鍧囧彲 cancel 浣滃簾 鈫� 宸蹭綔搴�20", "roles": ["浼氳", "璐㈠姟涓荤"]},
+       {"stage": "鏌ヨ处", "action": "鎬昏处鎸夋湡闂撮�愭湀姹囨�汇�佹槑缁嗚处閫愭潯鍒嗗綍骞剁粨杞綑棰濓紝涓よ�呭彧缁熻宸茶繃璐�10鐨勫嚟璇侊紝骞舵敮鎸佸鍑�", "roles": ["浼氳", "璐㈠姟涓荤"]},
+       {"stage": "璧勪骇", "action": "鍥哄畾璧勪骇(1鍦ㄧ敤/2闂茬疆/3缁翠慨涓�/4宸叉姤搴�)涓庢棤褰㈣祫浜�(1鍦ㄧ敤/2宸插け鏁�/3宸叉憡閿�瀹�)璁℃彁锛氫紶 ids 鎸夐�変腑璁℃彁锛宨ds 涓虹┖瀵瑰叏閮ㄦ湭鎶ュ簾/鏈け鏁堣祫浜ц鎻愪竴涓湀", "roles": ["璧勪骇浼氳"]},
+       {"stage": "鎶ヨ〃", "action": "鐪嬫澘姹囨�昏祫浜т笌鍑瘉鏃剁偣鏁� + 缁� ErpFinanceStatisticsApi 鍙� ERP 宸插鏍告敹璐т粯娆句笌璐攢閲戦锛岃緭鍑烘湀搴︽敹鏀秼鍔�", "roles": ["璐㈠姟涓荤", "绠$悊灞�"]}]},
     {"id": "ai", "name": "AI 涓氬姟娴佺▼", "mermaid": "mermaid/11-ai-business-flow.mmd", "color": "#56c8d8",
      "summary": "缁熶竴妯″紡锛氫笟鍔′晶鏀堕泦涓婁笅鏂�(蹇呰鏃惰皟 MDM/SRM/CRM API 鍙栧悕绉�)鈫掔粍瑁� Prompt鈫抍hat/chatWithImage鈫掕В鏋� JSON鈫掑墠绔濉垨寤鸿锛涙棤 Function Calling锛汻AG 鐭ヨ瘑搴撳凡寤烘垚浣嗘殏鏃犱笟鍔℃秷璐广��",
      "steps": [
@@ -578,7 +603,7 @@
     {"role": "璐ㄦ鍛�", "modules": ["MES-QC"], "operations": ["IQC/IPQC/OQC/RQC 妫�楠�", "NCR 璇勫澶勭疆"], "data": ["mes_qc_*"], "scope": "inspectorUserId 褰掑睘"},
     {"role": "璁惧鍛�/缁翠慨宸�", "modules": ["MES-DV"], "operations": ["鐐规淇濆吇", "缁翠慨宸ュ崟", "宸ヨ棰嗙敤"], "data": ["mes_dv_*", "mes_tm_*"], "scope": "chargeUserId 褰掑睘"},
     {"role": "瀹㈡湇/鍞悗宸ョ▼甯�", "modules": ["鍞悗"], "operations": ["宸ュ崟鍙楃悊娲惧伐", "缁翠慨", "閫�璐х敵璇�"], "data": ["after_sale_*"], "scope": "琛岀骇鏁版嵁鏉冮檺鍥涜鍥撅細鎴戣礋璐�/鎴戝弬涓�/涓嬪睘璐熻矗/鍏ㄩ儴(aftersales:view-all)"},
-    {"role": "璐㈠姟", "modules": ["ERP", "CRM"], "operations": ["鏀舵鍗�", "浠樻鍗�", "寮�绁ㄥ鏍�", "缁撶畻璐︽埛"], "data": ["erp_finance_*", "crm_invoice", "crm_receivable"], "scope": "financeUserId 褰掑睘"},
+    {"role": "璐㈠姟", "modules": ["ERP", "CRM", "FIN"], "operations": ["鏀舵鍗�", "浠樻鍗�", "寮�绁ㄥ鏍�", "缁撶畻璐︽埛", "浼氳绉戠洰缁存姢", "璁拌处鍑瘉褰曞叆涓庤繃璐�/浣滃簾", "鎬昏处涓庢槑缁嗚处鏌ヨ", "鍥哄畾璧勪骇鎶樻棫璁℃彁", "鏃犲舰璧勪骇鎽婇攢璁℃彁", "浼氳鏍哥畻鐪嬫澘"], "data": ["erp_finance_*", "crm_invoice", "crm_receivable", "fin_account_subject", "fin_voucher", "fin_voucher_entry", "fin_fixed_asset", "fin_intangible_asset"], "scope": "financeUserId 褰掑睘锛涜储鍔℃牳绠楁ā鍧楁寜 finance:* 鎺ュ彛鏉冮檺 + system_menu 鎺堟潈锛屾殏鏃犳暟鎹潈闄愬瓧娈�"},
     {"role": "HR", "modules": ["HRM"], "operations": ["鍛樺伐妗f鍏ヨ浆璋冪", "鑰冨嫟瑙勫垯鎺掔彮", "钖叕鏍哥畻鍙戞斁", "绂昏亴浜ゆ帴"], "data": ["hrm_*"], "scope": "閮ㄩ棬鑼冨洿 dataScope"},
     {"role": "绠$悊灞�", "modules": ["BI"], "operations": ["鏌ョ湅閲囪喘閿�鍞�/浠撳簱/鐢熶骇璁惧/璐ㄩ噺/浜哄姏浜斿ぇ涓婚澶у睆"], "data": ["bi_dashboard"], "scope": "鍙"}
   ]
@@ -614,6 +639,14 @@
       浠樻鍗�
       鏀舵鍗�
       缁撶畻璐︽埛
+    璐㈠姟绠$悊 浼氳鏍哥畻 FIN
+      浼氳绉戠洰鏍�
+      璁拌处鍑瘉涓庡垎褰�
+      鍑瘉杩囪处涓庝綔搴�
+      鎬昏处涓庢槑缁嗚处
+      鍥哄畾璧勪骇涓庢姌鏃�
+      鏃犲舰璧勪骇涓庢憡閿�
+      璐㈠姟鎶ヨ〃鐪嬫澘
     鐢熶骇绠$悊 MES
       涓荤敓浜ц鍒� MPS
       鐢熶骇宸ュ崟
@@ -910,6 +943,7 @@
   ERP -->|閿�鍞鍗曞鎵圭敓鎴怣PS| MES
   MES -->|鍏ュ簱鍑哄簱鍥炲啓鍗曟嵁杩涘害| ERP
   MES -->|ProductionFinishedEvent| ERP
+  ERP -->|ErpFinanceStatisticsApi.getMonthlyStatistics路鍙彇宸插鏍� status=20 鐨勬敹娆句粯娆句笌璐攢閲戦| FIN[璐㈠姟鏍哥畻 FIN 绉戠洰路鍑瘉路鎬昏处鏄庣粏璐β疯祫浜ф姌鏃ф憡閿�路鎶ヨ〃鐪嬫澘]
   MES -->|閫�璐ц川妫�鍏ュ簱鍥炶皟| AS[鍞悗宸ュ崟缁翠慨閫�璐
   CRM -->|瀹㈡埛涓绘暟鎹畖 AS
   HRM[HRM 鍛樺伐鍏ヨ浆璋冪浜ゆ帴鑰冨嫟钖叕] -.->|expandUserIds 鏁版嵁鍙鎬ф墿灞暵锋帴鍙e凡灏辩华寰呭悇妯″潡鎺ュ叆| CRM
@@ -927,9 +961,11 @@
   ERP --> BI
   CRM --> BI
   HRM --> BI
+  FIN --> BI
   SYS[绯荤粺搴曞骇 璁よ瘉路RBAC路瀛楀吀路闄勪欢涓績路瀹氭椂浠诲姟] --- MDM
   SYS --- MES
   STG[闄勪欢涓績 storage-blob+attachment 鍏ㄦā鍧楃粺涓�鎸傞檮浠禲 --- ERP
+  FIN -.->|鍑瘉闄勪欢 recordType=fin_voucher| STG
 </pre>
 <pre style="display:none" class="mmd-src" data-mmd="09-data-flow">
 flowchart LR
@@ -958,6 +994,19 @@
     RC[鏀舵鍗昡
     PI[閲囪喘鏉ョエ] --> PA[浠樻鍗昡
   end
+  subgraph S5 [璐㈠姟鏍哥畻 FIN 鐙珛妯″潡]
+    FA[浼氳绉戠洰 fin_account_subject] --> FV[璁拌处鍑瘉 fin_voucher]
+    FV --> FE[鍑瘉鍒嗗綍 fin_voucher_entry]
+    FE -->|鍊熻捶骞宠 鏍¢獙路涓嶅钩琛℃垨鍚堣涓�0鍒欐嫤涓媩 FVPOST{鍑瘉杩囪处}
+    FVPOST -->|宸茶繃璐�10| GL[鎬昏处路鎸夋湡闂撮�愭湀姹囨�籡
+    FVPOST -->|宸茶繃璐�10| DL[鏄庣粏璐β烽�愭潯鍒嗗綍骞剁粨杞綑棰漖
+    GL --> FDASH[浼氳鏍哥畻鐪嬫澘]
+    DL --> FDASH
+    FIX[鍥哄畾璧勪骇路1鍦ㄧ敤2闂茬疆3缁翠慨涓�4宸叉姤搴焆 -->|璁℃彁鎶樻棫路浼爄ds鎸夐�変腑 绌篿ds鍏ㄩ噺| FDEP[绱鎶樻棫涓庡噣鍊糫
+    FINT[鏃犲舰璧勪骇路1鍦ㄧ敤2宸插け鏁�3宸叉憡閿�瀹宂 -->|璁℃彁鎽婇攢路浼爄ds鎸夐�変腑 绌篿ds鍏ㄩ噺| FAMR[绱鎽婇攢涓庡噣鍊糫
+    FDEP --> FDASH
+    FAMR --> FDASH
+  end
   CT -->|generateSaleOrder 甯﹀叆瀹㈡埛浜у搧鎶樻墸瀹氶噾路鍥炲啓orderId| SO
   SO -->|瀹℃壒閫氳繃甯﹀叆 saleOrderId 琛孖D 鏁伴噺 瀹㈡埛 浜ゆ湡| MP
   PO -->|纭鏀惰揣甯﹀叆璁㈠崟琛岀墿鏂欎緵搴斿晢鏁伴噺| AN
@@ -971,6 +1020,11 @@
   IV -->|鍥炴寮哄埗鍏堝紑绁▅ RK
   RC -->|鍏宠仈CRM瀹㈡埛鏍¢獙| CU
   PA -->|鍏宠仈鏉ョエ涓庝緵搴斿晢| PI
+  RC -->|宸插鏍告敹娆惧崟閲戦 ErpFinanceStatisticsApi| FDASH
+  PA -->|宸插鏍镐粯娆惧崟閲戦 ErpFinanceStatisticsApi| FDASH
+  SO -->|宸插鏍搁攢鍞鍗曢噾棰� ErpFinanceStatisticsApi| FDASH
+  PO -->|宸插鏍搁噰璐鍗曢噾棰� ErpFinanceStatisticsApi| FDASH
+  FV -.->|鍑瘉闄勪欢 recordType=fin_voucher| PDFARCH
   subgraph S4 [鏅鸿兘璐ㄦ鎶ュ憡 qcreport]
     RPT[鎶ュ憡妯℃澘] --> RPTV[妯℃澘鐗堟湰]
     RPTVSAVE[璁捐鍣ㄤ繚瀛� create 鎴� update] -->|鍐欏叆渚ч椄闂� 瑙勫垯101_006 鐢诲竷瀹夊叏101_009 涓嶅悎鏍间笉钀藉簱| RPTV
@@ -1044,6 +1098,9 @@
   hrm_employee ||--o| hrm_user_handover : "绂昏亴浜ゆ帴鏄犲皠"
   qc_report_template ||--o{ qc_report_template_version : "澶氱増鏈�"
   qc_report_template_version ||--o{ qc_report_instance : "鍐荤粨鍑轰欢鐗堟湰"
+  fin_account_subject |o--o{ fin_voucher_entry : "鍒嗗綍鎸� subject_code 瀵瑰簲绉戠洰"
+  fin_voucher ||--o{ fin_voucher_entry : "鍑瘉鍚垎褰�"
+  %% fin_fixed_asset 鍥哄畾璧勪骇涓� fin_intangible_asset 鏃犲舰璧勪骇涓虹嫭绔嬭祫浜у崱鐗囷紝闈犺祫浜х紪鍙疯嚜绠$悊锛屾棤澶栭敭鍏宠仈锛屾晠涓嶄笂 ER
 </pre>
 <pre style="display:none" class="mmd-src" data-mmd="11-ai-business-flow">
 flowchart TD
@@ -1088,6 +1145,32 @@
   MODE[妯″瀷灞�:閫氫箟鍗冮棶涓轰富路棰勭暀DeepSeek/OpenAI/Ollama鏈疄鐜癩 --- O2
   NOTE[鍏ㄩ」鐩棤Function Calling路AI鍙緭鍑虹粨鏋勫寲JSON鐢变笟鍔′晶瑙f瀽] --- J2
 </pre>
+<pre style="display:none" class="mmd-src" data-mmd="12-finance-flow">
+flowchart TD
+  A[浼氳绉戠洰 fin_account_subject路涓�绾х鐩瀛愮害68鏉 -->|寤轰笅绾х鐩� parent_id| B[绉戠洰鏍懧穕ist 缁勮 children]
+  B -->|绉戠洰鍙栧�肩敤 subjectCode 鑰岄潪 id| C[璁拌处鍑瘉 fin_voucher路鐣欑┖鍒欒嚜鍔ㄧ敓鎴愬瓧鍙穄
+  C --> D[鍑瘉鍒嗗綍 fin_voucher_entry路閫愯绉戠洰+鍊�/璐�+杈呭姪鏍哥畻]
+  D -->|鍓嶇鏍¢獙路鍚庣澶嶆牳| E{鍊熻捶骞宠 鏍¢獙}
+  E -->|涓嶅钩琛℃垨鍚堣涓�0| E1[鎷︿笅路鎻愮ず鍑洪敊琛屼笌鍊熻捶宸]
+  E -->|骞宠 | F[淇濆瓨路鏈繃璐�0]
+  F -->|鍙紪杈� 鍙繃璐� 鍙綔搴� 鍙垹闄 F
+  F -->|杩囪处 post| G[宸茶繃璐�10路涓嶅彲鍐嶄慨鏀筣
+  F -->|浣滃簾 cancel| H[宸蹭綔搴�20]
+  G -->|浣滃簾 cancel| H
+  H -->|鍒犻櫎 delete| X[鍒犻櫎]
+  G -->|鍙粺璁″凡杩囪处鍑瘉鏄庣粏| I[鎬昏处 finance/ledger/general路鎸夋湡闂撮�愭湀姹囨�籡
+  G -->|鍙粺璁″凡杩囪处鍑瘉鏄庣粏| J[鏄庣粏璐� finance/ledger/detail路閫愭潯鍒嗗綍骞剁粨杞綑棰漖
+  I --> K[璐㈠姟鎶ヨ〃 / 浼氳鏍哥畻鐪嬫澘]
+  J --> K
+  L[鍥哄畾璧勪骇 fin_fixed_asset路鐘舵�� 1鍦ㄧ敤 2闂茬疆 3缁翠慨涓� 4宸叉姤搴焆 -->|璁℃彁鎶樻棫 depreciate路绌篿ds=鍏ㄩ噺| M[绱鎶樻棫閫掑路鍑�鍊�=鍘熷��-绱鎶樻棫]
+  N[鏃犲舰璧勪骇 fin_intangible_asset路鐘舵�� 1鍦ㄧ敤 2宸插け鏁� 3宸叉憡閿�瀹宂 -->|璁℃彁鎽婇攢 amortize路绌篿ds=鍏ㄩ噺| O[绱鎽婇攢閫掑路鍑�鍊�=鍘熷��-绱鎽婇攢]
+  M --> K
+  O --> K
+  P[ERP 宸插鏍稿崟鎹� status=20 鏀舵鍗� 浠樻鍗� 閿�鍞鍗� 閲囪喘璁㈠崟] -->|ErpFinanceStatisticsApi.getMonthlyStatistics 鎸夋湀| K
+  K -.->|绉戠洰鏁� 鍑瘉鏁� 鏈繃璐� 宸茶繃璐� 宸蹭綔搴� 璧勪骇鍑�鍊� 鏈堝害瓒嬪娍| Q[鐪嬫澘鍗$墖+鏈堝害瓒嬪娍鍥綸
+  V[闄勪欢涓績 recordType=fin_voucher] --- C
+  V -.->|涓嶈惤 file_url 鍒梶 C
+</pre>
 
 <script type="module">
 const DATA = {};
@@ -1107,7 +1190,7 @@
 const mmdSrc = {};
 document.querySelectorAll('pre.mmd-src').forEach(p=>{ mmdSrc[p.dataset.mmd]=p.textContent.trim(); });
 
-const flowColor = {sales:'var(--c-sale)',purchase:'var(--c-erp)',production:'var(--c-prod)',quality:'var(--c-qc)',warehouse:'var(--c-wm)',aftersales:'var(--c-as)',ai:'var(--c-ai)'};
+const flowColor = {sales:'var(--c-sale)',purchase:'var(--c-erp)',production:'var(--c-prod)',quality:'var(--c-qc)',warehouse:'var(--c-wm)',aftersales:'var(--c-as)',finance:'var(--c-fin)',ai:'var(--c-ai)'};
 
 async function boot(){
   DATA.business = await loadJson('business');
@@ -1144,8 +1227,8 @@
   </div>
   <h3 class="sec">涓氬姟妯″潡</h3>
   <div class="grid cards">`;
-  const targets = {crm:'flow-sales',erp:'flow-purchase',pro:'flow-production',wm:'flow-warehouse',qc:'flow-quality',aftersales:'flow-aftersales',ai:'ai',srm:'flow-purchase',mdm:'objects',dv:'objects',hrm:'objects',bpm:'relation'};
-  const accs = {mdm:'var(--c-base)',crm:'var(--c-sale)',srm:'var(--c-sup)',erp:'var(--c-erp)',pro:'var(--c-prod)',wm:'var(--c-wm)',qc:'var(--c-qc)',dv:'var(--c-dv)',aftersales:'var(--c-as)',hrm:'var(--c-hrm)',bpm:'var(--c-bpm)',ai:'var(--c-ai)'};
+  const targets = {crm:'flow-sales',erp:'flow-purchase',pro:'flow-production',wm:'flow-warehouse',qc:'flow-quality',aftersales:'flow-aftersales',ai:'ai',srm:'flow-purchase',mdm:'objects',dv:'objects',hrm:'objects',bpm:'relation',finance:'flow-finance'};
+  const accs = {mdm:'var(--c-base)',crm:'var(--c-sale)',srm:'var(--c-sup)',erp:'var(--c-erp)',pro:'var(--c-prod)',wm:'var(--c-wm)',qc:'var(--c-qc)',dv:'var(--c-dv)',aftersales:'var(--c-as)',hrm:'var(--c-hrm)',bpm:'var(--c-bpm)',ai:'var(--c-ai)',finance:'var(--c-fin)'};
   DATA.modules.modules.forEach(m=>{
     html += `<div class="card" style="--acc:${accs[m.key]||'#2c5cff'}" data-goto="${targets[m.key]||'panorama'}">
       <h3>${esc(m.name)}<span class="tag">${esc(m.tag)}</span></h3>
diff --git a/docs/project-business/mermaid/01-business-mindmap.mmd b/docs/project-business/mermaid/01-business-mindmap.mmd
index fad892e..399f83d 100644
--- a/docs/project-business/mermaid/01-business-mindmap.mmd
+++ b/docs/project-business/mermaid/01-business-mindmap.mmd
@@ -28,6 +28,14 @@
       浠樻鍗�
       鏀舵鍗�
       缁撶畻璐︽埛
+    璐㈠姟绠$悊 浼氳鏍哥畻 FIN
+      浼氳绉戠洰鏍�
+      璁拌处鍑瘉涓庡垎褰�
+      鍑瘉杩囪处涓庝綔搴�
+      鎬昏处涓庢槑缁嗚处
+      鍥哄畾璧勪骇涓庢姌鏃�
+      鏃犲舰璧勪骇涓庢憡閿�
+      璐㈠姟鎶ヨ〃鐪嬫澘
     鐢熶骇绠$悊 MES
       涓荤敓浜ц鍒� MPS
       鐢熶骇宸ュ崟
diff --git a/docs/project-business/mermaid/08-module-relation.mmd b/docs/project-business/mermaid/08-module-relation.mmd
index 3086aab..4eec809 100644
--- a/docs/project-business/mermaid/08-module-relation.mmd
+++ b/docs/project-business/mermaid/08-module-relation.mmd
@@ -8,6 +8,7 @@
   ERP -->|閿�鍞鍗曞鎵圭敓鎴怣PS| MES
   MES -->|鍏ュ簱鍑哄簱鍥炲啓鍗曟嵁杩涘害| ERP
   MES -->|ProductionFinishedEvent| ERP
+  ERP -->|ErpFinanceStatisticsApi.getMonthlyStatistics路鍙彇宸插鏍� status=20 鐨勬敹娆句粯娆句笌璐攢閲戦| FIN[璐㈠姟鏍哥畻 FIN 绉戠洰路鍑瘉路鎬昏处鏄庣粏璐β疯祫浜ф姌鏃ф憡閿�路鎶ヨ〃鐪嬫澘]
   MES -->|閫�璐ц川妫�鍏ュ簱鍥炶皟| AS[鍞悗宸ュ崟缁翠慨閫�璐
   CRM -->|瀹㈡埛涓绘暟鎹畖 AS
   HRM[HRM 鍛樺伐鍏ヨ浆璋冪浜ゆ帴鑰冨嫟钖叕] -.->|expandUserIds 鏁版嵁鍙鎬ф墿灞暵锋帴鍙e凡灏辩华寰呭悇妯″潡鎺ュ叆| CRM
@@ -25,6 +26,8 @@
   ERP --> BI
   CRM --> BI
   HRM --> BI
+  FIN --> BI
   SYS[绯荤粺搴曞骇 璁よ瘉路RBAC路瀛楀吀路闄勪欢涓績路瀹氭椂浠诲姟] --- MDM
   SYS --- MES
   STG[闄勪欢涓績 storage-blob+attachment 鍏ㄦā鍧楃粺涓�鎸傞檮浠禲 --- ERP
+  FIN -.->|鍑瘉闄勪欢 recordType=fin_voucher| STG
diff --git a/docs/project-business/mermaid/09-data-flow.mmd b/docs/project-business/mermaid/09-data-flow.mmd
index 83d6023..d7fa7b4 100644
--- a/docs/project-business/mermaid/09-data-flow.mmd
+++ b/docs/project-business/mermaid/09-data-flow.mmd
@@ -24,6 +24,19 @@
     RC[鏀舵鍗昡
     PI[閲囪喘鏉ョエ] --> PA[浠樻鍗昡
   end
+  subgraph S5 [璐㈠姟鏍哥畻 FIN 鐙珛妯″潡]
+    FA[浼氳绉戠洰 fin_account_subject] --> FV[璁拌处鍑瘉 fin_voucher]
+    FV --> FE[鍑瘉鍒嗗綍 fin_voucher_entry]
+    FE -->|鍊熻捶骞宠 鏍¢獙路涓嶅钩琛℃垨鍚堣涓�0鍒欐嫤涓媩 FVPOST{鍑瘉杩囪处}
+    FVPOST -->|宸茶繃璐�10| GL[鎬昏处路鎸夋湡闂撮�愭湀姹囨�籡
+    FVPOST -->|宸茶繃璐�10| DL[鏄庣粏璐β烽�愭潯鍒嗗綍骞剁粨杞綑棰漖
+    GL --> FDASH[浼氳鏍哥畻鐪嬫澘]
+    DL --> FDASH
+    FIX[鍥哄畾璧勪骇路1鍦ㄧ敤2闂茬疆3缁翠慨涓�4宸叉姤搴焆 -->|璁℃彁鎶樻棫路浼爄ds鎸夐�変腑 绌篿ds鍏ㄩ噺| FDEP[绱鎶樻棫涓庡噣鍊糫
+    FINT[鏃犲舰璧勪骇路1鍦ㄧ敤2宸插け鏁�3宸叉憡閿�瀹宂 -->|璁℃彁鎽婇攢路浼爄ds鎸夐�変腑 绌篿ds鍏ㄩ噺| FAMR[绱鎽婇攢涓庡噣鍊糫
+    FDEP --> FDASH
+    FAMR --> FDASH
+  end
   CT -->|generateSaleOrder 甯﹀叆瀹㈡埛浜у搧鎶樻墸瀹氶噾路鍥炲啓orderId| SO
   SO -->|瀹℃壒閫氳繃甯﹀叆 saleOrderId 琛孖D 鏁伴噺 瀹㈡埛 浜ゆ湡| MP
   PO -->|纭鏀惰揣甯﹀叆璁㈠崟琛岀墿鏂欎緵搴斿晢鏁伴噺| AN
@@ -37,6 +50,11 @@
   IV -->|鍥炴寮哄埗鍏堝紑绁▅ RK
   RC -->|鍏宠仈CRM瀹㈡埛鏍¢獙| CU
   PA -->|鍏宠仈鏉ョエ涓庝緵搴斿晢| PI
+  RC -->|宸插鏍告敹娆惧崟閲戦 ErpFinanceStatisticsApi| FDASH
+  PA -->|宸插鏍镐粯娆惧崟閲戦 ErpFinanceStatisticsApi| FDASH
+  SO -->|宸插鏍搁攢鍞鍗曢噾棰� ErpFinanceStatisticsApi| FDASH
+  PO -->|宸插鏍搁噰璐鍗曢噾棰� ErpFinanceStatisticsApi| FDASH
+  FV -.->|鍑瘉闄勪欢 recordType=fin_voucher| PDFARCH
   subgraph S4 [鏅鸿兘璐ㄦ鎶ュ憡 qcreport]
     RPT[鎶ュ憡妯℃澘] --> RPTV[妯℃澘鐗堟湰]
     RPTVSAVE[璁捐鍣ㄤ繚瀛� create 鎴� update] -->|鍐欏叆渚ч椄闂� 瑙勫垯101_006 鐢诲竷瀹夊叏101_009 涓嶅悎鏍间笉钀藉簱| RPTV
diff --git a/docs/project-business/mermaid/10-business-object.mmd b/docs/project-business/mermaid/10-business-object.mmd
index c10c095..d65f2ef 100644
--- a/docs/project-business/mermaid/10-business-object.mmd
+++ b/docs/project-business/mermaid/10-business-object.mmd
@@ -36,3 +36,7 @@
   hrm_employee ||--o| hrm_user_handover : "绂昏亴浜ゆ帴鏄犲皠"
   qc_report_template ||--o{ qc_report_template_version : "澶氱増鏈�"
   qc_report_template_version ||--o{ qc_report_instance : "鍐荤粨鍑轰欢鐗堟湰"
+  fin_account_subject |o--o{ fin_voucher_entry : "鍒嗗綍鎸� subject_code 瀵瑰簲绉戠洰"
+  fin_voucher ||--o{ fin_voucher_entry : "鍑瘉鍚垎褰�"
+  %% fin_fixed_asset 鍥哄畾璧勪骇涓� fin_intangible_asset 鏃犲舰璧勪骇涓虹嫭绔嬭祫浜у崱鐗囷紝闈犺祫浜х紪鍙疯嚜绠$悊锛屾棤澶栭敭鍏宠仈锛屾晠涓嶄笂 ER
+
diff --git a/pom.xml b/pom.xml
index 35f522b..914e27f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -25,6 +25,8 @@
         <module>yudao-module-crm-api</module>
         <module>yudao-module-erp</module>
         <module>yudao-module-erp-api</module>
+        <!-- 璐㈠姟绠$悊妯″潡锛堜細璁℃牳绠楋細绉戠洰銆佸嚟璇併�佹�昏处銆佽祫浜с�佹姤琛級 -->
+        <module>yudao-module-finance</module>
 <!--        <module>yudao-module-iot</module>-->
         <module>yudao-module-mes</module>
         <module>yudao-module-mes-api</module>
diff --git a/sql/mysql/finance_menu_init.sql b/sql/mysql/finance_menu_init.sql
new file mode 100644
index 0000000..fafc754
--- /dev/null
+++ b/sql/mysql/finance_menu_init.sql
@@ -0,0 +1,85 @@
+-- =============================================
+-- 璐㈠姟鏍哥畻妯″潡鑿滃崟鍒濆鍖栬剼鏈�
+-- =============================================
+-- 妯″潡锛歽udao-module-finance
+--      璁拌处鍑瘉 / 鎬昏处 / 鏄庣粏璐� / 浼氳绉戠洰 / 鍥哄畾璧勪骇 / 鏃犲舰璧勪骇 / 璐㈠姟鎶ヨ〃
+-- 鍓嶇椤甸潰锛歮om-pro2-before/src/views/erp/finance/**
+-- 鍚庣鏉冮檺鐮侊細瑙佸悇 Controller 鐨� @PreAuthorize
+--
+-- 璇存槑锛�
+--   1. 鏂板缓涓�绾ц彍鍗曘�岃储鍔℃牳绠椼��(/finance-core)锛屼笉瑙︾鍘熸湁 ERP 涓嬬殑銆岃储鍔$鐞嗐��(id=2645)
+--      鍙婂叾 缁撶畻璐︽埛/浠樻鍗�/鏀舵鍗� 涓変釜鑿滃崟銆�
+--   2. 椤甸潰鑿滃崟(type=2) 鍚勮嚜鎼哄甫 :query 鏉冮檺锛涘鍒犳敼/瀵煎嚭/鎶樻棫鎽婇攢绛夋搷浣滄潈闄愬缓鎴愭寜閽�(type=3)銆�
+--   3. 鎬昏处涓庢槑缁嗚处鍏辩敤 finance:ledger:query銆乫inance:ledger:export 涓や釜鏉冮檺鐮侊紝
+--      鍥犳銆屽鍑鸿处绨裤�嶆寜閽彧鎸傚湪銆屾�昏处銆嶄笅 鈥斺�� 鍚屼竴鏉冮檺鐮佸湪瑙掕壊鎺堟潈鏍戦噷鍑虹幇涓�娆″嵆鍙紝
+--      鎺堜簣鍚庛�屾槑缁嗚处銆嶇殑瀵煎嚭鎸夐挳鍚屾牱鐢熸晥銆�
+--   4. 骞傜瓑锛氬厛鍒犻櫎鏈剼鏈崰鐢ㄧ殑 id 鍖洪棿 (1075500~1075549) 鍐嶆彃鍏ワ紝鍙噸澶嶆墽琛屻��
+--      娉ㄦ剰锛氳嫢涔嬪悗宸插湪銆岃鑹茬鐞嗐�嶉噷缁欒繖浜涜彍鍗曟巿鏉冿紝閲嶈窇鏈剼鏈細涓㈠け瀵瑰簲 system_role_menu 璁板綍銆�
+--   5. 涓嶅惈 tenant_id锛堟湰椤圭洰闈炲绉熸埛锛夈��
+-- =============================================
+
+DELETE FROM system_menu WHERE id BETWEEN 1075500 AND 1075549;
+
+-- ---------------------------------------------------------------
+-- 涓�绾ц彍鍗�
+-- ---------------------------------------------------------------
+INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES
+(1075500, '璐㈠姟鏍哥畻', '', 1, 4, 0, '/finance-core', 'ep:money', '', '', 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0');
+
+-- ---------------------------------------------------------------
+-- 椤甸潰鑿滃崟 (type=2)
+-- ---------------------------------------------------------------
+INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES
+(1075501, '璁拌处鍑瘉', 'finance:voucher:query',           2, 1, 1075500, 'voucher',         'ep:document',        'erp/finance/voucher/index',           'FinVoucher',         0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0'),
+(1075502, '鎬昏处',     'finance:ledger:query',            2, 2, 1075500, 'general-ledger',  'ep:notebook',        'erp/finance/ledger/general/index',    'FinGeneralLedger',   0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0'),
+(1075503, '鏄庣粏璐�',   'finance:ledger:query',            2, 3, 1075500, 'detail-ledger',   'ep:list',            'erp/finance/ledger/detail/index',     'FinDetailLedger',    0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0'),
+(1075504, '浼氳绉戠洰', 'finance:account-subject:query',   2, 4, 1075500, 'subject',         'ep:collection-tag',  'erp/finance/subject/index',           'FinAccountSubject',  0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0'),
+(1075505, '鍥哄畾璧勪骇', 'finance:fixed-asset:query',       2, 5, 1075500, 'fixed-asset',     'ep:office-building', 'erp/finance/fixed-asset/index',       'FinFixedAsset',      0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0'),
+(1075506, '鏃犲舰璧勪骇', 'finance:intangible-asset:query',  2, 6, 1075500, 'intangible-asset','ep:medal',           'erp/finance/intangible-asset/index',  'FinIntangibleAsset', 0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0'),
+(1075507, '璐㈠姟鎶ヨ〃', 'finance:statistics:query',        2, 7, 1075500, 'statistics',      'ep:data-line',       'erp/finance/statistics/index',        'FinStatistics',      0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0');
+
+-- ---------------------------------------------------------------
+-- 鎸夐挳鏉冮檺 (type=3)
+-- ---------------------------------------------------------------
+INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, component_name, status, visible, keep_alive, always_show, creator, create_time, updater, update_time, deleted) VALUES
+-- 璁拌处鍑瘉
+(1075510, '鍑瘉鏂板',     'finance:voucher:create',        3, 1, 1075501, '', '', '', '', 0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0'),
+(1075511, '鍑瘉淇敼',     'finance:voucher:update',        3, 2, 1075501, '', '', '', '', 0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0'),
+(1075512, '鍑瘉鍒犻櫎',     'finance:voucher:delete',        3, 3, 1075501, '', '', '', '', 0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0'),
+(1075513, '鍑瘉鐘舵�佸彉鏇�', 'finance:voucher:update-status', 3, 4, 1075501, '', '', '', '', 0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0'),
+(1075514, '鍑瘉瀵煎嚭',     'finance:voucher:export',        3, 5, 1075501, '', '', '', '', 0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0'),
+-- 鎬昏处 / 鏄庣粏璐︼紙涓ら〉鍏辩敤 finance:ledger:query锛屽鍑烘潈闄愬彧鍦ㄦ澶勬寕涓�娆★紝瑙佹枃浠跺ご璇存槑 3锛�
+(1075520, '璐︾翱瀵煎嚭',     'finance:ledger:export',         3, 1, 1075502, '', '', '', '', 0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0'),
+-- 浼氳绉戠洰
+(1075521, '绉戠洰鏂板',     'finance:account-subject:create',3, 1, 1075504, '', '', '', '', 0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0'),
+(1075522, '绉戠洰淇敼',     'finance:account-subject:update',3, 2, 1075504, '', '', '', '', 0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0'),
+(1075523, '绉戠洰鍒犻櫎',     'finance:account-subject:delete',3, 3, 1075504, '', '', '', '', 0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0'),
+(1075524, '绉戠洰瀵煎嚭',     'finance:account-subject:export',3, 4, 1075504, '', '', '', '', 0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0'),
+-- 鍥哄畾璧勪骇
+(1075530, '璧勪骇鏂板',     'finance:fixed-asset:create',    3, 1, 1075505, '', '', '', '', 0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0'),
+(1075531, '璧勪骇淇敼',     'finance:fixed-asset:update',    3, 2, 1075505, '', '', '', '', 0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0'),
+(1075532, '璧勪骇鍒犻櫎',     'finance:fixed-asset:delete',    3, 3, 1075505, '', '', '', '', 0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0'),
+(1075533, '璁℃彁鎶樻棫',     'finance:fixed-asset:depreciate',3, 4, 1075505, '', '', '', '', 0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0'),
+(1075534, '璧勪骇瀵煎嚭',     'finance:fixed-asset:export',    3, 5, 1075505, '', '', '', '', 0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0'),
+-- 鏃犲舰璧勪骇
+(1075540, '璧勪骇鏂板',     'finance:intangible-asset:create',   3, 1, 1075506, '', '', '', '', 0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0'),
+(1075541, '璧勪骇淇敼',     'finance:intangible-asset:update',   3, 2, 1075506, '', '', '', '', 0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0'),
+(1075542, '璧勪骇鍒犻櫎',     'finance:intangible-asset:delete',   3, 3, 1075506, '', '', '', '', 0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0'),
+(1075543, '鏃犲舰璧勪骇鎽婇攢', 'finance:intangible-asset:amortize', 3, 4, 1075506, '', '', '', '', 0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0'),
+(1075544, '璧勪骇瀵煎嚭',     'finance:intangible-asset:export',   3, 5, 1075506, '', '', '', '', 0, b'1', b'0', b'1', '1', NOW(), '1', NOW(), b'0');
+
+-- ---------------------------------------------------------------
+-- 瑙掕壊鎺堟潈锛堝彲閫夛級
+-- 瓒呯骇绠$悊鍛� (super_admin) 鐢变唬鐮佺洿鎺ヨ繑鍥炲叏閮ㄨ彍鍗曪紝鏃犻渶鎺堟潈銆�
+-- 鑻ヨ鎶婅储鍔℃牳绠楁斁缁欐煇涓鑹诧紝鍦ㄣ�岀郴缁熺鐞� 鈫� 瑙掕壊绠$悊銆嶉噷鍕鹃�夊嵆鍙紱
+-- 涔熷彲鐢ㄤ笅闈㈢殑璇彞鐩存帴鎺堟潈锛堟妸 <roleId> 鎹㈡垚鐩爣瑙掕壊 id锛屽弬鑰冿細163 璁惧 / 164 璐ㄩ噺 / 165 閿�鍞級锛�
+--
+-- INSERT INTO system_role_menu (role_id, menu_id, creator, create_time, updater, update_time, deleted)
+-- SELECT 165, id, '1', NOW(), '1', NOW(), b'0' FROM system_menu WHERE id BETWEEN 1075500 AND 1075549;
+-- ---------------------------------------------------------------
+
+-- ---------------------------------------------------------------
+-- 鍥炴粴鑴氭湰
+-- DELETE FROM system_menu WHERE id BETWEEN 1075500 AND 1075549;
+-- DELETE FROM system_role_menu WHERE menu_id BETWEEN 1075500 AND 1075549;
+-- ---------------------------------------------------------------
diff --git a/sql/mysql/finance_module_init.sql b/sql/mysql/finance_module_init.sql
new file mode 100644
index 0000000..0975882
--- /dev/null
+++ b/sql/mysql/finance_module_init.sql
@@ -0,0 +1,331 @@
+-- =====================================================================================
+-- 璐㈠姟绠$悊妯″潡锛堜細璁℃牳绠楋級鍒濆鍖栬剼鏈�
+-- 妯″潡锛歽udao-module-finance   鎺ュ彛鍓嶇紑锛�/finance/**   琛ㄥ墠缂�锛歠in_
+--
+-- 鍐呭锛�
+--   1. 5 寮犱笟鍔¤〃锛氫細璁$鐩�佽璐﹀嚟璇併�佸嚟璇佸垎褰曘�佸浐瀹氳祫浜с�佹棤褰㈣祫浜�
+--   2. 6 缁勫瓧鍏革紙system_dict_type / system_dict_data锛�
+--   3. 涓�濂椾紒涓氫細璁″噯鍒欏父鐢ㄤ細璁$鐩瀛愭暟鎹紙浠呭湪浼氳绉戠洰琛ㄤ负绌烘椂鍐欏叆锛�
+--
+-- 璇存槑锛�
+--   * 鏈剼鏈笉鍚彍鍗�/鏉冮檺閰嶇疆锛岃彍鍗曡鑷鍦ㄣ�岀郴缁熺鐞� - 鑿滃崟绠$悊銆嶄腑閰嶇疆锛�
+--     鎺ュ彛鏉冮檺涓茶涓嬫柟鍚勮〃鐨勬帴鍙f敞閲婏紙finance:xxx:yyy锛夈��
+--   * 椤圭洰涓哄崟绉熸埛锛岃〃缁撴瀯涓嶅惈 tenant_id 瀛楁銆�
+--   * 鑴氭湰鍙噸澶嶆墽琛岋細寤鸿〃鐢� CREATE TABLE IF NOT EXISTS锛屽瓧鍏�/绉戠洰鍧囧甫瀛樺湪鎬у垽鏂��
+--   * 鎸夈�婃暟鎹簱鍒濆鍖栦笌閰嶇疆瀵煎嚭缁存姢瑙勫垯銆嬶紝鏈剼鏈唴瀹归渶鍚屾杩� docs/sql/config_export_all_<鏃ユ湡>.sql
+--     鐨勯樁娈典竴锛堢粨鏋� + 瀛楀吀閰嶇疆锛夛紝鍚﹀垯鏂板簱鍒濆鍖栦細缂鸿〃銆�
+-- =====================================================================================
+
+-- -------------------------------------------------------------------------------------
+-- 1.1 浼氳绉戠洰
+-- 鎺ュ彛锛�/finance/account-subject  鏉冮檺锛歠inance:account-subject:{query|create|update|delete|export}
+-- -------------------------------------------------------------------------------------
+CREATE TABLE IF NOT EXISTS `fin_account_subject`
+(
+    `id`                bigint       NOT NULL AUTO_INCREMENT COMMENT '绉戠洰缂栧彿',
+    `parent_id`         bigint       NULL DEFAULT NULL COMMENT '涓婄骇绉戠洰缂栧彿锛孨ULL 琛ㄧず涓�绾х鐩�',
+    `subject_code`      varchar(64)  NOT NULL COMMENT '绉戠洰缂栫爜',
+    `subject_name`      varchar(128) NOT NULL COMMENT '绉戠洰鍚嶇О',
+    `subject_type`      tinyint      NOT NULL COMMENT '绉戠洰绫诲瀷锛�1璧勪骇绫� 2璐熷�虹被 3鎵�鏈夎�呮潈鐩婄被 4鎴愭湰绫� 5鎹熺泭绫�',
+    `balance_direction` tinyint      NOT NULL COMMENT '浣欓鏂瑰悜锛�1鍊� 2璐�',
+    `status`            tinyint      NOT NULL DEFAULT 0 COMMENT '鐘舵�侊細0鍚敤 1绂佺敤',
+    `remark`            varchar(500) NULL DEFAULT '' COMMENT '澶囨敞',
+    `creator`           varchar(64)  NULL DEFAULT '' COMMENT '鍒涘缓鑰�',
+    `create_time`       datetime     NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    `updater`           varchar(64)  NULL DEFAULT '' COMMENT '鏇存柊鑰�',
+    `update_time`       datetime     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
+    `deleted`           bit(1)       NOT NULL DEFAULT b'0' COMMENT '鏄惁鍒犻櫎',
+    PRIMARY KEY (`id`),
+    KEY `idx_fin_subject_code` (`deleted`, `subject_code`),
+    KEY `idx_fin_subject_parent` (`deleted`, `parent_id`)
+) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '浼氳绉戠洰';
+
+-- -------------------------------------------------------------------------------------
+-- 1.2 璁拌处鍑瘉
+-- 鎺ュ彛锛�/finance/voucher  鏉冮檺锛歠inance:voucher:{query|create|update|delete|update-status|export}
+-- -------------------------------------------------------------------------------------
+CREATE TABLE IF NOT EXISTS `fin_voucher`
+(
+    `id`                 bigint        NOT NULL AUTO_INCREMENT COMMENT '鍑瘉缂栧彿',
+    `voucher_no`         varchar(64)   NOT NULL COMMENT '鍑瘉瀛楀彿锛屼緥濡傦細璁�2026090001',
+    `voucher_date`       date          NOT NULL COMMENT '鍑瘉鏃ユ湡',
+    `summary`            varchar(500)  NULL DEFAULT '' COMMENT '鎽樿',
+    `debit`              decimal(18,2) NOT NULL DEFAULT 0 COMMENT '鍊熸柟鍚堣',
+    `credit`             decimal(18,2) NOT NULL DEFAULT 0 COMMENT '璐锋柟鍚堣',
+    `status`             tinyint       NOT NULL DEFAULT 0 COMMENT '鍑瘉鐘舵�侊細0鏈繃璐� 10宸茶繃璐� 20宸蹭綔搴�',
+    `attachment_count`   int           NOT NULL DEFAULT 0 COMMENT '闄勪欢寮犳暟',
+    `remark`             varchar(500)  NULL DEFAULT '' COMMENT '澶囨敞',
+    `creator`            varchar(64)   NULL DEFAULT '' COMMENT '鍒跺崟浜�',
+    `create_time`        datetime      NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    `updater`            varchar(64)   NULL DEFAULT '' COMMENT '鏇存柊鑰�',
+    `update_time`        datetime      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
+    `deleted`            bit(1)        NOT NULL DEFAULT b'0' COMMENT '鏄惁鍒犻櫎',
+    PRIMARY KEY (`id`),
+    KEY `idx_fin_voucher_no` (`deleted`, `voucher_no`),
+    KEY `idx_fin_voucher_date` (`deleted`, `status`, `voucher_date`)
+) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '璁拌处鍑瘉';
+
+-- -------------------------------------------------------------------------------------
+-- 1.3 鍑瘉鍒嗗綍
+-- -------------------------------------------------------------------------------------
+CREATE TABLE IF NOT EXISTS `fin_voucher_entry`
+(
+    `id`             bigint        NOT NULL AUTO_INCREMENT COMMENT '鍒嗗綍缂栧彿',
+    `voucher_id`     bigint        NOT NULL COMMENT '鍑瘉缂栧彿锛屽叧鑱� fin_voucher.id',
+    `row_no`         int           NOT NULL DEFAULT 1 COMMENT '琛屽彿锛屼粠 1 寮�濮�',
+    `subject_code`   varchar(64)   NOT NULL COMMENT '绉戠洰缂栫爜锛屽叧鑱� fin_account_subject.subject_code',
+    `subject_name`   varchar(128)  NULL DEFAULT '' COMMENT '绉戠洰鍚嶇О锛堝啑浣欙級',
+    `summary`        varchar(500)  NULL DEFAULT '' COMMENT '鎽樿',
+    `debit`          decimal(18,2) NOT NULL DEFAULT 0 COMMENT '鍊熸柟閲戦',
+    `credit`         decimal(18,2) NOT NULL DEFAULT 0 COMMENT '璐锋柟閲戦',
+    `auxiliary_type` tinyint       NULL DEFAULT NULL COMMENT '杈呭姪鏍哥畻绫诲瀷锛�1瀹㈡埛 2渚涘簲鍟� 3閮ㄩ棬 4鍛樺伐 5椤圭洰',
+    `auxiliary_id`   varchar(64)   NULL DEFAULT '' COMMENT '杈呭姪鏍哥畻瀵硅薄缂栧彿',
+    `auxiliary_name` varchar(128)  NULL DEFAULT '' COMMENT '杈呭姪鏍哥畻瀵硅薄鍚嶇О',
+    `creator`        varchar(64)   NULL DEFAULT '' COMMENT '鍒涘缓鑰�',
+    `create_time`    datetime      NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    `updater`        varchar(64)   NULL DEFAULT '' COMMENT '鏇存柊鑰�',
+    `update_time`    datetime      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
+    `deleted`        bit(1)        NOT NULL DEFAULT b'0' COMMENT '鏄惁鍒犻櫎',
+    PRIMARY KEY (`id`),
+    KEY `idx_fin_entry_voucher` (`deleted`, `voucher_id`),
+    KEY `idx_fin_entry_subject` (`deleted`, `subject_code`)
+) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '鍑瘉鍒嗗綍';
+
+-- -------------------------------------------------------------------------------------
+-- 1.4 鍥哄畾璧勪骇
+-- 鎺ュ彛锛�/finance/fixed-asset  鏉冮檺锛歠inance:fixed-asset:{query|create|update|delete|depreciate|export}
+-- -------------------------------------------------------------------------------------
+CREATE TABLE IF NOT EXISTS `fin_fixed_asset`
+(
+    `id`                         bigint        NOT NULL AUTO_INCREMENT COMMENT '璧勪骇缂栧彿',
+    `asset_code`                 varchar(64)   NOT NULL COMMENT '璧勪骇缂栧彿',
+    `asset_name`                 varchar(128)  NOT NULL COMMENT '璧勪骇鍚嶇О',
+    `category`                   varchar(64)   NULL DEFAULT '' COMMENT '璧勪骇绫诲埆',
+    `specification`              varchar(255)  NULL DEFAULT '' COMMENT '瑙勬牸鍨嬪彿',
+    `purchase_date`              date          NOT NULL COMMENT '璐疆鏃ユ湡',
+    `original_value`             decimal(24,6) NOT NULL DEFAULT 0 COMMENT '璧勪骇鍘熷��',
+    `useful_life`                int           NOT NULL DEFAULT 1 COMMENT '浣跨敤骞撮檺锛堝勾锛�',
+    `residual_rate`              decimal(5,2)  NOT NULL DEFAULT 0 COMMENT '娈嬪�肩巼锛�%锛�',
+    `accumulated_depreciation`   decimal(24,6) NOT NULL DEFAULT 0 COMMENT '绱鎶樻棫',
+    `net_value`                  decimal(24,6) NOT NULL DEFAULT 0 COMMENT '鍑�鍊� = 鍘熷�� - 绱鎶樻棫',
+    `location`                   varchar(255)  NULL DEFAULT '' COMMENT '瀛樻斁鍦扮偣',
+    `department`                 varchar(128)  NULL DEFAULT '' COMMENT '浣跨敤閮ㄩ棬',
+    `keeper`                     varchar(64)   NULL DEFAULT '' COMMENT '淇濈浜�',
+    `status`                     tinyint       NOT NULL DEFAULT 1 COMMENT '鐘舵�侊細1鍦ㄧ敤 2闂茬疆 3缁翠慨涓� 4宸叉姤搴�',
+    `remark`                     varchar(500)  NULL DEFAULT '' COMMENT '澶囨敞',
+    `creator`                    varchar(64)   NULL DEFAULT '' COMMENT '鍒涘缓鑰�',
+    `create_time`                datetime      NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    `updater`                    varchar(64)   NULL DEFAULT '' COMMENT '鏇存柊鑰�',
+    `update_time`                datetime      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
+    `deleted`                    bit(1)        NOT NULL DEFAULT b'0' COMMENT '鏄惁鍒犻櫎',
+    PRIMARY KEY (`id`),
+    KEY `idx_fin_fixed_asset_code` (`deleted`, `asset_code`),
+    KEY `idx_fin_fixed_asset_status` (`deleted`, `status`)
+) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '鍥哄畾璧勪骇';
+
+-- -------------------------------------------------------------------------------------
+-- 1.5 鏃犲舰璧勪骇
+-- 鎺ュ彛锛�/finance/intangible-asset  鏉冮檺锛歠inance:intangible-asset:{query|create|update|delete|amortize|export}
+-- -------------------------------------------------------------------------------------
+CREATE TABLE IF NOT EXISTS `fin_intangible_asset`
+(
+    `id`                          bigint        NOT NULL AUTO_INCREMENT COMMENT '璧勪骇缂栧彿',
+    `asset_code`                  varchar(64)   NOT NULL COMMENT '璧勪骇缂栧彿',
+    `asset_name`                  varchar(128)  NOT NULL COMMENT '璧勪骇鍚嶇О',
+    `category`                    varchar(64)   NULL DEFAULT '' COMMENT '璧勪骇绫诲埆',
+    `certificate_no`              varchar(128)  NULL DEFAULT '' COMMENT '璇佷功缂栧彿',
+    `acquisition_date`            date          NOT NULL COMMENT '鍙栧緱鏃ユ湡',
+    `original_value`              decimal(24,6) NOT NULL DEFAULT 0 COMMENT '璧勪骇鍘熷��',
+    `amortization_period`         int           NOT NULL DEFAULT 1 COMMENT '鎽婇攢骞撮檺锛堝勾锛�',
+    `residual_rate`               decimal(5,2)  NOT NULL DEFAULT 0 COMMENT '娈嬪�肩巼锛�%锛�',
+    `accumulated_amortization`    decimal(24,6) NOT NULL DEFAULT 0 COMMENT '绱鎽婇攢',
+    `net_value`                   decimal(24,6) NOT NULL DEFAULT 0 COMMENT '鍑�鍊� = 鍘熷�� - 绱鎽婇攢',
+    `validity_date`               date          NULL DEFAULT NULL COMMENT '鏈夋晥鏈熻嚦',
+    `status`                      tinyint       NOT NULL DEFAULT 1 COMMENT '鐘舵�侊細1鍦ㄧ敤 2宸插け鏁� 3宸叉憡閿�瀹�',
+    `description`                 varchar(500)  NULL DEFAULT '' COMMENT '璧勪骇鎻忚堪',
+    `remark`                      varchar(500)  NULL DEFAULT '' COMMENT '澶囨敞',
+    `creator`                     varchar(64)   NULL DEFAULT '' COMMENT '鍒涘缓鑰�',
+    `create_time`                 datetime      NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '鍒涘缓鏃堕棿',
+    `updater`                     varchar(64)   NULL DEFAULT '' COMMENT '鏇存柊鑰�',
+    `update_time`                 datetime      NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '鏇存柊鏃堕棿',
+    `deleted`                     bit(1)        NOT NULL DEFAULT b'0' COMMENT '鏄惁鍒犻櫎',
+    PRIMARY KEY (`id`),
+    KEY `idx_fin_intangible_asset_code` (`deleted`, `asset_code`),
+    KEY `idx_fin_intangible_asset_status` (`deleted`, `status`)
+) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COMMENT = '鏃犲舰璧勪骇';
+
+-- -------------------------------------------------------------------------------------
+-- 2. 瀛楀吀
+-- -------------------------------------------------------------------------------------
+INSERT INTO `system_dict_type` (`name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT '浼氳绉戠洰绫诲瀷', 'finance_subject_type', 0, '璐㈠姟绠$悊 - 绉戠洰绫诲瀷', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_type` WHERE `type` = 'finance_subject_type' AND `deleted` = b'0');
+
+INSERT INTO `system_dict_type` (`name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT '浼氳浣欓鏂瑰悜', 'finance_balance_direction', 0, '璐㈠姟绠$悊 - 绉戠洰浣欓鏂瑰悜', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_type` WHERE `type` = 'finance_balance_direction' AND `deleted` = b'0');
+
+INSERT INTO `system_dict_type` (`name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT '杈呭姪鏍哥畻绫诲瀷', 'finance_auxiliary_type', 0, '璐㈠姟绠$悊 - 鍑瘉鍒嗗綍杈呭姪鏍哥畻绫诲瀷', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_type` WHERE `type` = 'finance_auxiliary_type' AND `deleted` = b'0');
+
+INSERT INTO `system_dict_type` (`name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT '鍑瘉鐘舵��', 'finance_voucher_status', 0, '璐㈠姟绠$悊 - 璁拌处鍑瘉鐘舵��', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_type` WHERE `type` = 'finance_voucher_status' AND `deleted` = b'0');
+
+INSERT INTO `system_dict_type` (`name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT '鍥哄畾璧勪骇鐘舵��', 'finance_fixed_asset_status', 0, '璐㈠姟绠$悊 - 鍥哄畾璧勪骇鐘舵��', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_type` WHERE `type` = 'finance_fixed_asset_status' AND `deleted` = b'0');
+
+INSERT INTO `system_dict_type` (`name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT '鏃犲舰璧勪骇鐘舵��', 'finance_intangible_asset_status', 0, '璐㈠姟绠$悊 - 鏃犲舰璧勪骇鐘舵��', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_type` WHERE `type` = 'finance_intangible_asset_status' AND `deleted` = b'0');
+
+INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT 1, '璧勪骇绫�', '1', 'finance_subject_type', 0, 'primary', '', '浼氳绉戠洰 - 璧勪骇绫�', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_data` WHERE `dict_type` = 'finance_subject_type' AND `value` = '1' AND `deleted` = b'0');
+INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT 2, '璐熷�虹被', '2', 'finance_subject_type', 0, 'warning', '', '浼氳绉戠洰 - 璐熷�虹被', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_data` WHERE `dict_type` = 'finance_subject_type' AND `value` = '2' AND `deleted` = b'0');
+INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT 3, '鎵�鏈夎�呮潈鐩婄被', '3', 'finance_subject_type', 0, 'success', '', '浼氳绉戠洰 - 鎵�鏈夎�呮潈鐩婄被', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_data` WHERE `dict_type` = 'finance_subject_type' AND `value` = '3' AND `deleted` = b'0');
+INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT 4, '鎴愭湰绫�', '4', 'finance_subject_type', 0, 'info', '', '浼氳绉戠洰 - 鎴愭湰绫�', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_data` WHERE `dict_type` = 'finance_subject_type' AND `value` = '4' AND `deleted` = b'0');
+INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT 5, '鎹熺泭绫�', '5', 'finance_subject_type', 0, 'danger', '', '浼氳绉戠洰 - 鎹熺泭绫�', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_data` WHERE `dict_type` = 'finance_subject_type' AND `value` = '5' AND `deleted` = b'0');
+
+INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT 1, '鍊�', '1', 'finance_balance_direction', 0, 'primary', '', '浣欓鏂瑰悜 - 鍊�', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_data` WHERE `dict_type` = 'finance_balance_direction' AND `value` = '1' AND `deleted` = b'0');
+INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT 2, '璐�', '2', 'finance_balance_direction', 0, 'warning', '', '浣欓鏂瑰悜 - 璐�', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_data` WHERE `dict_type` = 'finance_balance_direction' AND `value` = '2' AND `deleted` = b'0');
+
+INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT 1, '瀹㈡埛', '1', 'finance_auxiliary_type', 0, 'primary', '', '杈呭姪鏍哥畻 - 瀹㈡埛', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_data` WHERE `dict_type` = 'finance_auxiliary_type' AND `value` = '1' AND `deleted` = b'0');
+INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT 2, '渚涘簲鍟�', '2', 'finance_auxiliary_type', 0, 'warning', '', '杈呭姪鏍哥畻 - 渚涘簲鍟�', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_data` WHERE `dict_type` = 'finance_auxiliary_type' AND `value` = '2' AND `deleted` = b'0');
+INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT 3, '閮ㄩ棬', '3', 'finance_auxiliary_type', 0, 'info', '', '杈呭姪鏍哥畻 - 閮ㄩ棬', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_data` WHERE `dict_type` = 'finance_auxiliary_type' AND `value` = '3' AND `deleted` = b'0');
+INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT 4, '鍛樺伐', '4', 'finance_auxiliary_type', 0, 'success', '', '杈呭姪鏍哥畻 - 鍛樺伐', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_data` WHERE `dict_type` = 'finance_auxiliary_type' AND `value` = '4' AND `deleted` = b'0');
+INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT 5, '椤圭洰', '5', 'finance_auxiliary_type', 0, 'default', '', '杈呭姪鏍哥畻 - 椤圭洰', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_data` WHERE `dict_type` = 'finance_auxiliary_type' AND `value` = '5' AND `deleted` = b'0');
+
+INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT 1, '鏈繃璐�', '0', 'finance_voucher_status', 0, 'info', '', '鍑瘉鐘舵�� - 鏈繃璐�', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_data` WHERE `dict_type` = 'finance_voucher_status' AND `value` = '0' AND `deleted` = b'0');
+INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT 2, '宸茶繃璐�', '10', 'finance_voucher_status', 0, 'success', '', '鍑瘉鐘舵�� - 宸茶繃璐�', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_data` WHERE `dict_type` = 'finance_voucher_status' AND `value` = '10' AND `deleted` = b'0');
+INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT 3, '宸蹭綔搴�', '20', 'finance_voucher_status', 0, 'danger', '', '鍑瘉鐘舵�� - 宸蹭綔搴�', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_data` WHERE `dict_type` = 'finance_voucher_status' AND `value` = '20' AND `deleted` = b'0');
+
+INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT 1, '鍦ㄧ敤', '1', 'finance_fixed_asset_status', 0, 'success', '', '鍥哄畾璧勪骇 - 鍦ㄧ敤', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_data` WHERE `dict_type` = 'finance_fixed_asset_status' AND `value` = '1' AND `deleted` = b'0');
+INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT 2, '闂茬疆', '2', 'finance_fixed_asset_status', 0, 'info', '', '鍥哄畾璧勪骇 - 闂茬疆', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_data` WHERE `dict_type` = 'finance_fixed_asset_status' AND `value` = '2' AND `deleted` = b'0');
+INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT 3, '缁翠慨涓�', '3', 'finance_fixed_asset_status', 0, 'warning', '', '鍥哄畾璧勪骇 - 缁翠慨涓�', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_data` WHERE `dict_type` = 'finance_fixed_asset_status' AND `value` = '3' AND `deleted` = b'0');
+INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT 4, '宸叉姤搴�', '4', 'finance_fixed_asset_status', 0, 'danger', '', '鍥哄畾璧勪骇 - 宸叉姤搴�', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_data` WHERE `dict_type` = 'finance_fixed_asset_status' AND `value` = '4' AND `deleted` = b'0');
+
+INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT 1, '鍦ㄧ敤', '1', 'finance_intangible_asset_status', 0, 'success', '', '鏃犲舰璧勪骇 - 鍦ㄧ敤', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_data` WHERE `dict_type` = 'finance_intangible_asset_status' AND `value` = '1' AND `deleted` = b'0');
+INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT 2, '宸插け鏁�', '2', 'finance_intangible_asset_status', 0, 'danger', '', '鏃犲舰璧勪骇 - 宸插け鏁�', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_data` WHERE `dict_type` = 'finance_intangible_asset_status' AND `value` = '2' AND `deleted` = b'0');
+INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT 3, '宸叉憡閿�瀹�', '3', 'finance_intangible_asset_status', 0, 'info', '', '鏃犲舰璧勪骇 - 宸叉憡閿�瀹�', '1', NOW(), '1', NOW(), b'0'
+WHERE NOT EXISTS (SELECT 1 FROM `system_dict_data` WHERE `dict_type` = 'finance_intangible_asset_status' AND `value` = '3' AND `deleted` = b'0');
+
+-- -------------------------------------------------------------------------------------
+-- 3. 浼氳绉戠洰绉嶅瓙鏁版嵁锛堜紒涓氫細璁″噯鍒欏父鐢ㄧ鐩級
+-- 浠呭綋 fin_account_subject 涓虹┖鏃跺啓鍏ワ紝閬垮厤瑕嗙洊鐢ㄦ埛宸茬淮鎶ょ殑绉戠洰琛�
+-- parent_id 涓� NULL 琛ㄧず涓�绾х鐩紱绉戠洰缂栫爜閬靛惊銆�4 浣嶄竴绾� + 2 浣嶄簩绾с�嶇殑灞傜骇瑙勫垯
+-- -------------------------------------------------------------------------------------
+INSERT INTO `fin_account_subject` (`parent_id`, `subject_code`, `subject_name`, `subject_type`, `balance_direction`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
+SELECT *
+FROM (SELECT NULL AS parent_id, '1001' AS subject_code, '搴撳瓨鐜伴噾' AS subject_name, 1 AS subject_type, 1 AS balance_direction, 0 AS status, '' AS remark, '1' AS creator, NOW() AS create_time, '1' AS updater, NOW() AS update_time, b'0' AS deleted
+      UNION ALL SELECT NULL, '1002', '閾惰瀛樻', 1, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1012', '鍏朵粬璐у竵璧勯噾', 1, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1101', '浜ゆ槗鎬ч噾铻嶈祫浜�', 1, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1121', '搴旀敹绁ㄦ嵁', 1, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1122', '搴旀敹璐︽', 1, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1123', '棰勪粯璐︽', 1, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1131', '搴旀敹鑲″埄', 1, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1132', '搴旀敹鍒╂伅', 1, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1221', '鍏朵粬搴旀敹娆�', 1, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1231', '鍧忚处鍑嗗', 1, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1401', '鏉愭枡閲囪喘', 1, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1402', '鍦ㄩ�旂墿璧�', 1, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1403', '鍘熸潗鏂�', 1, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1405', '搴撳瓨鍟嗗搧', 1, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1406', '鍙戝嚭鍟嗗搧', 1, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1408', '濮旀墭鍔犲伐鐗╄祫', 1, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1411', '鍛ㄨ浆鏉愭枡', 1, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1471', '瀛樿揣璺屼环鍑嗗', 1, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1501', '鍊烘潈鎶曡祫', 1, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1601', '鍥哄畾璧勪骇', 1, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1602', '绱鎶樻棫', 1, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1603', '鍥哄畾璧勪骇鍑忓�煎噯澶�', 1, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1604', '鍦ㄥ缓宸ョ▼', 1, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1605', '宸ョ▼鐗╄祫', 1, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1606', '鍥哄畾璧勪骇娓呯悊', 1, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1701', '鏃犲舰璧勪骇', 1, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1702', '绱鎽婇攢', 1, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1703', '鏃犲舰璧勪骇鍑忓�煎噯澶�', 1, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1801', '闀挎湡寰呮憡璐圭敤', 1, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1811', '閫掑欢鎵�寰楃◣璧勪骇', 1, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '1901', '寰呭鐞嗚储浜ф崯婧�', 1, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '2001', '鐭湡鍊熸', 2, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '2201', '搴斾粯绁ㄦ嵁', 2, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '2202', '搴斾粯璐︽', 2, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '2203', '棰勬敹璐︽', 2, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '2211', '搴斾粯鑱屽伐钖叕', 2, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '2221', '搴斾氦绋庤垂', 2, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '2231', '搴斾粯鍒╂伅', 2, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '2232', '搴斾粯鑲″埄', 2, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '2241', '鍏朵粬搴斾粯娆�', 2, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '2501', '闀挎湡鍊熸', 2, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '2801', '棰勮璐熷��', 2, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '2901', '閫掑欢鎵�寰楃◣璐熷��', 2, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '4001', '瀹炴敹璧勬湰', 3, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '4002', '璧勬湰鍏Н', 3, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '4101', '鐩堜綑鍏Н', 3, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '4103', '鏈勾鍒╂鼎', 3, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '4104', '鍒╂鼎鍒嗛厤', 3, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '5001', '鐢熶骇鎴愭湰', 4, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '5101', '鍒堕�犺垂鐢�', 4, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '5201', '鍔冲姟鎴愭湰', 4, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '5301', '鐮斿彂鏀嚭', 4, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '6001', '涓昏惀涓氬姟鏀跺叆', 5, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '6051', '鍏朵粬涓氬姟鏀跺叆', 5, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '6101', '鍏厑浠峰�煎彉鍔ㄦ崯鐩�', 5, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '6111', '鎶曡祫鏀剁泭', 5, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '6301', '钀ヤ笟澶栨敹鍏�', 5, 2, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '6401', '涓昏惀涓氬姟鎴愭湰', 5, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '6402', '鍏朵粬涓氬姟鎴愭湰', 5, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '6403', '绋庨噾鍙婇檮鍔�', 5, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '6601', '閿�鍞垂鐢�', 5, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '6602', '绠$悊璐圭敤', 5, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '6603', '璐㈠姟璐圭敤', 5, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '6701', '璧勪骇鍑忓�兼崯澶�', 5, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '6711', '钀ヤ笟澶栨敮鍑�', 5, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '6801', '鎵�寰楃◣璐圭敤', 5, 1, 0, '', '1', NOW(), '1', NOW(), b'0'
+      UNION ALL SELECT NULL, '6901', '浠ュ墠骞村害鎹熺泭璋冩暣', 5, 1, 0, '', '1', NOW(), '1', NOW(), b'0') AS seed
+WHERE NOT EXISTS (SELECT 1 FROM `fin_account_subject` WHERE `deleted` = b'0');
diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/ColumnWidthMatchStyleStrategy.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/ColumnWidthMatchStyleStrategy.java
index 49a5b31..3ed512d 100644
--- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/ColumnWidthMatchStyleStrategy.java
+++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/ColumnWidthMatchStyleStrategy.java
@@ -41,6 +41,14 @@
         if (columnWidth < 0) {
             return;
         }
+        // 瀛楁涓婂0鏄庝簡 @ColumnWidth 鏃讹紝浠ュ0鏄庡�间负涓嬮檺銆�
+        // 鍚﹀垯娉ㄨВ鍊间細琚湰绛栫暐瑕嗙洊锛欵asyExcel 鑷繁鐨� AbstractHeadColumnWidthStyleStrategy锛堣礋璐f妸
+        // @ColumnWidth 鍐欒繘鍒楀锛変笌鏈瓥鐣� order 鐩稿悓锛堥兘鏄� 0锛屾寜娉ㄥ唽椤哄簭鎺掑湪鍓嶉潰锛夛紝
+        // 鏈瓥鐣ユ寜鍐呭闀垮害鍚庡啓锛岀洿鎺ユ妸澹版槑鐨勫搴﹀啿鎺� 鈥斺�� 渚嬪銆�2026-10-13 10:10:20銆嶅彧鏈� 19 涓瓧绗︼紝
+        // 鍒楀灏卞仠鍦� 19锛屽0鏄庡啀澶т篃娌$敤銆傚彇鏈�澶у�煎悗锛屾敞瑙f垚浜嗗唴瀹归暱搴︾殑涓嬮檺锛屽彧鏀惧涓嶆敹绐勩��
+        if (isHead && head != null && head.getColumnWidthProperty() != null) {
+            columnWidth = Math.max(columnWidth, head.getColumnWidthProperty().getWidth());
+        }
         if (columnWidth > MAX_COLUMN_WIDTH) {
             columnWidth = MAX_COLUMN_WIDTH;
         }
diff --git a/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/finance/ErpFinanceStatisticsApi.java b/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/finance/ErpFinanceStatisticsApi.java
new file mode 100644
index 0000000..24679f5
--- /dev/null
+++ b/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/finance/ErpFinanceStatisticsApi.java
@@ -0,0 +1,31 @@
+package cn.iocoder.yudao.module.erp.api.finance;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.module.erp.api.finance.dto.ErpFinanceMonthlyStatisticsRespDTO;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * ERP 璐㈠姟缁熻 API 鎺ュ彛
+ *
+ * 渚涜储鍔℃ā鍧楋紙yudao-module-finance锛夌殑璐㈠姟鎶ヨ〃 / 浼氳鏍哥畻鐪嬫澘鍙栨暟浣跨敤锛�
+ * 閬垮厤璺ㄦā鍧楃洿鎺ユ煡 erp 鐨勫簱琛�
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+public interface ErpFinanceStatisticsApi {
+
+    /**
+     * 鎸夋湀缁熻 ERP 鐨勬敹娆俱�佷粯娆俱�侀攢鍞鍗曘�侀噰璐鍗曢噾棰�
+     *
+     * 缁熻鍙e緞锛氬彧缁熻宸插鏍革紙status = 20锛夌殑鍗曟嵁锛屽凡浣滃簾銆佸鎵逛腑銆佽崏绋跨殑鍗曟嵁涓嶈鍏�
+     *
+     * @param startTime 寮�濮嬫椂闂达紙鍚級
+     * @param endTime   缁撴潫鏃堕棿锛堜笉鍚級
+     * @return 鎸夋湀缁熻缁撴灉锛屾寜鏈堜唤鍗囧簭锛涙病鏈夋暟鎹殑鏈堜唤涓嶄細杩斿洖
+     */
+    CommonResult<List<ErpFinanceMonthlyStatisticsRespDTO>> getMonthlyStatistics(LocalDateTime startTime,
+                                                                               LocalDateTime endTime);
+
+}
diff --git a/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/finance/dto/ErpFinanceMonthlyStatisticsRespDTO.java b/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/finance/dto/ErpFinanceMonthlyStatisticsRespDTO.java
new file mode 100644
index 0000000..b9bdacb
--- /dev/null
+++ b/yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/finance/dto/ErpFinanceMonthlyStatisticsRespDTO.java
@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.erp.api.finance.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Schema(description = "RPC 鏈嶅姟 - ERP 璐㈠姟鏈堝害缁熻 Response DTO")
+@Data
+public class ErpFinanceMonthlyStatisticsRespDTO {
+
+    @Schema(description = "鏈堜唤锛屾牸寮� yyyy-MM", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-10")
+    private String month;
+
+    @Schema(description = "鏀舵閲戦锛堟敹娆惧崟锛�", requiredMode = Schema.RequiredMode.REQUIRED, example = "100000.00")
+    private BigDecimal receiptPrice;
+
+    @Schema(description = "浠樻閲戦锛堜粯娆惧崟锛�", requiredMode = Schema.RequiredMode.REQUIRED, example = "60000.00")
+    private BigDecimal paymentPrice;
+
+    @Schema(description = "閿�鍞鍗曢噾棰�", requiredMode = Schema.RequiredMode.REQUIRED, example = "150000.00")
+    private BigDecimal saleOrderPrice;
+
+    @Schema(description = "閲囪喘璁㈠崟閲戦", requiredMode = Schema.RequiredMode.REQUIRED, example = "80000.00")
+    private BigDecimal purchaseOrderPrice;
+
+}
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/api/finance/ErpFinanceStatisticsApiImpl.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/api/finance/ErpFinanceStatisticsApiImpl.java
new file mode 100644
index 0000000..05853b7
--- /dev/null
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/api/finance/ErpFinanceStatisticsApiImpl.java
@@ -0,0 +1,35 @@
+package cn.iocoder.yudao.module.erp.api.finance;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.module.erp.api.finance.dto.ErpFinanceMonthlyStatisticsRespDTO;
+import cn.iocoder.yudao.module.erp.dal.mysql.finance.ErpFinanceStatisticsMapper;
+import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+/**
+ * ERP 璐㈠姟缁熻 API 瀹炵幇绫�
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Service
+@Validated
+public class ErpFinanceStatisticsApiImpl implements ErpFinanceStatisticsApi {
+
+    @Resource
+    private ErpFinanceStatisticsMapper financeStatisticsMapper;
+
+    @Override
+    public CommonResult<List<ErpFinanceMonthlyStatisticsRespDTO>> getMonthlyStatistics(LocalDateTime startTime,
+                                                                                       LocalDateTime endTime) {
+        return success(financeStatisticsMapper.selectMonthlyStatistics(ErpAuditStatus.APPROVE.getStatus(),
+                startTime, endTime));
+    }
+
+}
diff --git a/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpFinanceStatisticsMapper.java b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpFinanceStatisticsMapper.java
new file mode 100644
index 0000000..62922ef
--- /dev/null
+++ b/yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/finance/ErpFinanceStatisticsMapper.java
@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.module.erp.dal.mysql.finance;
+
+import cn.iocoder.yudao.module.erp.api.finance.dto.ErpFinanceMonthlyStatisticsRespDTO;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ * ERP 璐㈠姟缁熻 Mapper
+ *
+ * 涓鸿储鍔℃ā鍧楃殑鎶ヨ〃銆佺湅鏉挎彁渚涙寜鏈堣仛鍚堢殑鏀舵 / 浠樻 / 閿�鍞鍗� / 閲囪喘璁㈠崟閲戦
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Mapper
+public interface ErpFinanceStatisticsMapper {
+
+    /**
+     * 鎸夋湀缁熻鏀舵銆佷粯娆俱�侀攢鍞鍗曘�侀噰璐鍗曢噾棰�
+     *
+     * @param approveStatus 宸插鏍哥姸鎬佸��
+     * @param startTime     寮�濮嬫椂闂达紙鍚級
+     * @param endTime       缁撴潫鏃堕棿锛堜笉鍚級
+     * @return 鏈堝害缁熻鍒楄〃锛屾寜鏈堜唤鍗囧簭
+     */
+    List<ErpFinanceMonthlyStatisticsRespDTO> selectMonthlyStatistics(@Param("approveStatus") Integer approveStatus,
+                                                                     @Param("startTime") LocalDateTime startTime,
+                                                                     @Param("endTime") LocalDateTime endTime);
+
+}
diff --git a/yudao-module-erp/src/main/resources/mapper/finance/ErpFinanceStatisticsMapper.xml b/yudao-module-erp/src/main/resources/mapper/finance/ErpFinanceStatisticsMapper.xml
new file mode 100644
index 0000000..cd29750
--- /dev/null
+++ b/yudao-module-erp/src/main/resources/mapper/finance/ErpFinanceStatisticsMapper.xml
@@ -0,0 +1,65 @@
+<?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="cn.iocoder.yudao.module.erp.dal.mysql.finance.ErpFinanceStatisticsMapper">
+
+    <select id="selectMonthlyStatistics"
+            resultType="cn.iocoder.yudao.module.erp.api.finance.dto.ErpFinanceMonthlyStatisticsRespDTO">
+        SELECT t.month                                                    AS month,
+               IFNULL(SUM(t.receiptPrice), 0)                             AS receiptPrice,
+               IFNULL(SUM(t.paymentPrice), 0)                             AS paymentPrice,
+               IFNULL(SUM(t.saleOrderPrice), 0)                           AS saleOrderPrice,
+               IFNULL(SUM(t.purchaseOrderPrice), 0)                       AS purchaseOrderPrice
+        FROM (
+            SELECT DATE_FORMAT(receipt_time, '%Y-%m')  AS month,
+                   IFNULL(SUM(receipt_price), 0)       AS receiptPrice,
+                   0                                   AS paymentPrice,
+                   0                                   AS saleOrderPrice,
+                   0                                   AS purchaseOrderPrice
+            FROM erp_finance_receipt
+            WHERE deleted = 0
+              AND status = #{approveStatus}
+              AND receipt_time &gt;= #{startTime}
+              AND receipt_time &lt; #{endTime}
+            GROUP BY DATE_FORMAT(receipt_time, '%Y-%m')
+            UNION ALL
+            SELECT DATE_FORMAT(payment_time, '%Y-%m'),
+                   IFNULL(SUM(payment_price), 0),
+                   0,
+                   0,
+                   0
+            FROM erp_finance_payment
+            WHERE deleted = 0
+              AND status = #{approveStatus}
+              AND payment_time &gt;= #{startTime}
+              AND payment_time &lt; #{endTime}
+            GROUP BY DATE_FORMAT(payment_time, '%Y-%m')
+            UNION ALL
+            SELECT DATE_FORMAT(order_time, '%Y-%m'),
+                   IFNULL(SUM(total_price), 0),
+                   0,
+                   0,
+                   0
+            FROM erp_sale_order
+            WHERE deleted = 0
+              AND status = #{approveStatus}
+              AND order_time &gt;= #{startTime}
+              AND order_time &lt; #{endTime}
+            GROUP BY DATE_FORMAT(order_time, '%Y-%m')
+            UNION ALL
+            SELECT DATE_FORMAT(order_time, '%Y-%m'),
+                   IFNULL(SUM(total_price), 0),
+                   0,
+                   0,
+                   0
+            FROM erp_purchase_order
+            WHERE deleted = 0
+              AND status = #{approveStatus}
+              AND order_time &gt;= #{startTime}
+              AND order_time &lt; #{endTime}
+            GROUP BY DATE_FORMAT(order_time, '%Y-%m')
+        ) t
+        GROUP BY t.month
+        ORDER BY t.month
+    </select>
+
+</mapper>
diff --git a/yudao-module-finance/pom.xml b/yudao-module-finance/pom.xml
new file mode 100644
index 0000000..772e265
--- /dev/null
+++ b/yudao-module-finance/pom.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <groupId>cn.iocoder.boot</groupId>
+        <artifactId>yudao</artifactId>
+        <version>${revision}</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>yudao-module-finance</artifactId>
+    <packaging>jar</packaging>
+
+    <name>${project.artifactId}</name>
+    <description>
+        finance 鍖呬笅锛岃储鍔$鐞嗭紙Financial Management锛夈��
+        渚嬪璇达細浼氳绉戠洰銆佸嚟璇併�佹�昏处銆佹槑缁嗚处銆佸浐瀹氳祫浜с�佹棤褰㈣祫浜с�佽储鍔℃姤琛ㄧ瓑绛�
+    </description>
+
+    <dependencies>
+        <!-- System 妯″潡锛堟枃浠朵笂浼犺蛋 system 妯″潡鐨� Storage 闄勪欢鏈哄埗锛� -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-module-system</artifactId>
+            <version>${revision}</version>
+        </dependency>
+
+        <!-- ERP 妯″潡 API锛堣储鍔℃姤琛ㄩ渶瑕佽鍙栨敹浠樻鍗曘�侀噰璐攢鍞鍗曟暟鎹級 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-module-erp-api</artifactId>
+            <version>${revision}</version>
+        </dependency>
+
+        <!-- Web 鐩稿叧 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-web</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-security</artifactId>
+        </dependency>
+
+        <!-- DB 鐩稿叧 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-mybatis</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-redis</artifactId>
+        </dependency>
+
+        <!-- 宸ュ叿绫荤浉鍏� -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-excel</artifactId>
+        </dependency>
+
+        <!-- Test 娴嬭瘯鐩稿叧 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-spring-boot-starter-test</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/FinFixedAssetController.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/FinFixedAssetController.java
new file mode 100644
index 0000000..9d54c57
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/FinFixedAssetController.java
@@ -0,0 +1,188 @@
+package cn.iocoder.yudao.module.finance.controller.admin.asset;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.module.finance.controller.admin.asset.vo.FinFixedAssetDepreciateRespVO;
+import cn.iocoder.yudao.module.finance.controller.admin.asset.vo.FinFixedAssetPageReqVO;
+import cn.iocoder.yudao.module.finance.controller.admin.asset.vo.FinFixedAssetRespVO;
+import cn.iocoder.yudao.module.finance.controller.admin.asset.vo.FinFixedAssetSaveReqVO;
+import cn.iocoder.yudao.module.finance.dal.dataobject.asset.FinFixedAssetDO;
+import cn.iocoder.yudao.module.finance.service.asset.FinAssetAmountUtils;
+import cn.iocoder.yudao.module.finance.service.asset.FinFixedAssetService;
+import cn.iocoder.yudao.module.system.api.dept.DeptApi;
+import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
+
+@Tag(name = "绠$悊鍚庡彴 - 鍥哄畾璧勪骇")
+@RestController
+@RequestMapping("/finance/fixed-asset")
+@Validated
+public class FinFixedAssetController {
+
+    @Resource
+    private FinFixedAssetService fixedAssetService;
+    @Resource
+    private DeptApi deptApi;
+    @Resource
+    private AdminUserApi adminUserApi;
+
+    @PostMapping("/create")
+    @Operation(summary = "鍒涘缓鍥哄畾璧勪骇")
+    @PreAuthorize("@ss.hasPermission('finance:fixed-asset:create')")
+    public CommonResult<Long> createFixedAsset(@Valid @RequestBody FinFixedAssetSaveReqVO createReqVO) {
+        return success(fixedAssetService.createFixedAsset(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "鏇存柊鍥哄畾璧勪骇")
+    @PreAuthorize("@ss.hasPermission('finance:fixed-asset:update')")
+    public CommonResult<Boolean> updateFixedAsset(@Valid @RequestBody FinFixedAssetSaveReqVO updateReqVO) {
+        fixedAssetService.updateFixedAsset(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "鍒犻櫎鍥哄畾璧勪骇")
+    @Parameter(name = "id", description = "缂栧彿", required = true)
+    @PreAuthorize("@ss.hasPermission('finance:fixed-asset:delete')")
+    public CommonResult<Boolean> deleteFixedAsset(@RequestParam("id") Long id) {
+        fixedAssetService.deleteFixedAsset(id);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete-list")
+    @Operation(summary = "鎵归噺鍒犻櫎鍥哄畾璧勪骇")
+    @Parameter(name = "ids", description = "缂栧彿鍒楄〃", required = true)
+    @PreAuthorize("@ss.hasPermission('finance:fixed-asset:delete')")
+    public CommonResult<Boolean> deleteFixedAssetList(@RequestParam("ids") List<Long> ids) {
+        fixedAssetService.deleteFixedAssetList(ids);
+        return success(true);
+    }
+
+    @PostMapping("/depreciate")
+    @Operation(summary = "璁℃彁鎶樻棫", description = "鎸夎祫浜ч�愪釜璁℃彁涓�涓湀鐨勬姌鏃э紱ids 涓虹┖鏃跺鎵�鏈夋湭鎶ュ簾璧勪骇璁℃彁")
+    @Parameter(name = "ids", description = "璧勪骇缂栧彿鍒楄〃锛屼负绌鸿〃绀哄叏閮�", required = false)
+    @PreAuthorize("@ss.hasPermission('finance:fixed-asset:depreciate')")
+    public CommonResult<FinFixedAssetDepreciateRespVO> depreciateFixedAssetList(
+            @RequestParam(value = "ids", required = false) List<Long> ids) {
+        return success(fixedAssetService.depreciateFixedAssetList(ids));
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "鑾峰緱鍥哄畾璧勪骇")
+    @Parameter(name = "id", description = "缂栧彿", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('finance:fixed-asset:query')")
+    public CommonResult<FinFixedAssetRespVO> getFixedAsset(@RequestParam("id") Long id) {
+        FinFixedAssetDO asset = fixedAssetService.getFixedAsset(id);
+        if (asset == null) {
+            return success(null);
+        }
+        FinFixedAssetRespVO respVO = toRespVO(asset);
+        fillDepartmentAndKeeper(List.of(respVO));
+        return success(respVO);
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "鑾峰緱鍥哄畾璧勪骇鍒嗛〉")
+    @PreAuthorize("@ss.hasPermission('finance:fixed-asset:query')")
+    public CommonResult<PageResult<FinFixedAssetRespVO>> getFixedAssetPage(@Valid FinFixedAssetPageReqVO pageReqVO) {
+        PageResult<FinFixedAssetDO> pageResult = fixedAssetService.getFixedAssetPage(pageReqVO);
+        PageResult<FinFixedAssetRespVO> result = BeanUtils.toBean(pageResult, FinFixedAssetRespVO.class,
+                this::fillMonthlyDepreciation);
+        fillDepartmentAndKeeper(result.getList());
+        return success(result);
+    }
+
+    @GetMapping("/list")
+    @Operation(summary = "鑾峰緱鍥哄畾璧勪骇鍒楄〃")
+    @PreAuthorize("@ss.hasPermission('finance:fixed-asset:query')")
+    public CommonResult<List<FinFixedAssetRespVO>> getFixedAssetList() {
+        List<FinFixedAssetDO> list = fixedAssetService.getFixedAssetList();
+        List<FinFixedAssetRespVO> voList = convertList(list, this::toRespVO);
+        fillDepartmentAndKeeper(voList);
+        return success(voList);
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "瀵煎嚭鍥哄畾璧勪骇 Excel")
+    @PreAuthorize("@ss.hasPermission('finance:fixed-asset:export')")
+    @ApiAccessLog(operateType = EXPORT)
+    public void exportFixedAssetExcel(@Valid FinFixedAssetPageReqVO pageReqVO,
+                                      HttpServletResponse response) throws IOException {
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<FinFixedAssetDO> list = fixedAssetService.getFixedAssetPage(pageReqVO).getList();
+        List<FinFixedAssetRespVO> voList = BeanUtils.toBean(list, FinFixedAssetRespVO.class,
+                this::fillMonthlyDepreciation);
+        fillDepartmentAndKeeper(voList);
+        // 瀵煎嚭 Excel
+        ExcelUtils.write(response, "鍥哄畾璧勪骇.xls", "鏁版嵁", FinFixedAssetRespVO.class, voList);
+    }
+
+    private FinFixedAssetRespVO toRespVO(FinFixedAssetDO asset) {
+        return fillMonthlyDepreciation(BeanUtils.toBean(asset, FinFixedAssetRespVO.class));
+    }
+
+    /**
+     * 鍥炲~鏈堟姌鏃ч锛堜笉钀藉簱锛屼粎鐢ㄤ簬灞曠ず锛屽彛寰勪笌璁℃彁鎶樻棫涓�鑷达級
+     */
+    private FinFixedAssetRespVO fillMonthlyDepreciation(FinFixedAssetRespVO respVO) {
+        if (respVO.getUsefulLife() == null || respVO.getUsefulLife() <= 0
+                || respVO.getOriginalValue() == null) {
+            return respVO;
+        }
+        return respVO.setMonthlyDepreciation(FinAssetAmountUtils.calcMonthlyAmount(
+                respVO.getOriginalValue(), respVO.getResidualRate(), respVO.getUsefulLife()));
+    }
+
+    /**
+     * 鎵归噺鍥炲~銆屼娇鐢ㄩ儴闂ㄥ悕绉般�嶄笌銆屼繚绠′汉鏄电О銆嶃��
+     *
+     * DO 涓婂瓨鐨勬槸 departmentId / keeperId锛岃�屽垪琛ㄤ笌瀵煎嚭瑕佸睍绀哄悕绉帮紝
+     * 鎵�浠ユ寜 ERP 鏀舵鍗曠殑鏃㈡湁鍙e緞锛屽湪杩欓噷鎶婇儴闂ㄣ�佺敤鎴峰悇鎵归噺鏌ヤ竴娆″啀鍥炲~锛岄伩鍏嶉�愯鏌ュ簱銆�
+     * 瑙f瀽涓嶅埌鐨勶紙閮ㄩ棬/鐢ㄦ埛琚垹闄ゃ�佺紪鍙蜂负绌猴級灏变繚鎸佷负绌猴紝涓嶆姏寮傚父銆�
+     */
+    private void fillDepartmentAndKeeper(List<FinFixedAssetRespVO> list) {
+        if (CollUtil.isEmpty(list)) {
+            return;
+        }
+        Set<Long> deptIds = convertSet(list, FinFixedAssetRespVO::getDepartmentId,
+                respVO -> respVO.getDepartmentId() != null);
+        Set<Long> keeperIds = convertSet(list, FinFixedAssetRespVO::getKeeperId,
+                respVO -> respVO.getKeeperId() != null);
+        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(deptIds);
+        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(keeperIds);
+        list.forEach(respVO -> {
+            MapUtils.findAndThen(deptMap, respVO.getDepartmentId(),
+                    dept -> respVO.setDepartmentName(dept.getName()));
+            MapUtils.findAndThen(userMap, respVO.getKeeperId(),
+                    user -> respVO.setKeeperName(user.getNickname()));
+        });
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/FinIntangibleAssetController.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/FinIntangibleAssetController.java
new file mode 100644
index 0000000..95796ae
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/FinIntangibleAssetController.java
@@ -0,0 +1,139 @@
+package cn.iocoder.yudao.module.finance.controller.admin.asset;
+
+import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.module.finance.controller.admin.asset.vo.FinIntangibleAssetAmortizeRespVO;
+import cn.iocoder.yudao.module.finance.controller.admin.asset.vo.FinIntangibleAssetPageReqVO;
+import cn.iocoder.yudao.module.finance.controller.admin.asset.vo.FinIntangibleAssetRespVO;
+import cn.iocoder.yudao.module.finance.controller.admin.asset.vo.FinIntangibleAssetSaveReqVO;
+import cn.iocoder.yudao.module.finance.dal.dataobject.asset.FinIntangibleAssetDO;
+import cn.iocoder.yudao.module.finance.service.asset.FinAssetAmountUtils;
+import cn.iocoder.yudao.module.finance.service.asset.FinIntangibleAssetService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.IOException;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
+
+@Tag(name = "绠$悊鍚庡彴 - 鏃犲舰璧勪骇")
+@RestController
+@RequestMapping("/finance/intangible-asset")
+@Validated
+public class FinIntangibleAssetController {
+
+    @Resource
+    private FinIntangibleAssetService intangibleAssetService;
+
+    @PostMapping("/create")
+    @Operation(summary = "鍒涘缓鏃犲舰璧勪骇")
+    @PreAuthorize("@ss.hasPermission('finance:intangible-asset:create')")
+    public CommonResult<Long> createIntangibleAsset(@Valid @RequestBody FinIntangibleAssetSaveReqVO createReqVO) {
+        return success(intangibleAssetService.createIntangibleAsset(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "鏇存柊鏃犲舰璧勪骇")
+    @PreAuthorize("@ss.hasPermission('finance:intangible-asset:update')")
+    public CommonResult<Boolean> updateIntangibleAsset(@Valid @RequestBody FinIntangibleAssetSaveReqVO updateReqVO) {
+        intangibleAssetService.updateIntangibleAsset(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "鍒犻櫎鏃犲舰璧勪骇")
+    @Parameter(name = "id", description = "缂栧彿", required = true)
+    @PreAuthorize("@ss.hasPermission('finance:intangible-asset:delete')")
+    public CommonResult<Boolean> deleteIntangibleAsset(@RequestParam("id") Long id) {
+        intangibleAssetService.deleteIntangibleAsset(id);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete-list")
+    @Operation(summary = "鎵归噺鍒犻櫎鏃犲舰璧勪骇")
+    @Parameter(name = "ids", description = "缂栧彿鍒楄〃", required = true)
+    @PreAuthorize("@ss.hasPermission('finance:intangible-asset:delete')")
+    public CommonResult<Boolean> deleteIntangibleAssetList(@RequestParam("ids") List<Long> ids) {
+        intangibleAssetService.deleteIntangibleAssetList(ids);
+        return success(true);
+    }
+
+    @PostMapping("/amortize")
+    @Operation(summary = "璁℃彁鎽婇攢", description = "鎸夎祫浜ч�愪釜璁℃彁涓�涓湀鐨勬憡閿�锛沬ds 涓虹┖鏃跺鎵�鏈夋湭澶辨晥璧勪骇璁℃彁")
+    @Parameter(name = "ids", description = "璧勪骇缂栧彿鍒楄〃锛屼负绌鸿〃绀哄叏閮�", required = false)
+    @PreAuthorize("@ss.hasPermission('finance:intangible-asset:amortize')")
+    public CommonResult<FinIntangibleAssetAmortizeRespVO> amortizeIntangibleAssetList(
+            @RequestParam(value = "ids", required = false) List<Long> ids) {
+        return success(intangibleAssetService.amortizeIntangibleAssetList(ids));
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "鑾峰緱鏃犲舰璧勪骇")
+    @Parameter(name = "id", description = "缂栧彿", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('finance:intangible-asset:query')")
+    public CommonResult<FinIntangibleAssetRespVO> getIntangibleAsset(@RequestParam("id") Long id) {
+        FinIntangibleAssetDO asset = intangibleAssetService.getIntangibleAsset(id);
+        return success(asset == null ? null : toRespVO(asset));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "鑾峰緱鏃犲舰璧勪骇鍒嗛〉")
+    @PreAuthorize("@ss.hasPermission('finance:intangible-asset:query')")
+    public CommonResult<PageResult<FinIntangibleAssetRespVO>> getIntangibleAssetPage(
+            @Valid FinIntangibleAssetPageReqVO pageReqVO) {
+        PageResult<FinIntangibleAssetDO> pageResult = intangibleAssetService.getIntangibleAssetPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, FinIntangibleAssetRespVO.class, this::fillMonthlyAmortization));
+    }
+
+    @GetMapping("/list")
+    @Operation(summary = "鑾峰緱鏃犲舰璧勪骇鍒楄〃")
+    @PreAuthorize("@ss.hasPermission('finance:intangible-asset:query')")
+    public CommonResult<List<FinIntangibleAssetRespVO>> getIntangibleAssetList() {
+        List<FinIntangibleAssetDO> list = intangibleAssetService.getIntangibleAssetList();
+        return success(convertList(list, this::toRespVO));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "瀵煎嚭鏃犲舰璧勪骇 Excel")
+    @PreAuthorize("@ss.hasPermission('finance:intangible-asset:export')")
+    @ApiAccessLog(operateType = EXPORT)
+    public void exportIntangibleAssetExcel(@Valid FinIntangibleAssetPageReqVO pageReqVO,
+                                           HttpServletResponse response) throws IOException {
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<FinIntangibleAssetDO> list = intangibleAssetService.getIntangibleAssetPage(pageReqVO).getList();
+        // 瀵煎嚭 Excel
+        ExcelUtils.write(response, "鏃犲舰璧勪骇.xls", "鏁版嵁", FinIntangibleAssetRespVO.class,
+                BeanUtils.toBean(list, FinIntangibleAssetRespVO.class, this::fillMonthlyAmortization));
+    }
+
+    private FinIntangibleAssetRespVO toRespVO(FinIntangibleAssetDO asset) {
+        return fillMonthlyAmortization(BeanUtils.toBean(asset, FinIntangibleAssetRespVO.class));
+    }
+
+    /**
+     * 鍥炲~鏈堟憡閿�棰濓紙涓嶈惤搴擄紝浠呯敤浜庡睍绀猴紝鍙e緞涓庤鎻愭憡閿�涓�鑷达級
+     */
+    private FinIntangibleAssetRespVO fillMonthlyAmortization(FinIntangibleAssetRespVO respVO) {
+        if (respVO.getAmortizationPeriod() == null || respVO.getAmortizationPeriod() <= 0
+                || respVO.getOriginalValue() == null) {
+            return respVO;
+        }
+        return respVO.setMonthlyAmortization(FinAssetAmountUtils.calcMonthlyAmount(
+                respVO.getOriginalValue(), respVO.getResidualRate(), respVO.getAmortizationPeriod()));
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinFixedAssetDepreciateRespVO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinFixedAssetDepreciateRespVO.java
new file mode 100644
index 0000000..922c717
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinFixedAssetDepreciateRespVO.java
@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.finance.controller.admin.asset.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Schema(description = "绠$悊鍚庡彴 - 鍥哄畾璧勪骇璁℃彁鎶樻棫 Response VO")
+@Data
+public class FinFixedAssetDepreciateRespVO {
+
+    @Schema(description = "鏈璁℃彁鎶樻棫鐨勮祫浜ф暟閲�", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
+    private Integer processedCount;
+
+    @Schema(description = "鏈璁℃彁鐨勬湀鎶樻棫棰濆悎璁�", requiredMode = Schema.RequiredMode.REQUIRED, example = "2850.00")
+    private BigDecimal totalMonthlyDepreciation;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinFixedAssetPageReqVO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinFixedAssetPageReqVO.java
new file mode 100644
index 0000000..fb420c3
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinFixedAssetPageReqVO.java
@@ -0,0 +1,42 @@
+package cn.iocoder.yudao.module.finance.controller.admin.asset.vo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDate;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
+
+@Schema(description = "绠$悊鍚庡彴 - 鍥哄畾璧勪骇鍒嗛〉 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class FinFixedAssetPageReqVO extends PageParam {
+
+    @Schema(description = "璧勪骇缂栧彿", example = "GD202610120001")
+    private String assetCode;
+
+    @Schema(description = "璧勪骇鍚嶇О", example = "鏁版帶杞﹀簥")
+    private String assetName;
+
+    @Schema(description = "璧勪骇绫诲埆", example = "鐢熶骇璁惧")
+    private String category;
+
+    @Schema(description = "浣跨敤閮ㄩ棬缂栧彿", example = "100")
+    private Long departmentId;
+
+    @Schema(description = "淇濈浜虹紪鍙凤紙鐢ㄦ埛缂栧彿锛�", example = "1")
+    private Long keeperId;
+
+    @Schema(description = "鐘舵��", example = "1")
+    private Integer status;
+
+    @Schema(description = "璐疆鏃ユ湡鍖洪棿")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+    private LocalDate[] purchaseDate;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinFixedAssetRespVO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinFixedAssetRespVO.java
new file mode 100644
index 0000000..c365f1a
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinFixedAssetRespVO.java
@@ -0,0 +1,106 @@
+package cn.iocoder.yudao.module.finance.controller.admin.asset.vo;
+
+import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
+import cn.iocoder.yudao.module.finance.enums.DictTypeConstants;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import cn.idev.excel.annotation.write.style.ColumnWidth;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@Schema(description = "绠$悊鍚庡彴 - 鍥哄畾璧勪骇 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class FinFixedAssetRespVO {
+
+    @Schema(description = "璧勪骇缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    private Long id;
+
+    @Schema(description = "璧勪骇缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "GD202610120001")
+    @ExcelProperty("璧勪骇缂栧彿")
+    private String assetCode;
+
+    @Schema(description = "璧勪骇鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED, example = "鏁版帶杞﹀簥")
+    @ExcelProperty("璧勪骇鍚嶇О")
+    private String assetName;
+
+    @Schema(description = "璧勪骇绫诲埆", example = "鐢熶骇璁惧")
+    @ExcelProperty("璧勪骇绫诲埆")
+    private String category;
+
+    @Schema(description = "瑙勬牸鍨嬪彿", example = "CK6140")
+    @ExcelProperty("瑙勬牸鍨嬪彿")
+    private String specification;
+
+    @Schema(description = "璐疆鏃ユ湡", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("璐疆鏃ユ湡")
+    private LocalDate purchaseDate;
+
+    @Schema(description = "璧勪骇鍘熷��", requiredMode = Schema.RequiredMode.REQUIRED, example = "120000.00")
+    @ExcelProperty("璧勪骇鍘熷��")
+    private BigDecimal originalValue;
+
+    @Schema(description = "浣跨敤骞撮檺锛堝勾锛�", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+    @ExcelProperty("浣跨敤骞撮檺(骞�)")
+    private Integer usefulLife;
+
+    @Schema(description = "娈嬪�肩巼锛�%锛�", example = "5.00")
+    @ExcelProperty("娈嬪�肩巼(%)")
+    private BigDecimal residualRate;
+
+    @Schema(description = "鏈堟姌鏃ч", example = "950.00")
+    @ExcelProperty("鏈堟姌鏃ч")
+    private BigDecimal monthlyDepreciation;
+
+    @Schema(description = "绱鎶樻棫", requiredMode = Schema.RequiredMode.REQUIRED, example = "11400.00")
+    @ExcelProperty("绱鎶樻棫")
+    private BigDecimal accumulatedDepreciation;
+
+    @Schema(description = "鍑�鍊�", requiredMode = Schema.RequiredMode.REQUIRED, example = "108600.00")
+    @ExcelProperty("鍑�鍊�")
+    private BigDecimal netValue;
+
+    @Schema(description = "瀛樻斁鍦扮偣", example = "涓�鍙疯溅闂�")
+    @ExcelProperty("瀛樻斁鍦扮偣")
+    private String location;
+
+    @Schema(description = "浣跨敤閮ㄩ棬缂栧彿", example = "100")
+    private Long departmentId;
+
+    /**
+     * 浣跨敤閮ㄩ棬鍚嶇О锛屽垪琛ㄤ笌 Excel 瀵煎嚭鐢ㄣ��
+     * departmentId 瀛樼殑鏄儴闂ㄧ紪鍙凤紝鐩存帴灞曠ず灏辨槸涓�涓叉暟瀛楋紝鎵�浠ュ彟鐢ㄨ繖涓瓧娈垫壙杞藉悕绉帮紱
+     * 涓庡嚟璇佺殑鍒跺崟浜烘樀绉� creatorName 鍚屼竴濂楀彛寰勶紝鐢� Controller 鎵归噺瑙f瀽鍚庡洖濉��
+     */
+    @Schema(description = "浣跨敤閮ㄩ棬鍚嶇О", example = "鐢熶骇閮�")
+    @ExcelProperty("浣跨敤閮ㄩ棬")
+    private String departmentName;
+
+    @Schema(description = "淇濈浜虹紪鍙凤紙鐢ㄦ埛缂栧彿锛�", example = "1")
+    private Long keeperId;
+
+    @Schema(description = "淇濈浜烘樀绉�", example = "寮犱笁")
+    @ExcelProperty("淇濈浜�")
+    private String keeperName;
+
+    @Schema(description = "鐘舵��", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @ExcelProperty(value = "鐘舵��", converter = DictConvert.class)
+    @DictFormat(DictTypeConstants.FIXED_ASSET_STATUS)
+    private Integer status;
+
+    @Schema(description = "澶囨敞", example = "澶囨敞")
+    @ExcelProperty("澶囨敞")
+    private String remark;
+
+    @Schema(description = "鍒涘缓鏃堕棿", requiredMode = Schema.RequiredMode.REQUIRED)
+    // 銆寉yyy-MM-dd HH:mm:ss銆嶆濂� 19 涓瓧绗︼紝鑷�傚簲鍒楀灏卞彧缁� 19锛岃创鐫�杈瑰緢闅剧湅锛岃繖閲屾斁瀹藉埌 22
+    @ColumnWidth(22)
+    @ExcelProperty("鍒涘缓鏃堕棿")
+    private LocalDateTime createTime;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinFixedAssetSaveReqVO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinFixedAssetSaveReqVO.java
new file mode 100644
index 0000000..ab25d5f
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinFixedAssetSaveReqVO.java
@@ -0,0 +1,83 @@
+package cn.iocoder.yudao.module.finance.controller.admin.asset.vo;
+
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import cn.iocoder.yudao.module.finance.enums.FinFixedAssetStatusEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.DecimalMax;
+import jakarta.validation.constraints.DecimalMin;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
+
+@Schema(description = "绠$悊鍚庡彴 - 鍥哄畾璧勪骇鏂板/淇敼 Request VO")
+@Data
+public class FinFixedAssetSaveReqVO {
+
+    @Schema(description = "璧勪骇缂栧彿", example = "1024")
+    private Long id;
+
+    @Schema(description = "璧勪骇缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "GD202610120001")
+    @NotEmpty(message = "璧勪骇缂栧彿涓嶈兘涓虹┖")
+    @Size(max = 64, message = "璧勪骇缂栧彿闀垮害涓嶈兘瓒呰繃 64 涓瓧绗�")
+    private String assetCode;
+
+    @Schema(description = "璧勪骇鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED, example = "鏁版帶杞﹀簥")
+    @NotEmpty(message = "璧勪骇鍚嶇О涓嶈兘涓虹┖")
+    @Size(max = 128, message = "璧勪骇鍚嶇О闀垮害涓嶈兘瓒呰繃 128 涓瓧绗�")
+    private String assetName;
+
+    @Schema(description = "璧勪骇绫诲埆", example = "鐢熶骇璁惧")
+    @Size(max = 64, message = "璧勪骇绫诲埆闀垮害涓嶈兘瓒呰繃 64 涓瓧绗�")
+    private String category;
+
+    @Schema(description = "瑙勬牸鍨嬪彿", example = "CK6140")
+    @Size(max = 255, message = "瑙勬牸鍨嬪彿闀垮害涓嶈兘瓒呰繃 255 涓瓧绗�")
+    private String specification;
+
+    @Schema(description = "璐疆鏃ユ湡", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull(message = "璐疆鏃ユ湡涓嶈兘涓虹┖")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+    private LocalDate purchaseDate;
+
+    @Schema(description = "璧勪骇鍘熷��", requiredMode = Schema.RequiredMode.REQUIRED, example = "120000.00")
+    @NotNull(message = "璧勪骇鍘熷�间笉鑳戒负绌�")
+    @DecimalMin(value = "0", message = "璧勪骇鍘熷�间笉鑳藉皬浜� 0")
+    private BigDecimal originalValue;
+
+    @Schema(description = "浣跨敤骞撮檺锛堝勾锛�", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+    @NotNull(message = "浣跨敤骞撮檺涓嶈兘涓虹┖")
+    @Min(value = 1, message = "浣跨敤骞撮檺蹇呴』澶т簬绛変簬 1 骞�")
+    private Integer usefulLife;
+
+    @Schema(description = "娈嬪�肩巼锛�%锛�", example = "5.00")
+    @DecimalMin(value = "0", message = "娈嬪�肩巼涓嶈兘灏忎簬 0")
+    @DecimalMax(value = "100", message = "娈嬪�肩巼涓嶈兘澶т簬 100")
+    private BigDecimal residualRate;
+
+    @Schema(description = "瀛樻斁鍦扮偣", example = "涓�鍙疯溅闂�")
+    @Size(max = 255, message = "瀛樻斁鍦扮偣闀垮害涓嶈兘瓒呰繃 255 涓瓧绗�")
+    private String location;
+
+    @Schema(description = "浣跨敤閮ㄩ棬缂栧彿", example = "100")
+    private Long departmentId;
+
+    @Schema(description = "淇濈浜虹紪鍙凤紙鐢ㄦ埛缂栧彿锛�", example = "1")
+    private Long keeperId;
+
+    @Schema(description = "鐘舵��", example = "1")
+    @InEnum(value = FinFixedAssetStatusEnum.class, message = "鐘舵�佸繀椤绘槸 {value}")
+    private Integer status;
+
+    @Schema(description = "澶囨敞", example = "澶囨敞")
+    @Size(max = 500, message = "澶囨敞闀垮害涓嶈兘瓒呰繃 500 涓瓧绗�")
+    private String remark;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinIntangibleAssetAmortizeRespVO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinIntangibleAssetAmortizeRespVO.java
new file mode 100644
index 0000000..942e13c
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinIntangibleAssetAmortizeRespVO.java
@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.finance.controller.admin.asset.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Schema(description = "绠$悊鍚庡彴 - 鏃犲舰璧勪骇璁℃彁鎽婇攢 Response VO")
+@Data
+public class FinIntangibleAssetAmortizeRespVO {
+
+    @Schema(description = "鏈璁℃彁鎽婇攢鐨勮祫浜ф暟閲�", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
+    private Integer processedCount;
+
+    @Schema(description = "鏈璁℃彁鐨勬湀鎽婇攢棰濆悎璁�", requiredMode = Schema.RequiredMode.REQUIRED, example = "3000.00")
+    private BigDecimal totalMonthlyAmortization;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinIntangibleAssetPageReqVO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinIntangibleAssetPageReqVO.java
new file mode 100644
index 0000000..2a52c41
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinIntangibleAssetPageReqVO.java
@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.finance.controller.admin.asset.vo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDate;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
+
+@Schema(description = "绠$悊鍚庡彴 - 鏃犲舰璧勪骇鍒嗛〉 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class FinIntangibleAssetPageReqVO extends PageParam {
+
+    @Schema(description = "璧勪骇缂栧彿", example = "WX202610120001")
+    private String assetCode;
+
+    @Schema(description = "璧勪骇鍚嶇О", example = "ERP 杞欢钁椾綔鏉�")
+    private String assetName;
+
+    @Schema(description = "璧勪骇绫诲埆", example = "杞欢")
+    private String category;
+
+    @Schema(description = "璇佷功缂栧彿", example = "杞憲鐧诲瓧绗� 1234567 鍙�")
+    private String certificateNo;
+
+    @Schema(description = "鐘舵��", example = "1")
+    private Integer status;
+
+    @Schema(description = "鍙栧緱鏃ユ湡鍖洪棿")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+    private LocalDate[] acquisitionDate;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinIntangibleAssetRespVO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinIntangibleAssetRespVO.java
new file mode 100644
index 0000000..dac99ae
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinIntangibleAssetRespVO.java
@@ -0,0 +1,91 @@
+package cn.iocoder.yudao.module.finance.controller.admin.asset.vo;
+
+import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
+import cn.iocoder.yudao.module.finance.enums.DictTypeConstants;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import cn.idev.excel.annotation.write.style.ColumnWidth;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@Schema(description = "绠$悊鍚庡彴 - 鏃犲舰璧勪骇 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class FinIntangibleAssetRespVO {
+
+    @Schema(description = "璧勪骇缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    private Long id;
+
+    @Schema(description = "璧勪骇缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "WX202610120001")
+    @ExcelProperty("璧勪骇缂栧彿")
+    private String assetCode;
+
+    @Schema(description = "璧勪骇鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED, example = "ERP 杞欢钁椾綔鏉�")
+    @ExcelProperty("璧勪骇鍚嶇О")
+    private String assetName;
+
+    @Schema(description = "璧勪骇绫诲埆", example = "杞欢")
+    @ExcelProperty("璧勪骇绫诲埆")
+    private String category;
+
+    @Schema(description = "璇佷功缂栧彿", example = "杞憲鐧诲瓧绗� 1234567 鍙�")
+    @ExcelProperty("璇佷功缂栧彿")
+    private String certificateNo;
+
+    @Schema(description = "鍙栧緱鏃ユ湡", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("鍙栧緱鏃ユ湡")
+    private LocalDate acquisitionDate;
+
+    @Schema(description = "璧勪骇鍘熷��", requiredMode = Schema.RequiredMode.REQUIRED, example = "60000.00")
+    @ExcelProperty("璧勪骇鍘熷��")
+    private BigDecimal originalValue;
+
+    @Schema(description = "鎽婇攢骞撮檺锛堝勾锛�", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
+    @ExcelProperty("鎽婇攢骞撮檺(骞�)")
+    private Integer amortizationPeriod;
+
+    @Schema(description = "娈嬪�肩巼锛�%锛�", example = "0.00")
+    @ExcelProperty("娈嬪�肩巼(%)")
+    private BigDecimal residualRate;
+
+    @Schema(description = "鏈堟憡閿�棰�", example = "1000.00")
+    @ExcelProperty("鏈堟憡閿�棰�")
+    private BigDecimal monthlyAmortization;
+
+    @Schema(description = "绱鎽婇攢", requiredMode = Schema.RequiredMode.REQUIRED, example = "20000.00")
+    @ExcelProperty("绱鎽婇攢")
+    private BigDecimal accumulatedAmortization;
+
+    @Schema(description = "鍑�鍊�", requiredMode = Schema.RequiredMode.REQUIRED, example = "40000.00")
+    @ExcelProperty("鍑�鍊�")
+    private BigDecimal netValue;
+
+    @Schema(description = "鏈夋晥鏈熻嚦")
+    @ExcelProperty("鏈夋晥鏈熻嚦")
+    private LocalDate validityDate;
+
+    @Schema(description = "鐘舵��", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @ExcelProperty(value = "鐘舵��", converter = DictConvert.class)
+    @DictFormat(DictTypeConstants.INTANGIBLE_ASSET_STATUS)
+    private Integer status;
+
+    @Schema(description = "璧勪骇鎻忚堪", example = "鎻忚堪")
+    @ExcelProperty("璧勪骇鎻忚堪")
+    private String description;
+
+    @Schema(description = "澶囨敞", example = "澶囨敞")
+    @ExcelProperty("澶囨敞")
+    private String remark;
+
+    @Schema(description = "鍒涘缓鏃堕棿", requiredMode = Schema.RequiredMode.REQUIRED)
+    // 銆寉yyy-MM-dd HH:mm:ss銆嶆濂� 19 涓瓧绗︼紝鑷�傚簲鍒楀灏卞彧缁� 19锛岃创鐫�杈瑰緢闅剧湅锛岃繖閲屾斁瀹藉埌 22
+    @ColumnWidth(22)
+    @ExcelProperty("鍒涘缓鏃堕棿")
+    private LocalDateTime createTime;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinIntangibleAssetSaveReqVO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinIntangibleAssetSaveReqVO.java
new file mode 100644
index 0000000..fbf40f4
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/asset/vo/FinIntangibleAssetSaveReqVO.java
@@ -0,0 +1,81 @@
+package cn.iocoder.yudao.module.finance.controller.admin.asset.vo;
+
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import cn.iocoder.yudao.module.finance.enums.FinIntangibleAssetStatusEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.DecimalMax;
+import jakarta.validation.constraints.DecimalMin;
+import jakarta.validation.constraints.Min;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
+
+@Schema(description = "绠$悊鍚庡彴 - 鏃犲舰璧勪骇鏂板/淇敼 Request VO")
+@Data
+public class FinIntangibleAssetSaveReqVO {
+
+    @Schema(description = "璧勪骇缂栧彿", example = "1024")
+    private Long id;
+
+    @Schema(description = "璧勪骇缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "WX202610120001")
+    @NotEmpty(message = "璧勪骇缂栧彿涓嶈兘涓虹┖")
+    @Size(max = 64, message = "璧勪骇缂栧彿闀垮害涓嶈兘瓒呰繃 64 涓瓧绗�")
+    private String assetCode;
+
+    @Schema(description = "璧勪骇鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED, example = "ERP 杞欢钁椾綔鏉�")
+    @NotEmpty(message = "璧勪骇鍚嶇О涓嶈兘涓虹┖")
+    @Size(max = 128, message = "璧勪骇鍚嶇О闀垮害涓嶈兘瓒呰繃 128 涓瓧绗�")
+    private String assetName;
+
+    @Schema(description = "璧勪骇绫诲埆", example = "杞欢")
+    @Size(max = 64, message = "璧勪骇绫诲埆闀垮害涓嶈兘瓒呰繃 64 涓瓧绗�")
+    private String category;
+
+    @Schema(description = "璇佷功缂栧彿", example = "杞憲鐧诲瓧绗� 1234567 鍙�")
+    @Size(max = 128, message = "璇佷功缂栧彿闀垮害涓嶈兘瓒呰繃 128 涓瓧绗�")
+    private String certificateNo;
+
+    @Schema(description = "鍙栧緱鏃ユ湡", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull(message = "鍙栧緱鏃ユ湡涓嶈兘涓虹┖")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+    private LocalDate acquisitionDate;
+
+    @Schema(description = "璧勪骇鍘熷��", requiredMode = Schema.RequiredMode.REQUIRED, example = "60000.00")
+    @NotNull(message = "璧勪骇鍘熷�间笉鑳戒负绌�")
+    @DecimalMin(value = "0", message = "璧勪骇鍘熷�间笉鑳藉皬浜� 0")
+    private BigDecimal originalValue;
+
+    @Schema(description = "鎽婇攢骞撮檺锛堝勾锛�", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
+    @NotNull(message = "鎽婇攢骞撮檺涓嶈兘涓虹┖")
+    @Min(value = 1, message = "鎽婇攢骞撮檺蹇呴』澶т簬绛変簬 1 骞�")
+    private Integer amortizationPeriod;
+
+    @Schema(description = "娈嬪�肩巼锛�%锛�", example = "0.00")
+    @DecimalMin(value = "0", message = "娈嬪�肩巼涓嶈兘灏忎簬 0")
+    @DecimalMax(value = "100", message = "娈嬪�肩巼涓嶈兘澶т簬 100")
+    private BigDecimal residualRate;
+
+    @Schema(description = "鏈夋晥鏈熻嚦")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+    private LocalDate validityDate;
+
+    @Schema(description = "鐘舵��", example = "1")
+    @InEnum(value = FinIntangibleAssetStatusEnum.class, message = "鐘舵�佸繀椤绘槸 {value}")
+    private Integer status;
+
+    @Schema(description = "璧勪骇鎻忚堪", example = "鎻忚堪")
+    @Size(max = 500, message = "璧勪骇鎻忚堪闀垮害涓嶈兘瓒呰繃 500 涓瓧绗�")
+    private String description;
+
+    @Schema(description = "澶囨敞", example = "澶囨敞")
+    @Size(max = 500, message = "澶囨敞闀垮害涓嶈兘瓒呰繃 500 涓瓧绗�")
+    private String remark;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/ledger/FinLedgerController.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/ledger/FinLedgerController.java
new file mode 100644
index 0000000..3f7e061
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/ledger/FinLedgerController.java
@@ -0,0 +1,69 @@
+package cn.iocoder.yudao.module.finance.controller.admin.ledger;
+
+import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.module.finance.controller.admin.ledger.vo.FinLedgerReqVO;
+import cn.iocoder.yudao.module.finance.controller.admin.ledger.vo.FinLedgerRespVO;
+import cn.iocoder.yudao.module.finance.service.ledger.FinLedgerService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.IOException;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "绠$悊鍚庡彴 - 鎬昏处 / 鏄庣粏璐�")
+@RestController
+@RequestMapping("/finance/ledger")
+@Validated
+public class FinLedgerController {
+
+    @Resource
+    private FinLedgerService ledgerService;
+
+    @GetMapping("/general")
+    @Operation(summary = "鑾峰緱鎬昏处", description = "鎸夋湡闂撮�愭湀姹囨�伙紝鍙粺璁″凡杩囪处鍑瘉")
+    @PreAuthorize("@ss.hasPermission('finance:ledger:query')")
+    public CommonResult<List<FinLedgerRespVO>> getGeneralLedgerList(@Valid FinLedgerReqVO reqVO) {
+        return success(ledgerService.getGeneralLedgerList(reqVO));
+    }
+
+    @GetMapping("/detail")
+    @Operation(summary = "鑾峰緱鏄庣粏璐�", description = "閫愭潯鍒嗗綍灞曠ず骞剁粨杞綑棰濓紝鍙粺璁″凡杩囪处鍑瘉")
+    @PreAuthorize("@ss.hasPermission('finance:ledger:query')")
+    public CommonResult<List<FinLedgerRespVO>> getDetailLedgerList(@Valid FinLedgerReqVO reqVO) {
+        return success(ledgerService.getDetailLedgerList(reqVO));
+    }
+
+    @GetMapping("/general/export-excel")
+    @Operation(summary = "瀵煎嚭鎬昏处 Excel")
+    @PreAuthorize("@ss.hasPermission('finance:ledger:export')")
+    @ApiAccessLog(operateType = EXPORT)
+    public void exportGeneralLedgerExcel(@Valid FinLedgerReqVO reqVO,
+                                         HttpServletResponse response) throws IOException {
+        List<FinLedgerRespVO> list = ledgerService.getGeneralLedgerList(reqVO);
+        ExcelUtils.write(response, "鎬昏处.xls", "鏁版嵁", FinLedgerRespVO.class, list);
+    }
+
+    @GetMapping("/detail/export-excel")
+    @Operation(summary = "瀵煎嚭鏄庣粏璐� Excel")
+    @PreAuthorize("@ss.hasPermission('finance:ledger:export')")
+    @ApiAccessLog(operateType = EXPORT)
+    public void exportDetailLedgerExcel(@Valid FinLedgerReqVO reqVO,
+                                        HttpServletResponse response) throws IOException {
+        List<FinLedgerRespVO> list = ledgerService.getDetailLedgerList(reqVO);
+        ExcelUtils.write(response, "鏄庣粏璐�.xls", "鏁版嵁", FinLedgerRespVO.class, list);
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/ledger/vo/FinLedgerReqVO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/ledger/vo/FinLedgerReqVO.java
new file mode 100644
index 0000000..d72489b
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/ledger/vo/FinLedgerReqVO.java
@@ -0,0 +1,36 @@
+package cn.iocoder.yudao.module.finance.controller.admin.ledger.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.YearMonth;
+
+@Schema(description = "绠$悊鍚庡彴 - 鎬昏处/鏄庣粏璐︽煡璇� Request VO")
+@Data
+public class FinLedgerReqVO {
+
+    @Schema(description = "绉戠洰缂栫爜", requiredMode = Schema.RequiredMode.REQUIRED, example = "1001")
+    @NotEmpty(message = "鏌ヨ鎬昏处/鏄庣粏璐﹀繀椤绘寚瀹氫細璁$鐩�")
+    private String subjectCode;
+
+    // 娉ㄦ剰锛氳繖閲屽繀椤绘槸 YearMonth锛屼笉鑳芥槸 LocalDate銆�
+    // 鍓嶇鏈堜唤閫夋嫨鍣ㄤ紶鐨勬槸 "2026-01"锛孡ocalDate 鏃犳硶浠庛�屽勾+鏈堛�嶈繕鍘熷嚭銆屾棩銆嶏紝
+    // 浼氬湪鍙傛暟缁戝畾闃舵灏辨姏 "Failed to convert ... to java.time.LocalDate"锛�400锛夛紝
+    // 鍗充娇鍐欎簡 @DateTimeFormat(pattern = "yyyy-MM") 涔熸晳涓嶄簡 鈥斺�� 鏄瓧娈电被鍨嬮敊浜嗭紝涓嶆槸鏍煎紡閿欎簡銆�
+    @Schema(description = "寮�濮嬫湀浠斤紝鏍煎紡 yyyy-MM锛屽彇璇ユ湀 1 鍙蜂负璐︽湡璧风偣", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-01")
+    @NotNull(message = "寮�濮嬫湀浠戒笉鑳戒负绌�")
+    @DateTimeFormat(pattern = "yyyy-MM")
+    private YearMonth startMonth;
+
+    @Schema(description = "缁撴潫鏈堜唤锛屾牸寮� yyyy-MM锛屽彇璇ユ湀鏈�鍚庝竴澶╀负璐︽湡缁堢偣", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-12")
+    @NotNull(message = "缁撴潫鏈堜唤涓嶈兘涓虹┖")
+    @DateTimeFormat(pattern = "yyyy-MM")
+    private YearMonth endMonth;
+
+    @Schema(description = "鏄惁鍖呭惈涓嬬骇绉戠洰", example = "true")
+    private Boolean includeChildren;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/ledger/vo/FinLedgerRespVO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/ledger/vo/FinLedgerRespVO.java
new file mode 100644
index 0000000..5e22712
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/ledger/vo/FinLedgerRespVO.java
@@ -0,0 +1,73 @@
+package cn.iocoder.yudao.module.finance.controller.admin.ledger.vo;
+
+import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
+import cn.iocoder.yudao.module.finance.enums.DictTypeConstants;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+@Schema(description = "绠$悊鍚庡彴 - 鎬昏处/鏄庣粏璐﹁ Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class FinLedgerRespVO {
+
+    @Schema(description = "琛岀被鍨嬶細opening 鏈熷垵浣欓銆乪ntry 鏄庣粏銆乵onthly_total 鏈湀鍚堣銆亂early_total 鏈勾绱",
+            requiredMode = Schema.RequiredMode.REQUIRED, example = "entry")
+    private String rowType;
+
+    /**
+     * 琛岀被鍨嬬殑涓枃鍚嶏紝涓撲緵 Excel 瀵煎嚭銆�
+     * rowType 瀛樼殑鏄灇涓剧爜锛坥pening/entry/monthly_total/yearly_total锛夛紝EasyExcel 浼氬師鏍峰啓杩涘崟鍏冩牸锛�
+     * 鎵�浠ュ鍑虹殑銆岃绫诲瀷銆嶅垪蹇呴』鐢卞彟涓�涓瓧娈垫壙杞戒腑鏂囷紱rowType 鏈韩浠嶄繚鐣欏湪 JSON 閲岋紝鍓嶇闈犲畠鍋氭爣绛炬槧灏勩��
+     */
+    @Schema(description = "琛岀被鍨嬪悕绉�", example = "鏄庣粏")
+    @ExcelProperty("琛岀被鍨�")
+    private String rowTypeName;
+
+    @Schema(description = "鎵�灞炴湡闂达紝鏍煎紡 yyyy-MM", example = "2026-10")
+    @ExcelProperty("鏈熼棿")
+    private String period;
+
+    @Schema(description = "鏃ユ湡", example = "2026-10-12")
+    @ExcelProperty("鏃ユ湡")
+    private LocalDate date;
+
+    @Schema(description = "鍑瘉瀛楀彿", example = "璁�2026100001")
+    @ExcelProperty("鍑瘉瀛楀彿")
+    private String voucherNo;
+
+    @Schema(description = "绉戠洰缂栫爜", example = "1001")
+    @ExcelProperty("绉戠洰缂栫爜")
+    private String subjectCode;
+
+    @Schema(description = "绉戠洰鍚嶇О", example = "搴撳瓨鐜伴噾")
+    @ExcelProperty("绉戠洰鍚嶇О")
+    private String subjectName;
+
+    @Schema(description = "鎽樿", example = "鏀跺埌璐ф")
+    @ExcelProperty("鎽樿")
+    private String summary;
+
+    @Schema(description = "鍊熸柟閲戦", example = "1000.00")
+    @ExcelProperty("鍊熸柟閲戦")
+    private BigDecimal debit;
+
+    @Schema(description = "璐锋柟閲戦", example = "0.00")
+    @ExcelProperty("璐锋柟閲戦")
+    private BigDecimal credit;
+
+    @Schema(description = "浣欓鏂瑰悜", example = "1")
+    @ExcelProperty(value = "浣欓鏂瑰悜", converter = DictConvert.class)
+    @DictFormat(DictTypeConstants.BALANCE_DIRECTION)
+    private Integer direction;
+
+    @Schema(description = "浣欓锛屽�熸柟涓烘銆佽捶鏂逛负璐�", example = "1000.00")
+    @ExcelProperty("浣欓")
+    private BigDecimal balance;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/statistics/FinStatisticsController.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/statistics/FinStatisticsController.java
new file mode 100644
index 0000000..1fe9310
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/statistics/FinStatisticsController.java
@@ -0,0 +1,46 @@
+package cn.iocoder.yudao.module.finance.controller.admin.statistics;
+
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.module.finance.controller.admin.statistics.vo.FinStatisticsOverviewRespVO;
+import cn.iocoder.yudao.module.finance.controller.admin.statistics.vo.FinStatisticsReqVO;
+import cn.iocoder.yudao.module.finance.controller.admin.statistics.vo.FinStatisticsTrendRespVO;
+import cn.iocoder.yudao.module.finance.service.statistics.FinStatisticsService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+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;
+
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "绠$悊鍚庡彴 - 璐㈠姟鎶ヨ〃 / 浼氳鏍哥畻鐪嬫澘")
+@RestController
+@RequestMapping("/finance/statistics")
+@Validated
+public class FinStatisticsController {
+
+    @Resource
+    private FinStatisticsService statisticsService;
+
+    @GetMapping("/overview")
+    @Operation(summary = "鑾峰緱浼氳鏍哥畻鐪嬫澘鎬昏",
+            description = "鏀舵敮銆佽喘閿�閲戦鍙栬嚜 ERP 宸插鏍哥殑鏀舵鍗�/浠樻鍗�/閿�鍞鍗�/閲囪喘璁㈠崟锛岃祫浜т笌鍑瘉鍙栬嚜鏈ā鍧�")
+    @PreAuthorize("@ss.hasPermission('finance:statistics:query')")
+    public CommonResult<FinStatisticsOverviewRespVO> getStatisticsOverview(@Valid FinStatisticsReqVO reqVO) {
+        return success(statisticsService.getStatisticsOverview(reqVO));
+    }
+
+    @GetMapping("/trend")
+    @Operation(summary = "鑾峰緱鏈堝害鏀舵敮瓒嬪娍", description = "鏈熼棿鍐呮病鏈夋暟鎹殑鏈堜唤琛� 0锛屼究浜庡墠绔洿鎺ョ粯鍥�")
+    @PreAuthorize("@ss.hasPermission('finance:statistics:query')")
+    public CommonResult<List<FinStatisticsTrendRespVO>> getStatisticsTrend(@Valid FinStatisticsReqVO reqVO) {
+        return success(statisticsService.getStatisticsTrend(reqVO));
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/statistics/vo/FinStatisticsOverviewRespVO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/statistics/vo/FinStatisticsOverviewRespVO.java
new file mode 100644
index 0000000..562cb96
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/statistics/vo/FinStatisticsOverviewRespVO.java
@@ -0,0 +1,78 @@
+package cn.iocoder.yudao.module.finance.controller.admin.statistics.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+@Schema(description = "绠$悊鍚庡彴 - 浼氳鏍哥畻鐪嬫澘鎬昏 Response VO")
+@Data
+public class FinStatisticsOverviewRespVO {
+
+    @Schema(description = "寮�濮嬫棩鏈�", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDate startDate;
+
+    @Schema(description = "缁撴潫鏃ユ湡", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDate endDate;
+
+    // ========== 鏀舵敮锛堟潵婧愶細ERP 鏀舵鍗� / 浠樻鍗曪紝鍙粺璁″凡瀹℃牳鍗曟嵁锛� ==========
+
+    @Schema(description = "鎬昏惀鏀讹紙鏀舵鍗曟敹娆鹃噾棰濆悎璁★級", requiredMode = Schema.RequiredMode.REQUIRED, example = "100000.00")
+    private BigDecimal receiptPrice;
+
+    @Schema(description = "鎬绘敮鍑猴紙浠樻鍗曚粯娆鹃噾棰濆悎璁★級", requiredMode = Schema.RequiredMode.REQUIRED, example = "60000.00")
+    private BigDecimal paymentPrice;
+
+    @Schema(description = "鍑�鍒╂鼎锛堟�昏惀鏀� - 鎬绘敮鍑猴級", requiredMode = Schema.RequiredMode.REQUIRED, example = "40000.00")
+    private BigDecimal netProfit;
+
+    // ========== 璐攢锛堟潵婧愶細ERP 閿�鍞鍗� / 閲囪喘璁㈠崟锛屽彧缁熻宸插鏍稿崟鎹級 ==========
+
+    @Schema(description = "閿�鍞鍗曢噾棰濆悎璁★紙搴旀敹鍙e緞锛�", requiredMode = Schema.RequiredMode.REQUIRED, example = "150000.00")
+    private BigDecimal saleOrderPrice;
+
+    @Schema(description = "閲囪喘璁㈠崟閲戦鍚堣锛堝簲浠樺彛寰勶級", requiredMode = Schema.RequiredMode.REQUIRED, example = "80000.00")
+    private BigDecimal purchaseOrderPrice;
+
+    // ========== 璧勪骇锛堟椂鐐规暟锛屽彇褰撳墠鍏ㄩ儴璧勪骇锛� ==========
+
+    @Schema(description = "鍥哄畾璧勪骇鍘熷�煎悎璁�", requiredMode = Schema.RequiredMode.REQUIRED, example = "1200000.00")
+    private BigDecimal fixedAssetOriginalValue;
+
+    @Schema(description = "鍥哄畾璧勪骇绱鎶樻棫鍚堣", requiredMode = Schema.RequiredMode.REQUIRED, example = "240000.00")
+    private BigDecimal fixedAssetAccumulatedDepreciation;
+
+    @Schema(description = "鍥哄畾璧勪骇鍑�鍊煎悎璁�", requiredMode = Schema.RequiredMode.REQUIRED, example = "960000.00")
+    private BigDecimal fixedAssetNetValue;
+
+    @Schema(description = "鏃犲舰璧勪骇鍘熷�煎悎璁�", requiredMode = Schema.RequiredMode.REQUIRED, example = "300000.00")
+    private BigDecimal intangibleAssetOriginalValue;
+
+    @Schema(description = "鏃犲舰璧勪骇绱鎽婇攢鍚堣", requiredMode = Schema.RequiredMode.REQUIRED, example = "60000.00")
+    private BigDecimal intangibleAssetAccumulatedAmortization;
+
+    @Schema(description = "鏃犲舰璧勪骇鍑�鍊煎悎璁�", requiredMode = Schema.RequiredMode.REQUIRED, example = "240000.00")
+    private BigDecimal intangibleAssetNetValue;
+
+    @Schema(description = "璧勪骇鍑�鍊煎悎璁★紙鍥哄畾璧勪骇 + 鏃犲舰璧勪骇锛�", requiredMode = Schema.RequiredMode.REQUIRED, example = "1200000.00")
+    private BigDecimal totalAssetNetValue;
+
+    // ========== 浼氳鏍哥畻锛堟椂鐐规暟锛� ==========
+
+    @Schema(description = "浼氳绉戠洰鏁伴噺", requiredMode = Schema.RequiredMode.REQUIRED, example = "120")
+    private Long subjectCount;
+
+    @Schema(description = "鍑瘉鎬绘暟", requiredMode = Schema.RequiredMode.REQUIRED, example = "300")
+    private Long voucherCount;
+
+    @Schema(description = "鏈繃璐﹀嚟璇佹暟閲�", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+    private Long voucherUnpostedCount;
+
+    @Schema(description = "宸茶繃璐﹀嚟璇佹暟閲�", requiredMode = Schema.RequiredMode.REQUIRED, example = "280")
+    private Long voucherPostedCount;
+
+    @Schema(description = "宸蹭綔搴熷嚟璇佹暟閲�", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
+    private Long voucherCancelledCount;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/statistics/vo/FinStatisticsReqVO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/statistics/vo/FinStatisticsReqVO.java
new file mode 100644
index 0000000..f9cbd18
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/statistics/vo/FinStatisticsReqVO.java
@@ -0,0 +1,22 @@
+package cn.iocoder.yudao.module.finance.controller.admin.statistics.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.YearMonth;
+
+@Schema(description = "绠$悊鍚庡彴 - 璐㈠姟鎶ヨ〃 / 浼氳鏍哥畻鐪嬫澘鏌ヨ Request VO")
+@Data
+public class FinStatisticsReqVO {
+
+    // 鍚� FinLedgerReqVO锛氭湀浠藉弬鏁板繀椤绘槸 YearMonth锛孡ocalDate 鏃犳硶鎺ユ敹 "2026-01"銆�
+    @Schema(description = "寮�濮嬫湀浠斤紝鏍煎紡 yyyy-MM锛屼负绌烘椂鍙栨湰骞� 1 鏈�", example = "2026-01")
+    @DateTimeFormat(pattern = "yyyy-MM")
+    private YearMonth startMonth;
+
+    @Schema(description = "缁撴潫鏈堜唤锛屾牸寮� yyyy-MM锛屼负绌烘椂鍙栧綋鍓嶆湀", example = "2026-12")
+    @DateTimeFormat(pattern = "yyyy-MM")
+    private YearMonth endMonth;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/statistics/vo/FinStatisticsTrendRespVO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/statistics/vo/FinStatisticsTrendRespVO.java
new file mode 100644
index 0000000..5dd3c9e
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/statistics/vo/FinStatisticsTrendRespVO.java
@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.finance.controller.admin.statistics.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Schema(description = "绠$悊鍚庡彴 - 璐㈠姟浼氳鏍哥畻鐪嬫澘鏈堝害瓒嬪娍 Response VO")
+@Data
+public class FinStatisticsTrendRespVO {
+
+    @Schema(description = "鏈堜唤锛屾牸寮� yyyy-MM", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-10")
+    private String month;
+
+    @Schema(description = "鏀舵閲戦", requiredMode = Schema.RequiredMode.REQUIRED, example = "100000.00")
+    private BigDecimal receiptPrice;
+
+    @Schema(description = "浠樻閲戦", requiredMode = Schema.RequiredMode.REQUIRED, example = "60000.00")
+    private BigDecimal paymentPrice;
+
+    @Schema(description = "鍑�鍒╂鼎锛堟敹娆� - 浠樻锛�", requiredMode = Schema.RequiredMode.REQUIRED, example = "40000.00")
+    private BigDecimal netProfit;
+
+    @Schema(description = "閿�鍞鍗曢噾棰�", requiredMode = Schema.RequiredMode.REQUIRED, example = "150000.00")
+    private BigDecimal saleOrderPrice;
+
+    @Schema(description = "閲囪喘璁㈠崟閲戦", requiredMode = Schema.RequiredMode.REQUIRED, example = "80000.00")
+    private BigDecimal purchaseOrderPrice;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/subject/FinAccountSubjectController.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/subject/FinAccountSubjectController.java
new file mode 100644
index 0000000..3f4cfca
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/subject/FinAccountSubjectController.java
@@ -0,0 +1,148 @@
+package cn.iocoder.yudao.module.finance.controller.admin.subject;
+
+import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.module.finance.controller.admin.subject.vo.FinAccountSubjectPageReqVO;
+import cn.iocoder.yudao.module.finance.controller.admin.subject.vo.FinAccountSubjectRespVO;
+import cn.iocoder.yudao.module.finance.controller.admin.subject.vo.FinAccountSubjectSaveReqVO;
+import cn.iocoder.yudao.module.finance.dal.dataobject.subject.FinAccountSubjectDO;
+import cn.iocoder.yudao.module.finance.service.subject.FinAccountSubjectService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
+
+@Tag(name = "绠$悊鍚庡彴 - 璐㈠姟浼氳绉戠洰")
+@RestController
+@RequestMapping("/finance/account-subject")
+@Validated
+public class FinAccountSubjectController {
+
+    @Resource
+    private FinAccountSubjectService accountSubjectService;
+
+    @PostMapping("/create")
+    @Operation(summary = "鍒涘缓浼氳绉戠洰")
+    @PreAuthorize("@ss.hasPermission('finance:account-subject:create')")
+    public CommonResult<Long> createAccountSubject(@Valid @RequestBody FinAccountSubjectSaveReqVO createReqVO) {
+        return success(accountSubjectService.createAccountSubject(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "鏇存柊浼氳绉戠洰")
+    @PreAuthorize("@ss.hasPermission('finance:account-subject:update')")
+    public CommonResult<Boolean> updateAccountSubject(@Valid @RequestBody FinAccountSubjectSaveReqVO updateReqVO) {
+        accountSubjectService.updateAccountSubject(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "鍒犻櫎浼氳绉戠洰", description = "瀛樺湪涓嬬骇绉戠洰鎴栬鍑瘉鍒嗗綍寮曠敤鏃朵笉鍏佽鍒犻櫎")
+    @Parameter(name = "ids", description = "绉戠洰缂栧彿鏁扮粍", required = true, example = "1,2,3")
+    @PreAuthorize("@ss.hasPermission('finance:account-subject:delete')")
+    public CommonResult<Boolean> deleteAccountSubject(@RequestParam("ids") List<Long> ids) {
+        accountSubjectService.deleteAccountSubjectList(ids);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "鑾峰緱浼氳绉戠洰")
+    @Parameter(name = "id", description = "绉戠洰缂栧彿", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('finance:account-subject:query')")
+    public CommonResult<FinAccountSubjectRespVO> getAccountSubject(@RequestParam("id") Long id) {
+        FinAccountSubjectDO subject = accountSubjectService.getAccountSubject(id);
+        return success(BeanUtils.toBean(subject, FinAccountSubjectRespVO.class));
+    }
+
+    /**
+     * 浼氳绉戠洰鏍戝舰鍒楄〃椤电殑鍙栨暟鏂瑰紡锛氫竴娆℃媺鍏ㄩ噺鍛戒腑鐨勭鐩紝鍐嶅湪鍓嶇鍐呭瓨閲岀粍鏍戯紝鍒嗛〉瀵规爲娌℃湁鎰忎箟锛�
+     * 鎵�浠ュ墠绔紶 pageSize = -1锛堝嵆妗嗘灦鐨� PageParam.PAGE_SIZE_NONE锛岃涔夋槸銆屾煡璇㈠叏閮ㄣ�嶏級銆�
+     *
+     * 浣� PageParam 涓婃寕鐫� @Min(1) @Max(200)锛岃姹傞噷鐨� -1 浼氳鍙傛暟鏍¢獙鐩存帴鎷︽垚
+     * 400銆屾瘡椤垫潯鏁版渶灏忓�间负 1銆嶃�侾AGE_SIZE_NONE 鍦ㄦ鏋堕噷涓�鐩存槸鏈嶅姟绔鍑烘柟娉曞唴閮� set 鐨�
+     * 锛堣涓嬫柟 export-excel锛夛紝浠庢病璧� HTTP 鍙傛暟鏍¢獙锛屾墍浠ヨ繖閲屽繀椤诲幓鎺� @Valid锛�
+     * 鏀逛负鍦ㄦ柟娉曞唴鎶� pageSize < 1 鏄惧紡褰掍竴涓� PAGE_SIZE_NONE锛岃銆屾煡璇㈠叏閮ㄣ�嶈繖鏉¤矾璧板緱閫氥��
+     */
+    @GetMapping("/page")
+    @Operation(summary = "鑾峰緱浼氳绉戠洰鍒嗛〉", description = "pageSize 浼� -1 琛ㄧず鏌ヨ鍏ㄩ儴锛堟爲褰㈠垪琛ㄩ〉缁勬爲鐢級")
+    @PreAuthorize("@ss.hasPermission('finance:account-subject:query')")
+    public CommonResult<PageResult<FinAccountSubjectRespVO>> getAccountSubjectPage(FinAccountSubjectPageReqVO pageReqVO) {
+        if (pageReqVO.getPageSize() == null || pageReqVO.getPageSize() < 1) {
+            pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        }
+        PageResult<FinAccountSubjectDO> pageResult = accountSubjectService.getAccountSubjectPage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, FinAccountSubjectRespVO.class));
+    }
+
+    @GetMapping("/list")
+    @Operation(summary = "鑾峰緱浼氳绉戠洰鏍戝垪琛�", description = "杩斿洖鍏ㄩ噺绉戠洰锛屾寜鐖跺瓙灞傜骇缁勮 children")
+    @PreAuthorize("@ss.hasPermission('finance:account-subject:query')")
+    public CommonResult<List<FinAccountSubjectRespVO>> getAccountSubjectList() {
+        return success(buildTree(accountSubjectService.getAccountSubjectList()));
+    }
+
+    @GetMapping("/simple-list")
+    @Operation(summary = "鑾峰緱浼氳绉戠洰绮剧畝鍒楄〃", description = "鐢ㄤ簬鍑瘉鍒嗗綍鐨勭鐩笅鎷�/鏍戦�夋嫨")
+    public CommonResult<List<FinAccountSubjectRespVO>> getAccountSubjectSimpleList() {
+        List<FinAccountSubjectDO> list = accountSubjectService.getAccountSubjectList();
+        return success(convertList(list, subject -> new FinAccountSubjectRespVO()
+                .setId(subject.getId()).setParentId(subject.getParentId())
+                .setSubjectCode(subject.getSubjectCode()).setSubjectName(subject.getSubjectName())
+                .setSubjectType(subject.getSubjectType()).setBalanceDirection(subject.getBalanceDirection())
+                .setStatus(subject.getStatus())));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "瀵煎嚭浼氳绉戠洰 Excel")
+    @PreAuthorize("@ss.hasPermission('finance:account-subject:export')")
+    @ApiAccessLog(operateType = EXPORT)
+    public void exportAccountSubjectExcel(@Valid FinAccountSubjectPageReqVO pageReqVO,
+                                          HttpServletResponse response) throws IOException {
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<FinAccountSubjectDO> list = accountSubjectService.getAccountSubjectPage(pageReqVO).getList();
+        // 瀵煎嚭 Excel
+        ExcelUtils.write(response, "浼氳绉戠洰.xls", "鏁版嵁", FinAccountSubjectRespVO.class,
+                BeanUtils.toBean(list, FinAccountSubjectRespVO.class));
+    }
+
+    /**
+     * 灏嗘墎骞崇鐩垪琛ㄧ粍瑁呬负鏍戯細涓婄骇绉戠洰涓嶅瓨鍦紙宸茶鍒犻櫎锛夋椂锛屾寜鏍硅妭鐐瑰鐞嗭紝閬垮厤鑺傜偣涓㈠け
+     */
+    private List<FinAccountSubjectRespVO> buildTree(List<FinAccountSubjectDO> list) {
+        List<FinAccountSubjectRespVO> voList = BeanUtils.toBean(list, FinAccountSubjectRespVO.class);
+        Map<Long, FinAccountSubjectRespVO> idMap = convertMap(voList, FinAccountSubjectRespVO::getId);
+        List<FinAccountSubjectRespVO> roots = new ArrayList<>();
+        voList.forEach(vo -> {
+            FinAccountSubjectRespVO parent = vo.getParentId() == null ? null : idMap.get(vo.getParentId());
+            if (parent == null) {
+                roots.add(vo);
+            } else {
+                if (parent.getChildren() == null) {
+                    parent.setChildren(new ArrayList<>());
+                }
+                parent.getChildren().add(vo);
+            }
+        });
+        return roots;
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/subject/vo/FinAccountSubjectPageReqVO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/subject/vo/FinAccountSubjectPageReqVO.java
new file mode 100644
index 0000000..9b90b68
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/subject/vo/FinAccountSubjectPageReqVO.java
@@ -0,0 +1,27 @@
+package cn.iocoder.yudao.module.finance.controller.admin.subject.vo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+@Schema(description = "绠$悊鍚庡彴 - 浼氳绉戠洰鍒嗛〉 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class FinAccountSubjectPageReqVO extends PageParam {
+
+    @Schema(description = "绉戠洰缂栫爜", example = "1001")
+    private String subjectCode;
+
+    @Schema(description = "绉戠洰鍚嶇О", example = "搴撳瓨鐜伴噾")
+    private String subjectName;
+
+    @Schema(description = "绉戠洰绫诲瀷", example = "1")
+    private Integer subjectType;
+
+    @Schema(description = "鐘舵��", example = "0")
+    private Integer status;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/subject/vo/FinAccountSubjectRespVO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/subject/vo/FinAccountSubjectRespVO.java
new file mode 100644
index 0000000..be96f66
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/subject/vo/FinAccountSubjectRespVO.java
@@ -0,0 +1,63 @@
+package cn.iocoder.yudao.module.finance.controller.admin.subject.vo;
+
+import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
+import cn.iocoder.yudao.module.finance.enums.DictTypeConstants;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import cn.idev.excel.annotation.write.style.ColumnWidth;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Schema(description = "绠$悊鍚庡彴 - 浼氳绉戠洰 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class FinAccountSubjectRespVO {
+
+    @Schema(description = "绉戠洰缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    @ExcelProperty("绉戠洰缂栧彿")
+    private Long id;
+
+    @Schema(description = "鐖剁鐩紪鍙�", example = "1024")
+    private Long parentId;
+
+    @Schema(description = "绉戠洰缂栫爜", requiredMode = Schema.RequiredMode.REQUIRED, example = "1001")
+    @ExcelProperty("绉戠洰缂栫爜")
+    private String subjectCode;
+
+    @Schema(description = "绉戠洰鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED, example = "搴撳瓨鐜伴噾")
+    @ExcelProperty("绉戠洰鍚嶇О")
+    private String subjectName;
+
+    @Schema(description = "绉戠洰绫诲瀷", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @ExcelProperty(value = "绉戠洰绫诲瀷", converter = DictConvert.class)
+    @DictFormat(DictTypeConstants.SUBJECT_TYPE)
+    private Integer subjectType;
+
+    @Schema(description = "浣欓鏂瑰悜", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @ExcelProperty(value = "浣欓鏂瑰悜", converter = DictConvert.class)
+    @DictFormat(DictTypeConstants.BALANCE_DIRECTION)
+    private Integer balanceDirection;
+
+    @Schema(description = "鐘舵��", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
+    @ExcelProperty(value = "鐘舵��", converter = DictConvert.class)
+    @DictFormat(cn.iocoder.yudao.module.system.enums.DictTypeConstants.COMMON_STATUS)
+    private Integer status;
+
+    @Schema(description = "澶囨敞", example = "澶囨敞")
+    @ExcelProperty("澶囨敞")
+    private String remark;
+
+    @Schema(description = "鍒涘缓鏃堕棿", requiredMode = Schema.RequiredMode.REQUIRED)
+    // 銆寉yyy-MM-dd HH:mm:ss銆嶆濂� 19 涓瓧绗︼紝鑷�傚簲鍒楀灏卞彧缁� 19锛岃创鐫�杈瑰緢闅剧湅锛岃繖閲屾斁瀹藉埌 22
+    @ColumnWidth(22)
+    @ExcelProperty("鍒涘缓鏃堕棿")
+    private LocalDateTime createTime;
+
+    @Schema(description = "瀛愮鐩�", example = "[]")
+    private List<FinAccountSubjectRespVO> children;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/subject/vo/FinAccountSubjectSaveReqVO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/subject/vo/FinAccountSubjectSaveReqVO.java
new file mode 100644
index 0000000..130d31e
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/subject/vo/FinAccountSubjectSaveReqVO.java
@@ -0,0 +1,52 @@
+package cn.iocoder.yudao.module.finance.controller.admin.subject.vo;
+
+import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import cn.iocoder.yudao.module.finance.enums.FinBalanceDirectionEnum;
+import cn.iocoder.yudao.module.finance.enums.FinSubjectTypeEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+
+@Schema(description = "绠$悊鍚庡彴 - 浼氳绉戠洰鏂板/淇敼 Request VO")
+@Data
+public class FinAccountSubjectSaveReqVO {
+
+    @Schema(description = "绉戠洰缂栧彿", example = "1024")
+    private Long id;
+
+    @Schema(description = "鐖剁鐩紪鍙�", example = "1024")
+    private Long parentId;
+
+    @Schema(description = "绉戠洰缂栫爜", requiredMode = Schema.RequiredMode.REQUIRED, example = "1001")
+    @NotEmpty(message = "绉戠洰缂栫爜涓嶈兘涓虹┖")
+    @Size(max = 64, message = "绉戠洰缂栫爜闀垮害涓嶈兘瓒呰繃 64 涓瓧绗�")
+    private String subjectCode;
+
+    @Schema(description = "绉戠洰鍚嶇О", requiredMode = Schema.RequiredMode.REQUIRED, example = "搴撳瓨鐜伴噾")
+    @NotEmpty(message = "绉戠洰鍚嶇О涓嶈兘涓虹┖")
+    @Size(max = 128, message = "绉戠洰鍚嶇О闀垮害涓嶈兘瓒呰繃 128 涓瓧绗�")
+    private String subjectName;
+
+    @Schema(description = "绉戠洰绫诲瀷", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "绉戠洰绫诲瀷涓嶈兘涓虹┖")
+    @InEnum(value = FinSubjectTypeEnum.class, message = "绉戠洰绫诲瀷蹇呴』鏄� {value}")
+    private Integer subjectType;
+
+    @Schema(description = "浣欓鏂瑰悜", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "浣欓鏂瑰悜涓嶈兘涓虹┖")
+    @InEnum(value = FinBalanceDirectionEnum.class, message = "浣欓鏂瑰悜蹇呴』鏄� {value}")
+    private Integer balanceDirection;
+
+    @Schema(description = "鐘舵��", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
+    @NotNull(message = "鐘舵�佷笉鑳戒负绌�")
+    @InEnum(value = CommonStatusEnum.class, message = "鐘舵�佸繀椤绘槸 {value}")
+    private Integer status;
+
+    @Schema(description = "澶囨敞", example = "澶囨敞")
+    @Size(max = 500, message = "澶囨敞闀垮害涓嶈兘瓒呰繃 500 涓瓧绗�")
+    private String remark;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/voucher/FinVoucherController.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/voucher/FinVoucherController.java
new file mode 100644
index 0000000..d9602f5
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/voucher/FinVoucherController.java
@@ -0,0 +1,204 @@
+package cn.iocoder.yudao.module.finance.controller.admin.voucher;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
+import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
+import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
+import cn.iocoder.yudao.module.finance.controller.admin.voucher.vo.FinVoucherEntryRespVO;
+import cn.iocoder.yudao.module.finance.controller.admin.voucher.vo.FinVoucherPageReqVO;
+import cn.iocoder.yudao.module.finance.controller.admin.voucher.vo.FinVoucherRespVO;
+import cn.iocoder.yudao.module.finance.controller.admin.voucher.vo.FinVoucherSaveReqVO;
+import cn.iocoder.yudao.module.finance.dal.dataobject.voucher.FinVoucherDO;
+import cn.iocoder.yudao.module.finance.dal.dataobject.voucher.FinVoucherEntryDO;
+import cn.iocoder.yudao.module.finance.service.voucher.FinVoucherService;
+import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
+import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletResponse;
+import jakarta.validation.Valid;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Stream;
+
+import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
+import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
+
+@Tag(name = "绠$悊鍚庡彴 - 璁拌处鍑瘉")
+@RestController
+@RequestMapping("/finance/voucher")
+@Validated
+public class FinVoucherController {
+
+    /**
+     * 闄勪欢鐨勪笟鍔¤褰曠被鍨�
+     */
+    private static final String ATTACHMENT_RECORD_TYPE = "fin_voucher";
+
+    @Resource
+    private FinVoucherService voucherService;
+    @Resource
+    private StorageAttachmentApi storageAttachmentApi;
+    @Resource
+    private AdminUserApi adminUserApi;
+
+    @PostMapping("/create")
+    @Operation(summary = "鍒涘缓璁拌处鍑瘉")
+    @PreAuthorize("@ss.hasPermission('finance:voucher:create')")
+    public CommonResult<Long> createVoucher(@Valid @RequestBody FinVoucherSaveReqVO createReqVO) {
+        return success(voucherService.createVoucher(createReqVO));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "鏇存柊璁拌处鍑瘉", description = "浠呮湭杩囪处鐨勫嚟璇佸厑璁镐慨鏀�")
+    @PreAuthorize("@ss.hasPermission('finance:voucher:update')")
+    public CommonResult<Boolean> updateVoucher(@Valid @RequestBody FinVoucherSaveReqVO updateReqVO) {
+        voucherService.updateVoucher(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "鍒犻櫎璁拌处鍑瘉")
+    @Parameter(name = "id", description = "缂栧彿", required = true)
+    @PreAuthorize("@ss.hasPermission('finance:voucher:delete')")
+    public CommonResult<Boolean> deleteVoucher(@RequestParam("id") Long id) {
+        voucherService.deleteVoucher(id);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete-list")
+    @Operation(summary = "鎵归噺鍒犻櫎璁拌处鍑瘉")
+    @Parameter(name = "ids", description = "缂栧彿鍒楄〃", required = true)
+    @PreAuthorize("@ss.hasPermission('finance:voucher:delete')")
+    public CommonResult<Boolean> deleteVoucherList(@RequestParam("ids") List<Long> ids) {
+        voucherService.deleteVoucherList(ids);
+        return success(true);
+    }
+
+    @PutMapping("/post")
+    @Operation(summary = "鍑瘉杩囪处", description = "鏈繃璐� -> 宸茶繃璐︼紝杩囪处鍚庡嚟璇佷笉鍙啀淇敼")
+    @Parameter(name = "id", description = "缂栧彿", required = true)
+    @PreAuthorize("@ss.hasPermission('finance:voucher:update-status')")
+    public CommonResult<Boolean> postVoucher(@RequestParam("id") Long id) {
+        voucherService.postVoucher(id);
+        return success(true);
+    }
+
+    @PutMapping("/cancel")
+    @Operation(summary = "鍑瘉浣滃簾", description = "鏈繃璐� -> 宸蹭綔搴�")
+    @Parameter(name = "id", description = "缂栧彿", required = true)
+    @PreAuthorize("@ss.hasPermission('finance:voucher:update-status')")
+    public CommonResult<Boolean> cancelVoucher(@RequestParam("id") Long id) {
+        voucherService.cancelVoucher(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "鑾峰緱璁拌处鍑瘉", description = "鍖呭惈鍑瘉鍒嗗綍涓庨檮浠跺垪琛�")
+    @Parameter(name = "id", description = "缂栧彿", required = true, example = "1024")
+    @PreAuthorize("@ss.hasPermission('finance:voucher:query')")
+    public CommonResult<FinVoucherRespVO> getVoucher(@RequestParam("id") Long id) {
+        FinVoucherDO voucher = voucherService.getVoucher(id);
+        if (voucher == null) {
+            return success(null);
+        }
+        FinVoucherRespVO respVO = BeanUtils.toBean(voucher, FinVoucherRespVO.class);
+        respVO.setEntries(BeanUtils.toBean(voucherService.getVoucherEntryListByVoucherId(id), FinVoucherEntryRespVO.class));
+        // 娉ㄦ剰锛歋torageAttachmentApi.listAttachments 鍦ㄣ�岃璁板綍娌℃湁浠讳綍闄勪欢銆嶆椂杩斿洖 null锛岃�屼笉鏄┖闆嗗悎銆�
+        // 鐩存帴 .isEmpty() 浼� NPE锛氭柊澧炵殑鍑瘉杩樻病浼犻檮浠舵椂锛屼竴鏌ヨ鎯呭氨 500锛屽繀椤诲厛鍏滃簳鎴愮┖鍒楄〃銆�
+        List<StorageBlobRespDTO> attachments = storageAttachmentApi.listAttachments(ATTACHMENT_RECORD_TYPE, id);
+        if (attachments == null) {
+            attachments = List.of();
+        }
+        respVO.setAttachmentList(attachments);
+        respVO.setHasAttachment(!attachments.isEmpty());
+        return success(respVO);
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "鑾峰緱璁拌处鍑瘉鍒嗛〉")
+    @PreAuthorize("@ss.hasPermission('finance:voucher:query')")
+    public CommonResult<PageResult<FinVoucherRespVO>> getVoucherPage(@Valid FinVoucherPageReqVO pageReqVO) {
+        PageResult<FinVoucherDO> pageResult = voucherService.getVoucherPage(pageReqVO);
+        PageResult<FinVoucherRespVO> result = BeanUtils.toBean(pageResult, FinVoucherRespVO.class);
+        fillCreatorName(result.getList());
+        return success(result);
+    }
+
+    @GetMapping("/page-with-entry")
+    @Operation(summary = "鑾峰緱璁拌处鍑瘉鍒嗛〉锛堝惈鍒嗗綍锛�", description = "涓�娆℃�ц繑鍥炴湰椤垫墍鏈夊嚟璇佺殑鍒嗗綍锛岄伩鍏嶅墠绔�愭潯鏌ヨ")
+    @PreAuthorize("@ss.hasPermission('finance:voucher:query')")
+    public CommonResult<PageResult<FinVoucherRespVO>> getVoucherPageWithEntry(@Valid FinVoucherPageReqVO pageReqVO) {
+        PageResult<FinVoucherDO> pageResult = voucherService.getVoucherPage(pageReqVO);
+        PageResult<FinVoucherRespVO> result = BeanUtils.toBean(pageResult, FinVoucherRespVO.class);
+        if (result.getList().isEmpty()) {
+            return success(result);
+        }
+        fillCreatorName(result.getList());
+        // 鎵归噺鏌ヨ鏈〉鍑瘉鐨勫垎褰�
+        Map<Long, List<FinVoucherEntryDO>> entryMap = convertMultiMap(
+                voucherService.getVoucherEntryListByVoucherIds(
+                        convertList(result.getList(), FinVoucherRespVO::getId)),
+                FinVoucherEntryDO::getVoucherId);
+        result.getList().forEach(voucher -> {
+            voucher.setEntries(BeanUtils.toBean(
+                    entryMap.getOrDefault(voucher.getId(), List.of()), FinVoucherEntryRespVO.class));
+            // 鍒楄〃椤典篃闇�瑕佸睍绀洪檮浠舵爣璁帮紱闄勪欢琛ㄦ寜 recordId 鍗曟潯鏌ヨ锛屾湰椤垫渶澶� 20 鏉★紝鎴愭湰鍙帴鍙�
+            // 鍚屼笂锛氭病鏈夐檮浠舵椂 listAttachments 杩斿洖 null锛屽繀椤诲厹搴曪紝鍚﹀垯鍒楄〃閲屽彧瑕佹湁涓�鏉℃病闄勪欢鐨勫嚟璇佸氨鏁撮〉 500
+            List<StorageBlobRespDTO> attachments = storageAttachmentApi.listAttachments(ATTACHMENT_RECORD_TYPE, voucher.getId());
+            if (attachments == null) {
+                attachments = List.of();
+            }
+            voucher.setAttachmentList(attachments);
+            voucher.setHasAttachment(!attachments.isEmpty());
+        });
+        return success(result);
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "瀵煎嚭璁拌处鍑瘉 Excel")
+    @PreAuthorize("@ss.hasPermission('finance:voucher:export')")
+    @ApiAccessLog(operateType = EXPORT)
+    public void exportVoucherExcel(@Valid FinVoucherPageReqVO pageReqVO,
+                                   HttpServletResponse response) throws IOException {
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<FinVoucherDO> list = voucherService.getVoucherPage(pageReqVO).getList();
+        List<FinVoucherRespVO> voList = BeanUtils.toBean(list, FinVoucherRespVO.class);
+        fillCreatorName(voList);
+        // 瀵煎嚭 Excel
+        ExcelUtils.write(response, "璁拌处鍑瘉.xls", "鏁版嵁", FinVoucherRespVO.class, voList);
+    }
+
+    /**
+     * 濉厖鍒跺崟浜烘樀绉般��
+     * BaseDO.creator 瀛樼殑鏄敤鎴� ID锛屽垪琛ㄤ笌瀵煎嚭琛ㄩ噷鐩存帴灞曠ず灏辨槸涓�涓叉暟瀛楋紝
+     * 鎵�浠ユ寜 ERP 鏀舵鍗�/浠樻鍗曠殑鏃㈡湁鍙e緞锛岀粺涓�鍦ㄨ繖閲屾妸鐢ㄦ埛淇℃伅鎵归噺鏌ヤ竴娆″啀鍥炲~銆�
+     */
+    private void fillCreatorName(List<FinVoucherRespVO> list) {
+        if (CollUtil.isEmpty(list)) {
+            return;
+        }
+        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(list,
+                voucher -> Stream.of(NumberUtils.parseLong(voucher.getCreator()))));
+        list.forEach(voucher -> MapUtils.findAndThen(userMap, NumberUtils.parseLong(voucher.getCreator()),
+                user -> voucher.setCreatorName(user.getNickname())));
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/voucher/vo/FinVoucherEntryRespVO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/voucher/vo/FinVoucherEntryRespVO.java
new file mode 100644
index 0000000..c1bbdde
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/voucher/vo/FinVoucherEntryRespVO.java
@@ -0,0 +1,56 @@
+package cn.iocoder.yudao.module.finance.controller.admin.voucher.vo;
+
+import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
+import cn.iocoder.yudao.module.finance.enums.DictTypeConstants;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Schema(description = "绠$悊鍚庡彴 - 鍑瘉鍒嗗綍 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class FinVoucherEntryRespVO {
+
+    @Schema(description = "鍒嗗綍缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    private Long id;
+
+    @Schema(description = "琛屽彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Integer rowNo;
+
+    @Schema(description = "绉戠洰缂栫爜", requiredMode = Schema.RequiredMode.REQUIRED, example = "1001")
+    @ExcelProperty("绉戠洰缂栫爜")
+    private String subjectCode;
+
+    @Schema(description = "绉戠洰鍚嶇О", example = "搴撳瓨鐜伴噾")
+    @ExcelProperty("绉戠洰鍚嶇О")
+    private String subjectName;
+
+    @Schema(description = "鎽樿", example = "鏀跺埌璐ф")
+    @ExcelProperty("鎽樿")
+    private String summary;
+
+    @Schema(description = "鍊熸柟閲戦", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000.00")
+    @ExcelProperty("鍊熸柟閲戦")
+    private BigDecimal debit;
+
+    @Schema(description = "璐锋柟閲戦", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.00")
+    @ExcelProperty("璐锋柟閲戦")
+    private BigDecimal credit;
+
+    @Schema(description = "杈呭姪鏍哥畻绫诲瀷", example = "1")
+    @ExcelProperty(value = "杈呭姪鏍哥畻绫诲瀷", converter = DictConvert.class)
+    @DictFormat(DictTypeConstants.AUXILIARY_TYPE)
+    private Integer auxiliaryType;
+
+    @Schema(description = "杈呭姪鏍哥畻瀵硅薄缂栧彿", example = "1024")
+    private String auxiliaryId;
+
+    @Schema(description = "杈呭姪鏍哥畻瀵硅薄鍚嶇О", example = "鏉窞鏌愭煇鍏徃")
+    @ExcelProperty("杈呭姪鏍哥畻瀵硅薄")
+    private String auxiliaryName;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/voucher/vo/FinVoucherEntrySaveReqVO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/voucher/vo/FinVoucherEntrySaveReqVO.java
new file mode 100644
index 0000000..255b9ef
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/voucher/vo/FinVoucherEntrySaveReqVO.java
@@ -0,0 +1,43 @@
+package cn.iocoder.yudao.module.finance.controller.admin.voucher.vo;
+
+import cn.iocoder.yudao.module.finance.enums.FinAuxiliaryTypeEnum;
+import cn.iocoder.yudao.framework.common.validation.InEnum;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Schema(description = "绠$悊鍚庡彴 - 鍑瘉鍒嗗綍鏂板/淇敼 Request VO")
+@Data
+public class FinVoucherEntrySaveReqVO {
+
+    @Schema(description = "绉戠洰缂栫爜", requiredMode = Schema.RequiredMode.REQUIRED, example = "1001")
+    @NotEmpty(message = "鍒嗗綍鐨勭鐩紪鐮佷笉鑳戒负绌�")
+    private String subjectCode;
+
+    @Schema(description = "绉戠洰鍚嶇О", example = "搴撳瓨鐜伴噾")
+    private String subjectName;
+
+    @Schema(description = "鎽樿", example = "鏀跺埌璐ф")
+    @Size(max = 500, message = "鍒嗗綍鎽樿闀垮害涓嶈兘瓒呰繃 500 涓瓧绗�")
+    private String summary;
+
+    @Schema(description = "鍊熸柟閲戦", example = "1000.00")
+    private BigDecimal debit;
+
+    @Schema(description = "璐锋柟閲戦", example = "0.00")
+    private BigDecimal credit;
+
+    @Schema(description = "杈呭姪鏍哥畻绫诲瀷", example = "1")
+    @InEnum(value = FinAuxiliaryTypeEnum.class, message = "杈呭姪鏍哥畻绫诲瀷蹇呴』鏄� {value}")
+    private Integer auxiliaryType;
+
+    @Schema(description = "杈呭姪鏍哥畻瀵硅薄缂栧彿", example = "1024")
+    private String auxiliaryId;
+
+    @Schema(description = "杈呭姪鏍哥畻瀵硅薄鍚嶇О", example = "鏉窞鏌愭煇鍏徃")
+    private String auxiliaryName;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/voucher/vo/FinVoucherPageReqVO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/voucher/vo/FinVoucherPageReqVO.java
new file mode 100644
index 0000000..04db25b
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/voucher/vo/FinVoucherPageReqVO.java
@@ -0,0 +1,36 @@
+package cn.iocoder.yudao.module.finance.controller.admin.voucher.vo;
+
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDate;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
+
+@Schema(description = "绠$悊鍚庡彴 - 璁拌处鍑瘉鍒嗛〉 Request VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class FinVoucherPageReqVO extends PageParam {
+
+    @Schema(description = "鍑瘉瀛楀彿", example = "璁�-0001")
+    private String voucherNo;
+
+    @Schema(description = "鎽樿")
+    private String summary;
+
+    @Schema(description = "鍒跺崟浜�", example = "admin")
+    private String creator;
+
+    @Schema(description = "鍑瘉鐘舵��", example = "0")
+    private Integer status;
+
+    @Schema(description = "鍑瘉鏃ユ湡鍖洪棿")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+    private LocalDate[] voucherDate;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/voucher/vo/FinVoucherRespVO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/voucher/vo/FinVoucherRespVO.java
new file mode 100644
index 0000000..c421b17
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/voucher/vo/FinVoucherRespVO.java
@@ -0,0 +1,89 @@
+package cn.iocoder.yudao.module.finance.controller.admin.voucher.vo;
+
+import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
+import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
+import cn.iocoder.yudao.module.finance.enums.DictTypeConstants;
+import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import cn.idev.excel.annotation.write.style.ColumnWidth;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Schema(description = "绠$悊鍚庡彴 - 璁拌处鍑瘉 Response VO")
+@Data
+@ExcelIgnoreUnannotated
+public class FinVoucherRespVO {
+
+    @Schema(description = "鍑瘉缂栧彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    @ExcelProperty("鍑瘉缂栧彿")
+    private Long id;
+
+    @Schema(description = "鍑瘉瀛楀彿", requiredMode = Schema.RequiredMode.REQUIRED, example = "璁�-0001")
+    @ExcelProperty("鍑瘉瀛楀彿")
+    private String voucherNo;
+
+    @Schema(description = "鍑瘉鏃ユ湡", requiredMode = Schema.RequiredMode.REQUIRED)
+    @ExcelProperty("鍑瘉鏃ユ湡")
+    private LocalDate voucherDate;
+
+    @Schema(description = "鎽樿", example = "閿�鍞敹鍏�")
+    @ExcelProperty("鎽樿")
+    private String summary;
+
+    @Schema(description = "鍊熸柟鍚堣", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000.00")
+    @ExcelProperty("鍊熸柟鍚堣")
+    private BigDecimal debit;
+
+    @Schema(description = "璐锋柟鍚堣", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000.00")
+    @ExcelProperty("璐锋柟鍚堣")
+    private BigDecimal credit;
+
+    @Schema(description = "鍑瘉鐘舵��", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
+    // 娉ㄦ剰锛欯DictFormat 鍙礋璐c�屼粠瀛楁涓婂彇瀛楀吀绫诲瀷銆嶏紝鐪熸鍋氱炕璇戠殑鏄� DictConvert锛�
+    // 蹇呴』鍦ㄨ繖閲屾樉寮忔寕涓� converter锛屽惁鍒欏鍑虹殑鏄� 0/10/20 杩欐牱鐨勫師濮嬬爜
+    @ExcelProperty(value = "鍑瘉鐘舵��", converter = DictConvert.class)
+    @DictFormat(DictTypeConstants.VOUCHER_STATUS)
+    private Integer status;
+
+    @Schema(description = "鍒跺崟浜猴紙鐢ㄦ埛缂栧彿锛�", example = "1")
+    private String creator;
+
+    /**
+     * 鍒跺崟浜烘樀绉帮紝涓撲緵鍒楄〃涓� Excel 瀵煎嚭銆�
+     * BaseDO.creator 瀛樼殑鏄敤鎴� ID锛岀洿鎺ュ睍绀哄氨鏄竴涓叉暟瀛楋紝鎵�浠ュ彟鐢ㄨ繖涓瓧娈垫壙杞芥樀绉帮紱
+     * creator 鏈韩浠嶄繚鐣欏湪 JSON 閲岋紙鍓嶇鎼滅储鏉′欢銆佹潈闄愬垽鏂潬瀹冿級銆�
+     */
+    @Schema(description = "鍒跺崟浜烘樀绉�", example = "绠$悊鍛�")
+    @ExcelProperty("鍒跺崟浜�")
+    private String creatorName;
+
+    @Schema(description = "闄勪欢寮犳暟", example = "0")
+    @ExcelProperty("闄勪欢寮犳暟")
+    private Integer attachmentCount;
+
+    @Schema(description = "澶囨敞", example = "澶囨敞")
+    @ExcelProperty("澶囨敞")
+    private String remark;
+
+    @Schema(description = "鍒涘缓鏃堕棿", requiredMode = Schema.RequiredMode.REQUIRED)
+    // 銆寉yyy-MM-dd HH:mm:ss銆嶆濂� 19 涓瓧绗︼紝鑷�傚簲鍒楀灏卞彧缁� 19锛岃创鐫�杈瑰緢闅剧湅锛岃繖閲屾斁瀹藉埌 22
+    @ColumnWidth(22)
+    @ExcelProperty("鍒涘缓鏃堕棿")
+    private LocalDateTime createTime;
+
+    @Schema(description = "鍑瘉鍒嗗綍", example = "[]")
+    private List<FinVoucherEntryRespVO> entries;
+
+    @Schema(description = "闄勪欢鍒楄〃", example = "[]")
+    private List<StorageBlobRespDTO> attachmentList;
+
+    @Schema(description = "鏄惁瀛樺湪闄勪欢", example = "true")
+    private Boolean hasAttachment;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/voucher/vo/FinVoucherSaveReqVO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/voucher/vo/FinVoucherSaveReqVO.java
new file mode 100644
index 0000000..f7d69b2
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/controller/admin/voucher/vo/FinVoucherSaveReqVO.java
@@ -0,0 +1,51 @@
+package cn.iocoder.yudao.module.finance.controller.admin.voucher.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDate;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
+
+@Schema(description = "绠$悊鍚庡彴 - 璁拌处鍑瘉鏂板/淇敼 Request VO")
+@Data
+public class FinVoucherSaveReqVO {
+
+    @Schema(description = "鍑瘉缂栧彿", example = "1024")
+    private Long id;
+
+    @Schema(description = "鍑瘉瀛楀彿锛涗负绌烘椂鐢辩郴缁熸寜銆愬嚟璇佸瓧+骞存湀+搴忓彿銆戣嚜鍔ㄧ敓鎴�", example = "璁�2026090001")
+    @Size(max = 64, message = "鍑瘉瀛楀彿闀垮害涓嶈兘瓒呰繃 64 涓瓧绗�")
+    private String voucherNo;
+
+    @Schema(description = "鍑瘉鏃ユ湡", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotNull(message = "鍑瘉鏃ユ湡涓嶈兘涓虹┖")
+    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
+    private LocalDate voucherDate;
+
+    @Schema(description = "鎽樿", example = "閿�鍞敹鍏�")
+    @Size(max = 500, message = "鎽樿闀垮害涓嶈兘瓒呰繃 500 涓瓧绗�")
+    private String summary;
+
+    @Schema(description = "闄勪欢寮犳暟", example = "0")
+    private Integer attachmentCount;
+
+    @Schema(description = "澶囨敞", example = "澶囨敞")
+    @Size(max = 500, message = "澶囨敞闀垮害涓嶈兘瓒呰繃 500 涓瓧绗�")
+    private String remark;
+
+    @Schema(description = "鍑瘉鍒嗗綍", requiredMode = Schema.RequiredMode.REQUIRED)
+    @NotEmpty(message = "鍑瘉鍒嗗綍涓嶈兘涓虹┖")
+    @Valid
+    private List<FinVoucherEntrySaveReqVO> entries;
+
+    @Schema(description = "闄勪欢缂栧彿鏁扮粍", example = "[]")
+    private List<Long> blobIds;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/dataobject/asset/FinFixedAssetDO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/dataobject/asset/FinFixedAssetDO.java
new file mode 100644
index 0000000..c040c41
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/dataobject/asset/FinFixedAssetDO.java
@@ -0,0 +1,114 @@
+package cn.iocoder.yudao.module.finance.dal.dataobject.asset;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+/**
+ * 鍥哄畾璧勪骇 DO
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@TableName("fin_fixed_asset")
+@KeySequence("fin_fixed_asset_seq") // 鐢ㄤ簬 Oracle銆丳ostgreSQL銆並ingbase銆丏B2銆丠2 鏁版嵁搴撶殑涓婚敭鑷銆傚鏋滄槸 MySQL 绛夋暟鎹簱锛屽彲涓嶅啓銆�
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class FinFixedAssetDO extends BaseDO {
+
+    /**
+     * 璧勪骇缂栧彿
+     */
+    @TableId
+    private Long id;
+    /**
+     * 璧勪骇缂栧彿
+     *
+     * 渚嬪璇达細GD20260512001
+     */
+    private String assetCode;
+    /**
+     * 璧勪骇鍚嶇О
+     */
+    private String assetName;
+    /**
+     * 璧勪骇绫诲埆
+     */
+    private String category;
+    /**
+     * 瑙勬牸鍨嬪彿
+     */
+    private String specification;
+    /**
+     * 璐疆鏃ユ湡
+     */
+    private LocalDate purchaseDate;
+    /**
+     * 璧勪骇鍘熷��
+     */
+    private BigDecimal originalValue;
+    /**
+     * 浣跨敤骞撮檺锛堝勾锛�
+     */
+    private Integer usefulLife;
+    /**
+     * 娈嬪�肩巼锛�%锛�
+     */
+    private BigDecimal residualRate;
+    /**
+     * 绱鎶樻棫
+     */
+    private BigDecimal accumulatedDepreciation;
+    /**
+     * 鍑�鍊� = 鍘熷�� - 绱鎶樻棫
+     */
+    private BigDecimal netValue;
+    /**
+     * 瀛樻斁鍦扮偣
+     */
+    private String location;
+    /**
+     * 浣跨敤閮ㄩ棬锛堝凡搴熷純锛�
+     *
+     * 鍘嗗彶閬楃暀鐨勮嚜鐢辨枃鏈垪锛屽凡涓嶅啀鍐欏叆涓庤鍙栵紝灞曠ず鐢ㄧ殑閮ㄩ棬鍚嶇О鐢� departmentId 瀹炴椂瑙f瀽锛�
+     * 淇濈暀鍒楁槸涓轰簡涓嶄涪鍘嗗彶鏁版嵁锛屾柊閫昏緫涓�寰嬬敤 departmentId銆�
+     */
+    private String department;
+    /**
+     * 淇濈浜猴紙宸插簾寮冿級
+     *
+     * 鍚� department锛屽巻鍙查仐鐣欑殑鑷敱鏂囨湰鍒楋紝宸蹭笉鍐嶅啓鍏ヤ笌璇诲彇锛屽睍绀虹敤鏄电О鐢� keeperId 瀹炴椂瑙f瀽銆�
+     */
+    private String keeper;
+    /**
+     * 浣跨敤閮ㄩ棬缂栧彿
+     *
+     * 鍏宠仈 {@link cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO#getId()}
+     */
+    private Long departmentId;
+    /**
+     * 淇濈浜虹紪鍙�
+     *
+     * 鍏宠仈 {@link cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO#getId()}
+     */
+    private Long keeperId;
+    /**
+     * 鐘舵��
+     *
+     * 鏋氫妇 {@link cn.iocoder.yudao.module.finance.enums.FinFixedAssetStatusEnum}
+     */
+    private Integer status;
+    /**
+     * 澶囨敞
+     */
+    private String remark;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/dataobject/asset/FinIntangibleAssetDO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/dataobject/asset/FinIntangibleAssetDO.java
new file mode 100644
index 0000000..1f5b617
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/dataobject/asset/FinIntangibleAssetDO.java
@@ -0,0 +1,93 @@
+package cn.iocoder.yudao.module.finance.dal.dataobject.asset;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+/**
+ * 鏃犲舰璧勪骇 DO
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@TableName("fin_intangible_asset")
+@KeySequence("fin_intangible_asset_seq") // 鐢ㄤ簬 Oracle銆丳ostgreSQL銆並ingbase銆丏B2銆丠2 鏁版嵁搴撶殑涓婚敭鑷銆傚鏋滄槸 MySQL 绛夋暟鎹簱锛屽彲涓嶅啓銆�
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class FinIntangibleAssetDO extends BaseDO {
+
+    /**
+     * 璧勪骇缂栧彿
+     */
+    @TableId
+    private Long id;
+    /**
+     * 璧勪骇缂栧彿
+     *
+     * 渚嬪璇达細WX20260512001
+     */
+    private String assetCode;
+    /**
+     * 璧勪骇鍚嶇О
+     */
+    private String assetName;
+    /**
+     * 璧勪骇绫诲埆
+     */
+    private String category;
+    /**
+     * 璇佷功缂栧彿
+     */
+    private String certificateNo;
+    /**
+     * 鍙栧緱鏃ユ湡
+     */
+    private LocalDate acquisitionDate;
+    /**
+     * 璧勪骇鍘熷��
+     */
+    private BigDecimal originalValue;
+    /**
+     * 鎽婇攢骞撮檺锛堝勾锛�
+     */
+    private Integer amortizationPeriod;
+    /**
+     * 娈嬪�肩巼锛�%锛�
+     */
+    private BigDecimal residualRate;
+    /**
+     * 绱鎽婇攢
+     */
+    private BigDecimal accumulatedAmortization;
+    /**
+     * 鍑�鍊� = 鍘熷�� - 绱鎽婇攢
+     */
+    private BigDecimal netValue;
+    /**
+     * 鏈夋晥鏈熻嚦
+     */
+    private LocalDate validityDate;
+    /**
+     * 鐘舵��
+     *
+     * 鏋氫妇 {@link cn.iocoder.yudao.module.finance.enums.FinIntangibleAssetStatusEnum}
+     */
+    private Integer status;
+    /**
+     * 璧勪骇鎻忚堪
+     */
+    private String description;
+    /**
+     * 澶囨敞
+     */
+    private String remark;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/dataobject/ledger/FinLedgerEntryDO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/dataobject/ledger/FinLedgerEntryDO.java
new file mode 100644
index 0000000..6f8ddef
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/dataobject/ledger/FinLedgerEntryDO.java
@@ -0,0 +1,56 @@
+package cn.iocoder.yudao.module.finance.dal.dataobject.ledger;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+/**
+ * 鎬昏处 / 鏄庣粏璐︾殑鍒嗗綍鎶曞奖
+ *
+ * 璇ュ璞′笉鏄暟鎹簱琛紝浠呯敤浜庢壙杞� fin_voucher_entry 涓� fin_voucher 鐨勮仈琛ㄦ煡璇㈢粨鏋滐紝
+ * 渚涙�昏处銆佹槑缁嗚处鍦ㄥ唴瀛樹腑閲嶇畻浣欓浣跨敤
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Data
+public class FinLedgerEntryDO {
+
+    /**
+     * 鍑瘉缂栧彿
+     */
+    private Long voucherId;
+    /**
+     * 鍑瘉瀛楀彿
+     */
+    private String voucherNo;
+    /**
+     * 鍑瘉鏃ユ湡
+     */
+    private LocalDate voucherDate;
+    /**
+     * 琛屽彿
+     */
+    private Integer rowNo;
+    /**
+     * 绉戠洰缂栫爜
+     */
+    private String subjectCode;
+    /**
+     * 绉戠洰鍚嶇О
+     */
+    private String subjectName;
+    /**
+     * 鎽樿
+     */
+    private String summary;
+    /**
+     * 鍊熸柟閲戦
+     */
+    private BigDecimal debit;
+    /**
+     * 璐锋柟閲戦
+     */
+    private BigDecimal credit;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/dataobject/subject/FinAccountSubjectDO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/dataobject/subject/FinAccountSubjectDO.java
new file mode 100644
index 0000000..d233335
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/dataobject/subject/FinAccountSubjectDO.java
@@ -0,0 +1,66 @@
+package cn.iocoder.yudao.module.finance.dal.dataobject.subject;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+/**
+ * 浼氳绉戠洰 DO
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@TableName("fin_account_subject")
+@KeySequence("fin_account_subject_seq") // 鐢ㄤ簬 Oracle銆丳ostgreSQL銆並ingbase銆丏B2銆丠2 鏁版嵁搴撶殑涓婚敭鑷銆傚鏋滄槸 MySQL 绛夋暟鎹簱锛屽彲涓嶅啓銆�
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class FinAccountSubjectDO extends BaseDO {
+
+    /**
+     * 绉戠洰缂栧彿
+     */
+    @TableId
+    private Long id;
+    /**
+     * 鐖剁鐩紪鍙�
+     *
+     * 鍏宠仈 {@link #id}锛涗负绌烘椂琛ㄧず鏍圭鐩�
+     */
+    private Long parentId;
+    /**
+     * 绉戠洰缂栫爜
+     */
+    private String subjectCode;
+    /**
+     * 绉戠洰鍚嶇О
+     */
+    private String subjectName;
+    /**
+     * 绉戠洰绫诲瀷
+     *
+     * 鏋氫妇 {@link cn.iocoder.yudao.module.finance.enums.FinSubjectTypeEnum}
+     */
+    private Integer subjectType;
+    /**
+     * 浣欓鏂瑰悜
+     *
+     * 鏋氫妇 {@link cn.iocoder.yudao.module.finance.enums.FinBalanceDirectionEnum}
+     */
+    private Integer balanceDirection;
+    /**
+     * 鐘舵��
+     *
+     * 鏋氫妇 {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}
+     */
+    private Integer status;
+    /**
+     * 澶囨敞
+     */
+    private String remark;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/dataobject/voucher/FinVoucherDO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/dataobject/voucher/FinVoucherDO.java
new file mode 100644
index 0000000..ed105fe
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/dataobject/voucher/FinVoucherDO.java
@@ -0,0 +1,71 @@
+package cn.iocoder.yudao.module.finance.dal.dataobject.voucher;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+/**
+ * 璁拌处鍑瘉 DO
+ *
+ * 鍒跺崟浜轰娇鐢ㄥ熀绫� {@link BaseDO#getCreator()} 瀛楁锛屼笉鍐嶅崟鐙缓鍒�
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@TableName("fin_voucher")
+@KeySequence("fin_voucher_seq") // 鐢ㄤ簬 Oracle銆丳ostgreSQL銆並ingbase銆丏B2銆丠2 鏁版嵁搴撶殑涓婚敭鑷銆傚鏋滄槸 MySQL 绛夋暟鎹簱锛屽彲涓嶅啓銆�
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class FinVoucherDO extends BaseDO {
+
+    /**
+     * 鍑瘉缂栧彿
+     */
+    @TableId
+    private Long id;
+    /**
+     * 鍑瘉瀛楀彿
+     *
+     * 渚嬪璇达細璁�-0001
+     */
+    private String voucherNo;
+    /**
+     * 鍑瘉鏃ユ湡
+     */
+    private LocalDate voucherDate;
+    /**
+     * 鎽樿
+     */
+    private String summary;
+    /**
+     * 鍊熸柟鍚堣
+     */
+    private BigDecimal debit;
+    /**
+     * 璐锋柟鍚堣
+     */
+    private BigDecimal credit;
+    /**
+     * 鍑瘉鐘舵��
+     *
+     * 鏋氫妇 {@link cn.iocoder.yudao.module.finance.enums.FinVoucherStatusEnum}
+     */
+    private Integer status;
+    /**
+     * 闄勪欢寮犳暟
+     */
+    private Integer attachmentCount;
+    /**
+     * 澶囨敞
+     */
+    private String remark;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/dataobject/voucher/FinVoucherEntryDO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/dataobject/voucher/FinVoucherEntryDO.java
new file mode 100644
index 0000000..8ee2c4a
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/dataobject/voucher/FinVoucherEntryDO.java
@@ -0,0 +1,82 @@
+package cn.iocoder.yudao.module.finance.dal.dataobject.voucher;
+
+import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.*;
+
+import java.math.BigDecimal;
+
+/**
+ * 鍑瘉鍒嗗綍 DO
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@TableName("fin_voucher_entry")
+@KeySequence("fin_voucher_entry_seq") // 鐢ㄤ簬 Oracle銆丳ostgreSQL銆並ingbase銆丏B2銆丠2 鏁版嵁搴撶殑涓婚敭鑷銆傚鏋滄槸 MySQL 绛夋暟鎹簱锛屽彲涓嶅啓銆�
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class FinVoucherEntryDO extends BaseDO {
+
+    /**
+     * 鍒嗗綍缂栧彿
+     */
+    @TableId
+    private Long id;
+    /**
+     * 鍑瘉缂栧彿
+     *
+     * 鍏宠仈 {@link FinVoucherDO#getId()}
+     */
+    private Long voucherId;
+    /**
+     * 琛屽彿锛屼粠 1 寮�濮�
+     */
+    private Integer rowNo;
+    /**
+     * 绉戠洰缂栫爜
+     *
+     * 鍏宠仈 {@link cn.iocoder.yudao.module.finance.dal.dataobject.subject.FinAccountSubjectDO#getSubjectCode()}
+     */
+    private String subjectCode;
+    /**
+     * 绉戠洰鍚嶇О
+     *
+     * 鍐椾綑瀛楁锛屼繚瀛樺垎褰曟椂浠庝細璁$鐩甫鍑�
+     */
+    private String subjectName;
+    /**
+     * 鎽樿
+     */
+    private String summary;
+    /**
+     * 鍊熸柟閲戦
+     */
+    private BigDecimal debit;
+    /**
+     * 璐锋柟閲戦
+     */
+    private BigDecimal credit;
+    /**
+     * 杈呭姪鏍哥畻绫诲瀷
+     *
+     * 鏋氫妇 {@link cn.iocoder.yudao.module.finance.enums.FinAuxiliaryTypeEnum}
+     */
+    private Integer auxiliaryType;
+    /**
+     * 杈呭姪鏍哥畻瀵硅薄缂栧彿
+     *
+     * 鏍规嵁 {@link #auxiliaryType} 鎸囧悜瀹㈡埛銆佷緵搴斿晢銆侀儴闂ㄣ�佸憳宸ャ�侀」鐩殑涓婚敭
+     */
+    private String auxiliaryId;
+    /**
+     * 杈呭姪鏍哥畻瀵硅薄鍚嶇О
+     */
+    private String auxiliaryName;
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/mysql/asset/FinFixedAssetMapper.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/mysql/asset/FinFixedAssetMapper.java
new file mode 100644
index 0000000..0df9a4e
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/mysql/asset/FinFixedAssetMapper.java
@@ -0,0 +1,42 @@
+package cn.iocoder.yudao.module.finance.dal.mysql.asset;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.finance.controller.admin.asset.vo.FinFixedAssetPageReqVO;
+import cn.iocoder.yudao.module.finance.dal.dataobject.asset.FinFixedAssetDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 鍥哄畾璧勪骇 Mapper
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Mapper
+public interface FinFixedAssetMapper extends BaseMapperX<FinFixedAssetDO> {
+
+    default PageResult<FinFixedAssetDO> selectPage(FinFixedAssetPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<FinFixedAssetDO>()
+                .likeIfPresent(FinFixedAssetDO::getAssetCode, reqVO.getAssetCode())
+                .likeIfPresent(FinFixedAssetDO::getAssetName, reqVO.getAssetName())
+                .eqIfPresent(FinFixedAssetDO::getCategory, reqVO.getCategory())
+                .eqIfPresent(FinFixedAssetDO::getDepartmentId, reqVO.getDepartmentId())
+                .eqIfPresent(FinFixedAssetDO::getKeeperId, reqVO.getKeeperId())
+                .eqIfPresent(FinFixedAssetDO::getStatus, reqVO.getStatus())
+                .betweenIfPresent(FinFixedAssetDO::getPurchaseDate, reqVO.getPurchaseDate())
+                .orderByDesc(FinFixedAssetDO::getId));
+    }
+
+    default FinFixedAssetDO selectByAssetCode(String assetCode) {
+        return selectOne(FinFixedAssetDO::getAssetCode, assetCode);
+    }
+
+    default List<FinFixedAssetDO> selectListByIds(Collection<Long> ids) {
+        return selectList(new LambdaQueryWrapperX<FinFixedAssetDO>()
+                .in(FinFixedAssetDO::getId, ids));
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/mysql/asset/FinIntangibleAssetMapper.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/mysql/asset/FinIntangibleAssetMapper.java
new file mode 100644
index 0000000..43364d2
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/mysql/asset/FinIntangibleAssetMapper.java
@@ -0,0 +1,41 @@
+package cn.iocoder.yudao.module.finance.dal.mysql.asset;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.finance.controller.admin.asset.vo.FinIntangibleAssetPageReqVO;
+import cn.iocoder.yudao.module.finance.dal.dataobject.asset.FinIntangibleAssetDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 鏃犲舰璧勪骇 Mapper
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Mapper
+public interface FinIntangibleAssetMapper extends BaseMapperX<FinIntangibleAssetDO> {
+
+    default PageResult<FinIntangibleAssetDO> selectPage(FinIntangibleAssetPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<FinIntangibleAssetDO>()
+                .likeIfPresent(FinIntangibleAssetDO::getAssetCode, reqVO.getAssetCode())
+                .likeIfPresent(FinIntangibleAssetDO::getAssetName, reqVO.getAssetName())
+                .eqIfPresent(FinIntangibleAssetDO::getCategory, reqVO.getCategory())
+                .likeIfPresent(FinIntangibleAssetDO::getCertificateNo, reqVO.getCertificateNo())
+                .eqIfPresent(FinIntangibleAssetDO::getStatus, reqVO.getStatus())
+                .betweenIfPresent(FinIntangibleAssetDO::getAcquisitionDate, reqVO.getAcquisitionDate())
+                .orderByDesc(FinIntangibleAssetDO::getId));
+    }
+
+    default FinIntangibleAssetDO selectByAssetCode(String assetCode) {
+        return selectOne(FinIntangibleAssetDO::getAssetCode, assetCode);
+    }
+
+    default List<FinIntangibleAssetDO> selectListByIds(Collection<Long> ids) {
+        return selectList(new LambdaQueryWrapperX<FinIntangibleAssetDO>()
+                .in(FinIntangibleAssetDO::getId, ids));
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/mysql/ledger/FinLedgerMapper.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/mysql/ledger/FinLedgerMapper.java
new file mode 100644
index 0000000..f6e7fea
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/mysql/ledger/FinLedgerMapper.java
@@ -0,0 +1,49 @@
+package cn.iocoder.yudao.module.finance.dal.mysql.ledger;
+
+import cn.iocoder.yudao.module.finance.dal.dataobject.ledger.FinLedgerEntryDO;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 鎬昏处 / 鏄庣粏璐� Mapper
+ *
+ * 鍙粺璁″凡杩囪处锛坰tatus = posted锛夌殑鍑瘉鍒嗗綍锛涙�昏处銆佹槑缁嗚处鐨勪綑棰濆湪 Service 灞傛寜鏈熼棿閲嶇畻锛�
+ * 淇濊瘉鍙e緞涓庡嚟璇佺姸鎬佷竴鑷�
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Mapper
+public interface FinLedgerMapper {
+
+    /**
+     * 鏌ヨ鎸囧畾绉戠洰锛堝彲澶氫釜锛夊湪鎸囧畾鏃ユ湡鍖洪棿鍐呯殑宸茶繃璐﹀垎褰�
+     *
+     * @param subjectCodes 绉戠洰缂栫爜闆嗗悎
+     * @param startDate    寮�濮嬫棩鏈燂紙鍚級
+     * @param endDate      缁撴潫鏃ユ湡锛堝惈锛�
+     * @param status       鍑瘉鐘舵��
+     * @return 鍒嗗綍鍒楄〃锛堟寜鍑瘉鏃ユ湡銆佸嚟璇佺紪鍙枫�佽鍙峰崌搴忥級
+     */
+    List<FinLedgerEntryDO> selectEntryList(@Param("subjectCodes") Collection<String> subjectCodes,
+                                           @Param("startDate") LocalDate startDate,
+                                           @Param("endDate") LocalDate endDate,
+                                           @Param("status") Integer status);
+
+    /**
+     * 鏌ヨ鎸囧畾绉戠洰锛堝彲澶氫釜锛夊湪鎸囧畾鏃ユ湡涔嬪墠鐨勫凡杩囪处鍒嗗綍鍑�棰濓紝鍗虫湡鍒濅綑棰�
+     *
+     * @param subjectCodes 绉戠洰缂栫爜闆嗗悎
+     * @param date         鏃ユ湡锛堜笉鍚級
+     * @param status       鍑瘉鐘舵��
+     * @return 鍑�棰濓紝鍊熸柟涓烘銆佽捶鏂逛负璐�
+     */
+    BigDecimal selectBalanceBeforeDate(@Param("subjectCodes") Collection<String> subjectCodes,
+                                       @Param("date") LocalDate date,
+                                       @Param("status") Integer status);
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/mysql/subject/FinAccountSubjectMapper.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/mysql/subject/FinAccountSubjectMapper.java
new file mode 100644
index 0000000..ac94793
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/mysql/subject/FinAccountSubjectMapper.java
@@ -0,0 +1,48 @@
+package cn.iocoder.yudao.module.finance.dal.mysql.subject;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.finance.controller.admin.subject.vo.FinAccountSubjectPageReqVO;
+import cn.iocoder.yudao.module.finance.dal.dataobject.subject.FinAccountSubjectDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 浼氳绉戠洰 Mapper
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Mapper
+public interface FinAccountSubjectMapper extends BaseMapperX<FinAccountSubjectDO> {
+
+    default PageResult<FinAccountSubjectDO> selectPage(FinAccountSubjectPageReqVO reqVO) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<FinAccountSubjectDO>()
+                .likeIfPresent(FinAccountSubjectDO::getSubjectCode, reqVO.getSubjectCode())
+                .likeIfPresent(FinAccountSubjectDO::getSubjectName, reqVO.getSubjectName())
+                .eqIfPresent(FinAccountSubjectDO::getSubjectType, reqVO.getSubjectType())
+                .eqIfPresent(FinAccountSubjectDO::getStatus, reqVO.getStatus())
+                .orderByAsc(FinAccountSubjectDO::getSubjectCode));
+    }
+
+    default FinAccountSubjectDO selectBySubjectCode(String subjectCode) {
+        return selectOne(FinAccountSubjectDO::getSubjectCode, subjectCode);
+    }
+
+    default List<FinAccountSubjectDO> selectListByParentId(Long parentId) {
+        return selectList(FinAccountSubjectDO::getParentId, parentId);
+    }
+
+    default List<FinAccountSubjectDO> selectListBySubjectCodes(Collection<String> subjectCodes) {
+        return selectList(new LambdaQueryWrapperX<FinAccountSubjectDO>()
+                .in(FinAccountSubjectDO::getSubjectCode, subjectCodes));
+    }
+
+    default List<FinAccountSubjectDO> selectListAll() {
+        return selectList(new LambdaQueryWrapperX<FinAccountSubjectDO>()
+                .orderByAsc(FinAccountSubjectDO::getSubjectCode));
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/mysql/voucher/FinVoucherEntryMapper.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/mysql/voucher/FinVoucherEntryMapper.java
new file mode 100644
index 0000000..b16cb70
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/mysql/voucher/FinVoucherEntryMapper.java
@@ -0,0 +1,47 @@
+package cn.iocoder.yudao.module.finance.dal.mysql.voucher;
+
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.finance.dal.dataobject.voucher.FinVoucherEntryDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 鍑瘉鍒嗗綍 Mapper
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Mapper
+public interface FinVoucherEntryMapper extends BaseMapperX<FinVoucherEntryDO> {
+
+    default List<FinVoucherEntryDO> selectListByVoucherId(Long voucherId) {
+        return selectList(new LambdaQueryWrapperX<FinVoucherEntryDO>()
+                .eq(FinVoucherEntryDO::getVoucherId, voucherId)
+                .orderByAsc(FinVoucherEntryDO::getRowNo));
+    }
+
+    default List<FinVoucherEntryDO> selectListByVoucherIds(Collection<Long> voucherIds) {
+        return selectList(new LambdaQueryWrapperX<FinVoucherEntryDO>()
+                .in(FinVoucherEntryDO::getVoucherId, voucherIds)
+                .orderByAsc(FinVoucherEntryDO::getVoucherId)
+                .orderByAsc(FinVoucherEntryDO::getRowNo));
+    }
+
+    default Long selectCountBySubjectCodes(Collection<String> subjectCodes) {
+        return selectCount(new LambdaQueryWrapperX<FinVoucherEntryDO>()
+                .in(FinVoucherEntryDO::getSubjectCode, subjectCodes));
+    }
+
+    default void deleteByVoucherId(Long voucherId) {
+        delete(new LambdaQueryWrapperX<FinVoucherEntryDO>()
+                .eq(FinVoucherEntryDO::getVoucherId, voucherId));
+    }
+
+    default void deleteByVoucherIds(Collection<Long> voucherIds) {
+        delete(new LambdaQueryWrapperX<FinVoucherEntryDO>()
+                .in(FinVoucherEntryDO::getVoucherId, voucherIds));
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/mysql/voucher/FinVoucherMapper.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/mysql/voucher/FinVoucherMapper.java
new file mode 100644
index 0000000..0de457a
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/mysql/voucher/FinVoucherMapper.java
@@ -0,0 +1,58 @@
+package cn.iocoder.yudao.module.finance.dal.mysql.voucher;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
+import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.iocoder.yudao.module.finance.controller.admin.voucher.vo.FinVoucherPageReqVO;
+import cn.iocoder.yudao.module.finance.dal.dataobject.voucher.FinVoucherDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.time.LocalDate;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 璁拌处鍑瘉 Mapper
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Mapper
+public interface FinVoucherMapper extends BaseMapperX<FinVoucherDO> {
+
+    /**
+     * @param creatorIds 鍒跺崟浜虹紪鍙烽泦鍚堛�傚垪琛ㄩ噷灞曠ず鐨勬槸鏄电О锛屾墍浠� reqVO.getCreator() 鎷垮埌鐨勬槸鏄电О/缂栧彿杩欑被
+     *                   鎼滅储璇嶏紝鐢� Service 鍏堣В鏋愭垚缂栧彿闆嗗悎鍐嶄紶杩涙潵锛涗紶 null 琛ㄧず涓嶆寜鍒跺崟浜鸿繃婊ゃ��
+     */
+    default PageResult<FinVoucherDO> selectPage(FinVoucherPageReqVO reqVO, Collection<String> creatorIds) {
+        return selectPage(reqVO, new LambdaQueryWrapperX<FinVoucherDO>()
+                .likeIfPresent(FinVoucherDO::getVoucherNo, reqVO.getVoucherNo())
+                .likeIfPresent(FinVoucherDO::getSummary, reqVO.getSummary())
+                // 娉ㄦ剰锛歝reator 瀛樼殑鏄敤鎴风紪鍙凤紝鎼滅储璇嶄笉鏄紪鍙凤紙鏄樀绉帮級鏃朵笉鑳界洿鎺� like锛�
+                // 鍚﹀垯銆岃档纾娿�嶈繖绉嶆樀绉版案杩滃尮閰嶄笉涓婏紱Service 宸茬粡鎶婃悳绱㈣瘝瑙f瀽鎴愮紪鍙烽泦鍚�
+                .inIfPresent(FinVoucherDO::getCreator, creatorIds)
+                .eqIfPresent(FinVoucherDO::getStatus, reqVO.getStatus())
+                .betweenIfPresent(FinVoucherDO::getVoucherDate, reqVO.getVoucherDate())
+                .orderByDesc(FinVoucherDO::getVoucherDate)
+                .orderByDesc(FinVoucherDO::getId));
+    }
+
+    default FinVoucherDO selectByVoucherNo(String voucherNo) {
+        return selectOne(FinVoucherDO::getVoucherNo, voucherNo);
+    }
+
+    default List<FinVoucherDO> selectListByIds(Collection<Long> ids) {
+        return selectList(new LambdaQueryWrapperX<FinVoucherDO>()
+                .in(FinVoucherDO::getId, ids));
+    }
+
+    default Long selectCountByStatus(Integer status) {
+        return selectCount(FinVoucherDO::getStatus, status);
+    }
+
+    default List<FinVoucherDO> selectListByDateBetween(LocalDate startDate, LocalDate endDate) {
+        return selectList(new LambdaQueryWrapperX<FinVoucherDO>()
+                .geIfPresent(FinVoucherDO::getVoucherDate, startDate)
+                .leIfPresent(FinVoucherDO::getVoucherDate, endDate));
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/redis/RedisKeyConstants.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/redis/RedisKeyConstants.java
new file mode 100644
index 0000000..0db1cb7
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/redis/RedisKeyConstants.java
@@ -0,0 +1,18 @@
+package cn.iocoder.yudao.module.finance.dal.redis;
+
+/**
+ * Finance Redis Key 鏋氫妇绫�
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+public interface RedisKeyConstants {
+
+    /**
+     * 搴忓彿鐨勭紦瀛�
+     *
+     * KEY 鏍煎紡锛歠inance:seq_no:{prefix}
+     * VALUE 鏁版嵁鏍煎紡锛氱紪鍙疯嚜澧�
+     */
+    String NO = "finance:seq_no:";
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/redis/no/FinNoRedisDAO.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/redis/no/FinNoRedisDAO.java
new file mode 100644
index 0000000..8ff6b5c
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/dal/redis/no/FinNoRedisDAO.java
@@ -0,0 +1,46 @@
+package cn.iocoder.yudao.module.finance.dal.redis.no;
+
+import cn.hutool.core.date.DatePattern;
+import cn.hutool.core.date.DateUtil;
+import cn.iocoder.yudao.module.finance.dal.redis.RedisKeyConstants;
+import jakarta.annotation.Resource;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Repository;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+
+/**
+ * Finance 搴忓彿鐨� Redis DAO
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Repository
+public class FinNoRedisDAO {
+
+    /**
+     * 鍑瘉瀛楃殑榛樿瀛楀彿
+     */
+    public static final String VOUCHER_NO_PREFIX = "璁�";
+
+    @Resource
+    private StringRedisTemplate stringRedisTemplate;
+
+    /**
+     * 鐢熸垚搴忓彿锛屼娇鐢ㄥ綋鍓嶆棩鏈燂紝鏍煎紡涓� {PREFIX} + yyyyMM + 4 浣嶈嚜澧�
+     * 渚嬪璇达細璁� 202609 0001 锛堟病鏈変腑闂寸┖鏍硷級
+     *
+     * @param prefix 鍓嶇紑
+     * @return 搴忓彿
+     */
+    public String generate(String prefix) {
+        // 閫掑搴忓彿
+        String noPrefix = prefix + DateUtil.format(LocalDateTime.now(), DatePattern.SIMPLE_MONTH_PATTERN);
+        String key = RedisKeyConstants.NO + noPrefix;
+        Long no = stringRedisTemplate.opsForValue().increment(key);
+        // 璁剧疆杩囨湡鏃堕棿
+        stringRedisTemplate.expire(key, Duration.ofDays(1L));
+        return noPrefix + String.format("%04d", no);
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/DictTypeConstants.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/DictTypeConstants.java
new file mode 100644
index 0000000..557a66c
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/DictTypeConstants.java
@@ -0,0 +1,17 @@
+package cn.iocoder.yudao.module.finance.enums;
+
+/**
+ * Finance 瀛楀吀绫诲瀷鐨勬灇涓剧被
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+public interface DictTypeConstants {
+
+    String SUBJECT_TYPE = "finance_subject_type"; // 绉戠洰绫诲瀷
+    String BALANCE_DIRECTION = "finance_balance_direction"; // 浣欓鏂瑰悜
+    String AUXILIARY_TYPE = "finance_auxiliary_type"; // 杈呭姪鏍哥畻绫诲瀷
+    String VOUCHER_STATUS = "finance_voucher_status"; // 鍑瘉鐘舵��
+    String FIXED_ASSET_STATUS = "finance_fixed_asset_status"; // 鍥哄畾璧勪骇鐘舵��
+    String INTANGIBLE_ASSET_STATUS = "finance_intangible_asset_status"; // 鏃犲舰璧勪骇鐘舵��
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/ErrorCodeConstants.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/ErrorCodeConstants.java
new file mode 100644
index 0000000..ba30f29
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/ErrorCodeConstants.java
@@ -0,0 +1,56 @@
+package cn.iocoder.yudao.module.finance.enums;
+
+import cn.iocoder.yudao.framework.common.exception.ErrorCode;
+
+/**
+ * Finance 妯″潡閿欒鐮�
+ *
+ * finance 妯″潡锛屼娇鐢� 1-060-xxx-xxx 娈�
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+public interface ErrorCodeConstants {
+
+    // ========== 浼氳绉戠洰 1_060_100_xxx ==========
+    ErrorCode ACCOUNT_SUBJECT_NOT_EXISTS = new ErrorCode(1_060_100_000, "浼氳绉戠洰涓嶅瓨鍦�");
+    ErrorCode ACCOUNT_SUBJECT_CODE_DUPLICATE = new ErrorCode(1_060_100_001, "绉戠洰缂栫爜銆恵}銆戝凡瀛樺湪锛岃鏇存崲鍚庨噸璇�");
+    ErrorCode ACCOUNT_SUBJECT_PARENT_NOT_EXISTS = new ErrorCode(1_060_100_002, "鐖剁鐩�恵}銆戜笉瀛樺湪锛岃閲嶆柊閫夋嫨涓婄骇绉戠洰");
+    ErrorCode ACCOUNT_SUBJECT_PARENT_SELF = new ErrorCode(1_060_100_003, "涓婄骇绉戠洰涓嶈兘閫夋嫨鑷繁锛岃閲嶆柊閫夋嫨");
+    ErrorCode ACCOUNT_SUBJECT_PARENT_CYCLE = new ErrorCode(1_060_100_004, "涓婄骇绉戠洰銆恵}銆戞槸褰撳墠绉戠洰鐨勪笅绾э紝浼氬舰鎴愬惊鐜紝璇烽噸鏂伴�夋嫨");
+    ErrorCode ACCOUNT_SUBJECT_HAS_CHILDREN = new ErrorCode(1_060_100_005, "浼氳绉戠洰銆恵}銆戜笅杩樻湁 {} 涓笅绾х鐩紝璇峰厛鍒犻櫎鎴栬浆绉讳笅绾х鐩�");
+    ErrorCode ACCOUNT_SUBJECT_REFERENCED = new ErrorCode(1_060_100_006, "浼氳绉戠洰銆恵}銆戝凡琚� {} 鏉″嚟璇佸垎褰曞紩鐢紝鏃犳硶鍒犻櫎锛涜鍏堝鐞嗗搴斿嚟璇�");
+
+    // ========== 鍑瘉 1_060_101_xxx ==========
+    ErrorCode VOUCHER_NOT_EXISTS = new ErrorCode(1_060_101_000, "鍑瘉涓嶅瓨鍦�");
+    ErrorCode VOUCHER_NO_DUPLICATE = new ErrorCode(1_060_101_001, "鍑瘉瀛楀彿銆恵}銆戝凡瀛樺湪锛岃鏇存崲鍚庨噸璇�");
+    ErrorCode VOUCHER_ENTRY_EMPTY = new ErrorCode(1_060_101_002, "鍑瘉鑷冲皯闇�瑕佷竴鏉℃湁鏁堝垎褰曪紙绉戠洰涓嶄负绌猴紝涓斿�熸柟鎴栬捶鏂归噾棰濆ぇ浜� 0锛�");
+    ErrorCode VOUCHER_ENTRY_DIRECTION_BOTH = new ErrorCode(1_060_101_003, "绗� {} 琛屽垎褰曪紙绉戠洰銆恵}銆戯級鐨勫�熸柟閲戦銆恵}銆戜笌璐锋柟閲戦銆恵}銆戜笉鑳藉悓鏃跺ぇ浜� 0锛岃鍙~鍐欏叾涓竴鏂�");
+    ErrorCode VOUCHER_ENTRY_SUBJECT_NOT_EXISTS = new ErrorCode(1_060_101_004, "绗� {} 琛屽垎褰曠殑绉戠洰缂栫爜銆恵}銆戜笉瀛樺湪锛岃閲嶆柊閫夋嫨绉戠洰");
+    ErrorCode VOUCHER_ENTRY_UNBALANCED = new ErrorCode(1_060_101_005, "鍑瘉鍊熻捶涓嶅钩琛★細鍊熸柟鍚堣銆恵}銆戝厓锛岃捶鏂瑰悎璁°�恵}銆戝厓锛屼袱鑰呭繀椤荤浉绛変笖澶т簬 0");
+    ErrorCode VOUCHER_STATUS_NOT_UNPOSTED = new ErrorCode(1_060_101_006, "鍑瘉銆恵}銆戝綋鍓嶇姸鎬佷负銆恵}銆戯紝鍙湁鏈繃璐︾殑鍑瘉鍏佽淇敼鎴栬繃璐�");
+    ErrorCode VOUCHER_DELETE_NOT_ALLOWED = new ErrorCode(1_060_101_007, "鍑瘉銆恵}銆戝綋鍓嶇姸鎬佷负銆恵}銆戯紝涓嶅厑璁稿垹闄わ紱宸茶繃璐﹀嚟璇佽缂栧埗绾㈠瓧鍑瘉鍐查攢锛屽凡浣滃簾鍑瘉鍙洿鎺ュ垹闄�");
+    ErrorCode VOUCHER_ENTRY_SUBJECT_REQUIRED = new ErrorCode(1_060_101_008, "绗� {} 琛屽垎褰曞凡濉啓閲戦锛堝�熸柟銆恵}銆戝厓銆佽捶鏂广�恵}銆戝厓锛夛紝浣嗘湭閫夋嫨浼氳绉戠洰锛岃閫夋嫨绉戠洰鎴栧垹闄よ琛�");
+    ErrorCode VOUCHER_ENTRY_AMOUNT_REQUIRED = new ErrorCode(1_060_101_009, "绗� {} 琛屽垎褰曪紙绉戠洰銆恵}銆戯級鏈~鍐欏�熸柟鎴栬捶鏂归噾棰濓紝璇疯ˉ鍏ㄩ噾棰濇垨鍒犻櫎璇ヨ");
+    ErrorCode VOUCHER_STATUS_NOT_CANCELLABLE = new ErrorCode(1_060_101_010, "鍑瘉銆恵}銆戝綋鍓嶇姸鎬佷负銆恵}銆戯紝宸蹭綔搴熺殑鍑瘉涓嶈兘閲嶅浣滃簾");
+
+    // ========== 鎬昏处 / 鏄庣粏璐� 1_060_102_xxx ==========
+    ErrorCode LEDGER_SUBJECT_REQUIRED = new ErrorCode(1_060_102_000, "鏌ヨ鎬昏处/鏄庣粏璐﹀繀椤绘寚瀹氫細璁$鐩�");
+    ErrorCode LEDGER_MONTH_RANGE_INVALID = new ErrorCode(1_060_102_001, "鏌ヨ鏈熼棿涓嶅悎娉曪細寮�濮嬫湀浠姐�恵}銆戜笉鑳芥櫄浜庣粨鏉熸湀浠姐�恵}銆�");
+    ErrorCode LEDGER_SUBJECT_NOT_EXISTS = new ErrorCode(1_060_102_002, "浼氳绉戠洰銆恵}銆戜笉瀛樺湪锛岃閲嶆柊閫夋嫨绉戠洰");
+
+    // ========== 璐㈠姟鎶ヨ〃 / 浼氳鏍哥畻鐪嬫澘 1_060_103_xxx ==========
+    ErrorCode STATISTICS_PERIOD_INVALID = new ErrorCode(1_060_103_000, "缁熻鏈熼棿涓嶅悎娉曪細寮�濮嬫湀浠姐�恵}銆戜笉鑳芥櫄浜庣粨鏉熸湀浠姐�恵}銆�");
+
+    // ========== 鍥哄畾璧勪骇 1_060_200_xxx ==========
+    ErrorCode FIXED_ASSET_NOT_EXISTS = new ErrorCode(1_060_200_000, "鍥哄畾璧勪骇涓嶅瓨鍦�");
+    ErrorCode FIXED_ASSET_CODE_DUPLICATE = new ErrorCode(1_060_200_001, "璧勪骇缂栧彿銆恵}銆戝凡瀛樺湪锛岃鏇存崲鍚庨噸璇�");
+    ErrorCode FIXED_ASSET_DEPRECIATE_EMPTY = new ErrorCode(1_060_200_002, "娌℃湁鍙鎻愭姌鏃х殑鍥哄畾璧勪骇锛氭墍閫夎祫浜х殑绱鎶樻棫鍧囧凡鎻愯冻锛屾棤闇�鍐嶈鎻�");
+    ErrorCode FIXED_ASSET_DEPRECIATE_NEGATIVE = new ErrorCode(1_060_200_003, "鍥哄畾璧勪骇銆恵}銆戠殑鍘熷�间负銆恵}銆戝厓銆佷娇鐢ㄥ勾闄� {} 骞淬�佹畫鍊肩巼 {}%锛屾湀鎶樻棫棰濊绠楃粨鏋滃皬浜庣瓑浜� 0锛岃妫�鏌ュ師鍊笺�佷娇鐢ㄥ勾闄愪笌娈嬪�肩巼鍚庨噸鏂颁繚瀛樿璧勪骇");
+
+    // ========== 鏃犲舰璧勪骇 1_060_201_xxx ==========
+    ErrorCode INTANGIBLE_ASSET_NOT_EXISTS = new ErrorCode(1_060_201_000, "鏃犲舰璧勪骇涓嶅瓨鍦�");
+    ErrorCode INTANGIBLE_ASSET_CODE_DUPLICATE = new ErrorCode(1_060_201_001, "璧勪骇缂栧彿銆恵}銆戝凡瀛樺湪锛岃鏇存崲鍚庨噸璇�");
+    ErrorCode INTANGIBLE_ASSET_AMORTIZE_EMPTY = new ErrorCode(1_060_201_002, "娌℃湁鍙鎻愭憡閿�鐨勬棤褰㈣祫浜э細鎵�閫夎祫浜х殑绱鎽婇攢鍧囧凡鎽婂畬锛屾棤闇�鍐嶈鎻�");
+    ErrorCode INTANGIBLE_ASSET_AMORTIZE_NEGATIVE = new ErrorCode(1_060_201_003, "鏃犲舰璧勪骇銆恵}銆戠殑鍘熷�间负銆恵}銆戝厓銆佹憡閿�骞撮檺 {} 骞淬�佹畫鍊肩巼 {}%锛屾湀鎽婇攢棰濊绠楃粨鏋滃皬浜庣瓑浜� 0锛岃妫�鏌ュ師鍊笺�佹憡閿�骞撮檺涓庢畫鍊肩巼鍚庨噸鏂颁繚瀛樿璧勪骇");
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinAuxiliaryTypeEnum.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinAuxiliaryTypeEnum.java
new file mode 100644
index 0000000..bbf8661
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinAuxiliaryTypeEnum.java
@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.module.finance.enums;
+
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Arrays;
+
+/**
+ * 鍑瘉鍒嗗綍鍙栬緟鍔╂牳绠楃被鍨嬫灇涓�
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@RequiredArgsConstructor
+@Getter
+public enum FinAuxiliaryTypeEnum implements ArrayValuable<Integer> {
+
+    CUSTOMER(1, "瀹㈡埛"),
+    SUPPLIER(2, "渚涘簲鍟�"),
+    DEPARTMENT(3, "閮ㄩ棬"),
+    EMPLOYEE(4, "鍛樺伐"),
+    PROJECT(5, "椤圭洰");
+
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(FinAuxiliaryTypeEnum::getType).toArray(Integer[]::new);
+
+    /**
+     * 绫诲瀷
+     */
+    private final Integer type;
+    /**
+     * 绫诲瀷鍚�
+     */
+    private final String name;
+
+    @Override
+    public Integer[] array() {
+        return ARRAYS;
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinBalanceDirectionEnum.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinBalanceDirectionEnum.java
new file mode 100644
index 0000000..1362a6f
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinBalanceDirectionEnum.java
@@ -0,0 +1,49 @@
+package cn.iocoder.yudao.module.finance.enums;
+
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.math.BigDecimal;
+import java.util.Arrays;
+
+/**
+ * 浼氳绉戠洰浣欓鏂瑰悜鏋氫妇
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@RequiredArgsConstructor
+@Getter
+public enum FinBalanceDirectionEnum implements ArrayValuable<Integer> {
+
+    DEBIT(1, "鍊�"),
+    CREDIT(2, "璐�");
+
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(FinBalanceDirectionEnum::getDirection).toArray(Integer[]::new);
+
+    /**
+     * 鏂瑰悜
+     */
+    private final Integer direction;
+    /**
+     * 鏂瑰悜鍚�
+     */
+    private final String name;
+
+    @Override
+    public Integer[] array() {
+        return ARRAYS;
+    }
+
+    /**
+     * 鏍规嵁浣欓锛堝�熸柟涓烘銆佽捶鏂逛负璐燂級鍒ゆ柇浣欓鏂瑰悜
+     *
+     * @param balance 浣欓锛屽厑璁镐负绌猴紙鎸� 0 澶勭悊锛�
+     * @return 浣欓鏂瑰悜
+     */
+    public static Integer getByBalance(BigDecimal balance) {
+        return balance != null && balance.compareTo(BigDecimal.ZERO) < 0
+                ? CREDIT.getDirection() : DEBIT.getDirection();
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinFixedAssetStatusEnum.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinFixedAssetStatusEnum.java
new file mode 100644
index 0000000..3e4b4b9
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinFixedAssetStatusEnum.java
@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.finance.enums;
+
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Arrays;
+
+/**
+ * 鍥哄畾璧勪骇鐘舵�佹灇涓�
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@RequiredArgsConstructor
+@Getter
+public enum FinFixedAssetStatusEnum implements ArrayValuable<Integer> {
+
+    IN_USE(1, "鍦ㄧ敤"),
+    IDLE(2, "闂茬疆"),
+    REPAIR(3, "缁翠慨涓�"),
+    SCRAPPED(4, "宸叉姤搴�");
+
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(FinFixedAssetStatusEnum::getStatus).toArray(Integer[]::new);
+
+    /**
+     * 鐘舵��
+     */
+    private final Integer status;
+    /**
+     * 鐘舵�佸悕
+     */
+    private final String name;
+
+    @Override
+    public Integer[] array() {
+        return ARRAYS;
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinIntangibleAssetStatusEnum.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinIntangibleAssetStatusEnum.java
new file mode 100644
index 0000000..8b490ef
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinIntangibleAssetStatusEnum.java
@@ -0,0 +1,38 @@
+package cn.iocoder.yudao.module.finance.enums;
+
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Arrays;
+
+/**
+ * 鏃犲舰璧勪骇鐘舵�佹灇涓�
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@RequiredArgsConstructor
+@Getter
+public enum FinIntangibleAssetStatusEnum implements ArrayValuable<Integer> {
+
+    IN_USE(1, "鍦ㄧ敤"),
+    EXPIRED(2, "宸插け鏁�"),
+    AMORTIZED(3, "宸叉憡閿�瀹�");
+
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(FinIntangibleAssetStatusEnum::getStatus).toArray(Integer[]::new);
+
+    /**
+     * 鐘舵��
+     */
+    private final Integer status;
+    /**
+     * 鐘舵�佸悕
+     */
+    private final String name;
+
+    @Override
+    public Integer[] array() {
+        return ARRAYS;
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinLedgerRowTypeEnum.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinLedgerRowTypeEnum.java
new file mode 100644
index 0000000..1f62437
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinLedgerRowTypeEnum.java
@@ -0,0 +1,37 @@
+package cn.iocoder.yudao.module.finance.enums;
+
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * 鎬昏处 / 鏄庣粏璐﹁绫诲瀷鏋氫妇
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@RequiredArgsConstructor
+@Getter
+public enum FinLedgerRowTypeEnum {
+
+    OPENING("opening", "鏈熷垵浣欓"),
+    ENTRY("entry", "鏄庣粏"),
+    MONTHLY_TOTAL("monthly_total", "鏈湀鍚堣"),
+    YEARLY_TOTAL("yearly_total", "鏈勾绱");
+
+    /**
+     * 琛岀被鍨�
+     */
+    private final String type;
+    /**
+     * 琛岀被鍨嬪悕
+     */
+    private final String name;
+
+    public static FinLedgerRowTypeEnum valueOfType(String type) {
+        return Arrays.stream(values()).filter(item -> Objects.equals(item.getType(), type))
+                .findFirst().orElse(null);
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinSubjectTypeEnum.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinSubjectTypeEnum.java
new file mode 100644
index 0000000..adfe490
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinSubjectTypeEnum.java
@@ -0,0 +1,40 @@
+package cn.iocoder.yudao.module.finance.enums;
+
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Arrays;
+
+/**
+ * 浼氳绉戠洰绫诲瀷鏋氫妇
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@RequiredArgsConstructor
+@Getter
+public enum FinSubjectTypeEnum implements ArrayValuable<Integer> {
+
+    ASSET(1, "璧勪骇绫�"),
+    LIABILITY(2, "璐熷�虹被"),
+    EQUITY(3, "鎵�鏈夎�呮潈鐩婄被"),
+    COST(4, "鎴愭湰绫�"),
+    PROFIT_LOSS(5, "鎹熺泭绫�");
+
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(FinSubjectTypeEnum::getType).toArray(Integer[]::new);
+
+    /**
+     * 绫诲瀷
+     */
+    private final Integer type;
+    /**
+     * 绫诲瀷鍚�
+     */
+    private final String name;
+
+    @Override
+    public Integer[] array() {
+        return ARRAYS;
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinVoucherStatusEnum.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinVoucherStatusEnum.java
new file mode 100644
index 0000000..31ef107
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/enums/FinVoucherStatusEnum.java
@@ -0,0 +1,51 @@
+package cn.iocoder.yudao.module.finance.enums;
+
+import cn.iocoder.yudao.framework.common.core.ArrayValuable;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * 鍑瘉鐘舵�佹灇涓�
+ *
+ * 鐘舵�佹祦杞細unposted锛堟湭杩囪处锛�-> posted锛堝凡杩囪处锛夛紱unposted锛堟湭杩囪处锛�-> cancelled锛堝凡浣滃簾锛�
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@RequiredArgsConstructor
+@Getter
+public enum FinVoucherStatusEnum implements ArrayValuable<Integer> {
+
+    UNPOSTED(0, "鏈繃璐�"),
+    POSTED(10, "宸茶繃璐�"),
+    CANCELLED(20, "宸蹭綔搴�");
+
+    public static final Integer[] ARRAYS = Arrays.stream(values()).map(FinVoucherStatusEnum::getStatus).toArray(Integer[]::new);
+
+    /**
+     * 鐘舵��
+     */
+    private final Integer status;
+    /**
+     * 鐘舵�佸悕
+     */
+    private final String name;
+
+    @Override
+    public Integer[] array() {
+        return ARRAYS;
+    }
+
+    public static FinVoucherStatusEnum valueOf(Integer status) {
+        return Arrays.stream(values()).filter(item -> Objects.equals(item.getStatus(), status))
+                .findFirst().orElse(null);
+    }
+
+    public static String getName(Integer status) {
+        FinVoucherStatusEnum statusEnum = valueOf(status);
+        return statusEnum == null ? String.valueOf(status) : statusEnum.getName();
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/framework/web/config/FinanceWebConfiguration.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/framework/web/config/FinanceWebConfiguration.java
new file mode 100644
index 0000000..e5601b6
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/framework/web/config/FinanceWebConfiguration.java
@@ -0,0 +1,24 @@
+package cn.iocoder.yudao.module.finance.framework.web.config;
+
+import cn.iocoder.yudao.framework.swagger.config.YudaoSwaggerAutoConfiguration;
+import org.springdoc.core.models.GroupedOpenApi;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * finance 妯″潡鐨� web 缁勪欢鐨� Configuration
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Configuration(proxyBeanMethods = false)
+public class FinanceWebConfiguration {
+
+    /**
+     * finance 妯″潡鐨� API 鍒嗙粍
+     */
+    @Bean
+    public GroupedOpenApi financeGroupedOpenApi() {
+        return YudaoSwaggerAutoConfiguration.buildGroupedOpenApi("finance");
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/asset/FinAssetAmountUtils.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/asset/FinAssetAmountUtils.java
new file mode 100644
index 0000000..4decaa3
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/asset/FinAssetAmountUtils.java
@@ -0,0 +1,53 @@
+package cn.iocoder.yudao.module.finance.service.asset;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+
+/**
+ * 鍥哄畾璧勪骇鎶樻棫 / 鏃犲舰璧勪骇鎽婇攢鐨勮绠楀伐鍏�
+ *
+ * 缁熶竴鍙e緞锛�
+ * 1. 搴旇鎻愭�婚锛堝熀鏁帮級 = 鍘熷�� 脳 (1 - 娈嬪�肩巼 / 100)
+ * 2. 鏈堟姌鏃ч / 鏈堟憡閿�棰� = 搴旇鎻愭�婚 / (骞撮檺 脳 12)锛屽洓鑸嶄簲鍏ヤ繚鐣� 2 浣嶅皬鏁�
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+public class FinAssetAmountUtils {
+
+    private FinAssetAmountUtils() {
+    }
+
+    /**
+     * 璁$畻搴旇鎻愭�婚锛堝熀鏁帮級
+     *
+     * @param originalValue 鍘熷��
+     * @param residualRate  娈嬪�肩巼锛�%锛�
+     * @return 搴旇鎻愭�婚锛屼繚鐣� 2 浣嶅皬鏁�
+     */
+    public static BigDecimal calcDepreciableBase(BigDecimal originalValue, BigDecimal residualRate) {
+        return originalValue.multiply(BigDecimal.ONE.subtract(toResidualRatio(residualRate)))
+                .setScale(2, RoundingMode.HALF_UP);
+    }
+
+    /**
+     * 璁$畻鏈堟姌鏃ч / 鏈堟憡閿�棰�
+     *
+     * @param originalValue 鍘熷��
+     * @param residualRate  娈嬪�肩巼锛�%锛�
+     * @param years         骞撮檺锛堝勾锛夛紝蹇呴』澶т簬 0
+     * @return 鏈堟姌鏃ч / 鏈堟憡閿�棰濓紝淇濈暀 2 浣嶅皬鏁�
+     */
+    public static BigDecimal calcMonthlyAmount(BigDecimal originalValue, BigDecimal residualRate, int years) {
+        BigDecimal base = originalValue.multiply(BigDecimal.ONE.subtract(toResidualRatio(residualRate)));
+        return base.divide(BigDecimal.valueOf(years * 12L), 2, RoundingMode.HALF_UP);
+    }
+
+    /**
+     * 娈嬪�肩巼锛�%锛夎浆鎴愯绠楃敤鐨勬瘮渚嬶紝渚嬪 5 -> 0.05
+     */
+    private static BigDecimal toResidualRatio(BigDecimal residualRate) {
+        BigDecimal rate = residualRate == null ? BigDecimal.ZERO : residualRate;
+        return rate.divide(BigDecimal.valueOf(100), 10, RoundingMode.HALF_UP);
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/asset/FinFixedAssetService.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/asset/FinFixedAssetService.java
new file mode 100644
index 0000000..1dabdbd
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/asset/FinFixedAssetService.java
@@ -0,0 +1,93 @@
+package cn.iocoder.yudao.module.finance.service.asset;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.finance.controller.admin.asset.vo.FinFixedAssetDepreciateRespVO;
+import cn.iocoder.yudao.module.finance.controller.admin.asset.vo.FinFixedAssetPageReqVO;
+import cn.iocoder.yudao.module.finance.controller.admin.asset.vo.FinFixedAssetSaveReqVO;
+import cn.iocoder.yudao.module.finance.dal.dataobject.asset.FinFixedAssetDO;
+import jakarta.validation.Valid;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 鍥哄畾璧勪骇 Service 鎺ュ彛
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+public interface FinFixedAssetService {
+
+    /**
+     * 鍒涘缓鍥哄畾璧勪骇
+     *
+     * @param createReqVO 鍒涘缓淇℃伅
+     * @return 璧勪骇缂栧彿
+     */
+    Long createFixedAsset(@Valid FinFixedAssetSaveReqVO createReqVO);
+
+    /**
+     * 鏇存柊鍥哄畾璧勪骇
+     *
+     * 淇敼鍘熷�笺�佷娇鐢ㄥ勾闄愩�佹畫鍊肩巼鏃朵細鎸夋柊鍙傛暟閲嶇畻绱鎶樻棫涓庡噣鍊�
+     *
+     * @param updateReqVO 鏇存柊淇℃伅
+     */
+    void updateFixedAsset(@Valid FinFixedAssetSaveReqVO updateReqVO);
+
+    /**
+     * 鍒犻櫎鍥哄畾璧勪骇
+     *
+     * @param id 璧勪骇缂栧彿
+     */
+    void deleteFixedAsset(Long id);
+
+    /**
+     * 鎵归噺鍒犻櫎鍥哄畾璧勪骇
+     *
+     * @param ids 璧勪骇缂栧彿鏁扮粍
+     */
+    void deleteFixedAssetList(List<Long> ids);
+
+    /**
+     * 璁℃彁鎶樻棫
+     *
+     * 姣忎釜璧勪骇璁℃彁涓�涓湀鐨勬姌鏃э細鏈堟姌鏃ч = 鍘熷�� 脳 (1 - 娈嬪�肩巼 / 100) / (浣跨敤骞撮檺 脳 12)銆�
+     * 绱鎶樻棫浠ュ簲璁℃彁鎬婚灏侀《锛屾渶鍚庝竴鏈熸寜鍓╀綑鍙姌鏃ч璁℃彁锛屽噣鍊间笉浼氳鎻愭垚璐熸暟
+     *
+     * @param ids 璧勪骇缂栧彿鏁扮粍锛涗负绌烘椂瀵规墍鏈夋湭鎶ュ簾鐨勫浐瀹氳祫浜ц鎻�
+     * @return 璁℃彁缁撴灉
+     */
+    FinFixedAssetDepreciateRespVO depreciateFixedAssetList(List<Long> ids);
+
+    /**
+     * 鑾峰緱鍥哄畾璧勪骇
+     *
+     * @param id 璧勪骇缂栧彿
+     * @return 鍥哄畾璧勪骇
+     */
+    FinFixedAssetDO getFixedAsset(Long id);
+
+    /**
+     * 鑾峰緱鍥哄畾璧勪骇鍒嗛〉
+     *
+     * @param pageReqVO 鍒嗛〉鏌ヨ
+     * @return 鍥哄畾璧勪骇鍒嗛〉
+     */
+    PageResult<FinFixedAssetDO> getFixedAssetPage(FinFixedAssetPageReqVO pageReqVO);
+
+    /**
+     * 鑾峰緱鎸囧畾缂栧彿鐨勫浐瀹氳祫浜у垪琛�
+     *
+     * @param ids 璧勪骇缂栧彿鏁扮粍
+     * @return 鍥哄畾璧勪骇鍒楄〃
+     */
+    List<FinFixedAssetDO> getFixedAssetList(Collection<Long> ids);
+
+    /**
+     * 鑾峰緱鍏ㄩ儴鍥哄畾璧勪骇鍒楄〃
+     *
+     * @return 鍥哄畾璧勪骇鍒楄〃
+     */
+    List<FinFixedAssetDO> getFixedAssetList();
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/asset/FinFixedAssetServiceImpl.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/asset/FinFixedAssetServiceImpl.java
new file mode 100644
index 0000000..ce61f6a
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/asset/FinFixedAssetServiceImpl.java
@@ -0,0 +1,203 @@
+package cn.iocoder.yudao.module.finance.service.asset;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.finance.controller.admin.asset.vo.FinFixedAssetDepreciateRespVO;
+import cn.iocoder.yudao.module.finance.controller.admin.asset.vo.FinFixedAssetPageReqVO;
+import cn.iocoder.yudao.module.finance.controller.admin.asset.vo.FinFixedAssetSaveReqVO;
+import cn.iocoder.yudao.module.finance.dal.dataobject.asset.FinFixedAssetDO;
+import cn.iocoder.yudao.module.finance.dal.mysql.asset.FinFixedAssetMapper;
+import cn.iocoder.yudao.module.finance.enums.FinFixedAssetStatusEnum;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.validation.annotation.Validated;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.finance.enums.ErrorCodeConstants.*;
+import static cn.iocoder.yudao.module.finance.service.asset.FinAssetAmountUtils.calcDepreciableBase;
+import static cn.iocoder.yudao.module.finance.service.asset.FinAssetAmountUtils.calcMonthlyAmount;
+
+/**
+ * 鍥哄畾璧勪骇 Service 瀹炵幇绫�
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Service
+@Validated
+public class FinFixedAssetServiceImpl implements FinFixedAssetService {
+
+    @Resource
+    private FinFixedAssetMapper fixedAssetMapper;
+
+    @Override
+    public Long createFixedAsset(FinFixedAssetSaveReqVO createReqVO) {
+        // 1. 鏍¢獙璧勪骇缂栧彿鍞竴
+        validateAssetCodeUnique(null, createReqVO.getAssetCode());
+        // 2. 鎻掑叆锛氭柊璧勪骇灏氭湭璁℃彁鎶樻棫
+        FinFixedAssetDO asset = BeanUtils.toBean(createReqVO, FinFixedAssetDO.class);
+        asset.setAssetCode(StrUtil.trim(asset.getAssetCode()));
+        asset.setResidualRate(ObjectUtil.defaultIfNull(asset.getResidualRate(), BigDecimal.ZERO));
+        asset.setStatus(ObjectUtil.defaultIfNull(asset.getStatus(), FinFixedAssetStatusEnum.IN_USE.getStatus()));
+        asset.setAccumulatedDepreciation(BigDecimal.ZERO);
+        fillNetValue(asset);
+        fixedAssetMapper.insert(asset);
+        return asset.getId();
+    }
+
+    @Override
+    public void updateFixedAsset(FinFixedAssetSaveReqVO updateReqVO) {
+        // 1.1 鏍¢獙瀛樺湪
+        FinFixedAssetDO oldAsset = validateFixedAssetExists(updateReqVO.getId());
+        // 1.2 鏍¢獙璧勪骇缂栧彿鍞竴
+        validateAssetCodeUnique(updateReqVO.getId(), updateReqVO.getAssetCode());
+
+        // 2.1 鏇存柊锛堢疮璁℃姌鏃т笉浠庡墠绔帴鏀讹紝娌跨敤搴撲腑鍊硷級
+        FinFixedAssetDO updateObj = BeanUtils.toBean(updateReqVO, FinFixedAssetDO.class);
+        updateObj.setAssetCode(StrUtil.trim(updateObj.getAssetCode()));
+        updateObj.setResidualRate(ObjectUtil.defaultIfNull(updateObj.getResidualRate(), BigDecimal.ZERO));
+        updateObj.setStatus(ObjectUtil.defaultIfNull(updateObj.getStatus(), oldAsset.getStatus()));
+        updateObj.setAccumulatedDepreciation(ObjectUtil.defaultIfNull(oldAsset.getAccumulatedDepreciation(), BigDecimal.ZERO));
+        // 2.2 鍘熷�笺�佹畫鍊肩巼鍙樺寲鍚庯紝鎸夋柊鐨勫簲璁℃彁鎬婚灏侀《绱鎶樻棫锛屽苟閲嶇畻鍑�鍊�
+        BigDecimal depreciableBase = calcDepreciableBase(defaultIfNull(updateObj.getOriginalValue()),
+                updateObj.getResidualRate());
+        if (updateObj.getAccumulatedDepreciation().compareTo(depreciableBase) > 0) {
+            updateObj.setAccumulatedDepreciation(depreciableBase);
+        }
+        fillNetValue(updateObj);
+        fixedAssetMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteFixedAsset(Long id) {
+        validateFixedAssetExists(id);
+        fixedAssetMapper.deleteById(id);
+    }
+
+    @Override
+    public void deleteFixedAssetList(List<Long> ids) {
+        if (CollUtil.isEmpty(ids)) {
+            return;
+        }
+        fixedAssetMapper.deleteByIds(ids);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public FinFixedAssetDepreciateRespVO depreciateFixedAssetList(List<Long> ids) {
+        // 1. 鏌ヨ寰呰鎻愮殑璧勪骇锛氬凡鎶ュ簾鐨勪笉鍐嶈鎻�
+        List<FinFixedAssetDO> assets = CollUtil.isEmpty(ids)
+                ? fixedAssetMapper.selectList() : fixedAssetMapper.selectListByIds(ids);
+        List<FinFixedAssetDO> updateList = new ArrayList<>();
+        BigDecimal totalMonthlyDepreciation = BigDecimal.ZERO;
+        for (FinFixedAssetDO asset : assets) {
+            if (FinFixedAssetStatusEnum.SCRAPPED.getStatus().equals(asset.getStatus())) {
+                continue;
+            }
+            BigDecimal originalValue = defaultIfNull(asset.getOriginalValue());
+            BigDecimal residualRate = defaultIfNull(asset.getResidualRate());
+            int usefulLife = ObjectUtil.defaultIfNull(asset.getUsefulLife(), 0);
+            BigDecimal accumulated = defaultIfNull(asset.getAccumulatedDepreciation());
+            // 1.1 宸叉彁瓒崇殑涓嶅啀璁℃彁
+            BigDecimal depreciableBase = calcDepreciableBase(originalValue, residualRate);
+            BigDecimal remaining = depreciableBase.subtract(accumulated);
+            if (remaining.compareTo(BigDecimal.ZERO) <= 0) {
+                continue;
+            }
+            // 1.2 鍙傛暟寮傚父瀵艰嚧鏈堟姌鏃ч闈炴
+            BigDecimal monthlyDepreciation = usefulLife <= 0 ? BigDecimal.ZERO
+                    : calcMonthlyAmount(originalValue, residualRate, usefulLife);
+            if (monthlyDepreciation.compareTo(BigDecimal.ZERO) <= 0) {
+                throw exception(FIXED_ASSET_DEPRECIATE_NEGATIVE, asset.getAssetName(),
+                        formatAmount(originalValue), usefulLife, formatAmount(residualRate));
+            }
+            // 1.3 鏈�鍚庝竴鏈熸寜鍓╀綑鍙姌鏃ч灏侀《锛屽噣鍊间笉浼氳鎻愭垚璐熸暟
+            BigDecimal periodDepreciation = monthlyDepreciation.min(remaining);
+            BigDecimal newAccumulated = accumulated.add(periodDepreciation);
+            updateList.add(new FinFixedAssetDO().setId(asset.getId())
+                    .setAccumulatedDepreciation(newAccumulated)
+                    .setNetValue(originalValue.subtract(newAccumulated).max(BigDecimal.ZERO)));
+            totalMonthlyDepreciation = totalMonthlyDepreciation.add(periodDepreciation);
+        }
+        // 2. 娌℃湁鍙鎻愮殑璧勪骇锛屾槑纭憡鐭�
+        if (updateList.isEmpty()) {
+            throw exception(FIXED_ASSET_DEPRECIATE_EMPTY);
+        }
+        // 3. 鎵归噺鏇存柊
+        fixedAssetMapper.updateBatch(updateList);
+        return new FinFixedAssetDepreciateRespVO().setProcessedCount(updateList.size())
+                .setTotalMonthlyDepreciation(totalMonthlyDepreciation);
+    }
+
+    @Override
+    public FinFixedAssetDO getFixedAsset(Long id) {
+        return fixedAssetMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<FinFixedAssetDO> getFixedAssetPage(FinFixedAssetPageReqVO pageReqVO) {
+        return fixedAssetMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public List<FinFixedAssetDO> getFixedAssetList(Collection<Long> ids) {
+        if (CollUtil.isEmpty(ids)) {
+            return Collections.emptyList();
+        }
+        return fixedAssetMapper.selectListByIds(ids);
+    }
+
+    @Override
+    public List<FinFixedAssetDO> getFixedAssetList() {
+        return fixedAssetMapper.selectList();
+    }
+
+    private FinFixedAssetDO validateFixedAssetExists(Long id) {
+        if (id == null) {
+            throw exception(FIXED_ASSET_NOT_EXISTS);
+        }
+        FinFixedAssetDO asset = fixedAssetMapper.selectById(id);
+        if (asset == null) {
+            throw exception(FIXED_ASSET_NOT_EXISTS);
+        }
+        return asset;
+    }
+
+    private void validateAssetCodeUnique(Long id, String assetCode) {
+        FinFixedAssetDO asset = fixedAssetMapper.selectByAssetCode(StrUtil.trim(assetCode));
+        if (asset == null) {
+            return;
+        }
+        if (id == null || !asset.getId().equals(id)) {
+            throw exception(FIXED_ASSET_CODE_DUPLICATE, assetCode);
+        }
+    }
+
+    /**
+     * 鍑�鍊� = 鍘熷�� - 绱鎶樻棫锛屼笖涓嶅皬浜� 0
+     */
+    private void fillNetValue(FinFixedAssetDO asset) {
+        BigDecimal netValue = defaultIfNull(asset.getOriginalValue())
+                .subtract(defaultIfNull(asset.getAccumulatedDepreciation()));
+        asset.setNetValue(netValue.max(BigDecimal.ZERO));
+    }
+
+    private static BigDecimal defaultIfNull(BigDecimal amount) {
+        return amount == null ? BigDecimal.ZERO : amount;
+    }
+
+    private static String formatAmount(BigDecimal amount) {
+        return amount.setScale(2, RoundingMode.HALF_UP).toPlainString();
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/asset/FinIntangibleAssetService.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/asset/FinIntangibleAssetService.java
new file mode 100644
index 0000000..b8e0161
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/asset/FinIntangibleAssetService.java
@@ -0,0 +1,94 @@
+package cn.iocoder.yudao.module.finance.service.asset;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.finance.controller.admin.asset.vo.FinIntangibleAssetAmortizeRespVO;
+import cn.iocoder.yudao.module.finance.controller.admin.asset.vo.FinIntangibleAssetPageReqVO;
+import cn.iocoder.yudao.module.finance.controller.admin.asset.vo.FinIntangibleAssetSaveReqVO;
+import cn.iocoder.yudao.module.finance.dal.dataobject.asset.FinIntangibleAssetDO;
+import jakarta.validation.Valid;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 鏃犲舰璧勪骇 Service 鎺ュ彛
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+public interface FinIntangibleAssetService {
+
+    /**
+     * 鍒涘缓鏃犲舰璧勪骇
+     *
+     * @param createReqVO 鍒涘缓淇℃伅
+     * @return 璧勪骇缂栧彿
+     */
+    Long createIntangibleAsset(@Valid FinIntangibleAssetSaveReqVO createReqVO);
+
+    /**
+     * 鏇存柊鏃犲舰璧勪骇
+     *
+     * 淇敼鍘熷�笺�佹憡閿�骞撮檺銆佹畫鍊肩巼鏃朵細鎸夋柊鍙傛暟閲嶇畻绱鎽婇攢涓庡噣鍊�
+     *
+     * @param updateReqVO 鏇存柊淇℃伅
+     */
+    void updateIntangibleAsset(@Valid FinIntangibleAssetSaveReqVO updateReqVO);
+
+    /**
+     * 鍒犻櫎鏃犲舰璧勪骇
+     *
+     * @param id 璧勪骇缂栧彿
+     */
+    void deleteIntangibleAsset(Long id);
+
+    /**
+     * 鎵归噺鍒犻櫎鏃犲舰璧勪骇
+     *
+     * @param ids 璧勪骇缂栧彿鏁扮粍
+     */
+    void deleteIntangibleAssetList(List<Long> ids);
+
+    /**
+     * 璁℃彁鎽婇攢
+     *
+     * 姣忎釜璧勪骇璁℃彁涓�涓湀鐨勬憡閿�锛氭湀鎽婇攢棰� = 鍘熷�� 脳 (1 - 娈嬪�肩巼 / 100) / (鎽婇攢骞撮檺 脳 12)銆�
+     * 绱鎽婇攢浠ュ簲鎽婇攢鎬婚灏侀《锛屾渶鍚庝竴鏈熸寜鍓╀綑鍙憡閿�棰濊鎻愶紱
+     * 鍑�鍊兼憡瀹岋紙涓� 0锛夋椂鐘舵�佽嚜鍔ㄧ疆涓恒�屽凡鎽婇攢瀹屻��
+     *
+     * @param ids 璧勪骇缂栧彿鏁扮粍锛涗负绌烘椂瀵规墍鏈夋湭澶辨晥鐨勬棤褰㈣祫浜ц鎻�
+     * @return 璁℃彁缁撴灉
+     */
+    FinIntangibleAssetAmortizeRespVO amortizeIntangibleAssetList(List<Long> ids);
+
+    /**
+     * 鑾峰緱鏃犲舰璧勪骇
+     *
+     * @param id 璧勪骇缂栧彿
+     * @return 鏃犲舰璧勪骇
+     */
+    FinIntangibleAssetDO getIntangibleAsset(Long id);
+
+    /**
+     * 鑾峰緱鏃犲舰璧勪骇鍒嗛〉
+     *
+     * @param pageReqVO 鍒嗛〉鏌ヨ
+     * @return 鏃犲舰璧勪骇鍒嗛〉
+     */
+    PageResult<FinIntangibleAssetDO> getIntangibleAssetPage(FinIntangibleAssetPageReqVO pageReqVO);
+
+    /**
+     * 鑾峰緱鎸囧畾缂栧彿鐨勬棤褰㈣祫浜у垪琛�
+     *
+     * @param ids 璧勪骇缂栧彿鏁扮粍
+     * @return 鏃犲舰璧勪骇鍒楄〃
+     */
+    List<FinIntangibleAssetDO> getIntangibleAssetList(Collection<Long> ids);
+
+    /**
+     * 鑾峰緱鍏ㄩ儴鏃犲舰璧勪骇鍒楄〃
+     *
+     * @return 鏃犲舰璧勪骇鍒楄〃
+     */
+    List<FinIntangibleAssetDO> getIntangibleAssetList();
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/asset/FinIntangibleAssetServiceImpl.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/asset/FinIntangibleAssetServiceImpl.java
new file mode 100644
index 0000000..2a34ceb
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/asset/FinIntangibleAssetServiceImpl.java
@@ -0,0 +1,215 @@
+package cn.iocoder.yudao.module.finance.service.asset;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.finance.controller.admin.asset.vo.FinIntangibleAssetAmortizeRespVO;
+import cn.iocoder.yudao.module.finance.controller.admin.asset.vo.FinIntangibleAssetPageReqVO;
+import cn.iocoder.yudao.module.finance.controller.admin.asset.vo.FinIntangibleAssetSaveReqVO;
+import cn.iocoder.yudao.module.finance.dal.dataobject.asset.FinIntangibleAssetDO;
+import cn.iocoder.yudao.module.finance.dal.mysql.asset.FinIntangibleAssetMapper;
+import cn.iocoder.yudao.module.finance.enums.FinIntangibleAssetStatusEnum;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.validation.annotation.Validated;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.finance.enums.ErrorCodeConstants.*;
+import static cn.iocoder.yudao.module.finance.service.asset.FinAssetAmountUtils.calcDepreciableBase;
+import static cn.iocoder.yudao.module.finance.service.asset.FinAssetAmountUtils.calcMonthlyAmount;
+
+/**
+ * 鏃犲舰璧勪骇 Service 瀹炵幇绫�
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Service
+@Validated
+public class FinIntangibleAssetServiceImpl implements FinIntangibleAssetService {
+
+    @Resource
+    private FinIntangibleAssetMapper intangibleAssetMapper;
+
+    @Override
+    public Long createIntangibleAsset(FinIntangibleAssetSaveReqVO createReqVO) {
+        // 1. 鏍¢獙璧勪骇缂栧彿鍞竴
+        validateAssetCodeUnique(null, createReqVO.getAssetCode());
+        // 2. 鎻掑叆锛氭柊璧勪骇灏氭湭璁℃彁鎽婇攢
+        FinIntangibleAssetDO asset = BeanUtils.toBean(createReqVO, FinIntangibleAssetDO.class);
+        asset.setAssetCode(StrUtil.trim(asset.getAssetCode()));
+        asset.setResidualRate(ObjectUtil.defaultIfNull(asset.getResidualRate(), BigDecimal.ZERO));
+        asset.setStatus(ObjectUtil.defaultIfNull(asset.getStatus(), FinIntangibleAssetStatusEnum.IN_USE.getStatus()));
+        asset.setAccumulatedAmortization(BigDecimal.ZERO);
+        fillNetValue(asset);
+        intangibleAssetMapper.insert(asset);
+        return asset.getId();
+    }
+
+    @Override
+    public void updateIntangibleAsset(FinIntangibleAssetSaveReqVO updateReqVO) {
+        // 1.1 鏍¢獙瀛樺湪
+        FinIntangibleAssetDO oldAsset = validateIntangibleAssetExists(updateReqVO.getId());
+        // 1.2 鏍¢獙璧勪骇缂栧彿鍞竴
+        validateAssetCodeUnique(updateReqVO.getId(), updateReqVO.getAssetCode());
+
+        // 2.1 鏇存柊锛堢疮璁℃憡閿�涓嶄粠鍓嶇鎺ユ敹锛屾部鐢ㄥ簱涓�硷級
+        FinIntangibleAssetDO updateObj = BeanUtils.toBean(updateReqVO, FinIntangibleAssetDO.class);
+        updateObj.setAssetCode(StrUtil.trim(updateObj.getAssetCode()));
+        updateObj.setResidualRate(ObjectUtil.defaultIfNull(updateObj.getResidualRate(), BigDecimal.ZERO));
+        updateObj.setStatus(ObjectUtil.defaultIfNull(updateObj.getStatus(), oldAsset.getStatus()));
+        updateObj.setAccumulatedAmortization(
+                ObjectUtil.defaultIfNull(oldAsset.getAccumulatedAmortization(), BigDecimal.ZERO));
+        // 2.2 鍘熷�笺�佹畫鍊肩巼鍙樺寲鍚庯紝鎸夋柊鐨勫簲鎽婇攢鎬婚灏侀《绱鎽婇攢锛屽苟閲嶇畻鍑�鍊�
+        BigDecimal amortizableBase = calcDepreciableBase(defaultIfNull(updateObj.getOriginalValue()),
+                updateObj.getResidualRate());
+        if (updateObj.getAccumulatedAmortization().compareTo(amortizableBase) > 0) {
+            updateObj.setAccumulatedAmortization(amortizableBase);
+        }
+        fillNetValue(updateObj);
+        // 2.3 宸叉憡瀹屼絾鐘舵�佽繕鍦ㄧ敤鐨勶紝鍚屾涓恒�屽凡鎽婇攢瀹屻��
+        if (updateObj.getNetValue().compareTo(BigDecimal.ZERO) <= 0
+                && FinIntangibleAssetStatusEnum.IN_USE.getStatus().equals(updateObj.getStatus())) {
+            updateObj.setStatus(FinIntangibleAssetStatusEnum.AMORTIZED.getStatus());
+        }
+        intangibleAssetMapper.updateById(updateObj);
+    }
+
+    @Override
+    public void deleteIntangibleAsset(Long id) {
+        validateIntangibleAssetExists(id);
+        intangibleAssetMapper.deleteById(id);
+    }
+
+    @Override
+    public void deleteIntangibleAssetList(List<Long> ids) {
+        if (CollUtil.isEmpty(ids)) {
+            return;
+        }
+        intangibleAssetMapper.deleteByIds(ids);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public FinIntangibleAssetAmortizeRespVO amortizeIntangibleAssetList(List<Long> ids) {
+        // 1. 鏌ヨ寰呰鎻愮殑璧勪骇锛氬凡澶辨晥鐨勪笉鍐嶈鎻�
+        List<FinIntangibleAssetDO> assets = CollUtil.isEmpty(ids)
+                ? intangibleAssetMapper.selectList() : intangibleAssetMapper.selectListByIds(ids);
+        List<FinIntangibleAssetDO> updateList = new ArrayList<>();
+        BigDecimal totalMonthlyAmortization = BigDecimal.ZERO;
+        for (FinIntangibleAssetDO asset : assets) {
+            if (FinIntangibleAssetStatusEnum.EXPIRED.getStatus().equals(asset.getStatus())) {
+                continue;
+            }
+            BigDecimal originalValue = defaultIfNull(asset.getOriginalValue());
+            BigDecimal residualRate = defaultIfNull(asset.getResidualRate());
+            int years = ObjectUtil.defaultIfNull(asset.getAmortizationPeriod(), 0);
+            BigDecimal accumulated = defaultIfNull(asset.getAccumulatedAmortization());
+            // 1.1 宸叉憡瀹岀殑涓嶅啀璁℃彁
+            BigDecimal amortizableBase = calcDepreciableBase(originalValue, residualRate);
+            BigDecimal remaining = amortizableBase.subtract(accumulated);
+            if (remaining.compareTo(BigDecimal.ZERO) <= 0) {
+                continue;
+            }
+            // 1.2 鍙傛暟寮傚父瀵艰嚧鏈堟憡閿�棰濋潪姝�
+            BigDecimal monthlyAmortization = years <= 0 ? BigDecimal.ZERO
+                    : calcMonthlyAmount(originalValue, residualRate, years);
+            if (monthlyAmortization.compareTo(BigDecimal.ZERO) <= 0) {
+                throw exception(INTANGIBLE_ASSET_AMORTIZE_NEGATIVE, asset.getAssetName(),
+                        formatAmount(originalValue), years, formatAmount(residualRate));
+            }
+            // 1.3 鏈�鍚庝竴鏈熸寜鍓╀綑鍙憡閿�棰濆皝椤讹紝鍑�鍊间笉浼氳鎽婃垚璐熸暟
+            BigDecimal periodAmortization = monthlyAmortization.min(remaining);
+            BigDecimal newAccumulated = accumulated.add(periodAmortization);
+            BigDecimal newNetValue = originalValue.subtract(newAccumulated).max(BigDecimal.ZERO);
+            FinIntangibleAssetDO updateObj = new FinIntangibleAssetDO().setId(asset.getId())
+                    .setAccumulatedAmortization(newAccumulated)
+                    .setNetValue(newNetValue);
+            // 1.4 鎽婂畬鏃惰嚜鍔ㄧ疆涓恒�屽凡鎽婇攢瀹屻��
+            if (newNetValue.compareTo(BigDecimal.ZERO) <= 0) {
+                updateObj.setStatus(FinIntangibleAssetStatusEnum.AMORTIZED.getStatus());
+            }
+            updateList.add(updateObj);
+            totalMonthlyAmortization = totalMonthlyAmortization.add(periodAmortization);
+        }
+        // 2. 娌℃湁鍙鎻愮殑璧勪骇锛屾槑纭憡鐭�
+        if (updateList.isEmpty()) {
+            throw exception(INTANGIBLE_ASSET_AMORTIZE_EMPTY);
+        }
+        // 3. 鎵归噺鏇存柊
+        intangibleAssetMapper.updateBatch(updateList);
+        return new FinIntangibleAssetAmortizeRespVO().setProcessedCount(updateList.size())
+                .setTotalMonthlyAmortization(totalMonthlyAmortization);
+    }
+
+    @Override
+    public FinIntangibleAssetDO getIntangibleAsset(Long id) {
+        return intangibleAssetMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<FinIntangibleAssetDO> getIntangibleAssetPage(FinIntangibleAssetPageReqVO pageReqVO) {
+        return intangibleAssetMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public List<FinIntangibleAssetDO> getIntangibleAssetList(Collection<Long> ids) {
+        if (CollUtil.isEmpty(ids)) {
+            return Collections.emptyList();
+        }
+        return intangibleAssetMapper.selectListByIds(ids);
+    }
+
+    @Override
+    public List<FinIntangibleAssetDO> getIntangibleAssetList() {
+        return intangibleAssetMapper.selectList();
+    }
+
+    private FinIntangibleAssetDO validateIntangibleAssetExists(Long id) {
+        if (id == null) {
+            throw exception(INTANGIBLE_ASSET_NOT_EXISTS);
+        }
+        FinIntangibleAssetDO asset = intangibleAssetMapper.selectById(id);
+        if (asset == null) {
+            throw exception(INTANGIBLE_ASSET_NOT_EXISTS);
+        }
+        return asset;
+    }
+
+    private void validateAssetCodeUnique(Long id, String assetCode) {
+        FinIntangibleAssetDO asset = intangibleAssetMapper.selectByAssetCode(StrUtil.trim(assetCode));
+        if (asset == null) {
+            return;
+        }
+        if (id == null || !asset.getId().equals(id)) {
+            throw exception(INTANGIBLE_ASSET_CODE_DUPLICATE, assetCode);
+        }
+    }
+
+    /**
+     * 鍑�鍊� = 鍘熷�� - 绱鎽婇攢锛屼笖涓嶅皬浜� 0
+     */
+    private void fillNetValue(FinIntangibleAssetDO asset) {
+        BigDecimal netValue = defaultIfNull(asset.getOriginalValue())
+                .subtract(defaultIfNull(asset.getAccumulatedAmortization()));
+        asset.setNetValue(netValue.max(BigDecimal.ZERO));
+    }
+
+    private static BigDecimal defaultIfNull(BigDecimal amount) {
+        return amount == null ? BigDecimal.ZERO : amount;
+    }
+
+    private static String formatAmount(BigDecimal amount) {
+        return amount.setScale(2, RoundingMode.HALF_UP).toPlainString();
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/ledger/FinLedgerService.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/ledger/FinLedgerService.java
new file mode 100644
index 0000000..c368a8a
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/ledger/FinLedgerService.java
@@ -0,0 +1,36 @@
+package cn.iocoder.yudao.module.finance.service.ledger;
+
+import cn.iocoder.yudao.module.finance.controller.admin.ledger.vo.FinLedgerReqVO;
+import cn.iocoder.yudao.module.finance.controller.admin.ledger.vo.FinLedgerRespVO;
+import jakarta.validation.Valid;
+
+import java.util.List;
+
+/**
+ * 鎬昏处 / 鏄庣粏璐� Service 鎺ュ彛
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+public interface FinLedgerService {
+
+    /**
+     * 鑾峰緱鎬昏处鍒楄〃
+     *
+     * 鎸夋湡闂撮�愭湀姹囨�伙細鏈熷垵浣欓 -> 鍚勬湀銆屾湰鏈堝悎璁°�� -> 鏈湡绱
+     *
+     * @param reqVO 鏌ヨ鏉′欢
+     * @return 鎬昏处琛屽垪琛�
+     */
+    List<FinLedgerRespVO> getGeneralLedgerList(@Valid FinLedgerReqVO reqVO);
+
+    /**
+     * 鑾峰緱鏄庣粏璐﹀垪琛�
+     *
+     * 閫愭潯鍒嗗綍灞曠ず骞剁粨杞綑棰濓細鏈熷垵浣欓 -> 閫愭潯鏄庣粏锛堟瘡閫㈡湀鏈彃銆屾湰鏈堝悎璁°�嶏級 -> 鏈湡绱
+     *
+     * @param reqVO 鏌ヨ鏉′欢
+     * @return 鏄庣粏璐﹁鍒楄〃
+     */
+    List<FinLedgerRespVO> getDetailLedgerList(@Valid FinLedgerReqVO reqVO);
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/ledger/FinLedgerServiceImpl.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/ledger/FinLedgerServiceImpl.java
new file mode 100644
index 0000000..1f1ee7c
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/ledger/FinLedgerServiceImpl.java
@@ -0,0 +1,227 @@
+package cn.iocoder.yudao.module.finance.service.ledger;
+
+import cn.iocoder.yudao.module.finance.controller.admin.ledger.vo.FinLedgerReqVO;
+import cn.iocoder.yudao.module.finance.controller.admin.ledger.vo.FinLedgerRespVO;
+import cn.iocoder.yudao.module.finance.dal.dataobject.ledger.FinLedgerEntryDO;
+import cn.iocoder.yudao.module.finance.dal.dataobject.subject.FinAccountSubjectDO;
+import cn.iocoder.yudao.module.finance.dal.mysql.ledger.FinLedgerMapper;
+import cn.iocoder.yudao.module.finance.enums.FinBalanceDirectionEnum;
+import cn.iocoder.yudao.module.finance.enums.FinLedgerRowTypeEnum;
+import cn.iocoder.yudao.module.finance.enums.FinVoucherStatusEnum;
+import cn.iocoder.yudao.module.finance.service.subject.FinAccountSubjectService;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.YearMonth;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.finance.enums.ErrorCodeConstants.LEDGER_MONTH_RANGE_INVALID;
+import static cn.iocoder.yudao.module.finance.enums.ErrorCodeConstants.LEDGER_SUBJECT_NOT_EXISTS;
+
+/**
+ * 鎬昏处 / 鏄庣粏璐� Service 瀹炵幇绫�
+ *
+ * 鍙e緞璇存槑锛�
+ * 1. 鍙粺璁″凡杩囪处锛坧osted锛夌殑鍑瘉锛屾湭杩囪处銆佸凡浣滃簾鐨勫嚟璇佷笉璁″叆璐︾翱锛�
+ * 2. 浣欓涓�寰嬨�屽�熸柟姝c�佽捶鏂硅礋銆嶏紝鏈熷垵浣欓 = 璧峰鏈堜唤涔嬪墠鎵�鏈夊凡杩囪处鍒嗗綍鐨勫�熻捶鍑�棰濓紱
+ * 3. 绉戠洰鐨勪笅绾х鐩敱 parentId 閫掑綊鏀堕泦锛堜笉浣跨敤绉戠洰缂栫爜鍓嶇紑鍖归厤锛岄伩鍏嶆妸褰㈣繎缂栫爜璇畻杩涙潵锛夈��
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Service
+@Validated
+public class FinLedgerServiceImpl implements FinLedgerService {
+
+    @Resource
+    private FinLedgerMapper ledgerMapper;
+    @Resource
+    private FinAccountSubjectService accountSubjectService;
+
+    @Override
+    public List<FinLedgerRespVO> getGeneralLedgerList(FinLedgerReqVO reqVO) {
+        // 1. 鏍¢獙绉戠洰涓庢湡闂�
+        FinAccountSubjectDO subject = validateSubject(reqVO);
+        LocalDate startDate = reqVO.getStartMonth().atDay(1);
+        LocalDate endDate = reqVO.getEndMonth().atEndOfMonth();
+        validateMonthRange(startDate, endDate);
+        Set<String> subjectCodes = buildSubjectCodeSet(subject, reqVO.getIncludeChildren());
+
+        // 2. 鏈熷垵浣欓
+        List<FinLedgerRespVO> rows = new ArrayList<>();
+        BigDecimal balance = defaultIfNull(ledgerMapper.selectBalanceBeforeDate(subjectCodes, startDate,
+                FinVoucherStatusEnum.POSTED.getStatus()));
+        rows.add(buildOpeningRow(subject, startDate, balance));
+
+        // 3. 鎸夋湀姹囨��
+        List<FinLedgerEntryDO> entries = ledgerMapper.selectEntryList(subjectCodes, startDate, endDate,
+                FinVoucherStatusEnum.POSTED.getStatus());
+        Map<String, List<FinLedgerEntryDO>> monthMap = entries.stream()
+                .collect(Collectors.groupingBy(entry -> YearMonth.from(entry.getVoucherDate()).toString()));
+        BigDecimal totalDebit = BigDecimal.ZERO;
+        BigDecimal totalCredit = BigDecimal.ZERO;
+        YearMonth endMonth = YearMonth.from(endDate);
+        for (YearMonth month = YearMonth.from(startDate); !month.isAfter(endMonth); month = month.plusMonths(1)) {
+            List<FinLedgerEntryDO> monthEntries = monthMap.getOrDefault(month.toString(), List.of());
+            BigDecimal debit = sum(monthEntries, true);
+            BigDecimal credit = sum(monthEntries, false);
+            balance = balance.add(debit).subtract(credit);
+            totalDebit = totalDebit.add(debit);
+            totalCredit = totalCredit.add(credit);
+            LocalDate monthEndDate = minDate(month.atEndOfMonth(), endDate);
+            rows.add(buildRow(FinLedgerRowTypeEnum.MONTHLY_TOTAL, month.toString(), monthEndDate, subject, null,
+                    FinLedgerRowTypeEnum.MONTHLY_TOTAL.getName(), debit, credit, balance));
+        }
+
+        // 4. 鏈湡绱
+        rows.add(buildRow(FinLedgerRowTypeEnum.YEARLY_TOTAL, null, endDate, subject, null,
+                FinLedgerRowTypeEnum.YEARLY_TOTAL.getName(), totalDebit, totalCredit, balance));
+        return rows;
+    }
+
+    @Override
+    public List<FinLedgerRespVO> getDetailLedgerList(FinLedgerReqVO reqVO) {
+        // 1. 鏍¢獙绉戠洰涓庢湡闂�
+        FinAccountSubjectDO subject = validateSubject(reqVO);
+        LocalDate startDate = reqVO.getStartMonth().atDay(1);
+        LocalDate endDate = reqVO.getEndMonth().atEndOfMonth();
+        validateMonthRange(startDate, endDate);
+        Set<String> subjectCodes = buildSubjectCodeSet(subject, reqVO.getIncludeChildren());
+
+        // 2. 鏈熷垵浣欓
+        List<FinLedgerRespVO> rows = new ArrayList<>();
+        BigDecimal balance = defaultIfNull(ledgerMapper.selectBalanceBeforeDate(subjectCodes, startDate,
+                FinVoucherStatusEnum.POSTED.getStatus()));
+        rows.add(buildOpeningRow(subject, startDate, balance));
+
+        // 3. 閫愭潯鏄庣粏锛屽苟瀵规瘡鏉″垎褰曠粨杞綑棰濓紱姣忛�㈡湀鏈ˉ涓�琛屻�屾湰鏈堝悎璁°��
+        List<FinLedgerEntryDO> entries = ledgerMapper.selectEntryList(subjectCodes, startDate, endDate,
+                FinVoucherStatusEnum.POSTED.getStatus());
+        BigDecimal totalDebit = BigDecimal.ZERO;
+        BigDecimal totalCredit = BigDecimal.ZERO;
+        BigDecimal monthDebit = BigDecimal.ZERO;
+        BigDecimal monthCredit = BigDecimal.ZERO;
+        YearMonth currentMonth = null;
+        for (FinLedgerEntryDO entry : entries) {
+            YearMonth month = YearMonth.from(entry.getVoucherDate());
+            // 3.1 璺ㄦ湀锛氬厛琛ヤ笂涓湀鐨勬湰鏈堝悎璁�
+            if (currentMonth != null && !currentMonth.equals(month)) {
+                rows.add(buildRow(FinLedgerRowTypeEnum.MONTHLY_TOTAL, currentMonth.toString(),
+                        currentMonth.atEndOfMonth(), subject, null, FinLedgerRowTypeEnum.MONTHLY_TOTAL.getName(),
+                        monthDebit, monthCredit, balance));
+                monthDebit = BigDecimal.ZERO;
+                monthCredit = BigDecimal.ZERO;
+            }
+            currentMonth = month;
+            // 3.2 鏄庣粏琛�
+            BigDecimal debit = defaultIfNull(entry.getDebit());
+            BigDecimal credit = defaultIfNull(entry.getCredit());
+            balance = balance.add(debit).subtract(credit);
+            monthDebit = monthDebit.add(debit);
+            monthCredit = monthCredit.add(credit);
+            totalDebit = totalDebit.add(debit);
+            totalCredit = totalCredit.add(credit);
+            rows.add(buildRow(FinLedgerRowTypeEnum.ENTRY, month.toString(), entry.getVoucherDate(), subject,
+                    entry.getVoucherNo(), entry.getSummary(), debit, credit, balance)
+                    .setSubjectCode(entry.getSubjectCode()).setSubjectName(entry.getSubjectName()));
+        }
+
+        // 4. 鏈�鍚庝竴涓湀鐨勬湰鏈堝悎璁�
+        if (currentMonth != null) {
+            rows.add(buildRow(FinLedgerRowTypeEnum.MONTHLY_TOTAL, currentMonth.toString(),
+                    minDate(currentMonth.atEndOfMonth(), endDate), subject, null,
+                    FinLedgerRowTypeEnum.MONTHLY_TOTAL.getName(), monthDebit, monthCredit, balance));
+        }
+        // 5. 鏈湡绱
+        rows.add(buildRow(FinLedgerRowTypeEnum.YEARLY_TOTAL, null, endDate, subject, null,
+                FinLedgerRowTypeEnum.YEARLY_TOTAL.getName(), totalDebit, totalCredit, balance));
+        return rows;
+    }
+
+    private FinAccountSubjectDO validateSubject(FinLedgerReqVO reqVO) {
+        FinAccountSubjectDO subject = accountSubjectService.getAccountSubjectByCode(reqVO.getSubjectCode());
+        if (subject == null) {
+            throw exception(LEDGER_SUBJECT_NOT_EXISTS, reqVO.getSubjectCode());
+        }
+        return subject;
+    }
+
+    private void validateMonthRange(LocalDate startDate, LocalDate endDate) {
+        if (startDate.isAfter(endDate)) {
+            throw exception(LEDGER_MONTH_RANGE_INVALID, YearMonth.from(startDate), YearMonth.from(endDate));
+        }
+    }
+
+    /**
+     * 鏀堕泦鏌ヨ鐢ㄧ殑绉戠洰缂栫爜闆嗗悎锛氶粯璁ゅ寘鍚墍鏈変笅绾х鐩�
+     */
+    private Set<String> buildSubjectCodeSet(FinAccountSubjectDO subject, Boolean includeChildren) {
+        Set<String> subjectCodes = new HashSet<>();
+        subjectCodes.add(subject.getSubjectCode());
+        if (!Boolean.TRUE.equals(includeChildren)) {
+            return subjectCodes;
+        }
+        // 鐢� parentId 閫掑綊鏀堕泦涓嬬骇锛寁isited 鐢� subjectCodes 鐨� add 缁撴灉鍏间綔闃叉姢锛岄伩鍏嶈剰鏁版嵁鎴愮幆鏃舵寰幆
+        Map<Long, List<FinAccountSubjectDO>> childrenMap = accountSubjectService.getAccountSubjectList().stream()
+                .filter(item -> item.getParentId() != null)
+                .collect(Collectors.groupingBy(FinAccountSubjectDO::getParentId));
+        Deque<Long> queue = new ArrayDeque<>();
+        queue.add(subject.getId());
+        while (!queue.isEmpty()) {
+            for (FinAccountSubjectDO child : childrenMap.getOrDefault(queue.poll(), List.of())) {
+                if (subjectCodes.add(child.getSubjectCode())) {
+                    queue.add(child.getId());
+                }
+            }
+        }
+        return subjectCodes;
+    }
+
+    private FinLedgerRespVO buildOpeningRow(FinAccountSubjectDO subject, LocalDate date, BigDecimal balance) {
+        return buildRow(FinLedgerRowTypeEnum.OPENING, YearMonth.from(date).toString(), date, subject, null,
+                FinLedgerRowTypeEnum.OPENING.getName(), null, null, balance);
+    }
+
+    private FinLedgerRespVO buildRow(FinLedgerRowTypeEnum rowType, String period, LocalDate date,
+                                     FinAccountSubjectDO subject, String voucherNo, String summary,
+                                     BigDecimal debit, BigDecimal credit, BigDecimal balance) {
+        return new FinLedgerRespVO()
+                .setRowType(rowType.getType())
+                .setRowTypeName(rowType.getName())
+                .setPeriod(period)
+                .setDate(date)
+                .setVoucherNo(voucherNo)
+                .setSubjectCode(subject.getSubjectCode())
+                .setSubjectName(subject.getSubjectName())
+                .setSummary(summary)
+                .setDebit(debit)
+                .setCredit(credit)
+                .setDirection(FinBalanceDirectionEnum.getByBalance(balance))
+                .setBalance(balance);
+    }
+
+    private static BigDecimal sum(List<FinLedgerEntryDO> entries, boolean debit) {
+        return entries.stream()
+                .map(entry -> debit ? defaultIfNull(entry.getDebit()) : defaultIfNull(entry.getCredit()))
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+    }
+
+    private static BigDecimal defaultIfNull(BigDecimal amount) {
+        return amount == null ? BigDecimal.ZERO : amount;
+    }
+
+    private static LocalDate minDate(LocalDate date, LocalDate maxDate) {
+        return date.isAfter(maxDate) ? maxDate : date;
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/statistics/FinStatisticsService.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/statistics/FinStatisticsService.java
new file mode 100644
index 0000000..3ab9e15
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/statistics/FinStatisticsService.java
@@ -0,0 +1,39 @@
+package cn.iocoder.yudao.module.finance.service.statistics;
+
+import cn.iocoder.yudao.module.finance.controller.admin.statistics.vo.FinStatisticsOverviewRespVO;
+import cn.iocoder.yudao.module.finance.controller.admin.statistics.vo.FinStatisticsReqVO;
+import cn.iocoder.yudao.module.finance.controller.admin.statistics.vo.FinStatisticsTrendRespVO;
+import jakarta.validation.Valid;
+
+import java.util.List;
+
+/**
+ * 璐㈠姟鎶ヨ〃 / 浼氳鏍哥畻鐪嬫澘 Service 鎺ュ彛
+ *
+ * 鏁版嵁鏉ユ簮锛�
+ * 1. 鏀舵敮銆佽喘閿�閲戦鍙栬嚜 ERP 妯″潡锛堟敹娆惧崟銆佷粯娆惧崟銆侀攢鍞鍗曘�侀噰璐鍗曪級锛�
+ *    閫氳繃 {@link cn.iocoder.yudao.module.erp.api.finance.ErpFinanceStatisticsApi} 鍙栨暟锛屼笉璺ㄦā鍧楁煡琛紱
+ * 2. 璧勪骇鏁版嵁鍙栬嚜鏈ā鍧楃殑鍥哄畾璧勪骇銆佹棤褰㈣祫浜э紱
+ * 3. 鍑瘉鏁版嵁鍙栬嚜鏈ā鍧楃殑璁拌处鍑瘉銆�
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+public interface FinStatisticsService {
+
+    /**
+     * 鑾峰緱浼氳鏍哥畻鐪嬫澘鎬昏
+     *
+     * @param reqVO 鏌ヨ鏉′欢锛屾湡闂寸暀绌烘椂榛樿鏈勾 1 鏈堣嚦褰撳墠鏈�
+     * @return 鐪嬫澘鎬昏
+     */
+    FinStatisticsOverviewRespVO getStatisticsOverview(@Valid FinStatisticsReqVO reqVO);
+
+    /**
+     * 鑾峰緱鏈堝害鏀舵敮瓒嬪娍
+     *
+     * @param reqVO 鏌ヨ鏉′欢锛屾湡闂寸暀绌烘椂榛樿鏈勾 1 鏈堣嚦褰撳墠鏈�
+     * @return 鏈堝害瓒嬪娍鍒楄〃锛屾湡闂村唴娌℃湁鏁版嵁鐨勬湀浠借ˉ 0
+     */
+    List<FinStatisticsTrendRespVO> getStatisticsTrend(@Valid FinStatisticsReqVO reqVO);
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/statistics/FinStatisticsServiceImpl.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/statistics/FinStatisticsServiceImpl.java
new file mode 100644
index 0000000..6e82249
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/statistics/FinStatisticsServiceImpl.java
@@ -0,0 +1,182 @@
+package cn.iocoder.yudao.module.finance.service.statistics;
+
+import cn.iocoder.yudao.module.erp.api.finance.ErpFinanceStatisticsApi;
+import cn.iocoder.yudao.module.erp.api.finance.dto.ErpFinanceMonthlyStatisticsRespDTO;
+import cn.iocoder.yudao.module.finance.controller.admin.statistics.vo.FinStatisticsOverviewRespVO;
+import cn.iocoder.yudao.module.finance.controller.admin.statistics.vo.FinStatisticsReqVO;
+import cn.iocoder.yudao.module.finance.controller.admin.statistics.vo.FinStatisticsTrendRespVO;
+import cn.iocoder.yudao.module.finance.dal.dataobject.asset.FinFixedAssetDO;
+import cn.iocoder.yudao.module.finance.dal.dataobject.asset.FinIntangibleAssetDO;
+import cn.iocoder.yudao.module.finance.dal.mysql.subject.FinAccountSubjectMapper;
+import cn.iocoder.yudao.module.finance.dal.mysql.voucher.FinVoucherMapper;
+import cn.iocoder.yudao.module.finance.enums.FinVoucherStatusEnum;
+import cn.iocoder.yudao.module.finance.service.asset.FinFixedAssetService;
+import cn.iocoder.yudao.module.finance.service.asset.FinIntangibleAssetService;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.YearMonth;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
+import static cn.iocoder.yudao.module.finance.enums.ErrorCodeConstants.STATISTICS_PERIOD_INVALID;
+
+/**
+ * 璐㈠姟鎶ヨ〃 / 浼氳鏍哥畻鐪嬫澘 Service 瀹炵幇绫�
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Service
+@Validated
+public class FinStatisticsServiceImpl implements FinStatisticsService {
+
+    @Resource
+    private ErpFinanceStatisticsApi erpFinanceStatisticsApi;
+    @Resource
+    private FinVoucherMapper voucherMapper;
+    @Resource
+    private FinAccountSubjectMapper accountSubjectMapper;
+    @Resource
+    private FinFixedAssetService fixedAssetService;
+    @Resource
+    private FinIntangibleAssetService intangibleAssetService;
+
+    @Override
+    public FinStatisticsOverviewRespVO getStatisticsOverview(FinStatisticsReqVO reqVO) {
+        // 1. 鏈熼棿
+        LocalDate startDate = resolveStartDate(reqVO);
+        LocalDate endDate = resolveEndDate(reqVO);
+        validatePeriod(startDate, endDate);
+
+        // 2. ERP 鏀舵敮銆佽喘閿�閲戦
+        List<ErpFinanceMonthlyStatisticsRespDTO> monthlyList = getErpMonthlyStatistics(startDate, endDate);
+        BigDecimal receiptPrice = sumOf(monthlyList, ErpFinanceMonthlyStatisticsRespDTO::getReceiptPrice);
+        BigDecimal paymentPrice = sumOf(monthlyList, ErpFinanceMonthlyStatisticsRespDTO::getPaymentPrice);
+        BigDecimal saleOrderPrice = sumOf(monthlyList, ErpFinanceMonthlyStatisticsRespDTO::getSaleOrderPrice);
+        BigDecimal purchaseOrderPrice = sumOf(monthlyList, ErpFinanceMonthlyStatisticsRespDTO::getPurchaseOrderPrice);
+
+        // 3. 鍥哄畾璧勪骇銆佹棤褰㈣祫浜э紙鏃剁偣鏁帮紝鍙栧叏閮ㄨ祫浜э級
+        BigDecimal fixedAssetOriginalValue = BigDecimal.ZERO;
+        BigDecimal fixedAssetAccumulatedDepreciation = BigDecimal.ZERO;
+        BigDecimal fixedAssetNetValue = BigDecimal.ZERO;
+        for (FinFixedAssetDO asset : fixedAssetService.getFixedAssetList()) {
+            fixedAssetOriginalValue = fixedAssetOriginalValue.add(defaultIfNull(asset.getOriginalValue()));
+            fixedAssetAccumulatedDepreciation = fixedAssetAccumulatedDepreciation
+                    .add(defaultIfNull(asset.getAccumulatedDepreciation()));
+            fixedAssetNetValue = fixedAssetNetValue.add(defaultIfNull(asset.getNetValue()));
+        }
+        BigDecimal intangibleAssetOriginalValue = BigDecimal.ZERO;
+        BigDecimal intangibleAssetAccumulatedAmortization = BigDecimal.ZERO;
+        BigDecimal intangibleAssetNetValue = BigDecimal.ZERO;
+        for (FinIntangibleAssetDO asset : intangibleAssetService.getIntangibleAssetList()) {
+            intangibleAssetOriginalValue = intangibleAssetOriginalValue.add(defaultIfNull(asset.getOriginalValue()));
+            intangibleAssetAccumulatedAmortization = intangibleAssetAccumulatedAmortization
+                    .add(defaultIfNull(asset.getAccumulatedAmortization()));
+            intangibleAssetNetValue = intangibleAssetNetValue.add(defaultIfNull(asset.getNetValue()));
+        }
+
+        // 4. 鍑瘉缁熻
+        Long voucherUnpostedCount = defaultIfNull(voucherMapper.selectCountByStatus(FinVoucherStatusEnum.UNPOSTED.getStatus()));
+        Long voucherPostedCount = defaultIfNull(voucherMapper.selectCountByStatus(FinVoucherStatusEnum.POSTED.getStatus()));
+        Long voucherCancelledCount = defaultIfNull(voucherMapper.selectCountByStatus(FinVoucherStatusEnum.CANCELLED.getStatus()));
+
+        // 5. 缁勮
+        return new FinStatisticsOverviewRespVO()
+                .setStartDate(startDate).setEndDate(endDate)
+                .setReceiptPrice(receiptPrice)
+                .setPaymentPrice(paymentPrice)
+                .setNetProfit(receiptPrice.subtract(paymentPrice))
+                .setSaleOrderPrice(saleOrderPrice)
+                .setPurchaseOrderPrice(purchaseOrderPrice)
+                .setFixedAssetOriginalValue(fixedAssetOriginalValue)
+                .setFixedAssetAccumulatedDepreciation(fixedAssetAccumulatedDepreciation)
+                .setFixedAssetNetValue(fixedAssetNetValue)
+                .setIntangibleAssetOriginalValue(intangibleAssetOriginalValue)
+                .setIntangibleAssetAccumulatedAmortization(intangibleAssetAccumulatedAmortization)
+                .setIntangibleAssetNetValue(intangibleAssetNetValue)
+                .setTotalAssetNetValue(fixedAssetNetValue.add(intangibleAssetNetValue))
+                .setSubjectCount(defaultIfNull(accountSubjectMapper.selectCount()))
+                .setVoucherCount(voucherUnpostedCount + voucherPostedCount + voucherCancelledCount)
+                .setVoucherUnpostedCount(voucherUnpostedCount)
+                .setVoucherPostedCount(voucherPostedCount)
+                .setVoucherCancelledCount(voucherCancelledCount);
+    }
+
+    @Override
+    public List<FinStatisticsTrendRespVO> getStatisticsTrend(FinStatisticsReqVO reqVO) {
+        // 1. 鏈熼棿
+        LocalDate startDate = resolveStartDate(reqVO);
+        LocalDate endDate = resolveEndDate(reqVO);
+        validatePeriod(startDate, endDate);
+
+        // 2. ERP 鏈堝害鏁版嵁杞� Map锛屼究浜庢寜鏈堜唤琛� 0
+        Map<String, ErpFinanceMonthlyStatisticsRespDTO> monthMap = convertMap(
+                getErpMonthlyStatistics(startDate, endDate), ErpFinanceMonthlyStatisticsRespDTO::getMonth);
+
+        // 3. 鏈熼棿鍐呴�愭湀杈撳嚭锛屾病鏈夋暟鎹殑鏈堜唤琛� 0
+        List<FinStatisticsTrendRespVO> list = new ArrayList<>();
+        YearMonth endMonth = YearMonth.from(endDate);
+        for (YearMonth month = YearMonth.from(startDate); !month.isAfter(endMonth); month = month.plusMonths(1)) {
+            ErpFinanceMonthlyStatisticsRespDTO dto = monthMap.get(month.toString());
+            BigDecimal receiptPrice = dto == null ? BigDecimal.ZERO : defaultIfNull(dto.getReceiptPrice());
+            BigDecimal paymentPrice = dto == null ? BigDecimal.ZERO : defaultIfNull(dto.getPaymentPrice());
+            list.add(new FinStatisticsTrendRespVO()
+                    .setMonth(month.toString())
+                    .setReceiptPrice(receiptPrice)
+                    .setPaymentPrice(paymentPrice)
+                    .setNetProfit(receiptPrice.subtract(paymentPrice))
+                    .setSaleOrderPrice(dto == null ? BigDecimal.ZERO : defaultIfNull(dto.getSaleOrderPrice()))
+                    .setPurchaseOrderPrice(dto == null ? BigDecimal.ZERO : defaultIfNull(dto.getPurchaseOrderPrice())));
+        }
+        return list;
+    }
+
+    /**
+     * 璋冪敤 ERP 妯″潡 API 鑾峰彇鏈堝害缁熻锛屽苟鍋氬厹搴曡В鍖�
+     */
+    private List<ErpFinanceMonthlyStatisticsRespDTO> getErpMonthlyStatistics(LocalDate startDate, LocalDate endDate) {
+        List<ErpFinanceMonthlyStatisticsRespDTO> list = erpFinanceStatisticsApi
+                .getMonthlyStatistics(startDate.atStartOfDay(), endDate.plusDays(1).atStartOfDay())
+                .getCheckedData();
+        return list == null ? List.of() : list;
+    }
+
+    private LocalDate resolveStartDate(FinStatisticsReqVO reqVO) {
+        return reqVO.getStartMonth() != null ? reqVO.getStartMonth().atDay(1)
+                : LocalDate.now().withDayOfYear(1);
+    }
+
+    private LocalDate resolveEndDate(FinStatisticsReqVO reqVO) {
+        return reqVO.getEndMonth() != null ? reqVO.getEndMonth().atEndOfMonth()
+                : LocalDate.now();
+    }
+
+    private void validatePeriod(LocalDate startDate, LocalDate endDate) {
+        if (startDate.isAfter(endDate)) {
+            throw exception(STATISTICS_PERIOD_INVALID, YearMonth.from(startDate), YearMonth.from(endDate));
+        }
+    }
+
+    private static BigDecimal sumOf(List<ErpFinanceMonthlyStatisticsRespDTO> list,
+                                   Function<ErpFinanceMonthlyStatisticsRespDTO, BigDecimal> getter) {
+        return list.stream().map(getter).map(FinStatisticsServiceImpl::defaultIfNull)
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+    }
+
+    private static BigDecimal defaultIfNull(BigDecimal amount) {
+        return amount == null ? BigDecimal.ZERO : amount;
+    }
+
+    private static Long defaultIfNull(Long value) {
+        return value == null ? 0L : value;
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/subject/FinAccountSubjectService.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/subject/FinAccountSubjectService.java
new file mode 100644
index 0000000..6986a49
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/subject/FinAccountSubjectService.java
@@ -0,0 +1,101 @@
+package cn.iocoder.yudao.module.finance.service.subject;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.finance.controller.admin.subject.vo.FinAccountSubjectPageReqVO;
+import cn.iocoder.yudao.module.finance.controller.admin.subject.vo.FinAccountSubjectSaveReqVO;
+import cn.iocoder.yudao.module.finance.dal.dataobject.subject.FinAccountSubjectDO;
+import jakarta.validation.Valid;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
+
+/**
+ * 浼氳绉戠洰 Service 鎺ュ彛
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+public interface FinAccountSubjectService {
+
+    /**
+     * 鍒涘缓浼氳绉戠洰
+     *
+     * @param createReqVO 鍒涘缓淇℃伅
+     * @return 绉戠洰缂栧彿
+     */
+    Long createAccountSubject(@Valid FinAccountSubjectSaveReqVO createReqVO);
+
+    /**
+     * 鏇存柊浼氳绉戠洰
+     *
+     * @param updateReqVO 鏇存柊淇℃伅
+     */
+    void updateAccountSubject(@Valid FinAccountSubjectSaveReqVO updateReqVO);
+
+    /**
+     * 鍒犻櫎浼氳绉戠洰
+     *
+     * @param id 绉戠洰缂栧彿
+     */
+    void deleteAccountSubject(Long id);
+
+    /**
+     * 鎵归噺鍒犻櫎浼氳绉戠洰
+     *
+     * @param ids 绉戠洰缂栧彿鏁扮粍
+     */
+    void deleteAccountSubjectList(List<Long> ids);
+
+    /**
+     * 鑾峰緱浼氳绉戠洰
+     *
+     * @param id 绉戠洰缂栧彿
+     * @return 浼氳绉戠洰
+     */
+    FinAccountSubjectDO getAccountSubject(Long id);
+
+    /**
+     * 鑾峰緱浼氳绉戠洰鍒嗛〉
+     *
+     * @param pageReqVO 鍒嗛〉鏌ヨ
+     * @return 浼氳绉戠洰鍒嗛〉
+     */
+    PageResult<FinAccountSubjectDO> getAccountSubjectPage(FinAccountSubjectPageReqVO pageReqVO);
+
+    /**
+     * 鑾峰緱浼氳绉戠洰鍒楄〃锛堝叏閲忥紝鎸夌鐩紪鐮佸崌搴忥紝鍓嶇鎹鏋勫缓绉戠洰鏍戯級
+     *
+     * @return 浼氳绉戠洰鍒楄〃
+     */
+    List<FinAccountSubjectDO> getAccountSubjectList();
+
+    /**
+     * 鑾峰緱鎸囧畾绉戠洰缂栫爜鐨勪細璁$鐩紝涓嶅瓨鍦ㄦ椂杩斿洖 null
+     *
+     * @param subjectCode 绉戠洰缂栫爜
+     * @return 浼氳绉戠洰
+     */
+    FinAccountSubjectDO getAccountSubjectByCode(String subjectCode);
+
+    /**
+     * 鑾峰緱鎸囧畾绉戠洰缂栫爜鐨勪細璁$鐩� Map
+     *
+     * @param subjectCodes 绉戠洰缂栫爜鏁扮粍
+     * @return 浼氳绉戠洰 Map
+     */
+    default Map<String, FinAccountSubjectDO> getAccountSubjectMap(Collection<String> subjectCodes) {
+        List<FinAccountSubjectDO> list = getAccountSubjectListByCodes(subjectCodes);
+        return convertMap(list, FinAccountSubjectDO::getSubjectCode);
+    }
+
+    /**
+     * 鑾峰緱鎸囧畾绉戠洰缂栫爜鐨勪細璁$鐩垪琛�
+     *
+     * @param subjectCodes 绉戠洰缂栫爜鏁扮粍
+     * @return 浼氳绉戠洰鍒楄〃
+     */
+    List<FinAccountSubjectDO> getAccountSubjectListByCodes(Collection<String> subjectCodes);
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/subject/FinAccountSubjectServiceImpl.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/subject/FinAccountSubjectServiceImpl.java
new file mode 100644
index 0000000..dad957f
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/subject/FinAccountSubjectServiceImpl.java
@@ -0,0 +1,157 @@
+package cn.iocoder.yudao.module.finance.service.subject;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.finance.controller.admin.subject.vo.FinAccountSubjectPageReqVO;
+import cn.iocoder.yudao.module.finance.controller.admin.subject.vo.FinAccountSubjectSaveReqVO;
+import cn.iocoder.yudao.module.finance.dal.dataobject.subject.FinAccountSubjectDO;
+import cn.iocoder.yudao.module.finance.dal.mysql.subject.FinAccountSubjectMapper;
+import cn.iocoder.yudao.module.finance.dal.mysql.voucher.FinVoucherEntryMapper;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.validation.annotation.Validated;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.module.finance.enums.ErrorCodeConstants.*;
+
+/**
+ * 浼氳绉戠洰 Service 瀹炵幇绫�
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Service
+@Validated
+public class FinAccountSubjectServiceImpl implements FinAccountSubjectService {
+
+    @Resource
+    private FinAccountSubjectMapper accountSubjectMapper;
+    @Resource
+    private FinVoucherEntryMapper voucherEntryMapper;
+
+    @Override
+    public Long createAccountSubject(FinAccountSubjectSaveReqVO createReqVO) {
+        // 1. 鏍¢獙绉戠洰缂栫爜鍞竴
+        validateSubjectCodeUnique(null, createReqVO.getSubjectCode());
+        // 2. 鏍¢獙涓婄骇绉戠洰
+        validateParent(createReqVO.getParentId(), null);
+        // 3. 鎻掑叆
+        FinAccountSubjectDO subject = BeanUtils.toBean(createReqVO, FinAccountSubjectDO.class);
+        accountSubjectMapper.insert(subject);
+        return subject.getId();
+    }
+
+    @Override
+    public void updateAccountSubject(FinAccountSubjectSaveReqVO updateReqVO) {
+        // 1. 鏍¢獙瀛樺湪
+        validateAccountSubjectExists(updateReqVO.getId());
+        // 2. 鏍¢獙绉戠洰缂栫爜鍞竴
+        validateSubjectCodeUnique(updateReqVO.getId(), updateReqVO.getSubjectCode());
+        // 3. 鏍¢獙涓婄骇绉戠洰锛堜笉鑳介�夎嚜宸憋紝涔熶笉鑳介�夎嚜宸辩殑涓嬬骇锛�
+        validateParent(updateReqVO.getParentId(), updateReqVO.getId());
+        // 4. 鏇存柊
+        accountSubjectMapper.updateById(BeanUtils.toBean(updateReqVO, FinAccountSubjectDO.class));
+    }
+
+    @Override
+    public void deleteAccountSubject(Long id) {
+        FinAccountSubjectDO subject = validateAccountSubjectExists(id);
+        // 1. 鏍¢獙鏄惁瀛樺湪涓嬬骇绉戠洰
+        List<FinAccountSubjectDO> children = accountSubjectMapper.selectListByParentId(id);
+        if (!children.isEmpty()) {
+            throw exception(ACCOUNT_SUBJECT_HAS_CHILDREN, subject.getSubjectName(), children.size());
+        }
+        // 2. 鏍¢獙鏄惁琚嚟璇佸垎褰曞紩鐢�
+        Long referencedCount = voucherEntryMapper.selectCountBySubjectCodes(List.of(subject.getSubjectCode()));
+        if (referencedCount != null && referencedCount > 0) {
+            throw exception(ACCOUNT_SUBJECT_REFERENCED, subject.getSubjectName(), referencedCount);
+        }
+        // 3. 鍒犻櫎
+        accountSubjectMapper.deleteById(id);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteAccountSubjectList(List<Long> ids) {
+        ids.forEach(this::deleteAccountSubject);
+    }
+
+    private FinAccountSubjectDO validateAccountSubjectExists(Long id) {
+        FinAccountSubjectDO subject = accountSubjectMapper.selectById(id);
+        if (subject == null) {
+            throw exception(ACCOUNT_SUBJECT_NOT_EXISTS);
+        }
+        return subject;
+    }
+
+    private void validateSubjectCodeUnique(Long id, String subjectCode) {
+        FinAccountSubjectDO subject = accountSubjectMapper.selectBySubjectCode(subjectCode);
+        if (subject == null) {
+            return;
+        }
+        if (id == null || !Objects.equals(subject.getId(), id)) {
+            throw exception(ACCOUNT_SUBJECT_CODE_DUPLICATE, subjectCode);
+        }
+    }
+
+    /**
+     * 鏍¢獙涓婄骇绉戠洰锛氶潪绌烘椂蹇呴』瀛樺湪锛屼笖涓嶈兘鏄嚜宸辨垨鑷繁鐨勪笅绾э紙閬垮厤褰㈡垚鐜級
+     *
+     * @param parentId 涓婄骇绉戠洰缂栧彿
+     * @param selfId   褰撳墠绉戠洰缂栧彿锛涙柊澧炴椂浼� null
+     */
+    private void validateParent(Long parentId, Long selfId) {
+        if (parentId == null) {
+            return;
+        }
+        if (Objects.equals(parentId, selfId)) {
+            throw exception(ACCOUNT_SUBJECT_PARENT_SELF);
+        }
+        FinAccountSubjectDO parent = accountSubjectMapper.selectById(parentId);
+        if (parent == null) {
+            throw exception(ACCOUNT_SUBJECT_PARENT_NOT_EXISTS, parentId);
+        }
+        // 鑷笅鑰屼笂鍥炴函锛岃嫢閬囧埌鑷繁锛岃鏄庨�夋嫨鐨勪笂绾ф槸鑷繁鐨勪笅绾�
+        Set<Long> visited = new HashSet<>();
+        Long cursor = parentId;
+        while (cursor != null && visited.add(cursor)) {
+            if (Objects.equals(cursor, selfId)) {
+                throw exception(ACCOUNT_SUBJECT_PARENT_CYCLE, parent.getSubjectName());
+            }
+            FinAccountSubjectDO node = accountSubjectMapper.selectById(cursor);
+            cursor = node == null ? null : node.getParentId();
+        }
+    }
+
+    @Override
+    public FinAccountSubjectDO getAccountSubject(Long id) {
+        return accountSubjectMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<FinAccountSubjectDO> getAccountSubjectPage(FinAccountSubjectPageReqVO pageReqVO) {
+        return accountSubjectMapper.selectPage(pageReqVO);
+    }
+
+    @Override
+    public List<FinAccountSubjectDO> getAccountSubjectList() {
+        return accountSubjectMapper.selectListAll();
+    }
+
+    @Override
+    public FinAccountSubjectDO getAccountSubjectByCode(String subjectCode) {
+        return accountSubjectMapper.selectBySubjectCode(subjectCode);
+    }
+
+    @Override
+    public List<FinAccountSubjectDO> getAccountSubjectListByCodes(Collection<String> subjectCodes) {
+        return accountSubjectMapper.selectListBySubjectCodes(subjectCodes);
+    }
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/voucher/FinVoucherService.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/voucher/FinVoucherService.java
new file mode 100644
index 0000000..b3ef5f1
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/voucher/FinVoucherService.java
@@ -0,0 +1,129 @@
+package cn.iocoder.yudao.module.finance.service.voucher;
+
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.module.finance.controller.admin.voucher.vo.FinVoucherPageReqVO;
+import cn.iocoder.yudao.module.finance.controller.admin.voucher.vo.FinVoucherSaveReqVO;
+import cn.iocoder.yudao.module.finance.dal.dataobject.voucher.FinVoucherDO;
+import cn.iocoder.yudao.module.finance.dal.dataobject.voucher.FinVoucherEntryDO;
+import jakarta.validation.Valid;
+
+import java.time.LocalDate;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
+
+/**
+ * 璁拌处鍑瘉 Service 鎺ュ彛
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+public interface FinVoucherService {
+
+    /**
+     * 鍒涘缓璁拌处鍑瘉
+     *
+     * @param createReqVO 鍒涘缓淇℃伅
+     * @return 鍑瘉缂栧彿
+     */
+    Long createVoucher(@Valid FinVoucherSaveReqVO createReqVO);
+
+    /**
+     * 鏇存柊璁拌处鍑瘉
+     *
+     * 浠呮湭杩囪处鐨勫嚟璇佸厑璁镐慨鏀癸紱淇敼鏃朵細鏁磋〃閲嶅缓鍑瘉鍒嗗綍
+     *
+     * @param updateReqVO 鏇存柊淇℃伅
+     */
+    void updateVoucher(@Valid FinVoucherSaveReqVO updateReqVO);
+
+    /**
+     * 鍒犻櫎璁拌处鍑瘉
+     *
+     * @param id 鍑瘉缂栧彿
+     */
+    void deleteVoucher(Long id);
+
+    /**
+     * 鎵归噺鍒犻櫎璁拌处鍑瘉
+     *
+     * @param ids 鍑瘉缂栧彿鏁扮粍
+     */
+    void deleteVoucherList(List<Long> ids);
+
+    /**
+     * 鍑瘉杩囪处锛氭湭杩囪处 -> 宸茶繃璐�
+     *
+     * @param id 鍑瘉缂栧彿
+     */
+    void postVoucher(Long id);
+
+    /**
+     * 鍑瘉浣滃簾锛氭湭杩囪处 -> 宸蹭綔搴�
+     *
+     * @param id 鍑瘉缂栧彿
+     */
+    void cancelVoucher(Long id);
+
+    /**
+     * 鑾峰緱璁拌处鍑瘉
+     *
+     * @param id 鍑瘉缂栧彿
+     * @return 璁拌处鍑瘉
+     */
+    FinVoucherDO getVoucher(Long id);
+
+    /**
+     * 鑾峰緱璁拌处鍑瘉鍒嗛〉
+     *
+     * @param pageReqVO 鍒嗛〉鏌ヨ
+     * @return 璁拌处鍑瘉鍒嗛〉
+     */
+    PageResult<FinVoucherDO> getVoucherPage(FinVoucherPageReqVO pageReqVO);
+
+    /**
+     * 鑾峰緱鎸囧畾缂栧彿鐨勮璐﹀嚟璇佸垪琛�
+     *
+     * @param ids 鍑瘉缂栧彿鏁扮粍
+     * @return 璁拌处鍑瘉鍒楄〃
+     */
+    List<FinVoucherDO> getVoucherList(Collection<Long> ids);
+
+    /**
+     * 鑾峰緱鎸囧畾鏃ユ湡鍖洪棿鐨勮璐﹀嚟璇佸垪琛�
+     *
+     * @param startDate 寮�濮嬫棩鏈燂紙鍚級
+     * @param endDate   缁撴潫鏃ユ湡锛堝惈锛�
+     * @return 璁拌处鍑瘉鍒楄〃
+     */
+    List<FinVoucherDO> getVoucherListByDateBetween(LocalDate startDate, LocalDate endDate);
+
+    /**
+     * 鑾峰緱鎸囧畾鍑瘉鐨勫垎褰曞垪琛紙鎸夎鍙峰崌搴忥級
+     *
+     * @param voucherId 鍑瘉缂栧彿
+     * @return 鍒嗗綍鍒楄〃
+     */
+    List<FinVoucherEntryDO> getVoucherEntryListByVoucherId(Long voucherId);
+
+    /**
+     * 鑾峰緱鎸囧畾鍑瘉鐨勫垎褰� Map锛坘ey 涓哄嚟璇佺紪鍙凤級
+     *
+     * @param voucherIds 鍑瘉缂栧彿鏁扮粍
+     * @return 鍒嗗綍 Map
+     */
+    default Map<Long, List<FinVoucherEntryDO>> getVoucherEntryMap(Collection<Long> voucherIds) {
+        List<FinVoucherEntryDO> list = getVoucherEntryListByVoucherIds(voucherIds);
+        return convertMultiMap(list, FinVoucherEntryDO::getVoucherId);
+    }
+
+    /**
+     * 鑾峰緱鎸囧畾鍑瘉鐨勫垎褰曞垪琛�
+     *
+     * @param voucherIds 鍑瘉缂栧彿鏁扮粍
+     * @return 鍒嗗綍鍒楄〃
+     */
+    List<FinVoucherEntryDO> getVoucherEntryListByVoucherIds(Collection<Long> voucherIds);
+
+}
diff --git a/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/voucher/FinVoucherServiceImpl.java b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/voucher/FinVoucherServiceImpl.java
new file mode 100644
index 0000000..f1a7276
--- /dev/null
+++ b/yudao-module-finance/src/main/java/cn/iocoder/yudao/module/finance/service/voucher/FinVoucherServiceImpl.java
@@ -0,0 +1,389 @@
+package cn.iocoder.yudao.module.finance.service.voucher;
+
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
+import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.module.finance.controller.admin.voucher.vo.FinVoucherEntrySaveReqVO;
+import cn.iocoder.yudao.module.finance.controller.admin.voucher.vo.FinVoucherPageReqVO;
+import cn.iocoder.yudao.module.finance.controller.admin.voucher.vo.FinVoucherSaveReqVO;
+import cn.iocoder.yudao.module.finance.dal.dataobject.subject.FinAccountSubjectDO;
+import cn.iocoder.yudao.module.finance.dal.dataobject.voucher.FinVoucherDO;
+import cn.iocoder.yudao.module.finance.dal.dataobject.voucher.FinVoucherEntryDO;
+import cn.iocoder.yudao.module.finance.dal.mysql.voucher.FinVoucherEntryMapper;
+import cn.iocoder.yudao.module.finance.dal.mysql.voucher.FinVoucherMapper;
+import cn.iocoder.yudao.module.finance.dal.redis.no.FinNoRedisDAO;
+import cn.iocoder.yudao.module.finance.enums.FinVoucherStatusEnum;
+import cn.iocoder.yudao.module.finance.service.subject.FinAccountSubjectService;
+import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.validation.annotation.Validated;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
+import static cn.iocoder.yudao.module.finance.enums.ErrorCodeConstants.*;
+
+/**
+ * 璁拌处鍑瘉 Service 瀹炵幇绫�
+ *
+ * 鍊熻捶璁拌处瑙勫垯锛氭湁鍊熷繀鏈夎捶銆佸�熻捶蹇呯浉绛�
+ *
+ * @author 瓒呯骇绠$悊鍛�
+ */
+@Service
+@Validated
+public class FinVoucherServiceImpl implements FinVoucherService {
+
+    /**
+     * 闄勪欢锛氬簲鐢ㄧ敤閫�
+     */
+    private static final String ATTACHMENT_APPLICATION = "file";
+    /**
+     * 闄勪欢锛氫笟鍔¤褰曠被鍨�
+     */
+    private static final String ATTACHMENT_RECORD_TYPE = "fin_voucher";
+
+    @Resource
+    private FinVoucherMapper voucherMapper;
+    @Resource
+    private FinVoucherEntryMapper voucherEntryMapper;
+    @Resource
+    private FinAccountSubjectService accountSubjectService;
+    @Resource
+    private FinNoRedisDAO noRedisDAO;
+    @Resource
+    private StorageAttachmentApi storageAttachmentApi;
+    @Resource
+    private AdminUserApi adminUserApi;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public Long createVoucher(FinVoucherSaveReqVO createReqVO) {
+        // 1.1 鏍¢獙鍒嗗綍锛屽苟寰楀埌瑙勮寖鍖栧悗鐨勫垎褰曟槑缁�
+        List<FinVoucherEntryDO> entries = buildVoucherEntries(createReqVO.getEntries(), createReqVO.getSummary());
+        // 1.2 鍑瘉瀛楀彿锛氱暀绌烘椂鑷姩鐢熸垚锛屽苟鏍¢獙鍞竴
+        String voucherNo = StrUtil.isBlank(createReqVO.getVoucherNo())
+                ? noRedisDAO.generate(FinNoRedisDAO.VOUCHER_NO_PREFIX) : StrUtil.trim(createReqVO.getVoucherNo());
+        validateVoucherNoUnique(null, voucherNo);
+
+        // 2.1 鎻掑叆鍑瘉
+        FinVoucherDO voucher = BeanUtils.toBean(createReqVO, FinVoucherDO.class);
+        voucher.setVoucherNo(voucherNo);
+        voucher.setStatus(FinVoucherStatusEnum.UNPOSTED.getStatus());
+        voucher.setDebit(sumAmount(entries, true));
+        voucher.setCredit(sumAmount(entries, false));
+        voucher.setAttachmentCount(ObjectUtil.defaultIfNull(createReqVO.getAttachmentCount(), 0));
+        voucherMapper.insert(voucher);
+        // 2.2 鎻掑叆鍒嗗綍
+        insertVoucherEntryList(voucher.getId(), entries);
+        // 2.3 缁戝畾闄勪欢
+        storageAttachmentApi.bindAttachments(ATTACHMENT_APPLICATION, ATTACHMENT_RECORD_TYPE,
+                voucher.getId(), createReqVO.getBlobIds());
+        return voucher.getId();
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateVoucher(FinVoucherSaveReqVO updateReqVO) {
+        // 1.1 鏍¢獙瀛樺湪
+        FinVoucherDO voucher = validateVoucherExists(updateReqVO.getId());
+        // 1.2 鏍¢獙鐘舵�侊細鍙湁鏈繃璐︾殑鍑瘉鍏佽淇敼
+        validateVoucherUnposted(voucher);
+        // 1.3 鏍¢獙鍒嗗綍锛屽苟寰楀埌瑙勮寖鍖栧悗鐨勫垎褰曟槑缁�
+        List<FinVoucherEntryDO> entries = buildVoucherEntries(updateReqVO.getEntries(), updateReqVO.getSummary());
+        // 1.4 鍑瘉瀛楀彿锛氱暀绌烘椂娌跨敤鍘熷瓧鍙凤紝濉啓浜嗗垯鏍¢獙鍞竴
+        String voucherNo = StrUtil.isBlank(updateReqVO.getVoucherNo())
+                ? voucher.getVoucherNo() : StrUtil.trim(updateReqVO.getVoucherNo());
+        validateVoucherNoUnique(voucher.getId(), voucherNo);
+
+        // 2.1 鏇存柊鍑瘉锛堢姸鎬併�佸埗鍗曚汉涓嶇敱鍓嶇鏀瑰姩锛�
+        FinVoucherDO updateObj = BeanUtils.toBean(updateReqVO, FinVoucherDO.class);
+        updateObj.setId(voucher.getId());
+        updateObj.setVoucherNo(voucherNo);
+        updateObj.setDebit(sumAmount(entries, true));
+        updateObj.setCredit(sumAmount(entries, false));
+        updateObj.setAttachmentCount(ObjectUtil.defaultIfNull(updateReqVO.getAttachmentCount(), 0));
+        voucherMapper.updateById(updateObj);
+        // 2.2 鏁磋〃閲嶅缓鍒嗗綍
+        voucherEntryMapper.deleteByVoucherId(voucher.getId());
+        insertVoucherEntryList(voucher.getId(), entries);
+        // 2.3 鏇存柊闄勪欢
+        storageAttachmentApi.updateAttachments(ATTACHMENT_APPLICATION, ATTACHMENT_RECORD_TYPE,
+                voucher.getId(), updateReqVO.getBlobIds());
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteVoucher(Long id) {
+        FinVoucherDO voucher = validateVoucherExists(id);
+        // 鍙湁鏈繃璐︺�佸凡浣滃簾鐨勫嚟璇佸厑璁稿垹闄�
+        validateVoucherDeletable(voucher);
+        deleteVoucherList0(Collections.singletonList(id));
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteVoucherList(List<Long> ids) {
+        if (ids == null || ids.isEmpty()) {
+            return;
+        }
+        // 鏍¢獙鐘舵�侊細鍙瀛樺湪宸茶繃璐︾殑鍑瘉锛屾暣鎵规嫆缁濓紝閬垮厤閮ㄥ垎鎴愬姛
+        voucherMapper.selectListByIds(ids).forEach(this::validateVoucherDeletable);
+        deleteVoucherList0(ids);
+    }
+
+    private void deleteVoucherList0(List<Long> ids) {
+        voucherMapper.deleteByIds(ids);
+        voucherEntryMapper.deleteByVoucherIds(ids);
+        // 鍒犻櫎闄勪欢鍏宠仈
+        ids.forEach(id -> storageAttachmentApi.deleteAttachmentsByRecord(ATTACHMENT_RECORD_TYPE, id));
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void postVoucher(Long id) {
+        FinVoucherDO voucher = validateVoucherExists(id);
+        validateVoucherUnposted(voucher);
+        voucherMapper.updateById(new FinVoucherDO().setId(id)
+                .setStatus(FinVoucherStatusEnum.POSTED.getStatus()));
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void cancelVoucher(Long id) {
+        FinVoucherDO voucher = validateVoucherExists(id);
+        validateVoucherCancellable(voucher);
+        voucherMapper.updateById(new FinVoucherDO().setId(id)
+                .setStatus(FinVoucherStatusEnum.CANCELLED.getStatus()));
+    }
+
+    @Override
+    public FinVoucherDO getVoucher(Long id) {
+        return voucherMapper.selectById(id);
+    }
+
+    @Override
+    public PageResult<FinVoucherDO> getVoucherPage(FinVoucherPageReqVO pageReqVO) {
+        // 銆屽埗鍗曚汉銆嶆悳绱㈡閲屽~鐨勬槸鏄电О锛堝垪琛ㄥ睍绀虹殑灏辨槸鏄电О锛夛紝鍏堣В鏋愭垚鐢ㄦ埛缂栧彿鍐嶈繃婊�
+        Set<String> creatorIds = resolveCreatorIds(pageReqVO.getCreator());
+        if (creatorIds != null && creatorIds.isEmpty()) {
+            // 鎼滅储璇嶆病鍖归厤鍒颁换浣曠敤鎴凤細鐩存帴杩斿洖绌洪〉銆�
+            // 涓嶈兘鎶婄┖闆嗗悎褰撴垚銆屼笉杩囨护銆嶄紶涓嬪幓锛屽惁鍒欑敤鎴锋悳涓�涓笉瀛樺湪鐨勬樀绉颁細鐪嬪埌鍏ㄩ儴鍑瘉锛屽儚鏄瓫閫夊け鏁�
+            return new PageResult<>(Collections.emptyList(), 0L);
+        }
+        return voucherMapper.selectPage(pageReqVO, creatorIds);
+    }
+
+    /**
+     * 鎶娿�屽埗鍗曚汉銆嶆悳绱㈣瘝瑙f瀽鎴愮敤鎴风紪鍙烽泦鍚堛��
+     *
+     * 鏀寔涓夌杈撳叆锛氭樀绉般�佽处鍙凤紙閮芥槸妯$硦鍖归厤锛夈�佺函鏁板瓧鐨勭敤鎴风紪鍙凤紙鍏煎杩欎釜鎼滅储妗嗕互鍓嶆寜缂栧彿妯$硦鍖归厤鐨勮�佷範鎯級銆�
+     * 鏄电О鏄垪琛ㄥ睍绀虹殑瀛楁锛岃处鍙锋槸鎼滅储妗嗕笂鍐欑殑鎻愮ず璇嶏紝鎵�浠ヤ袱鑰呴兘瑕佽銆�
+     *
+     * @param creatorKeyword 鎼滅储璇�
+     * @return null 琛ㄧず鎼滅储璇嶄负绌恒�佷笉鎸夊埗鍗曚汉杩囨护锛涚┖闆嗗悎琛ㄧず鎼滅储璇嶆病鍖归厤鍒颁换浣曠敤鎴�
+     */
+    private Set<String> resolveCreatorIds(String creatorKeyword) {
+        if (StrUtil.isBlank(creatorKeyword)) {
+            return null;
+        }
+        String keyword = creatorKeyword.trim();
+        Set<Long> userIds = convertSet(adminUserApi.getUserListByNickname(keyword), AdminUserRespDTO::getId);
+        userIds.addAll(convertSet(adminUserApi.getUserListByUsername(keyword), AdminUserRespDTO::getId));
+        try {
+            userIds.add(Long.parseLong(keyword));
+        } catch (NumberFormatException ignored) {
+            // 涓嶆槸绾暟瀛楋紝鍙寜鏄电О銆佽处鍙峰尮閰�
+        }
+        return convertSet(userIds, String::valueOf);
+    }
+
+    @Override
+    public List<FinVoucherDO> getVoucherList(Collection<Long> ids) {
+        if (ids == null || ids.isEmpty()) {
+            return Collections.emptyList();
+        }
+        return voucherMapper.selectListByIds(ids);
+    }
+
+    @Override
+    public List<FinVoucherDO> getVoucherListByDateBetween(LocalDate startDate, LocalDate endDate) {
+        return voucherMapper.selectListByDateBetween(startDate, endDate);
+    }
+
+    @Override
+    public List<FinVoucherEntryDO> getVoucherEntryListByVoucherId(Long voucherId) {
+        return voucherEntryMapper.selectListByVoucherId(voucherId);
+    }
+
+    @Override
+    public List<FinVoucherEntryDO> getVoucherEntryListByVoucherIds(Collection<Long> voucherIds) {
+        if (voucherIds == null || voucherIds.isEmpty()) {
+            return Collections.emptyList();
+        }
+        return voucherEntryMapper.selectListByVoucherIds(voucherIds);
+    }
+
+    private FinVoucherDO validateVoucherExists(Long id) {
+        if (id == null) {
+            throw exception(VOUCHER_NOT_EXISTS);
+        }
+        FinVoucherDO voucher = voucherMapper.selectById(id);
+        if (voucher == null) {
+            throw exception(VOUCHER_NOT_EXISTS);
+        }
+        return voucher;
+    }
+
+    private void validateVoucherNoUnique(Long id, String voucherNo) {
+        FinVoucherDO voucher = voucherMapper.selectByVoucherNo(voucherNo);
+        if (voucher == null) {
+            return;
+        }
+        if (id == null || !voucher.getId().equals(id)) {
+            throw exception(VOUCHER_NO_DUPLICATE, voucherNo);
+        }
+    }
+
+    private void validateVoucherUnposted(FinVoucherDO voucher) {
+        if (!FinVoucherStatusEnum.UNPOSTED.getStatus().equals(voucher.getStatus())) {
+            throw exception(VOUCHER_STATUS_NOT_UNPOSTED, voucher.getVoucherNo(),
+                    FinVoucherStatusEnum.getName(voucher.getStatus()));
+        }
+    }
+
+    private void validateVoucherCancellable(FinVoucherDO voucher) {
+        if (FinVoucherStatusEnum.UNPOSTED.getStatus().equals(voucher.getStatus())
+                || FinVoucherStatusEnum.POSTED.getStatus().equals(voucher.getStatus())) {
+            return;
+        }
+        throw exception(VOUCHER_STATUS_NOT_CANCELLABLE, voucher.getVoucherNo(),
+                FinVoucherStatusEnum.getName(voucher.getStatus()));
+    }
+
+    private void validateVoucherDeletable(FinVoucherDO voucher) {
+        if (FinVoucherStatusEnum.UNPOSTED.getStatus().equals(voucher.getStatus())
+                || FinVoucherStatusEnum.CANCELLED.getStatus().equals(voucher.getStatus())) {
+            return;
+        }
+        throw exception(VOUCHER_DELETE_NOT_ALLOWED, voucher.getVoucherNo(),
+                FinVoucherStatusEnum.getName(voucher.getStatus()));
+    }
+
+    /**
+     * 鏍¢獙鍑瘉鍒嗗綍锛屽苟杞崲涓哄緟钀藉簱鐨勫垎褰曞垪琛�
+     *
+     * 鏍¢獙椤癸細鈶� 鏁磋绌虹櫧蹇界暐 鈶� 鏈夐噾棰濆繀椤绘湁绉戠洰 鈶� 鏈夌鐩繀椤绘湁閲戦
+     * 鈶� 鍗曡鍊熻捶涓嶅緱鍚屾椂澶т簬 0 鈶� 绉戠洰蹇呴』瀛樺湪 鈶� 鍊熻捶鍚堣鐩哥瓑涓斿ぇ浜� 0
+     *
+     * @param entryReqVOs    鍓嶇鎻愪氦鐨勫垎褰�
+     * @param voucherSummary 鍑瘉鎽樿锛岀敤浜庡洖濉垎褰曟憳瑕�
+     * @return 寰呰惤搴撶殑鍒嗗綍鍒楄〃锛堣鍙蜂粠 1 杩炵画缂栧彿锛�
+     */
+    private List<FinVoucherEntryDO> buildVoucherEntries(List<FinVoucherEntrySaveReqVO> entryReqVOs,
+                                                        String voucherSummary) {
+        // 1. 涓�娆℃�ф煡璇㈡墍鏈夋秹鍙婄殑浼氳绉戠洰锛岄伩鍏嶉�愯鏌ュ簱
+        Set<String> subjectCodes = convertSet(entryReqVOs,
+                entry -> StrUtil.trim(entry.getSubjectCode()),
+                entry -> StrUtil.isNotBlank(entry.getSubjectCode()));
+        Map<String, FinAccountSubjectDO> subjectMap = accountSubjectService.getAccountSubjectMap(subjectCodes);
+
+        // 2. 閫愯鏍¢獙
+        List<FinVoucherEntryDO> entries = new ArrayList<>(entryReqVOs.size());
+        for (int i = 0; i < entryReqVOs.size(); i++) {
+            int rowNo = i + 1; // 鎻愮ず涓殑琛屽彿涓庡墠绔〃鏍艰鍙蜂繚鎸佷竴鑷�
+            FinVoucherEntrySaveReqVO entry = entryReqVOs.get(i);
+            BigDecimal debit = normalizeAmount(entry.getDebit());
+            BigDecimal credit = normalizeAmount(entry.getCredit());
+            boolean hasAmount = isPositive(debit) || isPositive(credit);
+            // 2.1 鏁磋绌虹櫧锛堟棤绉戠洰銆佹棤閲戦锛夛紝鐩存帴蹇界暐
+            if (StrUtil.isBlank(entry.getSubjectCode())) {
+                if (!hasAmount) {
+                    continue;
+                }
+                throw exception(VOUCHER_ENTRY_SUBJECT_REQUIRED, rowNo, formatAmount(debit), formatAmount(credit));
+            }
+            String subjectCode = StrUtil.trim(entry.getSubjectCode());
+            // 2.2 鏈夌鐩絾娌″~閲戦
+            if (!hasAmount) {
+                throw exception(VOUCHER_ENTRY_AMOUNT_REQUIRED, rowNo, subjectCode);
+            }
+            // 2.3 鍗曡鍊熻捶涓嶅緱鍚屾椂澶т簬 0
+            if (isPositive(debit) && isPositive(credit)) {
+                throw exception(VOUCHER_ENTRY_DIRECTION_BOTH, rowNo, subjectCode,
+                        formatAmount(debit), formatAmount(credit));
+            }
+            // 2.4 绉戠洰蹇呴』瀛樺湪
+            FinAccountSubjectDO subject = subjectMap.get(subjectCode);
+            if (subject == null) {
+                throw exception(VOUCHER_ENTRY_SUBJECT_NOT_EXISTS, rowNo, subjectCode);
+            }
+            // 3. 缁勮
+            entries.add(FinVoucherEntryDO.builder()
+                    .rowNo(entries.size() + 1)
+                    .subjectCode(subjectCode)
+                    .subjectName(subject.getSubjectName())
+                    .summary(StrUtil.isBlank(entry.getSummary())
+                            ? StrUtil.blankToDefault(voucherSummary, subject.getSubjectName()) : entry.getSummary())
+                    .debit(debit)
+                    .credit(credit)
+                    .auxiliaryType(entry.getAuxiliaryType())
+                    .auxiliaryId(entry.getAuxiliaryId())
+                    .auxiliaryName(entry.getAuxiliaryName())
+                    .build());
+        }
+
+        // 4. 鑷冲皯涓�鏉℃湁鏁堝垎褰�
+        if (entries.isEmpty()) {
+            throw exception(VOUCHER_ENTRY_EMPTY);
+        }
+        // 5. 鍊熻捶骞宠 锛屼笖鍚堣澶т簬 0
+        BigDecimal debitTotal = sumAmount(entries, true);
+        BigDecimal creditTotal = sumAmount(entries, false);
+        if (!isPositive(debitTotal) || debitTotal.compareTo(creditTotal) != 0) {
+            throw exception(VOUCHER_ENTRY_UNBALANCED, formatAmount(debitTotal), formatAmount(creditTotal));
+        }
+        return entries;
+    }
+
+    private void insertVoucherEntryList(Long voucherId, List<FinVoucherEntryDO> entries) {
+        entries.forEach(entry -> entry.setVoucherId(voucherId));
+        voucherEntryMapper.insertBatch(entries);
+    }
+
+    private static BigDecimal sumAmount(List<FinVoucherEntryDO> entries, boolean debit) {
+        return entries.stream()
+                .map(entry -> debit ? entry.getDebit() : entry.getCredit())
+                .reduce(BigDecimal.ZERO, BigDecimal::add);
+    }
+
+    /**
+     * 閲戦瑙勮寖鍖栵細绌哄�兼寜 0 澶勭悊锛岀粺涓�淇濈暀 2 浣嶅皬鏁�
+     */
+    private static BigDecimal normalizeAmount(BigDecimal amount) {
+        return amount == null ? BigDecimal.ZERO : amount.setScale(2, RoundingMode.HALF_UP);
+    }
+
+    private static boolean isPositive(BigDecimal amount) {
+        return amount != null && amount.compareTo(BigDecimal.ZERO) > 0;
+    }
+
+    private static String formatAmount(BigDecimal amount) {
+        return amount == null ? "0.00" : amount.setScale(2, RoundingMode.HALF_UP).toPlainString();
+    }
+
+}
diff --git a/yudao-module-finance/src/main/resources/mapper/ledger/FinLedgerMapper.xml b/yudao-module-finance/src/main/resources/mapper/ledger/FinLedgerMapper.xml
new file mode 100644
index 0000000..23525a7
--- /dev/null
+++ b/yudao-module-finance/src/main/resources/mapper/ledger/FinLedgerMapper.xml
@@ -0,0 +1,42 @@
+<?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="cn.iocoder.yudao.module.finance.dal.mysql.ledger.FinLedgerMapper">
+
+    <select id="selectEntryList"
+            resultType="cn.iocoder.yudao.module.finance.dal.dataobject.ledger.FinLedgerEntryDO">
+        SELECT e.voucher_id   AS voucherId,
+               v.voucher_no   AS voucherNo,
+               v.voucher_date AS voucherDate,
+               e.row_no       AS rowNo,
+               e.subject_code AS subjectCode,
+               e.subject_name AS subjectName,
+               e.summary      AS summary,
+               e.debit        AS debit,
+               e.credit       AS credit
+        FROM fin_voucher_entry e
+                 INNER JOIN fin_voucher v ON v.id = e.voucher_id AND v.deleted = 0
+        WHERE e.deleted = 0
+          AND v.status = #{status}
+          AND v.voucher_date &gt;= #{startDate}
+          AND v.voucher_date &lt;= #{endDate}
+          AND e.subject_code IN
+        <foreach collection="subjectCodes" item="subjectCode" open="(" separator="," close=")">
+            #{subjectCode}
+        </foreach>
+        ORDER BY v.voucher_date ASC, v.id ASC, e.row_no ASC
+    </select>
+
+    <select id="selectBalanceBeforeDate" resultType="java.math.BigDecimal">
+        SELECT COALESCE(SUM(e.debit - e.credit), 0)
+        FROM fin_voucher_entry e
+                 INNER JOIN fin_voucher v ON v.id = e.voucher_id AND v.deleted = 0
+        WHERE e.deleted = 0
+          AND v.status = #{status}
+          AND v.voucher_date &lt; #{date}
+          AND e.subject_code IN
+        <foreach collection="subjectCodes" item="subjectCode" open="(" separator="," close=")">
+            #{subjectCode}
+        </foreach>
+    </select>
+
+</mapper>
diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java
index 8019b02..27d2748 100644
--- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java
@@ -81,6 +81,14 @@
     List<AdminUserRespDTO> getUserListByNickname(String nickname);
 
     /**
+     * 鏍规嵁璐﹀彿妯$硦鎼滅储鐢ㄦ埛
+     *
+     * @param username 璐﹀彿鍏抽敭璇�
+     * @return 鐢ㄦ埛鍒楄〃
+     */
+    List<AdminUserRespDTO> getUserListByUsername(String username);
+
+    /**
      * 鑾峰緱鐢ㄦ埛 Map
      *
      * @param ids 鐢ㄦ埛缂栧彿鏁扮粍
diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java
index d0a0c68..fdcb2a8 100644
--- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java
@@ -120,6 +120,12 @@
     }
 
     @Override
+    public List<AdminUserRespDTO> getUserListByUsername(String username) {
+        List<AdminUserDO> users = userService.getUserListByUsername(username);
+        return BeanUtils.toBean(users, AdminUserRespDTO.class);
+    }
+
+    @Override
     public void validateUserList(Collection<Long> ids) {
         userService.validateUserList(ids);
     }
diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java
index c0c9be8..ab87cc5 100644
--- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java
@@ -40,6 +40,10 @@
         return selectList(new LambdaQueryWrapperX<AdminUserDO>().like(AdminUserDO::getNickname, nickname));
     }
 
+    default List<AdminUserDO> selectListByUsername(String username) {
+        return selectList(new LambdaQueryWrapperX<AdminUserDO>().like(AdminUserDO::getUsername, username));
+    }
+
     default List<AdminUserDO> selectListByStatus(Integer status) {
         return selectList(AdminUserDO::getStatus, status);
     }
diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java
index 0656577..e4ae6fa 100644
--- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java
@@ -197,6 +197,14 @@
     List<AdminUserDO> getUserListByNickname(String nickname);
 
     /**
+     * 鑾峰緱鐢ㄦ埛鍒楄〃锛屽熀浜庤处鍙锋ā绯婂尮閰�
+     *
+     * @param username 璐﹀彿
+     * @return 鐢ㄦ埛鍒楄〃
+     */
+    List<AdminUserDO> getUserListByUsername(String username);
+
+    /**
      * 鎵归噺瀵煎叆鐢ㄦ埛
      *
      * @param importUsers     瀵煎叆鐢ㄦ埛鍒楄〃
diff --git a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java
index 6099e11..86d27cc 100644
--- a/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java
+++ b/yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java
@@ -389,6 +389,11 @@
         return userMapper.selectListByNickname(nickname);
     }
 
+    @Override
+    public List<AdminUserDO> getUserListByUsername(String username) {
+        return userMapper.selectListByUsername(username);
+    }
+
     /**
      * 鑾峰緱閮ㄩ棬鏉′欢锛氭煡璇㈡寚瀹氶儴闂ㄧ殑瀛愰儴闂ㄧ紪鍙蜂滑锛屽寘鎷嚜韬�
      *
diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml
index 1763130..a076722 100644
--- a/yudao-server/pom.xml
+++ b/yudao-server/pom.xml
@@ -101,6 +101,13 @@
             <version>${revision}</version>
         </dependency>
 
+        <!-- 璐㈠姟绠$悊鐩稿叧妯″潡 -->
+        <dependency>
+            <groupId>cn.iocoder.boot</groupId>
+            <artifactId>yudao-module-finance</artifactId>
+            <version>${revision}</version>
+        </dependency>
+
         <!-- AI 澶фā鍨嬬浉鍏虫ā鍧椼�傞粯璁ゆ敞閲婏紝淇濊瘉缂栬瘧閫熷害 -->
         <dependency>
             <groupId>cn.iocoder.boot</groupId>
diff --git a/yudao-server/src/main/resources/application-trt.yaml b/yudao-server/src/main/resources/application-trt.yaml
index 742e335..1c2c291 100644
--- a/yudao-server/src/main/resources/application-trt.yaml
+++ b/yudao-server/src/main/resources/application-trt.yaml
@@ -65,7 +65,7 @@
       primary: master
       datasource:
         master:
-          url: jdbc:mysql://172.17.0.1:10000/mom-hm?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 杩炴帴鐨勭ず渚�
+          url: jdbc:mysql://172.17.0.1:10000/mom-trt?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 杩炴帴鐨勭ず渚�
           username: root
           password: xd@123456..
 

--
Gitblit v1.9.3