From bb1d7eeda426e66393cf4403cb3ef082db696bd9 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期五, 07 八月 2026 15:42:19 +0800
Subject: [PATCH] feat(report): 新增会计报表功能并完善设备维护和首页统计

---
 src/main/java/com/ruoyi/account/mapper/AccountStatementMapper.java                                         |   18 
 src/main/java/com/ruoyi/device/dto/DeviceMaintenanceBatchCompleteDto.java                                  |   24 +
 src/main/java/com/ruoyi/home/mapper/HomeMapper.java                                                        |   10 
 src/main/resources/mapper/account/AccountStatementMapper.xml                                               |  135 ++++
 src/main/java/com/ruoyi/inspectiontask/service/impl/InspectionTaskServiceImpl.java                         |   54 ++
 src/main/java/com/ruoyi/measuringinstrumentledger/dto/SparePartsOutboundDto.java                           |   21 
 src/main/java/com/ruoyi/home/controller/HomeController.java                                                |   42 +
 src/main/java/com/ruoyi/device/dto/DeviceMaintenanceDto.java                                               |    3 
 src/main/java/com/ruoyi/common/enums/SparePartsRequisitionRecordSourceTypeEnum.java                        |    3 
 src/main/java/com/ruoyi/measuringinstrumentledger/service/SparePartsRequisitionRecordService.java          |   11 
 src/main/java/com/ruoyi/purchase/service/PurchaseReportService.java                                        |   10 
 src/main/java/com/ruoyi/purchase/dto/VatSummaryDto.java                                                    |   23 
 src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java                                             |  253 +++++++--
 src/main/resources/mapper/sales/SalesLedgerProductMapper.xml                                               |   44 +
 src/main/java/com/ruoyi/purchase/service/impl/PurchaseReportServiceImpl.java                               |   16 
 doc/旭宝峰系统功能优化需求整理.md                                                                                       |   23 
 src/main/java/com/ruoyi/purchase/controller/AccountingReportController.java                                |   42 +
 src/main/java/com/ruoyi/sales/mapper/SalesLedgerMapper.java                                                |    1 
 src/main/java/com/ruoyi/device/service/impl/DeviceMaintenanceServiceImpl.java                              |   85 +++
 src/main/resources/application-dev.yml                                                                     |    2 
 src/main/resources/mapper/home/HomeMapper.xml                                                              |   24 +
 src/main/java/com/ruoyi/device/controller/DeviceMaintenanceController.java                                 |   20 
 src/main/java/com/ruoyi/purchase/dto/VatDto.java                                                           |   69 ++
 src/main/java/com/ruoyi/device/service/IDeviceMaintenanceService.java                                      |    7 
 src/main/java/com/ruoyi/inspectiontask/pojo/InspectionTask.java                                            |    3 
 src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/SparePartsRequisitionRecordServiceImpl.java |   45 +
 src/main/java/com/ruoyi/inspectiontask/service/InspectionTaskService.java                                  |    8 
 src/main/java/com/ruoyi/inspectiontask/controller/InspectionTaskController.java                            |   35 
 doc/旭宝峰系统功能优化-前端联调文档.md                                                                                    |  349 ++++++++++++++
 src/main/java/com/ruoyi/home/service/HomeService.java                                                      |   14 
 src/main/java/com/ruoyi/measuringinstrumentledger/controller/SparePartsRequisitionRecordController.java    |   20 
 src/main/java/com/ruoyi/sales/mapper/SalesLedgerProductMapper.java                                         |    4 
 32 files changed, 1,217 insertions(+), 201 deletions(-)

