From a8e4132c3e2e9c3b3fcb0360901b35571b6464ea Mon Sep 17 00:00:00 2001
From: hsy <1029150275@qq.com>
Date: 星期五, 28 八月 2026 10:13:01 +0800
Subject: [PATCH] feat: 综合业务模块功能补充与实体拓展

---
 sql/vehicle_init.sql                                                                                     |   74 
 src/main/resources/application-local.yml                                                                 |  268 ++
 src/main/java/com/ruoyi/vehicle/controller/VehicleCostController.java                                    |   61 
 src/main/java/com/ruoyi/vehicle/controller/VehicleInfoController.java                                    |   51 
 src/main/java/com/ruoyi/projectManagement/controller/ProjectConfigController.java                        |   31 
 src/main/java/com/ruoyi/projectManagement/vo/AttachmentGroupVo.java                                      |   25 
 src/main/java/com/ruoyi/vehicle/pojo/FuelCardRecharge.java                                               |   53 
 src/main/java/com/ruoyi/basic/service/impl/CustomerServiceImpl.java                                      |   19 
 src/main/java/com/ruoyi/collaborativeApproval/service/BroadbandCommunicationService.java                 |   21 
 src/main/java/com/ruoyi/device/service/impl/DeviceRepairServiceImpl.java                                 |   64 
 src/main/java/com/ruoyi/device/service/impl/DeviceMaintenanceServiceImpl.java                            |   21 
 src/main/resources/mapper/projectManagement/InfoMapper.xml                                               |   11 
 src/main/java/com/ruoyi/sales/service/IBiddingManagementService.java                                     |   48 
 src/main/resources/mapper/collaborativeApproval/BroadbandCommunicationMapper.xml                         |    7 
 src/main/java/com/ruoyi/sales/mapper/SalesContractProductMapper.java                                     |   12 
 src/main/java/com/ruoyi/sales/service/impl/BiddingManagementServiceImpl.java                             |   66 
 src/main/resources/mapper/vehicle/FuelCardMapper.xml                                                     |   17 
 src/main/resources/mapper/vehicle/FuelCardRechargeMapper.xml                                             |   14 
 src/main/java/com/ruoyi/device/pojo/DeviceRepair.java                                                    |    9 
 src/main/java/com/ruoyi/device/service/impl/DeviceRepairSparePartServiceImpl.java                        |   18 
 src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java                                                   |    9 
 src/main/java/com/ruoyi/vehicle/controller/FuelCardController.java                                       |   52 
 doc/bidding_management.sql                                                                               |   26 
 src/main/java/com/ruoyi/vehicle/service/VehicleCostService.java                                          |    7 
 src/main/java/com/ruoyi/approve/bean/vo/FinReimbursementVo.java                                          |    3 
 src/main/java/com/ruoyi/device/controller/DeviceStatusChangeRecordController.java                        |   32 
 src/main/java/com/ruoyi/projectManagement/pojo/Roles.java                                                |    1 
 src/main/java/com/ruoyi/collaborativeApproval/service/HouseLeaseCostService.java                         |   12 
 src/main/java/com/ruoyi/sales/controller/MetricStatisticsController.java                                 |   25 
 src/main/java/com/ruoyi/collaborativeApproval/dto/BroadbandCommunicationDTO.java                         |   45 
 src/main/java/com/ruoyi/vehicle/service/FuelCardService.java                                             |    7 
 src/main/java/com/ruoyi/projectManagement/service/InfoService.java                                       |    7 
 src/main/java/com/ruoyi/collaborativeApproval/mapper/HouseLeaseMapper.java                               |   14 
 src/main/java/com/ruoyi/sales/mapper/SalesContractMapper.java                                            |   12 
 src/main/java/com/ruoyi/collaborativeApproval/mapper/BroadbandCommunicationMapper.java                   |    8 
 src/main/java/com/ruoyi/approve/mapper/FinReimbursementMapper.java                                       |    4 
 src/main/resources/mapper/approve/FinReimbursementMapper.xml                                             |   42 
 src/main/java/com/ruoyi/measuringinstrumentledger/pojo/MeasuringInstrumentLedgerRecord.java              |   33 
 src/main/java/com/ruoyi/approve/pojo/FinReimbursement.java                                               |   16 
 src/main/java/com/ruoyi/collaborativeApproval/service/impl/BroadbandCommunicationServiceImpl.java        |  102 +
 src/main/resources/mapper/sales/SalesLedgerMapper.xml                                                    |   28 
 alter_sales_ledger.sql                                                                                   |   11 
 src/main/java/com/ruoyi/vehicle/service/impl/VehicleCostServiceImpl.java                                 |   11 
 src/main/resources/mapper/vehicle/VehicleCostMapper.xml                                                  |    4 
 src/main/java/com/ruoyi/device/mapper/DeviceRepairSparePartMapper.java                                   |   15 
 src/main/java/com/ruoyi/projectManagement/service/impl/InfoServiceImpl.java                              |   63 
 src/main/resources/mapper/sales/SalesQuotationMapper.xml                                                 |    8 
 pom.xml                                                                                                  |   11 
 src/main/java/com/ruoyi/approve/controller/FinReimbursementController.java                               |   11 
 src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceServiceImpl.java                            |  108 +
 src/main/java/com/ruoyi/sales/controller/BiddingManagementController.java                                |   85 
 src/main/java/com/ruoyi/device/mapper/DeviceStatusChangeRecordMapper.java                                |    9 
 src/main/java/com/ruoyi/sales/pojo/SalesLawsuit.java                                                     |  107 +
 src/main/java/com/ruoyi/vehicle/service/impl/FuelCardServiceImpl.java                                    |   11 
 src/main/resources/mapper/vehicle/VehicleInfoMapper.xml                                                  |   27 
 src/main/java/com/ruoyi/collaborativeApproval/service/HouseLeaseService.java                             |   49 
 src/main/java/com/ruoyi/sales/controller/ShippingInfoController.java                                     |   16 
 src/main/java/com/ruoyi/device/pojo/DeviceStatusChangeRecord.java                                        |   49 
 src/main/java/com/ruoyi/projectManagement/controller/InfoController.java                                 |    7 
 src/main/java/com/ruoyi/sales/pojo/SalesContract.java                                                    |  145 +
 src/main/java/com/ruoyi/device/vo/DeviceMaintenanceVo.java                                               |    3 
 src/main/java/com/ruoyi/sales/dto/SalesLawsuitDTO.java                                                   |   33 
 src/main/java/com/ruoyi/sales/mapper/BiddingManagementMapper.java                                        |   12 
 src/main/java/com/ruoyi/vehicle/pojo/FuelRecord.java                                                     |   47 
 src/main/resources/mapper/sales/ShippingInfoMapper.xml                                                   |   54 
 src/main/java/com/ruoyi/sales/pojo/ShippingInfo.java                                                     |   40 
 src/main/java/com/ruoyi/vehicle/pojo/VehicleCost.java                                                    |   32 
 doc/sales_lawsuit.sql                                                                                    |   26 
 src/main/java/com/ruoyi/vehicle/mapper/VehicleInfoMapper.java                                            |   11 
 sql/vehicle_alter.sql                                                                                    |    1 
 src/main/java/com/ruoyi/device/service/impl/DeviceLedgerServiceImpl.java                                 |   75 
 src/main/java/com/ruoyi/quality/controller/QualityInspectController.java                                 |   13 
 src/main/java/com/ruoyi/sales/service/impl/SalesLawsuitServiceImpl.java                                  |  145 +
 src/main/java/com/ruoyi/collaborativeApproval/service/impl/HouseLeaseCostServiceImpl.java                |   16 
 src/main/java/com/ruoyi/projectManagement/pojo/ProjectConfig.java                                        |   64 
 src/main/java/com/ruoyi/vehicle/pojo/FuelCard.java                                                       |   36 
 src/main/java/com/ruoyi/collaborativeApproval/mapper/HouseLeaseCostMapper.java                           |   14 
 src/main/java/com/ruoyi/vehicle/mapper/FuelCardMapper.java                                               |   11 
 doc/sales_contract.sql                                                                                   |   58 
 src/main/java/com/ruoyi/projectManagement/service/ProjectConfigService.java                              |   17 
 src/main/resources/application.yml                                                                       |    2 
 src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java                                                  |    1 
 src/main/java/com/ruoyi/collaborativeApproval/pojo/BroadbandCommunication.java                           |   68 
 src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java                              |   73 
 src/main/java/com/ruoyi/sales/service/ISalesContractService.java                                         |   42 
 doc/broadband_communication.sql                                                                          |   25 
 src/main/java/com/ruoyi/vehicle/service/VehicleInfoService.java                                          |    7 
 src/main/java/com/ruoyi/common/enums/TypeEnums.java                                                      |    3 
 src/main/resources/mapper/basic/CustomerMapper.xml                                                       |  209 +
 src/main/resources/mapper/device/DeviceStatusChangeRecordMapper.xml                                      |    6 
 src/main/java/com/ruoyi/sales/pojo/SalesContractProduct.java                                             |   84 
 src/main/java/com/ruoyi/approve/service/impl/FinReimbursementServiceImpl.java                            |   15 
 src/main/java/com/ruoyi/sales/mapper/SalesLawsuitMapper.java                                             |   12 
 src/main/resources/mapper/vehicle/FuelRecordMapper.xml                                                   |   29 
 doc/house_lease.sql                                                                                      |   51 
 src/main/java/com/ruoyi/device/pojo/DeviceRepairSparePart.java                                           |   65 
 src/main/java/com/ruoyi/sales/vo/CustomerTransactionsDetailsVo.java                                      |    6 
 src/main/resources/mapper/sales/BiddingManagementMapper.xml                                              |   35 
 src/main/java/com/ruoyi/projectManagement/service/impl/ProjectConfigServiceImpl.java                     |   31 
 src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java                                  |   32 
 src/main/java/com/ruoyi/projectManagement/mapper/ProjectConfigMapper.java                                |    9 
 src/main/java/com/ruoyi/collaborativeApproval/pojo/HouseLease.java                                       |  119 +
 src/main/java/com/ruoyi/sales/vo/CustomerTransactionsVo.java                                             |   12 
 src/main/java/com/ruoyi/collaborativeApproval/service/impl/HouseLeaseServiceImpl.java                    |  159 +
 src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskJob.java                                      |   20 
 src/main/java/com/ruoyi/sales/vo/SalesContractVO.java                                                    |   19 
 src/main/resources/mapper/device/DeviceLedgerMapper.xml                                                  |   15 
 update_fin_reimbursement.sql                                                                             |    1 
 src/main/java/com/ruoyi/sales/pojo/SalesLedgerContractRel.java                                           |   47 
 src/main/java/com/ruoyi/sales/service/impl/SalesContractServiceImpl.java                                 |  183 +
 src/main/java/com/ruoyi/quality/pojo/QualityTestStandardParam.java                                       |   10 
 src/main/java/com/ruoyi/vehicle/controller/FuelCardRechargeController.java                               |   38 
 doc/project_management_config.sql                                                                        |   15 
 src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/MeasuringInstrumentLedgerServiceImpl.java |   11 
 src/main/java/com/ruoyi/quality/service/IQualityInspectService.java                                      |    2 
 src/main/java/com/ruoyi/collaborativeApproval/vo/HouseLeaseVO.java                                       |   23 
 src/main/java/com/ruoyi/vehicle/pojo/VehicleInfo.java                                                    |   39 
 src/main/java/com/ruoyi/collaborativeApproval/vo/BroadbandCommunicationVO.java                           |   42 
 src/main/java/com/ruoyi/sales/vo/ProjectTransactionsVo.java                                              |   47 
 src/main/java/com/ruoyi/device/pojo/MaintenanceTask.java                                                 |    6 
 src/main/java/com/ruoyi/collaborativeApproval/controller/HouseLeaseController.java                       |   64 
 src/main/java/com/ruoyi/vehicle/mapper/FuelCardRechargeMapper.java                                       |   13 
 src/main/java/com/ruoyi/device/dto/DeviceLedgerDto.java                                                  |   36 
 src/main/java/com/ruoyi/sales/mapper/ShippingInfoMapper.java                                             |    2 
 src/main/java/com/ruoyi/sales/vo/SalesLawsuitVO.java                                                     |   19 
 src/main/java/com/ruoyi/measuringinstrumentledger/dto/MeasuringInstrumentLedgerDto.java                  |   24 
 src/main/java/com/ruoyi/quality/pojo/QualityInspectParam.java                                            |   19 
 src/main/java/com/ruoyi/vehicle/mapper/FuelRecordMapper.java                                             |   11 
 src/main/java/com/ruoyi/ai/config/EmbeddingStoreConfig.java                                              |    4 
 src/main/java/com/ruoyi/projectManagement/vo/ListInfoVo.java                                             |    2 
 src/main/java/com/ruoyi/collaborativeApproval/controller/BroadbandCommunicationController.java           |   50 
 src/main/java/com/ruoyi/sales/dto/SalesLedgerDto.java                                                    |   37 
 src/main/resources/mapper/measuringinstrumentledger/MeasuringInstrumentLedgerRecordMapper.xml            |   20 
 doc/sales_ledger_contract_rel.sql                                                                        |   13 
 src/main/java/com/ruoyi/sales/pojo/SalesQuotation.java                                                   |    4 
 .gitignore                                                                                               |    6 
 src/main/java/com/ruoyi/vehicle/service/FuelRecordService.java                                           |   16 
 src/main/java/com/ruoyi/sales/controller/SalesContractController.java                                    |   81 
 src/main/java/com/ruoyi/device/service/IDeviceRepairSparePartService.java                                |   11 
 src/main/java/com/ruoyi/collaborativeApproval/dto/HouseLeaseDTO.java                                     |   29 
 src/main/java/com/ruoyi/sales/pojo/SalesLedger.java                                                      |   39 
 src/main/java/com/ruoyi/sales/dto/BiddingManagementDto.java                                              |   47 
 src/main/java/com/ruoyi/sales/dto/SalesContractDTO.java                                                  |   33 
 src/main/java/com/ruoyi/vehicle/service/impl/VehicleInfoServiceImpl.java                                 |   11 
 src/main/java/com/ruoyi/sales/controller/SalesLawsuitController.java                                     |   68 
 src/main/java/com/ruoyi/device/vo/DeviceRepairVo.java                                                    |    7 
 src/main/java/com/ruoyi/approve/service/FinReimbursementService.java                                     |    2 
 src/main/java/com/ruoyi/device/service/impl/DeviceStatusChangeRecordServiceImpl.java                     |   47 
 src/main/java/com/ruoyi/vehicle/mapper/VehicleCostMapper.java                                            |   21 
 src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java                                   |   31 
 src/main/java/com/ruoyi/sales/pojo/BiddingManagement.java                                                |  155 +
 src/main/java/com/ruoyi/sales/dto/SalesQuotationDto.java                                                 |    3 
 src/main/java/com/ruoyi/device/pojo/DeviceLedger.java                                                    |   36 
 src/main/java/com/ruoyi/basic/mapper/CustomerMapper.java                                                 |   10 
 src/main/java/com/ruoyi/vehicle/controller/FuelRecordController.java                                     |   59 
 src/main/java/com/ruoyi/basic/service/ICustomerService.java                                              |   16 
 src/main/java/com/ruoyi/collaborativeApproval/pojo/HouseLeaseCost.java                                   |   78 
 src/main/java/com/ruoyi/vehicle/service/IFuelCardRechargeService.java                                    |   12 
 src/main/java/com/ruoyi/sales/service/ISalesLawsuitService.java                                          |   42 
 src/main/java/com/ruoyi/vehicle/service/impl/FuelCardRechargeServiceImpl.java                            |   58 
 src/main/java/com/ruoyi/sales/mapper/SalesLedgerContractRelMapper.java                                   |   13 
 src/main/java/com/ruoyi/device/execl/DeviceLedgerExeclDto.java                                           |   34 
 src/main/resources/mapper/device/DeviceRepairMapper.xml                                                  |    8 
 src/main/java/com/ruoyi/sales/task/ContractReturnReminderTask.java                                       |   89 
 src/main/java/com/ruoyi/device/service/IDeviceStatusChangeRecordService.java                             |   10 
 src/main/java/com/ruoyi/device/pojo/DeviceMaintenance.java                                               |   12 
 src/main/java/com/ruoyi/vehicle/service/impl/FuelRecordServiceImpl.java                                  |  108 +
 167 files changed, 5,891 insertions(+), 62 deletions(-)

diff --git a/.gitignore b/.gitignore
index 8fac07c..bff985f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,3 +46,9 @@
 !*/build/*.java
 !*/build/*.html
 !*/build/*.xml
+
+/graphify-out
+/src/graphify-out
+/build_graph.py
+/gen_subagents.py
+/subagents_args.json
diff --git a/alter_sales_ledger.sql b/alter_sales_ledger.sql
new file mode 100644
index 0000000..9204df4
--- /dev/null
+++ b/alter_sales_ledger.sql
@@ -0,0 +1,11 @@
+ALTER TABLE `sales_ledger`
+ADD COLUMN `project_id` BIGINT COMMENT '椤圭洰ID锛堝叧鑱旈」鐩鐞嗭級',
+ADD COLUMN `year` INT COMMENT '骞翠唤锛堣嚜鍔ㄥ彇绛捐鏃ユ湡锛�',
+ADD COLUMN `signing_unit_id` BIGINT COMMENT '绛惧崟鍗曚綅ID锛堢粍缁囨灦鏋勶級',
+ADD COLUMN `office_id` BIGINT COMMENT '鍔炰簨澶処D锛堢粍缁囨灦鏋勶級',
+ADD COLUMN `group_leader_id` BIGINT COMMENT '缁勮礋璐d汉ID锛堝憳宸ワ級',
+ADD COLUMN `is_invoice` TINYINT(1) DEFAULT 0 COMMENT '鏄惁寮�鍙戠エ锛�1鏄� 0鍚︼級',
+ADD COLUMN `bid_price` DECIMAL(15,2) COMMENT '涓爣浠�',
+ADD COLUMN `is_bid` TINYINT(1) DEFAULT 0 COMMENT '鏄惁涓爣锛�1鏄� 0鍚︼級',
+ADD COLUMN `unbid_reason` VARCHAR(500) COMMENT '鏈腑鏍囧師鍥�',
+ADD COLUMN `contract_original_return_date` DATE COMMENT '鍚堝悓鍘熶欢绾﹀畾鍥炰紶鏃ユ湡';
diff --git a/doc/bidding_management.sql b/doc/bidding_management.sql
new file mode 100644
index 0000000..aac94d7
--- /dev/null
+++ b/doc/bidding_management.sql
@@ -0,0 +1,26 @@
+-- 鎷涙姇鏍囩鐞嗚〃
+DROP TABLE IF EXISTS `bidding_management`;
+CREATE TABLE `bidding_management` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '涓婚敭ID',
+  `project_id` bigint(20) DEFAULT NULL COMMENT '鎵�灞為」鐩甀D',
+  `project_name` varchar(255) DEFAULT NULL COMMENT '鎵�灞為」鐩悕绉�',
+  `bid_doc_name` varchar(255) DEFAULT NULL COMMENT '鏍囦功鍚嶇О',
+  `tender_unit` varchar(255) DEFAULT NULL COMMENT '鎷涙爣鍗曚綅',
+  `bid_unit` varchar(255) DEFAULT NULL COMMENT '鎶曟爣鍗曚綅',
+  `tender_date` date DEFAULT NULL COMMENT '鎷涙爣鏃ユ湡',
+  `bid_date` date DEFAULT NULL COMMENT '鎶曟爣鏃ユ湡',
+  `bid_doc_fee` decimal(18,2) DEFAULT NULL COMMENT '鏍囦功娆鹃噾棰�',
+  `bid_doc_fee_status` varchar(50) DEFAULT NULL COMMENT '鏍囦功娆剧姸鎬�',
+  `bid_doc_fee_status_change_date` date DEFAULT NULL COMMENT '鏍囦功娆剧姸鎬佸彉鏇存棩鏈�',
+  `bid_deposit` decimal(18,2) DEFAULT NULL COMMENT '鎶曟爣淇濊瘉閲戦噾棰�',
+  `bid_result` varchar(50) DEFAULT NULL COMMENT '鎷涙姇鏍囩粨鏋�',
+  `tenant_id` bigint(20) DEFAULT NULL COMMENT '绉熸埛ID',
+  `dept_id` bigint(20) DEFAULT NULL COMMENT '閮ㄩ棬ID',
+  `del_flag` char(1) DEFAULT '0' COMMENT '鍒犻櫎鏍囧織锛�0浠h〃瀛樺湪 2浠h〃鍒犻櫎锛�',
+  `create_user` int DEFAULT NULL COMMENT '鍒涘缓鑰�',
+  `create_time` timestamp NULL DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+  `update_user` int DEFAULT NULL COMMENT '鏇存柊鑰�',
+  `update_time` timestamp NULL DEFAULT NULL COMMENT '鏇存柊鏃堕棿',
+  `remark` varchar(500) DEFAULT NULL COMMENT '澶囨敞',
+  PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='鎷涙姇鏍囩鐞嗚〃';
diff --git a/doc/broadband_communication.sql b/doc/broadband_communication.sql
new file mode 100644
index 0000000..4405f6b
--- /dev/null
+++ b/doc/broadband_communication.sql
@@ -0,0 +1,25 @@
+    -- 瀹藉甫涓庨�氫俊绠$悊涓昏〃
+    DROP TABLE IF EXISTS `broadband_communication`;
+    CREATE TABLE `broadband_communication` (
+      `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+      `area` varchar(100) NOT NULL COMMENT '鍖哄煙',
+      `office` varchar(100) NOT NULL COMMENT '鍔炰簨澶�',
+      `resident_staff` varchar(100) NOT NULL COMMENT '椹荤渷浜哄憳',
+      `address` varchar(255) NOT NULL COMMENT '鍦板潃',
+      `install_date` date NOT NULL COMMENT '瀹夎鏃ユ湡',
+      `install_fee` decimal(18,2) DEFAULT '0.00' COMMENT '瀹夎璐�',
+      `payment_cycle` int(11) NOT NULL COMMENT '缂磋垂鍛ㄦ湡(鏈堟暟锛�1/3/6/12)',
+      `payment_method` varchar(50) NOT NULL COMMENT '缂磋垂鏂瑰紡',
+      `prepaid_amount` decimal(18,2) DEFAULT '0.00' COMMENT '棰勫瓨閲戦',
+      `monthly_fee` decimal(18,2) DEFAULT '0.00' COMMENT '鏈堣祫璐�',
+      `package_content` varchar(500) DEFAULT NULL COMMENT '濂楅鍐呭',
+      `next_payment_date` date DEFAULT NULL COMMENT '涓嬫缂磋垂鏃ユ湡',
+      `create_user` int(11) DEFAULT NULL COMMENT '鍒涘缓鑰�',
+      `create_time` timestamp NULL DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+      `update_user` int(11) DEFAULT NULL COMMENT '鏇存柊鑰�',
+      `update_time` timestamp NULL DEFAULT NULL COMMENT '鏇存柊鏃堕棿',
+      `tenant_id` int(11) DEFAULT NULL COMMENT '绉熸埛ID',
+      `dept_id` bigint(20) DEFAULT NULL COMMENT '閮ㄩ棬ID',
+      `del_flag` char(1) DEFAULT '0' COMMENT '鍒犻櫎鏍囧織锛�0浠h〃瀛樺湪 2浠h〃鍒犻櫎锛�',
+      PRIMARY KEY (`id`)
+    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='瀹藉甫涓庨�氫俊绠$悊琛�';
diff --git a/doc/house_lease.sql b/doc/house_lease.sql
new file mode 100644
index 0000000..0cb2eba
--- /dev/null
+++ b/doc/house_lease.sql
@@ -0,0 +1,51 @@
+-- 鎴垮眿绉熻祦涓昏〃
+DROP TABLE IF EXISTS `house_lease`;
+CREATE TABLE `house_lease` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+  `office` varchar(100) NOT NULL COMMENT '鍔炰簨澶�',
+  `user_name` varchar(100) NOT NULL COMMENT '浣跨敤浜�',
+  `resident_staff` varchar(100) NOT NULL COMMENT '椹荤渷浜哄憳',
+  `address` varchar(255) NOT NULL COMMENT '鍦板潃',
+  `rent_start_date` date NOT NULL COMMENT '璧风鏃堕棿',
+  `contract_start_date` date NOT NULL COMMENT '鍚堝悓璧风鏃�',
+  `contract_end_date` date NOT NULL COMMENT '鍚堝悓鍒版湡鏃�',
+  `tenant` varchar(100) NOT NULL COMMENT '鎵跨浜�',
+  `party_a` varchar(100) NOT NULL COMMENT '鍚堝悓鐢叉柟',
+  `phone` varchar(20) DEFAULT NULL COMMENT '鐢佃瘽',
+  `area` decimal(18,2) DEFAULT NULL COMMENT '闈㈢Н',
+  `payment_method` varchar(50) NOT NULL COMMENT '鏀粯鏂瑰紡',
+  `deposit` decimal(18,2) DEFAULT NULL COMMENT '鎶奸噾',
+  `rent` decimal(18,2) NOT NULL COMMENT '绉熼噾锛堝厓/鏈堬級',
+  `create_user` int DEFAULT NULL COMMENT '鍒涘缓鑰�',
+  `create_time` timestamp NULL DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+  `update_user` int DEFAULT NULL COMMENT '鏇存柊鑰�',
+  `update_time` timestamp NULL DEFAULT NULL COMMENT '鏇存柊鏃堕棿',
+  `tenant_id` int DEFAULT NULL COMMENT '绉熸埛ID',
+  `dept_id` bigint DEFAULT NULL COMMENT '閮ㄩ棬ID',
+  `remark` varchar(500) DEFAULT NULL COMMENT '澶囨敞',
+  `del_flag` char(1) DEFAULT '0' COMMENT '鍒犻櫎鏍囧織锛�0浠h〃瀛樺湪 2浠h〃鍒犻櫎锛�',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鎴垮眿绉熻祦绠$悊涓昏〃';
+
+-- 鎴垮眿绉熻祦璐圭敤瀛愯〃
+DROP TABLE IF EXISTS `house_lease_cost`;
+CREATE TABLE `house_lease_cost` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+  `lease_id` bigint(20) NOT NULL COMMENT '绉熻祦琛↖D',
+  `month` varchar(7) NOT NULL COMMENT '鏈堜唤 (YYYY-MM)',
+  `rent` decimal(18,2) DEFAULT '0.00' COMMENT '鎴跨',
+  `water_electricity` decimal(18,2) DEFAULT '0.00' COMMENT '姘寸數',
+  `property` decimal(18,2) DEFAULT '0.00' COMMENT '鐗╀笟',
+  `express` decimal(18,2) DEFAULT '0.00' COMMENT '蹇��',
+  `broadband` decimal(18,2) DEFAULT '0.00' COMMENT '瀹藉甫',
+  `create_user` int DEFAULT NULL COMMENT '鍒涘缓鑰�',
+  `create_time` timestamp NULL DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+  `update_user` int DEFAULT NULL COMMENT '鏇存柊鑰�',
+  `update_time` timestamp NULL DEFAULT NULL COMMENT '鏇存柊鏃堕棿',
+  `tenant_id` int DEFAULT NULL COMMENT '绉熸埛ID',
+  `dept_id` bigint DEFAULT NULL COMMENT '閮ㄩ棬ID',
+  `remark` varchar(500) DEFAULT NULL COMMENT '澶囨敞',
+  `del_flag` char(1) DEFAULT '0' COMMENT '鍒犻櫎鏍囧織锛�0浠h〃瀛樺湪 2浠h〃鍒犻櫎锛�',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `idx_lease_month` (`lease_id`, `month`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鎴垮眿绉熻祦绠$悊璐圭敤瀛愯〃';
diff --git a/doc/project_management_config.sql b/doc/project_management_config.sql
new file mode 100644
index 0000000..0982c26
--- /dev/null
+++ b/doc/project_management_config.sql
@@ -0,0 +1,15 @@
+CREATE TABLE `project_management_config` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+  `contract_return_reminder_days` int(11) DEFAULT '3' COMMENT '鍚堝悓鍘熶欢绾﹀畾鍥炰紶鎻愬墠鎻愰啋澶╂暟',
+  `create_user` int(11) DEFAULT NULL COMMENT '鍒涘缓鐢ㄦ埛',
+  `create_user_name` varchar(64) DEFAULT NULL COMMENT '鍒涘缓鐢ㄦ埛鍚嶇О',
+  `create_time` datetime DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+  `update_user` int(11) DEFAULT NULL COMMENT '鏇存柊鐢ㄦ埛',
+  `update_user_name` varchar(64) DEFAULT NULL COMMENT '鏇存柊鐢ㄦ埛鍚嶇О',
+  `update_time` datetime DEFAULT NULL COMMENT '鏇存柊鏃堕棿',
+  `tenant_id` bigint(20) DEFAULT NULL COMMENT '绉熸埛ID',
+  `dept_id` bigint(20) DEFAULT NULL COMMENT '閮ㄩ棬ID',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='椤圭洰绠$悊閰嶇疆琛�';
+
+INSERT INTO `project_management_config` (`id`, `contract_return_reminder_days`) VALUES (1, 3);
diff --git a/doc/sales_contract.sql b/doc/sales_contract.sql
new file mode 100644
index 0000000..080fb21
--- /dev/null
+++ b/doc/sales_contract.sql
@@ -0,0 +1,58 @@
+-- 鍚堝悓绠$悊涓昏〃
+DROP TABLE IF EXISTS `sales_contract`;
+CREATE TABLE `sales_contract` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+  `contract_no` varchar(100) NOT NULL COMMENT '鍚堝悓缂栧彿',
+  `sign_date` date NOT NULL COMMENT '绛捐鏃堕棿',
+  `signing_unit` varchar(255) NOT NULL COMMENT '绛剧害鍗曚綅绠�绉�',
+  `contract_period` varchar(100) DEFAULT NULL COMMENT '绛剧害鍛ㄦ湡',
+  `archive_status` varchar(50) NOT NULL COMMENT '瀛樻。鐘舵��',
+  `archive_remark` varchar(500) DEFAULT NULL COMMENT '瀛樻。鎯呭喌璇存槑',
+  `performance_bond` decimal(18,2) DEFAULT NULL COMMENT '灞ョ害淇濊瘉閲�',
+  `warranty_deposit` decimal(18,2) DEFAULT NULL COMMENT '璐ㄤ繚閲戞儏鍐�',
+  `output_value` decimal(18,2) DEFAULT NULL COMMENT '浜у�硷紙鑷姩姹囨�伙級',
+  `received_amount` decimal(18,2) DEFAULT NULL COMMENT '宸插洖娆鹃噾棰�',
+  `settlement_count` int(11) DEFAULT NULL COMMENT '缁撶畻娆℃暟',
+  `receivable_amount` decimal(18,2) DEFAULT NULL COMMENT '搴旀敹閲戦',
+  `payable_ratio` varchar(50) DEFAULT NULL COMMENT '搴斾粯姣斾緥',
+  `node_receivable` decimal(18,2) DEFAULT NULL COMMENT '鍚堝悓鑺傜偣搴旀敹',
+  `payment_nodes` varchar(255) DEFAULT NULL COMMENT '鍚堝悓绾﹀畾浠樻鑺傜偣',
+  `settlement_record` text COMMENT '缁撶畻璁板綍',
+  `invoice_requirement` varchar(255) DEFAULT NULL COMMENT '寮�绁ㄨ姹�',
+  `last_invoice_date` date DEFAULT NULL COMMENT '鏈�杩戜竴娆″紑绁ㄦ椂闂�',
+  `invoiced_amount` decimal(18,2) DEFAULT NULL COMMENT '宸插紑绁ㄩ噾棰�',
+  `penalty_agreement` text COMMENT '杩濈害閲戠害瀹�',
+  `remark` text COMMENT '鎯呭喌璇存槑',
+  `create_user` int DEFAULT NULL COMMENT '鍒涘缓鑰�',
+  `create_time` timestamp NULL DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+  `update_user` int DEFAULT NULL COMMENT '鏇存柊鑰�',
+  `update_time` timestamp NULL DEFAULT NULL COMMENT '鏇存柊鏃堕棿',
+  `tenant_id` int DEFAULT NULL COMMENT '绉熸埛ID',
+  `dept_id` bigint DEFAULT NULL COMMENT '閮ㄩ棬ID',
+  `del_flag` char(1) DEFAULT '0' COMMENT '鍒犻櫎鏍囧織锛�0浠h〃瀛樺湪 2浠h〃鍒犻櫎锛�',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='閿�鍞悎鍚屼富琛�';
+
+-- 鍚堝悓浜у搧鏄庣粏琛�
+DROP TABLE IF EXISTS `sales_contract_product`;
+CREATE TABLE `sales_contract_product` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+  `contract_id` bigint(20) NOT NULL COMMENT '鍏宠仈閿�鍞悎鍚� ID',
+  `product_category` varchar(100) DEFAULT NULL COMMENT '浜у搧澶х被',
+  `specification_model` varchar(100) DEFAULT NULL COMMENT '瑙勬牸鍨嬪彿',
+  `product_model_id` bigint(20) DEFAULT NULL COMMENT '瑙勬牸鍨嬪彿 ID',
+  `tax_inclusive_unit_price` decimal(18,2) DEFAULT NULL COMMENT '鍚◣鍗曚环',
+  `agreed_quantity` decimal(18,2) DEFAULT NULL COMMENT '绾﹀畾/鏆備及鐢ㄩ噺',
+  `contract_unit` varchar(50) DEFAULT NULL COMMENT '鍚堝悓璁¢噺鍗曚綅',
+  `shipped_quantity` decimal(18,2) DEFAULT NULL COMMENT '宸插彂璐ф暟閲�',
+  `output_value` decimal(18,2) DEFAULT NULL COMMENT '浜у��',
+  `create_user` int DEFAULT NULL COMMENT '鍒涘缓鑰�',
+  `create_time` timestamp NULL DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+  `update_user` int DEFAULT NULL COMMENT '鏇存柊鑰�',
+  `update_time` timestamp NULL DEFAULT NULL COMMENT '鏇存柊鏃堕棿',
+  `tenant_id` int DEFAULT NULL COMMENT '绉熸埛ID',
+  `dept_id` bigint DEFAULT NULL COMMENT '閮ㄩ棬ID',
+  `del_flag` char(1) DEFAULT '0' COMMENT '鍒犻櫎鏍囧織锛�0浠h〃瀛樺湪 2浠h〃鍒犻櫎锛�',
+  PRIMARY KEY (`id`),
+  KEY `idx_contract_id` (`contract_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='閿�鍞悎鍚屼骇鍝佹槑缁嗚〃';
diff --git a/doc/sales_lawsuit.sql b/doc/sales_lawsuit.sql
new file mode 100644
index 0000000..eb70f25
--- /dev/null
+++ b/doc/sales_lawsuit.sql
@@ -0,0 +1,26 @@
+-- 璇夎绠$悊琛�
+DROP TABLE IF EXISTS `sales_lawsuit`;
+CREATE TABLE `sales_lawsuit` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+  `case_no` varchar(100) NOT NULL COMMENT '妗堜欢缂栧彿',
+  `project_id` bigint(20) NOT NULL COMMENT '鎵�灞為」鐩� ID',
+  `project_name` varchar(255) DEFAULT NULL COMMENT '鎵�灞為」鐩悕绉�',
+  `contract_id` bigint(20) NOT NULL COMMENT '鍏宠仈閿�鍞悎鍚� ID',
+  `contract_no` varchar(100) DEFAULT NULL COMMENT '鍚堝悓缂栧彿',
+  `lawsuit_status` varchar(50) NOT NULL COMMENT '璇夎鐘舵��',
+  `filing_date` date NOT NULL COMMENT '绔嬫鏃堕棿',
+  `lawyer` varchar(100) DEFAULT NULL COMMENT '鎵垮姙寰嬪笀/娉曞姟璐熻矗浜�',
+  `hearing_date` date DEFAULT NULL COMMENT '寮�搴椂闂�',
+  `claim_amount` decimal(18,2) DEFAULT NULL COMMENT '璇夎鏍囩殑閲戦',
+  `judgment_summary` text COMMENT '鍒ゅ喅缁撴灉鎽樿',
+  `progress_remark` text COMMENT '璇夎杩涘睍澶囨敞',
+  `create_user` int DEFAULT NULL COMMENT '鍒涘缓鑰�',
+  `create_time` timestamp NULL DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+  `update_user` int DEFAULT NULL COMMENT '鏇存柊鑰�',
+  `update_time` timestamp NULL DEFAULT NULL COMMENT '鏇存柊鏃堕棿',
+  `tenant_id` int DEFAULT NULL COMMENT '绉熸埛ID',
+  `dept_id` bigint DEFAULT NULL COMMENT '閮ㄩ棬ID',
+  `remark` varchar(500) DEFAULT NULL COMMENT '澶囨敞',
+  `del_flag` char(1) DEFAULT '0' COMMENT '鍒犻櫎鏍囧織锛�0浠h〃瀛樺湪 2浠h〃鍒犻櫎锛�',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='璇夎绠$悊涓昏〃';
diff --git a/doc/sales_ledger_contract_rel.sql b/doc/sales_ledger_contract_rel.sql
new file mode 100644
index 0000000..7c901bc
--- /dev/null
+++ b/doc/sales_ledger_contract_rel.sql
@@ -0,0 +1,13 @@
+CREATE TABLE `sales_ledger_contract_rel` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+  `sales_ledger_id` bigint(20) NOT NULL COMMENT '閿�鍞彴璐D',
+  `contract_id` bigint(20) NOT NULL COMMENT '鍚堝悓ID',
+  `create_user` int(11) DEFAULT NULL COMMENT '鍒涘缓鐢ㄦ埛',
+  `create_time` datetime DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+  `update_user` int(11) DEFAULT NULL COMMENT '鏇存柊鐢ㄦ埛',
+  `update_time` datetime DEFAULT NULL COMMENT '鏇存柊鏃堕棿',
+  `del_flag` char(1) DEFAULT '0' COMMENT '鍒犻櫎鏍囧織锛�0浠h〃瀛樺湪 2浠h〃鍒犻櫎锛�',
+  PRIMARY KEY (`id`),
+  KEY `idx_sales_ledger_id` (`sales_ledger_id`),
+  KEY `idx_contract_id` (`contract_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='閿�鍞彴璐︿笌鍚堝悓鍏宠仈琛�';
diff --git a/pom.xml b/pom.xml
index e840acd..0c7442b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -427,6 +427,12 @@
             <version>${thumbnailator.version}</version>
         </dependency>
 
+        <dependency>
+            <groupId>e-iceblue</groupId>
+            <artifactId>spire.doc.free</artifactId>
+            <version>5.2.0</version>
+        </dependency>
+
     </dependencies>
 
     <build>
@@ -464,6 +470,11 @@
                 <enabled>true</enabled>
             </releases>
         </repository>
+        <repository>
+            <id>com.e-iceblue</id>
+            <name>e-iceblue</name>
+            <url>https://repo.e-iceblue.cn/repository/maven-public/</url>
+        </repository>
     </repositories>
 
     <pluginRepositories>
diff --git a/sql/vehicle_alter.sql b/sql/vehicle_alter.sql
new file mode 100644
index 0000000..fb2cfb1
--- /dev/null
+++ b/sql/vehicle_alter.sql
@@ -0,0 +1 @@
+ALTER TABLE `p_fuel_record` ADD COLUMN `dept_id` bigint(20) DEFAULT NULL COMMENT '鎵�灞為儴闂�';
diff --git a/sql/vehicle_init.sql b/sql/vehicle_init.sql
new file mode 100644
index 0000000..2929a73
--- /dev/null
+++ b/sql/vehicle_init.sql
@@ -0,0 +1,74 @@
+CREATE TABLE IF NOT EXISTS `p_vehicle_info` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '涓婚敭',
+  `plate_number` varchar(50) NOT NULL COMMENT '杞︾墝鍙�',
+  `dept_id` bigint(20) DEFAULT NULL COMMENT '鎵�灞為攢鍞洟闃燂紙閮ㄩ棬ID锛�',
+  `user_id` bigint(20) DEFAULT NULL COMMENT '璐d换浜篒D锛堢敤鎴稩D锛�',
+  `vehicle_status` varchar(20) DEFAULT 'NORMAL' COMMENT '杞﹁締鐘舵��(NORMAL:鍦ㄧ敤, IDLE:闂茬疆, REPAIR:缁翠慨)',
+  `property_ownership` varchar(50) DEFAULT NULL COMMENT '浜ф潈鎵�鏈�',
+  `inspection_expire_date` date DEFAULT NULL COMMENT '骞存鍒版湡鏃ユ湡',
+  `insurance_expire_date` date DEFAULT NULL COMMENT '淇濋櫓鍒版湡鏃ユ湡',
+  `remark` varchar(500) DEFAULT NULL COMMENT '澶囨敞',
+  `create_user` bigint(20) DEFAULT NULL COMMENT '鍒涘缓浜�',
+  `create_time` datetime DEFAULT NULL COMMENT '鍒涘缓鏃堕棿',
+  `update_user` bigint(20) DEFAULT NULL COMMENT '鏇存柊浜�',
+  `update_time` datetime DEFAULT NULL COMMENT '鏇存柊鏃堕棿',
+  `tenant_id` varchar(20) DEFAULT NULL COMMENT '绉熸埛ID',
+  `del_flag` char(1) DEFAULT '0' COMMENT '鍒犻櫎鏍囧織锛�0浠h〃瀛樺湪 2浠h〃鍒犻櫎锛�',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `idx_plate_number` (`plate_number`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='杞﹁締妗f琛�';
+
+CREATE TABLE IF NOT EXISTS `p_vehicle_cost` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `vehicle_id` bigint(20) NOT NULL COMMENT '杞﹁締ID',
+  `cost_type` varchar(20) NOT NULL COMMENT '璐圭敤绫诲瀷(MAINTENANCE:淇濆吇, INSPECTION:骞存, INSURANCE:淇濋櫓, REPAIR:缁翠慨)',
+  `amount` decimal(10,2) NOT NULL COMMENT '璐圭敤閲戦',
+  `cost_date` date NOT NULL COMMENT '鍙戠敓鏃ユ湡',
+  `remark` varchar(500) DEFAULT NULL COMMENT '澶囨敞/璇︽儏',
+  `create_user` bigint(20) DEFAULT NULL,
+  `create_time` datetime DEFAULT NULL,
+  `update_user` bigint(20) DEFAULT NULL,
+  `update_time` datetime DEFAULT NULL,
+  `tenant_id` varchar(20) DEFAULT NULL,
+  `del_flag` char(1) DEFAULT '0',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='杞﹁締璐圭敤璁板綍琛�';
+
+CREATE TABLE IF NOT EXISTS `p_fuel_card` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `card_number` varchar(100) NOT NULL COMMENT '鍔犳补鍗″彿',
+  `dept_id` bigint(20) DEFAULT NULL COMMENT '鎵�灞炲洟闃�(閮ㄩ棬ID)',
+  `issuer` varchar(100) DEFAULT NULL COMMENT '鍙戝崱鍗曚綅',
+  `initial_balance` decimal(10,2) DEFAULT '0.00' COMMENT '鏈熷垵浣欓',
+  `current_balance` decimal(10,2) DEFAULT '0.00' COMMENT '褰撳墠鍓╀綑浣欓',
+  `status` varchar(20) DEFAULT 'ACTIVE' COMMENT '鐘舵��(ACTIVE:姝e父, CANCELLED:娉ㄩ攢)',
+  `remark` varchar(500) DEFAULT NULL COMMENT '澶囨敞',
+  `create_user` bigint(20) DEFAULT NULL,
+  `create_time` datetime DEFAULT NULL,
+  `update_user` bigint(20) DEFAULT NULL,
+  `update_time` datetime DEFAULT NULL,
+  `tenant_id` varchar(20) DEFAULT NULL,
+  `del_flag` char(1) DEFAULT '0',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `idx_card_number` (`card_number`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鍔犳补鍗$鐞嗚〃';
+
+CREATE TABLE IF NOT EXISTS `p_fuel_record` (
+  `id` bigint(20) NOT NULL AUTO_INCREMENT,
+  `fuel_date` date NOT NULL COMMENT '鍔犳补鏃ユ湡',
+  `amount` decimal(10,2) NOT NULL COMMENT '鍔犳补閲戦',
+  `liters` decimal(10,2) NOT NULL COMMENT '鍔犳补鍗囨暟',
+  `vehicle_id` bigint(20) NOT NULL COMMENT '鍔犳补杞﹁締ID',
+  `fuel_card_id` bigint(20) NOT NULL COMMENT '鍔犳补鍗D',
+  `start_mileage` decimal(12,2) DEFAULT NULL COMMENT '璧峰鍏噷鏁�',
+  `end_mileage` decimal(12,2) DEFAULT NULL COMMENT '缁撴潫鍏噷鏁�',
+  `invoice_no` varchar(100) DEFAULT NULL COMMENT '鍙戠エ鍙风爜',
+  `remark` varchar(500) DEFAULT NULL COMMENT '澶囨敞',
+  `create_user` bigint(20) DEFAULT NULL,
+  `create_time` datetime DEFAULT NULL,
+  `update_user` bigint(20) DEFAULT NULL,
+  `update_time` datetime DEFAULT NULL,
+  `tenant_id` varchar(20) DEFAULT NULL,
+  `del_flag` char(1) DEFAULT '0',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='鍔犳补璁板綍琛�';
diff --git a/src/main/java/com/ruoyi/ai/config/EmbeddingStoreConfig.java b/src/main/java/com/ruoyi/ai/config/EmbeddingStoreConfig.java
index eaa487c..e46a4bb 100644
--- a/src/main/java/com/ruoyi/ai/config/EmbeddingStoreConfig.java
+++ b/src/main/java/com/ruoyi/ai/config/EmbeddingStoreConfig.java
@@ -45,8 +45,8 @@
                 .createIndex(PineconeServerlessIndexConfig.builder()
                         .cloud("AWS")
                         .region("us-east-1")
-                        .dimension(embeddingModel.dimension())
+                        .dimension(1536) // 涓存椂鍐欐缁村害(qwen涓�鑸槸1536)锛岄伩鍏嶉」鐩惎鍔ㄦ椂璋冪敤API瀵艰嚧娆犺垂鎶ラ敊
                         .build())
                 .build();
     }
-}
\ No newline at end of file
+}
diff --git a/src/main/java/com/ruoyi/approve/bean/vo/FinReimbursementVo.java b/src/main/java/com/ruoyi/approve/bean/vo/FinReimbursementVo.java
index 5706ff3..656de54 100644
--- a/src/main/java/com/ruoyi/approve/bean/vo/FinReimbursementVo.java
+++ b/src/main/java/com/ruoyi/approve/bean/vo/FinReimbursementVo.java
@@ -28,6 +28,9 @@
     //瀹℃壒娴佺▼
     private List<ApprovalTask> tasks;
 
+    @com.ruoyi.framework.aspectj.lang.annotation.Excel(name = "褰掑睘閮ㄩ棬")
+    private String deptName;
+
     //瀹℃壒璁板綍
     private List<ApprovalRecord>  records;
     private List<StorageBlobVO> storageBlobVOList;
diff --git a/src/main/java/com/ruoyi/approve/controller/FinReimbursementController.java b/src/main/java/com/ruoyi/approve/controller/FinReimbursementController.java
index a2eecdb..24a0e65 100644
--- a/src/main/java/com/ruoyi/approve/controller/FinReimbursementController.java
+++ b/src/main/java/com/ruoyi/approve/controller/FinReimbursementController.java
@@ -7,10 +7,13 @@
 import com.ruoyi.framework.web.domain.R;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.servlet.http.HttpServletResponse;
 import lombok.AllArgsConstructor;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.List;
+import com.ruoyi.common.utils.poi.ExcelUtil;
 
 /**
  * <p>
@@ -57,4 +60,12 @@
     public R delete(@RequestBody List<Long> ids) {
         return R.ok(finReimbursementService.delete(ids));
     }
+
+    @PostMapping("/export")
+    @Operation(summary = "瀵煎嚭璐圭敤鎶ラ攢鏁版嵁")
+    public void export(HttpServletResponse response, FinReimbursementDto finReimbursementDto) {
+        List<FinReimbursementVo> list = finReimbursementService.list(finReimbursementDto);
+        ExcelUtil<FinReimbursementVo> util = new ExcelUtil<FinReimbursementVo>(FinReimbursementVo.class);
+        util.exportExcel(response, list, "璐圭敤鎶ラ攢鏁版嵁");
+    }
 }
diff --git a/src/main/java/com/ruoyi/approve/mapper/FinReimbursementMapper.java b/src/main/java/com/ruoyi/approve/mapper/FinReimbursementMapper.java
index 19ac354..483246b 100644
--- a/src/main/java/com/ruoyi/approve/mapper/FinReimbursementMapper.java
+++ b/src/main/java/com/ruoyi/approve/mapper/FinReimbursementMapper.java
@@ -9,6 +9,8 @@
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
+import java.util.List;
+
 /**
  * <p>
  * 鎶ラ攢鍗曚富琛� Mapper 鎺ュ彛
@@ -21,4 +23,6 @@
 public interface FinReimbursementMapper extends BaseMapper<FinReimbursement> {
 
     IPage<FinReimbursementVo> listPage(@Param("ew") FinReimbursementDto finReimbursementDto, Page<FinReimbursementVo> page);
+
+    List<FinReimbursementVo> list(@Param("ew") FinReimbursementDto finReimbursementDto);
 }
diff --git a/src/main/java/com/ruoyi/approve/pojo/FinReimbursement.java b/src/main/java/com/ruoyi/approve/pojo/FinReimbursement.java
index 7e5c81f..917b64a 100644
--- a/src/main/java/com/ruoyi/approve/pojo/FinReimbursement.java
+++ b/src/main/java/com/ruoyi/approve/pojo/FinReimbursement.java
@@ -1,6 +1,7 @@
 package com.ruoyi.approve.pojo;
 
 import com.baomidou.mybatisplus.annotation.*;
+import com.ruoyi.framework.aspectj.lang.annotation.Excel;
 import io.swagger.annotations.ApiModel;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Getter;
@@ -39,6 +40,7 @@
      * 鎶ラ攢鍗曞彿
      */
     @Schema(description = "鎶ラ攢鍗曞彿")
+    @Excel(name = "鎶ラ攢鍗曞彿")
     private String billNo;
 
     /**
@@ -51,6 +53,7 @@
      * 璐圭敤绫诲瀷锛氬樊鏃呰垂/鍔炲叕閲囪喘/涓氬姟鎷涘緟/浜ら�氳垂/閫氳璐�/鍏朵粬
      */
     @Schema(description = "璐圭敤绫诲瀷锛氬樊鏃呰垂/鍔炲叕閲囪喘/涓氬姟鎷涘緟/浜ら�氳垂/閫氳璐�/鍏朵粬")
+    @Excel(name = "璐圭敤绫诲瀷")
     private String expenseType;
 
     /**
@@ -63,12 +66,14 @@
      * 鍛樺伐缂栧彿
      */
     @Schema(description = "鍛樺伐缂栧彿")
+    @Excel(name = "鐢宠浜虹紪鍙�")
     private String applicantCode;
 
     /**
      * 鍛樺伐濮撳悕
      */
     @Schema(description = "鍛樺伐濮撳悕")
+    @Excel(name = "鐢宠浜哄鍚�")
     private String applicantName;
 
     /**
@@ -87,12 +92,14 @@
      * 鎶ラ攢鍘熷洜
      */
     @Schema(description = "鎶ラ攢鍘熷洜")
+    @Excel(name = "鎶ラ攢鍘熷洜")
     private String reason;
 
     /**
      * 鐢宠閲戦
      */
     @Schema(description = "鐢宠閲戦")
+    @Excel(name = "鎶ラ攢閲戦")
     private BigDecimal applyAmount;
 
     /**
@@ -100,6 +107,13 @@
      */
     @Schema(description = "鏄庣粏姹囨�婚噾棰�")
     private BigDecimal detailTotalAmount;
+
+    /**
+     * 浠樻鏂�
+     */
+    @Schema(description = "浠樻鏂�")
+    @Excel(name = "浠樻鏂�")
+    private String payer;
 
     /**
      * 鏀舵浜�
@@ -135,6 +149,7 @@
      * 鍗曟嵁鐘舵�侊細DRAFT-鑽夌锛孖N_APPROVAL-瀹℃壒涓紝APPROVED-瀹℃壒閫氳繃锛孯EJECTED-瀹℃壒椹冲洖锛學ITHDRAWN-宸叉挙鍥烇紝PAID-宸蹭粯娆�
      */
     @Schema(description = "鍗曟嵁鐘舵�侊細DRAFT-鑽夌锛孖N_APPROVAL-瀹℃壒涓紝APPROVED-瀹℃壒閫氳繃锛孯EJECTED-瀹℃壒椹冲洖锛學ITHDRAWN-宸叉挙鍥烇紝PAID-宸蹭粯娆�")
+    @Excel(name = "鍗曟嵁鐘舵��")
     private String billStatus;
 
     /**
@@ -178,6 +193,7 @@
      * 鍒涘缓鏃堕棿
      */
     @Schema(description = "鍒涘缓鏃堕棿")
+    @Excel(name = "鐢宠鏃堕棿", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
     private LocalDateTime createTime;
 
     /**
diff --git a/src/main/java/com/ruoyi/approve/service/FinReimbursementService.java b/src/main/java/com/ruoyi/approve/service/FinReimbursementService.java
index c14b943..9491aad 100644
--- a/src/main/java/com/ruoyi/approve/service/FinReimbursementService.java
+++ b/src/main/java/com/ruoyi/approve/service/FinReimbursementService.java
@@ -21,6 +21,8 @@
 
     IPage<FinReimbursementVo> listPage(FinReimbursementDto finReimbursementDto, Page<FinReimbursementVo> page);
 
+    List<FinReimbursementVo> list(FinReimbursementDto finReimbursementDto);
+
     Boolean add(FinReimbursementDto finReimbursementDto);
 
     FinReimbursementVo detail(Long id);
diff --git a/src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceServiceImpl.java b/src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceServiceImpl.java
index 62d083d..6d3b8d6 100644
--- a/src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceServiceImpl.java
+++ b/src/main/java/com/ruoyi/approve/service/impl/ApprovalInstanceServiceImpl.java
@@ -48,6 +48,11 @@
 import com.ruoyi.sales.pojo.ShippingInfo;
 import com.ruoyi.staff.mapper.HolidayApplicationMapper;
 import com.ruoyi.staff.pojo.HolidayApplication;
+import com.alibaba.fastjson2.JSONObject;
+import com.ruoyi.device.pojo.DeviceStatusChangeRecord;
+import com.ruoyi.device.service.IDeviceStatusChangeRecordService;
+import com.ruoyi.device.pojo.DeviceLedger;
+import com.ruoyi.device.service.IDeviceLedgerService;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -94,6 +99,8 @@
     private final EnterpriseNewsMapper enterpriseNewsMapper;
     private final EnterpriseNewsScopeDeptMapper enterpriseNewsScopeDeptMapper;
     private final ApprovalTemplateNodeApproverMapper approvalTemplateNodeApproverMapper;
+    private final IDeviceStatusChangeRecordService deviceStatusChangeRecordService;
+    private final IDeviceLedgerService deviceLedgerService;
 
     @Override
     public R listPage(Page<ApprovalInstanceVo> page, ApprovalInstanceDto approvalInstanceDto) {
@@ -167,6 +174,63 @@
         approvalInstanceDto.setInstanceNo(instanceNo);
         approvalInstanceDto.setStatus("PENDING");
         approvalInstanceDto.setCurrentLevel(1);
+
+        if (approvalInstanceDto.getApplicantId() == null) {
+            approvalInstanceDto.setApplicantId(SecurityUtils.getUserId());
+            LoginUser loginUser = SecurityUtils.getLoginUser();
+            if (loginUser != null && loginUser.getUser() != null) {
+                approvalInstanceDto.setApplicantName(loginUser.getUser().getNickName());
+            }
+        }
+
+        // 鏍¢獙璁惧鐘舵�佸彉鏇村鎵癸細濡傛灉璇ヨ澶囧凡鏈夋鍦ㄥ鎵圭殑浠诲姟锛屾棤娉曞啀娆¤繘琛屽鎵�
+        if (TypeEnums.DEVICE_STATUS_CHANGE_APPROVAL.getCode().equals(approvalInstanceDto.getBusinessType())) {
+            Long deviceId = approvalInstanceDto.getBusinessId();
+
+            long pendingCount = deviceStatusChangeRecordService.count(Wrappers.<DeviceStatusChangeRecord>lambdaQuery()
+                    .eq(DeviceStatusChangeRecord::getDeviceId, deviceId)
+                    .eq(DeviceStatusChangeRecord::getApprovalStatus, "PENDING")
+                    .eq(DeviceStatusChangeRecord::getDeleted, 0)
+            );
+            if (pendingCount > 0) {
+                throw new ServiceException("璇ヨ澶囧凡鏈夋鍦ㄨ繘琛岀殑鐘舵�佸彉鏇村鎵癸紝鏃犳硶鍐嶆鎻愪氦");
+            }
+
+            DeviceLedger device = deviceLedgerService.getById(deviceId);
+            if (device == null) {
+                throw new ServiceException("璁惧涓嶅瓨鍦�");
+            }
+
+            Integer targetStatus = null;
+            String reason = "";
+            if (StringUtils.hasText(approvalInstanceDto.getFormConfig())) {
+                try {
+                    JSONObject formObj = JSONObject.parseObject(approvalInstanceDto.getFormConfig());
+                    if (formObj.containsKey("targetStatus")) {
+                        targetStatus = formObj.getInteger("targetStatus");
+                    } else if (formObj.containsKey("status")) {
+                        targetStatus = formObj.getInteger("status");
+                    }
+                    if (formObj.containsKey("reason")) {
+                        reason = formObj.getString("reason");
+                    }
+                } catch (Exception e) {
+                    log.error("瑙f瀽鐘舵�佸彉鏇磋〃鍗曞け璐�", e);
+                }
+            }
+
+            DeviceStatusChangeRecord record = new DeviceStatusChangeRecord();
+            record.setDeviceId(deviceId);
+            record.setOriginalStatus(Integer.valueOf(device.getStatus()));
+            record.setTargetStatus(targetStatus);
+            record.setReason(reason);
+            record.setApprovalStatus("PENDING");
+            deviceStatusChangeRecordService.save(record);
+
+            // 缁戝畾鍒板叧鑱旇褰曠殑涓婚敭
+            approvalInstanceDto.setBusinessId(record.getId());
+        }
+
         boolean saved = this.save(approvalInstanceDto);
         if (!saved) {
             return false;
@@ -284,7 +348,7 @@
         );
         //瀹℃壒鎷掔粷鐨勫鐞�
         if ("REJECTED".equals(approveAction)) {
-            return rejectCurrentNode(instance, currentNode, now);
+            return rejectCurrentNode(instance, currentNode, approvalInstanceDto.getApproveComment(), now);
         }
         if (!approveProcessConfigNodeUtils.canProceedToNextLevel(instance.getId(), currentNode.getApproveType())) {
             return R.ok("瀹℃壒鎴愬姛锛岀瓑寰呭叾浠栧鎵逛汉澶勭悊");
@@ -333,7 +397,7 @@
                 currentInstance.setStatus("APPROVED");
                 currentInstance.setFinishTime(LocalDateTime.now());
                 this.updateById(currentInstance);
-                handleBusinessAfterApprovalFinished(currentInstance);
+                handleBusinessAfterApprovalFinished(currentInstance, null);
                 return R.ok("瀹℃壒宸插畬鎴�");
             }
 
@@ -410,7 +474,7 @@
         );
     }
 
-    private R rejectCurrentNode(ApprovalInstance instance, ApprovalInstanceNode currentNode, LocalDateTime now) {
+    private R rejectCurrentNode(ApprovalInstance instance, ApprovalInstanceNode currentNode, String approveComment, LocalDateTime now) {
         if (!updateCurrentNodeStatus(currentNode.getId(), "REJECTED", now)) {
             return R.ok("褰撳墠鑺傜偣宸插鐞嗗畬鎴�");
         }
@@ -420,7 +484,7 @@
         instance.setFinishTime(now);
         this.updateById(instance);
         // 缁熶竴澶勭悊涓氬姟鐘舵�佹洿鏂�
-        handleBusinessAfterApprovalFinished(instance);
+        handleBusinessAfterApprovalFinished(instance, approveComment);
         return R.ok("瀹℃壒宸查┏鍥�");
     }
 
@@ -484,7 +548,7 @@
             instance.setStatus("APPROVED");
             instance.setFinishTime(now);
             this.updateById(instance);
-            handleBusinessAfterApprovalFinished(instance);
+            handleBusinessAfterApprovalFinished(instance, null);
             return R.ok("瀹℃壒宸插畬鎴�");
         }
 
@@ -521,7 +585,7 @@
         );
     }
 
-    private void handleBusinessAfterApprovalFinished(ApprovalInstance instance) {
+    private void handleBusinessAfterApprovalFinished(ApprovalInstance instance, String rejectReason) {
         String status = instance.getStatus();
         Long businessType = instance.getBusinessType();
         if (TypeEnums.PURCHASE_APPROVAL.getCode().equals(businessType)) {
@@ -549,7 +613,39 @@
         }
         if (TypeEnums.ENTERPRISE_NEWS_APPROVAL.getCode().equals(businessType)) {
             handleNewsApprovalFinished(instance, status);
+            return;
         }
+        if (TypeEnums.DEVICE_STATUS_CHANGE_APPROVAL.getCode().equals(businessType)) {
+            handleDeviceStatusChangeApprovalFinished(instance, status, rejectReason);
+            return;
+        }
+    }
+
+    private void handleDeviceStatusChangeApprovalFinished(ApprovalInstance instance, String status, String rejectReason) {
+        if (instance == null || instance.getBusinessId() == null) {
+            return;
+        }
+        DeviceStatusChangeRecord record = deviceStatusChangeRecordService.getById(instance.getBusinessId());
+        if (record == null) {
+            return;
+        }
+        if ("APPROVED".equals(status)) {
+            record.setApprovalStatus("APPROVED");
+            if (record.getTargetStatus() != null) {
+                DeviceLedger device = new DeviceLedger();
+                device.setId(record.getDeviceId());
+                device.setStatus(String.valueOf(record.getTargetStatus()));
+                deviceLedgerService.updateById(device);
+            }
+        } else if ("REJECTED".equals(status)) {
+            record.setApprovalStatus("REJECTED");
+            if (rejectReason != null) {
+                record.setRejectReason(rejectReason);
+            }
+        } else if ("PENDING".equals(status)) {
+            record.setApprovalStatus("PENDING");
+        }
+        deviceStatusChangeRecordService.updateById(record);
     }
 
     private void handleReimbursementApprovalFinished(ApprovalInstance instance, String status) {
diff --git a/src/main/java/com/ruoyi/approve/service/impl/FinReimbursementServiceImpl.java b/src/main/java/com/ruoyi/approve/service/impl/FinReimbursementServiceImpl.java
index a3d33f4..bdc217f 100644
--- a/src/main/java/com/ruoyi/approve/service/impl/FinReimbursementServiceImpl.java
+++ b/src/main/java/com/ruoyi/approve/service/impl/FinReimbursementServiceImpl.java
@@ -24,6 +24,8 @@
 import com.ruoyi.common.utils.OrderUtils;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.project.system.service.ISysNoticeService;
+import com.ruoyi.project.system.service.ISysDeptService;
+import com.ruoyi.project.system.domain.SysDept;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -60,6 +62,7 @@
     private final FinReimbursementDetailMapper finReimbursementDetailMapper;
     private final FileUtil fileUtil;
     private final ISysNoticeService sysNoticeService;
+    private final ISysDeptService sysDeptService;
     @Override
     public IPage<FinReimbursementVo> listPage(FinReimbursementDto finReimbursementDto, Page<FinReimbursementVo> page) {
         IPage<FinReimbursementVo> finReimbursementVoIPage = finReimbursementMapper.listPage(finReimbursementDto, page);
@@ -68,6 +71,11 @@
         });
 
         return finReimbursementVoIPage;
+    }
+
+    @Override
+    public List<FinReimbursementVo> list(FinReimbursementDto finReimbursementDto) {
+        return finReimbursementMapper.list(finReimbursementDto);
     }
 
     @Override
@@ -251,6 +259,13 @@
         vo.setDeptId(reimbursement.getDeptId());
         vo.setDeleted(reimbursement.getDeleted());
 
+        if (reimbursement.getDeptId() != null) {
+            SysDept dept = sysDeptService.selectDeptById(reimbursement.getDeptId());
+            if (dept != null) {
+                vo.setDeptName(dept.getDeptName());
+            }
+        }
+
         vo.setDetails(finReimbursementDetailMapper.selectList(
                 new LambdaQueryWrapper<FinReimbursementDetail>()
                         .eq(FinReimbursementDetail::getReimbursementId, reimbursement.getId())
diff --git a/src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java b/src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java
index b0c01bb..0a7733b 100644
--- a/src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java
+++ b/src/main/java/com/ruoyi/basic/enums/RecordTypeEnum.java
@@ -5,6 +5,7 @@
     INSPECTION_TASK("inspection_task"),
     PDA_VERSION("pda_version"),
     SALES_LEDGER("sales_ledger"),
+    SALES_LAWSUIT("sales_lawsuit"),
     SUPPLIER_MANAGE("supplier_manage"),
     APPROVAL_PROCESS("approval_process"),
     QR_CODE_SCAN_RECORDS("qr_code_scan_records"),
diff --git a/src/main/java/com/ruoyi/basic/mapper/CustomerMapper.java b/src/main/java/com/ruoyi/basic/mapper/CustomerMapper.java
index f871b91..a2e1b51 100644
--- a/src/main/java/com/ruoyi/basic/mapper/CustomerMapper.java
+++ b/src/main/java/com/ruoyi/basic/mapper/CustomerMapper.java
@@ -8,10 +8,12 @@
 import com.ruoyi.basic.vo.CustomerVo;
 import com.ruoyi.sales.vo.CustomerTransactionsDetailsVo;
 import com.ruoyi.sales.vo.CustomerTransactionsVo;
+import com.ruoyi.sales.vo.ProjectTransactionsVo;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * 瀹㈡埛妗fMapper鎺ュ彛
@@ -74,7 +76,11 @@
 
     List<CustomerVo> list(@Param("c") CustomerDto customer, @Param("loginUserId") Long loginUserId);
 
-    IPage<CustomerTransactionsVo> customewTransactions(Page page, @Param("customerName") String customerName);
+    IPage<ProjectTransactionsVo> projectTransactions(Page page, @Param("projectName") String projectName, @Param("customerName") String customerName, @Param("contractName") String contractName);
 
-    IPage<CustomerTransactionsDetailsVo> customewTransactionsDetails(Page page, @Param("customerId") Long customerId);
+    IPage<CustomerTransactionsVo> customewTransactions(Page page, @Param("projectId") Long projectId, @Param("customerName") String customerName, @Param("contractName") String contractName);
+
+    IPage<CustomerTransactionsDetailsVo> customewTransactionsDetails(Page page, @Param("projectId") Long projectId, @Param("customerId") Long customerId, @Param("contractName") String contractName);
+
+    Map<String, Object> transactionsSummary();
 }
diff --git a/src/main/java/com/ruoyi/basic/service/ICustomerService.java b/src/main/java/com/ruoyi/basic/service/ICustomerService.java
index 27e4739..020396b 100644
--- a/src/main/java/com/ruoyi/basic/service/ICustomerService.java
+++ b/src/main/java/com/ruoyi/basic/service/ICustomerService.java
@@ -9,6 +9,7 @@
 import com.ruoyi.framework.web.domain.R;
 import com.ruoyi.sales.vo.CustomerTransactionsDetailsVo;
 import com.ruoyi.sales.vo.CustomerTransactionsVo;
+import com.ruoyi.sales.vo.ProjectTransactionsVo;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.util.List;
@@ -101,14 +102,11 @@
      * @param page
      * @param customerName
      * @return
-     */
-    IPage<CustomerTransactionsVo> customewTransactions(Page page, String customerName);
+     */    IPage<ProjectTransactionsVo> projectTransactions(Page page, String projectName, String customerName, String contractName);
 
-    /**
-     * 鏌ヨ瀹㈡埛寰�鏉ユ槑缁嗗垪琛�
-     * @param page
-     * @param customerId
-     * @return
-     */
-    IPage<CustomerTransactionsDetailsVo> customewTransactionsDetails(Page page, Long customerId);
+    IPage<CustomerTransactionsVo> customewTransactions(Page page, Long projectId, String customerName, String contractName);
+
+    IPage<CustomerTransactionsDetailsVo> customewTransactionsDetails(Page page, Long projectId, Long customerId, String contractName);
+
+    Map<String, Object> transactionsSummary();
 }
diff --git a/src/main/java/com/ruoyi/basic/service/impl/CustomerServiceImpl.java b/src/main/java/com/ruoyi/basic/service/impl/CustomerServiceImpl.java
index 5a4d19a..202d627 100644
--- a/src/main/java/com/ruoyi/basic/service/impl/CustomerServiceImpl.java
+++ b/src/main/java/com/ruoyi/basic/service/impl/CustomerServiceImpl.java
@@ -29,6 +29,7 @@
 import com.ruoyi.sales.pojo.SalesQuotation;
 import com.ruoyi.sales.vo.CustomerTransactionsDetailsVo;
 import com.ruoyi.sales.vo.CustomerTransactionsVo;
+import com.ruoyi.sales.vo.ProjectTransactionsVo;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.BeanUtils;
@@ -452,13 +453,23 @@
     }
 
     @Override
-    public IPage<CustomerTransactionsVo> customewTransactions(Page page, String customerName) {
-        return customerMapper.customewTransactions(page, customerName);
+    public IPage<ProjectTransactionsVo> projectTransactions(Page page, String projectName, String customerName, String contractName) {
+        return customerMapper.projectTransactions(page, projectName, customerName, contractName);
     }
 
     @Override
-    public IPage<CustomerTransactionsDetailsVo> customewTransactionsDetails(Page page, Long customerId) {
-        return customerMapper.customewTransactionsDetails(page, customerId);
+    public IPage<CustomerTransactionsVo> customewTransactions(Page page, Long projectId, String customerName, String contractName) {
+        return customerMapper.customewTransactions(page, projectId, customerName, contractName);
+    }
+
+    @Override
+    public IPage<CustomerTransactionsDetailsVo> customewTransactionsDetails(Page page, Long projectId, Long customerId, String contractName) {
+        return customerMapper.customewTransactionsDetails(page, projectId, customerId, contractName);
+    }
+
+    @Override
+    public Map<String, Object> transactionsSummary() {
+        return customerMapper.transactionsSummary();
     }
 
     /**
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/controller/BroadbandCommunicationController.java b/src/main/java/com/ruoyi/collaborativeApproval/controller/BroadbandCommunicationController.java
new file mode 100644
index 0000000..f3b1120
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/controller/BroadbandCommunicationController.java
@@ -0,0 +1,50 @@
+package com.ruoyi.collaborativeApproval.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.collaborativeApproval.dto.BroadbandCommunicationDTO;
+import com.ruoyi.collaborativeApproval.pojo.BroadbandCommunication;
+import com.ruoyi.collaborativeApproval.service.BroadbandCommunicationService;
+import com.ruoyi.collaborativeApproval.vo.BroadbandCommunicationVO;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/broadband")
+public class BroadbandCommunicationController {
+
+    @Autowired
+    private BroadbandCommunicationService broadbandCommunicationService;
+
+    @GetMapping("/listPage")
+    public AjaxResult listPage(Page<BroadbandCommunication> page, BroadbandCommunicationDTO dto) {
+        IPage<BroadbandCommunicationVO> result = broadbandCommunicationService.listPage(page, dto);
+        return AjaxResult.success(result);
+    }
+
+    @GetMapping("/getDetail")
+    public AjaxResult getDetail(@RequestParam("id") Long id) {
+        return AjaxResult.success(broadbandCommunicationService.getDetail(id));
+    }
+
+    @PostMapping("/addOrUpdate")
+    public AjaxResult addOrUpdate(@RequestBody BroadbandCommunicationDTO dto) {
+        boolean success = broadbandCommunicationService.addOrUpdate(dto);
+        if (success) {
+            return AjaxResult.success("鎿嶄綔鎴愬姛");
+        }
+        return AjaxResult.error("鎿嶄綔澶辫触");
+    }
+
+    @DeleteMapping("/del")
+    public AjaxResult del(@RequestBody List<Long> ids) {
+        boolean success = broadbandCommunicationService.deleteByIds(ids);
+        if (success) {
+            return AjaxResult.success("鍒犻櫎鎴愬姛");
+        }
+        return AjaxResult.error("鍒犻櫎澶辫触");
+    }
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/controller/HouseLeaseController.java b/src/main/java/com/ruoyi/collaborativeApproval/controller/HouseLeaseController.java
new file mode 100644
index 0000000..1013470
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/controller/HouseLeaseController.java
@@ -0,0 +1,64 @@
+package com.ruoyi.collaborativeApproval.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.collaborativeApproval.dto.HouseLeaseDTO;
+import com.ruoyi.collaborativeApproval.pojo.HouseLease;
+import com.ruoyi.collaborativeApproval.service.HouseLeaseService;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+import jakarta.servlet.http.HttpServletResponse;
+
+import java.util.List;
+
+/**
+ * 鎴垮眿绉熻祦绠$悊 Controller
+ * 
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/houseLease")
+@RequiredArgsConstructor
+@Tag(name = "鎴垮眿绉熻祦绠$悊", description = "鎴垮眿绉熻祦鐩稿叧鎺ュ彛")
+public class HouseLeaseController {
+
+    private final HouseLeaseService houseLeaseService;
+
+    @GetMapping("/listPage")
+    @Operation(summary = "鍒嗛〉鏌ヨ")
+    public AjaxResult listPage(Page<HouseLease> page, HouseLeaseDTO houseLeaseDTO) {
+        return AjaxResult.success(houseLeaseService.listPage(page, houseLeaseDTO));
+    }
+
+    @GetMapping("/getDetail")
+    @Operation(summary = "鏌ヨ璇︽儏锛堝惈璐圭敤瀛愯〃锛�")
+    public AjaxResult getDetail(@RequestParam("id") Long id) {
+        return AjaxResult.success(houseLeaseService.getDetail(id));
+    }
+
+    @PostMapping("/addOrUpdate")
+    @Operation(summary = "鏂板/淇敼锛堝惈璐圭敤瀛愯〃锛�")
+    public AjaxResult addOrUpdate(@RequestBody HouseLeaseDTO houseLeaseDTO) {
+        return AjaxResult.success(houseLeaseService.addOrUpdate(houseLeaseDTO));
+    }
+
+    @DeleteMapping("/del")
+    @Operation(summary = "鎵归噺鍒犻櫎")
+    public AjaxResult del(@RequestBody List<Long> ids) {
+        if (ids == null || ids.isEmpty()) {
+            return AjaxResult.error("璇蜂紶鍏ヨ鍒犻櫎鐨処D");
+        }
+        return AjaxResult.success(houseLeaseService.removeByIds(ids));
+    }
+
+    @PostMapping("/export")
+    @Operation(summary = "瀵煎嚭鏁版嵁")
+    public void export(HttpServletResponse response, HouseLeaseDTO houseLeaseDTO) {
+        List<HouseLease> list = houseLeaseService.getList(houseLeaseDTO);
+        ExcelUtil<HouseLease> util = new ExcelUtil<>(HouseLease.class);
+        util.exportExcel(response, list, "鎴垮眿绉熻祦鏁版嵁");
+    }
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/dto/BroadbandCommunicationDTO.java b/src/main/java/com/ruoyi/collaborativeApproval/dto/BroadbandCommunicationDTO.java
new file mode 100644
index 0000000..8eedecb
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/dto/BroadbandCommunicationDTO.java
@@ -0,0 +1,45 @@
+package com.ruoyi.collaborativeApproval.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+@Data
+public class BroadbandCommunicationDTO {
+
+    private Long id;
+
+    private String area;
+
+    private String office;
+
+    private String residentStaff;
+
+    private String address;
+
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate installDate;
+
+    private BigDecimal installFee;
+
+    private Integer paymentCycle;
+
+    private String paymentMethod;
+
+    private BigDecimal prepaidAmount;
+
+    private BigDecimal monthlyFee;
+
+    private String packageContent;
+
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate nextPaymentDate;
+
+    /**
+     * normal: 姝e父 (>15澶�)
+     * expiring: 鍗冲皢鍒版湡 (0~15澶�)
+     * overdue: 宸查�炬湡 (<0澶�)
+     */
+    private String warnStatus;
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/dto/HouseLeaseDTO.java b/src/main/java/com/ruoyi/collaborativeApproval/dto/HouseLeaseDTO.java
new file mode 100644
index 0000000..6d1330a
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/dto/HouseLeaseDTO.java
@@ -0,0 +1,29 @@
+package com.ruoyi.collaborativeApproval.dto;
+
+import com.ruoyi.collaborativeApproval.pojo.HouseLease;
+import com.ruoyi.collaborativeApproval.pojo.HouseLeaseCost;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+/**
+ * 鎴垮眿绉熻祦璇锋眰 DTO
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class HouseLeaseDTO extends HouseLease {
+    
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 璐圭敤鏄庣粏鍒楄〃 (瀛愯〃)
+     */
+    private List<HouseLeaseCost> costList;
+
+    /**
+     * 鍒版湡棰勮鐘舵�侊細normal(姝e父), expiring(鍗冲皢鍒版湡 0-30澶�), overdue(宸查�炬湡 <0澶�)
+     * 浠呯敤浣滃垪琛ㄦ煡璇㈡潯浠�
+     */
+    private String warnStatus;
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/mapper/BroadbandCommunicationMapper.java b/src/main/java/com/ruoyi/collaborativeApproval/mapper/BroadbandCommunicationMapper.java
new file mode 100644
index 0000000..ec4bda4
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/mapper/BroadbandCommunicationMapper.java
@@ -0,0 +1,8 @@
+package com.ruoyi.collaborativeApproval.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.collaborativeApproval.pojo.BroadbandCommunication;
+
+public interface BroadbandCommunicationMapper extends BaseMapper<BroadbandCommunication> {
+
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/mapper/HouseLeaseCostMapper.java b/src/main/java/com/ruoyi/collaborativeApproval/mapper/HouseLeaseCostMapper.java
new file mode 100644
index 0000000..88e19b9
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/mapper/HouseLeaseCostMapper.java
@@ -0,0 +1,14 @@
+package com.ruoyi.collaborativeApproval.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.collaborativeApproval.pojo.HouseLeaseCost;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 鎴垮眿绉熻祦绠$悊璐圭敤瀛愯〃 Mapper 鎺ュ彛
+ * 
+ * @author ruoyi
+ */
+@Mapper
+public interface HouseLeaseCostMapper extends BaseMapper<HouseLeaseCost> {
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/mapper/HouseLeaseMapper.java b/src/main/java/com/ruoyi/collaborativeApproval/mapper/HouseLeaseMapper.java
new file mode 100644
index 0000000..a80653f
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/mapper/HouseLeaseMapper.java
@@ -0,0 +1,14 @@
+package com.ruoyi.collaborativeApproval.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.collaborativeApproval.pojo.HouseLease;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 鎴垮眿绉熻祦绠$悊 Mapper 鎺ュ彛
+ * 
+ * @author ruoyi
+ */
+@Mapper
+public interface HouseLeaseMapper extends BaseMapper<HouseLease> {
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/pojo/BroadbandCommunication.java b/src/main/java/com/ruoyi/collaborativeApproval/pojo/BroadbandCommunication.java
new file mode 100644
index 0000000..a62d87f
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/pojo/BroadbandCommunication.java
@@ -0,0 +1,68 @@
+package com.ruoyi.collaborativeApproval.pojo;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("broadband_communication")
+public class BroadbandCommunication {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private String area;
+
+    private String office;
+
+    private String residentStaff;
+
+    private String address;
+
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate installDate;
+
+    private BigDecimal installFee;
+
+    private Integer paymentCycle;
+
+    private String paymentMethod;
+
+    private BigDecimal prepaidAmount;
+
+    private BigDecimal monthlyFee;
+
+    private String packageContent;
+
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate nextPaymentDate;
+
+    private String delFlag;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Integer createUser;
+
+    @TableField(fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Integer updateUser;
+
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Integer tenantId;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/pojo/HouseLease.java b/src/main/java/com/ruoyi/collaborativeApproval/pojo/HouseLease.java
new file mode 100644
index 0000000..68c3ab5
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/pojo/HouseLease.java
@@ -0,0 +1,119 @@
+package com.ruoyi.collaborativeApproval.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.framework.aspectj.lang.annotation.Excel;
+import lombok.Data;
+import java.time.LocalDateTime;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+/**
+ * 鎴垮眿绉熻祦绠$悊涓昏〃瀵硅薄 house_lease
+ * 
+ * @author ruoyi
+ */
+@Data
+@TableName("house_lease")
+public class HouseLease {
+
+    private static final long serialVersionUID = 1L;
+
+    /** 涓婚敭 */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 鍔炰簨澶� */
+    @Excel(name = "鍔炰簨澶�")
+    private String office;
+
+    /** 浣跨敤浜� */
+    @Excel(name = "浣跨敤浜�")
+    private String userName;
+
+    /** 椹荤渷浜哄憳 */
+    @Excel(name = "椹荤渷浜哄憳")
+    private String residentStaff;
+
+    /** 鍦板潃 */
+    @Excel(name = "鍦板潃")
+    private String address;
+
+    /** 璧风鏃堕棿 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "璧风鏃堕棿", width = 30, dateFormat = "yyyy-MM-dd")
+    private LocalDate rentStartDate;
+
+    /** 鍚堝悓璧风鏃� */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "鍚堝悓璧风鏃�", width = 30, dateFormat = "yyyy-MM-dd")
+    private LocalDate contractStartDate;
+
+    /** 鍚堝悓鍒版湡鏃� */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "鍚堝悓鍒版湡鏃�", width = 30, dateFormat = "yyyy-MM-dd")
+    private LocalDate contractEndDate;
+
+    /** 鎵跨浜� */
+    @Excel(name = "鎵跨浜�")
+    private String tenant;
+
+    /** 鍚堝悓鐢叉柟 */
+    @Excel(name = "鍚堝悓鐢叉柟")
+    private String partyA;
+
+    /** 鐢佃瘽 */
+    @Excel(name = "鐢佃瘽")
+    private String phone;
+
+    /** 闈㈢Н */
+    @Excel(name = "闈㈢Н")
+    private BigDecimal area;
+
+    /** 鏀粯鏂瑰紡 */
+    @Excel(name = "鏀粯鏂瑰紡")
+    private String paymentMethod;
+
+    /** 鎶奸噾 */
+    @Excel(name = "鎶奸噾")
+    private BigDecimal deposit;
+
+    /** 绉熼噾锛堝厓/鏈堬級 */
+    @Excel(name = "绉熼噾锛堝厓/鏈堬級")
+    private BigDecimal rent;
+
+    /** 澶囨敞 */
+    @Excel(name = "澶囨敞")
+    private String remark;
+
+    /** 鍒涘缓鑰� */
+    @TableField(fill = FieldFill.INSERT)
+    private Integer createUser;
+
+    /** 鍒涘缓鏃堕棿 */
+    @TableField(fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    /** 鏇存柊鑰� */
+    @TableField(fill = FieldFill.UPDATE)
+    private Integer updateUser;
+
+    /** 鏇存柊鏃堕棿 */
+    @TableField(fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+
+    /** 绉熸埛ID */
+    @TableField(fill = FieldFill.INSERT)
+    private Integer tenantId;
+
+    /** 閮ㄩ棬ID */
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+
+    /** 鍒犻櫎鏍囧織锛�0浠h〃瀛樺湪 2浠h〃鍒犻櫎锛� */
+    @TableLogic
+    private String delFlag;
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/pojo/HouseLeaseCost.java b/src/main/java/com/ruoyi/collaborativeApproval/pojo/HouseLeaseCost.java
new file mode 100644
index 0000000..222c29d
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/pojo/HouseLeaseCost.java
@@ -0,0 +1,78 @@
+package com.ruoyi.collaborativeApproval.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import java.time.LocalDateTime;
+
+import java.math.BigDecimal;
+
+/**
+ * 鎴垮眿绉熻祦绠$悊璐圭敤瀛愯〃瀵硅薄 house_lease_cost
+ * 
+ * @author ruoyi
+ */
+@Data
+@TableName("house_lease_cost")
+public class HouseLeaseCost {
+
+    private static final long serialVersionUID = 1L;
+
+    /** 涓婚敭 */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 绉熻祦琛↖D */
+    private Long leaseId;
+
+    /** 鏈堜唤 (YYYY-MM) */
+    private String month;
+
+    /** 鎴跨 */
+    private BigDecimal rent;
+
+    /** 姘寸數 */
+    private BigDecimal waterElectricity;
+
+    /** 鐗╀笟 */
+    private BigDecimal property;
+
+    /** 蹇�� */
+    private BigDecimal express;
+
+    /** 瀹藉甫 */
+    private BigDecimal broadband;
+
+    /** 澶囨敞 */
+    private String remark;
+
+    /** 鍒涘缓鑰� */
+    @TableField(fill = FieldFill.INSERT)
+    private Integer createUser;
+
+    /** 鍒涘缓鏃堕棿 */
+    @TableField(fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    /** 鏇存柊鑰� */
+    @TableField(fill = FieldFill.UPDATE)
+    private Integer updateUser;
+
+    /** 鏇存柊鏃堕棿 */
+    @TableField(fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+
+    /** 绉熸埛ID */
+    @TableField(fill = FieldFill.INSERT)
+    private Integer tenantId;
+
+    /** 閮ㄩ棬ID */
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+
+    /** 鍒犻櫎鏍囧織锛�0浠h〃瀛樺湪 2浠h〃鍒犻櫎锛� */
+    @TableLogic
+    private String delFlag;
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/service/BroadbandCommunicationService.java b/src/main/java/com/ruoyi/collaborativeApproval/service/BroadbandCommunicationService.java
new file mode 100644
index 0000000..359a80c
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/service/BroadbandCommunicationService.java
@@ -0,0 +1,21 @@
+package com.ruoyi.collaborativeApproval.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.collaborativeApproval.dto.BroadbandCommunicationDTO;
+import com.ruoyi.collaborativeApproval.pojo.BroadbandCommunication;
+import com.ruoyi.collaborativeApproval.vo.BroadbandCommunicationVO;
+
+import java.util.List;
+
+public interface BroadbandCommunicationService extends IService<BroadbandCommunication> {
+
+    IPage<BroadbandCommunicationVO> listPage(Page<BroadbandCommunication> page, BroadbandCommunicationDTO dto);
+
+    BroadbandCommunicationVO getDetail(Long id);
+
+    boolean addOrUpdate(BroadbandCommunicationDTO dto);
+
+    boolean deleteByIds(List<Long> ids);
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/service/HouseLeaseCostService.java b/src/main/java/com/ruoyi/collaborativeApproval/service/HouseLeaseCostService.java
new file mode 100644
index 0000000..eda68f8
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/service/HouseLeaseCostService.java
@@ -0,0 +1,12 @@
+package com.ruoyi.collaborativeApproval.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.collaborativeApproval.pojo.HouseLeaseCost;
+
+/**
+ * 鎴垮眿绉熻祦绠$悊璐圭敤瀛愯〃 Service 鎺ュ彛
+ * 
+ * @author ruoyi
+ */
+public interface HouseLeaseCostService extends IService<HouseLeaseCost> {
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/service/HouseLeaseService.java b/src/main/java/com/ruoyi/collaborativeApproval/service/HouseLeaseService.java
new file mode 100644
index 0000000..76ed05b
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/service/HouseLeaseService.java
@@ -0,0 +1,49 @@
+package com.ruoyi.collaborativeApproval.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.collaborativeApproval.dto.HouseLeaseDTO;
+import com.ruoyi.collaborativeApproval.pojo.HouseLease;
+import com.ruoyi.collaborativeApproval.vo.HouseLeaseVO;
+import java.util.List;
+
+/**
+ * 鎴垮眿绉熻祦绠$悊 Service 鎺ュ彛
+ * 
+ * @author ruoyi
+ */
+public interface HouseLeaseService extends IService<HouseLease> {
+
+    /**
+     * 鍒嗛〉鏌ヨ
+     *
+     * @param page 鍒嗛〉瀵硅薄
+     * @param houseLeaseDTO 鏌ヨ鏉′欢
+     * @return 鍒嗛〉缁撴灉
+     */
+    Page<HouseLeaseVO> listPage(Page<HouseLease> page, HouseLeaseDTO houseLeaseDTO);
+
+    /**
+     * 鏌ヨ鍒楄〃 (鐢ㄤ簬瀵煎嚭)
+     *
+     * @param houseLeaseDTO 鏌ヨ鏉′欢
+     * @return 鍒楄〃缁撴灉
+     */
+    List<HouseLease> getList(HouseLeaseDTO houseLeaseDTO);
+
+    /**
+     * 鏌ヨ璇︽儏
+     *
+     * @param id 绉熻祦ID
+     * @return 璇︽儏VO
+     */
+    HouseLeaseVO getDetail(Long id);
+
+    /**
+     * 鏂板鎴栦慨鏀�
+     *
+     * @param houseLeaseDTO 鏁版嵁
+     * @return 鏄惁鎴愬姛
+     */
+    boolean addOrUpdate(HouseLeaseDTO houseLeaseDTO);
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/service/impl/BroadbandCommunicationServiceImpl.java b/src/main/java/com/ruoyi/collaborativeApproval/service/impl/BroadbandCommunicationServiceImpl.java
new file mode 100644
index 0000000..34a4d81
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/service/impl/BroadbandCommunicationServiceImpl.java
@@ -0,0 +1,102 @@
+package com.ruoyi.collaborativeApproval.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.collaborativeApproval.dto.BroadbandCommunicationDTO;
+import com.ruoyi.collaborativeApproval.mapper.BroadbandCommunicationMapper;
+import com.ruoyi.collaborativeApproval.pojo.BroadbandCommunication;
+import com.ruoyi.collaborativeApproval.service.BroadbandCommunicationService;
+import com.ruoyi.collaborativeApproval.vo.BroadbandCommunicationVO;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDate;
+import java.util.List;
+
+@Service
+public class BroadbandCommunicationServiceImpl extends ServiceImpl<BroadbandCommunicationMapper, BroadbandCommunication> implements BroadbandCommunicationService {
+
+    @Override
+    public IPage<BroadbandCommunicationVO> listPage(Page<BroadbandCommunication> page, BroadbandCommunicationDTO dto) {
+        LambdaQueryWrapper<BroadbandCommunication> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.eq(BroadbandCommunication::getDelFlag, "0");
+
+        if (StringUtils.isNotBlank(dto.getArea())) {
+            queryWrapper.like(BroadbandCommunication::getArea, dto.getArea());
+        }
+        if (StringUtils.isNotBlank(dto.getOffice())) {
+            queryWrapper.like(BroadbandCommunication::getOffice, dto.getOffice());
+        }
+        if (StringUtils.isNotBlank(dto.getResidentStaff())) {
+            queryWrapper.like(BroadbandCommunication::getResidentStaff, dto.getResidentStaff());
+        }
+
+        if (StringUtils.isNotBlank(dto.getWarnStatus())) {
+            LocalDate today = LocalDate.now();
+            if ("expiring".equals(dto.getWarnStatus())) {
+                // 鍗冲皢鍒版湡: nextPaymentDate 鍦ㄤ粖澶╁埌15澶╀箣鍚庝箣闂达紝涓斾笉鍖呮嫭灏忎簬浠婂ぉ
+                LocalDate future15 = today.plusDays(15);
+                queryWrapper.ge(BroadbandCommunication::getNextPaymentDate, today)
+                            .le(BroadbandCommunication::getNextPaymentDate, future15);
+            } else if ("overdue".equals(dto.getWarnStatus())) {
+                // 宸查�炬湡: nextPaymentDate < 浠婂ぉ
+                queryWrapper.lt(BroadbandCommunication::getNextPaymentDate, today);
+            } else if ("normal".equals(dto.getWarnStatus())) {
+                // 姝e父: nextPaymentDate > 15澶╀箣鍚�
+                LocalDate future15 = today.plusDays(15);
+                queryWrapper.gt(BroadbandCommunication::getNextPaymentDate, future15);
+            }
+        }
+
+        queryWrapper.orderByDesc(BroadbandCommunication::getCreateTime);
+
+        IPage<BroadbandCommunication> p = this.page(page, queryWrapper);
+        return p.convert(this::convertToVO);
+    }
+
+    @Override
+    public BroadbandCommunicationVO getDetail(Long id) {
+        BroadbandCommunication entity = this.getById(id);
+        if (entity != null && "0".equals(entity.getDelFlag())) {
+            return convertToVO(entity);
+        }
+        return null;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean addOrUpdate(BroadbandCommunicationDTO dto) {
+        BroadbandCommunication entity = new BroadbandCommunication();
+        BeanUtils.copyProperties(dto, entity);
+
+        // Calculate nextPaymentDate automatically if installDate and paymentCycle are present
+        if (entity.getInstallDate() != null && entity.getPaymentCycle() != null) {
+            entity.setNextPaymentDate(entity.getInstallDate().plusMonths(entity.getPaymentCycle()));
+        }
+
+        if (entity.getId() == null) {
+            return this.save(entity);
+        } else {
+            return this.updateById(entity);
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean deleteByIds(List<Long> ids) {
+        if (ids != null && !ids.isEmpty()) {
+            return this.removeByIds(ids);
+        }
+        return false;
+    }
+
+    private BroadbandCommunicationVO convertToVO(BroadbandCommunication entity) {
+        BroadbandCommunicationVO vo = new BroadbandCommunicationVO();
+        BeanUtils.copyProperties(entity, vo);
+        return vo;
+    }
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/service/impl/HouseLeaseCostServiceImpl.java b/src/main/java/com/ruoyi/collaborativeApproval/service/impl/HouseLeaseCostServiceImpl.java
new file mode 100644
index 0000000..6c11550
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/service/impl/HouseLeaseCostServiceImpl.java
@@ -0,0 +1,16 @@
+package com.ruoyi.collaborativeApproval.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.collaborativeApproval.mapper.HouseLeaseCostMapper;
+import com.ruoyi.collaborativeApproval.pojo.HouseLeaseCost;
+import com.ruoyi.collaborativeApproval.service.HouseLeaseCostService;
+import org.springframework.stereotype.Service;
+
+/**
+ * 鎴垮眿绉熻祦绠$悊璐圭敤瀛愯〃 Service 涓氬姟灞傚鐞�
+ * 
+ * @author ruoyi
+ */
+@Service
+public class HouseLeaseCostServiceImpl extends ServiceImpl<HouseLeaseCostMapper, HouseLeaseCost> implements HouseLeaseCostService {
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/service/impl/HouseLeaseServiceImpl.java b/src/main/java/com/ruoyi/collaborativeApproval/service/impl/HouseLeaseServiceImpl.java
new file mode 100644
index 0000000..ae99127
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/service/impl/HouseLeaseServiceImpl.java
@@ -0,0 +1,159 @@
+package com.ruoyi.collaborativeApproval.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.collaborativeApproval.dto.HouseLeaseDTO;
+import com.ruoyi.collaborativeApproval.mapper.HouseLeaseMapper;
+import com.ruoyi.collaborativeApproval.pojo.HouseLease;
+import com.ruoyi.collaborativeApproval.pojo.HouseLeaseCost;
+import com.ruoyi.collaborativeApproval.service.HouseLeaseCostService;
+import com.ruoyi.collaborativeApproval.service.HouseLeaseService;
+import com.ruoyi.collaborativeApproval.vo.HouseLeaseVO;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 鎴垮眿绉熻祦绠$悊 Service 涓氬姟灞傚鐞�
+ * 
+ * @author ruoyi
+ */
+@Service
+@RequiredArgsConstructor
+public class HouseLeaseServiceImpl extends ServiceImpl<HouseLeaseMapper, HouseLease> implements HouseLeaseService {
+
+    private final HouseLeaseCostService houseLeaseCostService;
+
+    @Override
+    public Page<HouseLeaseVO> listPage(Page<HouseLease> page, HouseLeaseDTO houseLeaseDTO) {
+        LambdaQueryWrapper<HouseLease> lqw = Wrappers.lambdaQuery();
+        
+        if (StringUtils.isNotBlank(houseLeaseDTO.getOffice())) {
+            lqw.like(HouseLease::getOffice, houseLeaseDTO.getOffice());
+        }
+        if (StringUtils.isNotBlank(houseLeaseDTO.getUserName())) {
+            lqw.like(HouseLease::getUserName, houseLeaseDTO.getUserName());
+        }
+        
+        // 澶勭悊鍒版湡棰勮鐘舵��
+        String warnStatus = houseLeaseDTO.getWarnStatus();
+        if (StringUtils.isNotBlank(warnStatus)) {
+            LocalDate today = LocalDate.now();
+            LocalDate in30Days = today.plusDays(30);
+            
+            if ("overdue".equals(warnStatus)) {
+                // 宸查�炬湡: 鍚堝悓鍒版湡鏃� < 浠婂ぉ
+                lqw.lt(HouseLease::getContractEndDate, today);
+            } else if ("expiring".equals(warnStatus)) {
+                // 鍗冲皢鍒版湡: 0 <= 鍚堝悓鍒版湡鏃� - 浠婂ぉ <= 30
+                lqw.ge(HouseLease::getContractEndDate, today)
+                   .le(HouseLease::getContractEndDate, in30Days);
+            } else if ("normal".equals(warnStatus)) {
+                // 姝e父: 鍚堝悓鍒版湡鏃� > 浠婂ぉ + 30澶�
+                lqw.gt(HouseLease::getContractEndDate, in30Days);
+            }
+        }
+        
+        lqw.orderByDesc(HouseLease::getCreateTime);
+        
+        Page<HouseLease> entityPage = this.page(page, lqw);
+        
+        // 杞崲鎴怴O鍒嗛〉瀵硅薄
+        Page<HouseLeaseVO> voPage = new Page<>(entityPage.getCurrent(), entityPage.getSize(), entityPage.getTotal());
+        List<HouseLeaseVO> voRecords = entityPage.getRecords().stream()
+                .map(entity -> BeanUtil.copyProperties(entity, HouseLeaseVO.class))
+                .collect(Collectors.toList());
+        voPage.setRecords(voRecords);
+        
+        return voPage;
+    }
+
+    @Override
+    public List<HouseLease> getList(HouseLeaseDTO houseLeaseDTO) {
+        LambdaQueryWrapper<HouseLease> lqw = Wrappers.lambdaQuery();
+        
+        if (StringUtils.isNotBlank(houseLeaseDTO.getOffice())) {
+            lqw.like(HouseLease::getOffice, houseLeaseDTO.getOffice());
+        }
+        if (StringUtils.isNotBlank(houseLeaseDTO.getUserName())) {
+            lqw.like(HouseLease::getUserName, houseLeaseDTO.getUserName());
+        }
+        
+        // 澶勭悊鍒版湡棰勮鐘舵��
+        String warnStatus = houseLeaseDTO.getWarnStatus();
+        if (StringUtils.isNotBlank(warnStatus)) {
+            LocalDate today = LocalDate.now();
+            LocalDate in30Days = today.plusDays(30);
+            
+            if ("overdue".equals(warnStatus)) {
+                lqw.lt(HouseLease::getContractEndDate, today);
+            } else if ("expiring".equals(warnStatus)) {
+                lqw.ge(HouseLease::getContractEndDate, today)
+                   .le(HouseLease::getContractEndDate, in30Days);
+            } else if ("normal".equals(warnStatus)) {
+                lqw.gt(HouseLease::getContractEndDate, in30Days);
+            }
+        }
+        
+        lqw.orderByDesc(HouseLease::getCreateTime);
+        
+        return this.list(lqw);
+    }
+
+    @Override
+    public HouseLeaseVO getDetail(Long id) {
+        HouseLease houseLease = this.getById(id);
+        if (houseLease == null) {
+            return null;
+        }
+        
+        HouseLeaseVO vo = BeanUtil.copyProperties(houseLease, HouseLeaseVO.class);
+        
+        // 鏌ヨ瀛愯〃
+        List<HouseLeaseCost> costList = houseLeaseCostService.list(
+                Wrappers.<HouseLeaseCost>lambdaQuery()
+                        .eq(HouseLeaseCost::getLeaseId, id)
+                        .orderByAsc(HouseLeaseCost::getMonth)
+        );
+        vo.setCostList(costList);
+        
+        return vo;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean addOrUpdate(HouseLeaseDTO houseLeaseDTO) {
+        HouseLease houseLease = BeanUtil.copyProperties(houseLeaseDTO, HouseLease.class);
+        
+        // 1. 淇濆瓨涓昏〃
+        this.saveOrUpdate(houseLease);
+        
+        // 2. 澶勭悊瀛愯〃 (鍏ㄥ垹鍏ㄦ彃妯″紡)
+        // 鍒犻櫎鏃ф暟鎹�
+        houseLeaseCostService.remove(
+                Wrappers.<HouseLeaseCost>lambdaQuery()
+                        .eq(HouseLeaseCost::getLeaseId, houseLease.getId())
+        );
+        
+        // 鎻掑叆鏂版暟鎹�
+        List<HouseLeaseCost> costList = houseLeaseDTO.getCostList();
+        if (costList != null && !costList.isEmpty()) {
+            for (HouseLeaseCost cost : costList) {
+                cost.setId(null); // 纭繚鏄柊澧�
+                cost.setLeaseId(houseLease.getId());
+            }
+            houseLeaseCostService.saveBatch(costList);
+        }
+        
+        return true;
+    }
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/vo/BroadbandCommunicationVO.java b/src/main/java/com/ruoyi/collaborativeApproval/vo/BroadbandCommunicationVO.java
new file mode 100644
index 0000000..1d151ce
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/vo/BroadbandCommunicationVO.java
@@ -0,0 +1,42 @@
+package com.ruoyi.collaborativeApproval.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@Data
+public class BroadbandCommunicationVO {
+
+    private Long id;
+
+    private String area;
+
+    private String office;
+
+    private String residentStaff;
+
+    private String address;
+
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate installDate;
+
+    private BigDecimal installFee;
+
+    private Integer paymentCycle;
+
+    private String paymentMethod;
+
+    private BigDecimal prepaidAmount;
+
+    private BigDecimal monthlyFee;
+
+    private String packageContent;
+
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate nextPaymentDate;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+}
diff --git a/src/main/java/com/ruoyi/collaborativeApproval/vo/HouseLeaseVO.java b/src/main/java/com/ruoyi/collaborativeApproval/vo/HouseLeaseVO.java
new file mode 100644
index 0000000..c83d516
--- /dev/null
+++ b/src/main/java/com/ruoyi/collaborativeApproval/vo/HouseLeaseVO.java
@@ -0,0 +1,23 @@
+package com.ruoyi.collaborativeApproval.vo;
+
+import com.ruoyi.collaborativeApproval.pojo.HouseLease;
+import com.ruoyi.collaborativeApproval.pojo.HouseLeaseCost;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+/**
+ * 鎴垮眿绉熻祦鍝嶅簲 VO
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class HouseLeaseVO extends HouseLease {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 璐圭敤鏄庣粏鍒楄〃 (瀛愯〃)
+     */
+    private List<HouseLeaseCost> costList;
+}
diff --git a/src/main/java/com/ruoyi/common/enums/TypeEnums.java b/src/main/java/com/ruoyi/common/enums/TypeEnums.java
index ba1c1f3..7d4172d 100644
--- a/src/main/java/com/ruoyi/common/enums/TypeEnums.java
+++ b/src/main/java/com/ruoyi/common/enums/TypeEnums.java
@@ -24,7 +24,8 @@
     OVERTIME_APPROVAL(15L, "鍔犵彮瀹℃壒"),
     TRAVEL_REIMBURSEMENT_APPROVAL(16L, "鍑哄樊鎶ラ攢瀹℃壒"),
     EXPENSE_APPROVAL(17L, "璐圭敤瀹℃壒"),
-    ENTERPRISE_NEWS_APPROVAL(18L, "浼佷笟鏂伴椈瀹℃壒");
+    ENTERPRISE_NEWS_APPROVAL(18L, "浼佷笟鏂伴椈瀹℃壒"),
+    DEVICE_STATUS_CHANGE_APPROVAL(19L, "璁惧鐘舵�佸彉鏇村鎵�");
 
 
 
diff --git a/src/main/java/com/ruoyi/device/controller/DeviceStatusChangeRecordController.java b/src/main/java/com/ruoyi/device/controller/DeviceStatusChangeRecordController.java
new file mode 100644
index 0000000..69ddafa
--- /dev/null
+++ b/src/main/java/com/ruoyi/device/controller/DeviceStatusChangeRecordController.java
@@ -0,0 +1,32 @@
+package com.ruoyi.device.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+
+import com.ruoyi.device.pojo.DeviceStatusChangeRecord;
+import com.ruoyi.device.service.IDeviceStatusChangeRecordService;
+import com.ruoyi.framework.web.controller.BaseController;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@Tag(name = "璁惧鐘舵�佸彉鏇磋褰�")
+@RestController
+@RequestMapping("/device/statusChangeRecord")
+public class DeviceStatusChangeRecordController extends BaseController {
+
+    @Autowired
+    private IDeviceStatusChangeRecordService deviceStatusChangeRecordService;
+
+    @Operation(summary = "鍒嗛〉鏌ヨ璁惧鐘舵�佸彉鏇磋褰�")
+    @GetMapping("/list")
+    public AjaxResult list(Page<DeviceStatusChangeRecord> page, DeviceStatusChangeRecord record) {
+        IPage<DeviceStatusChangeRecord> pageList = deviceStatusChangeRecordService.selectPageList(page, record);
+        return AjaxResult.success(pageList);
+    }
+}
diff --git a/src/main/java/com/ruoyi/device/dto/DeviceLedgerDto.java b/src/main/java/com/ruoyi/device/dto/DeviceLedgerDto.java
index 8ceb915..77510dd 100644
--- a/src/main/java/com/ruoyi/device/dto/DeviceLedgerDto.java
+++ b/src/main/java/com/ruoyi/device/dto/DeviceLedgerDto.java
@@ -57,6 +57,42 @@
      */
     private String supplierName;
 
+    @Schema(description = "璁惧缂栧彿")
+    private String uniqueCode;
+
+    @Schema(description = "鍑哄巶缂栧彿")
+    private String factoryNumber;
+
+    @Schema(description = "鍒堕�犲巶鍟�")
+    private String manufacturer;
+
+    @Schema(description = "璧勪骇鍘熷��")
+    private BigDecimal originalAssetValue;
+
+    @Schema(description = "璐疆鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate purchaseDate;
+
+    @Schema(description = "鍚敤鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate activationDate;
+
+    @Schema(description = "涓昏鎶�鏈弬鏁�")
+    private String technicalParameters;
+
+    @Schema(description = "涓昏鐢ㄩ��")
+    private String mainPurpose;
+
+    @Schema(description = "璐d换浜�")
+    private String responsiblePerson;
+
+    @Schema(description = "寤烘。鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate filingDate;
+
     /**
      * 璁惧闄勪欢(鐢ㄤ簬鎺ユ敹)
      */
diff --git a/src/main/java/com/ruoyi/device/execl/DeviceLedgerExeclDto.java b/src/main/java/com/ruoyi/device/execl/DeviceLedgerExeclDto.java
index a9eafa8..68e019f 100644
--- a/src/main/java/com/ruoyi/device/execl/DeviceLedgerExeclDto.java
+++ b/src/main/java/com/ruoyi/device/execl/DeviceLedgerExeclDto.java
@@ -84,10 +84,42 @@
     /**
      * 褰曞叆浜�
      */
-    @Excel(name = "褰曞叆浜�",sort = 9)
+    @Excel(name = "褰曞叆浜�",sort = 10)
     private String createUser;
 
+    @Excel(name = "璁惧缂栧彿", sort = 11)
+    private String uniqueCode;
 
+    @Excel(name = "鍑哄巶缂栧彿", sort = 12)
+    private String factoryNumber;
 
+    @Excel(name = "鍒堕�犲巶鍟�", sort = 13)
+    private String manufacturer;
 
+    @Excel(name = "璧勪骇鍘熷��", sort = 14)
+    private BigDecimal originalAssetValue;
+
+    @Excel(name = "璐疆鏃ユ湡", sort = 15, dateFormat = "yyyy-MM-dd")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private Date purchaseDate;
+
+    @Excel(name = "鍚敤鏃ユ湡", sort = 16, dateFormat = "yyyy-MM-dd")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private Date activationDate;
+
+    @Excel(name = "涓昏鎶�鏈弬鏁�", sort = 17)
+    private String technicalParameters;
+
+    @Excel(name = "涓昏鐢ㄩ��", sort = 18)
+    private String mainPurpose;
+
+    @Excel(name = "璐d换浜�", sort = 19)
+    private String responsiblePerson;
+
+    @Excel(name = "寤烘。鏃ユ湡", sort = 20, dateFormat = "yyyy-MM-dd")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private Date filingDate;
 }
diff --git a/src/main/java/com/ruoyi/device/mapper/DeviceRepairSparePartMapper.java b/src/main/java/com/ruoyi/device/mapper/DeviceRepairSparePartMapper.java
new file mode 100644
index 0000000..425abb6
--- /dev/null
+++ b/src/main/java/com/ruoyi/device/mapper/DeviceRepairSparePartMapper.java
@@ -0,0 +1,15 @@
+package com.ruoyi.device.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.device.pojo.DeviceRepairSparePart;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Select;
+
+import java.util.List;
+
+@Mapper
+public interface DeviceRepairSparePartMapper extends BaseMapper<DeviceRepairSparePart> {
+
+    @Select("SELECT d.*, s.name as spare_part_name FROM device_repair_spare_part d LEFT JOIN spare_parts s ON d.spare_part_id = s.id WHERE d.source_id = #{sourceId} AND d.source_type = #{sourceType}")
+    List<DeviceRepairSparePart> selectListWithPartName(Long sourceId, Integer sourceType);
+}
diff --git a/src/main/java/com/ruoyi/device/mapper/DeviceStatusChangeRecordMapper.java b/src/main/java/com/ruoyi/device/mapper/DeviceStatusChangeRecordMapper.java
new file mode 100644
index 0000000..e4c6d2b
--- /dev/null
+++ b/src/main/java/com/ruoyi/device/mapper/DeviceStatusChangeRecordMapper.java
@@ -0,0 +1,9 @@
+package com.ruoyi.device.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.device.pojo.DeviceStatusChangeRecord;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface DeviceStatusChangeRecordMapper extends BaseMapper<DeviceStatusChangeRecord> {
+}
diff --git a/src/main/java/com/ruoyi/device/pojo/DeviceLedger.java b/src/main/java/com/ruoyi/device/pojo/DeviceLedger.java
index 61239ac..a28941b 100644
--- a/src/main/java/com/ruoyi/device/pojo/DeviceLedger.java
+++ b/src/main/java/com/ruoyi/device/pojo/DeviceLedger.java
@@ -55,6 +55,42 @@
      */
     private String supplierName;
 
+    @Schema(description = "璁惧缂栧彿")
+    private String uniqueCode;
+
+    @Schema(description = "鍑哄巶缂栧彿")
+    private String factoryNumber;
+
+    @Schema(description = "鍒堕�犲巶鍟�")
+    private String manufacturer;
+
+    @Schema(description = "璧勪骇鍘熷��")
+    private BigDecimal originalAssetValue;
+
+    @Schema(description = "璐疆鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate purchaseDate;
+
+    @Schema(description = "鍚敤鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate activationDate;
+
+    @Schema(description = "涓昏鎶�鏈弬鏁�")
+    private String technicalParameters;
+
+    @Schema(description = "涓昏鐢ㄩ��")
+    private String mainPurpose;
+
+    @Schema(description = "璐d换浜�")
+    private String responsiblePerson;
+
+    @Schema(description = "寤烘。鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate filingDate;
+
     /**
      * 鍗曚綅
      */
diff --git a/src/main/java/com/ruoyi/device/pojo/DeviceMaintenance.java b/src/main/java/com/ruoyi/device/pojo/DeviceMaintenance.java
index b635e5f..4b45a8b 100644
--- a/src/main/java/com/ruoyi/device/pojo/DeviceMaintenance.java
+++ b/src/main/java/com/ruoyi/device/pojo/DeviceMaintenance.java
@@ -61,6 +61,18 @@
     @Schema(description = "淇濆吇缁撴灉 0 缁翠慨 1 瀹屽ソ")
     private String maintenanceResult;
 
+    @Schema(description = "淇濆吇绫诲瀷")
+    private String maintenanceType;
+
+    @Schema(description = "宸ヤ綔鍐呭鎻忚堪")
+    private String workContent;
+
+    @Schema(description = "杩愯鐘跺喌")
+    private String runningStatus;
+
+    @Schema(description = "瀹為檯鎵ц浜篒D")
+    private Long executorId;
+
     @Schema(description = "鐘舵�� 0 寰呬繚鍏� 1 瀹岀粨 2 澶辫触")
     private Integer status;
 
diff --git a/src/main/java/com/ruoyi/device/pojo/DeviceRepair.java b/src/main/java/com/ruoyi/device/pojo/DeviceRepair.java
index c9efa55..0d0f568 100644
--- a/src/main/java/com/ruoyi/device/pojo/DeviceRepair.java
+++ b/src/main/java/com/ruoyi/device/pojo/DeviceRepair.java
@@ -61,6 +61,15 @@
     @Schema(description = "楠屾敹澶囨敞")
     private String acceptanceRemark;
 
+    @Schema(description = "鎶ヤ慨绫诲瀷锛堝瓧鍏稿�硷級")
+    private String repairType;
+
+    @Schema(description = "鍘熷洜鍒嗘瀽")
+    private String reasonAnalysis;
+
+    @Schema(description = "澶勭悊鎺柦")
+    private String handlingMeasures;
+
     @Schema(description = "鐘舵�� 0 寰呯淮淇� 1瀹岀粨 2 澶辫触")
     // 0:寰呯淮淇� 1:瀹岀粨 2:澶辫触 3:寰呴獙鏀�
     private Integer status;
diff --git a/src/main/java/com/ruoyi/device/pojo/DeviceRepairSparePart.java b/src/main/java/com/ruoyi/device/pojo/DeviceRepairSparePart.java
new file mode 100644
index 0000000..30e30e9
--- /dev/null
+++ b/src/main/java/com/ruoyi/device/pojo/DeviceRepairSparePart.java
@@ -0,0 +1,65 @@
+package com.ruoyi.device.pojo;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+@Data
+@TableName("device_repair_spare_part")
+public class DeviceRepairSparePart {
+
+    @Schema(description = "涓婚敭ID")
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    @Schema(description = "鏉ユ簮ID锛堟姤淇崟ID鎴栦繚鍏诲崟ID锛�")
+    private Long sourceId;
+
+    @Schema(description = "鏉ユ簮绫诲瀷锛�1-鎶ヤ慨锛�2-淇濆吇")
+    private Integer sourceType;
+
+    @Schema(description = "閰嶄欢ID")
+    private Long sparePartId;
+
+    @Schema(description = "浣跨敤鏁伴噺")
+    private Integer quantity;
+
+    @Schema(description = "閰嶄欢鍚嶇О锛堥潪琛ㄥ瓧娈碉級")
+    @TableField(exist = false)
+    private String sparePartName;
+
+    @Schema(description = "鍒涘缓鏃堕棿")
+    @TableField(fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+
+    @Schema(description = "鏇存柊鏃堕棿")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime updateTime;
+
+    @Schema(description = "鍒涘缓浜�")
+    @TableField(fill = FieldFill.INSERT)
+    private Integer createUser;
+
+    @Schema(description = "鏇存柊浜�")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Integer updateUser;
+
+    @Schema(description = "绉熸埛id")
+    @TableField(fill = FieldFill.INSERT)
+    private Long tenantId;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+}
diff --git a/src/main/java/com/ruoyi/device/pojo/DeviceStatusChangeRecord.java b/src/main/java/com/ruoyi/device/pojo/DeviceStatusChangeRecord.java
new file mode 100644
index 0000000..8bd7a83
--- /dev/null
+++ b/src/main/java/com/ruoyi/device/pojo/DeviceStatusChangeRecord.java
@@ -0,0 +1,49 @@
+package com.ruoyi.device.pojo;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import lombok.Data;
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("device_status_change_record")
+public class DeviceStatusChangeRecord implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    private Long deviceId;
+    private Integer originalStatus;
+    private Integer targetStatus;
+    private String reason;
+    private String rejectReason;
+    private String approvalStatus;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Long createUser;
+
+    @TableField(exist = false)
+    private String createUserName;
+
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Long updateUser;
+
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Long tenantId;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+
+    private Integer deleted;
+}
diff --git a/src/main/java/com/ruoyi/device/pojo/MaintenanceTask.java b/src/main/java/com/ruoyi/device/pojo/MaintenanceTask.java
index 350a09d..342c459 100644
--- a/src/main/java/com/ruoyi/device/pojo/MaintenanceTask.java
+++ b/src/main/java/com/ruoyi/device/pojo/MaintenanceTask.java
@@ -69,6 +69,12 @@
     @Excel(name = "澶囨敞")
     private String remarks;
 
+    @Schema(description = "淇濆吇绫诲瀷")
+    private String maintenanceType;
+
+    @Schema(description = "鎵ц浜篒D")
+    private Long executorId;
+
     @Schema(description = "褰曞叆浜篿d")
     private Long registrantId;
 
diff --git a/src/main/java/com/ruoyi/device/service/IDeviceRepairSparePartService.java b/src/main/java/com/ruoyi/device/service/IDeviceRepairSparePartService.java
new file mode 100644
index 0000000..c06387f
--- /dev/null
+++ b/src/main/java/com/ruoyi/device/service/IDeviceRepairSparePartService.java
@@ -0,0 +1,11 @@
+package com.ruoyi.device.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.device.pojo.DeviceRepairSparePart;
+
+import java.util.List;
+
+public interface IDeviceRepairSparePartService extends IService<DeviceRepairSparePart> {
+
+    List<DeviceRepairSparePart> getListWithPartName(Long sourceId, Integer sourceType);
+}
diff --git a/src/main/java/com/ruoyi/device/service/IDeviceStatusChangeRecordService.java b/src/main/java/com/ruoyi/device/service/IDeviceStatusChangeRecordService.java
new file mode 100644
index 0000000..10c305c
--- /dev/null
+++ b/src/main/java/com/ruoyi/device/service/IDeviceStatusChangeRecordService.java
@@ -0,0 +1,10 @@
+package com.ruoyi.device.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.device.pojo.DeviceStatusChangeRecord;
+
+public interface IDeviceStatusChangeRecordService extends IService<DeviceStatusChangeRecord> {
+    IPage<DeviceStatusChangeRecord> selectPageList(Page<DeviceStatusChangeRecord> page, DeviceStatusChangeRecord record);
+}
diff --git a/src/main/java/com/ruoyi/device/service/impl/DeviceLedgerServiceImpl.java b/src/main/java/com/ruoyi/device/service/impl/DeviceLedgerServiceImpl.java
index 154923d..5b22c80 100644
--- a/src/main/java/com/ruoyi/device/service/impl/DeviceLedgerServiceImpl.java
+++ b/src/main/java/com/ruoyi/device/service/impl/DeviceLedgerServiceImpl.java
@@ -6,6 +6,7 @@
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.ruoyi.common.utils.DateUtils;
 import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.common.utils.bean.BeanUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.device.dto.DeviceLedgerDto;
@@ -31,6 +32,8 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.time.format.DateTimeFormatter;
+import java.time.LocalDate;
 
 @Service
 @RequiredArgsConstructor
@@ -56,6 +59,28 @@
         }
         DeviceLedger deviceLedger = new DeviceLedger();
         BeanUtils.copyProperties(deviceLedgerDto, deviceLedger);
+        
+        if (StringUtils.isEmpty(deviceLedger.getUniqueCode())) {
+            String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
+            LambdaQueryWrapper<DeviceLedger> queryWrapper = new LambdaQueryWrapper<>();
+            queryWrapper.likeRight(DeviceLedger::getUniqueCode, "SB" + dateStr);
+            queryWrapper.orderByDesc(DeviceLedger::getUniqueCode);
+            queryWrapper.last("limit 1");
+            DeviceLedger lastRecord = this.getOne(queryWrapper);
+            int sequence = 1;
+            if (lastRecord != null && StringUtils.isNotEmpty(lastRecord.getUniqueCode()) && lastRecord.getUniqueCode().length() >= 13) {
+                String lastSeqStr = lastRecord.getUniqueCode().substring(10);
+                try {
+                    sequence = Integer.parseInt(lastSeqStr) + 1;
+                } catch (NumberFormatException ignored) {}
+            }
+            deviceLedger.setUniqueCode("SB" + dateStr + String.format("%03d", sequence));
+        }
+
+        if (this.count(new LambdaQueryWrapper<DeviceLedger>().eq(DeviceLedger::getUniqueCode, deviceLedger.getUniqueCode())) > 0) {
+            return AjaxResult.error("璁惧缂栧彿宸插瓨鍦細" + deviceLedger.getUniqueCode());
+        }
+
         boolean save = this.save(deviceLedger);
         if (save){
             if (deviceLedgerDto.getStorageBlobDTOs() != null) {
@@ -73,6 +98,14 @@
 
     @Override
     public AjaxResult updateDeviceLedger(DeviceLedgerDto deviceLedgerDto) {
+        if (StringUtils.isNotEmpty(deviceLedgerDto.getUniqueCode())) {
+            if (this.count(new LambdaQueryWrapper<DeviceLedger>()
+                    .eq(DeviceLedger::getUniqueCode, deviceLedgerDto.getUniqueCode())
+                    .ne(DeviceLedger::getId, deviceLedgerDto.getId())) > 0) {
+                return AjaxResult.error("璁惧缂栧彿宸插瓨鍦細" + deviceLedgerDto.getUniqueCode());
+            }
+        }
+        
         DeviceLedger deviceLedger = new DeviceLedger();
         BeanUtils.copyProperties(deviceLedgerDto, deviceLedger);
         if (this.updateById(deviceLedger)) {
@@ -141,6 +174,25 @@
     public Boolean importData(MultipartFile file) throws IOException {
         ExcelUtil<DeviceLedgerExeclDto> util = new ExcelUtil<DeviceLedgerExeclDto>(DeviceLedgerExeclDto.class);
         List<DeviceLedgerExeclDto> userList = util.importExcel(file.getInputStream());
+
+        String dateStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
+        LambdaQueryWrapper<DeviceLedger> queryWrapper = new LambdaQueryWrapper<>();
+        queryWrapper.likeRight(DeviceLedger::getUniqueCode, "SB" + dateStr);
+        queryWrapper.orderByDesc(DeviceLedger::getUniqueCode);
+        queryWrapper.last("limit 1");
+        DeviceLedger lastRecord = this.getOne(queryWrapper);
+        
+        // 搴斿Java lambda琛ㄨ揪寮忓唴鍙橀噺蹇呴』鏄痜inal鐨勯棶棰橈紝鐢ㄦ暟缁勬垨绫荤殑鎴愬憳鍖呰9
+        final int[] maxSequence = {0};
+        if (lastRecord != null && StringUtils.isNotEmpty(lastRecord.getUniqueCode()) && lastRecord.getUniqueCode().length() >= 13) {
+            String lastSeqStr = lastRecord.getUniqueCode().substring(10);
+            try {
+                maxSequence[0] = Integer.parseInt(lastSeqStr);
+            } catch (NumberFormatException ignored) {}
+        }
+        
+        List<String> importUniqueCodes = new ArrayList<>();
+
         userList.forEach(c -> {
             DeviceLedger deviceLedger = new DeviceLedger();
             SysUser sysUser = sysUserMapper.selectUserByUserName(c.getCreateUser());
@@ -150,6 +202,18 @@
                 deviceLedger.setCreateUser(SecurityUtils.getUserId().intValue());
             }
             BeanUtils.copyProperties(c,deviceLedger);
+            
+            if (StringUtils.isEmpty(deviceLedger.getUniqueCode())) {
+                maxSequence[0]++;
+                deviceLedger.setUniqueCode("SB" + dateStr + String.format("%03d", maxSequence[0]));
+            }
+            if (importUniqueCodes.contains(deviceLedger.getUniqueCode())) {
+                throw new RuntimeException("瀵煎叆鏂囦欢涓瓨鍦ㄩ噸澶嶇殑璁惧缂栧彿锛�" + deviceLedger.getUniqueCode());
+            }
+            if (this.count(new LambdaQueryWrapper<DeviceLedger>().eq(DeviceLedger::getUniqueCode, deviceLedger.getUniqueCode())) > 0) {
+                throw new RuntimeException("璁惧缂栧彿宸插瓨鍦細" + deviceLedger.getUniqueCode());
+            }
+            importUniqueCodes.add(deviceLedger.getUniqueCode());
             // 閫氳繃鍚◣鍗曚环銆佹暟閲忋�佺◣鐜囪绠楀惈绋庢�讳环锛屼笉鍚◣鎬讳环
             deviceLedger.setTaxIncludingPriceTotal(c.getTaxIncludingPriceUnit());
             deviceLedger.setNumber(BigDecimal.ONE);
@@ -158,6 +222,17 @@
             if (deviceLedger.getTaxIncludingPriceTotal() != null && c.getTaxRate() != null) {
                 deviceLedger.setUnTaxIncludingPriceTotal(deviceLedger.getTaxIncludingPriceTotal().divide(BigDecimal.ONE.add(c.getTaxRate()), 2, RoundingMode.HALF_UP));
             }
+
+            if (c.getPurchaseDate() != null) {
+                deviceLedger.setPurchaseDate(DateUtils.toLocalDate(c.getPurchaseDate()));
+            }
+            if (c.getActivationDate() != null) {
+                deviceLedger.setActivationDate(DateUtils.toLocalDate(c.getActivationDate()));
+            }
+            if (c.getFilingDate() != null) {
+                deviceLedger.setFilingDate(DateUtils.toLocalDate(c.getFilingDate()));
+            }
+
             deviceLedgerMapper.insert(deviceLedger);
         });
 
diff --git a/src/main/java/com/ruoyi/device/service/impl/DeviceMaintenanceServiceImpl.java b/src/main/java/com/ruoyi/device/service/impl/DeviceMaintenanceServiceImpl.java
index fbf03ad..4ee147b 100644
--- a/src/main/java/com/ruoyi/device/service/impl/DeviceMaintenanceServiceImpl.java
+++ b/src/main/java/com/ruoyi/device/service/impl/DeviceMaintenanceServiceImpl.java
@@ -21,7 +21,11 @@
 import com.ruoyi.measuringinstrumentledger.pojo.SparePartsRequisitionRecord;
 import com.ruoyi.measuringinstrumentledger.service.SparePartsRequisitionRecordService;
 import jakarta.servlet.http.HttpServletResponse;
+import jakarta.servlet.http.HttpServletResponse;
 import lombok.RequiredArgsConstructor;
+import com.ruoyi.device.service.IDeviceRepairSparePartService;
+import com.ruoyi.device.pojo.DeviceRepairSparePart;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -38,6 +42,7 @@
     private final DeviceMaintenanceMapper deviceMaintenanceMapper;
     private final SparePartsMapper sparePartsMapper;
     private final SparePartsRequisitionRecordService sparePartsRequisitionRecordService;
+    private final IDeviceRepairSparePartService deviceRepairSparePartService;
     private final FileUtil fileUtil;
 
     @Override
@@ -62,6 +67,12 @@
     @Transactional(rollbackFor = Exception.class)
     public AjaxResult updateDeviceDeviceMaintenance(DeviceMaintenanceDto deviceMaintenance) {
         DeviceMaintenance oldDeviceMaintenance = this.getById(deviceMaintenance.getId());
+        
+        // 娓呯悊鏃х殑澶囦欢浣跨敤璁板綍锛岄槻姝㈤噸澶嶇疮璁�
+        deviceRepairSparePartService.remove(new QueryWrapper<DeviceRepairSparePart>()
+                .eq("source_id", deviceMaintenance.getId())
+                .eq("source_type", 2));
+
         // 澶勭悊澶囦欢浣跨敤鎯呭喌
         if (com.github.xiaoymin.knife4j.core.util.CollectionUtils.isNotEmpty(deviceMaintenance.getSparePartsUseList())) {
             List<Long> sparePartIds = new ArrayList<>();
@@ -84,6 +95,14 @@
                         record.setSparePartsId(sparePartUse.getId());
                         record.setQuantity(sparePartUse.getQuantity());
                         sparePartsRequisitionRecordService.save(record);
+
+                        // 鎻掑叆璁板綍鍒� device_repair_spare_part
+                        DeviceRepairSparePart partRecord = new DeviceRepairSparePart();
+                        partRecord.setSourceId(deviceMaintenance.getId());
+                        partRecord.setSourceType(2); // 2 淇濆吇
+                        partRecord.setSparePartId(sparePartUse.getId());
+                        partRecord.setQuantity(sparePartUse.getQuantity());
+                        deviceRepairSparePartService.save(partRecord);
                     } else {
                         return AjaxResult.error("澶囦欢 " + spareParts.getName() + " 鏁伴噺涓嶈冻");
                     }
@@ -122,6 +141,8 @@
     public DeviceMaintenanceVo detailById(Long id) {
         DeviceMaintenanceVo vo = deviceMaintenanceMapper.detailById(id);
         vo.setStorageBlobVOs(fileUtil.getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum.DEVICE_MAINTENANCE, id));
+        List<DeviceRepairSparePart> parts = deviceRepairSparePartService.getListWithPartName(id, 2);
+        vo.setSparePartsUseListDetails(parts);
         return vo;
     }
 }
diff --git a/src/main/java/com/ruoyi/device/service/impl/DeviceRepairServiceImpl.java b/src/main/java/com/ruoyi/device/service/impl/DeviceRepairServiceImpl.java
index e5a73ac..76d1a3a 100644
--- a/src/main/java/com/ruoyi/device/service/impl/DeviceRepairServiceImpl.java
+++ b/src/main/java/com/ruoyi/device/service/impl/DeviceRepairServiceImpl.java
@@ -6,6 +6,7 @@
 import com.github.xiaoymin.knife4j.core.util.CollectionUtils;
 import com.ruoyi.basic.enums.RecordTypeEnum;
 import com.ruoyi.basic.utils.FileUtil;
+import com.ruoyi.common.utils.DictUtils;
 import com.ruoyi.common.utils.bean.BeanUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
 import com.ruoyi.device.dto.DeviceDefectRecordDto;
@@ -21,6 +22,11 @@
 import com.ruoyi.framework.web.domain.AjaxResult;
 import com.ruoyi.measuringinstrumentledger.mapper.SparePartsMapper;
 import com.ruoyi.measuringinstrumentledger.pojo.SpareParts;
+import com.ruoyi.approve.pojo.KnowledgeBase;
+import com.ruoyi.approve.service.KnowledgeBaseService;
+import com.ruoyi.device.pojo.DeviceRepairSparePart;
+import com.ruoyi.device.service.IDeviceRepairSparePartService;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.ruoyi.measuringinstrumentledger.pojo.SparePartsRequisitionRecord;
 import com.ruoyi.measuringinstrumentledger.service.SparePartsRequisitionRecordService;
 import jakarta.servlet.http.HttpServletResponse;
@@ -45,6 +51,8 @@
     private final IDeviceLedgerService deviceLedgerService;
     private final SparePartsMapper sparePartsMapper;
     private final SparePartsRequisitionRecordService sparePartsRequisitionRecordService;
+    private final IDeviceRepairSparePartService deviceRepairSparePartService;
+    private final KnowledgeBaseService knowledgeBaseService;
     private final FileUtil fileUtil;
 
     private static final int STATUS_PENDING_REPAIR = 0;
@@ -92,6 +100,11 @@
                 || oldDeviceRepair.getStatus() != STATUS_COMPLETED)) {
             return AjaxResult.error("璇峰厛鎻愪氦楠屾敹瀹℃壒锛岄獙鏀堕�氳繃鍚庢墠鍙畬缁�");
         }
+        // 娓呯悊鏃х殑澶囦欢浣跨敤璁板綍锛岄槻姝㈤噸澶嶇疮璁�
+        deviceRepairSparePartService.remove(new QueryWrapper<DeviceRepairSparePart>()
+                .eq("source_id", deviceRepairDto.getId())
+                .eq("source_type", 1));
+
         // 澶勭悊澶囦欢浣跨敤鎯呭喌
         if (CollectionUtils.isNotEmpty(deviceRepairDto.getSparePartsUseList())) {
             List<Long> sparePartIds = new ArrayList<>();
@@ -114,6 +127,14 @@
                         record.setSparePartsId(sparePartUse.getId());
                         record.setQuantity(sparePartUse.getQuantity());
                         sparePartsRequisitionRecordService.save(record);
+
+                        // 鎻掑叆璁板綍鍒� device_repair_spare_part
+                        DeviceRepairSparePart partRecord = new DeviceRepairSparePart();
+                        partRecord.setSourceId(deviceRepairDto.getId());
+                        partRecord.setSourceType(1); // 1 鎶ヤ慨
+                        partRecord.setSparePartId(sparePartUse.getId());
+                        partRecord.setQuantity(sparePartUse.getQuantity());
+                        deviceRepairSparePartService.save(partRecord);
                     } else {
                         return AjaxResult.error("澶囦欢 " + spareParts.getName() + " 鏁伴噺涓嶈冻");
                     }
@@ -194,6 +215,47 @@
         update.setAcceptanceRemark(deviceRepairDto.getAcceptanceRemark());
         update.setStatus(STATUS_COMPLETED);
         if (this.updateById(update)) {
+            try {
+                DeviceLedger deviceLedger = deviceLedgerService.getById(oldDeviceRepair.getDeviceLedgerId());
+                List<DeviceRepairSparePart> parts = deviceRepairSparePartService.getListWithPartName(deviceRepairDto.getId(), 1);
+
+                String sparePartsInfo = "";
+                if (CollectionUtils.isNotEmpty(parts)) {
+                    StringBuilder sb = new StringBuilder();
+                    for (DeviceRepairSparePart p : parts) {
+                        sb.append(p.getSparePartName()).append("(").append(p.getQuantity()).append("涓�) ");
+                    }
+                    sparePartsInfo = " 閰嶄欢浣跨敤鎯呭喌: " + sb.toString();
+                }
+
+                KnowledgeBase kb = new KnowledgeBase();
+                String typeName = deviceLedger.getType();
+                String deviceModel = deviceLedger.getDeviceModel();
+                String repairTypeStr = oldDeviceRepair.getRepairType();
+                String repairTypeLabel = DictUtils.getDictLabel("device_repair_type", repairTypeStr);
+                if (StringUtils.isBlank(repairTypeLabel)) {
+                    repairTypeLabel = "缁翠慨";
+                }
+                
+                kb.setTitle((typeName != null ? typeName : "璁惧") + "_" + (deviceModel != null ? deviceModel : "") + " 鐨� " + repairTypeLabel + "璁板綍");
+                kb.setScenario("璁惧缁翠慨");
+                kb.setType("瑙e喅鏂规");
+
+                String remarkStr = oldDeviceRepair.getRemark() == null ? "" : oldDeviceRepair.getRemark();
+                kb.setProblem(String.format("璁惧鍙�: %s, 璁惧鍚嶇О: %s, 鏁呴殰鎻忚堪: %s",
+                        deviceLedger.getUniqueCode(), deviceLedger.getDeviceName(),
+                        remarkStr));
+
+                String reasonStr = oldDeviceRepair.getReasonAnalysis() == null ? "" : oldDeviceRepair.getReasonAnalysis();
+                String handlingStr = oldDeviceRepair.getHandlingMeasures() == null ? "" : oldDeviceRepair.getHandlingMeasures();
+                kb.setSolution(String.format("鍘熷洜鍒嗘瀽: %s. 澶勭悊鎺柦: %s.%s",
+                        reasonStr, handlingStr, sparePartsInfo));
+                kb.setCreator(oldDeviceRepair.getMaintenanceName());
+
+                knowledgeBaseService.save(kb);
+            } catch(Exception e) {
+                log.error("淇濆瓨鐭ヨ瘑搴撳け璐�", e);
+            }
             return AjaxResult.success();
         }
         return AjaxResult.error("楠屾敹瀹℃壒澶辫触");
@@ -251,6 +313,8 @@
     public DeviceRepairVo detailById(Long id) {
         DeviceRepairVo vo = deviceRepairMapper.detailById(id);
         vo.setStorageBlobVOs(fileUtil.getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum.DEVICE_REPAIR, id));
+        List<DeviceRepairSparePart> parts = deviceRepairSparePartService.getListWithPartName(id, 1);
+        vo.setSparePartsUseListDetails(parts);
         return vo;
     }
 
diff --git a/src/main/java/com/ruoyi/device/service/impl/DeviceRepairSparePartServiceImpl.java b/src/main/java/com/ruoyi/device/service/impl/DeviceRepairSparePartServiceImpl.java
new file mode 100644
index 0000000..b838e9c
--- /dev/null
+++ b/src/main/java/com/ruoyi/device/service/impl/DeviceRepairSparePartServiceImpl.java
@@ -0,0 +1,18 @@
+package com.ruoyi.device.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.device.mapper.DeviceRepairSparePartMapper;
+import com.ruoyi.device.pojo.DeviceRepairSparePart;
+import com.ruoyi.device.service.IDeviceRepairSparePartService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class DeviceRepairSparePartServiceImpl extends ServiceImpl<DeviceRepairSparePartMapper, DeviceRepairSparePart> implements IDeviceRepairSparePartService {
+
+    @Override
+    public List<DeviceRepairSparePart> getListWithPartName(Long sourceId, Integer sourceType) {
+        return baseMapper.selectListWithPartName(sourceId, sourceType);
+    }
+}
diff --git a/src/main/java/com/ruoyi/device/service/impl/DeviceStatusChangeRecordServiceImpl.java b/src/main/java/com/ruoyi/device/service/impl/DeviceStatusChangeRecordServiceImpl.java
new file mode 100644
index 0000000..380baa9
--- /dev/null
+++ b/src/main/java/com/ruoyi/device/service/impl/DeviceStatusChangeRecordServiceImpl.java
@@ -0,0 +1,47 @@
+package com.ruoyi.device.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.device.mapper.DeviceStatusChangeRecordMapper;
+import com.ruoyi.device.pojo.DeviceStatusChangeRecord;
+import com.ruoyi.device.service.IDeviceStatusChangeRecordService;
+import com.ruoyi.project.system.domain.SysUser;
+import com.ruoyi.project.system.mapper.SysUserMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class DeviceStatusChangeRecordServiceImpl extends ServiceImpl<DeviceStatusChangeRecordMapper, DeviceStatusChangeRecord> implements IDeviceStatusChangeRecordService {
+
+    @Autowired
+    private SysUserMapper sysUserMapper;
+
+    @Override
+    public IPage<DeviceStatusChangeRecord> selectPageList(Page<DeviceStatusChangeRecord> page, DeviceStatusChangeRecord record) {
+        LambdaQueryWrapper<DeviceStatusChangeRecord> wrapper = Wrappers.lambdaQuery();
+        if (record.getDeviceId() != null) {
+            wrapper.eq(DeviceStatusChangeRecord::getDeviceId, record.getDeviceId());
+        }
+        wrapper.eq(DeviceStatusChangeRecord::getDeleted, 0);
+        wrapper.orderByDesc(DeviceStatusChangeRecord::getCreateTime);
+
+        IPage<DeviceStatusChangeRecord> result = this.page(page, wrapper);
+        List<DeviceStatusChangeRecord> records = result.getRecords();
+        if (records != null && !records.isEmpty()) {
+            for (DeviceStatusChangeRecord r : records) {
+                if (r.getCreateUser() != null) {
+                    SysUser user = sysUserMapper.selectUserById(r.getCreateUser());
+                    if (user != null) {
+                        r.setCreateUserName(user.getNickName());
+                    }
+                }
+            }
+        }
+        return result;
+    }
+}
diff --git a/src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskJob.java b/src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskJob.java
index 204667b..5bcd13c 100644
--- a/src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskJob.java
+++ b/src/main/java/com/ruoyi/device/service/impl/MaintenanceTaskJob.java
@@ -8,6 +8,8 @@
 import org.springframework.jdbc.core.BeanPropertyRowMapper;
 import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.stereotype.Component;
+import com.ruoyi.project.system.service.ISysNoticeService;
+import com.ruoyi.project.system.domain.SysNotice;
 
 import java.io.Serializable;
 import java.time.DayOfWeek;
@@ -28,6 +30,9 @@
 
     @Autowired
     private JdbcTemplate jdbcTemplate;
+
+    @Autowired
+    private ISysNoticeService sysNoticeService;
 
     @Override
     public void execute(JobExecutionContext context) throws JobExecutionException {
@@ -52,6 +57,19 @@
             // 2. 鍒涘缓骞朵繚瀛樺贰妫�浠诲姟璁板綍 - 杩欏氨鏄偍鎻愪緵鐨勪唬鐮佸簲璇ユ斁鐨勪綅缃�
             DeviceMaintenance deviceMaintenance = createInspectionTask(timingTask);
             deviceMaintenanceService.save(deviceMaintenance);
+
+            // 缁欐墽琛屼汉鍙戦�佺郴缁熼�氱煡
+            if (timingTask.getExecutorId() != null) {
+                SysNotice notice = new SysNotice();
+                notice.setNoticeTitle("鏂颁繚鍏讳换鍔℃彁閱�");
+                notice.setNoticeType("1"); // 1 閫氱煡
+                notice.setNoticeContent("鎮ㄦ湁涓�涓柊鐨勮澶囦繚鍏讳换鍔¢渶瑕佹墽琛岋紝璁惧鍚嶇О锛�" + timingTask.getTaskName());
+                notice.setStatus("0"); // 姝e父
+                notice.setConsigneeId(timingTask.getExecutorId());
+                notice.setSenderId(0L); // 0浠h〃绯荤粺鍙戦��
+                notice.setTenantId(timingTask.getTenantId());
+                sysNoticeService.insertNotice(notice);
+            }
 
             // 3. 鏇存柊瀹氭椂浠诲姟鐨勬墽琛屾椂闂�
             if (!tasks.isEmpty()) {
@@ -94,6 +112,8 @@
         inspectionTask.setDeviceLedgerId(timingTask.getTaskId());
         inspectionTask.setMaintenancePlanTime(LocalDateTime.now());
         inspectionTask.setMaintenanceActuallyName(timingTask.getMaintenancePerson());
+        inspectionTask.setExecutorId(timingTask.getExecutorId());
+        inspectionTask.setMaintenanceType(timingTask.getMaintenanceType());
         inspectionTask.setFrequencyType(timingTask.getFrequencyType());
         inspectionTask.setFrequencyDetail(timingTask.getFrequencyDetail());
         inspectionTask.setTenantId(timingTask.getTenantId());
diff --git a/src/main/java/com/ruoyi/device/vo/DeviceMaintenanceVo.java b/src/main/java/com/ruoyi/device/vo/DeviceMaintenanceVo.java
index f71c5cb..4a0f5fa 100644
--- a/src/main/java/com/ruoyi/device/vo/DeviceMaintenanceVo.java
+++ b/src/main/java/com/ruoyi/device/vo/DeviceMaintenanceVo.java
@@ -48,4 +48,7 @@
 
     @Schema(description = "淇濆吇鍥剧墖鍒楄〃")
     private List<StorageBlobVO> storageBlobVOs;
+
+    @Schema(description = "浣跨敤澶囦欢璇︽儏鍒楄〃")
+    private List<com.ruoyi.device.pojo.DeviceRepairSparePart> sparePartsUseListDetails;
 }
diff --git a/src/main/java/com/ruoyi/device/vo/DeviceRepairVo.java b/src/main/java/com/ruoyi/device/vo/DeviceRepairVo.java
index e90c501..7bb2f81 100644
--- a/src/main/java/com/ruoyi/device/vo/DeviceRepairVo.java
+++ b/src/main/java/com/ruoyi/device/vo/DeviceRepairVo.java
@@ -2,6 +2,7 @@
 
 import com.ruoyi.basic.dto.StorageBlobVO;
 import com.ruoyi.device.pojo.DeviceRepair;
+import com.ruoyi.device.pojo.DeviceRepairSparePart;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
@@ -16,5 +17,11 @@
     @Schema(description = "缁翠慨鏃堕棿瀛楃涓�")
     private String maintenanceTimeStr;
 
+    @Schema(description = "澶勭悊鎺柦")
+    private String handlingMeasures;
+
+    @Schema(description = "澶囦欢浣跨敤璇︽儏鍒楄〃")
+    private List<DeviceRepairSparePart> sparePartsUseListDetails;
+
     private List<StorageBlobVO> storageBlobVOs;
 }
diff --git a/src/main/java/com/ruoyi/measuringinstrumentledger/dto/MeasuringInstrumentLedgerDto.java b/src/main/java/com/ruoyi/measuringinstrumentledger/dto/MeasuringInstrumentLedgerDto.java
index 648db49..75d71c4 100644
--- a/src/main/java/com/ruoyi/measuringinstrumentledger/dto/MeasuringInstrumentLedgerDto.java
+++ b/src/main/java/com/ruoyi/measuringinstrumentledger/dto/MeasuringInstrumentLedgerDto.java
@@ -39,5 +39,29 @@
     @Schema(description = "妫�瀹氫汉")
     private Long userId;
 
+    @Schema(description = "婧簮/鏍告煡绫诲瀷")
+    private String traceabilityType;
 
+    @Schema(description = "瀹炴柦鏈烘瀯")
+    private String implementingAgency;
+
+    @Schema(description = "璇佷功缂栧彿")
+    private String certificateNumber;
+
+    @Schema(description = "鏍稿績缁撴灉")
+    private String coreResult;
+
+    @Schema(description = "缁撴灉纭")
+    private String resultConfirmation;
+
+    @Schema(description = "纭浜篿d")
+    private Long confirmerId;
+
+    @Schema(description = "纭浜�")
+    private String confirmerName;
+
+    @Schema(description = "鏈夋晥鏈熻嚦")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private Date validUntil;
 }
diff --git a/src/main/java/com/ruoyi/measuringinstrumentledger/pojo/MeasuringInstrumentLedgerRecord.java b/src/main/java/com/ruoyi/measuringinstrumentledger/pojo/MeasuringInstrumentLedgerRecord.java
index 8ca16d8..fd5d3f5 100644
--- a/src/main/java/com/ruoyi/measuringinstrumentledger/pojo/MeasuringInstrumentLedgerRecord.java
+++ b/src/main/java/com/ruoyi/measuringinstrumentledger/pojo/MeasuringInstrumentLedgerRecord.java
@@ -81,6 +81,39 @@
     @Schema(description = "鏈夋晥鏈燂紙鍗曚綅澶╋級")
     private Integer valid;
 
+    @Schema(description = "婧簮/鏍告煡绫诲瀷")
+    @Excel(name = "婧簮/鏍告煡绫诲瀷")
+    private String traceabilityType;
+
+    @Schema(description = "瀹炴柦鏈烘瀯")
+    @Excel(name = "瀹炴柦鏈烘瀯")
+    private String implementingAgency;
+
+    @Schema(description = "璇佷功缂栧彿")
+    @Excel(name = "璇佷功缂栧彿")
+    private String certificateNumber;
+
+    @Schema(description = "鏍稿績缁撴灉")
+    @Excel(name = "鏍稿績缁撴灉")
+    private String coreResult;
+
+    @Schema(description = "缁撴灉纭")
+    @Excel(name = "缁撴灉纭")
+    private String resultConfirmation;
+
+    @Schema(description = "鏈夋晥鏈熻嚦")
+    @Excel(name = "鏈夋晥鏈熻嚦", width = 30, dateFormat = "yyyy-MM-dd")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private Date validUntil;
+
+    @Schema(description = "纭浜篿d")
+    private Long confirmerId;
+
+    @Schema(description = "纭浜�")
+    @Excel(name = "纭浜�")
+    private String confirmerName;
+
 
     /**
      * 鐘舵�侊紙1-鏈夋晥 2-閫炬湡锛�
diff --git a/src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/MeasuringInstrumentLedgerServiceImpl.java b/src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/MeasuringInstrumentLedgerServiceImpl.java
index 4025d75..5827270 100644
--- a/src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/MeasuringInstrumentLedgerServiceImpl.java
+++ b/src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/MeasuringInstrumentLedgerServiceImpl.java
@@ -78,6 +78,17 @@
             measuringInstrumentLedgerRecord.setValid(req.getValid());
             measuringInstrumentLedgerRecord.setUserId(req.getUserId());
             measuringInstrumentLedgerRecord.setUserName(sysUser.getUserName());
+            
+            // Set the new fields
+            measuringInstrumentLedgerRecord.setTraceabilityType(req.getTraceabilityType());
+            measuringInstrumentLedgerRecord.setImplementingAgency(req.getImplementingAgency());
+            measuringInstrumentLedgerRecord.setCertificateNumber(req.getCertificateNumber());
+            measuringInstrumentLedgerRecord.setCoreResult(req.getCoreResult());
+            measuringInstrumentLedgerRecord.setResultConfirmation(req.getResultConfirmation());
+            measuringInstrumentLedgerRecord.setConfirmerId(req.getConfirmerId());
+            measuringInstrumentLedgerRecord.setConfirmerName(req.getConfirmerName());
+            measuringInstrumentLedgerRecord.setValidUntil(req.getValidUntil());
+            
             measuringInstrumentLedgerRecordMapper.insert(measuringInstrumentLedgerRecord);
             // 鍙拌处缁戝畾涓�娆�
 //            if(!CollectionUtils.isEmpty(req.getTempFileIds())){
diff --git a/src/main/java/com/ruoyi/projectManagement/controller/InfoController.java b/src/main/java/com/ruoyi/projectManagement/controller/InfoController.java
index 56baf0f..e3af5e3 100644
--- a/src/main/java/com/ruoyi/projectManagement/controller/InfoController.java
+++ b/src/main/java/com/ruoyi/projectManagement/controller/InfoController.java
@@ -8,6 +8,7 @@
 import com.ruoyi.projectManagement.vo.SaveInfoStageVo;
 import com.ruoyi.projectManagement.vo.SaveInfoVo;
 import com.ruoyi.projectManagement.vo.SearchInfoVo;
+import com.ruoyi.projectManagement.vo.AttachmentGroupVo;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import io.swagger.v3.oas.annotations.Operation;
 import lombok.RequiredArgsConstructor;
@@ -83,5 +84,11 @@
         return AjaxResult.success();
     }
 
+    @GetMapping("/attachments/{id}")
+    @Operation(summary = "鏂囨。褰掓。锛氳幏鍙栭」鐩墍鏈夐檮浠�")
+    public AjaxResult getAttachments(@PathVariable("id") Long id) {
+        return AjaxResult.success(infoService.getProjectAttachments(id));
+    }
+
 
 }
diff --git a/src/main/java/com/ruoyi/projectManagement/controller/ProjectConfigController.java b/src/main/java/com/ruoyi/projectManagement/controller/ProjectConfigController.java
new file mode 100644
index 0000000..8574cec
--- /dev/null
+++ b/src/main/java/com/ruoyi/projectManagement/controller/ProjectConfigController.java
@@ -0,0 +1,31 @@
+package com.ruoyi.projectManagement.controller;
+
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.projectManagement.pojo.ProjectConfig;
+import com.ruoyi.projectManagement.service.ProjectConfigService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/projectManagement/config")
+@Tag(name = "椤圭洰绠$悊閰嶇疆琛�")
+@RequiredArgsConstructor
+public class ProjectConfigController {
+
+    private final ProjectConfigService projectConfigService;
+
+    @GetMapping
+    @Operation(summary = "鑾峰彇鍏ㄥ眬閰嶇疆")
+    public AjaxResult getConfig() {
+        return AjaxResult.success(projectConfigService.getGlobalConfig());
+    }
+
+    @PutMapping
+    @Operation(summary = "淇敼鍏ㄥ眬閰嶇疆")
+    public AjaxResult updateConfig(@RequestBody ProjectConfig projectConfig) {
+        projectConfigService.updateGlobalConfig(projectConfig);
+        return AjaxResult.success();
+    }
+}
diff --git a/src/main/java/com/ruoyi/projectManagement/mapper/ProjectConfigMapper.java b/src/main/java/com/ruoyi/projectManagement/mapper/ProjectConfigMapper.java
new file mode 100644
index 0000000..9a88728
--- /dev/null
+++ b/src/main/java/com/ruoyi/projectManagement/mapper/ProjectConfigMapper.java
@@ -0,0 +1,9 @@
+package com.ruoyi.projectManagement.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.projectManagement.pojo.ProjectConfig;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface ProjectConfigMapper extends BaseMapper<ProjectConfig> {
+}
diff --git a/src/main/java/com/ruoyi/projectManagement/pojo/ProjectConfig.java b/src/main/java/com/ruoyi/projectManagement/pojo/ProjectConfig.java
new file mode 100644
index 0000000..3d708b7
--- /dev/null
+++ b/src/main/java/com/ruoyi/projectManagement/pojo/ProjectConfig.java
@@ -0,0 +1,64 @@
+package com.ruoyi.projectManagement.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 椤圭洰绠$悊閰嶇疆琛�
+ */
+@Data
+@TableName("project_management_config")
+@Schema(description = "椤圭洰绠$悊閰嶇疆瀵硅薄")
+public class ProjectConfig implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 涓婚敭
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 鍚堝悓鍘熶欢绾﹀畾鍥炰紶鎻愬墠鎻愰啋澶╂暟
+     */
+    @Schema(description = "鍚堝悓鍘熶欢绾﹀畾鍥炰紶鎻愬墠鎻愰啋澶╂暟")
+    @TableField("contract_return_reminder_days")
+    private Integer contractReturnReminderDays;
+
+    @Schema(description = "鍒涘缓鐢ㄦ埛")
+    @TableField(fill = FieldFill.INSERT)
+    private Integer createUser;
+
+    @TableField(value = "create_user_name", fill = FieldFill.INSERT)
+    private String createUserName;
+
+    @Schema(description = "鍒涘缓鏃堕棿")
+    @TableField(fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime createTime;
+
+    @Schema(description = "鏇存柊鐢ㄦ埛")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Integer updateUser;
+
+    @TableField(value = "update_user_name", fill = FieldFill.INSERT_UPDATE)
+    private String updateUserName;
+
+    @Schema(description = "鏇存柊鏃堕棿")
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime updateTime;
+
+    @Schema(description = "绉熸埛ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long tenantId;
+
+    @Schema(description = "閮ㄩ棬ID")
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+}
diff --git a/src/main/java/com/ruoyi/projectManagement/pojo/Roles.java b/src/main/java/com/ruoyi/projectManagement/pojo/Roles.java
index ed111b8..1b65ea4 100644
--- a/src/main/java/com/ruoyi/projectManagement/pojo/Roles.java
+++ b/src/main/java/com/ruoyi/projectManagement/pojo/Roles.java
@@ -38,6 +38,7 @@
     private Integer status;
 
     @Schema(description = "鍒涘缓鏃堕棿")
+    @TableField(fill = FieldFill.INSERT)
     private LocalDateTime createTime;
 
     @Schema(description = "鍒涘缓鐢ㄦ埛")
diff --git a/src/main/java/com/ruoyi/projectManagement/service/InfoService.java b/src/main/java/com/ruoyi/projectManagement/service/InfoService.java
index 22a9250..82876f3 100644
--- a/src/main/java/com/ruoyi/projectManagement/service/InfoService.java
+++ b/src/main/java/com/ruoyi/projectManagement/service/InfoService.java
@@ -46,4 +46,11 @@
 
 
     InfoVo getInfoById(@NotNull Long id);
+
+    /**
+     * 鑾峰彇椤圭洰涓嬫墍鏈夐檮浠�
+     * @param id 椤圭洰id
+     * @return 闄勪欢鎸夋ā鍧楀垎缁勭殑闆嗗悎
+     */
+    java.util.List<com.ruoyi.projectManagement.vo.AttachmentGroupVo> getProjectAttachments(@NotNull Long id);
 }
diff --git a/src/main/java/com/ruoyi/projectManagement/service/ProjectConfigService.java b/src/main/java/com/ruoyi/projectManagement/service/ProjectConfigService.java
new file mode 100644
index 0000000..1e387ed
--- /dev/null
+++ b/src/main/java/com/ruoyi/projectManagement/service/ProjectConfigService.java
@@ -0,0 +1,17 @@
+package com.ruoyi.projectManagement.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.projectManagement.pojo.ProjectConfig;
+
+public interface ProjectConfigService extends IService<ProjectConfig> {
+    
+    /**
+     * 鑾峰彇鍏ㄥ眬閰嶇疆锛堝涓嶅瓨鍦ㄥ垯杩斿洖甯︽湁榛樿鍊肩殑瀹炰緥锛�
+     */
+    ProjectConfig getGlobalConfig();
+
+    /**
+     * 鏇存柊鍏ㄥ眬閰嶇疆
+     */
+    void updateGlobalConfig(ProjectConfig projectConfig);
+}
diff --git a/src/main/java/com/ruoyi/projectManagement/service/impl/InfoServiceImpl.java b/src/main/java/com/ruoyi/projectManagement/service/impl/InfoServiceImpl.java
index 85d1e88..d1f1897 100644
--- a/src/main/java/com/ruoyi/projectManagement/service/impl/InfoServiceImpl.java
+++ b/src/main/java/com/ruoyi/projectManagement/service/impl/InfoServiceImpl.java
@@ -18,8 +18,18 @@
 import com.ruoyi.projectManagement.vo.ListInfoVo;
 import com.ruoyi.projectManagement.vo.SaveInfoVo;
 import com.ruoyi.projectManagement.vo.SearchInfoVo;
+import com.ruoyi.projectManagement.vo.AttachmentGroupVo;
 import com.ruoyi.sales.pojo.SalesLedgerProduct;
+import com.ruoyi.sales.pojo.SalesLedger;
+import com.baomidou.mybatisplus.extension.toolkit.Db;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.ruoyi.basic.dto.StorageAttachmentDTO;
+import com.ruoyi.basic.dto.StorageBlobVO;
+import com.ruoyi.basic.enums.RecordTypeEnum;
+import com.ruoyi.basic.service.StorageAttachmentService;
 import com.ruoyi.sales.service.ISalesLedgerService;
+import com.ruoyi.sales.pojo.SalesLawsuit;
+import com.ruoyi.sales.service.ISalesLawsuitService;
 import lombok.RequiredArgsConstructor;
 import lombok.SneakyThrows;
 import org.springframework.stereotype.Service;
@@ -43,6 +53,8 @@
     private final ContractInfoHandleService contractInfoHandleService;
     private final ShippingAddressHandleService shippingAddressHandleService;
     private final ISalesLedgerService salesLedgerService;
+    private final StorageAttachmentService storageAttachmentService;
+    private final ISalesLawsuitService salesLawsuitService;
 
 
     @Transactional(rollbackFor = Exception.class)
@@ -128,4 +140,55 @@
         return vo;
     }
 
+    @Override
+    public List<AttachmentGroupVo> getProjectAttachments(Long id) {
+        List<AttachmentGroupVo> result = new java.util.ArrayList<>();
+        
+        // 椤圭洰绠$悊闄勪欢
+        StorageAttachmentDTO infoDto = new StorageAttachmentDTO();
+        infoDto.setRecordType(RecordTypeEnum.INFO.getType());
+        infoDto.setRecordId(id);
+        List<StorageBlobVO> infoFiles = storageAttachmentService.list(infoDto);
+        if (infoFiles != null && !infoFiles.isEmpty()) {
+            result.add(new AttachmentGroupVo("椤圭洰绠$悊", infoFiles));
+        }
+
+        // 閿�鍞彴璐﹂檮浠�
+        List<SalesLedger> ledgers = salesLedgerService.list(Wrappers.<SalesLedger>lambdaQuery().eq(SalesLedger::getProjectId, id));
+        if (ledgers != null && !ledgers.isEmpty()) {
+            List<StorageBlobVO> ledgerFiles = new java.util.ArrayList<>();
+            for (SalesLedger ledger : ledgers) {
+                StorageAttachmentDTO dto = new StorageAttachmentDTO();
+                dto.setRecordType(RecordTypeEnum.SALES_LEDGER.getType());
+                dto.setRecordId(ledger.getId());
+                List<StorageBlobVO> files = storageAttachmentService.list(dto);
+                if (files != null && !files.isEmpty()) {
+                    ledgerFiles.addAll(files);
+                }
+            }
+            if (!ledgerFiles.isEmpty()) {
+                result.add(new AttachmentGroupVo("閿�鍞彴璐�", ledgerFiles));
+            }
+        }
+
+        // 璇夎绠$悊闄勪欢
+        List<SalesLawsuit> lawsuits = salesLawsuitService.list(Wrappers.<SalesLawsuit>lambdaQuery().eq(SalesLawsuit::getProjectId, id));
+        if (lawsuits != null && !lawsuits.isEmpty()) {
+            List<StorageBlobVO> lawsuitFiles = new java.util.ArrayList<>();
+            for (SalesLawsuit lawsuit : lawsuits) {
+                StorageAttachmentDTO dto = new StorageAttachmentDTO();
+                dto.setRecordType(RecordTypeEnum.SALES_LAWSUIT.getType());
+                dto.setRecordId(lawsuit.getId());
+                List<StorageBlobVO> files = storageAttachmentService.list(dto);
+                if (files != null && !files.isEmpty()) {
+                    lawsuitFiles.addAll(files);
+                }
+            }
+            if (!lawsuitFiles.isEmpty()) {
+                result.add(new AttachmentGroupVo("璇夎绠$悊", lawsuitFiles));
+            }
+        }
+
+        return result;
+    }
 }
diff --git a/src/main/java/com/ruoyi/projectManagement/service/impl/ProjectConfigServiceImpl.java b/src/main/java/com/ruoyi/projectManagement/service/impl/ProjectConfigServiceImpl.java
new file mode 100644
index 0000000..35860e3
--- /dev/null
+++ b/src/main/java/com/ruoyi/projectManagement/service/impl/ProjectConfigServiceImpl.java
@@ -0,0 +1,31 @@
+package com.ruoyi.projectManagement.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.projectManagement.mapper.ProjectConfigMapper;
+import com.ruoyi.projectManagement.pojo.ProjectConfig;
+import com.ruoyi.projectManagement.service.ProjectConfigService;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+public class ProjectConfigServiceImpl extends ServiceImpl<ProjectConfigMapper, ProjectConfig> implements ProjectConfigService {
+
+    @Override
+    public ProjectConfig getGlobalConfig() {
+        ProjectConfig config = this.getById(1L);
+        if (config == null) {
+            config = new ProjectConfig();
+            config.setId(1L);
+            config.setContractReturnReminderDays(3);
+            this.save(config);
+        }
+        return config;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateGlobalConfig(ProjectConfig projectConfig) {
+        projectConfig.setId(1L); // 寮哄埗鏇存柊 ID=1 鐨勮褰�
+        this.saveOrUpdate(projectConfig);
+    }
+}
diff --git a/src/main/java/com/ruoyi/projectManagement/vo/AttachmentGroupVo.java b/src/main/java/com/ruoyi/projectManagement/vo/AttachmentGroupVo.java
new file mode 100644
index 0000000..8db97bb
--- /dev/null
+++ b/src/main/java/com/ruoyi/projectManagement/vo/AttachmentGroupVo.java
@@ -0,0 +1,25 @@
+package com.ruoyi.projectManagement.vo;
+
+import com.ruoyi.basic.dto.StorageBlobVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import java.util.List;
+
+@Data
+@Schema(description = "闄勪欢褰掓。鍒嗙粍瀵硅薄")
+public class AttachmentGroupVo {
+
+    @Schema(description = "妯″潡鍚嶇О(濡傦細椤圭洰绠$悊銆侀攢鍞彴璐︾瓑)")
+    private String moduleName;
+
+    @Schema(description = "妯″潡鍐呭搴旂殑闄勪欢鍒楄〃")
+    private List<StorageBlobVO> files;
+
+    public AttachmentGroupVo() {
+    }
+
+    public AttachmentGroupVo(String moduleName, List<StorageBlobVO> files) {
+        this.moduleName = moduleName;
+        this.files = files;
+    }
+}
diff --git a/src/main/java/com/ruoyi/projectManagement/vo/ListInfoVo.java b/src/main/java/com/ruoyi/projectManagement/vo/ListInfoVo.java
index 6ad395f..a395fbc 100644
--- a/src/main/java/com/ruoyi/projectManagement/vo/ListInfoVo.java
+++ b/src/main/java/com/ruoyi/projectManagement/vo/ListInfoVo.java
@@ -41,6 +41,8 @@
 
     private String createUserName; // 鍒涘缓浜哄悕绉�
     private String updateUserName; // 鏇存柊浜哄悕绉�
+    // 閿�鍞悎鍚屾�绘暟
+    private Integer salesContractCount;
     // 鍏ㄩ儴闃舵淇℃伅
     private List<PlanStageDto> planStage;
 }
diff --git a/src/main/java/com/ruoyi/quality/controller/QualityInspectController.java b/src/main/java/com/ruoyi/quality/controller/QualityInspectController.java
index cc295d5..adfed28 100644
--- a/src/main/java/com/ruoyi/quality/controller/QualityInspectController.java
+++ b/src/main/java/com/ruoyi/quality/controller/QualityInspectController.java
@@ -154,4 +154,17 @@
     public void down(HttpServletResponse response, @RequestBody QualityInspect qualityInspect) {
         qualityInspectService.down(response, qualityInspect);
     }
+
+    /**
+     * 涓嬭浇PDF
+     *
+     * @param response
+     * @param qualityInspect
+     */
+    @PostMapping("/downPdf")
+    @Operation(summary = "涓嬭浇妫�楠孭DF")
+    @Log(title = "涓嬭浇妫�楠孭DF", businessType = BusinessType.OTHER)
+    public void downPdf(HttpServletResponse response, @RequestBody QualityInspect qualityInspect) {
+        qualityInspectService.downPdf(response, qualityInspect);
+    }
 }
diff --git a/src/main/java/com/ruoyi/quality/pojo/QualityInspectParam.java b/src/main/java/com/ruoyi/quality/pojo/QualityInspectParam.java
index f765f9f..c7a2fc0 100644
--- a/src/main/java/com/ruoyi/quality/pojo/QualityInspectParam.java
+++ b/src/main/java/com/ruoyi/quality/pojo/QualityInspectParam.java
@@ -7,6 +7,7 @@
 import lombok.Data;
 
 import java.io.Serializable;
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
 
 /**
@@ -50,6 +51,24 @@
     private String controlValue;
 
     /**
+     * 鍙傛暟绫诲瀷(0-鏂囧瓧, 1-鏁板瓧)
+     */
+    @Excel(name = "鍙傛暟绫诲瀷")
+    private Integer parameterType;
+
+    /**
+     * 鏈�澶у��
+     */
+    @Excel(name = "鏈�澶у��")
+    private BigDecimal maxValue;
+
+    /**
+     * 鏈�灏忓��
+     */
+    @Excel(name = "鏈�灏忓��")
+    private BigDecimal minValue;
+
+    /**
      * 妫�楠屽��
      */
     @Excel(name = "妫�楠屽��")
diff --git a/src/main/java/com/ruoyi/quality/pojo/QualityTestStandardParam.java b/src/main/java/com/ruoyi/quality/pojo/QualityTestStandardParam.java
index 2638991..716c119 100644
--- a/src/main/java/com/ruoyi/quality/pojo/QualityTestStandardParam.java
+++ b/src/main/java/com/ruoyi/quality/pojo/QualityTestStandardParam.java
@@ -6,6 +6,7 @@
 import com.baomidou.mybatisplus.annotation.TableId;
 import com.baomidou.mybatisplus.annotation.TableName;
 import java.io.Serializable;
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Getter;
@@ -42,6 +43,15 @@
     @Schema(description = "鍐呮帶鍊�")
     private String controlValue;
 
+    @Schema(description = "鍙傛暟绫诲瀷(0-鏂囧瓧, 1-鏁板瓧)")
+    private Integer parameterType;
+
+    @Schema(description = "鏈�澶у��")
+    private BigDecimal maxValue;
+
+    @Schema(description = "鏈�灏忓��")
+    private BigDecimal minValue;
+
     @Schema(description = "鍒涘缓鏃堕棿")
     @TableField(fill = FieldFill.INSERT)
     private LocalDateTime createTime;
diff --git a/src/main/java/com/ruoyi/quality/service/IQualityInspectService.java b/src/main/java/com/ruoyi/quality/service/IQualityInspectService.java
index 8b42207..658761c 100644
--- a/src/main/java/com/ruoyi/quality/service/IQualityInspectService.java
+++ b/src/main/java/com/ruoyi/quality/service/IQualityInspectService.java
@@ -27,4 +27,6 @@
     R autoSubmit(Long id);
 
     void down(HttpServletResponse response, QualityInspect qualityInspect);
+
+    void downPdf(HttpServletResponse response, QualityInspect qualityInspect);
 }
diff --git a/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java b/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
index 328c358..3e157ed 100644
--- a/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
+++ b/src/main/java/com/ruoyi/quality/service/impl/QualityInspectServiceImpl.java
@@ -244,6 +244,12 @@
         for (QualityInspectParam detail : paramList) {
             detail.setIndex(index);
             index++;
+            
+            if (detail.getParameterType() != null && detail.getParameterType() == 1) {
+                String min = detail.getMinValue() != null ? detail.getMinValue().stripTrailingZeros().toPlainString() : "";
+                String max = detail.getMaxValue() != null ? detail.getMaxValue().stripTrailingZeros().toPlainString() : "";
+                detail.setStandardValue(min + "~" + max);
+            }
         }
         InputStream inputStream = this.getClass().getResourceAsStream("/static/report-template.docx");
         Configure configure = Configure.builder()
@@ -276,6 +282,73 @@
     }
 
     @Override
+    public void downPdf(HttpServletResponse response, QualityInspect qualityInspect) {
+        QualityInspect inspect = qualityInspectMapper.selectById(qualityInspect.getId());
+        String inspectType = "";
+        switch (inspect.getInspectType()) {
+            case 0:
+                inspectType = "鍘熸潗鏂欐楠�";
+                break;
+            case 1:
+                inspectType = "杩囩▼妫�楠�";
+                break;
+            case 2:
+                inspectType = "鍑哄巶妫�楠�";
+                break;
+        }
+        List<QualityInspectParam> paramList = qualityInspectParamService.list(Wrappers.<QualityInspectParam>lambdaQuery().eq(QualityInspectParam::getInspectId, inspect.getId()));
+        int index = 1;
+        for (QualityInspectParam detail : paramList) {
+            detail.setIndex(index);
+            index++;
+            
+            if (detail.getParameterType() != null && detail.getParameterType() == 1) {
+                String min = detail.getMinValue() != null ? detail.getMinValue().stripTrailingZeros().toPlainString() : "";
+                String max = detail.getMaxValue() != null ? detail.getMaxValue().stripTrailingZeros().toPlainString() : "";
+                detail.setStandardValue(min + "~" + max);
+            }
+        }
+        InputStream inputStream = this.getClass().getResourceAsStream("/static/report-template.docx");
+        Configure configure = Configure.builder()
+                .bind("paramList", new HackLoopTableRenderPolicy())
+                .build();
+        String finalInspectType = inspectType;
+        XWPFTemplate template = XWPFTemplate.compile(inputStream, configure).render(
+                new HashMap<String, Object>() {{
+                    put("inspect", inspect);
+                    put("inspectType", finalInspectType);
+                    put("paramList", paramList);
+                }});
+
+        try {
+            // 鍏堝皢妯℃澘鍐欏叆鍒� ByteArrayOutputStream 涓�
+            java.io.ByteArrayOutputStream bos = new java.io.ByteArrayOutputStream();
+            template.write(bos);
+            bos.flush();
+            bos.close();
+            inputStream.close();
+            
+            // 浣跨敤 Spire.Doc 璇诲彇 docx 鏁版嵁娴佸苟杞负 PDF
+            java.io.ByteArrayInputStream bis = new java.io.ByteArrayInputStream(bos.toByteArray());
+            com.spire.doc.Document document = new com.spire.doc.Document(bis);
+
+            response.setContentType("application/pdf");
+            String fileName = URLEncoder.encode("妫�楠屾姤鍛�", "UTF-8");
+            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
+            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".pdf");
+            
+            java.io.OutputStream os = response.getOutputStream();
+            document.saveToStream(os, com.spire.doc.FileFormat.PDF);
+            os.flush();
+            os.close();
+            bis.close();
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new RuntimeException("瀵煎嚭澶辫触");
+        }
+    }
+
+    @Override
     public int updateQualityInspect(QualityInspectDto qualityInspectDto) {
         if (ObjectUtils.isNotNull(qualityInspectDto.getQualityInspectParams())) {
             qualityInspectParamService.remove(Wrappers.<QualityInspectParam>lambdaQuery().eq(QualityInspectParam::getInspectId, qualityInspectDto.getId()));
diff --git a/src/main/java/com/ruoyi/sales/controller/BiddingManagementController.java b/src/main/java/com/ruoyi/sales/controller/BiddingManagementController.java
new file mode 100644
index 0000000..1f6947f
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/controller/BiddingManagementController.java
@@ -0,0 +1,85 @@
+package com.ruoyi.sales.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.controller.BaseController;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.sales.dto.BiddingManagementDto;
+import com.ruoyi.sales.pojo.BiddingManagement;
+import com.ruoyi.sales.service.IBiddingManagementService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 鎷涙姇鏍囩鐞咰ontroller
+ */
+@RestController
+@RequestMapping("/sales/bidding")
+@AllArgsConstructor
+@Tag(name = "鎷涙姇鏍囩鐞�")
+public class BiddingManagementController extends BaseController {
+
+    private final IBiddingManagementService biddingManagementService;
+
+    /**
+     * 鏌ヨ鎷涙姇鏍囩鐞嗗垪琛�
+     */
+    @GetMapping("/listPage")
+    @Operation(summary = "鏌ヨ鎷涙姇鏍囩鐞嗗垪琛�(鍒嗛〉)")
+    public AjaxResult listPage(Page<BiddingManagement> page, BiddingManagementDto dto) {
+        IPage<BiddingManagement> iPage = biddingManagementService.selectBiddingManagementList(page, dto);
+        return AjaxResult.success(iPage);
+    }
+
+    /**
+     * 鑾峰彇鎷涙姇鏍囩鐞嗚缁嗕俊鎭�
+     */
+    @GetMapping("/getDetail")
+    @Operation(summary = "鑾峰彇鎷涙姇鏍囩鐞嗚缁嗕俊鎭�")
+    public AjaxResult getDetail(@RequestParam("id") Long id) {
+        return AjaxResult.success(biddingManagementService.getById(id));
+    }
+
+    /**
+     * 鏂板鎴栦慨鏀规嫑鎶曟爣绠$悊
+     */
+    @Log(title = "鎷涙姇鏍囩鐞�", businessType = BusinessType.INSERT)
+    @PostMapping("/addOrUpdate")
+    @Operation(summary = "鏂板鎴栦慨鏀规嫑鎶曟爣绠$悊")
+    public AjaxResult addOrUpdate(@RequestBody BiddingManagementDto dto) {
+        return toAjax(biddingManagementService.addOrUpdateBiddingManagement(dto));
+    }
+
+    /**
+     * 鍒犻櫎鎷涙姇鏍囩鐞�
+     */
+    @Log(title = "鎷涙姇鏍囩鐞�", businessType = BusinessType.DELETE)
+    @DeleteMapping("/del")
+    @Operation(summary = "鍒犻櫎鎷涙姇鏍囩鐞�")
+    public AjaxResult remove(@RequestBody Long[] ids) {
+        if (ids == null || ids.length == 0) {
+            return AjaxResult.error("璇蜂紶鍏ヨ鍒犻櫎鐨処D");
+        }
+        return toAjax(biddingManagementService.deleteBiddingManagementByIds(ids));
+    }
+
+    /**
+     * 瀵煎嚭鎷涙姇鏍囩鐞嗗垪琛�
+     */
+    @Log(title = "鎷涙姇鏍囩鐞�", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    @Operation(summary = "瀵煎嚭鎷涙姇鏍囩鐞�")
+    public void export(HttpServletResponse response, BiddingManagementDto dto) {
+        List<BiddingManagement> list = biddingManagementService.selectBiddingManagementList(dto);
+        ExcelUtil<BiddingManagement> util = new ExcelUtil<>(BiddingManagement.class);
+        util.exportExcel(response, list, "鎷涙姇鏍囩鐞嗘暟鎹�");
+    }
+}
diff --git a/src/main/java/com/ruoyi/sales/controller/MetricStatisticsController.java b/src/main/java/com/ruoyi/sales/controller/MetricStatisticsController.java
index 577508a..039ba67 100644
--- a/src/main/java/com/ruoyi/sales/controller/MetricStatisticsController.java
+++ b/src/main/java/com/ruoyi/sales/controller/MetricStatisticsController.java
@@ -41,18 +41,27 @@
         return metricStatisticsService.statisticsTable(statisticsTableDto);
     }
 
+    @GetMapping("/projectTransactions")
+    @Operation(summary = "钀ラ攢绠$悊--瀹㈡埛寰�鏉�-椤圭洰姹囨��")
+    public R projectTransactions(Page page, String projectName, String customerName, String contractName) {
+        return R.ok(customerService.projectTransactions(page, projectName, customerName, contractName));
+    }
+
     @GetMapping("/customewTransactions")
-    @Log(title = "瀹㈡埛寰�鏉�", businessType = BusinessType.OTHER)
-    @Operation(summary = "瀹㈡埛寰�鏉�")
-    public R customewTransactions(Page page, String customerName) {
-        return R.ok(customerService.customewTransactions(page,customerName));
+    @Operation(summary = "钀ラ攢绠$悊--瀹㈡埛寰�鏉�-瀹㈡埛姹囨��")
+    public R customewTransactions(Page page, Long projectId, String customerName, String contractName) {
+        return R.ok(customerService.customewTransactions(page, projectId, customerName, contractName));
     }
 
     @GetMapping("/customewTransactionsDetails")
-    @Log(title = "瀹㈡埛寰�鏉ユ槑缁�", businessType = BusinessType.OTHER)
-    @Operation(summary = "瀹㈡埛寰�鏉ユ槑缁�")
-    public R customewTransactionsDetails(Page page, Long customerId) {
-        return R.ok(customerService.customewTransactionsDetails(page,customerId));
+    @Operation(summary = "钀ラ攢绠$悊--瀹㈡埛寰�鏉ユ槑缁�-鍚堝悓绾�")
+    public R customewTransactionsDetails(Page page, Long projectId, Long customerId, String contractName) {
+        return R.ok(customerService.customewTransactionsDetails(page, projectId, customerId, contractName));
     }
 
+    @GetMapping("/transactionsSummary")
+    @Operation(summary = "钀ラ攢绠$悊--瀹㈡埛寰�鏉�-澶х洏鎬昏")
+    public R transactionsSummary() {
+        return R.ok(customerService.transactionsSummary());
+    }
 }
diff --git a/src/main/java/com/ruoyi/sales/controller/SalesContractController.java b/src/main/java/com/ruoyi/sales/controller/SalesContractController.java
new file mode 100644
index 0000000..67a0ed4
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/controller/SalesContractController.java
@@ -0,0 +1,81 @@
+package com.ruoyi.sales.controller;
+
+import com.ruoyi.framework.aspectj.lang.annotation.Log;
+import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
+import com.ruoyi.framework.web.controller.BaseController;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.sales.dto.SalesContractDTO;
+import com.ruoyi.sales.pojo.SalesContract;
+import com.ruoyi.sales.service.ISalesContractService;
+import com.ruoyi.sales.vo.SalesContractVO;
+import jakarta.annotation.Resource;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 閿�鍞悎鍚� Controller
+ */
+@RestController
+@RequestMapping("/sales/contract")
+public class SalesContractController extends BaseController {
+
+    @Resource
+    private ISalesContractService salesContractService;
+
+    /**
+     * 鏌ヨ閿�鍞悎鍚屽垎椤靛垪琛�
+     */
+    @GetMapping("/listPage")
+    public AjaxResult listPage(Page<SalesContract> page, SalesContractDTO dto) {
+        return AjaxResult.success(salesContractService.listPage(page, dto));
+    }
+
+    /**
+     * 鑾峰彇閿�鍞悎鍚岃鎯�
+     */
+    @GetMapping("/getDetail")
+    public AjaxResult getDetail(@RequestParam("id") Long id) {
+        return salesContractService.getDetail(id);
+    }
+
+    /**
+     * 鏌ヨ閿�鍞悎鍚屼笉鍒嗛〉鍒楄〃
+     */
+    @GetMapping("/listNoPage")
+    public AjaxResult listNoPage(SalesContractDTO dto) {
+        return AjaxResult.success(salesContractService.selectList(dto));
+    }
+
+    /**
+     * 鏂板鎴栦慨鏀归攢鍞悎鍚�
+     */
+    @Log(title = "閿�鍞悎鍚�", businessType = BusinessType.INSERT)
+    @PostMapping("/addOrUpdate")
+    public AjaxResult addOrUpdate(@RequestBody SalesContractDTO dto) {
+        return salesContractService.addOrUpdate(dto);
+    }
+
+    /**
+     * 鍒犻櫎閿�鍞悎鍚�
+     */
+    @Log(title = "閿�鍞悎鍚�", businessType = BusinessType.DELETE)
+    @DeleteMapping("/del")
+    public AjaxResult del(@RequestBody List<Long> ids) {
+        return salesContractService.del(ids);
+    }
+
+    /**
+     * 瀵煎嚭閿�鍞悎鍚�
+     */
+    @Log(title = "閿�鍞悎鍚�", businessType = BusinessType.EXPORT)
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, SalesContractDTO dto) {
+        List<SalesContract> list = salesContractService.selectList(dto);
+        ExcelUtil<SalesContract> util = new ExcelUtil<>(SalesContract.class);
+        util.exportExcel(response, list, "閿�鍞悎鍚屾暟鎹�");
+    }
+}
diff --git a/src/main/java/com/ruoyi/sales/controller/SalesLawsuitController.java b/src/main/java/com/ruoyi/sales/controller/SalesLawsuitController.java
new file mode 100644
index 0000000..18fd6fb
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/controller/SalesLawsuitController.java
@@ -0,0 +1,68 @@
+package com.ruoyi.sales.controller;
+
+import com.ruoyi.framework.web.controller.BaseController;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.sales.dto.SalesLawsuitDTO;
+import com.ruoyi.sales.pojo.SalesLawsuit;
+import com.ruoyi.sales.service.ISalesLawsuitService;
+import com.ruoyi.sales.vo.SalesLawsuitVO;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+/**
+ * 璇夎绠$悊 Controller
+ */
+@RestController
+@RequestMapping("/sales/lawsuit")
+public class SalesLawsuitController extends BaseController {
+
+    @Autowired
+    private ISalesLawsuitService salesLawsuitService;
+
+    /**
+     * 鍒嗛〉鏌ヨ璇夎鍒楄〃
+     */
+    @GetMapping("/listPage")
+    public AjaxResult listPage(Page<SalesLawsuit> page, SalesLawsuitDTO dto) {
+        return AjaxResult.success(salesLawsuitService.listPage(page, dto));
+    }
+
+    /**
+     * 璇夎璇︽儏鍥炴樉
+     */
+    @GetMapping("/getDetail")
+    public AjaxResult getDetail(@RequestParam("id") Long id) {
+        return salesLawsuitService.getDetail(id);
+    }
+
+    /**
+     * 鏂板鎴栨洿鏂拌瘔璁�
+     */
+    @PostMapping("/addOrUpdate")
+    public AjaxResult addOrUpdate(@RequestBody SalesLawsuitDTO dto) {
+        return salesLawsuitService.addOrUpdate(dto);
+    }
+
+    /**
+     * 鎵归噺鍒犻櫎璇夎
+     */
+    @DeleteMapping("/del")
+    public AjaxResult del(@RequestBody List<Long> ids) {
+        return salesLawsuitService.del(ids);
+    }
+
+    /**
+     * 瀵煎嚭璇夎璁板綍
+     */
+    @PostMapping("/export")
+    public void export(HttpServletResponse response, SalesLawsuitDTO dto) {
+        List<SalesLawsuit> list = salesLawsuitService.selectList(dto);
+        ExcelUtil<SalesLawsuit> util = new ExcelUtil<>(SalesLawsuit.class);
+        util.exportExcel(response, list, "璇夎鏁版嵁");
+    }
+}
diff --git a/src/main/java/com/ruoyi/sales/controller/ShippingInfoController.java b/src/main/java/com/ruoyi/sales/controller/ShippingInfoController.java
index 552aaa9..5782115 100644
--- a/src/main/java/com/ruoyi/sales/controller/ShippingInfoController.java
+++ b/src/main/java/com/ruoyi/sales/controller/ShippingInfoController.java
@@ -70,6 +70,11 @@
         if (byId == null) {
             return AjaxResult.error("鍙戣揣淇℃伅涓嶅瓨鍦�");
         }
+        if ("瀹℃牳閫氳繃".equals(byId.getStatus()) && !"宸插彂璐�".equals(req.getStatus())) {
+            // Note: If the request is from outbound management trying to change status to "宸插彂璐�", it's allowed.
+            // But if it's a normal edit, it's blocked. We just check if it's already 瀹℃牳閫氳繃 and they are updating other fields.
+            return AjaxResult.error("宸插鏍搁�氳繃锛屼笉鑳界紪杈戝彂璐т俊鎭�");
+        }
         boolean update = shippingInfoService.updateById(req);
         return update ? AjaxResult.success() : AjaxResult.error();
     }
@@ -79,7 +84,12 @@
     @Transactional(rollbackFor = Exception.class)
     @Log(title = "鍙戣揣淇℃伅绠$悊", businessType = BusinessType.DELETE)
     public AjaxResult delete(@RequestBody List<Long> ids) {
-
+        for (Long id : ids) {
+            ShippingInfo byId = shippingInfoService.getById(id);
+            if (byId != null && ("宸插彂璐�".equals(byId.getStatus()) || "瀹℃牳閫氳繃".equals(byId.getStatus()))) {
+                return AjaxResult.error("瀛樺湪宸插彂璐ф垨瀹℃牳閫氳繃鐨勪骇鍝侊紝涓嶈兘鍒犻櫎");
+            }
+        }
         return shippingInfoService.delete(ids) ? AjaxResult.success("鍒犻櫎鎴愬姛") : AjaxResult.error("鍒犻櫎澶辫触");
     }
 
@@ -88,8 +98,8 @@
      */
     @PostMapping("/export")
     @Operation(summary = "瀵煎嚭鍙戣揣淇℃伅")
-    public void export(HttpServletResponse response) {
-        List<ShippingInfo> list = shippingInfoMapper.listAll();
+    public void export(HttpServletResponse response, ShippingInfo req) {
+        List<ShippingInfo> list = shippingInfoMapper.listAll(req);
         ExcelUtil<ShippingInfo> util = new ExcelUtil<ShippingInfo>(ShippingInfo.class);
         util.exportExcel(response, list, "鍙戣揣淇℃伅");
     }
diff --git a/src/main/java/com/ruoyi/sales/dto/BiddingManagementDto.java b/src/main/java/com/ruoyi/sales/dto/BiddingManagementDto.java
new file mode 100644
index 0000000..4ad35b5
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/dto/BiddingManagementDto.java
@@ -0,0 +1,47 @@
+package com.ruoyi.sales.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+public class BiddingManagementDto {
+    private Long id;
+    
+    private Long projectId;
+    
+    private String projectName;
+    
+    private String bidDocName;
+    
+    private String tenderUnit;
+    
+    private String bidUnit;
+    
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private Date tenderDate;
+    
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private Date bidDate;
+    
+    private String bidDateStart;
+    
+    private String bidDateEnd;
+    
+    private BigDecimal bidDocFee;
+    
+    private String bidDocFeeStatus;
+    
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private Date bidDocFeeStatusChangeDate;
+    
+    private BigDecimal bidDeposit;
+    
+    private String bidResult;
+}
diff --git a/src/main/java/com/ruoyi/sales/dto/SalesContractDTO.java b/src/main/java/com/ruoyi/sales/dto/SalesContractDTO.java
new file mode 100644
index 0000000..31429a8
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/dto/SalesContractDTO.java
@@ -0,0 +1,33 @@
+package com.ruoyi.sales.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.sales.pojo.SalesContract;
+import com.ruoyi.sales.pojo.SalesContractProduct;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.time.LocalDate;
+import java.util.List;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class SalesContractDTO extends SalesContract {
+
+    /**
+     * 浜у搧鏄庣粏鍒楄〃
+     */
+    private List<SalesContractProduct> productList;
+
+    /**
+     * 绛捐鏃堕棿锛堟煡璇㈠紑濮嬫椂闂达級
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate signDateStart;
+
+    /**
+     * 绛捐鏃堕棿锛堟煡璇㈢粨鏉熸椂闂达級
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate signDateEnd;
+
+}
diff --git a/src/main/java/com/ruoyi/sales/dto/SalesLawsuitDTO.java b/src/main/java/com/ruoyi/sales/dto/SalesLawsuitDTO.java
new file mode 100644
index 0000000..425c3c4
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/dto/SalesLawsuitDTO.java
@@ -0,0 +1,33 @@
+package com.ruoyi.sales.dto;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.basic.dto.StorageBlobDTO;
+import com.ruoyi.sales.pojo.SalesLawsuit;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.time.LocalDate;
+import java.util.List;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class SalesLawsuitDTO extends SalesLawsuit {
+
+    /**
+     * 闄勪欢鍒楄〃
+     */
+    private List<StorageBlobDTO> storageBlobDTOs;
+
+    /**
+     * 绔嬫鏃堕棿锛堟煡璇㈠紑濮嬫椂闂达級
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate filingDateStart;
+
+    /**
+     * 绔嬫鏃堕棿锛堟煡璇㈢粨鏉熸椂闂达級
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate filingDateEnd;
+
+}
diff --git a/src/main/java/com/ruoyi/sales/dto/SalesLedgerDto.java b/src/main/java/com/ruoyi/sales/dto/SalesLedgerDto.java
index a4fc57c..8a4c044 100644
--- a/src/main/java/com/ruoyi/sales/dto/SalesLedgerDto.java
+++ b/src/main/java/com/ruoyi/sales/dto/SalesLedgerDto.java
@@ -8,7 +8,9 @@
 import com.ruoyi.sales.pojo.SalesLedgerProduct;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
 
+import java.math.BigDecimal;
 import java.time.LocalDate;
 import java.util.Date;
 import java.util.List;
@@ -63,4 +65,39 @@
 
     private List<StorageBlobDTO> storageBlobDTOs;
     private List<StorageBlobVO> StorageBlobVOs;
+
+    @Schema(description = "椤圭洰ID")
+    private Long projectId;
+
+    @Schema(description = "骞翠唤")
+    private Integer year;
+
+    @Schema(description = "绛惧崟鍗曚綅ID")
+    private Long signingUnitId;
+
+    @Schema(description = "鍔炰簨澶処D")
+    private Long officeId;
+
+    @Schema(description = "缁勮礋璐d汉ID")
+    private Long groupLeaderId;
+
+    @Schema(description = "鏄惁寮�鍙戠エ(1鏄� 0鍚�)")
+    private Integer isInvoice;
+
+    @Schema(description = "涓爣浠�")
+    private BigDecimal bidPrice;
+
+    @Schema(description = "鏄惁涓爣(1鏄� 0鍚�)")
+    private Integer isBid;
+
+    @Schema(description = "鏈腑鏍囧師鍥�")
+    private String unbidReason;
+
+    @Schema(description = "鍚堝悓鍘熶欢绾﹀畾鍥炰紶鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate contractOriginalReturnDate;
+
+    @Schema(description = "鍏宠仈鍚堝悓ID鍒楄〃")
+    private List<Long> associatedContractIds;
 }
diff --git a/src/main/java/com/ruoyi/sales/dto/SalesQuotationDto.java b/src/main/java/com/ruoyi/sales/dto/SalesQuotationDto.java
index 4b20ff8..0dd6e25 100644
--- a/src/main/java/com/ruoyi/sales/dto/SalesQuotationDto.java
+++ b/src/main/java/com/ruoyi/sales/dto/SalesQuotationDto.java
@@ -19,4 +19,7 @@
     private String approveUserIds;
 
     private Long templateId;
+
+    @Schema(description = "椤圭洰鍚嶇О")
+    private String projectName;
 }
diff --git a/src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java b/src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java
index 724473a..46eb543 100644
--- a/src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java
+++ b/src/main/java/com/ruoyi/sales/dto/ShippingInfoDto.java
@@ -6,6 +6,7 @@
 import com.ruoyi.sales.pojo.CommonFile;
 import com.ruoyi.sales.pojo.ShippingInfo;
 import com.ruoyi.sales.pojo.ShippingProductDetail;
+import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
 import java.math.BigDecimal;
@@ -31,6 +32,14 @@
     @TableField(exist = false)
     private List<CommonFile> commonFileList;
 
+    @Schema(description = "閫�璐у崟鍙烽泦鍚�")
+    @TableField(exist = false)
+    private List<String> returnNoList;
+
+    @Schema(description = "閫�璐у崟鍙峰瓧绗︿覆")
+    @TableField(exist = false)
+    private String returnNos;
+
     private String  specificationModel;
 
     private String productName;
diff --git a/src/main/java/com/ruoyi/sales/mapper/BiddingManagementMapper.java b/src/main/java/com/ruoyi/sales/mapper/BiddingManagementMapper.java
new file mode 100644
index 0000000..1b3c005
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/mapper/BiddingManagementMapper.java
@@ -0,0 +1,12 @@
+package com.ruoyi.sales.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.sales.pojo.BiddingManagement;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 鎷涙姇鏍囩鐞哅apper鎺ュ彛
+ */
+@Mapper
+public interface BiddingManagementMapper extends BaseMapper<BiddingManagement> {
+}
diff --git a/src/main/java/com/ruoyi/sales/mapper/SalesContractMapper.java b/src/main/java/com/ruoyi/sales/mapper/SalesContractMapper.java
new file mode 100644
index 0000000..3bad994
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/mapper/SalesContractMapper.java
@@ -0,0 +1,12 @@
+package com.ruoyi.sales.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.sales.pojo.SalesContract;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 閿�鍞悎鍚� Mapper 鎺ュ彛
+ */
+@Mapper
+public interface SalesContractMapper extends BaseMapper<SalesContract> {
+}
diff --git a/src/main/java/com/ruoyi/sales/mapper/SalesContractProductMapper.java b/src/main/java/com/ruoyi/sales/mapper/SalesContractProductMapper.java
new file mode 100644
index 0000000..a769682
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/mapper/SalesContractProductMapper.java
@@ -0,0 +1,12 @@
+package com.ruoyi.sales.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.sales.pojo.SalesContractProduct;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 閿�鍞悎鍚屼骇鍝佹槑缁� Mapper 鎺ュ彛
+ */
+@Mapper
+public interface SalesContractProductMapper extends BaseMapper<SalesContractProduct> {
+}
diff --git a/src/main/java/com/ruoyi/sales/mapper/SalesLawsuitMapper.java b/src/main/java/com/ruoyi/sales/mapper/SalesLawsuitMapper.java
new file mode 100644
index 0000000..e040cd1
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/mapper/SalesLawsuitMapper.java
@@ -0,0 +1,12 @@
+package com.ruoyi.sales.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.sales.pojo.SalesLawsuit;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 璇夎绠$悊 Mapper 鎺ュ彛
+ */
+@Mapper
+public interface SalesLawsuitMapper extends BaseMapper<SalesLawsuit> {
+}
diff --git a/src/main/java/com/ruoyi/sales/mapper/SalesLedgerContractRelMapper.java b/src/main/java/com/ruoyi/sales/mapper/SalesLedgerContractRelMapper.java
new file mode 100644
index 0000000..4ea83c9
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/mapper/SalesLedgerContractRelMapper.java
@@ -0,0 +1,13 @@
+package com.ruoyi.sales.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.sales.pojo.SalesLedgerContractRel;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 閿�鍞彴璐︿笌鍚堝悓鍏宠仈琛∕apper鎺ュ彛
+ */
+@Mapper
+public interface SalesLedgerContractRelMapper extends BaseMapper<SalesLedgerContractRel> {
+
+}
diff --git a/src/main/java/com/ruoyi/sales/mapper/ShippingInfoMapper.java b/src/main/java/com/ruoyi/sales/mapper/ShippingInfoMapper.java
index 07bddbb..da6573d 100644
--- a/src/main/java/com/ruoyi/sales/mapper/ShippingInfoMapper.java
+++ b/src/main/java/com/ruoyi/sales/mapper/ShippingInfoMapper.java
@@ -17,7 +17,7 @@
 public interface ShippingInfoMapper extends BaseMapper<ShippingInfo> {
     IPage<ShippingInfoDto> listPage(Page page, @Param("req") ShippingInfo req);
 
-    List<ShippingInfo> listAll();
+    List<ShippingInfo> listAll(@Param("req") ShippingInfo req);
 
     List<ShippingProductVo> getReturnManagementDtoById(@Param("shippingId")Long shippingId);
 
diff --git a/src/main/java/com/ruoyi/sales/pojo/BiddingManagement.java b/src/main/java/com/ruoyi/sales/pojo/BiddingManagement.java
new file mode 100644
index 0000000..db9c5da
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/pojo/BiddingManagement.java
@@ -0,0 +1,155 @@
+package com.ruoyi.sales.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.framework.aspectj.lang.annotation.Excel;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.Date;
+
+/**
+ * 鎷涙姇鏍囩鐞嗗璞� bidding_management
+ */
+@TableName(value = "bidding_management")
+@Data
+public class BiddingManagement implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 涓婚敭ID
+     */
+    @TableId(value = "id", type = IdType.AUTO)
+    private Long id;
+
+    /**
+     * 鎵�灞為」鐩甀D
+     */
+    private Long projectId;
+
+    /**
+     * 鎵�灞為」鐩悕绉�
+     */
+    @Excel(name = "鎵�灞為」鐩悕绉�")
+    private String projectName;
+
+    /**
+     * 鏍囦功鍚嶇О
+     */
+    @Excel(name = "鏍囦功鍚嶇О")
+    private String bidDocName;
+
+    /**
+     * 鎷涙爣鍗曚綅
+     */
+    @Excel(name = "鎷涙爣鍗曚綅")
+    private String tenderUnit;
+
+    /**
+     * 鎶曟爣鍗曚綅
+     */
+    @Excel(name = "鎶曟爣鍗曚綅")
+    private String bidUnit;
+
+    /**
+     * 鎷涙爣鏃ユ湡
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "鎷涙爣鏃ユ湡", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date tenderDate;
+
+    /**
+     * 鎶曟爣鏃ユ湡
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "鎶曟爣鏃ユ湡", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date bidDate;
+
+    /**
+     * 鏍囦功娆鹃噾棰�
+     */
+    @Excel(name = "鏍囦功娆鹃噾棰�")
+    private BigDecimal bidDocFee;
+
+    /**
+     * 鏍囦功娆剧姸鎬�
+     */
+    @Excel(name = "鏍囦功娆剧姸鎬�")
+    private String bidDocFeeStatus;
+
+    /**
+     * 鏍囦功娆剧姸鎬佸彉鏇存棩鏈�
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "鏍囦功娆剧姸鎬佸彉鏇存棩鏈�", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date bidDocFeeStatusChangeDate;
+
+    /**
+     * 鎶曟爣淇濊瘉閲戦噾棰�
+     */
+    @Excel(name = "鎶曟爣淇濊瘉閲戦噾棰�")
+    private BigDecimal bidDeposit;
+
+    /**
+     * 鎷涙姇鏍囩粨鏋�
+     */
+    @Excel(name = "鎷涙姇鏍囩粨鏋�")
+    private String bidResult;
+
+    /**
+     * 绉熸埛ID
+     */
+    @TableField(fill = FieldFill.INSERT)
+    private Long tenantId;
+
+    /**
+     * 閮ㄩ棬ID
+     */
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+
+    /**
+     * 鍒犻櫎鏍囧織锛�0浠h〃瀛樺湪 2浠h〃鍒犻櫎锛�
+     */
+    @TableLogic
+    private String delFlag;
+
+    /**
+     * 鍒涘缓鑰�
+     */
+    @TableField(fill = FieldFill.INSERT)
+    private Integer createUser;
+
+    /**
+     * 鍒涘缓鏃堕棿
+     */
+    @TableField(fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime createTime;
+
+    /**
+     * 鏇存柊鑰�
+     */
+    @TableField(fill = FieldFill.UPDATE)
+    private Integer updateUser;
+
+    /**
+     * 鏇存柊鏃堕棿
+     */
+    @TableField(fill = FieldFill.UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private LocalDateTime updateTime;
+
+    /**
+     * 澶囨敞
+     */
+    @Excel(name = "澶囨敞")
+    private String remark;
+}
diff --git a/src/main/java/com/ruoyi/sales/pojo/SalesContract.java b/src/main/java/com/ruoyi/sales/pojo/SalesContract.java
new file mode 100644
index 0000000..f714169
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/pojo/SalesContract.java
@@ -0,0 +1,145 @@
+package com.ruoyi.sales.pojo;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.framework.aspectj.lang.annotation.Excel;
+import lombok.Data;
+import java.time.LocalDateTime;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+/**
+ * 閿�鍞悎鍚屼富琛ㄥ疄浣�
+ */
+@Data
+@TableName("sales_contract")
+public class SalesContract {
+
+    private static final long serialVersionUID = 1L;
+
+    /** 涓婚敭 */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 鍚堝悓缂栧彿 */
+    @Excel(name = "鍚堝悓缂栧彿")
+    private String contractNo;
+
+    /** 绛捐鏃堕棿 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "绛捐鏃堕棿", width = 30, dateFormat = "yyyy-MM-dd")
+    private LocalDate signDate;
+
+    /** 绛剧害鍗曚綅绠�绉� */
+    @Excel(name = "绛剧害鍗曚綅绠�绉�")
+    private String signingUnit;
+
+    /** 绛剧害鍛ㄦ湡 */
+    @Excel(name = "绛剧害鍛ㄦ湡")
+    private String contractPeriod;
+
+    /** 瀛樻。鐘舵�� */
+    @Excel(name = "瀛樻。鐘舵��")
+    private String archiveStatus;
+
+    /** 瀛樻。鎯呭喌璇存槑 */
+    @Excel(name = "瀛樻。鎯呭喌璇存槑")
+    private String archiveRemark;
+
+    /** 灞ョ害淇濊瘉閲� */
+    @Excel(name = "灞ョ害淇濊瘉閲�")
+    private BigDecimal performanceBond;
+
+    /** 璐ㄤ繚閲戞儏鍐� */
+    @Excel(name = "璐ㄤ繚閲戞儏鍐�")
+    private BigDecimal warrantyDeposit;
+
+    /** 浜у�硷紙鑷姩姹囨�伙級 */
+    @Excel(name = "浜у��")
+    private BigDecimal outputValue;
+
+    /** 宸插洖娆鹃噾棰� */
+    @Excel(name = "宸插洖娆鹃噾棰�")
+    private BigDecimal receivedAmount;
+
+    /** 缁撶畻娆℃暟 */
+    @Excel(name = "缁撶畻娆℃暟")
+    private Integer settlementCount;
+
+    /** 搴旀敹閲戦 */
+    @Excel(name = "搴旀敹閲戦")
+    private BigDecimal receivableAmount;
+
+    /** 搴斾粯姣斾緥 */
+    @Excel(name = "搴斾粯姣斾緥")
+    private String payableRatio;
+
+    /** 鍚堝悓鑺傜偣搴旀敹 */
+    @Excel(name = "鍚堝悓鑺傜偣搴旀敹")
+    private BigDecimal nodeReceivable;
+
+    /** 鍚堝悓绾﹀畾浠樻鑺傜偣 */
+    @Excel(name = "鍚堝悓绾﹀畾浠樻鑺傜偣")
+    private String paymentNodes;
+
+    /** 缁撶畻璁板綍 */
+    @Excel(name = "缁撶畻璁板綍")
+    private String settlementRecord;
+
+    /** 寮�绁ㄨ姹� */
+    @Excel(name = "寮�绁ㄨ姹�")
+    private String invoiceRequirement;
+
+    /** 鏈�杩戜竴娆″紑绁ㄦ椂闂� */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "鏈�杩戜竴娆″紑绁ㄦ椂闂�", width = 30, dateFormat = "yyyy-MM-dd")
+    private LocalDate lastInvoiceDate;
+
+    /** 宸插紑绁ㄩ噾棰� */
+    @Excel(name = "宸插紑绁ㄩ噾棰�")
+    private BigDecimal invoicedAmount;
+
+    /** 杩濈害閲戠害瀹� */
+    @Excel(name = "杩濈害閲戠害瀹�")
+    private String penaltyAgreement;
+
+    /** 鎯呭喌璇存槑 */
+    @Excel(name = "鎯呭喌璇存槑")
+    private String remark;
+
+    /** 鍒涘缓鏃堕棿 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    /** 鍒涘缓鑰� */
+    @TableField(fill = FieldFill.INSERT)
+    private Integer createUser;
+
+    /** 鏇存柊鏃堕棿 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @TableField(fill = FieldFill.UPDATE)
+    private LocalDateTime updateTime;
+
+    /** 鏇存柊鑰� */
+    @TableField(fill = FieldFill.UPDATE)
+    private Integer updateUser;
+
+    /** 绉熸埛ID */
+    @TableField(fill = FieldFill.INSERT)
+    private Integer tenantId;
+
+    /** 閮ㄩ棬ID */
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+
+    /** 鍒犻櫎鏍囧織 */
+    @TableLogic
+    private String delFlag;
+}
diff --git a/src/main/java/com/ruoyi/sales/pojo/SalesContractProduct.java b/src/main/java/com/ruoyi/sales/pojo/SalesContractProduct.java
new file mode 100644
index 0000000..5ed1c6d
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/pojo/SalesContractProduct.java
@@ -0,0 +1,84 @@
+package com.ruoyi.sales.pojo;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import java.time.LocalDateTime;
+
+import java.math.BigDecimal;
+
+/**
+ * 閿�鍞悎鍚屼骇鍝佹槑缁嗚〃瀹炰綋
+ */
+@Data
+@TableName("sales_contract_product")
+public class SalesContractProduct {
+
+    private static final long serialVersionUID = 1L;
+
+    /** 涓婚敭 */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 鍏宠仈閿�鍞悎鍚� ID */
+    private Long contractId;
+
+    /** 浜у搧澶х被 */
+    private String productCategory;
+
+    /** 瑙勬牸鍨嬪彿 */
+    private String specificationModel;
+
+    /** 瑙勬牸鍨嬪彿 ID */
+    private Long productModelId;
+
+    /** 鍚◣鍗曚环 */
+    private BigDecimal taxInclusiveUnitPrice;
+
+    /** 绾﹀畾/鏆備及鐢ㄩ噺 */
+    private BigDecimal agreedQuantity;
+
+    /** 鍚堝悓璁¢噺鍗曚綅 */
+    private String contractUnit;
+
+    /** 宸插彂璐ф暟閲� */
+    private BigDecimal shippedQuantity;
+
+    /** 浜у�� */
+    private BigDecimal outputValue;
+
+    /** 鍒涘缓鏃堕棿 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    /** 鍒涘缓鑰� */
+    @TableField(fill = FieldFill.INSERT)
+    private Integer createUser;
+
+    /** 鏇存柊鏃堕棿 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @TableField(fill = FieldFill.UPDATE)
+    private LocalDateTime updateTime;
+
+    /** 鏇存柊鑰� */
+    @TableField(fill = FieldFill.UPDATE)
+    private Integer updateUser;
+
+    /** 绉熸埛ID */
+    @TableField(fill = FieldFill.INSERT)
+    private Integer tenantId;
+
+    /** 閮ㄩ棬ID */
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+
+    /** 鍒犻櫎鏍囧織 */
+    @TableLogic
+    private String delFlag;
+}
diff --git a/src/main/java/com/ruoyi/sales/pojo/SalesLawsuit.java b/src/main/java/com/ruoyi/sales/pojo/SalesLawsuit.java
new file mode 100644
index 0000000..c597afa
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/pojo/SalesLawsuit.java
@@ -0,0 +1,107 @@
+package com.ruoyi.sales.pojo;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ruoyi.framework.aspectj.lang.annotation.Excel;
+import lombok.Data;
+import java.time.LocalDateTime;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+
+/**
+ * 璇夎绠$悊 涓昏〃瀹炰綋
+ */
+@Data
+@TableName("sales_lawsuit")
+public class SalesLawsuit {
+
+    private static final long serialVersionUID = 1L;
+
+    /** 涓婚敭 */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 妗堜欢缂栧彿 */
+    @Excel(name = "妗堜欢缂栧彿")
+    private String caseNo;
+
+    /** 鎵�灞為」鐩� ID */
+    private Long projectId;
+
+    /** 鎵�灞為」鐩悕绉� */
+    @Excel(name = "鎵�灞為」鐩悕绉�")
+    private String projectName;
+
+    /** 鍏宠仈閿�鍞悎鍚� ID */
+    private Long contractId;
+
+    /** 鍚堝悓缂栧彿 */
+    @Excel(name = "鍚堝悓缂栧彿")
+    private String contractNo;
+
+    /** 璇夎鐘舵�� */
+    @Excel(name = "璇夎鐘舵��")
+    private String lawsuitStatus;
+
+    /** 绔嬫鏃堕棿 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "绔嬫鏃堕棿", width = 30, dateFormat = "yyyy-MM-dd")
+    private LocalDate filingDate;
+
+    /** 鎵垮姙寰嬪笀/娉曞姟璐熻矗浜� */
+    @Excel(name = "鎵垮姙寰嬪笀/娉曞姟璐熻矗浜�")
+    private String lawyer;
+
+    /** 寮�搴椂闂� */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "寮�搴椂闂�", width = 30, dateFormat = "yyyy-MM-dd")
+    private LocalDate hearingDate;
+
+    /** 璇夎鏍囩殑閲戦 */
+    @Excel(name = "璇夎鏍囩殑閲戦")
+    private BigDecimal claimAmount;
+
+    /** 鍒ゅ喅缁撴灉鎽樿 */
+    @Excel(name = "鍒ゅ喅缁撴灉鎽樿")
+    private String judgmentSummary;
+
+    /** 璇夎杩涘睍澶囨敞 */
+    @Excel(name = "璇夎杩涘睍澶囨敞")
+    private String progressRemark;
+
+    /** 鍒涘缓鏃堕棿 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    /** 鍒涘缓鑰� */
+    @TableField(fill = FieldFill.INSERT)
+    private Integer createUser;
+
+    /** 鏇存柊鏃堕棿 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @TableField(fill = FieldFill.UPDATE)
+    private LocalDateTime updateTime;
+
+    /** 鏇存柊鑰� */
+    @TableField(fill = FieldFill.UPDATE)
+    private Integer updateUser;
+
+    /** 绉熸埛ID */
+    @TableField(fill = FieldFill.INSERT)
+    private Integer tenantId;
+
+    /** 閮ㄩ棬ID */
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+
+    /** 鍒犻櫎鏍囧織 */
+    @TableLogic
+    private String delFlag;
+}
diff --git a/src/main/java/com/ruoyi/sales/pojo/SalesLedger.java b/src/main/java/com/ruoyi/sales/pojo/SalesLedger.java
index c8a0443..8472ca4 100644
--- a/src/main/java/com/ruoyi/sales/pojo/SalesLedger.java
+++ b/src/main/java/com/ruoyi/sales/pojo/SalesLedger.java
@@ -10,6 +10,7 @@
 import java.math.BigDecimal;
 import java.time.LocalDate;
 import java.util.Date;
+import java.util.List;
 
 /**
  * 閿�鍞彴璐﹀璞� sales_ledger
@@ -143,5 +144,43 @@
     @TableField(exist = false)
     private Boolean hasProductionRecord;
 
+    @Schema(description = "椤圭洰ID")
+    private Long projectId;
+
+    @Schema(description = "骞翠唤")
+    @Excel(name = "骞翠唤")
+    private Integer year;
+
+    @Schema(description = "绛惧崟鍗曚綅ID")
+    private Long signingUnitId;
+
+    @Schema(description = "鍔炰簨澶処D")
+    private Long officeId;
+
+    @Schema(description = "缁勮礋璐d汉ID")
+    private Long groupLeaderId;
+
+    @Schema(description = "鏄惁寮�鍙戠エ(1鏄� 0鍚�)")
+    private Integer isInvoice;
+
+    @Schema(description = "涓爣浠�")
+    @Excel(name = "涓爣浠�")
+    private BigDecimal bidPrice;
+
+    @Schema(description = "鏄惁涓爣(1鏄� 0鍚�)")
+    private Integer isBid;
+
+    @Schema(description = "鏈腑鏍囧師鍥�")
+    private String unbidReason;
+
+    @Schema(description = "鍚堝悓鍘熶欢绾﹀畾鍥炰紶鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    @Excel(name = "鍚堝悓鍘熶欢绾﹀畾鍥炰紶鏃ユ湡", width = 30, dateFormat = "yyyy-MM-dd")
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate contractOriginalReturnDate;
+
+    @TableField(exist = false)
+    @Schema(description = "鍏宠仈鍚堝悓ID鍒楄〃")
+    private List<Long> associatedContractIds;
 }
 
diff --git a/src/main/java/com/ruoyi/sales/pojo/SalesLedgerContractRel.java b/src/main/java/com/ruoyi/sales/pojo/SalesLedgerContractRel.java
new file mode 100644
index 0000000..55f2262
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/pojo/SalesLedgerContractRel.java
@@ -0,0 +1,47 @@
+package com.ruoyi.sales.pojo;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableLogic;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ * 閿�鍞彴璐︿笌鍚堝悓鍏宠仈琛ㄥ疄浣�
+ */
+@Data
+@TableName("sales_ledger_contract_rel")
+public class SalesLedgerContractRel implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /** 涓婚敭 */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 閿�鍞彴璐D */
+    private Long salesLedgerId;
+
+    /** 鍚堝悓ID */
+    private Long contractId;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Integer createUser;
+
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Integer updateUser;
+
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+
+    @TableLogic
+    private String delFlag;
+}
diff --git a/src/main/java/com/ruoyi/sales/pojo/SalesQuotation.java b/src/main/java/com/ruoyi/sales/pojo/SalesQuotation.java
index d8a97a6..fac84c7 100644
--- a/src/main/java/com/ruoyi/sales/pojo/SalesQuotation.java
+++ b/src/main/java/com/ruoyi/sales/pojo/SalesQuotation.java
@@ -45,6 +45,10 @@
     private String deliveryPeriod;
     @ApiModelProperty(value = "鐘舵��")
     private String status;
+    
+    @ApiModelProperty(value = "椤圭洰ID")
+    private Long projectId;
+
     @ApiModelProperty(value = "鎶ヤ环鎬婚噾棰�")
     @Excel(name = "鎶ヤ环閲戦")
     private BigDecimal totalAmount;
diff --git a/src/main/java/com/ruoyi/sales/pojo/ShippingInfo.java b/src/main/java/com/ruoyi/sales/pojo/ShippingInfo.java
index ed9a268..d485cb9 100644
--- a/src/main/java/com/ruoyi/sales/pojo/ShippingInfo.java
+++ b/src/main/java/com/ruoyi/sales/pojo/ShippingInfo.java
@@ -91,4 +91,44 @@
 
     @TableField(fill = FieldFill.INSERT)
     private Long deptId;
+
+    @Schema(description = "鍙戣揣鍗曟嵁锛堟枃鏈級")
+    @Excel(name = "鍙戣揣鍗曟嵁")
+    private String shippingDocument;
+
+    @Schema(description = "鏄惁瀵硅处锛堝嬀閫夋锛�0鍚� 1鏄級")
+    @Excel(name = "鏄惁瀵硅处")
+    private Integer isReconciled;
+
+    @Schema(description = "瀵硅处鍗曞綊妗f儏鍐碉紙鏂囨湰鍩燂級")
+    @Excel(name = "瀵硅处鍗曞綊妗f儏鍐�")
+    private String statementArchivingStatus;
+
+    @Schema(description = "鏄惁缁撶畻锛堝嬀閫夋锛�0鍚� 1鏄級")
+    @Excel(name = "鏄惁缁撶畻")
+    private Integer isSettled;
+
+    @Schema(description = "缁撶畻鍗曞綊妗f儏鍐碉紙鏂囨湰鍩燂級")
+    @Excel(name = "缁撶畻鍗曞綊妗f儏鍐�")
+    private String settlementArchivingStatus;
+
+    @Schema(description = "鏄惁宸插紑绁紙鍕鹃�夋锛�0鍚� 1鏄級")
+    @Excel(name = "鏄惁宸插紑绁�")
+    private Integer isInvoiced;
+
+    @Schema(description = "椤圭洰ID")
+    private Long projectId;
+
+    @TableField(exist = false)
+    @Schema(description = "椤圭洰鍚嶇О")
+    @Excel(name = "椤圭洰鍚嶇О")
+    private String projectName;
+
+    @TableField(exist = false)
+    @Schema(description = "寮�濮嬪彂璐ф棩鏈�")
+    private String beginShippingDate;
+
+    @TableField(exist = false)
+    @Schema(description = "缁撴潫鍙戣揣鏃ユ湡")
+    private String endShippingDate;
 }
diff --git a/src/main/java/com/ruoyi/sales/service/IBiddingManagementService.java b/src/main/java/com/ruoyi/sales/service/IBiddingManagementService.java
new file mode 100644
index 0000000..f51793f
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/service/IBiddingManagementService.java
@@ -0,0 +1,48 @@
+package com.ruoyi.sales.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.sales.dto.BiddingManagementDto;
+import com.ruoyi.sales.pojo.BiddingManagement;
+
+import java.util.List;
+
+/**
+ * 鎷涙姇鏍囩鐞哠ervice鎺ュ彛
+ */
+public interface IBiddingManagementService extends IService<BiddingManagement> {
+
+    /**
+     * 鏌ヨ鎷涙姇鏍囩鐞嗗垪琛� (鍒嗛〉)
+     *
+     * @param page 鍒嗛〉瀵硅薄
+     * @param dto 鏌ヨ鏉′欢
+     * @return 鎷涙姇鏍囩鐞嗛泦鍚�
+     */
+    IPage<BiddingManagement> selectBiddingManagementList(Page<BiddingManagement> page, BiddingManagementDto dto);
+
+    /**
+     * 鏌ヨ鎷涙姇鏍囩鐞嗗垪琛� (涓嶅垎椤�)
+     *
+     * @param dto 鏌ヨ鏉′欢
+     * @return 鎷涙姇鏍囩鐞嗛泦鍚�
+     */
+    List<BiddingManagement> selectBiddingManagementList(BiddingManagementDto dto);
+
+    /**
+     * 鏂板鎴栦慨鏀规嫑鎶曟爣绠$悊
+     *
+     * @param dto 鎷涙姇鏍囩鐞嗕俊鎭�
+     * @return 缁撴灉
+     */
+    int addOrUpdateBiddingManagement(BiddingManagementDto dto);
+
+    /**
+     * 鎵归噺鍒犻櫎鎷涙姇鏍囩鐞�
+     *
+     * @param ids 闇�瑕佸垹闄ょ殑ID鏁扮粍
+     * @return 缁撴灉
+     */
+    int deleteBiddingManagementByIds(Long[] ids);
+}
diff --git a/src/main/java/com/ruoyi/sales/service/ISalesContractService.java b/src/main/java/com/ruoyi/sales/service/ISalesContractService.java
new file mode 100644
index 0000000..0b81173
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/service/ISalesContractService.java
@@ -0,0 +1,42 @@
+package com.ruoyi.sales.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.sales.dto.SalesContractDTO;
+import com.ruoyi.sales.pojo.SalesContract;
+import com.ruoyi.sales.vo.SalesContractVO;
+
+import java.util.List;
+
+/**
+ * 閿�鍞悎鍚� Service 鎺ュ彛
+ */
+public interface ISalesContractService extends IService<SalesContract> {
+
+    /**
+     * 鍒嗛〉鏌ヨ鍚堝悓鍒楄〃
+     */
+    IPage<SalesContractVO> listPage(Page<SalesContract> page, SalesContractDTO dto);
+
+    /**
+     * 鑾峰彇鍚堝悓璇︽儏锛堝惈鏄庣粏锛�
+     */
+    AjaxResult getDetail(Long id);
+
+    /**
+     * 鏂板鎴栨洿鏂板悎鍚�
+     */
+    AjaxResult addOrUpdate(SalesContractDTO dto);
+
+    /**
+     * 鍒犻櫎鍚堝悓
+     */
+    AjaxResult del(List<Long> ids);
+
+    /**
+     * 瀵煎嚭鏌ヨ鍒楄〃 (鏃犲垎椤�)
+     */
+    List<SalesContract> selectList(SalesContractDTO dto);
+}
diff --git a/src/main/java/com/ruoyi/sales/service/ISalesLawsuitService.java b/src/main/java/com/ruoyi/sales/service/ISalesLawsuitService.java
new file mode 100644
index 0000000..712d197
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/service/ISalesLawsuitService.java
@@ -0,0 +1,42 @@
+package com.ruoyi.sales.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.sales.dto.SalesLawsuitDTO;
+import com.ruoyi.sales.pojo.SalesLawsuit;
+import com.ruoyi.sales.vo.SalesLawsuitVO;
+
+import java.util.List;
+
+/**
+ * 璇夎绠$悊 Service 鎺ュ彛
+ */
+public interface ISalesLawsuitService extends IService<SalesLawsuit> {
+
+    /**
+     * 鍒嗛〉鏌ヨ
+     */
+    IPage<SalesLawsuitVO> listPage(Page<SalesLawsuit> page, SalesLawsuitDTO dto);
+
+    /**
+     * 鑾峰彇璇︽儏
+     */
+    AjaxResult getDetail(Long id);
+
+    /**
+     * 鏂板鎴栦慨鏀�
+     */
+    AjaxResult addOrUpdate(SalesLawsuitDTO dto);
+
+    /**
+     * 鍒犻櫎
+     */
+    AjaxResult del(List<Long> ids);
+
+    /**
+     * 瀵煎嚭鏌ヨ鍒楄〃 (鏃犲垎椤�)
+     */
+    List<SalesLawsuit> selectList(SalesLawsuitDTO dto);
+}
diff --git a/src/main/java/com/ruoyi/sales/service/impl/BiddingManagementServiceImpl.java b/src/main/java/com/ruoyi/sales/service/impl/BiddingManagementServiceImpl.java
new file mode 100644
index 0000000..f45c738
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/service/impl/BiddingManagementServiceImpl.java
@@ -0,0 +1,66 @@
+package com.ruoyi.sales.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.sales.dto.BiddingManagementDto;
+import com.ruoyi.sales.mapper.BiddingManagementMapper;
+import com.ruoyi.sales.pojo.BiddingManagement;
+import com.ruoyi.sales.service.IBiddingManagementService;
+import org.springframework.beans.BeanUtils;
+import org.springframework.stereotype.Service;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 鎷涙姇鏍囩鐞哠ervice涓氬姟灞傚鐞�
+ */
+@Service
+public class BiddingManagementServiceImpl extends ServiceImpl<BiddingManagementMapper, BiddingManagement> implements IBiddingManagementService {
+
+    @Override
+    public IPage<BiddingManagement> selectBiddingManagementList(Page<BiddingManagement> page, BiddingManagementDto dto) {
+        LambdaQueryWrapper<BiddingManagement> queryWrapper = buildQueryWrapper(dto);
+        queryWrapper.orderByDesc(BiddingManagement::getCreateTime);
+        return this.page(page, queryWrapper);
+    }
+
+    @Override
+    public List<BiddingManagement> selectBiddingManagementList(BiddingManagementDto dto) {
+        LambdaQueryWrapper<BiddingManagement> queryWrapper = buildQueryWrapper(dto);
+        queryWrapper.orderByDesc(BiddingManagement::getCreateTime);
+        return this.list(queryWrapper);
+    }
+
+    @Override
+    public int addOrUpdateBiddingManagement(BiddingManagementDto dto) {
+        BiddingManagement biddingManagement = new BiddingManagement();
+        BeanUtils.copyProperties(dto, biddingManagement);
+        
+        if (biddingManagement.getId() == null) {
+            return this.save(biddingManagement) ? 1 : 0;
+        } else {
+            return this.updateById(biddingManagement) ? 1 : 0;
+        }
+    }
+
+    @Override
+    public int deleteBiddingManagementByIds(Long[] ids) {
+        return this.removeByIds(Arrays.asList(ids)) ? ids.length : 0;
+    }
+
+    private LambdaQueryWrapper<BiddingManagement> buildQueryWrapper(BiddingManagementDto dto) {
+        LambdaQueryWrapper<BiddingManagement> queryWrapper = new LambdaQueryWrapper<>();
+        if (dto != null) {
+            queryWrapper.like(StringUtils.isNotEmpty(dto.getProjectName()), BiddingManagement::getProjectName, dto.getProjectName())
+                        .like(StringUtils.isNotEmpty(dto.getTenderUnit()), BiddingManagement::getTenderUnit, dto.getTenderUnit())
+                        .eq(StringUtils.isNotEmpty(dto.getBidResult()), BiddingManagement::getBidResult, dto.getBidResult())
+                        .ge(StringUtils.isNotEmpty(dto.getBidDateStart()), BiddingManagement::getBidDate, dto.getBidDateStart())
+                        .le(StringUtils.isNotEmpty(dto.getBidDateEnd()), BiddingManagement::getBidDate, dto.getBidDateEnd());
+        }
+        return queryWrapper;
+    }
+}
diff --git a/src/main/java/com/ruoyi/sales/service/impl/SalesContractServiceImpl.java b/src/main/java/com/ruoyi/sales/service/impl/SalesContractServiceImpl.java
new file mode 100644
index 0000000..af6554d
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/service/impl/SalesContractServiceImpl.java
@@ -0,0 +1,183 @@
+package com.ruoyi.sales.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.sales.dto.SalesContractDTO;
+import com.ruoyi.sales.mapper.SalesContractMapper;
+import com.ruoyi.sales.mapper.SalesContractProductMapper;
+import com.ruoyi.sales.pojo.SalesContract;
+import com.ruoyi.sales.pojo.SalesContractProduct;
+import com.ruoyi.sales.service.ISalesContractService;
+import com.ruoyi.sales.vo.SalesContractVO;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 閿�鍞悎鍚� Service 瀹炵幇
+ */
+@Slf4j
+@Service
+public class SalesContractServiceImpl extends ServiceImpl<SalesContractMapper, SalesContract> implements ISalesContractService {
+
+    @Resource
+    private SalesContractProductMapper salesContractProductMapper;
+
+    @Override
+    public IPage<SalesContractVO> listPage(Page<SalesContract> page, SalesContractDTO dto) {
+        LambdaQueryWrapper<SalesContract> queryWrapper = buildQueryWrapper(dto);
+        IPage<SalesContract> iPage = this.page(page, queryWrapper);
+
+        IPage<SalesContractVO> voPage = iPage.convert(item -> {
+            return BeanUtil.copyProperties(item, SalesContractVO.class);
+        });
+
+        return voPage;
+    }
+
+    @Override
+    public AjaxResult getDetail(Long id) {
+        SalesContract contract = this.getById(id);
+        if (contract == null) {
+            return AjaxResult.error("鍚堝悓鏁版嵁涓嶅瓨鍦�");
+        }
+        SalesContractVO vo = BeanUtil.copyProperties(contract, SalesContractVO.class);
+
+        // 鑾峰彇鏄庣粏
+        LambdaQueryWrapper<SalesContractProduct> productQuery = new LambdaQueryWrapper<>();
+        productQuery.eq(SalesContractProduct::getContractId, id);
+        List<SalesContractProduct> productList = salesContractProductMapper.selectList(productQuery);
+        vo.setProductList(productList);
+
+        return AjaxResult.success(vo);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public AjaxResult addOrUpdate(SalesContractDTO dto) {
+        SalesContract contract = BeanUtil.copyProperties(dto, SalesContract.class);
+        boolean isUpdate = contract.getId() != null;
+
+        List<SalesContractProduct> productList = dto.getProductList();
+        if (productList == null) {
+            productList = new ArrayList<>();
+        }
+
+        // 閲嶆柊璁$畻鍚勪骇鍝佺殑浜у�硷紝骞舵眹鎬诲悎鍚屾�讳骇鍊�
+        BigDecimal totalOutputValue = BigDecimal.ZERO;
+        for (SalesContractProduct product : productList) {
+            BigDecimal price = product.getTaxInclusiveUnitPrice() != null ? product.getTaxInclusiveUnitPrice() : BigDecimal.ZERO;
+            BigDecimal shipped = product.getShippedQuantity() != null ? product.getShippedQuantity() : BigDecimal.ZERO;
+            BigDecimal productOutputValue = price.multiply(shipped);
+            product.setOutputValue(productOutputValue);
+
+            totalOutputValue = totalOutputValue.add(productOutputValue);
+        }
+        contract.setOutputValue(totalOutputValue);
+
+        if (isUpdate) {
+            this.updateById(contract);
+
+            // 宸噺鏇存柊閫昏緫
+            handleProductListUpdate(contract.getId(), productList);
+        } else {
+            this.save(contract);
+
+            // 鎵归噺鎻掑叆鏄庣粏
+            for (SalesContractProduct product : productList) {
+                product.setContractId(contract.getId());
+                salesContractProductMapper.insert(product);
+            }
+        }
+
+        return AjaxResult.success();
+    }
+
+    /**
+     * 澶勭悊浜у搧鏄庣粏鐨勫樊閲忔洿鏂�
+     */
+    private void handleProductListUpdate(Long contractId, List<SalesContractProduct> newList) {
+        // 鑾峰彇鏁版嵁搴撲腑鏃х殑鏄庣粏鍒楄〃
+        LambdaQueryWrapper<SalesContractProduct> productQuery = new LambdaQueryWrapper<>();
+        productQuery.eq(SalesContractProduct::getContractId, contractId);
+        List<SalesContractProduct> oldList = salesContractProductMapper.selectList(productQuery);
+
+        Map<Long, SalesContractProduct> oldMap = oldList.stream()
+                .collect(Collectors.toMap(SalesContractProduct::getId, p -> p));
+
+        // 閬嶅巻鏂板垪琛ㄨ繘琛屾彃鍏ユ垨鏇存柊
+        for (SalesContractProduct newProduct : newList) {
+            newProduct.setContractId(contractId);
+            if (newProduct.getId() != null && oldMap.containsKey(newProduct.getId())) {
+                // 鏇存柊
+                salesContractProductMapper.updateById(newProduct);
+                oldMap.remove(newProduct.getId()); // 浠� Map 涓Щ闄わ紝鍓╀笅鐨勫氨鏄渶瑕佸垹闄ょ殑
+            } else {
+                // 鏂板
+                newProduct.setId(null); // 闃插尽鎬у鐞�
+                salesContractProductMapper.insert(newProduct);
+            }
+        }
+
+        // 鍒犻櫎鍦ㄥ墠绔垪琛ㄤ腑涓嶅瓨鍦ㄧ殑鏃ц褰�
+        if (!oldMap.isEmpty()) {
+            salesContractProductMapper.deleteBatchIds(oldMap.keySet());
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public AjaxResult del(List<Long> ids) {
+        if (CollectionUtils.isEmpty(ids)) {
+            return AjaxResult.error("鍙傛暟涓嶈兘涓虹┖");
+        }
+
+        // 鍒犻櫎涓昏〃
+        this.removeByIds(ids);
+
+        // 绾ц仈鍒犻櫎瀛愯〃
+        LambdaQueryWrapper<SalesContractProduct> delQuery = new LambdaQueryWrapper<>();
+        delQuery.in(SalesContractProduct::getContractId, ids);
+        salesContractProductMapper.delete(delQuery);
+
+        return AjaxResult.success();
+    }
+
+    @Override
+    public List<SalesContract> selectList(SalesContractDTO dto) {
+        return this.list(buildQueryWrapper(dto));
+    }
+
+    /**
+     * 鏋勫缓鏌ヨ鏉′欢
+     */
+    private LambdaQueryWrapper<SalesContract> buildQueryWrapper(SalesContractDTO dto) {
+        LambdaQueryWrapper<SalesContract> queryWrapper = new LambdaQueryWrapper<>();
+        if (dto == null) {
+            return queryWrapper;
+        }
+        queryWrapper.like(StringUtils.isNotBlank(dto.getContractNo()), SalesContract::getContractNo, dto.getContractNo());
+        queryWrapper.like(StringUtils.isNotBlank(dto.getSigningUnit()), SalesContract::getSigningUnit, dto.getSigningUnit());
+        queryWrapper.eq(StringUtils.isNotBlank(dto.getArchiveStatus()), SalesContract::getArchiveStatus, dto.getArchiveStatus());
+        queryWrapper.ge(dto.getSignDateStart() != null, SalesContract::getSignDate, dto.getSignDateStart());
+        queryWrapper.le(dto.getSignDateEnd() != null, SalesContract::getSignDate, dto.getSignDateEnd());
+
+        queryWrapper.orderByDesc(SalesContract::getCreateTime);
+        return queryWrapper;
+    }
+}
diff --git a/src/main/java/com/ruoyi/sales/service/impl/SalesLawsuitServiceImpl.java b/src/main/java/com/ruoyi/sales/service/impl/SalesLawsuitServiceImpl.java
new file mode 100644
index 0000000..a1680bf
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/service/impl/SalesLawsuitServiceImpl.java
@@ -0,0 +1,145 @@
+package com.ruoyi.sales.service.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.basic.enums.ApplicationTypeEnum;
+import com.ruoyi.basic.enums.RecordTypeEnum;
+import com.ruoyi.basic.dto.StorageBlobDTO;
+import com.ruoyi.basic.dto.StorageBlobVO;
+import com.ruoyi.basic.utils.FileUtil;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.sales.dto.SalesLawsuitDTO;
+import com.ruoyi.sales.mapper.SalesLawsuitMapper;
+import com.ruoyi.sales.pojo.SalesLawsuit;
+import com.ruoyi.sales.service.ISalesLawsuitService;
+import com.ruoyi.sales.vo.SalesLawsuitVO;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 璇夎绠$悊 Service 瀹炵幇
+ */
+@Slf4j
+@Service
+public class SalesLawsuitServiceImpl extends ServiceImpl<SalesLawsuitMapper, SalesLawsuit> implements ISalesLawsuitService {
+
+    @Resource
+    private FileUtil fileUtil;
+
+    @Override
+    public IPage<SalesLawsuitVO> listPage(Page<SalesLawsuit> page, SalesLawsuitDTO dto) {
+        LambdaQueryWrapper<SalesLawsuit> queryWrapper = buildQueryWrapper(dto);
+        IPage<SalesLawsuit> iPage = this.page(page, queryWrapper);
+
+        IPage<SalesLawsuitVO> voPage = iPage.convert(item -> {
+            SalesLawsuitVO vo = BeanUtil.copyProperties(item, SalesLawsuitVO.class);
+            return vo;
+        });
+
+        return voPage;
+    }
+
+    @Override
+    public AjaxResult getDetail(Long id) {
+        SalesLawsuit lawsuit = this.getById(id);
+        if (lawsuit == null) {
+            return AjaxResult.error("鏁版嵁涓嶅瓨鍦�");
+        }
+        SalesLawsuitVO vo = BeanUtil.copyProperties(lawsuit, SalesLawsuitVO.class);
+
+        // 缁勮闄勪欢
+        List<StorageBlobVO> storageBlobVOs = fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(
+                ApplicationTypeEnum.FILE,
+                RecordTypeEnum.SALES_LAWSUIT,
+                id
+        );
+        vo.setStorageBlobVOs(storageBlobVOs != null ? storageBlobVOs : new ArrayList<>());
+
+        return AjaxResult.success(vo);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public AjaxResult addOrUpdate(SalesLawsuitDTO dto) {
+        SalesLawsuit lawsuit = BeanUtil.copyProperties(dto, SalesLawsuit.class);
+        boolean isUpdate = lawsuit.getId() != null;
+
+        if (isUpdate) {
+            this.updateById(lawsuit);
+            log.info("TODO: 鍚屾鏇存柊椤圭洰鏃堕棿杞翠腑瀵瑰簲鐨勮瘔璁间簨浠惰妭鐐� (projectId: {}, lawsuitId: {})", lawsuit.getProjectId(), lawsuit.getId());
+        } else {
+            this.save(lawsuit);
+            log.info("TODO: 鍦ㄩ」鐩椂闂磋酱涓拷鍔犱竴鏉¤瘔璁艰妭鐐� (projectId: {}, lawsuitId: {})", lawsuit.getProjectId(), lawsuit.getId());
+        }
+
+        // 澶勭悊闄勪欢
+        List<StorageBlobDTO> storageBlobDTOs = dto.getStorageBlobDTOs();
+        if (storageBlobDTOs == null) {
+            storageBlobDTOs = new ArrayList<>();
+        }
+        fileUtil.saveStorageAttachment(
+                ApplicationTypeEnum.FILE,
+                RecordTypeEnum.SALES_LAWSUIT,
+                lawsuit.getId(),
+                storageBlobDTOs
+        );
+
+        return AjaxResult.success();
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public AjaxResult del(List<Long> ids) {
+        if (CollectionUtils.isEmpty(ids)) {
+            return AjaxResult.error("鍙傛暟涓嶈兘涓虹┖");
+        }
+        List<SalesLawsuit> lawsuits = this.listByIds(ids);
+
+        this.removeByIds(ids);
+
+        for (SalesLawsuit lawsuit : lawsuits) {
+            log.info("TODO: 鍚屾绉婚櫎椤圭洰鏃堕棿杞翠腑瀵瑰簲鐨勮瘔璁间簨浠惰妭鐐� (projectId: {}, lawsuitId: {})", lawsuit.getProjectId(), lawsuit.getId());
+        }
+
+        return AjaxResult.success();
+    }
+
+    @Override
+    public List<SalesLawsuit> selectList(SalesLawsuitDTO dto) {
+        return this.list(buildQueryWrapper(dto));
+    }
+
+    /**
+     * 鏋勫缓鏌ヨ鏉′欢
+     */
+    private LambdaQueryWrapper<SalesLawsuit> buildQueryWrapper(SalesLawsuitDTO dto) {
+        LambdaQueryWrapper<SalesLawsuit> queryWrapper = new LambdaQueryWrapper<>();
+        if (dto == null) {
+            return queryWrapper;
+        }
+        queryWrapper.like(StringUtils.isNotBlank(dto.getCaseNo()), SalesLawsuit::getCaseNo, dto.getCaseNo());
+        queryWrapper.eq(dto.getProjectId() != null, SalesLawsuit::getProjectId, dto.getProjectId());
+        queryWrapper.like(StringUtils.isNotBlank(dto.getProjectName()), SalesLawsuit::getProjectName, dto.getProjectName());
+        queryWrapper.eq(dto.getContractId() != null, SalesLawsuit::getContractId, dto.getContractId());
+        queryWrapper.like(StringUtils.isNotBlank(dto.getContractNo()), SalesLawsuit::getContractNo, dto.getContractNo());
+        queryWrapper.eq(StringUtils.isNotBlank(dto.getLawsuitStatus()), SalesLawsuit::getLawsuitStatus, dto.getLawsuitStatus());
+        queryWrapper.like(StringUtils.isNotBlank(dto.getLawyer()), SalesLawsuit::getLawyer, dto.getLawyer());
+        queryWrapper.ge(dto.getFilingDateStart() != null, SalesLawsuit::getFilingDate, dto.getFilingDateStart());
+        queryWrapper.le(dto.getFilingDateEnd() != null, SalesLawsuit::getFilingDate, dto.getFilingDateEnd());
+
+        queryWrapper.orderByDesc(SalesLawsuit::getCreateTime);
+        return queryWrapper;
+    }
+}
diff --git a/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java b/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
index 00e913f..73a5b7f 100644
--- a/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
+++ b/src/main/java/com/ruoyi/sales/service/impl/SalesLedgerServiceImpl.java
@@ -1,9 +1,9 @@
 package com.ruoyi.sales.service.impl;
 
+import cn.hutool.core.collection.CollUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
-import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -75,6 +75,9 @@
 import java.util.concurrent.TimeUnit;
 import java.util.function.Function;
 import java.util.stream.Collectors;
+import com.ruoyi.sales.mapper.SalesLedgerContractRelMapper;
+import com.ruoyi.sales.pojo.SalesLedgerContractRel;
+import org.springframework.util.CollectionUtils;
 
 /**
  * 閿�鍞彴璐ervice涓氬姟灞傚鐞�
@@ -106,6 +109,7 @@
     private final FileUtil fileUtil;
     private final AccountInvoiceApplicationMapper accountInvoiceApplicationMapper;
     private final AccountSalesCollectionMapper accountSalesCollectionMapper;
+    private final SalesLedgerContractRelMapper salesLedgerContractRelMapper;
 
     @Autowired
     private SysDeptMapper sysDeptMapper;
@@ -159,6 +163,15 @@
         SalesLedger salesLedger = salesLedgerMapper.selectById(salesLedgerDto.getId());
         if (salesLedger == null) {
             throw new BaseException("鍙拌处涓嶅瓨鍦�");
+        }
+
+        // 鏌ヨ鍏宠仈鐨勫悎鍚�
+        List<SalesLedgerContractRel> rels = salesLedgerContractRelMapper.selectList(
+                new LambdaQueryWrapper<SalesLedgerContractRel>().eq(SalesLedgerContractRel::getSalesLedgerId, salesLedger.getId())
+        );
+        if (!CollectionUtils.isEmpty(rels)) {
+            List<Long> contractIds = rels.stream().map(SalesLedgerContractRel::getContractId).collect(Collectors.toList());
+            salesLedger.setAssociatedContractIds(contractIds);
         }
 
         // 2. 鏌ヨ瀛愯〃
@@ -530,7 +543,7 @@
         // 鍒犻櫎鍙戣揣鍙拌处璁板綍
         List<ShippingInfo> shippingInfos = shippingInfoMapper.selectList(new LambdaQueryWrapper<ShippingInfo>()
                 .in(ShippingInfo::getSalesLedgerId, idList));
-        if (CollectionUtils.isNotEmpty(shippingInfos)) {
+        if (CollUtil.isNotEmpty(shippingInfos)) {
             shippingInfoServiceImpl.delete(shippingInfos.stream().map(ShippingInfo::getId).collect(Collectors.toList()));
         }
         // 鍒犻櫎闄勪欢琛�
@@ -539,7 +552,7 @@
         // 鍒犻櫎鐢熶骇绠℃帶鏁版嵁
         //鏌ヨ鐢熶骇鎶ュ伐id
         ArrayList<Long> mainIdList = productionProductMainService.listMain(idList);
-        if (CollectionUtils.isNotEmpty(mainIdList)) {
+        if (CollUtil.isNotEmpty(mainIdList)) {
             mainIdList.stream().forEach(mainId -> {
                 productionProductMainService.removeProductMain(mainId);
             });
@@ -577,6 +590,18 @@
             salesLedgerMapper.updateById(salesLedger);
         }
 
+        // 澶勭悊鍏宠仈鍚堝悓鏁版嵁
+        salesLedgerContractRelMapper.delete(
+                new LambdaQueryWrapper<SalesLedgerContractRel>().eq(SalesLedgerContractRel::getSalesLedgerId, salesLedger.getId())
+        );
+        if (!CollectionUtils.isEmpty(salesLedgerDto.getAssociatedContractIds())) {
+            for (Long contractId : salesLedgerDto.getAssociatedContractIds()) {
+                SalesLedgerContractRel rel = new SalesLedgerContractRel();
+                rel.setSalesLedgerId(salesLedger.getId());
+                rel.setContractId(contractId);
+                salesLedgerContractRelMapper.insert(rel);
+            }
+        }
 
         // 4. 澶勭悊瀛愯〃鏁版嵁
         List<SalesLedgerProduct> productList = salesLedgerDto.getProductData();
diff --git a/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java b/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java
index be34bc0..19b7960 100644
--- a/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java
+++ b/src/main/java/com/ruoyi/sales/service/impl/ShippingInfoServiceImpl.java
@@ -8,6 +8,7 @@
 import com.ruoyi.approve.mapper.ApprovalTemplateMapper;
 import com.ruoyi.approve.pojo.ApprovalTemplate;
 import com.ruoyi.approve.pojo.ApproveProcess;
+import com.ruoyi.approve.pojo.ApprovalInstance;
 import com.ruoyi.approve.service.ApprovalInstanceService;
 import com.ruoyi.approve.service.impl.ApproveProcessServiceImpl;
 import com.ruoyi.basic.enums.ApplicationTypeEnum;
@@ -23,9 +24,11 @@
 import com.ruoyi.sales.dto.ShippingApproveDto;
 import com.ruoyi.sales.dto.ShippingInfoDto;
 import com.ruoyi.sales.dto.ShippingProductDetailDto;
+import com.ruoyi.sales.mapper.SalesLedgerMapper;
 import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
 import com.ruoyi.sales.mapper.ShippingInfoMapper;
 import com.ruoyi.sales.mapper.ShippingProductDetailMapper;
+import com.ruoyi.sales.pojo.SalesLedger;
 import com.ruoyi.sales.pojo.ShippingInfo;
 import com.ruoyi.sales.pojo.ShippingProductDetail;
 import com.ruoyi.sales.service.ShippingInfoService;
@@ -36,6 +39,7 @@
 import org.springframework.transaction.annotation.Transactional;
 
 import java.time.LocalDateTime;
+import java.util.stream.Collectors;
 import java.util.List;
 
 /**
@@ -52,6 +56,7 @@
 
 
     private final SalesLedgerProductMapper salesLedgerProductMapper;
+    private final SalesLedgerMapper salesLedgerMapper;
 
     private final StockUtils stockUtils;
 
@@ -68,6 +73,11 @@
         IPage<ShippingInfoDto> listPage = shippingInfoMapper.listPage(page, req);
         listPage.getRecords().forEach(item -> {
             item.setStorageBlobVOs(fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum.IMAGE, RecordTypeEnum.SHIPPING_INFO, item.getId()));
+            if (item.getReturnNos() != null && !item.getReturnNos().isEmpty()) {
+                item.setReturnNoList(java.util.Arrays.asList(item.getReturnNos().split(",")));
+            } else {
+                item.setReturnNoList(new java.util.ArrayList<>());
+            }
         });
         return listPage;
     }
@@ -123,6 +133,20 @@
                 stockUtils.deleteStockOutRecord(shippingInfo.getId(), StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode());
             }
         }
+        
+        // 鍒犻櫎宸ヤ綔娴佸疄渚�
+        List<ApprovalInstance> approvalInstances = approvalInstanceService.list(new LambdaQueryWrapper<ApprovalInstance>()
+                .in(ApprovalInstance::getBusinessId, ids)
+                .eq(ApprovalInstance::getBusinessType, 7L)
+        );
+        if(!approvalInstances.isEmpty()){
+            approvalInstanceService.delete(
+                    approvalInstances.stream()
+                            .map(ApprovalInstance::getId)
+                            .collect(Collectors.toList())
+            );
+        }
+
         //鍒犻櫎鍙戣揣鏄庣粏
         shippingProductDetailMapper.delete(new LambdaQueryWrapper<ShippingProductDetail>().in(ShippingProductDetail::getShippingInfoId, ids));
 
@@ -188,6 +212,14 @@
         // 鍏堜繚瀛樺彂璐у崟锛屽啀鍙戣捣瀹℃壒锛涙棤瀹℃牳浜鸿嚜鍔ㄩ�氳繃鏃堕渶瑕佹寜鍙戣揣缂栧彿鍥炲啓鍙戣揣鐘舵�併��
         req.setShippingNo(sh);
         req.setStatus("寰呭鏍�");
+        // Fetch projectId from SalesLedger
+        if (req.getSalesLedgerId() != null) {
+            SalesLedger salesLedger = salesLedgerMapper.selectById(req.getSalesLedgerId());
+            if (salesLedger != null && salesLedger.getProjectId() != null) {
+                req.setProjectId(salesLedger.getProjectId());
+            }
+        }
+        
         boolean save = this.add(req);
         // 鍙戣揣瀹℃壒
         ApprovalTemplate approvalTemplate = approvalTemplateMapper.selectOne(
diff --git a/src/main/java/com/ruoyi/sales/task/ContractReturnReminderTask.java b/src/main/java/com/ruoyi/sales/task/ContractReturnReminderTask.java
new file mode 100644
index 0000000..23e4af5
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/task/ContractReturnReminderTask.java
@@ -0,0 +1,89 @@
+package com.ruoyi.sales.task;
+
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.ruoyi.project.system.domain.SysNotice;
+import com.ruoyi.project.system.service.ISysNoticeService;
+import com.ruoyi.projectManagement.vo.InfoVo;
+import com.ruoyi.projectManagement.pojo.ProjectConfig;
+import com.ruoyi.projectManagement.service.InfoService;
+import com.ruoyi.projectManagement.service.ProjectConfigService;
+import com.ruoyi.sales.pojo.SalesLedger;
+import com.ruoyi.sales.service.ISalesLedgerService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.time.LocalDate;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 閿�鍞彴璐�-鍚堝悓鍘熶欢绾﹀畾鍥炰紶鏃ユ湡棰勮浠诲姟
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class ContractReturnReminderTask {
+
+    private final ProjectConfigService projectConfigService;
+    private final ISalesLedgerService salesLedgerService;
+    private final InfoService infoService;
+    private final ISysNoticeService sysNoticeService;
+
+    // 姣忓ぉ涓婂崍 8 鐐规墽琛�
+    @Scheduled(cron = "0 0 8 * * ?")
+    public void processReminders() {
+        log.info("寮�濮嬫墽琛岋細鍚堝悓鍘熶欢鍥炰紶棰勮鎵弿");
+        ProjectConfig config = projectConfigService.getGlobalConfig();
+        Integer reminderDays = config.getContractReturnReminderDays();
+        if (reminderDays == null) {
+            reminderDays = 3;
+        }
+
+        LocalDate targetDate = LocalDate.now().plusDays(reminderDays);
+
+        // 鏌ヨ绗﹀悎鏉′欢鐨勯攢鍞彴璐�
+        List<SalesLedger> ledgers = salesLedgerService.list(Wrappers.<SalesLedger>lambdaQuery()
+                .eq(SalesLedger::getContractOriginalReturnDate, targetDate));
+
+        if (ledgers == null || ledgers.isEmpty()) {
+            log.info("浠婃棩鏃犻渶瑕侀璀︾殑鍚堝悓鍥炰紶璁板綍");
+            return;
+        }
+
+        log.info("鍙戠幇 {} 鏉¢璀﹁褰�", ledgers.size());
+
+        for (SalesLedger ledger : ledgers) {
+            if (ledger.getProjectId() == null) {
+                continue;
+            }
+            InfoVo projectInfo = infoService.getInfoById(ledger.getProjectId());
+            if (projectInfo != null && projectInfo.getInfo() != null && projectInfo.getInfo().getManagerId() != null) {
+                sendNotice(ledger, projectInfo);
+            }
+        }
+        log.info("鍚堝悓鍘熶欢鍥炰紶棰勮鎵弿缁撴潫");
+    }
+
+    private void sendNotice(SalesLedger ledger, InfoVo projectInfo) {
+        SysNotice notice = new SysNotice();
+        notice.setNoticeTitle("鍚堝悓鍘熶欢鍥炰紶棰勮");
+        notice.setNoticeType("1"); // 1涓洪�氱煡
+        String content = String.format("椤圭洰銆�%s銆戝叧鑱旂殑閿�鍞彴璐︼紙%s锛夌殑鍚堝悓鍘熶欢绾﹀畾鍥炰紶鏃ユ湡锛�%s锛夊嵆灏嗗埌鏈燂紝璇峰強鏃跺鐞嗭紒",
+                projectInfo.getInfo().getTitle(),
+                ledger.getSalesContractNo(),
+                ledger.getContractOriginalReturnDate().toString());
+        notice.setNoticeContent(content);
+        notice.setStatus("0"); // 姝e父
+        notice.setConsigneeId(projectInfo.getInfo().getManagerId());
+        // 鍙互璁剧疆 senderId 涓虹郴缁熺鐞嗗憳 (渚嬪 1L)
+        notice.setSenderId(1L);
+
+        try {
+            sysNoticeService.save(notice);
+        } catch (Exception e) {
+            log.error("鍙戦�侀璀﹂�氱煡澶辫触: salesLedgerId={}, error={}", ledger.getId(), e.getMessage());
+        }
+    }
+}
diff --git a/src/main/java/com/ruoyi/sales/vo/CustomerTransactionsDetailsVo.java b/src/main/java/com/ruoyi/sales/vo/CustomerTransactionsDetailsVo.java
index cc205b1..17f3d05 100644
--- a/src/main/java/com/ruoyi/sales/vo/CustomerTransactionsDetailsVo.java
+++ b/src/main/java/com/ruoyi/sales/vo/CustomerTransactionsDetailsVo.java
@@ -33,4 +33,10 @@
     @Schema(description = "閫�璐ч噾棰�")
     private BigDecimal returnAmount;
 
+    @Schema(description = "寮�绁ㄩ噾棰�")
+    private BigDecimal invoiceAmount;
+
+    @Schema(description = "鏄惁鍥炴瓒呮椂")
+    private Boolean isPaymentTimeout;
+
 }
diff --git a/src/main/java/com/ruoyi/sales/vo/CustomerTransactionsVo.java b/src/main/java/com/ruoyi/sales/vo/CustomerTransactionsVo.java
index a6c7d7f..69cab1d 100644
--- a/src/main/java/com/ruoyi/sales/vo/CustomerTransactionsVo.java
+++ b/src/main/java/com/ruoyi/sales/vo/CustomerTransactionsVo.java
@@ -12,6 +12,9 @@
     @Schema(description = "瀹㈡埛ID")
     private Long customerId;
 
+    @Schema(description = "椤圭洰ID")
+    private Long projectId;
+
     @Schema(description = "瀹㈡埛鍚嶇О")
     private String customerName;
 
@@ -30,4 +33,13 @@
     @Schema(description = "閫�璐ч噾棰�")
     private BigDecimal returnAmount;
 
+    @Schema(description = "寮�绁ㄩ噾棰�")
+    private BigDecimal invoiceAmount;
+
+    @Schema(description = "鍚堝悓鎬绘暟")
+    private Integer contractCount;
+
+    @Schema(description = "瓒呮湡鍚堝悓鏁�")
+    private Integer overdueCount;
+
 }
diff --git a/src/main/java/com/ruoyi/sales/vo/ProjectTransactionsVo.java b/src/main/java/com/ruoyi/sales/vo/ProjectTransactionsVo.java
new file mode 100644
index 0000000..f506344
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/vo/ProjectTransactionsVo.java
@@ -0,0 +1,47 @@
+package com.ruoyi.sales.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+@Schema(name = "ProjectTransactionsVo", description = "钀ラ攢绠$悊--瀹㈡埛寰�鏉�-椤圭洰灞傜骇(杩斿洖)")
+public class ProjectTransactionsVo {
+
+    @Schema(description = "椤圭洰ID")
+    private Long projectId;
+
+    @Schema(description = "椤圭洰缂栧彿")
+    private String projectNo;
+
+    @Schema(description = "椤圭洰鍚嶇О")
+    private String projectName;
+
+    @Schema(description = "鍚堝悓鎬婚噾棰�")
+    private BigDecimal contractAmounts;
+
+    @Schema(description = "鏀舵閲戦")
+    private BigDecimal receiptPaymentAmount;
+
+    @Schema(description = "搴旀敹閲戦")
+    private BigDecimal receiptableAmount;
+
+    @Schema(description = "閫�璐ч噾棰�")
+    private BigDecimal returnAmount;
+
+    @Schema(description = "寮�绁ㄩ噾棰�")
+    private BigDecimal invoiceAmount;
+
+    @Schema(description = "鍏宠仈瀹㈡埛鏁�")
+    private Integer customerCount;
+
+    @Schema(description = "鍚堝悓鎬绘暟")
+    private Integer contractCount;
+
+    @Schema(description = "瓒呮湡鍚堝悓鏁�")
+    private Integer overdueCount;
+
+    @Schema(description = "椤圭洰鐘舵�� (0杩涜涓� 1宸插畬鎴愮瓑)")
+    private Integer status;
+}
diff --git a/src/main/java/com/ruoyi/sales/vo/SalesContractVO.java b/src/main/java/com/ruoyi/sales/vo/SalesContractVO.java
new file mode 100644
index 0000000..08d68dd
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/vo/SalesContractVO.java
@@ -0,0 +1,19 @@
+package com.ruoyi.sales.vo;
+
+import com.ruoyi.sales.pojo.SalesContract;
+import com.ruoyi.sales.pojo.SalesContractProduct;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class SalesContractVO extends SalesContract {
+
+    /**
+     * 浜у搧鏄庣粏鍒楄〃
+     */
+    private List<SalesContractProduct> productList;
+
+}
diff --git a/src/main/java/com/ruoyi/sales/vo/SalesLawsuitVO.java b/src/main/java/com/ruoyi/sales/vo/SalesLawsuitVO.java
new file mode 100644
index 0000000..4c35cfb
--- /dev/null
+++ b/src/main/java/com/ruoyi/sales/vo/SalesLawsuitVO.java
@@ -0,0 +1,19 @@
+package com.ruoyi.sales.vo;
+
+import com.ruoyi.basic.dto.StorageBlobVO;
+import com.ruoyi.sales.pojo.SalesLawsuit;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class SalesLawsuitVO extends SalesLawsuit {
+
+    /**
+     * 闄勪欢鍥炴樉鍒楄〃
+     */
+    private List<StorageBlobVO> storageBlobVOs;
+
+}
diff --git a/src/main/java/com/ruoyi/vehicle/controller/FuelCardController.java b/src/main/java/com/ruoyi/vehicle/controller/FuelCardController.java
new file mode 100644
index 0000000..67ec893
--- /dev/null
+++ b/src/main/java/com/ruoyi/vehicle/controller/FuelCardController.java
@@ -0,0 +1,52 @@
+package com.ruoyi.vehicle.controller;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.vehicle.pojo.FuelCard;
+import com.ruoyi.vehicle.service.FuelCardService;
+import com.ruoyi.vehicle.mapper.FuelCardMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import java.util.List;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+
+@RestController
+@RequestMapping("/vehicle/fuelCard")
+public class FuelCardController {
+    @Autowired
+    private FuelCardService fuelCardService;
+    @Autowired
+    private FuelCardMapper fuelCardMapper;
+
+    @GetMapping("/listPage")
+    public AjaxResult listPage(Page<FuelCard> page, FuelCard query) {
+        IPage<FuelCard> pageInfo = fuelCardMapper.selectFuelCardPage(page, query);
+        return AjaxResult.success(pageInfo);
+    }
+
+    @GetMapping("/listAll")
+    public AjaxResult listAll() {
+        LambdaQueryWrapper<FuelCard> wrapper = new LambdaQueryWrapper<>();
+        wrapper.orderByDesc(FuelCard::getId);
+        List<FuelCard> list = fuelCardService.list(wrapper);
+        return AjaxResult.success(list);
+    }
+
+    @PostMapping
+    public AjaxResult add(@RequestBody FuelCard entity) {
+        if (entity.getInitialBalance() != null) {
+            entity.setCurrentBalance(entity.getInitialBalance());
+        }
+        return AjaxResult.success(fuelCardService.save(entity));
+    }
+
+    @PutMapping
+    public AjaxResult edit(@RequestBody FuelCard entity) {
+        return AjaxResult.success(fuelCardService.updateById(entity));
+    }
+
+    @DeleteMapping("/{id}")
+    public AjaxResult remove(@PathVariable Long id) {
+        return AjaxResult.success(fuelCardService.removeById(id));
+    }
+}
diff --git a/src/main/java/com/ruoyi/vehicle/controller/FuelCardRechargeController.java b/src/main/java/com/ruoyi/vehicle/controller/FuelCardRechargeController.java
new file mode 100644
index 0000000..a892bc9
--- /dev/null
+++ b/src/main/java/com/ruoyi/vehicle/controller/FuelCardRechargeController.java
@@ -0,0 +1,38 @@
+package com.ruoyi.vehicle.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.vehicle.pojo.FuelCardRecharge;
+import com.ruoyi.vehicle.service.IFuelCardRechargeService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/vehicle/fuelCardRecharge")
+public class FuelCardRechargeController {
+
+    @Autowired
+    private IFuelCardRechargeService fuelCardRechargeService;
+
+    @GetMapping("/list")
+    public AjaxResult list(Page<FuelCardRecharge> page, @RequestParam("cardId") Long cardId) {
+        IPage<FuelCardRecharge> pageList = fuelCardRechargeService.selectRechargePage(page, cardId);
+        return AjaxResult.success(pageList);
+    }
+
+    @PostMapping("/add")
+    public AjaxResult add(@RequestBody FuelCardRecharge recharge) {
+        if (recharge.getCardId() == null || recharge.getRechargeAmount() == null || recharge.getRechargeDate() == null) {
+            return AjaxResult.error("鍙傛暟涓嶅畬鏁�");
+        }
+        fuelCardRechargeService.addRecharge(recharge);
+        return AjaxResult.success();
+    }
+
+    @DeleteMapping("/{id}")
+    public AjaxResult remove(@PathVariable Long id) {
+        fuelCardRechargeService.deleteRecharge(id);
+        return AjaxResult.success();
+    }
+}
diff --git a/src/main/java/com/ruoyi/vehicle/controller/FuelRecordController.java b/src/main/java/com/ruoyi/vehicle/controller/FuelRecordController.java
new file mode 100644
index 0000000..4ea0004
--- /dev/null
+++ b/src/main/java/com/ruoyi/vehicle/controller/FuelRecordController.java
@@ -0,0 +1,59 @@
+package com.ruoyi.vehicle.controller;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.vehicle.pojo.FuelRecord;
+import com.ruoyi.vehicle.service.FuelRecordService;
+import com.ruoyi.vehicle.mapper.FuelRecordMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/vehicle/fuelRecord")
+public class FuelRecordController {
+    @Autowired
+    private FuelRecordService fuelRecordService;
+    @Autowired
+    private FuelRecordMapper fuelRecordMapper;
+
+    @GetMapping("/listPage")
+    public AjaxResult listPage(Page<FuelRecord> page, FuelRecord query, String beginDate, String endDate) {
+        IPage<FuelRecord> pageInfo = fuelRecordMapper.selectFuelRecordPage(page, query, beginDate, endDate);
+        return AjaxResult.success(pageInfo);
+    }
+
+    @PostMapping
+    public AjaxResult add(@RequestBody FuelRecord entity) {
+        return AjaxResult.success(fuelRecordService.saveRecord(entity));
+    }
+
+    @PutMapping
+    public AjaxResult edit(@RequestBody FuelRecord entity) {
+        return AjaxResult.success(fuelRecordService.updateRecord(entity));
+    }
+
+    @DeleteMapping("/{id}")
+    public AjaxResult remove(@PathVariable Long id) {
+        return AjaxResult.success(fuelRecordService.removeRecordById(id));
+    }
+    @PostMapping("/submit/{id}")
+    public AjaxResult submit(@PathVariable Long id) {
+        try {
+            fuelRecordService.submitRecord(id);
+            return AjaxResult.success();
+        } catch (Exception e) {
+            return AjaxResult.error(e.getMessage());
+        }
+    }
+
+    @PostMapping("/revoke/{id}")
+    public AjaxResult revoke(@PathVariable Long id) {
+        try {
+            fuelRecordService.revokeRecord(id);
+            return AjaxResult.success();
+        } catch (Exception e) {
+            return AjaxResult.error(e.getMessage());
+        }
+    }
+}
+
diff --git a/src/main/java/com/ruoyi/vehicle/controller/VehicleCostController.java b/src/main/java/com/ruoyi/vehicle/controller/VehicleCostController.java
new file mode 100644
index 0000000..4a71b7e
--- /dev/null
+++ b/src/main/java/com/ruoyi/vehicle/controller/VehicleCostController.java
@@ -0,0 +1,61 @@
+package com.ruoyi.vehicle.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.vehicle.pojo.VehicleCost;
+import com.ruoyi.vehicle.service.VehicleCostService;
+import com.ruoyi.vehicle.mapper.VehicleCostMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.util.StringUtils;
+
+@RestController
+@RequestMapping("/vehicle/cost")
+public class VehicleCostController {
+    @Autowired
+    private VehicleCostService vehicleCostService;
+
+    @Autowired
+    private VehicleCostMapper vehicleCostMapper;
+
+    @GetMapping("/listPage")
+    public AjaxResult listPage(Page<VehicleCost> page, VehicleCost query) {
+        LambdaQueryWrapper<VehicleCost> wrapper = new LambdaQueryWrapper<>();
+        if (query.getVehicleId() != null) {
+            wrapper.eq(VehicleCost::getVehicleId, query.getVehicleId());
+        }
+        if (StringUtils.hasText(query.getCostType())) {
+            wrapper.eq(VehicleCost::getCostType, query.getCostType());
+        }
+        wrapper.orderByDesc(VehicleCost::getCostDate);
+        IPage<VehicleCost> pageInfo = vehicleCostService.page(page, wrapper);
+        return AjaxResult.success(pageInfo);
+    }
+
+    @GetMapping("/summaryByMonth")
+    public AjaxResult summaryByMonth() {
+        return AjaxResult.success(vehicleCostMapper.summaryByMonth());
+    }
+
+    @GetMapping("/summaryByVehicle")
+    public AjaxResult summaryByVehicle() {
+        return AjaxResult.success(vehicleCostMapper.summaryByVehicle());
+    }
+
+    @PostMapping
+    public AjaxResult add(@RequestBody VehicleCost entity) {
+        return AjaxResult.success(vehicleCostService.save(entity));
+    }
+
+    @PutMapping
+    public AjaxResult edit(@RequestBody VehicleCost entity) {
+        return AjaxResult.success(vehicleCostService.updateById(entity));
+    }
+
+    @DeleteMapping("/{id}")
+    public AjaxResult remove(@PathVariable Long id) {
+        return AjaxResult.success(vehicleCostService.removeById(id));
+    }
+}
diff --git a/src/main/java/com/ruoyi/vehicle/controller/VehicleInfoController.java b/src/main/java/com/ruoyi/vehicle/controller/VehicleInfoController.java
new file mode 100644
index 0000000..9e36589
--- /dev/null
+++ b/src/main/java/com/ruoyi/vehicle/controller/VehicleInfoController.java
@@ -0,0 +1,51 @@
+package com.ruoyi.vehicle.controller;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.vehicle.pojo.VehicleInfo;
+import com.ruoyi.vehicle.service.VehicleInfoService;
+import com.ruoyi.vehicle.mapper.VehicleInfoMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+import java.time.LocalDate;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import java.util.List;
+
+@RestController
+@RequestMapping("/vehicle/info")
+public class VehicleInfoController {
+    @Autowired
+    private VehicleInfoService vehicleInfoService;
+    @Autowired
+    private VehicleInfoMapper vehicleInfoMapper;
+
+    @GetMapping("/listPage")
+    public AjaxResult listPage(Page<VehicleInfo> page, VehicleInfo query) {
+        IPage<VehicleInfo> pageInfo = vehicleInfoMapper.selectVehiclePage(page, query);
+        return AjaxResult.success(pageInfo);
+    }
+
+    @GetMapping("/warningList")
+    public AjaxResult warningList() {
+        LocalDate nextMonth = LocalDate.now().plusDays(30);
+        LambdaQueryWrapper<VehicleInfo> wrapper = new LambdaQueryWrapper<>();
+        wrapper.le(VehicleInfo::getInspectionExpireDate, nextMonth).or().le(VehicleInfo::getInsuranceExpireDate, nextMonth);
+        List<VehicleInfo> list = vehicleInfoService.list(wrapper);
+        return AjaxResult.success(list);
+    }
+
+    @PostMapping
+    public AjaxResult add(@RequestBody VehicleInfo entity) {
+        return AjaxResult.success(vehicleInfoService.save(entity));
+    }
+
+    @PutMapping
+    public AjaxResult edit(@RequestBody VehicleInfo entity) {
+        return AjaxResult.success(vehicleInfoService.updateById(entity));
+    }
+
+    @DeleteMapping("/{id}")
+    public AjaxResult remove(@PathVariable Long id) {
+        return AjaxResult.success(vehicleInfoService.removeById(id));
+    }
+}
diff --git a/src/main/java/com/ruoyi/vehicle/mapper/FuelCardMapper.java b/src/main/java/com/ruoyi/vehicle/mapper/FuelCardMapper.java
new file mode 100644
index 0000000..2a55921
--- /dev/null
+++ b/src/main/java/com/ruoyi/vehicle/mapper/FuelCardMapper.java
@@ -0,0 +1,11 @@
+package com.ruoyi.vehicle.mapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.vehicle.pojo.FuelCard;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+@Mapper
+public interface FuelCardMapper extends BaseMapper<FuelCard> {
+    IPage<FuelCard> selectFuelCardPage(Page<FuelCard> page, @Param("query") FuelCard query);
+}
diff --git a/src/main/java/com/ruoyi/vehicle/mapper/FuelCardRechargeMapper.java b/src/main/java/com/ruoyi/vehicle/mapper/FuelCardRechargeMapper.java
new file mode 100644
index 0000000..c57e61c
--- /dev/null
+++ b/src/main/java/com/ruoyi/vehicle/mapper/FuelCardRechargeMapper.java
@@ -0,0 +1,13 @@
+package com.ruoyi.vehicle.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.vehicle.pojo.FuelCardRecharge;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+@Mapper
+public interface FuelCardRechargeMapper extends BaseMapper<FuelCardRecharge> {
+    IPage<FuelCardRecharge> selectRechargePage(Page<FuelCardRecharge> page, @Param("cardId") Long cardId);
+}
diff --git a/src/main/java/com/ruoyi/vehicle/mapper/FuelRecordMapper.java b/src/main/java/com/ruoyi/vehicle/mapper/FuelRecordMapper.java
new file mode 100644
index 0000000..86a0113
--- /dev/null
+++ b/src/main/java/com/ruoyi/vehicle/mapper/FuelRecordMapper.java
@@ -0,0 +1,11 @@
+package com.ruoyi.vehicle.mapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.vehicle.pojo.FuelRecord;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+@Mapper
+public interface FuelRecordMapper extends BaseMapper<FuelRecord> {
+    IPage<FuelRecord> selectFuelRecordPage(Page<FuelRecord> page, @Param("query") FuelRecord query, @Param("beginDate") String beginDate, @Param("endDate") String endDate);
+}
diff --git a/src/main/java/com/ruoyi/vehicle/mapper/VehicleCostMapper.java b/src/main/java/com/ruoyi/vehicle/mapper/VehicleCostMapper.java
new file mode 100644
index 0000000..a44a5dc
--- /dev/null
+++ b/src/main/java/com/ruoyi/vehicle/mapper/VehicleCostMapper.java
@@ -0,0 +1,21 @@
+package com.ruoyi.vehicle.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.vehicle.pojo.VehicleCost;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Select;
+import java.util.List;
+import java.util.Map;
+
+@Mapper
+public interface VehicleCostMapper extends BaseMapper<VehicleCost> {
+    
+    @Select("SELECT DATE_FORMAT(cost_date, '%Y-%m') as month, cost_type as costType, sum(amount) as totalAmount " +
+            "FROM p_vehicle_cost WHERE del_flag = '0' GROUP BY DATE_FORMAT(cost_date, '%Y-%m'), cost_type")
+    List<Map<String, Object>> summaryByMonth();
+
+    @Select("SELECT v.plate_number as plateNumber, c.cost_type as costType, sum(c.amount) as totalAmount " +
+            "FROM p_vehicle_cost c LEFT JOIN p_vehicle_info v ON c.vehicle_id = v.id " +
+            "WHERE c.del_flag = '0' GROUP BY v.plate_number, c.cost_type")
+    List<Map<String, Object>> summaryByVehicle();
+}
diff --git a/src/main/java/com/ruoyi/vehicle/mapper/VehicleInfoMapper.java b/src/main/java/com/ruoyi/vehicle/mapper/VehicleInfoMapper.java
new file mode 100644
index 0000000..a62d4ee
--- /dev/null
+++ b/src/main/java/com/ruoyi/vehicle/mapper/VehicleInfoMapper.java
@@ -0,0 +1,11 @@
+package com.ruoyi.vehicle.mapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.vehicle.pojo.VehicleInfo;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+@Mapper
+public interface VehicleInfoMapper extends BaseMapper<VehicleInfo> {
+    IPage<VehicleInfo> selectVehiclePage(Page<VehicleInfo> page, @Param("query") VehicleInfo query);
+}
diff --git a/src/main/java/com/ruoyi/vehicle/pojo/FuelCard.java b/src/main/java/com/ruoyi/vehicle/pojo/FuelCard.java
new file mode 100644
index 0000000..7c038a5
--- /dev/null
+++ b/src/main/java/com/ruoyi/vehicle/pojo/FuelCard.java
@@ -0,0 +1,36 @@
+package com.ruoyi.vehicle.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("p_fuel_card")
+public class FuelCard {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    private String cardNumber;
+    private Long deptId;
+    private String issuer;
+    private BigDecimal initialBalance;
+    private BigDecimal currentBalance;
+    private String status;
+    private String remark;
+
+    @TableField(exist = false)
+    private String deptName;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Long createUser;
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Long updateUser;
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+    @TableField(fill = FieldFill.INSERT)
+    private String tenantId;
+    @TableLogic
+    private String delFlag;
+}
diff --git a/src/main/java/com/ruoyi/vehicle/pojo/FuelCardRecharge.java b/src/main/java/com/ruoyi/vehicle/pojo/FuelCardRecharge.java
new file mode 100644
index 0000000..c555080
--- /dev/null
+++ b/src/main/java/com/ruoyi/vehicle/pojo/FuelCardRecharge.java
@@ -0,0 +1,53 @@
+package com.ruoyi.vehicle.pojo;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+@TableName("p_fuel_card_recharge")
+public class FuelCardRecharge {
+    
+    @TableId
+    private Long id;
+
+    private Long cardId;
+
+    private BigDecimal rechargeAmount;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date rechargeDate;
+
+    private String remark;
+
+    private String delFlag;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Long createUser;
+
+    @TableField(fill = FieldFill.INSERT)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Long updateUser;
+
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date updateTime;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Long tenantId;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Long deptId;
+    
+    @TableField(exist = false)
+    private String createUserName;
+}
diff --git a/src/main/java/com/ruoyi/vehicle/pojo/FuelRecord.java b/src/main/java/com/ruoyi/vehicle/pojo/FuelRecord.java
new file mode 100644
index 0000000..ee7d07e
--- /dev/null
+++ b/src/main/java/com/ruoyi/vehicle/pojo/FuelRecord.java
@@ -0,0 +1,47 @@
+package com.ruoyi.vehicle.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("p_fuel_record")
+public class FuelRecord {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    private LocalDate fuelDate;
+    private BigDecimal amount;
+    private BigDecimal liters;
+    private Long vehicleId;
+    private Long fuelCardId;
+    private Long deptId;
+    private BigDecimal startMileage;
+    private BigDecimal endMileage;
+    private String invoiceNo;
+    private String remark;
+
+    @TableField(exist = false)
+    private String deptName;
+    @TableField(exist = false)
+    private String plateNumber;
+    @TableField(exist = false)
+    private String cardNumber;
+
+    
+    private String status;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Long createUser;
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Long updateUser;
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+    @TableField(fill = FieldFill.INSERT)
+    private String tenantId;
+    @TableLogic
+    private String delFlag;
+}
diff --git a/src/main/java/com/ruoyi/vehicle/pojo/VehicleCost.java b/src/main/java/com/ruoyi/vehicle/pojo/VehicleCost.java
new file mode 100644
index 0000000..2df0353
--- /dev/null
+++ b/src/main/java/com/ruoyi/vehicle/pojo/VehicleCost.java
@@ -0,0 +1,32 @@
+package com.ruoyi.vehicle.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("p_vehicle_cost")
+public class VehicleCost {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    private Long vehicleId;
+    private String costType;
+    private BigDecimal amount;
+    private LocalDate costDate;
+    private String remark;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Long createUser;
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Long updateUser;
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+    @TableField(fill = FieldFill.INSERT)
+    private String tenantId;
+    @TableLogic
+    private String delFlag;
+}
diff --git a/src/main/java/com/ruoyi/vehicle/pojo/VehicleInfo.java b/src/main/java/com/ruoyi/vehicle/pojo/VehicleInfo.java
new file mode 100644
index 0000000..90da144
--- /dev/null
+++ b/src/main/java/com/ruoyi/vehicle/pojo/VehicleInfo.java
@@ -0,0 +1,39 @@
+package com.ruoyi.vehicle.pojo;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@Data
+@TableName("p_vehicle_info")
+public class VehicleInfo {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    private String plateNumber;
+    private String propertyOwnership;
+    private Long deptId;
+    private Long userId;
+    private String vehicleStatus;
+    private LocalDate inspectionExpireDate;
+    private LocalDate insuranceExpireDate;
+    private String remark;
+
+    @TableField(exist = false)
+    private String deptName;
+    @TableField(exist = false)
+    private String userName;
+
+    @TableField(fill = FieldFill.INSERT)
+    private Long createUser;
+    @TableField(fill = FieldFill.INSERT)
+    private LocalDateTime createTime;
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private Long updateUser;
+    @TableField(fill = FieldFill.INSERT_UPDATE)
+    private LocalDateTime updateTime;
+    @TableField(fill = FieldFill.INSERT)
+    private String tenantId;
+    @TableLogic
+    private String delFlag;
+}
diff --git a/src/main/java/com/ruoyi/vehicle/service/FuelCardService.java b/src/main/java/com/ruoyi/vehicle/service/FuelCardService.java
new file mode 100644
index 0000000..92d13fc
--- /dev/null
+++ b/src/main/java/com/ruoyi/vehicle/service/FuelCardService.java
@@ -0,0 +1,7 @@
+package com.ruoyi.vehicle.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.vehicle.pojo.FuelCard;
+
+public interface FuelCardService extends IService<FuelCard> {
+}
diff --git a/src/main/java/com/ruoyi/vehicle/service/FuelRecordService.java b/src/main/java/com/ruoyi/vehicle/service/FuelRecordService.java
new file mode 100644
index 0000000..041c5e5
--- /dev/null
+++ b/src/main/java/com/ruoyi/vehicle/service/FuelRecordService.java
@@ -0,0 +1,16 @@
+package com.ruoyi.vehicle.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.vehicle.pojo.FuelRecord;
+
+public interface FuelRecordService extends IService<FuelRecord> {
+    boolean saveRecord(FuelRecord record);
+    boolean updateRecord(FuelRecord record);
+    boolean removeRecordById(Long id);
+    
+    // 鎻愪氦璁板綍骞舵墸娆�
+    boolean submitRecord(Long id);
+    
+    // 鎾ゅ洖璁板綍骞堕��娆�
+    boolean revokeRecord(Long id);
+}
diff --git a/src/main/java/com/ruoyi/vehicle/service/IFuelCardRechargeService.java b/src/main/java/com/ruoyi/vehicle/service/IFuelCardRechargeService.java
new file mode 100644
index 0000000..c57b775
--- /dev/null
+++ b/src/main/java/com/ruoyi/vehicle/service/IFuelCardRechargeService.java
@@ -0,0 +1,12 @@
+package com.ruoyi.vehicle.service;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.vehicle.pojo.FuelCardRecharge;
+
+public interface IFuelCardRechargeService extends IService<FuelCardRecharge> {
+    IPage<FuelCardRecharge> selectRechargePage(Page<FuelCardRecharge> page, Long cardId);
+    void addRecharge(FuelCardRecharge recharge);
+    void deleteRecharge(Long id);
+}
diff --git a/src/main/java/com/ruoyi/vehicle/service/VehicleCostService.java b/src/main/java/com/ruoyi/vehicle/service/VehicleCostService.java
new file mode 100644
index 0000000..6d8b7e8
--- /dev/null
+++ b/src/main/java/com/ruoyi/vehicle/service/VehicleCostService.java
@@ -0,0 +1,7 @@
+package com.ruoyi.vehicle.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.vehicle.pojo.VehicleCost;
+
+public interface VehicleCostService extends IService<VehicleCost> {
+}
diff --git a/src/main/java/com/ruoyi/vehicle/service/VehicleInfoService.java b/src/main/java/com/ruoyi/vehicle/service/VehicleInfoService.java
new file mode 100644
index 0000000..82795c7
--- /dev/null
+++ b/src/main/java/com/ruoyi/vehicle/service/VehicleInfoService.java
@@ -0,0 +1,7 @@
+package com.ruoyi.vehicle.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ruoyi.vehicle.pojo.VehicleInfo;
+
+public interface VehicleInfoService extends IService<VehicleInfo> {
+}
diff --git a/src/main/java/com/ruoyi/vehicle/service/impl/FuelCardRechargeServiceImpl.java b/src/main/java/com/ruoyi/vehicle/service/impl/FuelCardRechargeServiceImpl.java
new file mode 100644
index 0000000..bac0ce7
--- /dev/null
+++ b/src/main/java/com/ruoyi/vehicle/service/impl/FuelCardRechargeServiceImpl.java
@@ -0,0 +1,58 @@
+package com.ruoyi.vehicle.service.impl;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.vehicle.mapper.FuelCardMapper;
+import com.ruoyi.vehicle.mapper.FuelCardRechargeMapper;
+import com.ruoyi.vehicle.pojo.FuelCard;
+import com.ruoyi.vehicle.pojo.FuelCardRecharge;
+import com.ruoyi.vehicle.service.IFuelCardRechargeService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+public class FuelCardRechargeServiceImpl extends ServiceImpl<FuelCardRechargeMapper, FuelCardRecharge> implements IFuelCardRechargeService {
+
+    @Autowired
+    private FuelCardMapper fuelCardMapper;
+
+    @Override
+    public IPage<FuelCardRecharge> selectRechargePage(Page<FuelCardRecharge> page, Long cardId) {
+        return baseMapper.selectRechargePage(page, cardId);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void addRecharge(FuelCardRecharge recharge) {
+        // 淇濆瓨鍏呭�艰褰�
+        recharge.setDelFlag("0");
+        this.save(recharge);
+        
+        // 鏇存柊鍔犳补鍗′綑棰�
+        FuelCard card = fuelCardMapper.selectById(recharge.getCardId());
+        if (card != null && card.getCurrentBalance() != null) {
+            card.setCurrentBalance(card.getCurrentBalance().add(recharge.getRechargeAmount()));
+            fuelCardMapper.updateById(card);
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void deleteRecharge(Long id) {
+        FuelCardRecharge recharge = this.getById(id);
+        if (recharge != null && "0".equals(recharge.getDelFlag())) {
+            // 閫昏緫鍒犻櫎
+            recharge.setDelFlag("2");
+            this.updateById(recharge);
+            
+            // 鎵e洖浣欓
+            FuelCard card = fuelCardMapper.selectById(recharge.getCardId());
+            if (card != null && card.getCurrentBalance() != null) {
+                card.setCurrentBalance(card.getCurrentBalance().subtract(recharge.getRechargeAmount()));
+                fuelCardMapper.updateById(card);
+            }
+        }
+    }
+}
diff --git a/src/main/java/com/ruoyi/vehicle/service/impl/FuelCardServiceImpl.java b/src/main/java/com/ruoyi/vehicle/service/impl/FuelCardServiceImpl.java
new file mode 100644
index 0000000..ff9c5c4
--- /dev/null
+++ b/src/main/java/com/ruoyi/vehicle/service/impl/FuelCardServiceImpl.java
@@ -0,0 +1,11 @@
+package com.ruoyi.vehicle.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.vehicle.mapper.FuelCardMapper;
+import com.ruoyi.vehicle.pojo.FuelCard;
+import com.ruoyi.vehicle.service.FuelCardService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class FuelCardServiceImpl extends ServiceImpl<FuelCardMapper, FuelCard> implements FuelCardService {
+}
diff --git a/src/main/java/com/ruoyi/vehicle/service/impl/FuelRecordServiceImpl.java b/src/main/java/com/ruoyi/vehicle/service/impl/FuelRecordServiceImpl.java
new file mode 100644
index 0000000..d3b1cf8
--- /dev/null
+++ b/src/main/java/com/ruoyi/vehicle/service/impl/FuelRecordServiceImpl.java
@@ -0,0 +1,108 @@
+package com.ruoyi.vehicle.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.vehicle.mapper.FuelRecordMapper;
+import com.ruoyi.vehicle.pojo.FuelCard;
+import com.ruoyi.vehicle.pojo.FuelRecord;
+import com.ruoyi.vehicle.pojo.VehicleInfo;
+import com.ruoyi.vehicle.service.FuelCardService;
+import com.ruoyi.vehicle.service.FuelRecordService;
+import com.ruoyi.vehicle.service.VehicleInfoService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+public class FuelRecordServiceImpl extends ServiceImpl<FuelRecordMapper, FuelRecord> implements FuelRecordService {
+
+    @Autowired
+    private FuelCardService fuelCardService;
+    
+    @Autowired
+    private VehicleInfoService vehicleInfoService;
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean saveRecord(FuelRecord record) {
+        // 濡傛灉娌℃湁浼犲叆閮ㄩ棬锛屽皾璇曚粠鍔犳补鍗℃垨杞﹁締缁ф壙
+        if (record.getDeptId() == null) {
+            if (record.getFuelCardId() != null) {
+                FuelCard card = fuelCardService.getById(record.getFuelCardId());
+                if (card != null && card.getDeptId() != null) {
+                    record.setDeptId(card.getDeptId());
+                }
+            } else if (record.getVehicleId() != null) {
+                VehicleInfo vehicle = vehicleInfoService.getById(record.getVehicleId());
+                if (vehicle != null && vehicle.getDeptId() != null) {
+                    record.setDeptId(vehicle.getDeptId());
+                }
+            }
+        }
+        record.setStatus("0"); // 榛樿鑽夌
+        return this.save(record);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean updateRecord(FuelRecord record) {
+        FuelRecord oldRecord = this.getById(record.getId());
+        if (oldRecord != null && "1".equals(oldRecord.getStatus())) {
+            throw new RuntimeException("宸叉彁浜ょ殑璁板綍鏃犳硶淇敼");
+        }
+        return this.updateById(record);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean removeRecordById(Long id) {
+        FuelRecord oldRecord = this.getById(id);
+        if (oldRecord != null && "1".equals(oldRecord.getStatus())) {
+            throw new RuntimeException("宸叉彁浜ょ殑璁板綍鏃犳硶鍒犻櫎");
+        }
+        return this.removeById(id);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean submitRecord(Long id) {
+        FuelRecord record = this.getById(id);
+        if (record == null) return false;
+        if ("1".equals(record.getStatus())) {
+            throw new RuntimeException("璇ヨ褰曞凡缁忔彁浜�");
+        }
+        
+        record.setStatus("1");
+        boolean updated = this.updateById(record);
+        
+        if (updated && record.getFuelCardId() != null && record.getAmount() != null) {
+            FuelCard card = fuelCardService.getById(record.getFuelCardId());
+            if (card != null) {
+                card.setCurrentBalance(card.getCurrentBalance().subtract(record.getAmount()));
+                fuelCardService.updateById(card);
+            }
+        }
+        return updated;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean revokeRecord(Long id) {
+        FuelRecord record = this.getById(id);
+        if (record == null) return false;
+        if ("0".equals(record.getStatus())) {
+            throw new RuntimeException("鑽夌鐘舵�佹棤闇�鎾ゅ洖");
+        }
+        
+        record.setStatus("0");
+        boolean updated = this.updateById(record);
+        
+        if (updated && record.getFuelCardId() != null && record.getAmount() != null) {
+            FuelCard card = fuelCardService.getById(record.getFuelCardId());
+            if (card != null) {
+                card.setCurrentBalance(card.getCurrentBalance().add(record.getAmount()));
+                fuelCardService.updateById(card);
+            }
+        }
+        return updated;
+    }
+}
diff --git a/src/main/java/com/ruoyi/vehicle/service/impl/VehicleCostServiceImpl.java b/src/main/java/com/ruoyi/vehicle/service/impl/VehicleCostServiceImpl.java
new file mode 100644
index 0000000..0fee9d7
--- /dev/null
+++ b/src/main/java/com/ruoyi/vehicle/service/impl/VehicleCostServiceImpl.java
@@ -0,0 +1,11 @@
+package com.ruoyi.vehicle.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.vehicle.mapper.VehicleCostMapper;
+import com.ruoyi.vehicle.pojo.VehicleCost;
+import com.ruoyi.vehicle.service.VehicleCostService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class VehicleCostServiceImpl extends ServiceImpl<VehicleCostMapper, VehicleCost> implements VehicleCostService {
+}
diff --git a/src/main/java/com/ruoyi/vehicle/service/impl/VehicleInfoServiceImpl.java b/src/main/java/com/ruoyi/vehicle/service/impl/VehicleInfoServiceImpl.java
new file mode 100644
index 0000000..0165473
--- /dev/null
+++ b/src/main/java/com/ruoyi/vehicle/service/impl/VehicleInfoServiceImpl.java
@@ -0,0 +1,11 @@
+package com.ruoyi.vehicle.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.vehicle.mapper.VehicleInfoMapper;
+import com.ruoyi.vehicle.pojo.VehicleInfo;
+import com.ruoyi.vehicle.service.VehicleInfoService;
+import org.springframework.stereotype.Service;
+
+@Service
+public class VehicleInfoServiceImpl extends ServiceImpl<VehicleInfoMapper, VehicleInfo> implements VehicleInfoService {
+}
diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml
new file mode 100644
index 0000000..98d2de0
--- /dev/null
+++ b/src/main/resources/application-local.yml
@@ -0,0 +1,268 @@
+# 椤圭洰鐩稿叧閰嶇疆
+ruoyi:
+  # 鍚嶇О
+  name: RuoYi
+  # 鐗堟湰
+  version: 3.8.9
+  # 鐗堟潈骞翠唤
+  copyrightYear: 2025
+  # 鏂囦欢璺緞 绀轰緥锛� Windows閰嶇疆D:/ruoyi/uploadPath锛孡inux閰嶇疆 /home/ruoyi/uploadPath锛�
+  profile: /Users/mac/txkj/product-inventory/file
+
+  # 鑾峰彇ip鍦板潃寮�鍏�
+  addressEnabled: false
+  # 楠岃瘉鐮佺被鍨� math 鏁板瓧璁$畻 char 瀛楃楠岃瘉
+  captchaType: math
+  # 鍗忓悓瀹℃壒缂栧彿鍓嶇紑(閰嶇疆鏂囦欢鍚庣紑鍛藉悕)
+  approvalNumberPrefix: NEW
+
+  # 涓帹 Unipush 閰嶇疆
+  getui:
+    appId: PfjyAAE0FK64FaO1w2CMb1
+    appKey: zTMb831OEL6J4GK1uE3Ob4
+    masterSecret: K1GFtsv42v61tXGnF7SGE5
+    domain: https://restapi.getui.cn/v2/
+    # 绂荤嚎鎺ㄩ�佷娇鐢ㄧ殑鍖呭悕/缁勪欢鍚�
+    intentComponent: uni.app.UNI099A590/io.dcloud.PandoraEntry
+
+# 寮�鍙戠幆澧冮厤缃�
+server:
+  # 鏈嶅姟鍣ㄧ殑HTTP绔彛锛岄粯璁や负8080
+  port: 9003
+  servlet:
+    # 搴旂敤鐨勮闂矾寰�
+    context-path: /
+  tomcat:
+    # tomcat鐨刄RI缂栫爜
+    uri-encoding: UTF-8
+    # 杩炴帴鏁版弧鍚庣殑鎺掗槦鏁帮紝榛樿涓�100
+    accept-count: 1000
+    threads:
+      # tomcat鏈�澶х嚎绋嬫暟锛岄粯璁や负200
+      max: 800
+      # Tomcat鍚姩鍒濆鍖栫殑绾跨▼鏁帮紝榛樿鍊�10
+      min-spare: 100
+
+# 鏃ュ織閰嶇疆
+logging:
+  level:
+    com.ruoyi: warn
+    org.springframework: warn
+
+minio:
+  endpoint: http://114.132.189.42/
+  port: 7019
+  secure: false
+  accessKey: admin
+  secretKey: 12345678
+  preview-expiry: 24 # 棰勮鍦板潃榛樿24灏忔椂
+  default-bucket: jxc
+# 鐢ㄦ埛閰嶇疆
+user:
+  password:
+    # 瀵嗙爜鏈�澶ч敊璇鏁�
+    maxRetryCount: 5
+    # 瀵嗙爜閿佸畾鏃堕棿锛堥粯璁�10鍒嗛挓锛�
+    lockTime: 10
+
+# Spring閰嶇疆
+spring:
+  datasource:
+    type: com.alibaba.druid.pool.DruidDataSource
+    driverClassName: com.mysql.cj.jdbc.Driver
+    druid:
+      # 涓诲簱鏁版嵁婧�
+      master:
+        url: jdbc:mysql://localhost:3306/product-inventory-management-new-pro?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+        username: root
+        password: root@2026
+      # 浠庡簱鏁版嵁婧�
+      slave:
+        # 浠庢暟鎹簮寮�鍏�/榛樿鍏抽棴
+        enabled: false
+        url:
+        username:
+        password:
+      # 鍒濆杩炴帴鏁�
+      initialSize: 5
+      # 鏈�灏忚繛鎺ユ睜鏁伴噺
+      minIdle: 10
+      # 鏈�澶ц繛鎺ユ睜鏁伴噺
+      maxActive: 20
+      # 閰嶇疆鑾峰彇杩炴帴绛夊緟瓒呮椂鐨勬椂闂�
+      maxWait: 60000
+      # 閰嶇疆杩炴帴瓒呮椂鏃堕棿
+      connectTimeout: 30000
+      # 閰嶇疆缃戠粶瓒呮椂鏃堕棿
+      socketTimeout: 60000
+      # 閰嶇疆闂撮殧澶氫箙鎵嶈繘琛屼竴娆℃娴嬶紝妫�娴嬮渶瑕佸叧闂殑绌洪棽杩炴帴锛屽崟浣嶆槸姣
+      timeBetweenEvictionRunsMillis: 60000
+      # 閰嶇疆涓�涓繛鎺ュ湪姹犱腑鏈�灏忕敓瀛樼殑鏃堕棿锛屽崟浣嶆槸姣
+      minEvictableIdleTimeMillis: 300000
+      # 閰嶇疆涓�涓繛鎺ュ湪姹犱腑鏈�澶х敓瀛樼殑鏃堕棿锛屽崟浣嶆槸姣
+      maxEvictableIdleTimeMillis: 900000
+      # 閰嶇疆妫�娴嬭繛鎺ユ槸鍚︽湁鏁�
+      validationQuery: SELECT 1 FROM DUAL
+      testWhileIdle: true
+      testOnBorrow: false
+      testOnReturn: false
+      webStatFilter:
+        enabled: true
+      statViewServlet:
+        enabled: true
+        # 璁剧疆鐧藉悕鍗曪紝涓嶅~鍒欏厑璁告墍鏈夎闂�
+        allow:
+        url-pattern: /druid/*
+        # 鎺у埗鍙扮鐞嗙敤鎴峰悕鍜屽瘑鐮�
+        login-username: ruoyi
+        login-password: 123456
+      filter:
+        stat:
+          enabled: true
+          # 鎱QL璁板綍
+          log-slow-sql: true
+          slow-sql-millis: 1000
+          merge-sql: true
+        wall:
+          config:
+            multi-statement-allow: true
+  # 璧勬簮淇℃伅
+  messages:
+    # 鍥介檯鍖栬祫婧愭枃浠惰矾寰�
+    basename: i18n/messages
+  # 鏂囦欢涓婁紶
+  servlet:
+    multipart:
+      # 鍗曚釜鏂囦欢澶у皬
+      max-file-size: 1GB
+      # 璁剧疆鎬讳笂浼犵殑鏂囦欢澶у皬
+      max-request-size: 2GB
+  # 鏈嶅姟妯″潡
+  devtools:
+    restart:
+      # 鐑儴缃插紑鍏�
+      enabled: false
+  # redis 閰嶇疆
+  data:
+    mongodb:
+      uri: mongodb://114.132.189.42:9028/chat_memory_db
+    # redis 閰嶇疆
+    redis:
+      # 鍦板潃
+#      host: 127.0.0.1
+      host: 127.0.0.1
+      # 绔彛锛岄粯璁や负6379
+      port: 6379
+      # 鏁版嵁搴撶储寮�
+      database: 0
+      # 瀵嗙爜
+      #    password: root2022!
+      password: root@2026
+
+      # 杩炴帴瓒呮椂鏃堕棿
+      timeout: 10s
+      lettuce:
+        pool:
+          # 杩炴帴姹犱腑鐨勬渶灏忕┖闂茶繛鎺�
+          min-idle: 0
+          # 杩炴帴姹犱腑鐨勬渶澶х┖闂茶繛鎺�
+          max-idle: 8
+          # 杩炴帴姹犵殑鏈�澶ф暟鎹簱杩炴帴鏁�
+          max-active: 8
+          # #杩炴帴姹犳渶澶ч樆濉炵瓑寰呮椂闂达紙浣跨敤璐熷�艰〃绀烘病鏈夐檺鍒讹級
+          max-wait: -1ms
+
+  # Quartz瀹氭椂浠诲姟閰嶇疆锛堟柊澧為儴鍒嗭級
+  quartz:
+    job-store-type: jdbc  # 浣跨敤鏁版嵁搴撳瓨鍌�
+    jdbc:
+      initialize-schema: never  # 棣栨杩愯鏃惰嚜鍔ㄥ垱寤鸿〃缁撴瀯锛屾垚鍔熷悗鏀逛负never
+      schema: classpath:org/quartz/impl/jdbcjobstore/tables_mysql_innodb.sql  # MySQL琛ㄧ粨鏋勮剼鏈�
+    properties:
+      org:
+        quartz:
+          scheduler:
+            instanceName: RuoYiScheduler
+            instanceId: AUTO
+          jobStore:
+            class: org.quartz.impl.jdbcjobstore.JobStoreTX
+            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate  # MySQL閫傞厤
+            tablePrefix: qrtz_  # 琛ㄥ悕鍓嶇紑锛屼笌鑴氭湰涓�鑷�
+            isClustered: false  # 鍗曡妭鐐规ā寮忥紙闆嗙兢闇�鏀逛负true锛�
+            clusterCheckinInterval: 10000
+            txIsolationLevelSerializable: true
+          threadPool:
+            class: org.quartz.simpl.SimpleThreadPool
+            threadCount: 10  # 绾跨▼姹犲ぇ灏�
+            threadPriority: 5
+            makeThreadsDaemons: true
+          updateCheck: false  # 鍏抽棴鐗堟湰妫�鏌�
+# token閰嶇疆
+token:
+  # 浠ょ墝鑷畾涔夋爣璇�
+  header: Authorization
+  # 浠ょ墝瀵嗛挜
+  secret: xpAVjhCjQDaDB7mjPAzMDSbQWXNu2zYkTdDNUsPMS5Xx8QMmQVYN7n74eZrYJxDJ
+  # 浠ょ墝鏈夋晥鏈燂紙榛樿30鍒嗛挓锛�
+  expireTime: 450
+
+# MyBatis Plus閰嶇疆
+mybatis-plus:
+  # 鎼滅储鎸囧畾鍖呭埆鍚�   鏍规嵁鑷繁鐨勯」鐩潵
+  typeAliasesPackage: com.ruoyi.**.pojo
+  # 閰嶇疆mapper鐨勬壂鎻忥紝鎵惧埌鎵�鏈夌殑mapper.xml鏄犲皠鏂囦欢
+  mapperLocations: classpath*:mapper/**/*Mapper.xml
+  # 鍔犺浇鍏ㄥ眬鐨勯厤缃枃浠�
+  configLocation: classpath:mybatis/mybatis-config.xml
+  global-config:
+    enable-sql-runner: true
+    db-config:
+      id-type: auto
+
+# PageHelper鍒嗛〉鎻掍欢
+pagehelper:
+  helperDialect: mysql
+  supportMethodsArguments: true
+  params: count=countSql
+
+# Swagger閰嶇疆
+swagger:
+  # 鏄惁寮�鍚痵wagger
+  enabled: true
+  # 璇锋眰鍓嶇紑
+  pathMapping: /dev-api
+
+# 闃叉XSS鏀诲嚮
+xss:
+  # 杩囨护寮�鍏�
+  enabled: true
+  # 鎺掗櫎閾炬帴锛堝涓敤閫楀彿鍒嗛殧锛�
+  excludes: /system/notice
+  # 鍖归厤閾炬帴
+  urlPatterns: /system/*,/monitor/*,/tool/*
+
+# 浠g爜鐢熸垚
+gen:
+  # 浣滆��
+  author: ruoyi
+  # 榛樿鐢熸垚鍖呰矾寰� system 闇�鏀规垚鑷繁鐨勬ā鍧楀悕绉� 濡� system monitor tool
+  packageName: com.ruoyi.project.system
+  # 鑷姩鍘婚櫎琛ㄥ墠缂�锛岄粯璁ゆ槸true
+  autoRemovePre: false
+  # 琛ㄥ墠缂�锛堢敓鎴愮被鍚嶄笉浼氬寘鍚〃鍓嶇紑锛屽涓敤閫楀彿鍒嗛殧锛�
+  tablePrefix: sys_
+  # 鏄惁鍏佽鐢熸垚鏂囦欢瑕嗙洊鍒版湰鍦帮紙鑷畾涔夎矾寰勶級锛岄粯璁や笉鍏佽
+  allowOverwrite: false
+
+# 鏂囦欢涓婁紶閰嶇疆
+file:
+  temp-dir: /Users/mac/txkj/product-inventory/file/temp/uploads   # 涓存椂鐩綍
+  upload-dir: /Users/mac/txkj/product-inventory/file/prod/uploads # 姝e紡鐩綍
+  path: /Users/mac/txkj/product-inventory/file # 涓婁紶鐩綍
+  urlPrefix: /prod-api/common # 閾炬帴鍓嶇紑
+  domain: http://1.15.17.182:9049 # 鍩熷悕鍓嶇紑
+  expired: 120 # 杩囨湡鏃堕棿(鍗曚綅:鍒嗛挓)
+  useLimit: 10 # 浣跨敤娆℃暟
+  compress: true # 鏄惁鍘嬬缉
+  needCompressSize: 10MB # 鍘嬬缉闃堝��
+  compressQuality: 0.5 # 鍘嬬缉璐ㄩ噺(0.0-1.0)
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index ad3d4bc..0c25f7e 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -6,7 +6,7 @@
   main:
     allow-circular-references: true
   profiles:
-    active: dev
+    active: local
 
 # Pinecone 鍚戦噺鏁版嵁搴撻厤缃�
 pinecone:
diff --git a/src/main/resources/mapper/approve/FinReimbursementMapper.xml b/src/main/resources/mapper/approve/FinReimbursementMapper.xml
index 6b4931c..3b145c0 100644
--- a/src/main/resources/mapper/approve/FinReimbursementMapper.xml
+++ b/src/main/resources/mapper/approve/FinReimbursementMapper.xml
@@ -37,10 +37,12 @@
     <select id="listPage" resultType="com.ruoyi.approve.bean.vo.FinReimbursementVo">
         select fin_reimbursement.*,
         fin_reimbursement_travel.start_time ,
-        fin_reimbursement_travel.end_time
+        fin_reimbursement_travel.end_time,
+        sys_dept.dept_name as deptName
                from
                 fin_reimbursement
             left join fin_reimbursement_travel on fin_reimbursement.id = fin_reimbursement_travel.reimbursement_id
+            left join sys_dept on fin_reimbursement.dept_id = sys_dept.dept_id
         <where>
             <if test="ew.billNo != null and ew.billNo != ''">
                 bill_no like concat('%',#{ew.billNo},'%')
@@ -55,8 +57,46 @@
                 and create_time &gt;= #{ew.createTimeStart}
                 and create_time &lt;= #{ew.createTimeEnd}
             </if>
+            <if test="ew.deptId != null">
+                and fin_reimbursement.dept_id = #{ew.deptId}
+            </if>
+            <if test="ew.expenseType != null and ew.expenseType != ''">
+                and fin_reimbursement.expense_type = #{ew.expenseType}
+            </if>
         </where>
         order by create_time desc
     </select>
 
+    <select id="list" resultType="com.ruoyi.approve.bean.vo.FinReimbursementVo">
+        select fin_reimbursement.*,
+        fin_reimbursement_travel.start_time ,
+        fin_reimbursement_travel.end_time,
+        sys_dept.dept_name as deptName
+               from
+                fin_reimbursement
+            left join fin_reimbursement_travel on fin_reimbursement.id = fin_reimbursement_travel.reimbursement_id
+            left join sys_dept on fin_reimbursement.dept_id = sys_dept.dept_id
+        <where>
+            <if test="ew.billNo != null and ew.billNo != ''">
+                bill_no like concat('%',#{ew.billNo},'%')
+            </if>
+            <if test="ew.applicantName != null and ew.applicantName != ''">
+                and applicant_name like concat('%',#{ew.applicantName},'%')
+            </if>
+            <if test="ew.applicantCode != null and ew.applicantCode != ''">
+                and applicant_code like concat('%',#{ew.applicantCode},'%')
+            </if>
+            <if test="ew.createTimeStart != null and ew.createTimeStart !='' and ew.createTimeEnd != null and ew.createTimeEnd != ''">
+                and create_time &gt;= #{ew.createTimeStart}
+                and create_time &lt;= #{ew.createTimeEnd}
+            </if>
+            <if test="ew.deptId != null">
+                and fin_reimbursement.dept_id = #{ew.deptId}
+            </if>
+            <if test="ew.expenseType != null and ew.expenseType != ''">
+                and fin_reimbursement.expense_type = #{ew.expenseType}
+            </if>
+        </where>
+        order by create_time desc
+    </select>
 </mapper>
diff --git a/src/main/resources/mapper/basic/CustomerMapper.xml b/src/main/resources/mapper/basic/CustomerMapper.xml
index ffaff4e..93c7612 100644
--- a/src/main/resources/mapper/basic/CustomerMapper.xml
+++ b/src/main/resources/mapper/basic/CustomerMapper.xml
@@ -123,29 +123,175 @@
         </where>
         order by c.id desc
     </select>
-    <select id="customewTransactions" resultType="com.ruoyi.sales.vo.CustomerTransactionsVo">
-        select T1.customer_id,
-               c.customer_name,
+    <select id="projectTransactions" resultType="com.ruoyi.sales.vo.ProjectTransactionsVo">
+        select T1.project_id AS projectId,
+               IFNULL(p.title, '鏈垎绫�') AS projectName,
+               p.no AS projectNo,
+               p.status AS status,
                T1.contractAmounts AS contractAmounts,
                IFNULL(T2.receiptPaymentAmount, 0) AS receiptPaymentAmount,
                T1.contractAmounts - IFNULL(T2.receiptPaymentAmount, 0) - IFNULL(T4.returnAmount, 0) AS receiptableAmount,
-               IFNULL(T4.returnAmount, 0) AS returnAmount
-        from (select customer_id, sum(contract_amount) as contractAmounts from sales_ledger group by customer_id) T1
-        left join (select customer_id, sum(collection_amount) as receiptPaymentAmount from account_sales_collection group by customer_id) T2 on T1.customer_id = T2.customer_id
+               IFNULL(T4.returnAmount, 0) AS returnAmount,
+               IFNULL(T5.invoiceAmount, 0) AS invoiceAmount,
+               IFNULL(T6.contractCount, 0) AS contractCount,
+               IFNULL(T6.customerCount, 0) AS customerCount,
+               IFNULL(T6.overdueCount, 0) AS overdueCount
+        from (select IFNULL(project_id, -1) as project_id, sum(contract_amount) as contractAmounts from sales_ledger group by IFNULL(project_id, -1)) T1
+        left join project_management_info p on T1.project_id = p.id
         left join (
             select
-                sl.customer_id,
+                IFNULL(sl.project_id, -1) as project_id,
+                sum(ascc.collection_amount) as receiptPaymentAmount
+            from account_sales_collection ascc
+            left join stock_out_record sor on FIND_IN_SET(sor.id, ascc.stock_out_record_ids) > 0
+            left join shipping_info s on sor.record_id = s.id
+            LEFT JOIN sales_ledger sl ON s.sales_ledger_id = sl.id
+            WHERE sor.record_type='13' and sor.approval_status=1
+            group by IFNULL(sl.project_id, -1)
+        ) T2 on T1.project_id = T2.project_id
+        left join (
+            select
+                IFNULL(sl.project_id, -1) as project_id,
                 sum(rm.refund_amount) as returnAmount
             from return_management rm
             left join shipping_info si on rm.shipping_id = si.id
             left join sales_ledger sl on si.sales_ledger_id = sl.id
             where rm.status=1
-            group by sl.customer_id
-        ) T4 on T4.customer_id=T1.customer_id
+            group by IFNULL(sl.project_id, -1)
+        ) T4 on T4.project_id = T1.project_id
+        left join (
+            select 
+                IFNULL(sl.project_id, -1) as project_id,
+                sum(asi.tax_inclusive_price) as invoiceAmount
+            from account_sales_invoice asi
+            left join account_invoice_application aia on asi.account_invoice_application_id = aia.id
+            left join stock_out_record sor on FIND_IN_SET(sor.id, aia.stock_out_record_ids) > 0
+            left join shipping_info s on sor.record_id = s.id
+            left join sales_ledger sl on s.sales_ledger_id = sl.id
+            where asi.status=0
+            group by IFNULL(sl.project_id, -1)
+        ) T5 on T5.project_id = T1.project_id
+        left join (
+            select IFNULL(sl.project_id, -1) as project_id,
+                   count(sl.id) as contractCount,
+                   count(distinct sl.customer_id) as customerCount,
+                   sum(case when DATEDIFF(NOW(), sl.execution_date) > 30 and IFNULL(T1_rcpt.receiptPaymentAmount, 0) &lt; sl.contract_amount then 1 else 0 end) as overdueCount
+            from sales_ledger sl
+            left join (
+                select sl_in.id, sum(ascc.collection_amount) as receiptPaymentAmount
+                from account_sales_collection ascc
+                left join stock_out_record sor on FIND_IN_SET(sor.id, ascc.stock_out_record_ids) > 0
+                left join shipping_info s on sor.record_id = s.id
+                LEFT JOIN sales_ledger sl_in ON s.sales_ledger_id = sl_in.id
+                WHERE sor.record_type='13' and sor.approval_status=1
+                group by sl_in.id
+            ) T1_rcpt on T1_rcpt.id = sl.id
+            group by IFNULL(sl.project_id, -1)
+        ) T6 on T6.project_id = T1.project_id
+        <where>
+            <if test="projectName!=null and projectName!=''">
+                AND IFNULL(p.title, '鏈垎绫�') LIKE CONCAT('%', #{projectName}, '%')
+            </if>
+            <if test="customerName!=null and customerName!=''">
+                AND EXISTS (
+                    select 1 from sales_ledger sl2 
+                    left join customer c on sl2.customer_id = c.id
+                    where IFNULL(sl2.project_id, -1) = T1.project_id
+                    and c.customer_name like concat('%', #{customerName}, '%')
+                )
+            </if>
+            <if test="contractName!=null and contractName!=''">
+                AND EXISTS (
+                    select 1 from sales_ledger sl3
+                    where IFNULL(sl3.project_id, -1) = T1.project_id
+                    and sl3.sales_contract_no like concat('%', #{contractName}, '%')
+                )
+            </if>
+        </where>
+        order by CASE WHEN T1.project_id = -1 THEN 1 ELSE 0 END DESC, T1.project_id desc
+    </select>
+    <select id="customewTransactions" resultType="com.ruoyi.sales.vo.CustomerTransactionsVo">
+        select T1.customer_id,
+               T1.project_id AS projectId,
+               c.customer_name,
+               T1.contractAmounts AS contractAmounts,
+               IFNULL(T2.receiptPaymentAmount, 0) AS receiptPaymentAmount,
+               T1.contractAmounts - IFNULL(T2.receiptPaymentAmount, 0) - IFNULL(T4.returnAmount, 0) AS receiptableAmount,
+               IFNULL(T4.returnAmount, 0) AS returnAmount,
+               IFNULL(T5.invoiceAmount, 0) AS invoiceAmount,
+               IFNULL(T6.contractCount, 0) AS contractCount,
+               IFNULL(T6.overdueCount, 0) AS overdueCount
+        from (
+            select customer_id, IFNULL(project_id, -1) as project_id, sum(contract_amount) as contractAmounts 
+            from sales_ledger 
+            <where>
+                <if test="projectId!=null">
+                    and IFNULL(project_id, -1) = #{projectId}
+                </if>
+            </where>
+            group by customer_id, IFNULL(project_id, -1)
+        ) T1
+        left join (
+            select
+                sl.customer_id, IFNULL(sl.project_id, -1) as project_id,
+                sum(ascc.collection_amount) as receiptPaymentAmount
+            from account_sales_collection ascc
+            left join stock_out_record sor on FIND_IN_SET(sor.id, ascc.stock_out_record_ids) > 0
+            left join shipping_info s on sor.record_id = s.id
+            LEFT JOIN sales_ledger sl ON s.sales_ledger_id = sl.id
+            WHERE sor.record_type='13' and sor.approval_status=1
+            group by sl.customer_id, IFNULL(sl.project_id, -1)
+        ) T2 on T1.customer_id = T2.customer_id and T1.project_id = T2.project_id
+        left join (
+            select
+                sl.customer_id, IFNULL(sl.project_id, -1) as project_id,
+                sum(rm.refund_amount) as returnAmount
+            from return_management rm
+            left join shipping_info si on rm.shipping_id = si.id
+            left join sales_ledger sl on si.sales_ledger_id = sl.id
+            where rm.status=1
+            group by sl.customer_id, IFNULL(sl.project_id, -1)
+        ) T4 on T4.customer_id = T1.customer_id and T4.project_id = T1.project_id
+        left join (
+            select 
+                sl.customer_id, IFNULL(sl.project_id, -1) as project_id,
+                sum(asi.tax_inclusive_price) as invoiceAmount
+            from account_sales_invoice asi
+            left join account_invoice_application aia on asi.account_invoice_application_id = aia.id
+            left join stock_out_record sor on FIND_IN_SET(sor.id, aia.stock_out_record_ids) > 0
+            left join shipping_info s on sor.record_id = s.id
+            left join sales_ledger sl on s.sales_ledger_id = sl.id
+            where asi.status=0
+            group by sl.customer_id, IFNULL(sl.project_id, -1)
+        ) T5 on T5.customer_id = T1.customer_id and T5.project_id = T1.project_id
+        left join (
+            select sl.customer_id, IFNULL(sl.project_id, -1) as project_id,
+                   count(sl.id) as contractCount,
+                   sum(case when DATEDIFF(NOW(), sl.execution_date) > 30 and IFNULL(T1_rcpt.receiptPaymentAmount, 0) &lt; sl.contract_amount then 1 else 0 end) as overdueCount
+            from sales_ledger sl
+            left join (
+                select sl_in.id, sum(ascc.collection_amount) as receiptPaymentAmount
+                from account_sales_collection ascc
+                left join stock_out_record sor on FIND_IN_SET(sor.id, ascc.stock_out_record_ids) > 0
+                left join shipping_info s on sor.record_id = s.id
+                LEFT JOIN sales_ledger sl_in ON s.sales_ledger_id = sl_in.id
+                WHERE sor.record_type='13' and sor.approval_status=1
+                group by sl_in.id
+            ) T1_rcpt on T1_rcpt.id = sl.id
+            group by sl.customer_id, IFNULL(sl.project_id, -1)
+        ) T6 on T6.customer_id = T1.customer_id and T6.project_id = T1.project_id
         left join customer c on T1.customer_id = c.id
         <where>
             <if test="customerName!=null and customerName!=''">
                 AND c.customer_name LIKE CONCAT('%', #{customerName}, '%')
+            </if>
+            <if test="contractName!=null and contractName!=''">
+                AND EXISTS (
+                    select 1 from sales_ledger sl3
+                    where sl3.customer_id = T1.customer_id
+                    and IFNULL(sl3.project_id, -1) = T1.project_id
+                    and sl3.sales_contract_no like concat('%', #{contractName}, '%')
+                )
             </if>
         </where>
         order by T1.customer_id desc
@@ -158,7 +304,12 @@
                sl.contract_amount AS contract_amount,
                IFNULL(T1.receiptPaymentAmount, 0) AS receiptPaymentAmount,
                sl.contract_amount - IFNULL(T1.receiptPaymentAmount, 0) - IFNULL(T3.returnAmount, 0) AS receiptableAmount,
-               IFNULL(T3.returnAmount, 0) AS returnAmount
+               IFNULL(T3.returnAmount, 0) AS returnAmount,
+               IFNULL(T5.invoiceAmount, 0) AS invoiceAmount,
+               CASE 
+                   WHEN DATEDIFF(NOW(), sl.execution_date) > 30 AND IFNULL(T1.receiptPaymentAmount, 0) &lt; sl.contract_amount THEN 1
+                   ELSE 0 
+               END AS isPaymentTimeout
         from sales_ledger sl
         left join (
             select
@@ -170,7 +321,7 @@
             LEFT JOIN sales_ledger sl ON s.sales_ledger_id = sl.id
             WHERE sor.record_type='13'
               and sor.approval_status=1
-            group by  sl.id
+            group by sl.id
         )T1 on T1.id = sl.id
         left join (
             select sl.id,
@@ -181,7 +332,43 @@
             where rm.status=1
             group by sl.id
         )T3 on T3.id = sl.id
+        left join (
+            select 
+                sl.id,
+                sum(asi.tax_inclusive_price) as invoiceAmount
+            from account_sales_invoice asi
+            left join account_invoice_application aia on asi.account_invoice_application_id = aia.id
+            left join stock_out_record sor on FIND_IN_SET(sor.id, aia.stock_out_record_ids) > 0
+            left join shipping_info s on sor.record_id = s.id
+            left join sales_ledger sl on s.sales_ledger_id = sl.id
+            where asi.status=0
+            group by sl.id
+        ) T5 on T5.id = sl.id
         where sl.customer_id = #{customerId}
+        <if test="projectId!=null">
+            and IFNULL(sl.project_id, -1) = #{projectId}
+        </if>
+        <if test="contractName!=null and contractName!=''">
+            and sl.sales_contract_no like concat('%', #{contractName}, '%')
+        </if>
         order by sl.id desc
     </select>
+
+    <select id="transactionsSummary" resultType="java.util.Map">
+        select count(distinct IFNULL(sl.project_id, -1)) as totalProjects,
+               count(distinct sl.customer_id) as totalCustomers,
+               count(sl.id) as totalContracts,
+               IFNULL(sum(sl.contract_amount), 0) as totalAmount,
+               IFNULL(sum(case when DATEDIFF(NOW(), sl.execution_date) > 30 and IFNULL(rcpt.receiptPaymentAmount, 0) &lt; sl.contract_amount then 1 else 0 end), 0) as totalOverdueCount
+        from sales_ledger sl
+        left join (
+            select sl_in.id, sum(ascc.collection_amount) as receiptPaymentAmount
+            from account_sales_collection ascc
+            left join stock_out_record sor on FIND_IN_SET(sor.id, ascc.stock_out_record_ids) > 0
+            left join shipping_info s on sor.record_id = s.id
+            LEFT JOIN sales_ledger sl_in ON s.sales_ledger_id = sl_in.id
+            WHERE sor.record_type='13' and sor.approval_status=1
+            group by sl_in.id
+        ) rcpt on rcpt.id = sl.id
+    </select>
 </mapper>
diff --git a/src/main/resources/mapper/collaborativeApproval/BroadbandCommunicationMapper.xml b/src/main/resources/mapper/collaborativeApproval/BroadbandCommunicationMapper.xml
new file mode 100644
index 0000000..977f8c1
--- /dev/null
+++ b/src/main/resources/mapper/collaborativeApproval/BroadbandCommunicationMapper.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.collaborativeApproval.mapper.BroadbandCommunicationMapper">
+
+</mapper>
diff --git a/src/main/resources/mapper/device/DeviceLedgerMapper.xml b/src/main/resources/mapper/device/DeviceLedgerMapper.xml
index f3b674e..0fb594b 100644
--- a/src/main/resources/mapper/device/DeviceLedgerMapper.xml
+++ b/src/main/resources/mapper/device/DeviceLedgerMapper.xml
@@ -8,6 +8,16 @@
     <select id="queryPage" resultType="com.ruoyi.device.dto.DeviceLedgerDto">
         SELECT
         dl.id,
+        dl.unique_code,
+        dl.factory_number,
+        dl.manufacturer,
+        dl.original_asset_value,
+        dl.purchase_date,
+        dl.activation_date,
+        dl.technical_parameters,
+        dl.main_purpose,
+        dl.responsible_person,
+        dl.filing_date,
         dl.device_name,
         dl.device_model,
         dl.supplier_name,
@@ -35,6 +45,11 @@
         FROM device_ledger dl
         left join sys_user su on dl.create_user = su.user_id
         <where>
+            <!-- 璁惧缂栧彿 -->
+            <if test="deviceLedger.uniqueCode != null and deviceLedger.uniqueCode != ''">
+                AND unique_code LIKE CONCAT('%', #{deviceLedger.uniqueCode}, '%')
+            </if>
+            
             <!-- 璁惧鍚嶇О -->
             <if test="deviceLedger.deviceName != null and deviceLedger.deviceName != ''">
                 AND device_name LIKE CONCAT('%', #{deviceLedger.deviceName}, '%')
diff --git a/src/main/resources/mapper/device/DeviceRepairMapper.xml b/src/main/resources/mapper/device/DeviceRepairMapper.xml
index 10a0abf..7eda050 100644
--- a/src/main/resources/mapper/device/DeviceRepairMapper.xml
+++ b/src/main/resources/mapper/device/DeviceRepairMapper.xml
@@ -18,6 +18,10 @@
                  dr.acceptance_time,
                  dr.acceptance_remark,
                  dr.status,
+                 dr.repair_type,
+                 dr.reason_analysis,
+                 dr.handling_measures,
+                 dr.spare_parts_ids,
                 dr.create_time,
                 dr.update_time,
                 dr.create_user,
@@ -67,6 +71,10 @@
                dr.acceptance_time,
                dr.acceptance_remark,
                dr.status,
+               dr.repair_type,
+               dr.reason_analysis,
+               dr.handling_measures,
+               dr.spare_parts_ids,
                dr.create_time,
                dr.update_time,
                dr.create_user,
diff --git a/src/main/resources/mapper/device/DeviceStatusChangeRecordMapper.xml b/src/main/resources/mapper/device/DeviceStatusChangeRecordMapper.xml
new file mode 100644
index 0000000..fb6d3ad
--- /dev/null
+++ b/src/main/resources/mapper/device/DeviceStatusChangeRecordMapper.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.device.mapper.DeviceStatusChangeRecordMapper">
+</mapper>
diff --git a/src/main/resources/mapper/measuringinstrumentledger/MeasuringInstrumentLedgerRecordMapper.xml b/src/main/resources/mapper/measuringinstrumentledger/MeasuringInstrumentLedgerRecordMapper.xml
index e01ef1e..9e965b2 100644
--- a/src/main/resources/mapper/measuringinstrumentledger/MeasuringInstrumentLedgerRecordMapper.xml
+++ b/src/main/resources/mapper/measuringinstrumentledger/MeasuringInstrumentLedgerRecordMapper.xml
@@ -13,7 +13,15 @@
         t2.model,
         t1.valid,
         t1.record_date,
-        t1.entry_date
+        t1.entry_date,
+        t1.traceability_type,
+        t1.implementing_agency,
+        t1.certificate_number,
+        t1.core_result,
+        t1.result_confirmation,
+        t1.confirmer_id,
+        t1.confirmer_name,
+        t1.valid_until
         FROM
         measuring_instrument_ledger_record t1
         left join measuring_instrument_ledger t2 on t1.measuring_instrument_ledger_id = t2.id
@@ -44,7 +52,15 @@
         t2.model,
         t1.valid,
         t1.record_date,
-        t1.entry_date
+        t1.entry_date,
+        t1.traceability_type,
+        t1.implementing_agency,
+        t1.certificate_number,
+        t1.core_result,
+        t1.result_confirmation,
+        t1.confirmer_id,
+        t1.confirmer_name,
+        t1.valid_until
         FROM
         measuring_instrument_ledger_record t1
         left join measuring_instrument_ledger t2 on t1.measuring_instrument_ledger_id = t2.id
diff --git a/src/main/resources/mapper/projectManagement/InfoMapper.xml b/src/main/resources/mapper/projectManagement/InfoMapper.xml
index e101797..d1a6d10 100644
--- a/src/main/resources/mapper/projectManagement/InfoMapper.xml
+++ b/src/main/resources/mapper/projectManagement/InfoMapper.xml
@@ -49,17 +49,22 @@
         remark,attachment
     </sql>
     <resultMap id="ListInfoVoMap" type="com.ruoyi.projectManagement.vo.ListInfoVo">
-
         <result column="plan_stage"
                 property="planStage"
                 typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/>
-
     </resultMap>
     <select id="searchListInfo" resultMap="ListInfoVoMap">
-        select t1.*, t2.name as project_management_plan_name,t3.title as projectManagementInfoParentName
+        select t1.*, t2.name as project_management_plan_name,t3.title as projectManagementInfoParentName,
+               IFNULL(t4.sales_contract_count, 0) as salesContractCount
         from project_management_info as t1
         left join project_management_plan as t2 on t1.project_management_plan_id = t2.id
         left join project_management_info as t3 on t1.project_management_info_parent_id = t3.id
+        left join (
+            SELECT project_id, COUNT(DISTINCT sales_contract_no) AS sales_contract_count
+            FROM sales_ledger
+            WHERE sales_contract_no IS NOT NULL AND sales_contract_no != ''
+            GROUP BY project_id
+        ) as t4 on t1.id = t4.project_id
         where t1.is_delete = 0
         <if test="vo.noOrName != null and vo.noOrName != ''">
             and ( t1.no like concat('%', #{vo.noOrName}, '%') OR t1.title like concat('%', #{vo.noOrName}, '%') )
diff --git a/src/main/resources/mapper/sales/BiddingManagementMapper.xml b/src/main/resources/mapper/sales/BiddingManagementMapper.xml
new file mode 100644
index 0000000..ff41328
--- /dev/null
+++ b/src/main/resources/mapper/sales/BiddingManagementMapper.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.sales.mapper.BiddingManagementMapper">
+
+    <resultMap type="BiddingManagement" id="BiddingManagementResult">
+        <result property="id"    column="id"    />
+        <result property="projectId"    column="project_id"    />
+        <result property="projectName"    column="project_name"    />
+        <result property="bidDocName"    column="bid_doc_name"    />
+        <result property="tenderUnit"    column="tender_unit"    />
+        <result property="bidUnit"    column="bid_unit"    />
+        <result property="tenderDate"    column="tender_date"    />
+        <result property="bidDate"    column="bid_date"    />
+        <result property="bidDocFee"    column="bid_doc_fee"    />
+        <result property="bidDocFeeStatus"    column="bid_doc_fee_status"    />
+        <result property="bidDocFeeStatusChangeDate"    column="bid_doc_fee_status_change_date"    />
+        <result property="bidDeposit"    column="bid_deposit"    />
+        <result property="bidResult"    column="bid_result"    />
+        <result property="tenantId"    column="tenant_id"    />
+        <result property="deptId"    column="dept_id"    />
+        <result property="delFlag"    column="del_flag"    />
+        <result property="createBy"    column="create_by"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateBy"    column="update_by"    />
+        <result property="updateTime"    column="update_time"    />
+        <result property="remark"    column="remark"    />
+    </resultMap>
+
+    <sql id="selectBiddingManagementVo">
+        select id, project_id, project_name, bid_doc_name, tender_unit, bid_unit, tender_date, bid_date, bid_doc_fee, bid_doc_fee_status, bid_doc_fee_status_change_date, bid_deposit, bid_result, tenant_id, dept_id, del_flag, create_by, create_time, update_by, update_time, remark from bidding_management
+    </sql>
+
+</mapper>
diff --git a/src/main/resources/mapper/sales/SalesLedgerMapper.xml b/src/main/resources/mapper/sales/SalesLedgerMapper.xml
index 2d6b7f8..1cd1c37 100644
--- a/src/main/resources/mapper/sales/SalesLedgerMapper.xml
+++ b/src/main/resources/mapper/sales/SalesLedgerMapper.xml
@@ -19,6 +19,16 @@
         T1.sales_contract_no,
         T1.customer_contract_no,
         T1.project_name,
+        T1.project_id,
+        T1.year,
+        T1.signing_unit_id,
+        T1.office_id,
+        T1.group_leader_id,
+        T1.is_invoice,
+        T1.bid_price,
+        T1.is_bid,
+        T1.unbid_reason,
+        T1.contract_original_return_date,
         T1.entry_date,
         T1.salesman,
         T1.customer_id,
@@ -37,7 +47,10 @@
         LEFT JOIN sys_user T2 ON T1.entry_person = T2.user_id
         <where>
             <if test="salesLedgerDto.customerName != null and salesLedgerDto.customerName != '' ">
-                T1.customer_name LIKE CONCAT('%',#{salesLedgerDto.customerName},'%')
+                AND T1.customer_name LIKE CONCAT('%',#{salesLedgerDto.customerName},'%')
+            </if>
+            <if test="salesLedgerDto.projectId != null">
+                AND T1.project_id = #{salesLedgerDto.projectId}
             </if>
         </where>
     </select>
@@ -47,6 +60,16 @@
         T1.sales_contract_no,
         T1.customer_contract_no,
         T1.project_name,
+        T1.project_id,
+        T1.year,
+        T1.signing_unit_id,
+        T1.office_id,
+        T1.group_leader_id,
+        T1.is_invoice,
+        T1.bid_price,
+        T1.is_bid,
+        T1.unbid_reason,
+        T1.contract_original_return_date,
         T1.entry_date,
         T1.salesman,
         T1.customer_id,
@@ -80,6 +103,9 @@
             <if test="salesLedgerDto.customerName != null and salesLedgerDto.customerName != '' ">
                 AND T1.customer_name LIKE CONCAT('%',#{salesLedgerDto.customerName},'%')
             </if>
+            <if test="salesLedgerDto.projectId != null">
+                AND T1.project_id = #{salesLedgerDto.projectId}
+            </if>
             <if test="salesLedgerDto.customerContractNo != null and salesLedgerDto.customerContractNo !='' ">
                 AND T1.customer_contract_no LIKE CONCAT('%',#{salesLedgerDto.customerContractNo},'%')
             </if>
diff --git a/src/main/resources/mapper/sales/SalesQuotationMapper.xml b/src/main/resources/mapper/sales/SalesQuotationMapper.xml
index 746e331..ff151a5 100644
--- a/src/main/resources/mapper/sales/SalesQuotationMapper.xml
+++ b/src/main/resources/mapper/sales/SalesQuotationMapper.xml
@@ -4,18 +4,22 @@
         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.ruoyi.sales.mapper.SalesQuotationMapper">
     <select id="listPage" resultType="com.ruoyi.sales.dto.SalesQuotationDto">
-        SELECT t1.*
+        SELECT t1.*, p.title as projectName
         FROM sales_quotation t1
+        LEFT JOIN project_management_info p ON t1.project_id = p.id
         <where>
             <if test="salesQuotationDto.quotationNo != null and salesQuotationDto.quotationNo != '' ">
                 AND t1.quotation_no LIKE CONCAT('%',#{salesQuotationDto.quotationNo},'%')
             </if>
             <if test="salesQuotationDto.customer != null and salesQuotationDto.customer != '' ">
-                AND t1.customer = #{salesQuotationDto.customer}
+                AND t1.customer LIKE CONCAT('%',#{salesQuotationDto.customer},'%')
             </if>
             <if test="salesQuotationDto.status != null and salesQuotationDto.status != '' ">
                 AND t1.status = #{salesQuotationDto.status}
             </if>
+            <if test="salesQuotationDto.projectId != null">
+                AND t1.project_id = #{salesQuotationDto.projectId}
+            </if>
         </where>
         order by t1.id desc
     </select>
diff --git a/src/main/resources/mapper/sales/ShippingInfoMapper.xml b/src/main/resources/mapper/sales/ShippingInfoMapper.xml
index 8775933..b27c0c5 100644
--- a/src/main/resources/mapper/sales/ShippingInfoMapper.xml
+++ b/src/main/resources/mapper/sales/ShippingInfoMapper.xml
@@ -18,13 +18,22 @@
         s.create_user,
         s.update_user,
         s.tenant_id,
+        s.shipping_document,
+        s.is_reconciled,
+        s.statement_archiving_status,
+        s.is_settled,
+        s.settlement_archiving_status,
+        s.is_invoiced,
+        s.project_id,
+        sl.project_name as projectName,
         sl.sales_contract_no,
         pm.model as specification_model,
         pm.unit,
         p.product_name,
         sl.customer_name,
         spd.totalQuantity,
-        sor.outboundBatches
+        sor.outboundBatches,
+        rm_agg.returnNos
         FROM shipping_info s
         LEFT JOIN (select shipping_info_id,sum(quantity) totalQuantity from shipping_product_detail GROUP BY shipping_info_id) spd ON spd.shipping_info_id = s.id
         LEFT JOIN sales_ledger sl ON s.sales_ledger_id = sl.id
@@ -35,6 +44,9 @@
                    from stock_out_record
                    where record_type='13'and approval_status=1
                    group by record_id)sor on sor.record_id= s.id
+        left join (select shipping_id, GROUP_CONCAT(DISTINCT return_no SEPARATOR ',') AS returnNos
+                   from return_management
+                   group by shipping_id) rm_agg on rm_agg.shipping_id = s.id
         WHERE 1=1
         <if test="req.salesContractNo != null and req.salesContractNo != ''">
             AND sl.sales_contract_no LIKE CONCAT('%',#{req.salesContractNo},'%')
@@ -47,6 +59,15 @@
         </if>
         <if test="req.expressNumber != null and req.expressNumber != ''">
             AND s.express_number LIKE CONCAT('%',#{req.expressNumber},'%')
+        </if>
+        <if test="req.projectId != null">
+            AND s.project_id = #{req.projectId}
+        </if>
+        <if test="req.beginShippingDate != null and req.beginShippingDate != ''">
+            AND DATE(s.shipping_date) &gt;= DATE(#{req.beginShippingDate})
+        </if>
+        <if test="req.endShippingDate != null and req.endShippingDate != ''">
+            AND DATE(s.shipping_date) &lt;= DATE(#{req.endShippingDate})
         </if>
         order by create_time DESC
     </select>
@@ -61,10 +82,41 @@
             s.create_user,
             s.update_user,
             s.tenant_id,
+            s.shipping_document,
+            s.is_reconciled,
+            s.statement_archiving_status,
+            s.is_settled,
+            s.settlement_archiving_status,
+            s.is_invoiced,
+            s.project_id,
+            sl.project_name as projectName,
             sl.sales_contract_no,
             sl.customer_name
         FROM shipping_info s
                  LEFT JOIN sales_ledger sl ON s.sales_ledger_id = sl.id
+        WHERE 1=1
+        <if test="req.salesContractNo != null and req.salesContractNo != ''">
+            AND sl.sales_contract_no LIKE CONCAT('%',#{req.salesContractNo},'%')
+        </if>
+        <if test="req.shippingCarNumber != null and req.shippingCarNumber != ''">
+            AND s.shipping_car_number LIKE CONCAT('%',#{req.shippingCarNumber},'%')
+        </if>
+        <if test="req.shippingNo != null and req.shippingNo != ''">
+            AND s.shipping_no LIKE CONCAT('%',#{req.shippingNo},'%')
+        </if>
+        <if test="req.expressNumber != null and req.expressNumber != ''">
+            AND s.express_number LIKE CONCAT('%',#{req.expressNumber},'%')
+        </if>
+        <if test="req.projectId != null">
+            AND s.project_id = #{req.projectId}
+        </if>
+        <if test="req.beginShippingDate != null and req.beginShippingDate != ''">
+            AND DATE(s.shipping_date) &gt;= DATE(#{req.beginShippingDate})
+        </if>
+        <if test="req.endShippingDate != null and req.endShippingDate != ''">
+            AND DATE(s.shipping_date) &lt;= DATE(#{req.endShippingDate})
+        </if>
+        order by create_time DESC
     </select>
     <select id="getReturnManagementDtoById" resultType="com.ruoyi.procurementrecord.bean.vo.ShippingProductVo">
         SELECT distinct
diff --git a/src/main/resources/mapper/vehicle/FuelCardMapper.xml b/src/main/resources/mapper/vehicle/FuelCardMapper.xml
new file mode 100644
index 0000000..3d44b50
--- /dev/null
+++ b/src/main/resources/mapper/vehicle/FuelCardMapper.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.vehicle.mapper.FuelCardMapper">
+    <select id="selectFuelCardPage" resultType="com.ruoyi.vehicle.pojo.FuelCard">
+        SELECT c.*, d.dept_name as deptName
+        FROM p_fuel_card c
+        LEFT JOIN sys_dept d ON c.dept_id = d.dept_id
+        WHERE c.del_flag = '0'
+        <if test="query.cardNumber != null and query.cardNumber != ''">
+            AND c.card_number LIKE concat('%', #{query.cardNumber}, '%')
+        </if>
+        <if test="query.deptId != null">
+            AND (c.dept_id = #{query.deptId} OR c.dept_id IN (SELECT t.dept_id FROM sys_dept t WHERE FIND_IN_SET (#{query.deptId}, ancestors)))
+        </if>
+        ORDER BY c.id DESC
+    </select>
+</mapper>
diff --git a/src/main/resources/mapper/vehicle/FuelCardRechargeMapper.xml b/src/main/resources/mapper/vehicle/FuelCardRechargeMapper.xml
new file mode 100644
index 0000000..e0a5a06
--- /dev/null
+++ b/src/main/resources/mapper/vehicle/FuelCardRechargeMapper.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.vehicle.mapper.FuelCardRechargeMapper">
+    <select id="selectRechargePage" resultType="com.ruoyi.vehicle.pojo.FuelCardRecharge">
+        SELECT r.*, u.nick_name as createUserName
+        FROM p_fuel_card_recharge r
+        LEFT JOIN sys_user u ON r.create_user = u.user_id
+        WHERE r.del_flag = '0'
+        <if test="cardId != null">
+            AND r.card_id = #{cardId}
+        </if>
+        ORDER BY r.recharge_date DESC, r.create_time DESC
+    </select>
+</mapper>
diff --git a/src/main/resources/mapper/vehicle/FuelRecordMapper.xml b/src/main/resources/mapper/vehicle/FuelRecordMapper.xml
new file mode 100644
index 0000000..678d3da
--- /dev/null
+++ b/src/main/resources/mapper/vehicle/FuelRecordMapper.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.vehicle.mapper.FuelRecordMapper">
+    <select id="selectFuelRecordPage" resultType="com.ruoyi.vehicle.pojo.FuelRecord">
+        SELECT r.*, d.dept_name as deptName, v.plate_number as plateNumber, c.card_number as cardNumber
+        FROM p_fuel_record r
+        LEFT JOIN sys_dept d ON r.dept_id = d.dept_id
+        LEFT JOIN p_vehicle_info v ON r.vehicle_id = v.id
+        LEFT JOIN p_fuel_card c ON r.fuel_card_id = c.id
+        WHERE r.del_flag = '0'
+        <if test="query.vehicleId != null">
+            AND r.vehicle_id = #{query.vehicleId}
+        </if>
+        <if test="query.fuelCardId != null">
+            AND r.fuel_card_id = #{query.fuelCardId}
+        </if>
+        <if test="query.deptId != null">
+            AND (r.dept_id = #{query.deptId} OR r.dept_id IN (SELECT t.dept_id FROM sys_dept t WHERE FIND_IN_SET (#{query.deptId}, ancestors)))
+        </if>
+        <if test="beginDate != null and beginDate != '' and endDate != null and endDate != ''">
+            AND r.fuel_date BETWEEN #{beginDate} AND #{endDate}
+        </if>
+        
+        <if test="query.status != null and query.status != ''">
+            AND r.status = #{query.status}
+        </if>
+ORDER BY r.fuel_date DESC
+    </select>
+</mapper>
diff --git a/src/main/resources/mapper/vehicle/VehicleCostMapper.xml b/src/main/resources/mapper/vehicle/VehicleCostMapper.xml
new file mode 100644
index 0000000..dc9bc16
--- /dev/null
+++ b/src/main/resources/mapper/vehicle/VehicleCostMapper.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.vehicle.mapper.VehicleCostMapper">
+</mapper>
diff --git a/src/main/resources/mapper/vehicle/VehicleInfoMapper.xml b/src/main/resources/mapper/vehicle/VehicleInfoMapper.xml
new file mode 100644
index 0000000..f35b2d0
--- /dev/null
+++ b/src/main/resources/mapper/vehicle/VehicleInfoMapper.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.vehicle.mapper.VehicleInfoMapper">
+    <select id="selectVehiclePage" resultType="com.ruoyi.vehicle.pojo.VehicleInfo">
+        SELECT v.*, d.dept_name as deptName, u.nick_name as userName
+        FROM p_vehicle_info v
+        LEFT JOIN sys_dept d ON v.dept_id = d.dept_id
+        LEFT JOIN sys_user u ON v.user_id = u.user_id
+        WHERE v.del_flag = '0'
+        <if test="query.plateNumber != null and query.plateNumber != ''">
+            AND v.plate_number LIKE concat('%', #{query.plateNumber}, '%')
+        </if>
+        <if test="query.deptId != null">
+            AND (v.dept_id = #{query.deptId} OR v.dept_id IN (SELECT t.dept_id FROM sys_dept t WHERE FIND_IN_SET (#{query.deptId}, ancestors)))
+        </if>
+        <if test="query.vehicleStatus != null and query.vehicleStatus != ''">
+            AND v.vehicle_status = #{query.vehicleStatus}
+        </if>
+        ORDER BY 
+        CASE WHEN 
+            (v.inspection_expire_date IS NOT NULL AND DATEDIFF(v.inspection_expire_date, NOW()) &lt;= 30) 
+            OR 
+            (v.insurance_expire_date IS NOT NULL AND DATEDIFF(v.insurance_expire_date, NOW()) &lt;= 30) 
+        THEN 0 ELSE 1 END,
+        v.create_time DESC
+    </select>
+</mapper>
diff --git a/update_fin_reimbursement.sql b/update_fin_reimbursement.sql
new file mode 100644
index 0000000..7aee5f6
--- /dev/null
+++ b/update_fin_reimbursement.sql
@@ -0,0 +1 @@
+ALTER TABLE fin_reimbursement ADD COLUMN payer VARCHAR(50) DEFAULT NULL COMMENT '浠樻鏂�' AFTER detail_total_amount;

--
Gitblit v1.9.3