diff --git "a/doc/\346\227\255\345\256\235\345\263\260\347\263\273\347\273\237\345\212\237\350\203\275\344\274\230\345\214\226-\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243.md" "b/doc/\346\227\255\345\256\235\345\263\260\347\263\273\347\273\237\345\212\237\350\203\275\344\274\230\345\214\226-\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243.md"
new file mode 100644
index 0000000..aa9deec
--- /dev/null
+++ "b/doc/\346\227\255\345\256\235\345\263\260\347\263\273\347\273\237\345\212\237\350\203\275\344\274\230\345\214\226-\345\211\215\347\253\257\350\201\224\350\260\203\346\226\207\346\241\243.md"
@@ -0,0 +1,349 @@
+# 鏃疂宄扮郴缁熷姛鑳戒紭鍖� - 鍓嶇鑱旇皟鏂囨。
+
+---
+
+## 涓�銆佸浠剁鐞� - 澶囦欢鐙珛鍑哄簱
+
+### 1.1 鏂板鎺ュ彛
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| POST | `/sparePartsRequisitionRecord/outbound` | 澶囦欢鐙珛鍑哄簱锛堜笉渚濊禆缁翠慨/淇濆吇锛� |
+
+### 1.2 璇锋眰鍙傛暟
+
+```json
+{
+  "sparePartsId": 10,       // 澶囦欢ID (Long, 蹇呭~)
+  "quantity": 5,            // 鍑哄簱鏁伴噺 (Integer, 蹇呭~)
+  "deviceLedgerId": 1,      // 璁惧鍙拌处ID (Long, 鍙��)
+  "remark": "鎵嬪姩鍑哄簱澶囨敞"    // 澶囨敞 (String, 鍙��)
+}
+```
+
+### 1.3 杩斿洖鍊�
+
+鎴愬姛杩斿洖 `AjaxResult.success()`锛屽け璐ユ姏鍑� `IllegalArgumentException`锛�"澶囦欢涓嶅瓨鍦�" / "澶囦欢 xxx 搴撳瓨涓嶈冻"锛夛紝鍓嶇鐢� `msg` 灞曠ず銆�
+
+### 1.4 鏋氫妇鍙樻洿
+
+`sourceType` 鏂板 `2` = "鐩存帴鍑哄簱"锛坄SparePartsRequisitionRecordSourceTypeEnum`锛夈�傚垪琛ㄦ帴鍙� `/listPage` 杩斿洖鐨� `sourceText` 浼氳嚜鍔ㄦ槧灏勩��
+
+### 1.5 鍓嶇鏀归�犳寚寮�
+
+**鏂囦欢**: `src/views/equipmentManagement/spareParts/index.vue`
+
+- Tab銆屽浠堕鐢ㄨ褰曘�嶅尯鍩熸柊澧� **銆岀洿鎺ュ嚭搴撱��** 鎸夐挳
+- 鐐瑰嚮寮瑰嚭琛ㄥ崟寮圭獥锛屽瓧娈碉細
+  - 澶囦欢閫夋嫨锛坄el-select`锛屽繀濉級
+  - 鍑哄簱鏁伴噺锛坄el-input-number`锛宮in=1锛屽繀濉級
+  - 璁惧閫夋嫨锛堝彲閫夛紝鍙笉鍏宠仈璁惧锛�
+  - 澶囨敞锛堝彲閫夛級
+- 鎻愪氦鍓嶆牎楠屾暟閲� > 0 涓斾笉瓒呰繃搴撳瓨
+- 鎴愬姛鍚庡埛鏂板垪琛�
+
+**API 灏佽** (`src/api/equipmentManagement/sparePartsUsage.js`):
+
+```js
+export function outboundSpareParts(data) {
+  return request({
+    url: '/sparePartsRequisitionRecord/outbound',
+    method: 'post',
+    data
+  })
+}
+```
+
+---
+
+## 浜屻�佽澶囦繚鍏� - 淇濆吇瀹屾垚 / 鎵归噺瀹屾垚
+
+### 2.1 鏂板鎺ュ彛
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| PUT | `/device/maintenance/complete/{id}` | 鍗曟潯淇濆吇瀹屾垚 |
+| PUT | `/device/maintenance/batchComplete` | 涓�閿繚鍏诲畬鎴愶紙鎸夊垱寤烘棩鏈熸壒閲忥級 |
+
+### 2.2 鍗曟潯瀹屾垚 `PUT /complete/{id}`
+
+- Path 鍙傛暟: `id` 鈥� 淇濆吇璁板綍ID
+- 鏃� RequestBody
+- 鍚庣鑷姩璁剧疆 `status=1`銆乣maintenanceActuallyName`=褰撳墠鐧诲綍鐢ㄦ埛銆乣maintenanceActuallyTime`=褰撳墠鏃堕棿
+- 杩斿洖: 鎴愬姛 `AjaxResult.success()`锛屽け璐� `AjaxResult.error("淇濆吇璁板綍涓嶅瓨鍦�" / "璇ヤ繚鍏昏褰曞凡瀹屾垚")`
+
+### 2.3 鎵归噺瀹屾垚 `PUT /batchComplete`
+
+- Query 鍙傛暟: `dateStr` 鈥� 鏃ユ湡瀛楃涓诧紝鏍煎紡 `yyyy-MM-dd`
+- 鍚庣鎸� `createTime` 鏃ユ湡鑼冨洿鏌ヨ璇ユ棩鎵�鏈� `status=0` 璁板綍锛屾壒閲忚缃负瀹岀粨
+- 杩斿洖: 鎴愬姛 `AjaxResult.success("宸插畬鎴� N 鏉′繚鍏昏褰�")`锛屽け璐� `AjaxResult.error("鏃ユ湡鍙傛暟涓嶈兘涓虹┖" / "璇ユ棩鏈熶笅娌℃湁寰呭畬鎴愮殑淇濆吇璁板綍")`
+
+### 2.4 瀹炰綋鍙樻洿
+
+`DeviceMaintenanceDto` 鏂板 `dateStr` 瀛楁锛堟牸寮� `yyyy-MM-dd`锛夛紝鍒楄〃鏌ヨ鏃跺凡鑷姩濉厖锛屽墠绔彲鐢ㄤ簬鏃ユ湡鍒嗙粍銆�
+
+### 2.5 鍓嶇鏀归�犳寚寮�
+
+**鏂囦欢**: `src/views/equipmentManagement/upkeep/index.vue`
+
+1. **鎸� `dateStr` 鏃ユ湡鍒嗙粍鎶樺彔灞曠ず**锛堝弬鐓ч攢鍞彴璐� `type="expand"` 妯″紡锛�
+   - 涓昏鏄剧ず锛氭棩鏈熴�佽褰曟暟銆�**銆屼竴閿繚鍏诲畬鎴愩��** 鎸夐挳
+   - 灞曞紑鍚庡瓙琛ㄦ樉绀烘瘡鏉℃槑缁嗗強 **銆屼繚鍏诲畬鎴愩��** 鎸夐挳
+
+2. **鍗曟潯淇濆吇瀹屾垚鎸夐挳**
+   - 姣忔潯鏄庣粏鎿嶄綔鍒楁柊澧炪�屼繚鍏诲畬鎴愩��
+   - 浠� `status === 0`锛堝緟淇濆吇锛夋椂鏄剧ず
+   - 鐐瑰嚮纭鍚庤皟鐢� `PUT /device/maintenance/complete/{id}`
+
+3. **涓�閿繚鍏诲畬鎴愭寜閽�**
+   - 鍒嗙粍琛屼笂鏄剧ず锛屼紶鍏ヨ鍒嗙粍鐨� `dateStr`
+   - 鐐瑰嚮纭鍚庤皟鐢� `PUT /device/maintenance/batchComplete?dateStr=2026-08-07`
+
+**API 灏佽** (`src/api/equipmentManagement/upkeep.js`):
+
+```js
+export function completeMaintenance(id) {
+  return request({
+    url: '/device/maintenance/complete/' + id,
+    method: 'put'
+  })
+}
+
+export function batchCompleteMaintenance(dateStr) {
+  return request({
+    url: '/device/maintenance/batchComplete',
+    method: 'put',
+    params: { dateStr }
+  })
+}
+```
+
+---
+
+## 涓夈�佽澶囧贰妫� - 宸℃瀹屾垚 / 鎵归噺瀹屾垚
+
+### 3.1 鏂板鎺ュ彛
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| PUT | `/inspectionTask/complete/{id}` | 鍗曟潯宸℃瀹屾垚 |
+| PUT | `/inspectionTask/batchComplete` | 涓�閿贰妫�瀹屾垚锛堟寜鍒涘缓鏃ユ湡鎵归噺锛� |
+
+### 3.2 鍗曟潯瀹屾垚 `PUT /complete/{id}`
+
+- Path 鍙傛暟: `id` 鈥� 宸℃浠诲姟ID
+- 鏃� RequestBody
+- 鍚庣璁剧疆 `status=1`锛岃嫢 `inspectionResult` 涓虹┖鍒欓粯璁よ涓� `"1"`锛堟甯革級
+- 杩斿洖: `R.ok(鍙楀奖鍝嶈鏁�)`锛屽凡瀹屾垚鐨勮褰曚細鎶ラ敊
+
+### 3.3 鎵归噺瀹屾垚 `PUT /batchComplete`
+
+- Query 鍙傛暟: `dateStr` 鈥� 鏃ユ湡瀛楃涓诧紝鏍煎紡 `yyyy-MM-dd`
+- 鍚庣鎸� `createTime` 鏃ユ湡鑼冨洿鏌ヨ璇ユ棩鎵�鏈� `status=0` 璁板綍锛岄�愭潯璁剧疆涓� `status=1`
+- 杩斿洖: `R.ok(瀹屾垚鐨勮褰曟暟)`锛屾棤璁板綍鏃舵姤閿�
+
+### 3.4 瀹炰綋鍙樻洿
+
+`InspectionTask` 鏂板瀛楁锛�
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+|------|------|------|
+| status | Integer | 鐘舵�侊細0=寰呭贰妫�锛�1=宸插畬鎴� |
+
+> `GET /inspectionTask/list` 杩斿洖涓凡鍖呭惈 `status` 瀛楁鍜� `dateStr` 鍒嗙粍瀛楁銆�
+
+### 3.5 鏁版嵁搴撳彉鏇达紙闇� DBA 鎵ц锛�
+
+```sql
+ALTER TABLE `inspection_task`
+ADD COLUMN `status` tinyint NULL DEFAULT 0
+COMMENT '鐘舵�� 0=寰呭贰妫�, 1=宸插畬鎴�';
+```
+
+### 3.6 鍓嶇鏀归�犳寚寮�
+
+**鏂囦欢**: `src/views/equipmentManagement/inspectionManagement/index.vue`
+
+1. **鎸� `dateStr` 鏃ユ湡鍒嗙粍鎶樺彔灞曠ず**
+2. **鍗曟潯宸℃瀹屾垚鎸夐挳** 鈥� 浠� `status === 0` 鏃舵樉绀猴紝璋冪敤 `PUT /inspectionTask/complete/{id}`
+3. **涓�閿贰妫�瀹屾垚鎸夐挳** 鈥� 鍒嗙粍琛屼笂鏄剧ず锛岃皟鐢� `PUT /inspectionTask/batchComplete?dateStr=xxx`
+4. **鐘舵�佸垪** 鈥� 浣跨敤 `status` 瀛楁锛�0=寰呭贰妫�(warning)銆�1=宸插畬鎴�(success)
+
+**API 灏佽** (`src/api/inspectionManagement/index.js`):
+
+```js
+export function completeInspection(id) {
+  return request({
+    url: '/inspectionTask/complete/' + id,
+    method: 'put'
+  })
+}
+
+export function batchCompleteInspection(dateStr) {
+  return request({
+    url: '/inspectionTask/batchComplete',
+    method: 'put',
+    params: { dateStr }
+  })
+}
+```
+
+---
+
+## 鍥涖�佸鍊肩◣瀵规瘮椤甸潰
+
+### 4.1 鏂板鎺ュ彛
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| GET | `/purchase/report/listVatDetail` | 澧炲�肩◣鏄庣粏锛堟寜鍙戠エ/璁㈠崟灞曠ず锛� |
+
+### 4.2 `GET /listVatDetail` 璇锋眰鍙傛暟
+
+| 鍙傛暟 | 绫诲瀷 | 蹇呭~ | 璇存槑 |
+|------|------|------|------|
+| current | int | 鍚� | 椤电爜 |
+| size | int | 鍚� | 姣忛〉鏉℃暟 |
+| month | String | 鍚� | 鏈堜唤绛涢�夛紝鏍煎紡 `yyyy-MM` |
+
+### 4.3 `GET /listVatDetail` 杩斿洖缁撴瀯
+
+```json
+{
+  "records": [
+    {
+      "invoiceNo": "CG202608001",       // 鍙戠エ鍙凤紙閲囪喘鍚堝悓鍙�/閿�鍞悎鍚屽彿锛�
+      "salesContractNo": "XS202608001", // 閿�鍞悎鍚屽彿
+      "supplierName": "XX渚涘簲鍟�",        // 渚涘簲鍟嗗悕绉帮紙杩涢」鏃舵湁鍊硷級
+      "customerName": "XX瀹㈡埛",          // 瀹㈡埛鍚嶇О锛堥攢椤规椂鏈夊�硷級
+      "orderType": "杩涢」",               // 杩涢」 / 閿�椤�
+      "invoiceDate": "2026-08-03",      // 寮�绁ㄦ棩鏈�
+      "taxRate": 13.00,                 // 绋庣巼
+      "jTaxAmount": 13000.00,           // 杩涢」绋庨
+      "xTaxAmount": 0.00,               // 閿�椤圭◣棰�
+      "taxAmount": 13000.00             // 绋庨
+    }
+  ]
+}
+```
+
+### 4.4 宸叉湁鎺ュ彛锛堜繚鎸佷笉鍙橈級
+
+| 鏂规硶 | 璺緞 | 璇存槑 |
+|------|------|------|
+| GET | `/purchase/report/listVat` | 澧炲�肩◣鍙戠エ姹囨�� |
+| GET | `/purchase/report/vatChart` | 澧炲�肩◣杩涢攢椤瑰浘琛ㄦ暟鎹紙鎸夋湀鑱氬悎锛� |
+
+### 4.5 鍓嶇鏀归�犳寚寮�
+
+**鏂囦欢**: `src/views/reportAnalysis/taxComparison/index.vue`
+
+1. 鏂板 **Tab 鍒囨崲**:銆屾眹鎬昏鍥俱��/銆屾槑缁嗚鍥俱��
+   - 姹囨�昏鍥� 鈫� 璋冪敤鍘熸湁 `listVat` 鎺ュ彛
+   - 鏄庣粏瑙嗗浘 鈫� 璋冪敤鏂板 `listVatDetail` 鎺ュ彛
+2. 鏄庣粏瑙嗗浘琛ㄦ牸鍒�: 鍙戠エ鍙枫�侀攢鍞悎鍚屽彿銆佸鎴�/渚涘簲鍟嗐�佺被鍨�(tag)銆佸紑绁ㄦ棩鏈熴�佺◣鐜囥�佺◣棰�
+3. 淇濈暀鏈堜唤绛涢�夊拰鍥捐〃鍖哄煙
+
+**API 灏佽** (`src/api/procurementManagement/taxComparison.js`):
+
+```js
+export function getVatDetail(query) {
+  return request({
+    url: '/purchase/report/listVatDetail',
+    method: 'get',
+    params: query  // { current, size, month }
+  })
+}
+```
+
+---
+
+## 浜斻�佺鐞嗛┚椹惰埍锛堥椤� PSI 鏁版嵁鍒嗘瀽澶у睆锛�
+
+### 5.1 鍚庣鍙樻洿
+
+浠ヤ笅鎺ュ彛鏂板鍙�夋棩鏈熻寖鍥村弬鏁� `startDate` / `endDate`锛堟牸寮� `yyyy-MM-dd`锛夛紝涓嶄紶鍒欐寜鍘熸湁 type 閫昏緫璁$畻锛堝懆/鏈�/瀛e害锛夛細
+
+| 鏂规硶 | 璺緞 | 鍙樻洿璇存槑 |
+|------|------|----------|
+| GET | `/home/supplierPurchaseRanking` | 鏂板 `startDate`/`endDate` 鍙�夊弬鏁� |
+| GET | `/home/customerContributionRanking` | 鏂板 `startDate`/`endDate` 鍙�夊弬鏁� |
+| GET | `/home/productSalesAnalysis` | 鏂板 `startDate`/`endDate` 鍙�夊弬鏁� |
+| GET | `/home/rawMaterialPurchaseAmountRatio` | 鏂板 `startDate`/`endDate` 鍙�夊弬鏁� |
+| GET | `/home/salesPurchaseStorageProductCount` | 鏂板 `startDate`/`endDate` 鍙�夊弬鏁帮紱label 鏀逛负"閿�鍞鍗曟暟" |
+| GET | `/home/productInOutAnalysis` | 鏂板 `startDate`/`endDate` 鍙�夊弬鏁� |
+| GET | `/home/productTurnoverDays` | 鏂板 `startDate`/`endDate` 鍙�夊弬鏁� |
+
+### 5.2 鍏抽敭鍙樻洿璇︽儏
+
+**`/home/salesPurchaseStorageProductCount`**:
+
+- 绗竴涓寚鏍囦粠銆岄攢鍞骇鍝佹暟銆嶆敼涓� **銆岄攢鍞鍗曟暟銆�**锛堢粺璁� `sales_ledger` 琛ㄨ鍗曟暟锛岄潪浜у搧鏁帮級
+- Query 鍙傛暟锛堝潎涓哄彲閫夛級:
+  - `startDate`: 寮�濮嬫棩鏈� `yyyy-MM-dd`
+  - `endDate`: 缁撴潫鏃ユ湡 `yyyy-MM-dd`
+  - `type`: 涓嶄紶鏃ユ湡鍙傛暟鏃朵娇鐢紙1=鍛�, 2=鏈�, 3=瀛e害锛�
+- 杩斿洖缁撴瀯涓嶅彉 (`List<MapDto>`): `[{ name, value, rate }]`
+
+**`/home/productCategoryDistribution`**:
+
+- 閲囪喘鍝佸ぇ绫诲垎甯冪幆褰㈠浘鏁版嵁宸蹭慨澶嶏紝灏忕被鐨� `value`/`rate` 鐜板湪鍖呭惈鐪熷疄閲囪喘鏁版嵁
+- 鏃犻渶鍓嶇鏀瑰姩
+
+### 5.3 鍓嶇鏀归�犳寚寮�
+
+**鏂囦欢**: `src/views/reportAnalysis/PSIDataAnalysis/index.vue`
+
+1. **鏃ユ湡绛涢�夋帶浠�**
+   - 椤甸潰椤堕儴鏂板 `el-date-picker`锛堢被鍨� `daterange`锛屾牸寮� `yyyy-MM-dd`锛�
+   - 蹇嵎閫夐」: 鏈湀銆佽繎7澶┿�佽繎30澶┿�佽繎90澶�
+   - 閫氳繃 props / provide 灏嗛�変腑鏃ユ湡浼犵粰鍚勫瓙缁勪欢
+
+2. **鏍囩鏂囧瓧淇敼** (`components/center-top.vue`)
+   - 绗竴寮犲崱鐗囨爣棰�: 銆岄攢鍞骇鍝佹暟銆嶁啋 **銆岄攢鍞鍗曟暟銆�**
+
+3. **API 璋冪敤鏇存柊**
+   - 鍚勫瓙缁勪欢璋冪敤鎺ュ彛鏃朵紶鍏� `startDate` / `endDate` 鍙傛暟
+
+**API 灏佽绀轰緥** (`src/api/viewIndex.js`):
+
+```js
+export function getSalesPurchaseStorageProductCount(params) {
+  return request({
+    url: '/home/salesPurchaseStorageProductCount',
+    method: 'get',
+    params  // { startDate, endDate } 鎴� { type } 浜岄�変竴
+  })
+}
+```
+
+> 鎵�鏈� `/home` 鎺ュ彛鍧囬伒寰浉鍚屾ā寮忥細浼� `startDate`/`endDate` 鏃朵笉浼� `type`锛屽弽涔嬩害鐒躲��
+
+---
+
+## 鍏�佹敼鍔ㄦ枃浠舵竻鍗�
+
+| 椤甸潰 | 鏂囦欢璺緞 | 鏀瑰姩绫诲瀷 |
+|------|----------|----------|
+| 澶囦欢绠$悊 | `src/views/equipmentManagement/spareParts/index.vue` | 鏂板銆岀洿鎺ュ嚭搴撱�嶆寜閽�+寮圭獥 |
+| 澶囦欢API | `src/api/equipmentManagement/sparePartsUsage.js` | 鏂板 `outboundSpareParts` |
+| 璁惧淇濆吇 | `src/views/equipmentManagement/upkeep/index.vue` | 鏃ユ湡鍒嗙粍+淇濆吇瀹屾垚+涓�閿繚鍏诲畬鎴� |
+| 淇濆吇API | `src/api/equipmentManagement/upkeep.js` | 鏂板 `completeMaintenance` / `batchCompleteMaintenance` |
+| 璁惧宸℃ | `src/views/equipmentManagement/inspectionManagement/index.vue` | 鏃ユ湡鍒嗙粍+宸℃瀹屾垚+涓�閿贰妫�瀹屾垚 |
+| 宸℃API | `src/api/inspectionManagement/index.js` | 鏂板 `completeInspection` / `batchCompleteInspection` |
+| 澧炲�肩◣瀵规瘮 | `src/views/reportAnalysis/taxComparison/index.vue` | 鏂板鏄庣粏瑙嗗浘 Tab |
+| 澧炲�肩◣API | `src/api/procurementManagement/taxComparison.js` | 鏂板 `getVatDetail` |
+| PSI澶у睆 | `src/views/reportAnalysis/PSIDataAnalysis/index.vue` | 鏂板鏃ユ湡绛涢�夋帶浠� |
+| PSI缁熻鍗� | `src/views/reportAnalysis/PSIDataAnalysis/components/center-top.vue` | 鏍囩鏀�"閿�鍞鍗曟暟"+鏃ユ湡鍙傛暟 |
+| 澶у睆API | `src/api/viewIndex.js` | 鎵�鏈夋帴鍙e鍔� `startDate`/`endDate` 鍙傛暟 |
+
+---
+
+## 涓冦�佹敞鎰忎簨椤�
+
+1. **宸℃ DDL**: 闇�閫氱煡 DBA 鎵ц `ALTER TABLE inspection_task ADD COLUMN status tinyint ...`
+2. **HTTP 鏂规硶**: 瀹屾垚绫绘帴鍙d娇鐢� **PUT**锛屾柊澧炲嚭搴撲娇鐢� **POST**锛屾煡璇娇鐢� **GET**
+3. **鏃ユ湡鏍煎紡**: 鎵�鏈夋棩鏈熷弬鏁颁娇鐢� `yyyy-MM-dd` 瀛楃涓叉牸寮�
+4. **绌哄弬鏁板吋瀹�**: 鏃ユ湡鍙傛暟鍧囦负鍙�夛紝涓嶄紶鍒欎繚鎸佸師鏈� type 閫昏緫
+5. **鎵归噺瀹屾垚**: 淇濆吇/宸℃鐨勬壒閲忓畬鎴愬潎鎸� `createTime` 鏃ユ湡鑼冨洿鍖归厤锛岄潪 `planTime`
+6. **閿欒澶勭悊**: 澶囦欢鍑哄簱澶辫触浼氭姏寮傚父锛堥潪 AjaxResult锛夛紝鍓嶇闇�鍦� catch 涓睍绀洪敊璇俊鎭�
diff --git "a/doc/\346\227\255\345\256\235\345\263\260\347\263\273\347\273\237\345\212\237\350\203\275\344\274\230\345\214\226\351\234\200\346\261\202\346\225\264\347\220\206.md" "b/doc/\346\227\255\345\256\235\345\263\260\347\263\273\347\273\237\345\212\237\350\203\275\344\274\230\345\214\226\351\234\200\346\261\202\346\225\264\347\220\206.md"
new file mode 100644
index 0000000..7332c0c
--- /dev/null
+++ "b/doc/\346\227\255\345\256\235\345\263\260\347\263\273\347\273\237\345\212\237\350\203\275\344\274\230\345\214\226\351\234\200\346\261\202\346\225\264\347\220\206.md"
@@ -0,0 +1,23 @@
+# 涓�銆佽澶囩鐞嗘ā鍧�
+
+## 1\. 澶囦欢绠$悊 - 澶囦欢棰嗙敤璁板綍
+
+鐜版湁澶囦欢鍑哄簱浠呰兘渚濇墭璁惧妫�淇満鏅紝鍑哄簱娓犻亾瀛樺湪灞�闄愶紝闇�鏂板鐙珛澶囦欢鍑哄簱鍔熻兘鍏ュ彛锛屾敮鎸佺洿鎺ュ畬鎴愬浠堕鐢ㄥ嚭搴撴搷浣溿��
+
+## 澶囦欢棰嗙敤璁板綍椤甸潰2. 璁惧淇濆吇 - 淇濆吇浠诲姟
+
+淇濆吇浠诲姟涓嬪彂鍚庯紝椤甸潰鍙傜収閿�鍞� / 閲囪喘璁㈠崟鏍峰紡锛屾敮鎸佹寜鏃ユ湡鍒嗙粍鎶樺彔灞曠ず锛屽垎缁勬潯鐩彲灞曞紑鏌ョ湅鏄庣粏锛涘崟鏉℃槑缁嗚鏈熬澧炲姞銆愪繚鍏诲畬鎴愩�戞搷浣滄寜閽紝鍙崟鐙姙缁撳崟鏉′繚鍏讳换鍔★紱鎶樺彔姹囨�荤姸鎬佷笅锛屾柊澧炪�愪竴閿繚鍏诲畬鎴愩�戞壒閲忔搷浣滄寜閽紝蹇�熷姙缁撳綋鍓嶅垎缁勫叏閮ㄤ繚鍏婚」銆�
+
+## 淇濆吇浠诲姟椤甸潰3. 璁惧宸℃ - 宸℃浠诲姟
+
+宸℃浠诲姟涓嬪彂瀹屾垚鍚庯紝椤甸潰鍙傜収閿�鍞� / 閲囪喘璁㈠崟甯冨眬锛屾寜鏃ユ湡鍒嗙粍鎶樺彔灞曠ず锛屽垎缁勬敮鎸佸睍寮�鏌ョ湅鏄庣粏锛涙瘡鏉″贰妫�鏄庣粏鍚庡璁俱�愬贰妫�瀹屾垚銆戞寜閽紝鏀寔鍗曠嫭鍔炵粨鍗曟潯宸℃璁板綍锛涘垎缁勬姌鍙犳湭灞曞紑鐘舵�佷笅锛屾彁渚涖�愪竴閿贰妫�瀹屾垚銆戞壒閲忔搷浣滐紝缁熶竴鍔炵粨璇ュ垎缁勪笅鎵�鏈夊贰妫�浠诲姟銆�
+
+# 浜屻�佸喅绛栧垎鏋愭ā鍧�
+
+## 1\. 澧炲�肩◣瀵规瘮椤甸潰
+
+褰撳墠椤甸潰鏌ヨ鏃犳暟鎹紝鎸夌洃鐞嗚姹備紭鍖栨暟鎹睍绀洪�昏緫锛屽垪琛ㄥ睍绀哄悇璁㈠崟澧炲�肩◣鏄庣粏锛涢〉闈㈠彸渚ф柊澧炴煴鐘跺彲瑙嗗寲鍥捐〃锛岀洿瑙傚姣旇繘閿�椤瑰鍊肩◣鏁版嵁銆�
+
+## 澧炲�肩◣瀵规瘮椤甸潰2. 绠$悊椹鹃┒鑸憋紙PSI 鏁版嵁鍒嗘瀽澶у睆锛�
+
+鎸囨爣淇敼锛氬皢椤堕儴銆岄攢鍞骇鍝佹暟銆嶈皟鏁翠负銆岄攢鍞鍗曟暟銆嶏紱鏃堕棿绛涢�夛細椤甸潰宸︿笂瑙掓柊澧炴棩鏈熻寖鍥翠笅鎷夌瓫閫夋帶浠讹紝鎵�鏈夌粺璁℃寚鏍囨爣娉ㄥ綋鍓嶇瓫閫夋椂娈碉紱鏁版嵁淇锛氳В鍐炽�岄噰璐搧鍒嗗竷銆嶇幆褰㈠浘鏃犳暟鎹睍绀洪棶棰樸��
\ No newline at end of file
diff --git a/src/main/java/com/ruoyi/account/mapper/AccountStatementMapper.java b/src/main/java/com/ruoyi/account/mapper/AccountStatementMapper.java
index 89730cb..7c66129 100644
--- a/src/main/java/com/ruoyi/account/mapper/AccountStatementMapper.java
+++ b/src/main/java/com/ruoyi/account/mapper/AccountStatementMapper.java
@@ -7,22 +7,20 @@
 import com.ruoyi.account.bean.vo.StatementAccountVo;
 import com.ruoyi.account.pojo.AccountStatement;
 import com.ruoyi.purchase.dto.VatDto;
+import com.ruoyi.purchase.dto.VatSummaryDto;
 import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
-/**
- * <p>
- *  Mapper 鎺ュ彛
- * </p>
- *
- * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
- * @since 2026-05-19 09:42:47
- */
+import java.util.List;
+
 @Mapper
 public interface AccountStatementMapper extends BaseMapper<AccountStatement> {
 
-
     IPage<StatementAccountVo> listPageAccountStatement(Page page, @Param("req") StatementAccountDto statementAccountDto);
 
-    IPage<VatDto> selectVatDtoPage(Page page, @Param("month") String month);
+    IPage<VatDto> selectVatDtoPage(Page page, @Param("month") String month, @Param("type") String type);
+
+    List<VatSummaryDto> selectVatSummary(@Param("year") String year);
+
+    IPage<VatDto> listVatDetail(Page page, @Param("month") String month);
 }
diff --git a/src/main/java/com/ruoyi/common/enums/SparePartsRequisitionRecordSourceTypeEnum.java b/src/main/java/com/ruoyi/common/enums/SparePartsRequisitionRecordSourceTypeEnum.java
index b9acc40..d853453 100644
--- a/src/main/java/com/ruoyi/common/enums/SparePartsRequisitionRecordSourceTypeEnum.java
+++ b/src/main/java/com/ruoyi/common/enums/SparePartsRequisitionRecordSourceTypeEnum.java
@@ -5,7 +5,8 @@
 @Getter
 public enum SparePartsRequisitionRecordSourceTypeEnum implements BaseEnum<Integer> {
     SparePartsRequisitionRecordSourceTypeRepair(0, "缁翠慨"),
-    SparePartsRequisitionRecordSourceTypeMaintenance(1, "淇濆吇");
+    SparePartsRequisitionRecordSourceTypeMaintenance(1, "淇濆吇"),
+    SparePartsRequisitionRecordSourceTypeDirectOutbound(2, "鐩存帴鍑哄簱");
 
     private final Integer code;
     private final String value;
diff --git a/src/main/java/com/ruoyi/device/controller/DeviceMaintenanceController.java b/src/main/java/com/ruoyi/device/controller/DeviceMaintenanceController.java
index e34fc37..28e9b0d 100644
--- a/src/main/java/com/ruoyi/device/controller/DeviceMaintenanceController.java
+++ b/src/main/java/com/ruoyi/device/controller/DeviceMaintenanceController.java
@@ -2,6 +2,7 @@
 
 
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.device.dto.DeviceMaintenanceBatchCompleteDto;
 import com.ruoyi.device.dto.DeviceMaintenanceDto;
 import com.ruoyi.device.pojo.DeviceLedger;
 import com.ruoyi.device.pojo.DeviceMaintenance;
@@ -12,6 +13,7 @@
 import io.swagger.v3.oas.annotations.Operation;
 import jakarta.servlet.http.HttpServletResponse;
 import lombok.AllArgsConstructor;
+import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.Arrays;
@@ -79,5 +81,23 @@
         deviceMaintenanceService.export(response, ids);
     }
 
+    @PostMapping("/batchComplete")
+    @Operation(summary = "涓�閿繚鍏诲畬鎴�")
+    @Transactional(rollbackFor = Exception.class)
+    public AjaxResult batchComplete(@RequestBody DeviceMaintenanceBatchCompleteDto dto) {
+        return deviceMaintenanceService.batchComplete(dto);
+    }
+
+    @PutMapping("/complete/{id}")
+    @Operation(summary = "淇濆吇瀹屾垚")
+    public AjaxResult complete(@PathVariable Long id) {
+        return deviceMaintenanceService.complete(id);
+    }
+
+    @PutMapping("/batchComplete")
+    @Operation(summary = "涓�閿繚鍏诲畬鎴�")
+    public AjaxResult batchComplete(String dateStr) {
+        return deviceMaintenanceService.batchComplete(dateStr);
+    }
 
 }
diff --git a/src/main/java/com/ruoyi/device/dto/DeviceMaintenanceBatchCompleteDto.java b/src/main/java/com/ruoyi/device/dto/DeviceMaintenanceBatchCompleteDto.java
new file mode 100644
index 0000000..b3e7aff
--- /dev/null
+++ b/src/main/java/com/ruoyi/device/dto/DeviceMaintenanceBatchCompleteDto.java
@@ -0,0 +1,24 @@
+package com.ruoyi.device.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+@Data
+@Schema(name = "璁惧淇濆吇鎵归噺瀹屾垚璇锋眰")
+public class DeviceMaintenanceBatchCompleteDto {
+
+    @Schema(description = "璁″垝淇濆吇鏃ユ湡", requiredMode = Schema.RequiredMode.REQUIRED)
+    private LocalDate maintenancePlanDate;
+
+    @Schema(description = "瀹為檯淇濆吇浜�")
+    private String maintenanceActuallyName;
+
+    @Schema(description = "瀹為檯淇濆吇鏃堕棿")
+    private LocalDateTime maintenanceActuallyTime;
+
+    @Schema(description = "淇濆吇缁撴灉")
+    private String maintenanceResult;
+}
diff --git a/src/main/java/com/ruoyi/device/dto/DeviceMaintenanceDto.java b/src/main/java/com/ruoyi/device/dto/DeviceMaintenanceDto.java
index 127d82b..81d34a0 100644
--- a/src/main/java/com/ruoyi/device/dto/DeviceMaintenanceDto.java
+++ b/src/main/java/com/ruoyi/device/dto/DeviceMaintenanceDto.java
@@ -53,5 +53,8 @@
     @Schema(description = "淇濆吇鍥剧墖")
     private List<StorageBlobDTO> storageBlobDTOs;
 
+    @Schema(description = "鏃ユ湡鍒嗙粍瀛楁(yyyy-MM-dd)")
+    private String dateStr;
+
 }
 
diff --git a/src/main/java/com/ruoyi/device/service/IDeviceMaintenanceService.java b/src/main/java/com/ruoyi/device/service/IDeviceMaintenanceService.java
index e5a4080..3f1d770 100644
--- a/src/main/java/com/ruoyi/device/service/IDeviceMaintenanceService.java
+++ b/src/main/java/com/ruoyi/device/service/IDeviceMaintenanceService.java
@@ -3,6 +3,7 @@
 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.dto.DeviceMaintenanceBatchCompleteDto;
 import com.ruoyi.device.dto.DeviceMaintenanceDto;
 import com.ruoyi.device.pojo.DeviceMaintenance;
 import com.ruoyi.device.vo.DeviceMaintenanceVo;
@@ -21,4 +22,10 @@
     void export(HttpServletResponse response, Long[] ids);
 
     DeviceMaintenanceVo detailById(Long id);
+
+    AjaxResult batchComplete(DeviceMaintenanceBatchCompleteDto dto);
+
+    AjaxResult complete(Long id);
+
+    AjaxResult batchComplete(String dateStr);
 }
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..e426bfe 100644
--- a/src/main/java/com/ruoyi/device/service/impl/DeviceMaintenanceServiceImpl.java
+++ b/src/main/java/com/ruoyi/device/service/impl/DeviceMaintenanceServiceImpl.java
@@ -1,13 +1,16 @@
 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.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.ruoyi.basic.enums.RecordTypeEnum;
 import com.ruoyi.basic.utils.FileUtil;
+import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.bean.BeanUtils;
 import com.ruoyi.common.utils.poi.ExcelUtil;
+import com.ruoyi.device.dto.DeviceMaintenanceBatchCompleteDto;
 import com.ruoyi.device.dto.DeviceMaintenanceDto;
 import com.ruoyi.device.execl.DeviceMaintenanceExeclDto;
 import com.ruoyi.device.mapper.DeviceMaintenanceMapper;
@@ -27,6 +30,9 @@
 import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -95,6 +101,11 @@
             }
         }
 
+        // 鑻ヨ缃簡瀹為檯淇濆吇鏃ユ湡锛屽垯鑷姩鏍囪涓哄畬缁�
+        if (deviceMaintenance.getMaintenanceActuallyTime() != null && deviceMaintenance.getStatus() == null) {
+            deviceMaintenance.setStatus(1);
+        }
+
         if (this.updateById(deviceMaintenance)) {
             // 澶勭悊鍥剧墖涓婁紶
             fileUtil.saveStorageAttachmentByRecordTypeAndRecordId("file", RecordTypeEnum.DEVICE_MAINTENANCE, deviceMaintenance.getId(), deviceMaintenance.getStorageBlobDTOs());
@@ -104,6 +115,80 @@
     }
 
     @Override
+    @Transactional(rollbackFor = Exception.class)
+    public AjaxResult complete(Long id) {
+        DeviceMaintenance record = this.getById(id);
+        if (record == null) {
+            return AjaxResult.error("淇濆吇璁板綍涓嶅瓨鍦�");
+        }
+        if (record.getStatus() != null && record.getStatus() == 1) {
+            return AjaxResult.error("璇ヤ繚鍏昏褰曞凡瀹屾垚");
+        }
+        record.setStatus(1);
+        record.setMaintenanceActuallyName(SecurityUtils.getLoginUser().getNickName());
+        record.setMaintenanceActuallyTime(LocalDateTime.now());
+        this.updateById(record);
+        return AjaxResult.success();
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public AjaxResult batchComplete(String dateStr) {
+        if (StringUtils.isBlank(dateStr)) {
+            return AjaxResult.error("鏃ユ湡鍙傛暟涓嶈兘涓虹┖");
+        }
+        LocalDate date = LocalDate.parse(dateStr);
+        LocalDateTime startOfDay = date.atStartOfDay();
+        LocalDateTime endOfDay = date.atTime(LocalTime.MAX);
+        List<DeviceMaintenance> records = this.lambdaQuery()
+                .eq(DeviceMaintenance::getStatus, 0)
+                .ge(DeviceMaintenance::getCreateTime, startOfDay)
+                .le(DeviceMaintenance::getCreateTime, endOfDay)
+                .list();
+        if (records.isEmpty()) {
+            return AjaxResult.error("璇ユ棩鏈熶笅娌℃湁寰呭畬鎴愮殑淇濆吇璁板綍");
+        }
+        String nickName = SecurityUtils.getLoginUser().getNickName();
+        LocalDateTime now = LocalDateTime.now();
+        for (DeviceMaintenance record : records) {
+            record.setStatus(1);
+            record.setMaintenanceActuallyName(nickName);
+            record.setMaintenanceActuallyTime(now);
+        }
+        this.updateBatchById(records);
+        return AjaxResult.success("宸插畬鎴� " + records.size() + " 鏉′繚鍏昏褰�");
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public AjaxResult batchComplete(DeviceMaintenanceBatchCompleteDto dto) {
+        if (dto.getMaintenancePlanDate() == null) {
+            return AjaxResult.error("璁″垝淇濆吇鏃ユ湡涓嶈兘涓虹┖");
+        }
+        LambdaQueryWrapper<DeviceMaintenance> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(DeviceMaintenance::getStatus, 0)
+               .apply("DATE(maintenance_plan_time) = {0}", dto.getMaintenancePlanDate().toString());
+        List<DeviceMaintenance> list = this.list(wrapper);
+        if (list.isEmpty()) {
+            return AjaxResult.success("璇ユ棩鏈熶笅娌℃湁寰呬繚鍏荤殑璁板綍");
+        }
+        list.forEach(task -> {
+            task.setStatus(1);
+            if (StringUtils.isNotBlank(dto.getMaintenanceActuallyName())) {
+                task.setMaintenanceActuallyName(dto.getMaintenanceActuallyName());
+            }
+            if (dto.getMaintenanceActuallyTime() != null) {
+                task.setMaintenanceActuallyTime(dto.getMaintenanceActuallyTime());
+            }
+            if (StringUtils.isNotBlank(dto.getMaintenanceResult())) {
+                task.setMaintenanceResult(dto.getMaintenanceResult());
+            }
+        });
+        this.updateBatchById(list);
+        return AjaxResult.success("鍏卞畬鎴� " + list.size() + " 鏉′繚鍏昏褰�");
+    }
+
+    @Override
     public void export(HttpServletResponse response, Long[] ids) {
         List<DeviceMaintenance> supplierManageList = deviceMaintenanceMapper.selectList(null);
         ArrayList<DeviceMaintenanceExeclDto> deviceLedgerExeclDtos = new ArrayList<>();
diff --git a/src/main/java/com/ruoyi/home/controller/HomeController.java b/src/main/java/com/ruoyi/home/controller/HomeController.java
index 48a2185..e5f8513 100644
--- a/src/main/java/com/ruoyi/home/controller/HomeController.java
+++ b/src/main/java/com/ruoyi/home/controller/HomeController.java
@@ -103,8 +103,10 @@
     /********************************************************钀ラ攢閲囪喘绫�**************************************************/
     @GetMapping("/supplierPurchaseRanking")
     @Operation(summary = "渚涘簲鍟嗛噰璐帓鍚�")
-    public R supplierPurchaseRanking(@DefaultType Integer type) {
-        List<SupplierPurchaseRankingDto> list = homeService.supplierPurchaseRanking(type);
+    public R supplierPurchaseRanking(@DefaultType Integer type,
+            @RequestParam(required = false) String startDate,
+            @RequestParam(required = false) String endDate) {
+        List<SupplierPurchaseRankingDto> list = homeService.supplierPurchaseRanking(type, startDate, endDate);
         return R.ok(list);
     }
 
@@ -117,22 +119,28 @@
 
     @GetMapping("/customerContributionRanking")
     @Operation(summary = "瀹㈡埛閲戦璐$尞鎺掑悕")
-    public R customerContributionRanking(@DefaultType Integer type) {
-        List<CustomerContributionRankingDto> list = homeService.customerContributionRanking(type);
+    public R customerContributionRanking(@DefaultType Integer type,
+            @RequestParam(required = false) String startDate,
+            @RequestParam(required = false) String endDate) {
+        List<CustomerContributionRankingDto> list = homeService.customerContributionRanking(type, startDate, endDate);
         return R.ok(list);
     }
 
     @GetMapping("/productSalesAnalysis")
     @Operation(summary = "鍚勪骇鍝侀攢鍞噾棰濆垎鏋�")
-    public R productSalesAnalysis() {
-        List<MapDto> list = homeService.productSalesAnalysis();
+    public R productSalesAnalysis(
+            @RequestParam(required = false) String startDate,
+            @RequestParam(required = false) String endDate) {
+        List<MapDto> list = homeService.productSalesAnalysis(startDate, endDate);
         return R.ok(list);
     }
 
     @GetMapping("/rawMaterialPurchaseAmountRatio")
     @Operation(summary = "鍘熸潗鏂欓噰璐噾棰濆崰姣�")
-    public R rawMaterialPurchaseAmountRatio(){
-        List<MapDto> list = homeService.rawMaterialPurchaseAmountRatio();
+    public R rawMaterialPurchaseAmountRatio(
+            @RequestParam(required = false) String startDate,
+            @RequestParam(required = false) String endDate){
+        List<MapDto> list = homeService.rawMaterialPurchaseAmountRatio(startDate, endDate);
         return R.ok(list);
     }
 
@@ -473,22 +481,28 @@
 
     @GetMapping("/salesPurchaseStorageProductCount")
     @Operation(summary = "閿�鍞�-閲囪喘-鍌ㄥ瓨浜у搧鏁�")
-    public R salesPurchaseStorageProductCount(){
-        List<MapDto> list = homeService.salesPurchaseStorageProductCount();
+    public R salesPurchaseStorageProductCount(
+            @RequestParam(required = false) String startDate,
+            @RequestParam(required = false) String endDate){
+        List<MapDto> list = homeService.salesPurchaseStorageProductCount(startDate, endDate);
         return R.ok(list);
     }
 
     @GetMapping("/productInOutAnalysis")
     @Operation(summary = "浜у搧鍑哄叆搴撳垎鏋�")
-    public R productInOutAnalysis(@DefaultType Integer type){
-        List<Map<String, Object>> result = homeService.productInOutAnalysis(type);
+    public R productInOutAnalysis(@DefaultType Integer type,
+            @RequestParam(required = false) String startDate,
+            @RequestParam(required = false) String endDate){
+        List<Map<String, Object>> result = homeService.productInOutAnalysis(type, startDate, endDate);
         return R.ok(result);
     }
 
     @GetMapping("/productTurnoverDays")
     @Operation(summary = "浜у搧鍛ㄨ浆澶╂暟")
-    public R productTurnoverDays(){
-        List<MapDto> list = homeService.productTurnoverDays();
+    public R productTurnoverDays(
+            @RequestParam(required = false) String startDate,
+            @RequestParam(required = false) String endDate){
+        List<MapDto> list = homeService.productTurnoverDays(startDate, endDate);
         return R.ok(list);
     }
 
diff --git a/src/main/java/com/ruoyi/home/mapper/HomeMapper.java b/src/main/java/com/ruoyi/home/mapper/HomeMapper.java
index 1bf30e4..2014da6 100644
--- a/src/main/java/com/ruoyi/home/mapper/HomeMapper.java
+++ b/src/main/java/com/ruoyi/home/mapper/HomeMapper.java
@@ -2,6 +2,7 @@
 
 import com.ruoyi.dto.MapDto;
 import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
 import java.util.List;
 
 /**
@@ -17,4 +18,13 @@
      * @return 浜у搧鍛ㄨ浆澶╂暟鍒楄〃
      */
     List<MapDto> productTurnoverDays();
+
+    List<MapDto> productTurnoverDaysByDate(@Param("startDate") String startDate, @Param("endDate") String endDate);
+
+    /**
+     * 鏌ヨ閲囪喘鍝佸ぇ绫诲垎甯冿紙鍚勪骇鍝佸皬绫荤殑閲囪喘鏁伴噺/閲戦锛�
+     *
+     * @return 閲囪喘鍒嗗竷鍒楄〃
+     */
+    List<MapDto> selectPurchaseCategoryDistribution();
 }
diff --git a/src/main/java/com/ruoyi/home/service/HomeService.java b/src/main/java/com/ruoyi/home/service/HomeService.java
index 2c3c89b..ce335b2 100644
--- a/src/main/java/com/ruoyi/home/service/HomeService.java
+++ b/src/main/java/com/ruoyi/home/service/HomeService.java
@@ -38,23 +38,23 @@
 
     HomeSummaryDto summaryStatistics();
 
-    List<SupplierPurchaseRankingDto> supplierPurchaseRanking(Integer type);
+    List<SupplierPurchaseRankingDto> supplierPurchaseRanking(Integer type, String startDate, String endDate);
 
     CustomerRevenueAnalysisDto customerRevenueAnalysis(Long customerId, Integer type);
 
     ProductCategoryDistributionDto productCategoryDistribution();
 
-    List<CustomerContributionRankingDto> customerContributionRanking(Integer type);
+    List<CustomerContributionRankingDto> customerContributionRanking(Integer type, String startDate, String endDate);
 
-    List<MapDto> productSalesAnalysis();
+    List<MapDto> productSalesAnalysis(String startDate, String endDate);
 
-    List<MapDto> rawMaterialPurchaseAmountRatio();
+    List<MapDto> rawMaterialPurchaseAmountRatio(String startDate, String endDate);
 
-    List<MapDto> salesPurchaseStorageProductCount();
+    List<MapDto> salesPurchaseStorageProductCount(String startDate, String endDate);
 
-    List<Map<String, Object>> productInOutAnalysis(Integer type);
+    List<Map<String, Object>> productInOutAnalysis(Integer type, String startDate, String endDate);
 
-    List<MapDto> productTurnoverDays();
+    List<MapDto> productTurnoverDays(String startDate, String endDate);
 
     List<Map<String, Object>> incomeExpenseAnalysis(Integer type);
 
diff --git a/src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java b/src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
index 8aa7871..c24d322 100644
--- a/src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
+++ b/src/main/java/com/ruoyi/home/service/impl/HomeServiceImpl.java
@@ -697,36 +697,39 @@
     }
 
     @Override
-    public List<SupplierPurchaseRankingDto> supplierPurchaseRanking(Integer type) {
-        LocalDate today = LocalDate.now();
+    public List<SupplierPurchaseRankingDto> supplierPurchaseRanking(Integer type, String startDateStr, String endDateStr) {
         LocalDate startDate;
         LocalDate endDate;
-        switch (type) {
-            case 1: // 鍛�
-                startDate = today.with(DayOfWeek.MONDAY);
-                endDate = today.with(DayOfWeek.SUNDAY);
-                break;
-            case 2: // 鏈�
-                startDate = today.with(TemporalAdjusters.firstDayOfMonth());
-                endDate = today.with(TemporalAdjusters.lastDayOfMonth());
-                break;
-            case 3: // 瀛e害
-                Month currentMonth = today.getMonth();
-                Month firstMonthOfQuarter = currentMonth.firstMonthOfQuarter();
-                Month lastMonthOfQuarter = Month.of(firstMonthOfQuarter.getValue() + 2);
-
-                startDate = today.withMonth(firstMonthOfQuarter.getValue()).with(TemporalAdjusters.firstDayOfMonth());
-                endDate = today.withMonth(lastMonthOfQuarter.getValue()).with(TemporalAdjusters.lastDayOfMonth());
-                break;
-            default:
-                return new ArrayList<>();
+        if (com.ruoyi.common.utils.StringUtils.isNotBlank(startDateStr) && com.ruoyi.common.utils.StringUtils.isNotBlank(endDateStr)) {
+            startDate = LocalDate.parse(startDateStr);
+            endDate = LocalDate.parse(endDateStr);
+        } else {
+            LocalDate today = LocalDate.now();
+            switch (type) {
+                case 1: // 鍛�
+                    startDate = today.with(DayOfWeek.MONDAY);
+                    endDate = today.with(DayOfWeek.SUNDAY);
+                    break;
+                case 2: // 鏈�
+                    startDate = today.with(TemporalAdjusters.firstDayOfMonth());
+                    endDate = today.with(TemporalAdjusters.lastDayOfMonth());
+                    break;
+                case 3: // 瀛e害
+                    Month currentMonth = today.getMonth();
+                    Month firstMonthOfQuarter = currentMonth.firstMonthOfQuarter();
+                    Month lastMonthOfQuarter = Month.of(firstMonthOfQuarter.getValue() + 2);
+                    startDate = today.withMonth(firstMonthOfQuarter.getValue()).with(TemporalAdjusters.firstDayOfMonth());
+                    endDate = today.withMonth(lastMonthOfQuarter.getValue()).with(TemporalAdjusters.lastDayOfMonth());
+                    break;
+                default:
+                    return new ArrayList<>();
+            }
         }
 
         QueryWrapper<PurchaseLedger> queryWrapper = new QueryWrapper<>();
         queryWrapper.select("supplier_name", "SUM(contract_amount) as total_amount")
                 .ge("entry_date", startDate)
                 .le("entry_date", endDate)
-                // .ne("approval_status", 3)
                 .groupBy("supplier_name")
                 .orderByDesc("total_amount")
                 .last("LIMIT 5");
@@ -862,17 +865,81 @@
                 majorDto.setRate("0.00");
             }
 
+            // 鏌ヨ鍚勪骇鍝佸皬绫荤殑閲囪喘鏁伴噺
+            Map<String, BigDecimal> purchaseDataMap = new java.util.HashMap<>();
+            List<com.ruoyi.dto.MapDto> purchaseDistribution = homeMapper.selectPurchaseCategoryDistribution();
+            if (purchaseDistribution != null) {
+                for (com.ruoyi.dto.MapDto item : purchaseDistribution) {
+                    if (item.getName() != null && item.getValue() != null) {
+                        purchaseDataMap.put(item.getName(), new BigDecimal(item.getValue()));
+                    }
+                }
+            }
+
+            BigDecimal majorTotalChildrenValue = BigDecimal.ZERO;
             List<ProductCategoryDistributionDto.MinorCategoryDto> minorDtos = new ArrayList<>();
             for (Product minor : children) {
                 ProductCategoryDistributionDto.MinorCategoryDto minorDto = new ProductCategoryDistributionDto.MinorCategoryDto();
                 minorDto.setName(minor.getProductName());
-                minorDto.setValue("0");
+                BigDecimal minorValue = purchaseDataMap.getOrDefault(minor.getProductName(), BigDecimal.ZERO);
+                minorDto.setValue(minorValue.toString());
                 minorDto.setRate("0.00");
                 minorDto.setChildren(new ArrayList<>());
                 minorDtos.add(minorDto);
+                majorTotalChildrenValue = majorTotalChildrenValue.add(minorValue);
             }
             majorDto.setChildren(minorDtos);
+            // 浣跨敤瀹為檯閲囪喘鏁版嵁鏇存柊澶х被鐨� value
+            if (majorTotalChildrenValue.compareTo(BigDecimal.ZERO) > 0) {
+                majorDto.setValue(majorTotalChildrenValue.toString());
+            }
             majorItems.add(majorDto);
+        }
+
+        // 璁$畻澶х被鍗犳瘮
+        BigDecimal totalAllValue = BigDecimal.ZERO;
+        for (ProductCategoryDistributionDto.MajorCategoryDto m : majorItems) {
+            try {
+                totalAllValue = totalAllValue.add(new BigDecimal(m.getValue()));
+            } catch (NumberFormatException ignored) {
+            }
+        }
+        if (totalAllValue.compareTo(BigDecimal.ZERO) > 0) {
+            for (ProductCategoryDistributionDto.MajorCategoryDto m : majorItems) {
+                try {
+                    BigDecimal val = new BigDecimal(m.getValue());
+                    m.setRate(val.divide(totalAllValue, 4, RoundingMode.HALF_UP)
+                            .multiply(new BigDecimal("100"))
+                            .setScale(2, RoundingMode.HALF_UP)
+                            .toString());
+                } catch (NumberFormatException ignored) {
+                }
+            }
+        }
+        // 璁$畻灏忕被鍗犳瘮锛堝湪鍚屽ぇ绫诲唴锛�
+        for (ProductCategoryDistributionDto.MajorCategoryDto m : majorItems) {
+            if (m.getChildren() == null || m.getChildren().isEmpty()) {
+                continue;
+            }
+            BigDecimal parentVal = BigDecimal.ZERO;
+            for (ProductCategoryDistributionDto.MinorCategoryDto child : m.getChildren()) {
+                try {
+                    parentVal = parentVal.add(new BigDecimal(child.getValue()));
+                } catch (NumberFormatException ignored) {
+                }
+            }
+            if (parentVal.compareTo(BigDecimal.ZERO) > 0) {
+                for (ProductCategoryDistributionDto.MinorCategoryDto child : m.getChildren()) {
+                    try {
+                        BigDecimal childVal = new BigDecimal(child.getValue());
+                        child.setRate(childVal.divide(parentVal, 4, RoundingMode.HALF_UP)
+                                .multiply(new BigDecimal("100"))
+                                .setScale(2, RoundingMode.HALF_UP)
+                                .toString());
+                    } catch (NumberFormatException ignored) {
+                    }
+                }
+            }
         }
 
         dto.setItems(majorItems);
@@ -880,38 +947,42 @@
     }
 
     @Override
-    public List<CustomerContributionRankingDto> customerContributionRanking(Integer type) {
-        LocalDate today = LocalDate.now();
-        LocalDate startDate = null;
-        LocalDate endDate = null;
-        switch (type) {
-            case 1:
-                startDate = today.with(DayOfWeek.MONDAY);
-                endDate = today.with(DayOfWeek.SUNDAY);
-                break;
-            case 2:
-                startDate = today.with(TemporalAdjusters.firstDayOfMonth());
-                endDate = today.with(TemporalAdjusters.lastDayOfMonth());
-                break;
-            case 3:
-                Month currentMonth = today.getMonth();
-                Month firstMonthOfQuarter = currentMonth.firstMonthOfQuarter();
-                Month lastMonthOfQuarter = Month.of(firstMonthOfQuarter.getValue() + 2);
-                startDate = today.withMonth(firstMonthOfQuarter.getValue()).with(TemporalAdjusters.firstDayOfMonth());
-                endDate = today.withMonth(lastMonthOfQuarter.getValue()).with(TemporalAdjusters.lastDayOfMonth());
-                break;
+    public List<CustomerContributionRankingDto> customerContributionRanking(Integer type, String startDateStr, String endDateStr) {
+        LocalDate startDate;
+        LocalDate endDate;
+        if (com.ruoyi.common.utils.StringUtils.isNotBlank(startDateStr) && com.ruoyi.common.utils.StringUtils.isNotBlank(endDateStr)) {
+            startDate = LocalDate.parse(startDateStr);
+            endDate = LocalDate.parse(endDateStr);
+        } else {
+            LocalDate today = LocalDate.now();
+            switch (type) {
+                case 1:
+                    startDate = today.with(DayOfWeek.MONDAY);
+                    endDate = today.with(DayOfWeek.SUNDAY);
+                    break;
+                case 2:
+                    startDate = today.with(TemporalAdjusters.firstDayOfMonth());
+                    endDate = today.with(TemporalAdjusters.lastDayOfMonth());
+                    break;
+                case 3:
+                    Month currentMonth = today.getMonth();
+                    Month firstMonthOfQuarter = currentMonth.firstMonthOfQuarter();
+                    Month lastMonthOfQuarter = Month.of(firstMonthOfQuarter.getValue() + 2);
+                    startDate = today.withMonth(firstMonthOfQuarter.getValue()).with(TemporalAdjusters.firstDayOfMonth());
+                    endDate = today.withMonth(lastMonthOfQuarter.getValue()).with(TemporalAdjusters.lastDayOfMonth());
+                    break;
+                default:
+                    return new ArrayList<>();
+            }
         }
 
         QueryWrapper<SalesLedger> queryWrapper = new QueryWrapper<>();
         queryWrapper.select("customer_name", "SUM(contract_amount) as total_amount")
                 .isNotNull("customer_name")
+                .between("entry_date", startDate, endDate)
                 .groupBy("customer_name")
                 .orderByDesc("total_amount")
                 .last("LIMIT 5");
-
-        if (startDate != null && endDate != null) {
-            queryWrapper.between("entry_date", startDate, endDate);
-        }
 
         List<Map<String, Object>> maps = salesLedgerMapper.selectMaps(queryWrapper);
         List<CustomerContributionRankingDto> result = new ArrayList<>();
@@ -927,9 +998,13 @@
     }
 
     @Override
-    public List<MapDto> productSalesAnalysis() {
-        // 鑾峰彇鎵�鏈変骇鍝佸ぇ绫荤殑閿�鍞
-        List<Map<String, Object>> analysisResults = salesLedgerProductMapper.selectProductSalesAnalysis();
+    public List<MapDto> productSalesAnalysis(String startDateStr, String endDateStr) {
+        List<Map<String, Object>> analysisResults;
+        if (com.ruoyi.common.utils.StringUtils.isNotBlank(startDateStr) && com.ruoyi.common.utils.StringUtils.isNotBlank(endDateStr)) {
+            analysisResults = salesLedgerProductMapper.selectProductSalesAnalysisByDate(startDateStr, endDateStr);
+        } else {
+            analysisResults = salesLedgerProductMapper.selectProductSalesAnalysis();
+        }
         if (CollectionUtils.isEmpty(analysisResults)) {
             return new ArrayList<>();
         }
@@ -964,9 +1039,13 @@
     }
 
     @Override
-    public List<MapDto> rawMaterialPurchaseAmountRatio() {
-        // 鑾峰彇鎵�鏈夊師鏉愭枡鐨勯噰璐
-        List<Map<String, Object>> analysisResults = salesLedgerProductMapper.selectRawMaterialPurchaseAnalysis();
+    public List<MapDto> rawMaterialPurchaseAmountRatio(String startDateStr, String endDateStr) {
+        List<Map<String, Object>> analysisResults;
+        if (com.ruoyi.common.utils.StringUtils.isNotBlank(startDateStr) && com.ruoyi.common.utils.StringUtils.isNotBlank(endDateStr)) {
+            analysisResults = salesLedgerProductMapper.selectRawMaterialPurchaseAnalysisByDate(startDateStr, endDateStr);
+        } else {
+            analysisResults = salesLedgerProductMapper.selectRawMaterialPurchaseAnalysis();
+        }
         if (CollectionUtils.isEmpty(analysisResults)) {
             return new ArrayList<>();
         }
@@ -1001,25 +1080,47 @@
     }
 
     @Override
-    public List<MapDto> salesPurchaseStorageProductCount() {
+    public List<MapDto> salesPurchaseStorageProductCount(String startDate, String endDate) {
         LocalDateTime now = LocalDateTime.now();
-        LocalDateTime currentMonthStart = now.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
-        LocalDateTime lastMonth = now.minusMonths(1);
-        LocalDateTime lastMonthStart = lastMonth.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
-        LocalDateTime lastMonthEnd = lastMonth.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
+        LocalDateTime currentMonthStart;
+        LocalDateTime currentMonthEnd;
+        LocalDateTime lastMonthStart;
+        LocalDateTime lastMonthEnd;
 
-        //  閿�鍞�
-        int currentSales = salesLedgerProductMapper.selectProductCountByTypeAndDate(1, currentMonthStart, now);
-        int lastSales = salesLedgerProductMapper.selectProductCountByTypeAndDate(1, lastMonthStart, lastMonthEnd);
-        //  閲囪喘
-        int currentPurchase = salesLedgerProductMapper.selectProductCountByTypeAndDate(2, currentMonthStart, now);
+        if (StringUtils.isNotBlank(startDate) && StringUtils.isNotBlank(endDate)) {
+            LocalDate start = LocalDate.parse(startDate);
+            LocalDate end = LocalDate.parse(endDate);
+            currentMonthStart = start.atStartOfDay();
+            currentMonthEnd = end.atTime(LocalTime.MAX);
+            long daysBetween = java.time.temporal.ChronoUnit.DAYS.between(start, end) + 1;
+            LocalDate lastStart = start.minusDays(daysBetween);
+            LocalDate lastEnd = start.minusDays(1);
+            lastMonthStart = lastStart.atStartOfDay();
+            lastMonthEnd = lastEnd.atTime(LocalTime.MAX);
+        } else {
+            currentMonthStart = now.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
+            currentMonthEnd = now;
+            LocalDateTime lastMonth = now.minusMonths(1);
+            lastMonthStart = lastMonth.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN);
+            lastMonthEnd = lastMonth.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX);
+        }
+
+        // 閿�鍞鍗曟暟锛堢粺璁ales_ledger琛級
+        int currentSales = Math.toIntExact(salesLedgerMapper.selectCount(new LambdaQueryWrapper<SalesLedger>()
+                .ge(SalesLedger::getEntryDate, currentMonthStart)
+                .le(SalesLedger::getEntryDate, currentMonthEnd)));
+        int lastSales = Math.toIntExact(salesLedgerMapper.selectCount(new LambdaQueryWrapper<SalesLedger>()
+                .ge(SalesLedger::getEntryDate, lastMonthStart)
+                .le(SalesLedger::getEntryDate, lastMonthEnd)));
+        // 閲囪喘浜у搧鏁�
+        int currentPurchase = salesLedgerProductMapper.selectProductCountByTypeAndDate(2, currentMonthStart, currentMonthEnd);
         int lastPurchase = salesLedgerProductMapper.selectProductCountByTypeAndDate(2, lastMonthStart, lastMonthEnd);
-        //  鍌ㄥ瓨
-        int currentStorage = stockInventoryMapper.selectStorageProductCountByDate(currentMonthStart, now);
+        // 鍌ㄥ瓨浜у搧鏁�
+        int currentStorage = stockInventoryMapper.selectStorageProductCountByDate(currentMonthStart, currentMonthEnd);
         int lastStorage = stockInventoryMapper.selectStorageProductCountByDate(lastMonthStart, lastMonthEnd);
 
         List<MapDto> list = new ArrayList<>();
-        list.add(createMapDto("閿�鍞骇鍝佹暟", currentSales, lastSales));
+        list.add(createMapDto("閿�鍞鍗曟暟", currentSales, lastSales));
         list.add(createMapDto("閲囪喘浜у搧鏁�", currentPurchase, lastPurchase));
         list.add(createMapDto("鍌ㄥ瓨浜у搧鏁�", currentStorage, lastStorage));
 
@@ -1048,7 +1149,7 @@
     }
 
     @Override
-    public List<Map<String, Object>> productInOutAnalysis(Integer type) {
+    public List<Map<String, Object>> productInOutAnalysis(Integer type, String startDateStr, String endDateStr) {
         String targetName;
         if (type == 1) {
             targetName = "鍘熸潗鏂�";
@@ -1073,8 +1174,17 @@
         LocalDate now = LocalDate.now();
         DateTimeFormatter displayDtf = DateTimeFormatter.ofPattern("M/d");
 
-        String startDate = now.minusDays(6).toString();
-        String endDate = now.toString();
+        LocalDate start;
+        LocalDate end;
+        if (com.ruoyi.common.utils.StringUtils.isNotBlank(startDateStr) && com.ruoyi.common.utils.StringUtils.isNotBlank(endDateStr)) {
+            start = LocalDate.parse(startDateStr);
+            end = LocalDate.parse(endDateStr);
+        } else {
+            end = now;
+            start = now.minusDays(6);
+        }
+        String startDate = start.toString();
+        String endDate = end.toString();
 
         List<Map<String, Object>> inCounts = stockInventoryMapper.selectDailyStockInCounts(rootCategoryId, startDate,
                 endDate + " 23:59:59");
@@ -1111,7 +1221,10 @@
     }
 
     @Override
-    public List<MapDto> productTurnoverDays() {
+    public List<MapDto> productTurnoverDays(String startDateStr, String endDateStr) {
+        if (com.ruoyi.common.utils.StringUtils.isNotBlank(startDateStr) && com.ruoyi.common.utils.StringUtils.isNotBlank(endDateStr)) {
+            return homeMapper.productTurnoverDaysByDate(startDateStr, endDateStr);
+        }
         return homeMapper.productTurnoverDays();
     }
 
diff --git a/src/main/java/com/ruoyi/inspectiontask/controller/InspectionTaskController.java b/src/main/java/com/ruoyi/inspectiontask/controller/InspectionTaskController.java
index be8dda5..42d2d4e 100644
--- a/src/main/java/com/ruoyi/inspectiontask/controller/InspectionTaskController.java
+++ b/src/main/java/com/ruoyi/inspectiontask/controller/InspectionTaskController.java
@@ -15,12 +15,6 @@
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.bind.annotation.*;
 
-import java.io.IOException;
-
-/**
- * @author :yys
- * @date : 2025/9/19 10:52
- */
 @RestController
 @Tag(name = "宸℃浠诲姟璁板綍")
 @RequestMapping("/inspectionTask")
@@ -29,20 +23,13 @@
 
     private InspectionTaskService inspectionTaskService;
 
-    /**
-     * 宸℃浠诲姟琛ㄨ〃鏌ヨ
-     */
     @GetMapping("/list")
-    @Operation(summary = "宸℃浠诲姟琛ㄨ〃鏌ヨ")
+    @Operation(summary = "宸℃浠诲姟琛ㄦ煡璇�")
     public R<IPage<InspectionTaskDto>> list(Page<InspectionTask> page, InspectionTaskDto inspectionTaskDto) {
         IPage<InspectionTaskDto> list = inspectionTaskService.selectInspectionTaskList(page, inspectionTaskDto);
         return R.ok(list);
     }
 
-
-    /**
-     * 瀵煎嚭
-     */
     @PostMapping("/export")
     @Operation(summary = "瀵煎嚭瀹氭椂浠诲姟璁板綍")
     public void export(HttpServletResponse response) {
@@ -53,9 +40,6 @@
         util.exportExcel(response, list.getRecords(), "瀵煎嚭瀹氭椂浠诲姟璁板綍");
     }
 
-    /**
-     * 宸℃浠诲姟琛ㄦ柊澧炰慨鏀�
-     */
     @PostMapping("/addOrEditInspectionTask")
     @Operation(summary = "宸℃浠诲姟琛ㄦ柊澧炰慨鏀�")
     @Transactional(rollbackFor = Exception.class)
@@ -63,9 +47,6 @@
         return R.ok(inspectionTaskService.addOrEditInspectionTask(inspectionTaskDto));
     }
 
-    /**
-     * 宸℃浠诲姟琛ㄥ垹闄�
-     */
     @DeleteMapping("/delInspectionTask")
     @Operation(summary = "宸℃浠诲姟琛ㄥ垹闄�")
     @Transactional(rollbackFor = Exception.class)
@@ -73,4 +54,18 @@
         return R.ok(inspectionTaskService.delByIds(ids));
     }
 
+    @PutMapping("/complete/{id}")
+    @Operation(summary = "宸℃瀹屾垚")
+    @Transactional(rollbackFor = Exception.class)
+    public R complete(@PathVariable Long id) {
+        return R.ok(inspectionTaskService.complete(id));
+    }
+
+    @PutMapping("/batchComplete")
+    @Operation(summary = "涓�閿贰妫�瀹屾垚")
+    @Transactional(rollbackFor = Exception.class)
+    public R batchComplete(String dateStr) {
+        return R.ok(inspectionTaskService.batchComplete(dateStr));
+    }
+
 }
diff --git a/src/main/java/com/ruoyi/inspectiontask/pojo/InspectionTask.java b/src/main/java/com/ruoyi/inspectiontask/pojo/InspectionTask.java
index 4e7efad..d5b265a 100644
--- a/src/main/java/com/ruoyi/inspectiontask/pojo/InspectionTask.java
+++ b/src/main/java/com/ruoyi/inspectiontask/pojo/InspectionTask.java
@@ -82,6 +82,9 @@
     @Schema(description = "宸℃鍦扮偣璇︾粏鎻忚堪")
     private String inspectionLocation;
 
+    @Schema(description = "鐘舵�� 0=寰呭贰妫�, 1=宸插畬鎴�")
+    private Integer status;
+
     @Schema(description = "杞垹闄ゆ爣蹇楋紝0=鏈垹闄わ紝1=宸插垹闄�")
     private Integer deleted;
 
diff --git a/src/main/java/com/ruoyi/inspectiontask/service/InspectionTaskService.java b/src/main/java/com/ruoyi/inspectiontask/service/InspectionTaskService.java
index de175f2..23d4b62 100644
--- a/src/main/java/com/ruoyi/inspectiontask/service/InspectionTaskService.java
+++ b/src/main/java/com/ruoyi/inspectiontask/service/InspectionTaskService.java
@@ -6,10 +6,6 @@
 import com.ruoyi.inspectiontask.dto.InspectionTaskDto;
 import com.ruoyi.inspectiontask.pojo.InspectionTask;
 
-/**
- * @author :yys
- * @date : 2025/9/19 10:49
- */
 public interface InspectionTaskService extends IService<InspectionTask> {
 
     IPage<InspectionTaskDto> selectInspectionTaskList(Page<InspectionTask> page, InspectionTaskDto inspectionTaskDto);
@@ -17,4 +13,8 @@
     int addOrEditInspectionTask(InspectionTaskDto inspectionTaskDto);
 
     int delByIds(Long[] ids);
+
+    int complete(Long id);
+
+    int batchComplete(String dateStr);
 }
diff --git a/src/main/java/com/ruoyi/inspectiontask/service/impl/InspectionTaskServiceImpl.java b/src/main/java/com/ruoyi/inspectiontask/service/impl/InspectionTaskServiceImpl.java
index 9729b40..3335de3 100644
--- a/src/main/java/com/ruoyi/inspectiontask/service/impl/InspectionTaskServiceImpl.java
+++ b/src/main/java/com/ruoyi/inspectiontask/service/impl/InspectionTaskServiceImpl.java
@@ -187,9 +187,63 @@
         // 淇濆瓨鏂囦欢锛堝瓧娈典笉浼犲垯淇濈暀鍘嗗彶锛�
         saveInspectionAttachments(inspectionTask.getId(), inspectionTaskDto);
 
+        // 宸℃缁撴灉璁剧疆鍚庯紝鑷姩鏍囪涓哄凡瀹屾垚
+        if (StringUtils.isNotBlank(inspectionTaskDto.getInspectionResult())) {
+            InspectionTask statusUpdate = new InspectionTask();
+            statusUpdate.setId(inspectionTask.getId());
+            statusUpdate.setStatus(1);
+            inspectionTaskMapper.updateById(statusUpdate);
+        }
+
         return i;
     }
 
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int complete(Long id) {
+        InspectionTask task = inspectionTaskMapper.selectById(id);
+        if (task == null) {
+            throw new IllegalArgumentException("宸℃浠诲姟涓嶅瓨鍦�");
+        }
+        if (task.getStatus() != null && task.getStatus() == 1) {
+            throw new IllegalArgumentException("璇ュ贰妫�浠诲姟宸插畬鎴�");
+        }
+        task.setStatus(1);
+        if (task.getInspectionResult() == null) {
+            task.setInspectionResult("1");
+        }
+        return inspectionTaskMapper.updateById(task);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public int batchComplete(String dateStr) {
+        if (StringUtils.isBlank(dateStr)) {
+            throw new IllegalArgumentException("鏃ユ湡鍙傛暟涓嶈兘涓虹┖");
+        }
+        java.time.LocalDate date = java.time.LocalDate.parse(dateStr);
+        java.time.LocalDateTime startOfDay = date.atStartOfDay();
+        java.time.LocalDateTime endOfDay = date.atTime(java.time.LocalTime.MAX);
+        List<InspectionTask> tasks = inspectionTaskMapper.selectList(
+                new LambdaQueryWrapper<InspectionTask>()
+                        .eq(InspectionTask::getStatus, 0)
+                        .ge(InspectionTask::getCreateTime, startOfDay)
+                        .le(InspectionTask::getCreateTime, endOfDay));
+        if (tasks.isEmpty()) {
+            throw new IllegalArgumentException("璇ユ棩鏈熶笅娌℃湁寰呭畬鎴愮殑宸℃璁板綍");
+        }
+        int count = 0;
+        for (InspectionTask task : tasks) {
+            task.setStatus(1);
+            if (task.getInspectionResult() == null) {
+                task.setInspectionResult("1");
+            }
+            inspectionTaskMapper.updateById(task);
+            count++;
+        }
+        return count;
+    }
+
     private void validateInspectionInput(InspectionTaskDto inspectionTaskDto, InspectionTask oldInspectionTask) {
         String inspectionResult = inspectionTaskDto.getInspectionResult();
         if (StringUtils.isBlank(inspectionResult) && oldInspectionTask != null) {
diff --git a/src/main/java/com/ruoyi/measuringinstrumentledger/controller/SparePartsRequisitionRecordController.java b/src/main/java/com/ruoyi/measuringinstrumentledger/controller/SparePartsRequisitionRecordController.java
index 26195a4..ddebfa1 100644
--- a/src/main/java/com/ruoyi/measuringinstrumentledger/controller/SparePartsRequisitionRecordController.java
+++ b/src/main/java/com/ruoyi/measuringinstrumentledger/controller/SparePartsRequisitionRecordController.java
@@ -3,23 +3,18 @@
 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.measuringinstrumentledger.dto.SparePartsOutboundDto;
 import com.ruoyi.measuringinstrumentledger.dto.SparePartsRequisitionRecordDto;
 import com.ruoyi.measuringinstrumentledger.service.SparePartsRequisitionRecordService;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import io.swagger.v3.oas.annotations.Operation;
 import lombok.AllArgsConstructor;
 import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
-/**
- * <p>
- *  鍓嶇鎺у埗鍣�
- * </p>
- *
- * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
- * @since 2026-04-02 03:59:56
- */
 @RestController
 @RequestMapping("/sparePartsRequisitionRecord")
 @Tag(name = "澶囦欢棰嗙敤璁板綍鎺ュ彛")
@@ -28,9 +23,16 @@
     private SparePartsRequisitionRecordService sparePartsRequisitionRecordService;
 
     @GetMapping("/listPage")
-    @Operation(summary = "澶囦欢鍒嗙被-鍒嗛〉鏌ヨ")
+    @Operation(summary = "澶囦欢棰嗙敤璁板綍-鍒嗛〉鏌ヨ")
     public AjaxResult listPage(Page page, SparePartsRequisitionRecordDto sparePartsRequisitionRecordDto){
         IPage<SparePartsRequisitionRecordDto> listPage = sparePartsRequisitionRecordService.listPage(page, sparePartsRequisitionRecordDto);
         return AjaxResult.success(listPage);
     }
+
+    @PostMapping("/outbound")
+    @Operation(summary = "澶囦欢鐙珛鍑哄簱")
+    public AjaxResult outbound(@RequestBody SparePartsOutboundDto dto){
+        sparePartsRequisitionRecordService.outbound(dto);
+        return AjaxResult.success();
+    }
 }
diff --git a/src/main/java/com/ruoyi/measuringinstrumentledger/dto/SparePartsOutboundDto.java b/src/main/java/com/ruoyi/measuringinstrumentledger/dto/SparePartsOutboundDto.java
new file mode 100644
index 0000000..fbd62fa
--- /dev/null
+++ b/src/main/java/com/ruoyi/measuringinstrumentledger/dto/SparePartsOutboundDto.java
@@ -0,0 +1,21 @@
+package com.ruoyi.measuringinstrumentledger.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@Schema(name = "澶囦欢鍑哄簱璇锋眰")
+public class SparePartsOutboundDto {
+
+    @Schema(description = "澶囦欢ID", requiredMode = Schema.RequiredMode.REQUIRED)
+    private Long sparePartsId;
+
+    @Schema(description = "鍑哄簱鏁伴噺", requiredMode = Schema.RequiredMode.REQUIRED)
+    private Integer quantity;
+
+    @Schema(description = "璁惧ID锛堝彲閫夛級")
+    private Long deviceLedgerId;
+
+    @Schema(description = "澶囨敞锛堝彲閫夛級")
+    private String remark;
+}
diff --git a/src/main/java/com/ruoyi/measuringinstrumentledger/service/SparePartsRequisitionRecordService.java b/src/main/java/com/ruoyi/measuringinstrumentledger/service/SparePartsRequisitionRecordService.java
index 2306a3a..38a681a 100644
--- a/src/main/java/com/ruoyi/measuringinstrumentledger/service/SparePartsRequisitionRecordService.java
+++ b/src/main/java/com/ruoyi/measuringinstrumentledger/service/SparePartsRequisitionRecordService.java
@@ -3,17 +3,12 @@
 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.measuringinstrumentledger.dto.SparePartsOutboundDto;
 import com.ruoyi.measuringinstrumentledger.dto.SparePartsRequisitionRecordDto;
 import com.ruoyi.measuringinstrumentledger.pojo.SparePartsRequisitionRecord;
 
-/**
- * <p>
- *  鏈嶅姟绫�
- * </p>
- *
- * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
- * @since 2026-04-02 03:59:56
- */
 public interface SparePartsRequisitionRecordService extends IService<SparePartsRequisitionRecord> {
     IPage<SparePartsRequisitionRecordDto> listPage(Page page, SparePartsRequisitionRecordDto sparePartsRequisitionRecordDto);
+
+    void outbound(SparePartsOutboundDto dto);
 }
diff --git a/src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/SparePartsRequisitionRecordServiceImpl.java b/src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/SparePartsRequisitionRecordServiceImpl.java
index 19746f5..4942ef9 100644
--- a/src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/SparePartsRequisitionRecordServiceImpl.java
+++ b/src/main/java/com/ruoyi/measuringinstrumentledger/service/impl/SparePartsRequisitionRecordServiceImpl.java
@@ -4,35 +4,58 @@
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.ruoyi.common.enums.SparePartsRequisitionRecordSourceTypeEnum;
+import com.ruoyi.measuringinstrumentledger.dto.SparePartsOutboundDto;
 import com.ruoyi.measuringinstrumentledger.dto.SparePartsRequisitionRecordDto;
+import com.ruoyi.measuringinstrumentledger.mapper.SparePartsMapper;
 import com.ruoyi.measuringinstrumentledger.mapper.SparePartsRequisitionRecordMapper;
+import com.ruoyi.measuringinstrumentledger.pojo.SpareParts;
 import com.ruoyi.measuringinstrumentledger.pojo.SparePartsRequisitionRecord;
 import com.ruoyi.measuringinstrumentledger.service.SparePartsRequisitionRecordService;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
-/**
- * <p>
- * 鏈嶅姟瀹炵幇绫�
- * </p>
- *
- * @author 鑺杞欢锛堟睙鑻忥級鏈夐檺鍏徃
- * @since 2026-04-02 03:59:56
- */
+import java.math.BigDecimal;
+
 @Service
 @RequiredArgsConstructor
 public class SparePartsRequisitionRecordServiceImpl extends ServiceImpl<SparePartsRequisitionRecordMapper, SparePartsRequisitionRecord> implements SparePartsRequisitionRecordService {
     private final SparePartsRequisitionRecordMapper sparePartsRequisitionRecordMapper;
+    private final SparePartsMapper sparePartsMapper;
 
     @Override
     public IPage<SparePartsRequisitionRecordDto> listPage(Page page, SparePartsRequisitionRecordDto sparePartsRequisitionRecordDto) {
         IPage<SparePartsRequisitionRecordDto> result = sparePartsRequisitionRecordMapper.listPage(page, sparePartsRequisitionRecordDto);
-        // 澶勭悊鏉ユ簮绫诲瀷鏂囨湰
         result.getRecords().forEach(record -> {
             if (record.getSourceType() != null) {
-                record.setSourceText(SparePartsRequisitionRecordSourceTypeEnum.getByCode(record.getSourceType()).getValue());
+                SparePartsRequisitionRecordSourceTypeEnum typeEnum = SparePartsRequisitionRecordSourceTypeEnum.getByCode(record.getSourceType());
+                if (typeEnum != null) {
+                    record.setSourceText(typeEnum.getValue());
+                }
             }
         });
         return result;
     }
-}
\ No newline at end of file
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void outbound(SparePartsOutboundDto dto) {
+        SpareParts spareParts = sparePartsMapper.selectById(dto.getSparePartsId());
+        if (spareParts == null) {
+            throw new IllegalArgumentException("澶囦欢涓嶅瓨鍦�");
+        }
+        if (spareParts.getQuantity().compareTo(new BigDecimal(dto.getQuantity())) < 0) {
+            throw new IllegalArgumentException("澶囦欢 " + spareParts.getName() + " 搴撳瓨涓嶈冻");
+        }
+        // 鎵e噺搴撳瓨
+        spareParts.setQuantity(spareParts.getQuantity().subtract(new BigDecimal(dto.getQuantity())));
+        sparePartsMapper.updateById(spareParts);
+        // 鍒涘缓棰嗙敤璁板綍
+        SparePartsRequisitionRecord record = new SparePartsRequisitionRecord();
+        record.setSourceType(SparePartsRequisitionRecordSourceTypeEnum.SparePartsRequisitionRecordSourceTypeDirectOutbound.getCode());
+        record.setSparePartsId(dto.getSparePartsId());
+        record.setQuantity(dto.getQuantity());
+        record.setDeviceLedgerId(dto.getDeviceLedgerId());
+        this.save(record);
+    }
+}
diff --git a/src/main/java/com/ruoyi/purchase/controller/AccountingReportController.java b/src/main/java/com/ruoyi/purchase/controller/AccountingReportController.java
index 798112b..bc2aa48 100644
--- a/src/main/java/com/ruoyi/purchase/controller/AccountingReportController.java
+++ b/src/main/java/com/ruoyi/purchase/controller/AccountingReportController.java
@@ -1,12 +1,15 @@
 package com.ruoyi.purchase.controller;
 
+import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ruoyi.basic.service.ISupplierService;
 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.domain.AjaxResult;
 import com.ruoyi.framework.web.domain.R;
 import com.ruoyi.purchase.dto.VatDto;
+import com.ruoyi.purchase.dto.VatSummaryDto;
 import com.ruoyi.purchase.service.PurchaseReportService;
 import com.ruoyi.purchase.vo.PurchaseReportVo;
 import io.swagger.v3.oas.annotations.Operation;
@@ -29,49 +32,62 @@
     private final ISupplierService supplierService;
     private final PurchaseReportService purchaseReportService;
 
-
     @GetMapping("/list")
     @Log(title = "閲囪喘鎶ヨ〃-椤圭洰鍒╂鼎", businessType = BusinessType.OTHER)
     public R list(Page page, String customerName) {
-        return R.ok(purchaseReportService.list(page,customerName));
+        return R.ok(purchaseReportService.list(page, customerName));
     }
 
     @Log(title = "閲囪喘鎶ヨ〃-椤圭洰鍒╂鼎瀵煎嚭", businessType = BusinessType.EXPORT)
     @PostMapping("/export")
     @Operation(summary = "閲囪喘鎶ヨ〃-椤圭洰鍒╂鼎瀵煎嚭")
     public void export(HttpServletResponse response, String customerName) {
-        List<PurchaseReportVo> list = purchaseReportService.list(new Page(1,-1),customerName).getRecords();
+        List<PurchaseReportVo> list = purchaseReportService.list(new Page(1, -1), customerName).getRecords();
         ExcelUtil<PurchaseReportVo> util = new ExcelUtil<>(PurchaseReportVo.class);
-        util.exportExcel(response, list , "椤圭洰鍒╂鼎");
-
+        util.exportExcel(response, list, "椤圭洰鍒╂鼎");
     }
 
     @Log(title = "閲囪喘鎶ヨ〃-澧炲�肩◣姣斿", businessType = BusinessType.OTHER)
     @GetMapping("/listVat")
-    public R listVat(Page page,String month) {
-        return R.ok(purchaseReportService.listVat(page,month));
+    public R listVat(Page page, String month, String type) {
+        return R.ok(purchaseReportService.listVat(page, month, type));
     }
 
     @Log(title = "閲囪喘鎶ヨ〃-澧炲�肩◣姣斿", businessType = BusinessType.EXPORT)
     @PostMapping("/exportTwo")
-    @Operation(summary = "閲囪喘鎶ヨ〃-澧炲�肩◣姣斿")
-    public void exportTwo(HttpServletResponse response,String month) {
-        List<VatDto> list = purchaseReportService.listVat(new Page(1,-1),month).getRecords();
+    @Operation(summary = "閲囪喘鎶ヨ〃-澧炲�肩◣姣斿瀵煎嚭")
+    public void exportTwo(HttpServletResponse response, String month) {
+        List<VatDto> list = purchaseReportService.listVat(new Page(1, -1), month, null).getRecords();
         ExcelUtil<VatDto> util = new ExcelUtil<>(VatDto.class);
-        util.exportExcel(response, list , "澧炲�肩◣姣斿");
+        util.exportExcel(response, list, "澧炲�肩◣姣斿");
+    }
+
+    @Log(title = "閲囪喘鎶ヨ〃-澧炲�肩◣鍥捐〃鏁版嵁", businessType = BusinessType.OTHER)
+    @GetMapping("/vatChart")
+    @Operation(summary = "澧炲�肩◣杩涢攢椤瑰姣斿浘琛ㄦ暟鎹�")
+    public R vatChart(String year) {
+        List<VatSummaryDto> list = purchaseReportService.vatSummary(year);
+        return R.ok(list);
+    }
+
+    @GetMapping("/listVatDetail")
+    @Operation(summary = "澧炲�肩◣鏄庣粏-鎸夊彂绁�/璁㈠崟灞曠ず")
+    public AjaxResult listVatDetail(Page page, String month) {
+        IPage<VatDto> result = purchaseReportService.listVatDetail(page, month);
+        return AjaxResult.success(result);
     }
 
     @GetMapping("/supplierTransactions")
     @Log(title = "渚涘簲鍟嗗線鏉�", businessType = BusinessType.OTHER)
     @Operation(summary = "渚涘簲鍟嗗線鏉�")
     public R supplierTransactions(Page page, String supplierName) {
-        return R.ok(supplierService.supplierTransactions(page,supplierName));
+        return R.ok(supplierService.supplierTransactions(page, supplierName));
     }
 
     @GetMapping("/supplierTransactionsDetails")
     @Log(title = "渚涘簲鍟嗗線鏉ユ槑缁�", businessType = BusinessType.OTHER)
     @Operation(summary = "渚涘簲鍟嗗線鏉ユ槑缁�")
     public R supplierTransactionsDetails(Page page, Long supplierId) {
-        return R.ok(supplierService.supplierTransactionsDetails(page,supplierId));
+        return R.ok(supplierService.supplierTransactionsDetails(page, supplierId));
     }
 }
diff --git a/src/main/java/com/ruoyi/purchase/dto/VatDto.java b/src/main/java/com/ruoyi/purchase/dto/VatDto.java
index 3564183..62ceff2 100644
--- a/src/main/java/com/ruoyi/purchase/dto/VatDto.java
+++ b/src/main/java/com/ruoyi/purchase/dto/VatDto.java
@@ -1,19 +1,59 @@
 package com.ruoyi.purchase.dto;
 
 import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+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 java.math.BigDecimal;
+import java.time.LocalDate;
+
 @Data
-@Schema(name = "VatDto", description = "绠$悊椹鹃┒鑸�--澧炲�肩◣姣斿鍙傛暟")
+@Schema(name = "VatDto", description = "澧炲�肩◣姣斿鏄庣粏")
 @ExcelIgnoreUnannotated
 public class VatDto {
 
+    @Excel(name = "ID")
+    @Schema(description = "鍙戠エID")
+    private Long id;
+
+    @Excel(name = "鍙戠エ鍙风爜")
+    @Schema(description = "鍙戠エ鍙风爜")
+    private String invoiceNumber;
+
+    @Excel(name = "寮�绁ㄦ棩鏈�")
+    @Schema(description = "寮�绁ㄦ棩鏈�")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate issueDate;
+
+    @Excel(name = "绫诲瀷")
+    @Schema(description = "绫诲瀷 sales-閿�椤� purchase-杩涢」")
+    private String type;
+
+    @Excel(name = "鍙戠エ绫诲瀷")
+    @Schema(description = "鍙戠エ绫诲瀷")
+    private String invoiceType;
+
+    @Excel(name = "绋庨")
+    @Schema(description = "绋庨")
+    private BigDecimal taxAmount;
+
+    @Excel(name = "浠风◣鍚堣")
+    @Schema(description = "浠风◣鍚堣")
+    private BigDecimal totalAmount;
+
+    @Excel(name = "瀹㈡埛鍚嶇О")
+    @Schema(description = "瀹㈡埛鍚嶇О")
+    private String customerName;
+
+    @Excel(name = "渚涘簲鍟嗗悕绉�")
+    @Schema(description = "渚涘簲鍟嗗悕绉�")
+    private String supplierName;
+
     @Excel(name = "鏈堜唤")
     @Schema(description = "鏈堜唤")
-    private String month ;
+    private String month;
 
     @Excel(name = "閿�椤圭◣棰�")
     @Schema(description = "閿�椤圭◣棰�")
@@ -23,8 +63,27 @@
     @Schema(description = "杩涢」绋庨")
     private BigDecimal xTaxAmount;
 
-    @Excel(name = "閿�-杩�")
-    @Schema(description = "閿�-杩�")
-    private BigDecimal taxAmount;
+    // ========== 鏄庣粏瑙嗗浘瀛楁 ==========
+
+    @Excel(name = "鍙戠エ鍙�")
+    @Schema(description = "鍙戠エ鍙�")
+    private String invoiceNo;
+
+    @Excel(name = "閿�鍞悎鍚屽彿")
+    @Schema(description = "閿�鍞悎鍚屽彿")
+    private String salesContractNo;
+
+    @Excel(name = "璁㈠崟绫诲瀷")
+    @Schema(description = "绫诲瀷 杩涢」/閿�椤�")
+    private String orderType;
+
+    @Excel(name = "寮�绁ㄦ棩鏈�")
+    @Schema(description = "寮�绁ㄦ棩鏈�")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate invoiceDate;
+
+    @Excel(name = "绋庣巼")
+    @Schema(description = "绋庣巼")
+    private BigDecimal taxRate;
 
 }
diff --git a/src/main/java/com/ruoyi/purchase/dto/VatSummaryDto.java b/src/main/java/com/ruoyi/purchase/dto/VatSummaryDto.java
new file mode 100644
index 0000000..5bbb6d4
--- /dev/null
+++ b/src/main/java/com/ruoyi/purchase/dto/VatSummaryDto.java
@@ -0,0 +1,23 @@
+package com.ruoyi.purchase.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+@Schema(description = "澧炲�肩◣杩涢攢椤规寜鏈堟眹鎬伙紙鍥捐〃鐢級")
+public class VatSummaryDto {
+
+    @Schema(description = "鏈堜唤")
+    private String month;
+
+    @Schema(description = "閿�椤圭◣棰濆悎璁�")
+    private BigDecimal jTaxAmount;
+
+    @Schema(description = "杩涢」绋庨鍚堣")
+    private BigDecimal xTaxAmount;
+
+    @Schema(description = "宸锛堥攢-杩涳級")
+    private BigDecimal taxAmount;
+}
diff --git a/src/main/java/com/ruoyi/purchase/service/PurchaseReportService.java b/src/main/java/com/ruoyi/purchase/service/PurchaseReportService.java
index 4ec6522..3f7d263 100644
--- a/src/main/java/com/ruoyi/purchase/service/PurchaseReportService.java
+++ b/src/main/java/com/ruoyi/purchase/service/PurchaseReportService.java
@@ -1,14 +1,20 @@
 package com.ruoyi.purchase.service;
 
-
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ruoyi.purchase.dto.VatDto;
+import com.ruoyi.purchase.dto.VatSummaryDto;
 import com.ruoyi.purchase.vo.PurchaseReportVo;
+
+import java.util.List;
 
 public interface PurchaseReportService {
 
     IPage<PurchaseReportVo> list(Page page, String customerName);
 
-    IPage<VatDto> listVat(Page page, String month);
+    IPage<VatDto> listVat(Page page, String month, String type);
+
+    List<VatSummaryDto> vatSummary(String year);
+
+    IPage<VatDto> listVatDetail(Page page, String month);
 }
diff --git a/src/main/java/com/ruoyi/purchase/service/impl/PurchaseReportServiceImpl.java b/src/main/java/com/ruoyi/purchase/service/impl/PurchaseReportServiceImpl.java
index 676cf46..9b88ce7 100644
--- a/src/main/java/com/ruoyi/purchase/service/impl/PurchaseReportServiceImpl.java
+++ b/src/main/java/com/ruoyi/purchase/service/impl/PurchaseReportServiceImpl.java
@@ -4,6 +4,7 @@
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ruoyi.account.mapper.AccountStatementMapper;
 import com.ruoyi.purchase.dto.VatDto;
+import com.ruoyi.purchase.dto.VatSummaryDto;
 import com.ruoyi.purchase.service.PurchaseReportService;
 import com.ruoyi.purchase.vo.PurchaseReportVo;
 import com.ruoyi.sales.mapper.SalesLedgerMapper;
@@ -11,6 +12,7 @@
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
+import java.util.List;
 
 @Service
 @Slf4j
@@ -26,7 +28,17 @@
     }
 
     @Override
-    public IPage<VatDto> listVat(Page page, String month) {
-        return accountStatementMapper.selectVatDtoPage(page, month);
+    public IPage<VatDto> listVat(Page page, String month, String type) {
+        return accountStatementMapper.selectVatDtoPage(page, month, type);
+    }
+
+    @Override
+    public List<VatSummaryDto> vatSummary(String year) {
+        return accountStatementMapper.selectVatSummary(year);
+    }
+
+    @Override
+    public IPage<VatDto> listVatDetail(Page page, String month) {
+        return accountStatementMapper.listVatDetail(page, month);
     }
 }
diff --git a/src/main/java/com/ruoyi/sales/mapper/SalesLedgerMapper.java b/src/main/java/com/ruoyi/sales/mapper/SalesLedgerMapper.java
index 5518290..fb603ef 100644
--- a/src/main/java/com/ruoyi/sales/mapper/SalesLedgerMapper.java
+++ b/src/main/java/com/ruoyi/sales/mapper/SalesLedgerMapper.java
@@ -89,5 +89,4 @@
     IPage<SalesLedgerDto> listSalesLedgerAndShipped(Page page, @Param("ew") SalesLedgerDto salesLedgerDto);
 
     IPage<PurchaseReportVo> selectPurchaseReportVoPage(Page page, @Param("customerName") String customerName);
-
 }
diff --git a/src/main/java/com/ruoyi/sales/mapper/SalesLedgerProductMapper.java b/src/main/java/com/ruoyi/sales/mapper/SalesLedgerProductMapper.java
index 2615d19..dd29a7e 100644
--- a/src/main/java/com/ruoyi/sales/mapper/SalesLedgerProductMapper.java
+++ b/src/main/java/com/ruoyi/sales/mapper/SalesLedgerProductMapper.java
@@ -32,8 +32,12 @@
 
     List<Map<String, Object>> selectProductSalesAnalysis();
 
+    List<Map<String, Object>> selectProductSalesAnalysisByDate(@Param("startDate") String startDate, @Param("endDate") String endDate);
+
     List<Map<String, Object>> selectRawMaterialPurchaseAnalysis();
 
+    List<Map<String, Object>> selectRawMaterialPurchaseAnalysisByDate(@Param("startDate") String startDate, @Param("endDate") String endDate);
+
     int selectProductCountByTypeAndDate(@Param("type") Integer type, @Param("startDate") LocalDateTime startDate, @Param("endDate") LocalDateTime endDate);
 
     BigDecimal selectRawMaterialExpense();
diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml
index e5e2f9a..f1a5bf9 100644
--- a/src/main/resources/application-dev.yml
+++ b/src/main/resources/application-dev.yml
@@ -74,7 +74,7 @@
     druid:
       # 涓诲簱鏁版嵁婧�
       master:
-        url: jdbc:mysql://localhost:3306/product-inventory-management-bdpro?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+        url: jdbc:mysql://localhost:3306/product-inventory-management-new-pro?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
         username: root
         password: 123456
       # 浠庡簱鏁版嵁婧�
diff --git a/src/main/resources/mapper/account/AccountStatementMapper.xml b/src/main/resources/mapper/account/AccountStatementMapper.xml
index 32b1621..324d243 100644
--- a/src/main/resources/mapper/account/AccountStatementMapper.xml
+++ b/src/main/resources/mapper/account/AccountStatementMapper.xml
@@ -41,38 +41,129 @@
     ORDER BY lj.statement_month DESC
     </select>
     <select id="selectVatDtoPage" resultType="com.ruoyi.purchase.dto.VatDto">
-    SELECT
-        month,
-        jTaxAmount,
-        xTaxAmount,
-        (jTaxAmount - xTaxAmount) AS taxAmount
-    FROM (
+        SELECT * FROM (
+            SELECT
+                asi.id,
+                asi.invoice_number AS invoiceNumber,
+                asi.issue_date AS issueDate,
+                'sales' AS type,
+                asi.invoice_type AS invoiceType,
+                asi.tax_price AS taxAmount,
+                asi.tax_inclusive_price AS totalAmount,
+                c.customer_name AS customerName,
+                NULL AS supplierName,
+                DATE_FORMAT(asi.issue_date, '%Y-%m') AS month
+            FROM account_sales_invoice asi
+            LEFT JOIN customer c ON c.id = asi.customer_id
+            WHERE (asi.status IS NULL OR asi.status = 0)
+            UNION ALL
+            SELECT
+                api.id,
+                api.invoice_number AS invoiceNumber,
+                api.issue_date AS issueDate,
+                'purchase' AS type,
+                api.invoice_type AS invoiceType,
+                api.tax_price AS taxAmount,
+                api.tax_inclusive_price AS totalAmount,
+                NULL AS customerName,
+                s.supplier_name AS supplierName,
+                DATE_FORMAT(api.issue_date, '%Y-%m') AS month
+            FROM account_purchase_invoice api
+            LEFT JOIN supplier_manage s ON s.id = api.supplier_id
+            WHERE (api.status IS NULL OR api.status = 0)
+        ) AS invoice_detail
+        <where>
+            <if test="month != null and month != ''">
+                AND month = #{month}
+            </if>
+            <if test="type != null and type != ''">
+                AND type = #{type}
+            </if>
+        </where>
+        ORDER BY issueDate DESC, type
+    </select>
+
+    <select id="selectVatSummary" resultType="com.ruoyi.purchase.dto.VatSummaryDto">
         SELECT
             month,
-            SUM(IF(type = 'purchase', tax_price, 0)) AS xTaxAmount,
-            SUM(IF(type = 'sales', tax_price, 0)) AS jTaxAmount
+            SUM(jTaxAmount) AS jTaxAmount,
+            SUM(xTaxAmount) AS xTaxAmount,
+            SUM(jTaxAmount) - SUM(xTaxAmount) AS taxAmount
         FROM (
             SELECT
                 DATE_FORMAT(issue_date, '%Y-%m') AS month,
-                tax_price,
-                'sales' AS type
+                SUM(tax_price) AS jTaxAmount,
+                0 AS xTaxAmount
             FROM account_sales_invoice
-            WHERE status != 1
+            WHERE (status IS NULL OR status = 0)
+            <if test="year != null and year != ''">
+                AND YEAR(issue_date) = #{year}
+            </if>
+            GROUP BY DATE_FORMAT(issue_date, '%Y-%m')
             UNION ALL
             SELECT
                 DATE_FORMAT(issue_date, '%Y-%m') AS month,
-                tax_price,
-                'purchase' AS type
+                0 AS jTaxAmount,
+                SUM(tax_price) AS xTaxAmount
             FROM account_purchase_invoice
-            WHERE status != 1
-        ) AS all_data
-    GROUP BY month
-    ) AS TT
-     <where>
-            <if test="month != null">
-                and TT.month = #{month}
+            WHERE (status IS NULL OR status = 0)
+            <if test="year != null and year != ''">
+                AND YEAR(issue_date) = #{year}
             </if>
-     </where>
-    ORDER BY TT.month
+            GROUP BY DATE_FORMAT(issue_date, '%Y-%m')
+        ) t
+        GROUP BY month
+        ORDER BY month
+    </select>
+
+    <select id="listVatDetail" resultType="com.ruoyi.purchase.dto.VatDto">
+        SELECT *
+        FROM (
+            -- 杩涢」绋庢槑缁嗭紙閲囪喘璁㈠崟锛�
+            SELECT
+                pl.purchase_contract_number AS invoiceNo,
+                pl.sales_contract_no AS salesContractNo,
+                pl.supplier_name AS supplierName,
+                NULL AS customerName,
+                '杩涢」' AS orderType,
+                pl.entry_date AS invoiceDate,
+                slp.tax_rate AS taxRate,
+                ROUND(SUM(slp.tax_inclusive_total_price - slp.tax_exclusive_total_price), 2) AS jTaxAmount,
+                0 AS xTaxAmount,
+                ROUND(SUM(slp.tax_inclusive_total_price - slp.tax_exclusive_total_price), 2) AS taxAmount
+            FROM sales_ledger_product slp
+            LEFT JOIN purchase_ledger pl ON pl.id = slp.sales_ledger_id
+            WHERE slp.type = 2
+              AND slp.tax_rate IS NOT NULL
+              AND slp.tax_rate > 0
+            GROUP BY pl.id
+
+            UNION ALL
+
+            -- 閿�椤圭◣鏄庣粏锛堥攢鍞鍗曪級
+            SELECT
+                sl.sales_contract_no AS invoiceNo,
+                sl.sales_contract_no AS salesContractNo,
+                NULL AS supplierName,
+                sl.customer_name AS customerName,
+                '閿�椤�' AS orderType,
+                sl.entry_date AS invoiceDate,
+                slp.tax_rate AS taxRate,
+                0 AS jTaxAmount,
+                ROUND(SUM(slp.tax_inclusive_total_price - slp.tax_exclusive_total_price), 2) AS xTaxAmount,
+                ROUND(SUM(slp.tax_inclusive_total_price - slp.tax_exclusive_total_price), 2) AS taxAmount
+            FROM sales_ledger_product slp
+            LEFT JOIN sales_ledger sl ON sl.id = slp.sales_ledger_id
+            WHERE slp.type = 1
+              AND slp.tax_rate IS NOT NULL
+              AND slp.tax_rate > 0
+            GROUP BY sl.id
+        ) a
+        <where>
+            <if test="month != null and month != ''">
+                AND DATE_FORMAT(a.invoiceDate, '%Y-%m') = #{month}
+            </if>
+        </where>
+        ORDER BY a.invoiceDate DESC
     </select>
 </mapper>
diff --git a/src/main/resources/mapper/home/HomeMapper.xml b/src/main/resources/mapper/home/HomeMapper.xml
index 060099a..71a4df6 100644
--- a/src/main/resources/mapper/home/HomeMapper.xml
+++ b/src/main/resources/mapper/home/HomeMapper.xml
@@ -13,4 +13,28 @@
         GROUP BY p.id, p.product_name
     </select>
 
+    <select id="productTurnoverDaysByDate" resultType="com.ruoyi.dto.MapDto">
+        SELECT p.product_name                                                      AS name,
+               DATEDIFF(IFNULL(MAX(sor.create_time), NOW()), MAX(sir.create_time)) AS value
+        FROM product p
+                 JOIN product_model pm ON p.id = pm.product_id
+                 JOIN stock_in_record sir ON pm.id = sir.product_model_id
+                        AND sir.create_time &gt;= #{startDate}
+                        AND sir.create_time &lt;= #{endDate}
+                 LEFT JOIN stock_out_record sor ON pm.id = sor.product_model_id
+        GROUP BY p.id, p.product_name
+    </select>
+
+    <select id="selectPurchaseCategoryDistribution" resultType="com.ruoyi.dto.MapDto">
+        SELECT p2.product_name   AS name,
+               COUNT(DISTINCT slp.id) AS value
+        FROM sales_ledger_product slp
+                 LEFT JOIN product p ON p.id = slp.product_category
+                 LEFT JOIN product p2 ON p2.id = p.parent_id
+        WHERE slp.type = 2
+          AND p.parent_id IS NOT NULL
+        GROUP BY p2.product_name
+        ORDER BY value DESC
+    </select>
+
 </mapper>
diff --git a/src/main/resources/mapper/sales/SalesLedgerProductMapper.xml b/src/main/resources/mapper/sales/SalesLedgerProductMapper.xml
index ec5cca2..6059825 100644
--- a/src/main/resources/mapper/sales/SalesLedgerProductMapper.xml
+++ b/src/main/resources/mapper/sales/SalesLedgerProductMapper.xml
@@ -219,7 +219,29 @@
         LIMIT 5
     </select>
 
+    <select id="selectProductSalesAnalysisByDate" resultType="java.util.Map">
+        SELECT
+            product_category as name,
+            SUM( tax_inclusive_total_price ) AS value
+        FROM
+            sales_ledger_product
+        WHERE
+            type = 1
+            AND register_date &gt;= #{startDate}
+            AND register_date &lt;= #{endDate}
+        GROUP BY
+            product_category
+        ORDER BY
+            value DESC
+        LIMIT 5
+    </select>
+
     <select id="selectRawMaterialPurchaseAnalysis" resultType="java.util.Map">
+        WITH RECURSIVE raw_material_tree AS (
+            SELECT id FROM product WHERE product_name = '鍘熸潗鏂�' AND parent_id IS NULL
+            UNION ALL
+            SELECT p.id FROM product p INNER JOIN raw_material_tree rmt ON p.parent_id = rmt.id
+        )
         SELECT
             pr.product_name AS name,
             SUM( slp.tax_inclusive_total_price ) AS value
@@ -228,6 +250,26 @@
             JOIN product pr ON slp.product_id = pr.id
         WHERE
             slp.type = 2
+            AND pr.id IN (SELECT id FROM raw_material_tree)
+        GROUP BY
+            pr.id,
+            pr.product_name
+        ORDER BY
+            value DESC
+        LIMIT 5
+    </select>
+
+    <select id="selectRawMaterialPurchaseAnalysisByDate" resultType="java.util.Map">
+        SELECT
+            pr.product_name AS name,
+            SUM( slp.tax_inclusive_total_price ) AS value
+        FROM
+            sales_ledger_product slp
+            JOIN product pr ON slp.product_id = pr.id
+        WHERE
+            slp.type = 2
+            AND slp.register_date &gt;= #{startDate}
+            AND slp.register_date &lt;= #{endDate}
             AND pr.parent_id = ( SELECT id FROM product WHERE product_name = '鍘熸潗鏂�' )
         GROUP BY
             pr.id,
@@ -252,7 +294,7 @@
     <select id="selectRawMaterialExpense" resultType="java.math.BigDecimal">
         WITH RECURSIVE product_tree AS (SELECT id
                                         FROM product
-                                        WHERE product_name = '鍘熸潗鏂�'
+                                        WHERE product_name = '鍘熸潗鏂�' AND parent_id IS NULL
 
                                         UNION ALL
 

--
Gitblit v1.9.3